diff --git a/.mailmap b/.mailmap index d8f49483ebd0..bd6a72f29d9c 100644 --- a/.mailmap +++ b/.mailmap @@ -19,6 +19,7 @@ Abhinav Kumar Ahmad Masri Adam Oldham Adam Radford +Aditya Garg Adriana Reus Adrian Bunk Ajay Kaher @@ -207,7 +208,9 @@ Claudiu Beznea Colin Ian King Corey Minyard Damian Hobson-Garcia +Dan Carpenter Dan Carpenter +Dan Williams Daniel Borkmann Daniel Borkmann Daniel Borkmann @@ -309,7 +312,10 @@ Gokul Sriram Palanisamy Govindaraj Saminathan Guo Ren Guo Ren -Guru Das Srinagesh +Guru Das Srinagesh +Guru Das Srinagesh +Guru Das Srinagesh +Guru Das Srinagesh Gustavo Padovan Gustavo Padovan Hamza Mahfooz @@ -334,6 +340,7 @@ Herbert Xu Huacai Chen Huacai Chen Ignat Korchagin +Igor Korotin Ike Panhc J. Bruce Fields J. Bruce Fields @@ -424,6 +431,7 @@ John Stultz Jonas Gorski +Jonathan Cameron Jordan Crouse @@ -489,6 +497,7 @@ Leon Romanovsky Leon Romanovsky Leon Romanovsky Leo Yan +Liam R. Howlett Liam Mark Linas Vepstas Linus Lüssing @@ -499,6 +508,8 @@ Linus Walleij Linus Walleij Linus Walleij +Li Wang +Li Wang Li Yang Li Yang Lior David @@ -581,6 +592,7 @@ Michel Lespinasse Michel Lespinasse Mickaël Salaün Miguel Ojeda +Mike Leach Mike Rapoport Mike Rapoport Mike Rapoport @@ -670,6 +682,7 @@ Peter A Jonsson Peter Hilber Peter Oruba Peter Oruba +Peter Rosin Pierre-Louis Bossart Pratyush Anand Pratyush Yadav @@ -680,6 +693,7 @@ Punit Agrawal Puranjay Mohan Qais Yousef Qais Yousef +Qi Zheng Quentin Monnet Quentin Monnet Quentin Perret @@ -738,11 +752,13 @@ Sarangdhar Joshi Saravana Kannan Saravana Kannan Sascha Hauer +Sasha Finkelstein Sahitya Tummala Sathishkumar Muruganandam Satya Priya S.Çağlar Onur Sayali Lokhande +Sean Anderson Sean Christopherson Sean Nyekjaer Sean Tranchetti @@ -841,6 +857,7 @@ Tobias Klauser Tobias Klauser Tobias Klauser Todor Tomov +Tomasz Jeznach Tony Luck Trilok Soni TripleX Chung diff --git a/CREDITS b/CREDITS index a03b00452a1e..17962bdd6dbd 100644 --- a/CREDITS +++ b/CREDITS @@ -71,11 +71,6 @@ D: dosfs, LILO, some fd features, ATM, various other hacks here and there S: Buenos Aires S: Argentina -NTFS FILESYSTEM -N: Anton Altaparmakov -E: anton@tuxera.com -D: NTFS filesystem - N: Tim Alpaerts E: tim_alpaerts@toyota-motor-europe.com D: 802.2 class II logical link control layer, @@ -85,8 +80,8 @@ S: B-2610 Wilrijk-Antwerpen S: Belgium N: Anton Altaparmakov -E: aia21@cantab.net -W: http://www-stu.christs.cam.ac.uk/~aia21/ +E: anton@tuxera.com +W: http://www.tuxera.com/ D: Author of new NTFS driver, various other kernel hacks. S: Christ's College S: Cambridge CB2 3BU @@ -1456,6 +1451,14 @@ N: Andy Gospodarek E: andy@greyhouse.net D: Maintenance and contributions to the network interface bonding driver. +N: Vivek Goyal +E: vgoyal@redhat.com +D: KDUMP, KEXEC, and VIRTIO FILE SYSTEM + +N: Alexander Graf +E: graf@amazon.com +D: Kexec Handover (KHO) + N: Wolfgang Grandegger E: wg@grandegger.com D: Controller Area Network (device drivers) @@ -3649,6 +3652,11 @@ S: Dag Hammerskjolds v. 3E S: S-226 64 LUND S: Sweden +N: Tilman Schmidt +E: tilman@imap.cc +D: Siemens Gigaset ISDN driver author and maintainer +D: ISDN CAPI subsystem contributions + N: Henning P. Schmiedehausen E: hps@tanstaafl.de D: added PCI support to the serial driver @@ -4570,8 +4578,5 @@ D: MD driver D: EISA/sysfs subsystem S: France -# Don't add your name here, unless you really _are_ after Marc -# alphabetically. Leonard used to be very proud of being the -# last entry, and he'll get positively pissed if he can't even -# be second-to-last. (and this file really _is_ supposed to be -# in alphabetic order) +# Don't add your name here unless you really are last alphabetically. +# (This file is supposed to be kept in alphabetical order by last name.) diff --git a/Documentation/.renames.txt b/Documentation/.renames.txt index a37d68471d50..43d44753ab93 100644 --- a/Documentation/.renames.txt +++ b/Documentation/.renames.txt @@ -783,11 +783,9 @@ namespaces/compatibility-list admin-guide/namespaces/compatibility-list namespaces/index admin-guide/namespaces/index namespaces/resource-control admin-guide/namespaces/resource-control networking/altera_tse networking/device_drivers/ethernet/altera/altera_tse -networking/baycom networking/device_drivers/hamradio/baycom networking/bpf_flow_dissector bpf/prog_flow_dissector networking/cxacru networking/device_drivers/atm/cxacru networking/defza networking/device_drivers/fddi/defza -networking/device_drivers/3com/3c509 networking/device_drivers/ethernet/3com/3c509 networking/device_drivers/3com/vortex networking/device_drivers/ethernet/3com/vortex networking/device_drivers/amazon/ena networking/device_drivers/ethernet/amazon/ena networking/device_drivers/aquantia/atlantic networking/device_drivers/ethernet/aquantia/atlantic @@ -822,7 +820,6 @@ networking/device_drivers/microsoft/netvsc networking/device_drivers/ethernet/mi networking/device_drivers/netronome/nfp networking/device_drivers/ethernet/netronome/nfp networking/device_drivers/pensando/ionic networking/device_drivers/ethernet/pensando/ionic networking/device_drivers/qualcomm/rmnet networking/device_drivers/cellular/qualcomm/rmnet -networking/device_drivers/smsc/smc9 networking/device_drivers/ethernet/smsc/smc9 networking/device_drivers/stmicro/stmmac networking/device_drivers/ethernet/stmicro/stmmac networking/device_drivers/ti/cpsw networking/device_drivers/ethernet/ti/cpsw networking/device_drivers/ti/cpsw_switchdev networking/device_drivers/ethernet/ti/cpsw_switchdev @@ -836,19 +833,16 @@ networking/e100 networking/device_drivers/ethernet/intel/e100 networking/e1000 networking/device_drivers/ethernet/intel/e1000 networking/e1000e networking/device_drivers/ethernet/intel/e1000e networking/fm10k networking/device_drivers/ethernet/intel/fm10k -networking/fore200e networking/device_drivers/atm/fore200e networking/hinic networking/device_drivers/ethernet/huawei/hinic networking/i40e networking/device_drivers/ethernet/intel/i40e networking/iavf networking/device_drivers/ethernet/intel/iavf networking/ice networking/device_drivers/ethernet/intel/ice networking/igb networking/device_drivers/ethernet/intel/igb networking/igbvf networking/device_drivers/ethernet/intel/igbvf -networking/iphase networking/device_drivers/atm/iphase networking/ixgbe networking/device_drivers/ethernet/intel/ixgbe networking/ixgbevf networking/device_drivers/ethernet/intel/ixgbevf networking/netdev-FAQ process/maintainer-netdev networking/skfp networking/device_drivers/fddi/skfp -networking/z8530drv networking/device_drivers/hamradio/z8530drv nfc/index driver-api/nfc/index nfc/nfc-hci driver-api/nfc/nfc-hci nfc/nfc-pn544 driver-api/nfc/nfc-pn544 diff --git a/Documentation/ABI/obsolete/sysfs-selinux-user b/Documentation/ABI/removed/sysfs-selinux-user similarity index 100% rename from Documentation/ABI/obsolete/sysfs-selinux-user rename to Documentation/ABI/removed/sysfs-selinux-user diff --git a/Documentation/ABI/stable/sysfs-driver-speakup b/Documentation/ABI/stable/sysfs-driver-speakup index 8b508b4a7a00..69c26b911fe3 100644 --- a/Documentation/ABI/stable/sysfs-driver-speakup +++ b/Documentation/ABI/stable/sysfs-driver-speakup @@ -16,8 +16,8 @@ What: /sys/accessibility/speakup/bleeps KernelVersion: 2.6 Contact: speakup@linux-speakup.org Description: This controls whether one hears beeps through the PC speaker - when using speakup's review commands. - TODO: what values does it accept? + when using speakup's review commands. Range: 0-3. 0 = off, 1 = beeps + only, 2 = announcements only, 3 = beeps and announcements (default). What: /sys/accessibility/speakup/bleep_time KernelVersion: 2.6 diff --git a/Documentation/ABI/testing/evm b/Documentation/ABI/testing/evm index 44750a933db4..db3007babb58 100644 --- a/Documentation/ABI/testing/evm +++ b/Documentation/ABI/testing/evm @@ -26,6 +26,7 @@ Description: 2 Permit modification of EVM-protected metadata at runtime. Not supported if HMAC validation and creation is enabled (deprecated). + 3 Require asymmetric signatures to be version 3 31 Disable further runtime modification of EVM policy === ================================================== diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index d4b3696a9efb..19258471b7b2 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -53,10 +53,7 @@ Description: where 'imasig' is the original or the signature format v2. where 'modsig' is an appended signature, - where 'sigv3' is the signature format v3. (Currently - limited to fsverity digest based signatures - stored in security.ima xattr. Requires - specifying "digest_type=verity" first.) + where 'sigv3' is the signature format v3. appraise_flag:= [check_blacklist] (deprecated) Setting the check_blacklist flag is no longer necessary. @@ -186,6 +183,11 @@ Description: appraise func=BPRM_CHECK digest_type=verity \ appraise_type=sigv3 + Example of a regular IMA file hash 'appraise' rule requiring + signature version 3 format stored in security.ima xattr. + + appraise func=BPRM_CHECK appraise_type=sigv3 + All of these policy rules could, for example, be constrained either based on a filesystem's UUID (fsuuid) or based on LSM labels. diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm index f8016df64532..bc36ba32c900 100644 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm @@ -278,3 +278,13 @@ Date: Aug 2025 KernelVersion 6.18 Contact: Mao Jinlong Description: (Read) Show hardware context information of device. + +What: /sys/bus/coresight/devices//traceid +Date: March 2026 +KernelVersion: 7.1 +Contact: Jie Gan +Description: + (R) Show the trace ID that will appear in the trace stream + coming from this TPDM. The trace ID is inherited from the + connected TPDA device and is fixed for the lifetime of the + device. Returns -EINVAL if the device has not been enabled yet. diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl index c80a1b5a03db..16a9b3d2e2c0 100644 --- a/Documentation/ABI/testing/sysfs-bus-cxl +++ b/Documentation/ABI/testing/sysfs-bus-cxl @@ -508,6 +508,19 @@ Description: (RO) The size of extended linear cache, if there is an extended linear cache. Otherwise the attribute will not be visible. + +What: /sys/bus/cxl/devices/regionZ/locked +Date: Mar, 2026 +KernelVersion: v7.1 +Contact: linux-cxl@vger.kernel.org +Description: + (RO) The CXL driver has the capability to lock a region based on + a BIOS or platform dependent configuration. Regions created as + locked are never permitted to be destroyed. Resets to participating + decoders will not result in a region destroy and will not free the + decoder resources. + + What: /sys/bus/cxl/devices/regionZ/mode Date: January, 2023 KernelVersion: v6.3 diff --git a/Documentation/ABI/testing/sysfs-bus-i3c b/Documentation/ABI/testing/sysfs-bus-i3c index c1e048957a01..19f5cf8b1b11 100644 --- a/Documentation/ABI/testing/sysfs-bus-i3c +++ b/Documentation/ABI/testing/sysfs-bus-i3c @@ -172,3 +172,23 @@ Description: the automatic retries. Exist only when I3C constroller supports this retry on nack feature. +What: /sys/bus/i3c/devices/i3c-/do_daa +KernelVersion: 7.0 +Contact: linux-i3c@vger.kernel.org +Description: + Write-only attribute that triggers a Dynamic Address Assignment + (DAA) procedure which discovers new I3C devices on the bus. + Writing a boolean true value (1, y, yes, true, on) to this + attribute causes the master controller to perform DAA, which + includes broadcasting an ENTDAA (Enter Dynamic Address Assignment) + Common Command Code (CCC) on the bus. Writing a false value + returns -EINVAL. + + This is useful for discovering I3C devices that were not present + during initial bus initialization and are unable to issue + Hot-Join. Only devices without a currently assigned dynamic address + will respond to the ENTDAA broadcast and be assigned addresses. + + Note that this mechanism is distinct from Hot-Join, since this is + controller-initiated discovery, while Hot-Join is device-initiated + method to provoke controller discovery procedure. diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 5f87dcee78f7..4fc9f6bd4281 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1428,7 +1428,7 @@ KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: The name of the trigger source being used, as per string given - in /sys/class/iio/triggerY/name. + in /sys/bus/iio/devices/triggerY/name. What: /sys/bus/iio/devices/iio:deviceX/bufferY/length KernelVersion: 5.11 diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power index 4b21d5d23251..32697b926cc8 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -675,7 +675,8 @@ Description: Valid values: "Unknown", "SDP", "DCP", "CDP", "ACA", "C", "PD", - "PD_DRP", "PD_PPS", "BrickID" + "PD_DRP", "PD_PPS", "BrickID", "PD_SPR_AVS", + "PD_PPS_SPR_AVS" **Device Specific Properties** diff --git a/Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes b/Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes new file mode 100644 index 000000000000..a16c54ab841b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-reboot-mode-reboot_modes @@ -0,0 +1,36 @@ +What: /sys/class/reboot-mode//reboot_modes +Date: March 2026(TBD) +KernelVersion: TBD +Contact: linux-pm@vger.kernel.org + Description: + This interface exposes the reboot-mode arguments + registered with the reboot-mode framework. It is + a read-only interface and provides a space + separated list of reboot-mode arguments supported + on the current platform. + Example: + recovery fastboot bootloader + + The exact sysfs path may vary depending on the + name of the driver that registers the arguments. + Example: + /sys/class/reboot-mode/nvmem-reboot-mode/reboot_modes + /sys/class/reboot-mode/syscon-reboot-mode/reboot_modes + /sys/class/reboot-mode/qcom-pon/reboot_modes + + The supported arguments can be used by userspace to + invoke device reset using the standard reboot() system + call interface, with the "argument" as string to "*arg" + parameter along with LINUX_REBOOT_CMD_RESTART2. + + A driver can expose the supported arguments by + registering them with the reboot-mode framework + using the property names that follow the + mode- format. + Example: + mode-bootloader, mode-recovery. + + This attribute is useful for scripts or initramfs + logic that need to programmatically determine + which reboot-mode arguments are valid before + triggering a reboot. diff --git a/Documentation/ABI/testing/sysfs-driver-hid-lenovo-go b/Documentation/ABI/testing/sysfs-driver-hid-lenovo-go new file mode 100644 index 000000000000..c8221373ef76 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-lenovo-go @@ -0,0 +1,724 @@ +What: /sys/class/leds/go:rgb:joystick_rings/effect +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the display effect of the RGB interface. + + Values are monocolor, breathe, chroma, or rainbow. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/class/leds/go:rgb:joystick_rings/effect_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the effect attribute. + + Values are monocolor, breathe, chroma, or rainbow. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/class/leds/go:rgb:joystick_rings/enabled +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling or disabling the RGB interface. + + Values are true or false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/class/leds/go:rgb:joystick_rings/enabled_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the enabled attribute. + + Values are true or false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/class/leds/go:rgb:joystick_rings/mode +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the operating mode of the RGB interface. + + Values are dynamic or custom. Custom allows setting the RGB effect and color. + Dynamic is a Windows mode for syncing Lenovo RGB interfaces not currently + supported under Linux. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/class/leds/go:rgb:joystick_rings/mode_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the mode attribute. + + Values are dynamic or custom. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/class/leds/go:rgb:joystick_rings/profile +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls selecting the configured RGB profile. + + Values are 1-3. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/class/leds/go:rgb:joystick_rings/profile_range +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the profile attribute. + + Values are 1-3. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/class/leds/go:rgb:joystick_rings/speed +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the change rate for the breathe, chroma, and rainbow effects. + + Values are 0-100. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/class/leds/go:rgb:joystick_rings/speed_range +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the speed attribute. + + Values are 0-100. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./firmware_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the firmware version of the internal MCU. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./fps_mode_dpi +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the DPI of the right handle when the FPS mode switch is on. + + Values are 500, 800, 1200, and 1800. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./fps_mode_dpi_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the fps_mode_dpi attribute. + + Values are 500, 800, 1200, and 1800. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./hardware_generation +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the hardware generation of the internal MCU. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./hardware_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the hardware version of the internal MCU. + + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/auto_sleep_time +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the sleep timer due to inactivity for the left removable controller. + + Values are 0-255. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/auto_sleep_time_range +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the left_handle/auto_sleep_time attribute. + + Values are 0-255. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/calibrate_gyro +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This initiates or halts calibration of the left removable controller's IMU. + + Values are start, stop. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/calibrate_gyro_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the left_handle/calibrate_gyro attribute. + + Values are start, stop. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/calibrate_gyro_status +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the result of the last attempted calibration of the left removable controller's IMU. + + Values are unknown, success, failure. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/calibrate_joystick +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This initiates or halts calibration of the left removable controller's joystick. + + Values are start, stop. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/calibrate_joystick_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the left_handle/calibrate_jotstick attribute. + + Values are start, stop. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/calibrate_joystick_status +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the result of the last attempted calibration of the left removable controller's joystick. + + Values are unknown, success, failure. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/calibrate_tirgger +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This initiates or halts calibration of the left removable controller's trigger. + + Values are start, stop. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/calibrate_gyro_trigger +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the left_handle/calibrate_trigger attribute. + + Values are start, stop. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/calibrate_trigger_status +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the result of the last attempted calibration of the left removable controller's trigger. + + Values are unknown, success, failure. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/firmware_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the left removable controller's firmware version. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/hardware_generation +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the hardware generation of the left removable controller. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/hardware_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the hardware version of the left removable controller. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/imu_bypass_enabled +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling or disabling the IMU bypass function of the left removable controller. + + Values are true or false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/imu_bypass_enabled_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the left_handle/imu_bypass_enabled attribute. + + Values are true or false. + +What: /sys/bus/usb/devices/-:./::./left_handle/imu_enabled +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling or disabling the IMU of the left removable controller. + + Values are true or false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/imu_enabled_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the left_handle/imu_enabled attribute. + + Values are true or false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/product_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the product version of the left removable controller. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/protocol_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the protocol version of the left removable controller. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/reset +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: Resets the left removable controller to factory defaults. + + Writing 1 to this path initiates. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/rumble_mode +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls setting the response behavior for rumble events for the left removable controller. + + Values are fps, racing, standarg, spg, rpg. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/rumble_mode_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the left_handle/rumble_mode attribute. + + Values are fps, racing, standarg, spg, rpg. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/rumble_notification +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling haptic rumble events for the left removable controller. + + Values are true, false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/rumble_notification_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the left_handle/rumble_notification attribute. + + Values are true, false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./mode +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the operating mode of the built-in controller. + + Values are xinput or dinput. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./left_handle/mode_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the mode attribute. + + Values are xinput or dinput. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./os_mode +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the behavior of built in chord combinations. + + Values are windows or linux. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./os_mode_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the os_mode attribute. + + Values are windows or linux. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./product_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the product version of the internal MCU. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/protocol_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the protocol version of the internal MCU. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./reset_mcu +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: Resets the internal MCU to factory defaults. + + Writing 1 to this path initiates. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/auto_sleep_time +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the sleep timer due to inactivity for the right removable controller. + + Values are 0-255. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/auto_sleep_time_range +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the right_handle/auto_sleep_time attribute. + + Values are 0-255. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/calibrate_gyro +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This initiates or halts calibration of the right removable controller's IMU. + + Values are start, stop. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/calibrate_gyro_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the right_handle/calibrate_gyro attribute. + + Values are start, stop. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/calibrate_gyro_status +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the result of the last attempted calibration of the right removable controller's IMU. + + Values are unknown, success, failure. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/calibrate_joystick +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This initiates or halts calibration of the right removable controller's joystick. + + Values are start, stop. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/calibrate_joystick_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the right_handle/calibrate_jotstick attribute. + + Values are start, stop. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/calibrate_joystick_status +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the result of the last attempted calibration of the right removable controller's joystick. + + Values are unknown, success, failure. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/calibrate_tirgger +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This initiates or halts calibration of the right removable controller's trigger. + + Values are start, stop. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/calibrate_gyro_trigger +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the right_handle/calibrate_trigger attribute. + + Values are start, stop. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/calibrate_trigger_status +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the result of the last attempted calibration of the right removable controller's trigger. + + Values are unknown, success, failure. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/firmware_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the right removable controller's firmware version. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/hardware_generation +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the hardware generation of the right removable controller. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/hardware_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the hardware version of the right removable controller. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/imu_bypass_enabled +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling or disabling the IMU bypass function of the right removable controller. + + Values are true or false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/imu_bypass_enabled_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the right_handle/imu_bypass_enabled attribute. + + Values are true or false. + +What: /sys/bus/usb/devices/-:./::./right_handle/imu_enabled +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling or disabling the IMU of the right removable controller. + + Values are true or false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/imu_enabled_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the right_handle/imu_enabled attribute. + + Values are true or false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/product_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the product version of the right removable controller. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/protocol_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the protocol version of the right removable controller. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/reset +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: Resets the right removable controller to factory defaults. + + Writing 1 to this path initiates. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/rumble_mode +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls setting the response behavior for rumble events for the right removable controller. + + Values are fps, racing, standarg, spg, rpg. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/rumble_mode_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the right_handle/rumble_mode attribute. + + Values are fps, racing, standarg, spg, rpg. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/rumble_notification +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling haptic rumble events for the right removable controller. + + Values are true, false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./right_handle/rumble_notification_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the right_handle/rumble_notification attribute. + + Values are true, false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./rumble_intensity +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls setting the rumble intensity for both removable controllers. + + Values are off, low, medium, high. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./rumble_intensity_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the rumble_intensity attribute. + + Values are off, low, medium, high. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./touchpad/enabled +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling or disabling the touchpad. + + Values are true, false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./touchpad/enabled_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the touchpad/enabled attribute. + + Values are true, false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./touchpad/vibration_enabled +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling haptic rumble events for the touchpad. + + Values are true, false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./touchpad/vibration_enabled_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the touchpad/vibration_enabled attribute. + + Values are true, false. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./touchpad/vibration_intensity +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls setting the intensity of the touchpad haptics. + + Values are off, low, medium, high. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./touchpad/vibration_intensity_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the touchpad/vibration_intensity attribute. + + Values are off, low, medium, high. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./tx_dongle/firmware_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the firmware version of the internal wireless transmission dongle. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./tx_dongle/hardware_generation +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the hardware generation of the internal wireless transmission dongle. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./tx_dongle/hardware_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the hardware version of the internal wireless transmission dongle. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./tx_dongle/product_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the product version of the internal wireless transmission dongle. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./tx_dongle/protocol_version +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the protocol version of the internal wireless transmission dongle. + + Applies to Lenovo Legion Go and Go 2 line of handheld devices. + diff --git a/Documentation/ABI/testing/sysfs-driver-hid-lenovo-go-s b/Documentation/ABI/testing/sysfs-driver-hid-lenovo-go-s new file mode 100644 index 000000000000..4d317074bb7e --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-lenovo-go-s @@ -0,0 +1,304 @@ +What: /sys/class/leds/go_s:rgb:joystick_rings/effect +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the display effect of the RGB interface. + + Values are monocolor, breathe, chroma, or rainbow. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/class/leds/go_s:rgb:joystick_rings/effect_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the effect attribute. + + Values are monocolor, breathe, chroma, or rainbow. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/class/leds/go_s:rgb:joystick_rings/enabled +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling or disabling the RGB interface. + + Values are true or false. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/class/leds/go_s:rgb:joystick_rings/enabled_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the enabled attribute. + + Values are true or false. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/class/leds/go_s:rgb:joystick_rings/mode +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the operating mode of the RGB interface. + + Values are dynamic or custom. Custom allows setting the RGB effect and color. + Dynamic is a Windows mode for syncing Lenovo RGB interfaces not currently + supported under Linux. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/class/leds/go_s:rgb:joystick_rings/mode_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the mode attribute. + + Values are dynamic or custom. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/class/leds/go_s:rgb:joystick_rings/profile +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls selecting the configured RGB profile. + + Values are 1-3. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/class/leds/go_s:rgb:joystick_rings/profile_range +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the profile attribute. + + Values are 1-3. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/class/leds/go_s:rgb:joystick_rings/speed +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the change rate for the breathe, chroma, and rainbow effects. + + Values are 0-100. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/class/leds/go_s:rgb:joystick_rings/speed_range +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the speed attribute. + + Values are 0-100. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./gamepad/auto_sleep_time +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the sleep timer due to inactivity for the built-in controller. + + Values are 0-255. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./gamepad/auto_sleep_time_range +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the gamepad/auto_sleep_time attribute. + + Values are 0-255. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./gamepad/dpad_mode +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the operating mode of the built-in controllers D-pad. + + Values are 4-way or 8-way. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./gamepad/dpad_mode_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the gamepad/dpad_mode attribute. + + Values are 4-way or 8-way. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./gamepad/mode +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the operating mode of the built-in controller. + + Values are xinput or dinput. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./gamepad/mode_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the gamepad/mode attribute. + + Values are xinput or dinput. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./gamepad/poll_rate +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls the poll rate in Hz of the built-in controller. + + Values are 125, 250, 500, or 1000. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./gamepad/poll_rate_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the gamepad/poll_rate attribute. + + Values are 125, 250, 500, or 1000. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./imu/bypass_enabled +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling or disabling the IMU bypass function. When enabled the IMU data is directly reported to the OS through +an HIDRAW interface. + + Values are true or false. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./imu/bypass_enabled_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the imu/bypass_enabled attribute. + + Values are true or false. + +What: /sys/bus/usb/devices/-:./::./imu/manufacturer +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the manufacturer of the intertial measurment unit. + + Values are Bosch or ST. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./imu/sensor_enabled +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling or disabling the IMU. + + Values are true, false, or wake-2s. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./imu/sensor_enabled_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the imu/sensor_enabled attribute. + + Values are true, false, or wake-2s. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./mcu_id +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the MCU Identification Number + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./mouse/step +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls which value is used for the mouse sensitivity. + + Values are 1-127. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./mouse/step_range +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the mouse/step attribute. + + Values are 1-127. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./os_mode +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls which value is used for the touchpads operating mode. + + Values are windows or linux. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./os_mode_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the os_mode attribute. + + Values are windows or linux. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./touchpad/enabled +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls enabling or disabling the built-in touchpad. + + Values are true or false. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./touchpad/enabled_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the touchpad/enabled attribute. + + Values are true or false. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./touchpad/linux_mode +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls behavior of the touchpad events when os_mode is set to linux. + + Values are absolute or relative. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./touchpad/linux_mode_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the touchpad/linux_mode attribute. + + Values are absolute or relative. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./touchpad/windows_mode +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This controls behavior of the touchpad events when os_mode is set to windows. + + Values are absolute or relative. + + Applies to Lenovo Legion Go S line of handheld devices. + +What: /sys/bus/usb/devices/-:./::./touchpad/windows_mode_index +Date: April 2026 +Contact: linux-input@vger.kernel.org +Description: This displays the available options for the touchpad/windows_mode attribute. + + Values are absolute or relative. + + Applies to Lenovo Legion Go S line of handheld devices. diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index a90612ab5780..3c422aac778b 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -1768,3 +1768,26 @@ Description: ==================== =========================== The attribute is read only. + +What: /sys/bus/platform/drivers/ufshcd/*/dme_qos_notification +What: /sys/bus/platform/devices/*.ufs/dme_qos_notification +Date: March 2026 +Contact: Can Guo +Description: + This attribute reports and clears pending DME (Device Management + Entity) Quality of Service (QoS) notifications. This attribute + is a bitfield with the following bit assignments: + + Bit Description + === ====================================== + 0 DME QoS Monitor has been reset by host + 1 QoS from TX is detected + 2 QoS from RX is detected + 3 QoS from PA_INIT is detected + + Reading this attribute returns the pending DME QoS notification + bits. Writing '0' to this attribute clears pending DME QoS + notification bits. Writing any non-zero value is invalid and + will be rejected. + + The attribute is read/write. diff --git a/Documentation/ABI/testing/sysfs-driver-uniwill-laptop b/Documentation/ABI/testing/sysfs-driver-uniwill-laptop index 2df70792968f..2397c65c969a 100644 --- a/Documentation/ABI/testing/sysfs-driver-uniwill-laptop +++ b/Documentation/ABI/testing/sysfs-driver-uniwill-laptop @@ -51,3 +51,30 @@ Description: Reading this file returns the current status of the breathing animation functionality. + +What: /sys/bus/platform/devices/INOU0000:XX/ctgp_offset +Date: January 2026 +KernelVersion: 7.0 +Contact: Werner Sembach +Description: + Allows userspace applications to set the configurable TGP offset on top of the base + TGP. Base TGP and max TGP and therefore the max cTGP offset are device specific. + Note that setting the maximum cTGP leaves no window open for Dynamic Boost as + Dynamic Boost also can not go over max TGP. Setting the cTGP to maximum is + effectively disabling Dynamic Boost and telling the device to always prioritize the + GPU over the CPU. + + Reading this file returns the current configurable TGP offset. + +What: /sys/bus/platform/devices/INOU0000:XX/usb_c_power_priority +Date: February 2026 +KernelVersion: 7.1 +Contact: Werner Sembach +Description: + Allows userspace applications to choose the USB-C power distribution profile between + one that offers a bigger share of the power to the battery and one that offers more + of it to the CPU. Writing "charging"/"performance" into this file selects the + respective profile. + + Reading this file returns the profile names with the currently active one in + brackets. diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index c1d2b3fd9c65..423ec40e2e4e 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -407,6 +407,12 @@ Contact: "Hridya Valsaraju" Description: Average number of valid blocks. Available when CONFIG_F2FS_STAT_FS=y. +What: /sys/fs/f2fs//defrag_blocks +Date: February 2026 +Contact: "Jinbao Liu" +Description: Number of blocks moved by defragment. + Available when CONFIG_F2FS_STAT_FS=y. + What: /sys/fs/f2fs//mounted_time_sec Date: February 2020 Contact: "Jaegeuk Kim" diff --git a/Documentation/admin-guide/cgroup-v1/memcg_test.rst b/Documentation/admin-guide/cgroup-v1/memcg_test.rst index 9f8e27355cba..7c7cd457cf69 100644 --- a/Documentation/admin-guide/cgroup-v1/memcg_test.rst +++ b/Documentation/admin-guide/cgroup-v1/memcg_test.rst @@ -47,21 +47,19 @@ Please note that implementation details can be changed. Called when swp_entry's refcnt goes down to 0. A charge against swap disappears. -3. charge-commit-cancel +3. charge-commit ======================= Memcg pages are charged in two steps: - mem_cgroup_try_charge() - - mem_cgroup_commit_charge() or mem_cgroup_cancel_charge() + - commit_charge() At try_charge(), there are no flags to say "this page is charged". at this point, usage += PAGE_SIZE. At commit(), the page is associated with the memcg. - At cancel(), simply usage -= PAGE_SIZE. - Under below explanation, we assume CONFIG_SWAP=y. 4. Anonymous diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 8ad0b2781317..6efd0095ed99 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -220,7 +220,7 @@ cgroup v2 currently supports the following mount options. memory_hugetlb_accounting Count HugeTLB memory usage towards the cgroup's overall memory usage for the memory controller (for the purpose of - statistics reporting and memory protetion). This is a new + statistics reporting and memory protection). This is a new behavior that could regress existing setups, so it must be explicitly opted in with this mount option. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 4510b4b3c416..4d0f545fb3ec 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6,7 +6,6 @@ APPARMOR AppArmor support is enabled. ARM ARM architecture is enabled. ARM64 ARM64 architecture is enabled. - AX25 Appropriate AX.25 support is enabled. CLK Common clock infrastructure is enabled. CMA Contiguous Memory Area support is enabled. DRM Direct Rendering Management support is enabled. @@ -633,23 +632,6 @@ Kernel parameters 1 - Enable the BAU. unset - Disable the BAU. - baycom_epp= [HW,AX25] - Format: , - - baycom_par= [HW,AX25] BayCom Parallel Port AX.25 Modem - Format: , - See header of drivers/net/hamradio/baycom_par.c. - - baycom_ser_fdx= [HW,AX25] - BayCom Serial Port AX.25 Modem (Full Duplex Mode) - Format: ,,[,] - See header of drivers/net/hamradio/baycom_ser_fdx.c. - - baycom_ser_hdx= [HW,AX25] - BayCom Serial Port AX.25 Modem (Half Duplex Mode) - Format: ,, - See header of drivers/net/hamradio/baycom_ser_hdx.c. - bdev_allow_write_mounted= Format: Control the ability to open a mounted block device @@ -2410,23 +2392,6 @@ Kernel parameters [IMA] Define a custom template format. Format: { "field1|...|fieldN" } - ima.ahash_minsize= [IMA] Minimum file size for asynchronous hash usage - Format: - Set the minimal file size for using asynchronous hash. - If left unspecified, ahash usage is disabled. - - ahash performance varies for different data sizes on - different crypto accelerators. This option can be used - to achieve the best performance for a particular HW. - - ima.ahash_bufsize= [IMA] Asynchronous hash buffer size - Format: - Set hashing buffer size. Default: 4k. - - ahash performance varies for different chunk sizes on - different crypto accelerators. This option can be used - to achieve best performance for particular HW. - ima= [IMA] Enable or disable IMA Format: { "off" | "on" } Default: "on" @@ -3264,8 +3229,8 @@ Kernel parameters for the host. To force nVHE on VHE hardware, add "arm64_sw.hvhe=0 id_aa64mmfr1.vh=0" to the command-line. - "nested" is experimental and should be used with - extreme caution. + "nested" and "protected" are experimental and should be + used with extreme caution. kvm-arm.vgic_v3_group0_trap= [KVM,ARM,EARLY] Trap guest accesses to GICv3 group-0 diff --git a/Documentation/admin-guide/laptops/thinkpad-acpi.rst b/Documentation/admin-guide/laptops/thinkpad-acpi.rst index 03951ed6b628..f874db31801d 100644 --- a/Documentation/admin-guide/laptops/thinkpad-acpi.rst +++ b/Documentation/admin-guide/laptops/thinkpad-acpi.rst @@ -1522,6 +1522,27 @@ Currently 2 antenna types are supported as mentioned below: The property is read-only. If the platform doesn't have support the sysfs class is not created. +doubletap_enable +---------------- + +sysfs: doubletap_enable + +Controls whether TrackPoint doubletap events are filtered out. Doubletap is a +feature where quickly tapping the TrackPoint twice triggers a special function key event. + +The available commands are:: + + cat /sys/devices/platform/thinkpad_acpi/doubletap_enable + echo 1 | sudo tee /sys/devices/platform/thinkpad_acpi/doubletap_enable + echo 0 | sudo tee /sys/devices/platform/thinkpad_acpi/doubletap_enable + +Values: + + * 1 - doubletap events are processed (default) + * 0 - doubletap events are filtered out (ignored) + + This setting can also be toggled via the Fn+doubletap hotkey. + Auxmac ------ diff --git a/Documentation/admin-guide/laptops/uniwill-laptop.rst b/Documentation/admin-guide/laptops/uniwill-laptop.rst index aff5f57a6bd4..561334865feb 100644 --- a/Documentation/admin-guide/laptops/uniwill-laptop.rst +++ b/Documentation/admin-guide/laptops/uniwill-laptop.rst @@ -50,6 +50,10 @@ between 1 and 100 percent are supported. Additionally the driver signals the presence of battery charging issues through the standard ``health`` power supply sysfs attribute. +It also lets you set whether a USB-C power source should prioritise charging the battery or +delivering immediate power to the cpu. See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for +details. + Lightbar -------- @@ -58,3 +62,11 @@ LED class device. The default name of this LED class device is ``uniwill:multico See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details on how to control the various animation modes of the lightbar. + +Configurable TGP +---------------- + +The ``uniwill-laptop`` driver allows to set the configurable TGP for devices with NVIDIA GPUs that +allow it. + +See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details. diff --git a/Documentation/admin-guide/lockup-watchdogs.rst b/Documentation/admin-guide/lockup-watchdogs.rst index 3e09284a8b9b..8f245f4a95b7 100644 --- a/Documentation/admin-guide/lockup-watchdogs.rst +++ b/Documentation/admin-guide/lockup-watchdogs.rst @@ -16,7 +16,7 @@ details), and a compile option, "BOOTPARAM_SOFTLOCKUP_PANIC", are provided for this. A 'hardlockup' is defined as a bug that causes the CPU to loop in -kernel mode for more than 10 seconds (see "Implementation" below for +kernel mode for several seconds (see "Implementation" below for details), without letting other interrupts have a chance to run. Similarly to the softlockup case, the current stack trace is displayed upon detection and the system will stay locked up unless the default @@ -30,39 +30,135 @@ timeout is set through the confusingly named "kernel.panic" sysctl), to cause the system to reboot automatically after a specified amount of time. +Configuration +============= + +A kernel knob is provided that allows administrators to configure +this period. The "watchdog_thresh" parameter (default 10 seconds) +controls the threshold. The right value for a particular environment +is a trade-off between fast response to lockups and detection overhead. + Implementation ============== -The soft and hard lockup detectors are built on top of the hrtimer and -perf subsystems, respectively. A direct consequence of this is that, -in principle, they should work in any architecture where these -subsystems are present. +The soft and hard lockup detectors are built around an hrtimer. +In addition, the softlockup detector regularly schedules a job, and +the hard lockup detector might use Perf/NMI events on architectures +that support it. -A periodic hrtimer runs to generate interrupts and kick the watchdog -job. An NMI perf event is generated every "watchdog_thresh" -(compile-time initialized to 10 and configurable through sysctl of the -same name) seconds to check for hardlockups. If any CPU in the system -does not receive any hrtimer interrupt during that time the -'hardlockup detector' (the handler for the NMI perf event) will -generate a kernel warning or call panic, depending on the -configuration. +Frequency and Heartbeats +------------------------ -The watchdog job runs in a stop scheduling thread that updates a -timestamp every time it is scheduled. If that timestamp is not updated -for 2*watchdog_thresh seconds (the softlockup threshold) the +The core of the detectors is an hrtimer. It serves multiple purposes: + +- schedules watchdog job for the softlockup detector +- bumps the interrupt counter for hardlockup detectors (heartbeat) +- detects softlockups +- detects hardlockups in Buddy mode + +The period of this hrtimer is 2*watchdog_thresh/5, which is 4 seconds +by default. The hrtimer has two or three chances to generate an interrupt +(heartbeat) before the hardlockup detector kicks in. + +Softlockup Detector +------------------- + +The watchdog job is scheduled by the hrtimer and runs in a stop scheduling +thread. It updates a timestamp every time it is scheduled. If that timestamp +is not updated for 2*watchdog_thresh seconds (the softlockup threshold) the 'softlockup detector' (coded inside the hrtimer callback function) will dump useful debug information to the system log, after which it will call panic if it was instructed to do so or resume execution of other kernel code. -The period of the hrtimer is 2*watchdog_thresh/5, which means it has -two or three chances to generate an interrupt before the hardlockup -detector kicks in. +Hardlockup Detector (NMI/Perf) +------------------------------ -As explained above, a kernel knob is provided that allows -administrators to configure the period of the hrtimer and the perf -event. The right value for a particular environment is a trade-off -between fast response to lockups and detection overhead. +On architectures that support NMI (Non-Maskable Interrupt) perf events, +a periodic NMI is generated every "watchdog_thresh" seconds. + +If any CPU in the system does not receive any hrtimer interrupt +(heartbeat) during the "watchdog_thresh" window, the 'hardlockup +detector' (the handler for the NMI perf event) will generate a kernel +warning or call panic. + +**Detection Overhead (NMI):** + +The time to detect a lockup can vary depending on when the lockup +occurs relative to the NMI check window. Examples below assume a watchdog_thresh of 10. + +* **Best Case:** The lockup occurs just before the first heartbeat is + due. The detector will notice the missing hrtimer interrupt almost + immediately during the next check. + + :: + + Time 100.0: cpu 1 heartbeat + Time 100.1: hardlockup_check, cpu1 stores its state + Time 103.9: Hard Lockup on cpu1 + Time 104.0: cpu 1 heartbeat never comes + Time 110.1: hardlockup_check, cpu1 checks the state again, should be the same, declares lockup + + Time to detection: ~6 seconds + +* **Worst Case:** The lockup occurs shortly after a valid interrupt + (heartbeat) which itself happened just after the NMI check. The next + NMI check sees that the interrupt count has changed (due to that one + heartbeat), assumes the CPU is healthy, and resets the baseline. The + lockup is only detected at the subsequent check. + + :: + + Time 100.0: hardlockup_check, cpu1 stores its state + Time 100.1: cpu 1 heartbeat + Time 100.2: Hard Lockup on cpu1 + Time 110.0: hardlockup_check, cpu1 stores its state (misses lockup as state changed) + Time 120.0: hardlockup_check, cpu1 checks the state again, should be the same, declares lockup + + Time to detection: ~20 seconds + +Hardlockup Detector (Buddy) +--------------------------- + +On architectures or configurations where NMI perf events are not +available (or disabled), the kernel may use the "buddy" hardlockup +detector. This mechanism requires SMP (Symmetric Multi-Processing). + +In this mode, each CPU is assigned a "buddy" CPU to monitor. The +monitoring CPU runs its own hrtimer (the same one used for softlockup +detection) and checks if the buddy CPU's hrtimer interrupt count has +increased. + +To ensure timeliness and avoid false positives, the buddy system performs +checks at every hrtimer interval (2*watchdog_thresh/5, which is 4 seconds +by default). It uses a missed-interrupt threshold of 3. If the buddy's +interrupt count has not changed for 3 consecutive checks, it is assumed +that the buddy CPU is hardlocked (interrupts disabled). The monitoring +CPU will then trigger the hardlockup response (warning or panic). + +**Detection Overhead (Buddy):** + +With a default check interval of 4 seconds (watchdog_thresh = 10): + +* **Best case:** Lockup occurs just before a check. + Detected in ~8s (0s till 1st check + 4s till 2nd + 4s till 3rd). +* **Worst case:** Lockup occurs just after a check. + Detected in ~12s (4s till 1st check + 4s till 2nd + 4s till 3rd). + +**Limitations of the Buddy Detector:** + +1. **All-CPU Lockup:** If all CPUs lock up simultaneously, the buddy + detector cannot detect the condition because the monitoring CPUs + are also frozen. +2. **Stack Traces:** Unlike the NMI detector, the buddy detector + cannot directly interrupt the locked CPU to grab a stack trace. + It relies on architecture-specific mechanisms (like NMI backtrace + support) to try and retrieve the status of the locked CPU. If + such support is missing, the log may only show that a lockup + occurred without providing the locked CPU's stack. + +Watchdog Core Exclusion +======================= By default, the watchdog runs on all online cores. However, on a kernel configured with NO_HZ_FULL, by default the watchdog runs only diff --git a/Documentation/admin-guide/mm/damon/lru_sort.rst b/Documentation/admin-guide/mm/damon/lru_sort.rst index a7dea7c75a9b..14cc6b2db897 100644 --- a/Documentation/admin-guide/mm/damon/lru_sort.rst +++ b/Documentation/admin-guide/mm/damon/lru_sort.rst @@ -79,6 +79,10 @@ of parametrs except ``enabled`` again. Once the re-reading is done, this parameter is set as ``N``. If invalid parameters are found while the re-reading, DAMON_LRU_SORT will be disabled. +Once ``Y`` is written to this parameter, the user must not write to any +parameters until reading ``commit_inputs`` again returns ``N``. If users +violate this rule, the kernel may exhibit undefined behavior. + active_mem_bp ------------- diff --git a/Documentation/admin-guide/mm/damon/reclaim.rst b/Documentation/admin-guide/mm/damon/reclaim.rst index 47854c461706..d7a0225b4950 100644 --- a/Documentation/admin-guide/mm/damon/reclaim.rst +++ b/Documentation/admin-guide/mm/damon/reclaim.rst @@ -71,6 +71,10 @@ of parametrs except ``enabled`` again. Once the re-reading is done, this parameter is set as ``N``. If invalid parameters are found while the re-reading, DAMON_RECLAIM will be disabled. +Once ``Y`` is written to this parameter, the user must not write to any +parameters until reading ``commit_inputs`` again returns ``N``. If users +violate this rule, the kernel may exhibit undefined behavior. + min_age ------- diff --git a/Documentation/admin-guide/mm/kho.rst b/Documentation/admin-guide/mm/kho.rst index cb9a20f64920..2c26e560bd78 100644 --- a/Documentation/admin-guide/mm/kho.rst +++ b/Documentation/admin-guide/mm/kho.rst @@ -42,6 +42,45 @@ For example, if you used ``reserve_mem`` command line parameter to create an early memory reservation, the new kernel will have that memory at the same physical address as the old kernel. +Kexec Metadata +============== + +KHO automatically tracks metadata about the kexec chain, passing information +about the previous kernel to the next kernel. This feature helps diagnose +bugs that only reproduce when kexecing from specific kernel versions. + +On each KHO kexec, the kernel logs the previous kernel's version and the +number of kexec reboots since the last cold boot:: + + [ 0.000000] KHO: exec from: 6.19.0-rc4-next-20260107 (count 1) + +The metadata includes: + +``previous_release`` + The kernel version string (from ``uname -r``) of the kernel that + initiated the kexec. + +``kexec_count`` + The number of kexec boots since the last cold boot. On cold boot, + this counter starts at 0 and increments with each kexec. This helps + identify issues that only manifest after multiple consecutive kexec + reboots. + +Use Cases +--------- + +This metadata is particularly useful for debugging kexec transition bugs, +where a buggy kernel kexecs into a new kernel and the bug manifests only +in the second kernel. Examples of such bugs include: + +- Memory corruption from the previous kernel affecting the new kernel +- Incorrect hardware state left by the previous kernel +- Firmware/ACPI state issues that only appear in kexec scenarios + +At scale, correlating crashes to the previous kernel version enables +faster root cause analysis when issues only occur in specific kernel +transition scenarios. + debugfs Interfaces ================== @@ -80,5 +119,5 @@ stabilized. it finished to interpret their metadata. ``/sys/kernel/debug/kho/in/sub_fdts/`` - Similar to ``kho/out/sub_fdts/``, but contains sub FDT blobs + Similar to ``kho/out/sub_fdts/``, but contains sub blobs of KHO producers passed from the old kernel. diff --git a/Documentation/admin-guide/nfs/pnfs-block-server.rst b/Documentation/admin-guide/nfs/pnfs-block-server.rst index 20fe9f5117fe..7667dd2e17f1 100644 --- a/Documentation/admin-guide/nfs/pnfs-block-server.rst +++ b/Documentation/admin-guide/nfs/pnfs-block-server.rst @@ -40,3 +40,33 @@ how to translate the device into a serial number from SCSI EVPD 0x80:: echo "fencing client ${CLIENT} serial ${EVPD}" >> /var/log/pnfsd-fence.log EOF + +If the nfsd server needs to fence a non-responding client and the +fencing operation fails, the server logs a warning message in the +system log with the following format: + + FENCE failed client[IP_address] clid[#n] device[dev_name] + + where: + + - IP_address: refers to the IP address of the affected client. + - #n: indicates the unique client identifier. + - dev_name: specifies the name of the block device related + to the fencing attempt. + +The server will repeatedly retry the operation indefinitely. During +this time, access to the affected file is restricted for all other +clients. This is to prevent potential data corruption if multiple +clients access the same file simultaneously. + +To restore access to the affected file for other clients, the admin +needs to take the following actions: + + - shutdown or power off the client being fenced. + - manually expire the client to release all its state on the server:: + + echo 'expire' > /proc/fs/nfsd/clients/clid/ctl + + where: + + - clid: is the unique client identifier displayed in the system log. diff --git a/Documentation/admin-guide/nfs/pnfs-scsi-server.rst b/Documentation/admin-guide/nfs/pnfs-scsi-server.rst index b2eec2288329..b202508d281d 100644 --- a/Documentation/admin-guide/nfs/pnfs-scsi-server.rst +++ b/Documentation/admin-guide/nfs/pnfs-scsi-server.rst @@ -22,3 +22,34 @@ option and the underlying SCSI device support persistent reservations. On the client make sure the kernel has the CONFIG_PNFS_BLOCK option enabled, and the file system is mounted using the NFSv4.1 protocol version (mount -o vers=4.1). + +If the nfsd server needs to fence a non-responding client and the +fencing operation fails, the server logs a warning message in the +system log with the following format: + + FENCE failed client[IP_address] clid[#n] device[dev_name] + + where: + + - IP_address: refers to the IP address of the affected client. + - #n: indicates the unique client identifier. + - dev_name: specifies the name of the block device related + to the fencing attempt. + +The server will repeatedly retry the operation indefinitely. During +this time, access to the affected file is restricted for all other +clients. This is to prevent potential data corruption if multiple +clients access the same file simultaneously. + +To restore access to the affected file for other clients, the admin +needs to take the following actions: + + - shutdown or power off the client being fenced. + - manually expire the client to release all its state on the server:: + + echo 'expire' > /proc/fs/nfsd/clients/clid/ctl + + where: + + - clid: is the unique client identifier displayed in the system log. + diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 9aed74e65cf4..c6994e55d141 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -418,7 +418,8 @@ hung_task_detect_count ====================== Indicates the total number of tasks that have been detected as hung since -the system boot. +the system boot or since the counter was reset. The counter is zeroed when +a value of 0 is written. This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled. diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst index 4e0e8898c5d4..211119ce7adc 100644 --- a/Documentation/arch/arm64/silicon-errata.rst +++ b/Documentation/arch/arm64/silicon-errata.rst @@ -202,6 +202,8 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | ARM | Neoverse-V3AE | #3312417 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | C1-Pro | #4193714 | ARM64_ERRATUM_4193714 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | MMU-500 | #841119,826419 | ARM_SMMU_MMU_500_CPRE_ERRATA| | | | #562869,1047329 | | +----------------+-----------------+-----------------+-----------------------------+ diff --git a/Documentation/arch/riscv/cmodx.rst b/Documentation/arch/riscv/cmodx.rst index 40ba53bed5df..cbfa812a11b4 100644 --- a/Documentation/arch/riscv/cmodx.rst +++ b/Documentation/arch/riscv/cmodx.rst @@ -21,13 +21,13 @@ call at each patchable function entry, and patches it dynamically at runtime to enable or disable the redirection. In the case of RISC-V, 2 instructions, AUIPC + JALR, are required to compose a function call. However, it is impossible to patch 2 instructions and expect that a concurrent read-side executes them -without a race condition. This series makes atmoic code patching possible in +without a race condition. This series makes atomic code patching possible in RISC-V ftrace. Kernel preemption makes things even worse as it allows the old state to persist across the patching process with stop_machine(). In order to get rid of stop_machine() and run dynamic ftrace with full kernel preemption, we partially initialize each patchable function entry at boot-time, -setting the first instruction to AUIPC, and the second to NOP. Now, atmoic +setting the first instruction to AUIPC, and the second to NOP. Now, atomic patching is possible because the kernel only has to update one instruction. According to Ziccif, as long as an instruction is naturally aligned, the ISA guarantee an atomic update. @@ -36,8 +36,8 @@ By fixing down the first instruction, AUIPC, the range of the ftrace trampoline is limited to +-2K from the predetermined target, ftrace_caller, due to the lack of immediate encoding space in RISC-V. To address the issue, we introduce CALL_OPS, where an 8B naturally align metadata is added in front of each -pacthable function. The metadata is resolved at the first trampoline, then the -execution can be derect to another custom trampoline. +patchable function. The metadata is resolved at the first trampoline, then the +execution can be directed to another custom trampoline. CMODX in the User Space ----------------------- diff --git a/Documentation/arch/riscv/zicfilp.rst b/Documentation/arch/riscv/zicfilp.rst index ab7d8e62ddaf..12b35969d17a 100644 --- a/Documentation/arch/riscv/zicfilp.rst +++ b/Documentation/arch/riscv/zicfilp.rst @@ -78,7 +78,7 @@ the program. Per-task indirect branch tracking state can be monitored and controlled via the :c:macro:`PR_GET_CFI` and :c:macro:`PR_SET_CFI` -``prctl()` arguments (respectively), by supplying +``prctl()`` arguments (respectively), by supplying :c:macro:`PR_CFI_BRANCH_LANDING_PADS` as the second argument. These are architecture-agnostic, and will return -EINVAL if the underlying functionality is not supported. diff --git a/Documentation/arch/s390/pci.rst b/Documentation/arch/s390/pci.rst index d5755484d8e7..80f4ba193159 100644 --- a/Documentation/arch/s390/pci.rst +++ b/Documentation/arch/s390/pci.rst @@ -6,6 +6,7 @@ S/390 PCI Authors: - Pierre Morel + - Niklas Schnelle Copyright, IBM Corp. 2020 @@ -27,14 +28,16 @@ Command line parameters debugfs entries --------------- -The S/390 debug feature (s390dbf) generates views to hold various debug results in sysfs directories of the form: +The S/390 debug feature (s390dbf) generates views to hold various debug results +in sysfs directories of the form: * /sys/kernel/debug/s390dbf/pci_*/ For example: - /sys/kernel/debug/s390dbf/pci_msg/sprintf - Holds messages from the processing of PCI events, like machine check handling + + holds messages from the processing of PCI events, like machine check handling and setting of global functionality, like UID checking. Change the level of logging to be more or less verbose by piping @@ -47,87 +50,141 @@ Sysfs entries Entries specific to zPCI functions and entries that hold zPCI information. -* /sys/bus/pci/slots/XXXXXXXX +* /sys/bus/pci/slots/XXXXXXXX: - The slot entries are set up using the function identifier (FID) of the - PCI function. The format depicted as XXXXXXXX above is 8 hexadecimal digits - with 0 padding and lower case hexadecimal digits. + The slot entries are set up using the function identifier (FID) of the PCI + function as slot name. The format depicted as XXXXXXXX above is 8 hexadecimal + digits with 0 padding and lower case hexadecimal digits. - /sys/bus/pci/slots/XXXXXXXX/power + In addition to using the FID as the name of the slot, the slot directory + also contains the following s390-specific slot attributes. + + - uid: + The User-defined identifier (UID) of the function which may be configured + by this slot. See also the corresponding attribute of the device. + A physical function that currently supports a virtual function cannot be powered off until all virtual functions are removed with: - echo 0 > /sys/bus/pci/devices/XXXX:XX:XX.X/sriov_numvf + echo 0 > /sys/bus/pci/devices/DDDD:BB:dd.f/sriov_numvf -* /sys/bus/pci/devices/XXXX:XX:XX.X/ +* /sys/bus/pci/devices/DDDD:BB:dd.f/: - - function_id - A zPCI function identifier that uniquely identifies the function in the Z server. + - function_id: + The zPCI function identifier (FID) is a 32-bit hexadecimal value that + uniquely identifies the PCI function. Unless the hypervisor provides + a virtual FID e.g. on KVM this identifier is unique across the machine even + between different partitions. - - function_handle - Low-level identifier used for a configured PCI function. - It might be useful for debugging. + - function_handle: + This 32-bit hexadecimal value is a low-level identifier used for a PCI + function. Note that the function handle may be changed and become invalid + on PCI events and when enabling/disabling the PCI function. - - pchid - Model-dependent location of the I/O adapter. + - pchid: + This 16-bit hexadecimal value encodes a model-dependent location for + the PCI function. - - pfgid - PCI function group ID, functions that share identical functionality + - pfgid: + PCI function group ID; functions that share identical functionality use a common identifier. A PCI group defines interrupts, IOMMU, IOTLB, and DMA specifics. - - vfn + - vfn: The virtual function number, from 1 to N for virtual functions, 0 for physical functions. - - pft - The PCI function type + - pft: + The PCI function type is an s390-specific type attribute. It indicates + a more general, usage oriented, type than PCI Specification + class/vendor/device identifiers. That is PCI functions with the same pft + value may be backed by different hardware implementations. At the same time + apart from unclassified functions (pft is 0x00) the same pft value + generally implies a similar usage model. At the same time the same + PCI hardware device may appear with different pft values when in a + different usage model. For example NETD and NETH VFs may be implemented + by the same PCI hardware device but in NETD the parent Physical Function + is user managed while with NETH it is platform managed. - - port - The port corresponds to the physical port the function is attached to. - It also gives an indication of the physical function a virtual function - is attached to. + Currently the following PFT values are defined: - - uid - The user identifier (UID) may be defined as part of the machine - configuration or the z/VM or KVM guest configuration. If the accompanying - uid_is_unique attribute is 1 the platform guarantees that the UID is unique - within that instance and no devices with the same UID can be attached - during the lifetime of the system. + - 0x00 (UNC): Unclassified + - 0x02 (ROCE): RoCE Express + - 0x05 (ISM): Internal Shared Memory + - 0x0a (ROC2): RoCE Express 2 + - 0x0b (NVMe): NVMe + - 0x0c (NETH): Network Express hybrid + - 0x0d (CNW): Cloud Network Adapter + - 0x0f (NETD): Network Express direct - - uid_is_unique - Indicates whether the user identifier (UID) is guaranteed to be and remain - unique within this Linux instance. + - port: + The port is a decimal value corresponding to the physical port the function + is attached to. Virtual Functions (VFs) share the port with their parent + Physical Function (PF). A value of 0 indicates that the port attribute is + not applicable for that PCI function type. - - pfip/segmentX + - uid: + The user-defined identifier (UID) for a PCI function is a 32-bit + hexadecimal value. It is defined on a per instance basis as part of the + partition, KVM guest, or z/VM guest configuration. If UID Checking is + enabled the platform ensures that the UID is unique within that instance + and no two PCI functions with the same UID will be visible to the instance. + + Independent of this guarantee and unlike the function ID (FID) the UID may + be the same in different partitions within the same machine. This allows to + create PCI configurations in multiple partitions to be identical in the + UID-namespace. + + - uid_is_unique: + A 0 or 1 flag indicating whether the user-defined identifier (UID) is + guaranteed to be and remain unique within this Linux instance. This + platform feature is called UID Checking. + + - pfip/segmentX: The segments determine the isolation of a function. They correspond to the physical path to the function. The more the segments are different, the more the functions are isolated. + - fidparm: + Contains an 8-bit-per-PCI function parameter field in hexadecimal provided + by the platform. The meaning of this field is PCI function type specific. + For NETH VFs a value of 0x01 indicates that the function supports + promiscuous mode. + +* /sys/firmware/clp/uid_checking: + + In addition to the per-device uid_is_unique attribute this presents a + global indication of whether UID Checking is enabled. This allows users + to check for UID Checking even when no PCI functions are configured. + Enumeration and hotplug ======================= The PCI address consists of four parts: domain, bus, device and function, -and is of this form: DDDD:BB:dd.f +and is of this form: DDDD:BB:dd.f. -* When not using multi-functions (norid is set, or the firmware does not - support multi-functions): +* For a PCI function for which the platform does not expose the RID, the + pci=norid kernel parameter is used, or a so-called isolated Virtual Function + which does have RID information but is used without its parent Physical + Function being part of the same PCI configuration: - There is only one function per domain. - - The domain is set from the zPCI function's UID as defined during the - LPAR creation. + - The domain is set from the zPCI function's UID if UID Checking is on; + otherwise the domain ID is generated dynamically and is not stable + across reboots or hot plug. -* When using multi-functions (norid parameter is not set), - zPCI functions are addressed differently: +* For a PCI function for which the platform exposes the RID and which + is not an Isolated Virtual Function: - There is still only one bus per domain. - - There can be up to 256 functions per bus. + - There can be up to 256 PCI functions per bus. - - The domain part of the address of all functions for - a multi-Function device is set from the zPCI function's UID as defined - in the LPAR creation for the function zero. + - The domain part of the address of all functions within the same topology is + that of the configured PCI function with the lowest devfn within that + topology. - - New functions will only be ready for use after the function zero - (the function with devfn 0) has been enumerated. + - Virtual Functions generated by an SR-IOV capable Physical Function only + become visible once SR-IOV is enabled. diff --git a/Documentation/arch/x86/tdx.rst b/Documentation/arch/x86/tdx.rst index 61670e7df2f7..ff6b110291bc 100644 --- a/Documentation/arch/x86/tdx.rst +++ b/Documentation/arch/x86/tdx.rst @@ -60,44 +60,18 @@ Besides initializing the TDX module, a per-cpu initialization SEAMCALL must be done on one cpu before any other SEAMCALLs can be made on that cpu. -The kernel provides two functions, tdx_enable() and tdx_cpu_enable() to -allow the user of TDX to enable the TDX module and enable TDX on local -cpu respectively. - -Making SEAMCALL requires VMXON has been done on that CPU. Currently only -KVM implements VMXON. For now both tdx_enable() and tdx_cpu_enable() -don't do VMXON internally (not trivial), but depends on the caller to -guarantee that. - -To enable TDX, the caller of TDX should: 1) temporarily disable CPU -hotplug; 2) do VMXON and tdx_enable_cpu() on all online cpus; 3) call -tdx_enable(). For example:: - - cpus_read_lock(); - on_each_cpu(vmxon_and_tdx_cpu_enable()); - ret = tdx_enable(); - cpus_read_unlock(); - if (ret) - goto no_tdx; - // TDX is ready to use - -And the caller of TDX must guarantee the tdx_cpu_enable() has been -successfully done on any cpu before it wants to run any other SEAMCALL. -A typical usage is do both VMXON and tdx_cpu_enable() in CPU hotplug -online callback, and refuse to online if tdx_cpu_enable() fails. - User can consult dmesg to see whether the TDX module has been initialized. If the TDX module is initialized successfully, dmesg shows something like below:: [..] virt/tdx: 262668 KBs allocated for PAMT - [..] virt/tdx: module initialized + [..] virt/tdx: TDX-Module initialized If the TDX module failed to initialize, dmesg also shows it failed to initialize:: - [..] virt/tdx: module initialization failed ... + [..] virt/tdx: TDX-Module initialization failed ... TDX Interaction to Other Kernel Components ------------------------------------------ @@ -129,9 +103,9 @@ CPU Hotplug ~~~~~~~~~~~ TDX module requires the per-cpu initialization SEAMCALL must be done on -one cpu before any other SEAMCALLs can be made on that cpu. The kernel -provides tdx_cpu_enable() to let the user of TDX to do it when the user -wants to use a new cpu for TDX task. +one cpu before any other SEAMCALLs can be made on that cpu. The kernel, +via the CPU hotplug framework, performs the necessary initialization when +a CPU is first brought online. TDX doesn't support physical (ACPI) CPU hotplug. During machine boot, TDX verifies all boot-time present logical CPUs are TDX compatible before diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst index 75e6c078e0e7..6d497e720998 100644 --- a/Documentation/bpf/kfuncs.rst +++ b/Documentation/bpf/kfuncs.rst @@ -462,6 +462,20 @@ In order to accommodate such requirements, the verifier will enforce strict PTR_TO_BTF_ID type matching if two types have the exact same name, with one being suffixed with ``___init``. +2.8 Accessing arena memory through kfunc arguments +-------------------------------------------------- + +A read or write at any address inside an arena does not oops the kernel. +Unallocated arena pages are lazily backed by a scratch page and the +access is reported through the program's BPF stream as an error. Only +the BPF program's correctness is affected; the kernel itself remains +intact. + +The arena is followed by a ``GUARD_SZ / 2`` (32 KiB) guard region that +is also covered by this recovery. A kfunc handed an arena pointer may +therefore access up to ``GUARD_SZ / 2`` past it without bounds-checking +against the arena. Larger accesses must verify the range explicitly. + .. _BPF_kfunc_lifecycle_expectations: 3. kfunc lifecycle expectations diff --git a/Documentation/crypto/index.rst b/Documentation/crypto/index.rst index 4ee667c446f9..705f186d662b 100644 --- a/Documentation/crypto/index.rst +++ b/Documentation/crypto/index.rst @@ -13,6 +13,7 @@ for cryptographic use cases, as well as programming examples. :caption: Table of contents :maxdepth: 2 + libcrypto intro api-intro architecture @@ -27,4 +28,3 @@ for cryptographic use cases, as well as programming examples. descore-readme device_drivers/index krb5 - sha3 diff --git a/Documentation/crypto/libcrypto-blockcipher.rst b/Documentation/crypto/libcrypto-blockcipher.rst new file mode 100644 index 000000000000..dd5ce2f8b515 --- /dev/null +++ b/Documentation/crypto/libcrypto-blockcipher.rst @@ -0,0 +1,19 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Block ciphers +============= + +AES +--- + +Support for the AES block cipher. + +.. kernel-doc:: include/crypto/aes.h + +DES +--- + +Support for the DES block cipher. This algorithm is obsolete and is supported +only for backwards compatibility. + +.. kernel-doc:: include/crypto/des.h diff --git a/Documentation/crypto/libcrypto-hash.rst b/Documentation/crypto/libcrypto-hash.rst new file mode 100644 index 000000000000..4248e6fdc952 --- /dev/null +++ b/Documentation/crypto/libcrypto-hash.rst @@ -0,0 +1,86 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Hash functions, MACs, and XOFs +============================== + +AES-CMAC and AES-XCBC-MAC +------------------------- + +Support for the AES-CMAC and AES-XCBC-MAC message authentication codes. + +.. kernel-doc:: include/crypto/aes-cbc-macs.h + +BLAKE2b +------- + +Support for the BLAKE2b cryptographic hash function. + +.. kernel-doc:: include/crypto/blake2b.h + +BLAKE2s +------- + +Support for the BLAKE2s cryptographic hash function. + +.. kernel-doc:: include/crypto/blake2s.h + +GHASH and POLYVAL +----------------- + +Support for the GHASH and POLYVAL universal hash functions. These algorithms +are used only as internal components of other algorithms. + +.. kernel-doc:: include/crypto/gf128hash.h + +MD5 +--- + +Support for the MD5 cryptographic hash function and HMAC-MD5. This algorithm is +obsolete and is supported only for backwards compatibility. + +.. kernel-doc:: include/crypto/md5.h + +NH +-- + +Support for the NH universal hash function. This algorithm is used only as an +internal component of other algorithms. + +.. kernel-doc:: include/crypto/nh.h + +Poly1305 +-------- + +Support for the Poly1305 universal hash function. This algorithm is used only +as an internal component of other algorithms. + +.. kernel-doc:: include/crypto/poly1305.h + +SHA-1 +----- + +Support for the SHA-1 cryptographic hash function and HMAC-SHA1. This algorithm +is obsolete and is supported only for backwards compatibility. + +.. kernel-doc:: include/crypto/sha1.h + +SHA-2 +----- + +Support for the SHA-2 family of cryptographic hash functions, including SHA-224, +SHA-256, SHA-384, and SHA-512. This also includes their corresponding HMACs: +HMAC-SHA224, HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512. + +.. kernel-doc:: include/crypto/sha2.h + +SHA-3 +----- + +The SHA-3 functions are documented in :ref:`sha3`. + +SM3 +--- + +Support for the SM3 cryptographic hash function. + +.. kernel-doc:: include/crypto/sm3.h diff --git a/Documentation/crypto/libcrypto-signature.rst b/Documentation/crypto/libcrypto-signature.rst new file mode 100644 index 000000000000..e80d59fa51b6 --- /dev/null +++ b/Documentation/crypto/libcrypto-signature.rst @@ -0,0 +1,11 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Digital signature algorithms +============================ + +ML-DSA +------ + +Support for the ML-DSA digital signature algorithm. + +.. kernel-doc:: include/crypto/mldsa.h diff --git a/Documentation/crypto/libcrypto-utils.rst b/Documentation/crypto/libcrypto-utils.rst new file mode 100644 index 000000000000..9d833f47ed39 --- /dev/null +++ b/Documentation/crypto/libcrypto-utils.rst @@ -0,0 +1,6 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Utility functions +================= + +.. kernel-doc:: include/crypto/utils.h diff --git a/Documentation/crypto/libcrypto.rst b/Documentation/crypto/libcrypto.rst new file mode 100644 index 000000000000..a1557d45b0e5 --- /dev/null +++ b/Documentation/crypto/libcrypto.rst @@ -0,0 +1,165 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +============== +Crypto library +============== + +``lib/crypto/`` provides faster and easier access to cryptographic algorithms +than the traditional crypto API. + +Each cryptographic algorithm is supported via a set of dedicated functions. +"Crypto agility", where needed, is left to calling code. + +The crypto library functions are intended to be boring and straightforward, and +to follow familiar conventions. Their primary documentation is their (fairly +extensive) kernel-doc. This page just provides some extra high-level context. + +Note that the crypto library isn't entirely new. ``lib/`` has contained some +crypto functions since 2005. Rather, it's just an approach that's been expanded +over time as it's been found to work well. It also largely just matches how the +kernel already does things elsewhere. + +Scope and intended audience +=========================== + +The crypto library documentation is primarily meant for kernel developers who +need to use a particular cryptographic algorithm(s) in kernel code. For +example, "I just need to compute a SHA-256 hash." A secondary audience is +developers working on the crypto algorithm implementations themselves. + +If you're looking for more general information about cryptography, like the +differences between the different crypto algorithms or how to select an +appropriate algorithm, you should refer to external sources which cover that +type of information much more comprehensively. If you need help selecting +algorithms for a new kernel feature that doesn't already have its algorithms +predefined, please reach out to ``linux-crypto@vger.kernel.org`` for advice. + +Code organization +================= + +- ``lib/crypto/*.c``: the crypto algorithm implementations + +- ``lib/crypto/$(SRCARCH)/``: architecture-specific code for crypto algorithms. + It is here rather than somewhere in ``arch/`` partly because this allows + generic and architecture-optimized code to be easily built into a single + loadable module (when the algorithm is set to 'm' in the kconfig). + +- ``lib/crypto/tests/``: KUnit tests for the crypto algorithms + +- ``include/crypto/``: crypto headers, for both the crypto library and the + traditional crypto API + +Generally, there is one kernel module per algorithm. Sometimes related +algorithms are grouped into one module. There is intentionally no common +framework, though there are some utility functions that multiple algorithms use. + +Each algorithm module is controlled by a tristate kconfig symbol +``CRYPTO_LIB_$(ALGORITHM)``. As is the norm for library functions in the +kernel, these are hidden symbols which don't show up in the kconfig menu. +Instead, they are just selected by all the kconfig symbols that need them. + +Many of the algorithms have multiple implementations: a generic implementation +and architecture-optimized implementation(s). Each module initialization +function, or initcall in the built-in case, automatically enables the best +implementation based on the available CPU features. + +Note that the crypto library doesn't use the ``crypto/``, +``arch/$(SRCARCH)/crypto/``, or ``drivers/crypto/`` directories. These +directories are used by the traditional crypto API. When possible, algorithms +in the traditional crypto API are implemented by calls into the library. + +Advantages +========== + +Some of the advantages of the library over the traditional crypto API are: + +- The library functions tend to be much easier to use. For example, a hash + value can be computed using only a single function call. Most of the library + functions always succeed and return void, eliminating the need to write + error-handling code. Most also accept standard virtual addresses, rather than + scatterlists which are difficult and less efficient to work with. + +- The library functions are usually faster, especially for short inputs. They + call the crypto algorithms directly without inefficient indirect calls, memory + allocations, string parsing, lookups in an algorithm registry, and other + unnecessary API overhead. Architecture-optimized code is enabled by default. + +- The library functions use standard link-time dependencies instead of + error-prone dynamic loading by name. There's no need for workarounds such as + forcing algorithms to be built-in or adding module soft dependencies. + +- The library focuses on the approach that works the best on the vast majority + of systems: CPU-based implementations of the crypto algorithms, utilizing + on-CPU acceleration (such as AES instructions) when available. + +- The library uses standard KUnit tests, rather than custom ad-hoc tests. + +- The library tends to have higher assurance implementations of the crypto + algorithms. This is both due to its simpler design and because more of its + code is being regularly tested. + +- The library supports features that don't fit into the rigid framework of the + traditional crypto API, for example interleaved hashing and XOFs. + +When to use it +============== + +In-kernel users should use the library (rather than the traditional crypto API) +whenever possible. Many subsystems have already been converted. It usually +simplifies their code significantly and improves performance. + +Some kernel features allow userspace to provide an arbitrary string that selects +an arbitrary algorithm from the traditional crypto API by name. These features +generally will have to keep using the traditional crypto API for backwards +compatibility. + +Note: new kernel features shouldn't support every algorithm, but rather make a +deliberate choice about what algorithm(s) to support. History has shown that +making a deliberate, thoughtful choice greatly simplifies code maintenance, +reduces the chance for mistakes (such as using an obsolete, insecure, or +inappropriate algorithm), and makes your feature easier to use. + +Testing +======= + +The crypto library uses standard KUnit tests. Like many of the kernel's other +KUnit tests, they are included in the set of tests that is run by +``tools/testing/kunit/kunit.py run --alltests``. + +A ``.kunitconfig`` file is also provided to run just the crypto library tests. +For example, here's how to run them in user-mode Linux: + +.. code-block:: sh + + tools/testing/kunit/kunit.py run --kunitconfig=lib/crypto/ + +Many of the crypto algorithms have architecture-optimized implementations. +Testing those requires building an appropriate kernel and running the tests +either in QEMU or on appropriate hardware. Here's one example with QEMU: + +.. code-block:: sh + + tools/testing/kunit/kunit.py run --kunitconfig=lib/crypto/ --arch=arm64 --make_options LLVM=1 + +Depending on the code being tested, flags may need to be passed to QEMU to +emulate the correct type of hardware for the code to be reached. + +Since correctness is essential in cryptographic code, new architecture-optimized +code is accepted only if it can be tested in QEMU. + +Note: the crypto library also includes FIPS 140 self-tests. These are +lightweight, are designed specifically to meet FIPS 140 requirements, and exist +*only* to meet those requirements. Normal testing done by kernel developers and +integrators should use the much more comprehensive KUnit tests instead. + +API documentation +================= + +.. toctree:: + :maxdepth: 2 + + libcrypto-blockcipher + libcrypto-hash + libcrypto-signature + libcrypto-utils + sha3 diff --git a/Documentation/crypto/sha3.rst b/Documentation/crypto/sha3.rst index 37640f295118..250669c98f6b 100644 --- a/Documentation/crypto/sha3.rst +++ b/Documentation/crypto/sha3.rst @@ -1,5 +1,7 @@ .. SPDX-License-Identifier: GPL-2.0-or-later +.. _sha3: + ========================== SHA-3 Algorithm Collection ========================== diff --git a/Documentation/devicetree/bindings/arm/altera.yaml b/Documentation/devicetree/bindings/arm/altera.yaml index 13a3a9696821..206686f3eebc 100644 --- a/Documentation/devicetree/bindings/arm/altera.yaml +++ b/Documentation/devicetree/bindings/arm/altera.yaml @@ -84,6 +84,12 @@ properties: - altr,socfpga-stratix10-swvp - const: altr,socfpga-stratix10 + - description: Stratix 10 SoCDK eMMC variant + items: + - const: altr,socfpga-stratix10-socdk-emmc + - const: altr,socfpga-stratix10-socdk + - const: altr,socfpga-stratix10 + - description: AgileX boards items: - enum: @@ -105,6 +111,7 @@ properties: - enum: - intel,socfpga-agilex5-socdk - intel,socfpga-agilex5-socdk-013b + - intel,socfpga-agilex5-socdk-modular - intel,socfpga-agilex5-socdk-nand - const: intel,socfpga-agilex5 diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml index 2a91670ccb8c..949444aba1f8 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-cti.yaml @@ -128,6 +128,9 @@ properties: "#address-cells": const: 1 + access-controllers: + maxItems: 1 + patternProperties: '^trig-conns@([0-9]+)$': type: object diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml index b74db15e5f8a..b0693cd46d27 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-dynamic-funnel.yaml @@ -78,6 +78,9 @@ properties: description: Output connection to CoreSight Trace bus $ref: /schemas/graph.yaml#/properties/port + access-controllers: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml index 71f2e1ed27e5..10ebbbeadf93 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-etm.yaml @@ -118,6 +118,9 @@ properties: description: Output connection from the ETM to CoreSight Trace bus. $ref: /schemas/graph.yaml#/properties/port + access-controllers: + maxItems: 1 + required: - compatible - clocks diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml index 378380c3f5aa..f243e76f597f 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-stm.yaml @@ -73,6 +73,9 @@ properties: description: Output connection to the CoreSight Trace bus. $ref: /schemas/graph.yaml#/properties/port + access-controllers: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml index 96dd5b5f771a..9dc096698c65 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-tmc.yaml @@ -128,6 +128,9 @@ properties: - const: tracedata - const: metadata + access-controllers: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml b/Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml index a207f6899e67..29bbc3961fdf 100644 --- a/Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml +++ b/Documentation/devicetree/bindings/arm/arm,coresight-tpiu.yaml @@ -70,6 +70,9 @@ properties: description: Input connection from the CoreSight Trace bus. $ref: /schemas/graph.yaml#/properties/port + access-controllers: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/arm/arm,corstone1000.yaml b/Documentation/devicetree/bindings/arm/arm,corstone1000.yaml index cff1cdaadb13..48ab3356e383 100644 --- a/Documentation/devicetree/bindings/arm/arm,corstone1000.yaml +++ b/Documentation/devicetree/bindings/arm/arm,corstone1000.yaml @@ -15,11 +15,11 @@ description: |+ provides a flexible compute architecture that combines Cortex‑A and Cortex‑M processors. - Support for Cortex‑A32, Cortex‑A35 and Cortex‑A53 processors. Two expansion - systems for M-Class (or other) processors for adding sensors, connectivity, - video, audio and machine learning at the edge System and security IPs to build - a secure SoC for a range of rich IoT applications, for example gateways, smart - cameras and embedded systems. + Support for Cortex‑A32, Cortex‑A35, Cortex‑A53 and Cortex-A320 processors. + Two expansion systems for M-Class (or other) processors for adding sensors, + connectivity, video, audio and machine learning at the edge System and + security IPs to build a secure SoC for a range of rich IoT applications, for + example gateways, smart cameras and embedded systems. Integrated Secure Enclave providing hardware Root of Trust and supporting seamless integration of the optional CryptoCell™-312 cryptographic @@ -39,6 +39,11 @@ properties: implementation of this system. See ARM ecosystems FVP's. items: - const: arm,corstone1000-fvp + - description: Corstone1000-A320 FVP is the Fixed Virtual Platform + implementation of this system with Cortex-A320 cores and Ethos-U85 + NPU. See ARM ecosystems FVP's. + items: + - const: arm,corstone1000-a320-fvp additionalProperties: true diff --git a/Documentation/devicetree/bindings/arm/arm,vexpress-juno.yaml b/Documentation/devicetree/bindings/arm/arm,vexpress-juno.yaml index ba04576f0ad6..95d4baa85506 100644 --- a/Documentation/devicetree/bindings/arm/arm,vexpress-juno.yaml +++ b/Documentation/devicetree/bindings/arm/arm,vexpress-juno.yaml @@ -119,6 +119,16 @@ properties: items: - const: arm,foundation-aarch64 - const: arm,vexpress + - description: Arm Zena Compute Subsystem Platforms + Arm Zena Compute Subsystem (CSS) is a compute platform targeting + the automotive sector. Arm Zena CSS is a high-performance Arm + Cortex-A720AE Application Processor system augmented with an Arm + Cortex-R82AE based Safety Island and real-time domain. + items: + - enum: + - arm,zena-css-fvp + - const: arm,zena-css + - const: arm,vexpress arm,vexpress,position: description: When daughterboards are stacked on one site, their position diff --git a/Documentation/devicetree/bindings/arm/arm,vexpress-scc.yaml b/Documentation/devicetree/bindings/arm/arm,vexpress-scc.yaml new file mode 100644 index 000000000000..9b8f7e0c4ea0 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/arm,vexpress-scc.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/arm,vexpress-scc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM Versatile Express Serial Configuration Controller + +maintainers: + - Liviu Dudau + - Sudeep Holla + +description: | + Test chips for ARM Versatile Express platform implement SCC (Serial + Configuration Controller) interface, used to set initial conditions + for the test chip. + + In some cases its registers are also mapped in normal address space + and can be used to obtain runtime information about the chip internals + (like silicon temperature sensors) and as interface to other subsystems + like platform configuration control and power management. + +properties: + compatible: + items: + - enum: + - arm,vexpress-scc,v2p-ca15_a7 + - const: arm,vexpress-scc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + +additionalProperties: false + +examples: + - | + bus { + #address-cells = <2>; + #size-cells = <2>; + + scc@7fff0000 { + compatible = "arm,vexpress-scc,v2p-ca15_a7", "arm,vexpress-scc"; + reg = <0 0x7fff0000 0 0x1000>; + interrupts = <0 95 4>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml b/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml index f9925a14680e..8ec7a3e74a21 100644 --- a/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml +++ b/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml @@ -35,6 +35,7 @@ properties: - ampere,mtjade-bmc - aspeed,ast2500-evb - asrock,altrad8-bmc + - asrock,ast2500-paul-ipmi-card - asrock,e3c246d4i-bmc - asrock,e3c256d4i-bmc - asrock,romed8hm3-bmc @@ -80,6 +81,7 @@ properties: - ampere,mtmitchell-bmc - aspeed,ast2600-evb - aspeed,ast2600-evb-a1 + - asus,ast2600-kommando-ipmi-card - asus,x4tf-bmc - facebook,anacapa-bmc - facebook,bletchley-bmc diff --git a/Documentation/devicetree/bindings/arm/atmel,at91rm9200-sdramc.yaml b/Documentation/devicetree/bindings/arm/atmel,at91rm9200-sdramc.yaml new file mode 100644 index 000000000000..ac7e0f454a34 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/atmel,at91rm9200-sdramc.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/atmel,at91rm9200-sdramc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip (Atmel) SDRAM / DDR Controller (RAMC / DDRAMC / UDDRC) + +maintainers: + - Nicolas Ferre + - Claudiu Beznea + +description: + The SDRAM/DDR Controller (often called RAMC or DDRAMC) in various + Atmel/Microchip ARM9 and Cortex-A5/A7 SoCs manages external + SDRAM / DDR memory. It is typically exposed as a syscon node for + register access from other drivers (e.g. for initialization or mode + configuration). No interrupts or clocks are usually required in the + binding. + +properties: + compatible: + oneOf: + - items: + - const: atmel,at91rm9200-sdramc + - const: syscon + - items: + - const: microchip,sama7d65-uddrc + - const: microchip,sama7g5-uddrc + - enum: + - atmel,at91sam9260-sdramc + - atmel,at91sam9g45-ddramc + - atmel,sama5d3-ddramc + - microchip,sam9x60-ddramc + - microchip,sam9x7-ddramc + - microchip,sama7g5-uddrc + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + items: + - const: ddrck + - const: mpddr + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + ramc@ffffe400 { + compatible = "atmel,at91sam9g45-ddramc"; + reg = <0xffffe400 0x200>; + clocks = <&pmc PMC_TYPE_SYSTEM 2>; + clock-names = "ddrck"; + }; +... diff --git a/Documentation/devicetree/bindings/arm/atmel,at91rm9200-st.yaml b/Documentation/devicetree/bindings/arm/atmel,at91rm9200-st.yaml new file mode 100644 index 000000000000..3f6a934a2a69 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/atmel,at91rm9200-st.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/atmel,at91rm9200-st.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Atmel System Timer + +maintainers: + - Nicolas Ferre + - Claudiu Beznea + +description: + The System Timer (ST) module in AT91RM9200 provides periodic tick and + alarm capabilities. It is exposed as a simple multi-function device + (simple-mfd + syscon) because it shares its register space and interrupt + with other System Controller blocks. + +properties: + compatible: + items: + - const: atmel,at91rm9200-st + - const: syscon + - const: simple-mfd + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + +patternProperties: + "^watchdog@[0-9a-f]+$": + $ref: /schemas/watchdog/atmel,at91rm9200-wdt.yaml# + +required: + - compatible + - reg + - interrupts + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + timer@fffffd00 { + compatible = "atmel,at91rm9200-st", "syscon", "simple-mfd"; + reg = <0xfffffd00 0x100>; + interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&slow_xtal>; + #address-cells = <1>; + #size-cells = <1>; + + watchdog@fffffd40 { + compatible = "atmel,at91rm9200-wdt"; + reg = <0xfffffd40 0x40>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/arm/atmel,at91sam9260-pit.yaml b/Documentation/devicetree/bindings/arm/atmel,at91sam9260-pit.yaml new file mode 100644 index 000000000000..d1bdc4a4f9e0 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/atmel,at91sam9260-pit.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/atmel,at91sam9260-pit.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Atmel AT91SAM9260 Periodic Interval Timer (PIT) + +maintainers: + - Nicolas Ferre + - Claudiu Beznea + +description: + The Periodic Interval Timer (PIT) is part of the System Controller of + various Microchip 32-bit ARM-based SoCs (formerly Atmel AT91 series). + It is a simple down-counter timer used mainly as the kernel tick source. + The PIT is clocked from the slow clock and shares a single IRQ line with + other System Controller peripherals. + +properties: + compatible: + const: atmel,at91sam9260-pit + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include + timer@fffffd30 { + compatible = "atmel,at91sam9260-pit"; + reg = <0xfffffd30 0x10>; + interrupts = <1 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk32k>; + }; +... diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.yaml b/Documentation/devicetree/bindings/arm/atmel-at91.yaml index 68d306d17c2a..bf161e0950ea 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.yaml +++ b/Documentation/devicetree/bindings/arm/atmel-at91.yaml @@ -243,6 +243,12 @@ properties: - const: microchip,lan9668 - const: microchip,lan966 + - description: Microchip LAN9696 EV23X71A Evaluation Board + items: + - const: microchip,ev23x71a + - const: microchip,lan9696 + - const: microchip,lan9691 + - description: Kontron KSwitch D10 MMT series items: - enum: diff --git a/Documentation/devicetree/bindings/arm/atmel-sysregs.txt b/Documentation/devicetree/bindings/arm/atmel-sysregs.txt deleted file mode 100644 index 5ce54f9befe6..000000000000 --- a/Documentation/devicetree/bindings/arm/atmel-sysregs.txt +++ /dev/null @@ -1,48 +0,0 @@ -Atmel system registers - -Chipid required properties: -- compatible: Should be "atmel,sama5d2-chipid" or "microchip,sama7g5-chipid" - "microchip,sama7d65-chipid" -- reg : Should contain registers location and length - -PIT Timer required properties: -- compatible: Should be "atmel,at91sam9260-pit" -- reg: Should contain registers location and length -- interrupts: Should contain interrupt for the PIT which is the IRQ line - shared across all System Controller members. - -PIT64B Timer required properties: -- compatible: Should be "microchip,sam9x60-pit64b" or - "microchip,sam9x7-pit64b", "microchip,sam9x60-pit64b" - "microchip,sama7d65-pit64b", "microchip,sam9x60-pit64b" -- reg: Should contain registers location and length -- interrupts: Should contain interrupt for PIT64B timer -- clocks: Should contain the available clock sources for PIT64B timer. - -System Timer (ST) required properties: -- compatible: Should be "atmel,at91rm9200-st", "syscon", "simple-mfd" -- reg: Should contain registers location and length -- interrupts: Should contain interrupt for the ST which is the IRQ line - shared across all System Controller members. -- clocks: phandle to input clock. -Its subnodes can be: -- watchdog: compatible should be "atmel,at91rm9200-wdt" - -RAMC SDRAM/DDR Controller required properties: -- compatible: Should be "atmel,at91rm9200-sdramc", "syscon" or - "atmel,at91sam9260-sdramc" or - "atmel,at91sam9g45-ddramc" or - "atmel,sama5d3-ddramc" or - "microchip,sam9x60-ddramc" or - "microchip,sama7g5-uddrc" or - "microchip,sama7d65-uddrc", "microchip,sama7g5-uddrc" or - "microchip,sam9x7-ddramc", "atmel,sama5d3-ddramc". -- reg: Should contain registers location and length - -Examples: - - ramc0: ramc@ffffe800 { - compatible = "atmel,at91sam9g45-ddramc"; - reg = <0xffffe800 0x200>; - }; - diff --git a/Documentation/devicetree/bindings/arm/axis.yaml b/Documentation/devicetree/bindings/arm/axis.yaml index 63e9aca85db7..3062901196a6 100644 --- a/Documentation/devicetree/bindings/arm/axis.yaml +++ b/Documentation/devicetree/bindings/arm/axis.yaml @@ -31,6 +31,12 @@ properties: - axis,artpec8-grizzly - const: axis,artpec8 + - description: Axis ARTPEC-9 SoC board + items: + - enum: + - axis,artpec9-alfred + - const: axis,artpec9 + additionalProperties: true ... diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml index 736b7ab1bd0a..5f5ff5e51e51 100644 --- a/Documentation/devicetree/bindings/arm/cpus.yaml +++ b/Documentation/devicetree/bindings/arm/cpus.yaml @@ -79,149 +79,162 @@ properties: All other bits in the reg cells must be set to 0. compatible: - enum: - - apm,potenza - - apm,strega - - apple,avalanche - - apple,blizzard - - apple,cyclone - - apple,firestorm - - apple,hurricane-zephyr - - apple,icestorm - - apple,mistral - - apple,monsoon - - apple,twister - - apple,typhoon - - arm,arm710t - - arm,arm720t - - arm,arm740t - - arm,arm7ej-s - - arm,arm7tdmi - - arm,arm7tdmi-s - - arm,arm9es - - arm,arm9ej-s - - arm,arm920t - - arm,arm922t - - arm,arm925 - - arm,arm926e-s - - arm,arm926ej-s - - arm,arm940t - - arm,arm946e-s - - arm,arm966e-s - - arm,arm968e-s - - arm,arm9tdmi - - arm,arm1020e - - arm,arm1020t - - arm,arm1022e - - arm,arm1026ej-s - - arm,arm1136j-s - - arm,arm1136jf-s - - arm,arm1156t2-s - - arm,arm1156t2f-s - - arm,arm1176jzf - - arm,arm1176jz-s - - arm,arm1176jzf-s - - arm,arm11mpcore - - arm,armv8 # Only for s/w models - - arm,c1-nano - - arm,c1-premium - - arm,c1-pro - - arm,c1-ultra - - arm,cortex-a5 - - arm,cortex-a7 - - arm,cortex-a8 - - arm,cortex-a9 - - arm,cortex-a12 - - arm,cortex-a15 - - arm,cortex-a17 - - arm,cortex-a32 - - arm,cortex-a34 - - arm,cortex-a35 - - arm,cortex-a53 - - arm,cortex-a55 - - arm,cortex-a57 - - arm,cortex-a65 - - arm,cortex-a72 - - arm,cortex-a73 - - arm,cortex-a75 - - arm,cortex-a76 - - arm,cortex-a77 - - arm,cortex-a78 - - arm,cortex-a78ae - - arm,cortex-a78c - - arm,cortex-a320 - - arm,cortex-a510 - - arm,cortex-a520 - - arm,cortex-a520ae - - arm,cortex-a710 - - arm,cortex-a715 - - arm,cortex-a720 - - arm,cortex-a720ae - - arm,cortex-a725 - - arm,cortex-m0 - - arm,cortex-m0+ - - arm,cortex-m1 - - arm,cortex-m3 - - arm,cortex-m4 - - arm,cortex-r4 - - arm,cortex-r5 - - arm,cortex-r7 - - arm,cortex-r52 - - arm,cortex-x1 - - arm,cortex-x1c - - arm,cortex-x2 - - arm,cortex-x3 - - arm,cortex-x4 - - arm,cortex-x925 - - arm,neoverse-e1 - - arm,neoverse-n1 - - arm,neoverse-n2 - - arm,neoverse-n3 - - arm,neoverse-v1 - - arm,neoverse-v2 - - arm,neoverse-v3 - - arm,neoverse-v3ae - - arm,rainier - - brcm,brahma-b15 - - brcm,brahma-b53 - - brcm,vulcan - - cavium,thunder - - cavium,thunder2 - - faraday,fa526 - - intel,sa110 - - intel,sa1100 - - marvell,feroceon - - marvell,mohawk - - marvell,pj4a - - marvell,pj4b - - marvell,sheeva-v5 - - marvell,sheeva-v7 - - nvidia,tegra132-denver - - nvidia,tegra186-denver - - nvidia,tegra194-carmel - - qcom,krait - - qcom,kryo - - qcom,kryo240 - - qcom,kryo250 - - qcom,kryo260 - - qcom,kryo280 - - qcom,kryo360 - - qcom,kryo385 - - qcom,kryo465 - - qcom,kryo468 - - qcom,kryo470 - - qcom,kryo485 - - qcom,kryo560 - - qcom,kryo570 - - qcom,kryo660 - - qcom,kryo670 - - qcom,kryo685 - - qcom,kryo780 - - qcom,oryon - - qcom,scorpion - - samsung,mongoose-m2 - - samsung,mongoose-m3 - - samsung,mongoose-m5 + oneOf: + - enum: + - apm,potenza + - apm,strega + - apple,avalanche + - apple,blizzard + - apple,cyclone + - apple,everest + - apple,firestorm + - apple,hurricane-zephyr + - apple,icestorm + - apple,mistral + - apple,monsoon + - apple,sawtooth + - apple,twister + - apple,typhoon + - arm,arm710t + - arm,arm720t + - arm,arm740t + - arm,arm7ej-s + - arm,arm7tdmi + - arm,arm7tdmi-s + - arm,arm9es + - arm,arm9ej-s + - arm,arm920t + - arm,arm922t + - arm,arm925 + - arm,arm926e-s + - arm,arm926ej-s + - arm,arm940t + - arm,arm946e-s + - arm,arm966e-s + - arm,arm968e-s + - arm,arm9tdmi + - arm,arm1020e + - arm,arm1020t + - arm,arm1022e + - arm,arm1026ej-s + - arm,arm1136j-s + - arm,arm1136jf-s + - arm,arm1156t2-s + - arm,arm1156t2f-s + - arm,arm1176jzf + - arm,arm1176jz-s + - arm,arm1176jzf-s + - arm,arm11mpcore + - arm,armv8 # Only for s/w models + - arm,c1-nano + - arm,c1-premium + - arm,c1-pro + - arm,c1-ultra + - arm,cortex-a5 + - arm,cortex-a7 + - arm,cortex-a8 + - arm,cortex-a9 + - arm,cortex-a12 + - arm,cortex-a15 + - arm,cortex-a17 + - arm,cortex-a32 + - arm,cortex-a34 + - arm,cortex-a35 + - arm,cortex-a53 + - arm,cortex-a55 + - arm,cortex-a57 + - arm,cortex-a65 + - arm,cortex-a72 + - arm,cortex-a73 + - arm,cortex-a75 + - arm,cortex-a76 + - arm,cortex-a77 + - arm,cortex-a78 + - arm,cortex-a78ae + - arm,cortex-a78c + - arm,cortex-a320 + - arm,cortex-a510 + - arm,cortex-a520 + - arm,cortex-a520ae + - arm,cortex-a710 + - arm,cortex-a715 + - arm,cortex-a720 + - arm,cortex-a720ae + - arm,cortex-a725 + - arm,cortex-m0 + - arm,cortex-m0+ + - arm,cortex-m1 + - arm,cortex-m3 + - arm,cortex-m4 + - arm,cortex-r4 + - arm,cortex-r5 + - arm,cortex-r7 + - arm,cortex-r52 + - arm,cortex-x1 + - arm,cortex-x1c + - arm,cortex-x2 + - arm,cortex-x3 + - arm,cortex-x4 + - arm,cortex-x925 + - arm,neoverse-e1 + - arm,neoverse-n1 + - arm,neoverse-n2 + - arm,neoverse-n3 + - arm,neoverse-v1 + - arm,neoverse-v2 + - arm,neoverse-v3 + - arm,neoverse-v3ae + - arm,rainier + - brcm,brahma-b15 + - brcm,brahma-b53 + - brcm,vulcan + - cavium,thunder + - cavium,thunder2 + - faraday,fa526 + - intel,sa110 + - intel,sa1100 + - marvell,feroceon + - marvell,mohawk + - marvell,pj4a + - marvell,pj4b + - marvell,sheeva-v5 + - marvell,sheeva-v7 + - nvidia,tegra132-denver + - nvidia,tegra186-denver + - nvidia,tegra194-carmel + - qcom,krait + - qcom,kryo240 + - qcom,kryo250 + - qcom,kryo260 + - qcom,kryo280 + - qcom,kryo360 + - qcom,kryo385 + - qcom,kryo465 + - qcom,kryo468 + - qcom,kryo470 + - qcom,kryo485 + - qcom,kryo560 + - qcom,kryo570 + - qcom,kryo660 + - qcom,kryo670 + - qcom,kryo685 + - qcom,kryo780 + - qcom,oryon-1-1 + - qcom,oryon-1-2 + - qcom,oryon-1-3 + - qcom,oryon-1-4 + - qcom,oryon-2-1 + - qcom,oryon-2-2 + - qcom,oryon-2-3 + - qcom,scorpion + - samsung,mongoose-m2 + - samsung,mongoose-m3 + - samsung,mongoose-m5 + - enum: + - qcom,kryo + - qcom,oryon + # Too generic, do not use in new code + deprecated: true enable-method: $ref: /schemas/types.yaml#/definitions/string diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,imx51-m4if.yaml b/Documentation/devicetree/bindings/arm/freescale/fsl,imx51-m4if.yaml index 1f515bea3959..6130b048de7b 100644 --- a/Documentation/devicetree/bindings/arm/freescale/fsl,imx51-m4if.yaml +++ b/Documentation/devicetree/bindings/arm/freescale/fsl,imx51-m4if.yaml @@ -15,6 +15,7 @@ properties: compatible: oneOf: - enum: + - fsl,imx25-aips - fsl,imx51-m4if - fsl,imx51-tigerp - fsl,imx51-aipstz diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,imx7ulp-pm.yaml b/Documentation/devicetree/bindings/arm/freescale/fsl,imx7ulp-pm.yaml index 9d377e193c12..7ad470260c0d 100644 --- a/Documentation/devicetree/bindings/arm/freescale/fsl,imx7ulp-pm.yaml +++ b/Documentation/devicetree/bindings/arm/freescale/fsl,imx7ulp-pm.yaml @@ -28,6 +28,9 @@ properties: reg: maxItems: 1 + '#clock-cells': + const: 1 + clocks: maxItems: 2 @@ -39,6 +42,7 @@ properties: required: - compatible - reg + - '#clock-cells' additionalProperties: false @@ -47,4 +51,5 @@ examples: smc1@40410000 { compatible = "fsl,imx7ulp-smc1"; reg = <0x40410000 0x1000>; + #clock-cells = <1>; }; diff --git a/Documentation/devicetree/bindings/arm/fsl.yaml b/Documentation/devicetree/bindings/arm/fsl.yaml index 5716d701292c..0023cd126807 100644 --- a/Documentation/devicetree/bindings/arm/fsl.yaml +++ b/Documentation/devicetree/bindings/arm/fsl.yaml @@ -996,6 +996,14 @@ properties: - const: engicam,icore-mx8mm # i.MX8MM Engicam i.Core MX8M Mini SoM - const: fsl,imx8mm + - description: Ka-Ro Electronics TX8M-1610 based boards + items: + - enum: + - gocontroll,moduline-iv-306-d + - gocontroll,moduline-mini-111 + - const: karo,tx8m-1610 + - const: fsl,imx8mm + - description: Kontron BL i.MX8MM (N801X S) Board items: - const: kontron,imx8mm-bl @@ -1041,6 +1049,13 @@ properties: - const: phytec,imx8mm-phycore-som # phyCORE-i.MX8MM SoM - const: fsl,imx8mm + - description: SolidRun i.MX8MM SoM based boards + items: + - enum: + - solidrun,imx8mm-hummingboard-ripple # SolidRun i.MX8MM SoM on HummingBoard Ripple + - const: solidrun,imx8mm-sr-som + - const: fsl,imx8mm + - description: Variscite VAR-SOM-MX8MM based boards items: - const: variscite,var-som-mx8mm-symphony @@ -1069,6 +1084,7 @@ properties: - fsl,imx8mn-ddr4-evk # i.MX8MN DDR4 EVK Board - fsl,imx8mn-evk # i.MX8MN LPDDR4 EVK Board - gw,imx8mn-gw7902 # i.MX8MM Gateworks Board + - solidrun,solidsense-n8-compact # SolidRun SolidSense N8 Compact - const: fsl,imx8mn - description: ifm i.MX8MN VHIP4 based boards @@ -1106,6 +1122,7 @@ properties: - beacon,imx8mp-beacon-kit # i.MX8MP Beacon Development Kit - dmo,imx8mp-data-modul-edm-sbc # i.MX8MP eDM SBC - emcraft,imx8mp-navqp # i.MX8MP Emcraft Systems NavQ+ Kit + - fsl,imx8mp-ab2 # i.MX8MP Audio Board V2 - fsl,imx8mp-evk # i.MX8MP EVK Board - fsl,imx8mp-evk-revb4 # i.MX8MP EVK Rev B4 Board - fsl,imx8mp-frdm # i.MX8MP Freedom Board @@ -1225,6 +1242,7 @@ properties: items: - enum: - solidrun,imx8mp-cubox-m # SolidRun i.MX8MP SoM on CuBox-M + - solidrun,imx8mp-hummingboard-iiot # SolidRun i.MX8MP SoM on HummingBoard IIoT - solidrun,imx8mp-hummingboard-mate # SolidRun i.MX8MP SoM on HummingBoard Mate - solidrun,imx8mp-hummingboard-pro # SolidRun i.MX8MP SoM on HummingBoard Pro - solidrun,imx8mp-hummingboard-pulse # SolidRun i.MX8MP SoM on HummingBoard Pulse @@ -1420,6 +1438,16 @@ properties: - const: tq,imx8dxp-tqma8xdps # TQ-Systems GmbH TQMa8XDPS SOM - const: fsl,imx8dxp + - description: + TQMa8x is a series of SOM featuring NXP i.MX8 system-on-chip + variants. It is designed to be clicked on different carrier boards + MBa8x is the starterkit + items: + - enum: + - tq,imx8qm-tqma8qm-mba8x # TQ-Systems GmbH TQMa8QM SOM on MBa8x + - const: tq,imx8qm-tqma8qm # TQ-Systems GmbH TQMa8QM SOM + - const: fsl,imx8qm + - description: i.MX8ULP based Boards items: - enum: @@ -1432,6 +1460,7 @@ properties: - enum: - fsl,imx91-11x11-evk # i.MX91 11x11 EVK Board - fsl,imx91-11x11-frdm # FRDM i.MX91 Development Board + - fsl,imx91-11x11-frdm-s # FRDM i.MX91S Development Board - const: fsl,imx91 - description: i.MX93 based Boards @@ -1441,6 +1470,7 @@ properties: - fsl,imx93-11x11-evk # i.MX93 11x11 EVK Board - fsl,imx93-11x11-frdm # i.MX93 11x11 FRDM Board - fsl,imx93-14x14-evk # i.MX93 14x14 EVK Board + - fsl,imx93-wireless-evk # i.MX93 and IW610G WLCSP (Wi-Fi + BLE + 802.15.4) SiP EVK Board - const: fsl,imx93 - description: i.MX94 based Boards @@ -1477,6 +1507,36 @@ properties: - const: toradex,smarc-imx95 # Toradex SMARC iMX95 Module - const: fsl,imx95 + - description: Toradex Boards with Verdin iMX95 Modules + items: + - enum: + - toradex,verdin-imx95-nonwifi-dahlia # Verdin iMX95 Module on Dahlia + - toradex,verdin-imx95-nonwifi-dev # Verdin iMX95 Module on Verdin Development Board + - toradex,verdin-imx95-nonwifi-ivy # Verdin iMX95 Module on Ivy + - toradex,verdin-imx95-nonwifi-mallow # Verdin iMX95 Module on Mallow + - toradex,verdin-imx95-nonwifi-yavia # Verdin iMX95 Module on Yavia + - const: toradex,verdin-imx95-nonwifi # Verdin iMX95 Module without Wi-Fi / BT + - const: toradex,verdin-imx95 # Verdin iMX95 Module + - const: fsl,imx95 + + - description: Toradex Boards with Verdin iMX95 Wi-Fi / BT Modules + items: + - enum: + - toradex,verdin-imx95-wifi-dahlia # Verdin iMX95 Wi-Fi / BT Module on Dahlia + - toradex,verdin-imx95-wifi-dev # Verdin iMX95 Wi-Fi / BT Module on Verdin Development B. + - toradex,verdin-imx95-wifi-ivy # Verdin iMX95 Wi-Fi / BT Module on Ivy + - toradex,verdin-imx95-wifi-mallow # Verdin iMX95 Wi-Fi / BT Module on Mallow + - toradex,verdin-imx95-wifi-yavia # Verdin iMX95 Wi-Fi / BT Module on Yavia + - const: toradex,verdin-imx95-wifi # Verdin iMX95 Wi-Fi / BT Module + - const: toradex,verdin-imx95 # Verdin iMX95 Module + - const: fsl,imx95 + + - description: Variscite DART-MX95 based Boards + items: + - const: variscite,var-dart-mx95-sonata # Variscite DART-MX95 SOM on Sonata Development Board + - const: variscite,var-dart-mx95 # Variscite DART-MX95 SOM + - const: fsl,imx95 + - description: i.MXRT1050 based Boards items: - enum: @@ -1522,11 +1582,14 @@ properties: soldered on an adapter board or for the connector variant MBa93xxLA mainboard is a single board computer using the solderable SOM variant + MBa93xxLA-MINI mainboard is a single board computer using the solderable + SOM variant items: - enum: - tq,imx93-tqma9352-mba91xxca # TQ-Systems GmbH i.MX93 TQMa93xxCA/LA SOM on MBa91xxCA - tq,imx93-tqma9352-mba93xxca # TQ-Systems GmbH i.MX93 TQMa93xxCA/LA SOM on MBa93xxCA - tq,imx93-tqma9352-mba93xxla # TQ-Systems GmbH i.MX93 TQMa93xxLA SOM on MBa93xxLA SBC + - tq,imx93-tqma9352-mba93xxla-mini # TQ-Systems GmbH i.MX93 TQMa93xxLA SOM on MBa93xxLA-MINI SBC - const: tq,imx93-tqma9352 # TQ-Systems GmbH i.MX93 TQMa93xxCA/LA SOM - const: fsl,imx93 @@ -1545,6 +1608,12 @@ properties: - const: phytec,imx93-phycore-som # phyCORE-i.MX93 SoM - const: fsl,imx93 + - description: Variscite DART-MX91 based boards + items: + - const: variscite,var-dart-mx91-sonata # Variscite DART-MX91 on Sonata Development Board + - const: variscite,var-dart-mx91 # Variscite DART-MX91 SOM + - const: fsl,imx91 + - description: Variscite VAR-SOM-MX93 based boards items: - const: variscite,var-som-mx93-symphony @@ -1557,6 +1626,17 @@ properties: - const: kontron,imx93-osm-s # Kontron OSM-S i.MX93 SoM - const: fsl,imx93 + - description: + TQMa95xxLA is a series of SOM featuring NXP i.MX95 SoC variants, + designed to be soldered on different carrier boards. + MBa95xxCA is a carrier reference design / starter kit that allows + to use TQMa95xxLA via an adaper board. + items: + - enum: + - tq,imx95-tqma9596la-mba95xxca # TQ-Systems GmbH i.MX95 TQMa95xxLA SOM on MBa95xxCA + - const: tq,imx95-tqma9596la # TQ-Systems GmbH i.MX95 TQMa95xxLA SOM + - const: fsl,imx95 + - description: TQMa95xxSA is a series of SOM featuring NXP i.MX95 SoC variants. It has the SMARC form factor and is designed to be placed on @@ -1827,6 +1907,12 @@ properties: - fsl,s32v234-evb # S32V234-EVB2 Customer Evaluation Board - const: fsl,s32v234 + - description: S32N79 based Boards + items: + - enum: + - nxp,s32n79-rdb + - const: nxp,s32n79 + - description: Traverse LS1088A based Boards items: - enum: diff --git a/Documentation/devicetree/bindings/arm/marvell/armada-7k-8k.yaml b/Documentation/devicetree/bindings/arm/marvell/armada-7k-8k.yaml index 4bc7454a5d3a..7e77310da626 100644 --- a/Documentation/devicetree/bindings/arm/marvell/armada-7k-8k.yaml +++ b/Documentation/devicetree/bindings/arm/marvell/armada-7k-8k.yaml @@ -21,6 +21,17 @@ properties: - const: marvell,armada-ap806-dual - const: marvell,armada-ap806 + - description: + Falcon (DB-98CX85x0) Development board COM Express Carrier plus + Armada 7020 SoC COM Express CPU module + items: + - const: marvell,armada7020-falcon-carrier + - const: marvell,db-falcon-carrier + - const: marvell,armada7020-cpu-module + - const: marvell,armada7020 + - const: marvell,armada-ap806-dual + - const: marvell,armada-ap806 + - description: Armada 7040 SoC items: - enum: diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,audsys.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,audsys.yaml index 09a6c16e7e82..9aa39b002361 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,audsys.yaml +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,audsys.yaml @@ -49,38 +49,37 @@ required: - '#clock-cells' allOf: - - if: - properties: - compatible: - contains: - enum: - - mediatek,mt2701-audsys - - mediatek,mt7622-audsys - then: - properties: - audio-controller: - $ref: /schemas/sound/mediatek,mt2701-audio.yaml# + - if: + properties: + compatible: + contains: + enum: + - mediatek,mt2701-audsys + - mediatek,mt7622-audsys + then: + properties: + audio-controller: + $ref: /schemas/sound/mediatek,mt2701-audio.yaml# - - if: - properties: - compatible: - contains: - const: mediatek,mt8183-audiosys - then: - properties: - audio-controller: - $ref: /schemas/sound/mediatek,mt8183-audio.yaml# - - - if: - properties: - compatible: - contains: - const: mediatek,mt8192-audsys - then: - properties: - audio-controller: - $ref: /schemas/sound/mt8192-afe-pcm.yaml# + - if: + properties: + compatible: + contains: + const: mediatek,mt8183-audiosys + then: + properties: + audio-controller: + $ref: /schemas/sound/mediatek,mt8183-audio.yaml# + - if: + properties: + compatible: + contains: + const: mediatek,mt8192-audsys + then: + properties: + audio-controller: + $ref: /schemas/sound/mt8192-afe-pcm.yaml# additionalProperties: false diff --git a/Documentation/devicetree/bindings/arm/microchip,sam9x60-pit64b.yaml b/Documentation/devicetree/bindings/arm/microchip,sam9x60-pit64b.yaml new file mode 100644 index 000000000000..802cf2424c42 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/microchip,sam9x60-pit64b.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/microchip,sam9x60-pit64b.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip PIT64B 64-bit Periodic Interval Timer + +maintainers: + - Nicolas Ferre + - Claudiu Beznea + +description: + The Microchip PIT64B is a 64-bit periodic interval timer used in + several modern Microchip ARM SoCs including SAM9X60, SAM9X7 and + SAMA7D65 families. It provides extended timing range, flexible + clock selection and supports both periodic and one-shot interrupt + generation modes. + +properties: + compatible: + oneOf: + - const: microchip,sam9x60-pit64b + - items: + - enum: + - microchip,sam9x7-pit64b + - microchip,sama7d65-pit64b + - microchip,sama7g5-pit64b + - const: microchip,sam9x60-pit64b + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + maxItems: 2 + items: + enum: + - pclk + - gclk + +required: + - compatible + - reg + - interrupts + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + #include + timer@f0028000 { + compatible = "microchip,sama7g5-pit64b", "microchip,sam9x60-pit64b"; + reg = <0xf0028000 0x100>; + interrupts = <37 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 37>, <&pmc PMC_TYPE_GCK 37>; + clock-names = "pclk", "gclk"; + }; +... diff --git a/Documentation/devicetree/bindings/arm/microchip,sama7g5-chipid.yaml b/Documentation/devicetree/bindings/arm/microchip,sama7g5-chipid.yaml new file mode 100644 index 000000000000..4d6442ba5ac9 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/microchip,sama7g5-chipid.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/microchip,sama7g5-chipid.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Atmel/Microchip RAMC SDRAM/DDR Controller + +maintainers: + - Nicolas Ferre + - Claudiu Beznea + +description: + This binding describes the Atmel/Microchip Chip ID register block used + for SoC identification and revision information. It requires compatible + strings matching specific SoC families and a reg property defining the + register address and size. + +properties: + compatible: + enum: + - atmel,sama5d2-chipid + - microchip,sama7d65-chipid + - microchip,sama7g5-chipid + + reg: + maxItems: 1 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + chipid@fc069000 { + compatible = "atmel,sama5d2-chipid"; + reg = <0xfc069000 0x8>; + }; +... diff --git a/Documentation/devicetree/bindings/arm/nxp/lpc32xx.yaml b/Documentation/devicetree/bindings/arm/nxp/lpc32xx.yaml index 6b7f5e6f99cf..1e290f16a7a5 100644 --- a/Documentation/devicetree/bindings/arm/nxp/lpc32xx.yaml +++ b/Documentation/devicetree/bindings/arm/nxp/lpc32xx.yaml @@ -22,5 +22,27 @@ properties: - phytec,phy3250 - const: nxp,lpc3250 + - items: + - enum: + - ea,lpc4357-developers-kit + - const: nxp,lpc4357 + - const: nxp,lpc4350 + + - items: + - enum: + - ciaa,lpc4337 + - const: nxp,lpc4337 + - const: nxp,lpc4350 + + - items: + - enum: + - hitex,lpc4350-eval-board + - const: nxp,lpc4350 + + - items: + - enum: + - myir,myd-lpc4357 + - const: nxp,lpc4357 + additionalProperties: true ... diff --git a/Documentation/devicetree/bindings/arm/qcom.yaml b/Documentation/devicetree/bindings/arm/qcom.yaml index 1a955d1b14bf..b4943123d2e4 100644 --- a/Documentation/devicetree/bindings/arm/qcom.yaml +++ b/Documentation/devicetree/bindings/arm/qcom.yaml @@ -61,6 +61,21 @@ properties: - qcom,apq8084-sbc - const: qcom,apq8084 + - items: + - enum: + - qcom,eliza-mtp + - const: qcom,eliza + + - items: + - enum: + - qcom,glymur-crd + - const: qcom,glymur + + - items: + - enum: + - qcom,mahua-crd + - const: qcom,mahua + - items: - enum: - fairphone,fp6 @@ -171,6 +186,7 @@ properties: - qcom,msm8916-mtp - samsung,a3u-eur - samsung,a5u-eur + - samsung,coreprimeltevzw - samsung,e5 - samsung,e7 - samsung,fortuna3g @@ -186,6 +202,7 @@ properties: - samsung,serranove - thwc,uf896 - thwc,ufi001c + - wiko,chuppito - wingtech,wt86518 - wingtech,wt86528 - wingtech,wt88047 @@ -195,6 +212,8 @@ properties: - items: - enum: - xiaomi,riva + - xiaomi,rolex + - xiaomi,tiare - const: qcom,msm8917 - items: @@ -243,6 +262,13 @@ properties: - const: qcom,apq8096-sbc - const: qcom,apq8096 + - items: + - const: arrow,apq8096sg-db820c + - const: arrow,apq8096-db820c + - const: qcom,apq8096-sbc + - const: qcom,apq8096sg + - const: qcom,apq8096 + - items: - enum: - oneplus,oneplus3 @@ -297,6 +323,11 @@ properties: - tplink,archer-ax55-v1 - const: qcom,ipq5018 + - items: + - enum: + - qcom,ipq5210-rdp504 + - const: qcom,ipq5210 + - items: - enum: - qcom,ipq5332-ap-mi01.2 @@ -326,8 +357,10 @@ properties: - items: - enum: - qcom,ipq9574-ap-al02-c2 + - qcom,ipq9574-ap-al02-c2-emmc - qcom,ipq9574-ap-al02-c6 - qcom,ipq9574-ap-al02-c7 + - qcom,ipq9574-ap-al02-c7-emmc - qcom,ipq9574-ap-al02-c8 - qcom,ipq9574-ap-al02-c9 - const: qcom,ipq9574 @@ -360,6 +393,7 @@ properties: - qcom,qcs6490-rb3gen2 - radxa,dragon-q6a - shift,otter + - thundercomm,minipc-g1iot - thundercomm,rubikpi3 - const: qcom,qcm6490 @@ -385,6 +419,7 @@ properties: - items: - enum: - acer,aspire1 + - ecs,liva-qc710 - qcom,sc7180-idp - const: qcom,sc7180 @@ -882,6 +917,7 @@ properties: - items: - enum: + - arduino,monza - qcom,monaco-evk - qcom,qcs8300-ride - const: qcom,qcs8300 @@ -889,6 +925,7 @@ properties: - items: - enum: - qcom,qcs615-ride + - qcom,talos-evk - const: qcom,qcs615 - const: qcom,sm6150 @@ -972,6 +1009,7 @@ properties: - sony,pdx201 - xiaomi,ginkgo - xiaomi,laurel-sprout + - xiaomi,willow - const: qcom,sm6125 - items: @@ -1063,6 +1101,7 @@ properties: - items: - enum: + - ayaneo,pocket-s2 - qcom,sm8650-hdk - qcom,sm8650-mtp - qcom,sm8650-qrd @@ -1110,6 +1149,7 @@ properties: - dell,xps13-9345 - hp,elitebook-ultra-g1q - hp,omnibook-x14 + - lenovo,ideacentre-mini-01q8x10 - lenovo,yoga-slim7x - microsoft,romulus13 - microsoft,romulus15 @@ -1128,6 +1168,12 @@ properties: - const: microsoft,denali - const: qcom,x1e80100 + - items: + - enum: + - qcom,purwa-iot-evk + - const: qcom,purwa-iot-som + - const: qcom,x1p42100 + - items: - enum: - asus,zenbook-a14-ux3407qa-lcd @@ -1137,6 +1183,7 @@ properties: - items: - enum: + - asus,vivobook-s15-x1p4 - hp,omnibook-x14-fe1 - lenovo,thinkbook-16 - qcom,x1p42100-crd diff --git a/Documentation/devicetree/bindings/arm/rockchip.yaml b/Documentation/devicetree/bindings/arm/rockchip.yaml index ae77ded9fe47..1a9dde18626d 100644 --- a/Documentation/devicetree/bindings/arm/rockchip.yaml +++ b/Documentation/devicetree/bindings/arm/rockchip.yaml @@ -754,6 +754,11 @@ properties: - const: khadas,edge2 - const: rockchip,rk3588s + - description: Khadas Edge-2L series boards + items: + - const: khadas,edge-2l + - const: rockchip,rk3576 + - description: Kobol Helios64 items: - const: kobol,helios64 @@ -808,11 +813,22 @@ properties: - const: netxeon,r89 - const: rockchip,rk3288 + - description: Onion Omega4 Evaluation board + items: + - const: onion,omega4-evb + - const: onion,omega4 + - const: rockchip,rv1103b + - description: OPEN AI LAB EAIDK-610 items: - const: openailab,eaidk-610 - const: rockchip,rk3399 + - description: OneThing Edge Cube series + items: + - const: onething,edge-cube + - const: rockchip,rk3566 + - description: Xunlong Orange Pi RK3399 board items: - const: xunlong,rk3399-orangepi @@ -1187,7 +1203,9 @@ properties: - description: Rockchip RK3576 Evaluation board items: - - const: rockchip,rk3576-evb1-v10 + - enum: + - rockchip,rk3576-evb1-v10 + - rockchip,rk3576-evb2-v10 - const: rockchip,rk3576 - description: Rockchip RK3588 Evaluation board diff --git a/Documentation/devicetree/bindings/arm/samsung/samsung-boards.yaml b/Documentation/devicetree/bindings/arm/samsung/samsung-boards.yaml index f8e20e602c20..753b3ba1b607 100644 --- a/Documentation/devicetree/bindings/arm/samsung/samsung-boards.yaml +++ b/Documentation/devicetree/bindings/arm/samsung/samsung-boards.yaml @@ -117,6 +117,7 @@ properties: - description: Exynos5250 based boards items: - enum: + - google,manta # Google Manta (Nexus 10) - google,snow-rev5 # Google Snow Rev 5+ - google,spring # Google Spring - insignal,arndale # Insignal Arndale @@ -216,7 +217,9 @@ properties: items: - enum: - samsung,a2corelte # Samsung Galaxy A2 Core + - samsung,j5y17lte # Samsung Galaxy J5 (2017) - samsung,j6lte # Samsung Galaxy J6 + - samsung,j7xelte # Samsung Galaxy J7 (2016) - samsung,on7xelte # Samsung Galaxy J7 Prime - const: samsung,exynos7870 diff --git a/Documentation/devicetree/bindings/arm/stm32/stm32.yaml b/Documentation/devicetree/bindings/arm/stm32/stm32.yaml index ad144c02eb7e..c6af3a46364f 100644 --- a/Documentation/devicetree/bindings/arm/stm32/stm32.yaml +++ b/Documentation/devicetree/bindings/arm/stm32/stm32.yaml @@ -183,10 +183,12 @@ properties: - const: seeed,stm32mp157c-odyssey-som - const: st,stm32mp157 - - description: Phytec STM32MP1 SoM based Boards + - description: Phytec STM32MP157 SoM based Boards items: - - const: phytec,phycore-stm32mp1-3 - - const: phytec,phycore-stm32mp157c-som + - enum: + - phytec,phycore-stm32mp1-3 # phyBOARD-Sargas with phyCORE-STM32MP157C SoM + - enum: + - phytec,phycore-stm32mp157c-som # phyCORE-STM32MP157C SoM - const: st,stm32mp157 - description: Ultratronik STM32MP1 SBC based Boards diff --git a/Documentation/devicetree/bindings/arm/sunxi.yaml b/Documentation/devicetree/bindings/arm/sunxi.yaml index 9e4627f97d7e..e6443c266fa1 100644 --- a/Documentation/devicetree/bindings/arm/sunxi.yaml +++ b/Documentation/devicetree/bindings/arm/sunxi.yaml @@ -901,6 +901,11 @@ properties: - const: allwinner,sl631 - const: allwinner,sun8i-v3 + - description: TaiqiCat A01 + items: + - const: ultrapower,taiqicat-a01 + - const: allwinner,sun50i-h6 + - description: Tanix TX1 items: - const: oranth,tanix-tx1 diff --git a/Documentation/devicetree/bindings/arm/tegra.yaml b/Documentation/devicetree/bindings/arm/tegra.yaml index 50a31dba7bec..033a63f6c068 100644 --- a/Documentation/devicetree/bindings/arm/tegra.yaml +++ b/Documentation/devicetree/bindings/arm/tegra.yaml @@ -131,6 +131,33 @@ properties: - const: toradex,apalis-tk1-v1.2 - const: toradex,apalis-tk1 - const: nvidia,tegra124 + - items: + - const: google,nyan-big-rev7 + - const: google,nyan-big-rev6 + - const: google,nyan-big-rev5 + - const: google,nyan-big-rev4 + - const: google,nyan-big-rev3 + - const: google,nyan-big-rev2 + - const: google,nyan-big-rev1 + - const: google,nyan-big-rev0 + - const: google,nyan-big + - const: google,nyan + - const: nvidia,tegra124 + - items: + - const: google,nyan-blaze-rev10 + - const: google,nyan-blaze-rev9 + - const: google,nyan-blaze-rev8 + - const: google,nyan-blaze-rev7 + - const: google,nyan-blaze-rev6 + - const: google,nyan-blaze-rev5 + - const: google,nyan-blaze-rev4 + - const: google,nyan-blaze-rev3 + - const: google,nyan-blaze-rev2 + - const: google,nyan-blaze-rev1 + - const: google,nyan-blaze-rev0 + - const: google,nyan-blaze + - const: google,nyan + - const: nvidia,tegra124 - items: - enum: - nvidia,norrin @@ -184,17 +211,35 @@ properties: - const: nvidia,tegra124 - items: - enum: - - nvidia,darcy - nvidia,p2371-0000 - nvidia,p2371-2180 - nvidia,p2571 - - nvidia,p2894-0050-a08 - - nvidia,p3450-0000 - const: nvidia,tegra210 - items: - const: nvidia,p3541-0000 - const: nvidia,p3450-0000 - const: nvidia,tegra210 + - description: NVIDIA Jetson Nano + items: + - const: nvidia,p3450-0000 + - const: nvidia,tegra210 + - description: NVIDIA Shield TV + items: + - const: nvidia,p2894-0050-a08 + - const: nvidia,darcy + - const: nvidia,tegra210 + - description: Google Pixel C + items: + - const: google,smaug-rev8 + - const: google,smaug-rev7 + - const: google,smaug-rev6 + - const: google,smaug-rev5 + - const: google,smaug-rev4 + - const: google,smaug-rev3 + - const: google,smaug-rev2 + - const: google,smaug-rev1 + - const: google,smaug + - const: nvidia,tegra210 - description: Jetson TX2 Developer Kit items: - const: nvidia,p2771-0000 @@ -268,5 +313,10 @@ properties: - const: nvidia,p3971-0089+p3834-0008 - const: nvidia,p3834-0008 - const: nvidia,tegra264 + - description: Jetson AGX Thor Developer Kit + items: + - const: nvidia,p4071-0000+p3834-0008 + - const: nvidia,p3834-0008 + - const: nvidia,tegra264 additionalProperties: true diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra234-cbb.yaml b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra234-cbb.yaml index fcdf03131323..e69ee6a48fcc 100644 --- a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra234-cbb.yaml +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra234-cbb.yaml @@ -48,6 +48,10 @@ properties: - nvidia,tegra234-dce-fabric - nvidia,tegra234-rce-fabric - nvidia,tegra234-sce-fabric + - nvidia,tegra238-ape-fabric + - nvidia,tegra238-aon-fabric + - nvidia,tegra238-bpmp-fabric + - nvidia,tegra238-cbb-fabric reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/arm/ti/k3.yaml b/Documentation/devicetree/bindings/arm/ti/k3.yaml index 85deda6d4292..2a6a9441c23d 100644 --- a/Documentation/devicetree/bindings/arm/ti/k3.yaml +++ b/Documentation/devicetree/bindings/arm/ti/k3.yaml @@ -79,6 +79,7 @@ properties: - toradex,verdin-am62-nonwifi-ivy # Verdin AM62 Module on Ivy - toradex,verdin-am62-nonwifi-mallow # Verdin AM62 Module on Mallow - toradex,verdin-am62-nonwifi-yavia # Verdin AM62 Module on Yavia + - toradex,verdin-am62-nonwifi-zinnia # Verdin AM62 Module on Zinnia - const: toradex,verdin-am62-nonwifi # Verdin AM62 Module without Wi-Fi / BT - const: toradex,verdin-am62 # Verdin AM62 Module - const: ti,am625 @@ -91,6 +92,7 @@ properties: - toradex,verdin-am62-wifi-ivy # Verdin AM62 Wi-Fi / BT Module on Ivy - toradex,verdin-am62-wifi-mallow # Verdin AM62 Wi-Fi / BT Module on Mallow - toradex,verdin-am62-wifi-yavia # Verdin AM62 Wi-Fi / BT Module on Yavia + - toradex,verdin-am62-wifi-zinnia # Verdin AM62 Wi-Fi / BT Module on Zinnia - const: toradex,verdin-am62-wifi # Verdin AM62 Wi-Fi / BT Module - const: toradex,verdin-am62 # Verdin AM62 Module - const: ti,am625 @@ -103,6 +105,7 @@ properties: - toradex,verdin-am62p-nonwifi-ivy # Verdin AM62P Module on Ivy - toradex,verdin-am62p-nonwifi-mallow # Verdin AM62P Module on Mallow - toradex,verdin-am62p-nonwifi-yavia # Verdin AM62P Module on Yavia + - toradex,verdin-am62p-nonwifi-zinnia # Verdin AM62P Module on Zinnia - const: toradex,verdin-am62p-nonwifi # Verdin AM62P Module without Wi-Fi / BT - const: toradex,verdin-am62p # Verdin AM62P Module - const: ti,am62p5 @@ -115,6 +118,7 @@ properties: - toradex,verdin-am62p-wifi-ivy # Verdin AM62P Wi-Fi / BT Module on Ivy - toradex,verdin-am62p-wifi-mallow # Verdin AM62P Wi-Fi / BT Module on Mallow - toradex,verdin-am62p-wifi-yavia # Verdin AM62P Wi-Fi / BT Module on Yavia + - toradex,verdin-am62p-wifi-zinnia # Verdin AM62P Wi-Fi / BT Module on Zinnia - const: toradex,verdin-am62p-wifi # Verdin AM62P Wi-Fi / BT Module - const: toradex,verdin-am62p # Verdin AM62P Module - const: ti,am62p5 @@ -208,7 +212,6 @@ properties: items: - enum: - beagle,am67a-beagley-ai - - kontron,sa67 # Kontron SMARC-sAM67 board - ti,j722s-evm - const: ti,j722s diff --git a/Documentation/devicetree/bindings/arm/ti/omap.yaml b/Documentation/devicetree/bindings/arm/ti/omap.yaml index 14f1b9d8f59d..f694dcbf2348 100644 --- a/Documentation/devicetree/bindings/arm/ti/omap.yaml +++ b/Documentation/devicetree/bindings/arm/ti/omap.yaml @@ -144,6 +144,8 @@ properties: - motorola,droid-bionic # Motorola Droid Bionic XT875 - motorola,xyboard-mz609 - motorola,xyboard-mz617 + - samsung,espresso7 + - samsung,espresso10 - ti,omap4-panda - ti,omap4-sdp - const: ti,omap4430 diff --git a/Documentation/devicetree/bindings/arm/vexpress-scc.txt b/Documentation/devicetree/bindings/arm/vexpress-scc.txt deleted file mode 100644 index ae5043e42e5d..000000000000 --- a/Documentation/devicetree/bindings/arm/vexpress-scc.txt +++ /dev/null @@ -1,33 +0,0 @@ -ARM Versatile Express Serial Configuration Controller ------------------------------------------------------ - -Test chips for ARM Versatile Express platform implement SCC (Serial -Configuration Controller) interface, used to set initial conditions -for the test chip. - -In some cases its registers are also mapped in normal address space -and can be used to obtain runtime information about the chip internals -(like silicon temperature sensors) and as interface to other subsystems -like platform configuration control and power management. - -Required properties: - -- compatible value: "arm,vexpress-scc,", "arm,vexpress-scc"; - where is the full tile model name (as used - in the tile's Technical Reference Manual), - eg. for Coretile Express A15x2 A7x3 (V2P-CA15_A7): - compatible = "arm,vexpress-scc,v2p-ca15_a7", "arm,vexpress-scc"; - -Optional properties: - -- reg: when the SCC is memory mapped, physical address and size of the - registers window -- interrupts: when the SCC can generate a system-level interrupt - -Example: - - scc@7fff0000 { - compatible = "arm,vexpress-scc,v2p-ca15_a7", "arm,vexpress-scc"; - reg = <0 0x7fff0000 0 0x1000>; - interrupts = <0 95 4>; - }; diff --git a/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml b/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml index fe1272e86467..1d2e2429bd21 100644 --- a/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml +++ b/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml @@ -10,6 +10,7 @@ maintainers: - Robin van der Gracht allOf: + - $ref: /schemas/input/input.yaml# - $ref: /schemas/input/matrix-keymap.yaml# properties: @@ -33,9 +34,7 @@ properties: interrupts: maxItems: 1 - debounce-delay-ms: - maxItems: 1 - description: Debouncing interval time in milliseconds + debounce-delay-ms: true linux,keymap: true diff --git a/Documentation/devicetree/bindings/bus/baikal,bt1-apb.yaml b/Documentation/devicetree/bindings/bus/baikal,bt1-apb.yaml deleted file mode 100644 index 37ba3337f944..000000000000 --- a/Documentation/devicetree/bindings/bus/baikal,bt1-apb.yaml +++ /dev/null @@ -1,90 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/bus/baikal,bt1-apb.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Baikal-T1 APB-bus - -maintainers: - - Serge Semin - -description: | - Baikal-T1 CPU or DMAC MMIO requests are handled by the AMBA 3 AXI Interconnect - which routes them to the AXI-APB bridge. This interface is a single master - multiple slaves bus in turn serializing IO accesses and routing them to the - addressed APB slave devices. In case of any APB protocol collisions, slave - device not responding on timeout an IRQ is raised with an erroneous address - reported to the APB terminator (APB Errors Handler Block). - -allOf: - - $ref: /schemas/simple-bus.yaml# - -properties: - compatible: - contains: - const: baikal,bt1-apb - - reg: - items: - - description: APB EHB MMIO registers - - description: APB MMIO region with no any device mapped - - reg-names: - items: - - const: ehb - - const: nodev - - interrupts: - maxItems: 1 - - clocks: - items: - - description: APB reference clock - - clock-names: - items: - - const: pclk - - resets: - items: - - description: APB domain reset line - - reset-names: - items: - - const: prst - -unevaluatedProperties: false - -required: - - compatible - - reg - - reg-names - - interrupts - - clocks - - clock-names - -examples: - - | - #include - - bus@1f059000 { - compatible = "baikal,bt1-apb", "simple-bus"; - reg = <0x1f059000 0x1000>, - <0x1d000000 0x2040000>; - reg-names = "ehb", "nodev"; - #address-cells = <1>; - #size-cells = <1>; - - ranges; - - interrupts = ; - - clocks = <&ccu_sys 1>; - clock-names = "pclk"; - - resets = <&ccu_sys 1>; - reset-names = "prst"; - }; -... diff --git a/Documentation/devicetree/bindings/bus/baikal,bt1-axi.yaml b/Documentation/devicetree/bindings/bus/baikal,bt1-axi.yaml deleted file mode 100644 index 4ac78b44e45e..000000000000 --- a/Documentation/devicetree/bindings/bus/baikal,bt1-axi.yaml +++ /dev/null @@ -1,107 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/bus/baikal,bt1-axi.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Baikal-T1 AXI-bus - -maintainers: - - Serge Semin - -description: | - AXI3-bus is the main communication bus of Baikal-T1 SoC connecting all - high-speed peripheral IP-cores with RAM controller and with MIPS P5600 - cores. Traffic arbitration is done by means of DW AXI Interconnect (so - called AXI Main Interconnect) routing IO requests from one block to - another: from CPU to SoC peripherals and between some SoC peripherals - (mostly between peripheral devices and RAM, but also between DMA and - some peripherals). In case of any protocol error, device not responding - an IRQ is raised and a faulty situation is reported to the AXI EHB - (Errors Handler Block) embedded on top of the DW AXI Interconnect and - accessible by means of the Baikal-T1 System Controller. - -allOf: - - $ref: /schemas/simple-bus.yaml# - -properties: - compatible: - contains: - const: baikal,bt1-axi - - reg: - minItems: 1 - items: - - description: Synopsys DesignWare AXI Interconnect QoS registers - - description: AXI EHB MMIO system controller registers - - reg-names: - minItems: 1 - items: - - const: qos - - const: ehb - - '#interconnect-cells': - const: 1 - - syscon: - $ref: /schemas/types.yaml#/definitions/phandle - description: Phandle to the Baikal-T1 System Controller DT node - - interrupts: - maxItems: 1 - - clocks: - items: - - description: Main Interconnect uplink reference clock - - clock-names: - items: - - const: aclk - - resets: - items: - - description: Main Interconnect reset line - - reset-names: - items: - - const: arst - -unevaluatedProperties: false - -required: - - compatible - - reg - - reg-names - - syscon - - interrupts - - clocks - - clock-names - -examples: - - | - #include - - bus@1f05a000 { - compatible = "baikal,bt1-axi", "simple-bus"; - reg = <0x1f05a000 0x1000>, - <0x1f04d110 0x8>; - reg-names = "qos", "ehb"; - #address-cells = <1>; - #size-cells = <1>; - #interconnect-cells = <1>; - - syscon = <&syscon>; - - ranges; - - interrupts = ; - - clocks = <&ccu_axi 0>; - clock-names = "aclk"; - - resets = <&ccu_axi 0>; - reset-names = "arst"; - }; -... diff --git a/Documentation/devicetree/bindings/bus/st,stm32mp131-dbg-bus.yaml b/Documentation/devicetree/bindings/bus/st,stm32mp131-dbg-bus.yaml new file mode 100644 index 000000000000..6c74433efbe3 --- /dev/null +++ b/Documentation/devicetree/bindings/bus/st,stm32mp131-dbg-bus.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/bus/st,stm32mp131-dbg-bus.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STM32 Coresight bus + +maintainers: + - Gatien Chevallier + +description: + The STM32 debug bus is in charge of checking the debug configuration + of the platform before probing the peripheral drivers that rely on the debug + domain. + +properties: + compatible: + items: + - enum: + - st,stm32mp131-dbg-bus + - st,stm32mp151-dbg-bus + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: + minItems: 1 + maxItems: 2 + + "#access-controller-cells": + const: 1 + description: + Contains the debug profile necessary to access the peripheral. + +patternProperties: + "@[0-9a-f]+$": + description: Debug related peripherals + type: object + + additionalProperties: true + + required: + - access-controllers + +required: + - "#access-controller-cells" + - "#address-cells" + - "#size-cells" + - compatible + - ranges + +additionalProperties: false + +examples: + - | + #include + + dbg_bus: bus@50080000 { + compatible = "st,stm32mp131-dbg-bus"; + #address-cells = <1>; + #size-cells = <1>; + #access-controller-cells = <1>; + ranges = <0x50080000 0x50080000 0x3f80000>; + + cti@50094000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x50094000 0x1000>; + clocks = <&rcc CK_DBG>; + clock-names = "apb_pclk"; + access-controllers = <&dbg_bus 0>; + }; + }; diff --git a/Documentation/devicetree/bindings/cache/baikal,bt1-l2-ctl.yaml b/Documentation/devicetree/bindings/cache/baikal,bt1-l2-ctl.yaml deleted file mode 100644 index ec4f367bc0b4..000000000000 --- a/Documentation/devicetree/bindings/cache/baikal,bt1-l2-ctl.yaml +++ /dev/null @@ -1,63 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/cache/baikal,bt1-l2-ctl.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Baikal-T1 L2-cache Control Block - -maintainers: - - Serge Semin - -description: | - By means of the System Controller Baikal-T1 SoC exposes a few settings to - tune the MIPS P5600 CM2 L2 cache performance up. In particular it's possible - to change the Tag, Data and Way-select RAM access latencies. Baikal-T1 - L2-cache controller block is responsible for the tuning. Its DT node is - supposed to be a child of the system controller. - -properties: - compatible: - const: baikal,bt1-l2-ctl - - reg: - maxItems: 1 - - baikal,l2-ws-latency: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Cycles of latency for Way-select RAM accesses - default: 0 - minimum: 0 - maximum: 3 - - baikal,l2-tag-latency: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Cycles of latency for Tag RAM accesses - default: 0 - minimum: 0 - maximum: 3 - - baikal,l2-data-latency: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Cycles of latency for Data RAM accesses - default: 1 - minimum: 0 - maximum: 3 - -additionalProperties: false - -required: - - compatible - -examples: - - | - l2@1f04d028 { - compatible = "baikal,bt1-l2-ctl"; - reg = <0x1f04d028 0x004>; - - baikal,l2-ws-latency = <1>; - baikal,l2-tag-latency = <1>; - baikal,l2-data-latency = <2>; - }; -... diff --git a/Documentation/devicetree/bindings/cache/qcom,llcc.yaml b/Documentation/devicetree/bindings/cache/qcom,llcc.yaml index 6671e461e34a..995d57815781 100644 --- a/Documentation/devicetree/bindings/cache/qcom,llcc.yaml +++ b/Documentation/devicetree/bindings/cache/qcom,llcc.yaml @@ -33,6 +33,7 @@ properties: - qcom,sc7280-llcc - qcom,sc8180x-llcc - qcom,sc8280xp-llcc + - qcom,sdm670-llcc - qcom,sdm845-llcc - qcom,sm6350-llcc - qcom,sm7150-llcc @@ -204,6 +205,7 @@ allOf: contains: enum: - qcom,sc7280-llcc + - qcom,sdm670-llcc then: properties: reg: diff --git a/Documentation/devicetree/bindings/chrome/google,cros-ec-typec.yaml b/Documentation/devicetree/bindings/chrome/google,cros-ec-typec.yaml index 9f9816fbecbc..fd1a459879bd 100644 --- a/Documentation/devicetree/bindings/chrome/google,cros-ec-typec.yaml +++ b/Documentation/devicetree/bindings/chrome/google,cros-ec-typec.yaml @@ -8,17 +8,28 @@ title: Google Chrome OS EC(Embedded Controller) Type C port driver. maintainers: - Benson Leung - - Prashant Malani + - Abhishek Pandit-Subedi + - Andrei Kuchynski + - Łukasz Bartosik + - Jameson Thies description: Chrome OS devices have an Embedded Controller(EC) which has access to Type C port state. This node is intended to allow the host to read and - control the Type C ports. The node for this device should be under a - cros-ec node like google,cros-ec-spi. + control the Type C ports. This binding is compatible with both the + cros-ec-typec and cros-ec-ucsi drivers. The cros-ec-typec driver + supports the host command interface used by the Chrome OS EC with a + built-in Type-C port manager and external Type-C Port Controller + (TCPC). The cros-ec-ucsi driver supports the USB Type-C Connector + System Software (UCSI) interface used by the Chrome OS EC when the + platform has a separate power delivery controller (PDC). The node for + this device should be under a cros-ec node like google,cros-ec-spi. properties: compatible: - const: google,cros-ec-typec + enum: + - google,cros-ec-typec + - google,cros-ec-ucsi '#address-cells': const: 1 diff --git a/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml b/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml index a8471367175b..eb24a5687639 100644 --- a/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml +++ b/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml @@ -32,6 +32,7 @@ properties: - enum: - airoha,en7523-scu - airoha,en7581-scu + - econet,en751221-scu reg: items: @@ -67,7 +68,9 @@ allOf: - if: properties: compatible: - const: airoha,en7581-scu + enum: + - airoha,en7581-scu + - econet,en751221-scu then: properties: reg: @@ -98,3 +101,4 @@ examples: #reset-cells = <1>; }; }; + diff --git a/Documentation/devicetree/bindings/clock/axis,artpec9-clock.yaml b/Documentation/devicetree/bindings/clock/axis,artpec9-clock.yaml new file mode 100644 index 000000000000..63442b91e7ac --- /dev/null +++ b/Documentation/devicetree/bindings/clock/axis,artpec9-clock.yaml @@ -0,0 +1,232 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/axis,artpec9-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Axis ARTPEC-9 SoC clock controller + +maintainers: + - Jesper Nilsson + +description: | + ARTPEC-9 clock controller is comprised of several CMU (Clock Management Unit) + units, generating clocks for different domains. Those CMU units are modeled + as separate device tree nodes, and might depend on each other. + The root clock in that root tree is an external clock: OSCCLK (25 MHz). + This external clock must be defined as a fixed-rate clock in dts. + + CMU_CMU is a top-level CMU, where all base clocks are prepared using PLLs and + dividers, all other clocks of function blocks (other CMUs) are usually + derived from CMU_CMU. + + Each clock is assigned an identifier and client nodes can use this identifier + to specify the clock which they consume. All clocks available for usage + in clock consumer nodes are defined as preprocessor macros in + 'include/dt-bindings/clock/axis,artpec9-clk.h' header. + +properties: + compatible: + enum: + - axis,artpec9-cmu-cmu + - axis,artpec9-cmu-bus + - axis,artpec9-cmu-core + - axis,artpec9-cmu-cpucl + - axis,artpec9-cmu-fsys0 + - axis,artpec9-cmu-fsys1 + - axis,artpec9-cmu-imem + - axis,artpec9-cmu-peri + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 5 + + clock-names: + minItems: 1 + maxItems: 5 + + "#clock-cells": + const: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - "#clock-cells" + +allOf: + - if: + properties: + compatible: + const: axis,artpec9-cmu-cmu + + then: + properties: + clocks: + items: + - description: External reference clock (25 MHz) + + clock-names: + items: + - const: fin_pll + + - if: + properties: + compatible: + const: axis,artpec9-cmu-bus + + then: + properties: + clocks: + items: + - description: External reference clock (25 MHz) + - description: CMU_BUS bus clock (from CMU_CMU) + + clock-names: + items: + - const: fin_pll + - const: bus + + - if: + properties: + compatible: + const: axis,artpec9-cmu-core + + then: + properties: + clocks: + items: + - description: External reference clock (25 MHz) + - description: CMU_CORE main clock (from CMU_CMU) + + clock-names: + items: + - const: fin_pll + - const: main + + - if: + properties: + compatible: + const: axis,artpec9-cmu-cpucl + + then: + properties: + clocks: + items: + - description: External reference clock (25 MHz) + - description: CMU_CPUCL switch clock (from CMU_CMU) + + clock-names: + items: + - const: fin_pll + - const: switch + + - if: + properties: + compatible: + const: axis,artpec9-cmu-fsys0 + + then: + properties: + clocks: + items: + - description: External reference clock (25 MHz) + - description: CMU_FSYS0 bus clock (from CMU_CMU) + - description: CMU_FSYS0 IP clock (from CMU_CMU) + + clock-names: + items: + - const: fin_pll + - const: bus + - const: ip + + - if: + properties: + compatible: + const: axis,artpec9-cmu-fsys1 + + then: + properties: + clocks: + items: + - description: External reference clock (25 MHz) + - description: CMU_FSYS1 scan0 clock (from CMU_CMU) + - description: CMU_FSYS1 scan1 clock (from CMU_CMU) + - description: CMU_FSYS1 bus clock (from CMU_CMU) + + clock-names: + items: + - const: fin_pll + - const: scan0 + - const: scan1 + - const: bus + + - if: + properties: + compatible: + const: axis,artpec9-cmu-imem + + then: + properties: + clocks: + items: + - description: External reference clock (25 MHz) + - description: CMU_IMEM ACLK clock (from CMU_CMU) + - description: CMU_IMEM CA5 clock (from CMU_CMU) + - description: CMU_IMEM JPEG clock (from CMU_CMU) + - description: CMU_IMEM SSS clock (from CMU_CMU) + + clock-names: + items: + - const: fin_pll + - const: aclk + - const: ca5 + - const: jpeg + - const: sss + + - if: + properties: + compatible: + const: axis,artpec9-cmu-peri + + then: + properties: + clocks: + items: + - description: External reference clock (25 MHz) + - description: CMU_PERI IP clock (from CMU_CMU) + - description: CMU_PERI DISP clock (from CMU_CMU) + + clock-names: + items: + - const: fin_pll + - const: ip + - const: disp + +additionalProperties: false + +examples: + # Clock controller node for CMU_FSYS1 + - | + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + cmu_fsys1: clock-controller@14c10000 { + compatible = "axis,artpec9-cmu-fsys1"; + reg = <0x0 0x14c10000 0x0 0x4000>; + #clock-cells = <1>; + clocks = <&fin_pll>, + <&cmu_cmu CLK_DOUT_CMU_FSYS1_SCAN0>, + <&cmu_cmu CLK_DOUT_CMU_FSYS1_SCAN1>, + <&cmu_cmu CLK_DOUT_CMU_FSYS1_BUS>; + clock-names = "fin_pll", "scan0", "scan1", "bus"; + }; + }; +... diff --git a/Documentation/devicetree/bindings/clock/baikal,bt1-ccu-div.yaml b/Documentation/devicetree/bindings/clock/baikal,bt1-ccu-div.yaml deleted file mode 100644 index 30252c95700c..000000000000 --- a/Documentation/devicetree/bindings/clock/baikal,bt1-ccu-div.yaml +++ /dev/null @@ -1,196 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/clock/baikal,bt1-ccu-div.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Baikal-T1 Clock Control Unit Dividers - -maintainers: - - Serge Semin - -description: | - Clocks Control Unit is the core of Baikal-T1 SoC System Controller - responsible for the chip subsystems clocking and resetting. The CCU is - connected with an external fixed rate oscillator, which signal is transformed - into clocks of various frequencies and then propagated to either individual - IP-blocks or to groups of blocks (clock domains). The transformation is done - by means of an embedded into CCU PLLs and gateable/non-gateable dividers. The - later ones are described in this binding. Each clock domain can be also - individually reset by using the domain clocks divider configuration - registers. Baikal-T1 CCU is logically divided into the next components: - 1) External oscillator (normally XTAL's 25 MHz crystal oscillator, but - in general can provide any frequency supported by the CCU PLLs). - 2) PLLs clocks generators (PLLs). - 3) AXI-bus clock dividers (AXI) - described in this binding file. - 4) System devices reference clock dividers (SYS) - described in this binding - file. - which are connected with each other as shown on the next figure: - - +---------------+ - | Baikal-T1 CCU | - | +----+------|- MIPS P5600 cores - | +-|PLLs|------|- DDR controller - | | +----+ | - +----+ | | | | | - |XTAL|--|-+ | | +---+-| - +----+ | | | +-|AXI|-|- AXI-bus - | | | +---+-| - | | | | - | | +----+---+-|- APB-bus - | +-------|SYS|-|- Low-speed Devices - | +---+-|- High-speed Devices - +---------------+ - - Each sub-block is represented as a separate DT node and has an individual - driver to be bound with. - - In order to create signals of wide range frequencies the external oscillator - output is primarily connected to a set of CCU PLLs. Some of PLLs CLKOUT are - then passed over CCU dividers to create signals required for the target clock - domain (like AXI-bus or System Device consumers). The dividers have the - following structure: - - +--------------+ - CLKIN --|->+----+ 1|\ | - SETCLK--|--|/DIV|->| | | - CLKDIV--|--| | | |-|->CLKLOUT - LOCK----|--+----+ | | | - | |/ | - | | | - EN------|-----------+ | - RST-----|--------------|->RSTOUT - +--------------+ - - where CLKIN is the reference clock coming either from CCU PLLs or from an - external clock oscillator, SETCLK - a command to update the output clock in - accordance with a set divider, CLKDIV - clocks divider, LOCK - a signal of - the output clock stabilization, EN - enable/disable the divider block, - RST/RSTOUT - reset clocks domain signal. Depending on the consumer IP-core - peculiarities the dividers may lack of some functionality depicted on the - figure above (like EN, CLKDIV/LOCK/SETCLK). In this case the corresponding - clock provider just doesn't expose either switching functions, or the rate - configuration, or both of them. - - The clock dividers, which output clock is then consumed by the SoC individual - devices, are united into a single clocks provider called System Devices CCU. - Similarly the dividers with output clocks utilized as AXI-bus reference clocks - are called AXI-bus CCU. Both of them use the common clock bindings with no - custom properties. The list of exported clocks and reset signals can be found - in the files: 'include/dt-bindings/clock/bt1-ccu.h' and - 'include/dt-bindings/reset/bt1-ccu.h'. Since System Devices and AXI-bus CCU - are a part of the Baikal-T1 SoC System Controller their DT nodes are supposed - to be a children of later one. - -if: - properties: - compatible: - contains: - const: baikal,bt1-ccu-axi - -then: - properties: - clocks: - items: - - description: CCU SATA PLL output clock - - description: CCU PCIe PLL output clock - - description: CCU Ethernet PLL output clock - - clock-names: - items: - - const: sata_clk - - const: pcie_clk - - const: eth_clk - -else: - properties: - clocks: - items: - - description: External reference clock - - description: CCU SATA PLL output clock - - description: CCU PCIe PLL output clock - - description: CCU Ethernet PLL output clock - - clock-names: - items: - - const: ref_clk - - const: sata_clk - - const: pcie_clk - - const: eth_clk - -properties: - compatible: - enum: - - baikal,bt1-ccu-axi - - baikal,bt1-ccu-sys - - reg: - maxItems: 1 - - "#clock-cells": - const: 1 - - "#reset-cells": - const: 1 - - clocks: - minItems: 3 - maxItems: 4 - - clock-names: - minItems: 3 - maxItems: 4 - -additionalProperties: false - -required: - - compatible - - "#clock-cells" - - clocks - - clock-names - -examples: - # AXI-bus Clock Control Unit node: - - | - #include - - clock-controller@1f04d030 { - compatible = "baikal,bt1-ccu-axi"; - reg = <0x1f04d030 0x030>; - #clock-cells = <1>; - #reset-cells = <1>; - - clocks = <&ccu_pll CCU_SATA_PLL>, - <&ccu_pll CCU_PCIE_PLL>, - <&ccu_pll CCU_ETH_PLL>; - clock-names = "sata_clk", "pcie_clk", "eth_clk"; - }; - # System Devices Clock Control Unit node: - - | - #include - - clock-controller@1f04d060 { - compatible = "baikal,bt1-ccu-sys"; - reg = <0x1f04d060 0x0a0>; - #clock-cells = <1>; - #reset-cells = <1>; - - clocks = <&clk25m>, - <&ccu_pll CCU_SATA_PLL>, - <&ccu_pll CCU_PCIE_PLL>, - <&ccu_pll CCU_ETH_PLL>; - clock-names = "ref_clk", "sata_clk", "pcie_clk", - "eth_clk"; - }; - # Required Clock Control Unit PLL node: - - | - ccu_pll: clock-controller@1f04d000 { - compatible = "baikal,bt1-ccu-pll"; - reg = <0x1f04d000 0x028>; - #clock-cells = <1>; - - clocks = <&clk25m>; - clock-names = "ref_clk"; - }; -... diff --git a/Documentation/devicetree/bindings/clock/baikal,bt1-ccu-pll.yaml b/Documentation/devicetree/bindings/clock/baikal,bt1-ccu-pll.yaml deleted file mode 100644 index 7f8d98226437..000000000000 --- a/Documentation/devicetree/bindings/clock/baikal,bt1-ccu-pll.yaml +++ /dev/null @@ -1,131 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/clock/baikal,bt1-ccu-pll.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Baikal-T1 Clock Control Unit PLL - -maintainers: - - Serge Semin - -description: | - Clocks Control Unit is the core of Baikal-T1 SoC System Controller - responsible for the chip subsystems clocking and resetting. The CCU is - connected with an external fixed rate oscillator, which signal is transformed - into clocks of various frequencies and then propagated to either individual - IP-blocks or to groups of blocks (clock domains). The transformation is done - by means of PLLs and gateable/non-gateable dividers embedded into the CCU. - It's logically divided into the next components: - 1) External oscillator (normally XTAL's 25 MHz crystal oscillator, but - in general can provide any frequency supported by the CCU PLLs). - 2) PLLs clocks generators (PLLs) - described in this binding file. - 3) AXI-bus clock dividers (AXI). - 4) System devices reference clock dividers (SYS). - which are connected with each other as shown on the next figure: - - +---------------+ - | Baikal-T1 CCU | - | +----+------|- MIPS P5600 cores - | +-|PLLs|------|- DDR controller - | | +----+ | - +----+ | | | | | - |XTAL|--|-+ | | +---+-| - +----+ | | | +-|AXI|-|- AXI-bus - | | | +---+-| - | | | | - | | +----+---+-|- APB-bus - | +-------|SYS|-|- Low-speed Devices - | +---+-|- High-speed Devices - +---------------+ - - Each CCU sub-block is represented as a separate dts-node and has an - individual driver to be bound with. - - In order to create signals of wide range frequencies the external oscillator - output is primarily connected to a set of CCU PLLs. There are five PLLs - to create a clock for the MIPS P5600 cores, the embedded DDR controller, - SATA, Ethernet and PCIe domains. The last three domains though named by the - biggest system interfaces in fact include nearly all of the rest SoC - peripherals. Each of the PLLs is based on True Circuits TSMC CLN28HPM core - with an interface wrapper (so called safe PLL' clocks switcher) to simplify - the PLL configuration procedure. The PLLs work as depicted on the next - diagram: - - +--------------------------+ - | | - +-->+---+ +---+ +---+ | +---+ 0|\ - CLKF--->|/NF|--->|PFD|...|VCO|-+->|/OD|--->| | - +---+ +->+---+ +---+ /->+---+ | |--->CLKOUT - CLKOD---------C----------------+ 1| | - +--------C--------------------------->|/ - | | ^ - Rclk-+->+---+ | | - CLKR--->|/NR|-+ | - +---+ | - BYPASS--------------------------------------+ - BWADJ---> - - where Rclk is the reference clock coming from XTAL, NR - reference clock - divider, NF - PLL clock multiplier, OD - VCO output clock divider, CLKOUT - - output clock, BWADJ is the PLL bandwidth adjustment parameter. At this moment - the binding supports the PLL dividers configuration in accordance with a - requested rate, while bypassing and bandwidth adjustment settings can be - added in future if it gets to be necessary. - - The PLLs CLKOUT is then either directly connected with the corresponding - clocks consumer (like P5600 cores or DDR controller) or passed over a CCU - divider to create a signal required for the clock domain. - - The CCU PLL dts-node uses the common clock bindings with no custom - parameters. The list of exported clocks can be found in - 'include/dt-bindings/clock/bt1-ccu.h'. Since CCU PLL is a part of the - Baikal-T1 SoC System Controller its DT node is supposed to be a child of - later one. - -properties: - compatible: - const: baikal,bt1-ccu-pll - - reg: - maxItems: 1 - - "#clock-cells": - const: 1 - - clocks: - description: External reference clock - maxItems: 1 - - clock-names: - const: ref_clk - -additionalProperties: false - -required: - - compatible - - "#clock-cells" - - clocks - - clock-names - -examples: - # Clock Control Unit PLL node: - - | - clock-controller@1f04d000 { - compatible = "baikal,bt1-ccu-pll"; - reg = <0x1f04d000 0x028>; - #clock-cells = <1>; - - clocks = <&clk25m>; - clock-names = "ref_clk"; - }; - # Required external oscillator: - - | - clk25m: clock-oscillator-25m { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <25000000>; - clock-output-names = "clk25m"; - }; -... diff --git a/Documentation/devicetree/bindings/clock/eswin,eic7700-clock.yaml b/Documentation/devicetree/bindings/clock/eswin,eic7700-clock.yaml new file mode 100644 index 000000000000..3125ae52bde6 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/eswin,eic7700-clock.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/eswin,eic7700-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Eswin EIC7700 SoC clock controller + +maintainers: + - Yifeng Huang + - Xuyang Dong + +description: + The clock controller generates and supplies clock to all the modules + for eic7700 SoC. + +properties: + compatible: + const: eswin,eic7700-clock + + reg: + maxItems: 1 + + clocks: + items: + - description: External 24MHz oscillator clock + + '#clock-cells': + const: 1 + +required: + - compatible + - reg + - clocks + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller@51828000 { + compatible = "eswin,eic7700-clock"; + reg = <0x51828000 0x300>; + clocks = <&xtal24m>; + #clock-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/fsl,sai-clock.yaml b/Documentation/devicetree/bindings/clock/fsl,sai-clock.yaml index 3bca9d11c148..041a63fa2d2b 100644 --- a/Documentation/devicetree/bindings/clock/fsl,sai-clock.yaml +++ b/Documentation/devicetree/bindings/clock/fsl,sai-clock.yaml @@ -10,10 +10,10 @@ maintainers: - Michael Walle description: | - It is possible to use the BCLK pin of a SAI module as a generic clock - output. Some SoC are very constrained in their pin multiplexer - configuration. Eg. pins can only be changed groups. For example, on the - LS1028A SoC you can only enable SAIs in pairs. If you use only one SAI, + It is possible to use the BCLK or MCLK pin of a SAI module as a generic + clock output. Some SoC are very constrained in their pin multiplexer + configuration. E.g. pins can only be changed in groups. For example, on + the LS1028A SoC you can only enable SAIs in pairs. If you use only one SAI, the second pins are wasted. Using this binding it is possible to use the clock of the second SAI as a MCLK clock for an audio codec, for example. @@ -21,16 +21,45 @@ description: | properties: compatible: - const: fsl,vf610-sai-clock + oneOf: + - items: + - enum: + - fsl,imx8mm-sai-clock + - fsl,imx8mn-sai-clock + - fsl,imx8mp-sai-clock + - const: fsl,imx8mq-sai-clock + - items: + - enum: + - fsl,imx8mq-sai-clock + - fsl,vf610-sai-clock reg: maxItems: 1 clocks: - maxItems: 1 + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + items: + - const: bus + - const: mclk1 '#clock-cells': - const: 0 + maximum: 1 + +allOf: + - if: + properties: + compatible: + contains: + const: fsl,vf610-sai-clock + then: + properties: + clocks: + maxItems: 1 + clock-names: false required: - compatible diff --git a/Documentation/devicetree/bindings/clock/imx6q-clock.yaml b/Documentation/devicetree/bindings/clock/imx6q-clock.yaml index cd3c04c883df..0e6febe1c875 100644 --- a/Documentation/devicetree/bindings/clock/imx6q-clock.yaml +++ b/Documentation/devicetree/bindings/clock/imx6q-clock.yaml @@ -29,20 +29,24 @@ properties: const: 1 clocks: + minItems: 5 items: - description: 24m osc - description: 32k osc - description: ckih1 clock input - description: anaclk1 clock input - description: anaclk2 clock input + - description: clock input from enet ref pad clock-names: + minItems: 5 items: - const: osc - const: ckil - const: ckih1 - const: anaclk1 - const: anaclk2 + - const: enet_ref_pad fsl,pmic-stby-poweroff: $ref: /schemas/types.yaml#/definitions/flag diff --git a/Documentation/devicetree/bindings/clock/imx6ul-clock.yaml b/Documentation/devicetree/bindings/clock/imx6ul-clock.yaml index d57e18a210cc..035002721a3b 100644 --- a/Documentation/devicetree/bindings/clock/imx6ul-clock.yaml +++ b/Documentation/devicetree/bindings/clock/imx6ul-clock.yaml @@ -29,18 +29,22 @@ properties: const: 1 clocks: + minItems: 4 items: - description: 32k osc - description: 24m osc - description: ipp_di0 clock input - description: ipp_di1 clock input + - description: clock input from enet1 ref pad clock-names: + minItems: 4 items: - const: ckil - const: osc - const: ipp_di0 - const: ipp_di1 + - const: enet1_ref_pad required: - compatible diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra124-dfll.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra124-dfll.txt deleted file mode 100644 index f7d347385b57..000000000000 --- a/Documentation/devicetree/bindings/clock/nvidia,tegra124-dfll.txt +++ /dev/null @@ -1,155 +0,0 @@ -NVIDIA Tegra124 DFLL FCPU clocksource - -This binding uses the common clock binding: -Documentation/devicetree/bindings/clock/clock-bindings.txt - -The DFLL IP block on Tegra is a root clocksource designed for clocking -the fast CPU cluster. It consists of a free-running voltage controlled -oscillator connected to the CPU voltage rail (VDD_CPU), and a closed loop -control module that will automatically adjust the VDD_CPU voltage by -communicating with an off-chip PMIC either via an I2C bus or via PWM signals. - -Required properties: -- compatible : should be one of: - - "nvidia,tegra124-dfll": for Tegra124 - - "nvidia,tegra210-dfll": for Tegra210 -- reg : Defines the following set of registers, in the order listed: - - registers for the DFLL control logic. - - registers for the I2C output logic. - - registers for the integrated I2C master controller. - - look-up table RAM for voltage register values. -- interrupts: Should contain the DFLL block interrupt. -- clocks: Must contain an entry for each entry in clock-names. - See clock-bindings.txt for details. -- clock-names: Must include the following entries: - - soc: Clock source for the DFLL control logic. - - ref: The closed loop reference clock - - i2c: Clock source for the integrated I2C master. -- resets: Must contain an entry for each entry in reset-names. - See ../reset/reset.txt for details. -- reset-names: Must include the following entries: - - dvco: Reset control for the DFLL DVCO. -- #clock-cells: Must be 0. -- clock-output-names: Name of the clock output. -- vdd-cpu-supply: Regulator for the CPU voltage rail that the DFLL - hardware will start controlling. The regulator will be queried for - the I2C register, control values and supported voltages. - -Required properties for the control loop parameters: -- nvidia,sample-rate: Sample rate of the DFLL control loop. -- nvidia,droop-ctrl: See the register CL_DVFS_DROOP_CTRL in the TRM. -- nvidia,force-mode: See the field DFLL_PARAMS_FORCE_MODE in the TRM. -- nvidia,cf: Numeric value, see the field DFLL_PARAMS_CF_PARAM in the TRM. -- nvidia,ci: Numeric value, see the field DFLL_PARAMS_CI_PARAM in the TRM. -- nvidia,cg: Numeric value, see the field DFLL_PARAMS_CG_PARAM in the TRM. - -Optional properties for the control loop parameters: -- nvidia,cg-scale: Boolean value, see the field DFLL_PARAMS_CG_SCALE in the TRM. - -Optional properties for mode selection: -- nvidia,pwm-to-pmic: Use PWM to control regulator rather then I2C. - -Required properties for I2C mode: -- nvidia,i2c-fs-rate: I2C transfer rate, if using full speed mode. - -Required properties for PWM mode: -- nvidia,pwm-period-nanoseconds: period of PWM square wave in nanoseconds. -- nvidia,pwm-tristate-microvolts: Regulator voltage in micro volts when PWM - control is disabled and the PWM output is tristated. Note that this voltage is - configured in hardware, typically via a resistor divider. -- nvidia,pwm-min-microvolts: Regulator voltage in micro volts when PWM control - is enabled and PWM output is low. Hence, this is the minimum output voltage - that the regulator supports when PWM control is enabled. -- nvidia,pwm-voltage-step-microvolts: Voltage increase in micro volts - corresponding to a 1/33th increase in duty cycle. Eg the voltage for 2/33th - duty cycle would be: nvidia,pwm-min-microvolts + - nvidia,pwm-voltage-step-microvolts * 2. -- pinctrl-0: I/O pad configuration when PWM control is enabled. -- pinctrl-1: I/O pad configuration when PWM control is disabled. -- pinctrl-names: must include the following entries: - - dvfs_pwm_enable: I/O pad configuration when PWM control is enabled. - - dvfs_pwm_disable: I/O pad configuration when PWM control is disabled. - -Example for I2C: - -clock@70110000 { - compatible = "nvidia,tegra124-dfll"; - reg = <0 0x70110000 0 0x100>, /* DFLL control */ - <0 0x70110000 0 0x100>, /* I2C output control */ - <0 0x70110100 0 0x100>, /* Integrated I2C controller */ - <0 0x70110200 0 0x100>; /* Look-up table RAM */ - interrupts = ; - clocks = <&tegra_car TEGRA124_CLK_DFLL_SOC>, - <&tegra_car TEGRA124_CLK_DFLL_REF>, - <&tegra_car TEGRA124_CLK_I2C5>; - clock-names = "soc", "ref", "i2c"; - resets = <&tegra_car TEGRA124_RST_DFLL_DVCO>; - reset-names = "dvco"; - #clock-cells = <0>; - clock-output-names = "dfllCPU_out"; - vdd-cpu-supply = <&vdd_cpu>; - - nvidia,sample-rate = <12500>; - nvidia,droop-ctrl = <0x00000f00>; - nvidia,force-mode = <1>; - nvidia,cf = <10>; - nvidia,ci = <0>; - nvidia,cg = <2>; - - nvidia,i2c-fs-rate = <400000>; -}; - -Example for PWM: - -clock@70110000 { - compatible = "nvidia,tegra124-dfll"; - reg = <0 0x70110000 0 0x100>, /* DFLL control */ - <0 0x70110000 0 0x100>, /* I2C output control */ - <0 0x70110100 0 0x100>, /* Integrated I2C controller */ - <0 0x70110200 0 0x100>; /* Look-up table RAM */ - interrupts = ; - clocks = <&tegra_car TEGRA210_CLK_DFLL_SOC>, - <&tegra_car TEGRA210_CLK_DFLL_REF>, - <&tegra_car TEGRA124_CLK_I2C5>;; - clock-names = "soc", "ref", "i2c"; - resets = <&tegra_car TEGRA124_RST_DFLL_DVCO>; - reset-names = "dvco"; - #clock-cells = <0>; - clock-output-names = "dfllCPU_out"; - - nvidia,sample-rate = <25000>; - nvidia,droop-ctrl = <0x00000f00>; - nvidia,force-mode = <1>; - nvidia,cf = <6>; - nvidia,ci = <0>; - nvidia,cg = <2>; - - nvidia,pwm-min-microvolts = <708000>; /* 708mV */ - nvidia,pwm-period-nanoseconds = <2500>; /* 2.5us */ - nvidia,pwm-to-pmic; - nvidia,pwm-tristate-microvolts = <1000000>; - nvidia,pwm-voltage-step-microvolts = <19200>; /* 19.2mV */ - - pinctrl-names = "dvfs_pwm_enable", "dvfs_pwm_disable"; - pinctrl-0 = <&dvfs_pwm_active_state>; - pinctrl-1 = <&dvfs_pwm_inactive_state>; -}; - -/* pinmux nodes added for completeness. Binding doc can be found in: - * Documentation/devicetree/bindings/pinctrl/nvidia,tegra210-pinmux.yaml - */ - -pinmux: pinmux@700008d4 { - dvfs_pwm_active_state: dvfs_pwm_active { - dvfs_pwm_pbb1 { - nvidia,pins = "dvfs_pwm_pbb1"; - nvidia,tristate = ; - }; - }; - dvfs_pwm_inactive_state: dvfs_pwm_inactive { - dvfs_pwm_pbb1 { - nvidia,pins = "dvfs_pwm_pbb1"; - nvidia,tristate = ; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra124-dfll.yaml b/Documentation/devicetree/bindings/clock/nvidia,tegra124-dfll.yaml new file mode 100644 index 000000000000..5d689e48c438 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/nvidia,tegra124-dfll.yaml @@ -0,0 +1,290 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/nvidia,tegra124-dfll.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra124 (and later) DFLL FCPU clocksource + +maintainers: + - Thierry Reding + - Jon Hunter + +description: + The DFLL IP block on Tegra is a root clocksource designed for clocking + the fast CPU cluster. It consists of a free-running voltage controlled + oscillator connected to the CPU voltage rail (VDD_CPU), and a closed + loop control module that will automatically adjust the VDD_CPU voltage + by communicating with an off-chip PMIC either via an I2C bus or via + PWM signals. + +properties: + compatible: + enum: + - nvidia,tegra124-dfll + - nvidia,tegra210-dfll + + reg: + items: + - description: DFLL control logic + - description: I2C output logic + - description: Integrated I2C controller + - description: Look-up table RAM for voltage register values + + interrupts: + maxItems: 1 + + "#clock-cells": + const: 0 + + clocks: + items: + - description: Clock source for the DFLL control logic + - description: Closed loop reference clock + - description: Clock source for the integrated I2C controller + + clock-names: + items: + - const: soc + - const: ref + - const: i2c + + clock-output-names: + description: Name of the clock output + items: + - const: dfllCPU_out + + resets: + minItems: 1 + maxItems: 2 + + reset-names: + minItems: 1 + items: + - const: dvco + - const: dfll + + vdd-cpu-supply: + description: Regulator for the CPU voltage rail that the DFLL + hardware will start controlling. The regulator will be queried for + the I2C register, control values and supported voltages. + + nvidia,sample-rate: + description: Sample rate of the DFLL control loop + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 12500 + maximum: 25000 + + nvidia,droop-ctrl: + description: Droop control parameter (CL_DVFS_DROOP_CTRL) in the TRM + $ref: /schemas/types.yaml#/definitions/uint32 + + nvidia,force-mode: + description: See the field DFLL_PARAMS_FORCE_MODE in the TRM + $ref: /schemas/types.yaml#/definitions/uint32 + oneOf: + - description: disabled + const: 0 + - description: fixed delay mode + const: 1 + - description: auto mode + const: 2 + + nvidia,cf: + description: Numeric value, see the field DFLL_PARAMS_CF_PARAM in the TRM + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 63 + + nvidia,ci: + description: Numeric value, see the field DFLL_PARAMS_CI_PARAM in the TRM + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + nvidia,cg: + description: Numeric value, see the field DFLL_PARAMS_CG_PARAM in the TRM + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + + # optional properties + nvidia,cg-scale: + description: Boolean value, see the field DFLL_PARAMS_CG_SCALE in the TRM + $ref: /schemas/types.yaml#/definitions/flag + + nvidia,pwm-to-pmic: + description: Use PWM to control regulator rather than I2C + $ref: /schemas/types.yaml#/definitions/flag + + nvidia,i2c-fs-rate: + description: I2C transfer rate, if using full speed mode + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [100000, 400000] + + # required properties for PWM mode + nvidia,pwm-period-nanoseconds: + description: Period of PWM square wave in nanoseconds + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1000 + maximum: 1000000000 + + nvidia,pwm-tristate-microvolts: + description: Regulator voltage in microvolts when PWM control is disabled + and the PWM output is tristated. Note that this voltage is configured in + hardware, typically via a resistor divider. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3300000 + + nvidia,pwm-min-microvolts: + description: Regulator voltage in microvolts when PWM control is enabled + and PWM output is low. Hence, this is the minimum output voltage that + the regulator supports when PWM control is enabled. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3300000 + + nvidia,pwm-voltage-step-microvolts: + description: | + Voltage increase in micro volts corresponding to a 1/33th increase + in duty cycle. For example, the voltage for 2/33th duty cycle would be: + + nvidia,pwm-min-microvolts + nvidia,pwm-voltage-step-microvolts * 2 + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 100000 + + pinctrl-0: + description: I/O pad configuration when PWM control is enabled + + pinctrl-1: + description: I/O pad configuration when PWM control is disabled + + pinctrl-names: + items: + - const: dvfs_pwm_enable + - const: dvfs_pwm_disable + +required: + - compatible + - reg + - interrupts + - "#clock-cells" + - clocks + - clock-names + - clock-output-names + - resets + - reset-names + - nvidia,sample-rate + - nvidia,droop-ctrl + - nvidia,force-mode + - nvidia,cf + - nvidia,ci + - nvidia,cg + +additionalProperties: false + +allOf: + - if: + properties: + compatible: + contains: + const: nvidia,tegra124-dfll + then: + properties: + resets: + maxItems: 1 + + reset-names: + maxItems: 1 + else: + properties: + resets: + minItems: 2 + + reset-names: + minItems: 2 + + - if: + required: + - nvidia,pwm-to-pmic + then: + required: + - nvidia,pwm-min-microvolts + - nvidia,pwm-period-nanoseconds + - nvidia,pwm-tristate-microvolts + - nvidia,pwm-voltage-step-microvolts + else: + required: + - vdd-cpu-supply + +examples: + - | + #include + #include + #include + + clock@70110000 { + compatible = "nvidia,tegra124-dfll"; + reg = <0x70110000 0x100>, /* DFLL control */ + <0x70110000 0x100>, /* I2C output control */ + <0x70110100 0x100>, /* Integrated I2C controller */ + <0x70110200 0x100>; /* Look-up table RAM */ + interrupts = ; + clocks = <&tegra_car TEGRA124_CLK_DFLL_SOC>, + <&tegra_car TEGRA124_CLK_DFLL_REF>, + <&tegra_car TEGRA124_CLK_I2C5>; + clock-names = "soc", "ref", "i2c"; + resets = <&tegra_car TEGRA124_RST_DFLL_DVCO>; + reset-names = "dvco"; + #clock-cells = <0>; + clock-output-names = "dfllCPU_out"; + vdd-cpu-supply = <&vdd_cpu>; + + nvidia,sample-rate = <12500>; + nvidia,droop-ctrl = <0x00000f00>; + nvidia,force-mode = <1>; + nvidia,cf = <10>; + nvidia,ci = <0>; + nvidia,cg = <2>; + + nvidia,i2c-fs-rate = <400000>; + }; + + - | + #include + #include + #include + + clock@70110000 { + compatible = "nvidia,tegra210-dfll"; + reg = <0x70110000 0x100>, /* DFLL control */ + <0x70110000 0x100>, /* I2C output control */ + <0x70110100 0x100>, /* Integrated I2C controller */ + <0x70110200 0x100>; /* Look-up table RAM */ + interrupts = ; + clocks = <&tegra_car TEGRA210_CLK_DFLL_SOC>, + <&tegra_car TEGRA210_CLK_DFLL_REF>, + <&tegra_car TEGRA210_CLK_I2C5>; + clock-names = "soc", "ref", "i2c"; + resets = <&tegra_car TEGRA210_RST_DFLL_DVCO>, + <&tegra_car 155>; + reset-names = "dvco", "dfll"; + #clock-cells = <0>; + clock-output-names = "dfllCPU_out"; + vdd-cpu-supply = <&vdd_cpu>; + + nvidia,sample-rate = <25000>; + nvidia,droop-ctrl = <0x00000f00>; + nvidia,force-mode = <1>; + nvidia,cf = <6>; + nvidia,ci = <0>; + nvidia,cg = <2>; + + nvidia,pwm-min-microvolts = <708000>; /* 708mV */ + nvidia,pwm-period-nanoseconds = <2500>; /* 2.5us */ + nvidia,pwm-to-pmic; + nvidia,pwm-tristate-microvolts = <1000000>; + nvidia,pwm-voltage-step-microvolts = <19200>; /* 19.2mV */ + }; diff --git a/Documentation/devicetree/bindings/clock/qcom,eliza-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,eliza-dispcc.yaml new file mode 100644 index 000000000000..0935ec185dde --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,eliza-dispcc.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,eliza-dispcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Display Clock & Reset Controller for Qualcomm Eliza SoC + +maintainers: + - Bjorn Andersson + - Konrad Dybcio + - Krzysztof Kozlowski + +description: | + Display clock control module provides the clocks, resets and power + domains on Qualcomm Eliza SoC platform. + + See also: + - include/dt-bindings/clock/qcom,eliza-dispcc.h + +properties: + compatible: + enum: + - qcom,eliza-dispcc + + clocks: + items: + - description: Board XO source + - description: Board Always On XO source + - description: Display's AHB clock + - description: sleep clock + - description: Byte clock from DSI PHY0 + - description: Pixel clock from DSI PHY0 + - description: Byte clock from DSI PHY1 + - description: Pixel clock from DSI PHY1 + - description: Link clock from DP PHY0 + - description: VCO DIV clock from DP PHY0 + - description: Link clock from DP PHY1 + - description: VCO DIV clock from DP PHY1 + - description: Link clock from DP PHY2 + - description: VCO DIV clock from DP PHY2 + - description: Link clock from DP PHY3 + - description: VCO DIV clock from DP PHY3 + - description: HDMI link clock from HDMI PHY + + power-domains: + maxItems: 1 + + required-opps: + maxItems: 1 + +required: + - compatible + - clocks + - '#power-domain-cells' + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + clock-controller@af00000 { + compatible = "qcom,eliza-dispcc"; + reg = <0x0af00000 0x20000>; + clocks = <&bi_tcxo_div2>, + <&bi_tcxo_ao_div2>, + <&gcc GCC_DISP_AHB_CLK>, + <&sleep_clk>, + <&dsi0_phy DSI_BYTE_PLL_CLK>, + <&dsi0_phy DSI_PIXEL_PLL_CLK>, + <&dsi1_phy DSI_BYTE_PLL_CLK>, + <&dsi1_phy DSI_PIXEL_PLL_CLK>, + <&dp0_phy 0>, + <&dp0_phy 1>, + <&dp1_phy 0>, + <&dp1_phy 1>, + <&dp2_phy 0>, + <&dp2_phy 1>, + <&dp3_phy 0>, + <&dp3_phy 1>, + <&hdmi_phy>; + + #clock-cells = <1>; + #power-domain-cells = <1>; + #reset-cells = <1>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + required-opps = <&rpmhpd_opp_low_svs>; + }; +... diff --git a/Documentation/devicetree/bindings/clock/qcom,glymur-dispcc.yaml b/Documentation/devicetree/bindings/clock/qcom,glymur-dispcc.yaml index 45f027c70e03..9de4ba71f1d9 100644 --- a/Documentation/devicetree/bindings/clock/qcom,glymur-dispcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,glymur-dispcc.yaml @@ -4,14 +4,14 @@ $id: http://devicetree.org/schemas/clock/qcom,glymur-dispcc.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Qualcomm Display Clock & Reset Controller on GLYMUR +title: Qualcomm Display Clock & Reset Controller on Glymur SoC maintainers: - Taniya Das description: | Qualcomm display clock control module which supports the clocks, resets and - power domains for the MDSS instances on GLYMUR SoC. + power domains for the MDSS instances on Glymur SoC. See also: include/dt-bindings/clock/qcom,dispcc-glymur.h diff --git a/Documentation/devicetree/bindings/clock/qcom,ipq5210-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,ipq5210-gcc.yaml new file mode 100644 index 000000000000..f1cc3fc19085 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,ipq5210-gcc.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,ipq5210-gcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Global Clock & Reset Controller on IPQ5210 + +maintainers: + - Bjorn Andersson + - Kathiravan Thirumoorthy + +description: | + Qualcomm global clock control module provides the clocks, resets and power + domains on IPQ5210 + + See also: + include/dt-bindings/clock/qcom,ipq5210-gcc.h + include/dt-bindings/reset/qcom,ipq5210-gcc.h + +properties: + compatible: + const: qcom,ipq5210-gcc + + clocks: + items: + - description: Board XO source + - description: Sleep clock source + - description: PCIE30 PHY0 pipe clock source + - description: PCIE30 PHY1 pipe clock source + - description: USB3 PHY pipe clock source + - description: NSS common clock source + + '#power-domain-cells': false + + '#interconnect-cells': + const: 1 + +required: + - compatible + - clocks + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + clock-controller@1800000 { + compatible = "qcom,ipq5210-gcc"; + reg = <0x01800000 0x40000>; + clocks = <&xo_board_clk>, + <&sleep_clk>, + <&pcie30_phy0_pipe_clk>, + <&pcie30_phy1_pipe_clk>, + <&usb3phy_0_cc_pipe_clk>, + <&nss_cmn_clk>; + #clock-cells = <1>; + #reset-cells = <1>; + }; +... diff --git a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml index 817d51135fbf..de338c05190f 100644 --- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml @@ -26,6 +26,8 @@ properties: enum: - qcom,ipq5018-cmn-pll - qcom,ipq5424-cmn-pll + - qcom,ipq6018-cmn-pll + - qcom,ipq8074-cmn-pll - qcom,ipq9574-cmn-pll reg: diff --git a/Documentation/devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml b/Documentation/devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml index 5490a975f3db..466c884aa2ba 100644 --- a/Documentation/devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml @@ -20,7 +20,9 @@ description: | properties: compatible: enum: + - qcom,glymur-gxclkctl - qcom,kaanapali-gxclkctl + - qcom,sm8750-gxclkctl power-domains: description: diff --git a/Documentation/devicetree/bindings/clock/qcom,milos-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,milos-gcc.yaml index cf244c155f9a..c65a6ad893d2 100644 --- a/Documentation/devicetree/bindings/clock/qcom,milos-gcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,milos-gcc.yaml @@ -8,16 +8,21 @@ title: Qualcomm Global Clock & Reset Controller on Milos maintainers: - Luca Weiss + - Taniya Das description: | Qualcomm global clock control module provides the clocks, resets and power domains on Milos. - See also: include/dt-bindings/clock/qcom,milos-gcc.h + See also: + - include/dt-bindings/clock/qcom,eliza-gcc.h + - include/dt-bindings/clock/qcom,milos-gcc.h properties: compatible: - const: qcom,milos-gcc + enum: + - qcom,eliza-gcc + - qcom,milos-gcc clocks: items: @@ -30,9 +35,14 @@ properties: - description: UFS Phy Tx symbol 0 clock source - description: USB3 Phy wrapper pipe clock source + power-domains: + items: + - description: CX domain + required: - compatible - clocks + - power-domains - '#power-domain-cells' allOf: @@ -43,6 +53,7 @@ unevaluatedProperties: false examples: - | #include + #include clock-controller@100000 { compatible = "qcom,milos-gcc"; reg = <0x00100000 0x1f4200>; @@ -54,6 +65,7 @@ examples: <&ufs_mem_phy 1>, <&ufs_mem_phy 2>, <&usb_1_qmpphy>; + power-domains = <&rpmhpd RPMHPD_CX>; #clock-cells = <1>; #reset-cells = <1>; #power-domain-cells = <1>; diff --git a/Documentation/devicetree/bindings/clock/qcom,nord-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,nord-gcc.yaml new file mode 100644 index 000000000000..e35136722a93 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,nord-gcc.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,nord-gcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Global Clock & Reset Controller on Nord SoC + +maintainers: + - Taniya Das + +description: | + Qualcomm global clock control module provides the clocks, resets and power + domains on Nord SoC. + + See also: include/dt-bindings/clock/qcom,nord-gcc.h + +properties: + compatible: + const: qcom,nord-gcc + + clocks: + items: + - description: Board XO source + - description: Sleep clock source + - description: PCIE A Pipe clock source + - description: PCIE B Pipe clock source + - description: PCIE C Pipe clock source + - description: PCIE D Pipe clock source + +required: + - compatible + - clocks + - '#power-domain-cells' + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + clock-controller@100000 { + compatible = "qcom,nord-gcc"; + reg = <0x00100000 0x1f4200>; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&sleep_clk>, + <&pcie_a_pipe_clk>, + <&pcie_b_pipe_clk>, + <&pcie_c_pipe_clk>, + <&pcie_d_pipe_clk>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + +... diff --git a/Documentation/devicetree/bindings/clock/qcom,nord-negcc.yaml b/Documentation/devicetree/bindings/clock/qcom,nord-negcc.yaml new file mode 100644 index 000000000000..749389f65ee1 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,nord-negcc.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,nord-negcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Global North East Clock & Reset Controller on Nord SoC + +maintainers: + - Taniya Das + +description: | + Qualcomm global clock control (NE) module provides the clocks, resets + and power domains on Nord SoC. + + See also: include/dt-bindings/clock/qcom,nord-negcc.h + +properties: + compatible: + const: qcom,nord-negcc + + clocks: + items: + - description: Board XO source + - description: Sleep clock source + - description: UFS Phy Rx symbol 0 clock source + - description: UFS Phy Rx symbol 1 clock source + - description: UFS Phy Tx symbol 0 clock source + - description: USB3 Phy sec wrapper pipe clock source + - description: USB3 Phy wrapper pipe clock source + +required: + - compatible + - clocks + - '#power-domain-cells' + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + clock-controller@8900000 { + compatible = "qcom,nord-negcc"; + reg = <0x08900000 0xf4200>; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&sleep_clk>, + <&ufs_phy_rx_symbol_0_clk>, + <&ufs_phy_rx_symbol_1_clk>, + <&ufs_phy_tx_symbol_0_clk>, + <&usb3_phy_sec_pipe_clk>, + <&usb3_phy_pipe_clk>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + +... diff --git a/Documentation/devicetree/bindings/clock/qcom,nord-nwgcc.yaml b/Documentation/devicetree/bindings/clock/qcom,nord-nwgcc.yaml new file mode 100644 index 000000000000..ce33f966bdfd --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,nord-nwgcc.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,nord-nwgcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Global North West and South East Clock & Reset Controller + on Nord SoC + +maintainers: + - Taniya Das + +description: | + Qualcomm global clock control (NW, SE) module provides the clocks, resets + and power domains on Nord SoC. + + See also: + include/dt-bindings/clock/qcom,nord-nwgcc.h + include/dt-bindings/clock/qcom,nord-segcc.h + +properties: + compatible: + enum: + - qcom,nord-nwgcc + - qcom,nord-segcc + + clocks: + items: + - description: Board XO source + - description: Sleep clock source + +required: + - compatible + - clocks + - '#power-domain-cells' + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + clock-controller@8b00000 { + compatible = "qcom,nord-nwgcc"; + reg = <0x08b00000 0xf4200>; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&sleep_clk>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + +... diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml b/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml index 3f5f1336262e..a2c404a57981 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml @@ -17,9 +17,11 @@ description: | properties: compatible: enum: + - qcom,eliza-rpmh-clk - qcom,glymur-rpmh-clk - qcom,kaanapali-rpmh-clk - qcom,milos-rpmh-clk + - qcom,nord-rpmh-clk - qcom,qcs615-rpmh-clk - qcom,qdu1000-rpmh-clk - qcom,sa8775p-rpmh-clk diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8450-gpucc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8450-gpucc.yaml index 6feaa32569f9..fdbdf605ee69 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8450-gpucc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8450-gpucc.yaml @@ -8,12 +8,14 @@ title: Qualcomm Graphics Clock & Reset Controller on SM8450 maintainers: - Konrad Dybcio + - Taniya Das description: | Qualcomm graphics clock control module provides the clocks, resets and power domains on Qualcomm SoCs. - See also:: + See also: + include/dt-bindings/clock/qcom,glymur-gpucc.h include/dt-bindings/clock/qcom,kaanapali-gpucc.h include/dt-bindings/clock/qcom,milos-gpucc.h include/dt-bindings/clock/qcom,sar2130p-gpucc.h @@ -22,11 +24,13 @@ description: | include/dt-bindings/clock/qcom,sm8550-gpucc.h include/dt-bindings/reset/qcom,sm8450-gpucc.h include/dt-bindings/reset/qcom,sm8650-gpucc.h + include/dt-bindings/reset/qcom,sm8750-gpucc.h include/dt-bindings/reset/qcom,x1e80100-gpucc.h properties: compatible: enum: + - qcom,glymur-gpucc - qcom,kaanapali-gpucc - qcom,milos-gpucc - qcom,sar2130p-gpucc @@ -35,6 +39,7 @@ properties: - qcom,sm8475-gpucc - qcom,sm8550-gpucc - qcom,sm8650-gpucc + - qcom,sm8750-gpucc - qcom,x1e80100-gpucc - qcom,x1p42100-gpucc @@ -44,6 +49,16 @@ properties: - description: GPLL0 main branch source - description: GPLL0 div branch source + power-domains: + items: + - description: A phandle to the MX power-domain + - description: A phandle to the CX power-domain + + required-opps: + items: + - description: A phandle to an OPP node describing MX performance points + - description: A phandle to an OPP node describing CX performance points + required: - compatible - clocks @@ -51,6 +66,16 @@ required: allOf: - $ref: qcom,gcc.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,sm8750-gpucc + then: + required: + - power-domains + - required-opps unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml index e6beebd6a36e..7bbf120d928c 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml @@ -15,6 +15,7 @@ description: | domains on SM8450. See also: + include/dt-bindings/clock/qcom,glymur-videocc.h include/dt-bindings/clock/qcom,kaanapali-videocc.h include/dt-bindings/clock/qcom,sm8450-videocc.h include/dt-bindings/clock/qcom,sm8650-videocc.h @@ -23,6 +24,7 @@ description: | properties: compatible: enum: + - qcom,glymur-videocc - qcom,kaanapali-videocc - qcom,sm8450-videocc - qcom,sm8475-videocc @@ -63,6 +65,7 @@ allOf: compatible: contains: enum: + - qcom,glymur-videocc - qcom,kaanapali-videocc - qcom,sm8450-videocc - qcom,sm8550-videocc diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml index 784fef830681..1ccdf4b0f5dd 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml @@ -15,7 +15,9 @@ description: | power domains on SM8550 See also: + - include/dt-bindings/clock/qcom,eliza-tcsr.h - include/dt-bindings/clock/qcom,glymur-tcsr.h + - include/dt-bindings/clock/qcom,nord-tcsrcc.h - include/dt-bindings/clock/qcom,sm8550-tcsr.h - include/dt-bindings/clock/qcom,sm8650-tcsr.h - include/dt-bindings/clock/qcom,sm8750-tcsr.h @@ -24,9 +26,11 @@ properties: compatible: items: - enum: + - qcom,eliza-tcsr - qcom,glymur-tcsr - qcom,kaanapali-tcsr - qcom,milos-tcsr + - qcom,nord-tcsrcc - qcom,sar2130p-tcsr - qcom,sm8550-tcsr - qcom,sm8650-tcsr diff --git a/Documentation/devicetree/bindings/clock/renesas,rzg2l-cpg.yaml b/Documentation/devicetree/bindings/clock/renesas,rzg2l-cpg.yaml index 8c18616e5c4d..c0ce687d83ee 100644 --- a/Documentation/devicetree/bindings/clock/renesas,rzg2l-cpg.yaml +++ b/Documentation/devicetree/bindings/clock/renesas,rzg2l-cpg.yaml @@ -28,19 +28,30 @@ properties: - renesas,r9a07g044-cpg # RZ/G2{L,LC} - renesas,r9a07g054-cpg # RZ/V2L - renesas,r9a08g045-cpg # RZ/G3S + - renesas,r9a08g046-cpg # RZ/G3L - renesas,r9a09g011-cpg # RZ/V2M reg: maxItems: 1 clocks: - maxItems: 1 + minItems: 1 + items: + - description: Clock source to CPG can be either from external clock + input (EXCLK) or crystal oscillator (XIN/XOUT). + - description: ETH0 TXC clock input + - description: ETH0 RXC clock input + - description: ETH1 TXC clock input + - description: ETH1 RXC clock input clock-names: - description: - Clock source to CPG can be either from external clock input (EXCLK) or - crystal oscillator (XIN/XOUT). - const: extal + minItems: 1 + items: + - const: extal + - const: eth0_txc_tx_clk + - const: eth0_rxc_rx_clk + - const: eth1_txc_tx_clk + - const: eth1_rxc_rx_clk '#clock-cells': description: | @@ -74,6 +85,25 @@ required: - '#power-domain-cells' - '#reset-cells' +allOf: + - if: + properties: + compatible: + contains: + const: renesas,r9a08g046-cpg + then: + properties: + clocks: + minItems: 5 + clock-names: + minItems: 5 + else: + properties: + clocks: + maxItems: 1 + clock-names: + maxItems: 1 + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/clock/rockchip,rv1126b-cru.yaml b/Documentation/devicetree/bindings/clock/rockchip,rv1126b-cru.yaml index 04b0a5c51e4e..b6d3a04be8f1 100644 --- a/Documentation/devicetree/bindings/clock/rockchip,rv1126b-cru.yaml +++ b/Documentation/devicetree/bindings/clock/rockchip,rv1126b-cru.yaml @@ -17,6 +17,7 @@ description: properties: compatible: enum: + - rockchip,rv1103b-cru - rockchip,rv1126b-cru reg: diff --git a/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml b/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml index 1318720193b3..6b1fc61a2ff9 100644 --- a/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml +++ b/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml @@ -35,6 +35,7 @@ properties: - samsung,exynosautov920-cmu-cpucl0 - samsung,exynosautov920-cmu-cpucl1 - samsung,exynosautov920-cmu-cpucl2 + - samsung,exynosautov920-cmu-g3d - samsung,exynosautov920-cmu-hsi0 - samsung,exynosautov920-cmu-hsi1 - samsung,exynosautov920-cmu-hsi2 @@ -287,6 +288,26 @@ allOf: - const: oscclk - const: noc + - if: + properties: + compatible: + contains: + const: samsung,exynosautov920-cmu-g3d + + then: + properties: + clocks: + items: + - description: External reference clock (38.4 MHz) + - description: CMU_G3D SWITCH clock (from CMU_TOP) + - description: CMU_G3D NOCP clock (from CMU_TOP) + + clock-names: + items: + - const: oscclk + - const: switch + - const: nocp + required: - compatible - "#clock-cells" diff --git a/Documentation/devicetree/bindings/clock/tenstorrent,atlantis-prcm-rcpu.yaml b/Documentation/devicetree/bindings/clock/tenstorrent,atlantis-prcm-rcpu.yaml new file mode 100644 index 000000000000..7fa16526efce --- /dev/null +++ b/Documentation/devicetree/bindings/clock/tenstorrent,atlantis-prcm-rcpu.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/tenstorrent,atlantis-prcm-rcpu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Tenstorrent Atlantis PRCM (Power, Reset, Clock Management) Module + +maintainers: + - Anirudh Srinivasan + +description: + Multifunctional register block found in Tenstorrent Atlantis SoC whose main + function is to control clocks and resets. This block is instantiated multiple + times in the SoC, each block controls clock and resets for a different + subsystem. RCPU prcm serves low speed IO interfaces. + +properties: + compatible: + enum: + - tenstorrent,atlantis-prcm-rcpu + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + "#clock-cells": + const: 1 + description: + See for valid indices. + + "#reset-cells": + const: 1 + +required: + - compatible + - reg + - clocks + - "#clock-cells" + - "#reset-cells" + +additionalProperties: false + +examples: + - | + clock-controller@a8000000 { + compatible = "tenstorrent,atlantis-prcm-rcpu"; + reg = <0xa8000000 0x10000>; + clocks = <&osc_24m>; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index d97b29e49bf5..8ca0292490a2 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -300,6 +300,40 @@ properties: $ref: /schemas/types.yaml#/definitions/uint8-array maxItems: 4 + sink-load-step: + description: Indicates the preferred load step slew rate in mA/usec for + the port (in sink mode). This property is defined in "6.5.13.7" of + "USB Power Delivery Specification Revision 3.1 Version 1.8". + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [150, 500] + default: 150 + + sink-load-characteristics: + description: Indicates the port's (in sink mode) preferred load + characteristics. Users can leverage SINK_LOAD_CHAR() defined in + dt-bindings/usb/pd.h to populate this field. This property is defined in + "6.5.13.8" of "USB Power Delivery Specification Revision 3.1 Version 1.8". + $ref: /schemas/types.yaml#/definitions/uint16 + + sink-compliance: + description: Represents the types of sources the sink device has been tested + and certified with. This property is defined in "6.5.13.9" of + "USB Power Delivery Specification Revision 3.1 Version 1.8" + Bit 0 when set indicates it has been tested on LPS compliant source + Bit 1 when set indicates it has been tested on PS1 compliant source + Bit 2 when set indicates it has been tested on PS2 compliant source + $ref: /schemas/types.yaml#/definitions/uint8 + maximum: 7 + + charging-adapter-pdp-milliwatt: + description: This corresponds to the Power Delivery Profile rating of the + charging adapter shipped or recommended for use with the connector port. + This property is a requirement to infer the USB PD property + "SPR Sink Operational PDP" given in "6.5.13.14" of + "USB Power Delivery Specification Revision 3.1 Version 1.8". + minimum: 0 + maximum: 100000 + dependencies: pd-disable: [typec-power-opmode] sink-vdos-v1: [ sink-vdos ] @@ -331,8 +365,9 @@ $defs: "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.3 Sink Capabilities Message, the order of each entry(PDO) should follow the PD spec chapter 6.4.1. Required for power sink and power dual role. User - can specify the sink PDO array via PDO_FIXED/BATT/VAR/PPS_APDO() defined - in dt-bindings/usb/pd.h. + can specify the sink PDO array via + PDO_FIXED/BATT/VAR/PPS_APDO/SPR_AVS_SNK_APDO() defined in + dt-bindings/usb/pd.h. minItems: 1 maxItems: 7 $ref: /schemas/types.yaml#/definitions/uint32-array diff --git a/Documentation/devicetree/bindings/display/apple,h7-display-pipe-mipi.yaml b/Documentation/devicetree/bindings/display/apple,h7-display-pipe-mipi.yaml index 5e6da66499a5..d7c822df8a94 100644 --- a/Documentation/devicetree/bindings/display/apple,h7-display-pipe-mipi.yaml +++ b/Documentation/devicetree/bindings/display/apple,h7-display-pipe-mipi.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Apple pre-DCP display controller MIPI interface maintainers: - - Sasha Finkelstein + - Sasha Finkelstein description: The MIPI controller part of the pre-DCP Apple display controller diff --git a/Documentation/devicetree/bindings/display/apple,h7-display-pipe.yaml b/Documentation/devicetree/bindings/display/apple,h7-display-pipe.yaml index 102fb1804c0c..571fa32db2cf 100644 --- a/Documentation/devicetree/bindings/display/apple,h7-display-pipe.yaml +++ b/Documentation/devicetree/bindings/display/apple,h7-display-pipe.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Apple pre-DCP display controller maintainers: - - Sasha Finkelstein + - Sasha Finkelstein description: A secondary display controller used to drive the "touchbar" on diff --git a/Documentation/devicetree/bindings/display/bridge/lontium,lt8912b.yaml b/Documentation/devicetree/bindings/display/bridge/lontium,lt8912b.yaml index 63f000ebc9c5..988351f3cd01 100644 --- a/Documentation/devicetree/bindings/display/bridge/lontium,lt8912b.yaml +++ b/Documentation/devicetree/bindings/display/bridge/lontium,lt8912b.yaml @@ -39,9 +39,6 @@ properties: $ref: /schemas/media/video-interfaces.yaml# unevaluatedProperties: false - properties: - data-lanes: true - required: - data-lanes diff --git a/Documentation/devicetree/bindings/display/panel/apple,summit.yaml b/Documentation/devicetree/bindings/display/panel/apple,summit.yaml index f081755325e9..1c1ba59467f3 100644 --- a/Documentation/devicetree/bindings/display/panel/apple,summit.yaml +++ b/Documentation/devicetree/bindings/display/panel/apple,summit.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Apple "Summit" display panel maintainers: - - Sasha Finkelstein + - Sasha Finkelstein description: An OLED panel used as a touchbar on certain Apple laptops. diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple-lvds-dual-ports.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple-lvds-dual-ports.yaml index 548f5ac14500..8a2f6feafd37 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple-lvds-dual-ports.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple-lvds-dual-ports.yaml @@ -40,8 +40,12 @@ properties: - auo,g185han01 # AU Optronics Corporation 19.0" (1280x1024) TFT LCD panel - auo,g190ean01 + # AU Optronics Corporation 21.5" FHD (1920x1080) color TFT LCD panel + - auo,t215hvn01 # BOE AV123Z7M-N17 12.3" (1920x720) LVDS TFT LCD panel - boe,av123z7m-n17 + # InnoLux 15.6" FHD (1920x1080) TFT LCD panel + - innolux,g156hce-l01 # Kaohsiung Opto-Electronics Inc. 10.1" WUXGA (1920 x 1200) LVDS TFT LCD panel - koe,tx26d202vm0bwa # Lincoln Technology Solutions, LCD185-101CT 10.1" TFT 1920x1200 diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 9b8fa03f7cbc..3e41ed0ef5d5 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -61,8 +61,6 @@ properties: - auo,p238han01 # AU Optronics Corporation 31.5" FHD (1920x1080) TFT LCD panel - auo,p320hvn03 - # AU Optronics Corporation 21.5" FHD (1920x1080) color TFT LCD panel - - auo,t215hvn01 # Shanghai AVIC Optoelectronics 7" 1024x600 color TFT-LCD panel - avic,tm070ddh03 # BOE AV101HDT-a10 10.1" 1280x720 LVDS panel @@ -180,8 +178,6 @@ properties: - innolux,g121xce-l01 # InnoLux 15.0" G150XGE-L05 XGA (1024x768) TFT LCD panel - innolux,g150xge-l05 - # InnoLux 15.6" FHD (1920x1080) TFT LCD panel - - innolux,g156hce-l01 # InnoLux 13.3" FHD (1920x1080) TFT LCD panel - innolux,n133hse-ea1 # InnoLux 15.6" WXGA TFT LCD panel @@ -204,6 +200,8 @@ properties: - lemaker,bl035-rgb-002 # LG 7" (800x480 pixels) TFT LCD panel - lg,lb070wv8 + # LG 6.1" (1440x3120) IPS LCD panel + - lg,sw49410 # Logic Technologies LT161010-2NHC 7" WVGA TFT Cap Touch Module - logictechno,lt161010-2nhc # Logic Technologies LT161010-2NHR 7" WVGA TFT Resistive Touch Module diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-hdmi.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-hdmi.yaml index f77197e4869f..b4bf2662780b 100644 --- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-hdmi.yaml +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-hdmi.yaml @@ -82,6 +82,10 @@ properties: description: phandle of a display panel $ref: /schemas/types.yaml#/definitions/phandle + port: + description: HDMI output port for connection to HDMI connector or bridge + $ref: /schemas/graph.yaml#/properties/port + "#sound-dai-cells": const: 0 @@ -97,8 +101,13 @@ required: - reset-names - pll-supply - vdd-supply - - nvidia,ddc-i2c-bus - - nvidia,hpd-gpio + +anyOf: + - required: + - nvidia,ddc-i2c-bus + - nvidia,hpd-gpio + - required: + - port examples: - | diff --git a/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml b/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml index 38fcee91211e..49a007cbcd3a 100644 --- a/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml +++ b/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml @@ -36,34 +36,50 @@ properties: reg: description: Addresses to each DSS memory region described in the SoC's TRM. - items: - - description: common DSS register area - - description: VIDL1 light video plane - - description: VID video plane - - description: OVR1 overlay manager for vp1 - - description: OVR2 overlay manager for vp2 - - description: VP1 video port 1 - - description: VP2 video port 2 - - description: common1 DSS register area + oneOf: + - items: + - description: common DSS register area + - description: VIDL1 light video plane + - description: VID video plane + - description: OVR1 overlay manager for vp1 + - description: OVR2 overlay manager for vp2 + - description: VP1 video port 1 + - description: VP2 video port 2 + - description: common1 DSS register area + - items: + - description: common DSS register area + - description: VIDL1 light video plane + - description: OVR1 overlay manager for vp1 + - description: VP1 video port 1 + - description: common1 DSS register area reg-names: - items: - - const: common - - const: vidl1 - - const: vid - - const: ovr1 - - const: ovr2 - - const: vp1 - - const: vp2 - - const: common1 + oneOf: + - items: + - const: common + - const: vidl1 + - const: vid + - const: ovr1 + - const: ovr2 + - const: vp1 + - const: vp2 + - const: common1 + - items: + - const: common + - const: vidl1 + - const: ovr1 + - const: vp1 + - const: common1 clocks: + minItems: 2 items: - description: fck DSS functional clock - description: vp1 Video Port 1 pixel clock - description: vp2 Video Port 2 pixel clock clock-names: + minItems: 2 items: - const: fck - const: vp1 @@ -179,6 +195,24 @@ allOf: ports: properties: port@1: false + reg: + maxItems: 5 + reg-names: + maxItems: 5 + clocks: + maxItems: 2 + clock-names: + maxItems: 2 + else: + properties: + reg: + minItems: 8 + reg-names: + minItems: 8 + clocks: + minItems: 3 + clock-names: + minItems: 3 - if: properties: diff --git a/Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml b/Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml new file mode 100644 index 000000000000..8095214ccaf7 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/loongson,ls2k0300-dma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Loongson-2 Multi-Channel DMA controller + +description: + The Loongson-2 Multi-Channel DMA controller is used for transferring data + between system memory and the peripherals on the APB bus. + +maintainers: + - Binbin Zhou + +allOf: + - $ref: dma-controller.yaml# + +properties: + compatible: + enum: + - loongson,ls2k0300-dma + - loongson,ls2k3000-dma + + reg: + maxItems: 1 + + interrupts: + description: + Should contain all of the per-channel DMA interrupts in ascending order + with respect to the DMA channel index. + minItems: 4 + maxItems: 8 + + clocks: + maxItems: 1 + + '#dma-cells': + const: 2 + description: | + DMA request from clients consists of 2 cells: + 1. Channel index + 2. Transfer request factor number, If no transfer factor, use 0. + The number is SoC-specific, and this should be specified with + relation to the device to use the DMA controller. + + dma-channels: + enum: [4, 8] + +required: + - compatible + - reg + - interrupts + - clocks + - '#dma-cells' + - dma-channels + +unevaluatedProperties: false + +examples: + - | + #include + #include + + dma-controller@1612c000 { + compatible = "loongson,ls2k0300-dma"; + reg = <0x1612c000 0xff>; + interrupt-parent = <&liointc0>; + interrupts = <23 IRQ_TYPE_LEVEL_HIGH>, + <24 IRQ_TYPE_LEVEL_HIGH>, + <25 IRQ_TYPE_LEVEL_HIGH>, + <26 IRQ_TYPE_LEVEL_HIGH>, + <27 IRQ_TYPE_LEVEL_HIGH>, + <28 IRQ_TYPE_LEVEL_HIGH>, + <29 IRQ_TYPE_LEVEL_HIGH>, + <30 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LS2K0300_CLK_APB_GATE>; + #dma-cells = <2>; + dma-channels = <8>; + }; +... diff --git a/Documentation/devicetree/bindings/dma/renesas,rz-dmac.yaml b/Documentation/devicetree/bindings/dma/renesas,rz-dmac.yaml index d137b9cbaee9..0155a15e200b 100644 --- a/Documentation/devicetree/bindings/dma/renesas,rz-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/renesas,rz-dmac.yaml @@ -19,6 +19,7 @@ properties: - renesas,r9a07g044-dmac # RZ/G2{L,LC} - renesas,r9a07g054-dmac # RZ/V2L - renesas,r9a08g045-dmac # RZ/G3S + - renesas,r9a08g046-dmac # RZ/G3L - const: renesas,rz-dmac - items: @@ -29,6 +30,13 @@ properties: - const: renesas,r9a09g057-dmac # RZ/V2H(P) + - const: renesas,r9a09g077-dmac # RZ/T2H + + - items: + - enum: + - renesas,r9a09g087-dmac # RZ/N2H + - const: renesas,r9a09g077-dmac + reg: items: - description: Control and channel register block @@ -36,27 +44,12 @@ properties: minItems: 1 interrupts: + minItems: 16 maxItems: 17 interrupt-names: - items: - - const: error - - const: ch0 - - const: ch1 - - const: ch2 - - const: ch3 - - const: ch4 - - const: ch5 - - const: ch6 - - const: ch7 - - const: ch8 - - const: ch9 - - const: ch10 - - const: ch11 - - const: ch12 - - const: ch13 - - const: ch14 - - const: ch15 + minItems: 16 + maxItems: 17 clocks: items: @@ -122,6 +115,35 @@ required: allOf: - $ref: dma-controller.yaml# + - if: + properties: + compatible: + contains: + enum: + - renesas,rz-dmac + - renesas,r9a09g057-dmac + then: + properties: + interrupt-names: + items: + - const: error + - const: ch0 + - const: ch1 + - const: ch2 + - const: ch3 + - const: ch4 + - const: ch5 + - const: ch6 + - const: ch7 + - const: ch8 + - const: ch9 + - const: ch10 + - const: ch11 + - const: ch12 + - const: ch13 + - const: ch14 + - const: ch15 + - if: properties: compatible: @@ -131,6 +153,7 @@ allOf: - renesas,r9a07g044-dmac - renesas,r9a07g054-dmac - renesas,r9a08g045-dmac + - renesas,r9a08g046-dmac then: properties: reg: @@ -189,6 +212,49 @@ allOf: - renesas,icu - resets + - if: + properties: + compatible: + contains: + const: renesas,r9a09g077-dmac + then: + properties: + reg: + maxItems: 1 + clocks: + maxItems: 1 + + clock-names: false + resets: false + reset-names: false + + interrupts: + maxItems: 16 + + interrupt-names: + items: + - const: ch0 + - const: ch1 + - const: ch2 + - const: ch3 + - const: ch4 + - const: ch5 + - const: ch6 + - const: ch7 + - const: ch8 + - const: ch9 + - const: ch10 + - const: ch11 + - const: ch12 + - const: ch13 + - const: ch14 + - const: ch15 + + required: + - clocks + - power-domains + - renesas,icu + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml index 216cda21c538..804514732dbe 100644 --- a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml @@ -21,6 +21,7 @@ properties: - enum: - snps,axi-dma-1.01a - intel,kmb-axi-dma + - sophgo,cv1800b-axi-dma - starfive,jh7110-axi-dma - starfive,jh8100-axi-dma - items: @@ -68,6 +69,8 @@ properties: dma-noncoherent: true + dma-coherent: true + resets: minItems: 1 maxItems: 2 diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt deleted file mode 100644 index b567107270cb..000000000000 --- a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt +++ /dev/null @@ -1,111 +0,0 @@ -Xilinx AXI VDMA engine, it does transfers between memory and video devices. -It can be configured to have one channel or two channels. If configured -as two channels, one is to transmit to the video device and another is -to receive from the video device. - -Xilinx AXI DMA engine, it does transfers between memory and AXI4 stream -target devices. It can be configured to have one channel or two channels. -If configured as two channels, one is to transmit to the device and another -is to receive from the device. - -Xilinx AXI CDMA engine, it does transfers between memory-mapped source -address and a memory-mapped destination address. - -Xilinx AXI MCDMA engine, it does transfer between memory and AXI4 stream -target devices. It can be configured to have up to 16 independent transmit -and receive channels. - -Required properties: -- compatible: Should be one of- - "xlnx,axi-vdma-1.00.a" - "xlnx,axi-dma-1.00.a" - "xlnx,axi-cdma-1.00.a" - "xlnx,axi-mcdma-1.00.a" -- #dma-cells: Should be <1>, see "dmas" property below -- reg: Should contain VDMA registers location and length. -- xlnx,addrwidth: Should be the vdma addressing size in bits(ex: 32 bits). -- dma-ranges: Should be as the following . -- dma-channel child node: Should have at least one channel and can have up to - two channels per device. This node specifies the properties of each - DMA channel (see child node properties below). -- clocks: Input clock specifier. Refer to common clock bindings. -- clock-names: List of input clocks - For VDMA: - Required elements: "s_axi_lite_aclk" - Optional elements: "m_axi_mm2s_aclk" "m_axi_s2mm_aclk", - "m_axis_mm2s_aclk", "s_axis_s2mm_aclk" - For CDMA: - Required elements: "s_axi_lite_aclk", "m_axi_aclk" - For AXIDMA and MCDMA: - Required elements: "s_axi_lite_aclk" - Optional elements: "m_axi_mm2s_aclk", "m_axi_s2mm_aclk", - "m_axi_sg_aclk" - -Required properties for VDMA: -- xlnx,num-fstores: Should be the number of framebuffers as configured in h/w. - -Optional properties for AXI DMA and MCDMA: -- xlnx,sg-length-width: Should be set to the width in bits of the length - register as configured in h/w. Takes values {8...26}. If the property - is missing or invalid then the default value 23 is used. This is the - maximum value that is supported by all IP versions. - -Optional properties for AXI DMA: -- xlnx,axistream-connected: Tells whether DMA is connected to AXI stream IP. -- xlnx,irq-delay: Tells the interrupt delay timeout value. Valid range is from - 0-255. Setting this value to zero disables the delay timer interrupt. - 1 timeout interval = 125 * clock period of SG clock. -Optional properties for VDMA: -- xlnx,flush-fsync: Tells which channel to Flush on Frame sync. - It takes following values: - {1}, flush both channels - {2}, flush mm2s channel - {3}, flush s2mm channel - -Required child node properties: -- compatible: - For VDMA: It should be either "xlnx,axi-vdma-mm2s-channel" or - "xlnx,axi-vdma-s2mm-channel". - For CDMA: It should be "xlnx,axi-cdma-channel". - For AXIDMA and MCDMA: It should be either "xlnx,axi-dma-mm2s-channel" - or "xlnx,axi-dma-s2mm-channel". -- interrupts: Should contain per channel VDMA interrupts. -- xlnx,datawidth: Should contain the stream data width, take values - {32,64...1024}. - -Optional child node properties: -- xlnx,include-dre: Tells hardware is configured for Data - Realignment Engine. -Optional child node properties for VDMA: -- xlnx,genlock-mode: Tells Genlock synchronization is - enabled/disabled in hardware. -- xlnx,enable-vert-flip: Tells vertical flip is - enabled/disabled in hardware(S2MM path). -Optional child node properties for MCDMA: -- dma-channels: Number of dma channels in child node. - -Example: -++++++++ - -axi_vdma_0: axivdma@40030000 { - compatible = "xlnx,axi-vdma-1.00.a"; - #dma_cells = <1>; - reg = < 0x40030000 0x10000 >; - dma-ranges = <0x00000000 0x00000000 0x40000000>; - xlnx,num-fstores = <0x8>; - xlnx,flush-fsync = <0x1>; - xlnx,addrwidth = <0x20>; - clocks = <&clk 0>, <&clk 1>, <&clk 2>, <&clk 3>, <&clk 4>; - clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk", - "m_axis_mm2s_aclk", "s_axis_s2mm_aclk"; - dma-channel@40030000 { - compatible = "xlnx,axi-vdma-mm2s-channel"; - interrupts = < 0 54 4 >; - xlnx,datawidth = <0x40>; - } ; - dma-channel@40030030 { - compatible = "xlnx,axi-vdma-s2mm-channel"; - interrupts = < 0 53 4 >; - xlnx,datawidth = <0x40>; - } ; -} ; diff --git a/Documentation/devicetree/bindings/dma/xilinx/xlnx,axi-dma.yaml b/Documentation/devicetree/bindings/dma/xilinx/xlnx,axi-dma.yaml new file mode 100644 index 000000000000..340ae9e91cb0 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/xilinx/xlnx,axi-dma.yaml @@ -0,0 +1,299 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/xilinx/xlnx,axi-dma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Xilinx AXI VDMA, DMA, CDMA and MCDMA IP + +maintainers: + - Radhey Shyam Pandey + - Abin Joseph + +description: > + Xilinx AXI VDMA engine, it does transfers between memory and video devices. + It can be configured to have one channel or two channels. If configured + as two channels, one is to transmit to the video device and another is + to receive from the video device. + + Xilinx AXI DMA engine, it does transfers between memory and AXI4 stream + target devices. It can be configured to have one channel or two channels. + If configured as two channels, one is to transmit to the device and another + is to receive from the device. + + Xilinx AXI CDMA engine, it does transfers between memory-mapped source + address and a memory-mapped destination address. + + Xilinx AXI MCDMA engine, it does transfer between memory and AXI4 stream + target devices. It can be configured to have up to 16 independent transmit + and receive channels. + +properties: + compatible: + enum: + - xlnx,axi-cdma-1.00.a + - xlnx,axi-dma-1.00.a + - xlnx,axi-mcdma-1.00.a + - xlnx,axi-vdma-1.00.a + + reg: + maxItems: 1 + + "#dma-cells": + const: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + interrupts: + items: + - description: Interrupt for single channel (MM2S or S2MM) + - description: Interrupt for dual channel configuration + minItems: 1 + description: + Interrupt lines for the DMA controller. Only used when + xlnx,axistream-connected is present (DMA connected to AXI Stream + IP). When child dma-channel nodes are present, interrupts are + specified in the child nodes instead. + + clocks: + minItems: 1 + maxItems: 5 + + clock-names: + minItems: 1 + maxItems: 5 + + dma-ranges: true + + xlnx,addrwidth: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [32, 64] + description: The DMA addressing size in bits. + + xlnx,num-fstores: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 32 + description: Should be the number of framebuffers as configured in h/w. + + xlnx,flush-fsync: + type: boolean + description: Tells which channel to Flush on Frame sync. + + xlnx,sg-length-width: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 8 + maximum: 26 + default: 23 + description: + Width in bits of the length register as configured in hardware. + + xlnx,irq-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: + Tells the interrupt delay timeout value. Valid range is from 0-255. + Setting this value to zero disables the delay timer interrupt. + 1 timeout interval = 125 * clock period of SG clock. + + xlnx,axistream-connected: + type: boolean + description: Tells whether DMA is connected to AXI stream IP. + +patternProperties: + "^dma-channel(-mm2s|-s2mm)?$": + type: object + description: + Should have at least one channel and can have up to two channels per + device. This node specifies the properties of each DMA channel. + + properties: + compatible: + enum: + - xlnx,axi-vdma-mm2s-channel + - xlnx,axi-vdma-s2mm-channel + - xlnx,axi-cdma-channel + - xlnx,axi-dma-mm2s-channel + - xlnx,axi-dma-s2mm-channel + + interrupts: + maxItems: 1 + + xlnx,datawidth: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [32, 64, 128, 256, 512, 1024] + description: Should contain the stream data width, take values {32,64...1024}. + + xlnx,include-dre: + type: boolean + description: Tells hardware is configured for Data Realignment Engine. + + xlnx,genlock-mode: + type: boolean + description: Tells Genlock synchronization is enabled/disabled in hardware. + + xlnx,enable-vert-flip: + type: boolean + description: + Tells vertical flip is enabled/disabled in hardware(S2MM path). + + dma-channels: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of dma channels in child node. + + required: + - compatible + - interrupts + - xlnx,datawidth + + additionalProperties: false + +allOf: + - $ref: ../dma-controller.yaml# + + - if: + properties: + compatible: + contains: + const: xlnx,axi-vdma-1.00.a + then: + properties: + clock-names: + items: + - const: s_axi_lite_aclk + - const: m_axi_mm2s_aclk + - const: m_axi_s2mm_aclk + - const: m_axis_mm2s_aclk + - const: s_axis_s2mm_aclk + minItems: 1 + interrupts: false + patternProperties: + "^dma-channel(-mm2s|-s2mm)?$": + properties: + compatible: + enum: + - xlnx,axi-vdma-mm2s-channel + - xlnx,axi-vdma-s2mm-channel + required: + - xlnx,num-fstores + + - if: + properties: + compatible: + contains: + const: xlnx,axi-cdma-1.00.a + then: + properties: + clock-names: + items: + - const: s_axi_lite_aclk + - const: m_axi_aclk + interrupts: false + patternProperties: + "^dma-channel(-mm2s|-s2mm)?$": + properties: + compatible: + enum: + - xlnx,axi-cdma-channel + + - if: + properties: + compatible: + contains: + enum: + - xlnx,axi-dma-1.00.a + - xlnx,axi-mcdma-1.00.a + then: + properties: + clock-names: + items: + - const: s_axi_lite_aclk + - const: m_axi_mm2s_aclk + - const: m_axi_s2mm_aclk + - const: m_axi_sg_aclk + minItems: 1 + patternProperties: + "^dma-channel(-mm2s|-s2mm)?(@[0-9a-f]+)?$": + properties: + compatible: + enum: + - xlnx,axi-dma-mm2s-channel + - xlnx,axi-dma-s2mm-channel + +required: + - "#dma-cells" + - reg + - xlnx,addrwidth + - dma-ranges + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + + dma-controller@40030000 { + compatible = "xlnx,axi-vdma-1.00.a"; + reg = <0x40030000 0x10000>; + #dma-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + dma-ranges = <0x0 0x0 0x40000000>; + clocks = <&clk 0>, <&clk 1>, <&clk 2>, <&clk 3>, <&clk 4>; + clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk", + "m_axi_s2mm_aclk", "m_axis_mm2s_aclk", + "s_axis_s2mm_aclk"; + xlnx,num-fstores = <8>; + xlnx,flush-fsync; + xlnx,addrwidth = <32>; + + dma-channel-mm2s { + compatible = "xlnx,axi-vdma-mm2s-channel"; + interrupts = ; + xlnx,datawidth = <64>; + }; + + dma-channel-s2mm { + compatible = "xlnx,axi-vdma-s2mm-channel"; + interrupts = ; + xlnx,datawidth = <64>; + }; + }; + + - | + #include + + dma-controller@a4030000 { + compatible = "xlnx,axi-dma-1.00.a"; + reg = <0xa4030000 0x10000>; + #dma-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + dma-ranges = <0x0 0x0 0x40000000>; + clocks = <&clk 0>, <&clk 1>, <&clk 2>, <&clk 3>; + clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk", + "m_axi_s2mm_aclk", "m_axi_sg_aclk"; + xlnx,addrwidth = <32>; + xlnx,sg-length-width = <14>; + + dma-channel-mm2s { + compatible = "xlnx,axi-dma-mm2s-channel"; + interrupts = ; + xlnx,datawidth = <64>; + xlnx,include-dre; + }; + + dma-channel-s2mm { + compatible = "xlnx,axi-dma-s2mm-channel"; + interrupts = ; + xlnx,datawidth = <64>; + xlnx,include-dre; + }; + }; diff --git a/Documentation/devicetree/bindings/embedded-controller/kontron,sl28cpld.yaml b/Documentation/devicetree/bindings/embedded-controller/kontron,sl28cpld.yaml index a77e67f6cb82..0b752f3baaa9 100644 --- a/Documentation/devicetree/bindings/embedded-controller/kontron,sl28cpld.yaml +++ b/Documentation/devicetree/bindings/embedded-controller/kontron,sl28cpld.yaml @@ -16,12 +16,7 @@ description: | properties: compatible: - oneOf: - - items: - - enum: - - kontron,sa67mcu - - const: kontron,sl28cpld - - const: kontron,sl28cpld + const: kontron,sl28cpld reg: description: diff --git a/Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml b/Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml index 072b3c0c5fd0..79f88b5f4e5c 100644 --- a/Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml +++ b/Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml @@ -42,6 +42,9 @@ properties: description: A port node to link the usb controller for the dual role switch. + connector: + $ref: /schemas/connector/usb-connector.yaml# + required: - compatible - interrupts diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml index be817fd9cc34..d06cca9273c4 100644 --- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml +++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml @@ -146,6 +146,13 @@ properties: this platform. If set, the value should be non-zero. minimum: 1 + arm,no-completion-irq: + type: boolean + description: + This optional property is intended for hardware that does not generate + completion interrupts and can be used to unconditionally enable forced + polling mode of operation. + arm,smc-id: $ref: /schemas/types.yaml#/definitions/uint32 description: @@ -379,6 +386,9 @@ then: - shmem else: + properties: + arm,no-completion-irq: false + if: properties: compatible: diff --git a/Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml b/Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml index 4a1e3e3c0505..e68f9c3ca5e2 100644 --- a/Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml +++ b/Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml @@ -37,6 +37,7 @@ properties: maxItems: 1 pmic: + deprecated: true description: Child node describing the main PMIC. type: object additionalProperties: true @@ -45,6 +46,24 @@ properties: compatible: const: samsung,s2mpg10-pmic + pmic-1: + description: Child node describing the main PMIC. + type: object + additionalProperties: true + + properties: + compatible: + const: samsung,s2mpg10-pmic + + pmic-2: + description: Child node describing the sub PMIC. + type: object + additionalProperties: true + + properties: + compatible: + const: samsung,s2mpg11-pmic + shmem: description: List of phandle pointing to the shared memory (SHM) area. The memory @@ -62,7 +81,9 @@ additionalProperties: false examples: - | + #include #include + #include power-management { compatible = "google,gs101-acpm-ipc"; @@ -70,10 +91,12 @@ examples: mboxes = <&ap2apm_mailbox>; shmem = <&apm_sram>; - pmic { + pmic-1 { compatible = "samsung,s2mpg10-pmic"; interrupts-extended = <&gpa0 6 IRQ_TYPE_LEVEL_LOW>; + vinl3m-supply = <&buck8m>; + regulators { ldo1m { regulator-name = "vdd_ldo1"; @@ -82,7 +105,13 @@ examples: regulator-always-on; }; - // ... + ldo20m { + regulator-name = "vdd_dmics"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + samsung,ext-control = ; + }; buck8m { regulator-name = "vdd_mif"; @@ -93,4 +122,21 @@ examples: }; }; }; + + pmic-2 { + compatible = "samsung,s2mpg11-pmic"; + interrupts-extended = <&gpa0 7 IRQ_TYPE_LEVEL_LOW>; + + vinl1s-supply = <&buck8m>; + vinl2s-supply = <&buck6s>; + + regulators { + buckd { + regulator-name = "vcc_ufs"; + regulator-ramp-delay = <6250>; + enable-gpios = <&gpp0 1 GPIO_ACTIVE_HIGH>; + samsung,ext-control = ; + }; + }; + }; }; diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index d66459f1d84e..7918d31f58b4 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -23,15 +23,18 @@ properties: - enum: - qcom,scm-apq8064 - qcom,scm-apq8084 + - qcom,scm-eliza - qcom,scm-glymur - qcom,scm-ipq4019 - qcom,scm-ipq5018 + - qcom,scm-ipq5210 - qcom,scm-ipq5332 - qcom,scm-ipq5424 - qcom,scm-ipq6018 - qcom,scm-ipq806x - qcom,scm-ipq8074 - qcom,scm-ipq9574 + - qcom,scm-ipq9650 - qcom,scm-kaanapali - qcom,scm-mdm9607 - qcom,scm-milos @@ -204,6 +207,7 @@ allOf: compatible: contains: enum: + - qcom,scm-eliza - qcom,scm-kaanapali - qcom,scm-milos - qcom,scm-sm8450 diff --git a/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml b/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml index 3f4bbd57fc52..fe9b14a72d69 100644 --- a/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml @@ -27,7 +27,6 @@ properties: - gateworks,pld-gpio - ibm,ppc4xx-gpio - loongson,ls1x-gpio - - maxim,max77620 - nintendo,hollywood-gpio - nxp,pca9570 - nxp,pca9571 @@ -86,7 +85,6 @@ allOf: compatible: contains: enum: - - maxim,max77620 - rockchip,rk3328-grf-gpio - ti,lp3943-gpio - ti,palmas-gpio diff --git a/Documentation/devicetree/bindings/gpu/apple,agx.yaml b/Documentation/devicetree/bindings/gpu/apple,agx.yaml index 05af942ad174..59989d8bd1cb 100644 --- a/Documentation/devicetree/bindings/gpu/apple,agx.yaml +++ b/Documentation/devicetree/bindings/gpu/apple,agx.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Apple SoC GPU maintainers: - - Sasha Finkelstein + - Sasha Finkelstein properties: compatible: diff --git a/Documentation/devicetree/bindings/i2c/amlogic,meson6-i2c.yaml b/Documentation/devicetree/bindings/i2c/amlogic,meson6-i2c.yaml index c4cc8af18280..7b59b60b62e5 100644 --- a/Documentation/devicetree/bindings/i2c/amlogic,meson6-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/amlogic,meson6-i2c.yaml @@ -16,10 +16,15 @@ allOf: properties: compatible: - enum: - - amlogic,meson6-i2c # Meson6, Meson8 and compatible SoCs - - amlogic,meson-gxbb-i2c # GXBB and compatible SoCs - - amlogic,meson-axg-i2c # AXG and compatible SoCs + oneOf: + - items: + - enum: + - amlogic,t7-i2c + - const: amlogic,meson-axg-i2c + - enum: + - amlogic,meson6-i2c # Meson6, Meson8 and compatible SoCs + - amlogic,meson-gxbb-i2c # GXBB and compatible SoCs + - amlogic,meson-axg-i2c # AXG and compatible SoCs reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/i2c/apple,i2c.yaml b/Documentation/devicetree/bindings/i2c/apple,i2c.yaml index 500a965bdb7a..9e59200ad37b 100644 --- a/Documentation/devicetree/bindings/i2c/apple,i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/apple,i2c.yaml @@ -22,7 +22,9 @@ properties: compatible: oneOf: - items: - - const: apple,t6020-i2c + - enum: + - apple,t6020-i2c + - apple,t8122-i2c - const: apple,t8103-i2c - items: - enum: diff --git a/Documentation/devicetree/bindings/i2c/cnxt,cx92755-i2c.yaml b/Documentation/devicetree/bindings/i2c/cnxt,cx92755-i2c.yaml new file mode 100644 index 000000000000..c11bbf8aa9c5 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/cnxt,cx92755-i2c.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/cnxt,cx92755-i2c.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Conexant Digicolor I2C controller + +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + +maintainers: + - Baruch Siach + +properties: + compatible: + const: cnxt,cx92755-i2c + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-frequency: + default: 100000 + +required: + - compatible + - reg + - interrupts + - clocks + +unevaluatedProperties: false + +examples: + - | + i2c@f0000120 { + compatible = "cnxt,cx92755-i2c"; + reg = <0xf0000120 0x10>; + interrupts = <28>; + clocks = <&main_clk>; + clock-frequency = <100000>; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-digicolor.txt b/Documentation/devicetree/bindings/i2c/i2c-digicolor.txt deleted file mode 100644 index 457a098d4f7e..000000000000 --- a/Documentation/devicetree/bindings/i2c/i2c-digicolor.txt +++ /dev/null @@ -1,25 +0,0 @@ -Conexant Digicolor I2C controller - -Required properties: - - compatible: must be "cnxt,cx92755-i2c" - - reg: physical address and length of the device registers - - interrupts: a single interrupt specifier - - clocks: clock for the device - - #address-cells: should be <1> - - #size-cells: should be <0> - -Optional properties: -- clock-frequency: the desired I2C bus clock frequency in Hz; in - absence of this property the default value is used (100 kHz). - -Example: - - i2c: i2c@f0000120 { - compatible = "cnxt,cx92755-i2c"; - reg = <0xf0000120 0x10>; - interrupts = <28>; - clocks = <&main_clk>; - clock-frequency = <100000>; - #address-cells = <1>; - #size-cells = <0>; - }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-iop3xx.txt b/Documentation/devicetree/bindings/i2c/i2c-iop3xx.txt deleted file mode 100644 index dcc8390e0d24..000000000000 --- a/Documentation/devicetree/bindings/i2c/i2c-iop3xx.txt +++ /dev/null @@ -1,20 +0,0 @@ -i2c Controller on XScale platforms such as IOP3xx and IXP4xx - -Required properties: -- compatible : Must be one of - "intel,iop3xx-i2c" - "intel,ixp4xx-i2c"; -- reg -- #address-cells = <1>; -- #size-cells = <0>; - -Optional properties: -- Child nodes conforming to i2c bus binding - -Example: - -i2c@c8011000 { - compatible = "intel,ixp4xx-i2c"; - reg = <0xc8011000 0x18>; - interrupts = <33 IRQ_TYPE_LEVEL_LOW>; -}; diff --git a/Documentation/devicetree/bindings/i2c/intel,ixp4xx-i2c.yaml b/Documentation/devicetree/bindings/i2c/intel,ixp4xx-i2c.yaml new file mode 100644 index 000000000000..15ef510f6fd8 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/intel,ixp4xx-i2c.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/intel,ixp4xx-i2c.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: I2c Controller on XScale platforms such as IOP3xx and IXP4xx + +maintainers: + - Andi Shyti + +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + +properties: + compatible: + enum: + - intel,iop3xx-i2c + - intel,ixp4xx-i2c + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + i2c@c8011000 { + compatible = "intel,ixp4xx-i2c"; + reg = <0xc8011000 0x18>; + interrupts = <33 IRQ_TYPE_LEVEL_LOW>; + }; diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml b/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml index 399a09409e07..7c497a358e1d 100644 --- a/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml +++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml @@ -27,6 +27,7 @@ properties: - items: - enum: - qcom,kaanapali-cci + - qcom,milos-cci - qcom,qcm2290-cci - qcom,qcs8300-cci - qcom,sa8775p-cci @@ -34,6 +35,7 @@ properties: - qcom,sc8280xp-cci - qcom,sdm670-cci - qcom,sdm845-cci + - qcom,sm6150-cci - qcom,sm6350-cci - qcom,sm8250-cci - qcom,sm8450-cci @@ -251,6 +253,7 @@ allOf: contains: enum: - qcom,sa8775p-cci + - qcom,sm6150-cci - qcom,sm8550-cci - qcom,sm8650-cci - qcom,x1e80100-cci @@ -265,6 +268,23 @@ allOf: - const: cpas_ahb - const: cci + - if: + properties: + compatible: + contains: + enum: + - qcom,milos-cci + then: + properties: + clocks: + minItems: 3 + maxItems: 3 + clock-names: + items: + - const: soc_ahb + - const: cpas_ahb + - const: cci + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml index f9a449fee2b0..5873cfdc5b3e 100644 --- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml @@ -15,6 +15,8 @@ description: assigned to either I2C controller. RTL9310 SoCs have equal capabilities but support 12 common SDA lines which can be assigned to either I2C controller. + RTL9607C SoCs have equal capabilities but each controller only supports 1 + SCL/SDA line. properties: compatible: @@ -34,6 +36,7 @@ properties: - enum: - realtek,rtl9301-i2c - realtek,rtl9310-i2c + - realtek,rtl9607-i2c reg: items: @@ -51,6 +54,9 @@ properties: The SCL line number of this I2C controller. enum: [ 0, 1 ] + clocks: + maxItems: 1 + patternProperties: '^i2c@[0-9ab]$': $ref: /schemas/i2c/i2c-controller.yaml @@ -81,6 +87,15 @@ allOf: then: patternProperties: '^i2c@[89ab]$': false + - if: + properties: + compatible: + contains: + const: realtek,rtl9607-i2c + then: + required: + - realtek,scl + - clocks required: - compatible diff --git a/Documentation/devicetree/bindings/i2c/renesas,riic.yaml b/Documentation/devicetree/bindings/i2c/renesas,riic.yaml index 6876eade431b..ae1f71eadc66 100644 --- a/Documentation/devicetree/bindings/i2c/renesas,riic.yaml +++ b/Documentation/devicetree/bindings/i2c/renesas,riic.yaml @@ -25,6 +25,7 @@ properties: - items: - enum: - renesas,riic-r9a08g045 # RZ/G3S + - renesas,riic-r9a08g046 # RZ/G3L - renesas,riic-r9a09g047 # RZ/G3E - renesas,riic-r9a09g056 # RZ/V2N - const: renesas,riic-r9a09g057 # RZ/V2H(P) diff --git a/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml b/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml index 082fdc2e69ea..467bdcbb8538 100644 --- a/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/snps,designware-i2c.yaml @@ -32,8 +32,6 @@ properties: - const: renesas,r9a06g032-i2c # RZ/N1D - const: renesas,rzn1-i2c # RZ/N1 - const: snps,designware-i2c - - description: Baikal-T1 SoC System I2C controller - const: baikal,bt1-sys-i2c - description: Mobileye EyeQ DesignWare I2C controller items: - enum: diff --git a/Documentation/devicetree/bindings/i2c/spacemit,k1-i2c.yaml b/Documentation/devicetree/bindings/i2c/spacemit,k1-i2c.yaml index 5896fb120501..8c04c675b25e 100644 --- a/Documentation/devicetree/bindings/i2c/spacemit,k1-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/spacemit,k1-i2c.yaml @@ -14,7 +14,11 @@ allOf: properties: compatible: - const: spacemit,k1-i2c + oneOf: + - items: + - const: spacemit,k3-i2c + - const: spacemit,k1-i2c + - const: spacemit,k1-i2c reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml index 0ba0df46c3a9..02e734946f44 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml @@ -4,20 +4,23 @@ $id: http://devicetree.org/schemas/iio/accel/adi,adxl372.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Analog Devices ADXL372 3-Axis, +/-(200g) Digital Accelerometer +title: Analog Devices ADXL371/ADXL372 3-Axis, +/-(200g) Digital Accelerometer maintainers: - Marcelo Schmitt - Nuno Sá + - Antoniu Miclaus description: | - Analog Devices ADXL372 3-Axis, +/-(200g) Digital Accelerometer that supports - both I2C & SPI interfaces + Analog Devices ADXL371/ADXL372 3-Axis, +/-(200g) Digital Accelerometer that + supports both I2C & SPI interfaces + https://www.analog.com/en/products/adxl371.html https://www.analog.com/en/products/adxl372.html properties: compatible: enum: + - adi,adxl371 - adi,adxl372 reg: diff --git a/Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml b/Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml index c1387e02eb82..7f9c5eec35dd 100644 --- a/Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml +++ b/Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml @@ -16,25 +16,27 @@ description: properties: compatible: - enum: - # bmc150-accel driver in Linux - - bosch,bma222 - - bosch,bma222e - - bosch,bma250e - - bosch,bma253 - - bosch,bma254 - - bosch,bma255 - - bosch,bma280 - - bosch,bmc150_accel - - bosch,bmc156_accel - - bosch,bmi055_accel + oneOf: + - enum: + - bosch,bma222 + - bosch,bma222e + - bosch,bma250e + - bosch,bma253 + - bosch,bma254 + - bosch,bma255 + - bosch,bma280 + - bosch,bmc150_accel + - bosch,bmc156_accel + - bosch,bmi055_accel - # bma180 driver in Linux - - bosch,bma023 - - bosch,bma150 - - bosch,bma180 - - bosch,bma250 - - bosch,smb380 + - bosch,bma023 + - bosch,bma150 + - bosch,bma180 + - bosch,bma250 + - bosch,smb380 + - items: + - const: bosch,bmx055-accel + - const: bosch,bmc150_accel reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml index e22d518135f2..08b1f9d75f89 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml @@ -19,6 +19,10 @@ description: | * https://www.analog.com/media/en/technical-documentation/data-sheets/ad4030-24-4032-24.pdf * https://www.analog.com/media/en/technical-documentation/data-sheets/ad4630-24_ad4632-24.pdf * https://www.analog.com/media/en/technical-documentation/data-sheets/ad4630-16-4632-16.pdf + * https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4216.pdf + * https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4224.pdf + +$ref: /schemas/spi/spi-peripheral-props.yaml# properties: compatible: @@ -29,6 +33,8 @@ properties: - adi,ad4630-24 - adi,ad4632-16 - adi,ad4632-24 + - adi,adaq4216 + - adi,adaq4224 reg: maxItems: 1 @@ -60,6 +66,14 @@ properties: description: Internal buffered Reference. Used when ref-supply is not connected. + vddh-supply: + description: + PGIA Positive Power Supply. + + vdd-fda-supply: + description: + FDA Positive Power Supply. + cnv-gpios: description: The Convert Input (CNV). It initiates the sampling conversions. @@ -70,6 +84,17 @@ properties: The Reset Input (/RST). Used for asynchronous device reset. maxItems: 1 + pga-gpios: + description: + A0 and A1 pins for gain selection. For devices that have PGA configuration + input pins, pga-gpios should be defined. + minItems: 2 + maxItems: 2 + + pwms: + description: PWM signal connected to the CNV pin. + maxItems: 1 + interrupts: description: The BUSY pin is used to signal that the conversions results are available @@ -107,6 +132,22 @@ allOf: properties: spi-rx-bus-width: maxItems: 1 + # ADAQ devices require a gain property to indicate how hardware PGA is set + - if: + properties: + compatible: + contains: + pattern: ^adi,adaq + then: + required: + - vddh-supply + - vdd-fda-supply + - pga-gpios + properties: + ref-supply: false + else: + properties: + pga-gpios: false examples: - | @@ -148,3 +189,26 @@ examples: reset-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; }; }; + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,adaq4216"; + reg = <0>; + spi-max-frequency = <80000000>; + vdd-5v-supply = <&supply_5V>; + vdd-1v8-supply = <&supply_1_8V>; + vio-supply = <&supply_1_8V>; + refin-supply = <&refin_sup>; + vddh-supply = <&vddh>; + vdd-fda-supply = <&vdd_fda>; + cnv-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; + pga-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>, + <&gpio0 3 GPIO_ACTIVE_HIGH>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml index ccd6a0ac1539..79df2696ef24 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml @@ -27,10 +27,13 @@ properties: enum: - adi,ad4080 - adi,ad4081 + - adi,ad4082 - adi,ad4083 - adi,ad4084 + - adi,ad4085 - adi,ad4086 - adi,ad4087 + - adi,ad4088 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml index b91bfb16ed6b..396e1a1aa805 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml @@ -62,6 +62,11 @@ properties: spi-cpol: true spi-cpha: true + spi-rx-bus-width: + maxItems: 4 + items: + maximum: 1 + vcc-supply: description: A 3V to 3.6V supply that powers the chip. @@ -160,6 +165,23 @@ patternProperties: unevaluatedProperties: false allOf: + # 2-channel chips only have two SDO lines + - if: + properties: + compatible: + enum: + - adi,ad7380 + - adi,ad7381 + - adi,ad7383 + - adi,ad7384 + - adi,ad7386 + - adi,ad7387 + - adi,ad7388 + then: + properties: + spi-rx-bus-width: + maxItems: 2 + # pseudo-differential chips require common mode voltage supplies, # true differential chips don't use them - if: @@ -284,6 +306,7 @@ examples: spi-cpol; spi-cpha; spi-max-frequency = <80000000>; + spi-rx-bus-width = <1>, <1>, <1>, <1>; interrupts = <27 IRQ_TYPE_EDGE_FALLING>; interrupt-parent = <&gpio0>; diff --git a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml index bb9825e7346d..70ab4e140e71 100644 --- a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.yaml @@ -27,7 +27,11 @@ properties: - amlogic,meson-gxm-saradc - amlogic,meson-axg-saradc - amlogic,meson-g12a-saradc + # Usage of this generic fallback is not allowed for new devices - const: amlogic,meson-saradc + - items: + - const: amlogic,meson-s4-saradc + - const: amlogic,meson-g12a-saradc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml index 5cc6a9684077..c884b6e03767 100644 --- a/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml +++ b/Documentation/devicetree/bindings/iio/adc/lltc,ltc2497.yaml @@ -11,6 +11,12 @@ maintainers: - Liam Beguin description: | + LTC2305: + low noise, low power, 2-channel, 12-bit successive approximation ADC with an + I2C compatible serial interface. + + https://www.analog.com/media/en/technical-documentation/data-sheets/23015fb.pdf + LTC2309: low noise, low power, 8-channel, 12-bit successive approximation ADC with an I2C compatible serial interface. @@ -28,6 +34,7 @@ description: | properties: compatible: enum: + - lltc,ltc2305 - lltc,ltc2309 - lltc,ltc2497 - lltc,ltc2499 diff --git a/Documentation/devicetree/bindings/iio/adc/motorola,cpcap-adc.yaml b/Documentation/devicetree/bindings/iio/adc/motorola,cpcap-adc.yaml index 9ceb6f18c854..1f77da7f8e06 100644 --- a/Documentation/devicetree/bindings/iio/adc/motorola,cpcap-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/motorola,cpcap-adc.yaml @@ -19,6 +19,7 @@ properties: enum: - motorola,cpcap-adc - motorola,mapphone-cpcap-adc + - motorola,mot-cpcap-adc interrupts: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-adc5-gen3.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-adc5-gen3.yaml new file mode 100644 index 000000000000..149f4af8f4b8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-adc5-gen3.yaml @@ -0,0 +1,151 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/qcom,spmi-adc5-gen3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm's SPMI PMIC ADC5 Gen3 + +maintainers: + - Jishnu Prakash + +description: | + SPMI PMIC5 Gen3 voltage ADC (ADC) provides interface to clients to read + voltage. It is a 16-bit sigma-delta ADC. It also performs the same thermal + monitoring function as the existing ADC_TM devices. + + The interface is implemented on SDAM (Shared Direct Access Memory) peripherals + on the master PMIC rather than a dedicated ADC peripheral. The number of PMIC + SDAM peripherals allocated for ADC is not correlated with the PMIC used, it is + programmed in FW (PBS) and is fixed per SOC, based on the SOC requirements. + All boards using a particular (SOC + master PMIC) combination will have the + same number of ADC SDAMs supported on that PMIC. + +properties: + compatible: + const: qcom,spmi-adc5-gen3 + + reg: + items: + - description: SDAM0 base address in the SPMI PMIC register map + - description: SDAM1 base address + minItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + "#io-channel-cells": + const: 1 + + "#thermal-sensor-cells": + const: 1 + + interrupts: + items: + - description: SDAM0 end of conversion (EOC) interrupt + - description: SDAM1 EOC interrupt + minItems: 1 + +patternProperties: + "^channel@[0-9a-f]+$": + type: object + unevaluatedProperties: false + $ref: /schemas/iio/adc/qcom,spmi-vadc-common.yaml + description: + Represents the external channels which are connected to the ADC. + + properties: + qcom,decimation: + enum: [ 85, 340, 1360 ] + default: 1360 + + qcom,hw-settle-time: + enum: [ 15, 100, 200, 300, 400, 500, 600, 700, + 1000, 2000, 4000, 8000, 16000, 32000, 64000, 128000 ] + default: 15 + + qcom,avg-samples: + enum: [ 1, 2, 4, 8, 16 ] + default: 1 + + qcom,adc-tm: + description: + ADC_TM is a threshold monitoring feature in HW which can be enabled + on any ADC channel, to trigger an IRQ for threshold violation. In + earlier ADC generations, it was implemented in a separate device + (documented in Documentation/devicetree/bindings/thermal/qcom-spmi-adc-tm5.yaml.) + In Gen3, this feature can be enabled in the same ADC device for any + channel and threshold monitoring and IRQ triggering are handled in FW + (PBS) instead of another dedicated HW block. + This property indicates ADC_TM monitoring is done on this channel. + type: boolean + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + - "#io-channel-cells" + - interrupts + +additionalProperties: false + +examples: + - | + #include + + pmic { + #address-cells = <1>; + #size-cells = <0>; + + adc@9000 { + compatible = "qcom,spmi-adc5-gen3"; + reg = <0x9000>, <0x9100>; + interrupts = <0x0 0x90 0x1 IRQ_TYPE_EDGE_RISING>, + <0x0 0x91 0x1 IRQ_TYPE_EDGE_RISING>; + #address-cells = <1>; + #size-cells = <0>; + #io-channel-cells = <1>; + #thermal-sensor-cells = <1>; + + /* PMK8550 Channel nodes */ + channel@3 { + reg = <0x3>; + label = "pmk8550_die_temp"; + qcom,pre-scaling = <1 1>; + }; + + channel@44 { + reg = <0x44>; + label = "pmk8550_xo_therm"; + qcom,pre-scaling = <1 1>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + qcom,adc-tm; + }; + + /* PM8550 Channel nodes */ + channel@103 { + reg = <0x103>; + label = "pm8550_die_temp"; + qcom,pre-scaling = <1 1>; + }; + + /* PM8550B Channel nodes */ + channel@78f { + reg = <0x78f>; + label = "pm8550b_vbat_sns_qbg"; + qcom,pre-scaling = <1 3>; + }; + + /* PM8550VS_C Channel nodes */ + channel@203 { + reg = <0x203>; + label = "pm8550vs_c_die_temp"; + qcom,pre-scaling = <1 1>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml new file mode 100644 index 000000000000..3ae252c17b91 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc-common.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/qcom,spmi-vadc-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. SPMI PMIC ADC channels + +maintainers: + - Jishnu Prakash + +description: + This defines the common properties used to define Qualcomm VADC channels. + +properties: + reg: + description: + ADC channel number (PMIC-specific for versions after PMIC5 ADC). + maxItems: 1 + + label: + description: + ADC input of the platform as seen in the schematics. + For thermistor inputs connected to generic AMUX or GPIO inputs + these can vary across platform for the same pins. Hence select + the platform schematics name for this channel. + + qcom,decimation: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + This parameter is used to decrease ADC sampling rate. + Quicker measurements can be made by reducing decimation ratio. + + qcom,pre-scaling: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + Used for scaling the channel input signal before the signal is + fed to VADC. The configuration for this node is to know the + pre-determined ratio and use it for post scaling. It is a pair of + integers, denoting the numerator and denominator of the fraction by which + input signal is multiplied. For example, <1 3> indicates the signal is scaled + down to 1/3 of its value before ADC measurement. + If property is not found default value depending on chip will be used. + oneOf: + - items: + - const: 1 + - enum: [ 1, 3, 4, 6, 20, 8, 10, 16 ] + - items: + - const: 10 + - const: 81 + + qcom,ratiometric: + type: boolean + description: | + Channel calibration type. + - For compatible property "qcom,spmi-vadc", if this property is + specified VADC will use the VDD reference (1.8V) and GND for + channel calibration. If property is not found, channel will be + calibrated with 0.625V and 1.25V reference channels, also + known as absolute calibration. + - For other compatible properties, if this property is specified + VADC will use the VDD reference (1.875V) and GND for channel + calibration. If property is not found, channel will be calibrated + with 0V and 1.25V reference channels, also known as absolute calibration. + + qcom,hw-settle-time: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Time between AMUX getting configured and the ADC starting + conversion. The 'hw_settle_time' is an index used from valid values + and programmed in hardware to achieve the hardware settling delay. + + qcom,avg-samples: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Number of samples to be used for measurement. + Averaging provides the option to obtain a single measurement + from the ADC that is an average of multiple samples. The value + selected is 2^(value). + +required: + - reg + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml index b9dc04b0d307..72188041e8b5 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml @@ -15,6 +15,8 @@ description: | voltage. The VADC is a 15-bit sigma-delta ADC. SPMI PMIC5/PMIC7 voltage ADC (ADC) provides interface to clients to read voltage. The VADC is a 16-bit sigma-delta ADC. + Note that PMIC7 ADC is the generation between PMIC5 and PMIC5 Gen3 ADC, + it can be considered like PMIC5 Gen2. properties: compatible: @@ -56,7 +58,7 @@ required: patternProperties: "^channel@[0-9a-f]+$": type: object - additionalProperties: false + unevaluatedProperties: false description: | Represents the external channels which are connected to the ADC. For compatible property "qcom,spmi-vadc" following channels, also known as @@ -64,79 +66,7 @@ patternProperties: configuration nodes should be defined: VADC_REF_625MV and/or VADC_SPARE1(based on PMIC version) VADC_REF_1250MV, VADC_GND_REF and VADC_VDD_VADC. - - properties: - reg: - maxItems: 1 - description: | - ADC channel number. - See include/dt-bindings/iio/qcom,spmi-vadc.h - For PMIC7 ADC, the channel numbers are specified separately per PMIC - in the PMIC-specific files in include/dt-bindings/iio/. - - label: - description: | - ADC input of the platform as seen in the schematics. - For thermistor inputs connected to generic AMUX or GPIO inputs - these can vary across platform for the same pins. Hence select - the platform schematics name for this channel. - - qcom,decimation: - $ref: /schemas/types.yaml#/definitions/uint32 - description: | - This parameter is used to decrease ADC sampling rate. - Quicker measurements can be made by reducing decimation ratio. - - qcom,pre-scaling: - description: | - Used for scaling the channel input signal before the signal is - fed to VADC. The configuration for this node is to know the - pre-determined ratio and use it for post scaling. It is a pair of - integers, denoting the numerator and denominator of the fraction by which - input signal is multiplied. For example, <1 3> indicates the signal is scaled - down to 1/3 of its value before ADC measurement. - If property is not found default value depending on chip will be used. - $ref: /schemas/types.yaml#/definitions/uint32-array - oneOf: - - items: - - const: 1 - - enum: [ 1, 3, 4, 6, 20, 8, 10, 16 ] - - items: - - const: 10 - - const: 81 - - qcom,ratiometric: - description: | - Channel calibration type. - - For compatible property "qcom,spmi-vadc", if this property is - specified VADC will use the VDD reference (1.8V) and GND for - channel calibration. If property is not found, channel will be - calibrated with 0.625V and 1.25V reference channels, also - known as absolute calibration. - - For compatible property "qcom,spmi-adc5", "qcom,spmi-adc7" and - "qcom,spmi-adc-rev2", if this property is specified VADC will use - the VDD reference (1.875V) and GND for channel calibration. If - property is not found, channel will be calibrated with 0V and 1.25V - reference channels, also known as absolute calibration. - type: boolean - - qcom,hw-settle-time: - $ref: /schemas/types.yaml#/definitions/uint32 - description: | - Time between AMUX getting configured and the ADC starting - conversion. The 'hw_settle_time' is an index used from valid values - and programmed in hardware to achieve the hardware settling delay. - - qcom,avg-samples: - $ref: /schemas/types.yaml#/definitions/uint32 - description: | - Number of samples to be used for measurement. - Averaging provides the option to obtain a single measurement - from the ADC that is an average of multiple samples. The value - selected is 2^(value). - - required: - - reg + $ref: /schemas/iio/adc/qcom,spmi-vadc-common.yaml allOf: - if: diff --git a/Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml b/Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml new file mode 100644 index 000000000000..065637ce33a5 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/amplifiers/adi,ad8366.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AD8366 and similar Gain Amplifiers and Digital Attenuators + +maintainers: + - Michael Hennerich + - Rodrigo Alencar + +description: + Digital Variable Gain Amplifiers (VGAs) and Digital Attenuators with + SPI interface. + +properties: + compatible: + enum: + - adi,ad8366 + - adi,ada4961 + - adi,adl5240 + - adi,adrf5702 + - adi,adrf5703 + - adi,adrf5720 + - adi,adrf5730 + - adi,adrf5731 + - adi,hmc271a + - adi,hmc792a + - adi,hmc1018a + - adi,hmc1019a + - adi,hmc1119 + + reg: + maxItems: 1 + + vcc-supply: + description: Regulator that provides power to the device. + + reset-gpios: + maxItems: 1 + + enable-gpios: + maxItems: 1 + description: Power-up or Serial Mode Enable GPIO. + +required: + - compatible + - reg + - vcc-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + not: + properties: + compatible: + contains: + const: adi,hmc271a + then: + properties: + reset-gpios: false + - if: + not: + properties: + compatible: + contains: + anyOf: + - const: adi,ad8366 + - const: adi,ada4961 + - const: adi,adrf5702 + - const: adi,adrf5703 + - const: adi,adrf5720 + - const: adi,adrf5730 + - const: adi,adrf5731 + - const: adi,hmc792a + - const: adi,hmc1018a + - const: adi,hmc1019a + - const: adi,hmc1119 + then: + properties: + enable-gpios: false + +unevaluatedProperties: false + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + amplifier@0 { + compatible = "adi,ad8366"; + reg = <0>; + spi-max-frequency = <1000000>; + vcc-supply = <&vcc_3v3>; + enable-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/dac/lltc,ltc2632.yaml b/Documentation/devicetree/bindings/iio/dac/lltc,ltc2632.yaml index 733edc7d6d17..50a9cbb44e36 100644 --- a/Documentation/devicetree/bindings/iio/dac/lltc,ltc2632.yaml +++ b/Documentation/devicetree/bindings/iio/dac/lltc,ltc2632.yaml @@ -4,36 +4,49 @@ $id: http://devicetree.org/schemas/iio/dac/lltc,ltc2632.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Linear Technology LTC263x 12-/10-/8-Bit Rail-to-Rail DAC +title: Linear Technology LTC263x and LTC2654 Rail-to-Rail DAC maintainers: - Michael Hennerich description: | - Bindings for the Linear Technology LTC2632/2634/2636 DAC - Datasheet can be found here: https://www.analog.com/media/en/technical-documentation/data-sheets/LTC263[246].pdf + Bindings for the Linear Technology LTC2632/2634/2636/2654 DAC + Datasheet can be found here: + https://www.analog.com/media/en/technical-documentation/data-sheets/LTC263[246].pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/2654f.pdf properties: compatible: - enum: - - lltc,ltc2632-l12 - - lltc,ltc2632-l10 - - lltc,ltc2632-l8 - - lltc,ltc2632-h12 - - lltc,ltc2632-h10 - - lltc,ltc2632-h8 - - lltc,ltc2634-l12 - - lltc,ltc2634-l10 - - lltc,ltc2634-l8 - - lltc,ltc2634-h12 - - lltc,ltc2634-h10 - - lltc,ltc2634-h8 - - lltc,ltc2636-l12 - - lltc,ltc2636-l10 - - lltc,ltc2636-l8 - - lltc,ltc2636-h12 - - lltc,ltc2636-h10 - - lltc,ltc2636-h8 + oneOf: + - enum: + - lltc,ltc2632-l12 + - lltc,ltc2632-l10 + - lltc,ltc2632-l8 + - lltc,ltc2632-h12 + - lltc,ltc2632-h10 + - lltc,ltc2632-h8 + - lltc,ltc2634-l12 + - lltc,ltc2634-l10 + - lltc,ltc2634-l8 + - lltc,ltc2634-h12 + - lltc,ltc2634-h10 + - lltc,ltc2634-h8 + - lltc,ltc2636-l12 + - lltc,ltc2636-l10 + - lltc,ltc2636-l8 + - lltc,ltc2636-h12 + - lltc,ltc2636-h10 + - lltc,ltc2636-h8 + - lltc,ltc2654-l16 + - lltc,ltc2654-h16 + - items: + - enum: + - lltc,ltc2654-l12 + - const: lltc,ltc2634-l12 + - items: + - enum: + - lltc,ltc2654-h12 + - const: lltc,ltc2634-h12 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/dac/maxim,ds4424.yaml b/Documentation/devicetree/bindings/iio/dac/maxim,ds4424.yaml index 264fa7c5fe3a..4323df2036ac 100644 --- a/Documentation/devicetree/bindings/iio/dac/maxim,ds4424.yaml +++ b/Documentation/devicetree/bindings/iio/dac/maxim,ds4424.yaml @@ -4,18 +4,21 @@ $id: http://devicetree.org/schemas/iio/dac/maxim,ds4424.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Maxim Integrated DS4422/DS4424 7-bit Sink/Source Current DAC +title: Maxim Integrated DS4402/DS4404 and DS4422/DS4424 Current DACs maintainers: - Ismail Kose description: | - Datasheet publicly available at: + Datasheets publicly available at: + https://datasheets.maximintegrated.com/en/ds/DS4402-DS4404.pdf https://datasheets.maximintegrated.com/en/ds/DS4422-DS4424.pdf properties: compatible: enum: + - maxim,ds4402 + - maxim,ds4404 - maxim,ds4422 - maxim,ds4424 @@ -24,9 +27,43 @@ properties: vcc-supply: true + maxim,rfs-ohms: + description: | + Array of resistance values in Ohms for the external Rfs resistors + connected to the FS pins. These values determine the full-scale + output current. The actual resistance depends on the chip variant + and specific hardware design requirements. + minItems: 2 + maxItems: 4 + required: - compatible - reg + - maxim,rfs-ohms + +allOf: + - if: + properties: + compatible: + contains: + enum: + - maxim,ds4402 + - maxim,ds4422 + then: + properties: + maxim,rfs-ohms: + maxItems: 2 + - if: + properties: + compatible: + contains: + enum: + - maxim,ds4404 + - maxim,ds4424 + then: + properties: + maxim,rfs-ohms: + minItems: 4 additionalProperties: false @@ -40,6 +77,7 @@ examples: compatible = "maxim,ds4424"; reg = <0x10>; /* When A0, A1 pins are ground */ vcc-supply = <&vcc_3v3>; + maxim,rfs-ohms = <40000>, <40000>, <40000>, <40000>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml b/Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml index 20dd1370660d..624c640be4c8 100644 --- a/Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml +++ b/Documentation/devicetree/bindings/iio/dac/ti,dac7612.yaml @@ -9,7 +9,7 @@ title: Texas Instruments DAC7612 family of DACs description: The DAC7612 is a dual, 12-bit digital-to-analog converter (DAC) with guaranteed 12-bit monotonicity performance over the industrial temperature - range. Is is programmable through an SPI interface. + range. It is programmable through an SPI interface. maintainers: - Ricardo Ribalda Delgado diff --git a/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml b/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml index 3c6fe74af0b8..fcbd4b430e48 100644 --- a/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml +++ b/Documentation/devicetree/bindings/iio/gyroscope/bosch,bmg160.yaml @@ -11,10 +11,14 @@ maintainers: properties: compatible: - enum: - - bosch,bmg160 - - bosch,bmi055_gyro - - bosch,bmi088_gyro + oneOf: + - enum: + - bosch,bmg160 + - bosch,bmi055_gyro + - bosch,bmi088_gyro + - items: + - const: bosch,bmx055-gyro + - const: bosch,bmg160 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/light/vishay,vcnl4000.yaml b/Documentation/devicetree/bindings/iio/light/vishay,vcnl4000.yaml index 4d1a225e8868..516afef7a545 100644 --- a/Documentation/devicetree/bindings/iio/light/vishay,vcnl4000.yaml +++ b/Documentation/devicetree/bindings/iio/light/vishay,vcnl4000.yaml @@ -18,16 +18,32 @@ allOf: properties: compatible: - enum: - - vishay,vcnl4000 - - vishay,vcnl4010 - - vishay,vcnl4020 - - vishay,vcnl4040 - - vishay,vcnl4200 + oneOf: + - enum: + - capella,cm36672p + - vishay,vcnl4000 + - vishay,vcnl4010 + - vishay,vcnl4020 + - vishay,vcnl4040 + - vishay,vcnl4200 + - items: + - const: capella,cm36686 + - const: vishay,vcnl4040 interrupts: maxItems: 1 + vdd-supply: + description: Regulator providing power to the "VDD" pin. + + vio-supply: + description: Regulator providing power for pull-up of the I/O lines. + Does not connect to the sensor directly, but is needed for the + correct operation of the I2C and interrupt lines. + + vled-supply: + description: Regulator providing power to the IR anode pin. + reg: maxItems: 1 @@ -49,6 +65,9 @@ examples: compatible = "vishay,vcnl4200"; reg = <0x51>; proximity-near-level = <220>; + vdd-supply = <®_vdd>; + vio-supply = <®_vio>; + vled-supply = <®_vled>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/magnetometer/bosch,bmc150_magn.yaml b/Documentation/devicetree/bindings/iio/magnetometer/bosch,bmc150_magn.yaml index a3838ab0c524..c1a6892b0194 100644 --- a/Documentation/devicetree/bindings/iio/magnetometer/bosch,bmc150_magn.yaml +++ b/Documentation/devicetree/bindings/iio/magnetometer/bosch,bmc150_magn.yaml @@ -21,11 +21,15 @@ properties: description: Note the bmm150_magn is a deprecated compatible as this part contains only a magnetometer. - enum: - - bosch,bmc150_magn - - bosch,bmc156_magn - - bosch,bmm150 - - bosch,bmm150_magn + oneOf: + - enum: + - bosch,bmc150_magn + - bosch,bmc156_magn + - bosch,bmm150 + - bosch,bmm150_magn + - items: + - const: bosch,bmx055-magn + - const: bosch,bmc150_magn reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml b/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml index 322befc41de6..f7f8be1e379d 100644 --- a/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml @@ -4,14 +4,17 @@ $id: http://devicetree.org/schemas/iio/proximity/st,vl53l0x.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: ST VL53L0X ToF ranging sensor +title: ST VL53L0X/VL53L1X ToF ranging sensor maintainers: - Song Qiang + - Siratul Islam properties: compatible: - const: st,vl53l0x + enum: + - st,vl53l0x + - st,vl53l1x reg: maxItems: 1 @@ -21,6 +24,8 @@ properties: reset-gpios: maxItems: 1 + description: + Phandle to the XSHUT GPIO. Used for hardware reset. vdd-supply: true @@ -28,6 +33,16 @@ required: - compatible - reg +allOf: + - if: + properties: + compatible: + contains: + const: st,vl53l1x + then: + required: + - vdd-supply + additionalProperties: false examples: @@ -38,8 +53,9 @@ examples: #size-cells = <0>; proximity@29 { - compatible = "st,vl53l0x"; + compatible = "st,vl53l1x"; reg = <0x29>; + vdd-supply = <®_3v3>; interrupt-parent = <&gpio>; interrupts = <23 IRQ_TYPE_EDGE_FALLING>; }; diff --git a/Documentation/devicetree/bindings/iio/proximity/tyhx,hx9023s.yaml b/Documentation/devicetree/bindings/iio/proximity/tyhx,hx9023s.yaml index 64ce8bc8bd36..cc5b5284c267 100644 --- a/Documentation/devicetree/bindings/iio/proximity/tyhx,hx9023s.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/tyhx,hx9023s.yaml @@ -28,6 +28,9 @@ properties: vdd-supply: true + firmware-name: + maxItems: 1 + "#address-cells": const: 1 @@ -65,6 +68,7 @@ examples: interrupt-parent = <&pio>; interrupts = <16 IRQ_TYPE_EDGE_FALLING>; vdd-supply = <&pp1800_prox>; + firmware-name = "hx9023s.bin"; #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/input/awinic,aw86927.yaml b/Documentation/devicetree/bindings/input/awinic,aw86927.yaml index b7252916bd72..bd74b81488f6 100644 --- a/Documentation/devicetree/bindings/input/awinic,aw86927.yaml +++ b/Documentation/devicetree/bindings/input/awinic,aw86927.yaml @@ -11,7 +11,12 @@ maintainers: properties: compatible: - const: awinic,aw86927 + oneOf: + - const: awinic,aw86927 + - items: + - enum: + - awinic,aw86938 + - const: awinic,aw86927 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/input/cirrus,ep9307-keypad.yaml b/Documentation/devicetree/bindings/input/cirrus,ep9307-keypad.yaml index a0d2460c55ab..25b8b29c87d7 100644 --- a/Documentation/devicetree/bindings/input/cirrus,ep9307-keypad.yaml +++ b/Documentation/devicetree/bindings/input/cirrus,ep9307-keypad.yaml @@ -10,6 +10,7 @@ maintainers: - Alexander Sverdlin allOf: + - $ref: input.yaml# - $ref: /schemas/input/matrix-keymap.yaml# description: @@ -37,10 +38,8 @@ properties: clocks: maxItems: 1 - debounce-delay-ms: - description: | - Time in microseconds that key must be pressed or - released for state change interrupt to trigger. + # Time for state change interrupt to trigger + debounce-delay-ms: true cirrus,prescale: description: row/column counter pre-scaler load value diff --git a/Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.yaml b/Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.yaml new file mode 100644 index 000000000000..c085de6dab85 --- /dev/null +++ b/Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.yaml @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/input/gpio-charlieplex-keypad.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GPIO charlieplex keypad + +maintainers: + - Hugo Villeneuve + +description: | + The charlieplex keypad supports N^2)-N different key combinations (where N is + the number of I/O lines). Key presses and releases are detected by configuring + only one line as output at a time, and reading other line states. This process + is repeated for each line. Diodes are required to ensure current flows in only + one direction between any pair of pins, as well as pull-up or pull-down + resistors on all I/O lines. + This mechanism doesn't allow to detect simultaneous key presses. + + Wiring example for 3 lines keyboard with 6 switches and 3 diodes (pull-up/down + resistors not shown but needed on L0, L1 and L2): + + L0 --+---------------------+----------------------+ + | | | + L1 -------+-----------+---------------------+ | + | | | | | | + L2 -------------+----------------+-----+ | | + | | | | | | | | | + | | | | | | | | | + | S1 \ S2 \ | S3 \ S4 \ | S5 \ S6 \ + | | | | | | | | | + | +--+--+ | +--+--+ | +--+--+ + | | | | | | + | D1 v | D2 v | D3 v + | - (k) | - (k) | - (k) + | | | | | | + +-------+ +-------+ +-------+ + + L: GPIO line + S: switch + D: diode (k indicates cathode) + +allOf: + - $ref: input.yaml# + - $ref: /schemas/input/matrix-keymap.yaml# + +properties: + compatible: + const: gpio-charlieplex-keypad + + autorepeat: true + + debounce-delay-ms: + default: 5 + + line-gpios: + description: + List of GPIOs used as lines. The gpio specifier for this property + depends on the gpio controller to which these lines are connected. + + linux,keymap: true + + poll-interval: true + + settling-time-us: true + + wakeup-source: true + +required: + - compatible + - line-gpios + - linux,keymap + - poll-interval + +additionalProperties: false + +examples: + - | + #include + #include + + keyboard { + compatible = "gpio-charlieplex-keypad"; + debounce-delay-ms = <20>; + poll-interval = <5>; + settling-time-us = <2>; + + line-gpios = <&gpio2 25 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN) + &gpio2 26 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN) + &gpio2 27 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; + + /* MATRIX_KEY(output, input, key-code) */ + linux,keymap = < + /* + * According to wiring diagram above, if L1 is configured as + * output and HIGH, and we detect a HIGH level on input L0, + * then it means S1 is pressed: MATRIX_KEY(L1, L0, KEY...) + */ + MATRIX_KEY(1, 0, KEY_F1) /* S1 */ + MATRIX_KEY(2, 0, KEY_F2) /* S2 */ + MATRIX_KEY(0, 1, KEY_F3) /* S3 */ + MATRIX_KEY(2, 1, KEY_F4) /* S4 */ + MATRIX_KEY(1, 2, KEY_F5) /* S5 */ + MATRIX_KEY(0, 2, KEY_F6) /* S6 */ + >; + }; diff --git a/Documentation/devicetree/bindings/input/gpio-matrix-keypad.yaml b/Documentation/devicetree/bindings/input/gpio-matrix-keypad.yaml index ebfff9e42a36..69df24a5ae70 100644 --- a/Documentation/devicetree/bindings/input/gpio-matrix-keypad.yaml +++ b/Documentation/devicetree/bindings/input/gpio-matrix-keypad.yaml @@ -18,6 +18,7 @@ description: report the event using GPIO interrupts to the cpu. allOf: + - $ref: input.yaml# - $ref: /schemas/input/matrix-keymap.yaml# properties: @@ -46,9 +47,7 @@ properties: Force GPIO polarity to active low. In the absence of this property GPIOs are treated as active high. - debounce-delay-ms: - description: Debounce interval in milliseconds. - default: 0 + debounce-delay-ms: true col-scan-delay-us: description: diff --git a/Documentation/devicetree/bindings/input/input.yaml b/Documentation/devicetree/bindings/input/input.yaml index 94f7942189e8..64d1c46cb2f2 100644 --- a/Documentation/devicetree/bindings/input/input.yaml +++ b/Documentation/devicetree/bindings/input/input.yaml @@ -14,6 +14,14 @@ properties: description: Enable autorepeat when key is pressed and held down. type: boolean + debounce-delay-ms: + description: + Debounce delay in milliseconds. This is the time during which the key + press or release signal must remain stable before it is considered valid. + minimum: 0 + maximum: 999 + default: 0 + linux,keycodes: description: Specifies an array of numeric keycode values to be used for reporting @@ -58,6 +66,14 @@ properties: reset automatically. Device with key pressed reset feature can specify this property. + settling-time-us: + description: + Delay, in microseconds, when activating an output line/col/row before + we can reliably read other input lines that maybe affected by this + output. This can be the case for an output with a RC circuit that affects + ramp-up/down times. + default: 0 + dependencies: linux,input-type: [ "linux,code" ] diff --git a/Documentation/devicetree/bindings/input/matrix-keymap.yaml b/Documentation/devicetree/bindings/input/matrix-keymap.yaml index a715c2a773fe..ce910e4ac823 100644 --- a/Documentation/devicetree/bindings/input/matrix-keymap.yaml +++ b/Documentation/devicetree/bindings/input/matrix-keymap.yaml @@ -4,13 +4,13 @@ $id: http://devicetree.org/schemas/input/matrix-keymap.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Common Key Matrices on Matrix-connected Key Boards +title: Common Key Matrices on Matrix-connected Keyboards maintainers: - Olof Johansson description: | - A simple common binding for matrix-connected key boards. Currently targeted at + A simple common binding for matrix-connected keyboards. Currently targeted at defining the keys in the scope of linux key codes since that is a stable and standardized interface at this time. diff --git a/Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml b/Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml index e365413732e7..914dd3283df3 100644 --- a/Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml +++ b/Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml @@ -10,6 +10,7 @@ maintainers: - Mattijs Korpershoek allOf: + - $ref: input.yaml# - $ref: /schemas/input/matrix-keymap.yaml# description: | diff --git a/Documentation/devicetree/bindings/input/parade,tc3408.yaml b/Documentation/devicetree/bindings/input/parade,tc3408.yaml new file mode 100644 index 000000000000..30ffefb96c68 --- /dev/null +++ b/Documentation/devicetree/bindings/input/parade,tc3408.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/parade,tc3408.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Parade TC3408 touchscreen controller + +maintainers: + - Langyan Ye + +description: | + Parade TC3408 is a touchscreen controller supporting the I2C-HID protocol. + It requires a reset GPIO and two power supplies (3.3V and 1.8V). + +allOf: + - $ref: /schemas/input/touchscreen/touchscreen.yaml# + +properties: + compatible: + const: parade,tc3408 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + vcc33-supply: + description: The 3.3V supply to the touchscreen. + + vccio-supply: + description: The 1.8V supply to the touchscreen. + +required: + - compatible + - reg + - interrupts + - reset-gpios + - vcc33-supply + - vccio-supply + +unevaluatedProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen: touchscreen@24 { + compatible = "parade,tc3408"; + reg = <0x24>; + + interrupt-parent = <&pio>; + interrupts = <15 IRQ_TYPE_LEVEL_LOW>; + + reset-gpios = <&pio 126 GPIO_ACTIVE_LOW>; + vcc33-supply = <&pp3300_tchscr_x>; + vccio-supply = <&pp1800_tchscr_report_disable>; + }; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/apple,z2-multitouch.yaml b/Documentation/devicetree/bindings/input/touchscreen/apple,z2-multitouch.yaml index 402ca6bffd34..44158e89e818 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/apple,z2-multitouch.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/apple,z2-multitouch.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Apple touchscreens attached using the Z2 protocol maintainers: - - Sasha Finkelstein + - Sasha Finkelstein description: A series of touschscreen controllers used in Apple products diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml index 6f90522de8c0..68b2f1601654 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml @@ -33,19 +33,23 @@ allOf: properties: compatible: - enum: - - edt,edt-ft5206 - - edt,edt-ft5306 - - edt,edt-ft5406 - - edt,edt-ft5506 - - evervision,ev-ft5726 - - focaltech,ft3518 - - focaltech,ft5426 - - focaltech,ft5452 - - focaltech,ft6236 - - focaltech,ft8201 - - focaltech,ft8716 - - focaltech,ft8719 + oneOf: + - enum: + - edt,edt-ft5206 + - edt,edt-ft5306 + - edt,edt-ft5406 + - edt,edt-ft5506 + - evervision,ev-ft5726 + - focaltech,ft3518 + - focaltech,ft5426 + - focaltech,ft5452 + - focaltech,ft6236 + - focaltech,ft8201 + - focaltech,ft8716 + - focaltech,ft8719 + - items: + - const: focaltech,ft3519 + - const: focaltech,ft3518 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl,imx25-tcq.yaml b/Documentation/devicetree/bindings/input/touchscreen/fsl,imx25-tcq.yaml new file mode 100644 index 000000000000..94452ac423d0 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/fsl,imx25-tcq.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/fsl,imx25-tcq.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale mx25 TS conversion queue module + +maintainers: + - Frank Li + +description: + mx25 touchscreen conversion queue module which controls the ADC unit of the + mx25 for attached touchscreens. + +properties: + compatible: + const: fsl,imx25-tcq + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + fsl,wires: + description: touch wires number. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [4, 5] + + fsl,pen-debounce-ns: + description: + Pen debounce time in nanoseconds. + + fsl,pen-threshold: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Pen-down threshold for the touchscreen. This is a value + between 1 and 4096. It is the ratio between the internal reference voltage + and the measured voltage after the plate was precharged. Resistance between + plates and therefore the voltage decreases with pressure so that a smaller + value is equivalent to a higher pressure. + + fsl,settling-time-ns: + description: + Settling time in nanoseconds. The settling time is before + the actual touch detection to wait for an even charge distribution in the + plate. + +allOf: + - $ref: touchscreen.yaml + +required: + - compatible + - reg + - interrupts + - fsl,wires + +unevaluatedProperties: false + +examples: + - | + touchscreen@50030400 { + compatible = "fsl,imx25-tcq"; + reg = <0x50030400 0x60>; + interrupt-parent = <&tscadc>; + interrupts = <0>; + fsl,wires = <4>; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt b/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt deleted file mode 100644 index 99d6f9d25335..000000000000 --- a/Documentation/devicetree/bindings/input/touchscreen/fsl-mx25-tcq.txt +++ /dev/null @@ -1,34 +0,0 @@ -Freescale mx25 TS conversion queue module - -mx25 touchscreen conversion queue module which controls the ADC unit of the -mx25 for attached touchscreens. - -Required properties: - - compatible: Should be "fsl,imx25-tcq". - - reg: Memory range of the device. - - interrupts: Should be the interrupt number associated with this module within - the tscadc unit (<0>). - - fsl,wires: Should be '<4>' or '<5>' - -Optional properties: - - fsl,pen-debounce-ns: Pen debounce time in nanoseconds. - - fsl,pen-threshold: Pen-down threshold for the touchscreen. This is a value - between 1 and 4096. It is the ratio between the internal reference voltage - and the measured voltage after the plate was precharged. Resistance between - plates and therefore the voltage decreases with pressure so that a smaller - value is equivalent to a higher pressure. - - fsl,settling-time-ns: Settling time in nanoseconds. The settling time is before - the actual touch detection to wait for an even charge distribution in the - plate. - -This device includes two conversion queues which can be added as subnodes. -The first queue is for the touchscreen, the second for general purpose ADC. - -Example: - tsc: tcq@50030400 { - compatible = "fsl,imx25-tcq"; - reg = <0x50030400 0x60>; - interrupt-parent = <&tscadc>; - interrupts = <0>; - fsl,wires = <4>; - }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml b/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml index 978afaa4fcef..fe1fa217d842 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/sitronix,st1232.yaml @@ -32,6 +32,9 @@ properties: description: A phandle to the reset GPIO maxItems: 1 + wakeup-source: + type: boolean + required: - compatible - reg @@ -51,6 +54,7 @@ examples: reg = <0x55>; interrupts = <2 0>; gpios = <&gpio1 166 0>; + wakeup-source; touch-overlay { segment-0 { diff --git a/Documentation/devicetree/bindings/input/touchscreen/technologic,ts4800-ts.yaml b/Documentation/devicetree/bindings/input/touchscreen/technologic,ts4800-ts.yaml new file mode 100644 index 000000000000..c033774b4f44 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/technologic,ts4800-ts.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/technologic,ts4800-ts.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TS-4800 Touchscreen + +maintainers: + - Eduard Bostina + +properties: + compatible: + const: technologic,ts4800-ts + + reg: + maxItems: 1 + + syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: Phandle to the FPGA's syscon + - description: Offset to the touchscreen register + - description: Offset to the touchscreen enable bit + description: Phandle / integers array that points to the syscon node which + describes the FPGA's syscon registers. + +required: + - compatible + - reg + - syscon + +additionalProperties: false + +examples: + - | + touchscreen@1000 { + compatible = "technologic,ts4800-ts"; + reg = <0x1000 0x100>; + syscon = <&fpga_syscon 0x20 3>; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/trivial-touch.yaml b/Documentation/devicetree/bindings/input/touchscreen/trivial-touch.yaml index 6441d21223ca..6316a8d32f39 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/trivial-touch.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/trivial-touch.yaml @@ -53,14 +53,14 @@ properties: wakeup-source: true -allOf: - - $ref: touchscreen.yaml - required: - compatible - reg - interrupts +allOf: + - $ref: touchscreen.yaml + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt deleted file mode 100644 index 4c1c092c276b..000000000000 --- a/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt +++ /dev/null @@ -1,11 +0,0 @@ -* TS-4800 Touchscreen bindings - -Required properties: -- compatible: must be "technologic,ts4800-ts" -- reg: physical base address of the controller and length of memory mapped - region. -- syscon: phandle / integers array that points to the syscon node which - describes the FPGA's syscon registers. - - phandle to FPGA's syscon - - offset to the touchscreen register - - offset to the touchscreen enable bit diff --git a/Documentation/devicetree/bindings/interconnect/qcom,eliza-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,eliza-rpmh.yaml new file mode 100644 index 000000000000..9a926a97e7bf --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,eliza-rpmh.yaml @@ -0,0 +1,142 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,eliza-rpmh.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMh Network-On-Chip Interconnect on Eliza SoC + +maintainers: + - Odelu Kukatla + +description: | + RPMh interconnect providers support system bandwidth requirements through + RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is + able to communicate with the BCM through the Resource State Coordinator (RSC) + associated with each execution environment. Provider nodes must point to at + least one RPMh device child node pertaining to their RSC and each provider + can map to multiple RPMh resources. + + See also: include/dt-bindings/interconnect/qcom,eliza-rpmh.h + +properties: + compatible: + enum: + - qcom,eliza-aggre1-noc + - qcom,eliza-aggre2-noc + - qcom,eliza-clk-virt + - qcom,eliza-cnoc-cfg + - qcom,eliza-cnoc-main + - qcom,eliza-gem-noc + - qcom,eliza-lpass-ag-noc + - qcom,eliza-lpass-lpiaon-noc + - qcom,eliza-lpass-lpicx-noc + - qcom,eliza-mc-virt + - qcom,eliza-mmss-noc + - qcom,eliza-nsp-noc + - qcom,eliza-pcie-anoc + - qcom,eliza-system-noc + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + +required: + - compatible + +allOf: + - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,eliza-clk-virt + - qcom,eliza-mc-virt + then: + properties: + reg: false + else: + required: + - reg + + - if: + properties: + compatible: + contains: + enum: + - qcom,eliza-aggre1-noc + then: + properties: + clocks: + items: + - description: aggre UFS PHY AXI clock + - description: aggre USB3 PRIM AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,eliza-aggre2-noc + then: + properties: + clocks: + items: + - description: RPMH CC IPA clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,eliza-pcie-anoc + then: + properties: + clocks: + items: + - description: aggre-NOC PCIe AXI clock + - description: cfg-NOC PCIe a-NOC AHB clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,eliza-aggre1-noc + - qcom,eliza-aggre2-noc + - qcom,eliza-pcie-anoc + then: + required: + - clocks + else: + properties: + clocks: false + +unevaluatedProperties: false + +examples: + - | + gem_noc: interconnect@24100000 { + compatible = "qcom,eliza-gem-noc"; + reg = <0x24100000 0x163080>; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + mc_virt: interconnect-2 { + compatible = "qcom,eliza-mc-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + aggre1_noc: interconnect@16e0000 { + compatible = "qcom,eliza-aggre1-noc"; + reg = <0x16e0000 0x16400>; + #interconnect-cells = <2>; + clocks = <&gcc_phy_axi_clk>, <&gcc_prim_axi_clk>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; diff --git a/Documentation/devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml index d55a7bcf5591..f69b2facb658 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,glymur-rpmh.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/interconnect/qcom,glymur-rpmh.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Qualcomm RPMh Network-On-Chip Interconnect on GLYMUR +title: Qualcomm RPMh Network-On-Chip Interconnect on Glymur and Mahua SoCs maintainers: - Raviteja Laggyshetty @@ -21,28 +21,98 @@ description: | properties: compatible: - enum: - - qcom,glymur-aggre1-noc - - qcom,glymur-aggre2-noc - - qcom,glymur-aggre3-noc - - qcom,glymur-aggre4-noc - - qcom,glymur-clk-virt - - qcom,glymur-cnoc-cfg - - qcom,glymur-cnoc-main - - qcom,glymur-hscnoc - - qcom,glymur-lpass-ag-noc - - qcom,glymur-lpass-lpiaon-noc - - qcom,glymur-lpass-lpicx-noc - - qcom,glymur-mc-virt - - qcom,glymur-mmss-noc - - qcom,glymur-nsinoc - - qcom,glymur-nsp-noc - - qcom,glymur-oobm-ss-noc - - qcom,glymur-pcie-east-anoc - - qcom,glymur-pcie-east-slv-noc - - qcom,glymur-pcie-west-anoc - - qcom,glymur-pcie-west-slv-noc - - qcom,glymur-system-noc + oneOf: + - items: + - enum: + - qcom,mahua-aggre1-noc + - const: qcom,glymur-aggre1-noc + - items: + - enum: + - qcom,mahua-aggre2-noc + - const: qcom,glymur-aggre2-noc + - items: + - enum: + - qcom,mahua-aggre3-noc + - const: qcom,glymur-aggre3-noc + - items: + - enum: + - qcom,mahua-aggre4-noc + - const: qcom,glymur-aggre4-noc + - items: + - enum: + - qcom,mahua-clk-virt + - const: qcom,glymur-clk-virt + - items: + - enum: + - qcom,mahua-cnoc-main + - const: qcom,glymur-cnoc-main + - items: + - enum: + - qcom,mahua-lpass-ag-noc + - const: qcom,glymur-lpass-ag-noc + - items: + - enum: + - qcom,mahua-lpass-lpiaon-noc + - const: qcom,glymur-lpass-lpiaon-noc + - items: + - enum: + - qcom,mahua-lpass-lpicx-noc + - const: qcom,glymur-lpass-lpicx-noc + - items: + - enum: + - qcom,mahua-mmss-noc + - const: qcom,glymur-mmss-noc + - items: + - enum: + - qcom,mahua-nsinoc + - const: qcom,glymur-nsinoc + - items: + - enum: + - qcom,mahua-nsp-noc + - const: qcom,glymur-nsp-noc + - items: + - enum: + - qcom,mahua-oobm-ss-noc + - const: qcom,glymur-oobm-ss-noc + - items: + - enum: + - qcom,mahua-pcie-east-anoc + - const: qcom,glymur-pcie-east-anoc + - items: + - enum: + - qcom,mahua-pcie-east-slv-noc + - const: qcom,glymur-pcie-east-slv-noc + - items: + - enum: + - qcom,mahua-system-noc + - const: qcom,glymur-system-noc + - enum: + - qcom,glymur-aggre1-noc + - qcom,glymur-aggre2-noc + - qcom,glymur-aggre3-noc + - qcom,glymur-aggre4-noc + - qcom,glymur-clk-virt + - qcom,glymur-cnoc-cfg + - qcom,glymur-cnoc-main + - qcom,glymur-hscnoc + - qcom,glymur-lpass-ag-noc + - qcom,glymur-lpass-lpiaon-noc + - qcom,glymur-lpass-lpicx-noc + - qcom,glymur-mc-virt + - qcom,glymur-mmss-noc + - qcom,glymur-nsinoc + - qcom,glymur-nsp-noc + - qcom,glymur-oobm-ss-noc + - qcom,glymur-pcie-east-anoc + - qcom,glymur-pcie-east-slv-noc + - qcom,glymur-pcie-west-anoc + - qcom,glymur-pcie-west-slv-noc + - qcom,glymur-system-noc + - qcom,mahua-cnoc-cfg + - qcom,mahua-hscnoc + - qcom,mahua-mc-virt + - qcom,mahua-pcie-west-anoc + - qcom,mahua-pcie-west-slv-noc reg: maxItems: 1 @@ -63,6 +133,7 @@ allOf: enum: - qcom,glymur-clk-virt - qcom,glymur-mc-virt + - qcom,mahua-mc-virt then: properties: reg: false @@ -85,6 +156,20 @@ allOf: - description: aggre PCIE_4 WEST AXI clock - description: aggre PCIE_6 WEST AXI clock + - if: + properties: + compatible: + contains: + enum: + - qcom,mahua-pcie-west-anoc + then: + properties: + clocks: + items: + - description: aggre PCIE_3B WEST AXI clock + - description: aggre PCIE_4 WEST AXI clock + - description: aggre PCIE_6 WEST AXI clock + - if: properties: compatible: @@ -131,10 +216,11 @@ allOf: compatible: contains: enum: - - qcom,glymur-pcie-west-anoc - - qcom,glymur-pcie-east-anoc - qcom,glymur-aggre2-noc - qcom,glymur-aggre4-noc + - qcom,glymur-pcie-east-anoc + - qcom,glymur-pcie-west-anoc + - qcom,mahua-pcie-west-anoc then: required: - clocks diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml index 95ce25ce1f7d..b35f6dd11c71 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml @@ -26,27 +26,34 @@ properties: - qcom,msm8974-pnoc - qcom,msm8974-snoc - '#interconnect-cells': - const: 1 - clock-names: items: - const: bus - - const: bus_a clocks: items: - description: Bus Clock - - description: Bus A Clock required: - compatible - reg - - '#interconnect-cells' - - clock-names - - clocks -additionalProperties: false +unevaluatedProperties: false + +allOf: + - $ref: qcom,rpm-common.yaml# + - if: + properties: + compatible: + const: qcom,msm8974-mmssnoc + then: + required: + - clocks + - clock-names + else: + properties: + clocks: false + clock-names: false examples: - | @@ -56,7 +63,4 @@ examples: reg = <0xfc380000 0x6a000>; compatible = "qcom,msm8974-bimc"; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_BIMC_CLK>, - <&rpmcc RPM_SMD_BIMC_A_CLK>; }; diff --git a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml index 4b9b98fbe8f2..41b9f758bf8b 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml @@ -28,12 +28,14 @@ properties: - const: qcom,osm-l3 - items: - enum: + - qcom,eliza-epss-l3 - qcom,sa8775p-epss-l3 - qcom,sc7280-epss-l3 - qcom,sc8280xp-epss-l3 - qcom,sm6375-cpucp-l3 - qcom,sm8250-epss-l3 - qcom,sm8350-epss-l3 + - qcom,sm8550-epss-l3 - qcom,sm8650-epss-l3 - const: qcom,epss-l3 - items: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,qcs615-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,qcs615-rpmh.yaml index e06404828824..a9cd49bbe247 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,qcs615-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,qcs615-rpmh.yaml @@ -34,6 +34,13 @@ properties: reg: maxItems: 1 + clocks: + items: + - description: aggre UFS PHY AXI clock + - description: aggre USB2 SEC AXI clock + - description: aggre USB3 PRIM AXI clock + - description: RPMH CC IPA clock + required: - compatible @@ -53,6 +60,22 @@ allOf: required: - reg + - if: + properties: + compatible: + contains: + enum: + - qcom,qcs615-camnoc-virt + - qcom,qcs615-config-noc + - qcom,qcs615-dc-noc + - qcom,qcs615-gem-noc + - qcom,qcs615-mc-virt + - qcom,qcs615-mmss-noc + - qcom,qcs615-system-noc + then: + properties: + clocks: false + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,qcs8300-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,qcs8300-rpmh.yaml index e9f528d6d9a8..88fe17277110 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,qcs8300-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,qcs8300-rpmh.yaml @@ -35,6 +35,10 @@ properties: reg: maxItems: 1 + clocks: + minItems: 1 + maxItems: 4 + required: - compatible @@ -54,6 +58,64 @@ allOf: required: - reg + - if: + properties: + compatible: + contains: + enum: + - qcom,qcs8300-aggre1-noc + then: + properties: + clocks: + items: + - description: aggre UFS PHY AXI clock + - description: aggre QUP PRIM AXI clock + - description: aggre USB2 PRIM AXI clock + - description: aggre USB3 PRIM AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,qcs8300-aggre2-noc + then: + properties: + clocks: + items: + - description: RPMH CC IPA clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,qcs8300-gem-noc + then: + properties: + clocks: + items: + - description: GCC DDRSS GPU AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,qcs8300-clk-virt + - qcom,qcs8300-config-noc + - qcom,qcs8300-dc-noc + - qcom,qcs8300-gpdsp-anoc + - qcom,qcs8300-lpass-ag-noc + - qcom,qcs8300-mc-virt + - qcom,qcs8300-mmss-noc + - qcom,qcs8300-nspa-noc + - qcom,qcs8300-pcie-anoc + - qcom,qcs8300-system-noc + then: + properties: + clocks: false + unevaluatedProperties: false examples: @@ -63,6 +125,7 @@ examples: reg = <0x9100000 0xf7080>; #interconnect-cells = <2>; qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&gcc_ddrss_gpu_axi_clk>; }; clk_virt: interconnect-0 { diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml index bfd30aae682b..360a0643a0b5 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml @@ -50,7 +50,7 @@ properties: The 2nd cell contains the interrupt number for the interrupt type. SPI interrupts are in the range [0-987]. PPI interrupts are in the range [0-15]. Extended SPI interrupts are in the range [0-1023]. - Extended PPI interrupts are in the range [0-127]. + Extended PPI interrupts are in the range [0-63]. The 3rd cell is the flags, encoded as follows: bits[3:0] trigger type and level flags. diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,irqsteer.yaml b/Documentation/devicetree/bindings/interrupt-controller/fsl,irqsteer.yaml index 5c768c1e159c..13cd37bf48e4 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/fsl,irqsteer.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,irqsteer.yaml @@ -12,7 +12,9 @@ maintainers: properties: compatible: oneOf: - - const: fsl,imx-irqsteer + - enum: + - fsl,imx-irqsteer + - nxp,s32n79-irqsteer - items: - enum: - fsl,imx8m-irqsteer diff --git a/Documentation/devicetree/bindings/interrupt-controller/nvidia,tegra20-ictlr.yaml b/Documentation/devicetree/bindings/interrupt-controller/nvidia,tegra20-ictlr.yaml index 074a873880e5..d0c039d14ad2 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/nvidia,tegra20-ictlr.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/nvidia,tegra20-ictlr.yaml @@ -35,11 +35,12 @@ properties: - enum: - nvidia,tegra20-ictlr - nvidia,tegra30-ictlr + - nvidia,tegra210-ictlr reg: description: Each entry is a block of 32 interrupts minItems: 4 - maxItems: 5 + maxItems: 6 interrupt-controller: true @@ -64,10 +65,28 @@ allOf: properties: reg: maxItems: 4 - else: + + - if: + properties: + compatible: + contains: + const: nvidia,tegra30-ictlr + then: properties: reg: minItems: 5 + maxItems: 5 + + - if: + properties: + compatible: + contains: + const: nvidia,tegra210-ictlr + then: + properties: + reg: + minItems: 6 + maxItems: 6 examples: - | diff --git a/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml b/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml index f9321366cae4..b4942881b9c9 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml @@ -26,7 +26,9 @@ properties: compatible: items: - enum: + - qcom,eliza-pdc - qcom,glymur-pdc + - qcom,hawi-pdc - qcom,kaanapali-pdc - qcom,milos-pdc - qcom,qcs615-pdc diff --git a/Documentation/devicetree/bindings/leds/allwinner,sun50i-a100-ledc.yaml b/Documentation/devicetree/bindings/leds/allwinner,sun50i-a100-ledc.yaml index 760cb336dccb..0b73fe5b662f 100644 --- a/Documentation/devicetree/bindings/leds/allwinner,sun50i-a100-ledc.yaml +++ b/Documentation/devicetree/bindings/leds/allwinner,sun50i-a100-ledc.yaml @@ -21,6 +21,7 @@ properties: - enum: - allwinner,sun20i-d1-ledc - allwinner,sun50i-r329-ledc + - allwinner,sun55i-a523-ledc - const: allwinner,sun50i-a100-ledc reg: diff --git a/Documentation/devicetree/bindings/leds/leds-lp5860.yaml b/Documentation/devicetree/bindings/leds/leds-lp5860.yaml index 1ccba4854159..0e88c71c2d39 100644 --- a/Documentation/devicetree/bindings/leds/leds-lp5860.yaml +++ b/Documentation/devicetree/bindings/leds/leds-lp5860.yaml @@ -33,6 +33,11 @@ properties: '#size-cells': const: 0 + enable-gpios: + maxItems: 1 + description: | + GPIO attached to the chip's enable pin (VIO_EN). + patternProperties: '^multi-led@[0-9a-f]+$': type: object @@ -74,6 +79,7 @@ unevaluatedProperties: false examples: - | + #include #include spi { @@ -83,6 +89,7 @@ examples: led-controller@0 { compatible = "ti,lp5860"; reg = <0x0>; + enable-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>; #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/leds/sprd,sc2731-bltc.yaml b/Documentation/devicetree/bindings/leds/sprd,sc2731-bltc.yaml index 97535d6dc47a..2ae5cc31e623 100644 --- a/Documentation/devicetree/bindings/leds/sprd,sc2731-bltc.yaml +++ b/Documentation/devicetree/bindings/leds/sprd,sc2731-bltc.yaml @@ -18,7 +18,12 @@ description: | properties: compatible: - const: sprd,sc2731-bltc + oneOf: + - items: + - enum: + - sprd,sc2730-bltc + - const: sprd,sc2731-bltc + - const: sprd,sc2731-bltc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml index 7c4d6170491d..f5c584cf2146 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml @@ -24,6 +24,7 @@ properties: compatible: items: - enum: + - qcom,eliza-ipcc - qcom,glymur-ipcc - qcom,kaanapali-ipcc - qcom,milos-ipcc diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.yaml b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.yaml index 4e4fb4acd7f9..7a653a011f03 100644 --- a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.yaml @@ -52,6 +52,9 @@ properties: Should contain freqs and voltages and opp-supported-hw property, which is a bitfield indicating SoC speedo ID mask. +allOf: + - $ref: /schemas/thermal/thermal-cooling-devices.yaml + required: - compatible - reg @@ -59,7 +62,7 @@ required: - clock-names - nvidia,memory-controller -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -90,4 +93,5 @@ examples: operating-points-v2 = <&dvfs_opp_table>; #interconnect-cells = <0>; + #cooling-cells = <2>; }; diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-mc.yaml b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-mc.yaml new file mode 100644 index 000000000000..268d5ff958f9 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-mc.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/nvidia,tegra210-mc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra210 SoC Memory Controller + +maintainers: + - Thierry Reding + - Jon Hunter + +description: | + The NVIDIA Tegra210 SoC features a 64 bit memory controller that is split + into two 32 bit channels to support LPDDR3 and LPDDR4 with x16 subpartitions. + The MC handles memory requests for 34-bit virtual addresses from internal + clients and arbitrates among them to allocate memory bandwidth. + + Up to 8 GiB of physical memory can be supported. Security features such as + encryption of traffic to and from DRAM via general security apertures are + available for video and other secure applications. + +properties: + $nodename: + pattern: "^memory-controller@[0-9a-f]+$" + + compatible: + items: + - enum: + - nvidia,tegra210-mc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: module clock + + clock-names: + items: + - const: mc + + "#iommu-cells": + const: 1 + + "#reset-cells": + const: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - "#iommu-cells" + - "#reset-cells" + +additionalProperties: false + +examples: + - | + #include + #include + + memory-controller@70019000 { + compatible = "nvidia,tegra210-mc"; + reg = <0x70019000 0x1000>; + interrupts = ; + clocks = <&tegra_car TEGRA210_CLK_MC>; + clock-names = "mc"; + + #iommu-cells = <1>; + #reset-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/mfd/fsl,imx25-tsadc.yaml b/Documentation/devicetree/bindings/mfd/fsl,imx25-tsadc.yaml new file mode 100644 index 000000000000..b5c6a2d47501 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/fsl,imx25-tsadc.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/fsl,imx25-tsadc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale MX25 ADC/TSC MultiFunction Device (MFD) + +maintainers: + - Frank Li + +description: + This device combines two general purpose conversion queues one used for general + ADC and the other used for touchscreens. + +properties: + compatible: + const: fsl,imx25-tsadc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: ipg + + interrupt-controller: true + + '#interrupt-cells': + const: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 1 + + ranges: true + +patternProperties: + '^touchscreen@[0-9a-f]+$': + type: object + $ref: /schemas/input/touchscreen/fsl,imx25-tcq.yaml + unevaluatedProperties: false + + '^adc@[0-9a-f]+$': + type: object + $ref: /schemas/iio/adc/fsl,imx25-gcq.yaml + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - '#interrupt-cells' + - '#address-cells' + - '#size-cells' + +additionalProperties: false + +examples: + - | + tscadc@50030000 { + compatible = "fsl,imx25-tsadc"; + reg = <0x50030000 0xc>; + interrupts = <46>; + clocks = <&clks 119>; + clock-names = "ipg"; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + touchscreen@50030400 { + compatible = "fsl,imx25-tcq"; + reg = <0x50030400 0x60>; + interrupts = <0>; + fsl,wires = <4>; + }; + + adc@50030800 { + compatible = "fsl,imx25-gcq"; + reg = <0x50030800 0x60>; + interrupts = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml b/Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml index cfa69f1f380a..5cdb25be2731 100644 --- a/Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml +++ b/Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml @@ -76,8 +76,6 @@ properties: debounce-delay-ms: enum: [0, 30, 150, 750] default: 30 - description: - Sets the debouncing delay in milliseconds. active-low: description: Set active when pin is pulled low. diff --git a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt b/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt deleted file mode 100644 index b03505286997..000000000000 --- a/Documentation/devicetree/bindings/mfd/fsl-imx25-tsadc.txt +++ /dev/null @@ -1,47 +0,0 @@ -Freescale MX25 ADC/TSC MultiFunction Device (MFD) - -This device combines two general purpose conversion queues one used for general -ADC and the other used for touchscreens. - -Required properties: - - compatible: Should be "fsl,imx25-tsadc". - - reg: Start address and size of the memory area of - the device - - interrupts: Interrupt for this device - (See: ../interrupt-controller/interrupts.txt) - - clocks: An 'ipg' clock (See: ../clock/clock-bindings.txt) - - interrupt-controller: This device is an interrupt controller. It - controls the interrupts of both - conversion queues. - - #interrupt-cells: Should be '<1>'. - - #address-cells: Should be '<1>'. - - #size-cells: Should be '<1>'. - -This device includes two conversion queues which can be added as subnodes. -The first queue is for the touchscreen, the second for general purpose ADC. - -Example: - tscadc: tscadc@50030000 { - compatible = "fsl,imx25-tsadc"; - reg = <0x50030000 0xc>; - interrupts = <46>; - clocks = <&clks 119>; - clock-names = "ipg"; - interrupt-controller; - #interrupt-cells = <1>; - #address-cells = <1>; - #size-cells = <1>; - ranges; - - tsc: tcq@50030400 { - compatible = "fsl,imx25-tcq"; - reg = <0x50030400 0x60>; - ... - }; - - adc: gcq@50030800 { - compatible = "fsl,imx25-gcq"; - reg = <0x50030800 0x60>; - ... - }; - }; diff --git a/Documentation/devicetree/bindings/mfd/max77620.txt b/Documentation/devicetree/bindings/mfd/max77620.txt deleted file mode 100644 index 5a642a51d58e..000000000000 --- a/Documentation/devicetree/bindings/mfd/max77620.txt +++ /dev/null @@ -1,162 +0,0 @@ -MAX77620 Power management IC from Maxim Semiconductor. - -Required properties: -------------------- -- compatible: Must be one of - "maxim,max77620" - "maxim,max20024" - "maxim,max77663" -- reg: I2C device address. - -Optional properties: -------------------- -- interrupts: The interrupt on the parent the controller is - connected to. -- interrupt-controller: Marks the device node as an interrupt controller. -- #interrupt-cells: is <2> and their usage is compliant to the 2 cells - variant of <../interrupt-controller/interrupts.txt> - IRQ numbers for different interrupt source of MAX77620 - are defined at dt-bindings/mfd/max77620.h. - -- system-power-controller: Indicates that this PMIC is controlling the - system power, see [1] for more details. - -[1] Documentation/devicetree/bindings/power/power-controller.txt - -Optional subnodes and their properties: -======================================= - -Flexible power sequence configurations: --------------------------------------- -The Flexible Power Sequencer (FPS) allows each regulator to power up under -hardware or software control. Additionally, each regulator can power on -independently or among a group of other regulators with an adjustable power-up -and power-down delays (sequencing). GPIO1, GPIO2, and GPIO3 can be programmed -to be part of a sequence allowing external regulators to be sequenced along -with internal regulators. 32KHz clock can be programmed to be part of a -sequence. - -The flexible sequencing structure consists of two hardware enable inputs -(EN0, EN1), and 3 master sequencing timers called FPS0, FPS1 and FPS2. -Each master sequencing timer is programmable through its configuration -register to have a hardware enable source (EN1 or EN2) or a software enable -source (SW). When enabled/disabled, the master sequencing timer generates -eight sequencing events on different time periods called slots. The time -period between each event is programmable within the configuration register. -Each regulator, GPIO1, GPIO2, GPIO3, and 32KHz clock has a flexible power -sequence slave register which allows its enable source to be specified as -a flexible power sequencer timer or a software bit. When a FPS source of -regulators, GPIOs and clocks specifies the enable source to be a flexible -power sequencer, the power up and power down delays can be specified in -the regulators, GPIOs and clocks flexible power sequencer configuration -registers. - -When FPS event cleared (set to LOW), regulators, GPIOs and 32KHz -clock are set into following state at the sequencing event that -corresponds to its flexible sequencer configuration register. - Sleep state: In this state, regulators, GPIOs - and 32KHz clock get disabled at - the sequencing event. - Global Low Power Mode (GLPM): In this state, regulators are set in - low power mode at the sequencing event. - -The configuration parameters of FPS is provided through sub-node "fps" -and their child for FPS specific. The child node name for FPS are "fps0", -"fps1", and "fps2" for FPS0, FPS1 and FPS2 respectively. - -The FPS configurations like FPS source, power up and power down slots for -regulators, GPIOs and 32kHz clocks are provided in their respective -configuration nodes which is explained in respective sub-system DT -binding document. - -There is need for different FPS configuration parameters based on system -state like when system state changed from active to suspend or active to -power off (shutdown). - -Optional properties: -------------------- --maxim,fps-event-source: u32, FPS event source like external - hardware input to PMIC i.e. EN0, EN1 or - software (SW). - The macros are defined on - dt-bindings/mfd/max77620.h - for different control source. - - MAX77620_FPS_EVENT_SRC_EN0 - for hardware input pin EN0. - - MAX77620_FPS_EVENT_SRC_EN1 - for hardware input pin EN1. - - MAX77620_FPS_EVENT_SRC_SW - for software control. - --maxim,shutdown-fps-time-period-us: u32, FPS time period in microseconds - when system enters in to shutdown - state. - --maxim,suspend-fps-time-period-us: u32, FPS time period in microseconds - when system enters in to suspend state. - --maxim,device-state-on-disabled-event: u32, describe the PMIC state when FPS - event cleared (set to LOW) whether it - should go to sleep state or low-power - state. Following are valid values: - - MAX77620_FPS_INACTIVE_STATE_SLEEP - to set the PMIC state to sleep. - - MAX77620_FPS_INACTIVE_STATE_LOW_POWER - to set the PMIC state to low - power. - Absence of this property or other value - will not change device state when FPS - event get cleared. - -Here supported time periods by device in microseconds are as follows: -MAX77620 supports 40, 80, 160, 320, 640, 1280, 2560 and 5120 microseconds. -MAX20024 supports 20, 40, 80, 160, 320, 640, 1280 and 2540 microseconds. -MAX77663 supports 20, 40, 80, 160, 320, 640, 1280 and 2540 microseconds. - --maxim,power-ok-control: configure map power ok bit - 1: Enables POK(Power OK) to control nRST_IO and GPIO1 - POK function. - 0: Disables POK control. - if property missing, do not configure MPOK bit. - If POK mapping is enabled for GPIO1/nRST_IO then, - GPIO1/nRST_IO pins are HIGH only if all rails - that have POK control enabled are HIGH. - If any of the rails goes down(which are enabled for POK - control) then, GPIO1/nRST_IO goes LOW. - this property is valid for max20024 only. - -For DT binding details of different sub modules like GPIO, pincontrol, -regulator, power, please refer respective device-tree binding document -under their respective sub-system directories. - -Example: --------- -#include - -max77620@3c { - compatible = "maxim,max77620"; - reg = <0x3c>; - - interrupt-parent = <&intc>; - interrupts = <0 86 IRQ_TYPE_NONE>; - - interrupt-controller; - #interrupt-cells = <2>; - - fps { - fps0 { - maxim,shutdown-fps-time-period-us = <1280>; - maxim,fps-event-source = ; - }; - - fps1 { - maxim,shutdown-fps-time-period-us = <1280>; - maxim,fps-event-source = ; - }; - - fps2 { - maxim,shutdown-fps-time-period-us = <1280>; - maxim,fps-event-source = ; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/mfd/maxim,max77620.yaml b/Documentation/devicetree/bindings/mfd/maxim,max77620.yaml new file mode 100644 index 000000000000..602711865274 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/maxim,max77620.yaml @@ -0,0 +1,444 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/maxim,max77620.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MAX77620 Power management IC from Maxim Semiconductor + +maintainers: + - Svyatoslav Ryhel + +properties: + compatible: + enum: + - maxim,max20024 + - maxim,max77620 + - maxim,max77663 + + reg: + description: + Can contain an optional second I2C address pointing to the PMIC's + RTC device. If no RTC address is provided, a default address specific + to this PMIC will be used. + minItems: 1 + maxItems: 2 + + reg-names: + items: + - const: pmic + - const: rtc + + interrupts: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + gpio-controller: true + + "#gpio-cells": + const: 2 + description: + Device has 8 GPIO pins which can be configured as GPIO as well as + the special IO functions. The first cell is the pin number, and the + second cell is used to specify the gpio polarity (GPIO_ACTIVE_HIGH or + GPIO_ACTIVE_LOW). + + system-power-controller: true + + "#thermal-sensor-cells": + const: 0 + description: + Maxim Semiconductor MAX77620 supports alarm interrupts when its + die temperature crosses 120C and 140C. These threshold temperatures + are not configurable. Device does not provide the real temperature + of die other than just indicating whether temperature is above or + below threshold level. + + fps: + type: object + additionalProperties: false + description: | + The Flexible Power Sequencer (FPS) allows each regulator to power up + under hardware or software control. Additionally, each regulator can + power on independently or among a group of other regulators with an + adjustable power-up and power-down delays (sequencing). GPIO1, GPIO2, + and GPIO3 can be programmed to be part of a sequence allowing external + regulators to be sequenced along with internal regulators. 32KHz clock + can be programmed to be part of a sequence. + + The flexible sequencing structure consists of two hardware enable inputs + (EN0, EN1), and 3 master sequencing timers called FPS0, FPS1 and FPS2. + Each master sequencing timer is programmable through its configuration + register to have a hardware enable source (EN1 or EN2) or a software enable + source (SW). When enabled/disabled, the master sequencing timer generates + eight sequencing events on different time periods called slots. The time + period between each event is programmable within the configuration register. + Each regulator, GPIO1, GPIO2, GPIO3, and 32KHz clock has a flexible power + sequence slave register which allows its enable source to be specified as + a flexible power sequencer timer or a software bit. When a FPS source of + regulators, GPIOs and clocks specifies the enable source to be a flexible + power sequencer, the power up and power down delays can be specified in + the regulators, GPIOs and clocks flexible power sequencer configuration + registers. + + When FPS event cleared (set to LOW), regulators, GPIOs and 32KHz clock + are set into following state at the sequencing event that corresponds + to its flexible sequencer configuration register. + + Sleep state: In this state, regulators, GPIOs and 32KHz clock get disabled + at the sequencing event. + Global Low Power Mode (GLPM): In this state, regulators are set in low + power mode at the sequencing event. + + The configuration parameters of FPS is provided through sub-node "fps" + and their child for FPS specific. The child node name for FPS are "fps0", + "fps1", and "fps2" for FPS0, FPS1 and FPS2 respectively. + + The FPS configurations like FPS source, power up and power down slots for + regulators, GPIOs and 32kHz clocks are provided in their respective + configuration nodes which is explained in respective sub-system DT + binding document. + + There is need for different FPS configuration parameters based on system + state like when system state changed from active to suspend or active to + power off (shutdown). + + patternProperties: + "^fps[0-2]$": + type: object + additionalProperties: false + + properties: + maxim,fps-event-source: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + FPS event source like external hardware input to PMIC i.e. EN0, EN1 + or software (SW). + + The macros are defined on dt-bindings/mfd/max77620.h for different + control source. + - MAX77620_FPS_EVENT_SRC_EN0 for hardware input pin EN0. + - MAX77620_FPS_EVENT_SRC_EN1 for hardware input pin EN1. + - MAX77620_FPS_EVENT_SRC_SW for software control. + + maxim,shutdown-fps-time-period-us: + description: + FPS time period in microseconds when system enters in to shutdown state. + + maxim,suspend-fps-time-period-us: + description: + FPS time period in microseconds when system enters in to suspend state. + + maxim,device-state-on-disabled-event: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Describe the PMIC state when FPS event cleared (set to LOW) whether it + should go to sleep state or low-power state. Following are valid values: + - MAX77620_FPS_INACTIVE_STATE_SLEEP to set the PMIC state to sleep. + - MAX77620_FPS_INACTIVE_STATE_LOW_POWER to set the PMIC state to low + power. + Absence of this property or other value will not change device state + when FPS event get cleared. + + maxim,power-ok-control: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Configure map power ok bit + + 1: Enables POK(Power OK) to control nRST_IO and GPIO1 POK function. + 0: Disables POK control. + + If property missing, do not configure MPOK bit. If POK mapping is + enabled for GPIO1/nRST_IO then, GPIO1/nRST_IO pins are HIGH only if + all rails that have POK control enabled are HIGH. If any of the rails + goes down (which are enabled for POK control) then, GPIO1/nRST_IO + goes LOW. + enum: [0, 1] + + pinmux: + $ref: /schemas/pinctrl/maxim,max77620-pinctrl.yaml + + regulators: + $ref: /schemas/regulator/maxim,max77620-regulator.yaml + +allOf: + - if: + properties: + compatible: + contains: + enum: + - maxim,max20024 + - maxim,max77663 + then: + properties: + "#thermal-sensor-cells": false + fps: + patternProperties: + "^fps[0-2]$": + properties: + maxim,shutdown-fps-time-period-us: + enum: [20, 40, 80, 160, 320, 640, 1280, 2540] + maxim,suspend-fps-time-period-us: + enum: [20, 40, 80, 160, 320, 640, 1280, 2540] + maxim,power-ok-control: false + + - if: + properties: + compatible: + contains: + const: maxim,max77620 + then: + properties: + fps: + patternProperties: + "^fps[0-2]$": + properties: + maxim,shutdown-fps-time-period-us: + enum: [40, 80, 160, 320, 640, 1280, 2560, 5120] + maxim,suspend-fps-time-period-us: + enum: [40, 80, 160, 320, 640, 1280, 2560, 5120] + + - if: + properties: + compatible: + not: + contains: + const: maxim,max77663 + then: + properties: + reg-names: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pmic@3c { + compatible = "maxim,max77620"; + reg = <0x3c>; + + interrupt-parent = <&gpio>; + interrupts = <86 IRQ_TYPE_LEVEL_HIGH>; + + interrupt-controller; + #interrupt-cells = <2>; + + gpio-controller; + #gpio-cells = <2>; + + #thermal-sensor-cells = <0>; + + system-power-controller; + + pinctrl-names = "default"; + pinctrl-0 = <&max77620_default>; + + max77620_default: pinmux { + gpio0 { + pins = "gpio0"; + function = "gpio"; + }; + + gpio1 { + pins = "gpio1"; + function = "fps-out"; + maxim,active-fps-source = ; + }; + + gpio2 { + pins = "gpio2"; + function = "fps-out"; + maxim,active-fps-source = ; + }; + + gpio3 { + pins = "gpio3"; + function = "gpio"; + }; + + gpio4 { + pins = "gpio4"; + function = "32k-out1"; + }; + + gpio5-6 { + pins = "gpio5", "gpio6"; + function = "gpio"; + drive-push-pull = <1>; + }; + + gpio7 { + pins = "gpio7"; + function = "gpio"; + }; + }; + + fps { + fps0 { + maxim,shutdown-fps-time-period-us = <1280>; + maxim,fps-event-source = ; + }; + + fps1 { + maxim,shutdown-fps-time-period-us = <1280>; + maxim,fps-event-source = ; + }; + + fps2 { + maxim,shutdown-fps-time-period-us = <1280>; + maxim,fps-event-source = ; + }; + }; + + regulators { + in-sd0-supply = <&vdd_5v0_vbus>; + in-sd1-supply = <&vdd_5v0_vbus>; + in-sd2-supply = <&vdd_5v0_vbus>; + in-sd3-supply = <&vdd_5v0_vbus>; + + in-ldo0-1-supply = <&vdd_1v8_vio>; + in-ldo2-supply = <&vdd_3v3_vbat>; + in-ldo3-5-supply = <&vdd_3v3_vbat>; + in-ldo4-6-supply = <&vdd_3v3_vbat>; + in-ldo7-8-supply = <&vdd_1v8_vio>; + + sd0 { + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1250000>; + regulator-always-on; + regulator-boot-on; + + maxim,active-fps-source = ; + }; + + sd1 { + regulator-name = "vdd_core"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + + maxim,active-fps-source = ; + }; + + vdd_1v8_vio: sd2 { + regulator-name = "vdd_1v8_gen"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + + maxim,active-fps-source = ; + }; + + sd3 { + regulator-name = "vddio_ddr"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + + maxim,active-fps-source = ; + }; + + ldo0 { + regulator-name = "avdd_pll"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + + maxim,active-fps-source = ; + }; + + ldo1 { + regulator-name = "vdd_ddr_hs"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-boot-on; + + maxim,active-fps-source = ; + }; + + ldo2 { + regulator-name = "avdd_usb"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + maxim,active-fps-source = ; + }; + + ldo3 { + regulator-name = "vdd_sdmmc3"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + + maxim,active-fps-source = ; + }; + + ldo4 { + regulator-name = "vdd_rtc"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + + maxim,active-fps-source = ; + }; + + ldo5 { + regulator-name = "vdd_ddr_rx"; + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + regulator-always-on; + regulator-boot-on; + + maxim,active-fps-source = ; + }; + + ldo6 { + regulator-name = "avdd_osc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + + maxim,active-fps-source = ; + }; + + ldo7 { + regulator-name = "vdd_1v2_mhl"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1250000>; + + maxim,active-fps-source = ; + }; + + ldo8 { + regulator-name = "avdd_dsi_csi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + + maxim,active-fps-source = ; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml b/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml index 525de9ab3c2b..42e4a84d5204 100644 --- a/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml +++ b/Documentation/devicetree/bindings/mfd/maxim,max77759.yaml @@ -16,6 +16,9 @@ description: | The MAX77759 includes Battery Charger, Fuel Gauge, temperature sensors, USB Type-C Port Controller (TCPC), NVMEM, and a GPIO expander. +allOf: + - $ref: /schemas/power/supply/power-supply.yaml# + properties: compatible: const: maxim,max77759 @@ -37,12 +40,18 @@ properties: nvmem-0: $ref: /schemas/nvmem/maxim,max77759-nvmem.yaml + chgin-otg-regulator: + type: object + description: Provides Boost for sourcing VBUS. + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + required: - compatible - interrupts - reg -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -59,6 +68,11 @@ examples: interrupt-controller; #interrupt-cells = <2>; + power-supplies = <&maxtcpci>; + + chgin-otg-regulator { + regulator-name = "chgin-otg"; + }; gpio { compatible = "maxim,max77759-gpio"; diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml index e5931d18d998..644c42b5e2e5 100644 --- a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml @@ -135,6 +135,7 @@ patternProperties: "^adc@[0-9a-f]+$": type: object oneOf: + - $ref: /schemas/iio/adc/qcom,spmi-adc5-gen3.yaml# - $ref: /schemas/iio/adc/qcom,spmi-iadc.yaml# - $ref: /schemas/iio/adc/qcom,spmi-rradc.yaml# - $ref: /schemas/iio/adc/qcom,spmi-vadc.yaml# diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd72720-pmic.yaml b/Documentation/devicetree/bindings/mfd/rohm,bd72720-pmic.yaml index 9f42097dfbac..b094542339e8 100644 --- a/Documentation/devicetree/bindings/mfd/rohm,bd72720-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/rohm,bd72720-pmic.yaml @@ -4,19 +4,19 @@ $id: http://devicetree.org/schemas/mfd/rohm,bd72720-pmic.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: ROHM BD72720 Power Management Integrated Circuit +title: ROHM BD72720 and BD73900 Power Management Integrated Circuits maintainers: - Matti Vaittinen description: - BD72720 is a single-chip power management IC for battery-powered portable - devices. The BD72720 integrates 10 bucks and 11 LDOs, and a 3000 mA - switching charger. The IC also includes a Coulomb counter, a real-time - clock (RTC), GPIOs and a 32.768 kHz clock gate. + BD72720 and BD73900 are single-chip power management ICs for + battery-powered portable devices. They integrate 10 bucks and 11 LDOs, + and a 3000 mA switching charger. ICs also include a Coulomb counter, + a real-time clock (RTC), GPIOs and a 32.768 kHz clock gate. -# In addition to the properties found from the charger node, the ROHM BD72720 -# uses properties from a static battery node. Please see the: +# In addition to the properties found from the charger node, PMICs +# use properties from a static battery node. Please see the: # Documentation/devicetree/bindings/power/supply/battery.yaml # # Following properties are used @@ -48,7 +48,12 @@ description: properties: compatible: - const: rohm,bd72720 + oneOf: + - const: rohm,bd72720 + + - items: + - const: rohm,bd73900 + - const: rohm,bd72720 reg: description: @@ -84,7 +89,7 @@ properties: minimum: 10000 maximum: 50000 description: - BD72720 has a SAR ADC for measuring charging currents. External sense + PMIC has a SAR ADC for measuring charging currents. External sense resistor (RSENSE in data sheet) should be used. If some other but 30 mOhm resistor is used the resistance value should be given here in micro Ohms. @@ -100,7 +105,7 @@ properties: rohm,pin-fault_b: $ref: /schemas/types.yaml#/definitions/string description: - BD72720 has an OTP option to use fault_b-pin for different + PMIC has an OTP option to use fault_b-pin for different purposes. Set this property accordingly. OTP options are OTP0 - bi-directional FAULT_B or READY indicator depending on a 'sub option' @@ -116,7 +121,7 @@ patternProperties: "^rohm,pin-dvs[0-1]$": $ref: /schemas/types.yaml#/definitions/string description: - BD72720 has 4 different OTP options to determine the use of dvs-pins. + PMIC has 4 different OTP options to determine the use of dvs-pins. OTP0 - regulator RUN state control. OTP1 - GPI. OTP2 - GPO. @@ -130,7 +135,7 @@ patternProperties: "^rohm,pin-exten[0-1]$": $ref: /schemas/types.yaml#/definitions/string - description: BD72720 has an OTP option to use exten0-pin for different + description: PMIC has an OTP option to use exten0-pin for different purposes. Set this property accordingly. OTP0 - GPO OTP1 - Power sequencer output. diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml index e57add2bacd3..e22867088063 100644 --- a/Documentation/devicetree/bindings/mfd/syscon.yaml +++ b/Documentation/devicetree/bindings/mfd/syscon.yaml @@ -61,6 +61,7 @@ select: - cirrus,ep7209-syscon2 - cirrus,ep7209-syscon3 - cnxt,cx92755-uc + - econet,en751221-chip-scu - freecom,fsg-cs2-system-controller - fsl,imx93-aonmix-ns-syscfg - fsl,imx93-wakeupmix-syscfg @@ -173,6 +174,7 @@ properties: - cirrus,ep7209-syscon2 - cirrus,ep7209-syscon3 - cnxt,cx92755-uc + - econet,en751221-chip-scu - freecom,fsg-cs2-system-controller - fsl,imx93-aonmix-ns-syscfg - fsl,imx93-wakeupmix-syscfg diff --git a/Documentation/devicetree/bindings/mips/mobileye.yaml b/Documentation/devicetree/bindings/mips/mobileye.yaml index d60744550e46..83abe268e96b 100644 --- a/Documentation/devicetree/bindings/mips/mobileye.yaml +++ b/Documentation/devicetree/bindings/mips/mobileye.yaml @@ -31,6 +31,11 @@ properties: - enum: - mobileye,eyeq6h-epm6 - const: mobileye,eyeq6h + - description: Boards with Mobileye EyeQ6Lplus SoC + items: + - enum: + - mobileye,eyeq6lplus-epm6 + - const: mobileye,eyeq6lplus additionalProperties: true diff --git a/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml b/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml index d8e47db677cc..ca830dd06de2 100644 --- a/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml +++ b/Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml @@ -18,9 +18,14 @@ description: | properties: compatible: - enum: - - qcom,kaanapali-fastrpc - - qcom,fastrpc + oneOf: + - enum: + - qcom,kaanapali-fastrpc + - qcom,fastrpc + - items: + - enum: + - qcom,glymur-fastrpc + - const: qcom,kaanapali-fastrpc label: enum: diff --git a/Documentation/devicetree/bindings/misc/ti,fpc202.yaml b/Documentation/devicetree/bindings/misc/ti,fpc202.yaml index a8cb10f2d0df..71c5859d2e13 100644 --- a/Documentation/devicetree/bindings/misc/ti,fpc202.yaml +++ b/Documentation/devicetree/bindings/misc/ti,fpc202.yaml @@ -53,6 +53,22 @@ patternProperties: unevaluatedProperties: false + "^led@1[4-b]$": + $ref: /schemas/leds/common.yaml# + description: Output GPIO line with advanced LED features enabled. + + properties: + reg: + minimum: 0x14 + maximum: 0x1b + description: + GPIO line ID + + required: + - reg + + unevaluatedProperties: false + required: - compatible - reg @@ -89,6 +105,11 @@ examples: #size-cells = <0>; reg = <1>; }; + + led@14 { + reg = <0x14>; + label = "phy0:green:indicator"; + }; }; }; ... diff --git a/Documentation/devicetree/bindings/mtd/gpmi-nand.yaml b/Documentation/devicetree/bindings/mtd/gpmi-nand.yaml index 0badb2e978c7..adb684e3207c 100644 --- a/Documentation/devicetree/bindings/mtd/gpmi-nand.yaml +++ b/Documentation/devicetree/bindings/mtd/gpmi-nand.yaml @@ -101,7 +101,7 @@ required: unevaluatedProperties: false allOf: - - $ref: nand-controller.yaml + - $ref: nand-controller-legacy.yaml - if: properties: diff --git a/Documentation/devicetree/bindings/mtd/mxc-nand.yaml b/Documentation/devicetree/bindings/mtd/mxc-nand.yaml index bd8f7b683953..fbaff7d3eda8 100644 --- a/Documentation/devicetree/bindings/mtd/mxc-nand.yaml +++ b/Documentation/devicetree/bindings/mtd/mxc-nand.yaml @@ -10,22 +10,43 @@ maintainers: - Uwe Kleine-König allOf: - - $ref: nand-controller.yaml + - $ref: nand-controller-legacy.yaml properties: compatible: oneOf: - - const: fsl,imx27-nand + - enum: + - fsl,imx25-nand + - fsl,imx27-nand + - fsl,imx51-nand + - fsl,imx53-nand + - items: + - enum: + - fsl,imx35-nand + - const: fsl,imx25-nand - items: - enum: - fsl,imx31-nand - const: fsl,imx27-nand reg: - maxItems: 1 + minItems: 1 + items: + - description: IP register space + - description: Nand flash internal buffer space interrupts: maxItems: 1 + clocks: + maxItems: 1 + + dmas: + maxItems: 1 + + dma-names: + items: + - const: rx-tx + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/mtd/nand-chip.yaml b/Documentation/devicetree/bindings/mtd/nand-chip.yaml index 609d4a4ddd80..8800d1d07266 100644 --- a/Documentation/devicetree/bindings/mtd/nand-chip.yaml +++ b/Documentation/devicetree/bindings/mtd/nand-chip.yaml @@ -11,6 +11,7 @@ maintainers: allOf: - $ref: mtd.yaml# + - $ref: nand-property.yaml description: | This file covers the generic description of a NAND chip. It implies that the @@ -22,51 +23,6 @@ properties: description: Contains the chip-select IDs. - nand-ecc-engine: - description: | - A phandle on the hardware ECC engine if any. There are - basically three possibilities: - 1/ The ECC engine is part of the NAND controller, in this - case the phandle should reference the parent node. - 2/ The ECC engine is part of the NAND part (on-die), in this - case the phandle should reference the node itself. - 3/ The ECC engine is external, in this case the phandle should - reference the specific ECC engine node. - $ref: /schemas/types.yaml#/definitions/phandle - - nand-use-soft-ecc-engine: - description: Use a software ECC engine. - type: boolean - - nand-no-ecc-engine: - description: Do not use any ECC correction. - type: boolean - - nand-ecc-algo: - description: - Desired ECC algorithm. - $ref: /schemas/types.yaml#/definitions/string - enum: [hamming, bch, rs] - - nand-ecc-strength: - description: - Maximum number of bits that can be corrected per ECC step. - $ref: /schemas/types.yaml#/definitions/uint32 - minimum: 1 - - nand-ecc-step-size: - description: - Number of data bytes covered by a single ECC step. - $ref: /schemas/types.yaml#/definitions/uint32 - minimum: 1 - - secure-regions: - description: - Regions in the NAND chip which are protected using a secure element - like Trustzone. This property contains the start address and size of - the secure regions present. - $ref: /schemas/types.yaml#/definitions/uint64-matrix - required: - reg diff --git a/Documentation/devicetree/bindings/mtd/nand-controller-legacy.yaml b/Documentation/devicetree/bindings/mtd/nand-controller-legacy.yaml new file mode 100644 index 000000000000..d6e612413df1 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/nand-controller-legacy.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/nand-controller-legacy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NAND Controller Common Properties + +maintainers: + - Miquel Raynal + - Richard Weinberger + +description: > + The NAND controller should be represented with its own DT node, and + all NAND chips attached to this controller should be defined as + children nodes of the NAND controller. This representation should be + enforced even for simple controllers supporting only one chip. + + This is only for legacy nand controller, new controller should use + nand-controller.yaml + +properties: + + "#address-cells": + const: 1 + + "#size-cells": + enum: [0, 1] + + ranges: true + + cs-gpios: + description: + Array of chip-select available to the controller. The first + entries are a 1:1 mapping of the available chip-select on the + NAND controller (even if they are not used). As many additional + chip-select as needed may follow and should be phandles of GPIO + lines. 'reg' entries of the NAND chip subnodes become indexes of + this array when this property is present. + minItems: 1 + maxItems: 8 + + partitions: + type: object + + required: + - compatible + +patternProperties: + "^nand@[a-f0-9]$": + type: object + $ref: raw-nand-chip.yaml# + + "^partition@[0-9a-f]+$": + type: object + $ref: /schemas/mtd/partitions/partition.yaml#/$defs/partition-node + deprecated: true + +allOf: + - $ref: raw-nand-property.yaml# + - $ref: nand-property.yaml# + +# This is a generic file other binding inherit from and extend +additionalProperties: true + diff --git a/Documentation/devicetree/bindings/mtd/nand-controller.yaml b/Documentation/devicetree/bindings/mtd/nand-controller.yaml index 28167c0cf271..0aa61d5fa50b 100644 --- a/Documentation/devicetree/bindings/mtd/nand-controller.yaml +++ b/Documentation/devicetree/bindings/mtd/nand-controller.yaml @@ -16,6 +16,8 @@ description: | children nodes of the NAND controller. This representation should be enforced even for simple controllers supporting only one chip. +select: false + properties: $nodename: pattern: "^nand-controller(@.*)?" diff --git a/Documentation/devicetree/bindings/mtd/nand-property.yaml b/Documentation/devicetree/bindings/mtd/nand-property.yaml new file mode 100644 index 000000000000..55488a4b1548 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/nand-property.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/nand-property.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NAND Chip Common Properties + +maintainers: + - Miquel Raynal + +description: | + This file covers the generic properties of a NAND chip. It implies that the + bus interface should not be taken into account: both raw NAND devices and + SPI-NAND devices are concerned by this description. + +properties: + nand-ecc-engine: + description: | + A phandle on the hardware ECC engine if any. There are + basically three possibilities: + 1/ The ECC engine is part of the NAND controller, in this + case the phandle should reference the parent node. + 2/ The ECC engine is part of the NAND part (on-die), in this + case the phandle should reference the node itself. + 3/ The ECC engine is external, in this case the phandle should + reference the specific ECC engine node. + $ref: /schemas/types.yaml#/definitions/phandle + + nand-use-soft-ecc-engine: + description: Use a software ECC engine. + type: boolean + + nand-no-ecc-engine: + description: Do not use any ECC correction. + type: boolean + + nand-ecc-algo: + description: + Desired ECC algorithm. + $ref: /schemas/types.yaml#/definitions/string + enum: [hamming, bch, rs] + + nand-ecc-strength: + description: + Maximum number of bits that can be corrected per ECC step. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + + nand-ecc-step-size: + description: + Number of data bytes covered by a single ECC step. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + + secure-regions: + description: + Regions in the NAND chip which are protected using a secure element + like Trustzone. This property contains the start address and size of + the secure regions present. + $ref: /schemas/types.yaml#/definitions/uint64-matrix + +# This file can be referenced by more specific devices (like spi-nands) +additionalProperties: true diff --git a/Documentation/devicetree/bindings/mtd/partitions/partition.yaml b/Documentation/devicetree/bindings/mtd/partitions/partition.yaml index 2397d97ecac5..eaeac2f2ea94 100644 --- a/Documentation/devicetree/bindings/mtd/partitions/partition.yaml +++ b/Documentation/devicetree/bindings/mtd/partitions/partition.yaml @@ -57,6 +57,15 @@ properties: user space from type: boolean + part-concat-next: + description: List of phandles to MTD partitions that need be concatenated + with the current partition. + $ref: /schemas/types.yaml#/definitions/phandle-array + minItems: 1 + maxItems: 16 + items: + maxItems: 1 + align: $ref: /schemas/types.yaml#/definitions/uint32 minimum: 2 @@ -180,4 +189,15 @@ examples: reg = <0x200000 0x100000>; align = <0x4000>; }; + + part0: partition@400000 { + part-concat-next = <&part1>; + label = "part0_0"; + reg = <0x400000 0x100000>; + }; + + part1: partition@800000 { + label = "part0_1"; + reg = <0x800000 0x800000>; + }; }; diff --git a/Documentation/devicetree/bindings/mtd/raw-nand-chip.yaml b/Documentation/devicetree/bindings/mtd/raw-nand-chip.yaml index 092448d7bfc5..792de3e3c6ee 100644 --- a/Documentation/devicetree/bindings/mtd/raw-nand-chip.yaml +++ b/Documentation/devicetree/bindings/mtd/raw-nand-chip.yaml @@ -11,6 +11,7 @@ maintainers: allOf: - $ref: nand-chip.yaml# + - $ref: raw-nand-property.yaml# description: | The ECC strength and ECC step size properties define the user @@ -31,79 +32,6 @@ properties: description: Contains the chip-select IDs. - nand-ecc-placement: - description: - Location of the ECC bytes. This location is unknown by default - but can be explicitly set to "oob", if all ECC bytes are - known to be stored in the OOB area, or "interleaved" if ECC - bytes will be interleaved with regular data in the main area. - $ref: /schemas/types.yaml#/definitions/string - enum: [ oob, interleaved ] - deprecated: true - - nand-ecc-mode: - description: - Legacy ECC configuration mixing the ECC engine choice and - configuration. - $ref: /schemas/types.yaml#/definitions/string - enum: [none, soft, soft_bch, hw, hw_syndrome, on-die] - deprecated: true - - nand-bus-width: - description: - Bus width to the NAND chip - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [8, 16] - default: 8 - - nand-on-flash-bbt: - description: - With this property, the OS will search the device for a Bad - Block Table (BBT). If not found, it will create one, reserve - a few blocks at the end of the device to store it and update - it as the device ages. Otherwise, the out-of-band area of a - few pages of all the blocks will be scanned at boot time to - find Bad Block Markers (BBM). These markers will help to - build a volatile BBT in RAM. - $ref: /schemas/types.yaml#/definitions/flag - - nand-ecc-maximize: - description: - Whether or not the ECC strength should be maximized. The - maximum ECC strength is both controller and chip - dependent. The ECC engine has to select the ECC config - providing the best strength and taking the OOB area size - constraint into account. This is particularly useful when - only the in-band area is used by the upper layers, and you - want to make your NAND as reliable as possible. - $ref: /schemas/types.yaml#/definitions/flag - - nand-is-boot-medium: - description: - Whether or not the NAND chip is a boot medium. Drivers might - use this information to select ECC algorithms supported by - the boot ROM or similar restrictions. - $ref: /schemas/types.yaml#/definitions/flag - - nand-rb: - description: - Contains the native Ready/Busy IDs. - $ref: /schemas/types.yaml#/definitions/uint32-array - - rb-gpios: - description: - Contains one or more GPIO descriptor (the numper of descriptor - depends on the number of R/B pins exposed by the flash) for the - Ready/Busy pins. Active state refers to the NAND ready state and - should be set to GPIOD_ACTIVE_HIGH unless the signal is inverted. - - wp-gpios: - description: - Contains one GPIO descriptor for the Write Protect pin. - Active state refers to the NAND Write Protect state and should be - set to GPIOD_ACTIVE_LOW unless the signal is inverted. - maxItems: 1 - required: - reg diff --git a/Documentation/devicetree/bindings/mtd/raw-nand-property.yaml b/Documentation/devicetree/bindings/mtd/raw-nand-property.yaml new file mode 100644 index 000000000000..f853b72426c4 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/raw-nand-property.yaml @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/raw-nand-property.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Raw NAND Chip Common Properties + +maintainers: + - Miquel Raynal + +description: | + The ECC strength and ECC step size properties define the user + desires in terms of correction capability of a controller. Together, + they request the ECC engine to correct {strength} bit errors per + {size} bytes for a particular raw NAND chip. + + The interpretation of these parameters is implementation-defined, so + not all implementations must support all possible + combinations. However, implementations are encouraged to further + specify the value(s) they support. + +properties: + nand-ecc-placement: + description: + Location of the ECC bytes. This location is unknown by default + but can be explicitly set to "oob", if all ECC bytes are + known to be stored in the OOB area, or "interleaved" if ECC + bytes will be interleaved with regular data in the main area. + $ref: /schemas/types.yaml#/definitions/string + enum: [ oob, interleaved ] + deprecated: true + + nand-ecc-mode: + description: + Legacy ECC configuration mixing the ECC engine choice and + configuration. + $ref: /schemas/types.yaml#/definitions/string + enum: [none, soft, soft_bch, hw, hw_syndrome, on-die] + deprecated: true + + nand-bus-width: + description: + Bus width to the NAND chip + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [8, 16] + default: 8 + + nand-on-flash-bbt: + description: + With this property, the OS will search the device for a Bad + Block Table (BBT). If not found, it will create one, reserve + a few blocks at the end of the device to store it and update + it as the device ages. Otherwise, the out-of-band area of a + few pages of all the blocks will be scanned at boot time to + find Bad Block Markers (BBM). These markers will help to + build a volatile BBT in RAM. + $ref: /schemas/types.yaml#/definitions/flag + + nand-ecc-maximize: + description: + Whether or not the ECC strength should be maximized. The + maximum ECC strength is both controller and chip + dependent. The ECC engine has to select the ECC config + providing the best strength and taking the OOB area size + constraint into account. This is particularly useful when + only the in-band area is used by the upper layers, and you + want to make your NAND as reliable as possible. + $ref: /schemas/types.yaml#/definitions/flag + + nand-is-boot-medium: + description: + Whether or not the NAND chip is a boot medium. Drivers might + use this information to select ECC algorithms supported by + the boot ROM or similar restrictions. + $ref: /schemas/types.yaml#/definitions/flag + + nand-rb: + description: + Contains the native Ready/Busy IDs. + $ref: /schemas/types.yaml#/definitions/uint32-array + + rb-gpios: + description: + Contains one or more GPIO descriptor (the numper of descriptor + depends on the number of R/B pins exposed by the flash) for the + Ready/Busy pins. Active state refers to the NAND ready state and + should be set to GPIOD_ACTIVE_HIGH unless the signal is inverted. + + wp-gpios: + description: + Contains one GPIO descriptor for the Write Protect pin. + Active state refers to the NAND Write Protect state and should be + set to GPIOD_ACTIVE_LOW unless the signal is inverted. + maxItems: 1 + +# This is a generic file other binding inherit from and extend +additionalProperties: true diff --git a/Documentation/devicetree/bindings/mux/mux-controller.yaml b/Documentation/devicetree/bindings/mux/mux-controller.yaml index 78340bbe4df6..6defb9da10f7 100644 --- a/Documentation/devicetree/bindings/mux/mux-controller.yaml +++ b/Documentation/devicetree/bindings/mux/mux-controller.yaml @@ -63,18 +63,12 @@ description: | select: anyOf: - - properties: - $nodename: - pattern: '^mux-controller' - required: - '#mux-control-cells' - required: - '#mux-state-cells' properties: - $nodename: - pattern: '^mux-controller(@.*|-([0-9]|[1-9][0-9]+))?$' - '#mux-control-cells': enum: [ 0, 1 ] diff --git a/Documentation/devicetree/bindings/npu/arm,ethos.yaml b/Documentation/devicetree/bindings/npu/arm,ethos.yaml index 716c4997f976..d5a1fae4db9d 100644 --- a/Documentation/devicetree/bindings/npu/arm,ethos.yaml +++ b/Documentation/devicetree/bindings/npu/arm,ethos.yaml @@ -30,7 +30,7 @@ properties: - fsl,imx93-npu - const: arm,ethos-u65 - items: - - {} + - const: arm,corstone1000-ethos-u85 - const: arm,ethos-u85 reg: diff --git a/Documentation/devicetree/bindings/nvmem/apple,spmi-nvmem.yaml b/Documentation/devicetree/bindings/nvmem/apple,spmi-nvmem.yaml index 80b5a6cdcec9..4ca75ed07a54 100644 --- a/Documentation/devicetree/bindings/nvmem/apple,spmi-nvmem.yaml +++ b/Documentation/devicetree/bindings/nvmem/apple,spmi-nvmem.yaml @@ -9,7 +9,7 @@ title: Apple SPMI NVMEM description: Exports a series of SPMI registers as NVMEM cells maintainers: - - Sasha Finkelstein + - Sasha Finkelstein allOf: - $ref: nvmem.yaml# diff --git a/Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml b/Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml index afd1919c6b1c..c713e23819f1 100644 --- a/Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml +++ b/Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml @@ -19,12 +19,7 @@ select: false properties: compatible: - oneOf: - - items: - - enum: - - kontron,sa67-vpd - - const: kontron,sl28-vpd - - const: kontron,sl28-vpd + const: kontron,sl28-vpd serial-number: type: object diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml index 839513d4b499..2ab047f2bb69 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -26,6 +26,7 @@ properties: - qcom,ipq8064-qfprom - qcom,ipq8074-qfprom - qcom,ipq9574-qfprom + - qcom,kaanapali-qfprom - qcom,msm8226-qfprom - qcom,msm8916-qfprom - qcom,msm8917-qfprom diff --git a/Documentation/devicetree/bindings/nvmem/rockchip,otp.yaml b/Documentation/devicetree/bindings/nvmem/rockchip,otp.yaml index dc89020b0950..7e4d5e1c4ced 100644 --- a/Documentation/devicetree/bindings/nvmem/rockchip,otp.yaml +++ b/Documentation/devicetree/bindings/nvmem/rockchip,otp.yaml @@ -14,6 +14,9 @@ properties: enum: - rockchip,px30-otp - rockchip,rk3308-otp + - rockchip,rk3528-otp + - rockchip,rk3562-otp + - rockchip,rk3568-otp - rockchip,rk3576-otp - rockchip,rk3588-otp @@ -26,19 +29,15 @@ properties: clock-names: minItems: 3 - items: - - const: otp - - const: apb_pclk - - const: phy - - const: arb + maxItems: 4 resets: minItems: 1 - maxItems: 3 + maxItems: 4 reset-names: minItems: 1 - maxItems: 3 + maxItems: 4 required: - compatible @@ -64,13 +63,68 @@ allOf: clocks: maxItems: 3 clock-names: - maxItems: 3 + items: + - const: otp + - const: apb_pclk + - const: phy resets: maxItems: 1 reset-names: items: - const: phy + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3528-otp + then: + properties: + clocks: + maxItems: 3 + clock-names: + items: + - const: otp + - const: apb_pclk + - const: sbpi + resets: + minItems: 3 + maxItems: 3 + reset-names: + items: + - const: otp + - const: apb + - const: sbpi + + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3562-otp + - rockchip,rk3568-otp + then: + properties: + clocks: + minItems: 4 + maxItems: 4 + clock-names: + items: + - const: otp + - const: apb_pclk + - const: phy + - const: sbpi + resets: + minItems: 4 + maxItems: 4 + reset-names: + items: + - const: otp + - const: apb + - const: phy + - const: sbpi + - if: properties: compatible: @@ -82,7 +136,10 @@ allOf: clocks: maxItems: 3 clock-names: - maxItems: 3 + items: + - const: otp + - const: apb_pclk + - const: phy resets: minItems: 2 maxItems: 2 @@ -101,10 +158,16 @@ allOf: properties: clocks: minItems: 4 + maxItems: 4 clock-names: - minItems: 4 + items: + - const: otp + - const: apb_pclk + - const: phy + - const: arb resets: minItems: 3 + maxItems: 3 reset-names: items: - const: otp diff --git a/Documentation/devicetree/bindings/opp/opp-v2.yaml b/Documentation/devicetree/bindings/opp/opp-v2.yaml index 6972d76233aa..10000a758572 100644 --- a/Documentation/devicetree/bindings/opp/opp-v2.yaml +++ b/Documentation/devicetree/bindings/opp/opp-v2.yaml @@ -172,7 +172,7 @@ examples: cpu@0 { compatible = "arm,cortex-a7"; device_type = "cpu"; - reg = <0>; + reg = <0x0>; next-level-cache = <&L2>; clocks = <&clk_controller 0>; clock-names = "cpu"; @@ -183,7 +183,7 @@ examples: cpu@1 { compatible = "arm,cortex-a7"; device_type = "cpu"; - reg = <1>; + reg = <0x1>; next-level-cache = <&L2>; clocks = <&clk_controller 0>; clock-names = "cpu"; @@ -194,7 +194,7 @@ examples: cpu@100 { compatible = "arm,cortex-a15"; device_type = "cpu"; - reg = <100>; + reg = <0x100>; next-level-cache = <&L2>; clocks = <&clk_controller 1>; clock-names = "cpu"; @@ -205,7 +205,7 @@ examples: cpu@101 { compatible = "arm,cortex-a15"; device_type = "cpu"; - reg = <101>; + reg = <0x101>; next-level-cache = <&L2>; clocks = <&clk_controller 1>; clock-names = "cpu"; diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra264-pcie.yaml b/Documentation/devicetree/bindings/pci/nvidia,tegra264-pcie.yaml new file mode 100644 index 000000000000..dc4f8725c9f5 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/nvidia,tegra264-pcie.yaml @@ -0,0 +1,149 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/nvidia,tegra264-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NVIDIA Tegra264 PCIe controller + +maintainers: + - Thierry Reding + - Jon Hunter + +properties: + compatible: + const: nvidia,tegra264-pcie + + reg: + description: | + Of the six PCIe controllers found on Tegra264, one (C0) is used for the + internal GPU and the other five (C1-C5) are routed to connectors such as + PCI or M.2 slots. Therefore the UPHY registers (XPL) exist only for C1 + through C5, but not for C0. + minItems: 4 + items: + - description: ECAM-compatible configuration space + - description: application layer registers + - description: transaction layer registers + - description: privileged transaction layer registers + - description: data link/physical layer registers (not available on C0) + + reg-names: + minItems: 4 + items: + - const: ecam + - const: xal + - const: xtl + - const: xtl-pri + - const: xpl + + interrupts: + minItems: 1 + maxItems: 4 + + dma-coherent: true + + nvidia,bpmp: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + Must contain a pair of phandle (to the BPMP controller node) and + controller ID. The following are the controller IDs for each controller: + + 0: C0 + 1: C1 + 2: C2 + 3: C3 + 4: C4 + 5: C5 + items: + - items: + - description: phandle to the BPMP controller node + - description: PCIe controller ID + maximum: 5 + +required: + - interrupt-map + - interrupt-map-mask + - iommu-map + - msi-map + - nvidia,bpmp + +allOf: + - $ref: /schemas/pci/pci-host-bridge.yaml# + +unevaluatedProperties: false + +examples: + - | + bus { + #address-cells = <2>; + #size-cells = <2>; + + pci@c000000 { + compatible = "nvidia,tegra264-pcie"; + reg = <0xd0 0xb0000000 0x0 0x10000000>, + <0x00 0x0c000000 0x0 0x00004000>, + <0x00 0x0c004000 0x0 0x00001000>, + <0x00 0x0c005000 0x0 0x00001000>; + reg-names = "ecam", "xal", "xtl", "xtl-pri"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + linux,pci-domain = <0x00>; + #interrupt-cells = <0x1>; + + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0x0 0x0 155 4>, + <0x0 0x0 0x0 0x2 &gic 0x0 0x0 0x0 156 4>, + <0x0 0x0 0x0 0x3 &gic 0x0 0x0 0x0 157 4>, + <0x0 0x0 0x0 0x4 &gic 0x0 0x0 0x0 158 4>; + + iommu-map = <0x0 &smmu2 0x10000 0x10000>; + msi-map = <0x0 &its 0x210000 0x10000>; + dma-coherent; + + ranges = <0x81000000 0x00 0x84000000 0xd0 0x84000000 0x00 0x00200000>, + <0x82000000 0x00 0x20000000 0x00 0x20000000 0x00 0x08000000>, + <0xc3000000 0xd0 0xc0000000 0xd0 0xc0000000 0x07 0xc0000000>; + bus-range = <0x0 0xff>; + + nvidia,bpmp = <&bpmp 0>; + }; + }; + + - | + bus { + #address-cells = <2>; + #size-cells = <2>; + + pci@8400000 { + compatible = "nvidia,tegra264-pcie"; + reg = <0xa8 0xb0000000 0x0 0x10000000>, + <0x00 0x08400000 0x0 0x00004000>, + <0x00 0x08404000 0x0 0x00001000>, + <0x00 0x08405000 0x0 0x00001000>, + <0x00 0x08410000 0x0 0x00010000>; + reg-names = "ecam", "xal", "xtl", "xtl-pri", "xpl"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + linux,pci-domain = <0x01>; + #interrupt-cells = <1>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0x0 0x0 908 4>, + <0x0 0x0 0x0 0x2 &gic 0x0 0x0 0x0 909 4>, + <0x0 0x0 0x0 0x3 &gic 0x0 0x0 0x0 910 4>, + <0x0 0x0 0x0 0x4 &gic 0x0 0x0 0x0 911 4>; + + iommu-map = <0x0 &smmu1 0x10000 0x10000>; + msi-map = <0x0 &its 0x110000 0x10000>; + dma-coherent; + + ranges = <0x81000000 0x00 0x84000000 0xa8 0x84000000 0x00 0x00200000>, + <0x82000000 0x00 0x28000000 0x00 0x28000000 0x00 0x08000000>, + <0xc3000000 0xa8 0xc0000000 0xa8 0xc0000000 0x07 0xc0000000>; + bus-range = <0x00 0xff>; + + nvidia,bpmp = <&bpmp 1>; + }; + }; diff --git a/Documentation/devicetree/bindings/phy/canaan,k230-usb-phy.yaml b/Documentation/devicetree/bindings/phy/canaan,k230-usb-phy.yaml new file mode 100644 index 000000000000..b959b381c44c --- /dev/null +++ b/Documentation/devicetree/bindings/phy/canaan,k230-usb-phy.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/canaan,k230-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Canaan K230 USB2.0 PHY + +maintainers: + - Jiayu Du + +properties: + compatible: + const: canaan,k230-usb-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 1 + +required: + - compatible + - reg + - "#phy-cells" + +additionalProperties: false + +examples: + - | + usbphy: usb-phy@91585000 { + compatible = "canaan,k230-usb-phy"; + reg = <0x91585000 0x400>; + #phy-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/phy/eswin,eic7700-sata-phy.yaml b/Documentation/devicetree/bindings/phy/eswin,eic7700-sata-phy.yaml new file mode 100644 index 000000000000..fc7dbac77acf --- /dev/null +++ b/Documentation/devicetree/bindings/phy/eswin,eic7700-sata-phy.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/eswin,eic7700-sata-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Eswin EIC7700 SoC SATA PHY + +maintainers: + - Yulin Lu + - Huan He + +properties: + compatible: + const: eswin,eic7700-sata-phy + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: phy + + resets: + maxItems: 2 + + reset-names: + items: + - const: port + - const: phy + + eswin,tx-amplitude-tuning: + description: This adjusts the transmitter amplitude signal, and its value + is derived from eye diagram tuning. The three values correspond to Gen1, + Gen2, and Gen3 parameters respectively. + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: Gen1 parameter. + minimum: 0 + maximum: 0x7f + - description: Gen2 parameter. + minimum: 0 + maximum: 0x7f + - description: Gen3 parameter. + minimum: 0 + maximum: 0x7f + default: [0, 0, 0] + + eswin,tx-preemph-tuning: + description: This adjusts the transmitter de-emphasis signal, and its value + is derived from eye diagram tuning. The three values correspond to Gen1, + Gen2, and Gen3 parameters respectively. + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: Gen1 parameter. + minimum: 0 + maximum: 0x3f + - description: Gen2 parameter. + minimum: 0 + maximum: 0x3f + - description: Gen3 parameter. + minimum: 0 + maximum: 0x3f + default: [0, 0, 0] + + "#phy-cells": + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + - "#phy-cells" + +additionalProperties: false + +examples: + - | + sata-phy@50440300 { + compatible = "eswin,eic7700-sata-phy"; + reg = <0x50440300 0x40>; + clocks = <&hspcrg 17>; + clock-names = "phy"; + resets = <&hspcrg 0>, <&hspcrg 1>; + reset-names = "port", "phy"; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra194-xusb-padctl.yaml b/Documentation/devicetree/bindings/phy/nvidia,tegra194-xusb-padctl.yaml index 6e3398399628..d8de900a4fce 100644 --- a/Documentation/devicetree/bindings/phy/nvidia,tegra194-xusb-padctl.yaml +++ b/Documentation/devicetree/bindings/phy/nvidia,tegra194-xusb-padctl.yaml @@ -230,6 +230,10 @@ properties: connector: type: object + port: + description: connection to a USB Type C controller + $ref: /schemas/graph.yaml#/properties/port + mode: description: A string that determines the mode in which to run the port. @@ -256,7 +260,12 @@ properties: voltage. dependencies: - usb-role-switch: [ connector ] + usb-role-switch: + oneOf: + - required: + - connector + - required: + - port usb2-1: type: object @@ -268,6 +277,10 @@ properties: connector: type: object + port: + description: connection to a USB Type C controller + $ref: /schemas/graph.yaml#/properties/port + mode: description: A string that determines the mode in which to run the port. @@ -306,6 +319,10 @@ properties: connector: type: object + port: + description: connection to a USB Type C controller + $ref: /schemas/graph.yaml#/properties/port + mode: description: A string that determines the mode in which to run the port. @@ -344,6 +361,10 @@ properties: connector: type: object + port: + description: connection to a USB Type C controller + $ref: /schemas/graph.yaml#/properties/port + mode: description: A string that determines the mode in which to run the port. @@ -405,6 +426,10 @@ properties: description: A phandle to the regulator supplying the VBUS voltage. + port: + description: connection to a USB Type C controller + $ref: /schemas/graph.yaml#/properties/port + usb3-1: type: object additionalProperties: false @@ -438,6 +463,10 @@ properties: description: A phandle to the regulator supplying the VBUS voltage. + port: + description: connection to a USB Type C controller + $ref: /schemas/graph.yaml#/properties/port + usb3-2: type: object additionalProperties: false @@ -471,6 +500,10 @@ properties: description: A phandle to the regulator supplying the VBUS voltage. + port: + description: connection to a USB Type C controller + $ref: /schemas/graph.yaml#/properties/port + usb3-3: type: object additionalProperties: false @@ -504,6 +537,10 @@ properties: description: A phandle to the regulator supplying the VBUS voltage. + port: + description: connection to a USB Type C controller + $ref: /schemas/graph.yaml#/properties/port + additionalProperties: false required: diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra20-usb-phy.yaml b/Documentation/devicetree/bindings/phy/nvidia,tegra20-usb-phy.yaml index d61585c96e31..a37e8322dc50 100644 --- a/Documentation/devicetree/bindings/phy/nvidia,tegra20-usb-phy.yaml +++ b/Documentation/devicetree/bindings/phy/nvidia,tegra20-usb-phy.yaml @@ -16,6 +16,7 @@ properties: oneOf: - items: - enum: + - nvidia,tegra210-usb-phy - nvidia,tegra124-usb-phy - nvidia,tegra114-usb-phy - enum: diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml index a1731b08c9d1..9616c736b6d4 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml @@ -16,6 +16,10 @@ description: properties: compatible: oneOf: + - items: + - enum: + - qcom,qcs8300-qmp-ufs-phy + - const: qcom,sa8775p-qmp-ufs-phy - items: - enum: - qcom,qcs615-qmp-ufs-phy @@ -26,8 +30,8 @@ properties: - const: qcom,sm8550-qmp-ufs-phy - items: - enum: - - qcom,qcs8300-qmp-ufs-phy - - const: qcom,sa8775p-qmp-ufs-phy + - qcom,eliza-qmp-ufs-phy + - const: qcom,sm8650-qmp-ufs-phy - items: - enum: - qcom,kaanapali-qmp-ufs-phy diff --git a/Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml index 665ec79a69f1..41073176bc69 100644 --- a/Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml @@ -18,7 +18,9 @@ properties: compatible: oneOf: - items: - - const: apple,t6020-pinctrl + - enum: + - apple,t6020-pinctrl + - apple,t8122-pinctrl - const: apple,t8103-pinctrl - items: # Do not add additional SoC to this list. diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx27-iomuxc.yaml b/Documentation/devicetree/bindings/pinctrl/fsl,imx27-iomuxc.yaml new file mode 100644 index 000000000000..1254bfcaa7cb --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/fsl,imx27-iomuxc.yaml @@ -0,0 +1,126 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/fsl,imx27-iomuxc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX1/i.MX25/i.MX27 IOMUX Controller + +maintainers: + - Frank Li + +description: + Please refer to fsl,imx-pinctrl.txt and pinctrl-bindings.txt in this directory + for common binding part and usage. + +properties: + compatible: + enum: + - fsl,imx1-iomuxc + - fsl,imx27-iomuxc + + reg: + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 1 + + ranges: true + +patternProperties: + '^gpio@[0-9a-f]+$': + type: object + $ref: /schemas/gpio/fsl-imx-gpio.yaml + unevaluatedProperties: false + + 'grp$': + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + + properties: + fsl,pins: + description: + three integers array, represents a group of pins mux and config + setting. The format is fsl,pins = . + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + items: + - description: + PIN is an integer between 0 and 0xbf. imx27 has 6 ports with 32 + configurable pins each. PIN is PORT * 32 + PORT_PIN, PORT_PIN + is the pin number on the specific port (between 0 and 31) + - description: | + MUX_ID is function + (direction << 2) + (gpio_oconf << 4) + + (gpio_iconfa << 8) + (gpio_iconfb << 10) + + function value is used to select the pin function. + Possible values: + 0 - Primary function + 1 - Alternate function + 2 - GPIO + Registers: GIUS (GPIO In Use), GPR (General Purpose Register) + + direction defines the data direction of the pin. + Possible values: + 0 - Input + 1 - Output + Register: DDIR + + gpio_oconf configures the gpio submodule output signal. + This does not have any effect unless GPIO function is + selected. A/B/C_IN are output signals of function blocks + A,B and C. Specific function blocks are described in the + reference manual. + Possible values: + 0 - A_IN + 1 - B_IN + 2 - C_IN + 3 - Data Register + Registers: OCR1, OCR2 + + gpio_iconfa/b configures the gpio submodule input to + functionblocks A and B. GPIO function should be selected if + this is configured. + Possible values: + 0 - GPIO_IN + 1 - Interrupt Status Register + 2 - Pulldown + 3 - Pullup + Registers ICONFA1, ICONFA2, ICONFB1 and ICONFB2 + + - description: + CONFIG can be 0 or 1, meaning Pullup disable/enable. + required: + - fsl,pins + + additionalProperties: false + +required: + - compatible + - reg + +allOf: + - $ref: pinctrl.yaml# + +unevaluatedProperties: false + +examples: + - | + pinmux@10015000 { + compatible = "fsl,imx27-iomuxc"; + reg = <0x10015000 0x600>; + + uartgrp { + fsl,pins = < + 0x8c 0x004 0x0 /* UART1_TXD__UART1_TXD */ + 0x8d 0x000 0x0 /* UART1_RXD__UART1_RXD */ + 0x8e 0x004 0x0 /* UART1_CTS__UART1_CTS */ + 0x8f 0x000 0x0 /* UART1_RTS__UART1_RTS */ + >; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx27-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,imx27-pinctrl.txt deleted file mode 100644 index d1706ea82572..000000000000 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx27-pinctrl.txt +++ /dev/null @@ -1,121 +0,0 @@ -* Freescale IMX27 IOMUX Controller - -Required properties: -- compatible: "fsl,imx27-iomuxc" - -The iomuxc driver node should define subnodes containing of pinctrl configuration subnodes. - -Required properties for pin configuration node: -- fsl,pins: three integers array, represents a group of pins mux and config - setting. The format is fsl,pins = . - - PIN is an integer between 0 and 0xbf. imx27 has 6 ports with 32 configurable - configurable pins each. PIN is PORT * 32 + PORT_PIN, PORT_PIN is the pin - number on the specific port (between 0 and 31). - - MUX_ID is - function + (direction << 2) + (gpio_oconf << 4) + (gpio_iconfa << 8) + (gpio_iconfb << 10) - - function value is used to select the pin function. - Possible values: - 0 - Primary function - 1 - Alternate function - 2 - GPIO - Registers: GIUS (GPIO In Use), GPR (General Purpose Register) - - direction defines the data direction of the pin. - Possible values: - 0 - Input - 1 - Output - Register: DDIR - - gpio_oconf configures the gpio submodule output signal. This does not - have any effect unless GPIO function is selected. A/B/C_IN are output - signals of function blocks A,B and C. Specific function blocks are - described in the reference manual. - Possible values: - 0 - A_IN - 1 - B_IN - 2 - C_IN - 3 - Data Register - Registers: OCR1, OCR2 - - gpio_iconfa/b configures the gpio submodule input to functionblocks A and - B. GPIO function should be selected if this is configured. - Possible values: - 0 - GPIO_IN - 1 - Interrupt Status Register - 2 - Pulldown - 3 - Pullup - Registers ICONFA1, ICONFA2, ICONFB1 and ICONFB2 - - CONFIG can be 0 or 1, meaning Pullup disable/enable. - - -The iomux controller has gpio child nodes which are embedded in the iomux -control registers. They have to be defined as child nodes of the iomux device -node. If gpio subnodes are defined "#address-cells", "#size-cells" and "ranges" -properties for the iomux device node are required. - -Example: - -iomuxc: iomuxc@10015000 { - compatible = "fsl,imx27-iomuxc"; - reg = <0x10015000 0x600>; - #address-cells = <1>; - #size-cells = <1>; - ranges; - - gpio1: gpio@10015000 { - ... - }; - - ... - - uart { - pinctrl_uart1: uart-1 { - fsl,pins = < - 0x8c 0x004 0x0 /* UART1_TXD__UART1_TXD */ - 0x8d 0x000 0x0 /* UART1_RXD__UART1_RXD */ - 0x8e 0x004 0x0 /* UART1_CTS__UART1_CTS */ - 0x8f 0x000 0x0 /* UART1_RTS__UART1_RTS */ - >; - }; - - ... - }; -}; - - -For convenience there are macros defined in imx27-pinfunc.h which provide PIN -and MUX_ID. They are structured as MX27_PAD___. The names -are defined in the i.MX27 reference manual. - -The above example using macros: - -iomuxc: iomuxc@10015000 { - compatible = "fsl,imx27-iomuxc"; - reg = <0x10015000 0x600>; - #address-cells = <1>; - #size-cells = <1>; - ranges; - - gpio1: gpio@10015000 { - ... - }; - - ... - - uart { - pinctrl_uart1: uart-1 { - fsl,pins = < - MX27_PAD_UART1_TXD__UART1_TXD 0x0 - MX27_PAD_UART1_RXD__UART1_RXD 0x0 - MX27_PAD_UART1_CTS__UART1_CTS 0x0 - MX27_PAD_UART1_RTS__UART1_RTS 0x0 - >; - }; - - ... - }; -}; diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx35-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/fsl,imx35-pinctrl.yaml index 265c43ab76f4..846e110062b2 100644 --- a/Documentation/devicetree/bindings/pinctrl/fsl,imx35-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/fsl,imx35-pinctrl.yaml @@ -20,6 +20,7 @@ properties: compatible: oneOf: - enum: + - fsl,imx25-iomuxc - fsl,imx35-iomuxc - fsl,imx51-iomuxc - fsl,imx53-iomuxc diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,armada3710-xb-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/marvell,armada3710-xb-pinctrl.yaml index 4f9013d36874..727da7fb490c 100644 --- a/Documentation/devicetree/bindings/pinctrl/marvell,armada3710-xb-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/marvell,armada3710-xb-pinctrl.yaml @@ -84,11 +84,12 @@ patternProperties: properties: groups: - enum: [ emmc_nb, i2c1, i2c2, jtag, mii_col, onewire, pcie1, - pcie1_clkreq, pcie1_wakeup, pmic0, pmic1, ptp, ptp_clk, - ptp_trig, pwm0, pwm1, pwm2, pwm3, rgmii, sdio0, sdio_sb, smi, - spi_cs1, spi_cs2, spi_cs3, spi_quad, uart1, uart2, - usb2_drvvbus1, usb32_drvvbus0 ] + items: + enum: [ emmc_nb, i2c1, i2c2, jtag, mii_col, onewire, pcie1, + pcie1_clkreq, pcie1_wakeup, pmic0, pmic1, ptp, ptp_clk, + ptp_trig, pwm0, pwm1, pwm2, pwm3, rgmii, sdio0, sdio_sb, + smi, spi_cs1, spi_cs2, spi_cs3, spi_quad, uart1, uart2, + usb2_drvvbus1, usb32_drvvbus0 ] function: enum: [ drvbus, emmc, gpio, i2c, jtag, led, mii, mii_err, onewire, diff --git a/Documentation/devicetree/bindings/pinctrl/maxim,max77620-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/maxim,max77620-pinctrl.yaml new file mode 100644 index 000000000000..b3ea36474317 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/maxim,max77620-pinctrl.yaml @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/maxim,max77620-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Pinmux controller function for Maxim MAX77620 Power management IC + +maintainers: + - Svyatoslav Ryhel + +description: + Device has 8 GPIO pins which can be configured as GPIO as well as the + special IO functions. + +allOf: + - $ref: /schemas/pinctrl/pincfg-node.yaml + - $ref: /schemas/pinctrl/pinmux-node.yaml + +patternProperties: + "^(pin|gpio).": + type: object + additionalProperties: false + + properties: + pins: + items: + enum: [ gpio0, gpio1, gpio2, gpio3, gpio4, gpio5, gpio6, gpio7 ] + + function: + items: + enum: [ gpio, lpm-control-in, fps-out, 32k-out1, sd0-dvs-in, sd1-dvs-in, + reference-out ] + + drive-push-pull: true + drive-open-drain: true + bias-pull-up: true + bias-pull-down: true + + maxim,active-fps-source: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + FPS source for the GPIOs to get enabled/disabled when system is in + active state. Valid values are: + - MAX77620_FPS_SRC_0: FPS source is FPS0. + - MAX77620_FPS_SRC_1: FPS source is FPS1 + - MAX77620_FPS_SRC_2: FPS source is FPS2 + - MAX77620_FPS_SRC_NONE: GPIO is not controlled by FPS events and + it gets enabled/disabled by register access. + Absence of this property will leave the FPS configuration register + for that GPIO to default configuration. + + maxim,active-fps-power-up-slot: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Sequencing event slot number on which the GPIO get enabled when + master FPS input event set to HIGH. This is applicable if FPS source + is selected as FPS0, FPS1 or FPS2. + enum: [0, 1, 2, 3, 4, 5, 6, 7] + + maxim,active-fps-power-down-slot: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Sequencing event slot number on which the GPIO get disabled when + master FPS input event set to LOW. This is applicable if FPS source + is selected as FPS0, FPS1 or FPS2. + enum: [0, 1, 2, 3, 4, 5, 6, 7] + + maxim,suspend-fps-source: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + This is same as property "maxim,active-fps-source" but value get + configured when system enters in to suspend state. + + maxim,suspend-fps-power-up-slot: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + This is same as property "maxim,active-fps-power-up-slot" but this + value get configured into FPS configuration register when system + enters into suspend. This is applicable if suspend state FPS source + is selected as FPS0, FPS1 or FPS2. + enum: [0, 1, 2, 3, 4, 5, 6, 7] + + maxim,suspend-fps-power-down-slot: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + This is same as property "maxim,active-fps-power-down-slot" but this + value get configured into FPS configuration register when system + enters into suspend. This is applicable if suspend state FPS source + is selected as FPS0, FPS1 or FPS2. + enum: [0, 1, 2, 3, 4, 5, 6, 7] + + required: + - pins + +additionalProperties: false + +# see maxim,max77620.yaml for an example diff --git a/Documentation/devicetree/bindings/pinctrl/pincfg-node.yaml b/Documentation/devicetree/bindings/pinctrl/pincfg-node.yaml index a916d0fc79a9..97dbce8a261f 100644 --- a/Documentation/devicetree/bindings/pinctrl/pincfg-node.yaml +++ b/Documentation/devicetree/bindings/pinctrl/pincfg-node.yaml @@ -162,12 +162,105 @@ properties: this affects the expected delay in ps before latching a value to an output pin. -if: - required: - - skew-delay -then: - properties: - skew-delay-input-ps: false - skew-delay-output-ps: false + input-threshold-voltage-microvolt: + description: Specifies the input voltage level of the pin in microvolts. + This defines the reference for VIH (Input High Voltage) and VIL + (Input Low Voltage) thresholds for proper signal detection. + +allOf: + - if: + required: + - skew-delay + then: + properties: + skew-delay-input-ps: false + skew-delay-output-ps: false + + - if: + required: + - input-disable + then: + properties: + input-enable: false + input-threshold-voltage-microvolt: false + + - if: + required: + - output-disable + then: + properties: + output-enable: false + output-impedance-ohms: false + + - if: + required: + - output-low + then: + properties: + output-high: false + + - if: + required: + - low-power-enable + then: + properties: + low-power-disable: false + + - if: + required: + - input-schmitt-disable + then: + properties: + input-schmitt-enable: false + input-schmitt-microvolt: false + + - if: + required: + - drive-strength + then: + properties: + drive-strength-microamp: false + + - if: + anyOf: + - required: + - drive-open-source + - required: + - drive-open-drain + - required: + - drive-push-pull + then: + oneOf: + - required: + - drive-open-source + - required: + - drive-open-drain + - required: + - drive-push-pull + + - if: + anyOf: + - required: + - bias-disable + - required: + - bias-bus-hold + - required: + - bias-pull-up + - required: + - bias-pull-down + - required: + - bias-pull-pin-default + then: + oneOf: + - required: + - bias-disable + - required: + - bias-bus-hold + - required: + - bias-pull-up + - required: + - bias-pull-down + - required: + - bias-pull-pin-default additionalProperties: true diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-max77620.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-max77620.txt deleted file mode 100644 index 28fbca180068..000000000000 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-max77620.txt +++ /dev/null @@ -1,127 +0,0 @@ -Pincontrol driver for MAX77620 Power management IC from Maxim Semiconductor. - -Device has 8 GPIO pins which can be configured as GPIO as well as the -special IO functions. - -Please refer file -for details of the common pinctrl bindings used by client devices, -including the meaning of the phrase "pin configuration node". - -Optional Pinmux properties: --------------------------- -Following properties are required if default setting of pins are required -at boot. -- pinctrl-names: A pinctrl state named per . -- pinctrl[0...n]: Properties to contain the phandle for pinctrl states per - . - -The pin configurations are defined as child of the pinctrl states node. Each -sub-node have following properties: - -Required properties: ------------------- -- pins: List of pins. Valid values of pins properties are: - gpio0, gpio1, gpio2, gpio3, gpio4, gpio5, gpio6, gpio7. - -Optional properties: -------------------- -Following are optional properties defined as pinmux DT binding document -. Absence of properties will leave the configuration -on default. - function, - drive-push-pull, - drive-open-drain, - bias-pull-up, - bias-pull-down. - -Valid values for function properties are: - gpio, lpm-control-in, fps-out, 32k-out, sd0-dvs-in, sd1-dvs-in, - reference-out - -There are also customised properties for the GPIO1, GPIO2 and GPIO3. These -customised properties are required to configure FPS configuration parameters -of these GPIOs. Please refer for more -detail of Flexible Power Sequence (FPS). - -- maxim,active-fps-source: FPS source for the GPIOs to get - enabled/disabled when system is in - active state. Valid values are: - - MAX77620_FPS_SRC_0, - FPS source is FPS0. - - MAX77620_FPS_SRC_1, - FPS source is FPS1 - - MAX77620_FPS_SRC_2 and - FPS source is FPS2 - - MAX77620_FPS_SRC_NONE. - GPIO is not controlled - by FPS events and it gets - enabled/disabled by register - access. - Absence of this property will leave - the FPS configuration register for that - GPIO to default configuration. - -- maxim,active-fps-power-up-slot: Sequencing event slot number on which - the GPIO get enabled when - master FPS input event set to HIGH. - Valid values are 0 to 7. - This is applicable if FPS source is - selected as FPS0, FPS1 or FPS2. - -- maxim,active-fps-power-down-slot: Sequencing event slot number on which - the GPIO get disabled when master - FPS input event set to LOW. - Valid values are 0 to 7. - This is applicable if FPS source is - selected as FPS0, FPS1 or FPS2. - -- maxim,suspend-fps-source: This is same as property - "maxim,active-fps-source" but value - get configured when system enters in - to suspend state. - -- maxim,suspend-fps-power-up-slot: This is same as property - "maxim,active-fps-power-up-slot" but - this value get configured into FPS - configuration register when system - enters into suspend. - This is applicable if suspend state - FPS source is selected as FPS0, FPS1 or - -- maxim,suspend-fps-power-down-slot: This is same as property - "maxim,active-fps-power-down-slot" but - this value get configured into FPS - configuration register when system - enters into suspend. - This is applicable if suspend state - FPS source is selected as FPS0, FPS1 or - FPS2. - -Example: --------- -#include -... -max77620@3c { - - pinctrl-names = "default"; - pinctrl-0 = <&spmic_default>; - - spmic_default: pinmux@0 { - pin_gpio0 { - pins = "gpio0"; - function = "gpio"; - }; - - pin_gpio1 { - pins = "gpio1"; - function = "fps-out"; - maxim,active-fps-source = ; - }; - - pin_gpio2 { - pins = "gpio2"; - function = "fps-out"; - maxim,active-fps-source = ; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.yaml b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.yaml index 9135788cf62e..afe7329a1df2 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.yaml +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.yaml @@ -38,6 +38,10 @@ properties: - enum: - marvell,pxa1908-padconf - const: pinconf-single + - items: + - enum: + - brcm,bcm7038-padconf + - const: pinctrl-single reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml new file mode 100644 index 000000000000..282650426487 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,eliza-tlmm.yaml @@ -0,0 +1,138 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,eliza-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. Eliza TLMM block + +maintainers: + - Abel Vesa + +description: + Top Level Mode Multiplexer pin controller in Qualcomm Eliza SoC. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,eliza-tlmm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-reserved-ranges: + minItems: 1 + maxItems: 93 + + gpio-line-names: + maxItems: 185 + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-eliza-tlmm-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-eliza-tlmm-state" + additionalProperties: false + +$defs: + qcom-eliza-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + oneOf: + - pattern: "^gpio([0-9]|[1-9][0-9]|1[0-7][0-9]|18[0-4])$" + - enum: [ ufs_reset ] + minItems: 1 + maxItems: 36 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + enum: [ gpio, aoss_cti, atest_char, atest_usb, audio_ext_mclk0, + audio_ref_clk, cam_mclk, cci_async_in, cci_i2c_scl, + cci_i2c_sda, cci_timer, coex_uart1_rx, coex_uart1_tx, + coex_uart2_rx, coex_uart2_tx, dbg_out_clk, + ddr_bist_complete, ddr_bist_fail, ddr_bist_start, + ddr_bist_stop, ddr_pxi0, ddr_pxi1, dp0_hot, egpio, + gcc_gp1, gcc_gp2, gcc_gp3, gnss_adc0, gnss_adc1, + hdmi_ddc_scl, hdmi_ddc_sda, hdmi_dtest0, hdmi_dtest1, + hdmi_hot_plug, hdmi_pixel_clk, hdmi_rcv_det, hdmi_tx_cec, + host2wlan_sol, i2s0_data0, i2s0_data1, i2s0_sck, i2s0_ws, + ibi_i3c, jitter_bist, mdp_esync0_out, mdp_esync1_out, + mdp_vsync, mdp_vsync0_out, mdp_vsync11_out, + mdp_vsync1_out, mdp_vsync2_out, mdp_vsync3_out, + mdp_vsync_e, nav_gpio0, nav_gpio1, nav_gpio2, nav_gpio3, + pcie0_clk_req_n, pcie1_clk_req_n, phase_flag, + pll_bist_sync, pll_clk_aux, prng_rosc0, prng_rosc1, + prng_rosc2, prng_rosc3, qdss_cti, qdss_gpio_traceclk, + qdss_gpio_tracectl, qdss_gpio_tracedata, qlink_big_enable, + qlink_big_request, qlink_little_enable, + qlink_little_request, qlink_wmss, qspi0, qspi_clk, + qspi_cs, qup1_se0, qup1_se1, qup1_se2, qup1_se3, qup1_se4, + qup1_se5, qup1_se6, qup1_se7, qup2_se0, qup2_se1, + qup2_se2, qup2_se3, qup2_se4, qup2_se5, qup2_se6, + qup2_se7, resout_gpio, sd_write_protect, sdc1, sdc2, + sdc2_fb_clk, tb_trig_sdc1, tb_trig_sdc2, tmess_prng0, + tmess_prng1, tmess_prng2, tmess_prng3, tsense_pwm1, + tsense_pwm2, tsense_pwm3, tsense_pwm4, uim0_clk, + uim0_data, uim0_present, uim0_reset, uim1_clk, uim1_data, + uim1_present, uim1_reset, usb0_hs, usb_phy, vfr_0, vfr_1, + vsense_trigger_mirnat, wcn_sw_ctrl ] + required: + - pins + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + tlmm: pinctrl@f100000 { + compatible = "qcom,eliza-tlmm"; + reg = <0x0f100000 0x300000>; + + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + gpio-ranges = <&tlmm 0 0 186>; + + gpio-wo-state { + pins = "gpio1"; + function = "gpio"; + }; + + qup-uart14-default-state { + pins = "gpio18", "gpio19"; + function = "qup2_se5"; + drive-strength = <2>; + bias-disable; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,hawi-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,hawi-tlmm.yaml new file mode 100644 index 000000000000..3b3961789860 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,hawi-tlmm.yaml @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,hawi-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. Hawi TLMM block + +maintainers: + - Mukesh Ojha + +description: + Top Level Mode Multiplexer pin controller in Qualcomm Hawi SoC. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,hawi-tlmm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-reserved-ranges: + minItems: 1 + maxItems: 113 + + gpio-line-names: + maxItems: 226 + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-hawi-tlmm-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-hawi-tlmm-state" + additionalProperties: false + +$defs: + qcom-hawi-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + oneOf: + - pattern: "^gpio([0-9]|[1-9][0-9]|1[0-9][0-9]|20[0-9]|21[0-9]|22[0-5])$" + - enum: [ ufs_reset, sdc2_clk, sdc2_cmd, sdc2_data ] + minItems: 1 + maxItems: 36 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + enum: [ gpio, aoss_cti, atest_char, atest_usb, audio_ext_mclk, + audio_ref_clk, cam_mclk, cci_async_in, cci_i2c0, cci_i2c1, + cci_i2c2, cci_i2c3, cci_i2c4, cci_i2c5, cci_timer, coex_espmi, + coex_uart1_rx, coex_uart1_tx, dbg_out_clk, ddr_bist, ddr_pxi, + dp_hot, egpio, gcc_gp, gnss_adc, host_rst, i2chub0_se0, + i2chub0_se1, i2chub0_se2, i2chub0_se3, i2chub0_se4, i2s0, i2s1, + ibi_i3c, jitter_bist, mdp_esync0, mdp_esync1, mdp_esync2, + mdp_vsync, mdp_vsync_e, mdp_vsync_p, mdp_vsync0_out, + mdp_vsync1_out, mdp_vsync2_out, mdp_vsync3_out, mdp_vsync5_out, + modem_pps_in, modem_pps_out, nav_gpio, nav_gpio0, nav_gpio3, + nav_rffe, pcie0_clk_req_n, pcie0_rst_n, pcie1_clk_req_n, + phase_flag, pll_bist_sync, pll_clk_aux, qdss_cti, qlink, + qspi, qspi_clk, qspi_cs, qup1_se0, qup1_se1, qup1_se2, + qup1_se3, qup1_se4, qup1_se5, qup1_se6, qup1_se7, qup2_se0, + qup2_se1, qup2_se2, qup2_se3, qup2_se4_01, qup2_se4_23, + qup3_se0_01, qup3_se0_23, qup3_se1, qup3_se2, qup3_se3, + qup3_se4, qup3_se5, qup4_se0, qup4_se1, qup4_se2, qup4_se3_01, + qup4_se3_23, qup4_se3_l3, qup4_se4_01, qup4_se4_23, qup4_se4_l3, + rng_rosc, sd_write_protect, sdc4_clk, sdc4_cmd, sdc4_data, + sys_throttle, tb_trig_sdc, tmess_rng, tsense_clm, tsense_pwm, + uim0, uim1, usb0_hs, usb_phy, vfr, vsense_trigger_mirnat, + wcn_sw_ctrl ] + + required: + - pins + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + tlmm: pinctrl@f100000 { + compatible = "qcom,hawi-tlmm"; + reg = <0x0f100000 0x300000>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&tlmm 0 0 227>; + interrupt-controller; + #interrupt-cells = <2>; + + qup-uart7-state { + pins = "gpio62", "gpio63"; + function = "qup1_se7"; + }; + }; +... diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq5210-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,ipq5210-tlmm.yaml new file mode 100644 index 000000000000..12c5e76235a3 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq5210-tlmm.yaml @@ -0,0 +1,123 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,ipq5210-tlmm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm IPQ5210 TLMM pin controller + +maintainers: + - Bjorn Andersson + - Kathiravan Thirumoorthy + +description: + Top Level Mode Multiplexer pin controller in Qualcomm IPQ5210 SoC. + +allOf: + - $ref: /schemas/pinctrl/qcom,tlmm-common.yaml# + +properties: + compatible: + const: qcom,ipq5210-tlmm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-reserved-ranges: + minItems: 1 + maxItems: 27 + + gpio-line-names: + maxItems: 54 + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-ipq5210-tlmm-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-ipq5210-tlmm-state" + additionalProperties: false + +$defs: + qcom-ipq5210-tlmm-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,tlmm-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + pattern: "^gpio([0-9]|[1-4][0-9]|5[0-3])$" + minItems: 1 + maxItems: 36 + + function: + description: + Specify the alternative function to be configured for the specified + pins. + + enum: [ atest_char_start, atest_char_status0, atest_char_status1, + atest_char_status2, atest_char_status3, atest_tic_en, audio_pri, + audio_pri_mclk_out0, audio_pri_mclk_in0, audio_pri_mclk_out1, + audio_pri_mclk_in1, audio_pri_mclk_out2, audio_pri_mclk_in2, + audio_pri_mclk_out3, audio_pri_mclk_in3, audio_sec, + audio_sec_mclk_out0, audio_sec_mclk_in0, audio_sec_mclk_out1, + audio_sec_mclk_in1, audio_sec_mclk_out2, audio_sec_mclk_in2, + audio_sec_mclk_out3, audio_sec_mclk_in3, core_voltage_0, + cri_trng0, cri_trng1, cri_trng2, cri_trng3, dbg_out_clk, dg_out, + gcc_plltest_bypassnl, gcc_plltest_resetn, gcc_tlmm, gpio, led0, + led1, led2, mdc_mst, mdc_slv0, mdc_slv1, mdc_slv2, mdio_mst, + mdio_slv0, mdio_slv1, mdio_slv2, mux_tod_out, pcie0_clk_req_n, + pcie0_wake, pcie1_clk_req_n, pcie1_wake, pll_test, + pon_active_led, pon_mux_sel, pon_rx, pon_rx_los, pon_tx, + pon_tx_burst, pon_tx_dis, pon_tx_fault, pon_tx_sd, gpn_rx_los, + gpn_tx_burst, gpn_tx_dis, gpn_tx_fault, gpn_tx_sd, pps, pwm0, + pwm1, pwm2, pwm3, qdss_cti_trig_in_a0, qdss_cti_trig_in_a1, + qdss_cti_trig_in_b0, qdss_cti_trig_in_b1, qdss_cti_trig_out_a0, + qdss_cti_trig_out_a1, qdss_cti_trig_out_b0, + qdss_cti_trig_out_b1, qdss_traceclk_a, qdss_tracectl_a, + qdss_tracedata_a, qrng_rosc0, qrng_rosc1, qrng_rosc2, + qspi_data, qspi_clk, qspi_cs_n, qup_se0, qup_se1, qup_se2, + qup_se3, qup_se4, qup_se5, qup_se5_l1, resout, rx_los0, rx_los1, + rx_los2, sdc_clk, sdc_cmd, sdc_data, tsens_max ] + + required: + - pins + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + tlmm: pinctrl@1000000 { + compatible = "qcom,ipq5210-tlmm"; + reg = <0x01000000 0x300000>; + gpio-controller; + #gpio-cells = <0x2>; + gpio-ranges = <&tlmm 0 0 54>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <0x2>; + + qup-uart1-default-state { + pins = "gpio38", "gpio39"; + function = "qup_se1"; + drive-strength = <6>; + bias-pull-down; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-common.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-common.yaml index 619341dd637c..30f93b8159fd 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-common.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-common.yaml @@ -27,6 +27,14 @@ properties: gpio-ranges: maxItems: 1 + gpio-reserved-ranges: + minItems: 1 + maxItems: 30 + description: + Pins can be reserved for trusted applications or for LPASS, thereby + inaccessible from the OS. This property can be used to mark the pins + which resources should not be accessed by the OS. + required: - gpio-controller - "#gpio-cells" diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,milos-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,milos-lpass-lpi-pinctrl.yaml new file mode 100644 index 000000000000..73e84f188591 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,milos-lpass-lpi-pinctrl.yaml @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,milos-lpass-lpi-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Milos SoC LPASS LPI TLMM + +maintainers: + - Luca Weiss + +description: + Top Level Mode Multiplexer pin controller in the Low Power Audio SubSystem + (LPASS) Low Power Island (LPI) of Qualcomm Milos SoC. + +properties: + compatible: + const: qcom,milos-lpass-lpi-pinctrl + + reg: + items: + - description: LPASS LPI TLMM Control and Status registers + - description: LPASS LPI MCC registers + + clocks: + items: + - description: LPASS Core voting clock + - description: LPASS Audio voting clock + + clock-names: + items: + - const: core + - const: audio + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-milos-lpass-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-milos-lpass-state" + additionalProperties: false + +$defs: + qcom-milos-lpass-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,lpass-lpi-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + pattern: "^gpio([0-9]|1[0-9]|2[0-2])$" + + function: + enum: [ dmic1_clk, dmic1_data, dmic2_clk, dmic2_data, dmic3_clk, + dmic3_data, dmic4_clk, dmic4_data, ext_mclk1_a, ext_mclk1_b, + ext_mclk1_c, ext_mclk1_d, ext_mclk1_e, gpio, i2s0_clk, + i2s0_data, i2s0_ws, i2s1_clk, i2s1_data, i2s1_ws, i2s2_clk, + i2s2_data, i2s2_ws, i2s3_clk, i2s3_data, i2s3_ws, qca_swr_clk, + qca_swr_data, slimbus_clk, slimbus_data, swr_rx_clk, + swr_rx_data, swr_tx_clk, swr_tx_data, wsa_swr_clk, + wsa_swr_data ] + description: + Specify the alternative function to be configured for the specified + pins. + +allOf: + - $ref: qcom,lpass-lpi-common.yaml# + +required: + - compatible + - reg + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + + pinctrl@3440000 { + compatible = "qcom,milos-lpass-lpi-pinctrl"; + reg = <0x03440000 0x20000>, + <0x034d0000 0x10000>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&lpass_tlmm 0 0 23>; + + clocks = <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "core", + "audio"; + + tx-swr-active-clk-state { + pins = "gpio0"; + function = "swr_tx_clk"; + drive-strength = <4>; + slew-rate = <1>; + bias-disable; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sdm670-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sdm670-lpass-lpi-pinctrl.yaml new file mode 100644 index 000000000000..c76ad70e6b9f --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sdm670-lpass-lpi-pinctrl.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/qcom,sdm670-lpass-lpi-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SDM670 SoC LPASS LPI TLMM + +maintainers: + - Richard Acayan + +description: + Top Level Mode Multiplexer pin controller in the Low Power Audio SubSystem + (LPASS) Low Power Island (LPI) of Qualcomm SDM670 SoC. + +properties: + compatible: + const: qcom,sdm670-lpass-lpi-pinctrl + + reg: + items: + - description: LPASS LPI TLMM Control and Status registers + +patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/qcom-sdm670-lpass-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/qcom-sdm670-lpass-state" + additionalProperties: false + +$defs: + qcom-sdm670-lpass-state: + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: qcom,lpass-lpi-common.yaml#/$defs/qcom-tlmm-state + unevaluatedProperties: false + + properties: + pins: + description: + List of gpio pins affected by the properties specified in this + subnode. + items: + pattern: "^gpio([0-9]|1[0-9]|2[0-9]|3[0-1])$" + + function: + enum: [ gpio, comp_rx, dmic1_clk, dmic1_data, dmic2_clk, dmic2_data, + i2s1_clk, i2s_data, i2s_ws, lpi_cdc_rst, mclk0, pdm_rx, + pdm_sync, pdm_tx, slimbus_clk ] + description: + Specify the alternative function to be configured for the specified + pins. + +allOf: + - $ref: qcom,lpass-lpi-common.yaml# + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + lpi_tlmm: pinctrl@62b40000 { + compatible = "qcom,sdm670-lpass-lpi-pinctrl"; + reg = <0x62b40000 0x20000>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&lpi_tlmm 0 0 32>; + + cdc_comp_default: cdc-comp-default-state { + pins = "gpio22", "gpio24"; + function = "comp_rx"; + drive-strength = <4>; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm8450-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm8450-lpass-lpi-pinctrl.yaml index e7565592da86..541c1c54ddb0 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,sm8450-lpass-lpi-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm8450-lpass-lpi-pinctrl.yaml @@ -15,7 +15,13 @@ description: properties: compatible: - const: qcom,sm8450-lpass-lpi-pinctrl + oneOf: + - const: qcom,sm8450-lpass-lpi-pinctrl + - items: + - enum: + - qcom,qcs8300-lpass-lpi-pinctrl + - qcom,sa8775p-lpass-lpi-pinctrl + - const: qcom,sm8450-lpass-lpi-pinctrl reg: items: diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml index 74df912e60ad..1bf08860a4ba 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml @@ -19,7 +19,9 @@ properties: oneOf: - const: qcom,sm8650-lpass-lpi-pinctrl - items: - - const: qcom,sm8750-lpass-lpi-pinctrl + - enum: + - qcom,glymur-lpass-lpi-pinctrl + - qcom,sm8750-lpass-lpi-pinctrl - const: qcom,sm8650-lpass-lpi-pinctrl reg: diff --git a/Documentation/devicetree/bindings/pinctrl/realtek,rtd1315e-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/realtek,rtd1315e-pinctrl.yaml index 90bd49d87d2e..2a640e495cc7 100644 --- a/Documentation/devicetree/bindings/pinctrl/realtek,rtd1315e-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/realtek,rtd1315e-pinctrl.yaml @@ -135,8 +135,11 @@ patternProperties: realtek,duty-cycle: description: | - An integer describing the level to adjust output duty cycle, controlling - the proportion of positive and negative waveforms in nanoseconds. + An integer describing the level to adjust the output pulse width, it + provides a fixed nanosecond-level adjustment to the rising/falling + edges of an existing signal. It is used for Signal Integrity tuning + (adding/subtracting delay to fine-tune the high/low duration), rather + than generating a specific PWM frequency. Valid arguments are described as below: 0: 0ns 2: + 0.25ns diff --git a/Documentation/devicetree/bindings/pinctrl/realtek,rtd1319d-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/realtek,rtd1319d-pinctrl.yaml index b6211c8544ca..2136546adec8 100644 --- a/Documentation/devicetree/bindings/pinctrl/realtek,rtd1319d-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/realtek,rtd1319d-pinctrl.yaml @@ -134,8 +134,11 @@ patternProperties: realtek,duty-cycle: description: | - An integer describing the level to adjust output duty cycle, controlling - the proportion of positive and negative waveforms in nanoseconds. + An integer describing the level to adjust the output pulse width, it + provides a fixed nanosecond-level adjustment to the rising/falling + edges of an existing signal. It is used for Signal Integrity tuning + (adding/subtracting delay to fine-tune the high/low duration), rather + than generating a specific PWM frequency. Valid arguments are described as below: 0: 0ns 2: + 0.25ns diff --git a/Documentation/devicetree/bindings/pinctrl/realtek,rtd1619b-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/realtek,rtd1619b-pinctrl.yaml index e88bc649cc73..e8ea1362b16d 100644 --- a/Documentation/devicetree/bindings/pinctrl/realtek,rtd1619b-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/realtek,rtd1619b-pinctrl.yaml @@ -133,8 +133,11 @@ patternProperties: realtek,duty-cycle: description: | - An integer describing the level to adjust output duty cycle, controlling - the proportion of positive and negative waveforms in nanoseconds. + An integer describing the level to adjust the output pulse width, it + provides a fixed nanosecond-level adjustment to the rising/falling + edges of an existing signal. It is used for Signal Integrity tuning + (adding/subtracting delay to fine-tune the high/low duration), rather + than generating a specific PWM frequency. Valid arguments are described as below: 0: 0ns 2: + 0.25ns diff --git a/Documentation/devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml new file mode 100644 index 000000000000..9562a043707e --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/realtek,rtd1625-pinctrl.yaml @@ -0,0 +1,260 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2025 Realtek Semiconductor Corporation +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/realtek,rtd1625-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Realtek DHC RTD1625 Pin Controller + +maintainers: + - Tzuyi Chang + - Yu-Chun Lin + +description: + The Realtek DHC RTD1625 is a high-definition media processor SoC. The + RTD1625 pin controller is used to control pin function, pull-up/down + resistors, drive strength, slew rate, Schmitt trigger, power source + (I/O output voltage), input threshold domain selection and a higher-VIL mode. + +properties: + compatible: + items: + - enum: + - realtek,rtd1625-iso-pinctrl + - realtek,rtd1625-main2-pinctrl + - realtek,rtd1625-isom-pinctrl + - realtek,rtd1625-ve4-pinctrl + + reg: + maxItems: 1 + +patternProperties: + '-pins$': + type: object + allOf: + - $ref: pincfg-node.yaml# + - $ref: pinmux-node.yaml# + + properties: + pins: + items: + enum: [gpio_0, gpio_1, gpio_2, gpio_3, gpio_4, gpio_5, gpio_6, + gpio_7, gpio_8, gpio_9, gpio_10, gpio_11, gpio_12, gpio_13, + gpio_14, gpio_15, gpio_16, gpio_17, gpio_18, gpio_19, gpio_20, + gpio_21, gpio_22, gpio_23, gpio_24, gpio_25, gpio_28, gpio_29, + gpio_30, gpio_31, gpio_32, gpio_33, gpio_34, gpio_35, gpio_40, + gpio_41, gpio_42, gpio_43, gpio_44, gpio_45, gpio_46, gpio_47, + gpio_48, gpio_49, gpio_50, gpio_51, gpio_52, gpio_53, gpio_54, + gpio_55, gpio_56, gpio_57, gpio_58, gpio_59, gpio_60, gpio_61, + gpio_62, gpio_63, gpio_64, gpio_65, gpio_66, gpio_67, gpio_80, + gpio_81, gpio_82, gpio_83, gpio_84, gpio_85, gpio_86, gpio_87, + gpio_88, gpio_89, gpio_90, gpio_91, gpio_92, gpio_93, gpio_94, + gpio_95, gpio_96, gpio_97, gpio_98, gpio_99, gpio_100, + gpio_101, gpio_102, gpio_103, gpio_104, gpio_105, gpio_106, + gpio_107, gpio_108, gpio_109, gpio_110, gpio_111, gpio_112, + gpio_128, gpio_129, gpio_130, gpio_131, gpio_132, gpio_133, + gpio_134, gpio_135, gpio_136, gpio_137, gpio_138, gpio_139, + gpio_140, gpio_141, gpio_142, gpio_143, gpio_144, gpio_145, + gpio_146, gpio_147, gpio_148, gpio_149, gpio_150, gpio_151, + gpio_152, gpio_153, gpio_154, gpio_155, gpio_156, gpio_157, + gpio_158, gpio_159, gpio_160, gpio_161, gpio_162, gpio_163, + gpio_164, gpio_165, ai_i2s1_loc, ao_i2s1_loc, arm_trace_dbg_en, + csi_vdsel, ejtag_acpu_loc, ejtag_aucpu0_loc, ejtag_aucpu1_loc, + ejtag_pcpu_loc, ejtag_scpu_loc, ejtag_ve2_loc, emmc_clk, + emmc_cmd, emmc_data_0, emmc_data_1, emmc_data_2, emmc_data_3, + emmc_data_4, emmc_data_5, emmc_data_6, emmc_data_7, + emmc_dd_sb, emmc_rst_n, etn_phy_loc, hif_clk, hif_data, + hif_en, hif_rdy, hi_width, i2c6_loc, ir_rx_loc, rgmii_vdsel, + sf_en, spdif_in_mode, spdif_loc, uart0_loc, usb_cc1, usb_cc2, + ve4_uart_loc] + + function: + enum: [gpio, ai_i2s0, ai_i2s2, ai_tdm0, ai_tdm1, ai_tdm2, ao_i2s0, + ao_i2s2, ao_tdm0, ao_tdm1, ao_tdm2, csi0, csi1, csi_1v2, csi_1v8, + csi_2v5, csi_3v3, dmic0, dmic1, dmic2, dptx_hpd, edptx_hdp, emmc, + gspi0, gspi1, gspi2, hi_width_1bit, hi_width_disable, i2c0, i2c1, + i2c3, i2c4, i2c5, i2c7, iso_tristate, pcie0, pcie1, pcm, pctrl, + pwm4, pwm5, pwm6, rgmii, rgmii_1v2, rgmii_1v8, rgmii_2v5, + rgmii_3v3, rmii, sd, sdio, sf_disable, sf_enable, + spdif_in_coaxial, spdif_in_gpio, spdif_out, spi, ts0, ts1, uart1, + uart2, uart3, uart4, uart5, uart6, uart7, uart8, uart9, uart10, + usb_cc1, usb_cc2, vi0_dtv, vi1_dtv, vtc_ao_i2s, vtc_dmic, + vtc_i2s, ai_i2s1_loc0, ai_i2s1_loc1, ao_i2s0_loc0, ao_i2s0_loc1, + ao_i2s1_loc0, ao_i2s1_loc1, ao_tdm1_loc0, ao_tdm1_loc1, + etn_led_loc0, etn_led_loc1, etn_phy_loc0, etn_phy_loc1, + i2c6_loc0, i2c6_loc1, ir_rx_loc0, ir_rx_loc1, pwm0_loc0, + pwm0_loc1, pwm0_loc2, pwm0_loc3, pwm1_loc0, pwm1_loc1, pwm2_loc0, + pwm2_loc1, pwm3_loc0, pwm3_loc1, spdif_loc0, spdif_loc1, + uart0_loc0, uart0_loc1, ve4_uart_loc0, ve4_uart_loc1, + ve4_uart_loc2, acpu_ejtag_loc0, acpu_ejtag_loc1, acpu_ejtag_loc2, + aucpu0_ejtag_loc0, aucpu0_ejtag_loc1, aucpu0_ejtag_loc2, + aucpu1_ejtag_loc0, aucpu1_ejtag_loc1, aucpu1_ejtag_loc2, + aupu0_ejtag_loc1, aupu1_ejtag_loc1, gpu_ejtag_loc0, + pcpu_ejtag_loc0, pcpu_ejtag_loc1, pcpu_ejtag_loc2, + scpu_ejtag_loc0, scpu_ejtag_loc1, scpu_ejtag_loc2, + ve2_ejtag_loc0, ve2_ejtag_loc1, ve2_ejtag_loc2, pll_test_loc0, + pll_test_loc1, dbg_out1, isom_dbg_out, arm_trace_debug_disable, + arm_trace_debug_enable] + + drive-strength: + enum: [4, 8] + + bias-pull-down: true + + bias-pull-up: true + + bias-disable: true + + input-schmitt-enable: true + + input-schmitt-disable: true + + input-voltage-microvolt: + description: | + Select the input receiver voltage domain for the pin. + Valid arguments are: + - 1800000: 1.8V input logic level + - 3300000: 3.3V input logic level + enum: [1800000, 3300000] + + drive-push-pull: true + + power-source: + description: | + Valid arguments are described as below: + 0: power supply of 1.8V + 1: power supply of 3.3V + enum: [0, 1] + + slew-rate: + description: | + Valid arguments are described as below: + 1: ~1ns falling time + 10: ~10ns falling time + 20: ~20ns falling time + 30: ~30ns falling time + enum: [1, 10, 20, 30] + + realtek,drive-strength-p: + description: | + Some of pins can be driven using the P-MOS and N-MOS transistor to + achieve finer adjustments. The block-diagram representation is as + follows: + VDD + | + ||--+ + +-----o|| P-MOS-FET + | ||--+ + IN --+ +----- out + | ||--+ + +------|| N-MOS-FET + ||--+ + | + GND + The driving strength of the P-MOS/N-MOS transistors impacts the + waveform's rise/fall times. Greater driving strength results in + shorter rise/fall times. Each P-MOS and N-MOS transistor offers + 8 configurable levels (0 to 7), with higher values indicating + greater driving strength, contributing to achieving the desired + speed. + + The realtek,drive-strength-p is used to control the driving strength + of the P-MOS output. + + This value is not a simple count of transistors. Instead, it + represents a weighted configuration. There is a base driving + capability (even at value 0), and each bit adds a different weight to + the total strength. The resulting current is non-linear and varies + significantly based on the IO voltage (1.8V vs 3.3V) and the specific + pad group. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + realtek,drive-strength-n: + description: | + Similar to the realtek,drive-strength-p, the realtek,drive-strength-n + is used to control the driving strength of the N-MOS output. + + This property uses the same weighted configuration logic where values + 0-7 represent non-linear strength adjustments rather than a transistor + count. + + Higher values indicate greater driving strength, resulting in shorter + fall times. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + realtek,duty-cycle: + description: | + An integer describing the level to adjust the output pulse width, it + provides a fixed nanosecond-level adjustment to the rising/falling + edges of an existing signal. It is used for Signal Integrity tuning + (adding/subtracting delay to fine-tune the high/low duration), rather + than generating a specific PWM frequency. + + Valid arguments are described as below: + 0: 0ns + 2: + 0.25ns + 3: + 0.5ns + 4: -0.25ns + 5: -0.5ns + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 2, 3, 4, 5] + + realtek,high-vil-microvolt: + description: | + The threshold value for the input receiver's LOW recognition (VIL). + + This property is used to address specific HDMI I2C compatibility + issues where some sinks (TVs) have weak pull-down capabilities and + fail to pull the bus voltage below the standard VIL threshold + (~0.7V). + + Setting this property to 1100000 (1.1V) enables a specialized input + receiver mode that raises the effective VIL threshold to improve + detection. + enum: [1100000] + + required: + - pins + + additionalProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + pinctrl@4e000 { + compatible = "realtek,rtd1625-iso-pinctrl"; + reg = <0x4e000 0x130>; + + emmc-hs200-pins { + pins = "emmc_clk", + "emmc_cmd", + "emmc_data_0", + "emmc_data_1", + "emmc_data_2", + "emmc_data_3", + "emmc_data_4", + "emmc_data_5", + "emmc_data_6", + "emmc_data_7"; + function = "emmc"; + realtek,drive-strength-p = <2>; + realtek,drive-strength-n = <2>; + }; + + i2c-0-pins { + pins = "gpio_12", + "gpio_13"; + function = "i2c0"; + drive-strength = <4>; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/renesas,r9a09g077-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/renesas,r9a09g077-pinctrl.yaml index f049013a4e0c..63993b20524f 100644 --- a/Documentation/devicetree/bindings/pinctrl/renesas,r9a09g077-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/renesas,r9a09g077-pinctrl.yaml @@ -83,6 +83,23 @@ definitions: input: true input-enable: true output-enable: true + bias-disable: true + bias-pull-down: true + bias-pull-up: true + input-schmitt-enable: true + input-schmitt-disable: true + slew-rate: + description: 0 is slow slew rate, 1 is fast slew rate + enum: [0, 1] + drive-strength-microamp: + description: | + Four discrete levels are supported (via registers DRCTLm), corresponding + to the following nominal values: + - 2500 (Low strength) + - 5000 (Middle strength) + - 9000 (High strength) + - 11800 (Ultra High strength) + enum: [2500, 5000, 9000, 11800] oneOf: - required: [pinmux] - required: [pins] diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.yaml index 76e607281716..9b3cbeb54fed 100644 --- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.yaml @@ -50,6 +50,7 @@ properties: - rockchip,rk3568-pinctrl - rockchip,rk3576-pinctrl - rockchip,rk3588-pinctrl + - rockchip,rv1103b-pinctrl - rockchip,rv1108-pinctrl - rockchip,rv1126-pinctrl diff --git a/Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml b/Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml index 845b6b7b7552..8f8b4b68aaa3 100644 --- a/Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml +++ b/Documentation/devicetree/bindings/pinctrl/st,stm32-hdp.yaml @@ -27,6 +27,12 @@ properties: clocks: maxItems: 1 + access-controllers: + minItems: 1 + items: + - description: debug configuration access controller + - description: access controller that manages the HDP as a peripheral + patternProperties: "^hdp[0-7]-pins$": type: object diff --git a/Documentation/devicetree/bindings/power/reset/cortina,gemini-power-controller.yaml b/Documentation/devicetree/bindings/power/reset/cortina,gemini-power-controller.yaml new file mode 100644 index 000000000000..ef5e04f86be1 --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/cortina,gemini-power-controller.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/reset/cortina,gemini-power-controller.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cortina Systems Gemini Poweroff Controller + +maintainers: + - Linus Walleij + +description: | + The Gemini power controller is a dedicated IP block in the Cortina Gemini SoC that + controls system power-down operations. + +properties: + compatible: + const: cortina,gemini-power-controller + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + + poweroff@4b000000 { + compatible = "cortina,gemini-power-controller"; + reg = <0x4b000000 0x100>; + interrupts = <26 IRQ_TYPE_EDGE_FALLING>; + }; +... diff --git a/Documentation/devicetree/bindings/power/reset/gemini-poweroff.txt b/Documentation/devicetree/bindings/power/reset/gemini-poweroff.txt deleted file mode 100644 index 7fec3e100214..000000000000 --- a/Documentation/devicetree/bindings/power/reset/gemini-poweroff.txt +++ /dev/null @@ -1,17 +0,0 @@ -* Device-Tree bindings for Cortina Systems Gemini Poweroff - -This is a special IP block in the Cortina Gemini SoC that only -deals with different ways to power the system down. - -Required properties: -- compatible: should be "cortina,gemini-power-controller" -- reg: should contain the physical memory base and size -- interrupts: should contain the power management interrupt - -Example: - -power-controller@4b000000 { - compatible = "cortina,gemini-power-controller"; - reg = <0x4b000000 0x100>; - interrupts = <26 IRQ_TYPE_EDGE_FALLING>; -}; diff --git a/Documentation/devicetree/bindings/power/supply/cpcap-battery.yaml b/Documentation/devicetree/bindings/power/supply/cpcap-battery.yaml index 694bfdb5815c..6dcca55d6d90 100644 --- a/Documentation/devicetree/bindings/power/supply/cpcap-battery.yaml +++ b/Documentation/devicetree/bindings/power/supply/cpcap-battery.yaml @@ -55,6 +55,7 @@ properties: - const: chg_isense - const: batti + monitored-battery: true power-supplies: true required: diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml index 14242de7fc08..242b33f2bcba 100644 --- a/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml +++ b/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml @@ -20,6 +20,7 @@ properties: - maxim,max17050 - maxim,max17055 - maxim,max77705-battery + - maxim,max77759-fg - maxim,max77849-battery reg: @@ -27,36 +28,42 @@ properties: interrupts: maxItems: 1 - description: | - The ALRT pin, an open-drain interrupt. + description: + The ALRT pin (or FG_INTB pin on MAX77759), an open-drain interrupt. + + shunt-resistor-micro-ohms: + description: + Resistance of rsns resistor in micro Ohms (datasheet-recommended value is 10000). + Defining this property enables current-sense functionality. maxim,rsns-microohm: + deprecated: true $ref: /schemas/types.yaml#/definitions/uint32 - description: | + description: Resistance of rsns resistor in micro Ohms (datasheet-recommended value is 10000). Defining this property enables current-sense functionality. maxim,cold-temp: $ref: /schemas/types.yaml#/definitions/uint32 - description: | + description: Temperature threshold to report battery as cold (in tenths of degree Celsius). Default is not to report cold events. maxim,over-heat-temp: $ref: /schemas/types.yaml#/definitions/uint32 - description: | + description: Temperature threshold to report battery as over heated (in tenths of degree Celsius). Default is not to report over heating events. maxim,dead-volt: $ref: /schemas/types.yaml#/definitions/uint32 - description: | + description: Voltage threshold to report battery as dead (in mV). Default is not to report dead battery events. maxim,over-volt: $ref: /schemas/types.yaml#/definitions/uint32 - description: | + description: Voltage threshold to report battery as over voltage (in mV). Default is not to report over-voltage events. diff --git a/Documentation/devicetree/bindings/power/supply/samsung,s2mu005-fuel-gauge.yaml b/Documentation/devicetree/bindings/power/supply/samsung,s2mu005-fuel-gauge.yaml new file mode 100644 index 000000000000..05e420316a26 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/samsung,s2mu005-fuel-gauge.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/samsung,s2mu005-fuel-gauge.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Battery Fuel Gauge for Samsung S2M series PMICs + +maintainers: + - Kaustabh Chakraborty + +allOf: + - $ref: power-supply.yaml# + +properties: + compatible: + enum: + - samsung,s2mu005-fuel-gauge + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + fuel-gauge@3b { + compatible = "samsung,s2mu005-fuel-gauge"; + reg = <0x3b>; + + interrupt-parent = <&gpa0>; + interrupts = <3 IRQ_TYPE_EDGE_BOTH>; + + monitored-battery = <&battery>; + }; + }; diff --git a/Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml b/Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml index 04519b0c581d..d8f4f9ffe884 100644 --- a/Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml +++ b/Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml @@ -8,7 +8,7 @@ title: Apple FPWM controller maintainers: - asahi@lists.linux.dev - - Sasha Finkelstein + - Sasha Finkelstein description: PWM controller used for keyboard backlight on ARM Macs diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,milos-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,milos-pas.yaml index c47d97004b33..e5cce0d05fc6 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,milos-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,milos-pas.yaml @@ -16,6 +16,7 @@ description: properties: compatible: enum: + - qcom,eliza-adsp-pas - qcom,milos-adsp-pas - qcom,milos-cdsp-pas - qcom,milos-mpss-pas @@ -69,6 +70,7 @@ allOf: properties: compatible: enum: + - qcom,eliza-adsp-pas - qcom,milos-adsp-pas - qcom,milos-cdsp-pas then: @@ -89,6 +91,7 @@ allOf: compatible: contains: enum: + - qcom,eliza-adsp-pas - qcom,milos-adsp-pas then: properties: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,msm8916-mss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,msm8916-mss-pil.yaml index c179b560572b..faf2712e3d27 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,msm8916-mss-pil.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,msm8916-mss-pil.yaml @@ -17,10 +17,14 @@ properties: compatible: oneOf: - enum: + - qcom,mdm9607-mss-pil - qcom,msm8226-mss-pil - qcom,msm8909-mss-pil - qcom,msm8916-mss-pil + - qcom,msm8917-mss-pil - qcom,msm8926-mss-pil + - qcom,msm8937-mss-pil + - qcom,msm8940-mss-pil - qcom,msm8953-mss-pil - qcom,msm8974-mss-pil @@ -89,7 +93,7 @@ properties: description: PLL proxy supply (control handed over after startup) mss-supply: - description: MSS power domain supply (only valid for qcom,msm8974-mss-pil) + description: MSS power domain supply resets: items: @@ -137,7 +141,6 @@ properties: - description: MPSS reserved region firmware-name: - $ref: /schemas/types.yaml#/definitions/string-array items: - description: Name of MBA firmware - description: Name of modem firmware @@ -226,8 +229,12 @@ allOf: compatible: contains: enum: + - qcom,mdm9607-mss-pil - qcom,msm8909-mss-pil - qcom,msm8916-mss-pil + - qcom,msm8917-mss-pil + - qcom,msm8937-mss-pil + - qcom,msm8940-mss-pil then: properties: power-domains: @@ -271,6 +278,9 @@ allOf: contains: enum: - qcom,msm8926-mss-pil + - qcom,msm8917-mss-pil + - qcom,msm8937-mss-pil + - qcom,msm8940-mss-pil - qcom,msm8974-mss-pil then: required: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml index 4d2055f283ac..1b65813cc8ad 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml @@ -126,7 +126,6 @@ properties: - description: Metadata reserved region firmware-name: - $ref: /schemas/types.yaml#/definitions/string-array items: - description: Name of MBA firmware - description: Name of modem firmware diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml index 188a25194000..bcd2bcf96e24 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml @@ -51,7 +51,6 @@ properties: description: Reference to the AOSS side-channel message RAM. firmware-name: - $ref: /schemas/types.yaml#/definitions/string-array items: - description: Firmware name of the Hexagon core diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-mss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-mss-pil.yaml index b1402bef0ebe..7c9accac92d0 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-mss-pil.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-mss-pil.yaml @@ -98,7 +98,6 @@ properties: - description: metadata reserved region firmware-name: - $ref: /schemas/types.yaml#/definitions/string-array items: - description: Name of MBA firmware - description: Name of modem firmware diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-mss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-mss-pil.yaml index 005cb21732af..f349c303fa07 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-mss-pil.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7280-mss-pil.yaml @@ -98,7 +98,6 @@ properties: - description: metadata reserved region firmware-name: - $ref: /schemas/types.yaml#/definitions/string-array items: - description: Name of MBA firmware - description: Name of modem firmware diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc8280xp-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc8280xp-pas.yaml index 5dbda3a55047..8227527c1d77 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sc8280xp-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc8280xp-pas.yaml @@ -42,7 +42,7 @@ properties: description: Reference to the reserved-memory for the Hexagon core firmware-name: - $ref: /schemas/types.yaml#/definitions/string + maxItems: 1 description: Firmware name for the Hexagon core required: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sdx55-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sdx55-pas.yaml index 5d463272165f..8c4abde74915 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sdx55-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sdx55-pas.yaml @@ -56,7 +56,7 @@ properties: smd-edge: false firmware-name: - $ref: /schemas/types.yaml#/definitions/string + maxItems: 1 description: Firmware name for the Hexagon core required: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml index 11b056d6a480..1e4db0c9fcf9 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml @@ -28,8 +28,16 @@ properties: - qcom,x1e80100-adsp-pas - qcom,x1e80100-cdsp-pas - items: - - const: qcom,sm8750-adsp-pas + - enum: + - qcom,glymur-adsp-pas + - qcom,kaanapali-adsp-pas + - qcom,sm8750-adsp-pas - const: qcom,sm8550-adsp-pas + - items: + - enum: + - qcom,glymur-cdsp-pas + - qcom,kaanapali-cdsp-pas + - const: qcom,sm8550-cdsp-pas - items: - const: qcom,sm8750-cdsp-pas - const: qcom,sm8650-cdsp-pas @@ -52,7 +60,6 @@ properties: smd-edge: false firmware-name: - $ref: /schemas/types.yaml#/definitions/string-array items: - description: Firmware name of the Hexagon core - description: Firmware name of the Hexagon Devicetree @@ -95,6 +102,10 @@ allOf: compatible: contains: enum: + - qcom,glymur-adsp-pas + - qcom,glymur-cdsp-pas + - qcom,kaanapali-adsp-pas + - qcom,kaanapali-cdsp-pas - qcom,sm8750-adsp-pas then: properties: diff --git a/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml index a927551356e6..775e9b3a1938 100644 --- a/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml @@ -154,17 +154,44 @@ patternProperties: memory-region: description: | phandle to the reserved memory nodes to be associated with the - remoteproc device. There should be at least two reserved memory nodes - defined. The reserved memory nodes should be carveout nodes, and - should be defined with a "no-map" property as per the bindings in + remoteproc device. There should be two reserved memory nodes defined + for the basic layout or 6 partitions for a detailed layout. The + reserved memory nodes should be carveout nodes, and should be defined + with a "no-map" property as per the bindings in Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt - minItems: 2 - maxItems: 8 - items: - - description: region used for dynamic DMA allocations like vrings and - vring buffers - - description: region reserved for firmware image sections - additionalItems: true + oneOf: + - description: Basic layout + items: + - description: region used for dynamic DMA allocations like vrings and + vring buffers + - description: region reserved for firmware image sections + - description: Detailed layout + items: + - description: region used for dynamic DMA allocations like vrings and + vring buffers + - description: region reserved for IPC resources + - description: LPM FS stub binary + - description: LPM metadata + - description: LPM FS context data and reserved sections + - description: DM RM/PM trace and firmware code/data + + memory-region-names: + description: | + Names for the memory regions specified in the memory-region property. + The names must correspond with the entries in memory-region. + oneOf: + - description: Basic layout + items: + - const: dma + - const: firmware + - description: Detailed layout + items: + - const: dma + - const: ipc + - const: lpm-stub + - const: lpm-metadata + - const: lpm-context + - const: dm-firmware # Optional properties: # -------------------- @@ -218,6 +245,13 @@ patternProperties: - resets - firmware-name + if: + required: + - memory-region + then: + required: + - memory-region-names + unevaluatedProperties: false allOf: @@ -321,6 +355,7 @@ examples: mboxes = <&mailbox0 &mbox_mcu_r5fss0_core0>; memory-region = <&mcu_r5fss0_core0_dma_memory_region>, <&mcu_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; sram = <&mcu_r5fss0_core0_sram>; }; diff --git a/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml b/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml index c1b800a10b53..66650ef8f772 100644 --- a/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml +++ b/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml @@ -17,7 +17,9 @@ properties: compatible: oneOf: - items: - - const: renesas,r9a09g056-usb2phy-reset # RZ/V2N + - enum: + - renesas,r9a09g047-usb2phy-reset # RZ/G3E + - renesas,r9a09g056-usb2phy-reset # RZ/V2N - const: renesas,r9a09g057-usb2phy-reset - const: renesas,r9a09g057-usb2phy-reset # RZ/V2H(P) @@ -37,6 +39,9 @@ properties: '#reset-cells': const: 0 + '#mux-state-cells': + const: 1 + required: - compatible - reg @@ -44,6 +49,7 @@ required: - resets - power-domains - '#reset-cells' + - '#mux-state-cells' additionalProperties: false @@ -58,4 +64,5 @@ examples: resets = <&cpg 0xaf>; power-domains = <&cpg>; #reset-cells = <0>; + #mux-state-cells = <1>; }; diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml index c6ec9290fe07..2b0a8a93bb21 100644 --- a/Documentation/devicetree/bindings/riscv/extensions.yaml +++ b/Documentation/devicetree/bindings/riscv/extensions.yaml @@ -262,6 +262,23 @@ properties: ratified in RISC-V Profiles Version 1.0, with commit b1d806605f87 ("Updated to ratified state.") + - const: supm + description: | + The standard Supm extension for pointer masking support in user + mode (U-mode) as ratified at commit d70011dde6c2 ("Update to + ratified state") of riscv-j-extension. + + Supm represents a combination of underlying hardware capability + (Smnpm or Ssnpm), U-mode consumer privilege level, and M/S-mode + software configuration that enables pointer masking for U-mode. + + DO NOT include this property in device trees targeting privileged + system software (S-mode or M-mode). + + This property is only appropriate in device trees provided to + U-mode software where the next-higher-privilege-mode supports + Smnpm or Ssnpm and enables it for U-mode. + - const: svade description: | The standard Svade supervisor-level extension for SW-managed PTE A/D @@ -907,6 +924,16 @@ properties: then: contains: const: b + # Supm depends on Smnpm or Ssnpm + - if: + contains: + const: supm + then: + oneOf: + - contains: + const: smnpm + - contains: + const: ssnpm # Za64rs and Ziccrse depend on Zalrsc or A - if: contains: diff --git a/Documentation/devicetree/bindings/riscv/microchip.yaml b/Documentation/devicetree/bindings/riscv/microchip.yaml index 381d6eb6672e..137a6f413430 100644 --- a/Documentation/devicetree/bindings/riscv/microchip.yaml +++ b/Documentation/devicetree/bindings/riscv/microchip.yaml @@ -4,14 +4,14 @@ $id: http://devicetree.org/schemas/riscv/microchip.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Microchip PolarFire SoC-based boards +title: Microchip SoC-based boards maintainers: - Conor Dooley - Daire McNamara description: - Microchip PolarFire SoC-based boards + Microchip SoC-based boards properties: $nodename: @@ -46,6 +46,9 @@ properties: - microchip,mpfs-sev-kit - sundance,polarberry - const: microchip,mpfs + - items: + - const: microchip,pic64gx-curiosity-kit + - const: microchip,pic64gx additionalProperties: true diff --git a/Documentation/devicetree/bindings/rtc/isil,isl12026.txt b/Documentation/devicetree/bindings/rtc/isil,isl12026.txt deleted file mode 100644 index 2e0be45193bb..000000000000 --- a/Documentation/devicetree/bindings/rtc/isil,isl12026.txt +++ /dev/null @@ -1,28 +0,0 @@ -ISL12026 I2C RTC/EEPROM - -ISL12026 is an I2C RTC/EEPROM combination device. The RTC and control -registers respond at bus address 0x6f, and the EEPROM array responds -at bus address 0x57. The canonical "reg" value will be for the RTC portion. - -Required properties supported by the device: - - - "compatible": must be "isil,isl12026" - - "reg": I2C bus address of the device (always 0x6f) - -Optional properties: - - - "isil,pwr-bsw": If present PWR.BSW bit must be set to the specified - value for proper operation. - - - "isil,pwr-sbib": If present PWR.SBIB bit must be set to the specified - value for proper operation. - - -Example: - - rtc@6f { - compatible = "isil,isl12026"; - reg = <0x6f>; - isil,pwr-bsw = <0>; - isil,pwr-sbib = <1>; - } diff --git a/Documentation/devicetree/bindings/rtc/isil,isl12026.yaml b/Documentation/devicetree/bindings/rtc/isil,isl12026.yaml new file mode 100644 index 000000000000..152edce2ab41 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/isil,isl12026.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/isil,isl12026.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Intersil ISL12026 I2C RTC/EEPROM + +maintainers: + - Piyush Patle + +description: + The ISL12026 is a combination RTC and EEPROM device connected via I2C. + The RTC and control registers respond at address 0x6f, while the EEPROM + array responds at address 0x57. The "reg" property refers to the RTC + portion of the device. + +allOf: + - $ref: rtc.yaml# + +properties: + compatible: + const: isil,isl12026 + + reg: + maxItems: 1 + description: I2C address of the RTC portion (must be 0x6f) + + isil,pwr-bsw: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Value written to the PWR.BSW bit for proper device operation. + + isil,pwr-sbib: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Value written to the PWR.SBIB bit for proper device operation. + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + rtc@6f { + compatible = "isil,isl12026"; + reg = <0x6f>; + isil,pwr-bsw = <0>; + isil,pwr-sbib = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/rtc/microchip,mpfs-rtc.yaml b/Documentation/devicetree/bindings/rtc/microchip,mpfs-rtc.yaml index a3e60d9f8399..e26e92b1af03 100644 --- a/Documentation/devicetree/bindings/rtc/microchip,mpfs-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/microchip,mpfs-rtc.yaml @@ -47,6 +47,9 @@ properties: - const: rtc - const: rtcref + resets: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/rtc/microcrystal,rv3028.yaml b/Documentation/devicetree/bindings/rtc/microcrystal,rv3028.yaml index cda8ad7c1203..2ea3b4041953 100644 --- a/Documentation/devicetree/bindings/rtc/microcrystal,rv3028.yaml +++ b/Documentation/devicetree/bindings/rtc/microcrystal,rv3028.yaml @@ -32,6 +32,8 @@ properties: - 9000 - 15000 + vdd-supply: true + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt b/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt deleted file mode 100644 index a2891ceb6344..000000000000 --- a/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt +++ /dev/null @@ -1,5 +0,0 @@ -OLPC XO-1 RTC -~~~~~~~~~~~~~ - -Required properties: - - compatible : "olpc,xo1-rtc" diff --git a/Documentation/devicetree/bindings/rtc/sprd,sc2731-rtc.yaml b/Documentation/devicetree/bindings/rtc/sprd,sc2731-rtc.yaml index 5756f617df36..1deae2f4f09d 100644 --- a/Documentation/devicetree/bindings/rtc/sprd,sc2731-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/sprd,sc2731-rtc.yaml @@ -13,7 +13,12 @@ maintainers: properties: compatible: - const: sprd,sc2731-rtc + oneOf: + - items: + - enum: + - sprd,sc2730-rtc + - const: sprd,sc2731-rtc + - const: sprd,sc2731-rtc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml index b47822370d6f..722176c831aa 100644 --- a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml @@ -65,6 +65,8 @@ properties: - microcrystal,rv3029 # Real Time Clock - microcrystal,rv8523 + # OLPC XO-1 RTC + - olpc,xo1-rtc # I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC - ricoh,r2025sd # I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC diff --git a/Documentation/devicetree/bindings/serial/8250.yaml b/Documentation/devicetree/bindings/serial/8250.yaml index 73851f19330d..bb7b9c87a807 100644 --- a/Documentation/devicetree/bindings/serial/8250.yaml +++ b/Documentation/devicetree/bindings/serial/8250.yaml @@ -63,7 +63,9 @@ allOf: properties: compatible: contains: - const: spacemit,k1-uart + enum: + - spacemit,k1-uart + - spacemit,k3-uart then: properties: clock-names: @@ -76,6 +78,7 @@ allOf: contains: enum: - spacemit,k1-uart + - spacemit,k3-uart - nxp,lpc1850-uart then: required: @@ -179,6 +182,7 @@ properties: - const: ns16550a - items: - enum: + - loongson,ls3a4000-uart - loongson,ls3a5000-uart - loongson,ls3a6000-uart - loongson,ls2k2000-uart diff --git a/Documentation/devicetree/bindings/serial/amlogic,meson-uart.yaml b/Documentation/devicetree/bindings/serial/amlogic,meson-uart.yaml index d8ad1bb6172d..a2702319685d 100644 --- a/Documentation/devicetree/bindings/serial/amlogic,meson-uart.yaml +++ b/Documentation/devicetree/bindings/serial/amlogic,meson-uart.yaml @@ -56,6 +56,7 @@ properties: items: - enum: - amlogic,a4-uart + - amlogic,a9-uart - amlogic,s6-uart - amlogic,s7-uart - amlogic,s7d-uart diff --git a/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml b/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml index 087a8926f8b4..375cd50bc5cc 100644 --- a/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml +++ b/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml @@ -24,6 +24,7 @@ properties: - const: atmel,at91sam9260-usart - items: - enum: + - microchip,lan9691-usart - microchip,sam9x60-usart - microchip,sam9x7-usart - microchip,sama7d65-usart diff --git a/Documentation/devicetree/bindings/serial/renesas,rsci.yaml b/Documentation/devicetree/bindings/serial/renesas,rsci.yaml index e059b14775eb..85ebb3056066 100644 --- a/Documentation/devicetree/bindings/serial/renesas,rsci.yaml +++ b/Documentation/devicetree/bindings/serial/renesas,rsci.yaml @@ -14,6 +14,7 @@ properties: compatible: oneOf: - enum: + - renesas,r9a08g046-rsci # RZ/G3L - renesas,r9a09g047-rsci # RZ/G3E - renesas,r9a09g077-rsci # RZ/T2H @@ -145,6 +146,31 @@ allOf: - resets - reset-names + - if: + properties: + compatible: + contains: + const: renesas,r9a08g046-rsci + then: + properties: + interrupts: + minItems: 6 + + interrupt-names: + minItems: 6 + + clocks: + minItems: 2 + maxItems: 3 + + clock-names: + minItems: 2 + maxItems: 3 + + required: + - resets + - reset-names + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml index 6efe43089a74..685c1eceb782 100644 --- a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml +++ b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml @@ -71,6 +71,7 @@ properties: - rockchip,rk3568-uart - rockchip,rk3576-uart - rockchip,rk3588-uart + - rockchip,rv1103b-uart - rockchip,rv1108-uart - rockchip,rv1126-uart - sophgo,sg2044-uart diff --git a/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml b/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml new file mode 100644 index 000000000000..a01a515222c6 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/cix/cix,sky1-system-control.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cix Sky1 SoC system control register region + +maintainers: + - Gary Yang + +description: + An wide assortment of registers of the system controller on Sky1 SoC, + including resets, usb, wakeup sources and so on. + +properties: + compatible: + items: + - enum: + - cix,sky1-system-control + - cix,sky1-s5-system-control + - const: syscon + + reg: + maxItems: 1 + + '#reset-cells': + const: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + syscon@4160000 { + compatible = "cix,sky1-system-control", "syscon"; + reg = <0x4160000 0x100>; + #reset-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/soc/imx/fsl,imx93-media-blk-ctrl.yaml b/Documentation/devicetree/bindings/soc/imx/fsl,imx93-media-blk-ctrl.yaml index 34aea58094e5..d828c2e82965 100644 --- a/Documentation/devicetree/bindings/soc/imx/fsl,imx93-media-blk-ctrl.yaml +++ b/Documentation/devicetree/bindings/soc/imx/fsl,imx93-media-blk-ctrl.yaml @@ -40,6 +40,58 @@ properties: minItems: 8 maxItems: 10 + dpi-bridge: + type: object + additionalProperties: false + + properties: + compatible: + enum: + - nxp,imx91-pdfc + - nxp,imx93-pdfc + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: Input port node to receive pixel data. + + port@1: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: Output port node to downstream pixel data receivers. + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + bus-width: + enum: [ 16, 18, 24 ] + description: + Specify the physical parallel bus width. + + This property is optional if the display bus-width + matches the SoC bus-width, e.g. a 18-bit RGB666 (display) + is connected and all 18-bit data lines are muxed to the + parallel-output pads. + + This property must be set to 18 to cut only the LSBs + instead of the MSBs in case a 24-bit RGB888 display is + connected and only the lower 18-bit data lanes are muxed + to the parallel-output pads. + + required: + - port@0 + - port@1 + + required: + - compatible + - ports + allOf: - if: properties: @@ -112,4 +164,30 @@ examples: clock-names = "apb", "axi", "nic", "disp", "cam", "pxp", "lcdif", "isi", "csi", "dsi"; #power-domain-cells = <1>; + + dpi-bridge { + compatible = "nxp,imx93-pdfc"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pdfc_from_lcdif: endpoint { + remote-endpoint = <&lcdif_to_pdfc>; + }; + }; + + port@1 { + reg = <1>; + + pdfc_to_panel: endpoint { + remote-endpoint = <&panel_from_pdfc>; + bus-width = <18>; + }; + }; + }; + }; }; diff --git a/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-irqmux.yaml b/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-irqmux.yaml new file mode 100644 index 000000000000..51164772724f --- /dev/null +++ b/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-irqmux.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/microchip/microchip,mpfs-irqmux.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Polarfire SoC GPIO Interrupt Mux + +maintainers: + - Conor Dooley + +description: | + There are 3 GPIO controllers on this SoC, of which: + - GPIO controller 0 has 14 GPIOs + - GPIO controller 1 has 24 GPIOs + - GPIO controller 2 has 32 GPIOs + + All GPIOs are capable of generating interrupts, for a total of 70. + There are only 41 IRQs available however, so a configurable mux is used to + ensure all GPIOs can be used for interrupt generation. + 38 of the 41 interrupts are in what the documentation calls "direct mode", + as they provide an exclusive connection from a GPIO to the PLIC. + Lines 18 to 23 on GPIO controller 1 are always in "direct mode". + The 3 remaining interrupts are used to mux the interrupts which do not have + a exclusive connection, one for each GPIO controller. + +properties: + compatible: + const: microchip,mpfs-irqmux + + reg: + maxItems: 1 + + "#address-cells": + const: 0 + + "#interrupt-cells": + const: 1 + + interrupt-map-mask: + items: + - const: 0x7f + + interrupt-map: + description: | + Specifies the mapping from GPIO interrupt lines to plic interrupts. + + The child interrupt number set in arrays items is computed using the + following formula: + gpio_bank * 32 + gpio_number + with: + - gpio_bank: The GPIO bank number + - 0 for GPIO0, + - 1 for GPIO1, + - 2 for GPIO2 + - gpio_number: Number of the gpio in the bank (0..31) + maxItems: 70 + +required: + - compatible + - reg + - "#address-cells" + - "#interrupt-cells" + - interrupt-map-mask + - interrupt-map + +additionalProperties: false + +examples: + - | + interrupt-controller@54 { + compatible = "microchip,mpfs-irqmux"; + reg = <0x54 0x4>; + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0x7f>; + interrupt-map = <0 &plic 13>, <1 &plic 14>, <2 &plic 15>, + <3 &plic 16>, <4 &plic 17>, <5 &plic 18>, + <6 &plic 19>, <7 &plic 20>, <8 &plic 21>, + <9 &plic 22>, <10 &plic 23>, <11 &plic 24>, + <12 &plic 25>, <13 &plic 26>, + + <32 &plic 27>, <33 &plic 28>, <34 &plic 29>, + <35 &plic 30>, <36 &plic 31>, <37 &plic 32>, + <38 &plic 33>, <39 &plic 34>, <40 &plic 35>, + <41 &plic 36>, <42 &plic 37>, <43 &plic 38>, + <44 &plic 39>, <45 &plic 40>, <46 &plic 41>, + <47 &plic 42>, <48 &plic 43>, <49 &plic 44>, + <50 &plic 45>, <51 &plic 46>, <52 &plic 47>, + <53 &plic 48>, <54 &plic 49>, <55 &plic 50>, + + <64 &plic 53>, <65 &plic 53>, <66 &plic 53>, + <67 &plic 53>, <68 &plic 53>, <69 &plic 53>, + <70 &plic 53>, <71 &plic 53>, <72 &plic 53>, + <73 &plic 53>, <74 &plic 53>, <75 &plic 53>, + <76 &plic 53>, <77 &plic 53>, <78 &plic 53>, + <79 &plic 53>, <80 &plic 53>, <81 &plic 53>, + <82 &plic 53>, <83 &plic 53>, <84 &plic 53>, + <85 &plic 53>, <86 &plic 53>, <87 &plic 53>, + <88 &plic 53>, <89 &plic 53>, <90 &plic 53>, + <91 &plic 53>, <92 &plic 53>, <93 &plic 53>, + <94 &plic 53>, <95 &plic 53>; + }; diff --git a/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml b/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml index 44e4a50c3155..1e3725335b2c 100644 --- a/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml +++ b/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml @@ -15,10 +15,16 @@ description: properties: compatible: - items: - - const: microchip,mpfs-mss-top-sysreg - - const: syscon - - const: simple-mfd + oneOf: + - items: + - const: microchip,mpfs-mss-top-sysreg + - const: syscon + - const: simple-mfd + - items: + - const: microchip,pic64gx-mss-top-sysreg + - const: microchip,mpfs-mss-top-sysreg + - const: syscon + - const: simple-mfd reg: maxItems: 1 @@ -38,6 +44,10 @@ properties: of PolarFire clock/reset IDs. const: 1 + interrupt-controller@54: + type: object + $ref: /schemas/soc/microchip/microchip,mpfs-irqmux.yaml + pinctrl@200: type: object $ref: /schemas/pinctrl/microchip,mpfs-pinctrl-iomux0.yaml diff --git a/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml b/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml index a3fa04f3a1bd..6cebc19db4f5 100644 --- a/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml +++ b/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml @@ -24,7 +24,9 @@ properties: maxItems: 1 compatible: - const: microchip,mpfs-sys-controller + enum: + - microchip,mpfs-sys-controller + - microchip,pic64gx-sys-controller microchip,bitstream-flash: $ref: /schemas/types.yaml#/definitions/phandle diff --git a/Documentation/devicetree/bindings/soc/mobileye/mobileye,eyeq5-olb.yaml b/Documentation/devicetree/bindings/soc/mobileye/mobileye,eyeq5-olb.yaml index 6d11472ba5a7..56401d76a9b5 100644 --- a/Documentation/devicetree/bindings/soc/mobileye/mobileye,eyeq5-olb.yaml +++ b/Documentation/devicetree/bindings/soc/mobileye/mobileye,eyeq5-olb.yaml @@ -51,6 +51,9 @@ properties: clock-names: const: ref + '#phy-cells': + const: 1 + patternProperties: '-pins?$': type: object @@ -310,7 +313,7 @@ allOf: properties: '#reset-cells': false - # Only EyeQ5 has pinctrl in OLB. + # Only EyeQ5 has pinctrl and PHY in OLB. - if: not: properties: @@ -320,6 +323,8 @@ allOf: then: patternProperties: '-pins?$': false + properties: + '#phy-cells': false examples: - | diff --git a/Documentation/devicetree/bindings/soc/mobileye/mobileye,eyeq6lplus-olb.yaml b/Documentation/devicetree/bindings/soc/mobileye/mobileye,eyeq6lplus-olb.yaml new file mode 100644 index 000000000000..8334876cf4e6 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/mobileye/mobileye,eyeq6lplus-olb.yaml @@ -0,0 +1,208 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/mobileye/mobileye,eyeq6lplus-olb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mobileye EyeQ6Lplus SoC system controller + +maintainers: + - Benoît Monin + - Grégory Clement + - Théo Lebrun + - Vladimir Kondratiev + +description: + OLB ("Other Logic Block") is a hardware block grouping smaller blocks. + Clocks, resets, pinctrl are being handled from here. EyeQ6Lplus hosts + a single instance providing 22 clocks, two reset domains and one bank + of 32 pins. + +properties: + compatible: + items: + - const: mobileye,eyeq6lplus-olb + - const: syscon + + reg: + maxItems: 1 + + '#reset-cells': + description: + First cell is reset domain index. + Second cell is reset index inside that domain. + const: 2 + + '#clock-cells': + const: 1 + + clocks: + maxItems: 1 + description: + Input parent clock to all PLLs. Expected to be the main crystal. + + clock-names: + const: ref + +patternProperties: + '-pins?$': + type: object + description: Pin muxing configuration. + $ref: /schemas/pinctrl/pinmux-node.yaml# + additionalProperties: false + properties: + pins: true + function: + enum: [gpio, timer0, timer1, uart_ssi, spi0, uart0, timer2, timer3, + timer_ext0, spi1, timer_ext1, ext_ref_clk, mipi_ref_clk] + bias-disable: true + bias-pull-down: true + bias-pull-up: true + drive-strength: true + required: + - pins + - function + allOf: + - if: + properties: + function: + const: gpio + then: + properties: + pins: + items: # PA0 - PA31 + pattern: '^(PA[1,2]?[0-9]|PA3[0,1])$' + - if: + properties: + function: + const: timer0 + then: + properties: + pins: + items: + enum: [PA0, PA1] + - if: + properties: + function: + const: timer1 + then: + properties: + pins: + items: + enum: [PA2, PA3] + - if: + properties: + function: + const: uart_ssi + then: + properties: + pins: + items: + enum: [PA4, PA5] + - if: + properties: + function: + const: spi0 + then: + properties: + pins: + items: + enum: [PA6, PA7, PA8, PA9, PA10] + - if: + properties: + function: + const: uart0 + then: + properties: + pins: + items: + enum: [PA11, PA12] + - if: + properties: + function: + const: timer2 + then: + properties: + pins: + items: + enum: [PA13, PA14] + - if: + properties: + function: + const: timer3 + then: + properties: + pins: + items: + enum: [PA15, PA16] + - if: + properties: + function: + const: timer_ext0 + then: + properties: + pins: + items: + enum: [PA17, PA18, PA19, PA20] + - if: + properties: + function: + const: spi1 + then: + properties: + pins: + items: + enum: [PA21, PA22, PA23, PA24, PA25] + - if: + properties: + function: + const: timer_ext1 + then: + properties: + pins: + items: + enum: [PA26, PA27, PA28, PA29] + - if: + properties: + function: + const: ext_ref_clk + then: + properties: + pins: + items: + enum: [PA30] + - if: + properties: + function: + const: mipi_ref_clk + then: + properties: + pins: + items: + enum: [PA31] + +required: + - compatible + - reg + - '#clock-cells' + - clocks + - clock-names + - '#reset-cells' + +additionalProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + system-controller@e8400000 { + compatible = "mobileye,eyeq6lplus-olb", "syscon"; + reg = <0 0xe8400000 0x0 0x80000>; + #reset-cells = <2>; + #clock-cells = <1>; + clocks = <&xtal>; + clock-names = "ref"; + }; + }; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml index 7085bf88afab..ff01d2f3ee5b 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml @@ -23,6 +23,8 @@ properties: oneOf: - items: - enum: + - qcom,glymur-pmic-glink + - qcom,kaanapali-pmic-glink - qcom,qcm6490-pmic-glink - qcom,sc8180x-pmic-glink - qcom,sc8280xp-pmic-glink diff --git a/Documentation/devicetree/bindings/soc/renesas/renesas,rzg2l-sysc.yaml b/Documentation/devicetree/bindings/soc/renesas/renesas,rzg2l-sysc.yaml index 4386b2c3fa4d..94ae72eb8fb6 100644 --- a/Documentation/devicetree/bindings/soc/renesas/renesas,rzg2l-sysc.yaml +++ b/Documentation/devicetree/bindings/soc/renesas/renesas,rzg2l-sysc.yaml @@ -24,6 +24,7 @@ properties: - renesas,r9a07g044-sysc # RZ/G2{L,LC} - renesas,r9a07g054-sysc # RZ/V2L - renesas,r9a08g045-sysc # RZ/G3S + - renesas,r9a08g046-sysc # RZ/G3L reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/soc/renesas/renesas.yaml b/Documentation/devicetree/bindings/soc/renesas/renesas.yaml index f4947ac65460..5c22c51b1533 100644 --- a/Documentation/devicetree/bindings/soc/renesas/renesas.yaml +++ b/Documentation/devicetree/bindings/soc/renesas/renesas.yaml @@ -548,6 +548,19 @@ properties: - const: renesas,r9a08g045s33 # PCIe support - const: renesas,r9a08g045 + - description: RZ/G3L (R9A08G046) + items: + - enum: + - renesas,smarc2-evk # RZ SMARC Carrier-II EVK + - enum: + - renesas,rzg3l-smarcm # RZ/G3L SMARC Module (SoM) + - enum: + - renesas,r9a08g046l26 # Dual Cortex-A55 + Cortex-M33 + GE3D/VCP (14mm LFBGA) + - renesas,r9a08g046l28 # Dual Cortex-A55 + Cortex-M33 + GE3D/VCP (17mm LFBGA) + - renesas,r9a08g046l46 # Quad Cortex-A55 + Cortex-M33 + GE3D/VCP (14mm LFBGA) + - renesas,r9a08g046l48 # Quad Cortex-A55 + Cortex-M33 + GE3D/VCP (17mm LFBGA) + - const: renesas,r9a08g046 + - description: RZ/V2M (R9A09G011) items: - enum: diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml index 0b8e3294c83e..2cc43742b8e3 100644 --- a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml +++ b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml @@ -63,6 +63,7 @@ properties: - rockchip,rk3588-vo0-grf - rockchip,rk3588-vo1-grf - rockchip,rk3588-vop-grf + - rockchip,rv1103b-ioc - rockchip,rv1108-usbgrf - const: syscon - items: @@ -98,6 +99,7 @@ properties: - rockchip,rk3576-pmu0-grf - rockchip,rk3576-usb2phy-grf - rockchip,rk3588-usb2phy-grf + - rockchip,rv1103b-pmu-grf - rockchip,rv1108-grf - rockchip,rv1108-pmugrf - rockchip,rv1126-grf @@ -231,6 +233,7 @@ allOf: - rockchip,rk3036-grf - rockchip,rk3308-grf - rockchip,rk3368-pmugrf + - rockchip,rv1103b-pmu-grf then: properties: diff --git a/Documentation/devicetree/bindings/sound/ti,tas2781.yaml b/Documentation/devicetree/bindings/sound/ti,tas2781.yaml index f3a5638f4239..b21466bb0730 100644 --- a/Documentation/devicetree/bindings/sound/ti,tas2781.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas2781.yaml @@ -1,5 +1,5 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -# Copyright (C) 2022 - 2025 Texas Instruments Incorporated +# Copyright (C) 2022 - 2026 Texas Instruments Incorporated %YAML 1.2 --- $id: http://devicetree.org/schemas/sound/ti,tas2781.yaml# @@ -107,6 +107,9 @@ properties: ti,tas5830: 65-W Stereo, Digital Input, High Efficiency Closed-Loop Class-D Amplifier with Class-H Algorithm + + ti,tas5832: 81-W Stereo, Digital Input, High Efficiency Closed-Loop + Class-D Amplifier with Class-H Algorithm oneOf: - items: - enum: @@ -128,6 +131,7 @@ properties: - ti,tas5827 - ti,tas5828 - ti,tas5830 + - ti,tas5832 - const: ti,tas2781 - enum: - ti,tas2781 @@ -264,6 +268,7 @@ allOf: - ti,tas5827 - ti,tas5828 - ti,tas5830 + - ti,tas5832 then: properties: reg: diff --git a/Documentation/devicetree/bindings/spi/fsl,spi.yaml b/Documentation/devicetree/bindings/spi/fsl,spi.yaml index d74792fc9bf2..6a359488dd41 100644 --- a/Documentation/devicetree/bindings/spi/fsl,spi.yaml +++ b/Documentation/devicetree/bindings/spi/fsl,spi.yaml @@ -59,7 +59,7 @@ unevaluatedProperties: false examples: - | - #include + #include spi@4c0 { compatible = "fsl,spi"; @@ -67,8 +67,8 @@ examples: cell-index = <0>; interrupts = <82 0>; mode = "cpu"; - cs-gpios = <&gpio 18 IRQ_TYPE_EDGE_RISING // device reg=<0> - &gpio 19 IRQ_TYPE_EDGE_RISING>; // device reg=<1> + cs-gpios = <&gpio 18 GPIO_ACTIVE_HIGH>, // device reg=<0> + <&gpio 19 GPIO_ACTIVE_HIGH>; // device reg=<1> }; ... diff --git a/Documentation/devicetree/bindings/spmi/apple,spmi.yaml b/Documentation/devicetree/bindings/spmi/apple,spmi.yaml index ba524f1eb704..3e5b14bc8c31 100644 --- a/Documentation/devicetree/bindings/spmi/apple,spmi.yaml +++ b/Documentation/devicetree/bindings/spmi/apple,spmi.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Apple SPMI controller maintainers: - - Sasha Finkelstein + - Sasha Finkelstein description: A SPMI controller present on most Apple SoCs diff --git a/Documentation/devicetree/bindings/spmi/qcom,x1e80100-spmi-pmic-arb.yaml b/Documentation/devicetree/bindings/spmi/qcom,x1e80100-spmi-pmic-arb.yaml index 08369fdd2161..0f7089e0950a 100644 --- a/Documentation/devicetree/bindings/spmi/qcom,x1e80100-spmi-pmic-arb.yaml +++ b/Documentation/devicetree/bindings/spmi/qcom,x1e80100-spmi-pmic-arb.yaml @@ -24,7 +24,9 @@ properties: compatible: oneOf: - items: - - const: qcom,sar2130p-spmi-pmic-arb + - enum: + - qcom,eliza-spmi-pmic-arb + - qcom,sar2130p-spmi-pmic-arb - const: qcom,x1e80100-spmi-pmic-arb - const: qcom,x1e80100-spmi-pmic-arb diff --git a/Documentation/devicetree/bindings/sram/sram.yaml b/Documentation/devicetree/bindings/sram/sram.yaml index c451140962c8..8985f89170be 100644 --- a/Documentation/devicetree/bindings/sram/sram.yaml +++ b/Documentation/devicetree/bindings/sram/sram.yaml @@ -34,7 +34,9 @@ properties: - nvidia,tegra186-sysram - nvidia,tegra194-sysram - nvidia,tegra234-sysram + - qcom,hawi-imem - qcom,kaanapali-imem + - qcom,milos-imem - qcom,rpm-msg-ram - rockchip,rk3288-pmu-sram @@ -65,7 +67,7 @@ properties: type: boolean patternProperties: - "^([a-z0-9]*-)?sram(-section)?@[a-f0-9]+$": + "^([a-z0-9]+-)*sram(-section)?@[a-f0-9]+$": type: object description: Each child of the sram node specifies a region of reserved memory. diff --git a/Documentation/devicetree/bindings/thermal/max77620_thermal.txt b/Documentation/devicetree/bindings/thermal/max77620_thermal.txt deleted file mode 100644 index 82ed5d487966..000000000000 --- a/Documentation/devicetree/bindings/thermal/max77620_thermal.txt +++ /dev/null @@ -1,70 +0,0 @@ -Thermal driver for MAX77620 Power management IC from Maxim Semiconductor. - -Maxim Semiconductor MAX77620 supports alarm interrupts when its -die temperature crosses 120C and 140C. These threshold temperatures -are not configurable. Device does not provide the real temperature -of die other than just indicating whether temperature is above or -below threshold level. - -Required properties: -------------------- -#thermal-sensor-cells: For more details, please refer to - - The value must be 0. - -For more details, please refer generic thermal DT binding document -. - -Please refer for mfd DT binding -document for the MAX77620. - -Example: --------- -#include -#include -... - -i2c@7000d000 { - spmic: max77620@3c { - compatible = "maxim,max77620"; - ::::: - #thermal-sensor-cells = <0>; - ::: - }; -}; - -cool_dev: cool-dev { - compatible = "cooling-dev"; - #cooling-cells = <2>; -}; - -thermal-zones { - PMIC-Die { - polling-delay = <0>; - polling-delay-passive = <0>; - thermal-sensors = <&spmic>; - - trips { - pmic_die_warn_temp_thresh: hot-die { - temperature = <120000>; - type = "hot"; - hysteresis = <0>; - }; - - pmic_die_cirt_temp_thresh: cirtical-die { - temperature = <140000>; - type = "critical"; - hysteresis = <0>; - }; - }; - - cooling-maps { - map0 { - trip = <&pmic_die_warn_temp_thresh>; - cooling-device = <&cool_dev THERMAL_NO_LIMIT - THERMAL_NO_LIMIT>; - contribution = <100>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/thermal/thermal-zones.yaml b/Documentation/devicetree/bindings/thermal/thermal-zones.yaml index 0de0a9757ccc..07d9f576ffe7 100644 --- a/Documentation/devicetree/bindings/thermal/thermal-zones.yaml +++ b/Documentation/devicetree/bindings/thermal/thermal-zones.yaml @@ -129,63 +129,60 @@ patternProperties: which the thermal framework needs to take action. The actions to be taken are defined in another node called cooling-maps. - patternProperties: - "^[a-zA-Z][a-zA-Z0-9\\-_]{0,63}$": - type: object + additionalProperties: + type: object + additionalProperties: false - properties: - temperature: - $ref: /schemas/types.yaml#/definitions/int32 - minimum: -273000 - maximum: 200000 - description: - An integer expressing the trip temperature in millicelsius. + properties: + temperature: + $ref: /schemas/types.yaml#/definitions/int32 + minimum: -273000 + maximum: 200000 + description: + An integer expressing the trip temperature in millicelsius. - hysteresis: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - An unsigned integer expressing the hysteresis delta with - respect to the trip temperature property above, also in - millicelsius. Any cooling action initiated by the framework is - maintained until the temperature falls below - (trip temperature - hysteresis). This potentially prevents a - situation where the trip gets constantly triggered soon after - cooling action is removed. + hysteresis: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + An unsigned integer expressing the hysteresis delta with + respect to the trip temperature property above, also in + millicelsius. Any cooling action initiated by the framework is + maintained until the temperature falls below + (trip temperature - hysteresis). This potentially prevents a + situation where the trip gets constantly triggered soon after + cooling action is removed. - type: - $ref: /schemas/types.yaml#/definitions/string - enum: - - active # enable active cooling e.g. fans - - passive # enable passive cooling e.g. throttling cpu - - hot # send notification to driver - - critical # send notification to driver, trigger shutdown - description: | - There are four valid trip types: active, passive, hot, - critical. + type: + $ref: /schemas/types.yaml#/definitions/string + enum: + - active # enable active cooling e.g. fans + - passive # enable passive cooling e.g. throttling cpu + - hot # send notification to driver + - critical # send notification to driver, trigger shutdown + description: | + There are four valid trip types: active, passive, hot, + critical. - The critical trip type is used to set the maximum - temperature threshold above which the HW becomes - unstable and underlying firmware might even trigger a - reboot. Hitting the critical threshold triggers a system - shutdown. + The critical trip type is used to set the maximum + temperature threshold above which the HW becomes + unstable and underlying firmware might even trigger a + reboot. Hitting the critical threshold triggers a system + shutdown. - The hot trip type can be used to send a notification to - the thermal driver (if a .notify callback is registered). - The action to be taken is left to the driver. + The hot trip type can be used to send a notification to + the thermal driver (if a .notify callback is registered). + The action to be taken is left to the driver. - The passive trip type can be used to slow down HW e.g. run - the CPU, GPU, bus at a lower frequency. + The passive trip type can be used to slow down HW e.g. run + the CPU, GPU, bus at a lower frequency. - The active trip type can be used to control other HW to - help in cooling e.g. fans can be sped up or slowed down + The active trip type can be used to control other HW to + help in cooling e.g. fans can be sped up or slowed down - required: - - temperature - - hysteresis - - type - additionalProperties: false - - additionalProperties: false + required: + - temperature + - hysteresis + - type cooling-maps: type: object diff --git a/Documentation/devicetree/bindings/timer/sifive,clint.yaml b/Documentation/devicetree/bindings/timer/sifive,clint.yaml index 3bab40500df9..3c16b260db04 100644 --- a/Documentation/devicetree/bindings/timer/sifive,clint.yaml +++ b/Documentation/devicetree/bindings/timer/sifive,clint.yaml @@ -31,6 +31,7 @@ properties: - enum: - canaan,k210-clint # Canaan Kendryte K210 - eswin,eic7700-clint # ESWIN EIC7700 + - microchip,pic64gx-clint # Microchip PIC64GX - sifive,fu540-c000-clint # SiFive FU540 - spacemit,k1-clint # SpacemiT K1 - spacemit,k3-clint # SpacemiT K3 diff --git a/Documentation/devicetree/bindings/timer/xlnx,xps-timer.yaml b/Documentation/devicetree/bindings/timer/xlnx,xps-timer.yaml index b1597db04263..3538eafff6b1 100644 --- a/Documentation/devicetree/bindings/timer/xlnx,xps-timer.yaml +++ b/Documentation/devicetree/bindings/timer/xlnx,xps-timer.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Xilinx LogiCORE IP AXI Timer maintainers: - - Sean Anderson + - Sean Anderson properties: compatible: diff --git a/Documentation/devicetree/bindings/timestamp/nvidia,tegra194-hte.yaml b/Documentation/devicetree/bindings/timestamp/nvidia,tegra194-hte.yaml index 456797967adc..a96d6cd23895 100644 --- a/Documentation/devicetree/bindings/timestamp/nvidia,tegra194-hte.yaml +++ b/Documentation/devicetree/bindings/timestamp/nvidia,tegra194-hte.yaml @@ -25,6 +25,8 @@ properties: - nvidia,tegra194-gte-lic - nvidia,tegra234-gte-aon - nvidia,tegra234-gte-lic + - nvidia,tegra264-gte-aon + - nvidia,tegra264-gte-lic reg: maxItems: 1 @@ -112,10 +114,22 @@ allOf: contains: enum: - nvidia,tegra234-gte-aon + - nvidia,tegra264-gte-aon then: required: - nvidia,gpio-controller + - if: + properties: + compatible: + contains: + enum: + - nvidia,tegra264-gte-aon + - nvidia,tegra264-gte-lic + then: + properties: + nvidia,slices: false + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/ufs/qcom,sc7180-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sc7180-ufshc.yaml index d94ef4e6b85a..3c407426d697 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,sc7180-ufshc.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,sc7180-ufshc.yaml @@ -15,6 +15,7 @@ select: compatible: contains: enum: + - qcom,milos-ufshc - qcom,msm8998-ufshc - qcom,qcs8300-ufshc - qcom,sa8775p-ufshc @@ -31,21 +32,28 @@ select: properties: compatible: - items: - - enum: - - qcom,msm8998-ufshc - - qcom,qcs8300-ufshc - - qcom,sa8775p-ufshc - - qcom,sc7180-ufshc - - qcom,sc7280-ufshc - - qcom,sc8180x-ufshc - - qcom,sc8280xp-ufshc - - qcom,sm8250-ufshc - - qcom,sm8350-ufshc - - qcom,sm8450-ufshc - - qcom,sm8550-ufshc - - const: qcom,ufshc - - const: jedec,ufs-2.0 + oneOf: + - items: + - enum: + - qcom,x1e80100-ufshc + - const: qcom,sm8550-ufshc + - const: qcom,ufshc + - items: + - enum: + - qcom,milos-ufshc + - qcom,msm8998-ufshc + - qcom,qcs8300-ufshc + - qcom,sa8775p-ufshc + - qcom,sc7180-ufshc + - qcom,sc7280-ufshc + - qcom,sc8180x-ufshc + - qcom,sc8280xp-ufshc + - qcom,sm8250-ufshc + - qcom,sm8350-ufshc + - qcom,sm8450-ufshc + - qcom,sm8550-ufshc + - const: qcom,ufshc + - const: jedec,ufs-2.0 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml index cea84ab2204f..f28641c6e68f 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml @@ -15,6 +15,7 @@ select: compatible: contains: enum: + - qcom,eliza-ufshc - qcom,kaanapali-ufshc - qcom,sm8650-ufshc - qcom,sm8750-ufshc @@ -25,6 +26,7 @@ properties: compatible: items: - enum: + - qcom,eliza-ufshc - qcom,kaanapali-ufshc - qcom,sm8650-ufshc - qcom,sm8750-ufshc @@ -66,6 +68,18 @@ required: allOf: - $ref: qcom,ufs-common.yaml + - if: + properties: + compatible: + contains: + enum: + - qcom,eliza-ufshc + then: + properties: + reg: + minItems: 2 + reg-names: + minItems: 2 unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/ufs/rockchip,rk3576-ufshc.yaml b/Documentation/devicetree/bindings/ufs/rockchip,rk3576-ufshc.yaml index c7d17cf4dc42..e738153a309c 100644 --- a/Documentation/devicetree/bindings/ufs/rockchip,rk3576-ufshc.yaml +++ b/Documentation/devicetree/bindings/ufs/rockchip,rk3576-ufshc.yaml @@ -41,7 +41,7 @@ properties: maxItems: 1 resets: - maxItems: 4 + maxItems: 5 reset-names: items: @@ -49,6 +49,7 @@ properties: - const: sys - const: ufs - const: grf + - const: mphy reset-gpios: maxItems: 1 @@ -98,8 +99,8 @@ examples: interrupts = ; power-domains = <&power RK3576_PD_USB>; resets = <&cru SRST_A_UFS_BIU>, <&cru SRST_A_UFS_SYS>, <&cru SRST_A_UFS>, - <&cru SRST_P_UFS_GRF>; - reset-names = "biu", "sys", "ufs", "grf"; + <&cru SRST_P_UFS_GRF>, <&cru SRST_MPHY_INIT>; + reset-names = "biu", "sys", "ufs", "grf", "mphy"; reset-gpios = <&gpio4 RK_PD0 GPIO_ACTIVE_LOW>; }; }; diff --git a/Documentation/devicetree/bindings/usb/atmel,at91rm9200-udc.yaml b/Documentation/devicetree/bindings/usb/atmel,at91rm9200-udc.yaml new file mode 100644 index 000000000000..a4eabb935e6e --- /dev/null +++ b/Documentation/devicetree/bindings/usb/atmel,at91rm9200-udc.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/atmel,at91rm9200-udc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Atmel AT91 USB Device Controller (UDC) + +maintainers: + - Nicolas Ferre + - Alexandre Belloni + +description: + The Atmel AT91 USB Device Controller provides USB gadget (device-mode) + functionality on AT91 SoCs. It requires a peripheral clock and an AHB + clock for operation and may optionally control VBUS power through a GPIO. + +properties: + compatible: + enum: + - atmel,at91rm9200-udc + - atmel,at91sam9260-udc + - atmel,at91sam9261-udc + - atmel,at91sam9263-udc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: pclk + - const: hclk + + atmel,vbus-gpio: + description: GPIO used to enable or control VBUS power for the USB bus. + maxItems: 1 + + atmel,matrix: + $ref: /schemas/types.yaml#/definitions/phandle + description: Phandle to the Atmel bus matrix controller. + + atmel,pullup-gpio: + description: + GPIO controlling the USB D+ pull-up resistor used to signal device + connection to the host. + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + #include + #include + gadget@fffa4000 { + compatible = "atmel,at91rm9200-udc"; + reg = <0xfffa4000 0x4000>; + interrupts = <11 IRQ_TYPE_LEVEL_HIGH 2>; + clocks = <&udc_clk>, <&udpck>; + clock-names = "pclk", "hclk"; + atmel,vbus-gpio = <&pioC 5 GPIO_ACTIVE_HIGH>; + }; +... diff --git a/Documentation/devicetree/bindings/usb/atmel,at91sam9rl-udc.yaml b/Documentation/devicetree/bindings/usb/atmel,at91sam9rl-udc.yaml new file mode 100644 index 000000000000..cdbbd17f8036 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/atmel,at91sam9rl-udc.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/atmel,at91sam9rl-udc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Atmel High-Speed USB Device Controller (USBA) + +maintainers: + - Nicolas Ferre + - Alexandre Belloni + +description: + The Atmel High-Speed USB Device Controller (USBA) provides USB 2.0 + high-speed gadget functionality on several Atmel and Microchip SoCs. + The controller requires a peripheral clock and a host clock for operation + and may optionally use a GPIO to detect VBUS presence. + +properties: + compatible: + oneOf: + - enum: + - atmel,at91sam9rl-udc + - atmel,at91sam9g45-udc + - atmel,sama5d3-udc + - items: + - const: microchip,lan9662-udc + - const: atmel,sama5d3-udc + - const: microchip,sam9x60-udc + + reg: + maxItems: 2 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + minItems: 2 + maxItems: 2 + items: + enum: [pclk, hclk] + + atmel,vbus-gpio: + description: GPIO used to detect the presence of VBUS, indicating that + the USB cable is connected. + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + gadget@fff78000 { + compatible = "atmel,at91sam9g45-udc"; + reg = <0x00600000 0x80000 + 0xfff78000 0x400>; + interrupts = <27 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 27>, <&pmc PMC_TYPE_CORE PMC_UTMI>; + clock-names = "pclk", "hclk"; + atmel,vbus-gpio = <&pioC 15 GPIO_ACTIVE_HIGH>; + }; +... diff --git a/Documentation/devicetree/bindings/usb/atmel-usb.txt b/Documentation/devicetree/bindings/usb/atmel-usb.txt deleted file mode 100644 index 12183ef47ee4..000000000000 --- a/Documentation/devicetree/bindings/usb/atmel-usb.txt +++ /dev/null @@ -1,125 +0,0 @@ -Atmel SOC USB controllers - -OHCI - -Required properties: - - compatible: Should be "atmel,at91rm9200-ohci" for USB controllers - used in host mode. - - reg: Address and length of the register set for the device - - interrupts: Should contain ohci interrupt - - clocks: Should reference the peripheral, host and system clocks - - clock-names: Should contain three strings - "ohci_clk" for the peripheral clock - "hclk" for the host clock - "uhpck" for the system clock - - num-ports: Number of ports. - - atmel,vbus-gpio: If present, specifies a gpio that needs to be - activated for the bus to be powered. - - atmel,oc-gpio: If present, specifies a gpio that needs to be - activated for the overcurrent detection. - -usb0: ohci@500000 { - compatible = "atmel,at91rm9200-ohci", "usb-ohci"; - reg = <0x00500000 0x100000>; - clocks = <&uhphs_clk>, <&uhphs_clk>, <&uhpck>; - clock-names = "ohci_clk", "hclk", "uhpck"; - interrupts = <20 4>; - num-ports = <2>; -}; - -EHCI - -Required properties: - - compatible: Should be "atmel,at91sam9g45-ehci" for USB controllers - used in host mode. - - reg: Address and length of the register set for the device - - interrupts: Should contain ehci interrupt - - clocks: Should reference the peripheral and the UTMI clocks - - clock-names: Should contain two strings - "ehci_clk" for the peripheral clock - "usb_clk" for the UTMI clock - -Optional properties: - - phy_type : For multi port host USB controllers, should be one of - "utmi", or "hsic". - -usb1: ehci@800000 { - compatible = "atmel,at91sam9g45-ehci", "usb-ehci"; - reg = <0x00800000 0x100000>; - interrupts = <22 4>; - clocks = <&utmi>, <&uhphs_clk>; - clock-names = "usb_clk", "ehci_clk"; -}; - -AT91 USB device controller - -Required properties: - - compatible: Should be one of the following - "atmel,at91rm9200-udc" - "atmel,at91sam9260-udc" - "atmel,at91sam9261-udc" - "atmel,at91sam9263-udc" - - reg: Address and length of the register set for the device - - interrupts: Should contain macb interrupt - - clocks: Should reference the peripheral and the AHB clocks - - clock-names: Should contain two strings - "pclk" for the peripheral clock - "hclk" for the AHB clock - -Optional properties: - - atmel,vbus-gpio: If present, specifies a gpio that needs to be - activated for the bus to be powered. - -usb1: gadget@fffa4000 { - compatible = "atmel,at91rm9200-udc"; - reg = <0xfffa4000 0x4000>; - interrupts = <10 4>; - clocks = <&udc_clk>, <&udpck>; - clock-names = "pclk", "hclk"; - atmel,vbus-gpio = <&pioC 5 0>; -}; - -Atmel High-Speed USB device controller - -Required properties: - - compatible: Should be one of the following - "atmel,at91sam9rl-udc" - "atmel,at91sam9g45-udc" - "atmel,sama5d3-udc" - "microchip,sam9x60-udc" - "microchip,lan9662-udc" - For "microchip,lan9662-udc" the fallback "atmel,sama5d3-udc" - is required. - - reg: Address and length of the register set for the device - - interrupts: Should contain usba interrupt - - clocks: Should reference the peripheral and host clocks - - clock-names: Should contain two strings - "pclk" for the peripheral clock - "hclk" for the host clock - -Deprecated property: - - ep childnode: To specify the number of endpoints and their properties. - -Optional properties: - - atmel,vbus-gpio: If present, specifies a gpio that allows to detect whether - vbus is present (USB is connected). - -Deprecated child node properties: - - name: Name of the endpoint. - - reg: Num of the endpoint. - - atmel,fifo-size: Size of the fifo. - - atmel,nb-banks: Number of banks. - - atmel,can-dma: Boolean to specify if the endpoint support DMA. - - atmel,can-isoc: Boolean to specify if the endpoint support ISOC. - -usb2: gadget@fff78000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "atmel,at91sam9rl-udc"; - reg = <0x00600000 0x80000 - 0xfff78000 0x400>; - interrupts = <27 4 0>; - clocks = <&utmi>, <&udphs_clk>; - clock-names = "hclk", "pclk"; - atmel,vbus-gpio = <&pioB 19 0>; -}; diff --git a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml index f454ddd9bbaa..a199e5ba6416 100644 --- a/Documentation/devicetree/bindings/usb/cdns,usb3.yaml +++ b/Documentation/devicetree/bindings/usb/cdns,usb3.yaml @@ -85,6 +85,7 @@ required: allOf: - $ref: usb-drd.yaml# + - $ref: usb-xhci.yaml# unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/usb/corechips,sl6341.yaml b/Documentation/devicetree/bindings/usb/corechips,sl6341.yaml new file mode 100644 index 000000000000..82996791aaf1 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/corechips,sl6341.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/corechips,sl6341.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Corechips SL6341 USB 2.0/3.0 Hub Controller + +maintainers: + - Alexey Charkov + +allOf: + - $ref: usb-hub.yaml# + +properties: + compatible: + enum: + - usb3431,6241 + - usb3431,6341 + + reg: true + + peer-hub: true + + reset-gpios: + description: GPIO controlling the RSTN pin. + + vdd1v1-supply: + description: + The regulator that provides 1.1V core power to the hub. + + vdd3v3-supply: + description: + The regulator that provides 3.3V IO power to the hub. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + patternProperties: + '^port@': + $ref: /schemas/graph.yaml#/properties/port + + properties: + reg: + minimum: 1 + maximum: 4 + +required: + - compatible + - reg + - vdd1v1-supply + +unevaluatedProperties: false + +examples: + - | + #include + usb { + #address-cells = <1>; + #size-cells = <0>; + + /* 2.0 hub */ + hub_2_0: hub@1 { + compatible = "usb3431,6241"; + reg = <1>; + peer-hub = <&hub_3_0>; + reset-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; + vdd1v1-supply = <&vdd1v1_hub>; + }; + + /* 3.0 hub */ + hub_3_0: hub@2 { + compatible = "usb3431,6341"; + reg = <2>; + peer-hub = <&hub_2_0>; + reset-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; + vdd1v1-supply = <&vdd1v1_hub>; + }; + }; diff --git a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml index 73e7a60a0060..66d368e65c0a 100644 --- a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml @@ -10,6 +10,8 @@ title: NXP iMX8MP Soc USB Controller maintainers: - Li Jun +deprecated: true + properties: compatible: oneOf: diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index 601f097c09a6..55a5aa7d7a54 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -9,19 +9,6 @@ title: USB EHCI Controller maintainers: - Greg Kroah-Hartman -allOf: - - $ref: usb-hcd.yaml - - if: - properties: - compatible: - not: - contains: - const: ibm,usb-ehci-440epx - then: - properties: - reg: - maxItems: 1 - properties: compatible: oneOf: @@ -167,6 +154,39 @@ required: - reg - interrupts +allOf: + - $ref: usb-hcd.yaml + - if: + properties: + compatible: + not: + contains: + const: ibm,usb-ehci-440epx + then: + properties: + reg: + maxItems: 1 + - if: + properties: + compatible: + contains: + const: atmel,at91sam9g45-ehci + then: + properties: + clock-names: + items: + - const: usb_clk + - const: ehci_clk + + phy_type: + enum: + - utmi + - hsic + + required: + - clocks + - clock-names + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/usb/generic-ohci.yaml b/Documentation/devicetree/bindings/usb/generic-ohci.yaml index 961cbf85eeb5..d42f448fa204 100644 --- a/Documentation/devicetree/bindings/usb/generic-ohci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ohci.yaml @@ -55,6 +55,7 @@ properties: - ti,ohci-omap3 - items: - enum: + - atmel,at91rm9200-ohci - cavium,octeon-6335-ohci - nintendo,hollywood-usb-ohci - nxp,ohci-nxp @@ -137,6 +138,24 @@ properties: The associated ISP1301 device. Necessary for the UDC controller for connecting to the USB physical layer. + atmel,vbus-gpio: + description: + GPIO used to control or sense the USB VBUS power. Each entry + represents a VBUS-related GPIO; count and order may vary by hardware. + Entries follow standard GPIO specifier format. A value of 0 indicates + an unused or unavailable VBUS signal. + minItems: 1 + maxItems: 3 + + atmel,oc-gpio: + description: + GPIO used to signal USB overcurrent condition. Each entry represents + an OC detection GPIO; count and order may vary by hardware. Entries + follow standard GPIO specifier format. A value of 0 indicates an + unused or unavailable OC signal. + minItems: 1 + maxItems: 3 + required: - compatible - reg @@ -144,6 +163,28 @@ required: allOf: - $ref: usb-hcd.yaml + - if: + properties: + compatible: + contains: + const: atmel,at91rm9200-ohci + then: + properties: + clock-names: + items: + - const: ohci_clk + - const: hclk + - const: uhpck + + required: + - clocks + - clock-names + + else: + properties: + atmel,vbus-gpio: false + atmel,oc-gpio: false + - if: not: properties: diff --git a/Documentation/devicetree/bindings/usb/maxim,max33359.yaml b/Documentation/devicetree/bindings/usb/maxim,max33359.yaml index 3de4dc40b791..003c0b713068 100644 --- a/Documentation/devicetree/bindings/usb/maxim,max33359.yaml +++ b/Documentation/devicetree/bindings/usb/maxim,max33359.yaml @@ -32,6 +32,9 @@ properties: description: Properties for usb c connector. + vbus-supply: + description: Regulator to control sourcing Vbus. + required: - compatible - reg @@ -53,6 +56,7 @@ examples: reg = <0x25>; interrupt-parent = <&gpa8>; interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + vbus-supply = <&chgin_otg_reg>; connector { compatible = "usb-c-connector"; @@ -75,6 +79,10 @@ examples: PDO_FIXED(9000, 2000, 0)>; sink-bc12-completion-time-ms = <500>; pd-revision = /bits/ 8 <0x03 0x01 0x01 0x08>; + sink-load-step = <150>; + sink-load-characteristics = /bits/ 16 ; + sink-compliance = /bits/ 8 <(COMPLIANCE_LPS | COMPLIANCE_PS1)>; + charging-adapter-pdp-milliwatt = <18000>; }; }; }; diff --git a/Documentation/devicetree/bindings/usb/maxim,max3421.txt b/Documentation/devicetree/bindings/usb/maxim,max3421.txt deleted file mode 100644 index 90495b1aeec2..000000000000 --- a/Documentation/devicetree/bindings/usb/maxim,max3421.txt +++ /dev/null @@ -1,23 +0,0 @@ -Maxim Integrated SPI-based USB 2.0 host controller MAX3421E - -Required properties: - - compatible: Should be "maxim,max3421" - - spi-max-frequency: maximum frequency for this device must not exceed 26 MHz. - - reg: chip select number to which this device is connected. - - maxim,vbus-en-pin: - GPOUTx is the number (1-8) of the GPOUT pin of MAX3421E to drive Vbus. - ACTIVE_LEVEL is 0 or 1. - - interrupts: the interrupt line description for the interrupt controller. - The driver configures MAX3421E for active low level triggered interrupts, - configure your interrupt line accordingly. - -Example: - - usb@0 { - compatible = "maxim,max3421"; - reg = <0>; - maxim,vbus-en-pin = <3 1>; - spi-max-frequency = <26000000>; - interrupt-parent = <&PIC>; - interrupts = <42>; - }; diff --git a/Documentation/devicetree/bindings/usb/maxim,max3421.yaml b/Documentation/devicetree/bindings/usb/maxim,max3421.yaml new file mode 100644 index 000000000000..4639be7ab059 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/maxim,max3421.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/maxim,max3421.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MAXIM MAX3421e USB Peripheral/Host Controller + +maintainers: + - David Mosberger + +description: | + The controller provides USB2.0 compliant with Full Speed or Low Speed when in + the host mode. At peripheral, it operates at Full Speed. At both cases, it + uses a SPI interface. + Datasheet at: + https://www.analog.com/media/en/technical-documentation/data-sheets/max3421e.pdf + +properties: + compatible: + const: maxim,max3421 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + spi-max-frequency: + maximum: 26000000 + + maxim,vbus-en-pin: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + One of eight GPOUT pins to control external VBUS power and the polarity + of the active level. It's an array of GPIO number and the active level of it. + minItems: 2 + maxItems: 2 + +required: + - compatible + - reg + - interrupts + - maxim,vbus-en-pin + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + + usb@0 { + compatible = "maxim,max3421"; + reg = <0>; + maxim,vbus-en-pin = <3 1>; + spi-max-frequency = <26000000>; + interrupt-parent = <&gpio>; + interrupts = <42>; + }; + }; diff --git a/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml b/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml index a812317d8089..c4e1c2d73bdb 100644 --- a/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml +++ b/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml @@ -37,6 +37,9 @@ properties: clocks: maxItems: 1 + resets: + maxItems: 1 + microchip,ext-vbus-drv: description: Some ULPI USB PHYs do not support an internal VBUS supply and driving diff --git a/Documentation/devicetree/bindings/usb/nxp,imx-dwc3.yaml b/Documentation/devicetree/bindings/usb/nxp,imx-dwc3.yaml new file mode 100644 index 000000000000..1911e71f01eb --- /dev/null +++ b/Documentation/devicetree/bindings/usb/nxp,imx-dwc3.yaml @@ -0,0 +1,123 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright 2026 NXP +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/nxp,imx-dwc3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP i.MX Soc USB Controller + +maintainers: + - Xu Yang + +properties: + compatible: + oneOf: + - items: + - enum: + - nxp,imx94-dwc3 + - nxp,imx95-dwc3 + - const: nxp,imx8mp-dwc3 + - const: nxp,imx8mp-dwc3 + + reg: + items: + - description: DWC3 core registers + - description: HSIO Block Control registers + - description: Wrapper registers of dwc3 core + + reg-names: + items: + - const: core + - const: blkctl + - const: glue + + interrupts: + items: + - description: DWC3 controller interrupt + - description: Wakeup interrupt from glue logic + + interrupt-names: + items: + - const: dwc_usb3 + - const: wakeup + + iommus: + maxItems: 1 + + clocks: + items: + - description: System hsio root clock + - description: SoC Bus Clock for AHB/AXI/Native + - description: Reference clock for generating ITP when UTMI/ULPI PHY is suspended + - description: Suspend clock used for usb wakeup logic + + clock-names: + items: + - const: hsio + - const: bus_early + - const: ref + - const: suspend + + fsl,permanently-attached: + type: boolean + description: + Indicates if the device attached to a downstream port is + permanently attached + + fsl,disable-port-power-control: + type: boolean + description: + Indicates whether the host controller implementation includes port + power control. Defines Bit 3 in capability register (HCCPARAMS) + + fsl,over-current-active-low: + type: boolean + description: + Over current signal polarity is active low + + fsl,power-active-low: + type: boolean + description: + Power pad (PWR) polarity is active low + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - power-domains + +allOf: + - $ref: snps,dwc3-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + usb@4c100000 { + compatible = "nxp,imx94-dwc3", "nxp,imx8mp-dwc3"; + reg = <0x4c100000 0x10000>, + <0x4c010010 0x04>, + <0x4c1f0000 0x20>; + reg-names = "core", "blkctl", "glue"; + clocks = <&scmi_clk 74>, //IMX94_CLK_HSIO + <&scmi_clk 74>, //IMX94_CLK_HSIO + <&scmi_clk 2>, //IMX94_CLK_24M + <&scmi_clk 1>; //IMX94_CLK_32K + clock-names = "hsio", "bus_early", "ref", "suspend"; + interrupts = , + ; + interrupt-names = "dwc_usb3", "wakeup"; + power-domains = <&scmi_devpd 13>; //IMX94_PD_HSIO_TOP + phys = <&usb3_phy>, <&usb3_phy>; + phy-names = "usb2-phy", "usb3-phy"; + snps,gfladj-refclk-lpm-sel-quirk; + snps,parkmode-disable-ss-quirk; + }; diff --git a/Documentation/devicetree/bindings/usb/nxp,ptn5110.yaml b/Documentation/devicetree/bindings/usb/nxp,ptn5110.yaml index 65a8632b4d9e..581e5916eadd 100644 --- a/Documentation/devicetree/bindings/usb/nxp,ptn5110.yaml +++ b/Documentation/devicetree/bindings/usb/nxp,ptn5110.yaml @@ -26,6 +26,10 @@ properties: $ref: /schemas/connector/usb-connector.yaml# unevaluatedProperties: false + orientation-gpios: + maxItems: 1 + description: Optional orientation select control + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/usb/ohci-st.txt b/Documentation/devicetree/bindings/usb/ohci-st.txt deleted file mode 100644 index 1c735573abc0..000000000000 --- a/Documentation/devicetree/bindings/usb/ohci-st.txt +++ /dev/null @@ -1,36 +0,0 @@ -ST USB OHCI controller - -Required properties: - - - compatible : must be "st,st-ohci-300x" - - reg : physical base addresses of the controller and length of memory mapped - region - - interrupts : one OHCI controller interrupt should be described here - - clocks : phandle list of usb clocks - - clock-names : should be "ic" for interconnect clock and "clk48" -See: Documentation/devicetree/bindings/clock/clock-bindings.txt - - - phys : phandle for the PHY device - - phy-names : should be "usb" - - - resets : phandle to the powerdown and reset controller for the USB IP - - reset-names : should be "power" and "softreset". -See: Documentation/devicetree/bindings/reset/st,stih407-powerdown.yaml -See: Documentation/devicetree/bindings/reset/reset.txt - -Example: - - ohci0: usb@fe1ffc00 { - compatible = "st,st-ohci-300x"; - reg = <0xfe1ffc00 0x100>; - interrupts = ; - clocks = <&clk_s_a1_ls 0>, - <&clockgen_b0 0>; - clock-names = "ic", "clk48"; - phys = <&usb2_phy>; - phy-names = "usb"; - - resets = <&powerdown STIH416_USB0_POWERDOWN>, - <&softreset STIH416_USB0_SOFTRESET>; - reset-names = "power", "softreset"; - }; diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt deleted file mode 100644 index f0dbc5ae45ae..000000000000 --- a/Documentation/devicetree/bindings/usb/omap-usb.txt +++ /dev/null @@ -1,80 +0,0 @@ -OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS - -OMAP MUSB GLUE - - compatible : Should be "ti,omap4-musb" or "ti,omap3-musb" - - ti,hwmods : must be "usb_otg_hs" - - multipoint : Should be "1" indicating the musb controller supports - multipoint. This is a MUSB configuration-specific setting. - - num-eps : Specifies the number of endpoints. This is also a - MUSB configuration-specific setting. Should be set to "16" - - ram-bits : Specifies the ram address size. Should be set to "12" - - interface-type : This is a board specific setting to describe the type of - interface between the controller and the phy. It should be "0" or "1" - specifying ULPI and UTMI respectively. - - mode : Should be "3" to represent OTG. "1" signifies HOST and "2" - represents PERIPHERAL. - - power : Should be "50". This signifies the controller can supply up to - 100mA when operating in host mode. - - usb-phy : the phandle for the PHY device - - phys : the phandle for the PHY device (used by generic PHY framework) - - phy-names : the names of the PHY corresponding to the PHYs present in the - *phy* phandle. - -Optional properties: - - ctrl-module : phandle of the control module this glue uses to write to - mailbox - -SOC specific device node entry -usb_otg_hs: usb_otg_hs@4a0ab000 { - compatible = "ti,omap4-musb"; - ti,hwmods = "usb_otg_hs"; - multipoint = <1>; - num-eps = <16>; - ram-bits = <12>; - ctrl-module = <&omap_control_usb>; - phys = <&usb2_phy>; - phy-names = "usb2-phy"; -}; - -Board specific device node entry -&usb_otg_hs { - interface-type = <1>; - mode = <3>; - power = <50>; -}; - -OMAP DWC3 GLUE - - compatible : Should be - * "ti,dwc3" for OMAP5 and DRA7 - * "ti,am437x-dwc3" for AM437x - - ti,hwmods : Should be "usb_otg_ss" - - reg : Address and length of the register set for the device. - - interrupts : The irq number of this device that is used to interrupt the - MPU - - #address-cells, #size-cells : Must be present if the device has sub-nodes - - utmi-mode : controls the source of UTMI/PIPE status for VBUS and OTG ID. - It should be set to "1" for HW mode and "2" for SW mode. - - ranges: the child address space are mapped 1:1 onto the parent address space - -Optional Properties: - - extcon : phandle for the extcon device omap dwc3 uses to detect - connect/disconnect events. - - vbus-supply : phandle to the regulator device tree node if needed. - -Sub-nodes: -The dwc3 core should be added as subnode to omap dwc3 glue. -- dwc3 : - The binding details of dwc3 can be found in: - Documentation/devicetree/bindings/usb/snps,dwc3.yaml - -omap_dwc3 { - compatible = "ti,dwc3"; - ti,hwmods = "usb_otg_ss"; - reg = <0x4a020000 0x1ff>; - interrupts = <0 93 4>; - #address-cells = <1>; - #size-cells = <1>; - utmi-mode = <2>; - ranges; -}; - diff --git a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml index 7d784a648b7d..8201656b41ed 100644 --- a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml @@ -24,6 +24,7 @@ properties: compatible: items: - enum: + - qcom,eliza-dwc3 - qcom,glymur-dwc3 - qcom,glymur-dwc3-mp - qcom,ipq4019-dwc3 @@ -153,8 +154,6 @@ properties: wakeup-source: true -# Required child node: - required: - compatible - reg @@ -175,6 +174,7 @@ allOf: then: properties: clocks: + minItems: 3 maxItems: 3 clock-names: items: @@ -203,6 +203,7 @@ allOf: compatible: contains: enum: + - qcom,ipq5424-dwc3 - qcom,ipq9574-dwc3 - qcom,kaanapali-dwc3 - qcom,msm8953-dwc3 @@ -222,6 +223,7 @@ allOf: then: properties: clocks: + minItems: 5 maxItems: 5 clock-names: items: @@ -264,6 +266,7 @@ allOf: then: properties: clocks: + minItems: 4 maxItems: 4 clock-names: items: @@ -283,6 +286,7 @@ allOf: then: properties: clocks: + minItems: 4 maxItems: 4 clock-names: items: @@ -303,6 +307,7 @@ allOf: then: properties: clocks: + minItems: 9 maxItems: 9 clock-names: items: @@ -346,14 +351,17 @@ allOf: compatible: contains: enum: + - qcom,eliza-dwc3 - qcom,milos-dwc3 - qcom,qcm2290-dwc3 - qcom,qcs615-dwc3 - qcom,sar2130p-dwc3 - qcom,sc8180x-dwc3 - qcom,sc8180x-dwc3-mp + - qcom,sm4250-dwc3 - qcom,sm6115-dwc3 - qcom,sm6125-dwc3 + - qcom,sm6375-dwc3 - qcom,sm8150-dwc3 - qcom,sm8250-dwc3 - qcom,sm8450-dwc3 @@ -363,6 +371,7 @@ allOf: properties: clocks: minItems: 6 + maxItems: 6 clock-names: items: - const: cfg_noc @@ -404,6 +413,7 @@ allOf: then: properties: clocks: + minItems: 7 maxItems: 7 clock-names: items: @@ -446,6 +456,7 @@ allOf: - qcom,msm8996-dwc3 - qcom,qcs404-dwc3 - qcom,sdm660-dwc3 + - qcom,sm4250-dwc3 - qcom,sm6115-dwc3 - qcom,sm6125-dwc3 then: @@ -472,6 +483,7 @@ allOf: then: properties: interrupts: + minItems: 4 maxItems: 4 interrupt-names: items: @@ -480,6 +492,26 @@ allOf: - const: dp_hs_phy_irq - const: dm_hs_phy_irq + - if: + properties: + compatible: + contains: + enum: + - qcom,ipq5424-dwc3 + - qcom,ipq9574-dwc3 + then: + properties: + interrupts: + minItems: 5 + maxItems: 5 + interrupt-names: + items: + - const: dwc_usb3 + - const: pwr_event + - const: qusb2_phy + - const: dp_hs_phy_irq + - const: dm_hs_phy_irq + - if: properties: compatible: @@ -500,13 +532,14 @@ allOf: - const: pwr_event - const: dp_hs_phy_irq - const: dm_hs_phy_irq - - const: ss_phy_irq + - enum: [hs_phy_irq, ss_phy_irq] - if: properties: compatible: contains: enum: + - qcom,eliza-dwc3 - qcom,ipq4019-dwc3 - qcom,ipq8064-dwc3 - qcom,kaanapali-dwc3 @@ -523,8 +556,8 @@ allOf: - qcom,sdx55-dwc3 - qcom,sdx65-dwc3 - qcom,sdx75-dwc3 - - qcom,sm4250-dwc3 - qcom,sm6350-dwc3 + - qcom,sm6375-dwc3 - qcom,sm8150-dwc3 - qcom,sm8250-dwc3 - qcom,sm8350-dwc3 diff --git a/Documentation/devicetree/bindings/usb/renesas,upd720201-pci.yaml b/Documentation/devicetree/bindings/usb/renesas,upd720201-pci.yaml new file mode 100644 index 000000000000..4e890d0d2070 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/renesas,upd720201-pci.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/renesas,upd720201-pci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: UPD720201/UPD720202 USB 3.0 xHCI Host Controller (PCIe) + +maintainers: + - Neil Armstrong + +description: + UPD720201 USB 3.0 xHCI Host Controller via PCIe x1 Gen2 interface. + The UPD720202 supports up to two downstream ports, while UPD720201 + supports up to four downstream USB 3.0 rev1.0 ports. + +properties: + compatible: + enum: + - pci1912,0014 # UPD720201 + - pci1912,0015 # UPD720202 + + reg: + maxItems: 1 + + avdd33-supply: + description: +3.3 V power supply for analog circuit + + vdd10-supply: + description: +1.05 V power supply + + vdd33-supply: + description: +3.3 V power supply + +required: + - compatible + - reg + - avdd33-supply + - vdd10-supply + - vdd33-supply + +allOf: + - $ref: usb-xhci.yaml + +additionalProperties: true + +examples: + - | + pcie@0 { + reg = <0x0 0x1000>; + ranges = <0x02000000 0x0 0x100000 0x10000000 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + + usb-controller@0 { + compatible = "pci1912,0014"; + reg = <0x0 0x0 0x0 0x0 0x0>; + avdd33-supply = <&avdd33_reg>; + vdd10-supply = <&vdd10_reg>; + vdd33-supply = <&vdd33_reg>; + }; + }; diff --git a/Documentation/devicetree/bindings/usb/richtek,rt1711h.yaml b/Documentation/devicetree/bindings/usb/richtek,rt1711h.yaml index ae611f7e57ca..7ded36384518 100644 --- a/Documentation/devicetree/bindings/usb/richtek,rt1711h.yaml +++ b/Documentation/devicetree/bindings/usb/richtek,rt1711h.yaml @@ -18,11 +18,21 @@ description: | properties: compatible: - enum: - - richtek,rt1711h - - richtek,rt1715 + oneOf: + - enum: + - richtek,rt1711h + - richtek,rt1715 + - items: + - enum: + - hynetek,husb311 + - const: richtek,rt1711h + - items: + - enum: + - etekmicro,et7304 + - const: richtek,rt1715 description: - RT1711H support PD20, RT1715 support PD30 except Fast Role Swap. + RT1711H support PD20, ET7304 and RT1715 support PD30 except Fast Role Swap. + HUSB311 is a rebrand of RT1711H which is pin and register compatible. reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/usb/spacemit,k1-dwc3.yaml b/Documentation/devicetree/bindings/usb/spacemit,k1-dwc3.yaml index 0f0b5e061ca1..cc27b363ca79 100644 --- a/Documentation/devicetree/bindings/usb/spacemit,k1-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/spacemit,k1-dwc3.yaml @@ -27,7 +27,9 @@ allOf: properties: compatible: - const: spacemit,k1-dwc3 + enum: + - spacemit,k1-dwc3 + - spacemit,k3-dwc3 reg: maxItems: 1 @@ -42,11 +44,13 @@ properties: maxItems: 1 phys: + minItems: 1 items: - description: phandle to USB2/HS PHY - description: phandle to USB3/SS PHY phy-names: + minItems: 1 items: - const: usb2-phy - const: usb3-phy diff --git a/Documentation/devicetree/bindings/usb/st,st-ohci-300x.yaml b/Documentation/devicetree/bindings/usb/st,st-ohci-300x.yaml new file mode 100644 index 000000000000..a225bf5a2ee4 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/st,st-ohci-300x.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/st,st-ohci-300x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics USB OHCI Controller + +maintainers: + - Peter Griffin + +description: + The STMicroelectronics USB Open Host Controller Interface (OHCI) + compliant USB host controller found in ST platforms. The controller + provides full- and low-speed USB host functionality and interfaces + with an external USB PHY. It requires dedicated clock, reset, and + interrupt resources for proper operation. + +allOf: + - $ref: /schemas/usb/usb-hcd.yaml# + +properties: + compatible: + const: st,st-ohci-300x + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: ic + - const: clk48 + + phys: + maxItems: 1 + + phy-names: + items: + - const: usb + + resets: + maxItems: 2 + + reset-names: + items: + - const: power + - const: softreset + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - phys + - phy-names + - resets + - reset-names + +unevaluatedProperties: false + +examples: + - | + #include + #include + usb@fe1ffc00 { + compatible = "st,st-ohci-300x"; + reg = <0xfe1ffc00 0x100>; + interrupts = ; + clocks = <&clk_s_a1_ls 0>, + <&clockgen_b0 0>; + clock-names = "ic", "clk48"; + phys = <&usb2_phy>; + phy-names = "usb"; + resets = <&powerdown STIH407_USB2_PORT0_POWERDOWN>, + <&softreset STIH407_USB2_PORT0_SOFTRESET>; + reset-names = "power", "softreset"; + }; +... diff --git a/Documentation/devicetree/bindings/usb/starfive,jhb100-dwc3.yaml b/Documentation/devicetree/bindings/usb/starfive,jhb100-dwc3.yaml new file mode 100644 index 000000000000..fbabe99e9d5c --- /dev/null +++ b/Documentation/devicetree/bindings/usb/starfive,jhb100-dwc3.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/starfive,jhb100-dwc3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JHB100 DWC3 USB SoC Controller + +maintainers: + - Minda Chen + +description: + The USB DRD controller on JHB100 BMC SoC. + +allOf: + - $ref: snps,dwc3-common.yaml# + +properties: + compatible: + const: starfive,jhb100-dwc3 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: USB main enable clk + - description: DWC3 bus early clock + - description: DWC3 ref clock + + clock-names: + items: + - const: main + - const: bus_early + - const: ref + + resets: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + +unevaluatedProperties: false + +examples: + - | + usb@11800000 { + compatible = "starfive,jhb100-dwc3"; + reg = <0x11800000 0x10000>; + clocks = <&usbcrg 9>, + <&usbcrg 5>, + <&usbcrg 6>; + clock-names = "main", "bus_early", "ref"; + resets = <&usbcrg 4>; + interrupts = <105>; + dr_mode = "host"; + }; diff --git a/Documentation/devicetree/bindings/usb/terminus,fe11.yaml b/Documentation/devicetree/bindings/usb/terminus,fe11.yaml new file mode 100644 index 000000000000..645f97d73807 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/terminus,fe11.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/terminus,fe11.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Terminus FE1.1/1.1S USB 2.0 Hub Controller + +maintainers: + - Yixun Lan + +allOf: + - $ref: usb-hub.yaml# + +properties: + compatible: + enum: + - usb1a40,0101 + + reg: true + + reset-gpios: + description: + GPIO controlling the RESET#. + + vdd-supply: + description: + Regulator supply to the hub, one of 3.3V or 5V can be chosen. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + patternProperties: + '^port@': + $ref: /schemas/graph.yaml#/properties/port + + properties: + reg: + minimum: 1 + maximum: 4 + +required: + - compatible + - reg + - vdd-supply + +unevaluatedProperties: false + +examples: + - | + #include + usb { + #address-cells = <1>; + #size-cells = <0>; + + hub@1 { + compatible = "usb1a40,0101"; + reg = <1>; + reset-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; + vdd-supply = <&vcc_5v>; + }; + }; diff --git a/Documentation/devicetree/bindings/usb/ti,dwc3.yaml b/Documentation/devicetree/bindings/usb/ti,dwc3.yaml new file mode 100644 index 000000000000..77ac11c3b2db --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ti,dwc3.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/ti,dwc3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments OMAP DWC3 USB Glue Layer + +maintainers: + - Felipe Balbi + +description: + Texas Instruments glue layer for Synopsys DesignWare USB3 (DWC3) + controller on OMAP and AM43xx SoCs. Manages SoC-specific integration + including register mapping, interrupt routing, UTMI/PIPE interface mode + selection (HW/SW), and child DWC3 core instantiation via address space + translation. Supports both legacy single-instance and multi-instance + (numbered) configurations. + +properties: + compatible: + enum: + - ti,dwc3 + - ti,am437x-dwc3 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + utmi-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Controls the source of UTMI/PIPE status for VBUS and OTG ID. + 1 for HW mode, 2 for SW mode. + enum: [1, 2] + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + + extcon: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle for the extcon device used to detect connect/ + disconnect events. + + vbus-supply: + description: Phandle to the regulator device tree node if needed. + +patternProperties: + "^usb@[0-9a-f]+$": + type: object + $ref: snps,dwc3.yaml# + unevaluatedProperties: false + +required: + - reg + - compatible + - interrupts + - "#address-cells" + - "#size-cells" + - utmi-mode + - ranges + +unevaluatedProperties: false + +examples: + - | + #include + omap_dwc3_1@0 { + compatible = "ti,dwc3"; + reg = <0x0 0x10000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <1>; + utmi-mode = <2>; + ranges = <0 0 0x20000>; + + usb@10000 { + compatible = "snps,dwc3"; + reg = <0x10000 0x17000>; + interrupts = , + , + ; + interrupt-names = "peripheral", "host", "otg"; + phys = <&usb2_phy1>, <&usb3_phy1>; + phy-names = "usb2-phy", "usb3-phy"; + maximum-speed = "super-speed"; + dr_mode = "otg"; + snps,dis_u3_susphy_quirk; + snps,dis_u2_susphy_quirk; + }; + }; +... diff --git a/Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml b/Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml new file mode 100644 index 000000000000..a3d15f217658 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/ti,omap4-musb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments OMAP MUSB USB OTG Controller + +maintainers: + - Felipe Balbi + +description: + Texas Instruments glue layer for the Mentor Graphics MUSB OTG controller. + Handles SoC-specific integration including PHY interface bridging(ULPI/ + UTMI), interrupt aggregation, DMA engine coordination (internal/ + external), VBUS/session control via control module mailbox, and + clock/reset management. Provides fixed hardware configuration parameters + to the generic MUSB core driver. + +properties: + compatible: + enum: + - ti,omap3-musb + - ti,omap4-musb + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + items: + - const: mc + - const: dma + + multipoint: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Indicates the MUSB controller supports multipoint. This is a MUSB + configuration-specific setting. + const: 1 + + num-eps: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Specifies the number of endpoints. This is a MUSB configuration + specific setting. + const: 16 + + ram-bits: + description: Specifies the RAM address size. + const: 12 + + interface-type: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Describes the type of interface between the controller and the PHY. + 0 for ULPI, 1 for UTMI. + enum: [0, 1] + + mode: + $ref: /schemas/types.yaml#/definitions/uint32 + description: 1 for HOST, 2 for PERIPHERAL, 3 for OTG. + enum: [1, 2, 3] + + power: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Indicates the maximum current the controller can supply when + operating in host mode. A value of 50 corresponds to 100 mA, and a + value of 150 corresponds to 300 mA. + enum: [50, 150] + + phys: + maxItems: 1 + + phy-names: + const: usb2-phy + + usb-phy: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: Phandle for the PHY device. + deprecated: true + + ctrl-module: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle of the control module this glue uses to write to mailbox. + +required: + - reg + - compatible + - interrupts + - interrupt-names + +unevaluatedProperties: false + +examples: + - | + #include + usb@4a0ab000 { + compatible = "ti,omap4-musb"; + reg = <0x4a0ab000 0x1000>; + interrupts = , + ; + interrupt-names = "mc", "dma"; + multipoint = <1>; + num-eps = <16>; + ram-bits = <12>; + ctrl-module = <&omap_control_usb>; + phys = <&usb2_phy>; + phy-names = "usb2-phy"; + interface-type = <1>; + mode = <3>; + power = <50>; + }; +... diff --git a/Documentation/devicetree/bindings/usb/ti,usb8041.yaml b/Documentation/devicetree/bindings/usb/ti,usb8041.yaml index 5e3eae9c2961..07e13fae640b 100644 --- a/Documentation/devicetree/bindings/usb/ti,usb8041.yaml +++ b/Documentation/devicetree/bindings/usb/ti,usb8041.yaml @@ -11,6 +11,7 @@ maintainers: allOf: - $ref: usb-device.yaml# + - $ref: usb-hub.yaml# properties: compatible: @@ -30,17 +31,20 @@ properties: description: VDD power supply to the hub - peer-hub: - $ref: /schemas/types.yaml#/definitions/phandle - description: - phandle to the peer hub on the controller. + peer-hub: true + +patternProperties: + '^.*@[1-9a-f][0-9a-f]*$': + description: The hard wired USB devices + type: object + $ref: /schemas/usb/usb-device.yaml + additionalProperties: true required: - compatible - reg - - peer-hub -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -56,7 +60,14 @@ examples: compatible = "usb451,8142"; reg = <1>; peer-hub = <&hub_3_0>; + #address-cells = <1>; + #size-cells = <0>; reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>; + + hub@1 { + compatible = "usb123,4567"; + reg = <1>; + }; }; /* 3.0 hub on port 2 */ diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 00bffb9c4469..28784d66ae7b 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -223,6 +223,8 @@ patternProperties: description: Axiado Corporation "^axis,.*": description: Axis Communications AB + "^ayaneo,.*": + description: Anyun Intelligent Technology (Hong Kong) Co., Ltd "^azoteq,.*": description: Azoteq (Pty) Ltd "^azw,.*": @@ -363,6 +365,8 @@ patternProperties: description: CORERIVER Semiconductor Co.,Ltd. "^corpro,.*": description: Chengdu Corpro Technology Co., Ltd. + "^corechips,.*": + description: Shenzhen Corechips Microelectronics Co., Ltd. "^cortina,.*": description: Cortina Systems, Inc. "^cosmic,.*": @@ -545,6 +549,8 @@ patternProperties: description: ESTeem Wireless Modems "^eswin,.*": description: Beijing ESWIN Technology Group Co. Ltd. + "^etekmicro,.*": + description: Wuxi ETEK Micro-Electronics Co.,Ltd. "^ettus,.*": description: NI Ettus Research "^eukrea,.*": @@ -749,6 +755,8 @@ patternProperties: description: Hycon Technology Corp. "^hydis,.*": description: Hydis Technologies + "^hynetek,.*": + description: Hynetek Semiconductor Co., Ltd. "^hynitron,.*": description: Shanghai Hynitron Microelectronics Co. Ltd. "^hynix,.*": @@ -1207,6 +1215,8 @@ patternProperties: description: One Laptop Per Child "^oneplus,.*": description: OnePlus Technology (Shenzhen) Co., Ltd. + "^onething,.*": + description: Shenzhen OneThing Technologies Co., Ltd. "^onie,.*": description: Open Network Install Environment group "^onion,.*": @@ -1741,6 +1751,8 @@ patternProperties: description: Ufi Space Co., Ltd. "^ugoos,.*": description: Ugoos Industrial Co., Ltd. + "^ultrapower,.*": + description: Beijing Ultrapower Software Co., Ltd. "^uni-t,.*": description: Uni-Trend Technology (China) Co., Ltd. "^uniwest,.*": @@ -1833,6 +1845,8 @@ patternProperties: description: Wi2Wi, Inc. "^widora,.*": description: Beijing Widora Technology Co., Ltd. + "^wiko,.*": + description: Wiko SAS "^wiligear,.*": description: Wiligear, Ltd. "^willsemi,.*": diff --git a/Documentation/devicetree/of_unittest.rst b/Documentation/devicetree/of_unittest.rst index 8b557acd29d1..6ed6e3291964 100644 --- a/Documentation/devicetree/of_unittest.rst +++ b/Documentation/devicetree/of_unittest.rst @@ -48,30 +48,30 @@ from 'scripts/dtc/of_unittest_expect --help'. 3. Test-data ============ -The Device Tree Source file (drivers/of/unittest-data/testcases.dts) contains +The Device Tree Source file (drivers/of/unittest-data/testcases.dtso) contains the test data required for executing the unit tests automated in drivers/of/unittest.c. See the content of the folder:: drivers/of/unittest-data/tests-*.dtsi -for the Device Tree Source Include files (.dtsi) included in testcases.dts. +for the Device Tree Source Include files (.dtsi) included in testcases.dtso. When the kernel is built with CONFIG_OF_UNITTEST enabled, then the following make rule:: - $(obj)/%.dtb: $(src)/%.dts FORCE - $(call if_changed_dep, dtc) + $(obj)/%.dtbo: $(src)/%.dtso $(DTC) FORCE + $(call if_changed_dep,dtc) -is used to compile the DT source file (testcases.dts) into a binary blob -(testcases.dtb), also referred as flattened DT. +is used to compile the DT source file (testcases.dtso) into a binary blob +(testcases.dtbo), also referred as flattened DT. After that, using the following rule the binary blob above is wrapped as an -assembly file (testcases.dtb.S):: +assembly file (testcases.dtbo.S):: - $(obj)/%.dtb.S: $(obj)/%.dtb - $(call cmd, dt_S_dtb) + $(obj)/%.dtbo.S: $(obj)/%.dtbo FORCE + $(call if_changed,wrap_S_dtb) -The assembly file is compiled into an object file (testcases.dtb.o), and is +The assembly file is compiled into an object file (testcases.dtbo.o), and is linked into the kernel image. diff --git a/Documentation/driver-api/clk.rst b/Documentation/driver-api/clk.rst index 93bab5336dfd..c6aca8186a78 100644 --- a/Documentation/driver-api/clk.rst +++ b/Documentation/driver-api/clk.rst @@ -77,9 +77,6 @@ the operations defined in clk-provider.h:: void (*disable_unused)(struct clk_hw *hw); unsigned long (*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate); - long (*round_rate)(struct clk_hw *hw, - unsigned long rate, - unsigned long *parent_rate); int (*determine_rate)(struct clk_hw *hw, struct clk_rate_request *req); int (*set_parent)(struct clk_hw *hw, u8 index); @@ -220,9 +217,7 @@ optional or must be evaluated on a case-by-case basis. +----------------+------+-------------+---------------+-------------+------+ |.recalc_rate | | y | | | | +----------------+------+-------------+---------------+-------------+------+ - |.round_rate | | y [1]_ | | | | - +----------------+------+-------------+---------------+-------------+------+ - |.determine_rate | | y [1]_ | | | | + |.determine_rate | | y | | | | +----------------+------+-------------+---------------+-------------+------+ |.set_rate | | y | | | | +----------------+------+-------------+---------------+-------------+------+ @@ -238,8 +233,6 @@ optional or must be evaluated on a case-by-case basis. |.init | | | | | | +----------------+------+-------------+---------------+-------------+------+ -.. [1] either one of round_rate or determine_rate is required. - Finally, register your clock at run-time with a hardware-specific registration function. This function simply populates struct clk_foo's data and then passes the common struct clk parameters to the framework diff --git a/Documentation/driver-api/infiniband.rst b/Documentation/driver-api/infiniband.rst index 10d8be9e74fe..d48f246774d2 100644 --- a/Documentation/driver-api/infiniband.rst +++ b/Documentation/driver-api/infiniband.rst @@ -92,21 +92,6 @@ iSCSI Extensions for RDMA (iSER) .. kernel-doc:: drivers/infiniband/ulp/iser/iser_verbs.c :internal: -Omni-Path (OPA) Virtual NIC support ------------------------------------ - -.. kernel-doc:: drivers/infiniband/ulp/opa_vnic/opa_vnic_internal.h - :internal: - -.. kernel-doc:: drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.h - :internal: - -.. kernel-doc:: drivers/infiniband/ulp/opa_vnic/opa_vnic_vema_iface.c - :internal: - -.. kernel-doc:: drivers/infiniband/ulp/opa_vnic/opa_vnic_vema.c - :internal: - InfiniBand SCSI RDMA protocol target support -------------------------------------------- diff --git a/Documentation/driver-api/reset.rst b/Documentation/driver-api/reset.rst index f773100daaa4..7a6571849664 100644 --- a/Documentation/driver-api/reset.rst +++ b/Documentation/driver-api/reset.rst @@ -198,7 +198,6 @@ query the reset line status using reset_control_status(). reset_control_rearm reset_control_put of_reset_control_get_count - of_reset_control_array_get devm_reset_control_array_get reset_control_get_count diff --git a/Documentation/features/seccomp/seccomp-filter/arch-support.txt b/Documentation/features/seccomp/seccomp-filter/arch-support.txt index 13feb679649e..9336bdfc125c 100644 --- a/Documentation/features/seccomp/seccomp-filter/arch-support.txt +++ b/Documentation/features/seccomp/seccomp-filter/arch-support.txt @@ -6,7 +6,7 @@ ----------------------- | arch |status| ----------------------- - | alpha: | TODO | + | alpha: | ok | | arc: | TODO | | arm: | ok | | arm64: | ok | diff --git a/Documentation/features/vm/ioremap_prot/arch-support.txt b/Documentation/features/vm/ioremap_prot/arch-support.txt index 1638c2cb17f1..c0a2d8f56046 100644 --- a/Documentation/features/vm/ioremap_prot/arch-support.txt +++ b/Documentation/features/vm/ioremap_prot/arch-support.txt @@ -20,7 +20,7 @@ | openrisc: | TODO | | parisc: | TODO | | powerpc: | ok | - | riscv: | TODO | + | riscv: | ok | | s390: | ok | | sh: | ok | | sparc: | TODO | diff --git a/Documentation/filesystems/index.rst b/Documentation/filesystems/index.rst index 6cbc3e0292ae..fc7254d01a2b 100644 --- a/Documentation/filesystems/index.rst +++ b/Documentation/filesystems/index.rst @@ -99,6 +99,7 @@ Documentation for filesystem implementations. isofs nilfs2 nfs/index + ntfs ntfs3 ocfs2 ocfs2-online-filecheck diff --git a/Documentation/filesystems/isofs.rst b/Documentation/filesystems/isofs.rst index 08fd469091d4..2a30999b024f 100644 --- a/Documentation/filesystems/isofs.rst +++ b/Documentation/filesystems/isofs.rst @@ -57,7 +57,7 @@ Mount options unique to the isofs filesystem. Recommended documents about ISO 9660 standard are located at: - http://www.y-adagio.com/ -- ftp://ftp.ecma.ch/ecma-st/Ecma-119.pdf +- https://ecma-international.org/wp-content/uploads/ECMA-119_2nd_edition_december_1987.pdf Quoting from the PDF "This 2nd Edition of Standard ECMA-119 is technically identical with ISO 9660.", so it is a valid and gratis substitute of the diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst index 8025df6e6499..8421ea21bd35 100644 --- a/Documentation/filesystems/locking.rst +++ b/Documentation/filesystems/locking.rst @@ -398,6 +398,7 @@ prototypes:: bool (*lm_breaker_owns_lease)(struct file_lock *); bool (*lm_lock_expirable)(struct file_lock *); void (*lm_expire_lock)(void); + bool (*lm_breaker_timedout)(struct file_lease *); locking rules: @@ -412,6 +413,7 @@ lm_breaker_owns_lease: yes no no lm_lock_expirable yes no no lm_expire_lock no no yes lm_open_conflict yes no no +lm_breaker_timedout yes no no ====================== ============= ================= ========= buffer_head diff --git a/Documentation/filesystems/nfs/exporting.rst b/Documentation/filesystems/nfs/exporting.rst index a01d9b9b5bc3..4aa59b0bf253 100644 --- a/Documentation/filesystems/nfs/exporting.rst +++ b/Documentation/filesystems/nfs/exporting.rst @@ -206,3 +206,88 @@ following flags are defined: all of an inode's dirty data on last close. Exports that behave this way should set EXPORT_OP_FLUSH_ON_CLOSE so that NFSD knows to skip waiting for writeback when closing such files. + +Signed Filehandles +------------------ + +To protect against filehandle guessing attacks, the Linux NFS server can be +configured to sign filehandles with a Message Authentication Code (MAC). + +Standard NFS filehandles are often predictable. If an attacker can guess +a valid filehandle for a file they do not have permission to access via +directory traversal, they may be able to bypass path-based permissions +(though they still remain subject to inode-level permissions). + +Signed filehandles prevent this by appending a MAC to the filehandle +before it is sent to the client. Upon receiving a filehandle back from a +client, the server re-calculates the MAC using its internal key and +verifies it against the one provided. If the signatures do not match, +the server treats the filehandle as invalid (returning NFS[34]ERR_STALE). + +Note that signing filehandles provides integrity and authenticity but +not confidentiality. The contents of the filehandle remain visible to +the client; they simply cannot be forged or modified. + +Configuration +~~~~~~~~~~~~~ + +To enable signed filehandles, the administrator must provide a signing +key to the kernel and enable the "sign_fh" export option. + +1. Providing a Key + The signing key is managed via the nfsd netlink interface. This key + is per-network-namespace and must be set before any exports using + "sign_fh" become active. + +2. Export Options + The feature is controlled on a per-export basis in /etc/exports: + + sign_fh + Enables signing for all filehandles generated under this export. + + no_sign_fh + (Default) Disables signing. + +Key Management and Rotation +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The security of this mechanism relies entirely on the secrecy of the +signing key. + +Initial Setup: + The key should be generated using a high-quality random source and + loaded early in the boot process or during the nfs-server startup + sequence. + +Changing Keys: + If a key is changed while clients have active mounts, existing + filehandles held by those clients will become invalid, resulting in + "Stale file handle" errors on the client side. + +Safe Rotation: + Currently, there is no mechanism for "graceful" key rotation + (maintaining multiple valid keys). Changing the key is an atomic + operation that immediately invalidates all previous signatures. + +Transitioning Exports +~~~~~~~~~~~~~~~~~~~~~ + +When adding or removing the "sign_fh" flag from an active export, the +following behaviors should be expected: + ++-------------------+---------------------------------------------------+ +| Change | Result for Existing Clients | ++===================+===================================================+ +| Adding sign_fh | Clients holding unsigned filehandles will find | +| | them rejected, as the server now expects a | +| | signature. | ++-------------------+---------------------------------------------------+ +| Removing sign_fh | Clients holding signed filehandles will find them | +| | rejected, as the server now expects the | +| | filehandle to end at its traditional boundary | +| | without a MAC. | ++-------------------+---------------------------------------------------+ + +Because filehandles are often cached persistently by clients, adding or +removing this option should generally be done during a scheduled maintenance +window involving a NFS client unmount/remount. diff --git a/Documentation/filesystems/ntfs.rst b/Documentation/filesystems/ntfs.rst new file mode 100644 index 000000000000..5c96b04a4d7a --- /dev/null +++ b/Documentation/filesystems/ntfs.rst @@ -0,0 +1,159 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================================= +The Linux NTFS filesystem driver +================================= + + +.. Table of contents + + - Overview + - Utilities support + - Supported mount options + + +Overview +======== + +NTFS is a Linux kernel filesystem driver that provides full read and write +support for NTFS volumes. It is designed for high performance, modern +kernel infrastructure (iomap, folio), and stable long-term maintenance. + + +Utilities support +================= + +The NTFS utilities project, called ntfsprogs-plus, provides mkfs.ntfs, +fsck.ntfs, and other related tools (e.g., ntfsinfo, ntfsclone, etc.) for +creating, checking, and managing NTFS volumes. These utilities can be used +for filesystem testing with xfstests as well as for recovering corrupted +NTFS devices. + +The project is available at: + + https://github.com/ntfsprogs-plus/ntfsprogs-plus + + +Supported mount options +======================= + +The NTFS driver supports the following mount options: + +======================= ==================================================== +iocharset=name Character set to use for converting between + the encoding is used for user visible filename and + 16 bit Unicode characters. + +nls=name Deprecated option. Still supported but please use + iocharset=name in the future. + +uid= +gid= +umask= Provide default owner, group, and access mode mask. + These options work as documented in mount(8). By + default, the files/directories are owned by root + and he/she has read and write permissions, as well + as browse permission for directories. No one else + has any access permissions. I.e. the mode on all + files is by default rw------- and + for directories rwx------, a consequence of + the default fmask=0177 and dmask=0077. + Using a umask of zero will grant all permissions to + everyone, i.e. all files and directories will have + mode rwxrwxrwx. + +fmask= +dmask= Instead of specifying umask which applies both to + files and directories, fmask applies only to files + and dmask only to directories. + +showmeta= +show_sys_files= If show_sys_files is specified, show the system + files in directory listings. Otherwise the default + behaviour is to hide the system files. + Note that even when show_sys_files is specified, + "$MFT" will not be visible due to bugs/mis-features + in glibc. Further, note that irrespective of + show_sys_files, all files are accessible by name, + i.e. you can always do "ls -l \$UpCase" for example + to specifically show the system file containing + the Unicode upcase table. + +case_sensitive= If case_sensitive is specified, treat all filenames + as case sensitive and create file names in + the POSIX namespace (default behavior). Note, + the Linux NTFS driver will never create short + filenames and will remove them on rename/delete of + the corresponding long file name. Note that files + remain accessible via their short file name, if it + exists. + +nocase= If nocase is specified, treat filenames + case-insensitively. + +disable_sparse= If disable_sparse is specified, creation of sparse + regions, i.e. holes, inside files is disabled for + the volume (for the duration of this mount only). + By default, creation of sparse regions is enabled, + which is consistent with the behaviour of + traditional Unix filesystems. + +errors=opt Specify NTFS behavior on critical errors: panic, + remount the partition in read-only mode or + continue without doing anything (default behavior). + +mft_zone_multiplier= Set the MFT zone multiplier for the volume (this + setting is not persistent across mounts and can be + changed from mount to mount but cannot be changed + on remount). Values of 1 to 4 are allowed, 1 being + the default. The MFT zone multiplier determines + how much space is reserved for the MFT on the + volume. If all other space is used up, then the + MFT zone will be shrunk dynamically, so this has no + impact on the amount of free space. However, it + can have an impact on performance by affecting + fragmentation of the MFT. In general use the + default. If you have a lot of small files then use + a higher value. The values have the following + meaning: + + ===== ================================= + Value MFT zone size (% of volume size) + ===== ================================= + 1 12.5% + 2 25% + 3 37.5% + 4 50% + ===== ================================= + + Note this option is irrelevant for read-only mount. + +preallocated_size= Set preallocated size to optimize runlist merge + overhead with small chunck size.(64KB size by + default) + +acl= Enable POSIX ACL support. When specified, POSIX + ACLs stored in extended attributes are enforced. + Default is off. Requires kernel config + NTFS_FS_POSIX_ACL enabled. + +sys_immutable= Make NTFS system files (e.g. $MFT, $LogFile, + $Bitmap, $UpCase, etc.) immutable to user initiated + modifications for extra safety. Default is off. + +nohidden= Hide files and directories marked with the Windows + "hidden" attribute. By default hidden items are + shown. + +hide_dot_files= Hide names beginning with a dot ("."). By default + dot files are shown. When enabled, files and + directories created with a leading '.' will be + hidden from directory listings. + +windows_names= Refuse creation/rename of files with characters or + reserved device names disallowed on Windows (e.g. + CON, NUL, AUX, COM1, LPT1, etc.). Default is off. +discard= Issue block device discard for clusters freed on + file deletion/truncation to inform underlying + storage. +======================= ==================================================== diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst index d02aa57e4477..fdf074429cd3 100644 --- a/Documentation/filesystems/porting.rst +++ b/Documentation/filesystems/porting.rst @@ -1368,6 +1368,7 @@ lifetime, consider using inode_set_cached_link() instead. lookup_one_qstr_excl() is no longer exported - use start_creating() or similar. + --- ** mandatory** @@ -1375,3 +1376,12 @@ similar. lock_rename(), lock_rename_child(), unlock_rename() are no longer available. Use start_renaming() or similar. +--- + +**recommended** + +If you really need to iterate through dentries for given inode, use +for_each_alias(dentry, inode) instead of hlist_for_each_entry; better +yet, see if any of the exported primitives could be used instead of +the entire loop. You still need to hold ->i_lock of the inode over +either form of manual loop. diff --git a/Documentation/filesystems/proc.rst b/Documentation/filesystems/proc.rst index 7a4e67a04290..db6167befb7b 100644 --- a/Documentation/filesystems/proc.rst +++ b/Documentation/filesystems/proc.rst @@ -565,6 +565,10 @@ does not take into account swapped out page of underlying shmem objects. naturally aligned THP pages of any currently enabled size. 1 if true, 0 otherwise. +If both the kernel and the CPU support protection keys (pkeys), +"ProtectionKey" indicates the memory protection key associated with the +virtual memory area. + "VmFlags" field deserves a separate description. This member represents the kernel flags associated with the particular virtual memory area in two letter encoded manner. The codes are the following: diff --git a/Documentation/hwmon/sy7636a-hwmon.rst b/Documentation/hwmon/sy7636a-hwmon.rst index 0143ce0e5db7..03d866aba6e8 100644 --- a/Documentation/hwmon/sy7636a-hwmon.rst +++ b/Documentation/hwmon/sy7636a-hwmon.rst @@ -22,5 +22,5 @@ The following sensors are supported sysfs-Interface --------------- -temp0_input +temp1_input - Temperature of external NTC (milli-degree C) diff --git a/Documentation/hwmon/yogafan.rst b/Documentation/hwmon/yogafan.rst index c553a381f772..68761947a1a8 100644 --- a/Documentation/hwmon/yogafan.rst +++ b/Documentation/hwmon/yogafan.rst @@ -135,4 +135,4 @@ References 4. **Lenovo IdeaPad Laptop Driver:** Reference for DMI-based hardware feature gating in Lenovo laptops. - https://github.com/torvalds/linux/blob/master/drivers/platform/x86/ideapad-laptop.c + https://github.com/torvalds/linux/blob/master/drivers/platform/x86/lenovo/ideapad-laptop.c diff --git a/Documentation/iio/ad4030.rst b/Documentation/iio/ad4030.rst index b57424b650a8..9caafa4148b0 100644 --- a/Documentation/iio/ad4030.rst +++ b/Documentation/iio/ad4030.rst @@ -92,6 +92,45 @@ Interleaved mode In this mode, both channels conversion results are bit interleaved one SDO line. As such the wiring is the same as `One lane mode`_. +SPI offload wiring +^^^^^^^^^^^^^^^^^^ + +.. code-block:: + + +-------------+ +-------------+ + | CNV |<-----+--| GPIO | + | | +--| PWM0 | + | | | | + | | +--| PWM1 | + | | | +-------------+ + | | +->| TRIGGER | + | CS |<--------| CS | + | | | | + | ADC | | SPI | + | | | | + | SDI |<--------| SDO | + | SDO |-------->| SDI | + | SCLK |<--------| SCLK | + +-------------+ +-------------+ + +In this mode, both the ``cnv-gpios`` and a ``pwms`` properties are required. +The ``pwms`` property specifies the PWM that is connected to the ADC CNV pin. +The SPI offload will have a ``trigger-sources`` property to indicate the SPI +offload (PWM) trigger source. For AD4030 and similar ADCs, there are two +possible data transfer zones for sample N. One of them (zone 1) starts after the +data conversion for sample N is complete while the other one (zone 2) starts 9.8 +nanoseconds after the rising edge of CNV for sample N + 1. + +The configuration depicted in the above diagram is intended to perform data +transfer in zone 2. To achieve high sample rates while meeting ADC timing +requirements, an offset is added between the rising edges of PWM0 and PWM1 to +delay the SPI transfer until 9.8 nanoseconds after CNV rising edge. This +requires a specialized PWM controller that can provide such an offset. +The `AD4630-FMC HDL project`_, for example, can be configured to sample AD4030 +data during zone 2 data read window. + +.. _AD4630-FMC HDL project: https://analogdevicesinc.github.io/hdl/projects/ad4630_fmc/index.html + SPI Clock mode -------------- diff --git a/Documentation/iio/ad7191.rst b/Documentation/iio/ad7191.rst index 977d4fea14b0..fd6a23ad44fd 100644 --- a/Documentation/iio/ad7191.rst +++ b/Documentation/iio/ad7191.rst @@ -63,11 +63,11 @@ Clock Configuration The AD7191 supports both internal and external clock sources: -- When CLKSEL pin is tied LOW: Uses internal 4.92MHz clock (no clock property +- When CLKSEL pin is ACTIVE: Uses internal 4.92MHz clock (no clock property needed) -- When CLKSEL pin is tied HIGH: Requires external clock source +- When CLKSEL pin is INACTIVE: Requires external clock source - Can be a crystal between MCLK1 and MCLK2 pins - - Or a CMOS-compatible clock driving MCLK2 pin + - Or a CMOS-compatible clock driving MCLK1 pin and MCLK2 left unconnected - Must specify the "clocks" property in device tree when using external clock SPI Interface Requirements diff --git a/Documentation/iio/adxl345.rst b/Documentation/iio/adxl345.rst index bb19d64f67c3..978f746a8198 100644 --- a/Documentation/iio/adxl345.rst +++ b/Documentation/iio/adxl345.rst @@ -12,16 +12,21 @@ This driver supports Analog Device's ADXL345/375 on SPI/I2C bus. * `ADXL345 `_ * `ADXL375 `_ -The ADXL345 is a generic purpose low power, 3-axis accelerometer with selectable -measurement ranges. The ADXL345 supports the ±2 g, ±4 g, ±8 g, and ±16 g ranges. +The ADXL345 is a general-purpose, low-power, 3-axis accelerometer with selectable +measurement ranges. The ADXL345 supports the following ranges: + +- ±2g (approx. ±19.61 m/s^2) +- ±4g (approx. ±39.23 m/s^2) +- ±8g (approx. ±78.45 m/s^2) +- ±16g (approx. ±156.91 m/s^2) 2. Device Attributes ==================== -Each IIO device, has a device folder under ``/sys/bus/iio/devices/iio:deviceX``, +Each IIO device has a device folder under ``/sys/bus/iio/devices/iio:deviceX``, where X is the IIO index of the device. Under these folders reside a set of device files, depending on the characteristics and features of the hardware -device in questions. These files are consistently generalized and documented in +device in question. These files are consistently generalized and documented in the IIO ABI documentation. The following table shows the ADXL345 related device files, found in the @@ -42,7 +47,7 @@ specific device folder path ``/sys/bus/iio/devices/iio:deviceX``. +-------------------------------------------+----------------------------------------------------------+ | in_accel_x_raw | Raw X-axis accelerometer channel value. | +-------------------------------------------+----------------------------------------------------------+ -| in_accel_y_calibbias | y-axis acceleration offset correction | +| in_accel_y_calibbias | Y-axis acceleration offset correction | +-------------------------------------------+----------------------------------------------------------+ | in_accel_y_raw | Raw Y-axis accelerometer channel value. | +-------------------------------------------+----------------------------------------------------------+ @@ -68,7 +73,7 @@ present, simply assume its value is 0. +-------------------------------------+---------------------------+ | Channel type | Measurement unit | +-------------------------------------+---------------------------+ -| Acceleration on X, Y, and Z axis | Meters per second squared | +| Acceleration on X, Y, and Z axes | Meters per second squared | +-------------------------------------+---------------------------+ Sensor Events @@ -78,8 +83,8 @@ Specific IIO events are triggered by their corresponding interrupts. The sensor driver supports either none or a single active interrupt (INT) line, selectable from the two available options: INT1 or INT2. The active INT line should be specified in the device tree. If no INT line is configured, the sensor defaults -to FIFO bypass mode, where event detection is disabled and only X, Y, and Z axis -measurements are available. +to FIFO bypass mode, where event detection is disabled and only individual +X, Y, and Z axis measurements are available. The table below lists the ADXL345-related device files located in the device-specific path: ``/sys/bus/iio/devices/iio:deviceX/events``. @@ -90,38 +95,52 @@ listed. +---------------------------------------------+---------------------------------------------+ | Event handle | Description | +---------------------------------------------+---------------------------------------------+ -| in_accel_gesture_doubletap_en | Enable double tap detection on all axis | +| in_accel_gesture_doubletap_en | Enable double tap detection on all axes | +---------------------------------------------+---------------------------------------------+ | in_accel_gesture_doubletap_reset_timeout | Double tap window in [us] | +---------------------------------------------+---------------------------------------------+ -| in_accel_gesture_doubletap_tap2_min_delay | Double tap latent in [us] | +| in_accel_gesture_doubletap_scale | Double tap gesture threshold scale. | ++---------------------------------------------+---------------------------------------------+ +| in_accel_gesture_doubletap_tap2_min_delay | Double tap latency in [us] | ++---------------------------------------------+---------------------------------------------+ +| in_accel_gesture_doubletap_value | Double tap threshold value | ++---------------------------------------------+---------------------------------------------+ +| in_accel_gesture_singletap_scale | Single tap gesture threshold scale. | +---------------------------------------------+---------------------------------------------+ | in_accel_gesture_singletap_timeout | Single tap duration in [us] | +---------------------------------------------+---------------------------------------------+ -| in_accel_gesture_singletap_value | Single tap threshold value in 62.5/LSB | -+---------------------------------------------+---------------------------------------------+ -| in_accel_mag_falling_period | Inactivity time in seconds | -+---------------------------------------------+---------------------------------------------+ -| in_accel_mag_falling_value | Inactivity threshold value in 62.5/LSB | -+---------------------------------------------+---------------------------------------------+ -| in_accel_mag_adaptive_rising_en | Enable AC coupled activity on X axis | +| in_accel_gesture_singletap_value | Single tap threshold value | +---------------------------------------------+---------------------------------------------+ | in_accel_mag_adaptive_falling_period | AC coupled inactivity time in seconds | +---------------------------------------------+---------------------------------------------+ -| in_accel_mag_adaptive_falling_value | AC coupled inactivity threshold in 62.5/LSB | +| in_accel_mag_adaptive_falling_scale | AC coupled inactivity threshold scale. | +---------------------------------------------+---------------------------------------------+ -| in_accel_mag_adaptive_rising_value | AC coupled activity threshold in 62.5/LSB | +| in_accel_mag_adaptive_falling_value | AC coupled inactivity threshold | ++---------------------------------------------+---------------------------------------------+ +| in_accel_mag_adaptive_rising_en | Enable AC coupled activity on X axis | ++---------------------------------------------+---------------------------------------------+ +| in_accel_mag_adaptive_rising_scale | AC coupled activity threshold scale. | ++---------------------------------------------+---------------------------------------------+ +| in_accel_mag_adaptive_rising_value | AC coupled activity threshold | ++---------------------------------------------+---------------------------------------------+ +| in_accel_mag_falling_period | Inactivity time in seconds | ++---------------------------------------------+---------------------------------------------+ +| in_accel_mag_falling_scale | DC coupled inactivity threshold scale. | ++---------------------------------------------+---------------------------------------------+ +| in_accel_mag_falling_value | Inactivity threshold value | +---------------------------------------------+---------------------------------------------+ | in_accel_mag_rising_en | Enable activity detection on X axis | +---------------------------------------------+---------------------------------------------+ -| in_accel_mag_rising_value | Activity threshold value in 62.5/LSB | +| in_accel_mag_rising_scale | DC coupled activity threshold scale. | ++---------------------------------------------+---------------------------------------------+ +| in_accel_mag_rising_value | Activity threshold value | ++---------------------------------------------+---------------------------------------------+ +| in_accel_x&y&z_mag_adaptive_falling_en | Enable AC coupled inactivity on all axes | ++---------------------------------------------+---------------------------------------------+ +| in_accel_x&y&z_mag_falling_en | Enable inactivity detection on all axes | +---------------------------------------------+---------------------------------------------+ | in_accel_x_gesture_singletap_en | Enable single tap detection on X axis | +---------------------------------------------+---------------------------------------------+ -| in_accel_x&y&z_mag_falling_en | Enable inactivity detection on all axis | -+---------------------------------------------+---------------------------------------------+ -| in_accel_x&y&z_mag_adaptive_falling_en | Enable AC coupled inactivity on all axis | -+---------------------------------------------+---------------------------------------------+ | in_accel_y_gesture_singletap_en | Enable single tap detection on Y axis | +---------------------------------------------+---------------------------------------------+ | in_accel_z_gesture_singletap_en | Enable single tap detection on Z axis | @@ -140,8 +159,8 @@ When changing the **g range** configuration, the driver attempts to estimate appropriate activity and inactivity thresholds by scaling the default values based on the ratio of the previous range to the new one. The resulting threshold will never be zero and will always fall between 1 and 255, corresponding to up -to 62.5 g/LSB as specified in the datasheet. However, you can override these -estimated thresholds by setting explicit values. +to 62.5 mg/LSB (0.612915 m/s^2/LSB) as specified in the datasheet. However, +you can override these estimated thresholds by setting explicit values. When **activity** and **inactivity** events are enabled, the driver automatically manages hysteresis behavior by setting the **link** and @@ -270,13 +289,13 @@ Scale range configuration: .. code-block:: bash root:/sys/bus/iio/devices/iio:device0> cat ./in_accel_scale - 0.478899 + 0.004789 root:/sys/bus/iio/devices/iio:device0> cat ./in_accel_scale_available - 0.478899 0.957798 1.915595 3.831190 + 0.004789 0.009578 0.019156 0.038312 - root:/sys/bus/iio/devices/iio:device0> echo 1.915595 > ./in_accel_scale + root:/sys/bus/iio/devices/iio:device0> echo 0.019156 > ./in_accel_scale root:/sys/bus/iio/devices/iio:device0> cat ./in_accel_scale - 1.915595 + 0.019156 Set output data rate (ODR): @@ -312,10 +331,14 @@ Configure one or several events: root:/sys/bus/iio/devices/iio:device0> echo 24 > ./buffer0/length - ## AC coupled activity, threshold [62.5/LSB] + ## Check the event scale factor (0.0625 * 9.80665) + root:/sys/bus/iio/devices/iio:device0> cat ./events/in_accel_gesture_doubletap_scale + 0.612915 + + ## AC coupled activity, threshold [0.612915 m/s^2/LSB] root:/sys/bus/iio/devices/iio:device0> echo 6 > ./events/in_accel_mag_adaptive_rising_value - ## AC coupled inactivity, threshold, [62.5/LSB] + ## AC coupled inactivity, threshold, [0.612915 m/s^2/LSB] root:/sys/bus/iio/devices/iio:device0> echo 4 > ./events/in_accel_mag_adaptive_falling_value ## AC coupled inactivity, time [s] @@ -330,7 +353,7 @@ Configure one or several events: ## doubletap, window [us] root:/sys/bus/iio/devices/iio:device0> echo 0.025 > ./events/in_accel_gesture_doubletap_reset_timeout - ## doubletap, latent [us] + ## doubletap, latency [us] root:/sys/bus/iio/devices/iio:device0> echo 0.025 > ./events/in_accel_gesture_doubletap_tap2_min_delay ## AC coupled activity, enable diff --git a/Documentation/infiniband/index.rst b/Documentation/infiniband/index.rst index c11049d25703..f57387a92338 100644 --- a/Documentation/infiniband/index.rst +++ b/Documentation/infiniband/index.rst @@ -9,7 +9,6 @@ InfiniBand core_locking ipoib - opa_vnic sysfs tag_matching ucaps diff --git a/Documentation/infiniband/opa_vnic.rst b/Documentation/infiniband/opa_vnic.rst deleted file mode 100644 index 2f888d9ffec0..000000000000 --- a/Documentation/infiniband/opa_vnic.rst +++ /dev/null @@ -1,159 +0,0 @@ -================================================================= -Intel Omni-Path (OPA) Virtual Network Interface Controller (VNIC) -================================================================= - -Intel Omni-Path (OPA) Virtual Network Interface Controller (VNIC) feature -supports Ethernet functionality over Omni-Path fabric by encapsulating -the Ethernet packets between HFI nodes. - -Architecture -============= -The patterns of exchanges of Omni-Path encapsulated Ethernet packets -involves one or more virtual Ethernet switches overlaid on the Omni-Path -fabric topology. A subset of HFI nodes on the Omni-Path fabric are -permitted to exchange encapsulated Ethernet packets across a particular -virtual Ethernet switch. The virtual Ethernet switches are logical -abstractions achieved by configuring the HFI nodes on the fabric for -header generation and processing. In the simplest configuration all HFI -nodes across the fabric exchange encapsulated Ethernet packets over a -single virtual Ethernet switch. A virtual Ethernet switch, is effectively -an independent Ethernet network. The configuration is performed by an -Ethernet Manager (EM) which is part of the trusted Fabric Manager (FM) -application. HFI nodes can have multiple VNICs each connected to a -different virtual Ethernet switch. The below diagram presents a case -of two virtual Ethernet switches with two HFI nodes:: - - +-------------------+ - | Subnet/ | - | Ethernet | - | Manager | - +-------------------+ - / / - / / - / / - / / - +-----------------------------+ +------------------------------+ - | Virtual Ethernet Switch | | Virtual Ethernet Switch | - | +---------+ +---------+ | | +---------+ +---------+ | - | | VPORT | | VPORT | | | | VPORT | | VPORT | | - +--+---------+----+---------+-+ +-+---------+----+---------+---+ - | \ / | - | \ / | - | \/ | - | / \ | - | / \ | - +-----------+------------+ +-----------+------------+ - | VNIC | VNIC | | VNIC | VNIC | - +-----------+------------+ +-----------+------------+ - | HFI | | HFI | - +------------------------+ +------------------------+ - - -The Omni-Path encapsulated Ethernet packet format is as described below. - -==================== ================================ -Bits Field -==================== ================================ -Quad Word 0: -0-19 SLID (lower 20 bits) -20-30 Length (in Quad Words) -31 BECN bit -32-51 DLID (lower 20 bits) -52-56 SC (Service Class) -57-59 RC (Routing Control) -60 FECN bit -61-62 L2 (=10, 16B format) -63 LT (=1, Link Transfer Head Flit) - -Quad Word 1: -0-7 L4 type (=0x78 ETHERNET) -8-11 SLID[23:20] -12-15 DLID[23:20] -16-31 PKEY -32-47 Entropy -48-63 Reserved - -Quad Word 2: -0-15 Reserved -16-31 L4 header -32-63 Ethernet Packet - -Quad Words 3 to N-1: -0-63 Ethernet packet (pad extended) - -Quad Word N (last): -0-23 Ethernet packet (pad extended) -24-55 ICRC -56-61 Tail -62-63 LT (=01, Link Transfer Tail Flit) -==================== ================================ - -Ethernet packet is padded on the transmit side to ensure that the VNIC OPA -packet is quad word aligned. The 'Tail' field contains the number of bytes -padded. On the receive side the 'Tail' field is read and the padding is -removed (along with ICRC, Tail and OPA header) before passing packet up -the network stack. - -The L4 header field contains the virtual Ethernet switch id the VNIC port -belongs to. On the receive side, this field is used to de-multiplex the -received VNIC packets to different VNIC ports. - -Driver Design -============== -Intel OPA VNIC software design is presented in the below diagram. -OPA VNIC functionality has a HW dependent component and a HW -independent component. - -The support has been added for IB device to allocate and free the RDMA -netdev devices. The RDMA netdev supports interfacing with the network -stack thus creating standard network interfaces. OPA_VNIC is an RDMA -netdev device type. - -The HW dependent VNIC functionality is part of the HFI1 driver. It -implements the verbs to allocate and free the OPA_VNIC RDMA netdev. -It involves HW resource allocation/management for VNIC functionality. -It interfaces with the network stack and implements the required -net_device_ops functions. It expects Omni-Path encapsulated Ethernet -packets in the transmit path and provides HW access to them. It strips -the Omni-Path header from the received packets before passing them up -the network stack. It also implements the RDMA netdev control operations. - -The OPA VNIC module implements the HW independent VNIC functionality. -It consists of two parts. The VNIC Ethernet Management Agent (VEMA) -registers itself with IB core as an IB client and interfaces with the -IB MAD stack. It exchanges the management information with the Ethernet -Manager (EM) and the VNIC netdev. The VNIC netdev part allocates and frees -the OPA_VNIC RDMA netdev devices. It overrides the net_device_ops functions -set by HW dependent VNIC driver where required to accommodate any control -operation. It also handles the encapsulation of Ethernet packets with an -Omni-Path header in the transmit path. For each VNIC interface, the -information required for encapsulation is configured by the EM via VEMA MAD -interface. It also passes any control information to the HW dependent driver -by invoking the RDMA netdev control operations:: - - +-------------------+ +----------------------+ - | | | Linux | - | IB MAD | | Network | - | | | Stack | - +-------------------+ +----------------------+ - | | | - | | | - +----------------------------+ | - | | | - | OPA VNIC Module | | - | (OPA VNIC RDMA Netdev | | - | & EMA functions) | | - | | | - +----------------------------+ | - | | - | | - +------------------+ | - | IB core | | - +------------------+ | - | | - | | - +--------------------------------------------+ - | | - | HFI1 Driver with VNIC support | - | | - +--------------------------------------------+ diff --git a/Documentation/isdn/credits.rst b/Documentation/isdn/credits.rst deleted file mode 100644 index 319323f2091f..000000000000 --- a/Documentation/isdn/credits.rst +++ /dev/null @@ -1,73 +0,0 @@ -======= -Credits -======= - - -I want to thank all who contributed to this project and especially to: -(in alphabetical order) - -Thomas Bogendörfer (tsbogend@bigbug.franken.de) - Tester, lots of bugfixes and hints. - -Alan Cox (alan@lxorguk.ukuu.org.uk) - For help getting into standard-kernel. - -Henner Eisen (eis@baty.hanse.de) - For X.25 implementation. - -Volker Götz (volker@oops.franken.de) - For contribution of man-pages, the imontty-tool and a perfect - maintaining of the mailing-list at hub-wue. - -Matthias Hessler (hessler@isdn4linux.de) - For creating and maintaining the FAQ. - -Bernhard Hailer (Bernhard.Hailer@lrz.uni-muenchen.de) - For creating the FAQ, and the leafsite HOWTO. - -Michael 'Ghandi' Herold (michael@abadonna.franken.de) - For contribution of the vbox answering machine. - -Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) - For his Sync-PPP-code. - -Karsten Keil (keil@isdn4linux.de) - For adding 1TR6-support to the Teles-driver. - For the HiSax-driver. - -Michael Knigge (knick@cove.han.de) - For contributing the imon-tool - -Andreas Kool (akool@Kool.f.EUnet.de) - For contribution of the isdnlog/isdnrep-tool - -Pedro Roque Marques (roque@di.fc.ul.pt) - For lot of new ideas and the pcbit driver. - -Eberhard Mönkeberg (emoenke@gwdg.de) - For testing and help to get into kernel. - -Thomas Neumann (tn@ruhr.de) - For help with Cisco-SLARP and keepalive - -Jan den Ouden (denouden@groovin.xs4all.nl) - For contribution of the original teles-driver - -Carsten Paeth (calle@calle.in-berlin.de) - For the AVM-B1-CAPI2.0 driver - -Thomas Pfeiffer (pfeiffer@pds.de) - For V.110, extended T.70 and Hylafax extensions in isdn_tty.c - -Max Riegel (riegel@max.franken.de) - For making the ICN hardware-documentation and test-equipment available. - -Armin Schindler (mac@melware.de) - For the eicon active card driver. - -Gerhard 'Fido' Schneider (fido@wuff.mayn.de) - For heavy-duty-beta-testing with his BBS ;) - -Thomas Uhl (uhl@think.de) - For distributing the cards. - For pushing me to work ;-) diff --git a/Documentation/isdn/index.rst b/Documentation/isdn/index.rst deleted file mode 100644 index d1125a16a746..000000000000 --- a/Documentation/isdn/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -==== -ISDN -==== - -.. toctree:: - :maxdepth: 2 - - interface_capi - - m_isdn - - credits diff --git a/Documentation/isdn/interface_capi.rst b/Documentation/isdn/interface_capi.rst deleted file mode 100644 index 4d63b34b35cf..000000000000 --- a/Documentation/isdn/interface_capi.rst +++ /dev/null @@ -1,336 +0,0 @@ -========================================= -Kernel CAPI Interface to Hardware Drivers -========================================= - -1. Overview -=========== - -From the CAPI 2.0 specification: -COMMON-ISDN-API (CAPI) is an application programming interface standard used -to access ISDN equipment connected to basic rate interfaces (BRI) and primary -rate interfaces (PRI). - -Kernel CAPI operates as a dispatching layer between CAPI applications and CAPI -hardware drivers. Hardware drivers register ISDN devices (controllers, in CAPI -lingo) with Kernel CAPI to indicate their readiness to provide their service -to CAPI applications. CAPI applications also register with Kernel CAPI, -requesting association with a CAPI device. Kernel CAPI then dispatches the -application registration to an available device, forwarding it to the -corresponding hardware driver. Kernel CAPI then forwards CAPI messages in both -directions between the application and the hardware driver. - -Format and semantics of CAPI messages are specified in the CAPI 2.0 standard. -This standard is freely available from https://www.capi.org. - - -2. Driver and Device Registration -================================= - -CAPI drivers must register each of the ISDN devices they control with Kernel -CAPI by calling the Kernel CAPI function attach_capi_ctr() with a pointer to a -struct capi_ctr before they can be used. This structure must be filled with -the names of the driver and controller, and a number of callback function -pointers which are subsequently used by Kernel CAPI for communicating with the -driver. The registration can be revoked by calling the function -detach_capi_ctr() with a pointer to the same struct capi_ctr. - -Before the device can be actually used, the driver must fill in the device -information fields 'manu', 'version', 'profile' and 'serial' in the capi_ctr -structure of the device, and signal its readiness by calling capi_ctr_ready(). -From then on, Kernel CAPI may call the registered callback functions for the -device. - -If the device becomes unusable for any reason (shutdown, disconnect ...), the -driver has to call capi_ctr_down(). This will prevent further calls to the -callback functions by Kernel CAPI. - - -3. Application Registration and Communication -============================================= - -Kernel CAPI forwards registration requests from applications (calls to CAPI -operation CAPI_REGISTER) to an appropriate hardware driver by calling its -register_appl() callback function. A unique Application ID (ApplID, u16) is -allocated by Kernel CAPI and passed to register_appl() along with the -parameter structure provided by the application. This is analogous to the -open() operation on regular files or character devices. - -After a successful return from register_appl(), CAPI messages from the -application may be passed to the driver for the device via calls to the -send_message() callback function. Conversely, the driver may call Kernel -CAPI's capi_ctr_handle_message() function to pass a received CAPI message to -Kernel CAPI for forwarding to an application, specifying its ApplID. - -Deregistration requests (CAPI operation CAPI_RELEASE) from applications are -forwarded as calls to the release_appl() callback function, passing the same -ApplID as with register_appl(). After return from release_appl(), no CAPI -messages for that application may be passed to or from the device anymore. - - -4. Data Structures -================== - -4.1 struct capi_driver ----------------------- - -This structure describes a Kernel CAPI driver itself. It is used in the -register_capi_driver() and unregister_capi_driver() functions, and contains -the following non-private fields, all to be set by the driver before calling -register_capi_driver(): - -``char name[32]`` - the name of the driver, as a zero-terminated ASCII string -``char revision[32]`` - the revision number of the driver, as a zero-terminated ASCII string - -4.2 struct capi_ctr -------------------- - -This structure describes an ISDN device (controller) handled by a Kernel CAPI -driver. After registration via the attach_capi_ctr() function it is passed to -all controller specific lower layer interface and callback functions to -identify the controller to operate on. - -It contains the following non-private fields: - -to be set by the driver before calling attach_capi_ctr(): -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``struct module *owner`` - pointer to the driver module owning the device - -``void *driverdata`` - an opaque pointer to driver specific data, not touched by Kernel CAPI - -``char name[32]`` - the name of the controller, as a zero-terminated ASCII string - -``char *driver_name`` - the name of the driver, as a zero-terminated ASCII string - -``int (*load_firmware)(struct capi_ctr *ctrlr, capiloaddata *ldata)`` - (optional) pointer to a callback function for sending firmware and - configuration data to the device - - The function may return before the operation has completed. - - Completion must be signalled by a call to capi_ctr_ready(). - - Return value: 0 on success, error code on error - Called in process context. - -``void (*reset_ctr)(struct capi_ctr *ctrlr)`` - (optional) pointer to a callback function for stopping the device, - releasing all registered applications - - The function may return before the operation has completed. - - Completion must be signalled by a call to capi_ctr_down(). - - Called in process context. - -``void (*register_appl)(struct capi_ctr *ctrlr, u16 applid, capi_register_params *rparam)`` - pointers to callback function for registration of - applications with the device - - Calls to these functions are serialized by Kernel CAPI so that only - one call to any of them is active at any time. - -``void (*release_appl)(struct capi_ctr *ctrlr, u16 applid)`` - pointers to callback functions deregistration of - applications with the device - - Calls to these functions are serialized by Kernel CAPI so that only - one call to any of them is active at any time. - -``u16 (*send_message)(struct capi_ctr *ctrlr, struct sk_buff *skb)`` - pointer to a callback function for sending a CAPI message to the - device - - Return value: CAPI error code - - If the method returns 0 (CAPI_NOERROR) the driver has taken ownership - of the skb and the caller may no longer access it. If it returns a - non-zero (error) value then ownership of the skb returns to the caller - who may reuse or free it. - - The return value should only be used to signal problems with respect - to accepting or queueing the message. Errors occurring during the - actual processing of the message should be signaled with an - appropriate reply message. - - May be called in process or interrupt context. - - Calls to this function are not serialized by Kernel CAPI, ie. it must - be prepared to be re-entered. - -``char *(*procinfo)(struct capi_ctr *ctrlr)`` - pointer to a callback function returning the entry for the device in - the CAPI controller info table, /proc/capi/controller - -Note: - Callback functions except send_message() are never called in interrupt - context. - -to be filled in before calling capi_ctr_ready(): -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``u8 manu[CAPI_MANUFACTURER_LEN]`` - value to return for CAPI_GET_MANUFACTURER - -``capi_version version`` - value to return for CAPI_GET_VERSION - -``capi_profile profile`` - value to return for CAPI_GET_PROFILE - -``u8 serial[CAPI_SERIAL_LEN]`` - value to return for CAPI_GET_SERIAL - - -4.3 SKBs --------- - -CAPI messages are passed between Kernel CAPI and the driver via send_message() -and capi_ctr_handle_message(), stored in the data portion of a socket buffer -(skb). Each skb contains a single CAPI message coded according to the CAPI 2.0 -standard. - -For the data transfer messages, DATA_B3_REQ and DATA_B3_IND, the actual -payload data immediately follows the CAPI message itself within the same skb. -The Data and Data64 parameters are not used for processing. The Data64 -parameter may be omitted by setting the length field of the CAPI message to 22 -instead of 30. - - -4.4 The _cmsg Structure ------------------------ - -(declared in ) - -The _cmsg structure stores the contents of a CAPI 2.0 message in an easily -accessible form. It contains members for all possible CAPI 2.0 parameters, -including subparameters of the Additional Info and B Protocol structured -parameters, with the following exceptions: - -* second Calling party number (CONNECT_IND) - -* Data64 (DATA_B3_REQ and DATA_B3_IND) - -* Sending complete (subparameter of Additional Info, CONNECT_REQ and INFO_REQ) - -* Global Configuration (subparameter of B Protocol, CONNECT_REQ, CONNECT_RESP - and SELECT_B_PROTOCOL_REQ) - -Only those parameters appearing in the message type currently being processed -are actually used. Unused members should be set to zero. - -Members are named after the CAPI 2.0 standard names of the parameters they -represent. See for the exact spelling. Member data -types are: - -=========== ================================================================= -u8 for CAPI parameters of type 'byte' - -u16 for CAPI parameters of type 'word' - -u32 for CAPI parameters of type 'dword' - -_cstruct for CAPI parameters of type 'struct' - The member is a pointer to a buffer containing the parameter in - CAPI encoding (length + content). It may also be NULL, which will - be taken to represent an empty (zero length) parameter. - Subparameters are stored in encoded form within the content part. - -_cmstruct alternative representation for CAPI parameters of type 'struct' - (used only for the 'Additional Info' and 'B Protocol' parameters) - The representation is a single byte containing one of the values: - CAPI_DEFAULT: The parameter is empty/absent. - CAPI_COMPOSE: The parameter is present. - Subparameter values are stored individually in the corresponding - _cmsg structure members. -=========== ================================================================= - - -5. Lower Layer Interface Functions -================================== - -:: - - int attach_capi_ctr(struct capi_ctr *ctrlr) - int detach_capi_ctr(struct capi_ctr *ctrlr) - -register/unregister a device (controller) with Kernel CAPI - -:: - - void capi_ctr_ready(struct capi_ctr *ctrlr) - void capi_ctr_down(struct capi_ctr *ctrlr) - -signal controller ready/not ready - -:: - - void capi_ctr_handle_message(struct capi_ctr * ctrlr, u16 applid, - struct sk_buff *skb) - -pass a received CAPI message to Kernel CAPI -for forwarding to the specified application - - -6. Helper Functions and Macros -============================== - -Macros to extract/set element values from/in a CAPI message header -(from ): - -====================== ============================= ==================== -Get Macro Set Macro Element (Type) -====================== ============================= ==================== -CAPIMSG_LEN(m) CAPIMSG_SETLEN(m, len) Total Length (u16) -CAPIMSG_APPID(m) CAPIMSG_SETAPPID(m, applid) ApplID (u16) -CAPIMSG_COMMAND(m) CAPIMSG_SETCOMMAND(m,cmd) Command (u8) -CAPIMSG_SUBCOMMAND(m) CAPIMSG_SETSUBCOMMAND(m, cmd) Subcommand (u8) -CAPIMSG_CMD(m) - Command*256 - + Subcommand (u16) -CAPIMSG_MSGID(m) CAPIMSG_SETMSGID(m, msgid) Message Number (u16) - -CAPIMSG_CONTROL(m) CAPIMSG_SETCONTROL(m, contr) Controller/PLCI/NCCI - (u32) -CAPIMSG_DATALEN(m) CAPIMSG_SETDATALEN(m, len) Data Length (u16) -====================== ============================= ==================== - - -Library functions for working with _cmsg structures -(from ): - -``char *capi_cmd2str(u8 Command, u8 Subcommand)`` - Returns the CAPI 2.0 message name corresponding to the given command - and subcommand values, as a static ASCII string. The return value may - be NULL if the command/subcommand is not one of those defined in the - CAPI 2.0 standard. - - -7. Debugging -============ - -The module kernelcapi has a module parameter showcapimsgs controlling some -debugging output produced by the module. It can only be set when the module is -loaded, via a parameter "showcapimsgs=" to the modprobe command, either on -the command line or in the configuration file. - -If the lowest bit of showcapimsgs is set, kernelcapi logs controller and -application up and down events. - -In addition, every registered CAPI controller has an associated traceflag -parameter controlling how CAPI messages sent from and to the controller are -logged. The traceflag parameter is initialized with the value of the -showcapimsgs parameter when the controller is registered, but can later be -changed via the MANUFACTURER_REQ command KCAPI_CMD_TRACE. - -If the value of traceflag is non-zero, CAPI messages are logged. -DATA_B3 messages are only logged if the value of traceflag is > 2. - -If the lowest bit of traceflag is set, only the command/subcommand and message -length are logged. Otherwise, kernelcapi logs a readable representation of -the entire message. diff --git a/Documentation/isdn/m_isdn.rst b/Documentation/isdn/m_isdn.rst deleted file mode 100644 index 5847a164287e..000000000000 --- a/Documentation/isdn/m_isdn.rst +++ /dev/null @@ -1,9 +0,0 @@ -============ -mISDN Driver -============ - -mISDN is a new modular ISDN driver, in the long term it should replace -the old I4L driver architecture for passive ISDN cards. -It was designed to allow a broad range of applications and interfaces -but only have the basic function in kernel, the interface to the user -space is based on sockets with a own address family AF_ISDN. diff --git a/Documentation/netlink/genetlink-c.yaml b/Documentation/netlink/genetlink-c.yaml index 57f59fe23e3f..4ea31e8fc4d1 100644 --- a/Documentation/netlink/genetlink-c.yaml +++ b/Documentation/netlink/genetlink-c.yaml @@ -69,6 +69,15 @@ properties: header: description: For C-compatible languages, header which already defines this value. type: string + scope: + description: | + Visibility of this definition. "uapi" (default) renders into + the uAPI header, "kernel" renders into the kernel-side + generated header, "user" renders into the user-side + generated header. When combined with `header:`, the + definition is not rendered, and the named header is + included only by code matching the scope. + enum: [ uapi, kernel, user ] type: enum: [ const, enum, flags ] doc: diff --git a/Documentation/netlink/genetlink-legacy.yaml b/Documentation/netlink/genetlink-legacy.yaml index 66fb8653a344..f9c44747729a 100644 --- a/Documentation/netlink/genetlink-legacy.yaml +++ b/Documentation/netlink/genetlink-legacy.yaml @@ -83,6 +83,15 @@ properties: header: description: For C-compatible languages, header which already defines this value. type: string + scope: + description: | + Visibility of this definition. "uapi" (default) renders into + the uAPI header, "kernel" renders into the kernel-side + generated header, "user" renders into the user-side + generated header. When combined with `header:`, the + definition is not rendered, and the named header is + included only by code matching the scope. + enum: [ uapi, kernel, user ] type: enum: [ const, enum, flags, struct ] # Trim doc: diff --git a/Documentation/netlink/genetlink.yaml b/Documentation/netlink/genetlink.yaml index a1194d5d93fc..d3f3f3399ddf 100644 --- a/Documentation/netlink/genetlink.yaml +++ b/Documentation/netlink/genetlink.yaml @@ -55,6 +55,15 @@ properties: header: description: For C-compatible languages, header which already defines this value. type: string + scope: + description: | + Visibility of this definition. "uapi" (default) renders into + the uAPI header, "kernel" renders into the kernel-side + generated header, "user" renders into the user-side + generated header. When combined with `header:`, the + definition is not rendered, and the named header is + included only by code matching the scope. + enum: [ uapi, kernel, user ] type: enum: [ const, enum, flags ] doc: diff --git a/Documentation/netlink/netlink-raw.yaml b/Documentation/netlink/netlink-raw.yaml index dd98dda55bd0..4c436b59a34b 100644 --- a/Documentation/netlink/netlink-raw.yaml +++ b/Documentation/netlink/netlink-raw.yaml @@ -87,6 +87,15 @@ properties: header: description: For C-compatible languages, header which already defines this value. type: string + scope: + description: | + Visibility of this definition. "uapi" (default) renders into + the uAPI header, "kernel" renders into the kernel-side + generated header, "user" renders into the user-side + generated header. When combined with `header:`, the + definition is not rendered, and the named header is + included only by code matching the scope. + enum: [ uapi, kernel, user ] type: enum: [ const, enum, flags, struct ] # Trim doc: diff --git a/Documentation/netlink/specs/net_shaper.yaml b/Documentation/netlink/specs/net_shaper.yaml index 3f2ad772b64b..de01f922040a 100644 --- a/Documentation/netlink/specs/net_shaper.yaml +++ b/Documentation/netlink/specs/net_shaper.yaml @@ -33,6 +33,11 @@ doc: | @cap-get operation. definitions: + - + type: const + name: max-handle-id + value: 0x3fffffe + scope: kernel - type: enum name: scope @@ -140,6 +145,8 @@ attribute-sets: - name: id type: u32 + checks: + max: max-handle-id doc: | Numeric identifier of a shaper. The id semantic depends on the scope. For @queue scope it's the queue id and for @node diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml index f87b5a05e5e9..8ab43c8253b2 100644 --- a/Documentation/netlink/specs/nfsd.yaml +++ b/Documentation/netlink/specs/nfsd.yaml @@ -81,6 +81,11 @@ attribute-sets: - name: min-threads type: u32 + - + name: fh-key + type: binary + checks: + exact-len: 16 - name: version attributes: @@ -163,6 +168,7 @@ operations: - leasetime - scope - min-threads + - fh-key - name: threads-get doc: get the maximum number of running threads diff --git a/Documentation/netlink/specs/psp.yaml b/Documentation/netlink/specs/psp.yaml index 100c36cda8e5..bfcd6e4ecb85 100644 --- a/Documentation/netlink/specs/psp.yaml +++ b/Documentation/netlink/specs/psp.yaml @@ -188,6 +188,7 @@ operations: name: dev-set doc: Set the configuration of a PSP device. attribute-set: dev + flags: [admin-perm] do: request: attributes: @@ -207,6 +208,7 @@ operations: name: key-rotate doc: Rotate the device key. attribute-set: dev + flags: [admin-perm] do: request: attributes: diff --git a/Documentation/networking/6pack.rst b/Documentation/networking/6pack.rst deleted file mode 100644 index 66d5fd4fc821..000000000000 --- a/Documentation/networking/6pack.rst +++ /dev/null @@ -1,191 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -============== -6pack Protocol -============== - -This is the 6pack-mini-HOWTO, written by - -Andreas Könsgen DG3KQ - -:Internet: ajk@comnets.uni-bremen.de -:AMPR-net: dg3kq@db0pra.ampr.org -:AX.25: dg3kq@db0ach.#nrw.deu.eu - -Last update: April 7, 1998 - -1. What is 6pack, and what are the advantages to KISS? -====================================================== - -6pack is a transmission protocol for data exchange between the PC and -the TNC over a serial line. It can be used as an alternative to KISS. - -6pack has two major advantages: - -- The PC is given full control over the radio - channel. Special control data is exchanged between the PC and the TNC so - that the PC knows at any time if the TNC is receiving data, if a TNC - buffer underrun or overrun has occurred, if the PTT is - set and so on. This control data is processed at a higher priority than - normal data, so a data stream can be interrupted at any time to issue an - important event. This helps to improve the channel access and timing - algorithms as everything is computed in the PC. It would even be possible - to experiment with something completely different from the known CSMA and - DAMA channel access methods. - This kind of real-time control is especially important to supply several - TNCs that are connected between each other and the PC by a daisy chain - (however, this feature is not supported yet by the Linux 6pack driver). - -- Each packet transferred over the serial line is supplied with a checksum, - so it is easy to detect errors due to problems on the serial line. - Received packets that are corrupt are not passed on to the AX.25 layer. - Damaged packets that the TNC has received from the PC are not transmitted. - -More details about 6pack are described in the file 6pack.ps that is located -in the doc directory of the AX.25 utilities package. - -2. Who has developed the 6pack protocol? -======================================== - -The 6pack protocol has been developed by Ekki Plicht DF4OR, Henning Rech -DF9IC and Gunter Jost DK7WJ. A driver for 6pack, written by Gunter Jost and -Matthias Welwarsky DG2FEF, comes along with the PC version of FlexNet. -They have also written a firmware for TNCs to perform the 6pack -protocol (see section 4 below). - -3. Where can I get the latest version of 6pack for LinuX? -========================================================= - -At the moment, the 6pack stuff can obtained via anonymous ftp from -db0bm.automation.fh-aachen.de. In the directory /incoming/dg3kq, -there is a file named 6pack.tgz. - -4. Preparing the TNC for 6pack operation -======================================== - -To be able to use 6pack, a special firmware for the TNC is needed. The EPROM -of a newly bought TNC does not contain 6pack, so you will have to -program an EPROM yourself. The image file for 6pack EPROMs should be -available on any packet radio box where PC/FlexNet can be found. The name of -the file is 6pack.bin. This file is copyrighted and maintained by the FlexNet -team. It can be used under the terms of the license that comes along -with PC/FlexNet. Please do not ask me about the internals of this file as I -don't know anything about it. I used a textual description of the 6pack -protocol to program the Linux driver. - -TNCs contain a 64kByte EPROM, the lower half of which is used for -the firmware/KISS. The upper half is either empty or is sometimes -programmed with software called TAPR. In the latter case, the TNC -is supplied with a DIP switch so you can easily change between the -two systems. When programming a new EPROM, one of the systems is replaced -by 6pack. It is useful to replace TAPR, as this software is rarely used -nowadays. If your TNC is not equipped with the switch mentioned above, you -can build in one yourself that switches over the highest address pin -of the EPROM between HIGH and LOW level. After having inserted the new EPROM -and switched to 6pack, apply power to the TNC for a first test. The connect -and the status LED are lit for about a second if the firmware initialises -the TNC correctly. - -5. Building and installing the 6pack driver -=========================================== - -The driver has been tested with kernel version 2.1.90. Use with older -kernels may lead to a compilation error because the interface to a kernel -function has been changed in the 2.1.8x kernels. - -How to turn on 6pack support: ------------------------------ - -- In the linux kernel configuration program, select the code maturity level - options menu and turn on the prompting for development drivers. - -- Select the amateur radio support menu and turn on the serial port 6pack - driver. - -- Compile and install the kernel and the modules. - -To use the driver, the kissattach program delivered with the AX.25 utilities -has to be modified. - -- Do a cd to the directory that holds the kissattach sources. Edit the - kissattach.c file. At the top, insert the following lines:: - - #ifndef N_6PACK - #define N_6PACK (N_AX25+1) - #endif - - Then find the line: - - int disc = N_AX25; - - and replace N_AX25 by N_6PACK. - -- Recompile kissattach. Rename it to spattach to avoid confusions. - -Installing the driver: ----------------------- - -- Do an insmod 6pack. Look at your /var/log/messages file to check if the - module has printed its initialization message. - -- Do a spattach as you would launch kissattach when starting a KISS port. - Check if the kernel prints the message '6pack: TNC found'. - -- From here, everything should work as if you were setting up a KISS port. - The only difference is that the network device that represents - the 6pack port is called sp instead of sl or ax. So, sp0 would be the - first 6pack port. - -Although the driver has been tested on various platforms, I still declare it -ALPHA. BE CAREFUL! Sync your disks before insmoding the 6pack module -and spattaching. Watch out if your computer behaves strangely. Read section -6 of this file about known problems. - -Note that the connect and status LEDs of the TNC are controlled in a -different way than they are when the TNC is used with PC/FlexNet. When using -FlexNet, the connect LED is on if there is a connection; the status LED is -on if there is data in the buffer of the PC's AX.25 engine that has to be -transmitted. Under Linux, the 6pack layer is beyond the AX.25 layer, -so the 6pack driver doesn't know anything about connects or data that -has not yet been transmitted. Therefore the LEDs are controlled -as they are in KISS mode: The connect LED is turned on if data is transferred -from the PC to the TNC over the serial line, the status LED if data is -sent to the PC. - -6. Known problems -================= - -When testing the driver with 2.0.3x kernels and -operating with data rates on the radio channel of 9600 Baud or higher, -the driver may, on certain systems, sometimes print the message '6pack: -bad checksum', which is due to data loss if the other station sends two -or more subsequent packets. I have been told that this is due to a problem -with the serial driver of 2.0.3x kernels. I don't know yet if the problem -still exists with 2.1.x kernels, as I have heard that the serial driver -code has been changed with 2.1.x. - -When shutting down the sp interface with ifconfig, the kernel crashes if -there is still an AX.25 connection left over which an IP connection was -running, even if that IP connection is already closed. The problem does not -occur when there is a bare AX.25 connection still running. I don't know if -this is a problem of the 6pack driver or something else in the kernel. - -The driver has been tested as a module, not yet as a kernel-builtin driver. - -The 6pack protocol supports daisy-chaining of TNCs in a token ring, which is -connected to one serial port of the PC. This feature is not implemented -and at least at the moment I won't be able to do it because I do not have -the opportunity to build a TNC daisy-chain and test it. - -Some of the comments in the source code are inaccurate. They are left from -the SLIP/KISS driver, from which the 6pack driver has been derived. -I haven't modified or removed them yet -- sorry! The code itself needs -some cleaning and optimizing. This will be done in a later release. - -If you encounter a bug or if you have a question or suggestion concerning the -driver, feel free to mail me, using the addresses given at the beginning of -this file. - -Have fun! - -Andreas diff --git a/Documentation/networking/ax25.rst b/Documentation/networking/ax25.rst deleted file mode 100644 index 89c79dd6c6f9..000000000000 --- a/Documentation/networking/ax25.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -===== -AX.25 -===== - -To use the amateur radio protocols within Linux you will need to get a -suitable copy of the AX.25 Utilities. More detailed information about -AX.25, NET/ROM and ROSE, associated programs and utilities can be -found on https://linux-ax25.in-berlin.de. - -There is a mailing list for discussing Linux amateur radio matters -called linux-hams@vger.kernel.org. To subscribe to it, send a message to -linux-hams+subscribe@vger.kernel.org or use the web interface at -https://vger.kernel.org. The subject and body of the message are -ignored. You don't need to be subscribed to post but of course that -means you might miss an answer. diff --git a/Documentation/networking/caif/caif.rst b/Documentation/networking/caif/caif.rst deleted file mode 100644 index d922d419c513..000000000000 --- a/Documentation/networking/caif/caif.rst +++ /dev/null @@ -1,138 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 -.. include:: - - -================ -Using Linux CAIF -================ - - -:Copyright: |copy| ST-Ericsson AB 2010 - -:Author: Sjur Brendeland/ sjur.brandeland@stericsson.com - -Start -===== - -If you have compiled CAIF for modules do:: - - $modprobe crc_ccitt - $modprobe caif - $modprobe caif_socket - $modprobe chnl_net - - -Preparing the setup with a STE modem -==================================== - -If you are working on integration of CAIF you should make sure -that the kernel is built with module support. - -There are some things that need to be tweaked to get the host TTY correctly -set up to talk to the modem. -Since the CAIF stack is running in the kernel and we want to use the existing -TTY, we are installing our physical serial driver as a line discipline above -the TTY device. - -To achieve this we need to install the N_CAIF ldisc from user space. -The benefit is that we can hook up to any TTY. - -The use of Start-of-frame-extension (STX) must also be set as -module parameter "ser_use_stx". - -Normally Frame Checksum is always used on UART, but this is also provided as a -module parameter "ser_use_fcs". - -:: - - $ modprobe caif_serial ser_ttyname=/dev/ttyS0 ser_use_stx=yes - $ ifconfig caif_ttyS0 up - -PLEASE NOTE: - There is a limitation in Android shell. - It only accepts one argument to insmod/modprobe! - -Trouble shooting -================ - -There are debugfs parameters provided for serial communication. -/sys/kernel/debug/caif_serial// - -* ser_state: Prints the bit-mask status where - - - 0x02 means SENDING, this is a transient state. - - 0x10 means FLOW_OFF_SENT, i.e. the previous frame has not been sent - and is blocking further send operation. Flow OFF has been propagated - to all CAIF Channels using this TTY. - -* tty_status: Prints the bit-mask tty status information - - - 0x01 - tty->warned is on. - - 0x04 - tty->packed is on. - - 0x08 - tty->flow.tco_stopped is on. - - 0x10 - tty->hw_stopped is on. - - 0x20 - tty->flow.stopped is on. - -* last_tx_msg: Binary blob Prints the last transmitted frame. - - This can be printed with:: - - $od --format=x1 /sys/kernel/debug/caif_serial//last_rx_msg. - - The first two tx messages sent look like this. Note: The initial - byte 02 is start of frame extension (STX) used for re-syncing - upon errors. - - - Enumeration:: - - 0000000 02 05 00 00 03 01 d2 02 - | | | | | | - STX(1) | | | | - Length(2)| | | - Control Channel(1) - Command:Enumeration(1) - Link-ID(1) - Checksum(2) - - - Channel Setup:: - - 0000000 02 07 00 00 00 21 a1 00 48 df - | | | | | | | | - STX(1) | | | | | | - Length(2)| | | | | - Control Channel(1) - Command:Channel Setup(1) - Channel Type(1) - Priority and Link-ID(1) - Endpoint(1) - Checksum(2) - -* last_rx_msg: Prints the last transmitted frame. - - The RX messages for LinkSetup look almost identical but they have the - bit 0x20 set in the command bit, and Channel Setup has added one byte - before Checksum containing Channel ID. - - NOTE: - Several CAIF Messages might be concatenated. The maximum debug - buffer size is 128 bytes. - -Error Scenarios -=============== - -- last_tx_msg contains channel setup message and last_rx_msg is empty -> - The host seems to be able to send over the UART, at least the CAIF ldisc get - notified that sending is completed. - -- last_tx_msg contains enumeration message and last_rx_msg is empty -> - The host is not able to send the message from UART, the tty has not been - able to complete the transmit operation. - -- if /sys/kernel/debug/caif_serial//tty_status is non-zero there - might be problems transmitting over UART. - - E.g. host and modem wiring is not correct you will typically see - tty_status = 0x10 (hw_stopped) and ser_state = 0x10 (FLOW_OFF_SENT). - - You will probably see the enumeration message in last_tx_message - and empty last_rx_message. diff --git a/Documentation/networking/caif/index.rst b/Documentation/networking/caif/index.rst deleted file mode 100644 index ec29b6f4bdb4..000000000000 --- a/Documentation/networking/caif/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -CAIF -==== - -Contents: - -.. toctree:: - :maxdepth: 2 - - linux_caif - caif diff --git a/Documentation/networking/caif/linux_caif.rst b/Documentation/networking/caif/linux_caif.rst deleted file mode 100644 index a0480862ab8c..000000000000 --- a/Documentation/networking/caif/linux_caif.rst +++ /dev/null @@ -1,195 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 -.. include:: - -========== -Linux CAIF -========== - -Copyright |copy| ST-Ericsson AB 2010 - -:Author: Sjur Brendeland/ sjur.brandeland@stericsson.com -:License terms: GNU General Public License (GPL) version 2 - - -Introduction -============ - -CAIF is a MUX protocol used by ST-Ericsson cellular modems for -communication between Modem and host. The host processes can open virtual AT -channels, initiate GPRS Data connections, Video channels and Utility Channels. -The Utility Channels are general purpose pipes between modem and host. - -ST-Ericsson modems support a number of transports between modem -and host. Currently, UART and Loopback are available for Linux. - - -Architecture -============ - -The implementation of CAIF is divided into: - -* CAIF Socket Layer and GPRS IP Interface. -* CAIF Core Protocol Implementation -* CAIF Link Layer, implemented as NET devices. - -:: - - RTNL - ! - ! +------+ +------+ - ! +------+! +------+! - ! ! IP !! !Socket!! - +-------> !interf!+ ! API !+ <- CAIF Client APIs - ! +------+ +------! - ! ! ! - ! +-----------+ - ! ! - ! +------+ <- CAIF Core Protocol - ! ! CAIF ! - ! ! Core ! - ! +------+ - ! +----------!---------+ - ! ! ! ! - ! +------+ +-----+ +------+ - +--> ! HSI ! ! TTY ! ! USB ! <- Link Layer (Net Devices) - +------+ +-----+ +------+ - - - -Implementation -============== - - -CAIF Core Protocol Layer ------------------------- - -CAIF Core layer implements the CAIF protocol as defined by ST-Ericsson. -It implements the CAIF protocol stack in a layered approach, where -each layer described in the specification is implemented as a separate layer. -The architecture is inspired by the design patterns "Protocol Layer" and -"Protocol Packet". - -CAIF structure -^^^^^^^^^^^^^^ - -The Core CAIF implementation contains: - - - Simple implementation of CAIF. - - Layered architecture (a la Streams), each layer in the CAIF - specification is implemented in a separate c-file. - - Clients must call configuration function to add PHY layer. - - Clients must implement CAIF layer to consume/produce - CAIF payload with receive and transmit functions. - - Clients must call configuration function to add and connect the - Client layer. - - When receiving / transmitting CAIF Packets (cfpkt), ownership is passed - to the called function (except for framing layers' receive function) - -Layered Architecture -==================== - -The CAIF protocol can be divided into two parts: Support functions and Protocol -Implementation. The support functions include: - - - CFPKT CAIF Packet. Implementation of CAIF Protocol Packet. The - CAIF Packet has functions for creating, destroying and adding content - and for adding/extracting header and trailers to protocol packets. - -The CAIF Protocol implementation contains: - - - CFCNFG CAIF Configuration layer. Configures the CAIF Protocol - Stack and provides a Client interface for adding Link-Layer and - Driver interfaces on top of the CAIF Stack. - - - CFCTRL CAIF Control layer. Encodes and Decodes control messages - such as enumeration and channel setup. Also matches request and - response messages. - - - CFSERVL General CAIF Service Layer functionality; handles flow - control and remote shutdown requests. - - - CFVEI CAIF VEI layer. Handles CAIF AT Channels on VEI (Virtual - External Interface). This layer encodes/decodes VEI frames. - - - CFDGML CAIF Datagram layer. Handles CAIF Datagram layer (IP - traffic), encodes/decodes Datagram frames. - - - CFMUX CAIF Mux layer. Handles multiplexing between multiple - physical bearers and multiple channels such as VEI, Datagram, etc. - The MUX keeps track of the existing CAIF Channels and - Physical Instances and selects the appropriate instance based - on Channel-Id and Physical-ID. - - - CFFRML CAIF Framing layer. Handles Framing i.e. Frame length - and frame checksum. - - - CFSERL CAIF Serial layer. Handles concatenation/split of frames - into CAIF Frames with correct length. - -:: - - +---------+ - | Config | - | CFCNFG | - +---------+ - ! - +---------+ +---------+ +---------+ - | AT | | Control | | Datagram| - | CFVEIL | | CFCTRL | | CFDGML | - +---------+ +---------+ +---------+ - \_____________!______________/ - ! - +---------+ - | MUX | - | | - +---------+ - _____!_____ - / \ - +---------+ +---------+ - | CFFRML | | CFFRML | - | Framing | | Framing | - +---------+ +---------+ - ! ! - +---------+ +---------+ - | | | Serial | - | | | CFSERL | - +---------+ +---------+ - - -In this layered approach the following "rules" apply. - - - All layers embed the same structure "struct cflayer" - - A layer does not depend on any other layer's private data. - - Layers are stacked by setting the pointers:: - - layer->up , layer->dn - - - In order to send data upwards, each layer should do:: - - layer->up->receive(layer->up, packet); - - - In order to send data downwards, each layer should do:: - - layer->dn->transmit(layer->dn, packet); - - -CAIF Socket and IP interface -============================ - -The IP interface and CAIF socket API are implemented on top of the -CAIF Core protocol. The IP Interface and CAIF socket have an instance of -'struct cflayer', just like the CAIF Core protocol stack. -Net device and Socket implement the 'receive()' function defined by -'struct cflayer', just like the rest of the CAIF stack. In this way, transmit and -receive of packets is handled as by the rest of the layers: the 'dn->transmit()' -function is called in order to transmit data. - -Configuration of Link Layer ---------------------------- -The Link Layer is implemented as Linux network devices (struct net_device). -Payload handling and registration is done using standard Linux mechanisms. - -The CAIF Protocol relies on a loss-less link layer without implementing -retransmission. This implies that packet drops must not happen. -Therefore a flow-control mechanism is implemented where the physical -interface can initiate flow stop for all CAIF Channels. diff --git a/Documentation/networking/device_drivers/atm/fore200e.rst b/Documentation/networking/device_drivers/atm/fore200e.rst deleted file mode 100644 index 55df9ec09ac8..000000000000 --- a/Documentation/networking/device_drivers/atm/fore200e.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -============================================= -FORE Systems PCA-200E/SBA-200E ATM NIC driver -============================================= - -This driver adds support for the FORE Systems 200E-series ATM adapters -to the Linux operating system. It is based on the earlier PCA-200E driver -written by Uwe Dannowski. - -The driver simultaneously supports PCA-200E and SBA-200E adapters on -i386, alpha (untested), powerpc, sparc and sparc64 archs. - -The intent is to enable the use of different models of FORE adapters at the -same time, by hosts that have several bus interfaces (such as PCI+SBUS, -or PCI+EISA). - -Only PCI and SBUS devices are currently supported by the driver, but support -for other bus interfaces such as EISA should not be too hard to add. - - -Firmware Copyright Notice -------------------------- - -Please read the fore200e_firmware_copyright file present -in the linux/drivers/atm directory for details and restrictions. - - -Firmware Updates ----------------- - -The FORE Systems 200E-series driver is shipped with firmware data being -uploaded to the ATM adapters at system boot time or at module loading time. -The supplied firmware images should work with all adapters. - -However, if you encounter problems (the firmware doesn't start or the driver -is unable to read the PROM data), you may consider trying another firmware -version. Alternative binary firmware images can be found somewhere on the -ForeThought CD-ROM supplied with your adapter by FORE Systems. - -You can also get the latest firmware images from FORE Systems at -https://en.wikipedia.org/wiki/FORE_Systems. Register TACTics Online and go to -the 'software updates' pages. The firmware binaries are part of -the various ForeThought software distributions. - -Notice that different versions of the PCA-200E firmware exist, depending -on the endianness of the host architecture. The driver is shipped with -both little and big endian PCA firmware images. - -Name and location of the new firmware images can be set at kernel -configuration time: - -1. Copy the new firmware binary files (with .bin, .bin1 or .bin2 suffix) - to some directory, such as linux/drivers/atm. - -2. Reconfigure your kernel to set the new firmware name and location. - Expected pathnames are absolute or relative to the drivers/atm directory. - -3. Rebuild and re-install your kernel or your module. - - -Feedback --------- - -Feedback is welcome. Please send success stories/bug reports/ -patches/improvement/comments/flames to . diff --git a/Documentation/networking/device_drivers/atm/index.rst b/Documentation/networking/device_drivers/atm/index.rst index 724552ca0be4..9392c86f48bc 100644 --- a/Documentation/networking/device_drivers/atm/index.rst +++ b/Documentation/networking/device_drivers/atm/index.rst @@ -9,5 +9,3 @@ Contents: :maxdepth: 2 cxacru - fore200e - iphase diff --git a/Documentation/networking/device_drivers/atm/iphase.rst b/Documentation/networking/device_drivers/atm/iphase.rst deleted file mode 100644 index 388c7101e2cb..000000000000 --- a/Documentation/networking/device_drivers/atm/iphase.rst +++ /dev/null @@ -1,193 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -================================== -ATM (i)Chip IA Linux Driver Source -================================== - - READ ME FIRST - --------------------------------------------------------------------------------- - - Read This Before You Begin! - --------------------------------------------------------------------------------- - -Description -=========== - -This is the README file for the Interphase PCI ATM (i)Chip IA Linux driver -source release. - -The features and limitations of this driver are as follows: - - - A single VPI (VPI value of 0) is supported. - - Supports 4K VCs for the server board (with 512K control memory) and 1K - VCs for the client board (with 128K control memory). - - UBR, ABR and CBR service categories are supported. - - Only AAL5 is supported. - - Supports setting of PCR on the VCs. - - Multiple adapters in a system are supported. - - All variants of Interphase ATM PCI (i)Chip adapter cards are supported, - including x575 (OC3, control memory 128K , 512K and packet memory 128K, - 512K and 1M), x525 (UTP25) and x531 (DS3 and E3). See - http://www.iphase.com/ - for details. - - Only x86 platforms are supported. - - SMP is supported. - - -Before You Start -================ - - -Installation ------------- - -1. Installing the adapters in the system - - To install the ATM adapters in the system, follow the steps below. - - a. Login as root. - b. Shut down the system and power off the system. - c. Install one or more ATM adapters in the system. - d. Connect each adapter to a port on an ATM switch. The green 'Link' - LED on the front panel of the adapter will be on if the adapter is - connected to the switch properly when the system is powered up. - e. Power on and boot the system. - -2. [ Removed ] - -3. Rebuild kernel with ABR support - - [ a. and b. removed ] - - c. Reconfigure the kernel, choose the Interphase ia driver through "make - menuconfig" or "make xconfig". - d. Rebuild the kernel, loadable modules and the atm tools. - e. Install the new built kernel and modules and reboot. - -4. Load the adapter hardware driver (ia driver) if it is built as a module - - a. Login as root. - b. Change directory to /lib/modules//atm. - c. Run "insmod suni.o;insmod iphase.o" - The yellow 'status' LED on the front panel of the adapter will blink - while the driver is loaded in the system. - d. To verify that the 'ia' driver is loaded successfully, run the - following command:: - - cat /proc/atm/devices - - If the driver is loaded successfully, the output of the command will - be similar to the following lines:: - - Itf Type ESI/"MAC"addr AAL(TX,err,RX,err,drop) ... - 0 ia xxxxxxxxx 0 ( 0 0 0 0 0 ) 5 ( 0 0 0 0 0 ) - - You can also check the system log file /var/log/messages for messages - related to the ATM driver. - -5. Ia Driver Configuration - -5.1 Configuration of adapter buffers - The (i)Chip boards have 3 different packet RAM size variants: 128K, 512K and - 1M. The RAM size decides the number of buffers and buffer size. The default - size and number of buffers are set as following: - - ========= ======= ====== ====== ====== ====== ====== - Total Rx RAM Tx RAM Rx Buf Tx Buf Rx buf Tx buf - RAM size size size size size cnt cnt - ========= ======= ====== ====== ====== ====== ====== - 128K 64K 64K 10K 10K 6 6 - 512K 256K 256K 10K 10K 25 25 - 1M 512K 512K 10K 10K 51 51 - ========= ======= ====== ====== ====== ====== ====== - - These setting should work well in most environments, but can be - changed by typing the following command:: - - insmod /ia.o IA_RX_BUF= IA_RX_BUF_SZ= \ - IA_TX_BUF= IA_TX_BUF_SZ= - - Where: - - - RX_CNT = number of receive buffers in the range (1-128) - - RX_SIZE = size of receive buffers in the range (48-64K) - - TX_CNT = number of transmit buffers in the range (1-128) - - TX_SIZE = size of transmit buffers in the range (48-64K) - - 1. Transmit and receive buffer size must be a multiple of 4. - 2. Care should be taken so that the memory required for the - transmit and receive buffers is less than or equal to the - total adapter packet memory. - -5.2 Turn on ia debug trace - - When the ia driver is built with the CONFIG_ATM_IA_DEBUG flag, the driver - can provide more debug trace if needed. There is a bit mask variable, - IADebugFlag, which controls the output of the traces. You can find the bit - map of the IADebugFlag in iphase.h. - The debug trace can be turn on through the insmod command line option, for - example, "insmod iphase.o IADebugFlag=0xffffffff" can turn on all the debug - traces together with loading the driver. - -6. Ia Driver Test Using ttcp_atm and PVC - - For the PVC setup, the test machines can either be connected back-to-back or - through a switch. If connected through the switch, the switch must be - configured for the PVC(s). - - a. For UBR test: - - At the test machine intended to receive data, type:: - - ttcp_atm -r -a -s 0.100 - - At the other test machine, type:: - - ttcp_atm -t -a -s 0.100 -n 10000 - - Run "ttcp_atm -h" to display more options of the ttcp_atm tool. - b. For ABR test: - - It is the same as the UBR testing, but with an extra command option:: - - -Pabr:max_pcr= - - where: - - xxx = the maximum peak cell rate, from 170 - 353207. - - This option must be set on both the machines. - - c. For CBR test: - - It is the same as the UBR testing, but with an extra command option:: - - -Pcbr:max_pcr= - - where: - - xxx = the maximum peak cell rate, from 170 - 353207. - - This option may only be set on the transmit machine. - - -Outstanding Issues -================== - - - -Contact Information -------------------- - -:: - - Customer Support: - United States: Telephone: (214) 654-5555 - Fax: (214) 654-5500 - E-Mail: intouch@iphase.com - Europe: Telephone: 33 (0)1 41 15 44 00 - Fax: 33 (0)1 41 15 12 13 - World Wide Web: http://www.iphase.com - Anonymous FTP: ftp.iphase.com diff --git a/Documentation/networking/device_drivers/ethernet/3com/3c509.rst b/Documentation/networking/device_drivers/ethernet/3com/3c509.rst deleted file mode 100644 index 47f706bacdd9..000000000000 --- a/Documentation/networking/device_drivers/ethernet/3com/3c509.rst +++ /dev/null @@ -1,249 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -============================================================================= -Linux and the 3Com EtherLink III Series Ethercards (driver v1.18c and higher) -============================================================================= - -This file contains the instructions and caveats for v1.18c and higher versions -of the 3c509 driver. You should not use the driver without reading this file. - -release 1.0 - -28 February 2002 - -Current maintainer (corrections to): - David Ruggiero - -Introduction -============ - -The following are notes and information on using the 3Com EtherLink III series -ethercards in Linux. These cards are commonly known by the most widely-used -card's 3Com model number, 3c509. They are all 10mb/s ISA-bus cards and shouldn't -be (but sometimes are) confused with the similarly-numbered PCI-bus "3c905" -(aka "Vortex" or "Boomerang") series. Kernel support for the 3c509 family is -provided by the module 3c509.c, which has code to support all of the following -models: - - - 3c509 (original ISA card) - - 3c509B (later revision of the ISA card; supports full-duplex) - - 3c589 (PCMCIA) - - 3c589B (later revision of the 3c589; supports full-duplex) - - 3c579 (EISA) - -Large portions of this documentation were heavily borrowed from the guide -written the original author of the 3c509 driver, Donald Becker. The master -copy of that document, which contains notes on older versions of the driver, -currently resides on Scyld web server: http://www.scyld.com/. - - -Special Driver Features -======================= - -Overriding card settings - -The driver allows boot- or load-time overriding of the card's detected IOADDR, -IRQ, and transceiver settings, although this capability shouldn't generally be -needed except to enable full-duplex mode (see below). An example of the syntax -for LILO parameters for doing this:: - - ether=10,0x310,3,0x3c509,eth0 - -This configures the first found 3c509 card for IRQ 10, base I/O 0x310, and -transceiver type 3 (10base2). The flag "0x3c509" must be set to avoid conflicts -with other card types when overriding the I/O address. When the driver is -loaded as a module, only the IRQ may be overridden. For example, -setting two cards to IRQ10 and IRQ11 is done by using the irq module -option:: - - options 3c509 irq=10,11 - - -Full-duplex mode -================ - -The v1.18c driver added support for the 3c509B's full-duplex capabilities. -In order to enable and successfully use full-duplex mode, three conditions -must be met: - -(a) You must have a Etherlink III card model whose hardware supports full- -duplex operations. Currently, the only members of the 3c509 family that are -positively known to support full-duplex are the 3c509B (ISA bus) and 3c589B -(PCMCIA) cards. Cards without the "B" model designation do *not* support -full-duplex mode; these include the original 3c509 (no "B"), the original -3c589, the 3c529 (MCA bus), and the 3c579 (EISA bus). - -(b) You must be using your card's 10baseT transceiver (i.e., the RJ-45 -connector), not its AUI (thick-net) or 10base2 (thin-net/coax) interfaces. -AUI and 10base2 network cabling is physically incapable of full-duplex -operation. - -(c) Most importantly, your 3c509B must be connected to a link partner that is -itself full-duplex capable. This is almost certainly one of two things: a full- -duplex-capable Ethernet switch (*not* a hub), or a full-duplex-capable NIC on -another system that's connected directly to the 3c509B via a crossover cable. - -Full-duplex mode can be enabled using 'ethtool'. - -.. warning:: - - Extremely important caution concerning full-duplex mode - - Understand that the 3c509B's hardware's full-duplex support is much more - limited than that provide by more modern network interface cards. Although - at the physical layer of the network it fully supports full-duplex operation, - the card was designed before the current Ethernet auto-negotiation (N-way) - spec was written. This means that the 3c509B family ***cannot and will not - auto-negotiate a full-duplex connection with its link partner under any - circumstances, no matter how it is initialized***. If the full-duplex mode - of the 3c509B is enabled, its link partner will very likely need to be - independently _forced_ into full-duplex mode as well; otherwise various nasty - failures will occur - at the very least, you'll see massive numbers of packet - collisions. This is one of very rare circumstances where disabling auto- - negotiation and forcing the duplex mode of a network interface card or switch - would ever be necessary or desirable. - - -Available Transceiver Types -=========================== - -For versions of the driver v1.18c and above, the available transceiver types are: - -== ========================================================================= -0 transceiver type from EEPROM config (normally 10baseT); force half-duplex -1 AUI (thick-net / DB15 connector) -2 (undefined) -3 10base2 (thin-net == coax / BNC connector) -4 10baseT (RJ-45 connector); force half-duplex mode -8 transceiver type and duplex mode taken from card's EEPROM config settings -12 10baseT (RJ-45 connector); force full-duplex mode -== ========================================================================= - -Prior to driver version 1.18c, only transceiver codes 0-4 were supported. Note -that the new transceiver codes 8 and 12 are the *only* ones that will enable -full-duplex mode, no matter what the card's detected EEPROM settings might be. -This insured that merely upgrading the driver from an earlier version would -never automatically enable full-duplex mode in an existing installation; -it must always be explicitly enabled via one of these code in order to be -activated. - -The transceiver type can be changed using 'ethtool'. - - -Interpretation of error messages and common problems ----------------------------------------------------- - -Error Messages -^^^^^^^^^^^^^^ - -eth0: Infinite loop in interrupt, status 2011. -These are "mostly harmless" message indicating that the driver had too much -work during that interrupt cycle. With a status of 0x2011 you are receiving -packets faster than they can be removed from the card. This should be rare -or impossible in normal operation. Possible causes of this error report are: - - - a "green" mode enabled that slows the processor down when there is no - keyboard activity. - - - some other device or device driver hogging the bus or disabling interrupts. - Check /proc/interrupts for excessive interrupt counts. The timer tick - interrupt should always be incrementing faster than the others. - -No received packets -^^^^^^^^^^^^^^^^^^^ - -If a 3c509, 3c562 or 3c589 can successfully transmit packets, but never -receives packets (as reported by /proc/net/dev or 'ifconfig') you likely -have an interrupt line problem. Check /proc/interrupts to verify that the -card is actually generating interrupts. If the interrupt count is not -increasing you likely have a physical conflict with two devices trying to -use the same ISA IRQ line. The common conflict is with a sound card on IRQ10 -or IRQ5, and the easiest solution is to move the 3c509 to a different -interrupt line. If the device is receiving packets but 'ping' doesn't work, -you have a routing problem. - -Tx Carrier Errors Reported in /proc/net/dev -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - -If an EtherLink III appears to transmit packets, but the "Tx carrier errors" -field in /proc/net/dev increments as quickly as the Tx packet count, you -likely have an unterminated network or the incorrect media transceiver selected. - -3c509B card is not detected on machines with an ISA PnP BIOS. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -While the updated driver works with most PnP BIOS programs, it does not work -with all. This can be fixed by disabling PnP support using the 3Com-supplied -setup program. - -3c509 card is not detected on overclocked machines -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Increase the delay time in id_read_eeprom() from the current value, 500, -to an absurdly high value, such as 5000. - - -Decoding Status and Error Messages ----------------------------------- - - -The bits in the main status register are: - -===== ====================================== -value description -===== ====================================== -0x01 Interrupt latch -0x02 Tx overrun, or Rx underrun -0x04 Tx complete -0x08 Tx FIFO room available -0x10 A complete Rx packet has arrived -0x20 A Rx packet has started to arrive -0x40 The driver has requested an interrupt -0x80 Statistics counter nearly full -===== ====================================== - -The bits in the transmit (Tx) status word are: - -===== ============================================ -value description -===== ============================================ -0x02 Out-of-window collision. -0x04 Status stack overflow (normally impossible). -0x08 16 collisions. -0x10 Tx underrun (not enough PCI bus bandwidth). -0x20 Tx jabber. -0x40 Tx interrupt requested. -0x80 Status is valid (this should always be set). -===== ============================================ - - -When a transmit error occurs the driver produces a status message such as:: - - eth0: Transmit error, Tx status register 82 - -The two values typically seen here are: - -0x82 -^^^^ - -Out of window collision. This typically occurs when some other Ethernet -host is incorrectly set to full duplex on a half duplex network. - -0x88 -^^^^ - -16 collisions. This typically occurs when the network is exceptionally busy -or when another host doesn't correctly back off after a collision. If this -error is mixed with 0x82 errors it is the result of a host incorrectly set -to full duplex (see above). - -Both of these errors are the result of network problems that should be -corrected. They do not represent driver malfunction. - - -Revision history (this file) -============================ - -28Feb02 v1.0 DR New; major portions based on Becker original 3c509 docs - diff --git a/Documentation/networking/device_drivers/ethernet/index.rst b/Documentation/networking/device_drivers/ethernet/index.rst index 5f3f06111911..64621c21fd78 100644 --- a/Documentation/networking/device_drivers/ethernet/index.rst +++ b/Documentation/networking/device_drivers/ethernet/index.rst @@ -10,7 +10,6 @@ Contents: .. toctree:: :maxdepth: 2 - 3com/3c509 3com/vortex amazon/ena altera/altera_tse @@ -52,7 +51,6 @@ Contents: pensando/ionic pensando/ionic_rdma qualcomm/ppe/ppe - smsc/smc9 stmicro/stmmac ti/cpsw ti/cpsw_switchdev diff --git a/Documentation/networking/device_drivers/ethernet/smsc/smc9.rst b/Documentation/networking/device_drivers/ethernet/smsc/smc9.rst deleted file mode 100644 index e5eac896a631..000000000000 --- a/Documentation/networking/device_drivers/ethernet/smsc/smc9.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -================ -SMC 9xxxx Driver -================ - -Revision 0.12 - -3/5/96 - -Copyright 1996 Erik Stahlman - -Released under terms of the GNU General Public License. - -This file contains the instructions and caveats for my SMC9xxx driver. You -should not be using the driver without reading this file. - -Things to note about installation: - - 1. The driver should work on all kernels from 1.2.13 until 1.3.71. - (A kernel patch is supplied for 1.3.71 ) - - 2. If you include this into the kernel, you might need to change some - options, such as for forcing IRQ. - - - 3. To compile as a module, run 'make'. - Make will give you the appropriate options for various kernel support. - - 4. Loading the driver as a module:: - - use: insmod smc9194.o - optional parameters: - io=xxxx : your base address - irq=xx : your irq - ifport=x : 0 for whatever is default - 1 for twisted pair - 2 for AUI ( or BNC on some cards ) - -How to obtain the latest version? - -FTP: - ftp://fenris.campus.vt.edu/smc9/smc9-12.tar.gz - ftp://sfbox.vt.edu/filebox/F/fenris/smc9/smc9-12.tar.gz - - -Contacting me: - erik@mail.vt.edu diff --git a/Documentation/networking/device_drivers/hamradio/baycom.rst b/Documentation/networking/device_drivers/hamradio/baycom.rst deleted file mode 100644 index fe2d010f0e86..000000000000 --- a/Documentation/networking/device_drivers/hamradio/baycom.rst +++ /dev/null @@ -1,174 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -=============================== -Linux Drivers for Baycom Modems -=============================== - -Thomas M. Sailer, HB9JNX/AE4WA, - -The drivers for the baycom modems have been split into -separate drivers as they did not share any code, and the driver -and device names have changed. - -This document describes the Linux Kernel Drivers for simple Baycom style -amateur radio modems. - -The following drivers are available: -==================================== - -baycom_ser_fdx: - This driver supports the SER12 modems either full or half duplex. - Its baud rate may be changed via the ``baud`` module parameter, - therefore it supports just about every bit bang modem on a - serial port. Its devices are called bcsf0 through bcsf3. - This is the recommended driver for SER12 type modems, - however if you have a broken UART clone that does not have working - delta status bits, you may try baycom_ser_hdx. - -baycom_ser_hdx: - This is an alternative driver for SER12 type modems. - It only supports half duplex, and only 1200 baud. Its devices - are called bcsh0 through bcsh3. Use this driver only if baycom_ser_fdx - does not work with your UART. - -baycom_par: - This driver supports the par96 and picpar modems. - Its devices are called bcp0 through bcp3. - -baycom_epp: - This driver supports the EPP modem. - Its devices are called bce0 through bce3. - This driver is work-in-progress. - -The following modems are supported: - -======= ======================================================================== -ser12 This is a very simple 1200 baud AFSK modem. The modem consists only - of a modulator/demodulator chip, usually a TI TCM3105. The computer - is responsible for regenerating the receiver bit clock, as well as - for handling the HDLC protocol. The modem connects to a serial port, - hence the name. Since the serial port is not used as an async serial - port, the kernel driver for serial ports cannot be used, and this - driver only supports standard serial hardware (8250, 16450, 16550) - -par96 This is a modem for 9600 baud FSK compatible to the G3RUH standard. - The modem does all the filtering and regenerates the receiver clock. - Data is transferred from and to the PC via a shift register. - The shift register is filled with 16 bits and an interrupt is signalled. - The PC then empties the shift register in a burst. This modem connects - to the parallel port, hence the name. The modem leaves the - implementation of the HDLC protocol and the scrambler polynomial to - the PC. - -picpar This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem - is protocol compatible to par96, but uses only three low power ICs - and can therefore be fed from the parallel port and does not require - an additional power supply. Furthermore, it incorporates a carrier - detect circuitry. - -EPP This is a high-speed modem adaptor that connects to an enhanced parallel - port. - - Its target audience is users working over a high speed hub (76.8kbit/s). - -eppfpga This is a redesign of the EPP adaptor. -======= ======================================================================== - -All of the above modems only support half duplex communications. However, -the driver supports the KISS (see below) fullduplex command. It then simply -starts to send as soon as there's a packet to transmit and does not care -about DCD, i.e. it starts to send even if there's someone else on the channel. -This command is required by some implementations of the DAMA channel -access protocol. - - -The Interface of the drivers -============================ - -Unlike previous drivers, these drivers are no longer character devices, -but they are now true kernel network interfaces. Installation is therefore -simple. Once installed, four interfaces named bc{sf,sh,p,e}[0-3] are available. -sethdlc from the ax25 utilities may be used to set driver states etc. -Users of userland AX.25 stacks may use the net2kiss utility (also available -in the ax25 utilities package) to convert packets of a network interface -to a KISS stream on a pseudo tty. There's also a patch available from -me for WAMPES which allows attaching a kernel network interface directly. - - -Configuring the driver -====================== - -Every time a driver is inserted into the kernel, it has to know which -modems it should access at which ports. This can be done with the setbaycom -utility. If you are only using one modem, you can also configure the -driver from the insmod command line (or by means of an option line in -``/etc/modprobe.d/*.conf``). - -Examples:: - - modprobe baycom_ser_fdx mode="ser12*" iobase=0x3f8 irq=4 - sethdlc -i bcsf0 -p mode "ser12*" io 0x3f8 irq 4 - -Both lines configure the first port to drive a ser12 modem at the first -serial port (COM1 under DOS). The * in the mode parameter instructs the driver -to use the software DCD algorithm (see below):: - - insmod baycom_par mode="picpar" iobase=0x378 - sethdlc -i bcp0 -p mode "picpar" io 0x378 - -Both lines configure the first port to drive a picpar modem at the -first parallel port (LPT1 under DOS). (Note: picpar implies -hardware DCD, par96 implies software DCD). - -The channel access parameters can be set with sethdlc -a or kissparms. -Note that both utilities interpret the values slightly differently. - - -Hardware DCD versus Software DCD -================================ - -To avoid collisions on the air, the driver must know when the channel is -busy. This is the task of the DCD circuitry/software. The driver may either -utilise a software DCD algorithm (options=1) or use a DCD signal from -the hardware (options=0). - -======= ================================================================= -ser12 if software DCD is utilised, the radio's squelch should always be - open. It is highly recommended to use the software DCD algorithm, - as it is much faster than most hardware squelch circuitry. The - disadvantage is a slightly higher load on the system. - -par96 the software DCD algorithm for this type of modem is rather poor. - The modem simply does not provide enough information to implement - a reasonable DCD algorithm in software. Therefore, if your radio - feeds the DCD input of the PAR96 modem, the use of the hardware - DCD circuitry is recommended. - -picpar the picpar modem features a builtin DCD hardware, which is highly - recommended. -======= ================================================================= - - - -Compatibility with the rest of the Linux kernel -=============================================== - -The serial driver and the baycom serial drivers compete -for the same hardware resources. Of course only one driver can access a given -interface at a time. The serial driver grabs all interfaces it can find at -startup time. Therefore the baycom drivers subsequently won't be able to -access a serial port. You might therefore find it necessary to release -a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where -# is the number of the interface. The baycom drivers do not reserve any -ports at startup, unless one is specified on the 'insmod' command line. Another -method to solve the problem is to compile all drivers as modules and -leave it to kmod to load the correct driver depending on the application. - -The parallel port drivers (baycom_par, baycom_epp) now use the parport subsystem -to arbitrate the ports between different client drivers. - -vy 73s de - -Tom Sailer, sailer@ife.ee.ethz.ch - -hb9jnx @ hb9w.ampr.org diff --git a/Documentation/networking/device_drivers/hamradio/index.rst b/Documentation/networking/device_drivers/hamradio/index.rst deleted file mode 100644 index 6af481c5b020..000000000000 --- a/Documentation/networking/device_drivers/hamradio/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) - -Amateur Radio Device Drivers -============================ - -Contents: - -.. toctree:: - :maxdepth: 2 - - baycom - z8530drv diff --git a/Documentation/networking/device_drivers/hamradio/z8530drv.rst b/Documentation/networking/device_drivers/hamradio/z8530drv.rst deleted file mode 100644 index d2942760f167..000000000000 --- a/Documentation/networking/device_drivers/hamradio/z8530drv.rst +++ /dev/null @@ -1,686 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 -.. include:: - -========================================================= -SCC.C - Linux driver for Z8530 based HDLC cards for AX.25 -========================================================= - - -This is a subset of the documentation. To use this driver you MUST have the -full package from: - -Internet: - - 1. ftp://ftp.ccac.rwth-aachen.de/pub/jr/z8530drv-utils_3.0-3.tar.gz - - 2. ftp://ftp.pspt.fi/pub/ham/linux/ax25/z8530drv-utils_3.0-3.tar.gz - -Please note that the information in this document may be hopelessly outdated. -A new version of the documentation, along with links to other important -Linux Kernel AX.25 documentation and programs, is available on -http://yaina.de/jreuter - -Copyright |copy| 1993,2000 by Joerg Reuter DL1BKE - -portions Copyright |copy| 1993 Guido ten Dolle PE1NNZ - -for the complete copyright notice see >> Copying.Z8530DRV << - -1. Initialization of the driver -=============================== - -To use the driver, 3 steps must be performed: - - 1. if compiled as module: loading the module - 2. Setup of hardware, MODEM and KISS parameters with sccinit - 3. Attach each channel to the Linux kernel AX.25 with "ifconfig" - -Unlike the versions below 2.4 this driver is a real network device -driver. If you want to run xNOS instead of our fine kernel AX.25 -use a 2.x version (available from above sites) or read the -AX.25-HOWTO on how to emulate a KISS TNC on network device drivers. - - -1.1 Loading the module -====================== - -(If you're going to compile the driver as a part of the kernel image, - skip this chapter and continue with 1.2) - -Before you can use a module, you'll have to load it with:: - - insmod scc.o - -please read 'man insmod' that comes with module-init-tools. - -You should include the insmod in one of the /etc/rc.d/rc.* files, -and don't forget to insert a call of sccinit after that. It -will read your /etc/z8530drv.conf. - -1.2. /etc/z8530drv.conf -======================= - -To setup all parameters you must run /sbin/sccinit from one -of your rc.*-files. This has to be done BEFORE you can -"ifconfig" an interface. Sccinit reads the file /etc/z8530drv.conf -and sets the hardware, MODEM and KISS parameters. A sample file is -delivered with this package. Change it to your needs. - -The file itself consists of two main sections. - -1.2.1 configuration of hardware parameters -========================================== - -The hardware setup section defines the following parameters for each -Z8530:: - - chip 1 - data_a 0x300 # data port A - ctrl_a 0x304 # control port A - data_b 0x301 # data port B - ctrl_b 0x305 # control port B - irq 5 # IRQ No. 5 - pclock 4915200 # clock - board BAYCOM # hardware type - escc no # enhanced SCC chip? (8580/85180/85280) - vector 0 # latch for interrupt vector - special no # address of special function register - option 0 # option to set via sfr - - -chip - - this is just a delimiter to make sccinit a bit simpler to - program. A parameter has no effect. - -data_a - - the address of the data port A of this Z8530 (needed) -ctrl_a - - the address of the control port A (needed) -data_b - - the address of the data port B (needed) -ctrl_b - - the address of the control port B (needed) - -irq - - the used IRQ for this chip. Different chips can use different - IRQs or the same. If they share an interrupt, it needs to be - specified within one chip-definition only. - -pclock - the clock at the PCLK pin of the Z8530 (option, 4915200 is - default), measured in Hertz - -board - - the "type" of the board: - - ======================= ======== - SCC type value - ======================= ======== - PA0HZP SCC card PA0HZP - EAGLE card EAGLE - PC100 card PC100 - PRIMUS-PC (DG9BL) card PRIMUS - BayCom (U)SCC card BAYCOM - ======================= ======== - -escc - - if you want support for ESCC chips (8580, 85180, 85280), set - this to "yes" (option, defaults to "no") - -vector - - address of the vector latch (aka "intack port") for PA0HZP - cards. There can be only one vector latch for all chips! - (option, defaults to 0) - -special - - address of the special function register on several cards. - (option, defaults to 0) - -option - The value you write into that register (option, default is 0) - -You can specify up to four chips (8 channels). If this is not enough, -just change:: - - #define MAXSCC 4 - -to a higher value. - -Example for the BAYCOM USCC: ----------------------------- - -:: - - chip 1 - data_a 0x300 # data port A - ctrl_a 0x304 # control port A - data_b 0x301 # data port B - ctrl_b 0x305 # control port B - irq 5 # IRQ No. 5 (#) - board BAYCOM # hardware type (*) - # - # SCC chip 2 - # - chip 2 - data_a 0x302 - ctrl_a 0x306 - data_b 0x303 - ctrl_b 0x307 - board BAYCOM - -An example for a PA0HZP card: ------------------------------ - -:: - - chip 1 - data_a 0x153 - data_b 0x151 - ctrl_a 0x152 - ctrl_b 0x150 - irq 9 - pclock 4915200 - board PA0HZP - vector 0x168 - escc no - # - # - # - chip 2 - data_a 0x157 - data_b 0x155 - ctrl_a 0x156 - ctrl_b 0x154 - irq 9 - pclock 4915200 - board PA0HZP - vector 0x168 - escc no - -A DRSI would should probably work with this: --------------------------------------------- -(actually: two DRSI cards...) - -:: - - chip 1 - data_a 0x303 - data_b 0x301 - ctrl_a 0x302 - ctrl_b 0x300 - irq 7 - pclock 4915200 - board DRSI - escc no - # - # - # - chip 2 - data_a 0x313 - data_b 0x311 - ctrl_a 0x312 - ctrl_b 0x310 - irq 7 - pclock 4915200 - board DRSI - escc no - -Note that you cannot use the on-board baudrate generator off DRSI -cards. Use "mode dpll" for clock source (see below). - -This is based on information provided by Mike Bilow (and verified -by Paul Helay) - -The utility "gencfg" --------------------- - -If you only know the parameters for the PE1CHL driver for DOS, -run gencfg. It will generate the correct port addresses (I hope). -Its parameters are exactly the same as the ones you use with -the "attach scc" command in net, except that the string "init" must -not appear. Example:: - - gencfg 2 0x150 4 2 0 1 0x168 9 4915200 - -will print a skeleton z8530drv.conf for the OptoSCC to stdout. - -:: - - gencfg 2 0x300 2 4 5 -4 0 7 4915200 0x10 - -does the same for the BAYCOM USCC card. In my opinion it is much easier -to edit scc_config.h... - - -1.2.2 channel configuration -=========================== - -The channel definition is divided into three sub sections for each -channel: - -An example for scc0:: - - # DEVICE - - device scc0 # the device for the following params - - # MODEM / BUFFERS - - speed 1200 # the default baudrate - clock dpll # clock source: - # dpll = normal half duplex operation - # external = MODEM provides own Rx/Tx clock - # divider = use full duplex divider if - # installed (1) - mode nrzi # HDLC encoding mode - # nrzi = 1k2 MODEM, G3RUH 9k6 MODEM - # nrz = DF9IC 9k6 MODEM - # - bufsize 384 # size of buffers. Note that this must include - # the AX.25 header, not only the data field! - # (optional, defaults to 384) - - # KISS (Layer 1) - - txdelay 36 # (see chapter 1.4) - persist 64 - slot 8 - tail 8 - fulldup 0 - wait 12 - min 3 - maxkey 7 - idle 3 - maxdef 120 - group 0 - txoff off - softdcd on - slip off - -The order WITHIN these sections is unimportant. The order OF these -sections IS important. The MODEM parameters are set with the first -recognized KISS parameter... - -Please note that you can initialize the board only once after boot -(or insmod). You can change all parameters but "mode" and "clock" -later with the Sccparam program or through KISS. Just to avoid -security holes... - -(1) this divider is usually mounted on the SCC-PBC (PA0HZP) or not - present at all (BayCom). It feeds back the output of the DPLL - (digital pll) as transmit clock. Using this mode without a divider - installed will normally result in keying the transceiver until - maxkey expires --- of course without sending anything (useful). - -2. Attachment of a channel by your AX.25 software -================================================= - -2.1 Kernel AX.25 -================ - -To set up an AX.25 device you can simply type:: - - ifconfig scc0 44.128.1.1 hw ax25 dl0tha-7 - -This will create a network interface with the IP number 44.128.20.107 -and the callsign "dl0tha". If you do not have any IP number (yet) you -can use any of the 44.128.0.0 network. Note that you do not need -axattach. The purpose of axattach (like slattach) is to create a KISS -network device linked to a TTY. Please read the documentation of the -ax25-utils and the AX.25-HOWTO to learn how to set the parameters of -the kernel AX.25. - -2.2 NOS, NET and TFKISS -======================= - -Since the TTY driver (aka KISS TNC emulation) is gone you need -to emulate the old behaviour. The cost of using these programs is -that you probably need to compile the kernel AX.25, regardless of whether -you actually use it or not. First setup your /etc/ax25/axports, -for example:: - - 9k6 dl0tha-9 9600 255 4 9600 baud port (scc3) - axlink dl0tha-15 38400 255 4 Link to NOS - -Now "ifconfig" the scc device:: - - ifconfig scc3 44.128.1.1 hw ax25 dl0tha-9 - -You can now axattach a pseudo-TTY:: - - axattach /dev/ptys0 axlink - -and start your NOS and attach /dev/ptys0 there. The problem is that -NOS is reachable only via digipeating through the kernel AX.25 -(disastrous on a DAMA controlled channel). To solve this problem, -configure "rxecho" to echo the incoming frames from "9k6" to "axlink" -and outgoing frames from "axlink" to "9k6" and start:: - - rxecho - -Or simply use "kissbridge" coming with z8530drv-utils:: - - ifconfig scc3 hw ax25 dl0tha-9 - kissbridge scc3 /dev/ptys0 - - -3. Adjustment and Display of parameters -======================================= - -3.1 Displaying SCC Parameters: -============================== - -Once a SCC channel has been attached, the parameter settings and -some statistic information can be shown using the param program:: - - dl1bke-u:~$ sccstat scc0 - - Parameters: - - speed : 1200 baud - txdelay : 36 - persist : 255 - slottime : 0 - txtail : 8 - fulldup : 1 - waittime : 12 - mintime : 3 sec - maxkeyup : 7 sec - idletime : 3 sec - maxdefer : 120 sec - group : 0x00 - txoff : off - softdcd : on - SLIP : off - - Status: - - HDLC Z8530 Interrupts Buffers - ----------------------------------------------------------------------- - Sent : 273 RxOver : 0 RxInts : 125074 Size : 384 - Received : 1095 TxUnder: 0 TxInts : 4684 NoSpace : 0 - RxErrors : 1591 ExInts : 11776 - TxErrors : 0 SpInts : 1503 - Tx State : idle - - -The status info shown is: - -============== ============================================================== -Sent number of frames transmitted -Received number of frames received -RxErrors number of receive errors (CRC, ABORT) -TxErrors number of discarded Tx frames (due to various reasons) -Tx State status of the Tx interrupt handler: idle/busy/active/tail (2) -RxOver number of receiver overruns -TxUnder number of transmitter underruns -RxInts number of receiver interrupts -TxInts number of transmitter interrupts -EpInts number of receiver special condition interrupts -SpInts number of external/status interrupts -Size maximum size of an AX.25 frame (*with* AX.25 headers!) -NoSpace number of times a buffer could not get allocated -============== ============================================================== - -An overrun is abnormal. If lots of these occur, the product of -baudrate and number of interfaces is too high for the processing -power of your computer. NoSpace errors are unlikely to be caused by the -driver or the kernel AX.25. - - -3.2 Setting Parameters -====================== - - -The setting of parameters of the emulated KISS TNC is done in the -same way in the SCC driver. You can change parameters by using -the kissparms program from the ax25-utils package or use the program -"sccparam":: - - sccparam - -You can change the following parameters: - -=========== ===== -param value -=========== ===== -speed 1200 -txdelay 36 -persist 255 -slottime 0 -txtail 8 -fulldup 1 -waittime 12 -mintime 3 -maxkeyup 7 -idletime 3 -maxdefer 120 -group 0x00 -txoff off -softdcd on -SLIP off -=========== ===== - - -The parameters have the following meaning: - -speed: - The baudrate on this channel in bits/sec - - Example: sccparam /dev/scc3 speed 9600 - -txdelay: - The delay (in units of 10 ms) after keying of the - transmitter, until the first byte is sent. This is usually - called "TXDELAY" in a TNC. When 0 is specified, the driver - will just wait until the CTS signal is asserted. This - assumes the presence of a timer or other circuitry in the - MODEM and/or transmitter, that asserts CTS when the - transmitter is ready for data. - A normal value of this parameter is 30-36. - - Example: sccparam /dev/scc0 txd 20 - -persist: - This is the probability that the transmitter will be keyed - when the channel is found to be free. It is a value from 0 - to 255, and the probability is (value+1)/256. The value - should be somewhere near 50-60, and should be lowered when - the channel is used more heavily. - - Example: sccparam /dev/scc2 persist 20 - -slottime: - This is the time between samples of the channel. It is - expressed in units of 10 ms. About 200-300 ms (value 20-30) - seems to be a good value. - - Example: sccparam /dev/scc0 slot 20 - -tail: - The time the transmitter will remain keyed after the last - byte of a packet has been transferred to the SCC. This is - necessary because the CRC and a flag still have to leave the - SCC before the transmitter is keyed down. The value depends - on the baudrate selected. A few character times should be - sufficient, e.g. 40ms at 1200 baud. (value 4) - The value of this parameter is in 10 ms units. - - Example: sccparam /dev/scc2 4 - -full: - The full-duplex mode switch. This can be one of the following - values: - - 0: The interface will operate in CSMA mode (the normal - half-duplex packet radio operation) - 1: Fullduplex mode, i.e. the transmitter will be keyed at - any time, without checking the received carrier. It - will be unkeyed when there are no packets to be sent. - 2: Like 1, but the transmitter will remain keyed, also - when there are no packets to be sent. Flags will be - sent in that case, until a timeout (parameter 10) - occurs. - - Example: sccparam /dev/scc0 fulldup off - -wait: - The initial waittime before any transmit attempt, after the - frame has been queue for transmit. This is the length of - the first slot in CSMA mode. In full duplex modes it is - set to 0 for maximum performance. - The value of this parameter is in 10 ms units. - - Example: sccparam /dev/scc1 wait 4 - -maxkey: - The maximal time the transmitter will be keyed to send - packets, in seconds. This can be useful on busy CSMA - channels, to avoid "getting a bad reputation" when you are - generating a lot of traffic. After the specified time has - elapsed, no new frame will be started. Instead, the trans- - mitter will be switched off for a specified time (parameter - min), and then the selected algorithm for keyup will be - started again. - The value 0 as well as "off" will disable this feature, - and allow infinite transmission time. - - Example: sccparam /dev/scc0 maxk 20 - -min: - This is the time the transmitter will be switched off when - the maximum transmission time is exceeded. - - Example: sccparam /dev/scc3 min 10 - -idle: - This parameter specifies the maximum idle time in full duplex - 2 mode, in seconds. When no frames have been sent for this - time, the transmitter will be keyed down. A value of 0 is - has same result as the fullduplex mode 1. This parameter - can be disabled. - - Example: sccparam /dev/scc2 idle off # transmit forever - -maxdefer - This is the maximum time (in seconds) to wait for a free channel - to send. When this timer expires the transmitter will be keyed - IMMEDIATELY. If you love to get trouble with other users you - should set this to a very low value ;-) - - Example: sccparam /dev/scc0 maxdefer 240 # 2 minutes - - -txoff: - When this parameter has the value 0, the transmission of packets - is enable. Otherwise it is disabled. - - Example: sccparam /dev/scc2 txoff on - -group: - It is possible to build special radio equipment to use more than - one frequency on the same band, e.g. using several receivers and - only one transmitter that can be switched between frequencies. - Also, you can connect several radios that are active on the same - band. In these cases, it is not possible, or not a good idea, to - transmit on more than one frequency. The SCC driver provides a - method to lock transmitters on different interfaces, using the - "param group " command. This will only work when - you are using CSMA mode (parameter full = 0). - - The number must be 0 if you want no group restrictions, and - can be computed as follows to create restricted groups: - is the sum of some OCTAL numbers: - - - === ======================================================= - 200 This transmitter will only be keyed when all other - transmitters in the group are off. - 100 This transmitter will only be keyed when the carrier - detect of all other interfaces in the group is off. - 0xx A byte that can be used to define different groups. - Interfaces are in the same group, when the logical AND - between their xx values is nonzero. - === ======================================================= - - Examples: - - When 2 interfaces use group 201, their transmitters will never be - keyed at the same time. - - When 2 interfaces use group 101, the transmitters will only key - when both channels are clear at the same time. When group 301, - the transmitters will not be keyed at the same time. - - Don't forget to convert the octal numbers into decimal before - you set the parameter. - - Example: (to be written) - -softdcd: - use a software dcd instead of the real one... Useful for a very - slow squelch. - - Example: sccparam /dev/scc0 soft on - - -4. Problems -=========== - -If you have tx-problems with your BayCom USCC card please check -the manufacturer of the 8530. SGS chips have a slightly -different timing. Try Zilog... A solution is to write to register 8 -instead to the data port, but this won't work with the ESCC chips. -*SIGH!* - -A very common problem is that the PTT locks until the maxkeyup timer -expires, although interrupts and clock source are correct. In most -cases compiling the driver with CONFIG_SCC_DELAY (set with -make config) solves the problems. For more hints read the (pseudo) FAQ -and the documentation coming with z8530drv-utils. - -I got reports that the driver has problems on some 386-based systems. -(i.e. Amstrad) Those systems have a bogus AT bus timing which will -lead to delayed answers on interrupts. You can recognize these -problems by looking at the output of Sccstat for the suspected -port. If it shows under- and overruns you own such a system. - -Delayed processing of received data: This depends on - -- the kernel version - -- kernel profiling compiled or not - -- a high interrupt load - -- a high load of the machine --- running X, Xmorph, XV and Povray, - while compiling the kernel... hmm ... even with 32 MB RAM ... ;-) - Or running a named for the whole .ampr.org domain on an 8 MB - box... - -- using information from rxecho or kissbridge. - -Kernel panics: please read /linux/README and find out if it -really occurred within the scc driver. - -If you cannot solve a problem, send me - -- a description of the problem, -- information on your hardware (computer system, scc board, modem) -- your kernel version -- the output of cat /proc/net/z8530 - -4. Thor RLC100 -============== - -Mysteriously this board seems not to work with the driver. Anyone -got it up-and-running? - - -Many thanks to Linus Torvalds and Alan Cox for including the driver -in the Linux standard distribution and their support. - -:: - - Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ABH.#BAY.DEU.EU - Internet: jreuter@yaina.de - WWW : http://yaina.de/jreuter diff --git a/Documentation/networking/device_drivers/index.rst b/Documentation/networking/device_drivers/index.rst index 1df51c9f7827..1f54f01d24be 100644 --- a/Documentation/networking/device_drivers/index.rst +++ b/Documentation/networking/device_drivers/index.rst @@ -13,6 +13,5 @@ Contents: cellular/index ethernet/index fddi/index - hamradio/index wifi/index wwan/index diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst index c2406bd8ae0b..44a422ad3b05 100644 --- a/Documentation/networking/index.rst +++ b/Documentation/networking/index.rst @@ -17,7 +17,6 @@ Contents: diagnostic/index dsa/index devlink/index - caif/index ethtool-netlink ieee802154 iso15765-2 @@ -41,11 +40,9 @@ Contents: tls-handshake nfc 6lowpan - 6pack arcnet-hardware arcnet atm - ax25 bonding cdc_mbim dctcp diff --git a/Documentation/networking/netdevices.rst b/Documentation/networking/netdevices.rst index 83e28b96884f..93e06e8d51a9 100644 --- a/Documentation/networking/netdevices.rst +++ b/Documentation/networking/netdevices.rst @@ -289,6 +289,19 @@ ndo_tx_timeout: ndo_set_rx_mode: Synchronization: netif_addr_lock spinlock. Context: BHs disabled + Notes: Deprecated in favor of ndo_set_rx_mode_async which runs + in process context. + +ndo_set_rx_mode_async: + Synchronization: rtnl_lock() semaphore. In addition, netdev instance + lock if the driver implements queue management or shaper API. + Context: process (from a work queue) + Notes: Async version of ndo_set_rx_mode which runs in process + context. Receives snapshots of the unicast and multicast address lists. + +ndo_change_rx_flags: + Synchronization: rtnl_lock() semaphore. In addition, netdev instance + lock if the driver implements queue management or shaper API. ndo_setup_tc: ``TC_SETUP_BLOCK`` and ``TC_SETUP_FT`` are running under NFT locks diff --git a/Documentation/process/index.rst b/Documentation/process/index.rst index dbd6ea16aca7..aa7c959a52b8 100644 --- a/Documentation/process/index.rst +++ b/Documentation/process/index.rst @@ -86,6 +86,7 @@ regressions and security problems. debugging/index handling-regressions security-bugs + threat-model cve embargoed-hardware-issues diff --git a/Documentation/process/license-rules.rst b/Documentation/process/license-rules.rst index 59a7832df7d0..b0176bb8a465 100644 --- a/Documentation/process/license-rules.rst +++ b/Documentation/process/license-rules.rst @@ -63,8 +63,11 @@ License identifier syntax The SPDX license identifier in kernel files shall be added at the first possible line in a file which can contain a comment. For the majority of files this is the first line, except for scripts which require the - '#!PATH_TO_INTERPRETER' in the first line. For those scripts the SPDX - identifier goes into the second line. + '#!PATH_TO_INTERPRETER' in the first line. For those scripts, the SPDX + license identifier goes into the second line. + + The license identifier line can then be followed by one or multiple + SPDX-FileCopyrightText lines if desired. | diff --git a/Documentation/process/maintainer-netdev.rst b/Documentation/process/maintainer-netdev.rst index bda93b459a05..ec7b9aa2877f 100644 --- a/Documentation/process/maintainer-netdev.rst +++ b/Documentation/process/maintainer-netdev.rst @@ -528,7 +528,7 @@ The exact rules a driver must follow to acquire the ``Supported`` status: status will be withdrawn. 5. Test failures due to bugs either in the driver or the test itself, - or lack of support for the feature the test is targgeting are + or lack of support for the feature the test is targeting are *not* a basis for losing the ``Supported`` status. netdev CI will maintain an official page of supported devices, listing their diff --git a/Documentation/process/maintainer-soc.rst b/Documentation/process/maintainer-soc.rst index 7d6bad989ad8..a3a90a7d4c68 100644 --- a/Documentation/process/maintainer-soc.rst +++ b/Documentation/process/maintainer-soc.rst @@ -169,8 +169,6 @@ more information on the validation of devicetrees. For new platforms, or additions to existing ones, ``make dtbs_check`` should not add any new warnings. For RISC-V and Samsung SoC, ``make dtbs_check W=1`` is required to not add any new warnings. -If in any doubt about a devicetree change, reach out to the devicetree -maintainers. Branches and Pull Requests ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -209,3 +207,13 @@ The subject line of a pull request should begin with "[GIT PULL]" and made using a signed tag, rather than a branch. This tag should contain a short description summarising the changes in the pull request. For more detail on sending pull requests, please see Documentation/maintainer/pull-requests.rst. + +Defconfigs purpose +~~~~~~~~~~~~~~~~~~ + +Defconfigs are primarily used by the kernel developers, because distros have +their own configs. A change adding new CONFIG options to a defconfig should +explain why the kernel developers in general would want such option, e.g. by +providing a name of an upstream-supported machine/board using that new option. +This implies that enabling options in defconfig for non-upstream machines shall +not be accepted. diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst index 27b028e85861..3c51ddde31dd 100644 --- a/Documentation/process/security-bugs.rst +++ b/Documentation/process/security-bugs.rst @@ -66,6 +66,42 @@ In addition, the following information are highly desirable: the issue appear. It is useful to share them, as they can be helpful to keep end users protected during the time it takes them to apply the fix. +What qualifies as a security bug +-------------------------------- + +It is important that most bugs are handled publicly so as to involve the widest +possible audience and find the best solution. By nature, bugs that are handled +in closed discussions between a small set of participants are less likely to +produce the best possible fix (e.g., risk of missing valid use cases, limited +testing abilities). + +It turns out that the majority of the bugs reported via the security team are +just regular bugs that have been improperly qualified as security bugs due to +a lack of awareness of the Linux kernel's threat model, as described in +Documentation/process/threat-model.rst, and ought to have been sent through +the normal channels described in Documentation/admin-guide/reporting-issues.rst +instead. + +The security list exists for urgent bugs that grant an attacker a capability +they are not supposed to have on a correctly configured production system, and +can be easily exploited, representing an imminent threat to many users. Before +reporting, consider whether the issue actually crosses a trust boundary on such +a system. + +**If you resorted to AI assistance to identify a bug, you must treat it as +public**. While you may have valid reasons to believe it is not, the security +team's experience shows that bugs discovered this way systematically surface +simultaneously across multiple researchers, often on the same day. In this +case, do not publicly share a reproducer, as this could cause unintended harm; +just mention that one is available and maintainers might ask for it privately +if they need it. + +If you are unsure whether an issue qualifies, err on the side of reporting +privately: the security team would rather triage a borderline report than miss +a real vulnerability. Reporting ordinary bugs to the security list, however, +does not make them move faster and consumes triage capacity that other reports +need. + Identifying contacts -------------------- @@ -74,7 +110,7 @@ affected subsystem's maintainers and Cc: the Linux kernel security team. Do not send it to a public list at this stage, unless you have good reasons to consider the issue as being public or trivial to discover (e.g. result of a widely available automated vulnerability scanning tool that can be repeated by -anyone). +anyone, or use of AI-based tools). If you're sending a report for issues affecting multiple parts in the kernel, even if they're fairly similar issues, please send individual messages (think @@ -131,6 +167,64 @@ the Linux kernel security team only. Your message will be triaged, and you will receive instructions about whom to contact, if needed. Your message may equally be forwarded as-is to the relevant maintainers. +Responsible use of AI to find bugs +---------------------------------- + +A significant fraction of bug reports submitted to the security team are +actually the result of code reviews assisted by AI tools. While this can be an +efficient means to find bugs in rarely explored areas, it causes an overload on +maintainers, who are sometimes forced to ignore such reports due to their poor +quality or accuracy. As such, reporters must be particularly cautious about a +number of points which tend to make these reports needlessly difficult to +handle: + + * **Length**: AI-generated reports tend to be excessively long, containing + multiple sections and excessive detail. This makes it difficult to spot + important information such as affected files, versions, and impact. Please + ensure that a clear summary of the problem and all critical details are + presented first. Do not require triage engineers to scan multiple pages of + text. Configure your tools to produce concise, human-style reports. + + * **Formatting**: Most AI-generated reports are littered with Markdown tags. + These decorations complicate the search for important information and do + not survive the quoting processes involved in forwarding or replying. + Please **always convert your report to plain text** without any formatting + decorations before sending it. + + * **Impact Evaluation**: Many AI-generated reports lack an understanding + of the kernel's threat model (see Documentation/process/threat-model.rst) + and go to great lengths inventing theoretical consequences. This adds + noise and complicates triage. Please stick to verifiable facts (e.g., + "this bug permits any user to gain CAP_NET_ADMIN") without enumerating + speculative implications. Have your tool read this documentation as + part of the evaluation process. + + * **Reproducer**: AI-based tools are often capable of generating reproducers. + Please always ensure your tool provides one and **test it thoroughly**. If + the reproducer does not work, or if the tool cannot produce one, the + validity of the report should be seriously questioned. Note that since the + report will be posted to a public list, the reproducer should only be + shared upon maintainers' request. + + * **Propose a Fix**: Many AI tools are actually better at writing code than + evaluating it. Please ask your tool to propose a fix and **test it** before + reporting the problem. If the fix cannot be tested because it relies on + rare hardware or almost extinct network protocols, the issue is likely not + a security bug. In any case, if a fix is proposed, it must adhere to + Documentation/process/submitting-patches.rst and include a 'Fixes:' tag + designating the commit that introduced the bug. + +Failure to consider these points exposes your report to the risk of being +ignored. + +Use common sense when evaluating the report. If the affected file has not been +touched for more than one year and is maintained by a single individual, it is +likely that usage has declined and exposed users are virtually non-existent +(e.g., drivers for very old hardware, obsolete filesystems). In such cases, +there is no need to consume a maintainer's time with an unimportant report. If +the issue is clearly trivial and publicly discoverable, you should report it +directly to the public mailing lists. + Sending the report ------------------ @@ -148,7 +242,15 @@ run additional tests. Reports where the reporter does not respond promptly or cannot effectively discuss their findings may be abandoned if the communication does not quickly improve. -The report must be sent to maintainers, with the security team in ``Cc:``. +The report must be sent to maintainers. If there are two or fewer +recipients in your message, you must also always Cc: the Linux kernel +security team who will ensure the message is delivered to the proper +people, and will be able to assist small maintainer teams with processes +they may not be familiar with. For larger teams, Cc: the Linux kernel +security team for your first few reports or when seeking specific help, +such as when resending a message which got no response within a week. +Once you have become comfortable with the process for a few reports, it is +no longer necessary to Cc: the security list when sending to large teams. The Linux kernel security team can be contacted by email at . This is a private list of security officers who will help verify the bug report and assist developers working on a fix. diff --git a/Documentation/process/threat-model.rst b/Documentation/process/threat-model.rst new file mode 100644 index 000000000000..f177b8d3c1ca --- /dev/null +++ b/Documentation/process/threat-model.rst @@ -0,0 +1,235 @@ +The Linux Kernel threat model +============================= + +There are a lot of assumptions regarding what the kernel does and does not +protect against. These assumptions tend to cause confusion for bug reports +(:doc:`security-related ones ` vs :doc:`non-security ones +<../admin-guide/reporting-issues>`), and can complicate security enforcement +when the responsibilities for some boundaries is not clear between the kernel, +distros, administrators and users. + +This document tries to clarify the responsibilities of the kernel in this +domain. + +The kernel's responsibilities +----------------------------- + +The kernel abstracts access to local hardware resources and to remote systems +in a way that allows multiple local users to get a fair share of the available +resources granted to them, and, when the underlying hardware permits, to assign +a level of confidentiality to their communications and to the data they are +processing or storing. + +The kernel assumes that the underlying hardware behaves according to its +specifications. This includes the integrity of the CPU's instruction set, the +transparency of the branch prediction unit and the cache units, the consistency +of the Memory Management Unit (MMU), the isolation of DMA-capable peripherals +(e.g., via IOMMU), state transitions in controllers, ranges of values read from +registers, the respect of documented hardware limitations, etc. + +When hardware fails to maintain its specified isolation (e.g., CPU bugs, +side-channels, hardware response to unexpected inputs), the kernel will usually +attempt to implement reasonable mitigations. These are best-effort measures +intended to reduce the attack surface or elevate the cost of an attack within +the limits of the hardware's facilities; they do not constitute a +kernel-provided safety guarantee. + +Users always perform their activities under the authority of an administrator +who is able to grant or deny various types of permissions that may affect how +users benefit from available resources, or the level of confidentiality of +their activities. Administrators may also delegate all or part of their own +permissions to some users, particularly via capabilities but not only. All this +is performed via configuration (sysctl, file-system permissions etc). + +The Linux Kernel applies a certain collection of default settings that match +its threat model. Distros have their own threat model and will come with their +own configuration presets, that the administrator may have to adjust to better +suit their expectations (relax or restrict). + +By default, the Linux Kernel guarantees the following protections when running +on common processors featuring privilege levels and memory management units: + +* **User-based isolation**: an unprivileged user may restrict access to their + own data from other unprivileged users running on the same system. This + includes: + + * stored data, via file system permissions + * in-memory data (pages are not accessible by default to other users) + * process activity (ptrace is not permitted to other users) + * inter-process communication (other users may not observe data exchanged via + UNIX domain sockets or other IPC mechanisms). + * network communications within the same or with other systems + +* **Capability-based protection**: + + * users not having elevated capabilities (including but not limited to + CAP_SYS_ADMIN) may not alter the + kernel's configuration, memory nor state, change other users' view of the + file system layout, grant any user capabilities they do not have, nor + affect the system's availability (shutdown, reboot, panic, hang, or making + the system unresponsive via unbounded resource exhaustion). + * users not having the ``CAP_NET_ADMIN`` capability may not alter the network + configuration, intercept nor spoof network communications from other users + nor systems. + * users not having ``CAP_SYS_PTRACE`` may not observe other users' processes + activities. + +When ``CONFIG_USER_NS`` is set, the kernel also permits unprivileged users to +create their own user namespace in which they have all capabilities, but with a +number of restrictions (they may not perform actions that have impacts on the +initial user namespace, such as changing time, loading modules or mounting +block devices). Please refer to ``user_namespaces(7)`` for more details, the +possibilities of user namespaces are not covered in this document. + +The kernel also offers a lot of troubleshooting and debugging facilities, which +can constitute attack vectors when placed in wrong hands. While some of them +are designed to be accessible to regular local users with a low risk (e.g. +kernel logs via ``/proc/kmsg``), some would expose enough information to +represent a risk in most places and the decision to expose them is under the +administrator's responsibility (perf events, traces), and others are not +designed to be accessed by non-privileged users (e.g. debugfs). Access to these +facilities by a user who has been explicitly granted permission by an +administrator does not constitute a security breach. + +Bugs that permit to violate the principles above constitute security breaches. +However, bugs that permit one violation only once another one was already +achieved are only weaknesses. The kernel applies a number of self-protection +measures whose purpose is to avoid crossing a security boundary when certain +classes of bugs are found, but a failure of these extra protections do not +constitute a vulnerability alone. + +What does not constitute a security bug +--------------------------------------- + +In the Linux kernel's threat model, the following classes of problems are +**NOT** considered as Linux Kernel security bugs. However, when it is believed +that the kernel could do better, they should be reported, so that they can be +reviewed and fixed where reasonably possible, but they will be handled as any +regular bug: + +* **Configuration**: + + * outdated kernels and particularly end-of-life branches are out of the scope + of the kernel's threat model: administrators are responsible for keeping + their system up to date. For a bug to qualify as a security bug, it must be + demonstrated that it affects actively maintained versions. + + * build-level: changes to the kernel configuration that are explicitly + documented as lowering the security level (e.g. ``CONFIG_NOMMU``), or + targeted at developers only. + + * OS-level: changes to command line parameters, sysctls, filesystem + permissions, user capabilities, exposure of privileged interfaces, that + explicitly increase exposure by either offering non-default access to + unprivileged users, or reduce the kernel's ability to enforce some + protections or mitigations. Example: write access to procfs or debugfs. + + * issues triggered only when using features intended for development or + debugging (e.g., LOCKDEP, KASAN, FAULT_INJECTION): these features are known + to introduce overhead and potential instability and are not intended for + production use. + + * issues affecting drivers exposed under CONFIG_STAGING, as well as features + marked EXPERIMENTAL in the configuration. + + * loading of explicitly insecure/broken/staging modules, and generally any + using any subsystem marked as experimental or not intended for production + use. + + * running out-of-tree modules or unofficial kernel forks; these should be + reported to the relevant vendor. + +* **Excess of initial privileges**: + + * actions performed by a user already possessing the privileges required to + perform that action or modify that state (e.g. ``CAP_SYS_ADMIN``, + ``CAP_NET_ADMIN``, ``CAP_SYS_RAWIO``, ``CAP_SYS_MODULE`` with no further + boundary being crossed). + + * actions performed in user namespace that do not bypass the restrictions + imposed to the initial user (e.g. ptrace usage, signal delivery, resource + usage, access to FS/device/sysctl/memory, network binding, system/network + configuration etc). + + * anything performed by the root user in the initial namespace (e.g. kernel + oops when writing to a privileged device). + +* **Out of production use**: + + This covers theoretical/probabilistic attacks that rely on laboratory + conditions with zero system noise, or those requiring an unrealistic number + of attempts (e.g., billions of trials) that would be detected by standard + system monitoring long before success, such as: + + * prediction of random numbers that only works in a totally silent + environment (such as IP ID, TCP ports or sequence numbers that can only be + guessed in a lab). + + * activity observation and information leaks based on probabilistic + approaches that are prone to measurement noise and not realistically + reproducible on a production system. + + * issues that can only be triggered by heavy attacks (e.g. brute force) whose + impact on the system makes it unlikely or impossible to remain undetected + before they succeed (e.g. consuming all memory before succeeding). + + * problems seen only under development simulators, emulators, or combinations + that do not exist on real systems at the time of reporting (issues + involving tens of millions of threads, tens of thousands of CPUs, + unrealistic CPU frequencies, RAM sizes or disk capacities, network speeds. + + * issues whose reproduction requires hardware modification or emulation, + including fake USB devices that pretend to be another one. + + * as well as issues that can be triggered at a cost that is orders of + magnitude higher than the expected benefits (e.g. fully functional keyboard + emulator only to retrieve 7 uninitialized bytes in a structure, or + brute-force method involving millions of connection attempts to guess a + port number). + +* **Hardening failures**: + + * ability to bypass some of the kernel's hardening measures with no + demonstrable exploit path (e.g. ASLR bypass, events timing or probing with + no demonstrable consequence). These are just weaknesses, not + vulnerabilities. + + * missing argument checks and failure to report certain errors with no + immediate consequence. + +* **Random information leaks**: + + This concerns information leaks of small data parts that happen to be there + and that cannot be chosen by the attacker, or face access restrictions: + + * structure padding reported by syscalls or other interfaces. + + * identifiers, partial data, non-terminated strings reported in error + messages. + + * Leaks of kernel memory addresses/pointers do not constitute an immediately + exploitable vector and are not security bugs, though they must be reported + and fixed. + +* **Crafted file system images**: + + * bugs triggered by mounting a corrupted or maliciously crafted file system + image are generally not security bugs, as the kernel assumes the underlying + storage media is under the administrator's control, unless the filesystem + driver is specifically documented as being hardened against untrusted media. + + * issues that are resolved, mitigated, or detected by running a filesystem + consistency check (fsck) on the image prior to mounting. + +* **Physical access**: + + Issues that require physical access to the machine, hardware modification, or + the use of specialized hardware (e.g., logic analyzers, DMA-attack tools over + PCI-E/Thunderbolt) are out of scope unless the system is explicitly + configured with technologies meant to defend against such attacks + (e.g. IOMMU). + +* **Functional and performance regressions**: + + Any issue that can be mitigated by setting proper permissions and limits + doesn't qualify as a security bug. diff --git a/Documentation/sound/codecs/cs35l56.rst b/Documentation/sound/codecs/cs35l56.rst index d5363b08f515..b3f8c1c23851 100644 --- a/Documentation/sound/codecs/cs35l56.rst +++ b/Documentation/sound/codecs/cs35l56.rst @@ -40,7 +40,7 @@ There are two drivers in the kernel *For systems using SoundWire*: sound/soc/codecs/cs35l56.c and associated files -*For systems using HDA*: sound/pci/hda/cs35l56_hda.c +*For systems using HDA*: sound/hda/codecs/side-codecs/cs35l56_hda.c Firmware ======== diff --git a/Documentation/staging/magic-number.rst b/Documentation/staging/magic-number.rst index 79afddf0e692..670d3189a976 100644 --- a/Documentation/staging/magic-number.rst +++ b/Documentation/staging/magic-number.rst @@ -72,11 +72,8 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/uapi/l APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip/slip.h`` -BAYCOM_MAGIC 19730510 baycom_state ``drivers/net/hamradio/baycom_epp.c`` -HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` CODA_MAGIC 0xC0DAC0DA coda_file_info ``fs/coda/coda_fs_i.h`` -YAM_MAGIC 0xF10A7654 yam_port ``drivers/net/hamradio/yam.c`` CCB_MAGIC 0xf2691ad2 ccb ``drivers/scsi/ncr53c8xx.c`` QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry ``drivers/scsi/arm/queue.c`` QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry ``drivers/scsi/arm/queue.c`` diff --git a/Documentation/subsystem-apis.rst b/Documentation/subsystem-apis.rst index ff4fe8c936c8..b1ad48bb4001 100644 --- a/Documentation/subsystem-apis.rst +++ b/Documentation/subsystem-apis.rst @@ -46,7 +46,6 @@ Networking interfaces networking/index netlabel/index infiniband/index - isdn/index mhi/index Storage interfaces diff --git a/Documentation/sunrpc/xdr/nlm4.x b/Documentation/sunrpc/xdr/nlm4.x new file mode 100644 index 000000000000..0c44a80ef674 --- /dev/null +++ b/Documentation/sunrpc/xdr/nlm4.x @@ -0,0 +1,211 @@ +/* + * This file was extracted by hand from + * https://www.rfc-editor.org/rfc/rfc1813.html . + * + * Note that RFC 1813 is Informational. Its official date of + * publication (June 1995) is before the IETF required its RFCs to + * carry an explicit copyright or other IP ownership notices. + * + * Note also that RFC 1813 does not specify the whole NLM4 protocol. + * In particular, the argument and result types are not present in + * that document, and had to be reverse-engineered. + */ + +/* + * The NLMv4 protocol + */ + +pragma header nlm4; + +/* + * The following definitions are missing in RFC 1813, + * but can be found in the OpenNetworking Network Lock + * Manager protocol: + * + * https://pubs.opengroup.org/onlinepubs/9629799/chap10.htm + */ + +const LM_MAXSTRLEN = 1024; + +const LM_MAXNAMELEN = 1025; + +const MAXNETOBJ_SZ = 1024; + +typedef opaque netobj; + +enum fsh4_mode { + fsm_DN = 0, /* deny none */ + fsm_DR = 1, /* deny read */ + fsm_DW = 2, /* deny write */ + fsm_DRW = 3 /* deny read/write */ +}; + +enum fsh4_access { + fsa_NONE = 0, /* for completeness */ + fsa_R = 1, /* read-only */ + fsa_W = 2, /* write-only */ + fsa_RW = 3 /* read/write */ +}; + +/* + * The following definitions come from the OpenNetworking + * Network Status Monitor protocol: + * + * https://pubs.opengroup.org/onlinepubs/9629799/chap11.htm + */ + +const SM_MAXSTRLEN = 1024; + +/* + * The NLM protocol as extracted from: + * https://tools.ietf.org/html/rfc1813 Appendix II + */ + +typedef unsigned hyper uint64; + +typedef hyper int64; + +typedef unsigned long uint32; + +typedef long int32; + +enum nlm4_stats { + NLM4_GRANTED = 0, + NLM4_DENIED = 1, + NLM4_DENIED_NOLOCKS = 2, + NLM4_BLOCKED = 3, + NLM4_DENIED_GRACE_PERIOD = 4, + NLM4_DEADLCK = 5, + NLM4_ROFS = 6, + NLM4_STALE_FH = 7, + NLM4_FBIG = 8, + NLM4_FAILED = 9 +}; + +pragma big_endian nlm4_stats; + +struct nlm4_holder { + bool exclusive; + int32 svid; + netobj oh; + uint64 l_offset; + uint64 l_len; +}; + +union nlm4_testrply switch (nlm4_stats stat) { + case NLM4_DENIED: + nlm4_holder holder; + default: + void; +}; + +struct nlm4_stat { + nlm4_stats stat; +}; + +struct nlm4_res { + netobj cookie; + nlm4_stat stat; +}; + +struct nlm4_testres { + netobj cookie; + nlm4_testrply stat; +}; + +struct nlm4_lock { + string caller_name; + netobj fh; + netobj oh; + int32 svid; + uint64 l_offset; + uint64 l_len; +}; + +struct nlm4_lockargs { + netobj cookie; + bool block; + bool exclusive; + nlm4_lock alock; + bool reclaim; + int32 state; +}; + +struct nlm4_cancargs { + netobj cookie; + bool block; + bool exclusive; + nlm4_lock alock; +}; + +struct nlm4_testargs { + netobj cookie; + bool exclusive; + nlm4_lock alock; +}; + +struct nlm4_unlockargs { + netobj cookie; + nlm4_lock alock; +}; + +struct nlm4_share { + string caller_name; + netobj fh; + netobj oh; + fsh4_mode mode; + fsh4_access access; +}; + +struct nlm4_shareargs { + netobj cookie; + nlm4_share share; + bool reclaim; +}; + +struct nlm4_shareres { + netobj cookie; + nlm4_stats stat; + int32 sequence; +}; + +struct nlm4_notify { + string name; + int32 state; +}; + +/* + * Argument for the Linux-private SM_NOTIFY procedure + */ +const SM_PRIV_SIZE = 16; + +struct nlm4_notifyargs { + nlm4_notify notify; + opaque private[SM_PRIV_SIZE]; +}; + +program NLM4_PROG { + version NLM4_VERS { + void NLMPROC4_NULL(void) = 0; + nlm4_testres NLMPROC4_TEST(nlm4_testargs) = 1; + nlm4_res NLMPROC4_LOCK(nlm4_lockargs) = 2; + nlm4_res NLMPROC4_CANCEL(nlm4_cancargs) = 3; + nlm4_res NLMPROC4_UNLOCK(nlm4_unlockargs) = 4; + nlm4_res NLMPROC4_GRANTED(nlm4_testargs) = 5; + void NLMPROC4_TEST_MSG(nlm4_testargs) = 6; + void NLMPROC4_LOCK_MSG(nlm4_lockargs) = 7; + void NLMPROC4_CANCEL_MSG(nlm4_cancargs) = 8; + void NLMPROC4_UNLOCK_MSG(nlm4_unlockargs) = 9; + void NLMPROC4_GRANTED_MSG(nlm4_testargs) = 10; + void NLMPROC4_TEST_RES(nlm4_testres) = 11; + void NLMPROC4_LOCK_RES(nlm4_res) = 12; + void NLMPROC4_CANCEL_RES(nlm4_res) = 13; + void NLMPROC4_UNLOCK_RES(nlm4_res) = 14; + void NLMPROC4_GRANTED_RES(nlm4_res) = 15; + void NLMPROC4_SM_NOTIFY(nlm4_notifyargs) = 16; + nlm4_shareres NLMPROC4_SHARE(nlm4_shareargs) = 20; + nlm4_shareres NLMPROC4_UNSHARE(nlm4_shareargs) = 21; + nlm4_res NLMPROC4_NM_LOCK(nlm4_lockargs) = 22; + void NLMPROC4_FREE_ALL(nlm4_notify) = 23; + } = 4; +} = 100021; diff --git a/Documentation/translations/it_IT/staging/magic-number.rst b/Documentation/translations/it_IT/staging/magic-number.rst index cd8f23571835..43dd6398300b 100644 --- a/Documentation/translations/it_IT/staging/magic-number.rst +++ b/Documentation/translations/it_IT/staging/magic-number.rst @@ -78,11 +78,8 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` -HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` CODA_MAGIC 0xC0DAC0DA coda_file_info ``fs/coda/coda_fs_i.h`` -YAM_MAGIC 0xF10A7654 yam_port ``drivers/net/hamradio/yam.c`` CCB_MAGIC 0xf2691ad2 ccb ``drivers/scsi/ncr53c8xx.c`` QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry ``drivers/scsi/arm/queue.c`` QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry ``drivers/scsi/arm/queue.c`` diff --git a/Documentation/translations/sp_SP/process/magic-number.rst b/Documentation/translations/sp_SP/process/magic-number.rst index beb4b4c1de11..f5b4c3f2849f 100644 --- a/Documentation/translations/sp_SP/process/magic-number.rst +++ b/Documentation/translations/sp_SP/process/magic-number.rst @@ -77,11 +77,8 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` -HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` CODA_MAGIC 0xC0DAC0DA coda_file_info ``fs/coda/coda_fs_i.h`` -YAM_MAGIC 0xF10A7654 yam_port ``drivers/net/hamradio/yam.c`` CCB_MAGIC 0xf2691ad2 ccb ``drivers/scsi/ncr53c8xx.c`` QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry ``drivers/scsi/arm/queue.c`` QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry ``drivers/scsi/arm/queue.c`` diff --git a/Documentation/translations/zh_CN/devicetree/of_unittest.rst b/Documentation/translations/zh_CN/devicetree/of_unittest.rst index 5c1a8e0cfd16..cfd0b751ef27 100644 --- a/Documentation/translations/zh_CN/devicetree/of_unittest.rst +++ b/Documentation/translations/zh_CN/devicetree/of_unittest.rst @@ -32,27 +32,30 @@ OF Selftest被设计用来测试提供给设备驱动开发者的接口(includ 2. 测试数据 =========== -设备树源文件(drivers/of/unittest-data/testcases.dts)包含执行drivers/of/unittest.c -中自动化单元测试所需的测试数据。目前,以下设备树源包含文件(.dtsi)被包含在testcases.dt中:: +设备树源文件(drivers/of/unittest-data/testcases.dtso)包含执行drivers/of/unittest.c +中自动化单元测试所需的测试数据。目前,以下设备树源包含文件(.dtsi)被包含在testcases.dtso中:: drivers/of/unittest-data/tests-interrupts.dtsi drivers/of/unittest-data/tests-platform.dtsi drivers/of/unittest-data/tests-phandle.dtsi drivers/of/unittest-data/tests-match.dtsi + drivers/of/unittest-data/tests-address.dtsi + drivers/of/unittest-data/tests-overlay.dtsi + drivers/of/unittest-data/tests-lifecycle.dtsi 当内核在启用CONFIG_OF_UNITTEST的情况下被构建时,那么下面的make规则:: - $(obj)/%.dtb: $(src)/%.dts FORCE - $(call if_changed_dep, dtc) + $(obj)/%.dtbo: $(src)/%.dtso $(DTC) FORCE + $(call if_changed_dep,dtc) -用于将DT源文件(testcases.dts)编译成二进制blob(testcases.dtb),也被称为扁平化的DT。 +用于将DT源文件(testcases.dtso)编译成二进制blob(testcases.dtbo),也被称为扁平化的DT。 -之后,使用以下规则将上述二进制blob包装成一个汇编文件(testcases.dtb.S):: +之后,使用以下规则将上述二进制blob包装成一个汇编文件(testcases.dtbo.S):: - $(obj)/%.dtb.S: $(obj)/%.dtb - $(call cmd, dt_S_dtb) + $(obj)/%.dtbo.S: $(obj)/%.dtbo FORCE + $(call if_changed,wrap_S_dtb) -汇编文件被编译成一个对象文件(testcases.dtb.o),并被链接到内核镜像中。 +汇编文件被编译成一个对象文件(testcases.dtbo.o),并被链接到内核镜像中。 2.1. 添加测试数据 diff --git a/Documentation/translations/zh_CN/infiniband/index.rst b/Documentation/translations/zh_CN/infiniband/index.rst index 5634cc48379f..aeeea0b49939 100644 --- a/Documentation/translations/zh_CN/infiniband/index.rst +++ b/Documentation/translations/zh_CN/infiniband/index.rst @@ -24,7 +24,6 @@ infiniband core_locking ipoib - opa_vnic sysfs tag_matching user_mad diff --git a/Documentation/translations/zh_CN/infiniband/opa_vnic.rst b/Documentation/translations/zh_CN/infiniband/opa_vnic.rst deleted file mode 100644 index 12b147fbf792..000000000000 --- a/Documentation/translations/zh_CN/infiniband/opa_vnic.rst +++ /dev/null @@ -1,156 +0,0 @@ -.. include:: ../disclaimer-zh_CN.rst - -:Original: Documentation/infiniband/opa_vnic.rst - -:翻译: - - 司延腾 Yanteng Si - -:校译: - - 王普宇 Puyu Wang - 时奎亮 Alex Shi - -.. _cn_infiniband_opa_vnic: - -============================================= -英特尔全路径(OPA)虚拟网络接口控制器(VNIC) -============================================= - -英特尔全路径(OPA)虚拟网络接口控制器(VNIC)功能通过封装HFI节点之间的以 -太网数据包,支持Omni-Path结构上的以太网功能。 - -体系结构 -======== - -Omni-Path封装的以太网数据包的交换模式涉及Omni-Path结构拓扑上覆盖的一个或 -多个虚拟以太网交换机。Omni-Path结构上的HFI节点的一个子集被允许在特定的虚 -拟以太网交换机上交换封装的以太网数据包。虚拟以太网交换机是通过配置结构上的 -HFI节点实现的逻辑抽象,用于生成和处理报头。在最简单的配置中,整个结构的所有 -HFI节点通过一个虚拟以太网交换机交换封装的以太网数据包。一个虚拟以太网交换机, -实际上是一个独立的以太网网络。该配置由以太网管理器(EM)执行,它是可信的结 -构管理器(FM)应用程序的一部分。HFI节点可以有多个VNIC,每个连接到不同的虚 -拟以太网交换机。下图介绍了两个虚拟以太网交换机与两个HFI节点的情况:: - - +-------------------+ - | 子网/ | - | 以太网 | - | 管理 | - +-------------------+ - / / - / / - / / - / / - +-----------------------------+ +------------------------------+ - | 虚拟以太网切换 | | 虚拟以太网切换 | - | +---------+ +---------+ | | +---------+ +---------+ | - | | VPORT | | VPORT | | | | VPORT | | VPORT | | - +--+---------+----+---------+-+ +-+---------+----+---------+---+ - | \ / | - | \ / | - | \/ | - | / \ | - | / \ | - +-----------+------------+ +-----------+------------+ - | VNIC | VNIC | | VNIC | VNIC | - +-----------+------------+ +-----------+------------+ - | HFI | | HFI | - +------------------------+ +------------------------+ - - -Omni-Path封装的以太网数据包格式如下所述。 - -==================== ================================ -位 域 -==================== ================================ -Quad Word 0: -0-19 SLID (低20位) -20-30 长度 (以四字为单位) -31 BECN 位 -32-51 DLID (低20位) -52-56 SC (服务级别) -57-59 RC (路由控制) -60 FECN 位 -61-62 L2 (=10, 16B 格式) -63 LT (=1, 链路传输头 Flit) - -Quad Word 1: -0-7 L4 type (=0x78 ETHERNET) -8-11 SLID[23:20] -12-15 DLID[23:20] -16-31 PKEY -32-47 熵 -48-63 保留 - -Quad Word 2: -0-15 保留 -16-31 L4 头 -32-63 以太网数据包 - -Quad Words 3 to N-1: -0-63 以太网数据包 (pad拓展) - -Quad Word N (last): -0-23 以太网数据包 (pad拓展) -24-55 ICRC -56-61 尾 -62-63 LT (=01, 链路传输尾 Flit) -==================== ================================ - -以太网数据包在传输端被填充,以确保VNIC OPA数据包是四字对齐的。“尾”字段 -包含填充的字节数。在接收端,“尾”字段被读取,在将数据包向上传递到网络堆 -栈之前,填充物被移除(与ICRC、尾和OPA头一起)。 - -L4头字段包含VNIC端口所属的虚拟以太网交换机ID。在接收端,该字段用于将收 -到的VNIC数据包去多路复用到不同的VNIC端口。 - -驱动设计 -======== - -英特尔OPA VNIC的软件设计如下图所示。OPA VNIC功能有一个依赖于硬件的部分 -和一个独立于硬件的部分。 - -对IB设备分配和释放RDMA netdev设备的支持已经被加入。RDMA netdev支持与 -网络堆栈的对接,从而创建标准的网络接口。OPA_VNIC是一个RDMA netdev设备 -类型。 - -依赖于HW的VNIC功能是HFI1驱动的一部分。它实现了分配和释放OPA_VNIC RDMA -netdev的动作。它涉及VNIC功能的HW资源分配/管理。它与网络堆栈接口并实现所 -需的net_device_ops功能。它在传输路径中期待Omni-Path封装的以太网数据包, -并提供对它们的HW访问。在将数据包向上传递到网络堆栈之前,它把Omni-Path头 -从接收的数据包中剥离。它还实现了RDMA netdev控制操作。 - -OPA VNIC模块实现了独立于硬件的VNIC功能。它由两部分组成。VNIC以太网管理 -代理(VEMA)作为一个IB客户端向IB核心注册,并与IB MAD栈接口。它与以太网 -管理器(EM)和VNIC netdev交换管理信息。VNIC netdev部分分配和释放OPA_VNIC -RDMA netdev设备。它在需要时覆盖由依赖HW的VNIC驱动设置的net_device_ops函数, -以适应任何控制操作。它还处理以太网数据包的封装,在传输路径中使用Omni-Path头。 -对于每个VNIC接口,封装所需的信息是由EM通过VEMA MAD接口配置的。它还通过调用 -RDMA netdev控制操作将任何控制信息传递给依赖于HW的驱动程序:: - - +-------------------+ +----------------------+ - | | | Linux | - | IB MAD | | 网络 | - | | | 栈 | - +-------------------+ +----------------------+ - | | | - | | | - +----------------------------+ | - | | | - | OPA VNIC 模块 | | - | (OPA VNIC RDMA Netdev | | - | & EMA 函数) | | - | | | - +----------------------------+ | - | | - | | - +------------------+ | - | IB 核心 | | - +------------------+ | - | | - | | - +--------------------------------------------+ - | | - | HFI1 驱动和 VNIC 支持 | - | | - +--------------------------------------------+ diff --git a/Documentation/translations/zh_CN/networking/index.rst b/Documentation/translations/zh_CN/networking/index.rst index c276c0993c51..333e9f6cafff 100644 --- a/Documentation/translations/zh_CN/networking/index.rst +++ b/Documentation/translations/zh_CN/networking/index.rst @@ -42,7 +42,6 @@ Todolist: * diagnostic/index * dsa/index * devlink/index -* caif/index * ethtool-netlink * ieee802154 * iso15765-2 diff --git a/Documentation/translations/zh_CN/process/magic-number.rst b/Documentation/translations/zh_CN/process/magic-number.rst index 4ebc84cc0c54..05ee75cf4346 100644 --- a/Documentation/translations/zh_CN/process/magic-number.rst +++ b/Documentation/translations/zh_CN/process/magic-number.rst @@ -70,11 +70,8 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` -HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` CODA_MAGIC 0xC0DAC0DA coda_file_info ``fs/coda/coda_fs_i.h`` -YAM_MAGIC 0xF10A7654 yam_port ``drivers/net/hamradio/yam.c`` CCB_MAGIC 0xf2691ad2 ccb ``drivers/scsi/ncr53c8xx.c`` QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry ``drivers/scsi/arm/queue.c`` QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry ``drivers/scsi/arm/queue.c`` diff --git a/Documentation/translations/zh_TW/process/magic-number.rst b/Documentation/translations/zh_TW/process/magic-number.rst index 5582df6d7ca7..bc7eb025dd1e 100644 --- a/Documentation/translations/zh_TW/process/magic-number.rst +++ b/Documentation/translations/zh_TW/process/magic-number.rst @@ -64,11 +64,8 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/ APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c`` FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h`` SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h`` -BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c`` -HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h`` KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h`` CODA_MAGIC 0xC0DAC0DA coda_file_info ``fs/coda/coda_fs_i.h`` -YAM_MAGIC 0xF10A7654 yam_port ``drivers/net/hamradio/yam.c`` CCB_MAGIC 0xf2691ad2 ccb ``drivers/scsi/ncr53c8xx.c`` QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry ``drivers/scsi/arm/queue.c`` QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry ``drivers/scsi/arm/queue.c`` diff --git a/Documentation/userspace-api/dma-buf-heaps.rst b/Documentation/userspace-api/dma-buf-heaps.rst index 05445c83b79a..f56b743cdb36 100644 --- a/Documentation/userspace-api/dma-buf-heaps.rst +++ b/Documentation/userspace-api/dma-buf-heaps.rst @@ -16,6 +16,13 @@ following heaps: - The ``system`` heap allocates virtually contiguous, cacheable, buffers. + - The ``system_cc_shared`` heap allocates virtually contiguous, cacheable, + buffers using shared (decrypted) memory. It is only present on + confidential computing (CoCo) VMs where memory encryption is active + (e.g., AMD SEV, Intel TDX). The allocated pages have the encryption + bit cleared, making them accessible for device DMA without TDISP + support. On non-CoCo VM configurations, this heap is not registered. + - The ``default_cma_region`` heap allocates physically contiguous, cacheable, buffers. Only present if a CMA region is present. Such a region is usually created either through the kernel commandline diff --git a/Documentation/userspace-api/fwctl/bnxt_fwctl.rst b/Documentation/userspace-api/fwctl/bnxt_fwctl.rst new file mode 100644 index 000000000000..97c9b095cf21 --- /dev/null +++ b/Documentation/userspace-api/fwctl/bnxt_fwctl.rst @@ -0,0 +1,74 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================= +fwctl bnxt driver +================= + +:Author: Pavan Chebbi + +Overview +======== + +BNXT driver makes a fwctl service available through an auxiliary_device. +The bnxt_fwctl driver binds to this device and registers itself with the +fwctl subsystem. + +The bnxt_fwctl driver is agnostic to the device firmware internals. It +uses the Upper Layer Protocol (ULP) conduit provided by bnxt to send +HardWare Resource Manager (HWRM) commands to firmware. + +These commands can query or change firmware driven device configurations +and read/write registers that are useful for debugging. + +bnxt_fwctl User API +=================== + +Each RPC request contains the HWRM input structure in the fwctl_rpc +'in' buffer while 'out' will contain the response. + +A typical user application can send a FWCTL_INFO command using ioctl() +to discover bnxt_fwctl's RPC capabilities as shown below: + + ioctl(fd, FWCTL_INFO, &fwctl_info_msg); + +where fwctl_info_msg (of type struct fwctl_info) describes bnxt_info_msg +(of type struct fwctl_info_bnxt). fwctl_info_msg is set up as follows: + + size = sizeof(struct fwctl_info); + flags = 0; + device_data_len = sizeof(bnxt_info_msg); + out_device_data = (__aligned_u64)&bnxt_info_msg; + +The uctx_caps of bnxt_info_msg represents the capabilities as described +in fwctl_bnxt_commands of include/uapi/fwctl/bnxt.h + +The FW RPC itself, FWCTL_RPC can be sent using ioctl() as: + + ioctl(fd, FWCTL_RPC, &fwctl_rpc_msg); + +where fwctl_rpc_msg (of type struct fwctl_rpc) carries the HWRM command +in its 'in' buffer. The HWRM input structures are described in +include/linux/bnxt/hsi.h. An example for HWRM_VER_GET is shown below: + + struct hwrm_ver_get_output resp; + struct fwctl_rpc fwctl_rpc_msg; + struct hwrm_ver_get_input req; + + req.req_type = HWRM_VER_GET; + req.hwrm_intf_maj = HWRM_VERSION_MAJOR; + req.hwrm_intf_min = HWRM_VERSION_MINOR; + req.hwrm_intf_upd = HWRM_VERSION_UPDATE; + req.cmpl_ring = -1; + req.target_id = -1; + + fwctl_rpc_msg.size = sizeof(struct fwctl_rpc); + fwctl_rpc_msg.scope = FWCTL_RPC_DEBUG_READ_ONLY; + fwctl_rpc_msg.in_len = sizeof(req); + fwctl_rpc_msg.out_len = sizeof(resp); + fwctl_rpc_msg.in = (__aligned_u64)&req; + fwctl_rpc_msg.out = (__aligned_u64)&resp; + +An example python3 program that can exercise this interface can be found in +the following git repository: + +https://github.com/Broadcom/fwctl-tools diff --git a/Documentation/userspace-api/fwctl/fwctl.rst b/Documentation/userspace-api/fwctl/fwctl.rst index a74eab8d14c6..826817bfd54d 100644 --- a/Documentation/userspace-api/fwctl/fwctl.rst +++ b/Documentation/userspace-api/fwctl/fwctl.rst @@ -148,6 +148,7 @@ area resulting in clashes will be resolved in favour of a kernel implementation. fwctl User API ============== +.. kernel-doc:: include/uapi/fwctl/bnxt.h .. kernel-doc:: include/uapi/fwctl/fwctl.h .. kernel-doc:: include/uapi/fwctl/mlx5.h .. kernel-doc:: include/uapi/fwctl/pds.h diff --git a/Documentation/userspace-api/fwctl/index.rst b/Documentation/userspace-api/fwctl/index.rst index 316ac456ad3b..8062f7629654 100644 --- a/Documentation/userspace-api/fwctl/index.rst +++ b/Documentation/userspace-api/fwctl/index.rst @@ -10,5 +10,6 @@ to securely construct and execute RPCs inside device firmware. :maxdepth: 1 fwctl + bnxt_fwctl fwctl-cxl pds_fwctl diff --git a/Documentation/userspace-api/rseq.rst b/Documentation/userspace-api/rseq.rst index 3cd27a3c7c7e..8549a6c61531 100644 --- a/Documentation/userspace-api/rseq.rst +++ b/Documentation/userspace-api/rseq.rst @@ -24,6 +24,97 @@ Quick access to CPU number, node ID Allows to implement per CPU data efficiently. Documentation is in code and selftests. :( +Optimized RSEQ V2 +----------------- + +On architectures which utilize the generic entry code and generic TIF bits +the kernel supports runtime optimizations for RSEQ, which also enable +enhanced features like scheduler time slice extensions. + +To enable them a task has to register the RSEQ region with at least the +length advertised by getauxval(AT_RSEQ_FEATURE_SIZE). + +If existing binaries register with RSEQ_ORIG_SIZE (32 bytes), the kernel +keeps the legacy low performance mode enabled to fulfil the expectations +of existing users regarding the original RSEQ implementation behaviour. + +The following table documents the ABI and behavioral guarantees of the +legacy and the optimized V2 mode. + +.. list-table:: RSEQ modes + :header-rows: 1 + + * - Nr + - What + + - Legacy + - Optimized V2 + + * - 1 + - The cpu_id_start, cpu_id, node_id and mm_cid fields (User mode read + only) + .. Legacy + - Updated by the kernel unconditionally after each context switch and + before signal delivery + .. Optimized V2 + - Updated by the kernel if and only if they change, i.e. if the task + is migrated or mm_cid changes + + * - 2 + - The rseq_cs critical section field + .. Legacy + - Evaluated and handled unconditionally after each context switch and + before signal delivery + .. Optimized V2 + - Evaluated and handled conditionally only when user space was + interrupted and was scheduled out or before delivering a signal in + the interrupted context. + + * - 3 + - Read only fields + .. Legacy + - No strict enforcement except in debug mode + .. Optimized V2 + - Strict enforcement + + * - 4 + - membarrier(...RSEQ) + .. Legacy + - All running threads of the process are interrupted and the ID fields + are rewritten and eventually active critical sections are aborted + before they return to user space. All threads which are scheduled + out whether voluntary or not are covered by #1/#2 above. + .. Optimized V2 + - All running threads of the process are interrupted and eventually + active critical sections are aborted before these threads return to + user space. The ID fields are only updated if changed as a + consequence of the interrupt. All threads which are scheduled out + whether voluntary or not are covered by #1/#2 above. + + * - 5 + - Time slice extensions + .. Legacy + - Not supported + .. Optimized V2 + - Supported + +The legacy mode is obviously less performant as it does unconditional +updates and critical section checks even if not strictly required by the +ABI contract. That can't be changed anymore as some users depend on that +observed behavior, which in turn enables them to violate the ABI and +overwrite the cpu_id_start field for their own purposes. This is obviously +discouraged as it renders RSEQ incompatible with the intended usage and +breaks the expectation of other libraries in the same application. + +The ABI compliant optimized v2 mode, which respects the read only fields, +does not require unconditional updates and therefore is way more +performant. The kernel validates the read only fields for compliance. If +user space modifies them, the process is killed. Compliant usage allows +multiple libraries in the same application to benefit from the RSEQ +functionality without disturbing each other. The ABI compliant optimized v2 +mode also enables extended RSEQ features like time slice extensions. + + Scheduler time slice extensions ------------------------------- @@ -37,7 +128,8 @@ The prerequisites for this functionality are: * Enabled at boot time (default is enabled) - * A rseq userspace pointer has been registered for the thread + * A rseq userspace pointer has been registered for the thread in + optimized V2 mode The thread has to enable the functionality via prctl(2):: diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 032516783e96..52bbbb553ce1 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -907,10 +907,12 @@ The irq_type field has the following values: - KVM_ARM_IRQ_TYPE_CPU: out-of-kernel GIC: irq_id 0 is IRQ, irq_id 1 is FIQ - KVM_ARM_IRQ_TYPE_SPI: - in-kernel GIC: SPI, irq_id between 32 and 1019 (incl.) + in-kernel GICv2/GICv3: SPI, irq_id between 32 and 1019 (incl.) (the vcpu_index field is ignored) + in-kernel GICv5: SPI, irq_id between 0 and 65535 (incl.) - KVM_ARM_IRQ_TYPE_PPI: - in-kernel GIC: PPI, irq_id between 16 and 31 (incl.) + in-kernel GICv2/GICv3: PPI, irq_id between 16 and 31 (incl.) + in-kernel GICv5: PPI, irq_id between 0 and 127 (incl.) (The irq_id field thus corresponds nicely to the IRQ ID in the ARM GIC specs) @@ -9436,6 +9438,14 @@ KVM exits with the register state of either the L1 or L2 guest depending on which executed at the time of an exit. Userspace must take care to differentiate between these cases. +8.47 KVM_CAP_S390_VSIE_ESAMODE +------------------------------ + +:Architectures: s390 + +The presence of this capability indicates that the nested KVM guest can +start in ESA mode. + 9. Known KVM API problems ========================= diff --git a/Documentation/virt/kvm/arm/index.rst b/Documentation/virt/kvm/arm/index.rst index ec09881de4cf..0856b4942e05 100644 --- a/Documentation/virt/kvm/arm/index.rst +++ b/Documentation/virt/kvm/arm/index.rst @@ -10,6 +10,7 @@ ARM fw-pseudo-registers hyp-abi hypercalls + pkvm pvtime ptp_kvm vcpu-features diff --git a/Documentation/virt/kvm/arm/pkvm.rst b/Documentation/virt/kvm/arm/pkvm.rst new file mode 100644 index 000000000000..514992a79a83 --- /dev/null +++ b/Documentation/virt/kvm/arm/pkvm.rst @@ -0,0 +1,106 @@ +.. SPDX-License-Identifier: GPL-2.0 + +==================== +Protected KVM (pKVM) +==================== + +**NOTE**: pKVM is currently an experimental, development feature and +subject to breaking changes as new isolation features are implemented. +Please reach out to the developers at kvmarm@lists.linux.dev if you have +any questions. + +Overview +======== + +Booting a host kernel with '``kvm-arm.mode=protected``' enables +"Protected KVM" (pKVM). During boot, pKVM installs a stage-2 identity +map page-table for the host and uses it to isolate the hypervisor +running at EL2 from the rest of the host running at EL1/0. + +pKVM permits creation of protected virtual machines (pVMs) by passing +the ``KVM_VM_TYPE_ARM_PROTECTED`` machine type identifier to the +``KVM_CREATE_VM`` ioctl(). The hypervisor isolates pVMs from the host by +unmapping pages from the stage-2 identity map as they are accessed by a +pVM. Hypercalls are provided for a pVM to share specific regions of its +IPA space back with the host, allowing for communication with the VMM. +A Linux guest must be configured with ``CONFIG_ARM_PKVM_GUEST=y`` in +order to issue these hypercalls. + +See hypercalls.rst for more details. + +Isolation mechanisms +==================== + +pKVM relies on a number of mechanisms to isolate PVMs from the host: + +CPU memory isolation +-------------------- + +Status: Isolation of anonymous memory and metadata pages. + +Metadata pages (e.g. page-table pages and '``struct kvm_vcpu``' pages) +are donated from the host to the hypervisor during pVM creation and +are consequently unmapped from the stage-2 identity map until the pVM is +destroyed. + +Similarly to regular KVM, pages are lazily mapped into the guest in +response to stage-2 page faults handled by the host. However, when +running a pVM, these pages are first pinned and then unmapped from the +stage-2 identity map as part of the donation procedure. This gives rise +to some user-visible differences when compared to non-protected VMs, +largely due to the lack of MMU notifiers: + +* Memslots cannot be moved or deleted once the pVM has started running. +* Read-only memslots and dirty logging are not supported. +* With the exception of swap, file-backed pages cannot be mapped into a + pVM. +* Donated pages are accounted against ``RLIMIT_MLOCK`` and so the VMM + must have a sufficient resource limit or be granted ``CAP_IPC_LOCK``. + The lack of a runtime reclaim mechanism means that memory locked for + a pVM will remain locked until the pVM is destroyed. +* Changes to the VMM address space (e.g. a ``MAP_FIXED`` mmap() over a + mapping associated with a memslot) are not reflected in the guest and + may lead to loss of coherency. +* Accessing pVM memory that has not been shared back will result in the + delivery of a SIGSEGV. +* If a system call accesses pVM memory that has not been shared back + then it will either return ``-EFAULT`` or forcefully reclaim the + memory pages. Reclaimed memory is zeroed by the hypervisor and a + subsequent attempt to access it in the pVM will return ``-EFAULT`` + from the ``VCPU_RUN`` ioctl(). + +CPU state isolation +------------------- + +Status: **Unimplemented.** + +DMA isolation using an IOMMU +---------------------------- + +Status: **Unimplemented.** + +Proxying of Trustzone services +------------------------------ + +Status: FF-A and PSCI calls from the host are proxied by the pKVM +hypervisor. + +The FF-A proxy ensures that the host cannot share pVM or hypervisor +memory with Trustzone as part of a "confused deputy" attack. + +The PSCI proxy ensures that CPUs always have the stage-2 identity map +installed when they are executing in the host. + +Protected VM firmware (pvmfw) +----------------------------- + +Status: **Unimplemented.** + +Resources +========= + +Quentin Perret's KVM Forum 2022 talk entitled "Protected KVM on arm64: A +technical deep dive" remains a good resource for learning more about +pKVM, despite some of the details having changed in the meantime: + +https://www.youtube.com/watch?v=9npebeVFbFw diff --git a/Documentation/virt/kvm/devices/arm-vgic-v5.rst b/Documentation/virt/kvm/devices/arm-vgic-v5.rst new file mode 100644 index 000000000000..29335ea823fc --- /dev/null +++ b/Documentation/virt/kvm/devices/arm-vgic-v5.rst @@ -0,0 +1,50 @@ +.. SPDX-License-Identifier: GPL-2.0 + +==================================================== +ARM Virtual Generic Interrupt Controller v5 (VGICv5) +==================================================== + + +Device types supported: + - KVM_DEV_TYPE_ARM_VGIC_V5 ARM Generic Interrupt Controller v5.0 + +Only one VGIC instance may be instantiated through this API. The created VGIC +will act as the VM interrupt controller, requiring emulated user-space devices +to inject interrupts to the VGIC instead of directly to CPUs. + +Creating a guest GICv5 device requires a host GICv5 host. The current VGICv5 +device only supports PPI interrupts. These can either be injected from emulated +in-kernel devices (such as the Arch Timer, or PMU), or via the KVM_IRQ_LINE +ioctl. + +Groups: + KVM_DEV_ARM_VGIC_GRP_CTRL + Attributes: + + KVM_DEV_ARM_VGIC_CTRL_INIT + request the initialization of the VGIC, no additional parameter in + kvm_device_attr.addr. Must be called after all VCPUs have been created. + + KVM_DEV_ARM_VGIC_USERPSPACE_PPIs + request the mask of userspace-drivable PPIs. Only a subset of the PPIs can + be directly driven from userspace with GICv5, and the returned mask + informs userspace of which it is allowed to drive via KVM_IRQ_LINE. + + Userspace must allocate and point to __u64[2] of data in + kvm_device_attr.addr. When this call returns, the provided memory will be + populated with the userspace PPI mask. The lower __u64 contains the mask + for the lower 64 PPIS, with the remaining 64 being in the second __u64. + + This is a read-only attribute, and cannot be set. Attempts to set it are + rejected. + + Errors: + + ======= ======================================================== + -ENXIO VGIC not properly configured as required prior to calling + this attribute + -ENODEV no online VCPU + -ENOMEM memory shortage when allocating vgic internal data + -EFAULT Invalid guest ram access + -EBUSY One or more VCPUS are running + ======= ======================================================== diff --git a/Documentation/virt/kvm/devices/index.rst b/Documentation/virt/kvm/devices/index.rst index 192cda7405c8..70845aba38f4 100644 --- a/Documentation/virt/kvm/devices/index.rst +++ b/Documentation/virt/kvm/devices/index.rst @@ -10,6 +10,7 @@ Devices arm-vgic-its arm-vgic arm-vgic-v3 + arm-vgic-v5 mpic s390_flic vcpu diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst index 60bf205cb373..5e3805820010 100644 --- a/Documentation/virt/kvm/devices/vcpu.rst +++ b/Documentation/virt/kvm/devices/vcpu.rst @@ -37,7 +37,8 @@ Returns: A value describing the PMUv3 (Performance Monitor Unit v3) overflow interrupt number for this vcpu. This interrupt could be a PPI or SPI, but the interrupt type must be same for each vcpu. As a PPI, the interrupt number is the same for -all vcpus, while as an SPI it must be a separate number per vcpu. +all vcpus, while as an SPI it must be a separate number per vcpu. For +GICv5-based guests, the architected PPI (23) must be used. 1.2 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_INIT --------------------------------------- @@ -50,7 +51,7 @@ Returns: -EEXIST Interrupt number already used -ENODEV PMUv3 not supported or GIC not initialized -ENXIO PMUv3 not supported, missing VCPU feature or interrupt - number not set + number not set (non-GICv5 guests, only) -EBUSY PMUv3 already initialized ======= ====================================================== diff --git a/Documentation/virt/kvm/x86/amd-memory-encryption.rst b/Documentation/virt/kvm/x86/amd-memory-encryption.rst index b2395dd4769d..bd04a908a8db 100644 --- a/Documentation/virt/kvm/x86/amd-memory-encryption.rst +++ b/Documentation/virt/kvm/x86/amd-memory-encryption.rst @@ -656,8 +656,8 @@ References See [white-paper]_, [api-spec]_, [amd-apm]_, [kvm-forum]_, and [snp-fw-abi]_ for more info. -.. [white-paper] https://developer.amd.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf -.. [api-spec] https://support.amd.com/TechDocs/55766_SEV-KM_API_Specification.pdf -.. [amd-apm] https://support.amd.com/TechDocs/24593.pdf (section 15.34) +.. [white-paper] https://docs.amd.com/v/u/en-US/memory-encryption-white-paper +.. [api-spec] https://docs.amd.com/v/u/en-US/55766_PUB_3.24_SEV_API +.. [amd-apm] https://docs.amd.com/v/u/en-US/24593_3.44_APM_Vol2 (section 15.34) .. [kvm-forum] https://www.linux-kvm.org/images/7/74/02x08A-Thomas_Lendacky-AMDs_Virtualizatoin_Memory_Encryption_Technology.pdf -.. [snp-fw-abi] https://www.amd.com/system/files/TechDocs/56860.pdf +.. [snp-fw-abi] https://www.amd.com/content/dam/amd/en/documents/developer/56860.pdf diff --git a/Documentation/wmi/devices/bitland-mifs-wmi.rst b/Documentation/wmi/devices/bitland-mifs-wmi.rst new file mode 100644 index 000000000000..9e86ecc2993c --- /dev/null +++ b/Documentation/wmi/devices/bitland-mifs-wmi.rst @@ -0,0 +1,207 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +======================================== +Bitland MIFS driver (bitland-mifs-wmi) +======================================== + +Introduction +============ + + +EC WMI interface description +============================ + +The EC WMI interface description can be decoded from the embedded binary MOF (bmof) +data using the `bmfdec `_ utility: + +:: + + class WMIEvent : __ExtrinsicEvent { + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x40A"), Description("Root WMI HID_EVENT20"), guid("{46c93e13-ee9b-4262-8488-563bca757fef}")] + class HID_EVENT20 : WmiEvent { + [key, read] string InstanceName; + [read] boolean Active; + [WmiDataId(1), read, write, Description("Package Data")] uint8 EventDetail[8]; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x40A"), Description("Root WMI HID_EVENT21"), guid("{fa78e245-2c0f-4ca1-91cf-15f34e474850}")] + class HID_EVENT21 : WmiEvent { + [key, read] string InstanceName; + [read] boolean Active; + [WmiDataId(1), read, write, Description("Package Data")] uint8 EventDetail[8]; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x40A"), Description("Root WMI HID_EVENT22"), guid("{1dceaf0a-4d63-44bb-bd0c-0d6281bfddc5}")] + class HID_EVENT22 : WmiEvent { + [key, read] string InstanceName; + [read] boolean Active; + [WmiDataId(1), read, write, Description("Package Data")] uint8 EventDetail[8]; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x40A"), Description("Root WMI HID_EVENT23"), guid("{3f9e3c26-b077-4f86-91f5-37ff64d8c7ed}")] + class HID_EVENT23 : WmiEvent { + [key, read] string InstanceName; + [read] boolean Active; + [WmiDataId(1), read, write, Description("Package Data")] uint8 EventDetail[8]; + }; + + [WMI, Dynamic, provider("WmiProv"), Locale("MS\\0x409"), Description("Class used to operate firmware interface"), guid("{b60bfb48-3e5b-49e4-a0e9-8cffe1b3434b}")] + class MICommonInterface { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiMethodId(1), Implemented, read, write, Description("Method used to support system functions.")] void MiInterface([in, Description("WMI Interface")] uint8 InData[32], [out] uint8 OutData[30], [out] uint16 Reserved); + }; + +Reverse-Engineering the EC WMI interface +======================================== + +The OEM software can be download from `this link `_ + +Nothing is obfuscated, In this case, `ILSpy `_ could be helpful. + +WMI Methods (MICommonInterface) +======================================== + +The ``MICommonInterface`` class (GUID: ``{b60bfb48-3e5b-49e4-a0e9-8cffe1b3434b}``) +is the primary control interface. It uses a 32-byte buffer for both input +(``InData``) and output (``OutData``). + +Method Structure +---------------- + +The data packet follows a standardized format: + ++----------+------------------------------------------------------------------+ +| Byte | Description | ++==========+==================================================================+ +| 1 | Method Type: Get (0xFA / 250) or Set (0xFB / 251) | ++----------+------------------------------------------------------------------+ +| 3 | Command ID (Method Name) | ++----------+------------------------------------------------------------------+ +| 4 - 31 | Arguments (for Set) or Return Data (for Get) | ++----------+------------------------------------------------------------------+ + + +Command IDs +----------- + +The following Command IDs are used in the third byte of the buffer: + ++----------+-----------------------+------------------------------------------+ +| ID | Name | Values / Description | ++==========+=======================+==========================================+ +| 8 | SystemPerMode | 0: Balance, 1: Performance, 2: Quiet, | +| | | 3: Full-speed | ++----------+-----------------------+------------------------------------------+ +| 9 | GPUMode | 0: Hybrid, 1: Discrete, 2: UMA | ++----------+-----------------------+------------------------------------------+ +| 10 | KeyboardType | 0: White, 1: Single RGB, 2: Zone RGB | ++----------+-----------------------+------------------------------------------+ +| 11 | FnLock | 0: Off, 1: On | ++----------+-----------------------+------------------------------------------+ +| 12 | TPLock | 0: Unlock, 1: Lock (Touchpad) | ++----------+-----------------------+------------------------------------------+ +| 13 | CPUGPUSYSFanSpeed | Returns 12 bytes of fan data: | +| | | Bytes 4-5: CPU Fan RPM (Little Endian) | +| | | Bytes 6-7: GPU Fan RPM (Little Endian) | +| | | Bytes 10-11: SYS Fan RPM (Little Endian) | ++----------+-----------------------+------------------------------------------+ +| 16 | RGBKeyboardMode | 0: Off, 1: Auto Cyclic, 2: Fixed, | +| | | 3: Custom | ++----------+-----------------------+------------------------------------------+ +| 17 | RGBKeyboardColor | Bytes 4, 5, 6: Red, Green, Blue values | ++----------+-----------------------+------------------------------------------+ +| 18 | RGBKeyboardBrightness | 0-10: Brightness Levels, 128: Auto | ++----------+-----------------------+------------------------------------------+ +| 19 | SystemAcType | 1: Type-C, 2: Circular Hole (DC) | ++----------+-----------------------+------------------------------------------+ +| 20 | MaxFanSpeedSwitch | Byte 4: Fan Type (0: CPU/GPU, 1: SYS) | +| | | Byte 5: State (0: Off, 1: On) | ++----------+-----------------------+------------------------------------------+ +| 21 | MaxFanSpeed | Sets manual fan speed duty cycle | ++----------+-----------------------+------------------------------------------+ +| 22 | CPUThermometer | Returns CPU Temperature | ++----------+-----------------------+------------------------------------------+ + +WMI Events (HID_EVENT20) +======================== + +The driver listens for events from the ``HID_EVENT20`` class +(GUID: ``{46c93e13-ee9b-4262-8488-563bca757fef}``). These events are triggered +by hotkeys or system state changes (e.g., plugging in AC power). + +Event Structure +--------------- + +The event data is provided in an 8-byte array (``EventDetail``): + ++----------+------------------------------------------------------------------+ +| Byte | Description | ++==========+==================================================================+ +| 0 | Event Type (Always 0x01 for HotKey/Notification) | ++----------+------------------------------------------------------------------+ +| 1 | Event ID (Corresponds to the Command IDs above) | ++----------+------------------------------------------------------------------+ +| 2 | Value (The new state or value of the feature) | ++----------+------------------------------------------------------------------+ + +Common Event IDs: +----------------- + +Note: reserved event ids are not listed there + ++----------+------------------------------------------------------------------+ +| Event Id | Description | ++==========+==================================================================+ +| 4 | AirPlane mode change | ++----------+------------------------------------------------------------------+ +| 5 | Keyboard brightness change | ++----------+------------------------------------------------------------------+ +| 6 | Touchpad state (enabled/disabled) change | ++----------+------------------------------------------------------------------+ +| 7 | FnLock state (enabled/disabled) change | ++----------+------------------------------------------------------------------+ +| 8 | Keyboard mode change | ++----------+------------------------------------------------------------------+ +| 9 | CapsLock state change | ++----------+------------------------------------------------------------------+ +| 13 | NumLock state change | ++----------+------------------------------------------------------------------+ +| 14 | ScrollLock state change | ++----------+------------------------------------------------------------------+ +| 15 | Performance plan change | ++----------+------------------------------------------------------------------+ +| 25 | Display refresh rate change | ++----------+------------------------------------------------------------------+ +| 33 | Super key lock state (enabled/disabled) change | ++----------+------------------------------------------------------------------+ +| 35 | Open control center key | ++----------+------------------------------------------------------------------+ + +Implementation Details +====================== + +Performance Modes +----------------- +Changing the performance mode via Command ID 0x08 (SystemPerMode) affects the +power limits (PL1/PL2) and fan curves managed by the Embedded Controller (EC). +Note that the "Full-speed" and "Performance" mode (1, 3) is typically only +available when the system is connected to a DC power source (not USB-C/PD). + +In the driver implementation, switch to performance/full-speed mode without +DC power connected will throw the EOPNOTSUPP error. + +Graphics Switching +------------------ +The ``GPUMode`` (0x09) allows switching between Hybrid (Muxless) and Discrete +(Muxed) graphics. Changing this value usually requires a system reboot to +take effect in the BIOS/Firmware. + +Fan Control +----------- +The system supports both automatic EC control and manual overrides. Command ID +0x14 (``MaxFanSpeedSwitch``) is used to toggle manual control, while ID 0x15 +sets the actual PWM duty cycle. diff --git a/Documentation/wmi/driver-development-guide.rst b/Documentation/wmi/driver-development-guide.rst index fbc2d9b12fe9..387f508d57ad 100644 --- a/Documentation/wmi/driver-development-guide.rst +++ b/Documentation/wmi/driver-development-guide.rst @@ -71,7 +71,7 @@ to matching WMI devices using a struct wmi_device_id table: .remove = foo_remove, /* optional, devres is preferred */ .shutdown = foo_shutdown, /* optional, called during shutdown */ .notify_new = foo_notify, /* optional, for event handling */ - .no_notify_data = true, /* optional, enables events containing no additional data */ + .min_event_size = X, /* optional, simplifies event payload size verification */ .no_singleton = true, /* required for new WMI drivers */ }; module_wmi_driver(foo_driver); @@ -106,7 +106,8 @@ WMI method drivers WMI drivers can call WMI device methods using wmidev_invoke_method(). For each WMI method invocation the WMI driver needs to provide the instance number and the method ID, as well as -a buffer with the method arguments and optionally a buffer for the results. +a buffer with the method arguments and optionally a buffer for the results. When calling WMI +methods that do not return any values, wmidev_invoke_procedure() should be used instead. The layout of said buffers is device-specific and described by the Binary MOF data associated with a given WMI device. Said Binary MOF data also describes the method ID of a given WMI method @@ -141,8 +142,10 @@ right before and after calling its remove() or shutdown() callback. However WMI driver developers should be aware that multiple WMI events can be received concurrently, so any locking (if necessary) needs to be provided by the WMI driver itself. -In order to be able to receive WMI events containing no additional event data, -the ``no_notify_data`` flag inside struct wmi_driver should be set to ``true``. +The WMI driver can furthermore instruct the WMI driver core to automatically reject WMI events +that contain a undersized event payload by populating the ``min_event_size`` field inside +struct wmi_driver. Setting this field to 0 will thus enable the WMI driver to receive WMI events +without any event payload. Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver. diff --git a/MAINTAINERS b/MAINTAINERS index 06a8c74577aa..c2c6d79275c6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -35,6 +35,7 @@ Descriptions of section entries and preferred order F: drivers/net/ all files in and below drivers/net F: drivers/net/* all files in drivers/net, but not below F: */net/* all files in "any top level directory"/net + F: fs/**/*foo*.c all *foo*.c files in any subdirectory of fs One pattern per line. Multiple F: lines acceptable. X: *Excluded* files and directories that are NOT maintained, same rules as F:. Files exclusions are tested before file matches. @@ -67,6 +68,12 @@ Maintainers List first. When adding to this list, please keep the entries in alphabetical order. +3C509 NETWORK DRIVER +M: "Maciej W. Rozycki" +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/ethernet/3com/3c509.c + 3C59X NETWORK DRIVER M: Steffen Klassert L: netdev@vger.kernel.org @@ -102,12 +109,6 @@ F: Documentation/networking/6lowpan.rst F: include/net/6lowpan.h F: net/6lowpan/ -6PACK NETWORK DRIVER FOR AX.25 -M: Andreas Koensgen -L: linux-hams@vger.kernel.org -S: Maintained -F: drivers/net/hamradio/6pack.c - 802.11 (including CFG80211/NL80211) M: Johannes Berg L: linux-wireless@vger.kernel.org @@ -143,7 +144,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git F: drivers/tty/serial/8250* F: include/linux/serial_8250.h -8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.] +8390 NETWORK DRIVERS [NE2000, 3C503, etc.] L: netdev@vger.kernel.org S: Orphan / Obsolete F: drivers/net/ethernet/8390/ @@ -645,8 +646,11 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml F: drivers/iio/accel/adxl367* -ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER +ADXL371/ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER M: Michael Hennerich +M: Marcelo Schmitt +M: Nuno Sá +M: Antoniu Miclaus S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml @@ -1587,6 +1591,15 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml F: drivers/iio/adc/ad7780.c +ANALOG DEVICES INC AD8366 DRIVER +M: Michael Hennerich +M: Rodrigo Alencar +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/amplifiers/adi,ad8366.yaml +F: drivers/iio/amplifiers/ad8366.c + ANALOG DEVICES INC AD9467 DRIVER M: Michael Hennerich M: Nuno Sa @@ -1695,6 +1708,14 @@ S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/imu/adi,adis16550.yaml +ANALOG DEVICES INC ADL8113 DRIVER +M: Antoniu Miclaus +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/amplifiers/adi,adl8113.yaml +F: drivers/iio/amplifiers/adl8113.c + ANALOG DEVICES INC ADM1177 DRIVER M: Michael Hennerich L: linux-hwmon@vger.kernel.org @@ -2000,7 +2021,7 @@ F: Documentation/hwmon/aquacomputer_d5next.rst F: drivers/hwmon/aquacomputer_d5next.c AQUANTIA ETHERNET DRIVER (atlantic) -M: Igor Russkikh +M: Sukhdeep Singh L: netdev@vger.kernel.org S: Maintained W: https://www.marvell.com/ @@ -2009,7 +2030,7 @@ F: Documentation/networking/device_drivers/ethernet/aquantia/atlantic.rst F: drivers/net/ethernet/aquantia/atlantic/ AQUANTIA ETHERNET DRIVER PTP SUBSYSTEM -M: Egor Pomozov +M: Sukhdeep Singh L: netdev@vger.kernel.org S: Maintained W: http://www.aquantia.com @@ -2546,6 +2567,7 @@ F: drivers/nvmem/apple-spmi-nvmem.c F: drivers/phy/apple/ F: drivers/pinctrl/pinctrl-apple-gpio.c F: drivers/power/reset/macsmc-reboot.c +F: drivers/power/supply/macsmc-power.c F: drivers/pwm/pwm-apple.c F: drivers/rtc/rtc-macsmc.c F: drivers/soc/apple/* @@ -2704,8 +2726,9 @@ N: digicolor ARM/CORESIGHT FRAMEWORK AND DRIVERS M: Suzuki K Poulose -R: Mike Leach +R: Mike Leach R: James Clark +R: Leo Yan L: coresight@lists.linaro.org (moderated for non-subscribers) L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained @@ -2802,8 +2825,7 @@ S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/frank.li/linux.git F: Documentation/devicetree/bindings/firmware/fsl* F: Documentation/devicetree/bindings/firmware/nxp* -F: arch/arm/boot/dts/nxp/imx/ -F: arch/arm/boot/dts/nxp/mxs/ +F: arch/arm/boot/dts/nxp/ F: arch/arm64/boot/dts/freescale/ X: Documentation/devicetree/bindings/media/i2c/ X: arch/arm64/boot/dts/freescale/fsl-* @@ -2989,7 +3011,6 @@ S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/gclement/mvebu.git F: Documentation/devicetree/bindings/arm/marvell/ F: arch/arm/boot/dts/marvell/armada* -F: arch/arm/boot/dts/marvell/db-falcon* F: arch/arm/boot/dts/marvell/kirkwood* F: arch/arm/configs/mvebu_*_defconfig F: arch/arm/mach-mvebu/ @@ -3153,7 +3174,6 @@ F: drivers/clocksource/clksrc-dbx500-prcmu.c F: drivers/dma/ste_dma40* F: drivers/pmdomain/st/ste-ux500-pm-domain.c F: drivers/gpio/gpio-nomadik.c -F: drivers/hwspinlock/u8500_hsem.c F: drivers/i2c/busses/i2c-nomadik.c F: drivers/iio/adc/ab8500-gpadc.c F: drivers/mfd/ab8500* @@ -4056,7 +4076,7 @@ S: Maintained F: crypto/rsa* ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API -R: Dan Williams +R: Dan Williams S: Odd fixes W: http://sourceforge.net/projects/xscaleiop F: Documentation/crypto/async-tx-api.rst @@ -4159,14 +4179,16 @@ L: netdev@vger.kernel.org S: Maintained W: http://linux-atm.sourceforge.net F: drivers/atm/ +F: drivers/usb/atm/ F: include/linux/atm* F: include/linux/sonet.h F: include/uapi/linux/atm* F: include/uapi/linux/sonet.h +F: net/atm/ ATMEL MACB ETHERNET DRIVER -M: Nicolas Ferre -M: Claudiu Beznea +M: Théo Lebrun +R: Conor Dooley S: Maintained F: drivers/net/ethernet/cadence/ @@ -4282,27 +4304,17 @@ S: Maintained F: Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml F: drivers/video/backlight/aw99706.c -AX.25 NETWORK LAYER -L: linux-hams@vger.kernel.org -S: Orphan -W: https://linux-ax25.in-berlin.de -F: include/net/ax25.h -F: include/uapi/linux/ax25.h -F: net/ax25/ - AXENTIA ARM DEVICES -M: Peter Rosin L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -S: Maintained +S: Orphan F: arch/arm/boot/dts/microchip/at91-linea.dtsi F: arch/arm/boot/dts/microchip/at91-natte.dtsi F: arch/arm/boot/dts/microchip/at91-nattis-2-natte-2.dts F: arch/arm/boot/dts/microchip/at91-tse850-3.dts AXENTIA ASOC DRIVERS -M: Peter Rosin L: linux-sound@vger.kernel.org -S: Maintained +S: Orphan F: Documentation/devicetree/bindings/sound/axentia,* F: sound/soc/atmel/tse850-pcm5142.c @@ -4431,13 +4443,6 @@ F: include/uapi/linux/batadv_packet.h F: include/uapi/linux/batman_adv.h F: net/batman-adv/ -BAYCOM/HDLCDRV DRIVERS FOR AX.25 -M: Thomas Sailer -L: linux-hams@vger.kernel.org -S: Maintained -W: http://www.baycom.org/~tom/ham/ham.html -F: drivers/net/hamradio/baycom* - BCACHE (BLOCK LAYER CACHE) M: Coly Li M: Kent Overstreet @@ -5676,15 +5681,6 @@ T: git git://linuxtv.org/media.git F: Documentation/admin-guide/media/cafe_ccic* F: drivers/media/platform/marvell/ -CAIF NETWORK LAYER -L: netdev@vger.kernel.org -S: Orphan -F: Documentation/networking/caif/ -F: drivers/net/caif/ -F: include/net/caif/ -F: include/uapi/linux/caif/ -F: net/caif/ - CAKE QDISC M: Toke Høiland-Jørgensen L: cake@lists.bufferbloat.net (moderated for non-subscribers) @@ -5960,6 +5956,13 @@ S: Maintained F: Documentation/hwmon/powerz.rst F: drivers/hwmon/powerz.c +CHARLIEPLEX KEYPAD DRIVER +M: Hugo Villeneuve +S: Supported +W: http://www.mosaic-industries.com/embedded-systems/microcontroller-projects/electronic-circuits/matrix-keypad-scan-decode +F: Documentation/devicetree/bindings/input/gpio-charlieplex-keypad.yaml +F: drivers/input/keyboard/charlieplex_keypad.c + CHECKPATCH M: Andy Whitcroft M: Joe Perches @@ -6359,6 +6362,7 @@ F: include/uapi/linux/comedi.h COMMON CLK FRAMEWORK M: Michael Turquette M: Stephen Boyd +R: Brian Masney L: linux-clk@vger.kernel.org S: Maintained Q: http://patchwork.kernel.org/project/linux-clk/list/ @@ -6386,7 +6390,7 @@ L: linux-cifs@vger.kernel.org L: samba-technical@lists.samba.org (moderated for non-subscribers) S: Supported W: https://wiki.samba.org/index.php/LinuxCIFS -T: git git://git.samba.org/sfrench/cifs-2.6.git +T: git https://git.samba.org/sfrench/cifs-2.6.git F: Documentation/admin-guide/cifs/ F: fs/smb/client/ F: fs/smb/common/ @@ -6423,12 +6427,12 @@ F: include/linux/compiler_attributes.h COMPUTE EXPRESS LINK (CXL) M: Davidlohr Bueso -M: Jonathan Cameron +M: Jonathan Cameron M: Dave Jiang M: Alison Schofield M: Vishal Verma M: Ira Weiny -M: Dan Williams +M: Dan Williams L: linux-cxl@vger.kernel.org S: Maintained F: Documentation/driver-api/cxl @@ -6439,7 +6443,7 @@ F: include/uapi/linux/cxl_mem.h F: tools/testing/cxl/ COMPUTE EXPRESS LINK PMU (CPMU) -M: Jonathan Cameron +M: Jonathan Cameron L: linux-cxl@vger.kernel.org S: Maintained F: Documentation/admin-guide/perf/cxl.rst @@ -7024,20 +7028,6 @@ S: Maintained F: drivers/rtc/rtc-ds1685.c F: include/linux/rtc/ds1685.h -DAMA SLAVE for AX.25 -M: Joerg Reuter -L: linux-hams@vger.kernel.org -S: Maintained -W: http://yaina.de/jreuter/ -W: http://www.qsl.net/dl1bke/ -F: net/ax25/af_ax25.c -F: net/ax25/ax25_dev.c -F: net/ax25/ax25_ds_* -F: net/ax25/ax25_in.c -F: net/ax25/ax25_out.c -F: net/ax25/ax25_timer.c -F: net/ax25/sysctl_net_ax25.c - DASHARO ACPI PLATFORM DRIVER M: Michał Kopeć S: Maintained @@ -7092,6 +7082,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git core/debugobjec F: include/linux/debugobjects.h F: lib/debugobjects.c +DEC LANCE NETWORK DRIVER +M: "Maciej W. Rozycki" +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/ethernet/amd/declance.c + DECSTATION PLATFORM SUPPORT M: "Maciej W. Rozycki" L: linux-mips@vger.kernel.org @@ -7296,7 +7292,7 @@ S: Maintained F: scripts/dev-needs.sh DEVICE DIRECT ACCESS (DAX) -M: Dan Williams +M: Dan Williams M: Vishal Verma M: Dave Jiang L: nvdimm@lists.linux.dev @@ -7304,6 +7300,14 @@ L: linux-cxl@vger.kernel.org S: Supported F: drivers/dax/ +DEVICE DIRECT ACCESS (DAX) [fsdev_dax] +M: John Groves +M: John Groves +L: nvdimm@lists.linux.dev +L: linux-cxl@vger.kernel.org +S: Supported +F: drivers/dax/fsdev.c + DEVICE FREQUENCY (DEVFREQ) M: MyungJoo Ham M: Kyungmin Park @@ -7880,7 +7884,7 @@ F: drivers/gpu/drm/sun4i/sun8i* DRM DRIVER FOR APPLE TOUCH BARS M: Aun-Ali Zaidi -M: Aditya Garg +M: Aditya Garg L: dri-devel@lists.freedesktop.org S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git @@ -8200,10 +8204,9 @@ F: include/uapi/drm/nouveau_drm.h CORE DRIVER FOR NVIDIA GPUS [RUST] M: Danilo Krummrich M: Alexandre Courbot -L: nouveau@lists.freedesktop.org +L: nova-gpu@lists.linux.dev S: Supported W: https://rust-for-linux.com/nova-gpu-driver -Q: https://patchwork.freedesktop.org/project/nouveau/ B: https://gitlab.freedesktop.org/drm/nova/-/issues C: irc://irc.oftc.net/nouveau T: git https://gitlab.freedesktop.org/drm/rust/kernel.git drm-rust-next @@ -8212,10 +8215,9 @@ F: drivers/gpu/nova-core/ DRM DRIVER FOR NVIDIA GPUS [RUST] M: Danilo Krummrich -L: nouveau@lists.freedesktop.org +L: nova-gpu@lists.linux.dev S: Supported W: https://rust-for-linux.com/nova-gpu-driver -Q: https://patchwork.freedesktop.org/project/nouveau/ B: https://gitlab.freedesktop.org/drm/nova/-/issues C: irc://irc.oftc.net/nouveau T: git https://gitlab.freedesktop.org/drm/rust/kernel.git drm-rust-next @@ -8630,10 +8632,17 @@ S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: drivers/gpu/drm/gma500/ -DRM DRIVERS FOR HISILICON -M: Xinliang Liu +DRM DRIVERS FOR HISILICON HIBMC +M: Yongbang Shi M: Tian Tao R: Xinwei Kong +L: dri-devel@lists.freedesktop.org +S: Maintained +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: drivers/gpu/drm/hisilicon/hibmc + +DRM DRIVERS FOR HISILICON KIRIN +M: Xinliang Liu R: Sumit Semwal R: Yongqin Liu R: John Stultz @@ -8641,7 +8650,7 @@ L: dri-devel@lists.freedesktop.org S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/devicetree/bindings/display/hisilicon/ -F: drivers/gpu/drm/hisilicon/ +F: drivers/gpu/drm/hisilicon/kirin DRM DRIVERS FOR LIMA M: Qiang Yu @@ -8678,7 +8687,7 @@ F: drivers/phy/mediatek/phy-mtk-hdmi* F: drivers/phy/mediatek/phy-mtk-mipi* DRM DRIVERS FOR NVIDIA TEGRA -M: Thierry Reding +M: Thierry Reding M: Mikko Perttunen L: dri-devel@lists.freedesktop.org L: linux-tegra@vger.kernel.org @@ -8692,7 +8701,7 @@ F: include/linux/host1x.h F: include/uapi/drm/tegra_drm.h DRM DRIVERS FOR PRE-DCP APPLE DISPLAY OUTPUT -M: Sasha Finkelstein +M: Sasha Finkelstein R: Janne Grunau L: dri-devel@lists.freedesktop.org L: asahi@lists.linux.dev @@ -9140,6 +9149,8 @@ F: arch/mips/boot/dts/econet/ F: arch/mips/econet/ F: drivers/clocksource/timer-econet-en751221.c F: drivers/irqchip/irq-econet-en751221.c +F: include/dt-bindings/clock/econet,en751221-scu.h +F: include/dt-bindings/reset/econet,en751221-scu.h ECRYPT FILE SYSTEM M: Tyler Hicks @@ -9539,6 +9550,14 @@ T: git https://github.com/eswincomputing/linux-next.git F: Documentation/devicetree/bindings/riscv/eswin.yaml F: arch/riscv/boot/dts/eswin/ +ESWIN EIC7700 CLOCK DRIVER +M: Yifeng Huang +M: Xuyang Dong +S: Maintained +F: Documentation/devicetree/bindings/clock/eswin,eic7700-clock.yaml +F: drivers/clk/eswin/ +F: include/dt-bindings/clock/eswin,eic7700-clock.h + ET131X NETWORK DRIVER M: Mark Einon S: Odd Fixes @@ -9853,7 +9872,7 @@ F: include/linux/fcntl.h F: include/uapi/linux/fcntl.h FILESYSTEM DIRECT ACCESS (DAX) -M: Dan Williams +M: Dan Williams R: Matthew Wilcox R: Jan Kara L: linux-fsdevel@vger.kernel.org @@ -10308,7 +10327,7 @@ F: drivers/net/ethernet/freescale/dpaa FREESCALE QORIQ DPAA FMAN DRIVER M: Madalin Bucur -R: Sean Anderson +R: Sean Anderson L: netdev@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/net/fsl,fman*.yaml @@ -10598,13 +10617,19 @@ FWCTL SUBSYSTEM M: Dave Jiang M: Jason Gunthorpe M: Saeed Mahameed -R: Jonathan Cameron +R: Jonathan Cameron S: Maintained F: Documentation/userspace-api/fwctl/ F: drivers/fwctl/ F: include/linux/fwctl.h F: include/uapi/fwctl/ +FWCTL BNXT DRIVER +M: Pavan Chebbi +L: linux-kernel@vger.kernel.org +S: Maintained +F: drivers/fwctl/bnxt/ + FWCTL MLX5 DRIVER M: Saeed Mahameed R: Itay Avraham @@ -11427,14 +11452,6 @@ F: drivers/hid/hid-sensor-* F: drivers/iio/*/hid-* F: include/linux/hid-sensor-* -HID UNIVERSAL PIDFF DRIVER -M: Tomasz Pakuła -M: Oleg Makarenko -L: linux-input@vger.kernel.org -S: Maintained -B: https://github.com/JacKeTUs/universal-pidff/issues -F: drivers/hid/hid-universal-pidff.c - HID VRC-2 CAR CONTROLLER DRIVER M: Marcus Folkesson L: linux-input@vger.kernel.org @@ -11501,11 +11518,6 @@ T: git https://github.com/Rust-for-Linux/linux.git timekeeping-next F: rust/kernel/time.rs F: rust/kernel/time/ -HIGH-SPEED SCC DRIVER FOR AX.25 -L: linux-hams@vger.kernel.org -S: Orphan -F: drivers/net/hamradio/scc.c - HIGHPOINT ROCKETRAID 3xxx RAID DRIVER M: HighPoint Linux Team S: Supported @@ -12043,7 +12055,7 @@ F: Documentation/i2c/busses/i2c-nvidia-gpu.rst F: drivers/i2c/busses/i2c-nvidia-gpu.c I2C MUXES -M: Peter Rosin +M: Peter Rosin L: linux-i2c@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/i2c/i2c-arb* @@ -12093,11 +12105,12 @@ F: include/uapi/linux/i2c-*.h F: include/uapi/linux/i2c.h I2C SUBSYSTEM [RUST] -M: Igor Korotin +M: Igor Korotin R: Danilo Krummrich R: Daniel Almeida L: rust-for-linux@vger.kernel.org S: Maintained +T: git https://github.com/ikrtn/linux.git rust-i2c-next F: rust/kernel/i2c.rs F: samples/rust/rust_driver_i2c.rs F: samples/rust/rust_i2c_client.rs @@ -12443,7 +12456,7 @@ F: drivers/iio/industrialio-backend.c F: include/linux/iio/backend.h IIO DIGITAL POTENTIOMETER DAC -M: Peter Rosin +M: Peter Rosin L: linux-iio@vger.kernel.org S: Maintained F: Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac @@ -12451,7 +12464,7 @@ F: Documentation/devicetree/bindings/iio/dac/dpot-dac.yaml F: drivers/iio/dac/dpot-dac.c IIO ENVELOPE DETECTOR -M: Peter Rosin +M: Peter Rosin L: linux-iio@vger.kernel.org S: Maintained F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector @@ -12467,7 +12480,7 @@ F: include/linux/iio/iio-gts-helper.h F: drivers/iio/test/iio-test-gts.c IIO MULTIPLEXER -M: Peter Rosin +M: Peter Rosin L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml @@ -12498,7 +12511,7 @@ F: include/linux/iio/ F: tools/iio/ IIO UNIT CONVERTER -M: Peter Rosin +M: Peter Rosin L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/afe/current-sense-amplifier.yaml @@ -12622,6 +12635,7 @@ F: include/uapi/linux/if_infiniband.h F: include/uapi/rdma/ F: samples/bpf/ibumad_kern.c F: samples/bpf/ibumad_user.c +F: tools/testing/selftests/rdma/ INGENIC JZ4780 NAND DRIVER M: Harvey Hunt @@ -12735,6 +12749,7 @@ R: Eric Snowberg L: linux-integrity@vger.kernel.org S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git +F: include/linux/secure_boot.h F: security/integrity/ F: security/integrity/ima/ @@ -12940,7 +12955,7 @@ F: drivers/platform/x86/intel/hid.c INTEL I/OAT DMA DRIVER M: Dave Jiang -R: Dan Williams +R: Dan Williams L: dmaengine@vger.kernel.org S: Supported Q: https://patchwork.kernel.org/project/linux-dmaengine/list/ @@ -13395,6 +13410,12 @@ F: include/linux/interconnect-clk.h F: include/linux/interconnect-provider.h F: include/linux/interconnect.h +INTERCONNECT KUNIT TESTS +M: Kuan-Wei Chiu +L: linux-pm@vger.kernel.org +S: Maintained +F: drivers/interconnect/icc-kunit.c + INTERRUPT COUNTER DRIVER M: Oleksij Rempel R: Pengutronix Kernel Team @@ -13652,25 +13673,6 @@ S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending.git master F: drivers/infiniband/ulp/isert -ISDN/CMTP OVER BLUETOOTH -L: netdev@vger.kernel.org -S: Orphan -W: http://www.isdn4linux.de -F: Documentation/isdn/ -F: drivers/isdn/capi/ -F: include/linux/isdn/ -F: include/uapi/linux/isdn/ -F: net/bluetooth/cmtp/ - -ISDN/mISDN SUBSYSTEM -L: netdev@vger.kernel.org -S: Orphan -W: http://www.isdn4linux.de -F: drivers/isdn/Kconfig -F: drivers/isdn/Makefile -F: drivers/isdn/hardware/ -F: drivers/isdn/mISDN/ - ISL28022 HARDWARE MONITORING DRIVER M: Carsten Spieß L: linux-hwmon@vger.kernel.org @@ -13861,11 +13863,12 @@ F: scripts/Makefile.kcsan KDUMP M: Andrew Morton M: Baoquan He -R: Vivek Goyal -R: Dave Young +M: Mike Rapoport +M: Pasha Tatashin +M: Pratyush Yadav +R: Dave Young L: kexec@lists.infradead.org S: Maintained -W: http://lse.sourceforge.net/kdump/ F: Documentation/admin-guide/kdump/ F: fs/proc/vmcore.c F: include/linux/crash_core.h @@ -14007,7 +14010,7 @@ R: Sergey Senozhatsky R: Tom Talpey L: linux-cifs@vger.kernel.org S: Maintained -T: git git://git.samba.org/ksmbd.git +T: git https://git.samba.org/ksmbd.git F: Documentation/filesystems/smb/ksmbd.rst F: fs/smb/common/ F: fs/smb/server/ @@ -14058,6 +14061,7 @@ KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64) M: Marc Zyngier M: Oliver Upton R: Joey Gouly +R: Steffen Eiden R: Suzuki K Poulose R: Zenghui Yu L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) @@ -14177,6 +14181,9 @@ F: include/linux/kernfs.h KEXEC M: Andrew Morton M: Baoquan He +M: Mike Rapoport +M: Pasha Tatashin +M: Pratyush Yadav L: kexec@lists.infradead.org W: http://kernel.org/pub/linux/utils/kernel/kexec/ F: include/linux/kexec.h @@ -14184,18 +14191,18 @@ F: include/uapi/linux/kexec.h F: kernel/kexec* KEXEC HANDOVER (KHO) -M: Alexander Graf M: Mike Rapoport M: Pasha Tatashin -R: Pratyush Yadav +M: Pratyush Yadav +R: Alexander Graf L: kexec@lists.infradead.org L: linux-mm@kvack.org S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git F: Documentation/admin-guide/mm/kho.rst F: Documentation/core-api/kho/* F: include/linux/kexec_handover.h F: include/linux/kho/ -F: include/linux/kho/abi/ F: kernel/liveupdate/kexec_handover* F: lib/test_kho.c F: tools/testing/selftests/kho/ @@ -14550,6 +14557,17 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/lenovo/wmi-hotkey-utilities.c +LENOVO HID drivers +M: Derek J. Clark +M: Mark Pearson +L: linux-input@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/sysfs-driver-hid-lenovo-go +F: Documentation/ABI/testing/sysfs-driver-hid-lenovo-go-s +F: drivers/hid/hid-lenovo-go-s.c +F: drivers/hid/hid-lenovo-go.c +F: drivers/hid/hid-lenovo.c + LETSKETCH HID TABLET DRIVER M: Hans de Goede L: linux-input@vger.kernel.org @@ -14648,7 +14666,7 @@ K: libie LIBNVDIMM BTT: BLOCK TRANSLATION TABLE M: Vishal Verma -M: Dan Williams +M: Dan Williams M: Dave Jiang L: nvdimm@lists.linux.dev S: Supported @@ -14657,7 +14675,7 @@ P: Documentation/nvdimm/maintainer-entry-profile.rst F: drivers/nvdimm/btt* LIBNVDIMM PMEM: PERSISTENT MEMORY DRIVER -M: Dan Williams +M: Dan Williams M: Vishal Verma M: Dave Jiang L: nvdimm@lists.linux.dev @@ -14675,7 +14693,7 @@ F: Documentation/devicetree/bindings/pmem/pmem-region.yaml F: drivers/nvdimm/of_pmem.c LIBNVDIMM: NON-VOLATILE MEMORY DEVICE SUBSYSTEM -M: Dan Williams +M: Dan Williams M: Vishal Verma M: Dave Jiang M: Ira Weiny @@ -14883,15 +14901,15 @@ F: tools/testing/selftests/livepatch/ LIVE UPDATE M: Pasha Tatashin M: Mike Rapoport -R: Pratyush Yadav +M: Pratyush Yadav L: linux-kernel@vger.kernel.org S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux.git F: Documentation/core-api/liveupdate.rst F: Documentation/mm/memfd_preservation.rst F: Documentation/userspace-api/liveupdate.rst F: include/linux/kho/abi/ F: include/linux/liveupdate.h -F: include/linux/liveupdate/ F: include/uapi/linux/liveupdate.h F: kernel/liveupdate/ F: lib/tests/liveupdate.c @@ -15034,12 +15052,14 @@ S: Maintained F: Documentation/devicetree/bindings/gpio/loongson,ls-gpio.yaml F: drivers/gpio/gpio-loongson-64bit.c -LOONGSON-2 APB DMA DRIVER +LOONGSON-2 DMA DRIVER M: Binbin Zhou L: dmaengine@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml F: Documentation/devicetree/bindings/dma/loongson,ls2x-apbdma.yaml -F: drivers/dma/loongson2-apb-dma.c +F: drivers/dma/loongson/loongson2-apb-cmc-dma.c +F: drivers/dma/loongson/loongson2-apb-dma.c LOONGSON LS2X I2C DRIVER M: Binbin Zhou @@ -15241,7 +15261,7 @@ M: Andrea Cervesato M: Cyril Hrubis M: Jan Stancek M: Petr Vorel -M: Li Wang +M: Li Wang M: Yang Xu M: Xiao Yang L: ltp@lists.linux.it (subscribers-only) @@ -15294,6 +15314,13 @@ S: Maintained W: http://www.tazenda.demon.co.uk/phil/linux-hp F: arch/m68k/hp300/ +M68K ON MVME147 +M: Daniel Palmer +S: Maintained +F: arch/m68k/mvme147/ +F: drivers/net/ethernet/amd/mvme147.c +F: drivers/scsi/mvme147.* + M88DS3103 MEDIA DRIVER L: linux-media@vger.kernel.org S: Orphan @@ -15381,7 +15408,7 @@ F: include/net/netns/mctp.h F: net/mctp/ MAPLE TREE -M: Liam R. Howlett +M: Liam R. Howlett R: Alice Ryhl R: Andrew Ballance L: maple-tree@lists.infradead.org @@ -15701,7 +15728,7 @@ F: Documentation/devicetree/bindings/media/i2c/maxim,max96717.yaml F: drivers/media/i2c/max96717.c MAX9860 MONO AUDIO VOICE CODEC DRIVER -M: Peter Rosin +M: Peter Rosin L: linux-sound@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/sound/max9860.txt @@ -15800,6 +15827,12 @@ F: drivers/mfd/max77759.c F: drivers/nvmem/max77759-nvmem.c F: include/linux/mfd/max77759.h +MAXIM MAX77759 BATTERY CHARGER DRIVER +M: Amit Sunil Dhamne +L: linux-kernel@vger.kernel.org +S: Maintained +F: drivers/power/supply/max77759_charger.c + MAXIM MAX77802 PMIC REGULATOR DEVICE DRIVER M: Javier Martinez Canillas L: linux-kernel@vger.kernel.org @@ -15910,7 +15943,7 @@ F: Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml F: drivers/net/can/spi/mcp251xfd/ MCP4018 AND MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVERS -M: Peter Rosin +M: Peter Rosin L: linux-iio@vger.kernel.org S: Maintained F: Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531 @@ -16735,7 +16768,7 @@ MEMORY MANAGEMENT - CORE M: Andrew Morton M: David Hildenbrand R: Lorenzo Stoakes -R: Liam R. Howlett +R: Liam R. Howlett R: Vlastimil Babka R: Mike Rapoport R: Suren Baghdasaryan @@ -16781,7 +16814,7 @@ F: mm/sparse.c F: mm/util.c F: mm/vmpressure.c F: mm/vmstat.c -N: include/linux/page[-_]* +N: include\/linux\/page[-_][a-zA-Z]* MEMORY MANAGEMENT - EXECMEM M: Andrew Morton @@ -16848,8 +16881,12 @@ F: mm/migrate_device.c MEMORY MANAGEMENT - MGLRU (MULTI-GEN LRU) M: Andrew Morton -M: Axel Rasmussen -M: Yuanchu Xie +R: Kairui Song +R: Qi Zheng +R: Shakeel Butt +R: Barry Song +R: Axel Rasmussen +R: Yuanchu Xie R: Wei Xu L: linux-mm@kvack.org S: Maintained @@ -16867,7 +16904,7 @@ MEMORY MANAGEMENT - MISC M: Andrew Morton M: David Hildenbrand R: Lorenzo Stoakes -R: Liam R. Howlett +R: Liam R. Howlett R: Vlastimil Babka R: Mike Rapoport R: Suren Baghdasaryan @@ -16934,6 +16971,7 @@ S: Maintained F: include/linux/compaction.h F: include/linux/gfp.h F: include/linux/page-isolation.h +F: include/linux/pageblock-flags.h F: mm/compaction.c F: mm/debug_page_alloc.c F: mm/debug_page_ref.c @@ -16955,7 +16993,7 @@ M: Andrew Morton M: Johannes Weiner R: David Hildenbrand R: Michal Hocko -R: Qi Zheng +R: Qi Zheng R: Shakeel Butt R: Lorenzo Stoakes L: linux-mm@kvack.org @@ -16968,7 +17006,7 @@ M: Andrew Morton M: David Hildenbrand M: Lorenzo Stoakes R: Rik van Riel -R: Liam R. Howlett +R: Liam R. Howlett R: Vlastimil Babka R: Harry Yoo R: Jann Horn @@ -17015,7 +17053,7 @@ M: David Hildenbrand M: Lorenzo Stoakes R: Zi Yan R: Baolin Wang -R: Liam R. Howlett +R: Liam R. Howlett R: Nico Pache R: Ryan Roberts R: Dev Jain @@ -17053,7 +17091,7 @@ F: tools/testing/selftests/mm/uffd-*.[ch] MEMORY MANAGEMENT - RUST M: Alice Ryhl R: Lorenzo Stoakes -R: Liam R. Howlett +R: Liam R. Howlett L: linux-mm@kvack.org L: rust-for-linux@vger.kernel.org S: Maintained @@ -17067,7 +17105,7 @@ F: rust/kernel/page.rs MEMORY MAPPING M: Andrew Morton -M: Liam R. Howlett +M: Liam R. Howlett M: Lorenzo Stoakes R: Vlastimil Babka R: Jann Horn @@ -17099,7 +17137,7 @@ F: tools/testing/vma/ MEMORY MAPPING - LOCKING M: Andrew Morton M: Suren Baghdasaryan -M: Liam R. Howlett +M: Liam R. Howlett M: Lorenzo Stoakes R: Vlastimil Babka R: Shakeel Butt @@ -17114,7 +17152,7 @@ F: mm/mmap_lock.c MEMORY MAPPING - MADVISE (MEMORY ADVICE) M: Andrew Morton -M: Liam R. Howlett +M: Liam R. Howlett M: Lorenzo Stoakes M: David Hildenbrand R: Vlastimil Babka @@ -17827,6 +17865,7 @@ F: arch/mips/boot/dts/loongson/loongson1* F: arch/mips/configs/loongson1_defconfig F: arch/mips/loongson32/ F: drivers/*/*loongson1* +F: drivers/dma/loongson/loongson1-apb-dma.c F: drivers/mtd/nand/raw/loongson-nand-controller.c F: drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c F: sound/soc/loongson/loongson1_ac97.c @@ -17908,6 +17947,7 @@ F: drivers/media/dvb-frontends/mn88473* MOBILEYE MIPS SOCS M: Vladimir Kondratiev +M: Benoît Monin M: Gregory CLEMENT M: Théo Lebrun L: linux-mips@vger.kernel.org @@ -17915,12 +17955,13 @@ S: Maintained F: Documentation/devicetree/bindings/mips/mobileye.yaml F: Documentation/devicetree/bindings/soc/mobileye/ F: arch/mips/boot/dts/mobileye/ -F: arch/mips/configs/eyeq5_defconfig +F: arch/mips/configs/eyeq*_defconfig F: arch/mips/mobileye/board-epm5.its.S F: drivers/clk/clk-eyeq.c F: drivers/pinctrl/pinctrl-eyeq5.c F: drivers/reset/reset-eyeq.c F: include/dt-bindings/clock/mobileye,eyeq5-clk.h +F: include/dt-bindings/clock/mobileye,eyeq6lplus-clk.h MODULE SUPPORT M: Luis Chamberlain @@ -18207,7 +18248,7 @@ F: include/linux/mmc/ F: include/uapi/linux/mmc/ MULTIPLEXER SUBSYSTEM -M: Peter Rosin +M: Peter Rosin S: Odd Fixes F: Documentation/ABI/testing/sysfs-class-mux* F: Documentation/devicetree/bindings/mux/ @@ -18379,14 +18420,6 @@ F: net/bridge/br_netfilter*.c F: net/netfilter/ F: tools/testing/selftests/net/netfilter/ -NETROM NETWORK LAYER -L: linux-hams@vger.kernel.org -S: Orphan -W: https://linux-ax25.in-berlin.de -F: include/net/netrom.h -F: include/uapi/linux/netrom.h -F: net/netrom/ - NETRONIX EMBEDDED CONTROLLER M: Jonathan Neuschäfer S: Maintained @@ -18649,19 +18682,59 @@ F: net/xfrm/ F: tools/testing/selftests/net/ipsec.c NETWORKING [IPv4/IPv6] -M: "David S. Miller" M: David Ahern +M: Ido Schimmel L: netdev@vger.kernel.org S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git -F: arch/x86/net/* -F: include/linux/ip.h -F: include/linux/ipv6* +F: Documentation/netlink/specs/rt-addr.yaml +F: Documentation/netlink/specs/rt-neigh.yaml +F: Documentation/netlink/specs/rt-route.yaml +F: Documentation/netlink/specs/rt-rule.yaml +F: include/linux/inetdevice.h +F: include/linux/mroute* +F: include/net/addrconf.h +F: include/net/arp.h F: include/net/fib* +F: include/net/if_inet6.h +F: include/net/inetpeer.h F: include/net/ip* +F: include/net/lwtunnel.h +F: include/net/ndisc.h +F: include/net/netns/nexthop.h +F: include/net/nexthop.h F: include/net/route.h -F: net/ipv4/ -F: net/ipv6/ +F: include/uapi/linux/fib_rules.h +F: include/uapi/linux/in_route.h +F: include/uapi/linux/mroute* +F: include/uapi/linux/nexthop.h +F: net/core/fib* +F: net/core/lwtunnel.c +F: net/ipv4/arp.c +F: net/ipv4/devinet.c +F: net/ipv4/fib* +F: net/ipv4/icmp.c +F: net/ipv4/igmp.c +F: net/ipv4/inet_fragment.c +F: net/ipv4/inetpeer.c +F: net/ipv4/ip* +F: net/ipv4/metrics.c +F: net/ipv4/netlink.c +F: net/ipv4/nexthop.c +F: net/ipv4/route.c +F: net/ipv6/addr* +F: net/ipv6/anycast.c +F: net/ipv6/exthdrs.c +F: net/ipv6/exthdrs_core.c +F: net/ipv6/fib* +F: net/ipv6/icmp.c +F: net/ipv6/ip* +F: net/ipv6/mcast* +F: net/ipv6/ndisc.c +F: net/ipv6/output_core.c +F: net/ipv6/reassembly.c +F: net/ipv6/route.c +F: tools/testing/selftests/net/fib* +F: tools/testing/selftests/net/forwarding/ NETWORKING [LABELED] (NetLabel, Labeled IPsec, SECMARK) M: Paul Moore @@ -18796,18 +18869,11 @@ F: Documentation/networking/net_failover.rst F: drivers/net/net_failover.c F: include/net/net_failover.h -NEXTHOP -M: David Ahern -L: netdev@vger.kernel.org -S: Maintained -F: include/net/netns/nexthop.h -F: include/net/nexthop.h -F: include/uapi/linux/nexthop.h -F: net/ipv4/nexthop.c - NFC SUBSYSTEM -L: netdev@vger.kernel.org -S: Orphan +M: David Heidelberg +L: oe-linux-nfc@lists.linux.dev +S: Maintained +T: git https://codeberg.org/linux-nfc/linux.git F: Documentation/devicetree/bindings/net/nfc/ F: drivers/nfc/ F: include/net/nfc/ @@ -18983,6 +19049,15 @@ W: https://github.com/davejiang/linux/wiki T: git https://github.com/davejiang/linux.git F: drivers/ntb/hw/intel/ +NTFS FILESYSTEM +M: Namjae Jeon +M: Hyunchul Lee +L: linux-fsdevel@vger.kernel.org +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs.git +F: Documentation/filesystems/ntfs.rst +F: fs/ntfs/ + NTFS3 FILESYSTEM M: Konstantin Komarov L: ntfs3@lists.linux.dev @@ -19040,7 +19115,7 @@ NVIDIA GHES VENDOR CPER RECORD HANDLER M: Kai-Heng Feng L: linux-acpi@vger.kernel.org S: Maintained -F: drivers/acpi/apei/nvidia-ghes.c +F: drivers/acpi/apei/ghes-nvidia.c NVIDIA VRS RTC DRIVER M: Shubhi Garg @@ -19282,7 +19357,7 @@ F: include/dt-bindings/display/tda998x.h K: "nxp,tda998x" NXP TFA9879 DRIVER -M: Peter Rosin +M: Peter Rosin L: linux-sound@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/sound/trivial-codec.yaml @@ -19368,8 +19443,8 @@ F: drivers/net/dsa/ocelot/ocelot_ext.c F: include/linux/mfd/ocelot.h OCXL (Open Coherent Accelerator Processor Interface OpenCAPI) DRIVER -M: Frederic Barrat -M: Andrew Donnellan +M: Mahesh J Salgaonkar +R: Andrew Donnellan L: linuxppc-dev@lists.ozlabs.org S: Odd Fixes F: Documentation/userspace-api/accelerators/ocxl.rst @@ -19894,12 +19969,6 @@ L: linux-rtc@vger.kernel.org S: Maintained F: drivers/rtc/rtc-optee.c -OPA-VNIC DRIVER -M: Dennis Dalessandro -L: linux-rdma@vger.kernel.org -S: Supported -F: drivers/infiniband/ulp/opa_vnic - OPEN ALLIANCE 10BASE-T1S MACPHY SERIAL INTERFACE FRAMEWORK M: Parthiban Veerasooran L: netdev@vger.kernel.org @@ -20092,7 +20161,9 @@ F: kernel/padata.c PAGE CACHE M: Matthew Wilcox (Oracle) +R: Jan Kara L: linux-fsdevel@vger.kernel.org +L: linux-mm@kvack.org S: Supported T: git git://git.infradead.org/users/willy/pagecache.git F: Documentation/filesystems/locking.rst @@ -20287,13 +20358,14 @@ F: Documentation/devicetree/bindings/pci/marvell,armada8k-pcie.yaml F: drivers/pci/controller/dwc/pcie-armada8k.c PCI DRIVER FOR CADENCE PCIE IP +R: Aksh Garg L: linux-pci@vger.kernel.org S: Orphan F: Documentation/devicetree/bindings/pci/cdns,* -F: drivers/pci/controller/cadence/*cadence* +F: drivers/pci/controller/cadence/ PCI DRIVER FOR CIX Sky1 -M: Hans Zhang +M: Hans Zhang <18255117159@163.com> L: linux-pci@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/pci/cix,sky1-pcie-*.yaml @@ -20382,7 +20454,7 @@ S: Maintained F: drivers/pci/controller/*mvebu* PCI DRIVER FOR NVIDIA TEGRA -M: Thierry Reding +M: Thierry Reding L: linux-tegra@vger.kernel.org L: linux-pci@vger.kernel.org S: Supported @@ -20405,7 +20477,7 @@ F: drivers/pci/controller/plda/pcie-plda-host.c F: drivers/pci/controller/plda/pcie-plda.h PCI DRIVER FOR RENESAS R-CAR -M: Marek Vasut +M: Marek Vasut M: Yoshihiro Shimoda L: linux-pci@vger.kernel.org L: linux-renesas-soc@vger.kernel.org @@ -20746,6 +20818,7 @@ M: Dominik Brodowski S: Odd Fixes T: git git://git.kernel.org/pub/scm/linux/kernel/git/brodo/linux.git F: Documentation/pcmcia/ +F: drivers/net/ethernet/8390/pcnet_cs.c F: drivers/pcmcia/ F: include/pcmcia/ F: tools/pcmcia/ @@ -20858,7 +20931,7 @@ PERFORMANCE EVENTS TOOLING ARM64 R: John Garry R: Will Deacon R: James Clark -R: Mike Leach +R: Mike Leach R: Leo Yan L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported @@ -22878,7 +22951,7 @@ N: riscv K: riscv RISC-V IOMMU -M: Tomasz Jeznach +M: Tomasz Jeznach L: iommu@lists.linux.dev L: linux-riscv@lists.infradead.org S: Maintained @@ -22902,7 +22975,7 @@ F: Documentation/devicetree/bindings/pinctrl/microchip,mpfs-pinctrl-mssio.yaml F: Documentation/devicetree/bindings/pinctrl/microchip,pic64gx-pinctrl-gpio2.yaml F: Documentation/devicetree/bindings/pwm/microchip,corepwm.yaml F: Documentation/devicetree/bindings/riscv/microchip.yaml -F: Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml +F: Documentation/devicetree/bindings/soc/microchip/microchip,mpfs*.yaml F: Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml F: Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml F: arch/riscv/boot/dts/microchip/ @@ -22977,8 +23050,12 @@ M: Joel Stanley L: linux-riscv@lists.infradead.org S: Maintained T: git https://github.com/tenstorrent/linux.git +F: Documentation/devicetree/bindings/clock/tenstorrent,atlantis-prcm-rcpu.yaml F: Documentation/devicetree/bindings/riscv/tenstorrent.yaml F: arch/riscv/boot/dts/tenstorrent/ +F: drivers/clk/tenstorrent/ +F: drivers/reset/reset-tenstorrent-atlantis.c +F: include/dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h RISC-V THEAD SoC SUPPORT M: Drew Fustini @@ -23212,14 +23289,6 @@ F: include/linux/mfd/rohm-bd96802.h F: include/linux/mfd/rohm-generic.h F: include/linux/mfd/rohm-shared.h -ROSE NETWORK LAYER -L: linux-hams@vger.kernel.org -S: Orphan -W: https://linux-ax25.in-berlin.de -F: include/net/rose.h -F: include/uapi/linux/rose.h -F: net/rose/ - ROTATION DRIVER FOR ALLWINNER A83T M: Jernej Skrabec L: linux-media@vger.kernel.org @@ -23345,7 +23414,7 @@ RUST [ALLOC] M: Danilo Krummrich R: Lorenzo Stoakes R: Vlastimil Babka -R: Liam R. Howlett +R: Liam R. Howlett R: Uladzislau Rezki L: rust-for-linux@vger.kernel.org S: Maintained @@ -23497,7 +23566,7 @@ F: drivers/s390/net/ S390 PCI SUBSYSTEM M: Niklas Schnelle -M: Gerald Schaefer +M: Gerd Bayer L: linux-s390@vger.kernel.org S: Supported F: Documentation/arch/s390/pci.rst @@ -24290,7 +24359,7 @@ F: include/media/i2c/rj54n1cb0c.h SHRINKER M: Andrew Morton M: Dave Chinner -R: Qi Zheng +R: Qi Zheng R: Roman Gushchin R: Muchun Song L: linux-mm@kvack.org @@ -24590,8 +24659,9 @@ L: linux-cifs@vger.kernel.org L: samba-technical@lists.samba.org (moderated for non-subscribers) S: Maintained F: fs/smb/client/smbdirect.* -F: fs/smb/common/smbdirect/ +F: fs/smb/smbdirect/ F: fs/smb/server/transport_rdma.* +F: include/linux/smbdirect.h SMC91x ETHERNET DRIVER M: Nicolas Pitre @@ -24658,8 +24728,8 @@ F: Documentation/devicetree/bindings/net/socionext,synquacer-netsec.yaml F: drivers/net/ethernet/socionext/netsec.c SOCIONEXT (SNI) Synquacer SPI DRIVER -M: Masahisa Kojima -M: Jassi Brar +M: Masahisa Kojima +M: Jassi Brar L: linux-spi@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/spi/socionext,synquacer-spi.yaml @@ -24706,6 +24776,7 @@ L: linux-rdma@vger.kernel.org S: Supported F: drivers/infiniband/sw/rxe/ F: include/uapi/rdma/rdma_user_rxe.h +F: tools/testing/selftests/rdma/rxe* SOFTLOGIC 6x10 MPEG CODEC M: Bluecherry Maintainers @@ -24739,6 +24810,7 @@ SOFTWARE RAID (Multiple Disks) SUPPORT M: Song Liu M: Yu Kuai R: Li Nan +R: Xiao Ni L: linux-raid@vger.kernel.org S: Supported Q: https://patchwork.kernel.org/project/linux-raid/list/ @@ -25210,6 +25282,7 @@ F: drivers/hwmon/pmbus/stef48h28.c ST STM32 FIREWALL M: Gatien Chevallier S: Maintained +F: drivers/bus/stm32_dbg_bus.c F: drivers/bus/stm32_etzpc.c F: drivers/bus/stm32_firewall.c F: drivers/bus/stm32_rifsc.c @@ -25295,6 +25368,13 @@ S: Maintained F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml F: drivers/iio/proximity/vl53l0x-i2c.c +ST VL53L1X ToF RANGER(I2C) IIO DRIVER +M: Siratul Islam +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml +F: drivers/iio/proximity/vl53l1x-i2c.c + STABLE BRANCH M: Greg Kroah-Hartman M: Sasha Levin @@ -25346,7 +25426,7 @@ F: drivers/staging/ STANDALONE CACHE CONTROLLER DRIVERS M: Conor Dooley -M: Jonathan Cameron +M: Jonathan Cameron S: Maintained T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/ F: Documentation/devicetree/bindings/cache/ @@ -25451,10 +25531,11 @@ F: Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml F: drivers/reset/starfive/reset-starfive-jh71* F: include/dt-bindings/reset/starfive?jh71*.h -STARFIVE JH71X0 USB DRIVERS +STARFIVE USB DRIVERS M: Minda Chen S: Maintained F: Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml +F: Documentation/devicetree/bindings/usb/starfive,jhb100-dwc3.yaml F: drivers/usb/cdns3/cdns3-starfive.c STARFIVE JH71XX PMU CONTROLLER DRIVER @@ -25716,6 +25797,13 @@ S: Supported F: include/net/switchdev.h F: net/switchdev/ +SWITCHTEC DMA DRIVER +M: Kelvin Cao +M: Logan Gunthorpe +L: dmaengine@vger.kernel.org +S: Maintained +F: drivers/dma/switchtec_dma.c + SY8106A REGULATOR DRIVER M: Icenowy Zheng S: Maintained @@ -26144,7 +26232,7 @@ F: include/linux/tee_drv.h F: include/uapi/linux/tee.h TEGRA ARCHITECTURE SUPPORT -M: Thierry Reding +M: Thierry Reding M: Jonathan Hunter L: linux-tegra@vger.kernel.org S: Supported @@ -26176,7 +26264,7 @@ S: Supported F: drivers/i2c/busses/i2c-tegra.c TEGRA IOMMU DRIVERS -M: Thierry Reding +M: Thierry Reding R: Krishna Reddy L: linux-tegra@vger.kernel.org S: Supported @@ -26197,12 +26285,12 @@ F: Documentation/devicetree/bindings/mtd/nvidia,tegra20-nand.yaml F: drivers/mtd/nand/raw/tegra_nand.c TEGRA PWM DRIVER -M: Thierry Reding +M: Thierry Reding S: Supported F: drivers/pwm/pwm-tegra.c TEGRA QUAD SPI DRIVER -M: Thierry Reding +M: Thierry Reding M: Jonathan Hunter M: Sowjanya Komatineni L: linux-tegra@vger.kernel.org @@ -26220,7 +26308,7 @@ S: Supported F: drivers/spi/spi-tegra* TEGRA VIDEO DRIVER -M: Thierry Reding +M: Thierry Reding M: Jonathan Hunter M: Sowjanya Komatineni M: Luca Ceresoli @@ -26494,7 +26582,7 @@ F: drivers/media/i2c/thp7312.c F: include/uapi/linux/thp7312.h THUNDERBOLT DMA TRAFFIC TEST DRIVER -M: Isaac Hazan +M: Mika Westerberg L: linux-usb@vger.kernel.org S: Maintained F: drivers/thunderbolt/dma_test.c @@ -27066,7 +27154,7 @@ S: Maintained F: Documentation/devicetree/bindings/trigger-source/* TRUSTED EXECUTION ENVIRONMENT SECURITY MANAGER (TSM) -M: Dan Williams +M: Dan Williams L: linux-coco@lists.linux.dev S: Maintained F: Documentation/ABI/testing/configfs-tsm-report @@ -27474,6 +27562,15 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git F: Documentation/hid/hiddev.rst F: drivers/hid/usbhid/ +USB HID PID DRIVERS (USB WHEELBASES, JOYSTICKS, RUDDERS, ...) +M: Tomasz Pakuła +M: Oleg Makarenko +L: linux-input@vger.kernel.org +S: Maintained +B: https://github.com/JacKeTUs/universal-pidff/issues +F: drivers/hid/usbhid/hid-pidff* +F: drivers/hid/hid-universal-pidff.c + USB INTEL XHCI ROLE MUX DRIVER M: Hans de Goede L: linux-usb@vger.kernel.org @@ -29109,7 +29206,7 @@ S: Orphan F: drivers/net/ethernet/xilinx/ll_temac* XILINX PWM DRIVER -M: Sean Anderson +M: Sean Anderson S: Maintained F: drivers/pwm/pwm-xilinx.c F: include/clocksource/timer-xilinx.h @@ -29255,13 +29352,6 @@ F: lib/decompress_unxz.c F: lib/xz/ F: scripts/xz_wrap.sh -YAM DRIVER FOR AX.25 -M: Jean-Paul Roubelat -L: linux-hams@vger.kernel.org -S: Maintained -F: drivers/net/hamradio/yam* -F: include/linux/yam.h - YAMA SECURITY MODULE M: Kees Cook S: Supported @@ -29283,16 +29373,6 @@ S: Maintained F: Documentation/input/devices/yealink.rst F: drivers/input/misc/yealink.* -Z8530 DRIVER FOR AX.25 -M: Joerg Reuter -L: linux-hams@vger.kernel.org -S: Maintained -W: http://yaina.de/jreuter/ -W: http://www.qsl.net/dl1bke/ -F: Documentation/networking/device_drivers/hamradio/z8530drv.rst -F: drivers/net/hamradio/*scc.c -F: drivers/net/hamradio/z8530.h - ZD1211RW WIRELESS DRIVER L: linux-wireless@vger.kernel.org S: Orphan diff --git a/Makefile b/Makefile index 54e1ae602000..9f59598d3a08 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 7 -PATCHLEVEL = 0 +PATCHLEVEL = 1 SUBLEVEL = 0 -EXTRAVERSION = +EXTRAVERSION = -rc4 NAME = Baby Opossum Posse # *DOCUMENTATION* @@ -486,6 +486,8 @@ export rust_common_flags := --edition=2021 \ -Wclippy::as_ptr_cast_mut \ -Wclippy::as_underscore \ -Wclippy::cast_lossless \ + -Aclippy::collapsible_if \ + -Aclippy::collapsible_match \ -Wclippy::ignored_unit_patterns \ -Aclippy::incompatible_msrv \ -Wclippy::mut_mut \ @@ -657,6 +659,8 @@ export RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o \ # Basic helpers built in scripts/basic/ PHONY += scripts_basic +scripts_basic: KBUILD_HOSTCFLAGS := $(KBUILD_HOSTCFLAGS) +scripts_basic: KBUILD_HOSTLDFLAGS := $(KBUILD_HOSTLDFLAGS) scripts_basic: $(Q)$(MAKE) $(build)=scripts/basic diff --git a/arch/Kconfig b/arch/Kconfig index 334b69505381..e86880045158 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1127,6 +1127,13 @@ config ARCH_WANTS_MODULES_DATA_IN_VMALLOC For architectures like powerpc/32 which have constraints on module allocation and need to allocate module data outside of module area. +config ARCH_WANTS_MODULES_TEXT_SECTIONS + bool + help + For architectures like 32-bit parisc which require that functions in + modules have to keep code in own text sections (-ffunction-sections) + and to avoid merging all text into one big text section, + config ARCH_WANTS_EXECMEM_LATE bool help @@ -1839,4 +1846,7 @@ config ARCH_WANTS_PRE_LINK_VMLINUX config ARCH_HAS_CPU_ATTACK_VECTORS bool +config HAVE_ARCH_GET_SECUREBOOT + def_bool EFI + endmenu diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 6c7dbf0adad6..7b7dafe7d9df 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -31,6 +31,8 @@ config ALPHA select GENERIC_SMP_IDLE_THREAD select HAS_IOPORT select HAVE_ARCH_AUDITSYSCALL + select HAVE_ARCH_SECCOMP + select HAVE_ARCH_SECCOMP_FILTER select HAVE_MOD_ARCH_SPECIFIC select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_RELA diff --git a/arch/alpha/configs/defconfig b/arch/alpha/configs/defconfig index 3280bd9e6578..29748bbd94f3 100644 --- a/arch/alpha/configs/defconfig +++ b/arch/alpha/configs/defconfig @@ -45,7 +45,6 @@ CONFIG_NET_TULIP=y CONFIG_DE2104X=m CONFIG_TULIP=y CONFIG_TULIP_MMIO=y -CONFIG_YELLOWFIN=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_RTC_CLASS=y diff --git a/arch/alpha/include/asm/pgtable.h b/arch/alpha/include/asm/pgtable.h index 268ddde33617..8e00cf9dc39d 100644 --- a/arch/alpha/include/asm/pgtable.h +++ b/arch/alpha/include/asm/pgtable.h @@ -126,6 +126,17 @@ struct vm_area_struct; */ #define pgprot_noncached(prot) (prot) +/* + * All caching attribute macros are identity on Alpha, so the generic + * pgprot_modify() degenerates to tautological self-comparisons. + * Override it to just return newprot directly. + */ +#define pgprot_modify pgprot_modify +static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot) +{ + return newprot; +} + /* * On certain platforms whose physical address space can overlap KSEG, * namely EV6 and above, we must re-twiddle the physaddr to restore the diff --git a/arch/alpha/include/asm/seccomp.h b/arch/alpha/include/asm/seccomp.h new file mode 100644 index 000000000000..311934d20340 --- /dev/null +++ b/arch/alpha/include/asm/seccomp.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_ALPHA_SECCOMP_H +#define _ASM_ALPHA_SECCOMP_H + +#include +#include +#include + +#define SECCOMP_ARCH_NATIVE AUDIT_ARCH_ALPHA +#define SECCOMP_ARCH_NATIVE_NR NR_syscalls +#define SECCOMP_ARCH_NATIVE_NAME "alpha" + +#endif /* _ASM_ALPHA_SECCOMP_H */ diff --git a/arch/alpha/include/asm/syscall.h b/arch/alpha/include/asm/syscall.h index f21babaeed85..584b1ab2e325 100644 --- a/arch/alpha/include/asm/syscall.h +++ b/arch/alpha/include/asm/syscall.h @@ -3,6 +3,10 @@ #define _ASM_ALPHA_SYSCALL_H #include +#include +#include +#include +#include static inline int syscall_get_arch(struct task_struct *task) { @@ -12,7 +16,91 @@ static inline int syscall_get_arch(struct task_struct *task) static inline long syscall_get_return_value(struct task_struct *task, struct pt_regs *regs) { - return regs->r0; + return regs->r19 ? -(long)regs->r0 : (long)regs->r0; +} + +/* + * Alpha syscall ABI / kernel conventions: + * - PAL provides syscall number in r0 on entry. + * - The kernel tracks the active syscall number in regs->r1 (mutable) and + * preserves the original syscall number in regs->r2 for rollback/restart. + * - Return value is in regs->r0, with regs->r19 ("a3") as the error flag + * (0=success, 1=error; on error regs->r0 holds positive errno). + */ + +static inline long syscall_get_nr(struct task_struct *task, + struct pt_regs *regs) +{ + return (long)regs->r1; +} + +static inline void syscall_set_nr(struct task_struct *task, + struct pt_regs *regs, + long nr) +{ + regs->r1 = (unsigned long)nr; +} + +/* + * Syscall arguments: + * regs->r16..regs->r21 carry up to 6 syscall arguments on entry. + * Note: regs->r19 is also used as "a3" (error flag) on syscall return. + */ + +static inline void syscall_get_arguments(struct task_struct *task, + struct pt_regs *regs, + unsigned long *args) +{ + args[0] = regs->r16; + args[1] = regs->r17; + args[2] = regs->r18; + args[3] = regs->r19; + args[4] = regs->r20; + args[5] = regs->r21; +} + +static inline void syscall_set_arguments(struct task_struct *task, + struct pt_regs *regs, + const unsigned long *args) +{ + regs->r16 = args[0]; + regs->r17 = args[1]; + regs->r18 = args[2]; + regs->r19 = args[3]; + regs->r20 = args[4]; + regs->r21 = args[5]; +} +/* + * Set return value for a syscall. + * Alpha uses r0 for return value and r19 ("a3") as the error indicator: + * a3 = 0 => success + * a3 = 1 => error, and userspace interprets r0 as errno (positive). + * + * The kernel reports errors to userspace by setting a3=1 and placing a + * positive errno value in r0. Some syscall paths do this in entry.S, + * while others (e.g. seccomp/ptrace helpers) use syscall_set_return_value(). + */ + +static inline void syscall_set_return_value(struct task_struct *task, + struct pt_regs *regs, + int error, long val) +{ + + if (error) { + /* error is negative errno in this tree */ + regs->r0 = (unsigned long)(-error); /* positive errno */ + regs->r19 = 1; /* a3 = error */ + } else { + regs->r0 = (unsigned long)val; + regs->r19 = 0; /* a3 = success */ + } +} + +/* Restore the original syscall nr after seccomp/ptrace modified regs->r1. */ +static inline void syscall_rollback(struct task_struct *task, + struct pt_regs *regs) +{ + regs->r1 = regs->r2; } #endif /* _ASM_ALPHA_SYSCALL_H */ diff --git a/arch/alpha/include/asm/thread_info.h b/arch/alpha/include/asm/thread_info.h index 98ccbca64984..94ef9cfa30f5 100644 --- a/arch/alpha/include/asm/thread_info.h +++ b/arch/alpha/include/asm/thread_info.h @@ -56,7 +56,8 @@ register unsigned long *current_stack_pointer __asm__ ("$30"); * - pending work-to-be-done flags come first and must be assigned to be * within bits 0 to 7 to fit in and immediate operand. * - * TIF_SYSCALL_TRACE is known to be 0 via blbs. + * (Historically TIF_SYSCALL_TRACE was known to be 0 via blbs, but we may + * also test multiple bits via masks now.) */ #define TIF_SYSCALL_TRACE 0 /* syscall trace active */ #define TIF_NOTIFY_RESUME 1 /* callback before returning to user */ @@ -64,6 +65,7 @@ register unsigned long *current_stack_pointer __asm__ ("$30"); #define TIF_NEED_RESCHED 3 /* rescheduling necessary */ #define TIF_SYSCALL_AUDIT 4 /* syscall audit active */ #define TIF_NOTIFY_SIGNAL 5 /* signal notifications exist */ +#define TIF_SECCOMP 6 /* seccomp syscall filtering active */ #define TIF_DIE_IF_KERNEL 9 /* dik recursion lock */ #define TIF_MEMDIE 13 /* is terminating due to OOM killer */ #define TIF_POLLING_NRFLAG 14 /* idle is polling for TIF_NEED_RESCHED */ @@ -74,8 +76,20 @@ register unsigned long *current_stack_pointer __asm__ ("$30"); #define _TIF_NOTIFY_RESUME (1< #include #include +#include .text .set noat @@ -35,6 +36,64 @@ .size \func, . - \func .endm +/* + * SYSCALL_SKIP_RETURN_RESTART_GATE + * + * Used when syscall dispatch is skipped (seccomp/ptrace injected nr=-1). + * - Ensure we never return r0==-1 with a3==0 (success); convert to ENOSYS. + * - Gate whether syscall restart is allowed by preserving restart context + * only for ERESTART* returns. Result: + * $26 = 0 => restart allowed + * $26 = 1 => restart NOT allowed + * $18 = preserved syscall nr (regs->r2) if restart allowed, else 0 + */ +.macro SYSCALL_SKIP_RETURN_RESTART_GATE + /* Fix up invalid "-1 success" return state. */ + ldq $19, 72($sp) /* a3 */ + bne $19, 1f /* already error => skip fixup */ + + ldq $20, 0($sp) /* r0 */ + lda $21, -1($31) + cmpeq $20, $21, $22 + beq $22, 1f /* r0 != -1 => skip fixup */ + + + lda $20, ENOSYS($31) + stq $20, 0($sp) /* r0 = ENOSYS */ + lda $19, 1($31) + stq $19, 72($sp) /* a3 = 1 */ +1: + /* Restart gating: success is never restartable here. */ + ldq $19, 72($sp) /* a3 */ + beq $19, 3f /* success => not restartable */ + + ldq $20, 0($sp) /* r0 (positive errno if a3==1) */ + lda $21, ERESTARTSYS($31) + cmpeq $20, $21, $22 + bne $22, 2f + lda $21, ERESTARTNOINTR($31) + cmpeq $20, $21, $22 + bne $22, 2f + lda $21, ERESTARTNOHAND($31) + cmpeq $20, $21, $22 + bne $22, 2f + lda $21, ERESTART_RESTARTBLOCK($31) + cmpeq $20, $21, $22 + bne $22, 2f + +3: /* Not a restart code (or success) => restart NOT allowed. */ + addq $31, 1, $26 /* $26=1 => restart NOT allowed */ + mov 0, $18 + br 4f + +2: /* Restart allowed. */ + ldq $18, 16($sp) /* preserved syscall nr (regs->r2) */ + mov $31, $26 /* $26=0 => restart allowed */ + br 4f +4: +.endm + + /* * This defines the normal kernel pt-regs layout. * @@ -425,7 +484,7 @@ CFI_START_OSF_FRAME entDbg mov $sp, $16 jsr $31, do_entDbg CFI_END_OSF_FRAME entDbg - + /* * The system call entry point is special. Most importantly, it looks * like a function call to userspace as far as clobbered registers. We @@ -435,6 +494,17 @@ CFI_END_OSF_FRAME entDbg * So much for theory. We don't take advantage of this yet. * * Note that a0-a2 are not saved by PALcode as with the other entry points. + * + * Alpha syscall ABI uses: + * - r0 for return value + * - r19 ("a3") as error indicator (0=success, 1=error; r0 holds errno) + * + * For seccomp/ptrace/generic syscall helpers we track the syscall + * number separately: + * - regs->r1: current (mutable) syscall number (may be changed or set to -1) + * - regs->r2: original syscall number for restart/rollback + * + * On entry PAL provides the syscall number in r0; copy it into r1/r2. */ .align 4 @@ -447,6 +517,10 @@ CFI_END_OSF_FRAME entDbg .cfi_rel_offset $gp, 16 entSys: SAVE_ALL + ldq $1, 0($sp) /* syscall nr from saved r0 */ + stq $1, 8($sp) /* regs->r1 = shadow syscall nr */ + stq $1, 16($sp) /* regs->r2 = restart syscall nr */ + lda $8, 0x3fff bic $sp, $8, $8 lda $4, NR_syscalls($31) @@ -462,15 +536,19 @@ entSys: .cfi_rel_offset $17, SP_OFF+32 .cfi_rel_offset $18, SP_OFF+40 #ifdef CONFIG_AUDITSYSCALL - lda $6, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT + lda $6, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP and $3, $6, $3 bne $3, strace #else - blbs $3, strace /* check for SYSCALL_TRACE in disguise */ + lda $6, _TIF_SYSCALL_TRACE | _TIF_SECCOMP + and $3, $6, $3 + bne $3, strace #endif beq $4, 1f ldq $27, 0($5) -1: jsr $26, ($27), sys_ni_syscall +1: ldq $0, 8($sp) /* syscall nr shadow (regs->r1) */ + + jsr $26, ($27), sys_ni_syscall ldgp $gp, 0($26) blt $0, $syscall_error /* the call failed */ $ret_success: @@ -509,15 +587,8 @@ ret_to_kernel: .align 3 $syscall_error: - /* - * Some system calls (e.g., ptrace) can return arbitrary - * values which might normally be mistaken as error numbers. - * Those functions must zero $0 (v0) directly in the stack - * frame to indicate that a negative return value wasn't an - * error number.. - */ - ldq $18, 0($sp) /* old syscall nr (zero if success) */ - beq $18, $ret_success + /* Restart syscall nr comes from saved r2 (preserved even if r0 overwritten). */ + ldq $18, 16($sp) /* old syscall nr for restart */ ldq $19, 72($sp) /* .. and this a3 */ subq $31, $0, $0 /* with error in v0 */ @@ -581,6 +652,8 @@ strace: jsr $26, syscall_trace_enter /* returns the syscall number */ UNDO_SWITCH_STACK + stq $0, 8($sp) /* regs->r1 = shadow syscall nr */ + /* get the arguments back.. */ ldq $16, SP_OFF+24($sp) ldq $17, SP_OFF+32($sp) @@ -589,6 +662,11 @@ strace: ldq $20, 80($sp) ldq $21, 88($sp) + /* nr == -1: internal skip-dispatch or userspace syscall(-1)? */ + lda $6, -1($31) + cmpeq $0, $6, $6 + bne $6, $strace_skip_call /* nr == -1 => dispatch */ + /* get the system call pointer.. */ lda $1, NR_syscalls($31) lda $2, sys_call_table @@ -607,6 +685,8 @@ $strace_success: stq $31, 72($sp) /* a3=0 => no error */ stq $0, 0($sp) /* save return value */ +$strace_skip_call: + SYSCALL_SKIP_RETURN_RESTART_GATE DO_SWITCH_STACK jsr $26, syscall_trace_leave UNDO_SWITCH_STACK @@ -614,8 +694,7 @@ $strace_success: .align 3 $strace_error: - ldq $18, 0($sp) /* old syscall nr (zero if success) */ - beq $18, $strace_success + ldq $18, 16($sp) /* restart syscall nr */ ldq $19, 72($sp) /* .. and this a3 */ subq $31, $0, $0 /* with error in v0 */ @@ -634,7 +713,7 @@ $strace_error: mov $31, $26 /* tell "ret_from_sys_call" we can restart */ br ret_from_sys_call CFI_END_OSF_FRAME entSys - + /* * Save and restore the switch stack -- aka the balance of the user context. */ diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c index fde4c68e7a0b..0687760ea466 100644 --- a/arch/alpha/kernel/ptrace.c +++ b/arch/alpha/kernel/ptrace.c @@ -16,11 +16,14 @@ #include #include #include +#include +#include #include #include #include "proto.h" +#include #define DEBUG DBG_MEM #undef DEBUG @@ -312,6 +315,54 @@ long arch_ptrace(struct task_struct *child, long request, DBG(DBG_MEM, ("poke $%lu<-%#lx\n", addr, data)); ret = put_reg(child, addr, data); break; + case PTRACE_GETREGSET: + case PTRACE_SETREGSET: { + struct iovec __user *uiov = (struct iovec __user *)data; + struct iovec iov; + struct pt_regs *regs; + size_t len; + + /* Only support NT_PRSTATUS (general registers) for now. */ + if (addr != NT_PRSTATUS) { + ret = -EIO; + break; + } + + if (copy_from_user(&iov, uiov, sizeof(iov))) { + ret = -EFAULT; + break; + } + + regs = task_pt_regs(child); + len = min_t(size_t, iov.iov_len, sizeof(*regs)); + + if (request == PTRACE_GETREGSET) { + if (copy_to_user(iov.iov_base, regs, len)) { + ret = -EFAULT; + break; + } + } else { + /* + * Allow writing back regs. This is needed by the TRACE_syscall + * tests (they change PC/syscall nr/retval). + */ + if (copy_from_user(regs, iov.iov_base, len)) { + ret = -EFAULT; + break; + } + } + + /* Per API, update iov_len with amount transferred. */ + iov.iov_len = len; + if (copy_to_user(uiov, &iov, sizeof(iov))) { + ret = -EFAULT; + break; + } + + ret = 0; + break; + } + default: ret = ptrace_request(child, request, addr, data); break; @@ -321,15 +372,37 @@ long arch_ptrace(struct task_struct *child, long request, asmlinkage unsigned long syscall_trace_enter(void) { - unsigned long ret = 0; struct pt_regs *regs = current_pt_regs(); + if (test_thread_flag(TIF_SYSCALL_TRACE) && - ptrace_report_syscall_entry(current_pt_regs())) - ret = -1UL; - audit_syscall_entry(regs->r0, regs->r16, regs->r17, regs->r18, regs->r19); - return ret ?: current_pt_regs()->r0; + ptrace_report_syscall_entry(regs)) { + syscall_set_nr(current, regs, -1); + if (regs->r19 == 0 && regs->r0 == (unsigned long)-1) + syscall_set_return_value(current, regs, -ENOSYS, 0); + return -1UL; + } + + /* + * Do the secure computing after ptrace; failures should be fast. + * If this fails, seccomp may already have set up the return value + * (e.g. SECCOMP_RET_ERRNO / TRACE). + */ + if (secure_computing() == -1) { + if (regs->r19 == 0 && regs->r0 == (unsigned long)-1) + syscall_set_return_value(current, regs, -ENOSYS, 0); + syscall_set_nr(current, regs, -1); + return -1UL; + } + +#ifdef CONFIG_AUDITSYSCALL + audit_syscall_entry(syscall_get_nr(current, regs), + regs->r16, regs->r17, regs->r18, regs->r19); +#endif + return syscall_get_nr(current, regs); } + + asmlinkage void syscall_trace_leave(void) { diff --git a/arch/arc/net/bpf_jit_core.c b/arch/arc/net/bpf_jit_core.c index 1421eeced0f5..639a2736f029 100644 --- a/arch/arc/net/bpf_jit_core.c +++ b/arch/arc/net/bpf_jit_core.c @@ -79,7 +79,6 @@ struct arc_jit_data { * The JIT pertinent context that is used by different functions. * * prog: The current eBPF program being handled. - * orig_prog: The original eBPF program before any possible change. * jit: The JIT buffer and its length. * bpf_header: The JITed program header. "jit.buf" points inside it. * emit: If set, opcodes are written to memory; else, a dry-run. @@ -94,12 +93,10 @@ struct arc_jit_data { * need_extra_pass: A forecast if an "extra_pass" will occur. * is_extra_pass: Indicates if the current pass is an extra pass. * user_bpf_prog: True, if VM opcodes come from a real program. - * blinded: True if "constant blinding" step returned a new "prog". * success: Indicates if the whole JIT went OK. */ struct jit_context { struct bpf_prog *prog; - struct bpf_prog *orig_prog; struct jit_buffer jit; struct bpf_binary_header *bpf_header; bool emit; @@ -114,7 +111,6 @@ struct jit_context { bool need_extra_pass; bool is_extra_pass; bool user_bpf_prog; - bool blinded; bool success; }; @@ -161,13 +157,7 @@ static int jit_ctx_init(struct jit_context *ctx, struct bpf_prog *prog) { memset(ctx, 0, sizeof(*ctx)); - ctx->orig_prog = prog; - - /* If constant blinding was requested but failed, scram. */ - ctx->prog = bpf_jit_blind_constants(prog); - if (IS_ERR(ctx->prog)) - return PTR_ERR(ctx->prog); - ctx->blinded = (ctx->prog != ctx->orig_prog); + ctx->prog = prog; /* If the verifier doesn't zero-extend, then we have to do it. */ ctx->do_zext = !ctx->prog->aux->verifier_zext; @@ -214,14 +204,6 @@ static inline void maybe_free(struct jit_context *ctx, void **mem) */ static void jit_ctx_cleanup(struct jit_context *ctx) { - if (ctx->blinded) { - /* if all went well, release the orig_prog. */ - if (ctx->success) - bpf_jit_prog_release_other(ctx->prog, ctx->orig_prog); - else - bpf_jit_prog_release_other(ctx->orig_prog, ctx->prog); - } - maybe_free(ctx, (void **)&ctx->bpf2insn); maybe_free(ctx, (void **)&ctx->jit_data); @@ -229,12 +211,19 @@ static void jit_ctx_cleanup(struct jit_context *ctx) ctx->bpf2insn_valid = false; /* Freeing "bpf_header" is enough. "jit.buf" is a sub-array of it. */ - if (!ctx->success && ctx->bpf_header) { - bpf_jit_binary_free(ctx->bpf_header); - ctx->bpf_header = NULL; - ctx->jit.buf = NULL; - ctx->jit.index = 0; - ctx->jit.len = 0; + if (!ctx->success) { + if (ctx->bpf_header) { + bpf_jit_binary_free(ctx->bpf_header); + ctx->bpf_header = NULL; + ctx->jit.buf = NULL; + ctx->jit.index = 0; + ctx->jit.len = 0; + } + if (ctx->is_extra_pass) { + ctx->prog->bpf_func = NULL; + ctx->prog->jited = 0; + ctx->prog->jited_len = 0; + } } ctx->emit = false; @@ -1411,7 +1400,7 @@ static struct bpf_prog *do_extra_pass(struct bpf_prog *prog) * (re)locations involved that their addresses are not known * during the first run. */ -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) { vm_dump(prog); diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ec33376f8e2b..71fc5dd4123f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -42,6 +42,7 @@ config ARM select ARCH_SUPPORTS_CFI select ARCH_SUPPORTS_HUGETLBFS if ARM_LPAE select ARCH_SUPPORTS_PER_VMA_LOCK + select ARCH_SUPPORTS_RT select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_CMPXCHG_LOCKREF select ARCH_USE_MEMTEST diff --git a/arch/arm/boot/dts/arm/arm-realview-eb.dtsi b/arch/arm/boot/dts/arm/arm-realview-eb.dtsi index 16f784da5a55..ae370d4eb9d8 100644 --- a/arch/arm/boot/dts/arm/arm-realview-eb.dtsi +++ b/arch/arm/boot/dts/arm/arm-realview-eb.dtsi @@ -234,7 +234,7 @@ reg = <0x0c 0x04>; #clock-cells = <0>; lock-offset = <0x20>; - vco-offset = <0x0C>; + vco-offset = <0x0c>; clocks = <&xtal24mhz>; }; oscclk1: clock-controller@10 { diff --git a/arch/arm/boot/dts/arm/arm-realview-pb1176.dts b/arch/arm/boot/dts/arm/arm-realview-pb1176.dts index b9b10cbd65aa..a75d8b107fe1 100644 --- a/arch/arm/boot/dts/arm/arm-realview-pb1176.dts +++ b/arch/arm/boot/dts/arm/arm-realview-pb1176.dts @@ -260,7 +260,7 @@ reg = <0x0c 0x04>; #clock-cells = <0>; lock-offset = <0x20>; - vco-offset = <0x0C>; + vco-offset = <0x0c>; clocks = <&xtal24mhz>; }; oscclk1: clock-controller@10 { diff --git a/arch/arm/boot/dts/arm/arm-realview-pb11mp.dts b/arch/arm/boot/dts/arm/arm-realview-pb11mp.dts index db1b6793cd2c..59c78def83f2 100644 --- a/arch/arm/boot/dts/arm/arm-realview-pb11mp.dts +++ b/arch/arm/boot/dts/arm/arm-realview-pb11mp.dts @@ -343,7 +343,7 @@ reg = <0x0c 0x04>; #clock-cells = <0>; lock-offset = <0x20>; - vco-offset = <0x0C>; + vco-offset = <0x0c>; clocks = <&xtal24mhz>; }; oscclk1: clock-controller@10 { diff --git a/arch/arm/boot/dts/arm/arm-realview-pbx.dtsi b/arch/arm/boot/dts/arm/arm-realview-pbx.dtsi index e625403a9456..0d3d96ffa6e6 100644 --- a/arch/arm/boot/dts/arm/arm-realview-pbx.dtsi +++ b/arch/arm/boot/dts/arm/arm-realview-pbx.dtsi @@ -256,7 +256,7 @@ reg = <0x0c 0x04>; #clock-cells = <0>; lock-offset = <0x20>; - vco-offset = <0x0C>; + vco-offset = <0x0c>; clocks = <&xtal24mhz>; }; oscclk1: clock-controller@10 { diff --git a/arch/arm/boot/dts/aspeed/Makefile b/arch/arm/boot/dts/aspeed/Makefile index 20e5b64280a5..767f7c7652d5 100644 --- a/arch/arm/boot/dts/aspeed/Makefile +++ b/arch/arm/boot/dts/aspeed/Makefile @@ -12,9 +12,11 @@ dtb-$(CONFIG_ARCH_ASPEED) += \ aspeed-bmc-asrock-altrad8.dtb \ aspeed-bmc-asrock-e3c246d4i.dtb \ aspeed-bmc-asrock-e3c256d4i.dtb \ + aspeed-bmc-asrock-paul-ipmi-card.dtb \ aspeed-bmc-asrock-romed8hm3.dtb \ aspeed-bmc-asrock-spc621d8hm3.dtb \ aspeed-bmc-asrock-x570d4u.dtb \ + aspeed-bmc-asus-kommando-ipmi-card.dtb \ aspeed-bmc-asus-x4tf.dtb \ aspeed-bmc-bytedance-g220a.dtb \ aspeed-bmc-delta-ahe50dc.dtb \ diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-asrock-paul-ipmi-card.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-asrock-paul-ipmi-card.dts new file mode 100644 index 000000000000..f74f8fee9e1e --- /dev/null +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-asrock-paul-ipmi-card.dts @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright 2025 Anirudh Srinivasan + +/dts-v1/; + +#include "aspeed-g5.dtsi" +#include + +/{ + model = "ASRock Paul IPMI Card"; + compatible = "asrock,ast2500-paul-ipmi-card", "aspeed,ast2500"; + + aliases { + serial4 = &uart5; + }; + + chosen { + stdout-path = &uart5; + }; + + leds { + compatible = "gpio-leds"; + + led-fan-1 { + gpios = <&gpio ASPEED_GPIO(AA, 0) GPIO_ACTIVE_LOW>; + label = "fan1_red"; + default-state = "off"; + }; + + led-fan-2 { + gpios = <&gpio ASPEED_GPIO(AA, 1) GPIO_ACTIVE_LOW>; + label = "fan2_red"; + default-state = "off"; + }; + + led-fault { + gpios = <&gpio ASPEED_GPIO(Y, 3) GPIO_ACTIVE_LOW>; + label = "panic_red"; + panic-indicator; + default-state = "off"; + }; + + led-heartbeat { + gpios = <&gpio ASPEED_GPIO(H, 6) GPIO_ACTIVE_LOW>; + label = "heartbeat_green"; + linux,default-trigger = "timer"; + }; + }; + + memory@80000000 { + reg = <0x80000000 0x20000000>; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + video_engine_memory: video { + size = <0x02000000>; + alignment = <0x01000000>; + compatible = "shared-dma-pool"; + reusable; + }; + }; +}; + +&fmc { + status = "okay"; + flash@0 { + status = "okay"; + m25p,fast-read; + label = "bmc"; + spi-max-frequency = <50000000>; /* 50 MHz */ +#include "openbmc-flash-layout.dtsi" + }; +}; + +&gpio { + status = "okay"; + gpio-line-names = + /* A */ "", "", "", "", "", "", "", "", + /* B */ "", "", "", "", "", "", "", "", + /* C */ "", "", "", "", "", "", "", "", + /* D */ "", "BMC_PWRBTN", "", "BMC_RESETCON", "", "", "", "", + /* E */ "", "", "", "", "", "", "", "", + /* F */ "", "", "", "", "", "", "", "", + /* G */ "", "", "", "", "", "", "", "", + /* H */ "", "", "", "", "", "", "BMC_LED1", "", + /* I */ "", "", "", "", "", "", "", "", + /* J */ "", "", "", "", "", "", "", "", + /* K */ "", "", "", "", "", "", "", "", + /* L */ "", "", "", "", "", "", "", "", + /* M */ "", "", "", "", "", "", "", "", + /* N */ "", "", "", "", "", "", "", "", + /* O */ "", "", "", "", "", "", "", "", + /* P */ "", "", "", "", "", "", "", "", + /* Q */ "", "", "", "", "", "", "", "", + /* R */ "", "", "", "", "", "", "", "", + /* S */ "", "", "", "", "", "", "", "", + /* T */ "", "", "", "", "", "", "", "", + /* U */ "", "", "", "", "", "", "", "", + /* V */ "", "", "", "", "", "", "", "", + /* W */ "", "", "", "", "", "", "", "", + /* X */ "", "", "", "", "", "PCIE_STNDBY", "", "", + /* Y */ "", "", "", "SYSTEM_ERR_LED", "", "", "", "", + /* Z */ "", "", "", "", "", "", "", "", + /* AA */ "FAN_1_LED", "FAN_2_LED", "", "", "", "", "", "", + /* AB */ "", "", "", "", "", "", "", "", + /* AC */ "", "", "", "", "", "", "", ""; +}; + +&mac0 { + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rgmii1_default &pinctrl_mdio1_default>; +}; + +&uart5 { + status = "okay"; +}; + +&vhub { + status = "okay"; +}; + +&video { + status = "okay"; + memory-region = <&video_engine_memory>; +}; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-asus-kommando-ipmi-card.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-asus-kommando-ipmi-card.dts new file mode 100644 index 000000000000..ab7ad320067c --- /dev/null +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-asus-kommando-ipmi-card.dts @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright 2025 Anirudh Srinivasan + +/dts-v1/; + +#include "aspeed-g6.dtsi" +#include "aspeed-g6-pinctrl.dtsi" +#include + +/ { + model = "Asus Kommando IPMI Expansion Card"; + compatible = "asus,ast2600-kommando-ipmi-card", "aspeed,ast2600"; + + aliases { + serial4 = &uart5; + }; + + chosen { + stdout-path = "serial4:115200n8"; + }; + + leds { + compatible = "gpio-leds"; + + led-fault { + gpios = <&gpio1 ASPEED_GPIO(C, 5) GPIO_ACTIVE_HIGH>; + label = "panic_amber"; + panic-indicator; + default-state = "off"; + }; + + led-heartbeat { + gpios = <&gpio0 ASPEED_GPIO(A, 7) GPIO_ACTIVE_LOW>; + label = "heartbeat_green"; + linux,default-trigger = "timer"; + }; + + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x40000000>; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + video_engine_memory: video { + size = <0x04000000>; + alignment = <0x01000000>; + compatible = "shared-dma-pool"; + reusable; + }; + }; +}; + +&fmc { + status = "okay"; + + flash@0 { + status = "okay"; + m25p,fast-read; + label = "bmc"; + spi-max-frequency = <50000000>; +#include "openbmc-flash-layout-64.dtsi" + }; +}; + +&gpio0 { + gpio-line-names = + /*A0 0*/ "", "", "", "", "", "", "", "BMC_HBLED", + /*B0 8*/ "", "", "", "", "", "", "", "", + /*C0 16*/ "", "", "", "", "", "", "", "", + /*D0 24*/ "", "", "", "", "", "", "", "", + /*E0 32*/ "", "", "", "", "", "", "", "", + /*F0 40*/ "", "", "", "", "", "", "", "", + /*G0 48*/ "", "", "", "", "", "", "", "", + /*H0 56*/ "", "", "", "", "", "", "", "", + /*I0 64*/ "", "", "", "BMC_RSTBTN", "", "", "", "", + /*J0 72*/ "", "", "", "", "", "", "", "", + /*K0 80*/ "", "", "", "", "", "", "", "", + /*L0 88*/ "", "", "", "", "", "", "", "", + /*M0 96*/ "", "", "", "", "", "", "", "", + /*N0 104*/ "", "", "", "", "", "", "", "", + /*O0 112*/ "", "", "", "", "", "", "", "", + /*P0 120*/ "", "", "", "", "", "", "", "", + /*Q0 128*/ "", "", "", "", "", "", "", "", + /*R0 136*/ "", "", "", "", "", "", "", "", + /*S0 144*/ "", "", "", "", "", "", "", "", + /*T0 152*/ "", "", "", "", "", "", "", "", + /*U0 160*/ "", "", "", "", "", "", "", "", + /*V0 168*/ "", "", "", "", "BMC_PWRBTN", "", "MB_S0_DETECT", "", + /*W0 176*/ "", "", "", "", "", "", "", "", + /*X0 184*/ "", "", "", "", "", "", "", "", + /*Y0 192*/ "", "", "", "", "", "", "", "", + /*Z0 200*/ "", "", "", "", "", "", "", ""; +}; + +&gpio1 { + gpio-line-names = + /*18A0 0*/ "","","","","","","","", + /*18B0 8*/ "","","","","","","","", + /*18C0 16*/ "","","","","","BMC_MLED","","", + /*18D0 24*/ "","","","","","","","", + /*18E0 32*/ "","","","","","","",""; +}; + +&vhub { + status = "okay"; +}; + +&video { + status = "okay"; + memory-region = <&video_engine_memory>; +}; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-anacapa.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-anacapa.dts index 221af858cb6b..2cb7bd128d24 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-anacapa.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-anacapa.dts @@ -300,6 +300,12 @@ &i2c0 { status = "okay"; + eeprom@50 { + compatible = "atmel,24c2048"; + reg = <0x50>; + pagesize = <128>; + }; + i2c-mux@70 { compatible = "nxp,pca9546"; reg = <0x70>; @@ -334,6 +340,12 @@ &i2c1 { status = "okay"; + eeprom@50 { + compatible = "atmel,24c2048"; + reg = <0x50>; + pagesize = <128>; + }; + i2c-mux@70 { compatible = "nxp,pca9546"; reg = <0x70>; @@ -802,6 +814,16 @@ reg = <7>; #address-cells = <1>; #size-cells = <0>; + + nfc@28 { + compatible = "nxp,nxp-nci-i2c"; + reg = <0x28>; + + interrupt-parent = <&sgpiom0>; + interrupts = <156 IRQ_TYPE_LEVEL_HIGH>; + + enable-gpios = <&sgpiom0 241 GPIO_ACTIVE_HIGH>; + }; }; }; }; diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-msx4-bmc.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-msx4-bmc.dts index 44f95a3986cb..a12d4164de4a 100644 --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-msx4-bmc.dts +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-msx4-bmc.dts @@ -67,6 +67,7 @@ spi-tx-bus-width = <1>; spi-rx-bus-width = <1>; status = "okay"; + #include "openbmc-flash-layout-128-alt.dtsi" }; }; diff --git a/arch/arm/boot/dts/aspeed/openbmc-flash-layout-128-alt.dtsi b/arch/arm/boot/dts/aspeed/openbmc-flash-layout-128-alt.dtsi new file mode 100644 index 000000000000..08ce2eab472c --- /dev/null +++ b/arch/arm/boot/dts/aspeed/openbmc-flash-layout-128-alt.dtsi @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ + +partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + u-boot@0 { + reg = <0x0 0xe0000>; // 896KB + label = "alt-u-boot"; + }; + + u-boot-env@e0000 { + reg = <0xe0000 0x20000>; // 128KB + label = "alt-u-boot-env"; + }; + + kernel@100000 { + reg = <0x100000 0x900000>; // 9MB + label = "alt-kernel"; + }; + + rofs@a00000 { + reg = <0xa00000 0x5600000>; // 86MB + label = "alt-rofs"; + }; + + rwfs@6000000 { + reg = <0x6000000 0x2000000>; // 32MB + label = "alt-rwfs"; + }; +}; diff --git a/arch/arm/boot/dts/broadcom/bcm-ns.dtsi b/arch/arm/boot/dts/broadcom/bcm-ns.dtsi index d0d5f7e52a91..392a25713669 100644 --- a/arch/arm/boot/dts/broadcom/bcm-ns.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm-ns.dtsi @@ -95,7 +95,10 @@ axi@18000000 { compatible = "brcm,bus-axi"; reg = <0x18000000 0x1000>; - ranges = <0x00000000 0x18000000 0x00100000>; + ranges = <0x00000000 0x18000000 0x00100000>, + <0x08000000 0x08000000 0x08000000>, + <0x20000000 0x20000000 0x08000000>, + <0x28000000 0x28000000 0x08000000>; #address-cells = <1>; #size-cells = <1>; @@ -182,24 +185,75 @@ }; pcie0: pcie@12000 { + compatible = "brcm,iproc-pcie"; reg = <0x00012000 0x1000>; + ranges = <0x82000000 0 0x08000000 0x08000000 0 0x08000000>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>; + bus-range = <0x00 0xff>; + device_type = "pci"; + #interrupt-cells = <1>; #address-cells = <3>; #size-cells = <2>; + + pcie_bridge0: pcie@0 { + device_type = "pci"; + reg = <0x0000 0 0 0 0>; + bus-range = <0x00 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; }; pcie1: pcie@13000 { + compatible = "brcm,iproc-pcie"; reg = <0x00013000 0x1000>; + ranges = <0x82000000 0 0x20000000 0x20000000 0 0x08000000>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>; + bus-range = <0x00 0xff>; + device_type = "pci"; + #interrupt-cells = <1>; #address-cells = <3>; #size-cells = <2>; + + pcie_bridge1: pcie@0 { + device_type = "pci"; + reg = <0x0000 0 0 0 0>; + bus-range = <0x00 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; }; pcie2: pcie@14000 { + compatible = "brcm,iproc-pcie"; reg = <0x00014000 0x1000>; + ranges = <0x82000000 0 0x28000000 0x28000000 0 0x08000000>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>; + bus-range = <0x00 0xff>; + device_type = "pci"; + #interrupt-cells = <1>; #address-cells = <3>; #size-cells = <2>; + + pcie_bridge2: pcie@0 { + device_type = "pci"; + reg = <0x0000 0 0 0 0>; + bus-range = <0x00 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; }; usb2: usb2@21000 { @@ -479,7 +533,7 @@ }; nand_controller: nand-controller@18028000 { - compatible = "brcm,nand-iproc", "brcm,brcmnand-v6.1", "brcm,brcmnand"; + compatible = "brcm,nand-iproc", "brcm,brcmnand-v6.1"; reg = <0x18028000 0x600>, <0x1811a408 0x600>, <0x18028f00 0x20>; reg-names = "nand", "iproc-idm", "iproc-ext"; interrupts = ; diff --git a/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi b/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi index 9261b67dbee1..1e76b290510d 100644 --- a/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi @@ -141,9 +141,10 @@ interrupts = <1 10>; }; - vc4: gpu { - compatible = "brcm,bcm2835-vc4"; - }; + }; + + vc4: gpu { + compatible = "brcm,bcm2835-vc4"; }; }; diff --git a/arch/arm/boot/dts/broadcom/bcm2835-rpi.dtsi b/arch/arm/boot/dts/broadcom/bcm2835-rpi.dtsi index e9bf41b9f5c1..46c91468f4c5 100644 --- a/arch/arm/boot/dts/broadcom/bcm2835-rpi.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm2835-rpi.dtsi @@ -1,8 +1,8 @@ #include / { - soc { - firmware: firmware { + firmware { + firmware: rpi-firmware { compatible = "raspberrypi,bcm2835-firmware", "simple-mfd"; mboxes = <&mailbox>; @@ -10,14 +10,16 @@ compatible = "raspberrypi,firmware-clocks"; #clock-cells = <1>; }; - }; - power: power { - compatible = "raspberrypi,bcm2835-power"; - firmware = <&firmware>; - #power-domain-cells = <1>; + power: power { + compatible = "raspberrypi,bcm2835-power"; + firmware = <&firmware>; + #power-domain-cells = <1>; + }; }; + }; + soc { vchiq: mailbox@7e00b840 { compatible = "brcm,bcm2835-vchiq"; reg = <0x7e00b840 0x3c>; diff --git a/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts b/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts index 2ba5adf2b7e7..5bbc2ba0f959 100644 --- a/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts +++ b/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts @@ -5,6 +5,8 @@ /dts-v1/; +#include + #include "bcm4709.dtsi" #include "bcm5301x-nand-cs0-bch8.dtsi" @@ -25,6 +27,10 @@ nvram@1c080000 { compatible = "brcm,nvram"; reg = <0x1c080000 0x180000>; + + et2macaddr: et2macaddr { + #nvmem-cell-cells = <1>; + }; }; gpio-keys { @@ -36,18 +42,69 @@ gpios = <&chipcommon 3 GPIO_ACTIVE_LOW>; }; + button-rfkill { + label = "WiFi"; + linux,code = ; + gpios = <&chipcommon 4 GPIO_ACTIVE_LOW>; + }; + button-restart { label = "Reset"; linux,code = ; gpios = <&chipcommon 17 GPIO_ACTIVE_LOW>; }; }; + + leds { + compatible = "gpio-leds"; + + led-0 { + color = ; + function = LED_FUNCTION_WLAN; + gpios = <&chipcommon 0 GPIO_ACTIVE_LOW>; + linux,default-trigger = "rfkill-none"; + }; + + led-1 { + color = ; + function = LED_FUNCTION_USB; + function-enumerator = <2>; + gpios = <&chipcommon 6 GPIO_ACTIVE_LOW>; + linux,default-trigger = "usbport"; + trigger-sources = <&ohci_port2>, <&ehci_port2>; + }; + + led-2 { + color = ; + function = LED_FUNCTION_USB; + function-enumerator = <3>; + gpios = <&chipcommon 7 GPIO_ACTIVE_LOW>; + linux,default-trigger = "usbport"; + trigger-sources = <&ohci_port1>, <&ehci_port1>, + <&xhci_port1>; + }; + + led-3 { + color = ; + function = LED_FUNCTION_POWER; + gpios = <&chipcommon 8 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "default-on"; + }; + }; }; &usb3_phy { status = "okay"; }; +&usb2 { + vcc-gpios = <&chipcommon 13 GPIO_ACTIVE_HIGH>; +}; + +&usb3 { + vcc-gpios = <&chipcommon 14 GPIO_ACTIVE_HIGH>; +}; + &srab { status = "okay"; @@ -70,6 +127,9 @@ port@4 { label = "wan"; + + nvmem-cells = <&et2macaddr 1>; + nvmem-cell-names = "mac-address"; }; port@5 { @@ -85,3 +145,43 @@ }; }; }; + +&nandcs { + partitions { + compatible = "linksys,ns-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "boot"; + reg = <0x0000000 0x0080000>; + read-only; + }; + + partition@80000 { + label = "nvram"; + reg = <0x080000 0x0100000>; + }; + + partition@180000 { + label = "devinfo"; + reg = <0x0180000 0x080000>; + read-only; + }; + + partition@200000 { + reg = <0x0200000 0x02800000>; + compatible = "linksys,ns-firmware", "brcm,trx"; + }; + + partition@2a00000 { + reg = <0x02a00000 0x02800000>; + compatible = "linksys,ns-firmware", "brcm,trx"; + }; + + partition@5200000 { + label = "system"; + reg = <0x05200000 0x02e00000>; + }; + }; +}; diff --git a/arch/arm/boot/dts/broadcom/bcm4709-netgear-r8000.dts b/arch/arm/boot/dts/broadcom/bcm4709-netgear-r8000.dts index 127ca8741220..e85693fba16a 100644 --- a/arch/arm/boot/dts/broadcom/bcm4709-netgear-r8000.dts +++ b/arch/arm/boot/dts/broadcom/bcm4709-netgear-r8000.dts @@ -126,66 +126,52 @@ }; }; -&pcie0 { - #address-cells = <3>; - #size-cells = <2>; - - bridge@0,0,0 { +&pcie_bridge0 { + wifi@0,0 { + compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; reg = <0x0000 0 0 0 0>; - - #address-cells = <3>; - #size-cells = <2>; - - wifi@0,1,0 { - compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; - reg = <0x0000 0 0 0 0>; - ieee80211-freq-limit = <5735000 5835000>; - brcm,ccode-map = "JP-JP-78", "US-Q2-86"; - }; + ieee80211-freq-limit = <5735000 5835000>; + brcm,ccode-map = "JP-JP-78", "US-Q2-86"; }; }; -&pcie1 { - #address-cells = <3>; - #size-cells = <2>; - - bridge@1,0,0 { +&pcie_bridge1 { + pcie@0,0 { + device_type = "pci"; reg = <0x0000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + ranges; - bridge@1,1,0 { - reg = <0x0000 0 0 0 0>; + pcie@1,0 { + device_type = "pci"; + reg = <0x800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; + ranges; - bridge@1,0 { - reg = <0x800 0 0 0 0>; - - #address-cells = <3>; - #size-cells = <2>; - - wifi@0,0 { - compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; - reg = <0x0000 0 0 0 0>; - brcm,ccode-map = "JP-JP-78", "US-Q2-86"; - }; + wifi@0,0 { + compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; + reg = <0x0000 0 0 0 0>; + brcm,ccode-map = "JP-JP-78", "US-Q2-86"; }; + }; - bridge@1,2,2 { - reg = <0x1000 0 0 0 0>; + pcie@2,0 { + device_type = "pci"; + reg = <0x1000 0 0 0 0>; - #address-cells = <3>; - #size-cells = <2>; + #address-cells = <3>; + #size-cells = <2>; + ranges; - wifi@1,4,0 { - compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; - reg = <0x0000 0 0 0 0>; - ieee80211-freq-limit = <5170000 5730000>; - brcm,ccode-map = "JP-JP-78", "US-Q2-86"; - }; + wifi@0,0 { + compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; + reg = <0x0000 0 0 0 0>; + ieee80211-freq-limit = <5170000 5730000>; + brcm,ccode-map = "JP-JP-78", "US-Q2-86"; }; }; }; diff --git a/arch/arm/boot/dts/broadcom/bcm47094-asus-rt-ac3100.dtsi b/arch/arm/boot/dts/broadcom/bcm47094-asus-rt-ac3100.dtsi index 2cfaaabc7a6a..2d2e7e581291 100644 --- a/arch/arm/boot/dts/broadcom/bcm47094-asus-rt-ac3100.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm47094-asus-rt-ac3100.dtsi @@ -18,6 +18,10 @@ nvram@1c080000 { compatible = "brcm,nvram"; reg = <0x1c080000 0x00180000>; + + et0macaddr: et0macaddr { + #nvmem-cell-cells = <1>; + }; }; gpio-keys { @@ -143,6 +147,9 @@ port@4 { label = "wan"; + + nvmem-cells = <&et0macaddr 1>; + nvmem-cell-names = "mac-address"; }; port@5 { diff --git a/arch/arm/boot/dts/broadcom/bcm47094-asus-rt-ac5300.dts b/arch/arm/boot/dts/broadcom/bcm47094-asus-rt-ac5300.dts index 01ec8c03686a..8bf623c67de0 100644 --- a/arch/arm/boot/dts/broadcom/bcm47094-asus-rt-ac5300.dts +++ b/arch/arm/boot/dts/broadcom/bcm47094-asus-rt-ac5300.dts @@ -127,6 +127,9 @@ ports { port@0 { label = "wan"; + + nvmem-cells = <&et1macaddr 1>; + nvmem-cell-names = "mac-address"; }; port@1 { diff --git a/arch/arm/boot/dts/broadcom/bcm47094-linksys-panamera.dts b/arch/arm/boot/dts/broadcom/bcm47094-linksys-panamera.dts index 2b5c80d835e9..74161b76008a 100644 --- a/arch/arm/boot/dts/broadcom/bcm47094-linksys-panamera.dts +++ b/arch/arm/boot/dts/broadcom/bcm47094-linksys-panamera.dts @@ -25,6 +25,10 @@ nvram@1c080000 { compatible = "brcm,nvram"; reg = <0x1c080000 0x100000>; + + et2macaddr: et2macaddr { + #nvmem-cell-cells = <1>; + }; }; gpio-keys { @@ -230,6 +234,9 @@ port@4 { label = "wan"; + + nvmem-cells = <&et2macaddr 1>; + nvmem-cell-names = "mac-address"; }; port@5 { diff --git a/arch/arm/boot/dts/broadcom/bcm47094-luxul-xap-1610.dts b/arch/arm/boot/dts/broadcom/bcm47094-luxul-xap-1610.dts index badafa024d24..3a33705c2969 100644 --- a/arch/arm/boot/dts/broadcom/bcm47094-luxul-xap-1610.dts +++ b/arch/arm/boot/dts/broadcom/bcm47094-luxul-xap-1610.dts @@ -65,39 +65,19 @@ }; -&pcie0 { - #address-cells = <3>; - #size-cells = <2>; - - bridge@0,0 { +&pcie_bridge0 { + wifi@0,0 { + compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; reg = <0x0000 0 0 0 0>; - - #address-cells = <3>; - #size-cells = <2>; - - wifi@0,0 { - compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; - reg = <0x0000 0 0 0 0>; - brcm,ccode-map = "AU-AU-920", "CA-CA-892", "GB-DE-964", "NZ-AU-920", "US-US-825"; - }; + brcm,ccode-map = "AU-AU-920", "CA-CA-892", "GB-DE-964", "NZ-AU-920", "US-US-825"; }; }; -&pcie1 { - #address-cells = <3>; - #size-cells = <2>; - - bridge@0,0 { +&pcie_bridge1 { + wifi@0,0 { + compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; reg = <0x0000 0 0 0 0>; - - #address-cells = <3>; - #size-cells = <2>; - - wifi@0,0 { - compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; - reg = <0x0000 0 0 0 0>; - brcm,ccode-map = "AU-AU-920", "CA-CA-892", "GB-DE-964", "NZ-AU-920", "US-US-825"; - }; + brcm,ccode-map = "AU-AU-920", "CA-CA-892", "GB-DE-964", "NZ-AU-920", "US-US-825"; }; }; diff --git a/arch/arm/boot/dts/broadcom/bcm47094-luxul-xwr-3150-v1.dts b/arch/arm/boot/dts/broadcom/bcm47094-luxul-xwr-3150-v1.dts index 83c429afc297..8e487f60a2cc 100644 --- a/arch/arm/boot/dts/broadcom/bcm47094-luxul-xwr-3150-v1.dts +++ b/arch/arm/boot/dts/broadcom/bcm47094-luxul-xwr-3150-v1.dts @@ -81,39 +81,19 @@ nvmem-cell-names = "mac-address"; }; -&pcie0 { - #address-cells = <3>; - #size-cells = <2>; - - bridge@0,0 { +&pcie_bridge0 { + wifi@0,0 { + compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; reg = <0x0000 0 0 0 0>; - - #address-cells = <3>; - #size-cells = <2>; - - wifi@0,0 { - compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; - reg = <0x0000 0 0 0 0>; - brcm,ccode-map = "AU-AU-953", "CA-CA-946", "GB-E0-846", "NZ-AU-953", "US-Q2-930"; - }; + brcm,ccode-map = "AU-AU-953", "CA-CA-946", "GB-E0-846", "NZ-AU-953", "US-Q2-930"; }; }; -&pcie1 { - #address-cells = <3>; - #size-cells = <2>; - - bridge@0,0 { +&pcie_bridge1 { + wifi@0,0 { + compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; reg = <0x0000 0 0 0 0>; - - #address-cells = <3>; - #size-cells = <2>; - - wifi@0,0 { - compatible = "brcm,bcm4366-fmac", "brcm,bcm4329-fmac"; - reg = <0x0000 0 0 0 0>; - brcm,ccode-map = "AU-AU-953", "CA-CA-946", "GB-E0-846", "NZ-AU-953", "US-Q2-930"; - }; + brcm,ccode-map = "AU-AU-953", "CA-CA-946", "GB-E0-846", "NZ-AU-953", "US-Q2-930"; }; }; diff --git a/arch/arm/boot/dts/broadcom/bcm63138.dtsi b/arch/arm/boot/dts/broadcom/bcm63138.dtsi index 4ec568586b14..5b084d61edd7 100644 --- a/arch/arm/boot/dts/broadcom/bcm63138.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm63138.dtsi @@ -312,12 +312,21 @@ reg = <0x8000 0x50>; }; + i2c0: i2c@3e00 { + compatible = "brcm,brcmper-i2c"; + reg = <0x3e00 0x60>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + pl081_dma: dma-controller@d000 { compatible = "arm,pl081", "arm,primecell"; // The magic B105F00D info is missing arm,primecell-periphid = <0x00041081>; reg = <0xd000 0x1000>; - interrupts = ; + interrupts = ; memcpy-burst-size = <256>; memcpy-bus-width = <32>; clocks = <&periph_clk>; diff --git a/arch/arm/boot/dts/broadcom/bcm63148.dtsi b/arch/arm/boot/dts/broadcom/bcm63148.dtsi index e071cddb28fc..b2ddc2f583e1 100644 --- a/arch/arm/boot/dts/broadcom/bcm63148.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm63148.dtsi @@ -97,7 +97,7 @@ compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; - ranges = <0 0xfffe8000 0x8000>; + ranges = <0 0xfffe8000 0x20000>; /* GPIOs 0 .. 31 */ gpio0: gpio@100 { @@ -197,5 +197,14 @@ reg = <0>; }; }; + + i2c0: i2c@3e00 { + compatible = "brcm,brcmper-i2c"; + reg = <0x3e00 0x60>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; }; }; diff --git a/arch/arm/boot/dts/broadcom/bcm6846.dtsi b/arch/arm/boot/dts/broadcom/bcm6846.dtsi index f5591a45d2e4..47f177323c18 100644 --- a/arch/arm/boot/dts/broadcom/bcm6846.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm6846.dtsi @@ -242,6 +242,15 @@ status = "disabled"; }; + i2c0: i2c@2100 { + compatible = "brcm,brcmper-i2c"; + reg = <0x2100 0x60>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + pl081_dma: dma-controller@59000 { compatible = "arm,pl081", "arm,primecell"; // The magic B105F00D info is missing diff --git a/arch/arm/boot/dts/broadcom/bcm6855.dtsi b/arch/arm/boot/dts/broadcom/bcm6855.dtsi index a88c3f0fbcb0..b3c4cd24ce53 100644 --- a/arch/arm/boot/dts/broadcom/bcm6855.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm6855.dtsi @@ -240,6 +240,15 @@ }; }; + i2c0: i2c@2100 { + compatible = "brcm,brcmper-i2c"; + reg = <0x2100 0x60>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + leds: led-controller@3000 { #address-cells = <1>; #size-cells = <0>; @@ -278,5 +287,14 @@ clock-names = "uartclk", "apb_pclk"; status = "disabled"; }; + + i2c1: i2c@5a800 { + compatible = "brcm,brcmper-i2c"; + reg = <0x5a800 0x60>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; }; }; diff --git a/arch/arm/boot/dts/broadcom/bcm6878.dtsi b/arch/arm/boot/dts/broadcom/bcm6878.dtsi index dd837bf69390..e7fb45ae1d49 100644 --- a/arch/arm/boot/dts/broadcom/bcm6878.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm6878.dtsi @@ -239,12 +239,21 @@ }; }; + i2c0: i2c@2100 { + compatible = "brcm,brcmper-i2c"; + reg = <0x2100 0x60>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + pl081_dma: dma-controller@11000 { compatible = "arm,pl081", "arm,primecell"; // The magic B105F00D info is missing arm,primecell-periphid = <0x00041081>; reg = <0x11000 0x1000>; - interrupts = ; + interrupts = ; memcpy-burst-size = <256>; memcpy-bus-width = <32>; clocks = <&periph_clk>; diff --git a/arch/arm/boot/dts/marvell/armada-380.dtsi b/arch/arm/boot/dts/marvell/armada-380.dtsi deleted file mode 100644 index e94f22b0e9b5..000000000000 --- a/arch/arm/boot/dts/marvell/armada-380.dtsi +++ /dev/null @@ -1,148 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR MIT) -/* - * Device Tree Include file for Marvell Armada 380 SoC. - * - * Copyright (C) 2014 Marvell - * - * Lior Amsalem - * Gregory CLEMENT - * Thomas Petazzoni - */ - -#include "armada-38x.dtsi" - -/ { - model = "Marvell Armada 380 family SoC"; - compatible = "marvell,armada380"; - - cpus { - #address-cells = <1>; - #size-cells = <0>; - enable-method = "marvell,armada-380-smp"; - - cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a9"; - reg = <0>; - }; - }; - - soc { - internal-regs { - pinctrl@18000 { - compatible = "marvell,mv88f6810-pinctrl"; - }; - }; - - pcie { - compatible = "marvell,armada-370-pcie"; - status = "disabled"; - device_type = "pci"; - - #address-cells = <3>; - #size-cells = <2>; - - msi-parent = <&mpic>; - bus-range = <0x00 0xff>; - - ranges = - <0x82000000 0 0x80000 MBUS_ID(0xf0, 0x01) 0x80000 0 0x00002000 - 0x82000000 0 0x40000 MBUS_ID(0xf0, 0x01) 0x40000 0 0x00002000 - 0x82000000 0 0x44000 MBUS_ID(0xf0, 0x01) 0x44000 0 0x00002000 - 0x82000000 0 0x48000 MBUS_ID(0xf0, 0x01) 0x48000 0 0x00002000 - 0x82000000 0x1 0 MBUS_ID(0x08, 0xe8) 0 1 0 /* Port 0 MEM */ - 0x81000000 0x1 0 MBUS_ID(0x08, 0xe0) 0 1 0 /* Port 0 IO */ - 0x82000000 0x2 0 MBUS_ID(0x04, 0xe8) 0 1 0 /* Port 1 MEM */ - 0x81000000 0x2 0 MBUS_ID(0x04, 0xe0) 0 1 0 /* Port 1 IO */ - 0x82000000 0x3 0 MBUS_ID(0x04, 0xd8) 0 1 0 /* Port 2 MEM */ - 0x81000000 0x3 0 MBUS_ID(0x04, 0xd0) 0 1 0 /* Port 2 IO */>; - - /* x1 port */ - pcie@1,0 { - device_type = "pci"; - assigned-addresses = <0x82000800 0 0x80000 0 0x2000>; - reg = <0x0800 0 0 0 0>; - #address-cells = <3>; - #size-cells = <2>; - interrupt-names = "intx"; - interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; - #interrupt-cells = <1>; - ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 - 0x81000000 0 0 0x81000000 0x1 0 1 0>; - bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 7>; - interrupt-map = <0 0 0 1 &pcie1_intc 0>, - <0 0 0 2 &pcie1_intc 1>, - <0 0 0 3 &pcie1_intc 2>, - <0 0 0 4 &pcie1_intc 3>; - marvell,pcie-port = <0>; - marvell,pcie-lane = <0>; - clocks = <&gateclk 8>; - status = "disabled"; - - pcie1_intc: interrupt-controller { - interrupt-controller; - #interrupt-cells = <1>; - }; - }; - - /* x1 port */ - pcie@2,0 { - device_type = "pci"; - assigned-addresses = <0x82001000 0 0x40000 0 0x2000>; - reg = <0x1000 0 0 0 0>; - #address-cells = <3>; - #size-cells = <2>; - interrupt-names = "intx"; - interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; - #interrupt-cells = <1>; - ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 - 0x81000000 0 0 0x81000000 0x2 0 1 0>; - bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 7>; - interrupt-map = <0 0 0 1 &pcie2_intc 0>, - <0 0 0 2 &pcie2_intc 1>, - <0 0 0 3 &pcie2_intc 2>, - <0 0 0 4 &pcie2_intc 3>; - marvell,pcie-port = <1>; - marvell,pcie-lane = <0>; - clocks = <&gateclk 5>; - status = "disabled"; - - pcie2_intc: interrupt-controller { - interrupt-controller; - #interrupt-cells = <1>; - }; - }; - - /* x1 port */ - pcie@3,0 { - device_type = "pci"; - assigned-addresses = <0x82001800 0 0x44000 0 0x2000>; - reg = <0x1800 0 0 0 0>; - #address-cells = <3>; - #size-cells = <2>; - interrupt-names = "intx"; - interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; - #interrupt-cells = <1>; - ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 - 0x81000000 0 0 0x81000000 0x3 0 1 0>; - bus-range = <0x00 0xff>; - interrupt-map-mask = <0 0 0 7>; - interrupt-map = <0 0 0 1 &pcie3_intc 0>, - <0 0 0 2 &pcie3_intc 1>, - <0 0 0 3 &pcie3_intc 2>, - <0 0 0 4 &pcie3_intc 3>; - marvell,pcie-port = <2>; - marvell,pcie-lane = <0>; - clocks = <&gateclk 6>; - status = "disabled"; - - pcie3_intc: interrupt-controller { - interrupt-controller; - #interrupt-cells = <1>; - }; - }; - }; - }; -}; diff --git a/arch/arm/boot/dts/mediatek/mt7623.dtsi b/arch/arm/boot/dts/mediatek/mt7623.dtsi index 4b1685b93989..71ac2b94c6ba 100644 --- a/arch/arm/boot/dts/mediatek/mt7623.dtsi +++ b/arch/arm/boot/dts/mediatek/mt7623.dtsi @@ -328,7 +328,7 @@ efuse: efuse@10206000 { compatible = "mediatek,mt7623-efuse", - "mediatek,mt8173-efuse"; + "mediatek,efuse"; reg = <0 0x10206000 0 0x1000>; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/boot/dts/microchip/sam9x60.dtsi b/arch/arm/boot/dts/microchip/sam9x60.dtsi index b075865e6a76..e708b3df4ccd 100644 --- a/arch/arm/boot/dts/microchip/sam9x60.dtsi +++ b/arch/arm/boot/dts/microchip/sam9x60.dtsi @@ -75,8 +75,6 @@ ranges; usb0: gadget@500000 { - #address-cells = <1>; - #size-cells = <0>; compatible = "microchip,sam9x60-udc"; reg = <0x00500000 0x100000 0xf803c000 0x400>; diff --git a/arch/arm/boot/dts/microchip/sama7d65.dtsi b/arch/arm/boot/dts/microchip/sama7d65.dtsi index e21556f46384..67253bbc08df 100644 --- a/arch/arm/boot/dts/microchip/sama7d65.dtsi +++ b/arch/arm/boot/dts/microchip/sama7d65.dtsi @@ -67,6 +67,11 @@ #size-cells = <1>; }; + pmu { + compatible = "arm,cortex-a7-pmu"; + interrupts = ; + }; + soc { compatible = "simple-bus"; ranges; @@ -278,6 +283,41 @@ status = "disabled"; }; + xlcdc: lcd-controller@e1400000 { + compatible = "microchip,sama7d65-xlcdc"; + reg = <0xe1400000 0x2000>; + interrupts = ; + clocks = <&pmc PMC_TYPE_PERIPHERAL 56>, <&pmc PMC_TYPE_GCK 56>, <&clk32k 1>; + clock-names = "periph_clk", "sys_clk", "slow_clk"; + status = "disabled"; + + display-controller { + compatible = "atmel,hlcdc-display-controller"; + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + pwm { + compatible = "atmel,hlcdc-pwm"; + #pwm-cells = <3>; + }; + }; + + lvdsc: lvds-controller@e1408000 { + compatible = "microchip,sama7d65-lvds", "microchip,sam9x75-lvds"; + reg = <0xe1408000 0x100>; + interrupts = ; + clocks = <&pmc PMC_TYPE_PERIPHERAL 104>; + clock-names = "pclk"; + status = "disabled"; + }; + aes: crypto@e1600000 { compatible = "microchip,sama7d65-aes", "atmel,at91sam9g46-aes"; reg = <0xe1600000 0x100>; diff --git a/arch/arm/boot/dts/nvidia/tegra114-tn7.dts b/arch/arm/boot/dts/nvidia/tegra114-tn7.dts index bfbdb345575a..75fbafb4a872 100644 --- a/arch/arm/boot/dts/nvidia/tegra114-tn7.dts +++ b/arch/arm/boot/dts/nvidia/tegra114-tn7.dts @@ -43,7 +43,9 @@ compatible = "lg,ld070wx3-sl01"; reg = <0>; - power-supply = <&vdd_lcd>; + vdd-supply = <&avdd_lcd>; + vcc-supply = <&dvdd_lcd>; + backlight = <&backlight>; }; }; @@ -101,11 +103,10 @@ regulator-boot-on; }; - smps6 { + avdd_lcd: smps6 { regulator-name = "va-lcd-hv"; - regulator-min-microvolt = <3000000>; - regulator-max-microvolt = <3000000>; - regulator-always-on; + regulator-min-microvolt = <3160000>; + regulator-max-microvolt = <3160000>; regulator-boot-on; }; @@ -325,7 +326,7 @@ regulator-boot-on; }; - vdd_lcd: regulator-lcd { + dvdd_lcd: regulator-lcd { compatible = "regulator-fixed"; regulator-name = "VD_LCD_1V8"; regulator-min-microvolt = <1800000>; diff --git a/arch/arm/boot/dts/nvidia/tegra114.dtsi b/arch/arm/boot/dts/nvidia/tegra114.dtsi index a98667641be2..7e8f90d33935 100644 --- a/arch/arm/boot/dts/nvidia/tegra114.dtsi +++ b/arch/arm/boot/dts/nvidia/tegra114.dtsi @@ -6,6 +6,7 @@ #include #include #include +#include / { compatible = "nvidia,tegra114"; @@ -258,6 +259,8 @@ reg = <0x60006000 0x1000>; #clock-cells = <1>; #reset-cells = <1>; + + nvidia,external-memory-controller = <&emc>; }; flow-controller@60007000 { @@ -311,6 +314,18 @@ reg = <0x6000c000 0x150>; }; + actmon: actmon@6000c800 { + compatible = "nvidia,tegra114-actmon"; + reg = <0x6000c800 0x400>; + interrupts = ; + clocks = <&tegra_car TEGRA114_CLK_ACTMON>, + <&tegra_car TEGRA114_CLK_EMC>; + clock-names = "actmon", "emc"; + resets = <&tegra_car TEGRA114_CLK_ACTMON>; + reset-names = "actmon"; + #cooling-cells = <2>; + }; + gpio: gpio@6000d000 { compatible = "nvidia,tegra114-gpio", "nvidia,tegra30-gpio"; reg = <0x6000d000 0x1000>; @@ -642,6 +657,16 @@ #iommu-cells = <1>; }; + emc: external-memory-controller@7001b000 { + compatible = "nvidia,tegra114-emc"; + reg = <0x7001b000 0x800>; + interrupts = ; + clocks = <&tegra_car TEGRA114_CLK_EMC>; + clock-names = "emc"; + + nvidia,memory-controller = <&mc>; + }; + hda@70030000 { compatible = "nvidia,tegra114-hda", "nvidia,tegra30-hda"; reg = <0x70030000 0x10000>; @@ -751,6 +776,46 @@ }; }; + soctherm: thermal-sensor@700e2000 { + compatible = "nvidia,tegra114-soctherm"; + reg = <0x700e2000 0x600>, /* SOC_THERM reg_base */ + <0x60006000 0x400>; /* CAR reg_base */ + reg-names = "soctherm-reg", "car-reg"; + interrupts = , + ; + interrupt-names = "thermal", "edp"; + clocks = <&tegra_car TEGRA114_CLK_TSENSOR>, + <&tegra_car TEGRA114_CLK_SOC_THERM>; + clock-names = "tsensor", "soctherm"; + resets = <&tegra_car 78>; + reset-names = "soctherm"; + + assigned-clocks = <&tegra_car TEGRA114_CLK_TSENSOR>, + <&tegra_car TEGRA114_CLK_SOC_THERM>; + assigned-clock-rates = <500000>, <51000000>; + + assigned-clock-parents = <&tegra_car TEGRA114_CLK_CLK_M>, + <&tegra_car TEGRA114_CLK_PLL_P>; + + #thermal-sensor-cells = <1>; + + throttle-cfgs { + throttle_heavy: heavy { + nvidia,priority = <100>; + nvidia,cpu-throt-percent = <80>; + nvidia,gpu-throt-level = ; + #cooling-cells = <2>; + }; + + throttle_light: light { + nvidia,priority = <80>; + nvidia,cpu-throt-percent = <50>; + nvidia,gpu-throt-level = ; + #cooling-cells = <2>; + }; + }; + }; + mipi: mipi@700e3000 { compatible = "nvidia,tegra114-mipi"; reg = <0x700e3000 0x100>; @@ -921,24 +986,28 @@ clock-names = "cpu_g", "cpu_lp", "pll_x", "pll_p", "dfll"; /* FIXME: what's the actual transition time? */ clock-latency = <300000>; + #cooling-cells = <2>; }; cpu1: cpu@1 { device_type = "cpu"; compatible = "arm,cortex-a15"; reg = <1>; + #cooling-cells = <2>; }; cpu2: cpu@2 { device_type = "cpu"; compatible = "arm,cortex-a15"; reg = <2>; + #cooling-cells = <2>; }; cpu3: cpu@3 { device_type = "cpu"; compatible = "arm,cortex-a15"; reg = <3>; + #cooling-cells = <2>; }; }; @@ -951,6 +1020,158 @@ interrupt-affinity = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>; }; + thermal-zones { + cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + + thermal-sensors = + <&soctherm TEGRA114_SOCTHERM_SENSOR_CPU>; + + trips { + cpu-shutdown-trip { + temperature = <102000>; + hysteresis = <0>; + type = "critical"; + }; + + cpu_throttle_trip: cpu-throttle-trip { + temperature = <100000>; + hysteresis = <1000>; + type = "hot"; + }; + + cpu_balanced_trip: cpu-balanced-trip { + temperature = <90000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu_throttle_trip>; + cooling-device = <&throttle_heavy 1 1>; + }; + + map1 { + trip = <&cpu_balanced_trip>; + cooling-device = <&throttle_light 1 1>; + }; + }; + }; + + mem-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + + thermal-sensors = + <&soctherm TEGRA114_SOCTHERM_SENSOR_MEM>; + + trips { + mem-shutdown-trip { + temperature = <102000>; + hysteresis = <0>; + type = "critical"; + }; + + mem_throttle_trip: mem-throttle-trip { + temperature = <100000>; + hysteresis = <1000>; + type = "hot"; + }; + + mem_balanced_trip: mem-balanced-trip { + temperature = <90000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + + cooling-maps { + /* + * There are currently no cooling maps, + * because there are no cooling devices. + */ + }; + }; + + gpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + + thermal-sensors = + <&soctherm TEGRA114_SOCTHERM_SENSOR_GPU>; + + trips { + gpu-shutdown-trip { + temperature = <102000>; + hysteresis = <0>; + type = "critical"; + }; + + gpu_throttle_trip: gpu-throttle-trip { + temperature = <100000>; + hysteresis = <1000>; + type = "hot"; + }; + + gpu_balanced_trip: gpu-balanced-trip { + temperature = <90000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&gpu_throttle_trip>; + cooling-device = <&throttle_heavy 1 1>; + }; + + map1 { + trip = <&gpu_balanced_trip>; + cooling-device = <&throttle_light 1 1>; + }; + }; + }; + + pllx-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + + thermal-sensors = + <&soctherm TEGRA114_SOCTHERM_SENSOR_PLLX>; + + trips { + pllx-shutdown-trip { + temperature = <102000>; + hysteresis = <0>; + type = "critical"; + }; + + pllx_throttle_trip: pllx-throttle-trip { + temperature = <100000>; + hysteresis = <1000>; + type = "hot"; + }; + + pllx_balanced_trip: pllx-balanced-trip { + temperature = <90000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + + cooling-maps { + /* + * There are currently no cooling maps, + * because there are no cooling devices. + */ + }; + }; + }; + timer { compatible = "arm,armv7-timer"; interrupts = diff --git a/arch/arm/boot/dts/nvidia/tegra20-paz00.dts b/arch/arm/boot/dts/nvidia/tegra20-paz00.dts index 1408e1e00759..d1093ad569e6 100644 --- a/arch/arm/boot/dts/nvidia/tegra20-paz00.dts +++ b/arch/arm/boot/dts/nvidia/tegra20-paz00.dts @@ -706,6 +706,14 @@ enable-active-high; }; + rfkill { + compatible = "rfkill-gpio"; + label = "wifi_rfkill"; + radio-type = "wlan"; + reset-gpios = <&gpio TEGRA_GPIO(D, 1) GPIO_ACTIVE_HIGH>; + shutdown-gpios = <&gpio TEGRA_GPIO(K, 5) GPIO_ACTIVE_HIGH>; + }; + sound { compatible = "nvidia,tegra-audio-alc5632-paz00", "nvidia,tegra-audio-alc5632"; diff --git a/arch/arm/boot/dts/nvidia/tegra30-asus-tf600t.dts b/arch/arm/boot/dts/nvidia/tegra30-asus-tf600t.dts index 5d9e23a43820..9296e7970ce4 100644 --- a/arch/arm/boot/dts/nvidia/tegra30-asus-tf600t.dts +++ b/arch/arm/boot/dts/nvidia/tegra30-asus-tf600t.dts @@ -62,8 +62,11 @@ pll-supply = <&vdd_1v8_vio>; vdd-supply = <&vdd_3v3_sys>; - nvidia,hpd-gpio = <&gpio TEGRA_GPIO(N, 7) GPIO_ACTIVE_HIGH>; - nvidia,ddc-i2c-bus = <&hdmi_ddc>; + port { + hdmi_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; }; }; @@ -2174,6 +2177,20 @@ clock-output-names = "pmic-oscillator"; }; + hdmi-connector { + compatible = "hdmi-connector"; + type = "d"; + + hpd-gpios = <&gpio TEGRA_GPIO(N, 7) GPIO_ACTIVE_HIGH>; + ddc-i2c-bus = <&hdmi_ddc>; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&hdmi_out>; + }; + }; + }; + cpus { cpu0: cpu@0 { cpu-supply = <&vdd_cpu>; diff --git a/arch/arm/boot/dts/nvidia/tegra30-lg-p880.dts b/arch/arm/boot/dts/nvidia/tegra30-lg-p880.dts index c6ef0a20c19f..cc14e6dca770 100644 --- a/arch/arm/boot/dts/nvidia/tegra30-lg-p880.dts +++ b/arch/arm/boot/dts/nvidia/tegra30-lg-p880.dts @@ -116,6 +116,29 @@ }; }; + spi@7000dc00 { + dsi@2 { + /* + * JDI 4.57" 720x1280 DX12D100VM0EAA MIPI DSI panel + */ + panel@1 { + compatible = "jdi,dx12d100vm0eaa", "renesas,r69328"; + reg = <1>; + + reset-gpios = <&gpio TEGRA_GPIO(W, 0) GPIO_ACTIVE_LOW>; + + vdd-supply = <&vcc_3v0_lcd>; + vddio-supply = <&iovcc_1v8_lcd>; + + port { + panel_input: endpoint { + remote-endpoint = <&bridge_output>; + }; + }; + }; + }; + }; + memory-controller@7000f000 { emc-timings-0 { /* SAMSUNG 1GB K4P8G304EB FGC1 533MHz */ diff --git a/arch/arm/boot/dts/nvidia/tegra30-lg-p895.dts b/arch/arm/boot/dts/nvidia/tegra30-lg-p895.dts index e32fafc7f5e0..414117fd4382 100644 --- a/arch/arm/boot/dts/nvidia/tegra30-lg-p895.dts +++ b/arch/arm/boot/dts/nvidia/tegra30-lg-p895.dts @@ -109,6 +109,39 @@ syna,clip-x-high = <1535>; syna,clip-y-high = <2047>; }; + + rmi4-f1a@1a { + reg = <0x1a>; + + linux,keycodes = ; + }; + }; + }; + + spi@7000dc00 { + dsi@2 { + /* + * HITACHI/KOE 5" 768x1024 TX13D100VM0EAA MIPI DSI panel + */ + panel@1 { + compatible = "koe,tx13d100vm0eaa", "renesas,r61307"; + reg = <1>; + + reset-gpios = <&gpio TEGRA_GPIO(W, 0) GPIO_ACTIVE_LOW>; + + renesas,gamma = <3>; + renesas,column-inversion; + renesas,contrast; + + vcc-supply = <&vcc_3v0_lcd>; + iovcc-supply = <&iovcc_1v8_lcd>; + + port { + panel_input: endpoint { + remote-endpoint = <&bridge_output>; + }; + }; + }; }; }; diff --git a/arch/arm/boot/dts/nvidia/tegra30-lg-x3.dtsi b/arch/arm/boot/dts/nvidia/tegra30-lg-x3.dtsi index 909260a5d0fb..768e201456d8 100644 --- a/arch/arm/boot/dts/nvidia/tegra30-lg-x3.dtsi +++ b/arch/arm/boot/dts/nvidia/tegra30-lg-x3.dtsi @@ -20,6 +20,8 @@ rtc0 = &pmic; rtc1 = "/rtc@7000e000"; + display0 = &lcd; + serial0 = &uartd; /* Console */ serial1 = &uartc; /* Bluetooth */ serial2 = &uartb; /* GPS */ @@ -71,6 +73,21 @@ }; }; + host1x@50000000 { + lcd: dc@54200000 { + rgb { + status = "okay"; + + port { + dpi_output: endpoint { + remote-endpoint = <&bridge_input>; + bus-width = <24>; + }; + }; + }; + }; + }; + vde@6001a000 { assigned-clocks = <&tegra_car TEGRA30_CLK_VDE>; assigned-clock-parents = <&tegra_car TEGRA30_CLK_PLL_P>; @@ -1053,6 +1070,38 @@ syna,clip-y-low = <0>; }; }; + + max14526: muic@44 { + compatible = "maxim,max14526"; + reg = <0x44>; + + interrupt-parent = <&gpio>; + interrupts = ; + + muic_con: connector { + compatible = "usb-b-connector"; + label = "micro-USB"; + type = "micro"; + }; + + port { + #address-cells = <1>; + #size-cells = <0>; + + muic_to_charger: endpoint@0 { + reg = <0>; + remote-endpoint = <&charger_input>; + }; + }; + }; + + tsc2007: adc@48 { + compatible = "ti,tsc2007"; + reg = <0x48>; + + ti,x-plate-ohms = <1>; + #io-channel-cells = <1>; + }; }; cam_i2c: i2c@7000c500 { @@ -1309,6 +1358,22 @@ }; }; + max8971: charger@35 { + compatible = "maxim,max8971"; + reg = <0x35>; + + interrupt-parent = <&gpio>; + interrupts = ; + + monitored-battery = <&battery>; + + port { + charger_input: endpoint { + remote-endpoint = <&muic_to_charger>; + }; + }; + }; + fuel-gauge@36 { compatible = "maxim,max17043"; reg = <0x36>; @@ -1317,6 +1382,10 @@ interrupts = ; monitored-battery = <&battery>; + power-supplies = <&max8971>; + + io-channels = <&tbattery 0>; + io-channel-names = "temp"; maxim,alert-low-soc-level = <10>; wakeup-source; @@ -1357,7 +1426,58 @@ status = "okay"; spi-max-frequency = <25000000>; - /* DSI bridge */ + dsi@2 { + compatible = "solomon,ssd2825"; + reg = <2>; + + #address-cells = <1>; + #size-cells = <0>; + + spi-max-frequency = <1000000>; + + spi-cpha; + spi-cpol; + + reset-gpios = <&gpio TEGRA_GPIO(O, 2) GPIO_ACTIVE_LOW>; + + dvdd-supply = <&vdd_1v2_rgb>; + avdd-supply = <&vdd_1v2_rgb>; + vddio-supply = <&vdd_1v8_vio>; + + solomon,hs-zero-delay-ns = <300>; + solomon,hs-prep-delay-ns = <65>; + + clocks = <&tegra_pmc TEGRA_PMC_CLK_OUT_3>; + + assigned-clocks = <&tegra_car TEGRA30_CLK_EXTERN3>, + <&tegra_pmc TEGRA_PMC_CLK_OUT_3>; + assigned-clock-rates = <24000000>; + + assigned-clock-parents = <&tegra_car TEGRA30_CLK_PLL_P>, + <&tegra_car TEGRA30_CLK_EXTERN3>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + bridge_input: endpoint { + remote-endpoint = <&dpi_output>; + bus-width = <24>; + }; + }; + + port@1 { + reg = <1>; + + bridge_output: endpoint { + remote-endpoint = <&panel_input>; + }; + }; + }; + }; }; pmc@7000e400 { @@ -1446,12 +1566,13 @@ usb@7d000000 { compatible = "nvidia,tegra30-udc"; status = "okay"; - dr_mode = "peripheral"; + dr_mode = "otg"; + extcon = <&max14526>, <&max14526>; }; usb-phy@7d000000 { status = "okay"; - dr_mode = "peripheral"; + dr_mode = "otg"; nvidia,hssync-start-delay = <0>; nvidia,xcvr-lsfslew = <2>; nvidia,xcvr-lsrslew = <2>; @@ -1617,6 +1738,17 @@ vin-supply = <&vdd_3v3_vbat>; }; + vdd_1v2_rgb: regulator-rgb1v2 { + compatible = "regulator-fixed"; + regulator-name = "vdd_1v2_rgb"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + gpio = <&gpio TEGRA_GPIO(B, 1) GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_3v3_vbat>; + }; + vcc_3v0_lcd: regulator-lcd3v { compatible = "regulator-fixed"; regulator-name = "vcc_3v0_lcd"; @@ -1724,7 +1856,43 @@ <&tegra_car TEGRA30_CLK_EXTERN1>; }; + tbattery: thermal-sensor-battery { + compatible = "generic-adc-thermal"; + #thermal-sensor-cells = <0>; + + io-channels = <&tsc2007 4>; + io-channel-names = "sensor-channel"; + #io-channel-cells = <1>; + + temperature-lookup-table = < + (-50000) 4100 (-40000) 3980 (-30000) 3815 (-20000) 3610 + (-10000) 3285 0 2880 10000 2445 20000 1955 + 30000 1440 40000 1125 50000 840 60000 665 + 70000 465 80000 350 90000 230 100000 185 >; + }; + thermal-zones { + battery-thermal { + polling-delay-passive = <0>; /* milliseconds */ + polling-delay = <20000>; /* milliseconds */ + + thermal-sensors = <&tbattery>; + + trips { + battery-alert { + temperature = <55000>; + hysteresis = <2000>; + type = "hot"; + }; + + battery-crit { + temperature = <60000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + }; + /* * NCT72 has two sensors: * diff --git a/arch/arm/boot/dts/nxp/imx/Makefile b/arch/arm/boot/dts/nxp/imx/Makefile index de4142e8f3ce..856c9f21bd70 100644 --- a/arch/arm/boot/dts/nxp/imx/Makefile +++ b/arch/arm/boot/dts/nxp/imx/Makefile @@ -58,6 +58,31 @@ dtb-$(CONFIG_SOC_IMX53) += \ imx53-voipac-bsb.dtb imx53-qsb-hdmi-dtbs := imx53-qsb.dtb imx53-qsb-hdmi.dtbo imx53-qsrb-hdmi-dtbs := imx53-qsrb.dtb imx53-qsb-hdmi.dtbo + +imx6qdl-dhcom-pdk2-overlay-497-200-x12-dtbs := \ + imx6q-dhcom-pdk2.dtb \ + imx6qdl-dhcom-pdk2-overlay-497-200-x12.dtbo + +imx6qdl-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh-dtbs := \ + imx6q-dhcom-pdk2.dtb \ + imx6qdl-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh.dtbo + +imx6qdl-dhcom-pdk2-overlay-531-100-x21-dtbs := \ + imx6q-dhcom-pdk2.dtb \ + imx6qdl-dhcom-pdk2-overlay-531-100-x21.dtbo + +imx6qdl-dhcom-pdk2-overlay-531-100-x22-dtbs := \ + imx6q-dhcom-pdk2.dtb \ + imx6qdl-dhcom-pdk2-overlay-531-100-x22.dtbo + +imx6qdl-dhcom-pdk2-overlay-560-200-x12-dtbs := \ + imx6q-dhcom-pdk2.dtb \ + imx6qdl-dhcom-pdk2-overlay-560-200-x12.dtbo + +imx6qdl-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh-dtbs := \ + imx6q-dhcom-pdk2.dtb \ + imx6qdl-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh.dtbo + dtb-$(CONFIG_SOC_IMX6Q) += \ imx6dl-alti6p.dtb \ imx6dl-apf6dev.dtb \ @@ -179,6 +204,18 @@ dtb-$(CONFIG_SOC_IMX6Q) += \ imx6q-cubox-i-som-v15.dtb \ imx6q-dfi-fs700-m60.dtb \ imx6q-dhcom-pdk2.dtb \ + imx6qdl-dhcom-pdk2-overlay-497-200-x12.dtb \ + imx6qdl-dhcom-pdk2-overlay-497-200-x12.dtbo \ + imx6qdl-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh.dtb \ + imx6qdl-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh.dtbo \ + imx6qdl-dhcom-pdk2-overlay-531-100-x21.dtb \ + imx6qdl-dhcom-pdk2-overlay-531-100-x21.dtbo \ + imx6qdl-dhcom-pdk2-overlay-531-100-x22.dtb \ + imx6qdl-dhcom-pdk2-overlay-531-100-x22.dtbo \ + imx6qdl-dhcom-pdk2-overlay-560-200-x12.dtb \ + imx6qdl-dhcom-pdk2-overlay-560-200-x12.dtbo \ + imx6qdl-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh.dtb \ + imx6qdl-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh.dtbo \ imx6q-display5-tianma-tm070-1280x768.dtb \ imx6q-dmo-edmqmx6.dtb \ imx6q-dms-ba16.dtb \ diff --git a/arch/arm/boot/dts/nxp/imx/imx1-ads.dts b/arch/arm/boot/dts/nxp/imx/imx1-ads.dts index 2c817c4a4c68..823e7c42910b 100644 --- a/arch/arm/boot/dts/nxp/imx/imx1-ads.dts +++ b/arch/arm/boot/dts/nxp/imx/imx1-ads.dts @@ -76,60 +76,58 @@ }; &iomuxc { - imx1-ads { - pinctrl_cspi1: cspi1grp { - fsl,pins = < - MX1_PAD_SPI1_MISO__SPI1_MISO 0x0 - MX1_PAD_SPI1_MOSI__SPI1_MOSI 0x0 - MX1_PAD_SPI1_RDY__SPI1_RDY 0x0 - MX1_PAD_SPI1_SCLK__SPI1_SCLK 0x0 - MX1_PAD_SPI1_SS__GPIO3_15 0x0 - >; - }; + pinctrl_cspi1: cspi1grp { + fsl,pins = < + MX1_PAD_SPI1_MISO__SPI1_MISO 0x0 + MX1_PAD_SPI1_MOSI__SPI1_MOSI 0x0 + MX1_PAD_SPI1_RDY__SPI1_RDY 0x0 + MX1_PAD_SPI1_SCLK__SPI1_SCLK 0x0 + MX1_PAD_SPI1_SS__GPIO3_15 0x0 + >; + }; - pinctrl_i2c: i2cgrp { - fsl,pins = < - MX1_PAD_I2C_SCL__I2C_SCL 0x0 - MX1_PAD_I2C_SDA__I2C_SDA 0x0 - >; - }; + pinctrl_i2c: i2cgrp { + fsl,pins = < + MX1_PAD_I2C_SCL__I2C_SCL 0x0 + MX1_PAD_I2C_SDA__I2C_SDA 0x0 + >; + }; - pinctrl_uart1: uart1grp { - fsl,pins = < - MX1_PAD_UART1_TXD__UART1_TXD 0x0 - MX1_PAD_UART1_RXD__UART1_RXD 0x0 - MX1_PAD_UART1_CTS__UART1_CTS 0x0 - MX1_PAD_UART1_RTS__UART1_RTS 0x0 - >; - }; + pinctrl_uart1: uart1grp { + fsl,pins = < + MX1_PAD_UART1_TXD__UART1_TXD 0x0 + MX1_PAD_UART1_RXD__UART1_RXD 0x0 + MX1_PAD_UART1_CTS__UART1_CTS 0x0 + MX1_PAD_UART1_RTS__UART1_RTS 0x0 + >; + }; - pinctrl_uart2: uart2grp { - fsl,pins = < - MX1_PAD_UART2_TXD__UART2_TXD 0x0 - MX1_PAD_UART2_RXD__UART2_RXD 0x0 - MX1_PAD_UART2_CTS__UART2_CTS 0x0 - MX1_PAD_UART2_RTS__UART2_RTS 0x0 - >; - }; + pinctrl_uart2: uart2grp { + fsl,pins = < + MX1_PAD_UART2_TXD__UART2_TXD 0x0 + MX1_PAD_UART2_RXD__UART2_RXD 0x0 + MX1_PAD_UART2_CTS__UART2_CTS 0x0 + MX1_PAD_UART2_RTS__UART2_RTS 0x0 + >; + }; - pinctrl_weim: weimgrp { - fsl,pins = < - MX1_PAD_A0__A0 0x0 - MX1_PAD_A16__A16 0x0 - MX1_PAD_A17__A17 0x0 - MX1_PAD_A18__A18 0x0 - MX1_PAD_A19__A19 0x0 - MX1_PAD_A20__A20 0x0 - MX1_PAD_A21__A21 0x0 - MX1_PAD_A22__A22 0x0 - MX1_PAD_A23__A23 0x0 - MX1_PAD_A24__A24 0x0 - MX1_PAD_BCLK__BCLK 0x0 - MX1_PAD_CS4__CS4 0x0 - MX1_PAD_DTACK__DTACK 0x0 - MX1_PAD_ECB__ECB 0x0 - MX1_PAD_LBA__LBA 0x0 - >; - }; + pinctrl_weim: weimgrp { + fsl,pins = < + MX1_PAD_A0__A0 0x0 + MX1_PAD_A16__A16 0x0 + MX1_PAD_A17__A17 0x0 + MX1_PAD_A18__A18 0x0 + MX1_PAD_A19__A19 0x0 + MX1_PAD_A20__A20 0x0 + MX1_PAD_A21__A21 0x0 + MX1_PAD_A22__A22 0x0 + MX1_PAD_A23__A23 0x0 + MX1_PAD_A24__A24 0x0 + MX1_PAD_BCLK__BCLK 0x0 + MX1_PAD_CS4__CS4 0x0 + MX1_PAD_DTACK__DTACK 0x0 + MX1_PAD_ECB__ECB 0x0 + MX1_PAD_LBA__LBA 0x0 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx1-apf9328.dts b/arch/arm/boot/dts/nxp/imx/imx1-apf9328.dts index 058e9435524f..794e5bfee367 100644 --- a/arch/arm/boot/dts/nxp/imx/imx1-apf9328.dts +++ b/arch/arm/boot/dts/nxp/imx/imx1-apf9328.dts @@ -67,56 +67,54 @@ }; &iomuxc { - imx1-apf9328 { - pinctrl_eth: ethgrp { - fsl,pins = < - MX1_PAD_SIM_SVEN__GPIO2_14 0x0 - >; - }; + pinctrl_eth: ethgrp { + fsl,pins = < + MX1_PAD_SIM_SVEN__GPIO2_14 0x0 + >; + }; - pinctrl_i2c: i2cgrp { - fsl,pins = < - MX1_PAD_I2C_SCL__I2C_SCL 0x0 - MX1_PAD_I2C_SDA__I2C_SDA 0x0 - >; - }; + pinctrl_i2c: i2cgrp { + fsl,pins = < + MX1_PAD_I2C_SCL__I2C_SCL 0x0 + MX1_PAD_I2C_SDA__I2C_SDA 0x0 + >; + }; - pinctrl_uart1: uart1grp { - fsl,pins = < - MX1_PAD_UART1_TXD__UART1_TXD 0x0 - MX1_PAD_UART1_RXD__UART1_RXD 0x0 - MX1_PAD_UART1_CTS__UART1_CTS 0x0 - MX1_PAD_UART1_RTS__UART1_RTS 0x0 - >; - }; + pinctrl_uart1: uart1grp { + fsl,pins = < + MX1_PAD_UART1_TXD__UART1_TXD 0x0 + MX1_PAD_UART1_RXD__UART1_RXD 0x0 + MX1_PAD_UART1_CTS__UART1_CTS 0x0 + MX1_PAD_UART1_RTS__UART1_RTS 0x0 + >; + }; - pinctrl_uart2: uart2grp { - fsl,pins = < - MX1_PAD_UART2_TXD__UART2_TXD 0x0 - MX1_PAD_UART2_RXD__UART2_RXD 0x0 - MX1_PAD_UART2_CTS__UART2_CTS 0x0 - MX1_PAD_UART2_RTS__UART2_RTS 0x0 - >; - }; + pinctrl_uart2: uart2grp { + fsl,pins = < + MX1_PAD_UART2_TXD__UART2_TXD 0x0 + MX1_PAD_UART2_RXD__UART2_RXD 0x0 + MX1_PAD_UART2_CTS__UART2_CTS 0x0 + MX1_PAD_UART2_RTS__UART2_RTS 0x0 + >; + }; - pinctrl_weim: weimgrp { - fsl,pins = < - MX1_PAD_A0__A0 0x0 - MX1_PAD_A16__A16 0x0 - MX1_PAD_A17__A17 0x0 - MX1_PAD_A18__A18 0x0 - MX1_PAD_A19__A19 0x0 - MX1_PAD_A20__A20 0x0 - MX1_PAD_A21__A21 0x0 - MX1_PAD_A22__A22 0x0 - MX1_PAD_A23__A23 0x0 - MX1_PAD_A24__A24 0x0 - MX1_PAD_BCLK__BCLK 0x0 - MX1_PAD_CS4__CS4 0x0 - MX1_PAD_DTACK__DTACK 0x0 - MX1_PAD_ECB__ECB 0x0 - MX1_PAD_LBA__LBA 0x0 - >; - }; + pinctrl_weim: weimgrp { + fsl,pins = < + MX1_PAD_A0__A0 0x0 + MX1_PAD_A16__A16 0x0 + MX1_PAD_A17__A17 0x0 + MX1_PAD_A18__A18 0x0 + MX1_PAD_A19__A19 0x0 + MX1_PAD_A20__A20 0x0 + MX1_PAD_A21__A21 0x0 + MX1_PAD_A22__A22 0x0 + MX1_PAD_A23__A23 0x0 + MX1_PAD_A24__A24 0x0 + MX1_PAD_BCLK__BCLK 0x0 + MX1_PAD_CS4__CS4 0x0 + MX1_PAD_DTACK__DTACK 0x0 + MX1_PAD_ECB__ECB 0x0 + MX1_PAD_LBA__LBA 0x0 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx1.dtsi b/arch/arm/boot/dts/nxp/imx/imx1.dtsi index a1a89ccacf05..ed04a907b3f7 100644 --- a/arch/arm/boot/dts/nxp/imx/imx1.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx1.dtsi @@ -202,7 +202,7 @@ #clock-cells = <1>; }; - iomuxc: iomuxc@21c000 { + iomuxc: pinmux@21c000 { compatible = "fsl,imx1-iomuxc"; reg = <0x0021c000 0x1000>; #address-cells = <1>; diff --git a/arch/arm/boot/dts/nxp/imx/imx25-eukrea-cpuimx25.dtsi b/arch/arm/boot/dts/nxp/imx/imx25-eukrea-cpuimx25.dtsi index 93a6e4e680b4..31dc2a640362 100644 --- a/arch/arm/boot/dts/nxp/imx/imx25-eukrea-cpuimx25.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx25-eukrea-cpuimx25.dtsi @@ -34,27 +34,25 @@ }; &iomuxc { - imx25-eukrea-cpuimx25 { - pinctrl_fec: fecgrp { - fsl,pins = < - MX25_PAD_FEC_MDC__FEC_MDC 0x80000000 - MX25_PAD_FEC_MDIO__FEC_MDIO 0x400001e0 - MX25_PAD_FEC_TDATA0__FEC_TDATA0 0x80000000 - MX25_PAD_FEC_TDATA1__FEC_TDATA1 0x80000000 - MX25_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000 - MX25_PAD_FEC_RDATA0__FEC_RDATA0 0x80000000 - MX25_PAD_FEC_RDATA1__FEC_RDATA1 0x80000000 - MX25_PAD_FEC_RX_DV__FEC_RX_DV 0x80000000 - MX25_PAD_FEC_TX_CLK__FEC_TX_CLK 0x1c0 - >; - }; + pinctrl_fec: fecgrp { + fsl,pins = < + MX25_PAD_FEC_MDC__FEC_MDC 0x80000000 + MX25_PAD_FEC_MDIO__FEC_MDIO 0x400001e0 + MX25_PAD_FEC_TDATA0__FEC_TDATA0 0x80000000 + MX25_PAD_FEC_TDATA1__FEC_TDATA1 0x80000000 + MX25_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000 + MX25_PAD_FEC_RDATA0__FEC_RDATA0 0x80000000 + MX25_PAD_FEC_RDATA1__FEC_RDATA1 0x80000000 + MX25_PAD_FEC_RX_DV__FEC_RX_DV 0x80000000 + MX25_PAD_FEC_TX_CLK__FEC_TX_CLK 0x1c0 + >; + }; - pinctrl_i2c1: i2c1grp { - fsl,pins = < - MX25_PAD_I2C1_CLK__I2C1_CLK 0x80000000 - MX25_PAD_I2C1_DAT__I2C1_DAT 0x80000000 - >; - }; + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX25_PAD_I2C1_CLK__I2C1_CLK 0x80000000 + MX25_PAD_I2C1_DAT__I2C1_DAT 0x80000000 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx25-eukrea-mbimxsd25-baseboard-cmo-qvga.dts b/arch/arm/boot/dts/nxp/imx/imx25-eukrea-mbimxsd25-baseboard-cmo-qvga.dts index 6cddb2cc36fe..e08fcbfef4d5 100644 --- a/arch/arm/boot/dts/nxp/imx/imx25-eukrea-mbimxsd25-baseboard-cmo-qvga.dts +++ b/arch/arm/boot/dts/nxp/imx/imx25-eukrea-mbimxsd25-baseboard-cmo-qvga.dts @@ -43,10 +43,8 @@ }; &iomuxc { - imx25-eukrea-mbimxsd25-baseboard-cmo-qvga { - pinctrl_reg_lcd_3v3: reg_lcd_3v3 { - fsl,pins = ; - }; + pinctrl_reg_lcd_3v3: reg_lcd_3v3grp { + fsl,pins = ; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx25-eukrea-mbimxsd25-baseboard.dts b/arch/arm/boot/dts/nxp/imx/imx25-eukrea-mbimxsd25-baseboard.dts index c7207ea437c4..cf127e00793e 100644 --- a/arch/arm/boot/dts/nxp/imx/imx25-eukrea-mbimxsd25-baseboard.dts +++ b/arch/arm/boot/dts/nxp/imx/imx25-eukrea-mbimxsd25-baseboard.dts @@ -68,80 +68,78 @@ }; &iomuxc { - imx25-eukrea-mbimxsd25-baseboard { - pinctrl_audmux: audmuxgrp { - fsl,pins = < - MX25_PAD_KPP_COL3__AUD5_TXFS 0xe0 - MX25_PAD_KPP_COL2__AUD5_TXC 0xe0 - MX25_PAD_KPP_COL1__AUD5_RXD 0xe0 - MX25_PAD_KPP_COL0__AUD5_TXD 0xe0 - >; - }; + pinctrl_audmux: audmuxgrp { + fsl,pins = < + MX25_PAD_KPP_COL3__AUD5_TXFS 0xe0 + MX25_PAD_KPP_COL2__AUD5_TXC 0xe0 + MX25_PAD_KPP_COL1__AUD5_RXD 0xe0 + MX25_PAD_KPP_COL0__AUD5_TXD 0xe0 + >; + }; - pinctrl_esdhc1: esdhc1grp { - fsl,pins = < - MX25_PAD_SD1_CMD__ESDHC1_CMD 0x400000c0 - MX25_PAD_SD1_CLK__ESDHC1_CLK 0x400000c0 - MX25_PAD_SD1_DATA0__ESDHC1_DAT0 0x400000c0 - MX25_PAD_SD1_DATA1__ESDHC1_DAT1 0x400000c0 - MX25_PAD_SD1_DATA2__ESDHC1_DAT2 0x400000c0 - MX25_PAD_SD1_DATA3__ESDHC1_DAT3 0x400000c0 - >; - }; + pinctrl_esdhc1: esdhc1grp { + fsl,pins = < + MX25_PAD_SD1_CMD__ESDHC1_CMD 0x400000c0 + MX25_PAD_SD1_CLK__ESDHC1_CLK 0x400000c0 + MX25_PAD_SD1_DATA0__ESDHC1_DAT0 0x400000c0 + MX25_PAD_SD1_DATA1__ESDHC1_DAT1 0x400000c0 + MX25_PAD_SD1_DATA2__ESDHC1_DAT2 0x400000c0 + MX25_PAD_SD1_DATA3__ESDHC1_DAT3 0x400000c0 + >; + }; - pinctrl_gpiokeys: gpiokeysgrp { - fsl,pins = ; - }; + pinctrl_gpiokeys: gpiokeysgrp { + fsl,pins = ; + }; - pinctrl_gpioled: gpioledgrp { - fsl,pins = ; - }; + pinctrl_gpioled: gpioledgrp { + fsl,pins = ; + }; - pinctrl_lcdc: lcdcgrp { - fsl,pins = < - MX25_PAD_LD0__LD0 0x1 - MX25_PAD_LD1__LD1 0x1 - MX25_PAD_LD2__LD2 0x1 - MX25_PAD_LD3__LD3 0x1 - MX25_PAD_LD4__LD4 0x1 - MX25_PAD_LD5__LD5 0x1 - MX25_PAD_LD6__LD6 0x1 - MX25_PAD_LD7__LD7 0x1 - MX25_PAD_LD8__LD8 0x1 - MX25_PAD_LD9__LD9 0x1 - MX25_PAD_LD10__LD10 0x1 - MX25_PAD_LD11__LD11 0x1 - MX25_PAD_LD12__LD12 0x1 - MX25_PAD_LD13__LD13 0x1 - MX25_PAD_LD14__LD14 0x1 - MX25_PAD_LD15__LD15 0x1 - MX25_PAD_GPIO_E__LD16 0x1 - MX25_PAD_GPIO_F__LD17 0x1 - MX25_PAD_HSYNC__HSYNC 0x80000000 - MX25_PAD_VSYNC__VSYNC 0x80000000 - MX25_PAD_LSCLK__LSCLK 0x80000000 - MX25_PAD_OE_ACD__OE_ACD 0x80000000 - MX25_PAD_CONTRAST__CONTRAST 0x80000000 - >; - }; + pinctrl_lcdc: lcdcgrp { + fsl,pins = < + MX25_PAD_LD0__LD0 0x1 + MX25_PAD_LD1__LD1 0x1 + MX25_PAD_LD2__LD2 0x1 + MX25_PAD_LD3__LD3 0x1 + MX25_PAD_LD4__LD4 0x1 + MX25_PAD_LD5__LD5 0x1 + MX25_PAD_LD6__LD6 0x1 + MX25_PAD_LD7__LD7 0x1 + MX25_PAD_LD8__LD8 0x1 + MX25_PAD_LD9__LD9 0x1 + MX25_PAD_LD10__LD10 0x1 + MX25_PAD_LD11__LD11 0x1 + MX25_PAD_LD12__LD12 0x1 + MX25_PAD_LD13__LD13 0x1 + MX25_PAD_LD14__LD14 0x1 + MX25_PAD_LD15__LD15 0x1 + MX25_PAD_GPIO_E__LD16 0x1 + MX25_PAD_GPIO_F__LD17 0x1 + MX25_PAD_HSYNC__HSYNC 0x80000000 + MX25_PAD_VSYNC__VSYNC 0x80000000 + MX25_PAD_LSCLK__LSCLK 0x80000000 + MX25_PAD_OE_ACD__OE_ACD 0x80000000 + MX25_PAD_CONTRAST__CONTRAST 0x80000000 + >; + }; - pinctrl_uart1: uart1grp { - fsl,pins = < - MX25_PAD_UART1_RTS__UART1_RTS 0xe0 - MX25_PAD_UART1_CTS__UART1_CTS 0xe0 - MX25_PAD_UART1_TXD__UART1_TXD 0x80000000 - MX25_PAD_UART1_RXD__UART1_RXD 0xc0 - >; - }; + pinctrl_uart1: uart1grp { + fsl,pins = < + MX25_PAD_UART1_RTS__UART1_RTS 0xe0 + MX25_PAD_UART1_CTS__UART1_CTS 0xe0 + MX25_PAD_UART1_TXD__UART1_TXD 0x80000000 + MX25_PAD_UART1_RXD__UART1_RXD 0xc0 + >; + }; - pinctrl_uart2: uart2grp { - fsl,pins = < - MX25_PAD_UART2_RXD__UART2_RXD 0x80000000 - MX25_PAD_UART2_TXD__UART2_TXD 0x80000000 - MX25_PAD_UART2_RTS__UART2_RTS 0x80000000 - MX25_PAD_UART2_CTS__UART2_CTS 0x80000000 - >; - }; + pinctrl_uart2: uart2grp { + fsl,pins = < + MX25_PAD_UART2_RXD__UART2_RXD 0x80000000 + MX25_PAD_UART2_TXD__UART2_TXD 0x80000000 + MX25_PAD_UART2_RTS__UART2_RTS 0x80000000 + MX25_PAD_UART2_CTS__UART2_CTS 0x80000000 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx25-pdk.dts b/arch/arm/boot/dts/nxp/imx/imx25-pdk.dts index dd176fb54e58..a35778ba6ffa 100644 --- a/arch/arm/boot/dts/nxp/imx/imx25-pdk.dts +++ b/arch/arm/boot/dts/nxp/imx/imx25-pdk.dts @@ -130,109 +130,107 @@ }; &iomuxc { - imx25-pdk { - pinctrl_audmux: audmuxgrp { - fsl,pins = < - MX25_PAD_RW__AUD4_TXFS 0xe0 - MX25_PAD_OE__AUD4_TXC 0xe0 - MX25_PAD_EB0__AUD4_TXD 0xe0 - MX25_PAD_EB1__AUD4_RXD 0xe0 - >; - }; + pinctrl_audmux: audmuxgrp { + fsl,pins = < + MX25_PAD_RW__AUD4_TXFS 0xe0 + MX25_PAD_OE__AUD4_TXC 0xe0 + MX25_PAD_EB0__AUD4_TXD 0xe0 + MX25_PAD_EB1__AUD4_RXD 0xe0 + >; + }; - pinctrl_can1: can1grp { - fsl,pins = < - MX25_PAD_GPIO_A__CAN1_TX 0x0 - MX25_PAD_GPIO_B__CAN1_RX 0x0 - MX25_PAD_D14__GPIO_4_6 0x80000000 - >; - }; + pinctrl_can1: can1grp { + fsl,pins = < + MX25_PAD_GPIO_A__CAN1_TX 0x0 + MX25_PAD_GPIO_B__CAN1_RX 0x0 + MX25_PAD_D14__GPIO_4_6 0x80000000 + >; + }; - pinctrl_esdhc1: esdhc1grp { - fsl,pins = < - MX25_PAD_SD1_CMD__ESDHC1_CMD 0x80000000 - MX25_PAD_SD1_CLK__ESDHC1_CLK 0x80000000 - MX25_PAD_SD1_DATA0__ESDHC1_DAT0 0x80000000 - MX25_PAD_SD1_DATA1__ESDHC1_DAT1 0x80000000 - MX25_PAD_SD1_DATA2__ESDHC1_DAT2 0x80000000 - MX25_PAD_SD1_DATA3__ESDHC1_DAT3 0x80000000 - MX25_PAD_A14__GPIO_2_0 0x80000000 - MX25_PAD_A15__GPIO_2_1 0x80000000 - >; - }; + pinctrl_esdhc1: esdhc1grp { + fsl,pins = < + MX25_PAD_SD1_CMD__ESDHC1_CMD 0x80000000 + MX25_PAD_SD1_CLK__ESDHC1_CLK 0x80000000 + MX25_PAD_SD1_DATA0__ESDHC1_DAT0 0x80000000 + MX25_PAD_SD1_DATA1__ESDHC1_DAT1 0x80000000 + MX25_PAD_SD1_DATA2__ESDHC1_DAT2 0x80000000 + MX25_PAD_SD1_DATA3__ESDHC1_DAT3 0x80000000 + MX25_PAD_A14__GPIO_2_0 0x80000000 + MX25_PAD_A15__GPIO_2_1 0x80000000 + >; + }; - pinctrl_fec: fecgrp { - fsl,pins = < - MX25_PAD_FEC_MDC__FEC_MDC 0x80000000 - MX25_PAD_FEC_MDIO__FEC_MDIO 0x400001e0 - MX25_PAD_FEC_TDATA0__FEC_TDATA0 0x80000000 - MX25_PAD_FEC_TDATA1__FEC_TDATA1 0x80000000 - MX25_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000 - MX25_PAD_FEC_RDATA0__FEC_RDATA0 0x80000000 - MX25_PAD_FEC_RDATA1__FEC_RDATA1 0x80000000 - MX25_PAD_FEC_RX_DV__FEC_RX_DV 0x80000000 - MX25_PAD_FEC_TX_CLK__FEC_TX_CLK 0x1c0 - MX25_PAD_A17__GPIO_2_3 0x80000000 - MX25_PAD_D12__GPIO_4_8 0x80000000 - >; - }; + pinctrl_fec: fecgrp { + fsl,pins = < + MX25_PAD_FEC_MDC__FEC_MDC 0x80000000 + MX25_PAD_FEC_MDIO__FEC_MDIO 0x400001e0 + MX25_PAD_FEC_TDATA0__FEC_TDATA0 0x80000000 + MX25_PAD_FEC_TDATA1__FEC_TDATA1 0x80000000 + MX25_PAD_FEC_TX_EN__FEC_TX_EN 0x80000000 + MX25_PAD_FEC_RDATA0__FEC_RDATA0 0x80000000 + MX25_PAD_FEC_RDATA1__FEC_RDATA1 0x80000000 + MX25_PAD_FEC_RX_DV__FEC_RX_DV 0x80000000 + MX25_PAD_FEC_TX_CLK__FEC_TX_CLK 0x1c0 + MX25_PAD_A17__GPIO_2_3 0x80000000 + MX25_PAD_D12__GPIO_4_8 0x80000000 + >; + }; - pinctrl_i2c1: i2c1grp { - fsl,pins = < - MX25_PAD_I2C1_CLK__I2C1_CLK 0x80000000 - MX25_PAD_I2C1_DAT__I2C1_DAT 0x80000000 - >; - }; + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX25_PAD_I2C1_CLK__I2C1_CLK 0x80000000 + MX25_PAD_I2C1_DAT__I2C1_DAT 0x80000000 + >; + }; - pinctrl_kpp: kppgrp { - fsl,pins = < - MX25_PAD_KPP_ROW0__KPP_ROW0 0x80000000 - MX25_PAD_KPP_ROW1__KPP_ROW1 0x80000000 - MX25_PAD_KPP_ROW2__KPP_ROW2 0x80000000 - MX25_PAD_KPP_ROW3__KPP_ROW3 0x80000000 - MX25_PAD_KPP_COL0__KPP_COL0 0x80000000 - MX25_PAD_KPP_COL1__KPP_COL1 0x80000000 - MX25_PAD_KPP_COL2__KPP_COL2 0x80000000 - MX25_PAD_KPP_COL3__KPP_COL3 0x80000000 - >; - }; + pinctrl_kpp: kppgrp { + fsl,pins = < + MX25_PAD_KPP_ROW0__KPP_ROW0 0x80000000 + MX25_PAD_KPP_ROW1__KPP_ROW1 0x80000000 + MX25_PAD_KPP_ROW2__KPP_ROW2 0x80000000 + MX25_PAD_KPP_ROW3__KPP_ROW3 0x80000000 + MX25_PAD_KPP_COL0__KPP_COL0 0x80000000 + MX25_PAD_KPP_COL1__KPP_COL1 0x80000000 + MX25_PAD_KPP_COL2__KPP_COL2 0x80000000 + MX25_PAD_KPP_COL3__KPP_COL3 0x80000000 + >; + }; - pinctrl_lcd: lcdgrp { - fsl,pins = < - MX25_PAD_LD0__LD0 0xe0 - MX25_PAD_LD1__LD1 0xe0 - MX25_PAD_LD2__LD2 0xe0 - MX25_PAD_LD3__LD3 0xe0 - MX25_PAD_LD4__LD4 0xe0 - MX25_PAD_LD5__LD5 0xe0 - MX25_PAD_LD6__LD6 0xe0 - MX25_PAD_LD7__LD7 0xe0 - MX25_PAD_LD8__LD8 0xe0 - MX25_PAD_LD9__LD9 0xe0 - MX25_PAD_LD10__LD10 0xe0 - MX25_PAD_LD11__LD11 0xe0 - MX25_PAD_LD12__LD12 0xe0 - MX25_PAD_LD13__LD13 0xe0 - MX25_PAD_LD14__LD14 0xe0 - MX25_PAD_LD15__LD15 0xe0 - MX25_PAD_GPIO_E__LD16 0xe0 - MX25_PAD_GPIO_F__LD17 0xe0 - MX25_PAD_HSYNC__HSYNC 0xe0 - MX25_PAD_VSYNC__VSYNC 0xe0 - MX25_PAD_LSCLK__LSCLK 0xe0 - MX25_PAD_OE_ACD__OE_ACD 0xe0 - MX25_PAD_CONTRAST__CONTRAST 0xe0 - >; - }; + pinctrl_lcd: lcdgrp { + fsl,pins = < + MX25_PAD_LD0__LD0 0xe0 + MX25_PAD_LD1__LD1 0xe0 + MX25_PAD_LD2__LD2 0xe0 + MX25_PAD_LD3__LD3 0xe0 + MX25_PAD_LD4__LD4 0xe0 + MX25_PAD_LD5__LD5 0xe0 + MX25_PAD_LD6__LD6 0xe0 + MX25_PAD_LD7__LD7 0xe0 + MX25_PAD_LD8__LD8 0xe0 + MX25_PAD_LD9__LD9 0xe0 + MX25_PAD_LD10__LD10 0xe0 + MX25_PAD_LD11__LD11 0xe0 + MX25_PAD_LD12__LD12 0xe0 + MX25_PAD_LD13__LD13 0xe0 + MX25_PAD_LD14__LD14 0xe0 + MX25_PAD_LD15__LD15 0xe0 + MX25_PAD_GPIO_E__LD16 0xe0 + MX25_PAD_GPIO_F__LD17 0xe0 + MX25_PAD_HSYNC__HSYNC 0xe0 + MX25_PAD_VSYNC__VSYNC 0xe0 + MX25_PAD_LSCLK__LSCLK 0xe0 + MX25_PAD_OE_ACD__OE_ACD 0xe0 + MX25_PAD_CONTRAST__CONTRAST 0xe0 + >; + }; - pinctrl_uart1: uart1grp { - fsl,pins = < - MX25_PAD_UART1_RTS__UART1_RTS 0xe0 - MX25_PAD_UART1_CTS__UART1_CTS 0xe0 - MX25_PAD_UART1_TXD__UART1_TXD 0x80000000 - MX25_PAD_UART1_RXD__UART1_RXD 0xc0 - >; - }; + pinctrl_uart1: uart1grp { + fsl,pins = < + MX25_PAD_UART1_RTS__UART1_RTS 0xe0 + MX25_PAD_UART1_CTS__UART1_CTS 0xe0 + MX25_PAD_UART1_TXD__UART1_TXD 0x80000000 + MX25_PAD_UART1_RXD__UART1_RXD 0xc0 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx25.dtsi b/arch/arm/boot/dts/nxp/imx/imx25.dtsi index 82601a4b7b4b..94dbcef63b8c 100644 --- a/arch/arm/boot/dts/nxp/imx/imx25.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx25.dtsi @@ -195,7 +195,7 @@ status = "disabled"; }; - iomuxc: iomuxc@43fac000 { + iomuxc: pinmux@43fac000 { compatible = "fsl,imx25-iomuxc"; reg = <0x43fac000 0x4000>; }; @@ -305,7 +305,7 @@ status = "disabled"; }; - tsc: tcq@50030400 { + tsc: touchscreen@50030400 { compatible = "fsl,imx25-tcq"; reg = <0x50030400 0x60>; interrupt-parent = <&tscadc>; diff --git a/arch/arm/boot/dts/nxp/imx/imx27-apf27.dts b/arch/arm/boot/dts/nxp/imx/imx27-apf27.dts index 745d5d409952..b67bb21af3de 100644 --- a/arch/arm/boot/dts/nxp/imx/imx27-apf27.dts +++ b/arch/arm/boot/dts/nxp/imx/imx27-apf27.dts @@ -24,36 +24,34 @@ }; &iomuxc { - imx27-apf27 { - pinctrl_fec1: fec1grp { - fsl,pins = < - MX27_PAD_SD3_CMD__FEC_TXD0 0x0 - MX27_PAD_SD3_CLK__FEC_TXD1 0x0 - MX27_PAD_ATA_DATA0__FEC_TXD2 0x0 - MX27_PAD_ATA_DATA1__FEC_TXD3 0x0 - MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0 - MX27_PAD_ATA_DATA3__FEC_RXD1 0x0 - MX27_PAD_ATA_DATA4__FEC_RXD2 0x0 - MX27_PAD_ATA_DATA5__FEC_RXD3 0x0 - MX27_PAD_ATA_DATA6__FEC_MDIO 0x0 - MX27_PAD_ATA_DATA7__FEC_MDC 0x0 - MX27_PAD_ATA_DATA8__FEC_CRS 0x0 - MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0 - MX27_PAD_ATA_DATA10__FEC_RXD0 0x0 - MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0 - MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0 - MX27_PAD_ATA_DATA13__FEC_COL 0x0 - MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0 - MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0 - >; - }; + pinctrl_fec1: fec1grp { + fsl,pins = < + MX27_PAD_SD3_CMD__FEC_TXD0 0x0 + MX27_PAD_SD3_CLK__FEC_TXD1 0x0 + MX27_PAD_ATA_DATA0__FEC_TXD2 0x0 + MX27_PAD_ATA_DATA1__FEC_TXD3 0x0 + MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0 + MX27_PAD_ATA_DATA3__FEC_RXD1 0x0 + MX27_PAD_ATA_DATA4__FEC_RXD2 0x0 + MX27_PAD_ATA_DATA5__FEC_RXD3 0x0 + MX27_PAD_ATA_DATA6__FEC_MDIO 0x0 + MX27_PAD_ATA_DATA7__FEC_MDC 0x0 + MX27_PAD_ATA_DATA8__FEC_CRS 0x0 + MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0 + MX27_PAD_ATA_DATA10__FEC_RXD0 0x0 + MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0 + MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0 + MX27_PAD_ATA_DATA13__FEC_COL 0x0 + MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0 + MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0 + >; + }; - pinctrl_uart1: uart1grp { - fsl,pins = < - MX27_PAD_UART1_TXD__UART1_TXD 0x0 - MX27_PAD_UART1_RXD__UART1_RXD 0x0 - >; - }; + pinctrl_uart1: uart1grp { + fsl,pins = < + MX27_PAD_UART1_TXD__UART1_TXD 0x0 + MX27_PAD_UART1_RXD__UART1_RXD 0x0 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx27-apf27dev.dts b/arch/arm/boot/dts/nxp/imx/imx27-apf27dev.dts index 849306cb4532..dba97912cfd7 100644 --- a/arch/arm/boot/dts/nxp/imx/imx27-apf27dev.dts +++ b/arch/arm/boot/dts/nxp/imx/imx27-apf27dev.dts @@ -122,116 +122,114 @@ }; &iomuxc { - imx27-apf27dev { - pinctrl_cspi1: cspi1grp { - fsl,pins = < - MX27_PAD_CSPI1_MISO__CSPI1_MISO 0x0 - MX27_PAD_CSPI1_MOSI__CSPI1_MOSI 0x0 - MX27_PAD_CSPI1_SCLK__CSPI1_SCLK 0x0 - >; - }; + pinctrl_cspi1: cspi1grp { + fsl,pins = < + MX27_PAD_CSPI1_MISO__CSPI1_MISO 0x0 + MX27_PAD_CSPI1_MOSI__CSPI1_MOSI 0x0 + MX27_PAD_CSPI1_SCLK__CSPI1_SCLK 0x0 + >; + }; - pinctrl_cspi1_cs: cspi1csgrp { - fsl,pins = ; - }; + pinctrl_cspi1_cs: cspi1csgrp { + fsl,pins = ; + }; - pinctrl_cspi2: cspi2grp { - fsl,pins = < - MX27_PAD_CSPI2_MISO__CSPI2_MISO 0x0 - MX27_PAD_CSPI2_MOSI__CSPI2_MOSI 0x0 - MX27_PAD_CSPI2_SCLK__CSPI2_SCLK 0x0 - >; - }; + pinctrl_cspi2: cspi2grp { + fsl,pins = < + MX27_PAD_CSPI2_MISO__CSPI2_MISO 0x0 + MX27_PAD_CSPI2_MOSI__CSPI2_MOSI 0x0 + MX27_PAD_CSPI2_SCLK__CSPI2_SCLK 0x0 + >; + }; - pinctrl_cspi2_cs: cspi2csgrp { - fsl,pins = < - MX27_PAD_CSI_D5__GPIO2_17 0x0 - MX27_PAD_CSPI2_SS0__GPIO4_21 0x0 - MX27_PAD_CSPI1_SS1__GPIO4_27 0x0 - >; - }; + pinctrl_cspi2_cs: cspi2csgrp { + fsl,pins = < + MX27_PAD_CSI_D5__GPIO2_17 0x0 + MX27_PAD_CSPI2_SS0__GPIO4_21 0x0 + MX27_PAD_CSPI1_SS1__GPIO4_27 0x0 + >; + }; - pinctrl_gpio_leds: gpioledsgrp { - fsl,pins = ; - }; + pinctrl_gpio_leds: gpioledsgrp { + fsl,pins = ; + }; - pinctrl_gpio_keys: gpiokeysgrp { - fsl,pins = ; - }; + pinctrl_gpio_keys: gpiokeysgrp { + fsl,pins = ; + }; - pinctrl_imxfb1: imxfbgrp { - fsl,pins = < - MX27_PAD_CLS__CLS 0x0 - MX27_PAD_CONTRAST__CONTRAST 0x0 - MX27_PAD_LD0__LD0 0x0 - MX27_PAD_LD1__LD1 0x0 - MX27_PAD_LD2__LD2 0x0 - MX27_PAD_LD3__LD3 0x0 - MX27_PAD_LD4__LD4 0x0 - MX27_PAD_LD5__LD5 0x0 - MX27_PAD_LD6__LD6 0x0 - MX27_PAD_LD7__LD7 0x0 - MX27_PAD_LD8__LD8 0x0 - MX27_PAD_LD9__LD9 0x0 - MX27_PAD_LD10__LD10 0x0 - MX27_PAD_LD11__LD11 0x0 - MX27_PAD_LD12__LD12 0x0 - MX27_PAD_LD13__LD13 0x0 - MX27_PAD_LD14__LD14 0x0 - MX27_PAD_LD15__LD15 0x0 - MX27_PAD_LD16__LD16 0x0 - MX27_PAD_LD17__LD17 0x0 - MX27_PAD_LSCLK__LSCLK 0x0 - MX27_PAD_OE_ACD__OE_ACD 0x0 - MX27_PAD_PS__PS 0x0 - MX27_PAD_REV__REV 0x0 - MX27_PAD_SPL_SPR__SPL_SPR 0x0 - MX27_PAD_HSYNC__HSYNC 0x0 - MX27_PAD_VSYNC__VSYNC 0x0 - >; - }; + pinctrl_imxfb1: imxfbgrp { + fsl,pins = < + MX27_PAD_CLS__CLS 0x0 + MX27_PAD_CONTRAST__CONTRAST 0x0 + MX27_PAD_LD0__LD0 0x0 + MX27_PAD_LD1__LD1 0x0 + MX27_PAD_LD2__LD2 0x0 + MX27_PAD_LD3__LD3 0x0 + MX27_PAD_LD4__LD4 0x0 + MX27_PAD_LD5__LD5 0x0 + MX27_PAD_LD6__LD6 0x0 + MX27_PAD_LD7__LD7 0x0 + MX27_PAD_LD8__LD8 0x0 + MX27_PAD_LD9__LD9 0x0 + MX27_PAD_LD10__LD10 0x0 + MX27_PAD_LD11__LD11 0x0 + MX27_PAD_LD12__LD12 0x0 + MX27_PAD_LD13__LD13 0x0 + MX27_PAD_LD14__LD14 0x0 + MX27_PAD_LD15__LD15 0x0 + MX27_PAD_LD16__LD16 0x0 + MX27_PAD_LD17__LD17 0x0 + MX27_PAD_LSCLK__LSCLK 0x0 + MX27_PAD_OE_ACD__OE_ACD 0x0 + MX27_PAD_PS__PS 0x0 + MX27_PAD_REV__REV 0x0 + MX27_PAD_SPL_SPR__SPL_SPR 0x0 + MX27_PAD_HSYNC__HSYNC 0x0 + MX27_PAD_VSYNC__VSYNC 0x0 + >; + }; - pinctrl_i2c1: i2c1grp { - fsl,pins = < - MX27_PAD_I2C_DATA__I2C_DATA 0x0 - MX27_PAD_I2C_CLK__I2C_CLK 0x0 - >; - }; + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX27_PAD_I2C_DATA__I2C_DATA 0x0 + MX27_PAD_I2C_CLK__I2C_CLK 0x0 + >; + }; - pinctrl_i2c2: i2c2grp { - fsl,pins = < - MX27_PAD_I2C2_SDA__I2C2_SDA 0x0 - MX27_PAD_I2C2_SCL__I2C2_SCL 0x0 - >; - }; + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX27_PAD_I2C2_SDA__I2C2_SDA 0x0 + MX27_PAD_I2C2_SCL__I2C2_SCL 0x0 + >; + }; - pinctrl_max1027: max1027 { - fsl,pins = < - MX27_PAD_UART1_CTS__GPIO5_14 0x0 /* CNVST */ - MX27_PAD_UART1_RTS__GPIO5_15 0x0 /* EOC */ - >; - }; + pinctrl_max1027: max1027grp { + fsl,pins = < + MX27_PAD_UART1_CTS__GPIO5_14 0x0 /* CNVST */ + MX27_PAD_UART1_RTS__GPIO5_15 0x0 /* EOC */ + >; + }; - pinctrl_pwm: pwmgrp { - fsl,pins = < - MX27_PAD_PWMO__PWMO 0x0 - >; - }; + pinctrl_pwm: pwmgrp { + fsl,pins = < + MX27_PAD_PWMO__PWMO 0x0 + >; + }; - pinctrl_sdhc2: sdhc2grp { - fsl,pins = < - MX27_PAD_SD2_CLK__SD2_CLK 0x0 - MX27_PAD_SD2_CMD__SD2_CMD 0x0 - MX27_PAD_SD2_D0__SD2_D0 0x0 - MX27_PAD_SD2_D1__SD2_D1 0x0 - MX27_PAD_SD2_D2__SD2_D2 0x0 - MX27_PAD_SD2_D3__SD2_D3 0x0 - >; - }; + pinctrl_sdhc2: sdhc2grp { + fsl,pins = < + MX27_PAD_SD2_CLK__SD2_CLK 0x0 + MX27_PAD_SD2_CMD__SD2_CMD 0x0 + MX27_PAD_SD2_D0__SD2_D0 0x0 + MX27_PAD_SD2_D1__SD2_D1 0x0 + MX27_PAD_SD2_D2__SD2_D2 0x0 + MX27_PAD_SD2_D3__SD2_D3 0x0 + >; + }; - pinctrl_sdhc2_cd: sdhc2cdgrp { - fsl,pins = ; - }; + pinctrl_sdhc2_cd: sdhc2cdgrp { + fsl,pins = ; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx27-eukrea-cpuimx27.dtsi b/arch/arm/boot/dts/nxp/imx/imx27-eukrea-cpuimx27.dtsi index c7e923584878..46acd0dfc589 100644 --- a/arch/arm/boot/dts/nxp/imx/imx27-eukrea-cpuimx27.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx27-eukrea-cpuimx27.dtsi @@ -100,52 +100,52 @@ fsl,weim-cs-timing = <0x00008f03 0xa0330d01 0x002208c0>; }; - uart8250@3,200000 { + serial@3,200000 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart8250_1>; compatible = "ns8250"; clocks = <&clk14745600>; fsl,weim-cs-timing = <0x0000d603 0x0d1d0d01 0x00d20000>; - interrupts = <&gpio2 23 IRQ_TYPE_LEVEL_LOW>; + interrupts-extended = <&gpio2 23 IRQ_TYPE_LEVEL_LOW>; reg = <3 0x200000 0x1000>; reg-shift = <1>; reg-io-width = <1>; no-loopback-test; }; - uart8250@3,400000 { + serial@3,400000 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart8250_2>; compatible = "ns8250"; clocks = <&clk14745600>; fsl,weim-cs-timing = <0x0000d603 0x0d1d0d01 0x00d20000>; - interrupts = <&gpio2 22 IRQ_TYPE_LEVEL_LOW>; + interrupts-extended = <&gpio2 22 IRQ_TYPE_LEVEL_LOW>; reg = <3 0x400000 0x1000>; reg-shift = <1>; reg-io-width = <1>; no-loopback-test; }; - uart8250@3,800000 { + serial@3,800000 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart8250_3>; compatible = "ns8250"; clocks = <&clk14745600>; fsl,weim-cs-timing = <0x0000d603 0x0d1d0d01 0x00d20000>; - interrupts = <&gpio2 27 IRQ_TYPE_LEVEL_LOW>; + interrupts-extended = <&gpio2 27 IRQ_TYPE_LEVEL_LOW>; reg = <3 0x800000 0x1000>; reg-shift = <1>; reg-io-width = <1>; no-loopback-test; }; - uart8250@3,1000000 { + serial@3,1000000 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart8250_4>; compatible = "ns8250"; clocks = <&clk14745600>; fsl,weim-cs-timing = <0x0000d603 0x0d1d0d01 0x00d20000>; - interrupts = <&gpio2 30 IRQ_TYPE_LEVEL_LOW>; + interrupts-extended = <&gpio2 30 IRQ_TYPE_LEVEL_LOW>; reg = <3 0x1000000 0x1000>; reg-shift = <1>; reg-io-width = <1>; @@ -154,131 +154,129 @@ }; &iomuxc { - imx27-eukrea-cpuimx27 { - pinctrl_fec: fecgrp { - fsl,pins = < - MX27_PAD_SD3_CMD__FEC_TXD0 0x0 - MX27_PAD_SD3_CLK__FEC_TXD1 0x0 - MX27_PAD_ATA_DATA0__FEC_TXD2 0x0 - MX27_PAD_ATA_DATA1__FEC_TXD3 0x0 - MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0 - MX27_PAD_ATA_DATA3__FEC_RXD1 0x0 - MX27_PAD_ATA_DATA4__FEC_RXD2 0x0 - MX27_PAD_ATA_DATA5__FEC_RXD3 0x0 - MX27_PAD_ATA_DATA6__FEC_MDIO 0x0 - MX27_PAD_ATA_DATA7__FEC_MDC 0x0 - MX27_PAD_ATA_DATA8__FEC_CRS 0x0 - MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0 - MX27_PAD_ATA_DATA10__FEC_RXD0 0x0 - MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0 - MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0 - MX27_PAD_ATA_DATA13__FEC_COL 0x0 - MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0 - MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0 - >; - }; + pinctrl_fec: fecgrp { + fsl,pins = < + MX27_PAD_SD3_CMD__FEC_TXD0 0x0 + MX27_PAD_SD3_CLK__FEC_TXD1 0x0 + MX27_PAD_ATA_DATA0__FEC_TXD2 0x0 + MX27_PAD_ATA_DATA1__FEC_TXD3 0x0 + MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0 + MX27_PAD_ATA_DATA3__FEC_RXD1 0x0 + MX27_PAD_ATA_DATA4__FEC_RXD2 0x0 + MX27_PAD_ATA_DATA5__FEC_RXD3 0x0 + MX27_PAD_ATA_DATA6__FEC_MDIO 0x0 + MX27_PAD_ATA_DATA7__FEC_MDC 0x0 + MX27_PAD_ATA_DATA8__FEC_CRS 0x0 + MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0 + MX27_PAD_ATA_DATA10__FEC_RXD0 0x0 + MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0 + MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0 + MX27_PAD_ATA_DATA13__FEC_COL 0x0 + MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0 + MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0 + >; + }; - pinctrl_i2c1: i2c1grp { - fsl,pins = < - MX27_PAD_I2C_DATA__I2C_DATA 0x0 - MX27_PAD_I2C_CLK__I2C_CLK 0x0 - >; - }; + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX27_PAD_I2C_DATA__I2C_DATA 0x0 + MX27_PAD_I2C_CLK__I2C_CLK 0x0 + >; + }; - pinctrl_nfc: nfcgrp { - fsl,pins = < - MX27_PAD_NFRB__NFRB 0x0 - MX27_PAD_NFCLE__NFCLE 0x0 - MX27_PAD_NFWP_B__NFWP_B 0x0 - MX27_PAD_NFCE_B__NFCE_B 0x0 - MX27_PAD_NFALE__NFALE 0x0 - MX27_PAD_NFRE_B__NFRE_B 0x0 - MX27_PAD_NFWE_B__NFWE_B 0x0 - >; - }; + pinctrl_nfc: nfcgrp { + fsl,pins = < + MX27_PAD_NFRB__NFRB 0x0 + MX27_PAD_NFCLE__NFCLE 0x0 + MX27_PAD_NFWP_B__NFWP_B 0x0 + MX27_PAD_NFCE_B__NFCE_B 0x0 + MX27_PAD_NFALE__NFALE 0x0 + MX27_PAD_NFRE_B__NFRE_B 0x0 + MX27_PAD_NFWE_B__NFWE_B 0x0 + >; + }; - pinctrl_owire: owiregrp { - fsl,pins = < - MX27_PAD_RTCK__OWIRE 0x0 - >; - }; + pinctrl_owire: owiregrp { + fsl,pins = < + MX27_PAD_RTCK__OWIRE 0x0 + >; + }; - pinctrl_sdhc2: sdhc2grp { - fsl,pins = < - MX27_PAD_SD2_CLK__SD2_CLK 0x0 - MX27_PAD_SD2_CMD__SD2_CMD 0x0 - MX27_PAD_SD2_D0__SD2_D0 0x0 - MX27_PAD_SD2_D1__SD2_D1 0x0 - MX27_PAD_SD2_D2__SD2_D2 0x0 - MX27_PAD_SD2_D3__SD2_D3 0x0 - >; - }; + pinctrl_sdhc2: sdhc2grp { + fsl,pins = < + MX27_PAD_SD2_CLK__SD2_CLK 0x0 + MX27_PAD_SD2_CMD__SD2_CMD 0x0 + MX27_PAD_SD2_D0__SD2_D0 0x0 + MX27_PAD_SD2_D1__SD2_D1 0x0 + MX27_PAD_SD2_D2__SD2_D2 0x0 + MX27_PAD_SD2_D3__SD2_D3 0x0 + >; + }; - pinctrl_uart4: uart4grp { - fsl,pins = < - MX27_PAD_USBH1_TXDM__UART4_TXD 0x0 - MX27_PAD_USBH1_RXDP__UART4_RXD 0x0 - MX27_PAD_USBH1_TXDP__UART4_CTS 0x0 - MX27_PAD_USBH1_FS__UART4_RTS 0x0 - >; - }; + pinctrl_uart4: uart4grp { + fsl,pins = < + MX27_PAD_USBH1_TXDM__UART4_TXD 0x0 + MX27_PAD_USBH1_RXDP__UART4_RXD 0x0 + MX27_PAD_USBH1_TXDP__UART4_CTS 0x0 + MX27_PAD_USBH1_FS__UART4_RTS 0x0 + >; + }; - pinctrl_uart8250_1: uart82501grp { - fsl,pins = < - MX27_PAD_USB_PWR__GPIO2_23 0x0 - >; - }; + pinctrl_uart8250_1: uart82501grp { + fsl,pins = < + MX27_PAD_USB_PWR__GPIO2_23 0x0 + >; + }; - pinctrl_uart8250_2: uart82502grp { - fsl,pins = < - MX27_PAD_USBH1_SUSP__GPIO2_22 0x0 - >; - }; + pinctrl_uart8250_2: uart82502grp { + fsl,pins = < + MX27_PAD_USBH1_SUSP__GPIO2_22 0x0 + >; + }; - pinctrl_uart8250_3: uart82503grp { - fsl,pins = < - MX27_PAD_USBH1_OE_B__GPIO2_27 0x0 - >; - }; + pinctrl_uart8250_3: uart82503grp { + fsl,pins = < + MX27_PAD_USBH1_OE_B__GPIO2_27 0x0 + >; + }; - pinctrl_uart8250_4: uart82504grp { - fsl,pins = < - MX27_PAD_USBH1_RXDM__GPIO2_30 0x0 - >; - }; + pinctrl_uart8250_4: uart82504grp { + fsl,pins = < + MX27_PAD_USBH1_RXDM__GPIO2_30 0x0 + >; + }; - pinctrl_usbh2: usbh2grp { - fsl,pins = < - MX27_PAD_USBH2_CLK__USBH2_CLK 0x0 - MX27_PAD_USBH2_DIR__USBH2_DIR 0x0 - MX27_PAD_USBH2_NXT__USBH2_NXT 0x0 - MX27_PAD_USBH2_STP__USBH2_STP 0x0 - MX27_PAD_CSPI2_SCLK__USBH2_DATA0 0x0 - MX27_PAD_CSPI2_MOSI__USBH2_DATA1 0x0 - MX27_PAD_CSPI2_MISO__USBH2_DATA2 0x0 - MX27_PAD_CSPI2_SS1__USBH2_DATA3 0x0 - MX27_PAD_CSPI2_SS2__USBH2_DATA4 0x0 - MX27_PAD_CSPI1_SS2__USBH2_DATA5 0x0 - MX27_PAD_CSPI2_SS0__USBH2_DATA6 0x0 - MX27_PAD_USBH2_DATA7__USBH2_DATA7 0x0 - >; - }; + pinctrl_usbh2: usbh2grp { + fsl,pins = < + MX27_PAD_USBH2_CLK__USBH2_CLK 0x0 + MX27_PAD_USBH2_DIR__USBH2_DIR 0x0 + MX27_PAD_USBH2_NXT__USBH2_NXT 0x0 + MX27_PAD_USBH2_STP__USBH2_STP 0x0 + MX27_PAD_CSPI2_SCLK__USBH2_DATA0 0x0 + MX27_PAD_CSPI2_MOSI__USBH2_DATA1 0x0 + MX27_PAD_CSPI2_MISO__USBH2_DATA2 0x0 + MX27_PAD_CSPI2_SS1__USBH2_DATA3 0x0 + MX27_PAD_CSPI2_SS2__USBH2_DATA4 0x0 + MX27_PAD_CSPI1_SS2__USBH2_DATA5 0x0 + MX27_PAD_CSPI2_SS0__USBH2_DATA6 0x0 + MX27_PAD_USBH2_DATA7__USBH2_DATA7 0x0 + >; + }; - pinctrl_usbotg: usbotggrp { - fsl,pins = < - MX27_PAD_USBOTG_CLK__USBOTG_CLK 0x0 - MX27_PAD_USBOTG_DIR__USBOTG_DIR 0x0 - MX27_PAD_USBOTG_NXT__USBOTG_NXT 0x0 - MX27_PAD_USBOTG_STP__USBOTG_STP 0x0 - MX27_PAD_USBOTG_DATA0__USBOTG_DATA0 0x0 - MX27_PAD_USBOTG_DATA1__USBOTG_DATA1 0x0 - MX27_PAD_USBOTG_DATA2__USBOTG_DATA2 0x0 - MX27_PAD_USBOTG_DATA3__USBOTG_DATA3 0x0 - MX27_PAD_USBOTG_DATA4__USBOTG_DATA4 0x0 - MX27_PAD_USBOTG_DATA5__USBOTG_DATA5 0x0 - MX27_PAD_USBOTG_DATA6__USBOTG_DATA6 0x0 - MX27_PAD_USBOTG_DATA7__USBOTG_DATA7 0x0 - >; - }; + pinctrl_usbotg: usbotggrp { + fsl,pins = < + MX27_PAD_USBOTG_CLK__USBOTG_CLK 0x0 + MX27_PAD_USBOTG_DIR__USBOTG_DIR 0x0 + MX27_PAD_USBOTG_NXT__USBOTG_NXT 0x0 + MX27_PAD_USBOTG_STP__USBOTG_STP 0x0 + MX27_PAD_USBOTG_DATA0__USBOTG_DATA0 0x0 + MX27_PAD_USBOTG_DATA1__USBOTG_DATA1 0x0 + MX27_PAD_USBOTG_DATA2__USBOTG_DATA2 0x0 + MX27_PAD_USBOTG_DATA3__USBOTG_DATA3 0x0 + MX27_PAD_USBOTG_DATA4__USBOTG_DATA4 0x0 + MX27_PAD_USBOTG_DATA5__USBOTG_DATA5 0x0 + MX27_PAD_USBOTG_DATA6__USBOTG_DATA6 0x0 + MX27_PAD_USBOTG_DATA7__USBOTG_DATA7 0x0 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx27-eukrea-mbimxsd27-baseboard.dts b/arch/arm/boot/dts/nxp/imx/imx27-eukrea-mbimxsd27-baseboard.dts index d78793601306..26833ed3339e 100644 --- a/arch/arm/boot/dts/nxp/imx/imx27-eukrea-mbimxsd27-baseboard.dts +++ b/arch/arm/boot/dts/nxp/imx/imx27-eukrea-mbimxsd27-baseboard.dts @@ -76,7 +76,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_touch>; reg = <0>; - interrupts = <&gpio4 25 IRQ_TYPE_LEVEL_LOW>; + interrupts-extended = <&gpio4 25 IRQ_TYPE_LEVEL_LOW>; spi-cpol; spi-max-frequency = <1500000>; ti,keep-vref-on; @@ -147,113 +147,111 @@ }; &iomuxc { - imx27-eukrea-cpuimx27-baseboard { - pinctrl_cspi1: cspi1grp { - fsl,pins = < - MX27_PAD_CSPI1_MISO__CSPI1_MISO 0x0 - MX27_PAD_CSPI1_MOSI__CSPI1_MOSI 0x0 - MX27_PAD_CSPI1_SCLK__CSPI1_SCLK 0x0 - MX27_PAD_CSPI1_SS0__GPIO4_28 0x0 /* CS0 */ - >; - }; + pinctrl_cspi1: cspi1grp { + fsl,pins = < + MX27_PAD_CSPI1_MISO__CSPI1_MISO 0x0 + MX27_PAD_CSPI1_MOSI__CSPI1_MOSI 0x0 + MX27_PAD_CSPI1_SCLK__CSPI1_SCLK 0x0 + MX27_PAD_CSPI1_SS0__GPIO4_28 0x0 /* CS0 */ + >; + }; - pinctrl_backlight: backlightgrp { - fsl,pins = < - MX27_PAD_PWMO__GPIO5_5 0x0 - >; - }; + pinctrl_backlight: backlightgrp { + fsl,pins = < + MX27_PAD_PWMO__GPIO5_5 0x0 + >; + }; - pinctrl_gpioleds: gpioledsgrp { - fsl,pins = < - MX27_PAD_PC_PWRON__GPIO6_16 0x0 - MX27_PAD_PC_CD2_B__GPIO6_19 0x0 - >; - }; + pinctrl_gpioleds: gpioledsgrp { + fsl,pins = < + MX27_PAD_PC_PWRON__GPIO6_16 0x0 + MX27_PAD_PC_CD2_B__GPIO6_19 0x0 + >; + }; - pinctrl_imxfb: imxfbgrp { - fsl,pins = < - MX27_PAD_LD0__LD0 0x0 - MX27_PAD_LD1__LD1 0x0 - MX27_PAD_LD2__LD2 0x0 - MX27_PAD_LD3__LD3 0x0 - MX27_PAD_LD4__LD4 0x0 - MX27_PAD_LD5__LD5 0x0 - MX27_PAD_LD6__LD6 0x0 - MX27_PAD_LD7__LD7 0x0 - MX27_PAD_LD8__LD8 0x0 - MX27_PAD_LD9__LD9 0x0 - MX27_PAD_LD10__LD10 0x0 - MX27_PAD_LD11__LD11 0x0 - MX27_PAD_LD12__LD12 0x0 - MX27_PAD_LD13__LD13 0x0 - MX27_PAD_LD14__LD14 0x0 - MX27_PAD_LD15__LD15 0x0 - MX27_PAD_LD16__LD16 0x0 - MX27_PAD_LD17__LD17 0x0 - MX27_PAD_CONTRAST__CONTRAST 0x0 - MX27_PAD_OE_ACD__OE_ACD 0x0 - MX27_PAD_HSYNC__HSYNC 0x0 - MX27_PAD_VSYNC__VSYNC 0x0 - >; - }; + pinctrl_imxfb: imxfbgrp { + fsl,pins = < + MX27_PAD_LD0__LD0 0x0 + MX27_PAD_LD1__LD1 0x0 + MX27_PAD_LD2__LD2 0x0 + MX27_PAD_LD3__LD3 0x0 + MX27_PAD_LD4__LD4 0x0 + MX27_PAD_LD5__LD5 0x0 + MX27_PAD_LD6__LD6 0x0 + MX27_PAD_LD7__LD7 0x0 + MX27_PAD_LD8__LD8 0x0 + MX27_PAD_LD9__LD9 0x0 + MX27_PAD_LD10__LD10 0x0 + MX27_PAD_LD11__LD11 0x0 + MX27_PAD_LD12__LD12 0x0 + MX27_PAD_LD13__LD13 0x0 + MX27_PAD_LD14__LD14 0x0 + MX27_PAD_LD15__LD15 0x0 + MX27_PAD_LD16__LD16 0x0 + MX27_PAD_LD17__LD17 0x0 + MX27_PAD_CONTRAST__CONTRAST 0x0 + MX27_PAD_OE_ACD__OE_ACD 0x0 + MX27_PAD_HSYNC__HSYNC 0x0 + MX27_PAD_VSYNC__VSYNC 0x0 + >; + }; - pinctrl_lcdreg: lcdreggrp { - fsl,pins = < - MX27_PAD_CLS__GPIO1_25 0x0 - >; - }; + pinctrl_lcdreg: lcdreggrp { + fsl,pins = < + MX27_PAD_CLS__GPIO1_25 0x0 + >; + }; - pinctrl_sdhc1: sdhc1grp { - fsl,pins = < - MX27_PAD_SD1_CLK__SD1_CLK 0x0 - MX27_PAD_SD1_CMD__SD1_CMD 0x0 - MX27_PAD_SD1_D0__SD1_D0 0x0 - MX27_PAD_SD1_D1__SD1_D1 0x0 - MX27_PAD_SD1_D2__SD1_D2 0x0 - MX27_PAD_SD1_D3__SD1_D3 0x0 - >; - }; + pinctrl_sdhc1: sdhc1grp { + fsl,pins = < + MX27_PAD_SD1_CLK__SD1_CLK 0x0 + MX27_PAD_SD1_CMD__SD1_CMD 0x0 + MX27_PAD_SD1_D0__SD1_D0 0x0 + MX27_PAD_SD1_D1__SD1_D1 0x0 + MX27_PAD_SD1_D2__SD1_D2 0x0 + MX27_PAD_SD1_D3__SD1_D3 0x0 + >; + }; - pinctrl_ssi1: ssi1grp { - fsl,pins = < - MX27_PAD_SSI4_CLK__SSI4_CLK 0x0 - MX27_PAD_SSI4_FS__SSI4_FS 0x0 - MX27_PAD_SSI4_RXDAT__SSI4_RXDAT 0x1 - MX27_PAD_SSI4_TXDAT__SSI4_TXDAT 0x1 - >; - }; + pinctrl_ssi1: ssi1grp { + fsl,pins = < + MX27_PAD_SSI4_CLK__SSI4_CLK 0x0 + MX27_PAD_SSI4_FS__SSI4_FS 0x0 + MX27_PAD_SSI4_RXDAT__SSI4_RXDAT 0x1 + MX27_PAD_SSI4_TXDAT__SSI4_TXDAT 0x1 + >; + }; - pinctrl_touch: touchgrp { - fsl,pins = < - MX27_PAD_CSPI1_RDY__GPIO4_25 0x0 /* IRQ */ - >; - }; + pinctrl_touch: touchgrp { + fsl,pins = < + MX27_PAD_CSPI1_RDY__GPIO4_25 0x0 /* IRQ */ + >; + }; - pinctrl_uart1: uart1grp { - fsl,pins = < - MX27_PAD_UART1_TXD__UART1_TXD 0x0 - MX27_PAD_UART1_RXD__UART1_RXD 0x0 - MX27_PAD_UART1_CTS__UART1_CTS 0x0 - MX27_PAD_UART1_RTS__UART1_RTS 0x0 - >; - }; + pinctrl_uart1: uart1grp { + fsl,pins = < + MX27_PAD_UART1_TXD__UART1_TXD 0x0 + MX27_PAD_UART1_RXD__UART1_RXD 0x0 + MX27_PAD_UART1_CTS__UART1_CTS 0x0 + MX27_PAD_UART1_RTS__UART1_RTS 0x0 + >; + }; - pinctrl_uart2: uart2grp { - fsl,pins = < - MX27_PAD_UART2_TXD__UART2_TXD 0x0 - MX27_PAD_UART2_RXD__UART2_RXD 0x0 - MX27_PAD_UART2_CTS__UART2_CTS 0x0 - MX27_PAD_UART2_RTS__UART2_RTS 0x0 - >; - }; + pinctrl_uart2: uart2grp { + fsl,pins = < + MX27_PAD_UART2_TXD__UART2_TXD 0x0 + MX27_PAD_UART2_RXD__UART2_RXD 0x0 + MX27_PAD_UART2_CTS__UART2_CTS 0x0 + MX27_PAD_UART2_RTS__UART2_RTS 0x0 + >; + }; - pinctrl_uart3: uart3grp { - fsl,pins = < - MX27_PAD_UART3_TXD__UART3_TXD 0x0 - MX27_PAD_UART3_RXD__UART3_RXD 0x0 - MX27_PAD_UART3_CTS__UART3_CTS 0x0 - MX27_PAD_UART3_RTS__UART3_RTS 0x0 - >; - }; + pinctrl_uart3: uart3grp { + fsl,pins = < + MX27_PAD_UART3_TXD__UART3_TXD 0x0 + MX27_PAD_UART3_RXD__UART3_RXD 0x0 + MX27_PAD_UART3_CTS__UART3_CTS 0x0 + MX27_PAD_UART3_RTS__UART3_RTS 0x0 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx27-pdk.dts b/arch/arm/boot/dts/nxp/imx/imx27-pdk.dts index 21d436972aa4..2fc4ea5b9501 100644 --- a/arch/arm/boot/dts/nxp/imx/imx27-pdk.dts +++ b/arch/arm/boot/dts/nxp/imx/imx27-pdk.dts @@ -110,76 +110,74 @@ }; &iomuxc { - imx27-pdk { - pinctrl_cspi2: cspi2grp { - fsl,pins = < - MX27_PAD_CSPI2_MISO__CSPI2_MISO 0x0 - MX27_PAD_CSPI2_MOSI__CSPI2_MOSI 0x0 - MX27_PAD_CSPI2_SCLK__CSPI2_SCLK 0x0 - MX27_PAD_CSPI2_SS0__GPIO4_21 0x0 /* SPI2 CS0 */ - MX27_PAD_TOUT__GPIO3_14 0x0 /* PMIC IRQ */ - >; - }; + pinctrl_cspi2: cspi2grp { + fsl,pins = < + MX27_PAD_CSPI2_MISO__CSPI2_MISO 0x0 + MX27_PAD_CSPI2_MOSI__CSPI2_MOSI 0x0 + MX27_PAD_CSPI2_SCLK__CSPI2_SCLK 0x0 + MX27_PAD_CSPI2_SS0__GPIO4_21 0x0 /* SPI2 CS0 */ + MX27_PAD_TOUT__GPIO3_14 0x0 /* PMIC IRQ */ + >; + }; - pinctrl_fec: fecgrp { - fsl,pins = < - MX27_PAD_SD3_CMD__FEC_TXD0 0x0 - MX27_PAD_SD3_CLK__FEC_TXD1 0x0 - MX27_PAD_ATA_DATA0__FEC_TXD2 0x0 - MX27_PAD_ATA_DATA1__FEC_TXD3 0x0 - MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0 - MX27_PAD_ATA_DATA3__FEC_RXD1 0x0 - MX27_PAD_ATA_DATA4__FEC_RXD2 0x0 - MX27_PAD_ATA_DATA5__FEC_RXD3 0x0 - MX27_PAD_ATA_DATA6__FEC_MDIO 0x0 - MX27_PAD_ATA_DATA7__FEC_MDC 0x0 - MX27_PAD_ATA_DATA8__FEC_CRS 0x0 - MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0 - MX27_PAD_ATA_DATA10__FEC_RXD0 0x0 - MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0 - MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0 - MX27_PAD_ATA_DATA13__FEC_COL 0x0 - MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0 - MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0 - >; - }; + pinctrl_fec: fecgrp { + fsl,pins = < + MX27_PAD_SD3_CMD__FEC_TXD0 0x0 + MX27_PAD_SD3_CLK__FEC_TXD1 0x0 + MX27_PAD_ATA_DATA0__FEC_TXD2 0x0 + MX27_PAD_ATA_DATA1__FEC_TXD3 0x0 + MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0 + MX27_PAD_ATA_DATA3__FEC_RXD1 0x0 + MX27_PAD_ATA_DATA4__FEC_RXD2 0x0 + MX27_PAD_ATA_DATA5__FEC_RXD3 0x0 + MX27_PAD_ATA_DATA6__FEC_MDIO 0x0 + MX27_PAD_ATA_DATA7__FEC_MDC 0x0 + MX27_PAD_ATA_DATA8__FEC_CRS 0x0 + MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0 + MX27_PAD_ATA_DATA10__FEC_RXD0 0x0 + MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0 + MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0 + MX27_PAD_ATA_DATA13__FEC_COL 0x0 + MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0 + MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0 + >; + }; - pinctrl_nand: nandgrp { - fsl,pins = < - MX27_PAD_NFRB__NFRB 0x0 - MX27_PAD_NFCLE__NFCLE 0x0 - MX27_PAD_NFWP_B__NFWP_B 0x0 - MX27_PAD_NFCE_B__NFCE_B 0x0 - MX27_PAD_NFALE__NFALE 0x0 - MX27_PAD_NFRE_B__NFRE_B 0x0 - MX27_PAD_NFWE_B__NFWE_B 0x0 - >; - }; + pinctrl_nand: nandgrp { + fsl,pins = < + MX27_PAD_NFRB__NFRB 0x0 + MX27_PAD_NFCLE__NFCLE 0x0 + MX27_PAD_NFWP_B__NFWP_B 0x0 + MX27_PAD_NFCE_B__NFCE_B 0x0 + MX27_PAD_NFALE__NFALE 0x0 + MX27_PAD_NFRE_B__NFRE_B 0x0 + MX27_PAD_NFWE_B__NFWE_B 0x0 + >; + }; - pinctrl_uart1: uart1grp { - fsl,pins = < - MX27_PAD_UART1_TXD__UART1_TXD 0x0 - MX27_PAD_UART1_RXD__UART1_RXD 0x0 - MX27_PAD_UART1_CTS__UART1_CTS 0x0 - MX27_PAD_UART1_RTS__UART1_RTS 0x0 - >; - }; + pinctrl_uart1: uart1grp { + fsl,pins = < + MX27_PAD_UART1_TXD__UART1_TXD 0x0 + MX27_PAD_UART1_RXD__UART1_RXD 0x0 + MX27_PAD_UART1_CTS__UART1_CTS 0x0 + MX27_PAD_UART1_RTS__UART1_RTS 0x0 + >; + }; - pinctrl_usbotg: usbotggrp { - fsl,pins = < - MX27_PAD_USBOTG_NXT__USBOTG_NXT 0x0 - MX27_PAD_USBOTG_STP__USBOTG_STP 0x0 - MX27_PAD_USBOTG_DIR__USBOTG_DIR 0x0 - MX27_PAD_USBOTG_CLK__USBOTG_CLK 0x0 - MX27_PAD_USBOTG_DATA0__USBOTG_DATA0 0x0 - MX27_PAD_USBOTG_DATA1__USBOTG_DATA1 0x0 - MX27_PAD_USBOTG_DATA2__USBOTG_DATA2 0x0 - MX27_PAD_USBOTG_DATA3__USBOTG_DATA3 0x0 - MX27_PAD_USBOTG_DATA4__USBOTG_DATA4 0x0 - MX27_PAD_USBOTG_DATA5__USBOTG_DATA5 0x0 - MX27_PAD_USBOTG_DATA6__USBOTG_DATA6 0x0 - MX27_PAD_USBOTG_DATA7__USBOTG_DATA7 0x0 - >; - }; + pinctrl_usbotg: usbotggrp { + fsl,pins = < + MX27_PAD_USBOTG_NXT__USBOTG_NXT 0x0 + MX27_PAD_USBOTG_STP__USBOTG_STP 0x0 + MX27_PAD_USBOTG_DIR__USBOTG_DIR 0x0 + MX27_PAD_USBOTG_CLK__USBOTG_CLK 0x0 + MX27_PAD_USBOTG_DATA0__USBOTG_DATA0 0x0 + MX27_PAD_USBOTG_DATA1__USBOTG_DATA1 0x0 + MX27_PAD_USBOTG_DATA2__USBOTG_DATA2 0x0 + MX27_PAD_USBOTG_DATA3__USBOTG_DATA3 0x0 + MX27_PAD_USBOTG_DATA4__USBOTG_DATA4 0x0 + MX27_PAD_USBOTG_DATA5__USBOTG_DATA5 0x0 + MX27_PAD_USBOTG_DATA6__USBOTG_DATA6 0x0 + MX27_PAD_USBOTG_DATA7__USBOTG_DATA7 0x0 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycard-s-rdk.dts b/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycard-s-rdk.dts index 27c93b9fe049..2b884cb3e381 100644 --- a/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycard-s-rdk.dts +++ b/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycard-s-rdk.dts @@ -65,58 +65,56 @@ }; &iomuxc { - imx27-phycard-s-rdk { - pinctrl_i2c1: i2c1grp { - fsl,pins = < - MX27_PAD_I2C_DATA__I2C_DATA 0x0 - MX27_PAD_I2C_CLK__I2C_CLK 0x0 - >; - }; + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX27_PAD_I2C_DATA__I2C_DATA 0x0 + MX27_PAD_I2C_CLK__I2C_CLK 0x0 + >; + }; - pinctrl_owire1: owire1grp { - fsl,pins = < - MX27_PAD_RTCK__OWIRE 0x0 - >; - }; + pinctrl_owire1: owire1grp { + fsl,pins = < + MX27_PAD_RTCK__OWIRE 0x0 + >; + }; - pinctrl_sdhc2: sdhc2grp { - fsl,pins = < - MX27_PAD_SD2_CLK__SD2_CLK 0x0 - MX27_PAD_SD2_CMD__SD2_CMD 0x0 - MX27_PAD_SD2_D0__SD2_D0 0x0 - MX27_PAD_SD2_D1__SD2_D1 0x0 - MX27_PAD_SD2_D2__SD2_D2 0x0 - MX27_PAD_SD2_D3__SD2_D3 0x0 - MX27_PAD_SSI3_RXDAT__GPIO3_29 0x0 /* CD */ - >; - }; + pinctrl_sdhc2: sdhc2grp { + fsl,pins = < + MX27_PAD_SD2_CLK__SD2_CLK 0x0 + MX27_PAD_SD2_CMD__SD2_CMD 0x0 + MX27_PAD_SD2_D0__SD2_D0 0x0 + MX27_PAD_SD2_D1__SD2_D1 0x0 + MX27_PAD_SD2_D2__SD2_D2 0x0 + MX27_PAD_SD2_D3__SD2_D3 0x0 + MX27_PAD_SSI3_RXDAT__GPIO3_29 0x0 /* CD */ + >; + }; - pinctrl_uart1: uart1grp { - fsl,pins = < - MX27_PAD_UART1_TXD__UART1_TXD 0x0 - MX27_PAD_UART1_RXD__UART1_RXD 0x0 - MX27_PAD_UART1_CTS__UART1_CTS 0x0 - MX27_PAD_UART1_RTS__UART1_RTS 0x0 - >; - }; + pinctrl_uart1: uart1grp { + fsl,pins = < + MX27_PAD_UART1_TXD__UART1_TXD 0x0 + MX27_PAD_UART1_RXD__UART1_RXD 0x0 + MX27_PAD_UART1_CTS__UART1_CTS 0x0 + MX27_PAD_UART1_RTS__UART1_RTS 0x0 + >; + }; - pinctrl_uart2: uart2grp { - fsl,pins = < - MX27_PAD_UART2_TXD__UART2_TXD 0x0 - MX27_PAD_UART2_RXD__UART2_RXD 0x0 - MX27_PAD_UART2_CTS__UART2_CTS 0x0 - MX27_PAD_UART2_RTS__UART2_RTS 0x0 - >; - }; + pinctrl_uart2: uart2grp { + fsl,pins = < + MX27_PAD_UART2_TXD__UART2_TXD 0x0 + MX27_PAD_UART2_RXD__UART2_RXD 0x0 + MX27_PAD_UART2_CTS__UART2_CTS 0x0 + MX27_PAD_UART2_RTS__UART2_RTS 0x0 + >; + }; - pinctrl_uart3: uart3grp { - fsl,pins = < - MX27_PAD_UART3_TXD__UART3_TXD 0x0 - MX27_PAD_UART3_RXD__UART3_RXD 0x0 - MX27_PAD_UART3_CTS__UART3_CTS 0x0 - MX27_PAD_UART3_RTS__UART3_RTS 0x0 - >; - }; + pinctrl_uart3: uart3grp { + fsl,pins = < + MX27_PAD_UART3_TXD__UART3_TXD 0x0 + MX27_PAD_UART3_RXD__UART3_RXD 0x0 + MX27_PAD_UART3_CTS__UART3_CTS 0x0 + MX27_PAD_UART3_RTS__UART3_RTS 0x0 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycard-s-som.dtsi b/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycard-s-som.dtsi index 31b3fc972abb..2f60b3809f39 100644 --- a/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycard-s-som.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycard-s-som.dtsi @@ -58,94 +58,92 @@ }; &iomuxc { - imx27-phycard-s-som { - pinctrl_fec1: fec1grp { - fsl,pins = < - MX27_PAD_SD3_CMD__FEC_TXD0 0x0 - MX27_PAD_SD3_CLK__FEC_TXD1 0x0 - MX27_PAD_ATA_DATA0__FEC_TXD2 0x0 - MX27_PAD_ATA_DATA1__FEC_TXD3 0x0 - MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0 - MX27_PAD_ATA_DATA3__FEC_RXD1 0x0 - MX27_PAD_ATA_DATA4__FEC_RXD2 0x0 - MX27_PAD_ATA_DATA5__FEC_RXD3 0x0 - MX27_PAD_ATA_DATA6__FEC_MDIO 0x0 - MX27_PAD_ATA_DATA7__FEC_MDC 0x0 - MX27_PAD_ATA_DATA8__FEC_CRS 0x0 - MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0 - MX27_PAD_ATA_DATA10__FEC_RXD0 0x0 - MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0 - MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0 - MX27_PAD_ATA_DATA13__FEC_COL 0x0 - MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0 - MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0 - >; - }; + pinctrl_fec1: fec1grp { + fsl,pins = < + MX27_PAD_SD3_CMD__FEC_TXD0 0x0 + MX27_PAD_SD3_CLK__FEC_TXD1 0x0 + MX27_PAD_ATA_DATA0__FEC_TXD2 0x0 + MX27_PAD_ATA_DATA1__FEC_TXD3 0x0 + MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0 + MX27_PAD_ATA_DATA3__FEC_RXD1 0x0 + MX27_PAD_ATA_DATA4__FEC_RXD2 0x0 + MX27_PAD_ATA_DATA5__FEC_RXD3 0x0 + MX27_PAD_ATA_DATA6__FEC_MDIO 0x0 + MX27_PAD_ATA_DATA7__FEC_MDC 0x0 + MX27_PAD_ATA_DATA8__FEC_CRS 0x0 + MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0 + MX27_PAD_ATA_DATA10__FEC_RXD0 0x0 + MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0 + MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0 + MX27_PAD_ATA_DATA13__FEC_COL 0x0 + MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0 + MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0 + >; + }; - pinctrl_i2c2: i2c2grp { - fsl,pins = < - MX27_PAD_I2C2_SDA__I2C2_SDA 0x0 - MX27_PAD_I2C2_SCL__I2C2_SCL 0x0 - >; - }; + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX27_PAD_I2C2_SDA__I2C2_SDA 0x0 + MX27_PAD_I2C2_SCL__I2C2_SCL 0x0 + >; + }; - pinctrl_nfc: nfcgrp { - fsl,pins = < - MX27_PAD_NFRB__NFRB 0x0 - MX27_PAD_NFCLE__NFCLE 0x0 - MX27_PAD_NFWP_B__NFWP_B 0x0 - MX27_PAD_NFCE_B__NFCE_B 0x0 - MX27_PAD_NFALE__NFALE 0x0 - MX27_PAD_NFRE_B__NFRE_B 0x0 - MX27_PAD_NFWE_B__NFWE_B 0x0 - >; - }; + pinctrl_nfc: nfcgrp { + fsl,pins = < + MX27_PAD_NFRB__NFRB 0x0 + MX27_PAD_NFCLE__NFCLE 0x0 + MX27_PAD_NFWP_B__NFWP_B 0x0 + MX27_PAD_NFCE_B__NFCE_B 0x0 + MX27_PAD_NFALE__NFALE 0x0 + MX27_PAD_NFRE_B__NFRE_B 0x0 + MX27_PAD_NFWE_B__NFWE_B 0x0 + >; + }; - pinctrl_usbotgphy: usbotgphygrp { - fsl,pins = < - MX27_PAD_USBH1_RCV__GPIO2_25 0x1 /* reset gpio */ - >; - }; + pinctrl_usbotgphy: usbotgphygrp { + fsl,pins = < + MX27_PAD_USBH1_RCV__GPIO2_25 0x1 /* reset gpio */ + >; + }; - pinctrl_usbotg: usbotggrp { - fsl,pins = < - MX27_PAD_USBOTG_CLK__USBOTG_CLK 0x0 - MX27_PAD_USBOTG_DIR__USBOTG_DIR 0x0 - MX27_PAD_USBOTG_NXT__USBOTG_NXT 0x0 - MX27_PAD_USBOTG_STP__USBOTG_STP 0x0 - MX27_PAD_USBOTG_DATA0__USBOTG_DATA0 0x0 - MX27_PAD_USBOTG_DATA1__USBOTG_DATA1 0x0 - MX27_PAD_USBOTG_DATA2__USBOTG_DATA2 0x0 - MX27_PAD_USBOTG_DATA3__USBOTG_DATA3 0x0 - MX27_PAD_USBOTG_DATA4__USBOTG_DATA4 0x0 - MX27_PAD_USBOTG_DATA5__USBOTG_DATA5 0x0 - MX27_PAD_USBOTG_DATA6__USBOTG_DATA6 0x0 - MX27_PAD_USBOTG_DATA7__USBOTG_DATA7 0x0 - >; - }; + pinctrl_usbotg: usbotggrp { + fsl,pins = < + MX27_PAD_USBOTG_CLK__USBOTG_CLK 0x0 + MX27_PAD_USBOTG_DIR__USBOTG_DIR 0x0 + MX27_PAD_USBOTG_NXT__USBOTG_NXT 0x0 + MX27_PAD_USBOTG_STP__USBOTG_STP 0x0 + MX27_PAD_USBOTG_DATA0__USBOTG_DATA0 0x0 + MX27_PAD_USBOTG_DATA1__USBOTG_DATA1 0x0 + MX27_PAD_USBOTG_DATA2__USBOTG_DATA2 0x0 + MX27_PAD_USBOTG_DATA3__USBOTG_DATA3 0x0 + MX27_PAD_USBOTG_DATA4__USBOTG_DATA4 0x0 + MX27_PAD_USBOTG_DATA5__USBOTG_DATA5 0x0 + MX27_PAD_USBOTG_DATA6__USBOTG_DATA6 0x0 + MX27_PAD_USBOTG_DATA7__USBOTG_DATA7 0x0 + >; + }; - pinctrl_usbh2phy: usbh2phygrp { - fsl,pins = < - MX27_PAD_USBH1_SUSP__GPIO2_22 0x0 /* reset gpio */ - >; - }; + pinctrl_usbh2phy: usbh2phygrp { + fsl,pins = < + MX27_PAD_USBH1_SUSP__GPIO2_22 0x0 /* reset gpio */ + >; + }; - pinctrl_usbh2: usbh2grp { - fsl,pins = < - MX27_PAD_USBH2_CLK__USBH2_CLK 0x0 - MX27_PAD_USBH2_DIR__USBH2_DIR 0x0 - MX27_PAD_USBH2_NXT__USBH2_NXT 0x0 - MX27_PAD_USBH2_STP__USBH2_STP 0x0 - MX27_PAD_CSPI2_SCLK__USBH2_DATA0 0x0 - MX27_PAD_CSPI2_MOSI__USBH2_DATA1 0x0 - MX27_PAD_CSPI2_MISO__USBH2_DATA2 0x0 - MX27_PAD_CSPI2_SS1__USBH2_DATA3 0x0 - MX27_PAD_CSPI2_SS2__USBH2_DATA4 0x0 - MX27_PAD_CSPI1_SS2__USBH2_DATA5 0x0 - MX27_PAD_CSPI2_SS0__USBH2_DATA6 0x0 - MX27_PAD_USBH2_DATA7__USBH2_DATA7 0x0 - >; - }; + pinctrl_usbh2: usbh2grp { + fsl,pins = < + MX27_PAD_USBH2_CLK__USBH2_CLK 0x0 + MX27_PAD_USBH2_DIR__USBH2_DIR 0x0 + MX27_PAD_USBH2_NXT__USBH2_NXT 0x0 + MX27_PAD_USBH2_STP__USBH2_STP 0x0 + MX27_PAD_CSPI2_SCLK__USBH2_DATA0 0x0 + MX27_PAD_CSPI2_MOSI__USBH2_DATA1 0x0 + MX27_PAD_CSPI2_MISO__USBH2_DATA2 0x0 + MX27_PAD_CSPI2_SS1__USBH2_DATA3 0x0 + MX27_PAD_CSPI2_SS2__USBH2_DATA4 0x0 + MX27_PAD_CSPI1_SS2__USBH2_DATA5 0x0 + MX27_PAD_CSPI2_SS0__USBH2_DATA6 0x0 + MX27_PAD_USBH2_DATA7__USBH2_DATA7 0x0 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycore-rdk.dts b/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycore-rdk.dts index 5398e9067e60..d7136c399ae2 100644 --- a/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycore-rdk.dts +++ b/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycore-rdk.dts @@ -89,119 +89,117 @@ }; &iomuxc { - imx27_phycore_rdk { - pinctrl_csien: csiengrp { - fsl,pins = < - MX27_PAD_USB_OC_B__GPIO2_24 0x0 - >; - }; + pinctrl_csien: csiengrp { + fsl,pins = < + MX27_PAD_USB_OC_B__GPIO2_24 0x0 + >; + }; - pinctrl_cspi1cs1: cspi1cs1grp { - fsl,pins = < - MX27_PAD_CSPI1_SS1__GPIO4_27 0x0 - >; - }; + pinctrl_cspi1cs1: cspi1cs1grp { + fsl,pins = < + MX27_PAD_CSPI1_SS1__GPIO4_27 0x0 + >; + }; - pinctrl_imxfb1: imxfbgrp { - fsl,pins = < - MX27_PAD_LD0__LD0 0x0 - MX27_PAD_LD1__LD1 0x0 - MX27_PAD_LD2__LD2 0x0 - MX27_PAD_LD3__LD3 0x0 - MX27_PAD_LD4__LD4 0x0 - MX27_PAD_LD5__LD5 0x0 - MX27_PAD_LD6__LD6 0x0 - MX27_PAD_LD7__LD7 0x0 - MX27_PAD_LD8__LD8 0x0 - MX27_PAD_LD9__LD9 0x0 - MX27_PAD_LD10__LD10 0x0 - MX27_PAD_LD11__LD11 0x0 - MX27_PAD_LD12__LD12 0x0 - MX27_PAD_LD13__LD13 0x0 - MX27_PAD_LD14__LD14 0x0 - MX27_PAD_LD15__LD15 0x0 - MX27_PAD_LD16__LD16 0x0 - MX27_PAD_LD17__LD17 0x0 - MX27_PAD_CLS__CLS 0x0 - MX27_PAD_CONTRAST__CONTRAST 0x0 - MX27_PAD_LSCLK__LSCLK 0x0 - MX27_PAD_OE_ACD__OE_ACD 0x0 - MX27_PAD_PS__PS 0x0 - MX27_PAD_REV__REV 0x0 - MX27_PAD_SPL_SPR__SPL_SPR 0x0 - MX27_PAD_HSYNC__HSYNC 0x0 - MX27_PAD_VSYNC__VSYNC 0x0 - >; - }; + pinctrl_imxfb1: imxfbgrp { + fsl,pins = < + MX27_PAD_LD0__LD0 0x0 + MX27_PAD_LD1__LD1 0x0 + MX27_PAD_LD2__LD2 0x0 + MX27_PAD_LD3__LD3 0x0 + MX27_PAD_LD4__LD4 0x0 + MX27_PAD_LD5__LD5 0x0 + MX27_PAD_LD6__LD6 0x0 + MX27_PAD_LD7__LD7 0x0 + MX27_PAD_LD8__LD8 0x0 + MX27_PAD_LD9__LD9 0x0 + MX27_PAD_LD10__LD10 0x0 + MX27_PAD_LD11__LD11 0x0 + MX27_PAD_LD12__LD12 0x0 + MX27_PAD_LD13__LD13 0x0 + MX27_PAD_LD14__LD14 0x0 + MX27_PAD_LD15__LD15 0x0 + MX27_PAD_LD16__LD16 0x0 + MX27_PAD_LD17__LD17 0x0 + MX27_PAD_CLS__CLS 0x0 + MX27_PAD_CONTRAST__CONTRAST 0x0 + MX27_PAD_LSCLK__LSCLK 0x0 + MX27_PAD_OE_ACD__OE_ACD 0x0 + MX27_PAD_PS__PS 0x0 + MX27_PAD_REV__REV 0x0 + MX27_PAD_SPL_SPR__SPL_SPR 0x0 + MX27_PAD_HSYNC__HSYNC 0x0 + MX27_PAD_VSYNC__VSYNC 0x0 + >; + }; - pinctrl_i2c1: i2c1grp { - /* Add pullup to DATA line */ - fsl,pins = < - MX27_PAD_I2C_DATA__I2C_DATA 0x1 - MX27_PAD_I2C_CLK__I2C_CLK 0x0 - >; - }; + pinctrl_i2c1: i2c1grp { + /* Add pullup to DATA line */ + fsl,pins = < + MX27_PAD_I2C_DATA__I2C_DATA 0x1 + MX27_PAD_I2C_CLK__I2C_CLK 0x0 + >; + }; - pinctrl_owire1: owire1grp { - fsl,pins = < - MX27_PAD_RTCK__OWIRE 0x0 - >; - }; + pinctrl_owire1: owire1grp { + fsl,pins = < + MX27_PAD_RTCK__OWIRE 0x0 + >; + }; - pinctrl_sdhc2: sdhc2grp { - fsl,pins = < - MX27_PAD_SD2_CLK__SD2_CLK 0x0 - MX27_PAD_SD2_CMD__SD2_CMD 0x0 - MX27_PAD_SD2_D0__SD2_D0 0x0 - MX27_PAD_SD2_D1__SD2_D1 0x0 - MX27_PAD_SD2_D2__SD2_D2 0x0 - MX27_PAD_SD2_D3__SD2_D3 0x0 - MX27_PAD_SSI3_FS__GPIO3_28 0x0 /* WP */ - MX27_PAD_SSI3_RXDAT__GPIO3_29 0x0 /* CD */ - >; - }; + pinctrl_sdhc2: sdhc2grp { + fsl,pins = < + MX27_PAD_SD2_CLK__SD2_CLK 0x0 + MX27_PAD_SD2_CMD__SD2_CMD 0x0 + MX27_PAD_SD2_D0__SD2_D0 0x0 + MX27_PAD_SD2_D1__SD2_D1 0x0 + MX27_PAD_SD2_D2__SD2_D2 0x0 + MX27_PAD_SD2_D3__SD2_D3 0x0 + MX27_PAD_SSI3_FS__GPIO3_28 0x0 /* WP */ + MX27_PAD_SSI3_RXDAT__GPIO3_29 0x0 /* CD */ + >; + }; - pinctrl_uart1: uart1grp { - fsl,pins = < - MX27_PAD_UART1_TXD__UART1_TXD 0x0 - MX27_PAD_UART1_RXD__UART1_RXD 0x0 - MX27_PAD_UART1_CTS__UART1_CTS 0x0 - MX27_PAD_UART1_RTS__UART1_RTS 0x0 - >; - }; + pinctrl_uart1: uart1grp { + fsl,pins = < + MX27_PAD_UART1_TXD__UART1_TXD 0x0 + MX27_PAD_UART1_RXD__UART1_RXD 0x0 + MX27_PAD_UART1_CTS__UART1_CTS 0x0 + MX27_PAD_UART1_RTS__UART1_RTS 0x0 + >; + }; - pinctrl_uart2: uart2grp { - fsl,pins = < - MX27_PAD_UART2_TXD__UART2_TXD 0x0 - MX27_PAD_UART2_RXD__UART2_RXD 0x0 - MX27_PAD_UART2_CTS__UART2_CTS 0x0 - MX27_PAD_UART2_RTS__UART2_RTS 0x0 - >; - }; + pinctrl_uart2: uart2grp { + fsl,pins = < + MX27_PAD_UART2_TXD__UART2_TXD 0x0 + MX27_PAD_UART2_RXD__UART2_RXD 0x0 + MX27_PAD_UART2_CTS__UART2_CTS 0x0 + MX27_PAD_UART2_RTS__UART2_RTS 0x0 + >; + }; - pinctrl_usbh2: usbh2grp { - fsl,pins = < - MX27_PAD_USBH2_CLK__USBH2_CLK 0x0 - MX27_PAD_USBH2_DIR__USBH2_DIR 0x0 - MX27_PAD_USBH2_NXT__USBH2_NXT 0x0 - MX27_PAD_USBH2_STP__USBH2_STP 0x0 - MX27_PAD_CSPI2_SCLK__USBH2_DATA0 0x0 - MX27_PAD_CSPI2_MOSI__USBH2_DATA1 0x0 - MX27_PAD_CSPI2_MISO__USBH2_DATA2 0x0 - MX27_PAD_CSPI2_SS1__USBH2_DATA3 0x0 - MX27_PAD_CSPI2_SS2__USBH2_DATA4 0x0 - MX27_PAD_CSPI1_SS2__USBH2_DATA5 0x0 - MX27_PAD_CSPI2_SS0__USBH2_DATA6 0x0 - MX27_PAD_USBH2_DATA7__USBH2_DATA7 0x0 - >; - }; + pinctrl_usbh2: usbh2grp { + fsl,pins = < + MX27_PAD_USBH2_CLK__USBH2_CLK 0x0 + MX27_PAD_USBH2_DIR__USBH2_DIR 0x0 + MX27_PAD_USBH2_NXT__USBH2_NXT 0x0 + MX27_PAD_USBH2_STP__USBH2_STP 0x0 + MX27_PAD_CSPI2_SCLK__USBH2_DATA0 0x0 + MX27_PAD_CSPI2_MOSI__USBH2_DATA1 0x0 + MX27_PAD_CSPI2_MISO__USBH2_DATA2 0x0 + MX27_PAD_CSPI2_SS1__USBH2_DATA3 0x0 + MX27_PAD_CSPI2_SS2__USBH2_DATA4 0x0 + MX27_PAD_CSPI1_SS2__USBH2_DATA5 0x0 + MX27_PAD_CSPI2_SS0__USBH2_DATA6 0x0 + MX27_PAD_USBH2_DATA7__USBH2_DATA7 0x0 + >; + }; - pinctrl_weim: weimgrp { - fsl,pins = < - MX27_PAD_CS4_B__CS4_B 0x0 /* CS4 */ - MX27_PAD_SD1_D1__GPIO5_19 0x0 /* CAN IRQ */ - >; - }; + pinctrl_weim: weimgrp { + fsl,pins = < + MX27_PAD_CS4_B__CS4_B 0x0 /* CS4 */ + MX27_PAD_SD1_D1__GPIO5_19 0x0 /* CAN IRQ */ + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycore-som.dtsi b/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycore-som.dtsi index e958d7286ae9..7d5d24c781b9 100644 --- a/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycore-som.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx27-phytec-phycore-som.dtsi @@ -192,90 +192,88 @@ }; &iomuxc { - imx27_phycore_som { - pinctrl_cspi1: cspi1grp { - fsl,pins = < - MX27_PAD_CSPI1_MISO__CSPI1_MISO 0x0 - MX27_PAD_CSPI1_MOSI__CSPI1_MOSI 0x0 - MX27_PAD_CSPI1_SCLK__CSPI1_SCLK 0x0 - MX27_PAD_CSPI1_SS0__GPIO4_28 0x0 /* SPI1 CS0 */ - >; - }; + pinctrl_cspi1: cspi1grp { + fsl,pins = < + MX27_PAD_CSPI1_MISO__CSPI1_MISO 0x0 + MX27_PAD_CSPI1_MOSI__CSPI1_MOSI 0x0 + MX27_PAD_CSPI1_SCLK__CSPI1_SCLK 0x0 + MX27_PAD_CSPI1_SS0__GPIO4_28 0x0 /* SPI1 CS0 */ + >; + }; - pinctrl_fec1: fec1grp { - fsl,pins = < - MX27_PAD_SD3_CMD__FEC_TXD0 0x0 - MX27_PAD_SD3_CLK__FEC_TXD1 0x0 - MX27_PAD_ATA_DATA0__FEC_TXD2 0x0 - MX27_PAD_ATA_DATA1__FEC_TXD3 0x0 - MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0 - MX27_PAD_ATA_DATA3__FEC_RXD1 0x0 - MX27_PAD_ATA_DATA4__FEC_RXD2 0x0 - MX27_PAD_ATA_DATA5__FEC_RXD3 0x0 - MX27_PAD_ATA_DATA6__FEC_MDIO 0x0 - MX27_PAD_ATA_DATA7__FEC_MDC 0x0 - MX27_PAD_ATA_DATA8__FEC_CRS 0x0 - MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0 - MX27_PAD_ATA_DATA10__FEC_RXD0 0x0 - MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0 - MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0 - MX27_PAD_ATA_DATA13__FEC_COL 0x0 - MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0 - MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0 - MX27_PAD_SSI3_TXDAT__GPIO3_30 0x0 /* FEC RST */ - >; - }; + pinctrl_fec1: fec1grp { + fsl,pins = < + MX27_PAD_SD3_CMD__FEC_TXD0 0x0 + MX27_PAD_SD3_CLK__FEC_TXD1 0x0 + MX27_PAD_ATA_DATA0__FEC_TXD2 0x0 + MX27_PAD_ATA_DATA1__FEC_TXD3 0x0 + MX27_PAD_ATA_DATA2__FEC_RX_ER 0x0 + MX27_PAD_ATA_DATA3__FEC_RXD1 0x0 + MX27_PAD_ATA_DATA4__FEC_RXD2 0x0 + MX27_PAD_ATA_DATA5__FEC_RXD3 0x0 + MX27_PAD_ATA_DATA6__FEC_MDIO 0x0 + MX27_PAD_ATA_DATA7__FEC_MDC 0x0 + MX27_PAD_ATA_DATA8__FEC_CRS 0x0 + MX27_PAD_ATA_DATA9__FEC_TX_CLK 0x0 + MX27_PAD_ATA_DATA10__FEC_RXD0 0x0 + MX27_PAD_ATA_DATA11__FEC_RX_DV 0x0 + MX27_PAD_ATA_DATA12__FEC_RX_CLK 0x0 + MX27_PAD_ATA_DATA13__FEC_COL 0x0 + MX27_PAD_ATA_DATA14__FEC_TX_ER 0x0 + MX27_PAD_ATA_DATA15__FEC_TX_EN 0x0 + MX27_PAD_SSI3_TXDAT__GPIO3_30 0x0 /* FEC RST */ + >; + }; - pinctrl_i2c2: i2c2grp { - fsl,pins = < - MX27_PAD_I2C2_SDA__I2C2_SDA 0x0 - MX27_PAD_I2C2_SCL__I2C2_SCL 0x0 - >; - }; + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX27_PAD_I2C2_SDA__I2C2_SDA 0x0 + MX27_PAD_I2C2_SCL__I2C2_SCL 0x0 + >; + }; - pinctrl_nfc: nfcgrp { - fsl,pins = < - MX27_PAD_NFRB__NFRB 0x0 - MX27_PAD_NFCLE__NFCLE 0x0 - MX27_PAD_NFWP_B__NFWP_B 0x0 - MX27_PAD_NFCE_B__NFCE_B 0x0 - MX27_PAD_NFALE__NFALE 0x0 - MX27_PAD_NFRE_B__NFRE_B 0x0 - MX27_PAD_NFWE_B__NFWE_B 0x0 - >; - }; + pinctrl_nfc: nfcgrp { + fsl,pins = < + MX27_PAD_NFRB__NFRB 0x0 + MX27_PAD_NFCLE__NFCLE 0x0 + MX27_PAD_NFWP_B__NFWP_B 0x0 + MX27_PAD_NFCE_B__NFCE_B 0x0 + MX27_PAD_NFALE__NFALE 0x0 + MX27_PAD_NFRE_B__NFRE_B 0x0 + MX27_PAD_NFWE_B__NFWE_B 0x0 + >; + }; - pinctrl_pmic: pmicgrp { - fsl,pins = < - MX27_PAD_USB_PWR__GPIO2_23 0x0 /* PMIC IRQ */ - >; - }; + pinctrl_pmic: pmicgrp { + fsl,pins = < + MX27_PAD_USB_PWR__GPIO2_23 0x0 /* PMIC IRQ */ + >; + }; - pinctrl_ssi1: ssi1grp { - fsl,pins = < - MX27_PAD_SSI1_FS__SSI1_FS 0x0 - MX27_PAD_SSI1_RXDAT__SSI1_RXDAT 0x0 - MX27_PAD_SSI1_TXDAT__SSI1_TXDAT 0x0 - MX27_PAD_SSI1_CLK__SSI1_CLK 0x0 - >; - }; + pinctrl_ssi1: ssi1grp { + fsl,pins = < + MX27_PAD_SSI1_FS__SSI1_FS 0x0 + MX27_PAD_SSI1_RXDAT__SSI1_RXDAT 0x0 + MX27_PAD_SSI1_TXDAT__SSI1_TXDAT 0x0 + MX27_PAD_SSI1_CLK__SSI1_CLK 0x0 + >; + }; - pinctrl_usbotg: usbotggrp { - fsl,pins = < - MX27_PAD_USBOTG_CLK__USBOTG_CLK 0x0 - MX27_PAD_USBOTG_DIR__USBOTG_DIR 0x0 - MX27_PAD_USBOTG_NXT__USBOTG_NXT 0x0 - MX27_PAD_USBOTG_STP__USBOTG_STP 0x0 - MX27_PAD_USBOTG_DATA0__USBOTG_DATA0 0x0 - MX27_PAD_USBOTG_DATA1__USBOTG_DATA1 0x0 - MX27_PAD_USBOTG_DATA2__USBOTG_DATA2 0x0 - MX27_PAD_USBOTG_DATA3__USBOTG_DATA3 0x0 - MX27_PAD_USBOTG_DATA4__USBOTG_DATA4 0x0 - MX27_PAD_USBOTG_DATA5__USBOTG_DATA5 0x0 - MX27_PAD_USBOTG_DATA6__USBOTG_DATA6 0x0 - MX27_PAD_USBOTG_DATA7__USBOTG_DATA7 0x0 - >; - }; + pinctrl_usbotg: usbotggrp { + fsl,pins = < + MX27_PAD_USBOTG_CLK__USBOTG_CLK 0x0 + MX27_PAD_USBOTG_DIR__USBOTG_DIR 0x0 + MX27_PAD_USBOTG_NXT__USBOTG_NXT 0x0 + MX27_PAD_USBOTG_STP__USBOTG_STP 0x0 + MX27_PAD_USBOTG_DATA0__USBOTG_DATA0 0x0 + MX27_PAD_USBOTG_DATA1__USBOTG_DATA1 0x0 + MX27_PAD_USBOTG_DATA2__USBOTG_DATA2 0x0 + MX27_PAD_USBOTG_DATA3__USBOTG_DATA3 0x0 + MX27_PAD_USBOTG_DATA4__USBOTG_DATA4 0x0 + MX27_PAD_USBOTG_DATA5__USBOTG_DATA5 0x0 + MX27_PAD_USBOTG_DATA6__USBOTG_DATA6 0x0 + MX27_PAD_USBOTG_DATA7__USBOTG_DATA7 0x0 + >; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx27.dtsi b/arch/arm/boot/dts/nxp/imx/imx27.dtsi index 989b7659b669..28403a34638e 100644 --- a/arch/arm/boot/dts/nxp/imx/imx27.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx27.dtsi @@ -49,7 +49,7 @@ clocks { clk_osc26m: osc26m { - compatible = "fsl,imx-osc26m", "fixed-clock"; + compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <26000000>; }; @@ -289,7 +289,7 @@ status = "disabled"; }; - iomuxc: iomuxc@10015000 { + iomuxc: pinmux@10015000 { compatible = "fsl,imx27-iomuxc"; reg = <0x10015000 0x600>; #address-cells = <1>; diff --git a/arch/arm/boot/dts/nxp/imx/imx31.dtsi b/arch/arm/boot/dts/nxp/imx/imx31.dtsi index 8541a666747a..c58f855ea851 100644 --- a/arch/arm/boot/dts/nxp/imx/imx31.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx31.dtsi @@ -333,7 +333,7 @@ }; }; - emi@b8000000 { /* External Memory Interface */ + emi-bus@b8000000 { /* External Memory Interface */ compatible = "simple-bus"; reg = <0xb8000000 0x5000>; ranges; diff --git a/arch/arm/boot/dts/nxp/imx/imx35.dtsi b/arch/arm/boot/dts/nxp/imx/imx35.dtsi index 111d7c0331f5..ab7b64639989 100644 --- a/arch/arm/boot/dts/nxp/imx/imx35.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx35.dtsi @@ -79,7 +79,7 @@ compatible = "fsl,imx35-i2c", "fsl,imx1-i2c"; reg = <0x43f80000 0x4000>; clocks = <&clks 51>; - clock-names = "ipg_per"; + clock-names = "ipg"; interrupts = <10>; status = "disabled"; }; @@ -90,7 +90,7 @@ compatible = "fsl,imx35-i2c", "fsl,imx1-i2c"; reg = <0x43f84000 0x4000>; clocks = <&clks 53>; - clock-names = "ipg_per"; + clock-names = "ipg"; interrupts = <3>; status = "disabled"; }; @@ -119,7 +119,7 @@ compatible = "fsl,imx35-i2c", "fsl,imx1-i2c"; reg = <0x43f98000 0x4000>; clocks = <&clks 52>; - clock-names = "ipg_per"; + clock-names = "ipg"; interrupts = <4>; status = "disabled"; }; @@ -356,7 +356,7 @@ }; }; - emi@80000000 { /* External Memory Interface */ + emi-bus@80000000 { /* External Memory Interface */ compatible = "fsl,emi", "simple-bus"; #address-cells = <1>; #size-cells = <1>; @@ -393,21 +393,13 @@ }; }; - usbphy { - compatible = "simple-bus"; - #address-cells = <1>; - #size-cells = <0>; + usbphy0: usb-phy0 { + compatible = "usb-nop-xceiv"; + #phy-cells = <0>; + }; - usbphy0: usb-phy@0 { - reg = <0>; - compatible = "usb-nop-xceiv"; - #phy-cells = <0>; - }; - - usbphy1: usb-phy@1 { - reg = <1>; - compatible = "usb-nop-xceiv"; - #phy-cells = <0>; - }; + usbphy1: usb-phy1 { + compatible = "usb-nop-xceiv"; + #phy-cells = <0>; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx51-babbage.dts b/arch/arm/boot/dts/nxp/imx/imx51-babbage.dts index 1b6ec55f9068..b17264e06e69 100644 --- a/arch/arm/boot/dts/nxp/imx/imx51-babbage.dts +++ b/arch/arm/boot/dts/nxp/imx/imx51-babbage.dts @@ -327,7 +327,7 @@ }; }; - flash: at45db321d@1 { + flash: flash@1 { #address-cells = <1>; #size-cells = <1>; compatible = "atmel,at45db321d", "atmel,at45", "atmel,dataflash"; diff --git a/arch/arm/boot/dts/nxp/imx/imx51-ts4800.dts b/arch/arm/boot/dts/nxp/imx/imx51-ts4800.dts index 079bd3d14999..5118a68dbbdc 100644 --- a/arch/arm/boot/dts/nxp/imx/imx51-ts4800.dts +++ b/arch/arm/boot/dts/nxp/imx/imx51-ts4800.dts @@ -141,7 +141,7 @@ pinctrl-0 = <&pinctrl_weim>; status = "okay"; - fpga@0 { + fpga-bus@0,0 { compatible = "simple-bus"; fsl,weim-cs-timing = <0x0061008F 0x00000002 0x1c022000 0x00000000 0x1c092480 0x00000000>; diff --git a/arch/arm/boot/dts/nxp/imx/imx53-smd.dts b/arch/arm/boot/dts/nxp/imx/imx53-smd.dts index 386371c816f4..a1e19f9709b2 100644 --- a/arch/arm/boot/dts/nxp/imx/imx53-smd.dts +++ b/arch/arm/boot/dts/nxp/imx/imx53-smd.dts @@ -31,6 +31,20 @@ linux,code = ; }; }; + + reg_1v8: regulator-1v8 { + compatible = "regulator-fixed"; + regulator-name = "1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + reg_3v3: regulator-3v3 { + compatible = "regulator-fixed"; + regulator-name = "3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; }; &esdhc1 { @@ -253,6 +267,10 @@ codec: sgtl5000@a { compatible = "fsl,sgtl5000"; reg = <0x0a>; + #sound-dai-cells = <0>; + clocks = <&clks IMX5_CLK_DUMMY>; + VDDA-supply = <®_1v8>; + VDDIO-supply = <®_3v3>; }; magnetometer: mag3110@e { diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-alti6p.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-alti6p.dts index 9bb36db131c2..aed4fb7843e2 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-alti6p.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-alti6p.dts @@ -193,8 +193,8 @@ }; &clks { - clocks = <&clock_ksz8081>; - clock-names = "enet_ref_pad"; + clocks = <&osc>, <&ckil>, <&ckih1>, <&anaclk1>, <&anaclk2>, <&clock_ksz8081>; + clock-names = "osc", "ckil", "ckih1", "anaclk1", "anaclk2", "enet_ref_pad"; assigned-clocks = <&clks IMX6QDL_CLK_ENET_REF_SEL>; assigned-clock-parents = <&clock_ksz8081>; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-eckelmann-ci4x10.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-eckelmann-ci4x10.dts index 5ed55f74b398..5f61eeb9fad0 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-eckelmann-ci4x10.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-eckelmann-ci4x10.dts @@ -66,8 +66,8 @@ }; &clks { - clocks = <&rmii_clk>; - clock-names = "enet_ref_pad"; + clocks = <&osc>, <&ckil>, <&ckih1>, <&anaclk1>, <&anaclk2>, <&rmii_clk>; + clock-names = "osc", "ckil", "ckih1", "anaclk1", "anaclk2", "enet_ref_pad"; assigned-clocks = <&clks IMX6QDL_CLK_ENET_REF_SEL>; assigned-clock-parents = <&rmii_clk>; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-lanmcu.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-lanmcu.dts index 47a6d63c8e04..9bde65462558 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-lanmcu.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-lanmcu.dts @@ -119,8 +119,8 @@ }; &clks { - clocks = <&clock_ksz8081>; - clock-names = "enet_ref_pad"; + clocks = <&osc>, <&ckil>, <&ckih1>, <&anaclk1>, <&anaclk2>, <&clock_ksz8081>; + clock-names = "osc", "ckil", "ckih1", "anaclk1", "anaclk2", "enet_ref_pad"; assigned-clocks = <&clks IMX6QDL_CLK_ENET_REF_SEL>; assigned-clock-parents = <&clock_ksz8081>; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-plybas.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-plybas.dts index 84f34da06267..69e790ba5662 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-plybas.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-plybas.dts @@ -101,8 +101,8 @@ }; &clks { - clocks = <&clk50m_phy>; - clock-names = "enet_ref_pad"; + clocks = <&osc>, <&ckil>, <&ckih1>, <&anaclk1>, <&anaclk2>, <&clk50m_phy>; + clock-names = "osc", "ckil", "ckih1", "anaclk1", "anaclk2", "enet_ref_pad"; assigned-clocks = <&clks IMX6QDL_CLK_ENET_REF_SEL>; assigned-clock-parents = <&clk50m_phy>; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-plym2m.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-plym2m.dts index 0ef24a07dedf..fbff77944ce3 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-plym2m.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-plym2m.dts @@ -199,8 +199,8 @@ }; &clks { - clocks = <&clk50m_phy>; - clock-names = "enet_ref_pad"; + clocks = <&osc>, <&ckil>, <&ckih1>, <&anaclk1>, <&anaclk2>, <&clk50m_phy>; + clock-names = "osc", "ckil", "ckih1", "anaclk1", "anaclk2", "enet_ref_pad"; assigned-clocks = <&clks IMX6QDL_CLK_ENET_REF_SEL>; assigned-clock-parents = <&clk50m_phy>; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-prtmvt.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-prtmvt.dts index 2160b7177835..dcd5a4099c60 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-prtmvt.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-prtmvt.dts @@ -294,8 +294,8 @@ }; &clks { - clocks = <&clk50m_phy>; - clock-names = "enet_ref_pad"; + clocks = <&osc>, <&ckil>, <&ckih1>, <&anaclk1>, <&anaclk2>, <&clk50m_phy>; + clock-names = "osc", "ckil", "ckih1", "anaclk1", "anaclk2", "enet_ref_pad"; assigned-clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>, <&clks IMX6QDL_CLK_ENET_REF_SEL>; assigned-clock-parents = <&clks IMX6QDL_CLK_PLL5_VIDEO_DIV>, <&clk50m_phy>; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-qmx6.dtsi b/arch/arm/boot/dts/nxp/imx/imx6dl-qmx6.dtsi index d5baec5e7a78..fb674ac2c248 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-qmx6.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-qmx6.dtsi @@ -71,8 +71,9 @@ }; &clks { - clocks = <&rtc_sqw>; - clock-names = "ckil"; + clocks = <&osc>, <&rtc_sqw>, <&ckih1>, <&anaclk1>, <&anaclk2>; + clock-names = "osc", "ckil", "ckih1", "anaclk1", "anaclk2"; + assigned-clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>, <&clks IMX6QDL_CLK_LDB_DI1_SEL>; assigned-clock-parents = <&clks IMX6QDL_CLK_PLL2_PFD0_352M>, diff --git a/arch/arm/boot/dts/nxp/imx/imx6dl-victgo.dts b/arch/arm/boot/dts/nxp/imx/imx6dl-victgo.dts index 76b0007d20ad..18019a6bb3af 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6dl-victgo.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6dl-victgo.dts @@ -160,8 +160,8 @@ }; &clks { - clocks = <&clk50m_phy>; - clock-names = "enet_ref_pad"; + clocks = <&osc>, <&ckil>, <&ckih1>, <&anaclk1>, <&anaclk2>, <&clk50m_phy>; + clock-names = "osc", "ckil", "ckih1", "anaclk1", "anaclk2", "enet_ref_pad"; assigned-clocks = <&clks IMX6QDL_CLK_ENET_REF_SEL>; assigned-clock-parents = <&clk50m_phy>; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-b450v3.dts b/arch/arm/boot/dts/nxp/imx/imx6q-b450v3.dts index d994b32ad825..7326b8ad08cb 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-b450v3.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-b450v3.dts @@ -140,6 +140,7 @@ port@4 { reg = <4>; label = "cpu"; + phy-mode = "gmii"; ethernet = <&switch_nic>; phy-handle = <&switchphy4>; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-b650v3.dts b/arch/arm/boot/dts/nxp/imx/imx6q-b650v3.dts index b0d345f5d071..9a43935be11c 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-b650v3.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-b650v3.dts @@ -139,6 +139,7 @@ port@4 { reg = <4>; label = "cpu"; + phy-mode = "gmii"; ethernet = <&switch_nic>; phy-handle = <&switchphy4>; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-b850v3.dts b/arch/arm/boot/dts/nxp/imx/imx6q-b850v3.dts index cad112e05475..d00236a07dc4 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-b850v3.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-b850v3.dts @@ -53,23 +53,20 @@ }; }; -&ldb { - fsl,dual-channel; - status = "okay"; +&gpio4 { + gpio-line-names = + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "PWGIN", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", ""; +}; - lvds0: lvds-channel@0 { - fsl,data-mapping = "spwg"; - fsl,data-width = <24>; - status = "okay"; - - port@4 { - reg = <4>; - - lvds0_out: endpoint { - remote-endpoint = <&stdp4028_in>; - }; - }; - }; +&hdmi { + ddc-i2c-bus = <&mux2_i2c1>; }; &i2c2 { @@ -129,8 +126,23 @@ }; }; -&hdmi { - ddc-i2c-bus = <&mux2_i2c1>; +&ldb { + fsl,dual-channel; + status = "okay"; + + lvds0: lvds-channel@0 { + fsl,data-mapping = "spwg"; + fsl,data-width = <24>; + status = "okay"; + + port@4 { + reg = <4>; + + lvds0_out: endpoint { + remote-endpoint = <&stdp4028_in>; + }; + }; + }; }; &mux1_i2c1 { @@ -286,7 +298,12 @@ port@4 { reg = <4>; label = "cpu"; + phy-mode = "gmii"; ethernet = <&switch_nic>; phy-handle = <&switchphy4>; }; }; + +&usdhc4 { + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-bx50v3.dtsi b/arch/arm/boot/dts/nxp/imx/imx6q-bx50v3.dtsi index 1e2266a2368b..2c8d2ab8cda1 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-bx50v3.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6q-bx50v3.dtsi @@ -124,24 +124,28 @@ reg = <0>; interrupt-parent = <&switch>; interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + max-speed = <100>; /* only 100Mbit/s lanes are routed */ }; switchphy1: switchphy@1 { reg = <1>; interrupt-parent = <&switch>; interrupts = <1 IRQ_TYPE_LEVEL_HIGH>; + max-speed = <100>; /* only 100Mbit/s lanes are routed */ }; switchphy2: switchphy@2 { reg = <2>; interrupt-parent = <&switch>; interrupts = <2 IRQ_TYPE_LEVEL_HIGH>; + max-speed = <100>; /* only 100Mbit/s lanes are routed */ }; switchphy3: switchphy@3 { reg = <3>; interrupt-parent = <&switch>; interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; + max-speed = <100>; /* only 100Mbit/s lanes are routed */ }; switchphy4: switchphy@4 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6q-prtwd2.dts b/arch/arm/boot/dts/nxp/imx/imx6q-prtwd2.dts index 0e02e448db10..5ef1ce808699 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6q-prtwd2.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6q-prtwd2.dts @@ -57,8 +57,8 @@ }; &clks { - clocks = <&clk50m_phy>; - clock-names = "enet_ref_pad"; + clocks = <&osc>, <&ckil>, <&ckih1>, <&anaclk1>, <&anaclk2>, <&clk50m_phy>; + clock-names = "osc", "ckil", "ckih1", "anaclk1", "anaclk2", "enet_ref_pad"; assigned-clocks = <&clks IMX6QDL_CLK_ENET_REF_SEL>; assigned-clock-parents = <&clk50m_phy>; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-overlay-panel-dpi-ch101olhlwh.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-overlay-panel-dpi-ch101olhlwh.dtsi new file mode 100644 index 000000000000..90259785126b --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-overlay-panel-dpi-ch101olhlwh.dtsi @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021-2026 Marek Vasut + */ +/dts-v1/; +/plugin/; + +#include "imx6qdl-dhcom-overlay-panel-dpi.dtsi" + +&{/} { + lvds-encoder { + compatible = "onnn,fin3385", "lvds-encoder"; + pclk-sample = <1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + lvds_bridge_in: endpoint { + remote-endpoint = <&ipu1_dpi0_out>; + }; + }; + + port@1 { + reg = <1>; + + lvds_bridge_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; +}; + +&display_bl { + pwms = <&pwm1 0 5000000 0>; +}; + +&i2c2 { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen@41 { + compatible = "ilitek,ili251x"; + reg = <0x41>; + interrupt-parent = <&gpio4>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>; + touchscreen-size-x = <16384>; + touchscreen-size-y = <9600>; + touchscreen-inverted-x; + touchscreen-inverted-y; + }; + + eeprom@50 { + compatible = "atmel,24c04"; + reg = <0x50>; + pagesize = <16>; + }; +}; + +&ipu1_dpi0_out { + remote-endpoint = <&lvds_bridge_in>; +}; + +&panel { + compatible = "chefree,ch101olhlwh-002"; +}; + +&panel_in { + remote-endpoint = <&lvds_bridge_out>; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-overlay-panel-dpi.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-overlay-panel-dpi.dtsi new file mode 100644 index 000000000000..48d346b6b484 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-overlay-panel-dpi.dtsi @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021-2026 Marek Vasut + */ +#include +#include +#include +#include + +&{/} { + display_bl: display-bl { + compatible = "pwm-backlight"; + brightness-levels = <0 16 22 30 40 55 75 102 138 188 255>; + default-brightness-level = <8>; + enable-gpios = <&gpio3 27 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + + lcd_display: disp0 { + compatible = "fsl,imx-parallel-display"; + #address-cells = <1>; + #size-cells = <0>; + interface-pix-fmt = "rgb24"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu1_lcdif>; + status = "okay"; + + port@0 { + reg = <0>; + + ipu1_display_in: endpoint { + remote-endpoint = <&ipu1_di0_disp0>; + }; + }; + + port@1 { + reg = <1>; + + ipu1_dpi0_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + + panel: panel { + backlight = <&display_bl>; + + port { + panel_in: endpoint { + }; + }; + }; +}; + +&ipu1_di0_disp0 { + remote-endpoint = <&ipu1_display_in>; +}; + +&pwm1 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-497-200-x12.dtso b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-497-200-x12.dtso new file mode 100644 index 000000000000..a5cb77b351af --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-497-200-x12.dtso @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021-2026 Marek Vasut + */ +/dts-v1/; +/plugin/; + +#include "imx6qdl-dhcom-overlay-panel-dpi.dtsi" + +&display_bl { + pwms = <&pwm1 0 50000 PWM_POLARITY_INVERTED>; +}; + +&ipu1_dpi0_out { + remote-endpoint = <&panel_in>; +}; + +&panel { + compatible = "dataimage,scf0700c48ggu18"; +}; + +&panel_in { + remote-endpoint = <&ipu1_dpi0_out>; +}; + +&touch_som { /* TSC2004 */ + status = "okay"; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh.dtso b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh.dtso new file mode 100644 index 000000000000..3e02031b74d0 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh.dtso @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021-2026 Marek Vasut + */ +/dts-v1/; +/plugin/; + +#include "imx6qdl-dhcom-overlay-panel-dpi-ch101olhlwh.dtsi" + +&{/} { + gpio-keys { + /* BUTTON1 GPIO-B conflicts with touchscreen reset */ + button-1 { + /* Use status as /delete-node/ does not work in DTOs */ + status = "disabled"; + }; + }; + + led { + /* LED7 GPIO-H conflicts with touchscreen IRQ */ + led-7 { + /* Use status as /delete-node/ does not work in DTOs */ + status = "disabled"; + }; + }; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-531-100-x21.dtso b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-531-100-x21.dtso new file mode 100644 index 000000000000..e8cae43f300d --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-531-100-x21.dtso @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021-2026 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&ecspi1 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + eeprom@1 { + compatible = "microchip,25aa010a", "atmel,at25"; + reg = <1>; + address-width = <8>; + pagesize = <16>; + size = <128>; + spi-max-frequency = <5000000>; + }; +}; + +&i2c2 { + #address-cells = <1>; + #size-cells = <0>; + + eeprom@56 { + compatible = "atmel,24c04"; + reg = <0x56>; + pagesize = <16>; + }; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-531-100-x22.dtso b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-531-100-x22.dtso new file mode 100644 index 000000000000..6fde50bcda71 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-531-100-x22.dtso @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021-2026 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&ecspi2 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + eeprom@0 { + compatible = "microchip,25aa010a", "atmel,at25"; + reg = <0>; + address-width = <8>; + pagesize = <16>; + size = <128>; + spi-max-frequency = <5000000>; + }; +}; + +&i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + eeprom@56 { + compatible = "atmel,24c04"; + reg = <0x56>; + pagesize = <16>; + }; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-560-200-x12.dtso b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-560-200-x12.dtso new file mode 100644 index 000000000000..9f5814e7a04f --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-pdk2-overlay-560-200-x12.dtso @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021-2026 Marek Vasut + */ +/dts-v1/; +/plugin/; + +#include "imx6qdl-dhcom-overlay-panel-dpi.dtsi" + +&display_bl { + pwms = <&pwm1 0 50000 PWM_POLARITY_INVERTED>; +}; + +&i2c2 { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen@38 { + compatible = "edt,edt-ft5406"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_dhcom_e>; + /* Touchscreen IRQ GPIO-E conflicts with LED5 GPIO */ + interrupt-parent = <&gpio4>; + interrupts = <5 IRQ_TYPE_EDGE_FALLING>; /* GPIO E */ + }; +}; + +&ipu1_dpi0_out { + remote-endpoint = <&panel_in>; +}; + +&panel { + compatible = "edt,etm0700g0edh6"; +}; + +&panel_in { + remote-endpoint = <&ipu1_dpi0_out>; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh.dtso b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh.dtso new file mode 100644 index 000000000000..5c380572c409 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh.dtso @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2023-2026 Marek Vasut + */ +/dts-v1/; +/plugin/; + +#include "imx6qdl-dhcom-overlay-panel-dpi-ch101olhlwh.dtsi" diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-som.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-som.dtsi index af0d95396cd5..74dfb92f1514 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-som.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-dhcom-som.dtsi @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2015-2021 DH electronics GmbH - * Copyright (C) 2018 Marek Vasut + * Copyright (C) 2015-2026 DH electronics GmbH + * Copyright (C) 2018-2026 Marek Vasut */ #include @@ -303,7 +303,7 @@ }; }; - touchscreen@49 { /* TSC2004 */ + touch_som: touchscreen@49 { /* TSC2004 */ compatible = "ti,tsc2004"; interrupts-extended = <&gpio4 14 IRQ_TYPE_EDGE_FALLING>; pinctrl-0 = <&pinctrl_tsc2004>; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-mba6.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-mba6.dtsi index ee2c6bec92e8..74ccfe56828f 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-mba6.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-mba6.dtsi @@ -206,6 +206,10 @@ }; }; +&gpio1 { + bootph-pre-ram; +}; + &hdmi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hdmi>; @@ -214,6 +218,8 @@ }; &i2c1 { + bootph-pre-ram; + tlv320aic32x4: audio-codec@18 { compatible = "ti,tlv320aic32x4"; reg = <0x18>; @@ -274,6 +280,7 @@ &uart2 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart2>; + bootph-pre-ram; status = "okay"; }; @@ -346,6 +353,7 @@ no-sdio; cd-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>; wp-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; + bootph-all; status = "okay"; }; @@ -354,6 +362,7 @@ pinctrl-0 = <&pinctrl_wdog1>; /* does not work on unmodified starter kit */ /* fsl,ext-reset-output; */ + bootph-pre-ram; status = "okay"; }; @@ -544,6 +553,7 @@ MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x1b099 MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x1b099 >; + bootph-pre-ram; }; pinctrl_uart3: uart3grp { @@ -587,6 +597,7 @@ MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x0001b099 /* usdhc2 CD */ MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x0001b099 /* usdhc2 WP */ >; + bootph-all; }; pinctrl_usbotg: usbotggrp { @@ -602,5 +613,6 @@ /* Watchdog out */ MX6QDL_PAD_SD1_DAT2__WDOG1_B 0x0000b099 >; + bootph-pre-ram; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-skov-cpu.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-skov-cpu.dtsi index c93dbc595ef6..e2631db798a8 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-skov-cpu.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-skov-cpu.dtsi @@ -238,8 +238,8 @@ }; &clks { - clocks = <&clk50m_phy>; - clock-names = "enet_ref_pad"; + clocks = <&osc>, <&ckil>, <&ckih1>, <&anaclk1>, <&anaclk2>, <&clk50m_phy>; + clock-names = "osc", "ckil", "ckih1", "anaclk1", "anaclk2", "enet_ref_pad"; assigned-clocks = <&clks IMX6QDL_CLK_ENET_REF_SEL>; assigned-clock-parents = <&clk50m_phy>; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-sr-som-ti.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-sr-som-ti.dtsi index cd1e682f11ad..8192344d5294 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-sr-som-ti.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-sr-som-ti.dtsi @@ -41,6 +41,12 @@ #include / { + clk32k: clock-32k { + compatible = "fixed-clock"; + clock-frequency = <32768>; + #clock-cells = <0>; + }; + nvcc_sd1: regulator-nvcc-sd1 { compatible = "regulator-fixed"; regulator-always-on; @@ -58,7 +64,7 @@ */ compatible = "gpio-gate-clock"; #clock-cells = <0>; - clock-frequency = <32768>; + clocks = <&clk32k>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_microsom_ti_clk>; enable-gpios = <&gpio5 5 GPIO_ACTIVE_HIGH>; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-tqma6.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-tqma6.dtsi index 07492f63a1f8..14676d1d905a 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-tqma6.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-tqma6.dtsi @@ -11,6 +11,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ecspi1>; cs-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>; + bootph-pre-ram; status = "okay"; m25p80: flash@0 { @@ -19,6 +20,7 @@ spi-max-frequency = <50000000>; vcc-supply = <&sw4_reg>; m25p,fast-read; + bootph-pre-ram; partitions { compatible = "fixed-partitions"; @@ -28,6 +30,10 @@ }; }; +&gpio3 { + bootph-pre-ram; +}; + &iomuxc { pinctrl_ecspi1: ecspi1grp { fsl,pins = < @@ -38,6 +44,7 @@ /* eCSPI1 SS1 */ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0xb099 >; + bootph-pre-ram; }; pinctrl_i2c1: i2c1grp { @@ -45,6 +52,7 @@ MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x4001b899 MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x4001b899 >; + bootph-pre-ram; }; pinctrl_i2c1_recovery: i2c1recoverygrp { @@ -73,6 +81,7 @@ MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059 MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059 >; + bootph-all; }; }; @@ -117,6 +126,7 @@ regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; regulator-always-on; + bootph-pre-ram; }; reg_5v_600mA: swbst { @@ -186,6 +196,7 @@ bus-width = <8>; #address-cells = <1>; #size-cells = <0>; + bootph-all; status = "okay"; mmccard: mmccard@0 { diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-tqma6a.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-tqma6a.dtsi index e8fd37dd8835..67f8f59aff5a 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-tqma6a.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-tqma6a.dtsi @@ -20,6 +20,7 @@ scl-gpios = <&gpio5 27 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; sda-gpios = <&gpio5 26 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; clock-frequency = <100000>; + bootph-pre-ram; status = "okay"; pmic: pmic@8 { @@ -27,13 +28,13 @@ reg = <0x08>; }; - temperature-sensor@48 { + lm75_48: temperature-sensor@48 { compatible = "national,lm75a"; reg = <0x48>; vs-supply = <&sw4_reg>; }; - eeprom@50 { + m24c64_50: eeprom@50 { compatible = "st,24c64", "atmel,24c64"; reg = <0x50>; pagesize = <32>; diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-tqma6b.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-tqma6b.dtsi index 0e404c1f62f2..db552802554d 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl-tqma6b.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-tqma6b.dtsi @@ -13,6 +13,7 @@ scl-gpios = <&gpio1 5 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; sda-gpios = <&gpio1 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; clock-frequency = <100000>; + bootph-pre-ram; status = "okay"; pmic: pmic@8 { @@ -20,13 +21,13 @@ reg = <0x08>; }; - temperature-sensor@48 { + lm75_48: temperature-sensor@48 { compatible = "national,lm75a"; reg = <0x48>; vs-supply = <&sw4_reg>; }; - eeprom@50 { + m24c64_50: eeprom@50 { compatible = "st,24c64", "atmel,24c64"; reg = <0x50>; pagesize = <32>; @@ -40,6 +41,7 @@ MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b899 MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b899 >; + bootph-pre-ram; }; pinctrl_i2c3_recovery: i2c3recoverygrp { diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl.dtsi index 76e6043e1f91..4dc2c410cf61 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6qdl.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6qdl.dtsi @@ -54,23 +54,35 @@ }; clocks { - ckil { + ckil: ckil { compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <32768>; }; - ckih1 { + ckih1: ckih1 { compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <0>; }; - osc { + osc: osc { compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <24000000>; }; + + anaclk1: anaclk1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + anaclk2: anaclk2 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; }; ldb: ldb { @@ -149,6 +161,7 @@ compatible = "simple-bus"; interrupt-parent = <&gpc>; ranges; + bootph-all; dma_apbh: dma-controller@110000 { compatible = "fsl,imx6q-dma-apbh", "fsl,imx28-dma-apbh"; @@ -297,6 +310,7 @@ #size-cells = <1>; reg = <0x02000000 0x100000>; ranges; + bootph-pre-ram; spba-bus@2000000 { compatible = "fsl,spba-bus", "simple-bus"; @@ -304,6 +318,7 @@ #size-cells = <1>; reg = <0x02000000 0x40000>; ranges; + bootph-pre-ram; spdif: spdif@2004000 { compatible = "fsl,imx35-spdif"; @@ -920,6 +935,7 @@ iomuxc: pinctrl@20e0000 { compatible = "fsl,imx6dl-iomuxc", "fsl,imx6q-iomuxc"; reg = <0x20e0000 0x4000>; + bootph-pre-ram; }; dcic1: dcic@20e4000 { @@ -950,6 +966,7 @@ #size-cells = <1>; reg = <0x02100000 0x100000>; ranges; + bootph-pre-ram; crypto: crypto@2100000 { compatible = "fsl,sec-v4.0"; @@ -1320,6 +1337,7 @@ <&clks IMX6QDL_CLK_IPU1_DI1>; clock-names = "bus", "di0", "di1"; resets = <&src 2>; + bootph-all; ipu1_csi0: port@0 { reg = <0>; diff --git a/arch/arm/boot/dts/nxp/imx/imx6sx.dtsi b/arch/arm/boot/dts/nxp/imx/imx6sx.dtsi index 1426f357d474..aefae5a3a6be 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6sx.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6sx.dtsi @@ -1304,7 +1304,7 @@ }; lcdif1: lcdif@2220000 { - compatible = "fsl,imx6sx-lcdif", "fsl,imx28-lcdif"; + compatible = "fsl,imx6sx-lcdif"; reg = <0x02220000 0x4000>; interrupts = ; clocks = <&clks IMX6SX_CLK_LCDIF1_PIX>, @@ -1325,7 +1325,7 @@ }; lcdif2: lcdif@2224000 { - compatible = "fsl,imx6sx-lcdif", "fsl,imx28-lcdif"; + compatible = "fsl,imx6sx-lcdif"; reg = <0x02224000 0x4000>; interrupts = ; clocks = <&clks IMX6SX_CLK_LCDIF2_PIX>, diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul-common.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul-common.dtsi index 2dd635a615cb..4fa98e6a66d7 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul-common.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul-common.dtsi @@ -26,6 +26,7 @@ pinctrl-1 = <&pinctrl_i2c4_recovery>; scl-gpios = <&gpio1 20 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; sda-gpios = <&gpio1 21 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + bootph-pre-ram; status = "okay"; pfuze3000: pmic@8 { @@ -140,9 +141,14 @@ }; }; +&gpio1 { + bootph-pre-ram; +}; + &gpio4 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_pmic>; + bootph-pre-ram; /* * PMIC & temperature sensor IRQ @@ -159,6 +165,7 @@ &qspi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_qspi>; + bootph-pre-ram; status = "okay"; flash0: flash@0 { @@ -168,6 +175,7 @@ spi-rx-bus-width = <4>; spi-tx-bus-width = <1>; vcc-supply = <®_vldo4>; + bootph-pre-ram; partitions { compatible = "fixed-partitions"; @@ -189,6 +197,7 @@ non-removable; no-sdio; no-sd; + bootph-all; status = "okay"; }; @@ -212,5 +221,6 @@ /* PMIC irq */ MX6UL_PAD_CSI_DATA03__GPIO4_IO24 0x1b099 >; + bootph-pre-ram; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul2.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul2.dtsi index e2e95dd92263..f81cd09fe0c7 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul2.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul2.dtsi @@ -33,6 +33,7 @@ /* rst */ MX6UL_PAD_NAND_ALE__GPIO4_IO10 0x0001b051 >; + bootph-all; }; pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul2l-mba6ulx.dts b/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul2l-mba6ulx.dts index 9d9b6b744a1c..9d637c0a12ec 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul2l-mba6ulx.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul2l-mba6ulx.dts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) /* - * Copyright 2018-2022 TQ Systems GmbH + * Copyright 2018-2022 TQ-Systems GmbH * Author: Markus Niebel */ @@ -10,6 +10,6 @@ #include "mba6ulx.dtsi" / { - model = "TQ Systems TQMa6UL2L SoM on MBa6ULx board"; + model = "TQ-Systems TQMa6UL2L SoM on MBa6ULx board"; compatible = "tq,imx6ul-tqma6ul2l-mba6ulx", "tq,imx6ul-tqma6ul2l", "fsl,imx6ul"; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul2l.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul2l.dtsi index 4b87e2dc70dc..11c8f1af4173 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul2l.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ul2l.dtsi @@ -33,6 +33,7 @@ /* rst */ MX6UL_PAD_NAND_ALE__GPIO4_IO10 0x0001b051 >; + bootph-all; }; pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ulx-common.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ulx-common.dtsi index 5afb9046c202..5c90d0a3ee2e 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ulx-common.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ulx-common.dtsi @@ -39,5 +39,6 @@ MX6UL_PAD_NAND_CLE__QSPI_A_DATA03 0x70b9 MX6UL_PAD_NAND_DQS__QSPI_A_SS0_B 0x70a1 >; + bootph-pre-ram; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ulxl-common.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ulxl-common.dtsi index ba84a4f70ebd..133961ee7283 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ulxl-common.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-tqma6ulxl-common.dtsi @@ -44,5 +44,6 @@ MX6UL_PAD_NAND_CLE__QSPI_A_DATA03 0x70a9 MX6UL_PAD_NAND_DQS__QSPI_A_SS0_B 0x70a1 >; + bootph-pre-ram; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul.dtsi index 6eb80f867f50..24541fdf49ce 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul.dtsi @@ -115,6 +115,7 @@ #clock-cells = <0>; clock-frequency = <24000000>; clock-output-names = "osc"; + bootph-pre-ram; }; ipp_di0: clock-di0 { @@ -143,6 +144,7 @@ compatible = "simple-bus"; interrupt-parent = <&gpc>; ranges; + bootph-pre-ram; ocram: sram@900000 { compatible = "mmio-sram"; @@ -202,6 +204,7 @@ #size-cells = <1>; reg = <0x02000000 0x100000>; ranges; + bootph-pre-ram; spba-bus@2000000 { compatible = "fsl,spba-bus", "simple-bus"; @@ -209,6 +212,7 @@ #size-cells = <1>; reg = <0x02000000 0x40000>; ranges; + bootph-pre-ram; ecspi1: spi@2008000 { #address-cells = <1>; @@ -580,6 +584,7 @@ #clock-cells = <1>; clocks = <&ckil>, <&osc>, <&ipp_di0>, <&ipp_di1>; clock-names = "ckil", "osc", "ipp_di0", "ipp_di1"; + bootph-pre-ram; }; anatop: anatop@20c8000 { @@ -745,6 +750,7 @@ iomuxc: pinctrl@20e0000 { compatible = "fsl,imx6ul-iomuxc"; reg = <0x020e0000 0x4000>; + bootph-pre-ram; }; gpr: iomuxc-gpr@20e4000 { @@ -826,6 +832,7 @@ #size-cells = <1>; reg = <0x02100000 0x100000>; ranges; + bootph-pre-ram; crypto: crypto@2140000 { compatible = "fsl,imx6ul-caam", "fsl,sec-v4.0"; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-dhcor-som.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ull-dhcor-som.dtsi index 75486e1b0c15..a0adcd3fe122 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ull-dhcor-som.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-dhcor-som.dtsi @@ -201,7 +201,7 @@ * the speed can be increased accordingly. */ bluetooth: bluetooth { - compatible = "brcm,bcm43430a1-bt"; /* muRata 1DX */ + compatible = "brcm,bcm4329-bt"; /* muRata 1DX or 1YN */ max-speed = <3000000>; vbat-supply = <&vcc_3v3>; vddio-supply = <&vcc_3v3>; @@ -222,7 +222,7 @@ status = "okay"; brcmf: wifi@1 { - compatible = "brcm,bcm43430a1-fmac", "brcm,bcm4329-fmac"; /* muRata 1DX */ + compatible = "brcm,bcm4329-fmac"; /* muRata 1DX or 1YN */ reg = <1>; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-bmm.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-bmm.dts index 279d46c22cd7..f251a1028355 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-bmm.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-bmm.dts @@ -154,6 +154,7 @@ pinctrl-0 = <&pinctrl_tsc>; measure-delay-time = <0x9ffff>; pre-charge-time = <0xfff>; + debounce-delay-us = <62>; xnur-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; status = "okay"; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-tqma6ull2.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ull-tqma6ull2.dtsi index 8541cb3f3b3e..1224ef132439 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ull-tqma6ull2.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-tqma6ull2.dtsi @@ -38,6 +38,7 @@ /* rst */ MX6UL_PAD_NAND_ALE__GPIO4_IO10 0x0001b051 >; + bootph-all; }; pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-tqma6ull2l-mba6ulx.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-tqma6ull2l-mba6ulx.dts index 33437aae9822..5676904820a9 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ull-tqma6ull2l-mba6ulx.dts +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-tqma6ull2l-mba6ulx.dts @@ -10,6 +10,6 @@ #include "mba6ulx.dtsi" / { - model = "TQ Systems TQMa6ULL2L SoM on MBa6ULx board"; + model = "TQ-Systems TQMa6ULL2L SoM on MBa6ULx board"; compatible = "tq,imx6ull-tqma6ull2l-mba6ulx", "tq,imx6ull-tqma6ull2l", "fsl,imx6ull"; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-tqma6ull2l.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ull-tqma6ull2l.dtsi index be593d47e3b1..ac18caf5a76c 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ull-tqma6ull2l.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-tqma6ull2l.dtsi @@ -9,7 +9,7 @@ #include "imx6ul-tqma6ulxl-common.dtsi" / { - model = "TQ Systems TQMa6ULL2L SoM"; + model = "TQ-Systems TQMa6ULL2L SoM"; compatible = "tq,imx6ull-tqma6ull2l", "fsl,imx6ull"; }; @@ -38,6 +38,7 @@ /* rst */ MX6UL_PAD_NAND_ALE__GPIO4_IO10 0x0001b051 >; + bootph-all; }; pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ull.dtsi index db0c339022ac..ba0ea10c7b74 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ull.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ull.dtsi @@ -57,6 +57,7 @@ #size-cells = <1>; reg = <0x02200000 0x100000>; ranges; + bootph-pre-ram; dcp: crypto@2280000 { compatible = "fsl,imx6ull-dcp", "fsl,imx28-dcp"; diff --git a/arch/arm/boot/dts/nxp/imx/imx7-mba7.dtsi b/arch/arm/boot/dts/nxp/imx/imx7-mba7.dtsi index 4d948a9757f9..4192adb27223 100644 --- a/arch/arm/boot/dts/nxp/imx/imx7-mba7.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx7-mba7.dtsi @@ -287,6 +287,15 @@ status = "okay"; }; +&gpio4 { + /* Deassert BOOT_EN after boot to separate BOOT_CFG circuits from LCD signals */ + boot-en-hog { + gpio-hog; + gpios = <3 GPIO_ACTIVE_LOW>; + output-low; + }; +}; + &i2c1 { lm75: temperature-sensor@49 { compatible = "national,lm75a"; @@ -477,6 +486,7 @@ }; pinctrl_uart6: uart6grp { + bootph-pre-ram; fsl,pins = , , @@ -511,6 +521,7 @@ , , ; + bootph-pre-ram; }; pinctrl_usdhc1_100mhz: usdhc1_100mhzgrp { @@ -602,6 +613,7 @@ pinctrl-0 = <&pinctrl_uart6>; assigned-clocks = <&clks IMX7D_UART6_ROOT_SRC>; assigned-clock-parents = <&clks IMX7D_OSC_24M_CLK>; + bootph-pre-ram; status = "okay"; }; @@ -646,6 +658,7 @@ no-1-8-v; no-sdio; no-mmc; + bootph-pre-ram; status = "okay"; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx7-tqma7.dtsi b/arch/arm/boot/dts/nxp/imx/imx7-tqma7.dtsi index 2966a33bc528..f3d7a2d0cb7b 100644 --- a/arch/arm/boot/dts/nxp/imx/imx7-tqma7.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx7-tqma7.dtsi @@ -193,6 +193,7 @@ , , ; + bootph-pre-ram; }; pinctrl_qspi_reset: qspi_resetgrp { @@ -214,6 +215,7 @@ , , ; + bootph-pre-ram; }; pinctrl_usdhc3_100mhz: usdhc3_100mhzgrp { @@ -257,6 +259,7 @@ &qspi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_qspi &pinctrl_qspi_reset>; + bootph-pre-ram; status = "okay"; flash0: flash@0 { @@ -266,6 +269,7 @@ spi-rx-bus-width = <4>; spi-tx-bus-width = <4>; vcc-supply = <&vgen4_reg>; + bootph-pre-ram; partitions { compatible = "fixed-partitions"; @@ -288,6 +292,7 @@ no-sdio; vmmc-supply = <&vgen4_reg>; vqmmc-supply = <&sw2_reg>; + bootph-pre-ram; status = "okay"; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx7s-warp.dts b/arch/arm/boot/dts/nxp/imx/imx7s-warp.dts index 92b6258059ee..25f38acc5350 100644 --- a/arch/arm/boot/dts/nxp/imx/imx7s-warp.dts +++ b/arch/arm/boot/dts/nxp/imx/imx7s-warp.dts @@ -199,8 +199,6 @@ port { ov2680_to_mipi: endpoint { remote-endpoint = <&mipi_from_sensor>; - clock-lanes = <0>; - data-lanes = <1>; link-frequencies = /bits/ 64 <330000000>; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx7s.dtsi b/arch/arm/boot/dts/nxp/imx/imx7s.dtsi index 9235dd7e93bb..5c9be24ba7c5 100644 --- a/arch/arm/boot/dts/nxp/imx/imx7s.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx7s.dtsi @@ -209,6 +209,7 @@ compatible = "simple-bus"; interrupt-parent = <&gpc>; ranges; + bootph-pre-ram; ocram: sram@900000 { compatible = "mmio-sram"; @@ -371,6 +372,7 @@ #size-cells = <1>; reg = <0x30000000 0x400000>; ranges; + bootph-pre-ram; gpio1: gpio@30200000 { compatible = "fsl,imx7d-gpio", "fsl,imx35-gpio"; @@ -543,6 +545,7 @@ iomuxc: pinctrl@30330000 { compatible = "fsl,imx7d-iomuxc"; reg = <0x30330000 0x10000>; + bootph-pre-ram; }; gpr: iomuxc-gpr@30340000 { @@ -712,6 +715,7 @@ #size-cells = <1>; reg = <0x30400000 0x400000>; ranges; + bootph-pre-ram; adc1: adc@30610000 { compatible = "fsl,imx7d-adc"; @@ -902,6 +906,7 @@ #size-cells = <1>; reg = <0x30800000 0x400000>; ranges; + bootph-pre-ram; spba-bus@30800000 { compatible = "fsl,spba-bus", "simple-bus"; diff --git a/arch/arm/boot/dts/nxp/imx/imx7ulp.dtsi b/arch/arm/boot/dts/nxp/imx/imx7ulp.dtsi index 880b9a4f32b0..1355feda1aa7 100644 --- a/arch/arm/boot/dts/nxp/imx/imx7ulp.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx7ulp.dtsi @@ -41,6 +41,34 @@ compatible = "arm,cortex-a7"; device_type = "cpu"; reg = <0xf00>; + clocks = <&smc1 IMX7ULP_CLK_ARM>, + <&scg1 IMX7ULP_CLK_CORE>, + <&scg1 IMX7ULP_CLK_SYS_SEL>, + <&scg1 IMX7ULP_CLK_HSRUN_CORE>, + <&scg1 IMX7ULP_CLK_HSRUN_SYS_SEL>, + <&scg1 IMX7ULP_CLK_FIRC>; + clock-names = "arm", "core", "scs_sel", + "hsrun_core", "hsrun_scs_sel", + "firc"; + operating-points-v2 = <&cpu0_opp_table>; + }; + }; + + cpu0_opp_table: opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-500210000 { + opp-hz = /bits/ 64 <500210000>; + opp-microvolt = <1025000>; + clock-latency-ns = <150000>; + opp-suspend; + }; + + opp-720000000 { + opp-hz = /bits/ 64 <720000000>; + opp-microvolt = <1125000>; + clock-latency-ns = <150000>; }; }; diff --git a/arch/arm/boot/dts/nxp/imx/mba6ulx.dtsi b/arch/arm/boot/dts/nxp/imx/mba6ulx.dtsi index 65fde4f52587..1fda60d62ffe 100644 --- a/arch/arm/boot/dts/nxp/imx/mba6ulx.dtsi +++ b/arch/arm/boot/dts/nxp/imx/mba6ulx.dtsi @@ -95,6 +95,7 @@ regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; regulator-always-on; + bootph-pre-ram; }; reg_mba6ul_5v0: regulator-mba6ul-5v0 { @@ -336,6 +337,7 @@ &uart1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart1>; + bootph-pre-ram; status = "okay"; }; @@ -392,6 +394,7 @@ no-1-8-v; no-mmc; no-sdio; + bootph-all; status = "okay"; }; @@ -399,6 +402,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_wdog1>; fsl,ext-reset-output; + bootph-pre-ram; status = "okay"; }; @@ -494,6 +498,7 @@ MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1 MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1 >; + bootph-pre-ram; }; pinctrl_uart3: uart3grp { @@ -542,6 +547,7 @@ /* CD */ MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0001b099 >; + bootph-all; }; pinctrl_usdhc1_100mhz: usdhc1-100mhz-grp { diff --git a/arch/arm/boot/dts/nxp/mxs/imx23-olinuxino.dts b/arch/arm/boot/dts/nxp/mxs/imx23-olinuxino.dts index e372e9327a47..38be7dfabcc0 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx23-olinuxino.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx23-olinuxino.dts @@ -116,7 +116,7 @@ pinctrl-names = "default"; pinctrl-0 = <&led_pin_gpio2_1>; - user { + led-user { label = "green"; gpios = <&gpio2 1 GPIO_ACTIVE_HIGH>; }; diff --git a/arch/arm/boot/dts/nxp/mxs/imx23.dtsi b/arch/arm/boot/dts/nxp/mxs/imx23.dtsi index 5e21252fb7c9..58575e2c1ca5 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx23.dtsi +++ b/arch/arm/boot/dts/nxp/mxs/imx23.dtsi @@ -420,9 +420,9 @@ <60>, <58>, <9>, <0>, <0>, <0>, <0>, <0>; interrupt-names = "audio-adc", "audio-dac", "spdif-tx", "i2c", - "saif0", "empty", "auart0-rx", "auart0-tx", - "auart1-rx", "auart1-tx", "saif1", "empty", - "empty", "empty", "empty", "empty"; + "saif0", "empty0", "auart0-rx", "auart0-tx", + "auart1-rx", "auart1-tx", "saif1", "empty1", + "empty2", "empty3", "empty4", "empty5"; #dma-cells = <1>; dma-channels = <16>; clocks = <&clks 16>; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-apf28dev.dts b/arch/arm/boot/dts/nxp/mxs/imx28-apf28dev.dts index 6c87266eb135..63bbaa15c54d 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-apf28dev.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-apf28dev.dts @@ -29,7 +29,7 @@ leds { compatible = "gpio-leds"; - user { + led-user { label = "Heartbeat"; gpios = <&gpio0 21 0>; linux,default-trigger = "heartbeat"; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-apx4devkit.dts b/arch/arm/boot/dts/nxp/mxs/imx28-apx4devkit.dts index 0d845ca81e89..774eb49cfbc0 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-apx4devkit.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-apx4devkit.dts @@ -31,7 +31,7 @@ leds { compatible = "gpio-leds"; - user { + led-user { label = "Heartbeat"; gpios = <&gpio3 28 0>; linux,default-trigger = "heartbeat"; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-cfa10036.dts b/arch/arm/boot/dts/nxp/mxs/imx28-cfa10036.dts index f170df37b3f8..f4485334e9f8 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-cfa10036.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-cfa10036.dts @@ -21,7 +21,7 @@ pinctrl-names = "default"; pinctrl-0 = <&led_pins_cfa10036>; - power { + led-power { gpios = <&gpio3 4 1>; default-state = "on"; }; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-cfa10049.dts b/arch/arm/boot/dts/nxp/mxs/imx28-cfa10049.dts index f0ce897b9d5c..dfe3f08426ac 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-cfa10049.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-cfa10049.dts @@ -107,7 +107,7 @@ spi-max-frequency = <100000>; spi-cpol; spi-cpha; - gpios-reset = <&gpio3 30 0>; + reset-gpios = <&gpio3 30 0>; im-gpios = <&gpio5 4 0 &gpio5 5 0 &gpio5 6 0>; }; }; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-cfa10055.dts b/arch/arm/boot/dts/nxp/mxs/imx28-cfa10055.dts index cb68edd6101b..aeac1d29058d 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-cfa10055.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-cfa10055.dts @@ -33,7 +33,7 @@ spi-max-frequency = <100000>; spi-cpol; spi-cpha; - gpios-reset = <&gpio3 30 0>; + reset-gpios = <&gpio3 30 0>; }; }; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-cfa10056.dts b/arch/arm/boot/dts/nxp/mxs/imx28-cfa10056.dts index bc2d6fcad12f..add6a23728e7 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-cfa10056.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-cfa10056.dts @@ -32,7 +32,7 @@ spi-max-frequency = <100000>; spi-cpol; spi-cpha; - gpios-reset = <&gpio3 30 0>; + reset-gpios = <&gpio3 30 0>; }; }; }; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-duckbill-2-485.dts b/arch/arm/boot/dts/nxp/mxs/imx28-duckbill-2-485.dts index b73020ff1053..5e3f09109605 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-duckbill-2-485.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-duckbill-2-485.dts @@ -12,12 +12,12 @@ compatible = "i2se,duckbill-2-485", "i2se,duckbill-2", "fsl,imx28"; leds { - rs485-red { + led-rs485-red { label = "duckbill:red:rs485"; gpios = <&gpio3 24 GPIO_ACTIVE_LOW>; }; - rs485-green { + led-rs485-green { label = "duckbill:green:rs485"; gpios = <&gpio3 25 GPIO_ACTIVE_LOW>; }; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-duckbill-2-enocean.dts b/arch/arm/boot/dts/nxp/mxs/imx28-duckbill-2-enocean.dts index 473d99b9b42f..2168a59a33b3 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-duckbill-2-enocean.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-duckbill-2-enocean.dts @@ -13,17 +13,17 @@ compatible = "i2se,duckbill-2-enocean", "i2se,duckbill-2", "fsl,imx28"; leds { - enocean-blue { + led-enocean-blue { label = "duckbill:blue:enocean"; gpios = <&gpio3 24 GPIO_ACTIVE_LOW>; }; - enocean-red { + led-enocean-red { label = "duckbill:red:enocean"; gpios = <&gpio3 25 GPIO_ACTIVE_LOW>; }; - enocean-green { + led-enocean-green { label = "duckbill:green:enocean"; gpios = <&gpio3 2 GPIO_ACTIVE_LOW>; }; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-duckbill-2.dts b/arch/arm/boot/dts/nxp/mxs/imx28-duckbill-2.dts index 4e28212e9626..9bc703e7b2bc 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-duckbill-2.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-duckbill-2.dts @@ -31,13 +31,13 @@ pinctrl-names = "default"; pinctrl-0 = <&led_pins>; - status-red { + led-status-red { label = "duckbill:red:status"; gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>; linux,default-trigger = "default-on"; }; - status-green { + led-status-green { label = "duckbill:green:status"; gpios = <&gpio3 20 GPIO_ACTIVE_HIGH>; linux,default-trigger = "heartbeat"; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-duckbill.dts b/arch/arm/boot/dts/nxp/mxs/imx28-duckbill.dts index 13ffd533fdea..d5a1983f9289 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-duckbill.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-duckbill.dts @@ -30,13 +30,13 @@ pinctrl-names = "default"; pinctrl-0 = <&led_pins>; - status-red { + led-status-red { label = "duckbill:red:status"; gpios = <&gpio3 4 GPIO_ACTIVE_HIGH>; linux,default-trigger = "default-on"; }; - status-green { + led-status-green { label = "duckbill:green:status"; gpios = <&gpio3 5 GPIO_ACTIVE_HIGH>; linux,default-trigger = "heartbeat"; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-evk.dts b/arch/arm/boot/dts/nxp/mxs/imx28-evk.dts index 330d3aff6b6c..d1ff001b4037 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-evk.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-evk.dts @@ -108,7 +108,7 @@ pinctrl-names = "default"; pinctrl-0 = <&led_pin_gpio3_5>; - user { + led-user { label = "Heartbeat"; gpios = <&gpio3 5 0>; linux,default-trigger = "heartbeat"; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-m28cu3.dts b/arch/arm/boot/dts/nxp/mxs/imx28-m28cu3.dts index 34b4d3246db1..0f2bfb4845a9 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-m28cu3.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-m28cu3.dts @@ -27,13 +27,13 @@ pinctrl-names = "default"; pinctrl-0 = <&led_pins_gpio>; - user1 { + led-user1 { label = "sd0-led"; gpios = <&gpio2 26 0>; linux,default-trigger = "mmc0"; }; - user2 { + led-user2 { label = "sd1-led"; gpios = <&gpio2 24 0>; linux,default-trigger = "mmc2"; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-sps1.dts b/arch/arm/boot/dts/nxp/mxs/imx28-sps1.dts index ca62e7933116..9e19c63a4d51 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-sps1.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-sps1.dts @@ -127,7 +127,7 @@ flash: flash@0 { #address-cells = <1>; #size-cells = <1>; - compatible = "everspin,mr25h256", "mr25h256"; + compatible = "everspin,mr25h256"; spi-max-frequency = <40000000>; reg = <0>; }; diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-tx28.dts b/arch/arm/boot/dts/nxp/mxs/imx28-tx28.dts index 9290635352f1..197eba22f39c 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-tx28.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-tx28.dts @@ -20,11 +20,6 @@ ds1339 = &ds1339; gpio5 = &gpio5; lcdif = &lcdif; - lcdif_23bit_pins = &tx28_lcdif_23bit_pins; - lcdif_24bit_pins = &lcdif_24bit_pins_a; - reg_can_xcvr = ®_can_xcvr; - spi_gpio = &spi_gpio; - spi_mxs = &ssp3; stk5led = &user_led; usbotg = &usb0; }; @@ -122,7 +117,7 @@ leds { compatible = "gpio-leds"; - user_led: user { + user_led: led-user { label = "Heartbeat"; gpios = <&gpio4 10 GPIO_ACTIVE_HIGH>; linux,default-trigger = "heartbeat"; @@ -280,7 +275,7 @@ }; polytouch: edt-ft5x06@38 { - compatible = "edt,edt-ft5x06"; + compatible = "edt,edt-ft5206"; reg = <0x38>; pinctrl-names = "default"; pinctrl-0 = <&tx28_edt_ft5x06_pins>; diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index c7873dcef154..32a44b02d2fa 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -14,8 +14,6 @@ dtb-$(CONFIG_ARCH_QCOM) += \ qcom-apq8064-asus-nexus7-flo.dtb \ qcom-apq8064-lg-nexus4-mako.dtb \ qcom-apq8074-dragonboard.dtb \ - qcom-apq8084-ifc6540.dtb \ - qcom-apq8084-mtp.dtb \ qcom-ipq4018-ap120c-ac.dtb \ qcom-ipq4018-ap120c-ac-bit.dtb \ qcom-ipq4018-jalapeno.dtb \ diff --git a/arch/arm/boot/dts/qcom/qcom-apq8084-ifc6540.dts b/arch/arm/boot/dts/qcom/qcom-apq8084-ifc6540.dts deleted file mode 100644 index 1df24c922be9..000000000000 --- a/arch/arm/boot/dts/qcom/qcom-apq8084-ifc6540.dts +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "qcom-apq8084.dtsi" -#include "pma8084.dtsi" - -/ { - model = "Qualcomm APQ8084/IFC6540"; - compatible = "qcom,apq8084-sbc", "qcom,apq8084"; - - aliases { - serial0 = &blsp2_uart2; - usid0 = &pma8084_0; - }; - - chosen { - stdout-path = "serial0:115200n8"; - }; - - soc { - serial@f995e000 { - status = "okay"; - }; - }; -}; - -&sdhc_1 { - bus-width = <8>; - non-removable; - status = "okay"; -}; - -&sdhc_2 { - cd-gpios = <&tlmm 122 GPIO_ACTIVE_LOW>; - bus-width = <4>; -}; diff --git a/arch/arm/boot/dts/qcom/qcom-apq8084-mtp.dts b/arch/arm/boot/dts/qcom/qcom-apq8084-mtp.dts deleted file mode 100644 index d4e6aee034af..000000000000 --- a/arch/arm/boot/dts/qcom/qcom-apq8084-mtp.dts +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "qcom-apq8084.dtsi" -#include "pma8084.dtsi" - -/ { - model = "Qualcomm APQ 8084-MTP"; - compatible = "qcom,apq8084-mtp", "qcom,apq8084"; - - aliases { - serial0 = &blsp2_uart2; - usid0 = &pma8084_0; - }; - - chosen { - stdout-path = "serial0:115200n8"; - }; - - soc { - serial@f995e000 { - status = "okay"; - }; - }; -}; diff --git a/arch/arm/boot/dts/qcom/qcom-apq8084.dtsi b/arch/arm/boot/dts/qcom/qcom-apq8084.dtsi deleted file mode 100644 index cee0694ef127..000000000000 --- a/arch/arm/boot/dts/qcom/qcom-apq8084.dtsi +++ /dev/null @@ -1,852 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/dts-v1/; - -#include -#include -#include - -/ { - #address-cells = <1>; - #size-cells = <1>; - model = "Qualcomm APQ 8084"; - compatible = "qcom,apq8084"; - interrupt-parent = <&intc>; - - reserved-memory { - #address-cells = <1>; - #size-cells = <1>; - ranges; - - smem_mem: smem-region@fa00000 { - reg = <0xfa00000 0x200000>; - no-map; - }; - }; - - cpus { - #address-cells = <1>; - #size-cells = <0>; - - cpu@0 { - device_type = "cpu"; - compatible = "qcom,krait"; - reg = <0>; - enable-method = "qcom,kpss-acc-v2"; - next-level-cache = <&l2>; - qcom,acc = <&acc0>; - qcom,saw = <&saw0>; - cpu-idle-states = <&cpu_spc>; - }; - - cpu@1 { - device_type = "cpu"; - compatible = "qcom,krait"; - reg = <1>; - enable-method = "qcom,kpss-acc-v2"; - next-level-cache = <&l2>; - qcom,acc = <&acc1>; - qcom,saw = <&saw1>; - cpu-idle-states = <&cpu_spc>; - }; - - cpu@2 { - device_type = "cpu"; - compatible = "qcom,krait"; - reg = <2>; - enable-method = "qcom,kpss-acc-v2"; - next-level-cache = <&l2>; - qcom,acc = <&acc2>; - qcom,saw = <&saw2>; - cpu-idle-states = <&cpu_spc>; - }; - - cpu@3 { - device_type = "cpu"; - compatible = "qcom,krait"; - reg = <3>; - enable-method = "qcom,kpss-acc-v2"; - next-level-cache = <&l2>; - qcom,acc = <&acc3>; - qcom,saw = <&saw3>; - cpu-idle-states = <&cpu_spc>; - }; - - l2: l2-cache { - compatible = "cache"; - cache-level = <2>; - cache-unified; - qcom,saw = <&saw_l2>; - }; - - idle-states { - cpu_spc: cpu-spc { - compatible = "qcom,idle-state-spc", - "arm,idle-state"; - entry-latency-us = <150>; - exit-latency-us = <200>; - min-residency-us = <2000>; - }; - }; - }; - - memory { - device_type = "memory"; - reg = <0x0 0x0>; - }; - - firmware { - scm { - compatible = "qcom,scm-apq8084", "qcom,scm"; - clocks = <&gcc GCC_CE1_CLK> , <&gcc GCC_CE1_AXI_CLK>, <&gcc GCC_CE1_AHB_CLK>; - clock-names = "core", "bus", "iface"; - }; - }; - - thermal-zones { - cpu0-thermal { - polling-delay-passive = <250>; - polling-delay = <1000>; - - thermal-sensors = <&tsens 5>; - - trips { - cpu_alert0: trip0 { - temperature = <75000>; - hysteresis = <2000>; - type = "passive"; - }; - cpu_crit0: trip1 { - temperature = <110000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - }; - - cpu1-thermal { - polling-delay-passive = <250>; - polling-delay = <1000>; - - thermal-sensors = <&tsens 6>; - - trips { - cpu_alert1: trip0 { - temperature = <75000>; - hysteresis = <2000>; - type = "passive"; - }; - cpu_crit1: trip1 { - temperature = <110000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - }; - - cpu2-thermal { - polling-delay-passive = <250>; - polling-delay = <1000>; - - thermal-sensors = <&tsens 7>; - - trips { - cpu_alert2: trip0 { - temperature = <75000>; - hysteresis = <2000>; - type = "passive"; - }; - cpu_crit2: trip1 { - temperature = <110000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - }; - - cpu3-thermal { - polling-delay-passive = <250>; - polling-delay = <1000>; - - thermal-sensors = <&tsens 8>; - - trips { - cpu_alert3: trip0 { - temperature = <75000>; - hysteresis = <2000>; - type = "passive"; - }; - cpu_crit3: trip1 { - temperature = <110000>; - hysteresis = <2000>; - type = "critical"; - }; - }; - }; - }; - - cpu-pmu { - compatible = "qcom,krait-pmu"; - interrupts = ; - }; - - clocks { - xo_board: xo_board { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <19200000>; - }; - - sleep_clk: sleep_clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <32768>; - }; - }; - - timer { - compatible = "arm,armv7-timer"; - interrupts = , - , - , - ; - clock-frequency = <19200000>; - }; - - smem { - compatible = "qcom,smem"; - - qcom,rpm-msg-ram = <&rpm_msg_ram>; - memory-region = <&smem_mem>; - - hwlocks = <&tcsr_mutex 3>; - }; - - soc: soc { - #address-cells = <1>; - #size-cells = <1>; - ranges; - compatible = "simple-bus"; - - intc: interrupt-controller@f9000000 { - compatible = "qcom,msm-qgic2"; - interrupt-controller; - #interrupt-cells = <3>; - reg = <0xf9000000 0x1000>, - <0xf9002000 0x1000>; - }; - - apcs: syscon@f9011000 { - compatible = "syscon"; - reg = <0xf9011000 0x1000>; - }; - - sram@fc190000 { - compatible = "qcom,apq8084-rpm-stats"; - reg = <0xfc190000 0x10000>; - }; - - qfprom: efuse@fc4bc000 { - compatible = "qcom,apq8084-qfprom", "qcom,qfprom"; - reg = <0xfc4bc000 0x1000>; - #address-cells = <1>; - #size-cells = <1>; - - tsens_base1: base1@d0 { - reg = <0xd0 0x1>; - bits = <0 8>; - }; - - tsens_s0_p1: s0-p1@d1 { - reg = <0xd1 0x1>; - bits = <0 6>; - }; - - tsens_s1_p1: s1-p1@d2 { - reg = <0xd1 0x2>; - bits = <6 6>; - }; - - tsens_s2_p1: s2-p1@d2 { - reg = <0xd2 0x2>; - bits = <4 6>; - }; - - tsens_s3_p1: s3-p1@d3 { - reg = <0xd3 0x1>; - bits = <2 6>; - }; - - tsens_s4_p1: s4-p1@d4 { - reg = <0xd4 0x1>; - bits = <0 6>; - }; - - tsens_s5_p1: s5-p1@d4 { - reg = <0xd4 0x2>; - bits = <6 6>; - }; - - tsens_s6_p1: s6-p1@d5 { - reg = <0xd5 0x2>; - bits = <4 6>; - }; - - tsens_s7_p1: s7-p1@d6 { - reg = <0xd6 0x1>; - bits = <2 6>; - }; - - tsens_s8_p1: s8-p1@d7 { - reg = <0xd7 0x1>; - bits = <0 6>; - }; - - tsens_mode: mode@d7 { - reg = <0xd7 0x1>; - bits = <6 2>; - }; - - tsens_s9_p1: s9-p1@d8 { - reg = <0xd8 0x1>; - bits = <0 6>; - }; - - tsens_s10_p1: s10-p1@d8 { - reg = <0xd8 0x2>; - bits = <6 6>; - }; - - tsens_base2: base2@d9 { - reg = <0xd9 0x2>; - bits = <4 8>; - }; - - tsens_s0_p2: s0-p2@da { - reg = <0xda 0x2>; - bits = <4 6>; - }; - - tsens_s1_p2: s1-p2@db { - reg = <0xdb 0x1>; - bits = <2 6>; - }; - - tsens_s2_p2: s2-p2@dc { - reg = <0xdc 0x1>; - bits = <0 6>; - }; - - tsens_s3_p2: s3-p2@dc { - reg = <0xdc 0x2>; - bits = <6 6>; - }; - - tsens_s4_p2: s4-p2@dd { - reg = <0xdd 0x2>; - bits = <4 6>; - }; - - tsens_s5_p2: s5-p2@de { - reg = <0xde 0x2>; - bits = <2 6>; - }; - - tsens_s6_p2: s6-p2@df { - reg = <0xdf 0x1>; - bits = <0 6>; - }; - - tsens_s7_p2: s7-p2@e0 { - reg = <0xe0 0x1>; - bits = <0 6>; - }; - - tsens_s8_p2: s8-p2@e0 { - reg = <0xe0 0x2>; - bits = <6 6>; - }; - - tsens_s9_p2: s9-p2@e1 { - reg = <0xe1 0x2>; - bits = <4 6>; - }; - - tsens_s10_p2: s10-p2@e2 { - reg = <0xe2 0x2>; - bits = <2 6>; - }; - - tsens_s5_p2_backup: s5-p2-backup@e3 { - reg = <0xe3 0x2>; - bits = <0 6>; - }; - - tsens_mode_backup: mode-backup@e3 { - reg = <0xe3 0x1>; - bits = <6 2>; - }; - - tsens_s6_p2_backup: s6-p2-backup@e4 { - reg = <0xe4 0x1>; - bits = <0 6>; - }; - - tsens_s7_p2_backup: s7-p2-backup@e4 { - reg = <0xe4 0x2>; - bits = <6 6>; - }; - - tsens_s8_p2_backup: s8-p2-backup@e5 { - reg = <0xe5 0x2>; - bits = <4 6>; - }; - - tsens_s9_p2_backup: s9-p2-backup@e6 { - reg = <0xe6 0x2>; - bits = <2 6>; - }; - - tsens_s10_p2_backup: s10-p2-backup@e7 { - reg = <0xe7 0x1>; - bits = <0 6>; - }; - - tsens_base1_backup: base1-backup@440 { - reg = <0x440 0x1>; - bits = <0 8>; - }; - - tsens_s0_p1_backup: s0-p1-backup@441 { - reg = <0x441 0x1>; - bits = <0 6>; - }; - - tsens_s1_p1_backup: s1-p1-backup@442 { - reg = <0x441 0x2>; - bits = <6 6>; - }; - - tsens_s2_p1_backup: s2-p1-backup@442 { - reg = <0x442 0x2>; - bits = <4 6>; - }; - - tsens_s3_p1_backup: s3-p1-backup@443 { - reg = <0x443 0x1>; - bits = <2 6>; - }; - - tsens_s4_p1_backup: s4-p1-backup@444 { - reg = <0x444 0x1>; - bits = <0 6>; - }; - - tsens_s5_p1_backup: s5-p1-backup@444 { - reg = <0x444 0x2>; - bits = <6 6>; - }; - - tsens_s6_p1_backup: s6-p1-backup@445 { - reg = <0x445 0x2>; - bits = <4 6>; - }; - - tsens_s7_p1_backup: s7-p1-backup@446 { - reg = <0x446 0x1>; - bits = <2 6>; - }; - - tsens_use_backup: use-backup@447 { - reg = <0x447 0x1>; - bits = <5 3>; - }; - - tsens_s8_p1_backup: s8-p1-backup@448 { - reg = <0x448 0x1>; - bits = <0 6>; - }; - - tsens_s9_p1_backup: s9-p1-backup@448 { - reg = <0x448 0x2>; - bits = <6 6>; - }; - - tsens_s10_p1_backup: s10-p1-backup@449 { - reg = <0x449 0x2>; - bits = <4 6>; - }; - - tsens_base2_backup: base2-backup@44a { - reg = <0x44a 0x2>; - bits = <2 8>; - }; - - tsens_s0_p2_backup: s0-p2-backup@44b { - reg = <0x44b 0x3>; - bits = <2 6>; - }; - - tsens_s1_p2_backup: s1-p2-backup@44c { - reg = <0x44c 0x1>; - bits = <0 6>; - }; - - tsens_s2_p2_backup: s2-p2-backup@44c { - reg = <0x44c 0x2>; - bits = <6 6>; - }; - - tsens_s3_p2_backup: s3-p2-backup@44d { - reg = <0x44d 0x2>; - bits = <4 6>; - }; - - tsens_s4_p2_backup: s4-p2-backup@44e { - reg = <0x44e 0x1>; - bits = <2 6>; - }; - }; - - tsens: thermal-sensor@fc4a9000 { - compatible = "qcom,msm8974-tsens", "qcom,tsens-v0_1"; - reg = <0xfc4a9000 0x1000>, /* TM */ - <0xfc4a8000 0x1000>; /* SROT */ - nvmem-cells = <&tsens_mode>, - <&tsens_base1>, <&tsens_base2>, - <&tsens_use_backup>, - <&tsens_mode_backup>, - <&tsens_base1_backup>, <&tsens_base2_backup>, - <&tsens_s0_p1>, <&tsens_s0_p2>, - <&tsens_s1_p1>, <&tsens_s1_p2>, - <&tsens_s2_p1>, <&tsens_s2_p2>, - <&tsens_s3_p1>, <&tsens_s3_p2>, - <&tsens_s4_p1>, <&tsens_s4_p2>, - <&tsens_s5_p1>, <&tsens_s5_p2>, - <&tsens_s6_p1>, <&tsens_s6_p2>, - <&tsens_s7_p1>, <&tsens_s7_p2>, - <&tsens_s8_p1>, <&tsens_s8_p2>, - <&tsens_s9_p1>, <&tsens_s9_p2>, - <&tsens_s10_p1>, <&tsens_s10_p2>, - <&tsens_s0_p1_backup>, <&tsens_s0_p2_backup>, - <&tsens_s1_p1_backup>, <&tsens_s1_p2_backup>, - <&tsens_s2_p1_backup>, <&tsens_s2_p2_backup>, - <&tsens_s3_p1_backup>, <&tsens_s3_p2_backup>, - <&tsens_s4_p1_backup>, <&tsens_s4_p2_backup>, - <&tsens_s5_p1_backup>, <&tsens_s5_p2_backup>, - <&tsens_s6_p1_backup>, <&tsens_s6_p2_backup>, - <&tsens_s7_p1_backup>, <&tsens_s7_p2_backup>, - <&tsens_s8_p1_backup>, <&tsens_s8_p2_backup>, - <&tsens_s9_p1_backup>, <&tsens_s9_p2_backup>, - <&tsens_s10_p1_backup>, <&tsens_s10_p2_backup>; - nvmem-cell-names = "mode", - "base1", "base2", - "use_backup", - "mode_backup", - "base1_backup", "base2_backup", - "s0_p1", "s0_p2", - "s1_p1", "s1_p2", - "s2_p1", "s2_p2", - "s3_p1", "s3_p2", - "s4_p1", "s4_p2", - "s5_p1", "s5_p2", - "s6_p1", "s6_p2", - "s7_p1", "s7_p2", - "s8_p1", "s8_p2", - "s9_p1", "s9_p2", - "s10_p1", "s10_p2", - "s0_p1_backup", "s0_p2_backup", - "s1_p1_backup", "s1_p2_backup", - "s2_p1_backup", "s2_p2_backup", - "s3_p1_backup", "s3_p2_backup", - "s4_p1_backup", "s4_p2_backup", - "s5_p1_backup", "s5_p2_backup", - "s6_p1_backup", "s6_p2_backup", - "s7_p1_backup", "s7_p2_backup", - "s8_p1_backup", "s8_p2_backup", - "s9_p1_backup", "s9_p2_backup", - "s10_p1_backup", "s10_p2_backup"; - #qcom,sensors = <11>; - interrupts = ; - interrupt-names = "uplow"; - #thermal-sensor-cells = <1>; - }; - timer@f9020000 { - #address-cells = <1>; - #size-cells = <1>; - ranges; - compatible = "arm,armv7-timer-mem"; - reg = <0xf9020000 0x1000>; - clock-frequency = <19200000>; - - frame@f9021000 { - frame-number = <0>; - interrupts = , - ; - reg = <0xf9021000 0x1000>, - <0xf9022000 0x1000>; - }; - - frame@f9023000 { - frame-number = <1>; - interrupts = ; - reg = <0xf9023000 0x1000>; - status = "disabled"; - }; - - frame@f9024000 { - frame-number = <2>; - interrupts = ; - reg = <0xf9024000 0x1000>; - status = "disabled"; - }; - - frame@f9025000 { - frame-number = <3>; - interrupts = ; - reg = <0xf9025000 0x1000>; - status = "disabled"; - }; - - frame@f9026000 { - frame-number = <4>; - interrupts = ; - reg = <0xf9026000 0x1000>; - status = "disabled"; - }; - - frame@f9027000 { - frame-number = <5>; - interrupts = ; - reg = <0xf9027000 0x1000>; - status = "disabled"; - }; - - frame@f9028000 { - frame-number = <6>; - interrupts = ; - reg = <0xf9028000 0x1000>; - status = "disabled"; - }; - }; - - saw0: power-manager@f9089000 { - compatible = "qcom,apq8084-saw2-v2.1-cpu", "qcom,saw2"; - reg = <0xf9089000 0x1000>, <0xf9009000 0x1000>; - }; - - saw1: power-manager@f9099000 { - compatible = "qcom,apq8084-saw2-v2.1-cpu", "qcom,saw2"; - reg = <0xf9099000 0x1000>, <0xf9009000 0x1000>; - }; - - saw2: power-manager@f90a9000 { - compatible = "qcom,apq8084-saw2-v2.1-cpu", "qcom,saw2"; - reg = <0xf90a9000 0x1000>, <0xf9009000 0x1000>; - }; - - saw3: power-manager@f90b9000 { - compatible = "qcom,apq8084-saw2-v2.1-cpu", "qcom,saw2"; - reg = <0xf90b9000 0x1000>, <0xf9009000 0x1000>; - }; - - saw_l2: power-manager@f9012000 { - compatible = "qcom,apq8084-saw2-v2.1-l2", "qcom,saw2"; - reg = <0xf9012000 0x1000>; - }; - - acc0: power-manager@f9088000 { - compatible = "qcom,kpss-acc-v2"; - reg = <0xf9088000 0x1000>, - <0xf9008000 0x1000>; - }; - - acc1: power-manager@f9098000 { - compatible = "qcom,kpss-acc-v2"; - reg = <0xf9098000 0x1000>, - <0xf9008000 0x1000>; - }; - - acc2: power-manager@f90a8000 { - compatible = "qcom,kpss-acc-v2"; - reg = <0xf90a8000 0x1000>, - <0xf9008000 0x1000>; - }; - - acc3: power-manager@f90b8000 { - compatible = "qcom,kpss-acc-v2"; - reg = <0xf90b8000 0x1000>, - <0xf9008000 0x1000>; - }; - - restart@fc4ab000 { - compatible = "qcom,pshold"; - reg = <0xfc4ab000 0x4>; - }; - - gcc: clock-controller@fc400000 { - compatible = "qcom,gcc-apq8084"; - #clock-cells = <1>; - #reset-cells = <1>; - #power-domain-cells = <1>; - reg = <0xfc400000 0x4000>; - clocks = <&xo_board>, - <&sleep_clk>, - <0>, /* ufs */ - <0>, - <0>, - <0>, - <0>, /* sata */ - <0>, - <0>; /* pcie */ - clock-names = "xo", - "sleep_clk", - "ufs_rx_symbol_0_clk_src", - "ufs_rx_symbol_1_clk_src", - "ufs_tx_symbol_0_clk_src", - "ufs_tx_symbol_1_clk_src", - "sata_asic0_clk", - "sata_rx_clk", - "pcie_pipe"; - }; - - tcsr_mutex: hwlock@fd484000 { - compatible = "qcom,apq8084-tcsr-mutex", "qcom,tcsr-mutex"; - reg = <0xfd484000 0x1000>; - #hwlock-cells = <1>; - }; - - rpm_msg_ram: sram@fc428000 { - compatible = "qcom,rpm-msg-ram"; - reg = <0xfc428000 0x4000>; - }; - - tlmm: pinctrl@fd510000 { - compatible = "qcom,apq8084-pinctrl"; - reg = <0xfd510000 0x4000>; - gpio-controller; - gpio-ranges = <&tlmm 0 0 147>; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - interrupts = ; - }; - - blsp2_uart2: serial@f995e000 { - compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; - reg = <0xf995e000 0x1000>; - interrupts = ; - clocks = <&gcc GCC_BLSP2_UART2_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>; - clock-names = "core", "iface"; - status = "disabled"; - }; - - sdhc_1: mmc@f9824900 { - compatible = "qcom,apq8084-sdhci", "qcom,sdhci-msm-v4"; - reg = <0xf9824900 0x11c>, <0xf9824000 0x800>; - reg-names = "hc", "core"; - interrupts = , ; - interrupt-names = "hc_irq", "pwr_irq"; - clocks = <&gcc GCC_SDCC1_AHB_CLK>, - <&gcc GCC_SDCC1_APPS_CLK>, - <&xo_board>; - clock-names = "iface", "core", "xo"; - status = "disabled"; - }; - - sdhc_2: mmc@f98a4900 { - compatible = "qcom,apq8084-sdhci", "qcom,sdhci-msm-v4"; - reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>; - reg-names = "hc", "core"; - interrupts = , ; - interrupt-names = "hc_irq", "pwr_irq"; - clocks = <&gcc GCC_SDCC2_AHB_CLK>, - <&gcc GCC_SDCC2_APPS_CLK>, - <&xo_board>; - clock-names = "iface", "core", "xo"; - status = "disabled"; - }; - - spmi_bus: spmi@fc4cf000 { - compatible = "qcom,spmi-pmic-arb"; - reg-names = "core", "intr", "cnfg"; - reg = <0xfc4cf000 0x1000>, - <0xfc4cb000 0x1000>, - <0xfc4ca000 0x1000>; - interrupt-names = "periph_irq"; - interrupts = ; - qcom,ee = <0>; - qcom,channel = <0>; - #address-cells = <2>; - #size-cells = <0>; - interrupt-controller; - #interrupt-cells = <4>; - }; - }; - - rpm: remoteproc { - compatible = "qcom,apq8084-rpm-proc", "qcom,rpm-proc"; - - smd-edge { - interrupts = ; - qcom,ipc = <&apcs 8 0>; - qcom,smd-edge = <15>; - - rpm-requests { - compatible = "qcom,rpm-apq8084", "qcom,smd-rpm"; - qcom,smd-channels = "rpm_requests"; - - regulators-0 { - compatible = "qcom,rpm-pma8084-regulators"; - - pma8084_s1: s1 {}; - pma8084_s2: s2 {}; - pma8084_s3: s3 {}; - pma8084_s4: s4 {}; - pma8084_s5: s5 {}; - pma8084_s6: s6 {}; - pma8084_s7: s7 {}; - pma8084_s8: s8 {}; - pma8084_s9: s9 {}; - pma8084_s10: s10 {}; - pma8084_s11: s11 {}; - pma8084_s12: s12 {}; - - pma8084_l1: l1 {}; - pma8084_l2: l2 {}; - pma8084_l3: l3 {}; - pma8084_l4: l4 {}; - pma8084_l5: l5 {}; - pma8084_l6: l6 {}; - pma8084_l7: l7 {}; - pma8084_l8: l8 {}; - pma8084_l9: l9 {}; - pma8084_l10: l10 {}; - pma8084_l11: l11 {}; - pma8084_l12: l12 {}; - pma8084_l13: l13 {}; - pma8084_l14: l14 {}; - pma8084_l15: l15 {}; - pma8084_l16: l16 {}; - pma8084_l17: l17 {}; - pma8084_l18: l18 {}; - pma8084_l19: l19 {}; - pma8084_l20: l20 {}; - pma8084_l21: l21 {}; - pma8084_l22: l22 {}; - pma8084_l23: l23 {}; - pma8084_l24: l24 {}; - pma8084_l25: l25 {}; - pma8084_l26: l26 {}; - pma8084_l27: l27 {}; - - pma8084_lvs1: lvs1 {}; - pma8084_lvs2: lvs2 {}; - pma8084_lvs3: lvs3 {}; - pma8084_lvs4: lvs4 {}; - - pma8084_5vs1: 5vs1 {}; - }; - }; - }; - }; -}; diff --git a/arch/arm/boot/dts/qcom/qcom-ipq8062-smb208.dtsi b/arch/arm/boot/dts/qcom/qcom-ipq8062-smb208.dtsi deleted file mode 100644 index 9d06255104c7..000000000000 --- a/arch/arm/boot/dts/qcom/qcom-ipq8062-smb208.dtsi +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#include "qcom-ipq8062.dtsi" - -&rpm { - smb208_regulators: regulators { - compatible = "qcom,rpm-smb208-regulators"; - - smb208_s1a: s1a { - regulator-min-microvolt = <1050000>; - regulator-max-microvolt = <1150000>; - - qcom,switch-mode-frequency = <1200000>; - }; - - smb208_s1b: s1b { - regulator-min-microvolt = <1050000>; - regulator-max-microvolt = <1150000>; - - qcom,switch-mode-frequency = <1200000>; - }; - - smb208_s2a: s2a { - regulator-min-microvolt = < 800000>; - regulator-max-microvolt = <1150000>; - - qcom,switch-mode-frequency = <1200000>; - }; - - smb208_s2b: s2b { - regulator-min-microvolt = < 800000>; - regulator-max-microvolt = <1150000>; - - qcom,switch-mode-frequency = <1200000>; - }; - }; -}; diff --git a/arch/arm/boot/dts/qcom/qcom-ipq8062.dtsi b/arch/arm/boot/dts/qcom/qcom-ipq8062.dtsi deleted file mode 100644 index 5d3ebd3e2e51..000000000000 --- a/arch/arm/boot/dts/qcom/qcom-ipq8062.dtsi +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#include "qcom-ipq8064-v2.0.dtsi" - -/ { - model = "Qualcomm Technologies, Inc. IPQ8062"; - compatible = "qcom,ipq8062", "qcom,ipq8064"; -}; diff --git a/arch/arm/boot/dts/qcom/qcom-ipq8064-smb208.dtsi b/arch/arm/boot/dts/qcom/qcom-ipq8064-smb208.dtsi deleted file mode 100644 index ac9c44f0c164..000000000000 --- a/arch/arm/boot/dts/qcom/qcom-ipq8064-smb208.dtsi +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include "qcom-ipq8064.dtsi" - -&rpm { - smb208_regulators: regulators { - compatible = "qcom,rpm-smb208-regulators"; - - smb208_s1a: s1a { - regulator-min-microvolt = <1050000>; - regulator-max-microvolt = <1150000>; - - qcom,switch-mode-frequency = <1200000>; - }; - - smb208_s1b: s1b { - regulator-min-microvolt = <1050000>; - regulator-max-microvolt = <1150000>; - - qcom,switch-mode-frequency = <1200000>; - }; - - smb208_s2a: s2a { - regulator-min-microvolt = < 800000>; - regulator-max-microvolt = <1250000>; - - qcom,switch-mode-frequency = <1200000>; - }; - - smb208_s2b: s2b { - regulator-min-microvolt = < 800000>; - regulator-max-microvolt = <1250000>; - - qcom,switch-mode-frequency = <1200000>; - }; - }; -}; diff --git a/arch/arm/boot/dts/qcom/qcom-ipq8064-v2.0-smb208.dtsi b/arch/arm/boot/dts/qcom/qcom-ipq8064-v2.0-smb208.dtsi deleted file mode 100644 index 0442580b22de..000000000000 --- a/arch/arm/boot/dts/qcom/qcom-ipq8064-v2.0-smb208.dtsi +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include "qcom-ipq8064-v2.0.dtsi" - -&rpm { - smb208_regulators: regulators { - compatible = "qcom,rpm-smb208-regulators"; - - smb208_s1a: s1a { - regulator-min-microvolt = <1050000>; - regulator-max-microvolt = <1150000>; - - qcom,switch-mode-frequency = <1200000>; - }; - - smb208_s1b: s1b { - regulator-min-microvolt = <1050000>; - regulator-max-microvolt = <1150000>; - - qcom,switch-mode-frequency = <1200000>; - }; - - smb208_s2a: s2a { - regulator-min-microvolt = < 800000>; - regulator-max-microvolt = <1250000>; - - qcom,switch-mode-frequency = <1200000>; - }; - - smb208_s2b: s2b { - regulator-min-microvolt = < 800000>; - regulator-max-microvolt = <1250000>; - - qcom,switch-mode-frequency = <1200000>; - }; - }; -}; diff --git a/arch/arm/boot/dts/qcom/qcom-ipq8065-smb208.dtsi b/arch/arm/boot/dts/qcom/qcom-ipq8065-smb208.dtsi deleted file mode 100644 index 803e6ff99ef8..000000000000 --- a/arch/arm/boot/dts/qcom/qcom-ipq8065-smb208.dtsi +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include "qcom-ipq8065.dtsi" - -&rpm { - smb208_regulators: regulators { - compatible = "qcom,rpm-smb208-regulators"; - - smb208_s1a: s1a { - regulator-min-microvolt = <1050000>; - regulator-max-microvolt = <1150000>; - - qcom,switch-mode-frequency = <1200000>; - }; - - smb208_s1b: s1b { - regulator-min-microvolt = <1050000>; - regulator-max-microvolt = <1150000>; - - qcom,switch-mode-frequency = <1200000>; - }; - - smb208_s2a: s2a { - regulator-min-microvolt = <775000>; - regulator-max-microvolt = <1275000>; - - qcom,switch-mode-frequency = <1200000>; - }; - - smb208_s2b: s2b { - regulator-min-microvolt = <775000>; - regulator-max-microvolt = <1275000>; - - qcom,switch-mode-frequency = <1200000>; - }; - }; -}; diff --git a/arch/arm/boot/dts/qcom/qcom-ipq8065.dtsi b/arch/arm/boot/dts/qcom/qcom-ipq8065.dtsi deleted file mode 100644 index ea49f6cc416d..000000000000 --- a/arch/arm/boot/dts/qcom/qcom-ipq8065.dtsi +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include "qcom-ipq8064-v2.0.dtsi" - -/ { - model = "Qualcomm Technologies, Inc. IPQ8065"; - compatible = "qcom,ipq8065", "qcom,ipq8064"; -}; diff --git a/arch/arm/boot/dts/qcom/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom/qcom-msm8974.dtsi index 2a82ddce94a2..95be1d2e214f 100644 --- a/arch/arm/boot/dts/qcom/qcom-msm8974.dtsi +++ b/arch/arm/boot/dts/qcom/qcom-msm8974.dtsi @@ -1115,9 +1115,6 @@ reg = <0xfc380000 0x6a000>; compatible = "qcom,msm8974-bimc"; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_BIMC_CLK>, - <&rpmcc RPM_SMD_BIMC_A_CLK>; }; gcc: clock-controller@fc400000 { @@ -1162,45 +1159,32 @@ reg = <0xfc460000 0x4000>; compatible = "qcom,msm8974-snoc"; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_SNOC_CLK>, - <&rpmcc RPM_SMD_SNOC_A_CLK>; }; pnoc: interconnect@fc468000 { reg = <0xfc468000 0x4000>; compatible = "qcom,msm8974-pnoc"; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_PNOC_CLK>, - <&rpmcc RPM_SMD_PNOC_A_CLK>; }; ocmemnoc: interconnect@fc470000 { reg = <0xfc470000 0x4000>; compatible = "qcom,msm8974-ocmemnoc"; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_OCMEMGX_CLK>, - <&rpmcc RPM_SMD_OCMEMGX_A_CLK>; }; mmssnoc: interconnect@fc478000 { reg = <0xfc478000 0x4000>; compatible = "qcom,msm8974-mmssnoc"; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&mmcc MMSS_S0_AXI_CLK>, - <&mmcc MMSS_S0_AXI_CLK>; + clocks = <&mmcc MMSS_S0_AXI_CLK>; + clock-names = "bus"; }; cnoc: interconnect@fc480000 { reg = <0xfc480000 0x4000>; compatible = "qcom,msm8974-cnoc"; #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_CNOC_CLK>, - <&rpmcc RPM_SMD_CNOC_A_CLK>; }; tsens: thermal-sensor@fc4a9000 { @@ -2223,6 +2207,7 @@ <0xfec00000 0x180000>; reg-names = "ctrl", "mem"; ranges = <0 0xfec00000 0x180000>; + /* core clock doesn't exist anymore, kept for ABI compliance */ clocks = <&rpmcc RPM_SMD_OCMEMGX_CLK>, <&mmcc OCMEMCX_OCMEMNOC_CLK>; clock-names = "core", "iface"; diff --git a/arch/arm/boot/dts/renesas/r7s9210-rza2mevb.dts b/arch/arm/boot/dts/renesas/r7s9210-rza2mevb.dts index f69a7fe56b6e..55221c82ef64 100644 --- a/arch/arm/boot/dts/renesas/r7s9210-rza2mevb.dts +++ b/arch/arm/boot/dts/renesas/r7s9210-rza2mevb.dts @@ -94,8 +94,7 @@ renesas,no-ether-link; phy-handle = <&phy1>; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id001c.c816", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id001c.c816"; reg = <0>; }; }; diff --git a/arch/arm/boot/dts/renesas/r8a7740-armadillo800eva.dts b/arch/arm/boot/dts/renesas/r8a7740-armadillo800eva.dts index 04d24b6d8056..1d56bdef5453 100644 --- a/arch/arm/boot/dts/renesas/r8a7740-armadillo800eva.dts +++ b/arch/arm/boot/dts/renesas/r8a7740-armadillo800eva.dts @@ -65,6 +65,17 @@ enable-active-high; }; + vcc_sdhi1: regulator-vcc-sdhi1 { + compatible = "regulator-fixed"; + + regulator-name = "SDHI1 Vcc"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&pfc 16 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + reg_5p0v: regulator-5p0v { compatible = "regulator-fixed"; regulator-name = "fixed-5.0V"; @@ -228,6 +239,7 @@ pinctrl-0 = <&st1232_pins>; pinctrl-names = "default"; gpios = <&pfc 166 GPIO_ACTIVE_LOW>; + wakeup-source; }; }; @@ -285,6 +297,11 @@ function = "sdhi0"; }; + sdhi1_pins: sd1 { + groups = "sdhi1_data4", "sdhi1_ctrl", "sdhi1_cd", "sdhi1_wp"; + function = "sdhi1"; + }; + fsia_pins: sounda { groups = "fsia_sclk_in", "fsia_mclk_out", "fsia_data_in_1", "fsia_data_out_0"; @@ -302,6 +319,12 @@ gpios = <176 0>; output-high; }; + + sdhi1-select-hog { + gpio-hog; + gpios = <6 0>; + input; + }; }; &tpu { @@ -336,6 +359,15 @@ status = "okay"; }; +&sdhi1 { + pinctrl-0 = <&sdhi1_pins>; + pinctrl-names = "default"; + + vmmc-supply = <&vcc_sdhi1>; + bus-width = <4>; + status = "okay"; +}; + &sh_fsi2 { pinctrl-0 = <&fsia_pins>; pinctrl-names = "default"; diff --git a/arch/arm/boot/dts/renesas/r8a7742-iwg21d-q7-dbcm-ca.dts b/arch/arm/boot/dts/renesas/r8a7742-iwg21d-q7-dbcm-ca.dts index 33ac4bd1e63b..c43c08d9ff94 100644 --- a/arch/arm/boot/dts/renesas/r8a7742-iwg21d-q7-dbcm-ca.dts +++ b/arch/arm/boot/dts/renesas/r8a7742-iwg21d-q7-dbcm-ca.dts @@ -85,8 +85,7 @@ status = "okay"; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1560", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1560"; reg = <1>; micrel,led-mode = <1>; }; diff --git a/arch/arm/boot/dts/renesas/r8a7743-sk-rzg1m.dts b/arch/arm/boot/dts/renesas/r8a7743-sk-rzg1m.dts index 9b16fe7ce713..60217797e534 100644 --- a/arch/arm/boot/dts/renesas/r8a7743-sk-rzg1m.dts +++ b/arch/arm/boot/dts/renesas/r8a7743-sk-rzg1m.dts @@ -70,8 +70,7 @@ status = "okay"; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1537", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1537"; reg = <1>; interrupts-extended = <&irqc 0 IRQ_TYPE_LEVEL_LOW>; micrel,led-mode = <1>; diff --git a/arch/arm/boot/dts/renesas/r8a7745-sk-rzg1e.dts b/arch/arm/boot/dts/renesas/r8a7745-sk-rzg1e.dts index 571615a50620..42e82f069755 100644 --- a/arch/arm/boot/dts/renesas/r8a7745-sk-rzg1e.dts +++ b/arch/arm/boot/dts/renesas/r8a7745-sk-rzg1e.dts @@ -65,8 +65,7 @@ status = "okay"; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1537", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1537"; reg = <1>; interrupts-extended = <&irqc 8 IRQ_TYPE_LEVEL_LOW>; micrel,led-mode = <1>; diff --git a/arch/arm/boot/dts/renesas/r8a7790-lager.dts b/arch/arm/boot/dts/renesas/r8a7790-lager.dts index 4f002aa7fbaf..8e7665501675 100644 --- a/arch/arm/boot/dts/renesas/r8a7790-lager.dts +++ b/arch/arm/boot/dts/renesas/r8a7790-lager.dts @@ -685,8 +685,7 @@ status = "okay"; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1537", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1537"; reg = <1>; interrupts-extended = <&irqc0 0 IRQ_TYPE_LEVEL_LOW>; micrel,led-mode = <1>; diff --git a/arch/arm/boot/dts/renesas/r8a7790-stout.dts b/arch/arm/boot/dts/renesas/r8a7790-stout.dts index b1e20579e071..8ba9d85f1038 100644 --- a/arch/arm/boot/dts/renesas/r8a7790-stout.dts +++ b/arch/arm/boot/dts/renesas/r8a7790-stout.dts @@ -208,8 +208,7 @@ status = "okay"; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1537", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1537"; reg = <1>; interrupts-extended = <&irqc0 1 IRQ_TYPE_LEVEL_LOW>; micrel,led-mode = <1>; diff --git a/arch/arm/boot/dts/renesas/r8a7791-koelsch.dts b/arch/arm/boot/dts/renesas/r8a7791-koelsch.dts index 61ea438eb6af..48db62e0ff87 100644 --- a/arch/arm/boot/dts/renesas/r8a7791-koelsch.dts +++ b/arch/arm/boot/dts/renesas/r8a7791-koelsch.dts @@ -676,8 +676,7 @@ status = "okay"; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1537", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1537"; reg = <1>; interrupts-extended = <&irqc0 0 IRQ_TYPE_LEVEL_LOW>; micrel,led-mode = <1>; diff --git a/arch/arm/boot/dts/renesas/r8a7791-porter.dts b/arch/arm/boot/dts/renesas/r8a7791-porter.dts index 81b3c5d74e9b..811e263452ac 100644 --- a/arch/arm/boot/dts/renesas/r8a7791-porter.dts +++ b/arch/arm/boot/dts/renesas/r8a7791-porter.dts @@ -326,8 +326,7 @@ status = "okay"; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1537", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1537"; reg = <1>; interrupts-extended = <&irqc0 0 IRQ_TYPE_LEVEL_LOW>; micrel,led-mode = <1>; diff --git a/arch/arm/boot/dts/renesas/r8a7793-gose.dts b/arch/arm/boot/dts/renesas/r8a7793-gose.dts index 5c6928c941ac..69d9c674bb03 100644 --- a/arch/arm/boot/dts/renesas/r8a7793-gose.dts +++ b/arch/arm/boot/dts/renesas/r8a7793-gose.dts @@ -616,8 +616,7 @@ status = "okay"; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1537", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1537"; reg = <1>; interrupts-extended = <&irqc0 0 IRQ_TYPE_LEVEL_LOW>; micrel,led-mode = <1>; diff --git a/arch/arm/boot/dts/renesas/r8a7794-alt.dts b/arch/arm/boot/dts/renesas/r8a7794-alt.dts index 3f06a7f67d62..5d6d0d8cc4dd 100644 --- a/arch/arm/boot/dts/renesas/r8a7794-alt.dts +++ b/arch/arm/boot/dts/renesas/r8a7794-alt.dts @@ -378,8 +378,7 @@ status = "okay"; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1537", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1537"; reg = <1>; interrupts-extended = <&irqc0 8 IRQ_TYPE_LEVEL_LOW>; micrel,led-mode = <1>; diff --git a/arch/arm/boot/dts/renesas/r8a7794-silk.dts b/arch/arm/boot/dts/renesas/r8a7794-silk.dts index 342825605768..af474b1d9676 100644 --- a/arch/arm/boot/dts/renesas/r8a7794-silk.dts +++ b/arch/arm/boot/dts/renesas/r8a7794-silk.dts @@ -412,8 +412,7 @@ status = "okay"; phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1537", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1537"; reg = <1>; interrupts-extended = <&irqc0 8 IRQ_TYPE_LEVEL_LOW>; micrel,led-mode = <1>; diff --git a/arch/arm/boot/dts/renesas/r9a06g032-rzn1d400-db.dts b/arch/arm/boot/dts/renesas/r9a06g032-rzn1d400-db.dts index 4a72aa7663f2..5626d7fd6c3e 100644 --- a/arch/arm/boot/dts/renesas/r9a06g032-rzn1d400-db.dts +++ b/arch/arm/boot/dts/renesas/r9a06g032-rzn1d400-db.dts @@ -185,6 +185,18 @@ }; }; +&gpioirqmux { + interrupt-map = <89 &gic GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>, /* pin 147: phy@4 */ + <91 &gic GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>; /* pin 149: phy@5 */ + status = "okay"; +}; + +&gpio2 { + pinctrl-0 = <&pins_gpio2>; + pinctrl-names = "default"; + status = "okay"; +}; + &i2c2 { pinctrl-0 = <&pins_i2c2>; pinctrl-names = "default"; @@ -237,13 +249,13 @@ pinctrl-names = "default"; pinctrl-0 = <&pins_cpld>; - pins_can0: pins_can0 { + pins_can0: pins-can0 { pinmux = , /* CAN0_TXD */ ; /* CAN0_RXD */ drive-strength = <6>; }; - pins_can1: pins_can1 { + pins_can1: pins-can1 { pinmux = , /* CAN1_TXD */ ; /* CAN1_RXD */ drive-strength = <6>; @@ -256,7 +268,7 @@ ; }; - pins_eth3: pins_eth3 { + pins_eth3: pins-eth3 { pinmux = , , , @@ -273,7 +285,7 @@ bias-disable; }; - pins_eth4: pins_eth4 { + pins_eth4: pins-eth4 { pinmux = , , , @@ -290,16 +302,101 @@ bias-disable; }; - pins_i2c2: pins_i2c2 { + pins_gpio2: pins-gpio2 { + pinmux = , + ; + drive-strength = <6>; + bias-pull-up; + }; + + pins_i2c2: pins-i2c2 { pinmux = , ; drive-strength = <12>; }; - pins_mdio1: pins_mdio1 { + pins_mdio1: pins-mdio1 { pinmux = , ; }; + + pins_qspi0: pins-qspi0 { + pinmux = , + , + , + , + , + ; + bias-disable; + }; +}; + +&qspi0 { + pinctrl-0 = <&pins_qspi0>; + pinctrl-names = "default"; + status = "okay"; + bootph-all; + + flash@0 { + reg = <0>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <62500000>; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <4>; + cdns,read-delay = <1>; + cdns,tshsl-ns = <200>; + cdns,tsd2d-ns = <255>; + cdns,tchsh-ns = <20>; + cdns,tslch-ns = <20>; + bootph-all; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + /* 64KiB */ + label = "spl"; + reg = <0x0000000 0x00010000>; + }; + partition@10000 { + /* 64KiB */ + label = "pkgt"; + reg = <0x0010000 0x00010000>; + }; + partition@20000 { + /* 512KiB */ + label = "u-boot"; + reg = <0x0020000 0x00080000>; + }; + partition@a0000 { + /* 64KiB */ + label = "env"; + reg = <0x00a0000 0x00010000>; + }; + partition@b0000 { + /* 128KiB */ + label = "dtb"; + reg = <0x00b0000 0x00020000>; + }; + partition@d0000 { + /* 1MiB */ + label = "cm3"; + reg = <0x00d0000 0x00100000>; + }; + partition@1d0000 { + /* 6MiB */ + label = "kernel"; + reg = <0x01d0000 0x00600000>; + }; + partition@7d0000 { + /* Remaining */ + label = "data"; + reg = <0x07d0000 0x1830000>; + }; + }; + }; }; &rtc0 { @@ -323,11 +420,13 @@ switch0phy4: ethernet-phy@4 { reg = <4>; micrel,led-mode = <1>; + interrupts-extended = <&gpio2a 25 IRQ_TYPE_LEVEL_LOW>; }; switch0phy5: ethernet-phy@5 { reg = <5>; micrel,led-mode = <1>; + interrupts-extended = <&gpio2a 27 IRQ_TYPE_LEVEL_LOW>; }; }; }; diff --git a/arch/arm/boot/dts/renesas/r9a06g032.dtsi b/arch/arm/boot/dts/renesas/r9a06g032.dtsi index f4f760aff28b..442ea26b40f5 100644 --- a/arch/arm/boot/dts/renesas/r9a06g032.dtsi +++ b/arch/arm/boot/dts/renesas/r9a06g032.dtsi @@ -15,6 +15,39 @@ #size-cells = <1>; interrupt-parent = <&gic>; + /* + * The CPUs clock is based on the 'ref' clock (output of OPPDIV divisor) + * with x1, x2 or x4 ratio between the CPUs clock frequency and this + * 'ref' clock frequency. + * + * The table below is built on the assumption that the 'ref' clock + * frequency is set to 500MHz which is its default value. + * + * The table should be overridden in the board device-tree file based + * on the 'ref' clock frequency if this frequency value is not the + * default one. + */ + cpu_opp_table: opp-table-cpu { + compatible = "operating-points-v2"; + opp-shared; + + opp-125000000 { + opp-hz = /bits/ 64 <125000000>; + /* ~35 clocks cycles at 125mhz */ + clock-latency-ns = <300>; + }; + + opp-250000000 { + opp-hz = /bits/ 64 <250000000>; + clock-latency-ns = <300>; + }; + + opp-500000000 { + opp-hz = /bits/ 64 <500000000>; + clock-latency-ns = <300>; + }; + }; + cpus { #address-cells = <1>; #size-cells = <0>; @@ -24,6 +57,7 @@ compatible = "arm,cortex-a7"; reg = <0>; clocks = <&sysctrl R9A06G032_CLK_A7MP>; + operating-points-v2 = <&cpu_opp_table>; }; cpu@1 { @@ -33,6 +67,7 @@ clocks = <&sysctrl R9A06G032_CLK_A7MP>; enable-method = "renesas,r9a06g032-smp"; cpu-release-addr = <0 0x4000c204>; + operating-points-v2 = <&cpu_opp_table>; }; }; @@ -66,6 +101,18 @@ #size-cells = <1>; ranges; + qspi0: spi@40005000 { + compatible = "renesas,r9a06g032-qspi", "renesas,rzn1-qspi"; + reg = <0x40005000 0x1000>, <0x10000000 0x10000000>; + interrupts = ; + clocks = <&sysctrl R9A06G032_CLK_QSPI0>, <&sysctrl R9A06G032_HCLK_QSPI0>, + <&sysctrl R9A06G032_HCLK_QSPI0>; + clock-names = "ref", "ahb", "apb"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + rtc0: rtc@40006000 { compatible = "renesas,r9a06g032-rtc", "renesas,rzn1-rtc"; reg = <0x40006000 0x1000>; @@ -507,6 +554,7 @@ compatible = "arm,gic-400", "arm,cortex-a7-gic"; interrupt-controller; #interrupt-cells = <3>; + #address-cells = <0>; reg = <0x44101000 0x1000>, /* Distributer */ <0x44102000 0x2000>, /* CPU interface */ <0x44104000 0x2000>, /* Virt interface control */ diff --git a/arch/arm/boot/dts/rockchip/Makefile b/arch/arm/boot/dts/rockchip/Makefile index 716f5540e438..d0154fd7ff24 100644 --- a/arch/arm/boot/dts/rockchip/Makefile +++ b/arch/arm/boot/dts/rockchip/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_ARCH_ROCKCHIP) += \ + rv1103b-omega4-evb.dtb \ rv1108-elgin-r1.dtb \ rv1108-evb.dtb \ rv1109-relfor-saib.dtb \ diff --git a/arch/arm/boot/dts/rockchip/rk3036-evb.dts b/arch/arm/boot/dts/rockchip/rk3036-evb.dts index becdc0b664bf..c8100dc4c7ce 100644 --- a/arch/arm/boot/dts/rockchip/rk3036-evb.dts +++ b/arch/arm/boot/dts/rockchip/rk3036-evb.dts @@ -16,8 +16,6 @@ &emac { phy = <&phy0>; - phy-reset-duration = <10>; /* millisecond */ - phy-reset-gpios = <&gpio2 RK_PC6 GPIO_ACTIVE_LOW>; /* PHY_RST */ pinctrl-names = "default"; pinctrl-0 = <&emac_xfer>, <&emac_mdio>; status = "okay"; @@ -28,6 +26,8 @@ phy0: ethernet-phy@0 { reg = <0>; + reset-gpios = <&gpio2 RK_PC6 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; }; }; }; diff --git a/arch/arm/boot/dts/rockchip/rk3036-kylin.dts b/arch/arm/boot/dts/rockchip/rk3036-kylin.dts index ae2f84a4e922..bc6e6468fcc4 100644 --- a/arch/arm/boot/dts/rockchip/rk3036-kylin.dts +++ b/arch/arm/boot/dts/rockchip/rk3036-kylin.dts @@ -102,8 +102,6 @@ &emac { phy = <&phy0>; - phy-reset-duration = <10>; /* millisecond */ - phy-reset-gpios = <&gpio2 RK_PC6 GPIO_ACTIVE_LOW>; /* PHY_RST */ pinctrl-names = "default"; pinctrl-0 = <&emac_xfer>, <&emac_mdio>; status = "okay"; @@ -114,6 +112,8 @@ phy0: ethernet-phy@0 { reg = <0>; + reset-gpios = <&gpio2 RK_PC6 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; }; }; }; diff --git a/arch/arm/boot/dts/rockchip/rk3188-bqedison2qc.dts b/arch/arm/boot/dts/rockchip/rk3188-bqedison2qc.dts index edc2b7f9112d..b56095fc2441 100644 --- a/arch/arm/boot/dts/rockchip/rk3188-bqedison2qc.dts +++ b/arch/arm/boot/dts/rockchip/rk3188-bqedison2qc.dts @@ -262,7 +262,7 @@ interrupts = ; pinctrl-names = "default"; pinctrl-0 = <&gsensor_int>; - rotation-matrix = "1", "0", "0", + mount-matrix = "1", "0", "0", "0", "-1", "0", "0", "0", "1"; vdd-supply = <&vcc_io>; diff --git a/arch/arm/boot/dts/rockchip/rk3288-firefly-reload.dts b/arch/arm/boot/dts/rockchip/rk3288-firefly-reload.dts index a55270672732..8b491b002992 100644 --- a/arch/arm/boot/dts/rockchip/rk3288-firefly-reload.dts +++ b/arch/arm/boot/dts/rockchip/rk3288-firefly-reload.dts @@ -197,11 +197,10 @@ }; &i2c0 { - hym8563: hym8563@51 { + hym8563: rtc@51 { compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; - clock-frequency = <32768>; clock-output-names = "xin32k"; interrupt-parent = <&gpio7>; interrupts = ; diff --git a/arch/arm/boot/dts/rockchip/rk3288-phycore-rdk.dts b/arch/arm/boot/dts/rockchip/rk3288-phycore-rdk.dts index 10ce0554d4fc..46362e804daf 100644 --- a/arch/arm/boot/dts/rockchip/rk3288-phycore-rdk.dts +++ b/arch/arm/boot/dts/rockchip/rk3288-phycore-rdk.dts @@ -86,6 +86,10 @@ touchscreen@44 { compatible = "st,stmpe811"; reg = <0x44>; + interrupt-parent = <&gpio5>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&ts_irq_pin>; }; adc@64 { @@ -94,7 +98,7 @@ }; i2c_rtc: rtc@68 { - compatible = "rv4162"; + compatible = "microcrystal,rv4162"; reg = <0x68>; pinctrl-names = "default"; pinctrl-0 = <&i2c_rtc_int>; @@ -121,25 +125,25 @@ compatible = "nxp,pca9533"; reg = <0x62>; - led1 { + led-1 { label = "red:user1"; linux,default-trigger = "none"; type = ; }; - led2 { + led-2 { label = "green:user2"; linux,default-trigger = "none"; type = ; }; - led3 { + led-3 { label = "blue:user3"; linux,default-trigger = "none"; type = ; }; - led4 { + led-4 { label = "red:user4"; linux,default-trigger = "none"; type = ; @@ -199,7 +203,7 @@ touchscreen { ts_irq_pin: ts-irq-pin { - rockchip,pins = <5 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>; + rockchip,pins = <5 RK_PB4 RK_FUNC_GPIO &pcfg_pull_up>; }; }; diff --git a/arch/arm/boot/dts/rockchip/rk3288-phycore-som.dtsi b/arch/arm/boot/dts/rockchip/rk3288-phycore-som.dtsi index 12ab10c4adde..0816e388852f 100644 --- a/arch/arm/boot/dts/rockchip/rk3288-phycore-som.dtsi +++ b/arch/arm/boot/dts/rockchip/rk3288-phycore-som.dtsi @@ -100,7 +100,7 @@ tx_delay = <0x0>; rx_delay = <0x0>; - mdio0 { + mdio { compatible = "snps,dwmac-mdio"; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm/boot/dts/rockchip/rk3288-veyron-brain.dts b/arch/arm/boot/dts/rockchip/rk3288-veyron-brain.dts index ade9cc291813..d7790eebfdcd 100644 --- a/arch/arm/boot/dts/rockchip/rk3288-veyron-brain.dts +++ b/arch/arm/boot/dts/rockchip/rk3288-veyron-brain.dts @@ -91,14 +91,12 @@ regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-name = "vdd10_lcd"; - regulator-suspend-mem-disabled; }; vcc18_hdmi: SWITCH_REG2 { regulator-always-on; regulator-boot-on; regulator-name = "vcc18_hdmi"; - regulator-suspend-mem-disabled; }; }; }; diff --git a/arch/arm/boot/dts/rockchip/rk3288-veyron-fievel.dts b/arch/arm/boot/dts/rockchip/rk3288-veyron-fievel.dts index 6a0844e16279..3da105060302 100644 --- a/arch/arm/boot/dts/rockchip/rk3288-veyron-fievel.dts +++ b/arch/arm/boot/dts/rockchip/rk3288-veyron-fievel.dts @@ -98,9 +98,8 @@ snps,reset-gpio = <&gpio4 RK_PB0 0>; snps,reset-active-low; snps,reset-delays-us = <0 10000 30000>; - wakeup-source; - mdio0 { + mdio { compatible = "snps,dwmac-mdio"; #address-cells = <1>; #size-cells = <0>; @@ -177,7 +176,7 @@ #address-cells = <1>; #size-cells = <0>; - btmrvl: btmrvl@2 { + btmrvl: bluetooth@2 { compatible = "marvell,sd8897-bt"; reg = <2>; interrupt-parent = <&gpio4>; diff --git a/arch/arm/boot/dts/rockchip/rk3288-veyron-jaq.dts b/arch/arm/boot/dts/rockchip/rk3288-veyron-jaq.dts index 0d4c50e05558..cba2898f8b7d 100644 --- a/arch/arm/boot/dts/rockchip/rk3288-veyron-jaq.dts +++ b/arch/arm/boot/dts/rockchip/rk3288-veyron-jaq.dts @@ -48,7 +48,7 @@ #address-cells = <1>; #size-cells = <0>; - btmrvl: btmrvl@2 { + btmrvl: bluetooth@2 { compatible = "marvell,sd8897-bt"; reg = <2>; interrupt-parent = <&gpio4>; diff --git a/arch/arm/boot/dts/rockchip/rk3288-veyron-jerry.dts b/arch/arm/boot/dts/rockchip/rk3288-veyron-jerry.dts index 6894763979f0..0bf03b1ff2ab 100644 --- a/arch/arm/boot/dts/rockchip/rk3288-veyron-jerry.dts +++ b/arch/arm/boot/dts/rockchip/rk3288-veyron-jerry.dts @@ -488,7 +488,7 @@ interrupts = ; reg = <0x2c>; hid-descr-addr = <0x0020>; - vcc-supply = <&vcc33_io>; + vdd-supply = <&vcc33_io>; wakeup-source; }; }; diff --git a/arch/arm/boot/dts/rockchip/rk3288-veyron-mickey.dts b/arch/arm/boot/dts/rockchip/rk3288-veyron-mickey.dts index d665c3e8862c..20fe84683928 100644 --- a/arch/arm/boot/dts/rockchip/rk3288-veyron-mickey.dts +++ b/arch/arm/boot/dts/rockchip/rk3288-veyron-mickey.dts @@ -246,7 +246,6 @@ regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-name = "vdd10_lcd"; - regulator-suspend-mem-disabled; }; vcc18_lcd: LDO_REG8 { @@ -255,7 +254,6 @@ regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-name = "vcc18_lcd"; - regulator-suspend-mem-disabled; }; }; }; diff --git a/arch/arm/boot/dts/rockchip/rk3288-veyron-pinky.dts b/arch/arm/boot/dts/rockchip/rk3288-veyron-pinky.dts index cc27d116d025..e241f93b2310 100644 --- a/arch/arm/boot/dts/rockchip/rk3288-veyron-pinky.dts +++ b/arch/arm/boot/dts/rockchip/rk3288-veyron-pinky.dts @@ -47,6 +47,7 @@ key-power { gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_HIGH>; + linux,code = ; }; }; diff --git a/arch/arm/boot/dts/rockchip/rk3288.dtsi b/arch/arm/boot/dts/rockchip/rk3288.dtsi index 4e5e7509de48..4f2c048aee54 100644 --- a/arch/arm/boot/dts/rockchip/rk3288.dtsi +++ b/arch/arm/boot/dts/rockchip/rk3288.dtsi @@ -551,7 +551,6 @@ pinctrl-1 = <&otp_out>; pinctrl-2 = <&otp_pin>; #thermal-sensor-cells = <1>; - rockchip,grf = <&grf>; rockchip,hw-tshut-temp = <95000>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/rockchip/rv1103b-omega4-evb.dts b/arch/arm/boot/dts/rockchip/rv1103b-omega4-evb.dts new file mode 100644 index 000000000000..c6472f933aa5 --- /dev/null +++ b/arch/arm/boot/dts/rockchip/rv1103b-omega4-evb.dts @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + * Copyright (c) 2025 plan44.ch/luz + * Copyright (c) 2026 Onion Corporation + */ + +/dts-v1/; + +#include +#include +#include "rv1103b-omega4.dtsi" + +/ { + model = "Onion Omega4 Evaluation Board"; + compatible = "onion,omega4-evb", "onion,omega4", "rockchip,rv1103b"; + + aliases { + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + leds { + compatible = "gpio-leds"; + + led-0 { + color = ; + default-state = "on"; + function = LED_FUNCTION_STATUS; + gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>; + label = "sys"; + pinctrl-names = "default"; + pinctrl-0 = <&led>; + }; + }; +}; + +&fspi0 { + status = "okay"; +}; + +&pinctrl { + leds { + led: led { + rockchip,pins = <0 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&sdmmc0 { + status = "okay"; +}; + +&sdmmc1 { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/rockchip/rv1103b-omega4.dtsi b/arch/arm/boot/dts/rockchip/rv1103b-omega4.dtsi new file mode 100644 index 000000000000..6a8e8e0f80c5 --- /dev/null +++ b/arch/arm/boot/dts/rockchip/rv1103b-omega4.dtsi @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + * Copyright (c) 2025 plan44.ch/luz + * Copyright (c) 2026 Onion Corporation + */ + +/dts-v1/; + +#include "rv1103b.dtsi" + +/ { + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_enable_h>; + post-power-on-delay-ms = <300>; + reset-gpios = <&gpio2 RK_PB2 GPIO_ACTIVE_LOW>; + }; + + vcc3v3_sd: vcc3v3-sd { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PA4 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc_pwren>; + regulator-name = "vcc3v3_sd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + vccio_sd: vccio-sd { + compatible = "regulator-gpio"; + gpios = <&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc_volt>; + regulator-name = "vccio_sd"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + states = <3300000 1 1800000 0>; + }; +}; + +&uart0 { + bootph-all; + pinctrl-names = "default"; + pinctrl-0 = <&uart0m0_xfer>; +}; + +&fspi0 { + spi_nand: flash@0 { + compatible = "spi-nand"; + reg = <0>; + bootph-pre-ram; + bootph-some-ram; + spi-max-frequency = <75000000>; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <1>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + reg = <0x00000000 0x00040000>; + label = "env"; + }; + + partition@40000 { + reg = <0x00040000 0x00100000>; + label = "idblock"; + read-only; + }; + + partition@140000 { + reg = <0x00140000 0x00100000>; + label = "uboot"; + read-only; + }; + + partition@240000 { + reg = <0x00240000 0x00800000>; + label = "boot"; + }; + + partition@a40000 { + reg = <0x00a40000 0x0f5c0000>; + label = "ubi"; + }; + }; + }; +}; + +&sdmmc0 { + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + no-sdio; + no-mmc; + sd-uhs-sdr104; + vmmc-supply = <&vcc3v3_sd>; + vqmmc-supply = <&vccio_sd>; + status = "disabled"; +}; + +&sdmmc1 { + bus-width = <4>; + cap-sd-highspeed; + cap-sdio-irq; + keep-power-in-suspend; + mmc-pwrseq = <&sdio_pwrseq>; + no-sd; + no-mmc; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc1_cmd &sdmmc1_clk &sdmmc1_bus4>; + status = "disabled"; +}; + +&pinctrl { + sdio-pwrseq { + /omit-if-no-ref/ + wifi_enable_h: wifi-enable-h { + rockchip,pins = <2 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + sdmmc { + /omit-if-no-ref/ + sdmmc_pwren: sdmmc-pwren { + rockchip,pins = <0 RK_PA4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + sdmmc_volt: sdmmc-volt { + rockchip,pins = <0 RK_PA3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + wireless-wlan { + /omit-if-no-ref/ + wifi_host_wake_irq: wifi-host-wake-irq { + rockchip,pins = <2 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; diff --git a/arch/arm/boot/dts/rockchip/rv1103b-pinctrl.dtsi b/arch/arm/boot/dts/rockchip/rv1103b-pinctrl.dtsi new file mode 100644 index 000000000000..15516c384139 --- /dev/null +++ b/arch/arm/boot/dts/rockchip/rv1103b-pinctrl.dtsi @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2026 Rockchip Electronics Co., Ltd. + */ + +#include +#include + +&pinctrl { + cam-clk0 { + /omit-if-no-ref/ + cam_clk0: cam-clk0 { + rockchip,pins = + /* cam_clk0_out */ + <1 RK_PB5 1 &pcfg_pull_none>; + }; + }; + + cam-clk1 { + /omit-if-no-ref/ + cam_clk1: cam-clk1 { + rockchip,pins = + /* cam_clk1_out */ + <1 RK_PB6 1 &pcfg_pull_none>; + }; + }; + + cam-spi { + /omit-if-no-ref/ + cam_spi_bus4: cam-spi-bus4 { + rockchip,pins = + /* cam_spi_d0 */ + <0 RK_PB5 4 &pcfg_pull_up_drv_level_2>, + /* cam_spi_d1 */ + <0 RK_PB2 4 &pcfg_pull_up_drv_level_2>, + /* cam_spi_d2 */ + <0 RK_PB1 4 &pcfg_pull_up_drv_level_2>, + /* cam_spi_d3 */ + <0 RK_PB0 4 &pcfg_pull_up_drv_level_2>; + }; + + /omit-if-no-ref/ + cam_spi_clk: cam-spi-clk { + rockchip,pins = + /* cam_spi_clk */ + <0 RK_PB4 4 &pcfg_pull_none>; + }; + /omit-if-no-ref/ + cam_spi_cs0n: cam-spi-cs0n { + rockchip,pins = + /* cam_spi_cs0n */ + <0 RK_PB3 4 &pcfg_pull_none>; + }; + }; + + clk { + /omit-if-no-ref/ + clk_32k: clk-32k { + rockchip,pins = + /* clk_32k */ + <0 RK_PA0 2 &pcfg_pull_none>; + }; + }; + + clk-24m { + /omit-if-no-ref/ + clk_24m_out: clk-24m-out { + rockchip,pins = + /* clk_24m_out */ + <0 RK_PA0 3 &pcfg_pull_none>; + }; + }; + + cpu { + /omit-if-no-ref/ + cpu: cpu { + rockchip,pins = + /* cpu_avs */ + <0 RK_PA1 2 &pcfg_pull_none>; + }; + }; + + emmc { + /omit-if-no-ref/ + emmc_bus4: emmc-bus4 { + rockchip,pins = + /* emmc_d0 */ + <1 RK_PA1 1 &pcfg_pull_up_drv_level_2>, + /* emmc_d1 */ + <1 RK_PA2 1 &pcfg_pull_up_drv_level_2>, + /* emmc_d2 */ + <1 RK_PA3 1 &pcfg_pull_up_drv_level_2>, + /* emmc_d3 */ + <1 RK_PA0 1 &pcfg_pull_up_drv_level_2>; + }; + + /omit-if-no-ref/ + emmc_clk: emmc-clk { + rockchip,pins = + /* emmc_clk */ + <1 RK_PA4 1 &pcfg_pull_up_drv_level_2>; + }; + + /omit-if-no-ref/ + emmc_cmd: emmc-cmd { + rockchip,pins = + /* emmc_cmd */ + <1 RK_PA5 1 &pcfg_pull_up_drv_level_2>; + }; + }; + + fspi { + /omit-if-no-ref/ + fspi_bus4: fspi-bus4 { + rockchip,pins = + /* fspi_d0 */ + <1 RK_PA1 2 &pcfg_pull_none>, + /* fspi_d1 */ + <1 RK_PA2 2 &pcfg_pull_none>, + /* fspi_d2 */ + <1 RK_PA3 2 &pcfg_pull_none>, + /* fspi_d3 */ + <1 RK_PA0 2 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + fspi_cs0: fspi-cs0 { + rockchip,pins = + /* fspi_cs0n */ + <1 RK_PA5 2 &pcfg_pull_up>; + }; + + /omit-if-no-ref/ + fspi_clk: fspi-clk { + rockchip,pins = + /* fspi_clk */ + <1 RK_PA4 2 &pcfg_pull_none>; + }; + }; + + i2c0 { + /omit-if-no-ref/ + i2c0m0_xfer: i2c0m0-xfer { + rockchip,pins = + /* i2c0_scl_m0 */ + <0 RK_PA5 3 &pcfg_pull_none_smt>, + /* i2c0_sda_m0 */ + <0 RK_PA6 3 &pcfg_pull_none_smt>; + }; + + /omit-if-no-ref/ + i2c0m1_xfer: i2c0m1-xfer { + rockchip,pins = + /* i2c0_scl_m1 */ + <1 RK_PB4 5 &pcfg_pull_none_smt>, + /* i2c0_sda_m1 */ + <1 RK_PB3 5 &pcfg_pull_none_smt>; + }; + + /omit-if-no-ref/ + i2c0m2_xfer: i2c0m2-xfer { + rockchip,pins = + /* i2c0_scl_m2 */ + <1 RK_PB5 2 &pcfg_pull_none_smt>, + /* i2c0_sda_m2 */ + <1 RK_PB6 2 &pcfg_pull_none_smt>; + }; + }; + + i2c1 { + /omit-if-no-ref/ + i2c1m0_xfer: i2c1m0-xfer { + rockchip,pins = + /* i2c1_scl_m0 */ + <0 RK_PB0 1 &pcfg_pull_none_smt>, + /* i2c1_sda_m0 */ + <0 RK_PB1 1 &pcfg_pull_none_smt>; + }; + + /omit-if-no-ref/ + i2c1m1_xfer: i2c1m1-xfer { + rockchip,pins = + /* i2c1_scl_m1 */ + <2 RK_PA4 4 &pcfg_pull_none_smt>, + /* i2c1_sda_m1 */ + <2 RK_PA5 4 &pcfg_pull_none_smt>; + }; + }; + + i2c2 { + /omit-if-no-ref/ + i2c2m0_xfer: i2c2m0-xfer { + rockchip,pins = + /* i2c2_scl_m0 */ + <0 RK_PB2 1 &pcfg_pull_none_smt>, + /* i2c2_sda_m0 */ + <0 RK_PB3 1 &pcfg_pull_none_smt>; + }; + + /omit-if-no-ref/ + i2c2m1_xfer: i2c2m1-xfer { + rockchip,pins = + /* i2c2_scl_m1 */ + <2 RK_PA6 4 &pcfg_pull_none_smt>, + /* i2c2_sda_m1 */ + <2 RK_PA7 4 &pcfg_pull_none_smt>; + }; + }; + + i2c3 { + /omit-if-no-ref/ + i2c3m0_xfer: i2c3m0-xfer { + rockchip,pins = + /* i2c3_scl_m0 */ + <0 RK_PB4 1 &pcfg_pull_none_smt>, + /* i2c3_sda_m0 */ + <0 RK_PB5 1 &pcfg_pull_none_smt>; + }; + + /omit-if-no-ref/ + i2c3m1_xfer: i2c3m1-xfer { + rockchip,pins = + /* i2c3_scl_m1 */ + <2 RK_PB3 4 &pcfg_pull_none_smt>, + /* i2c3_sda_m1 */ + <2 RK_PB2 4 &pcfg_pull_none_smt>; + }; + }; + + i2c4 { + /omit-if-no-ref/ + i2c4m0_xfer: i2c4m0-xfer { + rockchip,pins = + /* i2c4_scl_m0 */ + <2 RK_PB0 4 &pcfg_pull_none_smt>, + /* i2c4_sda_m0 */ + <2 RK_PB1 4 &pcfg_pull_none_smt>; + }; + + /omit-if-no-ref/ + i2c4m1_xfer: i2c4m1-xfer { + rockchip,pins = + /* i2c4_scl_m1 */ + <1 RK_PB7 2 &pcfg_pull_none_smt>, + /* i2c4_sda_m1 */ + <1 RK_PC0 2 &pcfg_pull_none_smt>; + }; + }; + + jtag { + /omit-if-no-ref/ + jtagm0: jtagm0 { + rockchip,pins = + /* jtag_tck_m0 */ + <0 RK_PA5 5 &pcfg_pull_none>, + /* jtag_tms_m0 */ + <0 RK_PA6 5 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + jtagm1: jtagm1 { + rockchip,pins = + /* jtag_tck_m1 */ + <0 RK_PB4 3 &pcfg_pull_none>, + /* jtag_tms_m1 */ + <0 RK_PB5 3 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + jtagm2: jtagm2 { + rockchip,pins = + /* jtag_tck_m2 */ + <1 RK_PB4 3 &pcfg_pull_none>, + /* jtag_tms_m2 */ + <1 RK_PB3 3 &pcfg_pull_none>; + }; + }; + + psram-spi { + /omit-if-no-ref/ + psram_spi_bus4: psram-spi-bus4 { + rockchip,pins = + /* psram_spi_d0 */ + <0 RK_PA2 4 &pcfg_pull_none>, + /* psram_spi_d1 */ + <0 RK_PA1 4 &pcfg_pull_none>, + /* psram_spi_d2 */ + <0 RK_PA5 4 &pcfg_pull_none>, + /* psram_spi_d3 */ + <0 RK_PA6 4 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + psram_spi_clk: psram-spi-clk { + rockchip,pins = + /* psram_spi_clk */ + <0 RK_PA0 4 &pcfg_pull_none>; + }; + /omit-if-no-ref/ + psram_spi_cs0n: psram-spi-cs0n { + rockchip,pins = + /* psram_spi_cs0n */ + <0 RK_PA4 4 &pcfg_pull_none>; + }; + }; + + pwm0 { + /omit-if-no-ref/ + pwm0m0_ch0: pwm0m0-ch0 { + rockchip,pins = + /* pwm0m0_ch0 */ + <0 RK_PA1 1 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm0m0_ch1: pwm0m0-ch1 { + rockchip,pins = + /* pwm0m0_ch1 */ + <0 RK_PA5 2 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm0m0_ch2: pwm0m0-ch2 { + rockchip,pins = + /* pwm0m0_ch2 */ + <0 RK_PA6 2 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm0m0_ch3: pwm0m0-ch3 { + rockchip,pins = + /* pwm0m0_ch3 */ + <0 RK_PA2 1 &pcfg_pull_none_drv_level_0>; + }; + + /omit-if-no-ref/ + pwm0m1_ch0: pwm0m1-ch0 { + rockchip,pins = + /* pwm0m1_ch0 */ + <2 RK_PA0 3 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm0m1_ch1: pwm0m1-ch1 { + rockchip,pins = + /* pwm0m1_ch1 */ + <2 RK_PA1 3 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm0m1_ch2: pwm0m1-ch2 { + rockchip,pins = + /* pwm0m1_ch2 */ + <2 RK_PA2 3 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm0m1_ch3: pwm0m1-ch3 { + rockchip,pins = + /* pwm0m1_ch3 */ + <2 RK_PB0 3 &pcfg_pull_none_drv_level_0>; + }; + + /omit-if-no-ref/ + pwm0m2_ch1: pwm0m2-ch1 { + rockchip,pins = + /* pwm0m2_ch1 */ + <1 RK_PB7 1 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm0m2_ch2: pwm0m2-ch2 { + rockchip,pins = + /* pwm0m2_ch2 */ + <1 RK_PC0 1 &pcfg_pull_none_drv_level_0>; + }; + }; + + pwm1 { + /omit-if-no-ref/ + pwm1m0_ch0: pwm1m0-ch0 { + rockchip,pins = + /* pwm1m0_ch0 */ + <0 RK_PB0 3 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm1m0_ch1: pwm1m0-ch1 { + rockchip,pins = + /* pwm1m0_ch1 */ + <0 RK_PB1 3 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm1m0_ch2: pwm1m0-ch2 { + rockchip,pins = + /* pwm1m0_ch2 */ + <0 RK_PB2 3 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm1m0_ch3: pwm1m0-ch3 { + rockchip,pins = + /* pwm1m0_ch3 */ + <0 RK_PB3 3 &pcfg_pull_none_drv_level_0>; + }; + + /omit-if-no-ref/ + pwm1m1_ch0: pwm1m1-ch0 { + rockchip,pins = + /* pwm1m1_ch0 */ + <2 RK_PA3 3 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm1m1_ch1: pwm1m1-ch1 { + rockchip,pins = + /* pwm1m1_ch1 */ + <2 RK_PA4 3 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm1m1_ch2: pwm1m1-ch2 { + rockchip,pins = + /* pwm1m1_ch2 */ + <2 RK_PA5 3 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm1m1_ch3: pwm1m1-ch3 { + rockchip,pins = + /* pwm1m1_ch3 */ + <2 RK_PB1 3 &pcfg_pull_none_drv_level_0>; + }; + }; + + pwm2 { + /omit-if-no-ref/ + pwm2m0_ch0: pwm2m0-ch0 { + rockchip,pins = + /* pwm2m0_ch0 */ + <1 RK_PB0 4 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm2m0_ch1: pwm2m0-ch1 { + rockchip,pins = + /* pwm2m0_ch1 */ + <1 RK_PA7 4 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm2m0_ch2: pwm2m0-ch2 { + rockchip,pins = + /* pwm2m0_ch2 */ + <1 RK_PB4 4 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm2m0_ch3: pwm2m0-ch3 { + rockchip,pins = + /* pwm2m0_ch3 */ + <1 RK_PB3 4 &pcfg_pull_none_drv_level_0>; + }; + + /omit-if-no-ref/ + pwm2m1_ch0: pwm2m1-ch0 { + rockchip,pins = + /* pwm2m1_ch0 */ + <2 RK_PA6 3 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm2m1_ch1: pwm2m1-ch1 { + rockchip,pins = + /* pwm2m1_ch1 */ + <2 RK_PA7 3 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm2m1_ch2: pwm2m1-ch2 { + rockchip,pins = + /* pwm2m1_ch2 */ + <2 RK_PB2 3 &pcfg_pull_none_drv_level_0>; + }; + /omit-if-no-ref/ + pwm2m1_ch3: pwm2m1-ch3 { + rockchip,pins = + /* pwm2m1_ch3 */ + <2 RK_PB3 3 &pcfg_pull_none_drv_level_0>; + }; + }; + + pwr { + /omit-if-no-ref/ + pwr: pwr { + rockchip,pins = + /* pwr_ctrl0 */ + <0 RK_PA3 1 &pcfg_pull_none>, + /* pwr_ctrl1 */ + <0 RK_PA4 1 &pcfg_pull_none>; + }; + }; + + rtc_32k { + /omit-if-no-ref/ + rtc_32k: rtc-32k { + rockchip,pins = + /* rtc_32k_out */ + <0 RK_PA0 1 &pcfg_pull_none>; + }; + }; + + sai { + /omit-if-no-ref/ + sai: sai { + rockchip,pins = + /* sai_lrck */ + <2 RK_PB1 5 &pcfg_pull_none>, + /* sai_mclk */ + <2 RK_PB0 5 &pcfg_pull_none>, + /* sai_sclk */ + <2 RK_PA7 5 &pcfg_pull_none>, + /* sai_sdi */ + <2 RK_PA6 5 &pcfg_pull_none>, + /* sai_sdo */ + <2 RK_PB2 5 &pcfg_pull_none>; + }; + }; + + sdmmc0 { + /omit-if-no-ref/ + sdmmc0_bus4: sdmmc0-bus4 { + rockchip,pins = + /* sdmmc0_d0 */ + <1 RK_PB0 1 &pcfg_pull_up_drv_level_2>, + /* sdmmc0_d1 */ + <1 RK_PA7 1 &pcfg_pull_up_drv_level_2>, + /* sdmmc0_d2 */ + <1 RK_PB4 1 &pcfg_pull_up_drv_level_2>, + /* sdmmc0_d3 */ + <1 RK_PB3 1 &pcfg_pull_up_drv_level_2>; + }; + + /omit-if-no-ref/ + sdmmc0_clk: sdmmc0-clk { + rockchip,pins = + /* sdmmc0_clk */ + <1 RK_PB1 1 &pcfg_pull_up_drv_level_2>; + }; + + /omit-if-no-ref/ + sdmmc0_cmd: sdmmc0-cmd { + rockchip,pins = + /* sdmmc0_cmd */ + <1 RK_PB2 1 &pcfg_pull_up_drv_level_2>; + }; + + /omit-if-no-ref/ + sdmmc0_det: sdmmc0-det { + rockchip,pins = + /* sdmmc0_det */ + <1 RK_PA6 1 &pcfg_pull_up>; + }; + }; + + sdmmc1 { + /omit-if-no-ref/ + sdmmc1_bus4: sdmmc1-bus4 { + rockchip,pins = + /* sdmmc1_d0 */ + <2 RK_PA1 1 &pcfg_pull_up_drv_level_2>, + /* sdmmc1_d1 */ + <2 RK_PA0 1 &pcfg_pull_up_drv_level_2>, + /* sdmmc1_d2 */ + <2 RK_PA5 1 &pcfg_pull_up_drv_level_2>, + /* sdmmc1_d3 */ + <2 RK_PA4 1 &pcfg_pull_up_drv_level_2>; + }; + + /omit-if-no-ref/ + sdmmc1_clk: sdmmc1-clk { + rockchip,pins = + /* sdmmc1_clk */ + <2 RK_PA2 1 &pcfg_pull_up_drv_level_2>; + }; + + /omit-if-no-ref/ + sdmmc1_cmd: sdmmc1-cmd { + rockchip,pins = + /* sdmmc1_cmd */ + <2 RK_PA3 1 &pcfg_pull_up_drv_level_2>; + }; + }; + + spi0 { + /omit-if-no-ref/ + spi0m0_clk: spi0m0-clk { + rockchip,pins = + /* spi0_clk_m0 */ + <2 RK_PB0 2 &pcfg_pull_none>, + /* spi0_miso_m0 */ + <2 RK_PB3 2 &pcfg_pull_none>, + /* spi0_mosi_m0 */ + <2 RK_PB1 2 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + spi0m0_cs0: spi0m0-cs0 { + rockchip,pins = + /* spi0_cs0n_m0 */ + <2 RK_PB2 2 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + spi0m0_cs1: spi0m0-cs1 { + rockchip,pins = + /* spi0_cs1n_m0 */ + <2 RK_PA7 2 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + spi0m1_clk: spi0m1-clk { + rockchip,pins = + /* spi0_clk_m1 */ + <2 RK_PA2 5 &pcfg_pull_none>, + /* spi0_miso_m1 */ + <2 RK_PA4 5 &pcfg_pull_none>, + /* spi0_mosi_m1 */ + <2 RK_PA1 5 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + spi0m1_cs0: spi0m1-cs0 { + rockchip,pins = + /* spi0_cs0n_m1 */ + <2 RK_PA3 5 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + spi0m1_cs1: spi0m1-cs1 { + rockchip,pins = + /* spi0_cs1n_m1 */ + <2 RK_PA0 5 &pcfg_pull_none>; + }; + }; + + uart0 { + /omit-if-no-ref/ + uart0m0_xfer: uart0m0-xfer { + rockchip,pins = + /* uart0_rx_m0 */ + <0 RK_PA6 1 &pcfg_pull_up>, + /* uart0_tx_m0 */ + <0 RK_PA5 1 &pcfg_pull_up>; + }; + + /omit-if-no-ref/ + uart0m1_xfer: uart0m1-xfer { + rockchip,pins = + /* uart0_rx_m1 */ + <0 RK_PB5 2 &pcfg_pull_up>, + /* uart0_tx_m1 */ + <0 RK_PB4 2 &pcfg_pull_up>; + }; + + /omit-if-no-ref/ + uart0m2_xfer: uart0m2-xfer { + rockchip,pins = + /* uart0_rx_m2 */ + <1 RK_PB3 2 &pcfg_pull_up>, + /* uart0_tx_m2 */ + <1 RK_PB4 2 &pcfg_pull_up>; + }; + }; + + uart1 { + /omit-if-no-ref/ + uart1m0_xfer: uart1m0-xfer { + rockchip,pins = + /* uart1_rx_m0 */ + <0 RK_PB2 2 &pcfg_pull_up>, + /* uart1_tx_m0 */ + <0 RK_PB3 2 &pcfg_pull_up>; + }; + + /omit-if-no-ref/ + uart1m0_ctsn: uart1m0-ctsn { + rockchip,pins = + /* uart1m0_ctsn */ + <0 RK_PB5 5 &pcfg_pull_none>; + }; + /omit-if-no-ref/ + uart1m0_rtsn: uart1m0-rtsn { + rockchip,pins = + /* uart1m0_rtsn */ + <0 RK_PB4 5 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + uart1m1_xfer: uart1m1-xfer { + rockchip,pins = + /* uart1_rx_m1 */ + <1 RK_PA7 2 &pcfg_pull_up>, + /* uart1_tx_m1 */ + <1 RK_PB0 2 &pcfg_pull_up>; + }; + + /omit-if-no-ref/ + uart1m1_ctsn: uart1m1-ctsn { + rockchip,pins = + /* uart1m1_ctsn */ + <1 RK_PB2 2 &pcfg_pull_none>; + }; + /omit-if-no-ref/ + uart1m1_rtsn: uart1m1-rtsn { + rockchip,pins = + /* uart1m1_rtsn */ + <1 RK_PB1 2 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + uart1m2_xfer: uart1m2-xfer { + rockchip,pins = + /* uart1_rx_m2 */ + <2 RK_PA7 1 &pcfg_pull_up>, + /* uart1_tx_m2 */ + <2 RK_PA6 1 &pcfg_pull_up>; + }; + + /omit-if-no-ref/ + uart1m2_ctsn: uart1m2-ctsn { + rockchip,pins = + /* uart1m2_ctsn */ + <2 RK_PA5 2 &pcfg_pull_none>; + }; + /omit-if-no-ref/ + uart1m2_rtsn: uart1m2-rtsn { + rockchip,pins = + /* uart1m2_rtsn */ + <2 RK_PA4 2 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + uart1m3_xfer: uart1m3-xfer { + rockchip,pins = + /* uart1_rx_m3 */ + <2 RK_PA3 2 &pcfg_pull_up>, + /* uart1_tx_m3 */ + <2 RK_PA2 2 &pcfg_pull_up>; + }; + + /omit-if-no-ref/ + uart1m3_ctsn: uart1m3-ctsn { + rockchip,pins = + /* uart1m3_ctsn */ + <2 RK_PA1 2 &pcfg_pull_none>; + }; + /omit-if-no-ref/ + uart1m3_rtsn: uart1m3-rtsn { + rockchip,pins = + /* uart1m3_rtsn */ + <2 RK_PA0 2 &pcfg_pull_none>; + }; + }; + + uart2 { + /omit-if-no-ref/ + uart2m0_xfer: uart2m0-xfer { + rockchip,pins = + /* uart2_rx_m0 */ + <0 RK_PB1 2 &pcfg_pull_up>, + /* uart2_tx_m0 */ + <0 RK_PB0 2 &pcfg_pull_up>; + }; + + /omit-if-no-ref/ + uart2m0_ctsn: uart2m0-ctsn { + rockchip,pins = + /* uart2m0_ctsn */ + <0 RK_PB3 5 &pcfg_pull_none>; + }; + /omit-if-no-ref/ + uart2m0_rtsn: uart2m0-rtsn { + rockchip,pins = + /* uart2m0_rtsn */ + <0 RK_PB2 5 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + uart2m1_xfer: uart2m1-xfer { + rockchip,pins = + /* uart2_rx_m1 */ + <2 RK_PB1 1 &pcfg_pull_up>, + /* uart2_tx_m1 */ + <2 RK_PB0 1 &pcfg_pull_up>; + }; + + /omit-if-no-ref/ + uart2m1_ctsn: uart2m1-ctsn { + rockchip,pins = + /* uart2m1_ctsn */ + <2 RK_PB3 1 &pcfg_pull_none>; + }; + /omit-if-no-ref/ + uart2m1_rtsn: uart2m1-rtsn { + rockchip,pins = + /* uart2m1_rtsn */ + <2 RK_PB2 1 &pcfg_pull_none>; + }; + + /omit-if-no-ref/ + uart2m2_xfer: uart2m2-xfer { + rockchip,pins = + /* uart2_rx_m2 */ + <1 RK_PB6 3 &pcfg_pull_up>, + /* uart2_tx_m2 */ + <1 RK_PB5 3 &pcfg_pull_up>; + }; + + /omit-if-no-ref/ + uart2m2_ctsn: uart2m2-ctsn { + rockchip,pins = + /* uart2m2_ctsn */ + <1 RK_PC0 3 &pcfg_pull_none>; + }; + /omit-if-no-ref/ + uart2m2_rtsn: uart2m2-rtsn { + rockchip,pins = + /* uart2m2_rtsn */ + <1 RK_PB7 3 &pcfg_pull_none>; + }; + }; +}; diff --git a/arch/arm/boot/dts/rockchip/rv1103b.dtsi b/arch/arm/boot/dts/rockchip/rv1103b.dtsi new file mode 100644 index 000000000000..39f78e0733c9 --- /dev/null +++ b/arch/arm/boot/dts/rockchip/rv1103b.dtsi @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2026 Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "rockchip,rv1103b"; + + interrupt-parent = <&gic>; + + arm-pmu { + compatible = "arm,cortex-a7-pmu"; + interrupts = ; + interrupt-affinity = <&cpu0>; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "arm,cortex-a7"; + reg = <0x0>; + clocks = <&cru ARMCLK>; + device_type = "cpu"; + }; + }; + + timer { + compatible = "arm,armv7-timer"; + clock-frequency = <24000000>; + interrupts = , + ; + }; + + xin24m: oscillator-24m { + compatible = "fixed-clock"; + clock-frequency = <24000000>; + clock-output-names = "xin24m"; + #clock-cells = <0>; + }; + + pinctrl: pinctrl { + compatible = "rockchip,rv1103b-pinctrl"; + rockchip,grf = <&ioc>; + ranges; + #address-cells = <1>; + #size-cells = <1>; + + gpio0: gpio@20520000 { + compatible = "rockchip,gpio-bank"; + reg = <0x20520000 0x200>; + clocks = <&cru PCLK_PMU_GPIO0>, <&cru DBCLK_PMU_GPIO0>; + gpio-controller; + interrupts = ; + interrupt-controller; + #gpio-cells = <2>; + #interrupt-cells = <2>; + }; + + gpio1: gpio@20d80000 { + compatible = "rockchip,gpio-bank"; + reg = <0x20d80000 0x200>; + clocks = <&cru PCLK_GPIO1>, <&cru DBCLK_GPIO1>; + gpio-controller; + interrupts = ; + interrupt-controller; + #gpio-cells = <2>; + #interrupt-cells = <2>; + }; + + gpio2: gpio@20840000 { + compatible = "rockchip,gpio-bank"; + reg = <0x20840000 0x200>; + clocks = <&cru PCLK_GPIO2>, <&cru DBCLK_GPIO2>; + gpio-controller; + interrupts = ; + interrupt-controller; + #gpio-cells = <2>; + #interrupt-cells = <2>; + }; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + cru: clock-controller@20000000 { + compatible = "rockchip,rv1103b-cru"; + reg = <0x20000000 0x81000>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + pmu_grf: syscon@20160000 { + compatible = "rockchip,rv1103b-pmu-grf", "syscon", "simple-mfd"; + reg = <0x20160000 0x1000>; + + reboot_mode: reboot-mode { + compatible = "syscon-reboot-mode"; + offset = <0x200>; + mode-normal = ; + mode-recovery = ; + mode-bootloader = ; + mode-loader = ; + }; + }; + + ioc: syscon@20170000 { + compatible = "rockchip,rv1103b-ioc", "syscon"; + reg = <0x20170000 0x60000>; + }; + + gic: interrupt-controller@20411000 { + compatible = "arm,gic-400"; + reg = <0x20411000 0x1000>, + <0x20412000 0x2000>, + <0x20414000 0x2000>, + <0x20416000 0x2000>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; + #address-cells = <0>; + }; + + uart0: serial@20540000 { + compatible = "rockchip,rv1103b-uart", "snps,dw-apb-uart"; + reg = <0x20540000 0x100>; + clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>; + clock-names = "baudclk", "apb_pclk"; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&uart0m0_xfer>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + sdmmc1: mmc@20650000 { + compatible = "rockchip,rv1103b-dw-mshc", "rockchip,rk3576-dw-mshc"; + reg = <0x20650000 0x4000>; + clocks = <&cru HCLK_SDMMC1>, <&cru CCLK_SDMMC1>; + clock-names = "biu", "ciu"; + fifo-depth = <0x100>; + interrupts = ; + max-frequency = <150000000>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc1_clk &sdmmc1_cmd &sdmmc1_bus4>; + status = "disabled"; + }; + + uart1: serial@20870000 { + compatible = "rockchip,rv1103b-uart", "snps,dw-apb-uart"; + reg = <0x20870000 0x100>; + clocks = <&cru SCLK_UART1>, <&cru PCLK_UART1>; + clock-names = "baudclk", "apb_pclk"; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&uart1m0_xfer>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart2: serial@20880000 { + compatible = "rockchip,rv1103b-uart", "snps,dw-apb-uart"; + reg = <0x20880000 0x100>; + clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>; + clock-names = "baudclk", "apb_pclk"; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&uart2m0_xfer>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + sdmmc0: mmc@20d20000 { + compatible = "rockchip,rv1103b-dw-mshc", "rockchip,rk3576-dw-mshc"; + reg = <0x20d20000 0x4000>; + clocks = <&cru HCLK_SDMMC0>, <&cru CCLK_SDMMC0>; + clock-names = "biu", "ciu"; + fifo-depth = <0x100>; + interrupts = ; + max-frequency = <150000000>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0_det &sdmmc0_clk &sdmmc0_cmd &sdmmc0_bus4>; + status = "disabled"; + }; + + emmc: mmc@20d30000 { + compatible = "rockchip,rv1103b-dw-mshc", "rockchip,rk3576-dw-mshc"; + reg = <0x20d30000 0x4000>; + clocks = <&cru HCLK_EMMC>, <&cru CCLK_EMMC>; + clock-names = "biu", "ciu"; + fifo-depth = <0x100>; + interrupts = ; + max-frequency = <150000000>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus4>; + status = "disabled"; + }; + + fspi0: spi@20d40000 { + compatible = "rockchip,sfc"; + reg = <0x20d40000 0x4000>; + clocks = <&cru SCLK_SFC_2X>, <&cru HCLK_SFC>; + clock-names = "clk_sfc", "hclk_sfc"; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&fspi_bus4 &fspi_cs0 &fspi_clk>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + system_sram: sram@210f6000 { + compatible = "mmio-sram"; + reg = <0x210f6000 0x8000>; + ranges = <0 0x210f6000 0x8000>; + #address-cells = <1>; + #size-cells = <1>; + }; + }; +}; + +#include "rv1103b-pinctrl.dtsi" diff --git a/arch/arm/boot/dts/samsung/Makefile b/arch/arm/boot/dts/samsung/Makefile index 7becf36656b1..e0143ee8c82d 100644 --- a/arch/arm/boot/dts/samsung/Makefile +++ b/arch/arm/boot/dts/samsung/Makefile @@ -26,6 +26,7 @@ dtb-$(CONFIG_ARCH_EXYNOS4) += \ exynos4412-trats2.dtb dtb-$(CONFIG_ARCH_EXYNOS5) += \ exynos5250-arndale.dtb \ + exynos5250-manta.dtb \ exynos5250-smdk5250.dtb \ exynos5250-snow.dtb \ exynos5250-snow-rev5.dtb \ diff --git a/arch/arm/boot/dts/samsung/exynos3250-artik5.dtsi b/arch/arm/boot/dts/samsung/exynos3250-artik5.dtsi index 3fdd922e635c..059c0f44e164 100644 --- a/arch/arm/boot/dts/samsung/exynos3250-artik5.dtsi +++ b/arch/arm/boot/dts/samsung/exynos3250-artik5.dtsi @@ -74,8 +74,6 @@ }; &i2c_0 { - #address-cells = <1>; - #size-cells = <0>; samsung,i2c-sda-delay = <100>; samsung,i2c-slave-addr = <0x10>; samsung,i2c-max-bus-freq = <100000>; diff --git a/arch/arm/boot/dts/samsung/exynos3250-monk.dts b/arch/arm/boot/dts/samsung/exynos3250-monk.dts index 68236c7297d7..d59ed46614bd 100644 --- a/arch/arm/boot/dts/samsung/exynos3250-monk.dts +++ b/arch/arm/boot/dts/samsung/exynos3250-monk.dts @@ -191,8 +191,6 @@ }; &i2c_0 { - #address-cells = <1>; - #size-cells = <0>; samsung,i2c-sda-delay = <100>; samsung,i2c-slave-addr = <0x10>; samsung,i2c-max-bus-freq = <100000>; @@ -414,8 +412,6 @@ }; &i2c_1 { - #address-cells = <1>; - #size-cells = <0>; samsung,i2c-sda-delay = <100>; samsung,i2c-slave-addr = <0x10>; samsung,i2c-max-bus-freq = <400000>; diff --git a/arch/arm/boot/dts/samsung/exynos3250-rinato.dts b/arch/arm/boot/dts/samsung/exynos3250-rinato.dts index 36d2171c1ce8..fa983b732898 100644 --- a/arch/arm/boot/dts/samsung/exynos3250-rinato.dts +++ b/arch/arm/boot/dts/samsung/exynos3250-rinato.dts @@ -263,8 +263,6 @@ }; &i2c_0 { - #address-cells = <1>; - #size-cells = <0>; samsung,i2c-sda-delay = <100>; samsung,i2c-slave-addr = <0x10>; samsung,i2c-max-bus-freq = <100000>; @@ -594,8 +592,6 @@ }; &i2c_1 { - #address-cells = <1>; - #size-cells = <0>; samsung,i2c-sda-delay = <100>; samsung,i2c-slave-addr = <0x10>; samsung,i2c-max-bus-freq = <400000>; diff --git a/arch/arm/boot/dts/samsung/exynos4210-smdkv310.dts b/arch/arm/boot/dts/samsung/exynos4210-smdkv310.dts index 18f4f494093b..a6b73a8967c6 100644 --- a/arch/arm/boot/dts/samsung/exynos4210-smdkv310.dts +++ b/arch/arm/boot/dts/samsung/exynos4210-smdkv310.dts @@ -68,8 +68,6 @@ }; &i2c_0 { - #address-cells = <1>; - #size-cells = <0>; samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <100000>; status = "okay"; diff --git a/arch/arm/boot/dts/samsung/exynos4412-itop-scp-core.dtsi b/arch/arm/boot/dts/samsung/exynos4412-itop-scp-core.dtsi index 7bc6968af9c3..223907e5919c 100644 --- a/arch/arm/boot/dts/samsung/exynos4412-itop-scp-core.dtsi +++ b/arch/arm/boot/dts/samsung/exynos4412-itop-scp-core.dtsi @@ -130,8 +130,6 @@ }; &i2c_1 { - #address-cells = <1>; - #size-cells = <0>; samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <400000>; pinctrl-0 = <&i2c1_bus>; diff --git a/arch/arm/boot/dts/samsung/exynos4412-origen.dts b/arch/arm/boot/dts/samsung/exynos4412-origen.dts index 10ab7bc90f50..c5fb551ab352 100644 --- a/arch/arm/boot/dts/samsung/exynos4412-origen.dts +++ b/arch/arm/boot/dts/samsung/exynos4412-origen.dts @@ -113,8 +113,6 @@ }; &i2c_0 { - #address-cells = <1>; - #size-cells = <0>; samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <20000>; pinctrl-0 = <&i2c0_bus>; diff --git a/arch/arm/boot/dts/samsung/exynos5250-manta.dts b/arch/arm/boot/dts/samsung/exynos5250-manta.dts new file mode 100644 index 000000000000..24a27b342227 --- /dev/null +++ b/arch/arm/boot/dts/samsung/exynos5250-manta.dts @@ -0,0 +1,564 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Google Manta (Nexus 10) board device tree source + * + * Copyright (c) 2023-2026 Alexandre Marquet + * Copyright (c) 2025-2026 Lukas Timmermann + */ + +/dts-v1/; +#include "exynos5250.dtsi" +#include "exynos-pinctrl.h" +#include "exynos-mfc-reserved-memory.dtsi" + +#include +#include +#include +#include + +/ { + model = "Google Nexus 10"; + compatible = "google,manta", "samsung,exynos5250", "samsung,exynos5"; + + aliases { + mmc0 = &mmc_0; /* eMMC */ + mmc1 = &mmc_1; /* WiFi */ + }; + + /* Voltage source unknown */ + bmp180_vdda_reg: regulator-bmp180-vdda { + compatible = "regulator-fixed"; + regulator-name = "BMP180_VDDA"; + }; + + /* Voltage source unknown */ + bmp180_vddd_reg: regulator-bmp180-vddd { + compatible = "regulator-fixed"; + regulator-name = "BMP180_VDDD"; + }; + + chosen { + stdout-path = "serial2:115200n8"; + }; + + flash-controller { + compatible = "gpio-leds"; + + led-flash { + function = LED_FUNCTION_FLASH; + color = ; + linux,default-trigger = "flash"; + gpios = <&gpe0 1 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; + + fixed-rate-clocks { + xxti { + compatible = "samsung,clock-xxti"; + clock-frequency = <24000000>; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&gpio_keys>; + pinctrl-names = "default"; + + button-volume-down { + label = "Volume Down"; + gpios = <&gpx2 1 GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <5>; + }; + + button-volume-up { + label = "Volume Up"; + gpios = <&gpx2 0 GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <5>; + }; + + button-power { + label = "Power"; + gpios = <&gpx2 7 GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <5>; + wakeup-source; + }; + + lid-switch { + label = "Hall Effect Sensor"; + gpios = <&gpx1 3 GPIO_ACTIVE_LOW>; + linux,input-type = ; + linux,code = ; + debounce-interval = <10>; + wakeup-source; + }; + }; + + multi-led { + compatible = "leds-group-multicolor"; + color = ; + function = LED_FUNCTION_STATUS; + leds = <&status_red>, <&status_green>, <&status_blue>, <&status_white>; + }; + + pwrseq: mmc1-pwrseq { + compatible = "mmc-pwrseq-simple"; + + reset-gpios = <&gpv1 0 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&wlan_pmena>; + pinctrl-names = "default"; + + clocks = <&max77686 MAX77686_CLK_PMIC>; + clock-names = "ext_clock"; + + post-power-on-delay-ms = <300>; + power-off-delay-us = <50>; + }; + + firmware@204efff { + compatible = "samsung,secure-firmware"; + reg = <0x0204efff 0x1000>; + }; + + memory@40000000 { + device_type = "memory"; + reg = <0x40000000 0x20000000>, + <0x60000000 0x20000000>, + <0x80000000 0x20000000>, + <0xa0000000 0x1ff00000>; + }; +}; + +&clock { + assigned-clocks = <&clock CLK_FOUT_APLL>; + assigned-clock-rates = <1000000000>; +}; + +&cpu0 { + cpu-supply = <&buck2_reg>; +}; + +&cpu1 { + cpu-supply = <&buck2_reg>; +}; + +&ehci { + status = "disabled"; +}; + +&i2c_1 { + status = "okay"; + + pressure-sensor@77 { + compatible = "bosch,bmp180"; + reg = <0x77>; + vddd-supply = <&bmp180_vddd_reg>; + vdda-supply = <&bmp180_vdda_reg>; + }; + + accelerometer@68 { + compatible = "invensense,mpu6050"; + reg = <0x68>; + + pinctrl-0 = <&acc_int>; + pinctrl-names = "default"; + + interrupt-parent = <&gpx1>; + interrupts = <4 IRQ_TYPE_EDGE_RISING>; + + mount-matrix = "0", "-1", "0", + "-1", "0", "0", + "0", "0", "-1"; + + i2c-gate { + #address-cells = <1>; + #size-cells = <0>; + + magnetometer@c { + compatible = "asahi-kasei,ak8963"; + reg = <0x0c>; + + pinctrl-0 = <&msense_reset>; + pinctrl-names = "default"; + + mount-matrix = "-1", "0", "0", + "0", "1", "0", + "0", "0", "-1"; + }; + }; + }; + + led-controller@42 { + compatible = "ams,as3668"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x42>; + + status_red: led@0 { + reg = <0>; + function = LED_FUNCTION_STATUS; + color = ; + }; + + status_green: led@1 { + reg = <1>; + function = LED_FUNCTION_STATUS; + color = ; + }; + + status_blue: led@2 { + reg = <2>; + function = LED_FUNCTION_STATUS; + color = ; + }; + + status_white: led@3 { + reg = <3>; + function = LED_FUNCTION_STATUS; + color = ; + }; + }; +}; + +&i2c_2 { + status = "okay"; + + light-sensor@23 { + compatible = "rohm,bh1721"; + reg = <0x23>; + + pinctrl-0 = <&bh1721fvc_reset>; + pinctrl-names = "default"; + + reset-gpios = <&gph1 2 GPIO_ACTIVE_LOW>; + }; + + onewire@18 { + compatible = "maxim,ds2484"; + reg = <0x18>; + + pinctrl-0 = <&onewire_sleep>; + pinctrl-names = "default"; + }; +}; + +&i2c_5 { + samsung,i2c-sda-delay = <100>; + status = "okay"; + + max77686: pmic@9 { + compatible = "maxim,max77686"; + reg = <0x09>; + + interrupt-parent = <&gpx0>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-0 = <&max77686_irq>; + pinctrl-names = "default"; + + #clock-cells = <1>; + wakeup-source; + + voltage-regulators { + buck1_reg: BUCK1 { + regulator-name = "vdd_mif"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + }; + + buck2_reg: BUCK2 { + regulator-name = "vdd_arm"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + }; + + buck3_reg: BUCK3 { + regulator-name = "vdd_int"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + }; + + buck4_reg: BUCK4 { + regulator-name = "vdd_g3d"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + + ldo3_reg: LDO3 { + regulator-name = "VCC_1.8V_AP"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + ldo8_reg: LDO8 { + regulator-name = "VMIPI_1.0V"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-boot-on; + }; + + ldo9_reg: LDO9 { + regulator-name = "TOUCH_VDD_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo10_reg: LDO10 { + regulator-name = "VMIPI_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + ldo12_reg: LDO12 { + regulator-name = "VUOTG_3.0V"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + regulator-boot-on; + }; + + ldo15_reg: LDO15 { + regulator-name = "VHSIC_1.0V"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-boot-on; + }; + + ldo16_reg: LDO16 { + regulator-name = "VHSIC_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + ldo17_reg: LDO17 { + regulator-name = "5M_CORE_1.5V"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + }; + + ldo18_reg: LDO18 { + regulator-name = "CAM_IO_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo19_reg: LDO19 { + regulator-name = "VT_CAM_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo20_reg: LDO20 { + regulator-name = "TA_CHECK_1.35V"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + }; + + ldo23_reg: LDO23 { + regulator-name = "TSP_AVDD_2.8V"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + ldo24_reg: LDO24 { + regulator-name = "CAM_AF_2.8V"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + ldo25_reg: LDO25 { + regulator-name = "VADC_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; +}; + +&mixer { + status = "okay"; +}; + +/* eMMC */ +&mmc_0 { + non-removable; + max-frequency = <200000000>; + sd-uhs-ddr50; + mmc-ddr-1_8v; + cap-mmc-hw-reset; + mmc-hs200-1_8v; + bus-width = <8>; + card-detect-delay = <200>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <1 2>; + samsung,dw-mshc-ddr-timing = <2 3>; + + pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus4 &sd0_bus8>; + pinctrl-names = "default"; + + status = "okay"; +}; + +/* WiFi */ +&mmc_1 { + non-removable; + max-frequency = <100000000>; + sd-uhs-sdr50; + cap-sd-highspeed; + keep-power-in-suspend; + bus-width = <4>; + card-detect-delay = <0>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <2 4>; + samsung,dw-mshc-ddr-timing = <2 3>; + + pinctrl-names = "default"; + pinctrl-0 = <&sd1_clk &sd1_cmd &sd1_bus4>; + mmc-pwrseq = <&pwrseq>; + + status = "okay"; + + wifi@1 { + compatible = "brcm,bcm4330-fmac", "brcm,bcm4329-fmac"; + reg = <1>; + interrupt-parent = <&gpx2>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "host-wake"; + pinctrl-0 = <&wlan_irq>; + pinctrl-names = "default"; + }; +}; + +&ohci { + status = "disabled"; +}; + +&pinctrl_0 { + acc_int: acc-int-pins { + samsung,pins = "gpx1-4"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + max77686_irq: max77686-irq-pins { + samsung,pins = "gpx0-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + gpio_keys: gpio-keys-pins { + samsung,pins = "gpx2-0", "gpx2-1", "gpx2-7", "gpx1-3"; + samsung,pin-pud = ; + }; + + wlan_irq: wlan-irq-pins { + samsung,pins = "gpx2-5"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; +}; + +&pinctrl_1 { + bh1721fvc_reset: bh1721fvc-reset-pins { + samsung,pins = "gph1-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + msense_reset: msense-reset-pins { + samsung,pins = "gpg2-0"; + samsung,pin-function = ; + }; + + onewire_sleep: onewire-sleep-pins { + samsung,pins = "gpg0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-con-pdn = ; + samsung,pin-pud-pdn = ; + }; +}; + +&pinctrl_2 { + wlan_pmena: wlan-pmena-pins { + samsung,pins = "gpv1-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + samsung,pin-con-pdn = ; + samsung,pin-val = <0>; + }; +}; + +&pmu_system_controller { + assigned-clocks = <&pmu_system_controller 0>; + assigned-clock-parents = <&clock CLK_FIN_PLL>; +}; + +&rtc { + clocks = <&clock CLK_RTC>, <&max77686 MAX77686_CLK_AP>; + clock-names = "rtc", "rtc_src"; + status = "okay"; +}; + +&sd1_bus4 { + samsung,pin-con-pdn = ; + samsung,pin-pud-pdn = ; +}; + +&sd1_cmd { + samsung,pin-con-pdn = ; + samsung,pin-pud-pdn = ; +}; + +/* Bluetooth */ +&serial_0 { + status = "disabled"; +}; + +/* GPS */ +&serial_1 { + status = "disabled"; +}; + +&serial_2 { + pinctrl-0 = <&uart2_data>; + pinctrl-1 = <&uart2_data>, <&uart2_fctl>; + pinctrl-names = "default", "flow-control"; + status = "okay"; +}; + +&usbdrd { + status = "disabled"; +}; + +&usbdrd_dwc3 { + status = "disabled"; +}; + +&usbdrd_phy { + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/st/Makefile b/arch/arm/boot/dts/st/Makefile index e906bf6ba004..10c5d75f6169 100644 --- a/arch/arm/boot/dts/st/Makefile +++ b/arch/arm/boot/dts/st/Makefile @@ -16,6 +16,91 @@ dtb-$(CONFIG_ARCH_STI) += \ stih410-b2260.dtb \ stih418-b2199.dtb \ stih418-b2264.dtb + +stm32mp13xx-dhcor-dhsbc-overlay-rb-tft32-v2-dtbs := \ + stm32mp135f-dhcor-dhsbc.dtb \ + stm32mp13xx-dhcor-dhsbc-overlay-rb-tft32-v2.dtbo + +stm32mp15xx-avenger96-overlay-644-100-x6-otm8009a-dtbs := \ + stm32mp157a-avenger96.dtb \ + stm32mp15xx-avenger96-overlay-644-100-x6-otm8009a.dtbo + +stm32mp15xx-avenger96-overlay-644-100-x6-rpi7inch-dtbs := \ + stm32mp157a-avenger96.dtb \ + stm32mp15xx-avenger96-overlay-644-100-x6-rpi7inch.dtbo + +stm32mp15xx-avenger96-overlay-fdcan1-x6-dtbs := \ + stm32mp157a-avenger96.dtb \ + stm32mp15xx-avenger96-overlay-fdcan1-x6.dtbo + +stm32mp15xx-avenger96-overlay-fdcan2-x6-dtbs := \ + stm32mp157a-avenger96.dtb \ + stm32mp15xx-avenger96-overlay-fdcan2-x6.dtbo + +stm32mp15xx-avenger96-overlay-i2c1-eeprom-x6-dtbs := \ + stm32mp157a-avenger96.dtb \ + stm32mp15xx-avenger96-overlay-i2c1-eeprom-x6.dtbo + +stm32mp15xx-avenger96-overlay-i2c2-eeprom-x6-dtbs := \ + stm32mp157a-avenger96.dtb \ + stm32mp15xx-avenger96-overlay-i2c2-eeprom-x6.dtbo + +stm32mp15xx-avenger96-overlay-ov5640-x7-dtbs := \ + stm32mp157a-avenger96.dtb \ + stm32mp15xx-avenger96-overlay-ov5640-x7.dtbo + +stm32mp15xx-avenger96-overlay-spi2-eeprom-x6-dtbs := \ + stm32mp157a-avenger96.dtb \ + stm32mp15xx-avenger96-overlay-spi2-eeprom-x6.dtbo + +stm32mp15xx-dhcom-drc02-overlay-wifi-rsi-dtbs := \ + stm32mp153c-dhcom-drc02.dtb \ + stm32mp15xx-dhcom-drc02-overlay-wifi-rsi.dtbo + +stm32mp15xx-dhcom-pdk2-overlay-460-200-x11-dtbs := \ + stm32mp157c-dhcom-pdk2.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-460-200-x11.dtbo \ + +stm32mp15xx-dhcom-pdk2-overlay-497-200-x12-dtbs := \ + stm32mp157c-dhcom-pdk2.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-497-200-x12.dtbo \ + +stm32mp15xx-dhcom-pdk2-overlay-531-100-x21-dtbs := \ + stm32mp157c-dhcom-pdk2.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-531-100-x21.dtbo \ + +stm32mp15xx-dhcom-pdk2-overlay-531-100-x22-dtbs := \ + stm32mp157c-dhcom-pdk2.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-531-100-x22.dtbo \ + +stm32mp15xx-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh-dtbs := \ + stm32mp157c-dhcom-pdk2.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh.dtbo \ + +stm32mp15xx-dhcom-pdk2-overlay-560-200-x12-dtbs := \ + stm32mp157c-dhcom-pdk2.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-560-200-x12.dtbo \ + +stm32mp15xx-dhcom-pdk2-overlay-638-100-x12-rpi7inch-dtbs := \ + stm32mp157c-dhcom-pdk2.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-638-100-x12-rpi7inch.dtbo \ + +stm32mp15xx-dhcom-pdk2-overlay-672-100-x18-dtbs := \ + stm32mp157c-dhcom-pdk2.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-672-100-x18.dtbo \ + +stm32mp15xx-dhcom-picoitx-overlay-548-200-x2-mi0700s4t-6-dtbs := \ + stm32mp157c-dhcom-picoitx.dtb \ + stm32mp15xx-dhcom-picoitx-overlay-548-200-x2-mi0700s4t-6.dtbo \ + +stm32mp15xx-dhcom-picoitx-overlay-553-100-x2-tst043015cmhx-dtbs := \ + stm32mp157c-dhcom-pdk2.dtb \ + stm32mp15xx-dhcom-picoitx-overlay-553-100-x2-tst043015cmhx.dtbo \ + +stm32mp15xx-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh-dtbs := \ + stm32mp157c-dhcom-pdk2.dtb \ + stm32mp15xx-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh.dtbo \ + dtb-$(CONFIG_ARCH_STM32) += \ stm32f429-disco.dtb \ stm32f469-disco.dtb \ @@ -30,6 +115,8 @@ dtb-$(CONFIG_ARCH_STM32) += \ stm32h750i-art-pi.dtb \ stm32mp133c-prihmb.dtb \ stm32mp135f-dhcor-dhsbc.dtb \ + stm32mp13xx-dhcor-dhsbc-overlay-rb-tft32-v2.dtb \ + stm32mp13xx-dhcor-dhsbc-overlay-rb-tft32-v2.dtbo \ stm32mp135f-dk.dtb \ stm32mp151a-prtt1a.dtb \ stm32mp151a-prtt1c.dtb \ @@ -39,12 +126,30 @@ dtb-$(CONFIG_ARCH_STM32) += \ stm32mp151c-mect1s.dtb \ stm32mp151c-plyaqm.dtb \ stm32mp153c-dhcom-drc02.dtb \ + stm32mp15xx-dhcom-drc02-overlay-wifi-rsi.dtb \ + stm32mp15xx-dhcom-drc02-overlay-wifi-rsi.dtbo \ stm32mp153c-dhcor-drc-compact.dtb \ stm32mp153c-lxa-fairytux2-gen1.dtb \ stm32mp153c-lxa-fairytux2-gen2.dtb \ stm32mp153c-lxa-tac-gen3.dtb \ stm32mp153c-mecio1r1.dtb \ stm32mp157a-avenger96.dtb \ + stm32mp15xx-avenger96-overlay-644-100-x6-otm8009a.dtb \ + stm32mp15xx-avenger96-overlay-644-100-x6-otm8009a.dtbo \ + stm32mp15xx-avenger96-overlay-644-100-x6-rpi7inch.dtb \ + stm32mp15xx-avenger96-overlay-644-100-x6-rpi7inch.dtbo \ + stm32mp15xx-avenger96-overlay-fdcan1-x6.dtb \ + stm32mp15xx-avenger96-overlay-fdcan1-x6.dtbo \ + stm32mp15xx-avenger96-overlay-fdcan2-x6.dtb \ + stm32mp15xx-avenger96-overlay-fdcan2-x6.dtbo \ + stm32mp15xx-avenger96-overlay-i2c1-eeprom-x6.dtb \ + stm32mp15xx-avenger96-overlay-i2c1-eeprom-x6.dtbo \ + stm32mp15xx-avenger96-overlay-i2c2-eeprom-x6.dtb \ + stm32mp15xx-avenger96-overlay-i2c2-eeprom-x6.dtbo \ + stm32mp15xx-avenger96-overlay-ov5640-x7.dtb \ + stm32mp15xx-avenger96-overlay-ov5640-x7.dtbo \ + stm32mp15xx-avenger96-overlay-spi2-eeprom-x6.dtb \ + stm32mp15xx-avenger96-overlay-spi2-eeprom-x6.dtbo \ stm32mp157a-dhcor-avenger96.dtb \ stm32mp157a-dk1.dtb \ stm32mp157a-dk1-scmi.dtb \ @@ -56,7 +161,29 @@ dtb-$(CONFIG_ARCH_STM32) += \ stm32mp157a-icore-stm32mp1-edimm2.2.dtb \ stm32mp157a-stinger96.dtb \ stm32mp157c-dhcom-pdk2.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-460-200-x11.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-460-200-x11.dtbo \ + stm32mp15xx-dhcom-pdk2-overlay-497-200-x12.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-497-200-x12.dtbo \ + stm32mp15xx-dhcom-pdk2-overlay-531-100-x21.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-531-100-x21.dtbo \ + stm32mp15xx-dhcom-pdk2-overlay-531-100-x22.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-531-100-x22.dtbo \ + stm32mp15xx-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh.dtbo \ + stm32mp15xx-dhcom-pdk2-overlay-560-200-x12.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-560-200-x12.dtbo \ + stm32mp15xx-dhcom-pdk2-overlay-638-100-x12-rpi7inch.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-638-100-x12-rpi7inch.dtbo \ + stm32mp15xx-dhcom-pdk2-overlay-672-100-x18.dtb \ + stm32mp15xx-dhcom-pdk2-overlay-672-100-x18.dtbo \ stm32mp157c-dhcom-picoitx.dtb \ + stm32mp15xx-dhcom-picoitx-overlay-548-200-x2-mi0700s4t-6.dtb \ + stm32mp15xx-dhcom-picoitx-overlay-548-200-x2-mi0700s4t-6.dtbo \ + stm32mp15xx-dhcom-picoitx-overlay-553-100-x2-tst043015cmhx.dtb \ + stm32mp15xx-dhcom-picoitx-overlay-553-100-x2-tst043015cmhx.dtbo \ + stm32mp15xx-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh.dtb \ + stm32mp15xx-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh.dtbo \ stm32mp157c-dk2.dtb \ stm32mp157c-dk2-scmi.dtb \ stm32mp157c-ed1.dtb \ @@ -69,7 +196,7 @@ dtb-$(CONFIG_ARCH_STM32) += \ stm32mp157c-lxa-tac-gen2.dtb \ stm32mp157c-odyssey.dtb \ stm32mp157c-osd32mp1-red.dtb \ - stm32mp157c-phycore-stm32mp1-3.dtb \ + stm32mp157c-phyboard-sargas-rdk.dtb \ stm32mp157c-ultra-fly-sbc.dtb \ stm32mp157f-dk2.dtb dtb-$(CONFIG_ARCH_U8500) += \ diff --git a/arch/arm/boot/dts/st/spear13xx.dtsi b/arch/arm/boot/dts/st/spear13xx.dtsi index 159e941708ca..0bb88f2d4ef5 100644 --- a/arch/arm/boot/dts/st/spear13xx.dtsi +++ b/arch/arm/boot/dts/st/spear13xx.dtsi @@ -332,7 +332,6 @@ thermal@e07008c4 { compatible = "st,thermal-spear1340"; reg = <0xe07008c4 0x4>; - thermal_flags = <0x7000>; }; }; }; diff --git a/arch/arm/boot/dts/st/stm32mp131.dtsi b/arch/arm/boot/dts/st/stm32mp131.dtsi index b9657ff91c23..83ae59b73dd0 100644 --- a/arch/arm/boot/dts/st/stm32mp131.dtsi +++ b/arch/arm/boot/dts/st/stm32mp131.dtsi @@ -3,6 +3,7 @@ * Copyright (C) STMicroelectronics 2021 - All Rights Reserved * Author: Alexandre Torgue for STMicroelectronics. */ +#include #include #include #include @@ -469,8 +470,8 @@ compatible = "st,stm32mp13-i2c"; reg = <0x40012000 0x400>; interrupt-names = "event", "error"; - interrupts = , - ; + interrupts-extended = <&exti 21 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc I2C1_K>; resets = <&rcc I2C1_R>; #address-cells = <1>; @@ -478,6 +479,7 @@ dmas = <&dmamux1 33 0x400 0x1>, <&dmamux1 34 0x400 0x1>; dma-names = "rx", "tx"; + wakeup-source; st,syscfg-fmp = <&syscfg 0x4 0x1>; i2c-analog-filter; status = "disabled"; @@ -487,8 +489,8 @@ compatible = "st,stm32mp13-i2c"; reg = <0x40013000 0x400>; interrupt-names = "event", "error"; - interrupts = , - ; + interrupts-extended = <&exti 22 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc I2C2_K>; resets = <&rcc I2C2_R>; #address-cells = <1>; @@ -497,6 +499,7 @@ <&dmamux1 36 0x400 0x1>; dma-names = "rx", "tx"; st,syscfg-fmp = <&syscfg 0x4 0x2>; + wakeup-source; i2c-analog-filter; status = "disabled"; }; @@ -964,9 +967,125 @@ compatible = "st,stm32mp131-hdp"; reg = <0x5002a000 0x400>; clocks = <&rcc HDP>; + access-controllers = <&dbg_bus 1>; status = "disabled"; }; + dbg_bus: bus@50080000 { + compatible = "st,stm32mp131-dbg-bus"; + #address-cells = <1>; + #size-cells = <1>; + #access-controller-cells = <1>; + ranges = <0x50080000 0x50080000 0x3f80000>; + status = "disabled"; + + cs_etf: etf@50092000 { + compatible = "arm,coresight-tmc", "arm,primecell"; + reg = <0x50092000 0x1000>; + clocks = <&rcc CK_DBG>; + clock-names = "apb_pclk"; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + + in-ports { + port { + etf_in_port: endpoint { + remote-endpoint = <&etm0_out_port>; + }; + }; + }; + + out-ports { + port { + etf_out_port: endpoint { + remote-endpoint = <&tpiu_in_port>; + }; + }; + }; + }; + + cs_tpiu: tpiu@50093000 { + compatible = "arm,coresight-tpiu", "arm,primecell"; + reg = <0x50093000 0x1000>; + clocks = <&rcc CK_DBG>, <&rcc CK_TRACE>; + clock-names = "apb_pclk", "atclk"; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + + in-ports { + port { + tpiu_in_port: endpoint { + remote-endpoint = <&etf_out_port>; + }; + }; + }; + }; + + cs_cti_trace: cti@50094000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x50094000 0x1000>; + clocks = <&rcc CK_DBG>; + clock-names = "apb_pclk"; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + }; + + cs_cti_cpu0: cti@500d8000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x500d8000 0x1000>; + clocks = <&rcc CK_DBG>; + clock-names = "apb_pclk"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + + trig-conns@0 { + reg = <0>; + arm,trig-in-sigs = <0 4 5>; + arm,trig-in-types = ; + arm,trig-out-sigs = <0 7>; + arm,trig-out-types = ; + cpu = <&cpu0>; + }; + + trig-conns@2 { + reg = <2>; + arm,trig-in-sigs = <2 3 6>; + arm,trig-in-types = ; + arm,trig-out-sigs = <1 2 3 4>; + arm,trig-out-types = ; + arm,cs-dev-assoc = <&cs_etm0>; + }; + }; + + cs_etm0: etm@500dc000 { + compatible = "arm,coresight-etm3x", "arm,primecell"; + reg = <0x500dc000 0x1000>; + cpu = <&cpu0>; + clocks = <&rcc CK_DBG>, <&rcc CK_TRACE>; + clock-names = "apb_pclk", "atclk"; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + + out-ports { + port { + etm0_out_port: endpoint { + remote-endpoint = <&etf_in_port>; + }; + }; + }; + }; + }; + mdma: dma-controller@58000000 { compatible = "st,stm32h7-mdma"; reg = <0x58000000 0x1000>; @@ -1129,7 +1248,7 @@ resets = <&rcc USART1_R>; wakeup-source; dmas = <&dmamux1 41 0x400 0x5>, - <&dmamux1 42 0x400 0x1>; + <&dmamux1 42 0x400 0x1>; dma-names = "rx", "tx"; access-controllers = <&etzpc 16>; status = "disabled"; @@ -1143,7 +1262,7 @@ resets = <&rcc USART2_R>; wakeup-source; dmas = <&dmamux1 43 0x400 0x5>, - <&dmamux1 44 0x400 0x1>; + <&dmamux1 44 0x400 0x1>; dma-names = "rx", "tx"; access-controllers = <&etzpc 17>; status = "disabled"; @@ -1155,7 +1274,7 @@ #sound-dai-cells = <0>; interrupts = ; dmas = <&dmamux1 83 0x400 0x01>, - <&dmamux1 84 0x400 0x01>; + <&dmamux1 84 0x400 0x01>; dma-names = "rx", "tx"; access-controllers = <&etzpc 13>; status = "disabled"; @@ -1195,8 +1314,8 @@ compatible = "st,stm32mp13-i2c"; reg = <0x4c004000 0x400>; interrupt-names = "event", "error"; - interrupts = , - ; + interrupts-extended = <&exti 23 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc I2C3_K>; resets = <&rcc I2C3_R>; #address-cells = <1>; @@ -1205,6 +1324,7 @@ <&dmamux1 74 0x400 0x1>; dma-names = "rx", "tx"; st,syscfg-fmp = <&syscfg 0x4 0x4>; + wakeup-source; i2c-analog-filter; access-controllers = <&etzpc 20>; status = "disabled"; @@ -1214,8 +1334,8 @@ compatible = "st,stm32mp13-i2c"; reg = <0x4c005000 0x400>; interrupt-names = "event", "error"; - interrupts = , - ; + interrupts-extended = <&exti 24 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc I2C4_K>; resets = <&rcc I2C4_R>; #address-cells = <1>; @@ -1224,6 +1344,7 @@ <&dmamux1 76 0x400 0x1>; dma-names = "rx", "tx"; st,syscfg-fmp = <&syscfg 0x4 0x8>; + wakeup-source; i2c-analog-filter; access-controllers = <&etzpc 21>; status = "disabled"; @@ -1233,8 +1354,8 @@ compatible = "st,stm32mp13-i2c"; reg = <0x4c006000 0x400>; interrupt-names = "event", "error"; - interrupts = , - ; + interrupts-extended = <&exti 25 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc I2C5_K>; resets = <&rcc I2C5_R>; #address-cells = <1>; @@ -1243,6 +1364,7 @@ <&dmamux1 116 0x400 0x1>; dma-names = "rx", "tx"; st,syscfg-fmp = <&syscfg 0x4 0x10>; + wakeup-source; i2c-analog-filter; access-controllers = <&etzpc 22>; status = "disabled"; @@ -1348,9 +1470,9 @@ clocks = <&rcc TIM15_K>; clock-names = "int"; dmas = <&dmamux1 105 0x400 0x1>, - <&dmamux1 106 0x400 0x1>, - <&dmamux1 107 0x400 0x1>, - <&dmamux1 108 0x400 0x1>; + <&dmamux1 106 0x400 0x1>, + <&dmamux1 107 0x400 0x1>, + <&dmamux1 108 0x400 0x1>; dma-names = "ch1", "up", "trig", "com"; access-controllers = <&etzpc 26>; status = "disabled"; @@ -1383,7 +1505,7 @@ clocks = <&rcc TIM16_K>; clock-names = "int"; dmas = <&dmamux1 109 0x400 0x1>, - <&dmamux1 110 0x400 0x1>; + <&dmamux1 110 0x400 0x1>; dma-names = "ch1", "up"; access-controllers = <&etzpc 27>; status = "disabled"; diff --git a/arch/arm/boot/dts/st/stm32mp135f-dk.dts b/arch/arm/boot/dts/st/stm32mp135f-dk.dts index 8dcf68b212b4..4d4cec8b86ac 100644 --- a/arch/arm/boot/dts/st/stm32mp135f-dk.dts +++ b/arch/arm/boot/dts/st/stm32mp135f-dk.dts @@ -187,6 +187,30 @@ status = "okay"; }; +&cs_cti_trace { + status = "okay"; +}; + +&cs_cti_cpu0 { + status = "okay"; +}; + +&cs_etf { + status = "okay"; +}; + +&cs_etm0 { + status = "okay"; +}; + +&cs_tpiu { + status = "okay"; +}; + +&dbg_bus { + status = "okay"; +}; + &dcmipp { pinctrl-names = "default", "sleep"; pinctrl-0 = <&dcmipp_pins_a>; diff --git a/arch/arm/boot/dts/st/stm32mp13xx-dhcor-dhsbc-overlay-rb-tft32-v2.dtso b/arch/arm/boot/dts/st/stm32mp13xx-dhcor-dhsbc-overlay-rb-tft32-v2.dtso new file mode 100644 index 000000000000..3801dab141e8 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp13xx-dhcor-dhsbc-overlay-rb-tft32-v2.dtso @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2024 Marek Vasut + */ + +#include +#include +#include + +/dts-v1/; +/plugin/; + +&{/} { + gpio-keys { + compatible = "gpio-keys"; + + button-1 { + label = "KEY2"; + linux,code = ; + gpios = <&gpiog 10 GPIO_ACTIVE_LOW>; + wakeup-source; + }; + }; + + gpio-keys-polled { + compatible = "gpio-keys-polled"; + poll-interval = <20>; + + button-0 { + label = "KEY1"; + linux,code = ; + /* IRQ bank A shared with PA1 touch controller */ + gpios = <&gpioa 4 GPIO_ACTIVE_LOW>; + }; + + button-2 { + label = "KEY3"; + linux,code = ; + /* IRQ line 0 taken by PI0 / SoM RTC IRQ */ + gpios = <&gpiod 0 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&m_can1 { + /* Collides with KEY2/PG10 KEY3/PD0 */ + status = "disabled"; +}; + +&m_can2 { + /* Collides with TP_CS/PE6 */ + status = "disabled"; +}; + +&usart2 { + /* Collides with TP_IRQ/PA1 */ + status = "disabled"; +}; + +&spi3 { + #address-cells = <1>; + #size-cells = <0>; + cs-gpios = <&gpiof 3 0>, <&gpioe 0 0>; + status = "okay"; + + lcd@0 { + compatible = "adafruit,yx240qv29", "ilitek,ili9341"; + reg = <0>; + spi-max-frequency = <10000000>; + dc-gpios = <&gpioe 4 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpiod 3 GPIO_ACTIVE_HIGH>; + rotation = <90>; + }; + + tp@1 { + compatible = "ti,tsc2046"; + reg = <1>; + interrupt-parent = <&gpioa>; + interrupts = <1 IRQ_TYPE_EDGE_FALLING>; + pendown-gpio = <&gpioa 1 GPIO_ACTIVE_LOW>; + spi-max-frequency = <500000>; + ti,pressure-max = /bits/ 16 <255>; + ti,x-plate-ohms = /bits/ 16 <60>; + }; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15-pinctrl.dtsi b/arch/arm/boot/dts/st/stm32mp15-pinctrl.dtsi index 8613a6a17ee9..aaa91b634c12 100644 --- a/arch/arm/boot/dts/st/stm32mp15-pinctrl.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15-pinctrl.dtsi @@ -231,6 +231,45 @@ }; }; + /omit-if-no-ref/ + dcmi_pins_d: dcmi-3 { + pins { + pinmux = ,/* DCMI_HSYNC */ + ,/* DCMI_VSYNC */ + ,/* DCMI_PIXCLK */ + ,/* DCMI_D0 */ + ,/* DCMI_D1 */ + ,/* DCMI_D2 */ + ,/* DCMI_D3 */ + ,/* DCMI_D4 */ + ,/* DCMI_D5 */ + ,/* DCMI_D6 */ + ,/* DCMI_D7 */ + ,/* DCMI_D8 */ + ;/* DCMI_D9 */ + bias-disable; + }; + }; + + /omit-if-no-ref/ + dcmi_sleep_pins_d: dcmi-sleep-3 { + pins { + pinmux = ,/* DCMI_HSYNC */ + ,/* DCMI_VSYNC */ + ,/* DCMI_PIXCLK */ + ,/* DCMI_D0 */ + ,/* DCMI_D1 */ + ,/* DCMI_D2 */ + ,/* DCMI_D3 */ + ,/* DCMI_D4 */ + ,/* DCMI_D5 */ + ,/* DCMI_D6 */ + ,/* DCMI_D7 */ + ,/* DCMI_D8 */ + ;/* DCMI_D9 */ + }; + }; + /omit-if-no-ref/ ethernet0_rgmii_pins_a: rgmii-0 { pins1 { @@ -394,6 +433,7 @@ ethernet0_rgmii_pins_d: rgmii-3 { pins1 { pinmux = , /* ETH_RGMII_CLK125 */ + , /* ETH_RGMII_GTX_CLK */ , /* ETH_RGMII_TXD0 */ , /* ETH_RGMII_TXD1 */ , /* ETH_RGMII_TXD2 */ @@ -1343,6 +1383,65 @@ }; }; + /omit-if-no-ref/ + ltdc_pins_f: ltdc-5 { + pins { + pinmux = , /* LCD_CLK */ + , /* LCD_HSYNC */ + , /* LCD_VSYNC */ + , /* LCD_DE */ + , /* LCD_R2 */ + , /* LCD_R3 */ + , /* LCD_R4 */ + , /* LCD_R5 */ + , /* LCD_R6 */ + , /* LCD_R7 */ + , /* LCD_G2 */ + , /* LCD_G3 */ + , /* LCD_G4 */ + , /* LCD_G5 */ + , /* LCD_G6 */ + , /* LCD_G7 */ + , /* LCD_B2 */ + , /* LCD_B3 */ + , /* LCD_B4 */ + , /* LCD_B5 */ + , /* LCD_B6 */ + ; /* LCD_B7 */ + bias-disable; + drive-push-pull; + slew-rate = <1>; + }; + }; + + /omit-if-no-ref/ + ltdc_sleep_pins_f: ltdc-sleep-5 { + pins { + pinmux = , /* LCD_CLK */ + , /* LCD_HSYNC */ + , /* LCD_VSYNC */ + , /* LCD_DE */ + , /* LCD_R2 */ + , /* LCD_R3 */ + , /* LCD_R4 */ + , /* LCD_R5 */ + , /* LCD_R6 */ + , /* LCD_R7 */ + , /* LCD_G2 */ + , /* LCD_G3 */ + , /* LCD_G4 */ + , /* LCD_G5 */ + , /* LCD_G6 */ + , /* LCD_G7 */ + , /* LCD_B2 */ + , /* LCD_B3 */ + , /* LCD_B4 */ + , /* LCD_B5 */ + , /* LCD_B6 */ + ; /* LCD_B7 */ + }; + }; + /omit-if-no-ref/ mco1_pins_a: mco1-0 { pins { @@ -1683,6 +1782,23 @@ }; }; + /omit-if-no-ref/ + pwm5_pins_c: pwm5-2 { + pins { + pinmux = ; /* TIM5_CH4 */ + bias-pull-down; + drive-push-pull; + slew-rate = <0>; + }; + }; + + /omit-if-no-ref/ + pwm5_sleep_pins_c: pwm5-sleep-2 { + pins { + pinmux = ; /* TIM5_CH4 */ + }; + }; + /omit-if-no-ref/ pwm8_pins_a: pwm8-0 { pins { @@ -1908,6 +2024,21 @@ }; }; + /omit-if-no-ref/ + sai2a_pins_d: sai2a-3 { + pins { + pinmux = ; /* SAI2_SD_A */ + bias-disable; + }; + }; + + /omit-if-no-ref/ + sai2a_sleep_pins_d: sai2a-3 { + pins { + pinmux = ; /* SAI2_SD_A */ + }; + }; + /omit-if-no-ref/ sai2b_pins_a: sai2b-0 { pins1 { @@ -2895,6 +3026,39 @@ }; }; + /omit-if-no-ref/ + uart4_pins_f: uart4-5 { + pins1 { + pinmux = ; /* UART4_TX */ + bias-disable; + drive-push-pull; + slew-rate = <0>; + }; + pins2 { + pinmux = ; /* UART4_RX */ + bias-disable; + }; + }; + + /omit-if-no-ref/ + uart4_idle_pins_f: uart4-idle-5 { + pins1 { + pinmux = ; /* UART4_TX */ + }; + pins2 { + pinmux = ; /* UART4_RX */ + bias-disable; + }; + }; + + /omit-if-no-ref/ + uart4_sleep_pins_f: uart4-sleep-5 { + pins { + pinmux = , /* UART4_TX */ + ; /* UART4_RX */ + }; + }; + /omit-if-no-ref/ uart5_pins_a: uart5-0 { pins1 { diff --git a/arch/arm/boot/dts/st/stm32mp151.dtsi b/arch/arm/boot/dts/st/stm32mp151.dtsi index b1b568dfd126..84f68e8563d8 100644 --- a/arch/arm/boot/dts/st/stm32mp151.dtsi +++ b/arch/arm/boot/dts/st/stm32mp151.dtsi @@ -3,6 +3,7 @@ * Copyright (C) STMicroelectronics 2017 - All Rights Reserved * Author: Ludovic Barre for STMicroelectronics. */ +#include #include #include #include @@ -123,6 +124,14 @@ interrupt-parent = <&intc>; ranges; + sram4: sram@10050000 { + compatible = "mmio-sram"; + reg = <0x10050000 0x10000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x10050000 0x10000>; + }; + ipcc: mailbox@4c001000 { compatible = "st,stm32mp1-ipcc"; #mbox-cells = <1>; @@ -274,9 +283,180 @@ compatible = "st,stm32mp151-hdp"; reg = <0x5002a000 0x400>; clocks = <&rcc HDP>; + access-controllers = <&dbg_bus 1>; status = "disabled"; }; + dbg_bus: bus@50080000 { + compatible = "st,stm32mp151-dbg-bus"; + #address-cells = <1>; + #size-cells = <1>; + #access-controller-cells = <1>; + ranges = <0x50080000 0x50080000 0x3f80000>, + <0x90000000 0x90000000 0x1000000>; + status = "disabled"; + + cs_funnel: funnel@50091000 { + compatible = "arm,coresight-dynamic-funnel", "arm,primecell"; + reg = <0x50091000 0x1000>; + clocks = <&rcc CK_DBG>, <&rcc CK_TRACE>; + clock-names = "apb_pclk", "atclk"; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + + in-ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + funnel_in_port0: endpoint { + remote-endpoint = <&stm_out_port>; + }; + }; + + port@1 { + reg = <1>; + funnel_in_port1: endpoint { + remote-endpoint = <&etm0_out>; + }; + }; + }; + + out-ports { + port { + funnel_out_port: endpoint { + remote-endpoint = <&etf_in_port>; + }; + }; + }; + }; + + cs_etf: etf@50092000 { + compatible = "arm,coresight-tmc", "arm,primecell"; + reg = <0x50092000 0x1000>; + clocks = <&rcc CK_DBG>; + clock-names = "apb_pclk"; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + + in-ports { + port { + etf_in_port: endpoint { + remote-endpoint = <&funnel_out_port>; + }; + }; + }; + + out-ports { + port { + etf_out_port: endpoint { + remote-endpoint = <&tpiu_in_port>; + }; + }; + }; + }; + + cs_tpiu: tpiu@50093000 { + compatible = "arm,coresight-tpiu", "arm,primecell"; + reg = <0x50093000 0x1000>; + clocks = <&rcc CK_DBG>, <&rcc CK_TRACE>; + clock-names = "apb_pclk", "atclk"; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + + in-ports { + port { + tpiu_in_port: endpoint { + remote-endpoint = <&etf_out_port>; + }; + }; + }; + }; + + cs_cti_trace: cti@50094000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x50094000 0x1000>; + clocks = <&rcc CK_DBG>; + clock-names = "apb_pclk"; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + }; + + cs_stm: stm@500a0000 { + compatible = "arm,coresight-stm", "arm,primecell"; + reg = <0x500a0000 0x00001000>, + <0x90000000 0x01000000>; + reg-names = "stm-base", "stm-stimulus-base"; + clocks = <&rcc CK_DBG>, <&rcc CK_TRACE>; + clock-names = "apb_pclk", "atclk"; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + + out-ports { + port { + stm_out_port: endpoint { + remote-endpoint = <&funnel_in_port0>; + }; + }; + }; + }; + + cs_cti_cpu0: cti@500d8000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x500d8000 0x1000>; + clocks = <&rcc CK_DBG>; + clock-names = "apb_pclk"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + + trig-conns@0 { + reg = <0>; + arm,trig-in-sigs = <0 4 5>; + arm,trig-in-types = ; + arm,trig-out-sigs = <0 7>; + arm,trig-out-types = ; + cpu = <&cpu0>; + }; + + trig-conns@2 { + reg = <2>; + arm,trig-in-sigs = <2 3 6>; + arm,trig-in-types = ; + arm,trig-out-sigs = <1 2 3 4>; + arm,trig-out-types = ; + arm,cs-dev-assoc = <&cs_etm0>; + }; + }; + + cs_etm0: etm@500dc000 { + compatible = "arm,coresight-etm3x", "arm,primecell"; + reg = <0x500dc000 0x1000>; + cpu = <&cpu0>; + clocks = <&rcc CK_DBG>, <&rcc CK_TRACE>; + clock-names = "apb_pclk", "atclk"; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + + out-ports { + port { + etm0_out: endpoint { + remote-endpoint = <&funnel_in_port1>; + }; + }; + }; + }; + }; + mdma1: dma-controller@58000000 { compatible = "st,stm32h7-mdma"; reg = <0x58000000 0x1000>; @@ -867,12 +1047,15 @@ compatible = "st,stm32mp15-i2c"; reg = <0x40012000 0x400>; interrupt-names = "event", "error"; - interrupts = , - ; + interrupts-extended = <&exti 21 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc I2C1_K>; resets = <&rcc I2C1_R>; #address-cells = <1>; #size-cells = <0>; + dmas = <&dmamux1 33 0x400 0x1>, + <&dmamux1 34 0x400 0x1>; + dma-names = "rx", "tx"; st,syscfg-fmp = <&syscfg 0x4 0x1>; wakeup-source; i2c-analog-filter; @@ -884,12 +1067,15 @@ compatible = "st,stm32mp15-i2c"; reg = <0x40013000 0x400>; interrupt-names = "event", "error"; - interrupts = , - ; + interrupts-extended = <&exti 22 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc I2C2_K>; resets = <&rcc I2C2_R>; #address-cells = <1>; #size-cells = <0>; + dmas = <&dmamux1 35 0x400 0x1>, + <&dmamux1 36 0x400 0x1>; + dma-names = "rx", "tx"; st,syscfg-fmp = <&syscfg 0x4 0x2>; wakeup-source; i2c-analog-filter; @@ -901,12 +1087,15 @@ compatible = "st,stm32mp15-i2c"; reg = <0x40014000 0x400>; interrupt-names = "event", "error"; - interrupts = , - ; + interrupts-extended = <&exti 23 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc I2C3_K>; resets = <&rcc I2C3_R>; #address-cells = <1>; #size-cells = <0>; + dmas = <&dmamux1 73 0x400 0x1>, + <&dmamux1 74 0x400 0x1>; + dma-names = "rx", "tx"; st,syscfg-fmp = <&syscfg 0x4 0x4>; wakeup-source; i2c-analog-filter; @@ -918,12 +1107,15 @@ compatible = "st,stm32mp15-i2c"; reg = <0x40015000 0x400>; interrupt-names = "event", "error"; - interrupts = , - ; + interrupts-extended = <&exti 25 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc I2C5_K>; resets = <&rcc I2C5_R>; #address-cells = <1>; #size-cells = <0>; + dmas = <&dmamux1 115 0x400 0x1>, + <&dmamux1 116 0x400 0x1>; + dma-names = "rx", "tx"; st,syscfg-fmp = <&syscfg 0x4 0x10>; wakeup-source; i2c-analog-filter; @@ -1083,7 +1275,7 @@ clocks = <&rcc USART6_K>; wakeup-source; dmas = <&dmamux1 71 0x400 0x15>, - <&dmamux1 72 0x400 0x11>; + <&dmamux1 72 0x400 0x11>; dma-names = "rx", "tx"; access-controllers = <&etzpc 51>; status = "disabled"; @@ -1095,7 +1287,7 @@ reg = <0x44004000 0x400>; interrupts = ; dmas = <&dmamux1 37 0x400 0x01>, - <&dmamux1 38 0x400 0x01>; + <&dmamux1 38 0x400 0x01>; dma-names = "rx", "tx"; access-controllers = <&etzpc 52>; status = "disabled"; @@ -1110,7 +1302,7 @@ clocks = <&rcc SPI1_K>; resets = <&rcc SPI1_R>; dmas = <&dmamux1 37 0x400 0x05>, - <&dmamux1 38 0x400 0x05>; + <&dmamux1 38 0x400 0x05>; dma-names = "rx", "tx"; access-controllers = <&etzpc 52>; status = "disabled"; @@ -1125,7 +1317,7 @@ clocks = <&rcc SPI4_K>; resets = <&rcc SPI4_R>; dmas = <&dmamux1 83 0x400 0x05>, - <&dmamux1 84 0x400 0x05>; + <&dmamux1 84 0x400 0x05>; dma-names = "rx", "tx"; access-controllers = <&etzpc 53>; status = "disabled"; @@ -1176,7 +1368,7 @@ clocks = <&rcc TIM16_K>; clock-names = "int"; dmas = <&dmamux1 109 0x400 0x1>, - <&dmamux1 110 0x400 0x1>; + <&dmamux1 110 0x400 0x1>; dma-names = "ch1", "up"; access-controllers = <&etzpc 55>; status = "disabled"; @@ -1209,7 +1401,7 @@ clocks = <&rcc TIM17_K>; clock-names = "int"; dmas = <&dmamux1 111 0x400 0x1>, - <&dmamux1 112 0x400 0x1>; + <&dmamux1 112 0x400 0x1>; dma-names = "ch1", "up"; access-controllers = <&etzpc 56>; status = "disabled"; @@ -1241,7 +1433,7 @@ clocks = <&rcc SPI5_K>; resets = <&rcc SPI5_R>; dmas = <&dmamux1 85 0x400 0x05>, - <&dmamux1 86 0x400 0x05>; + <&dmamux1 86 0x400 0x05>; dma-names = "rx", "tx"; access-controllers = <&etzpc 57>; status = "disabled"; @@ -1829,12 +2021,15 @@ compatible = "st,stm32mp15-i2c"; reg = <0x5c002000 0x400>; interrupt-names = "event", "error"; - interrupts = , - ; + interrupts-extended = <&exti 24 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc I2C4_K>; resets = <&rcc I2C4_R>; #address-cells = <1>; #size-cells = <0>; + dmas = <&mdma1 36 0x0 0x40008 0x0 0x0>, + <&mdma1 37 0x0 0x40002 0x0 0x0>; + dma-names = "rx", "tx"; st,syscfg-fmp = <&syscfg 0x4 0x8>; wakeup-source; i2c-analog-filter; @@ -1846,12 +2041,15 @@ compatible = "st,stm32mp15-i2c"; reg = <0x5c009000 0x400>; interrupt-names = "event", "error"; - interrupts = , - ; + interrupts-extended = <&exti 54 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc I2C6_K>; resets = <&rcc I2C6_R>; #address-cells = <1>; #size-cells = <0>; + dmas = <&mdma1 38 0x0 0x40008 0x0 0x0>, + <&mdma1 39 0x0 0x40002 0x0 0x0>; + dma-names = "rx", "tx"; st,syscfg-fmp = <&syscfg 0x4 0x20>; wakeup-source; i2c-analog-filter; diff --git a/arch/arm/boot/dts/st/stm32mp153.dtsi b/arch/arm/boot/dts/st/stm32mp153.dtsi index 92794b942ab2..17d52d93695e 100644 --- a/arch/arm/boot/dts/st/stm32mp153.dtsi +++ b/arch/arm/boot/dts/st/stm32mp153.dtsi @@ -30,6 +30,74 @@ }; }; +&cs_funnel { + in-ports { + port@2 { + reg = <2>; + funnel_in_port2: endpoint { + remote-endpoint = <&etm1_out>; + }; + }; + }; +}; + +&dbg_bus { + cs_cti_cpu1: cti@500d9000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x500d9000 0x1000>; + clocks = <&rcc CK_DBG>; + clock-names = "apb_pclk"; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + + trig-conns@0 { + reg = <0>; + arm,trig-in-sigs = <0 4 5>; + arm,trig-in-types = ; + arm,trig-out-sigs = <0 7>; + arm,trig-out-types = ; + cpu = <&cpu1>; + }; + + trig-conns@2 { + reg = <2>; + arm,trig-in-sigs = <2 3 6>; + arm,trig-in-types = ; + arm,trig-out-sigs = <1 2 3 4>; + arm,trig-out-types = ; + arm,cs-dev-assoc = <&cs_etm1>; + }; + }; + + cs_etm1: etm@500dd000 { + compatible = "arm,coresight-etm3x", "arm,primecell"; + reg = <0x500dd000 0x1000>; + cpu = <&cpu1>; + clocks = <&rcc CK_DBG>, <&rcc CK_TRACE>; + clock-names = "apb_pclk", "atclk"; + access-controllers = <&dbg_bus 0>; + status = "disabled"; + + out-ports { + port { + etm1_out: endpoint { + remote-endpoint = <&funnel_in_port2>; + }; + }; + }; + }; +}; + &etzpc { m_can1: can@4400e000 { compatible = "bosch,m_can"; diff --git a/arch/arm/boot/dts/st/stm32mp157c-ev1.dts b/arch/arm/boot/dts/st/stm32mp157c-ev1.dts index 4e46d58bf61f..0e65a1862eb5 100644 --- a/arch/arm/boot/dts/st/stm32mp157c-ev1.dts +++ b/arch/arm/boot/dts/st/stm32mp157c-ev1.dts @@ -81,11 +81,59 @@ status = "okay"; }; +&cs_cti_trace { + status = "okay"; +}; + +&cs_cti_cpu0 { + status = "okay"; +}; + +&cs_cti_cpu1 { + status = "okay"; +}; + +&cs_etf { + status = "okay"; +}; + +&cs_etm0 { + status = "okay"; +}; + +&cs_etm1 { + status = "okay"; +}; + +&cs_funnel { + status = "okay"; +}; + +&cs_stm { + status = "okay"; +}; + +&cs_tpiu { + status = "okay"; +}; + +&dbg_bus { + status = "okay"; +}; + &dcmi { status = "okay"; pinctrl-names = "default", "sleep"; pinctrl-0 = <&dcmi_pins_a>; pinctrl-1 = <&dcmi_sleep_pins_a>; + /* + * Enable DMA-MDMA chaining by adding a SRAM pool and + * a MDMA channel + */ + sram = <&dcmi_pool>; + + dmas = <&dmamux1 75 0x400 0x01>, <&mdma1 0 0x3 0x1200000a 0 0>; + dma-names = "tx", "mdma_tx"; port { dcmi_0: endpoint { @@ -171,6 +219,8 @@ pinctrl-1 = <&i2c2_sleep_pins_a>; i2c-scl-rising-time-ns = <185>; i2c-scl-falling-time-ns = <20>; + /delete-property/dmas; + /delete-property/dma-names; status = "okay"; ov5640: camera@3c { @@ -227,6 +277,8 @@ pinctrl-1 = <&i2c5_sleep_pins_a>; i2c-scl-rising-time-ns = <185>; i2c-scl-falling-time-ns = <20>; + /delete-property/dmas; + /delete-property/dma-names; status = "okay"; }; @@ -302,6 +354,13 @@ status = "disabled"; }; +&sram4 { + dcmi_pool: dcmi-sram@0 { + reg = <0x0 0x8000>; + pool; + }; +}; + &timers2 { /* spare dmas for other usage (un-delete to enable pwm capture) */ /delete-property/dmas; diff --git a/arch/arm/boot/dts/st/stm32mp157c-phycore-stm32mp1-3.dts b/arch/arm/boot/dts/st/stm32mp157c-phyboard-sargas-rdk.dts similarity index 58% rename from arch/arm/boot/dts/st/stm32mp157c-phycore-stm32mp1-3.dts rename to arch/arm/boot/dts/st/stm32mp157c-phyboard-sargas-rdk.dts index 28d7203264ce..c18a37266083 100644 --- a/arch/arm/boot/dts/st/stm32mp157c-phycore-stm32mp1-3.dts +++ b/arch/arm/boot/dts/st/stm32mp157c-phyboard-sargas-rdk.dts @@ -6,39 +6,21 @@ /dts-v1/; -#include #include "stm32mp157.dtsi" #include "stm32mp15xc.dtsi" -#include "stm32mp15xxac-pinctrl.dtsi" -#include "stm32mp157c-phycore-stm32mp15-som.dtsi" +#include "stm32mp15xx-phycore-som.dtsi" +#include "stm32mp15xx-phyboard-sargas.dtsi" / { - model = "PHYTEC phyCORE-STM32MP1-3 Dev Board"; + model = "PHYTEC phyBOARD-Sargas STM32MP157C"; compatible = "phytec,phycore-stm32mp1-3", "phytec,phycore-stm32mp157c-som", "st,stm32mp157"; - - aliases { - mmc0 = &sdmmc1; - mmc1 = &sdmmc2; - mmc2 = &sdmmc3; - serial0 = &uart4; - serial1 = &usart3; - serial2 = &usart1; - }; }; &cryp1 { status = "okay"; }; -&dts { - status = "okay"; -}; - -&fmc { - status = "disabled"; -}; - &gpu { status = "okay"; }; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-644-100-x6-otm8009a.dtso b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-644-100-x6-otm8009a.dtso new file mode 100644 index 000000000000..103a2f0cf57b --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-644-100-x6-otm8009a.dtso @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +#include + +/dts-v1/; +/plugin/; + +&dsi { + #address-cells = <1>; + #size-cells = <0>; + + phy-dsi-supply = <®18>; + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi_in: endpoint { + remote-endpoint = <<dc_ep1_out>; + }; + }; + + port@1 { + reg = <1>; + dsi_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + + panel@0 { + compatible = "orisetech,otm8009a"; + reg = <0>; + reset-gpios = <&gpiod 8 GPIO_ACTIVE_LOW>; + power-supply = <&v3v3>; + status = "okay"; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; +}; + +<dc { + port { + #address-cells = <1>; + #size-cells = <0>; + + ltdc_ep1_out: endpoint@1 { + reg = <1>; + remote-endpoint = <&dsi_in>; + }; + }; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-644-100-x6-rpi7inch.dtso b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-644-100-x6-rpi7inch.dtso new file mode 100644 index 000000000000..cde2f8f68f86 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-644-100-x6-rpi7inch.dtso @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen: touchscreen@38 { + }; + + attiny: regulator@45 { + }; +}; + +#include "stm32mp15xx-dhsom-overlay-panel-dsi-rpi7inch.dtsi" + +<dc { + port { + #address-cells = <1>; + #size-cells = <0>; + + ltdc_ep_out: endpoint@1 { + reg = <1>; + remote-endpoint = <&dsi_in>; + }; + }; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-fdcan1-x6.dtso b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-fdcan1-x6.dtso new file mode 100644 index 000000000000..a9916aa8df75 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-fdcan1-x6.dtso @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&m_can1 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-fdcan2-x6.dtso b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-fdcan2-x6.dtso new file mode 100644 index 000000000000..c994ff055227 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-fdcan2-x6.dtso @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&m_can2 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-i2c1-eeprom-x6.dtso b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-i2c1-eeprom-x6.dtso new file mode 100644 index 000000000000..1edfff973a81 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-i2c1-eeprom-x6.dtso @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + eeprom@56 { + compatible = "atmel,24c04"; + reg = <0x56>; + pagesize = <16>; + }; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-i2c2-eeprom-x6.dtso b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-i2c2-eeprom-x6.dtso new file mode 100644 index 000000000000..bb3db38e3e63 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-i2c2-eeprom-x6.dtso @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&i2c2 { + #address-cells = <1>; + #size-cells = <0>; + + eeprom@56 { + compatible = "atmel,24c04"; + reg = <0x56>; + pagesize = <16>; + }; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-ov5640-x7.dtso b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-ov5640-x7.dtso new file mode 100644 index 000000000000..3056be6cd196 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-ov5640-x7.dtso @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +#include +#include + +/dts-v1/; +/plugin/; + +&{/} { + camera0_1v5_pwr: regulator-camera0-1v5 { + compatible = "regulator-fixed"; + regulator-name = "camera0-1v5-reg"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + }; + + camera0_1v8_pwr: regulator-camera0-1v8 { + compatible = "regulator-fixed"; + regulator-name = "camera0-1v8-reg"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + camera0_2v8_pwr: regulator-camera0-2v8 { + compatible = "regulator-fixed"; + regulator-name = "camera0-2v8-reg"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-always-on; + }; +}; + +&dcmi { + status = "okay"; +}; + +&dcmi_0 { + hsync-active = <0>; + vsync-active = <0>; + pclk-sample = <0>; +}; + +&i2c2 { + #address-cells = <1>; + #size-cells = <0>; + + camera@3c { + compatible = "ovti,ov5640"; + reg = <0x3c>; + clocks = <&rcc CK_MCO1>; + clock-names = "xclk"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&mco1_pins_a>; + pinctrl-1 = <&mco1_sleep_pins_a>; + assigned-clocks = <&rcc CK_MCO1>; + assigned-clock-parents = <&rcc CK_HSE>; + assigned-clock-rates = <24000000>; + AVDD-supply = <&camera0_2v8_pwr>; + DOVDD-supply = <&camera0_1v8_pwr>; + DVDD-supply = <&camera0_1v5_pwr>; + /* GPIO-J on the Dragonboard Dual-Leopard OV5640 board */ + powerdown-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; + /* GPIO-I on the Dragonboard Dual-Leopard OV5640 board */ + reset-gpios = <&gpioa 12 GPIO_ACTIVE_LOW>; + rotation = <180>; + status = "okay"; + + port { + ov5640_0: endpoint { + remote-endpoint = <&stmipi_0>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + }; +}; + +&stmipi { + status = "okay"; +}; + +&stmipi_0 { + data-lanes = <1 2>; + remote-endpoint = <&ov5640_0>; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-spi2-eeprom-x6.dtso b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-spi2-eeprom-x6.dtso new file mode 100644 index 000000000000..acfd25c5bbcd --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-avenger96-overlay-spi2-eeprom-x6.dtso @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&spi2 { + pinctrl-names = "default"; + pinctrl-0 = <&spi2_pins_a>; + status = "okay"; + cs-gpios = <&gpioi 0 0>; + #address-cells = <1>; + #size-cells = <0>; + + eeprom@0 { + compatible = "microchip,25aa010a", "atmel,at25"; + reg = <0>; + address-width = <8>; + pagesize = <16>; + size = <128>; + spi-max-frequency = <5000000>; + }; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-drc02-overlay-wifi-rsi.dtso b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-drc02-overlay-wifi-rsi.dtso new file mode 100644 index 000000000000..aa79f95906f8 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-drc02-overlay-wifi-rsi.dtso @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&sdmmc3 { + broken-cd; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-overlay-panel-dpi-ch101olhlwh.dtsi b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-overlay-panel-dpi-ch101olhlwh.dtsi new file mode 100644 index 000000000000..be9eb1e11ecd --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-overlay-panel-dpi-ch101olhlwh.dtsi @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021 Marek Vasut + */ +/dts-v1/; +/plugin/; + +#include "stm32mp15xx-dhcom-overlay-panel-dpi.dtsi" + +&{/} { + lvds-encoder { + compatible = "onnn,fin3385", "lvds-encoder"; + pclk-sample = <1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + lvds_bridge_in: endpoint { + remote-endpoint = <<dc_dpi_out>; + }; + }; + + port@1 { + reg = <1>; + + lvds_bridge_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; +}; + +&display_bl { + pwms = <&pwm2 3 5000000 0>; +}; + +&i2c5 { + #address-cells = <1>; + #size-cells = <0>; + + ili251x@41 { + compatible = "ilitek,ili251x"; + reg = <0x41>; + interrupt-parent = <&gpioi>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpiod 6 GPIO_ACTIVE_LOW>; + touchscreen-size-x = <16384>; + touchscreen-size-y = <9600>; + touchscreen-inverted-x; + touchscreen-inverted-y; + }; + + eeprom@50 { + compatible = "atmel,24c04"; + reg = <0x50>; + pagesize = <16>; + }; +}; + +<dc_dpi_out { + remote-endpoint = <&lvds_bridge_in>; +}; + +&panel { + compatible = "chefree,ch101olhlwh-002"; +}; + +&panel_in { + remote-endpoint = <&lvds_bridge_out>; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-overlay-panel-dpi.dtsi b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-overlay-panel-dpi.dtsi new file mode 100644 index 000000000000..41229ec680cc --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-overlay-panel-dpi.dtsi @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021 Marek Vasut + */ +#include +#include +#include +#include + +&{/} { + display_bl: display-bl { + compatible = "pwm-backlight"; + brightness-levels = <0 16 22 30 40 55 75 102 138 188 255>; + default-brightness-level = <8>; + enable-gpios = <&gpioi 0 GPIO_ACTIVE_HIGH>; + power-supply = <®_panel_bl>; + status = "okay"; + }; + + panel: panel { + backlight = <&display_bl>; + power-supply = <®_panel_bl>; + + port { + panel_in: endpoint { + }; + }; + }; + + reg_panel_bl: regulator-panel-bl { + compatible = "regulator-fixed"; + regulator-name = "panel_backlight"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <®_panel_supply>; + }; + + reg_panel_supply: regulator-panel-supply { + compatible = "regulator-fixed"; + regulator-name = "panel_supply"; + regulator-min-microvolt = <24000000>; + regulator-max-microvolt = <24000000>; + }; +}; + +&timers2 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + pwm2: pwm { + #pwm-cells = <3>; + pinctrl-0 = <&pwm2_pins_a>; + pinctrl-names = "default"; + status = "okay"; + }; + + timer@1 { + reg = <1>; + status = "okay"; + }; +}; + +<dc { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <<dc_pins_b>; + pinctrl-1 = <<dc_sleep_pins_b>; + status = "okay"; + + port { + ltdc_dpi_out: endpoint { + }; + }; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-460-200-x11.dtso b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-460-200-x11.dtso new file mode 100644 index 000000000000..161e401f5e21 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-460-200-x11.dtso @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&fmc { + #address-cells = <2>; + #size-cells = <1>; + + sram@3,0 { + compatible = "mtd-ram"; + reg = <3 0x0 0x80000>; + bank-width = <2>; + + /* Timing values are in nS */ + st,fmc2-ebi-cs-mux-enable; + st,fmc2-ebi-cs-transaction-type = <4>; + st,fmc2-ebi-cs-buswidth = <16>; + st,fmc2-ebi-cs-address-setup-ns = <6>; + st,fmc2-ebi-cs-address-hold-ns = <6>; + st,fmc2-ebi-cs-data-setup-ns = <127>; + st,fmc2-ebi-cs-bus-turnaround-ns = <9>; + st,fmc2-ebi-cs-data-hold-ns = <9>; + }; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-497-200-x12.dtso b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-497-200-x12.dtso new file mode 100644 index 000000000000..1de244545054 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-497-200-x12.dtso @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +#include "stm32mp15xx-dhcom-overlay-panel-dpi.dtsi" + +&display_bl { + pwms = <&pwm2 3 500000 PWM_POLARITY_INVERTED>; +}; + +<dc_dpi_out { + remote-endpoint = <&panel_in>; +}; + +&panel { + compatible = "dataimage,scf0700c48ggu18"; +}; + +&panel_in { + remote-endpoint = <<dc_dpi_out>; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh.dtso b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh.dtso new file mode 100644 index 000000000000..6ef9bcf527ad --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-505-200-x12-ch101olhlwh.dtso @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +#include "stm32mp15xx-dhcom-overlay-panel-dpi-ch101olhlwh.dtsi" + +&{/} { + gpio-keys-polled { + /* BUTTON1 GPIO-B conflicts with touchscreen reset */ + button-1 { + /* Use status as /delete-node/ does not work in DTOs */ + status = "disabled"; + }; + }; + + led { + /* LED7 GPIO-H conflicts with touchscreen IRQ */ + led-2 { + /* Use status as /delete-node/ does not work in DTOs */ + status = "disabled"; + }; + }; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-531-100-x21.dtso b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-531-100-x21.dtso new file mode 100644 index 000000000000..ce291736abbb --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-531-100-x21.dtso @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&i2c5 { + #address-cells = <1>; + #size-cells = <0>; + + eeprom@56 { + compatible = "atmel,24c04"; + reg = <0x56>; + pagesize = <16>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spi1_pins_a>; + status = "okay"; + cs-gpios = <&gpioz 3 0>; + #address-cells = <1>; + #size-cells = <0>; + + eeprom@0 { + compatible = "microchip,25aa010a", "atmel,at25"; + reg = <0>; + address-width = <8>; + pagesize = <16>; + size = <128>; + spi-max-frequency = <5000000>; + }; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-531-100-x22.dtso b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-531-100-x22.dtso new file mode 100644 index 000000000000..7e040b2d8f24 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-531-100-x22.dtso @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&i2c2 { + #address-cells = <1>; + #size-cells = <0>; + + eeprom@56 { + compatible = "atmel,24c04"; + reg = <0x56>; + pagesize = <16>; + }; +}; + +/* SPI2 is not connected on STM32MP1 DHCOM SoM */ diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-560-200-x12.dtso b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-560-200-x12.dtso new file mode 100644 index 000000000000..a5cef9ba7dd2 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-560-200-x12.dtso @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +#include "stm32mp15xx-dhcom-overlay-panel-dpi.dtsi" + +&{/} { + gpio-keys { + /* + * The EXTi IRQ line 6 is shared with touchscreen IRQ, + * so operate button-1 as polled GPIO key. + */ + button-1 { + /* Use status as /delete-node/ does not work in DTOs */ + status = "disabled"; + }; + }; + + gpio-keys-polled { + button-1 { + label = "TA2-GPIO-B"; + linux,code = ; + gpios = <&gpiod 6 GPIO_ACTIVE_LOW>; + }; + }; + + led { + /* LED5 GPIO-E conflicts with touchscreen IRQ */ + led-0 { + /* Use status as /delete-node/ does not work in DTOs */ + status = "disabled"; + }; + }; +}; + +&display_bl { + pwms = <&pwm2 3 500000 PWM_POLARITY_INVERTED>; +}; + +&i2c5 { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen@38 { + compatible = "edt,edt-ft5406"; + reg = <0x38>; + /* Touchscreen IRQ GPIO-E conflicts with LED5 GPIO */ + interrupt-parent = <&gpioc>; + interrupts = <6 IRQ_TYPE_EDGE_FALLING>; /* GPIO E */ + }; +}; + +<dc_dpi_out { + remote-endpoint = <&panel_in>; +}; + +&panel { + compatible = "edt,etm0700g0edh6"; +}; + +&panel_in { + remote-endpoint = <<dc_dpi_out>; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-638-100-x12-rpi7inch.dtso b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-638-100-x12-rpi7inch.dtso new file mode 100644 index 000000000000..ee8a2d1a7b87 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-638-100-x12-rpi7inch.dtso @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&i2c5 { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen: touchscreen@38 { + }; + + attiny: regulator@45 { + }; +}; + +#include "stm32mp15xx-dhsom-overlay-panel-dsi-rpi7inch.dtsi" + +<dc { + status = "okay"; + port { + ltdc_ep_out: endpoint { + remote-endpoint = <&dsi_in>; + }; + }; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-672-100-x18.dtso b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-672-100-x18.dtso new file mode 100644 index 000000000000..41e473986189 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2-overlay-672-100-x18.dtso @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +&m_can2 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&m_can2_pins_a>; + pinctrl-1 = <&m_can2_sleep_pins_a>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2.dtsi b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2.dtsi index 5c77202ee196..0075d9391181 100644 --- a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-pdk2.dtsi @@ -13,15 +13,6 @@ clock-frequency = <24000000>; }; - display_bl: display-bl { - compatible = "pwm-backlight"; - pwms = <&pwm2 3 500000 PWM_POLARITY_INVERTED>; - brightness-levels = <0 16 22 30 40 55 75 102 138 188 255>; - default-brightness-level = <8>; - enable-gpios = <&gpioi 0 GPIO_ACTIVE_HIGH>; - power-supply = <®_panel_bl>; - }; - gpio-keys-polled { compatible = "gpio-keys-polled"; poll-interval = <20>; @@ -75,7 +66,6 @@ label = "green:led5"; gpios = <&gpioc 6 GPIO_ACTIVE_HIGH>; default-state = "off"; - status = "disabled"; }; led-1 { @@ -97,33 +87,6 @@ }; }; - panel { - compatible = "edt,etm0700g0edh6"; - backlight = <&display_bl>; - power-supply = <®_panel_bl>; - - port { - lcd_panel_in: endpoint { - remote-endpoint = <&lcd_display_out>; - }; - }; - }; - - reg_panel_bl: regulator-panel-bl { - compatible = "regulator-fixed"; - regulator-name = "panel_backlight"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - vin-supply = <®_panel_supply>; - }; - - reg_panel_supply: regulator-panel-supply { - compatible = "regulator-fixed"; - regulator-name = "panel_supply"; - regulator-min-microvolt = <24000000>; - regulator-max-microvolt = <24000000>; - }; - sound { compatible = "audio-graph-card"; widgets = "Headphone", "Headphone Jack", @@ -188,26 +151,6 @@ }; }; - - touchscreen@38 { - compatible = "edt,edt-ft5406"; - reg = <0x38>; - interrupt-parent = <&gpioc>; - interrupts = <6 IRQ_TYPE_EDGE_FALLING>; /* GPIO E */ - }; -}; - -<dc { - pinctrl-names = "default", "sleep"; - pinctrl-0 = <<dc_pins_b>; - pinctrl-1 = <<dc_sleep_pins_b>; - status = "okay"; - - port { - lcd_display_out: endpoint { - remote-endpoint = <&lcd_panel_in>; - }; - }; }; &sai2 { @@ -259,21 +202,6 @@ }; }; -&timers2 { - /* spare dmas for other usage (un-delete to enable pwm capture) */ - /delete-property/dmas; - /delete-property/dma-names; - status = "okay"; - pwm2: pwm { - pinctrl-0 = <&pwm2_pins_a>; - pinctrl-names = "default"; - status = "okay"; - }; - timer@1 { - status = "okay"; - }; -}; - &usart3 { pinctrl-names = "default"; pinctrl-0 = <&usart3_pins_a>; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-picoitx-overlay-548-200-x2-mi0700s4t-6.dtso b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-picoitx-overlay-548-200-x2-mi0700s4t-6.dtso new file mode 100644 index 000000000000..c462c6a08833 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-picoitx-overlay-548-200-x2-mi0700s4t-6.dtso @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021 Andreas Geisreiter + */ +/dts-v1/; +/plugin/; + +#include "stm32mp15xx-dhcom-overlay-panel-dpi.dtsi" + +&display_bl { + pwms = <&pwm2 3 10000000 0>; +}; + +&i2c5 { + #address-cells = <1>; + #size-cells = <0>; + + eeprom@50 { + compatible = "atmel,24c04"; + reg = <0x50>; + pagesize = <16>; + }; +}; + +<dc_dpi_out { + remote-endpoint = <&panel_in>; +}; + +&panel { + compatible = "multi-inno,mi0700s4t-6"; +}; + +&panel_in { + remote-endpoint = <<dc_dpi_out>; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-picoitx-overlay-553-100-x2-tst043015cmhx.dtso b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-picoitx-overlay-553-100-x2-tst043015cmhx.dtso new file mode 100644 index 000000000000..06338b7f7b67 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-picoitx-overlay-553-100-x2-tst043015cmhx.dtso @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2023 Marek Vasut + */ +/dts-v1/; +/plugin/; + +#include "stm32mp15xx-dhcom-overlay-panel-dpi.dtsi" + +&display_bl { + pwms = <&pwm2 3 10000000 0>; +}; + +&i2c5 { + #address-cells = <1>; + #size-cells = <0>; + + eeprom@50 { + compatible = "atmel,24c04"; + reg = <0x50>; + pagesize = <16>; + }; +}; + +<dc_dpi_out { + remote-endpoint = <&panel_in>; +}; + +&panel { + compatible = "team-source-display,tst043015cmhx"; +}; + +&panel_in { + remote-endpoint = <<dc_dpi_out>; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh.dtso b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh.dtso new file mode 100644 index 000000000000..bf5c1f6eece0 --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcom-picoitx-overlay-626-100-x2-ch101olhlwh.dtso @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2020 Marek Vasut + */ +/dts-v1/; +/plugin/; + +#include "stm32mp15xx-dhcom-overlay-panel-dpi-ch101olhlwh.dtsi" diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhcor-avenger96.dtsi b/arch/arm/boot/dts/st/stm32mp15xx-dhcor-avenger96.dtsi index aceeff6c38ba..85d93ddfa12a 100644 --- a/arch/arm/boot/dts/st/stm32mp15xx-dhcor-avenger96.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhcor-avenger96.dtsi @@ -258,15 +258,9 @@ &i2c4 { stmipi: stmipi@14 { compatible = "st,st-mipid02"; - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&mco1_pins_a>; - pinctrl-1 = <&mco1_sleep_pins_a>; reg = <0x14>; clocks = <&rcc CK_MCO1>; clock-names = "xclk"; - assigned-clocks = <&rcc CK_MCO1>; - assigned-clock-parents = <&rcc CK_HSE>; - assigned-clock-rates = <24000000>; VDDE-supply = <&v1v8>; VDDIN-supply = <&v1v8>; reset-gpios = <&gpioz 0 GPIO_ACTIVE_LOW>; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dhsom-overlay-panel-dsi-rpi7inch.dtsi b/arch/arm/boot/dts/st/stm32mp15xx-dhsom-overlay-panel-dsi-rpi7inch.dtsi new file mode 100644 index 000000000000..518c269a1dba --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-dhsom-overlay-panel-dsi-rpi7inch.dtsi @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021 Marek Vasut + */ +#include + +&{/} { + panel { + compatible = "powertip,ph800480t013-idf02"; + backlight = <&attiny>; + power-supply = <&attiny>; + + port { + panel_in: endpoint { + remote-endpoint = <&bridge_out>; + }; + }; + }; +}; + +&attiny { + compatible = "raspberrypi,7inch-touchscreen-panel-regulator"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x45>; +}; + +&dsi { + #address-cells = <1>; + #size-cells = <0>; + + phy-dsi-supply = <®18>; + status = "okay"; + + bridge@0 { + compatible = "toshiba,tc358762"; + reg = <0>; + reset-gpios = <&attiny 0 GPIO_ACTIVE_HIGH>; + vddc-supply = <&attiny>; + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + bridge_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + + port@1 { + reg = <1>; + bridge_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi_in: endpoint { + remote-endpoint = <<dc_ep_out>; + }; + }; + + port@1 { + reg = <1>; + dsi_out: endpoint { + remote-endpoint = <&bridge_in>; + }; + }; + }; +}; + +&touchscreen { + compatible = "edt,edt-ft5406"; + reg = <0x38>; + reset-gpios = <&attiny 1 GPIO_ACTIVE_LOW>; + /* + * Disabled, since the IRQ line is not on + * the FPC cable, so we cannot get touch + * IRQs unless its connected otherwise. In + * that case, add entry like this one and + * enable below. + * + * interrupt-parent = <&gpiog>; + * interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + */ + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-dkx.dtsi b/arch/arm/boot/dts/st/stm32mp15xx-dkx.dtsi index 7ed2b01958fe..599ea07bdb19 100644 --- a/arch/arm/boot/dts/st/stm32mp15xx-dkx.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15xx-dkx.dtsi @@ -155,6 +155,46 @@ status = "okay"; }; +&cs_cti_trace { + status = "okay"; +}; + +&cs_cti_cpu0 { + status = "okay"; +}; + +&cs_cti_cpu1 { + status = "okay"; +}; + +&cs_etf { + status = "okay"; +}; + +&cs_etm0 { + status = "okay"; +}; + +&cs_etm1 { + status = "okay"; +}; + +&cs_funnel { + status = "okay"; +}; + +&cs_stm { + status = "okay"; +}; + +&cs_tpiu { + status = "okay"; +}; + +&dbg_bus { + status = "okay"; +}; + &dts { status = "okay"; }; diff --git a/arch/arm/boot/dts/st/stm32mp15xx-phyboard-sargas.dtsi b/arch/arm/boot/dts/st/stm32mp15xx-phyboard-sargas.dtsi new file mode 100644 index 000000000000..f7e7aa9b916a --- /dev/null +++ b/arch/arm/boot/dts/st/stm32mp15xx-phyboard-sargas.dtsi @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2022-2023 Steffen Trumtrar + * Copyright (C) Phytec GmbH 2019-2020 - All Rights Reserved + * Author: Dom VOVARD . + */ + +#include +#include +#include +#include + +/ { + aliases { + mmc0 = &sdmmc1; + mmc1 = &sdmmc2; + mmc2 = &sdmmc3; + serial0 = &uart4; + serial1 = &usart3; + serial2 = &usart1; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + gpio-keys { + compatible = "gpio-keys"; + + key-home { + label = "Home"; + gpios = <&gpioa 13 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + key-enter { + label = "Enter"; + gpios = <&gpioa 14 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + sound { + compatible = "audio-graph-card"; + label = "STM32MP1-PHYCORE"; + routing = + "Playback", "MCLK", /* Set a route between "MCLK" and "playback" widgets */ + "Capture", "MCLK"; + dais = <&sai2b_port>, + <&sai2a_port>; + }; +}; + +&dcmi { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&dcmi_pins_d>; + pinctrl-1 = <&dcmi_sleep_pins_d>; +}; + +&i2c1 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c1_pins_b>; + pinctrl-1 = <&i2c1_sleep_pins_b>; + i2c-scl-rising-time-ns = <100>; + i2c-scl-falling-time-ns = <7>; + status = "okay"; + + codec@18 { + compatible = "ti,tlv320aic3007"; + reg = <0x18>; + #sound-dai-cells = <0>; + + ai3x-micbias-vg = <2>; + + AVDD-supply = <&v3v3>; + IOVDD-supply = <&v3v3>; + DRVDD-supply = <&v3v3>; + DVDD-supply = <&v1v8_audio>; + + clocks = <&sai2b>; + + port { + #address-cells = <1>; + #size-cells = <0>; + + tlv320_tx_endpoint: endpoint@0 { + reg = <0>; + remote-endpoint = <&sai2b_endpoint>; + frame-master; + bitclock-master; + }; + + tlv320_rx_endpoint: endpoint@1 { + reg = <1>; + remote-endpoint = <&sai2a_endpoint>; + frame-master; + bitclock-master; + }; + }; + }; + + touch@44 { + compatible = "st,stmpe811"; + reg = <0x44>; + interrupts = <3 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpioi>; + vio-supply = <&v3v3>; + vcc-supply = <&v3v3>; + st,sample-time = <4>; + st,mod-12b = <1>; + st,ref-sel = <0>; + st,adc-freq = <1>; + + touchscreen { + compatible = "st,stmpe-ts"; + st,ave-ctrl = <1>; + st,touch-det-delay = <2>; + st,settling = <2>; + st,fraction-z = <7>; + st,i-drive = <1>; + }; + }; + + leds@62 { + compatible = "nxp,pca9533"; + reg = <0x62>; + + led-0 { + color = ; + function = LED_FUNCTION_POWER; + type = ; + }; + + led-1 { + color = ; + function = LED_FUNCTION_POWER; + type = ; + }; + + led-2 { + color = ; + function = LED_FUNCTION_HEARTBEAT; + type = ; + linux,default-trigger = "heartbeat"; + }; + }; +}; + +<dc { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <<dc_pins_f>; + pinctrl-1 = <<dc_sleep_pins_f>; +}; + +&m_can2 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&m_can2_pins_a>; + pinctrl-1 = <&m_can2_sleep_pins_a>; + status = "okay"; +}; + +&sai2 { + clocks = <&rcc SAI2>, <&rcc PLL3_Q>, <&rcc PLL3_R>; + clock-names = "pclk", "x8k", "x11k"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&sai2a_pins_d>, <&sai2b_pins_d>; + pinctrl-1 = <&sai2a_sleep_pins_d>, <&sai2b_sleep_pins_d>; + status = "okay"; +}; + +&sai2a { + dma-names = "rx"; + clocks = <&rcc SAI2_K>, <&sai2b>; + clock-names = "sai_ck", "MCLK"; + #clock-cells = <0>; + st,sync = <&sai2b 2>; + + sai2a_port: port { + sai2a_endpoint: endpoint { + remote-endpoint = <&tlv320_rx_endpoint>; + mclk-fs = <256>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <16>; + }; + }; +}; + +&sai2b { + dma-names = "tx"; + #clock-cells = <0>; + + sai2b_port: port { + sai2b_endpoint: endpoint { + remote-endpoint = <&tlv320_tx_endpoint>; + mclk-fs = <256>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <16>; + }; + }; +}; + +&sdmmc1 { + pinctrl-names = "default", "opendrain", "sleep"; + pinctrl-0 = <&sdmmc1_b4_pins_b>; + pinctrl-1 = <&sdmmc1_b4_od_pins_b>; + pinctrl-2 = <&sdmmc1_b4_sleep_pins_b>; + cd-gpios = <&gpiof 3 GPIO_ACTIVE_LOW>; + disable-wp; + bus-width = <4>; + vmmc-supply = <&v3v3>; + st,neg-edge; + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&spi1_pins_a>; + pinctrl-1 = <&spi1_sleep_pins_a>; + cs-gpios = <&gpioz 3 0>; + status = "okay"; +}; + +&timers5 { + /* spare dmas for other usage */ + /delete-property/dmas; + /delete-property/dma-names; + pwm5: pwm { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pwm5_pins_c>; + pinctrl-1 = <&pwm5_sleep_pins_c>; + }; +}; + +&uart4 { + pinctrl-names = "default", "sleep", "idle"; + pinctrl-0 = <&uart4_pins_f>; + pinctrl-1 = <&uart4_sleep_pins_f>; + pinctrl-2 = <&uart4_idle_pins_f>; + /delete-property/dmas; + /delete-property/dma-names; + status = "okay"; +}; + +&usart1 { + pinctrl-names = "default", "sleep", "idle"; + pinctrl-0 = <&usart1_pins_b &usart1_pins_a>; + pinctrl-1 = <&usart1_sleep_pins_b &usart1_sleep_pins_a>; + pinctrl-2 = <&usart1_idle_pins_b &usart1_idle_pins_a>; + uart-has-rtscts; + status = "okay"; +}; + +&usart3 { + pinctrl-names = "default", "sleep", "idle"; + pinctrl-0 = <&usart3_pins_a>; + pinctrl-1 = <&usart3_sleep_pins_a>; + pinctrl-2 = <&usart3_idle_pins_a>; + status = "okay"; +}; + +&usbh_ehci { + status = "okay"; +}; + +&usbh_ohci { + status = "okay"; +}; + +&usbotg_hs { + phys = <&usbphyc_port1 0>; + phy-names = "usb2-phy"; + status = "okay"; +}; + +&usbphyc { + status = "okay"; +}; + +&usbphyc_port0 { + phy-supply = <&vdd_usb>; +}; + +&usbphyc_port1 { + phy-supply = <&vdd_usb>; +}; diff --git a/arch/arm/boot/dts/st/stm32mp157c-phycore-stm32mp15-som.dtsi b/arch/arm/boot/dts/st/stm32mp15xx-phycore-som.dtsi similarity index 53% rename from arch/arm/boot/dts/st/stm32mp157c-phycore-stm32mp15-som.dtsi rename to arch/arm/boot/dts/st/stm32mp15xx-phycore-som.dtsi index 370b2afbf15b..126446c4198f 100644 --- a/arch/arm/boot/dts/st/stm32mp157c-phycore-stm32mp15-som.dtsi +++ b/arch/arm/boot/dts/st/stm32mp15xx-phycore-som.dtsi @@ -1,23 +1,20 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (C) 2022-2023 Steffen Trumtrar * Copyright (C) Phytec GmbH 2019-2020 - All Rights Reserved - * Author: Dom VOVARD . + * Author: Dom VOVARD + * Copyright (C) 2022-2023 Steffen Trumtrar + * Copyright (C) 2024 PHYTEC Messtechnik GmbH + * Author: Christophe Parant */ #include #include -#include -#include -#include -#include #include #include #include "stm32mp15-pinctrl.dtsi" +#include "stm32mp15xxac-pinctrl.dtsi" / { - model = "PHYTEC phyCORE-STM32MP15 SOM"; - compatible = "phytec,phycore-stm32mp157c-som", "st,stm32mp157"; aliases { ethernet0 = ðernet0; @@ -25,24 +22,13 @@ rtc1 = &rtc; }; - chosen { - stdout-path = "serial0:115200n8"; - }; - - gpio-keys { - compatible = "gpio-keys"; - - key-home { - label = "Home"; - gpios = <&gpioa 13 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - key-enter { - label = "Enter"; - gpios = <&gpioa 14 GPIO_ACTIVE_LOW>; - linux,code = ; - }; + /* + * Set the minimum memory size here and + * let the bootloader set the real size. + */ + memory@c0000000 { + device_type = "memory"; + reg = <0xc0000000 0x20000000>; }; reserved-memory { @@ -50,18 +36,6 @@ #size-cells = <1>; ranges; - retram: retram@38000000 { - compatible = "shared-dma-pool"; - reg = <0x38000000 0x10000>; - no-map; - }; - - mcuram: mcuram@30000000 { - compatible = "shared-dma-pool"; - reg = <0x30000000 0x40000>; - no-map; - }; - mcuram2: mcuram2@10000000 { compatible = "shared-dma-pool"; reg = <0x10000000 0x40000>; @@ -85,21 +59,23 @@ reg = <0x10042000 0x4000>; no-map; }; - }; - sound { - compatible = "audio-graph-card"; - label = "STM32MP1-PHYCORE"; - routing = - "Playback", "MCLK", /* Set a route between "MCLK" and "playback" widgets */ - "Capture", "MCLK"; - dais = <&sai2b_port>, - <&sai2a_port>; + mcuram: mcuram@30000000 { + compatible = "shared-dma-pool"; + reg = <0x30000000 0x40000>; + no-map; + }; + + retram: retram@38000000 { + compatible = "shared-dma-pool"; + reg = <0x38000000 0x10000>; + no-map; + }; }; regulator_vin: regulator { compatible = "regulator-fixed"; - regulator-name = "vin"; + regulator-name = "VIN"; regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; regulator-always-on; @@ -126,101 +102,28 @@ reg = <1>; interrupt-parent = <&gpiog>; interrupts = <12 IRQ_TYPE_EDGE_FALLING>; + enet-phy-lane-no-swap; ti,rx-internal-delay = ; ti,tx-internal-delay = ; ti,fifo-depth = ; ti,min-output-impedance; - enet-phy-lane-no-swap; ti,clk-output-sel = ; }; }; }; -&i2c1 { +&fmc { pinctrl-names = "default", "sleep"; - pinctrl-0 = <&i2c1_pins_b>; - pinctrl-1 = <&i2c1_sleep_pins_b>; - i2c-scl-rising-time-ns = <100>; - i2c-scl-falling-time-ns = <7>; - status = "okay"; + pinctrl-0 = <&fmc_pins_a>; + pinctrl-1 = <&fmc_sleep_pins_a>; + status = "disabled"; - codec@18 { - compatible = "ti,tlv320aic3007"; - reg = <0x18>; - #sound-dai-cells = <0>; - - ai3x-micbias-vg = <2>; - - AVDD-supply = <&v3v3>; - IOVDD-supply = <&v3v3>; - DRVDD-supply = <&v3v3>; - DVDD-supply = <&v1v8_audio>; - - clocks = <&sai2b>; - - port { - #address-cells = <1>; - #size-cells = <0>; - - tlv320_tx_endpoint: endpoint@0 { - reg = <0>; - remote-endpoint = <&sai2b_endpoint>; - frame-master; - bitclock-master; - }; - - tlv320_rx_endpoint: endpoint@1 { - reg = <1>; - remote-endpoint = <&sai2a_endpoint>; - frame-master; - bitclock-master; - }; - }; - }; - - touch@44 { - compatible = "st,stmpe811"; - reg = <0x44>; - interrupts = <3 IRQ_TYPE_EDGE_FALLING>; - interrupt-parent = <&gpioi>; - vio-supply = <&v3v3>; - vcc-supply = <&v3v3>; - st,sample-time = <4>; - st,mod-12b = <1>; - st,ref-sel = <0>; - st,adc-freq = <1>; - - touchscreen { - compatible = "st,stmpe-ts"; - st,ave-ctrl = <1>; - st,touch-det-delay = <2>; - st,settling = <2>; - st,fraction-z = <7>; - st,i-drive = <1>; - }; - }; - - leds@62 { - compatible = "nxp,pca9533"; - reg = <0x62>; - - led-0 { - color = ; - function = LED_FUNCTION_POWER; - type = ; - }; - - led-1 { - color = ; - function = LED_FUNCTION_POWER; - type = ; - }; - - led-2 { - color = ; - function = LED_FUNCTION_HEARTBEAT; - type = ; - linux,default-trigger = "heartbeat"; + nand-controller@4,0 { + nand0: nand@0 { + reg = <0>; + nand-on-flash-bbt; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; }; }; }; @@ -257,7 +160,7 @@ pwr_sw2-supply = <&bst_out>; vddcore: buck1 { - regulator-name = "vddcore"; + regulator-name = "VDD_CORE"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1350000>; regulator-always-on; @@ -265,7 +168,7 @@ }; vdd_ddr: buck2 { - regulator-name = "vdd_ddr"; + regulator-name = "VDD_DDR"; regulator-min-microvolt = <1350000>; regulator-max-microvolt = <1350000>; regulator-always-on; @@ -273,7 +176,7 @@ }; vdd: buck3 { - regulator-name = "vdd"; + regulator-name = "VDD"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; regulator-always-on; @@ -282,7 +185,7 @@ }; v3v3: buck4 { - regulator-name = "v3v3"; + regulator-name = "VDD_BUCK4"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; regulator-always-on; @@ -290,7 +193,7 @@ }; v1v8_audio: ldo1 { - regulator-name = "v1v8_audio"; + regulator-name = "VDD_LDO1"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-always-on; @@ -299,7 +202,7 @@ }; vdd_eth_2v5: ldo2 { - regulator-name = "dd_eth_2v5"; + regulator-name = "VDD_ETH_2V5"; regulator-min-microvolt = <2500000>; regulator-max-microvolt = <2500000>; regulator-always-on; @@ -308,7 +211,7 @@ }; vtt_ddr: ldo3 { - regulator-name = "vtt_ddr"; + regulator-name = "VTT_DDR"; regulator-min-microvolt = <500000>; regulator-max-microvolt = <750000>; regulator-always-on; @@ -316,12 +219,12 @@ }; vdd_usb: ldo4 { - regulator-name = "vdd_usb"; + regulator-name = "VDD_USB"; interrupts = ; }; vdda: ldo5 { - regulator-name = "vdda"; + regulator-name = "VDDA"; regulator-min-microvolt = <2900000>; regulator-max-microvolt = <2900000>; interrupts = ; @@ -329,7 +232,7 @@ }; vdd_eth_1v0: ldo6 { - regulator-name = "vdd_eth_1v0"; + regulator-name = "VDD_ETH_1V0"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; regulator-always-on; @@ -338,23 +241,23 @@ }; vref_ddr: vref_ddr { - regulator-name = "vref_ddr"; + regulator-name = "VDD_REFDDR"; regulator-always-on; }; bst_out: boost { - regulator-name = "bst_out"; + regulator-name = "BST_OUT"; interrupts = ; }; vbus_otg: pwr_sw1 { - regulator-name = "vbus_otg"; + regulator-name = "VBUS_OTG"; interrupts = ; regulator-active-discharge = <1>; }; vbus_sw: pwr_sw2 { - regulator-name = "vbus_sw"; + regulator-name = "VBUS_SW"; interrupts = ; regulator-active-discharge = <1>; }; @@ -375,14 +278,15 @@ }; i2c4_eeprom: eeprom@50 { - compatible = "microchip,24c32", - "atmel,24c32"; + compatible = "atmel,24c32"; reg = <0x50>; + status = "disabled"; }; i2c4_rtc: rtc@52 { compatible = "microcrystal,rv3028"; reg = <0x52>; + status = "disabled"; }; }; @@ -395,13 +299,6 @@ status = "okay"; }; -&m_can2 { - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&m_can2_pins_a>; - pinctrl-1 = <&m_can2_sleep_pins_a>; - status = "okay"; -}; - &m4_rproc { memory-region = <&retram>, <&mcuram>, <&mcuram2>, <&vdev0vring0>, <&vdev0vring1>, <&vdev0buffer>; @@ -419,18 +316,22 @@ &qspi { pinctrl-names = "default", "sleep"; - pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a>; - pinctrl-1 = <&qspi_clk_sleep_pins_a &qspi_bk1_sleep_pins_a>; - status = "okay"; + pinctrl-0 = <&qspi_clk_pins_a + &qspi_bk1_pins_a + &qspi_cs1_pins_a>; + pinctrl-1 = <&qspi_clk_sleep_pins_a + &qspi_bk1_sleep_pins_a + &qspi_cs1_sleep_pins_a>; + reg = <0x58003000 0x1000>, + <0x70000000 0x1000000>; + status = "disabled"; flash0: flash@0 { - compatible = "winbond,w25q128", "jedec,spi-nor"; + compatible = "jedec,spi-nor"; reg = <0>; spi-rx-bus-width = <4>; spi-max-frequency = <50000000>; m25p,fast-read; - #address-cells = <1>; - #size-cells = <1>; }; }; @@ -442,56 +343,7 @@ status = "okay"; }; -&sai2 { - clocks = <&rcc SAI2>, <&rcc PLL3_Q>, <&rcc PLL3_R>; - clock-names = "pclk", "x8k", "x11k"; - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&sai2a_pins_b>, <&sai2b_pins_d>; - pinctrl-1 = <&sai2a_sleep_pins_b>, <&sai2b_sleep_pins_d>; - status = "okay"; -}; - -&sai2a { - dma-names = "rx"; - st,sync = <&sai2b 2>; - clocks = <&rcc SAI2_K>, <&sai2b>; - clock-names = "sai_ck", "MCLK"; - #clock-cells = <0>; - - sai2a_port: port { - sai2a_endpoint: endpoint { - remote-endpoint = <&tlv320_rx_endpoint>; - mclk-fs = <256>; - dai-tdm-slot-num = <2>; - dai-tdm-slot-width = <16>; - }; - }; -}; - -&sai2b { - dma-names = "tx"; - #clock-cells = <0>; - - sai2b_port: port { - sai2b_endpoint: endpoint { - remote-endpoint = <&tlv320_tx_endpoint>; - mclk-fs = <256>; - dai-tdm-slot-num = <2>; - dai-tdm-slot-width = <16>; - }; - }; -}; - -&sdmmc1 { - pinctrl-names = "default", "opendrain", "sleep"; - pinctrl-0 = <&sdmmc1_b4_pins_b>; - pinctrl-1 = <&sdmmc1_b4_od_pins_b>; - pinctrl-2 = <&sdmmc1_b4_sleep_pins_b>; - cd-gpios = <&gpiof 3 GPIO_ACTIVE_LOW>; - disable-wp; - st,neg-edge; - bus-width = <4>; - vmmc-supply = <&v3v3>; +&dts { status = "okay"; }; @@ -503,71 +355,10 @@ non-removable; no-sd; no-sdio; - st,neg-edge; bus-width = <8>; vmmc-supply = <&v3v3>; vqmmc-supply = <&v3v3>; mmc-ddr-3_3v; -}; - -&spi1 { - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&spi1_pins_a>; - pinctrl-1 = <&spi1_sleep_pins_a>; - cs-gpios = <&gpioz 3 0>; - status = "okay"; -}; - -&uart4 { - pinctrl-names = "default", "sleep", "idle"; - pinctrl-0 = <&uart4_pins_a>; - pinctrl-1 = <&uart4_sleep_pins_a>; - pinctrl-2 = <&uart4_idle_pins_a>; - pinctrl-3 = <&uart4_pins_a>; - /delete-property/dmas; - /delete-property/dma-names; - status = "okay"; -}; - -&usart1 { - pinctrl-names = "default", "sleep", "idle"; - pinctrl-0 = <&usart1_pins_b &usart1_pins_a>; - pinctrl-1 = <&usart1_sleep_pins_b &usart1_sleep_pins_a>; - pinctrl-2 = <&usart1_idle_pins_b &usart1_idle_pins_a>; - uart-has-rtscts; - status = "okay"; -}; - -&usart3 { - pinctrl-names = "default", "sleep", "idle"; - pinctrl-0 = <&usart3_pins_a>; - pinctrl-1 = <&usart3_sleep_pins_a>; - pinctrl-2 = <&usart3_idle_pins_a>; - status = "okay"; -}; - -&usbh_ehci { - status = "okay"; -}; - -&usbh_ohci { - status = "okay"; -}; - -&usbotg_hs { - phys = <&usbphyc_port1 0>; - phy-names = "usb2-phy"; - status = "okay"; -}; - -&usbphyc { - status = "okay"; -}; - -&usbphyc_port0 { - phy-supply = <&vdd_usb>; -}; - -&usbphyc_port1 { - phy-supply = <&vdd_usb>; + st,neg-edge; + status = "disabled"; }; diff --git a/arch/arm/boot/dts/ti/omap/Makefile b/arch/arm/boot/dts/ti/omap/Makefile index 3f54b515c471..3a4d9204339b 100644 --- a/arch/arm/boot/dts/ti/omap/Makefile +++ b/arch/arm/boot/dts/ti/omap/Makefile @@ -80,8 +80,14 @@ dtb-$(CONFIG_ARCH_OMAP4) += \ omap4-sdp-es23plus.dtb \ omap4-var-dvk-om44.dtb \ omap4-var-stk-om44.dtb \ + omap4-samsung-espresso7.dtb \ + omap4-samsung-espresso10.dtb \ omap4-xyboard-mz609.dtb \ omap4-xyboard-mz617.dtb + +am335x-bonegreen-hdmi-00a0-dtbs := am335x-bonegreen-eco.dtb \ + am335x-bone-hdmi-00a0.dtbo + dtb-$(CONFIG_SOC_AM33XX) += \ am335x-baltos-ir2110.dtb \ am335x-baltos-ir3220.dtb \ @@ -93,6 +99,7 @@ dtb-$(CONFIG_SOC_AM33XX) += \ am335x-bonegreen.dtb \ am335x-bonegreen-wireless.dtb \ am335x-bonegreen-eco.dtb \ + am335x-bonegreen-hdmi-00a0.dtb \ am335x-chiliboard.dtb \ am335x-cm-t335.dtb \ am335x-evm.dtb \ @@ -174,3 +181,11 @@ dtb-$(CONFIG_SOC_TI81XX) += \ dm8148-t410.dtb \ dm8168-evm.dtb \ dra62x-j5eco-evm.dtb + +# Enable support for device-tree overlays +DTC_FLAGS_am335x-bone += -@ +DTC_FLAGS_am335x-boneblack += -@ +DTC_FLAGS_am335x-boneblack-wireless += -@ +DTC_FLAGS_am335x-bonegreen += -@ +DTC_FLAGS_am335x-bonegreen-wireless += -@ +DTC_FLAGS_am335x-bonegreen-eco += -@ diff --git a/arch/arm/boot/dts/ti/omap/am335x-bone-hdmi-00a0.dtso b/arch/arm/boot/dts/ti/omap/am335x-bone-hdmi-00a0.dtso new file mode 100644 index 000000000000..f43f44c79c96 --- /dev/null +++ b/arch/arm/boot/dts/ti/omap/am335x-bone-hdmi-00a0.dtso @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 2017 Copyright (c) Seeed Technology Inc. All right reserved. + * Author: Baozhu Zuo + * Copyright (c) Bootlin 2026 + * + * This device tree overlay is compatible with the BeagleBone Black, Green + * and their subversions. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&{/} { + hdmi0: connector-hdmi { + compatible = "hdmi-connector"; + label = "hdmi"; + type = "a"; + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&it66121_out>; + }; + }; + }; + + clk_mcasp0_fixed: clk-mcasp0-fixed { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <24576000>; + }; + + clk_mcasp0: clk-mcasp0 { + #clock-cells = <0>; + compatible = "gpio-gate-clock"; + clocks = <&clk_mcasp0_fixed>; + enable-gpios = <&gpio1 27 0>; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "TI BeagleBone Green HDMI cape"; + simple-audio-card,format = "i2s"; + simple-audio-card,bitclock-master = <&sound_master>; + simple-audio-card,frame-master = <&sound_master>; + + sound_master: simple-audio-card,cpu { + sound-dai = <&mcasp0>; + clocks = <&clk_mcasp0>; + }; + + simple-audio-card,codec { + sound-dai = <&it66121>; + }; + }; +}; + +&am33xx_pinmux { + bb_lcd_pins: pinmux-bb-lcd-pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_GPMC_ADVN_ALE, PIN_OUTPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_GPMC_OEN_REN, PIN_OUTPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_GPMC_WEN, PIN_INPUT, MUX_MODE7) + >; + }; + mcasp0_pins: mcasp0-pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKX, PIN_INPUT_PULLUP, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE2) + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_OUTPUT_PULLUP, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + >; + }; +}; + +&i2c2 { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + it66121: it66121 { + compatible = "ite,it66121"; + reg = <0x4d>; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_pins>; + + #sound-dai-cells = <0>; + + interrupt-parent = <&gpio2>; + interrupts = <4 IRQ_TYPE_EDGE_RISING>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + it66121_in: endpoint { + bus-width = <24>; + remote-endpoint = <&lcdc_0>; + }; + }; + + port@1 { + reg = <1>; + it66121_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; +}; + +&lcdc { + status = "okay"; + blue-and-red-wiring = "straight"; + port { + lcdc_0: endpoint@0 { + remote-endpoint = <&it66121_in>; + }; + }; +}; + + +&mcasp0 { + status = "okay"; + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&mcasp0_pins>; + op-mode = <0>; + tdm-slots = <2>; + serial-dir = < 0 0 1 0 >; + tx-num-evt = <32>; + rx-num-evt = <32>; +}; + diff --git a/arch/arm/boot/dts/ti/omap/dm816x.dtsi b/arch/arm/boot/dts/ti/omap/dm816x.dtsi index 407d7bc5b13a..a1e0e904e0f0 100644 --- a/arch/arm/boot/dts/ti/omap/dm816x.dtsi +++ b/arch/arm/boot/dts/ti/omap/dm816x.dtsi @@ -94,8 +94,8 @@ #address-cells = <1>; #size-cells = <0>; #pinctrl-cells = <1>; - pinctrl-single,register-width = <16>; - pinctrl-single,function-mask = <0xf>; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x1f>; }; /* Device Configuration Registers */ diff --git a/arch/arm/boot/dts/ti/omap/dra7-l4.dtsi b/arch/arm/boot/dts/ti/omap/dra7-l4.dtsi index db6c53bbaf51..c8d325b0f57b 100644 --- a/arch/arm/boot/dts/ti/omap/dra7-l4.dtsi +++ b/arch/arm/boot/dts/ti/omap/dra7-l4.dtsi @@ -327,7 +327,7 @@ ranges = <0 0 0x8000>; reg = <0x0 0x20>; - pcie1_phy: pciephy@4000 { + pcie1_phy: pcie-phy@4000 { compatible = "ti,phy-pipe3-pcie"; reg = <0x4000 0x80>, /* phy_rx */ <0x4400 0x64>; /* phy_tx */ @@ -347,7 +347,7 @@ #phy-cells = <0>; }; - pcie2_phy: pciephy@5000 { + pcie2_phy: pcie-phy@5000 { compatible = "ti,phy-pipe3-pcie"; reg = <0x5000 0x80>, /* phy_rx */ <0x5400 0x64>; /* phy_tx */ diff --git a/arch/arm/boot/dts/ti/omap/omap4-l4.dtsi b/arch/arm/boot/dts/ti/omap/omap4-l4.dtsi index 4881dd674393..4c78a0b28fab 100644 --- a/arch/arm/boot/dts/ti/omap/omap4-l4.dtsi +++ b/arch/arm/boot/dts/ti/omap/omap4-l4.dtsi @@ -72,13 +72,13 @@ #size-cells = <1>; }; - omap_control_usb2phy: control-phy@300 { + omap_control_usb2phy: phy@300 { compatible = "ti,control-phy-usb2"; reg = <0x300 0x4>; reg-names = "power"; }; - omap_control_usbotg: control-phy@33c { + omap_control_usbotg: phy@33c { compatible = "ti,control-phy-otghs"; reg = <0x33c 0x4>; reg-names = "otghs_control"; diff --git a/arch/arm/boot/dts/ti/omap/omap4-samsung-espresso-common.dtsi b/arch/arm/boot/dts/ti/omap/omap4-samsung-espresso-common.dtsi new file mode 100644 index 000000000000..06651072f000 --- /dev/null +++ b/arch/arm/boot/dts/ti/omap/omap4-samsung-espresso-common.dtsi @@ -0,0 +1,744 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/dts-v1/; +#include "dt-bindings/gpio/gpio.h" +#include +#include +#include "omap443x.dtsi" + +/ { + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x40000000>; /* 1 GB */ + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + continuous_splash: framebuffer@bef00000{ + reg = <0xbef00000 (1024 * 600 * 4)>; + no-map; + }; + }; + + chosen { + stdout-path = &uart3; + #address-cells = <1>; + }; + + i2c-gpio5 { + compatible = "i2c-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c5_pins>; + sda-gpios = <&gpio4 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio4 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <10>; + #address-cells = <1>; + #size-cells = <0>; + + /* TODO: SMB136 Charger for 7" variant at 0x4d */ + }; + + i2c-gpio6 { + compatible = "i2c-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c6_pins>; + sda-gpios = <&gpio3 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio3 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <10>; + #address-cells = <1>; + #size-cells = <0>; + + /* TODO: STMPE811 ADC at 0x41 */ + }; + + i2c-gpio7 { + compatible = "i2c-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c7_pins>; + sda-gpios = <&gpio2 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio2 29 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <3>; + #address-cells = <1>; + #size-cells = <0>; + + fuel-gauge@36 { + compatible = "maxim,max17042"; + reg = <0x36>; + pinctrl-0 = <&fuel_alert_irq>; + pinctrl-names = "default"; + interrupt-parent = <&gpio2>; + interrupts = <12 IRQ_TYPE_LEVEL_LOW>; + maxim,rsns-microohm = <10000>; + maxim,over-heat-temp = <500>; + maxim,dead-volt = <2500>; + maxim,over-volt = <4300>; + }; + }; + + reg_espresso_wlan: regulator-espresso-wlan { + pinctrl-names = "default"; + pinctrl-0 = <&wlanen_gpio>; + compatible = "regulator-fixed"; + regulator-name = "espresso_wlan"; + regulator-max-microvolt = <2000000>; + regulator-min-microvolt = <2000000>; + gpio = <&gpio4 8 GPIO_ACTIVE_HIGH>; /* GPIO_104 */ + startup-delay-us = <70000>; + regulator-always-on; + enable-active-high; + }; + + wlan_pwrseq: wlan-pwrseq { + compatible = "mmc-pwrseq-simple"; + clocks = <&twl 0>; + clock-names = "ext_clock"; + }; + + reg_espresso_internal: regulator-espresso-internal { + compatible = "regulator-fixed"; + regulator-name = "eMMC_LDO"; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>; /* GPIO_63 */ + startup-delay-us = <100000>; + regulator-boot-on; + regulator-always-on; + enable-active-high; + }; + + reg_espresso_external: regulator-espresso-external { + compatible = "regulator-fixed"; + regulator-name = "vmmc1"; + regulator-max-microvolt = <2800000>; + regulator-min-microvolt = <2800000>; + gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>; /* GPIO_34 */ + enable-active-high; + }; + + reg_touch_ldo_en: regulator-touch-ldo-en { + compatible = "regulator-fixed"; + regulator-name = "touch_ldo_en"; + regulator-max-microvolt = <2800000>; + regulator-min-microvolt = <2800000>; + gpios = <&gpio2 22 GPIO_ACTIVE_HIGH>; /* GPIO_54 */ + regulator-always-on; + enable-active-high; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&gpio_keys>; + + key-power { + label = "power"; + + gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>; /* GPIO_wk3 */ + linux,code = ; + wakeup-source; + }; + + button-volup { + linux,code = ; + label = "volume_up"; /* GPIO_wk30 */ + gpios = <&gpio1 30 GPIO_ACTIVE_LOW>; + }; + + button-voldown { + linux,code = ; + label = "volume_down"; /* GPIO_wk8 */ + gpios = <&gpio1 8 GPIO_ACTIVE_LOW>; + }; + }; + + reg_lcd: regulator-lcd { + compatible = "regulator-fixed"; + regulator-name = "lcd_en"; + gpios = <&gpio5 7 GPIO_ACTIVE_HIGH>; /* GPIO_135 */ + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + enable-active-high; + regulator-boot-on; + }; + + pwm10: pwm-10 { + pinctrl-names = "default"; + pinctrl-0 = <&pwm10_default>; + compatible = "ti,omap-dmtimer-pwm"; + #pwm-cells = <3>; + ti,timers = <&timer10>; + ti,clock-source = <0x00>; + }; + + lvds-encoder { + compatible = "doestek,dtc34lm85am", "lvds-encoder"; + powerdown-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; /* GPIO_136 */ + power-supply = <®_lcd>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + bridge_in: endpoint { + remote-endpoint = <&dpi_out>; + }; + }; + + port@1 { + reg = <1>; + bridge_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; + + vibrator { + compatible = "gpio-vibrator"; + enable-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>; /* GPIO_38 */ + pinctrl-names = "default"; + pinctrl-0 = <&vibrator_default>; + }; + + gp2a_shunt: current-sense-shunt { + compatible = "current-sense-shunt"; + io-channels = <&gpadc 4>; + shunt-resistor-micro-ohms = <24000000>; /* 24 ohms */ + #io-channel-cells = <0>; + }; + + led-ir { + compatible = "gpio-ir-tx"; + gpios = <&gpio2 27 GPIO_ACTIVE_HIGH>; /* GPIO_59 */ + pinctrl-names = "default"; + pinctrl-0 = <&ledir_pins>; + }; +}; + +&omap4_pmx_wkup { + gpio_keys: gpio-keys-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x046, PIN_INPUT | MUX_MODE3) + /* sim_cd.gpio_wk3 - EXT_WAKEUP */ + OMAP4_IOPAD(0x056, PIN_INPUT | MUX_MODE3) + /* fref_clk3_req.gpio_wk30 - VOL_UP */ + OMAP4_IOPAD(0x05C, PIN_INPUT | MUX_MODE3) + /* fref_clk4_out.gpio_wk8 - VOL_DN */ + >; + }; +}; + +&omap4_pmx_core { + backlight_pins: pinmux-backlight-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0X0D8, PIN_OUTPUT | PIN_OFF_OUTPUT_LOW | MUX_MODE3) + /* usbb1_ulpitll_dat7.gpio_95 - LED_BACKLIGHT_RESET */ + >; + }; + + bluetooth_pins: pinmux-bluetooth-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x094, PIN_OUTPUT | MUX_MODE3) + /* gpmc_ncs6.gpio_103 - BT_EN */ + OMAP4_IOPAD(0x0be, PIN_OUTPUT | MUX_MODE3) + /* cam_strobe.gpio_82 - BT_nRST */ + OMAP4_IOPAD(0x0c0, PIN_INPUT | MUX_MODE3) + /* cam_globalreset.gpio_83 - BT_HOST_WAKE */ + OMAP4_IOPAD(0x0d4, PIN_OUTPUT | MUX_MODE3) + /* usbb1_ulpitll_dat5.gpio_93 - BT_WAKE */ + >; + }; + + dss_dpi_pins: pinmux-dss-dpi-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x162, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data23 */ + OMAP4_IOPAD(0x164, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data22 */ + OMAP4_IOPAD(0x166, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data21 */ + OMAP4_IOPAD(0x168, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data20 */ + OMAP4_IOPAD(0x16a, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data19 */ + OMAP4_IOPAD(0x16c, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data18 */ + OMAP4_IOPAD(0x16e, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data15 */ + OMAP4_IOPAD(0x170, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data14 */ + OMAP4_IOPAD(0x172, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data13 */ + OMAP4_IOPAD(0x174, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data12 */ + OMAP4_IOPAD(0x176, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data11 */ + OMAP4_IOPAD(0x1b4, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data10 */ + OMAP4_IOPAD(0x1b6, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data9 */ + OMAP4_IOPAD(0x1b8, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data16 */ + OMAP4_IOPAD(0x1ba, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data17 */ + OMAP4_IOPAD(0x1bc, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_hsync */ + OMAP4_IOPAD(0x1be, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_pclk */ + OMAP4_IOPAD(0x1c0, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_vsync */ + OMAP4_IOPAD(0x1c2, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_de */ + OMAP4_IOPAD(0x1c4, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data8 */ + OMAP4_IOPAD(0x1c6, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data7 */ + OMAP4_IOPAD(0x1c8, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data6 */ + OMAP4_IOPAD(0x1ca, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data5 */ + OMAP4_IOPAD(0x1cc, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data4 */ + OMAP4_IOPAD(0x1ce, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data3 */ + + OMAP4_IOPAD(0x1d0, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data2 */ + OMAP4_IOPAD(0x1d2, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data1 */ + OMAP4_IOPAD(0x1d4, PIN_OFF_OUTPUT_LOW | MUX_MODE5) + /* dispc2_data0 */ + >; + }; + + fuel_alert_irq: pinmux-fuel-alert-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x068, PIN_INPUT_PULLUP | MUX_MODE3) + /* gpmc_a20.gpio_44 */ + >; + }; + + i2c1_pins: pinmux-i2c1-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x122, PIN_INPUT_PULLUP | MUX_MODE0) + /* i2c1_scl */ + OMAP4_IOPAD(0x124, PIN_INPUT_PULLUP | MUX_MODE0) + /* i2c1_sda */ + >; + }; + + i2c2_pins: pinmux-i2c2-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x126, PIN_INPUT_PULLUP | MUX_MODE0) + /* i2c2_scl */ + OMAP4_IOPAD(0x128, PIN_INPUT_PULLUP | MUX_MODE0) + /* i2c2_sda */ + >; + }; + + i2c3_pins: pinmux-i2c3-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x12a, PIN_INPUT_PULLUP | MUX_MODE0) + /* i2c3_scl */ + OMAP4_IOPAD(0x12c, PIN_INPUT_PULLUP | MUX_MODE0) + /* i2c3_sda */ + >; + }; + + i2c4_pins: pinmux-i2c4-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x12e, PIN_INPUT_PULLUP | MUX_MODE0) + /* i2c4_scl */ + OMAP4_IOPAD(0x130, PIN_INPUT_PULLUP | MUX_MODE0) + /* i2c4_sda */ + >; + }; + + i2c5_pins: pinmux-i2c5-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x0de, PIN_INPUT_PULLUP | MUX_MODE3) + /* usbc1_icusb_dp.gpio_98 */ + OMAP4_IOPAD(0x0e0, PIN_INPUT_PULLUP | MUX_MODE3) + /* usbc1_icusb_dm.gpio_99 */ + >; + }; + + i2c6_pins: pinmux-i2c6-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x09c, PIN_INPUT_PULLUP | MUX_MODE3) + /* hdmi_ddc_scl.gpio_65 */ + OMAP4_IOPAD(0x09e, PIN_INPUT_PULLUP | MUX_MODE3) + /* hdmi_ddc_sda.gpio_66 */ + >; + }; + + i2c7_pins: pinmux-i2c7-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x08a, PIN_INPUT_PULLUP | MUX_MODE3) + /* gpmc_wait0.gpio_61 */ + OMAP4_IOPAD(0x08c, PIN_INPUT_PULLUP | MUX_MODE3) + /* gpmc_wait1.gpio_62 */ + >; + }; + + ledir_pins: pimux-ledir-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x086, PIN_INPUT_PULLDOWN | MUX_MODE7) + /* gpmc_nbe0_cle.gpio_59 */ + OMAP4_IOPAD(0x156, PIN_INPUT_PULLDOWN | MUX_MODE7) + /* mcspi4_simo.gpio_152 */ + >; + }; + + lvds_pins: pinmux-lvds-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0X136, PIN_OUTPUT | MUX_MODE3) + /* mcspi1_simo.gpio_136 - LVDS_nSHDN */ + >; + }; + + mmc1_pins: pinmux-mmc1-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x0e2, PIN_INPUT_PULLDOWN | MUX_MODE0) + /* sdmmc1_clk */ + OMAP4_IOPAD(0x0e4, PIN_INPUT_PULLUP | MUX_MODE0) + /* sdmcc1_cmd */ + OMAP4_IOPAD(0x0e6, PIN_INPUT_PULLUP | MUX_MODE0) + /* sdmcc1_dat0 */ + OMAP4_IOPAD(0x0e8, PIN_INPUT_PULLUP | MUX_MODE0) + /* sdmmc1_dat1 */ + OMAP4_IOPAD(0x0ea, PIN_INPUT_PULLUP | MUX_MODE0) + /* sdmmc1_dat2 */ + OMAP4_IOPAD(0x0ec, PIN_INPUT_PULLUP | MUX_MODE0) + /* sdmmc1_dat3 */ + >; + }; + + mmc2_pins: pinmux-mmc2-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x040, PIN_INPUT_PULLUP | MUX_MODE1) + /* sdmmc2_dat0 */ + OMAP4_IOPAD(0x042, PIN_INPUT_PULLUP | MUX_MODE1) + /* sdmmc2_dat1 */ + OMAP4_IOPAD(0x044, PIN_INPUT_PULLUP | MUX_MODE1) + /* sdmmc2_dat2 */ + OMAP4_IOPAD(0x046, PIN_INPUT_PULLUP | MUX_MODE1) + /* sdmmc2_dat3 */ + OMAP4_IOPAD(0x048, PIN_INPUT_PULLUP | MUX_MODE1) + /* sdmmc2_dat4 */ + OMAP4_IOPAD(0x04a, PIN_INPUT_PULLUP | MUX_MODE1) + /* sdmmc2_dat5 */ + OMAP4_IOPAD(0x04c, PIN_INPUT_PULLUP | MUX_MODE1) + /* sdmmc2_dat6 */ + OMAP4_IOPAD(0x04e, PIN_INPUT_PULLUP | MUX_MODE1) + /* sdmmc2_dat7 */ + OMAP4_IOPAD(0x082, PIN_INPUT_PULLUP | MUX_MODE1) + /* sdmmc2_clk */ + OMAP4_IOPAD(0x084, PIN_INPUT_PULLUP | MUX_MODE1) + /* sdmmc2_cmd */ + >; + }; + + mmc5_pins: pinmux-mmc5-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x148, PIN_INPUT_PULLDOWN | MUX_MODE0) + /* sdmmc5_clk.sdmmc5_clk */ + OMAP4_IOPAD(0x14a, PIN_INPUT_PULLUP | MUX_MODE0) + /* sdmmc5_cmd.sdmmc5_cmd */ + OMAP4_IOPAD(0x14c, PIN_INPUT_PULLUP | MUX_MODE0) + /* sdmmc5_dat0.sdmmc5_dat0 */ + OMAP4_IOPAD(0x14e, PIN_INPUT_PULLUP | MUX_MODE0) + /* sdmmc5_dat1.sdmmc5_dat1 */ + OMAP4_IOPAD(0x150, PIN_INPUT_PULLUP | MUX_MODE0) + /* sdmmc5_dat2.sdmmc5_dat2 */ + OMAP4_IOPAD(0x152, PIN_INPUT_PULLUP | MUX_MODE0) + /* sdmmc5_dat3.sdmmc5_dat3 */ + >; + }; + + pwm10_default: pinmux-pwm10-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0X0D6, PIN_OUTPUT | PIN_OFF_OUTPUT_LOW | MUX_MODE1) + /* usbb1_ulpitll_dat6.dmtimer10_pwm_evt - LED_BACKLIGHT_PWM */ + >; + }; + + touch_pins: pinmux-touch-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x06c, PIN_INPUT | MUX_MODE3) + /* gpmc_a22.gpio_46 - TSP_INT */ + >; + }; + + uart2_pins: pinmux-uart2-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x118, PIN_INPUT_PULLUP | MUX_MODE0) + /* uart2_cts.uart2_cts */ + OMAP4_IOPAD(0x11a, PIN_INPUT_PULLUP | MUX_MODE0) + /* uart2_rts.uart2_rts */ + OMAP4_IOPAD(0x11c, PIN_INPUT_PULLUP | MUX_MODE0) + /* uart2_rx.uart2_rx */ + OMAP4_IOPAD(0x11e, PIN_INPUT_PULLUP | MUX_MODE0) + /* uart2_tx.uart2_tx */ + >; + }; + + uart3_pins: pinmux-uart3-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x144, PIN_INPUT | MUX_MODE0) + /* uart3_rx_irrx */ + OMAP4_IOPAD(0x146, PIN_OUTPUT | MUX_MODE0) + /* uart3_tx_irtx */ + >; + }; + + vibrator_default: pinmux-vibrator-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x05c, PIN_INPUT_PULLDOWN | MUX_MODE3) + /* gpmc_ad14.gpio_38 - MOTOR_EN */ + >; + }; + + wlanen_gpio: pinmux-wlanen-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x096, PIN_OUTPUT | MUX_MODE3) + /* gpmc_ncs7.gpio_104 */ + >; + }; + + wlan_host_wake: pinmux-wlan-host-wake-pins { + pinctrl-single,pins = < + OMAP4_IOPAD(0x0bc, PIN_INPUT | MUX_MODE3) + /* cam_shutter.gpio_81 - WLAN_HOST_WAKE */ + >; + }; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins>; + + interrupts-extended = <&wakeupgen GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH + &omap4_pmx_core OMAP4_UART3_RX>; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + + clock-frequency = <400000>; + + twl: pmic@48 { + reg = <0x48>; + #clock-cells = <1>; + + pinctrl-names = "default"; + pinctrl-0 = < + &twl6030_pins + &twl6030_wkup_pins + >; + + /* SPI = 0, IRQ# = 7, 4 = active high level-sensitive */ + interrupts = ; /* IRQ_SYS_1N cascaded to gic */ + interrupt-parent = <&gic>; + system-power-controller; + }; +}; + +#include "twl6032.dtsi" +#include "twl6030_omap4.dtsi" + +&ldo1 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-always-on; + regulator-state-mem { + regulator-off-in-suspend; + }; +}; + +&ldo3 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-state-mem { + regulator-off-in-suspend; + }; +}; + +&ldo4 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-always-on; +}; + +&ldo5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&ldo6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-state-mem { + regulator-off-in-suspend; + }; +}; + +&smps4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&ldousb { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; +}; + +&i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c3_pins>; +}; + +&i2c4 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c4_pins>; + + accelerometer@18 { + compatible = "bosch,bma254"; + reg = <0x18>; + vdd-supply = <&ldo4>; + vddio-supply = <&ldo5>; + interrupt-parent = <&gpio4>; + interrupts = <25 (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING)>, + <26 (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING)>; + }; + + magnetometer@2e { + compatible = "yamaha,yas530"; + reg = <0x2e>; + vdd-supply = <&ldo4>; + iovdd-supply = <&ldo5>; + reset-gpios = <&gpio5 29 GPIO_ACTIVE_LOW>; + interrupts = <&gpio6 10 IRQ_TYPE_EDGE_RISING>; + }; + + light-sensor@44 { + compatible = "sharp,gp2ap002a00f"; + reg = <0x44>; + interrupt-parent = <&gpio1>; + interrupts = <1 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&ldo4>; + vio-supply = <&ldo4>; + io-channels = <&gp2a_shunt>; + io-channel-names = "alsout"; + sharp,proximity-far-hysteresis = /bits/ 8 <0x40>; + sharp,proximity-close-hysteresis = /bits/ 8 <0x20>; + }; +}; + +&dss { + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&dss_dpi_pins>; + + port { + dpi_out: endpoint { + remote-endpoint = <&bridge_in>; + data-lines = <24>; + }; + }; +}; + +&twl_usb_comparator { + usb-supply = <&ldousb>; +}; + +&usb_otg_hs { + interface-type = <1>; + mode = <3>; + power = <50>; +}; + +&mmc1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&mmc1_pins>; + + vmmc-supply = <®_espresso_external>; + bus-width = <4>; +}; + +&mmc2 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc2_pins>; + + vmmc-supply = <®_espresso_internal>; + ti,non-removable; + bus-width = <8>; +}; + +&mmc3 { + status = "disabled"; +}; + +&mmc4 { + status = "disabled"; +}; + +&mmc5 { + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + + non-removable; + bus-width = <4>; + vmmc-supply = <®_espresso_wlan>; + mmc-pwrseq = <&wlan_pwrseq>; + + pinctrl-names = "default"; + pinctrl-0 = <&mmc5_pins>; + + brcmf: wifi@1 { + compatible = "brcm,bcm4330-fmac", "brcm,bcm4329-fmac"; + reg = <1>; + + interrupt-parent = <&gpio3>; + interrupts = <17 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "host-wake"; + pinctrl-names = "default"; + pinctrl-0 = <&wlan_host_wake>; + }; +}; + +&uart2 { + interrupts-extended = <&wakeupgen GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH + &omap4_pmx_core OMAP4_UART2_RX>; + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + bluetooth { + compatible = "brcm,bcm4330-bt"; + pinctrl-names = "default"; + pinctrl-0 = <&bluetooth_pins>; + shutdown-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio3 18 GPIO_ACTIVE_LOW>; + device-wakeup-gpios = <&gpio3 29 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&gpio3>; + interrupts = <19 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "host-wakeup"; + }; +}; diff --git a/arch/arm/boot/dts/ti/omap/omap4-samsung-espresso10.dts b/arch/arm/boot/dts/ti/omap/omap4-samsung-espresso10.dts new file mode 100644 index 000000000000..558dc3c029e9 --- /dev/null +++ b/arch/arm/boot/dts/ti/omap/omap4-samsung-espresso10.dts @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/dts-v1/; + +#include "omap4-samsung-espresso-common.dtsi" +#include +/ { + model = "Samsung Galaxy Tab 2 (10 inch)"; + compatible = "samsung,espresso10", "ti,omap4430", "ti,omap4"; + + i2c-gpio5 { + smb347: charger@6 { + compatible = "summit,smb347"; + reg = <0x6>; // 0x0C >> 1 + interrupt-parent = <&gpio2>; + interrupts = <0 IRQ_TYPE_EDGE_BOTH>; + + summit,enable-usb-charging; + summit,enable-charge-control = ; + summit,chip-temperature-threshold-celsius = <120>; + summit,usb-current-limit-microamp = <1800000>; + }; + }; + + backlight: backlight { + compatible = "pwm-backlight"; + pinctrl-names = "default"; + pinctrl-0 = <&backlight_pins>; + pwms = <&pwm10 0 1600 0>; + power-supply = <®_lcd>; + enable-gpios = <&gpio3 31 GPIO_ACTIVE_HIGH>; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <7>; + }; + + panel { + compatible = "samsung,ltn101al03", "panel-lvds"; + power-supply = <®_lcd>; + width-mm = <223>; + height-mm = <125>; + data-mapping = "vesa-24"; + backlight = <&backlight>; + + panel-timing { + clock-frequency = <69818000>; + + hback-porch = <64>; + hactive = <1280>; + hfront-porch = <16>; + hsync-len = <48>; + + vback-porch = <11>; + vactive = <800>; + vfront-porch = <16>; + vsync-len = <3>; + + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <1>; + }; + + port { + panel_in: endpoint { + remote-endpoint = <&bridge_out>; + }; + }; + }; +}; + +&i2c3 { + touchscreen@20 { + compatible = "syna,rmi4-i2c"; + reg = <0x20>; + #address-cells = <1>; + #size-cells = <0>; + + interrupt-parent = <&gpio2>; + interrupts = <14 IRQ_TYPE_EDGE_FALLING>; + + pinctrl-names = "default"; + pinctrl-0 = <&touch_pins>; + + //avdd-supply = <®_touch_ldo_en>; + vdd-supply = <&ldo6>; + + syna,reset-delay-ms = <200>; + syna,startup-delay-ms = <200>; + + rmi4-f01@1 { + reg = <0x01>; + syna,nosleep-mode = <1>; + }; + + rmi4-f11@11 { + reg = <0x11>; + touchscreen-size-x = <1280>; + touchscreen-size-y = <800>; + syna,sensor-type = <1>; + }; + }; +}; diff --git a/arch/arm/boot/dts/ti/omap/omap4-samsung-espresso7.dts b/arch/arm/boot/dts/ti/omap/omap4-samsung-espresso7.dts new file mode 100644 index 000000000000..cae37ff066c3 --- /dev/null +++ b/arch/arm/boot/dts/ti/omap/omap4-samsung-espresso7.dts @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/dts-v1/; + +#include "omap4-samsung-espresso-common.dtsi" + +/ { + model = "Samsung Galaxy Tab 2 (7 inch)"; + compatible = "samsung,espresso7", "ti,omap4430", "ti,omap4"; + + backlight: backlight { + compatible = "pwm-backlight"; + pinctrl-names = "default"; + pinctrl-0 = <&backlight_pins>; + pwms = <&pwm10 0 1200 0>; + power-supply = <®_lcd>; + enable-gpios = <&gpio3 31 GPIO_ACTIVE_HIGH>; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <2>; + }; + + panel { + compatible = "samsung,ltn070nl01", "panel-lvds"; + power-supply = <®_lcd>; + width-mm = <154>; + height-mm = <90>; + data-mapping = "vesa-24"; + backlight = <&backlight>; + + panel-timing { + clock-frequency = <47255554>; + + hback-porch = <210>; + hactive = <1024>; + hfront-porch = <186>; + hsync-len = <50>; + + vback-porch = <11>; + vactive = <600>; + vfront-porch = <24>; + vsync-len = <10>; + + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <1>; + syncclk-active = <0>; + }; + + port { + panel_in: endpoint { + remote-endpoint = <&bridge_out>; + }; + }; + }; +}; + +&i2c3 { + touchscreen@48 { + compatible = "melfas,mms136"; + reg = <0x48>; + interrupt-parent = <&gpio2>; + interrupts = <14 IRQ_TYPE_EDGE_FALLING>; + touchscreen-size-x = <1024>; + touchscreen-size-y = <600>; + pinctrl-names = "default"; + pinctrl-0 = <&touch_pins>; + avdd-supply = <®_touch_ldo_en>; + vdd-supply = <&ldo6>; + }; +}; diff --git a/arch/arm/boot/dts/ti/omap/omap5-l4.dtsi b/arch/arm/boot/dts/ti/omap/omap5-l4.dtsi index 487259132ebf..915870eb5c99 100644 --- a/arch/arm/boot/dts/ti/omap/omap5-l4.dtsi +++ b/arch/arm/boot/dts/ti/omap/omap5-l4.dtsi @@ -472,7 +472,7 @@ #phy-cells = <0>; }; - usb3_phy: usb3phy@4400 { + usb3_phy: usb3-phy@4400 { compatible = "ti,omap-usb3"; reg = <0x4400 0x80>, <0x4800 0x64>, diff --git a/arch/arm/boot/dts/ti/omap/twl6032.dtsi b/arch/arm/boot/dts/ti/omap/twl6032.dtsi new file mode 100644 index 000000000000..d599a2ca62a4 --- /dev/null +++ b/arch/arm/boot/dts/ti/omap/twl6032.dtsi @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Integrated Power Management Chip + * https://www.ti.com/lit/ds/symlink/twl6032.pdf + */ + +&twl { + compatible = "ti,twl6032"; + interrupt-controller; + #interrupt-cells = <1>; + + rtc { + compatible = "ti,twl4030-rtc"; + interrupts = <11>; + }; + + vio: regulator-vio { + compatible = "ti,twl6032-vio"; + }; + + ldo1: regulator-ldo1 { + compatible = "ti,twl6032-ldo1"; + }; + + ldo2: regulator-ldo2 { + compatible = "ti,twl6032-ldo2"; + }; + + ldo3: regulator-ldo3 { + compatible = "ti,twl6032-ldo3"; + }; + + ldo4: regulator-ldo4 { + compatible = "ti,twl6032-ldo4"; + }; + + ldo5: regulator-ldo5 { + compatible = "ti,twl6032-ldo5"; + }; + + ldo6: regulator-ldo6 { + compatible = "ti,twl6032-ldo6"; + }; + + ldoln: regulator-ldoln { + compatible = "ti,twl6032-ldoln"; + }; + + ldousb: regulator-ldousb { + compatible = "ti,twl6032-ldousb"; + }; + + smps4: regulator-smps4 { + compatible = "ti,twl6032-smps4"; + }; + + gpadc: gpadc { + compatible = "ti,twl6032-gpadc"; + interrupts = <3>; + #io-channel-cells = <1>; + }; + + twl_usb_comparator: usb-comparator { + compatible = "ti,twl6030-usb"; + interrupts = <4>, <10>; + }; + + twl_pwm: pwm { + compatible = "ti,twl6030-pwm"; + #pwm-cells = <2>; + }; + + twl_pwmled: pwmled { + compatible = "ti,twl6030-pwmled"; + #pwm-cells = <2>; + }; +}; diff --git a/arch/arm/configs/davinci_all_defconfig b/arch/arm/configs/davinci_all_defconfig index 673408a10888..72703ef0c51c 100644 --- a/arch/arm/configs/davinci_all_defconfig +++ b/arch/arm/configs/davinci_all_defconfig @@ -148,7 +148,6 @@ CONFIG_DRM_TINYDRM=m CONFIG_TINYDRM_ST7586=m CONFIG_FB=y CONFIG_FB_DA8XX=y -CONFIG_FIRMWARE_EDID=y CONFIG_BACKLIGHT_PWM=m CONFIG_BACKLIGHT_GPIO=m CONFIG_FRAMEBUFFER_CONSOLE=y diff --git a/arch/arm/configs/ixp4xx_defconfig b/arch/arm/configs/ixp4xx_defconfig index 81199dddcde7..01d72580bcc5 100644 --- a/arch/arm/configs/ixp4xx_defconfig +++ b/arch/arm/configs/ixp4xx_defconfig @@ -52,9 +52,6 @@ CONFIG_IP_NF_MANGLE=m CONFIG_IP_NF_ARPTABLES=m CONFIG_IP_NF_ARPFILTER=m CONFIG_ATM=y -CONFIG_ATM_CLIP=y -CONFIG_ATM_LANE=m -CONFIG_ATM_MPOA=m CONFIG_ATM_BR2684=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m @@ -108,7 +105,6 @@ CONFIG_ATA=y CONFIG_PATA_IXP4XX_CF=y CONFIG_NETDEVICES=y CONFIG_DUMMY=y -CONFIG_ATM_TCP=m CONFIG_IXP4XX_ETH=y CONFIG_WAN=y CONFIG_HDLC=y diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 70d05f74049e..bcc9aabc1202 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -155,6 +155,7 @@ CONFIG_QRTR_SMD=m CONFIG_CAN=y CONFIG_BT=m CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIUART_BCM=y CONFIG_BT_MRVL=m CONFIG_BT_MRVL_SDIO=m @@ -309,6 +310,11 @@ CONFIG_MWIFIEX=m CONFIG_MWIFIEX_SDIO=m CONFIG_RT2X00=m CONFIG_RT2800USB=m +CONFIG_WL1251=m +CONFIG_WL1251_SDIO=m +CONFIG_WL12XX=m +CONFIG_WL18XX=m +CONFIG_WLCORE_SDIO=m CONFIG_INPUT_JOYDEV=y CONFIG_INPUT_EVDEV=y CONFIG_KEYBOARD_QT1070=m @@ -317,8 +323,10 @@ CONFIG_KEYBOARD_TEGRA=y CONFIG_KEYBOARD_PXA27x=m CONFIG_KEYBOARD_SAMSUNG=m CONFIG_KEYBOARD_ST_KEYSCAN=y +CONFIG_KEYBOARD_OMAP4=m CONFIG_KEYBOARD_SPEAR=y CONFIG_KEYBOARD_TM2_TOUCHKEY=m +CONFIG_KEYBOARD_TWL4030=m CONFIG_KEYBOARD_CROS_EC=m CONFIG_MOUSE_PS2_ELANTECH=y CONFIG_MOUSE_CYAPA=m @@ -341,6 +349,8 @@ CONFIG_INPUT_GPIO_DECODER=m CONFIG_INPUT_CPCAP_PWRBUTTON=m CONFIG_INPUT_TPS65219_PWRBUTTON=m CONFIG_INPUT_AXP20X_PEK=m +CONFIG_INPUT_TWL4030_PWRBUTTON=m +CONFIG_INPUT_TWL4030_VIBRA=m CONFIG_INPUT_DA9063_ONKEY=m CONFIG_INPUT_ADXL34X=m CONFIG_INPUT_STPMIC1_ONKEY=y @@ -520,6 +530,8 @@ CONFIG_AXP20X_POWER=m CONFIG_BATTERY_MAX17040=m CONFIG_BATTERY_MAX17042=m CONFIG_CHARGER_CPCAP=m +CONFIG_CHARGER_TWL4030=m +CONFIG_CHARGER_TWL6030=m CONFIG_CHARGER_GPIO=m CONFIG_CHARGER_MAX14577=m CONFIG_CHARGER_MAX77693=m @@ -548,6 +560,8 @@ CONFIG_ARMADA_THERMAL=y CONFIG_BCM2711_THERMAL=m CONFIG_BCM2835_THERMAL=m CONFIG_BRCMSTB_THERMAL=m +CONFIG_OMAP4_THERMAL=y +CONFIG_OMAP5_THERMAL=y CONFIG_ST_THERMAL_MEMMAP=y CONFIG_RCAR_THERMAL=y CONFIG_TEGRA_SOCTHERM=m @@ -562,10 +576,12 @@ CONFIG_AT91SAM9X_WATCHDOG=y CONFIG_SAMA5D4_WATCHDOG=y CONFIG_S3C2410_WATCHDOG=m CONFIG_DW_WATCHDOG=y +CONFIG_OMAP_WATCHDOG=y CONFIG_DAVINCI_WATCHDOG=m CONFIG_ORION_WATCHDOG=y CONFIG_RN5T618_WATCHDOG=y CONFIG_SUNXI_WATCHDOG=y +CONFIG_TWL4030_WATCHDOG=m CONFIG_IMX2_WDT=y CONFIG_ST_LPC_WATCHDOG=y CONFIG_TEGRA_WATCHDOG=m @@ -734,6 +750,7 @@ CONFIG_DRM_RCAR_DU=m CONFIG_DRM_SUN4I=m CONFIG_DRM_OMAP=m CONFIG_OMAP5_DSS_HDMI=y +CONFIG_DRM_TILCDC=m CONFIG_DRM_MSM=m CONFIG_DRM_FSL_DCU=m CONFIG_DRM_TEGRA=y @@ -750,6 +767,7 @@ CONFIG_DRM_PANEL_EDP=y CONFIG_DRM_PANEL_SIMPLE=y CONFIG_DRM_DISPLAY_CONNECTOR=m CONFIG_DRM_I2C_NXP_TDA998X=m +CONFIG_DRM_ITE_IT66121=m CONFIG_DRM_LVDS_CODEC=m CONFIG_DRM_NXP_PTN3460=m CONFIG_DRM_PARADE_PS8622=m @@ -844,6 +862,9 @@ CONFIG_SND_SOC_TEGRA_TRIMSLICE=m CONFIG_SND_SOC_TEGRA_ALC5632=m CONFIG_SND_SOC_TEGRA_MAX98090=m CONFIG_SND_SOC_DAVINCI_MCASP=m +CONFIG_SND_SOC_OMAP_DMIC=m +CONFIG_SND_SOC_OMAP_MCPDM=m +CONFIG_SND_SOC_OMAP3_TWL4030=m CONFIG_SND_SOC_AC97_CODEC=m CONFIG_SND_SOC_AK4642=m CONFIG_SND_SOC_CPCAP=m @@ -986,6 +1007,7 @@ CONFIG_LEDS_PCA9532=m CONFIG_LEDS_PCA9532_GPIO=y CONFIG_LEDS_GPIO=y CONFIG_LEDS_PWM=y +CONFIG_LEDS_TCA6507=m CONFIG_LEDS_MAX8997=m CONFIG_LEDS_ACER_A500=m CONFIG_LEDS_TRIGGERS=y @@ -1087,6 +1109,7 @@ CONFIG_COMMON_CLK_RK808=m CONFIG_COMMON_CLK_SCMI=y CONFIG_COMMON_CLK_LAN966X=y CONFIG_COMMON_CLK_S2MPS11=m +CONFIG_CLK_TWL=m CONFIG_CLK_RASPBERRYPI=y CONFIG_COMMON_CLK_QCOM=y CONFIG_QCOM_A53PLL=y @@ -1141,10 +1164,6 @@ CONFIG_QCOM_SOCINFO=m CONFIG_QCOM_STATS=m CONFIG_QCOM_WCNSS_CTRL=m CONFIG_ROCKCHIP_IODOMAIN=y -CONFIG_ARCH_TEGRA_2x_SOC=y -CONFIG_ARCH_TEGRA_3x_SOC=y -CONFIG_ARCH_TEGRA_114_SOC=y -CONFIG_ARCH_TEGRA_124_SOC=y CONFIG_SOC_TI=y CONFIG_KEYSTONE_NAVIGATOR_QMSS=y CONFIG_KEYSTONE_NAVIGATOR_DMA=y @@ -1182,6 +1201,8 @@ CONFIG_STM32_ADC_CORE=m CONFIG_STM32_ADC=m CONFIG_STM32_DFSDM_ADC=m CONFIG_STMPE_ADC=m +CONFIG_TWL4030_MADC=m +CONFIG_TWL6030_GPADC=m CONFIG_VF610_ADC=m CONFIG_XILINX_XADC=y CONFIG_IIO_CROS_EC_SENSORS_CORE=m @@ -1204,6 +1225,7 @@ CONFIG_PWM_BCM2835=y CONFIG_PWM_BRCMSTB=m CONFIG_PWM_FSL_FTM=m CONFIG_PWM_MESON=m +CONFIG_PWM_OMAP_DMTIMER=m CONFIG_PWM_RENESAS_RCAR=m CONFIG_PWM_RENESAS_TPU=y CONFIG_PWM_ROCKCHIP=m @@ -1214,6 +1236,8 @@ CONFIG_PWM_STM32_LP=m CONFIG_PWM_SUN4I=y CONFIG_PWM_TEGRA=y CONFIG_PWM_TIECAP=m +CONFIG_PWM_TWL=m +CONFIG_PWM_TWL_LED=m CONFIG_PWM_VT8500=y CONFIG_KEYSTONE_IRQ=y CONFIG_RESET_MCHP_SPARX5=y diff --git a/arch/arm/configs/neponset_defconfig b/arch/arm/configs/neponset_defconfig index 4d720001c12e..8a5dcca743fc 100644 --- a/arch/arm/configs/neponset_defconfig +++ b/arch/arm/configs/neponset_defconfig @@ -40,7 +40,6 @@ CONFIG_BLK_DEV_SD=m CONFIG_NETDEVICES=y CONFIG_NET_VENDOR_SMC=y CONFIG_PCMCIA_PCNET=y -CONFIG_SMC9194=y CONFIG_SMC91X=y CONFIG_NET_PCMCIA=y # CONFIG_INPUT_MOUSE is not set diff --git a/arch/arm/configs/omap1_defconfig b/arch/arm/configs/omap1_defconfig index df88763fc7c3..c6155f101fc9 100644 --- a/arch/arm/configs/omap1_defconfig +++ b/arch/arm/configs/omap1_defconfig @@ -136,7 +136,6 @@ CONFIG_FB_OMAP_LCDC_EXTERNAL=y CONFIG_FB_OMAP_LCDC_HWA742=y CONFIG_FB_OMAP_MANUAL_UPDATE=y CONFIG_FB_OMAP_LCD_MIPID=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_MODE_HELPERS=y CONFIG_LCD_CLASS_DEVICE=y CONFIG_FRAMEBUFFER_CONSOLE=y diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig index ae2883d3ff0e..ad5ae1636dee 100644 --- a/arch/arm/configs/omap2plus_defconfig +++ b/arch/arm/configs/omap2plus_defconfig @@ -486,6 +486,7 @@ CONFIG_VIDEO_OMAP3=m CONFIG_VIDEO_MT9P031=m CONFIG_VIDEO_TVP5150=m CONFIG_DRM=m +CONFIG_DRM_ITE_IT66121=m CONFIG_DRM_OMAP=m CONFIG_OMAP5_DSS_HDMI=y CONFIG_OMAP2_DSS_SDI=y @@ -505,7 +506,6 @@ CONFIG_DRM_SIMPLE_BRIDGE=m CONFIG_DRM_TI_TFP410=m CONFIG_DRM_TI_TPD12S015=m CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_MODE_HELPERS=y CONFIG_FB_TILEBLITTING=y CONFIG_LCD_CLASS_DEVICE=y diff --git a/arch/arm/configs/pxa_defconfig b/arch/arm/configs/pxa_defconfig index eacd08fd87ad..c51ae373ca88 100644 --- a/arch/arm/configs/pxa_defconfig +++ b/arch/arm/configs/pxa_defconfig @@ -391,7 +391,6 @@ CONFIG_LCD_CORGI=m CONFIG_LCD_PLATFORM=m CONFIG_BACKLIGHT_PWM=m CONFIG_FRAMEBUFFER_CONSOLE=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_TILEBLITTING=y CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y CONFIG_LOGO=y diff --git a/arch/arm/configs/sama7_defconfig b/arch/arm/configs/sama7_defconfig index e2ad9a05566f..e52f671ccec4 100644 --- a/arch/arm/configs/sama7_defconfig +++ b/arch/arm/configs/sama7_defconfig @@ -108,6 +108,8 @@ CONFIG_CAN_M_CAN_PLATFORM=y CONFIG_INPUT_EVDEV=y CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=y CONFIG_LEGACY_PTY_COUNT=4 CONFIG_SERIAL_ATMEL=y CONFIG_SERIAL_ATMEL_CONSOLE=y @@ -133,6 +135,7 @@ CONFIG_GENERIC_ADC_THERMAL=y CONFIG_WATCHDOG=y CONFIG_SAMA5D4_WATCHDOG=y CONFIG_MFD_ATMEL_FLEXCOM=y +CONFIG_MFD_ATMEL_HLCDC=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_MCP16502=y @@ -147,6 +150,12 @@ CONFIG_VIDEO_MICROCHIP_CSI2DC=y CONFIG_VIDEO_IMX219=m CONFIG_VIDEO_IMX274=m CONFIG_VIDEO_OV5647=m +CONFIG_DRM=y +CONFIG_DRM_ATMEL_HLCDC=y +CONFIG_DRM_MICROCHIP_LVDS_SERIALIZER=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_SOC=y @@ -197,6 +206,7 @@ CONFIG_AT91_SAMA5D2_ADC=y CONFIG_PAC1934=m CONFIG_PWM=y CONFIG_PWM_ATMEL=y +CONFIG_PWM_ATMEL_HLCDC_PWM=y CONFIG_MCHP_EIC=y CONFIG_RESET_CONTROLLER=y CONFIG_NVMEM_MICROCHIP_OTPC=y diff --git a/arch/arm/configs/shmobile_defconfig b/arch/arm/configs/shmobile_defconfig index ef487eab17cc..6f9696e9fe17 100644 --- a/arch/arm/configs/shmobile_defconfig +++ b/arch/arm/configs/shmobile_defconfig @@ -140,17 +140,17 @@ CONFIG_VIDEO_ADV7604_CEC=y CONFIG_VIDEO_ML86V7667=y CONFIG_DRM=y CONFIG_DRM_FBDEV_EMULATION=y -CONFIG_DRM_RCAR_DU=y -# CONFIG_DRM_RCAR_USE_MIPI_DSI is not set -CONFIG_DRM_SHMOBILE=y -CONFIG_DRM_PANEL_EDP=y -CONFIG_DRM_PANEL_SIMPLE=y CONFIG_DRM_DISPLAY_CONNECTOR=y CONFIG_DRM_LVDS_CODEC=y CONFIG_DRM_SII902X=y CONFIG_DRM_SIMPLE_BRIDGE=y CONFIG_DRM_I2C_ADV7511=y CONFIG_DRM_I2C_ADV7511_AUDIO=y +CONFIG_DRM_PANEL_EDP=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_RCAR_DU=y +# CONFIG_DRM_RCAR_USE_MIPI_DSI is not set +CONFIG_DRM_SHMOBILE=y CONFIG_FB_DEVICE=y CONFIG_BACKLIGHT_PWM=y CONFIG_BACKLIGHT_AS3711=y @@ -217,7 +217,6 @@ CONFIG_TMPFS=y CONFIG_NFS_FS=y CONFIG_NFS_V3_ACL=y CONFIG_NFS_V4=y -CONFIG_NFS_V4_1=y CONFIG_ROOT_NFS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig index 68aedaf92667..66157e9721ef 100644 --- a/arch/arm/configs/tegra_defconfig +++ b/arch/arm/configs/tegra_defconfig @@ -296,10 +296,6 @@ CONFIG_CROS_EC=y CONFIG_CROS_EC_I2C=m CONFIG_CROS_EC_SPI=m CONFIG_TEGRA_IOMMU_SMMU=y -CONFIG_ARCH_TEGRA_2x_SOC=y -CONFIG_ARCH_TEGRA_3x_SOC=y -CONFIG_ARCH_TEGRA_114_SOC=y -CONFIG_ARCH_TEGRA_124_SOC=y CONFIG_ARM_TEGRA_DEVFREQ=y CONFIG_MEMORY=y CONFIG_IIO=y diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig index e88533b78327..2fe25f5eb9a5 100644 --- a/arch/arm/configs/u8500_defconfig +++ b/arch/arm/configs/u8500_defconfig @@ -37,7 +37,6 @@ CONFIG_CFG80211=y CONFIG_CFG80211_DEBUGFS=y CONFIG_MAC80211=y CONFIG_MAC80211_LEDS=y -CONFIG_CAIF=y CONFIG_NFC=m CONFIG_NFC_HCI=m CONFIG_NFC_SHDLC=y @@ -148,7 +147,6 @@ CONFIG_RTC_DRV_PL031=y CONFIG_DMADEVICES=y CONFIG_STE_DMA40=y CONFIG_HWSPINLOCK=y -CONFIG_HSEM_U8500=y CONFIG_EXTCON_FSA9480=y CONFIG_IIO=y CONFIG_IIO_SW_TRIGGER=y diff --git a/arch/arm/kernel/module-plts.c b/arch/arm/kernel/module-plts.c index 354ce16d83cb..b5338fe59706 100644 --- a/arch/arm/kernel/module-plts.c +++ b/arch/arm/kernel/module-plts.c @@ -225,6 +225,18 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, mod->arch.init.plt = s; else if (s->sh_type == SHT_SYMTAB) syms = (Elf32_Sym *)s->sh_addr; +#if defined(CONFIG_ARM_UNWIND) && !defined(CONFIG_VMSPLIT_3G) + else if (s->sh_type == ELF_SECTION_UNWIND || + (strncmp(".ARM.extab", secstrings + s->sh_name, 10) == 0)) { + /* + * To avoid the possible relocation out of range issue for + * R_ARM_PREL31, mark unwind section .ARM.extab and .ARM.exidx as + * executable so they will be allocated along with .text section to + * meet +/-1GB range requirement of the R_ARM_PREL31 relocation + */ + s->sh_flags |= SHF_EXECINSTR; + } +#endif } if (!mod->arch.core.plt || !mod->arch.init.plt) { diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 0ca5aae1bcc3..9295055cdfc9 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -39,9 +39,4 @@ endif $(obj)/csumpartialcopy.o: $(obj)/csumpartialcopygeneric.S $(obj)/csumpartialcopyuser.o: $(obj)/csumpartialcopygeneric.S -ifeq ($(CONFIG_KERNEL_MODE_NEON),y) - CFLAGS_xor-neon.o += $(CC_FLAGS_FPU) - obj-$(CONFIG_XOR_BLOCKS) += xor-neon.o -endif - obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c index a5e22678e27b..e9b0ed07bb90 100644 --- a/arch/arm/mach-exynos/firmware.c +++ b/arch/arm/mach-exynos/firmware.c @@ -61,10 +61,10 @@ static int exynos_cpu_boot(int cpu) * Exynos3250 doesn't need to send smc command for secondary CPU boot * because Exynos3250 removes WFE in secure mode. * - * On Exynos5 devices the call is ignored by trustzone firmware. + * On most Exynos5 devices the call is ignored by trustzone firmware. */ if (!soc_is_exynos4210() && !soc_is_exynos4212() && - !soc_is_exynos4412()) + !soc_is_exynos4412() && !of_machine_is_compatible("google,manta")) return 0; /* diff --git a/arch/arm/mach-omap1/clock_data.c b/arch/arm/mach-omap1/clock_data.c index c58d200e4816..5203b047deac 100644 --- a/arch/arm/mach-omap1/clock_data.c +++ b/arch/arm/mach-omap1/clock_data.c @@ -700,8 +700,8 @@ int __init omap1_clk_init(void) /* Make sure UART clocks are enabled early */ if (cpu_is_omap16xx()) omap_writel(omap_readl(MOD_CONF_CTRL_0) | - CONF_MOD_UART1_CLK_MODE_R | - CONF_MOD_UART3_CLK_MODE_R, MOD_CONF_CTRL_0); + (1 << CONF_MOD_UART1_CLK_MODE_R) | + (1 << CONF_MOD_UART3_CLK_MODE_R), MOD_CONF_CTRL_0); #endif /* USB_REQ_EN will be disabled later if necessary (usb_dc_ck) */ diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 821727eefd5a..f3f19bcfca2c 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -42,7 +42,6 @@ config ARCH_OMAP4 select PM if CPU_IDLE select ARM_ERRATA_754322 select ARM_ERRATA_775420 - select OMAP_INTERCONNECT config SOC_OMAP5 bool "TI OMAP5" diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c index 43f5944850e6..cf2bfb447ee2 100644 --- a/arch/arm/mach-omap2/id.c +++ b/arch/arm/mach-omap2/id.c @@ -17,6 +17,7 @@ #include #include #include +#include #ifdef CONFIG_SOC_BUS #include @@ -250,7 +251,7 @@ static void __init omap3_cpuinfo(void) cpu_name = "OMAP3503"; } - scnprintf(soc_name, sizeof(soc_name), "%s", cpu_name); + strscpy(soc_name, cpu_name); /* Print verbose information */ n += scnprintf(buf, sizeof(buf) - n, "%s %s (", soc_name, soc_rev); diff --git a/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c index 4277ba5b3ae0..747a96c58df1 100644 --- a/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c +++ b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c @@ -141,7 +141,7 @@ static struct notifier_block regulator_quirk_nb = { static int __init rcar_gen2_regulator_quirk(void) { struct regulator_quirk *quirk, *pos, *tmp; - struct of_phandle_args *argsa, *argsb; + struct of_phandle_args *args; const struct of_device_id *id; struct device_node *np; u32 mon, addr; @@ -171,14 +171,14 @@ static int __init rcar_gen2_regulator_quirk(void) goto err_mem; } - argsa = &quirk->irq_args; + args = &quirk->irq_args; memcpy(&quirk->i2c_msg, id->data, sizeof(quirk->i2c_msg)); quirk->id = id; quirk->np = of_node_get(np); quirk->i2c_msg.addr = addr; - ret = of_irq_parse_one(np, 0, argsa); + ret = of_irq_parse_one(np, 0, args); if (ret) { /* Skip invalid entry and continue */ of_node_put(np); kfree(quirk); @@ -186,15 +186,7 @@ static int __init rcar_gen2_regulator_quirk(void) } list_for_each_entry(pos, &quirk_list, list) { - argsb = &pos->irq_args; - - if (argsa->args_count != argsb->args_count) - continue; - - ret = memcmp(argsa->args, argsb->args, - argsa->args_count * - sizeof(argsa->args[0])); - if (!ret) { + if (of_phandle_args_equal(args, &pos->irq_args)) { pos->shared = true; quirk->shared = true; } diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index a2bb55bc0081..9e3abb14fbc1 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -15,5 +15,3 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += pm-tegra30.o obj-$(CONFIG_ARCH_TEGRA_124_SOC) += pm-tegra30.o - -obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-paz00.o diff --git a/arch/arm/mach-tegra/board-paz00.c b/arch/arm/mach-tegra/board-paz00.c deleted file mode 100644 index 3ec810b6f1a7..000000000000 --- a/arch/arm/mach-tegra/board-paz00.c +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * arch/arm/mach-tegra/board-paz00.c - * - * Copyright (C) 2011 Marc Dietrich - * - * Based on board-harmony.c - * Copyright (C) 2010 Google, Inc. - */ - -#include -#include -#include -#include -#include -#include - -#include "board.h" - -static const struct software_node tegra_gpiochip_node = { - .name = "tegra-gpio", -}; - -static const struct property_entry wifi_rfkill_prop[] __initconst = { - PROPERTY_ENTRY_STRING("name", "wifi_rfkill"), - PROPERTY_ENTRY_STRING("type", "wlan"), - PROPERTY_ENTRY_GPIO("reset-gpios", - &tegra_gpiochip_node, 25, GPIO_ACTIVE_HIGH), - PROPERTY_ENTRY_GPIO("shutdown-gpios", - &tegra_gpiochip_node, 85, GPIO_ACTIVE_HIGH), - { } -}; - -static const struct platform_device_info wifi_rfkill_info __initconst = { - .name = "rfkill_gpio", - .id = PLATFORM_DEVID_NONE, - .properties = wifi_rfkill_prop, -}; - -void __init tegra_paz00_wifikill_init(void) -{ - struct platform_device *pd; - int err; - - err = software_node_register(&tegra_gpiochip_node); - if (err) { - pr_err("failed to register %s node: %d\n", - tegra_gpiochip_node.name, err); - return; - } - - pd = platform_device_register_full(&wifi_rfkill_info); - err = PTR_ERR_OR_ZERO(pd); - if (err) - pr_err("failed to register WiFi rfkill device: %d\n", err); -} diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index 7b3ef0dc9be1..86c3ea0d6b30 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -19,6 +19,4 @@ void __init tegra_map_common_io(void); void __init tegra_init_irq(void); -void __init tegra_paz00_wifikill_init(void); - #endif diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index 9ef1dfa7b926..f324a7e491d8 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -83,10 +83,6 @@ static void __init tegra_dt_init(void) static void __init tegra_dt_init_late(void) { - if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && - of_machine_is_compatible("compal,paz00")) - tegra_paz00_wifikill_init(); - if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && of_machine_is_compatible("nvidia,tegra20")) platform_device_register_simple("tegra20-cpufreq", -1, NULL, 0); diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index ed4330cc3f4e..e62cc4be5adf 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -115,32 +115,6 @@ static inline bool is_write_fault(unsigned int fsr) return (fsr & FSR_WRITE) && !(fsr & FSR_CM); } -static inline bool is_translation_fault(unsigned int fsr) -{ - int fs = fsr_fs(fsr); -#ifdef CONFIG_ARM_LPAE - if ((fs & FS_MMU_NOLL_MASK) == FS_TRANS_NOLL) - return true; -#else - if (fs == FS_L1_TRANS || fs == FS_L2_TRANS) - return true; -#endif - return false; -} - -static inline bool is_permission_fault(unsigned int fsr) -{ - int fs = fsr_fs(fsr); -#ifdef CONFIG_ARM_LPAE - if ((fs & FS_MMU_NOLL_MASK) == FS_PERM_NOLL) - return true; -#else - if (fs == FS_L1_PERM || fs == FS_L2_PERM) - return true; -#endif - return false; -} - static void die_kernel_fault(const char *msg, struct mm_struct *mm, unsigned long addr, unsigned int fsr, struct pt_regs *regs) @@ -190,7 +164,8 @@ __do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, /* * Something tried to access memory that isn't in our memory map.. - * User mode accesses just cause a SIGSEGV + * User mode accesses just cause a SIGSEGV. Ensure interrupts are enabled + * for preempt RT. */ static void __do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig, @@ -198,6 +173,8 @@ __do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig, { struct task_struct *tsk = current; + local_irq_enable(); + #ifdef CONFIG_DEBUG_USER if (((user_debug & UDBG_SEGV) && (sig == SIGSEGV)) || ((user_debug & UDBG_BUS) && (sig == SIGBUS))) { @@ -258,6 +235,70 @@ static inline bool ttbr0_usermode_access_allowed(struct pt_regs *regs) } #endif +/* + * Handle a vmalloc fault, copying the non-leaf page table entries from + * init_mm.pgd. Any kernel context can trigger this, so we must not sleep + * or enable interrupts. Having two CPUs execute this for the same page is + * no problem, we'll just copy the same data twice. + * + * Returns false on failure. + */ +static bool __kprobes __maybe_unused vmalloc_fault(unsigned long addr) +{ + unsigned int index; + pgd_t *pgd, *pgd_k; + p4d_t *p4d, *p4d_k; + pud_t *pud, *pud_k; + pmd_t *pmd, *pmd_k; + + index = pgd_index(addr); + + pgd = cpu_get_pgd() + index; + pgd_k = init_mm.pgd + index; + + p4d = p4d_offset(pgd, addr); + p4d_k = p4d_offset(pgd_k, addr); + + if (p4d_none(*p4d_k)) + return false; + if (!p4d_present(*p4d)) + set_p4d(p4d, *p4d_k); + + pud = pud_offset(p4d, addr); + pud_k = pud_offset(p4d_k, addr); + + if (pud_none(*pud_k)) + return false; + if (!pud_present(*pud)) + set_pud(pud, *pud_k); + + pmd = pmd_offset(pud, addr); + pmd_k = pmd_offset(pud_k, addr); + +#ifdef CONFIG_ARM_LPAE + /* + * Only one hardware entry per PMD with LPAE. + */ + index = 0; +#else + /* + * On ARM one Linux PGD entry contains two hardware entries (see page + * tables layout in pgtable.h). We normally guarantee that we always + * fill both L1 entries. But create_mapping() doesn't follow the rule. + * It can create inidividual L1 entries, so here we have to call + * pmd_none() check for the entry really corresponded to address, not + * for the first of pair. + */ + index = (addr >> SECTION_SHIFT) & 1; +#endif + if (pmd_none(pmd_k[index])) + return false; + + copy_pmd(pmd, pmd_k); + + return true; +} + static int __kprobes do_kernel_address_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, struct pt_regs *regs) @@ -268,6 +309,7 @@ do_kernel_address_page_fault(struct mm_struct *mm, unsigned long addr, * should not be faulting in kernel space, which includes the * vector/khelper page. Handle the branch predictor hardening * while interrupts are still disabled, then send a SIGSEGV. + * Note that __do_user_fault() will enable interrupts. */ harden_branch_predictor(); __do_user_fault(addr, fsr, SIGSEGV, SEGV_MAPERR, regs); @@ -492,10 +534,9 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) * directly to do_kernel_address_page_fault() to handle. * * Otherwise, we're probably faulting in the vmalloc() area, so try to fix - * that up. Note that we must not take any locks or enable interrupts in - * this case. + * that up via vmalloc_fault(). * - * If vmalloc() fixup fails, that means the non-leaf page tables did not + * If vmalloc_fault() fails, that means the non-leaf page tables did not * contain an entry for this address, so handle this via * do_kernel_address_page_fault(). */ @@ -504,65 +545,12 @@ static int __kprobes do_translation_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { - unsigned int index; - pgd_t *pgd, *pgd_k; - p4d_t *p4d, *p4d_k; - pud_t *pud, *pud_k; - pmd_t *pmd, *pmd_k; - if (addr < TASK_SIZE) return do_page_fault(addr, fsr, regs); - if (user_mode(regs)) - goto bad_area; + if (!user_mode(regs) && vmalloc_fault(addr)) + return 0; - index = pgd_index(addr); - - pgd = cpu_get_pgd() + index; - pgd_k = init_mm.pgd + index; - - p4d = p4d_offset(pgd, addr); - p4d_k = p4d_offset(pgd_k, addr); - - if (p4d_none(*p4d_k)) - goto bad_area; - if (!p4d_present(*p4d)) - set_p4d(p4d, *p4d_k); - - pud = pud_offset(p4d, addr); - pud_k = pud_offset(p4d_k, addr); - - if (pud_none(*pud_k)) - goto bad_area; - if (!pud_present(*pud)) - set_pud(pud, *pud_k); - - pmd = pmd_offset(pud, addr); - pmd_k = pmd_offset(pud_k, addr); - -#ifdef CONFIG_ARM_LPAE - /* - * Only one hardware entry per PMD with LPAE. - */ - index = 0; -#else - /* - * On ARM one Linux PGD entry contains two hardware entries (see page - * tables layout in pgtable.h). We normally guarantee that we always - * fill both L1 entries. But create_mapping() doesn't follow the rule. - * It can create inidividual L1 entries, so here we have to call - * pmd_none() check for the entry really corresponded to address, not - * for the first of pair. - */ - index = (addr >> SECTION_SHIFT) & 1; -#endif - if (pmd_none(pmd_k[index])) - goto bad_area; - - copy_pmd(pmd, pmd_k); - return 0; - -bad_area: do_kernel_address_page_fault(current->mm, addr, fsr, regs); return 0; diff --git a/arch/arm/mm/fault.h b/arch/arm/mm/fault.h index e8f8c1902544..207f1b06941d 100644 --- a/arch/arm/mm/fault.h +++ b/arch/arm/mm/fault.h @@ -5,12 +5,9 @@ /* * Fault status register encodings. We steal bit 31 for our own purposes. */ -#define FSR_LNX_PF (1 << 31) -#define FSR_CM (1 << 13) -#define FSR_WRITE (1 << 11) -#define FSR_FS4 (1 << 10) -#define FSR_FS3_0 (15) -#define FSR_FS5_0 (0x3f) +#define FSR_LNX_PF BIT(31) +#define FSR_CM BIT(13) +#define FSR_WRITE BIT(11) #ifdef CONFIG_ARM_LPAE #define FSR_FS_AEA 17 @@ -18,10 +15,26 @@ #define FS_PERM_NOLL 0xC #define FS_MMU_NOLL_MASK 0x3C +#define FSR_FS5_0 GENMASK(5, 0) + static inline int fsr_fs(unsigned int fsr) { return fsr & FSR_FS5_0; } + +static inline bool is_translation_fault(unsigned int fsr) +{ + int fs = fsr_fs(fsr); + + return (fs & FS_MMU_NOLL_MASK) == FS_TRANS_NOLL; +} + +static inline bool is_permission_fault(unsigned int fsr) +{ + int fs = fsr_fs(fsr); + + return (fs & FS_MMU_NOLL_MASK) == FS_PERM_NOLL; +} #else #define FSR_FS_AEA 22 #define FS_L1_TRANS 0x5 @@ -29,10 +42,27 @@ static inline int fsr_fs(unsigned int fsr) #define FS_L1_PERM 0xD #define FS_L2_PERM 0xF +#define FSR_FS4 BIT(10) +#define FSR_FS3_0 GENMASK(3, 0) + static inline int fsr_fs(unsigned int fsr) { return (fsr & FSR_FS3_0) | (fsr & FSR_FS4) >> 6; } + +static inline bool is_translation_fault(unsigned int fsr) +{ + int fs = fsr_fs(fsr); + + return fs == FS_L1_TRANS || fs == FS_L2_TRANS; +} + +static inline bool is_permission_fault(unsigned int fsr) +{ + int fs = fsr_fs(fsr); + + return fs == FS_L1_PERM || fs == FS_L2_PERM; +} #endif void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs); diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index 19470d938b23..4d7ef5cc36b6 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -304,8 +304,10 @@ void __sync_icache_dcache(pte_t pteval) else mapping = NULL; - if (!test_and_set_bit(PG_dcache_clean, &folio->flags.f)) + if (!test_bit(PG_dcache_clean, &folio->flags.f)) { __flush_dcache_folio(mapping, folio); + set_bit(PG_dcache_clean, &folio->flags.f); + } if (pte_exec(pteval)) __flush_icache_all(); diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c index deeb8f292454..9ede81afbc50 100644 --- a/arch/arm/net/bpf_jit_32.c +++ b/arch/arm/net/bpf_jit_32.c @@ -1852,6 +1852,9 @@ exit: { u64 val = (u32)imm | (u64)insn[1].imm << 32; + if (insn->src_reg == BPF_PSEUDO_FUNC) + goto notyet; + emit_a32_mov_i64(dst, val, ctx); return 1; @@ -2055,6 +2058,9 @@ go_jmp: const s8 *r5 = bpf2a32[BPF_REG_5]; const u32 func = (u32)__bpf_call_base + (u32)imm; + if (insn->src_reg == BPF_PSEUDO_CALL) + goto notyet; + emit_a32_mov_r64(true, r0, r1, ctx); emit_a32_mov_r64(true, r1, r2, ctx); emit_push_r64(r5, ctx); @@ -2142,11 +2148,9 @@ bool bpf_jit_needs_zext(void) return true; } -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) { - struct bpf_prog *tmp, *orig_prog = prog; struct bpf_binary_header *header; - bool tmp_blinded = false; struct jit_ctx ctx; unsigned int tmp_idx; unsigned int image_size; @@ -2156,20 +2160,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) * the interpreter. */ if (!prog->jit_requested) - return orig_prog; - - /* If constant blinding was enabled and we failed during blinding - * then we must fall back to the interpreter. Otherwise, we save - * the new JITed code. - */ - tmp = bpf_jit_blind_constants(prog); - - if (IS_ERR(tmp)) - return orig_prog; - if (tmp != prog) { - tmp_blinded = true; - prog = tmp; - } + return prog; memset(&ctx, 0, sizeof(ctx)); ctx.prog = prog; @@ -2179,10 +2170,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) * we must fall back to the interpreter */ ctx.offsets = kcalloc(prog->len, sizeof(int), GFP_KERNEL); - if (ctx.offsets == NULL) { - prog = orig_prog; - goto out; - } + if (ctx.offsets == NULL) + return prog; /* 1) fake pass to find in the length of the JITed code, * to compute ctx->offsets and other context variables @@ -2194,10 +2183,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) * being successful in the second pass, so just fall back * to the interpreter. */ - if (build_body(&ctx)) { - prog = orig_prog; + if (build_body(&ctx)) goto out_off; - } tmp_idx = ctx.idx; build_prologue(&ctx); @@ -2213,10 +2200,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) ctx.idx += ctx.imm_count; if (ctx.imm_count) { ctx.imms = kcalloc(ctx.imm_count, sizeof(u32), GFP_KERNEL); - if (ctx.imms == NULL) { - prog = orig_prog; + if (ctx.imms == NULL) goto out_off; - } } #else /* there's nothing about the epilogue on ARMv7 */ @@ -2238,10 +2223,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) /* Not able to allocate memory for the structure then * we must fall back to the interpretation */ - if (header == NULL) { - prog = orig_prog; + if (header == NULL) goto out_imms; - } /* 2.) Actual pass to generate final JIT code */ ctx.target = (u32 *) image_ptr; @@ -2278,16 +2261,12 @@ out_imms: #endif out_off: kfree(ctx.offsets); -out: - if (tmp_blinded) - bpf_jit_prog_release_other(prog, prog == orig_prog ? - tmp : orig_prog); + return prog; out_free: image_ptr = NULL; bpf_jit_binary_free(header); - prog = orig_prog; goto out_imms; } diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 94bf7cc43063..fe60738e5943 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -54,6 +54,7 @@ config ARM64 select ARCH_HAS_STRICT_MODULE_RWX select ARCH_HAS_SYNC_DMA_FOR_DEVICE select ARCH_HAS_SYNC_DMA_FOR_CPU + select ARCH_HAS_BATCHED_DMA_SYNC select ARCH_HAS_SYSCALL_WRAPPER select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_HAS_ZONE_DMA_SET if EXPERT @@ -1141,6 +1142,18 @@ config ARM64_ERRATUM_4311569 If unsure, say Y. +config ARM64_ERRATUM_4193714 + bool "C1-Pro: 4193714: SME DVMSync early acknowledgement" + depends on ARM64_SME + default y + help + Enable workaround for C1-Pro acknowledging the DVMSync before + the SME memory accesses are complete. This will cause TLB + maintenance for processes using SME to also issue an IPI to + the affected CPUs. + + If unsure, say Y. + config CAVIUM_ERRATUM_22375 bool "Cavium erratum 22375, 24313" default y diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index 54eb1d7fd419..72c812e76b0b 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -188,16 +188,15 @@ config ARCH_MESON This enables support for the arm64 based Amlogic SoCs such as the s905, S905X/D, S912, A113X/D or S905X/D2 -menu "Microchip SoC support" +menuconfig ARCH_MICROCHIP + bool "Microchip SoC support" -config ARCH_MICROCHIP - bool +if ARCH_MICROCHIP config ARCH_LAN969X bool "Microchip LAN969X SoC family" select PINCTRL select DW_APB_TIMER_OF - select ARCH_MICROCHIP help This enables support for the Microchip LAN969X ARMv8-based SoC family of TSN-capable gigabit switches. @@ -212,7 +211,6 @@ config ARCH_SPARX5 bool "Microchip Sparx5 SoC family" select PINCTRL select DW_APB_TIMER_OF - select ARCH_MICROCHIP help This enables support for the Microchip Sparx5 ARMv8-based SoC family of TSN-capable gigabit switches. @@ -223,7 +221,7 @@ config ARCH_SPARX5 security through TCAM-based frame processing using versatile content aware processor (VCAP). -endmenu +endif config ARCH_MMP bool "Marvell MMP SoC Family" diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile index 2edfa7bf4ab3..d116864b6c2b 100644 --- a/arch/arm64/boot/dts/allwinner/Makefile +++ b/arch/arm64/boot/dts/allwinner/Makefile @@ -38,6 +38,7 @@ dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-orangepi-lite2.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-orangepi-one-plus.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-pine-h64.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-pine-h64-model-b.dtb +dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-taiqicat-a01.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-tanix-tx6.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-tanix-tx6-mini.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h313-tanix-tx1.dtb diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index 0fecf0abb204..04a26762a19a 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -1000,6 +1000,8 @@ reg-io-width = <4>; clocks = <&ccu CLK_BUS_UART0>; resets = <&ccu RST_BUS_UART0>; + dmas = <&dma 6>, <&dma 6>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -1011,6 +1013,8 @@ reg-io-width = <4>; clocks = <&ccu CLK_BUS_UART1>; resets = <&ccu RST_BUS_UART1>; + dmas = <&dma 7>, <&dma 7>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -1022,6 +1026,8 @@ reg-io-width = <4>; clocks = <&ccu CLK_BUS_UART2>; resets = <&ccu RST_BUS_UART2>; + dmas = <&dma 8>, <&dma 8>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -1033,6 +1039,8 @@ reg-io-width = <4>; clocks = <&ccu CLK_BUS_UART3>; resets = <&ccu RST_BUS_UART3>; + dmas = <&dma 9>, <&dma 9>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -1044,6 +1052,8 @@ reg-io-width = <4>; clocks = <&ccu CLK_BUS_UART4>; resets = <&ccu RST_BUS_UART4>; + dmas = <&dma 10>, <&dma 10>; + dma-names = "tx", "rx"; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-taiqicat-a01.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-taiqicat-a01.dts new file mode 100644 index 000000000000..225f42bd3b9e --- /dev/null +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-taiqicat-a01.dts @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +// Copyright (C) 2026 Jun Yan + +/dts-v1/; + +#include "sun50i-h6.dtsi" +#include "sun50i-h6-cpu-opp.dtsi" +#include "sun50i-h6-gpu-opp.dtsi" + +#include + +/ { + model = "TaiqiCat (TQC) A01"; + compatible = "ultrapower,taiqicat-a01", "allwinner,sun50i-h6"; + + aliases { + ethernet1 = &sdio_wifi; + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + connector { + compatible = "hdmi-connector"; + ddc-en-gpios = <&pio 7 2 GPIO_ACTIVE_HIGH>; /* PH2 */ + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + + ext_osc32k: ext-osc32k-clk { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <32768>; + clock-output-names = "ext_osc32k"; + }; + + leds { + compatible = "gpio-leds"; + + led-0 { + label = "taiqicat:blue:power"; + gpios = <&r_pio 0 4 GPIO_ACTIVE_HIGH>; /* PL4 */ + default-state = "on"; + }; + }; + + reg_vcc5v: vcc5v { + /* board wide 5V supply directly from the DC jack */ + compatible = "regulator-fixed"; + regulator-name = "vcc-5v"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + sound-spdif { + compatible = "simple-audio-card"; + simple-audio-card,name = "sun50i-h6-spdif"; + + simple-audio-card,cpu { + sound-dai = <&spdif>; + }; + + simple-audio-card,codec { + sound-dai = <&spdif_out>; + }; + }; + + spdif_out: spdif-out { + #sound-dai-cells = <0>; + compatible = "linux,spdif-dit"; + }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + clocks = <&rtc CLK_OSC32K_FANOUT>; + clock-names = "ext_clock"; + reset-gpios = <&r_pio 0 8 GPIO_ACTIVE_LOW>; /* PL8 */ + post-power-on-delay-ms = <200>; + }; +}; + +&cpu0 { + cpu-supply = <®_dcdca>; +}; + +&de { + status = "okay"; +}; + +&dwc3 { + status = "okay"; +}; + +&ehci0 { + status = "okay"; +}; + +&ehci3 { + status = "okay"; +}; + +&gpu { + mali-supply = <®_dcdcc>; + status = "okay"; +}; + +&hdmi { + hvcc-supply = <®_bldo2>; + status = "okay"; +}; + +&hdmi_out { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + +&mmc0 { + vmmc-supply = <®_cldo1>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */ + bus-width = <4>; + disable-wp; + status = "okay"; +}; + +&mmc1 { + vmmc-supply = <®_cldo3>; + vqmmc-supply = <®_bldo3>; + mmc-pwrseq = <&wifi_pwrseq>; + bus-width = <4>; + non-removable; + keep-power-in-suspend; + status = "okay"; + + sdio_wifi: wifi@1 { + reg = <1>; + compatible = "brcm,bcm43430a1-fmac", "brcm,bcm4329-fmac"; + interrupt-parent = <&r_pio>; + interrupts = <1 0 IRQ_TYPE_LEVEL_LOW>; /* PM0 */ + interrupt-names = "host-wake"; + }; +}; + +&mmc2 { + vmmc-supply = <®_cldo1>; + vqmmc-supply = <®_bldo2>; + cap-mmc-hw-reset; + non-removable; + mmc-hs200-1_8v; + bus-width = <8>; + status = "okay"; +}; + +&ohci0 { + status = "okay"; +}; + +&ohci3 { + status = "okay"; +}; + +&pio { + vcc-pc-supply = <®_bldo2>; + vcc-pd-supply = <®_cldo1>; + vcc-pg-supply = <®_bldo3>; +}; + +&r_i2c { + status = "okay"; + + axp805: pmic@36 { + compatible = "x-powers,axp805", "x-powers,axp806"; + reg = <0x36>; + interrupt-parent = <&r_intc>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <1>; + x-powers,self-working-mode; + vina-supply = <®_vcc5v>; + vinb-supply = <®_vcc5v>; + vinc-supply = <®_vcc5v>; + vind-supply = <®_vcc5v>; + vine-supply = <®_vcc5v>; + aldoin-supply = <®_vcc5v>; + bldoin-supply = <®_vcc5v>; + cldoin-supply = <®_vcc5v>; + + regulators { + reg_aldo1: aldo1 { + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-pl-led-ir-pg-pm-ts"; + }; + + reg_aldo2: aldo2 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-ac200"; + regulator-enable-ramp-delay = <100000>; + }; + + aldo3 { + /* unused */ + }; + + reg_bldo1: bldo1 { + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc18-dram-bias-pll"; + }; + + reg_bldo2: bldo2 { + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc-pc-emmc-efuse-hdmi"; + }; + + reg_bldo3: bldo3 { + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc-pg-wifiio"; + }; + + bldo4 { + /* unused */ + }; + + reg_cldo1: cldo1 { + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc33-io-pd-emmc-sd-usb-uart"; + }; + + /* This regulator is connected with CLDO3 */ + reg_cldo2: cldo2 { + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-wifi-1"; + }; + + reg_cldo3: cldo3 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc-wifi-2"; + }; + + reg_dcdca: dcdca { + regulator-always-on; + regulator-min-microvolt = <810000>; + regulator-max-microvolt = <1160000>; + regulator-ramp-delay = <2500>; + regulator-name = "vdd-cpu"; + }; + + reg_dcdcc: dcdcc { + regulator-enable-ramp-delay = <32000>; + regulator-min-microvolt = <810000>; + regulator-max-microvolt = <1080000>; + regulator-ramp-delay = <2500>; + regulator-name = "vdd-gpu"; + }; + + reg_dcdcd: dcdcd { + regulator-always-on; + regulator-min-microvolt = <960000>; + regulator-max-microvolt = <960000>; + regulator-name = "vdd-sys-hdmi-usb"; + }; + + reg_dcdce: dcdce { + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-name = "vcc-dram"; + }; + + sw { + /* unused */ + }; + }; + }; +}; + +&r_ir { + status = "okay"; +}; + +&r_pio { + /* + * PL0 and PL1 are used for PMIC I2C + * don't enable the pl-supply else + * it will fail at boot + * + * vcc-pl-supply = <®_aldo1>; + */ + vcc-pm-supply = <®_aldo1>; +}; + +&rtc { + clocks = <&ext_osc32k>; +}; + +&spdif { + pinctrl-names = "default"; + pinctrl-0 = <&spdif_tx_pin>; + status = "okay"; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_ph_pins>; + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>, <&uart1_rts_cts_pins>; + uart-has-rtscts; + status = "okay"; + + bluetooth { + compatible = "brcm,bcm43430a1-bt"; + clocks = <&rtc CLK_OSC32K_FANOUT>; + clock-names = "lpo"; + vbat-supply = <®_cldo3>; + vddio-supply = <®_bldo3>; + device-wakeup-gpios = <&r_pio 1 2 GPIO_ACTIVE_HIGH>; /* PM2 */ + host-wakeup-gpios = <&r_pio 1 1 GPIO_ACTIVE_HIGH>; /* PM1 */ + shutdown-gpios = <&r_pio 1 4 GPIO_ACTIVE_HIGH>; /* PM4 */ + max-speed = <1500000>; + }; +}; + +&usb2otg { + dr_mode = "host"; + status = "okay"; +}; + +&usb2phy { + usb0_vbus-supply = <®_vcc5v>; + usb3_vbus-supply = <®_vcc5v>; + status = "okay"; +}; + +&usb3phy { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi index 73e8604315c5..72ce1a75647b 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi @@ -540,6 +540,8 @@ reg-io-width = <4>; clocks = <&ccu CLK_BUS_UART0>; resets = <&ccu RST_BUS_UART0>; + dmas = <&dma 14>, <&dma 14>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -551,6 +553,8 @@ reg-io-width = <4>; clocks = <&ccu CLK_BUS_UART1>; resets = <&ccu RST_BUS_UART1>; + dmas = <&dma 15>, <&dma 15>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -562,6 +566,8 @@ reg-io-width = <4>; clocks = <&ccu CLK_BUS_UART2>; resets = <&ccu RST_BUS_UART2>; + dmas = <&dma 16>, <&dma 16>; + dma-names = "tx", "rx"; status = "disabled"; }; @@ -573,6 +579,8 @@ reg-io-width = <4>; clocks = <&ccu CLK_BUS_UART3>; resets = <&ccu RST_BUS_UART3>; + dmas = <&dma 17>, <&dma 17>; + dma-names = "tx", "rx"; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi index 8d1110c14bad..bf054869e78b 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi @@ -228,6 +228,15 @@ }; }; + timer0: timer@3009000 { + compatible = "allwinner,sun50i-h616-timer", + "allwinner,sun8i-a23-timer"; + reg = <0x03009000 0xa0>; + interrupts = , + ; + clocks = <&osc24M>; + }; + watchdog: watchdog@30090a0 { compatible = "allwinner,sun50i-h616-wdt", "allwinner,sun6i-a31-wdt"; diff --git a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi index a4230205c02b..5afa8d92acbf 100644 --- a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi @@ -153,6 +153,13 @@ bias-disable; }; + /omit-if-no-ref/ + ledc_ph_pin: ledc-ph-pin { + pins = "PH19"; + function = "ledc"; + allwinner,pinmux = <5>; + }; + mmc0_pins: mmc0-pins { pins = "PF0" ,"PF1", "PF2", "PF3", "PF4", "PF5"; allwinner,pinmux = <2>; @@ -221,6 +228,13 @@ allwinner,pinmux = <4>; }; + /omit-if-no-ref/ + spi0_pj_pins: spi0-pj-pins { + pins = "PJ21", "PJ22", "PJ23"; + function = "spi0"; + allwinner,pinmux = <5>; + }; + /omit-if-no-ref/ spi0_cs0_pc_pin: spi0-cs0-pc-pin { pins = "PC3"; @@ -228,6 +242,13 @@ allwinner,pinmux = <4>; }; + /omit-if-no-ref/ + spi0_cs0_pj_pin: spi0-cs0-pj-pin { + pins = "PJ20"; + function = "spi0"; + allwinner,pinmux = <5>; + }; + /omit-if-no-ref/ spi0_cs1_pc_pin: spi0-cs1-pc-pin { pins = "PC7"; @@ -235,6 +256,13 @@ allwinner,pinmux = <4>; }; + /omit-if-no-ref/ + spi0_cs1_pj_pin: spi0-cs1-pj-pin { + pins = "PJ24"; + function = "spi0"; + allwinner,pinmux = <5>; + }; + /omit-if-no-ref/ spi0_hold_pc_pin: spi0-hold-pc-pin { /* conflicts with eMMC D7 */ @@ -243,6 +271,13 @@ allwinner,pinmux = <4>; }; + /omit-if-no-ref/ + spi0_hold_pj_pin: spi0-hold-pj-pin { + pins = "PJ26"; + function = "spi0"; + allwinner,pinmux = <5>; + }; + /omit-if-no-ref/ spi0_wp_pc_pin: spi0-wp-pc-pin { /* conflicts with eMMC D2 */ @@ -251,6 +286,13 @@ allwinner,pinmux = <4>; }; + /omit-if-no-ref/ + spi0_wp_pj_pin: spi0-wp-pj-pin { + pins = "PJ25"; + function = "spi0"; + allwinner,pinmux = <5>; + }; + uart0_pb_pins: uart0-pb-pins { pins = "PB9", "PB10"; allwinner,pinmux = <2>; @@ -283,6 +325,21 @@ #reset-cells = <1>; }; + ledc: led-controller@2008000 { + compatible = "allwinner,sun55i-a523-ledc", + "allwinner,sun50i-a100-ledc"; + reg = <0x02008000 0x400>; + interrupts = ; + clocks = <&ccu CLK_BUS_LEDC>, <&ccu CLK_LEDC>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_LEDC>; + dmas = <&dma 42>; + dma-names = "tx"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + wdt: watchdog@2050000 { compatible = "allwinner,sun55i-a523-wdt"; reg = <0x2050000 0x20>; diff --git a/arch/arm64/boot/dts/allwinner/sun55i-t527-avaota-a1.dts b/arch/arm64/boot/dts/allwinner/sun55i-t527-avaota-a1.dts index 054d0357c139..474354fbfcec 100644 --- a/arch/arm64/boot/dts/allwinner/sun55i-t527-avaota-a1.dts +++ b/arch/arm64/boot/dts/allwinner/sun55i-t527-avaota-a1.dts @@ -6,6 +6,7 @@ #include "sun55i-a523.dtsi" #include +#include / { model = "Avaota A1"; @@ -99,6 +100,46 @@ status = "okay"; }; +&ledc { + pinctrl-names = "default"; + pinctrl-0 = <&ledc_ph_pin>; + allwinner,pixel-format = "grb"; + allwinner,t0l-ns = <800>; + allwinner,t0h-ns = <300>; + allwinner,t1l-ns = <320>; + allwinner,t1h-ns = <800>; + allwinner,treset-ns = <84>; + status = "okay"; + + multi-led@0 { + reg = <0x0>; + color = ; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <0>; + }; + + multi-led@1 { + reg = <0x1>; + color = ; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <1>; + }; + + multi-led@2 { + reg = <0x2>; + color = ; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <2>; + }; + + multi-led@3 { + reg = <0x3>; + color = ; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <3>; + }; +}; + &mdio0 { ext_rgmii0_phy: ethernet-phy@1 { compatible = "ethernet-phy-ieee802.3-c22"; @@ -362,6 +403,21 @@ assigned-clock-rates = <32768>; }; +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pj_pins>, <&spi0_cs0_pj_pin>, + <&spi0_hold_pj_pin>, <&spi0_wp_pj_pin>; + status = "okay"; + + nand@0 { + compatible = "spi-nand"; + reg = <0>; + spi-max-frequency = <100000000>; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <4>; + }; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pb_pins>; diff --git a/arch/arm64/boot/dts/altera/Makefile b/arch/arm64/boot/dts/altera/Makefile index 1bf0c472f6b4..540bb5ae746b 100644 --- a/arch/arm64/boot/dts/altera/Makefile +++ b/arch/arm64/boot/dts/altera/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only dtb-$(CONFIG_ARCH_INTEL_SOCFPGA) += socfpga_stratix10_socdk.dtb \ + socfpga_stratix10_socdk_emmc.dtb \ socfpga_stratix10_socdk_nand.dtb \ socfpga_stratix10_swvp.dtb diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts index 4ae18a013bbe..e2a1cea7f3da 100644 --- a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts +++ b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts @@ -3,53 +3,11 @@ * Copyright Altera Corporation (C) 2015. All rights reserved. */ -#include "socfpga_stratix10.dtsi" +#include "socfpga_stratix10_socdk.dtsi" / { model = "SoCFPGA Stratix 10 SoCDK"; compatible = "altr,socfpga-stratix10-socdk", "altr,socfpga-stratix10"; - - aliases { - serial0 = &uart0; - ethernet0 = &gmac0; - ethernet1 = &gmac1; - ethernet2 = &gmac2; - }; - - chosen { - stdout-path = "serial0:115200n8"; - }; - - leds { - compatible = "gpio-leds"; - led-hps0 { - label = "hps_led0"; - gpios = <&portb 20 GPIO_ACTIVE_HIGH>; - }; - - led-hps1 { - label = "hps_led1"; - gpios = <&portb 19 GPIO_ACTIVE_HIGH>; - }; - - led-hps2 { - label = "hps_led2"; - gpios = <&portb 21 GPIO_ACTIVE_HIGH>; - }; - }; - - memory@80000000 { - device_type = "memory"; - /* We expect the bootloader to fill in the reg */ - reg = <0 0x80000000 0 0>; - }; - - ref_033v: regulator-v-ref { - compatible = "regulator-fixed"; - regulator-name = "0.33V"; - regulator-min-microvolt = <330000>; - regulator-max-microvolt = <330000>; - }; }; &pinctrl0 { @@ -68,10 +26,6 @@ }; }; -&gpio1 { - status = "okay"; -}; - &gmac0 { status = "okay"; phy-mode = "rgmii"; @@ -83,7 +37,7 @@ #address-cells = <1>; #size-cells = <0>; compatible = "snps,dwmac-mdio"; - phy0: ethernet-phy@0 { + phy0: ethernet-phy@4 { reg = <4>; txd0-skew-ps = <0>; /* -420ps */ @@ -111,23 +65,6 @@ clk-phase-sd-hs = <0>, <135>; }; -&osc1 { - clock-frequency = <25000000>; -}; - -&uart0 { - status = "okay"; -}; - -&usb0 { - status = "okay"; - disable-over-current; -}; - -&watchdog0 { - status = "okay"; -}; - &i2c1 { status = "okay"; clock-frequency = <100000>; diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dtsi b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dtsi new file mode 100755 index 000000000000..1d50f7b21160 --- /dev/null +++ b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dtsi @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright Altera Corporation (C) 2026. All rights reserved. + */ + +#include "socfpga_stratix10.dtsi" + +/ { + aliases { + serial0 = &uart0; + ethernet0 = &gmac0; + ethernet1 = &gmac1; + ethernet2 = &gmac2; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + leds { + compatible = "gpio-leds"; + led-hps0 { + label = "hps_led0"; + gpios = <&portb 20 GPIO_ACTIVE_HIGH>; + }; + + led-hps1 { + label = "hps_led1"; + gpios = <&portb 19 GPIO_ACTIVE_HIGH>; + }; + + led-hps2 { + label = "hps_led2"; + gpios = <&portb 21 GPIO_ACTIVE_HIGH>; + }; + }; + + memory@80000000 { + device_type = "memory"; + /* We expect the bootloader to fill in the reg */ + reg = <0 0x80000000 0 0>; + }; + + ref_033v: regulator-0v33-ref { + compatible = "regulator-fixed"; + regulator-name = "0.33V"; + regulator-min-microvolt = <330000>; + regulator-max-microvolt = <330000>; + }; +}; + +&gpio1 { + status = "okay"; +}; + +&osc1 { + clock-frequency = <25000000>; +}; + +&uart0 { + status = "okay"; +}; + +&usb0 { + status = "okay"; + disable-over-current; +}; + +&watchdog0 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk_emmc.dts b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk_emmc.dts new file mode 100755 index 000000000000..b2a3449638dd --- /dev/null +++ b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk_emmc.dts @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright Altera Corporation (C) 2026. All rights reserved. + */ + +#include "socfpga_stratix10_socdk.dtsi" + +/ { + model = "SoCFPGA Stratix 10 SoCDK eMMC daughter board"; + compatible = "altr,socfpga-stratix10-socdk-emmc", + "altr,socfpga-stratix10-socdk", + "altr,socfpga-stratix10"; +}; + +&gmac2 { + status = "okay"; + /* PHY delays is configured via skew properties */ + phy-mode = "rgmii"; + phy-handle = <&phy0>; + + max-frame-size = <9000>; + + mdio0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + phy0: ethernet-phy@4 { + reg = <4>; + + txd0-skew-ps = <0>; /* -420ps */ + txd1-skew-ps = <0>; /* -420ps */ + txd2-skew-ps = <0>; /* -420ps */ + txd3-skew-ps = <0>; /* -420ps */ + rxd0-skew-ps = <420>; /* 0ps */ + rxd1-skew-ps = <420>; /* 0ps */ + rxd2-skew-ps = <420>; /* 0ps */ + rxd3-skew-ps = <420>; /* 0ps */ + txen-skew-ps = <0>; /* -420ps */ + txc-skew-ps = <900>; /* 0ps */ + rxdv-skew-ps = <420>; /* 0ps */ + rxc-skew-ps = <1680>; /* 780ps */ + }; + }; +}; + +&mmc { + status = "okay"; + cap-mmc-highspeed; + broken-cd; + bus-width = <4>; + clk-phase-sd-hs = <0>, <135>; +}; + +&i2c2 { + status = "okay"; + clock-frequency = <100000>; + i2c-sda-falling-time-ns = <890>; /* hcnt */ + i2c-scl-falling-time-ns = <890>; /* lcnt */ + + adc@14 { + compatible = "lltc,ltc2497"; + reg = <0x14>; + vref-supply = <&ref_033v>; + }; + + temp@4c { + compatible = "maxim,max1619"; + reg = <0x4c>; + }; + + eeprom@51 { + compatible = "atmel,24c32"; + reg = <0x51>; + pagesize = <32>; + }; + + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; +}; diff --git a/arch/arm64/boot/dts/amlogic/amlogic-s6.dtsi b/arch/arm64/boot/dts/amlogic/amlogic-s6.dtsi index 8ef631939033..ab3acef2b147 100644 --- a/arch/arm64/boot/dts/amlogic/amlogic-s6.dtsi +++ b/arch/arm64/boot/dts/amlogic/amlogic-s6.dtsi @@ -53,10 +53,10 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; psci { @@ -84,7 +84,7 @@ interrupt-controller; reg = <0x0 0xff200000 0 0x10000>, <0x0 0xff240000 0 0x80000>; - interrupts = ; + interrupts = ; }; apb: bus@fe000000 { diff --git a/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts b/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts index fffdab96b12e..f4c953034be3 100644 --- a/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts +++ b/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts @@ -8,7 +8,7 @@ #include "amlogic-t7.dtsi" / { - model = "Khadas vim4"; + model = "Khadas VIM4"; compatible = "khadas,vim4", "amlogic,a311d2", "amlogic,t7"; aliases { @@ -17,7 +17,9 @@ memory@0 { device_type = "memory"; - reg = <0x0 0x0 0x2 0x0>; /* 8 GB */ + reg = <0x0 0x0 0x0 0xE0000000 + 0x1 0x0 0x0 0xE0000000 + 0x2 0x0 0x0 0x40000000>; /* 8 GB */ }; reserved-memory { diff --git a/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi b/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi index 6510068bcff9..d523cbc0ed22 100644 --- a/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi +++ b/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi @@ -213,7 +213,9 @@ #address-cells = <0>; interrupt-controller; reg = <0x0 0xfff01000 0 0x1000>, - <0x0 0xfff02000 0 0x0100>; + <0x0 0xfff02000 0 0x2000>, + <0x0 0xfff04000 0 0x2000>, + <0x0 0xfff06000 0 0x2000>; interrupts = ; }; diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi index cc72491eaf6f..f1f53fd98ae2 100644 --- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi @@ -72,6 +72,12 @@ compatible = "arm,cortex-a53"; reg = <0x0 0x0>; enable-method = "psci"; + d-cache-line-size = <32>; + d-cache-size = <0x8000>; + d-cache-sets = <32>; + i-cache-line-size = <32>; + i-cache-size = <0x8000>; + i-cache-sets = <32>; next-level-cache = <&l2>; clocks = <&scpi_dvfs 0>; dynamic-power-coefficient = <140>; diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts index 7dffeb5931c9..701de57ff0f3 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts @@ -84,7 +84,8 @@ reset-gpios = <&gpio GPIOZ_14 GPIO_ACTIVE_LOW>; interrupt-parent = <&gpio_intc>; - interrupts = <29 IRQ_TYPE_LEVEL_LOW>; + /* MAC_INTR on GPIOZ_15 */ + interrupts = <25 IRQ_TYPE_LEVEL_LOW>; eee-broken-1000t; }; }; diff --git a/arch/arm64/boot/dts/apple/spi1-nvram.dtsi b/arch/arm64/boot/dts/apple/spi1-nvram.dtsi index 9740fbf200f0..d2720b307774 100644 --- a/arch/arm64/boot/dts/apple/spi1-nvram.dtsi +++ b/arch/arm64/boot/dts/apple/spi1-nvram.dtsi @@ -2,7 +2,7 @@ // // Devicetree include for common spi-nor nvram flash. // -// Apple uses a consistent configiguration for the nvram on all known M1* and +// Apple uses a consistent configuration for the nvram on all known M1* and // M2* devices. // // Copyright The Asahi Linux Contributors diff --git a/arch/arm64/boot/dts/arm/Makefile b/arch/arm64/boot/dts/arm/Makefile index f30ee045dc95..b35b03da2d84 100644 --- a/arch/arm64/boot/dts/arm/Makefile +++ b/arch/arm64/boot/dts/arm/Makefile @@ -7,4 +7,6 @@ dtb-$(CONFIG_ARCH_VEXPRESS) += rtsm_ve-aemv8a.dtb dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2f-1xv7-ca53x2.dtb dtb-$(CONFIG_ARCH_VEXPRESS) += fvp-base-revc.dtb dtb-$(CONFIG_ARCH_VEXPRESS) += corstone1000-fvp.dtb corstone1000-mps3.dtb +dtb-$(CONFIG_ARCH_VEXPRESS) += corstone1000-a320-fvp.dtb dtb-$(CONFIG_ARCH_VEXPRESS) += morello-sdp.dtb morello-fvp.dtb +dtb-$(CONFIG_ARCH_VEXPRESS) += zena-css-fvp.dtb diff --git a/arch/arm64/boot/dts/arm/corstone1000-a320-fvp.dts b/arch/arm64/boot/dts/arm/corstone1000-a320-fvp.dts new file mode 100644 index 000000000000..0f72af78b5e1 --- /dev/null +++ b/arch/arm64/boot/dts/arm/corstone1000-a320-fvp.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (c) 2026, Arm Limited. All rights reserved. + * + */ + +/dts-v1/; + +#include "corstone1000-a320.dtsi" +#include "corstone1000-fvp.dtsi" + +/ { + model = "ARM Corstone1000-A320 FVP (Fixed Virtual Platform)"; + compatible = "arm,corstone1000-a320-fvp"; +}; diff --git a/arch/arm64/boot/dts/arm/corstone1000-a320.dtsi b/arch/arm64/boot/dts/arm/corstone1000-a320.dtsi new file mode 100644 index 000000000000..f0937914350c --- /dev/null +++ b/arch/arm64/boot/dts/arm/corstone1000-a320.dtsi @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (c) 2026, Arm Limited. All rights reserved. + * + */ + +#include + +#include "corstone1000.dtsi" + +/ { + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <1>; + + cpus: cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a320"; + reg = <0 0>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + + cpu1: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a320"; + reg = <0 0x100>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + + cpu2: cpu@200 { + device_type = "cpu"; + compatible = "arm,cortex-a320"; + reg = <0 0x200>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + + cpu3: cpu@300 { + device_type = "cpu"; + compatible = "arm,cortex-a320"; + reg = <0 0x300>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + }; + + sram: sram@2400000 { + compatible = "mmio-sram"; + reg = <0x02400000 0x200000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + }; + + gic: interrupt-controller@1c000000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + #address-cells = <1>; + #size-cells = <1>; + interrupt-controller; + reg = <0x1c000000 0x10000>, + <0x1c040000 0x80000>; + interrupts = ; + }; + + + soc { + npu@1a050000 { + compatible = "arm,corstone1000-ethos-u85", "arm,ethos-u85"; + reg = <0x1a050000 0x1400>; + interrupts = ; + clocks = <&refclk100mhz>, <&refclk100mhz>; + clock-names = "core", "apb"; + sram = <&sram>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/arm/corstone1000-fvp.dts b/arch/arm64/boot/dts/arm/corstone1000-fvp.dts index 66ba6b027193..fac0999b1901 100644 --- a/arch/arm64/boot/dts/arm/corstone1000-fvp.dts +++ b/arch/arm64/boot/dts/arm/corstone1000-fvp.dts @@ -8,70 +8,46 @@ /dts-v1/; #include "corstone1000.dtsi" +#include "corstone1000-fvp.dtsi" / { model = "ARM Corstone1000 FVP (Fixed Virtual Platform)"; compatible = "arm,corstone1000-fvp"; - smsc: ethernet@4010000 { - compatible = "smsc,lan91c111"; - reg = <0x40100000 0x10000>; - phy-mode = "mii"; - interrupts = ; - reg-io-width = <2>; - }; + cpus: cpus { + #address-cells = <2>; + #size-cells = <0>; - vmmc_v3_3d: regulator-vmmc { - compatible = "regulator-fixed"; - regulator-name = "vmmc_supply"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; + cpu: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a35"; + reg = <0 0>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; - sdmmc0: mmc@40300000 { - compatible = "arm,pl18x", "arm,primecell"; - reg = <0x40300000 0x1000>; - interrupts = ; - max-frequency = <12000000>; - vmmc-supply = <&vmmc_v3_3d>; - clocks = <&smbclk>, <&refclk100mhz>; - clock-names = "smclk", "apb_pclk"; - }; + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a35"; + reg = <0 0x1>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; - sdmmc1: mmc@50000000 { - compatible = "arm,pl18x", "arm,primecell"; - reg = <0x50000000 0x10000>; - interrupts = ; - max-frequency = <12000000>; - vmmc-supply = <&vmmc_v3_3d>; - clocks = <&smbclk>, <&refclk100mhz>; - clock-names = "smclk", "apb_pclk"; - }; -}; + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a35"; + reg = <0 0x2>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; -&cpus { - cpu1: cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a35"; - reg = <0x1>; - enable-method = "psci"; - next-level-cache = <&L2_0>; - }; - - cpu2: cpu@2 { - device_type = "cpu"; - compatible = "arm,cortex-a35"; - reg = <0x2>; - enable-method = "psci"; - next-level-cache = <&L2_0>; - }; - - cpu3: cpu@3 { - device_type = "cpu"; - compatible = "arm,cortex-a35"; - reg = <0x3>; - enable-method = "psci"; - next-level-cache = <&L2_0>; + cpu3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a35"; + reg = <0 0x3>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; }; }; diff --git a/arch/arm64/boot/dts/arm/corstone1000-fvp.dtsi b/arch/arm64/boot/dts/arm/corstone1000-fvp.dtsi new file mode 100644 index 000000000000..dc6d77446e8f --- /dev/null +++ b/arch/arm64/boot/dts/arm/corstone1000-fvp.dtsi @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022, Linaro Limited. All rights reserved. + * + */ + +/ { + smsc: ethernet@4010000 { + compatible = "smsc,lan91c111"; + reg = <0x40100000 0x10000>; + phy-mode = "mii"; + interrupts = ; + reg-io-width = <2>; + }; + + vmmc_v3_3d: regulator-vmmc { + compatible = "regulator-fixed"; + regulator-name = "vmmc_supply"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + sdmmc0: mmc@40300000 { + compatible = "arm,pl18x", "arm,primecell"; + reg = <0x40300000 0x1000>; + interrupts = ; + max-frequency = <12000000>; + vmmc-supply = <&vmmc_v3_3d>; + clocks = <&smbclk>, <&refclk100mhz>; + clock-names = "smclk", "apb_pclk"; + }; + + sdmmc1: mmc@50000000 { + compatible = "arm,pl18x", "arm,primecell"; + reg = <0x50000000 0x10000>; + interrupts = ; + max-frequency = <12000000>; + vmmc-supply = <&vmmc_v3_3d>; + clocks = <&smbclk>, <&refclk100mhz>; + clock-names = "smclk", "apb_pclk"; + }; +}; diff --git a/arch/arm64/boot/dts/arm/corstone1000-mps3.dts b/arch/arm64/boot/dts/arm/corstone1000-mps3.dts index 10d265be0c02..adcfaf7c55b8 100644 --- a/arch/arm64/boot/dts/arm/corstone1000-mps3.dts +++ b/arch/arm64/boot/dts/arm/corstone1000-mps3.dts @@ -13,6 +13,19 @@ model = "ARM Corstone1000 FPGA MPS3 board"; compatible = "arm,corstone1000-mps3"; + cpus: cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a35"; + reg = <0 0>; + enable-method = "psci"; + next-level-cache = <&L2_0>; + }; + }; + smsc: ethernet@4010000 { compatible = "smsc,lan9220", "smsc,lan9115"; reg = <0x40100000 0x10000>; diff --git a/arch/arm64/boot/dts/arm/corstone1000.dtsi b/arch/arm64/boot/dts/arm/corstone1000.dtsi index f35a5c96f3da..4d57dc197918 100644 --- a/arch/arm64/boot/dts/arm/corstone1000.dtsi +++ b/arch/arm64/boot/dts/arm/corstone1000.dtsi @@ -21,19 +21,6 @@ stdout-path = "serial0:115200n8"; }; - cpus: cpus { - #address-cells = <1>; - #size-cells = <0>; - - cpu: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a35"; - reg = <0>; - enable-method = "psci"; - next-level-cache = <&L2_0>; - }; - }; - memory@88200000 { device_type = "memory"; reg = <0x88200000 0x77e00000>; diff --git a/arch/arm64/boot/dts/arm/zena-css-fvp.dts b/arch/arm64/boot/dts/arm/zena-css-fvp.dts new file mode 100644 index 000000000000..53c5412d92b2 --- /dev/null +++ b/arch/arm64/boot/dts/arm/zena-css-fvp.dts @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + */ + +/dts-v1/; + +#include "zena-css.dtsi" + +/ { + model = "Zena CSS Fixed Virtual Platform"; + compatible = "arm,zena-css-fvp", "arm,zena-css", "arm,vexpress"; + + chosen { + stdout-path = &soc_serial0; + }; + + memory@80000000 { + device_type = "memory"; + + /* ~2GB mapped at 2GB, another 2GB at 2TB */ + reg = <0x00000000 0x80000000 0x00000000 0x7f000000>, + <0x00000200 0x00000000 0x00000000 0x80000000>; + }; +}; + +&soc { + virtio@30020000 { + compatible = "virtio,mmio"; + reg = <0x0 0x30020000 0x0 0x10000>; + interrupts = ; + }; + + virtio@30030000 { + compatible = "virtio,mmio"; + reg = <0x0 0x30030000 0x0 0x10000>; + interrupts = ; + }; + + virtio@30040000 { + compatible = "virtio,mmio"; + reg = <0x0 0x30040000 0x0 0x10000>; + interrupts = ; + }; + + virtio@30050000 { + compatible = "virtio,mmio"; + reg = <0x0 0x30050000 0x0 0x10000>; + interrupts = ; + }; + + virtio@30060000 { + compatible = "virtio,mmio"; + reg = <0x0 0x30060000 0x0 0x10000>; + interrupts = ; + }; + + virtio@30080000 { + compatible = "virtio,mmio"; + reg = <0x0 0x30080000 0x0 0x10000>; + interrupts = ; + }; +}; diff --git a/arch/arm64/boot/dts/arm/zena-css.dtsi b/arch/arm64/boot/dts/arm/zena-css.dtsi new file mode 100644 index 000000000000..0b41ee4bf4c6 --- /dev/null +++ b/arch/arm64/boot/dts/arm/zena-css.dtsi @@ -0,0 +1,769 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + */ + +#include + +/ { + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + + soc_clk24mhz: clock-24000000 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "refclk24mhz"; + }; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + /* + * The latency and residency numbers below are for illustrative + * purposes only and may vary on actual silicon. These values are + * considered just to demonstrate that the cpuidle governor logic + * works. + */ + idle-states { + entry-method = "psci"; + + cpu_sleep: cpu-sleep { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x10000>; + entry-latency-us = <800>; + exit-latency-us = <3200>; + local-timer-stop; + min-residency-us = <4200>; + }; + + cluster_sleep: cluster-sleep { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x1010000>; + entry-latency-us = <1000>; + exit-latency-us = <3200>; + local-timer-stop; + min-residency-us = <4500>; + }; + }; + + cpu-map { + cluster0 { + core0 { cpu = <&cpu0>; }; + core1 { cpu = <&cpu1>; }; + core2 { cpu = <&cpu2>; }; + core3 { cpu = <&cpu3>; }; + }; + + cluster1 { + core0 { cpu = <&cpu4>; }; + core1 { cpu = <&cpu5>; }; + core2 { cpu = <&cpu6>; }; + core3 { cpu = <&cpu7>; }; + }; + + cluster2 { + core0 { cpu = <&cpu8>; }; + core1 { cpu = <&cpu9>; }; + core2 { cpu = <&cpu10>; }; + core3 { cpu = <&cpu11>; }; + }; + + cluster3 { + core0 { cpu = <&cpu12>; }; + core1 { cpu = <&cpu13>; }; + core2 { cpu = <&cpu14>; }; + core3 { cpu = <&cpu15>; }; + }; + }; + + cpu0: cpu@0 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x0000>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl0_l2_0>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl0_l2_0: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl0_l3>; + }; + }; + + cpu1: cpu@100 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x0100>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl0_l2_1>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl0_l2_1: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl0_l3>; + }; + }; + + cpu2: cpu@200 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x0200>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl0_l2_2>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl0_l2_2: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl0_l3>; + }; + }; + + cpu3: cpu@300 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x0300>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl0_l2_3>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl0_l2_3: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl0_l3>; + }; + }; + + cpu4: cpu@10000 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x10000>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl1_l2_0>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl1_l2_0: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl1_l3>; + }; + }; + + cpu5: cpu@10100 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x10100>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl1_l2_1>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl1_l2_1: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl1_l3>; + }; + }; + + cpu6: cpu@10200 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x10200>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl1_l2_2>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl1_l2_2: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl1_l3>; + }; + }; + + cpu7: cpu@10300 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x10300>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl1_l2_3>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl1_l2_3: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl1_l3>; + }; + }; + + cpu8: cpu@20000 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x20000>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl2_l2_0>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl2_l2_0: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl2_l3>; + }; + }; + + cpu9: cpu@20100 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x20100>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl2_l2_1>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl2_l2_1: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl2_l3>; + }; + }; + + cpu10: cpu@20200 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x20200>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl2_l2_2>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl2_l2_2: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl2_l3>; + }; + }; + + cpu11: cpu@20300 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x20300>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl2_l2_3>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl2_l2_3: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl2_l3>; + }; + }; + + cpu12: cpu@30000 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x30000>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl3_l2_0>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl3_l2_0: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl3_l3>; + }; + }; + + cpu13: cpu@30100 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x30100>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl3_l2_1>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl3_l2_1: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl3_l3>; + }; + }; + + cpu14: cpu@30200 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x30200>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl3_l2_2>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl3_l2_2: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl3_l3>; + }; + }; + + cpu15: cpu@30300 { + compatible = "arm,cortex-a720ae"; + device_type = "cpu"; + reg = <0x00 0x30300>; + enable-method = "psci"; + + clocks = <&scmi_dvfs 0>; + cpu-idle-states = <&cpu_sleep &cluster_sleep>; + next-level-cache = <&cl3_l2_3>; + + i-cache-line-size = <64>; + i-cache-sets = <256>; + i-cache-size = <0x10000>; + + d-cache-line-size = <64>; + d-cache-sets = <256>; + d-cache-size = <0x10000>; + + cl3_l2_3: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <0x400>; /* 8-way set */ + cache-size = <0x80000>; /* 512KB */ + cache-unified; + next-level-cache = <&cl3_l3>; + }; + }; + + cl0_l3: l3-cache0 { + compatible = "cache"; + cache-level = <3>; + cache-line-size = <64>; + cache-sets = <0x1000>; /* 16-way set */ + cache-size = <0x400000>; /* 4MB */ + cache-unified; + }; + + cl1_l3: l3-cache1 { + compatible = "cache"; + cache-level = <3>; + cache-line-size = <64>; + cache-sets = <0x1000>; /* 16-way set */ + cache-size = <0x400000>; /* 4MB */ + cache-unified; + }; + + cl2_l3: l3-cache2 { + compatible = "cache"; + cache-level = <3>; + cache-line-size = <64>; + cache-sets = <0x1000>; /* 16-way set */ + cache-size = <0x400000>; /* 4MB */ + cache-unified; + }; + + cl3_l3: l3-cache3 { + compatible = "cache"; + cache-level = <3>; + cache-line-size = <64>; + cache-sets = <0x1000>; /* 16-way set */ + cache-size = <0x400000>; /* 4MB */ + cache-unified; + }; + }; + + firmware { + scmi { + compatible = "arm,scmi"; + #address-cells = <1>; + #size-cells = <0>; + + mbox-names = "tx", "tx_reply", "rx"; + mboxes = <&mbox_db_tx 0 0 0>, + <&mbox_db_rx 0 0 0>, + <&mbox_db_rx 0 0 2>; + shmem = <&scmi_shmem_tx &scmi_shmem_rx>; + + scmi_dvfs: protocol@13 { + reg = <0x13>; + #clock-cells = <1>; + }; + }; + }; + + dsu-pmu-0 { + compatible = "arm,dsu-pmu"; + cpus = <&cpu0 &cpu1 &cpu2 &cpu3>; + interrupts = ; + }; + + dsu-pmu-1 { + compatible = "arm,dsu-pmu"; + cpus = <&cpu4 &cpu5 &cpu6 &cpu7>; + interrupts = ; + }; + + dsu-pmu-2 { + compatible = "arm,dsu-pmu"; + cpus = <&cpu8 &cpu9 &cpu10 &cpu11>; + interrupts = ; + }; + + dsu-pmu-3 { + compatible = "arm,dsu-pmu"; + cpus = <&cpu12 &cpu13 &cpu14 &cpu15>; + interrupts = ; + }; + + psci { + compatible = "arm,psci-1.0", "arm,psci-0.2"; + method = "smc"; + }; + + soc: soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + sram: sram@104000 { + compatible = "mmio-sram"; + reg = <0x0 0x00104000 0x0 0x00001000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x0 0x00104000 0x00001000>; + + scmi_shmem_tx: scpshmem-sram-section@0 { + compatible = "arm,scmi-shmem"; + reg = <0x0 0x100>; + }; + + scmi_shmem_rx: scpshmem-sram-section@100 { + compatible = "arm,scmi-shmem"; + reg = <0x100 0x100>; + }; + }; + + timer@1a810000 { + compatible = "arm,armv7-timer-mem"; + reg = <0x0 0x1a810000 0x0 0x10000>; + #address-cells = <1>; + #size-cells = <1>; + + /* + * Map child space [0x0..0x30000) to parent @ 0x1a810000 + */ + ranges = <0x0 0x0 0x1a810000 0x00030000>; + + frame@20000 { + reg = <0x20000 0x10000>; + frame-number = <0>; + interrupts = ; + }; + }; + + gic: interrupt-controller@20800000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + #address-cells = <2>; + #size-cells = <2>; + #redistributor-regions = <16>; + interrupt-controller; + interrupts = ; + ranges; + + /* + * With GIC-A720AE multiview enabled, GICR_TYPER.Last is + * always reported as 1 on redistributor views other than + * view 0. This breaks discovery of a single contiguous + * GICR frame region, so each core is described with its own + * redistributor region. + */ + reg = <0x0 0x20800000 0x0 0x10000>, /* GICD */ + <0x0 0x20880000 0x0 0x40000>, /* 16 * GICR */ + <0x0 0x208c0000 0x0 0x40000>, + <0x0 0x20900000 0x0 0x40000>, + <0x0 0x20940000 0x0 0x40000>, + <0x0 0x20980000 0x0 0x40000>, + <0x0 0x209c0000 0x0 0x40000>, + <0x0 0x20a00000 0x0 0x40000>, + <0x0 0x20a40000 0x0 0x40000>, + <0x0 0x20a80000 0x0 0x40000>, + <0x0 0x20ac0000 0x0 0x40000>, + <0x0 0x20b00000 0x0 0x40000>, + <0x0 0x20b40000 0x0 0x40000>, + <0x0 0x20b80000 0x0 0x40000>, + <0x0 0x20bc0000 0x0 0x40000>, + <0x0 0x20c00000 0x0 0x40000>, + <0x0 0x20c40000 0x0 0x40000>; + + its: msi-controller@20840000 { + compatible = "arm,gic-v3-its"; + reg = <0x0 0x20840000 0x0 0x40000>; + msi-controller; + #msi-cells = <1>; + }; + }; + + /* + * UART is fixed at 24MHz, both UARTCLK and PCLK. + */ + soc_serial0: serial@1a400000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0 0x1a400000 0x0 0x10000>; + interrupts = ; + clocks = <&soc_clk24mhz>, <&soc_clk24mhz>; + clock-names = "uartclk", "apb_pclk"; + }; + + watchdog@1a420000 { + compatible = "arm,sbsa-gwdt"; + reg = <0x0 0x1a420000 0x0 0x10000>, + <0x0 0x1a430000 0x0 0x10000>; + interrupts = ; + }; + + rtc@300d0000 { + compatible = "arm,pl031", "arm,primecell"; + reg = <0x0 0x300d0000 0x0 0x10000>; + interrupts = ; + clocks = <&soc_clk24mhz>; + clock-names = "apb_pclk"; + }; + + mbox_db_tx: mailbox@40020000 { + compatible = "arm,mhuv3"; + reg = <0x0 0x40020000 0x0 0x30000>; + interrupts = ; + interrupt-names = "combined"; + clocks = <&soc_clk24mhz>; + #mbox-cells = <3>; + }; + + mbox_db_rx: mailbox@40060000 { + compatible = "arm,mhuv3"; + reg = <0x0 0x40060000 0x0 0x30000>; + interrupts = ; + interrupt-names = "combined"; + clocks = <&soc_clk24mhz>; + #mbox-cells = <3>; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + , + ; + }; +}; diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts index 7de24d60bcd1..127be0fc27c2 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts +++ b/arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts @@ -35,3 +35,17 @@ "PMIC_SCL", // AON_SGPIO_04 "PMIC_SDA"; // AON_SGPIO_05 }; + +&pinctrl { + compatible = "brcm,bcm2712d0-pinctrl"; + reg = <0x7d504100 0x20>; +}; + +&pinctrl_aon { + compatible = "brcm,bcm2712d0-aon-pinctrl"; + reg = <0x7d510700 0x1c>; +}; + +&uart10 { + interrupts = ; +}; diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi index 04738bf281eb..b7a6bc34ae1a 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi @@ -44,6 +44,30 @@ }; }; + firmware { + firmware: rpi-firmware { + compatible = "raspberrypi,bcm2835-firmware", "simple-mfd"; + + mboxes = <&mailbox>; + + firmware_clocks: clocks { + compatible = "raspberrypi,firmware-clocks"; + #clock-cells = <1>; + }; + + reset: reset { + compatible = "raspberrypi,firmware-reset"; + #reset-cells = <1>; + }; + + power: power { + compatible = "raspberrypi,bcm2835-power"; + firmware = <&firmware>; + #power-domain-cells = <1>; + }; + }; + }; + sd_io_1v8_reg: sd-io-1v8-reg { compatible = "regulator-gpio"; regulator-name = "vdd-sd-io"; @@ -189,33 +213,6 @@ }; }; -&soc { - firmware: firmware { - compatible = "raspberrypi,bcm2835-firmware", "simple-mfd"; - #address-cells = <1>; - #size-cells = <1>; - - mboxes = <&mailbox>; - dma-ranges; - - firmware_clocks: clocks { - compatible = "raspberrypi,firmware-clocks"; - #clock-cells = <1>; - }; - - reset: reset { - compatible = "raspberrypi,firmware-reset"; - #reset-cells = <1>; - }; - }; - - power: power { - compatible = "raspberrypi,bcm2835-power"; - firmware = <&firmware>; - #power-domain-cells = <1>; - }; -}; - /* uarta communicates with the BT module */ &uarta { uart-has-rtscts; @@ -252,3 +249,7 @@ &pcie2 { status = "okay"; }; + +&v3d { + clocks = <&firmware_clocks 5>; +}; diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts index 285608281446..0fc57e72632e 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts @@ -68,6 +68,30 @@ function = "vbus1"; groups = "vbus1"; }; + + rp1_i2c4_default_state: rp1-i2c4-default-state { + function = "i2c4"; + groups = "i2c4_2"; + drive-strength = <12>; + bias-pull-up; + }; + + rp1_i2c6_default_state: rp1-i2c6-default-state { + function = "i2c6"; + groups = "i2c6_0"; + drive-strength = <12>; + bias-pull-up; + }; +}; + +&rp1_i2c4 { + pinctrl-0 = <&rp1_i2c4_default_state>; + pinctrl-names = "default"; +}; + +&rp1_i2c6 { + pinctrl-0 = <&rp1_i2c6_default_state>; + pinctrl-names = "default"; }; &rp1_usb0 { diff --git a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi index d57a9b1bff70..761c59d90ffc 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi @@ -1,5 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0 OR MIT) #include +#include / { compatible = "brcm,bcm2712"; @@ -508,10 +509,6 @@ <0x18 0x00000000 0x18 0x00000000 0x04 0x00000000>, <0x1c 0x00000000 0x1c 0x00000000 0x04 0x00000000>; - vc4: gpu { - compatible = "brcm,bcm2712-vc6"; - }; - pcie0: pcie@1000100000 { compatible = "brcm,bcm2712-pcie"; reg = <0x10 0x00100000 0x00 0x9310>; @@ -646,6 +643,30 @@ msi-ranges = <&gicv2 GIC_SPI 247 IRQ_TYPE_EDGE_RISING 8>; brcm,msi-offset = <8>; }; + + isp: isp@1000880000 { + compatible = "brcm,bcm2712-pispbe", "raspberrypi,pispbe"; + reg = <0x10 0x00880000 0x0 0x4000>; + interrupts = ; + clocks = <&firmware_clocks 7>; + }; + + v3d: gpu@1002000000 { + compatible = "brcm,2712-v3d"; + reg = <0x10 0x02000000 0x00 0x4000>, + <0x10 0x02008000 0x00 0x6000>, + <0x10 0x02030800 0x00 0x0700>; + reg-names = "hub", "core0", "sms"; + + power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>; + resets = <&pm BCM2835_RESET_V3D>; + interrupts = , + ; + }; + }; + + vc4: gpu { + compatible = "brcm,bcm2712-vc6"; }; timer { diff --git a/arch/arm64/boot/dts/broadcom/rp1-common.dtsi b/arch/arm64/boot/dts/broadcom/rp1-common.dtsi index 5a815c379794..16f535939583 100644 --- a/arch/arm64/boot/dts/broadcom/rp1-common.dtsi +++ b/arch/arm64/boot/dts/broadcom/rp1-common.dtsi @@ -26,6 +26,83 @@ pci_ep_bus: pci-ep-bus@1 { <200000000>; // RP1_CLK_SYS }; + rp1_i2c0: i2c@40070000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x40070000 0x0 0x1000>; + interrupts = <7 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + + status = "disabled"; + }; + + rp1_i2c1: i2c@40074000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x40074000 0x0 0x1000>; + interrupts = <8 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + + status = "disabled"; + }; + + rp1_i2c2: i2c@40078000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x40078000 0x0 0x1000>; + interrupts = <9 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + + status = "disabled"; + }; + + rp1_i2c3: i2c@4007c000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x4007c000 0x0 0x1000>; + interrupts = <10 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + + status = "disabled"; + }; + + rp1_i2c4: i2c@40080000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x40080000 0x0 0x1000>; + interrupts = <11 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + + status = "disabled"; + }; + + rp1_i2c5: i2c@40084000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x40084000 0x0 0x1000>; + interrupts = <12 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + + status = "disabled"; + }; + + rp1_i2c6: i2c@40088000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x40088000 0x0 0x1000>; + interrupts = <13 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rp1_clocks RP1_CLK_SYS>; + i2c-scl-rising-time-ns = <65>; + i2c-scl-falling-time-ns = <100>; + + status = "disabled"; + }; + rp1_gpio: pinctrl@400d0000 { compatible = "raspberrypi,rp1-gpio"; reg = <0x00 0x400d0000 0x0 0xc000>, @@ -56,6 +133,34 @@ pci_ep_bus: pci-ep-bus@1 { #size-cells = <0>; }; + rp1_csi0: csi@40110000 { + compatible = "raspberrypi,rp1-cfe"; + reg = <0x0 0x40110000 0x0 0x100>, // CSI2 DMA address + <0x0 0x40114000 0x0 0x100>, // PHY/CSI Host address + <0x0 0x40120000 0x0 0x100>, // MIPI CFG address + <0x0 0x40124000 0x0 0x1000>; // PiSP FE address + interrupts = <47 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>; + assigned-clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>; + assigned-clock-rates = <25000000>; + + status = "disabled"; + }; + + rp1_csi1: csi@40128000 { + compatible = "raspberrypi,rp1-cfe"; + reg = <0x0 0x40128000 0x0 0x100>, // CSI2 DMA address + <0x0 0x4012c000 0x0 0x100>, // PHY/CSI Host address + <0x0 0x40138000 0x0 0x100>, // MIPI CFG address + <0x0 0x4013c000 0x0 0x1000>; // PiSP FE address + interrupts = <48 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>; + assigned-clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>; + assigned-clock-rates = <25000000>; + + status = "disabled"; + }; + rp1_usb0: usb@40200000 { compatible = "snps,dwc3"; reg = <0x00 0x40200000 0x0 0x100000>; diff --git a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts index 4dee8cd0b86d..e39c87774c12 100644 --- a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts +++ b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts @@ -36,6 +36,22 @@ }; +&fch_gpio0 { + status = "okay"; +}; + +&fch_gpio1 { + status = "okay"; +}; + +&fch_gpio2 { + status = "okay"; +}; + +&fch_gpio3 { + status = "okay"; +}; + &iomuxc { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hog>; @@ -86,6 +102,18 @@ status = "okay"; }; +&s5_gpio0 { + status = "okay"; +}; + +&s5_gpio1 { + status = "okay"; +}; + +&s5_gpio2 { + status = "okay"; +}; + &uart2 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/cix/sky1-power.h b/arch/arm64/boot/dts/cix/sky1-power.h new file mode 100644 index 000000000000..53f4a3af36b3 --- /dev/null +++ b/arch/arm64/boot/dts/cix/sky1-power.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2026 Cix Technology Group Co., Ltd. + */ + +#ifndef __SKY1_POWER_H__ +#define __SKY1_POWER_H__ + +/* The Rich OS need flow the macro */ +#define SKY1_PD_AUDIO 0 +#define SKY1_PD_PCIE_CTRL0 1 +#define SKY1_PD_PCIE_DUMMY 2 +#define SKY1_PD_PCIEHUB 3 +#define SKY1_PD_MMHUB 4 +#define SKY1_PD_MMHUB_SMMU 5 +#define SKY1_PD_DPU0 6 +#define SKY1_PD_DPU1 7 +#define SKY1_PD_DPU2 8 +#define SKY1_PD_DPU3 9 +#define SKY1_PD_DPU4 10 +#define SKY1_PD_VPU_TOP 11 +#define SKY1_PD_VPU_CORE0 12 +#define SKY1_PD_VPU_CORE1 13 +#define SKY1_PD_VPU_CORE2 14 +#define SKY1_PD_VPU_CORE3 15 +#define SKY1_PD_NPU_CORE0 16 +#define SKY1_PD_NPU_CORE1 17 +#define SKY1_PD_NPU_CORE2 18 +#define SKY1_PD_NPU_TOP 19 +#define SKY1_PD_ISP0 20 +#define SKY1_PD_GPU 21 + +#endif diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi index fb8c826bbc97..bb5cfb1f2113 100644 --- a/arch/arm64/boot/dts/cix/sky1.dtsi +++ b/arch/arm64/boot/dts/cix/sky1.dtsi @@ -6,6 +6,7 @@ #include #include +#include "sky1-power.h" / { interrupt-parent = <&gic>; @@ -168,6 +169,19 @@ #clock-cells = <1>; }; }; + + ap_to_tfa_scmi: scmi-1 { + compatible = "arm,scmi-smc"; + arm,smc-id = <0xc2000001>; + #address-cells = <1>; + #size-cells = <0>; + shmem = <&ap_tfa_scmi_mem>; + + smc_devpd: protocol@11 { + reg = <0x11>; + #power-domain-cells = <1>; + }; + }; }; pmu-a520 { @@ -185,6 +199,13 @@ method = "smc"; }; + s5_gpio_apb_clk: clock-100000000 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <100000000>; + clock-output-names = "s5_gpio_apb_clk"; + }; + soc@0 { compatible = "simple-bus"; ranges = <0 0 0 0 0x20 0>; @@ -348,6 +369,76 @@ status = "disabled"; }; + fch_gpio0: gpio-controller@4120000 { + compatible = "cdns,gpio-r1p02"; + reg = <0x0 0x4120000 0x0 0x1000>; + clocks = <&scmi_clk CLK_TREE_FCH_GPIO_APB>; + + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + fch_gpio1: gpio-controller@4130000 { + compatible = "cdns,gpio-r1p02"; + reg = <0x0 0x4130000 0x0 0x1000>; + clocks = <&scmi_clk CLK_TREE_FCH_GPIO_APB>; + + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + fch_gpio2: gpio-controller@4140000 { + compatible = "cdns,gpio-r1p02"; + reg = <0x0 0x4140000 0x0 0x1000>; + clocks = <&scmi_clk CLK_TREE_FCH_GPIO_APB>; + + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + fch_gpio3: gpio-controller@4150000 { + compatible = "cdns,gpio-r1p02"; + reg = <0x0 0x4150000 0x0 0x1000>; + clocks = <&scmi_clk CLK_TREE_FCH_GPIO_APB>; + + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + ngpios = <17>; + + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + syscon: syscon@4160000 { + compatible = "cix,sky1-system-control", "syscon"; + reg = <0x0 0x4160000 0x0 0x100>; + #reset-cells = <1>; + }; + iomuxc: pinctrl@4170000 { compatible = "cix,sky1-pinctrl"; reg = <0x0 0x04170000 0x0 0x1000>; @@ -428,6 +519,7 @@ #size-cells = <2>; bus-range = <0xc0 0xff>; device_type = "pci"; + power-domains = <&smc_devpd SKY1_PD_PCIE_CTRL0>; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; interrupt-map = <0 0 0 1 &gic 0 0 GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH 0>, @@ -568,10 +660,70 @@ }; }; + s5_syscon: syscon@16000000 { + compatible = "cix,sky1-s5-system-control", "syscon"; + reg = <0x0 0x16000000 0x0 0x1000>; + #reset-cells = <1>; + }; + + s5_gpio0: gpio-controller@16004000 { + compatible = "cdns,gpio-r1p02"; + reg = <0x0 0x16004000 0x0 0x1000>; + clocks = <&s5_gpio_apb_clk>; + + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + s5_gpio1: gpio-controller@16005000 { + compatible = "cdns,gpio-r1p02"; + reg = <0x0 0x16005000 0x0 0x1000>; + clocks = <&s5_gpio_apb_clk>; + + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + ngpios = <10>; + + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + + s5_gpio2: gpio-controller@16006000 { + compatible = "cdns,gpio-r1p02"; + reg = <0x0 0x16006000 0x0 0x1000>; + clocks = <&s5_gpio_apb_clk>; + + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + ngpios = <10>; + + interrupt-controller; + #interrupt-cells = <2>; + status = "disabled"; + }; + iomuxc_s5: pinctrl@16007000 { compatible = "cix,sky1-pinctrl-s5"; reg = <0x0 0x16007000 0x0 0x1000>; }; + + ap_tfa_scmi_mem: shmem@84380000 { + compatible = "arm,scmi-shmem"; + reg = <0x0 0x84380000 0x0 0x80>; + reg-io-width = <4>; + }; }; timer { diff --git a/arch/arm64/boot/dts/exynos/Makefile b/arch/arm64/boot/dts/exynos/Makefile index bcca63136557..76cc23acb9b2 100644 --- a/arch/arm64/boot/dts/exynos/Makefile +++ b/arch/arm64/boot/dts/exynos/Makefile @@ -8,7 +8,9 @@ dtb-$(CONFIG_ARCH_EXYNOS) += \ exynos5433-tm2e.dtb \ exynos7-espresso.dtb \ exynos7870-a2corelte.dtb \ + exynos7870-j5y17lte.dtb \ exynos7870-j6lte.dtb \ + exynos7870-j7xelte.dtb \ exynos7870-on7xelte.dtb \ exynos7885-jackpotlte.dtb \ exynos850-e850-96.dtb \ diff --git a/arch/arm64/boot/dts/exynos/axis/Makefile b/arch/arm64/boot/dts/exynos/axis/Makefile index ccf00de64016..da6a426516fc 100644 --- a/arch/arm64/boot/dts/exynos/axis/Makefile +++ b/arch/arm64/boot/dts/exynos/axis/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_ARCH_ARTPEC) += \ - artpec8-grizzly.dtb + artpec8-grizzly.dtb \ + artpec9-alfred.dtb diff --git a/arch/arm64/boot/dts/exynos/axis/artpec9-alfred.dts b/arch/arm64/boot/dts/exynos/axis/artpec9-alfred.dts new file mode 100644 index 000000000000..5a779f1acf3b --- /dev/null +++ b/arch/arm64/boot/dts/exynos/axis/artpec9-alfred.dts @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Axis ARTPEC-9 Alfred board device tree source + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * https://www.samsung.com + * Copyright (c) 2025 Axis Communications AB. + * https://www.axis.com + */ + +/dts-v1/; +#include "artpec9.dtsi" +#include "artpec9-pinctrl.dtsi" +#include + +/ { + model = "ARTPEC-9 alfred board"; + compatible = "axis,artpec9-alfred", "axis,artpec9"; + + aliases { + serial0 = &serial_0; + }; + + chosen { + stdout-path = &serial_0; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x80000000>; + }; +}; + +&osc_clk { + clock-frequency = <50000000>; +}; diff --git a/arch/arm64/boot/dts/exynos/axis/artpec9-pinctrl.dtsi b/arch/arm64/boot/dts/exynos/axis/artpec9-pinctrl.dtsi new file mode 100644 index 000000000000..a9fbdf7734d4 --- /dev/null +++ b/arch/arm64/boot/dts/exynos/axis/artpec9-pinctrl.dtsi @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Axis ARTPEC-9 SoC pin-mux and pin-config device tree source + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * https://www.samsung.com + * Copyright (c) 2025 Axis Communications AB. + * https://www.axis.com + */ + +#include "artpec-pinctrl.h" + +&pinctrl_fsys0 { + gpe0: gpe0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpe1: gpe1-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpe2: gpe2-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpe3: gpe3-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpe4: gpe4-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpf0: gpf0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpf1: gpf1-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpi0: gpi0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gps0: gps0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gps1: gps1-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; +}; + +&pinctrl_fsys1 { + gpu0: gpu0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + serial0_bus: serial0-bus-pins { + samsung,pins = "gpu0-0", "gpu0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; +}; + +&pinctrl_peric { + gpa0: gpa0-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpa1: gpa1-gpio-bank { + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; +}; diff --git a/arch/arm64/boot/dts/exynos/axis/artpec9.dtsi b/arch/arm64/boot/dts/exynos/axis/artpec9.dtsi new file mode 100644 index 000000000000..f8ed43c6e825 --- /dev/null +++ b/arch/arm64/boot/dts/exynos/axis/artpec9.dtsi @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Axis ARTPEC-9 SoC device tree source + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * https://www.samsung.com + * Copyright (c) 2025 Axis Communications AB. + * https://www.axis.com + */ + +#include +#include + +/ { + compatible = "axis,artpec9"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + pinctrl0 = &pinctrl_fsys0; + pinctrl1 = &pinctrl_fsys1; + pinctrl2 = &pinctrl_peric; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0>; + enable-method = "psci"; + cpu-idle-states = <&cpu_sleep>; + clocks = <&cmu_cpucl CLK_GOUT_CPUCL_CLUSTER_CPU>; + clock-names = "cpu"; + }; + + cpu1: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x100>; + enable-method = "psci"; + cpu-idle-states = <&cpu_sleep>; + }; + + cpu2: cpu@200 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x200>; + enable-method = "psci"; + cpu-idle-states = <&cpu_sleep>; + }; + + cpu3: cpu@300 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x300>; + enable-method = "psci"; + cpu-idle-states = <&cpu_sleep>; + }; + + cpu4: cpu@400 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x400>; + enable-method = "psci"; + cpu-idle-states = <&cpu_sleep>; + }; + + cpu5: cpu@500 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x500>; + enable-method = "psci"; + cpu-idle-states = <&cpu_sleep>; + }; + + idle-states { + entry-method = "psci"; + + cpu_sleep: cpu-sleep { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x0010000>; + local-timer-stop; + entry-latency-us = <300>; + exit-latency-us = <1200>; + min-residency-us = <2000>; + }; + }; + }; + + fin_pll: clock-finpll { + compatible = "fixed-factor-clock"; + clocks = <&osc_clk>; + #clock-cells = <0>; + clock-div = <2>; + clock-mult = <1>; + clock-output-names = "fin_pll"; + }; + + osc_clk: clock-osc { + /* XXTI */ + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "osc_clk"; + }; + + pmu { + compatible = "arm,cortex-a55-pmu"; + interrupts = ; + interrupt-affinity = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>, <&cpu4>, <&cpu5>; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + soc: soc@0 { + compatible = "simple-bus"; + ranges = <0x0 0x0 0x0 0x0 0x0 0x17000000>; + #address-cells = <2>; + #size-cells = <2>; + + cmu_imem: clock-controller@10010000 { + compatible = "axis,artpec9-cmu-imem"; + reg = <0x0 0x10010000 0x0 0x4000>; + #clock-cells = <1>; + clocks = <&fin_pll>, + <&cmu_cmu CLK_DOUT_CMU_IMEM_ACLK>, + <&cmu_cmu CLK_DOUT_CMU_IMEM_CA5>, + <&cmu_cmu CLK_DOUT_CMU_IMEM_JPEG>, + <&cmu_cmu CLK_DOUT_CMU_IMEM_SSS>; + clock-names = "fin_pll", "aclk", "ca5", "jpeg", "sss"; + }; + + timer@10040000 { + compatible = "axis,artpec9-mct", "samsung,exynos4210-mct"; + reg = <0x0 0x10040000 0x0 0x1000>; + clocks = <&fin_pll>, <&cmu_imem CLK_GOUT_IMEM_MCT0_PCLK>; + clock-names = "fin_pll", "mct"; + interrupts = , + , + , + , + , + , + , + , + , + , + , + ; + }; + + gic: interrupt-controller@10400000 { + compatible = "arm,gic-v3"; + reg = <0x0 0x10400000 0x0 0x00040000>, + <0x0 0x10440000 0x0 0x000c0000>; + #interrupt-cells = <3>; + interrupt-controller; + redistributor-stride = <0x0 0x20000>; + interrupts = ; + }; + + cmu_cpucl: clock-controller@12810000 { + compatible = "axis,artpec9-cmu-cpucl"; + reg = <0x0 0x12810000 0x0 0x4000>; + #clock-cells = <1>; + clocks = <&fin_pll>, + <&cmu_cmu CLK_DOUT_CMU_CPUCL_SWITCH>; + clock-names = "fin_pll", "switch"; + }; + + cmu_cmu: clock-controller@12c00000 { + compatible = "axis,artpec9-cmu-cmu"; + reg = <0x0 0x12c00000 0x0 0x4000>; + #clock-cells = <1>; + clocks = <&fin_pll>; + clock-names = "fin_pll"; + }; + + cmu_core: clock-controller@12c10000 { + compatible = "axis,artpec9-cmu-core"; + reg = <0x0 0x12c10000 0x0 0x4000>; + #clock-cells = <1>; + clocks = <&fin_pll>, + <&cmu_cmu CLK_DOUT_CMU_CORE_MAIN>; + clock-names = "fin_pll", "main"; + }; + + cmu_bus: clock-controller@13410000 { + compatible = "axis,artpec9-cmu-bus"; + reg = <0x0 0x13410000 0x0 0x4000>; + #clock-cells = <1>; + clocks = <&fin_pll>, + <&cmu_cmu CLK_DOUT_CMU_BUS>; + clock-names = "fin_pll", "bus"; + }; + + cmu_peri: clock-controller@14010000 { + compatible = "axis,artpec9-cmu-peri"; + reg = <0x0 0x14010000 0x0 0x4000>; + #clock-cells = <1>; + clocks = <&fin_pll>, + <&cmu_cmu CLK_DOUT_CMU_PERI_IP>, + <&cmu_cmu CLK_DOUT_CMU_PERI_DISP>; + clock-names = "fin_pll", "ip", "disp"; + }; + + pinctrl_peric: pinctrl@141f0000 { + compatible = "axis,artpec9-pinctrl"; + reg = <0x0 0x141f0000 0x0 0x1000>; + interrupts = ; + }; + + cmu_fsys0: clock-controller@14410000 { + compatible = "axis,artpec9-cmu-fsys0"; + reg = <0x0 0x14410000 0x0 0x4000>; + #clock-cells = <1>; + clocks = <&fin_pll>, + <&cmu_cmu CLK_DOUT_CMU_FSYS0_BUS>, + <&cmu_cmu CLK_DOUT_CMU_FSYS0_IP>; + clock-names = "fin_pll", "bus", "ip"; + }; + + pinctrl_fsys0: pinctrl@14430000 { + compatible = "axis,artpec9-pinctrl"; + reg = <0x0 0x14430000 0x0 0x1000>; + interrupts = ; + }; + + cmu_fsys1: clock-controller@14c10000 { + compatible = "axis,artpec9-cmu-fsys1"; + reg = <0x0 0x14c10000 0x0 0x4000>; + #clock-cells = <1>; + clocks = <&fin_pll>, + <&cmu_cmu CLK_DOUT_CMU_FSYS1_SCAN0>, + <&cmu_cmu CLK_DOUT_CMU_FSYS1_SCAN1>, + <&cmu_cmu CLK_DOUT_CMU_FSYS1_BUS>; + clock-names = "fin_pll", "scan0", "scan1", "bus"; + }; + + pinctrl_fsys1: pinctrl@14c30000 { + compatible = "axis,artpec9-pinctrl"; + reg = <0x0 0x14c30000 0x0 0x1000>; + interrupts = ; + }; + + pmu_system_controller: system-controller@14c40000 { + compatible = "axis,artpec9-pmu", "samsung,exynos7-pmu", "syscon"; + reg = <0x0 0x14c40000 0x0 0x10000>; + }; + + serial_0: serial@14c70000 { + compatible = "axis,artpec9-uart", "samsung,exynos8895-uart"; + reg = <0x0 0x14c70000 0x0 0x100>; + clocks = <&cmu_fsys1 CLK_GOUT_FSYS1_UART0_PCLK>, + <&cmu_fsys1 CLK_GOUT_FSYS1_UART0_SCLK_UART>; + clock-names = "uart", "clk_uart_baud0"; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&serial0_bus>; + samsung,uart-fifosize = <64>; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + }; +}; diff --git a/arch/arm64/boot/dts/exynos/exynos7870-a2corelte.dts b/arch/arm64/boot/dts/exynos/exynos7870-a2corelte.dts index 6f40ca4350ed..0888cd2faca4 100644 --- a/arch/arm64/boot/dts/exynos/exynos7870-a2corelte.dts +++ b/arch/arm64/boot/dts/exynos/exynos7870-a2corelte.dts @@ -158,9 +158,6 @@ }; &hsi2c0 { - #address-cells = <1>; - #size-cells = <0>; - status = "okay"; pmic@66 { @@ -394,9 +391,6 @@ }; &i2c5 { - #address-cells = <1>; - #size-cells = <0>; - samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <400000>; @@ -428,9 +422,6 @@ }; &i2c6 { - #address-cells = <1>; - #size-cells = <0>; - samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <400000>; diff --git a/arch/arm64/boot/dts/exynos/exynos7870-j5y17lte.dts b/arch/arm64/boot/dts/exynos/exynos7870-j5y17lte.dts new file mode 100644 index 000000000000..36c327668db5 --- /dev/null +++ b/arch/arm64/boot/dts/exynos/exynos7870-j5y17lte.dts @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Samsung Galaxy J5 (2017) (j5y17lte) device tree source + * + * Copyright (c) 2024 Andras Sebok + */ + +/dts-v1/; +#include "exynos7870.dtsi" +#include +#include +#include + +/ { + model = "Samsung Galaxy J5 (2017)"; + compatible = "samsung,j5y17lte", "samsung,exynos7870"; + chassis-type = "handset"; + + aliases { + mmc0 = &mmc0; + mmc1 = &mmc1; + mmc2 = &mmc2; + serial0 = &serial0; + serial1 = &serial1; + serial2 = &serial2; + }; + + chosen { + #address-cells = <2>; + #size-cells = <1>; + ranges; + + stdout-path = &serial2; + + framebuffer@67000000 { + compatible = "simple-framebuffer"; + reg = <0x0 0x67000000 (720 * 1280 * 4)>; + width = <720>; + height = <1280>; + stride = <(720 * 4)>; + format = "a8r8g8b8"; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&key_power &key_voldown &key_volup>; + + key-home { + interrupt-parent = <&gpa1>; + linux,code = ; + label = "gpio-keys: KEY_HOMEPAGE"; + gpios = <&gpa1 7 GPIO_ACTIVE_LOW>; + }; + + key-power { + interrupt-parent = <&gpa0>; + linux,code = ; + label = "gpio-keys: KEY_POWER"; + gpios = <&gpa0 0 GPIO_ACTIVE_LOW>; + }; + + key-voldown { + interrupt-parent = <&gpa2>; + linux,code = ; + label = "gpio-keys: KEY_VOLUMEDOWN"; + gpios = <&gpa2 1 GPIO_ACTIVE_LOW>; + }; + + key-volup { + interrupt-parent = <&gpa2>; + linux,code = ; + label = "gpio-keys: KEY_VOLUMEUP"; + gpios = <&gpa2 0 GPIO_ACTIVE_LOW>; + }; + }; + + memory@40000000 { + device_type = "memory"; + reg = <0x0 0x40000000 0x3e400000>, + <0x0 0x80000000 0x40000000>; + }; + + pwrseq_mmc1: pwrseq-mmc1 { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpd3 6 GPIO_ACTIVE_LOW>; + }; + + vdd_fixed_mmc2: regulator-fixed-mmc2 { + compatible = "regulator-fixed"; + regulator-name = "vdd_fixed_mmc2"; + regulator-max-microvolt = <2800000>; + regulator-min-microvolt = <2800000>; + + gpio = <&gpc0 0 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <1>; + ranges; + + ramoops@46e00000 { + compatible = "ramoops"; + reg = <0x0 0x46e00000 0x8000>; + console-size = <0x4000>; + pmsg-size = <0x4000>; + }; + + framebuffer@67000000 { + reg = <0x0 0x67000000 (720 * 1280 * 4)>; + no-map; + }; + }; + + vibrator { + compatible = "regulator-haptic"; + haptic-supply = <&vdd_ldo32>; + min-microvolt = <3300000>; + max-microvolt = <3300000>; + }; +}; + +&gpu { + status = "okay"; +}; + +&hsi2c0 { + status = "okay"; + + pmic@66 { + compatible = "samsung,s2mpu05-pmic"; + reg = <0x66>; + + interrupt-parent = <&gpa0>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-names = "default"; + pinctrl-0 = <&pmic_irq>; + + regulators { + vdd_buck1: buck1 { + regulator-name = "vdd_buck1"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_buck2: buck2 { + regulator-name = "vdd_buck2"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_buck3: buck3 { + regulator-name = "vdd_buck3"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_buck4: buck4 { + regulator-name = "vdd_buck4"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1500000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_buck5: buck5 { + regulator-name = "vdd_buck5"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2100000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo1: ldo1 { + regulator-name = "vdd_ldo1"; + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo2: ldo2 { + regulator-name = "vdd_ldo2"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2800000>; + regulator-ramp-delay = <12000>; + }; + + vdd_ldo3: ldo3 { + regulator-name = "vdd_ldo3"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2375000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo4: ldo4 { + regulator-name = "vdd_ldo4"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo5: ldo5 { + regulator-name = "vdd_ldo5"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo6: ldo6 { + regulator-name = "vdd_ldo6"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo7: ldo7 { + regulator-name = "vdd_ldo7"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2375000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo8: ldo8 { + regulator-name = "vdd_ldo8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3375000>; + regulator-ramp-delay = <12000>; + }; + + vdd_ldo9: ldo9 { + regulator-name = "vdd_ldo9"; + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo10: ldo10 { + regulator-name = "vdd_ldo10"; + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo25: ldo25 { + regulator-name = "vdd_ldo25"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2375000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo26: ldo26 { + regulator-name = "vdd_ldo26"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3375000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo27: ldo27 { + regulator-name = "vdd_ldo27"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2375000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo29: ldo29 { + regulator-name = "vdd_ldo29"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo30: ldo30 { + regulator-name = "vdd_ldo30"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo31: ldo31 { + regulator-name = "vdd_ldo31"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-ramp-delay = <12000>; + regulator-boot-on; + regulator-always-on; + }; + + vdd_ldo32: ldo32 { + regulator-name = "vdd_ldo32"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-ramp-delay = <12000>; + }; + + vdd_ldo33: ldo33 { + regulator-name = "vdd_ldo33"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-ramp-delay = <12000>; + }; + + vdd_ldo34: ldo34 { + regulator-name = "vdd_ldo34"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + vdd_ldo35: ldo35 { + regulator-name = "vdd_ldo35"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + }; + }; +}; + +&i2c2 { + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <400000>; + + status = "okay"; + + touchscreen@50 { + compatible = "imagis,ist3038h"; + reg = <0x50>; + + interrupt-parent = <&gpa0>; + interrupts = <6 IRQ_TYPE_EDGE_FALLING>; + + pinctrl-names = "default"; + pinctrl-0 = <&touch_irq>; + + touchscreen-size-x = <720>; + touchscreen-size-y = <1280>; + + vdd-supply = <&vdd_ldo34>; + }; +}; + +&mmc0 { + pinctrl-names = "default"; + pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_rdqs &sd0_bus1 &sd0_bus4 &sd0_bus8>; + + vmmc-supply = <&vdd_ldo26>; + vqmmc-supply = <&vdd_ldo27>; + + fifo-depth = <64>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <0 4>; + samsung,dw-mshc-ddr-timing = <2 4>; + non-removable; + + status = "okay"; +}; + +&mmc1 { + #address-cells = <1>; + #size-cells = <0>; + + pinctrl-names = "default"; + pinctrl-0 = <&sd1_clk &sd1_cmd &sd1_bus1 &sd1_bus4>; + + mmc-pwrseq = <&pwrseq_mmc1>; + + bus-width = <4>; + fifo-depth = <64>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <0 3>; + samsung,dw-mshc-ddr-timing = <1 2>; + non-removable; + cap-sd-highspeed; + cap-sdio-irq; + + status = "okay"; + + wifi@0 { + compatible = "brcm,bcm43455-fmac", "brcm,bcm4329-fmac"; + reg = <0x0>; + + interrupt-names = "host-wake"; + interrupt-parent = <&gpa2>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + + reset-gpios = <&gpd3 6 GPIO_ACTIVE_LOW>; + }; +}; + +&oscclk { + clock-frequency = <26000000>; +}; + +&pinctrl_alive { + accel_irq: accel-irq-pins { + samsung,pins = "gpa2-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + dwmmc2_irq: dwmmc2-irq-pins { + samsung,pins = "gpa0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + fuel_irq: fuel-irq-pins { + samsung,pins = "gpa0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + hall_irq: hall-irq-pins { + samsung,pins = "gpa1-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + key_power: key-power-pins { + samsung,pins = "gpa0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + key_voldown: key-voldown-pins { + samsung,pins = "gpa2-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + key_volup: key-volup-pins { + samsung,pins = "gpa2-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pmic_irq: pmic-irq-pins { + samsung,pins = "gpa0-2"; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + touch_irq: touch-irq-pins { + samsung,pins = "gpa0-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + wlan_hostwake: wlan-hostwake-pins { + samsung,pins = "gpa2-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; +}; + +&pinctrl_top { + wlan_enable: wlan-enable-pins { + samsung,pins = "gpd3-6"; + samsung,pin-function = ; + samsung,pin-con-pdn = ; + samsung,pin-pud-pdn = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + samsung,pin-val = <0>; + }; +}; + +&serial2 { + status = "okay"; +}; + +&usbdrd { + vdd33-supply = <&vdd_ldo8>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts b/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts index 09f2367cfec9..de30d0970336 100644 --- a/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts +++ b/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts @@ -383,9 +383,6 @@ }; &i2c5 { - #address-cells = <1>; - #size-cells = <0>; - samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <400000>; @@ -409,9 +406,6 @@ }; &i2c6 { - #address-cells = <1>; - #size-cells = <0>; - samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <400000>; diff --git a/arch/arm64/boot/dts/exynos/exynos7870-j7xelte.dts b/arch/arm64/boot/dts/exynos/exynos7870-j7xelte.dts new file mode 100644 index 000000000000..079b2b581307 --- /dev/null +++ b/arch/arm64/boot/dts/exynos/exynos7870-j7xelte.dts @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Samsung Galaxy J7 (2016) (j7xelte) device tree source + * + * Copyright (c) 2025 Rayan Marzouk + */ + +/dts-v1/; +#include "exynos7870.dtsi" +#include +#include +#include + +/ { + model = "Samsung Galaxy J7 (2016)"; + compatible = "samsung,j7xelte", "samsung,exynos7870"; + chassis-type = "handset"; + + aliases { + mmc0 = &mmc0; + mmc1 = &mmc1; + mmc2 = &mmc2; + serial0 = &serial0; + serial1 = &serial1; + serial2 = &serial2; + }; + + chosen { + #address-cells = <2>; + #size-cells = <1>; + ranges; + + stdout-path = &serial2; + + framebuffer@67000000 { + compatible = "simple-framebuffer"; + reg = <0x0 0x67000000 (720 * 1280 * 4)>; + width = <720>; + height = <1280>; + stride = <(720 * 4)>; + format = "a8r8g8b8"; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + label = "GPIO Keys"; + + pinctrl-names = "default"; + pinctrl-0 = <&key_home &key_power &key_voldown &key_volup>; + + key-home { + label = "Home Key"; + gpios = <&gpa1 7 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + key-power { + label = "Power Key"; + gpios = <&gpa0 0 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + key-voldown { + label = "Volume Down Key"; + gpios = <&gpa2 1 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + key-volup { + label = "Volume Up Key"; + gpios = <&gpa2 0 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + memory@40000000 { + device_type = "memory"; + reg = <0x0 0x40000000 0x3e400000>, + <0x0 0x80000000 0x40000000>; + }; + + pwrseq_mmc1: pwrseq-mmc1 { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpd3 6 GPIO_ACTIVE_LOW>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <1>; + ranges; + + ramoops@46e00000 { + compatible = "ramoops"; + reg = <0x0 0x46e00000 0x8000>; + console-size = <0x4000>; + pmsg-size = <0x4000>; + }; + + cont_splash_mem: framebuffer@67000000 { + reg = <0x0 0x67000000 0x00384000>; + no-map; + }; + }; + + vdd_fixed_mmc2: regulator-fixed-mmc2 { + compatible = "regulator-fixed"; + regulator-name = "vdd_fixed_mmc2"; + regulator-max-microvolt = <2800000>; + regulator-min-microvolt = <2800000>; + gpio = <&gpc0 0 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + vibrator { + compatible = "regulator-haptic"; + haptic-supply = <&vdd_ldo32>; + min-microvolt = <3300000>; + max-microvolt = <3300000>; + }; +}; + +&gpu { + status = "okay"; +}; + +&hsi2c0 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + + pmic@66 { + compatible = "samsung,s2mpu05-pmic"; + reg = <0x66>; + interrupt-parent = <&gpa0>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-names = "default"; + pinctrl-0 = <&pmic_irq>; + + regulators { + vdd_buck1: buck1 { + regulator-name = "vdd_buck1"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_buck2: buck2 { + regulator-name = "vdd_buck2"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_buck3: buck3 { + regulator-name = "vdd_buck3"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_buck4: buck4 { + regulator-name = "vdd_buck4"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_buck5: buck5 { + regulator-name = "vdd_buck5"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2100000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo1: ldo1 { + regulator-name = "vdd_ldo1"; + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo2: ldo2 { + regulator-name = "vdd_ldo2"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2800000>; + }; + + vdd_ldo3: ldo3 { + regulator-name = "vdd_ldo3"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2375000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo4: ldo4 { + regulator-name = "vdd_ldo4"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo5: ldo5 { + regulator-name = "vdd_ldo5"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo6: ldo6 { + regulator-name = "vdd_ldo6"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo7: ldo7 { + regulator-name = "vdd_ldo7"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2375000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo8: ldo8 { + regulator-name = "vdd_ldo8"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + vdd_ldo9: ldo9 { + regulator-name = "vdd_ldo9"; + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo10: ldo10 { + regulator-name = "vdd_ldo10"; + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo25: ldo25 { + regulator-name = "vdd_ldo25"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2375000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo26: ldo26 { + regulator-name = "vdd_ldo26"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3375000>; + }; + + vdd_ldo27: ldo27 { + regulator-name = "vdd_ldo27"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2375000>; + }; + + vdd_ldo29: ldo29 { + regulator-name = "vdd_ldo29"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo30: ldo30 { + regulator-name = "vdd_ldo30"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo31: ldo31 { + regulator-name = "vdd_ldo31"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo32: ldo32 { + regulator-name = "vdd_ldo32"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + vdd_ldo33: ldo33 { + regulator-name = "vdd_ldo33"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo34: ldo34 { + regulator-name = "vdd_ldo34"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_ldo35: ldo35 { + regulator-name = "vdd_ldo35"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; +}; + +&i2c1 { + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <400000>; + + status = "okay"; + + touchscreen@48 { + compatible = "melfas,mip4_ts"; + reg = <0x48>; + + interrupt-parent = <&gpc3>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + + pinctrl-names = "default"; + pinctrl-0 = <&touch_irq>; + }; +}; + +&mmc0 { + pinctrl-names = "default"; + pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_rdqs &sd0_bus1 &sd0_bus4 &sd0_bus8>; + + vmmc-supply = <&vdd_ldo26>; + vqmmc-supply = <&vdd_ldo27>; + + fifo-depth = <64>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <0 4>; + samsung,dw-mshc-ddr-timing = <2 4>; + non-removable; + + status = "okay"; +}; + +&mmc1 { + #address-cells = <1>; + #size-cells = <0>; + + pinctrl-names = "default"; + pinctrl-0 = <&sd1_clk &sd1_cmd &sd1_bus1 &sd1_bus4>; + + mmc-pwrseq = <&pwrseq_mmc1>; + + bus-width = <4>; + fifo-depth = <64>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <0 3>; + samsung,dw-mshc-ddr-timing = <1 2>; + non-removable; + cap-sd-highspeed; + cap-sdio-irq; + + status = "okay"; + + wifi@1 { + compatible = "brcm,bcm43430a1-fmac", "brcm,bcm4329-fmac"; + reg = <0x1>; + + interrupt-names = "host-wake"; + interrupt-parent = <&gpa2>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; + + reset-gpios = <&gpd3 6 GPIO_ACTIVE_LOW>; + }; +}; + +&mmc2 { + pinctrl-names = "default"; + pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_bus1 &sd2_bus4 &dwmmc2_irq>; + + vmmc-supply = <&vdd_fixed_mmc2>; + vqmmc-supply = <&vdd_ldo2>; + + bus-width = <4>; + card-detect-delay = <200>; + fifo-depth = <64>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <0 3>; + samsung,dw-mshc-ddr-timing = <1 2>; + sd-uhs-sdr50; + sd-uhs-sdr104; + broken-cd; + disable-wp; + + status = "okay"; +}; + +&oscclk { + clock-frequency = <26000000>; +}; + +&pinctrl_alive { + dwmmc2_irq: dwmmc2-irq-pins { + samsung,pins = "gpa0-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + fuel_irq: fuel-irq-pins { + samsung,pins = "gpa0-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + key_home: key-home-pins { + samsung,pins = "gpa1-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + key_power: key-power-pins { + samsung,pins = "gpa0-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + key_voldown: key-voldown-pins { + samsung,pins = "gpa2-1"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + key_volup: key-volup-pins { + samsung,pins = "gpa2-0"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + + pmic_irq: pmic-irq-pins { + samsung,pins = "gpa0-2"; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; +}; + +&pinctrl_touch { + touch_irq: touch-irq-pins { + samsung,pins = "gpc3-2"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; +}; + +&serial2 { + status = "okay"; +}; + +&usbdrd { + vdd33-supply = <&vdd_ldo8>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/exynos/exynos7870-on7xelte.dts b/arch/arm64/boot/dts/exynos/exynos7870-on7xelte.dts index 29e124c72e9d..29be4764f84d 100644 --- a/arch/arm64/boot/dts/exynos/exynos7870-on7xelte.dts +++ b/arch/arm64/boot/dts/exynos/exynos7870-on7xelte.dts @@ -398,9 +398,6 @@ }; &i2c1 { - #address-cells = <1>; - #size-cells = <0>; - samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <400000>; @@ -438,9 +435,6 @@ }; &i2c7 { - #address-cells = <1>; - #size-cells = <0>; - samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <400000>; @@ -464,9 +458,6 @@ }; &i2c8 { - #address-cells = <1>; - #size-cells = <0>; - samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <400000>; diff --git a/arch/arm64/boot/dts/exynos/exynos7870.dtsi b/arch/arm64/boot/dts/exynos/exynos7870.dtsi index 2827e10d6962..19f9ae783587 100644 --- a/arch/arm64/boot/dts/exynos/exynos7870.dtsi +++ b/arch/arm64/boot/dts/exynos/exynos7870.dtsi @@ -220,6 +220,9 @@ clock-names = "hsi2c"; clocks = <&cmu_mif CLK_GOUT_MIF_HSI2C_IPCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -422,6 +425,9 @@ clock-names = "i2c"; clocks = <&cmu_peri CLK_GOUT_PERI_I2C0_PCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -437,6 +443,9 @@ clock-names = "i2c"; clocks = <&cmu_peri CLK_GOUT_PERI_I2C1_PCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -452,6 +461,9 @@ clock-names = "i2c"; clocks = <&cmu_peri CLK_GOUT_PERI_I2C2_PCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -467,6 +479,9 @@ clock-names = "i2c"; clocks = <&cmu_peri CLK_GOUT_PERI_I2C3_PCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -482,6 +497,9 @@ clock-names = "i2c"; clocks = <&cmu_peri CLK_GOUT_PERI_I2C4_PCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -497,6 +515,9 @@ clock-names = "i2c"; clocks = <&cmu_peri CLK_GOUT_PERI_I2C5_PCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -512,6 +533,9 @@ clock-names = "i2c"; clocks = <&cmu_peri CLK_GOUT_PERI_I2C6_PCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -527,6 +551,9 @@ clock-names = "hsi2c"; clocks = <&cmu_peri CLK_GOUT_PERI_HSI2C1_IPCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -542,6 +569,9 @@ clock-names = "hsi2c"; clocks = <&cmu_peri CLK_GOUT_PERI_HSI2C2_IPCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -557,6 +587,9 @@ clock-names = "hsi2c"; clocks = <&cmu_peri CLK_GOUT_PERI_HSI2C3_IPCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -572,6 +605,9 @@ clock-names = "i2c"; clocks = <&cmu_peri CLK_GOUT_PERI_I2C7_PCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -587,6 +623,9 @@ clock-names = "i2c"; clocks = <&cmu_peri CLK_GOUT_PERI_I2C8_PCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -602,6 +641,9 @@ clock-names = "hsi2c"; clocks = <&cmu_peri CLK_GOUT_PERI_HSI2C4_IPCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -617,6 +659,9 @@ clock-names = "hsi2c"; clocks = <&cmu_peri CLK_GOUT_PERI_HSI2C5_IPCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -632,6 +677,9 @@ clock-names = "hsi2c"; clocks = <&cmu_peri CLK_GOUT_PERI_HSI2C6_IPCLK>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; diff --git a/arch/arm64/boot/dts/exynos/exynos8895-dreamlte.dts b/arch/arm64/boot/dts/exynos/exynos8895-dreamlte.dts index 61e064af3337..305dc72f93d8 100644 --- a/arch/arm64/boot/dts/exynos/exynos8895-dreamlte.dts +++ b/arch/arm64/boot/dts/exynos/exynos8895-dreamlte.dts @@ -103,8 +103,6 @@ }; &hsi2c_23 { - #address-cells = <1>; - #size-cells = <0>; status = "okay"; touchscreen@48 { diff --git a/arch/arm64/boot/dts/exynos/exynos8895.dtsi b/arch/arm64/boot/dts/exynos/exynos8895.dtsi index f92d2a8a20a2..ff114cd6c9ea 100644 --- a/arch/arm64/boot/dts/exynos/exynos8895.dtsi +++ b/arch/arm64/boot/dts/exynos/exynos8895.dtsi @@ -266,6 +266,10 @@ interrupts = ; pinctrl-0 = <&hsi2c5_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -305,6 +309,10 @@ interrupts = ; pinctrl-0 = <&hsi2c6_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -328,6 +336,10 @@ interrupts = ; pinctrl-0 = <&hsi2c5_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -367,6 +379,10 @@ interrupts = ; pinctrl-0 = <&hsi2c8_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -390,6 +406,10 @@ interrupts = ; pinctrl-0 = <&hsi2c9_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -429,6 +449,10 @@ interrupts = ; pinctrl-0 = <&hsi2c10_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -452,6 +476,10 @@ interrupts = ; pinctrl-0 = <&hsi2c11_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -491,6 +519,10 @@ interrupts = ; pinctrl-0 = <&hsi2c12_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -565,6 +597,10 @@ interrupts = ; pinctrl-0 = <&hsi2c13_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -604,6 +640,10 @@ interrupts = ; pinctrl-0 = <&hsi2c14_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -627,6 +667,10 @@ interrupts = ; pinctrl-0 = <&hsi2c15_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -666,6 +710,10 @@ interrupts = ; pinctrl-0 = <&hsi2c16_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -689,6 +737,10 @@ interrupts = ; pinctrl-0 = <&hsi2c17_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -728,6 +780,10 @@ interrupts = ; pinctrl-0 = <&hsi2c18_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -751,6 +807,10 @@ interrupts = ; pinctrl-0 = <&hsi2c19_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -790,6 +850,10 @@ interrupts = ; pinctrl-0 = <&hsi2c20_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -813,6 +877,10 @@ interrupts = ; pinctrl-0 = <&hsi2c21_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -852,6 +920,10 @@ interrupts = ; pinctrl-0 = <&hsi2c22_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -875,6 +947,10 @@ interrupts = ; pinctrl-0 = <&hsi2c23_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -914,6 +990,10 @@ interrupts = ; pinctrl-0 = <&hsi2c24_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -937,6 +1017,10 @@ interrupts = ; pinctrl-0 = <&hsi2c25_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -976,6 +1060,10 @@ interrupts = ; pinctrl-0 = <&hsi2c26_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -999,6 +1087,10 @@ interrupts = ; pinctrl-0 = <&hsi2c27_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -1038,6 +1130,10 @@ interrupts = ; pinctrl-0 = <&hsi2c28_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -1061,6 +1157,10 @@ interrupts = ; pinctrl-0 = <&hsi2c29_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -1100,6 +1200,10 @@ interrupts = ; pinctrl-0 = <&hsi2c30_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -1123,6 +1227,10 @@ interrupts = ; pinctrl-0 = <&hsi2c31_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -1162,6 +1270,10 @@ interrupts = ; pinctrl-0 = <&hsi2c32_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; }; @@ -1180,6 +1292,10 @@ interrupts = ; pinctrl-0 = <&hsi2c1_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -1191,6 +1307,10 @@ interrupts = ; pinctrl-0 = <&hsi2c2_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -1202,6 +1322,10 @@ interrupts = ; pinctrl-0 = <&hsi2c3_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; @@ -1213,6 +1337,10 @@ interrupts = ; pinctrl-0 = <&hsi2c4_bus>; pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; }; diff --git a/arch/arm64/boot/dts/exynos/exynosautov920.dtsi b/arch/arm64/boot/dts/exynos/exynosautov920.dtsi index 02bf2ca52fdc..0bf7c4cb9846 100644 --- a/arch/arm64/boot/dts/exynos/exynosautov920.dtsi +++ b/arch/arm64/boot/dts/exynos/exynosautov920.dtsi @@ -1473,6 +1473,19 @@ "noc"; }; + cmu_g3d: clock-controller@1a000000 { + compatible = "samsung,exynosautov920-cmu-g3d"; + reg = <0x1a000000 0x8000>; + #clock-cells = <1>; + + clocks = <&xtcxo>, + <&cmu_top DOUT_CLKCMU_G3D_SWITCH>, + <&cmu_top DOUT_CLKCMU_G3D_NOCP>; + clock-names = "oscclk", + "switch", + "nocp"; + }; + pinctrl_aud: pinctrl@1a460000 { compatible = "samsung,exynosautov920-pinctrl"; reg = <0x1a460000 0x10000>; diff --git a/arch/arm64/boot/dts/exynos/google/gs101-oriole.dts b/arch/arm64/boot/dts/exynos/google/gs101-oriole.dts index 8df42bedbc03..36721adcaa23 100644 --- a/arch/arm64/boot/dts/exynos/google/gs101-oriole.dts +++ b/arch/arm64/boot/dts/exynos/google/gs101-oriole.dts @@ -8,6 +8,7 @@ /dts-v1/; +#include #include "gs101-pixel-common.dtsi" / { @@ -15,6 +16,27 @@ compatible = "google,gs101-oriole", "google,gs101"; }; +&acpm_ipc { + pmic-1 { + regulators { + ldo14m { + /* PLL */ + regulator-name = "avdd18_tcxo"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + regulator-always-on; + samsung,ext-control = ; + }; + + ldo31m { + regulator-name = "nfc"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + }; + }; + }; +}; + &cont_splash_mem { reg = <0x0 0xfac00000 (1080 * 2400 * 4)>; status = "okay"; diff --git a/arch/arm64/boot/dts/exynos/google/gs101-pixel-common.dtsi b/arch/arm64/boot/dts/exynos/google/gs101-pixel-common.dtsi index 93892adaa679..5227cd4e314b 100644 --- a/arch/arm64/boot/dts/exynos/google/gs101-pixel-common.dtsi +++ b/arch/arm64/boot/dts/exynos/google/gs101-pixel-common.dtsi @@ -10,6 +10,7 @@ #include #include +#include #include #include "gs101-pinctrl.h" #include "gs101.dtsi" @@ -28,6 +29,8 @@ framebuffer0: framebuffer-0 { compatible = "simple-framebuffer"; memory-region = <&cont_splash_mem>; + vci-supply = <&s2mpg10_ldo22m>; + vddi-supply = <&s2mpg11_bucka>; /* format properties to be added by actual board */ status = "disabled"; }; @@ -101,7 +104,7 @@ }; &acpm_ipc { - pmic { + pmic-1 { compatible = "samsung,s2mpg10-pmic"; interrupts-extended = <&gpa0 6 IRQ_TYPE_LEVEL_LOW>; pinctrl-names = "default"; @@ -109,6 +112,22 @@ system-power-controller; wakeup-source; + vinl1m-supply = <&s2mpg11_buck3s>; + vinl2m-supply = <&s2mpg11_buck3s>; + vinl3m-supply = <&s2mpg10_buck8m>; + vinl4m-supply = <&s2mpg10_buck9m>; + vinl5m-supply = <&s2mpg10_buck9m>; + vinl6m-supply = <&s2mpg10_buck9m>; + vinl7m-supply = <&s2mpg11_buck6s>; + vinl8m-supply = <&s2mpg11_buck6s>; + vinl9m-supply = <&s2mpg11_buck7s>; + vinl10m-supply = <&s2mpg11_buck7s>; + vinl11m-supply = <&s2mpg11_buck7s>; + vinl12m-supply = <&s2mpg11_bucka>; + vinl13m-supply = <&s2mpg11_bucka>; + vinl14m-supply = <&s2mpg11_buckboost>; + vinl15m-supply = <&s2mpg11_buckboost>; + clocks { compatible = "samsung,s2mpg10-clk"; #clock-cells = <1>; @@ -117,6 +136,489 @@ }; regulators { + buck1m { + regulator-name = "vdd_mif"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <6250>; + regulator-always-on; + samsung,ext-control = ; + }; + + buck2m { + regulator-name = "vdd_cpucl2"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + samsung,ext-control = ; + }; + + buck3m { + regulator-name = "vdd_cpucl1"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + samsung,ext-control = ; + }; + + buck4m { + regulator-name = "vdd_cpucl0"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + samsung,ext-control = ; + }; + + buck5m { + regulator-name = "vdd_int"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + samsung,ext-control = ; + }; + + buck6m { + regulator-name = "vdd_cpucl2_m"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + samsung,ext-control = ; + }; + + buck7m { + /* GPU */ + regulator-name = "vdd_int_m"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <6250>; + regulator-always-on; + samsung,ext-control = ; + }; + + s2mpg10_buck8m: buck8m { + regulator-name = "lldo2"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <6250>; + }; + + s2mpg10_buck9m: buck9m { + regulator-name = "lldo3"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <6250>; + }; + + buck10m { + regulator-name = "vdd_tpu"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + samsung,ext-control = ; + }; + + ldo1m { + /* ALIVE, AOC PLL */ + regulator-name = "vdd_l1m_alive"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <6250>; + regulator-always-on; + }; + + ldo2m { + /* lots, DDR */ + regulator-name = "vdd_l2m_alive"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + regulator-always-on; + }; + + ldo3m { + /* AVDD: MIPI CSI & DSI, PLL: CPUCL SHARED TPU UFS */ + regulator-name = "ldo3m"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + samsung,ext-control = ; + }; + + ldo4m { + /* AVDD: MIPI CSI & DSI, UFS, OTP, TS_SUB, TS_TOP, XOTP */ + regulator-name = "ldo4m"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + regulator-always-on; + samsung,ext-control = ; + }; + + ldo5m { + /* VDD: ADD, AVDD: TCXO & TCXO_FAR */ + regulator-name = "avdd075_tcxo"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + samsung,ext-control = ; + }; + + ldo6m { + /* PLL CPUCL & MIFx, UFS clk, MIPI DSI */ + regulator-name = "vdd_pll"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + samsung,ext-control = ; + }; + + ldo7m { + /* IO (HSI (USB)) */ + regulator-name = "vdd_hsi"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <6250>; + /* + * TODO: link to HSI power domain, without this, + * Linux hangs during USB access. + */ + regulator-always-on; + }; + + ldo8m { + regulator-name = "vdd085_usb"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + }; + + ldo9m { + regulator-name = "vdd18_usb"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + regulator-always-on; + }; + + ldo10m { + regulator-name = "vdd33_usb"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3350000>; + regulator-always-on; + }; + + ldo11m { + regulator-name = "vdd_cpucl1_m"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <6250>; + regulator-always-on; + samsung,ext-control = ; + }; + + ldo12m { + regulator-name = "vdd_cpucl0_m"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <6250>; + regulator-always-on; + samsung,ext-control = ; + }; + + ldo13m { + regulator-name = "vdd_tpu_m"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + samsung,ext-control = ; + }; + + /* ldo14m is board specific */ + + ldo15m { + regulator-name = "vdd_slc_m"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + }; + + ldo16m { + regulator-name = "vdd085_pcie0"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1300000>; + }; + + ldo17m { + regulator-name = "vdd085_pcie1"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1300000>; + }; + + ldo18m { + regulator-name = "vdd18_pcie0"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + }; + + ldo19m { + regulator-name = "vdd18_pcie1"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + }; + + ldo20m { + /* DMIC, memory power */ + regulator-name = "vddq_aoc_pdm"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + regulator-always-on; + samsung,ext-control = ; + }; + + ldo21m { + /* Dauntless */ + regulator-name = "vdd_dtls"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + s2mpg10_ldo22m: ldo22m { + /* display */ + regulator-name = "vci_disp"; + regulator-min-microvolt = <3025000>; + regulator-max-microvolt = <3025000>; + }; + + /* ldo23m & ldo24m are unused */ + + ldo25m { + /* touch */ + regulator-name = "dvdd_tsp"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + }; + + ldo26m { + /* touch */ + regulator-name = "avdd_ts"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <3300000>; + }; + + ldo27m { + /* under-display fingerprint scanner */ + regulator-name = "avdd_udfps"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <3300000>; + }; + + /* + *ldo28m .. ldo30m are unused, ldo31m is board specific + */ + }; + }; + + pmic-2 { + compatible = "samsung,s2mpg11-pmic"; + interrupts-extended = <&gpa0 7 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&sub_pmic_int>; + wakeup-source; + + vinl1s-supply = <&s2mpg10_buck8m>; + vinl2s-supply = <&s2mpg11_buck6s>; + vinl3s-supply = <&s2mpg11_buck7s>; + vinl4s-supply = <&s2mpg11_buck7s>; + vinl5s-supply = <&s2mpg11_buckboost>; + vinl6s-supply = <&s2mpg11_buckboost>; + vinbd-supply = <&s2mpg11_buckboost>; + + regulators { + buck1s { + /* multimedia */ + regulator-name = "vdd_cam"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + samsung,ext-control = ; + }; + + buck2s { + regulator-name = "vdd_g3d"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + samsung,ext-control = ; + }; + + s2mpg11_buck3s: buck3s { + regulator-name = "lldo1"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <6250>; + }; + + buck4s { + /* DDR */ + regulator-name = "vdd2h_mem"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <6250>; + regulator-always-on; + }; + + buck5s { + /* DDR */ + regulator-name = "vddq_mem"; + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <6250>; + regulator-always-on; + samsung,ext-control = ; + }; + + s2mpg11_buck6s: buck6s { + regulator-name = "lldo4"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1500000>; + regulator-ramp-delay = <6250>; + }; + + s2mpg11_buck7s: buck7s { + regulator-name = "mldo"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <2200000>; + regulator-ramp-delay = <6250>; + }; + + buck8s { + regulator-name = "vdd_g3d_l2"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + samsung,ext-control = ; + }; + + buck9s { + regulator-name = "vdd_aoc"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + }; + + buck10s { + /* DDR */ + regulator-name = "vdd2l_mem"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <1300000>; + regulator-ramp-delay = <6250>; + regulator-always-on; + }; + + buckd { + regulator-name = "vcc_ufs"; + regulator-min-microvolt = <2400000>; + regulator-max-microvolt = <3300000>; + regulator-ramp-delay = <6250>; + regulator-always-on; + samsung,ext-control = ; + }; + + s2mpg11_bucka: bucka { + /* lots, IO */ + regulator-name = "bucka"; + regulator-min-microvolt = <1700000>; + regulator-max-microvolt = <2100000>; + regulator-ramp-delay = <6250>; + regulator-always-on; + }; + + s2mpg11_buckboost: buckboost { + regulator-name = "buckboost"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3600000>; + }; + + ldo1s { + regulator-name = "vdd_g3d_m"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <6250>; + regulator-always-on; + samsung,ext-control = ; + }; + + ldo2s { + regulator-name = "vdd_aoc_ret"; + regulator-min-microvolt = <450000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + }; + + /* ldo3s & ldo5s are unused, ldo4s is board specific */ + + ldo6s { + /* sensors */ + regulator-name = "vdd_prox"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <3300000>; + }; + + ldo7s { + /* sensors */ + regulator-name = "vdd_sensors"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + }; + + ldo8s { + regulator-name = "vccq_ufs"; + regulator-min-microvolt = <1130400>; + regulator-max-microvolt = <1281200>; + regulator-always-on; + samsung,ext-control = ; + }; + + ldo9s { + regulator-name = "vdd_gnss"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1300000>; + }; + + ldo10s { + regulator-name = "vdd_gnss_rf"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + }; + + ldo11s { + regulator-name = "vdd_gnss_aux"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + }; + + /* ldo12s is unused */ + + ldo13s { + regulator-name = "vddq_mmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3350000>; + regulator-always-on; + samsung,ext-control = ; + }; + + /* ldo14s is board specific, ldo15s is unused */ }; }; }; @@ -225,6 +727,17 @@ }; }; + fuel-gauge@36 { + compatible = "maxim,max77759-fg"; + reg = <0x36>; + + pinctrl-0 = <&if_pmic_fg_int>; + pinctrl-names = "default"; + interrupts-extended = <&gpa9 3 IRQ_TYPE_LEVEL_LOW>; + + shunt-resistor-micro-ohms = <5000>; + }; + pmic@66 { compatible = "maxim,max77759"; reg = <0x66>; @@ -317,6 +830,19 @@ samsung,pin-pud = ; }; + sub_pmic_int: sub-pmic-int-pins { + samsung,pins = "gpa0-7"; + samsung,pin-function = ; + samsung,pin-pud = ; + }; + + if_pmic_fg_int: if-pmic-fg-int-pins { + samsung,pins = "gpa9-3"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; + key_power: key-power-pins { samsung,pins = "gpa10-1"; samsung,pin-function = ; diff --git a/arch/arm64/boot/dts/exynos/google/gs101-raven.dts b/arch/arm64/boot/dts/exynos/google/gs101-raven.dts index 1e7e6b34b864..a422542715f2 100644 --- a/arch/arm64/boot/dts/exynos/google/gs101-raven.dts +++ b/arch/arm64/boot/dts/exynos/google/gs101-raven.dts @@ -15,6 +15,24 @@ compatible = "google,gs101-raven", "google,gs101"; }; +&acpm_ipc { + pmic-2 { + regulators { + ldo4s { + regulator-name = "vdd2_uwb"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <3300000>; + }; + + ldo14s { + regulator-name = "vdd3_uwb"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <1950000>; + }; + }; + }; +}; + &cont_splash_mem { reg = <0x0 0xfac00000 (1440 * 3120 * 4)>; status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile index 700bab4d3e60..711e36cc2c99 100644 --- a/arch/arm64/boot/dts/freescale/Makefile +++ b/arch/arm64/boot/dts/freescale/Makefile @@ -126,6 +126,8 @@ imx8mm-evk-pcie-ep-dtbs += imx8mm-evk.dtb imx-pcie0-ep.dtbo imx8mm-evkb-pcie-ep-dtbs += imx8mm-evkb.dtb imx-pcie0-ep.dtbo dtb-$(CONFIG_ARCH_MXC) += imx8mm-evk-pcie-ep.dtb imx8mm-evkb-pcie-ep.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mm-hummingboard-ripple.dtb +DTC_FLAGS_imx8mm-hummingboard-ripple += -@ dtb-$(CONFIG_ARCH_MXC) += imx8mm-icore-mx8mm-ctouch2.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-icore-mx8mm-edimm2.2.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-iot-gateway.dtb @@ -158,7 +160,10 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mm-phycore-rpmsg.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-phygate-tauri-l.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-prt8mm.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-tqma8mqml-mba8mx.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mm-tx8m-1610-moduline-iv-306-d.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mm-tx8m-1610-moduline-mini-111.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-var-som-symphony.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mm-var-som-symphony-legacy.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-venice-gw71xx-0x.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-venice-gw72xx-0x.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-venice-gw73xx-0x.dtb @@ -178,7 +183,9 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mm-verdin-wifi-ivy.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-verdin-wifi-mallow.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-verdin-wifi-yavia.dtb +imx8mm-tqma8mqml-mba8mx-lvds-g133han01-dtbs += imx8mm-tqma8mqml-mba8mx.dtb imx8mm-tqma8mqml-mba8mx-lvds-g133han01.dtbo imx8mm-tqma8mqml-mba8mx-lvds-tm070jvhg33-dtbs += imx8mm-tqma8mqml-mba8mx.dtb imx8mm-tqma8mqml-mba8mx-lvds-tm070jvhg33.dtbo +dtb-$(CONFIG_ARCH_MXC) += imx8mm-tqma8mqml-mba8mx-lvds-g133han01.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-tqma8mqml-mba8mx-lvds-tm070jvhg33.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-beacon-kit.dtb @@ -189,6 +196,8 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mn-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-ddr3l-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-ddr4-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-rve-gateway.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mn-solidsense-n8-compact.dtb +DTC_FLAGS_imx8mn-solidsense-n8-compact += -@ dtb-$(CONFIG_ARCH_MXC) += imx8mn-tqma8mqnl-mba8mx.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-var-som-symphony.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-venice-gw7902.dtb @@ -212,17 +221,21 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mn-vhip4-evalboard-v1.dtb \ imx8mn-vhip4-evalboard-v2-overlay-ksz8794.dtb \ imx8mn-vhip4-evalboard-v2-overlay-ksz8794.dtbo +imx8mn-tqma8mqnl-mba8mx-lvds-g133han01-dtbs += imx8mn-tqma8mqnl-mba8mx.dtb imx8mm-tqma8mqml-mba8mx-lvds-g133han01.dtbo imx8mn-tqma8mqnl-mba8mx-lvds-tm070jvhg33-dtbs += imx8mn-tqma8mqnl-mba8mx.dtb imx8mn-tqma8mqnl-mba8mx-lvds-tm070jvhg33.dtbo imx8mn-tqma8mqnl-mba8mx-usbotg-dtbs += imx8mn-tqma8mqnl-mba8mx.dtb imx8mn-tqma8mqnl-mba8mx-usbotg.dtbo +dtb-$(CONFIG_ARCH_MXC) += imx8mn-tqma8mqnl-mba8mx-lvds-g133han01.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-tqma8mqnl-mba8mx-lvds-tm070jvhg33.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-tqma8mqnl-mba8mx-usbotg.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-ab2.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-aristainetos3-adpismarc.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-aristainetos3-helios.dtb imx8mp-aristainetos3-helios-lvds-dtbs += imx8mp-aristainetos3-helios.dtb imx8mp-aristainetos3-helios-lvds.dtbo dtb-$(CONFIG_ARCH_MXC) += imx8mp-aristainetos3-helios-lvds.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-aristainetos3-proton2s.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-beacon-kit.dtb +DTC_FLAGS_imx8mp-cubox-m := -@ dtb-$(CONFIG_ARCH_MXC) += imx8mp-cubox-m.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-data-modul-edm-sbc.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-debix-model-a.dtb @@ -234,9 +247,27 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mp-dhcom-picoitx.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-edm-g-wb.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-frdm.dtb +DTC_FLAGS_imx8mp-hummingboard-iiot := -@ +dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-iiot.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-iiot-panel-dsi-WJ70N3TYJHMNG0.dtbo +imx8mp-hummingboard-iiot-panel-dsi-WJ70N3TYJHMNG0-dtbs += imx8mp-hummingboard-iiot.dtb imx8mp-hummingboard-iiot-panel-dsi-WJ70N3TYJHMNG0.dtbo +dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-iiot-panel-dsi-WJ70N3TYJHMNG0.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-iiot-panel-lvds-WF70A8SYJHLNGA.dtbo +imx8mp-hummingboard-iiot-panel-lvds-WF70A8SYJHLNGA-dtbs += imx8mp-hummingboard-iiot.dtb imx8mp-hummingboard-iiot-panel-lvds-WF70A8SYJHLNGA.dtbo +dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-iiot-panel-lvds-WF70A8SYJHLNGA.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-iiot-rs485-a.dtbo +imx8mp-hummingboard-iiot-rs485-a-dtbs += imx8mp-hummingboard-iiot.dtb imx8mp-hummingboard-iiot-rs485-a.dtbo +dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-iiot-rs485-a.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-iiot-rs485-b.dtbo +imx8mp-hummingboard-iiot-rs485-b-dtbs += imx8mp-hummingboard-iiot.dtb imx8mp-hummingboard-iiot-rs485-b.dtbo +dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-iiot-rs485-b.dtb +DTC_FLAGS_imx8mp-hummingboard-mate := -@ dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-mate.dtb +DTC_FLAGS_imx8mp-hummingboard-pro := -@ dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-pro.dtb +DTC_FLAGS_imx8mp-hummingboard-pulse := -@ dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-pulse.dtb +DTC_FLAGS_imx8mp-hummingboard-ripple := -@ dtb-$(CONFIG_ARCH_MXC) += imx8mp-hummingboard-ripple.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-icore-mx8mp-edimm2.2.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-iota2-lumpy.dtb @@ -339,8 +370,11 @@ imx8mp-tqma8mpql-mba8mp-ras314-imx219-dtbs += imx8mp-tqma8mpql-mba8mp-ras314.dtb imx8mp-tqma8mpql-mba8mp-ras314-lvds-tm070jvhg33-dtbs += imx8mp-tqma8mpql-mba8mp-ras314.dtb imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtbo imx8mp-tqma8mpql-mba8mp-ras314-lvds-tm070jvhg33-imx219-dtbs += imx8mp-tqma8mpql-mba8mp-ras314.dtb imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtbo imx8mp-tqma8mpql-mba8mp-ras314-imx219.dtbo dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mpxl-lvds-g133han01.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mpxl-lvds-g133han01.dtbo dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtbo dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mp-ras314-imx219.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mp-ras314-imx219.dtbo dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mp-ras314-lvds-tm070jvhg33.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-tqma8mpql-mba8mp-ras314-lvds-tm070jvhg33-imx219.dtb @@ -406,17 +440,31 @@ dtb-$(CONFIG_ARCH_MXC) += imx8ulp-9x9-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx8ulp-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx91-11x11-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx91-11x11-frdm.dtb +dtb-$(CONFIG_ARCH_MXC) += imx91-11x11-frdm-s.dtb dtb-$(CONFIG_ARCH_MXC) += imx91-phyboard-segin.dtb dtb-$(CONFIG_ARCH_MXC) += imx91-tqma9131-mba91xxca.dtb + +imx91-tqma9131-mba91xxca-lvds-tm070jvhg33-dtbs := imx91-tqma9131-mba91xxca.dtb imx93-tqma9352-mba91xxca-lvds-tm070jvhg33.dtbo +imx91-tqma9131-mba91xxca-rgb-cdtech-dc44-dtbs := imx91-tqma9131-mba91xxca.dtb imx93-tqma9352-mba91xxca-rgb-cdtech-dc44.dtbo +dtb-$(CONFIG_ARCH_MXC) += imx91-tqma9131-mba91xxca-lvds-tm070jvhg33.dtb +dtb-$(CONFIG_ARCH_MXC) += imx91-tqma9131-mba91xxca-rgb-cdtech-dc44.dtb +dtb-$(CONFIG_ARCH_MXC) += imx91-var-dart-sonata.dtb + dtb-$(CONFIG_ARCH_MXC) += imx93-9x9-qsb.dtb imx93-9x9-qsb-can1-dtbs += imx93-9x9-qsb.dtb imx93-9x9-qsb-can1.dtbo imx93-9x9-qsb-i3c-dtbs += imx93-9x9-qsb.dtb imx93-9x9-qsb-i3c.dtbo +imx93-9x9-qsb-ontat-kd50g21-40nt-a1-dtbs += imx93-9x9-qsb.dtb imx93-9x9-qsb-ontat-kd50g21-40nt-a1.dtbo dtb-$(CONFIG_ARCH_MXC) += imx93-9x9-qsb-can1.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-9x9-qsb-i3c.dtb +dtb-$(CONFIG_ARCH_MXC) += imx93-9x9-qsb-ontat-kd50g21-40nt-a1.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-frdm.dtb + +imx93-11x11-frdm-pixpaper-dtbs += imx93-11x11-frdm.dtb imx93-11x11-frdm-pixpaper.dtbo +dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-frdm-pixpaper.dtb + dtb-$(CONFIG_ARCH_MXC) += imx93-14x14-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-kontron-bl-osm-s.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-nash.dtb @@ -425,12 +473,14 @@ dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-segin.dtb imx93-phyboard-nash-jtag-dtbs += imx93-phyboard-nash.dtb imx93-phyboard-nash-jtag.dtbo imx93-phyboard-nash-peb-wlbt-07-dtbs += imx93-phyboard-nash.dtb imx93-phyboard-nash-peb-wlbt-07.dtbo imx93-phyboard-nash-pwm-fan-dtbs += imx93-phyboard-nash.dtb imx93-phyboard-nash-pwm-fan.dtbo +imx93-phyboard-segin-peb-av-02-dtbs += imx93-phyboard-segin.dtb imx93-phyboard-segin-peb-av-02.dtbo imx93-phyboard-segin-peb-eval-01-dtbs += imx93-phyboard-segin.dtb imx93-phyboard-segin-peb-eval-01.dtbo imx93-phyboard-segin-peb-wlbt-05-dtbs += imx93-phyboard-segin.dtb imx93-phyboard-segin-peb-wlbt-05.dtbo imx93-phycore-rpmsg-dtbs += imx93-phyboard-nash.dtb imx93-phyboard-segin.dtb imx93-phycore-rpmsg.dtbo dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-nash-jtag.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-nash-peb-wlbt-07.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-nash-pwm-fan.dtb +dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-segin-peb-av-02.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-segin-peb-eval-01.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-phyboard-segin-peb-wlbt-05.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-phycore-rpmsg.dtb @@ -438,7 +488,19 @@ dtb-$(CONFIG_ARCH_MXC) += imx93-phycore-rpmsg.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba91xxca.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba93xxca.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba93xxla.dtb +dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba93xxla-mini.dtb + +imx93-tqma9352-mba91xxca-lvds-tm070jvhg33-dtbs := imx93-tqma9352-mba91xxca.dtb imx93-tqma9352-mba91xxca-lvds-tm070jvhg33.dtbo +imx93-tqma9352-mba91xxca-rgb-cdtech-dc44-dtbs := imx93-tqma9352-mba91xxca.dtb imx93-tqma9352-mba91xxca-rgb-cdtech-dc44.dtbo +dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba91xxca-lvds-tm070jvhg33.dtb +dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba91xxca-rgb-cdtech-dc44.dtb + +imx93-tqma9352-mba93xxla-mini-ezurio-wlan-dtbs += imx93-tqma9352-mba93xxla-mini.dtb imx93-tqma9352-mba93xxla-mini-ezurio-wlan.dtbo + +dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba93xxla-mini-ezurio-wlan.dtb + dtb-$(CONFIG_ARCH_MXC) += imx93-var-som-symphony.dtb +dtb-$(CONFIG_ARCH_MXC) += imx93w-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx943-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-frdm.dtb @@ -446,6 +508,17 @@ dtb-$(CONFIG_ARCH_MXC) += imx95-19x19-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx95-19x19-evk-sof.dtb dtb-$(CONFIG_ARCH_MXC) += imx95-toradex-smarc-dev.dtb dtb-$(CONFIG_ARCH_MXC) += imx95-tqma9596sa-mb-smarc-2.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-var-dart-sonata.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-verdin-nonwifi-dahlia.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-verdin-nonwifi-dev.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-verdin-nonwifi-ivy.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-verdin-nonwifi-mallow.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-verdin-nonwifi-yavia.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-verdin-wifi-dahlia.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-verdin-wifi-dev.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-verdin-wifi-ivy.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-verdin-wifi-mallow.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-verdin-wifi-yavia.dtb imx95-15x15-evk-pcie0-ep-dtbs = imx95-15x15-evk.dtb imx-pcie0-ep.dtbo dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-pcie0-ep.dtb @@ -501,4 +574,5 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mm-phygate-tauri-l-rs232-rs485.dtb dtb-$(CONFIG_ARCH_S32) += s32g274a-evb.dtb dtb-$(CONFIG_ARCH_S32) += s32g274a-rdb2.dtb dtb-$(CONFIG_ARCH_S32) += s32g399a-rdb3.dtb +dtb-$(CONFIG_ARCH_S32) += s32n79-rdb.dtb dtb-$(CONFIG_ARCH_S32) += s32v234-evb.dtb diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi index e7f9c9319319..f4ba3d16ab86 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi @@ -114,14 +114,10 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; pmu { @@ -138,8 +134,7 @@ <0x0 0x06040000 0 0x40000>; /* GIC Redistributor */ #interrupt-cells = <3>; interrupt-controller; - interrupts = ; + interrupts = ; its: msi-controller@6020000 { compatible = "arm,gic-v3-its"; msi-controller; diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi index eec2cd6c6d32..90956ffb8ea9 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi @@ -14,6 +14,7 @@ aliases { crypto = &crypto; + rtc0 = &com_rtc; }; sb_3v3: regulator-sb3v3 { @@ -154,7 +155,7 @@ &i2c4 { status = "okay"; - rtc@51 { + com_rtc: rtc@51 { compatible = "nxp,pcf2129"; reg = <0x51>; interrupts-extended = <&gpio2 8 IRQ_TYPE_LEVEL_LOW>; @@ -162,6 +163,8 @@ }; &fspi { + pinctrl-names = "default"; + pinctrl-0 = <&fspi_data74_pins>, <&fspi_data30_pins>, <&fspi_dqs_sck_cs10_pins>; status = "okay"; flash@0 { @@ -177,6 +180,11 @@ }; }; +&pinmux_i2crv { + pinctrl-names = "default"; + pinctrl-0 = <&gpio0_14_12_pins>; +}; + &usb0 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-itx.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-itx.dtsi index af6258b2fe82..580ee9b3026e 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-itx.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-itx.dtsi @@ -89,6 +89,8 @@ }; &esdhc0 { + pinctrl-names = "default"; + pinctrl-0 = <&esdhc0_cd_wp_pins>, <&esdhc0_cmd_data30_clk_vsel_pins>; sd-uhs-sdr104; sd-uhs-sdr50; sd-uhs-sdr25; diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi index 853b01452813..479982948ee5 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi @@ -750,9 +750,10 @@ clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(16)>; pinctrl-names = "default", "gpio"; - pinctrl-0 = <&i2c0_scl>; - pinctrl-1 = <&i2c0_scl_gpio>; + pinctrl-0 = <&i2c0_pins>; + pinctrl-1 = <&gpio0_3_2_pins>; scl-gpios = <&gpio0 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; status = "disabled"; }; @@ -766,9 +767,10 @@ clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(16)>; pinctrl-names = "default", "gpio"; - pinctrl-0 = <&i2c1_scl>; - pinctrl-1 = <&i2c1_scl_gpio>; + pinctrl-0 = <&i2c1_pins>; + pinctrl-1 = <&gpio0_31_30_pins>; scl-gpios = <&gpio0 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; status = "disabled"; }; @@ -782,9 +784,10 @@ clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(16)>; pinctrl-names = "default", "gpio"; - pinctrl-0 = <&i2c2_scl>; - pinctrl-1 = <&i2c2_scl_gpio>; + pinctrl-0 = <&i2c2_pins>; + pinctrl-1 = <&gpio0_29_28_pins>; scl-gpios = <&gpio0 29 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 28 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; status = "disabled"; }; @@ -798,9 +801,10 @@ clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(16)>; pinctrl-names = "default", "gpio"; - pinctrl-0 = <&i2c3_scl>; - pinctrl-1 = <&i2c3_scl_gpio>; + pinctrl-0 = <&i2c3_pins>; + pinctrl-1 = <&gpio0_27_26_pins>; scl-gpios = <&gpio0 27 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 26 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; status = "disabled"; }; @@ -814,9 +818,10 @@ clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(16)>; pinctrl-names = "default", "gpio"; - pinctrl-0 = <&i2c4_scl>; - pinctrl-1 = <&i2c4_scl_gpio>; + pinctrl-0 = <&i2c4_pins>; + pinctrl-1 = <&gpio0_25_24_pins>; scl-gpios = <&gpio0 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; status = "disabled"; }; @@ -830,9 +835,10 @@ clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(16)>; pinctrl-names = "default", "gpio"; - pinctrl-0 = <&i2c5_scl>; - pinctrl-1 = <&i2c5_scl_gpio>; + pinctrl-0 = <&i2c5_pins>; + pinctrl-1 = <&gpio0_23_22_pins>; scl-gpios = <&gpio0 23 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 22 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; status = "disabled"; }; @@ -846,9 +852,10 @@ clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(16)>; pinctrl-names = "default", "gpio"; - pinctrl-0 = <&i2c6_scl>; - pinctrl-1 = <&i2c6_scl_gpio>; + pinctrl-0 = <&i2c6_i2c7_pins>; + pinctrl-1 = <&gpio1_18_15_pins>; scl-gpios = <&gpio1 16 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio1 15 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; status = "disabled"; }; @@ -862,9 +869,10 @@ clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(16)>; pinctrl-names = "default", "gpio"; - pinctrl-0 = <&i2c7_scl>; - pinctrl-1 = <&i2c7_scl_gpio>; + pinctrl-0 = <&i2c6_i2c7_pins>; + pinctrl-1 = <&gpio1_18_15_pins>; scl-gpios = <&gpio1 18 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio1 17 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; status = "disabled"; }; @@ -1713,68 +1721,159 @@ pinctrl-single,register-width = <32>; pinctrl-single,function-mask = <0x7>; - i2c1_scl: i2c1-scl-pins { - pinctrl-single,bits = <0x0 0 0x7>; + /* RCWSR12 */ + i2c1_pins: iic2-i2c-pins { + pinctrl-single,bits = <0x0 0x0 0x7>; }; - i2c1_scl_gpio: i2c1-scl-gpio-pins { + gpio0_31_30_pins: iic2-gpio-pins { pinctrl-single,bits = <0x0 0x1 0x7>; }; - i2c2_scl: i2c2-scl-pins { - pinctrl-single,bits = <0x0 0 (0x7 << 3)>; + ftm0_ch10_pins: iic2-ftm-pins { + pinctrl-single,bits = <0x0 0x2 0x7>; }; - i2c2_scl_gpio: i2c2-scl-gpio-pins { + esdhc0_cd_wp_pins: iic2-sdhc-pins { + pinctrl-single,bits = <0x0 0x6 0x7>; + }; + + i2c2_pins: iic3-i2c-pins { + pinctrl-single,bits = <0x0 0x0 (0x7 << 3)>; + }; + + gpio0_29_28_pins: iic3-gpio-pins { pinctrl-single,bits = <0x0 (0x1 << 3) (0x7 << 3)>; }; - i2c3_scl: i2c3-scl-pins { - pinctrl-single,bits = <0x0 0 (0x7 << 6)>; + can0_pins: iic3-can-pins { + pinctrl-single,bits = <0x0 (0x2 << 3) (0x7 << 3)>; }; - i2c3_scl_gpio: i2c3-scl-gpio-pins { + event65_pins: iic3-event-pins { + pinctrl-single,bits = <0x0 (0x6 << 3) (0x7 << 3)>; + }; + + i2c3_pins: iic4-i2c-pins { + pinctrl-single,bits = <0x0 0x0 (0x7 << 6)>; + }; + + gpio0_27_26_pins: iic4-gpio-pins { pinctrl-single,bits = <0x0 (0x1 << 6) (0x7 << 6)>; }; - i2c4_scl: i2c4-scl-pins { - pinctrl-single,bits = <0x0 0 (0x7 << 9)>; + can1_pins: iic4-can-pins { + pinctrl-single,bits = <0x0 (0x2 << 6) (0x7 << 6)>; }; - i2c4_scl_gpio: i2c4-scl-gpio-pins { + event87_pins: iic4-event-pins { + pinctrl-single,bits = <0x0 (0x6 << 6) (0x7 << 6)>; + }; + + i2c4_pins: iic5-i2c-pins { + pinctrl-single,bits = <0x0 0x0 (0x7 << 9)>; + }; + + gpio0_25_24_pins: iic5-gpio-pins { pinctrl-single,bits = <0x0 (0x1 << 9) (0x7 << 9)>; }; - i2c5_scl: i2c5-scl-pins { - pinctrl-single,bits = <0x0 0 (0x7 << 12)>; + esdhc0_clksync_pins: iic5-sdhc-clk-pins { + pinctrl-single,bits = <0x0 (0x2 << 9) (0x7 << 9)>; }; - i2c5_scl_gpio: i2c5-scl-gpio-pins { + dspi2_miso_mosi_pins: iic5-spi3-pins { + pinctrl-single,bits = <0x3 (0x2 << 9) (0x7 << 9)>; + }; + + i2c5_pins: iic6-i2c-pins { + pinctrl-single,bits = <0x0 0x0 (0x7 << 12)>; + }; + + gpio0_23_22_pins: iic6-gpio-pins { pinctrl-single,bits = <0x0 (0x1 << 12) (0x7 << 12)>; }; - i2c6_scl: i2c6-scl-pins { - pinctrl-single,bits = <0x4 0x2 0x7>; + esdhc1_clksync_pins: iic6-sdhc-clk-pins { + pinctrl-single,bits = <0x0 (0x2 << 12) (0x7 << 12)>; }; - i2c6_scl_gpio: i2c6-scl-gpio-pins { + fspi_data74_pins: xspi1-data74-pins { + pinctrl-single,bits = <0x0 0x0 (0x7 << 15)>; + }; + + gpio1_31_28_pins: xspi1-data74-gpio-pins { + pinctrl-single,bits = <0x0 0x1 (0x7 << 15)>; + }; + + fspi_data30_pins: xspi1-data30-pins { + pinctrl-single,bits = <0x0 0x0 (0x7 << 18)>; + }; + + gpio1_27_24_pins: xspi1-data30-gpio-pins { + pinctrl-single,bits = <0x0 0x1 (0x7 << 18)>; + }; + + fspi_dqs_sck_cs10_pins: xspi1-base-pins { + pinctrl-single,bits = <0x0 0x0 (0x7 << 21)>; + }; + + gpio1_23_20_pins: xspi1-base-gpio-pins { + pinctrl-single,bits = <0x0 0x1 (0x7 << 21)>; + }; + + esdhc0_cmd_data30_clk_vsel_pins: sdhc1-base-sdhc-vsel-pins { + pinctrl-single,bits = <0x0 0x0 (0x7 << 24)>; + }; + + gpio0_21_15_pins: sdhc1-base-gpio-pins { + pinctrl-single,bits = <0x0 (0x1 << 24) (0x7 << 24)>; + }; + + dspi0_pins: sdhc1-base-spi1-pins { + pinctrl-single,bits = <0x0 (0x2 << 24) (0x7 << 24)>; + }; + + esdhc0_cmd_data30_clk_dspi2_cs0_pins: sdhc1-base-sdhc-spi3-pins { + pinctrl-single,bits = <0x0 (0x3 << 24) (0x7 << 24)>; + }; + + esdhc0_cmd_data30_clk_data4_pins: sdhc1-base-sdhc-data4-pins { + pinctrl-single,bits = <0x0 (0x4 << 24) (0x7 << 24)>; + }; + + esdhc0_dir_pins: sdhc1-dir-pins { + pinctrl-single,bits = <0x0 0x0 (0x7 << 27)>; + }; + + gpio0_14_12_pins: sdhc1-dir-gpio-pins { + pinctrl-single,bits = <0x0 (0x1 << 27) (0x7 << 27)>; + }; + + dspi2_cs31_pins: sdhc1-dir-spi3-pins { + pinctrl-single,bits = <0x0 (0x3 << 27) (0x7 << 27)>; + }; + + esdhc0_data75_pins: sdhc1-dir-sdhc-pins { + pinctrl-single,bits = <0x0 (0x4 << 27) (0x7 << 27)>; + }; + + /* RCWSR13 */ + gpio1_18_15_pins: iic8-iic7-gpio-pins { pinctrl-single,bits = <0x4 0x1 0x7>; }; - i2c7_scl: i2c7-scl-pins { + i2c6_i2c7_pins: iic8-iic7-i2c-pins { pinctrl-single,bits = <0x4 0x2 0x7>; }; - i2c7_scl_gpio: i2c7-scl-gpio-pins { - pinctrl-single,bits = <0x4 0x1 0x7>; + /* RCWSR14 */ + i2c0_pins: iic1-i2c-pins { + pinctrl-single,bits = <0x8 0x0 (0x1 << 10)>; }; - i2c0_scl: i2c0-scl-pins { - pinctrl-single,bits = <0x8 0 (0x7 << 10)>; - }; - - i2c0_scl_gpio: i2c0-scl-gpio-pins { - pinctrl-single,bits = <0x8 (0x1 << 10) (0x7 << 10)>; + gpio0_3_2_pins: iic1-gpio-pins { + pinctrl-single,bits = <0x8 (0x1 << 10) (0x1 << 10)>; }; }; diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts b/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts index eafef8718a0f..9d50d3e2761d 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts +++ b/arch/arm64/boot/dts/freescale/fsl-lx2162a-clearfog.dts @@ -41,21 +41,29 @@ led_sfp_at: led-sfp-at { gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>; /* PROC_IRQ5 */ default-state = "off"; + linux,default-trigger = "netdev"; + trigger-sources = <&dpmac3>; }; led_sfp_ab: led-sfp-ab { gpios = <&gpio2 11 GPIO_ACTIVE_HIGH>; /* PROC_IRQ11 */ default-state = "off"; + linux,default-trigger = "netdev"; + trigger-sources = <&dpmac4>; }; led_sfp_bt: led-sfp-bt { gpios = <&gpio2 13 GPIO_ACTIVE_HIGH>; /* EVT1_B */ default-state = "off"; + linux,default-trigger = "netdev"; + trigger-sources = <&dpmac5>; }; led_sfp_bb: led-sfp-bb { gpios = <&gpio2 14 GPIO_ACTIVE_HIGH>; /* EVT2_B */ default-state = "off"; + linux,default-trigger = "netdev"; + trigger-sources = <&dpmac6>; }; }; @@ -223,6 +231,8 @@ }; &esdhc0 { + pinctrl-names = "default"; + pinctrl-0 = <&esdhc0_cd_wp_pins>, <&esdhc0_cmd_data30_clk_vsel_pins>; sd-uhs-sdr104; sd-uhs-sdr50; sd-uhs-sdr25; diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2162a-sr-som.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2162a-sr-som.dtsi index e914291e63a1..3ad908d52a18 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2162a-sr-som.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-lx2162a-sr-som.dtsi @@ -5,6 +5,16 @@ // Copyright 2021 Rabeeh Khoury // Copyright 2023 Josua Mayer +/ { + model = "SolidRun LX2162A System on Module"; + compatible = "solidrun,lx2162a-som", "fsl,lx2160a"; + + aliases { + crypto = &crypto; + rtc0 = &som_rtc; + }; +}; + &crypto { status = "okay"; }; @@ -30,6 +40,8 @@ }; &fspi { + pinctrl-names = "default"; + pinctrl-0 = <&fspi_data74_pins>, <&fspi_data30_pins>, <&fspi_dqs_sck_cs10_pins>; status = "okay"; flash@0 { @@ -75,8 +87,13 @@ &i2c5 { status = "okay"; - rtc@6f { + som_rtc: rtc@6f { compatible = "microchip,mcp7940x"; reg = <0x6f>; }; }; + +&pinmux_i2crv { + pinctrl-names = "default"; + pinctrl-0 = <&gpio0_14_12_pins>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8-apalis-eval.dtsi b/arch/arm64/boot/dts/freescale/imx8-apalis-eval.dtsi index 06790255a764..6f5af37ba9af 100644 --- a/arch/arm64/boot/dts/freescale/imx8-apalis-eval.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8-apalis-eval.dtsi @@ -22,10 +22,6 @@ status = "okay"; }; -&amix { - status = "okay"; -}; - &asrc0 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8-apalis-ixora-v1.1.dtsi b/arch/arm64/boot/dts/freescale/imx8-apalis-ixora-v1.1.dtsi index 7022de46b8bf..93f485140b20 100644 --- a/arch/arm64/boot/dts/freescale/imx8-apalis-ixora-v1.1.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8-apalis-ixora-v1.1.dtsi @@ -21,6 +21,7 @@ color = ; default-state = "off"; function = LED_FUNCTION_STATUS; + function-enumerator = <1>; gpios = <&lsio_gpio5 27 GPIO_ACTIVE_HIGH>; }; @@ -29,6 +30,7 @@ color = ; default-state = "off"; function = LED_FUNCTION_STATUS; + function-enumerator = <1>; gpios = <&lsio_gpio5 29 GPIO_ACTIVE_HIGH>; }; @@ -37,6 +39,7 @@ color = ; default-state = "off"; function = LED_FUNCTION_STATUS; + function-enumerator = <2>; gpios = <&lsio_gpio5 20 GPIO_ACTIVE_HIGH>; }; @@ -45,6 +48,7 @@ color = ; default-state = "off"; function = LED_FUNCTION_STATUS; + function-enumerator = <2>; gpios = <&lsio_gpio5 21 GPIO_ACTIVE_HIGH>; }; }; @@ -62,10 +66,6 @@ status = "okay"; }; -&amix { - status = "okay"; -}; - &asrc0 { status = "okay"; }; @@ -89,8 +89,6 @@ status = "okay"; }; -/* TODO: GPU */ - /* Apalis I2C1 */ &i2c2 { status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/imx8-apalis-ixora-v1.2.dtsi b/arch/arm64/boot/dts/freescale/imx8-apalis-ixora-v1.2.dtsi index 12732ed7f811..5c86bcee55fb 100644 --- a/arch/arm64/boot/dts/freescale/imx8-apalis-ixora-v1.2.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8-apalis-ixora-v1.2.dtsi @@ -21,6 +21,7 @@ color = ; default-state = "off"; function = LED_FUNCTION_STATUS; + function-enumerator = <1>; gpios = <&lsio_gpio5 27 GPIO_ACTIVE_HIGH>; }; @@ -29,6 +30,7 @@ color = ; default-state = "off"; function = LED_FUNCTION_STATUS; + function-enumerator = <1>; gpios = <&lsio_gpio5 29 GPIO_ACTIVE_HIGH>; }; @@ -37,6 +39,7 @@ color = ; default-state = "off"; function = LED_FUNCTION_STATUS; + function-enumerator = <2>; gpios = <&lsio_gpio5 20 GPIO_ACTIVE_HIGH>; }; @@ -45,6 +48,7 @@ color = ; default-state = "off"; function = LED_FUNCTION_STATUS; + function-enumerator = <2>; gpios = <&lsio_gpio5 21 GPIO_ACTIVE_HIGH>; }; }; @@ -94,10 +98,6 @@ status = "okay"; }; -&amix { - status = "okay"; -}; - &asrc0 { status = "okay"; }; @@ -123,8 +123,6 @@ status = "okay"; }; -/* TODO: GPU */ - /* Apalis I2C1 */ &i2c2 { status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts index 5c68d33e19f2..bc62ae5ca812 100644 --- a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts @@ -259,33 +259,37 @@ }; sound-wm8960-2 { - compatible = "fsl,imx-audio-wm8960"; - model = "wm8960-audio-2"; - audio-cpu = <&sai2>; - audio-codec = <&wm8960_2>; - audio-routing = "Headphone Jack", "HP_L", - "Headphone Jack", "HP_R", - "Ext Spk", "SPK_LP", - "Ext Spk", "SPK_LN", - "Ext Spk", "SPK_RP", - "Ext Spk", "SPK_RN", - "LINPUT1", "Mic Jack", - "Mic Jack", "MICB"; + compatible = "audio-graph-card2"; + label = "wm8960-audio-2"; + links = <&sai2_port2>; + routing = "Headphones", "HP_L", + "Headphones", "HP_R", + "Ext Spk", "SPK_LP", + "Ext Spk", "SPK_LN", + "Ext Spk", "SPK_RP", + "Ext Spk", "SPK_RN", + "LINPUT1", "Mic Jack", + "Mic Jack", "MICB"; + widgets = "Headphone", "Headphones", + "Speaker", "Ext Spk", + "Microphone", "Mic Jack"; }; sound-wm8960-3 { - compatible = "fsl,imx-audio-wm8960"; - model = "wm8960-audio-3"; - audio-cpu = <&sai3>; - audio-codec = <&wm8960_3>; - audio-routing = "Headphone Jack", "HP_L", - "Headphone Jack", "HP_R", - "Ext Spk", "SPK_LP", - "Ext Spk", "SPK_LN", - "Ext Spk", "SPK_RP", - "Ext Spk", "SPK_RN", - "LINPUT1", "Mic Jack", - "Mic Jack", "MICB"; + compatible = "audio-graph-card2"; + label = "wm8960-audio-3"; + links = <&sai3_port2>; + routing = "Headphones", "HP_L", + "Headphones", "HP_R", + "Ext Spk", "SPK_LP", + "Ext Spk", "SPK_LN", + "Ext Spk", "SPK_RP", + "Ext Spk", "SPK_RN", + "LINPUT1", "Mic Jack", + "Mic Jack", "MICB"; + widgets = "Headphone", "Headphones", + "Speaker", "Ext Spk", + "Microphone", "Mic Jack"; }; }; @@ -481,6 +485,16 @@ DCVDD-supply = <®_audio_1v8>; SPKVDD1-supply = <®_audio_5v>; SPKVDD2-supply = <®_audio_5v>; + + port { + capture-only; + + wm8960_2_ep: endpoint { + bitclock-master; + frame-master; + remote-endpoint = <&sai2_endpoint2>; + }; + }; }; }; @@ -510,6 +524,16 @@ DCVDD-supply = <®_audio_1v8>; SPKVDD1-supply = <®_audio_5v>; SPKVDD2-supply = <®_audio_5v>; + + port { + capture-only; + + wm8960_3_ep: endpoint { + bitclock-master; + frame-master; + remote-endpoint = <&sai3_endpoint2>; + }; + }; }; }; @@ -700,6 +724,27 @@ pinctrl-0 = <&pinctrl_sai2>; fsl,sai-asynchronous; status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + sai2_port1: port@1 { + reg = <1>; + endpoint { /* not used */ }; + }; + + sai2_port2: port@2 { + reg = <2>; + capture-only; + + sai2_endpoint2: endpoint { + dai-format = "i2s"; + remote-endpoint = <&wm8960_2_ep>; + system-clock-direction-out; + }; + }; + }; }; &sai3 { @@ -712,6 +757,27 @@ pinctrl-0 = <&pinctrl_sai3>; fsl,sai-asynchronous; status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + sai3_port1: port@1 { + reg = <1>; + endpoint { /* not used */ }; + }; + + sai3_port2: port@2 { + reg = <2>; + capture-only; + + sai3_endpoint2: endpoint { + dai-format = "i2s"; + remote-endpoint = <&wm8960_3_ep>; + system-clock-direction-out; + }; + }; + }; }; &thermal_zones { diff --git a/arch/arm64/boot/dts/freescale/imx8mm-emtop-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-emtop-som.dtsi index 67d22d3768aa..507d1824d99d 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-emtop-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-emtop-som.dtsi @@ -60,7 +60,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_pmic>; interrupt-parent = <&gpio1>; - interrupts = <3 IRQ_TYPE_EDGE_RISING>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; regulators { buck1: BUCK1 { @@ -194,7 +194,7 @@ pinctrl_pmic: emtop-pmic-grp { fsl,pins = < - MX8MM_IOMUXC_GPIO1_IO03_GPIO1_IO3 0x41 + MX8MM_IOMUXC_GPIO1_IO03_GPIO1_IO3 0x141 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi index 6eab8a6001db..8be44eaf4e1e 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi @@ -608,12 +608,34 @@ status = "okay"; }; +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + assigned-clocks = <&clk IMX8MM_CLK_UART1>; + assigned-clock-parents = <&clk IMX8MM_SYS_PLL1_80M>; + uart-has-rtscts; + status = "okay"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; +}; + &uart2 { /* console */ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart2>; status = "okay"; }; +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3>; + assigned-clocks = <&clk IMX8MM_CLK_UART3>; + assigned-clock-parents = <&clk IMX8MM_SYS_PLL1_80M>; + uart-has-rtscts; + status = "okay"; +}; + &usbphynop1 { wakeup-source; }; @@ -691,7 +713,7 @@ pinctrl_ir: irgrp { fsl,pins = < - MX8MM_IOMUXC_GPIO1_IO13_GPIO1_IO13 0x4f + MX8MM_IOMUXC_GPIO1_IO13_GPIO1_IO13 0x4f >; }; @@ -724,26 +746,26 @@ pinctrl_pcie0: pcie0grp { fsl,pins = < - MX8MM_IOMUXC_I2C4_SCL_PCIE1_CLKREQ_B 0x61 - MX8MM_IOMUXC_SAI2_RXFS_GPIO4_IO21 0x41 + MX8MM_IOMUXC_I2C4_SCL_PCIE1_CLKREQ_B 0x61 + MX8MM_IOMUXC_SAI2_RXFS_GPIO4_IO21 0x41 >; }; pinctrl_pcie0_reg: pcie0reggrp { fsl,pins = < - MX8MM_IOMUXC_GPIO1_IO05_GPIO1_IO5 0x41 + MX8MM_IOMUXC_GPIO1_IO05_GPIO1_IO5 0x41 >; }; pinctrl_pdm: pdmgrp { fsl,pins = < - MX8MM_IOMUXC_SAI5_MCLK_SAI5_MCLK 0xd6 - MX8MM_IOMUXC_SAI5_RXC_PDM_CLK 0xd6 - MX8MM_IOMUXC_SAI5_RXFS_SAI5_RX_SYNC 0xd6 - MX8MM_IOMUXC_SAI5_RXD0_PDM_DATA0 0xd6 - MX8MM_IOMUXC_SAI5_RXD1_PDM_DATA1 0xd6 - MX8MM_IOMUXC_SAI5_RXD2_PDM_DATA2 0xd6 - MX8MM_IOMUXC_SAI5_RXD3_PDM_DATA3 0xd6 + MX8MM_IOMUXC_SAI5_MCLK_SAI5_MCLK 0xd6 + MX8MM_IOMUXC_SAI5_RXC_PDM_CLK 0xd6 + MX8MM_IOMUXC_SAI5_RXFS_SAI5_RX_SYNC 0xd6 + MX8MM_IOMUXC_SAI5_RXD0_PDM_DATA0 0xd6 + MX8MM_IOMUXC_SAI5_RXD1_PDM_DATA1 0xd6 + MX8MM_IOMUXC_SAI5_RXD2_PDM_DATA2 0xd6 + MX8MM_IOMUXC_SAI5_RXD3_PDM_DATA3 0xd6 >; }; @@ -761,19 +783,19 @@ pinctrl_sai2: sai2grp { fsl,pins = < - MX8MM_IOMUXC_SAI2_TXC_SAI2_TX_BCLK 0xd6 - MX8MM_IOMUXC_SAI2_TXFS_SAI2_TX_SYNC 0xd6 - MX8MM_IOMUXC_SAI2_TXD0_SAI2_TX_DATA0 0xd6 - MX8MM_IOMUXC_SAI2_RXD0_SAI2_RX_DATA0 0xd6 + MX8MM_IOMUXC_SAI2_TXC_SAI2_TX_BCLK 0xd6 + MX8MM_IOMUXC_SAI2_TXFS_SAI2_TX_SYNC 0xd6 + MX8MM_IOMUXC_SAI2_TXD0_SAI2_TX_DATA0 0xd6 + MX8MM_IOMUXC_SAI2_RXD0_SAI2_RX_DATA0 0xd6 >; }; pinctrl_sai3: sai3grp { fsl,pins = < - MX8MM_IOMUXC_SAI3_TXFS_SAI3_TX_SYNC 0xd6 - MX8MM_IOMUXC_SAI3_TXC_SAI3_TX_BCLK 0xd6 - MX8MM_IOMUXC_SAI3_MCLK_SAI3_MCLK 0xd6 - MX8MM_IOMUXC_SAI3_TXD_SAI3_TX_DATA0 0xd6 + MX8MM_IOMUXC_SAI3_TXFS_SAI3_TX_SYNC 0xd6 + MX8MM_IOMUXC_SAI3_TXC_SAI3_TX_BCLK 0xd6 + MX8MM_IOMUXC_SAI3_MCLK_SAI3_MCLK 0xd6 + MX8MM_IOMUXC_SAI3_TXD_SAI3_TX_DATA0 0xd6 >; }; @@ -790,6 +812,15 @@ >; }; + pinctrl_uart1: uart1grp { + fsl,pins = < + MX8MM_IOMUXC_UART1_RXD_UART1_DCE_RX 0x140 + MX8MM_IOMUXC_UART1_TXD_UART1_DCE_TX 0x140 + MX8MM_IOMUXC_UART3_RXD_UART1_DCE_CTS_B 0x140 + MX8MM_IOMUXC_UART3_TXD_UART1_DCE_RTS_B 0x140 + >; + }; + pinctrl_uart2: uart2grp { fsl,pins = < MX8MM_IOMUXC_UART2_RXD_UART2_DCE_RX 0x140 @@ -797,6 +828,15 @@ >; }; + pinctrl_uart3: uart3grp { + fsl,pins = < + MX8MM_IOMUXC_ECSPI1_SCLK_UART3_DCE_RX 0x140 + MX8MM_IOMUXC_ECSPI1_MOSI_UART3_DCE_TX 0x140 + MX8MM_IOMUXC_ECSPI1_SS0_UART3_DCE_RTS_B 0x140 + MX8MM_IOMUXC_ECSPI1_MISO_UART3_DCE_CTS_B 0x140 + >; + }; + pinctrl_usdhc2_gpio: usdhc2grpgpiogrp { fsl,pins = < MX8MM_IOMUXC_GPIO1_IO15_GPIO1_IO15 0x1c4 diff --git a/arch/arm64/boot/dts/freescale/imx8mm-hummingboard-ripple.dts b/arch/arm64/boot/dts/freescale/imx8mm-hummingboard-ripple.dts new file mode 100644 index 000000000000..18b58634d3c2 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-hummingboard-ripple.dts @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2025 Josua Mayer + */ + +/dts-v1/; + +#include + +#include "imx8mm-sr-som.dtsi" + +/ { + compatible = "solidrun,imx8mm-hummingboard-ripple", + "solidrun,imx8mm-sr-som", "fsl,imx8mm"; + model = "SolidRun i.MX8MM HummingBoard Ripple"; + + aliases { + rtc0 = &carrier_rtc; + rtc1 = &snvs_rtc; + }; + + hdmi-connector { + compatible = "hdmi-connector"; + label = "hdmi"; + type = "c"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&adv7535_out>; + }; + }; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-0 = <&led_pins>; + pinctrl-names = "default"; + + led-0 { + color = ; + default-state = "on"; + gpios = <&gpio5 29 GPIO_ACTIVE_LOW>; + label = "D30"; + }; + + led-1 { + color = ; + default-state = "on"; + gpios = <&gpio5 9 GPIO_ACTIVE_LOW>; + label = "D31"; + }; + + led-2 { + color = ; + default-state = "on"; + gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; + label = "D32"; + }; + + led-3 { + color = ; + default-state = "on"; + gpios = <&gpio5 7 GPIO_ACTIVE_LOW>; + label = "D33"; + }; + + led-4 { + color = ; + default-state = "on"; + gpios = <&gpio5 6 GPIO_ACTIVE_LOW>; + label = "D34"; + }; + }; + + v_1_2: regulator-1-2 { + compatible = "regulator-fixed"; + regulator-name = "1v2"; + regulator-max-microvolt = <1200000>; + regulator-min-microvolt = <1200000>; + }; + + vmmc: regulator-mmc { + compatible = "regulator-fixed"; + regulator-name = "vmmc"; + pinctrl-0 = <&vmmc_pins>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + startup-delay-us = <250>; + gpio = <&gpio2 19 GPIO_ACTIVE_LOW>; + }; + + vbus1: regulator-vbus-1 { + compatible = "regulator-fixed"; + regulator-name = "vbus1"; + pinctrl-0 = <&vbus1_pins>; + pinctrl-names = "default"; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + gpio = <&gpio2 11 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + vbus2: regulator-vbus-2 { + compatible = "regulator-fixed"; + regulator-name = "vbus2"; + pinctrl-0 = <&vbus2_pins>; + pinctrl-names = "default"; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + gpio = <&gpio4 21 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + rfkill-mpcie-wifi { + compatible = "rfkill-gpio"; + /* rfkill-gpio inverts internally */ + shutdown-gpios = <&gpio2 20 GPIO_ACTIVE_HIGH>; + label = "mpcie WiFi"; + pinctrl-0 = <&pcie_rfkill_pins>; + pinctrl-names = "default"; + radio-type = "wlan"; + }; +}; + +&i2c3 { + clock-frequency = <100000>; + pinctrl-0 = <&i2c3_pins>; + pinctrl-names = "default"; + status = "okay"; + + hdmi@3d { + compatible = "adi,adv7535"; + reg = <0x3d>, <0x3f>, <0x3c>, <0x38>; + reg-names = "main", "edid", "cec", "packet"; + interrupt-parent = <&gpio1>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + a2vdd-supply = <&v_1_8>; + avdd-supply = <&v_1_8>; + dvdd-supply = <&v_1_8>; + pd-gpios = <&gpio3 22 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&hdmi_pins>; + pinctrl-names = "default"; + pvdd-supply = <&v_1_8>; + v3p3-supply = <&v_3_3>; + adi,dsi-lanes = <4>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + adv7535_from_dsim: endpoint { + remote-endpoint = <&mipi_dsi_out>; + }; + }; + + port@1 { + reg = <1>; + + adv7535_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; + + carrier_eeprom: eeprom@57 { + compatible = "st,24c02", "atmel,24c02"; + reg = <0x57>; + pagesize = <16>; + }; + + carrier_rtc: rtc@69 { + compatible = "abracon,ab1805"; + reg = <0x69>; + abracon,tc-diode = "schottky"; + abracon,tc-resistor = <3>; + }; +}; + +&iomuxc { + hdmi_pins: pinctrl-hdmi-grp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO07_GPIO1_IO7 0x0 + MX8MM_IOMUXC_SAI5_RXD1_GPIO3_IO22 0x0 + >; + }; + + i2c3_pins: pinctrl-i2c3-grp { + fsl,pins = < + MX8MM_IOMUXC_I2C3_SCL_I2C3_SCL 0x400001c3 + MX8MM_IOMUXC_I2C3_SDA_I2C3_SDA 0x400001c3 + >; + }; + + led_pins: pinctrl-led-grp { + fsl,pins = < + MX8MM_IOMUXC_UART4_TXD_GPIO5_IO29 0x0 + MX8MM_IOMUXC_ECSPI1_SS0_GPIO5_IO9 0x0 + MX8MM_IOMUXC_ECSPI1_MISO_GPIO5_IO8 0x0 + MX8MM_IOMUXC_ECSPI1_MOSI_GPIO5_IO7 0x0 + MX8MM_IOMUXC_ECSPI1_SCLK_GPIO5_IO6 0x0 + >; + }; + + pcie_rfkill_pins: pinctrl-pcie-rfkill-grp { + fsl,pins = < + MX8MM_IOMUXC_SD2_WP_GPIO2_IO20 0x0 + >; + }; + + usb_hub_pins: pinctrl-usb-hub-grp { + fsl,pins = < + MX8MM_IOMUXC_SAI3_RXD_GPIO4_IO30 0x0 + >; + }; + + usdhc2_100mhz_pins: pinctrl-usdhc2-100mhz-grp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x194 + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d4 + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d4 + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d4 + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4 + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x140 + MX8MM_IOMUXC_SD2_CD_B_USDHC2_CD_B 0x0 + >; + }; + + usdhc2_200mhz_pins: pinctrl-usdhc2-100mhz-grp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x196 + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d6 + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d6 + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d6 + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6 + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x140 + MX8MM_IOMUXC_SD2_CD_B_USDHC2_CD_B 0x0 + >; + }; + + usdhc2_pins: pinctrl-usdhc2-grp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x190 + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d0 + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d0 + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d0 + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0 + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x140 + MX8MM_IOMUXC_SD2_CD_B_USDHC2_CD_B 0x0 + >; + }; + + vbus1_pins: pinctrl-vbus-1-grp { + fsl,pins = < + MX8MM_IOMUXC_SD1_STROBE_GPIO2_IO11 0x20 + >; + }; + + vbus2_pins: pinctrl-vbus-2-grp { + fsl,pins = < + MX8MM_IOMUXC_SAI2_RXFS_GPIO4_IO21 0x20 + >; + }; + + vmmc_pins: pinctrl-vmmc-grp { + fsl,pins = < + MX8MM_IOMUXC_SD2_RESET_B_GPIO2_IO19 0x41 + >; + }; +}; + +&lcdif { + status = "okay"; +}; + +&mipi_dsi { + samsung,esc-clock-frequency = <10000000>; + status = "okay"; +}; + +&mipi_dsi_out { + remote-endpoint = <&adv7535_from_dsim>; +}; + +&usbotg1 { + dr_mode = "host"; + vbus-supply = <&vbus2>; + status = "okay"; +}; + +&usbotg2 { + #address-cells = <1>; + #size-cells = <0>; + dr_mode = "host"; + pinctrl-0 = <&usb_hub_pins>; + pinctrl-names = "default"; + vbus-supply = <&vbus1>; + status = "okay"; + + hub_2_0: hub@1 { + compatible = "usb4b4,6502", "usb4b4,6506"; + reg = <1>; + peer-hub = <&hub_3_0>; + reset-gpios = <&gpio4 30 GPIO_ACTIVE_LOW>; + vdd2-supply = <&v_3_3>; + vdd-supply = <&v_1_2>; + }; + + /* this device is not visible because host supports 2.0 only */ + hub_3_0: hub@2 { + compatible = "usb4b4,6500", "usb4b4,6504"; + reg = <2>; + peer-hub = <&hub_2_0>; + reset-gpios = <&gpio4 30 GPIO_ACTIVE_LOW>; + vdd2-supply = <&v_3_3>; + vdd-supply = <&v_1_2>; + }; +}; + +&usdhc2 { + bus-width = <4>; + pinctrl-0 = <&usdhc2_pins>; + pinctrl-1 = <&usdhc2_100mhz_pins>; + pinctrl-2 = <&usdhc2_200mhz_pins>; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + vmmc-supply = <&vmmc>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h b/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h index b1f11098d248..31557b7b9ccc 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h +++ b/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h @@ -6,6 +6,39 @@ #ifndef __DTS_IMX8MM_PINFUNC_H #define __DTS_IMX8MM_PINFUNC_H +/* Drive Strength */ +#define MX8MM_DSE_X1 0x0 +#define MX8MM_DSE_X2 0x4 +#define MX8MM_DSE_X4 0x2 +#define MX8MM_DSE_X6 0x6 + +/* Slew Rate */ +#define MX8MM_FSEL_FAST 0x10 +#define MX8MM_FSEL_SLOW 0x0 + +/* Open Drain */ +#define MX8MM_ODE_ENABLE 0x20 +#define MX8MM_ODE_DISABLE 0x0 + +#define MX8MM_PULL_DOWN 0x0 +#define MX8MM_PULL_UP 0x40 + +/* Hysteresis */ +#define MX8MM_HYS_CMOS 0x0 +#define MX8MM_HYS_SCHMITT 0x80 + +#define MX8MM_PULL_ENABLE 0x100 +#define MX8MM_PULL_DISABLE 0x0 + +/* SION force input mode */ +#define MX8MM_SION 0x40000000 + +/* long defaults */ +#define MX8MM_USDHC_DATA_DEFAULT (MX8MM_FSEL_FAST | MX8MM_PULL_UP | \ + MX8MM_HYS_SCHMITT | MX8MM_PULL_ENABLE) +#define MX8MM_I2C_DEFAULT (MX8MM_DSE_X6 | MX8MM_PULL_UP | MX8MM_HYS_SCHMITT | \ + MX8MM_PULL_ENABLE | MX8MM_SION) + /* * The pin function ID is a tuple of * diff --git a/arch/arm64/boot/dts/freescale/imx8mm-sr-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-sr-som.dtsi new file mode 100644 index 000000000000..8d0249f1e92d --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-sr-som.dtsi @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2025 Josua Mayer + */ + +#include + +#include "imx8mm.dtsi" + +/ { + compatible = "solidrun,imx8mm-sr-som", "fsl,imx8mm"; + model = "SolidRun i.MX8MM SoM"; + + chosen { + bootargs = "earlycon=ec_imx6q,0x30890000,115200"; + stdout-path = &uart2; + }; + + v_1_8: regulator-1-8 { + compatible = "regulator-fixed"; + regulator-name = "1v8"; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + }; + + v_3_3: regulator-3-3 { + compatible = "regulator-fixed"; + regulator-name = "3v3"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + }; + + usdhc1_pwrseq: usdhc1-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>; + }; + + memory@40000000 { + reg = <0x0 0x40000000 0 0x80000000>; + device_type = "memory"; + }; +}; + +&fec1 { + phy = <&phy0>; + phy-mode = "rgmii-id"; + pinctrl-0 = <&fec1_pins>; + pinctrl-names = "default"; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@4 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x4>; + phy-reset-duration = <10>; + reset-gpios = <&gpio4 22 GPIO_ACTIVE_LOW>; + vddio-supply = <&vddio>; + qca,smarteee-tw-us-1g = <24>; + + vddio: vddio-regulator { + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + }; + }; + }; +}; + +&i2c1 { + clock-frequency = <400000>; + pinctrl-0 = <&i2c1_pins>; + pinctrl-names = "default"; + status = "okay"; + + pmic@4b { + compatible = "rohm,bd71847"; + reg = <0x4b>; + interrupt-parent = <&gpio1>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + clocks = <&osc_32k>; + #clock-cells = <0>; + clock-output-names = "clk-32k-out"; + pinctrl-0 = <&pmic_pins>; + pinctrl-names = "default"; + rohm,reset-snvs-powered; + + regulators { + buck1_reg: BUCK1 { + regulator-name = "buck1"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1300000>; + regulator-min-microvolt = <700000>; + regulator-ramp-delay = <1250>; + }; + + buck2_reg: BUCK2 { + regulator-name = "buck2"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1300000>; + regulator-min-microvolt = <700000>; + regulator-ramp-delay = <1250>; + rohm,dvs-idle-voltage = <900000>; + rohm,dvs-run-voltage = <1000000>; + }; + + buck3_reg: BUCK3 { + // BUCK5 in datasheet + regulator-name = "buck3"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1350000>; + regulator-min-microvolt = <700000>; + }; + + buck4_reg: BUCK4 { + // BUCK6 in datasheet + regulator-name = "buck4"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3000000>; + }; + + buck5_reg: BUCK5 { + // BUCK7 in datasheet + regulator-name = "buck5"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1995000>; + regulator-min-microvolt = <1605000>; + }; + + buck6_reg: BUCK6 { + // BUCK8 in datasheet + regulator-name = "buck6"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1400000>; + regulator-min-microvolt = <800000>; + }; + + ldo1_reg: LDO1 { + regulator-name = "ldo1"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <1600000>; + }; + + ldo2_reg: LDO2 { + regulator-name = "ldo2"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <900000>; + regulator-min-microvolt = <800000>; + }; + + ldo3_reg: LDO3 { + regulator-name = "ldo3"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <1800000>; + }; + + ldo4_reg: LDO4 { + regulator-name = "ldo4"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <900000>; + }; + + ldo6_reg: LDO6 { + regulator-name = "ldo6"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <900000>; + }; + }; + }; + + som_eeprom: eeprom@50 { + compatible = "st,24c01", "atmel,24c01"; + reg = <0x50>; + pagesize = <16>; + }; +}; + +&iomuxc { + fec1_pins: pinctrl-fec1-grp { + fsl,pins = < + MX8MM_IOMUXC_ENET_MDC_ENET1_MDC 0x3 + MX8MM_IOMUXC_ENET_MDIO_ENET1_MDIO 0x3 + MX8MM_IOMUXC_ENET_TD3_ENET1_RGMII_TD3 0x1f + MX8MM_IOMUXC_ENET_TD2_ENET1_RGMII_TD2 0x1f + MX8MM_IOMUXC_ENET_TD1_ENET1_RGMII_TD1 0x1f + MX8MM_IOMUXC_ENET_TD0_ENET1_RGMII_TD0 0x1f + MX8MM_IOMUXC_ENET_RD3_ENET1_RGMII_RD3 0x91 + MX8MM_IOMUXC_ENET_RD2_ENET1_RGMII_RD2 0x91 + MX8MM_IOMUXC_ENET_RD1_ENET1_RGMII_RD1 0x91 + MX8MM_IOMUXC_ENET_RD0_ENET1_RGMII_RD0 0x91 + MX8MM_IOMUXC_ENET_TXC_ENET1_RGMII_TXC 0x1f + MX8MM_IOMUXC_ENET_RXC_ENET1_RGMII_RXC 0x91 + MX8MM_IOMUXC_ENET_RX_CTL_ENET1_RGMII_RX_CTL 0x91 + MX8MM_IOMUXC_ENET_TX_CTL_ENET1_RGMII_TX_CTL 0x1f + MX8MM_IOMUXC_SAI2_RXC_GPIO4_IO22 0x19 + >; + }; + + i2c1_pins: pinctrl-i2c1-grp { + fsl,pins = < + MX8MM_IOMUXC_I2C1_SCL_I2C1_SCL 0x400001c3 + MX8MM_IOMUXC_I2C1_SDA_I2C1_SDA 0x400001c3 + >; + }; + + pcie_pins: pinctrl-pcie-grp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO15_GPIO1_IO15 0x0 + >; + }; + + pmic_pins: pinctrl-pmic-grp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO03_GPIO1_IO3 0x140 + >; + }; + + uart1_pins: pinctrl-uart1-grp { + fsl,pins = < + MX8MM_IOMUXC_UART1_RXD_UART1_DCE_RX 0x140 + MX8MM_IOMUXC_UART1_TXD_UART1_DCE_TX 0x140 + MX8MM_IOMUXC_UART3_RXD_UART1_DCE_CTS_B 0x140 + MX8MM_IOMUXC_UART3_TXD_UART1_DCE_RTS_B 0x140 + /* BT_REG_ON */ + MX8MM_IOMUXC_SD1_DATA4_GPIO2_IO6 0x0 + /* BT_WAKE_DEV */ + MX8MM_IOMUXC_SD1_DATA5_GPIO2_IO7 0x0 + /* BT_WAKE_HOST */ + MX8MM_IOMUXC_SD1_DATA6_GPIO2_IO8 0x100 + >; + }; + + uart2_pins: pinctrl-uart2-grp { + fsl,pins = < + MX8MM_IOMUXC_UART2_RXD_UART2_DCE_RX 0x140 + MX8MM_IOMUXC_UART2_TXD_UART2_DCE_TX 0x140 + >; + }; + + usdhc1_pins: pinctrl-usdhc1-grp { + fsl,pins = < + MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK 0x190 + MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD 0x1d0 + MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x1d0 + MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x1d0 + MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x1d0 + MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x1d0 + /* wifi refclk */ + MX8MM_IOMUXC_GPIO1_IO00_ANAMIX_REF_CLK_32K 0x0 + /* WL_REG_ON */ + MX8MM_IOMUXC_SD1_RESET_B_GPIO2_IO10 0x0 + /* WL_WAKE_HOST */ + MX8MM_IOMUXC_SD1_DATA7_GPIO2_IO9 0x100 + >; + }; + + usdhc3_100mhz_pins: pinctrl-usdhc3-100mhz-grp { + fsl,pins = < + MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x194 + MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d4 + MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d4 + MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d4 + MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d4 + MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d4 + MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d4 + MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d4 + MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d4 + MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d4 + MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x194 + >; + }; + + usdhc3_200mhz_pins: pinctrl-usdhc3-200mhz-grp { + fsl,pins = < + MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x196 + MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d6 + MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d6 + MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d6 + MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d6 + MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d6 + MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d6 + MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d6 + MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d6 + MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d6 + MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x196 + >; + }; + + usdhc3_pins: pinctrl-usdhc3-grp { + fsl,pins = < + MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x190 + MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d0 + MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d0 + MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d0 + MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d0 + MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d0 + MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d0 + MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d0 + MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d0 + MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d0 + MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x190 + >; + }; + + wdog1_pins: pinctrl-wdog1-grp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO02_WDOG1_WDOG_B 0x140 + >; + }; +}; + +/* assembly-option for AI accelerator on SoM, otherwise routed to carrier */ +&pcie0 { + pinctrl-0 = <&pcie_pins>; + pinctrl-names = "default"; + reset-gpios = <&gpio1 15 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + status = "okay"; +}; + +&pcie_phy { + fsl,clkreq-unsupported; + fsl,refclk-pad-mode = ; + status = "okay"; +}; + +&uart1 { + /* select 80MHz parent clock to support maximum baudrate 4Mbps */ + assigned-clock-parents = <&clk IMX8MM_SYS_PLL1_80M>; + assigned-clocks = <&clk IMX8MM_CLK_UART1>; + pinctrl-0 = <&uart1_pins>; + pinctrl-names = "default"; + uart-has-rtscts; + status = "okay"; + + bluetooth { + compatible = "brcm,bcm4330-bt"; + device-wakeup-gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>; + host-wakeup-gpios = <&gpio2 8 GPIO_ACTIVE_HIGH>; + max-speed = <3000000>; + shutdown-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>; + }; +}; + +&uart2 { + pinctrl-0 = <&uart2_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&usdhc1 { + bus-width = <4>; + mmc-pwrseq = <&usdhc1_pwrseq>; + pinctrl-0 = <&usdhc1_pins>; + pinctrl-names = "default"; + vmmc-supply = <&v_3_3>; + vqmmc-supply = <&v_1_8>; + status = "okay"; +}; + +&usdhc3 { + bus-width = <8>; + non-removable; + pinctrl-0 = <&usdhc3_pins>; + pinctrl-1 = <&usdhc3_100mhz_pins>; + pinctrl-2 = <&usdhc3_200mhz_pins>; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + vmmc-supply = <&v_3_3>; + vqmmc-supply = <&v_1_8>; + status = "okay"; +}; + +&wdog1 { + pinctrl-0 = <&wdog1_pins>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml-mba8mx-lvds-g133han01.dtso b/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml-mba8mx-lvds-g133han01.dtso new file mode 100644 index 000000000000..ce12bc46553d --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml-mba8mx-lvds-g133han01.dtso @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) +/* + * Copyright (c) 2019-2026 TQ-Systems GmbH , + * D-82229 Seefeld, Germany. + * Author: Alexander Stein + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&backlight_lvds { + status = "okay"; +}; + +&dsi_lvds_bridge { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@2 { + reg = <2>; + + lvds_bridge_out0: endpoint { + remote-endpoint = <&panel_in_lvds0>; + }; + }; + + port@3 { + reg = <3>; + + lvds_bridge_out1: endpoint { + remote-endpoint = <&panel_in_lvds1>; + }; + }; + }; +}; + +&expander0 { + dsi-mux-oe-hog { + gpio-hog; + gpios = <10 GPIO_ACTIVE_LOW>; + output-high; + line-name = "DSI_MUX_OE#"; + }; +}; + +&lcdif { + status = "okay"; +}; + +&mipi_dsi { + status = "okay"; +}; + +&panel { + compatible = "auo,g133han01"; + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dual-lvds-odd-pixels; + + panel_in_lvds0: endpoint { + remote-endpoint = <&lvds_bridge_out0>; + }; + }; + + port@1 { + reg = <1>; + dual-lvds-even-pixels; + + panel_in_lvds1: endpoint { + remote-endpoint = <&lvds_bridge_out1>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml-mba8mx-lvds-tm070jvhg33.dtso b/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml-mba8mx-lvds-tm070jvhg33.dtso index e44249c6d8a0..046399a455ba 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml-mba8mx-lvds-tm070jvhg33.dtso +++ b/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml-mba8mx-lvds-tm070jvhg33.dtso @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) /* - * Copyright (c) 2022-2023 TQ-Systems GmbH , + * Copyright (c) 2022-2026 TQ-Systems GmbH , * D-82229 Seefeld, Germany. * Author: Alexander Stein */ @@ -10,10 +10,6 @@ #include -&{/} { - compatible = "tq,imx8mm-tqma8mqml-mba8mx", "tq,imx8mm-tqma8mqml", "fsl,imx8mm"; -}; - &backlight_lvds { status = "okay"; }; @@ -36,7 +32,8 @@ }; &mipi_dsi { - status = "okay"; + samsung,burst-clock-frequency = <600000000>; + status = "okay"; }; &panel { diff --git a/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml-mba8mx.dts b/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml-mba8mx.dts index 8dcc5cbcb8f6..8490b7b04e9b 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml-mba8mx.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml-mba8mx.dts @@ -76,7 +76,6 @@ }; &mipi_dsi { - samsung,burst-clock-frequency = <891000000>; samsung,esc-clock-frequency = <20000000>; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml.dtsi index 29b298af0d73..1b5ba3c47164 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-tqma8mqml.dtsi @@ -292,7 +292,7 @@ }; pinctrl_pmic: pmicgrp { - fsl,pins = ; + fsl,pins = ; }; pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { diff --git a/arch/arm64/boot/dts/freescale/imx8mm-tx8m-1610-moduline-iv-306-d.dts b/arch/arm64/boot/dts/freescale/imx8mm-tx8m-1610-moduline-iv-306-d.dts new file mode 100644 index 000000000000..6cc04aa90f21 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-tx8m-1610-moduline-iv-306-d.dts @@ -0,0 +1,799 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2025 Maud Spierings + */ + +/dts-v1/; + +#include + +#include "imx8mm-tx8m-1610.dtsi" + +/ { + chassis-type = "embedded"; + compatible = "gocontroll,moduline-iv-306-d", "karo,tx8m-1610", "fsl,imx8mm"; + hardware = "Moduline IV V3.06-D"; + model = "GOcontroll Moduline IV"; + + aliases { + spi0 = &ecspi2; /* spidev number compatibility */ + spi1 = &ecspi3; /* spidev number compatibility */ + spi2 = &ecspi1; /* spidev number compatibility */ + }; + + chosen { + stdout-path = "serial2:115200n8"; + }; + + mcp_clock: mcp-clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <20000000>; + }; + + reg_3v3_m2: regulator-3v3-m2 { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio4 8 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&pinctrl_reg_m2>; + pinctrl-names = "default"; + power-supply = <®_6v4>; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "3v3-m.2"; + }; + + reg_5v0: regulator-5v0 { + compatible = "regulator-fixed"; + power-supply = <®_6v4>; + regulator-always-on; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + regulator-name = "5v0"; + }; + + reg_6v4: regulator-6v4 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-max-microvolt = <6400000>; + regulator-min-microvolt = <6400000>; + regulator-name = "6v4"; + }; + + reg_can1_stby: regulator-can1-stby { + compatible = "regulator-fixed"; + gpio = <&gpio3 16 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_can1_reg>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "can1-stby"; + }; + + reg_can2_stby: regulator-can2-stby { + compatible = "regulator-fixed"; + gpio = <&gpio3 17 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_can2_reg>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "can2-stby"; + }; + + reg_can3_stby: regulator-can3-stby { + compatible = "regulator-fixed"; + gpio = <&gpio1 11 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_can3_reg>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "can3-stby"; + }; + + reg_can4_stby: regulator-can4-stby { + compatible = "regulator-fixed"; + gpio = <&gpio3 8 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_can4_reg>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "can4-stby"; + }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-0 = <&pinctrl_wl_reg>; + pinctrl-names = "default"; + post-power-on-delay-ms = <100>; + power-off-delay-us = <500000>; + reset-gpios = <&gpio3 3 GPIO_ACTIVE_LOW>; + }; +}; + +/* SPI 2 */ +&ecspi1 { + cs-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>, + <&gpio1 0 GPIO_ACTIVE_LOW>, + <&gpio5 2 GPIO_ACTIVE_LOW>, + <&gpio4 27 GPIO_ACTIVE_LOW>, + <&gpio3 1 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_ecspi1>; + pinctrl-names = "default"; + status = "okay"; + + connector@0 { + compatible = "gocontroll,moduline-module-slot"; + reg = <0>; + i2c-bus = <&i2c2>; + interrupt-parent = <&gpio1>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; + slot-number = <3>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_vdd_3v3>; + }; + + connector@1 { + compatible = "gocontroll,moduline-module-slot"; + reg = <1>; + i2c-bus = <&i2c2>; + interrupt-parent = <&gpio5>; + interrupts = <21 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio3 15 GPIO_ACTIVE_LOW>; + slot-number = <4>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_vdd_3v3>; + }; + + connector@2 { + compatible = "gocontroll,moduline-module-slot"; + reg = <2>; + i2c-bus = <&i2c2>; + interrupt-parent = <&gpio5>; + interrupts = <1 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio4 25 GPIO_ACTIVE_LOW>; + slot-number = <5>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_vdd_3v3>; + }; + + connector@3 { + compatible = "gocontroll,moduline-module-slot"; + reg = <3>; + i2c-bus = <&i2c2>; + interrupt-parent = <&gpio4>; + interrupts = <26 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio4 28 GPIO_ACTIVE_LOW>; + slot-number = <6>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_vdd_3v3>; + }; + + adc@4 { + compatible = "microchip,mcp3004"; + reg = <4>; + spi-max-frequency = <2300000>; + vref-supply = <®_vdd_3v3>; + }; +}; + +&ecspi2 { + cs-gpios = <&gpio3 23 GPIO_ACTIVE_LOW>, + <&gpio5 9 GPIO_ACTIVE_LOW>, + <&gpio3 2 GPIO_ACTIVE_LOW>, + <&gpio5 25 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_ecspi2>; + pinctrl-names = "default"; + status = "okay"; + + connector@0 { + compatible = "gocontroll,moduline-module-slot"; + reg = <0>; + i2c-bus = <&i2c2>; + interrupt-parent = <&gpio3>; + interrupts = <19 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio3 24 GPIO_ACTIVE_LOW>; + slot-number = <7>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_vdd_3v3>; + }; + + connector@1 { + compatible = "gocontroll,moduline-module-slot"; + reg = <1>; + i2c-bus = <&i2c2>; + interrupt-parent = <&gpio3>; + interrupts = <22 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio3 21 GPIO_ACTIVE_LOW>; + slot-number = <8>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_vdd_3v3>; + }; + + can@2 { + compatible = "microchip,mcp25625"; + reg = <2>; + clocks = <&mcp_clock>; + interrupt-parent = <&gpio3>; + interrupts = <4 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&pinctrl_can1>; + pinctrl-names = "default"; + spi-max-frequency = <10000000>; + vdd-supply = <®_vdd_3v3>; + xceiver-supply = <®_can1_stby>; + }; + + can@3 { + compatible = "microchip,mcp25625"; + reg = <3>; + clocks = <&mcp_clock>; + interrupt-parent = <&gpio3>; + interrupts = <13 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&pinctrl_can2>; + pinctrl-names = "default"; + spi-max-frequency = <10000000>; + vdd-supply = <®_vdd_3v3>; + xceiver-supply = <®_can2_stby>; + }; +}; + +&ecspi3 { + cs-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>, + <&gpio1 10 GPIO_ACTIVE_LOW>, + <&gpio5 5 GPIO_ACTIVE_LOW>, + <&gpio5 4 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_ecspi3>; + pinctrl-names = "default"; + status = "okay"; + + connector@0 { + compatible = "gocontroll,moduline-module-slot"; + reg = <0>; + i2c-bus = <&i2c2>; + interrupt-parent = <&gpio1>; + interrupts = <6 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>; + slot-number = <1>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_vdd_3v3>; + }; + + connector@1 { + compatible = "gocontroll,moduline-module-slot"; + reg = <1>; + i2c-bus = <&i2c2>; + interrupt-parent = <&gpio5>; + interrupts = <20 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio3 11 GPIO_ACTIVE_LOW>; + slot-number = <2>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_vdd_3v3>; + }; + + can@2 { + compatible = "microchip,mcp25625"; + reg = <2>; + clocks = <&mcp_clock>; + interrupt-parent = <&gpio3>; + interrupts = <12 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&pinctrl_can3>; + pinctrl-names = "default"; + spi-max-frequency = <10000000>; + vdd-supply = <®_vdd_3v3>; + xceiver-supply = <®_can3_stby>; + }; + + can@3 { + compatible = "microchip,mcp25625"; + reg = <3>; + clocks = <&mcp_clock>; + interrupt-parent = <&gpio3>; + interrupts = <18 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&pinctrl_can4>; + pinctrl-names = "default"; + spi-max-frequency = <10000000>; + vdd-supply = <®_vdd_3v3>; + xceiver-supply = <®_can4_stby>; + }; +}; + +&gpu_2d { + status = "disabled"; +}; + +&gpu_3d { + status = "disabled"; +}; + +&i2c2 { + clock-frequency = <400000>; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&pinctrl_i2c2>; + pinctrl-1 = <&pinctrl_i2c2_gpio>; + scl-gpios = <&gpio5 16 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio5 17 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; +}; + +&i2c3 { + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_i2c3>; + pinctrl-1 = <&pinctrl_i2c3_gpio>; + pinctrl-names = "default", "gpio"; + scl-gpios = <&gpio5 18 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio5 19 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; + + lp5012@14 { + compatible = "ti,lp5012"; + reg = <0x14>; + vled-supply = <®_6v4>; + #address-cells = <1>; + #size-cells = <0>; + + multi-led@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + color = ; + label = "case-led1"; + + led@0 { + color = ; + reg = <0>; + }; + + led@1 { + color = ; + reg = <1>; + }; + + led@2 { + color = ; + reg = <2>; + }; + }; + + multi-led@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + color = ; + label = "case-led2"; + + led@0 { + color = ; + reg = <0>; + }; + + led@1 { + color = ; + reg = <1>; + }; + + led@2 { + color = ; + reg = <2>; + }; + }; + + multi-led@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + color = ; + label = "case-led3"; + + led@0 { + color = ; + reg = <0>; + }; + + led@1 { + color = ; + reg = <1>; + }; + + led@2 { + color = ; + reg = <2>; + }; + }; + + multi-led@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + color = ; + label = "case-led4"; + + led@0 { + color = ; + reg = <0>; + }; + + led@1 { + color = ; + reg = <1>; + }; + + led@2 { + color = ; + reg = <2>; + }; + }; + }; +}; + +&iomuxc { + pinctrl_bt: btgrp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO01_GPIO1_IO1 + MX8MM_DSE_X1 + MX8MM_IOMUXC_SAI5_MCLK_GPIO3_IO25 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE | MX8MM_HYS_SCHMITT) + MX8MM_IOMUXC_ECSPI2_SS0_GPIO5_IO13 + MX8MM_DSE_X1 + >; + }; + + pinctrl_can1: can1grp { + fsl,pins = < + MX8MM_IOMUXC_NAND_ALE_GPIO3_IO0 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_NAND_CE3_B_GPIO3_IO4 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE | MX8MM_HYS_SCHMITT) + >; + }; + + pinctrl_can1_reg: can1reggrp { + fsl,pins = < + MX8MM_IOMUXC_NAND_READY_B_GPIO3_IO16 + (MX8MM_DSE_X2 | MX8MM_FSEL_FAST | MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_can2: can2grp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_NAND_DATA07_GPIO3_IO13 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE | MX8MM_HYS_SCHMITT) + >; + }; + + pinctrl_can2_reg: can2reggrp { + fsl,pins = < + MX8MM_IOMUXC_NAND_WE_B_GPIO3_IO17 + (MX8MM_DSE_X2 | MX8MM_FSEL_FAST | MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_can3: can3grp { + fsl,pins = < + MX8MM_IOMUXC_SPDIF_TX_GPIO5_IO3 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_NAND_DATA06_GPIO3_IO12 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE | MX8MM_HYS_SCHMITT) + >; + }; + + pinctrl_can3_reg: can3reggrp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO11_GPIO1_IO11 + (MX8MM_DSE_X2 | MX8MM_FSEL_FAST | MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_can4: can4grp { + fsl,pins = < + MX8MM_IOMUXC_NAND_DQS_GPIO3_IO14 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_NAND_WP_B_GPIO3_IO18 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE | MX8MM_HYS_SCHMITT) + >; + }; + + pinctrl_can4_reg: can4reggrp { + fsl,pins = < + MX8MM_IOMUXC_NAND_DATA02_GPIO3_IO8 + (MX8MM_DSE_X2 | MX8MM_FSEL_FAST | MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_ecspi1: ecspi1grp { + fsl,pins = < + MX8MM_IOMUXC_ECSPI1_MOSI_ECSPI1_MOSI + MX8MM_DSE_X4 + MX8MM_IOMUXC_ECSPI1_MISO_ECSPI1_MISO + (MX8MM_DSE_X4 | MX8MM_HYS_SCHMITT) + MX8MM_IOMUXC_ECSPI1_SCLK_ECSPI1_SCLK + MX8MM_DSE_X4 + MX8MM_IOMUXC_GPIO1_IO09_GPIO1_IO9 + MX8MM_DSE_X1 + MX8MM_IOMUXC_GPIO1_IO00_GPIO1_IO0 + MX8MM_DSE_X1 + MX8MM_IOMUXC_SAI3_MCLK_GPIO5_IO2 + MX8MM_DSE_X1 + MX8MM_IOMUXC_SAI2_MCLK_GPIO4_IO27 + MX8MM_DSE_X1 + MX8MM_IOMUXC_NAND_CE0_B_GPIO3_IO1 + MX8MM_DSE_X1 + >; + }; + + pinctrl_ecspi2: ecspi2grp { + fsl,pins = < + MX8MM_IOMUXC_ECSPI2_MOSI_ECSPI2_MOSI + MX8MM_DSE_X4 + MX8MM_IOMUXC_ECSPI2_MISO_ECSPI2_MISO + (MX8MM_DSE_X4 | MX8MM_HYS_SCHMITT) + MX8MM_IOMUXC_ECSPI2_SCLK_ECSPI2_SCLK + MX8MM_DSE_X4 + MX8MM_IOMUXC_SAI5_RXD2_GPIO3_IO23 + MX8MM_DSE_X1 + MX8MM_IOMUXC_ECSPI1_SS0_GPIO5_IO9 + MX8MM_DSE_X1 + MX8MM_IOMUXC_NAND_CE1_B_GPIO3_IO2 + MX8MM_DSE_X1 + MX8MM_IOMUXC_UART2_TXD_GPIO5_IO25 + MX8MM_DSE_X1 + >; + }; + + pinctrl_ecspi3: ecspi3grp { + fsl,pins = < + MX8MM_IOMUXC_UART1_TXD_ECSPI3_MOSI + MX8MM_DSE_X4 + MX8MM_IOMUXC_UART2_RXD_ECSPI3_MISO + (MX8MM_DSE_X4 | MX8MM_HYS_SCHMITT) + MX8MM_IOMUXC_UART1_RXD_ECSPI3_SCLK + MX8MM_DSE_X4 + MX8MM_IOMUXC_GPIO1_IO04_GPIO1_IO4 + MX8MM_DSE_X1 + MX8MM_IOMUXC_GPIO1_IO10_GPIO1_IO10 + MX8MM_DSE_X1 + MX8MM_IOMUXC_SPDIF_EXT_CLK_GPIO5_IO5 + MX8MM_DSE_X1 + MX8MM_IOMUXC_SPDIF_RX_GPIO5_IO4 + MX8MM_DSE_X1 + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX8MM_IOMUXC_I2C2_SCL_I2C2_SCL + MX8MM_I2C_DEFAULT + MX8MM_IOMUXC_I2C2_SDA_I2C2_SDA + MX8MM_I2C_DEFAULT + >; + }; + + pinctrl_i2c2_gpio: i2c2-gpiogrp { + fsl,pins = < + MX8MM_IOMUXC_I2C2_SCL_GPIO5_IO16 + MX8MM_I2C_DEFAULT + MX8MM_IOMUXC_I2C2_SDA_GPIO5_IO17 + MX8MM_I2C_DEFAULT + >; + }; + + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX8MM_IOMUXC_I2C3_SCL_I2C3_SCL + MX8MM_I2C_DEFAULT + MX8MM_IOMUXC_I2C3_SDA_I2C3_SDA + MX8MM_I2C_DEFAULT + >; + }; + + pinctrl_i2c3_gpio: i2c3-gpiogrp { + fsl,pins = < + MX8MM_IOMUXC_I2C3_SCL_GPIO5_IO18 + MX8MM_I2C_DEFAULT + MX8MM_IOMUXC_I2C3_SDA_GPIO5_IO19 + MX8MM_I2C_DEFAULT + >; + }; + + pinctrl_reg_m2: reg-m2grp { + fsl,pins = < + MX8MM_IOMUXC_SAI1_RXD6_GPIO4_IO8 + MX8MM_DSE_X1 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX8MM_IOMUXC_SAI2_RXC_UART1_DCE_RX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI2_RXFS_UART1_DCE_TX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI2_RXD0_UART1_DCE_RTS_B + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI2_TXFS_UART1_DCE_CTS_B + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_uart2: uart2grp { + fsl,pins = < + MX8MM_IOMUXC_SAI3_TXFS_UART2_DCE_RX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI3_TXC_UART2_DCE_TX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI3_RXD_UART2_DCE_RTS_B + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI3_RXC_UART2_DCE_CTS_B + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_uart3: uart3grp { + fsl,pins = < + MX8MM_IOMUXC_UART3_RXD_UART3_DCE_RX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_UART3_TXD_UART3_DCE_TX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_uart4: uart4grp { + fsl,pins = < + MX8MM_IOMUXC_UART4_RXD_UART4_DCE_RX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_UART4_TXD_UART4_DCE_TX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_usdhc2: pinctrlusdhc2grp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK + (MX8MM_DSE_X2 | MX8MM_FSEL_FAST | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + >; + }; + + pinctrl_wl_int: wlintgrp { + fsl,pins = < + MX8MM_IOMUXC_SAI5_RXC_GPIO3_IO20 + (MX8MM_PULL_UP | MX8MM_HYS_SCHMITT | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_wl_reg: wlreggrp { + fsl,pins = < + MX8MM_IOMUXC_NAND_CE2_B_GPIO3_IO3 + MX8MM_DSE_X1 + >; + }; +}; + +&uart1 { + pinctrl-0 = <&pinctrl_uart1>; + pinctrl-names = "default"; + uart-has-rtscts; + status = "okay"; + + bluetooth { + compatible = "infineon,cyw43439-bt", "brcm,bcm4329-bt"; + device-wakeup-gpios = <&gpio5 13 GPIO_ACTIVE_HIGH>; + interrupt-names = "host-wakeup"; + interrupt-parent = <&gpio3>; + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; + max-speed = <921600>; + pinctrl-0 = <&pinctrl_bt>; + pinctrl-names = "default"; + shutdown-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>; + vbat-supply = <®_3v3_m2>; + vddio-supply = <®_3v3_m2>; + }; +}; + +&uart2 { + pinctrl-0 = <&pinctrl_uart2>; + pinctrl-names = "default"; + uart-has-rtscts; + status = "okay"; +}; + +&uart3 { + pinctrl-0 = <&pinctrl_uart3>; + pinctrl-names = "default"; + status = "okay"; +}; + +&uart4 { + pinctrl-0 = <&pinctrl_uart4>; + pinctrl-names = "default"; + status = "okay"; +}; + +&usbotg1 { + disable-over-current; + dr_mode = "peripheral"; + status = "okay"; +}; + +&usbotg2 { + disable-over-current; + dr_mode = "host"; + vbus-supply = <®_5v0>; + status = "okay"; +}; + +&usdhc2 { + #address-cells = <1>; + #size-cells = <0>; + cap-power-off-card; + keep-power-in-suspend; + max-frequency = <50000000>; + mmc-pwrseq = <&wifi_pwrseq>; + non-removable; + pinctrl-0 = <&pinctrl_usdhc2>; + pinctrl-names = "default"; + sd-uhs-sdr25; + vmmc-supply = <®_3v3_m2>; + status = "okay"; + + wifi@1 { + compatible = "infineon,cyw43439-fmac", "brcm,bcm4329-fmac"; + reg = <1>; + pinctrl-0 = <&pinctrl_wl_int>; + pinctrl-names = "default"; + interrupt-names = "host-wake"; + interrupt-parent = <&gpio3>; + interrupts = <20 IRQ_TYPE_LEVEL_LOW>; + brcm,board-type = "GOcontroll,moduline"; + }; +}; + +&vpu_blk_ctrl { + status = "disabled"; +}; + +&vpu_g1 { + status = "disabled"; +}; + +&vpu_g2 { + status = "disabled"; +}; + +&wdog1 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-tx8m-1610-moduline-mini-111.dts b/arch/arm64/boot/dts/freescale/imx8mm-tx8m-1610-moduline-mini-111.dts new file mode 100644 index 000000000000..39b7d9077e8e --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-tx8m-1610-moduline-mini-111.dts @@ -0,0 +1,687 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2025 Maud Spierings + */ + +/dts-v1/; + +#include + +#include "imx8mm-tx8m-1610.dtsi" + +/ { + chassis-type = "embedded"; + compatible = "gocontroll,moduline-mini-111", "karo,tx8m-1610", "fsl,imx8mm"; + hardware = "Moduline Mini V1.11"; + model = "GOcontroll Moduline Mini"; + + aliases { + spi0 = &ecspi2; /* spidev number compatibility */ + spi1 = &ecspi3; /* spidev number compatibility */ + spi2 = &ecspi1; /* spidev number compatibility */ + }; + + chosen { + stdout-path = "serial2:115200n8"; + }; + + mcp_clock: mcp-clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <20000000>; + }; + + reg_3v3_comm: regulator-3v3-communication { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio1 11 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&pinctrl_reg_comm>; + pinctrl-names = "default"; + power-supply = <®_6v4>; + /* also powers the cellular modem which can't vote on the regulator */ + regulator-always-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "3v3_comm"; + }; + + reg_5v0: regulator-5v0 { + compatible = "regulator-fixed"; + power-supply = <®_6v4>; + regulator-always-on; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + regulator-name = "5v0"; + }; + + reg_6v4: regulator-6v4 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-max-microvolt = <6400000>; + regulator-min-microvolt = <6400000>; + regulator-name = "6v4"; + }; + + reg_can1_stby: regulator-can1-stby { + compatible = "regulator-fixed"; + gpio = <&gpio2 12 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_can1_reg>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "can1-stby"; + }; + + reg_can2_stby: regulator-can2-stby { + compatible = "regulator-fixed"; + gpio = <&gpio3 15 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_can2_reg>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "can2-stby"; + }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-0 = <&pinctrl_wl_reg>; + pinctrl-names = "default"; + post-power-on-delay-ms = <100>; + power-off-delay-us = <500000>; + reset-gpios = <&gpio5 28 GPIO_ACTIVE_LOW>; + }; +}; + +&ecspi1 { + cs-gpios = <&gpio4 27 GPIO_ACTIVE_LOW>, + <&gpio3 23 GPIO_ACTIVE_LOW>, + <&gpio3 1 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_ecspi1>; + pinctrl-names = "default"; + status = "okay"; + + connector@0 { + compatible = "gocontroll,moduline-module-slot"; + reg = <0>; + i2c-bus = <&i2c2>; + interrupt-parent = <&gpio4>; + interrupts = <26 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio4 28 GPIO_ACTIVE_LOW>; + slot-number = <3>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_vdd_3v3>; + }; + + connector@1 { + compatible = "gocontroll,moduline-module-slot"; + reg = <1>; + i2c-bus = <&i2c2>; + interrupt-parent = <&gpio3>; + interrupts = <19 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio3 21 GPIO_ACTIVE_LOW>; + slot-number = <4>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_vdd_3v3>; + }; + + adc@2 { + compatible = "microchip,mcp3004"; + reg = <2>; + spi-max-frequency = <2300000>; + vref-supply = <®_vdd_3v3>; + }; +}; + +&ecspi2 { + cs-gpios = <&gpio3 24 GPIO_ACTIVE_LOW>, + <&gpio3 9 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_ecspi2>; + pinctrl-names = "default"; + status = "okay"; + + can@0 { + compatible = "microchip,mcp25625"; + reg = <0>; + clocks = <&mcp_clock>; + interrupt-parent = <&gpio3>; + interrupts = <22 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&pinctrl_can1>; + pinctrl-names = "default"; + spi-max-frequency = <10000000>; + vdd-supply = <®_vdd_3v3>; + xceiver-supply = <®_can1_stby>; + }; + + can@1 { + compatible = "microchip,mcp25625"; + reg = <1>; + clocks = <&mcp_clock>; + interrupt-parent = <&gpio3>; + interrupts = <6 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&pinctrl_can2>; + pinctrl-names = "default"; + spi-max-frequency = <10000000>; + vdd-supply = <®_vdd_3v3>; + xceiver-supply = <®_can2_stby>; + }; +}; + +&ecspi3 { + cs-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>, + <&gpio1 2 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_ecspi3>; + pinctrl-names = "default"; + status = "okay"; + + connector@0 { + compatible = "gocontroll,moduline-module-slot"; + reg = <0>; + i2c-bus = <&i2c2>; + interrupt-parent = <&gpio1>; + interrupts = <10 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; + slot-number = <1>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_vdd_3v3>; + }; + + connector@1 { + compatible = "gocontroll,moduline-module-slot"; + reg = <1>; + i2c-bus = <&i2c2>; + interrupt-parent = <&gpio1>; + interrupts = <5 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio5 21 GPIO_ACTIVE_LOW>; + slot-number = <2>; + spi-max-frequency = <54000000>; + sync-gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + vddhpp-supply = <®_6v4>; + vddp-supply = <®_5v0>; + vdd-supply = <®_vdd_3v3>; + }; +}; + +&gpu_2d { + status = "disabled"; +}; + +&gpu_3d { + status = "disabled"; +}; + +&i2c2 { + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_i2c2>; + pinctrl-1 = <&pinctrl_i2c2_gpio>; + pinctrl-names = "default", "gpio"; + scl-gpios = <&gpio5 16 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio5 17 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; +}; + +&i2c3 { + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_i2c3>; + pinctrl-1 = <&pinctrl_i2c3_gpio>; + pinctrl-names = "default", "gpio"; + scl-gpios = <&gpio5 18 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio5 19 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; + + lp5012@14 { + compatible = "ti,lp5012"; + reg = <0x14>; + vled-supply = <®_6v4>; + #address-cells = <1>; + #size-cells = <0>; + + multi-led@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + color = ; + label = "case-led1"; + + led@0 { + reg = <0>; + color = ; + }; + + led@1 { + reg = <1>; + color = ; + }; + + led@2 { + reg = <2>; + color = ; + }; + }; + + multi-led@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + color = ; + label = "case-led2"; + + led@0 { + reg = <0>; + color = ; + }; + + led@1 { + reg = <1>; + color = ; + }; + + led@2 { + reg = <2>; + color = ; + }; + }; + + multi-led@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + color = ; + label = "case-led3"; + + led@0 { + reg = <0>; + color = ; + }; + + led@1 { + reg = <1>; + color = ; + }; + + led@2 { + reg = <2>; + color = ; + }; + }; + + multi-led@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + color = ; + label = "case-led4"; + + led@0 { + reg = <0>; + color = ; + }; + + led@1 { + reg = <1>; + color = ; + }; + + led@2 { + reg = <2>; + color = ; + }; + }; + }; + + accelerometer@18 { + compatible = "st,lis2dw12"; + reg = <0x18>; + interrupt-parent = <&gpio5>; + interrupts = <3 IRQ_TYPE_EDGE_RISING>, <5 IRQ_TYPE_EDGE_RISING>; + pinctrl-0 = <&pinctrl_lis_int>; + pinctrl-names = "default"; + vddio-supply = <®_vdd_3v3>; + vdd-supply = <®_vdd_3v3>; + }; + + humidity-sensor@5f { + compatible = "st,hts221"; + reg = <0x5f>; + interrupt-parent = <&gpio3>; + interrupts = <10 IRQ_TYPE_EDGE_RISING>; + pinctrl-0 = <&pinctrl_hts_int>; + pinctrl-names = "default"; + vdd-supply = <®_vdd_3v3>; + }; +}; + +&iomuxc { + pinctrl_bt: btgrp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO01_GPIO1_IO1 + MX8MM_DSE_X1 + MX8MM_IOMUXC_SAI5_MCLK_GPIO3_IO25 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE | MX8MM_HYS_SCHMITT) + MX8MM_IOMUXC_ECSPI2_SS0_GPIO5_IO13 + MX8MM_DSE_X1 + >; + }; + + pinctrl_can1: can1grp { + fsl,pins = < + MX8MM_IOMUXC_SAI2_TXC_GPIO4_IO25 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI5_RXD1_GPIO3_IO22 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE | MX8MM_HYS_SCHMITT) + >; + }; + + pinctrl_can1_reg: can1reggrp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 + (MX8MM_DSE_X2 | MX8MM_FSEL_FAST | MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_can2: can2grp { + fsl,pins = < + MX8MM_IOMUXC_NAND_CLE_GPIO3_IO5 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_NAND_DATA00_GPIO3_IO6 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE | MX8MM_HYS_SCHMITT) + >; + }; + + pinctrl_can2_reg: can2reggrp { + fsl,pins = < + MX8MM_IOMUXC_NAND_RE_B_GPIO3_IO15 + (MX8MM_DSE_X2 | MX8MM_FSEL_FAST | MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_ecspi1: ecspi1grp { + fsl,pins = < + MX8MM_IOMUXC_ECSPI1_MOSI_ECSPI1_MOSI + MX8MM_DSE_X4 + MX8MM_IOMUXC_ECSPI1_MISO_ECSPI1_MISO + (MX8MM_DSE_X4 | MX8MM_HYS_SCHMITT) + MX8MM_IOMUXC_ECSPI1_SCLK_ECSPI1_SCLK + MX8MM_DSE_X4 + MX8MM_IOMUXC_SAI2_MCLK_GPIO4_IO27 + MX8MM_DSE_X1 + MX8MM_IOMUXC_SAI5_RXD2_GPIO3_IO23 + MX8MM_DSE_X1 + MX8MM_IOMUXC_NAND_CE0_B_GPIO3_IO1 + MX8MM_DSE_X1 + >; + }; + + pinctrl_ecspi2: ecspi2grp { + fsl,pins = < + MX8MM_IOMUXC_ECSPI2_MOSI_ECSPI2_MOSI + MX8MM_DSE_X4 + MX8MM_IOMUXC_ECSPI2_MISO_ECSPI2_MISO + (MX8MM_DSE_X4 | MX8MM_HYS_SCHMITT) + MX8MM_IOMUXC_ECSPI2_SCLK_ECSPI2_SCLK + MX8MM_DSE_X4 + MX8MM_IOMUXC_SAI5_RXD3_GPIO3_IO24 + MX8MM_DSE_X1 + MX8MM_IOMUXC_NAND_DATA03_GPIO3_IO9 + MX8MM_DSE_X1 + >; + }; + + pinctrl_ecspi3: ecspi3grp { + fsl,pins = < + MX8MM_IOMUXC_UART1_TXD_ECSPI3_MOSI + MX8MM_DSE_X4 + MX8MM_IOMUXC_UART2_RXD_ECSPI3_MISO + (MX8MM_DSE_X4 | MX8MM_HYS_SCHMITT) + MX8MM_IOMUXC_UART1_RXD_ECSPI3_SCLK + MX8MM_DSE_X4 + MX8MM_IOMUXC_GPIO1_IO09_GPIO1_IO9 + MX8MM_DSE_X1 + MX8MM_IOMUXC_GPIO1_IO02_GPIO1_IO2 + MX8MM_DSE_X1 + >; + }; + + pinctrl_hts_int: htsintgrp { + fsl,pins = < + MX8MM_IOMUXC_NAND_DATA04_GPIO3_IO10 + (MX8MM_PULL_ENABLE | MX8MM_HYS_SCHMITT) + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX8MM_IOMUXC_I2C2_SCL_I2C2_SCL + MX8MM_I2C_DEFAULT + MX8MM_IOMUXC_I2C2_SDA_I2C2_SDA + MX8MM_I2C_DEFAULT + >; + }; + + pinctrl_i2c2_gpio: i2c2-gpiogrp { + fsl,pins = < + MX8MM_IOMUXC_I2C2_SCL_GPIO5_IO16 + MX8MM_I2C_DEFAULT + MX8MM_IOMUXC_I2C2_SDA_GPIO5_IO17 + MX8MM_I2C_DEFAULT + >; + }; + + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX8MM_IOMUXC_I2C3_SCL_I2C3_SCL + MX8MM_I2C_DEFAULT + MX8MM_IOMUXC_I2C3_SDA_I2C3_SDA + MX8MM_I2C_DEFAULT + >; + }; + + pinctrl_i2c3_gpio: i2c3-gpiogrp { + fsl,pins = < + MX8MM_IOMUXC_I2C3_SCL_GPIO5_IO18 + MX8MM_I2C_DEFAULT + MX8MM_IOMUXC_I2C3_SDA_GPIO5_IO19 + MX8MM_I2C_DEFAULT + >; + }; + + pinctrl_lis_int: lisintgrp { + fsl,pins = < + MX8MM_IOMUXC_SPDIF_TX_GPIO5_IO3 + (MX8MM_PULL_ENABLE | MX8MM_HYS_SCHMITT) + MX8MM_IOMUXC_SPDIF_EXT_CLK_GPIO5_IO5 + (MX8MM_PULL_ENABLE | MX8MM_HYS_SCHMITT) + >; + }; + + pinctrl_reg_comm: reg_commgrp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO11_GPIO1_IO11 + MX8MM_DSE_X1 + >; + }; + + pinctrl_sysfs_gpios: sysfsgpiogrp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO07_GPIO1_IO7 + MX8MM_DSE_X1 + MX8MM_IOMUXC_I2C4_SDA_GPIO5_IO21 + MX8MM_DSE_X1 + MX8MM_IOMUXC_SAI3_RXFS_GPIO4_IO28 + MX8MM_DSE_X1 + MX8MM_IOMUXC_SAI5_RXD0_GPIO3_IO21 + MX8MM_DSE_X1 + MX8MM_IOMUXC_SD2_WP_GPIO2_IO20 + MX8MM_DSE_X1 + MX8MM_IOMUXC_GPIO1_IO00_GPIO1_IO0 + MX8MM_DSE_X1 + MX8MM_IOMUXC_GPIO1_IO04_GPIO1_IO4 + MX8MM_DSE_X1 + MX8MM_IOMUXC_GPIO1_IO06_GPIO1_IO6 + MX8MM_DSE_X1 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX8MM_IOMUXC_SAI2_RXC_UART1_DCE_RX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI2_RXFS_UART1_DCE_TX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI2_RXD0_UART1_DCE_RTS_B + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI2_TXFS_UART1_DCE_CTS_B + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_uart2: uart2grp { + fsl,pins = < + MX8MM_IOMUXC_SAI3_TXFS_UART2_DCE_RX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI3_TXC_UART2_DCE_TX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI3_RXD_UART2_DCE_RTS_B + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SAI3_RXC_UART2_DCE_CTS_B + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_uart3: uart3grp { + fsl,pins = < + MX8MM_IOMUXC_UART3_RXD_UART3_DCE_RX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_UART3_TXD_UART3_DCE_TX + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_usdhc2: pinctrlusdhc2grp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK + (MX8MM_DSE_X2 | MX8MM_FSEL_FAST | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + >; + }; + + pinctrl_wl_int: wlintgrp { + fsl,pins = < + MX8MM_IOMUXC_SAI5_RXC_GPIO3_IO20 + (MX8MM_PULL_UP | MX8MM_HYS_SCHMITT | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_wl_reg: wlreggrp { + fsl,pins = < + MX8MM_IOMUXC_UART4_RXD_GPIO5_IO28 + MX8MM_DSE_X1 + >; + }; +}; + +&uart1 { + pinctrl-0 = <&pinctrl_uart1>; + pinctrl-names = "default"; + uart-has-rtscts; + status = "okay"; + + bluetooth { + compatible = "infineon,cyw43439-bt", "brcm,bcm4329-bt"; + device-wakeup-gpios = <&gpio5 13 GPIO_ACTIVE_HIGH>; + interrupt-names = "host-wakeup"; + interrupt-parent = <&gpio3>; + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; + max-speed = <921600>; + pinctrl-0 = <&pinctrl_bt>; + pinctrl-names = "default"; + shutdown-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>; + vbat-supply = <®_3v3_comm>; + vddio-supply = <®_3v3_comm>; + }; +}; + +&uart2 { + pinctrl-0 = <&pinctrl_uart2>; + pinctrl-names = "default"; + uart-has-rtscts; + status = "okay"; +}; + +&uart3 { + pinctrl-0 = <&pinctrl_uart3>; + pinctrl-names = "default"; + status = "okay"; +}; + +&usbotg1 { + disable-over-current; + dr_mode = "peripheral"; + status = "okay"; +}; + +&usbotg2 { + disable-over-current; + dr_mode = "host"; + vbus-supply = <®_5v0>; + status = "okay"; +}; + +&usdhc2 { + #address-cells = <1>; + #size-cells = <0>; + cap-power-off-card; + keep-power-in-suspend; + max-frequency = <50000000>; + mmc-pwrseq = <&wifi_pwrseq>; + non-removable; + pinctrl-0 = <&pinctrl_usdhc2>; + pinctrl-names = "default"; + sd-uhs-sdr25; + vmmc-supply = <®_3v3_comm>; + status = "okay"; + + wifi@1 { + compatible = "infineon,cyw43439-fmac", "brcm,bcm4329-fmac"; + reg = <1>; + pinctrl-0 = <&pinctrl_wl_int>; + pinctrl-names = "default"; + interrupt-names = "host-wake"; + interrupt-parent = <&gpio3>; + interrupts = <20 IRQ_TYPE_LEVEL_LOW>; + brcm,board-type = "GOcontroll,moduline"; + }; +}; + +&vpu_blk_ctrl { + status = "disabled"; +}; + +&vpu_g1 { + status = "disabled"; +}; + +&vpu_g2 { + status = "disabled"; +}; + +&wdog1 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-tx8m-1610.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-tx8m-1610.dtsi new file mode 100644 index 000000000000..ba00f7063476 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-tx8m-1610.dtsi @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2021 Lothar Waßmann + * 2025 Maud Spierings + */ + +#include "imx8mm.dtsi" + +/ { + model = "Ka-Ro Electronics TX8M-1610"; + compatible = "karo,tx8m-1610", "fsl,imx8mm"; + + reg_3v3_etn: regulator-3v3-etn { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&pinctrl_reg_3v3_etn>; + pinctrl-names = "default"; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "3v3-etn"; + }; +}; + +&A53_0 { + cpu-supply = <®_vdd_arm>; +}; + +&A53_1 { + cpu-supply = <®_vdd_arm>; +}; + +&A53_2 { + cpu-supply = <®_vdd_arm>; +}; + +&A53_3 { + cpu-supply = <®_vdd_arm>; +}; + +&ddrc { + operating-points-v2 = <&ddrc_opp_table>; + + ddrc_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-400000000 { + opp-hz = /bits/ 64 <400000000>; + }; + }; +}; + +&fec1 { + assigned-clocks = <&clk IMX8MM_CLK_ENET_AXI>, + <&clk IMX8MM_CLK_ENET_TIMER>, + <&clk IMX8MM_CLK_ENET_REF>, + <&clk IMX8MM_CLK_ENET_REF>; + assigned-clock-parents = <&clk IMX8MM_SYS_PLL1_266M>, + <&clk IMX8MM_SYS_PLL2_100M>, + <&clk IMX8MM_SYS_PLL2_50M>, + <&clk IMX8MM_SYS_PLL2_50M>; + assigned-clock-rates = <0>, <100000000>, <50000000>, <50000000>; + clocks = <&clk IMX8MM_CLK_ENET1_ROOT>, + <&clk IMX8MM_CLK_ENET1_ROOT>, + <&clk IMX8MM_CLK_ENET_TIMER>, + <&clk IMX8MM_CLK_ENET_REF>; + phy-handle = <ðphy0>; + phy-mode = "rmii"; + phy-reset-duration = <25>; + phy-reset-gpios = <&gpio1 29 GPIO_ACTIVE_LOW>; + phy-reset-post-delay = <1>; + phy-supply = <®_3v3_etn>; + pinctrl-0 = <&pinctrl_fec1>, <&pinctrl_ethphy_rst>; + pinctrl-names = "default"; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + ethphy0: ethernet-phy@0 { + reg = <0>; + clocks = <&clk IMX8MM_CLK_ENET_REF>; + interrupt-parent = <&gpio1>; + interrupts = <28 IRQ_TYPE_EDGE_FALLING>; + pinctrl-0 = <&pinctrl_ethphy_int>; + pinctrl-names = "default"; + smsc,disable-energy-detect; + }; + }; +}; + +&gpio1 { + gpio-line-names = "SODIMM_152", "SODIMM_42", "SODIMM_153", "PMIC_IRQ_B", + "SODIMM_154", "SODIMM_155", "SODIMM_156", "SODIMM_157", + "SODIMM_158", "SODIMM_159", "SODIMM_161", "SODIMM_162", + "SODIMM_34", "SODIMM_36", "SODIMM_27", "SODIMM_28", + "", "", "", "", + "", "", "", "ENET_POWER", + "", "", "", "", + "ENET_nINT", "ENET_nRST", "", ""; +}; + +&gpio2 { + gpio-line-names = "", "", "", "", + "", "", "", "", + "", "", "", "", + "SODIMM_51", "SODIMM_57", "SODIMM_56", "SODIMM_52", + "SODIMM_53", "SODIMM_54", "SODIMM_55", "SODIMM_15", + "SODIMM_45", "", "", "", + "", "", "", "", + "", "", "", ""; +}; + +&gpio3 { + gpio-line-names = "SODIMM_103", "SODIMM_104", "SODIMM_105", "SODIMM_106", + "SODIMM_107", "SODIMM_112", "SODIMM_108", "SODIMM_109", + "SODIMM_95", "SODIMM_110", "SODIMM_96", "SODIMM_97", + "SODIMM_98", "SODIMM_99", "SODIMM_113", "SODIMM_114", + "SODIMM_115", "SODIMM_101", "SODIMM_100", "SODIMM_77", + "SODIMM_72", "SODIMM_73", "SODIMM_74", "SODIMM_75", + "SODIMM_76", "SODIMM_43", "", "", + "", "", "", ""; +}; + +&gpio4 { + gpio-line-names = "SODIMM_178", "SODIMM_180", "SODIMM_184", "SODIMM_185", + "SODIMM_186", "SODIMM_187", "SODIMM_188", "SODIMM_189", + "SODIMM_190", "SODIMM_191", "SODIMM_179", "SODIMM_181", + "SODIMM_192", "SODIMM_193", "SODIMM_194", "SODIMM_195", + "SODIMM_196", "SODIMM_197", "SODIMM_198", "SODIMM_199", + "SODIMM_182", "SODIMM_79", "SODIMM_78", "SODIMM_84", + "SODIMM_87", "SODIMM_86", "SODIMM_85", "SODIMM_83", + "SODIMM_81", "SODIMM_80", "SODIMM_90", "SODIMM_93"; +}; + +&gpio5 { + gpio-line-names = "SODIMM_92", "SODIMM_91", "SODIMM_89", "SODIMM_144", + "SODIMM_143", "SODIMM_146", "SODIMM_68", "SODIMM_67", + "SODIMM_70", "SODIMM_69", "SODIMM_48", "SODIMM_46", + "SODIMM_47", "SODIMM_44", "PMIC_SCL", "PMIC_SDA", + "SODIMM_41", "SODIMM_40", "SODIMM_148", "SODIMM_149", + "SODIMM_150", "SODIMM_151", "SODIMM_60", "SODIMM_59", + "SODIMM_64", "SODIMM_63", "SODIMM_62", "SODIMM_61", + "SODIMM_66", "SODIMM_65", "", ""; +}; + +&i2c1 { + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_i2c1>; + pinctrl-1 = <&pinctrl_i2c1_gpio>; + pinctrl-names = "default", "gpio"; + scl-gpios = <&gpio5 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio5 15 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; + + pmic: pmic@4b { + compatible = "rohm,bd71847"; + reg = <0x4b>; + interrupt-parent = <&gpio1>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&pinctrl_pmic>; + pinctrl-names = "default"; + rohm,reset-snvs-powered; + + regulators { + BUCK1 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <900000>; + regulator-min-microvolt = <780000>; + regulator-name = "buck1"; + regulator-ramp-delay = <1250>; + }; + + reg_vdd_arm: BUCK2 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <950000>; + regulator-min-microvolt = <805000>; + regulator-name = "buck2"; + regulator-ramp-delay = <1250>; + rohm,dvs-run-voltage = <950000>; + rohm,dvs-idle-voltage = <810000>; + }; + + BUCK3 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <900000>; + regulator-min-microvolt = <805000>; + regulator-name = "buck3"; + }; + + reg_vdd_3v3: BUCK4 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "buck4"; + }; + + reg_vdd_1v8: BUCK5 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1950000>; + regulator-min-microvolt = <1700000>; + regulator-name = "buck5"; + }; + + BUCK6 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <(1350000 + 100000)>; + regulator-min-microvolt = <(1350000 - 67000)>; + regulator-name = "buck6"; + rohm,fb-pull-up-microvolt = <0>; + rohm,feedback-pull-up-r1-ohms = <2200>; + rohm,feedback-pull-up-r2-ohms = <499>; + }; + + LDO1 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1980000>; + regulator-min-microvolt = <1620000>; + regulator-name = "ldo1"; + }; + + LDO2 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <900000>; + regulator-min-microvolt = <760000>; + regulator-name = "ldo2"; + }; + + LDO3 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1890000>; + regulator-min-microvolt = <1710000>; + regulator-name = "ldo3"; + }; + + LDO4 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1000000>; + regulator-min-microvolt = <855000>; + regulator-name = "ldo4"; + }; + + LDO5 { + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <1800000>; + regulator-name = "ldo5"; + }; + + LDO6 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1260000>; + regulator-min-microvolt = <1140000>; + regulator-name = "ldo6"; + }; + }; + }; +}; + +&iomuxc { + pinctrl_ethphy_int: etnphy-intgrp { + fsl,pins = < + MX8MM_IOMUXC_ENET_RD2_GPIO1_IO28 + (MX8MM_FSEL_FAST | MX8MM_HYS_SCHMITT) + >; + }; + + pinctrl_ethphy_rst: etnphy-rstgrp { + fsl,pins = < + MX8MM_IOMUXC_ENET_RD3_GPIO1_IO29 + (MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_fec1: fec1grp { + fsl,pins = < + MX8MM_IOMUXC_ENET_MDC_ENET1_MDC + (MX8MM_DSE_X4 | MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_ENET_MDIO_ENET1_MDIO + (MX8MM_DSE_X4 | MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_ENET_TD2_ENET1_TX_CLK + (MX8MM_FSEL_FAST | MX8MM_SION) + MX8MM_IOMUXC_ENET_TD0_ENET1_RGMII_TD0 + (MX8MM_DSE_X6 | MX8MM_FSEL_FAST) + MX8MM_IOMUXC_ENET_TD1_ENET1_RGMII_TD1 + (MX8MM_DSE_X6 | MX8MM_FSEL_FAST) + MX8MM_IOMUXC_ENET_RD0_ENET1_RGMII_RD0 + (MX8MM_FSEL_FAST | MX8MM_HYS_SCHMITT) + MX8MM_IOMUXC_ENET_RD1_ENET1_RGMII_RD1 + (MX8MM_FSEL_FAST | MX8MM_HYS_SCHMITT) + MX8MM_IOMUXC_ENET_RXC_ENET1_RX_ER + MX8MM_FSEL_FAST + MX8MM_IOMUXC_ENET_RX_CTL_ENET1_RGMII_RX_CTL + MX8MM_FSEL_FAST + MX8MM_IOMUXC_ENET_TX_CTL_ENET1_RGMII_TX_CTL + (MX8MM_DSE_X6 | MX8MM_FSEL_FAST) + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX8MM_IOMUXC_I2C1_SCL_I2C1_SCL + MX8MM_I2C_DEFAULT + MX8MM_IOMUXC_I2C1_SDA_I2C1_SDA + MX8MM_I2C_DEFAULT + >; + }; + + pinctrl_i2c1_gpio: i2c1-gpiogrp { + fsl,pins = < + MX8MM_IOMUXC_I2C1_SCL_GPIO5_IO14 + MX8MM_I2C_DEFAULT + MX8MM_IOMUXC_I2C1_SDA_GPIO5_IO15 + MX8MM_I2C_DEFAULT + >; + }; + + pinctrl_pmic: pmicgrp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO03_GPIO1_IO3 + (MX8MM_PULL_UP | MX8MM_HYS_SCHMITT | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_reg_3v3_etn: reg-3v3-etngrp { + fsl,pins = < + MX8MM_IOMUXC_ENET_TXC_GPIO1_IO23 + (MX8MM_DSE_X4 | MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK + (MX8MM_FSEL_FAST | MX8MM_HYS_SCHMITT | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD + MX8MM_USDHC_DATA_DEFAULT + MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 + MX8MM_USDHC_DATA_DEFAULT + MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 + MX8MM_USDHC_DATA_DEFAULT + MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 + MX8MM_USDHC_DATA_DEFAULT + MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 + MX8MM_USDHC_DATA_DEFAULT + MX8MM_IOMUXC_SD1_DATA4_USDHC1_DATA4 + MX8MM_USDHC_DATA_DEFAULT + MX8MM_IOMUXC_SD1_DATA5_USDHC1_DATA5 + MX8MM_USDHC_DATA_DEFAULT + MX8MM_IOMUXC_SD1_DATA6_USDHC1_DATA6 + MX8MM_USDHC_DATA_DEFAULT + MX8MM_IOMUXC_SD1_DATA7_USDHC1_DATA7 + MX8MM_USDHC_DATA_DEFAULT + MX8MM_IOMUXC_SD1_STROBE_USDHC1_STROBE + (MX8MM_FSEL_FAST | MX8MM_HYS_SCHMITT | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SD1_RESET_B_USDHC1_RESET_B + (MX8MM_DSE_X6 | MX8MM_FSEL_FAST | MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { + fsl,pins = < + MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK + (MX8MM_DSE_X2 | MX8MM_FSEL_FAST | MX8MM_HYS_SCHMITT | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA4_USDHC1_DATA4 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA5_USDHC1_DATA5 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA6_USDHC1_DATA6 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA7_USDHC1_DATA7 + (MX8MM_DSE_X2 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_STROBE_USDHC1_STROBE + (MX8MM_DSE_X2 | MX8MM_FSEL_FAST | MX8MM_HYS_SCHMITT | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SD1_RESET_B_USDHC1_RESET_B + (MX8MM_DSE_X6 | MX8MM_FSEL_FAST | MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { + fsl,pins = < + MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK + (MX8MM_DSE_X6 | MX8MM_FSEL_FAST | MX8MM_HYS_SCHMITT | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD + (MX8MM_DSE_X6 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 + (MX8MM_DSE_X6 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 + (MX8MM_DSE_X6 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 + (MX8MM_DSE_X6 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 + (MX8MM_DSE_X6 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA4_USDHC1_DATA4 + (MX8MM_DSE_X6 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA5_USDHC1_DATA5 + (MX8MM_DSE_X6 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA6_USDHC1_DATA6 + (MX8MM_DSE_X6 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_DATA7_USDHC1_DATA7 + (MX8MM_DSE_X6 | MX8MM_USDHC_DATA_DEFAULT) + MX8MM_IOMUXC_SD1_STROBE_USDHC1_STROBE + (MX8MM_DSE_X6 | MX8MM_FSEL_FAST | MX8MM_HYS_SCHMITT | MX8MM_PULL_ENABLE) + MX8MM_IOMUXC_SD1_RESET_B_USDHC1_RESET_B + (MX8MM_DSE_X6 | MX8MM_FSEL_FAST | MX8MM_PULL_UP | MX8MM_PULL_ENABLE) + >; + }; +}; + +&usdhc1 { + assigned-clocks = <&clk IMX8MM_CLK_USDHC1>; + assigned-clock-rates = <400000000>; + bus-width = <8>; + non-removable; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + vmmc-supply = <®_vdd_3v3>; + vqmmc-supply = <®_vdd_1v8>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-var-som-symphony-legacy.dts b/arch/arm64/boot/dts/freescale/imx8mm-var-som-symphony-legacy.dts new file mode 100644 index 000000000000..faa707402de9 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-var-som-symphony-legacy.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2026 Variscite Ltd. + */ + +#include "imx8mm-var-som-symphony.dts" +#include "imx8mm-var-som-wifi-brcm-legacy.dtsi" + +&bluetooth_iw61x { + status = "disabled"; +}; + +&iw61x_pwrseq { + status = "disabled"; +}; + +&usdhc1 { + /delete-property/ mmc-pwrseq; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-var-som-symphony.dts b/arch/arm64/boot/dts/freescale/imx8mm-var-som-symphony.dts index affbc67c2ef6..857325ef4461 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-var-som-symphony.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-var-som-symphony.dts @@ -5,12 +5,25 @@ /dts-v1/; +#include +#include #include "imx8mm-var-som.dtsi" +#include "imx8mm-var-som-wifi-bt-iw61x.dtsi" / { model = "Variscite VAR-SOM-MX8MM Symphony evaluation board"; compatible = "variscite,var-som-mx8mm-symphony", "variscite,var-som-mx8mm", "fsl,imx8mm"; + chosen { + stdout-path = &uart4; + }; + + pcie0_refclk: pcie0-refclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <100000000>; + }; + reg_usdhc2_vmmc: regulator-usdhc2-vmmc { compatible = "regulator-fixed"; pinctrl-names = "default"; @@ -18,7 +31,8 @@ regulator-name = "VSD_3V3"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; - gpio = <&gpio2 19 GPIO_ACTIVE_HIGH>; + gpio = <&gpio4 22 GPIO_ACTIVE_HIGH>; + off-on-delay-us = <20000>; enable-active-high; }; @@ -26,6 +40,7 @@ compatible = "regulator-fixed"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_reg_usb_otg2_vbus>; + pinctrl-1 = <&pinctrl_reg_usb_otg2_vbus_sleep>; regulator-name = "usb_otg2_vbus"; regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; @@ -67,7 +82,24 @@ }; ðphy { - reset-gpios = <&pca9534 5 GPIO_ACTIVE_HIGH>; + leds { + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + color = ; + function = LED_FUNCTION_LAN; + linux,default-trigger = "netdev"; + }; + + led@1 { + reg = <1>; + color = ; + function = LED_FUNCTION_LAN; + linux,default-trigger = "netdev"; + }; + }; }; &i2c2 { @@ -110,17 +142,38 @@ }; }; - extcon_usbotg1: typec@3d { + /* USB Type-C Controller */ + ptn5150: typec@3d { compatible = "nxp,ptn5150"; reg = <0x3d>; interrupt-parent = <&gpio1>; interrupts = <11 IRQ_TYPE_LEVEL_LOW>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ptn5150>; + + port { + typec1_dr_sw: endpoint { + remote-endpoint = <&usb1_drd_sw>; + }; + }; }; }; &i2c3 { + pca6408: gpio@21 { + compatible = "nxp,pcal6408"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + }; + + st33ktpm2xi2c: tpm@2e { + compatible = "st,st33ktpm2xi2c", "tcg,tpm-tis-i2c"; + reg = <0x2e>; + label = "tpm"; + reset-gpios = <&pca6408 4 GPIO_ACTIVE_LOW>; + }; + /* Capacitive touch controller */ ft5x06_ts: touchscreen@38 { compatible = "edt,edt-ft5406"; @@ -142,6 +195,39 @@ }; }; +&i2c4 { + clock-frequency = <100000>; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&pinctrl_i2c4>; + pinctrl-1 = <&pinctrl_i2c4_gpio>; + scl-gpios = <&gpio5 20 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio5 21 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; +}; + +&pcie_phy { + clocks = <&pcie0_refclk>; + clock-names = "ref"; + fsl,refclk-pad-mode = ; + fsl,tx-deemph-gen1 = <0x2d>; + fsl,tx-deemph-gen2 = <0xf>; + fsl,clkreq-unsupported; + status = "okay"; +}; + +&pcie0 { + reset-gpio = <&pca6408 1 GPIO_ACTIVE_LOW>; + clocks = <&clk IMX8MM_CLK_PCIE1_ROOT>, <&pcie0_refclk>, + <&clk IMX8MM_CLK_PCIE1_AUX>; + clock-names = "pcie", "pcie_bus", "pcie_aux"; + assigned-clocks = <&clk IMX8MM_CLK_PCIE1_AUX>, + <&clk IMX8MM_CLK_PCIE1_CTRL>; + assigned-clock-rates = <10000000>, <250000000>; + assigned-clock-parents = <&clk IMX8MM_SYS_PLL2_50M>, + <&clk IMX8MM_SYS_PLL2_250M>; + status = "okay"; +}; + /* Header */ &uart1 { pinctrl-names = "default"; @@ -156,28 +242,49 @@ status = "okay"; }; +/* Console */ +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; + status = "okay"; +}; + &usbotg1 { - disable-over-current; - extcon = <&extcon_usbotg1>, <&extcon_usbotg1>; + dr_mode = "otg"; + hnp-disable; + srp-disable; + adp-disable; + usb-role-switch; + samsung,picophy-pre-emp-curr-control = <3>; + samsung,picophy-dc-vol-level-adjust = <7>; + status = "okay"; + + port { + usb1_drd_sw: endpoint { + remote-endpoint = <&typec1_dr_sw>; + }; + }; }; &usbotg2 { dr_mode = "host"; vbus-supply = <®_usb_otg2_vbus>; - srp-disable; - hnp-disable; - adp-disable; + samsung,picophy-pre-emp-curr-control = <3>; + samsung,picophy-dc-vol-level-adjust = <7>; disable-over-current; - /delete-property/ usb-role-switch; - /* - * FIXME: having USB2 enabled hangs the boot just after: - * [ 1.943365] ci_hdrc ci_hdrc.1: EHCI Host Controller - * [ 1.948287] ci_hdrc ci_hdrc.1: new USB bus registered, assigned bus number 1 - * [ 1.971006] ci_hdrc ci_hdrc.1: USB 2.0 started, EHCI 1.00 - * [ 1.977203] hub 1-0:1.0: USB hub found - * [ 1.980987] hub 1-0:1.0: 1 port detected - */ - status = "disabled"; + status = "okay"; +}; + +/* SD */ +&usdhc2 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>; + pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>; + cd-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; + vmmc-supply = <®_usdhc2_vmmc>; + bus-width = <4>; + status = "okay"; }; &pinctrl_fec1 { @@ -214,6 +321,20 @@ >; }; + pinctrl_i2c4: i2c4grp { + fsl,pins = < + MX8MM_IOMUXC_I2C4_SCL_I2C4_SCL 0x400001c3 + MX8MM_IOMUXC_I2C4_SDA_I2C4_SDA 0x400001c3 + >; + }; + + pinctrl_i2c4_gpio: i2c4gpiogrp { + fsl,pins = < + MX8MM_IOMUXC_I2C4_SCL_GPIO5_IO20 0x1c3 + MX8MM_IOMUXC_I2C4_SDA_GPIO5_IO21 0x1c3 + >; + }; + pinctrl_pca9534: pca9534grp { fsl,pins = < MX8MM_IOMUXC_GPIO1_IO07_GPIO1_IO7 0x16 @@ -232,9 +353,15 @@ >; }; + pinctrl_reg_usb_otg2_vbus_sleep: regusbotg2vbus-sleepgrp { + fsl,pins = < + MX8MM_IOMUXC_SAI3_TXD_GPIO5_IO1 0x120 + >; + }; + pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { fsl,pins = < - MX8MM_IOMUXC_SD2_RESET_B_GPIO2_IO19 0x41 + MX8MM_IOMUXC_SAI2_RXC_GPIO4_IO22 0x41 >; }; @@ -251,4 +378,53 @@ MX8MM_IOMUXC_UART3_TXD_UART3_DCE_TX 0x140 >; }; + + pinctrl_uart4: uart4grp { + fsl,pins = < + MX8MM_IOMUXC_UART4_RXD_UART4_DCE_RX 0x140 + MX8MM_IOMUXC_UART4_TXD_UART4_DCE_TX 0x140 + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x190 + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d0 + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d0 + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d0 + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0 + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 + >; + }; + + pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x194 + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d4 + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d4 + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d4 + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4 + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 + >; + }; + + pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { + fsl,pins = < + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x196 + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d6 + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d6 + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d6 + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6 + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 + >; + }; + + pinctrl_usdhc2_gpio: usdhc2gpiogrp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO10_GPIO1_IO10 0xc1 + >; + }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-var-som-wifi-brcm-legacy.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-var-som-wifi-brcm-legacy.dtsi new file mode 100644 index 000000000000..f44a846ea6f9 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-var-som-wifi-brcm-legacy.dtsi @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2026 Variscite Ltd. + */ + +/* WIFI */ +&usdhc1 { + brcmf: wifi@1 { + reg = <1>; + compatible = "brcm,bcm4329-fmac"; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-var-som-wifi-bt-iw61x.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-var-som-wifi-bt-iw61x.dtsi new file mode 100644 index 000000000000..15990d141d2a --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-var-som-wifi-bt-iw61x.dtsi @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2026 Variscite Ltd. + */ + +/ { + iw61x_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + post-power-on-delay-ms = <100>; + power-off-delay-us = <10000>; + reset-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>, /* WIFI_RESET */ + <&gpio2 20 GPIO_ACTIVE_LOW>; /* WIFI_PWR_EN */ + }; +}; + +&uart2 { + pinctrl-0 = <&pinctrl_uart2>, <&pinctrl_bt>; + + bluetooth_iw61x: bluetooth { + compatible = "nxp,88w8987-bt"; + }; +}; + +/* WIFI */ +&usdhc1 { + pinctrl-0 = <&pinctrl_usdhc1>, <&pinctrl_wifi>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>, <&pinctrl_wifi>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>, <&pinctrl_wifi>; + mmc-pwrseq = <&iw61x_pwrseq>; +}; + +&iomuxc { + pinctrl_bt: bluetoothgrp { + fsl,pins = < + MX8MM_IOMUXC_SD1_DATA4_GPIO2_IO6 0xc1 + >; + }; + + pinctrl_wifi: wifigrp { + fsl,pins = < + MX8MM_IOMUXC_SD2_WP_GPIO2_IO20 0x140 + MX8MM_IOMUXC_SD1_RESET_B_GPIO2_IO10 0xc1 + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-var-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-var-som.dtsi index 190bde4edcd7..d05b1ab17fed 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-var-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-var-som.dtsi @@ -9,15 +9,26 @@ / { model = "Variscite VAR-SOM-MX8MM module"; - chosen { - stdout-path = &uart4; - }; - memory@40000000 { device_type = "memory"; reg = <0x0 0x40000000 0 0x80000000>; }; + clk40m: oscillator { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <40000000>; + clock-output-names = "can_osc"; + }; + + reg_audio_supply: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "wm8904-supply"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + reg_eth_phy: regulator-eth-phy { compatible = "regulator-fixed"; pinctrl-names = "default"; @@ -28,6 +39,41 @@ gpio = <&gpio2 9 GPIO_ACTIVE_HIGH>; enable-active-high; }; + + reg_phy_vddio: regulator-phy-vddio { + compatible = "regulator-fixed"; + regulator-name = "vddio-1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,bitclock-master = <&codec_dai>; + simple-audio-card,format = "i2s"; + simple-audio-card,frame-master = <&codec_dai>; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "wm8904-audio"; + simple-audio-card,routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "IN2L", "Line In Jack", + "IN2R", "Line In Jack", + "IN1L", "Microphone Jack", + "IN1R", "Microphone Jack"; + simple-audio-card,widgets = + "Microphone", "Microphone Jack", + "Headphone", "Headphone Jack", + "Line", "Line In Jack"; + + codec_dai: simple-audio-card,codec { + sound-dai = <&wm8904>; + }; + + simple-audio-card,cpu { + sound-dai = <&sai5>; + }; + }; }; &A53_0 { @@ -100,15 +146,33 @@ ti,keep-vref-on; wakeup-source; }; + + /* CAN controller */ + can0: can@1 { + compatible = "microchip,mcp251xfd"; + reg = <1>; + clocks = <&clk40m>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can>; + interrupt-parent = <&gpio1>; + interrupts = <5 IRQ_TYPE_LEVEL_LOW>; + spi-max-frequency = <20000000>; + microchip,rx-int-gpios = <&gpio2 12 GPIO_ACTIVE_LOW>; + }; }; &fec1 { - pinctrl-names = "default"; + pinctrl-names = "default", "sleep"; pinctrl-0 = <&pinctrl_fec1>; + pinctrl-1 = <&pinctrl_fec1_sleep>; + /* + * The required RGMII TX and RX 2ns delays are implemented directly + * in hardware via passive delay elements on the SOM PCB. + * No delay configuration is needed in software via PHY driver. + */ phy-mode = "rgmii"; phy-handle = <ðphy>; phy-supply = <®_eth_phy>; - fsl,magic-packet; status = "okay"; mdio { @@ -120,7 +184,8 @@ reg = <4>; reset-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; reset-assert-us = <10000>; - reset-deassert-us = <10000>; + reset-deassert-us = <100000>; + vddio-supply = <®_phy_vddio>; }; }; }; @@ -248,18 +313,57 @@ &i2c3 { clock-frequency = <400000>; - pinctrl-names = "default"; + pinctrl-names = "default", "gpio"; pinctrl-0 = <&pinctrl_i2c3>; + pinctrl-1 = <&pinctrl_i2c3_gpio>; + scl-gpios = <&gpio5 18 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio5 19 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; status = "okay"; - /* TODO: configure audio, as of now just put a placeholder */ wm8904: codec@1a { compatible = "wlf,wm8904"; reg = <0x1a>; - status = "disabled"; + #sound-dai-cells = <0>; + clocks = <&clk IMX8MM_CLK_SAI5_ROOT>; + clock-names = "mclk"; + AVDD-supply = <&ldo5_reg>; + CPVDD-supply = <&ldo5_reg>; + DBVDD-supply = <®_audio_supply>; + DCVDD-supply = <&ldo5_reg>; + MICVDD-supply = <&ldo5_reg>; + wlf,drc-cfg-names = "default", "peaklimiter", "tradition", + "soft", "music"; + /* + * Config registers per name, respectively: + * KNEE_IP = 0, KNEE_OP = 0, HI_COMP = 1, LO_COMP = 1 + * KNEE_IP = -24, KNEE_OP = -6, HI_COMP = 1/4, LO_COMP = 1 + * KNEE_IP = -42, KNEE_OP = -3, HI_COMP = 0, LO_COMP = 1 + * KNEE_IP = -45, KNEE_OP = -9, HI_COMP = 1/8, LO_COMP = 1 + * KNEE_IP = -30, KNEE_OP = -10.5, HI_COMP = 1/4, LO_COMP = 1 + */ + wlf,drc-cfg-regs = /bits/ 16 <0x01af 0x3248 0x0000 0x0000>, + /bits/ 16 <0x04af 0x324b 0x0010 0x0408>, + /bits/ 16 <0x04af 0x324b 0x0028 0x0704>, + /bits/ 16 <0x04af 0x324b 0x0018 0x078c>, + /bits/ 16 <0x04af 0x324b 0x0010 0x050e>; + /* GPIO1 = DMIC_CLK, don't touch others */ + wlf,gpio-cfg = <0x0018>, <0xffff>, <0xffff>, <0xffff>; }; }; +&sai5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sai5>; + assigned-clocks = <&clk IMX8MM_CLK_SAI5>; + assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>; + assigned-clock-rates = <1536000>; + #sound-dai-cells = <0>; + dmas = <&sdma2 8 25 0>, <&sdma2 9 25 0>; + dma-names = "rx", "tx"; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + &snvs_pwrkey { status = "okay"; }; @@ -274,26 +378,6 @@ status = "okay"; }; -/* Console */ -&uart4 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart4>; - status = "okay"; -}; - -&usbotg1 { - dr_mode = "otg"; - usb-role-switch; - status = "okay"; -}; - -&usbotg2 { - dr_mode = "otg"; - usb-role-switch; - status = "okay"; -}; - -/* WIFI */ &usdhc1 { #address-cells = <1>; #size-cells = <0>; @@ -305,11 +389,6 @@ non-removable; keep-power-in-suspend; status = "okay"; - - brcmf: wifi@1 { - reg = <1>; - compatible = "brcm,bcm4329-fmac"; - }; }; /* SD */ @@ -347,6 +426,13 @@ }; &iomuxc { + pinctrl_can: cangrp { + fsl,pins = < + MX8MM_IOMUXC_GPIO1_IO05_GPIO1_IO5 0x16 + MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 0x16 + >; + }; + pinctrl_ecspi1: ecspi1grp { fsl,pins = < MX8MM_IOMUXC_ECSPI1_SCLK_ECSPI1_SCLK 0x13 @@ -377,180 +463,212 @@ >; }; + pinctrl_fec1_sleep: fec1sleepgrp { + fsl,pins = < + MX8MM_IOMUXC_ENET_MDC_GPIO1_IO16 0x120 + MX8MM_IOMUXC_ENET_MDIO_GPIO1_IO17 0x120 + MX8MM_IOMUXC_ENET_TD3_GPIO1_IO18 0x120 + MX8MM_IOMUXC_ENET_TD2_GPIO1_IO19 0x120 + MX8MM_IOMUXC_ENET_TD1_GPIO1_IO20 0x120 + MX8MM_IOMUXC_ENET_TD0_GPIO1_IO21 0x120 + MX8MM_IOMUXC_ENET_RD3_GPIO1_IO29 0x120 + MX8MM_IOMUXC_ENET_RD2_GPIO1_IO28 0x120 + MX8MM_IOMUXC_ENET_RD1_GPIO1_IO27 0x120 + MX8MM_IOMUXC_ENET_RD0_GPIO1_IO26 0x120 + MX8MM_IOMUXC_ENET_TXC_GPIO1_IO23 0x120 + MX8MM_IOMUXC_ENET_RXC_GPIO1_IO25 0x120 + MX8MM_IOMUXC_ENET_RX_CTL_GPIO1_IO24 0x120 + MX8MM_IOMUXC_ENET_TX_CTL_GPIO1_IO22 0x120 + MX8MM_IOMUXC_GPIO1_IO09_GPIO1_IO9 0x100 + >; + }; + pinctrl_i2c1: i2c1grp { fsl,pins = < - MX8MM_IOMUXC_I2C1_SCL_I2C1_SCL 0x400001c3 - MX8MM_IOMUXC_I2C1_SDA_I2C1_SDA 0x400001c3 + MX8MM_IOMUXC_I2C1_SCL_I2C1_SCL 0x400001c3 + MX8MM_IOMUXC_I2C1_SDA_I2C1_SDA 0x400001c3 >; }; pinctrl_i2c3: i2c3grp { fsl,pins = < - MX8MM_IOMUXC_I2C3_SCL_I2C3_SCL 0x400001c3 - MX8MM_IOMUXC_I2C3_SDA_I2C3_SDA 0x400001c3 + MX8MM_IOMUXC_I2C3_SCL_I2C3_SCL 0x400001c3 + MX8MM_IOMUXC_I2C3_SDA_I2C3_SDA 0x400001c3 + >; + }; + + pinctrl_i2c3_gpio: i2c3gpiogrp { + fsl,pins = < + MX8MM_IOMUXC_I2C3_SCL_GPIO5_IO18 0x1c3 + MX8MM_IOMUXC_I2C3_SDA_GPIO5_IO19 0x1c3 >; }; pinctrl_pmic: pmicirqgrp { fsl,pins = < - MX8MM_IOMUXC_SD1_DATA6_GPIO2_IO8 0x141 + MX8MM_IOMUXC_SD1_DATA6_GPIO2_IO8 0x141 >; }; pinctrl_reg_eth_phy: regethphygrp { fsl,pins = < - MX8MM_IOMUXC_SD1_DATA7_GPIO2_IO9 0x41 + MX8MM_IOMUXC_SD1_DATA7_GPIO2_IO9 0x41 >; }; pinctrl_restouch: restouchgrp { fsl,pins = < - MX8MM_IOMUXC_GPIO1_IO03_GPIO1_IO3 0x1c0 + MX8MM_IOMUXC_GPIO1_IO03_GPIO1_IO3 0x1c0 + >; + }; + + pinctrl_sai5: sai5grp { + fsl,pins = < + MX8MM_IOMUXC_SAI5_RXD1_SAI5_TX_SYNC 0xd6 + MX8MM_IOMUXC_SAI5_RXFS_SAI5_RX_SYNC 0xd6 + MX8MM_IOMUXC_SAI5_RXD3_SAI5_TX_DATA0 0xd6 + MX8MM_IOMUXC_SAI5_RXD0_SAI5_RX_DATA0 0xd6 + MX8MM_IOMUXC_SAI5_RXC_SAI5_RX_BCLK 0xd6 + MX8MM_IOMUXC_SAI5_RXD2_SAI5_TX_BCLK 0xd6 + MX8MM_IOMUXC_SAI5_MCLK_SAI5_MCLK 0xd6 >; }; pinctrl_uart2: uart2grp { fsl,pins = < - MX8MM_IOMUXC_SAI3_TXFS_UART2_DCE_RX 0x140 - MX8MM_IOMUXC_SAI3_TXC_UART2_DCE_TX 0x140 - MX8MM_IOMUXC_SAI3_RXC_UART2_DCE_CTS_B 0x140 - MX8MM_IOMUXC_SAI3_RXD_UART2_DCE_RTS_B 0x140 - >; - }; - - pinctrl_uart4: uart4grp { - fsl,pins = < - MX8MM_IOMUXC_UART4_RXD_UART4_DCE_RX 0x140 - MX8MM_IOMUXC_UART4_TXD_UART4_DCE_TX 0x140 + MX8MM_IOMUXC_SAI3_TXFS_UART2_DCE_RX 0x140 + MX8MM_IOMUXC_SAI3_TXC_UART2_DCE_TX 0x140 + MX8MM_IOMUXC_SAI3_RXC_UART2_DCE_CTS_B 0x140 + MX8MM_IOMUXC_SAI3_RXD_UART2_DCE_RTS_B 0x140 >; }; pinctrl_usdhc1: usdhc1grp { fsl,pins = < - MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK 0x190 - MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD 0x1d0 - MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x1d0 - MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x1d0 - MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x1d0 - MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x1d0 + MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK 0x190 + MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD 0x1d0 + MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x1d0 + MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x1d0 + MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x1d0 + MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x1d0 >; }; pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { fsl,pins = < - MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK 0x194 - MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD 0x1d4 - MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x1d4 - MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x1d4 - MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x1d4 - MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x1d4 + MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK 0x194 + MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD 0x1d4 + MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x1d4 + MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x1d4 + MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x1d4 + MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x1d4 >; }; pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { fsl,pins = < - MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK 0x196 - MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD 0x1d6 - MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x1d6 - MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x1d6 - MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x1d6 - MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x1d6 + MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK 0x196 + MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD 0x1d6 + MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x1d6 + MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x1d6 + MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x1d6 + MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x1d6 >; }; pinctrl_usdhc2_gpio: usdhc2gpiogrp { fsl,pins = < - MX8MM_IOMUXC_GPIO1_IO10_GPIO1_IO10 0xc1 + MX8MM_IOMUXC_GPIO1_IO10_GPIO1_IO10 0xc1 >; }; pinctrl_usdhc2: usdhc2grp { fsl,pins = < - MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x190 - MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d0 - MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d0 - MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d0 - MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0 - MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0 - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x190 + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d0 + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d0 + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d0 + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0 + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 >; }; pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { fsl,pins = < - MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x194 - MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d4 - MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d4 - MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d4 - MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4 - MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4 - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x194 + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d4 + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d4 + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d4 + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4 + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 >; }; pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { fsl,pins = < - MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x196 - MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d6 - MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d6 - MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d6 - MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6 - MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6 - MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 + MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x196 + MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d6 + MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d6 + MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d6 + MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6 + MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6 + MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 >; }; pinctrl_usdhc3: usdhc3grp { fsl,pins = < - MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x190 - MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d0 - MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d0 - MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d0 - MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d0 - MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d0 - MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d0 - MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d0 - MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d0 - MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d0 - MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x190 + MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x190 + MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d0 + MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d0 + MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d0 + MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d0 + MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d0 + MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d0 + MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d0 + MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d0 + MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d0 + MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x190 >; }; pinctrl_usdhc3_100mhz: usdhc3-100mhzgrp { fsl,pins = < - MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x194 - MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d4 - MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d4 - MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d4 - MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d4 - MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d4 - MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d4 - MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d4 - MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d4 - MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d4 - MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x194 + MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x194 + MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d4 + MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d4 + MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d4 + MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d4 + MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d4 + MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d4 + MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d4 + MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d4 + MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d4 + MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x194 >; }; pinctrl_usdhc3_200mhz: usdhc3-200mhzgrp { fsl,pins = < - MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x196 - MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d6 - MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d6 - MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d6 - MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d6 - MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d6 - MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d6 - MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d6 - MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d6 - MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d6 - MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x196 + MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x196 + MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d6 + MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d6 + MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d6 + MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d6 + MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d6 + MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d6 + MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d6 + MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d6 + MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d6 + MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x196 >; }; pinctrl_wdog: wdoggrp { fsl,pins = < - MX8MM_IOMUXC_GPIO1_IO02_WDOG1_WDOG_B 0x166 + MX8MM_IOMUXC_GPIO1_IO02_WDOG1_WDOG_B 0x166 >; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi index 9f49c0b386d3..4cc5ad01d0e2 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi @@ -220,16 +220,15 @@ pmu { compatible = "arm,cortex-a53-pmu"; - interrupts = ; + interrupts = ; }; timer { compatible = "arm,armv8-timer"; - interrupts = , /* Physical Secure */ - , /* Physical Non-Secure */ - , /* Virtual */ - ; /* Hypervisor */ + interrupts = , /* Physical Secure */ + , /* Physical Non-Secure */ + , /* Virtual */ + ; /* Hypervisor */ clock-frequency = <8000000>; arm,no-tick-in-suspend; }; @@ -400,7 +399,7 @@ }; spdif1: spdif@30090000 { - compatible = "fsl,imx35-spdif"; + compatible = "fsl,imx8mm-spdif"; reg = <0x30090000 0x10000>; interrupts = ; clocks = <&clk IMX8MM_CLK_AUDIO_AHB>, /* core */ @@ -1149,8 +1148,10 @@ clocks = <&clk IMX8MM_CLK_DSI_CORE>, <&clk IMX8MM_CLK_DSI_PHY_REF>; clock-names = "bus_clk", "sclk_mipi"; - assigned-clocks = <&clk IMX8MM_CLK_DSI_CORE>; - assigned-clock-parents = <&clk IMX8MM_SYS_PLL1_266M>; + assigned-clocks = <&clk IMX8MM_CLK_DSI_CORE>, + <&clk IMX8MM_CLK_DSI_PHY_REF>; + assigned-clock-parents = <&clk IMX8MM_SYS_PLL1_266M>, + <&clk IMX8MM_CLK_24M>; interrupts = ; power-domains = <&disp_blk_ctrl IMX8MM_DISPBLK_PD_MIPI_DSI>; status = "disabled"; diff --git a/arch/arm64/boot/dts/freescale/imx8mn-evk.dtsi b/arch/arm64/boot/dts/freescale/imx8mn-evk.dtsi index 145355ff91b4..3e590afa4fab 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-evk.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mn-evk.dtsi @@ -415,6 +415,10 @@ assigned-clock-parents = <&clk IMX8MN_SYS_PLL1_80M>; uart-has-rtscts; status = "okay"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; }; &uart2 { /* console */ diff --git a/arch/arm64/boot/dts/freescale/imx8mn-solidsense-n8-compact.dts b/arch/arm64/boot/dts/freescale/imx8mn-solidsense-n8-compact.dts new file mode 100644 index 000000000000..c8c6760524db --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mn-solidsense-n8-compact.dts @@ -0,0 +1,851 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Device Tree file for SolidSense N8 Compact + * + * Copyright 2024 Josua Mayer + */ + +/dts-v1/; + +#include + +#include "imx8mn.dtsi" + +/ { + compatible = "solidrun,solidsense-n8-compact", "fsl,imx8mn"; + model = "SolidRun SolidSense N8 Compact"; + + /* LED labels based on enclosure, schematic names differ. */ + leds { + compatible = "gpio-leds"; + pinctrl-0 = <&led_pins>; + pinctrl-names = "default"; + + /* D20 */ + led1 { + default-state = "off"; + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "led1"; + }; + + /* D18 */ + led2 { + default-state = "off"; + gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; + label = "led2"; + }; + + /* D19 */ + led3 { + default-state = "off"; + gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>; + label = "led3"; + }; + }; + + aliases { + gpio5 = &expander; + rtc0 = &rtc; + rtc1 = &snvs_rtc; + usb0 = &usbotg1; + watchdog0 = &wdog1; + watchdog1 = &rtc; + }; + + chosen { + stdout-path = &uart2; + }; + + reg_modem_vbat: regulator-modem-vbat { + compatible = "regulator-fixed"; + regulator-name = "modem-vbat"; + pinctrl-0 = <®ulator_modem_vbat_pins>; + pinctrl-names = "default"; + regulator-always-on; + regulator-max-microvolt = <3800000>; + regulator-min-microvolt = <3800000>; + gpio = <&gpio3 25 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + /* power to lte modems behind hub ports 2/3 */ + reg_modem_vbus: regulator-modem-vbus { + compatible = "regulator-fixed"; + regulator-name = "modem-vbus"; + pinctrl-0 = <®ulator_modem_vbus_pins>; + pinctrl-names = "default"; + regulator-always-on; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + gpio = <&gpio5 4 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + /* power to usb hub, and type-a behind hub port 1 */ + reg_usb1_vbus: regulator-usb1-vbus { + compatible = "regulator-fixed"; + regulator-name = "usb1-vbus"; + pinctrl-0 = <®ulator_usb1_vbus_pins>; + pinctrl-names = "default"; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + gpio = <&gpio5 5 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_usdhc2_vmmc: regulator-usdhc2-vmmc { + compatible = "regulator-fixed"; + regulator-name = "usdhc2-vmmc"; + off-on-delay-us = <250>; + pinctrl-0 = <®ulator_usdhc2_vmmc_pins>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + vin-supply = <®_vdd_3v3>; + gpio = <&gpio2 19 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_vdd_1v8: regulator-vdd-1v8 { + compatible = "regulator-fixed"; + regulator-name = "vdd-1v8"; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + }; + + reg_vdd_3v3: regulator-vdd-3v3 { + compatible = "regulator-fixed"; + regulator-name = "vdd-3v3"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + }; + + rfkill { + compatible = "rfkill-gpio"; + /* rfkill-gpio inverts internally */ + shutdown-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; + label = "rfkill-wwan"; + pinctrl-0 = <&modem_pins>; + pinctrl-names = "default"; + radio-type = "wwan"; + }; + + usdhc1_pwrseq: usdhc1-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>; + }; + + memory@40000000 { + reg = <0x0 0x40000000 0 0x80000000>; + device_type = "memory"; + }; +}; + +&A53_0 { + cpu-supply = <&buck2_reg>; +}; + +&A53_1 { + cpu-supply = <&buck2_reg>; +}; + +&A53_2 { + cpu-supply = <&buck2_reg>; +}; + +&A53_3 { + cpu-supply = <&buck2_reg>; +}; + +&ddrc { + operating-points-v2 = <&ddrc_opp_table>; + + ddrc_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-266500000 { + opp-hz = /bits/ 64 <266500000>; + }; + + opp-600000000 { + opp-hz = /bits/ 64 <600000000>; + }; + }; +}; + +&ecspi2 { + /* native chip-select causes reading 0xffffffff */ + cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>; + num-cs = <1>; + pinctrl-0 = <&ecspi2_pins>; + pinctrl-names = "default"; + status = "okay"; + + can@0 { + compatible = "microchip,mcp2518fd"; + reg = <0>; + interrupt-parent = <&gpio5>; + interrupts = <1 IRQ_TYPE_EDGE_FALLING>; + clocks = <&clk IMX8MN_CLK_CLKOUT1>; + /* generate 8MHz clock from soc-internal 24mhz reference */ + assigned-clock-parents = <&clk IMX8MN_CLK_24M>, <0>; + assigned-clock-rates = <0>, <8000000>; + assigned-clocks = <&clk IMX8MN_CLK_CLKOUT1_SEL>, + <&clk IMX8MN_CLK_CLKOUT1_DIV>; + pinctrl-0 = <&can_pins>; + pinctrl-names = "default"; + spi-max-frequency = <20000000>; + }; +}; + +&fec1 { + phy-handle = <&phy4>; + phy-mode = "rgmii-id"; + pinctrl-0 = <&fec1_pins>; + pinctrl-names = "default"; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + /* + * Depending on board revision two different phys are used: + * - v1.1: atheros phy at address 4 + * - v1.2+: analog devices phy at address 0 + * Configure first version by default. + * On v1.2 and later, U-Boot will enable the correct phy + * based on runtime detection and patch dtb accordingly. + */ + + /* ADIN1300 */ + phy0: ethernet-phy@0 { + reg = <0>; + interrupt-parent = <&gpio1>; + interrupts = <10 IRQ_TYPE_LEVEL_LOW>; + reset-assert-us = <10>; + reset-deassert-us = <5000>; + reset-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>; + adi,led-polarity = ; + adi,link-st-polarity = ; + status = "disabled"; + + leds { + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + active-low; + color = ; + default-state = "keep"; + function = LED_FUNCTION_LAN; + }; + }; + }; + + /* AR8035 */ + phy4: ethernet-phy@4 { + reg = <4>; + reset-assert-us = <10000>; + reset-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>; + status = "okay"; + }; + }; +}; + +&gpio5 { + usb-hub-reset-hog { + line-name = "usb-hub-reset"; + gpios = <3 GPIO_ACTIVE_LOW>; + gpio-hog; + /* deasserted */ + output-low; + }; +}; + +&i2c1 { + pinctrl-0 = <&i2c1_pins>; + pinctrl-names = "default"; + status = "okay"; + + pmic@4b { + compatible = "rohm,bd71847"; + reg = <0x4b>; + interrupt-parent = <&gpio1>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + clocks = <&osc_32k>; + #clock-cells = <0>; + clock-output-names = "clk-32k-out"; + pinctrl-0 = <&pmic_pins>; + pinctrl-names = "default"; + rohm,reset-snvs-powered; + + regulators { + BUCK1 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1300000>; + regulator-min-microvolt = <700000>; + regulator-ramp-delay = <1250>; + // supplies soc vdd, soc mipi vdd @ 0.9V + regulator-name = "buck1"; + rohm,dvs-run-voltage = <850000>; + rohm,dvs-suspend-voltage = <750000>; + }; + + buck2_reg: BUCK2 { + regulator-name = "buck2"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1300000>; + regulator-min-microvolt = <700000>; + regulator-ramp-delay = <1250>; + rohm,dvs-idle-voltage = <900000>; + rohm,dvs-run-voltage = <1000000>; + rohm,dvs-suspend-voltage = <0>; + }; + + BUCK3 { + // BUCK5 in datasheet + // output floating + regulator-name = "buck3"; + regulator-max-microvolt = <1350000>; + regulator-min-microvolt = <700000>; + }; + + BUCK4 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3000000>; + // BUCK6 in datasheet + // supplies ldo3, ldo5, muxsw + regulator-name = "buck4"; + }; + + BUCK5 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1995000>; + regulator-min-microvolt = <1605000>; + // BUCK7 in datasheet + // supplies ldo4, ldo6, muxsw + // enables dram vpp @ 2.5V + regulator-name = "buck5"; + }; + + BUCK6 { + // BUCK8 in datasheet + // supplies dram @ 1.2V + regulator-name = "buck6"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1400000>; + regulator-min-microvolt = <800000>; + }; + + LDO1 { + // supplies soc snvs @ 1.8V + regulator-name = "ldo1"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <1600000>; + }; + + LDO2 { + // supplies soc snvs @ 0.8V + regulator-name = "ldo2"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <900000>; + regulator-min-microvolt = <800000>; + }; + + LDO3 { + // supplies soc vdd @ 1.8V + regulator-name = "ldo3"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <1800000>; + }; + + LDO4 { + // output floating + regulator-name = "ldo4"; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <900000>; + }; + + LDO5 { + // output floating + regulator-name = "ldo5"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <800000>; + }; + + LDO6 { + // supplies soc vdd mipi @ 1.2V + regulator-name = "ldo6"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <900000>; + }; + }; + }; +}; + +&i2c2 { + /* + * routed to various connectors: + * - basler camera (CON2) + * - touchscreen (J3) + * - expansion connector (J14) + */ + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + status = "okay"; +}; + +&i2c3 { + pinctrl-0 = <&i2c3_pins>; + pinctrl-names = "default"; + status = "okay"; + + expander: gpio@20 { + compatible = "ti,tca6408"; + reg = <0x20>; + #interrupt-cells = <2>; + interrupt-controller; + interrupt-parent = <&gpio2>; + interrupts = <11 IRQ_TYPE_EDGE_FALLING>; + #gpio-cells = <2>; + gpio-controller; + gpio-line-names = "SYSGD", "PFO#", "CAPGD", "CAPFLT#", + "CHGEN#", "BSTEN#", "", ""; + pinctrl-0 = <&gpio_expander_pins>; + pinctrl-names = "default"; + reset-gpios = <&gpio3 16 GPIO_ACTIVE_LOW>; + }; + + light-sensor@44 { + compatible = "isil,isl29023"; + reg = <0x44>; + }; + + accelerometer@53 { + compatible = "adi,adxl345"; + reg = <0x53>; + }; + + /* battery-charger@68 */ + + rtc: rtc@69 { + compatible = "abracon,abx80x"; + reg = <0x69>; + interrupt-parent = <&gpio1>; + interrupts = <1 IRQ_TYPE_EDGE_FALLING>; + pinctrl-0 = <&rtc_pins>; + pinctrl-names = "default"; + abracon,tc-diode = "schottky"; + abracon,tc-resistor = <3>; + }; +}; + +&i2c4 { + /* routed to expansion connector (J14) */ + pinctrl-names = "default"; + pinctrl-0 = <&i2c4_pins>; + status = "okay"; +}; + +&iomuxc { + pinctrl-0 = <&tamper_pins>, <&usb_hub_pins>; + pinctrl-names = "default"; + + can_pins: pinctrl-can-grp { + fsl,pins = < + MX8MN_IOMUXC_SAI3_TXD_GPIO5_IO1 0x140 + >; + }; + + ecspi2_pins: pinctrl-ecspi2-grp { + fsl,pins = < + MX8MN_IOMUXC_ECSPI2_SCLK_ECSPI2_SCLK 0x96 + MX8MN_IOMUXC_ECSPI2_MOSI_ECSPI2_MOSI 0x1d6 + MX8MN_IOMUXC_ECSPI2_MISO_ECSPI2_MISO 0x1d6 + MX8MN_IOMUXC_ECSPI2_SS0_GPIO5_IO13 0x1d6 + >; + }; + + fec1_pins: pinctrl-fec1-grp { + /* + * Some pins are sampled at phy reset to apply configuration: + * - AR803x PHY (revision 1.1) + * - RXD[1:0]: phy address bits [1:0] + * - RXD[3:2],RX_CTL: mac interface select bits 3,1,0 + * - ADIN1300 PHY (revision 1.2 or later) + * - RXD[3:0]: phy address bits [3:0] + * - RX_CTL,RXC: mac interface select bits 1,0 + * SoC enables pull-down at reset, PHYs have internal + * pull-down, so pinmux may unset pull-enable. + */ + fsl,pins = < + MX8MN_IOMUXC_ENET_MDC_ENET1_MDC 0x2 + MX8MN_IOMUXC_ENET_MDIO_ENET1_MDIO 0x2 + MX8MN_IOMUXC_ENET_TD3_ENET1_RGMII_TD3 0x1e + MX8MN_IOMUXC_ENET_TD2_ENET1_RGMII_TD2 0x1e + MX8MN_IOMUXC_ENET_TD1_ENET1_RGMII_TD1 0x1e + MX8MN_IOMUXC_ENET_TD0_ENET1_RGMII_TD0 0x1e + /* RD[3:0] sampled at phy reset for address bits [3:0] */ + MX8MN_IOMUXC_ENET_RD3_ENET1_RGMII_RD3 0x90 + MX8MN_IOMUXC_ENET_RD2_ENET1_RGMII_RD2 0x90 + MX8MN_IOMUXC_ENET_RD1_ENET1_RGMII_RD1 0x90 + MX8MN_IOMUXC_ENET_RD0_ENET1_RGMII_RD0 0x90 + MX8MN_IOMUXC_ENET_TXC_ENET1_RGMII_TXC 0x10 + MX8MN_IOMUXC_ENET_RXC_ENET1_RGMII_RXC 0x90 + MX8MN_IOMUXC_ENET_RX_CTL_ENET1_RGMII_RX_CTL 0x90 + MX8MN_IOMUXC_ENET_TX_CTL_ENET1_RGMII_TX_CTL 0x10 + /* phy reset */ + MX8MN_IOMUXC_SAI5_RXFS_GPIO3_IO19 0x0 + /* phy interrupt */ + MX8MN_IOMUXC_GPIO1_IO10_GPIO1_IO10 0x140 + >; + }; + + gpio_expander_pins: pinctrl-gpio-expander-grp { + fsl,pins = < + MX8MN_IOMUXC_NAND_READY_B_GPIO3_IO16 0x140 + MX8MN_IOMUXC_SD1_STROBE_GPIO2_IO11 0x140 + >; + }; + + i2c1_pins: pinctrl-i2c1-grp { + fsl,pins = < + MX8MN_IOMUXC_I2C1_SCL_I2C1_SCL 0x400001c2 + MX8MN_IOMUXC_I2C1_SDA_I2C1_SDA 0x400001c2 + >; + }; + + i2c2_pins: pinctrl-i2c2-grp { + fsl,pins = < + MX8MN_IOMUXC_I2C2_SCL_I2C2_SCL 0x400001c2 + MX8MN_IOMUXC_I2C2_SDA_I2C2_SDA 0x400001c2 + >; + }; + + i2c3_pins: pinctrl-i2c3-grp { + fsl,pins = < + MX8MN_IOMUXC_I2C3_SCL_I2C3_SCL 0x400001c2 + MX8MN_IOMUXC_I2C3_SDA_I2C3_SDA 0x400001c2 + >; + }; + + i2c4_pins: pinctrl-i2c4-grp { + fsl,pins = < + MX8MN_IOMUXC_I2C4_SCL_I2C4_SCL 0x400001c2 + MX8MN_IOMUXC_I2C4_SDA_I2C4_SDA 0x400001c2 + >; + }; + + ieee802151_radio_pins: pinctrl-ieee802151-radio-grp { + fsl,pins = < + /* RESETN */ + MX8MN_IOMUXC_GPIO1_IO05_GPIO1_IO5 0x0 + /* VDD_EN */ + MX8MN_IOMUXC_GPIO1_IO06_GPIO1_IO6 0x0 + /* SWDCLK */ + MX8MN_IOMUXC_GPIO1_IO14_GPIO1_IO14 0x0 + /* SDIO */ + MX8MN_IOMUXC_GPIO1_IO15_GPIO1_IO15 0x0 + >; + }; + + led_pins: pinctrl-led-grp { + fsl,pins = < + MX8MN_IOMUXC_GPIO1_IO11_GPIO1_IO11 0x100 + MX8MN_IOMUXC_GPIO1_IO12_GPIO1_IO12 0x100 + MX8MN_IOMUXC_GPIO1_IO13_GPIO1_IO13 0x100 + >; + }; + + modem_pins: pinctrl-modem-grp { + fsl,pins = < + /* RESET_N: modem-internal pull-down */ + MX8MN_IOMUXC_GPIO1_IO07_GPIO1_IO7 0x0 + /* PWRKEY: pull-down ensures always-on */ + MX8MN_IOMUXC_GPIO1_IO08_GPIO1_IO8 0x100 + >; + }; + + pmic_pins: pinctrl-pmic-grp { + fsl,pins = < + MX8MN_IOMUXC_GPIO1_IO03_GPIO1_IO3 0x140 + >; + }; + + regulator_modem_vbat_pins: pinctrl-regulator-modem-vbat-grp { + fsl,pins = < + MX8MN_IOMUXC_SAI5_MCLK_GPIO3_IO25 0x0 + >; + }; + + regulator_modem_vbus_pins: pinctrl-regulator-modem-vbus-grp { + fsl,pins = < + MX8MN_IOMUXC_SPDIF_RX_GPIO5_IO4 0x0 + >; + }; + + regulator_usb1_vbus_pins: pinctrl-regulator-usb1-vbus-grp { + fsl,pins = < + MX8MN_IOMUXC_SPDIF_EXT_CLK_GPIO5_IO5 0x0 + >; + }; + + regulator_usdhc2_vmmc_pins: pinctrl-regulator-usdhc2-vmmc-grp { + fsl,pins = < + MX8MN_IOMUXC_SD2_RESET_B_GPIO2_IO19 0x0 + >; + }; + + rtc_pins: pinctrl-rtc-grp { + fsl,pins = < + MX8MN_IOMUXC_GPIO1_IO01_GPIO1_IO1 0x140 + MX8MN_IOMUXC_SAI3_RXFS_GPIO4_IO28 0x100 + >; + }; + + tamper_pins: pinctrl-tamper-grp { + /* + * Routed to physical tamper input (J12), + * accelerometer and light-sensor interrupts. + */ + fsl,pins = < + MX8MN_IOMUXC_GPIO1_IO09_GPIO1_IO9 0x140 + >; + }; + + uart1_pins: pinctrl-uart1-grp { + fsl,pins = < + MX8MN_IOMUXC_UART1_RXD_UART1_DCE_RX 0x140 + MX8MN_IOMUXC_UART1_TXD_UART1_DCE_TX 0x140 + MX8MN_IOMUXC_UART3_RXD_UART1_DCE_CTS_B 0x140 + MX8MN_IOMUXC_UART3_TXD_UART1_DCE_RTS_B 0x140 + /* BT_REG_ON */ + MX8MN_IOMUXC_SD1_DATA4_GPIO2_IO6 0x0 + /* BT_WAKE_DEV */ + MX8MN_IOMUXC_SD1_DATA5_GPIO2_IO7 0x0 + /* BT_WAKE_HOST */ + MX8MN_IOMUXC_SD1_DATA6_GPIO2_IO8 0x100 + >; + }; + + uart2_pins: pinctrl-uart2-grp { + fsl,pins = < + MX8MN_IOMUXC_UART2_RXD_UART2_DCE_RX 0x140 + MX8MN_IOMUXC_UART2_TXD_UART2_DCE_TX 0x140 + >; + }; + + uart3_pins: pinctrl-uart3-grp { + fsl,pins = < + MX8MN_IOMUXC_ECSPI1_MOSI_UART3_DTE_RX 0x140 + MX8MN_IOMUXC_ECSPI1_SCLK_UART3_DTE_TX 0x140 + MX8MN_IOMUXC_ECSPI1_MISO_UART3_DTE_RTS_B 0x140 + MX8MN_IOMUXC_ECSPI1_SS0_UART3_DTE_CTS_B 0x140 + >; + }; + + uart4_pins: pinctrl-uart4-grp { + fsl,pins = < + MX8MN_IOMUXC_UART4_RXD_UART4_DCE_RX 0x140 + MX8MN_IOMUXC_UART4_TXD_UART4_DCE_TX 0x140 + >; + }; + + usb_hub_pins: pinctrl-usb-hub-grp { + fsl,pins = < + MX8MN_IOMUXC_SPDIF_TX_GPIO5_IO3 0x0 + >; + }; + + usdhc1_pins: pinctrl-usdhc1-grp { + fsl,pins = < + MX8MN_IOMUXC_SD1_CLK_USDHC1_CLK 0x190 + MX8MN_IOMUXC_SD1_CMD_USDHC1_CMD 0x1d0 + MX8MN_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x1d0 + MX8MN_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x1d0 + MX8MN_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x1d0 + MX8MN_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x1d0 + /* wifi refclk */ + MX8MN_IOMUXC_GPIO1_IO00_ANAMIX_REF_CLK_32K 0x0 + /* WL_WAKE_HOST */ + MX8MN_IOMUXC_SD1_DATA7_GPIO2_IO9 0x100 + /* WL_REG_ON */ + MX8MN_IOMUXC_SD1_RESET_B_GPIO2_IO10 0x0 + >; + }; + + usdhc2_100mhz_pins: pinctrl-usdhc2-100mhz-grp { + fsl,pins = < + MX8MN_IOMUXC_SD2_CLK_USDHC2_CLK 0x194 + MX8MN_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d4 + MX8MN_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d4 + MX8MN_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d4 + MX8MN_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4 + MX8MN_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4 + MX8MN_IOMUXC_SD2_CD_B_USDHC2_CD_B 0x0 + /* usdhc2 signalling voltage pmic control */ + MX8MN_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x140 + >; + }; + + usdhc2_200mhz_pins: pinctrl-usdhc2-100mhz-grp { + fsl,pins = < + MX8MN_IOMUXC_SD2_CLK_USDHC2_CLK 0x196 + MX8MN_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d6 + MX8MN_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d6 + MX8MN_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d6 + MX8MN_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6 + MX8MN_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6 + MX8MN_IOMUXC_SD2_CD_B_USDHC2_CD_B 0x0 + /* usdhc2 signalling voltage pmic control */ + MX8MN_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x140 + >; + }; + + usdhc2_pins: pinctrl-usdhc2-grp { + fsl,pins = < + MX8MN_IOMUXC_SD2_CLK_USDHC2_CLK 0x190 + MX8MN_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d0 + MX8MN_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d0 + MX8MN_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d0 + MX8MN_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0 + MX8MN_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0 + MX8MN_IOMUXC_SD2_CD_B_USDHC2_CD_B 0x0 + /* usdhc2 signalling voltage pmic control */ + MX8MN_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x140 + >; + }; + + usdhc3_pins: pinctrl-usdhc3-grp { + fsl,pins = < + MX8MN_IOMUXC_NAND_WE_B_USDHC3_CLK 0x190 + MX8MN_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d0 + MX8MN_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d0 + MX8MN_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d0 + MX8MN_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d0 + MX8MN_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d0 + MX8MN_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d0 + MX8MN_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d0 + MX8MN_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d0 + MX8MN_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d0 + MX8MN_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x190 + >; + }; + + wdog1_pins: pinctrl-wdog1-grp { + fsl,pins = < + MX8MN_IOMUXC_GPIO1_IO02_WDOG1_WDOG_B 0x140 + >; + }; +}; + +/* Bluetooth */ +&uart1 { + assigned-clock-parents = <&clk IMX8MN_SYS_PLL1_80M>; + /* select 80MHz parent clock to support maximum baudrate 4Mbps */ + assigned-clocks = <&clk IMX8MN_CLK_UART1>; + pinctrl-0 = <&uart1_pins>; + pinctrl-names = "default"; + uart-has-rtscts; + status = "okay"; + + bluetooth { + compatible = "brcm,bcm4330-bt"; + device-wakeup-gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>; + host-wakeup-gpios = <&gpio2 8 GPIO_ACTIVE_HIGH>; + max-speed = <3000000>; + shutdown-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>; + }; +}; + +/* console */ +&uart2 { + pinctrl-0 = <&uart2_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +/* RS485 */ +&uart3 { + pinctrl-0 = <&uart3_pins>; + pinctrl-names = "default"; + uart-has-rtscts; + fsl,dte-mode; + linux,rs485-enabled-at-boot-time; + status = "okay"; +}; + +/* 802.15.1 radio */ +&uart4 { + pinctrl-0 = <&uart4_pins &ieee802151_radio_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&usbotg1 { + disable-over-current; + dr_mode = "host"; + vbus-supply = <®_usb1_vbus>; + status = "okay"; +}; + +/* WiFi */ +&usdhc1 { + bus-width = <4>; + mmc-pwrseq = <&usdhc1_pwrseq>; + pinctrl-0 = <&usdhc1_pins>; + pinctrl-names = "default"; + vmmc-supply = <®_vdd_3v3>; + vqmmc-supply = <®_vdd_1v8>; + status = "okay"; +}; + +/* microSD */ +&usdhc2 { + broken-cd; + bus-width = <4>; + pinctrl-0 = <&usdhc2_pins>; + pinctrl-1 = <&usdhc2_100mhz_pins>; + pinctrl-2 = <&usdhc2_200mhz_pins>; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + vmmc-supply = <®_usdhc2_vmmc>; + status = "okay"; +}; + +/* eMMC */ +&usdhc3 { + bus-width = <8>; + non-removable; + pinctrl-0 = <&usdhc3_pins>; + vmmc-supply = <®_vdd_3v3>; + vqmmc-supply = <®_vdd_1v8>; + /* + * Use lowest drive strength for all high-speed modes to minimise + * electro-magnetic emissions. + * In this particular design HS-400 still works okay, no extra + * pinctrl for 100mhz and 200mhz are required. + */ + pinctrl-names = "default"; + status = "okay"; +}; + +&wdog1 { + pinctrl-0 = <&wdog1_pins>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl-mba8mx-lvds-tm070jvhg33.dtso b/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl-mba8mx-lvds-tm070jvhg33.dtso index 29235e390a5d..046399a455ba 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl-mba8mx-lvds-tm070jvhg33.dtso +++ b/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl-mba8mx-lvds-tm070jvhg33.dtso @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) /* - * Copyright (c) 2022-2023 TQ-Systems GmbH , + * Copyright (c) 2022-2026 TQ-Systems GmbH , * D-82229 Seefeld, Germany. * Author: Alexander Stein */ @@ -10,10 +10,6 @@ #include -&{/} { - compatible = "tq,imx8mn-tqma8mqnl-mba8mx", "tq,imx8mn-tqma8mqnl", "fsl,imx8mn"; -}; - &backlight_lvds { status = "okay"; }; @@ -36,6 +32,7 @@ }; &mipi_dsi { + samsung,burst-clock-frequency = <600000000>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl-mba8mx.dts b/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl-mba8mx.dts index 664f4a6950a8..01d565cdbfea 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl-mba8mx.dts +++ b/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl-mba8mx.dts @@ -65,7 +65,6 @@ }; &mipi_dsi { - samsung,burst-clock-frequency = <891000000>; samsung,esc-clock-frequency = <20000000>; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl.dtsi b/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl.dtsi index 31a3ca137e63..48a687926aa1 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl.dtsi @@ -283,7 +283,7 @@ }; pinctrl_pmic: pmicgrp { - fsl,pins = ; + fsl,pins = ; }; pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { diff --git a/arch/arm64/boot/dts/freescale/imx8mn.dtsi b/arch/arm64/boot/dts/freescale/imx8mn.dtsi index 3199bc0966b0..79b169b07c4f 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mn.dtsi @@ -215,8 +215,7 @@ pmu { compatible = "arm,cortex-a53-pmu"; - interrupts = ; + interrupts = ; }; psci { @@ -258,10 +257,10 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; clock-frequency = <8000000>; arm,no-tick-in-suspend; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-ab2.dts b/arch/arm64/boot/dts/freescale/imx8mp-ab2.dts new file mode 100644 index 000000000000..dbbc0df0e3d1 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-ab2.dts @@ -0,0 +1,912 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2020-2026 NXP + */ + +/dts-v1/; + +#include +#include "imx8mp.dtsi" + +/ { + compatible = "fsl,imx8mp-ab2", "fsl,imx8mp"; + model = "NXP i.MX8MP SOM on AB2"; + + chosen { + stdout-path = &uart2; + }; + + gpio-leds { + compatible = "gpio-leds"; + pinctrl-0 = <&pinctrl_gpio_led>; + pinctrl-names = "default"; + + status { + default-state = "on"; + gpios = <&gpio3 16 GPIO_ACTIVE_HIGH>; + label = "yellow:status"; + }; + }; + + native-hdmi-connector { + compatible = "hdmi-connector"; + label = "HDMI OUT"; + type = "a"; + + port { + hdmi_in: endpoint { + remote-endpoint = <&hdmi_tx_out>; + }; + }; + }; + + reg_ab2_ana_pwr: regulator-ab2-ana-pwr { + compatible = "regulator-fixed"; + regulator-name = "ab2_ana_pwr"; + pinctrl-0 = <&pinctrl_ab2_ana_pwr>; + pinctrl-names = "default"; + regulator-always-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + gpio = <&gpio1 10 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_ab2_vdd_pwr_5v0: regulator-ab2-vdd-pwr-5v0 { + compatible = "regulator-fixed"; + regulator-name = "ab2_vdd_pwr_5v0"; + pinctrl-0 = <&pinctrl_ab2_vdd_pwr_5v0>; + pinctrl-names = "default"; + regulator-always-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_usdhc2_vmmc: regulator-usdhc2 { + compatible = "regulator-fixed"; + regulator-name = "VSD_3V3"; + pinctrl-0 = <&pinctrl_reg_usdhc2_vmmc>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + gpio = <&gpio2 19 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reserved-memory { + ranges; + #address-cells = <2>; + #size-cells = <2>; + + dsp_vdev0vring0: vdev0vring0@942f0000 { + reg = <0 0x942f0000 0 0x8000>; + no-map; + }; + + dsp_vdev0vring1: vdev0vring1@942f8000 { + reg = <0 0x942f8000 0 0x8000>; + no-map; + }; + + dsp_vdev0buffer: vdev0buffer@94300000 { + compatible = "shared-dma-pool"; + reg = <0 0x94300000 0 0x100000>; + no-map; + }; + }; + + sound-ak4458 { + compatible = "fsl,imx-audio-card"; + model = "ak4458-audio"; + + pri-dai-link { + format = "i2s"; + link-name = "akcodec"; + fsl,mclk-equal-bclk; + + codec { + sound-dai = <&ak4458_1>, <&ak4458_2>; + }; + + cpu { + sound-dai = <&sai1>; + }; + }; + }; + + sound-ak5552 { + compatible = "fsl,imx-audio-card"; + model = "ak5552-audio"; + + pri-dai-link { + format = "i2s"; + link-name = "akcodec"; + fsl,mclk-equal-bclk; + + codec { + sound-dai = <&ak5552>; + }; + + cpu { + sound-dai = <&sai3>; + }; + }; + }; + + sound-hdmi { + compatible = "fsl,imx-audio-hdmi"; + audio-cpu = <&aud2htx>; + hdmi-out; + model = "audio-hdmi"; + }; + + sound-micfil { + compatible = "fsl,imx-audio-card"; + model = "micfil-audio"; + + pri-dai-link { + format = "i2s"; + link-name = "micfil hifi"; + + cpu { + sound-dai = <&micfil>; + }; + }; + }; + + sound-xcvr { + compatible = "fsl,imx-audio-card"; + model = "imx-audio-xcvr"; + + pri-dai-link { + link-name = "XCVR PCM"; + + cpu { + sound-dai = <&xcvr>; + }; + }; + }; + + memory@40000000 { + reg = <0x0 0x40000000 0 0xc0000000>, + <0x1 0x00000000 0 0xc0000000>; + device_type = "memory"; + }; +}; + +&A53_0 { + cpu-supply = <&buck2>; +}; + +&A53_1 { + cpu-supply = <&buck2>; +}; + +&A53_2 { + cpu-supply = <&buck2>; +}; + +&A53_3 { + cpu-supply = <&buck2>; +}; + +&aud2htx { + status = "okay"; +}; + +&dsp { + memory-region = <&dsp_vdev0buffer>, <&dsp_vdev0vring0>, + <&dsp_vdev0vring1>, <&dsp_reserved>; + status = "okay"; +}; + +&dsp_reserved { + status = "okay"; +}; + +&easrc { + #sound-dai-cells = <0>; + fsl,asrc-rate = <48000>; + status = "okay"; +}; + +&eqos { + phy-handle = <ðphy0>; + phy-mode = "rgmii-id"; + pinctrl-0 = <&pinctrl_eqos>; + pinctrl-names = "default"; + status = "okay"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + ethphy0: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + }; +}; + +&flexspi { + pinctrl-0 = <&pinctrl_flexspi0>; + pinctrl-names = "default"; + status = "okay"; + + mt25qu256aba: flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <80000000>; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <1>; + }; +}; + +&hdmi_pai { + status = "okay"; +}; + +&hdmi_pvi { + status = "okay"; +}; + +&hdmi_tx { + pinctrl-0 = <&pinctrl_hdmi>; + pinctrl-names = "default"; + status = "okay"; + + ports { + port@1 { + hdmi_tx_out: endpoint { + remote-endpoint = <&hdmi_in>; + }; + }; + }; +}; + +&hdmi_tx_phy { + status = "okay"; +}; + +&i2c1 { + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_i2c1>; + pinctrl-names = "default"; + status = "okay"; + + pca9450: pmic@25 { + compatible = "nxp,pca9450c"; + reg = <0x25>; + interrupt-parent = <&gpio1>; + interrupts = <3 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_pmic>; + + regulators { + buck1: BUCK1 { + regulator-name = "BUCK1"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <2187500>; + regulator-min-microvolt = <600000>; + regulator-ramp-delay = <3125>; + }; + + buck2: BUCK2 { + regulator-name = "BUCK2"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <2187500>; + regulator-min-microvolt = <600000>; + regulator-ramp-delay = <3125>; + nxp,dvs-run-voltage = <950000>; + nxp,dvs-standby-voltage = <850000>; + }; + + buck4: BUCK4 { + regulator-name = "BUCK4"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3400000>; + regulator-min-microvolt = <600000>; + }; + + buck5: BUCK5 { + regulator-name = "BUCK5"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3400000>; + regulator-min-microvolt = <600000>; + }; + + buck6: BUCK6 { + regulator-name = "BUCK6"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3400000>; + regulator-min-microvolt = <600000>; + }; + + ldo1: LDO1 { + regulator-name = "LDO1"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <1600000>; + }; + + ldo2: LDO2 { + regulator-name = "LDO2"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1150000>; + regulator-min-microvolt = <800000>; + }; + + ldo3: LDO3 { + regulator-name = "LDO3"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <800000>; + }; + + ldo4: LDO4 { + regulator-name = "LDO4"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <800000>; + }; + + ldo5: LDO5 { + regulator-name = "LDO5"; + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <1800000>; + }; + }; + }; +}; + +&i2c2 { + clock-frequency = <100000>; + pinctrl-0 = <&pinctrl_i2c2>; + pinctrl-names = "default"; + status = "okay"; + + pca6408: gpio@20 { + compatible = "ti,tca6408"; + reg = <0x20>; + #gpio-cells = <2>; + gpio-controller; + }; + + pca6416_2: gpio@21 { + compatible = "ti,tca6416"; + reg = <0x21>; + #gpio-cells = <2>; + gpio-controller; + }; +}; + +&i2c3 { + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_i2c3>; + pinctrl-names = "default"; + status = "okay"; + + ak4458_1: audio-codec@10 { + compatible = "asahi-kasei,ak4458"; + reg = <0x10>; + #sound-dai-cells = <0>; + AVDD-supply = <®_ab2_ana_pwr>; + DVDD-supply = <®_ab2_ana_pwr>; + reset-gpios = <&pca6416 4 GPIO_ACTIVE_LOW>; + sound-name-prefix = "0"; + }; + + ak4458_2: audio-codec@11 { + compatible = "asahi-kasei,ak4458"; + reg = <0x11>; + #sound-dai-cells = <0>; + AVDD-supply = <®_ab2_ana_pwr>; + DVDD-supply = <®_ab2_ana_pwr>; + reset-gpios = <&pca6416 4 GPIO_ACTIVE_LOW>; + sound-name-prefix = "1"; + }; + + ak4458_3: audio-codec@12 { + compatible = "asahi-kasei,ak4458"; + reg = <0x12>; + #sound-dai-cells = <0>; + AVDD-supply = <®_ab2_ana_pwr>; + DVDD-supply = <®_ab2_ana_pwr>; + reset-gpios = <&pca6416 4 GPIO_ACTIVE_LOW>; + }; + + ak5552: audio-codec@13 { + compatible = "asahi-kasei,ak5552"; + reg = <0x13>; + #sound-dai-cells = <0>; + AVDD-supply = <®_ab2_ana_pwr>; + DVDD-supply = <®_ab2_ana_pwr>; + reset-gpios = <&pca6416 2 GPIO_ACTIVE_LOW>; + }; + + pca6416: gpio@20 { + compatible = "ti,tca6416"; + reg = <0x20>; + #gpio-cells = <2>; + gpio-controller; + }; +}; + +&iomuxc { + pinctrl-0 = <&pinctrl_hog>; + pinctrl-names = "default"; + + pinctrl_ab2_ana_pwr: ab2anapwrgrp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO10__GPIO1_IO10 0xd6 + >; + }; + + pinctrl_ab2_vdd_pwr_5v0: ab2vddpwr5v0grp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO07__GPIO1_IO07 0xd6 + >; + }; + + pinctrl_eqos: eqosgrp { + fsl,pins = < + MX8MP_IOMUXC_ENET_MDC__ENET_QOS_MDC 0x2 + MX8MP_IOMUXC_ENET_MDIO__ENET_QOS_MDIO 0x2 + MX8MP_IOMUXC_ENET_RD0__ENET_QOS_RGMII_RD0 0x90 + MX8MP_IOMUXC_ENET_RD1__ENET_QOS_RGMII_RD1 0x90 + MX8MP_IOMUXC_ENET_RD2__ENET_QOS_RGMII_RD2 0x90 + MX8MP_IOMUXC_ENET_RD3__ENET_QOS_RGMII_RD3 0x90 + MX8MP_IOMUXC_ENET_RXC__CCM_ENET_QOS_CLOCK_GENERATE_RX_CLK 0x90 + MX8MP_IOMUXC_ENET_RX_CTL__ENET_QOS_RGMII_RX_CTL 0x90 + MX8MP_IOMUXC_ENET_TD0__ENET_QOS_RGMII_TD0 0x16 + MX8MP_IOMUXC_ENET_TD1__ENET_QOS_RGMII_TD1 0x16 + MX8MP_IOMUXC_ENET_TD2__ENET_QOS_RGMII_TD2 0x16 + MX8MP_IOMUXC_ENET_TD3__ENET_QOS_RGMII_TD3 0x16 + MX8MP_IOMUXC_ENET_TX_CTL__ENET_QOS_RGMII_TX_CTL 0x16 + MX8MP_IOMUXC_ENET_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK 0x16 + MX8MP_IOMUXC_GPIO1_IO05__GPIO1_IO05 0x10 + >; + }; + + pinctrl_flexspi0: flexspi0grp { + fsl,pins = < + MX8MP_IOMUXC_NAND_ALE__FLEXSPI_A_SCLK 0x1c2 + MX8MP_IOMUXC_NAND_CE0_B__FLEXSPI_A_SS0_B 0x82 + MX8MP_IOMUXC_NAND_DATA00__FLEXSPI_A_DATA00 0x82 + MX8MP_IOMUXC_NAND_DATA01__FLEXSPI_A_DATA01 0x82 + MX8MP_IOMUXC_NAND_DATA02__FLEXSPI_A_DATA02 0x82 + MX8MP_IOMUXC_NAND_DATA03__FLEXSPI_A_DATA03 0x82 + >; + }; + + pinctrl_gpio_led: gpioledgrp { + fsl,pins = < + MX8MP_IOMUXC_NAND_READY_B__GPIO3_IO16 0x10 + >; + }; + + pinctrl_hdmi: hdmigrp { + fsl,pins = < + MX8MP_IOMUXC_HDMI_DDC_SCL__HDMIMIX_HDMI_SCL 0x1c2 + MX8MP_IOMUXC_HDMI_DDC_SDA__HDMIMIX_HDMI_SDA 0x1c2 + MX8MP_IOMUXC_HDMI_CEC__HDMIMIX_HDMI_CEC 0x10 + >; + }; + + pinctrl_hog: hoggrp { + fsl,pins = < + MX8MP_IOMUXC_HDMI_HPD__HDMIMIX_HDMI_HPD 0x40000010 + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX8MP_IOMUXC_I2C1_SCL__I2C1_SCL 0x400001c2 + MX8MP_IOMUXC_I2C1_SDA__I2C1_SDA 0x400001c2 + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX8MP_IOMUXC_I2C2_SCL__I2C2_SCL 0x400001c2 + MX8MP_IOMUXC_I2C2_SDA__I2C2_SDA 0x400001c2 + >; + }; + + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX8MP_IOMUXC_I2C3_SCL__I2C3_SCL 0x400001c2 + MX8MP_IOMUXC_I2C3_SDA__I2C3_SDA 0x400001c2 + >; + }; + + pinctrl_pdm: pdmgrp { + fsl,pins = < + MX8MP_IOMUXC_SAI5_RXC__AUDIOMIX_PDM_CLK 0xd6 + MX8MP_IOMUXC_SAI5_RXD0__AUDIOMIX_PDM_BIT_STREAM00 0xd6 + MX8MP_IOMUXC_SAI5_RXD1__AUDIOMIX_PDM_BIT_STREAM01 0xd6 + MX8MP_IOMUXC_SAI5_RXD2__AUDIOMIX_PDM_BIT_STREAM02 0xd6 + MX8MP_IOMUXC_SAI5_RXD3__AUDIOMIX_PDM_BIT_STREAM03 0xd6 + >; + }; + + pinctrl_pmic: pmicgrp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x1c0 + >; + }; + + pinctrl_pwm1: pwm1grp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO01__PWM1_OUT 0x116 + >; + }; + + pinctrl_pwm2: pwm2grp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO11__PWM2_OUT 0x116 + >; + }; + + pinctrl_pwm4: pwm4grp { + fsl,pins = < + MX8MP_IOMUXC_SAI5_RXFS__PWM4_OUT 0x116 + >; + }; + + pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { + fsl,pins = < + MX8MP_IOMUXC_SD2_RESET_B__GPIO2_IO19 0x41 + >; + }; + + pinctrl_sai1: sai1grp { + fsl,pins = < + MX8MP_IOMUXC_SAI1_MCLK__AUDIOMIX_SAI1_MCLK 0xd6 + MX8MP_IOMUXC_SAI1_TXFS__AUDIOMIX_SAI1_TX_SYNC 0xd6 + MX8MP_IOMUXC_SAI1_TXC__AUDIOMIX_SAI1_TX_BCLK 0xd6 + MX8MP_IOMUXC_SAI1_TXD0__AUDIOMIX_SAI1_TX_DATA00 0xd6 + MX8MP_IOMUXC_SAI1_TXD1__AUDIOMIX_SAI1_TX_DATA01 0xd6 + MX8MP_IOMUXC_SAI1_TXD2__AUDIOMIX_SAI1_TX_DATA02 0xd6 + MX8MP_IOMUXC_SAI1_TXD3__AUDIOMIX_SAI1_TX_DATA03 0xd6 + MX8MP_IOMUXC_SAI1_TXD4__AUDIOMIX_SAI1_TX_DATA04 0xd6 + MX8MP_IOMUXC_SAI1_TXD5__AUDIOMIX_SAI1_TX_DATA05 0xd6 + MX8MP_IOMUXC_SAI1_TXD6__AUDIOMIX_SAI1_TX_DATA06 0xd6 + MX8MP_IOMUXC_SAI1_TXD7__AUDIOMIX_SAI1_TX_DATA07 0xd6 + >; + }; + + pinctrl_sai3: sai3grp { + fsl,pins = < + MX8MP_IOMUXC_SAI3_MCLK__AUDIOMIX_SAI3_MCLK 0xd6 + MX8MP_IOMUXC_SAI3_RXC__AUDIOMIX_SAI3_RX_BCLK 0xd6 + MX8MP_IOMUXC_SAI3_RXFS__AUDIOMIX_SAI3_RX_SYNC 0xd6 + MX8MP_IOMUXC_SAI3_RXD__AUDIOMIX_SAI3_RX_DATA00 0xd6 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX8MP_IOMUXC_UART1_RXD__UART1_DCE_RX 0x140 + MX8MP_IOMUXC_UART1_TXD__UART1_DCE_TX 0x140 + MX8MP_IOMUXC_UART3_RXD__UART1_DCE_CTS 0x140 + MX8MP_IOMUXC_UART3_TXD__UART1_DCE_RTS 0x140 + >; + }; + + pinctrl_uart2: uart2grp { + fsl,pins = < + MX8MP_IOMUXC_UART2_RXD__UART2_DCE_RX 0x140 + MX8MP_IOMUXC_UART2_TXD__UART2_DCE_TX 0x140 + >; + }; + + pinctrl_uart3: uart3grp { + fsl,pins = < + MX8MP_IOMUXC_ECSPI1_SCLK__UART3_DCE_RX 0x140 + MX8MP_IOMUXC_ECSPI1_MOSI__UART3_DCE_TX 0x140 + MX8MP_IOMUXC_ECSPI1_SS0__UART3_DCE_RTS 0x140 + MX8MP_IOMUXC_ECSPI1_MISO__UART3_DCE_CTS 0x140 + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX8MP_IOMUXC_SD1_CLK__USDHC1_CLK 0x190 + MX8MP_IOMUXC_SD1_CMD__USDHC1_CMD 0x1d0 + MX8MP_IOMUXC_SD1_DATA0__USDHC1_DATA0 0x1d0 + MX8MP_IOMUXC_SD1_DATA1__USDHC1_DATA1 0x1d0 + MX8MP_IOMUXC_SD1_DATA2__USDHC1_DATA2 0x1d0 + MX8MP_IOMUXC_SD1_DATA3__USDHC1_DATA3 0x1d0 + >; + }; + + pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { + fsl,pins = < + MX8MP_IOMUXC_SD1_CLK__USDHC1_CLK 0x194 + MX8MP_IOMUXC_SD1_CMD__USDHC1_CMD 0x1d4 + MX8MP_IOMUXC_SD1_DATA0__USDHC1_DATA0 0x1d4 + MX8MP_IOMUXC_SD1_DATA1__USDHC1_DATA1 0x1d4 + MX8MP_IOMUXC_SD1_DATA2__USDHC1_DATA2 0x1d4 + MX8MP_IOMUXC_SD1_DATA3__USDHC1_DATA3 0x1d4 + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { + fsl,pins = < + MX8MP_IOMUXC_SD1_CLK__USDHC1_CLK 0x196 + MX8MP_IOMUXC_SD1_CMD__USDHC1_CMD 0x1d6 + MX8MP_IOMUXC_SD1_DATA0__USDHC1_DATA0 0x1d6 + MX8MP_IOMUXC_SD1_DATA1__USDHC1_DATA1 0x1d6 + MX8MP_IOMUXC_SD1_DATA2__USDHC1_DATA2 0x1d6 + MX8MP_IOMUXC_SD1_DATA3__USDHC1_DATA3 0x1d6 + >; + }; + + pinctrl_usdhc2_gpio: usdhc2gpiogrp { + fsl,pins = < + MX8MP_IOMUXC_SD2_CD_B__GPIO2_IO12 0x1c4 + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX8MP_IOMUXC_SD2_CLK__USDHC2_CLK 0x190 + MX8MP_IOMUXC_SD2_CMD__USDHC2_CMD 0x1d0 + MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 0x1d0 + MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d0 + MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d0 + MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d0 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0xc0 + >; + }; + + pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { + fsl,pins = < + MX8MP_IOMUXC_SD2_CLK__USDHC2_CLK 0x194 + MX8MP_IOMUXC_SD2_CMD__USDHC2_CMD 0x1d4 + MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 0x1d4 + MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d4 + MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d4 + MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d4 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0xc0 + >; + }; + + pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { + fsl,pins = < + MX8MP_IOMUXC_SD2_CLK__USDHC2_CLK 0x196 + MX8MP_IOMUXC_SD2_CMD__USDHC2_CMD 0x1d6 + MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 0x1d6 + MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d6 + MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d6 + MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d6 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0xc0 + >; + }; + + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX8MP_IOMUXC_NAND_WE_B__USDHC3_CLK 0x190 + MX8MP_IOMUXC_NAND_WP_B__USDHC3_CMD 0x1d0 + MX8MP_IOMUXC_NAND_DATA04__USDHC3_DATA0 0x1d0 + MX8MP_IOMUXC_NAND_DATA05__USDHC3_DATA1 0x1d0 + MX8MP_IOMUXC_NAND_DATA06__USDHC3_DATA2 0x1d0 + MX8MP_IOMUXC_NAND_DATA07__USDHC3_DATA3 0x1d0 + MX8MP_IOMUXC_NAND_RE_B__USDHC3_DATA4 0x1d0 + MX8MP_IOMUXC_NAND_CE2_B__USDHC3_DATA5 0x1d0 + MX8MP_IOMUXC_NAND_CE3_B__USDHC3_DATA6 0x1d0 + MX8MP_IOMUXC_NAND_CLE__USDHC3_DATA7 0x1d0 + MX8MP_IOMUXC_NAND_CE1_B__USDHC3_STROBE 0x190 + >; + }; + + pinctrl_usdhc3_100mhz: usdhc3-100mhzgrp { + fsl,pins = < + MX8MP_IOMUXC_NAND_WE_B__USDHC3_CLK 0x194 + MX8MP_IOMUXC_NAND_WP_B__USDHC3_CMD 0x1d4 + MX8MP_IOMUXC_NAND_DATA04__USDHC3_DATA0 0x1d4 + MX8MP_IOMUXC_NAND_DATA05__USDHC3_DATA1 0x1d4 + MX8MP_IOMUXC_NAND_DATA06__USDHC3_DATA2 0x1d4 + MX8MP_IOMUXC_NAND_DATA07__USDHC3_DATA3 0x1d4 + MX8MP_IOMUXC_NAND_RE_B__USDHC3_DATA4 0x1d4 + MX8MP_IOMUXC_NAND_CE2_B__USDHC3_DATA5 0x1d4 + MX8MP_IOMUXC_NAND_CE3_B__USDHC3_DATA6 0x1d4 + MX8MP_IOMUXC_NAND_CLE__USDHC3_DATA7 0x1d4 + MX8MP_IOMUXC_NAND_CE1_B__USDHC3_STROBE 0x194 + >; + }; + + pinctrl_usdhc3_200mhz: usdhc3-200mhzgrp { + fsl,pins = < + MX8MP_IOMUXC_NAND_WE_B__USDHC3_CLK 0x196 + MX8MP_IOMUXC_NAND_WP_B__USDHC3_CMD 0x1d6 + MX8MP_IOMUXC_NAND_DATA04__USDHC3_DATA0 0x1d6 + MX8MP_IOMUXC_NAND_DATA05__USDHC3_DATA1 0x1d6 + MX8MP_IOMUXC_NAND_DATA06__USDHC3_DATA2 0x1d6 + MX8MP_IOMUXC_NAND_DATA07__USDHC3_DATA3 0x1d6 + MX8MP_IOMUXC_NAND_RE_B__USDHC3_DATA4 0x1d6 + MX8MP_IOMUXC_NAND_CE2_B__USDHC3_DATA5 0x1d6 + MX8MP_IOMUXC_NAND_CE3_B__USDHC3_DATA6 0x1d6 + MX8MP_IOMUXC_NAND_CLE__USDHC3_DATA7 0x1d6 + MX8MP_IOMUXC_NAND_CE1_B__USDHC3_STROBE 0x196 + >; + }; + + pinctrl_wdog: wdoggrp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO02__WDOG1_WDOG_B 0xc6 + >; + }; + + pinctrl_xcvr: xcvrgrp { + fsl,pins = < + MX8MP_IOMUXC_SPDIF_EXT_CLK__AUDIOMIX_SPDIF1_EXT_CLK 0xd6 + MX8MP_IOMUXC_SPDIF_RX__AUDIOMIX_SPDIF1_IN 0xd6 + MX8MP_IOMUXC_SPDIF_TX__AUDIOMIX_SPDIF1_OUT 0xd6 + >; + }; +}; + +&lcdif3 { + status = "okay"; +}; + +&micfil { + assigned-clocks = <&clk IMX8MP_CLK_PDM>; + assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL1_OUT>; + assigned-clock-rates = <196608000>; + #sound-dai-cells = <0>; + pinctrl-0 = <&pinctrl_pdm>; + pinctrl-names = "default"; + status = "okay"; +}; + +&pwm1 { + pinctrl-0 = <&pinctrl_pwm1>; + pinctrl-names = "default"; + status = "okay"; +}; + +&pwm2 { + pinctrl-0 = <&pinctrl_pwm2>; + pinctrl-names = "default"; + status = "okay"; +}; + +&pwm4 { + pinctrl-0 = <&pinctrl_pwm4>; + pinctrl-names = "default"; + status = "okay"; +}; + +&sai1 { + clocks = <&audio_blk_ctrl IMX8MP_CLK_AUDIOMIX_SAI1_IPG>, <&clk IMX8MP_CLK_DUMMY>, + <&audio_blk_ctrl IMX8MP_CLK_AUDIOMIX_SAI1_MCLK1>, <&clk IMX8MP_CLK_DUMMY>, + <&clk IMX8MP_CLK_DUMMY>, <&clk IMX8MP_AUDIO_PLL1_OUT>, + <&clk IMX8MP_AUDIO_PLL2_OUT>; + clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3", "pll8k", "pll11k"; + assigned-clocks = <&clk IMX8MP_CLK_SAI1>; + assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL1_OUT>; + assigned-clock-rates = <49152000>; + dmas = <&sdma2 0 25 0>, <&sdma2 1 25 0>; + #sound-dai-cells = <0>; + pinctrl-0 = <&pinctrl_sai1>; + pinctrl-names = "default"; + fsl,dataline = <2 0xff 0xff>; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + +&sai3 { + clocks = <&audio_blk_ctrl IMX8MP_CLK_AUDIOMIX_SAI3_IPG>, <&clk IMX8MP_CLK_DUMMY>, + <&audio_blk_ctrl IMX8MP_CLK_AUDIOMIX_SAI3_MCLK1>, <&clk IMX8MP_CLK_DUMMY>, + <&clk IMX8MP_CLK_DUMMY>, <&clk IMX8MP_AUDIO_PLL1_OUT>, + <&clk IMX8MP_AUDIO_PLL2_OUT>; + clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3", "pll8k", "pll11k"; + assigned-clocks = <&clk IMX8MP_CLK_SAI3>; + assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL1_OUT>; + assigned-clock-rates = <49152000>; + #sound-dai-cells = <0>; + pinctrl-0 = <&pinctrl_sai3>; + pinctrl-names = "default"; + fsl,sai-asynchronous; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + +&sdma2 { + status = "okay"; +}; + +&snvs_pwrkey { + status = "okay"; +}; + +&uart1 { + assigned-clocks = <&clk IMX8MP_CLK_UART1>; + assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_80M>; + pinctrl-0 = <&pinctrl_uart1>; + pinctrl-names = "default"; + uart-has-rtscts; + status = "okay"; +}; + +&uart2 { + pinctrl-0 = <&pinctrl_uart2>; + pinctrl-names = "default"; + status = "okay"; +}; + +&uart3 { + assigned-clocks = <&clk IMX8MP_CLK_UART3>; + assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_80M>; + pinctrl-0 = <&pinctrl_uart3>; + pinctrl-names = "default"; + uart-has-rtscts; + status = "okay"; +}; + +&usdhc1 { + assigned-clocks = <&clk IMX8MP_CLK_USDHC1>; + assigned-clock-rates = <400000000>; + bus-width = <4>; + non-removable; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + status = "okay"; +}; + +&usdhc2 { + assigned-clocks = <&clk IMX8MP_CLK_USDHC2>; + assigned-clock-rates = <400000000>; + bus-width = <4>; + cd-gpios = <&gpio2 12 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>; + pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + vmmc-supply = <®_usdhc2_vmmc>; + status = "okay"; +}; + +&usdhc3 { + assigned-clocks = <&clk IMX8MP_CLK_USDHC3>; + assigned-clock-rates = <400000000>; + bus-width = <8>; + non-removable; + pinctrl-0 = <&pinctrl_usdhc3>; + pinctrl-1 = <&pinctrl_usdhc3_100mhz>; + pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + status = "okay"; +}; + +&wdog1 { + pinctrl-0 = <&pinctrl_wdog>; + pinctrl-names = "default"; + fsl,ext-reset-output; + status = "okay"; +}; + +&xcvr { + #sound-dai-cells = <0>; + pinctrl-0 = <&pinctrl_xcvr>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-aristainetos3a-som-v1.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-aristainetos3a-som-v1.dtsi index f654d866e58c..e7666e54310b 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-aristainetos3a-som-v1.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-aristainetos3a-som-v1.dtsi @@ -903,7 +903,7 @@ pinctrl_pmic: aristainetos3-pmic-grp { fsl,pins = < - MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x41 + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x1c0 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-beacon-kit.dts b/arch/arm64/boot/dts/freescale/imx8mp-beacon-kit.dts index 31c33acb560c..385aa6bae520 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-beacon-kit.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-beacon-kit.dts @@ -266,8 +266,7 @@ #size-cells = <0>; ethphy1: ethernet-phy@3 { - compatible = "ethernet-phy-id0022.1640", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <3>; reset-gpios = <&gpio4 18 GPIO_ACTIVE_LOW>; reset-assert-us = <10000>; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-beacon-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-beacon-som.dtsi index 6a62cb32e22e..1007f7db85e9 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-beacon-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-beacon-som.dtsi @@ -60,8 +60,7 @@ #size-cells = <0>; ethphy0: ethernet-phy@3 { - compatible = "ethernet-phy-id0022.1640", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <3>; reset-gpios = <&gpio4 10 GPIO_ACTIVE_LOW>; interrupt-parent = <&gpio1>; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-cubox-m.dts b/arch/arm64/boot/dts/freescale/imx8mp-cubox-m.dts index 8290f187b79f..7bc213499f09 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-cubox-m.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-cubox-m.dts @@ -68,7 +68,7 @@ regulator-name = "vmmc"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; - gpio = <&gpio2 19 GPIO_ACTIVE_HIGH>; + gpio = <&gpio2 19 GPIO_ACTIVE_LOW>; startup-delay-us = <250>; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-data-modul-edm-sbc.dts b/arch/arm64/boot/dts/freescale/imx8mp-data-modul-edm-sbc.dts index 7e46537a22a0..cb28cf1cdd23 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-data-modul-edm-sbc.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-data-modul-edm-sbc.dts @@ -1001,7 +1001,7 @@ pinctrl_pmic: pmic-grp { fsl,pins = < /* PMIC_nINT */ - MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x40000090 + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x1c0 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-debix-model-a.dts b/arch/arm64/boot/dts/freescale/imx8mp-debix-model-a.dts index 9422beee30b2..201cf7f5eb0e 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-debix-model-a.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-debix-model-a.dts @@ -440,7 +440,7 @@ pinctrl_pmic: pmicirqgrp { fsl,pins = < - MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x41 + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x1c0 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-debix-som-a-bmb-08.dts b/arch/arm64/boot/dts/freescale/imx8mp-debix-som-a-bmb-08.dts index 04619a722906..1471ff361b54 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-debix-som-a-bmb-08.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-debix-som-a-bmb-08.dts @@ -499,7 +499,7 @@ pinctrl_pmic: pmicgrp { fsl,pins = < - MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x41 + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x1c0 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-debix-som-a.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-debix-som-a.dtsi index 91094c227744..b31e8fe95ca7 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-debix-som-a.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-debix-som-a.dtsi @@ -241,7 +241,7 @@ pinctrl_pmic: pmicgrp { fsl,pins = < - MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x41 + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x1c0 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-dhcom-pdk3.dts b/arch/arm64/boot/dts/freescale/imx8mp-dhcom-pdk3.dts index ef012e8365b1..6ad824a7e07e 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-dhcom-pdk3.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-dhcom-pdk3.dts @@ -345,7 +345,7 @@ pinctrl_ptn5150: ptn5150grp { fsl,pins = < - MX8MP_IOMUXC_SAI2_TXC__GPIO4_IO25 0x40000000 + MX8MP_IOMUXC_SAI2_TXC__GPIO4_IO25 MX8MP_SION >; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-dhcom-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-dhcom-som.dtsi index f8303b7e2bd2..0a6a60670f76 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-dhcom-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-dhcom-som.dtsi @@ -989,7 +989,7 @@ pinctrl_pmic: dhcom-pmic-grp { fsl,pins = < /* PMIC_nINT */ - MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x40000090 + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x1c0 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-edm-g.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-edm-g.dtsi index 3f1e0837f349..91b87a7248dd 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-edm-g.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-edm-g.dtsi @@ -563,7 +563,7 @@ pinctrl_pmic: pmicirqgrp { fsl,pins = < - MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x41 + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x1c0 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts index b256be710ea1..d0a2bd975a18 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts @@ -134,7 +134,7 @@ enable-active-high; }; - reg_pcie0: regulator-pcie { + reg_m2_wlan: reg_pcie0: regulator-pcie { compatible = "regulator-fixed"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_pcie0_reg>; @@ -250,6 +250,13 @@ }; }; + usdhc1_pwrseq: usdhc1_pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc1_pwrseq>; + reset-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>; + }; + reserved-memory { #address-cells = <2>; #size-cells = <2>; @@ -595,7 +602,8 @@ hdmi@3d { compatible = "adi,adv7535"; - reg = <0x3d>; + reg = <0x3d>, <0x3f>, <0x3b>, <0x38>; + reg-names = "main", "edid", "cec", "packet"; interrupt-parent = <&gpio1>; interrupts = <9 IRQ_TYPE_EDGE_FALLING>; adi,dsi-lanes = <4>; @@ -825,6 +833,10 @@ assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_80M>; uart-has-rtscts; status = "okay"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; }; &uart2 { @@ -858,6 +870,19 @@ status = "okay"; }; +&usdhc1 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + keep-power-in-suspend; + non-removable; + wakeup-source; + mmc-pwrseq = <&usdhc1_pwrseq>; + vmmc-supply = <®_m2_wlan>; + status = "okay"; +}; + &usdhc2 { assigned-clocks = <&clk IMX8MP_CLK_USDHC2>; assigned-clock-rates = <400000000>; @@ -965,33 +990,33 @@ pinctrl_flexcan1: flexcan1grp { fsl,pins = < - MX8MP_IOMUXC_SPDIF_RX__CAN1_RX 0x154 - MX8MP_IOMUXC_SPDIF_TX__CAN1_TX 0x154 + MX8MP_IOMUXC_SPDIF_RX__CAN1_RX 0x154 + MX8MP_IOMUXC_SPDIF_TX__CAN1_TX 0x154 >; }; pinctrl_flexcan2: flexcan2grp { fsl,pins = < - MX8MP_IOMUXC_SAI5_MCLK__CAN2_RX 0x154 - MX8MP_IOMUXC_SAI5_RXD3__CAN2_TX 0x154 + MX8MP_IOMUXC_SAI5_MCLK__CAN2_RX 0x154 + MX8MP_IOMUXC_SAI5_RXD3__CAN2_TX 0x154 >; }; pinctrl_flexcan_phy: flexcanphygrp { fsl,pins = < - MX8MP_IOMUXC_SPDIF_EXT_CLK__GPIO5_IO05 0x154 /* CAN1_STBY */ - MX8MP_IOMUXC_SAI2_MCLK__GPIO4_IO27 0x154 /* CAN2_STBY */ + MX8MP_IOMUXC_SPDIF_EXT_CLK__GPIO5_IO05 0x154 /* CAN1_STBY */ + MX8MP_IOMUXC_SAI2_MCLK__GPIO4_IO27 0x154 /* CAN2_STBY */ >; }; pinctrl_flexspi0: flexspi0grp { fsl,pins = < - MX8MP_IOMUXC_NAND_ALE__FLEXSPI_A_SCLK 0x1c2 - MX8MP_IOMUXC_NAND_CE0_B__FLEXSPI_A_SS0_B 0x82 - MX8MP_IOMUXC_NAND_DATA00__FLEXSPI_A_DATA00 0x82 - MX8MP_IOMUXC_NAND_DATA01__FLEXSPI_A_DATA01 0x82 - MX8MP_IOMUXC_NAND_DATA02__FLEXSPI_A_DATA02 0x82 - MX8MP_IOMUXC_NAND_DATA03__FLEXSPI_A_DATA03 0x82 + MX8MP_IOMUXC_NAND_ALE__FLEXSPI_A_SCLK 0x1c2 + MX8MP_IOMUXC_NAND_CE0_B__FLEXSPI_A_SS0_B 0x82 + MX8MP_IOMUXC_NAND_DATA00__FLEXSPI_A_DATA00 0x82 + MX8MP_IOMUXC_NAND_DATA01__FLEXSPI_A_DATA01 0x82 + MX8MP_IOMUXC_NAND_DATA02__FLEXSPI_A_DATA02 0x82 + MX8MP_IOMUXC_NAND_DATA03__FLEXSPI_A_DATA03 0x82 >; }; @@ -1044,8 +1069,8 @@ pinctrl_i2c5: i2c5grp { fsl,pins = < - MX8MP_IOMUXC_SPDIF_RX__I2C5_SDA 0x400001c2 - MX8MP_IOMUXC_SPDIF_TX__I2C5_SCL 0x400001c2 + MX8MP_IOMUXC_SPDIF_RX__I2C5_SDA 0x400001c2 + MX8MP_IOMUXC_SPDIF_TX__I2C5_SCL 0x400001c2 >; }; @@ -1064,7 +1089,7 @@ pinctrl_pcie0_reg: pcie0reggrp { fsl,pins = < - MX8MP_IOMUXC_SD1_DATA4__GPIO2_IO06 0x40 + MX8MP_IOMUXC_SD1_DATA4__GPIO2_IO06 0x140 >; }; @@ -1164,6 +1189,45 @@ >; }; + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX8MP_IOMUXC_SD1_CLK__USDHC1_CLK 0x190 + MX8MP_IOMUXC_SD1_CMD__USDHC1_CMD 0x1d0 + MX8MP_IOMUXC_SD1_DATA0__USDHC1_DATA0 0x1d0 + MX8MP_IOMUXC_SD1_DATA1__USDHC1_DATA1 0x1d0 + MX8MP_IOMUXC_SD1_DATA2__USDHC1_DATA2 0x1d0 + MX8MP_IOMUXC_SD1_DATA3__USDHC1_DATA3 0x1d0 + >; + }; + + pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { + fsl,pins = < + MX8MP_IOMUXC_SD1_CLK__USDHC1_CLK 0x194 + MX8MP_IOMUXC_SD1_CMD__USDHC1_CMD 0x1d4 + MX8MP_IOMUXC_SD1_DATA0__USDHC1_DATA0 0x1d4 + MX8MP_IOMUXC_SD1_DATA1__USDHC1_DATA1 0x1d4 + MX8MP_IOMUXC_SD1_DATA2__USDHC1_DATA2 0x1d4 + MX8MP_IOMUXC_SD1_DATA3__USDHC1_DATA3 0x1d4 + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { + fsl,pins = < + MX8MP_IOMUXC_SD1_CLK__USDHC1_CLK 0x196 + MX8MP_IOMUXC_SD1_CMD__USDHC1_CMD 0x1d6 + MX8MP_IOMUXC_SD1_DATA0__USDHC1_DATA0 0x1d6 + MX8MP_IOMUXC_SD1_DATA1__USDHC1_DATA1 0x1d6 + MX8MP_IOMUXC_SD1_DATA2__USDHC1_DATA2 0x1d6 + MX8MP_IOMUXC_SD1_DATA3__USDHC1_DATA3 0x1d6 + >; + }; + + pinctrl_usdhc1_pwrseq: usdhc1pwrseqgrp { + fsl,pins = < + MX8MP_IOMUXC_SD1_RESET_B__GPIO2_IO10 0x140 + >; + }; + pinctrl_usdhc2: usdhc2grp { fsl,pins = < MX8MP_IOMUXC_SD2_CLK__USDHC2_CLK 0x190 diff --git a/arch/arm64/boot/dts/freescale/imx8mp-frdm.dts b/arch/arm64/boot/dts/freescale/imx8mp-frdm.dts index 55690f5e53d7..5fb9714215bf 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-frdm.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-frdm.dts @@ -42,6 +42,67 @@ reg = <0x0 0x40000000 0 0xc0000000>, <0x1 0x00000000 0 0x40000000>; }; + + native-hdmi-connector { + compatible = "hdmi-connector"; + label = "HDMI OUT"; + type = "a"; + + port { + hdmi_in: endpoint { + remote-endpoint = <&hdmi_tx_out>; + }; + }; + }; + + reg_usdhc2_vmmc: regulator-sd { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_usdhc2_vmmc>; + regulator-name = "VSD_3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio2 19 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_usb_vbus: regulator-vbus { + compatible = "regulator-fixed"; + regulator-name = "USB_VBUS"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&pcal6416_1 5 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_usdhc1_vmmc: regulator-wifi-vmmc { + compatible = "regulator-fixed"; + regulator-name = "WLAN_EN"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&pcal6416_1 10 GPIO_ACTIVE_HIGH>; + /* + * IW612 wifi chip needs more delay than other wifi chips to complete + * the host interface initialization after power up, otherwise the + * internal state of IW612 may be unstable, resulting in the failure of + * the SDIO3.0 switch voltage. + */ + enable-active-high; + startup-delay-us = <20000>; + }; + + reg_usdhc1_vqmmc: regulator-wifi-vqmmc { + compatible = "regulator-fixed"; + regulator-name = "regulator-wifi-vqmmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + enable-active-high; + }; + + sdio_pwrseq: usdhc1-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>; + }; }; &A53_0 { @@ -60,6 +121,146 @@ cpu-supply = <®_arm>; }; +&eqos { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_eqos>; + phy-mode = "rgmii-id"; + phy-handle = <ðphy0>; + snps,force_thresh_dma_mode; + snps,mtl-tx-config = <&mtl_tx_setup>; + snps,mtl-rx-config = <&mtl_rx_setup>; + status = "okay"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + ethphy0: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_eqos_phy>; + reset-gpios = <&gpio4 22 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <80000>; + }; + }; + + mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <5>; + + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + }; + + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + }; + + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + }; + + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + }; + + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + }; + }; + + mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <5>; + snps,rx-sched-sp; + + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + snps,map-to-dma-channel = <0>; + }; + + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + snps,map-to-dma-channel = <1>; + }; + + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + snps,map-to-dma-channel = <2>; + }; + + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + snps,map-to-dma-channel = <3>; + }; + + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + snps,map-to-dma-channel = <4>; + }; + }; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec>; + phy-mode = "rgmii-id"; + phy-handle = <ðphy1>; + fsl,magic-packet; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + ethphy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec_phy>; + eee-broken-1000t; + reset-gpios = <&gpio4 2 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <80000>; + realtek,aldps-enable; + realtek,clkout-disable; + }; + }; +}; + +&hdmi_pvi { + status = "okay"; +}; + +&hdmi_tx { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi>; + status = "okay"; + + ports { + port@1 { + hdmi_tx_out: endpoint { + remote-endpoint = <&hdmi_in>; + }; + }; + }; +}; + +&hdmi_tx_phy { + status = "okay"; +}; + &i2c1 { clock-frequency = <400000>; pinctrl-names = "default"; @@ -218,6 +419,10 @@ status = "okay"; }; +&lcdif3 { + status = "okay"; +}; + &snvs_pwrkey { status = "okay"; }; @@ -237,6 +442,58 @@ status = "okay"; }; +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb3_phy0 { + status = "okay"; +}; + +&usb3_phy1 { + vbus-supply = <®_usb_vbus>; + status = "okay"; +}; + +&usb_dwc3_1 { + dr_mode = "host"; + status = "okay"; +}; + +&usdhc1 { + assigned-clocks = <&clk IMX8MP_CLK_USDHC1>; + assigned-clock-rates = <200000000>; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + mmc-pwrseq = <&sdio_pwrseq>; + vmmc-supply = <®_usdhc1_vmmc>; + vqmmc-supply = <®_usdhc1_vqmmc>; + bus-width = <4>; + non-removable; + no-sd; + no-mmc; + status = "okay"; +}; + +&usdhc2 { + assigned-clocks = <&clk IMX8MP_CLK_USDHC2>; + assigned-clock-rates = <400000000>; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>; + pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>; + cd-gpios = <&gpio2 12 GPIO_ACTIVE_LOW>; + vmmc-supply = <®_usdhc2_vmmc>; + bus-width = <4>; + status = "okay"; +}; + &usdhc3 { assigned-clocks = <&clk IMX8MP_CLK_USDHC3>; assigned-clock-rates = <400000000>; @@ -250,106 +507,291 @@ }; &iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + pinctrl_eqos: eqosgrp { + fsl,pins = < + MX8MP_IOMUXC_ENET_MDC__ENET_QOS_MDC 0x2 + MX8MP_IOMUXC_ENET_MDIO__ENET_QOS_MDIO 0x2 + MX8MP_IOMUXC_ENET_RD0__ENET_QOS_RGMII_RD0 0x90 + MX8MP_IOMUXC_ENET_RD1__ENET_QOS_RGMII_RD1 0x90 + MX8MP_IOMUXC_ENET_RD2__ENET_QOS_RGMII_RD2 0x90 + MX8MP_IOMUXC_ENET_RD3__ENET_QOS_RGMII_RD3 0x90 + MX8MP_IOMUXC_ENET_RXC__CCM_ENET_QOS_CLOCK_GENERATE_RX_CLK 0x90 + MX8MP_IOMUXC_ENET_RX_CTL__ENET_QOS_RGMII_RX_CTL 0x90 + MX8MP_IOMUXC_ENET_TD0__ENET_QOS_RGMII_TD0 0x16 + MX8MP_IOMUXC_ENET_TD1__ENET_QOS_RGMII_TD1 0x16 + MX8MP_IOMUXC_ENET_TD2__ENET_QOS_RGMII_TD2 0x16 + MX8MP_IOMUXC_ENET_TD3__ENET_QOS_RGMII_TD3 0x16 + MX8MP_IOMUXC_ENET_TX_CTL__ENET_QOS_RGMII_TX_CTL 0x16 + MX8MP_IOMUXC_ENET_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK 0x16 + >; + }; + + pinctrl_eqos_phy: eqosphygrp { + fsl,pins = < + MX8MP_IOMUXC_SAI2_RXC__GPIO4_IO22 0x10 + >; + }; + + pinctrl_fec: fecgrp { + fsl,pins = < + MX8MP_IOMUXC_SAI1_RXD2__ENET1_MDC 0x2 + MX8MP_IOMUXC_SAI1_RXD3__ENET1_MDIO 0x2 + MX8MP_IOMUXC_SAI1_RXD4__ENET1_RGMII_RD0 0x90 + MX8MP_IOMUXC_SAI1_RXD5__ENET1_RGMII_RD1 0x90 + MX8MP_IOMUXC_SAI1_RXD6__ENET1_RGMII_RD2 0x90 + MX8MP_IOMUXC_SAI1_RXD7__ENET1_RGMII_RD3 0x90 + MX8MP_IOMUXC_SAI1_TXC__ENET1_RGMII_RXC 0x90 + MX8MP_IOMUXC_SAI1_TXFS__ENET1_RGMII_RX_CTL 0x90 + MX8MP_IOMUXC_SAI1_TXD0__ENET1_RGMII_TD0 0x16 + MX8MP_IOMUXC_SAI1_TXD1__ENET1_RGMII_TD1 0x16 + MX8MP_IOMUXC_SAI1_TXD2__ENET1_RGMII_TD2 0x16 + MX8MP_IOMUXC_SAI1_TXD3__ENET1_RGMII_TD3 0x16 + MX8MP_IOMUXC_SAI1_TXD4__ENET1_RGMII_TX_CTL 0x16 + MX8MP_IOMUXC_SAI1_TXD5__ENET1_RGMII_TXC 0x16 + >; + }; + + pinctrl_fec_phy: fecphygrp { + fsl,pins = < + MX8MP_IOMUXC_SAI1_RXD0__GPIO4_IO02 0x10 + >; + }; + + pinctrl_hdmi: hdmigrp { + fsl,pins = < + MX8MP_IOMUXC_HDMI_CEC__HDMIMIX_HDMI_CEC 0x10 + >; + }; + + pinctrl_hog: hoggrp { + fsl,pins = < + /* Pin might be required by multiple drivers + * (e. g. HDMI Audio and HDMI TX) + */ + MX8MP_IOMUXC_HDMI_HPD__HDMIMIX_HDMI_HPD 0x40000010 + >; + }; + pinctrl_i2c1: i2c1grp { fsl,pins = < - MX8MP_IOMUXC_I2C1_SCL__I2C1_SCL 0x400001c2 - MX8MP_IOMUXC_I2C1_SDA__I2C1_SDA 0x400001c2 + MX8MP_IOMUXC_I2C1_SCL__I2C1_SCL (MX8MP_DSE_X4 | MX8MP_I2C_DEFAULT) + MX8MP_IOMUXC_I2C1_SDA__I2C1_SDA (MX8MP_DSE_X4 | MX8MP_I2C_DEFAULT) >; }; pinctrl_i2c2: i2c2grp { fsl,pins = < - MX8MP_IOMUXC_I2C2_SCL__I2C2_SCL 0x400001c2 - MX8MP_IOMUXC_I2C2_SDA__I2C2_SDA 0x400001c2 + MX8MP_IOMUXC_I2C2_SCL__I2C2_SCL (MX8MP_DSE_X4 | MX8MP_I2C_DEFAULT) + MX8MP_IOMUXC_I2C2_SDA__I2C2_SDA (MX8MP_DSE_X4 | MX8MP_I2C_DEFAULT) >; }; pinctrl_i2c3: i2c3grp { fsl,pins = < - MX8MP_IOMUXC_I2C3_SCL__I2C3_SCL 0x400001c2 - MX8MP_IOMUXC_I2C3_SDA__I2C3_SDA 0x400001c2 + MX8MP_IOMUXC_I2C3_SCL__I2C3_SCL (MX8MP_DSE_X4 | MX8MP_I2C_DEFAULT) + MX8MP_IOMUXC_I2C3_SDA__I2C3_SDA (MX8MP_DSE_X4 | MX8MP_I2C_DEFAULT) >; }; pinctrl_pmic: pmicgrp { fsl,pins = < - MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x000001c0 + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 + (MX8MP_PULL_UP | MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) >; }; pinctrl_pcal6416_0_int: pcal6416-0-int-grp { fsl,pins = < - MX8MP_IOMUXC_NAND_READY_B__GPIO3_IO16 0x146 + MX8MP_IOMUXC_NAND_READY_B__GPIO3_IO16 + (MX8MP_DSE_X6 | MX8MP_PULL_UP | MX8MP_PULL_ENABLE) >; }; pinctrl_pcal6416_1_int: pcal6416-1-int-grp { fsl,pins = < - MX8MP_IOMUXC_SD1_STROBE__GPIO2_IO11 0x146 + MX8MP_IOMUXC_SD1_STROBE__GPIO2_IO11 + (MX8MP_DSE_X6 | MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + + >; + }; + + pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { + fsl,pins = < + MX8MP_IOMUXC_SD2_RESET_B__GPIO2_IO19 0x40 >; }; pinctrl_uart2: uart2grp { fsl,pins = < - MX8MP_IOMUXC_UART2_RXD__UART2_DCE_RX 0x140 - MX8MP_IOMUXC_UART2_TXD__UART2_DCE_TX 0x140 + MX8MP_IOMUXC_UART2_RXD__UART2_DCE_RX (MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_UART2_TXD__UART2_DCE_TX (MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + >; }; pinctrl_uart3: uart3grp { fsl,pins = < - MX8MP_IOMUXC_ECSPI1_SCLK__UART3_DCE_RX 0x140 - MX8MP_IOMUXC_ECSPI1_MOSI__UART3_DCE_TX 0x140 - MX8MP_IOMUXC_ECSPI1_SS0__UART3_DCE_RTS 0x140 - MX8MP_IOMUXC_ECSPI1_MISO__UART3_DCE_CTS 0x140 + MX8MP_IOMUXC_ECSPI1_SCLK__UART3_DCE_RX (MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ECSPI1_MOSI__UART3_DCE_TX (MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ECSPI1_SS0__UART3_DCE_RTS (MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_ECSPI1_MISO__UART3_DCE_CTS (MX8MP_PULL_UP | MX8MP_PULL_ENABLE) + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX8MP_IOMUXC_SD1_CLK__USDHC1_CLK 0x190 + MX8MP_IOMUXC_SD1_CMD__USDHC1_CMD 0x1d0 + MX8MP_IOMUXC_SD1_DATA0__USDHC1_DATA0 0x1d0 + MX8MP_IOMUXC_SD1_DATA1__USDHC1_DATA1 0x1d0 + MX8MP_IOMUXC_SD1_DATA2__USDHC1_DATA2 0x1d0 + MX8MP_IOMUXC_SD1_DATA3__USDHC1_DATA3 0x1d0 + >; + }; + + pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { + fsl,pins = < + MX8MP_IOMUXC_SD1_CLK__USDHC1_CLK 0x194 + MX8MP_IOMUXC_SD1_CMD__USDHC1_CMD 0x1d4 + MX8MP_IOMUXC_SD1_DATA0__USDHC1_DATA0 0x1d4 + MX8MP_IOMUXC_SD1_DATA1__USDHC1_DATA1 0x1d4 + MX8MP_IOMUXC_SD1_DATA2__USDHC1_DATA2 0x1d4 + MX8MP_IOMUXC_SD1_DATA3__USDHC1_DATA3 0x1d4 + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { + fsl,pins = < + MX8MP_IOMUXC_SD1_CLK__USDHC1_CLK 0x196 + MX8MP_IOMUXC_SD1_CMD__USDHC1_CMD 0x1d6 + MX8MP_IOMUXC_SD1_DATA0__USDHC1_DATA0 0x1d6 + MX8MP_IOMUXC_SD1_DATA1__USDHC1_DATA1 0x1d6 + MX8MP_IOMUXC_SD1_DATA2__USDHC1_DATA2 0x1d6 + MX8MP_IOMUXC_SD1_DATA3__USDHC1_DATA3 0x1d6 + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX8MP_IOMUXC_SD2_CLK__USDHC2_CLK 0x190 + MX8MP_IOMUXC_SD2_CMD__USDHC2_CMD 0x1d0 + MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 0x1d0 + MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d0 + MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d0 + MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d0 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0xc0 + >; + }; + + pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { + fsl,pins = < + MX8MP_IOMUXC_SD2_CLK__USDHC2_CLK 0x194 + MX8MP_IOMUXC_SD2_CMD__USDHC2_CMD 0x1d4 + MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 0x1d4 + MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d4 + MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d4 + MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d4 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0xc0 + >; + }; + + pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { + fsl,pins = < + MX8MP_IOMUXC_SD2_CLK__USDHC2_CLK 0x196 + MX8MP_IOMUXC_SD2_CMD__USDHC2_CMD 0x1d6 + MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 0x1d6 + MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d6 + MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d6 + MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d6 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0xc0 + >; + }; + + pinctrl_usdhc2_gpio: usdhc2gpiogrp { + fsl,pins = < + MX8MP_IOMUXC_SD2_CD_B__GPIO2_IO12 0x1c4 >; }; pinctrl_usdhc3: usdhc3grp { fsl,pins = < - MX8MP_IOMUXC_NAND_WE_B__USDHC3_CLK 0x190 - MX8MP_IOMUXC_NAND_WP_B__USDHC3_CMD 0x1d0 - MX8MP_IOMUXC_NAND_DATA04__USDHC3_DATA0 0x1d0 - MX8MP_IOMUXC_NAND_DATA05__USDHC3_DATA1 0x1d0 - MX8MP_IOMUXC_NAND_DATA06__USDHC3_DATA2 0x1d0 - MX8MP_IOMUXC_NAND_DATA07__USDHC3_DATA3 0x1d0 - MX8MP_IOMUXC_NAND_RE_B__USDHC3_DATA4 0x1d0 - MX8MP_IOMUXC_NAND_CE2_B__USDHC3_DATA5 0x1d0 - MX8MP_IOMUXC_NAND_CE3_B__USDHC3_DATA6 0x1d0 - MX8MP_IOMUXC_NAND_CLE__USDHC3_DATA7 0x1d0 - MX8MP_IOMUXC_NAND_CE1_B__USDHC3_STROBE 0x190 + MX8MP_IOMUXC_NAND_WE_B__USDHC3_CLK + (MX8MP_FSEL_FAST | MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_NAND_WP_B__USDHC3_CMD + (MX8MP_FSEL_FAST | MX8MP_PULL_UP | + MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_NAND_DATA04__USDHC3_DATA0 MX8MP_USDHC_DATA_DEFAULT + MX8MP_IOMUXC_NAND_DATA05__USDHC3_DATA1 MX8MP_USDHC_DATA_DEFAULT + MX8MP_IOMUXC_NAND_DATA06__USDHC3_DATA2 MX8MP_USDHC_DATA_DEFAULT + MX8MP_IOMUXC_NAND_DATA07__USDHC3_DATA3 MX8MP_USDHC_DATA_DEFAULT + MX8MP_IOMUXC_NAND_RE_B__USDHC3_DATA4 MX8MP_USDHC_DATA_DEFAULT + MX8MP_IOMUXC_NAND_CE2_B__USDHC3_DATA5 MX8MP_USDHC_DATA_DEFAULT + MX8MP_IOMUXC_NAND_CE3_B__USDHC3_DATA6 MX8MP_USDHC_DATA_DEFAULT + MX8MP_IOMUXC_NAND_CLE__USDHC3_DATA7 MX8MP_USDHC_DATA_DEFAULT + MX8MP_IOMUXC_NAND_CE1_B__USDHC3_STROBE + (MX8MP_FSEL_FAST | MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) >; }; pinctrl_usdhc3_100mhz: usdhc3-100mhzgrp { fsl,pins = < - MX8MP_IOMUXC_NAND_WE_B__USDHC3_CLK 0x194 - MX8MP_IOMUXC_NAND_WP_B__USDHC3_CMD 0x1d4 - MX8MP_IOMUXC_NAND_DATA04__USDHC3_DATA0 0x1d4 - MX8MP_IOMUXC_NAND_DATA05__USDHC3_DATA1 0x1d4 - MX8MP_IOMUXC_NAND_DATA06__USDHC3_DATA2 0x1d4 - MX8MP_IOMUXC_NAND_DATA07__USDHC3_DATA3 0x1d4 - MX8MP_IOMUXC_NAND_RE_B__USDHC3_DATA4 0x1d4 - MX8MP_IOMUXC_NAND_CE2_B__USDHC3_DATA5 0x1d4 - MX8MP_IOMUXC_NAND_CE3_B__USDHC3_DATA6 0x1d4 - MX8MP_IOMUXC_NAND_CLE__USDHC3_DATA7 0x1d4 - MX8MP_IOMUXC_NAND_CE1_B__USDHC3_STROBE 0x194 + MX8MP_IOMUXC_NAND_WE_B__USDHC3_CLK + (MX8MP_DSE_X2 | MX8MP_FSEL_FAST | + MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_NAND_WP_B__USDHC3_CMD + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_DATA04__USDHC3_DATA0 + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_DATA05__USDHC3_DATA1 + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_DATA06__USDHC3_DATA2 + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_DATA07__USDHC3_DATA3 + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_RE_B__USDHC3_DATA4 + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_CE2_B__USDHC3_DATA5 + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_CE3_B__USDHC3_DATA6 + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_CLE__USDHC3_DATA7 + (MX8MP_DSE_X2 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_CE1_B__USDHC3_STROBE + (MX8MP_DSE_X2 | MX8MP_FSEL_FAST | + MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) >; }; pinctrl_usdhc3_200mhz: usdhc3-200mhzgrp { fsl,pins = < - MX8MP_IOMUXC_NAND_WE_B__USDHC3_CLK 0x196 - MX8MP_IOMUXC_NAND_WP_B__USDHC3_CMD 0x1d6 - MX8MP_IOMUXC_NAND_DATA04__USDHC3_DATA0 0x1d6 - MX8MP_IOMUXC_NAND_DATA05__USDHC3_DATA1 0x1d6 - MX8MP_IOMUXC_NAND_DATA06__USDHC3_DATA2 0x1d6 - MX8MP_IOMUXC_NAND_DATA07__USDHC3_DATA3 0x1d6 - MX8MP_IOMUXC_NAND_RE_B__USDHC3_DATA4 0x1d6 - MX8MP_IOMUXC_NAND_CE2_B__USDHC3_DATA5 0x1d6 - MX8MP_IOMUXC_NAND_CE3_B__USDHC3_DATA6 0x1d6 - MX8MP_IOMUXC_NAND_CLE__USDHC3_DATA7 0x1d6 - MX8MP_IOMUXC_NAND_CE1_B__USDHC3_STROBE 0x196 + MX8MP_IOMUXC_NAND_WE_B__USDHC3_CLK + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST | + MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_NAND_WP_B__USDHC3_CMD + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST | MX8MP_PULL_UP | + MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) + MX8MP_IOMUXC_NAND_DATA04__USDHC3_DATA0 + (MX8MP_DSE_X6 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_DATA05__USDHC3_DATA1 + (MX8MP_DSE_X6 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_DATA06__USDHC3_DATA2 + (MX8MP_DSE_X6 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_DATA07__USDHC3_DATA3 + (MX8MP_DSE_X6 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_RE_B__USDHC3_DATA4 + (MX8MP_DSE_X6 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_CE2_B__USDHC3_DATA5 + (MX8MP_DSE_X6 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_CE3_B__USDHC3_DATA6 + (MX8MP_DSE_X6 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_CLE__USDHC3_DATA7 + (MX8MP_DSE_X6 | MX8MP_USDHC_DATA_DEFAULT) + MX8MP_IOMUXC_NAND_CE1_B__USDHC3_STROBE + (MX8MP_DSE_X6 | MX8MP_FSEL_FAST | + MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) >; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot-panel-dsi-WJ70N3TYJHMNG0.dtso b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot-panel-dsi-WJ70N3TYJHMNG0.dtso new file mode 100644 index 000000000000..6c41f2633f14 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot-panel-dsi-WJ70N3TYJHMNG0.dtso @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2025 Josua Mayer + * + * Overlay for enabling HummingBoard IIoT MIPI-DSI connector + * with Winstar WJ70N3TYJHMNG0 panel. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&{/} { + dsi_backlight: dsi-backlight { + compatible = "gpio-backlight"; + gpios = <&tca6408_u48 3 GPIO_ACTIVE_LOW>; + }; +}; + +&i2c_dsi { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen@41 { + compatible = "ilitek,ili2130"; + reg = <0x41>; + interrupts-extended = <&tca6416_u21 13 IRQ_TYPE_LEVEL_LOW>; + reset-gpios = <&tca6408_u48 6 GPIO_ACTIVE_LOW>; + }; +}; + +&lcdif1 { + status = "okay"; +}; + +&mipi_dsi { + #address-cells = <1>; + #size-cells = <0>; + samsung,esc-clock-frequency = <10000000>; + status = "okay"; + + panel@0 { + /* This is a Winstar panel, but the ronbo panel uses same controls. */ + compatible = "ronbo,rb070d30"; + reg = <0>; + /* reset is active-low but driver inverts it internally */ + reset-gpios = <&tca6408_u48 1 GPIO_ACTIVE_HIGH>; + backlight = <&dsi_backlight>; + power-gpios = <&tca6408_u48 2 GPIO_ACTIVE_HIGH>; + shlr-gpios = <&tca6408_u48 4 GPIO_ACTIVE_LOW>; + updn-gpios = <&tca6408_u48 5 GPIO_ACTIVE_HIGH>; + vcc-lcd-supply = <®_dsi_panel>; + + port { + panel_from_dsim: endpoint { + remote-endpoint = <&mipi_dsi_out>; + }; + }; + }; +}; + +&mipi_dsi_out { + data-lanes = <1 2 3 4>; + remote-endpoint = <&panel_from_dsim>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot-panel-lvds-WF70A8SYJHLNGA.dtso b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot-panel-lvds-WF70A8SYJHLNGA.dtso new file mode 100644 index 000000000000..ca4e7b8fee8e --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot-panel-lvds-WF70A8SYJHLNGA.dtso @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2025 Josua Mayer + * + * Overlay for enabling HummingBoard IIoT LVDS connector + * with Winstar WF70A8SYJHLNGA panel. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&{/} { + lvds_backlight: lvds-backlight { + compatible = "gpio-backlight"; + gpios = <&tca6408_u37 3 GPIO_ACTIVE_LOW>; + }; + + panel-lvds { + compatible = "winstar,wf70a8syjhlnga", "panel-lvds"; + backlight = <&lvds_backlight>; + data-mapping = "vesa-24"; + enable-gpios = <&tca6408_u37 2 GPIO_ACTIVE_HIGH>; + height-mm = <86>; + power-supply = <®_dsi_panel>; + reset-gpios = <&tca6408_u37 1 GPIO_ACTIVE_HIGH>; + width-mm = <154>; + + panel-timing { + /* + * Note: NXP BSP hard-codes 74MHz clock in ldb driver: + * drivers/gpu/drm/imx/imx8mp-ldb.c + * SolidRun BSP carries patch. + */ + clock-frequency = <49500000>; + de-active = <1>; + hactive = <1024>; + hback-porch = <144>; + hfront-porch = <40>; + hsync-active = <0>; + hsync-len = <104>; + vactive = <600>; + vback-porch = <11>; + vfront-porch = <3>; + vsync-active = <1>; + vsync-len = <10>; + }; + + port { + panel_from_lvds: endpoint { + remote-endpoint = <&ldb_lvds_ch0>; + }; + }; + }; +}; + +&i2c_lvds { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen@41 { + compatible = "ilitek,ili2130"; + reg = <0x41>; + interrupts-extended = <&tca6416_u21 13 IRQ_TYPE_LEVEL_LOW>; + reset-gpios = <&tca6408_u37 6 GPIO_ACTIVE_LOW>; + }; +}; + +&lcdif2 { + status = "okay"; +}; + +&ldb_lvds_ch0 { + remote-endpoint = <&panel_from_lvds>; +}; + +&lvds_bridge { + status = "okay"; +}; + +&tca6408_u37 { + lvds-lr-hog { + gpios = <4 GPIO_ACTIVE_HIGH>; + gpio-hog; + line-name = "lvds-l/r"; + output-high; + }; + + lvds-ud-hog { + gpios = <5 GPIO_ACTIVE_HIGH>; + gpio-hog; + line-name = "lvds-u/d"; + output-high; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot-rs485-a.dtso b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot-rs485-a.dtso new file mode 100644 index 000000000000..ae64d6efad9d --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot-rs485-a.dtso @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2025 Josua Mayer + * + * Overlay for enabling HummingBoard IIoT on-board RS485 Port A on connector J5004. + */ + +/dts-v1/; +/plugin/; + +&uart3 { + linux,rs485-enabled-at-boot-time; +}; + +&uart3_rs_232_485_mux { + /* select rs485 */ + idle-state = <1>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot-rs485-b.dtso b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot-rs485-b.dtso new file mode 100644 index 000000000000..2718fa5b2c66 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot-rs485-b.dtso @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2025 Josua Mayer + * + * Overlay for enabling HummingBoard IIoT on-board RS485 Port B on connector J5004. + */ + +/dts-v1/; +/plugin/; + +&uart4 { + linux,rs485-enabled-at-boot-time; +}; + +&uart4_rs_232_485_mux { + /* select rs485 */ + idle-state = <1>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot.dts b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot.dts new file mode 100644 index 000000000000..7c5b77c928d3 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-iiot.dts @@ -0,0 +1,716 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 Yazan Shhady + * Copyright 2025 Josua Mayer + */ + +/dts-v1/; + +#include +#include + +#include "imx8mp-sr-som.dtsi" + +/ { + compatible = "solidrun,imx8mp-hummingboard-iiot", + "solidrun,imx8mp-sr-som", "fsl,imx8mp"; + model = "SolidRun i.MX8MP HummingBoard IIoT"; + + /* power for M.2 B-Key connector (J6) */ + regulator-m2-b { + compatible = "regulator-fixed"; + regulator-name = "m2-b"; + gpios = <&tca6416_u20 5 GPIO_ACTIVE_HIGH>; + regulator-always-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + enable-active-high; + }; + + /* power for M.2 M-Key connector (J4) */ + regulator-m2-m { + compatible = "regulator-fixed"; + regulator-name = "m2-m"; + gpios = <&tca6416_u20 6 GPIO_ACTIVE_HIGH>; + regulator-always-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + enable-active-high; + }; + + /* power for USB-A J27 behind USB Hub Port 3 */ + regulator-vbus-2 { + compatible = "regulator-fixed"; + regulator-name = "vbus2"; + regulator-always-on; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + gpio = <&tca6416_u20 12 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + /* power for USB-A J27 behind USB Hub Port 4 */ + regulator-vbus-3 { + compatible = "regulator-fixed"; + regulator-name = "vbus3"; + regulator-always-on; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + gpio = <&tca6416_u20 13 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + aliases { + /* J10 */ + ethernet0 = &eqos; + /* J11 */ + ethernet1 = &fec; + gpio5 = &tca6408_u48; + gpio6 = &tca6408_u37; + gpio7 = &tca6416_u20; + gpio8 = &tca6416_u21; + i2c6 = &i2c_exp; + i2c7 = &i2c_csi; + i2c8 = &i2c_dsi; + i2c9 = &i2c_lvds; + rtc0 = &carrier_rtc; + rtc1 = &snvs_rtc; + }; + + gpio-keys { + compatible = "gpio-keys"; + + wakeup-event { + interrupts-extended = <&tca6416_u21 11 IRQ_TYPE_EDGE_FALLING>; + label = "m2-m-wakeup"; + wakeup-source; + linux,code = ; + }; + }; + + can_mux: mux-controller-0 { + compatible = "gpio-mux"; + /* + * Mux routes CAN bus signals between SoM connector pins, + * expansion connector (J22) and on-board transceivers using + * two GPIO: + * - IO3: 0 = on-board transceivers, 1 = expansion connector + * - IO4: 0 = J9-55/57/59/61, 1 = J7-12/16 & J9-54/56 + */ + mux-gpios = <&tca6416_u20 3 GPIO_ACTIVE_HIGH>, + <&tca6416_u20 4 GPIO_ACTIVE_HIGH>; + /* default J7-12/16 & J9-54/56 to on-board transceivers */ + idle-state = <2>; + #mux-control-cells = <0>; + }; + + spi_mux: mux-controller-1 { + compatible = "gpio-mux"; + /* default on-board */ + idle-state = <0>; + /* + * Mux switches spi bus between on-board tpm + * and expansion connector (J22). + */ + mux-gpios = <&tca6416_u21 0 GPIO_ACTIVE_HIGH>; + #mux-control-cells = <0>; + }; + + uart3_uart4_b2b_mux: mux-controller-2 { + compatible = "gpio-mux"; + /* default on-board */ + idle-state = <0>; + /* + * Mux switches both uart3 and uart4 tx/rx between expansion + * connector (J22) and on-board rs232/rs485 transceivers + * using one GPIO: 0 = on-board, 1 = connector. + */ + mux-gpios = <&tca6416_u20 0 GPIO_ACTIVE_HIGH>; + #mux-control-cells = <0>; + }; + + uart3_rs_232_485_mux: mux-controller-3 { + compatible = "gpio-mux"; + /* default rs232 */ + idle-state = <0>; + /* + * Mux switches uart3 tx/rx between rs232 and rs485 + * transceivers. using one GPIO: 0 = rs232, 1 = rs485. + */ + mux-gpios = <&tca6416_u20 1 GPIO_ACTIVE_HIGH>; + #mux-control-cells = <0>; + }; + + uart4_rs_232_485_mux: mux-controller-4 { + compatible = "gpio-mux"; + /* default rs232 */ + idle-state = <0>; + /* + * Mux switches uart4 tx/rx between rs232 and rs485 + * transceivers. using one GPIO: 0 = rs232, 1 = rs485. + */ + mux-gpios = <&tca6416_u20 2 GPIO_ACTIVE_HIGH>; + #mux-control-cells = <0>; + }; + + v_1_2: regulator-1-2 { + compatible = "regulator-fixed"; + regulator-name = "1v2"; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + }; + + reg_dsi_panel: regulator-dsi-panel { + compatible = "regulator-fixed"; + regulator-name = "dsi-panel"; + gpios = <&tca6416_u20 15 GPIO_ACTIVE_HIGH>; + regulator-max-microvolt = <11200000>; + regulator-min-microvolt = <11200000>; + enable-active-high; + }; + + vmmc: regulator-mmc { + compatible = "regulator-fixed"; + regulator-name = "vmmc"; + pinctrl-0 = <&vmmc_pins>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + startup-delay-us = <250>; + gpio = <&gpio2 19 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + /* power for USB-A J5003 */ + vbus1: regulator-vbus-1 { + compatible = "regulator-fixed"; + regulator-name = "vbus1"; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + gpio = <&tca6416_u20 14 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + rfkill-m2-b-gnss { + compatible = "rfkill-gpio"; + /* rfkill-gpio inverts internally */ + shutdown-gpios = <&tca6416_u20 10 GPIO_ACTIVE_HIGH>; + label = "m2-b gnss"; + radio-type = "gps"; + }; + + rfkill-m2-b-wwan { + compatible = "rfkill-gpio"; + /* rfkill-gpio inverts internally */ + shutdown-gpios = <&tca6416_u20 9 GPIO_ACTIVE_HIGH>; + label = "m2-b radio"; + radio-type = "wwan"; + }; +}; + +&ecspi2 { + cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>; + num-cs = <1>; + pinctrl-0 = <&ecspi2_pins>; + pinctrl-names = "default"; + status = "okay"; + + ecspi2_muxed: spi@0 { + compatible = "spi-mux"; + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + mux-controls = <&spi_mux>; + /* mux bandwidth is 2GHz, soc max. spi clock is 166MHz */ + spi-max-frequency = <166000000>; + + tpm@0 { + compatible = "infineon,slb9670", "tcg,tpm_tis-spi"; + reg = <0>; + interrupts-extended = <&tca6416_u21 9 IRQ_TYPE_LEVEL_LOW>; + reset-gpios = <&tca6416_u21 1 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; + spi-max-frequency = <43000000>; + }; + }; +}; + +&flexcan1 { + pinctrl-0 = <&can1_pins>; + pinctrl-names = "default"; + status = "okay"; + + can-transceiver { + max-bitrate = <8000000>; + }; +}; + +&flexcan2 { + pinctrl-0 = <&can2_pins>; + pinctrl-names = "default"; + status = "okay"; + + can-transceiver { + max-bitrate = <8000000>; + }; +}; + +&i2c2 { + i2c-mux@70 { + compatible = "nxp,pca9546"; + reg = <0x70>; + #address-cells = <1>; + #size-cells = <0>; + /* + * This reset is open drain, + * but reset core does not support GPIO_OPEN_DRAIN flag. + */ + reset-gpios = <&tca6416_u21 2 GPIO_ACTIVE_LOW>; + + /* channel 0 routed to expansion connector (J22) */ + i2c_exp: i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + /* channel 1 routed to mipi-csi connector (J23) */ + i2c_csi: i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + + /* channel 2 routed to mipi-dsi connector (J25) */ + i2c_dsi: i2c@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + + tca6408_u48: gpio@21 { + compatible = "ti,tca6408"; + reg = <0x21>; + #gpio-cells = <2>; + gpio-controller; + gpio-line-names = "CAM_RST#", "DSI_RESET", + "DSI_STBYB", "DSI_PWM_BL", + "DSI_L/R", "DSI_U/D", + "DSI_CTP_/RST", "CAM_TRIG"; + /* + * reset shared between U37 and U48, to be + * supported once gpio-pca953x switches to + * reset framework. + * + * reset-gpios = <&tca6416_u21 4 + * (GPIO_ACTIVE_LOW|GPIO_PULL_UP|GPIO_OPEN_DRAIN)>; + */ + }; + + }; + + /* channel 2 routed to lvds connector (J24) */ + i2c_lvds: i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + tca6408_u37: gpio@20 { + compatible = "ti,tca6408"; + reg = <0x20>; + #gpio-cells = <2>; + gpio-controller; + gpio-line-names = "SELB", "LVDS_RESET", + "LVDS_STBYB", "LVDS_PWM_BL", + "LVDS_L/R", "LVDS_U/D", + "LVDS_CTP_/RST", ""; + /* + * reset shared between U37 and U48, to be + * supported once gpio-pca953x switches to + * reset framework. + * + * reset-gpios = <&tca6416_u21 4 + * (GPIO_ACTIVE_LOW|GPIO_PULL_UP|GPIO_OPEN_DRAIN)>; + */ + }; + + }; + }; +}; + +&i2c3 { + /* highest i2c clock supported by all peripherals is 400kHz */ + clock-frequency = <400000>; + + tca6416_u20: gpio@20 { + reg = <0x20>; + #gpio-cells = <2>; + gpio-controller; + gpio-line-names = "TCA_INT/EXT_UART", "TCA_UARTA_232/485", + "TCA_UARTB_232/485", "TCA_INT/EXT_CAN", + "TCA_NXP/REN", "TCA_M.2B_3V3_EN", + "TCA_M.2M_3V3_EN", "TCA_M.2M_RESET#", + "TCA_M.2B_RESET#", "TCA_M.2B_W_DIS#", + "TCA_M.2B_GPS_EN#", "TCA_USB-HUB_RST#", + "TCA_USB_HUB3_PWR_EN", "TCA_USB_HUB4_PWR_EN", + "TCA_USB1_PWR_EN", "TCA_VIDEO_PWR_EN"; + /* + * This is a TI TCAL6416 using same programming model as + * NXP PCAL6416, not to be confused with TI TCA6416. + */ + compatible = "nxp,pcal6416"; + + m2-b-reset-hog { + gpios = <8 GPIO_ACTIVE_LOW>; + gpio-hog; + line-name = "m2-b-reset"; + output-low; + }; + }; + + tca6416_u21: gpio@21 { + reg = <0x21>; + #interrupt-cells = <2>; + interrupt-controller; + #gpio-cells = <2>; + gpio-controller; + gpio-line-names = "TCA_SPI_TPM/EXT", "TCA_TPM_RST#", + "TCA_I2C_RST", "TCA_RS232_SHTD#", + "TCA_LCD_I2C_RST", "TCA_DIG_OUT1", + "TCA_bDIG_IN1", "TCA_SENS_INT", + "TCA_ALERT#", "TCA_TPM_PIRQ#", + "TCA_RTC_INT", "TCA_M.2M_WAKW_ON_LAN", + "TCA_M.2M_CLKREQ#", "TCA_LVDS_INT#", + "", "TCA_POE_AT"; + interrupts-extended = <&gpio1 15 IRQ_TYPE_EDGE_FALLING>; + pinctrl-0 = <&tca6416_u21_int_pins>; + pinctrl-names = "default"; + /* + * This is a TI TCAL6416 using same programming model as + * NXP PCAL6416, not to be confused with TI TCA6416. + */ + compatible = "nxp,pcal6416"; + + lcd-i2c-reset-hog { + gpios = <4 (GPIO_ACTIVE_LOW|GPIO_PULL_UP|GPIO_OPEN_DRAIN)>; + line-name = "lcd-i2c-reset"; + output-low; + /* + * reset shared between U37 and U48, to be + * supported once gpio-pca953x switches to + * reset framework. + */ + gpio-hog; + }; + + m2-m-clkreq-hog { + gpios = <12 GPIO_ACTIVE_LOW>; + gpio-hog; + input; + line-name = "m2-m-clkreq"; + }; + + rs232_shutdown: rs232-shutdown-hog { + gpios = <3 GPIO_ACTIVE_LOW>; + gpio-hog; + line-name = "rs232-shutdown"; + output-low; + }; + }; + + led-controller@30 { + compatible = "ti,lp5562"; + reg = <0x30>; + #address-cells = <1>; + #size-cells = <0>; + /* use internal clock, could use external generated by rtc */ + clock-mode = /bits/ 8 <1>; + + multi-led@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + color = ; + label = "D7"; + + led@0 { + reg = <0x0>; + color = ; + led-cur = /bits/ 8 <0x32>; + max-cur = /bits/ 8 <0x64>; + }; + + led@1 { + reg = <0x1>; + color = ; + led-cur = /bits/ 8 <0x19>; + max-cur = /bits/ 8 <0x32>; + }; + + led@2 { + reg = <0x2>; + color = ; + led-cur = /bits/ 8 <0x19>; + max-cur = /bits/ 8 <0x32>; + }; + }; + + led@3 { + reg = <0x3>; + chan-name = "D8"; + color = ; + label = "D8"; + led-cur = /bits/ 8 <0x19>; + max-cur = /bits/ 8 <0x64>; + }; + }; + + light-sensor@44 { + compatible = "isil,isl29023"; + reg = <0x44>; + /* IRQ shared between accelerometer, light-sensor and Tamper input (J5007) */ + interrupts-extended = <&tca6416_u21 7 IRQ_TYPE_EDGE_FALLING>; + }; + + accelerometer@53 { + compatible = "adi,adxl345"; + reg = <0x53>; + /* IRQ shared between accelerometer, light-sensor and Tamper input (J5007) */ + interrupts-extended = <&tca6416_u21 7 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "INT1"; + }; + + carrier_eeprom: eeprom@57 { + compatible = "atmel,24c02"; + reg = <0x57>; + pagesize = <8>; + }; + + carrier_rtc: rtc@69 { + compatible = "abracon,ab1805"; + reg = <0x69>; + abracon,tc-diode = "schottky"; + abracon,tc-resistor = <3>; + /* + * AM1805 RTC used on this board has only nTIRQ pins wired, + * which is for countdown timer irqs only. + * Driver does not support this, disable for now. + * + * interrupts-extended = <&tca6416_u21 10 IRQ_TYPE_EDGE_FALLING>; + */ + }; +}; + +&iomuxc { + can1_pins: pinctrl-can1-grp { + fsl,pins = < + MX8MP_IOMUXC_SPDIF_RX__CAN1_RX 0x154 + MX8MP_IOMUXC_SPDIF_TX__CAN1_TX 0x154 + >; + }; + + can2_pins: pinctrl-can2-grp { + fsl,pins = < + MX8MP_IOMUXC_SAI5_MCLK__CAN2_RX 0x154 + MX8MP_IOMUXC_SAI5_RXD3__CAN2_TX 0x154 + >; + }; + + ecspi2_pins: pinctrl-ecspi2-grp { + fsl,pins = < + MX8MP_IOMUXC_ECSPI2_SCLK__ECSPI2_SCLK 0x140 + MX8MP_IOMUXC_ECSPI2_MOSI__ECSPI2_MOSI 0x140 + MX8MP_IOMUXC_ECSPI2_MISO__ECSPI2_MISO 0x140 + MX8MP_IOMUXC_ECSPI2_SS0__GPIO5_IO13 0x140 + >; + }; + + tca6416_u21_int_pins: pinctrl-tca6416-u21-int-grp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO15__GPIO1_IO15 0x0 + >; + }; + + /* UARTA */ + uart3_pins: pinctrl-uart3-grp { + fsl,pins = < + MX8MP_IOMUXC_ECSPI1_SCLK__UART3_DCE_RX 0x140 + MX8MP_IOMUXC_ECSPI1_MOSI__UART3_DCE_TX 0x140 + MX8MP_IOMUXC_ECSPI1_SS0__GPIO5_IO09 0x140 + >; + }; + + /* UARTB */ + uart4_pins: pinctrl-uart4-grp { + fsl,pins = < + MX8MP_IOMUXC_UART4_RXD__UART4_DCE_RX 0x140 + MX8MP_IOMUXC_UART4_TXD__UART4_DCE_TX 0x140 + MX8MP_IOMUXC_ECSPI1_MISO__GPIO5_IO08 0x140 + >; + }; + + usdhc2_100mhz_pins: pinctrl-usdhc2-100mhz-grp { + fsl,pins = < + MX8MP_IOMUXC_SD2_CLK__USDHC2_CLK 0x194 + MX8MP_IOMUXC_SD2_CMD__USDHC2_CMD 0x1d4 + MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 0x1d4 + MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d4 + MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d4 + MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d4 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x140 + MX8MP_IOMUXC_SD2_CD_B__USDHC2_CD_B 0x140 + >; + }; + + usdhc2_200mhz_pins: pinctrl-usdhc2-200mhz-grp { + fsl,pins = < + MX8MP_IOMUXC_SD2_CLK__USDHC2_CLK 0x196 + MX8MP_IOMUXC_SD2_CMD__USDHC2_CMD 0x1d6 + MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 0x1d6 + MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d6 + MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d6 + MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d6 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x140 + MX8MP_IOMUXC_SD2_CD_B__USDHC2_CD_B 0x140 + >; + }; + + usdhc2_pins: pinctrl-usdhc2-grp { + fsl,pins = < + MX8MP_IOMUXC_SD2_CLK__USDHC2_CLK 0x190 + MX8MP_IOMUXC_SD2_CMD__USDHC2_CMD 0x1d0 + MX8MP_IOMUXC_SD2_DATA0__USDHC2_DATA0 0x1d0 + MX8MP_IOMUXC_SD2_DATA1__USDHC2_DATA1 0x1d0 + MX8MP_IOMUXC_SD2_DATA2__USDHC2_DATA2 0x1d0 + MX8MP_IOMUXC_SD2_DATA3__USDHC2_DATA3 0x1d0 + MX8MP_IOMUXC_GPIO1_IO04__USDHC2_VSELECT 0x140 + MX8MP_IOMUXC_SD2_CD_B__USDHC2_CD_B 0x140 + >; + }; + + vmmc_pins: pinctrl-vmmc-grp { + fsl,pins = < + MX8MP_IOMUXC_SD2_RESET_B__GPIO2_IO19 0x0 + >; + }; +}; + +&pcie { + reset-gpio = <&tca6416_u20 7 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +/* M.2 M-Key (J4) */ +&pcie_phy { + clocks = <&hsio_blk_ctrl>; + clock-names = "ref"; + fsl,clkreq-unsupported; + fsl,refclk-pad-mode = ; + status = "okay"; +}; + +&phy0 { + leds { + #address-cells = <1>; + #size-cells = <0>; + + /* ADIN1300 LED_0 pin */ + led@0 { + reg = <0>; + color = ; + default-state = "keep"; + function = LED_FUNCTION_LAN; + }; + }; +}; + +&phy1 { + leds { + #address-cells = <1>; + #size-cells = <0>; + + /* ADIN1300 LED_0 pin */ + led@0 { + reg = <0>; + color = ; + default-state = "keep"; + function = LED_FUNCTION_LAN; + }; + }; +}; + +&uart3 { + assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_80M>; + /* select 80MHz parent clock to support maximum baudrate 4Mbps */ + assigned-clocks = <&clk IMX8MP_CLK_UART3>; + pinctrl-0 = <&uart3_pins>; + pinctrl-names = "default"; + rts-gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&uart4 { + assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_80M>; + /* select 80MHz parent clock to support maximum baudrate 4Mbps */ + assigned-clocks = <&clk IMX8MP_CLK_UART4>; + pinctrl-0 = <&uart4_pins>; + pinctrl-names = "default"; + rts-gpios = <&gpio5 8 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&usb3_0 { + status = "okay"; +}; + +&usb3_1 { + status = "okay"; +}; + +&usb3_phy0 { + vbus-supply = <&vbus1>; + fsl,phy-tx-preemp-amp-tune-microamp = <1200>; + status = "okay"; +}; + +&usb3_phy1 { + status = "okay"; +}; + +&usb_dwc3_0 { + dr_mode = "host"; +}; + +&usb_dwc3_1 { + #address-cells = <1>; + #size-cells = <0>; + dr_mode = "host"; + + hub_2_0: hub@1 { + compatible = "usb4b4,6502", "usb4b4,6506"; + reg = <1>; + peer-hub = <&hub_3_0>; + reset-gpios = <&tca6416_u20 11 GPIO_ACTIVE_LOW>; + vdd2-supply = <&v_3_3>; + vdd-supply = <&v_1_2>; + }; + + hub_3_0: hub@2 { + compatible = "usb4b4,6500", "usb4b4,6504"; + reg = <2>; + peer-hub = <&hub_2_0>; + reset-gpios = <&tca6416_u20 11 GPIO_ACTIVE_LOW>; + vdd2-supply = <&v_3_3>; + vdd-supply = <&v_1_2>; + }; +}; + +&usdhc2 { + bus-width = <4>; + cap-power-off-card; + full-pwr-cycle; + pinctrl-0 = <&usdhc2_pins>; + pinctrl-1 = <&usdhc2_100mhz_pins>; + pinctrl-2 = <&usdhc2_200mhz_pins>; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + vmmc-supply = <&vmmc>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-pulse-common.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-pulse-common.dtsi index fa7cb9759d01..0b4e5f300eb1 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-pulse-common.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-pulse-common.dtsi @@ -73,7 +73,7 @@ regulator-name = "vmmc"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; - gpio = <&gpio2 19 GPIO_ACTIVE_HIGH>; + gpio = <&gpio2 19 GPIO_ACTIVE_LOW>; startup-delay-us = <250>; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-pulse-mini-hdmi.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-pulse-mini-hdmi.dtsi index 46916ddc0533..0e5f4607c7c1 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-pulse-mini-hdmi.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-hummingboard-pulse-mini-hdmi.dtsi @@ -41,7 +41,7 @@ reg = <0>; adv7535_from_dsim: endpoint { - remote-endpoint = <&dsim_to_adv7535>; + remote-endpoint = <&mipi_dsi_out>; }; }; @@ -71,11 +71,8 @@ &mipi_dsi { samsung,esc-clock-frequency = <10000000>; status = "okay"; +}; - port@1 { - dsim_to_adv7535: endpoint { - remote-endpoint = <&adv7535_from_dsim>; - attach-bridge; - }; - }; +&mipi_dsi_out { + remote-endpoint = <&adv7535_from_dsim>; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-icore-mx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-icore-mx8mp.dtsi index a6319824ea2e..69558ffefa9a 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-icore-mx8mp.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-icore-mx8mp.dtsi @@ -132,7 +132,7 @@ pinctrl_pmic: pmicgrp { fsl,pins = < - MX8MP_IOMUXC_NAND_CE0_B__GPIO3_IO01 0x41 + MX8MP_IOMUXC_NAND_CE0_B__GPIO3_IO01 0x1c0 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-kontron-dl.dtso b/arch/arm64/boot/dts/freescale/imx8mp-kontron-dl.dtso index a3cba41d2b53..41a2bb74f156 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-kontron-dl.dtso +++ b/arch/arm64/boot/dts/freescale/imx8mp-kontron-dl.dtso @@ -7,6 +7,7 @@ /plugin/; #include +#include #include "imx8mp-pinfunc.h" &{/} { @@ -77,12 +78,14 @@ touchscreen@5d { compatible = "goodix,gt928"; reg = <0x5d>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_touch>; interrupt-parent = <&gpio1>; - interrupts = <6 8>; - irq-gpios = <&gpio1 6 0>; + interrupts = <6 IRQ_TYPE_LEVEL_LOW>; + irq-gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>; AVDD28-supply = <®_vcc_panel>; VDDIO-supply = <®_vcc_panel>; - reset-gpios = <&gpio1 7 0>; + reset-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; }; }; @@ -98,6 +101,16 @@ status = "okay"; }; +/* redefine to remove touch controller GPIOs */ +&pinctrl_gpio1 { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO00__GPIO1_IO00 0x19 /* GPIO_A_0 */ + MX8MP_IOMUXC_GPIO1_IO01__GPIO1_IO01 0x19 /* GPIO_A_1 */ + MX8MP_IOMUXC_GPIO1_IO05__GPIO1_IO05 0x19 /* GPIO_A_2 */ + MX8MP_IOMUXC_GPIO1_IO08__GPIO1_IO08 0x19 /* GPIO_A_5 */ + >; +}; + &pwm1 { status = "okay"; }; @@ -108,4 +121,11 @@ MX8MP_IOMUXC_SAI3_RXFS__GPIO4_IO28 0x19 >; }; + + pinctrl_touch: touchgrp { + fsl,pins = < + MX8MP_IOMUXC_GPIO1_IO06__GPIO1_IO06 0x19 + MX8MP_IOMUXC_GPIO1_IO07__GPIO1_IO07 0x150 + >; + }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi index b97bfeb1c30f..bc1a261bb000 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-kontron-osm-s.dtsi @@ -330,6 +330,12 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_rtc>; interrupts-extended = <&gpio3 24 IRQ_TYPE_LEVEL_LOW>; + /* + * While specifying the vdd-supply is normally not strictly necessary, + * here it also makes sure that the PMIC driver enables the level- + * shifter for the RTC before the RTC is probed. + */ + vdd-supply = <®_vdd_3v3>; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-kontron-smarc-eval-carrier.dts b/arch/arm64/boot/dts/freescale/imx8mp-kontron-smarc-eval-carrier.dts index 2173a36ff691..74d620dd06b7 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-kontron-smarc-eval-carrier.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-kontron-smarc-eval-carrier.dts @@ -249,6 +249,5 @@ }; &usdhc2 { - vmmc-supply = <®_vdd_3v3>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-navqp.dts b/arch/arm64/boot/dts/freescale/imx8mp-navqp.dts index 4a4f7c1adc23..9dedb9f11145 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-navqp.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-navqp.dts @@ -356,7 +356,7 @@ pinctrl_pmic: pmicgrp { fsl,pins = < - MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x41 + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x1c0 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-nitrogen-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-nitrogen-som.dtsi index f658309612ef..8465b36d440a 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-nitrogen-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-nitrogen-som.dtsi @@ -296,7 +296,7 @@ pinctrl_pmic: pmicirqgrp { fsl,pins = < - MX8MP_IOMUXC_NAND_ALE__GPIO3_IO00 0x41 + MX8MP_IOMUXC_NAND_ALE__GPIO3_IO00 0x1c0 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-pinfunc.h b/arch/arm64/boot/dts/freescale/imx8mp-pinfunc.h index 16f5899de415..26e7a9428c4c 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-pinfunc.h +++ b/arch/arm64/boot/dts/freescale/imx8mp-pinfunc.h @@ -36,7 +36,7 @@ /* long defaults */ #define MX8MP_USDHC_DATA_DEFAULT (MX8MP_FSEL_FAST | MX8MP_PULL_UP | \ MX8MP_HYS_SCHMITT | MX8MP_PULL_ENABLE) -#define MX8MP_I2C_DEFAULT (MX8MP_DSE_X6 | MX8MP_PULL_UP | MX8MP_HYS_SCHMITT | \ +#define MX8MP_I2C_DEFAULT (MX8MP_PULL_UP | MX8MP_HYS_SCHMITT | \ MX8MP_PULL_ENABLE | MX8MP_SION) /* diff --git a/arch/arm64/boot/dts/freescale/imx8mp-sr-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-sr-som.dtsi index 3cdb0bc0ab72..c3f7daa773ea 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-sr-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-sr-som.dtsi @@ -174,7 +174,7 @@ pinctrl-0 = <&pmic_pins>; pinctrl-names = "default"; interrupt-parent = <&gpio1>; - interrupts = <3 GPIO_ACTIVE_LOW>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; nxp,i2c-lt-enable; regulators { @@ -417,7 +417,7 @@ pmic_pins: pinctrl-pmic-grp { fsl,pins = < - MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x41 + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x1c0 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mp-ras314.dts b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mp-ras314.dts index b7f69c92b774..90d6b5ae215f 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mp-ras314.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mp-ras314.dts @@ -184,6 +184,9 @@ pinctrl-0 = <&pinctrl_eqos>; phy-mode = "rgmii-id"; phy-handle = <ðphy3>; + snps,force_thresh_dma_mode; + snps,mtl-tx-config = <&mtl_tx_setup>; + snps,mtl-rx-config = <&mtl_rx_setup>; status = "okay"; mdio { @@ -209,6 +212,70 @@ ti,clk-output-sel = ; }; }; + + mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <5>; + + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + }; + + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + }; + + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + }; + + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + }; + + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + }; + }; + + mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <5>; + snps,rx-sched-sp; + + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + snps,map-to-dma-channel = <0>; + }; + + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + snps,map-to-dma-channel = <1>; + }; + + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + snps,map-to-dma-channel = <2>; + }; + + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + snps,map-to-dma-channel = <3>; + }; + + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + snps,map-to-dma-channel = <4>; + }; + }; }; &fec { @@ -520,6 +587,7 @@ bluetooth { compatible = "nxp,88w8987-bt"; + vcc-supply = <®_vcc_3v3>; }; }; @@ -848,8 +916,8 @@ pinctrl_uart1: uart1grp { fsl,pins = , , - , - ; + , + ; }; pinctrl_uart1_gpio: uart1gpiogrp { diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl-lvds-g133han01.dtso b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl-lvds-g133han01.dtso index 5058cd9409c7..129b02a69ccf 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl-lvds-g133han01.dtso +++ b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl-lvds-g133han01.dtso @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) /* - * Copyright (c) 2023 TQ-Systems GmbH , + * Copyright (c) 2023-2026 TQ-Systems GmbH , * D-82229 Seefeld, Germany. * Author: Alexander Stein */ @@ -10,10 +10,6 @@ #include -&{/} { - compatible = "tq,imx8mp-tqma8mpql-mba8mpxl", "tq,imx8mp-tqma8mpql", "fsl,imx8mp"; -}; - &backlight_lvds { status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtso b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtso index ea44d605342b..f6aaad91d7f8 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtso +++ b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl-lvds-tm070jvhg33.dtso @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) /* - * Copyright (c) 2022 TQ-Systems GmbH , + * Copyright (c) 2022-2026 TQ-Systems GmbH , * D-82229 Seefeld, Germany. * Author: Alexander Stein */ @@ -8,10 +8,6 @@ /dts-v1/; /plugin/; -&{/} { - compatible = "tq,imx8mp-tqma8mpql-mba8mpxl", "tq,imx8mp-tqma8mpql", "fsl,imx8mp"; -}; - &backlight_lvds { status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts index ad49bf85a04d..890d1e525a48 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts @@ -336,6 +336,9 @@ pinctrl-0 = <&pinctrl_eqos>, <&pinctrl_eqos_phy>; phy-mode = "rgmii-id"; phy-handle = <ðphy3>; + snps,force_thresh_dma_mode; + snps,mtl-tx-config = <&mtl_tx_setup>; + snps,mtl-rx-config = <&mtl_rx_setup>; status = "okay"; mdio { @@ -359,6 +362,70 @@ interrupts = <3 IRQ_TYPE_LEVEL_LOW>; }; }; + + mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <5>; + + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + }; + + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + }; + + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + }; + + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + }; + + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + }; + }; + + mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <5>; + snps,rx-sched-sp; + + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + snps,map-to-dma-channel = <0>; + }; + + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + snps,map-to-dma-channel = <1>; + }; + + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + snps,map-to-dma-channel = <2>; + }; + + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + snps,map-to-dma-channel = <3>; + }; + + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + snps,map-to-dma-channel = <4>; + }; + }; }; &fec { diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av101hdt-a10.dtso b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av101hdt-a10.dtso index e3965caca6be..c6fc5d5b1e5f 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av101hdt-a10.dtso +++ b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106-av101hdt-a10.dtso @@ -77,6 +77,7 @@ compatible = "usb-c-connector"; data-role = "host"; pd-disable; + typec-power-opmode = "default"; vbus-supply = <®_vbus>; port { diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106.dts b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106.dts index 399230144ce3..87b20b856458 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81-moduline-display-106.dts @@ -302,36 +302,36 @@ pinctrl_i2c2: i2c2grp { fsl,pins = < MX8MP_IOMUXC_I2C2_SCL__I2C2_SCL - MX8MP_I2C_DEFAULT + (MX8MP_DSE_X6 | MX8MP_I2C_DEFAULT) MX8MP_IOMUXC_I2C2_SDA__I2C2_SDA - MX8MP_I2C_DEFAULT + (MX8MP_DSE_X6 | MX8MP_I2C_DEFAULT) >; }; pinctrl_i2c2_gpio: i2c2-gpiogrp { fsl,pins = < MX8MP_IOMUXC_I2C2_SCL__GPIO5_IO16 - MX8MP_I2C_DEFAULT + (MX8MP_DSE_X6 | MX8MP_I2C_DEFAULT) MX8MP_IOMUXC_I2C2_SDA__GPIO5_IO17 - MX8MP_I2C_DEFAULT + (MX8MP_DSE_X6 | MX8MP_I2C_DEFAULT) >; }; pinctrl_i2c4: i2c4grp { fsl,pins = < MX8MP_IOMUXC_ECSPI2_MISO__I2C4_SCL - MX8MP_I2C_DEFAULT + (MX8MP_DSE_X6 | MX8MP_I2C_DEFAULT) MX8MP_IOMUXC_ECSPI2_SS0__I2C4_SDA - MX8MP_I2C_DEFAULT + (MX8MP_DSE_X6 | MX8MP_I2C_DEFAULT) >; }; pinctrl_i2c4_gpio: i2c4-gpiogrp { fsl,pins = < MX8MP_IOMUXC_ECSPI2_MISO__GPIO5_IO12 - MX8MP_I2C_DEFAULT + (MX8MP_DSE_X6 | MX8MP_I2C_DEFAULT) MX8MP_IOMUXC_ECSPI2_SS0__GPIO5_IO13 - MX8MP_I2C_DEFAULT + (MX8MP_DSE_X6 | MX8MP_I2C_DEFAULT) >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81.dtsi index 761ee046eb72..bf49ae942d41 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-tx8p-ml81.dtsi @@ -425,18 +425,18 @@ pinctrl_i2c1: i2c1grp { fsl,pins = < MX8MP_IOMUXC_I2C1_SCL__I2C1_SCL - MX8MP_I2C_DEFAULT + (MX8MP_DSE_X6 | MX8MP_I2C_DEFAULT) MX8MP_IOMUXC_I2C1_SDA__I2C1_SDA - MX8MP_I2C_DEFAULT + (MX8MP_DSE_X6 | MX8MP_I2C_DEFAULT) >; }; pinctrl_i2c1_gpio: i2c1-gpiogrp { fsl,pins = < MX8MP_IOMUXC_I2C1_SCL__GPIO5_IO14 - MX8MP_I2C_DEFAULT + (MX8MP_DSE_X6 | MX8MP_I2C_DEFAULT) MX8MP_IOMUXC_I2C1_SDA__GPIO5_IO15 - MX8MP_I2C_DEFAULT + (MX8MP_DSE_X6 | MX8MP_I2C_DEFAULT) >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-ultra-mach-sbc.dts b/arch/arm64/boot/dts/freescale/imx8mp-ultra-mach-sbc.dts index 9ecec1a41878..3e6f9c88cc20 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-ultra-mach-sbc.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-ultra-mach-sbc.dts @@ -275,7 +275,7 @@ reg = <0x25>; pinctrl-0 = <&pinctrl_pmic>; interrupt-parent = <&gpio1>; - interrupts = <3 GPIO_ACTIVE_LOW>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; /* * i.MX 8M Plus Data Sheet for Consumer Products @@ -739,7 +739,7 @@ pinctrl_pmic: pmic-grp { fsl,pins = < - MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x40 /* #PMIC_INT */ + MX8MP_IOMUXC_GPIO1_IO03__GPIO1_IO03 0x1c0 /* #PMIC_INT */ >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi index 9b2b3a9bf9e8..90d7bb8f5619 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi @@ -310,8 +310,7 @@ pmu { compatible = "arm,cortex-a53-pmu"; - interrupts = ; + interrupts = ; }; psci { @@ -397,10 +396,10 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; clock-frequency = <8000000>; arm,no-tick-in-suspend; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mq-tqma8mq-mba8mx-lvds-tm070jvhg33.dtso b/arch/arm64/boot/dts/freescale/imx8mq-tqma8mq-mba8mx-lvds-tm070jvhg33.dtso index 306977d6ba0c..78f24f72cc69 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq-tqma8mq-mba8mx-lvds-tm070jvhg33.dtso +++ b/arch/arm64/boot/dts/freescale/imx8mq-tqma8mq-mba8mx-lvds-tm070jvhg33.dtso @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) /* - * Copyright (c) 2019-2023 TQ-Systems GmbH , + * Copyright (c) 2019-2026 TQ-Systems GmbH , * D-82229 Seefeld, Germany. * Author: Alexander Stein */ @@ -10,10 +10,6 @@ #include -&{/} { - compatible = "tq,imx8mq-tqma8mq-mba8mx", "tq,imx8mq-tqma8mq", "fsl,imx8mq"; -}; - &backlight_lvds { status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8qm-mek.dts b/arch/arm64/boot/dts/freescale/imx8qm-mek.dts index dadc136aec6e..011a89d85961 100644 --- a/arch/arm64/boot/dts/freescale/imx8qm-mek.dts +++ b/arch/arm64/boot/dts/freescale/imx8qm-mek.dts @@ -611,9 +611,17 @@ usb_con1: connector { compatible = "usb-c-connector"; label = "USB-C"; - power-role = "source"; + power-role = "dual"; data-role = "dual"; + try-power-role = "sink"; source-pdos = ; + /* + * Set operational current to 0mA as we don't want EN_SNK + * enable 12V VBUS switch when it work as a sink. + */ + sink-pdos = ; + op-sink-microwatt = <0>; + self-powered; ports { #address-cells = <1>; diff --git a/arch/arm64/boot/dts/freescale/imx8qm-ss-hsio.dtsi b/arch/arm64/boot/dts/freescale/imx8qm-ss-hsio.dtsi index bd6e0aa27efe..f2c94cdb682b 100644 --- a/arch/arm64/boot/dts/freescale/imx8qm-ss-hsio.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8qm-ss-hsio.dtsi @@ -20,8 +20,9 @@ ranges = <0x81000000 0 0x00000000 0x4ff80000 0 0x00010000>, <0x82000000 0 0x40000000 0x40000000 0 0x0ff00000>; #interrupt-cells = <1>; - interrupts = ; - interrupt-names = "msi"; + interrupts = , + ; + interrupt-names = "msi", "dma"; #address-cells = <3>; #size-cells = <2>; clocks = <&pciea_lpcg IMX_LPCG_CLK_6>, diff --git a/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts b/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts index 40a0bc9f4e84..623169f7ddb5 100644 --- a/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts +++ b/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts @@ -566,9 +566,17 @@ usb_con1: connector { compatible = "usb-c-connector"; label = "USB-C"; - power-role = "source"; + power-role = "dual"; data-role = "dual"; + try-power-role = "sink"; source-pdos = ; + /* + * Set operational current to 0mA as we don't want EN_SNK + * enable 12V VBUS switch when it work as a sink. + */ + sink-pdos = ; + op-sink-microwatt = <0>; + self-powered; ports { #address-cells = <1>; diff --git a/arch/arm64/boot/dts/freescale/imx8ulp.dtsi b/arch/arm64/boot/dts/freescale/imx8ulp.dtsi index 9b5d98766512..1de3ad60c6aa 100644 --- a/arch/arm64/boot/dts/freescale/imx8ulp.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8ulp.dtsi @@ -86,8 +86,7 @@ pmu { compatible = "arm,cortex-a35-pmu"; interrupt-parent = <&gic>; - interrupts = ; + interrupts = ; interrupt-affinity = <&A35_0>, <&A35_1>; }; diff --git a/arch/arm64/boot/dts/freescale/imx91-11x11-evk.dts b/arch/arm64/boot/dts/freescale/imx91-11x11-evk.dts index 03f460d62f7a..c083b97476a5 100644 --- a/arch/arm64/boot/dts/freescale/imx91-11x11-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx91-11x11-evk.dts @@ -23,6 +23,7 @@ i2c2 = &lpi2c3; mmc0 = &usdhc1; mmc1 = &usdhc2; + mmc2 = &usdhc3; rtc0 = &bbnsm_rtc; serial0 = &lpuart1; serial1 = &lpuart2; @@ -57,6 +58,15 @@ enable-active-high; }; + reg_m2_pwr: regulator-m2-pwr { + compatible = "regulator-fixed"; + regulator-name = "M.2-power"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&pcal6524 13 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + reg_usdhc2_vmmc: regulator-usdhc2 { compatible = "regulator-fixed"; off-on-delay-us = <12000>; @@ -69,6 +79,23 @@ enable-active-high; }; + reg_usdhc3_vmmc: regulator-usdhc3 { + compatible = "regulator-fixed"; + regulator-name = "WLAN_EN"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <®_m2_pwr>; + gpio = <&pcal6524 20 GPIO_ACTIVE_HIGH>; + /* + * IW612 wifi chip needs more delay than other wifi chips to complete + * the host interface initialization after power up, otherwise the + * internal state of IW612 may be unstable, resulting in the failure of + * the SDIO3.0 switch voltage. + */ + startup-delay-us = <20000>; + enable-active-high; + }; + reserved-memory { ranges; #address-cells = <2>; @@ -144,6 +171,11 @@ }; }; }; + + usdhc3_pwrseq: usdhc3_pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&pcal6524 12 GPIO_ACTIVE_LOW>; + }; }; &adc1 { @@ -246,6 +278,12 @@ interrupt-parent = <&gpio3>; pinctrl-0 = <&pinctrl_pcal6524>; pinctrl-names = "default"; + + m2-pcm-level-shifter-hog { + gpio-hog; + gpios = <19 GPIO_ACTIVE_HIGH>; + output-high; + }; }; pmic@25 { @@ -514,6 +552,7 @@ pinctrl-1 = <&pinctrl_usdhc1_100mhz>; pinctrl-2 = <&pinctrl_usdhc1_200mhz>; pinctrl-names = "default", "state_100mhz", "state_200mhz"; + fsl,tuning-step = <1>; status = "okay"; }; @@ -528,6 +567,22 @@ pinctrl-3 = <&pinctrl_usdhc2_sleep>, <&pinctrl_usdhc2_gpio_sleep>; pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep"; vmmc-supply = <®_usdhc2_vmmc>; + fsl,tuning-step = <1>; + status = "okay"; +}; + +&usdhc3 { + bus-width = <4>; + keep-power-in-suspend; + mmc-pwrseq = <&usdhc3_pwrseq>; + non-removable; + pinctrl-0 = <&pinctrl_usdhc3>; + pinctrl-1 = <&pinctrl_usdhc3_100mhz>; + pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + pinctrl-3 = <&pinctrl_usdhc3_sleep>; + pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep"; + vmmc-supply = <®_usdhc3_vmmc>; + wakeup-source; status = "okay"; }; @@ -850,4 +905,47 @@ >; }; + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX91_PAD_SD3_CLK__USDHC3_CLK 0x1582 + MX91_PAD_SD3_CMD__USDHC3_CMD 0x1382 + MX91_PAD_SD3_DATA0__USDHC3_DATA0 0x1382 + MX91_PAD_SD3_DATA1__USDHC3_DATA1 0x1382 + MX91_PAD_SD3_DATA2__USDHC3_DATA2 0x1382 + MX91_PAD_SD3_DATA3__USDHC3_DATA3 0x1382 + >; + }; + + pinctrl_usdhc3_100mhz: usdhc3-100mhzgrp { + fsl,pins = < + MX91_PAD_SD3_CLK__USDHC3_CLK 0x158e + MX91_PAD_SD3_CMD__USDHC3_CMD 0x138e + MX91_PAD_SD3_DATA0__USDHC3_DATA0 0x138e + MX91_PAD_SD3_DATA1__USDHC3_DATA1 0x138e + MX91_PAD_SD3_DATA2__USDHC3_DATA2 0x138e + MX91_PAD_SD3_DATA3__USDHC3_DATA3 0x138e + >; + }; + + pinctrl_usdhc3_200mhz: usdhc3-200mhzgrp { + fsl,pins = < + MX91_PAD_SD3_CLK__USDHC3_CLK 0x15fe + MX91_PAD_SD3_CMD__USDHC3_CMD 0x13fe + MX91_PAD_SD3_DATA0__USDHC3_DATA0 0x13fe + MX91_PAD_SD3_DATA1__USDHC3_DATA1 0x13fe + MX91_PAD_SD3_DATA2__USDHC3_DATA2 0x13fe + MX91_PAD_SD3_DATA3__USDHC3_DATA3 0x13fe + >; + }; + + pinctrl_usdhc3_sleep: usdhc3-sleepgrp { + fsl,pins = < + MX91_PAD_SD3_CLK__GPIO3_IO20 0x31e + MX91_PAD_SD3_CMD__GPIO3_IO21 0x31e + MX91_PAD_SD3_DATA0__GPIO3_IO22 0x31e + MX91_PAD_SD3_DATA1__GPIO3_IO23 0x31e + MX91_PAD_SD3_DATA2__GPIO3_IO24 0x31e + MX91_PAD_SD3_DATA3__GPIO3_IO25 0x31e + >; + }; }; diff --git a/arch/arm64/boot/dts/freescale/imx91-11x11-frdm-s.dts b/arch/arm64/boot/dts/freescale/imx91-11x11-frdm-s.dts new file mode 100644 index 000000000000..62dc1dedfb0e --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx91-11x11-frdm-s.dts @@ -0,0 +1,769 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2025 NXP + */ + +/dts-v1/; + +#include +#include "imx91.dtsi" + +/ { + compatible = "fsl,imx91-11x11-frdm-s", "fsl,imx91"; + model = "NXP FRDM-IMX91S board"; + + aliases { + ethernet0 = &eqos; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; + gpio3 = &gpio4; + i2c0 = &lpi2c1; + i2c1 = &lpi2c2; + mmc0 = &usdhc1; + mmc1 = &usdhc2; + rtc0 = &pcf2131; + rtc1 = &bbnsm_rtc; + serial0 = &lpuart1; + serial4 = &lpuart5; + }; + + chosen { + stdout-path = &lpuart1; + }; + + flexcan1_phy: can-phy { + compatible = "nxp,tja1051"; + #phy-cells = <0>; + max-bitrate = <5000000>; + silent-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&pinctrl_gpio_key>; + pinctrl-names = "default"; + + button { + interrupt-parent = <&gpio3>; + interrupts = <26 IRQ_TYPE_EDGE_FALLING>; + gpios = <&gpio3 26 GPIO_PULL_UP>; + label = "User Button"; + linux,code = ; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + + led-0 { + default-state = "on"; + gpios = <&pcal6524 7 GPIO_ACTIVE_LOW>; + label = "green:status"; + }; + }; + + reg_vref_1v8: regulator-adc-vref { + compatible = "regulator-fixed"; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + regulator-name = "vref_1v8"; + }; + + reg_usdhc1_vmmc: regulator-usdhc1 { + compatible = "regulator-fixed"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "WLAN_EN"; + startup-delay-us = <20000>; + gpio = <&pcal6524 3 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_usdhc2_vmmc: regulator-usdhc2 { + compatible = "regulator-fixed"; + off-on-delay-us = <12000>; + pinctrl-0 = <&pinctrl_reg_usdhc2_vmmc>; + pinctrl-names = "default"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "VSD_3V3"; + gpio = <&gpio3 7 GPIO_ACTIVE_HIGH>; + enable-active-high; + bootph-pre-ram; + bootph-some-ram; + }; + + reg_usb_vbus: regulator-vbus { + compatible = "regulator-fixed"; + regulator-max-microvolt = <5000000>; + regulator-min-microvolt = <5000000>; + regulator-name = "USB_VBUS"; + gpio = <&pcal6524 15 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + linux,cma { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x80000000 0 0x20000000>; + reusable; + size = <0 0x2000000>; + linux,cma-default; + }; + }; + + soc@0 { + bootph-all; + bootph-pre-ram; + }; + + sound-mqs { + compatible = "fsl,imx-audio-mqs"; + model = "mqs-audio"; + audio-codec = <&mqs1>; + audio-cpu = <&sai1>; + }; + + usdhc1_pwrseq: usdhc1-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&pcal6524 18 GPIO_ACTIVE_LOW>; + }; +}; + +&adc1 { + vref-supply = <®_vref_1v8>; + status = "okay"; +}; + +&aips1 { + bootph-pre-ram; + bootph-all; +}; + +&aips2 { + bootph-pre-ram; + bootph-some-ram; +}; + +&aips3 { + bootph-pre-ram; + bootph-some-ram; +}; + +&clk { + bootph-all; + bootph-pre-ram; +}; + +&clk_ext1 { + bootph-all; + bootph-pre-ram; +}; + +&eqos { + phy-handle = <ðphy1>; + phy-mode = "rgmii-id"; + pinctrl-0 = <&pinctrl_eqos>; + pinctrl-1 = <&pinctrl_eqos_sleep>; + pinctrl-names = "default", "sleep"; + status = "okay"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <5000000>; + + ethphy1: ethernet-phy@4 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <4>; + }; + }; +}; + +&flexcan1 { + phys = <&flexcan1_phy>; + pinctrl-0 = <&pinctrl_flexcan1>; + pinctrl-1 = <&pinctrl_flexcan1_sleep>; + pinctrl-names = "default", "sleep"; + status = "okay"; +}; + +&flexspi1 { + pinctrl-0 = <&pinctrl_flexspi1>; + pinctrl-names = "default"; + status = "okay"; + + flash@0 { + compatible = "spi-nand"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <104000000>; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <4>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + reg = <0x000000 0x800000>; + label = "bootloader"; + }; + + partition@1 { + reg = <0x800000 0x800000>; + label = "env"; + }; + + partition@2 { + reg = <0x1000000 0x2800000>; + label = "kernel"; + }; + + partition@3 { + reg = <0x3800000 0x20000>; + label = "dtb"; + }; + + partition@4 { + reg = <0x3820000 0xc7e0000>; + label = "rootfs"; + linux,rootfs; + }; + }; + }; +}; + +&gpio1 { + bootph-pre-ram; + bootph-some-ram; +}; + +&gpio2 { + bootph-pre-ram; + bootph-some-ram; +}; + +&gpio3 { + bootph-pre-ram; + bootph-some-ram; +}; + +&gpio4 { + bootph-pre-ram; + bootph-some-ram; +}; + +&iomuxc { + bootph-pre-ram; + bootph-some-ram; + + pinctrl_eqos: eqosgrp { + fsl,pins = < + MX91_PAD_ENET1_MDC__ENET1_MDC 0x57e + MX91_PAD_ENET1_MDIO__ENET_QOS_MDIO 0x57e + MX91_PAD_ENET1_RD0__ENET_QOS_RGMII_RD0 0x57e + MX91_PAD_ENET1_RD1__ENET_QOS_RGMII_RD1 0x57e + MX91_PAD_ENET1_RD2__ENET_QOS_RGMII_RD2 0x57e + MX91_PAD_ENET1_RD3__ENET_QOS_RGMII_RD3 0x57e + MX91_PAD_ENET1_RXC__ENET_QOS_RGMII_RXC 0x5fe + MX91_PAD_ENET1_RX_CTL__ENET_QOS_RGMII_RX_CTL 0x57e + MX91_PAD_ENET1_TD0__ENET_QOS_RGMII_TD0 0x57e + MX91_PAD_ENET1_TD1__ENET1_RGMII_TD1 0x57e + MX91_PAD_ENET1_TD2__ENET_QOS_RGMII_TD2 0x57e + MX91_PAD_ENET1_TD3__ENET_QOS_RGMII_TD3 0x57e + MX91_PAD_ENET1_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK 0x5fe + MX91_PAD_ENET1_TX_CTL__ENET_QOS_RGMII_TX_CTL 0x57e + >; + }; + + pinctrl_eqos_sleep: eqossleepgrp { + fsl,pins = < + MX91_PAD_ENET1_MDC__GPIO4_IO0 0x31e + MX91_PAD_ENET1_MDIO__GPIO4_IO1 0x31e + MX91_PAD_ENET1_RD0__GPIO4_IO10 0x31e + MX91_PAD_ENET1_RD1__GPIO4_IO11 0x31e + MX91_PAD_ENET1_RD2__GPIO4_IO12 0x31e + MX91_PAD_ENET1_RD3__GPIO4_IO13 0x31e + MX91_PAD_ENET1_RXC__GPIO4_IO9 0x31e + MX91_PAD_ENET1_RX_CTL__GPIO4_IO8 0x31e + MX91_PAD_ENET1_TD0__GPIO4_IO5 0x31e + MX91_PAD_ENET1_TD1__GPIO4_IO4 0x31e + MX91_PAD_ENET1_TD2__GPIO4_IO3 0x31e + MX91_PAD_ENET1_TD3__GPIO4_IO2 0x31e + MX91_PAD_ENET1_TXC__GPIO4_IO7 0x31e + MX91_PAD_ENET1_TX_CTL__GPIO4_IO6 0x31e + >; + }; + + pinctrl_flexcan1: flexcan1grp { + fsl,pins = < + MX91_PAD_GPIO_IO28__CAN1_TX 0x139e + MX91_PAD_GPIO_IO29__CAN1_RX 0x139e + MX91_PAD_PDM_BIT_STREAM1__GPIO1_IO10 0x31e + >; + }; + + pinctrl_flexcan1_sleep: flexcan1sleepgrp { + fsl,pins = < + MX91_PAD_GPIO_IO28__GPIO2_IO28 0x31e + MX91_PAD_GPIO_IO29__GPIO2_IO29 0x31e + MX91_PAD_PDM_BIT_STREAM1__GPIO1_IO10 0x31e + >; + }; + + pinctrl_flexspi1: flexspi1grp { + fsl,pins = < + MX91_PAD_SD3_CLK__FLEXSPI1_A_SCLK 0x3fe + MX91_PAD_SD3_CMD__FLEXSPI1_A_SS0_B 0x3fe + MX91_PAD_SD3_DATA0__FLEXSPI1_A_DATA0 0x3fe + MX91_PAD_SD3_DATA1__FLEXSPI1_A_DATA1 0x3fe + MX91_PAD_SD3_DATA2__FLEXSPI1_A_DATA2 0x3fe + MX91_PAD_SD3_DATA3__FLEXSPI1_A_DATA3 0x3fe + >; + }; + + pinctrl_gpio_key: gpiokeysgrp { + fsl,pins = < + MX91_PAD_CCM_CLKO1__GPIO3_IO26 0x31e + >; + }; + + pinctrl_lpi2c1: lpi2c1grp { + fsl,pins = < + MX91_PAD_I2C1_SCL__LPI2C1_SCL 0x40000b9e + MX91_PAD_I2C1_SDA__LPI2C1_SDA 0x40000b9e + >; + bootph-pre-ram; + bootph-some-ram; + }; + + pinctrl_lpi2c2: lpi2c2grp { + fsl,pins = < + MX91_PAD_I2C2_SCL__LPI2C2_SCL 0x40000b9e + MX91_PAD_I2C2_SDA__LPI2C2_SDA 0x40000b9e + >; + bootph-pre-ram; + bootph-some-ram; + }; + + pinctrl_mqs1: mqs1grp { + fsl,pins = < + MX91_PAD_PDM_CLK__MQS1_LEFT 0x31e + MX91_PAD_PDM_BIT_STREAM0__MQS1_RIGHT 0x31e + >; + }; + + pinctrl_pcal6524: pcal6524grp { + fsl,pins = < + MX91_PAD_CCM_CLKO2__GPIO3_IO27 0x31e + MX91_PAD_SD1_DATA5__GPIO3_IO15 0x31e + >; + }; + + pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { + fsl,pins = < + MX91_PAD_SD2_RESET_B__GPIO3_IO7 0x31e + >; + bootph-pre-ram; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX91_PAD_UART1_RXD__LPUART1_RX 0x31e + MX91_PAD_UART1_TXD__LPUART1_TX 0x31e + >; + bootph-pre-ram; + bootph-some-ram; + }; + + pinctrl_uart5: uart5grp { + fsl,pins = < + MX91_PAD_DAP_TDO_TRACESWO__LPUART5_TX 0x31e + MX91_PAD_DAP_TDI__LPUART5_RX 0x31e + MX91_PAD_DAP_TMS_SWDIO__LPUART5_RTS_B 0x31e + MX91_PAD_DAP_TCLK_SWCLK__LPUART5_CTS_B 0x31e + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX91_PAD_SD1_CLK__USDHC1_CLK 0x1582 + MX91_PAD_SD1_CMD__USDHC1_CMD 0x1382 + MX91_PAD_SD1_DATA0__USDHC1_DATA0 0x1382 + MX91_PAD_SD1_DATA1__USDHC1_DATA1 0x1382 + MX91_PAD_SD1_DATA2__USDHC1_DATA2 0x1382 + MX91_PAD_SD1_DATA3__USDHC1_DATA3 0x1382 + MX91_PAD_SD1_DATA4__GPIO3_IO14 0x31e + MX91_PAD_SD1_STROBE__GPIO3_IO18 0x31e + >; + bootph-pre-ram; + bootph-some-ram; + }; + + pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { + fsl,pins = < + MX91_PAD_SD1_CLK__USDHC1_CLK 0x158e + MX91_PAD_SD1_CMD__USDHC1_CMD 0x138e + MX91_PAD_SD1_DATA0__USDHC1_DATA0 0x138e + MX91_PAD_SD1_DATA1__USDHC1_DATA1 0x138e + MX91_PAD_SD1_DATA2__USDHC1_DATA2 0x138e + MX91_PAD_SD1_DATA3__USDHC1_DATA3 0x138e + MX91_PAD_SD1_DATA4__GPIO3_IO14 0x31e + MX91_PAD_SD1_STROBE__GPIO3_IO18 0x31e + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { + fsl,pins = < + MX91_PAD_SD1_CLK__USDHC1_CLK 0x15fe + MX91_PAD_SD1_CMD__USDHC1_CMD 0x13fe + MX91_PAD_SD1_DATA0__USDHC1_DATA0 0x13fe + MX91_PAD_SD1_DATA1__USDHC1_DATA1 0x13fe + MX91_PAD_SD1_DATA2__USDHC1_DATA2 0x13fe + MX91_PAD_SD1_DATA3__USDHC1_DATA3 0x13fe + MX91_PAD_SD1_DATA4__GPIO3_IO14 0x31e + MX91_PAD_SD1_STROBE__GPIO3_IO18 0x31e + >; + }; + + pinctrl_usdhc2_gpio: usdhc2gpiogrp { + fsl,pins = < + MX91_PAD_SD2_CD_B__GPIO3_IO0 0x31e + >; + bootph-pre-ram; + bootph-some-ram; + }; + + pinctrl_usdhc2_gpio_sleep: usdhc2gpiosleepgrp { + fsl,pins = < + MX91_PAD_SD2_CD_B__GPIO3_IO0 0x51e + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX91_PAD_SD2_CLK__USDHC2_CLK 0x1582 + MX91_PAD_SD2_CMD__USDHC2_CMD 0x1382 + MX91_PAD_SD2_DATA0__USDHC2_DATA0 0x1382 + MX91_PAD_SD2_DATA1__USDHC2_DATA1 0x1382 + MX91_PAD_SD2_DATA2__USDHC2_DATA2 0x1382 + MX91_PAD_SD2_DATA3__USDHC2_DATA3 0x1382 + MX91_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + bootph-pre-ram; + bootph-some-ram; + }; + + pinctrl_usdhc2_sleep: usdhc2sleepgrp { + fsl,pins = < + MX91_PAD_SD2_CLK__GPIO3_IO1 0x51e + MX91_PAD_SD2_CMD__GPIO3_IO2 0x51e + MX91_PAD_SD2_DATA0__GPIO3_IO3 0x51e + MX91_PAD_SD2_DATA1__GPIO3_IO4 0x51e + MX91_PAD_SD2_DATA2__GPIO3_IO5 0x51e + MX91_PAD_SD2_DATA3__GPIO3_IO6 0x51e + MX91_PAD_SD2_VSELECT__GPIO3_IO19 0x51e + >; + }; + + pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { + fsl,pins = < + MX91_PAD_SD2_CLK__USDHC2_CLK 0x158e + MX91_PAD_SD2_CMD__USDHC2_CMD 0x138e + MX91_PAD_SD2_DATA0__USDHC2_DATA0 0x138e + MX91_PAD_SD2_DATA1__USDHC2_DATA1 0x138e + MX91_PAD_SD2_DATA2__USDHC2_DATA2 0x138e + MX91_PAD_SD2_DATA3__USDHC2_DATA3 0x138e + MX91_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; + + pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { + fsl,pins = < + MX91_PAD_SD2_CLK__USDHC2_CLK 0x15fe + MX91_PAD_SD2_CMD__USDHC2_CMD 0x13fe + MX91_PAD_SD2_DATA0__USDHC2_DATA0 0x13fe + MX91_PAD_SD2_DATA1__USDHC2_DATA1 0x13fe + MX91_PAD_SD2_DATA2__USDHC2_DATA2 0x13fe + MX91_PAD_SD2_DATA3__USDHC2_DATA3 0x13fe + MX91_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; + + pinctrl_wdog: wdoggrp { + fsl,pins = < + MX91_PAD_WDOG_ANY__WDOG1_WDOG_ANY 0x31e + >; + }; +}; + +&lpi2c1 { + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_lpi2c1>; + pinctrl-names = "default"; + bootph-pre-ram; + bootph-some-ram; + status = "okay"; + + pcf2131: rtc@53 { + compatible = "nxp,pcf2131"; + reg = <0x53>; + interrupt-parent = <&pcal6524>; + interrupts = <1 IRQ_TYPE_EDGE_FALLING>; + }; +}; + +&lpi2c2 { + clock-frequency = <400000>; + pinctrl-0 = <&pinctrl_lpi2c2>; + pinctrl-names = "default"; + bootph-pre-ram; + bootph-some-ram; + status = "okay"; + + pcal6524: gpio@22 { + compatible = "nxp,pcal6524"; + reg = <0x22>; + #interrupt-cells = <2>; + interrupt-controller; + interrupt-parent = <&gpio3>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; + #gpio-cells = <2>; + gpio-controller; + pinctrl-0 = <&pinctrl_pcal6524>; + pinctrl-names = "default"; + reset-gpios = <&gpio3 15 GPIO_ACTIVE_LOW>; + }; + + pmic@32 { + compatible = "nxp,pf9453"; + reg = <0x32>; + interrupt-parent = <&pcal6524>; + interrupts = <10 IRQ_TYPE_EDGE_FALLING>; + bootph-pre-ram; + bootph-some-ram; + + regulators { + bootph-pre-ram; + bootph-some-ram; + + buck1: BUCK1 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3775000>; + regulator-min-microvolt = <600000>; + regulator-name = "BUCK1"; + }; + + buck2: BUCK2 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <2187500>; + regulator-min-microvolt = <600000>; + regulator-name = "BUCK2"; + regulator-ramp-delay = <12500>; + }; + + buck3: BUCK3 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3775000>; + regulator-min-microvolt = <600000>; + regulator-name = "BUCK3"; + }; + + buck4: BUCK4 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3775000>; + regulator-min-microvolt = <600000>; + regulator-name = "BUCK4"; + }; + + ldo1: LDO1 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <800000>; + regulator-name = "LDO1"; + }; + + ldo2: LDO2 { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <1950000>; + regulator-min-microvolt = <500000>; + regulator-name = "LDO2"; + }; + + ldo_snvs: LDO-SNVS { + regulator-always-on; + regulator-boot-on; + regulator-max-microvolt = <3400000>; + regulator-min-microvolt = <1200000>; + regulator-name = "LDO-SNVS"; + }; + }; + }; + + ptn5110: tcpc@52 { + compatible = "nxp,ptn5110", "tcpci"; + reg = <0x52>; + interrupt-parent = <&gpio3>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; + + typec1_con: connector { + compatible = "usb-c-connector"; + data-role = "dual"; + label = "USB-C"; + op-sink-microwatt = <15000000>; + power-role = "dual"; + self-powered; + sink-pdos = ; + source-pdos = ; + try-power-role = "sink"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + typec1_dr_sw: endpoint { + remote-endpoint = <&usb1_drd_sw>; + }; + }; + }; + }; + }; +}; + +&lpuart1 { + pinctrl-0 = <&pinctrl_uart1>; + pinctrl-names = "default"; + bootph-pre-ram; + bootph-some-ram; + status = "okay"; +}; + +&lpuart5 { + pinctrl-0 = <&pinctrl_uart5>; + pinctrl-names = "default"; + status = "okay"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; +}; + +&media_blk_ctrl { + status = "okay"; +}; + +&mqs1 { + clocks = <&clk IMX93_CLK_MQS1_GATE>; + clock-names = "mclk"; + pinctrl-0 = <&pinctrl_mqs1>; + pinctrl-names = "default"; + status = "okay"; +}; + +&osc_32k { + bootph-all; + bootph-pre-ram; +}; + +&osc_24m { + bootph-all; + bootph-pre-ram; +}; + +&sai1 { + clocks = <&clk IMX93_CLK_SAI1_IPG>, <&clk IMX93_CLK_DUMMY>, + <&clk IMX93_CLK_SAI1_GATE>, <&clk IMX93_CLK_DUMMY>, + <&clk IMX93_CLK_DUMMY>, <&clk IMX93_CLK_AUDIO_PLL>; + clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3", "pll8k"; + assigned-clocks = <&clk IMX93_CLK_SAI1>; + assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>; + assigned-clock-rates = <24576000>; + #sound-dai-cells = <0>; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + +&usbotg1 { + adp-disable; + disable-over-current; + dr_mode = "otg"; + hnp-disable; + srp-disable; + usb-role-switch; + samsung,picophy-dc-vol-level-adjust = <7>; + samsung,picophy-pre-emp-curr-control = <3>; + status = "okay"; + + port { + usb1_drd_sw: endpoint { + remote-endpoint = <&typec1_dr_sw>; + }; + }; +}; + +&usbotg2 { + disable-over-current; + dr_mode = "host"; + vbus-supply = <®_usb_vbus>; + samsung,picophy-dc-vol-level-adjust = <7>; + samsung,picophy-pre-emp-curr-control = <3>; + status = "okay"; +}; + +&usdhc1 { + bus-width = <8>; + keep-power-in-suspend; + mmc-pwrseq = <&usdhc1_pwrseq>; + non-removable; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + vmmc-supply = <®_usdhc1_vmmc>; + wakeup-source; + bootph-pre-ram; + bootph-some-ram; + status = "okay"; +}; + +&usdhc2 { + bus-width = <4>; + cd-gpios = <&gpio3 0 GPIO_ACTIVE_LOW>; + no-mmc; + no-sdio; + pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>; + pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-3 = <&pinctrl_usdhc2_sleep>, <&pinctrl_usdhc2_gpio_sleep>; + pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep"; + vmmc-supply = <®_usdhc2_vmmc>; + bootph-pre-ram; + bootph-some-ram; + status = "okay"; +}; + +&wdog3 { + pinctrl-0 = <&pinctrl_wdog>; + pinctrl-names = "default"; + fsl,ext-reset-output; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx91-var-dart-sonata.dts b/arch/arm64/boot/dts/freescale/imx91-var-dart-sonata.dts new file mode 100644 index 000000000000..afa39dab240a --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx91-var-dart-sonata.dts @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Variscite Sonata carrier board for DART-MX91 + * + * Link: https://variscite.com/carrier-boards/sonata-board/ + * + * Copyright (C) 2025 Variscite Ltd. - https://www.variscite.com/ + * + */ + +/dts-v1/; + +#include "imx91-var-dart.dtsi" + +/ { + model = "Variscite DART-MX91 on Sonata-Board"; + compatible = "variscite,var-dart-mx91-sonata", + "variscite,var-dart-mx91", + "fsl,imx91"; + + aliases { + ethernet0 = &eqos; + ethernet1 = &fec; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; + i2c0 = &lpi2c1; + i2c1 = &lpi2c2; + i2c2 = &lpi2c3; + mmc0 = &usdhc1; + mmc1 = &usdhc2; + serial0 = &lpuart1; + serial1 = &lpuart2; + serial2 = &lpuart3; + serial3 = &lpuart4; + serial4 = &lpuart5; + serial5 = &lpuart6; + }; + + chosen { + stdout-path = &lpuart1; + }; + + gpio-keys { + compatible = "gpio-keys"; + + button-home { + label = "Home"; + linux,code = ; + gpios = <&pca6408_1 4 GPIO_ACTIVE_LOW>; + wakeup-source; + }; + + button-up { + label = "Up"; + linux,code = ; + gpios = <&pca6408_1 5 GPIO_ACTIVE_LOW>; + wakeup-source; + }; + + button-down { + label = "Down"; + linux,code = ; + gpios = <&pca6408_1 6 GPIO_ACTIVE_LOW>; + wakeup-source; + }; + + button-back { + label = "Back"; + linux,code = ; + gpios = <&pca6408_1 7 GPIO_ACTIVE_LOW>; + wakeup-source; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + + led-emmc { + label = "eMMC"; + gpios = <&pca6408_2 7 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + }; + }; + + reg_vref_1v8: regulator-adc-vref { + compatible = "regulator-fixed"; + regulator-name = "vref_1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + reg_usdhc2_vmmc: regulator-vmmc-usdhc2 { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_usdhc2_vmmc>; + regulator-name = "VDD_SD2_3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio4 28 GPIO_ACTIVE_HIGH>; + enable-active-high; + off-on-delay-us = <20000>; + }; + + reserved-memory { + ranges; + #address-cells = <2>; + #size-cells = <2>; + + linux,cma { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x80000000 0 0x40000000>; + reusable; + size = <0 0x10000000>; + linux,cma-default; + }; + }; +}; + +&adc1 { + vref-supply = <®_vref_1v8>; + status = "okay"; +}; + +/* Use external instead of internal RTC */ +&bbnsm_rtc { + status = "disabled"; +}; + +&eqos { + mdio { + ethphy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + reset-gpios = <&pca6408_2 0 GPIO_ACTIVE_LOW>; + reset-assert-us = <15000>; + reset-deassert-us = <100000>; + + leds { + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + color = ; + function = LED_FUNCTION_LAN; + linux,default-trigger = "netdev"; + }; + + led@1 { + reg = <1>; + color = ; + function = LED_FUNCTION_LAN; + linux,default-trigger = "netdev"; + }; + }; + }; + }; +}; + +&fec { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_fec>; + pinctrl-1 = <&pinctrl_fec_sleep>; + /* + * The required RGMII TX and RX 2ns delays are implemented directly + * in hardware via passive delay elements on the SOM PCB. + * No delay configuration is needed in software via PHY driver. + */ + phy-mode = "rgmii"; + phy-handle = <ðphy1>; + status = "okay"; +}; + +&flexcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan1>; + status = "okay"; +}; + +&lpi2c1 { + clock-frequency = <400000>; + pinctrl-names = "default", "sleep", "gpio"; + pinctrl-0 = <&pinctrl_lpi2c1>; + pinctrl-1 = <&pinctrl_lpi2c1_gpio>; + pinctrl-2 = <&pinctrl_lpi2c1_gpio>; + scl-gpios = <&gpio1 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio1 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; + + pca6408_1: gpio@20 { + compatible = "nxp,pcal6408"; + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&gpio1>; + interrupts = <10 IRQ_TYPE_LEVEL_LOW>; + }; + + pca6408_2: gpio@21 { + compatible = "nxp,pcal6408"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&gpio1>; + interrupts = <10 IRQ_TYPE_LEVEL_LOW>; + }; + + pca9534: gpio@22 { + compatible = "nxp,pca9534"; + reg = <0x22>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&gpio1>; + interrupts = <10 IRQ_TYPE_LEVEL_LOW>; + }; + + st33ktpm2xi2c: tpm@2e { + compatible = "st,st33ktpm2xi2c", "tcg,tpm-tis-i2c"; + reg = <0x2e>; + }; + + /* Capacitive touch controller */ + ft5x06_ts: touchscreen@38 { + compatible = "edt,edt-ft5206"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_captouch>; + reset-gpios = <&pca6408_2 4 GPIO_ACTIVE_LOW>; + interrupt-parent = <&gpio3>; + interrupts = <27 IRQ_TYPE_EDGE_FALLING>; + touchscreen-size-x = <800>; + touchscreen-size-y = <480>; + touchscreen-inverted-x; + touchscreen-inverted-y; + wakeup-source; + }; + + /* USB Type-C Controller */ + typec@3d { + compatible = "nxp,ptn5150"; + reg = <0x3d>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_extcon>; + interrupt-parent = <&gpio4>; + interrupts = <29 IRQ_TYPE_LEVEL_HIGH>; + + port { + typec1_dr_sw: endpoint { + remote-endpoint = <&usb1_drd_sw>; + }; + }; + }; + + rtc@68 { + compatible = "dallas,ds1337"; + reg = <0x68>; + }; +}; + +/* Console (J10) */ +&lpuart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; +}; + +/* Header (J12.4, J12.6) */ +&lpuart6 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart6>; + status = "okay"; +}; + +&usbotg1 { + dr_mode = "otg"; + hnp-disable; + srp-disable; + adp-disable; + usb-role-switch; + disable-over-current; + samsung,picophy-pre-emp-curr-control = <3>; + samsung,picophy-dc-vol-level-adjust = <7>; + status = "okay"; + + port { + usb1_drd_sw: endpoint { + remote-endpoint = <&typec1_dr_sw>; + }; + }; +}; + +&usbotg2 { + disable-over-current; + dr_mode = "host"; + status = "okay"; +}; + +/* SD */ +&usdhc2 { + pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep"; + pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>; + pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-3 = <&pinctrl_usdhc2_sleep>, <&pinctrl_usdhc2_gpio_sleep>; + cd-gpios = <&gpio3 0 GPIO_ACTIVE_LOW>; + vmmc-supply = <®_usdhc2_vmmc>; + bus-width = <4>; + no-sdio; + no-mmc; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + pinctrl_hog: hoggrp { + fsl,pins = < + /* GPIO Expanders shared IRQ */ + MX91_PAD_PDM_BIT_STREAM1__GPIO1_IO10 0x31e + >; + }; + + pinctrl_captouch: captouchgrp { + fsl,pins = < + MX91_PAD_CCM_CLKO2__GPIO3_IO27 0x31e + >; + }; + + pinctrl_extcon: extcongrp { + fsl,pins = < + MX91_PAD_CCM_CLKO4__GPIO4_IO29 0x31e + >; + }; + + pinctrl_fec: fecgrp { + fsl,pins = < + MX91_PAD_ENET2_RD0__ENET2_RGMII_RD0 0x57e + MX91_PAD_ENET2_RD1__ENET2_RGMII_RD1 0x57e + MX91_PAD_ENET2_RD2__ENET2_RGMII_RD2 0x57e + MX91_PAD_ENET2_RD3__ENET2_RGMII_RD3 0x37e + MX91_PAD_ENET2_RXC__ENET2_RGMII_RXC 0x5fe + MX91_PAD_ENET2_RX_CTL__ENET2_RGMII_RX_CTL 0x57e + MX91_PAD_ENET2_TD0__ENET2_RGMII_TD0 0x57e + MX91_PAD_ENET2_TD1__ENET2_RGMII_TD1 0x57e + MX91_PAD_ENET2_TD2__ENET2_RGMII_TD2 0x57e + MX91_PAD_ENET2_TD3__ENET2_RGMII_TD3 0x57e + MX91_PAD_ENET2_TXC__ENET2_RGMII_TXC 0x5fe + MX91_PAD_ENET2_TX_CTL__ENET2_RGMII_TX_CTL 0x57e + >; + }; + + pinctrl_fec_sleep: fecsleepgrp { + fsl,pins = < + MX91_PAD_ENET2_RD0__GPIO4_IO24 0x51e + MX91_PAD_ENET2_RD1__GPIO4_IO25 0x51e + MX91_PAD_ENET2_RD2__GPIO4_IO26 0x51e + MX91_PAD_ENET2_RD3__GPIO4_IO27 0x31e + MX91_PAD_ENET2_RXC__GPIO4_IO23 0x51e + MX91_PAD_ENET2_RX_CTL__GPIO4_IO22 0x51e + MX91_PAD_ENET2_TD0__GPIO4_IO19 0x51e + MX91_PAD_ENET2_TD1__GPIO4_IO18 0x51e + MX91_PAD_ENET2_TD2__GPIO4_IO17 0x51e + MX91_PAD_ENET2_TD3__GPIO4_IO16 0x51e + MX91_PAD_ENET2_TXC__GPIO4_IO21 0x51e + MX91_PAD_ENET2_TX_CTL__GPIO4_IO20 0x51e + >; + }; + + pinctrl_flexcan1: flexcan1grp { + fsl,pins = < + MX91_PAD_PDM_CLK__CAN1_TX 0x139e + MX91_PAD_PDM_BIT_STREAM0__CAN1_RX 0x139e + >; + }; + + pinctrl_lpi2c1: lpi2c1grp { + fsl,pins = < + MX91_PAD_I2C1_SCL__LPI2C1_SCL 0x40000b9e + MX91_PAD_I2C1_SDA__LPI2C1_SDA 0x40000b9e + >; + }; + + pinctrl_lpi2c1_gpio: lpi2c1-gpiogrp { + fsl,pins = < + MX91_PAD_I2C1_SCL__GPIO1_IO0 0x31e + MX91_PAD_I2C1_SDA__GPIO1_IO1 0x31e + >; + }; + + pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { + fsl,pins = < + MX91_PAD_CCM_CLKO3__GPIO4_IO28 0x31e + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX91_PAD_UART1_RXD__LPUART1_RX 0x31e + MX91_PAD_UART1_TXD__LPUART1_TX 0x31e + >; + }; + + pinctrl_uart6: uart6grp { + fsl,pins = < + MX91_PAD_GPIO_IO05__LPUART6_RX 0x31e + MX91_PAD_GPIO_IO04__LPUART6_TX 0x31e + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX91_PAD_SD2_CLK__USDHC2_CLK 0x1582 + MX91_PAD_SD2_CMD__USDHC2_CMD 0x1382 + MX91_PAD_SD2_DATA0__USDHC2_DATA0 0x1382 + MX91_PAD_SD2_DATA1__USDHC2_DATA1 0x1382 + MX91_PAD_SD2_DATA2__USDHC2_DATA2 0x1382 + MX91_PAD_SD2_DATA3__USDHC2_DATA3 0x1382 + MX91_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; + + pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { + fsl,pins = < + MX91_PAD_SD2_CLK__USDHC2_CLK 0x158e + MX91_PAD_SD2_CMD__USDHC2_CMD 0x138e + MX91_PAD_SD2_DATA0__USDHC2_DATA0 0x138e + MX91_PAD_SD2_DATA1__USDHC2_DATA1 0x138e + MX91_PAD_SD2_DATA2__USDHC2_DATA2 0x138e + MX91_PAD_SD2_DATA3__USDHC2_DATA3 0x138e + MX91_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; + + pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { + fsl,pins = < + MX91_PAD_SD2_CLK__USDHC2_CLK 0x15fe + MX91_PAD_SD2_CMD__USDHC2_CMD 0x13fe + MX91_PAD_SD2_DATA0__USDHC2_DATA0 0x13fe + MX91_PAD_SD2_DATA1__USDHC2_DATA1 0x13fe + MX91_PAD_SD2_DATA2__USDHC2_DATA2 0x13fe + MX91_PAD_SD2_DATA3__USDHC2_DATA3 0x13fe + MX91_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; + + pinctrl_usdhc2_sleep: usdhc2sleepgrp { + fsl,pins = < + MX91_PAD_SD2_CLK__GPIO3_IO1 0x51e + MX91_PAD_SD2_CMD__GPIO3_IO2 0x51e + MX91_PAD_SD2_DATA0__GPIO3_IO3 0x51e + MX91_PAD_SD2_DATA1__GPIO3_IO4 0x51e + MX91_PAD_SD2_DATA2__GPIO3_IO5 0x51e + MX91_PAD_SD2_DATA3__GPIO3_IO6 0x51e + MX91_PAD_SD2_VSELECT__GPIO3_IO19 0x51e + >; + }; + + pinctrl_usdhc2_gpio: usdhc2gpiogrp { + fsl,pins = < + MX91_PAD_SD2_CD_B__GPIO3_IO0 0x31e + >; + }; + + pinctrl_usdhc2_gpio_sleep: usdhc2gpiosleepgrp { + fsl,pins = < + MX91_PAD_SD2_CD_B__GPIO3_IO0 0x51e + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx91-var-dart.dtsi b/arch/arm64/boot/dts/freescale/imx91-var-dart.dtsi new file mode 100644 index 000000000000..a9e44efad13f --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx91-var-dart.dtsi @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Common dtsi for Variscite DART-MX91 + * + * Link: https://variscite.com/system-on-module-som/i-mx-9/i-mx-91/dart-mx91/ + * + * Copyright (C) 2026 Variscite Ltd. - https://www.variscite.com/ + * + */ + +/dts-v1/; + +#include +#include +#include "imx91.dtsi" + +/ { + model = "Variscite DART-MX91 Module"; + compatible = "variscite,var-dart-mx91", "fsl,imx91"; + + sound-wm8904 { + compatible = "simple-audio-card"; + simple-audio-card,bitclock-master = <&codec_dai>; + simple-audio-card,format = "i2s"; + simple-audio-card,frame-master = <&codec_dai>; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "wm8904-audio"; + simple-audio-card,routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "IN2L", "Line In Jack", + "IN2R", "Line In Jack", + "IN1L", "Microphone Jack", + "IN1R", "Microphone Jack"; + simple-audio-card,widgets = + "Microphone", "Microphone Jack", + "Headphone", "Headphone Jack", + "Line", "Line In Jack"; + + codec_dai: simple-audio-card,codec { + sound-dai = <&wm8904>; + }; + + simple-audio-card,cpu { + sound-dai = <&sai1>; + }; + }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + post-power-on-delay-ms = <100>; + power-off-delay-us = <10000>; + reset-gpios = <&gpio4 14 GPIO_ACTIVE_LOW>, /* WIFI_RESET */ + <&gpio3 7 GPIO_ACTIVE_LOW>; /* WIFI_PWR_EN */ + }; +}; + +&eqos { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_eqos>; + pinctrl-1 = <&pinctrl_eqos_sleep>; + /* + * The required RGMII TX and RX 2ns delays are implemented directly + * in hardware via passive delay elements on the SOM PCB. + * No delay configuration is needed in software via PHY driver. + */ + phy-mode = "rgmii"; + phy-handle = <ðphy0>; + snps,clk-csr = <5>; + status = "okay"; + + mdio { + compatible = "snps,dwmac-mdio"; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + + ethphy0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; + reset-assert-us = <15000>; + reset-deassert-us = <100000>; + + leds { + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + color = ; + function = LED_FUNCTION_LAN; + linux,default-trigger = "netdev"; + }; + + led@1 { + reg = <1>; + color = ; + function = LED_FUNCTION_LAN; + linux,default-trigger = "netdev"; + }; + }; + }; + }; +}; + +&lpi2c3 { + clock-frequency = <400000>; + pinctrl-names = "default", "sleep", "gpio"; + pinctrl-0 = <&pinctrl_lpi2c3>; + pinctrl-1 = <&pinctrl_lpi2c3_gpio>; + pinctrl-2 = <&pinctrl_lpi2c3_gpio>; + scl-gpios = <&gpio2 29 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio2 28 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; + + wm8904: audio-codec@1a { + compatible = "wlf,wm8904"; + reg = <0x1a>; + #sound-dai-cells = <0>; + clocks = <&clk IMX93_CLK_SAI1_GATE>; + clock-names = "mclk"; + AVDD-supply = <&buck5>; + CPVDD-supply = <&buck5>; + DBVDD-supply = <&buck4>; + DCVDD-supply = <&buck5>; + MICVDD-supply = <&buck5>; + wlf,drc-cfg-names = "default", "peaklimiter", "tradition", + "soft", "music"; + /* + * Config registers per name, respectively: + * KNEE_IP = 0, KNEE_OP = 0, HI_COMP = 1, LO_COMP = 1 + * KNEE_IP = -24, KNEE_OP = -6, HI_COMP = 1/4, LO_COMP = 1 + * KNEE_IP = -42, KNEE_OP = -3, HI_COMP = 0, LO_COMP = 1 + * KNEE_IP = -45, KNEE_OP = -9, HI_COMP = 1/8, LO_COMP = 1 + * KNEE_IP = -30, KNEE_OP = -10.5, HI_COMP = 1/4, LO_COMP = 1 + */ + wlf,drc-cfg-regs = /bits/ 16 <0x01af 0x3248 0x0000 0x0000>, + /bits/ 16 <0x04af 0x324b 0x0010 0x0408>, + /bits/ 16 <0x04af 0x324b 0x0028 0x0704>, + /bits/ 16 <0x04af 0x324b 0x0018 0x078c>, + /bits/ 16 <0x04af 0x324b 0x0010 0x050e>; + /* GPIO1 = DMIC_CLK, don't touch others */ + wlf,gpio-cfg = <0x0018>, <0xffff>, <0xffff>, <0xffff>; + /* DMIC is connected to IN1L */ + wlf,in1l-as-dmicdat1; + }; + + pmic@25 { + compatible = "nxp,pca9451a"; + reg = <0x25>; + + regulators { + buck1: BUCK1 { + regulator-name = "BUCK1"; + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <2237500>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <3125>; + }; + + buck2: BUCK2 { + regulator-name = "BUCK2"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <2187500>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <3125>; + }; + + buck4: BUCK4 { + regulator-name = "BUCK4"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <3400000>; + regulator-boot-on; + regulator-always-on; + }; + + buck5: BUCK5 { + regulator-name = "BUCK5"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <3400000>; + regulator-boot-on; + regulator-always-on; + }; + + buck6: BUCK6 { + regulator-name = "BUCK6"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <3400000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo1: LDO1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo4: LDO4 { + regulator-name = "LDO4"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo5: LDO5 { + regulator-name = "LDO5"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + }; + }; +}; + +/* BT module */ +&lpuart5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart5>, <&pinctrl_bt>; + uart-has-rtscts; + status = "okay"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; +}; + +&sai1 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_sai1>; + pinctrl-1 = <&pinctrl_sai1_sleep>; + assigned-clocks = <&clk IMX93_CLK_SAI1>; + assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>; + assigned-clock-rates = <12288000>; + #sound-dai-cells = <0>; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + +/* eMMC */ +&usdhc1 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + bus-width = <8>; + non-removable; + status = "okay"; +}; + +/* WiFi */ +&usdhc3 { + pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep"; + pinctrl-0 = <&pinctrl_usdhc3>, <&pinctrl_usdhc3_wlan>; + pinctrl-1 = <&pinctrl_usdhc3_100mhz>, <&pinctrl_usdhc3_wlan>; + pinctrl-2 = <&pinctrl_usdhc3_200mhz>, <&pinctrl_usdhc3_wlan>; + pinctrl-3 = <&pinctrl_usdhc3_sleep>, <&pinctrl_usdhc3_wlan>; + mmc-pwrseq = <&wifi_pwrseq>; + keep-power-in-suspend; + bus-width = <4>; + non-removable; + wakeup-source; + status = "okay"; +}; + +&wdog3 { + status = "okay"; +}; + +&iomuxc { + pinctrl_bt: btgrp { + fsl,pins = < + MX91_PAD_ENET2_MDIO__GPIO4_IO15 0x51e + >; + }; + + pinctrl_eqos: eqosgrp { + fsl,pins = < + MX91_PAD_ENET1_MDC__ENET1_MDC 0x57e + MX91_PAD_ENET1_MDIO__ENET_QOS_MDIO 0x57e + MX91_PAD_ENET1_RD0__ENET_QOS_RGMII_RD0 0x57e + MX91_PAD_ENET1_RD1__ENET_QOS_RGMII_RD1 0x57e + MX91_PAD_ENET1_RD2__ENET_QOS_RGMII_RD2 0x57e + MX91_PAD_ENET1_RD3__ENET_QOS_RGMII_RD3 0x57e + MX91_PAD_ENET1_RXC__ENET_QOS_RGMII_RXC 0x5fe + MX91_PAD_ENET1_RX_CTL__ENET_QOS_RGMII_RX_CTL 0x57e + MX91_PAD_ENET1_TD0__ENET_QOS_RGMII_TD0 0x57e + MX91_PAD_ENET1_TD1__ENET1_RGMII_TD1 0x57e + MX91_PAD_ENET1_TD2__ENET_QOS_RGMII_TD2 0x57e + MX91_PAD_ENET1_TD3__ENET_QOS_RGMII_TD3 0x57e + MX91_PAD_ENET1_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK 0x5fe + MX91_PAD_ENET1_TX_CTL__ENET_QOS_RGMII_TX_CTL 0x57e + MX91_PAD_UART2_TXD__GPIO1_IO7 0x51e + >; + }; + + pinctrl_eqos_sleep: eqos-sleepgrp { + fsl,pins = < + MX91_PAD_ENET1_MDC__GPIO4_IO0 0x31e + MX91_PAD_ENET1_MDIO__GPIO4_IO1 0x31e + MX91_PAD_ENET1_RD0__GPIO4_IO10 0x31e + MX91_PAD_ENET1_RD1__GPIO4_IO11 0x31e + MX91_PAD_ENET1_RD2__GPIO4_IO12 0x31e + MX91_PAD_ENET1_RD3__GPIO4_IO13 0x31e + MX91_PAD_ENET1_RXC__GPIO4_IO9 0x31e + MX91_PAD_ENET1_RX_CTL__GPIO4_IO8 0x31e + MX91_PAD_ENET1_TD0__GPIO4_IO5 0x31e + MX91_PAD_ENET1_TD1__GPIO4_IO4 0x31e + MX91_PAD_ENET1_TD2__GPIO4_IO3 0x31e + MX91_PAD_ENET1_TD3__GPIO4_IO2 0x31e + MX91_PAD_ENET1_TXC__GPIO4_IO7 0x31e + MX91_PAD_ENET1_TX_CTL__GPIO4_IO6 0x31e + >; + }; + + pinctrl_lpi2c3: lpi2c3grp { + fsl,pins = < + MX91_PAD_GPIO_IO28__LPI2C3_SDA 0x40000b9e + MX91_PAD_GPIO_IO29__LPI2C3_SCL 0x40000b9e + >; + }; + + pinctrl_lpi2c3_gpio: lpi2c3gpiogrp { + fsl,pins = < + MX91_PAD_GPIO_IO28__GPIO2_IO28 0x40000b9e + MX91_PAD_GPIO_IO29__GPIO2_IO29 0x40000b9e + >; + }; + + pinctrl_sai1: sai1grp { + fsl,pins = < + MX91_PAD_SAI1_TXC__SAI1_TX_BCLK 0x31e + MX91_PAD_SAI1_TXFS__SAI1_TX_SYNC 0x31e + MX91_PAD_SAI1_TXD0__SAI1_TX_DATA0 0x31e + MX91_PAD_SAI1_RXD0__SAI1_RX_DATA0 0x31e + MX91_PAD_I2C2_SDA__SAI1_RX_BCLK 0x31e + MX91_PAD_I2C2_SCL__SAI1_RX_SYNC 0x31e + MX91_PAD_UART2_RXD__SAI1_MCLK 0x31e + >; + }; + + pinctrl_sai1_sleep: sai1-sleepgrp { + fsl,pins = < + MX91_PAD_SAI1_TXC__GPIO1_IO12 0x31e + MX91_PAD_SAI1_TXFS__GPIO1_IO11 0x31e + MX91_PAD_SAI1_TXD0__GPIO1_IO13 0x31e + MX91_PAD_SAI1_RXD0__GPIO1_IO14 0x31e + MX91_PAD_UART2_RXD__GPIO1_IO6 0x31e + MX91_PAD_I2C2_SDA__GPIO1_IO3 0x31e + MX91_PAD_I2C2_SCL__GPIO1_IO2 0x31e + >; + }; + + pinctrl_uart5: uart5grp { + fsl,pins = < + MX91_PAD_DAP_TDO_TRACESWO__LPUART5_TX 0x31e + MX91_PAD_DAP_TDI__LPUART5_RX 0x31e + MX91_PAD_DAP_TMS_SWDIO__LPUART5_RTS_B 0x31e + MX91_PAD_DAP_TCLK_SWCLK__LPUART5_CTS_B 0x31e + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX91_PAD_SD1_CLK__USDHC1_CLK 0x1582 + MX91_PAD_SD1_CMD__USDHC1_CMD 0x1382 + MX91_PAD_SD1_DATA0__USDHC1_DATA0 0x1382 + MX91_PAD_SD1_DATA1__USDHC1_DATA1 0x1382 + MX91_PAD_SD1_DATA2__USDHC1_DATA2 0x1382 + MX91_PAD_SD1_DATA3__USDHC1_DATA3 0x1382 + MX91_PAD_SD1_DATA4__USDHC1_DATA4 0x1382 + MX91_PAD_SD1_DATA5__USDHC1_DATA5 0x1382 + MX91_PAD_SD1_DATA6__USDHC1_DATA6 0x1382 + MX91_PAD_SD1_DATA7__USDHC1_DATA7 0x1382 + MX91_PAD_SD1_STROBE__USDHC1_STROBE 0x1582 + >; + }; + + pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { + fsl,pins = < + MX91_PAD_SD1_CLK__USDHC1_CLK 0x158e + MX91_PAD_SD1_CMD__USDHC1_CMD 0x138e + MX91_PAD_SD1_DATA0__USDHC1_DATA0 0x138e + MX91_PAD_SD1_DATA1__USDHC1_DATA1 0x138e + MX91_PAD_SD1_DATA2__USDHC1_DATA2 0x138e + MX91_PAD_SD1_DATA3__USDHC1_DATA3 0x138e + MX91_PAD_SD1_DATA4__USDHC1_DATA4 0x138e + MX91_PAD_SD1_DATA5__USDHC1_DATA5 0x138e + MX91_PAD_SD1_DATA6__USDHC1_DATA6 0x138e + MX91_PAD_SD1_DATA7__USDHC1_DATA7 0x138e + MX91_PAD_SD1_STROBE__USDHC1_STROBE 0x158e + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { + fsl,pins = < + MX91_PAD_SD1_CLK__USDHC1_CLK 0x15fe + MX91_PAD_SD1_CMD__USDHC1_CMD 0x13fe + MX91_PAD_SD1_DATA0__USDHC1_DATA0 0x13fe + MX91_PAD_SD1_DATA1__USDHC1_DATA1 0x13fe + MX91_PAD_SD1_DATA2__USDHC1_DATA2 0x13fe + MX91_PAD_SD1_DATA3__USDHC1_DATA3 0x13fe + MX91_PAD_SD1_DATA4__USDHC1_DATA4 0x13fe + MX91_PAD_SD1_DATA5__USDHC1_DATA5 0x13fe + MX91_PAD_SD1_DATA6__USDHC1_DATA6 0x13fe + MX91_PAD_SD1_DATA7__USDHC1_DATA7 0x13fe + MX91_PAD_SD1_STROBE__USDHC1_STROBE 0x15fe + >; + }; + + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX91_PAD_SD3_CLK__USDHC3_CLK 0x1582 + MX91_PAD_SD3_CMD__USDHC3_CMD 0x1382 + MX91_PAD_SD3_DATA0__USDHC3_DATA0 0x1382 + MX91_PAD_SD3_DATA1__USDHC3_DATA1 0x1382 + MX91_PAD_SD3_DATA2__USDHC3_DATA2 0x1382 + MX91_PAD_SD3_DATA3__USDHC3_DATA3 0x1382 + >; + }; + + pinctrl_usdhc3_100mhz: usdhc3-100mhzgrp { + fsl,pins = < + MX91_PAD_SD3_CLK__USDHC3_CLK 0x158e + MX91_PAD_SD3_CMD__USDHC3_CMD 0x138e + MX91_PAD_SD3_DATA0__USDHC3_DATA0 0x138e + MX91_PAD_SD3_DATA1__USDHC3_DATA1 0x138e + MX91_PAD_SD3_DATA2__USDHC3_DATA2 0x138e + MX91_PAD_SD3_DATA3__USDHC3_DATA3 0x138e + >; + }; + + pinctrl_usdhc3_200mhz: usdhc3-200mhzgrp { + fsl,pins = < + MX91_PAD_SD3_CLK__USDHC3_CLK 0x15fe + MX91_PAD_SD3_CMD__USDHC3_CMD 0x13fe + MX91_PAD_SD3_DATA0__USDHC3_DATA0 0x13fe + MX91_PAD_SD3_DATA1__USDHC3_DATA1 0x13fe + MX91_PAD_SD3_DATA2__USDHC3_DATA2 0x13fe + MX91_PAD_SD3_DATA3__USDHC3_DATA3 0x13fe + >; + }; + + pinctrl_usdhc3_sleep: usdhc3-sleepgrp { + fsl,pins = < + MX91_PAD_SD3_CLK__GPIO3_IO20 0x31e + MX91_PAD_SD3_CMD__GPIO3_IO21 0x31e + MX91_PAD_SD3_DATA0__GPIO3_IO22 0x31e + MX91_PAD_SD3_DATA1__GPIO3_IO23 0x31e + MX91_PAD_SD3_DATA2__GPIO3_IO24 0x31e + MX91_PAD_SD3_DATA3__GPIO3_IO25 0x31e + >; + }; + + pinctrl_usdhc3_wlan: usdhc3wlangrp { + fsl,pins = < + MX91_PAD_ENET2_MDC__GPIO4_IO14 0x51e + MX91_PAD_SD2_RESET_B__GPIO3_IO7 0x51e + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx91.dtsi b/arch/arm64/boot/dts/freescale/imx91.dtsi index f075592bfc01..d63569b39bbc 100644 --- a/arch/arm64/boot/dts/freescale/imx91.dtsi +++ b/arch/arm64/boot/dts/freescale/imx91.dtsi @@ -11,7 +11,7 @@ cpu-thermal { polling-delay-passive = <250>; polling-delay = <2000>; - thermal-sensors = <&tmu 0>; + thermal-sensors = <&tmu>; trips { cpu_alert: cpu-alert { diff --git a/arch/arm64/boot/dts/freescale/imx91_93_common.dtsi b/arch/arm64/boot/dts/freescale/imx91_93_common.dtsi index 7958cef35376..46a5d2df074d 100644 --- a/arch/arm64/boot/dts/freescale/imx91_93_common.dtsi +++ b/arch/arm64/boot/dts/freescale/imx91_93_common.dtsi @@ -69,7 +69,7 @@ pmu { compatible = "arm,cortex-a55-pmu"; - interrupts = ; + interrupts = ; }; psci { @@ -79,10 +79,10 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; clock-frequency = <24000000>; arm,no-tick-in-suspend; interrupt-parent = <&gic>; @@ -1122,8 +1122,62 @@ <&clk IMX93_CLK_MIPI_DSI_GATE>; clock-names = "apb", "axi", "nic", "disp", "cam", "pxp", "lcdif", "isi", "csi", "dsi"; + assigned-clocks = <&clk IMX93_CLK_MEDIA_AXI>, + <&clk IMX93_CLK_MEDIA_APB>, + <&clk IMX93_CLK_MEDIA_DISP_PIX>; + assigned-clock-parents = <&clk IMX93_CLK_SYS_PLL_PFD1>, + <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>, + <&clk IMX93_CLK_VIDEO_PLL>; + assigned-clock-rates = <400000000>, <133333333>; #power-domain-cells = <1>; status = "disabled"; + + dpi_bridge: dpi-bridge { + compatible = "nxp,imx93-pdfc"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dpi_from_lcdif: endpoint { + remote-endpoint = <&lcdif_to_dpi>; + }; + }; + + port@1 { + reg = <1>; + + dpi_to_panel: endpoint { + }; + }; + }; + }; + }; + + lcdif: display-controller@4ae30000 { + compatible = "fsl,imx93-lcdif"; + reg = <0x4ae30000 0x23c>; + interrupts = ; + clocks = <&clk IMX93_CLK_MEDIA_DISP_PIX>, + <&clk IMX93_CLK_LCDIF_GATE>, + <&clk IMX93_CLK_MEDIA_AXI>; + clock-names = "pix", "axi", "disp_axi"; + power-domains = <&media_blk_ctrl IMX93_MEDIABLK_PD_LCDIF>; + status = "disabled"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + lcdif_to_dpi: endpoint@0 { + reg = <0>; + remote-endpoint = <&dpi_from_lcdif>; + }; + }; }; usbotg1: usb@4c100000 { diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-common.dtsi b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-common.dtsi new file mode 100644 index 000000000000..7d3fc4ad7b8b --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-common.dtsi @@ -0,0 +1,861 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2022,2026 NXP + */ + +#include + +/ { + aliases { + ethernet0 = &fec; + ethernet1 = &eqos; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; + i2c0 = &lpi2c1; + i2c1 = &lpi2c2; + i2c2 = &lpi2c3; + mmc0 = &usdhc1; + mmc1 = &usdhc2; + rtc0 = &bbnsm_rtc; + serial0 = &lpuart1; + serial1 = &lpuart2; + serial2 = &lpuart3; + serial3 = &lpuart4; + serial4 = &lpuart5; + }; + + chosen { + stdout-path = &lpuart1; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + linux,cma { + compatible = "shared-dma-pool"; + reusable; + alloc-ranges = <0 0x80000000 0 0x40000000>; + size = <0 0x10000000>; + linux,cma-default; + }; + + vdev0vring0: vdev0vring0@a4000000 { + reg = <0 0xa4000000 0 0x8000>; + no-map; + }; + + vdev0vring1: vdev0vring1@a4008000 { + reg = <0 0xa4008000 0 0x8000>; + no-map; + }; + + vdev1vring0: vdev1vring0@a4010000 { + reg = <0 0xa4010000 0 0x8000>; + no-map; + }; + + vdev1vring1: vdev1vring1@a4018000 { + reg = <0 0xa4018000 0 0x8000>; + no-map; + }; + + rsc_table: rsc-table@2021e000 { + reg = <0 0x2021e000 0 0x1000>; + no-map; + }; + + vdevbuffer: vdevbuffer@a4020000 { + compatible = "shared-dma-pool"; + reg = <0 0xa4020000 0 0x100000>; + no-map; + }; + + }; + + flexcan_phy: can-phy { + compatible = "nxp,tja1057"; + #phy-cells = <0>; + max-bitrate = <5000000>; + silent-gpios = <&adp5585 6 GPIO_ACTIVE_HIGH>; + }; + + reg_vdd_12v: regulator-vdd-12v { + compatible = "regulator-fixed"; + regulator-name = "VDD_12V"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + gpio = <&pcal6524 14 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_vref_1v8: regulator-adc-vref { + compatible = "regulator-fixed"; + regulator-name = "vref_1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + reg_audio_pwr: regulator-audio-pwr { + compatible = "regulator-fixed"; + regulator-name = "audio-pwr"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&adp5585 1 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_usdhc2_vmmc: regulator-usdhc2 { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_usdhc2_vmmc>; + regulator-name = "VSD_3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio3 7 GPIO_ACTIVE_HIGH>; + off-on-delay-us = <12000>; + enable-active-high; + }; + + backlight_lvds: backlight-lvds { + compatible = "pwm-backlight"; + pwms = <&adp5585 0 100000 0>; + brightness-levels = <0 100>; + num-interpolated-steps = <100>; + default-brightness-level = <100>; + power-supply = <®_vdd_12v>; + enable-gpios = <&adp5585 9 GPIO_ACTIVE_HIGH>; + status = "disabled"; + }; + + sound-wm8962 { + compatible = "fsl,imx-audio-wm8962"; + model = "wm8962-audio"; + audio-cpu = <&sai3>; + audio-codec = <&wm8962>; + hp-det-gpio = <&pcal6524 4 GPIO_ACTIVE_HIGH>; + audio-routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "Ext Spk", "SPKOUTL", + "Ext Spk", "SPKOUTR", + "AMIC", "MICBIAS", + "IN3R", "AMIC", + "IN1R", "AMIC"; + }; + + sound-xcvr { + compatible = "fsl,imx-audio-card"; + model = "imx-audio-xcvr"; + + pri-dai-link { + link-name = "XCVR PCM"; + + cpu { + sound-dai = <&xcvr>; + }; + }; + }; +}; + +&adc1 { + vref-supply = <®_vref_1v8>; + status = "okay"; +}; + +&cm33 { + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&mu1 0 1>, + <&mu1 1 1>, + <&mu1 3 1>; + memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, + <&vdev1vring0>, <&vdev1vring1>, <&rsc_table>; + status = "okay"; +}; + +&eqos { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_eqos>; + pinctrl-1 = <&pinctrl_eqos_sleep>; + phy-mode = "rgmii-id"; + phy-handle = <ðphy1>; + status = "okay"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <5000000>; + + ethphy1: ethernet-phy@1 { + reg = <1>; + reset-gpios = <&pcal6524 15 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <80000>; + realtek,clkout-disable; + }; + }; +}; + +&fec { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_fec>; + pinctrl-1 = <&pinctrl_fec_sleep>; + phy-mode = "rgmii-id"; + phy-handle = <ðphy2>; + fsl,magic-packet; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <5000000>; + + ethphy2: ethernet-phy@2 { + reg = <2>; + reset-gpios = <&pcal6524 16 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <80000>; + realtek,clkout-disable; + }; + }; +}; + +&flexcan2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan2>; + phys = <&flexcan_phy>; + status = "okay"; +}; + +&lpi2c1 { + clock-frequency = <400000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lpi2c1>; + status = "okay"; + + wm8962: codec@1a { + compatible = "wlf,wm8962"; + reg = <0x1a>; + clocks = <&clk IMX93_CLK_SAI3_GATE>; + DCVDD-supply = <®_audio_pwr>; + DBVDD-supply = <®_audio_pwr>; + AVDD-supply = <®_audio_pwr>; + CPVDD-supply = <®_audio_pwr>; + MICVDD-supply = <®_audio_pwr>; + PLLVDD-supply = <®_audio_pwr>; + SPKVDD1-supply = <®_audio_pwr>; + SPKVDD2-supply = <®_audio_pwr>; + gpio-cfg = < + 0x0000 /* 0:Default */ + 0x0000 /* 1:Default */ + 0x0000 /* 2:FN_DMICCLK */ + 0x0000 /* 3:Default */ + 0x0000 /* 4:FN_DMICCDAT */ + 0x0000 /* 5:Default */ + >; + }; + + inertial-meter@6a { + compatible = "st,lsm6dso"; + reg = <0x6a>; + }; +}; + +&lpi2c2 { + clock-frequency = <400000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lpi2c2>; + status = "okay"; + + pcal6524: gpio@22 { + compatible = "nxp,pcal6524"; + reg = <0x22>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcal6524>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio3>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; + }; + + pmic@25 { + compatible = "nxp,pca9451a"; + reg = <0x25>; + interrupt-parent = <&pcal6524>; + interrupts = <11 IRQ_TYPE_EDGE_FALLING>; + + regulators { + buck1: BUCK1 { + regulator-name = "BUCK1"; + regulator-min-microvolt = <610000>; + regulator-max-microvolt = <950000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <3125>; + }; + + buck2: BUCK2 { + regulator-name = "BUCK2"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <670000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <3125>; + }; + + buck4: BUCK4 { + regulator-name = "BUCK4"; + regulator-min-microvolt = <1620000>; + regulator-max-microvolt = <3400000>; + regulator-boot-on; + regulator-always-on; + }; + + buck5: BUCK5 { + regulator-name = "BUCK5"; + regulator-min-microvolt = <1620000>; + regulator-max-microvolt = <3400000>; + regulator-boot-on; + regulator-always-on; + }; + + buck6: BUCK6 { + regulator-name = "BUCK6"; + regulator-min-microvolt = <1060000>; + regulator-max-microvolt = <1140000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo1: LDO1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <1620000>; + regulator-max-microvolt = <1980000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo4: LDO4 { + regulator-name = "LDO4"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <840000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo5: LDO5 { + regulator-name = "LDO5"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + }; + }; + + adp5585: io-expander@34 { + compatible = "adi,adp5585-00", "adi,adp5585"; + reg = <0x34>; + vdd-supply = <&buck4>; + gpio-controller; + #gpio-cells = <2>; + gpio-reserved-ranges = <5 1>; + #pwm-cells = <3>; + }; +}; + +&lpi2c3 { + clock-frequency = <400000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lpi2c3>; + status = "okay"; + + adp5585_isp: io-expander@34 { + compatible = "adi,adp5585-01", "adi,adp5585"; + reg = <0x34>; + gpio-controller; + #gpio-cells = <2>; + #pwm-cells = <3>; + }; + + ptn5110: tcpc@50 { + compatible = "nxp,ptn5110", "tcpci"; + reg = <0x50>; + interrupt-parent = <&gpio3>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; + + typec1_con: connector { + compatible = "usb-c-connector"; + label = "USB-C"; + power-role = "dual"; + data-role = "dual"; + try-power-role = "sink"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <15000000>; + self-powered; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + typec1_dr_sw: endpoint { + remote-endpoint = <&usb1_drd_sw>; + }; + }; + }; + }; + }; + + ptn5110_2: tcpc@51 { + compatible = "nxp,ptn5110", "tcpci"; + reg = <0x51>; + interrupt-parent = <&gpio3>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; + + typec2_con: connector { + compatible = "usb-c-connector"; + label = "USB-C"; + power-role = "dual"; + data-role = "dual"; + try-power-role = "sink"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <15000000>; + self-powered; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + typec2_dr_sw: endpoint { + remote-endpoint = <&usb2_drd_sw>; + }; + }; + }; + }; + }; + + pcf2131: rtc@53 { + compatible = "nxp,pcf2131"; + reg = <0x53>; + interrupt-parent = <&pcal6524>; + interrupts = <1 IRQ_TYPE_EDGE_FALLING>; + }; +}; + +&lpuart1 { /* console */ + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; +}; + +&lpuart5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart5>; + status = "okay"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; +}; + +&mu1 { + status = "okay"; +}; + +&mu2 { + status = "okay"; +}; + +&sai3 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_sai3>; + pinctrl-1 = <&pinctrl_sai3_sleep>; + assigned-clocks = <&clk IMX93_CLK_SAI3>; + assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>; + assigned-clock-rates = <12288000>; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + +&usbotg1 { + dr_mode = "otg"; + hnp-disable; + srp-disable; + adp-disable; + usb-role-switch; + disable-over-current; + samsung,picophy-pre-emp-curr-control = <3>; + samsung,picophy-dc-vol-level-adjust = <7>; + status = "okay"; + + port { + usb1_drd_sw: endpoint { + remote-endpoint = <&typec1_dr_sw>; + }; + }; +}; + +&usbotg2 { + dr_mode = "otg"; + hnp-disable; + srp-disable; + adp-disable; + usb-role-switch; + disable-over-current; + samsung,picophy-pre-emp-curr-control = <3>; + samsung,picophy-dc-vol-level-adjust = <7>; + status = "okay"; + + port { + usb2_drd_sw: endpoint { + remote-endpoint = <&typec2_dr_sw>; + }; + }; +}; + +&usdhc1 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + bus-width = <8>; + non-removable; + fsl,tuning-step = <1>; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep"; + pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>; + pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-3 = <&pinctrl_usdhc2_sleep>, <&pinctrl_usdhc2_gpio_sleep>; + cd-gpios = <&gpio3 00 GPIO_ACTIVE_LOW>; + vmmc-supply = <®_usdhc2_vmmc>; + bus-width = <4>; + fsl,tuning-step = <1>; + status = "okay"; + no-mmc; +}; + +&wdog3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_wdog>; + fsl,ext-reset-output; + status = "okay"; +}; + +&xcvr { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_spdif>; + pinctrl-1 = <&pinctrl_spdif_sleep>; + assigned-clocks = <&clk IMX93_CLK_SPDIF>, + <&clk IMX93_CLK_AUDIO_XCVR>; + assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>, + <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>; + assigned-clock-rates = <12288000>, <200000000>; + status = "okay"; +}; + +&iomuxc { + pinctrl_eqos: eqosgrp { + fsl,pins = < + MX93_PAD_ENET1_MDC__ENET_QOS_MDC 0x57e + MX93_PAD_ENET1_MDIO__ENET_QOS_MDIO 0x57e + MX93_PAD_ENET1_RD0__ENET_QOS_RGMII_RD0 0x57e + MX93_PAD_ENET1_RD1__ENET_QOS_RGMII_RD1 0x57e + MX93_PAD_ENET1_RD2__ENET_QOS_RGMII_RD2 0x57e + MX93_PAD_ENET1_RD3__ENET_QOS_RGMII_RD3 0x57e + MX93_PAD_ENET1_RXC__CCM_ENET_QOS_CLOCK_GENERATE_RX_CLK 0x58e + MX93_PAD_ENET1_RX_CTL__ENET_QOS_RGMII_RX_CTL 0x57e + MX93_PAD_ENET1_TD0__ENET_QOS_RGMII_TD0 0x57e + MX93_PAD_ENET1_TD1__ENET_QOS_RGMII_TD1 0x57e + MX93_PAD_ENET1_TD2__ENET_QOS_RGMII_TD2 0x57e + MX93_PAD_ENET1_TD3__ENET_QOS_RGMII_TD3 0x57e + MX93_PAD_ENET1_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK 0x58e + MX93_PAD_ENET1_TX_CTL__ENET_QOS_RGMII_TX_CTL 0x57e + >; + }; + + pinctrl_eqos_sleep: eqossleepgrp { + fsl,pins = < + MX93_PAD_ENET1_MDC__GPIO4_IO00 0x31e + MX93_PAD_ENET1_MDIO__GPIO4_IO01 0x31e + MX93_PAD_ENET1_RD0__GPIO4_IO10 0x31e + MX93_PAD_ENET1_RD1__GPIO4_IO11 0x31e + MX93_PAD_ENET1_RD2__GPIO4_IO12 0x31e + MX93_PAD_ENET1_RD3__GPIO4_IO13 0x31e + MX93_PAD_ENET1_RXC__GPIO4_IO09 0x31e + MX93_PAD_ENET1_RX_CTL__GPIO4_IO08 0x31e + MX93_PAD_ENET1_TD0__GPIO4_IO05 0x31e + MX93_PAD_ENET1_TD1__GPIO4_IO04 0x31e + MX93_PAD_ENET1_TD2__GPIO4_IO03 0x31e + MX93_PAD_ENET1_TD3__GPIO4_IO02 0x31e + MX93_PAD_ENET1_TXC__GPIO4_IO07 0x31e + MX93_PAD_ENET1_TX_CTL__GPIO4_IO06 0x31e + >; + }; + + pinctrl_fec: fecgrp { + fsl,pins = < + MX93_PAD_ENET2_MDC__ENET1_MDC 0x57e + MX93_PAD_ENET2_MDIO__ENET1_MDIO 0x57e + MX93_PAD_ENET2_RD0__ENET1_RGMII_RD0 0x57e + MX93_PAD_ENET2_RD1__ENET1_RGMII_RD1 0x57e + MX93_PAD_ENET2_RD2__ENET1_RGMII_RD2 0x57e + MX93_PAD_ENET2_RD3__ENET1_RGMII_RD3 0x57e + MX93_PAD_ENET2_RXC__ENET1_RGMII_RXC 0x58e + MX93_PAD_ENET2_RX_CTL__ENET1_RGMII_RX_CTL 0x57e + MX93_PAD_ENET2_TD0__ENET1_RGMII_TD0 0x57e + MX93_PAD_ENET2_TD1__ENET1_RGMII_TD1 0x57e + MX93_PAD_ENET2_TD2__ENET1_RGMII_TD2 0x57e + MX93_PAD_ENET2_TD3__ENET1_RGMII_TD3 0x57e + MX93_PAD_ENET2_TXC__ENET1_RGMII_TXC 0x58e + MX93_PAD_ENET2_TX_CTL__ENET1_RGMII_TX_CTL 0x57e + >; + }; + + pinctrl_fec_sleep: fecsleepgrp { + fsl,pins = < + MX93_PAD_ENET2_MDC__GPIO4_IO14 0x51e + MX93_PAD_ENET2_MDIO__GPIO4_IO15 0x51e + MX93_PAD_ENET2_RD0__GPIO4_IO24 0x51e + MX93_PAD_ENET2_RD1__GPIO4_IO25 0x51e + MX93_PAD_ENET2_RD2__GPIO4_IO26 0x51e + MX93_PAD_ENET2_RD3__GPIO4_IO27 0x51e + MX93_PAD_ENET2_RXC__GPIO4_IO23 0x51e + MX93_PAD_ENET2_RX_CTL__GPIO4_IO22 0x51e + MX93_PAD_ENET2_TD0__GPIO4_IO19 0x51e + MX93_PAD_ENET2_TD1__GPIO4_IO18 0x51e + MX93_PAD_ENET2_TD2__GPIO4_IO17 0x51e + MX93_PAD_ENET2_TD3__GPIO4_IO16 0x51e + MX93_PAD_ENET2_TXC__GPIO4_IO21 0x51e + MX93_PAD_ENET2_TX_CTL__GPIO4_IO20 0x51e + >; + }; + + pinctrl_flexcan2: flexcan2grp { + fsl,pins = < + MX93_PAD_GPIO_IO25__CAN2_TX 0x139e + MX93_PAD_GPIO_IO27__CAN2_RX 0x139e + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX93_PAD_UART1_RXD__LPUART1_RX 0x31e + MX93_PAD_UART1_TXD__LPUART1_TX 0x31e + >; + }; + + pinctrl_uart5: uart5grp { + fsl,pins = < + MX93_PAD_DAP_TDO_TRACESWO__LPUART5_TX 0x31e + MX93_PAD_DAP_TDI__LPUART5_RX 0x31e + MX93_PAD_DAP_TMS_SWDIO__LPUART5_RTS_B 0x31e + MX93_PAD_DAP_TCLK_SWCLK__LPUART5_CTS_B 0x31e + >; + }; + + pinctrl_lpi2c1: lpi2c1grp { + fsl,pins = < + MX93_PAD_I2C1_SCL__LPI2C1_SCL 0x40000b9e + MX93_PAD_I2C1_SDA__LPI2C1_SDA 0x40000b9e + >; + }; + + pinctrl_lpi2c2: lpi2c2grp { + fsl,pins = < + MX93_PAD_I2C2_SCL__LPI2C2_SCL 0x40000b9e + MX93_PAD_I2C2_SDA__LPI2C2_SDA 0x40000b9e + >; + }; + + pinctrl_lpi2c3: lpi2c3grp { + fsl,pins = < + MX93_PAD_GPIO_IO28__LPI2C3_SDA 0x40000b9e + MX93_PAD_GPIO_IO29__LPI2C3_SCL 0x40000b9e + >; + }; + + pinctrl_pcal6524: pcal6524grp { + fsl,pins = < + MX93_PAD_CCM_CLKO2__GPIO3_IO27 0x31e + >; + }; + + /* need to config the SION for data and cmd pad, refer to ERR052021 */ + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX93_PAD_SD1_CLK__USDHC1_CLK 0x1582 + MX93_PAD_SD1_CMD__USDHC1_CMD 0x40001382 + MX93_PAD_SD1_DATA0__USDHC1_DATA0 0x40001382 + MX93_PAD_SD1_DATA1__USDHC1_DATA1 0x40001382 + MX93_PAD_SD1_DATA2__USDHC1_DATA2 0x40001382 + MX93_PAD_SD1_DATA3__USDHC1_DATA3 0x40001382 + MX93_PAD_SD1_DATA4__USDHC1_DATA4 0x40001382 + MX93_PAD_SD1_DATA5__USDHC1_DATA5 0x40001382 + MX93_PAD_SD1_DATA6__USDHC1_DATA6 0x40001382 + MX93_PAD_SD1_DATA7__USDHC1_DATA7 0x40001382 + MX93_PAD_SD1_STROBE__USDHC1_STROBE 0x1582 + >; + }; + + /* need to config the SION for data and cmd pad, refer to ERR052021 */ + pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { + fsl,pins = < + MX93_PAD_SD1_CLK__USDHC1_CLK 0x158e + MX93_PAD_SD1_CMD__USDHC1_CMD 0x4000138e + MX93_PAD_SD1_DATA0__USDHC1_DATA0 0x4000138e + MX93_PAD_SD1_DATA1__USDHC1_DATA1 0x4000138e + MX93_PAD_SD1_DATA2__USDHC1_DATA2 0x4000138e + MX93_PAD_SD1_DATA3__USDHC1_DATA3 0x4000138e + MX93_PAD_SD1_DATA4__USDHC1_DATA4 0x4000138e + MX93_PAD_SD1_DATA5__USDHC1_DATA5 0x4000138e + MX93_PAD_SD1_DATA6__USDHC1_DATA6 0x4000138e + MX93_PAD_SD1_DATA7__USDHC1_DATA7 0x4000138e + MX93_PAD_SD1_STROBE__USDHC1_STROBE 0x158e + >; + }; + + /* need to config the SION for data and cmd pad, refer to ERR052021 */ + pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { + fsl,pins = < + MX93_PAD_SD1_CLK__USDHC1_CLK 0x15fe + MX93_PAD_SD1_CMD__USDHC1_CMD 0x400013fe + MX93_PAD_SD1_DATA0__USDHC1_DATA0 0x400013fe + MX93_PAD_SD1_DATA1__USDHC1_DATA1 0x400013fe + MX93_PAD_SD1_DATA2__USDHC1_DATA2 0x400013fe + MX93_PAD_SD1_DATA3__USDHC1_DATA3 0x400013fe + MX93_PAD_SD1_DATA4__USDHC1_DATA4 0x400013fe + MX93_PAD_SD1_DATA5__USDHC1_DATA5 0x400013fe + MX93_PAD_SD1_DATA6__USDHC1_DATA6 0x400013fe + MX93_PAD_SD1_DATA7__USDHC1_DATA7 0x400013fe + MX93_PAD_SD1_STROBE__USDHC1_STROBE 0x15fe + >; + }; + + pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { + fsl,pins = < + MX93_PAD_SD2_RESET_B__GPIO3_IO07 0x31e + >; + }; + + pinctrl_sai3: sai3grp { + fsl,pins = < + MX93_PAD_GPIO_IO26__SAI3_TX_SYNC 0x31e + MX93_PAD_GPIO_IO16__SAI3_TX_BCLK 0x31e + MX93_PAD_GPIO_IO17__SAI3_MCLK 0x31e + MX93_PAD_GPIO_IO19__SAI3_TX_DATA00 0x31e + MX93_PAD_GPIO_IO20__SAI3_RX_DATA00 0x31e + >; + }; + + pinctrl_sai3_sleep: sai3sleepgrp { + fsl,pins = < + MX93_PAD_GPIO_IO26__GPIO2_IO26 0x51e + MX93_PAD_GPIO_IO16__GPIO2_IO16 0x51e + MX93_PAD_GPIO_IO17__GPIO2_IO17 0x51e + MX93_PAD_GPIO_IO19__GPIO2_IO19 0x51e + MX93_PAD_GPIO_IO20__GPIO2_IO20 0x51e + >; + }; + + pinctrl_spdif: spdifgrp { + fsl,pins = < + MX93_PAD_GPIO_IO22__SPDIF_IN 0x31e + MX93_PAD_GPIO_IO23__SPDIF_OUT 0x31e + >; + }; + + pinctrl_spdif_sleep: spdifsleepgrp { + fsl,pins = < + MX93_PAD_GPIO_IO22__GPIO2_IO22 0x31e + MX93_PAD_GPIO_IO23__GPIO2_IO23 0x31e + >; + }; + + pinctrl_usdhc2_gpio: usdhc2gpiogrp { + fsl,pins = < + MX93_PAD_SD2_CD_B__GPIO3_IO00 0x31e + >; + }; + + pinctrl_usdhc2_gpio_sleep: usdhc2gpiosleepgrp { + fsl,pins = < + MX93_PAD_SD2_CD_B__GPIO3_IO00 0x51e + >; + }; + + /* need to config the SION for data and cmd pad, refer to ERR052021 */ + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX93_PAD_SD2_CLK__USDHC2_CLK 0x1582 + MX93_PAD_SD2_CMD__USDHC2_CMD 0x40001382 + MX93_PAD_SD2_DATA0__USDHC2_DATA0 0x40001382 + MX93_PAD_SD2_DATA1__USDHC2_DATA1 0x40001382 + MX93_PAD_SD2_DATA2__USDHC2_DATA2 0x40001382 + MX93_PAD_SD2_DATA3__USDHC2_DATA3 0x40001382 + MX93_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; + + /* need to config the SION for data and cmd pad, refer to ERR052021 */ + pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { + fsl,pins = < + MX93_PAD_SD2_CLK__USDHC2_CLK 0x158e + MX93_PAD_SD2_CMD__USDHC2_CMD 0x4000138e + MX93_PAD_SD2_DATA0__USDHC2_DATA0 0x4000138e + MX93_PAD_SD2_DATA1__USDHC2_DATA1 0x4000138e + MX93_PAD_SD2_DATA2__USDHC2_DATA2 0x4000138e + MX93_PAD_SD2_DATA3__USDHC2_DATA3 0x4000138e + MX93_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; + + /* need to config the SION for data and cmd pad, refer to ERR052021 */ + pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { + fsl,pins = < + MX93_PAD_SD2_CLK__USDHC2_CLK 0x15fe + MX93_PAD_SD2_CMD__USDHC2_CMD 0x400013fe + MX93_PAD_SD2_DATA0__USDHC2_DATA0 0x400013fe + MX93_PAD_SD2_DATA1__USDHC2_DATA1 0x400013fe + MX93_PAD_SD2_DATA2__USDHC2_DATA2 0x400013fe + MX93_PAD_SD2_DATA3__USDHC2_DATA3 0x400013fe + MX93_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; + + pinctrl_usdhc2_sleep: usdhc2sleepgrp { + fsl,pins = < + MX93_PAD_SD2_CLK__GPIO3_IO01 0x51e + MX93_PAD_SD2_CMD__GPIO3_IO02 0x51e + MX93_PAD_SD2_DATA0__GPIO3_IO03 0x51e + MX93_PAD_SD2_DATA1__GPIO3_IO04 0x51e + MX93_PAD_SD2_DATA2__GPIO3_IO05 0x51e + MX93_PAD_SD2_DATA3__GPIO3_IO06 0x51e + MX93_PAD_SD2_VSELECT__GPIO3_IO19 0x51e + >; + }; + + pinctrl_wdog: wdoggrp { + fsl,pins = < + MX93_PAD_WDOG_ANY__WDOG1_WDOG_ANY 0x31e + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts index 8dd5340e8141..c6db9c85f2ac 100644 --- a/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts @@ -1,116 +1,19 @@ // SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* - * Copyright 2022 NXP + * Copyright 2022,2026 NXP */ /dts-v1/; -#include #include "imx93.dtsi" +#include "imx93-11x11-evk-common.dtsi" / { model = "NXP i.MX93 11X11 EVK board"; compatible = "fsl,imx93-11x11-evk", "fsl,imx93"; aliases { - ethernet0 = &fec; - ethernet1 = &eqos; - gpio0 = &gpio1; - gpio1 = &gpio2; - gpio2 = &gpio3; - i2c0 = &lpi2c1; - i2c1 = &lpi2c2; - i2c2 = &lpi2c3; - mmc0 = &usdhc1; - mmc1 = &usdhc2; - rtc0 = &bbnsm_rtc; - serial0 = &lpuart1; - serial1 = &lpuart2; - serial2 = &lpuart3; - serial3 = &lpuart4; - serial4 = &lpuart5; - }; - - chosen { - stdout-path = &lpuart1; - }; - - reserved-memory { - #address-cells = <2>; - #size-cells = <2>; - ranges; - - linux,cma { - compatible = "shared-dma-pool"; - reusable; - alloc-ranges = <0 0x80000000 0 0x40000000>; - size = <0 0x10000000>; - linux,cma-default; - }; - - vdev0vring0: vdev0vring0@a4000000 { - reg = <0 0xa4000000 0 0x8000>; - no-map; - }; - - vdev0vring1: vdev0vring1@a4008000 { - reg = <0 0xa4008000 0 0x8000>; - no-map; - }; - - vdev1vring0: vdev1vring0@a4010000 { - reg = <0 0xa4010000 0 0x8000>; - no-map; - }; - - vdev1vring1: vdev1vring1@a4018000 { - reg = <0 0xa4018000 0 0x8000>; - no-map; - }; - - rsc_table: rsc-table@2021e000 { - reg = <0 0x2021e000 0 0x1000>; - no-map; - }; - - vdevbuffer: vdevbuffer@a4020000 { - compatible = "shared-dma-pool"; - reg = <0 0xa4020000 0 0x100000>; - no-map; - }; - - }; - - flexcan_phy: can-phy { - compatible = "nxp,tja1057"; - #phy-cells = <0>; - max-bitrate = <5000000>; - silent-gpios = <&adp5585 6 GPIO_ACTIVE_HIGH>; - }; - - reg_vdd_12v: regulator-vdd-12v { - compatible = "regulator-fixed"; - regulator-name = "VDD_12V"; - regulator-min-microvolt = <12000000>; - regulator-max-microvolt = <12000000>; - gpio = <&pcal6524 14 GPIO_ACTIVE_HIGH>; - enable-active-high; - }; - - reg_vref_1v8: regulator-adc-vref { - compatible = "regulator-fixed"; - regulator-name = "vref_1v8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - reg_audio_pwr: regulator-audio-pwr { - compatible = "regulator-fixed"; - regulator-name = "audio-pwr"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&adp5585 1 GPIO_ACTIVE_HIGH>; - enable-active-high; + mmc2 = &usdhc3; }; reg_m2_pwr: regulator-m2-pwr { @@ -122,18 +25,6 @@ enable-active-high; }; - reg_usdhc2_vmmc: regulator-usdhc2 { - compatible = "regulator-fixed"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_reg_usdhc2_vmmc>; - regulator-name = "VSD_3V3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio3 7 GPIO_ACTIVE_HIGH>; - off-on-delay-us = <12000>; - enable-active-high; - }; - reg_usdhc3_vmmc: regulator-usdhc3 { compatible = "regulator-fixed"; regulator-name = "WLAN_EN"; @@ -156,17 +47,6 @@ reset-gpios = <&pcal6524 12 GPIO_ACTIVE_LOW>; }; - backlight_lvds: backlight-lvds { - compatible = "pwm-backlight"; - pwms = <&adp5585 0 100000 0>; - brightness-levels = <0 100>; - num-interpolated-steps = <100>; - default-brightness-level = <100>; - power-supply = <®_vdd_12v>; - enable-gpios = <&adp5585 9 GPIO_ACTIVE_HIGH>; - status = "disabled"; - }; - bt_sco_codec: bt-sco-codec { compatible = "linux,bt-sco"; #sound-dai-cells = <1>; @@ -204,348 +84,6 @@ }; }; }; - - sound-wm8962 { - compatible = "fsl,imx-audio-wm8962"; - model = "wm8962-audio"; - audio-cpu = <&sai3>; - audio-codec = <&wm8962>; - hp-det-gpio = <&pcal6524 4 GPIO_ACTIVE_HIGH>; - audio-routing = - "Headphone Jack", "HPOUTL", - "Headphone Jack", "HPOUTR", - "Ext Spk", "SPKOUTL", - "Ext Spk", "SPKOUTR", - "AMIC", "MICBIAS", - "IN3R", "AMIC", - "IN1R", "AMIC"; - }; - - sound-xcvr { - compatible = "fsl,imx-audio-card"; - model = "imx-audio-xcvr"; - - pri-dai-link { - link-name = "XCVR PCM"; - - cpu { - sound-dai = <&xcvr>; - }; - }; - }; -}; - -&adc1 { - vref-supply = <®_vref_1v8>; - status = "okay"; -}; - -&cm33 { - mbox-names = "tx", "rx", "rxdb"; - mboxes = <&mu1 0 1>, - <&mu1 1 1>, - <&mu1 3 1>; - memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, - <&vdev1vring0>, <&vdev1vring1>, <&rsc_table>; - status = "okay"; -}; - -&eqos { - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&pinctrl_eqos>; - pinctrl-1 = <&pinctrl_eqos_sleep>; - phy-mode = "rgmii-id"; - phy-handle = <ðphy1>; - status = "okay"; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <1>; - #size-cells = <0>; - clock-frequency = <5000000>; - - ethphy1: ethernet-phy@1 { - reg = <1>; - reset-gpios = <&pcal6524 15 GPIO_ACTIVE_LOW>; - reset-assert-us = <10000>; - reset-deassert-us = <80000>; - realtek,clkout-disable; - }; - }; -}; - -&fec { - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&pinctrl_fec>; - pinctrl-1 = <&pinctrl_fec_sleep>; - phy-mode = "rgmii-id"; - phy-handle = <ðphy2>; - fsl,magic-packet; - status = "okay"; - - mdio { - #address-cells = <1>; - #size-cells = <0>; - clock-frequency = <5000000>; - - ethphy2: ethernet-phy@2 { - reg = <2>; - reset-gpios = <&pcal6524 16 GPIO_ACTIVE_LOW>; - reset-assert-us = <10000>; - reset-deassert-us = <80000>; - realtek,clkout-disable; - }; - }; -}; - -&flexcan2 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_flexcan2>; - phys = <&flexcan_phy>; - status = "okay"; -}; - -&lpi2c1 { - clock-frequency = <400000>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_lpi2c1>; - status = "okay"; - - wm8962: codec@1a { - compatible = "wlf,wm8962"; - reg = <0x1a>; - clocks = <&clk IMX93_CLK_SAI3_GATE>; - DCVDD-supply = <®_audio_pwr>; - DBVDD-supply = <®_audio_pwr>; - AVDD-supply = <®_audio_pwr>; - CPVDD-supply = <®_audio_pwr>; - MICVDD-supply = <®_audio_pwr>; - PLLVDD-supply = <®_audio_pwr>; - SPKVDD1-supply = <®_audio_pwr>; - SPKVDD2-supply = <®_audio_pwr>; - gpio-cfg = < - 0x0000 /* 0:Default */ - 0x0000 /* 1:Default */ - 0x0000 /* 2:FN_DMICCLK */ - 0x0000 /* 3:Default */ - 0x0000 /* 4:FN_DMICCDAT */ - 0x0000 /* 5:Default */ - >; - }; - - inertial-meter@6a { - compatible = "st,lsm6dso"; - reg = <0x6a>; - }; -}; - -&lpi2c2 { - clock-frequency = <400000>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_lpi2c2>; - status = "okay"; - - pcal6524: gpio@22 { - compatible = "nxp,pcal6524"; - reg = <0x22>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_pcal6524>; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - interrupt-parent = <&gpio3>; - interrupts = <27 IRQ_TYPE_LEVEL_LOW>; - }; - - pmic@25 { - compatible = "nxp,pca9451a"; - reg = <0x25>; - interrupt-parent = <&pcal6524>; - interrupts = <11 IRQ_TYPE_EDGE_FALLING>; - - regulators { - buck1: BUCK1 { - regulator-name = "BUCK1"; - regulator-min-microvolt = <610000>; - regulator-max-microvolt = <950000>; - regulator-boot-on; - regulator-always-on; - regulator-ramp-delay = <3125>; - }; - - buck2: BUCK2 { - regulator-name = "BUCK2"; - regulator-min-microvolt = <600000>; - regulator-max-microvolt = <670000>; - regulator-boot-on; - regulator-always-on; - regulator-ramp-delay = <3125>; - }; - - buck4: BUCK4 { - regulator-name = "BUCK4"; - regulator-min-microvolt = <1620000>; - regulator-max-microvolt = <3400000>; - regulator-boot-on; - regulator-always-on; - }; - - buck5: BUCK5 { - regulator-name = "BUCK5"; - regulator-min-microvolt = <1620000>; - regulator-max-microvolt = <3400000>; - regulator-boot-on; - regulator-always-on; - }; - - buck6: BUCK6 { - regulator-name = "BUCK6"; - regulator-min-microvolt = <1060000>; - regulator-max-microvolt = <1140000>; - regulator-boot-on; - regulator-always-on; - }; - - ldo1: LDO1 { - regulator-name = "LDO1"; - regulator-min-microvolt = <1620000>; - regulator-max-microvolt = <1980000>; - regulator-boot-on; - regulator-always-on; - }; - - ldo4: LDO4 { - regulator-name = "LDO4"; - regulator-min-microvolt = <800000>; - regulator-max-microvolt = <840000>; - regulator-boot-on; - regulator-always-on; - }; - - ldo5: LDO5 { - regulator-name = "LDO5"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - regulator-always-on; - }; - }; - }; - - adp5585: io-expander@34 { - compatible = "adi,adp5585-00", "adi,adp5585"; - reg = <0x34>; - vdd-supply = <&buck4>; - gpio-controller; - #gpio-cells = <2>; - gpio-reserved-ranges = <5 1>; - #pwm-cells = <3>; - }; -}; - -&lpi2c3 { - clock-frequency = <400000>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_lpi2c3>; - status = "okay"; - - adp5585_isp: io-expander@34 { - compatible = "adi,adp5585-01", "adi,adp5585"; - reg = <0x34>; - gpio-controller; - #gpio-cells = <2>; - #pwm-cells = <3>; - }; - - ptn5110: tcpc@50 { - compatible = "nxp,ptn5110", "tcpci"; - reg = <0x50>; - interrupt-parent = <&gpio3>; - interrupts = <27 IRQ_TYPE_LEVEL_LOW>; - - typec1_con: connector { - compatible = "usb-c-connector"; - label = "USB-C"; - power-role = "dual"; - data-role = "dual"; - try-power-role = "sink"; - source-pdos = ; - sink-pdos = ; - op-sink-microwatt = <15000000>; - self-powered; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - typec1_dr_sw: endpoint { - remote-endpoint = <&usb1_drd_sw>; - }; - }; - }; - }; - }; - - ptn5110_2: tcpc@51 { - compatible = "nxp,ptn5110", "tcpci"; - reg = <0x51>; - interrupt-parent = <&gpio3>; - interrupts = <27 IRQ_TYPE_LEVEL_LOW>; - - typec2_con: connector { - compatible = "usb-c-connector"; - label = "USB-C"; - power-role = "dual"; - data-role = "dual"; - try-power-role = "sink"; - source-pdos = ; - sink-pdos = ; - op-sink-microwatt = <15000000>; - self-powered; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - typec2_dr_sw: endpoint { - remote-endpoint = <&usb2_drd_sw>; - }; - }; - }; - }; - }; - - pcf2131: rtc@53 { - compatible = "nxp,pcf2131"; - reg = <0x53>; - interrupt-parent = <&pcal6524>; - interrupts = <1 IRQ_TYPE_EDGE_FALLING>; - }; -}; - -&lpuart1 { /* console */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart1>; - status = "okay"; -}; - -&lpuart5 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart5>; - status = "okay"; - - bluetooth { - compatible = "nxp,88w8987-bt"; - }; }; &micfil { @@ -558,12 +96,12 @@ status = "okay"; }; -&mu1 { - status = "okay"; -}; - -&mu2 { - status = "okay"; +&pcal6524 { + m2-pcm-level-shifter-hog { + gpio-hog; + gpios = <19 GPIO_ACTIVE_HIGH>; + output-high; + }; }; &sai1 { @@ -577,76 +115,6 @@ status = "okay"; }; -&sai3 { - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&pinctrl_sai3>; - pinctrl-1 = <&pinctrl_sai3_sleep>; - assigned-clocks = <&clk IMX93_CLK_SAI3>; - assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>; - assigned-clock-rates = <12288000>; - fsl,sai-mclk-direction-output; - status = "okay"; -}; - -&usbotg1 { - dr_mode = "otg"; - hnp-disable; - srp-disable; - adp-disable; - usb-role-switch; - disable-over-current; - samsung,picophy-pre-emp-curr-control = <3>; - samsung,picophy-dc-vol-level-adjust = <7>; - status = "okay"; - - port { - usb1_drd_sw: endpoint { - remote-endpoint = <&typec1_dr_sw>; - }; - }; -}; - -&usbotg2 { - dr_mode = "otg"; - hnp-disable; - srp-disable; - adp-disable; - usb-role-switch; - disable-over-current; - samsung,picophy-pre-emp-curr-control = <3>; - samsung,picophy-dc-vol-level-adjust = <7>; - status = "okay"; - - port { - usb2_drd_sw: endpoint { - remote-endpoint = <&typec2_dr_sw>; - }; - }; -}; - -&usdhc1 { - pinctrl-names = "default", "state_100mhz", "state_200mhz"; - pinctrl-0 = <&pinctrl_usdhc1>; - pinctrl-1 = <&pinctrl_usdhc1_100mhz>; - pinctrl-2 = <&pinctrl_usdhc1_200mhz>; - bus-width = <8>; - non-removable; - status = "okay"; -}; - -&usdhc2 { - pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep"; - pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>; - pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>; - pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>; - pinctrl-3 = <&pinctrl_usdhc2_sleep>, <&pinctrl_usdhc2_gpio_sleep>; - cd-gpios = <&gpio3 00 GPIO_ACTIVE_LOW>; - vmmc-supply = <®_usdhc2_vmmc>; - bus-width = <4>; - status = "okay"; - no-mmc; -}; - &usdhc3 { pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep"; pinctrl-0 = <&pinctrl_usdhc3>, <&pinctrl_usdhc3_wlan>; @@ -662,152 +130,7 @@ status = "okay"; }; -&wdog3 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_wdog>; - fsl,ext-reset-output; - status = "okay"; -}; - -&xcvr { - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&pinctrl_spdif>; - pinctrl-1 = <&pinctrl_spdif_sleep>; - assigned-clocks = <&clk IMX93_CLK_SPDIF>, - <&clk IMX93_CLK_AUDIO_XCVR>; - assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>, - <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>; - assigned-clock-rates = <12288000>, <200000000>; - status = "okay"; -}; - &iomuxc { - pinctrl_eqos: eqosgrp { - fsl,pins = < - MX93_PAD_ENET1_MDC__ENET_QOS_MDC 0x57e - MX93_PAD_ENET1_MDIO__ENET_QOS_MDIO 0x57e - MX93_PAD_ENET1_RD0__ENET_QOS_RGMII_RD0 0x57e - MX93_PAD_ENET1_RD1__ENET_QOS_RGMII_RD1 0x57e - MX93_PAD_ENET1_RD2__ENET_QOS_RGMII_RD2 0x57e - MX93_PAD_ENET1_RD3__ENET_QOS_RGMII_RD3 0x57e - MX93_PAD_ENET1_RXC__CCM_ENET_QOS_CLOCK_GENERATE_RX_CLK 0x58e - MX93_PAD_ENET1_RX_CTL__ENET_QOS_RGMII_RX_CTL 0x57e - MX93_PAD_ENET1_TD0__ENET_QOS_RGMII_TD0 0x57e - MX93_PAD_ENET1_TD1__ENET_QOS_RGMII_TD1 0x57e - MX93_PAD_ENET1_TD2__ENET_QOS_RGMII_TD2 0x57e - MX93_PAD_ENET1_TD3__ENET_QOS_RGMII_TD3 0x57e - MX93_PAD_ENET1_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK 0x58e - MX93_PAD_ENET1_TX_CTL__ENET_QOS_RGMII_TX_CTL 0x57e - >; - }; - - pinctrl_eqos_sleep: eqossleepgrp { - fsl,pins = < - MX93_PAD_ENET1_MDC__GPIO4_IO00 0x31e - MX93_PAD_ENET1_MDIO__GPIO4_IO01 0x31e - MX93_PAD_ENET1_RD0__GPIO4_IO10 0x31e - MX93_PAD_ENET1_RD1__GPIO4_IO11 0x31e - MX93_PAD_ENET1_RD2__GPIO4_IO12 0x31e - MX93_PAD_ENET1_RD3__GPIO4_IO13 0x31e - MX93_PAD_ENET1_RXC__GPIO4_IO09 0x31e - MX93_PAD_ENET1_RX_CTL__GPIO4_IO08 0x31e - MX93_PAD_ENET1_TD0__GPIO4_IO05 0x31e - MX93_PAD_ENET1_TD1__GPIO4_IO04 0x31e - MX93_PAD_ENET1_TD2__GPIO4_IO03 0x31e - MX93_PAD_ENET1_TD3__GPIO4_IO02 0x31e - MX93_PAD_ENET1_TXC__GPIO4_IO07 0x31e - MX93_PAD_ENET1_TX_CTL__GPIO4_IO06 0x31e - >; - }; - - pinctrl_fec: fecgrp { - fsl,pins = < - MX93_PAD_ENET2_MDC__ENET1_MDC 0x57e - MX93_PAD_ENET2_MDIO__ENET1_MDIO 0x57e - MX93_PAD_ENET2_RD0__ENET1_RGMII_RD0 0x57e - MX93_PAD_ENET2_RD1__ENET1_RGMII_RD1 0x57e - MX93_PAD_ENET2_RD2__ENET1_RGMII_RD2 0x57e - MX93_PAD_ENET2_RD3__ENET1_RGMII_RD3 0x57e - MX93_PAD_ENET2_RXC__ENET1_RGMII_RXC 0x58e - MX93_PAD_ENET2_RX_CTL__ENET1_RGMII_RX_CTL 0x57e - MX93_PAD_ENET2_TD0__ENET1_RGMII_TD0 0x57e - MX93_PAD_ENET2_TD1__ENET1_RGMII_TD1 0x57e - MX93_PAD_ENET2_TD2__ENET1_RGMII_TD2 0x57e - MX93_PAD_ENET2_TD3__ENET1_RGMII_TD3 0x57e - MX93_PAD_ENET2_TXC__ENET1_RGMII_TXC 0x58e - MX93_PAD_ENET2_TX_CTL__ENET1_RGMII_TX_CTL 0x57e - >; - }; - - pinctrl_fec_sleep: fecsleepgrp { - fsl,pins = < - MX93_PAD_ENET2_MDC__GPIO4_IO14 0x51e - MX93_PAD_ENET2_MDIO__GPIO4_IO15 0x51e - MX93_PAD_ENET2_RD0__GPIO4_IO24 0x51e - MX93_PAD_ENET2_RD1__GPIO4_IO25 0x51e - MX93_PAD_ENET2_RD2__GPIO4_IO26 0x51e - MX93_PAD_ENET2_RD3__GPIO4_IO27 0x51e - MX93_PAD_ENET2_RXC__GPIO4_IO23 0x51e - MX93_PAD_ENET2_RX_CTL__GPIO4_IO22 0x51e - MX93_PAD_ENET2_TD0__GPIO4_IO19 0x51e - MX93_PAD_ENET2_TD1__GPIO4_IO18 0x51e - MX93_PAD_ENET2_TD2__GPIO4_IO17 0x51e - MX93_PAD_ENET2_TD3__GPIO4_IO16 0x51e - MX93_PAD_ENET2_TXC__GPIO4_IO21 0x51e - MX93_PAD_ENET2_TX_CTL__GPIO4_IO20 0x51e - >; - }; - - pinctrl_flexcan2: flexcan2grp { - fsl,pins = < - MX93_PAD_GPIO_IO25__CAN2_TX 0x139e - MX93_PAD_GPIO_IO27__CAN2_RX 0x139e - >; - }; - - pinctrl_uart1: uart1grp { - fsl,pins = < - MX93_PAD_UART1_RXD__LPUART1_RX 0x31e - MX93_PAD_UART1_TXD__LPUART1_TX 0x31e - >; - }; - - pinctrl_uart5: uart5grp { - fsl,pins = < - MX93_PAD_DAP_TDO_TRACESWO__LPUART5_TX 0x31e - MX93_PAD_DAP_TDI__LPUART5_RX 0x31e - MX93_PAD_DAP_TMS_SWDIO__LPUART5_RTS_B 0x31e - MX93_PAD_DAP_TCLK_SWCLK__LPUART5_CTS_B 0x31e - >; - }; - - pinctrl_lpi2c1: lpi2c1grp { - fsl,pins = < - MX93_PAD_I2C1_SCL__LPI2C1_SCL 0x40000b9e - MX93_PAD_I2C1_SDA__LPI2C1_SDA 0x40000b9e - >; - }; - - pinctrl_lpi2c2: lpi2c2grp { - fsl,pins = < - MX93_PAD_I2C2_SCL__LPI2C2_SCL 0x40000b9e - MX93_PAD_I2C2_SDA__LPI2C2_SDA 0x40000b9e - >; - }; - - pinctrl_lpi2c3: lpi2c3grp { - fsl,pins = < - MX93_PAD_GPIO_IO28__LPI2C3_SDA 0x40000b9e - MX93_PAD_GPIO_IO29__LPI2C3_SCL 0x40000b9e - >; - }; - - pinctrl_pcal6524: pcal6524grp { - fsl,pins = < - MX93_PAD_CCM_CLKO2__GPIO3_IO27 0x31e - >; - }; - pinctrl_pdm: pdmgrp { fsl,pins = < MX93_PAD_PDM_CLK__PDM_CLK 0x31e @@ -842,160 +165,6 @@ >; }; - /* need to config the SION for data and cmd pad, refer to ERR052021 */ - pinctrl_usdhc1: usdhc1grp { - fsl,pins = < - MX93_PAD_SD1_CLK__USDHC1_CLK 0x1582 - MX93_PAD_SD1_CMD__USDHC1_CMD 0x40001382 - MX93_PAD_SD1_DATA0__USDHC1_DATA0 0x40001382 - MX93_PAD_SD1_DATA1__USDHC1_DATA1 0x40001382 - MX93_PAD_SD1_DATA2__USDHC1_DATA2 0x40001382 - MX93_PAD_SD1_DATA3__USDHC1_DATA3 0x40001382 - MX93_PAD_SD1_DATA4__USDHC1_DATA4 0x40001382 - MX93_PAD_SD1_DATA5__USDHC1_DATA5 0x40001382 - MX93_PAD_SD1_DATA6__USDHC1_DATA6 0x40001382 - MX93_PAD_SD1_DATA7__USDHC1_DATA7 0x40001382 - MX93_PAD_SD1_STROBE__USDHC1_STROBE 0x1582 - >; - }; - - /* need to config the SION for data and cmd pad, refer to ERR052021 */ - pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { - fsl,pins = < - MX93_PAD_SD1_CLK__USDHC1_CLK 0x158e - MX93_PAD_SD1_CMD__USDHC1_CMD 0x4000138e - MX93_PAD_SD1_DATA0__USDHC1_DATA0 0x4000138e - MX93_PAD_SD1_DATA1__USDHC1_DATA1 0x4000138e - MX93_PAD_SD1_DATA2__USDHC1_DATA2 0x4000138e - MX93_PAD_SD1_DATA3__USDHC1_DATA3 0x4000138e - MX93_PAD_SD1_DATA4__USDHC1_DATA4 0x4000138e - MX93_PAD_SD1_DATA5__USDHC1_DATA5 0x4000138e - MX93_PAD_SD1_DATA6__USDHC1_DATA6 0x4000138e - MX93_PAD_SD1_DATA7__USDHC1_DATA7 0x4000138e - MX93_PAD_SD1_STROBE__USDHC1_STROBE 0x158e - >; - }; - - /* need to config the SION for data and cmd pad, refer to ERR052021 */ - pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { - fsl,pins = < - MX93_PAD_SD1_CLK__USDHC1_CLK 0x15fe - MX93_PAD_SD1_CMD__USDHC1_CMD 0x400013fe - MX93_PAD_SD1_DATA0__USDHC1_DATA0 0x400013fe - MX93_PAD_SD1_DATA1__USDHC1_DATA1 0x400013fe - MX93_PAD_SD1_DATA2__USDHC1_DATA2 0x400013fe - MX93_PAD_SD1_DATA3__USDHC1_DATA3 0x400013fe - MX93_PAD_SD1_DATA4__USDHC1_DATA4 0x400013fe - MX93_PAD_SD1_DATA5__USDHC1_DATA5 0x400013fe - MX93_PAD_SD1_DATA6__USDHC1_DATA6 0x400013fe - MX93_PAD_SD1_DATA7__USDHC1_DATA7 0x400013fe - MX93_PAD_SD1_STROBE__USDHC1_STROBE 0x15fe - >; - }; - - pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { - fsl,pins = < - MX93_PAD_SD2_RESET_B__GPIO3_IO07 0x31e - >; - }; - - pinctrl_sai3: sai3grp { - fsl,pins = < - MX93_PAD_GPIO_IO26__SAI3_TX_SYNC 0x31e - MX93_PAD_GPIO_IO16__SAI3_TX_BCLK 0x31e - MX93_PAD_GPIO_IO17__SAI3_MCLK 0x31e - MX93_PAD_GPIO_IO19__SAI3_TX_DATA00 0x31e - MX93_PAD_GPIO_IO20__SAI3_RX_DATA00 0x31e - >; - }; - - pinctrl_sai3_sleep: sai3sleepgrp { - fsl,pins = < - MX93_PAD_GPIO_IO26__GPIO2_IO26 0x51e - MX93_PAD_GPIO_IO16__GPIO2_IO16 0x51e - MX93_PAD_GPIO_IO17__GPIO2_IO17 0x51e - MX93_PAD_GPIO_IO19__GPIO2_IO19 0x51e - MX93_PAD_GPIO_IO20__GPIO2_IO20 0x51e - >; - }; - - pinctrl_spdif: spdifgrp { - fsl,pins = < - MX93_PAD_GPIO_IO22__SPDIF_IN 0x31e - MX93_PAD_GPIO_IO23__SPDIF_OUT 0x31e - >; - }; - - pinctrl_spdif_sleep: spdifsleepgrp { - fsl,pins = < - MX93_PAD_GPIO_IO22__GPIO2_IO22 0x31e - MX93_PAD_GPIO_IO23__GPIO2_IO23 0x31e - >; - }; - - pinctrl_usdhc2_gpio: usdhc2gpiogrp { - fsl,pins = < - MX93_PAD_SD2_CD_B__GPIO3_IO00 0x31e - >; - }; - - pinctrl_usdhc2_gpio_sleep: usdhc2gpiosleepgrp { - fsl,pins = < - MX93_PAD_SD2_CD_B__GPIO3_IO00 0x51e - >; - }; - - /* need to config the SION for data and cmd pad, refer to ERR052021 */ - pinctrl_usdhc2: usdhc2grp { - fsl,pins = < - MX93_PAD_SD2_CLK__USDHC2_CLK 0x1582 - MX93_PAD_SD2_CMD__USDHC2_CMD 0x40001382 - MX93_PAD_SD2_DATA0__USDHC2_DATA0 0x40001382 - MX93_PAD_SD2_DATA1__USDHC2_DATA1 0x40001382 - MX93_PAD_SD2_DATA2__USDHC2_DATA2 0x40001382 - MX93_PAD_SD2_DATA3__USDHC2_DATA3 0x40001382 - MX93_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e - >; - }; - - /* need to config the SION for data and cmd pad, refer to ERR052021 */ - pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { - fsl,pins = < - MX93_PAD_SD2_CLK__USDHC2_CLK 0x158e - MX93_PAD_SD2_CMD__USDHC2_CMD 0x4000138e - MX93_PAD_SD2_DATA0__USDHC2_DATA0 0x4000138e - MX93_PAD_SD2_DATA1__USDHC2_DATA1 0x4000138e - MX93_PAD_SD2_DATA2__USDHC2_DATA2 0x4000138e - MX93_PAD_SD2_DATA3__USDHC2_DATA3 0x4000138e - MX93_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e - >; - }; - - /* need to config the SION for data and cmd pad, refer to ERR052021 */ - pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { - fsl,pins = < - MX93_PAD_SD2_CLK__USDHC2_CLK 0x15fe - MX93_PAD_SD2_CMD__USDHC2_CMD 0x400013fe - MX93_PAD_SD2_DATA0__USDHC2_DATA0 0x400013fe - MX93_PAD_SD2_DATA1__USDHC2_DATA1 0x400013fe - MX93_PAD_SD2_DATA2__USDHC2_DATA2 0x400013fe - MX93_PAD_SD2_DATA3__USDHC2_DATA3 0x400013fe - MX93_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e - >; - }; - - pinctrl_usdhc2_sleep: usdhc2sleepgrp { - fsl,pins = < - MX93_PAD_SD2_CLK__GPIO3_IO01 0x51e - MX93_PAD_SD2_CMD__GPIO3_IO02 0x51e - MX93_PAD_SD2_DATA0__GPIO3_IO03 0x51e - MX93_PAD_SD2_DATA1__GPIO3_IO04 0x51e - MX93_PAD_SD2_DATA2__GPIO3_IO05 0x51e - MX93_PAD_SD2_DATA3__GPIO3_IO06 0x51e - MX93_PAD_SD2_VSELECT__GPIO3_IO19 0x51e - >; - }; - /* need to config the SION for data and cmd pad, refer to ERR052021 */ pinctrl_usdhc3: usdhc3grp { fsl,pins = < @@ -1048,10 +217,4 @@ MX93_PAD_CCM_CLKO1__GPIO3_IO26 0x31e >; }; - - pinctrl_wdog: wdoggrp { - fsl,pins = < - MX93_PAD_WDOG_ANY__WDOG1_WDOG_ANY 0x31e - >; - }; }; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-frdm-pixpaper.dtso b/arch/arm64/boot/dts/freescale/imx93-11x11-frdm-pixpaper.dtso new file mode 100644 index 000000000000..d220d7e07df2 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-frdm-pixpaper.dtso @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device Tree Overlay for Mayqueen (Open-EP Community) pixpaper display + * support on NXP FRDM i.MX 93 Development Board + * + * Copyright (C) 2026 Wig Cheng + */ + +#include +#include "imx93-pinfunc.h" + +/dts-v1/; +/plugin/; + +&iomuxc { + pinctrl_epd_ctrl: epdctrlgrp { + fsl,pins = < + MX93_PAD_GPIO_IO05__GPIO2_IO05 0x31e /* DC pin */ + MX93_PAD_GPIO_IO06__GPIO2_IO06 0x31e /* RESET pin */ + MX93_PAD_GPIO_IO26__GPIO2_IO26 0x31e /* BUSY pin */ + >; + }; + + pinctrl_lpspi3: lpspi3grp { + fsl,pins = < + MX93_PAD_GPIO_IO08__GPIO2_IO08 0x3fe /* SPI3 CE0 */ + MX93_PAD_GPIO_IO09__LPSPI3_SIN 0x3fe /* SPI3 MISO */ + MX93_PAD_GPIO_IO10__LPSPI3_SOUT 0x3fe /* SPI3 MOSI */ + MX93_PAD_GPIO_IO11__LPSPI3_SCK 0x3fe /* SPI3 CLK */ + >; + }; +}; + +&lpspi3 { + #address-cells = <1>; + #size-cells = <0>; + cs-gpios = <&gpio2 8 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pinctrl_lpspi3>, <&pinctrl_epd_ctrl>; + pinctrl-names = "default"; + status = "okay"; + + display@0 { + compatible = "mayqueen,pixpaper"; + reg = <0>; + busy-gpios = <&gpio2 26 GPIO_ACTIVE_HIGH>; + dc-gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>; + spi-max-frequency = <1000000>; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk.dts index 61843b2c1b1b..ec78c03f4788 100644 --- a/arch/arm64/boot/dts/freescale/imx93-14x14-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk.dts @@ -25,6 +25,7 @@ mmc1 = &usdhc2; rtc0 = &bbnsm_rtc; serial0 = &lpuart1; + serial4 = &lpuart5; }; bt_sco_codec: bt-sco-codec { @@ -400,6 +401,17 @@ status = "okay"; }; +&lpuart5 { + /* BT */ + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart5>; + status = "okay"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; +}; + &mu1 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-ontat-kd50g21-40nt-a1.dtso b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-ontat-kd50g21-40nt-a1.dtso new file mode 100644 index 000000000000..d167c9fc3b8f --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-ontat-kd50g21-40nt-a1.dtso @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2026 NXP + */ + +/dts-v1/; +/plugin/; + +#include +#include "imx93-pinfunc.h" + +&{/} { + backlight: backlight { + compatible = "gpio-backlight"; + gpios = <&pcal6524 2 GPIO_ACTIVE_HIGH>; + }; + + panel { + compatible = "ontat,kd50g21-40nt-a1"; + backlight = <&backlight>; + power-supply = <®_rpi_3v3>; + + port { + panel_in: endpoint { + remote-endpoint = <&dpi_to_panel>; + }; + }; + }; +}; + +&dpi_bridge { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lcdif>; + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + + dpi_to_panel: endpoint { + remote-endpoint = <&panel_in>; + bus-width = <18>; + }; + }; + }; +}; + +&iomuxc { + pinctrl_lcdif: lcdifgrp { + fsl,pins = < + MX93_PAD_GPIO_IO00__MEDIAMIX_DISP_CLK 0x31e + MX93_PAD_GPIO_IO01__MEDIAMIX_DISP_DE 0x31e + MX93_PAD_GPIO_IO02__MEDIAMIX_DISP_VSYNC 0x31e + MX93_PAD_GPIO_IO03__MEDIAMIX_DISP_HSYNC 0x31e + MX93_PAD_GPIO_IO04__MEDIAMIX_DISP_DATA00 0x31e + MX93_PAD_GPIO_IO05__MEDIAMIX_DISP_DATA01 0x31e + MX93_PAD_GPIO_IO06__MEDIAMIX_DISP_DATA02 0x31e + MX93_PAD_GPIO_IO07__MEDIAMIX_DISP_DATA03 0x31e + MX93_PAD_GPIO_IO08__MEDIAMIX_DISP_DATA04 0x31e + MX93_PAD_GPIO_IO09__MEDIAMIX_DISP_DATA05 0x31e + MX93_PAD_GPIO_IO10__MEDIAMIX_DISP_DATA06 0x31e + MX93_PAD_GPIO_IO11__MEDIAMIX_DISP_DATA07 0x31e + MX93_PAD_GPIO_IO12__MEDIAMIX_DISP_DATA08 0x31e + MX93_PAD_GPIO_IO13__MEDIAMIX_DISP_DATA09 0x31e + MX93_PAD_GPIO_IO14__MEDIAMIX_DISP_DATA10 0x31e + MX93_PAD_GPIO_IO15__MEDIAMIX_DISP_DATA11 0x31e + MX93_PAD_GPIO_IO16__MEDIAMIX_DISP_DATA12 0x31e + MX93_PAD_GPIO_IO17__MEDIAMIX_DISP_DATA13 0x31e + MX93_PAD_GPIO_IO18__MEDIAMIX_DISP_DATA14 0x31e + MX93_PAD_GPIO_IO19__MEDIAMIX_DISP_DATA15 0x31e + MX93_PAD_GPIO_IO20__MEDIAMIX_DISP_DATA16 0x31e + MX93_PAD_GPIO_IO21__MEDIAMIX_DISP_DATA17 0x31e + >; + }; +}; + +&lcdif { + status = "okay"; +}; + +&media_blk_ctrl { + status = "okay"; +}; + +&pcal6524 { + /* + * exp-sel-hog has property 'output-low' while DT overlay doesn't + * support /delete-property/. Both 'output-low' and 'output-high' + * will exist under hog nodes if DT overlay file sets 'output-high'. + * Workaround is to disable this hog and create new hog with + * 'output-high'. + */ + exp-sel-hog { + status = "disabled"; + }; + + exp-high-sel-hog { + gpio-hog; + gpios = <22 GPIO_ACTIVE_HIGH>; + output-high; + }; +}; + +&sai3 { + /* disable due to GPIO12 and GPIO17~20 pin conflicts with LCDIF */ + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts index 197c8f8b7f66..7bcebd702106 100644 --- a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts +++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts @@ -349,6 +349,12 @@ gpios = <17 GPIO_ACTIVE_HIGH>; output-low; }; + + m2-pcm-level-shifter-hog { + gpio-hog; + gpios = <19 GPIO_ACTIVE_HIGH>; + output-high; + }; }; pmic@25 { diff --git a/arch/arm64/boot/dts/freescale/imx93-phyboard-nash.dts b/arch/arm64/boot/dts/freescale/imx93-phyboard-nash.dts index 9e875e082ee8..eac389ed30f3 100644 --- a/arch/arm64/boot/dts/freescale/imx93-phyboard-nash.dts +++ b/arch/arm64/boot/dts/freescale/imx93-phyboard-nash.dts @@ -144,8 +144,11 @@ /* I2C2 */ &lpi2c2 { clock-frequency = <400000>; - pinctrl-names = "default"; + pinctrl-names = "default", "gpio"; pinctrl-0 = <&pinctrl_lpi2c2>; + pinctrl-1 = <&pinctrl_lpi2c2_gpio>; + scl-gpios = <&gpio1 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio1 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; status = "okay"; /* RTC */ @@ -277,6 +280,13 @@ >; }; + pinctrl_lpi2c2_gpio: lpi2c2gpiogrp { + fsl,pins = < + MX93_PAD_I2C2_SCL__GPIO1_IO02 0x31e + MX93_PAD_I2C2_SDA__GPIO1_IO03 0x31e + >; + }; + pinctrl_lpspi6: lpspi6grp { fsl,pins = < MX93_PAD_GPIO_IO00__GPIO2_IO00 0x386 diff --git a/arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-av-02.dtso b/arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-av-02.dtso new file mode 100644 index 000000000000..af330756abfd --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-phyboard-segin-peb-av-02.dtso @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2025 PHYTEC Messtechnik GmbH + * Copyright (C) 2025 Pengutronix + * + * Author: Andrej Picej + * Author: Marco Felsch + */ + +#include +#include +#include +#include "imx93-pinfunc.h" + +/dts-v1/; +/plugin/; + +&{/} { + backlight: backlight { + compatible = "pwm-backlight"; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <5>; + power-supply = <®_vcc_3v3_con>; + pwms = <&pwm7 0 5000000 0>; + }; + + panel { + compatible = "edt,etm0700g0edh6"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_panel>; + + backlight = <&backlight>; + enable-gpios = <&gpio4 29 GPIO_ACTIVE_HIGH>; + power-supply = <®_vcc_3v3_con>; + + port { + panel_in: endpoint { + remote-endpoint = <&dpi_to_panel>; + }; + }; + }; + + /* TODO: Convert to FlexIO PWM once supported */ + pwm7: pwm-7 { + compatible = "pwm-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm7>; + gpios = <&gpio4 28 GPIO_ACTIVE_HIGH>; + #pwm-cells = <3>; + }; + + reg_vcc_3v3_con: regulator-vcc-3v3-con { + compatible = "regulator-fixed"; + regulator-name = "VCC3V3_CON"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + }; +}; + +&dpi_bridge { + status = "okay"; +}; + +&dpi_to_panel { + remote-endpoint = <&panel_in>; +}; + +&lcdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lcdif>; + assigned-clocks = <&clk IMX93_CLK_VIDEO_PLL>; + assigned-clock-rates = <332600000>; + status = "okay"; +}; + +&lpi2c2 { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen@38 { + compatible = "edt,edt-ft5406"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_touchscreen>; + interrupt-parent = <&gpio4>; + interrupts = <12 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpio4 1 GPIO_ACTIVE_LOW>; + vcc-supply = <®_vcc_3v3_con>; + iovcc-supply = <®_vcc_3v3_con>; + touchscreen-size-x = <1792>; + touchscreen-size-y = <1024>; + wakeup-source; + }; +}; + +&media_blk_ctrl { + status = "okay"; +}; + +&iomuxc { + pinctrl_lcdif: lcdifgrp { + fsl,pins = < + MX93_PAD_GPIO_IO00__MEDIAMIX_DISP_CLK 0x50e + MX93_PAD_GPIO_IO01__MEDIAMIX_DISP_DE 0x50e + MX93_PAD_GPIO_IO02__MEDIAMIX_DISP_VSYNC 0x50e + MX93_PAD_GPIO_IO03__MEDIAMIX_DISP_HSYNC 0x50e + MX93_PAD_GPIO_IO04__MEDIAMIX_DISP_DATA00 0x50e + MX93_PAD_GPIO_IO05__MEDIAMIX_DISP_DATA01 0x50e + MX93_PAD_GPIO_IO06__MEDIAMIX_DISP_DATA02 0x50e + MX93_PAD_GPIO_IO07__MEDIAMIX_DISP_DATA03 0x50e + MX93_PAD_GPIO_IO08__MEDIAMIX_DISP_DATA04 0x50e + MX93_PAD_GPIO_IO09__MEDIAMIX_DISP_DATA05 0x51e + MX93_PAD_GPIO_IO10__MEDIAMIX_DISP_DATA06 0x50e + MX93_PAD_GPIO_IO11__MEDIAMIX_DISP_DATA07 0x50e + MX93_PAD_GPIO_IO12__MEDIAMIX_DISP_DATA08 0x50e + MX93_PAD_GPIO_IO13__MEDIAMIX_DISP_DATA09 0x50e + MX93_PAD_GPIO_IO14__MEDIAMIX_DISP_DATA10 0x50e + MX93_PAD_GPIO_IO15__MEDIAMIX_DISP_DATA11 0x50e + MX93_PAD_GPIO_IO16__MEDIAMIX_DISP_DATA12 0x506 + MX93_PAD_GPIO_IO17__MEDIAMIX_DISP_DATA13 0x506 + MX93_PAD_GPIO_IO18__MEDIAMIX_DISP_DATA14 0x506 + MX93_PAD_GPIO_IO19__MEDIAMIX_DISP_DATA15 0x506 + MX93_PAD_GPIO_IO20__MEDIAMIX_DISP_DATA16 0x506 + MX93_PAD_GPIO_IO21__MEDIAMIX_DISP_DATA17 0x506 + >; + }; + + pinctrl_panel: panelgrp { + fsl,pins = < + MX93_PAD_CCM_CLKO4__GPIO4_IO29 0x1133e + >; + }; + + pinctrl_pwm7: pwm7grp { + fsl,pins = < + MX93_PAD_CCM_CLKO3__GPIO4_IO28 0x1133e + >; + }; + + pinctrl_touchscreen: touchscreengrp { + fsl,pins = < + MX93_PAD_ENET1_MDIO__GPIO4_IO01 0x11e + MX93_PAD_ENET1_RD2__GPIO4_IO12 0x1133e + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-phyboard-segin.dts b/arch/arm64/boot/dts/freescale/imx93-phyboard-segin.dts index ac64abacc4a2..a982606de1ee 100644 --- a/arch/arm64/boot/dts/freescale/imx93-phyboard-segin.dts +++ b/arch/arm64/boot/dts/freescale/imx93-phyboard-segin.dts @@ -148,8 +148,11 @@ /* I2C2 */ &lpi2c2 { clock-frequency = <400000>; - pinctrl-names = "default"; + pinctrl-names = "default", "gpio"; pinctrl-0 = <&pinctrl_lpi2c2>; + pinctrl-1 = <&pinctrl_lpi2c2_gpio>; + scl-gpios = <&gpio1 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio1 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; status = "okay"; /* Codec */ @@ -262,6 +265,13 @@ >; }; + pinctrl_lpi2c2_gpio: lpi2c2gpiogrp { + fsl,pins = < + MX93_PAD_I2C2_SCL__GPIO1_IO02 0x31e + MX93_PAD_I2C2_SDA__GPIO1_IO03 0x31e + >; + }; + pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { fsl,pins = < MX93_PAD_SD2_RESET_B__GPIO3_IO07 0x31e diff --git a/arch/arm64/boot/dts/freescale/imx93-phycore-som.dtsi b/arch/arm64/boot/dts/freescale/imx93-phycore-som.dtsi index 3f069905cf0b..ebc57841f27f 100644 --- a/arch/arm64/boot/dts/freescale/imx93-phycore-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx93-phycore-som.dtsi @@ -93,8 +93,11 @@ /* I2C3 */ &lpi2c3 { clock-frequency = <400000>; - pinctrl-names = "default"; + pinctrl-names = "default", "gpio"; pinctrl-0 = <&pinctrl_lpi2c3>; + pinctrl-1 = <&pinctrl_lpi2c3_gpio>; + scl-gpios = <&gpio2 29 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio2 28 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; status = "okay"; pmic@25 { @@ -234,6 +237,13 @@ >; }; + pinctrl_lpi2c3_gpio: lpi2c3gpiogrp { + fsl,pins = < + MX93_PAD_GPIO_IO28__GPIO2_IO28 0x31e + MX93_PAD_GPIO_IO29__GPIO2_IO29 0x31e + >; + }; + pinctrl_pmic: pmicgrp { fsl,pins = < MX93_PAD_ENET2_RD3__GPIO4_IO27 0x31e diff --git a/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba91xxca-lvds-tm070jvhg33.dtso b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba91xxca-lvds-tm070jvhg33.dtso new file mode 100644 index 000000000000..40a519e9c91e --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba91xxca-lvds-tm070jvhg33.dtso @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) +/* + * Copyright (c) 2023-2026 TQ-Systems GmbH , + * D-82229 Seefeld, Germany. + * Author: Alexander Stein + */ + +#include + +/dts-v1/; +/plugin/; + +&backlight { + status = "okay"; +}; + +&display { + compatible = "tianma,tm070jvhg33"; + status = "okay"; +}; + +&dpi_bridge { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rgbdisp>; + status = "okay"; +}; + +&dpi_to_panel { + remote-endpoint = <&lvds_encoder_input>; +}; + +&lcdif { + assigned-clocks = <&clk IMX93_CLK_VIDEO_PLL>; + assigned-clock-rates = <477400000>; + status = "okay"; +}; + +&lvds_encoder { + status = "okay"; +}; + +&lvds_encoder_input { + remote-endpoint = <&dpi_to_panel>; +}; + +&lvds_encoder_output { + remote-endpoint = <&panel_in>; +}; + +&media_blk_ctrl { + status = "okay"; +}; + +&panel_in { + remote-endpoint = <&lvds_encoder_output>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba91xxca-rgb-cdtech-dc44.dtso b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba91xxca-rgb-cdtech-dc44.dtso new file mode 100644 index 000000000000..869e3ad1d828 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba91xxca-rgb-cdtech-dc44.dtso @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) +/* + * Copyright (c) 2023-2026 TQ-Systems GmbH , + * D-82229 Seefeld, Germany. + * Author: Alexander Stein + */ + +#include +#include +#include + +/dts-v1/; +/plugin/; + +&backlight { + status = "okay"; +}; + +&display { + compatible = "cdtech,s070swv29hg-dc44"; + status = "okay"; +}; + +&dpi_bridge { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rgbdisp>; + status = "okay"; +}; + +&dpi_to_panel { + remote-endpoint = <&panel_in>; +}; + +&lcdif { + assigned-clocks = <&clk IMX93_CLK_VIDEO_PLL>; + assigned-clock-rates = <333333333>; + status = "okay"; +}; + +&lpi2c3 { + #address-cells = <1>; + #size-cells = <0>; + + polytouch: touchscreen@38 { + compatible = "edt,edt-ft5406"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_touch>; + interrupt-parent = <&gpio1>; + interrupts = <11 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&expander2 0 GPIO_ACTIVE_LOW>; + iovcc-supply = <®_3v3>; + vcc-supply = <®_3v3>; + gain = <20>; + touchscreen-size-x = <800>; + touchscreen-size-y = <480>; + }; +}; + +&media_blk_ctrl { + status = "okay"; +}; + +&panel_in { + remote-endpoint = <&dpi_to_panel>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba91xxca.dts b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba91xxca.dts index 2673d9dccbf4..737326ba1b2a 100644 --- a/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba91xxca.dts +++ b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba91xxca.dts @@ -232,7 +232,7 @@ reset-assert-us = <500000>; reset-deassert-us = <50000>; interrupt-parent = <&gpio3>; - interrupts = <26 IRQ_TYPE_EDGE_FALLING>; + interrupts = <26 IRQ_TYPE_LEVEL_LOW>; enet-phy-lane-no-swap; ti,rx-internal-delay = ; ti,tx-internal-delay = ; @@ -265,7 +265,7 @@ reset-assert-us = <500000>; reset-deassert-us = <50000>; interrupt-parent = <&gpio3>; - interrupts = <27 IRQ_TYPE_EDGE_FALLING>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; enet-phy-lane-no-swap; ti,rx-internal-delay = ; ti,tx-internal-delay = ; diff --git a/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxca.dts b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxca.dts index 4760d07ea24b..9108181e6592 100644 --- a/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxca.dts +++ b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxca.dts @@ -242,7 +242,7 @@ reset-assert-us = <500000>; reset-deassert-us = <50000>; interrupt-parent = <&gpio3>; - interrupts = <26 IRQ_TYPE_EDGE_FALLING>; + interrupts = <26 IRQ_TYPE_LEVEL_LOW>; enet-phy-lane-no-swap; ti,rx-internal-delay = ; ti,tx-internal-delay = ; @@ -275,7 +275,7 @@ reset-assert-us = <500000>; reset-deassert-us = <50000>; interrupt-parent = <&gpio3>; - interrupts = <27 IRQ_TYPE_EDGE_FALLING>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; enet-phy-lane-no-swap; ti,rx-internal-delay = ; ti,tx-internal-delay = ; diff --git a/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxla-mini-ezurio-wlan.dtso b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxla-mini-ezurio-wlan.dtso new file mode 100644 index 000000000000..12a14d871103 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxla-mini-ezurio-wlan.dtso @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) +/* + * Copyright (c) 2025-2026 TQ-Systems GmbH , + * D-82229 Seefeld, Germany. + * Author: Martin Schmiedel + */ + +#include +#include + +/dts-v1/; +/plugin/; + +&lpuart7 { + status = "okay"; + + bluetooth { + compatible = "brcm,bcm4329-bt"; + vbat-supply = <®_3v3>; + vddio-supply = <®_3v3>; + shutdown-gpios = <&expander0 5 GPIO_ACTIVE_HIGH>; + }; +}; + +&usdhc3 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + brcmf_sdio: wifi@1 { + compatible = "brcm,bcm4329-fmac"; + reg = <1>; + reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxla-mini.dts b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxla-mini.dts new file mode 100644 index 000000000000..4afd6b650d9d --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxla-mini.dts @@ -0,0 +1,598 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) +/* + * Copyright (c) 2025-2026 TQ-Systems GmbH , + * D-82229 Seefeld, Germany. + * Author: Martin Schmiedel + */ +/dts-v1/; + +#include +#include +#include +#include +#include + +#include "imx93-tqma9352.dtsi" + +/{ + model = "TQ-Systems i.MX93 TQMa93xxLA on MBa93xxLA-MINI SBC"; + compatible = "tq,imx93-tqma9352-mba93xxla-mini", + "tq,imx93-tqma9352", "fsl,imx93"; + chassis-type = "embedded"; + + chosen { + stdout-path = &lpuart1; + }; + + aliases { + eeprom0 = &eeprom0; + ethernet0 = &eqos; + ethernet1 = &fec; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; + gpio3 = &gpio4; + i2c0 = &lpi2c1; + i2c1 = &lpi2c2; + i2c2 = &lpi2c3; + i2c3 = &lpi2c4; + i2c4 = &lpi2c5; + mmc0 = &usdhc1; + mmc1 = &usdhc2; + mmc2 = &usdhc3; + rtc0 = &pcf85063; + rtc1 = &bbnsm_rtc; + serial0 = &lpuart1; + serial1 = &lpuart2; + serial2 = &lpuart3; + serial3 = &lpuart4; + serial4 = &lpuart5; + serial5 = &lpuart6; + serial6 = &lpuart7; + serial7 = &lpuart8; + spi0 = &lpspi1; + spi1 = &lpspi2; + spi2 = &lpspi3; + spi3 = &lpspi4; + spi4 = &lpspi5; + spi5 = &lpspi6; + }; + + iio-hwmon { + compatible = "iio-hwmon"; + io-channels = <&adc1 0>, <&adc1 1>, <&adc1 2>, <&adc1 3>; + }; + + reg_3v3: regulator-3v3 { + compatible = "regulator-fixed"; + regulator-name = "V_3V3_MB"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_5v0_usb: regulator-5v0-usb { + compatible = "regulator-fixed"; + regulator-name = "V_5V0_HUB"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&expander1 0 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_12v0: regulator-12v0 { + compatible = "regulator-fixed"; + regulator-name = "V_12V"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + gpio = <&expander0 7 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; +}; + +&adc1 { + status = "okay"; +}; + +&eqos { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_eqos>; + phy-mode = "rgmii-id"; + phy-handle = <ðphy_eqos>; + status = "okay"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + ethphy_eqos: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_eqos_phy>; + interrupt-parent = <&gpio3>; + interrupts = <26 IRQ_TYPE_LEVEL_LOW>; + reset-gpios = <&expander0 0 GPIO_ACTIVE_LOW>; + reset-assert-us = <500000>; + reset-deassert-us = <50000>; + enet-phy-lane-no-swap; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; + ti,fifo-depth = ; + ti,dp83867-rxctrl-strap-quirk; + ti,clk-output-sel = ; + }; + }; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec>; + phy-mode = "rgmii-id"; + phy-handle = <ðphy_fec>; + fsl,magic-packet; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <5000000>; + + ethphy_fec: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec_phy>; + interrupt-parent = <&gpio3>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; + reset-gpios = <&expander0 1 GPIO_ACTIVE_LOW>; + reset-assert-us = <500000>; + reset-deassert-us = <50000>; + enet-phy-lane-no-swap; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; + ti,fifo-depth = ; + ti,dp83867-rxctrl-strap-quirk; + ti,clk-output-sel = ; + }; + }; +}; + +&flexcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan1>; + status = "okay"; +}; + +&flexcan2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan2>; + status = "okay"; +}; + +/* deactivated because pins are used for SDIO */ +&flexspi1 { + status = "disabled"; +}; + +&gpio1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_m2_key_b>, <&pinctrl_m2_key_e>; + + gpio-line-names = + /* 00 */ "", "", "M2_KEYE_ALERT#", "", + /* 04 */ "", "", "M2_KEYE_UART_WAKE#", "BM1_M2_KEYE_SDIO_WAKE#", + /* 08 */ "", "", "", "BM2_M2_KEYE_SDIO_RST#", + /* 12 */ "M2_KEYB_WOWWAN#", "BM3_M2_KEYB_PEWAKE#", "", "", + /* 16 */ "", "", "", "", + /* 20 */ "", "", "", "", + /* 24 */ "", "", "", "", + /* 28 */ "", "", "", ""; +}; + +&gpio2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio2>; + + gpio-line-names = + /* 00 */ "", "", "", "", + /* 04 */ "LVDS_RESET#", "LVDS_BLT_EN", "", "LVDS_PWR_EN", + /* 08 */ "", "", "", "", + /* 12 */ "", "", "", "", + /* 16 */ "X1_9", "X1_19", "X1_15", "X1_11", + /* 20 */ "X1_13", "X1_7", "", "CAM_TRIGGER", + /* 24 */ "CAM_SYNC", "", "X1_5", "", + /* 28 */ "", "", "", ""; +}; + +&gpio4 { + gpio-line-names = + /* 00 */ "", "", "", "", + /* 04 */ "", "", "", "", + /* 08 */ "", "", "", "", + /* 12 */ "", "", "", "", + /* 16 */ "", "", "", "", + /* 20 */ "", "", "", "", + /* 24 */ "", "", "", "", + /* 28 */ "", "DSI_GPIO", "", ""; +}; + +&lpi2c3 { + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <400000>; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&pinctrl_lpi2c3>; + pinctrl-1 = <&pinctrl_lpi2c3_gpio>; + scl-gpios = <&gpio2 29 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio2 28 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; + + expander0: gpio@70 { + compatible = "nxp,pca9538"; + reg = <0x70>; + gpio-controller; + #gpio-cells = <2>; + vcc-supply = <®_3v3>; + gpio-line-names = "ENET1_RESET#", "ENET2_RESET#", + "M2_KEYE_PERST#", "M2_KEYB_PERST#", + "M2_KEYE_W_DISABLE1#", "M2_KEYE_W_DISABLE2#", + "M2_KEYA_W_DISABLE1#", "12V_EN"; + }; + + expander1: gpio@71 { + compatible = "nxp,pca9538"; + reg = <0x71>; + gpio-controller; + #gpio-cells = <2>; + vcc-supply = <®_3v3>; + gpio-line-names = "USB_HUB_PWR", "DSI_RST#", + "CAM_PWR#", "CAMRST#", + "M2_KEYB_FULL_CARD_PWR_OFF#", "M2_KEYB_W_DISABLE2#", + "M2_KEYB_RST#", "M2_KEYB_DPR"; + + /* + * Controls the LTE card FULL_CARD_PWR_OFF pin which is low active + * as power down signal. The output-low states, the signal + * is inactive, e.g. not power down + */ + full-card-power-off-hog { + gpio-hog; + gpios = <4 GPIO_ACTIVE_LOW>; + output-low; + line-name = "M2_KEYB_FULL_CARD_PWR_OFF#"; + }; + + /* + * Controls the LTE card reset pin which is low active + * as reset signal. The output-low states, the signal + * is inactive, e.g. not in reset + */ + wlan-perst-hog { + gpio-hog; + gpios = <6 GPIO_ACTIVE_LOW>; + output-low; + line-name = "M2_KEYB_RST#"; + }; + }; +}; + +&lpspi6 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lpspi6>, <&pinctrl_lpspi6_cs>; + cs-gpios = <&gpio2 0 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&lpuart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; +}; + +/* disabled per default, console for M33 */ +&lpuart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3>; + status = "disabled"; +}; + +/* disabled per default, used for bluetooth on M.2 slot */ +&lpuart7 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart7>; + uart-has-rtscts; + status = "disabled"; +}; + +&lpuart8 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart8>; + status = "okay"; +}; + +&pcf85063 { + /* RTC_EVENT# from SoM is connected on mainboard */ + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcf85063>; + interrupt-parent = <&gpio1>; + interrupts = <14 IRQ_TYPE_EDGE_FALLING>; +}; + +&tpm5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tpm5>; +}; + +&usbotg1 { + disable-over-current; + dr_mode = "peripheral"; + samsung,picophy-pre-emp-curr-control = <3>; + samsung,picophy-dc-vol-level-adjust = <7>; + status = "okay"; +}; + +&usbotg2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbhub>; + #address-cells = <1>; + #size-cells = <0>; + disable-over-current; + dr_mode = "host"; + vbus-supply = <®_5v0_usb>; + samsung,picophy-pre-emp-curr-control = <3>; + samsung,picophy-dc-vol-level-adjust = <7>; + status = "okay"; + + hub_2_0: usb-hub@1 { + compatible = "usb424,2517"; + reg = <1>; + reset-gpios = <&gpio2 22 GPIO_ACTIVE_LOW>; + vdd-supply = <®_3v3>; + }; +}; + +&usdhc2 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc2_hs>, <&pinctrl_usdhc2_gpio>; + pinctrl-1 = <&pinctrl_usdhc2_uhs>, <&pinctrl_usdhc2_gpio>; + pinctrl-2 = <&pinctrl_usdhc2_uhs>, <&pinctrl_usdhc2_gpio>; + bus-width = <4>; + cd-gpios = <&gpio3 0 GPIO_ACTIVE_LOW>; + disable-wp; + no-sdio; + no-mmc; + vmmc-supply = <®_usdhc2_vmmc>; + status = "okay"; +}; + +&usdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3>; + bus-width = <4>; + non-removable; + vmmc-supply = <®_3v3>; +}; + +&iomuxc { + pinctrl_eqos: eqosgrp { + fsl,pins = /* PD | FSEL_2 | DSE X4 */ + , + /* SION | HYS | FSEL_2 | DSE X4 */ + , + /* HYS | FSEL_0 | DSE no drive */ + , + , + , + , + , + /* HYS | PD | FSEL_0 | DSE no drive */ + , + /* PD | FSEL_2 | DSE X5 */ + , + , + , + , + , + /* PD | FSEL_3 | DSE X4 */ + ; + }; + + pinctrl_eqos_phy: eqosphygrp { + fsl,pins = /* HYS | FSEL_0 | DSE no drive */ + ; + }; + + pinctrl_fec: fecgrp { + fsl,pins = /* PD | FSEL_2 | DSE X4 */ + , + /* SION | HYS | FSEL_2 | DSE X4 */ + , + /* HYS | FSEL_0 | DSE no drive */ + , + , + , + , + , + /* HYS | PD | FSEL_0 | DSE no drive */ + , + /* PD | FSEL_2 | DSE X5 */ + , + , + , + , + , + /* PD | FSEL_3 | DSE X4 */ + ; + }; + + pinctrl_fec_phy: fecphygrp { + fsl,pins = /* HYS | FSEL_0 | DSE no drive */ + ; + }; + + pinctrl_flexcan1: flexcan1grp { + fsl,pins = /* HYS | PU | FSEL_0 | DSE no drive */ + , + /* PU | FSEL_3 | DSE X4 */ + ; + }; + + pinctrl_flexcan2: flexcan2grp { + fsl,pins = /* HYS | PU | FSEL_0 | DSE no drive */ + , + /* PU | FSEL_3 | DSE X4 */ + ; + }; + + pinctrl_gpio2: gpio2grp { + fsl,pins = /* HYS | PD | FSEL_2 | DSE X4 */ + , + , + , + , + , + , + ; + }; + + pinctrl_jtag: jtaggrp { + fsl,pins = , + , + , + ; + }; + + pinctrl_lpi2c3: lpi2c3grp { + fsl,pins = /* SION | HYS | OD | FSEL_3 | DSE X4 */ + , + ; + }; + + pinctrl_lpi2c3_gpio: lpi2c3-gpiogrp { + fsl,pins = /* SION | HYS | OD | FSEL_3 | DSE X4 */ + , + ; + }; + + pinctrl_lpspi6: lpspi6grp { + fsl,pins = /* HYS | PD | FSEL_0 | DSE no drive */ + , + /* PD | FSEL_2 | DSE X4 */ + , + ; + }; + + pinctrl_lpspi6_cs: lpspi6csgrp { + fsl,pins = /* FSEL_2 | DSE X4 */ + ; + }; + + pinctrl_m2_key_b: m2keybgrp { + fsl,pins = , + ; + }; + + pinctrl_m2_key_e: m2keyegrp { + fsl,pins = , + , + , + ; + }; + + /*CAM_MCLK, DSI_GPIO, CAM_TRIGGER, CAM_SYNC*/ + pinctrl_mipi_csi_dsi: mipi_csi_dsigrp { + fsl,pins = , + , + , + ; + }; + + pinctrl_pcf85063: pcf85063grp { + fsl,pins = /* HYS | FSEL_0 | No DSE */ + ; + }; + + pinctrl_tpm5: tpm5grp { + fsl,pins = ; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = /* HYS | FSEL_0 | No DSE */ + , + /* FSEL_2 | DSE X4 */ + ; + }; + + pinctrl_uart3: uart3grp { + fsl,pins = /* HYS | FSEL_0 | No DSE */ + , + /* FSEL_2 | DSE X4 */ + ; + }; + + pinctrl_uart7: uart7grp { + fsl,pins = , + , + , + ; + }; + + pinctrl_uart8: uart8grp { + fsl,pins = /* HYS | FSEL_0 | No DSE */ + , + /* FSEL_2 | DSE X4 */ + ; + }; + + pinctrl_usbhub: usbhubgrp { + fsl,pins = /* HYS | PD | FSEL_2 | DSE X4 */ + ; + }; + + pinctrl_usdhc2_gpio: usdhc2gpiogrp { + fsl,pins = /* HYS | FSEL_0 | No DSE */ + ; + }; + + /* enable SION for data and cmd pad due to ERR052021 */ + pinctrl_usdhc2_hs: usdhc2hsgrp { + fsl,pins = /* PD | FSEL_3 | DSE X5 */ + , + /* HYS | PU | FSEL_3 | DSE X4 */ + , + /* HYS | PU | FSEL_3 | DSE X3 */ + , + , + , + , + /* FSEL_2 | DSE X3 */ + ; + }; + + /* enable SION for data and cmd pad due to ERR052021 */ + pinctrl_usdhc2_uhs: usdhc2uhsgrp { + fsl,pins = /* PD | FSEL_3 | DSE X6 */ + , + /* HYS | PU | FSEL_3 | DSE X4 */ + , + , + , + , + , + /* FSEL_2 | DSE X3 */ + ; + }; + + /* enable SION for data and cmd pad due to ERR052021 */ + pinctrl_usdhc3: usdhc3grp { + fsl,pins = /* PD | FSEL_3 | DSE X6 */ + , + /* HYS | PU | FSEL_3 | DSE X4 */ + , + , + , + , + ; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxla.dts b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxla.dts index 8a88c98ac05a..a78bbc46c59b 100644 --- a/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxla.dts +++ b/arch/arm64/boot/dts/freescale/imx93-tqma9352-mba93xxla.dts @@ -172,7 +172,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_eqos_phy>; interrupt-parent = <&gpio3>; - interrupts = <26 IRQ_TYPE_EDGE_FALLING>; + interrupts = <26 IRQ_TYPE_LEVEL_LOW>; reset-gpios = <&expander1 0 GPIO_ACTIVE_LOW>; reset-assert-us = <500000>; reset-deassert-us = <50000>; @@ -205,7 +205,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_fec_phy>; interrupt-parent = <&gpio3>; - interrupts = <27 IRQ_TYPE_EDGE_FALLING>; + interrupts = <27 IRQ_TYPE_LEVEL_LOW>; reset-gpios = <&expander1 1 GPIO_ACTIVE_LOW>; reset-assert-us = <500000>; reset-deassert-us = <50000>; diff --git a/arch/arm64/boot/dts/freescale/imx93.dtsi b/arch/arm64/boot/dts/freescale/imx93.dtsi index 7b27012dfcb5..b9abe143cb56 100644 --- a/arch/arm64/boot/dts/freescale/imx93.dtsi +++ b/arch/arm64/boot/dts/freescale/imx93.dtsi @@ -43,6 +43,30 @@ }; }; }; + + soc@0 { + npu@4a900000 { + compatible = "fsl,imx93-npu", "arm,ethos-u65"; + reg = <0x4a900000 0x1000>; + interrupts = ; + power-domains = <&mlmix>; + clocks = <&clk IMX93_CLK_ML>, <&clk IMX93_CLK_ML_APB>; + clock-names = "core", "apb"; + sram = <&sram>; + assigned-clocks = <&clk IMX93_CLK_ML>, <&clk IMX93_CLK_ML_APB>; + assigned-clock-parents = <&clk IMX93_CLK_SYS_PLL_PFD1>, + <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>; + assigned-clock-rates = <800000000>, <133000000>; + }; + }; + + sram: sram@20480000 { + compatible = "mmio-sram"; + reg = <0x0 0x20480000 0x0 0x18000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x20480000 0x18000>; + }; }; &aips1 { @@ -150,6 +174,18 @@ }; }; +&lcdif { + port { + lcdif_to_ldb: endpoint@1 { + reg = <1>; + }; + + lcdif_to_dsi: endpoint@2 { + reg = <2>; + }; + }; +}; + &src { mlmix: power-domain@44461800 { compatible = "fsl,imx93-src-slice"; diff --git a/arch/arm64/boot/dts/freescale/imx93w-evk.dts b/arch/arm64/boot/dts/freescale/imx93w-evk.dts new file mode 100644 index 000000000000..8e53e7384013 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93w-evk.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2026 NXP + */ + +/dts-v1/; + +#include "imx93w.dtsi" +#include "imx93-11x11-evk-common.dtsi" + +/ { + model = "NXP i.MX93W EVK board"; + compatible = "fsl,imx93-wireless-evk", "fsl,imx93"; +}; + +&lpi2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lpi2c3>; +}; + +&iomuxc { + pinctrl_lpi2c3: lpi2c3grp { + fsl,pins = < + MX93_PAD_GPIO_IO00__LPI2C3_SDA 0x40000b9e + MX93_PAD_GPIO_IO01__LPI2C3_SCL 0x40000b9e + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93w.dtsi b/arch/arm64/boot/dts/freescale/imx93w.dtsi new file mode 100644 index 000000000000..95fb025c3949 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93w.dtsi @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2026 NXP + */ + +#include "imx93.dtsi" + +/ { + aliases { + mmc2 = &usdhc3; + }; + + reg_usdhc3_vmmc: regulator-usdhc3 { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_usdhc3_vmmc>; + regulator-name = "WLAN_EN"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio2 29 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + usdhc3_pwrseq: usdhc3_pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3_pwrseq>; + reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; + }; +}; + +&usdhc3 { + pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep"; + pinctrl-0 = <&pinctrl_usdhc3>; + pinctrl-1 = <&pinctrl_usdhc3_100mhz>; + pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + pinctrl-3 = <&pinctrl_usdhc3_sleep>; + mmc-pwrseq = <&usdhc3_pwrseq>; + vmmc-supply = <®_usdhc3_vmmc>; + bus-width = <4>; + keep-power-in-suspend; + non-removable; + wakeup-source; + status = "okay"; +}; + +&iomuxc { + pinctrl_reg_usdhc3_vmmc: regusdhc3vmmcgrp { + fsl,pins = < + /* + * Enable open drain and internal pull-up to allow the IW610 JTAG + * connector to control the PDn status. + */ + MX93_PAD_GPIO_IO29__GPIO2_IO29 0xb9e + >; + }; + + /* need to config the SION for data and cmd pad, refer to ERR052021 */ + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX93_PAD_SD3_CLK__USDHC3_CLK 0x1582 + MX93_PAD_SD3_CMD__USDHC3_CMD 0x40001382 + MX93_PAD_SD3_DATA0__USDHC3_DATA0 0x40001382 + MX93_PAD_SD3_DATA1__USDHC3_DATA1 0x40001382 + MX93_PAD_SD3_DATA2__USDHC3_DATA2 0x40001382 + MX93_PAD_SD3_DATA3__USDHC3_DATA3 0x40001382 + >; + }; + + /* need to config the SION for data and cmd pad, refer to ERR052021 */ + pinctrl_usdhc3_100mhz: usdhc3-100mhzgrp { + fsl,pins = < + MX93_PAD_SD3_CLK__USDHC3_CLK 0x158e + MX93_PAD_SD3_CMD__USDHC3_CMD 0x4000138e + MX93_PAD_SD3_DATA0__USDHC3_DATA0 0x4000138e + MX93_PAD_SD3_DATA1__USDHC3_DATA1 0x4000138e + MX93_PAD_SD3_DATA2__USDHC3_DATA2 0x4000138e + MX93_PAD_SD3_DATA3__USDHC3_DATA3 0x4000138e + >; + }; + + /* need to config the SION for data and cmd pad, refer to ERR052021 */ + pinctrl_usdhc3_200mhz: usdhc3-200mhzgrp { + fsl,pins = < + MX93_PAD_SD3_CLK__USDHC3_CLK 0x15fe + MX93_PAD_SD3_CMD__USDHC3_CMD 0x400013fe + MX93_PAD_SD3_DATA0__USDHC3_DATA0 0x400013fe + MX93_PAD_SD3_DATA1__USDHC3_DATA1 0x400013fe + MX93_PAD_SD3_DATA2__USDHC3_DATA2 0x400013fe + MX93_PAD_SD3_DATA3__USDHC3_DATA3 0x400013fe + >; + }; + + pinctrl_usdhc3_sleep: usdhc3grpsleepgrp { + fsl,pins = < + MX93_PAD_SD3_CLK__GPIO3_IO20 0x31e + MX93_PAD_SD3_CMD__GPIO3_IO21 0x31e + MX93_PAD_SD3_DATA0__GPIO3_IO22 0x31e + MX93_PAD_SD3_DATA1__GPIO3_IO23 0x31e + MX93_PAD_SD3_DATA2__GPIO3_IO24 0x31e + MX93_PAD_SD3_DATA3__GPIO3_IO25 0x31e + >; + }; + + pinctrl_usdhc3_pwrseq: usdhc3pwrseqgrp { + fsl,pins = < + MX93_PAD_PDM_BIT_STREAM1__GPIO1_IO10 0x39e + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx94-pinfunc.h b/arch/arm64/boot/dts/freescale/imx94-pinfunc.h index 00255db89185..d5056e917140 100644 --- a/arch/arm64/boot/dts/freescale/imx94-pinfunc.h +++ b/arch/arm64/boot/dts/freescale/imx94-pinfunc.h @@ -233,6 +233,7 @@ #define IMX94_PAD_GPIO_IO17__GPT_MUX_INOUT3 0x0054 0x0358 0x0704 0x05 0x01 #define IMX94_PAD_GPIO_IO17__FLEXPWM4_PWMB0 0x0054 0x0358 0x06e4 0x06 0x00 #define IMX94_PAD_GPIO_IO17__XBAR1_XBAR_INOUT31 0x0054 0x0358 0x08b4 0x07 0x00 +#define IMX94_PAD_GPIO_IO17__XSPI1_IPP_IND_INTFA_B 0x0054 0x0358 0x0000 0x0100 0x00 #define IMX94_PAD_GPIO_IO18__GPIO2_IO18 0x0058 0x035c 0x0000 0x00 0x00 #define IMX94_PAD_GPIO_IO18__LPI2C4_SDA 0x0058 0x035c 0x0738 0x01 0x00 @@ -242,6 +243,7 @@ #define IMX94_PAD_GPIO_IO18__GPT_MUX_INOUT6 0x0058 0x035c 0x0710 0x05 0x01 #define IMX94_PAD_GPIO_IO18__FLEXPWM4_PWMA1 0x0058 0x035c 0x06d8 0x06 0x00 #define IMX94_PAD_GPIO_IO18__XBAR1_XBAR_INOUT32 0x0058 0x035c 0x08b8 0x07 0x00 +#define IMX94_PAD_GPIO_IO18__USB1_OTG_OC 0x0058 0x035c 0x0000 0x0100 0x00 #define IMX94_PAD_GPIO_IO19__GPIO2_IO19 0x005c 0x0360 0x0000 0x00 0x00 #define IMX94_PAD_GPIO_IO19__LPI2C4_SCL 0x005c 0x0360 0x0734 0x01 0x00 @@ -251,6 +253,7 @@ #define IMX94_PAD_GPIO_IO19__GPT_MUX_INOUT9 0x005c 0x0360 0x071c 0x05 0x01 #define IMX94_PAD_GPIO_IO19__FLEXPWM4_PWMB1 0x005c 0x0360 0x06e8 0x06 0x00 #define IMX94_PAD_GPIO_IO19__XBAR1_XBAR_INOUT33 0x005c 0x0360 0x08bc 0x07 0x00 +#define IMX94_PAD_GPIO_IO19__USB2_OTG_OC 0x005c 0x0360 0x0000 0x0100 0x00 #define IMX94_PAD_GPIO_IO20__GPIO2_IO20 0x0060 0x0364 0x0000 0x00 0x00 #define IMX94_PAD_GPIO_IO20__PCIE1_CLKREQ_B 0x0060 0x0364 0x0000 0x01 0x00 @@ -413,6 +416,7 @@ #define IMX94_PAD_GPIO_IO37__FLEXPWM3_PWMB2 0x00a4 0x03a8 0x06c0 0x05 0x00 #define IMX94_PAD_GPIO_IO37__FLEXPWM2_PWMX1 0x00a4 0x03a8 0x06a4 0x06 0x00 #define IMX94_PAD_GPIO_IO37__XBAR1_XBAR_INOUT13 0x00a4 0x03a8 0x0890 0x07 0x00 +#define IMX94_PAD_GPIO_IO37__XSPI1_IPP_IND_INTFA_B 0x00a4 0x03a8 0x0000 0x0100 0x00 #define IMX94_PAD_GPIO_IO38__GPIO3_IO6 0x00a8 0x03ac 0x0000 0x00 0x00 #define IMX94_PAD_GPIO_IO38__NETC_1588MUX_INOUT0 0x00a8 0x03ac 0x064c 0x01 0x00 @@ -574,6 +578,7 @@ #define IMX94_PAD_GPIO_IO55__TPM4_CH3 0x00ec 0x03f0 0x083c 0x05 0x01 #define IMX94_PAD_GPIO_IO55__SINC3_EMBIT0 0x00ec 0x03f0 0x0000 0x06 0x00 #define IMX94_PAD_GPIO_IO55__XBAR1_XBAR_INOUT19 0x00ec 0x03f0 0x08a8 0x07 0x00 +#define IMX94_PAD_GPIO_IO55__XSPI1_IPP_IND_INTFA_B 0x00ec 0x03f0 0x0000 0x0100 0x00 #define IMX94_PAD_GPIO_IO56__GPIO3_IO24 0x00f0 0x03f4 0x0000 0x00 0x00 #define IMX94_PAD_GPIO_IO56__NETC_1588MUX_INOUT6 0x00f0 0x03f4 0x0664 0x01 0x00 @@ -592,6 +597,8 @@ #define IMX94_PAD_GPIO_IO57__TPM6_CH3 0x00f4 0x03f8 0x084c 0x05 0x01 #define IMX94_PAD_GPIO_IO57__SINC3_EMBIT1 0x00f4 0x03f8 0x0000 0x06 0x00 #define IMX94_PAD_GPIO_IO57__ENET_REF_CLK_ROOT 0x00f4 0x03f8 0x0000 0x07 0x00 +#define IMX94_PAD_GPIO_IO57__XBAR1_XBAR_INOUT21 0x00f4 0x03f8 0x0000 0x0100 0x00 +#define IMX94_PAD_GPIO_IO57__SAI3_RX_SYNC 0x00f4 0x03f8 0x0000 0x0200 0x00 #define IMX94_PAD_CCM_CLKO1__CLKO_1 0x00f8 0x03fc 0x0000 0x00 0x00 #define IMX94_PAD_CCM_CLKO1__NETC_1588MUX_INOUT8 0x00f8 0x03fc 0x066c 0x01 0x00 @@ -619,6 +626,7 @@ #define IMX94_PAD_CCM_CLKO3__GPIO4_IO2 0x0100 0x0404 0x0000 0x05 0x00 #define IMX94_PAD_CCM_CLKO3__SINC3_EMCLK3 0x0100 0x0404 0x0000 0x06 0x00 #define IMX94_PAD_CCM_CLKO3__ENET_REF_CLK_ROOT 0x0100 0x0404 0x0000 0x07 0x00 +#define IMX94_PAD_CCM_CLKO3__XBAR1_XBAR_INOUT24 0x0100 0x0404 0x0000 0x0105 0x00 #define IMX94_PAD_CCM_CLKO4__CLKO_4 0x0104 0x0408 0x0000 0x00 0x00 #define IMX94_PAD_CCM_CLKO4__NETC_1588MUX_INOUT11 0x0104 0x0408 0x0000 0x01 0x00 @@ -872,6 +880,7 @@ #define IMX94_PAD_ETH4_MDIO_GPIO2__GPIO6_IO29 0x017c 0x0480 0x0000 0x05 0x00 #define IMX94_PAD_ETH4_MDIO_GPIO2__FLEXPWM4_PWMX1 0x017c 0x0480 0x06f8 0x06 0x02 #define IMX94_PAD_ETH4_MDIO_GPIO2__SINC_FILTER_GLUE4_BREAK 0x017c 0x0480 0x0000 0x07 0x00 +#define IMX94_PAD_ETH4_MDIO_GPIO2__XSPI2_IPP_IND_INTFA_B 0x017c 0x0480 0x0000 0x0105 0x00 #define IMX94_PAD_ETH4_TX_CLK__NETC_PINMUX_ETH4_TX_CLK 0x0180 0x0484 0x0648 0x00 0x00 #define IMX94_PAD_ETH4_TX_CLK__USDHC3_CLK 0x0180 0x0484 0x0000 0x01 0x00 @@ -917,6 +926,7 @@ #define IMX94_PAD_ETH4_TXD2__GPIO7_IO2 0x0190 0x0494 0x0000 0x05 0x00 #define IMX94_PAD_ETH4_TXD2__FLEXPWM4_PWMA2 0x0190 0x0494 0x06dc 0x06 0x01 #define IMX94_PAD_ETH4_TXD2__ETH4_RMII_REF50_CLK 0x0190 0x0494 0x0000 0x07 0x00 +#define IMX94_PAD_ETH4_TXD2__XBAR1_XBAR_INOUT34 0x0190 0x0494 0x0000 0x0105 0x00 #define IMX94_PAD_ETH4_TXD3__NETC_PINMUX_ETH4_TXD3 0x0194 0x0498 0x0000 0x00 0x00 #define IMX94_PAD_ETH4_TXD3__USDHC3_DATA3 0x0194 0x0498 0x0868 0x01 0x01 @@ -965,6 +975,7 @@ #define IMX94_PAD_ETH4_RX_CTL__GPIO7_IO8 0x01a8 0x04ac 0x0000 0x05 0x00 #define IMX94_PAD_ETH4_RX_CTL__DIG_ENCODER2_DATA_OUT 0x01a8 0x04ac 0x0000 0x06 0x00 #define IMX94_PAD_ETH4_RX_CTL__XBAR1_XBAR_INOUT6 0x01a8 0x04ac 0x0874 0x07 0x01 +#define IMX94_PAD_ETH4_RX_CTL__XSPI2_IPP_IND_INTFA_B 0x01a8 0x04ac 0x0000 0x0105 0x00 #define IMX94_PAD_ETH4_RX_CLK__NETC_PINMUX_ETH4_RX_CLK 0x01ac 0x04b0 0x0630 0x00 0x00 #define IMX94_PAD_ETH4_RX_CLK__XSPI2_A_DQS 0x01ac 0x04b0 0x0000 0x02 0x00 @@ -1344,6 +1355,7 @@ #define IMX94_PAD_XSPI1_SS1_B__GPIO7_IO27 0x028c 0x0590 0x0000 0x05 0x00 #define IMX94_PAD_XSPI1_SS1_B__SINC1_MOD_CLK0 0x028c 0x0590 0x0000 0x06 0x00 #define IMX94_PAD_XSPI1_SS1_B__SINC_FILTER_GLUE1_BREAK 0x028c 0x0590 0x0000 0x07 0x00 +#define IMX94_PAD_XSPI1_SS1_B__XSPI1_IPP_IND_INTFA_B 0x028c 0x0590 0x0000 0x0105 0x00 #define IMX94_PAD_SD2_CD_B__USDHC2_CD_B 0x0290 0x0594 0x0000 0x00 0x00 #define IMX94_PAD_SD2_CD_B__NETC_PINMUX_ETH4_RX_CTL 0x0290 0x0594 0x0634 0x01 0x01 diff --git a/arch/arm64/boot/dts/freescale/imx94.dtsi b/arch/arm64/boot/dts/freescale/imx94.dtsi index d2f31c8caf6e..c460ece6070f 100644 --- a/arch/arm64/boot/dts/freescale/imx94.dtsi +++ b/arch/arm64/boot/dts/freescale/imx94.dtsi @@ -7,6 +7,7 @@ #include #include #include +#include #include "imx94-clock.h" #include "imx94-pinfunc.h" @@ -94,14 +95,27 @@ #clock-cells = <1>; }; + scmi_sensor: protocol@15 { + reg = <0x15>; + #thermal-sensor-cells = <1>; + }; + scmi_iomuxc: protocol@19 { reg = <0x19>; }; + scmi_lmm: protocol@80 { + reg = <0x80>; + }; + scmi_bbm: protocol@81 { reg = <0x81>; }; + scmi_cpu: protocol@82 { + reg = <0x82>; + }; + scmi_misc: protocol@84 { reg = <0x84>; }; @@ -120,7 +134,7 @@ pmu { compatible = "arm,cortex-a55-pmu"; - interrupts = ; + interrupts = ; }; psci { @@ -130,15 +144,22 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; clock-frequency = <24000000>; interrupt-parent = <&gic>; arm,no-tick-in-suspend; }; + usbphynop: usbphynop { + compatible = "usb-nop-xceiv"; + clocks = <&scmi_clk IMX94_CLK_HSIO>; + clock-names = "main_clk"; + #phy-cells = <0>; + }; + gic: interrupt-controller@48000000 { compatible = "arm,gic-v3"; reg = <0 0x48000000 0 0x10000>, @@ -1205,6 +1226,48 @@ }; }; + mailbox@47300000 { + compatible = "fsl,imx95-mu-v2x"; + reg = <0x0 0x47300000 0x0 0x10000>; + interrupts = ; + #mbox-cells = <2>; + }; + + mailbox@47310000 { + compatible = "fsl,imx95-mu-v2x"; + reg = <0x0 0x47310000 0x0 0x10000>; + interrupts = ; + #mbox-cells = <2>; + }; + + mailbox@47330000 { + compatible = "fsl,imx95-mu-v2x"; + reg = <0x0 0x47330000 0x0 0x10000>; + interrupts = ; + #mbox-cells = <2>; + }; + + mailbox@47340000 { + compatible = "fsl,imx95-mu-v2x"; + reg = <0x0 0x47340000 0x0 0x10000>; + interrupts = ; + #mbox-cells = <2>; + }; + + mailbox@47350000 { + compatible = "fsl,imx95-mu-v2x"; + reg = <0x0 0x47350000 0x0 0x10000>; + interrupts = ; + #mbox-cells = <2>; + }; + + mailbox@47550000 { + compatible = "fsl,imx95-mu-ele"; + reg = <0x0 0x47550000 0x0 0x10000>; + interrupts = ; + #mbox-cells = <2>; + }; + aips4: bus@49000000 { compatible = "fsl,aips-bus", "simple-bus"; reg = <0x0 0x49000000 0x0 0x800000>; @@ -1223,6 +1286,60 @@ }; }; + usb3: usb@4c100000 { + compatible = "nxp,imx94-dwc3", "nxp,imx8mp-dwc3"; + reg = <0x0 0x4c100000 0x0 0x10000>, + <0x0 0x4c010010 0x0 0x04>, + <0x0 0x4c1f0000 0x0 0x20>; + reg-names = "core", "blkctl", "glue"; + clocks = <&scmi_clk IMX94_CLK_HSIO>, + <&scmi_clk IMX94_CLK_HSIO>, + <&scmi_clk IMX94_CLK_24M>, + <&scmi_clk IMX94_CLK_32K>; + clock-names = "hsio", "bus_early", "ref", "suspend"; + interrupts = , + ; + interrupt-names = "dwc_usb3", "wakeup"; + power-domains = <&scmi_devpd IMX94_PD_HSIO_TOP>; + phys = <&usb3_phy>, <&usb3_phy>; + phy-names = "usb2-phy", "usb3-phy"; + snps,gfladj-refclk-lpm-sel-quirk; + snps,parkmode-disable-ss-quirk; + status = "disabled"; + }; + + usb3_phy: phy@4c1f0040 { + compatible = "fsl,imx95-usb-phy", "fsl,imx8mp-usb-phy"; + reg = <0x0 0x4c1f0040 0x0 0x40>, + <0x0 0x4c1fc000 0x0 0x100>; + clocks = <&scmi_clk IMX94_CLK_HSIO>; + clock-names = "phy"; + #phy-cells = <0>; + power-domains = <&scmi_devpd IMX94_PD_HSIO_TOP>; + status = "disabled"; + }; + + usb2: usb@4c200000 { + compatible = "fsl,imx95-usb", "fsl,imx7d-usb", "fsl,imx27-usb"; + reg = <0x0 0x4c200000 0x0 0x200>; + interrupts = , + ; + clocks = <&scmi_clk IMX94_CLK_HSIO>, + <&scmi_clk IMX94_CLK_32K>; + clock-names = "usb_ctrl_root", "usb_wakeup"; + power-domains = <&scmi_devpd IMX94_PD_HSIO_TOP>; + phys = <&usbphynop>; + fsl,usbmisc = <&usbmisc 0>; + status = "disabled"; + }; + + usbmisc: usbmisc@4c200200 { + compatible = "fsl,imx95-usbmisc", "fsl,imx7d-usbmisc", "fsl,imx6q-usbmisc"; + reg = <0x0 0x4c200200 0x0 0x200>, + <0x0 0x4c010014 0x0 0x04>; + #index-cells = <1>; + }; + netc_blk_ctrl: system-controller@4ceb0000 { compatible = "nxp,imx94-netc-blk-ctrl"; reg = <0x0 0x4ceb0000 0x0 0x10000>, diff --git a/arch/arm64/boot/dts/freescale/imx943-evk.dts b/arch/arm64/boot/dts/freescale/imx943-evk.dts index 31fa9675cee1..52f7ef7dbf27 100644 --- a/arch/arm64/boot/dts/freescale/imx943-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx943-evk.dts @@ -6,6 +6,14 @@ /dts-v1/; #include "imx943.dtsi" +#include +#include + +#define BRD_SM_CTRL_BT_WAKE 0x8000 /*!< PCAL6416A-3 */ +#define BRD_SM_CTRL_SD3_WAKE 0x8001 /*!< PCAL6416A-4 */ +#define BRD_SM_CTRL_PCIE1_WAKE 0x8002 /*!< PCAL6416A-5 */ +#define BRD_SM_CTRL_PCIE2_WAKE 0x8003 /*!< PCAL6416A-6 */ +#define BRD_SM_CTRL_BUTTON 0x8004 /*!< PCAL6416A-7 */ / { compatible = "fsl,imx943-evk", "fsl,imx94"; @@ -20,7 +28,9 @@ i2c5 = &lpi2c6; mmc0 = &usdhc1; mmc1 = &usdhc2; + mmc2 = &usdhc3; serial0 = &lpuart1; + serial5 = &lpuart6; }; bt_sco_codec: bt-sco-codec { @@ -53,6 +63,32 @@ #sound-dai-cells = <0>; }; + reg_m2_pwr: regulator-m2-pwr { + compatible = "regulator-fixed"; + regulator-name = "M.2-power"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&pcal6416_i2c3_u46 2 GPIO_ACTIVE_HIGH>; + enable-active-high; + /* + * M.2 device only can be enabled(W_DISABLE1#) after all Power + * Rails reach their minimum operating voltage (PCI Express M.2 + * Specification r5.1 3.1.4 Power-up Timing). + * Set a delay equal to the max value of Tsettle here. + */ + startup-delay-us = <5000>; + }; + + reg_m2_wlan: regulator-wlan { + compatible = "regulator-fixed"; + regulator-name = "WLAN_EN"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <®_m2_pwr>; + gpio = <&pcal6416_i2c3_u46 5 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + reg_usdhc2_vmmc: regulator-usdhc2 { compatible = "regulator-fixed"; off-on-delay-us = <12000>; @@ -140,6 +176,11 @@ model = "wm8962-audio"; }; + usdhc3_pwrseq: usdhc3_pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&pcal6416_i2c3_u46 4 GPIO_ACTIVE_LOW>; + }; + memory@80000000 { reg = <0x0 0x80000000 0x0 0x80000000>; device_type = "memory"; @@ -197,6 +238,48 @@ gpio-controller; }; + ptn5110: tcpc@50 { + compatible = "nxp,ptn5110", "tcpci"; + reg = <0x50>; + interrupt-parent = <&gpio3>; + interrupts = <12 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_typec>; + + typec_con: connector { + compatible = "usb-c-connector"; + label = "USB-C"; + power-role = "dual"; + data-role = "dual"; + try-power-role = "sink"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <0>; + self-powered; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + typec_con_hs: endpoint { + remote-endpoint = <&usb3_data_hs>; + }; + }; + + port@1 { + reg = <1>; + + typec_con_ss: endpoint { + remote-endpoint = <&usb3_data_ss>; + }; + }; + }; + }; + }; + pca9548_i2c3: i2c-mux@77 { compatible = "nxp,pca9548"; reg = <0x77>; @@ -253,6 +336,28 @@ SPKVDD1-supply = <®_audio_pwr>; SPKVDD2-supply = <®_audio_pwr>; }; + + fan_controller: pwm@2f { + compatible = "microchip,emc2301", "microchip,emc2305"; + reg = <0x2f>; + #pwm-cells = <3>; + #address-cells = <1>; + #size-cells = <0>; + + fan0: fan@0 { + reg = <0x0>; + pwms = <&fan_controller 26000 1 PWM_POLARITY_INVERTED>; + #cooling-cells = <2>; + }; + }; + + ptn5150: tcpc@3d { + compatible = "nxp,ptn5150"; + reg = <0x3d>; + interrupt-parent = <&pcal6408_i2c3_u172>; + interrupts = <6 IRQ_TYPE_EDGE_FALLING>; + status = "disabled"; + }; }; i2c@5 { @@ -437,6 +542,17 @@ status = "okay"; }; +&lpuart6 { + /* BT */ + pinctrl-0 = <&pinctrl_uart6>; + pinctrl-names = "default"; + status = "okay"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; +}; + &micfil { assigned-clocks = <&scmi_clk IMX94_CLK_AUDIOPLL1_VCO>, <&scmi_clk IMX94_CLK_AUDIOPLL2_VCO>, @@ -636,6 +752,12 @@ >; }; + pinctrl_typec: typecgrp { + fsl,pins = < + IMX94_PAD_GPIO_IO44__GPIO3_IO12 0x30e + >; + }; + pinctrl_uart1: uart1grp { fsl,pins = < IMX94_PAD_UART1_TXD__LPUART1_TX 0x31e @@ -643,6 +765,15 @@ >; }; + pinctrl_uart6: uart6grp { + fsl,pins = < + IMX94_PAD_GPIO_IO04__LPUART6_TX 0x31e + IMX94_PAD_GPIO_IO05__LPUART6_RX 0x31e + IMX94_PAD_GPIO_IO06__LPUART6_CTS_B 0x31e + IMX94_PAD_GPIO_IO07__LPUART6_RTS_B 0x31e + >; + }; + pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { fsl,pins = < IMX94_PAD_SD1_CLK__USDHC1_CLK 0x158e @@ -739,6 +870,18 @@ >; }; + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + IMX94_PAD_GPIO_IO48__USDHC3_CLK 0x158e + /* Need to config the SION for CMD pad, refer to ERR053138 */ + IMX94_PAD_GPIO_IO49__USDHC3_CMD 0x4000138e + IMX94_PAD_GPIO_IO50__USDHC3_DATA0 0x138e + IMX94_PAD_GPIO_IO51__USDHC3_DATA1 0x138e + IMX94_PAD_GPIO_IO52__USDHC3_DATA2 0x138e + IMX94_PAD_GPIO_IO53__USDHC3_DATA3 0x138e + >; + }; + pinctrl_xspi1: xspi1grp { fsl,pins = < IMX94_PAD_XSPI1_SCLK__XSPI1_A_SCLK 0x3fe @@ -756,6 +899,138 @@ }; }; +&scmi_misc { + nxp,ctrl-ids = ; +}; + +&thermal_zones { + a55-thermal { + trips { + atrip2: trip2 { + temperature = <55000>; + hysteresis = <2000>; + type = "active"; + }; + + atrip3: trip3 { + temperature = <65000>; + hysteresis = <2000>; + type = "active"; + }; + + atrip4: trip4 { + temperature = <75000>; + hysteresis = <2000>; + type = "active"; + }; + }; + + cooling-maps { + map1 { + trip = <&atrip2>; + cooling-device = <&fan0 4 6>; + }; + + map2 { + trip = <&atrip3>; + cooling-device = <&fan0 6 8>; + }; + + map3 { + trip = <&atrip4>; + cooling-device = <&fan0 8 10>; + }; + }; + }; + + pf09-thermal { + polling-delay-passive = <250>; + polling-delay = <2000>; + thermal-sensors = <&scmi_sensor 2>; + + trips { + pf09_alert: trip0 { + temperature = <140000>; + hysteresis = <2000>; + type = "passive"; + }; + + pf09_crit: trip1 { + temperature = <155000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + }; + + pf53soc-thermal { + polling-delay-passive = <250>; + polling-delay = <2000>; + thermal-sensors = <&scmi_sensor 3>; + + trips { + pf5302_alert: trip0 { + temperature = <140000>; + hysteresis = <2000>; + type = "passive"; + }; + + pf5302_crit: trip1 { + temperature = <155000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + }; +}; + +&usb2 { + dr_mode = "otg"; + disable-over-current; + adp-disable; + hnp-disable; + srp-disable; + samsung,picophy-dc-vol-level-adjust = <10>; + status = "okay"; +}; + +&usb3 { + dr_mode = "otg"; + adp-disable; + hnp-disable; + srp-disable; + usb-role-switch; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + status = "okay"; + + port { + usb3_data_hs: endpoint { + remote-endpoint = <&typec_con_hs>; + }; + }; +}; + +&usb3_phy { + orientation-switch; + fsl,phy-pcs-tx-deemph-3p5db-attenuation-db = <17>; + fsl,phy-pcs-tx-swing-full-percent = <100>; + fsl,phy-tx-preemp-amp-tune-microamp = <600>; + fsl,phy-tx-vboost-level-microvolt = <1156>; + fsl,phy-tx-vref-tune-percent = <100>; + status = "okay"; + + port { + usb3_data_ss: endpoint { + remote-endpoint = <&typec_con_ss>; + }; + }; +}; + &usdhc1 { pinctrl-0 = <&pinctrl_usdhc1>; pinctrl-1 = <&pinctrl_usdhc1_100mhz>; @@ -781,6 +1056,23 @@ status = "okay"; }; +&usdhc3 { + /* + * Only enable SDIO2.0 mode as the corresponding GPIO pads are 3.3V, the + * max frequency is 50MHz. + */ + pinctrl-0 = <&pinctrl_usdhc3>; + pinctrl-1 = <&pinctrl_usdhc3>; + pinctrl-names = "default", "sleep"; + bus-width = <4>; + vmmc-supply = <®_m2_wlan>; + mmc-pwrseq = <&usdhc3_pwrseq>; + keep-power-in-suspend; + non-removable; + wakeup-source; + status = "okay"; +}; + &wdog3 { fsl,ext-reset-output; status = "okay"; diff --git a/arch/arm64/boot/dts/freescale/imx943.dtsi b/arch/arm64/boot/dts/freescale/imx943.dtsi index 45b8da758e87..dfd956ece2e3 100644 --- a/arch/arm64/boot/dts/freescale/imx943.dtsi +++ b/arch/arm64/boot/dts/freescale/imx943.dtsi @@ -145,4 +145,68 @@ cache-unified; }; }; + + thermal_zones: thermal-zones { + a55-thermal { + polling-delay-passive = <250>; + polling-delay = <2000>; + thermal-sensors = <&scmi_sensor 1>; + + trips { + cpu_alert0: trip0 { + temperature = <105000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu_crit0: trip1 { + temperature = <125000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu_alert0>; + cooling-device = + <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + ana-thermal { + polling-delay-passive = <250>; + polling-delay = <2000>; + thermal-sensors = <&scmi_sensor 0>; + + trips { + ana_alert: trip0 { + temperature = <105000>; + hysteresis = <2000>; + type = "passive"; + }; + + ana_crit0: trip1 { + temperature = <125000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&ana_alert>; + cooling-device = + <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + }; }; diff --git a/arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts b/arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts index d4184fb8b28c..7eb12e7d5014 100644 --- a/arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts @@ -116,7 +116,6 @@ reg_m2_pwr: regulator-m2-pwr { compatible = "regulator-fixed"; - regulator-always-on; regulator-max-microvolt = <3300000>; regulator-min-microvolt = <3300000>; regulator-name = "M.2-power"; diff --git a/arch/arm64/boot/dts/freescale/imx95-15x15-frdm.dts b/arch/arm64/boot/dts/freescale/imx95-15x15-frdm.dts index ca1c4966c867..0f43e3be7058 100644 --- a/arch/arm64/boot/dts/freescale/imx95-15x15-frdm.dts +++ b/arch/arm64/boot/dts/freescale/imx95-15x15-frdm.dts @@ -243,6 +243,12 @@ }; }; + sound-mqs { + compatible = "audio-graph-card2"; + links = <&sai1_port1>; + label = "mqs-audio"; + }; + usdhc3_pwrseq: usdhc3-pwrseq { compatible = "mmc-pwrseq-simple"; reset-gpios = <&pcal6524 8 GPIO_ACTIVE_LOW>; @@ -473,6 +479,21 @@ status = "okay"; }; +&mqs1 { + clocks = <&scmi_clk IMX95_CLK_SAI1>; + clock-names = "mclk"; + pinctrl-0 = <&pinctrl_mqs1>; + pinctrl-names = "default"; + status = "okay"; + + mqs1_port: port { + mqs1_ep: endpoint { + dai-format = "left_j"; + remote-endpoint = <&sai1_port1_ep>; + }; + }; +}; + &netc_blk_ctrl { status = "okay"; }; @@ -534,6 +555,51 @@ status = "okay"; }; +&sai1 { + clocks = <&scmi_clk IMX95_CLK_BUSAON>, <&dummy>, + <&scmi_clk IMX95_CLK_SAI1>, <&dummy>, + <&dummy>, <&scmi_clk IMX95_CLK_AUDIOPLL1>, + <&scmi_clk IMX95_CLK_AUDIOPLL2>; + clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3", "pll8k", "pll11k"; + assigned-clocks = <&scmi_clk IMX95_CLK_AUDIOPLL1_VCO>, + <&scmi_clk IMX95_CLK_AUDIOPLL2_VCO>, + <&scmi_clk IMX95_CLK_AUDIOPLL1>, + <&scmi_clk IMX95_CLK_AUDIOPLL2>, + <&scmi_clk IMX95_CLK_SAI1>; + assigned-clock-parents = <0>, <0>, <0>, <0>, <&scmi_clk IMX95_CLK_AUDIOPLL1>; + assigned-clock-rates = <3932160000>, <3612672000>, + <393216000>, <361267200>, + <24576000>; + fsl,sai-mclk-direction-output; + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + /* leave unconnected - no RX in the context of MQS */ + port@0 { + reg = <0>; + + endpoint { + }; + }; + + sai1_port1: port@1 { + reg = <1>; + mclk-fs = <512>; + + sai1_port1_ep: endpoint { + dai-format = "left_j"; + system-clock-direction-out; + bitclock-master; + frame-master; + remote-endpoint = <&mqs1_ep>; + }; + }; + }; +}; + &scmi_iomuxc { pinctrl_emdio: emdiogrp { fsl,pins = < @@ -618,6 +684,13 @@ >; }; + pinctrl_mqs1: mqs1grp { + fsl,pins = < + IMX95_PAD_SAI1_TXFS__AONMIX_TOP_MQS1_LEFT 0x31e + IMX95_PAD_SAI1_RXD0__AONMIX_TOP_MQS1_RIGHT 0x31e + >; + }; + pinctrl_pcal6524: pcal6524grp { fsl,pins = < IMX95_PAD_GPIO_IO34__GPIO5_IO_BIT14 0x31e diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk-sof.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-sof.dts index 808a9fe3ebb2..264703f6eef6 100644 --- a/arch/arm64/boot/dts/freescale/imx95-19x19-evk-sof.dts +++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-sof.dts @@ -55,8 +55,11 @@ }; &edma2 { - /* channels 30 and 31 reserved for FW usage */ - dma-channel-mask = <0xc0000000>, <0x0>; + /* + * channels 0 and 1 reserved for V2X fast hash, + * channels 30 and 31 reserved for FW usage + */ + dma-channel-mask = <0xc0000003>, <0x0>; }; &sai3 { diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts index aaa0da55a22b..041fd838fabb 100644 --- a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts @@ -475,7 +475,7 @@ &lpuart5 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart5>; - status = "disabled"; + status = "okay"; bluetooth { compatible = "nxp,88w8987-bt"; diff --git a/arch/arm64/boot/dts/freescale/imx95-toradex-smarc.dtsi b/arch/arm64/boot/dts/freescale/imx95-toradex-smarc.dtsi index 5932ba238a8a..7a73958f6eec 100644 --- a/arch/arm64/boot/dts/freescale/imx95-toradex-smarc.dtsi +++ b/arch/arm64/boot/dts/freescale/imx95-toradex-smarc.dtsi @@ -144,6 +144,14 @@ startup-delay-us = <2000>; }; + remoteproc-cm7 { + compatible = "fsl,imx95-cm7"; + mboxes = <&mu7 0 1 &mu7 1 1 &mu7 3 1>; + mbox-names = "tx", "rx", "rxdb"; + memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, + <&vdev1vring0>, <&vdev1vring1>, <&rsc_table>, <&m7_reserved>; + }; + reserved-memory { #address-cells = <2>; #size-cells = <2>; @@ -156,6 +164,42 @@ alloc-ranges = <0 0x80000000 0 0x7f000000>; linux,cma-default; }; + + m7_reserved: memory@80000000 { + reg = <0 0x80000000 0 0x1000000>; + no-map; + }; + + rsc_table: rsc-table@88220000 { + reg = <0 0x88220000 0 0x1000>; + no-map; + }; + + vdev0vring0: vdev0vring0@88000000 { + reg = <0 0x88000000 0 0x8000>; + no-map; + }; + + vdev0vring1: vdev0vring1@88008000 { + reg = <0 0x88008000 0 0x8000>; + no-map; + }; + + vdev1vring0: vdev1vring0@88010000 { + reg = <0 0x88010000 0 0x8000>; + no-map; + }; + + vdev1vring1: vdev1vring1@88018000 { + reg = <0 0x88018000 0 0x8000>; + no-map; + }; + + vdevbuffer: vdevbuffer@88020000 { + compatible = "shared-dma-pool"; + reg = <0 0x88020000 0 0x100000>; + no-map; + }; }; }; @@ -262,7 +306,6 @@ "", "", "", - "", "PMIC_SD2_VSEL"; status = "okay"; }; @@ -572,6 +615,10 @@ pinctrl-0 = <&pinctrl_uart3>; }; +&mu7 { + status = "okay"; +}; + /* SMARC MDIO, shared between all ethernet ports */ &netc_emdio { pinctrl-names = "default"; diff --git a/arch/arm64/boot/dts/freescale/imx95-var-dart-sonata.dts b/arch/arm64/boot/dts/freescale/imx95-var-dart-sonata.dts new file mode 100644 index 000000000000..0f3d2e488f4a --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-var-dart-sonata.dts @@ -0,0 +1,595 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Variscite Sonata carrier board for DART-MX95 + * + * Link: https://variscite.com/carrier-boards/sonata-board/ + * + * Copyright (C) 2026 Variscite Ltd. - https://www.variscite.com/ + * + */ + +#include "imx95-var-dart.dtsi" + +/ { + model = "Variscite DART-MX95 on Sonata-Board"; + compatible = "variscite,var-dart-mx95-sonata", + "variscite,var-dart-mx95", + "fsl,imx95"; + + aliases { + ethernet0 = &enetc_port0; + ethernet1 = &enetc_port1; + ethernet2 = &enetc_port2; + mmc0 = &usdhc1; + mmc1 = &usdhc2; + serial0 = &lpuart1; + }; + + chosen { + stdout-path = &lpuart1; + }; + + clk_osc_can0: clock-osc-40m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <40000000>; + }; + + typec_con: connector { + compatible = "usb-c-connector"; + data-role = "dual"; + label = "USB-C"; + op-sink-microwatt = <0>; + power-role = "dual"; + self-powered; + sink-pdos = ; + source-pdos = ; + try-power-role = "sink"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + typec_con_hs: endpoint { + remote-endpoint = <&usb3_data_hs>; + }; + }; + + port@1 { + reg = <1>; + + typec_con_ss: endpoint { + remote-endpoint = <&usb3_data_ss>; + }; + }; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + button-back { + gpios = <&pca6408_1 7 GPIO_ACTIVE_LOW>; + label = "Back"; + wakeup-source; + linux,code = ; + }; + + button-down { + gpios = <&pca6408_1 6 GPIO_ACTIVE_LOW>; + label = "Down"; + wakeup-source; + linux,code = ; + }; + + button-home { + gpios = <&pca6408_1 4 GPIO_ACTIVE_LOW>; + label = "Home"; + wakeup-source; + linux,code = ; + }; + + button-up { + gpios = <&pca6408_1 5 GPIO_ACTIVE_LOW>; + label = "Up"; + wakeup-source; + linux,code = ; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio_leds>; + + led-heartbeat { + label = "Heartbeat"; + gpios = <&gpio3 27 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + }; + + reg_phy1_supply: regulator-phy1 { + compatible = "regulator-fixed"; + regulator-name = "SUPPLY_PHY1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&pca6408_2 0 GPIO_ACTIVE_HIGH>; + enable-active-high; + startup-delay-us = <10000>; + regulator-always-on; + }; + + reg_usdhc2_vmmc: regulator-vmmc-usdhc2 { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_usdhc2_vmmc>; + regulator-name = "VDD_SD2_3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio3 7 GPIO_ACTIVE_HIGH>; + enable-active-high; + off-on-delay-us = <12000>; + }; + + sfp0: sfp { + compatible = "sff,sfp"; + i2c-bus = <&lpi2c3>; + los-gpios = <&pca9534 1 GPIO_ACTIVE_HIGH>; + maximum-power-milliwatt = <2000>; + }; +}; + +&enetc_port1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enetc1>; + phy-handle = <ðphy1>; + /* + * The required RGMII TX and RX 2ns delays are implemented directly + * in hardware via passive delay elements on the SOM PCB. + * No delay configuration is needed in software via PHY driver. + */ + phy-mode = "rgmii"; + status = "okay"; +}; + +&enetc_port2 { + phy-mode = "10gbase-r"; + sfp = <&sfp0>; + status = "okay"; +}; + +&flexcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan1>; + status = "okay"; +}; + +&lpi2c3 { + clock-frequency = <400000>; + pinctrl-names = "default", "gpio", "sleep"; + pinctrl-0 = <&pinctrl_lpi2c3>; + pinctrl-1 = <&pinctrl_lpi2c3_gpio>; + pinctrl-2 = <&pinctrl_lpi2c3_gpio>; + scl-gpios = <&gpio2 28 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio2 29 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; + + pca9534: gpio@22 { + compatible = "nxp,pca9534"; + reg = <0x22>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&gpio5>; + interrupts = <17 IRQ_TYPE_LEVEL_LOW>; + + pcie2-sel-hog { + gpio-hog; + gpios = <6 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "pcie-clk-sw"; + }; + + sfp-sel-hog { + gpio-hog; + gpios = <5 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "sfp-sw"; + }; + }; + + /* Capacitive touch controller */ + ft5x06_ts: touchscreen@38 { + compatible = "edt,edt-ft5206"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_captouch>; + reset-gpios = <&pca6408_2 4 GPIO_ACTIVE_LOW>; + interrupt-parent = <&gpio5>; + interrupts = <13 IRQ_TYPE_EDGE_FALLING>; + touchscreen-size-x = <800>; + touchscreen-size-y = <480>; + touchscreen-inverted-x; + touchscreen-inverted-y; + wakeup-source; + }; + + typec@3d { + compatible = "nxp,ptn5150"; + reg = <0x3d>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ptn5150>; + interrupt-parent = <&gpio5>; + interrupts = <14 IRQ_TYPE_LEVEL_HIGH>; + + port { + typec_dr_sw: endpoint { + remote-endpoint = <&usb3_drd_sw>; + }; + }; + }; + + /* DS1337 RTC module */ + rtc@68 { + compatible = "dallas,ds1337"; + reg = <0x68>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rtc>; + interrupt-parent = <&gpio5>; + interrupts = <12 IRQ_TYPE_EDGE_FALLING>; + wakeup-source; + }; +}; + +&lpi2c4 { + clock-frequency = <400000>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_lpi2c4>; + pinctrl-1 = <&pinctrl_lpi2c4>; + status = "okay"; +}; + +&lpi2c8 { + pca6408_1: gpio@20 { + compatible = "nxp,pcal6408"; + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&gpio5>; + interrupts = <17 IRQ_TYPE_LEVEL_LOW>; + }; + + pca6408_2: gpio@21 { + compatible = "nxp,pcal6408"; + reg = <0x21>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&gpio5>; + interrupts = <17 IRQ_TYPE_LEVEL_LOW>; + }; + + st33ktpm2xi2c: tpm@2e { + compatible = "st,st33ktpm2xi2c", "tcg,tpm-tis-i2c"; + reg = <0x2e>; + }; +}; + +&lpspi7 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lpspi7>; + cs-gpios = <&gpio2 4 GPIO_ACTIVE_LOW>; + status = "okay"; + + /* Resistive touch controller */ + ads7846: touchscreen@0 { + compatible = "ti,ads7846"; + reg = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_restouch>; + interrupt-parent = <&gpio2>; + interrupts = <24 IRQ_TYPE_EDGE_FALLING>; + pendown-gpio = <&gpio2 24 GPIO_ACTIVE_LOW>; + spi-max-frequency = <1500000>; + wakeup-source; + ti,x-min = /bits/ 16 <125>; + ti,x-max = /bits/ 16 <4008>; + ti,y-min = /bits/ 16 <282>; + ti,y-max = /bits/ 16 <3864>; + ti,x-plate-ohms = /bits/ 16 <180>; + ti,pressure-max = /bits/ 16 <255>; + ti,debounce-max = /bits/ 16 <10>; + ti,debounce-tol = /bits/ 16 <3>; + ti,debounce-rep = /bits/ 16 <1>; + ti,settle-delay-usec = /bits/ 16 <150>; + ti,keep-vref-on; + }; +}; + +/* Console */ +&lpuart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; +}; + +/* Header (J12.4, J12.6) */ +&lpuart8 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart8>; + status = "okay"; +}; + +&netc_emdio { + ethphy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + reset-gpios = <&pca6408_2 0 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <100000>; + + leds { + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + color = ; + function = LED_FUNCTION_LAN; + linux,default-trigger = "netdev"; + }; + + led@1 { + reg = <1>; + color = ; + function = LED_FUNCTION_LAN; + linux,default-trigger = "netdev"; + }; + }; + }; +}; + +&pcie0 { + reset-gpio = <&pca6408_2 3 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&pcie1 { + reset-gpio = <&pca6408_2 2 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&usb2 { + dr_mode = "host"; + adp-disable; + hnp-disable; + srp-disable; + disable-over-current; + status = "okay"; +}; + +&usb3 { + status = "okay"; +}; + +&usb3_dwc3 { + dr_mode = "otg"; + hnp-disable; + srp-disable; + adp-disable; + usb-role-switch; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + status = "okay"; + + port { + usb3_drd_sw: endpoint { + remote-endpoint = <&typec_dr_sw>; + }; + }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb3_data_hs: endpoint { + remote-endpoint = <&typec_con_hs>; + }; + }; + + port@1 { + reg = <1>; + usb3_data_ss: endpoint { + remote-endpoint = <&typec_con_ss>; + }; + }; + }; +}; + +&usb3_phy { + fsl,phy-pcs-tx-deemph-3p5db-attenuation-db = <17>; + fsl,phy-pcs-tx-swing-full-percent = <100>; + fsl,phy-tx-preemp-amp-tune-microamp = <600>; + fsl,phy-tx-vboost-level-microvolt = <1156>; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default","state_100mhz","state_200mhz","sleep"; + pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>; + pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>; + pinctrl-3 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>; + cd-gpios = <&gpio3 0 GPIO_ACTIVE_LOW>; + vmmc-supply = <®_usdhc2_vmmc>; + bus-width = <4>; + status = "okay"; +}; + +&scmi_iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + pinctrl_hog: hoggrp { + fsl,pins = < + /* GPIO Expanders shared IRQ */ + IMX95_PAD_GPIO_IO37__GPIO5_IO_BIT17 0x31e + >; + }; + + pinctrl_captouch: captouchgrp { + fsl,pins = < + IMX95_PAD_GPIO_IO33__GPIO5_IO_BIT13 0x31e + >; + }; + + pinctrl_enetc1: enetc1grp { + fsl,pins = < + IMX95_PAD_ENET2_TXC__NETCMIX_TOP_ETH1_RGMII_TX_CLK 0x57e + IMX95_PAD_ENET2_TX_CTL__NETCMIX_TOP_ETH1_RGMII_TX_CTL 0x57e + IMX95_PAD_ENET2_TD3__NETCMIX_TOP_ETH1_RGMII_TD3 0x57e + IMX95_PAD_ENET2_TD2__NETCMIX_TOP_ETH1_RGMII_TD2 0x57e + IMX95_PAD_ENET2_TD1__NETCMIX_TOP_ETH1_RGMII_TD1 0x57e + IMX95_PAD_ENET2_TD0__NETCMIX_TOP_ETH1_RGMII_TD0 0x57e + IMX95_PAD_ENET2_RXC__NETCMIX_TOP_ETH1_RGMII_RX_CLK 0x57e + IMX95_PAD_ENET2_RX_CTL__NETCMIX_TOP_ETH1_RGMII_RX_CTL 0x57e + IMX95_PAD_ENET2_RD0__NETCMIX_TOP_ETH1_RGMII_RD0 0x57e + IMX95_PAD_ENET2_RD1__NETCMIX_TOP_ETH1_RGMII_RD1 0x57e + IMX95_PAD_ENET2_RD2__NETCMIX_TOP_ETH1_RGMII_RD2 0x57e + IMX95_PAD_ENET2_RD3__NETCMIX_TOP_ETH1_RGMII_RD3 0x37e + >; + }; + + pinctrl_flexcan1: flexcan1grp { + fsl,pins = < + IMX95_PAD_PDM_CLK__AONMIX_TOP_CAN1_TX 0x39e + IMX95_PAD_PDM_BIT_STREAM0__AONMIX_TOP_CAN1_RX 0x39e + >; + }; + + pinctrl_gpio_leds: ledgrp { + fsl,pins = < + IMX95_PAD_CCM_CLKO2__GPIO3_IO_BIT27 0x31e + >; + }; + + pinctrl_lpi2c3: lpi2c3grp { + fsl,pins = < + IMX95_PAD_GPIO_IO28__LPI2C3_SDA 0x40000b9e + IMX95_PAD_GPIO_IO29__LPI2C3_SCL 0x40000b9e + >; + }; + + pinctrl_lpi2c3_gpio: lpi2c3gpiogrp { + fsl,pins = < + IMX95_PAD_GPIO_IO28__GPIO2_IO_BIT28 0x31e + IMX95_PAD_GPIO_IO29__GPIO2_IO_BIT29 0x31e + >; + }; + + pinctrl_lpi2c4: lpi2c4grp { + fsl,pins = < + IMX95_PAD_GPIO_IO30__LPI2C4_SDA 0x40000b9e + IMX95_PAD_GPIO_IO31__LPI2C4_SCL 0x40000b9e + >; + }; + + pinctrl_lpspi7: lpspi7grp { + fsl,pins = < + /* j16.4 ADS7846 */ + IMX95_PAD_GPIO_IO04__GPIO2_IO_BIT4 0x3fe + /* j14.4 MCP2518FDT */ + IMX95_PAD_UART2_TXD__AONMIX_TOP_GPIO1_IO_BIT7 0x3fe + /* j25.2 spidev */ + IMX95_PAD_XSPI1_DATA4__GPIO5_IO_BIT4 0x3fe + IMX95_PAD_GPIO_IO05__LPSPI7_SIN 0x3fe + IMX95_PAD_GPIO_IO06__LPSPI7_SOUT 0x3fe + IMX95_PAD_GPIO_IO07__LPSPI7_SCK 0x3fe + >; + }; + + pinctrl_ptn5150: ptn5150grp { + fsl,pins = < + IMX95_PAD_GPIO_IO34__GPIO5_IO_BIT14 0x31e + >; + }; + + pinctrl_reg_usdhc2_vmmc: regusdhc2vmmcgrp { + fsl,pins = < + IMX95_PAD_SD2_RESET_B__GPIO3_IO_BIT7 0x31e + >; + }; + + pinctrl_restouch: restouchgrp { + fsl,pins = < + IMX95_PAD_GPIO_IO24__GPIO2_IO_BIT24 0x31e + >; + }; + + pinctrl_rtc: rtcgrp { + fsl,pins = < + IMX95_PAD_GPIO_IO32__GPIO5_IO_BIT12 0x31e + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + IMX95_PAD_UART1_RXD__AONMIX_TOP_LPUART1_RX 0x31e + IMX95_PAD_UART1_TXD__AONMIX_TOP_LPUART1_TX 0x31e + >; + }; + + pinctrl_uart8: uart8grp { + fsl,pins = < + IMX95_PAD_GPIO_IO13__LPUART8_RX 0x31e + IMX95_PAD_GPIO_IO12__LPUART8_TX 0x31e + >; + }; + + pinctrl_usdhc2_gpio: usdhc2gpiogrp { + fsl,pins = < + IMX95_PAD_SD2_CD_B__GPIO3_IO_BIT0 0x31e + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + IMX95_PAD_SD2_CLK__USDHC2_CLK 0x158e + IMX95_PAD_SD2_CMD__USDHC2_CMD 0x138e + IMX95_PAD_SD2_DATA0__USDHC2_DATA0 0x138e + IMX95_PAD_SD2_DATA1__USDHC2_DATA1 0x138e + IMX95_PAD_SD2_DATA2__USDHC2_DATA2 0x138e + IMX95_PAD_SD2_DATA3__USDHC2_DATA3 0x138e + IMX95_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; + + pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp { + fsl,pins = < + IMX95_PAD_SD2_CLK__USDHC2_CLK 0x158e + IMX95_PAD_SD2_CMD__USDHC2_CMD 0x138e + IMX95_PAD_SD2_DATA0__USDHC2_DATA0 0x138e + IMX95_PAD_SD2_DATA1__USDHC2_DATA1 0x138e + IMX95_PAD_SD2_DATA2__USDHC2_DATA2 0x138e + IMX95_PAD_SD2_DATA3__USDHC2_DATA3 0x138e + IMX95_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; + + pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { + fsl,pins = < + IMX95_PAD_SD2_CLK__USDHC2_CLK 0x15fe + IMX95_PAD_SD2_CMD__USDHC2_CMD 0x13fe + IMX95_PAD_SD2_DATA0__USDHC2_DATA0 0x13fe + IMX95_PAD_SD2_DATA1__USDHC2_DATA1 0x13fe + IMX95_PAD_SD2_DATA2__USDHC2_DATA2 0x13fe + IMX95_PAD_SD2_DATA3__USDHC2_DATA3 0x13fe + IMX95_PAD_SD2_VSELECT__USDHC2_VSELECT 0x51e + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-var-dart.dtsi b/arch/arm64/boot/dts/freescale/imx95-var-dart.dtsi new file mode 100644 index 000000000000..a20fadacaa6d --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-var-dart.dtsi @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Common dtsi for Variscite DART-MX95 + * + * Link: https://variscite.com/system-on-module-som/i-mx-9/i-mx-95/dart-mx95/ + * + * Copyright (C) 2026 Variscite Ltd. - https://www.variscite.com/ + * + */ + +/dts-v1/; + +#include +#include +#include "imx95.dtsi" + +/ { + model = "Variscite DART-MX95 Module"; + compatible = "variscite,var-dart-mx95", "fsl,imx95"; + + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0 0x80000000>; + }; + + reg_1p8v: regulator-1p8v { + compatible = "regulator-fixed"; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + regulator-name = "+V1.8_SW"; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "+V3.3_SW"; + }; + + reg_vref_1v8: regulator-adc-vref { + compatible = "regulator-fixed"; + regulator-name = "vref_1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + reg_audio: regulator-audio-vdd { + compatible = "regulator-fixed"; + regulator-name = "wm8904_supply"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reserved-memory { + ranges; + #address-cells = <2>; + #size-cells = <2>; + + linux_cma: linux,cma { + compatible = "shared-dma-pool"; + alloc-ranges = <0 0x80000000 0 0x7F000000>; + reusable; + size = <0 0x3c000000>; + linux,cma-default; + }; + }; + + sound-wm8904 { + compatible = "simple-audio-card"; + simple-audio-card,bitclock-master = <&codec_dai>; + simple-audio-card,format = "i2s"; + simple-audio-card,frame-master = <&codec_dai>; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "wm8904-audio"; + simple-audio-card,routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "IN2L", "Line In Jack", + "IN2R", "Line In Jack", + "IN1L", "Microphone Jack", + "IN1R", "Microphone Jack"; + simple-audio-card,widgets = + "Microphone", "Microphone Jack", + "Headphone", "Headphone Jack", + "Line", "Line In Jack"; + + codec_dai: simple-audio-card,codec { + sound-dai = <&wm8904>; + }; + + simple-audio-card,cpu { + sound-dai = <&sai3>; + }; + }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + post-power-on-delay-ms = <100>; + power-off-delay-us = <10000>; + reset-gpios = <&gpio4 29 GPIO_ACTIVE_LOW>, /* WIFI_RESET */ + <&gpio2 27 GPIO_ACTIVE_LOW>; /* WIFI_PWR_EN */ + }; +}; + +&adc1 { + vref-supply = <®_vref_1v8>; + status = "okay"; +}; + +&enetc_port0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enetc0>; + phy-handle = <ðphy0>; + /* + * The required RGMII TX and RX 2ns delays are implemented directly + * in hardware via passive delay elements on the SOM PCB. + * No delay configuration is needed in software via PHY driver. + */ + phy-mode = "rgmii"; + status = "okay"; +}; + +&lpi2c8 { + clock-frequency = <400000>; + pinctrl-names = "default","gpio","sleep"; + pinctrl-0 = <&pinctrl_lpi2c8>; + pinctrl-1 = <&pinctrl_lpi2c8_gpio>; + pinctrl-2 = <&pinctrl_lpi2c8_gpio>; + scl-gpios = <&gpio2 10 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio2 11 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; + + wm8904: audio-codec@1a { + compatible = "wlf,wm8904"; + reg = <0x1a>; + #sound-dai-cells = <0>; + clocks = <&scmi_clk IMX95_CLK_SAI3>; + clock-names = "mclk"; + AVDD-supply = <®_audio>; + CPVDD-supply = <®_audio>; + DBVDD-supply = <®_audio>; + DCVDD-supply = <®_audio>; + MICVDD-supply = <®_audio>; + wlf,drc-cfg-names = "default", "peaklimiter", "tradition", + "soft", "music"; + /* + * Config registers per name, respectively: + * KNEE_IP = 0, KNEE_OP = 0, HI_COMP = 1, LO_COMP = 1 + * KNEE_IP = -24, KNEE_OP = -6, HI_COMP = 1/4, LO_COMP = 1 + * KNEE_IP = -42, KNEE_OP = -3, HI_COMP = 0, LO_COMP = 1 + * KNEE_IP = -45, KNEE_OP = -9, HI_COMP = 1/8, LO_COMP = 1 + * KNEE_IP = -30, KNEE_OP = -10.5, HI_COMP = 1/4, LO_COMP = 1 + */ + wlf,drc-cfg-regs = /bits/ 16 <0x01af 0x3248 0x0000 0x0000>, + /bits/ 16 <0x04af 0x324b 0x0010 0x0408>, + /bits/ 16 <0x04af 0x324b 0x0028 0x0704>, + /bits/ 16 <0x04af 0x324b 0x0018 0x078c>, + /bits/ 16 <0x04af 0x324b 0x0010 0x050e>; + /* GPIO1 = DMIC_CLK, don't touch others */ + wlf,gpio-cfg = <0x0018>, <0xffff>, <0xffff>, <0xffff>; + }; +}; + +/* BT */ +&lpuart5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart5>, <&pinctrl_bt>; + status = "okay"; + + bluetooth { + compatible = "nxp,88w8987-bt"; + }; +}; + +&mu7 { + status = "okay"; +}; + +&netc_emdio { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_emdio>, <&pinctrl_phy0res>; + status = "okay"; + + ethphy0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + reset-gpios = <&gpio5 16 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <100000>; + + leds { + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + color = ; + function = LED_FUNCTION_LAN; + linux,default-trigger = "netdev"; + }; + + led@1 { + reg = <1>; + color = ; + function = LED_FUNCTION_LAN; + linux,default-trigger = "netdev"; + }; + }; + }; +}; + +&netc_timer { + status = "okay"; +}; + +&sai3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sai3>; + assigned-clocks = <&scmi_clk IMX95_CLK_AUDIOPLL1_VCO>, + <&scmi_clk IMX95_CLK_AUDIOPLL2_VCO>, + <&scmi_clk IMX95_CLK_AUDIOPLL1>, + <&scmi_clk IMX95_CLK_AUDIOPLL2>, + <&scmi_clk IMX95_CLK_SAI3>; + assigned-clock-parents = <0>, <0>, <0>, <0>, + <&scmi_clk IMX95_CLK_AUDIOPLL1>; + assigned-clock-rates = <3932160000>, + <3612672000>, <393216000>, + <361267200>, <12288000>; + #sound-dai-cells = <0>; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + +/* eMMC */ +&usdhc1 { + pinctrl-names = "default","state_100mhz","state_200mhz","sleep"; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + pinctrl-3 = <&pinctrl_usdhc1>; + bus-width = <8>; + non-removable; + no-sdio; + no-sd; + status = "okay"; +}; + +/* WiFi */ +&usdhc3 { + pinctrl-names = "default","state_100mhz","state_200mhz","sleep"; + pinctrl-0 = <&pinctrl_usdhc3>, <&pinctrl_usdhc3_gpio>; + pinctrl-1 = <&pinctrl_usdhc3_100mhz>, <&pinctrl_usdhc3_gpio>; + pinctrl-2 = <&pinctrl_usdhc3_200mhz>, <&pinctrl_usdhc3_gpio>; + pinctrl-3 = <&pinctrl_usdhc3>, <&pinctrl_usdhc3_gpio>; + mmc-pwrseq = <&wifi_pwrseq>; + bus-width = <4>; + non-removable; + wakeup-source; + keep-power-in-suspend; + status = "okay"; +}; + +&wdog3 { + fsl,ext-reset-output; + status = "okay"; +}; + +&scmi_iomuxc { + pinctrl_bt: btgrp { + fsl,pins = < + IMX95_PAD_CCM_CLKO3__GPIO4_IO_BIT28 0x31e + >; + }; + + pinctrl_emdio: emdiogrp { + fsl,pins = < + IMX95_PAD_ENET1_MDC__NETCMIX_TOP_NETC_MDC 0x57e + IMX95_PAD_ENET1_MDIO__NETCMIX_TOP_NETC_MDIO 0x97e + >; + }; + + pinctrl_enetc0: enetc0grp { + fsl,pins = < + IMX95_PAD_ENET1_TD3__NETCMIX_TOP_ETH0_RGMII_TD3 0x57e + IMX95_PAD_ENET1_TD2__NETCMIX_TOP_ETH0_RGMII_TD2 0x57e + IMX95_PAD_ENET1_TD1__NETCMIX_TOP_ETH0_RGMII_TD1 0x57e + IMX95_PAD_ENET1_TD0__NETCMIX_TOP_ETH0_RGMII_TD0 0x57e + IMX95_PAD_ENET1_TX_CTL__NETCMIX_TOP_ETH0_RGMII_TX_CTL 0x57e + IMX95_PAD_ENET1_TXC__NETCMIX_TOP_ETH0_RGMII_TX_CLK 0x58e + IMX95_PAD_ENET1_RX_CTL__NETCMIX_TOP_ETH0_RGMII_RX_CTL 0x57e + IMX95_PAD_ENET1_RXC__NETCMIX_TOP_ETH0_RGMII_RX_CLK 0x58e + IMX95_PAD_ENET1_RD0__NETCMIX_TOP_ETH0_RGMII_RD0 0x57e + IMX95_PAD_ENET1_RD1__NETCMIX_TOP_ETH0_RGMII_RD1 0x57e + IMX95_PAD_ENET1_RD2__NETCMIX_TOP_ETH0_RGMII_RD2 0x57e + IMX95_PAD_ENET1_RD3__NETCMIX_TOP_ETH0_RGMII_RD3 0x57e + >; + }; + + pinctrl_lpi2c8: lpi2c8grp { + fsl,pins = < + IMX95_PAD_GPIO_IO10__LPI2C8_SDA 0x40000b9e + IMX95_PAD_GPIO_IO11__LPI2C8_SCL 0x40000b9e + >; + }; + + pinctrl_lpi2c8_gpio: lpi2c8gpiogrp { + fsl,pins = < + IMX95_PAD_GPIO_IO10__GPIO2_IO_BIT10 0x31e + IMX95_PAD_GPIO_IO11__GPIO2_IO_BIT11 0x31e + >; + }; + + pinctrl_phy0res: phy0resgrp { + fsl,pins = < + IMX95_PAD_GPIO_IO36__GPIO5_IO_BIT16 0x31e + >; + }; + + pinctrl_sai3: sai3grp { + fsl,pins = < + IMX95_PAD_GPIO_IO17__SAI3_MCLK 0x31e + IMX95_PAD_GPIO_IO16__SAI3_TX_BCLK 0x31e + IMX95_PAD_GPIO_IO26__SAI3_TX_SYNC 0x31e + IMX95_PAD_GPIO_IO20__SAI3_RX_DATA_BIT0 0x31e + IMX95_PAD_GPIO_IO21__SAI3_TX_DATA_BIT0 0x31e + >; + }; + + pinctrl_uart5: uart5grp { + fsl,pins = < + IMX95_PAD_GPIO_IO00__LPUART5_TX 0x31e + IMX95_PAD_GPIO_IO01__LPUART5_RX 0x31e + IMX95_PAD_GPIO_IO02__LPUART5_CTS_B 0x31e + IMX95_PAD_GPIO_IO03__LPUART5_RTS_B 0x31e + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + IMX95_PAD_SD1_CLK__USDHC1_CLK 0x158e + IMX95_PAD_SD1_CMD__USDHC1_CMD 0x138e + IMX95_PAD_SD1_DATA0__USDHC1_DATA0 0x138e + IMX95_PAD_SD1_DATA1__USDHC1_DATA1 0x138e + IMX95_PAD_SD1_DATA2__USDHC1_DATA2 0x138e + IMX95_PAD_SD1_DATA3__USDHC1_DATA3 0x138e + IMX95_PAD_SD1_DATA4__USDHC1_DATA4 0x138e + IMX95_PAD_SD1_DATA5__USDHC1_DATA5 0x138e + IMX95_PAD_SD1_DATA6__USDHC1_DATA6 0x138e + IMX95_PAD_SD1_DATA7__USDHC1_DATA7 0x138e + IMX95_PAD_SD1_STROBE__USDHC1_STROBE 0x158e + >; + }; + + pinctrl_usdhc1_100mhz: usdhc1-100mhzgrp { + fsl,pins = < + IMX95_PAD_SD1_CLK__USDHC1_CLK 0x158e + IMX95_PAD_SD1_CMD__USDHC1_CMD 0x138e + IMX95_PAD_SD1_DATA0__USDHC1_DATA0 0x138e + IMX95_PAD_SD1_DATA1__USDHC1_DATA1 0x138e + IMX95_PAD_SD1_DATA2__USDHC1_DATA2 0x138e + IMX95_PAD_SD1_DATA3__USDHC1_DATA3 0x138e + IMX95_PAD_SD1_DATA4__USDHC1_DATA4 0x138e + IMX95_PAD_SD1_DATA5__USDHC1_DATA5 0x138e + IMX95_PAD_SD1_DATA6__USDHC1_DATA6 0x138e + IMX95_PAD_SD1_DATA7__USDHC1_DATA7 0x138e + IMX95_PAD_SD1_STROBE__USDHC1_STROBE 0x158e + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { + fsl,pins = < + IMX95_PAD_SD1_CLK__USDHC1_CLK 0x15fe + IMX95_PAD_SD1_CMD__USDHC1_CMD 0x13fe + IMX95_PAD_SD1_DATA0__USDHC1_DATA0 0x13fe + IMX95_PAD_SD1_DATA1__USDHC1_DATA1 0x13fe + IMX95_PAD_SD1_DATA2__USDHC1_DATA2 0x13fe + IMX95_PAD_SD1_DATA3__USDHC1_DATA3 0x13fe + IMX95_PAD_SD1_DATA4__USDHC1_DATA4 0x13fe + IMX95_PAD_SD1_DATA5__USDHC1_DATA5 0x13fe + IMX95_PAD_SD1_DATA6__USDHC1_DATA6 0x13fe + IMX95_PAD_SD1_DATA7__USDHC1_DATA7 0x13fe + IMX95_PAD_SD1_STROBE__USDHC1_STROBE 0x15fe + >; + }; + + pinctrl_usdhc3_gpio: usdhc3gpiogrp { + fsl,pins = < + IMX95_PAD_GPIO_IO27__GPIO2_IO_BIT27 0x31e + IMX95_PAD_CCM_CLKO4__GPIO4_IO_BIT29 0x31e + >; + }; + + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + IMX95_PAD_SD3_CLK__USDHC3_CLK 0x158e + IMX95_PAD_SD3_CMD__USDHC3_CMD 0x138e + IMX95_PAD_SD3_DATA0__USDHC3_DATA0 0x138e + IMX95_PAD_SD3_DATA1__USDHC3_DATA1 0x138e + IMX95_PAD_SD3_DATA2__USDHC3_DATA2 0x138e + IMX95_PAD_SD3_DATA3__USDHC3_DATA3 0x138e + >; + }; + + pinctrl_usdhc3_100mhz: usdhc3-100mhzgrp { + fsl,pins = < + IMX95_PAD_SD3_CLK__USDHC3_CLK 0x158e + IMX95_PAD_SD3_CMD__USDHC3_CMD 0x138e + IMX95_PAD_SD3_DATA0__USDHC3_DATA0 0x138e + IMX95_PAD_SD3_DATA1__USDHC3_DATA1 0x138e + IMX95_PAD_SD3_DATA2__USDHC3_DATA2 0x138e + IMX95_PAD_SD3_DATA3__USDHC3_DATA3 0x138e + >; + }; + + pinctrl_usdhc3_200mhz: usdhc3-200mhzgrp { + fsl,pins = < + IMX95_PAD_SD3_CLK__USDHC3_CLK 0x15fe + IMX95_PAD_SD3_CMD__USDHC3_CMD 0x13fe + IMX95_PAD_SD3_DATA0__USDHC3_DATA0 0x13fe + IMX95_PAD_SD3_DATA1__USDHC3_DATA1 0x13fe + IMX95_PAD_SD3_DATA2__USDHC3_DATA2 0x13fe + IMX95_PAD_SD3_DATA3__USDHC3_DATA3 0x13fe + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-dahlia.dtsi b/arch/arm64/boot/dts/freescale/imx95-verdin-dahlia.dtsi new file mode 100644 index 000000000000..889b71aa3de0 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-dahlia.dtsi @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * Common dtsi for Verdin iMX95 SoM on Dahlia carrier board + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/dahlia-carrier-board-kit + */ + +/ { + aliases { + eeprom1 = &carrier_eeprom; + }; + + reg_1v8_sw: regulator-1v8-sw { + compatible = "regulator-fixed"; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + regulator-name = "On-carrier +V1.8_SW"; + }; + + reg_pcie: regulator-pcie { + compatible = "regulator-fixed"; + /* Verdin CTRL_SLEEP_MOCI# (SODIMM 256) */ + gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-boot-on; + regulator-name = "PCIE_1_PWR_EN"; + }; + + reg_usb_hub: regulator-usb-hub { + compatible = "regulator-fixed"; + /* Verdin CTRL_SLEEP_MOCI# (SODIMM 256) */ + gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-boot-on; + regulator-name = "HUB_PWR_EN"; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,bitclock-master = <&codec_dai>; + simple-audio-card,format = "i2s"; + simple-audio-card,frame-master = <&codec_dai>; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "verdin-wm8904"; + simple-audio-card,routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "IN2L", "Line In Jack", + "IN2R", "Line In Jack", + "Microphone Jack", "MICBIAS", + "IN1L", "Microphone Jack"; + simple-audio-card,widgets = + "Microphone", "Microphone Jack", + "Headphone", "Headphone Jack", + "Line", "Line In Jack"; + + codec_dai: simple-audio-card,codec { + sound-dai = <&wm8904_1a>; + }; + + simple-audio-card,cpu { + sound-dai = <&sai3>; + }; + }; +}; + +/* Verdin ADC_1, ADC_2, ADC_3 and ADC_4 */ +&adc1 { + status = "okay"; +}; + +/* Verdin ETH_1 (On-module PHY) */ +&enetc_port0 { + status = "okay"; +}; + +/* Verdin CAN_1 */ +&flexcan1 { + status = "okay"; +}; + +/* Verdin CAN_2 */ +&flexcan2 { + status = "okay"; +}; + +/* Verdin QSPI_1 */ +&flexspi1 { + status = "okay"; +}; + +&gpio1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ctrl_sleep_moci>; +}; + +&gpio2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio1>, + <&pinctrl_gpio2>, + <&pinctrl_gpio3>; +}; + +&gpio3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio6>; +}; + +&gpio4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio5>; +}; + +&gpio5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio4>; +}; + +/* Verdin I2C_3_HDMI */ +&i3c2 { + status = "okay"; +}; + +/* Verdin I2C_2_DSI */ +&lpi2c3 { + status = "okay"; +}; + +/* Verdin I2C_1 */ +&lpi2c4 { + status = "okay"; + + wm8904_1a: audio-codec@1a { + compatible = "wlf,wm8904"; + reg = <0x1a>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sai3_mclk>; + clocks = <&scmi_clk IMX95_CLK_SAI3>; + clock-names = "mclk"; + #sound-dai-cells = <0>; + AVDD-supply = <®_1v8_sw>; + CPVDD-supply = <®_1v8_sw>; + DBVDD-supply = <®_1v8_sw>; + DCVDD-supply = <®_1v8_sw>; + MICVDD-supply = <®_1v8_sw>; + }; + + /* Current measurement into module VCC */ + hwmon@40 { + compatible = "ti,ina219"; + reg = <0x40>; + shunt-resistor = <10000>; + }; + + temperature-sensor@4f { + compatible = "ti,tmp75c"; + reg = <0x4f>; + }; + + carrier_eeprom: eeprom@57 { + compatible = "st,24c02", "atmel,24c02"; + reg = <0x57>; + pagesize = <16>; + }; +}; + +/* Verdin I2C_4_CSI */ +&lpi2c5 { + status = "okay"; +}; + +/* Verdin UART_3, used as the Linux console */ +&lpuart1 { + status = "okay"; +}; + +/* Verdin UART_4 */ +&lpuart2 { + status = "okay"; +}; + +/* Verdin UART_1 */ +&lpuart7 { + status = "okay"; +}; + +/* Verdin UART_2 */ +&lpuart8 { + status = "okay"; +}; + +/* Verdin PCIE_1 */ +&pcie0 { + vpcie-supply = <®_pcie>; + + status = "okay"; +}; + +/* We support turning off sleep moci on Dahlia */ +®_force_sleep_moci { + status = "disabled"; +}; + +/* Verdin I2S_1 */ +&sai3 { + status = "okay"; +}; + +/* Verdin PWM_1 */ +&tpm4 { + status = "okay"; +}; + +/* Verdin PWM_2 */ +&tpm5 { + status = "okay"; +}; + +/* Verdin PWM_3_DSI */ +&tpm6 { + status = "okay"; +}; + +/* Verdin USB_1 */ +&usb2 { + status = "okay"; +}; + +/* Verdin USB_2 */ +&usb3 { + fsl,permanently-attached; + + status = "okay"; +}; + +&usb3_dwc3 { + #address-cells = <1>; + #size-cells = <0>; + + usb_hub_3_0: usb-hub@1 { + compatible = "usb424,5744"; + reg = <1>; + peer-hub = <&usb_hub_2_0>; + vdd-supply = <®_usb_hub>; + }; + + usb_hub_2_0: usb-hub@2 { + compatible = "usb424,2744"; + reg = <2>; + peer-hub = <&usb_hub_3_0>; + vdd-supply = <®_usb_hub>; + }; +}; + +&usb3_phy { + status = "okay"; +}; + +/* Verdin SD_1 */ +&usdhc2 { + status = "okay"; +}; + +/* Verdin CTRL_WAKE1_MICO# */ +&verdin_gpio_keys { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-dev.dtsi b/arch/arm64/boot/dts/freescale/imx95-verdin-dev.dtsi new file mode 100644 index 000000000000..2848f9adf152 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-dev.dtsi @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * Common dtsi for Verdin iMX95 SoM on development carrier board + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/verdin-development-board-kit + */ + +/ { + aliases { + eeprom1 = &carrier_eeprom; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,bitclock-master = <&codec_dai>; + simple-audio-card,format = "i2s"; + simple-audio-card,frame-master = <&codec_dai>; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "verdin-nau8822"; + simple-audio-card,routing = + "Headphones", "LHP", + "Headphones", "RHP", + "Speaker", "LSPK", + "Speaker", "RSPK", + "Line Out", "AUXOUT1", + "Line Out", "AUXOUT2", + "LAUX", "Line In", + "RAUX", "Line In", + "LMICP", "Mic In", + "RMICP", "Mic In"; + simple-audio-card,widgets = + "Headphones", "Headphones", + "Line Out", "Line Out", + "Speaker", "Speaker", + "Microphone", "Mic In", + "Line", "Line In"; + + codec_dai: simple-audio-card,codec { + clocks = <&scmi_clk IMX95_CLK_SAI3>; + sound-dai = <&nau8822_1a>; + }; + + simple-audio-card,cpu { + sound-dai = <&sai3>; + }; + }; +}; + +/* Verdin ADC_1, ADC_2, ADC_3 and ADC_4 */ +&adc1 { + status = "okay"; +}; + +/* Verdin ETH_1 (On-module PHY) */ +&enetc_port0 { + status = "okay"; +}; + +/* Verdin ETH_2_RGMII */ +&enetc_port1 { + phy-handle = <ðphy2>; + phy-mode = "rgmii-id"; + + status = "okay"; +}; + +/* Verdin CAN_1 */ +&flexcan1 { + status = "okay"; +}; + +/* Verdin CAN_2 */ +&flexcan2 { + status = "okay"; +}; + +/* Verdin QSPI_1 */ +&flexspi1 { + status = "okay"; +}; + +&gpio1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ctrl_sleep_moci>; +}; + +&gpio2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio1>, + <&pinctrl_gpio2>, + <&pinctrl_gpio3>; +}; + +&gpio3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio6>; +}; + +&gpio4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio5>; +}; + +&gpio5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio4>; +}; + +/* Verdin I2C_3_HDMI */ +&i3c2 { + status = "okay"; +}; + +/* Verdin I2C_2_DSI */ +&lpi2c3 { + status = "okay"; +}; + +/* Verdin I2C_1 */ +&lpi2c4 { + status = "okay"; + + nau8822_1a: audio-codec@1a { + compatible = "nuvoton,nau8822"; + reg = <0x1a>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sai3_mclk>; + #sound-dai-cells = <0>; + }; + + carrier_gpio_expander: gpio@21 { + compatible = "nxp,pcal6416"; + reg = <0x21>; + #gpio-cells = <2>; + gpio-controller; + }; + + /* Current measurement into module VCC */ + hwmon@40 { + compatible = "ti,ina219"; + reg = <0x40>; + shunt-resistor = <10000>; + }; + + temperature-sensor@4f { + compatible = "ti,tmp75c"; + reg = <0x4f>; + }; + + carrier_eeprom: eeprom@57 { + compatible = "st,24c02", "atmel,24c02"; + reg = <0x57>; + pagesize = <16>; + }; +}; + +/* Verdin I2C_4_CSI */ +&lpi2c5 { + status = "okay"; +}; + +/* Verdin UART_3, used as the Linux console */ +&lpuart1 { + status = "okay"; +}; + +/* Verdin UART_4 */ +&lpuart2 { + status = "okay"; +}; + +/* Verdin UART_1, connector X50 through RS485 transceiver */ +&lpuart7 { + rs485-rts-active-low; + rs485-rx-during-tx; + linux,rs485-enabled-at-boot-time; + + status = "okay"; +}; + +/* Verdin UART_2 */ +&lpuart8 { + status = "okay"; +}; + +&netc_emdio { + ethphy2: ethernet-phy@7 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <7>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_eth2_rgmii_int>; + interrupt-parent = <&gpio1>; + interrupts = <12 IRQ_TYPE_LEVEL_LOW>; + micrel,led-mode = <0>; + }; +}; + +/* Verdin PCIE_1 */ +&pcie0 { + status = "okay"; +}; + +/* Verdin I2S_1 */ +&sai3 { + status = "okay"; +}; + +/* Verdin PWM_1 */ +&tpm4 { + status = "okay"; +}; + +/* Verdin PWM_2 */ +&tpm5 { + status = "okay"; +}; + +/* Verdin PWM_3_DSI */ +&tpm6 { + status = "okay"; +}; + +/* Verdin USB_1 */ +&usb2 { + status = "okay"; +}; + +/* Verdin USB_2 */ +&usb3 { + fsl,permanently-attached; + + status = "okay"; +}; + +&usb3_phy { + status = "okay"; +}; + +/* Verdin SD_1 */ +&usdhc2 { + status = "okay"; +}; + +/* Verdin CTRL_WAKE1_MICO# */ +&verdin_gpio_keys { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-ivy.dtsi b/arch/arm64/boot/dts/freescale/imx95-verdin-ivy.dtsi new file mode 100644 index 000000000000..8337c8b25f05 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-ivy.dtsi @@ -0,0 +1,515 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * Common dtsi for Verdin iMX95 SoM on Ivy carrier board + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/ivy-carrier-board + */ + +#include +#include +#include + +/ { + aliases { + eeprom1 = &carrier_eeprom; + }; + + ain1-current { + compatible = "io-channel-mux"; + channels = "", "ain1_current"; + io-channels = <&ain1_current_unmanaged>; + io-channel-names = "parent"; + mux-controls = <&ain1_mode_mux_ctrl>; + settle-time-us = <1000>; + }; + + ain1-voltage { + compatible = "io-channel-mux"; + channels = "ain1_voltage", ""; + io-channels = <&ain1_voltage_unmanaged 0>; + io-channel-names = "parent"; + mux-controls = <&ain1_mode_mux_ctrl>; + settle-time-us = <1000>; + }; + + ain2-current { + compatible = "io-channel-mux"; + channels = "", "ain2_current"; + io-channels = <&ain2_current_unmanaged>; + io-channel-names = "parent"; + mux-controls = <&ain2_mode_mux_ctrl>; + settle-time-us = <1000>; + }; + + ain2-voltage { + compatible = "io-channel-mux"; + channels = "ain2_voltage", ""; + io-channels = <&ain2_voltage_unmanaged 0>; + io-channel-names = "parent"; + mux-controls = <&ain2_mode_mux_ctrl>; + settle-time-us = <1000>; + }; + + /* AIN1 Current w/o AIN1_MODE gpio control */ + ain1_current_unmanaged: current-sense-shunt-ain1 { + compatible = "current-sense-shunt"; + #io-channel-cells = <0>; + io-channels = <&ivy_adc1 1>; + shunt-resistor-micro-ohms = <100000000>; + }; + + /* AIN2 Current w/o AIN2_MODE gpio control */ + ain2_current_unmanaged: current-sense-shunt-ain2 { + compatible = "current-sense-shunt"; + #io-channel-cells = <0>; + io-channels = <&ivy_adc2 1>; + shunt-resistor-micro-ohms = <100000000>; + }; + + /* Ivy Power Supply Input Voltage */ + ivy-1v8-voltage { + compatible = "voltage-divider"; + /* Verdin ADC_4 */ + io-channels = <&adc1 3>; + full-ohms = <39000>; /* 12k + 27k */ + output-ohms = <27000>; + }; + + ivy-3v3-voltage { + compatible = "voltage-divider"; + /* Verdin ADC_3 */ + io-channels = <&adc1 2>; + full-ohms = <54000>; /* 27k + 27k */ + output-ohms = <27000>; + }; + + ivy-5v-voltage { + compatible = "voltage-divider"; + /* Verdin ADC_2 */ + io-channels = <&adc1 1>; + full-ohms = <39000>; /* 27k + 12k */ + output-ohms = <12000>; + }; + + ivy-input-voltage { + compatible = "voltage-divider"; + /* Verdin ADC_1 */ + io-channels = <&adc1 0>; + full-ohms = <204700>; /* 200k + 4.7k */ + output-ohms = <4700>; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ivy_leds>; + + /* D7 Blue - SODIMM 30 - LEDs.GPIO1 */ + led-0 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <1>; + gpios = <&gpio2 16 GPIO_ACTIVE_HIGH>; + }; + + /* D7 Green - SODIMM 32 - LEDs.GPIO2 */ + led-1 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <1>; + gpios = <&gpio2 26 GPIO_ACTIVE_HIGH>; + }; + + /* D7 Red - SODIMM 34 - LEDs.GPIO3 */ + led-2 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <1>; + gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>; + }; + + /* D8 Blue - SODIMM 36 - LEDs.GPIO4 */ + led-3 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <2>; + gpios = <&gpio2 20 GPIO_ACTIVE_HIGH>; + }; + + /* D8 Green - SODIMM 54 - LEDs.GPIO5 */ + led-4 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <2>; + gpios = <&gpio5 10 GPIO_ACTIVE_HIGH>; + }; + + /* D8 Red - SODIMM 44 - LEDs.GPIO6 */ + led-5 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <2>; + gpios = <&gpio5 5 GPIO_ACTIVE_HIGH>; + }; + + /* D9 Blue - SODIMM 46 - LEDs.GPIO7 */ + led-6 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <3>; + gpios = <&gpio5 4 GPIO_ACTIVE_HIGH>; + }; + + /* D9 Red - SODIMM 48 - LEDs.GPIO8 */ + led-7 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <3>; + gpios = <&gpio5 7 GPIO_ACTIVE_HIGH>; + }; + }; + + /* AIN1_MODE - SODIMM 216 */ + ain1_mode_mux_ctrl: mux-controller-0 { + compatible = "gpio-mux"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio5>; + #mux-control-cells = <0>; + mux-gpios = <&gpio4 28 GPIO_ACTIVE_HIGH>; + }; + + /* AIN2_MODE - SODIMM 218 */ + ain2_mode_mux_ctrl: mux-controller-1 { + compatible = "gpio-mux"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio6>; + #mux-control-cells = <0>; + mux-gpios = <&gpio3 27 GPIO_ACTIVE_HIGH>; + }; + + reg_3v2_ain1: regulator-3v2-ain1 { + compatible = "regulator-fixed"; + regulator-max-microvolt = <3200000>; + regulator-min-microvolt = <3200000>; + regulator-name = "+3V2_AIN1"; + }; + + reg_3v2_ain2: regulator-3v2-ain2 { + compatible = "regulator-fixed"; + regulator-max-microvolt = <3200000>; + regulator-min-microvolt = <3200000>; + regulator-name = "+3V2_AIN2"; + }; + + /* AIN1 Voltage w/o AIN1_MODE gpio control */ + ain1_voltage_unmanaged: voltage-divider-ain1 { + compatible = "voltage-divider"; + #io-channel-cells = <1>; + io-channels = <&ivy_adc1 0>; + full-ohms = <19>; + output-ohms = <1>; + }; + + /* AIN2 Voltage w/o AIN2_MODE gpio control */ + ain2_voltage_unmanaged: voltage-divider-ain2 { + compatible = "voltage-divider"; + #io-channel-cells = <1>; + io-channels = <&ivy_adc2 0>; + full-ohms = <19>; + output-ohms = <1>; + }; +}; + +/* Verdin ADC_1, ADC_2, ADC_3 and ADC_4 */ +&adc1 { + status = "okay"; +}; + +/* Verdin ETH_1 (On-module PHY) */ +&enetc_port0 { + status = "okay"; +}; + +/* Verdin ETH_2_RGMII */ +&enetc_port1 { + phy-handle = <ðphy2>; + phy-mode = "rgmii-id"; + + status = "okay"; +}; + +/* Verdin CAN_1 */ +&flexcan1 { + status = "okay"; +}; + +/* Verdin CAN_2 */ +&flexcan2 { + status = "okay"; +}; + +&gpio1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ctrl_sleep_moci>; + gpio-line-names = ""; +}; + +&gpio2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio2>, + <&pinctrl_gpio3>; + gpio-line-names = + "", /* 0 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 10 */ + "", + "", + "", + "", + "", + "", + "", + "GPIO2", /* Verdin GPIO_2 - SODIMM 208 */ + "", + "", /* 20 */ + "", + "", + "", + "GPIO3", /* Verdin GPIO_3 - SODIMM 210 */ + "", + "", + "", + "", + "", + "", /* 30 */ + ""; +}; + +&gpio3 { + gpio-line-names = ""; +}; + +&gpio4 { + gpio-line-names = ""; +}; + +&gpio5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_qspi1_cs2_gpio>, + <&pinctrl_qspi1_dqs_gpio>, + <&pinctrl_qspi1_io0_gpio>, + <&pinctrl_qspi1_io1_gpio>, + <&pinctrl_qspi1_io2_gpio>, + <&pinctrl_qspi1_io3_gpio>; + gpio-line-names = + "DIGI_1", /* SODIMM 56 */ + "DIGI_2", /* SODIMM 58 */ + "REL1", /* SODIMM 60 */ + "REL2", /* SODIMM 62 */ + "", + "", + "", + "", + "REL4", /* SODIMM 66 */ + "", + "", /* 10 */ + "REL3", /* SODIMM 64 */ + "", + "", + "", + "", + "", + ""; +}; + +/* Verdin I2C_1 */ +&lpi2c4 { + status = "okay"; + + temperature-sensor@4f { + compatible = "ti,tmp1075"; + reg = <0x4f>; + }; + + carrier_eeprom: eeprom@57 { + compatible = "st,24c02", "atmel,24c02"; + reg = <0x57>; + pagesize = <16>; + }; +}; + +/* Verdin I2C_4_CSI */ +&lpi2c5 { + status = "okay"; + + ivy_adc1: adc@40 { + compatible = "ti,ads1119"; + reg = <0x40>; + interrupt-parent = <&som_gpio_expander>; + interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + avdd-supply = <®_3v2_ain1>; + dvdd-supply = <®_3v2_ain1>; + vref-supply = <®_3v2_ain1>; + #address-cells = <1>; + #io-channel-cells = <1>; + #size-cells = <0>; + + /* AIN1 0-33V Voltage Input */ + channel@0 { + reg = <0>; + diff-channels = <0 1>; + }; + + /* AIN1 0-20mA Current Input */ + channel@1 { + reg = <1>; + diff-channels = <2 3>; + }; + }; + + ivy_adc2: adc@41 { + compatible = "ti,ads1119"; + reg = <0x41>; + interrupt-parent = <&som_gpio_expander>; + interrupts = <1 IRQ_TYPE_EDGE_FALLING>; + avdd-supply = <®_3v2_ain2>; + dvdd-supply = <®_3v2_ain2>; + vref-supply = <®_3v2_ain2>; + #address-cells = <1>; + #io-channel-cells = <1>; + #size-cells = <0>; + + /* AIN2 0-33V Voltage Input */ + channel@0 { + reg = <0>; + diff-channels = <0 1>; + }; + + /* AIN2 0-20mA Current Input */ + channel@1 { + reg = <1>; + diff-channels = <2 3>; + }; + }; +}; + +/* Verdin SPI_1 */ +&lpspi6 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lpspi6>, + <&pinctrl_spi1_cs>, + <&pinctrl_gpio1>, + <&pinctrl_gpio4>; + cs-gpios = <&gpio4 29 GPIO_ACTIVE_LOW>, + <&som_gpio_expander 13 GPIO_ACTIVE_LOW>, + <&gpio2 0 GPIO_ACTIVE_LOW>, + <&gpio5 12 GPIO_ACTIVE_LOW>; + + tpm@2 { + compatible = "infineon,slb9670", "tcg,tpm_tis-spi"; + reg = <2>; + spi-max-frequency = <18500000>; + }; + + fram@3 { + compatible = "fujitsu,mb85rs256", "atmel,at25"; + reg = <3>; + address-width = <16>; + size = <32768>; + spi-max-frequency = <33000000>; + pagesize = <1>; + }; +}; + +/* Verdin UART_3, used as the Linux console */ +&lpuart1 { + status = "okay"; +}; + +/* Verdin UART_1 */ +&lpuart7 { + status = "okay"; +}; + +/* Verdin UART_2, through RS485 transceiver */ +&lpuart8 { + rs485-rts-active-low; + rs485-rx-during-tx; + linux,rs485-enabled-at-boot-time; + + status = "okay"; +}; + +&netc_emdio { + ethphy2: ethernet-phy@2 { + reg = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_eth2_rgmii_int>; + interrupt-parent = <&gpio1>; + interrupts = <12 IRQ_TYPE_LEVEL_LOW>; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; + }; +}; + +/* Verdin PCIE_1 */ +&pcie0 { + status = "okay"; +}; + +&som_gpio_expander { + gpio-line-names = ""; +}; + +/* Verdin USB_1 */ +&usb2 { + status = "okay"; +}; + +/* Verdin USB_2 */ +&usb3 { + fsl,permanently-attached; + + status = "okay"; +}; + +&usb3_phy { + status = "okay"; +}; + +/* Verdin SD_1 */ +&usdhc2 { + status = "okay"; +}; + +&scmi_iomuxc { + pinctrl_ivy_leds: ivyledsgrp { + fsl,pins = , /* SODIMM 30 */ + , /* SODIMM 32 */ + , /* SODIMM 34 */ + , /* SODIMM 36 */ + , /* SODIMM 44 */ + , /* SODIMM 46 */ + , /* SODIMM 48 */ + ; /* SODIMM 54 */ + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-mallow.dtsi b/arch/arm64/boot/dts/freescale/imx95-verdin-mallow.dtsi new file mode 100644 index 000000000000..53506b7550f5 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-mallow.dtsi @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * Common dtsi for Verdin iMX95 SoM on Mallow carrier board + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/mallow-carrier-board + */ + +#include + +/ { + aliases { + eeprom1 = &carrier_eeprom; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_qspi1_clk_gpio>, + <&pinctrl_qspi1_cs_gpio>, + <&pinctrl_qspi1_io0_gpio>, + <&pinctrl_qspi1_io1_gpio>; + + /* SODIMM 52 - USER_LED_1_RED */ + led-0 { + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <1>; + gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>; + }; + + /* SODIMM 54 - USER_LED_1_GREEN */ + led-1 { + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <1>; + gpios = <&gpio5 10 GPIO_ACTIVE_HIGH>; + }; + + /* SODIMM 56 - USER_LED_2_RED */ + led-2 { + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <2>; + gpios = <&gpio5 0 GPIO_ACTIVE_HIGH>; + }; + + /* SODIMM 58 - USER_LED_2_GREEN */ + led-3 { + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <2>; + gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +/* Verdin ADC_1, ADC_2, ADC_3 and ADC_4 */ +&adc1 { + status = "okay"; +}; + +/* Verdin ETH_1 (On-module PHY) */ +&enetc_port0 { + status = "okay"; +}; + +/* Verdin CAN_1 */ +&flexcan1 { + status = "okay"; +}; + +/* Verdin CAN_2 */ +&flexcan2 { + status = "okay"; +}; + +&gpio1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ctrl_sleep_moci>; +}; + +&gpio2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio1>, + <&pinctrl_gpio2>, + <&pinctrl_gpio3>; +}; + +&gpio3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio6>; +}; + +&gpio4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio5>; +}; + +&gpio5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio4>; +}; + +/* Verdin I2C_3_HDMI */ +&i3c2 { + status = "okay"; +}; + +/* Verdin I2C_2_DSI */ +&lpi2c3 { + status = "okay"; +}; + +/* Verdin I2C_1 */ +&lpi2c4 { + status = "okay"; + + temperature-sensor@4f { + compatible = "ti,tmp1075"; + reg = <0x4f>; + }; + + carrier_eeprom: eeprom@57 { + compatible = "st,24c02", "atmel,24c02"; + reg = <0x57>; + pagesize = <16>; + }; +}; + +/* Verdin I2C_4_CSI */ +&lpi2c5 { + status = "okay"; +}; + +/* Verdin SPI_1 */ +&lpspi6 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lpspi6>, + <&pinctrl_spi1_cs>, + <&pinctrl_qspi1_cs2_gpio>; + cs-gpios = <&gpio4 29 GPIO_ACTIVE_LOW>, + <&som_gpio_expander 13 GPIO_ACTIVE_LOW>, + <&gpio5 11 GPIO_ACTIVE_LOW>; + + tpm@2 { + compatible = "infineon,slb9670", "tcg,tpm_tis-spi"; + reg = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_qspi1_dqs_gpio>; + interrupt-parent = <&gpio5>; + interrupts = <8 IRQ_TYPE_EDGE_FALLING>; + spi-max-frequency = <18500000>; + }; +}; + +/* Verdin UART_3, used as the Linux console */ +&lpuart1 { + status = "okay"; +}; + +/* Verdin UART_4 */ +&lpuart2 { + status = "okay"; +}; + +/* Verdin UART_1 */ +&lpuart7 { + status = "okay"; +}; + +/* Verdin UART_2 */ +&lpuart8 { + status = "okay"; +}; + +/* Verdin PCIE_1 */ +&pcie0 { + status = "okay"; +}; + +/* Verdin PWM_1 */ +&tpm4 { + status = "okay"; +}; + +/* Verdin PWM_2 */ +&tpm5 { + status = "okay"; +}; + +/* Verdin PWM_3_DSI */ +&tpm6 { + status = "okay"; +}; + +/* Verdin USB_1 */ +&usb2 { + status = "okay"; +}; + +/* Verdin USB_2 */ +&usb3 { + fsl,permanently-attached; + + status = "okay"; +}; + +&usb3_phy { + status = "okay"; +}; + +/* Verdin SD_1 */ +&usdhc2 { + status = "okay"; +}; + +/* Verdin CTRL_WAKE1_MICO# */ +&verdin_gpio_keys { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-dahlia.dts b/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-dahlia.dts new file mode 100644 index 000000000000..16975ae12fcb --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-dahlia.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/dahlia-carrier-board-kit + */ + +/dts-v1/; + +#include "imx95-verdin.dtsi" +#include "imx95-verdin-nonwifi.dtsi" +#include "imx95-verdin-dahlia.dtsi" + +/ { + model = "Toradex Verdin iMX95 on Dahlia Board"; + compatible = "toradex,verdin-imx95-nonwifi-dahlia", + "toradex,verdin-imx95-nonwifi", + "toradex,verdin-imx95", + "fsl,imx95"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-dev.dts b/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-dev.dts new file mode 100644 index 000000000000..97636ec7c26a --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-dev.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/verdin-development-board-kit + */ + +/dts-v1/; + +#include "imx95-verdin.dtsi" +#include "imx95-verdin-nonwifi.dtsi" +#include "imx95-verdin-dev.dtsi" + +/ { + model = "Toradex Verdin iMX95 on Verdin Development Board"; + compatible = "toradex,verdin-imx95-nonwifi-dev", + "toradex,verdin-imx95-nonwifi", + "toradex,verdin-imx95", + "fsl,imx95"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-ivy.dts b/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-ivy.dts new file mode 100644 index 000000000000..ebe1aec1ffa4 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-ivy.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/ivy-carrier-board + */ + +/dts-v1/; + +#include "imx95-verdin.dtsi" +#include "imx95-verdin-nonwifi.dtsi" +#include "imx95-verdin-ivy.dtsi" + +/ { + model = "Toradex Verdin iMX95 on Ivy Board"; + compatible = "toradex,verdin-imx95-nonwifi-ivy", + "toradex,verdin-imx95-nonwifi", + "toradex,verdin-imx95", + "fsl,imx95"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-mallow.dts b/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-mallow.dts new file mode 100644 index 000000000000..5a9c0e4a79c9 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-mallow.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/mallow-carrier-board + */ + +/dts-v1/; + +#include "imx95-verdin.dtsi" +#include "imx95-verdin-nonwifi.dtsi" +#include "imx95-verdin-mallow.dtsi" + +/ { + model = "Toradex Verdin iMX95 on Mallow Board"; + compatible = "toradex,verdin-imx95-nonwifi-mallow", + "toradex,verdin-imx95-nonwifi", + "toradex,verdin-imx95", + "fsl,imx95"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-yavia.dts b/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-yavia.dts new file mode 100644 index 000000000000..4f7b4e3a518b --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi-yavia.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/yavia + */ + +/dts-v1/; + +#include "imx95-verdin.dtsi" +#include "imx95-verdin-nonwifi.dtsi" +#include "imx95-verdin-yavia.dtsi" + +/ { + model = "Toradex Verdin iMX95 on Yavia Board"; + compatible = "toradex,verdin-imx95-nonwifi-yavia", + "toradex,verdin-imx95-nonwifi", + "toradex,verdin-imx95", + "fsl,imx95"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi.dtsi b/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi.dtsi new file mode 100644 index 000000000000..7aba22067de5 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-nonwifi.dtsi @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * Common dtsi for Verdin iMX95 SoM non-WB variant + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + */ + +/* SDIO on MSP 30, 31, 32, 33, 34, 35 */ +&usdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3>; + + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-dahlia.dts b/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-dahlia.dts new file mode 100644 index 000000000000..bafbe1179ec8 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-dahlia.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/dahlia-carrier-board-kit + */ + +/dts-v1/; + +#include "imx95-verdin.dtsi" +#include "imx95-verdin-wifi.dtsi" +#include "imx95-verdin-dahlia.dtsi" + +/ { + model = "Toradex Verdin iMX95 WB on Dahlia Board"; + compatible = "toradex,verdin-imx95-wifi-dahlia", + "toradex,verdin-imx95-wifi", + "toradex,verdin-imx95", + "fsl,imx95"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-dev.dts b/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-dev.dts new file mode 100644 index 000000000000..345d37247025 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-dev.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/verdin-development-board-kit + */ + +/dts-v1/; + +#include "imx95-verdin.dtsi" +#include "imx95-verdin-wifi.dtsi" +#include "imx95-verdin-dev.dtsi" + +/ { + model = "Toradex Verdin iMX95 WB on Verdin Development Board"; + compatible = "toradex,verdin-imx95-wifi-dev", + "toradex,verdin-imx95-wifi", + "toradex,verdin-imx95", + "fsl,imx95"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-ivy.dts b/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-ivy.dts new file mode 100644 index 000000000000..7ff2d03a254d --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-ivy.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/ivy-carrier-board + */ + +/dts-v1/; + +#include "imx95-verdin.dtsi" +#include "imx95-verdin-wifi.dtsi" +#include "imx95-verdin-ivy.dtsi" + +/ { + model = "Toradex Verdin iMX95 WB on Ivy Board"; + compatible = "toradex,verdin-imx95-wifi-ivy", + "toradex,verdin-imx95-wifi", + "toradex,verdin-imx95", + "fsl,imx95"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-mallow.dts b/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-mallow.dts new file mode 100644 index 000000000000..eaa67a39be1c --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-mallow.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/mallow-carrier-board + */ + +/dts-v1/; + +#include "imx95-verdin.dtsi" +#include "imx95-verdin-wifi.dtsi" +#include "imx95-verdin-mallow.dtsi" + +/ { + model = "Toradex Verdin iMX95 WB on Mallow Board"; + compatible = "toradex,verdin-imx95-wifi-mallow", + "toradex,verdin-imx95-wifi", + "toradex,verdin-imx95", + "fsl,imx95"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-yavia.dts b/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-yavia.dts new file mode 100644 index 000000000000..43d35b770db2 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-wifi-yavia.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/yavia + */ + +/dts-v1/; + +#include "imx95-verdin.dtsi" +#include "imx95-verdin-wifi.dtsi" +#include "imx95-verdin-yavia.dtsi" + +/ { + model = "Toradex Verdin iMX95 WB on Yavia Board"; + compatible = "toradex,verdin-imx95-wifi-yavia", + "toradex,verdin-imx95-wifi", + "toradex,verdin-imx95", + "fsl,imx95"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-wifi.dtsi b/arch/arm64/boot/dts/freescale/imx95-verdin-wifi.dtsi new file mode 100644 index 000000000000..256c9ed04605 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-wifi.dtsi @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * Common dtsi for Verdin iMX95 SoM WB variant + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + */ + +/ { + reg_wifi_en: regulator-wifi-en { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_wifi_pwr_en>; + /* PMIC_EN_WIFI */ + gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "PDn_MAYA-W260"; + startup-delay-us = <2000>; + }; +}; + +/* On-module Bluetooth */ +&lpuart6 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_bt_uart>; + uart-has-rtscts; + + status = "okay"; + + som_bt: bluetooth { + compatible = "nxp,88w8987-bt"; + fw-init-baudrate = <3000000>; + }; +}; + +/* On-module Wi-Fi */ +&usdhc3 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc3>; + pinctrl-1 = <&pinctrl_usdhc3>; + pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + keep-power-in-suspend; + non-removable; + vmmc-supply = <®_wifi_en>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin-yavia.dtsi b/arch/arm64/boot/dts/freescale/imx95-verdin-yavia.dtsi new file mode 100644 index 000000000000..6403ae584e70 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin-yavia.dtsi @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * Common dtsi for Verdin iMX95 SoM on Yavia carrier board + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + * https://www.toradex.com/products/carrier-board/yavia + */ + +#include + +/ { + aliases { + eeprom1 = &carrier_eeprom; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_qspi1_clk_gpio>, + <&pinctrl_qspi1_cs_gpio>, + <&pinctrl_qspi1_io0_gpio>, + <&pinctrl_qspi1_io1_gpio>, + <&pinctrl_qspi1_io2_gpio>, + <&pinctrl_qspi1_io3_gpio>; + + /* SODIMM 52 - LD1_RED */ + led-0 { + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <1>; + gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>; + }; + /* SODIMM 54 - LD1_GREEN */ + led-1 { + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <1>; + gpios = <&gpio5 10 GPIO_ACTIVE_HIGH>; + }; + /* SODIMM 56 - LD1_BLUE */ + led-2 { + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <1>; + gpios = <&gpio5 0 GPIO_ACTIVE_HIGH>; + }; + /* SODIMM 58 - LD2_RED */ + led-3 { + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <2>; + gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>; + }; + /* SODIMM 60 - LD2_GREEN */ + led-4 { + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <2>; + gpios = <&gpio5 2 GPIO_ACTIVE_HIGH>; + }; + /* SODIMM 62 - LD2_BLUE */ + led-5 { + color = ; + function = LED_FUNCTION_DEBUG; + function-enumerator = <2>; + gpios = <&gpio5 3 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +/* Verdin ADC_1, ADC_2, ADC_3 and ADC_4 */ +&adc1 { + status = "okay"; +}; + +/* Verdin ETH_1 (On-module PHY) */ +&enetc_port0 { + status = "okay"; +}; + +/* Verdin CAN_1 */ +&flexcan1 { + status = "okay"; +}; + +/* Verdin CAN_2 */ +&flexcan2 { + status = "okay"; +}; + +&gpio1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ctrl_sleep_moci>; +}; + +&gpio2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio1>, + <&pinctrl_gpio2>, + <&pinctrl_gpio3>; +}; + +&gpio3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio6>; +}; + +&gpio4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio5>; +}; + +&gpio5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio4>, + <&pinctrl_qspi1_cs2_gpio>, + <&pinctrl_qspi1_dqs_gpio>; +}; + +/* Verdin I2C_3_HDMI */ +&i3c2 { + status = "okay"; +}; + +/* Verdin I2C_2_DSI */ +&lpi2c3 { + status = "okay"; +}; + +/* Verdin I2C_1 */ +&lpi2c4 { + status = "okay"; + + temperature-sensor@4f { + compatible = "ti,tmp75c"; + reg = <0x4f>; + }; + + carrier_eeprom: eeprom@57 { + compatible = "st,24c02", "atmel,24c02"; + reg = <0x57>; + pagesize = <16>; + }; +}; + +/* Verdin I2C_4_CSI */ +&lpi2c5 { + status = "okay"; +}; + +/* Verdin UART_3, used as the Linux console */ +&lpuart1 { + status = "okay"; +}; + +/* Verdin UART_4 */ +&lpuart2 { + status = "okay"; +}; + +/* Verdin UART_1 */ +&lpuart7 { + status = "okay"; +}; + +/* Verdin UART_2 */ +&lpuart8 { + status = "okay"; +}; + +/* Verdin PCIE_1 */ +&pcie0 { + status = "okay"; +}; + +/* Verdin PWM_1 */ +&tpm4 { + status = "okay"; +}; + +/* Verdin PWM_2 */ +&tpm5 { + status = "okay"; +}; + +/* Verdin PWM_3_DSI */ +&tpm6 { + status = "okay"; +}; + +/* Verdin USB_1 */ +&usb2 { + status = "okay"; +}; + +/* Verdin USB_2 */ +&usb3 { + fsl,permanently-attached; + + status = "okay"; +}; + +&usb3_phy { + status = "okay"; +}; + +/* Verdin SD_1 */ +&usdhc2 { + status = "okay"; +}; + +/* Verdin CTRL_WAKE1_MICO# */ +&verdin_gpio_keys { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-verdin.dtsi b/arch/arm64/boot/dts/freescale/imx95-verdin.dtsi new file mode 100644 index 000000000000..d3737956e2f9 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-verdin.dtsi @@ -0,0 +1,1162 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * Common dtsi for Verdin iMX95 SoM + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/nxp-imx95 + */ + +#include +#include "imx95.dtsi" + +/ { + aliases { + can0 = &flexcan1; + can1 = &flexcan2; + eeprom0 = &som_eeprom; + ethernet0 = &enetc_port0; + ethernet1 = &enetc_port1; + i2c0 = &lpi2c2; + i2c1 = &lpi2c4; + i2c2 = &lpi2c3; + i2c3 = &i3c2; + i2c4 = &lpi2c5; + mmc0 = &usdhc1; + mmc1 = &usdhc2; + mmc2 = &usdhc3; + rtc0 = &rtc_i2c; + rtc1 = &scmi_bbm; + serial0 = &lpuart7; + serial1 = &lpuart8; + serial2 = &lpuart1; + serial3 = &lpuart2; + serial4 = &lpuart6; + usb0 = &usb2; + usb1 = &usb3; + }; + + chosen { + stdout-path = "serial2:115200n8"; + }; + + connector { + compatible = "gpio-usb-b-connector", "usb-b-connector"; + /* Verdin USB_1_ID (SODIMM 161) */ + id-gpios = <&som_gpio_expander 5 GPIO_ACTIVE_HIGH>; + label = "USB_1"; + self-powered; + vbus-supply = <®_usb1_vbus>; + + port { + usb_dr_connector: endpoint { + remote-endpoint = <&usb1_id>; + }; + }; + }; + + verdin_gpio_keys: gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ctrl_wake1_mico>; + + status = "disabled"; + + verdin_key_wakeup: key-wakeup { + /* Verdin CTRL_WAKE1_MICO# (SODIMM 252) */ + gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; + label = "Wake-Up"; + linux,code = ; + wakeup-source; + }; + }; + + reg_1p8v: regulator-1p8v { + compatible = "regulator-fixed"; + regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <1800000>; + regulator-name = "On-module +V1.8"; + }; + + /* + * By default we enable CTRL_SLEEP_MOCI#, this is required to have + * peripherals on the carrier board powered. + * If more granularity or power saving is required this can be disabled + * in the carrier board device tree files. + */ + reg_force_sleep_moci: regulator-force-sleep-moci { + compatible = "regulator-fixed"; + /* Verdin CTRL_SLEEP_MOCI# (SODIMM 256) */ + gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-always-on; + regulator-boot-on; + regulator-name = "CTRL_SLEEP_MOCI#"; + }; + + reg_usb1_vbus: regulator-usb1-vbus { + compatible = "regulator-fixed"; + /* Verdin USB_1_EN (SODIMM 155) */ + gpios = <&som_gpio_expander 7 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-name = "USB_1_EN"; + }; + + reg_usb2_vbus: regulator-usb2-vbus { + compatible = "regulator-fixed"; + /* Verdin USB_2_EN (SODIMM 185) */ + gpios = <&som_gpio_expander 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-name = "USB_2_EN"; + }; + + reg_usdhc2_vqmmc: regulator-usdhc2-vqmmc { + compatible = "regulator-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc2_vsel>; + gpios = <&gpio3 19 GPIO_ACTIVE_HIGH>; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <1800000>; + states = <1800000 0x1>, + <3300000 0x0>; + regulator-name = "PMIC_SD2_VSEL"; + }; + + reg_usdhc2_vmmc: regulator-vmmc-usdhc2 { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc2_pwr_en>; + /* Verdin SD_1_PWR_EN (SODIMM 76) */ + gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + enable-active-high; + off-on-delay-us = <100000>; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-name = "SD_1_PWR_EN"; + startup-delay-us = <20000>; + }; + + cm7: remoteproc-cm7 { + compatible = "fsl,imx95-cm7"; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&mu7 0 1 + &mu7 1 1 + &mu7 3 1>; + memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, + <&vdev1vring0>, <&vdev1vring1>, <&rsc_table>, <&m7_reserved>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + linux_cma: linux,cma { + compatible = "shared-dma-pool"; + reusable; + size = <0 0x3c000000>; + alloc-ranges = <0 0x80000000 0 0x7F000000>; + linux,cma-default; + }; + + m7_reserved: memory@80000000 { + reg = <0 0x80000000 0 0x1000000>; + no-map; + }; + + vdev0vring0: vdev0vring0@88000000 { + reg = <0 0x88000000 0 0x8000>; + no-map; + }; + + vdev0vring1: vdev0vring1@88008000 { + reg = <0 0x88008000 0 0x8000>; + no-map; + }; + + vdev1vring0: vdev1vring0@88010000 { + reg = <0 0x88010000 0 0x8000>; + no-map; + }; + + vdev1vring1: vdev1vring1@88018000 { + reg = <0 0x88018000 0 0x8000>; + no-map; + }; + + vdevbuffer: vdevbuffer@88020000 { + compatible = "shared-dma-pool"; + reg = <0 0x88020000 0 0x100000>; + no-map; + }; + + rsc_table: rsc-table@88220000 { + reg = <0 0x88220000 0 0x1000>; + no-map; + }; + }; +}; + +/* Verdin ADC_1, ADC_2, ADC_3 and ADC_4 */ +&adc1 { + vref-supply = <®_1p8v>; +}; + +/* Verdin ETH_1 (On-module PHY) */ +&enetc_port0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enetc0>; + phy-handle = <ðphy1>; + phy-mode = "rgmii-id"; +}; + +/* Verdin ETH_2_RGMII */ +&enetc_port1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enetc1>; +}; + +/* Verdin CAN_1 */ +&flexcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan1>; +}; + +/* Verdin CAN_2 */ +&flexcan2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan2>; +}; + +/* Verdin QSPI_1 */ +&flexspi1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexspi1>; +}; + +&gpio1 { + gpio-line-names = + "", /* 0 */ + "", + "", + "", + "SODIMM_147", + "SODIMM_149", + "SODIMM_151", + "SODIMM_153", + "SODIMM_20", + "SODIMM_22", + "SODIMM_252", /* 10 */ + "", + "SODIMM_189", + "IO_EXP_INT", + "SODIMM_256", + ""; + + status = "okay"; +}; + +&gpio2 { + gpio-line-names = + "SODIMM_206", /* 0 */ + "SODIMM_198", + "SODIMM_200", + "SODIMM_196", + "", + "SODIMM_15", + "SODIMM_16", + "", + "SODIMM_131", + "SODIMM_129", + "SODIMM_135", /* 10 */ + "SODIMM_133", + "SODIMM_139", + "SODIMM_137", + "SODIMM_143", + "SODIMM_141", + "SODIMM_30", + "SODIMM_38", + "SODIMM_208", + "SODIMM_19", + "SODIMM_36", /* 20 */ + "SODIMM_34", + "SODIMM_93", + "SODIMM_95", + "SODIMM_210", + "SODIMM_24", + "SODIMM_32", + "SODIMM_26", + "SODIMM_53", + "SODIMM_55", + "SODIMM_12", /* 30 */ + "SODIMM_14"; +}; + +&gpio3 { + gpio-line-names = + "SODIMM_84", /* 0 */ + "SODIMM_78", + "SODIMM_74", + "SODIMM_80", + "SODIMM_82", + "SODIMM_70", + "SODIMM_72", + "SODIMM_76", + "", + "", + "", /* 10 */ + "", + "", + "", + "", + "", + "", + "", + "", + "PMIC_SD2_VSEL", + "", /* 20 */ + "", + "", + "", + "", + "", + "SODIMM_91", + "SODIMM_218", + "", + "", + "", /* 30 */ + ""; +}; + +&gpio4 { + gpio-line-names = + "SODIMM_59", /* 0 */ + "SODIMM_57", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 10 */ + "", + "", + "", + "SODIMM_193", + "SODIMM_191", + "SODIMM_215", + "SODIMM_217", + "SODIMM_219", + "SODIMM_221", + "SODIMM_211", /* 20 */ + "SODIMM_213", + "SODIMM_199", + "SODIMM_197", + "SODIMM_201", + "SODIMM_203", + "SODIMM_205", + "SODIMM_207", + "SODIMM_216", + "SODIMM_202"; +}; + +&gpio5 { + gpio-line-names = + "SODIMM_56", /* 0 */ + "SODIMM_58", + "SODIMM_60", + "SODIMM_62", + "SODIMM_46", + "SODIMM_44", + "SODIMM_42", + "SODIMM_48", + "SODIMM_66", + "SODIMM_52", + "SODIMM_54", /* 10 */ + "SODIMM_64", + "SODIMM_212", + "", + "", + "", + "", + ""; +}; + +/* Verdin I2C_3_HDMI */ +&i3c2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i3c2>; + i2c-scl-hz = <400000>; +}; + +/* CTRL_I2C (On-module I2C) */ +&lpi2c2 { + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&pinctrl_lpi2c2>, <&pinctrl_io_exp_int>; + pinctrl-1 = <&pinctrl_lpi2c2_gpio>, <&pinctrl_io_exp_int>; + clock-frequency = <400000>; + scl-gpios = <&gpio1 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio1 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + single-master; + + status = "okay"; + + som_gpio_expander: gpio@20 { + compatible = "nxp,pcal6416"; + reg = <0x20>; + #interrupt-cells = <2>; + interrupt-controller; + interrupt-parent = <&gpio1>; + interrupts = <13 IRQ_TYPE_LEVEL_LOW>; + #gpio-cells = <2>; + gpio-controller; + + gpio-line-names = + "SODIMM_220", /* 0 */ + "SODIMM_222", + "SODIMM_17", + "SODIMM_21", + "SODIMM_244", + "SODIMM_161", + "SODIMM_157", + "SODIMM_155", + "SODIMM_185", + "SODIMM_187", + "USB_RECOV_CTRL#", /* 10 */ + "ENET1_INT#", + "TPM_INT#", + "TPM_CS#", + "", + ""; + + /* + * Switch USB to default position: + * - SoC USB2 -> Verdin USB_1 + * - SoC USB1 -> Verdin USB_2 + * Reset configuration: + * - SoC USB1 -> Verdin USB_1 (USB recovery) + * - SoC USB2 not connected + */ + usb_recov_ctrl: usb-recov-ctrl-hog { + gpio-hog; + gpios = <10 GPIO_ACTIVE_HIGH>; + line-name = "USB_RECOV_CTRL#"; + output-high; + }; + }; + + rtc_i2c: rtc@32 { + compatible = "epson,rx8130"; + reg = <0x32>; + }; + + temperature-sensor@48 { + compatible = "ti,tmp1075"; + reg = <0x48>; + }; + + som_eeprom: eeprom@50 { + compatible = "st,24c02", "atmel,24c02"; + reg = <0x50>; + pagesize = <16>; + }; +}; + +/* Verdin I2C_2_DSI */ +&lpi2c3 { + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&pinctrl_lpi2c3>; + pinctrl-1 = <&pinctrl_lpi2c3_gpio>; + clock-frequency = <100000>; + scl-gpios = <&gpio2 29 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio2 28 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + single-master; +}; + +/* Verdin I2C_1 */ +&lpi2c4 { + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&pinctrl_lpi2c4>; + pinctrl-1 = <&pinctrl_lpi2c4_gpio>; + clock-frequency = <100000>; + scl-gpios = <&gpio2 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio2 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + single-master; +}; + +/* Verdin I2C_4_CSI */ +&lpi2c5 { + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&pinctrl_lpi2c5>; + pinctrl-1 = <&pinctrl_lpi2c5_gpio>; + clock-frequency = <100000>; + scl-gpios = <&gpio2 23 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio2 22 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + single-master; +}; + +/* Verdin SPI_1 */ +&lpspi6 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lpspi6>, <&pinctrl_spi1_cs>; + cs-gpios = <&gpio4 29 GPIO_ACTIVE_LOW>, + <&som_gpio_expander 13 GPIO_ACTIVE_LOW>; + + status = "okay"; + + som_tpm: tpm@1 { + compatible = "infineon,slb9670", "tcg,tpm_tis-spi"; + reg = <0x1>; + interrupt-parent = <&som_gpio_expander>; + interrupts = <12 IRQ_TYPE_EDGE_FALLING>; + /* + * Maximum TPM-supported speed is 18.5 MHz, limited to 12 MHz + * here as lpspi6's per-clock (twice the max speed) is 24 MHz + */ + spi-max-frequency = <12000000>; + }; +}; + +/* Verdin UART_3, used as the Linux console */ +&lpuart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; +}; + +/* Verdin UART_4 */ +&lpuart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2>; +}; + +/* Verdin UART_1 */ +&lpuart7 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart7>; + uart-has-rtscts; +}; + +/* Verdin UART_2 */ +&lpuart8 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart8>; + uart-has-rtscts; +}; + +&mu7 { + status = "okay"; +}; + +&netc_blk_ctrl { + status = "okay"; +}; + +&netc_bus0 { + msi-map = <0x0 &its 0x60 0x1>, //ENETC0 PF + <0x10 &its 0x61 0x1>, //ENETC0 VF0 + <0x20 &its 0x62 0x1>, //ENETC0 VF1 + <0x40 &its 0x63 0x1>, //ENETC1 PF + <0x50 &its 0x65 0x1>, //ENETC1 VF0 + <0x60 &its 0x66 0x1>, //ENETC1 VF1 + <0x80 &its 0x64 0x1>, //ENETC2 PF + <0xc0 &its 0x67 0x1>; //NETC Timer + iommu-map = <0x0 &smmu 0x20 0x1>, + <0x10 &smmu 0x21 0x1>, + <0x20 &smmu 0x22 0x1>, + <0x40 &smmu 0x23 0x1>, + <0x50 &smmu 0x25 0x1>, + <0x60 &smmu 0x26 0x1>, + <0x80 &smmu 0x24 0x1>, + <0xc0 &smmu 0x27 0x1>; +}; + +/* Verdin ETH_2_RGMII_MDIO, shared between all ethernet ports */ +&netc_emdio { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_emdio>; + + status = "okay"; + + ethphy1: ethernet-phy@0 { + reg = <0>; + interrupt-parent = <&som_gpio_expander>; + interrupts = <11 IRQ_TYPE_EDGE_FALLING>; + ti,rx-internal-delay = ; + ti,tx-internal-delay = ; + }; +}; + +&netc_timer { + status = "okay"; +}; + +&netcmix_blk_ctrl { + status = "okay"; +}; + +/* Verdin PCIE_1 */ +&pcie0 { + /* PCIE_1_RESET# (SODIMM 244) */ + reset-gpios = <&som_gpio_expander 4 GPIO_ACTIVE_LOW>; +}; + +/* Verdin I2S_1 */ +&sai3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sai3>; + assigned-clocks = <&scmi_clk IMX95_CLK_AUDIOPLL1_VCO>, + <&scmi_clk IMX95_CLK_AUDIOPLL2_VCO>, + <&scmi_clk IMX95_CLK_AUDIOPLL1>, + <&scmi_clk IMX95_CLK_AUDIOPLL2>, + <&scmi_clk IMX95_CLK_SAI3>; + assigned-clock-parents = <0>, <0>, <0>, <0>, + <&scmi_clk IMX95_CLK_AUDIOPLL1>; + assigned-clock-rates = <3932160000>, + <3612672000>, <393216000>, + <361267200>, <12288000>; + #sound-dai-cells = <0>; + fsl,sai-mclk-direction-output; +}; + +&scmi_bbm { + linux,code = ; +}; + +&thermal_zones { + /* PF09 Main PMIC */ + pf09-thermal { + polling-delay = <2000>; + polling-delay-passive = <250>; + thermal-sensors = <&scmi_sensor 2>; + + trips { + trip0 { + hysteresis = <2000>; + temperature = <155000>; + type = "critical"; + }; + }; + }; + + /* PF53 VDD_ARM PMIC */ + pf53-arm-thermal { + polling-delay = <2000>; + polling-delay-passive = <250>; + thermal-sensors = <&scmi_sensor 4>; + + trips { + trip0 { + hysteresis = <2000>; + temperature = <155000>; + type = "critical"; + }; + }; + }; + + /* PF53 VDD_SOC PMIC */ + pf53-soc-thermal { + polling-delay = <2000>; + polling-delay-passive = <250>; + thermal-sensors = <&scmi_sensor 3>; + + trips { + trip0 { + hysteresis = <2000>; + temperature = <155000>; + type = "critical"; + }; + }; + }; +}; + +/* Verdin PWM_1 */ +&tpm4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tpm4>; +}; + +/* Verdin PWM_2 */ +&tpm5 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tpm5>; +}; + +/* Verdin PWM_3_DSI */ +&tpm6 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tpm6>; +}; + +/* Verdin USB_1 */ +&usb2 { + dr_mode = "otg"; + adp-disable; + hnp-disable; + srp-disable; + usb-role-switch; + vbus-supply = <®_usb1_vbus>; + + port { + usb1_id: endpoint { + remote-endpoint = <&usb_dr_connector>; + }; + }; +}; + +/* Verdin USB_2 */ +&usb3 { + fsl,disable-port-power-control; +}; + +&usb3_dwc3 { + dr_mode = "host"; +}; + +&usb3_phy { + vbus-supply = <®_usb2_vbus>; +}; + +/* On-module eMMC */ +&usdhc1 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + bus-width = <8>; + non-removable; + no-sdio; + no-sd; + + status = "okay"; +}; + +/* Verdin SD_1 */ +&usdhc2 { + pinctrl-names = "default", "state_100mhz", "state_200mhz", "sleep"; + pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_cd>; + pinctrl-1 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_cd>; + pinctrl-2 = <&pinctrl_usdhc2_200mhz>,<&pinctrl_usdhc2_cd>; + pinctrl-3 = <&pinctrl_usdhc2_sleep>, <&pinctrl_usdhc2_cd>; + cd-gpios = <&gpio3 0 GPIO_ACTIVE_LOW>; + vmmc-supply = <®_usdhc2_vmmc>; + vqmmc-supply = <®_usdhc2_vqmmc>; +}; + +&wdog3 { + fsl,ext-reset-output; + + status = "okay"; +}; + +&scmi_iomuxc { + /* On-module Bluetooth on WB SKUs, module-specific UART otherwise */ + pinctrl_bt_uart: btuartgrp { + fsl,pins = , /* WiFi_UART_SoC_TXD */ + , /* WiFi_UART_SoC_RXD */ + , /* WiFi_UART_SoC_CTS */ + ; /* WiFi_UART_SoC_RTS */ + }; + + /* Verdin CSI_1_MCLK */ + pinctrl_csi1_mclk: csi1mclkgrp { + fsl,pins = ; /* SODIMM 91 */ + }; + + /* Verdin CTRL_SLEEP_MOCI# */ + pinctrl_ctrl_sleep_moci: ctrlsleepmocigrp { + fsl,pins = ; /* SODIMM 256 */ + }; + + /* Verdin CTRL_WAKE1_MICO# */ + pinctrl_ctrl_wake1_mico: ctrlwake1micogrp { + fsl,pins = ; /* SODIMM 252 */ + }; + + /* Verdin ETH_2_RGMII_MDIO, shared between all ethernet ports */ + pinctrl_emdio: emdiogrp { + fsl,pins = , /* ENET2_MDC, SODIMM 193 */ + ; /* ENET2_MDIO, SODIMM 191 */ + }; + + /* Verdin ETH_1 (On-module PHY) */ + pinctrl_enetc0: enetc0grp { + fsl,pins = , /* ENET1_TX_CTL */ + , /* ENET1_TXC */ + , /* ENET1_TDO */ + , /* ENET1_TD1 */ + , /* ENET1_TD2 */ + , /* ENET1_TD3 */ + , /* ENET1_RX_CTL */ + , /* ENET1_RXC */ + , /* ENET1_RD0 */ + , /* ENET1_RD1 */ + , /* ENET1_RD2 */ + ; /* ENET1_RD3 */ + }; + + /* Verdin ETH_2_RGMII */ + pinctrl_enetc1: enetc1grp { + fsl,pins = , /* ENET2_TX_CTL */ + , /* ENET2_TXC */ + , /* ENET2_TD0 */ + , /* ENET2_TD1 */ + , /* ENET2_TD2 */ + , /* ENET2_TD3 */ + , /* ENET2_RX_CTL */ + , /* ENET2_RXC */ + , /* ENET2_RD0 */ + , /* ENET2_RD1 */ + , /* ENET2_RD2 */ + ; /* ENET2_RD3 */ + }; + + /* Verdin ETH_2_RGMII_INT# */ + pinctrl_eth2_rgmii_int: eth2rgmiiintgrp { + fsl,pins = ; /* SODIMM 189 */ + }; + + /* Verdin CAN_1 */ + pinctrl_flexcan1: flexcan1grp { + fsl,pins = , /* SODIMM 20 */ + ; /* SODIMM 22 */ + }; + + /* Verdin CAN_2 */ + pinctrl_flexcan2: flexcan2grp { + fsl,pins = , /* SODIMM 24 */ + ; /* SODIMM 26 */ + }; + + /* Verdin QSPI_1 */ + pinctrl_flexspi1: flexspi1grp { + fsl,pins = , /* SODIMM 54 */ + , /* SODIMM 64 */ + , /* SODIMM 52 */ + , /* SODIMM 56 */ + , /* SODIMM 58 */ + , /* SODIMM 60 */ + , /* SODIMM 62 */ + ; /* SODIMM 66 */ + }; + + /* Verdin GPIO_1 */ + pinctrl_gpio1: gpio1grp { + fsl,pins = ; /* SODIMM 206 */ + }; + + /* Verdin GPIO_2 */ + pinctrl_gpio2: gpio2grp { + fsl,pins = ; /* SODIMM 208 */ + }; + + /* Verdin GPIO_3 */ + pinctrl_gpio3: gpio3grp { + fsl,pins = ; /* SODIMM 210 */ + }; + + /* Verdin GPIO_4 */ + pinctrl_gpio4: gpio4grp { + fsl,pins = ; /* SODIMM 212 */ + }; + + /* Verdin GPIO_5_CSI */ + pinctrl_gpio5: gpio5grp { + fsl,pins = ; /* SODIMM 216 */ + }; + + /* Verdin GPIO_6_CSI */ + pinctrl_gpio6: gpio6grp { + fsl,pins = ; /* SODIMM 218 */ + }; + + /* Verdin I2S_2_BCLK as GPIO (conflict with Verdin I2S_2) */ + pinctrl_i2s_2_bclk_gpio: i2s2bclkgpiogrp { + fsl,pins = ; /* SODIMM 42 */ + }; + + /* Verdin I2S_2_D_IN as GPIO (conflict with Verdin I2S_2) */ + pinctrl_i2s_2_d_in_gpio: i2s2dingpiogrp { + fsl,pins = ; /* SODIMM 48 */ + }; + + /* Verdin I2S_2_D_OUT as GPIO (conflict with Verdin I2S_2) */ + pinctrl_i2s_2_d_out_gpio: i2s2doutgpiogrp { + fsl,pins = ; /* SODIMM 46 */ + }; + + /* Verdin I2S_2_SYNC as GPIO (conflict with Verdin I2S_2) */ + pinctrl_i2s_2_sync_gpio: i2s2syncgpiogrp { + fsl,pins = ; /* SODIMM 44 */ + }; + + /* Verdin I2C_3_HDMI */ + pinctrl_i3c2: i3c2cgrp { + fsl,pins = , /* SODIMM 59 */ + ; /* SODIMM 57 */ + }; + + pinctrl_io_exp_int: ioexpintgrp { + fsl,pins = ; /* IO_EXP_INT */ + }; + + /* CTRL_I2C (On-module I2C) */ + pinctrl_lpi2c2_gpio: lpi2c2gpiogrp { + fsl,pins = , /* CTRL_I2C_SCL */ + ; /* CTRL_I2C_SDA */ + }; + + pinctrl_lpi2c2: lpi2c2grp { + fsl,pins = , /* CTRL_I2C_SCL */ + ; /* CTRL_I2C_SDA */ + }; + + /* Verdin I2C_2_DSI */ + pinctrl_lpi2c3_gpio: lpi2c3gpiogrp { + fsl,pins = , /* SODIMM 53 */ + ; /* SODIMM 55 */ + }; + + pinctrl_lpi2c3: lpi2c3grp { + fsl,pins = , /* SODIMM 53 */ + ; /* SODIMM 55 */ + }; + + /* Verdin I2C_1 */ + pinctrl_lpi2c4_gpio: lpi2c4gpiogrp { + fsl,pins = , /* SODIMM 14 */ + ; /* SODIMM 12 */ + }; + + pinctrl_lpi2c4: lpi2c4grp { + fsl,pins = , /* SODIMM 14 */ + ; /* SODIMM 12 */ + }; + + /* Verdin I2C_4_CSI */ + pinctrl_lpi2c5_gpio: lpi2c5gpiogrp { + fsl,pins = , /* SODIMM 93 */ + ; /* SODIMM 95 */ + }; + + pinctrl_lpi2c5: lpi2c5grp { + fsl,pins = , /* SODIMM 93 */ + ; /* SODIMM 95 */ + }; + + /* Verdin SPI_1 */ + pinctrl_lpspi6: lpspi6grp { + fsl,pins = , /* SODIMM 198 */ + , /* SODIMM 200 */ + ; /* SODIMM 196 */ + }; + + /* Verdin QSPI_1_CLK as GPIO (conflict with Verdin QSPI_1 interface) */ + pinctrl_qspi1_clk_gpio: qspi1clkgpiogrp { + fsl,pins = ; /* SODIMM 52 */ + }; + + /* Verdin QSPI_1_CS2# as GPIO (conflict with Verdin QSPI_1 interface) */ + pinctrl_qspi1_cs2_gpio: qspi1cs2gpiogrp { + fsl,pins = ; /* SODIMM 64 */ + }; + + /* Verdin QSPI_1_CS# as GPIO (conflict with Verdin QSPI_1 interface) */ + pinctrl_qspi1_cs_gpio: qspi1csgpiogrp { + fsl,pins = ; /* SODIMM 54 */ + }; + + /* Verdin QSPI_1_DQS as GPIO (conflict with Verdin QSPI_1 interface) */ + pinctrl_qspi1_dqs_gpio: qspi1dqsgpiogrp { + fsl,pins = ; /* SODIMM 66 */ + }; + + /* Verdin QSPI_1_IO0 as GPIO (conflict with Verdin QSPI_1 interface) */ + pinctrl_qspi1_io0_gpio: qspi1io0gpiogrp { + fsl,pins = ; /* SODIMM 56 */ + }; + + /* Verdin QSPI_1_IO1 as GPIO (conflict with Verdin QSPI_1 interface) */ + pinctrl_qspi1_io1_gpio: qspi1io1gpiogrp { + fsl,pins = ; /* SODIMM 58 */ + }; + + /* Verdin QSPI_1_IO2 as GPIO (conflict with Verdin QSPI_1 interface) */ + pinctrl_qspi1_io2_gpio: qspi1io2gpiogrp { + fsl,pins = ; /* SODIMM 60 */ + }; + + /* Verdin QSPI_1_IO3 as GPIO (conflict with Verdin QSPI_1 interface) */ + pinctrl_qspi1_io3_gpio: qspi1io3gpiogrp { + fsl,pins = ; /* SODIMM 62 */ + }; + + /* Verdin I2S_1 */ + pinctrl_sai3: sai3grp { + fsl,pins = , /* SODIMM 30 */ + , /* SODIMM 36 */ + , /* SODIMM 34 */ + ; /* SODIMM 32 */ + }; + + /* Verdin I2S_1_MCLK */ + pinctrl_sai3_mclk: sai3mclkgrp { + fsl,pins = ; /* SODIMM 38 */ + }; + + /* Verdin I2S_2 */ + pinctrl_sai5: sai5grp { + fsl,pins = , /* SODIMM 46 */ + , /* SODIMM 44 */ + , /* SODIMM 42 */ + ; /* SODIMM 48 */ + }; + + /* Verdin SPI_1_CS */ + pinctrl_spi1_cs: spi1csgrp { + fsl,pins = ; /* SODIMM 202 */ + }; + + /* Verdin PWM_1 */ + pinctrl_tpm4: tpm4grp { + fsl,pins = ; /* SODIMM 15 */ + }; + + /* Verdin PWM_2 */ + pinctrl_tpm5: tpm5grp { + fsl,pins = ; /* SODIMM 16 */ + }; + + /* Verdin PWM_3_DSI as GPIO */ + pinctrl_tpm6_gpio: tpm6gpiogrp { + fsl,pins = ; /* SODIMM 19 */ + }; + + /* Verdin PWM_3_DSI */ + pinctrl_tpm6: tpm6grp { + fsl,pins = ; /* SODIMM 19 */ + }; + + /* Verdin UART_3, used as the Linux Console */ + pinctrl_uart1: uart1grp { + fsl,pins = , /* SODIMM 147 */ + ; /* SODIMM 149 */ + }; + + /* Verdin UART_4 */ + pinctrl_uart2: uart2grp { + fsl,pins = , /* SODIMM 151 */ + ; /* SODIMM 153 */ + }; + + /* Verdin UART_1 */ + pinctrl_uart7: uart7grp { + fsl,pins = , /* SODIMM 131 */ + , /* SODIMM 129 */ + , /* SODIMM 135 */ + ; /* SODIMM 133 */ + }; + + /* Verdin UART_2 */ + pinctrl_uart8: uart8grp { + fsl,pins = , /* SODIMM 139 */ + , /* SODIMM 137 */ + , /* SODIMM 143 */ + ; /* SODIMM 141 */ + }; + + /* On-module eMMC */ + pinctrl_usdhc1: usdhc1grp { + fsl,pins = , /* SD1_CLK */ + , /* SD1_CMD */ + , /* SD1_DATA0 */ + , /* SD1_DATA1 */ + , /* SD1_DATA2 */ + , /* SD1_DATA3 */ + , /* SD1_DATA4 */ + , /* SD1_DATA5 */ + , /* SD1_DATA6 */ + , /* SD1_DATA7 */ + ; /* SD1_STROBE */ + }; + + pinctrl_usdhc1_200mhz: usdhc1-200mhzgrp { + fsl,pins = , /* SD1_CLK */ + , /* SD1_CMD */ + , /* SD1_DATA0 */ + , /* SD1_DATA1 */ + , /* SD1_DATA2 */ + , /* SD1_DATA3 */ + , /* SD1_DATA4 */ + , /* SD1_DATA5 */ + , /* SD1_DATA6 */ + , /* SD1_DATA7 */ + ; /* SD1_STROBE */ + }; + + /* Verdin SD_1 */ + pinctrl_usdhc2: usdhc2grp { + fsl,pins = , /* SODIMM 78 */ + , /* SODIMM 74 */ + , /* SODIMM 80 */ + , /* SODIMM 82 */ + , /* SODIMM 70 */ + ; /* SODIMM 72 */ + }; + + pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp { + fsl,pins = , /* SODIMM 78 */ + , /* SODIMM 74 */ + , /* SODIMM 80 */ + , /* SODIMM 82 */ + , /* SODIMM 70 */ + ; /* SODIMM 72 */ + }; + + pinctrl_usdhc2_sleep: usdhc2-sleepgrp { + fsl,pins = , /* SODIMM 78 */ + , /* SODIMM 74 */ + , /* SODIMM 80 */ + , /* SODIMM 82 */ + , /* SODIMM 70 */ + ; /* SODIMM 72 */ + }; + + /* Verdin SD_1_CD# */ + pinctrl_usdhc2_cd: usdhc2-cdgrp { + fsl,pins = ; /* SODIMM 84 */ + }; + + /* Verdin SD_1_PWR_EN */ + pinctrl_usdhc2_pwr_en: usdhc2-pwrengrp { + fsl,pins = ; /* SODIMM 76 */ + }; + + pinctrl_usdhc2_vsel: usdhc2-vselgrp { + fsl,pins = ; /* PMIC_SD2_VSEL */ + }; + + /* On-module Wi-Fi on WB SKUs, module-specific SDIO otherwise */ + pinctrl_usdhc3: usdhc3grp { + fsl,pins = , /* SD3_CLK */ + , /* SD3_CMD */ + , /* SD3_DATA0 */ + , /* SD3_DATA1 */ + , /* SD3_DATA2 */ + ; /* SD3_DATA3 */ + }; + + pinctrl_usdhc3_200mhz: usdhc3-200mhzgrp { + fsl,pins = , /* SD3_CLK */ + , /* SD3_CMD */ + , /* SD3_DATA1 */ + , /* SD3_DATA2 */ + , /* SD3_DATA3 */ + ; /* SD3_DATA4 */ + }; + + pinctrl_wifi_pwr_en: wifipwrengrp { + fsl,pins = ; /* PMIC_EN_WIFI */ + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi index 55e2da094c88..71394871d8dd 100644 --- a/arch/arm64/boot/dts/freescale/imx95.dtsi +++ b/arch/arm64/boot/dts/freescale/imx95.dtsi @@ -391,9 +391,60 @@ }; }; + funnel0: funnel { + /* + * non-configurable funnel don't show up on the AMBA + * bus. As such no need to add "arm,primecell". + */ + compatible = "arm,coresight-static-funnel"; + status = "disabled"; + + in-ports { + port { + ca_funnel_in_port0: endpoint { + remote-endpoint = <&etm0_out_port>; + }; + }; + }; + + out-ports { + port { + ca_funnel_out_port0: endpoint { + remote-endpoint = <&hugo_funnel_in_port0>; + }; + }; + }; + }; + + funnel1: funnel-sys { + compatible = "arm,coresight-static-funnel"; + status = "disabled"; + + in-ports { + port { + hugo_funnel_in_port0: endpoint { + remote-endpoint = <&ca_funnel_out_port0>; + }; + }; + }; + + out-ports { + port { + hugo_funnel_out_port0: endpoint { + remote-endpoint = <&etf_in_port>; + }; + }; + }; + }; + + mqs1: mqs-1 { + compatible = "fsl,imx95-aonmix-mqs"; + status = "disabled"; + }; + pmu { compatible = "arm,cortex-a55-pmu"; - interrupts = ; + interrupts = ; }; thermal_zones: thermal-zones { @@ -470,10 +521,10 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; clock-frequency = <24000000>; arm,no-tick-in-suspend; interrupt-parent = <&gic>; @@ -532,52 +583,6 @@ }; }; - funnel0: funnel { - /* - * non-configurable funnel don't show up on the AMBA - * bus. As such no need to add "arm,primecell". - */ - compatible = "arm,coresight-static-funnel"; - status = "disabled"; - - in-ports { - port { - ca_funnel_in_port0: endpoint { - remote-endpoint = <&etm0_out_port>; - }; - }; - }; - - out-ports { - port { - ca_funnel_out_port0: endpoint { - remote-endpoint = <&hugo_funnel_in_port0>; - }; - }; - }; - }; - - funnel1: funnel-sys { - compatible = "arm,coresight-static-funnel"; - status = "disabled"; - - in-ports { - port { - hugo_funnel_in_port0: endpoint { - remote-endpoint = <&ca_funnel_out_port0>; - }; - }; - }; - - out-ports { - port { - hugo_funnel_out_port0: endpoint { - remote-endpoint = <&etf_in_port>; - }; - }; - }; - }; - etf: etf@41030000 { compatible = "arm,coresight-tmc", "arm,primecell"; reg = <0x0 0x41030000 0x0 0x1000>; @@ -631,6 +636,8 @@ reg = <0x42000000 0x210000>; #dma-cells = <3>; dma-channels = <64>; + /* channels 0 and 1 reserved for V2X fast hash */ + dma-channel-mask = <0x3>; interrupts = , , , diff --git a/arch/arm64/boot/dts/freescale/imx952-evk.dts b/arch/arm64/boot/dts/freescale/imx952-evk.dts index b838323468d4..62d1c1c7c501 100644 --- a/arch/arm64/boot/dts/freescale/imx952-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx952-evk.dts @@ -43,10 +43,21 @@ spi6 = &lpspi7; }; + bt_sco_codec: audio-codec-bt-sco { + #sound-dai-cells = <1>; + compatible = "linux,bt-sco"; + }; + chosen { stdout-path = &lpuart1; }; + dmic: dmic { + compatible = "dmic-codec"; + #sound-dai-cells = <0>; + num-channels = <2>; + }; + memory@80000000 { device_type = "memory"; reg = <0x0 0x80000000 0 0x80000000>; @@ -110,6 +121,15 @@ regulator-max-microvolt = <1800000>; }; + reg_audio_pwr: regulator-audio-pwr { + compatible = "regulator-fixed"; + regulator-name = "audio-pwr"; + regulator-max-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + gpio = <&i2c4_pcal6408 1 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + reg_usdhc2_vmmc: regulator-usdhc2 { compatible = "regulator-fixed"; pinctrl-names = "default"; @@ -131,6 +151,88 @@ enable-active-high; }; + sound-bt-sco { + compatible = "simple-audio-card"; + simple-audio-card,bitclock-inversion; + simple-audio-card,bitclock-master = <&btcpu>; + simple-audio-card,format = "dsp_a"; + simple-audio-card,frame-master = <&btcpu>; + simple-audio-card,name = "bt-sco-audio"; + + simple-audio-card,codec { + sound-dai = <&bt_sco_codec 1>; + }; + + btcpu: simple-audio-card,cpu { + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <16>; + sound-dai = <&sai1>; + }; + }; + + sound-micfil { + compatible = "fsl,imx-audio-card"; + model = "micfil-audio"; + + pri-dai-link { + format = "i2s"; + link-name = "micfil hifi"; + + codec { + sound-dai = <&dmic>; + }; + + cpu { + sound-dai = <&micfil>; + }; + }; + }; + + sound-wm8962 { + compatible = "fsl,imx-audio-wm8962"; + audio-asrc = <&asrc1>; + audio-codec = <&wm8962>; + audio-cpu = <&sai3>; + audio-routing = "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "Ext Spk", "SPKOUTL", + "Ext Spk", "SPKOUTR", + "AMIC", "MICBIAS", + "IN3R", "AMIC", + "IN1R", "AMIC"; + hp-det-gpio = <&gpio2 11 GPIO_ACTIVE_HIGH>; + model = "wm8962-audio"; + pinctrl-0 = <&pinctrl_hp>; + pinctrl-names = "default"; + }; +}; + +&asrc1 { + assigned-clocks = <&scmi_clk IMX952_CLK_AUDIOPLL1_VCO>, + <&scmi_clk IMX952_CLK_AUDIOPLL2_VCO>, + <&scmi_clk IMX952_CLK_AUDIOPLL1>, + <&scmi_clk IMX952_CLK_AUDIOPLL2>, + <&scmi_clk IMX952_CLK_ASRC1>; + assigned-clock-parents = <0>, <0>, <0>, <0>, + <&scmi_clk IMX952_CLK_AUDIOPLL1>; + assigned-clock-rates = <3932160000>, <3612672000>, + <393216000>, <361267200>, <49152000>; + fsl,asrc-rate = <48000>; + status = "okay"; +}; + +&asrc2 { + assigned-clocks = <&scmi_clk IMX952_CLK_AUDIOPLL1_VCO>, + <&scmi_clk IMX952_CLK_AUDIOPLL2_VCO>, + <&scmi_clk IMX952_CLK_AUDIOPLL1>, + <&scmi_clk IMX952_CLK_AUDIOPLL2>, + <&scmi_clk IMX952_CLK_ASRC2>; + assigned-clock-parents = <0>, <0>, <0>, <0>, + <&scmi_clk IMX952_CLK_AUDIOPLL1>; + assigned-clock-rates = <3932160000>, <3612672000>, + <393216000>, <361267200>, <49152000>; + fsl,asrc-rate = <48000>; + status = "okay"; }; /* pin conflict with PDM */ @@ -185,6 +287,27 @@ pinctrl-0 = <&pinctrl_lpi2c4>; status = "okay"; + wm8962: audio-codec@1a { + compatible = "wlf,wm8962"; + reg = <0x1a>; + clocks = <&scmi_clk IMX952_CLK_SAI3>; + AVDD-supply = <®_audio_pwr>; + CPVDD-supply = <®_audio_pwr>; + DBVDD-supply = <®_audio_pwr>; + DCVDD-supply = <®_audio_pwr>; + gpio-cfg = < 0x0000 /* 0:Default */ + 0x0000 /* 1:Default */ + 0x0000 /* 2:FN_DMICCLK */ + 0x0000 /* 3:Default */ + 0x0000 /* 4:FN_DMICCDAT */ + 0x0000 /* 5:Default */ + >; + MICVDD-supply = <®_audio_pwr>; + PLLVDD-supply = <®_audio_pwr>; + SPKVDD1-supply = <®_audio_pwr>; + SPKVDD2-supply = <®_audio_pwr>; + }; + i2c4_pcal6408: gpio@21 { compatible = "nxp,pcal6408"; reg = <0x21>; @@ -312,6 +435,57 @@ status = "okay"; }; +&micfil { + assigned-clocks = <&scmi_clk IMX952_CLK_AUDIOPLL1_VCO>, + <&scmi_clk IMX952_CLK_AUDIOPLL2_VCO>, + <&scmi_clk IMX952_CLK_AUDIOPLL1>, + <&scmi_clk IMX952_CLK_AUDIOPLL2>, + <&scmi_clk IMX952_CLK_PDM>; + assigned-clock-parents = <0>, <0>, <0>, <0>, + <&scmi_clk IMX952_CLK_AUDIOPLL1>; + assigned-clock-rates = <3932160000>, <3612672000>, + <393216000>, <361267200>, <49152000>; + pinctrl-0 = <&pinctrl_pdm>; + pinctrl-1 = <&pinctrl_pdm_sleep>; + pinctrl-names = "default", "sleep"; + status = "okay"; +}; + +&sai1 { + assigned-clocks = <&scmi_clk IMX952_CLK_AUDIOPLL1_VCO>, + <&scmi_clk IMX952_CLK_AUDIOPLL2_VCO>, + <&scmi_clk IMX952_CLK_AUDIOPLL1>, + <&scmi_clk IMX952_CLK_AUDIOPLL2>, + <&scmi_clk IMX952_CLK_SAI1>; + assigned-clock-parents = <0>, <0>, <0>, <0>, + <&scmi_clk IMX952_CLK_AUDIOPLL1>; + assigned-clock-rates = <3932160000>, <3612672000>, + <393216000>, <361267200>, <12288000>; + pinctrl-0 = <&pinctrl_sai1>; + pinctrl-1 = <&pinctrl_sai1_sleep>; + pinctrl-names = "default", "sleep"; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + +&sai3 { + assigned-clocks = <&scmi_clk IMX952_CLK_AUDIOPLL1_VCO>, + <&scmi_clk IMX952_CLK_AUDIOPLL2_VCO>, + <&scmi_clk IMX952_CLK_AUDIOPLL1>, + <&scmi_clk IMX952_CLK_AUDIOPLL2>, + <&scmi_clk IMX952_CLK_SAI3>; + assigned-clock-parents = <0>, <0>, <0>, <0>, + <&scmi_clk IMX952_CLK_AUDIOPLL1>; + assigned-clock-rates = <3932160000>, <3612672000>, + <393216000>, <361267200>, <12288000>; + pinctrl-0 = <&pinctrl_sai3>; + pinctrl-1 = <&pinctrl_sai3_sleep>; + pinctrl-names = "default", "sleep"; + fsl,sai-amix-mode = "bypass"; + fsl,sai-mclk-direction-output; + status = "okay"; +}; + &scmi_misc { nxp,ctrl-ids = ; }; + pinctrl_hp: hpgrp { + fsl,pins = < + IMX952_PAD_GPIO_IO11__WAKEUPMIX_TOP_GPIO2_IO_11 0x31e + >; + }; + pinctrl_lpi2c2: lpi2c2grp { fsl,pins = < IMX952_PAD_I2C2_SCL__AONMIX_TOP_LPI2C2_SCL 0x40000b9e @@ -464,6 +644,20 @@ >; }; + pinctrl_pdm: pdmgrp { + fsl,pins = < + IMX952_PAD_PDM_CLK__AONMIX_TOP_PDM_CLK 0x31e + IMX952_PAD_PDM_BIT_STREAM0__AONMIX_TOP_PDM_BIT_STREAM_0 0x31e + >; + }; + + pinctrl_pdm_sleep: pdmsleepgrp { + fsl,pins = < + IMX952_PAD_PDM_CLK__AONMIX_TOP_GPIO1_IO_8 0x31e + IMX952_PAD_PDM_BIT_STREAM0__AONMIX_TOP_GPIO1_IO_9 0x31e + >; + }; + pinctrl_ptn5110: ptn5110grp { fsl,pins = < IMX952_PAD_GPIO_IO34__WAKEUPMIX_TOP_GPIO5_IO_14 0x31e @@ -476,6 +670,44 @@ >; }; + pinctrl_sai1: sai1grp { + fsl,pins = < + IMX952_PAD_SAI1_RXD0__AONMIX_TOP_SAI1_RX_DATA_0 0x31e + IMX952_PAD_SAI1_TXC__AONMIX_TOP_SAI1_TX_BCLK 0x31e + IMX952_PAD_SAI1_TXFS__AONMIX_TOP_SAI1_TX_SYNC 0x31e + IMX952_PAD_SAI1_TXD0__AONMIX_TOP_SAI1_TX_DATA_0 0x31e + >; + }; + + pinctrl_sai1_sleep: sai1sleepgrp { + fsl,pins = < + IMX952_PAD_SAI1_RXD0__AONMIX_TOP_GPIO1_IO_14 0x51e + IMX952_PAD_SAI1_TXC__AONMIX_TOP_GPIO1_IO_12 0x51e + IMX952_PAD_SAI1_TXFS__AONMIX_TOP_GPIO1_IO_11 0x51e + IMX952_PAD_SAI1_TXD0__AONMIX_TOP_GPIO1_IO_13 0x51e + >; + }; + + pinctrl_sai3: sai3grp { + fsl,pins = < + IMX952_PAD_GPIO_IO17__WAKEUPMIX_TOP_SAI3_MCLK 0x31e + IMX952_PAD_GPIO_IO16__WAKEUPMIX_TOP_AUDMIX_TDM_OUT_TXBCLK 0x31e + IMX952_PAD_GPIO_IO26__WAKEUPMIX_TOP_AUDMIX_TDM_OUT_TXSYNC 0x31e + IMX952_PAD_GPIO_IO20__WAKEUPMIX_TOP_SAI3_RX_DATA_0 0x31e + IMX952_PAD_GPIO_IO21__WAKEUPMIX_TOP_AUDMIX_TDM_OUT_TXDATA 0x31e + >; + }; + + pinctrl_sai3_sleep: sai3sleepgrp { + fsl,pins = < + IMX952_PAD_GPIO_IO17__WAKEUPMIX_TOP_GPIO2_IO_17 0x31e + IMX952_PAD_GPIO_IO16__WAKEUPMIX_TOP_GPIO2_IO_16 0x31e + IMX952_PAD_GPIO_IO26__WAKEUPMIX_TOP_GPIO2_IO_26 0x31e + IMX952_PAD_GPIO_IO20__WAKEUPMIX_TOP_GPIO2_IO_20 0x31e + IMX952_PAD_GPIO_IO21__WAKEUPMIX_TOP_GPIO2_IO_21 0x31e + >; + }; + pinctrl_tpm3: tpm3grp { fsl,pins = < IMX952_PAD_GPIO_IO12__WAKEUPMIX_TOP_TPM3_CH2 0x51e @@ -593,4 +825,45 @@ IMX952_PAD_SD2_CD_B__WAKEUPMIX_TOP_GPIO3_IO_0 0x31e >; }; + + pinctrl_xspi1: xspi1grp { + fsl,pins = < + IMX952_PAD_XSPI1_DATA0__WAKEUPMIX_TOP_XSPI1_A_DATA_0 0x39e + IMX952_PAD_XSPI1_DATA1__WAKEUPMIX_TOP_XSPI1_A_DATA_1 0x39e + IMX952_PAD_XSPI1_DATA2__WAKEUPMIX_TOP_XSPI1_A_DATA_2 0x39e + IMX952_PAD_XSPI1_DATA3__WAKEUPMIX_TOP_XSPI1_A_DATA_3 0x39e + IMX952_PAD_XSPI1_DATA4__WAKEUPMIX_TOP_XSPI1_A_DATA_4 0x39e + IMX952_PAD_XSPI1_DATA5__WAKEUPMIX_TOP_XSPI1_A_DATA_5 0x39e + IMX952_PAD_XSPI1_DATA6__WAKEUPMIX_TOP_XSPI1_A_DATA_6 0x39e + IMX952_PAD_XSPI1_DATA7__WAKEUPMIX_TOP_XSPI1_A_DATA_7 0x39e + IMX952_PAD_XSPI1_DQS__WAKEUPMIX_TOP_XSPI1_A_DQS 0x39e + IMX952_PAD_XSPI1_SCLK__WAKEUPMIX_TOP_XSPI1_A_SCLK 0x39e + IMX952_PAD_XSPI1_SS0_B__WAKEUPMIX_TOP_XSPI1_A_SS0_B 0x39e + >; + }; + + pinctrl_xspi1_reset: xspi1-reset-grp { + fsl,pins = < + IMX952_PAD_XSPI1_SS1_B__WAKEUPMIX_TOP_GPIO5_IO_11 0x39e + >; + }; +}; + +&xspi1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_xspi1>; + status = "okay"; + + mt35xu01gbba: flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_xspi1_reset>; + reset-gpios = <&gpio5 11 GPIO_ACTIVE_LOW>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <200000000>; + spi-tx-bus-width = <8>; + spi-rx-bus-width = <8>; + }; }; diff --git a/arch/arm64/boot/dts/freescale/imx952.dtsi b/arch/arm64/boot/dts/freescale/imx952.dtsi index 91fe4916ac04..b30707837f35 100644 --- a/arch/arm64/boot/dts/freescale/imx952.dtsi +++ b/arch/arm64/boot/dts/freescale/imx952.dtsi @@ -285,7 +285,7 @@ pmu { compatible = "arm,cortex-a55-pmu"; - interrupts = ; + interrupts = ; }; psci { @@ -295,10 +295,10 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; clock-frequency = <24000000>; arm,no-tick-in-suspend; interrupt-parent = <&gic>; @@ -672,6 +672,25 @@ #mbox-cells = <2>; status = "disabled"; }; + + xspi1: spi@42400000 { + compatible = "nxp,imx952-xspi", "nxp,imx94-xspi"; + reg = <0x42400000 0x50000>, <0x28000000 0x8000000>; + reg-names = "base", "mmap"; + interrupts = , + , + , + , + ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&scmi_clk IMX952_CLK_XSPI1>; + clock-names = "per"; + assigned-clocks = <&scmi_clk IMX952_CLK_XSPI1>; + assigned-clock-parents = <&scmi_clk IMX952_CLK_SYSPLL1_PFD1>; + assigned-clock-rates = <200000000>; + status = "disabled"; + }; }; aips3: bus@42800000 { @@ -804,6 +823,160 @@ }; }; + aips5: bus@43000000 { + compatible = "fsl,aips-bus", "simple-bus"; + reg = <0 0x43000000 0 0x800000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x43000000 0x0 0x43000000 0x800000>; + + asrc1: asrc@43000000 { + compatible = "fsl,imx952-asrc"; + reg = <0x43000000 0x10000>; + interrupts = ; + clocks = <&scmi_clk IMX952_CLK_BUSWAKEUP>, + <&scmi_clk IMX952_CLK_BUSWAKEUP>, + <&scmi_clk IMX952_CLK_ASRC1>, + <&scmi_clk IMX952_CLK_ASRC2>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>; + clock-names = "mem", "ipg", + "asrck_0", "asrck_1", "asrck_2", "asrck_3", + "asrck_4", "asrck_5", "asrck_6", "asrck_7", + "asrck_8", "asrck_9", "asrck_a", "asrck_b", + "asrck_c", "asrck_d", "asrck_e", "asrck_f", + "spba"; + dmas = <&edma2 97 0 0>, <&edma2 98 0 0>, <&edma2 99 0 0>, + <&edma2 100 0 FSL_EDMA_RX>, <&edma2 101 0 FSL_EDMA_RX>, + <&edma2 102 0 FSL_EDMA_RX>; + /* tx* is output channel of asrc, it is rx channel for eDMA */ + dma-names = "rxa", "rxb", "rxc", + "txa", "txb", "txc"; + #sound-dai-cells = <0>; + fsl,asrc-rate = <8000>; + fsl,asrc-width = <16>; + status = "disabled"; + }; + + asrc2: asrc@430f0000 { + compatible = "fsl,imx952-asrc"; + reg = <0x430f0000 0x10000>; + interrupts = ; + clocks = <&scmi_clk IMX952_CLK_BUSWAKEUP>, + <&scmi_clk IMX952_CLK_BUSWAKEUP>, + <&scmi_clk IMX952_CLK_ASRC1>, + <&scmi_clk IMX952_CLK_ASRC2>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>, + <&clk_dummy>; + clock-names = "mem", "ipg", + "asrck_0", "asrck_1", "asrck_2", "asrck_3", + "asrck_4", "asrck_5", "asrck_6", "asrck_7", + "asrck_8", "asrck_9", "asrck_a", "asrck_b", + "asrck_c", "asrck_d", "asrck_e", "asrck_f", + "spba"; + dmas = <&edma2 103 0 0>, <&edma2 104 0 0>, <&edma2 105 0 0>, + <&edma2 106 0 FSL_EDMA_RX>, <&edma2 107 0 FSL_EDMA_RX>, + <&edma2 108 0 FSL_EDMA_RX>; + /* tx* is output channel of asrc, it is rx channel for eDMA */ + dma-names = "rxa", "rxb", "rxc", + "txa", "txb", "txc"; + #sound-dai-cells = <0>; + fsl,asrc-rate = <8000>; + fsl,asrc-width = <16>; + status = "disabled"; + }; + + amix: amix@431f0000 { + compatible = "fsl,imx952-audmix"; + reg = <0x431f0000 0x10000>; + clocks = <&scmi_clk IMX952_CLK_AUDMIX1>; + clock-names = "ipg"; + status = "disabled"; + }; + + sai3: sai@433e0000 { + compatible = "fsl,imx952-sai", "fsl,imx95-sai"; + reg = <0x433e0000 0x10000>; + interrupts = ; + clocks = <&scmi_clk IMX952_CLK_BUSWAKEUP>, <&clk_dummy>, + <&scmi_clk IMX952_CLK_SAI3>, <&clk_dummy>, + <&clk_dummy>; + clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3"; + dmas = <&edma2 61 0 FSL_EDMA_RX>, <&edma2 60 0 0>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + sai4: sai@433f0000 { + compatible = "fsl,imx952-sai", "fsl,imx95-sai"; + reg = <0x433f0000 0x10000>; + interrupts = ; + clocks = <&scmi_clk IMX952_CLK_BUSWAKEUP>, <&clk_dummy>, + <&scmi_clk IMX952_CLK_SAI4>, <&clk_dummy>, + <&clk_dummy>; + clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3"; + dmas = <&edma2 68 0 FSL_EDMA_RX>, <&edma2 67 0 0>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + sai5: sai@43400000 { + compatible = "fsl,imx952-sai", "fsl,imx95-sai"; + reg = <0x43400000 0x10000>; + interrupts = ; + clocks = <&scmi_clk IMX952_CLK_BUSWAKEUP>, <&clk_dummy>, + <&scmi_clk IMX952_CLK_SAI5>, <&clk_dummy>, + <&clk_dummy>; + clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3"; + dmas = <&edma2 70 0 FSL_EDMA_RX>, <&edma2 69 0 0>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + sai6: sai@43410000 { + compatible = "fsl,imx952-sai", "fsl,imx95-sai"; + reg = <0x43410000 0x10000>; + interrupts = ; + clocks = <&scmi_clk IMX952_CLK_BUSWAKEUP>, <&clk_dummy>, + <&scmi_clk IMX952_CLK_SAI3>, <&clk_dummy>, + <&clk_dummy>; + clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3"; + dmas = <&edma2 96 0 FSL_EDMA_RX>, <&edma2 95 0 0>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + status = "disabled"; + }; + }; + gpio2: gpio@43810000 { compatible = "fsl,imx95-gpio", "fsl,imx8ulp-gpio"; reg = <0x0 0x43810000 0x0 0x1000>; @@ -1043,6 +1216,40 @@ status = "disabled"; }; + sai1: sai@443b0000 { + compatible = "fsl,imx952-sai", "fsl,imx95-sai"; + reg = <0x443b0000 0x10000>; + interrupts = ; + clocks = <&scmi_clk IMX952_CLK_BUSAON>, <&clk_dummy>, + <&scmi_clk IMX952_CLK_SAI1>, <&clk_dummy>, + <&clk_dummy>; + clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3"; + dmas = <&edma1 25 0 FSL_EDMA_RX>, <&edma1 24 0 0>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + micfil: micfil@44520000 { + compatible = "fsl,imx943-micfil"; + reg = <0x44520000 0x10000>; + interrupts = , + , + , + ; + clocks = <&scmi_clk IMX952_CLK_BUSAON>, + <&scmi_clk IMX952_CLK_PDM>, + <&scmi_clk IMX952_CLK_AUDIOPLL1>, + <&scmi_clk IMX952_CLK_AUDIOPLL2>, + <&clk_dummy>; + clock-names = "ipg_clk", "ipg_clk_app", + "pll8k", "pll11k", "clkext3"; + dmas = <&edma1 6 0 (FSL_EDMA_MULTI_FIFO | FSL_EDMA_RX)>; + dma-names = "rx"; + #sound-dai-cells = <0>; + status = "disabled"; + }; + adc1: adc@44530000 { compatible = "nxp,imx93-adc"; reg = <0x44530000 0x10000>; @@ -1262,5 +1469,20 @@ reg = <0x0 0x4c200200 0x0 0x200>, <0x0 0x4c010014 0x0 0x4>; }; + + sai2: sai@4c880000 { + compatible = "fsl,imx952-sai", "fsl,imx95-sai"; + reg = <0x0 0x4c880000 0x0 0x10000>; + interrupts = ; + clocks = <&scmi_clk IMX952_CLK_BUSNETCMIX>, <&clk_dummy>, + <&scmi_clk IMX952_CLK_SAI2>, <&clk_dummy>, + <&clk_dummy>; + clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3"; + dmas = <&edma2 59 0 FSL_EDMA_RX>, <&edma2 58 0 0>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + power-domains = <&scmi_devpd IMX952_PD_NETC>; + status = "disabled"; + }; }; }; diff --git a/arch/arm64/boot/dts/freescale/mba8mx.dtsi b/arch/arm64/boot/dts/freescale/mba8mx.dtsi index 10d5c211b1c9..c24ae953cbc2 100644 --- a/arch/arm64/boot/dts/freescale/mba8mx.dtsi +++ b/arch/arm64/boot/dts/freescale/mba8mx.dtsi @@ -209,7 +209,7 @@ interrupt-controller; #interrupt-cells = <2>; gpio-line-names = "", "", "", "", - "", "", "LVDS_BRIDGE_EN", "", + "", "", "LVDS_BRIDGE_EN", "LVDS_BRIDGE_IRQ", "", "", "", "", "", "", "", ""; @@ -298,6 +298,8 @@ reg = <0x2d>; enable-gpios = <&gpio_delays 0 130000 0>; vcc-supply = <®_sn65dsi83_1v8>; + interrupt-parent = <&expander0>; + interrupts = <7 IRQ_TYPE_EDGE_RISING>; status = "disabled"; ports { diff --git a/arch/arm64/boot/dts/freescale/s32n79-rdb.dts b/arch/arm64/boot/dts/freescale/s32n79-rdb.dts new file mode 100644 index 000000000000..1feccd61258e --- /dev/null +++ b/arch/arm64/boot/dts/freescale/s32n79-rdb.dts @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright 2026 NXP + * + * NXP S32N79 Reference Design Board (S32N79-RDB) + */ + +/dts-v1/; +#include "s32n79.dtsi" + +/ { + compatible = "nxp,s32n79-rdb", "nxp,s32n79"; + model = "NXP S32N79-RDB"; + + aliases { + serial0 = &uart0; + serial1 = &uart5; + serial2 = &uart6; + serial3 = &uart7; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + reserved-memory { + ranges; + #address-cells = <2>; + #size-cells = <2>; + + scmi_shbuf: memory@93000000 { + compatible = "arm,scmi-shmem"; + reg = <0x0 0x93000000 0x0 0x80>; + no-map; + }; + }; + + memory@80000000 { + reg = <0x00 0x80000000 0x00 0x80000000>, + <0x88 0x00000000 0x03 0x40000000>, + <0xc0 0x00000000 0x03 0x40000000>; + device_type = "memory"; + }; +}; + +&irqsteer_coss { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart5 { + status = "okay"; +}; + +&uart6 { + status = "okay"; +}; + +&uart7 { + status = "okay"; +}; + +&usdhc0 { + disable-wp; + no-sdio; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/s32n79.dtsi b/arch/arm64/boot/dts/freescale/s32n79.dtsi new file mode 100644 index 000000000000..94ab58783fdc --- /dev/null +++ b/arch/arm64/boot/dts/freescale/s32n79.dtsi @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * NXP S32N79 SoC + * + * Copyright 2026 NXP + */ + +#include + +/ { + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + cis-bus { + compatible = "simple-bus"; + ranges = <0x4f200000 0x0 0x4f200000 0xc00000>; + #address-cells = <1>; + #size-cells = <1>; + + gic: interrupt-controller@4f200000 { + compatible = "arm,gic-v3"; + reg = <0x4f200000 0x10000>, /* GIC Dist */ + <0x4f260000 0x100000>; + #interrupt-cells = <3>; + interrupt-controller; + interrupts = ; + #address-cells = <1>; + #size-cells = <1>; + /* GICR (RD_base + SGI_base) */ + ranges; + + its: msi-controller@4f240000 { + compatible = "arm,gic-v3-its"; + reg = <0x4f240000 0x20000>; + #msi-cells = <1>; + msi-controller; + }; + }; + + smmu: iommu@4fc00000 { + compatible = "arm,smmu-v3"; + reg = <0x4fc00000 0x200000>; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + interrupt-names = "eventq", "gerror", "priq", "cmdq-sync"; + #iommu-cells = <1>; + dma-coherent; + status = "disabled"; + }; + }; + + coss-bus { + compatible = "simple-bus"; + ranges = <0x4a000000 0x0 0x4a000000 0xff0000>, + <0x4e000000 0x0 0x4e000000 0x1000000>; + #address-cells = <1>; + #size-cells = <1>; + + uart0: serial@4a030000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x4a030000 0x1000>; + interrupt-parent = <&irqsteer_coss>; + interrupts = <264>; + clocks = <&clks 0x9a>, <&clks 0x9a>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + uart5: serial@4a060000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x4a060000 0x1000>; + interrupt-parent = <&irqsteer_coss>; + interrupts = <269>; + clocks = <&clks 0x9a>, <&clks 0x9a>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + uart6: serial@4aa30000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x4aa30000 0x1000>; + interrupt-parent = <&irqsteer_coss>; + interrupts = <270>; + clocks = <&clks 0x9a>, <&clks 0x9a>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + uart7: serial@4aa40000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x4aa40000 0x1000>; + interrupt-parent = <&irqsteer_coss>; + interrupts = <271>; + clocks = <&clks 0x9a>, <&clks 0x9a>; + clock-names = "uartclk", "apb_pclk"; + status = "disabled"; + }; + + irqsteer_coss: interrupt-controller@4ed00000 { + compatible = "nxp,s32n79-irqsteer"; + reg = <0x4ed00000 0x10000>; + #interrupt-cells = <1>; + interrupt-controller; + interrupt-parent = <&gic>; + interrupts = , + , + , + , + , + , + , + ; + clocks = <&clks 0x9a>; + clock-names = "ipg"; + fsl,channel = <0>; + fsl,num-irqs = <512>; + status = "disabled"; + }; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu-map { + cluster0 { + core0 { + cpu = <&cpu0>; + }; + + core1 { + cpu = <&cpu1>; + }; + }; + + cluster1 { + core0 { + cpu = <&cpu2>; + }; + + core1 { + cpu = <&cpu3>; + }; + }; + + cluster2 { + core0 { + cpu = <&cpu4>; + }; + + core1 { + cpu = <&cpu5>; + }; + }; + + cluster3 { + core0 { + cpu = <&cpu6>; + }; + + core1 { + cpu = <&cpu7>; + }; + }; + }; + + l2_0: l2-cache0 { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <512>; + cache-size = <524288>; + cache-unified; + next-level-cache = <&l3_0>; + }; + + l2_1: l2-cache1 { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <512>; + cache-size = <524288>; + cache-unified; + next-level-cache = <&l3_1>; + }; + + l2_2: l2-cache2 { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <512>; + cache-size = <524288>; + cache-unified; + next-level-cache = <&l3_2>; + }; + + l2_3: l2-cache3 { + compatible = "cache"; + cache-level = <2>; + cache-line-size = <64>; + cache-sets = <512>; + cache-size = <524288>; + cache-unified; + next-level-cache = <&l3_3>; + }; + + l3_0: l3-cache0 { + compatible = "cache"; + cache-level = <3>; + cache-line-size = <64>; + cache-sets = <1024>; + cache-size = <1048576>; + cache-unified; + }; + + l3_1: l3-cache1 { + compatible = "cache"; + cache-level = <3>; + cache-line-size = <64>; + cache-sets = <1024>; + cache-size = <1048576>; + cache-unified; + }; + + l3_2: l3-cache2 { + compatible = "cache"; + cache-level = <3>; + cache-line-size = <64>; + cache-sets = <1024>; + cache-size = <1048576>; + cache-unified; + }; + + l3_3: l3-cache3 { + compatible = "cache"; + cache-level = <3>; + cache-line-size = <64>; + cache-sets = <1024>; + cache-size = <1048576>; + cache-unified; + }; + + cpu0: cpu@0 { + compatible = "arm,cortex-a78ae"; + reg = <0x0>; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <&l2_0>; + }; + + cpu1: cpu@100 { + compatible = "arm,cortex-a78ae"; + reg = <0x100>; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <&l2_0>; + }; + + cpu2: cpu@10000 { + compatible = "arm,cortex-a78ae"; + reg = <0x10000>; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <&l2_1>; + }; + + cpu3: cpu@10100 { + compatible = "arm,cortex-a78ae"; + reg = <0x10100>; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <&l2_1>; + }; + + cpu4: cpu@20000 { + compatible = "arm,cortex-a78ae"; + reg = <0x20000>; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <&l2_2>; + }; + + cpu5: cpu@20100 { + compatible = "arm,cortex-a78ae"; + reg = <0x20100>; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <&l2_2>; + }; + + cpu6: cpu@30000 { + compatible = "arm,cortex-a78ae"; + reg = <0x30000>; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <&l2_3>; + }; + + cpu7: cpu@30100 { + compatible = "arm,cortex-a78ae"; + reg = <0x30100>; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <&l2_3>; + }; + }; + + firmware { + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + + scmi: scmi { + compatible = "arm,scmi-smc"; + #address-cells = <1>; + #size-cells = <0>; + shmem = <&scmi_shbuf>; + arm,smc-id = <0xc20000fe>; + status = "okay"; + + clks: protocol@14 { + reg = <0x14>; + #clock-cells = <1>; + }; + }; + }; + + fss-bus { + compatible = "simple-bus"; + ranges = <0x5b490000 0x0 0x5b490000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + usdhc0: mmc@5b490000 { + compatible = "nxp,s32n79-usdhc"; + reg = <0x5b490000 0x1000>; + interrupts = ; + clocks = <&clks 0x58>, <&clks 0x50>, <&clks 0x5f>; + clock-names = "ipg", "ahb", "per"; + bus-width = <8>; + status = "disabled"; + }; + }; + + pmu: pmu { + compatible = "arm,armv8-pmuv3"; + interrupts = ; + }; + + timer: timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + }; +}; diff --git a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi index 957a1b41f19b..374aa173bec6 100644 --- a/arch/arm64/boot/dts/hisilicon/hi3660.dtsi +++ b/arch/arm64/boot/dts/hisilicon/hi3660.dtsi @@ -1057,7 +1057,7 @@ }; /* SD */ - dwmmc1: dwmmc1@ff37f000 { + dwmmc1: mmc@ff37f000 { compatible = "hisilicon,hi3660-dw-mshc"; reg = <0x0 0xff37f000 0x0 0x1000>; #address-cells = <1>; @@ -1075,7 +1075,7 @@ }; /* SDIO */ - dwmmc2: dwmmc2@ff3ff000 { + dwmmc2: mmc@ff3ff000 { compatible = "hisilicon,hi3660-dw-mshc"; reg = <0x0 0xff3ff000 0x0 0x1000>; #address-cells = <0x1>; diff --git a/arch/arm64/boot/dts/hisilicon/hi3670.dtsi b/arch/arm64/boot/dts/hisilicon/hi3670.dtsi index 886b93c5893a..0db1849a2878 100644 --- a/arch/arm64/boot/dts/hisilicon/hi3670.dtsi +++ b/arch/arm64/boot/dts/hisilicon/hi3670.dtsi @@ -679,7 +679,7 @@ }; /* SD */ - dwmmc1: dwmmc1@ff37f000 { + dwmmc1: mmc@ff37f000 { compatible = "hisilicon,hi3670-dw-mshc", "hisilicon,hi3660-dw-mshc"; reg = <0x0 0xff37f000 0x0 0x1000>; @@ -698,7 +698,7 @@ }; /* SDIO */ - dwmmc2: dwmmc2@fc183000 { + dwmmc2: mmc@fc183000 { compatible = "hisilicon,hi3670-dw-mshc", "hisilicon,hi3660-dw-mshc"; reg = <0x0 0xfc183000 0x0 0x1000>; diff --git a/arch/arm64/boot/dts/hisilicon/hi6220.dtsi b/arch/arm64/boot/dts/hisilicon/hi6220.dtsi index f8b56d443850..61eaa7f8c1c9 100644 --- a/arch/arm64/boot/dts/hisilicon/hi6220.dtsi +++ b/arch/arm64/boot/dts/hisilicon/hi6220.dtsi @@ -799,7 +799,7 @@ #mbox-cells = <3>; }; - dwmmc_0: dwmmc0@f723d000 { + dwmmc_0: mmc@f723d000 { compatible = "hisilicon,hi6220-dw-mshc"; reg = <0x0 0xf723d000 0x0 0x1000>; interrupts = <0x0 0x48 0x4>; @@ -812,7 +812,7 @@ &emmc_cfg_func &emmc_rst_cfg_func>; }; - dwmmc_1: dwmmc1@f723e000 { + dwmmc_1: mmc@f723e000 { compatible = "hisilicon,hi6220-dw-mshc"; hisilicon,peripheral-syscon = <&ao_ctrl>; reg = <0x0 0xf723e000 0x0 0x1000>; @@ -828,7 +828,7 @@ pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>; }; - dwmmc_2: dwmmc2@f723f000 { + dwmmc_2: mmc@f723f000 { compatible = "hisilicon,hi6220-dw-mshc"; reg = <0x0 0xf723f000 0x0 0x1000>; interrupts = <0x0 0x4a 0x4>; diff --git a/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi b/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi index 352c96d144a8..02e62d954e94 100644 --- a/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi +++ b/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi @@ -152,10 +152,10 @@ timer { compatible = "arm,armv8-timer"; interrupt-parent = <&intc>; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; usbphy0: usbphy { diff --git a/arch/arm64/boot/dts/marvell/armada-3720-atlas-v5.dts b/arch/arm64/boot/dts/marvell/armada-3720-atlas-v5.dts index 070d10a705bb..a313d5687789 100644 --- a/arch/arm64/boot/dts/marvell/armada-3720-atlas-v5.dts +++ b/arch/arm64/boot/dts/marvell/armada-3720-atlas-v5.dts @@ -82,7 +82,6 @@ mmc-ddr-1_8v; mmc-hs400-1_8v; sd-uhs-sdr104; - marvell,xenon-emmc; marvell,xenon-tun-count = <9>; marvell,pad-type = "fixed-1-8v"; vqmmc-supply = <&vsdc_reg>; diff --git a/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dtsi b/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dtsi index fed2dcecb323..37e16fb3a383 100644 --- a/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dtsi @@ -78,7 +78,6 @@ bus-width = <8>; mmc-ddr-1_8v; mmc-hs400-1_8v; - marvell,xenon-emmc; marvell,xenon-tun-count = <9>; marvell,pad-type = "fixed-1-8v"; diff --git a/arch/arm64/boot/dts/marvell/armada-3720-uDPU.dtsi b/arch/arm64/boot/dts/marvell/armada-3720-uDPU.dtsi index 242820845707..12deacb741cc 100644 --- a/arch/arm64/boot/dts/marvell/armada-3720-uDPU.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-3720-uDPU.dtsi @@ -15,6 +15,11 @@ #include "armada-372x.dtsi" / { + aliases { + ethernet0 = ð0; + ethernet1 = ð1; + }; + chosen { stdout-path = "serial0:115200n8"; }; @@ -156,7 +161,7 @@ &usb3 { status = "okay"; phys = <&usb2_utmi_otg_phy>; - phy-names = "usb2-utmi-otg-phy"; + phy-names = "usb2-phy"; }; &uart0 { diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi index 87f9367aec12..360fc24fdde2 100644 --- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi @@ -369,11 +369,10 @@ compatible = "marvell,armada3700-xhci", "generic-xhci"; reg = <0x58000 0x4000>; - marvell,usb-misc-reg = <&usb32_syscon>; interrupts = ; clocks = <&sb_periph_clk 12>; - phys = <&comphy0 0>, <&usb2_utmi_otg_phy>; - phy-names = "usb3-phy", "usb2-utmi-otg-phy"; + phys = <&usb2_utmi_otg_phy>, <&comphy0 0>; + phy-names = "usb2-phy", "usb3-phy"; status = "disabled"; }; @@ -393,10 +392,9 @@ usb2: usb@5e000 { compatible = "marvell,armada-3700-ehci"; reg = <0x5e000 0x1000>; - marvell,usb-misc-reg = <&usb2_syscon>; interrupts = ; phys = <&usb2_utmi_host_phy>; - phy-names = "usb2-utmi-host-phy"; + phy-names = "usb"; status = "disabled"; }; @@ -534,7 +532,6 @@ armada-3700-rwtm { compatible = "marvell,armada-3700-rwtm-firmware"; mboxes = <&rwtm 0>; - status = "okay"; }; }; }; diff --git a/arch/arm64/boot/dts/marvell/armada-7020-comexpress.dtsi b/arch/arm64/boot/dts/marvell/armada-7020-comexpress.dtsi index 2b5ec4a451e3..0cfcf5f6bde1 100644 --- a/arch/arm64/boot/dts/marvell/armada-7020-comexpress.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-7020-comexpress.dtsi @@ -70,7 +70,7 @@ &cp0_eth1 { status = "okay"; - phy = <&phy0>; + phy-handle = <&phy0>; phy-mode = "rgmii-id"; }; diff --git a/arch/arm64/boot/dts/marvell/armada-8020.dtsi b/arch/arm64/boot/dts/marvell/armada-8020.dtsi deleted file mode 100644 index b6fc18876093..000000000000 --- a/arch/arm64/boot/dts/marvell/armada-8020.dtsi +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR MIT) -/* - * Copyright (C) 2016 Marvell Technology Group Ltd. - * - * Device Tree file for the Armada 8020 SoC, made of an AP806 Dual and - * two CP110. - */ - -#include "armada-ap806-dual.dtsi" -#include "armada-80x0.dtsi" - -/* The RTC requires external oscillator. But on Aramda 80x0, the RTC clock - * in CP master is not connected (by package) to the oscillator. So - * disable it. However, the RTC clock in CP slave is connected to the - * oscillator so this one is let enabled. - */ - -&cp0_rtc { - status = "disabled"; -}; diff --git a/arch/arm64/boot/dts/marvell/cn9130-db-comexpress.dtsi b/arch/arm64/boot/dts/marvell/cn9130-db-comexpress.dtsi deleted file mode 100644 index 028496ebc473..000000000000 --- a/arch/arm64/boot/dts/marvell/cn9130-db-comexpress.dtsi +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR MIT) -/* - * Copyright (C) 2023 Marvell International Ltd. - * - * Device tree for the CN9130-DB Com Express CPU module board. - */ - -#include "cn9130-db.dtsi" - -/ { - model = "Marvell Armada CN9130-DB COM EXPRESS type 7 CPU module board"; - compatible = "marvell,cn9130-cpu-module", "marvell,cn9130", - "marvell,armada-ap807-quad", "marvell,armada-ap807"; - -}; - -&ap0_reg_sd_vccq { - regulator-max-microvolt = <1800000>; - states = <1800000 0x1 1800000 0x0>; - /delete-property/ gpios; -}; - -&cp0_reg_usb3_vbus0 { - /delete-property/ gpio; -}; - -&cp0_reg_usb3_vbus1 { - /delete-property/ gpio; -}; - -&cp0_reg_sd_vcc { - status = "disabled"; -}; - -&cp0_reg_sd_vccq { - status = "disabled"; -}; - -&cp0_sdhci0 { - status = "disabled"; -}; - -&cp0_eth0 { - status = "disabled"; -}; - -&cp0_eth1 { - status = "okay"; - phy = <&phy0>; - phy-mode = "rgmii-id"; -}; - -&cp0_eth2 { - status = "disabled"; -}; - -&cp0_mdio { - status = "okay"; - pinctrl-0 = <&cp0_ge_mdio_pins>; - phy0: ethernet-phy@0 { - status = "okay"; - }; -}; - -&cp0_syscon0 { - cp0_pinctrl: pinctrl { - compatible = "marvell,cp115-standalone-pinctrl"; - - cp0_ge_mdio_pins: ge-mdio-pins { - marvell,pins = "mpp40", "mpp41"; - marvell,function = "ge"; - }; - }; -}; - -&cp0_sdhci0 { - status = "disabled"; -}; - -&cp0_spi1 { - status = "okay"; -}; - -&cp0_usb3_0 { - status = "okay"; - usb-phy = <&cp0_usb3_0_phy0>; - phy-names = "usb"; - /delete-property/ phys; -}; - -&cp0_usb3_1 { - status = "okay"; - usb-phy = <&cp0_usb3_0_phy1>; - phy-names = "usb"; - /delete-property/ phys; -}; diff --git a/arch/arm64/boot/dts/mediatek/mt6359.dtsi b/arch/arm64/boot/dts/mediatek/mt6359.dtsi index 467d8a4c2aa7..45ad69ee49ed 100644 --- a/arch/arm64/boot/dts/mediatek/mt6359.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt6359.dtsi @@ -205,7 +205,7 @@ regulator-max-microvolt = <1700000>; }; mt6359_vrfck_1_ldo_reg: ldo_vrfck_1 { - regulator-name = "vrfck"; + regulator-name = "vrfck_1"; regulator-min-microvolt = <1240000>; regulator-max-microvolt = <1600000>; }; @@ -227,7 +227,7 @@ regulator-max-microvolt = <3300000>; }; mt6359_vemc_1_ldo_reg: ldo_vemc_1 { - regulator-name = "vemc"; + regulator-name = "vemc_1"; regulator-min-microvolt = <2500000>; regulator-max-microvolt = <3300000>; }; diff --git a/arch/arm64/boot/dts/mediatek/mt6795.dtsi b/arch/arm64/boot/dts/mediatek/mt6795.dtsi index ae2aaa51c9ad..134cfa77e3b1 100644 --- a/arch/arm64/boot/dts/mediatek/mt6795.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt6795.dtsi @@ -371,7 +371,7 @@ ; gpio-controller; #gpio-cells = <2>; - gpio-ranges = <&pio 0 0 196>; + gpio-ranges = <&pio 0 0 197>; interrupt-controller; #interrupt-cells = <2>; }; diff --git a/arch/arm64/boot/dts/mediatek/mt7981b.dtsi b/arch/arm64/boot/dts/mediatek/mt7981b.dtsi index 4084f4dfa3e5..1bbe219380f9 100644 --- a/arch/arm64/boot/dts/mediatek/mt7981b.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt7981b.dtsi @@ -332,7 +332,7 @@ interrupt-controller; interrupts = ; interrupt-parent = <&gic>; - gpio-ranges = <&pio 0 0 56>; + gpio-ranges = <&pio 0 0 57>; gpio-controller; #gpio-cells = <2>; #interrupt-cells = <2>; diff --git a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi index 9693f62fd013..9ebc196107e5 100644 --- a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi @@ -187,7 +187,7 @@ "iocfg_lb", "iocfg_tr", "iocfg_tl", "eint"; gpio-controller; #gpio-cells = <2>; - gpio-ranges = <&pio 0 0 100>; + gpio-ranges = <&pio 0 0 101>; interrupt-controller; interrupts = ; interrupt-parent = <&gic>; diff --git a/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-4e.dts b/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-4e.dts index c7ea6e88c4f4..621d01e3cd89 100644 --- a/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-4e.dts +++ b/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-4e.dts @@ -9,7 +9,7 @@ #include "mt7988a-bananapi-bpi-r4-pro.dtsi" / { - model = "Bananapi BPI-R4"; + model = "Bananapi BPI-R4 Pro 4E"; compatible = "bananapi,bpi-r4-pro-4e", "bananapi,bpi-r4-pro", "mediatek,mt7988a"; diff --git a/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-8x.dts b/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-8x.dts index c9a0e69e9dd5..bb15bfa5e6ae 100644 --- a/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-8x.dts +++ b/arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-8x.dts @@ -9,7 +9,7 @@ #include "mt7988a-bananapi-bpi-r4-pro.dtsi" / { - model = "Bananapi BPI-R4"; + model = "Bananapi BPI-R4 Pro 8X"; compatible = "bananapi,bpi-r4-pro-8x", "bananapi,bpi-r4-pro", "mediatek,mt7988a"; diff --git a/arch/arm64/boot/dts/mediatek/mt8167.dtsi b/arch/arm64/boot/dts/mediatek/mt8167.dtsi index 2374c0953057..27cf32d7ae35 100644 --- a/arch/arm64/boot/dts/mediatek/mt8167.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8167.dtsi @@ -29,12 +29,6 @@ #clock-cells = <1>; }; - apmixedsys: apmixedsys@10018000 { - compatible = "mediatek,mt8167-apmixedsys", "syscon"; - reg = <0 0x10018000 0 0x710>; - #clock-cells = <1>; - }; - scpsys: syscon@10006000 { compatible = "mediatek,mt8167-scpsys", "syscon", "simple-mfd"; reg = <0 0x10006000 0 0x1000>; @@ -101,18 +95,6 @@ }; }; - imgsys: syscon@15000000 { - compatible = "mediatek,mt8167-imgsys", "syscon"; - reg = <0 0x15000000 0 0x1000>; - #clock-cells = <1>; - }; - - vdecsys: syscon@16000000 { - compatible = "mediatek,mt8167-vdecsys", "syscon"; - reg = <0 0x16000000 0 0x1000>; - #clock-cells = <1>; - }; - pio: pinctrl@1000b000 { compatible = "mediatek,mt8167-pinctrl"; reg = <0 0x1000b000 0 0x1000>; @@ -124,21 +106,26 @@ interrupts = ; }; + apmixedsys: apmixedsys@10018000 { + compatible = "mediatek,mt8167-apmixedsys", "syscon"; + reg = <0 0x10018000 0 0x710>; + #clock-cells = <1>; + }; + + iommu: m4u@10203000 { + compatible = "mediatek,mt8167-m4u"; + reg = <0 0x10203000 0 0x1000>; + mediatek,larbs = <&larb0>, <&larb1>, <&larb2>; + interrupts = ; + #iommu-cells = <1>; + }; + mmsys: syscon@14000000 { compatible = "mediatek,mt8167-mmsys", "syscon"; reg = <0 0x14000000 0 0x1000>; #clock-cells = <1>; }; - smi_common: smi@14017000 { - compatible = "mediatek,mt8167-smi-common"; - reg = <0 0x14017000 0 0x1000>; - clocks = <&mmsys CLK_MM_SMI_COMMON>, - <&mmsys CLK_MM_SMI_COMMON>; - clock-names = "apb", "smi"; - power-domains = <&spm MT8167_POWER_DOMAIN_MM>; - }; - larb0: larb@14016000 { compatible = "mediatek,mt8167-smi-larb"; reg = <0 0x14016000 0 0x1000>; @@ -149,6 +136,21 @@ power-domains = <&spm MT8167_POWER_DOMAIN_MM>; }; + smi_common: smi@14017000 { + compatible = "mediatek,mt8167-smi-common"; + reg = <0 0x14017000 0 0x1000>; + clocks = <&mmsys CLK_MM_SMI_COMMON>, + <&mmsys CLK_MM_SMI_COMMON>; + clock-names = "apb", "smi"; + power-domains = <&spm MT8167_POWER_DOMAIN_MM>; + }; + + imgsys: syscon@15000000 { + compatible = "mediatek,mt8167-imgsys", "syscon"; + reg = <0 0x15000000 0 0x1000>; + #clock-cells = <1>; + }; + larb1: larb@15001000 { compatible = "mediatek,mt8167-smi-larb"; reg = <0 0x15001000 0 0x1000>; @@ -159,6 +161,12 @@ power-domains = <&spm MT8167_POWER_DOMAIN_ISP>; }; + vdecsys: syscon@16000000 { + compatible = "mediatek,mt8167-vdecsys", "syscon"; + reg = <0 0x16000000 0 0x1000>; + #clock-cells = <1>; + }; + larb2: larb@16010000 { compatible = "mediatek,mt8167-smi-larb"; reg = <0 0x16010000 0 0x1000>; @@ -168,13 +176,5 @@ clock-names = "apb", "smi"; power-domains = <&spm MT8167_POWER_DOMAIN_VDEC>; }; - - iommu: m4u@10203000 { - compatible = "mediatek,mt8167-m4u"; - reg = <0 0x10203000 0 0x1000>; - mediatek,larbs = <&larb0>, <&larb1>, <&larb2>; - interrupts = ; - #iommu-cells = <1>; - }; }; }; diff --git a/arch/arm64/boot/dts/mediatek/mt8195-cherry-dojo-r1.dts b/arch/arm64/boot/dts/mediatek/mt8195-cherry-dojo-r1.dts index 49664de99b88..57cc329f49c4 100644 --- a/arch/arm64/boot/dts/mediatek/mt8195-cherry-dojo-r1.dts +++ b/arch/arm64/boot/dts/mediatek/mt8195-cherry-dojo-r1.dts @@ -11,6 +11,28 @@ compatible = "google,dojo-sku7", "google,dojo-sku5", "google,dojo-sku3", "google,dojo-sku1", "google,dojo", "mediatek,mt8195"; + + nvme-connector { + compatible = "pcie-m2-m-connector"; + /* power is controlled by EC */ + vpcie3v3-supply = <&pp3300_z2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + nvme_ep: endpoint@0 { + reg = <0>; + remote-endpoint = <&pcie0_ep>; + }; + }; + }; + }; }; &audio_codec { @@ -72,6 +94,22 @@ pinctrl-names = "default"; pinctrl-0 = <&pcie0_pins_default>; status = "okay"; + + pcie@0 { + compatible = "pciclass,0604"; + reg = <0 0 0 0 0>; + device_type = "pci"; + num-lanes = <2>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + port { + pcie0_ep: endpoint { + remote-endpoint = <&nvme_ep>; + }; + }; + }; }; &pciephy { diff --git a/arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi b/arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi index b3761b80cac7..f1ff64a84267 100644 --- a/arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi @@ -83,6 +83,17 @@ vin-supply = <&pp3300_z2>; }; + pp3300_wlan: regulator-pp3300-wlan { + compatible = "regulator-fixed"; + pinctrl-names = "default"; + pinctrl-0 = <&pp3300_wlan_en_pin>; + regulator-name = "pp3300_wlan"; + /* load switch */ + enable-active-high; + gpio = <&pio 58 GPIO_ACTIVE_HIGH>; + vin-supply = <&pp3300_z2>; + }; + /* system wide 3.3V power rail */ pp3300_z2: regulator-pp3300-z2 { compatible = "regulator-fixed"; @@ -760,10 +771,25 @@ }; &pcie1 { - status = "okay"; - pinctrl-names = "default"; pinctrl-0 = <&pcie1_pins_default>; + status = "okay"; + + pcie@0 { + compatible = "pciclass,0604"; + reg = <0 0 0 0 0>; + device_type = "pci"; + num-lanes = <1>; + vpcie3v3-supply = <&pp3300_wlan>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + wifi@0 { + reg = <0 0 0 0 0>; + wakeup-source; + }; + }; }; &pio { @@ -1179,12 +1205,6 @@ }; pio_default: pio-default-pins { - pins-wifi-enable { - pinmux = ; - output-high; - drive-strength = <14>; - }; - pins-low-power-pd { pinmux = , , @@ -1222,6 +1242,12 @@ }; }; + pp3300_wlan_en_pin: pp3300-wlan-en-pins { + pins-en { + pinmux = ; + }; + }; + rt1019p_pins_default: rt1019p-default-pins { pins-amp-sdb { pinmux = ; @@ -1495,6 +1521,7 @@ }; &u3phy1 { + /* shared between xhci1 and pcie1. */ status = "okay"; }; @@ -1563,27 +1590,16 @@ vbus-supply = <&usb_vbus>; }; -&xhci1 { - status = "okay"; - - phys = <&u2port1 PHY_TYPE_USB2>; - rx-fifo-depth = <3072>; - vusb33-supply = <&mt6359_vusb_ldo_reg>; - vbus-supply = <&usb_vbus>; - mediatek,u3p-dis-msk = <1>; -}; - &xhci2 { status = "okay"; vbus-supply = <&usb_vbus>; }; &xhci3 { - status = "okay"; - /* MT7921's USB Bluetooth has issues with USB2 LPM */ usb2-lpm-disable; - vbus-supply = <&usb_vbus>; + vbus-supply = <&pp3300_wlan>; + status = "okay"; }; #include diff --git a/arch/arm64/boot/dts/mediatek/mt8365-evk.dts b/arch/arm64/boot/dts/mediatek/mt8365-evk.dts index b5dd5ef9fa11..a30ee523b0b5 100644 --- a/arch/arm64/boot/dts/mediatek/mt8365-evk.dts +++ b/arch/arm64/boot/dts/mediatek/mt8365-evk.dts @@ -20,8 +20,10 @@ compatible = "mediatek,mt8365-evk", "mediatek,mt8365"; aliases { - serial0 = &uart0; ethernet = ðernet; + mmc0 = &mmc0; + mmc1 = &mmc1; + serial0 = &uart0; }; chosen { diff --git a/arch/arm64/boot/dts/mediatek/mt8365.dtsi b/arch/arm64/boot/dts/mediatek/mt8365.dtsi index a5ca3cda6ef3..2e782558fb77 100644 --- a/arch/arm64/boot/dts/mediatek/mt8365.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8365.dtsi @@ -536,10 +536,9 @@ #iommu-cells = <1>; }; - infracfg_nao: infracfg@1020e000 { - compatible = "mediatek,mt8365-infracfg", "syscon"; + infracfg_nao: syscon@1020e000 { + compatible = "mediatek,mt8365-infracfg-nao", "syscon"; reg = <0 0x1020e000 0 0x1000>; - #clock-cells = <1>; }; rng: rng@1020f000 { diff --git a/arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi b/arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi index d849af4d3613..62c336e21500 100644 --- a/arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi @@ -18,8 +18,10 @@ / { aliases { - serial0 = &uart0; ethernet0 = ð + mmc0 = &mmc0; + mmc1 = &mmc1; + serial0 = &uart0; }; chosen { diff --git a/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts b/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts index 1cd4b84e9861..bf91305e8e4a 100644 --- a/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts +++ b/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts @@ -21,12 +21,14 @@ compatible = "radxa,nio-12l", "mediatek,mt8395", "mediatek,mt8195"; aliases { + ethernet0 = ð i2c0 = &i2c2; i2c1 = &i2c3; i2c2 = &i2c4; i2c3 = &i2c0; i2c4 = &i2c1; - ethernet0 = ð + mmc0 = &mmc0; + mmc1 = &mmc1; serial0 = &uart0; serial1 = &uart1; spi0 = &spi1; diff --git a/arch/arm64/boot/dts/microchip/Makefile b/arch/arm64/boot/dts/microchip/Makefile index c6e0313eea0f..09d16fc1ce9a 100644 --- a/arch/arm64/boot/dts/microchip/Makefile +++ b/arch/arm64/boot/dts/microchip/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_LAN969X) += lan9696-ev23x71a.dtb dtb-$(CONFIG_ARCH_SPARX5) += sparx5_pcb125.dtb dtb-$(CONFIG_ARCH_SPARX5) += sparx5_pcb134.dtb sparx5_pcb134_emmc.dtb dtb-$(CONFIG_ARCH_SPARX5) += sparx5_pcb135.dtb sparx5_pcb135_emmc.dtb diff --git a/arch/arm64/boot/dts/microchip/clk-lan9691.h b/arch/arm64/boot/dts/microchip/clk-lan9691.h new file mode 100644 index 000000000000..0f2d7a0f881e --- /dev/null +++ b/arch/arm64/boot/dts/microchip/clk-lan9691.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) */ + +#ifndef _DTS_CLK_LAN9691_H +#define _DTS_CLK_LAN9691_H + +#define GCK_ID_QSPI0 0 +#define GCK_ID_QSPI2 1 +#define GCK_ID_SDMMC0 2 +#define GCK_ID_SDMMC1 3 +#define GCK_ID_MCAN0 4 +#define GCK_ID_MCAN1 5 +#define GCK_ID_FLEXCOM0 6 +#define GCK_ID_FLEXCOM1 7 +#define GCK_ID_FLEXCOM2 8 +#define GCK_ID_FLEXCOM3 9 +#define GCK_ID_TIMER 10 +#define GCK_ID_USB_REFCLK 11 + +/* Gate clocks */ +#define GCK_GATE_USB_DRD 12 +#define GCK_GATE_MCRAMC 13 +#define GCK_GATE_HMATRIX 14 + +#endif diff --git a/arch/arm64/boot/dts/microchip/lan9691.dtsi b/arch/arm64/boot/dts/microchip/lan9691.dtsi new file mode 100644 index 000000000000..235e56bebbdb --- /dev/null +++ b/arch/arm64/boot/dts/microchip/lan9691.dtsi @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) +/* + * Copyright (c) 2025 Microchip Technology Inc. and its subsidiaries. + */ + +#include +#include +#include +#include + +#include "clk-lan9691.h" + +/ { + #address-cells = <1>; + #size-cells = <1>; + + model = "Microchip LAN969x"; + compatible = "microchip,lan9691"; + interrupt-parent = <&gic>; + + clocks { + fx100_clk: fx100-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <320000000>; + }; + + cpu_clk: cpu-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <1000000000>; + }; + + ddr_clk: ddr-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <600000000>; + }; + + fabric_clk: fabric-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <250000000>; + }; + }; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + reg = <0x0 0x0>; + next-level-cache = <&l2_0>; + }; + + l2_0: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + }; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + + pmu { + compatible = "arm,cortex-a53-pmu"; + interrupts = ; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , /* Secure Phys IRQ */ + , /* Non-secure Phys IRQ */ + , /* Virt IRQ */ + ; /* Hyp IRQ */ + }; + + axi: axi { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + usb: usb@300000 { + compatible = "microchip,lan9691-dwc3", "snps,dwc3"; + reg = <0x300000 0x80000>; + interrupts = ; + clocks = <&clks GCK_GATE_USB_DRD>, + <&clks GCK_ID_USB_REFCLK>; + clock-names = "bus_early", "ref"; + assigned-clocks = <&clks GCK_ID_USB_REFCLK>; + assigned-clock-rates = <60000000>; + maximum-speed = "high-speed"; + dr_mode = "host"; + status = "disabled"; + }; + + flx0: flexcom@e0040000 { + compatible = "microchip,lan9691-flexcom", "atmel,sama5d2-flexcom"; + reg = <0xe0040000 0x100>; + ranges = <0x0 0xe0040000 0x800>; + clocks = <&clks GCK_ID_FLEXCOM0>; + #address-cells = <1>; + #size-cells = <1>; + status = "disabled"; + + usart0: serial@200 { + compatible = "microchip,lan9691-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(3)>, + <&dma AT91_XDMAC_DT_PERID(2)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + clock-names = "usart"; + atmel,fifo-size = <32>; + atmel,usart-mode = ; + status = "disabled"; + }; + + spi0: spi@400 { + compatible = "microchip,lan9691-spi", "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(3)>, + <&dma AT91_XDMAC_DT_PERID(2)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + clock-names = "spi_clk"; + #address-cells = <1>; + #size-cells = <0>; + atmel,fifo-size = <32>; + status = "disabled"; + }; + + i2c0: i2c@600 { + compatible = "microchip,lan9691-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(3)>, + <&dma AT91_XDMAC_DT_PERID(2)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + }; + + flx1: flexcom@e0044000 { + compatible = "microchip,lan9691-flexcom", "atmel,sama5d2-flexcom"; + reg = <0xe0044000 0x100>; + ranges = <0x0 0xe0044000 0x800>; + clocks = <&clks GCK_ID_FLEXCOM1>; + #address-cells = <1>; + #size-cells = <1>; + status = "disabled"; + + usart1: serial@200 { + compatible = "microchip,lan9691-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(3)>, + <&dma AT91_XDMAC_DT_PERID(2)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + clock-names = "usart"; + atmel,fifo-size = <32>; + atmel,usart-mode = ; + status = "disabled"; + }; + + spi1: spi@400 { + compatible = "microchip,lan9691-spi", "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(3)>, + <&dma AT91_XDMAC_DT_PERID(2)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + clock-names = "spi_clk"; + #address-cells = <1>; + #size-cells = <0>; + atmel,fifo-size = <32>; + status = "disabled"; + }; + + i2c1: i2c@600 { + compatible = "microchip,lan9691-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(3)>, + <&dma AT91_XDMAC_DT_PERID(2)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + }; + + trng: rng@e0048000 { + compatible = "microchip,lan9691-trng", "atmel,at91sam9g45-trng"; + reg = <0xe0048000 0x100>; + clocks = <&fabric_clk>; + status = "disabled"; + }; + + aes: crypto@e004c000 { + compatible = "microchip,lan9691-aes", "atmel,at91sam9g46-aes"; + reg = <0xe004c000 0x100>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(12)>, + <&dma AT91_XDMAC_DT_PERID(13)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + clock-names = "aes_clk"; + status = "disabled"; + }; + + flx2: flexcom@e0060000 { + compatible = "microchip,lan9691-flexcom", "atmel,sama5d2-flexcom"; + reg = <0xe0060000 0x100>; + ranges = <0x0 0xe0060000 0x800>; + clocks = <&clks GCK_ID_FLEXCOM2>; + #address-cells = <1>; + #size-cells = <1>; + status = "disabled"; + + usart2: serial@200 { + compatible = "microchip,lan9691-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(7)>, + <&dma AT91_XDMAC_DT_PERID(6)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + clock-names = "usart"; + atmel,fifo-size = <32>; + atmel,usart-mode = ; + status = "disabled"; + }; + + spi2: spi@400 { + compatible = "microchip,lan9691-spi", "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(7)>, + <&dma AT91_XDMAC_DT_PERID(6)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + clock-names = "spi_clk"; + #address-cells = <1>; + #size-cells = <0>; + atmel,fifo-size = <32>; + status = "disabled"; + }; + + i2c2: i2c@600 { + compatible = "microchip,lan9691-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(7)>, + <&dma AT91_XDMAC_DT_PERID(6)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + }; + + flx3: flexcom@e0064000 { + compatible = "microchip,lan9691-flexcom", "atmel,sama5d2-flexcom"; + reg = <0xe0064000 0x100>; + ranges = <0x0 0xe0064000 0x800>; + clocks = <&clks GCK_ID_FLEXCOM3>; + #address-cells = <1>; + #size-cells = <1>; + status = "disabled"; + + usart3: serial@200 { + compatible = "microchip,lan9691-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(9)>, + <&dma AT91_XDMAC_DT_PERID(8)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + clock-names = "usart"; + atmel,fifo-size = <32>; + atmel,usart-mode = ; + status = "disabled"; + }; + + spi3: spi@400 { + compatible = "microchip,lan9691-spi", "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(9)>, + <&dma AT91_XDMAC_DT_PERID(8)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + clock-names = "spi_clk"; + #address-cells = <1>; + #size-cells = <0>; + atmel,fifo-size = <32>; + status = "disabled"; + }; + + i2c3: i2c@600 { + compatible = "microchip,lan9691-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(9)>, + <&dma AT91_XDMAC_DT_PERID(8)>; + dma-names = "tx", "rx"; + clocks = <&fabric_clk>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + }; + + dma: dma-controller@e0068000 { + compatible = "microchip,lan9691-dma", "microchip,sama7g5-dma"; + reg = <0xe0068000 0x1000>; + interrupts = ; + dma-channels = <16>; + #dma-cells = <1>; + clocks = <&fabric_clk>; + clock-names = "dma_clk"; + }; + + sha: crypto@e006c000 { + compatible = "microchip,lan9691-sha", "atmel,at91sam9g46-sha"; + reg = <0xe006c000 0xec>; + interrupts = ; + dmas = <&dma AT91_XDMAC_DT_PERID(14)>; + dma-names = "tx"; + clocks = <&fabric_clk>; + clock-names = "sha_clk"; + status = "disabled"; + }; + + timer: timer@e008c000 { + compatible = "snps,dw-apb-timer"; + reg = <0xe008c000 0x400>; + clocks = <&fabric_clk>; + clock-names = "timer"; + interrupts = ; + status = "disabled"; + }; + + watchdog: watchdog@e0090000 { + compatible = "snps,dw-wdt"; + reg = <0xe0090000 0x1000>; + interrupts = ; + clocks = <&fabric_clk>; + }; + + cpu_ctrl: syscon@e00c0000 { + compatible = "microchip,lan966x-cpu-syscon", "syscon"; + reg = <0xe00c0000 0x350>; + }; + + switch: switch@e00c0000 { + compatible = "microchip,lan9691-switch"; + reg = <0xe00c0000 0x0010000>, + <0xe2010000 0x1410000>; + reg-names = "cpu", "devices"; + interrupt-names = "xtr", "fdma", "ptp"; + interrupts = , + , + ; + resets = <&reset 0>; + reset-names = "switch"; + status = "disabled"; + }; + + clks: clock-controller@e00c00b4 { + compatible = "microchip,lan9691-gck"; + reg = <0xe00c00b4 0x30>, <0xe00c0308 0x4>; + #clock-cells = <1>; + clocks = <&cpu_clk>, <&ddr_clk>, <&fx100_clk>; + clock-names = "cpu", "ddr", "sys"; + }; + + reset: reset-controller@e201000c { + compatible = "microchip,lan9691-switch-reset", + "microchip,lan966x-switch-reset"; + reg = <0xe201000c 0x4>; + reg-names = "gcb"; + #reset-cells = <1>; + cpu-syscon = <&cpu_ctrl>; + }; + + gpio: pinctrl@e20100d4 { + compatible = "microchip,lan9691-pinctrl"; + reg = <0xe20100d4 0xd4>, + <0xe2010370 0xa8>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&gpio 0 0 66>; + interrupt-controller; + interrupts = ; + #interrupt-cells = <2>; + }; + + mdio0: mdio@e20101a8 { + compatible = "microchip,lan9691-miim", "mscc,ocelot-miim"; + reg = <0xe20101a8 0x24>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&fx100_clk>; + status = "disabled"; + }; + + mdio1: mdio@e20101cc { + compatible = "microchip,lan9691-miim", "mscc,ocelot-miim"; + reg = <0xe20101cc 0x24>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&fx100_clk>; + status = "disabled"; + }; + + sgpio: gpio@e2010230 { + compatible = "microchip,lan9691-sgpio", "microchip,sparx5-sgpio"; + reg = <0xe2010230 0x118>; + clocks = <&fx100_clk>; + resets = <&reset 0>; + reset-names = "switch"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + sgpio_in: gpio@0 { + compatible = "microchip,lan9691-sgpio-bank", + "microchip,sparx5-sgpio-bank"; + reg = <0>; + gpio-controller; + #gpio-cells = <3>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; + }; + + sgpio_out: gpio@1 { + compatible = "microchip,lan9691-sgpio-bank", + "microchip,sparx5-sgpio-bank"; + reg = <1>; + gpio-controller; + #gpio-cells = <3>; + }; + }; + + tmon: hwmon@e2020100 { + compatible = "microchip,lan9691-temp", "microchip,sparx5-temp"; + reg = <0xe2020100 0xc>; + clocks = <&fx100_clk>; + #thermal-sensor-cells = <0>; + }; + + serdes: serdes@e3410000 { + compatible = "microchip,lan9691-serdes"; + reg = <0xe3410000 0x150000>; + #phy-cells = <1>; + clocks = <&fabric_clk>; + }; + + gic: interrupt-controller@e8c11000 { + compatible = "arm,gic-400"; + reg = <0xe8c11000 0x1000>, /* Distributor GICD_ */ + <0xe8c12000 0x2000>, /* CPU interface GICC_ */ + <0xe8c14000 0x2000>, /* Virt interface control */ + <0xe8c16000 0x2000>; /* Virt CPU interface */ + #interrupt-cells = <3>; + interrupt-controller; + interrupts = ; + }; + }; +}; diff --git a/arch/arm64/boot/dts/microchip/lan9696-ev23x71a.dts b/arch/arm64/boot/dts/microchip/lan9696-ev23x71a.dts new file mode 100644 index 000000000000..4012ea7d07bb --- /dev/null +++ b/arch/arm64/boot/dts/microchip/lan9696-ev23x71a.dts @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) +/* + * Copyright (c) 2025 Microchip Technology Inc. and its subsidiaries. + */ + +/dts-v1/; + +#include +#include +#include "lan9691.dtsi" + +/ { + model = "Microchip EV23X71A"; + compatible = "microchip,ev23x71a", "microchip,lan9696", "microchip,lan9691"; + + aliases { + serial0 = &usart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + gpio-restart { + compatible = "gpio-restart"; + gpios = <&gpio 60 GPIO_ACTIVE_LOW>; + open-source; + priority = <200>; + }; + + i2c-mux { + compatible = "i2c-mux-gpio"; + #address-cells = <1>; + #size-cells = <0>; + i2c-parent = <&i2c3>; + idle-state = <0x8>; + mux-gpios = <&sgpio_out 0 1 GPIO_ACTIVE_HIGH>, + <&sgpio_out 0 2 GPIO_ACTIVE_HIGH>, + <&sgpio_out 0 3 GPIO_ACTIVE_HIGH>; + settle-time-us = <100>; + + i2c_sfp0: i2c@0 { + reg = <0x0>; + }; + + i2c_sfp1: i2c@1 { + reg = <0x1>; + }; + + i2c_sfp2: i2c@2 { + reg = <0x2>; + }; + + i2c_sfp3: i2c@3 { + reg = <0x3>; + }; + + i2c_poe: i2c@7 { + reg = <0x7>; + }; + }; + + leds { + compatible = "gpio-leds"; + + led-status { + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&gpio 61 GPIO_ACTIVE_LOW>; + }; + + led-sfp1-green { + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <0>; + gpios = <&sgpio_out 6 0 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + led-sfp1-yellow { + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <0>; + gpios = <&sgpio_out 6 1 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + led-sfp2-green { + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <1>; + gpios = <&sgpio_out 7 0 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + led-sfp2-yellow { + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <1>; + gpios = <&sgpio_out 7 1 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + led-sfp3-green { + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <2>; + gpios = <&sgpio_out 8 0 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + led-sfp3-yellow { + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <2>; + gpios = <&sgpio_out 8 1 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + led-sfp4-green { + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <3>; + gpios = <&sgpio_out 9 0 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + + led-sfp4-yellow { + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <3>; + gpios = <&sgpio_out 9 1 GPIO_ACTIVE_LOW>; + default-state = "off"; + }; + }; + + mux-controller { + compatible = "gpio-mux"; + #mux-control-cells = <0>; + mux-gpios = <&sgpio_out 1 2 GPIO_ACTIVE_LOW>, + <&sgpio_out 1 3 GPIO_ACTIVE_LOW>; + }; + + sfp0: sfp0 { + compatible = "sff,sfp"; + i2c-bus = <&i2c_sfp0>; + tx-disable-gpios = <&sgpio_out 6 2 GPIO_ACTIVE_HIGH>; + los-gpios = <&sgpio_in 6 0 GPIO_ACTIVE_HIGH>; + mod-def0-gpios = <&sgpio_in 6 1 GPIO_ACTIVE_LOW>; + tx-fault-gpios = <&sgpio_in 6 2 GPIO_ACTIVE_HIGH>; + }; + + sfp1: sfp1 { + compatible = "sff,sfp"; + i2c-bus = <&i2c_sfp1>; + tx-disable-gpios = <&sgpio_out 7 2 GPIO_ACTIVE_HIGH>; + los-gpios = <&sgpio_in 7 0 GPIO_ACTIVE_HIGH>; + mod-def0-gpios = <&sgpio_in 7 1 GPIO_ACTIVE_LOW>; + tx-fault-gpios = <&sgpio_in 7 2 GPIO_ACTIVE_HIGH>; + }; + + sfp2: sfp2 { + compatible = "sff,sfp"; + i2c-bus = <&i2c_sfp2>; + tx-disable-gpios = <&sgpio_out 8 2 GPIO_ACTIVE_HIGH>; + los-gpios = <&sgpio_in 8 0 GPIO_ACTIVE_HIGH>; + mod-def0-gpios = <&sgpio_in 8 1 GPIO_ACTIVE_LOW>; + tx-fault-gpios = <&sgpio_in 8 2 GPIO_ACTIVE_HIGH>; + }; + + sfp3: sfp3 { + compatible = "sff,sfp"; + i2c-bus = <&i2c_sfp3>; + tx-disable-gpios = <&sgpio_out 9 2 GPIO_ACTIVE_HIGH>; + los-gpios = <&sgpio_in 9 0 GPIO_ACTIVE_HIGH>; + mod-def0-gpios = <&sgpio_in 9 1 GPIO_ACTIVE_LOW>; + tx-fault-gpios = <&sgpio_in 9 2 GPIO_ACTIVE_HIGH>; + }; +}; + +&gpio { + emmc_sd_pins: emmc-sd-pins { + /* eMMC_SD - CMD, CLK, D0, D1, D2, D3, D4, D5, D6, D7, RSTN */ + pins = "GPIO_14", "GPIO_15", "GPIO_16", "GPIO_17", + "GPIO_18", "GPIO_19", "GPIO_20", "GPIO_21", + "GPIO_22", "GPIO_23", "GPIO_24"; + function = "emmc_sd"; + }; + + fan_pins: fan-pins { + pins = "GPIO_25", "GPIO_26"; + function = "fan"; + }; + + fc0_pins: fc0-pins { + pins = "GPIO_3", "GPIO_4"; + function = "fc"; + }; + + fc2_pins: fc2-pins { + pins = "GPIO_64", "GPIO_65", "GPIO_66"; + function = "fc"; + }; + + fc3_pins: fc3-pins { + pins = "GPIO_55", "GPIO_56"; + function = "fc"; + }; + + mdio_irq_pins: mdio-irq-pins { + pins = "GPIO_11"; + function = "miim_irq"; + }; + + mdio_pins: mdio-pins { + pins = "GPIO_9", "GPIO_10"; + function = "miim"; + }; + + ptp_ext_pins: ptp-ext-pins { + pins = "GPIO_59"; + function = "ptpsync_5"; + }; + + ptp_out_pins: ptp-out-pins { + pins = "GPIO_58"; + function = "ptpsync_4"; + }; + + sgpio_pins: sgpio-pins { + /* SCK, D0, D1, LD */ + pins = "GPIO_5", "GPIO_6", "GPIO_7", "GPIO_8"; + function = "sgpio_a"; + }; + + usb_over_pins: usb-over-pins { + pins = "GPIO_13"; + function = "usb_over_detect"; + }; + + usb_power_pins: usb-power-pins { + pins = "GPIO_1"; + function = "usb_power"; + }; + + usb_rst_pins: usb-rst-pins { + pins = "GPIO_12"; + function = "usb2phy_rst"; + }; + + usb_ulpi_pins: usb-ulpi-pins { + pins = "GPIO_30", "GPIO_31", "GPIO_32", "GPIO_33", + "GPIO_34", "GPIO_35", "GPIO_36", "GPIO_37", + "GPIO_38", "GPIO_39", "GPIO_40", "GPIO_41"; + function = "usb_ulpi"; + }; +}; + +&flx0 { + atmel,flexcom-mode = ; + status = "okay"; +}; + +&flx2 { + atmel,flexcom-mode = ; + status = "okay"; +}; + +&flx3 { + atmel,flexcom-mode = ; + status = "okay"; +}; + +&i2c3 { + pinctrl-0 = <&fc3_pins>; + pinctrl-names = "default"; + i2c-analog-filter; + i2c-digital-filter; + i2c-digital-filter-width-ns = <35>; + i2c-sda-hold-time-ns = <1500>; + status = "okay"; +}; + +&mdio0 { + pinctrl-0 = <&mdio_pins>, <&mdio_irq_pins>; + pinctrl-names = "default"; + reset-gpios = <&gpio 62 GPIO_ACTIVE_LOW>; + status = "okay"; + + phy3: phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy4: phy@4 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <4>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy5: phy@5 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <5>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <6>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy7: phy@7 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <7>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy8: phy@8 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <8>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy9: phy@9 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <9>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy10: phy@10 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <10>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy11: phy@11 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <11>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy12: phy@12 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <12>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy13: phy@13 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <13>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy14: phy@14 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <14>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy15: phy@15 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <15>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy16: phy@16 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <16>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy17: phy@17 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <17>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy18: phy@18 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <18>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy19: phy@19 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <19>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy20: phy@20 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <20>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy21: phy@21 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <21>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy22: phy@22 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <22>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy23: phy@23 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <23>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy24: phy@24 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <24>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy25: phy@25 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <25>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy26: phy@26 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <26>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; + + phy27: phy@27 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <27>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + }; +}; + +&serdes { + status = "okay"; +}; + +&sgpio { + pinctrl-0 = <&sgpio_pins>; + pinctrl-names = "default"; + microchip,sgpio-port-ranges = <0 1>, <6 9>; + status = "okay"; + + gpio@0 { + ngpios = <128>; + }; + gpio@1 { + ngpios = <128>; + }; +}; + +&spi2 { + pinctrl-0 = <&fc2_pins>; + pinctrl-names = "default"; + cs-gpios = <&gpio 63 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&switch { + pinctrl-0 = <&ptp_out_pins>, <&ptp_ext_pins>; + pinctrl-names = "default"; + status = "okay"; + + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + + port0: port@0 { + reg = <0>; + phy-handle = <&phy4>; + phy-mode = "qsgmii"; + phys = <&serdes 0>; + microchip,bandwidth = <1000>; + }; + + port1: port@1 { + reg = <1>; + phy-handle = <&phy5>; + phy-mode = "qsgmii"; + phys = <&serdes 0>; + microchip,bandwidth = <1000>; + }; + + port2: port@2 { + reg = <2>; + phy-handle = <&phy6>; + phy-mode = "qsgmii"; + phys = <&serdes 0>; + microchip,bandwidth = <1000>; + }; + + port3: port@3 { + reg = <3>; + phy-handle = <&phy7>; + phy-mode = "qsgmii"; + phys = <&serdes 0>; + microchip,bandwidth = <1000>; + }; + + port4: port@4 { + reg = <4>; + phy-handle = <&phy8>; + phy-mode = "qsgmii"; + phys = <&serdes 1>; + microchip,bandwidth = <1000>; + }; + + port5: port@5 { + reg = <5>; + phy-handle = <&phy9>; + phy-mode = "qsgmii"; + phys = <&serdes 1>; + microchip,bandwidth = <1000>; + }; + + port6: port@6 { + reg = <6>; + phy-handle = <&phy10>; + phy-mode = "qsgmii"; + phys = <&serdes 1>; + microchip,bandwidth = <1000>; + }; + + port7: port@7 { + reg = <7>; + phy-handle = <&phy11>; + phy-mode = "qsgmii"; + phys = <&serdes 1>; + microchip,bandwidth = <1000>; + }; + + port8: port@8 { + reg = <8>; + phy-handle = <&phy12>; + phy-mode = "qsgmii"; + phys = <&serdes 2>; + microchip,bandwidth = <1000>; + }; + + port9: port@9 { + reg = <9>; + phy-handle = <&phy13>; + phy-mode = "qsgmii"; + phys = <&serdes 2>; + microchip,bandwidth = <1000>; + }; + + port10: port@10 { + reg = <10>; + phy-handle = <&phy14>; + phy-mode = "qsgmii"; + phys = <&serdes 2>; + microchip,bandwidth = <1000>; + }; + + port11: port@11 { + reg = <11>; + phy-handle = <&phy15>; + phy-mode = "qsgmii"; + phys = <&serdes 2>; + microchip,bandwidth = <1000>; + }; + + port12: port@12 { + reg = <12>; + phy-handle = <&phy16>; + phy-mode = "qsgmii"; + phys = <&serdes 3>; + microchip,bandwidth = <1000>; + }; + + port13: port@13 { + reg = <13>; + phy-handle = <&phy17>; + phy-mode = "qsgmii"; + phys = <&serdes 3>; + microchip,bandwidth = <1000>; + }; + + port14: port@14 { + reg = <14>; + phy-handle = <&phy18>; + phy-mode = "qsgmii"; + phys = <&serdes 3>; + microchip,bandwidth = <1000>; + }; + + port15: port@15 { + reg = <15>; + phy-handle = <&phy19>; + phy-mode = "qsgmii"; + phys = <&serdes 3>; + microchip,bandwidth = <1000>; + }; + + port16: port@16 { + reg = <16>; + phy-handle = <&phy20>; + phy-mode = "qsgmii"; + phys = <&serdes 4>; + microchip,bandwidth = <1000>; + }; + + port17: port@17 { + reg = <17>; + phy-handle = <&phy21>; + phy-mode = "qsgmii"; + phys = <&serdes 4>; + microchip,bandwidth = <1000>; + }; + + port18: port@18 { + reg = <18>; + phy-handle = <&phy22>; + phy-mode = "qsgmii"; + phys = <&serdes 4>; + microchip,bandwidth = <1000>; + }; + + port19: port@19 { + reg = <19>; + phy-handle = <&phy23>; + phy-mode = "qsgmii"; + phys = <&serdes 4>; + microchip,bandwidth = <1000>; + }; + + port20: port@20 { + reg = <20>; + phy-handle = <&phy24>; + phy-mode = "qsgmii"; + phys = <&serdes 5>; + microchip,bandwidth = <1000>; + }; + + port21: port@21 { + reg = <21>; + phy-handle = <&phy25>; + phy-mode = "qsgmii"; + phys = <&serdes 5>; + microchip,bandwidth = <1000>; + }; + + port22: port@22 { + reg = <22>; + phy-handle = <&phy26>; + phy-mode = "qsgmii"; + phys = <&serdes 5>; + microchip,bandwidth = <1000>; + }; + + port23: port@23 { + reg = <23>; + phy-handle = <&phy27>; + phy-mode = "qsgmii"; + phys = <&serdes 5>; + microchip,bandwidth = <1000>; + }; + + port24: port@24 { + reg = <24>; + phys = <&serdes 6>; + phy-mode = "10gbase-r"; + sfp = <&sfp0>; + managed = "in-band-status"; + microchip,bandwidth = <10000>; + microchip,sd-sgpio = <24>; + }; + + port25: port@25 { + reg = <25>; + phys = <&serdes 7>; + phy-mode = "10gbase-r"; + sfp = <&sfp1>; + managed = "in-band-status"; + microchip,bandwidth = <10000>; + microchip,sd-sgpio = <28>; + }; + + port26: port@26 { + reg = <26>; + phys = <&serdes 8>; + phy-mode = "10gbase-r"; + sfp = <&sfp2>; + managed = "in-band-status"; + microchip,bandwidth = <10000>; + microchip,sd-sgpio = <32>; + }; + + port27: port@27 { + reg = <27>; + phys = <&serdes 9>; + phy-mode = "10gbase-r"; + sfp = <&sfp3>; + managed = "in-band-status"; + microchip,bandwidth = <10000>; + microchip,sd-sgpio = <36>; + }; + + port29: port@29 { + reg = <29>; + phy-handle = <&phy3>; + phy-mode = "rgmii-id"; + microchip,bandwidth = <1000>; + }; + }; +}; + +&tmon { + pinctrl-0 = <&fan_pins>; + pinctrl-names = "default"; +}; + +&usart0 { + pinctrl-0 = <&fc0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&usb { + pinctrl-0 = <&usb_ulpi_pins>, <&usb_rst_pins>, <&usb_over_pins>, <&usb_power_pins>; + pinctrl-names = "default"; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/nuvoton/nuvoton-common-npcm8xx.dtsi b/arch/arm64/boot/dts/nuvoton/nuvoton-common-npcm8xx.dtsi index 24133528b8e9..c781190b42c5 100644 --- a/arch/arm64/boot/dts/nuvoton/nuvoton-common-npcm8xx.dtsi +++ b/arch/arm64/boot/dts/nuvoton/nuvoton-common-npcm8xx.dtsi @@ -145,7 +145,6 @@ reg = <0x801c 0x4>; status = "disabled"; clocks = <&refclk>; - syscon = <&gcr>; }; watchdog1: watchdog@901c { @@ -154,7 +153,6 @@ reg = <0x901c 0x4>; status = "disabled"; clocks = <&refclk>; - syscon = <&gcr>; }; watchdog2: watchdog@a01c { @@ -163,7 +161,6 @@ reg = <0xa01c 0x4>; status = "disabled"; clocks = <&refclk>; - syscon = <&gcr>; }; }; }; diff --git a/arch/arm64/boot/dts/nvidia/Makefile b/arch/arm64/boot/dts/nvidia/Makefile index b139cbd14442..72c0cb5efa47 100644 --- a/arch/arm64/boot/dts/nvidia/Makefile +++ b/arch/arm64/boot/dts/nvidia/Makefile @@ -14,6 +14,7 @@ DTC_FLAGS_tegra234-p3740-0002+p3701-0008 := -@ DTC_FLAGS_tegra234-p3768-0000+p3767-0000 := -@ DTC_FLAGS_tegra234-p3768-0000+p3767-0005 := -@ DTC_FLAGS_tegra264-p3971-0089+p3834-0008 := -@ +DTC_FLAGS_tegra264-p4071-0000+p3834-0008 := -@ dtb-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra132-norrin.dtb dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2371-0000.dtb @@ -35,3 +36,4 @@ dtb-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra234-p3740-0002+p3701-0008.dtb dtb-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra234-p3768-0000+p3767-0000.dtb dtb-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra234-p3768-0000+p3767-0005.dtb dtb-$(CONFIG_ARCH_TEGRA_264_SOC) += tegra264-p3971-0089+p3834-0008.dtb +dtb-$(CONFIG_ARCH_TEGRA_264_SOC) += tegra264-p4071-0000+p3834-0008.dtb diff --git a/arch/arm64/boot/dts/nvidia/tegra210-smaug.dts b/arch/arm64/boot/dts/nvidia/tegra210-smaug.dts index b88428aa831e..f0b8c2c80aa5 100644 --- a/arch/arm64/boot/dts/nvidia/tegra210-smaug.dts +++ b/arch/arm64/boot/dts/nvidia/tegra210-smaug.dts @@ -1892,6 +1892,18 @@ }; }; + spi@70410000 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <104000000>; + spi-tx-bus-width = <2>; + spi-rx-bus-width = <2>; + }; + }; + clk32k_in: clock-32k { compatible = "fixed-clock"; clock-frequency = <32768>; diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi index 137aa8375257..5f5e5370d709 100644 --- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi @@ -309,9 +309,7 @@ reg = <0x0 0x54500000 0x0 0x00040000>; interrupts = ; clocks = <&tegra_car TEGRA210_CLK_TSECB>; - clock-names = "tsec"; resets = <&tegra_car 206>; - reset-names = "tsec"; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi b/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi index 58bf55c0e414..c10d041c183b 100644 --- a/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra234-p3701.dtsi @@ -9,6 +9,7 @@ mmc0 = "/bus@0/mmc@3460000"; mmc1 = "/bus@0/mmc@3400000"; rtc0 = "/bpmp/i2c/pmic@3c"; + rtc1 = "/bus@0/rtc@c2a0000"; }; bus@0 { diff --git a/arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi b/arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi index ab391a71c3d3..9e9e80d57623 100644 --- a/arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra234-p3767.dtsi @@ -8,6 +8,7 @@ aliases { mmc0 = "/bus@0/mmc@3400000"; rtc0 = "/bpmp/i2c/pmic@3c"; + rtc1 = "/bus@0/rtc@c2a0000"; }; bus@0 { diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi index 850c473235e3..04a95b6658ca 100644 --- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi @@ -3621,7 +3621,7 @@ snps,axi-config = <&mgbe0_axi_setup>; mgbe0_axi_setup: stmmac-axi-config { - snps,blen = <256 128 64 32>; + snps,blen = <256 128 64 32 0 0 0>; snps,rd_osr_lmt = <63>; snps,wr_osr_lmt = <63>; }; @@ -3663,7 +3663,7 @@ snps,axi-config = <&mgbe1_axi_setup>; mgbe1_axi_setup: stmmac-axi-config { - snps,blen = <256 128 64 32>; + snps,blen = <256 128 64 32 0 0 0>; snps,rd_osr_lmt = <63>; snps,wr_osr_lmt = <63>; }; @@ -3705,7 +3705,7 @@ snps,axi-config = <&mgbe2_axi_setup>; mgbe2_axi_setup: stmmac-axi-config { - snps,blen = <256 128 64 32>; + snps,blen = <256 128 64 32 0 0 0>; snps,rd_osr_lmt = <63>; snps,wr_osr_lmt = <63>; }; diff --git a/arch/arm64/boot/dts/nvidia/tegra264-p4071-0000+p3834-0008.dts b/arch/arm64/boot/dts/nvidia/tegra264-p4071-0000+p3834-0008.dts new file mode 100644 index 000000000000..df6555b6d0e0 --- /dev/null +++ b/arch/arm64/boot/dts/nvidia/tegra264-p4071-0000+p3834-0008.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +/dts-v1/; + +// module files must be included first +#include "tegra264-p3834-0008.dtsi" +#include "tegra264-p4071-0000+p3834.dtsi" + +/ { + model = "NVIDIA Jetson AGX Thor Developer Kit"; + compatible = "nvidia,p4071-0000+p3834-0008", "nvidia,p3834-0008", "nvidia,tegra264"; +}; diff --git a/arch/arm64/boot/dts/nvidia/tegra264-p4071-0000+p3834.dtsi b/arch/arm64/boot/dts/nvidia/tegra264-p4071-0000+p3834.dtsi new file mode 100644 index 000000000000..45f8df9bbfd6 --- /dev/null +++ b/arch/arm64/boot/dts/nvidia/tegra264-p4071-0000+p3834.dtsi @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + +/ { + aliases { + serial0 = &{/bus@0/serial@c4e0000}; + serial1 = &{/bus@0/serial@c5a0000}; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; +}; diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi index 7644a41d5f72..06d8357bdf52 100644 --- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi @@ -32,7 +32,7 @@ #address-cells = <2>; #size-cells = <2>; - ranges = <0x00 0x00000000 0x00 0x00000000 0x01 0x00000000>; + ranges = <0x00 0x00000000 0x00 0x00000000 0x00 0x20000000>; /* MMIO (512 MiB) */ misc@100000 { compatible = "nvidia,tegra234-misc"; @@ -3277,6 +3277,50 @@ status = "disabled"; }; + gpio_main: gpio@c300000 { + compatible = "nvidia,tegra264-gpio"; + reg = <0x00 0x0c300000 0x0 0x4000>, + <0x00 0x0c310000 0x0 0x4000>; + reg-names = "security", "gpio"; + wakeup-parent = <&pmc>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + serial@c4e0000 { compatible = "nvidia,tegra264-utc"; reg = <0x0 0x0c4e0000 0x0 0x8000>, @@ -3347,6 +3391,22 @@ #interrupt-cells = <2>; interrupt-controller; }; + + gpio_aon: gpio@cf00000 { + compatible = "nvidia,tegra264-gpio-aon"; + reg = <0x0 0x0cf00000 0x0 0x10000>, + <0x0 0x0cf10000 0x0 0x1000>; + reg-names = "security", "gpio"; + wakeup-parent = <&pmc>; + interrupts = , + , + , + ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; }; /* TOP_MMIO */ @@ -3356,9 +3416,10 @@ #address-cells = <2>; #size-cells = <2>; - ranges = <0x00 0x00000000 0x81 0x00000000 0x01 0x00000000>, /* MMIO */ - <0x01 0x00000000 0x00 0x20000000 0x00 0x40000000>, /* non-prefetchable memory (32-bit) */ - <0x02 0x00000000 0xd0 0x00000000 0x08 0x80000000>; /* ECAM, prefetchable memory, I/O */ + ranges = <0x00 0x00000000 0x81 0x00000000 0x00 0x20000000>, /* MMIO (512 MiB) */ + <0x00 0x20000000 0x00 0x20000000 0x00 0x20000000>, /* non-prefetchable memory (32-bit, 512 MiB) */ + <0x00 0x40000000 0x81 0x40000000 0x00 0x20000000>, /* MMIO (512 MiB) */ + <0xa8 0x80000000 0xa8 0x80000000 0x57 0x80000000>; /* I/O, ECAM, prefetchable memory (64-bit) */ smmu1: iommu@5000000 { compatible = "nvidia,tegra264-smmu", "arm,smmu-v3"; @@ -3402,23 +3463,23 @@ mc: memory-controller@8020000 { compatible = "nvidia,tegra264-mc"; - reg = <0x00 0x8020000 0x0 0x20000>, /* MC broadcast */ - <0x00 0x8040000 0x0 0x20000>, /* MC 0 */ - <0x00 0x8060000 0x0 0x20000>, /* MC 1 */ - <0x00 0x8080000 0x0 0x20000>, /* MC 2 */ - <0x00 0x80a0000 0x0 0x20000>, /* MC 3 */ - <0x00 0x80c0000 0x0 0x20000>, /* MC 4 */ - <0x00 0x80e0000 0x0 0x20000>, /* MC 5 */ - <0x00 0x8100000 0x0 0x20000>, /* MC 6 */ - <0x00 0x8120000 0x0 0x20000>, /* MC 7 */ - <0x00 0x8140000 0x0 0x20000>, /* MC 8 */ - <0x00 0x8160000 0x0 0x20000>, /* MC 9 */ - <0x00 0x8180000 0x0 0x20000>, /* MC 10 */ - <0x00 0x81a0000 0x0 0x20000>, /* MC 11 */ - <0x00 0x81c0000 0x0 0x20000>, /* MC 12 */ - <0x00 0x81e0000 0x0 0x20000>, /* MC 13 */ - <0x00 0x8200000 0x0 0x20000>, /* MC 14 */ - <0x00 0x8220000 0x0 0x20000>; /* MC 15 */ + reg = <0x000 0x8020000 0x0 0x20000>, /* MC broadcast */ + <0x000 0x8040000 0x0 0x20000>, /* MC 0 */ + <0x000 0x8060000 0x0 0x20000>, /* MC 1 */ + <0x000 0x8080000 0x0 0x20000>, /* MC 2 */ + <0x000 0x80a0000 0x0 0x20000>, /* MC 3 */ + <0x000 0x80c0000 0x0 0x20000>, /* MC 4 */ + <0x000 0x80e0000 0x0 0x20000>, /* MC 5 */ + <0x000 0x8100000 0x0 0x20000>, /* MC 6 */ + <0x000 0x8120000 0x0 0x20000>, /* MC 7 */ + <0x000 0x8140000 0x0 0x20000>, /* MC 8 */ + <0x000 0x8160000 0x0 0x20000>, /* MC 9 */ + <0x000 0x8180000 0x0 0x20000>, /* MC 10 */ + <0x000 0x81a0000 0x0 0x20000>, /* MC 11 */ + <0x000 0x81c0000 0x0 0x20000>, /* MC 12 */ + <0x000 0x81e0000 0x0 0x20000>, /* MC 13 */ + <0x000 0x8200000 0x0 0x20000>, /* MC 14 */ + <0x000 0x8220000 0x0 0x20000>; /* MC 15 */ reg-names = "broadcast", "ch0", "ch1", "ch2", "ch3", "ch4", "ch5", "ch6", "ch7", "ch8", "ch9", "ch10", "ch11", "ch12", "ch13", "ch14", @@ -3437,12 +3498,12 @@ #size-cells = <2>; /* limit the DMA range for memory clients to [39:0] */ - dma-ranges = <0x0 0x0 0x0 0x0 0x100 0x0>; + dma-ranges = <0x000 0x0 0x000 0x0 0x100 0x0>; emc: external-memory-controller@8800000 { compatible = "nvidia,tegra264-emc"; - reg = <0x00 0x8800000 0x0 0x20000>, - <0x00 0x8890000 0x0 0x20000>; + reg = <0x000 0x8800000 0x0 0x20000>, + <0x000 0x8890000 0x0 0x20000>; interrupts = ; clocks = <&bpmp TEGRA264_CLK_EMC>, <&bpmp TEGRA264_CLK_DBB_UPHY0>; @@ -3493,6 +3554,38 @@ status = "disabled"; }; + pci@c000000 { + compatible = "nvidia,tegra264-pcie"; + reg = <0xd0 0xb0000000 0x0 0x10000000>, + <0x00 0x0c000000 0x0 0x00004000>, + <0x00 0x0c004000 0x0 0x00001000>, + <0x00 0x0c005000 0x0 0x00001000>; + reg-names = "ecam", "xal", "xtl", "xtl-pri"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + linux,pci-domain = <0x00>; + #interrupt-cells = <0x1>; + + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0x0 0x0 155 IRQ_TYPE_LEVEL_HIGH>, + <0x0 0x0 0x0 0x2 &gic 0x0 0x0 0x0 156 IRQ_TYPE_LEVEL_HIGH>, + <0x0 0x0 0x0 0x3 &gic 0x0 0x0 0x0 157 IRQ_TYPE_LEVEL_HIGH>, + <0x0 0x0 0x0 0x4 &gic 0x0 0x0 0x0 158 IRQ_TYPE_LEVEL_HIGH>; + + iommu-map = <0x0 &smmu2 0x10000 0x10000>; + msi-map = <0x0 &its 0x210000 0x10000>; + dma-coherent; + + ranges = <0x81000000 0x00 0x84000000 0xd0 0x84000000 0x00 0x00200000>, /* I/O */ + <0x82000000 0x00 0x20000000 0x00 0x20000000 0x00 0x08000000>, /* non-prefetchable memory (128 MiB) */ + <0xc3000000 0xd0 0xc0000000 0xd0 0xc0000000 0x07 0xc0000000>; /* prefetchable memory */ + bus-range = <0x0 0xff>; + + nvidia,bpmp = <&bpmp 0>; + status = "disabled"; + }; + i2c14: i2c@c410000 { compatible = "nvidia,tegra264-i2c"; reg = <0x00 0x0c410000 0x0 0x10000>; @@ -3720,7 +3813,7 @@ #address-cells = <2>; #size-cells = <2>; - ranges = <0x00 0x00000000 0x88 0x00000000 0x01 0x00000000>; + ranges = <0x00 0x00000000 0x88 0x00000000 0x00 0x20000000>; /* MMIO (512 MiB) */ smmu3: iommu@6000000 { compatible = "nvidia,tegra264-smmu", "arm,smmu-v3"; @@ -3765,8 +3858,197 @@ #address-cells = <2>; #size-cells = <2>; - ranges = <0x00 0x00000000 0xa8 0x00000000 0x40 0x00000000>, /* MMIO, ECAM, prefetchable memory, I/O */ - <0x80 0x00000000 0x00 0x20000000 0x00 0x40000000>; /* non-prefetchable memory (32-bit) */ + ranges = <0x00 0x00000000 0xa8 0x00000000 0x00 0x20000000>, /* MMIO (512 MiB) */ + <0x00 0x20000000 0x00 0x20000000 0x00 0x60000000>, /* non-prefetchable memory (32-bit, 1536 GiB) */ + <0xa8 0x80000000 0xa8 0x80000000 0x57 0x80000000>; /* I/O, ECAM, prefetchable memory (64-bit) */ + + gpio_uphy: gpio@8300000 { + compatible = "nvidia,tegra264-gpio-uphy"; + reg = <0x00 0x08300000 0x0 0x2000>, + <0x00 0x08310000 0x0 0x2000>; + reg-names = "security", "gpio"; + wakeup-parent = <&pmc>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + pci@8400000 { + compatible = "nvidia,tegra264-pcie"; + reg = <0xa8 0xb0000000 0x0 0x10000000>, + <0x00 0x08400000 0x0 0x00004000>, + <0x00 0x08404000 0x0 0x00001000>, + <0x00 0x08405000 0x0 0x00001000>, + <0x00 0x08410000 0x0 0x00010000>; + reg-names = "ecam", "xal", "xtl", "xtl-pri", "xpl"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + linux,pci-domain = <0x01>; + #interrupt-cells = <1>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0x0 0x0 908 IRQ_TYPE_LEVEL_HIGH>, /* INTA */ + <0x0 0x0 0x0 0x2 &gic 0x0 0x0 0x0 909 IRQ_TYPE_LEVEL_HIGH>, /* INTB */ + <0x0 0x0 0x0 0x3 &gic 0x0 0x0 0x0 910 IRQ_TYPE_LEVEL_HIGH>, /* INTC */ + <0x0 0x0 0x0 0x4 &gic 0x0 0x0 0x0 911 IRQ_TYPE_LEVEL_HIGH>; /* INTD */ + + iommu-map = <0x0 &smmu1 0x10000 0x10000>; + msi-map = <0x0 &its 0x110000 0x10000>; + dma-coherent; + + ranges = <0x81000000 0x00 0x84000000 0xa8 0x84000000 0x00 0x00200000>, /* I/O */ + <0x82000000 0x00 0x28000000 0x00 0x28000000 0x00 0x08000000>, /* non-prefetchable memory */ + <0xc3000000 0xa8 0xc0000000 0xa8 0xc0000000 0x07 0xc0000000>; /* prefetchable memory */ + bus-range = <0x00 0xff>; + + nvidia,bpmp = <&bpmp 1>; + status = "disabled"; + }; + + pci@8420000 { + compatible = "nvidia,tegra264-pcie"; + reg = <0xb0 0xb0000000 0x0 0x10000000>, + <0x00 0x08420000 0x0 0x00004000>, + <0x00 0x08424000 0x0 0x00001000>, + <0x00 0x08425000 0x0 0x00001000>, + <0x00 0x08430000 0x0 0x00010000>; + reg-names = "ecam", "xal", "xtl", "xtl-pri", "xpl"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + linux,pci-domain = <0x02>; + #interrupt-cells = <1>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0x0 0x0 917 IRQ_TYPE_LEVEL_HIGH>, /* INTA */ + <0x0 0x0 0x0 0x2 &gic 0x0 0x0 0x0 918 IRQ_TYPE_LEVEL_HIGH>, /* INTB */ + <0x0 0x0 0x0 0x3 &gic 0x0 0x0 0x0 919 IRQ_TYPE_LEVEL_HIGH>, /* INTC */ + <0x0 0x0 0x0 0x4 &gic 0x0 0x0 0x0 920 IRQ_TYPE_LEVEL_HIGH>; /* INTD */ + + iommu-map = <0x0 &smmu1 0x20000 0x10000>; + msi-map = <0x0 &its 0x120000 0x10000>; + dma-coherent; + + ranges = <0x81000000 0x00 0x84000000 0xb0 0x84000000 0x00 0x00200000>, /* I/O */ + <0x82000000 0x00 0x30000000 0x00 0x30000000 0x00 0x08000000>, /* non-prefetchable memory */ + <0xc3000000 0xb0 0xc0000000 0xb0 0xc0000000 0x07 0xc0000000>; /* prefetchable memory */ + bus-range = <0x00 0xff>; + + nvidia,bpmp = <&bpmp 2>; + status = "disabled"; + }; + + pci@8440000 { + compatible = "nvidia,tegra264-pcie"; + reg = <0xb8 0xb0000000 0x0 0x10000000>, + <0x00 0x08440000 0x0 0x00004000>, + <0x00 0x08444000 0x0 0x00001000>, + <0x00 0x08445000 0x0 0x00001000>, + <0x00 0x08450000 0x0 0x00010000>; + reg-names = "ecam", "xal", "xtl", "xtl-pri", "xpl"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + linux,pci-domain = <0x03>; + #interrupt-cells = <1>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0x0 0x0 926 IRQ_TYPE_LEVEL_HIGH>, /* INTA */ + <0x0 0x0 0x0 0x2 &gic 0x0 0x0 0x0 927 IRQ_TYPE_LEVEL_HIGH>, /* INTB */ + <0x0 0x0 0x0 0x3 &gic 0x0 0x0 0x0 928 IRQ_TYPE_LEVEL_HIGH>, /* INTC */ + <0x0 0x0 0x0 0x4 &gic 0x0 0x0 0x0 929 IRQ_TYPE_LEVEL_HIGH>; /* INTD */ + + iommu-map = <0x0 &smmu1 0x30000 0x10000>; + msi-map = <0x0 &its 0x130000 0x10000>; + dma-coherent; + + ranges = <0x81000000 0x00 0x84000000 0xb8 0x84000000 0x00 0x00200000>, /* I/O */ + <0x82000000 0x00 0x38000000 0x00 0x38000000 0x00 0x08000000>, /* non-prefetchable memory */ + <0xc3000000 0xb8 0xc0000000 0xb8 0xc0000000 0x07 0xc0000000>; /* prefetchable memory */ + bus-range = <0x00 0xff>; + + nvidia,bpmp = <&bpmp 3>; + status = "disabled"; + }; + + pci@8460000 { + compatible = "nvidia,tegra264-pcie"; + reg = <0xc0 0xb0000000 0x0 0x10000000>, + <0x00 0x08460000 0x0 0x00004000>, + <0x00 0x08464000 0x0 0x00001000>, + <0x00 0x08465000 0x0 0x00001000>, + <0x00 0x08470000 0x0 0x00010000>; + reg-names = "ecam", "xal", "xtl", "xtl-pri", "xpl"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + linux,pci-domain = <0x04>; + #interrupt-cells = <1>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0x0 0x0 935 IRQ_TYPE_LEVEL_HIGH>, /* INTA */ + <0x0 0x0 0x0 0x2 &gic 0x0 0x0 0x0 936 IRQ_TYPE_LEVEL_HIGH>, /* INTB */ + <0x0 0x0 0x0 0x3 &gic 0x0 0x0 0x0 937 IRQ_TYPE_LEVEL_HIGH>, /* INTC */ + <0x0 0x0 0x0 0x4 &gic 0x0 0x0 0x0 938 IRQ_TYPE_LEVEL_HIGH>; /* INTD */ + + iommu-map = <0x0 &smmu1 0x40000 0x10000>; + msi-map = <0x0 &its 0x140000 0x10000>; + dma-coherent; + + ranges = <0x81000000 0x00 0x84000000 0xc0 0x84000000 0x00 0x00200000>, /* I/O */ + <0x82000000 0x00 0x40000000 0x00 0x40000000 0x00 0x08000000>, /* non-prefetchable memory */ + <0xc3000000 0xc0 0xc0000000 0xc0 0xc0000000 0x07 0xc0000000>; /* prefetchable memory */ + bus-range = <0x00 0xff>; + + nvidia,bpmp = <&bpmp 4>; + status = "disabled"; + }; + + pci@8480000 { + compatible = "nvidia,tegra264-pcie"; + reg = <0xc8 0xb0000000 0x0 0x10000000>, + <0x00 0x08480000 0x0 0x00004000>, + <0x00 0x08484000 0x0 0x00001000>, + <0x00 0x08485000 0x0 0x00001000>, + <0x00 0x08490000 0x0 0x00010000>; + reg-names = "ecam", "xal", "xtl", "xtl-pri", "xpl"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + linux,pci-domain = <0x05>; + #interrupt-cells = <1>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0x0 0x0 944 IRQ_TYPE_LEVEL_HIGH>, /* INTA */ + <0x0 0x0 0x0 0x2 &gic 0x0 0x0 0x0 945 IRQ_TYPE_LEVEL_HIGH>, /* INTB */ + <0x0 0x0 0x0 0x3 &gic 0x0 0x0 0x0 946 IRQ_TYPE_LEVEL_HIGH>, /* INTC */ + <0x0 0x0 0x0 0x4 &gic 0x0 0x0 0x0 947 IRQ_TYPE_LEVEL_HIGH>; /* INTD */ + + iommu-map = <0x0 &smmu1 0x50000 0x10000>; + msi-map = <0x0 &its 0x150000 0x10000>; + dma-coherent; + + ranges = <0x81000000 0x00 0x84000000 0xc8 0x84000000 0x00 0x00200000>, /* I/O */ + <0x82000000 0x00 0x48000000 0x00 0x48000000 0x00 0x08000000>, /* non-prefetchable memory */ + <0xc3000000 0xc8 0xc0000000 0xc8 0xc0000000 0x07 0xc0000000>; /* prefetchable memory */ + bus-range = <0x00 0xff>; + + nvidia,bpmp = <&bpmp 5>; + status = "disabled"; + }; }; cpus { diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index f80b5d9cf1e8..4ba8e7306419 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -12,10 +12,19 @@ dtb-$(CONFIG_ARCH_QCOM) += apq8016-schneider-hmibsc.dtb dtb-$(CONFIG_ARCH_QCOM) += apq8039-t2.dtb dtb-$(CONFIG_ARCH_QCOM) += apq8094-sony-xperia-kitakami-karin_windy.dtb dtb-$(CONFIG_ARCH_QCOM) += apq8096-db820c.dtb +dtb-$(CONFIG_ARCH_QCOM) += apq8096sg-db820c.dtb dtb-$(CONFIG_ARCH_QCOM) += apq8096-ifc6640.dtb +dtb-$(CONFIG_ARCH_QCOM) += eliza-mtp.dtb +dtb-$(CONFIG_ARCH_QCOM) += glymur-crd.dtb dtb-$(CONFIG_ARCH_QCOM) += hamoa-iot-evk.dtb + +hamoa-iot-evk-el2-dtbs := hamoa-iot-evk.dtb x1-el2.dtbo + +dtb-$(CONFIG_ARCH_QCOM) += hamoa-iot-evk-el2.dtb +dtb-$(CONFIG_ARCH_QCOM) += hamoa-lenovo-ideacentre-mini-01q8x10.dtb dtb-$(CONFIG_ARCH_QCOM) += ipq5018-rdp432-c2.dtb dtb-$(CONFIG_ARCH_QCOM) += ipq5018-tplink-archer-ax55-v1.dtb +dtb-$(CONFIG_ARCH_QCOM) += ipq5210-rdp504.dtb dtb-$(CONFIG_ARCH_QCOM) += ipq5332-rdp441.dtb dtb-$(CONFIG_ARCH_QCOM) += ipq5332-rdp442.dtb dtb-$(CONFIG_ARCH_QCOM) += ipq5332-rdp468.dtb @@ -25,8 +34,8 @@ dtb-$(CONFIG_ARCH_QCOM) += ipq6018-cp01-c1.dtb dtb-$(CONFIG_ARCH_QCOM) += ipq8074-hk01.dtb dtb-$(CONFIG_ARCH_QCOM) += ipq8074-hk10-c1.dtb dtb-$(CONFIG_ARCH_QCOM) += ipq8074-hk10-c2.dtb -dtb-$(CONFIG_ARCH_QCOM) += ipq9574-rdp418.dtb -dtb-$(CONFIG_ARCH_QCOM) += ipq9574-rdp433.dtb +dtb-$(CONFIG_ARCH_QCOM) += ipq9574-rdp418.dtb ipq9574-rdp418-emmc.dtb +dtb-$(CONFIG_ARCH_QCOM) += ipq9574-rdp433.dtb ipq9574-rdp433-emmc.dtb dtb-$(CONFIG_ARCH_QCOM) += ipq9574-rdp449.dtb dtb-$(CONFIG_ARCH_QCOM) += ipq9574-rdp453.dtb dtb-$(CONFIG_ARCH_QCOM) += ipq9574-rdp454.dtb @@ -43,8 +52,21 @@ dtb-$(CONFIG_ARCH_QCOM) += lemans-evk-camera.dtb lemans-evk-el2-dtbs := lemans-evk.dtb lemans-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += lemans-evk-el2.dtb +lemans-evk-ifp-mezzanine-dtbs := lemans-evk.dtb lemans-evk-ifp-mezzanine.dtbo +dtb-$(CONFIG_ARCH_QCOM) += lemans-evk-ifp-mezzanine.dtb +dtb-$(CONFIG_ARCH_QCOM) += mahua-crd.dtb dtb-$(CONFIG_ARCH_QCOM) += milos-fairphone-fp6.dtb +dtb-$(CONFIG_ARCH_QCOM) += monaco-arduino-monza.dtb dtb-$(CONFIG_ARCH_QCOM) += monaco-evk.dtb + +monaco-evk-camera-imx577-dtbs := monaco-evk.dtb monaco-evk-camera-imx577.dtbo +dtb-$(CONFIG_ARCH_QCOM) += monaco-evk-camera-imx577.dtb + +monaco-evk-el2-dtbs := monaco-evk.dtb monaco-el2.dtbo + +dtb-$(CONFIG_ARCH_QCOM) += monaco-evk-el2.dtb +monaco-evk-ifp-mezzanine-dtbs := monaco-evk.dtb monaco-evk-ifp-mezzanine.dtbo +dtb-$(CONFIG_ARCH_QCOM) += monaco-evk-ifp-mezzanine.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8216-samsung-fortuna3g.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-acer-a1-724.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-alcatel-idol347.dtb @@ -61,6 +83,7 @@ dtb-$(CONFIG_ARCH_QCOM) += msm8916-motorola-surnia.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-mtp.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-a3u-eur.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-a5u-eur.dtb +dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-coreprimeltevzw.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-e5.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-e7.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-gprimeltecan.dtb @@ -75,11 +98,14 @@ dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-rossa.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-samsung-serranove.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-thwc-uf896.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-thwc-ufi001c.dtb +dtb-$(CONFIG_ARCH_QCOM) += msm8916-wiko-chuppito.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-wingtech-wt86518.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-wingtech-wt86528.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-wingtech-wt88047.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8916-yiming-uz801v3.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8917-xiaomi-riva.dtb +dtb-$(CONFIG_ARCH_QCOM) += msm8917-xiaomi-rolex.dtb +dtb-$(CONFIG_ARCH_QCOM) += msm8917-xiaomi-tiare.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8929-wingtech-wt82918hd.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8937-xiaomi-land.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8939-asus-z00t.dtb @@ -130,6 +156,7 @@ dtb-$(CONFIG_ARCH_QCOM) += msm8998-sony-xperia-yoshino-lilac.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8998-sony-xperia-yoshino-maple.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8998-sony-xperia-yoshino-poplar.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8998-xiaomi-sagit.dtb +dtb-$(CONFIG_ARCH_QCOM) += purwa-iot-evk.dtb dtb-$(CONFIG_ARCH_QCOM) += qcm6490-fairphone-fp5.dtb dtb-$(CONFIG_ARCH_QCOM) += qcm6490-idp.dtb dtb-$(CONFIG_ARCH_QCOM) += qcm6490-particle-tachyon.dtb @@ -137,6 +164,10 @@ dtb-$(CONFIG_ARCH_QCOM) += qcm6490-shift-otter.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs404-evb-1000.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs404-evb-4000.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs615-ride.dtb + +qcs615-ride-el2-dtbs := qcs615-ride.dtb talos-el2.dtbo + +dtb-$(CONFIG_ARCH_QCOM) += qcs615-ride-el2.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs6490-radxa-dragon-q6a.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs6490-rb3gen2.dtb @@ -145,8 +176,13 @@ qcs6490-rb3gen2-industrial-mezzanine-dtbs := qcs6490-rb3gen2.dtb qcs6490-rb3gen2 dtb-$(CONFIG_ARCH_QCOM) += qcs6490-rb3gen2-industrial-mezzanine.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs6490-rb3gen2-vision-mezzanine.dtb +dtb-$(CONFIG_ARCH_QCOM) += qcs6490-thundercomm-minipc-g1iot.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs6490-thundercomm-rubikpi3.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs8300-ride.dtb + +qcs8300-ride-el2-dtbs := qcs8300-ride.dtb monaco-el2.dtbo + +dtb-$(CONFIG_ARCH_QCOM) += qcs8300-ride-el2.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs8550-aim300-aiot.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs9100-ride.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs9100-ride-r3.dtb @@ -178,6 +214,8 @@ dtb-$(CONFIG_ARCH_QCOM) += sa8775p-ride.dtb dtb-$(CONFIG_ARCH_QCOM) += sa8775p-ride-r3.dtb sc7180-acer-aspire1-el2-dtbs := sc7180-acer-aspire1.dtb sc7180-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += sc7180-acer-aspire1.dtb sc7180-acer-aspire1-el2.dtb +sc7180-ecs-liva-qc710-el2-dtbs := sc7180-ecs-liva-qc710.dtb sc7180-el2.dtbo +dtb-$(CONFIG_ARCH_QCOM) += sc7180-ecs-liva-qc710.dtb sc7180-ecs-liva-qc710-el2.dtb dtb-$(CONFIG_ARCH_QCOM) += sc7180-idp.dtb dtb-$(CONFIG_ARCH_QCOM) += sc7180-trogdor-coachz-r1.dtb dtb-$(CONFIG_ARCH_QCOM) += sc7180-trogdor-coachz-r1-lte.dtb @@ -294,8 +332,9 @@ dtb-$(CONFIG_ARCH_QCOM) += sm4450-qrd.dtb dtb-$(CONFIG_ARCH_QCOM) += sm6115-fxtec-pro1x.dtb dtb-$(CONFIG_ARCH_QCOM) += sm6115p-lenovo-j606f.dtb dtb-$(CONFIG_ARCH_QCOM) += sm6125-sony-xperia-seine-pdx201.dtb -dtb-$(CONFIG_ARCH_QCOM) += sm6125-xiaomi-ginkgo.dtb +dtb-$(CONFIG_ARCH_QCOM) += sm6125-xiaomi-ginkgo.dtb dtb-$(CONFIG_ARCH_QCOM) += sm6125-xiaomi-laurel-sprout.dtb +dtb-$(CONFIG_ARCH_QCOM) += sm6125-xiaomi-willow.dtb dtb-$(CONFIG_ARCH_QCOM) += sm6350-sony-xperia-lena-pdx213.dtb dtb-$(CONFIG_ARCH_QCOM) += sm6375-sony-xperia-murray-pdx225.dtb dtb-$(CONFIG_ARCH_QCOM) += sm7125-xiaomi-curtana.dtb @@ -327,14 +366,19 @@ dtb-$(CONFIG_ARCH_QCOM) += sm8450-samsung-r0q.dtb dtb-$(CONFIG_ARCH_QCOM) += sm8450-sony-xperia-nagara-pdx223.dtb dtb-$(CONFIG_ARCH_QCOM) += sm8450-sony-xperia-nagara-pdx224.dtb +sm8550-hdk-display-card-dtbs := sm8550-hdk.dtb sm8550-hdk-display-card.dtbo +sm8550-hdk-display-card-rear-camera-card-dtbs := sm8550-hdk.dtb sm8550-hdk-display-card.dtbo sm8550-hdk-rear-camera-card.dtbo sm8550-hdk-rear-camera-card-dtbs := sm8550-hdk.dtb sm8550-hdk-rear-camera-card.dtbo +dtb-$(CONFIG_ARCH_QCOM) += sm8550-hdk-display-card-rear-camera-card.dtb +dtb-$(CONFIG_ARCH_QCOM) += sm8550-hdk-display-card.dtb dtb-$(CONFIG_ARCH_QCOM) += sm8550-hdk-rear-camera-card.dtb dtb-$(CONFIG_ARCH_QCOM) += sm8550-hdk.dtb dtb-$(CONFIG_ARCH_QCOM) += sm8550-mtp.dtb dtb-$(CONFIG_ARCH_QCOM) += sm8550-qrd.dtb dtb-$(CONFIG_ARCH_QCOM) += sm8550-samsung-q5q.dtb dtb-$(CONFIG_ARCH_QCOM) += sm8550-sony-xperia-yodo-pdx234.dtb +dtb-$(CONFIG_ARCH_QCOM) += sm8650-ayaneo-pocket-s2.dtb sm8650-hdk-display-card-dtbs := sm8650-hdk.dtb sm8650-hdk-display-card.dtbo sm8650-hdk-display-card-rear-camera-card-dtbs := sm8650-hdk.dtb sm8650-hdk-display-card.dtbo sm8650-hdk-rear-camera-card.dtbo @@ -348,6 +392,15 @@ dtb-$(CONFIG_ARCH_QCOM) += sm8650-mtp.dtb dtb-$(CONFIG_ARCH_QCOM) += sm8650-qrd.dtb dtb-$(CONFIG_ARCH_QCOM) += sm8750-mtp.dtb dtb-$(CONFIG_ARCH_QCOM) += sm8750-qrd.dtb +dtb-$(CONFIG_ARCH_QCOM) += talos-evk.dtb +talos-evk-usb1-peripheral-dtbs := talos-evk.dtb talos-evk-usb1-peripheral.dtbo +dtb-$(CONFIG_ARCH_QCOM) += talos-evk-usb1-peripheral.dtb +dtb-$(CONFIG_ARCH_QCOM) += talos-evk-camera-imx577.dtbo +talos-evk-camera-imx577-dtbs := talos-evk.dtb talos-evk-camera-imx577.dtbo +dtb-$(CONFIG_ARCH_QCOM) += talos-evk-camera-imx577.dtb +talos-evk-lvds-auo,g133han01-dtbs := \ + talos-evk.dtb talos-evk-lvds-auo,g133han01.dtbo +dtb-$(CONFIG_ARCH_QCOM) += talos-evk-lvds-auo,g133han01.dtb x1e001de-devkit-el2-dtbs := x1e001de-devkit.dtb x1-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += x1e001de-devkit.dtb x1e001de-devkit-el2.dtb x1e78100-lenovo-thinkpad-t14s-el2-dtbs := x1e78100-lenovo-thinkpad-t14s.dtb x1-el2.dtbo @@ -374,12 +427,16 @@ x1e80100-lenovo-yoga-slim7x-el2-dtbs := x1e80100-lenovo-yoga-slim7x.dtb x1-el2.d dtb-$(CONFIG_ARCH_QCOM) += x1e80100-lenovo-yoga-slim7x.dtb x1e80100-lenovo-yoga-slim7x-el2.dtb x1e80100-medion-sprchrgd-14-s1-el2-dtbs := x1e80100-medion-sprchrgd-14-s1.dtb x1-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += x1e80100-medion-sprchrgd-14-s1.dtb x1e80100-medion-sprchrgd-14-s1-el2.dtb +x1e80100-microsoft-denali-oled-el2-dtbs := x1e80100-microsoft-denali-oled.dtb x1-el2.dtbo +dtb-$(CONFIG_ARCH_QCOM) += x1e80100-microsoft-denali-oled.dtb x1e80100-microsoft-denali-oled-el2.dtb x1e80100-microsoft-romulus13-el2-dtbs := x1e80100-microsoft-romulus13.dtb x1-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += x1e80100-microsoft-romulus13.dtb x1e80100-microsoft-romulus13-el2.dtb x1e80100-microsoft-romulus15-el2-dtbs := x1e80100-microsoft-romulus15.dtb x1-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += x1e80100-microsoft-romulus15.dtb x1e80100-microsoft-romulus15-el2.dtb x1e80100-qcp-el2-dtbs := x1e80100-qcp.dtb x1-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += x1e80100-qcp.dtb x1e80100-qcp-el2.dtb +x1p42100-asus-vivobook-s15-el2-dtbs := x1p42100-asus-vivobook-s15.dtb x1-el2.dtbo +dtb-$(CONFIG_ARCH_QCOM) += x1p42100-asus-vivobook-s15.dtb x1p42100-asus-vivobook-s15-el2.dtb x1p42100-asus-zenbook-a14-el2-dtbs := x1p42100-asus-zenbook-a14.dtb x1-el2.dtbo dtb-$(CONFIG_ARCH_QCOM) += x1p42100-asus-zenbook-a14.dtb x1p42100-asus-zenbook-a14-el2.dtb x1p42100-asus-zenbook-a14-lcd-el2-dtbs := x1p42100-asus-zenbook-a14-lcd.dtb x1-el2.dtbo diff --git a/arch/arm64/boot/dts/qcom/agatti.dtsi b/arch/arm64/boot/dts/qcom/agatti.dtsi index 893cb0689013..8a7337239b1e 100644 --- a/arch/arm64/boot/dts/qcom/agatti.dtsi +++ b/arch/arm64/boot/dts/qcom/agatti.dtsi @@ -2834,9 +2834,9 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; }; diff --git a/arch/arm64/boot/dts/qcom/apq8096-db820c.dts b/arch/arm64/boot/dts/qcom/apq8096-db820c.dts index 9fa70ff6887b..47b4568e4039 100644 --- a/arch/arm64/boot/dts/qcom/apq8096-db820c.dts +++ b/arch/arm64/boot/dts/qcom/apq8096-db820c.dts @@ -6,1133 +6,9 @@ /dts-v1/; #include "msm8996.dtsi" -#include "pm8994.dtsi" -#include "pmi8994.dtsi" -#include -#include -#include -#include -#include -#include -#include - -/* - * GPIO name legend: proper name = the GPIO line is used as GPIO - * NC = not connected (pin out but not routed from the chip to - * anything the board) - * "[PER]" = pin is muxed for [peripheral] (not GPIO) - * LSEC = Low Speed External Connector - * P HSEC = Primary High Speed External Connector - * S HSEC = Secondary High Speed External Connector - * J14 = Camera Connector - * TP = Test Points - * - * Line names are taken from the schematic "DragonBoard 820c", - * drawing no: LM25-P2751-1 - * - * For the lines routed to the external connectors the - * lines are named after the 96Boards CE Specification 1.0, - * Appendix "Expansion Connector Signal Description". - * - * When the 96Board naming of a line and the schematic name of - * the same line are in conflict, the 96Board specification - * takes precedence, which means that the external UART on the - * LSEC is named UART0 while the schematic and SoC names this - * UART3. This is only for the informational lines i.e. "[FOO]", - * the GPIO named lines "GPIO-A" thru "GPIO-L" are the only - * ones actually used for GPIO. - */ +#include "apq8096-db820c.dtsi" / { model = "Qualcomm Technologies, Inc. DB820c"; compatible = "arrow,apq8096-db820c", "qcom,apq8096-sbc", "qcom,apq8096"; - - aliases { - serial0 = &blsp2_uart2; - serial1 = &blsp2_uart3; - serial2 = &blsp1_uart2; - i2c0 = &blsp1_i2c3; - i2c1 = &blsp2_i2c1; - i2c2 = &blsp2_i2c1; - spi0 = &blsp1_spi1; - spi1 = &blsp2_spi6; - }; - - chosen { - stdout-path = "serial0:115200n8"; - }; - - div1_mclk: divclk1 { - compatible = "gpio-gate-clock"; - pinctrl-0 = <&audio_mclk>; - pinctrl-names = "default"; - clocks = <&rpmcc RPM_SMD_DIV_CLK1>; - #clock-cells = <0>; - enable-gpios = <&pm8994_gpios 15 0>; - }; - - divclk4: divclk4 { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <32768>; - clock-output-names = "divclk4"; - - pinctrl-names = "default"; - pinctrl-0 = <&divclk4_pin_a>; - }; - - gpio-keys { - compatible = "gpio-keys"; - autorepeat; - - pinctrl-names = "default"; - pinctrl-0 = <&volume_up_gpio>; - - button { - label = "Volume Up"; - linux,code = ; - gpios = <&pm8994_gpios 2 GPIO_ACTIVE_LOW>; - }; - }; - - usb2_id: usb2-id { - compatible = "linux,extcon-usb-gpio"; - id-gpios = <&pmi8994_gpios 6 GPIO_ACTIVE_HIGH>; - pinctrl-names = "default"; - pinctrl-0 = <&usb2_vbus_det_gpio>; - }; - - usb3_id: usb3-id { - compatible = "linux,extcon-usb-gpio"; - id-gpios = <&pm8994_gpios 22 GPIO_ACTIVE_HIGH>; - pinctrl-names = "default"; - pinctrl-0 = <&usb3_vbus_det_gpio>; - }; - - vph_pwr: vph-pwr-regulator { - compatible = "regulator-fixed"; - regulator-name = "vph_pwr"; - regulator-always-on; - regulator-boot-on; - - regulator-min-microvolt = <3700000>; - regulator-max-microvolt = <3700000>; - }; - - wlan_en: wlan-en-1-8v { - pinctrl-names = "default"; - pinctrl-0 = <&wlan_en_gpios>; - compatible = "regulator-fixed"; - regulator-name = "wlan-en-regulator"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - - gpio = <&pm8994_gpios 8 0>; - - /* WLAN card specific delay */ - startup-delay-us = <70000>; - enable-active-high; - }; -}; - -&blsp1_i2c3 { - /* On Low speed expansion: LS-I2C0 */ - status = "okay"; -}; - -&blsp1_spi1 { - /* On Low speed expansion */ - status = "okay"; -}; - -&blsp1_uart2 { - label = "BT-UART"; - status = "okay"; - - bluetooth { - compatible = "qcom,qca6174-bt"; - - /* bt_disable_n gpio */ - enable-gpios = <&pm8994_gpios 19 GPIO_ACTIVE_HIGH>; - - clocks = <&divclk4>; - }; -}; - -&adsp_pil { - status = "okay"; - firmware-name = "qcom/apq8096/adsp.mbn"; -}; - -&blsp2_i2c1 { - /* On High speed expansion: HS-I2C2 */ - status = "okay"; -}; - -&blsp2_i2c1 { - /* On Low speed expansion: LS-I2C1 */ - status = "okay"; -}; - -&blsp2_spi6 { - /* On High speed expansion */ - status = "okay"; -}; - -&blsp2_uart2 { - label = "LS-UART1"; - status = "okay"; - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&blsp2_uart2_2pins_default>; - pinctrl-1 = <&blsp2_uart2_2pins_sleep>; -}; - -&blsp2_uart3 { - label = "LS-UART0"; - status = "disabled"; - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&blsp2_uart3_4pins_default>; - pinctrl-1 = <&blsp2_uart3_4pins_sleep>; -}; - -&camss { - vdda-supply = <&vreg_l2a_1p25>; -}; - -&gpu { - status = "okay"; -}; - -&gpu_zap_shader { - firmware-name = "qcom/apq8096/a530_zap.mbn"; -}; - -&hsusb_phy1 { - status = "okay"; - - vdd-supply = <&vreg_l28a_0p925>; - vdda-pll-supply = <&vreg_l12a_1p8>; - vdda-phy-dpdm-supply = <&vreg_l24a_3p075>; -}; - -&hsusb_phy2 { - status = "okay"; - - vdd-supply = <&vreg_l28a_0p925>; - vdda-pll-supply = <&vreg_l12a_1p8>; - vdda-phy-dpdm-supply = <&vreg_l24a_3p075>; -}; - -&mdp { - status = "okay"; -}; - -&mdss { - status = "okay"; -}; - -&mdss_hdmi { - status = "okay"; - - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&hdmi_hpd_active &hdmi_ddc_active>; - pinctrl-1 = <&hdmi_hpd_suspend &hdmi_ddc_suspend>; - - core-vdda-supply = <&vreg_l12a_1p8>; - core-vcc-supply = <&vreg_s4a_1p8>; -}; - -&mdss_hdmi_phy { - status = "okay"; - - vddio-supply = <&vreg_l12a_1p8>; - vcca-supply = <&vreg_l28a_0p925>; - #phy-cells = <0>; -}; - -&mmcc { - vdd-gfx-supply = <&vdd_gfx>; -}; - -&mss_pil { - status = "okay"; - pll-supply = <&vreg_l12a_1p8>; - firmware-name = "qcom/apq8096/mba.mbn", "qcom/apq8096/modem.mbn"; -}; - -&pm8994_resin { - status = "okay"; - linux,code = ; -}; - -&tlmm { - gpio-line-names = - "[SPI0_DOUT]", /* GPIO_0, BLSP1_SPI_MOSI, LSEC pin 14 */ - "[SPI0_DIN]", /* GPIO_1, BLSP1_SPI_MISO, LSEC pin 10 */ - "[SPI0_CS]", /* GPIO_2, BLSP1_SPI_CS_N, LSEC pin 12 */ - "[SPI0_SCLK]", /* GPIO_3, BLSP1_SPI_CLK, LSEC pin 8 */ - "[UART1_TxD]", /* GPIO_4, BLSP8_UART_TX, LSEC pin 11 */ - "[UART1_RxD]", /* GPIO_5, BLSP8_UART_RX, LSEC pin 13 */ - "[I2C1_SDA]", /* GPIO_6, BLSP8_I2C_SDA, LSEC pin 21 */ - "[I2C1_SCL]", /* GPIO_7, BLSP8_I2C_SCL, LSEC pin 19 */ - "GPIO-H", /* GPIO_8, LCD0_RESET_N, LSEC pin 30 */ - "TP93", /* GPIO_9 */ - "GPIO-G", /* GPIO_10, MDP_VSYNC_P, LSEC pin 29 */ - "[MDP_VSYNC_S]", /* GPIO_11, S HSEC pin 55 */ - "NC", /* GPIO_12 */ - "[CSI0_MCLK]", /* GPIO_13, CAM_MCLK0, P HSEC pin 15 */ - "[CAM_MCLK1]", /* GPIO_14, J14 pin 11 */ - "[CSI1_MCLK]", /* GPIO_15, CAM_MCLK2, P HSEC pin 17 */ - "TP99", /* GPIO_16 */ - "[I2C2_SDA]", /* GPIO_17, CCI_I2C_SDA0, P HSEC pin 34 */ - "[I2C2_SCL]", /* GPIO_18, CCI_I2C_SCL0, P HSEC pin 32 */ - "[CCI_I2C_SDA1]", /* GPIO_19, S HSEC pin 38 */ - "[CCI_I2C_SCL1]", /* GPIO_20, S HSEC pin 36 */ - "FLASH_STROBE_EN", /* GPIO_21, S HSEC pin 5 */ - "FLASH_STROBE_TRIG", /* GPIO_22, S HSEC pin 1 */ - "GPIO-K", /* GPIO_23, CAM2_RST_N, LSEC pin 33 */ - "GPIO-D", /* GPIO_24, LSEC pin 26 */ - "GPIO-I", /* GPIO_25, CAM0_RST_N, LSEC pin 31 */ - "GPIO-J", /* GPIO_26, CAM0_STANDBY_N, LSEC pin 32 */ - "BLSP6_I2C_SDA", /* GPIO_27 */ - "BLSP6_I2C_SCL", /* GPIO_28 */ - "GPIO-B", /* GPIO_29, TS0_RESET_N, LSEC pin 24 */ - "GPIO30", /* GPIO_30, S HSEC pin 4 */ - "HDMI_CEC", /* GPIO_31 */ - "HDMI_DDC_CLOCK", /* GPIO_32 */ - "HDMI_DDC_DATA", /* GPIO_33 */ - "HDMI_HOT_PLUG_DETECT", /* GPIO_34 */ - "PCIE0_RST_N", /* GPIO_35 */ - "PCIE0_CLKREQ_N", /* GPIO_36 */ - "PCIE0_WAKE", /* GPIO_37 */ - "SD_CARD_DET_N", /* GPIO_38 */ - "TSIF1_SYNC", /* GPIO_39, S HSEC pin 48 */ - "W_DISABLE_N", /* GPIO_40 */ - "[BLSP9_UART_TX]", /* GPIO_41 */ - "[BLSP9_UART_RX]", /* GPIO_42 */ - "[BLSP2_UART_CTS_N]", /* GPIO_43 */ - "[BLSP2_UART_RFR_N]", /* GPIO_44 */ - "[BLSP3_UART_TX]", /* GPIO_45 */ - "[BLSP3_UART_RX]", /* GPIO_46 */ - "[I2C0_SDA]", /* GPIO_47, LS_I2C0_SDA, LSEC pin 17 */ - "[I2C0_SCL]", /* GPIO_48, LS_I2C0_SCL, LSEC pin 15 */ - "[UART0_TxD]", /* GPIO_49, BLSP9_UART_TX, LSEC pin 5 */ - "[UART0_RxD]", /* GPIO_50, BLSP9_UART_RX, LSEC pin 7 */ - "[UART0_CTS]", /* GPIO_51, BLSP9_UART_CTS_N, LSEC pin 3 */ - "[UART0_RTS]", /* GPIO_52, BLSP9_UART_RFR_N, LSEC pin 9 */ - "[CODEC_INT1_N]", /* GPIO_53 */ - "[CODEC_INT2_N]", /* GPIO_54 */ - "[BLSP7_I2C_SDA]", /* GPIO_55 */ - "[BLSP7_I2C_SCL]", /* GPIO_56 */ - "MI2S_MCLK", /* GPIO_57, S HSEC pin 3 */ - "[PCM_CLK]", /* GPIO_58, QUA_MI2S_SCK, LSEC pin 18 */ - "[PCM_FS]", /* GPIO_59, QUA_MI2S_WS, LSEC pin 16 */ - "[PCM_DO]", /* GPIO_60, QUA_MI2S_DATA0, LSEC pin 20 */ - "[PCM_DI]", /* GPIO_61, QUA_MI2S_DATA1, LSEC pin 22 */ - "GPIO-E", /* GPIO_62, LSEC pin 27 */ - "TP87", /* GPIO_63 */ - "[CODEC_RST_N]", /* GPIO_64 */ - "[PCM1_CLK]", /* GPIO_65 */ - "[PCM1_SYNC]", /* GPIO_66 */ - "[PCM1_DIN]", /* GPIO_67 */ - "[PCM1_DOUT]", /* GPIO_68 */ - "AUDIO_REF_CLK", /* GPIO_69 */ - "SLIMBUS_CLK", /* GPIO_70 */ - "SLIMBUS_DATA0", /* GPIO_71 */ - "SLIMBUS_DATA1", /* GPIO_72 */ - "NC", /* GPIO_73 */ - "NC", /* GPIO_74 */ - "NC", /* GPIO_75 */ - "NC", /* GPIO_76 */ - "TP94", /* GPIO_77 */ - "NC", /* GPIO_78 */ - "TP95", /* GPIO_79 */ - "GPIO-A", /* GPIO_80, MEMS_RESET_N, LSEC pin 23 */ - "TP88", /* GPIO_81 */ - "TP89", /* GPIO_82 */ - "TP90", /* GPIO_83 */ - "TP91", /* GPIO_84 */ - "[SD_DAT0]", /* GPIO_85, BLSP12_SPI_MOSI, P HSEC pin 1 */ - "[SD_CMD]", /* GPIO_86, BLSP12_SPI_MISO, P HSEC pin 11 */ - "[SD_DAT3]", /* GPIO_87, BLSP12_SPI_CS_N, P HSEC pin 7 */ - "[SD_SCLK]", /* GPIO_88, BLSP12_SPI_CLK, P HSEC pin 9 */ - "TSIF1_CLK", /* GPIO_89, S HSEC pin 42 */ - "TSIF1_EN", /* GPIO_90, S HSEC pin 46 */ - "TSIF1_DATA", /* GPIO_91, S HSEC pin 44 */ - "NC", /* GPIO_92 */ - "TSIF2_CLK", /* GPIO_93, S HSEC pin 52 */ - "TSIF2_EN", /* GPIO_94, S HSEC pin 56 */ - "TSIF2_DATA", /* GPIO_95, S HSEC pin 54 */ - "TSIF2_SYNC", /* GPIO_96, S HSEC pin 58 */ - "NC", /* GPIO_97 */ - "CAM1_STANDBY_N", /* GPIO_98 */ - "NC", /* GPIO_99 */ - "NC", /* GPIO_100 */ - "[LCD1_RESET_N]", /* GPIO_101, S HSEC pin 51 */ - "BOOT_CONFIG1", /* GPIO_102 */ - "USB_HUB_RESET", /* GPIO_103 */ - "CAM1_RST_N", /* GPIO_104 */ - "NC", /* GPIO_105 */ - "NC", /* GPIO_106 */ - "NC", /* GPIO_107 */ - "NC", /* GPIO_108 */ - "NC", /* GPIO_109 */ - "NC", /* GPIO_110 */ - "NC", /* GPIO_111 */ - "NC", /* GPIO_112 */ - "PMI8994_BUA", /* GPIO_113 */ - "PCIE2_RST_N", /* GPIO_114 */ - "PCIE2_CLKREQ_N", /* GPIO_115 */ - "PCIE2_WAKE", /* GPIO_116 */ - "SSC_IRQ_0", /* GPIO_117 */ - "SSC_IRQ_1", /* GPIO_118 */ - "SSC_IRQ_2", /* GPIO_119 */ - "NC", /* GPIO_120 */ - "GPIO121", /* GPIO_121, S HSEC pin 2 */ - "NC", /* GPIO_122 */ - "SSC_IRQ_6", /* GPIO_123 */ - "SSC_IRQ_7", /* GPIO_124 */ - "GPIO-C", /* GPIO_125, TS_INT0, LSEC pin 25 */ - "BOOT_CONFIG5", /* GPIO_126 */ - "NC", /* GPIO_127 */ - "NC", /* GPIO_128 */ - "BOOT_CONFIG7", /* GPIO_129 */ - "PCIE1_RST_N", /* GPIO_130 */ - "PCIE1_CLKREQ_N", /* GPIO_131 */ - "PCIE1_WAKE", /* GPIO_132 */ - "GPIO-L", /* GPIO_133, CAM2_STANDBY_N, LSEC pin 34 */ - "NC", /* GPIO_134 */ - "NC", /* GPIO_135 */ - "BOOT_CONFIG8", /* GPIO_136 */ - "NC", /* GPIO_137 */ - "NC", /* GPIO_138 */ - "GPS_SSBI2", /* GPIO_139 */ - "GPS_SSBI1", /* GPIO_140 */ - "NC", /* GPIO_141 */ - "NC", /* GPIO_142 */ - "NC", /* GPIO_143 */ - "BOOT_CONFIG6", /* GPIO_144 */ - "NC", /* GPIO_145 */ - "NC", /* GPIO_146 */ - "NC", /* GPIO_147 */ - "NC", /* GPIO_148 */ - "NC"; /* GPIO_149 */ - - sdc2_cd_on: sdc2-cd-on-state { - pins = "gpio38"; - function = "gpio"; - bias-pull-up; - drive-strength = <16>; - }; - - sdc2_cd_off: sdc2-cd-off-state { - pins = "gpio38"; - function = "gpio"; - bias-pull-up; - drive-strength = <2>; - }; - - hdmi_hpd_active: hdmi-hpd-active-state { - pins = "gpio34"; - function = "hdmi_hot"; - bias-pull-down; - drive-strength = <16>; - }; - - hdmi_hpd_suspend: hdmi-hpd-suspend-state { - pins = "gpio34"; - function = "hdmi_hot"; - bias-pull-down; - drive-strength = <2>; - }; - - hdmi_ddc_active: hdmi-ddc-active-state { - pins = "gpio32", "gpio33"; - function = "hdmi_ddc"; - drive-strength = <2>; - bias-pull-up; - }; - - hdmi_ddc_suspend: hdmi-ddc-suspend-state { - pins = "gpio32", "gpio33"; - function = "hdmi_ddc"; - drive-strength = <2>; - bias-pull-down; - }; -}; - -&pcie0 { - status = "okay"; - perst-gpios = <&tlmm 35 GPIO_ACTIVE_LOW>; - vddpe-3v3-supply = <&wlan_en>; - vdda-supply = <&vreg_l28a_0p925>; -}; - -&pcie1 { - status = "okay"; - perst-gpios = <&tlmm 130 GPIO_ACTIVE_LOW>; - vdda-supply = <&vreg_l28a_0p925>; -}; - -&pcie2 { - status = "okay"; - perst-gpios = <&tlmm 114 GPIO_ACTIVE_LOW>; - vdda-supply = <&vreg_l28a_0p925>; -}; - -&pcie_phy { - status = "okay"; - - vdda-phy-supply = <&vreg_l28a_0p925>; - vdda-pll-supply = <&vreg_l12a_1p8>; -}; - -&pm8994_gpios { - gpio-line-names = - "NC", - "KEY_VOLP_N", - "NC", - "BL1_PWM", - "GPIO-F", /* BL0_PWM, LSEC pin 28 */ - "BL1_EN", - "NC", - "WLAN_EN", - "NC", - "NC", - "NC", - "NC", - "NC", - "NC", - "DIVCLK1", - "DIVCLK2", - "DIVCLK3", - "DIVCLK4", - "BT_EN", - "PMIC_SLB", - "PMIC_BUA", - "USB_VBUS_DET"; - - pinctrl-names = "default"; - pinctrl-0 = <&ls_exp_gpio_f &bt_en_gpios>; - - ls_exp_gpio_f: pm8994-gpio5-state { - pinconf { - pins = "gpio5"; - function = PMIC_GPIO_FUNC_NORMAL; - output-low; - power-source = ; /* 1.8V */ - }; - }; - - bt_en_gpios: bt-en-pios-state { - pinconf { - pins = "gpio19"; - function = PMIC_GPIO_FUNC_NORMAL; - output-low; - power-source = ; /* 1.8V */ - qcom,drive-strength = ; - bias-pull-down; - }; - }; - - wlan_en_gpios: wlan-en-gpios-state { - pinconf { - pins = "gpio8"; - function = PMIC_GPIO_FUNC_NORMAL; - output-low; - power-source = ; /* 1.8V */ - qcom,drive-strength = ; - bias-pull-down; - }; - }; - - audio_mclk: clk-div1-state { - pinconf { - pins = "gpio15"; - function = "func1"; - power-source = ; /* 1.8V */ - }; - }; - - volume_up_gpio: pm8996-gpio2-state { - pinconf { - pins = "gpio2"; - function = "normal"; - input-enable; - drive-push-pull; - bias-pull-up; - qcom,drive-strength = ; - power-source = ; /* 1.8V */ - }; - }; - - divclk4_pin_a: divclk4-state { - pinconf { - pins = "gpio18"; - function = PMIC_GPIO_FUNC_FUNC2; - - bias-disable; - power-source = ; - }; - }; - - usb3_vbus_det_gpio: pm8996-gpio22-state { - pinconf { - pins = "gpio22"; - function = PMIC_GPIO_FUNC_NORMAL; - input-enable; - bias-pull-down; - qcom,drive-strength = ; - power-source = ; /* 1.8V */ - }; - }; -}; - -&pm8994_mpps { - gpio-line-names = - "VDDPX_BIAS", - "WIFI_LED", - "NC", - "BT_LED", - "PM_MPP05", - "PM_MPP06", - "PM_MPP07", - "NC"; -}; - -&pm8994_spmi_regulators { - qcom,saw-reg = <&saw3>; - vdd_s11-supply = <&vph_pwr>; - - s9 { - qcom,saw-slave; - }; - s10 { - qcom,saw-slave; - }; - s11 { - qcom,saw-leader; - regulator-name = "VDD_APCC"; - regulator-always-on; - regulator-min-microvolt = <980000>; - regulator-max-microvolt = <980000>; - }; -}; - -&pmi8994_gpios { - gpio-line-names = - "NC", - "SPKR_AMP_EN1", - "SPKR_AMP_EN2", - "TP61", - "NC", - "USB2_VBUS_DET", - "NC", - "NC", - "NC", - "NC"; - - usb2_vbus_det_gpio: pmi8996-gpio6-state { - pinconf { - pins = "gpio6"; - function = PMIC_GPIO_FUNC_NORMAL; - input-enable; - bias-pull-down; - qcom,drive-strength = ; - power-source = ; /* 1.8V */ - }; - }; -}; - -&pmi8994_lpg { - qcom,power-source = <1>; - - pinctrl-names = "default"; - pinctrl-0 = <&pmi8994_mpp2_userled4>; - - qcom,dtest = <0 0>, - <0 0>, - <0 0>, - <4 1>; - - status = "okay"; - - led@1 { - reg = <1>; - color = ; - function = LED_FUNCTION_HEARTBEAT; - function-enumerator = <1>; - - linux,default-trigger = "heartbeat"; - default-state = "on"; - }; - - led@2 { - reg = <2>; - color = ; - function = LED_FUNCTION_HEARTBEAT; - function-enumerator = <0>; - }; - - led@3 { - reg = <3>; - color = ; - function = LED_FUNCTION_HEARTBEAT; - function-enumerator = <2>; - }; - - led@4 { - reg = <4>; - color = ; - function = LED_FUNCTION_HEARTBEAT; - function-enumerator = <3>; - }; -}; - -&pmi8994_mpps { - pmi8994_mpp2_userled4: mpp2-userled4-state { - pins = "mpp2"; - function = "sink"; - - output-low; - qcom,dtest = <4>; - }; -}; - -&pmi8994_spmi_regulators { - vdd_s2-supply = <&vph_pwr>; - - vdd_gfx: s2 { - regulator-name = "VDD_GFX"; - regulator-min-microvolt = <980000>; - regulator-max-microvolt = <980000>; - }; -}; - -&rpm_requests { - regulators-0 { - compatible = "qcom,rpm-pm8994-regulators"; - - vdd_s1-supply = <&vph_pwr>; - vdd_s2-supply = <&vph_pwr>; - vdd_s3-supply = <&vph_pwr>; - vdd_s4-supply = <&vph_pwr>; - vdd_s5-supply = <&vph_pwr>; - vdd_s6-supply = <&vph_pwr>; - vdd_s7-supply = <&vph_pwr>; - vdd_s8-supply = <&vph_pwr>; - vdd_s9-supply = <&vph_pwr>; - vdd_s10-supply = <&vph_pwr>; - vdd_s11-supply = <&vph_pwr>; - vdd_s12-supply = <&vph_pwr>; - vdd_l1-supply = <&vreg_s1b_1p025>; - vdd_l2_l26_l28-supply = <&vreg_s3a_1p3>; - vdd_l3_l11-supply = <&vreg_s3a_1p3>; - vdd_l4_l27_l31-supply = <&vreg_s3a_1p3>; - vdd_l5_l7-supply = <&vreg_s5a_2p15>; - vdd_l6_l12_l32-supply = <&vreg_s5a_2p15>; - vdd_l8_l16_l30-supply = <&vph_pwr>; - vdd_l9_l10_l18_l22-supply = <&vph_pwr_bbyp>; - vdd_l13_l19_l23_l24-supply = <&vph_pwr_bbyp>; - vdd_l14_l15-supply = <&vreg_s5a_2p15>; - vdd_l17_l29-supply = <&vph_pwr_bbyp>; - vdd_l20_l21-supply = <&vph_pwr_bbyp>; - vdd_l25-supply = <&vreg_s3a_1p3>; - vdd_lvs1_2-supply = <&vreg_s4a_1p8>; - - vreg_s3a_1p3: s3 { - regulator-name = "vreg_s3a_1p3"; - regulator-min-microvolt = <1300000>; - regulator-max-microvolt = <1300000>; - }; - - /** - * 1.8v required on LS expansion - * for mezzanine boards - */ - vreg_s4a_1p8: s4 { - regulator-name = "vreg_s4a_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-always-on; - }; - vreg_s5a_2p15: s5 { - regulator-name = "vreg_s5a_2p15"; - regulator-min-microvolt = <2150000>; - regulator-max-microvolt = <2150000>; - }; - vreg_s7a_1p0: s7 { - regulator-name = "vreg_s7a_1p0"; - regulator-min-microvolt = <800000>; - regulator-max-microvolt = <800000>; - }; - - vreg_l1a_1p0: l1 { - regulator-name = "vreg_l1a_1p0"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; - }; - vreg_l2a_1p25: l2 { - regulator-name = "vreg_l2a_1p25"; - regulator-min-microvolt = <1250000>; - regulator-max-microvolt = <1250000>; - }; - vreg_l3a_0p875: l3 { - regulator-name = "vreg_l3a_0p875"; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <850000>; - }; - vreg_l4a_1p225: l4 { - regulator-name = "vreg_l4a_1p225"; - regulator-min-microvolt = <1225000>; - regulator-max-microvolt = <1225000>; - }; - vreg_l6a_1p2: l6 { - regulator-name = "vreg_l6a_1p2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - }; - vreg_l8a_1p8: l8 { - regulator-name = "vreg_l8a_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - vreg_l9a_1p8: l9 { - regulator-name = "vreg_l9a_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - vreg_l10a_1p8: l10 { - regulator-name = "vreg_l10a_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - vreg_l11a_1p15: l11 { - regulator-name = "vreg_l11a_1p15"; - regulator-min-microvolt = <1150000>; - regulator-max-microvolt = <1150000>; - }; - vreg_l12a_1p8: l12 { - regulator-name = "vreg_l12a_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - vreg_l13a_2p95: l13 { - regulator-name = "vreg_l13a_2p95"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <2950000>; - }; - vreg_l14a_1p8: l14 { - regulator-name = "vreg_l14a_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - vreg_l15a_1p8: l15 { - regulator-name = "vreg_l15a_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - vreg_l16a_2p7: l16 { - regulator-name = "vreg_l16a_2p7"; - regulator-min-microvolt = <2700000>; - regulator-max-microvolt = <2700000>; - }; - vreg_l17a_2p8: l17 { - regulator-name = "vreg_l17a_2p8"; - regulator-min-microvolt = <2500000>; - regulator-max-microvolt = <2500000>; - }; - vreg_l18a_2p85: l18 { - regulator-name = "vreg_l18a_2p85"; - regulator-min-microvolt = <2700000>; - regulator-max-microvolt = <2900000>; - }; - vreg_l19a_2p8: l19 { - regulator-name = "vreg_l19a_2p8"; - regulator-min-microvolt = <3000000>; - regulator-max-microvolt = <3000000>; - }; - vreg_l20a_2p95: l20 { - regulator-name = "vreg_l20a_2p95"; - regulator-min-microvolt = <2950000>; - regulator-max-microvolt = <2950000>; - regulator-allow-set-load; - }; - vreg_l21a_2p95: l21 { - regulator-name = "vreg_l21a_2p95"; - regulator-min-microvolt = <2950000>; - regulator-max-microvolt = <2950000>; - regulator-allow-set-load; - regulator-system-load = <200000>; - }; - vreg_l22a_3p0: l22 { - regulator-name = "vreg_l22a_3p0"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - }; - vreg_l23a_2p8: l23 { - regulator-name = "vreg_l23a_2p8"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - }; - vreg_l24a_3p075: l24 { - regulator-name = "vreg_l24a_3p075"; - regulator-min-microvolt = <3075000>; - regulator-max-microvolt = <3075000>; - }; - vreg_l25a_1p2: l25 { - regulator-name = "vreg_l25a_1p2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-allow-set-load; - }; - vreg_l26a_0p8: l27 { - regulator-name = "vreg_l26a_0p8"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; - }; - vreg_l28a_0p925: l28 { - regulator-name = "vreg_l28a_0p925"; - regulator-min-microvolt = <925000>; - regulator-max-microvolt = <925000>; - regulator-allow-set-load; - }; - vreg_l29a_2p8: l29 { - regulator-name = "vreg_l29a_2p8"; - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - }; - vreg_l30a_1p8: l30 { - regulator-name = "vreg_l30a_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - vreg_l32a_1p8: l32 { - regulator-name = "vreg_l32a_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - vreg_lvs1a_1p8: lvs1 { - regulator-name = "vreg_lvs1a_1p8"; - }; - - vreg_lvs2a_1p8: lvs2 { - regulator-name = "vreg_lvs2a_1p8"; - }; - }; - - regulators-1 { - compatible = "qcom,rpm-pmi8994-regulators"; - - vdd_s1-supply = <&vph_pwr>; - vdd_s2-supply = <&vph_pwr>; - vdd_s3-supply = <&vph_pwr>; - vdd_bst_byp-supply = <&vph_pwr>; - - vph_pwr_bbyp: boost-bypass { - regulator-name = "vph_pwr_bbyp"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - }; - - vreg_s1b_1p025: s1 { - regulator-name = "vreg_s1b_1p025"; - regulator-min-microvolt = <1025000>; - regulator-max-microvolt = <1025000>; - }; - }; -}; - -&sdhc2 { - /* External SD card */ - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&sdc2_state_on &sdc2_cd_on>; - pinctrl-1 = <&sdc2_state_off &sdc2_cd_off>; - cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; - vmmc-supply = <&vreg_l21a_2p95>; - vqmmc-supply = <&vreg_l13a_2p95>; - status = "okay"; -}; - -&q6asmdai { - dai@0 { - reg = ; - }; - - dai@1 { - reg = ; - }; - - dai@2 { - reg = ; - }; -}; - -&slim_msm { - status = "okay"; - - slim@1 { - reg = <1>; - #address-cells = <2>; - #size-cells = <0>; - - tasha_ifd: tas-ifd@0,0 { - compatible = "slim217,1a0"; - reg = <0 0>; - }; - - wcd9335: codec@1,0 { - compatible = "slim217,1a0"; - reg = <1 0>; - - clock-names = "mclk", "slimbus"; - clocks = <&div1_mclk>, - <&rpmcc RPM_SMD_BB_CLK1>; - interrupt-parent = <&tlmm>; - interrupts = <54 IRQ_TYPE_LEVEL_HIGH>, - <53 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "intr1", "intr2"; - interrupt-controller; - #interrupt-cells = <1>; - - pinctrl-0 = <&cdc_reset_active &wcd_intr_default>; - pinctrl-names = "default"; - - reset-gpios = <&tlmm 64 GPIO_ACTIVE_LOW>; - slim-ifc-dev = <&tasha_ifd>; - - #sound-dai-cells = <1>; - - vdd-buck-supply = <&vreg_s4a_1p8>; - vdd-buck-sido-supply = <&vreg_s4a_1p8>; - vdd-tx-supply = <&vreg_s4a_1p8>; - vdd-rx-supply = <&vreg_s4a_1p8>; - vdd-io-supply = <&vreg_s4a_1p8>; - }; - }; -}; - -&sound { - compatible = "qcom,apq8096-sndcard"; - model = "DB820c"; - audio-routing = "RX_BIAS", "MCLK"; - - mm1-dai-link { - link-name = "MultiMedia1"; - cpu { - sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; - }; - }; - - mm2-dai-link { - link-name = "MultiMedia2"; - cpu { - sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA2>; - }; - }; - - mm3-dai-link { - link-name = "MultiMedia3"; - cpu { - sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA3>; - }; - }; - - hdmi-dai-link { - link-name = "HDMI"; - cpu { - sound-dai = <&q6afedai HDMI_RX>; - }; - - platform { - sound-dai = <&q6routing>; - }; - - codec { - sound-dai = <&mdss_hdmi 0>; - }; - }; - - slim-dai-link { - link-name = "SLIM Playback"; - cpu { - sound-dai = <&q6afedai SLIMBUS_6_RX>; - }; - - platform { - sound-dai = <&q6routing>; - }; - - codec { - sound-dai = <&wcd9335 AIF4_PB>; - }; - }; - - slimcap-dai-link { - link-name = "SLIM Capture"; - cpu { - sound-dai = <&q6afedai SLIMBUS_0_TX>; - }; - - platform { - sound-dai = <&q6routing>; - }; - - codec { - sound-dai = <&wcd9335 AIF1_CAP>; - }; - }; -}; - -&ufsphy { - status = "okay"; - - vdda-phy-supply = <&vreg_l28a_0p925>; - vdda-pll-supply = <&vreg_l12a_1p8>; -}; - -&ufshc { - status = "okay"; - - vcc-supply = <&vreg_l20a_2p95>; - vccq-supply = <&vreg_l25a_1p2>; - vccq2-supply = <&vreg_s4a_1p8>; - vdd-hba-supply = <&vreg_l25a_1p2>; - - vcc-max-microamp = <600000>; - vccq-max-microamp = <450000>; - vccq2-max-microamp = <450000>; -}; - -&usb2 { - status = "okay"; - extcon = <&usb2_id>; -}; - -&usb2_dwc3 { - extcon = <&usb2_id>; - dr_mode = "otg"; - maximum-speed = "high-speed"; -}; - -&usb3 { - status = "okay"; - extcon = <&usb3_id>; -}; - -&usb3_dwc3 { - extcon = <&usb3_id>; - dr_mode = "otg"; -}; - -&usb3phy { - status = "okay"; - - vdda-phy-supply = <&vreg_l28a_0p925>; - vdda-pll-supply = <&vreg_l12a_1p8>; -}; - -&venus { - status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi new file mode 100644 index 000000000000..0c076852b494 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi @@ -0,0 +1,1133 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + */ + + +#include "pm8994.dtsi" +#include "pmi8994.dtsi" +#include +#include +#include +#include +#include +#include +#include + +/* + * GPIO name legend: proper name = the GPIO line is used as GPIO + * NC = not connected (pin out but not routed from the chip to + * anything the board) + * "[PER]" = pin is muxed for [peripheral] (not GPIO) + * LSEC = Low Speed External Connector + * P HSEC = Primary High Speed External Connector + * S HSEC = Secondary High Speed External Connector + * J14 = Camera Connector + * TP = Test Points + * + * Line names are taken from the schematic "DragonBoard 820c", + * drawing no: LM25-P2751-1 + * + * For the lines routed to the external connectors the + * lines are named after the 96Boards CE Specification 1.0, + * Appendix "Expansion Connector Signal Description". + * + * When the 96Board naming of a line and the schematic name of + * the same line are in conflict, the 96Board specification + * takes precedence, which means that the external UART on the + * LSEC is named UART0 while the schematic and SoC names this + * UART3. This is only for the informational lines i.e. "[FOO]", + * the GPIO named lines "GPIO-A" thru "GPIO-L" are the only + * ones actually used for GPIO. + */ + +/ { + aliases { + serial0 = &blsp2_uart2; + serial1 = &blsp2_uart3; + serial2 = &blsp1_uart2; + i2c0 = &blsp1_i2c3; + i2c1 = &blsp2_i2c1; + i2c2 = &blsp2_i2c1; + spi0 = &blsp1_spi1; + spi1 = &blsp2_spi6; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + div1_mclk: divclk1 { + compatible = "gpio-gate-clock"; + pinctrl-0 = <&audio_mclk>; + pinctrl-names = "default"; + clocks = <&rpmcc RPM_SMD_DIV_CLK1>; + #clock-cells = <0>; + enable-gpios = <&pm8994_gpios 15 0>; + }; + + divclk4: divclk4 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + clock-output-names = "divclk4"; + + pinctrl-names = "default"; + pinctrl-0 = <&divclk4_pin_a>; + }; + + gpio-keys { + compatible = "gpio-keys"; + autorepeat; + + pinctrl-names = "default"; + pinctrl-0 = <&volume_up_gpio>; + + button { + label = "Volume Up"; + linux,code = ; + gpios = <&pm8994_gpios 2 GPIO_ACTIVE_LOW>; + }; + }; + + usb2_id: usb2-id { + compatible = "linux,extcon-usb-gpio"; + id-gpios = <&pmi8994_gpios 6 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb2_vbus_det_gpio>; + }; + + usb3_id: usb3-id { + compatible = "linux,extcon-usb-gpio"; + id-gpios = <&pm8994_gpios 22 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb3_vbus_det_gpio>; + }; + + vph_pwr: vph-pwr-regulator { + compatible = "regulator-fixed"; + regulator-name = "vph_pwr"; + regulator-always-on; + regulator-boot-on; + + regulator-min-microvolt = <3700000>; + regulator-max-microvolt = <3700000>; + }; + + wlan_en: wlan-en-1-8v { + pinctrl-names = "default"; + pinctrl-0 = <&wlan_en_gpios>; + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + gpio = <&pm8994_gpios 8 0>; + + /* WLAN card specific delay */ + startup-delay-us = <70000>; + enable-active-high; + }; +}; + +&blsp1_i2c3 { + /* On Low speed expansion: LS-I2C0 */ + status = "okay"; +}; + +&blsp1_spi1 { + /* On Low speed expansion */ + status = "okay"; +}; + +&blsp1_uart2 { + label = "BT-UART"; + status = "okay"; + + bluetooth { + compatible = "qcom,qca6174-bt"; + + /* bt_disable_n gpio */ + enable-gpios = <&pm8994_gpios 19 GPIO_ACTIVE_HIGH>; + + clocks = <&divclk4>; + }; +}; + +&adsp_pil { + status = "okay"; + firmware-name = "qcom/apq8096/adsp.mbn"; +}; + +&blsp2_i2c1 { + /* On High speed expansion: HS-I2C2 */ + status = "okay"; +}; + +&blsp2_i2c1 { + /* On Low speed expansion: LS-I2C1 */ + status = "okay"; +}; + +&blsp2_spi6 { + /* On High speed expansion */ + status = "okay"; +}; + +&blsp2_uart2 { + label = "LS-UART1"; + status = "okay"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&blsp2_uart2_2pins_default>; + pinctrl-1 = <&blsp2_uart2_2pins_sleep>; +}; + +&blsp2_uart3 { + label = "LS-UART0"; + status = "disabled"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&blsp2_uart3_4pins_default>; + pinctrl-1 = <&blsp2_uart3_4pins_sleep>; +}; + +&camss { + vdda-supply = <&vreg_l2a_1p25>; +}; + +&gpu { + status = "okay"; +}; + +&gpu_zap_shader { + firmware-name = "qcom/apq8096/a530_zap.mbn"; +}; + +&hsusb_phy1 { + status = "okay"; + + vdd-supply = <&vreg_l28a_0p925>; + vdda-pll-supply = <&vreg_l12a_1p8>; + vdda-phy-dpdm-supply = <&vreg_l24a_3p075>; +}; + +&hsusb_phy2 { + status = "okay"; + + vdd-supply = <&vreg_l28a_0p925>; + vdda-pll-supply = <&vreg_l12a_1p8>; + vdda-phy-dpdm-supply = <&vreg_l24a_3p075>; +}; + +&mdp { + status = "okay"; +}; + +&mdss { + status = "okay"; +}; + +&mdss_hdmi { + status = "okay"; + + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&hdmi_hpd_active &hdmi_ddc_active>; + pinctrl-1 = <&hdmi_hpd_suspend &hdmi_ddc_suspend>; + + core-vdda-supply = <&vreg_l12a_1p8>; + core-vcc-supply = <&vreg_s4a_1p8>; +}; + +&mdss_hdmi_phy { + status = "okay"; + + vddio-supply = <&vreg_l12a_1p8>; + vcca-supply = <&vreg_l28a_0p925>; + #phy-cells = <0>; +}; + +&mmcc { + vdd-gfx-supply = <&vdd_gfx>; +}; + +&mss_pil { + status = "okay"; + pll-supply = <&vreg_l12a_1p8>; + firmware-name = "qcom/apq8096/mba.mbn", "qcom/apq8096/modem.mbn"; +}; + +&pm8994_resin { + status = "okay"; + linux,code = ; +}; + +&tlmm { + gpio-line-names = + "[SPI0_DOUT]", /* GPIO_0, BLSP1_SPI_MOSI, LSEC pin 14 */ + "[SPI0_DIN]", /* GPIO_1, BLSP1_SPI_MISO, LSEC pin 10 */ + "[SPI0_CS]", /* GPIO_2, BLSP1_SPI_CS_N, LSEC pin 12 */ + "[SPI0_SCLK]", /* GPIO_3, BLSP1_SPI_CLK, LSEC pin 8 */ + "[UART1_TxD]", /* GPIO_4, BLSP8_UART_TX, LSEC pin 11 */ + "[UART1_RxD]", /* GPIO_5, BLSP8_UART_RX, LSEC pin 13 */ + "[I2C1_SDA]", /* GPIO_6, BLSP8_I2C_SDA, LSEC pin 21 */ + "[I2C1_SCL]", /* GPIO_7, BLSP8_I2C_SCL, LSEC pin 19 */ + "GPIO-H", /* GPIO_8, LCD0_RESET_N, LSEC pin 30 */ + "TP93", /* GPIO_9 */ + "GPIO-G", /* GPIO_10, MDP_VSYNC_P, LSEC pin 29 */ + "[MDP_VSYNC_S]", /* GPIO_11, S HSEC pin 55 */ + "NC", /* GPIO_12 */ + "[CSI0_MCLK]", /* GPIO_13, CAM_MCLK0, P HSEC pin 15 */ + "[CAM_MCLK1]", /* GPIO_14, J14 pin 11 */ + "[CSI1_MCLK]", /* GPIO_15, CAM_MCLK2, P HSEC pin 17 */ + "TP99", /* GPIO_16 */ + "[I2C2_SDA]", /* GPIO_17, CCI_I2C_SDA0, P HSEC pin 34 */ + "[I2C2_SCL]", /* GPIO_18, CCI_I2C_SCL0, P HSEC pin 32 */ + "[CCI_I2C_SDA1]", /* GPIO_19, S HSEC pin 38 */ + "[CCI_I2C_SCL1]", /* GPIO_20, S HSEC pin 36 */ + "FLASH_STROBE_EN", /* GPIO_21, S HSEC pin 5 */ + "FLASH_STROBE_TRIG", /* GPIO_22, S HSEC pin 1 */ + "GPIO-K", /* GPIO_23, CAM2_RST_N, LSEC pin 33 */ + "GPIO-D", /* GPIO_24, LSEC pin 26 */ + "GPIO-I", /* GPIO_25, CAM0_RST_N, LSEC pin 31 */ + "GPIO-J", /* GPIO_26, CAM0_STANDBY_N, LSEC pin 32 */ + "BLSP6_I2C_SDA", /* GPIO_27 */ + "BLSP6_I2C_SCL", /* GPIO_28 */ + "GPIO-B", /* GPIO_29, TS0_RESET_N, LSEC pin 24 */ + "GPIO30", /* GPIO_30, S HSEC pin 4 */ + "HDMI_CEC", /* GPIO_31 */ + "HDMI_DDC_CLOCK", /* GPIO_32 */ + "HDMI_DDC_DATA", /* GPIO_33 */ + "HDMI_HOT_PLUG_DETECT", /* GPIO_34 */ + "PCIE0_RST_N", /* GPIO_35 */ + "PCIE0_CLKREQ_N", /* GPIO_36 */ + "PCIE0_WAKE", /* GPIO_37 */ + "SD_CARD_DET_N", /* GPIO_38 */ + "TSIF1_SYNC", /* GPIO_39, S HSEC pin 48 */ + "W_DISABLE_N", /* GPIO_40 */ + "[BLSP9_UART_TX]", /* GPIO_41 */ + "[BLSP9_UART_RX]", /* GPIO_42 */ + "[BLSP2_UART_CTS_N]", /* GPIO_43 */ + "[BLSP2_UART_RFR_N]", /* GPIO_44 */ + "[BLSP3_UART_TX]", /* GPIO_45 */ + "[BLSP3_UART_RX]", /* GPIO_46 */ + "[I2C0_SDA]", /* GPIO_47, LS_I2C0_SDA, LSEC pin 17 */ + "[I2C0_SCL]", /* GPIO_48, LS_I2C0_SCL, LSEC pin 15 */ + "[UART0_TxD]", /* GPIO_49, BLSP9_UART_TX, LSEC pin 5 */ + "[UART0_RxD]", /* GPIO_50, BLSP9_UART_RX, LSEC pin 7 */ + "[UART0_CTS]", /* GPIO_51, BLSP9_UART_CTS_N, LSEC pin 3 */ + "[UART0_RTS]", /* GPIO_52, BLSP9_UART_RFR_N, LSEC pin 9 */ + "[CODEC_INT1_N]", /* GPIO_53 */ + "[CODEC_INT2_N]", /* GPIO_54 */ + "[BLSP7_I2C_SDA]", /* GPIO_55 */ + "[BLSP7_I2C_SCL]", /* GPIO_56 */ + "MI2S_MCLK", /* GPIO_57, S HSEC pin 3 */ + "[PCM_CLK]", /* GPIO_58, QUA_MI2S_SCK, LSEC pin 18 */ + "[PCM_FS]", /* GPIO_59, QUA_MI2S_WS, LSEC pin 16 */ + "[PCM_DO]", /* GPIO_60, QUA_MI2S_DATA0, LSEC pin 20 */ + "[PCM_DI]", /* GPIO_61, QUA_MI2S_DATA1, LSEC pin 22 */ + "GPIO-E", /* GPIO_62, LSEC pin 27 */ + "TP87", /* GPIO_63 */ + "[CODEC_RST_N]", /* GPIO_64 */ + "[PCM1_CLK]", /* GPIO_65 */ + "[PCM1_SYNC]", /* GPIO_66 */ + "[PCM1_DIN]", /* GPIO_67 */ + "[PCM1_DOUT]", /* GPIO_68 */ + "AUDIO_REF_CLK", /* GPIO_69 */ + "SLIMBUS_CLK", /* GPIO_70 */ + "SLIMBUS_DATA0", /* GPIO_71 */ + "SLIMBUS_DATA1", /* GPIO_72 */ + "NC", /* GPIO_73 */ + "NC", /* GPIO_74 */ + "NC", /* GPIO_75 */ + "NC", /* GPIO_76 */ + "TP94", /* GPIO_77 */ + "NC", /* GPIO_78 */ + "TP95", /* GPIO_79 */ + "GPIO-A", /* GPIO_80, MEMS_RESET_N, LSEC pin 23 */ + "TP88", /* GPIO_81 */ + "TP89", /* GPIO_82 */ + "TP90", /* GPIO_83 */ + "TP91", /* GPIO_84 */ + "[SD_DAT0]", /* GPIO_85, BLSP12_SPI_MOSI, P HSEC pin 1 */ + "[SD_CMD]", /* GPIO_86, BLSP12_SPI_MISO, P HSEC pin 11 */ + "[SD_DAT3]", /* GPIO_87, BLSP12_SPI_CS_N, P HSEC pin 7 */ + "[SD_SCLK]", /* GPIO_88, BLSP12_SPI_CLK, P HSEC pin 9 */ + "TSIF1_CLK", /* GPIO_89, S HSEC pin 42 */ + "TSIF1_EN", /* GPIO_90, S HSEC pin 46 */ + "TSIF1_DATA", /* GPIO_91, S HSEC pin 44 */ + "NC", /* GPIO_92 */ + "TSIF2_CLK", /* GPIO_93, S HSEC pin 52 */ + "TSIF2_EN", /* GPIO_94, S HSEC pin 56 */ + "TSIF2_DATA", /* GPIO_95, S HSEC pin 54 */ + "TSIF2_SYNC", /* GPIO_96, S HSEC pin 58 */ + "NC", /* GPIO_97 */ + "CAM1_STANDBY_N", /* GPIO_98 */ + "NC", /* GPIO_99 */ + "NC", /* GPIO_100 */ + "[LCD1_RESET_N]", /* GPIO_101, S HSEC pin 51 */ + "BOOT_CONFIG1", /* GPIO_102 */ + "USB_HUB_RESET", /* GPIO_103 */ + "CAM1_RST_N", /* GPIO_104 */ + "NC", /* GPIO_105 */ + "NC", /* GPIO_106 */ + "NC", /* GPIO_107 */ + "NC", /* GPIO_108 */ + "NC", /* GPIO_109 */ + "NC", /* GPIO_110 */ + "NC", /* GPIO_111 */ + "NC", /* GPIO_112 */ + "PMI8994_BUA", /* GPIO_113 */ + "PCIE2_RST_N", /* GPIO_114 */ + "PCIE2_CLKREQ_N", /* GPIO_115 */ + "PCIE2_WAKE", /* GPIO_116 */ + "SSC_IRQ_0", /* GPIO_117 */ + "SSC_IRQ_1", /* GPIO_118 */ + "SSC_IRQ_2", /* GPIO_119 */ + "NC", /* GPIO_120 */ + "GPIO121", /* GPIO_121, S HSEC pin 2 */ + "NC", /* GPIO_122 */ + "SSC_IRQ_6", /* GPIO_123 */ + "SSC_IRQ_7", /* GPIO_124 */ + "GPIO-C", /* GPIO_125, TS_INT0, LSEC pin 25 */ + "BOOT_CONFIG5", /* GPIO_126 */ + "NC", /* GPIO_127 */ + "NC", /* GPIO_128 */ + "BOOT_CONFIG7", /* GPIO_129 */ + "PCIE1_RST_N", /* GPIO_130 */ + "PCIE1_CLKREQ_N", /* GPIO_131 */ + "PCIE1_WAKE", /* GPIO_132 */ + "GPIO-L", /* GPIO_133, CAM2_STANDBY_N, LSEC pin 34 */ + "NC", /* GPIO_134 */ + "NC", /* GPIO_135 */ + "BOOT_CONFIG8", /* GPIO_136 */ + "NC", /* GPIO_137 */ + "NC", /* GPIO_138 */ + "GPS_SSBI2", /* GPIO_139 */ + "GPS_SSBI1", /* GPIO_140 */ + "NC", /* GPIO_141 */ + "NC", /* GPIO_142 */ + "NC", /* GPIO_143 */ + "BOOT_CONFIG6", /* GPIO_144 */ + "NC", /* GPIO_145 */ + "NC", /* GPIO_146 */ + "NC", /* GPIO_147 */ + "NC", /* GPIO_148 */ + "NC"; /* GPIO_149 */ + + sdc2_cd_on: sdc2-cd-on-state { + pins = "gpio38"; + function = "gpio"; + bias-pull-up; + drive-strength = <16>; + }; + + sdc2_cd_off: sdc2-cd-off-state { + pins = "gpio38"; + function = "gpio"; + bias-pull-up; + drive-strength = <2>; + }; + + hdmi_hpd_active: hdmi-hpd-active-state { + pins = "gpio34"; + function = "hdmi_hot"; + bias-pull-down; + drive-strength = <16>; + }; + + hdmi_hpd_suspend: hdmi-hpd-suspend-state { + pins = "gpio34"; + function = "hdmi_hot"; + bias-pull-down; + drive-strength = <2>; + }; + + hdmi_ddc_active: hdmi-ddc-active-state { + pins = "gpio32", "gpio33"; + function = "hdmi_ddc"; + drive-strength = <2>; + bias-pull-up; + }; + + hdmi_ddc_suspend: hdmi-ddc-suspend-state { + pins = "gpio32", "gpio33"; + function = "hdmi_ddc"; + drive-strength = <2>; + bias-pull-down; + }; +}; + +&pcie0 { + status = "okay"; + perst-gpios = <&tlmm 35 GPIO_ACTIVE_LOW>; + vddpe-3v3-supply = <&wlan_en>; + vdda-supply = <&vreg_l28a_0p925>; +}; + +&pcie1 { + status = "okay"; + perst-gpios = <&tlmm 130 GPIO_ACTIVE_LOW>; + vdda-supply = <&vreg_l28a_0p925>; +}; + +&pcie2 { + status = "okay"; + perst-gpios = <&tlmm 114 GPIO_ACTIVE_LOW>; + vdda-supply = <&vreg_l28a_0p925>; +}; + +&pcie_phy { + status = "okay"; + + vdda-phy-supply = <&vreg_l28a_0p925>; + vdda-pll-supply = <&vreg_l12a_1p8>; +}; + +&pm8994_gpios { + gpio-line-names = + "NC", + "KEY_VOLP_N", + "NC", + "BL1_PWM", + "GPIO-F", /* BL0_PWM, LSEC pin 28 */ + "BL1_EN", + "NC", + "WLAN_EN", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "DIVCLK1", + "DIVCLK2", + "DIVCLK3", + "DIVCLK4", + "BT_EN", + "PMIC_SLB", + "PMIC_BUA", + "USB_VBUS_DET"; + + pinctrl-names = "default"; + pinctrl-0 = <&ls_exp_gpio_f &bt_en_gpios>; + + ls_exp_gpio_f: pm8994-gpio5-state { + pinconf { + pins = "gpio5"; + function = PMIC_GPIO_FUNC_NORMAL; + output-low; + power-source = ; /* 1.8V */ + }; + }; + + bt_en_gpios: bt-en-pios-state { + pinconf { + pins = "gpio19"; + function = PMIC_GPIO_FUNC_NORMAL; + output-low; + power-source = ; /* 1.8V */ + qcom,drive-strength = ; + bias-pull-down; + }; + }; + + wlan_en_gpios: wlan-en-gpios-state { + pinconf { + pins = "gpio8"; + function = PMIC_GPIO_FUNC_NORMAL; + output-low; + power-source = ; /* 1.8V */ + qcom,drive-strength = ; + bias-pull-down; + }; + }; + + audio_mclk: clk-div1-state { + pinconf { + pins = "gpio15"; + function = "func1"; + power-source = ; /* 1.8V */ + }; + }; + + volume_up_gpio: pm8996-gpio2-state { + pinconf { + pins = "gpio2"; + function = "normal"; + input-enable; + drive-push-pull; + bias-pull-up; + qcom,drive-strength = ; + power-source = ; /* 1.8V */ + }; + }; + + divclk4_pin_a: divclk4-state { + pinconf { + pins = "gpio18"; + function = PMIC_GPIO_FUNC_FUNC2; + + bias-disable; + power-source = ; + }; + }; + + usb3_vbus_det_gpio: pm8996-gpio22-state { + pinconf { + pins = "gpio22"; + function = PMIC_GPIO_FUNC_NORMAL; + input-enable; + bias-pull-down; + qcom,drive-strength = ; + power-source = ; /* 1.8V */ + }; + }; +}; + +&pm8994_mpps { + gpio-line-names = + "VDDPX_BIAS", + "WIFI_LED", + "NC", + "BT_LED", + "PM_MPP05", + "PM_MPP06", + "PM_MPP07", + "NC"; +}; + +&pm8994_spmi_regulators { + qcom,saw-reg = <&saw3>; + vdd_s11-supply = <&vph_pwr>; + + s9 { + qcom,saw-slave; + }; + s10 { + qcom,saw-slave; + }; + s11 { + qcom,saw-leader; + regulator-name = "VDD_APCC"; + regulator-always-on; + regulator-min-microvolt = <980000>; + regulator-max-microvolt = <980000>; + }; +}; + +&pmi8994_gpios { + gpio-line-names = + "NC", + "SPKR_AMP_EN1", + "SPKR_AMP_EN2", + "TP61", + "NC", + "USB2_VBUS_DET", + "NC", + "NC", + "NC", + "NC"; + + usb2_vbus_det_gpio: pmi8996-gpio6-state { + pinconf { + pins = "gpio6"; + function = PMIC_GPIO_FUNC_NORMAL; + input-enable; + bias-pull-down; + qcom,drive-strength = ; + power-source = ; /* 1.8V */ + }; + }; +}; + +&pmi8994_lpg { + qcom,power-source = <1>; + + pinctrl-names = "default"; + pinctrl-0 = <&pmi8994_mpp2_userled4>; + + qcom,dtest = <0 0>, + <0 0>, + <0 0>, + <4 1>; + + status = "okay"; + + led@1 { + reg = <1>; + color = ; + function = LED_FUNCTION_HEARTBEAT; + function-enumerator = <1>; + + linux,default-trigger = "heartbeat"; + default-state = "on"; + }; + + led@2 { + reg = <2>; + color = ; + function = LED_FUNCTION_HEARTBEAT; + function-enumerator = <0>; + }; + + led@3 { + reg = <3>; + color = ; + function = LED_FUNCTION_HEARTBEAT; + function-enumerator = <2>; + }; + + led@4 { + reg = <4>; + color = ; + function = LED_FUNCTION_HEARTBEAT; + function-enumerator = <3>; + }; +}; + +&pmi8994_mpps { + pmi8994_mpp2_userled4: mpp2-userled4-state { + pins = "mpp2"; + function = "sink"; + + output-low; + qcom,dtest = <4>; + }; +}; + +&pmi8994_spmi_regulators { + vdd_s2-supply = <&vph_pwr>; + + vdd_gfx: s2 { + regulator-name = "VDD_GFX"; + regulator-min-microvolt = <980000>; + regulator-max-microvolt = <980000>; + }; +}; + +&rpm_requests { + regulators-0 { + compatible = "qcom,rpm-pm8994-regulators"; + + vdd_s1-supply = <&vph_pwr>; + vdd_s2-supply = <&vph_pwr>; + vdd_s3-supply = <&vph_pwr>; + vdd_s4-supply = <&vph_pwr>; + vdd_s5-supply = <&vph_pwr>; + vdd_s6-supply = <&vph_pwr>; + vdd_s7-supply = <&vph_pwr>; + vdd_s8-supply = <&vph_pwr>; + vdd_s9-supply = <&vph_pwr>; + vdd_s10-supply = <&vph_pwr>; + vdd_s11-supply = <&vph_pwr>; + vdd_s12-supply = <&vph_pwr>; + vdd_l1-supply = <&vreg_s1b_1p025>; + vdd_l2_l26_l28-supply = <&vreg_s3a_1p3>; + vdd_l3_l11-supply = <&vreg_s3a_1p3>; + vdd_l4_l27_l31-supply = <&vreg_s3a_1p3>; + vdd_l5_l7-supply = <&vreg_s5a_2p15>; + vdd_l6_l12_l32-supply = <&vreg_s5a_2p15>; + vdd_l8_l16_l30-supply = <&vph_pwr>; + vdd_l9_l10_l18_l22-supply = <&vph_pwr_bbyp>; + vdd_l13_l19_l23_l24-supply = <&vph_pwr_bbyp>; + vdd_l14_l15-supply = <&vreg_s5a_2p15>; + vdd_l17_l29-supply = <&vph_pwr_bbyp>; + vdd_l20_l21-supply = <&vph_pwr_bbyp>; + vdd_l25-supply = <&vreg_s3a_1p3>; + vdd_lvs1_2-supply = <&vreg_s4a_1p8>; + + vreg_s3a_1p3: s3 { + regulator-name = "vreg_s3a_1p3"; + regulator-min-microvolt = <1300000>; + regulator-max-microvolt = <1300000>; + }; + + /** + * 1.8v required on LS expansion + * for mezzanine boards + */ + vreg_s4a_1p8: s4 { + regulator-name = "vreg_s4a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + vreg_s5a_2p15: s5 { + regulator-name = "vreg_s5a_2p15"; + regulator-min-microvolt = <2150000>; + regulator-max-microvolt = <2150000>; + }; + vreg_s7a_1p0: s7 { + regulator-name = "vreg_s7a_1p0"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + }; + + vreg_l1a_1p0: l1 { + regulator-name = "vreg_l1a_1p0"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + }; + vreg_l2a_1p25: l2 { + regulator-name = "vreg_l2a_1p25"; + regulator-min-microvolt = <1250000>; + regulator-max-microvolt = <1250000>; + }; + vreg_l3a_0p875: l3 { + regulator-name = "vreg_l3a_0p875"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + }; + vreg_l4a_1p225: l4 { + regulator-name = "vreg_l4a_1p225"; + regulator-min-microvolt = <1225000>; + regulator-max-microvolt = <1225000>; + }; + vreg_l6a_1p2: l6 { + regulator-name = "vreg_l6a_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + vreg_l8a_1p8: l8 { + regulator-name = "vreg_l8a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + vreg_l9a_1p8: l9 { + regulator-name = "vreg_l9a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + vreg_l10a_1p8: l10 { + regulator-name = "vreg_l10a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + vreg_l11a_1p15: l11 { + regulator-name = "vreg_l11a_1p15"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1150000>; + }; + vreg_l12a_1p8: l12 { + regulator-name = "vreg_l12a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + vreg_l13a_2p95: l13 { + regulator-name = "vreg_l13a_2p95"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2950000>; + }; + vreg_l14a_1p8: l14 { + regulator-name = "vreg_l14a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + vreg_l15a_1p8: l15 { + regulator-name = "vreg_l15a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + vreg_l16a_2p7: l16 { + regulator-name = "vreg_l16a_2p7"; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <2700000>; + }; + vreg_l17a_2p8: l17 { + regulator-name = "vreg_l17a_2p8"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + }; + vreg_l18a_2p85: l18 { + regulator-name = "vreg_l18a_2p85"; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <2900000>; + }; + vreg_l19a_2p8: l19 { + regulator-name = "vreg_l19a_2p8"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + }; + vreg_l20a_2p95: l20 { + regulator-name = "vreg_l20a_2p95"; + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <2950000>; + regulator-allow-set-load; + }; + vreg_l21a_2p95: l21 { + regulator-name = "vreg_l21a_2p95"; + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <2950000>; + regulator-allow-set-load; + regulator-system-load = <200000>; + }; + vreg_l22a_3p0: l22 { + regulator-name = "vreg_l22a_3p0"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + vreg_l23a_2p8: l23 { + regulator-name = "vreg_l23a_2p8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + vreg_l24a_3p075: l24 { + regulator-name = "vreg_l24a_3p075"; + regulator-min-microvolt = <3075000>; + regulator-max-microvolt = <3075000>; + }; + vreg_l25a_1p2: l25 { + regulator-name = "vreg_l25a_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-allow-set-load; + }; + vreg_l26a_0p8: l27 { + regulator-name = "vreg_l26a_0p8"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + }; + vreg_l28a_0p925: l28 { + regulator-name = "vreg_l28a_0p925"; + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <925000>; + regulator-allow-set-load; + }; + vreg_l29a_2p8: l29 { + regulator-name = "vreg_l29a_2p8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + vreg_l30a_1p8: l30 { + regulator-name = "vreg_l30a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + vreg_l32a_1p8: l32 { + regulator-name = "vreg_l32a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + vreg_lvs1a_1p8: lvs1 { + regulator-name = "vreg_lvs1a_1p8"; + }; + + vreg_lvs2a_1p8: lvs2 { + regulator-name = "vreg_lvs2a_1p8"; + }; + }; + + regulators-1 { + compatible = "qcom,rpm-pmi8994-regulators"; + + vdd_s1-supply = <&vph_pwr>; + vdd_s2-supply = <&vph_pwr>; + vdd_s3-supply = <&vph_pwr>; + vdd_bst_byp-supply = <&vph_pwr>; + + vph_pwr_bbyp: boost-bypass { + regulator-name = "vph_pwr_bbyp"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + vreg_s1b_1p025: s1 { + regulator-name = "vreg_s1b_1p025"; + regulator-min-microvolt = <1025000>; + regulator-max-microvolt = <1025000>; + }; + }; +}; + +&sdhc2 { + /* External SD card */ + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&sdc2_state_on &sdc2_cd_on>; + pinctrl-1 = <&sdc2_state_off &sdc2_cd_off>; + cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; + vmmc-supply = <&vreg_l21a_2p95>; + vqmmc-supply = <&vreg_l13a_2p95>; + status = "okay"; +}; + +&q6asmdai { + dai@0 { + reg = ; + }; + + dai@1 { + reg = ; + }; + + dai@2 { + reg = ; + }; +}; + +&slim_msm { + status = "okay"; + + slim@1 { + reg = <1>; + #address-cells = <2>; + #size-cells = <0>; + + tasha_ifd: tas-ifd@0,0 { + compatible = "slim217,1a0"; + reg = <0 0>; + }; + + wcd9335: codec@1,0 { + compatible = "slim217,1a0"; + reg = <1 0>; + + clock-names = "mclk", "slimbus"; + clocks = <&div1_mclk>, + <&rpmcc RPM_SMD_BB_CLK1>; + interrupt-parent = <&tlmm>; + interrupts = <54 IRQ_TYPE_LEVEL_HIGH>, + <53 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "intr1", "intr2"; + interrupt-controller; + #interrupt-cells = <1>; + + pinctrl-0 = <&cdc_reset_active &wcd_intr_default>; + pinctrl-names = "default"; + + reset-gpios = <&tlmm 64 GPIO_ACTIVE_LOW>; + slim-ifc-dev = <&tasha_ifd>; + + #sound-dai-cells = <1>; + + vdd-buck-supply = <&vreg_s4a_1p8>; + vdd-buck-sido-supply = <&vreg_s4a_1p8>; + vdd-tx-supply = <&vreg_s4a_1p8>; + vdd-rx-supply = <&vreg_s4a_1p8>; + vdd-io-supply = <&vreg_s4a_1p8>; + }; + }; +}; + +&sound { + compatible = "qcom,apq8096-sndcard"; + model = "DB820c"; + audio-routing = "RX_BIAS", "MCLK"; + + mm1-dai-link { + link-name = "MultiMedia1"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + }; + + mm2-dai-link { + link-name = "MultiMedia2"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA2>; + }; + }; + + mm3-dai-link { + link-name = "MultiMedia3"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA3>; + }; + }; + + hdmi-dai-link { + link-name = "HDMI"; + cpu { + sound-dai = <&q6afedai HDMI_RX>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <&mdss_hdmi 0>; + }; + }; + + slim-dai-link { + link-name = "SLIM Playback"; + cpu { + sound-dai = <&q6afedai SLIMBUS_6_RX>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <&wcd9335 AIF4_PB>; + }; + }; + + slimcap-dai-link { + link-name = "SLIM Capture"; + cpu { + sound-dai = <&q6afedai SLIMBUS_0_TX>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <&wcd9335 AIF1_CAP>; + }; + }; +}; + +&ufsphy { + status = "okay"; + + vdda-phy-supply = <&vreg_l28a_0p925>; + vdda-pll-supply = <&vreg_l12a_1p8>; +}; + +&ufshc { + status = "okay"; + + vcc-supply = <&vreg_l20a_2p95>; + vccq-supply = <&vreg_l25a_1p2>; + vccq2-supply = <&vreg_s4a_1p8>; + vdd-hba-supply = <&vreg_l25a_1p2>; + + vcc-max-microamp = <600000>; + vccq-max-microamp = <450000>; + vccq2-max-microamp = <450000>; +}; + +&usb2 { + status = "okay"; + extcon = <&usb2_id>; +}; + +&usb2_dwc3 { + extcon = <&usb2_id>; + dr_mode = "otg"; + maximum-speed = "high-speed"; +}; + +&usb3 { + status = "okay"; + extcon = <&usb3_id>; +}; + +&usb3_dwc3 { + extcon = <&usb3_id>; + dr_mode = "otg"; +}; + +&usb3phy { + status = "okay"; + + vdda-phy-supply = <&vreg_l28a_0p925>; + vdda-pll-supply = <&vreg_l12a_1p8>; +}; + +&venus { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/apq8096sg-db820c.dts b/arch/arm64/boot/dts/qcom/apq8096sg-db820c.dts new file mode 100644 index 000000000000..f3ab5a7c6e53 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/apq8096sg-db820c.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; + +#include "msm8996pro.dtsi" +#include "apq8096-db820c.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. DB820c"; + compatible = "arrow,apq8096sg-db820c", "arrow,apq8096-db820c", + "qcom,apq8096-sbc", "qcom,apq8096sg", "qcom,apq8096"; +}; diff --git a/arch/arm64/boot/dts/qcom/eliza-mtp.dts b/arch/arm64/boot/dts/qcom/eliza-mtp.dts new file mode 100644 index 000000000000..90f629800cb0 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/eliza-mtp.dts @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; + +#include +#include +#include "eliza.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. Eliza MTP"; + compatible = "qcom,eliza-mtp", "qcom,eliza"; + chassis-type = "handset"; + + aliases { + serial0 = &uart14; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + clocks { + xo_board: xo-board { + compatible = "fixed-clock"; + clock-frequency = <76800000>; + #clock-cells = <0>; + }; + + sleep_clk: sleep-clk { + compatible = "fixed-clock"; + clock-frequency = <32764>; + #clock-cells = <0>; + }; + + bi_tcxo_div2: bi-tcxo-div2-clk { + compatible = "fixed-factor-clock"; + #clock-cells = <0>; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-mult = <1>; + clock-div = <2>; + }; + + bi_tcxo_ao_div2: bi-tcxo-ao-div2-clk { + compatible = "fixed-factor-clock"; + #clock-cells = <0>; + + clocks = <&rpmhcc RPMH_CXO_CLK_A>; + clock-mult = <1>; + clock-div = <2>; + }; + }; + + vph_pwr: regulator-vph-pwr { + compatible = "regulator-fixed"; + + regulator-name = "vph_pwr"; + regulator-min-microvolt = <3700000>; + regulator-max-microvolt = <3700000>; + + regulator-always-on; + regulator-boot-on; + }; +}; + +&apps_rsc { + regulators-0 { + compatible = "qcom,pm7550-rpmh-regulators"; + + vdd-l1-supply = <&vreg_s3b>; + vdd-l2-l3-supply = <&vreg_s3b>; + vdd-l4-l5-supply = <&vreg_s2b>; + vdd-l6-supply = <&vreg_s2b>; + vdd-l7-supply = <&vreg_s1b>; + vdd-l8-supply = <&vreg_s1b>; + vdd-l9-l10-supply = <&vreg_s1b>; + vdd-l11-supply = <&vreg_s1b>; + vdd-l12-l14-supply = <&vreg_bob>; + vdd-l13-l16-supply = <&vreg_bob>; + vdd-l15-l17-l18-l19-l20-l21-l22-l23-supply = <&vreg_bob>; + vdd-s1-supply = <&vph_pwr>; + vdd-s2-supply = <&vph_pwr>; + vdd-s3-supply = <&vph_pwr>; + vdd-s4-supply = <&vph_pwr>; + vdd-s5-supply = <&vph_pwr>; + vdd-s6-supply = <&vph_pwr>; + + vdd-bob-supply = <&vph_pwr>; + + qcom,pmic-id = "b"; + + vreg_s1b: smps1 { + regulator-name = "vreg_s1b"; + regulator-min-microvolt = <1850000>; + regulator-max-microvolt = <2040000>; + regulator-initial-mode = ; + }; + + vreg_s2b: smps2 { + regulator-name = "vreg_s2b"; + regulator-min-microvolt = <375000>; + regulator-max-microvolt = <2744000>; + regulator-initial-mode = ; + }; + + vreg_s3b: smps3 { + regulator-name = "vreg_s3b"; + regulator-min-microvolt = <375000>; + regulator-max-microvolt = <2744000>; + regulator-initial-mode = ; + }; + + vreg_s4b: smps4 { + regulator-name = "vreg_s4b"; + regulator-min-microvolt = <2156000>; + regulator-max-microvolt = <2400000>; + regulator-initial-mode = ; + }; + + vreg_l2b: ldo2 { + regulator-name = "vreg_l2b"; + regulator-min-microvolt = <720000>; + regulator-max-microvolt = <950000>; + regulator-initial-mode = ; + }; + + vreg_l3b: ldo3 { + regulator-name = "vreg_l3b"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + }; + + vreg_l4b: ldo4 { + regulator-name = "vreg_l4b"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + + vreg_l6b: ldo6 { + regulator-name = "vreg_l6b"; + regulator-min-microvolt = <866000>; + regulator-max-microvolt = <958000>; + regulator-initial-mode = ; + }; + + vreg_l7b: ldo7 { + regulator-name = "vreg_l7b"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l8b: ldo8 { + regulator-name = "vreg_l8b"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l9b: ldo9 { + regulator-name = "vreg_l9b"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l10b: ldo10 { + regulator-name = "vreg_l10b"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l11b: ldo11 { + regulator-name = "vreg_l11b"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l12b: ldo12 { + regulator-name = "vreg_l12b"; + /* Voltage range for UFS 3.x and above */ + regulator-min-microvolt = <2400000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = ; + }; + + vreg_l13b: ldo13 { + regulator-name = "vreg_l13b"; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = ; + }; + + vreg_l14b: ldo14 { + regulator-name = "vreg_l14b"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3304000>; + regulator-initial-mode = ; + }; + + vreg_l15b: ldo15 { + regulator-name = "vreg_l15b"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3304000>; + regulator-initial-mode = ; + }; + + vreg_l16b: ldo16 { + regulator-name = "vreg_l16b"; + regulator-min-microvolt = <3008000>; + regulator-max-microvolt = <3008000>; + regulator-initial-mode = ; + }; + + vreg_l17b: ldo17 { + regulator-name = "vreg_l17b"; + regulator-min-microvolt = <3104000>; + regulator-max-microvolt = <3104000>; + regulator-initial-mode = ; + }; + + vreg_l18b: ldo18 { + regulator-name = "vreg_l18b"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-initial-mode = ; + }; + + vreg_l19b: ldo19 { + regulator-name = "vreg_l19b"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-initial-mode = ; + }; + + vreg_l20b: ldo20 { + regulator-name = "vreg_l20b"; + regulator-min-microvolt = <1620000>; + regulator-max-microvolt = <3544000>; + regulator-initial-mode = ; + }; + + vreg_l21b: ldo21 { + regulator-name = "vreg_l21b"; + regulator-min-microvolt = <1620000>; + regulator-max-microvolt = <3544000>; + regulator-initial-mode = ; + }; + + vreg_l22b: ldo22 { + regulator-name = "vreg_l22b"; + regulator-min-microvolt = <3200000>; + regulator-max-microvolt = <3200000>; + regulator-initial-mode = ; + }; + + vreg_l23b: ldo23 { + regulator-name = "vreg_l23b"; + regulator-min-microvolt = <1650000>; + regulator-max-microvolt = <3544000>; + regulator-initial-mode = ; + }; + + vreg_bob: bob { + regulator-name = "vreg_bob"; + regulator-min-microvolt = <3008000>; + regulator-max-microvolt = <3960000>; + regulator-initial-mode = ; + }; + }; + + regulators-1 { + compatible = "qcom,pm8550vs-rpmh-regulators"; + + vdd-l1-supply = <&vreg_s2b>; + + qcom,pmic-id = "d"; + + vreg_l1d: ldo1 { + regulator-name = "vreg_l1d"; + regulator-min-microvolt = <1140000>; + regulator-max-microvolt = <1260000>; + regulator-initial-mode = ; + }; + }; + + regulators-2 { + compatible = "qcom,pm8550vs-rpmh-regulators"; + + vdd-l1-supply = <&vreg_s2b>; + vdd-l3-supply = <&vreg_s2b>; + + qcom,pmic-id = "g"; + + vreg_l1g: ldo1 { + regulator-name = "vreg_l1g"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1260000>; + regulator-initial-mode = ; + }; + + vreg_l3g: ldo3 { + regulator-name = "vreg_l3g"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1260000>; + regulator-initial-mode = ; + }; + + }; + + regulators-3 { + compatible = "qcom,pmr735d-rpmh-regulators"; + + vdd-l1-l2-l5-supply = <&vreg_s3b>; + vdd-l3-l4-supply = <&vreg_s2b>; + vdd-l6-supply = <&vreg_s1b>; + vdd-l7-supply = <&vreg_s3b>; + + qcom,pmic-id = "k"; + + vreg_l1k: ldo1 { + regulator-name = "vreg_l1k"; + regulator-min-microvolt = <488000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + }; + + vreg_l2k: ldo2 { + regulator-name = "vreg_l2k"; + regulator-min-microvolt = <920000>; + regulator-max-microvolt = <969000>; + regulator-initial-mode = ; + }; + + vreg_l3k: ldo3 { + regulator-name = "vreg_l3k"; + regulator-min-microvolt = <1080000>; + regulator-max-microvolt = <1350000>; + regulator-initial-mode = ; + }; + + vreg_l4k: ldo4 { + regulator-name = "vreg_l4k"; + regulator-min-microvolt = <960000>; + regulator-max-microvolt = <1980000>; + regulator-initial-mode = ; + }; + + vreg_l5k: ldo5 { + regulator-name = "vreg_l5k"; + regulator-min-microvolt = <866000>; + regulator-max-microvolt = <931000>; + regulator-initial-mode = ; + }; + + vreg_l6k: ldo6 { + regulator-name = "vreg_l6k"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = ; + }; + + vreg_l7k: ldo7 { + regulator-name = "vreg_l7k"; + regulator-min-microvolt = <720000>; + regulator-max-microvolt = <958000>; + regulator-initial-mode = ; + }; + }; +}; + +&tlmm { + gpio-reserved-ranges = <20 4>, /* NFC SPI */ + <111 2>, /* WCN UART1 */ + <118 1>; /* NFC Secure I/O */ +}; + +&uart14 { + compatible = "qcom,geni-debug-uart"; + + status = "okay"; +}; + +&ufs_mem_hc { + reset-gpios = <&tlmm 185 GPIO_ACTIVE_LOW>; + + vcc-supply = <&vreg_l12b>; + vcc-max-microamp = <1300000>; + vccq-supply = <&vreg_l1d>; + vccq-max-microamp = <1200000>; + + status = "okay"; +}; + +&ufs_mem_phy { + vdda-phy-supply = <&vreg_l6b>; + vdda-pll-supply = <&vreg_l4b>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/eliza.dtsi b/arch/arm64/boot/dts/qcom/eliza.dtsi new file mode 100644 index 000000000000..4a7a0ac40ce6 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/eliza.dtsi @@ -0,0 +1,1885 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/ { + interrupt-parent = <&intc>; + + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a520"; + reg = <0x0 0x0>; + + clocks = <&cpufreq_hw 0>; + + power-domains = <&cpu_pd0>; + power-domain-names = "psci"; + + enable-method = "psci"; + next-level-cache = <&l2_0>; + capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <100>; + + qcom,freq-domain = <&cpufreq_hw 0>; + + l2_0: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + next-level-cache = <&l3>; + + l3: l3-cache { + compatible = "cache"; + cache-level = <3>; + cache-unified; + }; + }; + }; + + cpu1: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a520"; + reg = <0x0 0x100>; + + clocks = <&cpufreq_hw 0>; + + power-domains = <&cpu_pd1>; + power-domain-names = "psci"; + + enable-method = "psci"; + next-level-cache = <&l2_0>; + capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <100>; + + qcom,freq-domain = <&cpufreq_hw 0>; + }; + + cpu2: cpu@200 { + device_type = "cpu"; + compatible = "arm,cortex-a520"; + reg = <0x0 0x200>; + + clocks = <&cpufreq_hw 0>; + + power-domains = <&cpu_pd2>; + power-domain-names = "psci"; + + enable-method = "psci"; + next-level-cache = <&l2_2>; + capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <100>; + + qcom,freq-domain = <&cpufreq_hw 0>; + + l2_2: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + next-level-cache = <&l3>; + }; + }; + + cpu3: cpu@300 { + device_type = "cpu"; + compatible = "arm,cortex-a720"; + reg = <0x0 0x300>; + + clocks = <&cpufreq_hw 1>; + + power-domains = <&cpu_pd3>; + power-domain-names = "psci"; + + enable-method = "psci"; + next-level-cache = <&l2_3>; + capacity-dmips-mhz = <1792>; + dynamic-power-coefficient = <238>; + + qcom,freq-domain = <&cpufreq_hw 1>; + + l2_3: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + next-level-cache = <&l3>; + }; + }; + + cpu4: cpu@400 { + device_type = "cpu"; + compatible = "arm,cortex-a720"; + reg = <0x0 0x400>; + + clocks = <&cpufreq_hw 1>; + + power-domains = <&cpu_pd4>; + power-domain-names = "psci"; + + enable-method = "psci"; + next-level-cache = <&l2_4>; + capacity-dmips-mhz = <1792>; + dynamic-power-coefficient = <238>; + + qcom,freq-domain = <&cpufreq_hw 1>; + + l2_4: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + next-level-cache = <&l3>; + }; + }; + + cpu5: cpu@500 { + device_type = "cpu"; + compatible = "arm,cortex-a720"; + reg = <0x0 0x500>; + + clocks = <&cpufreq_hw 1>; + + power-domains = <&cpu_pd5>; + power-domain-names = "psci"; + + enable-method = "psci"; + next-level-cache = <&l2_5>; + capacity-dmips-mhz = <1792>; + dynamic-power-coefficient = <238>; + + qcom,freq-domain = <&cpufreq_hw 1>; + + l2_5: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + next-level-cache = <&l3>; + }; + }; + + cpu6: cpu@600 { + device_type = "cpu"; + compatible = "arm,cortex-a720"; + reg = <0x0 0x600>; + + clocks = <&cpufreq_hw 1>; + + power-domains = <&cpu_pd6>; + power-domain-names = "psci"; + + enable-method = "psci"; + next-level-cache = <&l2_6>; + capacity-dmips-mhz = <1792>; + dynamic-power-coefficient = <238>; + + qcom,freq-domain = <&cpufreq_hw 1>; + + l2_6: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + next-level-cache = <&l3>; + }; + }; + + cpu7: cpu@700 { + device_type = "cpu"; + compatible = "arm,cortex-x3"; + reg = <0x0 0x700>; + + clocks = <&cpufreq_hw 2>; + + power-domains = <&cpu_pd7>; + power-domain-names = "psci"; + + enable-method = "psci"; + next-level-cache = <&l2_7>; + capacity-dmips-mhz = <1894>; + dynamic-power-coefficient = <588>; + + qcom,freq-domain = <&cpufreq_hw 2>; + + l2_7: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + next-level-cache = <&l3>; + }; + }; + + cpu-map { + cluster0 { + core0 { + cpu = <&cpu0>; + }; + + core1 { + cpu = <&cpu1>; + }; + + core2 { + cpu = <&cpu2>; + }; + + core3 { + cpu = <&cpu3>; + }; + + core4 { + cpu = <&cpu4>; + }; + + core5 { + cpu = <&cpu5>; + }; + + core6 { + cpu = <&cpu6>; + }; + + core7 { + cpu = <&cpu7>; + }; + }; + }; + + idle-states { + entry-method = "psci"; + + cluster0_c4: cpu-sleep-0 { + compatible = "arm,idle-state"; + idle-state-name = "silver-rail-power-collapse"; + arm,psci-suspend-param = <0x40000004>; + entry-latency-us = <550>; + exit-latency-us = <750>; + min-residency-us = <6700>; + }; + + cluster1_c4: cpu-sleep-1 { + compatible = "arm,idle-state"; + idle-state-name = "gold-rail-power-collapse"; + arm,psci-suspend-param = <0x40000004>; + entry-latency-us = <550>; + exit-latency-us = <1050>; + min-residency-us = <7951>; + }; + + cluster2_c4: cpu-sleep-2 { + compatible = "arm,idle-state"; + idle-state-name = "gold-plus-rail-power-collapse"; + arm,psci-suspend-param = <0x40000004>; + entry-latency-us = <500>; + exit-latency-us = <1350>; + min-residency-us = <7480>; + }; + }; + + domain-idle-states { + cluster_sleep_0: cluster-sleep-0 { + compatible = "domain-idle-state"; + arm,psci-suspend-param = <0x41000044>; + entry-latency-us = <750>; + exit-latency-us = <2350>; + min-residency-us = <9144>; + }; + + cluster_sleep_1: cluster-sleep-1 { + compatible = "domain-idle-state"; + arm,psci-suspend-param = <0x4100b344>; + entry-latency-us = <2800>; + exit-latency-us = <4400>; + min-residency-us = <10150>; + }; + }; + }; + + firmware { + scm: scm { + compatible = "qcom,scm-eliza", "qcom,scm"; + interconnects = <&aggre2_noc MASTER_CRYPTO QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + qcom,dload-mode = <&tcsr 0x1a000>; + }; + }; + + clk_virt: interconnect-0 { + compatible = "qcom,eliza-clk-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + mc_virt: interconnect-1 { + compatible = "qcom,eliza-mc-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + memory@a0000000 { + device_type = "memory"; + /* We expect the bootloader to fill in the size */ + reg = <0x0 0xa0000000 0x0 0x0>; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + + cpu_pd0: power-domain-cpu0 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&cluster0_c4>; + }; + + cpu_pd1: power-domain-cpu1 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&cluster0_c4>; + }; + + cpu_pd2: power-domain-cpu2 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&cluster0_c4>; + }; + + cpu_pd3: power-domain-cpu3 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&cluster1_c4>; + }; + + cpu_pd4: power-domain-cpu4 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&cluster1_c4>; + }; + + cpu_pd5: power-domain-cpu5 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&cluster1_c4>; + }; + + cpu_pd6: power-domain-cpu6 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&cluster1_c4>; + }; + + cpu_pd7: power-domain-cpu7 { + #power-domain-cells = <0>; + power-domains = <&cluster_pd>; + domain-idle-states = <&cluster2_c4>; + }; + + cluster_pd: power-domain-cluster { + #power-domain-cells = <0>; + domain-idle-states = <&cluster_sleep_0>, + <&cluster_sleep_1>; + }; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gunyah_hyp_mem: gunyah-hyp@80000000 { + reg = <0x0 0x80000000 0x0 0xe00000>; + no-map; + }; + + cpusys_vm_mem: cpusys-vm-mem@80e00000 { + reg = <0x0 0x80e00000 0x0 0x40000>; + no-map; + }; + + cpucp_mem: cpucp@81200000 { + reg = <0x0 0x81200000 0x0 0x200000>; + no-map; + }; + + xbl_dtlog_mem: xbl-dtlog@81a00000 { + reg = <0x0 0x81a00000 0x0 0x40000>; + no-map; + }; + + aop_image_mem: aop-image@81c00000 { + reg = <0x0 0x81c00000 0x0 0x60000>; + no-map; + }; + + aop_cmd_db_mem: aop-cmd-db@81c60000 { + compatible = "qcom,cmd-db"; + reg = <0x0 0x81c60000 0x0 0x20000>; + no-map; + }; + + /* Merged aop_config, tme_crash_dump, tme_log and uefi_log regions */ + aop_tme_uefi_merged_mem: aop-tme-uefi-merged@81c80000 { + reg = <0x0 0x81c80000 0x0 0x74000>; + no-map; + }; + + /* Secdata region can be reused by apps */ + smem_mem: smem@81d00000 { + compatible = "qcom,smem"; + reg = <0x0 0x81d00000 0x0 0x200000>; + hwlocks = <&tcsr_mutex 3>; + no-map; + }; + + cpucp_scandump_mem: cpucp-scandump@82000000 { + reg = <0x0 0x82200000 0x0 0x180000>; + no-map; + }; + + adsp_mhi_mem: adsp-mhi@82380000 { + reg = <0x0 0x82380000 0x0 0x20000>; + no-map; + }; + + soccp_sdi_mem: soccp-sdi@823a0000 { + reg = <0x0 0x823a0000 0x0 0x40000>; + no-map; + }; + + pmic_minii_dump_mem: pmic-minii-dump@823e0000 { + reg = <0x0 0x823e0000 0x0 0x80000>; + no-map; + }; + + pvmfw_mem: pvmfw@824a0000 { + reg = <0x0 0x824a0000 0x0 0x100000>; + no-map; + }; + + hyp_db_mem: hyp-db@825a0000 { + reg = <0x0 0x825a0000 0x0 0x60000>; + no-map; + }; + + global_sync_mem: global-sync@82600000 { + reg = <0x0 0x82600000 0x0 0x100000>; + no-map; + }; + + tz_stat_mem: tz-stat@82700000 { + reg = <0x0 0x82700000 0x0 0x100000>; + no-map; + }; + + qdss_mem: qdss@82800000 { + reg = <0x0 0x82800000 0x0 0x2000000>; + no-map; + }; + + dsm_partition_1_mem: dsm-partition-1@84a00000 { + reg = <0x0 0x84a00000 0x0 0x3700000>; + no-map; + }; + + mpss_mem: mpss@88100000 { + reg = <0x0 0x88100000 0x0 0xcd00000>; + no-map; + }; + + q6_mpss_dtb_mem: q6-mpss-dtb@94e00000 { + reg = <0x0 0x94e00000 0x0 0x80000>; + no-map; + }; + + ipa_fw_mem: ipa-fw@94e80000 { + reg = <0x0 0x94e80000 0x0 0x10000>; + no-map; + }; + + ipa_gsi_mem: ipa-gsi@94e90000 { + reg = <0x0 0x94e90000 0x0 0xa000>; + no-map; + }; + + gpu_micro_code_mem: gpu-micro-code@94e9a000 { + reg = <0x0 0x94e9a000 0x0 0x2000>; + no-map; + }; + + camera_mem: camera@94f00000 { + reg = <0x0 0x94f00000 0x0 0x800000>; + no-map; + }; + + camera_2_mem: camera-2@95700000 { + reg = <0x0 0x95700000 0x0 0x800000>; + no-map; + }; + + video_mem: video@95f00000 { + reg = <0x0 0x95f00000 0x0 0x800000>; + no-map; + }; + + soccp_mem: soccp@96700000 { + reg = <0x0 0x96700000 0x0 0x180000>; + no-map; + }; + + wpss_mem: wpss@97000000 { + reg = <0x0 0x97000000 0x0 0x1900000>; + no-map; + }; + + cdsp_mem: cdsp@98900000 { + reg = <0x0 0x98900000 0x0 0x1400000>; + no-map; + }; + + q6_cdsp_dtb_mem: q6-cdsp-dtb@99d00000 { + reg = <0x0 0x99d00000 0x0 0x80000>; + no-map; + }; + + q6_adsp_dtb_mem: q6-adsp-dtb@99d80000 { + reg = <0x0 0x99d80000 0x0 0x80000>; + no-map; + }; + + adspslpi_mem: adspslpi@99e00000 { + reg = <0x0 0x99e00000 0x0 0x2a00000>; + no-map; + }; + + wlan_msa_mem: wlan-msa@a6400000 { + reg = <0x0 0xa6400000 0x0 0xc00000>; + no-map; + }; + + xbl_ramdump_mem: xbl-ramdump@b8000000 { + reg = <0x0 0xb8000000 0x0 0x1c0000>; + no-map; + }; + + /* Merged tz_reserved, xbl_sc, and qtee regions */ + tz_merged_mem: tz-merged@d8000000 { + reg = <0x0 0xd8000000 0x0 0x600000>; + no-map; + }; + + trust_ui_vm_mem: trust-ui-vm@f3800000 { + reg = <0x0 0xf3800000 0x0 0x4400000>; + no-map; + }; + + oem_vm_mem: oem-vm@f7c00000 { + reg = <0x0 0xf7c00000 0x0 0x4c00000>; + no-map; + }; + + llcc_lpi_mem: llcc-lpi@ff800000 { + reg = <0x0 0xff800000 0x0 0x180000>; + no-map; + }; + }; + + soc: soc@0 { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <2>; + dma-ranges = <0x0 0x0 0x0 0x0 0x10 0x0>; + ranges = <0x0 0x0 0x0 0x0 0x10 0x0>; + + gcc: clock-controller@100000 { + compatible = "qcom,eliza-gcc"; + reg = <0x0 0x00100000 0x0 0x1f4200>; + + clocks = <&bi_tcxo_div2>, + <&sleep_clk>, + <0>, + <0>, + <&ufs_mem_phy 0>, + <&ufs_mem_phy 1>, + <&ufs_mem_phy 2>, + <0>; + + power-domains = <&rpmhpd RPMHPD_CX>; + + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + + qupv3_2: geniqup@8c0000 { + compatible = "qcom,geni-se-qup"; + reg = <0x0 0x008c0000 0x0 0x2000>; + + clocks = <&gcc GCC_QUPV3_WRAP_2_M_AHB_CLK>, + <&gcc GCC_QUPV3_WRAP_2_S_AHB_CLK>; + clock-names = "m-ahb", + "s-ahb"; + + iommus = <&apps_smmu 0x423 0x0>; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + uart14: serial@894000 { + compatible = "qcom,geni-uart"; + reg = <0x0 0x00894000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP2_S5_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config"; + + pinctrl-0 = <&qup_uart14_default>; + pinctrl-names = "default"; + + status = "disabled"; + }; + }; + + config_noc: interconnect@1600000 { + compatible = "qcom,eliza-cnoc-cfg"; + reg = <0x0 0x01600000 0x0 0x5200>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + cnoc_main: interconnect@1500000 { + compatible = "qcom,eliza-cnoc-main"; + reg = <0x0 0x01500000 0x0 0x16080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + system_noc: interconnect@1680000 { + compatible = "qcom,eliza-system-noc"; + reg = <0x0 0x01680000 0x0 0x40000>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + pcie_noc: interconnect@16c0000 { + compatible = "qcom,eliza-pcie-anoc"; + reg = <0x0 0x016c0000 0x0 0x11400>; + qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&gcc GCC_AGGRE_NOC_PCIE_AXI_CLK>, + <&gcc GCC_CFG_NOC_PCIE_ANOC_AHB_CLK>; + #interconnect-cells = <2>; + }; + + aggre1_noc: interconnect@16e0000 { + compatible = "qcom,eliza-aggre1-noc"; + reg = <0x0 0x016e0000 0x0 0x16400>; + qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>, + <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>; + #interconnect-cells = <2>; + }; + + aggre2_noc: interconnect@1700000 { + compatible = "qcom,eliza-aggre2-noc"; + reg = <0x0 0x01700000 0x0 0x1f400>; + qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&rpmhcc RPMH_IPA_CLK>; + #interconnect-cells = <2>; + }; + + mmss_noc: interconnect@1780000 { + compatible = "qcom,eliza-mmss-noc"; + reg = <0x0 0x01780000 0x0 0x7d800>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + ufs_mem_phy: phy@1d80000 { + compatible = "qcom,eliza-qmp-ufs-phy", + "qcom,sm8650-qmp-ufs-phy"; + reg = <0x0 0x01d80000 0x0 0x2000>; + + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_UFS_PHY_PHY_AUX_CLK>, + <&tcsr TCSR_UFS_CLKREF_EN>; + clock-names = "ref", + "ref_aux", + "qref"; + + resets = <&ufs_mem_hc 0>; + reset-names = "ufsphy"; + + power-domains = <&gcc GCC_UFS_MEM_PHY_GDSC>; + + #clock-cells = <1>; + #phy-cells = <0>; + + status = "disabled"; + }; + + ufs_mem_hc: ufshc@1d84000 { + compatible = "qcom,eliza-ufshc", + "qcom,ufshc", + "jedec,ufs-2.0"; + reg = <0x0 0x01d84000 0x0 0x3000>, + <0x0 0x01da0000 0x0 0x15000>; + reg-names = "std", + "mcq"; + + interrupts = ; + + clocks = <&gcc GCC_UFS_PHY_AXI_CLK>, + <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>, + <&gcc GCC_UFS_PHY_UNIPRO_CORE_CLK>, + <&rpmhcc RPMH_LN_BB_CLK3>, + <&gcc GCC_UFS_PHY_TX_SYMBOL_0_CLK>, + <&gcc GCC_UFS_PHY_RX_SYMBOL_0_CLK>, + <&gcc GCC_UFS_PHY_RX_SYMBOL_1_CLK>; + clock-names = "core_clk", + "bus_aggr_clk", + "iface_clk", + "core_clk_unipro", + "ref_clk", + "tx_lane0_sync_clk", + "rx_lane0_sync_clk", + "rx_lane1_sync_clk"; + + operating-points-v2 = <&ufs_opp_table>; + + resets = <&gcc GCC_UFS_PHY_BCR>; + reset-names = "rst"; + + interconnects = <&aggre1_noc MASTER_UFS_MEM QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_UFS_MEM_CFG QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "ufs-ddr", + "cpu-ufs"; + + power-domains = <&gcc GCC_UFS_PHY_GDSC>; + required-opps = <&rpmhpd_opp_nom>; + + iommus = <&apps_smmu 0x60 0x0>; + dma-coherent; + + msi-parent = <&gic_its 0x60>; + + lanes-per-direction = <2>; + qcom,ice = <&ice>; + + phys = <&ufs_mem_phy>; + phy-names = "ufsphy"; + + #reset-cells = <1>; + + status = "disabled"; + + ufs_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-100000000 { + opp-hz = /bits/ 64 <100000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <100000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-201500000 { + opp-hz = /bits/ 64 <201500000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <201500000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-403000000 { + opp-hz = /bits/ 64 <403000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <403000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>; + required-opps = <&rpmhpd_opp_nom>; + }; + }; + }; + + ice: crypto@1d88000 { + compatible = "qcom,eliza-inline-crypto-engine", + "qcom,inline-crypto-engine"; + reg = <0x0 0x01d88000 0x0 0x18000>; + + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + }; + + tcsr_mutex: hwlock@1f40000 { + compatible = "qcom,tcsr-mutex"; + reg = <0x0 0x01f40000 0x0 0x20000>; + #hwlock-cells = <1>; + }; + + tcsr: clock-controller@1fbf000 { + compatible = "qcom,eliza-tcsr", "syscon"; + reg = <0x0 0x01fbf000 0x0 0x21000>; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + + #clock-cells = <1>; + #reset-cells = <1>; + }; + + lpass_ag_noc: interconnect@7e40000 { + compatible = "qcom,eliza-lpass-ag-noc"; + reg = <0x0 0x07e40000 0x0 0xe080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + lpass_lpiaon_noc: interconnect@7400000 { + compatible = "qcom,eliza-lpass-lpiaon-noc"; + reg = <0x0 0x07400000 0x0 0x19080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + lpass_lpicx_noc: interconnect@7420000 { + compatible = "qcom,eliza-lpass-lpicx-noc"; + reg = <0x0 0x07420000 0x0 0x44080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + pdc: interrupt-controller@b220000 { + compatible = "qcom,eliza-pdc", "qcom,pdc"; + reg = <0x0 0x0b220000 0x0 0x40000>, + <0x0 0x174000f0 0x0 0x64>; + + qcom,pdc-ranges = <0 480 8>, <8 719 1>, <9 718 1>, + <10 230 1>, <11 724 1>, <12 716 1>, + <13 727 1>, <14 720 1>, <15 726 1>, + <16 721 1>, <17 262 1>, <18 70 1>, + <19 723 1>, <20 234 1>, <22 725 1>, + <23 231 1>, <24 504 5>, <30 510 8>, + <40 520 6>, <51 531 4>, <58 538 2>, + <61 541 5>, <66 92 1>, <67 547 13>, + <80 240 1>, <81 235 1>, <82 310 2>, + <84 248 1>, <85 241 1>, <86 238 2>, + <88 254 1>, <89 509 1>, <90 563 1>, + <91 259 2>, <93 201 1>, <94 246 1>, + <95 93 1>, <96 611 29>, <125 63 1>, + <126 366 2>, <128 374 1>, <129 377 1>, + <130 428 1>, <131 434 2>, <133 437 1>, + <134 452 2>, <136 458 2>, <138 464 11>, + <149 671 1>, <150 688 1>, <151 714 2>, + <153 722 1>, <154 255 1>, <155 269 2>, + <157 276 1>, <158 287 1>, <159 306 4>; + #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupt-controller; + }; + + tsens0: thermal-sensor@c228000 { + compatible = "qcom,eliza-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c228000 0x0 0x1000>, + <0x0 0x0c222000 0x0 0x1000>; + + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + + #qcom,sensors = <13>; + + #thermal-sensor-cells = <1>; + }; + + tsens1: thermal-sensor@c229000 { + compatible = "qcom,eliza-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c229000 0x0 0x1000>, + <0x0 0x0c223000 0x0 0x1000>; + + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + + #qcom,sensors = <14>; + + #thermal-sensor-cells = <1>; + }; + + tsens2: thermal-sensor@c22a000 { + compatible = "qcom,eliza-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22a000 0x0 0x1000>, + <0x0 0x0c224000 0x0 0x1000>; + + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + + #qcom,sensors = <5>; + + #thermal-sensor-cells = <1>; + }; + + spmi: arbiter@c400000 { + compatible = "qcom,eliza-spmi-pmic-arb", + "qcom,x1e80100-spmi-pmic-arb"; + reg = <0x0 0x0c400000 0x0 0x3000>, + <0x0 0x0c500000 0x0 0x400000>, + <0x0 0x0c440000 0x0 0x80000>; + reg-names = "core", + "chnls", + "obsrvr"; + + qcom,ee = <0>; + qcom,channel = <0>; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + spmi_bus0: spmi@c42d000 { + reg = <0x0 0x0c42d000 0x0 0x4000>, + <0x0 0x0c4c0000 0x0 0x10000>; + reg-names = "cnfg", + "intr"; + + interrupt-names = "periph_irq"; + interrupts-extended = <&pdc 1 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; + #interrupt-cells = <4>; + + #address-cells = <2>; + #size-cells = <0>; + }; + + spmi_bus1: spmi@c432000 { + reg = <0x0 0x0c432000 0x0 0x4000>, + <0x0 0x0c4d0000 0x0 0x10000>; + reg-names = "cnfg", + "intr"; + + interrupt-names = "periph_irq"; + interrupts-extended = <&pdc 3 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; + #interrupt-cells = <4>; + + #address-cells = <2>; + #size-cells = <0>; + }; + }; + + apps_smmu: iommu@15000000 { + compatible = "qcom,eliza-smmu-500", "qcom,smmu-500", "arm,mmu-500"; + reg = <0x0 0x15000000 0x0 0x100000>; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + + #iommu-cells = <2>; + #global-interrupts = <1>; + + dma-coherent; + }; + + intc: interrupt-controller@17100000 { + compatible = "arm,gic-v3"; + reg = <0x0 0x17100000 0x0 0x10000>, + <0x0 0x17180000 0x0 0x200000>; + + interrupts = ; + + #interrupt-cells = <3>; + interrupt-controller; + + #redistributor-regions = <1>; + redistributor-stride = <0x0 0x40000>; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gic_its: msi-controller@17140000 { + compatible = "arm,gic-v3-its"; + reg = <0x0 0x17140000 0x0 0x40000>; + + msi-controller; + #msi-cells = <1>; + }; + }; + + apps_rsc: rsc@17a00000 { + compatible = "qcom,rpmh-rsc"; + reg = <0x0 0x17a00000 0x0 0x10000>, + <0x0 0x17a10000 0x0 0x10000>, + <0x0 0x17a20000 0x0 0x10000>; + reg-names = "drv-0", + "drv-1", + "drv-2"; + + interrupts = , + , + ; + + power-domains = <&cluster_pd>; + label = "apps_rsc"; + + qcom,tcs-offset = <0xd00>; + qcom,drv-id = <2>; + qcom,tcs-config = , + , + , + ; + + apps_bcm_voter: bcm-voter { + compatible = "qcom,bcm-voter"; + }; + + rpmhcc: clock-controller { + compatible = "qcom,eliza-rpmh-clk"; + #clock-cells = <1>; + clocks = <&xo_board>; + clock-names = "xo"; + }; + + rpmhpd: power-controller { + compatible = "qcom,eliza-rpmhpd"; + + operating-points-v2 = <&rpmhpd_opp_table>; + + #power-domain-cells = <1>; + + rpmhpd_opp_table: opp-table { + compatible = "operating-points-v2"; + + rpmhpd_opp_ret: opp-16 { + opp-level = ; + }; + + rpmhpd_opp_min_svs: opp-48 { + opp-level = ; + }; + + rpmhpd_opp_low_svs_d3: opp-50 { + opp-level = ; + }; + + rpmhpd_opp_low_svs_d2: opp-52 { + opp-level = ; + }; + + rpmhpd_opp_low_svs_d1: opp-56 { + opp-level = ; + }; + + rpmhpd_opp_low_svs_d0: opp-60 { + opp-level = ; + }; + + rpmhpd_opp_low_svs: opp-64 { + opp-level = ; + }; + + rpmhpd_opp_low_svs_l1: opp-80 { + opp-level = ; + }; + + rpmhpd_opp_svs: opp-128 { + opp-level = ; + }; + + rpmhpd_opp_svs_l0: opp-144 { + opp-level = ; + }; + + rpmhpd_opp_svs_l1: opp-192 { + opp-level = ; + }; + + rpmhpd_opp_svs_l2: opp-224 { + opp-level = ; + }; + + rpmhpd_opp_nom: opp-256 { + opp-level = ; + }; + + rpmhpd_opp_nom_l1: opp-320 { + opp-level = ; + }; + + rpmhpd_opp_nom_l2: opp-336 { + opp-level = ; + }; + + rpmhpd_opp_turbo: opp-384 { + opp-level = ; + }; + + rpmhpd_opp_turbo_l1: opp-416 { + opp-level = ; + }; + + rpmhpd_opp_turbo_l2: opp-432 { + opp-level = ; + }; + + rpmhpd_opp_turbo_l3: opp-448 { + opp-level = ; + }; + + rpmhpd_opp_turbo_l4: opp-452 { + opp-level = ; + }; + + rpmhpd_opp_super_turbo_no_cpr: opp-480 { + opp-level = ; + }; + }; + }; + }; + + epss_l3: interconnect@17d90000 { + compatible = "qcom,eliza-epss-l3", "qcom,epss-l3"; + reg = <0x0 0x17d90000 0x0 0x1000>; + + clocks = <&bi_tcxo_div2>, <&gcc GCC_GPLL0>; + clock-names = "xo", "alternate"; + + #interconnect-cells = <1>; + }; + + cpufreq_hw: cpufreq@17d91000 { + compatible = "qcom,eliza-cpufreq-epss", "qcom,cpufreq-epss"; + reg = <0x0 0x17d91000 0x0 0x1000>, + <0x0 0x17d92000 0x0 0x1000>, + <0x0 0x17d93000 0x0 0x1000>; + reg-names = "freq-domain0", + "freq-domain1", + "freq-domain2"; + + interrupts = , + , + ; + interrupt-names = "dcvsh-irq-0", + "dcvsh-irq-1", + "dcvsh-irq-2"; + + clocks = <&bi_tcxo_div2>, <&gcc GCC_GPLL0>; + clock-names = "xo", "alternate"; + + #freq-domain-cells = <1>; + #clock-cells = <1>; + }; + + tlmm: pinctrl@f100000 { + compatible = "qcom,eliza-tlmm"; + reg = <0x0 0x0f100000 0x0 0xf00000>; + + interrupts = ; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + gpio-ranges = <&tlmm 0 0 184>; + wakeup-parent = <&pdc>; + + qup_uart14_default: qup-uart14-default-state { + /* TX, RX */ + pins = "gpio18", "gpio19"; + function = "qup2_se5"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + gem_noc: interconnect@24100000 { + compatible = "qcom,eliza-gem-noc"; + reg = <0x0 0x24100000 0x0 0x163080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + nsp_noc: interconnect@320c0000 { + compatible = "qcom,eliza-nsp-noc"; + reg = <0x0 0x320c0000 0x0 0xe080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + }; + + thermal-zones { + aoss0-thermal { + thermal-sensors = <&tsens0 0>; + + trips { + aoss-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + aoss-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + aoss1-thermal { + thermal-sensors = <&tsens1 0>; + + trips { + aoss-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + aoss-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + aoss2-thermal { + thermal-sensors = <&tsens2 0>; + + trips { + aoss-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + aoss-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + camera0-thermal { + thermal-sensors = <&tsens1 12>; + + trips { + camera-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + camera-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + camera1-thermal { + thermal-sensors = <&tsens1 13>; + + trips { + camera-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + camera-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu0-thermal { + thermal-sensors = <&tsens1 1>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu1-thermal { + thermal-sensors = <&tsens1 2>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu2-thermal { + thermal-sensors = <&tsens1 3>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu3-top-thermal { + thermal-sensors = <&tsens0 3>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu3-bottom-thermal { + thermal-sensors = <&tsens0 4>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu4-top-thermal { + thermal-sensors = <&tsens0 5>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu4-bottom-thermal { + thermal-sensors = <&tsens0 6>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu5-top-thermal { + thermal-sensors = <&tsens0 7>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu5-bottom-thermal { + thermal-sensors = <&tsens0 8>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu6-top-thermal { + thermal-sensors = <&tsens0 9>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu6-bottom-thermal { + thermal-sensors = <&tsens0 10>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu7-top-thermal { + thermal-sensors = <&tsens0 11>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu7-bottom-thermal { + thermal-sensors = <&tsens0 12>; + + trips { + cpu-critical { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpuss0-thermal { + thermal-sensors = <&tsens0 1>; + + trips { + cpuss-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + cpuss-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpuss1-thermal { + thermal-sensors = <&tsens0 2>; + + trips { + cpuss-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + cpuss-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + ddr-thermal { + thermal-sensors = <&tsens1 11>; + + trips { + ddr-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + ddr-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss0-thermal { + polling-delay-passive = <10>; + + thermal-sensors = <&tsens1 8>; + + trips { + gpu-alert { + temperature = <95000>; + hysteresis = <1000>; + type = "passive"; + }; + + gpu-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + gpu-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss1-thermal { + polling-delay-passive = <10>; + + thermal-sensors = <&tsens1 9>; + + trips { + gpu-alert { + temperature = <95000>; + hysteresis = <1000>; + type = "passive"; + }; + + gpu-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + gpu-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + modem0-thermal { + thermal-sensors = <&tsens2 1>; + + trips { + modem-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + modem-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + modem1-thermal { + thermal-sensors = <&tsens2 2>; + + trips { + modem-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + modem-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + modem2-thermal { + thermal-sensors = <&tsens2 3>; + + trips { + modem-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + modem-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + modem3-thermal { + thermal-sensors = <&tsens2 4>; + + trips { + modem-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + modem-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphmx0-thermal { + thermal-sensors = <&tsens1 6>; + + trips { + nsphmx-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + nsphmx-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphmx1-thermal { + thermal-sensors = <&tsens1 7>; + + trips { + nsphmx-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + nsphmx-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphvx0-thermal { + thermal-sensors = <&tsens1 4>; + + trips { + nsphvx-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + nsphvx-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphvx1-thermal { + thermal-sensors = <&tsens1 5>; + + trips { + nsphvx-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + nsphvx-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + video-thermal { + thermal-sensors = <&tsens1 10>; + + trips { + video-hot { + temperature = <110000>; + hysteresis = <1000>; + type = "hot"; + }; + + video-critical { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + + interrupts = , + , + , + ; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/glymur-crd.dts b/arch/arm64/boot/dts/qcom/glymur-crd.dts new file mode 100644 index 000000000000..35aaf09e4e2b --- /dev/null +++ b/arch/arm64/boot/dts/qcom/glymur-crd.dts @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; + +#include "glymur.dtsi" +#include "glymur-crd.dtsi" + +#include + +/ { + model = "Qualcomm Technologies, Inc. Glymur CRD"; + compatible = "qcom,glymur-crd", "qcom,glymur"; + + pmic-glink { + compatible = "qcom,glymur-pmic-glink", + "qcom,pmic-glink"; + #address-cells = <1>; + #size-cells = <0>; + + connector@0 { + compatible = "usb-c-connector"; + reg = <0>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pmic_glink_hs_in: endpoint { + remote-endpoint = <&usb_0_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + pmic_glink_ss_in: endpoint { + remote-endpoint = <&usb_0_qmpphy_out>; + }; + }; + }; + }; + + connector@1 { + compatible = "usb-c-connector"; + reg = <1>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pmic_glink_hs_in1: endpoint { + remote-endpoint = <&usb_1_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + pmic_glink_ss_in1: endpoint { + remote-endpoint = <&usb_1_qmpphy_out>; + }; + }; + }; + }; + }; + + vreg_edp_3p3: regulator-edp-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_EDP_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 70 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&edp_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_misc_3p3: regulator-misc-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_MISC_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&pmh0110_f_e0_gpios 6 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&misc_3p3_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; +}; + +&i2c0 { + clock-frequency = <400000>; + + status = "okay"; + + touchpad@2c { + compatible = "hid-over-i2c"; + reg = <0x2c>; + + hid-descr-addr = <0x20>; + interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; + + vdd-supply = <&vreg_misc_3p3>; + vddl-supply = <&vreg_l15b_e0_1p8>; + + pinctrl-0 = <&tpad_default>; + pinctrl-names = "default"; + + wakeup-source; + }; + + keyboard@3a { + compatible = "hid-over-i2c"; + reg = <0x3a>; + + hid-descr-addr = <0x1>; + interrupts-extended = <&tlmm 67 IRQ_TYPE_LEVEL_LOW>; + + vdd-supply = <&vreg_misc_3p3>; + vddl-supply = <&vreg_l15b_e0_1p8>; + + pinctrl-0 = <&kybd_default>; + pinctrl-names = "default"; + + wakeup-source; + }; +}; + +&i2c8 { + clock-frequency = <400000>; + + status = "okay"; + + touchscreen@38 { + compatible = "hid-over-i2c"; + reg = <0x38>; + + hid-descr-addr = <0x1>; + interrupts-extended = <&tlmm 51 IRQ_TYPE_LEVEL_LOW>; + + vdd-supply = <&vreg_misc_3p3>; + vddl-supply = <&vreg_l15b_e0_1p8>; + + pinctrl-0 = <&ts0_default>; + pinctrl-names = "default"; + }; +}; + +&i2c5 { + clock-frequency = <400000>; + + status = "okay"; + + ptn3222_0: redriver@43 { + compatible = "nxp,ptn3222"; + reg = <0x43>; + + reset-gpios = <&tlmm 8 GPIO_ACTIVE_LOW>; + + vdd3v3-supply = <&vreg_l8b_e0_1p50>; + vdd1v8-supply = <&vreg_l15b_e0_1p8>; + + #phy-cells = <0>; + }; + + ptn3222_1: redriver@47 { + compatible = "nxp,ptn3222"; + reg = <0x47>; + + reset-gpios = <&tlmm 9 GPIO_ACTIVE_LOW>; + + vdd3v3-supply = <&vreg_l8b_e0_1p50>; + vdd1v8-supply = <&vreg_l15b_e0_1p8>; + + #phy-cells = <0>; + }; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dp0 { + status = "okay"; +}; + +&mdss_dp0_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp1 { + status = "okay"; +}; + +&mdss_dp1_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp3 { + /delete-property/ #sound-dai-cells; + + status = "okay"; + + aux-bus { + panel { + compatible = "samsung,atna60cl08", "samsung,atna33xc20"; + enable-gpios = <&tlmm 18 GPIO_ACTIVE_HIGH>; + power-supply = <&vreg_edp_3p3>; + + pinctrl-0 = <&edp_bl_en>; + pinctrl-names = "default"; + + port { + edp_panel_in: endpoint { + remote-endpoint = <&mdss_dp3_out>; + }; + }; + }; + }; +}; + +&mdss_dp3_out { + data-lanes = <0 1 2 3>; + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; + + remote-endpoint = <&edp_panel_in>; +}; + +&mdss_dp3_phy { + vdda-phy-supply = <&vreg_l2f_e1_0p83>; + vdda-pll-supply = <&vreg_l4f_e1_1p08>; + + status = "okay"; +}; + +&pmh0110_f_e0_gpios { + misc_3p3_reg_en: misc-3p3-reg-en-state { + pins = "gpio6"; + function = "normal"; + bias-disable; + input-disable; + output-enable; + drive-push-pull; + power-source = <1>; /* 1.8 V */ + qcom,drive-strength = ; + }; +}; + +&smb2370_j_e2_eusb2_repeater { + vdd18-supply = <&vreg_l15b_e0_1p8>; + vdd3-supply = <&vreg_l7b_e0_2p79>; +}; + +&smb2370_k_e2_eusb2_repeater { + vdd18-supply = <&vreg_l15b_e0_1p8>; + vdd3-supply = <&vreg_l7b_e0_2p79>; +}; + +&tlmm { + edp_bl_en: edp-bl-en-state { + pins = "gpio18"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + edp_reg_en: edp-reg-en-state { + pins = "gpio70"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + kybd_default: kybd-default-state { + pins = "gpio67"; + function = "gpio"; + bias-disable; + }; + + tpad_default: tpad-default-state { + pins = "gpio3"; + function = "gpio"; + bias-disable; + }; + + ts0_default: ts0-default-state { + int-n-pins { + pins = "gpio51"; + function = "gpio"; + bias-disable; + }; + + reset-n-pins { + pins = "gpio48"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + }; +}; + +&usb_0 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_0_dwc3_hs { + remote-endpoint = <&pmic_glink_hs_in>; +}; + +&usb_0_hsphy { + vdd-supply = <&vreg_l3f_e0_0p72>; + vdda12-supply = <&vreg_l4h_e0_1p2>; + + phys = <&smb2370_j_e2_eusb2_repeater>; + + status = "okay"; +}; + +&usb_0_qmpphy { + vdda-phy-supply = <&vreg_l4h_e0_1p2>; + vdda-pll-supply = <&vreg_l3f_e0_0p72>; + refgen-supply = <&vreg_l2f_e0_0p82>; + + status = "okay"; +}; + +&usb_0_qmpphy_out { + remote-endpoint = <&pmic_glink_ss_in>; +}; + +&usb_1 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_1_dwc3_hs { + remote-endpoint = <&pmic_glink_hs_in1>; +}; + +&usb_1_hsphy { + vdd-supply = <&vreg_l3f_e0_0p72>; + vdda12-supply = <&vreg_l4h_e0_1p2>; + + phys = <&smb2370_k_e2_eusb2_repeater>; + + status = "okay"; +}; + +&usb_1_qmpphy { + vdda-phy-supply = <&vreg_l4h_e0_1p2>; + vdda-pll-supply = <&vreg_l1h_e0_0p89>; + refgen-supply = <&vreg_l2f_e0_0p82>; + + status = "okay"; +}; + +&usb_1_qmpphy_out { + remote-endpoint = <&pmic_glink_ss_in1>; +}; + +&usb_hs { + status = "okay"; +}; + +&usb_hs_phy { + vdd-supply = <&vreg_l2h_e0_0p72>; + vdda12-supply = <&vreg_l4h_e0_1p2>; + + phys = <&ptn3222_1>; + + status = "okay"; +}; + +&usb_mp { + status = "okay"; +}; + +&usb_mp_hsphy0 { + vdd-supply = <&vreg_l2h_e0_0p72>; + vdda12-supply = <&vreg_l4h_e0_1p2>; + + phys = <&ptn3222_0>; + + status = "okay"; +}; + +&usb_mp_hsphy1 { + vdd-supply = <&vreg_l2h_e0_0p72>; + vdda12-supply = <&vreg_l4h_e0_1p2>; + + status = "okay"; +}; + +&usb_mp_qmpphy0 { + vdda-phy-supply = <&vreg_l4h_e0_1p2>; + vdda-pll-supply = <&vreg_l2h_e0_0p72>; + refgen-supply = <&vreg_l4f_e1_1p08>; + + status = "okay"; +}; + +&usb_mp_qmpphy1 { + vdda-phy-supply = <&vreg_l4h_e0_1p2>; + vdda-pll-supply = <&vreg_l2h_e0_0p72>; + refgen-supply = <&vreg_l4f_e1_1p08>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/glymur-crd.dtsi b/arch/arm64/boot/dts/qcom/glymur-crd.dtsi new file mode 100644 index 000000000000..2852d257ac8c --- /dev/null +++ b/arch/arm64/boot/dts/qcom/glymur-crd.dtsi @@ -0,0 +1,697 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include "pmcx0102.dtsi" /* SPMI0: SID-2/3 SPMI1: SID-2/3 */ +#include "pmh0101.dtsi" /* SPMI0: SID-1 */ +#include "pmh0110-glymur.dtsi" /* SPMI0: SID-5/7 SPMI1: SID-5 */ +#include "pmh0104-glymur.dtsi" /* SPMI0: SID-8/9 SPMI1: SID-11 */ +#include "pmk8850.dtsi" /* SPMI0: SID-0 */ +#include "smb2370.dtsi" /* SPMI2: SID-9/10/11 */ + +/ { + model = "Qualcomm Technologies, Inc. Glymur CRD"; + compatible = "qcom,glymur-crd", "qcom,glymur"; + + aliases { + serial0 = &uart21; + serial1 = &uart14; + i2c0 = &i2c0; + i2c1 = &i2c4; + i2c2 = &i2c5; + spi0 = &spi18; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + clocks { + xo_board: xo-board { + compatible = "fixed-clock"; + clock-frequency = <38400000>; + #clock-cells = <0>; + }; + + sleep_clk: sleep-clk { + compatible = "fixed-clock"; + clock-frequency = <32000>; + #clock-cells = <0>; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&key_vol_up_default>; + pinctrl-names = "default"; + + key-volume-up { + label = "Volume Up"; + linux,code = ; + gpios = <&pmh0101_gpios 6 GPIO_ACTIVE_LOW>; + debounce-interval = <15>; + linux,can-disable; + wakeup-source; + }; + }; + + vreg_nvme: regulator-nvme { + compatible = "regulator-fixed"; + + regulator-name = "VREG_NVME_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&pmh0101_gpios 14 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&nvme_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_nvmesec: regulator-nvmesec { + compatible = "regulator-fixed"; + + regulator-name = "VREG_NVME_SEC_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&pmh0110_f_e1_gpios 14 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&nvme_sec_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_wcn_0p95: regulator-wcn-0p95 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_0P95"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <950000>; + + vin-supply = <&vreg_wcn_3p3>; + }; + + vreg_wcn_3p3: regulator-wcn-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 94 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&wcn_sw_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_wwan: regulator-wwan { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WWAN_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 246 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&wwan_reg_en>; + pinctrl-names = "default"; + }; + + wcn7850-pmu { + compatible = "qcom,wcn7850-pmu"; + + vdd-supply = <&vreg_wcn_0p95>; + vddio-supply = <&vreg_l15b_e0_1p8>; + vddaon-supply = <&vreg_l15b_e0_1p8>; + vdddig-supply = <&vreg_l15b_e0_1p8>; + vddrfa1p2-supply = <&vreg_l15b_e0_1p8>; + vddrfa1p8-supply = <&vreg_l15b_e0_1p8>; + + wlan-enable-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>; + bt-enable-gpios = <&tlmm 116 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&wcn_wlan_bt_en>; + pinctrl-names = "default"; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p8: ldo7 { + regulator-name = "vreg_pmu_rfa_1p8"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; +}; + +&apps_rsc { + regulators-0 { + compatible = "qcom,pmh0101-rpmh-regulators"; + qcom,pmic-id = "B_E0"; + + vreg_bob1_e0: bob1 { + regulator-name = "vreg_bob1_e0"; + regulator-min-microvolt = <2200000>; + regulator-max-microvolt = <4224000>; + regulator-initial-mode = ; + }; + + vreg_bob2_e0: bob2 { + regulator-name = "vreg_bob2_e0"; + regulator-min-microvolt = <2540000>; + regulator-max-microvolt = <3600000>; + regulator-initial-mode = ; + }; + + vreg_l1b_e0_1p8: ldo1 { + regulator-name = "vreg_l1b_e0_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l2b_e0_2p9: ldo2 { + regulator-name = "vreg_l2b_e0_2p9"; + regulator-min-microvolt = <2904000>; + regulator-max-microvolt = <2904000>; + regulator-initial-mode = ; + }; + + vreg_l7b_e0_2p79: ldo7 { + regulator-name = "vreg_l7b_e0_2p79"; + regulator-min-microvolt = <2790000>; + regulator-max-microvolt = <2792000>; + regulator-initial-mode = ; + }; + + vreg_l8b_e0_1p50: ldo8 { + regulator-name = "vreg_l8b_e0_1p50"; + regulator-min-microvolt = <1504000>; + regulator-max-microvolt = <1504000>; + regulator-initial-mode = ; + }; + + vreg_l9b_e0_2p7: ldo9 { + regulator-name = "vreg_l9b_e0_2p7"; + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2704000>; + regulator-initial-mode = ; + }; + + vreg_l10b_e0_1p8: ldo10 { + regulator-name = "vreg_l10b_e0_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l11b_e0_1p2: ldo11 { + regulator-name = "vreg_l11b_e0_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + + vreg_l12b_e0_1p14: ldo12 { + regulator-name = "vreg_l12b_e0_1p14"; + regulator-min-microvolt = <1144000>; + regulator-max-microvolt = <1144000>; + regulator-initial-mode = ; + }; + + vreg_l15b_e0_1p8: ldo15 { + regulator-name = "vreg_l15b_e0_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l17b_e0_2p4: ldo17 { + regulator-name = "vreg_l17b_e0_2p4"; + regulator-min-microvolt = <2400000>; + regulator-max-microvolt = <2700000>; + regulator-initial-mode = ; + }; + + vreg_l18b_e0_1p2: ldo18 { + regulator-name = "vreg_l18b_e0_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + }; + + regulators-1 { + compatible = "qcom,pmcx0102-rpmh-regulators"; + qcom,pmic-id = "C_E1"; + + vreg_l1c_e1_0p82: ldo1 { + regulator-name = "vreg_l1c_e1_0p82"; + regulator-min-microvolt = <832000>; + regulator-max-microvolt = <832000>; + regulator-initial-mode = ; + }; + + vreg_l2c_e1_1p14: ldo2 { + regulator-name = "vreg_l2c_e1_1p14"; + regulator-min-microvolt = <1144000>; + regulator-max-microvolt = <1144000>; + regulator-initial-mode = ; + }; + + vreg_l3c_e1_0p89: ldo3 { + regulator-name = "vreg_l3c_e1_0p89"; + regulator-min-microvolt = <890000>; + regulator-max-microvolt = <980000>; + regulator-initial-mode = ; + }; + + vreg_l4c_e1_0p72: ldo4 { + regulator-name = "vreg_l4c_e1_0p72"; + regulator-min-microvolt = <720000>; + regulator-max-microvolt = <720000>; + regulator-initial-mode = ; + }; + }; + + regulators-2 { + compatible = "qcom,pmh0110-rpmh-regulators"; + qcom,pmic-id = "F_E0"; + + vreg_s7f_e0_1p32: smps7 { + regulator-name = "vreg_s7f_e0_1p32"; + regulator-min-microvolt = <1320000>; + regulator-max-microvolt = <1352000>; + regulator-initial-mode = ; + }; + + vreg_s8f_e0_0p95: smps8 { + regulator-name = "vreg_s8f_e0_0p95"; + regulator-min-microvolt = <952000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + + vreg_s9f_e0_1p9: smps9 { + regulator-name = "vreg_s9f_e0_1p9"; + regulator-min-microvolt = <1900000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = ; + }; + + vreg_l2f_e0_0p82: ldo2 { + regulator-name = "vreg_l2f_e0_0p82"; + regulator-min-microvolt = <832000>; + regulator-max-microvolt = <832000>; + regulator-initial-mode = ; + }; + + vreg_l3f_e0_0p72: ldo3 { + regulator-name = "vreg_l3f_e0_0p72"; + regulator-min-microvolt = <720000>; + regulator-max-microvolt = <720000>; + regulator-initial-mode = ; + }; + + vreg_l4f_e0_0p3: ldo4 { + regulator-name = "vreg_l4f_e0_0p3"; + regulator-min-microvolt = <1080000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + }; + + regulators-3 { + compatible = "qcom,pmh0110-rpmh-regulators"; + qcom,pmic-id = "F_E1"; + + vreg_s7f_e1_0p3: smps7 { + regulator-name = "vreg_s7f_e1_0p3"; + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + + vreg_l1f_e1_0p82: ldo1 { + regulator-name = "vreg_l1f_e1_0p82"; + regulator-min-microvolt = <832000>; + regulator-max-microvolt = <832000>; + regulator-initial-mode = ; + }; + + vreg_l2f_e1_0p83: ldo2 { + regulator-name = "vreg_l2f_e1_0p83"; + regulator-min-microvolt = <832000>; + regulator-max-microvolt = <832000>; + regulator-initial-mode = ; + }; + + vreg_l4f_e1_1p08: ldo4 { + regulator-name = "vreg_l4f_e1_1p08"; + regulator-min-microvolt = <1080000>; + regulator-max-microvolt = <1320000>; + regulator-initial-mode = ; + }; + }; + + regulators-4 { + compatible = "qcom,pmh0110-rpmh-regulators"; + qcom,pmic-id = "H_E0"; + + vreg_l1h_e0_0p89: ldo1 { + regulator-name = "vreg_l1h_e0_0p89"; + regulator-min-microvolt = <832000>; + regulator-max-microvolt = <832000>; + regulator-initial-mode = ; + }; + + vreg_l2h_e0_0p72: ldo2 { + regulator-name = "vreg_l2h_e0_0p72"; + regulator-min-microvolt = <832000>; + regulator-max-microvolt = <832000>; + regulator-initial-mode = ; + }; + + vreg_l3h_e0_0p32: ldo3 { + regulator-name = "vreg_l3h_e0_0p32"; + regulator-min-microvolt = <320000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = ; + }; + + vreg_l4h_e0_1p2: ldo4 { + regulator-name = "vreg_l4h_e0_1p2"; + regulator-min-microvolt = <1080000>; + regulator-max-microvolt = <1320000>; + regulator-initial-mode = ; + }; + }; +}; + +&pcie3b { + vddpe-3v3-supply = <&vreg_nvmesec>; + + pinctrl-0 = <&pcie3b_default>; + pinctrl-names = "default"; +}; + +&pcie3b_phy { + vdda-phy-supply = <&vreg_l3c_e1_0p89>; + vdda-pll-supply = <&vreg_l2c_e1_1p14>; +}; + +&pcie3b_port0 { + reset-gpios = <&tlmm 155 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 157 GPIO_ACTIVE_LOW>; +}; + +&pcie4 { + pinctrl-0 = <&pcie4_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie4_phy { + vdda-phy-supply = <&vreg_l1c_e1_0p82>; + vdda-pll-supply = <&vreg_l4f_e1_1p08>; + + status = "okay"; +}; + +&pcie4_port0 { + reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; + + wifi@0 { + compatible = "pci17cb,1107"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + }; +}; + +&pcie5 { + vddpe-3v3-supply = <&vreg_nvme>; + + pinctrl-0 = <&pcie5_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie5_phy { + vdda-phy-supply = <&vreg_l2f_e0_0p82>; + vdda-pll-supply = <&vreg_l4h_e0_1p2>; + + status = "okay"; +}; + +&pcie5_port0 { + reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +}; + +&pcie6 { + vddpe-3v3-supply = <&vreg_wwan>; + + pinctrl-0 = <&pcie6_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie6_phy { + vdda-phy-supply = <&vreg_l1c_e1_0p82>; + vdda-pll-supply = <&vreg_l4f_e1_1p08>; + + status = "okay"; +}; + +&pcie6_port0 { + reset-gpios = <&tlmm 149 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 151 GPIO_ACTIVE_LOW>; +}; + +&pmh0101_gpios { + nvme_reg_en: nvme-reg-en-state { + pins = "gpio14"; + function = "normal"; + bias-disable; + }; +}; + +&pmh0110_f_e1_gpios { + nvme_sec_reg_en: nvme-reg-en-state { + pins = "gpio14"; + function = "normal"; + bias-disable; + }; +}; + +&pmh0101_gpios { + key_vol_up_default: key-vol-up-default-state { + pins = "gpio6"; + function = "normal"; + output-disable; + bias-pull-up; + }; +}; + +&pmk8850_rtc { + qcom,no-alarm; +}; + +&pon_resin { + linux,code = ; + status = "okay"; +}; + +&tlmm { + gpio-reserved-ranges = <4 4>, /* EC TZ Secure I3C */ + <10 2>, /* OOB UART */ + <44 4>; /* Security SPI (TPM) */ + + pcie4_default: pcie4-default-state { + clkreq-n-pins { + pins = "gpio147"; + function = "pcie4_clk_req_n"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio146"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio148"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie5_default: pcie5-default-state { + clkreq-n-pins { + pins = "gpio153"; + function = "pcie5_clk_req_n"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio152"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio154"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie6_default: pcie6-default-state { + clkreq-n-pins { + pins = "gpio150"; + function = "pcie6_clk_req_n"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio149"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio151"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie3b_default: pcie3b-default-state { + clkreq-n-pins { + pins = "gpio156"; + function = "pcie3b_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio155"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio157"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + wcn_wlan_bt_en: wcn-wlan-bt-en-state { + pins = "gpio116", "gpio117"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wcn_sw_en: wcn-sw-en-state { + pins = "gpio94"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wwan_reg_en: wwan-reg-en-state { + pins = "gpio246"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; +}; + +&uart14 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn7850-bt"; + max-speed = <3200000>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi new file mode 100644 index 000000000000..f23cf81ddb77 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi @@ -0,0 +1,7135 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glymur-ipcc.h" + +/ { + interrupt-parent = <&intc>; + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "qcom,oryon-2-2"; + reg = <0x0 0x0>; + enable-method = "psci"; + power-domains = <&cpu_pd0>, <&scmi_perf 0>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_0>; + + l2_0: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + }; + }; + + cpu1: cpu@100 { + device_type = "cpu"; + compatible = "qcom,oryon-2-2"; + reg = <0x0 0x100>; + enable-method = "psci"; + power-domains = <&cpu_pd1>, <&scmi_perf 0>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_0>; + }; + + cpu2: cpu@200 { + device_type = "cpu"; + compatible = "qcom,oryon-2-2"; + reg = <0x0 0x200>; + enable-method = "psci"; + power-domains = <&cpu_pd2>, <&scmi_perf 0>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_0>; + }; + + cpu3: cpu@300 { + device_type = "cpu"; + compatible = "qcom,oryon-2-2"; + reg = <0x0 0x300>; + enable-method = "psci"; + power-domains = <&cpu_pd3>, <&scmi_perf 0>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_0>; + }; + + cpu4: cpu@400 { + device_type = "cpu"; + compatible = "qcom,oryon-2-2"; + reg = <0x0 0x400>; + enable-method = "psci"; + power-domains = <&cpu_pd4>, <&scmi_perf 0>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_0>; + }; + + cpu5: cpu@500 { + device_type = "cpu"; + compatible = "qcom,oryon-2-2"; + reg = <0x0 0x500>; + enable-method = "psci"; + power-domains = <&cpu_pd5>, <&scmi_perf 0>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_0>; + }; + + cpu6: cpu@10000 { + device_type = "cpu"; + compatible = "qcom,oryon-2-1"; + reg = <0x0 0x10000>; + enable-method = "psci"; + power-domains = <&cpu_pd6>, <&scmi_perf 1>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_1>; + + l2_1: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + }; + }; + + cpu7: cpu@10100 { + device_type = "cpu"; + compatible = "qcom,oryon-2-1"; + reg = <0x0 0x10100>; + enable-method = "psci"; + power-domains = <&cpu_pd7>, <&scmi_perf 1>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_1>; + }; + + cpu8: cpu@10200 { + device_type = "cpu"; + compatible = "qcom,oryon-2-1"; + reg = <0x0 0x10200>; + enable-method = "psci"; + power-domains = <&cpu_pd8>, <&scmi_perf 1>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_1>; + }; + + cpu9: cpu@10300 { + device_type = "cpu"; + compatible = "qcom,oryon-2-1"; + reg = <0x0 0x10300>; + enable-method = "psci"; + power-domains = <&cpu_pd9>, <&scmi_perf 1>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_1>; + }; + + cpu10: cpu@10400 { + device_type = "cpu"; + compatible = "qcom,oryon-2-1"; + reg = <0x0 0x10400>; + enable-method = "psci"; + power-domains = <&cpu_pd10>, <&scmi_perf 1>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_1>; + }; + + cpu11: cpu@10500 { + device_type = "cpu"; + compatible = "qcom,oryon-2-1"; + reg = <0x0 0x10500>; + enable-method = "psci"; + power-domains = <&cpu_pd11>, <&scmi_perf 1>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_1>; + }; + + cpu12: cpu@20000 { + device_type = "cpu"; + compatible = "qcom,oryon-2-1"; + reg = <0x0 0x20000>; + enable-method = "psci"; + power-domains = <&cpu_pd12>, <&scmi_perf 2>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_2>; + + l2_2: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + }; + }; + + cpu13: cpu@20100 { + device_type = "cpu"; + compatible = "qcom,oryon-2-1"; + reg = <0x0 0x20100>; + enable-method = "psci"; + power-domains = <&cpu_pd13>, <&scmi_perf 2>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_2>; + }; + + cpu14: cpu@20200 { + device_type = "cpu"; + compatible = "qcom,oryon-2-1"; + reg = <0x0 0x20200>; + enable-method = "psci"; + power-domains = <&cpu_pd14>, <&scmi_perf 2>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_2>; + }; + + cpu15: cpu@20300 { + device_type = "cpu"; + compatible = "qcom,oryon-2-1"; + reg = <0x0 0x20300>; + enable-method = "psci"; + power-domains = <&cpu_pd15>, <&scmi_perf 2>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_2>; + }; + + cpu16: cpu@20400 { + device_type = "cpu"; + compatible = "qcom,oryon-2-1"; + reg = <0x0 0x20400>; + enable-method = "psci"; + power-domains = <&cpu_pd16>, <&scmi_perf 2>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_2>; + }; + + cpu17: cpu@20500 { + device_type = "cpu"; + compatible = "qcom,oryon-2-1"; + reg = <0x0 0x20500>; + enable-method = "psci"; + power-domains = <&cpu_pd17>, <&scmi_perf 2>; + power-domain-names = "psci", "perf"; + next-level-cache = <&l2_2>; + }; + + cpu-map { + cluster0 { + core0 { + cpu = <&cpu0>; + }; + + core1 { + cpu = <&cpu1>; + }; + + core2 { + cpu = <&cpu2>; + }; + + core3 { + cpu = <&cpu3>; + }; + + core4 { + cpu = <&cpu4>; + }; + + core5 { + cpu = <&cpu5>; + }; + }; + + cluster1 { + core0 { + cpu = <&cpu6>; + }; + + core1 { + cpu = <&cpu7>; + }; + + core2 { + cpu = <&cpu8>; + }; + + core3 { + cpu = <&cpu9>; + }; + + core4 { + cpu = <&cpu10>; + }; + + core5 { + cpu = <&cpu11>; + }; + }; + + cpu_map_cluster2: cluster2 { + core0 { + cpu = <&cpu12>; + }; + + core1 { + cpu = <&cpu13>; + }; + + core2 { + cpu = <&cpu14>; + }; + + core3 { + cpu = <&cpu15>; + }; + + core4 { + cpu = <&cpu16>; + }; + + core5 { + cpu = <&cpu17>; + }; + }; + }; + + idle-states { + entry-method = "psci"; + + cpu_c4: cpu-sleep-0 { + compatible = "arm,idle-state"; + idle-state-name = "ret"; + arm,psci-suspend-param = <0x00000004>; + entry-latency-us = <180>; + exit-latency-us = <320>; + min-residency-us = <1000>; + }; + }; + + domain-idle-states { + cluster_cl5: cluster-sleep-0 { + compatible = "domain-idle-state"; + arm,psci-suspend-param = <0x01000054>; + entry-latency-us = <2000>; + exit-latency-us = <2000>; + min-residency-us = <9000>; + }; + + domain_ss3: domain-sleep-0 { + compatible = "domain-idle-state"; + arm,psci-suspend-param = <0x0200c354>; + entry-latency-us = <2800>; + exit-latency-us = <4400>; + min-residency-us = <10150>; + }; + }; + }; + + firmware { + scm: scm { + compatible = "qcom,scm-glymur", "qcom,scm"; + qcom,dload-mode = <&tcsr 0x4000>; + interconnects = <&aggre2_noc MASTER_CRYPTO QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + }; + + scmi { + compatible = "arm,scmi"; + mboxes = <&pdp0_mbox 0>, <&pdp0_mbox 1>; + mbox-names = "tx", "rx"; + shmem = <&cpu_scp_lpri1>, <&cpu_scp_lpri0>; + + #address-cells = <1>; + #size-cells = <0>; + + scmi_perf: protocol@13 { + reg = <0x13>; + #power-domain-cells = <1>; + }; + }; + }; + + clk_virt: interconnect-0 { + compatible = "qcom,glymur-clk-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + mc_virt: interconnect-1 { + compatible = "qcom,glymur-mc-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + pmu { + compatible = "arm,armv8-pmuv3"; + interrupts = ; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + + cpu_pd0: power-domain-cpu0 { + #power-domain-cells = <0>; + power-domains = <&cluster0_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd1: power-domain-cpu1 { + #power-domain-cells = <0>; + power-domains = <&cluster0_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd2: power-domain-cpu2 { + #power-domain-cells = <0>; + power-domains = <&cluster0_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd3: power-domain-cpu3 { + #power-domain-cells = <0>; + power-domains = <&cluster0_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd4: power-domain-cpu4 { + #power-domain-cells = <0>; + power-domains = <&cluster0_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd5: power-domain-cpu5 { + #power-domain-cells = <0>; + power-domains = <&cluster0_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd6: power-domain-cpu6 { + #power-domain-cells = <0>; + power-domains = <&cluster1_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd7: power-domain-cpu7 { + #power-domain-cells = <0>; + power-domains = <&cluster1_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd8: power-domain-cpu8 { + #power-domain-cells = <0>; + power-domains = <&cluster1_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd9: power-domain-cpu9 { + #power-domain-cells = <0>; + power-domains = <&cluster1_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd10: power-domain-cpu10 { + #power-domain-cells = <0>; + power-domains = <&cluster1_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd11: power-domain-cpu11 { + #power-domain-cells = <0>; + power-domains = <&cluster1_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd12: power-domain-cpu12 { + #power-domain-cells = <0>; + power-domains = <&cluster2_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd13: power-domain-cpu13 { + #power-domain-cells = <0>; + power-domains = <&cluster2_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd14: power-domain-cpu14 { + #power-domain-cells = <0>; + power-domains = <&cluster2_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd15: power-domain-cpu15 { + #power-domain-cells = <0>; + power-domains = <&cluster2_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd16: power-domain-cpu16 { + #power-domain-cells = <0>; + power-domains = <&cluster2_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cpu_pd17: power-domain-cpu17 { + #power-domain-cells = <0>; + power-domains = <&cluster2_pd>; + domain-idle-states = <&cpu_c4>; + }; + + cluster0_pd: power-domain-cpu-cluster0 { + #power-domain-cells = <0>; + power-domains = <&system_pd>; + domain-idle-states = <&cluster_cl5>; + }; + + cluster1_pd: power-domain-cpu-cluster1 { + #power-domain-cells = <0>; + power-domains = <&system_pd>; + domain-idle-states = <&cluster_cl5>; + }; + + cluster2_pd: power-domain-cpu-cluster2 { + #power-domain-cells = <0>; + power-domains = <&system_pd>; + domain-idle-states = <&cluster_cl5>; + }; + + system_pd: power-domain-system { + #power-domain-cells = <0>; + domain-idle-states = <&domain_ss3>; + }; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + pdp_mem: pdp@81400000 { + reg = <0x0 0x81400000 0x0 0x100000>; + no-map; + }; + + aop_cmd_db_mem: aop-cmd-db@81c60000 { + compatible = "qcom,cmd-db"; + reg = <0x0 0x81c60000 0x0 0x20000>; + no-map; + }; + + pdp_ns_shared_mem: pdp-ns-shared@81e00000 { + reg = <0x0 0x81e00000 0x0 0x200000>; + no-map; + }; + + oobdaretag_mem: oobdaretag@86e10000 { + reg = <0x0 0x86e10000 0x0 0x360000>; + no-map; + }; + + oob_secure_mem: oob-secure@87170000 { + reg = <0x0 0x87170000 0x0 0xbc0000>; + no-map; + }; + + oobdtbqc_mem: oobdtbqc@87d30000 { + reg = <0x0 0x87d30000 0x0 0x20000>; + no-map; + }; + + oobdtboem_mem: oobdtboem@87d50000 { + reg = <0x0 0x87d50000 0x0 0x20000>; + no-map; + }; + + oob_nonsecure_mem: oob-nonsecure@87e00000 { + reg = <0x0 0x87e00000 0x0 0xc00000>; + no-map; + }; + + spss_region_mem: spss@88a00000 { + reg = <0x0 0x88a00000 0x0 0x400000>; + no-map; + }; + + soccpdtb_mem: soccpdtb@892e0000 { + reg = <0x0 0x892e0000 0x0 0x20000>; + no-map; + }; + + soccp_mem: soccp@89300000 { + reg = <0x0 0x89300000 0x0 0x400000>; + no-map; + }; + + cvp_mem: cvp@89700000 { + reg = <0x0 0x89700000 0x0 0x700000>; + no-map; + }; + + adspslpi_mem: adspslpi@89e00000 { + reg = <0x0 0x89e00000 0x0 0x3a00000>; + no-map; + }; + + q6_adsp_dtb_mem: q6-adsp-dtb@8d800000 { + reg = <0x0 0x8d800000 0x0 0x80000>; + no-map; + }; + + cdsp_mem: cdsp@8d900000 { + reg = <0x0 0x8d900000 0x0 0x4000000>; + no-map; + }; + + q6_cdsp_dtb_mem: q6-cdsp-dtb@91900000 { + reg = <0x0 0x91900000 0x0 0x80000>; + no-map; + }; + + gpu_microcode_mem: gpu-microcode@919fe000 { + reg = <0x0 0x919fe000 0x0 0x2000>; + no-map; + }; + + camera_mem: camera@91a00000 { + reg = <0x0 0x91a00000 0x0 0x800000>; + no-map; + }; + + av1_encoder_mem: av1-encoder@92200000 { + reg = <0x0 0x92200000 0x0 0x700000>; + no-map; + }; + + video_mem: video@92900000 { + reg = <0x0 0x92900000 0x0 0xc00000>; + no-map; + }; + + smem_mem: smem@ffe00000 { + compatible = "qcom,smem"; + reg = <0x0 0xffe00000 0x0 0x200000>; + hwlocks = <&tcsr_mutex 3>; + no-map; + }; + }; + + smp2p-adsp { + compatible = "qcom,smp2p"; + + interrupts-extended = <&ipcc IPCC_MPROC_LPASS + IPCC_MPROC_SIGNAL_SMP2P + IRQ_TYPE_EDGE_RISING>; + + mboxes = <&ipcc IPCC_MPROC_LPASS IPCC_MPROC_SIGNAL_SMP2P>; + + qcom,smem = <443>, <429>; + qcom,local-pid = <0>; + qcom,remote-pid = <2>; + + smp2p_adsp_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + smp2p_adsp_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + smp2p-cdsp { + compatible = "qcom,smp2p"; + + interrupts-extended = <&ipcc IPCC_MPROC_CDSP + IPCC_MPROC_SIGNAL_SMP2P + IRQ_TYPE_EDGE_RISING>; + + mboxes = <&ipcc IPCC_MPROC_CDSP IPCC_MPROC_SIGNAL_SMP2P>; + + qcom,smem = <94>, <432>; + qcom,local-pid = <0>; + qcom,remote-pid = <5>; + + smp2p_cdsp_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + smp2p_cdsp_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + smp2p-soccp { + compatible = "qcom,smp2p"; + + interrupts-extended = <&ipcc IPCC_MPROC_SOCCP + IPCC_MPROC_SIGNAL_SMP2P + IRQ_TYPE_EDGE_RISING>; + + mboxes = <&ipcc IPCC_MPROC_SOCCP + IPCC_MPROC_SIGNAL_SMP2P>; + + qcom,smem = <617>, <616>; + qcom,local-pid = <0>; + qcom,remote-pid = <19>; + + soccp_smp2p_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + soccp_smp2p_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + soc: soc@0 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x0 0x0 0x0 0x100 0x0>; + dma-ranges = <0x0 0x0 0x0 0x0 0x100 0x0>; + + gcc: clock-controller@100000 { + compatible = "qcom,glymur-gcc"; + reg = <0x0 0x00100000 0x0 0x1f9000>; + clocks = <&rpmhcc RPMH_CXO_CLK>, /* Board XO source */ + <&rpmhcc RPMH_CXO_CLK_A>, /* Board XO_A source */ + <&sleep_clk>, /* Sleep */ + <0>, /* USB 0 Phy DP0 GMUX */ + <0>, /* USB 0 Phy DP1 GMUX */ + <0>, /* USB 0 Phy PCIE PIPEGMUX */ + <0>, /* USB 0 Phy PIPEGMUX */ + <0>, /* USB 0 Phy SYS PCIE PIPEGMUX */ + <0>, /* USB 1 Phy DP0 GMUX 2 */ + <0>, /* USB 1 Phy DP1 GMUX 2 */ + <0>, /* USB 1 Phy PCIE PIPEGMUX */ + <0>, /* USB 1 Phy PIPEGMUX */ + <0>, /* USB 1 Phy SYS PCIE PIPEGMUX */ + <0>, /* USB 2 Phy DP0 GMUX 2 */ + <0>, /* USB 2 Phy DP1 GMUX 2 */ + <0>, /* USB 2 Phy PCIE PIPEGMUX */ + <0>, /* USB 2 Phy PIPEGMUX */ + <0>, /* USB 2 Phy SYS PCIE PIPEGMUX */ + <0>, /* PCIe 3a */ + <&pcie3b_phy>, /* PCIe 3b */ + <&pcie4_phy>, /* PCIe 4 */ + <&pcie5_phy>, /* PCIe 5 */ + <&pcie6_phy>, /* PCIe 6 */ + <0>, /* QUSB4 0 PHY RX 0 */ + <0>, /* QUSB4 0 PHY RX 1 */ + <0>, /* QUSB4 1 PHY RX 0 */ + <0>, /* QUSB4 1 PHY RX 1 */ + <0>, /* QUSB4 2 PHY RX 0 */ + <0>, /* QUSB4 2 PHY RX 1 */ + <0>, /* UFS PHY RX Symbol 0 */ + <0>, /* UFS PHY RX Symbol 1 */ + <0>, /* UFS PHY TX Symbol 0 */ + <&usb_0_qmpphy QMP_USB43DP_USB3_PIPE_CLK>, + <&usb_1_qmpphy QMP_USB43DP_USB3_PIPE_CLK>, + <&usb_2_qmpphy QMP_USB43DP_USB3_PIPE_CLK>, + <&usb_mp_qmpphy0 QMP_USB43DP_USB3_PIPE_CLK>, + <&usb_mp_qmpphy1 QMP_USB43DP_USB3_PIPE_CLK>, + <0>, /* USB4 PHY 0 pcie pipe */ + <0>, /* USB4 PHY 0 Max pipe */ + <0>, /* USB4 PHY 1 pcie pipe */ + <0>, /* USB4 PHY 1 Max pipe */ + <0>, /* USB4 PHY 2 pcie */ + <0>; /* USB4 PHY 2 Max */ + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + + gpi_dma2: dma-controller@800000 { + compatible = "qcom,glymur-gpi-dma", "qcom,sm6350-gpi-dma"; + reg = <0x0 0x00800000 0x0 0x60000>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + dma-channels = <16>; + dma-channel-mask = <0x3f>; + #dma-cells = <3>; + iommus = <&apps_smmu 0xd76 0x0>; + }; + + qupv3_2: geniqup@8c0000 { + compatible = "qcom,geni-se-qup"; + reg = <0x0 0x008c0000 0x0 0x3000>; + clocks = <&gcc GCC_QUPV3_WRAP_2_M_AHB_CLK>, + <&gcc GCC_QUPV3_WRAP_2_S_AHB_CLK>; + clock-names = "m-ahb", + "s-ahb"; + iommus = <&apps_smmu 0xd63 0x0>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + i2c16: i2c@880000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00880000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S0_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 0 QCOM_GPI_I2C>, + <&gpi_dma2 1 0 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c16_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi16: spi@880000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00880000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S0_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 0 QCOM_GPI_SPI>, + <&gpi_dma2 1 0 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi16_data_clk>, <&qup_spi16_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c17: i2c@884000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00884000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S1_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 1 QCOM_GPI_I2C>, + <&gpi_dma2 1 1 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c17_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi17: spi@884000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00884000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S1_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 1 QCOM_GPI_SPI>, + <&gpi_dma2 1 1 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi17_data_clk>, <&qup_spi17_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c18: i2c@888000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00888000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S2_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 2 QCOM_GPI_I2C>, + <&gpi_dma2 1 2 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c18_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi18: spi@888000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00888000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S2_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 2 QCOM_GPI_SPI>, + <&gpi_dma2 1 2 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi18_data_clk>, <&qup_spi18_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c19: i2c@88c000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x0088c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S3_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 3 QCOM_GPI_I2C>, + <&gpi_dma2 1 3 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c19_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi19: spi@88c000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x0088c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S3_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 3 QCOM_GPI_SPI>, + <&gpi_dma2 1 3 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi19_data_clk>, <&qup_spi19_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + uart19: serial@88c000 { + compatible = "qcom,geni-uart"; + reg = <0x0 0x0088c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S3_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config"; + pinctrl-0 = <&qup_uart19_default>; + pinctrl-names = "default"; + + status = "disabled"; + }; + + i2c20: i2c@890000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00890000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S4_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 4 QCOM_GPI_I2C>, + <&gpi_dma2 1 4 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c20_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi20: spi@890000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00890000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S4_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 4 QCOM_GPI_SPI>, + <&gpi_dma2 1 4 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi20_data_clk>, <&qup_spi20_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c21: i2c@894000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00894000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S5_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 5 QCOM_GPI_I2C>, + <&gpi_dma2 1 5 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c21_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi21: spi@894000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00894000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S5_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 5 QCOM_GPI_SPI>, + <&gpi_dma2 1 5 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi21_data_clk>, <&qup_spi21_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + uart21: serial@894000 { + compatible = "qcom,geni-debug-uart"; + reg = <0x0 0x00894000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S5_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config"; + pinctrl-0 = <&qup_uart21_default>; + pinctrl-names = "default"; + }; + + i2c22: i2c@898000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00898000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S6_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 6 QCOM_GPI_I2C>, + <&gpi_dma2 1 6 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c22_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi22: spi@898000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00898000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S6_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 6 QCOM_GPI_SPI>, + <&gpi_dma2 1 6 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi22_data_clk>, <&qup_spi22_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + uart22: serial@898000 { + compatible = "qcom,geni-uart"; + reg = <0x0 0x00898000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S6_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config"; + pinctrl-0 = <&qup_uart22_default>; + pinctrl-names = "default"; + + status = "disabled"; + }; + + i2c23: i2c@89c000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x0089c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S7_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 7 QCOM_GPI_I2C>, + <&gpi_dma2 1 7 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c23_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi23: spi@89c000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x0089c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP2_S7_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma2 0 7 QCOM_GPI_SPI>, + <&gpi_dma2 1 7 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi23_data_clk>, <&qup_spi23_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + }; + + gpi_dma1: dma-controller@a00000 { + compatible = "qcom,glymur-gpi-dma", "qcom,sm6350-gpi-dma"; + reg = <0x0 0x00a00000 0x0 0x60000>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + dma-channels = <16>; + dma-channel-mask = <0x3f>; + #dma-cells = <3>; + iommus = <&apps_smmu 0xcb6 0x0>; + }; + + qupv3_1: geniqup@ac0000 { + compatible = "qcom,geni-se-qup"; + reg = <0x0 0x00ac0000 0x0 0x3000>; + clocks = <&gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>, + <&gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>; + clock-names = "m-ahb", + "s-ahb"; + iommus = <&apps_smmu 0xca3 0x0>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + i2c8: i2c@a80000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a80000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S0_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 0 QCOM_GPI_I2C>, + <&gpi_dma1 1 0 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c8_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi8: spi@a80000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a80000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S0_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 0 QCOM_GPI_SPI>, + <&gpi_dma1 1 0 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi8_data_clk>, <&qup_spi8_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c9: i2c@a84000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a84000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S1_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 1 QCOM_GPI_I2C>, + <&gpi_dma1 1 1 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c9_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi9: spi@a84000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a84000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S1_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 1 QCOM_GPI_SPI>, + <&gpi_dma1 1 1 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi9_data_clk>, <&qup_spi9_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c10: i2c@a88000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a88000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S2_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 2 QCOM_GPI_I2C>, + <&gpi_dma1 1 2 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c10_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi10: spi@a88000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a88000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S2_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 2 QCOM_GPI_SPI>, + <&gpi_dma1 1 2 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi10_data_clk>, <&qup_spi10_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c11: i2c@a8c000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a8c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S3_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 3 QCOM_GPI_I2C>, + <&gpi_dma1 1 3 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c11_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi11: spi@a8c000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a8c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S3_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 3 QCOM_GPI_SPI>, + <&gpi_dma1 1 3 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi11_data_clk>, <&qup_spi11_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c12: i2c@a90000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a90000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S4_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 4 QCOM_GPI_I2C>, + <&gpi_dma1 1 4 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c12_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi12: spi@a90000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a90000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S4_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 4 QCOM_GPI_SPI>, + <&gpi_dma1 1 4 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi12_data_clk>, <&qup_spi12_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c13: i2c@a94000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a94000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S5_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 5 QCOM_GPI_I2C>, + <&gpi_dma1 1 5 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c13_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi13: spi@a94000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a94000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S5_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 5 QCOM_GPI_SPI>, + <&gpi_dma1 1 5 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi13_data_clk>, <&qup_spi13_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c14: i2c@a98000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a98000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S6_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 6 QCOM_GPI_I2C>, + <&gpi_dma1 1 6 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c14_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi14: spi@a98000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a98000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S6_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 6 QCOM_GPI_SPI>, + <&gpi_dma1 1 6 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi14_data_clk>, <&qup_spi14_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + uart14: serial@a98000 { + compatible = "qcom,geni-uart"; + reg = <0x0 0x00a98000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S6_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config"; + pinctrl-0 = <&qup_uart14_default>; + pinctrl-names = "default"; + + status = "disabled"; + }; + + i2c15: i2c@a9c000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a9c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S7_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 7 QCOM_GPI_I2C>, + <&gpi_dma1 1 7 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c15_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi15: spi@a9c000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a9c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP1_S7_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma1 0 7 QCOM_GPI_SPI>, + <&gpi_dma1 1 7 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi15_data_clk>, <&qup_spi15_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + }; + + gpi_dma0: dma-controller@b00000 { + compatible = "qcom,glymur-gpi-dma", "qcom,sm6350-gpi-dma"; + reg = <0x0 0x00b00000 0x0 0x60000>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + dma-channels = <16>; + dma-channel-mask = <0x3f>; + #dma-cells = <3>; + iommus = <&apps_smmu 0xd36 0x0>; + }; + + qupv3_0: geniqup@bc0000 { + compatible = "qcom,geni-se-qup"; + reg = <0x0 0x00bc0000 0x0 0x3000>; + clocks = <&gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>, + <&gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>; + clock-names = "m-ahb", + "s-ahb"; + iommus = <&apps_smmu 0xd23 0x0>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + i2c0: i2c@b80000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00b80000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 0 QCOM_GPI_I2C>, + <&gpi_dma0 1 0 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c0_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi0: spi@b80000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00b80000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 0 QCOM_GPI_SPI>, + <&gpi_dma0 1 0 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi0_data_clk>, <&qup_spi0_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c1: i2c@b84000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00b84000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 1 QCOM_GPI_I2C>, + <&gpi_dma0 1 1 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c1_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi1: spi@b84000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00b84000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 1 QCOM_GPI_SPI>, + <&gpi_dma0 1 1 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi1_data_clk>, <&qup_spi1_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c2: i2c@b88000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00b88000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 2 QCOM_GPI_I2C>, + <&gpi_dma0 1 2 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c2_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi2: spi@b88000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00b88000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 2 QCOM_GPI_SPI>, + <&gpi_dma0 1 2 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi2_data_clk>, <&qup_spi2_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + uart2: serial@b88000 { + compatible = "qcom,geni-uart"; + reg = <0x0 0x00b88000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config"; + pinctrl-0 = <&qup_uart2_default>; + pinctrl-names = "default"; + + status = "disabled"; + }; + + i2c3: i2c@b8c000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00b8c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S3_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 3 QCOM_GPI_I2C>, + <&gpi_dma0 1 3 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c3_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi3: spi@b8c000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00b8c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S3_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 3 QCOM_GPI_SPI>, + <&gpi_dma0 1 3 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi3_data_clk>, <&qup_spi3_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c4: i2c@b90000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00b90000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S4_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 4 QCOM_GPI_I2C>, + <&gpi_dma0 1 4 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c4_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi4: spi@b90000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00b90000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S4_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 4 QCOM_GPI_SPI>, + <&gpi_dma0 1 4 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi4_data_clk>, <&qup_spi4_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c5: i2c@b94000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00b94000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S5_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 5 QCOM_GPI_I2C>, + <&gpi_dma0 1 5 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c5_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi5: spi@b94000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00b94000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S5_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 5 QCOM_GPI_SPI>, + <&gpi_dma0 1 5 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi5_data_clk>, <&qup_spi5_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c6: i2c@b98000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00b98000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 6 QCOM_GPI_I2C>, + <&gpi_dma0 1 6 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c6_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi6: spi@b98000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00b98000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 6 QCOM_GPI_SPI>, + <&gpi_dma0 1 6 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi6_data_clk>, <&qup_spi6_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c7: i2c@b9c000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00b9c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 7 QCOM_GPI_I2C>, + <&gpi_dma0 1 7 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_i2c7_data_clk>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi7: spi@b9c000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00b9c000 0x0 0x4000>; + interrupts = ; + clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>; + clock-names = "se"; + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, + <&aggre3_noc MASTER_QUP_0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + dmas = <&gpi_dma0 0 7 QCOM_GPI_SPI>, + <&gpi_dma0 1 7 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + pinctrl-0 = <&qup_spi7_data_clk>, <&qup_spi7_cs>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + }; + + usb_hs_phy: phy@fa0000 { + compatible = "qcom,glymur-m31-eusb2-phy", + "qcom,sm8750-m31-eusb2-phy"; + reg = <0x0 0x00fa0000 0x0 0x154>; + #phy-cells = <0>; + + clocks = <&tcsr TCSR_USB2_1_CLKREF_EN>; + clock-names = "ref"; + + resets = <&gcc GCC_QUSB2PHY_USB20_HS_BCR>; + + status = "disabled"; + }; + + usb_mp_hsphy0: phy@fa1000 { + compatible = "qcom,glymur-m31-eusb2-phy", + "qcom,sm8750-m31-eusb2-phy"; + + reg = <0x0 0x00fa1000 0x0 0x29c>; + #phy-cells = <0>; + + clocks = <&tcsr TCSR_USB2_1_CLKREF_EN>; + clock-names = "ref"; + + resets = <&gcc GCC_QUSB2PHY_HS0_MP_BCR>; + + status = "disabled"; + }; + + usb_mp_hsphy1: phy@fa2000 { + compatible = "qcom,glymur-m31-eusb2-phy", + "qcom,sm8750-m31-eusb2-phy"; + + reg = <0x0 0x00fa2000 0x0 0x29c>; + #phy-cells = <0>; + + clocks = <&tcsr TCSR_USB2_2_CLKREF_EN>; + clock-names = "ref"; + + resets = <&gcc GCC_QUSB2PHY_HS1_MP_BCR>; + + status = "disabled"; + }; + + usb_mp_qmpphy0: phy@fa3000 { + compatible = "qcom,glymur-qmp-usb3-uni-phy"; + reg = <0x0 0x00fa3000 0x0 0x2000>; + + clocks = <&gcc GCC_USB3_MP_PHY_AUX_CLK>, + <&tcsr TCSR_USB3_0_CLKREF_EN>, + <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_USB3_MP_PHY_COM_AUX_CLK>, + <&gcc GCC_USB3_MP_PHY_PIPE_0_CLK>; + clock-names = "aux", + "clkref", + "ref", + "com_aux", + "pipe"; + + power-domains = <&gcc GCC_USB3_MP_SS0_PHY_GDSC>; + + resets = <&gcc GCC_USB3_MP_SS0_PHY_BCR>, + <&gcc GCC_USB3UNIPHY_PHY_MP0_BCR>; + reset-names = "phy", + "phy_phy"; + + clock-output-names = "usb3_uni_phy_0_pipe_clk_src"; + #clock-cells = <0>; + #phy-cells = <0>; + + status = "disabled"; + }; + + usb_mp_qmpphy1: phy@fa5000 { + compatible = "qcom,glymur-qmp-usb3-uni-phy"; + reg = <0x0 0x00fa5000 0x0 0x2000>; + + clocks = <&gcc GCC_USB3_MP_PHY_AUX_CLK>, + <&tcsr TCSR_USB3_1_CLKREF_EN>, + <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_USB3_MP_PHY_COM_AUX_CLK>, + <&gcc GCC_USB3_MP_PHY_PIPE_1_CLK>; + clock-names = "aux", + "clkref", + "ref", + "com_aux", + "pipe"; + + power-domains = <&gcc GCC_USB3_MP_SS1_PHY_GDSC>; + + resets = <&gcc GCC_USB3_MP_SS1_PHY_BCR>, + <&gcc GCC_USB3UNIPHY_PHY_MP1_BCR>; + reset-names = "phy", + "phy_phy"; + + clock-output-names = "usb3_uni_phy_1_pipe_clk_src"; + + #clock-cells = <0>; + #phy-cells = <0>; + + status = "disabled"; + }; + + mdss_dp3_phy: phy@faac00 { + compatible = "qcom,glymur-dp-phy"; + reg = <0x0 0x00faac00 0x0 0x1d0>, + <0x0 0x00faa400 0x0 0x128>, + <0x0 0x00faa800 0x0 0x128>, + <0x0 0x00faa000 0x0 0x358>; + + clocks = <&dispcc DISP_CC_MDSS_DPTX3_AUX_CLK>, + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&tcsr TCSR_EDP_CLKREF_EN>; + clock-names = "aux", + "cfg_ahb", + "ref"; + + power-domains = <&rpmhpd RPMHPD_MX>; + + #clock-cells = <1>; + #phy-cells = <0>; + + status = "disabled"; + }; + + usb_0_hsphy: phy@fd3000 { + compatible = "qcom,glymur-m31-eusb2-phy", + "qcom,sm8750-m31-eusb2-phy"; + + reg = <0x0 0x00fd3000 0x0 0x29c>; + #phy-cells = <0>; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-names = "ref"; + + resets = <&gcc GCC_QUSB2PHY_PRIM_BCR>; + + status = "disabled"; + }; + + usb_0_qmpphy: phy@fd5000 { + compatible = "qcom,glymur-qmp-usb3-dp-phy"; + reg = <0x0 0x00fd5000 0x0 0x8000>; + + clocks = <&gcc GCC_USB3_PRIM_PHY_AUX_CLK>, + <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>, + <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>; + clock-names = "aux", + "ref", + "com_aux", + "usb3_pipe"; + + resets = <&gcc GCC_USB3_PHY_PRIM_BCR>, + <&gcc GCC_USB3PHY_PHY_PRIM_BCR>; + + reset-names = "phy", + "common"; + + power-domains = <&gcc GCC_USB_0_PHY_GDSC>; + + #clock-cells = <1>; + #phy-cells = <1>; + + mode-switch; + orientation-switch; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_0_qmpphy_out: endpoint { + }; + }; + + port@1 { + reg = <1>; + + usb_0_qmpphy_usb_ss_in: endpoint { + remote-endpoint = <&usb_0_dwc3_ss>; + }; + }; + + port@2 { + reg = <2>; + + usb_dp_qmpphy_dp_in: endpoint { + remote-endpoint = <&mdss_dp0_out>; + }; + }; + }; + }; + + usb_1_hsphy: phy@fdd000 { + compatible = "qcom,glymur-m31-eusb2-phy", + "qcom,sm8750-m31-eusb2-phy"; + + reg = <0x0 0x00fdd000 0x0 0x29c>; + #phy-cells = <0>; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-names = "ref"; + + resets = <&gcc GCC_QUSB2PHY_SEC_BCR>; + + status = "disabled"; + }; + + usb_1_qmpphy: phy@fde000 { + compatible = "qcom,glymur-qmp-usb3-dp-phy"; + reg = <0x0 0x00fde000 0x0 0x8000>; + + clocks = <&gcc GCC_USB3_SEC_PHY_AUX_CLK>, + <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_USB3_SEC_PHY_COM_AUX_CLK>, + <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>, + <&tcsr TCSR_USB4_1_CLKREF_EN>; + clock-names = "aux", + "ref", + "com_aux", + "usb3_pipe", + "clkref"; + + power-domains = <&gcc GCC_USB_1_PHY_GDSC>; + + resets = <&gcc GCC_USB3_PHY_SEC_BCR>, + <&gcc GCC_USB3PHY_PHY_SEC_BCR>; + reset-names = "phy", + "common"; + + #clock-cells = <1>; + #phy-cells = <1>; + + mode-switch; + orientation-switch; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_1_qmpphy_out: endpoint { + }; + }; + + port@1 { + reg = <1>; + + usb_1_qmpphy_usb_ss_in: endpoint { + remote-endpoint = <&usb_1_dwc3_ss>; + }; + }; + + port@2 { + reg = <2>; + + usb_1_qmpphy_dp_in: endpoint { + remote-endpoint = <&mdss_dp1_out>; + }; + }; + }; + }; + + + /* cluster0 */ + bwmon_cluster0: pmu@100c400 { + compatible = "qcom,glymur-cpu-bwmon", "qcom,sdm845-bwmon"; + reg = <0x0 0x0100c400 0x0 0x600>; + + interrupts = ; + + interconnects = <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>; + + operating-points-v2 = <&cpu_bwmon_opp_table>; + + cpu_bwmon_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-0 { + opp-peak-kBps = <800000>; + }; + + opp-1 { + opp-peak-kBps = <2188800>; + }; + + opp-2 { + opp-peak-kBps = <5414400>; + }; + + opp-3 { + opp-peak-kBps = <6220800>; + }; + + opp-4 { + opp-peak-kBps = <6835200>; + }; + + opp-5 { + opp-peak-kBps = <8371200>; + }; + + opp-6 { + opp-peak-kBps = <10944000>; + }; + + opp-7 { + opp-peak-kBps = <12748800>; + }; + + opp-8 { + opp-peak-kBps = <14745600>; + }; + + opp-9 { + opp-peak-kBps = <16896000>; + }; + + opp-10 { + opp-peak-kBps = <19046400>; + }; + + opp-11 { + opp-peak-kBps = <21332000>; + }; + }; + }; + + /* cluster1 */ + bwmon_cluster1: pmu@100d400 { + compatible = "qcom,glymur-cpu-bwmon", "qcom,sdm845-bwmon"; + reg = <0x0 0x0100d400 0x0 0x600>; + + interrupts = ; + + interconnects = <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>; + + operating-points-v2 = <&cpu_bwmon_opp_table>; + }; + + /* cluster2 */ + bwmon_cluster2: pmu@100e400 { + compatible = "qcom,glymur-cpu-bwmon", "qcom,sdm845-bwmon"; + reg = <0x0 0x0100e400 0x0 0x600>; + + interrupts = ; + + interconnects = <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>; + + operating-points-v2 = <&cpu_bwmon_opp_table>; + }; + cnoc_main: interconnect@1500000 { + compatible = "qcom,glymur-cnoc-main"; + reg = <0x0 0x01500000 0x0 0x17080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + config_noc: interconnect@1600000 { + compatible = "qcom,glymur-cnoc-cfg"; + reg = <0x0 0x01600000 0x0 0x6600>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + system_noc: interconnect@1680000 { + compatible = "qcom,glymur-system-noc"; + reg = <0x0 0x01680000 0x0 0x1c080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + pcie_west_anoc: interconnect@16c0000 { + compatible = "qcom,glymur-pcie-west-anoc"; + reg = <0x0 0x016c0000 0x0 0xf580>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + clocks = <&gcc GCC_AGGRE_NOC_PCIE_3A_WEST_SF_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_PCIE_3B_WEST_SF_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_PCIE_4_WEST_SF_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_PCIE_6_WEST_SF_AXI_CLK>; + }; + + pcie_east_anoc: interconnect@16d0000 { + compatible = "qcom,glymur-pcie-east-anoc"; + reg = <0x0 0x016d0000 0x0 0xf300>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + clocks = <&gcc GCC_AGGRE_NOC_PCIE_5_EAST_SF_AXI_CLK>; + }; + + aggre1_noc: interconnect@16e0000 { + compatible = "qcom,glymur-aggre1-noc"; + reg = <0x0 0x016e0000 0x0 0x14400>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + aggre2_noc: interconnect@1720000 { + compatible = "qcom,glymur-aggre2-noc"; + reg = <0x0 0x01720000 0x0 0x14400>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + clocks = <&gcc GCC_AGGRE_USB3_TERT_AXI_CLK>, + <&gcc GCC_AGGRE_USB4_2_AXI_CLK>, + <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>; + }; + + aggre3_noc: interconnect@1700000 { + compatible = "qcom,glymur-aggre3-noc"; + reg = <0x0 0x01700000 0x0 0x1d400>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + aggre4_noc: interconnect@1740000 { + compatible = "qcom,glymur-aggre4-noc"; + reg = <0x0 0x01740000 0x0 0x14400>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + clocks = <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, + <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>, + <&gcc GCC_AGGRE_USB4_0_AXI_CLK>, + <&gcc GCC_AGGRE_USB4_1_AXI_CLK>; + }; + + mmss_noc: interconnect@1780000 { + compatible = "qcom,glymur-mmss-noc"; + reg = <0x0 0x01780000 0x0 0x5b800>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + pcie_east_slv_noc: interconnect@1900000 { + compatible = "qcom,glymur-pcie-east-slv-noc"; + reg = <0x0 0x01900000 0x0 0xe080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + pcie_west_slv_noc: interconnect@1920000 { + compatible = "qcom,glymur-pcie-west-slv-noc"; + reg = <0x0 0x01920000 0x0 0xf180>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + pcie4: pci@1bf0000 { + device_type = "pci"; + compatible = "qcom,glymur-pcie", "qcom,pcie-x1e80100"; + reg = <0x0 0x01bf0000 0x0 0x3000>, + <0x0 0x78000000 0x0 0xf20>, + <0x0 0x78000f40 0x0 0xa8>, + <0x0 0x78001000 0x0 0x4000>, + <0x0 0x78005000 0x0 0x100000>, + <0x0 0x01bf3000 0x0 0x1000>; + reg-names = "parf", + "dbi", + "elbi", + "atu", + "config", + "mhi"; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x01000000 0x0 0x00000000 0x0 0x78105000 0x0 0x100000>, + <0x02000000 0x0 0x78205000 0x0 0x78205000 0x0 0x1dfb000>, + <0x03000000 0x7 0x80000000 0x7 0x80000000 0x0 0x20000000>; + bus-range = <0x00 0xff>; + + dma-coherent; + + linux,pci-domain = <4>; + num-lanes = <2>; + + operating-points-v2 = <&pcie4_opp_table>; + + msi-map = <0x0 &gic_its 0xc0000 0x10000>; + iommu-map = <0x0 &pcie_smmu 0x40000 0x10000>; + + interrupts = , + , + , + , + , + , + , + , + ; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "global"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 0 0 513 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &intc 0 0 0 514 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &intc 0 0 0 515 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &intc 0 0 0 516 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&gcc GCC_PCIE_4_AUX_CLK>, + <&gcc GCC_PCIE_4_CFG_AHB_CLK>, + <&gcc GCC_PCIE_4_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_4_SLV_AXI_CLK>, + <&gcc GCC_PCIE_4_SLV_Q2A_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_PCIE_4_WEST_SF_AXI_CLK>; + clock-names = "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a", + "noc_aggr"; + + assigned-clocks = <&gcc GCC_PCIE_4_AUX_CLK>; + assigned-clock-rates = <19200000>; + + interconnects = <&pcie_west_anoc MASTER_PCIE_4 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &pcie_west_slv_noc SLAVE_PCIE_4 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "pcie-mem", + "cpu-pcie"; + + resets = <&gcc GCC_PCIE_4_BCR>, + <&gcc GCC_PCIE_4_LINK_DOWN_BCR>; + reset-names = "pci", + "link_down"; + + power-domains = <&gcc GCC_PCIE_4_GDSC>; + + eq-presets-8gts = /bits/ 16 <0x5555 0x5555>; + eq-presets-16gts = /bits/ 8 <0x55 0x55>; + + status = "disabled"; + + pcie4_opp_table: opp-table { + compatible = "operating-points-v2"; + + /* GEN 1 x1 */ + opp-2500000-1 { + opp-hz = /bits/ 64 <2500000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <250000 1>; + opp-level = <1>; + }; + + /* GEN 1 x2 */ + opp-5000000-1 { + opp-hz = /bits/ 64 <5000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <500000 1>; + opp-level = <1>; + }; + + /* GEN 2 x1 */ + opp-5000000-2 { + opp-hz = /bits/ 64 <5000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <500000 1>; + opp-level = <2>; + }; + + /* GEN 2 x2 */ + opp-10000000-2 { + opp-hz = /bits/ 64 <10000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <1000000 1>; + opp-level = <2>; + }; + + /* GEN 3 x1 */ + opp-8000000-3 { + opp-hz = /bits/ 64 <8000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <984500 1>; + opp-level = <3>; + }; + + /* GEN 3 x2 */ + opp-16000000-3 { + opp-hz = /bits/ 64 <16000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <1969000 1>; + opp-level = <3>; + }; + + /* GEN 4 x1 */ + opp-16000000-4 { + opp-hz = /bits/ 64 <16000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <1969000 1>; + opp-level = <4>; + }; + + /* GEN 4 x2 */ + opp-32000000-4 { + opp-hz = /bits/ 64 <32000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <3938000 1>; + opp-level = <4>; + }; + + }; + + pcie4_port0: pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + phys = <&pcie4_phy>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + + pcie4_phy: phy@1bf6000 { + compatible = "qcom,glymur-qmp-gen4x2-pcie-phy"; + reg = <0x0 0x01bf6000 0x0 0x2000>; + + clocks = <&gcc GCC_PCIE_PHY_4_AUX_CLK>, + <&gcc GCC_PCIE_4_CFG_AHB_CLK>, + <&tcsr TCSR_PCIE_2_CLKREF_EN>, + <&gcc GCC_PCIE_4_PHY_RCHNG_CLK>, + <&gcc GCC_PCIE_4_PIPE_CLK>, + <&gcc GCC_PCIE_4_PIPE_DIV2_CLK>; + clock-names = "aux", + "cfg_ahb", + "ref", + "rchng", + "pipe", + "pipediv2"; + + resets = <&gcc GCC_PCIE_4_PHY_BCR>, + <&gcc GCC_PCIE_4_NOCSR_COM_PHY_BCR>; + reset-names = "phy", + "phy_nocsr"; + + assigned-clocks = <&gcc GCC_PCIE_4_PHY_RCHNG_CLK>; + assigned-clock-rates = <100000000>; + + power-domains = <&gcc GCC_PCIE_4_PHY_GDSC>; + + #clock-cells = <0>; + clock-output-names = "pcie4_pipe_clk"; + + #phy-cells = <0>; + + status = "disabled"; + }; + + pcie5: pci@1b40000 { + device_type = "pci"; + compatible = "qcom,glymur-pcie", "qcom,pcie-x1e80100"; + reg = <0x0 0x01b40000 0x0 0x3000>, + <0x0 0x7a000000 0x0 0xf20>, + <0x0 0x7a000f40 0x0 0xa8>, + <0x0 0x7a001000 0x0 0x4000>, + <0x0 0x7a100000 0x0 0x100000>, + <0x0 0x01b43000 0x0 0x1000>; + reg-names = "parf", + "dbi", + "elbi", + "atu", + "config", + "mhi"; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x01000000 0x0 0x00000000 0x0 0x7a200000 0x0 0x100000>, + <0x02000000 0x0 0x7a300000 0x0 0x7a300000 0x0 0x3d00000>, + <0x03000000 0x7 0xa0000000 0x7 0xa0000000 0x0 0x40000000>; + bus-range = <0x00 0xff>; + + dma-coherent; + + linux,pci-domain = <5>; + num-lanes = <4>; + + operating-points-v2 = <&pcie5_opp_table>; + + msi-map = <0x0 &gic_its 0xd0000 0x10000>; + iommu-map = <0x0 &pcie_smmu 0x50000 0x10000>; + + interrupts = , + , + , + , + , + , + , + , + ; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "global"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 0 0 526 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &intc 0 0 0 428 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &intc 0 0 0 429 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &intc 0 0 0 435 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&gcc GCC_PCIE_5_AUX_CLK>, + <&gcc GCC_PCIE_5_CFG_AHB_CLK>, + <&gcc GCC_PCIE_5_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_5_SLV_AXI_CLK>, + <&gcc GCC_PCIE_5_SLV_Q2A_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_PCIE_5_EAST_SF_AXI_CLK>; + clock-names = "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a", + "noc_aggr"; + + assigned-clocks = <&gcc GCC_PCIE_5_AUX_CLK>; + assigned-clock-rates = <19200000>; + + interconnects = <&pcie_east_anoc MASTER_PCIE_5 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &pcie_east_slv_noc SLAVE_PCIE_5 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "pcie-mem", + "cpu-pcie"; + + resets = <&gcc GCC_PCIE_5_BCR>, + <&gcc GCC_PCIE_5_LINK_DOWN_BCR>; + reset-names = "pci", + "link_down"; + + power-domains = <&gcc GCC_PCIE_5_GDSC>; + + eq-presets-8gts = /bits/ 16 <0x5555 0x5555 0x5555 0x5555>; + eq-presets-16gts = /bits/ 8 <0x55 0x55 0x55 0x55>; + eq-presets-32gts = /bits/ 8 <0x55 0x55 0x55 0x55>; + + status = "disabled"; + + pcie5_opp_table: opp-table { + compatible = "operating-points-v2"; + + /* GEN 1 x1 */ + opp-2500000-1 { + opp-hz = /bits/ 64 <2500000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <250000 1>; + opp-level = <1>; + }; + + /* GEN 1 x2 */ + opp-5000000-1 { + opp-hz = /bits/ 64 <5000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <500000 1>; + opp-level = <1>; + }; + + /* GEN 1 x4 */ + opp-10000000-1 { + opp-hz = /bits/ 64 <10000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <1000000 1>; + opp-level = <1>; + }; + + /* GEN 2 x1 */ + opp-5000000-2 { + opp-hz = /bits/ 64 <5000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <500000 1>; + opp-level = <2>; + }; + + /* GEN 2 x2 */ + opp-10000000-2 { + opp-hz = /bits/ 64 <10000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <1000000 1>; + opp-level = <2>; + }; + + /* GEN 2 x4 */ + opp-20000000-2 { + opp-hz = /bits/ 64 <20000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <2000000 1>; + opp-level = <2>; + }; + + /* GEN 3 x1 */ + opp-8000000-3 { + opp-hz = /bits/ 64 <8000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <984500 1>; + opp-level = <3>; + }; + + /* GEN 3 x2 */ + opp-16000000-3 { + opp-hz = /bits/ 64 <16000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <1969000 1>; + opp-level = <3>; + }; + + /* GEN 3 x4 */ + opp-32000000-3 { + opp-hz = /bits/ 64 <32000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <3938000 1>; + opp-level = <3>; + }; + + /* GEN 4 x1 */ + opp-16000000-4 { + opp-hz = /bits/ 64 <16000000>; + required-opps = <&rpmhpd_opp_svs>; + opp-peak-kBps = <1969000 1>; + opp-level = <4>; + }; + + /* GEN 4 x2 */ + opp-32000000-4 { + opp-hz = /bits/ 64 <32000000>; + required-opps = <&rpmhpd_opp_svs>; + opp-peak-kBps = <3938000 1>; + opp-level = <4>; + }; + + /* GEN 4 x4 */ + opp-64000000-4 { + opp-hz = /bits/ 64 <64000000>; + required-opps = <&rpmhpd_opp_svs>; + opp-peak-kBps = <7876000 1>; + opp-level = <4>; + }; + + /* GEN 5 x1 */ + opp-32000000-5 { + opp-hz = /bits/ 64 <32000000>; + required-opps = <&rpmhpd_opp_nom>; + opp-peak-kBps = <3938000 1>; + opp-level = <5>; + }; + + /* GEN 5 x2 */ + opp-64000000-5 { + opp-hz = /bits/ 64 <64000000>; + required-opps = <&rpmhpd_opp_nom>; + opp-peak-kBps = <7876000 1>; + opp-level = <5>; + }; + + /* GEN 5 x4 */ + opp-128000000-5 { + opp-hz = /bits/ 64 <128000000>; + required-opps = <&rpmhpd_opp_nom>; + opp-peak-kBps = <15753000 1>; + opp-level = <5>; + }; + }; + + pcie5_port0: pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + phys = <&pcie5_phy>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + + pcie5_phy: phy@1b50000 { + compatible = "qcom,glymur-qmp-gen5x4-pcie-phy"; + reg = <0x0 0x01b50000 0x0 0x10000>; + + clocks = <&gcc GCC_PCIE_PHY_5_AUX_CLK>, + <&gcc GCC_PCIE_5_CFG_AHB_CLK>, + <&tcsr TCSR_PCIE_1_CLKREF_EN>, + <&gcc GCC_PCIE_5_PHY_RCHNG_CLK>, + <&gcc GCC_PCIE_5_PIPE_CLK>, + <&gcc GCC_PCIE_5_PIPE_DIV2_CLK>; + clock-names = "aux", + "cfg_ahb", + "ref", + "rchng", + "pipe", + "pipediv2"; + + resets = <&gcc GCC_PCIE_5_PHY_BCR>, + <&gcc GCC_PCIE_5_NOCSR_COM_PHY_BCR>; + reset-names = "phy", + "phy_nocsr"; + + assigned-clocks = <&gcc GCC_PCIE_5_PHY_RCHNG_CLK>; + assigned-clock-rates = <100000000>; + + power-domains = <&gcc GCC_PCIE_5_PHY_GDSC>; + + #clock-cells = <0>; + clock-output-names = "pcie5_pipe_clk"; + + #phy-cells = <0>; + + status = "disabled"; + }; + + pcie6: pci@1c00000 { + device_type = "pci"; + compatible = "qcom,glymur-pcie", "qcom,pcie-x1e80100"; + reg = <0x0 0x01c00000 0x0 0x3000>, + <0x0 0x7e000000 0x0 0xf20>, + <0x0 0x7e000f40 0x0 0xa8>, + <0x0 0x7e001000 0x0 0x4000>, + <0x0 0x7e100000 0x0 0x100000>, + <0x0 0x01c03000 0x0 0x1000>; + reg-names = "parf", + "dbi", + "elbi", + "atu", + "config", + "mhi"; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x01000000 0x0 0x00000000 0x0 0x7e200000 0x0 0x100000>, + <0x02000000 0x0 0x7e300000 0x0 0x7e300000 0x0 0x1d00000>, + <0x03000000 0x7 0xe0000000 0x7 0xe0000000 0x0 0x20000000>; + bus-range = <0x00 0xff>; + + dma-coherent; + + linux,pci-domain = <6>; + num-lanes = <2>; + + operating-points-v2 = <&pcie6_opp_table>; + + msi-map = <0x0 &gic_its 0xe0000 0x10000>; + iommu-map = <0x0 &pcie_smmu 0x60000 0x10000>; + + interrupts = , + , + , + , + , + , + , + , + ; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "global"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 0 0 472 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &intc 0 0 0 473 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &intc 0 0 0 474 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &intc 0 0 0 475 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&gcc GCC_PCIE_6_AUX_CLK>, + <&gcc GCC_PCIE_6_CFG_AHB_CLK>, + <&gcc GCC_PCIE_6_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_6_SLV_AXI_CLK>, + <&gcc GCC_PCIE_6_SLV_Q2A_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_PCIE_6_WEST_SF_AXI_CLK>; + clock-names = "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a", + "noc_aggr"; + + assigned-clocks = <&gcc GCC_PCIE_6_AUX_CLK>; + assigned-clock-rates = <19200000>; + + interconnects = <&pcie_west_anoc MASTER_PCIE_6 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &pcie_west_slv_noc SLAVE_PCIE_6 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "pcie-mem", + "cpu-pcie"; + + resets = <&gcc GCC_PCIE_6_BCR>, + <&gcc GCC_PCIE_6_LINK_DOWN_BCR>; + reset-names = "pci", + "link_down"; + + power-domains = <&gcc GCC_PCIE_6_GDSC>; + + eq-presets-8gts = /bits/ 16 <0x5555 0x5555>; + eq-presets-16gts = /bits/ 8 <0x55 0x55>; + + status = "disabled"; + + pcie6_opp_table: opp-table { + compatible = "operating-points-v2"; + + /* GEN 1 x1 */ + opp-2500000-1 { + opp-hz = /bits/ 64 <2500000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <250000 1>; + opp-level = <1>; + }; + + /* GEN 1 x2 */ + opp-5000000-1 { + opp-hz = /bits/ 64 <5000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <500000 1>; + opp-level = <1>; + }; + + /* GEN 2 x1 */ + opp-5000000-2 { + opp-hz = /bits/ 64 <5000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <500000 1>; + opp-level = <2>; + }; + + /* GEN 2 x2 */ + opp-10000000-2 { + opp-hz = /bits/ 64 <10000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <1000000 1>; + opp-level = <2>; + }; + + /* GEN 3 x1 */ + opp-8000000-3 { + opp-hz = /bits/ 64 <8000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <984500 1>; + opp-level = <3>; + }; + + /* GEN 3 x2 */ + opp-16000000-3 { + opp-hz = /bits/ 64 <16000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <1969000 1>; + opp-level = <3>; + }; + + /* GEN 4 x1 */ + opp-16000000-4 { + opp-hz = /bits/ 64 <16000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <1969000 1>; + opp-level = <4>; + }; + + /* GEN 4 x2 */ + opp-32000000-4 { + opp-hz = /bits/ 64 <32000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <3938000 1>; + opp-level = <4>; + }; + + }; + + pcie6_port0: pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + phys = <&pcie6_phy>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + + pcie6_phy: phy@1c06000 { + compatible = "qcom,glymur-qmp-gen4x2-pcie-phy"; + reg = <0x0 0x01c06000 0x0 0x2000>; + + clocks = <&gcc GCC_PCIE_PHY_6_AUX_CLK>, + <&gcc GCC_PCIE_6_CFG_AHB_CLK>, + <&tcsr TCSR_PCIE_4_CLKREF_EN>, + <&gcc GCC_PCIE_6_PHY_RCHNG_CLK>, + <&gcc GCC_PCIE_6_PIPE_CLK>, + <&gcc GCC_PCIE_6_PIPE_DIV2_CLK>; + clock-names = "aux", + "cfg_ahb", + "ref", + "rchng", + "pipe", + "pipediv2"; + + resets = <&gcc GCC_PCIE_6_PHY_BCR>, + <&gcc GCC_PCIE_6_NOCSR_COM_PHY_BCR>; + reset-names = "phy", + "phy_nocsr"; + + assigned-clocks = <&gcc GCC_PCIE_6_PHY_RCHNG_CLK>; + assigned-clock-rates = <100000000>; + + power-domains = <&gcc GCC_PCIE_6_PHY_GDSC>; + + #clock-cells = <0>; + clock-output-names = "pcie6_pipe_clk"; + + #phy-cells = <0>; + + status = "disabled"; + }; + + pcie3b: pci@1b80000 { + device_type = "pci"; + compatible = "qcom,glymur-pcie", "qcom,pcie-x1e80100"; + reg = <0x0 0x01b80000 0x0 0x3000>, + <0x0 0x74000000 0x0 0xf20>, + <0x0 0x74000f40 0x0 0xa8>, + <0x0 0x74001000 0x0 0x4000>, + <0x0 0x74100000 0x0 0x100000>, + <0x0 0x01b83000 0x0 0x1000>; + reg-names = "parf", + "dbi", + "elbi", + "atu", + "config", + "mhi"; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x01000000 0x0 0x00000000 0x0 0x74200000 0x0 0x100000>, + <0x02000000 0x0 0x74300000 0x0 0x74300000 0x0 0x3d00000>, + <0x03000000 0x7 0x40000000 0x7 0x40000000 0x0 0x40000000>; + bus-range = <0x00 0xff>; + + dma-coherent; + + linux,pci-domain = <7>; + num-lanes = <4>; + + operating-points-v2 = <&pcie3b_opp_table>; + + msi-map = <0x0 &gic_its 0xf0000 0x10000>; + iommu-map = <0x0 &pcie_smmu 0x70000 0x10000>; + + interrupts = , + , + , + , + , + , + , + , + ; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "global"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 0 0 831 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &intc 0 0 0 832 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &intc 0 0 0 833 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &intc 0 0 0 834 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&gcc GCC_PCIE_3B_AUX_CLK>, + <&gcc GCC_PCIE_3B_CFG_AHB_CLK>, + <&gcc GCC_PCIE_3B_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_3B_SLV_AXI_CLK>, + <&gcc GCC_PCIE_3B_SLV_Q2A_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_PCIE_3B_WEST_SF_AXI_CLK>; + clock-names = "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a", + "noc_aggr"; + + assigned-clocks = <&gcc GCC_PCIE_3B_AUX_CLK>; + assigned-clock-rates = <19200000>; + + interconnects = <&pcie_west_anoc MASTER_PCIE_3B QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ALWAYS + &pcie_west_slv_noc SLAVE_PCIE_3B QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "pcie-mem", + "cpu-pcie"; + + resets = <&gcc GCC_PCIE_3B_BCR>, + <&gcc GCC_PCIE_3B_LINK_DOWN_BCR>; + reset-names = "pci", + "link_down"; + + power-domains = <&gcc GCC_PCIE_3B_GDSC>; + + eq-presets-8gts = /bits/ 16 <0x5555 0x5555 0x5555 0x5555>; + eq-presets-16gts = /bits/ 8 <0x55 0x55 0x55 0x55>; + eq-presets-32gts = /bits/ 8 <0x55 0x55 0x55 0x55>; + + status = "disabled"; + + pcie3b_opp_table: opp-table { + compatible = "operating-points-v2"; + + /* GEN 1 x1 */ + opp-2500000-1 { + opp-hz = /bits/ 64 <2500000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <250000 1>; + opp-level = <1>; + }; + + /* GEN 1 x2 */ + opp-5000000-1 { + opp-hz = /bits/ 64 <5000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <500000 1>; + opp-level = <1>; + }; + + /* GEN 1 x4 */ + opp-10000000-1 { + opp-hz = /bits/ 64 <10000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <1000000 1>; + opp-level = <1>; + }; + + /* GEN 2 x1 */ + opp-5000000-2 { + opp-hz = /bits/ 64 <5000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <500000 1>; + opp-level = <2>; + }; + + /* GEN 2 x2 */ + opp-10000000-2 { + opp-hz = /bits/ 64 <10000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <1000000 1>; + opp-level = <2>; + }; + + /* GEN 2 x4 */ + opp-20000000-2 { + opp-hz = /bits/ 64 <20000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <2000000 1>; + opp-level = <2>; + }; + + /* GEN 3 x1 */ + opp-8000000-3 { + opp-hz = /bits/ 64 <8000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <984500 1>; + opp-level = <3>; + }; + + /* GEN 3 x2 */ + opp-16000000-3 { + opp-hz = /bits/ 64 <16000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <1969000 1>; + opp-level = <3>; + }; + + /* GEN 3 x4 */ + opp-32000000-3 { + opp-hz = /bits/ 64 <32000000>; + required-opps = <&rpmhpd_opp_low_svs>; + opp-peak-kBps = <3938000 1>; + opp-level = <3>; + }; + + /* GEN 4 x1 */ + opp-16000000-4 { + opp-hz = /bits/ 64 <16000000>; + required-opps = <&rpmhpd_opp_svs>; + opp-peak-kBps = <1969000 1>; + opp-level = <4>; + }; + + /* GEN 4 x2 */ + opp-32000000-4 { + opp-hz = /bits/ 64 <32000000>; + required-opps = <&rpmhpd_opp_svs>; + opp-peak-kBps = <3938000 1>; + opp-level = <4>; + }; + + /* GEN 4 x4 */ + opp-64000000-4 { + opp-hz = /bits/ 64 <64000000>; + required-opps = <&rpmhpd_opp_svs>; + opp-peak-kBps = <7876000 1>; + opp-level = <4>; + }; + + /* GEN 5 x1 */ + opp-32000000-5 { + opp-hz = /bits/ 64 <32000000>; + required-opps = <&rpmhpd_opp_nom>; + opp-peak-kBps = <3938000 1>; + opp-level = <5>; + }; + + /* GEN 5 x2 */ + opp-64000000-5 { + opp-hz = /bits/ 64 <64000000>; + required-opps = <&rpmhpd_opp_nom>; + opp-peak-kBps = <7876000 1>; + opp-level = <5>; + }; + + /* GEN 5 x4 */ + opp-128000000-5 { + opp-hz = /bits/ 64 <128000000>; + required-opps = <&rpmhpd_opp_nom>; + opp-peak-kBps = <15753000 1>; + opp-level = <5>; + }; + }; + + pcie3b_port0: pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + phys = <&pcie3b_phy>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + + pcie3b_phy: phy@f10000 { + compatible = "qcom,glymur-qmp-gen5x4-pcie-phy"; + reg = <0x0 0x00f10000 0x0 0x10000>; + + clocks = <&gcc GCC_PCIE_PHY_3B_AUX_CLK>, + <&gcc GCC_PCIE_3B_CFG_AHB_CLK>, + <&tcsr TCSR_PCIE_3_CLKREF_EN>, + <&gcc GCC_PCIE_3B_PHY_RCHNG_CLK>, + <&gcc GCC_PCIE_3B_PIPE_CLK>, + <&gcc GCC_PCIE_3B_PIPE_DIV2_CLK>; + clock-names = "aux", + "cfg_ahb", + "ref", + "rchng", + "pipe", + "pipediv2"; + + resets = <&gcc GCC_PCIE_3B_PHY_BCR>, + <&gcc GCC_PCIE_3B_NOCSR_COM_PHY_BCR>; + reset-names = "phy", + "phy_nocsr"; + + assigned-clocks = <&gcc GCC_PCIE_3B_PHY_RCHNG_CLK>; + assigned-clock-rates = <100000000>; + + power-domains = <&gcc GCC_PCIE_3B_PHY_GDSC>; + + #clock-cells = <0>; + clock-output-names = "pcie3b_pipe_clk"; + + #phy-cells = <0>; + + status = "disabled"; + }; + + tcsr_mutex: hwlock@1f40000 { + compatible = "qcom,tcsr-mutex"; + reg = <0x0 0x01f40000 0x0 0x20000>; + + #hwlock-cells = <1>; + }; + + tcsr: clock-controller@1fd5000 { + compatible = "qcom,glymur-tcsr", + "syscon"; + reg = <0x0 0x1fd5000 0x0 0x21000>; + clocks = <&rpmhcc RPMH_CXO_CLK>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + hsc_noc: interconnect@2000000 { + compatible = "qcom,glymur-hscnoc"; + reg = <0x0 0x02000000 0x0 0x93a080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + ipcc: mailbox@3e04000 { + compatible = "qcom,glymur-ipcc", "qcom,ipcc"; + reg = <0x0 0x03e04000 0x0 0x1000>; + + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; + + #mbox-cells = <2>; + }; + + lpass_lpiaon_noc: interconnect@7400000 { + compatible = "qcom,glymur-lpass-lpiaon-noc"; + reg = <0x0 0x07400000 0x0 0x19080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + lpass_lpicx_noc: interconnect@7420000 { + compatible = "qcom,glymur-lpass-lpicx-noc"; + reg = <0x0 0x07420000 0x0 0x44080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + lpass_ag_noc: interconnect@7e40000 { + compatible = "qcom,glymur-lpass-ag-noc"; + reg = <0x0 0x07e40000 0x0 0xe080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + usb_2_hsphy: phy@88e0000 { + compatible = "qcom,glymur-m31-eusb2-phy", + "qcom,sm8750-m31-eusb2-phy"; + + reg = <0x0 0x088e0000 0x0 0x29c>; + #phy-cells = <0>; + + clocks = <&tcsr TCSR_USB2_4_CLKREF_EN>; + clock-names = "ref"; + + resets = <&gcc GCC_QUSB2PHY_TERT_BCR>; + + status = "disabled"; + }; + + usb_2_qmpphy: phy@88e1000 { + compatible = "qcom,glymur-qmp-usb3-dp-phy"; + reg = <0x0 0x088e1000 0x0 0x8000>; + + clocks = <&gcc GCC_USB3_TERT_PHY_AUX_CLK>, + <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_USB3_TERT_PHY_COM_AUX_CLK>, + <&gcc GCC_USB3_TERT_PHY_PIPE_CLK>, + <&tcsr TCSR_USB4_2_CLKREF_EN>; + clock-names = "aux", + "ref", + "com_aux", + "usb3_pipe", + "clkref"; + + power-domains = <&gcc GCC_USB_2_PHY_GDSC>; + + resets = <&gcc GCC_USB3_PHY_TERT_BCR>, + <&gcc GCC_USB3PHY_PHY_TERT_BCR>; + reset-names = "phy", + "common"; + + #clock-cells = <1>; + #phy-cells = <1>; + + mode-switch; + orientation-switch; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_2_qmpphy_out: endpoint { + }; + }; + + port@1 { + reg = <1>; + + usb_2_qmpphy_usb_ss_in: endpoint { + remote-endpoint = <&usb_2_dwc3_ss>; + }; + }; + + port@2 { + reg = <2>; + + usb_2_qmpphy_dp_in: endpoint { + remote-endpoint = <&mdss_dp2_out>; + }; + }; + }; + }; + + usb_0: usb@a600000 { + compatible = "qcom,glymur-dwc3", "qcom,snps-dwc3"; + reg = <0x0 0x0a600000 0x0 0xfc100>; + + clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, + <&gcc GCC_USB30_PRIM_MASTER_CLK>, + <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, + <&gcc GCC_USB30_PRIM_SLEEP_CLK>, + <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, + <&gcc GCC_CFG_NOC_USB_ANOC_AHB_CLK>, + <&gcc GCC_CFG_NOC_USB_ANOC_SOUTH_AHB_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "noc_aggr_north", + "noc_aggr_south"; + + interrupts-extended = <&intc GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 90 IRQ_TYPE_EDGE_BOTH>, + <&pdc 60 IRQ_TYPE_EDGE_BOTH>, + <&pdc 17 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "dwc_usb3", + "pwr_event", + "dp_hs_phy_irq", + "dm_hs_phy_irq", + "ss_phy_irq"; + + power-domains = <&gcc GCC_USB30_PRIM_GDSC>; + resets = <&gcc GCC_USB30_PRIM_BCR>; + + iommus = <&apps_smmu 0x1420 0x0>; + phys = <&usb_0_hsphy>, + <&usb_0_qmpphy QMP_USB43DP_USB3_PHY>; + phy-names = "usb2-phy", + "usb3-phy"; + + snps,hird-threshold = /bits/ 8 <0x0>; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + snps,is-utmi-l1-suspend; + snps,usb3_lpm_capable; + snps,has-lpm-erratum; + tx-fifo-resize; + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + + usb-role-switch; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_0_dwc3_hs: endpoint { + }; + }; + + port@1 { + reg = <1>; + + usb_0_dwc3_ss: endpoint { + remote-endpoint = <&usb_0_qmpphy_usb_ss_in>; + }; + }; + }; + }; + + usb_1: usb@a800000 { + compatible = "qcom,glymur-dwc3", "qcom,snps-dwc3"; + reg = <0x0 0x0a800000 0x0 0xfc100>; + + clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>, + <&gcc GCC_USB30_SEC_MASTER_CLK>, + <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>, + <&gcc GCC_USB30_SEC_SLEEP_CLK>, + <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, + <&gcc GCC_CFG_NOC_USB_ANOC_AHB_CLK>, + <&gcc GCC_CFG_NOC_USB_ANOC_SOUTH_AHB_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "noc_aggr_north", + "noc_aggr_south"; + + interrupts-extended = <&intc GIC_SPI 875 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 369 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 88 IRQ_TYPE_EDGE_BOTH>, + <&pdc 87 IRQ_TYPE_EDGE_BOTH>, + <&pdc 76 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "dwc_usb3", + "pwr_event", + "dp_hs_phy_irq", + "dm_hs_phy_irq", + "ss_phy_irq"; + + resets = <&gcc GCC_USB30_SEC_BCR>; + power-domains = <&gcc GCC_USB30_SEC_GDSC>; + + iommus = <&apps_smmu 0x1460 0x0>; + + phys = <&usb_1_hsphy>, + <&usb_1_qmpphy QMP_USB43DP_USB3_PHY>; + phy-names = "usb2-phy", + "usb3-phy"; + + snps,hird-threshold = /bits/ 8 <0x0>; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + snps,is-utmi-l1-suspend; + snps,usb3_lpm_capable; + snps,has-lpm-erratum; + tx-fifo-resize; + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_1_dwc3_hs: endpoint { + }; + }; + + port@1 { + reg = <1>; + + usb_1_dwc3_ss: endpoint { + remote-endpoint = <&usb_1_qmpphy_usb_ss_in>; + }; + }; + }; + }; + + usb_2: usb@a000000 { + compatible = "qcom,glymur-dwc3", "qcom,snps-dwc3"; + reg = <0x0 0x0a000000 0x0 0xfc100>; + + clocks = <&gcc GCC_CFG_NOC_USB3_TERT_AXI_CLK>, + <&gcc GCC_USB30_TERT_MASTER_CLK>, + <&gcc GCC_AGGRE_USB3_TERT_AXI_CLK>, + <&gcc GCC_USB30_TERT_SLEEP_CLK>, + <&gcc GCC_USB30_TERT_MOCK_UTMI_CLK>, + <&gcc GCC_CFG_NOC_USB_ANOC_AHB_CLK>, + <&gcc GCC_CFG_NOC_USB_ANOC_SOUTH_AHB_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "noc_aggr_north", + "noc_aggr_south"; + + interrupts-extended = <&intc GIC_SPI 871 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 370 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 89 IRQ_TYPE_EDGE_BOTH>, + <&pdc 81 IRQ_TYPE_EDGE_BOTH>, + <&pdc 75 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "dwc_usb3", + "pwr_event", + "dp_hs_phy_irq", + "dm_hs_phy_irq", + "ss_phy_irq"; + + resets = <&gcc GCC_USB30_TERT_BCR>; + power-domains = <&gcc GCC_USB30_TERT_GDSC>; + + iommus = <&apps_smmu 0x420 0x0>; + + phys = <&usb_2_hsphy>, + <&usb_2_qmpphy QMP_USB43DP_USB3_PHY>; + phy-names = "usb2-phy", + "usb3-phy"; + + snps,hird-threshold = /bits/ 8 <0x0>; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + snps,is-utmi-l1-suspend; + snps,usb3_lpm_capable; + snps,has-lpm-erratum; + tx-fifo-resize; + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_2_dwc3_hs: endpoint { + }; + }; + + port@1 { + reg = <1>; + + usb_2_dwc3_ss: endpoint { + remote-endpoint = <&usb_2_qmpphy_usb_ss_in>; + }; + }; + }; + }; + + usb_hs: usb@a2f8800 { + compatible = "qcom,glymur-dwc3", "qcom,snps-dwc3"; + reg = <0x0 0x0a200000 0x0 0xfc100>; + + clocks = <&gcc GCC_CFG_NOC_USB2_PRIM_AXI_CLK>, + <&gcc GCC_USB20_MASTER_CLK>, + <&gcc GCC_AGGRE_USB2_PRIM_AXI_CLK>, + <&gcc GCC_USB20_SLEEP_CLK>, + <&gcc GCC_USB20_MOCK_UTMI_CLK>, + <&gcc GCC_CFG_NOC_USB_ANOC_AHB_CLK>, + <&gcc GCC_CFG_NOC_USB_ANOC_SOUTH_AHB_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "noc_aggr_north", + "noc_aggr_south"; + + assigned-clocks = <&gcc GCC_USB20_MOCK_UTMI_CLK>, + <&gcc GCC_USB20_MASTER_CLK>; + assigned-clock-rates = <19200000>, <200000000>; + + interrupts-extended = <&intc GIC_SPI 240 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 246 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 92 IRQ_TYPE_EDGE_BOTH>, + <&pdc 57 IRQ_TYPE_EDGE_BOTH>, + <&intc GIC_SPI 239 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "dwc_usb3", + "pwr_event", + "dp_hs_phy_irq", + "dm_hs_phy_irq", + "hs_phy_irq"; + + resets = <&gcc GCC_USB20_PRIM_BCR>; + + power-domains = <&gcc GCC_USB20_PRIM_GDSC>; + required-opps = <&rpmhpd_opp_nom>; + + iommus = <&apps_smmu 0x0ce0 0x0>; + + interconnects = <&aggre3_noc MASTER_USB2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_USB2 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "usb-ddr", + "apps-usb"; + + phys = <&usb_hs_phy>; + phy-names = "usb2-phy"; + + snps,hird-threshold = /bits/ 8 <0x0>; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + snps,is-utmi-l1-suspend; + snps,usb3_lpm_capable; + snps,has-lpm-erratum; + tx-fifo-resize; + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + + dr_mode = "host"; + + maximum-speed = "high-speed"; + + status = "disabled"; + }; + + usb_mp: usb@a400000 { + compatible = "qcom,glymur-dwc3-mp", "qcom,snps-dwc3"; + reg = <0x0 0x0a400000 0x0 0xfc100>; + + clocks = <&gcc GCC_CFG_NOC_USB3_MP_AXI_CLK>, + <&gcc GCC_USB30_MP_MASTER_CLK>, + <&gcc GCC_AGGRE_USB3_MP_AXI_CLK>, + <&gcc GCC_USB30_MP_SLEEP_CLK>, + <&gcc GCC_USB30_MP_MOCK_UTMI_CLK>, + <&gcc GCC_CFG_NOC_USB_ANOC_AHB_CLK>, + <&gcc GCC_CFG_NOC_USB_ANOC_SOUTH_AHB_CLK>; + clock-names = "cfg_noc", + "core", + "iface", + "sleep", + "mock_utmi", + "noc_aggr_north", + "noc_aggr_south"; + + interrupts-extended = <&intc GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 345 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 346 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 162 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 12 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 11 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 14 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 13 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 78 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 77 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "dwc_usb3", + "pwr_event_1", + "pwr_event_2", + "hs_phy_1", + "hs_phy_2", + "dp_hs_phy_1", + "dm_hs_phy_1", + "dp_hs_phy_2", + "dm_hs_phy_2", + "ss_phy_1", + "ss_phy_2"; + + resets = <&gcc GCC_USB30_MP_BCR>; + power-domains = <&gcc GCC_USB30_MP_GDSC>; + + iommus = <&apps_smmu 0xda0 0x0>; + + phys = <&usb_mp_hsphy0>, + <&usb_mp_qmpphy0>, + <&usb_mp_hsphy1>, + <&usb_mp_qmpphy1>; + phy-names = "usb2-0", + "usb3-0", + "usb2-1", + "usb3-1"; + + snps,hird-threshold = /bits/ 8 <0x0>; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + snps,is-utmi-l1-suspend; + snps,usb3_lpm_capable; + snps,has-lpm-erratum; + tx-fifo-resize; + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + + dr_mode = "host"; + + status = "disabled"; + }; + + mdss: display-subsystem@ae00000 { + compatible = "qcom,glymur-mdss"; + reg = <0x0 0x0ae00000 0x0 0x1000>; + reg-names = "mdss"; + + interrupts = ; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>, + <&dispcc DISP_CC_MDSS_MDP_CLK>; + + resets = <&dispcc DISP_CC_MDSS_CORE_BCR>; + + interconnects = <&mmss_noc MASTER_MDP QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_DISPLAY_CFG QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "mdp0-mem", + "cpu-cfg"; + + power-domains = <&dispcc DISP_CC_MDSS_CORE_GDSC>; + + iommus = <&apps_smmu 0x1de0 0x2>; + + interrupt-controller; + #interrupt-cells = <1>; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + status = "disabled"; + + mdss_mdp: display-controller@ae01000 { + compatible = "qcom,glymur-dpu"; + reg = <0x0 0x0ae01000 0x0 0x93000>, + <0x0 0x0aeb0000 0x0 0x3000>; + reg-names = "mdp", + "vbif"; + + interrupts-extended = <&mdss 0>; + + clocks = <&gcc GCC_DISP_HF_AXI_CLK>, + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&dispcc DISP_CC_MDSS_MDP_LUT_CLK>, + <&dispcc DISP_CC_MDSS_MDP_CLK>, + <&dispcc DISP_CC_MDSS_VSYNC_CLK>; + clock-names = "nrt_bus", + "iface", + "lut", + "core", + "vsync"; + + operating-points-v2 = <&mdp_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dpu_intf0_out: endpoint { + remote-endpoint = <&mdss_dp0_in>; + }; + }; + + port@4 { + reg = <4>; + + mdss_intf4_out: endpoint { + remote-endpoint = <&mdss_dp1_in>; + }; + }; + + port@5 { + reg = <5>; + + mdss_intf5_out: endpoint { + remote-endpoint = <&mdss_dp3_in>; + }; + }; + + port@6 { + reg = <6>; + + mdss_intf6_out: endpoint { + remote-endpoint = <&mdss_dp2_in>; + }; + }; + }; + + mdp_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-156000000 { + opp-hz = /bits/ 64 <156000000>; + required-opps = <&rpmhpd_opp_low_svs_d1>; + }; + + opp-205000000 { + opp-hz = /bits/ 64 <205000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-337000000 { + opp-hz = /bits/ 64 <337000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-417000000 { + opp-hz = /bits/ 64 <417000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-532000000 { + opp-hz = /bits/ 64 <532000000>; + required-opps = <&rpmhpd_opp_nom>; + }; + + opp-600000000 { + opp-hz = /bits/ 64 <600000000>; + required-opps = <&rpmhpd_opp_nom_l1>; + }; + + opp-660000000 { + opp-hz = /bits/ 64 <660000000>; + required-opps = <&rpmhpd_opp_turbo>; + }; + + opp-717000000 { + opp-hz = /bits/ 64 <717000000>; + required-opps = <&rpmhpd_opp_turbo_l1>; + }; + }; + }; + + mdss_dp0: displayport-controller@af54000 { + compatible = "qcom,glymur-dp"; + reg = <0x0 0xaf54000 0x0 0x200>, + <0x0 0xaf54200 0x0 0x200>, + <0x0 0xaf55000 0x0 0xc00>, + <0x0 0xaf56000 0x0 0x400>, + <0x0 0xaf57000 0x0 0x400>, + <0x0 0xaf58000 0x0 0x400>, + <0x0 0xaf59000 0x0 0x400>, + <0x0 0xaf5a000 0x0 0x600>, + <0x0 0xaf5b000 0x0 0x600>; + + interrupts-extended = <&mdss 12>; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&dispcc DISP_CC_MDSS_DPTX0_AUX_CLK>, + <&dispcc DISP_CC_MDSS_DPTX0_LINK_CLK>, + <&dispcc DISP_CC_MDSS_DPTX0_LINK_INTF_CLK>, + <&dispcc DISP_CC_MDSS_DPTX0_PIXEL0_CLK>, + <&dispcc DISP_CC_MDSS_DPTX0_PIXEL1_CLK>; + clock-names = "core_iface", + "core_aux", + "ctrl_link", + "ctrl_link_iface", + "stream_pixel", + "stream_1_pixel"; + + assigned-clocks = <&dispcc DISP_CC_MDSS_DPTX0_LINK_CLK_SRC>, + <&dispcc DISP_CC_MDSS_DPTX0_PIXEL0_CLK_SRC>, + <&dispcc DISP_CC_MDSS_DPTX0_PIXEL1_CLK_SRC>; + assigned-clock-parents = <&usb_0_qmpphy QMP_USB43DP_DP_LINK_CLK>, + <&usb_0_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, + <&usb_0_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>; + + operating-points-v2 = <&mdss_dp0_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + phys = <&usb_0_qmpphy QMP_USB43DP_DP_PHY>; + phy-names = "dp"; + + #sound-dai-cells = <0>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss_dp0_in: endpoint { + remote-endpoint = <&dpu_intf0_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dp0_out: endpoint { + remote-endpoint = <&usb_dp_qmpphy_dp_in>; + }; + }; + }; + + mdss_dp0_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-270000000 { + opp-hz = /bits/ 64 <270000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-540000000 { + opp-hz = /bits/ 64 <540000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-675000000 { + opp-hz = /bits/ 64 <675000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-810000000 { + opp-hz = /bits/ 64 <810000000>; + required-opps = <&rpmhpd_opp_nom>; + }; + }; + }; + + mdss_dp1: displayport-controller@af5c000 { + compatible = "qcom,glymur-dp"; + reg = <0x0 0xaf5c000 0x0 0x200>, + <0x0 0xaf5c200 0x0 0x200>, + <0x0 0xaf5d000 0x0 0xc00>, + <0x0 0xaf5e000 0x0 0x400>, + <0x0 0xaf5f000 0x0 0x400>, + <0x0 0xaf60000 0x0 0x400>, + <0x0 0xaf61000 0x0 0x400>, + <0x0 0xaf62000 0x0 0x600>, + <0x0 0xaf63000 0x0 0x600>; + + interrupts-extended = <&mdss 13>; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&dispcc DISP_CC_MDSS_DPTX1_AUX_CLK>, + <&dispcc DISP_CC_MDSS_DPTX1_LINK_CLK>, + <&dispcc DISP_CC_MDSS_DPTX1_LINK_INTF_CLK>, + <&dispcc DISP_CC_MDSS_DPTX1_PIXEL0_CLK>, + <&dispcc DISP_CC_MDSS_DPTX1_PIXEL1_CLK>; + clock-names = "core_iface", + "core_aux", + "ctrl_link", + "ctrl_link_iface", + "stream_pixel", + "stream_1_pixel"; + + assigned-clocks = <&dispcc DISP_CC_MDSS_DPTX1_LINK_CLK_SRC>, + <&dispcc DISP_CC_MDSS_DPTX1_PIXEL0_CLK_SRC>, + <&dispcc DISP_CC_MDSS_DPTX1_PIXEL1_CLK_SRC>; + assigned-clock-parents = <&usb_1_qmpphy QMP_USB43DP_DP_LINK_CLK>, + <&usb_1_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, + <&usb_1_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>; + + operating-points-v2 = <&mdss_dp0_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + phys = <&usb_1_qmpphy QMP_USB43DP_DP_PHY>; + phy-names = "dp"; + + #sound-dai-cells = <0>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss_dp1_in: endpoint { + remote-endpoint = <&mdss_intf4_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dp1_out: endpoint { + remote-endpoint = <&usb_1_qmpphy_dp_in>; + }; + }; + }; + }; + + mdss_dp2: displayport-controller@af64000 { + compatible = "qcom,glymur-dp"; + reg = <0x0 0x0af64000 0x0 0x200>, + <0x0 0x0af64200 0x0 0x200>, + <0x0 0x0af65000 0x0 0xc00>, + <0x0 0x0af66000 0x0 0x400>, + <0x0 0x0af67000 0x0 0x400>, + <0x0 0x0af68000 0x0 0x400>, + <0x0 0x0af69000 0x0 0x400>, + <0x0 0x0af6a000 0x0 0x600>, + <0x0 0x0af6b000 0x0 0x600>; + + interrupts-extended = <&mdss 14>; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&dispcc DISP_CC_MDSS_DPTX2_AUX_CLK>, + <&dispcc DISP_CC_MDSS_DPTX2_LINK_CLK>, + <&dispcc DISP_CC_MDSS_DPTX2_LINK_INTF_CLK>, + <&dispcc DISP_CC_MDSS_DPTX2_PIXEL0_CLK>, + <&dispcc DISP_CC_MDSS_DPTX2_PIXEL1_CLK>; + clock-names = "core_iface", + "core_aux", + "ctrl_link", + "ctrl_link_iface", + "stream_pixel", + "stream_1_pixel"; + + assigned-clocks = <&dispcc DISP_CC_MDSS_DPTX2_LINK_CLK_SRC>, + <&dispcc DISP_CC_MDSS_DPTX2_PIXEL0_CLK_SRC>, + <&dispcc DISP_CC_MDSS_DPTX2_PIXEL1_CLK_SRC>; + assigned-clock-parents = <&usb_2_qmpphy QMP_USB43DP_DP_LINK_CLK>, + <&usb_2_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, + <&usb_2_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>; + + operating-points-v2 = <&mdss_dp0_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + phys = <&usb_2_qmpphy QMP_USB43DP_DP_PHY>; + phy-names = "dp"; + + #sound-dai-cells = <0>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + mdss_dp2_in: endpoint { + remote-endpoint = <&mdss_intf6_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dp2_out: endpoint { + remote-endpoint = <&usb_2_qmpphy_dp_in>; + }; + }; + }; + }; + + mdss_dp3: displayport-controller@af6c000 { + compatible = "qcom,glymur-dp"; + reg = <0x0 0x0af6c000 0x0 0x200>, + <0x0 0x0af6c200 0x0 0x200>, + <0x0 0x0af6d000 0x0 0xc00>, + <0x0 0x0af6e000 0x0 0x400>, + <0x0 0x0af6f000 0x0 0x400>, + <0x0 0x0af70000 0x0 0x400>, + <0x0 0x0af71000 0x0 0x400>, + <0x0 0x0af72000 0x0 0x600>, + <0x0 0x0af73000 0x0 0x600>; + + interrupts-extended = <&mdss 15>; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&dispcc DISP_CC_MDSS_DPTX3_AUX_CLK>, + <&dispcc DISP_CC_MDSS_DPTX3_LINK_CLK>, + <&dispcc DISP_CC_MDSS_DPTX3_LINK_INTF_CLK>, + <&dispcc DISP_CC_MDSS_DPTX3_PIXEL0_CLK>; + clock-names = "core_iface", + "core_aux", + "ctrl_link", + "ctrl_link_iface", + "stream_pixel"; + + assigned-clocks = <&dispcc DISP_CC_MDSS_DPTX3_LINK_CLK_SRC>, + <&dispcc DISP_CC_MDSS_DPTX3_PIXEL0_CLK_SRC>; + assigned-clock-parents = <&mdss_dp3_phy 0>, + <&mdss_dp3_phy 1>; + + operating-points-v2 = <&mdss_dp0_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + phys = <&mdss_dp3_phy>; + phy-names = "dp"; + + #sound-dai-cells = <0>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss_dp3_in: endpoint { + remote-endpoint = <&mdss_intf5_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dp3_out: endpoint { + }; + }; + }; + }; + }; + + dispcc: clock-controller@af00000 { + compatible = "qcom,glymur-dispcc"; + reg = <0x0 0x0af00000 0x0 0x20000>; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&sleep_clk>, + <&usb_0_qmpphy QMP_USB43DP_DP_LINK_CLK>, /* dp0 */ + <&usb_0_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, + <&usb_1_qmpphy QMP_USB43DP_DP_LINK_CLK>, /* dp1 */ + <&usb_1_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, + <&usb_2_qmpphy QMP_USB43DP_DP_LINK_CLK>, /* dp2 */ + <&usb_2_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, + <&mdss_dp3_phy 0>, /* dp3 */ + <&mdss_dp3_phy 1>, + <0>, /* dsi0 */ + <0>, + <0>, /* dsi1 */ + <0>, + <0>, + <0>, + <0>, + <0>; + power-domains = <&rpmhpd RPMHPD_MMCX>; + required-opps = <&rpmhpd_opp_low_svs>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + + pdc: interrupt-controller@b220000 { + compatible = "qcom,glymur-pdc", "qcom,pdc"; + reg = <0x0 0x0b220000 0x0 0x10000>; + qcom,pdc-ranges = <0 745 51>, + <51 527 47>, + <98 609 32>, + <130 717 12>, + <142 251 5>, + <147 796 16>, + <171 4104 36>; + #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupt-controller; + }; + + tsens0: thermal-sensor@c22c000 { + compatible = "qcom,glymur-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22c000 0x0 0x1000>, + <0x0 0x0c222000 0x0 0x1000>; + + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + + #qcom,sensors = <13>; + + #thermal-sensor-cells = <1>; + }; + + tsens1: thermal-sensor@c22d000 { + compatible = "qcom,glymur-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22d000 0x0 0x1000>, + <0x0 0x0c223000 0x0 0x1000>; + + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + + #qcom,sensors = <9>; + + #thermal-sensor-cells = <1>; + }; + + tsens2: thermal-sensor@c22e000 { + compatible = "qcom,glymur-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22e000 0x0 0x1000>, + <0x0 0x0c224000 0x0 0x1000>; + + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + + #qcom,sensors = <13>; + + #thermal-sensor-cells = <1>; + }; + + tsens3: thermal-sensor@c22f000 { + compatible = "qcom,glymur-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22f000 0x0 0x1000>, + <0x0 0x0c225000 0x0 0x1000>; + + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + + #qcom,sensors = <8>; + + #thermal-sensor-cells = <1>; + }; + + tsens4: thermal-sensor@c230000 { + compatible = "qcom,glymur-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c230000 0x0 0x1000>, + <0x0 0x0c226000 0x0 0x1000>; + + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + + #qcom,sensors = <13>; + + #thermal-sensor-cells = <1>; + }; + + tsens5: thermal-sensor@c231000 { + compatible = "qcom,glymur-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c231000 0x0 0x1000>, + <0x0 0x0c227000 0x0 0x1000>; + + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + + #qcom,sensors = <8>; + + #thermal-sensor-cells = <1>; + }; + + tsens6: thermal-sensor@c232000 { + compatible = "qcom,glymur-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c232000 0x0 0x1000>, + <0x0 0x0c228000 0x0 0x1000>; + + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + + #qcom,sensors = <13>; + + #thermal-sensor-cells = <1>; + }; + + tsens7: thermal-sensor@c233000 { + compatible = "qcom,glymur-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c233000 0x0 0x1000>, + <0x0 0x0c229000 0x0 0x1000>; + + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + + #qcom,sensors = <15>; + + #thermal-sensor-cells = <1>; + }; + + aoss_qmp: power-management@c300000 { + compatible = "qcom,glymur-aoss-qmp", "qcom,aoss-qmp"; + reg = <0x0 0x0c300000 0x0 0x400>; + interrupt-parent = <&ipcc>; + interrupts-extended = <&ipcc IPCC_MPROC_AOP IPCC_MPROC_SIGNAL_GLINK_QMP + IRQ_TYPE_EDGE_RISING>; + mboxes = <&ipcc IPCC_MPROC_AOP IPCC_MPROC_SIGNAL_GLINK_QMP>; + + #clock-cells = <0>; + }; + + sram@c30f000 { + compatible = "qcom,rpmh-stats"; + reg = <0x0 0x0c30f000 0x0 0x400>; + }; + + arbiter@c400000 { + compatible = "qcom,glymur-spmi-pmic-arb"; + reg = <0x0 0x0c400000 0x0 0x3000>, + <0x0 0x0c900000 0x0 0x400000>, + <0x0 0x0c4c0000 0x0 0x400000>, + <0x0 0x0c403000 0x0 0x8000>; + reg-names = "core", + "chnls", + "obsrvr", + "chnl_map"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + qcom,channel = <0>; + qcom,ee = <0>; + + spmi_bus0: spmi@c426000 { + reg = <0x0 0x0c426000 0x0 0x4000>, + <0x0 0x0c8c0000 0x0 0x10000>, + <0x0 0x0c42a000 0x0 0x8000>; + reg-names = "cnfg", + "intr", + "chnl_owner"; + interrupts-extended = <&pdc 1 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "periph_irq"; + interrupt-controller; + #interrupt-cells = <4>; + #address-cells = <2>; + #size-cells = <0>; + }; + + spmi_bus1: spmi@c437000 { + reg = <0x0 0x0c437000 0x0 0x4000>, + <0x0 0x0c8d0000 0x0 0x10000>, + <0x0 0x0c43b000 0x0 0x8000>; + reg-names = "cnfg", + "intr", + "chnl_owner"; + interrupts-extended = <&pdc 3 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "periph_irq"; + interrupt-controller; + #interrupt-cells = <4>; + #address-cells = <2>; + #size-cells = <0>; + }; + + spmi_bus2: spmi@c48000 { + reg = <0x0 0x0c448000 0x0 0x4000>, + <0x0 0x0c8e0000 0x0 0x10000>, + <0x0 0x0c44c000 0x0 0x8000>; + reg-names = "cnfg", + "intr", + "chnl_owner"; + interrupts-extended = <&pdc 72 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "periph_irq"; + interrupt-controller; + #interrupt-cells = <4>; + #address-cells = <2>; + #size-cells = <0>; + }; + }; + + tlmm: pinctrl@f100000 { + compatible = "qcom,glymur-tlmm"; + reg = <0x0 0x0f100000 0x0 0xf00000>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&tlmm 0 0 249>; + wakeup-parent = <&pdc>; + + qup_i2c0_data_clk: qup-i2c0-data-clk-state { + /* SDA, SCL */ + pins = "gpio0", "gpio1"; + function = "qup0_se0"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c1_data_clk: qup-i2c1-data-clk-state { + /* SDA, SCL */ + pins = "gpio4", "gpio5"; + function = "qup0_se1"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c2_data_clk: qup-i2c2-data-clk-state { + /* SDA, SCL */ + pins = "gpio8", "gpio9"; + function = "qup0_se2"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c3_data_clk: qup-i2c3-data-clk-state { + /* SDA, SCL */ + pins = "gpio12", "gpio13"; + function = "qup0_se3"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c4_data_clk: qup-i2c4-data-clk-state { + /* SDA, SCL */ + pins = "gpio16", "gpio17"; + function = "qup0_se4"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c5_data_clk: qup-i2c5-data-clk-state { + /* SDA, SCL */ + pins = "gpio20", "gpio21"; + function = "qup0_se5"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c6_data_clk: qup-i2c6-data-clk-state { + /* SDA, SCL */ + pins = "gpio6", "gpio7"; + function = "qup0_se6"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c7_data_clk: qup-i2c7-data-clk-state { + /* SDA, SCL */ + pins = "gpio14", "gpio15"; + function = "qup0_se7"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c8_data_clk: qup-i2c8-data-clk-state { + /* SDA, SCL */ + pins = "gpio32", "gpio33"; + function = "qup1_se0"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c9_data_clk: qup-i2c9-data-clk-state { + /* SDA, SCL */ + pins = "gpio36", "gpio37"; + function = "qup1_se1"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c10_data_clk: qup-i2c10-data-clk-state { + /* SDA, SCL */ + pins = "gpio40", "gpio41"; + function = "qup1_se2"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c11_data_clk: qup-i2c11-data-clk-state { + /* SDA, SCL */ + pins = "gpio44", "gpio45"; + function = "qup1_se3"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c12_data_clk: qup-i2c12-data-clk-state { + /* SDA, SCL */ + pins = "gpio48", "gpio49"; + function = "qup1_se4"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c13_data_clk: qup-i2c13-data-clk-state { + /* SDA, SCL */ + pins = "gpio52", "gpio53"; + function = "qup1_se5"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c14_data_clk: qup-i2c14-data-clk-state { + /* SDA, SCL */ + pins = "gpio56", "gpio57"; + function = "qup1_se6"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c15_data_clk: qup-i2c15-data-clk-state { + /* SDA, SCL */ + pins = "gpio54", "gpio55"; + function = "qup1_se7"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c16_data_clk: qup-i2c16-data-clk-state { + /* SDA, SCL */ + pins = "gpio64", "gpio65"; + function = "qup2_se0"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c17_data_clk: qup-i2c17-data-clk-state { + /* SDA, SCL */ + pins = "gpio68", "gpio69"; + function = "qup2_se1"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c18_data_clk: qup-i2c18-data-clk-state { + /* SDA, SCL */ + pins = "gpio72", "gpio73"; + function = "qup2_se2"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c19_data_clk: qup-i2c19-data-clk-state { + /* SDA, SCL */ + pins = "gpio76", "gpio77"; + function = "qup2_se3"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c20_data_clk: qup-i2c20-data-clk-state { + /* SDA, SCL */ + pins = "gpio80", "gpio81"; + function = "qup2_se4"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c21_data_clk: qup-i2c21-data-clk-state { + /* SDA, SCL */ + pins = "gpio84", "gpio85"; + function = "qup2_se5"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c22_data_clk: qup-i2c22-data-clk-state { + /* SDA, SCL */ + pins = "gpio88", "gpio89"; + function = "qup2_se6"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_i2c23_data_clk: qup-i2c23-data-clk-state { + /* SDA, SCL */ + pins = "gpio80", "gpio81"; + function = "qup2_se7"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + qup_spi0_cs: qup-spi0-cs-state { + pins = "gpio3"; + function = "qup0_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi0_data_clk: qup-spi0-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio0", "gpio1", "gpio2"; + function = "qup0_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi1_cs: qup-spi1-cs-state { + pins = "gpio7"; + function = "qup0_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi1_data_clk: qup-spi1-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio4", "gpio5", "gpio6"; + function = "qup0_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi2_cs: qup-spi2-cs-state { + pins = "gpio11"; + function = "qup0_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi2_data_clk: qup-spi2-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio8", "gpio9", "gpio10"; + function = "qup0_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi3_cs: qup-spi3-cs-state { + pins = "gpio15"; + function = "qup0_se3"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi3_data_clk: qup-spi3-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio12", "gpio13", "gpio14"; + function = "qup0_se3"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi4_cs: qup-spi4-cs-state { + pins = "gpio19"; + function = "qup0_se4"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi4_data_clk: qup-spi4-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio16", "gpio17", "gpio18"; + function = "qup0_se4"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi5_cs: qup-spi5-cs-state { + pins = "gpio23"; + function = "qup0_se5"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi5_data_clk: qup-spi5-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio20", "gpio21", "gpio22"; + function = "qup0_se5"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi6_cs: qup-spi6-cs-state { + pins = "gpio5"; + function = "qup0_se6"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi6_data_clk: qup-spi6-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio6", "gpio7", "gpio4"; + function = "qup0_se6"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi7_cs: qup-spi7-cs-state { + pins = "gpio13"; + function = "qup0_se7"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi7_data_clk: qup-spi7-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio14", "gpio15", "gpio12"; + function = "qup0_se7"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi8_cs: qup-spi8-cs-state { + pins = "gpio35"; + function = "qup1_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi8_data_clk: qup-spi8-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio32", "gpio33", "gpio34"; + function = "qup1_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi9_cs: qup-spi9-cs-state { + pins = "gpio39"; + function = "qup1_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi9_data_clk: qup-spi9-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio36", "gpio37", "gpio38"; + function = "qup1_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi10_cs: qup-spi10-cs-state { + pins = "gpio43"; + function = "qup1_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi10_data_clk: qup-spi10-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio40", "gpio41", "gpio42"; + function = "qup1_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi11_cs: qup-spi11-cs-state { + pins = "gpio47"; + function = "qup1_se3"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi11_data_clk: qup-spi11-data-clk-state { + pins = "gpio44", "gpio45", "gpio46"; + function = "qup1_se3"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi12_cs: qup-spi12-cs-state { + pins = "gpio51"; + function = "qup1_se4"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi12_data_clk: qup-spi12-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio48", "gpio49", "gpio50"; + function = "qup1_se4"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi13_cs: qup-spi13-cs-state { + pins = "gpio55"; + function = "qup1_se5"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi13_data_clk: qup-spi13-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio52", "gpio53", "gpio54"; + function = "qup1_se5"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi14_cs: qup-spi14-cs-state { + pins = "gpio59"; + function = "qup1_se6"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi14_data_clk: qup-spi14-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio56", "gpio57", "gpio58"; + function = "qup1_se6"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi15_cs: qup-spi15-cs-state { + pins = "gpio53"; + function = "qup1_se7"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi15_data_clk: qup-spi15-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio54", "gpio55", "gpio52"; + function = "qup1_se7"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi16_cs: qup-spi16-cs-state { + pins = "gpio67"; + function = "qup2_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi16_data_clk: qup-spi16-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio64", "gpio65", "gpio66"; + function = "qup2_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi17_cs: qup-spi17-cs-state { + pins = "gpio71"; + function = "qup2_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi17_data_clk: qup-spi17-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio68", "gpio69", "gpio70"; + function = "qup2_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi18_cs: qup-spi18-cs-state { + pins = "gpio75"; + function = "qup2_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi18_data_clk: qup-spi18-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio72", "gpio73", "gpio74"; + function = "qup2_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi19_cs: qup-spi19-cs-state { + pins = "gpio79"; + function = "qup2_se3"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi19_data_clk: qup-spi19-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio76", "gpio77", "gpio78"; + function = "qup2_se3"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi20_cs: qup-spi20-cs-state { + pins = "gpio83"; + function = "qup2_se4"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi20_data_clk: qup-spi20-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio80", "gpio81", "gpio82"; + function = "qup2_se4"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi21_cs: qup-spi21-cs-state { + pins = "gpio87"; + function = "qup2_se5"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi21_data_clk: qup-spi21-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio84", "gpio85", "gpio86"; + function = "qup2_se5"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi22_cs: qup-spi22-cs-state { + pins = "gpio91"; + function = "qup2_se6"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi22_data_clk: qup-spi22-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio88", "gpio89", "gpio90"; + function = "qup2_se6"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi23_cs: qup-spi23-cs-state { + pins = "gpio83"; + function = "qup2_se7"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi23_data_clk: qup-spi23-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio80", "gpio81", "gpio82"; + function = "qup2_se7"; + drive-strength = <6>; + bias-disable; + }; + + qup_uart2_default: qup-uart2-default-state { + tx-pins { + pins = "gpio10"; + function = "qup0_se2"; + drive-strength = <2>; + bias-disable; + }; + + rx-pins { + pins = "gpio11"; + function = "qup0_se2"; + drive-strength = <2>; + bias-disable; + }; + }; + + qup_uart14_default: qup-uart14-default-state { + cts-pins { + pins = "gpio56"; + function = "qup1_se6"; + drive-strength = <2>; + bias-disable; + }; + + rts-pins { + pins = "gpio57"; + function = "qup1_se6"; + drive-strength = <2>; + bias-disable; + }; + + tx-pins { + pins = "gpio58"; + function = "qup1_se6"; + drive-strength = <2>; + bias-disable; + }; + + rx-pins { + pins = "gpio59"; + function = "qup1_se6"; + drive-strength = <2>; + bias-disable; + }; + }; + + qup_uart19_default: qup-uart19-default-state { + cts-pins { + pins = "gpio76"; + function = "qup2_se3"; + drive-strength = <2>; + bias-disable; + }; + + rts-pins { + pins = "gpio77"; + function = "qup2_se3"; + drive-strength = <2>; + bias-disable; + }; + + tx-pins { + pins = "gpio78"; + function = "qup2_se3"; + drive-strength = <2>; + bias-disable; + }; + + rx-pins { + pins = "gpio79"; + function = "qup2_se3"; + drive-strength = <2>; + bias-disable; + }; + }; + + qup_uart21_default: qup-uart21-default-state { + tx-pins { + pins = "gpio86"; + function = "qup2_se5"; + drive-strength = <2>; + bias-disable; + }; + + rx-pins { + pins = "gpio87"; + function = "qup2_se5"; + drive-strength = <2>; + bias-disable; + }; + }; + + qup_uart22_default: qup-uart22-default-state { + tx-pins { + pins = "gpio90"; + function = "qup2_se6"; + drive-strength = <2>; + bias-disable; + }; + + rx-pins { + pins = "gpio91"; + function = "qup2_se6"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + apps_smmu: iommu@15000000 { + compatible = "qcom,glymur-smmu-500", + "qcom,smmu-500", + "arm,mmu-500"; + reg = <0x0 0x15000000 0x0 0x100000>; + + #iommu-cells = <2>; + #global-interrupts = <1>; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + + dma-coherent; + }; + + pcie_smmu: iommu@15480000 { + compatible = "arm,smmu-v3"; + reg = <0x0 0x15480000 0x0 0x20000>; + interrupts = , + , + ; + interrupt-names = "eventq", "cmdq-sync", "gerror"; + dma-coherent; + #iommu-cells = <1>; + }; + + intc: interrupt-controller@17000000 { + compatible = "arm,gic-v3"; + reg = <0x0 0x17000000 0x0 0x10000>, + <0x0 0x17080000 0x0 0x480000>; + + interrupts = ; + + #interrupt-cells = <3>; + interrupt-controller; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gic_its: msi-controller@17040000 { + compatible = "arm,gic-v3-its"; + reg = <0x0 0x17040000 0x0 0x40000>; + + msi-controller; + #msi-cells = <1>; + }; + }; + + watchdog@17600000 { + compatible = "qcom,apss-wdt-glymur", "qcom,kpss-wdt"; + reg = <0x0 0x17600000 0x0 0x1000>; + clocks = <&sleep_clk>; + interrupts = ; + }; + + pdp0_mbox: mailbox@17610000 { + compatible = "qcom,glymur-cpucp-mbox", "qcom,x1e80100-cpucp-mbox"; + reg = <0x0 0x17610000 0 0x8000>, <0 0x19980000 0 0x8000>; + interrupts = ; + #mbox-cells = <1>; + }; + + timer@17810000 { + compatible = "arm,armv7-timer-mem"; + reg = <0x0 0x17810000 0x0 0x1000>; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0x0 0x0 0x0 0x0 0x20000000>; + + frame@17811000 { + reg = <0x0 0x17811000 0x1000>, + <0x0 0x17812000 0x1000>; + + interrupts = , + ; + + frame-number = <0>; + }; + + frame@17813000 { + reg = <0x0 0x17813000 0x1000>; + + interrupts = ; + + frame-number = <1>; + + status = "disabled"; + }; + + frame@17815000 { + reg = <0x0 0x17815000 0x1000>; + + interrupts = ; + + frame-number = <2>; + + status = "disabled"; + }; + + frame@17817000 { + reg = <0x0 0x17817000 0x1000>; + + interrupts = ; + + frame-number = <3>; + + status = "disabled"; + }; + + frame@17819000 { + reg = <0x0 0x17819000 0x1000>; + + interrupts = ; + + frame-number = <4>; + + status = "disabled"; + }; + + frame@1781b000 { + reg = <0x0 0x1781b000 0x1000>; + + interrupts = ; + + frame-number = <5>; + + status = "disabled"; + }; + + frame@1781d000 { + reg = <0x0 0x1781d000 0x1000>; + + interrupts = ; + + frame-number = <6>; + + status = "disabled"; + }; + }; + + apps_rsc: rsc@18900000 { + compatible = "qcom,rpmh-rsc"; + label = "apps_rsc"; + reg = <0x0 0x18900000 0x0 0x10000>, + <0x0 0x18910000 0x0 0x10000>, + <0x0 0x18920000 0x0 0x10000>; + reg-names = "drv-0", + "drv-1", + "drv-2"; + interrupts = , + , + ; + qcom,tcs-offset = <0xd00>; + qcom,drv-id = <2>; + qcom,tcs-config = , + , + , + ; + power-domains = <&system_pd>; + + apps_bcm_voter: bcm-voter { + compatible = "qcom,bcm-voter"; + }; + + rpmhcc: clock-controller { + compatible = "qcom,glymur-rpmh-clk"; + + clocks = <&xo_board>; + clock-names = "xo"; + + #clock-cells = <1>; + }; + + rpmhpd: power-controller { + compatible = "qcom,glymur-rpmhpd"; + + operating-points-v2 = <&rpmhpd_opp_table>; + + #power-domain-cells = <1>; + + rpmhpd_opp_table: opp-table { + compatible = "operating-points-v2"; + + rpmhpd_opp_ret: opp-16 { + opp-level = ; + }; + + rpmhpd_opp_min_svs: opp-48 { + opp-level = ; + }; + + rpmhpd_opp_low_svs_d2: opp-52 { + opp-level = ; + }; + + rpmhpd_opp_low_svs_d1: opp-56 { + opp-level = ; + }; + + rpmhpd_opp_low_svs_d0: opp-60 { + opp-level = ; + }; + + rpmhpd_opp_low_svs: opp-64 { + opp-level = ; + }; + + rpmhpd_opp_low_svs_l1: opp-80 { + opp-level = ; + }; + + rpmhpd_opp_svs: opp-128 { + opp-level = ; + }; + + rpmhpd_opp_svs_l0: opp-144 { + opp-level = ; + }; + + rpmhpd_opp_svs_l1: opp-192 { + opp-level = ; + }; + + rpmhpd_opp_nom: opp-256 { + opp-level = ; + }; + + rpmhpd_opp_nom_l1: opp-320 { + opp-level = ; + }; + + rpmhpd_opp_nom_l2: opp-336 { + opp-level = ; + }; + + rpmhpd_opp_turbo: opp-384 { + opp-level = ; + }; + + rpmhpd_opp_turbo_l1: opp-416 { + opp-level = ; + }; + }; + }; + }; + + nsi_noc: interconnect@1d600000 { + compatible = "qcom,glymur-nsinoc"; + reg = <0x0 0x1d600000 0x0 0x14080>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + oobm_ss_noc: interconnect@1f300000 { + compatible = "qcom,glymur-oobm-ss-noc"; + reg = <0x0 0x1f300000 0x0 0x49a00>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + system-cache-controller@20400000 { + compatible = "qcom,glymur-llcc"; + reg = <0x0 0x21800000 0x0 0x100000>, + <0x0 0x21a00000 0x0 0x100000>, + <0x0 0x21c00000 0x0 0x100000>, + <0x0 0x21e00000 0x0 0x100000>, + <0x0 0x22800000 0x0 0x100000>, + <0x0 0x22a00000 0x0 0x100000>, + <0x0 0x22c00000 0x0 0x100000>, + <0x0 0x22e00000 0x0 0x100000>, + <0x0 0x23800000 0x0 0x100000>, + <0x0 0x23a00000 0x0 0x100000>, + <0x0 0x23c00000 0x0 0x100000>, + <0x0 0x23e00000 0x0 0x100000>, + <0x0 0x20400000 0x0 0x100000>, + <0x0 0x20600000 0x0 0x100000>; + reg-names = "llcc0_base", + "llcc1_base", + "llcc2_base", + "llcc3_base", + "llcc4_base", + "llcc5_base", + "llcc6_base", + "llcc7_base", + "llcc8_base", + "llcc9_base", + "llcc10_base", + "llcc11_base", + "llcc_broadcast_base", + "llcc_broadcast_and_base"; + + interrupts = ; + }; + + nsp_noc: interconnect@320c0000 { + compatible = "qcom,glymur-nsp-noc"; + reg = <0x0 0x320c0000 0x0 0x21280>; + qcom,bcm-voters = <&apps_bcm_voter>; + #interconnect-cells = <2>; + }; + + imem: sram@81e08000 { + compatible = "mmio-sram"; + reg = <0x0 0x81e08600 0x0 0x300>; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x81e08600 0x300>; + + cpu_scp_lpri0: scp-sram-section@0 { + compatible = "arm,scmi-shmem"; + reg = <0x0 0x180>; + }; + + cpu_scp_lpri1: scp-sram-section@180 { + compatible = "arm,scmi-shmem"; + reg = <0x180 0x180>; + }; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + , + ; + }; + + thermal_zones: thermal-zones { + aoss-0-thermal { + thermal-sensors = <&tsens0 0>; + + trips { + aoss-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-0-0-0-thermal { + thermal-sensors = <&tsens0 1>; + + trips { + cpu-0-0-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-0-0-1-thermal { + thermal-sensors = <&tsens0 2>; + + trips { + cpu-0-0-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-0-1-0-thermal { + thermal-sensors = <&tsens0 3>; + + trips { + cpu-0-1-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-0-1-1-thermal { + thermal-sensors = <&tsens0 4>; + + trips { + cpu-0-1-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-0-2-0-thermal { + thermal-sensors = <&tsens0 5>; + + trips { + cpu-0-2-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-0-2-1-thermal { + thermal-sensors = <&tsens0 6>; + + trips { + cpu-0-2-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-0-3-0-thermal { + thermal-sensors = <&tsens0 7>; + + trips { + cpu-0-3-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-0-3-1-thermal { + thermal-sensors = <&tsens0 8>; + + trips { + cpu-0-3-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-0-4-0-thermal { + thermal-sensors = <&tsens0 9>; + + trips { + cpu-0-4-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-0-4-1-thermal { + thermal-sensors = <&tsens0 10>; + + trips { + cpu-0-4-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-0-5-0-thermal { + thermal-sensors = <&tsens0 11>; + + trips { + cpu-0-5-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-0-5-1-thermal { + thermal-sensors = <&tsens0 12>; + + trips { + cpu-0-5-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + aoss-1-thermal { + thermal-sensors = <&tsens1 0>; + + trips { + aoss-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpullc-0-0-thermal { + thermal-sensors = <&tsens1 1>; + + trips { + cpullc-0-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpullc-0-1-thermal { + thermal-sensors = <&tsens1 2>; + + trips { + cpullc-0-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + qmx-0-0-thermal { + thermal-sensors = <&tsens1 3>; + + trips { + qmx-0-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + qmx-0-1-thermal { + thermal-sensors = <&tsens1 4>; + + trips { + qmx-0-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + qmx-0-2-thermal { + thermal-sensors = <&tsens1 5>; + + trips { + qmx-0-2-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + ddr-0-thermal { + thermal-sensors = <&tsens1 6>; + + trips { + ddr-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_video_0: video-0-thermal { + thermal-sensors = <&tsens1 7>; + + trips { + video-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_video_1: video-1-thermal { + thermal-sensors = <&tsens1 8>; + + trips { + video-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + aoss-2-thermal { + thermal-sensors = <&tsens2 0>; + + trips { + aoss-2-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-1-0-0-thermal { + thermal-sensors = <&tsens2 1>; + + trips { + cpu-1-0-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-1-0-1-thermal { + thermal-sensors = <&tsens2 2>; + + trips { + cpu-1-0-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-1-1-0-thermal { + thermal-sensors = <&tsens2 3>; + + trips { + cpu-1-1-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-1-1-1-thermal { + thermal-sensors = <&tsens2 4>; + + trips { + cpu-1-1-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-1-2-0-thermal { + thermal-sensors = <&tsens2 5>; + + trips { + cpu-1-2-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-1-2-1-thermal { + thermal-sensors = <&tsens2 6>; + + trips { + cpu-1-2-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-1-3-0-thermal { + thermal-sensors = <&tsens2 7>; + + trips { + cpu-1-3-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-1-3-1-thermal { + thermal-sensors = <&tsens2 8>; + + trips { + cpu-1-3-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-1-4-0-thermal { + thermal-sensors = <&tsens2 9>; + + trips { + cpu-1-4-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-1-4-1-thermal { + thermal-sensors = <&tsens2 10>; + + trips { + cpu-1-4-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-1-5-0-thermal { + thermal-sensors = <&tsens2 11>; + + trips { + cpu-1-5-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpu-1-5-1-thermal { + thermal-sensors = <&tsens2 12>; + + trips { + cpu-1-5-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + aoss-3-thermal { + thermal-sensors = <&tsens3 0>; + + trips { + aoss-3-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpullc-1-0-thermal { + thermal-sensors = <&tsens3 1>; + + trips { + cpullc-1-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + cpullc-1-1-thermal { + thermal-sensors = <&tsens3 2>; + + trips { + cpullc-1-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + qmx-1-0-thermal { + thermal-sensors = <&tsens3 3>; + + trips { + qmx-1-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + qmx-1-1-thermal { + thermal-sensors = <&tsens3 4>; + + trips { + qmx-1-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + qmx-1-2-thermal { + thermal-sensors = <&tsens3 5>; + + trips { + qmx-1-2-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + qmx-1-3-thermal { + thermal-sensors = <&tsens3 6>; + + trips { + qmx-1-3-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + qmx-1-4-thermal { + thermal-sensors = <&tsens3 7>; + + trips { + qmx-1-4-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + aoss-4-thermal { + thermal-sensors = <&tsens4 0>; + + trips { + aoss-4-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpu_2_0_0: cpu-2-0-0-thermal { + thermal-sensors = <&tsens4 1>; + + trips { + cpu-2-0-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpu_2_0_1: cpu-2-0-1-thermal { + thermal-sensors = <&tsens4 2>; + + trips { + cpu-2-0-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpu_2_1_0: cpu-2-1-0-thermal { + thermal-sensors = <&tsens4 3>; + + trips { + cpu-2-1-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpu_2_1_1: cpu-2-1-1-thermal { + thermal-sensors = <&tsens4 4>; + + trips { + cpu-2-1-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpu_2_2_0: cpu-2-2-0-thermal { + thermal-sensors = <&tsens4 5>; + + trips { + cpu-2-2-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpu_2_2_1: cpu-2-2-1-thermal { + thermal-sensors = <&tsens4 6>; + + trips { + cpu-2-2-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpu_2_3_0: cpu-2-3-0-thermal { + thermal-sensors = <&tsens4 7>; + + trips { + cpu-2-3-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpu_2_3_1: cpu-2-3-1-thermal { + thermal-sensors = <&tsens4 8>; + + trips { + cpu-2-3-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpu_2_4_0: cpu-2-4-0-thermal { + thermal-sensors = <&tsens4 9>; + + trips { + cpu-2-4-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpu_2_4_1: cpu-2-4-1-thermal { + thermal-sensors = <&tsens4 10>; + + trips { + cpu-2-4-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpu_2_5_0: cpu-2-5-0-thermal { + thermal-sensors = <&tsens4 11>; + + trips { + cpu-2-5-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpu_2_5_1: cpu-2-5-1-thermal { + thermal-sensors = <&tsens4 12>; + + trips { + cpu-2-5-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + aoss-5-thermal { + thermal-sensors = <&tsens5 0>; + + trips { + aoss-5-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpullc_2_0: cpullc-2-0-thermal { + thermal-sensors = <&tsens5 1>; + + trips { + cpullc-2-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_cpuillc_2_1: cpuillc-2-1-thermal { + thermal-sensors = <&tsens5 2>; + + trips { + cpullc-2-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_qmx_2_0: qmx-2-0-thermal { + thermal-sensors = <&tsens5 3>; + + trips { + qmx-2-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_qmx_2_1: qmx-2-1-thermal { + thermal-sensors = <&tsens5 4>; + + trips { + qmx-2-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_qmx_2_2: qmx-2-2-thermal { + thermal-sensors = <&tsens5 5>; + + trips { + qmx-2-2-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_qmx_2_3: qmx-2-3-thermal { + thermal-sensors = <&tsens5 6>; + + trips { + qmx-2-3-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_qmx_2_4: qmx-2-4-thermal { + thermal-sensors = <&tsens5 7>; + + trips { + qmx-2-4-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_aoss_6: aoss-6-thermal { + thermal-sensors = <&tsens6 0>; + + trips { + aoss-6-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_nsphvx_0: nsphvx-0-thermal { + thermal-sensors = <&tsens6 1>; + + trips { + nsphvx-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_nsphvx_1: nsphvx-1-thermal { + thermal-sensors = <&tsens6 2>; + + trips { + nsphvx-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_nsphvx_2: nsphvx-2-thermal { + thermal-sensors = <&tsens6 3>; + + trips { + nsphvx-2-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_nsphvx_3: nsphvx-3-thermal { + thermal-sensors = <&tsens6 4>; + + trips { + nsphvx-3-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_nsphmx_0: nsphmx-0-thermal { + thermal-sensors = <&tsens6 5>; + + trips { + nsphmx-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_nsphmx_1: nsphmx-1-thermal { + thermal-sensors = <&tsens6 6>; + + trips { + nsphmx-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_nsphmx_2: nsphmx-2-thermal { + thermal-sensors = <&tsens6 7>; + + trips { + nsphmx-2-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_nsphmx_3: nsphmx-3-thermal { + thermal-sensors = <&tsens6 8>; + + trips { + nsphmx-3-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_camera_0: camera-0-thermal { + thermal-sensors = <&tsens6 9>; + + trips { + camera-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_camera_1: camera-1-thermal { + thermal-sensors = <&tsens6 10>; + + trips { + camera-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_ddr_1: ddr-1-thermal { + thermal-sensors = <&tsens6 11>; + + trips { + ddr-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_ddr_2: ddr-2-thermal { + thermal-sensors = <&tsens6 12>; + + trips { + ddr-2-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_aoss_7: aoss-7-thermal { + thermal-sensors = <&tsens7 0>; + + trips { + aoss-7-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpu_0_0: gpu-0-0-thermal { + thermal-sensors = <&tsens7 1>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpu-0-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpu_0_1: gpu-0-1-thermal { + thermal-sensors = <&tsens7 2>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpu-0-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpu_0_2: gpu-0-2-thermal { + thermal-sensors = <&tsens7 3>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpu-0-2-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpu_1_0: gpu-1-0-thermal { + thermal-sensors = <&tsens7 4>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpu-1-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpu_1_1: gpu-1-1-thermal { + thermal-sensors = <&tsens7 5>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpu-1-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpu_1_2: gpu-1-2-thermal { + thermal-sensors = <&tsens7 6>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpu-1-2-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpu_2_0: gpu-2-0-thermal { + thermal-sensors = <&tsens7 7>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpu-2-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpu_2_1: gpu-2-1-thermal { + thermal-sensors = <&tsens7 8>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpu-2-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpu_2_2: gpu-2-2-thermal { + thermal-sensors = <&tsens7 9>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpu-2-2-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpu_3_0: gpu-3-0-thermal { + thermal-sensors = <&tsens7 10>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpu-3-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpu_3_1: gpu-3-1-thermal { + thermal-sensors = <&tsens7 11>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpu-3-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpu_3_2: gpu-3-2-thermal { + thermal-sensors = <&tsens7 12>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpu-3-2-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpuss_0: gpuss-0-thermal { + thermal-sensors = <&tsens7 13>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-0-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + thermal_gpuss_1: gpuss-1-thermal { + thermal-sensors = <&tsens7 14>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-1-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts b/arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts index 2390648a248f..460f27dcd6f6 100644 --- a/arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts +++ b/arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts @@ -616,6 +616,38 @@ sound-dai = <&q6apm>; }; }; + + dp0-dai-link { + link-name = "DP0 Playback"; + + codec { + sound-dai = <&mdss_dp0>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + dp1-dai-link { + link-name = "DP1 Playback"; + + codec { + sound-dai = <&mdss_dp1>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_1>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; }; usb-1-ss0-sbu-mux { @@ -1102,9 +1134,7 @@ pins = "gpio10"; function = "normal"; }; -}; -&pmc8380_3_gpios { pm_sde7_aux_3p3_en: pcie-aux-3p3-default-state { pins = "gpio8"; function = "normal"; @@ -1144,6 +1174,22 @@ status = "okay"; }; +&sdhc_2 { + cd-gpios = <&tlmm 71 GPIO_ACTIVE_LOW>; + + vmmc-supply = <&vreg_l9b_2p9>; + vqmmc-supply = <&vreg_l6b_1p8>; + + no-sdio; + no-mmc; + + pinctrl-0 = <&sdc2_default &sdc2_card_det_n>; + pinctrl-1 = <&sdc2_sleep &sdc2_card_det_n>; + pinctrl-names = "default", "sleep"; + + status = "okay"; +}; + &smb2360_0 { status = "okay"; }; @@ -1326,6 +1372,13 @@ bias-disable; }; + sdc2_card_det_n: sd-card-det-n-state { + pins = "gpio71"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + usb1_pwr_1p15_reg_en: usb1-pwr-1p15-reg-en-state { pins = "gpio188"; function = "gpio"; @@ -1461,6 +1514,24 @@ status = "okay"; }; +&ufs_mem_phy { + vdda-phy-supply = <&vreg_l3i_0p8>; + vdda-pll-supply = <&vreg_l3e_1p2>; + + status = "okay"; +}; + +&ufs_mem_hc { + reset-gpios = <&tlmm 238 GPIO_ACTIVE_LOW>; + + vcc-supply = <&vreg_l17b_2p5>; + vcc-max-microamp = <1300000>; + vccq-supply = <&vreg_l2i_1p2>; + vccq-max-microamp = <1200000>; + + status = "okay"; +}; + &usb_1_ss0_dwc3_hs { remote-endpoint = <&pmic_glink_ss0_hs_in>; }; @@ -1511,3 +1582,69 @@ &usb_mp_hsphy1 { phys = <&eusb6_repeater>; }; + +&thermal_zones { + gpuss-0-thermal { + trips { + trip-point0 { + temperature = <105000>; + }; + }; + }; + + gpuss-1-thermal { + trips { + trip-point0 { + temperature = <105000>; + }; + }; + }; + + gpuss-2-thermal { + trips { + trip-point0 { + temperature = <105000>; + }; + }; + }; + + gpuss-3-thermal { + trips { + trip-point0 { + temperature = <105000>; + }; + }; + }; + + gpuss-4-thermal { + trips { + trip-point0 { + temperature = <105000>; + }; + }; + }; + + gpuss-5-thermal { + trips { + trip-point0 { + temperature = <105000>; + }; + }; + }; + + gpuss-6-thermal { + trips { + trip-point0 { + temperature = <105000>; + }; + }; + }; + + gpuss-7-thermal { + trips { + trip-point0 { + temperature = <105000>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/hamoa-iot-som.dtsi b/arch/arm64/boot/dts/qcom/hamoa-iot-som.dtsi index b8e3e04a6fbd..9c5e77df0054 100644 --- a/arch/arm64/boot/dts/qcom/hamoa-iot-som.dtsi +++ b/arch/arm64/boot/dts/qcom/hamoa-iot-som.dtsi @@ -447,14 +447,20 @@ }; &qupv3_0 { + firmware-name = "qcom/x1e80100/qupv3fw.elf"; + status = "okay"; }; &qupv3_1 { + firmware-name = "qcom/x1e80100/qupv3fw.elf"; + status = "okay"; }; &qupv3_2 { + firmware-name = "qcom/x1e80100/qupv3fw.elf"; + status = "okay"; }; @@ -570,12 +576,10 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "otg"; usb-role-switch; + + status = "okay"; }; &usb_1_ss0_hsphy { @@ -593,12 +597,10 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "otg"; usb-role-switch; + + status = "okay"; }; &usb_1_ss1_hsphy { @@ -616,12 +618,10 @@ }; &usb_1_ss2 { - status = "okay"; -}; - -&usb_1_ss2_dwc3 { dr_mode = "otg"; usb-role-switch; + + status = "okay"; }; &usb_1_ss2_hsphy { @@ -639,11 +639,9 @@ }; &usb_2 { - status = "okay"; -}; - -&usb_2_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_2_hsphy { diff --git a/arch/arm64/boot/dts/qcom/hamoa-lenovo-ideacentre-mini-01q8x10.dts b/arch/arm64/boot/dts/qcom/hamoa-lenovo-ideacentre-mini-01q8x10.dts new file mode 100644 index 000000000000..bfb7cea56df9 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/hamoa-lenovo-ideacentre-mini-01q8x10.dts @@ -0,0 +1,1200 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; + +#include +#include + +#include "hamoa.dtsi" +#include "hamoa-pmics.dtsi" + +/ { + model = "Lenovo IdeaCentre Mini 01Q8X10"; + compatible = "lenovo,ideacentre-mini-01q8x10", "qcom,x1e80100"; + chassis-type = "desktop"; + + aliases { + serial0 = &uart14; + }; + + wcd938x: audio-codec { + compatible = "qcom,wcd9385-codec"; + + pinctrl-0 = <&wcd_default>; + pinctrl-names = "default"; + + qcom,micbias1-microvolt = <1800000>; + qcom,micbias2-microvolt = <1800000>; + qcom,micbias3-microvolt = <1800000>; + qcom,micbias4-microvolt = <1800000>; + qcom,mbhc-buttons-vthreshold-microvolt = <75000 150000 237000 500000 500000 500000 500000 500000>; + qcom,mbhc-headset-vthreshold-microvolt = <1700000>; + qcom,mbhc-headphone-vthreshold-microvolt = <50000>; + qcom,rx-device = <&wcd_rx>; + qcom,tx-device = <&wcd_tx>; + + reset-gpios = <&tlmm 191 GPIO_ACTIVE_LOW>; + mux-controls = <&us_euro_mux_ctrl>; + + vdd-buck-supply = <&vreg_l15b>; + vdd-rxtx-supply = <&vreg_l15b>; + vdd-io-supply = <&vreg_l15b>; + vdd-mic-bias-supply = <&vreg_bob1>; + + #sound-dai-cells = <1>; + }; + + dp-connector { + compatible = "dp-connector"; + type = "full-size"; + + pinctrl-0 = <&usb1_dp_hot_plug_detect>; + pinctrl-names = "default"; + + port { + dp_port_in: endpoint { + remote-endpoint = <&usb_1_ss1_qmpphy_out_dp>; + }; + }; + }; + + hdmi-bridge { + compatible = "parade,ps185hdm"; + + pinctrl-0 = <&usb2_dp_hot_plug_detect>; + pinctrl-names = "default"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + hdmi_bridge_in: endpoint { + remote-endpoint = <&usb_1_ss2_qmpphy_out_dp>; + }; + }; + + port@1 { + reg = <1>; + + hdmi_bridge_out: endpoint { + remote-endpoint = <&hdmi_con>; + }; + }; + }; + }; + + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con: endpoint { + remote-endpoint = <&hdmi_bridge_out>; + }; + }; + }; + + us_euro_mux_ctrl: mux-controller { + compatible = "gpio-mux"; + pinctrl-0 = <&us_euro_hs_sel>; + pinctrl-names = "default"; + mux-supply = <&vreg_l16b>; + #mux-control-cells = <0>; + mux-gpios = <&tlmm 68 GPIO_ACTIVE_HIGH>; + }; + + pmic-glink { + compatible = "qcom,x1e80100-pmic-glink", + "qcom,sm8550-pmic-glink", + "qcom,pmic-glink"; + orientation-gpios = <&tlmm 121 GPIO_ACTIVE_HIGH>; + + #address-cells = <1>; + #size-cells = <0>; + + connector@0 { + compatible = "usb-c-connector"; + reg = <0>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pmic_glink_port0_hs_in: endpoint { + remote-endpoint = <&usb_1_ss0_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + pmic_glink_port0_ss_in: endpoint { + remote-endpoint = <&usb0_retimer_out>; + }; + }; + + port@2 { + reg = <2>; + + pmic_glink_port0_sbu_in: endpoint { + remote-endpoint = <&usb0_retimer_sbu_out>; + }; + }; + }; + }; + }; + + vreg_nvme1_3p3: regulator-nvme1-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_NVME1_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 18 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&nvme_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_nvme2_3p3: regulator-nvme2-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_NVME2_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 18 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&nvme_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_v0p9: regulator-v0p9 { + compatible = "regulator-fixed"; + + regulator-name = "V0P9"; + regulator-min-microvolt = <906000>; + regulator-max-microvolt = <906000>; + + gpio = <&pmc8380_5_gpios 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb0_pwr_1p15_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_vdd1v8: regulator-vdd1v8 { + compatible = "regulator-fixed"; + + regulator-name = "VDD1V8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + vin-supply = <&vreg_l15b>; + + gpio = <&pm8550ve_9_gpios 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb0_1p8_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_vdd33_rtmr: regulator-vdd33-rtmr { + compatible = "regulator-fixed"; + + regulator-name = "VDD33_RTMR"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&pm8550_gpios 11 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb0_3p3_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_wcn_0p95: regulator-wcn-0p95 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_0P95"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <950000>; + + vin-supply = <&vreg_wifi_3v3>; + }; + + vreg_wifi_3v3: regulator-wcn-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "WIFI_3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 214 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&wcn_sw_en>; + pinctrl-names = "default"; + }; + + vreg_wifi_1v8: regulator-wifi-1v8 { + compatible = "regulator-fixed"; + + regulator-name = "WIFI_1V8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + vin-supply = <&vreg_l15b>; + }; + + reserved-memory { + linux,cma { + compatible = "shared-dma-pool"; + size = <0x0 0x8000000>; + reusable; + linux,cma-default; + }; + }; + + sound { + compatible = "qcom,x1e80100-sndcard"; + model = "X1E80100-LENOVO-IdeaCentre-Mini"; + audio-routing = "IN1_HPHL", "HPHL_OUT", + "IN2_HPHR", "HPHR_OUT", + "AMIC2", "MIC BIAS2", + "TX SWR_INPUT1", "ADC2_OUTPUT"; + + wcd-playback-dai-link { + link-name = "WCD Playback"; + + cpu { + sound-dai = <&q6apmbedai RX_CODEC_DMA_RX_0>; + }; + + codec { + sound-dai = <&wcd938x 0>, <&swr1 0>, <&lpass_rxmacro 0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + wcd-capture-dai-link { + link-name = "WCD Capture"; + + cpu { + sound-dai = <&q6apmbedai TX_CODEC_DMA_TX_3>; + }; + + codec { + sound-dai = <&wcd938x 1>, <&swr2 1>, <&lpass_txmacro 0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + displayport-0-dai-link { + link-name = "DisplayPort0 Playback"; + + codec { + sound-dai = <&mdss_dp0>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + displayport-1-dai-link { + link-name = "DisplayPort1 Playback"; + + codec { + sound-dai = <&mdss_dp1>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_1>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + displayport-2-dai-link { + link-name = "DisplayPort2 Playback"; + + codec { + sound-dai = <&mdss_dp2>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_2>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + }; + + wcn7850-pmu { + compatible = "qcom,wcn7850-pmu"; + + vdd-supply = <&vreg_wcn_0p95>; + vddio-supply = <&vreg_l15b>; + vddaon-supply = <&vreg_wcn_0p95>; + vdddig-supply = <&vreg_wcn_0p95>; + vddrfa1p2-supply = <&vreg_wifi_1v8>; + vddrfa1p8-supply = <&vreg_wifi_1v8>; + + wlan-enable-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>; + bt-enable-gpios = <&tlmm 116 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&wcn_wlan_bt_en>; + pinctrl-names = "default"; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p8: ldo7 { + regulator-name = "vreg_pmu_rfa_1p8"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; +}; + +&apps_rsc { + regulators-0 { + compatible = "qcom,pm8550-rpmh-regulators"; + qcom,pmic-id = "b"; + + vreg_bob1: bob1 { + regulator-name = "vreg_bob1"; + regulator-min-microvolt = <3008000>; + regulator-max-microvolt = <3960000>; + regulator-initial-mode = ; + }; + + vreg_l2b: ldo2 { + regulator-name = "VREG_L2B"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + + vreg_l4b: ldo4 { + regulator-name = "VREG_L4B"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l13b: ldo13 { + regulator-name = "VREG_L13B"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + + vreg_l15b: ldo15 { + regulator-name = "VREG_L15B"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + regulator-always-on; + }; + + vreg_l16b: ldo16 { + regulator-name = "VREG_L16B"; + regulator-min-microvolt = <2504000>; + regulator-max-microvolt = <2504000>; + regulator-initial-mode = ; + }; + + }; + + regulators-1 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + qcom,pmic-id = "c"; + + vreg_l3c: ldo3 { + regulator-name = "VREG_L3C"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + }; + }; + + regulators-2 { + compatible = "qcom,pmc8380-rpmh-regulators"; + qcom,pmic-id = "d"; + + vreg_l1d: ldo1 { + regulator-name = "VREG_L1D"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + + vreg_l2d: ldo2 { + regulator-name = "VREG_L2D"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + }; + + vreg_l3d: ldo3 { + regulator-name = "VREG_L3D"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + }; + + regulators-3 { + compatible = "qcom,pmc8380-rpmh-regulators"; + qcom,pmic-id = "e"; + + vreg_l2e: ldo2 { + regulator-name = "VREG_L2E"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + regulator-initial-mode = ; + }; + + vreg_l3e: ldo3 { + regulator-name = "VREG_L3E"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + }; + + regulators-4 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + qcom,pmic-id = "i"; + + vreg_l3i: ldo3 { + regulator-name = "VREG_L3I"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + }; + + regulators-5 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + qcom,pmic-id = "j"; + + vreg_l1j: ldo1 { + regulator-name = "VREG_L1J"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + }; + + vreg_l2j: ldo2 { + regulator-name = "VREG_L2J"; + regulator-min-microvolt = <1256000>; + regulator-max-microvolt = <1256000>; + regulator-initial-mode = ; + }; + + vreg_l3j: ldo3 { + regulator-name = "VREG_L3J"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + regulator-initial-mode = ; + }; + }; +}; + +&i2c3 { + clock-frequency = <400000>; + + status = "okay"; + + typec-mux@8 { + compatible = "parade,ps8833", "parade,ps8830"; + reg = <0x08>; + + clocks = <&rpmhcc RPMH_RF_CLK3>; + + vdd-supply = <&vreg_v0p9>; + vdd33-supply = <&vreg_vdd33_rtmr>; + vdd33-cap-supply = <&vreg_vdd33_rtmr>; + vddar-supply = <&vreg_v0p9>; + vddat-supply = <&vreg_v0p9>; + vddio-supply = <&vreg_vdd1v8>; + + reset-gpios = <&pm8550_gpios 10 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&usb0_reset_n>; + pinctrl-names = "default"; + + retimer-switch; + orientation-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb0_retimer_out: endpoint { + remote-endpoint = <&pmic_glink_port0_ss_in>; + }; + }; + + port@1 { + reg = <1>; + + usb0_retimer_in: endpoint { + remote-endpoint = <&usb_1_ss0_qmpphy_out>; + }; + }; + + port@2 { + reg = <2>; + + usb0_retimer_sbu_out: endpoint { + remote-endpoint = <&pmic_glink_port0_sbu_in>; + }; + }; + }; + }; +}; + +&i2c5 { + clock-frequency = <400000>; + + status = "okay"; + + eusb3_repeater: redriver@47 { + compatible = "nxp,ptn3222"; + reg = <0x47>; + #phy-cells = <0>; + + vdd3v3-supply = <&vreg_l13b>; + vdd1v8-supply = <&vreg_l4b>; + + reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&eusb3_reset_n>; + pinctrl-names = "default"; + }; + + eusb6_repeater: redriver@4f { + compatible = "nxp,ptn3222"; + reg = <0x4f>; + #phy-cells = <0>; + + vdd3v3-supply = <&vreg_l13b>; + vdd1v8-supply = <&vreg_l4b>; + + reset-gpios = <&tlmm 184 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&eusb6_reset_n>; + pinctrl-names = "default"; + }; +}; + +&iris { + firmware-name = "qcom/x1e80100/LENOVO/91B6/qcvss8380.mbn"; + + status = "okay"; +}; + +&gpu { + status = "okay"; +}; + +&gpu_zap_shader { + firmware-name = "qcom/x1e80100/LENOVO/91B6/qcdxkmsuc8380.mbn"; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dp0 { + status = "okay"; +}; + +&mdss_dp0_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp1 { + status = "okay"; +}; + +&mdss_dp1_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp2 { + status = "okay"; +}; + +&mdss_dp2_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&pcie3 { + pinctrl-0 = <&pcie3_default>; + pinctrl-names = "default"; + + vddpe-3v3-supply = <&vreg_nvme2_3p3>; + + status = "okay"; +}; + +&pcie3_phy { + vdda-phy-supply = <&vreg_l3c>; + vdda-pll-supply = <&vreg_l3e>; + + status = "okay"; +}; + +&pcie3_port0 { + reset-gpios = <&tlmm 143 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 145 GPIO_ACTIVE_LOW>; +}; + +&pcie4 { + pinctrl-0 = <&pcie4_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie4_phy { + vdda-phy-supply = <&vreg_l3i>; + vdda-pll-supply = <&vreg_l3e>; + + status = "okay"; +}; + +&pcie4_port0 { + reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; + + wifi@0 { + compatible = "pci17cb,1107"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + }; +}; + +&pcie5 { + pinctrl-0 = <&pcie5_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie5_phy { + vdda-phy-supply = <&vreg_l3i>; + vdda-pll-supply = <&vreg_l3e>; + + status = "okay"; +}; + +&pcie5_port0 { + reset-gpios = <&tlmm 149 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 151 GPIO_ACTIVE_LOW>; +}; + +&pcie6a { + vddpe-3v3-supply = <&vreg_nvme1_3p3>; + + pinctrl-0 = <&pcie6a_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie6a_phy { + vdda-phy-supply = <&vreg_l1d>; + vdda-pll-supply = <&vreg_l2j>; + + status = "okay"; +}; + +&pcie6a_port0 { + reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +}; + +&pmc8380_5_gpios { + usb0_pwr_1p15_reg_en: usb0-pwr-1p15-reg-en-state { + pins = "gpio8"; + function = "normal"; + power-source = <1>; + bias-disable; + input-disable; + output-enable; + }; +}; + +&pm8550_gpios { + usb0_reset_n: usb0-reset-n-state { + pins = "gpio10"; + function = "normal"; + power-source = <1>; + bias-disable; + input-disable; + output-enable; + }; + + usb0_3p3_reg_en: usb0-3p3-reg-en-state { + pins = "gpio11"; + function = "normal"; + power-source = <1>; + bias-disable; + input-disable; + output-enable; + }; +}; + +&pm8550ve_9_gpios { + usb0_1p8_reg_en: usb0-1p8-reg-en-state { + pins = "gpio8"; + function = "normal"; + power-source = <1>; + bias-disable; + input-disable; + output-enable; + }; +}; + +&qupv3_0 { + status = "okay"; +}; + +&qupv3_1 { + status = "okay"; +}; + +&qupv3_2 { + status = "okay"; +}; + +&remoteproc_adsp { + firmware-name = "qcom/x1e80100/LENOVO/91B6/qcadsp8380.mbn", + "qcom/x1e80100/LENOVO/91B6/adsp_dtbs.elf"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/x1e80100/LENOVO/91B6/qccdsp8380.mbn", + "qcom/x1e80100/LENOVO/91B6/cdsp_dtbs.elf"; + + status = "okay"; +}; + +&smb2360_0 { + status = "okay"; +}; + +&smb2360_0_eusb2_repeater { + vdd18-supply = <&vreg_l3d>; + vdd3-supply = <&vreg_l2b>; +}; + +&swr1 { + status = "okay"; + + /* WCD9385 RX */ + wcd_rx: codec@0,4 { + compatible = "sdw20217010d00"; + reg = <0 4>; + qcom,rx-port-mapping = <1 2 3 4 5>; + }; +}; + +&swr2 { + status = "okay"; + + /* WCD9385 TX */ + wcd_tx: codec@0,3 { + compatible = "sdw20217010d00"; + reg = <0 3>; + qcom,tx-port-mapping = <2 2 3 4>; + }; +}; + +&tlmm { + gpio-reserved-ranges = <44 4>, /* SPI11 (TPM) */ + <76 4>, /* SPI19 (TZ Protected) */ + <238 1>; /* UFS Reset */ + + eusb3_reset_n: eusb3-reset-n-state { + pins = "gpio6"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-low; + }; + + nvme_reg_en: nvme-reg-en-state { + pins = "gpio18"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + us_euro_hs_sel: us-euro-hs-sel-state { + pins = "gpio68"; + function = "gpio"; + bias-pull-down; + drive-strength = <2>; + }; + + wcn_wlan_bt_en: wcn-wlan-bt-en-state { + pins = "gpio116", "gpio117"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + usb1_dp_hot_plug_detect: ub1-dp-hot-plug-detect-state { + pins = "gpio124"; + function = "usb1_dp"; + bias-disable; + }; + + usb2_dp_hot_plug_detect: usb2-dp-hot-plug-detect-state { + pins = "gpio126"; + function = "usb2_dp"; + bias-disable; + }; + + pcie3_default: pcie3-default-state { + clkreq-n-pins { + pins = "gpio144"; + function = "pcie3_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio143"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + wake-n-pins { + pins = "gpio145"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie4_default: pcie4-default-state { + clkreq-n-pins { + pins = "gpio147"; + function = "pcie4_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio146"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio148"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie5_default: pcie5-default-state { + clkreq-n-pins { + pins = "gpio150"; + function = "pcie5_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio149"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio151"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie6a_default: pcie6a-default-state { + clkreq-n-pins { + pins = "gpio153"; + function = "pcie6a_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio152"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio154"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + eusb6_reset_n: eusb6-reset-n-state { + pins = "gpio184"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-low; + }; + + wcd_default: wcd-reset-n-active-state { + pins = "gpio191"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; + + wcn_sw_en: wcn-sw-en-state { + pins = "gpio214"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; +}; + +&uart14 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn7850-bt"; + max-speed = <3200000>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + }; +}; + +&usb_1_ss0 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_1_ss0_dwc3_hs { + remote-endpoint = <&pmic_glink_port0_hs_in>; +}; + +&usb_1_ss0_hsphy { + vdd-supply = <&vreg_l3j>; + vdda12-supply = <&vreg_l2j>; + + phys = <&smb2360_0_eusb2_repeater>; + + status = "okay"; +}; + +&usb_1_ss0_qmpphy { + vdda-phy-supply = <&vreg_l2j>; + vdda-pll-supply = <&vreg_l1j>; + + status = "okay"; +}; + +&usb_1_ss0_qmpphy_out { + remote-endpoint = <&usb0_retimer_in>; +}; + +&usb_1_ss1_qmpphy { + vdda-phy-supply = <&vreg_l2j>; + vdda-pll-supply = <&vreg_l2d>; + + /delete-property/ mode-switch; + /delete-property/ orientation-switch; + + status = "okay"; + + ports { + port@0 { + #address-cells = <1>; + #size-cells = <0>; + + /delete-node/ endpoint; + + usb_1_ss1_qmpphy_out_dp: endpoint@0 { + reg = <0>; + + data-lanes = <3 2 1 0>; + remote-endpoint = <&dp_port_in>; + }; + }; + }; +}; + +&usb_1_ss2_qmpphy { + vdda-phy-supply = <&vreg_l2j>; + vdda-pll-supply = <&vreg_l2d>; + + /delete-property/ mode-switch; + /delete-property/ orientation-switch; + + status = "okay"; + + ports { + port@0 { + #address-cells = <1>; + #size-cells = <0>; + + /delete-node/ endpoint; + + usb_1_ss2_qmpphy_out_dp: endpoint@0 { + reg = <0>; + + data-lanes = <3 2 1 0>; + remote-endpoint = <&hdmi_bridge_in>; + }; + }; + }; +}; + +&usb_mp { + status = "okay"; +}; + +&usb_mp_hsphy0 { + vdd-supply = <&vreg_l2e>; + vdda12-supply = <&vreg_l3e>; + + phys = <&eusb3_repeater>; + + status = "okay"; +}; + +&usb_mp_hsphy1 { + vdd-supply = <&vreg_l2e>; + vdda12-supply = <&vreg_l3e>; + + phys = <&eusb6_repeater>; + + status = "okay"; +}; + +&usb_mp_qmpphy0 { + vdda-phy-supply = <&vreg_l3e>; + vdda-pll-supply = <&vreg_l3c>; + + status = "okay"; +}; + +&usb_mp_qmpphy1 { + vdda-phy-supply = <&vreg_l3e>; + vdda-pll-supply = <&vreg_l3c>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/hamoa.dtsi b/arch/arm64/boot/dts/qcom/hamoa.dtsi index 4b0784af4bd3..051dee076416 100644 --- a/arch/arm64/boot/dts/qcom/hamoa.dtsi +++ b/arch/arm64/boot/dts/qcom/hamoa.dtsi @@ -75,6 +75,7 @@ next-level-cache = <&l2_0>; power-domains = <&cpu_pd0>, <&scmi_dvfs 0>; power-domain-names = "psci", "perf"; + #cooling-cells = <2>; l2_0: l2-cache { compatible = "cache"; @@ -91,6 +92,7 @@ next-level-cache = <&l2_0>; power-domains = <&cpu_pd1>, <&scmi_dvfs 0>; power-domain-names = "psci", "perf"; + #cooling-cells = <2>; }; cpu2: cpu@200 { @@ -101,6 +103,7 @@ next-level-cache = <&l2_0>; power-domains = <&cpu_pd2>, <&scmi_dvfs 0>; power-domain-names = "psci", "perf"; + #cooling-cells = <2>; }; cpu3: cpu@300 { @@ -111,6 +114,7 @@ next-level-cache = <&l2_0>; power-domains = <&cpu_pd3>, <&scmi_dvfs 0>; power-domain-names = "psci", "perf"; + #cooling-cells = <2>; }; cpu4: cpu@10000 { @@ -121,6 +125,7 @@ next-level-cache = <&l2_1>; power-domains = <&cpu_pd4>, <&scmi_dvfs 1>; power-domain-names = "psci", "perf"; + #cooling-cells = <2>; l2_1: l2-cache { compatible = "cache"; @@ -137,6 +142,7 @@ next-level-cache = <&l2_1>; power-domains = <&cpu_pd5>, <&scmi_dvfs 1>; power-domain-names = "psci", "perf"; + #cooling-cells = <2>; }; cpu6: cpu@10200 { @@ -147,6 +153,7 @@ next-level-cache = <&l2_1>; power-domains = <&cpu_pd6>, <&scmi_dvfs 1>; power-domain-names = "psci", "perf"; + #cooling-cells = <2>; }; cpu7: cpu@10300 { @@ -157,6 +164,7 @@ next-level-cache = <&l2_1>; power-domains = <&cpu_pd7>, <&scmi_dvfs 1>; power-domain-names = "psci", "perf"; + #cooling-cells = <2>; }; cpu8: cpu@20000 { @@ -167,6 +175,7 @@ next-level-cache = <&l2_2>; power-domains = <&cpu_pd8>, <&scmi_dvfs 2>; power-domain-names = "psci", "perf"; + #cooling-cells = <2>; l2_2: l2-cache { compatible = "cache"; @@ -183,6 +192,7 @@ next-level-cache = <&l2_2>; power-domains = <&cpu_pd9>, <&scmi_dvfs 2>; power-domain-names = "psci", "perf"; + #cooling-cells = <2>; }; cpu10: cpu@20200 { @@ -193,6 +203,7 @@ next-level-cache = <&l2_2>; power-domains = <&cpu_pd10>, <&scmi_dvfs 2>; power-domain-names = "psci", "perf"; + #cooling-cells = <2>; }; cpu11: cpu@20300 { @@ -203,6 +214,7 @@ next-level-cache = <&l2_2>; power-domains = <&cpu_pd11>, <&scmi_dvfs 2>; power-domain-names = "psci", "perf"; + #cooling-cells = <2>; }; cpu-map { @@ -835,9 +847,9 @@ <0>, <0>, <0>, - <0>, - <0>, - <0>; + <&ufs_mem_phy 0>, + <&ufs_mem_phy 1>, + <&ufs_mem_phy 2>; power-domains = <&rpmhpd RPMHPD_CX>; #clock-cells = <1>; @@ -3869,6 +3881,122 @@ status = "disabled"; }; + ufs_mem_phy: phy@1d80000 { + compatible = "qcom,x1e80100-qmp-ufs-phy", + "qcom,sm8550-qmp-ufs-phy"; + reg = <0x0 0x01d80000 0x0 0x2000>; + + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_UFS_PHY_PHY_AUX_CLK>, + <&tcsr TCSR_UFS_PHY_CLKREF_EN>; + + clock-names = "ref", + "ref_aux", + "qref"; + resets = <&ufs_mem_hc 0>; + reset-names = "ufsphy"; + + power-domains = <&gcc GCC_UFS_MEM_PHY_GDSC>; + + #clock-cells = <1>; + #phy-cells = <0>; + + status = "disabled"; + }; + + ufs_mem_hc: ufshc@1d84000 { + compatible = "qcom,x1e80100-ufshc", + "qcom,sm8550-ufshc", + "qcom,ufshc"; + reg = <0x0 0x01d84000 0x0 0x3000>; + + interrupts = ; + + clocks = <&gcc GCC_UFS_PHY_AXI_CLK>, + <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>, + <&gcc GCC_UFS_PHY_UNIPRO_CORE_CLK>, + <&rpmhcc RPMH_LN_BB_CLK3>, + <&gcc GCC_UFS_PHY_TX_SYMBOL_0_CLK>, + <&gcc GCC_UFS_PHY_RX_SYMBOL_0_CLK>, + <&gcc GCC_UFS_PHY_RX_SYMBOL_1_CLK>; + clock-names = "core_clk", + "bus_aggr_clk", + "iface_clk", + "core_clk_unipro", + "ref_clk", + "tx_lane0_sync_clk", + "rx_lane0_sync_clk", + "rx_lane1_sync_clk"; + + operating-points-v2 = <&ufs_opp_table>; + + resets = <&gcc GCC_UFS_PHY_BCR>; + reset-names = "rst"; + + interconnects = <&aggre1_noc MASTER_UFS_MEM QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_UFS_MEM_CFG QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "ufs-ddr", + "cpu-ufs"; + + power-domains = <&gcc GCC_UFS_PHY_GDSC>; + required-opps = <&rpmhpd_opp_nom>; + + iommus = <&apps_smmu 0x1a0 0>; + dma-coherent; + + lanes-per-direction = <2>; + + phys = <&ufs_mem_phy>; + phy-names = "ufsphy"; + + #reset-cells = <1>; + + status = "disabled"; + + ufs_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-75000000 { + opp-hz = /bits/ 64 <75000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <75000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-150000000 { + opp-hz = /bits/ 64 <150000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <150000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <300000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>; + required-opps = <&rpmhpd_opp_nom>; + }; + }; + }; + cryptobam: dma-controller@1dc4000 { compatible = "qcom,bam-v1.7.4", "qcom,bam-v1.7.0"; reg = <0x0 0x01dc4000 0x0 0x28000>; @@ -4714,7 +4842,7 @@ clocks = <&gcc GCC_SDCC2_AHB_CLK>, <&gcc GCC_SDCC2_APPS_CLK>, - <&rpmhcc RPMH_CXO_CLK>; + <&bi_tcxo_div2>; clock-names = "iface", "core", "xo"; iommus = <&apps_smmu 0x520 0>; qcom,dll-config = <0x0007642c>; @@ -4767,7 +4895,7 @@ clocks = <&gcc GCC_SDCC4_AHB_CLK>, <&gcc GCC_SDCC4_APPS_CLK>, - <&rpmhcc RPMH_CXO_CLK>; + <&bi_tcxo_div2>; clock-names = "iface", "core", "xo"; iommus = <&apps_smmu 0x160 0>; qcom,dll-config = <0x0007642c>; @@ -4908,9 +5036,9 @@ status = "disabled"; }; - usb_1_ss2: usb@a0f8800 { - compatible = "qcom,x1e80100-dwc3", "qcom,dwc3"; - reg = <0 0x0a0f8800 0 0x400>; + usb_1_ss2: usb@a000000 { + compatible = "qcom,x1e80100-dwc3", "qcom,snps-dwc3"; + reg = <0 0x0a000000 0 0xfc100>; clocks = <&gcc GCC_CFG_NOC_USB3_TERT_AXI_CLK>, <&gcc GCC_USB30_TERT_MASTER_CLK>, @@ -4936,11 +5064,13 @@ assigned-clock-rates = <19200000>, <200000000>; - interrupts-extended = <&intc GIC_SPI 370 IRQ_TYPE_LEVEL_HIGH>, + interrupts-extended = <&intc GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 370 IRQ_TYPE_LEVEL_HIGH>, <&pdc 58 IRQ_TYPE_EDGE_BOTH>, <&pdc 57 IRQ_TYPE_EDGE_BOTH>, <&pdc 10 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "pwr_event", + interrupt-names = "dwc_usb3", + "pwr_event", "dp_hs_phy_irq", "dm_hs_phy_irq", "ss_phy_irq"; @@ -4959,61 +5089,47 @@ wakeup-source; - #address-cells = <2>; - #size-cells = <2>; - ranges; + iommus = <&apps_smmu 0x14a0 0x0>; + + phys = <&usb_1_ss2_hsphy>, + <&usb_1_ss2_qmpphy QMP_USB43DP_USB3_PHY>; + phy-names = "usb2-phy", + "usb3-phy"; + + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + snps,usb3_lpm_capable; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + + dma-coherent; status = "disabled"; - usb_1_ss2_dwc3: usb@a000000 { - compatible = "snps,dwc3"; - reg = <0 0x0a000000 0 0xcd00>; + ports { + #address-cells = <1>; + #size-cells = <0>; - interrupts = ; + port@0 { + reg = <0>; - iommus = <&apps_smmu 0x14a0 0x0>; - - phys = <&usb_1_ss2_hsphy>, - <&usb_1_ss2_qmpphy QMP_USB43DP_USB3_PHY>; - phy-names = "usb2-phy", - "usb3-phy"; - - snps,dis_u2_susphy_quirk; - snps,dis_enblslpm_quirk; - snps,usb3_lpm_capable; - snps,dis-u1-entry-quirk; - snps,dis-u2-entry-quirk; - - dma-coherent; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - usb_1_ss2_dwc3_hs: endpoint { - }; + usb_1_ss2_dwc3_hs: endpoint { }; + }; - port@1 { - reg = <1>; + port@1 { + reg = <1>; - usb_1_ss2_dwc3_ss: endpoint { - remote-endpoint = <&usb_1_ss2_qmpphy_usb_ss_in>; - }; + usb_1_ss2_dwc3_ss: endpoint { + remote-endpoint = <&usb_1_ss2_qmpphy_usb_ss_in>; }; }; }; }; - usb_2: usb@a2f8800 { - compatible = "qcom,x1e80100-dwc3", "qcom,dwc3"; - reg = <0 0x0a2f8800 0 0x400>; - #address-cells = <2>; - #size-cells = <2>; - ranges; + usb_2: usb@a200000 { + compatible = "qcom,x1e80100-dwc3", "qcom,snps-dwc3"; + reg = <0 0x0a200000 0 0xfc100>; clocks = <&gcc GCC_CFG_NOC_USB2_PRIM_AXI_CLK>, <&gcc GCC_USB20_MASTER_CLK>, @@ -5038,10 +5154,12 @@ <&gcc GCC_USB20_MASTER_CLK>; assigned-clock-rates = <19200000>, <200000000>; - interrupts-extended = <&intc GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>, + interrupts-extended = <&intc GIC_SPI 240 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>, <&pdc 50 IRQ_TYPE_EDGE_BOTH>, <&pdc 49 IRQ_TYPE_EDGE_BOTH>; - interrupt-names = "pwr_event", + interrupt-names = "dwc_usb3", + "pwr_event", "dp_hs_phy_irq", "dm_hs_phy_irq"; @@ -5060,31 +5178,26 @@ qcom,select-utmi-as-pipe-clk; wakeup-source; + iommus = <&apps_smmu 0x14e0 0x0>; + phys = <&usb_2_hsphy>; + phy-names = "usb2-phy"; + maximum-speed = "high-speed"; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + + dma-coherent; + status = "disabled"; - usb_2_dwc3: usb@a200000 { - compatible = "snps,dwc3"; - reg = <0 0x0a200000 0 0xcd00>; - interrupts = ; - iommus = <&apps_smmu 0x14e0 0x0>; - phys = <&usb_2_hsphy>; - phy-names = "usb2-phy"; - maximum-speed = "high-speed"; - snps,dis-u1-entry-quirk; - snps,dis-u2-entry-quirk; - - dma-coherent; - - port { - usb_2_dwc3_hs: endpoint { - }; + port { + usb_2_dwc3_hs: endpoint { }; }; }; - usb_mp: usb@a4f8800 { - compatible = "qcom,x1e80100-dwc3-mp", "qcom,dwc3"; - reg = <0 0x0a4f8800 0 0x400>; + usb_mp: usb@a400000 { + compatible = "qcom,x1e80100-dwc3-mp", "qcom,snps-dwc3"; + reg = <0 0x0a400000 0 0xfc100>; clocks = <&gcc GCC_CFG_NOC_USB3_MP_AXI_CLK>, <&gcc GCC_USB30_MP_MASTER_CLK>, @@ -5110,7 +5223,8 @@ assigned-clock-rates = <19200000>, <200000000>; - interrupts-extended = <&intc GIC_SPI 313 IRQ_TYPE_LEVEL_HIGH>, + interrupts-extended = <&intc GIC_SPI 307 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 313 IRQ_TYPE_LEVEL_HIGH>, <&intc GIC_SPI 314 IRQ_TYPE_LEVEL_HIGH>, <&intc GIC_SPI 309 IRQ_TYPE_LEVEL_HIGH>, <&intc GIC_SPI 312 IRQ_TYPE_LEVEL_HIGH>, @@ -5120,7 +5234,8 @@ <&pdc 53 IRQ_TYPE_EDGE_BOTH>, <&pdc 55 IRQ_TYPE_LEVEL_HIGH>, <&pdc 56 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "pwr_event_1", "pwr_event_2", + interrupt-names = "dwc_usb3", + "pwr_event_1", "pwr_event_2", "hs_phy_1", "hs_phy_2", "dp_hs_phy_1", "dm_hs_phy_1", "dp_hs_phy_2", "dm_hs_phy_2", @@ -5140,39 +5255,28 @@ wakeup-source; - #address-cells = <2>; - #size-cells = <2>; - ranges; + iommus = <&apps_smmu 0x1400 0x0>; + + phys = <&usb_mp_hsphy0>, <&usb_mp_qmpphy0>, + <&usb_mp_hsphy1>, <&usb_mp_qmpphy1>; + phy-names = "usb2-0", "usb3-0", + "usb2-1", "usb3-1"; + dr_mode = "host"; + + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + snps,usb3_lpm_capable; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + + dma-coherent; status = "disabled"; - - usb_mp_dwc3: usb@a400000 { - compatible = "snps,dwc3"; - reg = <0 0x0a400000 0 0xcd00>; - - interrupts = ; - - iommus = <&apps_smmu 0x1400 0x0>; - - phys = <&usb_mp_hsphy0>, <&usb_mp_qmpphy0>, - <&usb_mp_hsphy1>, <&usb_mp_qmpphy1>; - phy-names = "usb2-0", "usb3-0", - "usb2-1", "usb3-1"; - dr_mode = "host"; - - snps,dis_u2_susphy_quirk; - snps,dis_enblslpm_quirk; - snps,usb3_lpm_capable; - snps,dis-u1-entry-quirk; - snps,dis-u2-entry-quirk; - - dma-coherent; - }; }; - usb_1_ss0: usb@a6f8800 { - compatible = "qcom,x1e80100-dwc3", "qcom,dwc3"; - reg = <0 0x0a6f8800 0 0x400>; + usb_1_ss0: usb@a600000 { + compatible = "qcom,x1e80100-dwc3", "qcom,snps-dwc3"; + reg = <0 0x0a600000 0 0xfc100>; clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>, @@ -5198,11 +5302,13 @@ assigned-clock-rates = <19200000>, <200000000>; - interrupts-extended = <&intc GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>, + interrupts-extended = <&intc GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>, <&pdc 61 IRQ_TYPE_EDGE_BOTH>, <&pdc 15 IRQ_TYPE_EDGE_BOTH>, <&pdc 17 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "pwr_event", + interrupt-names = "dwc_usb3", + "pwr_event", "dp_hs_phy_irq", "dm_hs_phy_irq", "ss_phy_irq"; @@ -5214,58 +5320,47 @@ wakeup-source; - #address-cells = <2>; - #size-cells = <2>; - ranges; + iommus = <&apps_smmu 0x1420 0x0>; + + phys = <&usb_1_ss0_hsphy>, + <&usb_1_ss0_qmpphy QMP_USB43DP_USB3_PHY>; + phy-names = "usb2-phy", + "usb3-phy"; + + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + snps,usb3_lpm_capable; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + + dma-coherent; status = "disabled"; - usb_1_ss0_dwc3: usb@a600000 { - compatible = "snps,dwc3"; - reg = <0 0x0a600000 0 0xcd00>; + ports { + #address-cells = <1>; + #size-cells = <0>; - interrupts = ; + port@0 { + reg = <0>; - iommus = <&apps_smmu 0x1420 0x0>; - - phys = <&usb_1_ss0_hsphy>, - <&usb_1_ss0_qmpphy QMP_USB43DP_USB3_PHY>; - phy-names = "usb2-phy", - "usb3-phy"; - - snps,dis_u2_susphy_quirk; - snps,dis_enblslpm_quirk; - snps,usb3_lpm_capable; - snps,dis-u1-entry-quirk; - snps,dis-u2-entry-quirk; - - dma-coherent; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - usb_1_ss0_dwc3_hs: endpoint { - }; + usb_1_ss0_dwc3_hs: endpoint { }; + }; - port@1 { - reg = <1>; + port@1 { + reg = <1>; - usb_1_ss0_dwc3_ss: endpoint { - remote-endpoint = <&usb_1_ss0_qmpphy_usb_ss_in>; - }; + usb_1_ss0_dwc3_ss: endpoint { + remote-endpoint = <&usb_1_ss0_qmpphy_usb_ss_in>; }; }; }; }; - usb_1_ss1: usb@a8f8800 { - compatible = "qcom,x1e80100-dwc3", "qcom,dwc3"; - reg = <0 0x0a8f8800 0 0x400>; + usb_1_ss1: usb@a800000 { + compatible = "qcom,x1e80100-dwc3", "qcom,snps-dwc3"; + reg = <0 0x0a800000 0 0xfc100>; clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>, <&gcc GCC_USB30_SEC_MASTER_CLK>, @@ -5291,11 +5386,13 @@ assigned-clock-rates = <19200000>, <200000000>; - interrupts-extended = <&intc GIC_SPI 372 IRQ_TYPE_LEVEL_HIGH>, + interrupts-extended = <&intc GIC_SPI 357 IRQ_TYPE_LEVEL_HIGH>, + <&intc GIC_SPI 372 IRQ_TYPE_LEVEL_HIGH>, <&pdc 60 IRQ_TYPE_EDGE_BOTH>, <&pdc 11 IRQ_TYPE_EDGE_BOTH>, <&pdc 47 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "pwr_event", + interrupt-names = "dwc_usb3", + "pwr_event", "dp_hs_phy_irq", "dm_hs_phy_irq", "ss_phy_irq"; @@ -5314,50 +5411,39 @@ wakeup-source; - #address-cells = <2>; - #size-cells = <2>; - ranges; + iommus = <&apps_smmu 0x1460 0x0>; + + phys = <&usb_1_ss1_hsphy>, + <&usb_1_ss1_qmpphy QMP_USB43DP_USB3_PHY>; + phy-names = "usb2-phy", + "usb3-phy"; + + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + snps,usb3_lpm_capable; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + + dma-coherent; status = "disabled"; - usb_1_ss1_dwc3: usb@a800000 { - compatible = "snps,dwc3"; - reg = <0 0x0a800000 0 0xcd00>; + ports { + #address-cells = <1>; + #size-cells = <0>; - interrupts = ; + port@0 { + reg = <0>; - iommus = <&apps_smmu 0x1460 0x0>; - - phys = <&usb_1_ss1_hsphy>, - <&usb_1_ss1_qmpphy QMP_USB43DP_USB3_PHY>; - phy-names = "usb2-phy", - "usb3-phy"; - - snps,dis_u2_susphy_quirk; - snps,dis_enblslpm_quirk; - snps,usb3_lpm_capable; - snps,dis-u1-entry-quirk; - snps,dis-u2-entry-quirk; - - dma-coherent; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - usb_1_ss1_dwc3_hs: endpoint { - }; + usb_1_ss1_dwc3_hs: endpoint { }; + }; - port@1 { - reg = <1>; + port@1 { + reg = <1>; - usb_1_ss1_dwc3_ss: endpoint { - remote-endpoint = <&usb_1_ss1_qmpphy_usb_ss_in>; - }; + usb_1_ss1_dwc3_ss: endpoint { + remote-endpoint = <&usb_1_ss1_qmpphy_usb_ss_in>; }; }; }; @@ -5432,19 +5518,19 @@ opp-366000000 { opp-hz = /bits/ 64 <366000000>; - required-opps = <&rpmhpd_opp_svs_l1>, + required-opps = <&rpmhpd_opp_svs>, <&rpmhpd_opp_svs_l1>; }; opp-444000000 { opp-hz = /bits/ 64 <444000000>; - required-opps = <&rpmhpd_opp_nom>, + required-opps = <&rpmhpd_opp_svs_l1>, <&rpmhpd_opp_nom>; }; opp-481000000 { opp-hz = /bits/ 64 <481000000>; - required-opps = <&rpmhpd_opp_turbo>, + required-opps = <&rpmhpd_opp_svs_l1>, <&rpmhpd_opp_turbo>; }; }; @@ -5658,8 +5744,8 @@ mdss_dp0_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5747,8 +5833,8 @@ mdss_dp1_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5835,8 +5921,8 @@ mdss_dp2_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5918,8 +6004,8 @@ mdss_dp3_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -9230,7 +9316,7 @@ }; }; - cpu2-0-top-thermal { + thermal_cpu2_0_top: cpu2-0-top-thermal { thermal-sensors = <&tsens2 1>; trips { @@ -9242,7 +9328,7 @@ }; }; - cpu2-0-btm-thermal { + thermal_cpu2_0_btm: cpu2-0-btm-thermal { thermal-sensors = <&tsens2 2>; trips { @@ -9254,7 +9340,7 @@ }; }; - cpu2-1-top-thermal { + thermal_cpu2_1_top: cpu2-1-top-thermal { thermal-sensors = <&tsens2 3>; trips { @@ -9266,7 +9352,7 @@ }; }; - cpu2-1-btm-thermal { + thermal_cpu2_1_btm: cpu2-1-btm-thermal { thermal-sensors = <&tsens2 4>; trips { @@ -9278,7 +9364,7 @@ }; }; - cpu2-2-top-thermal { + thermal_cpu2_2_top: cpu2-2-top-thermal { thermal-sensors = <&tsens2 5>; trips { @@ -9290,7 +9376,7 @@ }; }; - cpu2-2-btm-thermal { + thermal_cpu2_2_btm: cpu2-2-btm-thermal { thermal-sensors = <&tsens2 6>; trips { @@ -9302,7 +9388,7 @@ }; }; - cpu2-3-top-thermal { + thermal_cpu2_3_top: cpu2-3-top-thermal { thermal-sensors = <&tsens2 7>; trips { @@ -9314,7 +9400,7 @@ }; }; - cpu2-3-btm-thermal { + thermal_cpu2_3_btm: cpu2-3-btm-thermal { thermal-sensors = <&tsens2 8>; trips { @@ -9326,7 +9412,7 @@ }; }; - cpuss2-top-thermal { + thermal_cpuss2_top: cpuss2-top-thermal { thermal-sensors = <&tsens2 9>; trips { @@ -9338,7 +9424,7 @@ }; }; - cpuss2-btm-thermal { + thermal_cpuss2_btm: cpuss2-btm-thermal { thermal-sensors = <&tsens2 10>; trips { @@ -9350,7 +9436,7 @@ }; }; - aoss3-thermal { + thermal_aoss3: aoss3-thermal { thermal-sensors = <&tsens3 0>; trips { @@ -9368,7 +9454,7 @@ }; }; - nsp0-thermal { + thermal_nsp0: nsp0-thermal { thermal-sensors = <&tsens3 1>; trips { @@ -9386,7 +9472,7 @@ }; }; - nsp1-thermal { + thermal_nsp1: nsp1-thermal { thermal-sensors = <&tsens3 2>; trips { @@ -9404,7 +9490,7 @@ }; }; - nsp2-thermal { + thermal_nsp2: nsp2-thermal { thermal-sensors = <&tsens3 3>; trips { @@ -9422,7 +9508,7 @@ }; }; - nsp3-thermal { + thermal_nsp3: nsp3-thermal { thermal-sensors = <&tsens3 4>; trips { @@ -9440,7 +9526,7 @@ }; }; - gpuss-0-thermal { + thermal_gpuss_0: gpuss-0-thermal { polling-delay-passive = <200>; thermal-sensors = <&tsens3 5>; @@ -9467,7 +9553,7 @@ }; }; - gpuss-1-thermal { + thermal_gpuss_1: gpuss-1-thermal { polling-delay-passive = <200>; thermal-sensors = <&tsens3 6>; @@ -9494,7 +9580,7 @@ }; }; - gpuss-2-thermal { + thermal_gpuss_2: gpuss-2-thermal { polling-delay-passive = <200>; thermal-sensors = <&tsens3 7>; @@ -9521,7 +9607,7 @@ }; }; - gpuss-3-thermal { + thermal_gpuss_3: gpuss-3-thermal { polling-delay-passive = <200>; thermal-sensors = <&tsens3 8>; @@ -9548,7 +9634,7 @@ }; }; - gpuss-4-thermal { + thermal_gpuss_4: gpuss-4-thermal { polling-delay-passive = <200>; thermal-sensors = <&tsens3 9>; @@ -9575,7 +9661,7 @@ }; }; - gpuss-5-thermal { + thermal_gpuss_5: gpuss-5-thermal { polling-delay-passive = <200>; thermal-sensors = <&tsens3 10>; @@ -9602,7 +9688,7 @@ }; }; - gpuss-6-thermal { + thermal_gpuss_6: gpuss-6-thermal { polling-delay-passive = <200>; thermal-sensors = <&tsens3 11>; @@ -9629,7 +9715,7 @@ }; }; - gpuss-7-thermal { + thermal_gpuss_7: gpuss-7-thermal { polling-delay-passive = <200>; thermal-sensors = <&tsens3 12>; @@ -9656,7 +9742,7 @@ }; }; - camera0-thermal { + thermal_camera0: camera0-thermal { thermal-sensors = <&tsens3 13>; trips { @@ -9674,7 +9760,7 @@ }; }; - camera1-thermal { + thermal_camera1: camera1-thermal { thermal-sensors = <&tsens3 14>; trips { diff --git a/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts b/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts new file mode 100644 index 000000000000..941f866ecfe9 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/ipq5210-rdp504.dts @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; + +#include "ipq5210.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. IPQ5210 RDP504"; + compatible = "qcom,ipq5210-rdp504", "qcom,ipq5210"; + + aliases { + serial0 = &uart1; + }; + + chosen { + stdout-path = "serial0"; + }; +}; + +&sdhc { + max-frequency = <192000000>; + bus-width = <4>; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + pinctrl-0 = <&sdhc_default_state>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&sleep_clk { + clock-frequency = <32000>; +}; + +&tlmm { + qup_uart1_default_state: qup-uart1-default-state { + pins = "gpio38", "gpio39"; + function = "qup_se1"; + drive-strength = <6>; + bias-pull-down; + }; + + sdhc_default_state: sdhc-default-state { + clk-pins { + pins = "gpio5"; + function = "sdc_clk"; + drive-strength = <8>; + bias-disable; + }; + + cmd-pins { + pins = "gpio4"; + function = "sdc_cmd"; + drive-strength = <8>; + bias-pull-up; + }; + + data-pins { + pins = "gpio0", "gpio1", "gpio2", "gpio3"; + function = "sdc_data"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&uart1 { + pinctrl-0 = <&qup_uart1_default_state>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&xo_board { + clock-frequency = <24000000>; +}; diff --git a/arch/arm64/boot/dts/qcom/ipq5210.dtsi b/arch/arm64/boot/dts/qcom/ipq5210.dtsi new file mode 100644 index 000000000000..3761eb03ab24 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/ipq5210.dtsi @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +/ { + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&intc>; + + clocks { + sleep_clk: sleep-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + }; + + xo_board: xo-board-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + }; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x0>; + enable-method = "psci"; + next-level-cache = <&l2_0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x1>; + enable-method = "psci"; + next-level-cache = <&l2_0>; + }; + + cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x2>; + enable-method = "psci"; + next-level-cache = <&l2_0>; + }; + + cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x3>; + enable-method = "psci"; + next-level-cache = <&l2_0>; + }; + + l2_0: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-unified; + }; + }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + + scm { + compatible = "qcom,scm-ipq5210", "qcom,scm"; + }; + }; + + memory@80000000 { + device_type = "memory"; + /* We expect the bootloader to fill in the size */ + reg = <0x0 0x80000000 0x0 0x0>; + }; + + pmu { + compatible = "arm,cortex-a53-pmu"; + interrupts = ; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + bootloader@87800000 { + reg = <0x0 0x87800000 0x0 0x400000>; + no-map; + }; + + smem@87c00000 { + compatible = "qcom,smem"; + reg = <0x0 0x87c00000 0x0 0x40000>; + no-map; + + hwlocks = <&tcsr_mutex 3>; + }; + + tfa@87d00000 { + reg = <0x0 0x87d00000 0x0 0x80000>; + no-map; + }; + + optee@87d80000 { + reg = <0x0 0x87d80000 0x0 0x280000>; + no-map; + }; + }; + + soc@0 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + dma-ranges = <0 0 0 0 0x10 0>; + ranges = <0 0 0 0 0x10 0>; + + tlmm: pinctrl@1000000 { + compatible = "qcom,ipq5210-tlmm"; + reg = <0x0 0x01000000 0x0 0x300000>; + interrupts = ; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&tlmm 0 0 54>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gcc: clock-controller@1800000 { + compatible = "qcom,ipq5210-gcc"; + reg = <0x0 0x01800000 0x0 0x40000>; + clocks = <&xo_board>, + <&sleep_clk>, + <0>, + <0>, + <0>, + <0>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + tcsr_mutex: hwlock@1905000 { + compatible = "qcom,tcsr-mutex"; + reg = <0x0 0x01905000 0x0 0x20000>; + #hwlock-cells = <1>; + }; + + qupv3: geniqup@1ac0000 { + compatible = "qcom,geni-se-qup"; + reg = <0x0 0x01ac0000 0x0 0x2000>; + clocks = <&gcc GCC_QUPV3_AHB_MST_CLK>, + <&gcc GCC_QUPV3_AHB_SLV_CLK>; + clock-names = "m-ahb", "s-ahb"; + ranges; + #address-cells = <2>; + #size-cells = <2>; + + uart1: serial@1a84000 { + compatible = "qcom,geni-debug-uart"; + reg = <0x0 0x01a84000 0x0 0x4000>; + clocks = <&gcc GCC_QUPV3_WRAP_SE1_CLK>; + clock-names = "se"; + interrupts = ; + + status = "disabled"; + }; + }; + + sdhc: mmc@7804000 { + compatible = "qcom,ipq5210-sdhci", "qcom,sdhci-msm-v5"; + reg = <0x0 0x07804000 0x0 0x1000>, + <0x0 0x07805000 0x0 0x1000>; + reg-names = "hc", + "cqhci"; + + interrupts = , + ; + interrupt-names = "hc_irq", + "pwr_irq"; + + clocks = <&gcc GCC_SDCC1_AHB_CLK>, + <&gcc GCC_SDCC1_APPS_CLK>, + <&xo_board>; + clock-names = "iface", + "core", + "xo"; + non-removable; + + status = "disabled"; + }; + + intc: interrupt-controller@b000000 { + compatible = "qcom,msm-qgic2"; + interrupt-controller; + #interrupt-cells = <3>; + reg = <0x0 0xb000000 0x0 0x1000>, + <0x0 0xb002000 0x0 0x1000>, + <0x0 0xb001000 0x0 0x1000>, + <0x0 0xb004000 0x0 0x1000>; + interrupts = ; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0 0 0 0x0b00c000 0 0x3000>; + + v2m0: v2m@0 { + compatible = "arm,gic-v2m-frame"; + reg = <0x0 0x0 0x0 0xffd>; + msi-controller; + }; + + v2m1: v2m@1000 { + compatible = "arm,gic-v2m-frame"; + reg = <0x0 0x00001000 0x0 0xffd>; + msi-controller; + }; + + v2m2: v2m@2000 { + compatible = "arm,gic-v2m-frame"; + reg = <0x0 0x00002000 0x0 0xffd>; + msi-controller; + }; + }; + + timer@b120000 { + compatible = "arm,armv7-timer-mem"; + reg = <0x0 0x0b120000 0x0 0x1000>; + ranges = <0 0 0 0x10000000>; + #address-cells = <1>; + #size-cells = <1>; + + frame@b121000 { + frame-number = <0>; + interrupts = , + ; + reg = <0x0b121000 0x1000>, + <0x0b122000 0x1000>; + }; + + frame@b123000 { + frame-number = <1>; + interrupts = ; + reg = <0x0b123000 0x1000>; + + status = "disabled"; + }; + + frame@b124000 { + frame-number = <2>; + interrupts = ; + reg = <0x0b124000 0x1000>; + + status = "disabled"; + }; + + frame@b125000 { + frame-number = <3>; + interrupts = ; + reg = <0x0b125000 0x1000>; + + status = "disabled"; + }; + + frame@b126000 { + frame-number = <4>; + interrupts = ; + reg = <0x0b126000 0x1000>; + + status = "disabled"; + }; + + frame@b127000 { + frame-number = <5>; + interrupts = ; + reg = <0x0b127000 0x1000>; + + status = "disabled"; + }; + + frame@b128000 { + frame-number = <6>; + interrupts = ; + reg = <0x0b128000 0x1000>; + + status = "disabled"; + }; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/ipq5332-rdp-common.dtsi b/arch/arm64/boot/dts/qcom/ipq5332-rdp-common.dtsi index b37ae7749083..8967861be5fd 100644 --- a/arch/arm64/boot/dts/qcom/ipq5332-rdp-common.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq5332-rdp-common.dtsi @@ -78,4 +78,48 @@ drive-strength = <8>; bias-pull-down; }; + + qpic_snand_default_state: qpic-snand-default-state { + clock-pins { + pins = "gpio13"; + function = "qspi_clk"; + drive-strength = <8>; + bias-disable; + }; + + cs-pins { + pins = "gpio12"; + function = "qspi_cs"; + drive-strength = <8>; + bias-disable; + }; + + data-pins { + pins = "gpio8", "gpio9", "gpio10", "gpio11"; + function = "qspi_data"; + drive-strength = <8>; + bias-disable; + }; + }; +}; + +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + pinctrl-0 = <&qpic_snand_default_state>; + pinctrl-names = "default"; + + status = "okay"; + + flash@0 { + compatible = "spi-nand"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + nand-ecc-engine = <&qpic_nand>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + }; }; diff --git a/arch/arm64/boot/dts/qcom/ipq5332-rdp442.dts b/arch/arm64/boot/dts/qcom/ipq5332-rdp442.dts index ed8a54eb95c0..6e2abde9ed89 100644 --- a/arch/arm64/boot/dts/qcom/ipq5332-rdp442.dts +++ b/arch/arm64/boot/dts/qcom/ipq5332-rdp442.dts @@ -35,17 +35,6 @@ }; }; -&sdhc { - bus-width = <4>; - max-frequency = <192000000>; - mmc-ddr-1_8v; - mmc-hs200-1_8v; - non-removable; - pinctrl-0 = <&sdc_default_state>; - pinctrl-names = "default"; - status = "okay"; -}; - &tlmm { i2c_1_pins: i2c-1-state { pins = "gpio29", "gpio30"; @@ -54,29 +43,6 @@ bias-pull-up; }; - sdc_default_state: sdc-default-state { - clk-pins { - pins = "gpio13"; - function = "sdc_clk"; - drive-strength = <8>; - bias-disable; - }; - - cmd-pins { - pins = "gpio12"; - function = "sdc_cmd"; - drive-strength = <8>; - bias-pull-up; - }; - - data-pins { - pins = "gpio8", "gpio9", "gpio10", "gpio11"; - function = "sdc_data"; - drive-strength = <8>; - bias-pull-up; - }; - }; - spi_0_data_clk_pins: spi-0-data-clk-state { pins = "gpio14", "gpio15", "gpio16"; function = "blsp0_spi"; diff --git a/arch/arm64/boot/dts/qcom/ipq5332.dtsi b/arch/arm64/boot/dts/qcom/ipq5332.dtsi index 45fc512a3bab..e227730d99a6 100644 --- a/arch/arm64/boot/dts/qcom/ipq5332.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq5332.dtsi @@ -423,6 +423,39 @@ status = "disabled"; }; + qpic_bam: dma-controller@7984000 { + compatible = "qcom,bam-v1.7.4", "qcom,bam-v1.7.0"; + reg = <0x07984000 0x1c000>; + interrupts = ; + clocks = <&gcc GCC_QPIC_AHB_CLK>; + clock-names = "bam_clk"; + #dma-cells = <1>; + qcom,ee = <0>; + status = "disabled"; + }; + + qpic_nand: spi@79b0000 { + compatible = "qcom,ipq5332-snand", "qcom,ipq9574-snand"; + reg = <0x079b0000 0x10000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&gcc GCC_QPIC_CLK>, + <&gcc GCC_QPIC_AHB_CLK>, + <&gcc GCC_QPIC_IO_MACRO_CLK>; + clock-names = "core", + "aon", + "iom"; + + dmas = <&qpic_bam 0>, + <&qpic_bam 1>, + <&qpic_bam 2>; + dma-names = "tx", + "rx", + "cmd"; + + status = "disabled"; + }; + usb: usb@8af8800 { compatible = "qcom,ipq5332-dwc3", "qcom,dwc3"; reg = <0x08af8800 0x400>; diff --git a/arch/arm64/boot/dts/qcom/ipq5424-rdp466.dts b/arch/arm64/boot/dts/qcom/ipq5424-rdp466.dts index 738618551203..de71b72ae6dc 100644 --- a/arch/arm64/boot/dts/qcom/ipq5424-rdp466.dts +++ b/arch/arm64/boot/dts/qcom/ipq5424-rdp466.dts @@ -124,13 +124,6 @@ status = "okay"; }; -&sdhc { - pinctrl-0 = <&sdc_default_state>; - pinctrl-names = "default"; - - status = "okay"; -}; - &sleep_clk { clock-frequency = <32000>; }; @@ -201,26 +194,26 @@ }; }; - sdc_default_state: sdc-default-state { - clk-pins { + qpic_snand_default_state: qpic-snand-default-state { + clock-pins { pins = "gpio5"; - function = "sdc_clk"; + function = "qspi_clk"; drive-strength = <8>; - bias-disable; + bias-pull-down; }; - cmd-pins { + cs-pins { pins = "gpio4"; - function = "sdc_cmd"; + function = "qspi_cs"; drive-strength = <8>; bias-pull-up; }; data-pins { pins = "gpio0", "gpio1", "gpio2", "gpio3"; - function = "sdc_data"; + function = "qspi_data"; drive-strength = <8>; - bias-pull-up; + bias-pull-down; }; }; @@ -246,6 +239,27 @@ }; }; +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + pinctrl-0 = <&qpic_snand_default_state>; + pinctrl-names = "default"; + + status = "okay"; + + flash@0 { + compatible = "spi-nand"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + nand-ecc-engine = <&qpic_nand>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + }; +}; + &uart0 { pinctrl-0 = <&uart0_pins>; pinctrl-names = "default"; diff --git a/arch/arm64/boot/dts/qcom/ipq5424.dtsi b/arch/arm64/boot/dts/qcom/ipq5424.dtsi index eb393f3fd728..f20cda429094 100644 --- a/arch/arm64/boot/dts/qcom/ipq5424.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq5424.dtsi @@ -572,6 +572,39 @@ status = "disabled"; }; + qpic_bam: dma-controller@7984000 { + compatible = "qcom,bam-v1.7.4", "qcom,bam-v1.7.0"; + reg = <0x0 0x07984000 0x0 0x1c000>; + interrupts = ; + clocks = <&gcc GCC_QPIC_AHB_CLK>; + clock-names = "bam_clk"; + #dma-cells = <1>; + qcom,ee = <0>; + status = "disabled"; + }; + + qpic_nand: spi@79b0000 { + compatible = "qcom,ipq5424-snand", "qcom,ipq9574-snand"; + reg = <0x0 0x079b0000 0x0 0x10000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&gcc GCC_QPIC_CLK>, + <&gcc GCC_QPIC_AHB_CLK>, + <&gcc GCC_QPIC_IO_MACRO_CLK>; + clock-names = "core", + "aon", + "iom"; + + dmas = <&qpic_bam 0>, + <&qpic_bam 1>, + <&qpic_bam 2>; + dma-names = "tx", + "rx", + "cmd"; + + status = "disabled"; + }; + intc: interrupt-controller@f200000 { compatible = "arm,gic-v3"; reg = <0 0xf200000 0 0x10000>, /* GICD */ diff --git a/arch/arm64/boot/dts/qcom/ipq9574-rdp-common.dtsi b/arch/arm64/boot/dts/qcom/ipq9574-rdp-common.dtsi index bdb396afb992..62877b46f9b3 100644 --- a/arch/arm64/boot/dts/qcom/ipq9574-rdp-common.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp-common.dtsi @@ -22,6 +22,15 @@ stdout-path = "serial0:115200n8"; }; + regulator_fixed_1p8: s1800 { + compatible = "regulator-fixed"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + regulator-name = "fixed_1p8"; + }; + regulator_fixed_3p3: s3300 { compatible = "regulator-fixed"; regulator-min-microvolt = <3300000>; @@ -88,11 +97,27 @@ status = "okay"; }; +&cpu0 { + cpu-supply = <&mp5496_s1>; +}; + +&cpu1 { + cpu-supply = <&mp5496_s1>; +}; + +&cpu2 { + cpu-supply = <&mp5496_s1>; +}; + +&cpu3 { + cpu-supply = <&mp5496_s1>; +}; + &rpm_requests { regulators { compatible = "qcom,rpm-mp5496-regulators"; - ipq9574_s1: s1 { + mp5496_s1: s1 { /* * During kernel bootup, the SoC runs at 800MHz with 875mV set by the bootloaders. * During regulator registration, kernel not knowing the initial voltage, @@ -121,6 +146,11 @@ }; }; +&sdhc_1 { + vmmc-supply = <®ulator_fixed_3p3>; + vqmmc-supply = <®ulator_fixed_1p8>; +}; + &sleep_clk { clock-frequency = <32000>; }; @@ -169,6 +199,38 @@ bias-disable; }; }; + + sdc_default_state: sdc-default-state { + clk-pins { + pins = "gpio5"; + function = "sdc_clk"; + drive-strength = <8>; + bias-disable; + }; + + cmd-pins { + pins = "gpio4"; + function = "sdc_cmd"; + drive-strength = <8>; + bias-pull-up; + }; + + data-pins { + pins = "gpio0", "gpio1", "gpio2", + "gpio3", "gpio6", "gpio7", + "gpio8", "gpio9"; + function = "sdc_data"; + drive-strength = <8>; + bias-pull-up; + }; + + rclk-pins { + pins = "gpio10"; + function = "sdc_rclk"; + drive-strength = <8>; + bias-pull-down; + }; + }; }; &qpic_bam { @@ -179,8 +241,6 @@ pinctrl-0 = <&qpic_snand_default_state>; pinctrl-names = "default"; - status = "okay"; - flash@0 { compatible = "spi-nand"; reg = <0>; diff --git a/arch/arm64/boot/dts/qcom/ipq9574-rdp418-emmc.dts b/arch/arm64/boot/dts/qcom/ipq9574-rdp418-emmc.dts new file mode 100644 index 000000000000..2cf35c717411 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp418-emmc.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * IPQ9574 RDP418 (eMMC variant) board device tree source + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; + +#include "ipq9574-rdp-common.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. IPQ9574/AP-AL02-C2 (eMMC)"; + compatible = "qcom,ipq9574-ap-al02-c2-emmc", "qcom,ipq9574"; + +}; + +&sdhc_1 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/ipq9574-rdp418.dts b/arch/arm64/boot/dts/qcom/ipq9574-rdp418.dts index f4f9199d4ab1..23d4cba7c6b6 100644 --- a/arch/arm64/boot/dts/qcom/ipq9574-rdp418.dts +++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp418.dts @@ -16,48 +16,6 @@ }; -&sdhc_1 { - pinctrl-0 = <&sdc_default_state>; - pinctrl-names = "default"; - mmc-ddr-1_8v; - mmc-hs200-1_8v; - mmc-hs400-1_8v; - mmc-hs400-enhanced-strobe; - max-frequency = <384000000>; - bus-width = <8>; +&qpic_nand { status = "okay"; }; - -&tlmm { - sdc_default_state: sdc-default-state { - clk-pins { - pins = "gpio5"; - function = "sdc_clk"; - drive-strength = <8>; - bias-disable; - }; - - cmd-pins { - pins = "gpio4"; - function = "sdc_cmd"; - drive-strength = <8>; - bias-pull-up; - }; - - data-pins { - pins = "gpio0", "gpio1", "gpio2", - "gpio3", "gpio6", "gpio7", - "gpio8", "gpio9"; - function = "sdc_data"; - drive-strength = <8>; - bias-pull-up; - }; - - rclk-pins { - pins = "gpio10"; - function = "sdc_rclk"; - drive-strength = <8>; - bias-pull-down; - }; - }; -}; diff --git a/arch/arm64/boot/dts/qcom/ipq9574-rdp433-common.dtsi b/arch/arm64/boot/dts/qcom/ipq9574-rdp433-common.dtsi new file mode 100644 index 000000000000..3422058ac480 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp433-common.dtsi @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * IPQ9574 RDP433 board device tree source + * + * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +&pcie1_phy { + status = "okay"; +}; + +&pcie1 { + pinctrl-0 = <&pcie1_default>; + pinctrl-names = "default"; + + perst-gpios = <&tlmm 26 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 27 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&pcie2_phy { + status = "okay"; +}; + +&pcie2 { + pinctrl-0 = <&pcie2_default>; + pinctrl-names = "default"; + + perst-gpios = <&tlmm 29 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 30 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&pcie3_phy { + status = "okay"; +}; + +&pcie3 { + pinctrl-0 = <&pcie3_default>; + pinctrl-names = "default"; + + perst-gpios = <&tlmm 32 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 33 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&tlmm { + + pcie1_default: pcie1-default-state { + clkreq-n-pins { + pins = "gpio25"; + function = "pcie1_clk"; + drive-strength = <6>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio26"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + output-low; + }; + + wake-n-pins { + pins = "gpio27"; + function = "pcie1_wake"; + drive-strength = <6>; + bias-pull-up; + }; + }; + + pcie2_default: pcie2-default-state { + clkreq-n-pins { + pins = "gpio28"; + function = "pcie2_clk"; + drive-strength = <6>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio29"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + output-low; + }; + + wake-n-pins { + pins = "gpio30"; + function = "pcie2_wake"; + drive-strength = <6>; + bias-pull-up; + }; + }; + + pcie3_default: pcie3-default-state { + clkreq-n-pins { + pins = "gpio31"; + function = "pcie3_clk"; + drive-strength = <6>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio32"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + output-low; + }; + + wake-n-pins { + pins = "gpio33"; + function = "pcie3_wake"; + drive-strength = <6>; + bias-pull-up; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/ipq9574-rdp433-emmc.dts b/arch/arm64/boot/dts/qcom/ipq9574-rdp433-emmc.dts new file mode 100644 index 000000000000..44884231499f --- /dev/null +++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp433-emmc.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * IPQ9574 RDP433 (eMMC variant) board device tree source + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; + +#include "ipq9574-rdp-common.dtsi" +#include "ipq9574-rdp433-common.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. IPQ9574/AP-AL02-C7 (eMMC)"; + compatible = "qcom,ipq9574-ap-al02-c7-emmc", "qcom,ipq9574"; +}; + +&sdhc_1 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts b/arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts index 5a546a14998b..88439697c074 100644 --- a/arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts +++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts @@ -8,124 +8,14 @@ /dts-v1/; -#include #include "ipq9574-rdp-common.dtsi" +#include "ipq9574-rdp433-common.dtsi" / { model = "Qualcomm Technologies, Inc. IPQ9574/AP-AL02-C7"; compatible = "qcom,ipq9574-ap-al02-c7", "qcom,ipq9574"; }; -&pcie1_phy { +&qpic_nand { status = "okay"; }; - -&pcie1 { - pinctrl-0 = <&pcie1_default>; - pinctrl-names = "default"; - - perst-gpios = <&tlmm 26 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 27 GPIO_ACTIVE_LOW>; - status = "okay"; -}; - -&pcie2_phy { - status = "okay"; -}; - -&pcie2 { - pinctrl-0 = <&pcie2_default>; - pinctrl-names = "default"; - - perst-gpios = <&tlmm 29 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 30 GPIO_ACTIVE_LOW>; - status = "okay"; -}; - -&pcie3_phy { - status = "okay"; -}; - -&pcie3 { - pinctrl-0 = <&pcie3_default>; - pinctrl-names = "default"; - - perst-gpios = <&tlmm 32 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 33 GPIO_ACTIVE_LOW>; - status = "okay"; -}; - -&tlmm { - - pcie1_default: pcie1-default-state { - clkreq-n-pins { - pins = "gpio25"; - function = "pcie1_clk"; - drive-strength = <6>; - bias-pull-up; - }; - - perst-n-pins { - pins = "gpio26"; - function = "gpio"; - drive-strength = <8>; - bias-pull-down; - output-low; - }; - - wake-n-pins { - pins = "gpio27"; - function = "pcie1_wake"; - drive-strength = <6>; - bias-pull-up; - }; - }; - - pcie2_default: pcie2-default-state { - clkreq-n-pins { - pins = "gpio28"; - function = "pcie2_clk"; - drive-strength = <6>; - bias-pull-up; - }; - - perst-n-pins { - pins = "gpio29"; - function = "gpio"; - drive-strength = <8>; - bias-pull-down; - output-low; - }; - - wake-n-pins { - pins = "gpio30"; - function = "pcie2_wake"; - drive-strength = <6>; - bias-pull-up; - }; - }; - - pcie3_default: pcie3-default-state { - clkreq-n-pins { - pins = "gpio31"; - function = "pcie3_clk"; - drive-strength = <6>; - bias-pull-up; - }; - - perst-n-pins { - pins = "gpio32"; - function = "gpio"; - drive-strength = <8>; - bias-pull-up; - output-low; - }; - - wake-n-pins { - pins = "gpio33"; - function = "pcie3_wake"; - drive-strength = <6>; - bias-pull-up; - }; - }; -}; diff --git a/arch/arm64/boot/dts/qcom/ipq9574-rdp449.dts b/arch/arm64/boot/dts/qcom/ipq9574-rdp449.dts index d36d1078763e..cbc9047cfe92 100644 --- a/arch/arm64/boot/dts/qcom/ipq9574-rdp449.dts +++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp449.dts @@ -15,3 +15,7 @@ compatible = "qcom,ipq9574-ap-al02-c6", "qcom,ipq9574"; }; + +&qpic_nand { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/ipq9574-rdp453.dts b/arch/arm64/boot/dts/qcom/ipq9574-rdp453.dts index c30c9fbedf26..d233ec530cc3 100644 --- a/arch/arm64/boot/dts/qcom/ipq9574-rdp453.dts +++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp453.dts @@ -15,3 +15,7 @@ compatible = "qcom,ipq9574-ap-al02-c8", "qcom,ipq9574"; }; + +&qpic_nand { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/ipq9574-rdp454.dts b/arch/arm64/boot/dts/qcom/ipq9574-rdp454.dts index 0dc382f5d5ec..f2334b9e0ed4 100644 --- a/arch/arm64/boot/dts/qcom/ipq9574-rdp454.dts +++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp454.dts @@ -14,3 +14,7 @@ model = "Qualcomm Technologies, Inc. IPQ9574/AP-AL02-C9"; compatible = "qcom,ipq9574-ap-al02-c9", "qcom,ipq9574"; }; + +&qpic_nand { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/ipq9574.dtsi b/arch/arm64/boot/dts/qcom/ipq9574.dtsi index d7278f2137ac..622cfa96ed2b 100644 --- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi @@ -56,7 +56,6 @@ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; clock-names = "cpu"; operating-points-v2 = <&cpu_opp_table>; - cpu-supply = <&ipq9574_s1>; #cooling-cells = <2>; }; @@ -69,7 +68,6 @@ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; clock-names = "cpu"; operating-points-v2 = <&cpu_opp_table>; - cpu-supply = <&ipq9574_s1>; #cooling-cells = <2>; }; @@ -82,7 +80,6 @@ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; clock-names = "cpu"; operating-points-v2 = <&cpu_opp_table>; - cpu-supply = <&ipq9574_s1>; #cooling-cells = <2>; }; @@ -95,7 +92,6 @@ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; clock-names = "cpu"; operating-points-v2 = <&cpu_opp_table>; - cpu-supply = <&ipq9574_s1>; #cooling-cells = <2>; }; @@ -467,6 +463,15 @@ clock-names = "iface", "core", "xo", "ice"; non-removable; supports-cqe; + pinctrl-0 = <&sdc_default_state>; + pinctrl-names = "default"; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + max-frequency = <384000000>; + bus-width = <8>; + status = "disabled"; }; diff --git a/arch/arm64/boot/dts/qcom/kaanapali-mtp.dts b/arch/arm64/boot/dts/qcom/kaanapali-mtp.dts index 32a082598434..07247dc98b70 100644 --- a/arch/arm64/boot/dts/qcom/kaanapali-mtp.dts +++ b/arch/arm64/boot/dts/qcom/kaanapali-mtp.dts @@ -5,9 +5,21 @@ /dts-v1/; +#include +#include +#include #include #include "kaanapali.dtsi" +#include "pm8010-kaanapali.dtsi" /* SPMI1: SID-12/13 */ +#include "pmd8028-kaanapali.dtsi" /* SPMI1: SID-4 */ +#include "pmh0101.dtsi" /* SPMI0: SID-1 */ +#include "pmh0104-kaanapali.dtsi" /* SPMI1: SID-9 */ +#include "pmh0110-kaanapali.dtsi" /* SPMI0: SID-3/5/6/8 */ +#include "pmih0108-kaanapali.dtsi" /* SPMI1: SID-7 */ +#include "pmk8850.dtsi" /* SPMI0: SID-0 */ +#include "pmr735d-kaanapali.dtsi" /* SPMI1: SID-10 */ + / { model = "Qualcomm Technologies, Inc. Kaanapali MTP"; compatible = "qcom,kaanapali-mtp", "qcom,kaanapali"; @@ -15,6 +27,7 @@ aliases { serial0 = &uart7; + serial1 = &uart18; }; chosen { @@ -52,6 +65,193 @@ clock-div = <2>; }; }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&key_vol_up_default>; + pinctrl-names = "default"; + + key-volume-up { + label = "Volume Up"; + linux,code = ; + gpios = <&tlmm 101 GPIO_ACTIVE_LOW>; + debounce-interval = <15>; + linux,can-disable; + wakeup-source; + }; + }; + + sound { + compatible = "qcom,kaanapali-sndcard", "qcom,sm8450-sndcard"; + model = "Kaanapali-MTP"; + + audio-routing = "SpkrLeft IN", "WSA_SPK1 OUT", + "SpkrRight IN", "WSA_SPK2 OUT", + "IN1_HPHL", "HPHL_OUT", + "IN2_HPHR", "HPHR_OUT", + "AMIC2", "MIC BIAS2", + "VA DMIC0", "MIC BIAS1", + "VA DMIC1", "MIC BIAS1", + "VA DMIC2", "MIC BIAS3", + "VA DMIC3", "MIC BIAS3", + "TX SWR_INPUT1", "ADC2_OUTPUT"; + + va-dai-link { + link-name = "VA Capture"; + + codec { + sound-dai = <&lpass_vamacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai VA_CODEC_DMA_TX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + wcd-capture-dai-link { + link-name = "WCD Capture"; + + codec { + sound-dai = <&wcd939x 1>, <&swr2 0>, <&lpass_txmacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai TX_CODEC_DMA_TX_3>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + wcd-playback-dai-link { + link-name = "WCD Playback"; + + codec { + sound-dai = <&wcd939x 0>, <&swr1 0>, <&lpass_rxmacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai RX_CODEC_DMA_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + wsa-dai-link { + link-name = "WSA Playback"; + + codec { + sound-dai = <&north_spkr>, <&south_spkr>, <&swr0 0>, + <&lpass_wsamacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai WSA_CODEC_DMA_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + }; + + wcd939x: audio-codec { + compatible = "qcom,wcd9395-codec", "qcom,wcd9390-codec"; + + pinctrl-0 = <&wcd_default>; + pinctrl-names = "default"; + + qcom,micbias1-microvolt = <1800000>; + qcom,micbias2-microvolt = <1800000>; + qcom,micbias3-microvolt = <1800000>; + qcom,micbias4-microvolt = <1800000>; + qcom,mbhc-buttons-vthreshold-microvolt = <75000 150000 237000 500000 + 500000 500000 500000 500000>; + qcom,mbhc-headset-vthreshold-microvolt = <1700000>; + qcom,mbhc-headphone-vthreshold-microvolt = <50000>; + qcom,rx-device = <&wcd_rx>; + qcom,tx-device = <&wcd_tx>; + + reset-gpios = <&tlmm 161 GPIO_ACTIVE_LOW>; + + vdd-buck-supply = <&vreg_l15b_1p8>; + vdd-rxtx-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l15b_1p8>; + vdd-mic-bias-supply = <&vreg_bob1>; + vdd-px-supply = <&vreg_l1g_1p2>; + + #sound-dai-cells = <1>; + }; + + wcn7850-pmu { + compatible = "qcom,wcn7850-pmu"; + + pinctrl-0 = <&bt_default>, <&sw_ctrl_default>, <&wlan_en>; + pinctrl-names = "default"; + + bt-enable-gpios = <&pmh0104_j_e1_gpios 5 GPIO_ACTIVE_HIGH>; + wlan-enable-gpios = <&tlmm 16 GPIO_ACTIVE_HIGH>; + + vdd-supply = <&vreg_s2j_0p8>; + vddio-supply = <&vreg_l2g_1p8>; + vddio1p2-supply = <&vreg_l3g_1p2>; + vddaon-supply = <&vreg_s7g_0p9>; + vdddig-supply = <&vreg_s1j_0p8>; + vddrfa1p2-supply = <&vreg_s7f_1p2>; + vddrfa1p8-supply = <&vreg_s8f_1p8>; + + clocks = <&rpmhcc RPMH_RF_CLK1>; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p8: ldo7 { + regulator-name = "vreg_pmu_rfa_1p8"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; }; &apps_rsc { @@ -175,7 +375,7 @@ vreg_l12b_1p8: ldo12 { regulator-name = "vreg_l12b_1p8"; - regulator-min-microvolt = <1200000>; + regulator-min-microvolt = <1650000>; regulator-max-microvolt = <1800000>; regulator-initial-mode = ; regulator-allow-set-load; @@ -665,6 +865,59 @@ }; }; +&lpass_vamacro { + pinctrl-0 = <&dmic01_default>, <&dmic23_default>; + pinctrl-names = "default"; + + vdd-micb-supply = <&vreg_l10b_1p8>; + qcom,dmic-sample-rate = <4800000>; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dsi0 { + vdda-supply = <&vreg_l1d_1p2>; + status = "okay"; + + panel@0 { + compatible = "novatek,nt37801"; + reg = <0>; + + pinctrl-0 = <&sde_dsi_active &sde_te_active &sde_esync0_suspend + &sde_mdp_vsync_p_1p2_active &sde_mdp_vsync_p_1p8_active + &sde_disp0_rst_1p2_active &sde_disp0_rst_1p8_active>; + pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend &sde_esync0_suspend + &sde_mdp_vsync_p_1p2_active &sde_mdp_vsync_p_1p8_active + &sde_disp0_rst_1p2_active &sde_disp0_rst_1p8_active>; + pinctrl-names = "default", "sleep"; + + vci-supply = <&vreg_l13b_3p0>; + vdd-supply = <&vreg_l11b_1p0>; + vddio-supply = <&vreg_l12b_1p8>; + + reset-gpios = <&tlmm 98 GPIO_ACTIVE_LOW>; + + port { + panel0_in: endpoint { + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + }; +}; + +&mdss_dsi0_out { + remote-endpoint = <&panel0_in>; + data-lanes = <0 1 2 3>; +}; + +&mdss_dsi0_phy { + vdds-supply = <&vreg_l3d_0p8>; + + status = "okay"; +}; + &pcie0 { pinctrl-0 = <&pcie0_default_state>; pinctrl-names = "default"; @@ -682,6 +935,140 @@ &pcie_port0 { wake-gpios = <&tlmm 104 GPIO_ACTIVE_HIGH>; reset-gpios = <&tlmm 102 GPIO_ACTIVE_LOW>; + + wifi@0 { + compatible = "pci17cb,1107"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + }; +}; + +&pmh0101_flash { + status = "okay"; + + led-0 { + function = LED_FUNCTION_FLASH; + function-enumerator = <0>; + color = ; + led-sources = <1>, <4>; + led-max-microamp = <500000>; + flash-max-microamp = <2000000>; + flash-max-timeout-us = <1280000>; + }; + + led-1 { + function = LED_FUNCTION_FLASH; + function-enumerator = <1>; + color = ; + led-sources = <2>, <3>; + led-max-microamp = <500000>; + flash-max-microamp = <2000000>; + flash-max-timeout-us = <1280000>; + }; +}; + +&pmh0101_pwm { + status = "okay"; + + multi-led { + color = ; + function = LED_FUNCTION_STATUS; + + #address-cells = <1>; + #size-cells = <0>; + + led@1 { + reg = <1>; + color = ; + }; + + led@2 { + reg = <2>; + color = ; + }; + + led@3 { + reg = <3>; + color = ; + }; + }; +}; + +&pmh0104_j_e1_gpios { + bt_default: bt-default-state { + pins = "gpio5"; + function = "normal"; + input-disable; + output-enable; + output-low; + bias-disable; + power-source = <1>; + }; +}; + +&pmh0110_d_e0_gpios { + sde_mdp_vsync_p_1p2_active: sde-mdp-vsync-p-1p2-active-state { + pins = "gpio9"; + function = "paired"; + input-disable; + output-enable; + power-source = <2>; /* 1.2v */ + }; + + sde_mdp_vsync_p_1p8_active: sde-mdp-vsync-p-1p8-active-state { + pins = "gpio10"; + function = "paired"; + input-enable; + output-disable; + power-source = <1>; /* 1.8v */ + }; +}; + +&pmh0110_f_e0_gpios { + sde_disp0_rst_1p2_active: sde-disp0-rst-1p2-active-state { + pins = "gpio9"; + function = "paired"; + input-enable; + output-disable; + power-source = <2>; /* 1.2v */ + }; + + sde_disp0_rst_1p8_active: sde-disp0-rst-1p8-active-state { + pins = "gpio10"; + function = "paired"; + input-disable; + output-enable; + power-source = <1>; /* 1.8v */ + }; +}; + +&pon_resin { + linux,code = ; + + status = "okay"; +}; + +&remoteproc_adsp { + firmware-name = "qcom/kaanapali/adsp.mbn", + "qcom/kaanapali/adsp_dtb.mbn"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/kaanapali/cdsp.mbn", + "qcom/kaanapali/cdsp_dtb.mbn"; + + status = "okay"; }; &sdhc_2 { @@ -701,12 +1088,169 @@ status = "okay"; }; +&swr0 { + status = "okay"; + + /* WSA8845, Speaker North */ + north_spkr: speaker@0,0 { + compatible = "sdw20217020400"; + reg = <0 0>; + pinctrl-0 = <&spkr_0_sd_n_active>; + pinctrl-names = "default"; + powerdown-gpios = <&tlmm 76 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "SpkrLeft"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l2i_1p2>; + + /* + * WSA8845 Port 1 (DAC) <=> SWR0 Port 1 (SPKR_L) + * WSA8845 Port 2 (COMP) <=> SWR0 Port 2 (SPKR_L_COMP) + * WSA8845 Port 3 (BOOST) <=> SWR0 Port 3 (SPKR_L_BOOST) + * WSA8845 Port 4 (PBR) <=> SWR0 Port 7 (PBR) + * WSA8845 Port 5 (VISENSE) <=> SWR0 Port 10 (SPKR_L_VI) + * WSA8845 Port 6 (CPS) <=> SWR0 Port 13 (CPS) + */ + qcom,port-mapping = <1 2 3 7 10 13>; + }; + + /* WSA8845, Speaker South */ + south_spkr: speaker@0,1 { + compatible = "sdw20217020400"; + reg = <0 1>; + pinctrl-0 = <&spkr_1_sd_n_active>; + pinctrl-names = "default"; + powerdown-gpios = <&tlmm 77 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "SpkrRight"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l2i_1p2>; + + /* + * WSA8845 Port 1 (DAC) <=> SWR0 Port 4 (SPKR_R) + * WSA8845 Port 2 (COMP) <=> SWR0 Port 5 (SPKR_R_COMP) + * WSA8845 Port 3 (BOOST) <=> SWR0 Port 6 (SPKR_R_BOOST) + * WSA8845 Port 4 (PBR) <=> SWR0 Port 7 (PBR) + * WSA8845 Port 5 (VISENSE) <=> SWR0 Port 11 (SPKR_R_VI) + * WSA8845 Port 6 (CPS) <=> SWR0 Port 13 (CPS) + */ + qcom,port-mapping = <4 5 6 7 11 13>; + }; +}; + +&swr1 { + status = "okay"; + + /* WCD9395 RX */ + wcd_rx: codec@0,4 { + compatible = "sdw20217010e00"; + reg = <0 4>; + + /* + * WCD9395 RX Port 1 (HPH_L/R) <=> SWR1 Port 1 (HPH_L/R) + * WCD9395 RX Port 2 (CLSH) <=> SWR1 Port 2 (CLSH) + * WCD9395 RX Port 3 (COMP_L/R) <=> SWR1 Port 3 (COMP_L/R) + * WCD9395 RX Port 4 (LO) <=> SWR1 Port 4 (LO) + * WCD9395 RX Port 5 (DSD_L/R) <=> SWR1 Port 5 (DSD_L/R) + * WCD9395 RX Port 6 (HIFI_PCM_L/R) <=> SWR1 Port 9 (HIFI_PCM_L/R) + */ + qcom,rx-port-mapping = <1 2 3 4 5 9>; + }; +}; + +&swr2 { + status = "okay"; + + /* WCD9395 TX */ + wcd_tx: codec@0,3 { + compatible = "sdw20217010e00"; + reg = <0 3>; + + /* + * WCD9395 TX Port 1 (ADC1,2,3,4) <=> SWR2 Port 2 (TX SWR_INPUT 0,1,2,3) + * WCD9395 TX Port 2 (ADC3,4 & DMIC0,1) <=> SWR2 Port 2 (TX SWR_INPUT 0,1,2,3) + * WCD9395 TX Port 3 (DMIC0,1,2,3 & MBHC) <=> SWR2 Port 3 (TX SWR_INPUT 4,5,6,7) + * WCD9395 TX Port 4 (DMIC4,5,6,7) <=> SWR2 Port 4 (TX SWR_INPUT 8,9,10,11) + */ + qcom,tx-port-mapping = <2 2 3 4>; + }; +}; + &tlmm { gpio-reserved-ranges = <36 4>, /* NFC eSE SPI */ <74 1>, /* eSE */ <119 2>, /* SoCCP */ <144 4>; /* CXM UART */ + wlan_en: wlan-en-state { + pins = "gpio16"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; + + sw_ctrl_default: sw-ctrl-default-state { + pins = "gpio18"; + function = "gpio"; + bias-pull-down; + }; + + spkr_0_sd_n_active: spkr-0-sd-n-active-state { + pins = "gpio76"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + spkr_1_sd_n_active: spkr-1-sd-n-active-state { + pins = "gpio77"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + sde_te_active: sde-te-active-state { + pins = "gpio86"; + function = "mdp_vsync"; + drive-strength = <2>; + bias-pull-down; + }; + + sde_te_suspend: sde-te-suspend-state { + pins = "gpio86"; + function = "mdp_vsync"; + drive-strength = <2>; + bias-pull-down; + }; + + sde_esync0_suspend: sde-esync0-suspend-state { + pins = "gpio88"; + function = "mdp_esync0_out"; + drive-strength = <2>; + bias-pull-down; + }; + + sde_dsi_active: sde-dsi-active-state { + pins = "gpio98"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + + sde_dsi_suspend: sde-dsi-suspend-state { + pins = "gpio98"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + key_vol_up_default: key-vol-up-default-state { + pins = "gpio101"; + function = "gpio"; + output-disable; + bias-pull-up; + }; + pcie0_default_state: pcie0-default-state { perst-n-pins { pins = "gpio102"; @@ -729,12 +1273,37 @@ bias-pull-up; }; }; + + wcd_default: wcd-reset-n-active-state { + pins = "gpio161"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; }; &uart7 { status = "okay"; }; +&uart18 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn7850-bt"; + + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + + max-speed = <3200000>; + }; +}; + &ufs_mem_hc { reset-gpios = <&tlmm 217 GPIO_ACTIVE_LOW>; diff --git a/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts b/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts index 66b423a497b3..da0e8f9091c3 100644 --- a/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts +++ b/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts @@ -5,9 +5,21 @@ /dts-v1/; +#include +#include +#include #include #include "kaanapali.dtsi" +#include "pm8010-kaanapali.dtsi" /* SPMI1: SID-12/13 */ +#include "pmd8028-kaanapali.dtsi" /* SPMI1: SID-4 */ +#include "pmh0101.dtsi" /* SPMI0: SID-1 */ +#include "pmh0104-kaanapali.dtsi" /* SPMI1: SID-9 */ +#include "pmh0110-kaanapali.dtsi" /* SPMI0: SID-3/5/6/8 */ +#include "pmih0108-kaanapali.dtsi" /* SPMI1: SID-7 */ +#include "pmk8850.dtsi" /* SPMI0: SID-0 */ +#include "pmr735d-kaanapali.dtsi" /* SPMI1: SID-10 */ + / { model = "Qualcomm Technologies, Inc. Kaanapali QRD"; compatible = "qcom,kaanapali-qrd", "qcom,kaanapali"; @@ -52,6 +64,22 @@ clock-div = <2>; }; }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&key_vol_up_default>; + pinctrl-names = "default"; + + key-volume-up { + label = "Volume Up"; + linux,code = ; + gpios = <&tlmm 101 GPIO_ACTIVE_LOW>; + debounce-interval = <15>; + linux,can-disable; + wakeup-source; + }; + }; }; &apps_rsc { @@ -665,6 +693,63 @@ }; }; +&pmh0101_flash { + status = "okay"; + + led-0 { + function = LED_FUNCTION_FLASH; + function-enumerator = <0>; + color = ; + led-sources = <1>, <4>; + led-max-microamp = <500000>; + flash-max-microamp = <2000000>; + flash-max-timeout-us = <1280000>; + }; + + led-1 { + function = LED_FUNCTION_FLASH; + function-enumerator = <1>; + color = ; + led-sources = <2>, <3>; + led-max-microamp = <500000>; + flash-max-microamp = <2000000>; + flash-max-timeout-us = <1280000>; + }; +}; + +&pmh0101_pwm { + status = "okay"; + + multi-led { + color = ; + function = LED_FUNCTION_STATUS; + + #address-cells = <1>; + #size-cells = <0>; + + led@1 { + reg = <1>; + color = ; + }; + + led@2 { + reg = <2>; + color = ; + }; + + led@3 { + reg = <3>; + color = ; + }; + }; +}; + +&pon_resin { + linux,code = ; + + status = "okay"; +}; + &sdhc_2 { cd-gpios = <&tlmm 55 GPIO_ACTIVE_LOW>; @@ -682,11 +767,32 @@ status = "okay"; }; +&remoteproc_adsp { + firmware-name = "qcom/kaanapali/adsp.mbn", + "qcom/kaanapali/adsp_dtb.mbn"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/kaanapali/cdsp.mbn", + "qcom/kaanapali/cdsp_dtb.mbn"; + + status = "okay"; +}; + &tlmm { gpio-reserved-ranges = <36 4>, /* NFC eSE SPI */ <74 1>, /* eSE */ <119 2>, /* SoCCP */ <144 4>; /* CXM UART */ + + key_vol_up_default: key-vol-up-default-state { + pins = "gpio101"; + function = "gpio"; + output-disable; + bias-pull-up; + }; }; &uart7 { diff --git a/arch/arm64/boot/dts/qcom/kaanapali.dtsi b/arch/arm64/boot/dts/qcom/kaanapali.dtsi index 9ef57ad0ca71..7cc326aa1a1a 100644 --- a/arch/arm64/boot/dts/qcom/kaanapali.dtsi +++ b/arch/arm64/boot/dts/qcom/kaanapali.dtsi @@ -3,9 +3,17 @@ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ +#include +#include +#include +#include #include +#include +#include +#include #include #include +#include #include #include #include @@ -15,7 +23,9 @@ #include #include #include +#include #include +#include #include "kaanapali-ipcc.h" @@ -442,6 +452,58 @@ }; }; + smp2p-adsp { + compatible = "qcom,smp2p"; + + interrupts-extended = <&ipcc IPCC_MPROC_LPASS + IPCC_MPROC_SIGNAL_SMP2P + IRQ_TYPE_EDGE_RISING>; + + mboxes = <&ipcc IPCC_MPROC_LPASS + IPCC_MPROC_SIGNAL_SMP2P>; + + qcom,smem = <443>, <429>; + qcom,local-pid = <0>; + qcom,remote-pid = <2>; + + smp2p_adsp_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + smp2p_adsp_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + smp2p-cdsp { + compatible = "qcom,smp2p"; + + interrupts-extended = <&ipcc IPCC_MPROC_CDSP + IPCC_MPROC_SIGNAL_SMP2P + IRQ_TYPE_EDGE_RISING>; + + mboxes = <&ipcc IPCC_MPROC_CDSP + IPCC_MPROC_SIGNAL_SMP2P>; + + qcom,smem = <94>, <432>; + qcom,local-pid = <0>; + qcom,remote-pid = <5>; + + smp2p_cdsp_out: master-kernel { + qcom,entry-name = "master-kernel"; + #qcom,smem-state-cells = <1>; + }; + + smp2p_cdsp_in: slave-kernel { + qcom,entry-name = "slave-kernel"; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + soc: soc@0 { compatible = "simple-bus"; @@ -468,6 +530,508 @@ #power-domain-cells = <1>; }; + gpi_dma2: dma-controller@800000 { + compatible = "qcom,kaanapali-gpi-dma", "qcom,sm6350-gpi-dma"; + reg = <0x0 0x00800000 0x0 0x60000>; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + ; + + dma-channels = <12>; + dma-channel-mask = <0x1f>; + #dma-cells = <3>; + + iommus = <&apps_smmu 0x436 0x0>; + dma-coherent; + }; + + qupv3_2: geniqup@8c0000 { + compatible = "qcom,geni-se-qup"; + reg = <0x0 0x008c0000 0x0 0x2000>; + + clocks = <&gcc GCC_QUPV3_WRAP_2_M_AHB_CLK>, + <&gcc GCC_QUPV3_WRAP_2_S_AHB_CLK>; + clock-names = "m-ahb", + "s-ahb"; + + iommus = <&apps_smmu 0x423 0x0>; + + dma-coherent; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + i2c8: i2c@880000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00880000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP2_S0_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma2 0 0 QCOM_GPI_I2C>, + <&gpi_dma2 1 0 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c8_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi8: spi@880000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00880000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP2_S0_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma2 0 0 QCOM_GPI_SPI>, + <&gpi_dma2 1 0 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi8_data_clk>, <&qup_spi8_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c9: i2c@884000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00884000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP2_S1_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma2 0 1 QCOM_GPI_I2C>, + <&gpi_dma2 1 1 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c9_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi9: spi@884000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00884000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP2_S1_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma2 0 1 QCOM_GPI_SPI>, + <&gpi_dma2 1 1 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi9_data_clk>, <&qup_spi9_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c10: i2c@888000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00888000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP2_S2_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma2 0 2 QCOM_GPI_I2C>, + <&gpi_dma2 1 2 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c10_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi10: spi@888000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00888000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP2_S2_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma2 0 2 QCOM_GPI_SPI>, + <&gpi_dma2 1 2 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi10_data_clk>, <&qup_spi10_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c11: i2c@88c000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x0088c000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP2_S3_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma2 0 3 QCOM_GPI_I2C>, + <&gpi_dma2 1 3 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c11_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi11: spi@88c000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x0088c000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP2_S3_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma2 0 3 QCOM_GPI_SPI>, + <&gpi_dma2 1 3 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi11_data_clk>, <&qup_spi11_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c12: i2c@890000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00890000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP2_S4_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_2 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma2 0 4 QCOM_GPI_I2C>, + <&gpi_dma2 1 4 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c12_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + }; + + i2c_master_hub: geniqup@9c0000 { + compatible = "qcom,geni-se-i2c-master-hub"; + reg = <0x0 0x009c0000 0x0 0x2000>; + + clocks = <&gcc GCC_QUPV3_I2C_S_AHB_CLK>; + clock-names = "s-ahb"; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + status = "disabled"; + + i2c_hub_0: i2c@980000 { + compatible = "qcom,geni-i2c-master-hub"; + reg = <0x0 0x00980000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_I2C_S0_CLK>, + <&gcc GCC_QUPV3_I2C_CORE_CLK>; + clock-names = "se", + "core"; + + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_I2C QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config"; + + pinctrl-0 = <&hub_i2c0_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c_hub_1: i2c@984000 { + compatible = "qcom,geni-i2c-master-hub"; + reg = <0x0 0x00984000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_I2C_S1_CLK>, + <&gcc GCC_QUPV3_I2C_CORE_CLK>; + clock-names = "se", + "core"; + + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_I2C QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config"; + + pinctrl-0 = <&hub_i2c1_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c_hub_2: i2c@988000 { + compatible = "qcom,geni-i2c-master-hub"; + reg = <0x0 0x00988000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_I2C_S2_CLK>, + <&gcc GCC_QUPV3_I2C_CORE_CLK>; + clock-names = "se", + "core"; + + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_I2C QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config"; + + pinctrl-0 = <&hub_i2c2_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c_hub_3: i2c@98c000 { + compatible = "qcom,geni-i2c-master-hub"; + reg = <0x0 0x0098c000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_I2C_S3_CLK>, + <&gcc GCC_QUPV3_I2C_CORE_CLK>; + clock-names = "se", + "core"; + + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_I2C QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config"; + + pinctrl-0 = <&hub_i2c3_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c_hub_4: i2c@990000 { + compatible = "qcom,geni-i2c-master-hub"; + reg = <0x0 0x00990000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_I2C_S4_CLK>, + <&gcc GCC_QUPV3_I2C_CORE_CLK>; + clock-names = "se", + "core"; + + interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_I2C QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config"; + + pinctrl-0 = <&hub_i2c4_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + }; + + gpi_dma1: dma-controller@a00000 { + compatible = "qcom,kaanapali-gpi-dma", "qcom,sm6350-gpi-dma"; + reg = <0x0 0x00a00000 0x0 0x60000>; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + ; + + dma-channels = <12>; + dma-channel-mask = <0x1f>; + #dma-cells = <3>; + + iommus = <&apps_smmu 0xb6 0x0>; + dma-coherent; + }; + qupv3_1: geniqup@ac0000 { compatible = "qcom,geni-se-qup"; reg = <0x0 0x00ac0000 0x0 0x2000>; @@ -485,6 +1049,447 @@ #size-cells = <2>; ranges; + i2c0: i2c@a80000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a80000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S0_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma1 0 0 QCOM_GPI_I2C>, + <&gpi_dma1 1 0 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c0_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi0: spi@a80000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a80000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S0_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma1 0 0 QCOM_GPI_SPI>, + <&gpi_dma1 1 0 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi0_data_clk>, <&qup_spi0_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c1: i2c@a84000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a84000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S1_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma1 0 1 QCOM_GPI_I2C>, + <&gpi_dma1 1 1 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c1_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi1: spi@a84000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a84000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S1_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma1 0 1 QCOM_GPI_SPI>, + <&gpi_dma1 1 1 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi1_data_clk>, <&qup_spi1_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c2: i2c@a88000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a88000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S2_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma1 0 2 QCOM_GPI_I2C>, + <&gpi_dma1 1 2 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c2_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi2: spi@a88000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a88000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S2_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma1 0 2 QCOM_GPI_SPI>, + <&gpi_dma1 1 2 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi2_data_clk>, <&qup_spi2_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c3: i2c@a8c000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a8c000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S3_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma1 0 3 QCOM_GPI_I2C>, + <&gpi_dma1 1 3 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c3_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi3: spi@a8c000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a8c000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S3_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma1 0 3 QCOM_GPI_SPI>, + <&gpi_dma1 1 3 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi3_data_clk>, <&qup_spi3_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c4: i2c@a90000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a90000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S4_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma1 0 4 QCOM_GPI_I2C>, + <&gpi_dma1 1 4 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c4_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi4: spi@a90000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a90000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S4_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma1 0 4 QCOM_GPI_SPI>, + <&gpi_dma1 1 4 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi4_data_clk>, <&qup_spi4_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c5: i2c@a94000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a94000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S5_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma1 0 5 QCOM_GPI_I2C>, + <&gpi_dma1 1 5 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c5_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi5: spi@a94000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a94000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S5_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma1 0 5 QCOM_GPI_SPI>, + <&gpi_dma1 1 5 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi5_data_clk>, <&qup_spi5_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c6: i2c@a98000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x00a98000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S6_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma1 0 6 QCOM_GPI_I2C>, + <&gpi_dma1 1 6 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c6_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi6: spi@a98000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x00a98000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP1_S6_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_1 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma1 0 6 QCOM_GPI_SPI>, + <&gpi_dma1 1 6 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi6_data_clk>, <&qup_spi6_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + uart7: serial@a9c000 { compatible = "qcom,geni-debug-uart"; reg = <0x0 0x00a9c000 0x0 0x4000>; @@ -559,6 +1564,24 @@ <&rpmhcc RPMH_IPA_CLK>; }; + cambistmclkcc: clock-controller@1760000 { + compatible = "qcom,kaanapali-cambistmclkcc"; + reg = <0x0 0x01760000 0x0 0x8000>; + + clocks = <&gcc GCC_CAM_BIST_MCLK_AHB_CLK>, + <&bi_tcxo_div2>, + <&bi_tcxo_ao_div2>, + <&sleep_clk>; + + power-domains = <&rpmhpd RPMHPD_MMCX>, + <&rpmhpd RPMHPD_MX>; + required-opps = <&rpmhpd_opp_low_svs>, + <&rpmhpd_opp_low_svs>; + + #clock-cells = <1>; + #reset-cells = <1>; + }; + mmss_noc: interconnect@1780000 { compatible = "qcom,kaanapali-mmss-noc"; reg = <0x0 0x01780000 0x0 0x5b800>; @@ -566,6 +1589,653 @@ #interconnect-cells = <2>; }; + gpi_dma3: dma-controller@1900000 { + compatible = "qcom,kaanapali-gpi-dma", "qcom,sm6350-gpi-dma"; + reg = <0x0 0x01900000 0x0 0x60000>; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + ; + + dma-channels = <12>; + dma-channel-mask = <0x1e>; + #dma-cells = <3>; + + iommus = <&apps_smmu 0x4d6 0x0>; + dma-coherent; + }; + + qupv3_3: geniqup@19c0000 { + compatible = "qcom,geni-se-qup"; + reg = <0x0 0x019c0000 0x0 0x2000>; + + clocks = <&gcc GCC_QUPV3_WRAP_3_M_AHB_CLK>, + <&gcc GCC_QUPV3_WRAP_3_S_AHB_CLK>; + clock-names = "m-ahb", + "s-ahb"; + + iommus = <&apps_smmu 0x4c3 0x0>; + + dma-coherent; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + i2c13: i2c@1980000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x01980000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP3_S0_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_3 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_3 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma3 0 0 QCOM_GPI_I2C>, + <&gpi_dma3 1 0 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c13_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c14: i2c@1984000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x01984000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP3_S1_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_3 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_3 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma3 0 1 QCOM_GPI_I2C>, + <&gpi_dma3 1 1 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c14_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi14: spi@1984000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x01984000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP3_S1_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_3 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma3 0 1 QCOM_GPI_SPI>, + <&gpi_dma3 1 1 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi14_data_clk>, <&qup_spi14_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c15: i2c@1988000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x01988000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP3_S2_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_3 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_3 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma3 0 2 QCOM_GPI_I2C>, + <&gpi_dma3 1 2 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c15_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi15: spi@1988000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x01988000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP3_S2_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_3 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma3 0 2 QCOM_GPI_SPI>, + <&gpi_dma3 1 2 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi15_data_clk>, <&qup_spi15_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c16: i2c@198c000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x0198c000 0x0 0x4000>; + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP3_S3_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_3 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_3 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma3 0 3 QCOM_GPI_I2C>, + <&gpi_dma3 1 3 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c16_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi16: spi@198c000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x198c000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP3_S3_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma3 0 3 QCOM_GPI_SPI>, + <&gpi_dma3 1 3 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi16_data_clk>, <&qup_spi16_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c17: i2c@1990000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x01990000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP3_S4_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_3 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_3 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma3 0 4 QCOM_GPI_I2C>, + <&gpi_dma3 1 4 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c17_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi17: spi@1990000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x01990000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP3_S4_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_3 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma3 0 4 QCOM_GPI_SPI>, + <&gpi_dma3 1 4 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi17_data_clk>, <&qup_spi17_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + uart18: serial@1994000 { + compatible = "qcom,geni-uart"; + reg = <0x0 0x01994000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP3_S5_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_3 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_3 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + pinctrl-0 = <&qup_uart18_default>, <&qup_uart18_cts_rts>; + pinctrl-names = "default"; + + status = "disabled"; + }; + }; + + gpi_dma4: dma-controller@1a00000 { + compatible = "qcom,kaanapali-gpi-dma", "qcom,sm6350-gpi-dma"; + reg = <0x0 0x01a00000 0x0 0x60000>; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + ; + + dma-channels = <12>; + dma-channel-mask = <0x1e>; + #dma-cells = <3>; + + iommus = <&apps_smmu 0x536 0x0>; + dma-coherent; + }; + + qupv3_4: geniqup@1ac0000 { + compatible = "qcom,geni-se-qup"; + reg = <0x0 0x01ac0000 0x0 0x2000>; + + clocks = <&gcc GCC_QUPV3_WRAP_4_M_AHB_CLK>, + <&gcc GCC_QUPV3_WRAP_4_S_AHB_CLK>; + clock-names = "m-ahb", + "s-ahb"; + + iommus = <&apps_smmu 0x523 0x0>; + + dma-coherent; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + i2c19: i2c@1a80000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x01a80000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP4_S0_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_4 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_4 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma4 0 0 QCOM_GPI_I2C>, + <&gpi_dma4 1 0 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c19_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi19: spi@1a80000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x01a80000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP4_S0_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_4 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma4 0 0 QCOM_GPI_SPI>, + <&gpi_dma4 1 0 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi19_data_clk>, <&qup_spi19_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c20: i2c@1a84000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x01a84000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP4_S1_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_4 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_4 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma4 0 1 QCOM_GPI_I2C>, + <&gpi_dma4 1 1 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c20_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi20: spi@1a84000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x01a84000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP4_S1_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_4 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma4 0 1 QCOM_GPI_SPI>, + <&gpi_dma4 1 1 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi20_data_clk>, <&qup_spi20_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c21: i2c@1a88000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x01a88000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP4_S2_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_4 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_4 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma4 0 2 QCOM_GPI_I2C>, + <&gpi_dma4 1 2 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c21_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi21: spi@1a88000 { + compatible = "qcom,geni-spi"; + reg = <0x0 0x01a88000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP4_S2_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_4 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", + "qup-config"; + + dmas = <&gpi_dma4 0 2 QCOM_GPI_SPI>, + <&gpi_dma4 1 2 QCOM_GPI_SPI>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_spi21_data_clk>, <&qup_spi21_cs>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c22: i2c@1a8c000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x01a8c000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP4_S3_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_4 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_4 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma4 0 3 QCOM_GPI_I2C>, + <&gpi_dma4 1 3 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c22_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + i2c23: i2c@1a90000 { + compatible = "qcom,geni-i2c"; + reg = <0x0 0x01a90000 0x0 0x4000>; + + interrupts = ; + + clocks = <&gcc GCC_QUPV3_WRAP4_S4_CLK>; + clock-names = "se"; + + interconnects = <&clk_virt MASTER_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_4 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_4 QCOM_ICC_TAG_ACTIVE_ONLY>, + <&aggre_noc MASTER_QUP_4 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "qup-core", + "qup-config", + "qup-memory"; + + dmas = <&gpi_dma4 0 4 QCOM_GPI_I2C>, + <&gpi_dma4 1 4 QCOM_GPI_I2C>; + dma-names = "tx", + "rx"; + + pinctrl-0 = <&qup_i2c23_data_clk>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + }; + pcie0: pcie@1c00000 { device_type = "pci"; compatible = "qcom,kaanapali-pcie", "qcom,pcie-sm8550"; @@ -887,6 +2557,354 @@ #reset-cells = <1>; }; + videocc: clock-controller@20f0000 { + compatible = "qcom,kaanapali-videocc"; + reg = <0x0 0x020f0000 0x0 0x10000>; + clocks = <&bi_tcxo_div2>, + <&gcc GCC_VIDEO_AHB_CLK>; + + power-domains = <&rpmhpd RPMHPD_MMCX>, + <&rpmhpd RPMHPD_MXC>; + required-opps = <&rpmhpd_opp_low_svs>, + <&rpmhpd_opp_low_svs>; + + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + + gxclkctl: clock-controller@3d64000 { + compatible = "qcom,kaanapali-gxclkctl"; + reg = <0x0 0x03d64000 0x0 0x6000>; + + power-domains = <&rpmhpd RPMHPD_GFX>, + <&rpmhpd RPMHPD_GMXC>, + <&gpucc GPU_CC_CX_GDSC>; + + #power-domain-cells = <1>; + }; + + gpucc: clock-controller@3d90000 { + compatible = "qcom,kaanapali-gpucc"; + reg = <0x0 0x03d90000 0x0 0x9800>; + + clocks = <&bi_tcxo_div2>, + <&gcc GCC_GPU_GPLL0_CLK_SRC>, + <&gcc GCC_GPU_GPLL0_DIV_CLK_SRC>; + + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + + remoteproc_adsp: remoteproc@6800000 { + compatible = "qcom,kaanapali-adsp-pas", "qcom,sm8550-adsp-pas"; + reg = <0x0 0x06800000 0x0 0x10000>; + + interrupts-extended = <&pdc 6 IRQ_TYPE_EDGE_RISING>, + <&smp2p_adsp_in 0 IRQ_TYPE_EDGE_RISING>, + <&smp2p_adsp_in 1 IRQ_TYPE_EDGE_RISING>, + <&smp2p_adsp_in 2 IRQ_TYPE_EDGE_RISING>, + <&smp2p_adsp_in 3 IRQ_TYPE_EDGE_RISING>, + <&smp2p_adsp_in 7 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog", + "fatal", + "ready", + "handover", + "stop-ack", + "shutdown-ack"; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-names = "xo"; + + interconnects = <&lpass_lpicx_noc MASTER_LPASS_PROC QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + + power-domains = <&rpmhpd RPMHPD_LCX>, + <&rpmhpd RPMHPD_LMX>; + power-domain-names = "lcx", + "lmx"; + + memory-region = <&adspslpi_mem>, <&q6_adsp_dtb_mem>; + + qcom,qmp = <&aoss_qmp>; + + qcom,smem-states = <&smp2p_adsp_out 0>; + qcom,smem-state-names = "stop"; + + status = "disabled"; + + remoteproc_adsp_glink: glink-edge { + interrupts-extended = <&ipcc IPCC_MPROC_LPASS + IPCC_MPROC_SIGNAL_GLINK_QMP + IRQ_TYPE_EDGE_RISING>; + + mboxes = <&ipcc IPCC_MPROC_LPASS + IPCC_MPROC_SIGNAL_GLINK_QMP>; + + qcom,remote-pid = <2>; + + label = "lpass"; + + fastrpc { + compatible = "qcom,kaanapali-fastrpc"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + label = "adsp"; + #address-cells = <1>; + #size-cells = <0>; + + compute-cb@3 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <3>; + + iommus = <&apps_smmu 0x1003 0x80>, + <&apps_smmu 0x1043 0x20>; + dma-coherent; + }; + + compute-cb@4 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <4>; + + iommus = <&apps_smmu 0x1004 0x80>, + <&apps_smmu 0x1044 0x20>; + dma-coherent; + }; + + compute-cb@5 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <5>; + + iommus = <&apps_smmu 0x1005 0x80>, + <&apps_smmu 0x1045 0x20>; + dma-coherent; + }; + + compute-cb@6 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <6>; + + iommus = <&apps_smmu 0x1006 0x80>, + <&apps_smmu 0x1046 0x20>; + dma-coherent; + }; + + compute-cb@7 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <7>; + + iommus = <&apps_smmu 0x1007 0x40>, + <&apps_smmu 0x1067 0x0>, + <&apps_smmu 0x1087 0x0>; + dma-coherent; + }; + }; + + gpr { + compatible = "qcom,gpr"; + qcom,glink-channels = "adsp_apps"; + qcom,domain = ; + qcom,intents = <512 20>; + #address-cells = <1>; + #size-cells = <0>; + + q6apm: service@1 { + compatible = "qcom,q6apm"; + reg = ; + #sound-dai-cells = <0>; + qcom,protection-domain = "avs/audio", + "msm/adsp/audio_pd"; + + q6apmbedai: bedais { + compatible = "qcom,q6apm-lpass-dais"; + #sound-dai-cells = <1>; + }; + + q6apmdai: dais { + compatible = "qcom,q6apm-dais"; + iommus = <&apps_smmu 0x1001 0x80>, + <&apps_smmu 0x1041 0x20>; + }; + }; + + q6prm: service@2 { + compatible = "qcom,q6prm"; + reg = ; + qcom,protection-domain = "avs/audio", + "msm/adsp/audio_pd"; + + q6prmcc: clock-controller { + compatible = "qcom,q6prm-lpass-clocks"; + #clock-cells = <2>; + }; + }; + }; + }; + }; + + lpass_wsa2macro: codec@6aa0000 { + compatible = "qcom,kaanapali-lpass-wsa-macro", + "qcom,sm8550-lpass-wsa-macro"; + reg = <0x0 0x06aa0000 0x0 0x1000>; + clocks = <&q6prmcc LPASS_CLK_ID_WSA2_CORE_TX_MCLK + LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&lpass_vamacro>; + clock-names = "mclk", + "macro", + "dcodec", + "fsgen"; + + #clock-cells = <0>; + clock-output-names = "wsa2-mclk"; + #sound-dai-cells = <1>; + }; + + swr3: soundwire@6ab0000 { + compatible = "qcom,soundwire-v2.2.0", "qcom,soundwire-v2.0.0"; + reg = <0 0x06ab0000 0 0x10000>; + interrupts = ; + clocks = <&lpass_wsa2macro>; + clock-names = "iface"; + label = "WSA2"; + + pinctrl-0 = <&wsa2_swr_active>; + pinctrl-names = "default"; + + qcom,din-ports = <4>; + qcom,dout-ports = <9>; + + qcom,ports-sinterval = /bits/ 16 <0x07 0x1f 0x3f 0x07 0x1f 0x3f 0x18f 0xff 0xff 0x0f 0x0f 0xff 0x31f>; + qcom,ports-offset1 = /bits/ 8 <0x01 0x03 0x05 0x02 0x04 0x15 0x00 0xff 0xff 0x06 0x0d 0xff 0x00>; + qcom,ports-offset2 = /bits/ 8 <0xff 0x07 0x1f 0xff 0x07 0x1f 0xff 0xff 0xff 0xff 0xff 0xff 0xff>; + qcom,ports-hstart = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0x08 0xff 0xff 0xff 0xff 0xff 0x0f>; + qcom,ports-hstop = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0x08 0xff 0xff 0xff 0xff 0xff 0x0f>; + qcom,ports-word-length = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0x08 0xff 0xff 0xff 0xff 0xff 0x18>; + qcom,ports-block-pack-mode = /bits/ 8 <0x00 0x01 0x01 0x00 0x01 0x01 0x00 0x00 0x00 0x01 0x01 0x00 0x00>; + qcom,ports-block-group-count = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff>; + qcom,ports-lane-control = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff>; + + #address-cells = <2>; + #size-cells = <0>; + #sound-dai-cells = <1>; + status = "disabled"; + }; + + lpass_rxmacro: codec@6ac0000 { + compatible = "qcom,kaanapali-lpass-rx-macro", "qcom,sm8550-lpass-rx-macro"; + reg = <0x0 0x06ac0000 0x0 0x1000>; + clocks = <&q6prmcc LPASS_CLK_ID_RX_CORE_TX_MCLK + LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&lpass_vamacro>; + clock-names = "mclk", + "macro", + "dcodec", + "fsgen"; + + #clock-cells = <0>; + clock-output-names = "mclk"; + #sound-dai-cells = <1>; + }; + + swr1: soundwire@6ad0000 { + compatible = "qcom,soundwire-v2.2.0", "qcom,soundwire-v2.0.0"; + reg = <0 0x06ad0000 0 0x10000>; + interrupts = ; + clocks = <&lpass_rxmacro>; + clock-names = "iface"; + label = "RX"; + + pinctrl-0 = <&rx_swr_active>; + pinctrl-names = "default"; + + qcom,din-ports = <1>; + qcom,dout-ports = <11>; + + qcom,ports-sinterval = /bits/ 16 <0x03 0x3f 0x1f 0x07 0x00 0x18f 0xff 0xff 0xff 0xff 0xff 0xff>; + qcom,ports-offset1 = /bits/ 8 <0x00 0x00 0x0b 0x01 0x00 0x00 0xff 0xff 0xff 0xff 0xff 0xff>; + qcom,ports-offset2 = /bits/ 8 <0x00 0x00 0x0b 0x00 0x00 0x00 0xff 0xff 0xff 0xff 0xff 0xff>; + qcom,ports-hstart = /bits/ 8 <0xff 0x03 0xff 0xff 0xff 0x08 0xff 0xff 0xff 0xff 0xff 0xff>; + qcom,ports-hstop = /bits/ 8 <0xff 0x06 0xff 0xff 0xff 0x08 0xff 0xff 0xff 0xff 0xff 0xff>; + qcom,ports-word-length = /bits/ 8 <0x01 0x07 0x04 0xff 0xff 0x0f 0xff 0xff 0xff 0xff 0xff 0xff>; + qcom,ports-block-pack-mode = /bits/ 8 <0xff 0x00 0x01 0xff 0xff 0x00 0xff 0xff 0xff 0xff 0xff 0xff>; + qcom,ports-block-group-count = /bits/ 8 <0xff 0xff 0xff 0xff 0x00 0x00 0xff 0xff 0xff 0xff 0xff 0xff>; + qcom,ports-lane-control = /bits/ 8 <0x01 0x00 0x00 0x00 0x00 0x00 0xff 0xff 0xff 0xff 0xff 0xff>; + + #address-cells = <2>; + #size-cells = <0>; + #sound-dai-cells = <1>; + status = "disabled"; + }; + + lpass_txmacro: codec@6ae0000 { + compatible = "qcom,kaanapali-lpass-tx-macro", "qcom,sm8550-lpass-tx-macro"; + reg = <0x0 0x06ae0000 0x0 0x1000>; + clocks = <&q6prmcc LPASS_CLK_ID_TX_CORE_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&lpass_vamacro>; + clock-names = "mclk", + "macro", + "dcodec", + "fsgen"; + + #clock-cells = <0>; + clock-output-names = "mclk"; + #sound-dai-cells = <1>; + }; + + lpass_wsamacro: codec@6b00000 { + compatible = "qcom,kaanapali-lpass-wsa-macro", + "qcom,sm8550-lpass-wsa-macro"; + reg = <0x0 0x06b00000 0x0 0x1000>; + clocks = <&q6prmcc LPASS_CLK_ID_WSA_CORE_TX_MCLK + LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&lpass_vamacro>; + clock-names = "mclk", + "macro", + "dcodec", + "fsgen"; + + #clock-cells = <0>; + clock-output-names = "mclk"; + #sound-dai-cells = <1>; + }; + + swr0: soundwire@6b10000 { + compatible = "qcom,soundwire-v2.2.0", "qcom,soundwire-v2.0.0"; + reg = <0 0x06b10000 0 0x10000>; + interrupts = ; + clocks = <&lpass_wsamacro>; + clock-names = "iface"; + label = "WSA"; + + pinctrl-0 = <&wsa_swr_active>; + pinctrl-names = "default"; + + qcom,din-ports = <4>; + qcom,dout-ports = <9>; + + qcom,ports-sinterval = /bits/ 16 <0x07 0x1f 0x3f 0x07 0x1f 0x3f 0x18f 0xff 0xff 0x0f 0x0f 0xff 0x31f>; + qcom,ports-offset1 = /bits/ 8 <0x01 0x03 0x05 0x02 0x04 0x15 0x00 0xff 0xff 0x06 0x0d 0xff 0x00>; + qcom,ports-offset2 = /bits/ 8 <0xff 0x07 0x1f 0xff 0x07 0x1f 0xff 0xff 0xff 0xff 0xff 0xff 0xff>; + qcom,ports-hstart = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0x08 0xff 0xff 0xff 0xff 0xff 0x0f>; + qcom,ports-hstop = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0x08 0xff 0xff 0xff 0xff 0xff 0x0f>; + qcom,ports-word-length = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0x08 0xff 0xff 0xff 0xff 0xff 0x18>; + qcom,ports-block-pack-mode = /bits/ 8 <0x00 0x01 0x01 0x00 0x01 0x01 0x00 0x00 0x00 0x01 0x01 0x00 0x00>; + qcom,ports-block-group-count = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff>; + qcom,ports-lane-control = /bits/ 8 <0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff>; + + #address-cells = <2>; + #size-cells = <0>; + #sound-dai-cells = <1>; + status = "disabled"; + }; + lpass_lpiaon_noc: interconnect@7400000 { compatible = "qcom,kaanapali-lpass-lpiaon-noc"; reg = <0x0 0x07400000 0x0 0x19080>; @@ -901,6 +2919,168 @@ #interconnect-cells = <2>; }; + swr2: soundwire@7630000 { + compatible = "qcom,soundwire-v2.2.0", "qcom,soundwire-v2.0.0"; + reg = <0 0x07630000 0 0x10000>; + interrupts-extended = <&intc GIC_SPI 761 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 40 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "core", "wakeup"; + clocks = <&lpass_txmacro>; + clock-names = "iface"; + label = "TX"; + + pinctrl-0 = <&tx_swr_active>; + pinctrl-names = "default"; + + qcom,din-ports = <4>; + qcom,dout-ports = <0>; + qcom,ports-sinterval-low = /bits/ 8 <0x01 0x01 0x03 0x03>; + qcom,ports-offset1 = /bits/ 8 <0x00 0x00 0x01 0x01>; + qcom,ports-offset2 = /bits/ 8 <0x00 0x00 0x00 0x00>; + qcom,ports-hstart = /bits/ 8 <0xff 0xff 0xff 0xff>; + qcom,ports-hstop = /bits/ 8 <0xff 0xff 0xff 0xff>; + qcom,ports-word-length = /bits/ 8 <0xff 0xff 0xff 0xff>; + qcom,ports-block-pack-mode = /bits/ 8 <0xff 0xff 0xff 0xff>; + qcom,ports-block-group-count = /bits/ 8 <0xff 0xff 0xff 0xff>; + qcom,ports-lane-control = /bits/ 8 <0x01 0x02 0x00 0x00>; + + #address-cells = <2>; + #size-cells = <0>; + #sound-dai-cells = <1>; + status = "disabled"; + }; + + lpass_vamacro: codec@7660000 { + compatible = "qcom,kaanapali-lpass-va-macro", "qcom,sm8550-lpass-va-macro"; + reg = <0 0x07660000 0 0x2000>; + clocks = <&q6prmcc LPASS_CLK_ID_TX_CORE_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "mclk", "macro", "dcodec"; + + #clock-cells = <0>; + clock-output-names = "fsgen"; + #sound-dai-cells = <1>; + }; + + lpass_tlmm: pinctrl@7760000 { + compatible = "qcom,sm8750-lpass-lpi-pinctrl", + "qcom,sm8650-lpass-lpi-pinctrl"; + reg = <0 0x07760000 0 0x20000>; + + clocks = <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "core", "audio"; + + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&lpass_tlmm 0 0 23>; + + tx_swr_active: tx-swr-active-state { + clk-pins { + pins = "gpio0"; + function = "swr_tx_clk"; + drive-strength = <2>; + slew-rate = <1>; + bias-disable; + }; + + data-pins { + pins = "gpio1", "gpio2", "gpio14"; + function = "swr_tx_data"; + drive-strength = <2>; + slew-rate = <1>; + bias-bus-hold; + }; + }; + + rx_swr_active: rx-swr-active-state { + clk-pins { + pins = "gpio3"; + function = "swr_rx_clk"; + drive-strength = <2>; + slew-rate = <1>; + bias-disable; + }; + + data-pins { + pins = "gpio4", "gpio5"; + function = "swr_rx_data"; + drive-strength = <2>; + slew-rate = <1>; + bias-bus-hold; + }; + }; + + dmic01_default: dmic01-default-state { + clk-pins { + pins = "gpio6"; + function = "dmic1_clk"; + drive-strength = <8>; + output-high; + }; + + data-pins { + pins = "gpio7"; + function = "dmic1_data"; + drive-strength = <8>; + input-enable; + }; + }; + + dmic23_default: dmic23-default-state { + clk-pins { + pins = "gpio8"; + function = "dmic2_clk"; + drive-strength = <8>; + output-high; + }; + + data-pins { + pins = "gpio9"; + function = "dmic2_data"; + drive-strength = <8>; + input-enable; + }; + }; + + wsa_swr_active: wsa-swr-active-state { + clk-pins { + pins = "gpio10"; + function = "wsa_swr_clk"; + drive-strength = <2>; + slew-rate = <1>; + bias-disable; + }; + + data-pins { + pins = "gpio11"; + function = "wsa_swr_data"; + drive-strength = <2>; + slew-rate = <1>; + bias-bus-hold; + }; + }; + + wsa2_swr_active: wsa2-swr-active-state { + clk-pins { + pins = "gpio15"; + function = "wsa2_swr_clk"; + drive-strength = <2>; + slew-rate = <1>; + bias-disable; + }; + + data-pins { + pins = "gpio16"; + function = "wsa2_swr_data"; + drive-strength = <2>; + slew-rate = <1>; + bias-bus-hold; + }; + }; + }; + lpass_ag_noc: interconnect@7f40000 { compatible = "qcom,kaanapali-lpass-ag-noc"; reg = <0x0 0x07f40000 0x0 0xe080>; @@ -958,6 +3138,290 @@ }; }; + camcc: clock-controller@956d000 { + compatible = "qcom,kaanapali-camcc"; + reg = <0x0 0x0956d000 0x0 0x80000>; + + clocks = <&gcc GCC_CAMERA_AHB_CLK>, + <&bi_tcxo_div2>, + <&bi_tcxo_ao_div2>, + <&sleep_clk>; + + power-domains = <&rpmhpd RPMHPD_MMCX>, + <&rpmhpd RPMHPD_MXC>; + required-opps = <&rpmhpd_opp_low_svs>, + <&rpmhpd_opp_low_svs>; + + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + + mdss: display-subsystem@9800000 { + compatible = "qcom,kaanapali-mdss"; + reg = <0x0 0x09800000 0x0 0x1000>; + reg-names = "mdss"; + + interrupts = ; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>, + <&dispcc DISP_CC_MDSS_MDP_CLK>, + <&dispcc DISP_CC_MDSS_AHB_SWI_CLK>; + resets = <&dispcc DISP_CC_MDSS_CORE_BCR>; + + interconnects = <&mmss_noc MASTER_MDP QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_DISPLAY_CFG QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "mdp0-mem", + "cpu-cfg"; + + power-domains = <&dispcc DISP_CC_MDSS_CORE_GDSC>; + + iommus = <&apps_smmu 0x800 0x2>; + + interrupt-controller; + #interrupt-cells = <1>; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + status = "disabled"; + + mdss_mdp: display-controller@9801000 { + compatible = "qcom,kaanapali-dpu"; + reg = <0x0 0x09801000 0x0 0x1c8000>, + <0x0 0x09b16000 0x0 0x3000>; + reg-names = "mdp", + "vbif"; + + interrupts-extended = <&mdss 0>; + + clocks = <&gcc GCC_DISP_HF_AXI_CLK>, + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&dispcc DISP_CC_MDSS_MDP_LUT_CLK>, + <&dispcc DISP_CC_MDSS_MDP_CLK>, + <&dispcc DISP_CC_MDSS_VSYNC_CLK>; + clock-names = "nrt_bus", + "iface", + "lut", + "core", + "vsync"; + + assigned-clocks = <&dispcc DISP_CC_MDSS_VSYNC_CLK>; + assigned-clock-rates = <19200000>; + + operating-points-v2 = <&mdp_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dpu_intf1_out: endpoint { + remote-endpoint = <&mdss_dsi0_in>; + }; + }; + + port@1 { + reg = <1>; + + dpu_intf2_out: endpoint { + }; + }; + + port@2 { + reg = <2>; + + dpu_intf0_out: endpoint { + }; + }; + }; + + mdp_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-156000000 { + opp-hz = /bits/ 64 <156000000>; + required-opps = <&rpmhpd_opp_low_svs_d1>; + }; + + opp-207000000 { + opp-hz = /bits/ 64 <207000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-337000000 { + opp-hz = /bits/ 64 <337000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-417000000 { + opp-hz = /bits/ 64 <417000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-532000000 { + opp-hz = /bits/ 64 <532000000>; + required-opps = <&rpmhpd_opp_nom>; + }; + + opp-600000000 { + opp-hz = /bits/ 64 <600000000>; + required-opps = <&rpmhpd_opp_nom_l1>; + }; + + opp-650000000 { + opp-hz = /bits/ 64 <650000000>; + required-opps = <&rpmhpd_opp_turbo>; + }; + }; + }; + + mdss_dsi0: dsi@9ac0000 { + compatible = "qcom,kaanapali-dsi-ctrl", "qcom,mdss-dsi-ctrl"; + reg = <0x0 0x09ac0000 0x0 0x1000>; + reg-names = "dsi_ctrl"; + + interrupts-extended = <&mdss 4>; + + clocks = <&dispcc DISP_CC_MDSS_BYTE0_CLK>, + <&dispcc DISP_CC_MDSS_BYTE0_INTF_CLK>, + <&dispcc DISP_CC_MDSS_PCLK0_CLK>, + <&dispcc DISP_CC_MDSS_ESC0_CLK>, + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>, + <&mdss_dsi0_phy DSI_PIXEL_PLL_CLK>, + <&mdss_dsi0_phy DSI_BYTE_PLL_CLK>, + <&dispcc DISP_CC_ESYNC0_CLK>, + <&dispcc DISP_CC_OSC_CLK>, + <&dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>, + <&dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus", + "dsi_pll_pixel", + "dsi_pll_byte", + "esync", + "osc", + "byte_src", + "pixel_src"; + + operating-points-v2 = <&mdss_dsi_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + phys = <&mdss_dsi0_phy>; + phy-names = "dsi"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss_dsi0_in: endpoint { + remote-endpoint = <&dpu_intf1_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dsi0_out: endpoint { + }; + }; + }; + + mdss_dsi_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-187500000 { + opp-hz = /bits/ 64 <187500000>; + required-opps = <&rpmhpd_opp_low_svs_d1>; + }; + + opp-250000000 { + opp-hz = /bits/ 64 <250000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-312500000 { + opp-hz = /bits/ 64 <312500000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-358000000 { + opp-hz = /bits/ 64 <358000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + }; + }; + + mdss_dsi0_phy: phy@9ac1000 { + compatible = "qcom,kaanapali-dsi-phy-3nm"; + reg = <0x0 0x09ac1000 0x0 0x1cc>, + <0x0 0x09ac1200 0x0 0x280>, + <0x0 0x09ac1500 0x0 0x400>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", + "ref"; + + #clock-cells = <1>; + #phy-cells = <0>; + + status = "disabled"; + }; + }; + + dispcc: clock-controller@9ba2000 { + compatible = "qcom,kaanapali-dispcc"; + reg = <0x0 0x09ba2000 0x0 0x20000>; + clocks = <&bi_tcxo_div2>, + <&bi_tcxo_ao_div2>, + <&gcc GCC_DISP_AHB_CLK>, + <&sleep_clk>, + <0>, + <0>, + <0>, + <0>, + <0>, + <0>, + <0>, + <0>, + <&mdss_dsi0_phy DSI_BYTE_PLL_CLK>, + <&mdss_dsi0_phy DSI_PIXEL_PLL_CLK>, + <0>, + <0>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + required-opps = <&rpmhpd_opp_low_svs>; + + #clock-cells = <1>; + #power-domain-cells = <1>; + #reset-cells = <1>; + }; + pdc: interrupt-controller@b220000 { compatible = "qcom,kaanapali-pdc", "qcom,pdc"; reg = <0x0 0x0b220000 0x0 0x10000>, @@ -992,6 +3456,90 @@ interrupt-controller; }; + tsens0: thermal-sensor@c229000 { + compatible = "qcom,kaanapali-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c229000 0x0 0x1000>, + <0x0 0x0c222000 0x0 0x1000>; + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + #qcom,sensors = <5>; + #thermal-sensor-cells = <1>; + }; + + tsens1: thermal-sensor@c22a000 { + compatible = "qcom,kaanapali-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22a000 0x0 0x1000>, + <0x0 0x0c223000 0x0 0x1000>; + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + #qcom,sensors = <12>; + #thermal-sensor-cells = <1>; + }; + + tsens2: thermal-sensor@c22b000 { + compatible = "qcom,kaanapali-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22b000 0x0 0x1000>, + <0x0 0x0c224000 0x0 0x1000>; + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + #qcom,sensors = <7>; + #thermal-sensor-cells = <1>; + }; + + tsens3: thermal-sensor@c22c000 { + compatible = "qcom,kaanapali-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22c000 0x0 0x1000>, + <0x0 0x0c225000 0x0 0x1000>; + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + #qcom,sensors = <4>; + #thermal-sensor-cells = <1>; + }; + + tsens4: thermal-sensor@c22d000 { + compatible = "qcom,kaanapali-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22d000 0x0 0x1000>, + <0x0 0x0c226000 0x0 0x1000>; + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + #qcom,sensors = <8>; + #thermal-sensor-cells = <1>; + }; + + tsens5: thermal-sensor@c22e000 { + compatible = "qcom,kaanapali-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22e000 0x0 0x1000>, + <0x0 0x0c227000 0x0 0x1000>; + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + #qcom,sensors = <12>; + #thermal-sensor-cells = <1>; + }; + + tsens6: thermal-sensor@c22f000 { + compatible = "qcom,kaanapali-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22f000 0x0 0x1000>, + <0x0 0x0c228000 0x0 0x1000>; + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + #qcom,sensors = <7>; + #thermal-sensor-cells = <1>; + }; + aoss_qmp: power-management@c300000 { compatible = "qcom,kaanapali-aoss-qmp", "qcom,aoss-qmp"; reg = <0x0 0x0c300000 0x0 0x400>; @@ -1006,6 +3554,53 @@ #clock-cells = <0>; }; + arbiter@c400000 { + compatible = "qcom,kaanapali-spmi-pmic-arb", "qcom,glymur-spmi-pmic-arb"; + reg = <0x0 0x0c400000 0x0 0x3000>, + <0x0 0x0c900000 0x0 0x400000>, + <0x0 0x0c4c0000 0x0 0x400000>, + <0x0 0x0c403000 0x0 0x8000>; + reg-names = "core", + "chnls", + "obsrvr", + "chnl_map"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + qcom,channel = <0>; + qcom,ee = <0>; + + spmi_bus0: spmi@c426000 { + reg = <0x0 0x0c426000 0x0 0x4000>, + <0x0 0x0c8c0000 0x0 0x10000>, + <0x0 0x0c42a000 0x0 0x8000>; + reg-names = "cnfg", + "intr", + "chnl_owner"; + interrupts-extended = <&pdc 1 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "periph_irq"; + interrupt-controller; + #interrupt-cells = <4>; + #address-cells = <2>; + #size-cells = <0>; + }; + + spmi_bus1: spmi@c437000 { + reg = <0x0 0x0c437000 0x0 0x4000>, + <0x0 0x0c8d0000 0x0 0x10000>, + <0x0 0x0c43b000 0x0 0x8000>; + reg-names = "cnfg", + "intr", + "chnl_owner"; + interrupts-extended = <&pdc 3 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "periph_irq"; + interrupt-controller; + #interrupt-cells = <4>; + #address-cells = <2>; + #size-cells = <0>; + }; + }; + tlmm: pinctrl@f100000 { compatible = "qcom,kaanapali-tlmm"; reg = <0x0 0x0f100000 0x0 0x300000>; @@ -1017,6 +3612,491 @@ #interrupt-cells = <2>; wakeup-parent = <&pdc>; + hub_i2c0_data_clk: hub-i2c0-data-clk-state { + /* SDA, SCL */ + pins = "gpio66", "gpio67"; + function = "i2chub0_se0"; + drive-strength = <2>; + bias-pull-up; + }; + + hub_i2c1_data_clk: hub-i2c1-data-clk-state { + /* SDA, SCL */ + pins = "gpio78", "gpio79"; + function = "i2chub0_se1"; + drive-strength = <2>; + bias-pull-up; + }; + + hub_i2c2_data_clk: hub-i2c2-data-clk-state { + /* SDA, SCL */ + pins = "gpio68", "gpio69"; + function = "i2chub0_se2"; + drive-strength = <2>; + bias-pull-up; + }; + + hub_i2c3_data_clk: hub-i2c3-data-clk-state { + /* SDA, SCL */ + pins = "gpio70", "gpio71"; + function = "i2chub0_se3"; + drive-strength = <2>; + bias-pull-up; + }; + + hub_i2c4_data_clk: hub-i2c4-data-clk-state { + /* SDA, SCL */ + pins = "gpio72", "gpio73"; + function = "i2chub0_se4"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c0_data_clk: qup-i2c0-data-clk-state { + /* SDA, SCL */ + pins = "gpio80", "gpio83"; + function = "qup1_se0"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c1_data_clk: qup-i2c1-data-clk-state { + /* SDA, SCL */ + pins = "gpio74", "gpio75"; + function = "qup1_se1"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c2_data_clk: qup-i2c2-data-clk-state { + /* SDA, SCL */ + pins = "gpio40", "gpio41"; + function = "qup1_se2"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c3_data_clk: qup-i2c3-data-clk-state { + /* SDA, SCL */ + pins = "gpio44", "gpio45"; + function = "qup1_se3"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c4_data_clk: qup-i2c4-data-clk-state { + /* SDA, SCL */ + pins = "gpio36", "gpio37"; + function = "qup1_se4"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c5_data_clk: qup-i2c5-data-clk-state { + /* SDA, SCL */ + pins = "gpio52", "gpio53"; + function = "qup1_se5"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c6_data_clk: qup-i2c6-data-clk-state { + /* SDA, SCL */ + pins = "gpio56", "gpio57"; + function = "qup1_se6"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c8_data_clk: qup-i2c8-data-clk-state { + /* SDA, SCL */ + pins = "gpio0", "gpio1"; + function = "qup2_se0"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c9_data_clk: qup-i2c9-data-clk-state { + /* SDA, SCL */ + pins = "gpio4", "gpio5"; + function = "qup2_se1"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c10_data_clk: qup-i2c10-data-clk-state { + /* SDA, SCL */ + pins = "gpio117", "gpio118"; + function = "qup2_se2"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c11_data_clk: qup-i2c11-data-clk-state { + /* SDA, SCL */ + pins = "gpio122", "gpio123"; + function = "qup2_se3"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c12_data_clk: qup-i2c12-data-clk-state { + /* SDA, SCL */ + pins = "gpio208", "gpio209"; + function = "qup2_se4"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c13_data_clk: qup-i2c13-data-clk-state { + /* SDA, SCL */ + pins = "gpio64", "gpio65"; + function = "qup3_se0"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c14_data_clk: qup-i2c14-data-clk-state { + /* SDA, SCL */ + pins = "gpio8", "gpio9"; + function = "qup3_se1"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c15_data_clk: qup-i2c15-data-clk-state { + /* SDA, SCL */ + pins = "gpio12", "gpio13"; + function = "qup3_se2"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c16_data_clk: qup-i2c16-data-clk-state { + /* SDA, SCL */ + pins = "gpio16", "gpio17"; + function = "qup3_se3"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c17_data_clk: qup-i2c17-data-clk-state { + /* SDA, SCL */ + pins = "gpio20", "gpio21"; + function = "qup3_se4"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c19_data_clk: qup-i2c19-data-clk-state { + /* SDA, SCL */ + pins = "gpio48", "gpio49"; + function = "qup4_se0"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c20_data_clk: qup-i2c20-data-clk-state { + /* SDA, SCL */ + pins = "gpio28", "gpio29"; + function = "qup4_se1"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c21_data_clk: qup-i2c21-data-clk-state { + /* SDA, SCL */ + pins = "gpio32", "gpio33"; + function = "qup4_se2"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c22_data_clk: qup-i2c22-data-clk-state { + /* SDA, SCL */ + pins = "gpio121", "gpio84"; + function = "qup4_se3"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_i2c23_data_clk: qup-i2c23-data-clk-state { + /* SDA, SCL */ + pins = "gpio161", "gpio162"; + function = "qup4_se4"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_spi0_cs: qup-spi0-cs-state { + pins = "gpio81"; + function = "qup1_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi0_data_clk: qup-spi0-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio80", "gpio83", "gpio82"; + function = "qup1_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi1_cs: qup-spi1-cs-state { + pins = "gpio77"; + function = "qup1_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi1_data_clk: qup-spi1-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio74", "gpio75", "gpio76"; + function = "qup1_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi2_cs: qup-spi2-cs-state { + pins = "gpio43"; + function = "qup1_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi2_data_clk: qup-spi2-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio40", "gpio41", "gpio42"; + function = "qup1_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi3_cs: qup-spi3-cs-state { + pins = "gpio47"; + function = "qup1_se3"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi3_data_clk: qup-spi3-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio44", "gpio45", "gpio46"; + function = "qup1_se3"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi4_cs: qup-spi4-cs-state { + pins = "gpio39"; + function = "qup1_se4"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi4_data_clk: qup-spi4-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio36", "gpio37", "gpio38"; + function = "qup1_se4"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi5_cs: qup-spi5-cs-state { + pins = "gpio55"; + function = "qup1_se5"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi5_data_clk: qup-spi5-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio52", "gpio53", "gpio54"; + function = "qup1_se5"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi6_cs: qup-spi6-cs-state { + pins = "gpio59"; + function = "qup1_se6"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi6_data_clk: qup-spi6-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio56", "gpio57", "gpio58"; + function = "qup1_se6"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi8_cs: qup-spi8-cs-state { + pins = "gpio3"; + function = "qup2_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi8_data_clk: qup-spi8-data-clk-state { + /* MISO, MOSI, CLK */pins = "gpio0", "gpio1", "gpio2"; + function = "qup2_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi9_cs: qup-spi9-cs-state { + pins = "gpio7"; + function = "qup2_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi9_data_clk: qup-spi9-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio4", "gpio5", "gpio6"; + function = "qup2_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi10_cs: qup-spi10-cs-state { + pins = "gpio120"; + function = "qup2_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi10_data_clk: qup-spi10-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio117", "gpio118", "gpio119"; + function = "qup2_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi11_cs: qup-spi11-cs-state { + pins = "gpio125"; + function = "qup2_se3"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi11_data_clk: qup-spi11-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio122", "gpio123", "gpio124"; + function = "qup2_se3"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi14_cs: qup-spi14-cs-state { + pins = "gpio11"; + function = "qup3_se1"; + drive-strength = <6>; + bias-pull-up; + }; + + qup_spi14_data_clk: qup-spi14-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio8", "gpio9", "gpio10"; + function = "qup3_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi15_cs: qup-spi15-cs-state { + pins = "gpio15"; + function = "qup3_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi15_data_clk: qup-spi15-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio12", "gpio13", "gpio14"; + function = "qup3_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi16_cs: qup-spi16-cs-state { + pins = "gpio19"; + function = "qup3_se3"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi16_data_clk: qup-spi16-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio16", "gpio17", "gpio18"; + function = "qup3_se3"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi17_cs: qup-spi17-cs-state { + pins = "gpio23"; + function = "qup3_se4"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi17_data_clk: qup-spi17-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio20", "gpio21", "gpio22"; + function = "qup3_se4"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi19_cs: qup-spi19-cs-state { + pins = "gpio51"; + function = "qup4_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi19_data_clk: qup-spi19-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio48", "gpio49", "gpio50"; + function = "qup4_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi20_cs: qup-spi20-cs-state { + pins = "gpio31"; + function = "qup4_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi20_data_clk: qup-spi20-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio28", "gpio29", "gpio30"; + function = "qup4_se1"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi21_cs: qup-spi21-cs-state { + pins = "gpio35"; + function = "qup4_se2"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi21_data_clk: qup-spi21-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio32", "gpio33", "gpio34"; + function = "qup4_se2"; + drive-strength = <6>; + bias-disable; + }; + qup_uart7_default: qup-uart7-state { /* TX, RX */ pins = "gpio62", "gpio63"; @@ -1025,6 +4105,22 @@ bias-disable; }; + qup_uart18_default: qup-uart18-default-state { + /* TX, RX */ + pins = "gpio26", "gpio27"; + function = "qup3_se5"; + drive-strength = <2>; + bias-pull-up; + }; + + qup_uart18_cts_rts: qup-uart18-cts-rts-state { + /* CTS, RTS */ + pins = "gpio24", "gpio25"; + function = "qup3_se5"; + drive-strength = <2>; + bias-pull-down; + }; + sdc2_default: sdc2-default-state { clk-pins { pins = "sdc2_clk"; @@ -1080,6 +4176,1114 @@ }; }; + stm@10002000 { + compatible = "arm,coresight-stm", "arm,primecell"; + reg = <0x0 0x10002000 0x0 0x1000>, + <0x0 0x16280000 0x0 0x180000>; + reg-names = "stm-base", + "stm-stimulus-base"; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + stm_out: endpoint { + remote-endpoint = <&funnel_in0_in7>; + }; + }; + }; + }; + + tpdm@10003000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x10003000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,cmb-element-bits = <32>; + + out-ports { + port { + tpdm_dcc_out: endpoint { + remote-endpoint = <&tpda_qdss_in0>; + }; + }; + }; + }; + + tpda@10004000 { + compatible = "qcom,coresight-tpda", "arm,primecell"; + reg = <0x0 0x10004000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + in-ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + tpda_qdss_in0: endpoint { + remote-endpoint = <&tpdm_dcc_out>; + }; + }; + + port@1 { + reg = <1>; + + tpda_qdss_in1: endpoint { + remote-endpoint = <&tpdm_spdm_out>; + }; + }; + }; + + out-ports { + port { + tpda_qdss_out: endpoint { + remote-endpoint = <&funnel_in0_in6>; + }; + }; + }; + }; + + tpdm@1000f000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x1000f000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,cmb-element-bits = <32>; + + out-ports { + port { + tpdm_spdm_out: endpoint { + remote-endpoint = <&tpda_qdss_in1>; + }; + }; + }; + }; + + funnel@10041000 { + compatible = "arm,coresight-dynamic-funnel", "arm,primecell"; + reg = <0x0 0x10041000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + in-ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + funnel_in0_in0: endpoint { + remote-endpoint = <&tn_ag_out>; + }; + }; + + port@6 { + reg = <6>; + + funnel_in0_in6: endpoint { + remote-endpoint = <&tpda_qdss_out>; + }; + }; + + port@7 { + reg = <7>; + + funnel_in0_in7: endpoint { + remote-endpoint = <&stm_out>; + }; + }; + }; + + out-ports { + port { + funnel_in0_out: endpoint { + remote-endpoint = <&funnel_aoss_in6>; + }; + }; + }; + }; + + tpdm@11000000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x11000000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,dsb-element-bits = <32>; + qcom,dsb-msrs-num = <32>; + + out-ports { + port { + tpdm_modem0_out: endpoint { + remote-endpoint = <&tpda_modem_in0>; + }; + }; + }; + }; + + tpda@11004000 { + compatible = "qcom,coresight-tpda", "arm,primecell"; + reg = <0x0 0x11004000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + in-ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + tpda_modem_in0: endpoint { + remote-endpoint = <&tpdm_modem0_out>; + }; + }; + + port@1 { + reg = <1>; + + tpda_modem_in1: endpoint { + remote-endpoint = <&tpdm_modem1_out>; + }; + }; + + port@2 { + reg = <2>; + + tpda_modem_in2: endpoint { + remote-endpoint = <&tpdm_modem2_out>; + }; + }; + }; + + out-ports { + port { + tpda_modem_out: endpoint { + remote-endpoint = <&funnel_modem_dl_in0>; + }; + }; + }; + }; + + funnel@11005000 { + compatible = "arm,coresight-dynamic-funnel", "arm,primecell"; + reg = <0x0 0x11005000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + in-ports { + port { + funnel_modem_dl_in0: endpoint { + remote-endpoint = <&tpda_modem_out>; + }; + }; + }; + + out-ports { + port { + funnel_modem_dl_out: endpoint { + remote-endpoint = <&tn_ag_in13>; + }; + }; + }; + }; + + tpdm@1102c000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x1102c000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,dsb-msrs-num = <32>; + + out-ports { + port { + tpdm_gcc_out: endpoint { + remote-endpoint = <&tn_ag_in17>; + }; + }; + }; + }; + + tpdm@11180000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x11180000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,dsb-element-bits = <32>; + qcom,dsb-msrs-num = <32>; + + out-ports { + port { + tpdm_cdsp_out: endpoint { + remote-endpoint = <&tpda_cdsp_in0>; + }; + }; + }; + }; + + tpdm@11183000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x11183000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,dsb-element-bits = <32>; + qcom,cmb-element-bits = <32>; + + out-ports { + port { + tpdm_cdsp_cmsr1_out: endpoint { + remote-endpoint = <&tpda_cdsp_in3>; + }; + }; + }; + }; + + tpdm@11184000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x11184000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,dsb-element-bits = <32>; + qcom,cmb-element-bits = <32>; + + out-ports { + port { + tpdm_cdsp_cmsr2_out: endpoint { + remote-endpoint = <&tpda_cdsp_in4>; + }; + }; + }; + }; + + tpdm@11185000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x11185000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,cmb-element-bits = <64>; + + out-ports { + port { + tpdm_cdsp_dpm1_out: endpoint { + remote-endpoint = <&tpda_cdsp_in5>; + }; + }; + }; + }; + + tpdm@11186000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x11186000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,cmb-element-bits = <64>; + + out-ports { + port { + tpdm_cdsp_dpm2_out: endpoint { + remote-endpoint = <&tpda_cdsp_in6>; + }; + }; + }; + }; + + tpda@11188000 { + compatible = "qcom,coresight-tpda", "arm,primecell"; + reg = <0x0 0x11188000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + in-ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + tpda_cdsp_in0: endpoint { + remote-endpoint = <&tpdm_cdsp_out>; + }; + }; + + port@1 { + reg = <1>; + + tpda_cdsp_in1: endpoint { + remote-endpoint = <&tpdm_cdsp_llm_out>; + }; + }; + + port@2 { + reg = <2>; + + tpda_cdsp_in2: endpoint { + remote-endpoint = <&tpdm_cdsp_llm2_out>; + }; + }; + + port@3 { + reg = <3>; + + tpda_cdsp_in3: endpoint { + remote-endpoint = <&tpdm_cdsp_cmsr1_out>; + }; + }; + + port@4 { + reg = <4>; + + tpda_cdsp_in4: endpoint { + remote-endpoint = <&tpdm_cdsp_cmsr2_out>; + }; + }; + + port@5 { + reg = <5>; + + tpda_cdsp_in5: endpoint { + remote-endpoint = <&tpdm_cdsp_dpm1_out>; + }; + }; + + port@6 { + reg = <6>; + + tpda_cdsp_in6: endpoint { + remote-endpoint = <&tpdm_cdsp_dpm2_out>; + }; + }; + }; + + out-ports { + port { + tpda_cdsp_out: endpoint { + remote-endpoint = <&funnel_cdsp_in0>; + }; + }; + }; + }; + + funnel@11189000 { + compatible = "arm,coresight-dynamic-funnel", "arm,primecell"; + reg = <0x0 0x11189000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + in-ports { + port { + funnel_cdsp_in0: endpoint { + remote-endpoint = <&tpda_cdsp_out>; + }; + }; + }; + + out-ports { + port { + funnel_cdsp_out: endpoint { + remote-endpoint = <&tn_ag_in16>; + }; + }; + }; + }; + + tpdm@111a3000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111a3000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,dsb-msrs-num = <32>; + + out-ports { + port { + tpdm_pmu_out: endpoint { + remote-endpoint = <&tn_ag_in29>; + }; + }; + }; + }; + + tpdm@111a4000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111a4000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + tpdm_qrng_out: endpoint { + remote-endpoint = <&tn_ag_in18>; + }; + }; + }; + }; + + tpdm@111a5000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111a5000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,dsb-msrs-num = <32>; + + out-ports { + port { + tpdm_dlmm_out: endpoint { + remote-endpoint = <&tn_ag_in25>; + }; + }; + }; + }; + + tpdm@111a6000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111a6000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,dsb-msrs-num = <32>; + + out-ports { + port { + tpdm_north_dsb_out: endpoint { + remote-endpoint = <&tn_ag_in26>; + }; + }; + }; + }; + + tpdm@111a7000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111a7000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,dsb-msrs-num = <32>; + + out-ports { + port { + tpdm_south_dsb_out: endpoint { + remote-endpoint = <&tn_ag_in27>; + }; + }; + }; + }; + + tpdm@111a8000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111a8000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + tpdm_rdpm_cmb0_out: endpoint { + remote-endpoint = <&tn_ag_in30>; + }; + }; + }; + }; + + tpdm@111a9000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111a9000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + tpdm_rdpm_cmb1_out: endpoint { + remote-endpoint = <&tn_ag_in31>; + }; + }; + }; + }; + + tpdm@111aa000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111aa000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + tpdm_rdpm_cmb2_out: endpoint { + remote-endpoint = <&tn_ag_in32>; + }; + }; + }; + }; + + tpdm@111ab000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111ab000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + tpdm_ipcc_cmb0_out: endpoint { + remote-endpoint = <&tn_ag_in36>; + }; + }; + }; + }; + + tpdm@111ac000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111ac000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + tpdm_ipcc_cmb1_out: endpoint { + remote-endpoint = <&tn_ag_in28>; + }; + }; + }; + }; + + tpdm@111ad000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111ad000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + tpdm_ipcc_cmb2_out: endpoint { + remote-endpoint = <&tn_ag_in34>; + }; + }; + }; + }; + + tpdm@111ae000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111ae000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + tpdm_ipcc_cmb3_out: endpoint { + remote-endpoint = <&tn_ag_in37>; + }; + }; + }; + }; + + tpdm@111af000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111af000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + tpdm_ipcc_cmb4_out: endpoint { + remote-endpoint = <&tn_ag_in35>; + }; + }; + }; + }; + + tpdm@111b3000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111b3000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + tpdm_pcie_rscc_out: endpoint { + remote-endpoint = <&tn_ag_in8>; + }; + }; + }; + }; + + tn@111b8000 { + compatible = "qcom,coresight-tnoc", "arm,primecell"; + reg = <0x0 0x111b8000 0x0 0x4200>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + in-ports { + #address-cells = <1>; + #size-cells = <0>; + + port@8 { + reg = <8>; + + tn_ag_in8: endpoint { + remote-endpoint = <&tpdm_pcie_rscc_out>; + }; + }; + + port@d { + reg = <0xd>; + + tn_ag_in13: endpoint { + remote-endpoint = <&funnel_modem_dl_out>; + }; + }; + + port@10 { + reg = <0x10>; + + tn_ag_in16: endpoint { + remote-endpoint = <&funnel_cdsp_out>; + }; + }; + + port@11 { + reg = <0x11>; + + tn_ag_in17: endpoint { + remote-endpoint = <&tpdm_gcc_out>; + }; + }; + + port@12 { + reg = <0x12>; + + tn_ag_in18: endpoint { + remote-endpoint = <&tpdm_qrng_out>; + }; + }; + + port@13 { + reg = <0x13>; + + tn_ag_in19: endpoint { + remote-endpoint = <&tpdm_qm_out>; + }; + }; + + port@15 { + reg = <0x15>; + + tn_ag_in21: endpoint { + remote-endpoint = <&tpdm_ipa_out>; + }; + }; + + port@19 { + reg = <0x19>; + + tn_ag_in25: endpoint { + remote-endpoint = <&tpdm_dlmm_out>; + }; + }; + + port@1a { + reg = <0x1a>; + + tn_ag_in26: endpoint { + remote-endpoint = <&tpdm_north_dsb_out>; + }; + }; + + port@1b { + reg = <0x1b>; + + tn_ag_in27: endpoint { + remote-endpoint = <&tpdm_south_dsb_out>; + }; + }; + + port@1c { + reg = <0x1c>; + + tn_ag_in28: endpoint { + remote-endpoint = <&tpdm_ipcc_cmb1_out>; + }; + }; + + port@1d { + reg = <0x1d>; + + tn_ag_in29: endpoint { + remote-endpoint = <&tpdm_pmu_out>; + }; + }; + + port@1e { + reg = <0x1e>; + + tn_ag_in30: endpoint { + remote-endpoint = <&tpdm_rdpm_cmb0_out>; + }; + }; + + port@1f { + reg = <0x1f>; + + tn_ag_in31: endpoint { + remote-endpoint = <&tpdm_rdpm_cmb1_out>; + }; + }; + + port@20 { + reg = <0x20>; + + tn_ag_in32: endpoint { + remote-endpoint = <&tpdm_rdpm_cmb2_out>; + }; + }; + + port@22 { + reg = <0x22>; + + tn_ag_in34: endpoint { + remote-endpoint = <&tpdm_ipcc_cmb2_out>; + }; + }; + + port@23 { + reg = <0x23>; + + tn_ag_in35: endpoint { + remote-endpoint = <&tpdm_ipcc_cmb4_out>; + }; + }; + + port@24 { + reg = <0x24>; + + tn_ag_in36: endpoint { + remote-endpoint = <&tpdm_ipcc_cmb0_out>; + }; + }; + + port@25 { + reg = <37>; + + tn_ag_in37: endpoint { + remote-endpoint = <&tpdm_ipcc_cmb3_out>; + }; + }; + }; + + out-ports { + port { + tn_ag_out: endpoint { + remote-endpoint = <&funnel_in0_in0>; + }; + }; + }; + }; + + tpdm@111d0000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x111d0000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + tpdm_qm_out: endpoint { + remote-endpoint = <&tn_ag_in19>; + }; + }; + }; + }; + + tpdm@11303000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x11303000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,cmb-element-bits = <64>; + + out-ports { + port { + tpdm_swao_prio4_out: endpoint { + remote-endpoint = <&tpda_aoss_in4>; + }; + }; + }; + }; + + funnel@11304000 { + compatible = "arm,coresight-dynamic-funnel", "arm,primecell"; + reg = <0x0 0x11304000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + in-ports { + #address-cells = <1>; + #size-cells = <0>; + + port@5 { + reg = <5>; + + funnel_aoss_in5: endpoint { + remote-endpoint = <&tpda_aoss_out>; + }; + }; + + port@6 { + reg = <6>; + + funnel_aoss_in6: endpoint { + remote-endpoint = <&funnel_in0_out>; + }; + }; + + }; + + out-ports { + port { + funnel_aoss_out: endpoint { + remote-endpoint = <&tmc_etf_in>; + }; + }; + }; + }; + + tmc@11305000 { + compatible = "arm,coresight-tmc", "arm,primecell"; + reg = <0x0 0x11305000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + in-ports { + port { + tmc_etf_in: endpoint { + remote-endpoint = <&funnel_aoss_out>; + }; + }; + }; + }; + + tpda@11308000 { + compatible = "qcom,coresight-tpda", "arm,primecell"; + reg = <0x0 0x11308000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + in-ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + tpda_aoss_in0: endpoint { + remote-endpoint = <&tpdm_swao_prio0_out>; + }; + }; + + port@1 { + reg = <1>; + + tpda_aoss_in1: endpoint { + remote-endpoint = <&tpdm_swao_prio1_out>; + }; + }; + + port@2 { + reg = <2>; + + tpda_aoss_in2: endpoint { + remote-endpoint = <&tpdm_swao_prio2_out>; + }; + }; + + port@3 { + reg = <3>; + + tpda_aoss_in3: endpoint { + remote-endpoint = <&tpdm_swao_prio3_out>; + }; + }; + + port@4 { + reg = <4>; + + tpda_aoss_in4: endpoint { + remote-endpoint = <&tpdm_swao_prio4_out>; + }; + }; + + port@5 { + reg = <5>; + + tpda_aoss_in5: endpoint { + remote-endpoint = <&tpdm_swao_out>; + }; + }; + }; + + out-ports { + port { + tpda_aoss_out: endpoint { + remote-endpoint = <&funnel_aoss_in5>; + }; + }; + }; + }; + + tpdm@11309000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x11309000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,cmb-element-bits = <64>; + + out-ports { + port { + tpdm_swao_prio0_out: endpoint { + remote-endpoint = <&tpda_aoss_in0>; + }; + }; + }; + }; + + tpdm@1130a000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x1130a000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,cmb-element-bits = <64>; + + out-ports { + port { + tpdm_swao_prio1_out: endpoint { + remote-endpoint = <&tpda_aoss_in1>; + }; + }; + }; + }; + + tpdm@1130b000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x1130b000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,cmb-element-bits = <64>; + + out-ports { + port { + tpdm_swao_prio2_out: endpoint { + remote-endpoint = <&tpda_aoss_in2>; + }; + }; + }; + }; + + tpdm@1130c000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x1130c000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,cmb-element-bits = <64>; + + out-ports { + port { + tpdm_swao_prio3_out: endpoint { + remote-endpoint = <&tpda_aoss_in3>; + }; + }; + }; + }; + + tpdm@1130d000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x1130d000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,dsb-element-bits = <32>; + qcom,dsb-msrs-num = <32>; + + out-ports { + port { + tpdm_swao_out: endpoint { + remote-endpoint = <&tpda_aoss_in5>; + }; + }; + }; + }; + + tpdm@11422000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0 0x11422000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + qcom,dsb-msrs-num = <32>; + + out-ports { + port { + tpdm_ipa_out: endpoint { + remote-endpoint = <&tn_ag_in21>; + }; + }; + }; + }; + sram@14680000 { compatible = "qcom,kaanapali-imem", "mmio-sram"; reg = <0x0 0x14680000 0x0 0x1000>; @@ -1239,7 +5443,7 @@ gic_its: msi-controller@17040000 { compatible = "arm,gic-v3-its"; - reg = <0x0 0x17040000 0x0 0x20000>; + reg = <0x0 0x17040000 0x0 0x40000>; msi-controller; #msi-cells = <1>; @@ -1476,8 +5680,165 @@ #interconnect-cells = <2>; }; + remoteproc_cdsp: remoteproc@26300000 { + compatible = "qcom,kaanapali-cdsp-pas", "qcom,sm8550-cdsp-pas"; + reg = <0x0 0x26300000 0x0 0x10000>; + + interrupts-extended = <&intc GIC_SPI 578 IRQ_TYPE_EDGE_RISING>, + <&smp2p_cdsp_in 0 IRQ_TYPE_EDGE_RISING>, + <&smp2p_cdsp_in 1 IRQ_TYPE_EDGE_RISING>, + <&smp2p_cdsp_in 2 IRQ_TYPE_EDGE_RISING>, + <&smp2p_cdsp_in 3 IRQ_TYPE_EDGE_RISING>, + <&smp2p_cdsp_in 7 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog", + "fatal", + "ready", + "handover", + "stop-ack", + "shutdown-ack"; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-names = "xo"; + + interconnects = <&nsp_noc MASTER_CDSP_PROC QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + + power-domains = <&rpmhpd RPMHPD_CX>, + <&rpmhpd RPMHPD_MXC>, + <&rpmhpd RPMHPD_NSP>; + power-domain-names = "cx", + "mxc", + "nsp"; + + memory-region = <&cdsp_mem>, <&q6_cdsp_dtb_mem>; + qcom,qmp = <&aoss_qmp>; + qcom,smem-states = <&smp2p_cdsp_out 0>; + qcom,smem-state-names = "stop"; + + status = "disabled"; + + glink-edge { + interrupts-extended = <&ipcc IPCC_MPROC_CDSP + IPCC_MPROC_SIGNAL_GLINK_QMP + IRQ_TYPE_EDGE_RISING>; + mboxes = <&ipcc IPCC_MPROC_CDSP + IPCC_MPROC_SIGNAL_GLINK_QMP>; + qcom,remote-pid = <5>; + label = "cdsp"; + + fastrpc { + compatible = "qcom,kaanapali-fastrpc"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + label = "cdsp"; + #address-cells = <1>; + #size-cells = <0>; + + compute-cb@1 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <1>; + iommus = <&apps_smmu 0x19c1 0x0>, + <&apps_smmu 0x1961 0x0>, + <&apps_smmu 0x0c21 0x0>, + <&apps_smmu 0x0c01 0x40>; + dma-coherent; + }; + + compute-cb@2 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <2>; + iommus = <&apps_smmu 0x1962 0x0>, + <&apps_smmu 0x0c02 0x20>, + <&apps_smmu 0x0c42 0x0>, + <&apps_smmu 0x19c2 0x0>; + dma-coherent; + }; + + compute-cb@3 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <3>; + iommus = <&apps_smmu 0x1963 0x0>, + <&apps_smmu 0x0c23 0x0>, + <&apps_smmu 0x0c03 0x40>, + <&apps_smmu 0x19c3 0x0>; + dma-coherent; + }; + + compute-cb@4 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <4>; + iommus = <&apps_smmu 0x1964 0x0>, + <&apps_smmu 0x0c44 0x0>, + <&apps_smmu 0x0c04 0x20>, + <&apps_smmu 0x19c4 0x0>; + dma-coherent; + }; + + compute-cb@5 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <5>; + iommus = <&apps_smmu 0x1965 0x0>, + <&apps_smmu 0x0c45 0x0>, + <&apps_smmu 0x0c05 0x20>, + <&apps_smmu 0x19c5 0x0>; + dma-coherent; + }; + + compute-cb@6 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <6>; + iommus = <&apps_smmu 0x1966 0x0>, + <&apps_smmu 0x0c06 0x20>, + <&apps_smmu 0x0c46 0x0>, + <&apps_smmu 0x19c6 0x0>; + dma-coherent; + }; + + compute-cb@7 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <7>; + iommus = <&apps_smmu 0x1967 0x0>, + <&apps_smmu 0x0c27 0x0>, + <&apps_smmu 0x0c07 0x40>, + <&apps_smmu 0x19c7 0x0>; + dma-coherent; + }; + + compute-cb@8 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <8>; + iommus = <&apps_smmu 0x1968 0x0>, + <&apps_smmu 0x0c08 0x20>, + <&apps_smmu 0x0c48 0x0>, + <&apps_smmu 0x19c8 0x0>; + dma-coherent; + }; + + compute-cb@12 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <12>; + iommus = <&apps_smmu 0x196c 0x0>, + <&apps_smmu 0x0c2c 0x00>, + <&apps_smmu 0x0c0c 0x40>, + <&apps_smmu 0x19cc 0x0>; + dma-coherent; + }; + + compute-cb@13 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <13>; + iommus = <&apps_smmu 0x196d 0x0>, + <&apps_smmu 0x0c0d 0x40>, + <&apps_smmu 0x0c2e 0x0>, + <&apps_smmu 0x0c2d 0x0>, + <&apps_smmu 0x19cd 0x0>; + dma-coherent; + }; + }; + }; + }; + /* Cluster 0 */ - pmu@310b3400 { + pmu@310b3400 { compatible = "qcom,kaanapali-cpu-bwmon", "qcom,sdm845-bwmon"; reg = <0x0 0x310b3400 0x0 0x600>; @@ -1538,7 +5899,7 @@ }; /* Cluster 1 */ - pmu@310b7400 { + pmu@310b7400 { compatible = "qcom,kaanapali-cpu-bwmon", "qcom,sdm845-bwmon"; reg = <0x0 0x310b7400 0x0 0x600>; @@ -1595,6 +5956,997 @@ }; }; + thermal-zones { + cpullc-0-0-thermal { + thermal-sensors = <&tsens0 0>; + + trips { + cpullc-0-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpullc-0-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpullc-0-1-thermal { + thermal-sensors = <&tsens0 1>; + + trips { + cpullc-0-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpullc-0-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + qmx-0-0-thermal { + thermal-sensors = <&tsens0 2>; + + trips { + qmx-0-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + qmx-0-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + qmx-0-1-thermal { + thermal-sensors = <&tsens0 3>; + + trips { + qmx-0-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + qmx-0-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + qmx-0-2-thermal { + thermal-sensors = <&tsens0 4>; + + trips { + qmx-0-2-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + qmx-0-2-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-0-0-thermal { + thermal-sensors = <&tsens1 0>; + + trips { + cpu-0-0-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-0-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-0-1-thermal { + thermal-sensors = <&tsens1 1>; + + trips { + cpu-0-0-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-0-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-1-0-thermal { + thermal-sensors = <&tsens1 2>; + + trips { + cpu-0-1-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-1-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-1-1-thermal { + thermal-sensors = <&tsens1 3>; + + trips { + cpu-0-1-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-1-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-2-0-thermal { + thermal-sensors = <&tsens1 4>; + + trips { + cpu-0-2-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-2-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-2-1-thermal { + thermal-sensors = <&tsens1 5>; + + trips { + cpu-0-2-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-2-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-3-0-thermal { + thermal-sensors = <&tsens1 6>; + + trips { + cpu-0-3-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-3-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-3-1-thermal { + thermal-sensors = <&tsens1 7>; + + trips { + cpu-0-3-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-3-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-4-0-thermal { + thermal-sensors = <&tsens1 8>; + + trips { + cpu-0-4-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-4-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-4-1-thermal { + thermal-sensors = <&tsens1 9>; + + trips { + cpu-0-4-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-4-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-5-0-thermal { + thermal-sensors = <&tsens1 10>; + + trips { + cpu-0-5-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-5-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-5-1-thermal { + thermal-sensors = <&tsens1 11>; + + trips { + cpu-0-5-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-5-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpullc-1-0-thermal { + thermal-sensors = <&tsens2 0>; + + trips { + cpullc-1-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpullc-1-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpullc-1-1-thermal { + thermal-sensors = <&tsens2 1>; + + trips { + cpullc-1-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpullc-1-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + qmx-1-0-thermal { + thermal-sensors = <&tsens2 2>; + + trips { + qmx-1-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + qmx-1-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + qmx-1-1-thermal { + thermal-sensors = <&tsens2 3>; + + trips { + qmx-1-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + qmx-1-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + qmx-1-2-thermal { + thermal-sensors = <&tsens2 4>; + + trips { + qmx-1-2-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + qmx-1-2-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + qmx-1-3-thermal { + thermal-sensors = <&tsens2 5>; + + trips { + qmx-1-3-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + qmx-1-3-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + qmx-1-4-thermal { + thermal-sensors = <&tsens2 6>; + + trips { + qmx-1-4-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + qmx-1-4-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-1-0-0-thermal { + thermal-sensors = <&tsens3 0>; + + trips { + cpu-1-0-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-1-0-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-1-0-1-thermal { + thermal-sensors = <&tsens3 1>; + + trips { + cpu-1-0-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-1-0-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-1-1-0-thermal { + thermal-sensors = <&tsens3 2>; + + trips { + cpu-1-1-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-1-1-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-1-1-1-thermal { + thermal-sensors = <&tsens3 3>; + + trips { + cpu-1-1-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-1-1-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphvx-0-thermal { + thermal-sensors = <&tsens4 0>; + + trips { + nsphvx-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphvx-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphvx-1-thermal { + thermal-sensors = <&tsens4 1>; + + trips { + nsphvx-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphvx-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphvx-2-thermal { + thermal-sensors = <&tsens4 2>; + + trips { + nsphvx-2-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphvx-2-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphvx-3-thermal { + thermal-sensors = <&tsens4 3>; + + trips { + nsphvx-3-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphvx-3-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphmx-0-thermal { + thermal-sensors = <&tsens4 4>; + + trips { + nsphmx-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphmx-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphmx-1-thermal { + thermal-sensors = <&tsens4 5>; + + trips { + nsphmx-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphmx-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphmx-2-thermal { + thermal-sensors = <&tsens4 6>; + + trips { + nsphmx-2-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphmx-2-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphmx-3-thermal { + thermal-sensors = <&tsens4 7>; + + trips { + nsphmx-3-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphmx-3-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss-0-thermal { + thermal-sensors = <&tsens5 0>; + + trips { + gpuss-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss-1-thermal { + thermal-sensors = <&tsens5 1>; + + trips { + gpuss-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss-2-thermal { + thermal-sensors = <&tsens5 2>; + + trips { + gpuss-2-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-2-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss-3-thermal { + thermal-sensors = <&tsens5 3>; + + trips { + gpuss-3-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-3-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss-4-thermal { + thermal-sensors = <&tsens5 4>; + + trips { + gpuss-4-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-4-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss-5-thermal { + thermal-sensors = <&tsens5 5>; + + trips { + gpuss-5-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-5-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss-6-thermal { + thermal-sensors = <&tsens5 6>; + + trips { + gpuss-6-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-6-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss-7-thermal { + thermal-sensors = <&tsens5 7>; + + trips { + gpuss-7-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-7-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss-8-thermal { + thermal-sensors = <&tsens5 8>; + + trips { + gpuss-8-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-8-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss-9-thermal { + thermal-sensors = <&tsens5 9>; + + trips { + gpuss-9-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-9-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss-10-thermal { + thermal-sensors = <&tsens5 10>; + + trips { + gpuss-10-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-10-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + ddr-thermal { + thermal-sensors = <&tsens5 11>; + + trips { + ddr-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + ddr-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + mdmss-0-thermal { + thermal-sensors = <&tsens6 0>; + + trips { + mdmss-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + mdmss-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + mdmss-1-thermal { + thermal-sensors = <&tsens6 1>; + trips { + mdmss-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + mdmss-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + mdmss-2-thermal { + thermal-sensors = <&tsens6 2>; + + trips { + mdmss-2-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + mdmss-2-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + mdmss-3-thermal { + thermal-sensors = <&tsens6 3>; + + trips { + mdmss-3-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + mdmss-3-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + camera-0-thermal { + thermal-sensors = <&tsens6 4>; + + trips { + camera-0-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + camera-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + camera-1-thermal { + thermal-sensors = <&tsens6 5>; + + trips { + camera-1-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + camera-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + video-thermal { + thermal-sensors = <&tsens6 6>; + + trips { + video-hot { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + video-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; + timer { compatible = "arm,armv8-timer"; @@ -1603,4 +6955,56 @@ , ; }; + + tpdm-cdsp-llm { + compatible = "qcom,coresight-static-tpdm"; + qcom,cmb-element-bits = <32>; + + out-ports { + port { + tpdm_cdsp_llm_out: endpoint { + remote-endpoint = <&tpda_cdsp_in1>; + }; + }; + }; + }; + + tpdm-cdsp-llm2 { + compatible = "qcom,coresight-static-tpdm"; + qcom,cmb-element-bits = <32>; + + out-ports { + port { + tpdm_cdsp_llm2_out: endpoint { + remote-endpoint = <&tpda_cdsp_in2>; + }; + }; + }; + }; + + tpdm-modem1 { + compatible = "qcom,coresight-static-tpdm"; + qcom,cmb-element-bits = <32>; + + out-ports { + port { + tpdm_modem1_out: endpoint { + remote-endpoint = <&tpda_modem_in1>; + }; + }; + }; + }; + + tpdm-modem2 { + compatible = "qcom,coresight-static-tpdm"; + qcom,cmb-element-bits = <64>; + + out-ports { + port { + tpdm_modem2_out: endpoint { + remote-endpoint = <&tpda_modem_in2>; + }; + }; + }; + }; }; diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi index 6079e67ea829..988ca5f7c8a0 100644 --- a/arch/arm64/boot/dts/qcom/kodiak.dtsi +++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi @@ -2445,7 +2445,7 @@ reg = <0 0x01c0e000 0 0x1000>; clocks = <&gcc GCC_PCIE_1_AUX_CLK>, <&gcc GCC_PCIE_1_CFG_AHB_CLK>, - <&gcc GCC_PCIE_CLKREF_EN>, + <&rpmhcc RPMH_CXO_CLK>, <&gcc GCC_PCIE1_PHY_RCHNG_CLK>, <&gcc GCC_PCIE_1_PIPE_CLK>; clock-names = "aux", @@ -2642,6 +2642,8 @@ qcom,smem-state-names = "ipa-clock-enabled-valid", "ipa-clock-enabled"; + sram = <&ipa_modem_tables>; + status = "disabled"; }; @@ -3036,6 +3038,110 @@ bias-pull-down; }; + lpass_i2s1_active: i2s1-active-state { + clk-pins { + pins = "gpio6"; + function = "i2s1_clk"; + drive-strength = <8>; + bias-disable; + output-high; + }; + + ws-pins { + pins = "gpio7"; + function = "i2s1_ws"; + drive-strength = <8>; + bias-disable; + output-high; + }; + + data-pins { + pins = "gpio8", "gpio9"; + function = "i2s1_data"; + drive-strength = <8>; + bias-disable; + output-high; + }; + }; + + lpass_i2s1_sleep: i2s1-sleep-state { + clk-pins { + pins = "gpio6"; + function = "i2s1_clk"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + + ws-pins { + pins = "gpio7"; + function = "i2s1_ws"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + + data-pins { + pins = "gpio8", "gpio9"; + function = "i2s1_data"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + + lpass_i2s2_active: i2s2-active-state { + clk-pins { + pins = "gpio10"; + function = "i2s2_clk"; + drive-strength = <8>; + bias-disable; + output-high; + }; + + ws-pins { + pins = "gpio11"; + function = "i2s2_ws"; + drive-strength = <8>; + bias-disable; + output-high; + }; + + data-pins { + pins = "gpio12", "gpio13"; + function = "i2s2_data"; + drive-strength = <8>; + bias-disable; + output-high; + }; + }; + + lpass_i2s2_sleep: i2s2-sleep-state { + clk-pins { + pins = "gpio10"; + function = "i2s2_clk"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + + ws-pins { + pins = "gpio11"; + function = "i2s2_ws"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + + data-pins { + pins = "gpio12", "gpio13"; + function = "i2s2_data"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + lpass_rx_swr_clk: rx-swr-clk-state { pins = "gpio3"; function = "swr_rx_clk"; @@ -4849,6 +4955,7 @@ phys = <&usb_1_hsphy>, <&usb_1_qmpphy QMP_USB43DP_USB3_PHY>; phy-names = "usb2-phy", "usb3-phy"; maximum-speed = "super-speed"; + usb-role-switch; ports { #address-cells = <1>; @@ -5505,8 +5612,8 @@ edp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5604,8 +5711,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5721,6 +5828,13 @@ gpio-ranges = <&tlmm 0 0 175>; wakeup-parent = <&pdc>; + cam_mclk3_default: cam-mclk3-default-state { + pins = "gpio67"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + cci0_default: cci0-default-state { pins = "gpio69", "gpio70"; function = "cci_i2c"; @@ -6577,6 +6691,10 @@ ranges = <0 0 0x146a5000 0x6000>; + ipa_modem_tables: modem-tables@3000 { + reg = <0x3000 0x2000>; + }; + pil-reloc@594c { compatible = "qcom,pil-reloc-info"; reg = <0x594c 0xc8>; diff --git a/arch/arm64/boot/dts/qcom/lemans-el2.dtso b/arch/arm64/boot/dts/qcom/lemans-el2.dtso index ed615dce6c78..621ad930cf54 100644 --- a/arch/arm64/boot/dts/qcom/lemans-el2.dtso +++ b/arch/arm64/boot/dts/qcom/lemans-el2.dtso @@ -10,6 +10,10 @@ /dts-v1/; /plugin/; +&gpu_zap_shader { + status = "disabled"; +}; + &iris { status = "disabled"; }; diff --git a/arch/arm64/boot/dts/qcom/lemans-evk-ifp-mezzanine.dtso b/arch/arm64/boot/dts/qcom/lemans-evk-ifp-mezzanine.dtso new file mode 100644 index 000000000000..268fc6b05d4b --- /dev/null +++ b/arch/arm64/boot/dts/qcom/lemans-evk-ifp-mezzanine.dtso @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; +/plugin/; + +#include + +&{/} { + model = "Qualcomm Technologies, Inc. Lemans-evk IFP Mezzanine"; + + vreg_0p9: regulator-0v9 { + compatible = "regulator-fixed"; + regulator-name = "VREG_0P9"; + + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + regulator-always-on; + regulator-boot-on; + }; + + vreg_1p8: regulator-1v8 { + compatible = "regulator-fixed"; + regulator-name = "VREG_1P8"; + + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; +}; + +ðernet1 { + phy-handle = <&hsgmii_phy1>; + phy-mode = "2500base-x"; + + pinctrl-0 = <ðernet1_default>; + pinctrl-names = "default"; + + snps,mtl-rx-config = <&mtl_rx_setup1>; + snps,mtl-tx-config = <&mtl_tx_setup1>; + + nvmem-cells = <&mac_addr1>; + nvmem-cell-names = "mac-address"; + + status = "okay"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + hsgmii_phy1: ethernet-phy@18 { + compatible = "ethernet-phy-id004d.d101"; + reg = <0x18>; + reset-gpios = <&pmm8654au_2_gpios 9 GPIO_ACTIVE_LOW>; + reset-assert-us = <11000>; + reset-deassert-us = <70000>; + }; + }; + + mtl_rx_setup1: rx-queues-config { + snps,rx-queues-to-use = <4>; + snps,rx-sched-sp; + + queue0 { + snps,dcb-algorithm; + snps,map-to-dma-channel = <0x0>; + snps,route-up; + snps,priority = <0x1>; + }; + + queue1 { + snps,dcb-algorithm; + snps,map-to-dma-channel = <0x1>; + snps,route-ptp; + }; + + queue2 { + snps,avb-algorithm; + snps,map-to-dma-channel = <0x2>; + snps,route-avcp; + }; + + queue3 { + snps,avb-algorithm; + snps,map-to-dma-channel = <0x3>; + snps,priority = <0xc>; + }; + }; + + mtl_tx_setup1: tx-queues-config { + snps,tx-queues-to-use = <4>; + + queue0 { + snps,dcb-algorithm; + }; + + queue1 { + snps,dcb-algorithm; + }; + + queue2 { + snps,avb-algorithm; + snps,send_slope = <0x1000>; + snps,idle_slope = <0x1000>; + snps,high_credit = <0x3e800>; + snps,low_credit = <0xffc18000>; + }; + + queue3 { + snps,avb-algorithm; + snps,send_slope = <0x1000>; + snps,idle_slope = <0x1000>; + snps,high_credit = <0x3e800>; + snps,low_credit = <0xffc18000>; + }; + }; +}; + +&i2c18 { + #address-cells = <1>; + #size-cells = <0>; + + eeprom@52 { + compatible = "giantec,gt24c256c", "atmel,24c256"; + reg = <0x52>; + pagesize = <64>; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + mac_addr1: mac-addr@0 { + reg = <0x0 0x6>; + }; + }; + }; +}; + +&pcie0 { + iommu-map = <0x0 &pcie_smmu 0x0 0x1>, + <0x100 &pcie_smmu 0x1 0x1>, + <0x208 &pcie_smmu 0x2 0x1>, + <0x210 &pcie_smmu 0x3 0x1>, + <0x218 &pcie_smmu 0x4 0x1>, + <0x300 &pcie_smmu 0x5 0x1>, + <0x400 &pcie_smmu 0x6 0x1>, + <0x500 &pcie_smmu 0x7 0x1>, + <0x501 &pcie_smmu 0x8 0x1>; +}; + +&pcieport0 { + #address-cells = <3>; + #size-cells = <2>; + + pcie@0,0 { + compatible = "pci1179,0623"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x2 0xff>; + + vddc-supply = <&vreg_0p9>; + vdd18-supply = <&vreg_1p8>; + vdd09-supply = <&vreg_0p9>; + vddio1-supply = <&vreg_1p8>; + vddio2-supply = <&vreg_1p8>; + vddio18-supply = <&vreg_1p8>; + + i2c-parent = <&i2c18 0x77>; + + resx-gpios = <&tlmm 140 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&tc9563_resx_n>; + pinctrl-names = "default"; + + pcie@1,0 { + reg = <0x20800 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x3 0xff>; + }; + + pcie@2,0 { + reg = <0x21000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x4 0xff>; + }; + + pcie@3,0 { + reg = <0x21800 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + bus-range = <0x5 0xff>; + + pci@0,0 { + reg = <0x50000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + }; + + pci@0,1 { + reg = <0x50100 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + }; + }; + }; +}; + +&serdes1 { + phy-supply = <&vreg_l5a>; + + status = "okay"; +}; + +&tlmm { + ethernet1_default: ethernet1-default-state { + ethernet1-mdc-pins { + pins = "gpio20"; + function = "emac1_mdc"; + drive-strength = <16>; + bias-pull-up; + }; + + ethernet1-mdio-pins { + pins = "gpio21"; + function = "emac1_mdio"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + tc9563_resx_n: tc9563-resx-state { + pins = "gpio140"; + function = "gpio"; + bias-disable; + /* Reset pin of tc9563 is active low hence set default + * state of this pin to output-high. + */ + output-high; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/lemans-evk.dts b/arch/arm64/boot/dts/qcom/lemans-evk.dts index 90fce947ca7e..c665db6a4595 100644 --- a/arch/arm64/boot/dts/qcom/lemans-evk.dts +++ b/arch/arm64/boot/dts/qcom/lemans-evk.dts @@ -21,6 +21,7 @@ ethernet0 = ðernet0; mmc1 = &sdhc; serial0 = &uart10; + serial2 = &uart0; }; dmic: audio-codec-0 { @@ -44,7 +45,7 @@ data-role = "dual"; power-role = "dual"; - vbus-supply = <&vbus_supply_regulator_0>; + vbus-supply = <&usb0_vbus>; ports { #address-cells = <1>; @@ -68,6 +69,25 @@ }; }; + connector-2 { + compatible = "gpio-usb-b-connector", "usb-b-connector"; + label = "micro-USB"; + type = "micro"; + + id-gpios = <&pmm8654au_2_gpios 11 GPIO_ACTIVE_HIGH>; + vbus-gpios = <&expander3 3 GPIO_ACTIVE_HIGH>; + vbus-supply = <&usb2_vbus>; + + pinctrl-0 = <&usb2_id>; + pinctrl-names = "default"; + + port { + usb2_con_hs_ep: endpoint { + remote-endpoint = <&usb_2_dwc3_hs>; + }; + }; + }; + edp0-connector { compatible = "dp-connector"; label = "EDP0"; @@ -132,15 +152,24 @@ }; }; - vbus_supply_regulator_0: regulator-vbus-supply-0 { + usb0_vbus: regulator-usb0-vbus { compatible = "regulator-fixed"; - regulator-name = "vbus_supply_0"; + regulator-name = "usb0_vbus"; gpio = <&expander1 2 GPIO_ACTIVE_HIGH>; regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; enable-active-high; }; + usb2_vbus: regulator-usb2-vbus { + compatible = "regulator-fixed"; + regulator-name = "usb2_vbus"; + gpio = <&pmm8654au_1_gpios 9 GPIO_ACTIVE_HIGH>; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + }; + vmmc_sdc: regulator-vmmc-sdc { compatible = "regulator-fixed"; @@ -546,6 +575,11 @@ reg = <0x38>; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&tlmm 138 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&expander0_int>; + pinctrl-names = "default"; }; expander1: gpio@39 { @@ -553,6 +587,11 @@ reg = <0x39>; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&tlmm 19 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&expander1_int>; + pinctrl-names = "default"; }; expander2: gpio@3a { @@ -560,6 +599,11 @@ reg = <0x3a>; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&tlmm 139 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&expander2_int>; + pinctrl-names = "default"; }; expander3: gpio@3b { @@ -567,6 +611,11 @@ reg = <0x3b>; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&tlmm 39 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&expander3_int>; + pinctrl-names = "default"; }; eeprom@50 { @@ -699,6 +748,14 @@ bias-pull-up; power-source = <0>; }; + + usb2_id: usb2-id-state { + pins = "gpio11"; + function = "normal"; + input-enable; + bias-pull-up; + power-source = <0>; + }; }; &qup_i2c19_default { @@ -804,6 +861,30 @@ }; }; + expander0_int: expander0-int-state { + pins = "gpio138"; + function = "gpio"; + bias-pull-up; + }; + + expander1_int: expander1-int-state { + pins = "gpio19"; + function = "gpio"; + bias-pull-up; + }; + + expander2_int: expander2-int-state { + pins = "gpio139"; + function = "gpio"; + bias-pull-up; + }; + + expander3_int: expander3-int-state { + pins = "gpio39"; + function = "gpio"; + bias-pull-up; + }; + pcie0_default_state: pcie0-default-state { clkreq-pins { pins = "gpio1"; @@ -870,6 +951,10 @@ }; }; +&uart0 { + status = "okay"; +}; + &uart10 { compatible = "qcom,geni-debug-uart"; pinctrl-0 = <&qup_uart10_default>; @@ -922,6 +1007,22 @@ status = "okay"; }; +&usb_2 { + status = "okay"; +}; + +&usb_2_dwc3_hs { + remote-endpoint = <&usb2_con_hs_ep>; +}; + +&usb_2_hsphy { + vdda-pll-supply = <&vreg_l7a>; + vdda18-supply = <&vreg_l6c>; + vdda33-supply = <&vreg_l9a>; + + status = "okay"; +}; + &xo_board_clk { clock-frequency = <38400000>; }; diff --git a/arch/arm64/boot/dts/qcom/lemans-ride-common.dtsi b/arch/arm64/boot/dts/qcom/lemans-ride-common.dtsi index 8fb7d1fc6d56..31bd00546d55 100644 --- a/arch/arm64/boot/dts/qcom/lemans-ride-common.dtsi +++ b/arch/arm64/boot/dts/qcom/lemans-ride-common.dtsi @@ -21,28 +21,6 @@ stdout-path = "serial0:115200n8"; }; - vreg_12p0: vreg-12p0-regulator { - compatible = "regulator-fixed"; - regulator-name = "VREG_12P0"; - - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <12000000>; - regulator-max-microvolt = <12000000>; - }; - - vreg_5p0: vreg-5p0-regulator { - compatible = "regulator-fixed"; - regulator-name = "VREG_5P0"; - - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - - vin-supply = <&vreg_12p0>; - }; - vreg_1p8: vreg-1p8-regulator { compatible = "regulator-fixed"; regulator-name = "VREG_1P8"; @@ -51,8 +29,6 @@ regulator-boot-on; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; - - vin-supply = <&vreg_5p0>; }; vreg_1p0: vreg-1p0-regulator { @@ -75,8 +51,6 @@ regulator-boot-on; regulator-min-microvolt = <3000000>; regulator-max-microvolt = <3000000>; - - vin-supply = <&vreg_12p0>; }; vreg_conn_1p8: vreg_conn_1p8 { diff --git a/arch/arm64/boot/dts/qcom/lemans.dtsi b/arch/arm64/boot/dts/qcom/lemans.dtsi index 808827b83553..fe6e76351823 100644 --- a/arch/arm64/boot/dts/qcom/lemans.dtsi +++ b/arch/arm64/boot/dts/qcom/lemans.dtsi @@ -1512,7 +1512,7 @@ reg = <0x0 0x898000 0x0 0x4000>; #address-cells = <1>; #size-cells = <0>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP2_S6_CLK>; clock-names = "se"; pinctrl-0 = <&qup_i2c20_default>; @@ -1539,7 +1539,7 @@ reg = <0x0 0x898000 0x0 0x4000>; #address-cells = <1>; #size-cells = <0>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP2_S6_CLK>; clock-names = "se"; pinctrl-0 = <&qup_spi20_default>; @@ -1564,7 +1564,7 @@ uart20: serial@898000 { compatible = "qcom,geni-uart"; reg = <0x0 0x00898000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP2_S6_CLK>; clock-names = "se"; pinctrl-0 = <&qup_uart20_default>; @@ -2510,7 +2510,7 @@ reg = <0x0 0xa98000 0x0 0x4000>; #address-cells = <1>; #size-cells = <0>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP1_S6_CLK>; clock-names = "se"; pinctrl-0 = <&qup_i2c13_default>; @@ -4270,7 +4270,14 @@ snps,dis-u1-entry-quirk; snps,dis-u2-entry-quirk; + usb-role-switch; + status = "disabled"; + + port { + usb_2_dwc3_hs: endpoint { + }; + }; }; tcsr_mutex: hwlock@1f40000 { @@ -4625,19 +4632,19 @@ opp-444000000 { opp-hz = /bits/ 64 <444000000>; - required-opps = <&rpmhpd_opp_nom>, + required-opps = <&rpmhpd_opp_svs_l1>, <&rpmhpd_opp_nom>; }; opp-533000000 { opp-hz = /bits/ 64 <533000000>; - required-opps = <&rpmhpd_opp_turbo>, + required-opps = <&rpmhpd_opp_nom>, <&rpmhpd_opp_turbo>; }; opp-560000000 { opp-hz = /bits/ 64 <560000000>; - required-opps = <&rpmhpd_opp_turbo_l1>, + required-opps = <&rpmhpd_opp_nom>, <&rpmhpd_opp_turbo_l1>; }; }; @@ -5404,8 +5411,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5492,8 +5499,8 @@ dp1_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -8575,10 +8582,10 @@ arch_timer: timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; turing-llm-tpdm { diff --git a/arch/arm64/boot/dts/qcom/mahua-crd.dts b/arch/arm64/boot/dts/qcom/mahua-crd.dts new file mode 100644 index 000000000000..9c8244e892dd --- /dev/null +++ b/arch/arm64/boot/dts/qcom/mahua-crd.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; + +#include "mahua.dtsi" +#include "glymur-crd.dtsi" + +/delete-node/ &pmcx0102_d_e0; +/delete-node/ &pmcx0102_d0_thermal; +/delete-node/ &pmh0104_i_e0; +/delete-node/ &pmh0104_i0_thermal; +/delete-node/ &pmh0104_j_e0; +/delete-node/ &pmh0104_j0_thermal; + +/ { + model = "Qualcomm Technologies, Inc. Mahua CRD"; + compatible = "qcom,mahua-crd", "qcom,mahua"; +}; diff --git a/arch/arm64/boot/dts/qcom/mahua.dtsi b/arch/arm64/boot/dts/qcom/mahua.dtsi new file mode 100644 index 000000000000..990a02c6afc1 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/mahua.dtsi @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/* Mahua is heavily based on Glymur, with some meaningful differences */ +#include "glymur.dtsi" + +/delete-node/ &bwmon_cluster2; +/delete-node/ &cluster2_pd; +/delete-node/ &cpu_map_cluster2; +/delete-node/ &cpu12; +/delete-node/ &cpu13; +/delete-node/ &cpu14; +/delete-node/ &cpu15; +/delete-node/ &cpu16; +/delete-node/ &cpu17; +/delete-node/ &cpu_pd12; +/delete-node/ &cpu_pd13; +/delete-node/ &cpu_pd14; +/delete-node/ &cpu_pd15; +/delete-node/ &cpu_pd16; +/delete-node/ &cpu_pd17; +/delete-node/ &thermal_aoss_6; +/delete-node/ &thermal_aoss_7; +/delete-node/ &thermal_cpu_2_0_0; +/delete-node/ &thermal_cpu_2_0_1; +/delete-node/ &thermal_cpu_2_1_0; +/delete-node/ &thermal_cpu_2_1_1; +/delete-node/ &thermal_cpu_2_2_0; +/delete-node/ &thermal_cpu_2_2_1; +/delete-node/ &thermal_cpu_2_3_0; +/delete-node/ &thermal_cpu_2_3_1; +/delete-node/ &thermal_cpu_2_4_0; +/delete-node/ &thermal_cpu_2_4_1; +/delete-node/ &thermal_cpu_2_5_0; +/delete-node/ &thermal_cpu_2_5_1; +/delete-node/ &thermal_cpuillc_2_1; +/delete-node/ &thermal_cpullc_2_0; +/delete-node/ &thermal_ddr_2; +/delete-node/ &thermal_gpu_3_0; +/delete-node/ &thermal_gpu_3_1; +/delete-node/ &thermal_gpu_3_2; +/delete-node/ &thermal_qmx_2_0; +/delete-node/ &thermal_qmx_2_1; +/delete-node/ &thermal_qmx_2_2; +/delete-node/ &thermal_qmx_2_3; +/delete-node/ &thermal_qmx_2_4; +/delete-node/ &thermal_video_1; +/delete-node/ &tsens6; +/delete-node/ &tsens7; + +&aggre1_noc { + compatible = "qcom,mahua-aggre1-noc", "qcom,glymur-aggre1-noc"; +}; + +&aggre2_noc { + compatible = "qcom,mahua-aggre2-noc", "qcom,glymur-aggre2-noc"; +}; + +&aggre3_noc { + compatible = "qcom,mahua-aggre3-noc", "qcom,glymur-aggre3-noc"; +}; + +&aggre4_noc { + compatible = "qcom,mahua-aggre4-noc", "qcom,glymur-aggre4-noc"; +}; + +&clk_virt { + compatible = "qcom,mahua-clk-virt", "qcom,glymur-clk-virt"; +}; + +&cnoc_main { + compatible = "qcom,mahua-cnoc-main", "qcom,glymur-cnoc-main"; +}; + +&config_noc { + compatible = "qcom,mahua-cnoc-cfg"; +}; + +&hsc_noc { + compatible = "qcom,mahua-hscnoc"; +}; + +&lpass_ag_noc { + compatible = "qcom,mahua-lpass-ag-noc", "qcom,glymur-lpass-ag-noc"; +}; + +&lpass_lpiaon_noc { + compatible = "qcom,mahua-lpass-lpiaon-noc", "qcom,glymur-lpass-lpiaon-noc"; +}; + +&lpass_lpicx_noc { + compatible = "qcom,mahua-lpass-lpicx-noc", "qcom,glymur-lpass-lpicx-noc"; +}; + +&mc_virt { + compatible = "qcom,mahua-mc-virt"; +}; + +&mmss_noc { + compatible = "qcom,mahua-mmss-noc", "qcom,glymur-mmss-noc"; +}; + +&nsi_noc { + compatible = "qcom,mahua-nsinoc", "qcom,glymur-nsinoc"; +}; + +&nsp_noc { + compatible = "qcom,mahua-nsp-noc", "qcom,glymur-nsp-noc"; +}; + +&oobm_ss_noc { + compatible = "qcom,mahua-oobm-ss-noc", "qcom,glymur-oobm-ss-noc"; +}; + +&pcie_east_anoc { + compatible = "qcom,mahua-pcie-east-anoc", "qcom,glymur-pcie-east-anoc"; +}; + +&pcie_east_slv_noc { + compatible = "qcom,mahua-pcie-east-slv-noc", "qcom,glymur-pcie-east-slv-noc"; +}; + +&pcie_west_anoc { + compatible = "qcom,mahua-pcie-west-anoc"; + clocks = <&gcc GCC_AGGRE_NOC_PCIE_3B_WEST_SF_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_PCIE_4_WEST_SF_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_PCIE_6_WEST_SF_AXI_CLK>; +}; + +&pcie_west_slv_noc { + compatible = "qcom,mahua-pcie-west-slv-noc"; +}; + +&system_noc { + compatible = "qcom,mahua-system-noc", "qcom,glymur-system-noc"; +}; + +&thermal_camera_0 { + thermal-sensors = <&tsens4 9>; +}; + +&thermal_camera_1 { + thermal-sensors = <&tsens4 10>; +}; + +&thermal_ddr_1 { + thermal-sensors = <&tsens1 7>; +}; + +&thermal_gpu_0_0 { + thermal-sensors = <&tsens5 1>; +}; + +&thermal_gpu_0_1 { + thermal-sensors = <&tsens5 2>; +}; + +&thermal_gpu_0_2 { + thermal-sensors = <&tsens5 3>; +}; + +&thermal_gpu_1_0 { + thermal-sensors = <&tsens5 4>; +}; + +&thermal_gpu_1_1 { + thermal-sensors = <&tsens5 5>; +}; + +&thermal_gpu_1_2 { + thermal-sensors = <&tsens5 6>; +}; + +&thermal_gpu_2_0 { + thermal-sensors = <&tsens5 7>; +}; + +&thermal_gpu_2_1 { + thermal-sensors = <&tsens5 8>; +}; + +&thermal_gpu_2_2 { + thermal-sensors = <&tsens5 9>; +}; + +&thermal_gpuss_0 { + thermal-sensors = <&tsens5 10>; +}; + +&thermal_gpuss_1 { + thermal-sensors = <&tsens5 11>; +}; + +&thermal_nsphmx_0 { + thermal-sensors = <&tsens4 5>; +}; + +&thermal_nsphmx_1 { + thermal-sensors = <&tsens4 6>; +}; + +&thermal_nsphmx_2 { + thermal-sensors = <&tsens4 7>; +}; + +&thermal_nsphmx_3 { + thermal-sensors = <&tsens4 8>; +}; + +&thermal_nsphvx_0 { + thermal-sensors = <&tsens4 1>; +}; + +&thermal_nsphvx_1 { + thermal-sensors = <&tsens4 2>; +}; + +&thermal_nsphvx_2 { + thermal-sensors = <&tsens4 3>; +}; + +&thermal_nsphvx_3 { + thermal-sensors = <&tsens4 4>; +}; + +&thermal_video_0 { + thermal-sensors = <&tsens1 8>; +}; + +&thermal_zones { + gpuss-2-thermal { + thermal-sensors = <&tsens5 12>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-2-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + gpuss-3-thermal { + thermal-sensors = <&tsens5 13>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-3-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + gpuss-4-thermal { + thermal-sensors = <&tsens5 14>; + + trips { + trip-point0 { + temperature = <90000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss-4-critical { + temperature = <115000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; +}; + +&tlmm { + compatible = "qcom,mahua-tlmm"; +}; + +&tsens4 { + #qcom,sensors = <11>; +}; + +&tsens5 { + #qcom,sensors = <15>; +}; + diff --git a/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts b/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts index 52895dd9e4fa..c1899db46e71 100644 --- a/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts +++ b/arch/arm64/boot/dts/qcom/milos-fairphone-fp6.dts @@ -29,9 +29,19 @@ gpio-keys { compatible = "gpio-keys"; - pinctrl-0 = <&volume_up_default>; + pinctrl-0 = <&volume_up_default>, <&hall_sensor_default>; pinctrl-names = "default"; + /* Powered by the always-on vreg_l10b */ + event-hall-sensor { + label = "Hall Effect Sensor"; + gpios = <&tlmm 70 GPIO_ACTIVE_LOW>; + linux,input-type = ; + linux,code = ; + linux,can-disable; + wakeup-source; + }; + key-volume-up { label = "Volume Up"; gpios = <&pm7550_gpios 6 GPIO_ACTIVE_LOW>; @@ -316,6 +326,8 @@ regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-initial-mode = ; + /* Hall sensor VDD */ + regulator-always-on; }; vreg_l11b: ldo11 { @@ -529,6 +541,56 @@ }; }; +&cci0 { + status = "okay"; +}; + +&cci0_i2c0 { + /* Main cam: Sony IMX896 @ 0x1a */ + + eeprom@50 { + compatible = "puya,p24c128f", "atmel,24c128"; + reg = <0x50>; + vcc-supply = <&vreg_l6p>; + read-only; + }; + + /* Dongwoon DW9784 VCM/OIS @ 0x72 */ +}; + + +&cci0_i2c1 { + /* Awinic AW86017 VCM @ 0x0c */ + /* UW cam: OmniVision OV13B10 @ 0x36 */ + + eeprom@52 { + compatible = "puya,p24c128f", "atmel,24c128"; + reg = <0x52>; + vcc-supply = <&vreg_l6p>; + read-only; + }; +}; + +&cci1 { + /* cci1_i2c0 is not used for CCI */ + pinctrl-0 = <&cci1_1_default>; + pinctrl-1 = <&cci1_1_sleep>; + + status = "okay"; +}; + +&cci1_i2c1 { + /* Awinic AW86016 VCM @ 0x0c */ + /* Front cam: Samsung S5KKD1 @ 0x3d */ + + eeprom@51 { + compatible = "puya,p24c128f", "atmel,24c128"; + reg = <0x51>; + vcc-supply = <&vreg_l6p>; + read-only; + }; +}; + &gcc { protected-clocks = , , , , @@ -755,6 +817,13 @@ bias-pull-up; }; + hall_sensor_default: hall-sensor-default-state { + pins = "gpio70"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + pm8008_int_default: pm8008-int-default-state { pins = "gpio125"; function = "gpio"; @@ -767,6 +836,24 @@ status = "okay"; }; +&ufs_mem_hc { + reset-gpios = <&tlmm 167 GPIO_ACTIVE_LOW>; + + vcc-supply = <&vreg_l12b>; + vcc-max-microamp = <800000>; + vccq-supply = <&vreg_l5f>; + vccq-max-microamp = <750000>; + + status = "okay"; +}; + +&ufs_mem_phy { + vdda-phy-supply = <&vreg_l2b>; + vdda-pll-supply = <&vreg_l4b>; + + status = "okay"; +}; + &usb_1 { dr_mode = "otg"; diff --git a/arch/arm64/boot/dts/qcom/milos.dtsi b/arch/arm64/boot/dts/qcom/milos.dtsi index e1a51d43943f..4a64a98a434b 100644 --- a/arch/arm64/boot/dts/qcom/milos.dtsi +++ b/arch/arm64/boot/dts/qcom/milos.dtsi @@ -18,7 +18,9 @@ #include #include #include +#include #include +#include / { interrupt-parent = <&intc>; @@ -797,11 +799,13 @@ <&sleep_clk>, <0>, /* pcie_0_pipe_clk */ <0>, /* pcie_1_pipe_clk */ - <0>, /* ufs_phy_rx_symbol_0_clk */ - <0>, /* ufs_phy_rx_symbol_1_clk */ - <0>, /* ufs_phy_tx_symbol_0_clk */ + <&ufs_mem_phy 0>, + <&ufs_mem_phy 1>, + <&ufs_mem_phy 2>, <0>; /* usb3_phy_wrapper_gcc_usb30_pipe_clk */ + power-domains = <&rpmhpd RPMHPD_CX>; + #clock-cells = <1>; #reset-cells = <1>; #power-domain-cells = <1>; @@ -1151,6 +1155,129 @@ qcom,bcm-voters = <&apps_bcm_voter>; }; + ufs_mem_phy: phy@1d80000 { + compatible = "qcom,milos-qmp-ufs-phy"; + reg = <0x0 0x01d80000 0x0 0x2000>; + + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_UFS_PHY_PHY_AUX_CLK>, + <&tcsr TCSR_UFS_CLKREF_EN>; + clock-names = "ref", + "ref_aux", + "qref"; + + resets = <&ufs_mem_hc 0>; + reset-names = "ufsphy"; + + power-domains = <&gcc UFS_MEM_PHY_GDSC>; + + #clock-cells = <1>; + #phy-cells = <0>; + + status = "disabled"; + }; + + ufs_mem_hc: ufshc@1d84000 { + compatible = "qcom,milos-ufshc", "qcom,ufshc", "jedec,ufs-2.0"; + reg = <0x0 0x01d84000 0x0 0x3000>; + + interrupts = ; + + clocks = <&gcc GCC_UFS_PHY_AXI_CLK>, + <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>, + <&gcc GCC_UFS_PHY_UNIPRO_CORE_CLK>, + <&tcsr TCSR_UFS_PAD_CLKREF_EN>, + <&gcc GCC_UFS_PHY_TX_SYMBOL_0_CLK>, + <&gcc GCC_UFS_PHY_RX_SYMBOL_0_CLK>, + <&gcc GCC_UFS_PHY_RX_SYMBOL_1_CLK>; + clock-names = "core_clk", + "bus_aggr_clk", + "iface_clk", + "core_clk_unipro", + "ref_clk", + "tx_lane0_sync_clk", + "rx_lane0_sync_clk", + "rx_lane1_sync_clk"; + + resets = <&gcc GCC_UFS_PHY_BCR>; + reset-names = "rst"; + + interconnects = <&aggre1_noc MASTER_UFS_MEM QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &cnoc_cfg SLAVE_UFS_MEM_CFG QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "ufs-ddr", + "cpu-ufs"; + + power-domains = <&gcc UFS_PHY_GDSC>; + required-opps = <&rpmhpd_opp_nom>; + + operating-points-v2 = <&ufs_opp_table>; + + iommus = <&apps_smmu 0x60 0>; + + dma-coherent; + + lanes-per-direction = <2>; + qcom,ice = <&ice>; + + phys = <&ufs_mem_phy>; + phy-names = "ufsphy"; + + #reset-cells = <1>; + + status = "disabled"; + + ufs_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-75000000 { + opp-hz = /bits/ 64 <75000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <75000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-150000000 { + opp-hz = /bits/ 64 <150000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <150000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <300000000>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>, + /bits/ 64 <0>; + required-opps = <&rpmhpd_opp_nom>; + }; + }; + }; + + ice: crypto@1d88000 { + compatible = "qcom,milos-inline-crypto-engine", + "qcom,inline-crypto-engine"; + reg = <0x0 0x01d88000 0x0 0x18000>; + + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + }; + tcsr_mutex: hwlock@1f40000 { compatible = "qcom,tcsr-mutex"; reg = <0x0 0x01f40000 0x0 0x20000>; @@ -1214,6 +1341,197 @@ label = "lpass"; qcom,remote-pid = <2>; + + fastrpc { + compatible = "qcom,fastrpc"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + label = "adsp"; + qcom,non-secure-domain; + #address-cells = <1>; + #size-cells = <0>; + + compute-cb@3 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <3>; + iommus = <&apps_smmu 0x1003 0x0>, + <&apps_smmu 0x1063 0x0>; + dma-coherent; + }; + + compute-cb@4 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <4>; + iommus = <&apps_smmu 0x1004 0x0>, + <&apps_smmu 0x1064 0x0>; + dma-coherent; + }; + + compute-cb@5 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <5>; + iommus = <&apps_smmu 0x1005 0x0>, + <&apps_smmu 0x1065 0x0>; + dma-coherent; + }; + + compute-cb@6 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <6>; + iommus = <&apps_smmu 0x1006 0x0>, + <&apps_smmu 0x1066 0x0>; + dma-coherent; + }; + + compute-cb@7 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <7>; + iommus = <&apps_smmu 0x1007 0x0>, + <&apps_smmu 0x1067 0x0>; + dma-coherent; + }; + }; + + gpr { + compatible = "qcom,gpr"; + qcom,glink-channels = "adsp_apps"; + qcom,domain = ; + qcom,intents = <512 20>; + #address-cells = <1>; + #size-cells = <0>; + + q6apm: service@1 { + compatible = "qcom,q6apm"; + reg = ; + #sound-dai-cells = <0>; + qcom,protection-domain = "avs/audio", + "msm/adsp/audio_pd"; + + q6apmbedai: bedais { + compatible = "qcom,q6apm-lpass-dais"; + #sound-dai-cells = <1>; + }; + + q6apmdai: dais { + compatible = "qcom,q6apm-dais"; + iommus = <&apps_smmu 0x1001 0x0>, + <&apps_smmu 0x1061 0x0>; + }; + }; + + q6prm: service@2 { + compatible = "qcom,q6prm"; + reg = ; + qcom,protection-domain = "avs/audio", + "msm/adsp/audio_pd"; + + q6prmcc: clock-controller { + compatible = "qcom,q6prm-lpass-clocks"; + #clock-cells = <2>; + }; + }; + }; + }; + }; + + lpass_tlmm: pinctrl@3440000 { + compatible = "qcom,milos-lpass-lpi-pinctrl"; + reg = <0x0 0x03440000 0x0 0x20000>, + <0x0 0x034d0000 0x0 0x10000>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&lpass_tlmm 0 0 23>; + + clocks = <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "core", + "audio"; + + tx_swr_active: tx-swr-active-state { + clk-pins { + pins = "gpio0"; + function = "swr_tx_clk"; + drive-strength = <4>; + slew-rate = <1>; + bias-disable; + }; + + data-pins { + pins = "gpio1", "gpio2", "gpio14"; + function = "swr_tx_data"; + drive-strength = <4>; + slew-rate = <1>; + bias-bus-hold; + }; + }; + + rx_swr_active: rx-swr-active-state { + clk-pins { + pins = "gpio3"; + function = "swr_rx_clk"; + drive-strength = <2>; + slew-rate = <1>; + bias-disable; + }; + + data-pins { + pins = "gpio4", "gpio5"; + function = "swr_rx_data"; + drive-strength = <2>; + slew-rate = <1>; + bias-bus-hold; + }; + }; + + lpi_i2s2_active: lpi-i2s2-active-state { + clk-pins { + pins = "gpio10"; + function = "i2s2_clk"; + drive-strength = <8>; + bias-disable; + output-high; + }; + + ws-pins { + pins = "gpio11"; + function = "i2s2_ws"; + drive-strength = <8>; + bias-disable; + output-high; + }; + + data-pins { + pins = "gpio12", "gpio13"; + function = "i2s2_data"; + drive-strength = <8>; + bias-disable; + output-high; + }; + }; + + lpi_i2s2_sleep: lpi-i2s2-sleep-state { + clk-pins { + pins = "gpio10"; + function = "i2s2_clk"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + + ws-pins { + pins = "gpio11"; + function = "i2s2_ws"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + + data-pins { + pins = "gpio12", "gpio13"; + function = "i2s2_data"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; }; }; @@ -1531,6 +1849,72 @@ #power-domain-cells = <1>; }; + cci0: cci@ac15000 { + compatible = "qcom,milos-cci", "qcom,msm8996-cci"; + reg = <0x0 0x0ac15000 0x0 0x1000>; + interrupts = ; + power-domains = <&camcc CAM_CC_CAMSS_TOP_GDSC>; + clocks = <&camcc CAM_CC_SOC_AHB_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CCI_0_CLK>; + clock-names = "soc_ahb", + "cpas_ahb", + "cci"; + pinctrl-0 = <&cci0_0_default &cci0_1_default>; + pinctrl-1 = <&cci0_0_sleep &cci0_1_sleep>; + pinctrl-names = "default", "sleep"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + + cci0_i2c0: i2c-bus@0 { + reg = <0>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + + cci0_i2c1: i2c-bus@1 { + reg = <1>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + cci1: cci@ac16000 { + compatible = "qcom,milos-cci", "qcom,msm8996-cci"; + reg = <0x0 0x0ac16000 0x0 0x1000>; + interrupts = ; + power-domains = <&camcc CAM_CC_CAMSS_TOP_GDSC>; + clocks = <&camcc CAM_CC_SOC_AHB_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CCI_1_CLK>; + clock-names = "soc_ahb", + "cpas_ahb", + "cci"; + pinctrl-0 = <&cci1_0_default &cci1_1_default>; + pinctrl-1 = <&cci1_0_sleep &cci1_1_sleep>; + pinctrl-names = "default", "sleep"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + + cci1_i2c0: i2c-bus@0 { + reg = <0>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + + cci1_i2c1: i2c-bus@1 { + reg = <1>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + camcc: clock-controller@adb0000 { compatible = "qcom,milos-camcc"; reg = <0x0 0x0adb0000 0x0 0x40000>; @@ -1667,6 +2051,21 @@ wakeup-parent = <&pdc>; + qup_spi0_data_clk: qup-spi0-data-clk-state { + /* MISO, MOSI, CLK */ + pins = "gpio0", "gpio1", "gpio2"; + function = "qup0_se0"; + drive-strength = <6>; + bias-disable; + }; + + qup_spi0_cs: qup-spi0-cs-state { + pins = "gpio3"; + function = "qup0_se0"; + drive-strength = <6>; + bias-disable; + }; + qup_i2c1_data_clk: qup-i2c1-data-clk-state { /* SDA, SCL */ pins = "gpio4", "gpio5"; @@ -1683,29 +2082,6 @@ bias-pull-up = <2200>; }; - qup_i2c7_data_clk: qup-i2c7-data-clk-state { - /* SDA, SCL */ - pins = "gpio32", "gpio33"; - function = "qup1_se0"; - drive-strength = <2>; - bias-pull-up; - }; - - qup_spi0_cs: qup-spi0-cs-state { - pins = "gpio3"; - function = "qup0_se0"; - drive-strength = <6>; - bias-disable; - }; - - qup_spi0_data_clk: qup-spi0-data-clk-state { - /* MISO, MOSI, CLK */ - pins = "gpio0", "gpio1", "gpio2"; - function = "qup0_se0"; - drive-strength = <6>; - bias-disable; - }; - qup_uart5_default: qup-uart5-default-state { /* TX, RX */ pins = "gpio25", "gpio26"; @@ -1714,10 +2090,10 @@ bias-disable; }; - qup_uart11_default: qup-uart11-default-state { - /* TX, RX */ - pins = "gpio50", "gpio51"; - function = "qup1_se4"; + qup_i2c7_data_clk: qup-i2c7-data-clk-state { + /* SDA, SCL */ + pins = "gpio32", "gpio33"; + function = "qup1_se0"; drive-strength = <2>; bias-pull-up; }; @@ -1730,6 +2106,14 @@ bias-pull-down; }; + qup_uart11_default: qup-uart11-default-state { + /* TX, RX */ + pins = "gpio50", "gpio51"; + function = "qup1_se4"; + drive-strength = <2>; + bias-pull-up; + }; + sdc2_default: sdc2-default-state { clk-pins { pins = "gpio62"; @@ -1775,6 +2159,134 @@ bias-pull-up; }; }; + + cci0_0_default: cci0-0-default-state { + sda-pins { + pins = "gpio88"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + scl-pins { + pins = "gpio89"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + }; + + cci0_0_sleep: cci0-0-sleep-state { + sda-pins { + pins = "gpio88"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-down; + }; + + scl-pins { + pins = "gpio89"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + cci0_1_default: cci0-1-default-state { + sda-pins { + pins = "gpio90"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + scl-pins { + pins = "gpio91"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + }; + + cci0_1_sleep: cci0-1-sleep-state { + sda-pins { + pins = "gpio90"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-down; + }; + + scl-pins { + pins = "gpio91"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + cci1_0_default: cci1-0-default-state { + sda-pins { + pins = "gpio92"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + scl-pins { + pins = "gpio93"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + }; + + cci1_0_sleep: cci1-0-sleep-state { + sda-pins { + pins = "gpio92"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-down; + }; + + scl-pins { + pins = "gpio93"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + cci1_1_default: cci1-1-default-state { + sda-pins { + pins = "gpio94"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + scl-pins { + pins = "gpio95"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + }; + + cci1_1_sleep: cci1-1-sleep-state { + sda-pins { + pins = "gpio94"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-down; + }; + + scl-pins { + pins = "gpio95"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-down; + }; + }; }; apps_smmu: iommu@15000000 { @@ -1911,7 +2423,7 @@ gic_its: msi-controller@17140000 { compatible = "arm,gic-v3-its"; - reg = <0x0 0x17140000 0x0 0x20000>; + reg = <0x0 0x17140000 0x0 0x40000>; msi-controller; #msi-cells = <1>; @@ -2164,6 +2676,101 @@ label = "cdsp"; qcom,remote-pid = <5>; + + fastrpc { + compatible = "qcom,fastrpc"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + label = "cdsp"; + qcom,non-secure-domain; + #address-cells = <1>; + #size-cells = <0>; + + compute-cb@1 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <1>; + iommus = <&apps_smmu 0x0c01 0x0>; + dma-coherent; + }; + + compute-cb@2 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <2>; + iommus = <&apps_smmu 0x0c02 0x0>; + dma-coherent; + }; + + compute-cb@3 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <3>; + iommus = <&apps_smmu 0x0c03 0x0>; + dma-coherent; + }; + + compute-cb@4 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <4>; + iommus = <&apps_smmu 0x0c04 0x0>; + dma-coherent; + }; + + compute-cb@5 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <5>; + iommus = <&apps_smmu 0x0c05 0x0>; + dma-coherent; + }; + + compute-cb@6 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <6>; + iommus = <&apps_smmu 0x0c06 0x0>; + dma-coherent; + }; + + compute-cb@7 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <7>; + iommus = <&apps_smmu 0x0c07 0x0>; + dma-coherent; + }; + + compute-cb@8 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <8>; + iommus = <&apps_smmu 0x0c08 0x0>; + dma-coherent; + }; + + /* note: secure cb9 in downstream */ + + compute-cb@12 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <12>; + iommus = <&apps_smmu 0x0c0c 0x0>; + dma-coherent; + }; + + compute-cb@13 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <13>; + iommus = <&apps_smmu 0x0c0d 0x0>; + dma-coherent; + }; + + compute-cb@14 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <14>; + iommus = <&apps_smmu 0x0c0e 0x0>; + dma-coherent; + }; + + compute-cb@15 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <15>; + iommus = <&apps_smmu 0x0c0f 0x0>; + dma-coherent; + }; + }; }; }; }; diff --git a/arch/arm64/boot/dts/qcom/monaco-arduino-monza.dts b/arch/arm64/boot/dts/qcom/monaco-arduino-monza.dts new file mode 100644 index 000000000000..ca14f0ea4dae --- /dev/null +++ b/arch/arm64/boot/dts/qcom/monaco-arduino-monza.dts @@ -0,0 +1,466 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; + +#include +#include +#include + +#include "monaco.dtsi" +#include "monaco-pmics.dtsi" +#include "monaco-monza-som.dtsi" + +/ { + model = "Arduino VENTUNO Q"; + compatible = "arduino,monza", "qcom,qcs8300"; + + aliases { + ethernet0 = ðernet0; + i2c1 = &i2c1; + serial0 = &uart7; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&gpio_keys_default>; + pinctrl-names = "default"; + + button-home { + label = "Home Key"; + linux,code = ; + gpios = <&tlmm 79 GPIO_ACTIVE_LOW>; + debounce-interval = <60>; + }; + }; + + hdmi-connector { + compatible = "hdmi-connector"; + label = "hdmi"; + type = "a"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&adv7535_out>; + }; + }; + }; + + sound { + compatible = "qcom,qcs8275-sndcard"; + model = "arduino-monza"; + audio-routing = "IN12", "Headset Mic12", + "Headset Mic12", "MICBIAS", + "IN56", "Headset Mic56", + "Headset Mic56", "MICBIAS", + "MIC1", "MICBIAS", + "Headphone", "HPL", + "Headphone", "HPR", + "Receiver", "RCVL", + "Receiver", "RCVR", + "Speaker", "SPKL", + "Speaker", "SPKR"; + + pinctrl-0 = <&quad_mi2s_active>, <&quad_mclk_active>, <&lpi_i2s4_active>; + pinctrl-names = "default"; + + pri-i2s-playback-dai-link { + link-name = "Analog Playback"; + + codec { + sound-dai = <&max98091>; + }; + + cpu { + sound-dai = <&q6apmbedai 137>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + pri-i2s-capture-dai-link { + link-name = "Analog Capture"; + + codec { + sound-dai = <&max98091>; + }; + + cpu { + sound-dai = <&q6apmbedai 138>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + hdmi-mi2s-playback-dai-link { + link-name = "HDMI Playback"; + + codec { + sound-dai = <&adv7535>; + }; + + cpu { + sound-dai = <&q6apmbedai 145>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + }; + + vdc_3v3: regulator-3v3 { + compatible = "regulator-fixed"; + regulator-name = "vdc_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + vdc_1v8: regulator-1v8 { + compatible = "regulator-fixed"; + regulator-name = "vdc_1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + vdc_5v: regulator-5v0 { + compatible = "regulator-fixed"; + regulator-name = "vdc_5v"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&tlmm 49 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-always-on; + startup-delay-us = <20000>; + }; + + vreg_nvme: regulator-3p3-m2 { + compatible = "regulator-fixed"; + regulator-name = "vreg_m2_3p3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&tlmm 51 GPIO_ACTIVE_HIGH>; + enable-active-high; + startup-delay-us = <20000>; + }; +}; + +ðernet0 { + phy-mode = "2500base-x"; + phy-handle = <&hsgmii_phy0>; + + pinctrl-0 = <ðernet0_default>; + pinctrl-names = "default"; + + snps,mtl-rx-config = <&mtl_rx_setup>; + snps,mtl-tx-config = <&mtl_tx_setup>; + + status = "okay"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + hsgmii_phy0: ethernet-phy@1c { + compatible = "ethernet-phy-id004d.d101"; + reg = <0x1c>; + reset-gpios = <&tlmm 50 GPIO_ACTIVE_LOW>; + reset-assert-us = <11000>; + reset-deassert-us = <70000>; + }; + }; + + mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <4>; + snps,rx-sched-sp; + + queue0 { + snps,dcb-algorithm; + snps,map-to-dma-channel = <0x0>; + snps,route-up; + snps,priority = <0x1>; + }; + + queue1 { + snps,dcb-algorithm; + snps,map-to-dma-channel = <0x1>; + snps,route-ptp; + }; + + queue2 { + snps,avb-algorithm; + snps,map-to-dma-channel = <0x2>; + snps,route-avcp; + }; + + queue3 { + snps,avb-algorithm; + snps,map-to-dma-channel = <0x3>; + snps,priority = <0xc>; + }; + }; + + mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <4>; + + queue0 { + snps,dcb-algorithm; + }; + + queue1 { + snps,dcb-algorithm; + }; + + queue2 { + snps,avb-algorithm; + snps,send_slope = <0x1000>; + snps,idle_slope = <0x1000>; + snps,high_credit = <0x3e800>; + snps,low_credit = <0xffc18000>; + }; + + queue3 { + snps,avb-algorithm; + snps,send_slope = <0x1000>; + snps,idle_slope = <0x1000>; + snps,high_credit = <0x3e800>; + snps,low_credit = <0xffc18000>; + }; + }; +}; + +&i2c12 { + clock-frequency = <400000>; + + status = "okay"; + + max98091: audio-codec@10 { + compatible = "maxim,max98091"; + reg = <0x10>; + pinctrl-0 = <&max98091_default>; + pinctrl-names = "default"; + interrupts-extended = <&tlmm 16 IRQ_TYPE_LEVEL_HIGH>; /* GPIO_16 */ + clocks = <&q6prmcc LPASS_CLK_ID_MCLK_3 LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "mclk"; + #sound-dai-cells = <0>; + }; + + adv7535: bridge@3d { + compatible = "adi,adv7535"; + reg = <0x3d>; + pinctrl-0 = <&adv7535_default>; + pinctrl-names = "default"; + interrupts-extended = <&tlmm 93 IRQ_TYPE_EDGE_FALLING>; + avdd-supply = <&vdc_1v8>; + dvdd-supply = <&vdc_1v8>; + pvdd-supply = <&vdc_1v8>; + a2vdd-supply = <&vdc_1v8>; + v3p3-supply = <&vdc_3v3>; + v1p2-supply = <&vdc_1v8>; + adi,dsi-lanes = <4>; + #sound-dai-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + adv7535_in: endpoint { + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + adv7535_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dp0 { + status = "okay"; +}; + +&mdss_dp0_out { + data-lanes = <0 1 2 3>; + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp0_phy { + status = "okay"; +}; + +&mdss_dsi0 { + status = "okay"; +}; + +&mdss_dsi0_out { + remote-endpoint = <&adv7535_in>; + data-lanes = <0 1 2 3>; +}; + +&mdss_dsi0_phy { + status = "okay"; +}; + +&pcie0 { + pinctrl-0 = <&pcie0_default_state>; + pinctrl-names = "default"; + + vddpe-3v3-supply = <&vdc_3v3>; +}; + +&pcie1 { + pinctrl-0 = <&pcie1_default_state>; + pinctrl-names = "default"; + + vddpe-3v3-supply = <&vreg_nvme>; +}; + +&pcieport0 { + reset-gpios = <&tlmm 2 GPIO_ACTIVE_LOW>; + + pci@0,0 { + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + bus-range = <0x01 0xff>; + ranges; + reg = <0x010000 0x00 0x00 0x00 0x00>; + + pci@2,0 { + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + bus-range = <0x00 0xff>; + ranges; + reg = <0x021000 0x00 0x00 0x00 0x00>; + + usb@0 { + compatible = "pci104c,8241"; + reg = <0 0 0 0 0>; + ti,pwron-active-high; + }; + }; + }; +}; + +&pcieport1 { + reset-gpios = <&tlmm 23 GPIO_ACTIVE_LOW>; +}; + +&tlmm { + pcie0_default_state: pcie0-default-state { + wake-pins { + pins = "gpio0"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + clkreq-pins { + pins = "gpio1"; + function = "pcie0_clkreq"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-pins { + pins = "gpio2"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + ethernet0_default: ethernet0-default-state { + ethernet0_mdc: ethernet0-mdc-pins { + pins = "gpio5"; + function = "emac0_mdc"; + drive-strength = <16>; + bias-pull-up; + }; + + ethernet0_mdio: ethernet0-mdio-pins { + pins = "gpio6"; + function = "emac0_mdio"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + max98091_default: max98091-default-state { + pins = "gpio16"; + function = "gpio"; + bias-pull-up; + }; + + pcie1_default_state: pcie1-default-state { + wake-pins { + pins = "gpio21"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + clkreq-pins { + pins = "gpio22"; + function = "pcie1_clkreq"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-pins { + pins = "gpio23"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + gpio_keys_default: gpio-keys-default-state { + pins = "gpio79"; + function = "gpio"; + bias-disable; + }; + + adv7535_default: adv7535-default-state { + pins = "gpio93"; + function = "gpio"; + bias-pull-up; + }; +}; + +&uart7 { + status = "okay"; +}; + +&usb_1 { + status = "okay"; +}; + +/* Internally connected to the MCU (e.g. for DFU). */ +&usb_2 { + dr_mode = "host"; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/monaco-el2.dtso b/arch/arm64/boot/dts/qcom/monaco-el2.dtso new file mode 100644 index 000000000000..a7e3270f8609 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/monaco-el2.dtso @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * Monaco specific modifications required to boot in EL2. + */ + +/dts-v1/; +/plugin/; + +&gpu_zap_shader { + status = "disabled"; +}; + +&iris { + status = "disabled"; +}; + +&remoteproc_adsp { + iommus = <&apps_smmu 0x2000 0x0>; +}; + +&remoteproc_cdsp { + iommus = <&apps_smmu 0x19c0 0x0400>; +}; + +&remoteproc_gpdsp { + iommus = <&apps_smmu 0x28a0 0x0>; +}; diff --git a/arch/arm64/boot/dts/qcom/monaco-evk-camera-imx577.dtso b/arch/arm64/boot/dts/qcom/monaco-evk-camera-imx577.dtso new file mode 100644 index 000000000000..0d5ccd020e6e --- /dev/null +++ b/arch/arm64/boot/dts/qcom/monaco-evk-camera-imx577.dtso @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&camss { + vdda-phy-supply = <&vreg_l4a>; + vdda-pll-supply = <&vreg_l5a>; + + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + + csiphy1_ep: endpoint { + data-lanes = <0 1 2 3>; + remote-endpoint = <&imx577_ep1>; + }; + }; + }; +}; + +&cci1 { + pinctrl-0 = <&cci1_0_default>; + pinctrl-1 = <&cci1_0_sleep>; + + status = "okay"; +}; + +&cci1_i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + camera@1a { + compatible = "sony,imx577"; + reg = <0x1a>; + + reset-gpios = <&expander2 1 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&cam1_default>; + pinctrl-names = "default"; + + clocks = <&camcc CAM_CC_MCLK1_CLK>; + assigned-clocks = <&camcc CAM_CC_MCLK1_CLK>; + assigned-clock-rates = <24000000>; + + avdd-supply = <&vreg_cam1_2p8>; + + port { + imx577_ep1: endpoint { + link-frequencies = /bits/ 64 <600000000>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&csiphy1_ep>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/monaco-evk-ifp-mezzanine.dtso b/arch/arm64/boot/dts/qcom/monaco-evk-ifp-mezzanine.dtso new file mode 100644 index 000000000000..e6beb4393430 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/monaco-evk-ifp-mezzanine.dtso @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; +/plugin/; + +#include + +&{/} { + model = "Qualcomm Technologies, Inc. Monaco-EVK IFP Mezzanine"; + + vreg_0p9: regulator-0v9 { + compatible = "regulator-fixed"; + regulator-name = "VREG_0P9"; + + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + regulator-always-on; + regulator-boot-on; + }; + + vreg_1p8: regulator-1v8 { + compatible = "regulator-fixed"; + regulator-name = "VREG_1P8"; + + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; +}; + +&i2c15 { + #address-cells = <1>; + #size-cells = <0>; + + eeprom1: eeprom@52 { + compatible = "giantec,gt24c256c", "atmel,24c256"; + reg = <0x52>; + pagesize = <64>; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + }; + }; +}; + +&pcie0 { + iommu-map = <0x0 &pcie_smmu 0x0 0x1>, + <0x100 &pcie_smmu 0x1 0x1>, + <0x208 &pcie_smmu 0x2 0x1>, + <0x210 &pcie_smmu 0x3 0x1>, + <0x218 &pcie_smmu 0x4 0x1>, + <0x300 &pcie_smmu 0x5 0x1>, + <0x400 &pcie_smmu 0x6 0x1>, + <0x500 &pcie_smmu 0x7 0x1>, + <0x501 &pcie_smmu 0x8 0x1>; +}; + +&pcieport0 { + #address-cells = <3>; + #size-cells = <2>; + + pcie@0,0 { + compatible = "pci1179,0623"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x2 0xff>; + + vddc-supply = <&vreg_0p9>; + vdd18-supply = <&vreg_1p8>; + vdd09-supply = <&vreg_0p9>; + vddio1-supply = <&vreg_1p8>; + vddio2-supply = <&vreg_1p8>; + vddio18-supply = <&vreg_1p8>; + + i2c-parent = <&i2c15 0x77>; + + resx-gpios = <&tlmm 124 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&tc9563_resx_n>; + pinctrl-names = "default"; + + pcie@1,0 { + reg = <0x20800 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x3 0xff>; + }; + + pcie@2,0 { + reg = <0x21000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x4 0xff>; + }; + + pcie@3,0 { + reg = <0x21800 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + bus-range = <0x5 0xff>; + + pci@0,0 { + reg = <0x50000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + }; + + pci@0,1 { + reg = <0x50100 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + }; + }; + }; +}; + +&tlmm { + tc9563_resx_n: tc9563-resx-state { + pins = "gpio124"; + function = "gpio"; + bias-disable; + /* Reset pin of tc9563 is active low hence set default + * state of this pin to output-high. + */ + output-high; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/monaco-evk.dts b/arch/arm64/boot/dts/qcom/monaco-evk.dts index 565418b86b2a..9d17ef7d2caf 100644 --- a/arch/arm64/boot/dts/qcom/monaco-evk.dts +++ b/arch/arm64/boot/dts/qcom/monaco-evk.dts @@ -21,12 +21,32 @@ ethernet0 = ðernet0; i2c1 = &i2c1; serial0 = &uart7; + serial2 = &uart6; }; chosen { stdout-path = "serial0:115200n8"; }; + connector-2 { + compatible = "gpio-usb-b-connector", "usb-b-connector"; + label = "micro-USB"; + type = "micro"; + + id-gpios = <&pmm8620au_0_gpios 9 GPIO_ACTIVE_HIGH>; + vbus-gpios = <&expander6 7 GPIO_ACTIVE_HIGH>; + vbus-supply = <&usb2_vbus>; + + pinctrl-0 = <&usb2_id>; + pinctrl-names = "default"; + + port { + usb2_con_hs_ep: endpoint { + remote-endpoint = <&usb_2_dwc3_hs>; + }; + }; + }; + dmic: audio-codec-0 { compatible = "dmic-codec"; #sound-dai-cells = <0>; @@ -38,6 +58,39 @@ #sound-dai-cells = <0>; }; + dp-connector-0 { + compatible = "dp-connector"; + label = "DP0"; + type = "mini"; + + port { + dp0_connector_in: endpoint { + remote-endpoint = <<8713sx_dp0_out>; + }; + }; + }; + + dp-connector-1 { + compatible = "dp-connector"; + label = "DP1"; + type = "mini"; + + port { + dp1_connector_in: endpoint { + remote-endpoint = <<8713sx_dp1_out>; + }; + }; + }; + + usb2_vbus: regulator-usb2-vbus { + compatible = "regulator-fixed"; + regulator-name = "usb2_vbus"; + gpio = <&pmm8650au_1_gpios 7 GPIO_ACTIVE_HIGH>; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + }; + sound { compatible = "qcom,qcs8275-sndcard"; model = "MONACO-EVK"; @@ -77,6 +130,57 @@ }; }; }; + + vreg_cam0_2p8: vreg-cam0-2p8 { + compatible = "regulator-fixed"; + regulator-name = "vreg_cam0_2p8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + startup-delay-us = <10000>; + + gpio = <&tlmm 73 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&cam0_avdd_2v8_en_default>; + pinctrl-names = "default"; + }; + + vreg_cam1_2p8: vreg-cam1-2p8 { + compatible = "regulator-fixed"; + regulator-name = "vreg_cam1_2p8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + startup-delay-us = <10000>; + + gpio = <&tlmm 74 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&cam1_avdd_2v8_en_default>; + pinctrl-names = "default"; + }; + + vreg_cam2_2p8: vreg-cam2-2p8 { + compatible = "regulator-fixed"; + regulator-name = "vreg_cam2_2p8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + startup-delay-us = <10000>; + + gpio = <&tlmm 75 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&cam2_avdd_2v8_en_default>; + pinctrl-names = "default"; + }; + + /* This comes from a PMIC handled within the SAIL domain */ + vreg_s2s: vreg-s2s { + compatible = "regulator-fixed"; + regulator-name = "vreg_s2s"; + + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; }; &apps_rsc { @@ -318,6 +422,45 @@ firmware-name = "qcom/qcs8300/a623_zap.mbn"; }; +&i2c0 { + status = "okay"; + + bridge@4f { + compatible = "lontium,lt8713sx"; + reg = <0x4f>; + reset-gpios = <&expander5 6 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + lt8713sx_dp_in: endpoint { + remote-endpoint = <&mdss_dp0_out>; + }; + }; + + port@1 { + reg = <1>; + + lt8713sx_dp0_out: endpoint { + remote-endpoint = <&dp0_connector_in>; + }; + }; + + port@2 { + reg = <2>; + + lt8713sx_dp1_out: endpoint { + remote-endpoint = <&dp1_connector_in>; + }; + }; + }; + }; +}; + &i2c1 { pinctrl-0 = <&qup_i2c1_default>; pinctrl-names = "default"; @@ -362,6 +505,11 @@ reg = <0x38>; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&tlmm 56 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&expander0_int>; + pinctrl-names = "default"; }; expander1: gpio@39 { @@ -369,6 +517,11 @@ reg = <0x39>; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&tlmm 16 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&expander1_int>; + pinctrl-names = "default"; }; expander2: gpio@3a { @@ -376,6 +529,11 @@ reg = <0x3a>; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&tlmm 95 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&expander2_int>; + pinctrl-names = "default"; }; expander3: gpio@3b { @@ -383,6 +541,11 @@ reg = <0x3b>; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&tlmm 24 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&expander3_int>; + pinctrl-names = "default"; }; expander4: gpio@3c { @@ -390,6 +553,11 @@ reg = <0x3c>; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&tlmm 96 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&expander4_int>; + pinctrl-names = "default"; }; expander5: gpio@3d { @@ -397,6 +565,11 @@ reg = <0x3d>; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&expander5_int>; + pinctrl-names = "default"; }; expander6: gpio@3e { @@ -404,6 +577,11 @@ reg = <0x3e>; #gpio-cells = <2>; gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&tlmm 52 IRQ_TYPE_LEVEL_LOW>; + pinctrl-0 = <&expander6_int>; + pinctrl-names = "default"; }; }; @@ -411,6 +589,30 @@ status = "okay"; }; +&mdss { + status = "okay"; +}; + +&mdss_dp0 { + pinctrl-0 = <&dp_hot_plug_det>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&mdss_dp0_out { + data-lanes = <0 1 2 3>; + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; + remote-endpoint = <<8713sx_dp_in>; +}; + +&mdss_dp0_phy { + vdda-phy-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l4a>; + + status = "okay"; +}; + &pcie0 { pinctrl-0 = <&pcie0_default_state>; pinctrl-names = "default"; @@ -449,6 +651,21 @@ wake-gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; }; +&pmm8620au_0_gpios { + usb2_id: usb2-id-state { + pins = "gpio9"; + function = "normal"; + input-enable; + bias-pull-up; + power-source = <0>; + }; +}; + +&qup_i2c0_data_clk { + drive-strength = <2>; + bias-pull-up; +}; + &qupv3_id_0 { firmware-name = "qcom/qcs8300/qupv3fw.elf"; status = "okay"; @@ -477,6 +694,17 @@ status = "okay"; }; +&sdhc_1 { + vmmc-supply = <&vreg_l8a>; + vqmmc-supply = <&vreg_s2s>; + + no-sd; + no-sdio; + non-removable; + + status = "okay"; +}; + &serdes0 { phy-supply = <&vreg_l4a>; @@ -494,7 +722,6 @@ }; &tlmm { - pcie0_default_state: pcie0-default-state { wake-pins { pins = "gpio0"; @@ -534,6 +761,18 @@ }; }; + expander5_int: expander5-int-state { + pins = "gpio3"; + function = "gpio"; + bias-pull-up; + }; + + expander1_int: expander1-int-state { + pins = "gpio16"; + function = "gpio"; + bias-pull-up; + }; + qup_i2c1_default: qup-i2c1-state { pins = "gpio19", "gpio20"; function = "qup0_se1"; @@ -564,12 +803,67 @@ }; }; + expander3_int: expander3-int-state { + pins = "gpio24"; + function = "gpio"; + bias-pull-up; + }; + + expander6_int: expander6-int-state { + pins = "gpio52"; + function = "gpio"; + bias-pull-up; + }; + + expander0_int: expander0-int-state { + pins = "gpio56"; + function = "gpio"; + bias-pull-up; + }; + + cam0_avdd_2v8_en_default: cam0-avdd-2v8-en-state { + pins = "gpio73"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + cam1_avdd_2v8_en_default: cam1-avdd-2v8-en-state { + pins = "gpio74"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + cam2_avdd_2v8_en_default: cam2-avdd-2v8-en-state { + pins = "gpio75"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + qup_i2c15_default: qup-i2c15-state { pins = "gpio91", "gpio92"; function = "qup1_se7"; drive-strength = <2>; bias-pull-up; }; + + expander2_int: expander2-int-state { + pins = "gpio95"; + function = "gpio"; + bias-pull-up; + }; + + expander4_int: expander4-int-state { + pins = "gpio96"; + function = "gpio"; + bias-pull-up; + }; +}; + +&uart6 { + status = "okay"; }; &uart7 { @@ -613,3 +907,19 @@ status = "okay"; }; + +&usb_2 { + status = "okay"; +}; + +&usb_2_dwc3_hs { + remote-endpoint = <&usb2_con_hs_ep>; +}; + +&usb_2_hsphy { + vdda-pll-supply = <&vreg_l7a>; + vdda18-supply = <&vreg_l7c>; + vdda33-supply = <&vreg_l9a>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/monaco-monza-som.dtsi b/arch/arm64/boot/dts/qcom/monaco-monza-som.dtsi new file mode 100644 index 000000000000..9b5ed55939b8 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/monaco-monza-som.dtsi @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include + +#include "monaco.dtsi" +#include "monaco-pmics.dtsi" + +/ { + /* This comes from a PMIC handled within the SAIL domain */ + vreg_s2s: vreg-s2s { + compatible = "regulator-fixed"; + regulator-name = "vreg_s2s"; + + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; +}; + +&apps_rsc { + regulators-0 { + compatible = "qcom,pmm8654au-rpmh-regulators"; + qcom,pmic-id = "a"; + + vreg_l3a: ldo3 { + regulator-name = "vreg_l3a"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + regulator-always-on; + }; + + vreg_l4a: ldo4 { + regulator-name = "vreg_l4a"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l5a: ldo5 { + regulator-name = "vreg_l5a"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l6a: ldo6 { + regulator-name = "vreg_l6a"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l7a: ldo7 { + regulator-name = "vreg_l7a"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l8a: ldo8 { + regulator-name = "vreg_l8a"; + regulator-min-microvolt = <2504000>; + regulator-max-microvolt = <2960000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l9a: ldo9 { + regulator-name = "vreg_l9a"; + regulator-min-microvolt = <2970000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + }; + + regulators-1 { + compatible = "qcom,pmm8654au-rpmh-regulators"; + qcom,pmic-id = "c"; + + vreg_s5c: smps5 { /* LPDDR VDD2H */ + regulator-name = "vreg_s5c"; + regulator-min-microvolt = <1104000>; + regulator-max-microvolt = <1104000>; + regulator-initial-mode = ; + }; + + vreg_l1c: ldo1 { /* LPDDR VDDQ */ + regulator-name = "vreg_l1c"; + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <512000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l2c: ldo2 { /* LPDDR VDD2L */ + regulator-name = "vreg_l2c"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <904000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l4c: ldo4 { + regulator-name = "vreg_l4c"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l7c: ldo7 { + regulator-name = "vreg_l7c"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l8c: ldo8 { /* LPDDR VDD1 */ + regulator-name = "vreg_l8c"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l9c: ldo9 { /* QFPROM */ + regulator-name = "vreg_l9c"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + }; +}; + +&mdss_dp0 { + pinctrl-0 = <&dp_hpd>; + pinctrl-names = "default"; +}; + +&mdss_dp0_phy { + vdda-phy-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l4a>; +}; + +&mdss_dsi0 { + vdda-supply = <&vreg_l5a>; +}; + +&mdss_dsi0_phy { + vdds-supply = <&vreg_l4a>; +}; + +&gpi_dma0 { + status = "okay"; +}; + +&gpi_dma1 { + status = "okay"; +}; + +&gpu { + status = "okay"; +}; + +&gpu_zap_shader { + firmware-name = "qcom/qcs8300/a623_zap.mbn"; +}; + +&iris { + status = "okay"; +}; + +/* PCIe0 Gen4 x2 */ +&pcie0 { + iommu-map = <0x0 &pcie_smmu 0x0000 0x1>, + <0x100 &pcie_smmu 0x0001 0x1>, + <0x200 &pcie_smmu 0x0007 0x1>, + <0x208 &pcie_smmu 0x0002 0x1>, + <0x210 &pcie_smmu 0x0003 0x1>, + <0x218 &pcie_smmu 0x0004 0x1>, + <0x300 &pcie_smmu 0x0005 0x1>, + <0x400 &pcie_smmu 0x0006 0x1>; + + status = "okay"; +}; + +&pcie0_phy { + vdda-phy-supply = <&vreg_l6a>; + vdda-pll-supply = <&vreg_l5a>; + + status = "okay"; +}; + +/* PCIe1 Gen4 x4 */ +&pcie1 { + status = "okay"; +}; + +&pcie1_phy { + vdda-phy-supply = <&vreg_l6a>; + vdda-pll-supply = <&vreg_l5a>; + + status = "okay"; +}; + +&qupv3_id_0 { + firmware-name = "qcom/qcs8300/qupv3fw.elf"; + + status = "okay"; +}; + +&qupv3_id_1 { + firmware-name = "qcom/qcs8300/qupv3fw.elf"; + status = "okay"; +}; + +/* There is a HW/FW issue preventing proper REFGEN hardware voting + * for the USB2 HS PHY. As a workaround, we force REFGEN to stay + * always‑on in software, matching initial bootloader config. + */ +&refgen { + regulator-always-on; +}; + +&remoteproc_adsp { + firmware-name = "qcom/qcs8300/adsp.mbn"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/qcs8300/cdsp0.mbn"; + + status = "okay"; +}; + +&remoteproc_gpdsp { + firmware-name = "qcom/qcs8300/gpdsp0.mbn"; + + status = "okay"; +}; + +/* OnSom eMMC */ +&sdhc_1 { + vmmc-supply = <&vreg_l8a>; + vqmmc-supply = <&vreg_s2s>; + + bus-width = <8>; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + + no-sd; + no-sdio; + non-removable; + + status = "okay"; +}; + +/* Ethernet/SGMII */ +&serdes0 { + phy-supply = <&vreg_l5a>; + + status = "okay"; +}; + +&tlmm { + dp_hpd: dp-hpd-state { + pins = "gpio94"; + function = "edp0_hot"; + bias-disable; + }; +}; + +/* USB0 HS + SS */ +&usb_1_hsphy { + vdda-pll-supply = <&vreg_l7a>; + vdda18-supply = <&vreg_l7c>; + vdda33-supply = <&vreg_l9a>; + + status = "okay"; +}; + +&usb_qmpphy { + vdda-phy-supply = <&vreg_l7a>; + vdda-pll-supply = <&vreg_l5a>; + + status = "okay"; +}; + +/* USB1 HS */ +&usb_2_hsphy { + vdda-pll-supply = <&vreg_l7a>; + vdda18-supply = <&vreg_l7c>; + vdda33-supply = <&vreg_l9a>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi index 0cb9fd154b68..7b1d57460f1e 100644 --- a/arch/arm64/boot/dts/qcom/monaco.dtsi +++ b/arch/arm64/boot/dts/qcom/monaco.dtsi @@ -3,6 +3,7 @@ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include / { @@ -2239,6 +2241,10 @@ reg = <0x0 0x016c0000 0x0 0x17080>; #interconnect-cells = <2>; qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_QUPV3_AXI_CLK>, + <&gcc GCC_AGGRE_USB2_PRIM_AXI_CLK>, + <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>; }; aggre2_noc: interconnect@1700000 { @@ -2246,6 +2252,7 @@ reg = <0x0 0x01700000 0x0 0x1a080>; #interconnect-cells = <2>; qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&rpmhcc RPMH_IPA_CLK>; }; pcie_anoc: interconnect@1760000 { @@ -2871,6 +2878,75 @@ }; }; + lpass_tlmm: pinctrl@3440000 { + compatible = "qcom,qcs8300-lpass-lpi-pinctrl", "qcom,sm8450-lpass-lpi-pinctrl"; + reg = <0x0 0x03440000 0x0 0x20000>, + <0x0 0x034d0000 0x0 0x10000>; + + clocks = <&q6prmcc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "core", "audio"; + + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&lpass_tlmm 0 0 23>; + + quad_mclk_active: quad-mclk-state { + clk-pins { + pins = "gpio5"; + function = "ext_mclk1_c"; + drive-strength = <8>; + bias-disable; + }; + }; + + quad_mi2s_active: quad-active-state { + data-pins { + pins = "gpio2", "gpio3"; + function = "qua_mi2s_data"; + drive-strength = <8>; + bias-disable; + }; + + sclk-pins { + pins = "gpio0"; + function = "qua_mi2s_sclk"; + drive-strength = <8>; + bias-disable; + }; + + ws-pins { + pins = "gpio1"; + function = "qua_mi2s_ws"; + drive-strength = <8>; + bias-disable; + }; + }; + + lpi_i2s4_active: lpi_i2s4-active-state { + data0-pins { + pins = "gpio17"; + function = "i2s4_data"; + drive-strength = <8>; + bias-disable; + }; + + clk-pins { + pins = "gpio12"; + function = "i2s4_clk"; + drive-strength = <8>; + bias-disable; + }; + + ws-pins { + pins = "gpio13"; + function = "i2s4_ws"; + drive-strength = <8>; + bias-disable; + }; + }; + }; + lpass_ag_noc: interconnect@3c40000 { compatible = "qcom,qcs8300-lpass-ag-noc"; reg = <0x0 0x03c40000 0x0 0x17200>; @@ -4745,11 +4821,21 @@ interconnect-names = "sdhc-ddr", "cpu-sdhc"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&sdc1_state_on>; + pinctrl-1 = <&sdc1_state_off>; + qcom,dll-config = <0x000f64ee>; qcom,ddr-config = <0x80040868>; + bus-width = <8>; supports-cqe; dma-coherent; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + status = "disabled"; sdhc1_opp_table: opp-table { @@ -5108,6 +5194,7 @@ reg = <0x0 0x9100000 0x0 0xf7080>; #interconnect-cells = <2>; qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&gcc GCC_DDRSS_GPU_AXI_CLK>; }; llcc: system-cache-controller@9200000 { @@ -5176,9 +5263,29 @@ snps,dis_u2_susphy_quirk; snps,dis_u3_susphy_quirk; + usb-role-switch; wakeup-source; status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_1_dwc3_hs: endpoint { + }; + }; + + port@1 { + reg = <1>; + + usb_1_dwc3_ss: endpoint { + }; + }; + }; }; usb_2: usb@a400000 { @@ -5237,7 +5344,14 @@ qcom,select-utmi-as-pipe-clk; wakeup-source; + usb-role-switch; + status = "disabled"; + + port { + usb_2_dwc3_hs: endpoint { + }; + }; }; iris: video-codec@aa00000 { @@ -5293,19 +5407,19 @@ opp-444000000 { opp-hz = /bits/ 64 <444000000>; - required-opps = <&rpmhpd_opp_nom>, + required-opps = <&rpmhpd_opp_svs_l1>, <&rpmhpd_opp_nom>; }; opp-533000000 { opp-hz = /bits/ 64 <533000000>; - required-opps = <&rpmhpd_opp_turbo>, + required-opps = <&rpmhpd_opp_nom>, <&rpmhpd_opp_turbo>; }; opp-560000000 { opp-hz = /bits/ 64 <560000000>; - required-opps = <&rpmhpd_opp_turbo_l1>, + required-opps = <&rpmhpd_opp_nom>, <&rpmhpd_opp_turbo_l1>; }; }; @@ -5324,6 +5438,117 @@ #power-domain-cells = <1>; }; + cci0: cci@ac13000 { + compatible = "qcom,qcs8300-cci", "qcom,msm8996-cci"; + reg = <0x0 0x0ac13000 0x0 0x1000>; + + interrupts = ; + + clocks = <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CCI_0_CLK>; + clock-names = "ahb", + "cci"; + + power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>; + + pinctrl-0 = <&cci0_0_default &cci0_1_default>; + pinctrl-1 = <&cci0_0_sleep &cci0_1_sleep>; + pinctrl-names = "default", "sleep"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + cci0_i2c0: i2c-bus@0 { + reg = <0>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + + cci0_i2c1: i2c-bus@1 { + reg = <1>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + cci1: cci@ac14000 { + compatible = "qcom,qcs8300-cci", "qcom,msm8996-cci"; + reg = <0x0 0x0ac14000 0x0 0x1000>; + + interrupts = ; + + clocks = <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CCI_1_CLK>; + clock-names = "ahb", + "cci"; + + power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>; + + pinctrl-0 = <&cci1_0_default &cci1_1_default>; + pinctrl-1 = <&cci1_0_sleep &cci1_1_sleep>; + pinctrl-names = "default", "sleep"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + cci1_i2c0: i2c-bus@0 { + reg = <0>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + + cci1_i2c1: i2c-bus@1 { + reg = <1>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + cci2: cci@ac15000 { + compatible = "qcom,qcs8300-cci", "qcom,msm8996-cci"; + reg = <0x0 0x0ac15000 0x0 0x1000>; + + interrupts = ; + + clocks = <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CCI_2_CLK>; + clock-names = "ahb", + "cci"; + + power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>; + + pinctrl-0 = <&cci2_0_default &cci2_1_default>; + pinctrl-1 = <&cci2_0_sleep &cci2_1_sleep>; + pinctrl-names = "default", "sleep"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + cci2_i2c0: i2c-bus@0 { + reg = <0>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + + cci2_i2c1: i2c-bus@1 { + reg = <1>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + camss: isp@ac78000 { compatible = "qcom,qcs8300-camss"; @@ -5578,9 +5803,19 @@ reg = <0>; dpu_intf0_out: endpoint { + remote-endpoint = <&mdss_dp0_in>; }; }; + + port@1 { + reg = <1>; + + dpu_intf1_out: endpoint { + + remote-endpoint = <&mdss_dsi0_in>; + }; + }; }; mdp_opp_table: opp-table { @@ -5608,6 +5843,98 @@ }; }; + mdss_dsi0: dsi@ae94000 { + compatible = "qcom,qcs8300-dsi-ctrl", + "qcom,sa8775p-dsi-ctrl", + "qcom,mdss-dsi-ctrl"; + reg = <0x0 0x0ae94000 0x0 0x400>; + reg-names = "dsi_ctrl"; + + interrupt-parent = <&mdss>; + interrupts = <4>; + + clocks = <&dispcc MDSS_DISP_CC_MDSS_BYTE0_CLK>, + <&dispcc MDSS_DISP_CC_MDSS_BYTE0_INTF_CLK>, + <&dispcc MDSS_DISP_CC_MDSS_PCLK0_CLK>, + <&dispcc MDSS_DISP_CC_MDSS_ESC0_CLK>, + <&dispcc MDSS_DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus"; + + assigned-clocks = <&dispcc MDSS_DISP_CC_MDSS_BYTE0_CLK_SRC>, + <&dispcc MDSS_DISP_CC_MDSS_PCLK0_CLK_SRC>; + assigned-clock-parents = <&mdss_dsi0_phy DSI_BYTE_PLL_CLK>, + <&mdss_dsi0_phy DSI_PIXEL_PLL_CLK>; + + phys = <&mdss_dsi0_phy>; + + operating-points-v2 = <&mdss_dsi_opp_table>; + power-domains = <&rpmhpd RPMHPD_MMCX>; + + refgen-supply = <&refgen>; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss_dsi0_in: endpoint { + + remote-endpoint = <&dpu_intf1_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dsi0_out: endpoint { + }; + }; + }; + + mdss_dsi_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-358000000 { + opp-hz = /bits/ 64 <358000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + }; + }; + + mdss_dsi0_phy: phy@ae94400 { + compatible = "qcom,qcs8300-dsi-phy-5nm", + "qcom,sa8775p-dsi-phy-5nm"; + reg = <0x0 0x0ae94400 0x0 0x200>, + <0x0 0x0ae94600 0x0 0x280>, + <0x0 0x0ae94900 0x0 0x280>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&dispcc MDSS_DISP_CC_MDSS_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", + "ref"; + + status = "disabled"; + }; + mdss_dp0_phy: phy@aec2a00 { compatible = "qcom,qcs8300-edp-phy", "qcom,sa8775p-edp-phy"; @@ -5702,8 +6029,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5735,7 +6062,9 @@ <&mdss_dp0_phy 0>, <&mdss_dp0_phy 1>, <0>, <0>, - <0>, <0>, <0>, <0>; + <&mdss_dsi0_phy DSI_BYTE_PLL_CLK>, + <&mdss_dsi0_phy DSI_PIXEL_PLL_CLK>, + <0>, <0>; power-domains = <&rpmhpd RPMHPD_MMCX>; #clock-cells = <1>; #reset-cells = <1>; @@ -5881,6 +6210,225 @@ #interrupt-cells = <2>; wakeup-parent = <&pdc>; + cam0_default: cam0-default-state { + pins = "gpio67"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + cam1_default: cam1-default-state { + pins = "gpio68"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + cam2_default: cam2-default-state { + pins = "gpio69"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + cci0_0_default: cci0-0-default-state { + sda-pins { + pins = "gpio57"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + scl-pins { + pins = "gpio58"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + }; + + cci0_0_sleep: cci0-0-sleep-state { + sda-pins { + pins = "gpio57"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-down; + }; + + scl-pins { + pins = "gpio58"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + cci0_1_default: cci0-1-default-state { + sda-pins { + pins = "gpio29"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + scl-pins { + pins = "gpio30"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + }; + + cci0_1_sleep: cci0-1-sleep-state { + sda-pins { + pins = "gpio29"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-down; + }; + + scl-pins { + pins = "gpio30"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + cci1_0_default: cci1-0-default-state { + sda-pins { + pins = "gpio59"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + scl-pins { + pins = "gpio60"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + }; + + cci1_0_sleep: cci1-0-sleep-state { + sda-pins { + pins = "gpio59"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-down; + }; + + scl-pins { + pins = "gpio60"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + cci1_1_default: cci1-1-default-state { + sda-pins { + pins = "gpio31"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + scl-pins { + pins = "gpio32"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + }; + + cci1_1_sleep: cci1-1-sleep-state { + sda-pins { + pins = "gpio31"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-down; + }; + + scl-pins { + pins = "gpio32"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + cci2_0_default: cci2-0-default-state { + sda-pins { + pins = "gpio61"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + scl-pins { + pins = "gpio62"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + }; + + cci2_0_sleep: cci2-0-sleep-state { + sda-pins { + pins = "gpio61"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-down; + }; + + scl-pins { + pins = "gpio62"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + cci2_1_default: cci2-1-default-state { + sda-pins { + pins = "gpio54"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + + scl-pins { + pins = "gpio55"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-up = <2200>; + }; + }; + + cci2_1_sleep: cci2-1-sleep-state { + sda-pins { + pins = "gpio54"; + function = "cci_i2c_sda"; + drive-strength = <2>; + bias-pull-down; + }; + + scl-pins { + pins = "gpio55"; + function = "cci_i2c_scl"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + dp_hot_plug_det: dp-hot-plug-det-state { + pins = "gpio94"; + function = "edp0_hot"; + bias-disable; + }; + hs0_mi2s_active: hs0-mi2s-active-state { pins = "gpio106", "gpio107", "gpio108", "gpio109"; function = "hs0_mi2s"; @@ -7197,6 +7745,55 @@ <&apps_smmu 0x1964 0x0400>; dma-coherent; }; + + compute-cb@5 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <5>; + iommus = <&apps_smmu 0x19c5 0x0400>; + dma-coherent; + }; + + compute-cb@6 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <6>; + iommus = <&apps_smmu 0x19c6 0x0400>; + dma-coherent; + }; + + compute-cb@7 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <7>; + iommus = <&apps_smmu 0x19c7 0x0400>; + dma-coherent; + }; + + compute-cb@8 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <8>; + iommus = <&apps_smmu 0x19c8 0x0400>; + dma-coherent; + }; + + compute-cb@9 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <9>; + iommus = <&apps_smmu 0x19c9 0x0400>; + dma-coherent; + }; + + compute-cb@11 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <0xb>; + iommus = <&apps_smmu 0x19cb 0x0400>; + dma-coherent; + }; + + compute-cb@12 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <0xc>; + iommus = <&apps_smmu 0x19cc 0x000>; + dma-coherent; + }; }; }; }; @@ -7712,9 +8309,9 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; }; diff --git a/arch/arm64/boot/dts/qcom/msm8216-samsung-fortuna3g.dts b/arch/arm64/boot/dts/qcom/msm8216-samsung-fortuna3g.dts index fba68bf8bf79..aed90c8089aa 100644 --- a/arch/arm64/boot/dts/qcom/msm8216-samsung-fortuna3g.dts +++ b/arch/arm64/boot/dts/qcom/msm8216-samsung-fortuna3g.dts @@ -16,6 +16,15 @@ constant-charge-voltage-max-microvolt = <4350000>; }; +&charger { + richtek,usb-connector = <&usb_con_sm5502>; + status = "okay"; +}; + +&muic_sm5502 { + status = "okay"; +}; + &st_accel { status = "okay"; }; @@ -23,3 +32,12 @@ &st_magn { status = "okay"; }; + +&usb { + extcon = <&muic_sm5502>, <&muic_sm5502>; + status = "okay"; +}; + +&usb_hs_phy { + extcon = <&muic_sm5502>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8916-samsung-coreprimeltevzw.dts b/arch/arm64/boot/dts/qcom/msm8916-samsung-coreprimeltevzw.dts new file mode 100644 index 000000000000..40415b5635ef --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm8916-samsung-coreprimeltevzw.dts @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/dts-v1/; + +#include "msm8916-samsung-rossa-common.dtsi" + +/ { + model = "Samsung Galaxy Core Prime LTE Verizon Wireless"; + compatible = "samsung,coreprimeltevzw", "qcom,msm8916"; + chassis-type = "handset"; +}; + +&battery { + charge-term-current-microamp = <150000>; + constant-charge-current-max-microamp = <700000>; + constant-charge-voltage-max-microvolt = <4400000>; +}; + +&charger { + richtek,usb-connector = <&usb_con_sm5502>; + status = "okay"; +}; + +&mpss_mem { + /* Firmware for coreprimeltevzw needs more space */ + reg = <0x0 0x86800000 0x0 0x5400000>; +}; + +&muic_sm5502 { + status = "okay"; +}; + +&s3fwrn5_nfc { + status = "disabled"; +}; + +&usb { + extcon = <&muic_sm5502>, <&muic_sm5502>; + status = "okay"; +}; + +&usb_hs_phy { + extcon = <&muic_sm5502>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8916-samsung-fortuna-common.dtsi b/arch/arm64/boot/dts/qcom/msm8916-samsung-fortuna-common.dtsi index fb790b02736a..fd62e82075c4 100644 --- a/arch/arm64/boot/dts/qcom/msm8916-samsung-fortuna-common.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916-samsung-fortuna-common.dtsi @@ -144,14 +144,31 @@ &blsp_i2c1 { status = "okay"; - muic: extcon@25 { + /* MUIC/extcon varies depending on model variant */ + muic_sm5504: extcon@14 { + compatible = "siliconmitus,sm5504-muic"; + reg = <0x14>; + interrupts-extended = <&tlmm 12 IRQ_TYPE_EDGE_FALLING>; + pinctrl-0 = <&muic_int_default>; + pinctrl-names = "default"; + status = "disabled"; + + usb_con_sm5504: connector { + compatible = "usb-b-connector"; + label = "micro-USB"; + type = "micro"; + }; + }; + + muic_sm5502: extcon@25 { compatible = "siliconmitus,sm5502-muic"; reg = <0x25>; interrupts-extended = <&tlmm 12 IRQ_TYPE_EDGE_FALLING>; pinctrl-0 = <&muic_int_default>; pinctrl-names = "default"; + status = "disabled"; - usb_con: connector { + usb_con_sm5502: connector { compatible = "usb-b-connector"; label = "micro-USB"; type = "micro"; @@ -298,7 +315,7 @@ charger: charger { compatible = "richtek,rt5033-charger"; monitored-battery = <&battery>; - richtek,usb-connector = <&usb_con>; + status = "disabled"; }; }; }; @@ -348,15 +365,6 @@ "AMIC3", "MIC BIAS External1"; }; -&usb { - extcon = <&muic>, <&muic>; - status = "okay"; -}; - -&usb_hs_phy { - extcon = <&muic>; -}; - &venus { status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/msm8916-samsung-gprimeltecan.dts b/arch/arm64/boot/dts/qcom/msm8916-samsung-gprimeltecan.dts index 677e4e286ac0..15dcfe8234d9 100644 --- a/arch/arm64/boot/dts/qcom/msm8916-samsung-gprimeltecan.dts +++ b/arch/arm64/boot/dts/qcom/msm8916-samsung-gprimeltecan.dts @@ -86,7 +86,7 @@ charger: charger { compatible = "richtek,rt5033-charger"; monitored-battery = <&battery>; - richtek,usb-connector = <&usb_con>; + richtek,usb-connector = <&usb_con_sm5502>; }; }; }; @@ -95,3 +95,16 @@ /* Firmware for gprimeltecan needs more space */ reg = <0x0 0x86800000 0x0 0x5400000>; }; + +&muic_sm5502 { + status = "okay"; +}; + +&usb { + extcon = <&muic_sm5502>, <&muic_sm5502>; + status = "okay"; +}; + +&usb_hs_phy { + extcon = <&muic_sm5502>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8916-samsung-grandprimelte.dts b/arch/arm64/boot/dts/qcom/msm8916-samsung-grandprimelte.dts index 582bfcb09684..268277c1caf4 100644 --- a/arch/arm64/boot/dts/qcom/msm8916-samsung-grandprimelte.dts +++ b/arch/arm64/boot/dts/qcom/msm8916-samsung-grandprimelte.dts @@ -24,7 +24,25 @@ status = "okay"; }; +&charger { + richtek,usb-connector = <&usb_con_sm5502>; + status = "okay"; +}; + &mpss_mem { /* Firmware for grandprimelte needs more space */ reg = <0x0 0x86800000 0x0 0x5400000>; }; + +&muic_sm5502 { + status = "okay"; +}; + +&usb { + extcon = <&muic_sm5502>, <&muic_sm5502>; + status = "okay"; +}; + +&usb_hs_phy { + extcon = <&muic_sm5502>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8916-samsung-rossa-common.dtsi b/arch/arm64/boot/dts/qcom/msm8916-samsung-rossa-common.dtsi index e33453c3e51e..5b08f0e11105 100644 --- a/arch/arm64/boot/dts/qcom/msm8916-samsung-rossa-common.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916-samsung-rossa-common.dtsi @@ -2,28 +2,9 @@ #include "msm8916-samsung-fortuna-common.dtsi" -/* SM5504 MUIC instead of SM5502 */ -/delete-node/ &muic; - /* IST3038 instead of Zinitix BT541 */ /delete-node/ &touchscreen; -&blsp_i2c1 { - muic: extcon@14 { - compatible = "siliconmitus,sm5504-muic"; - reg = <0x14>; - interrupts-extended = <&tlmm 12 IRQ_TYPE_EDGE_FALLING>; - pinctrl-0 = <&muic_int_default>; - pinctrl-names = "default"; - - usb_con: connector { - compatible = "usb-b-connector"; - label = "micro-USB"; - type = "micro"; - }; - }; -}; - &blsp_i2c5 { touchscreen: touchscreen@50 { compatible = "imagis,ist3038"; diff --git a/arch/arm64/boot/dts/qcom/msm8916-samsung-rossa.dts b/arch/arm64/boot/dts/qcom/msm8916-samsung-rossa.dts index 1981bb71f6a9..a5106afc3c59 100644 --- a/arch/arm64/boot/dts/qcom/msm8916-samsung-rossa.dts +++ b/arch/arm64/boot/dts/qcom/msm8916-samsung-rossa.dts @@ -16,7 +16,25 @@ constant-charge-voltage-max-microvolt = <4400000>; }; +&charger { + richtek,usb-connector = <&usb_con_sm5504>; + status = "okay"; +}; + &mpss_mem { /* Firmware for rossa needs more space */ reg = <0x0 0x86800000 0x0 0x5800000>; }; + +&muic_sm5504 { + status = "okay"; +}; + +&usb { + extcon = <&muic_sm5504>, <&muic_sm5504>; + status = "okay"; +}; + +&usb_hs_phy { + extcon = <&muic_sm5504>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8916-wiko-chuppito.dts b/arch/arm64/boot/dts/qcom/msm8916-wiko-chuppito.dts new file mode 100644 index 000000000000..262d9a959e6a --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm8916-wiko-chuppito.dts @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/dts-v1/; + +#include "msm8916-pm8916.dtsi" +#include "msm8916-modem-qdsp6.dtsi" + +#include +#include +#include + +/ { + model = "Wiko Pulp 4G"; + compatible = "wiko,chuppito", "qcom,msm8916"; + chassis-type = "handset"; + + aliases { + mmc0 = &sdhc_1; /* eMMC */ + mmc1 = &sdhc_2; /* SD card */ + serial0 = &blsp_uart2; + }; + + chosen { + stdout-path = "serial0"; + }; + + backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pm8916_pwm 0 100000>; + brightness-levels = <0 255>; + num-interpolated-steps = <255>; + default-brightness-level = <255>; + enable-gpios = <&tlmm 119 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&button_backlight_default>; + pinctrl-names = "default"; + }; + + gpio-hall-sensor { + compatible = "gpio-keys"; + pinctrl-0 = <&gpio_hall_sensor_default>; + pinctrl-names = "default"; + label = "Hall Effect Sensor"; + + event-hall-sensor { + label = "Hall Effect Sensor"; + gpios = <&tlmm 117 GPIO_ACTIVE_LOW>; + linux,input-type = ; + linux,code = ; + linux,can-disable; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&gpio_keys_default>; + pinctrl-names = "default"; + label = "Buttons"; + + button-volume-up { + label = "Volume up"; + gpios = <&tlmm 107 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + usb_id: usb-id { + compatible = "linux,extcon-usb-gpio"; + id-gpios = <&tlmm 110 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&usb_id_default>; + pinctrl-names = "default"; + }; +}; + +&blsp_i2c2 { + status = "okay"; + + magnetometer@c { + compatible = "asahi-kasei,ak09911"; + reg = <0x0c>; + vdd-supply = <&pm8916_l17>; + vid-supply = <&pm8916_l6>; + reset-gpios = <&tlmm 120 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&mag_reset_default>; + pinctrl-names = "default"; + mount-matrix = "1", "0", "0", + "0", "1", "0", + "0", "0", "1"; + }; + + proximity@48 { + compatible = "sensortek,stk3310"; + reg = <0x48>; + interrupts-extended = <&tlmm 113 IRQ_TYPE_EDGE_FALLING>; + pinctrl-0 = <&proximity_int_default>; + pinctrl-names = "default"; + }; + + imu@68 { + compatible = "invensense,mpu6880"; + reg = <0x68>; + interrupts-extended = <&tlmm 115 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&pm8916_l17>; + vddio-supply = <&pm8916_l6>; + pinctrl-0 = <&imu_int_default>; + pinctrl-names = "default"; + mount-matrix = "0", "-1", "0", + "-1", "0", "0", + "0", "0", "-1"; + }; +}; + +&blsp_i2c5 { + status = "okay"; + + touchscreen@39 { + compatible = "syna,rmi4-i2c"; + reg = <0x39>; + interrupts-extended = <&tlmm 13 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&pm8916_l17>; + vio-supply = <&pm8916_l6>; + pinctrl-0 = <&touchscreen_default>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + syna,startup-delay-ms = <100>; + syna,reset-delay-ms = <160>; + + rmi4-f01@1 { + reg = <0x1>; + syna,nosleep-mode = <1>; + }; + + rmi4-f11@11 { + reg = <0x11>; + syna,sensor-type = <1>; + }; + }; +}; + +&blsp_uart2 { + status = "okay"; +}; + +&gpu { + status = "okay"; +}; + +&mdss { + status = "okay"; +}; + +&mpss_mem { + reg = <0x0 0x86800000 0x0 0x5600000>; +}; + +&pm8916_codec { + qcom,hphl-jack-type-normally-open; +}; + +&pm8916_mpps { + pwm_out: mpp4-state { + pins = "mpp4"; + function = "digital"; + power-source = ; + output-low; + qcom,dtest = <1>; + }; +}; + +&pm8916_pwm { + pinctrl-0 = <&pwm_out>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pm8916_resin { + linux,code = ; + + status = "okay"; +}; + +&pm8916_rpm_regulators { + pm8916_l17: l17 { + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + }; +}; + +&pm8916_vib { + status = "okay"; +}; + +&sdhc_1 { + status = "okay"; +}; + +&sdhc_2 { + pinctrl-0 = <&sdc2_default>, <&sdc2_cd_default>; + pinctrl-1 = <&sdc2_sleep>, <&sdc2_cd_default>; + pinctrl-names = "default", "sleep"; + cd-gpios = <&tlmm 38 GPIO_ACTIVE_HIGH>; + + status = "okay"; +}; + +&sound { + audio-routing = "AMIC1", "MIC BIAS Internal1", + "AMIC2", "MIC BIAS Internal2"; +}; + +&tlmm { + button_backlight_default: button-backlight-default-state { + pins = "gpio119"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + gpio_hall_sensor_default: gpio-hall-sensor-default-state { + pins = "gpio117"; + function = "gpio"; + drive-strength = <6>; + bias-pull-up; + }; + + gpio_keys_default: gpio-keys-default-state { + pins = "gpio107"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + imu_int_default: imu-int-default-state { + pins = "gpio115"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + mag_reset_default: mag-reset-default-state { + pins = "gpio120"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + proximity_int_default: proximity-int-default-state { + pins = "gpio113"; + function = "gpio"; + drive-strength = <6>; + bias-pull-up; + }; + + sdc2_cd_default: sdc2-cd-default-state { + pins = "gpio38"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + touchscreen_default: touchscreen-default-state { + touchscreen-pins { + pins = "gpio13"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + reset-pins { + pins = "gpio12"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + }; + + usb_id_default: usb-id-default-state { + pins = "gpio110"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; +}; + +&usb { + extcon = <&usb_id>, <&usb_id>; + + status = "okay"; +}; + +&usb_hs_phy { + extcon = <&usb_id>; +}; + +&venus { + status = "okay"; +}; + +&venus_mem { + status = "okay"; +}; + +&wcnss { + status = "okay"; +}; + +&wcnss_iris { + compatible = "qcom,wcn3620"; +}; + +&wcnss_mem { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8917-xiaomi-riva.dts b/arch/arm64/boot/dts/qcom/msm8917-xiaomi-riva.dts index 9db503e21888..4ea4fbdb5ce7 100644 --- a/arch/arm64/boot/dts/qcom/msm8917-xiaomi-riva.dts +++ b/arch/arm64/boot/dts/qcom/msm8917-xiaomi-riva.dts @@ -5,28 +5,13 @@ /dts-v1/; -#include -#include -#include "msm8917.dtsi" -#include "pm8937.dtsi" - -/delete-node/ &qseecom_mem; +#include "msm8917-xiaomi-wingtech.dtsi" / { model = "Xiaomi Redmi 5A (riva)"; compatible = "xiaomi,riva", "qcom,msm8917"; - chassis-type = "handset"; - qcom,msm-id = ; - qcom,board-id = <0x1000b 2>, <0x2000b 2>; - - pwm_backlight: backlight { - compatible = "pwm-backlight"; - pwms = <&pm8937_pwm 0 100000>; - brightness-levels = <0 255>; - num-interpolated-steps = <255>; - default-brightness-level = <128>; - }; + qcom,board-id = <0x1000b 1>, <0x1000b 2>; battery: battery { compatible = "simple-battery"; @@ -38,96 +23,18 @@ charge-term-current-microamp = <60000>; voltage-min-design-microvolt = <3400000>; }; - - chosen { - #address-cells = <2>; - #size-cells = <2>; - ranges; - - stdout-path = "framebuffer0"; - - framebuffer0: framebuffer@90001000 { - compatible = "simple-framebuffer"; - reg = <0x0 0x90001000 0x0 (720 * 1280 * 3)>; - width = <720>; - height = <1280>; - stride = <(720 * 3)>; - format = "r8g8b8"; - - clocks = <&gcc GCC_MDSS_AHB_CLK>, - <&gcc GCC_MDSS_AXI_CLK>, - <&gcc GCC_MDSS_VSYNC_CLK>, - <&gcc GCC_MDSS_MDP_CLK>, - <&gcc GCC_MDSS_BYTE0_CLK>, - <&gcc GCC_MDSS_PCLK0_CLK>, - <&gcc GCC_MDSS_ESC0_CLK>; - power-domains = <&gcc MDSS_GDSC>; - }; - }; - - gpio-keys { - compatible = "gpio-keys"; - - pinctrl-0 = <&gpio_keys_default>; - pinctrl-names = "default"; - - key-volup { - label = "Volume Up"; - linux,code = ; - gpios = <&tlmm 91 GPIO_ACTIVE_LOW>; - debounce-interval = <15>; - }; - }; - - vph_pwr: regulator-vph-pwr { - compatible = "regulator-fixed"; - regulator-name = "vph_pwr"; - regulator-min-microvolt = <3700000>; - regulator-max-microvolt = <3700000>; - regulator-always-on; - regulator-boot-on; - }; - - reserved-memory { - qseecom_mem: qseecom@84a00000 { - reg = <0x0 0x84a00000 0x0 0x1900000>; - no-map; - }; - - framebuffer_mem: memory@90001000 { - reg = <0x0 0x90001000 0x0 (720 * 1280 * 3)>; - no-map; - }; - }; -}; - -&blsp1_i2c3 { - status = "okay"; - - touchscreen@38 { - compatible = "edt,edt-ft5306"; - reg = <0x38>; - interrupts-extended = <&tlmm 65 IRQ_TYPE_LEVEL_LOW>; - reset-gpios = <&tlmm 64 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&tsp_int_rst_default>; - pinctrl-names = "default"; - vcc-supply = <&pm8937_l10>; - iovcc-supply = <&pm8937_l5>; - touchscreen-size-x = <720>; - touchscreen-size-y = <1280>; - }; }; &blsp2_i2c1 { status = "okay"; - bq27426@55 { + power-monitor@55 { compatible = "ti,bq27426"; reg = <0x55>; monitored-battery = <&battery>; }; - bq25601@6b { + charger@6b { compatible = "ti,bq25601"; reg = <0x6b>; interrupts-extended = <&tlmm 61 IRQ_TYPE_EDGE_FALLING>; @@ -139,172 +46,6 @@ }; }; -&pm8937_gpios { - pwm_enable_default: pwm-enable-default-state { - pins = "gpio8"; - function = "dtest2"; - output-low; - bias-disable; - qcom,drive-strength = <2>; - }; -}; - -&pm8937_pwm { - pinctrl-0 = <&pwm_enable_default>; - pinctrl-names = "default"; - - status = "okay"; -}; - -&pm8937_resin { - linux,code = ; - - status = "okay"; -}; - -&rpm_requests { - regulators-0 { - compatible = "qcom,rpm-pm8937-regulators"; - - vdd_s1-supply = <&vph_pwr>; - vdd_s2-supply = <&vph_pwr>; - vdd_s3-supply = <&vph_pwr>; - vdd_s4-supply = <&vph_pwr>; - - vdd_l1_l19-supply = <&pm8937_s3>; - vdd_l2_l23-supply = <&pm8937_s3>; - vdd_l3-supply = <&pm8937_s3>; - vdd_l4_l5_l6_l7_l16-supply = <&pm8937_s4>; - vdd_l8_l11_l12_l17_l22-supply = <&vph_pwr>; - vdd_l9_l10_l13_l14_l15_l18-supply = <&vph_pwr>; - - pm8937_s1: s1 { - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1225000>; - }; - - pm8937_s3: s3 { - regulator-min-microvolt = <1300000>; - regulator-max-microvolt = <1300000>; - }; - - pm8937_s4: s4 { - regulator-min-microvolt = <2050000>; - regulator-max-microvolt = <2050000>; - }; - - pm8937_l2: l2 { - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - }; - - pm8937_l5: l5 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - pm8937_l6: l6 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - pm8937_l7: l7 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - pm8937_l8: l8 { - regulator-min-microvolt = <2850000>; - regulator-max-microvolt = <2900000>; - }; - - pm8937_l9: l9 { - regulator-min-microvolt = <3000000>; - regulator-max-microvolt = <3300000>; - }; - - pm8937_l10: l10 { - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <3000000>; - }; - - pm8937_l11: l11 { - regulator-min-microvolt = <2950000>; - regulator-max-microvolt = <2950000>; - regulator-allow-set-load; - regulator-system-load = <200000>; - }; - - pm8937_l12: l12 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <2950000>; - }; - - pm8937_l13: l13 { - regulator-min-microvolt = <3075000>; - regulator-max-microvolt = <3075000>; - }; - - pm8937_l14: l14 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - }; - - pm8937_l15: l15 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - }; - - pm8937_l16: l16 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - pm8937_l17: l17 { - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2900000>; - }; - - pm8937_l19: l19 { - regulator-min-microvolt = <1225000>; - regulator-max-microvolt = <1350000>; - }; - - pm8937_l22: l22 { - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - }; - - pm8937_l23: l23 { - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - }; - }; - -}; - -&sdhc_1 { - vmmc-supply = <&pm8937_l8>; - vqmmc-supply = <&pm8937_l5>; - - status = "okay"; -}; - -&sdhc_2 { - cd-gpios = <&tlmm 67 GPIO_ACTIVE_LOW>; - vmmc-supply = <&pm8937_l11>; - vqmmc-supply = <&pm8937_l12>; - pinctrl-0 = <&sdc2_default &sdc2_cd_default>; - pinctrl-1 = <&sdc2_sleep &sdc2_cd_default>; - pinctrl-names = "default", "sleep"; - - status = "okay"; -}; - -&sleep_clk { - clock-frequency = <32768>; -}; - &tlmm { bq25601_int_default: bq25601-int-default-state { pins = "gpio61"; @@ -312,47 +53,4 @@ drive-strength = <2>; bias-pull-up; }; - - gpio_keys_default: gpio-keys-default-state { - pins = "gpio91"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - - sdc2_cd_default: sdc2-cd-default-state { - pins = "gpio67"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - - tsp_int_rst_default: tsp-int-rst-default-state { - pins = "gpio64", "gpio65"; - function = "gpio"; - drive-strength = <8>; - bias-pull-up; - }; -}; - -&wcnss { - vddpx-supply = <&pm8937_l5>; - - status = "okay"; -}; - -&wcnss_iris { - compatible = "qcom,wcn3620"; - vddxo-supply = <&pm8937_l7>; - vddrfa-supply = <&pm8937_l19>; - vddpa-supply = <&pm8937_l9>; - vdddig-supply = <&pm8937_l5>; -}; - -&wcnss_mem { - status = "okay"; -}; - -&xo_board { - clock-frequency = <19200000>; }; diff --git a/arch/arm64/boot/dts/qcom/msm8917-xiaomi-rolex.dts b/arch/arm64/boot/dts/qcom/msm8917-xiaomi-rolex.dts new file mode 100644 index 000000000000..f0b72d9878c4 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm8917-xiaomi-rolex.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026, Barnabas Czeman + */ + +/dts-v1/; + +#include "msm8917-xiaomi-wingtech.dtsi" + +/ { + model = "Xiaomi Redmi 4A (rolex)"; + compatible = "xiaomi,rolex", "qcom,msm8917"; + + qcom,board-id = <0x1000b 1>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8917-xiaomi-tiare.dts b/arch/arm64/boot/dts/qcom/msm8917-xiaomi-tiare.dts new file mode 100644 index 000000000000..fe844230030f --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm8917-xiaomi-tiare.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026, Barnabas Czeman + */ + +/dts-v1/; + +#include "msm8917-xiaomi-wingtech.dtsi" + +/ { + model = "Xiaomi Redmi Go (tiare)"; + compatible = "xiaomi,tiare", "qcom,msm8917"; + + qcom,board-id = <0x1000b 1>; +}; + +&pm8937_l22 { + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8917-xiaomi-wingtech.dtsi b/arch/arm64/boot/dts/qcom/msm8917-xiaomi-wingtech.dtsi new file mode 100644 index 000000000000..69eda5f42c06 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm8917-xiaomi-wingtech.dtsi @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, Barnabas Czeman + */ + +/dts-v1/; + +#include +#include +#include "msm8917.dtsi" +#include "pm8937.dtsi" + +/delete-node/ &qseecom_mem; + +/ { + chassis-type = "handset"; + + qcom,msm-id = ; + + pwm_backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pm8937_pwm 0 100000>; + brightness-levels = <0 255>; + num-interpolated-steps = <255>; + default-brightness-level = <128>; + }; + + chosen { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + framebuffer { + compatible = "simple-framebuffer"; + memory-region = <&framebuffer_mem>; + width = <720>; + height = <1280>; + stride = <(720 * 3)>; + format = "r8g8b8"; + + clocks = <&gcc GCC_MDSS_AHB_CLK>, + <&gcc GCC_MDSS_AXI_CLK>, + <&gcc GCC_MDSS_VSYNC_CLK>, + <&gcc GCC_MDSS_MDP_CLK>, + <&gcc GCC_MDSS_BYTE0_CLK>, + <&gcc GCC_MDSS_PCLK0_CLK>, + <&gcc GCC_MDSS_ESC0_CLK>; + power-domains = <&gcc MDSS_GDSC>; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&gpio_keys_default>; + pinctrl-names = "default"; + + key-volup { + label = "Volume Up"; + linux,code = ; + gpios = <&tlmm 91 GPIO_ACTIVE_LOW>; + debounce-interval = <15>; + }; + }; + + vph_pwr: regulator-vph-pwr { + compatible = "regulator-fixed"; + regulator-name = "vph_pwr"; + regulator-min-microvolt = <3700000>; + regulator-max-microvolt = <3700000>; + regulator-always-on; + regulator-boot-on; + }; + + reserved-memory { + qseecom_mem: qseecom@84a00000 { + reg = <0x0 0x84a00000 0x0 0x1900000>; + no-map; + }; + + framebuffer_mem: memory@90001000 { + reg = <0x0 0x90001000 0x0 (720 * 1280 * 3)>; + no-map; + }; + }; +}; + +&blsp1_i2c3 { + status = "okay"; + + edt_ft5306: touchscreen@38 { + compatible = "edt,edt-ft5306"; + reg = <0x38>; + interrupts-extended = <&tlmm 65 IRQ_TYPE_LEVEL_LOW>; + reset-gpios = <&tlmm 64 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&tsp_int_rst_default>; + pinctrl-names = "default"; + vcc-supply = <&pm8937_l10>; + iovcc-supply = <&pm8937_l5>; + touchscreen-size-x = <720>; + touchscreen-size-y = <1280>; + + status = "disabled"; + }; + + goodix_gt911: touchscreen@5d { + compatible = "goodix,gt911"; + reg = <0x5d>; + interrupts-extended = <&tlmm 65 IRQ_TYPE_LEVEL_LOW>; + irq-gpios = <&tlmm 65 GPIO_ACTIVE_HIGH>; + reset-gpios = <&tlmm 64 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&tsp_int_rst_default>; + pinctrl-names = "default"; + AVDD28-supply = <&pm8937_l10>; + VDDIO-supply = <&pm8937_l5>; + touchscreen-size-x = <720>; + touchscreen-size-y = <1280>; + + status = "disabled"; + }; +}; + +&pm8937_gpios { + pwm_enable_default: pwm-enable-default-state { + pins = "gpio8"; + function = "dtest2"; + output-low; + bias-disable; + qcom,drive-strength = <2>; + }; +}; + +&pm8937_pwm { + pinctrl-0 = <&pwm_enable_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pm8937_resin { + linux,code = ; + + status = "okay"; +}; + +&rpm_requests { + regulators-0 { + compatible = "qcom,rpm-pm8937-regulators"; + + vdd_s1-supply = <&vph_pwr>; + vdd_s2-supply = <&vph_pwr>; + vdd_s3-supply = <&vph_pwr>; + vdd_s4-supply = <&vph_pwr>; + + vdd_l1_l19-supply = <&pm8937_s3>; + vdd_l2_l23-supply = <&pm8937_s3>; + vdd_l3-supply = <&pm8937_s3>; + vdd_l4_l5_l6_l7_l16-supply = <&pm8937_s4>; + vdd_l8_l11_l12_l17_l22-supply = <&vph_pwr>; + vdd_l9_l10_l13_l14_l15_l18-supply = <&vph_pwr>; + + pm8937_s1: s1 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1225000>; + }; + + pm8937_s3: s3 { + regulator-min-microvolt = <1300000>; + regulator-max-microvolt = <1300000>; + }; + + pm8937_s4: s4 { + regulator-min-microvolt = <2050000>; + regulator-max-microvolt = <2050000>; + }; + + pm8937_l2: l2 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + pm8937_l5: l5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + pm8937_l6: l6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + pm8937_l7: l7 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + pm8937_l8: l8 { + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2900000>; + }; + + pm8937_l9: l9 { + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3300000>; + }; + + pm8937_l10: l10 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <3000000>; + }; + + pm8937_l11: l11 { + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <2950000>; + regulator-allow-set-load; + regulator-system-load = <200000>; + }; + + pm8937_l12: l12 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2950000>; + }; + + pm8937_l13: l13 { + regulator-min-microvolt = <3075000>; + regulator-max-microvolt = <3075000>; + }; + + pm8937_l14: l14 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + pm8937_l15: l15 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + pm8937_l16: l16 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + pm8937_l17: l17 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2900000>; + }; + + pm8937_l19: l19 { + regulator-min-microvolt = <1225000>; + regulator-max-microvolt = <1350000>; + }; + + pm8937_l22: l22 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + pm8937_l23: l23 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + }; +}; + +&sdhc_1 { + vmmc-supply = <&pm8937_l8>; + vqmmc-supply = <&pm8937_l5>; + + status = "okay"; +}; + +&sdhc_2 { + cd-gpios = <&tlmm 67 GPIO_ACTIVE_LOW>; + vmmc-supply = <&pm8937_l11>; + vqmmc-supply = <&pm8937_l12>; + pinctrl-0 = <&sdc2_default &sdc2_cd_default>; + pinctrl-1 = <&sdc2_sleep &sdc2_cd_default>; + pinctrl-names = "default", "sleep"; + + status = "okay"; +}; + +&sleep_clk { + clock-frequency = <32768>; +}; + +&tlmm { + gpio_keys_default: gpio-keys-default-state { + pins = "gpio91"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + sdc2_cd_default: sdc2-cd-default-state { + pins = "gpio67"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + tsp_int_rst_default: tsp-int-rst-default-state { + pins = "gpio64", "gpio65"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; +}; + +&wcnss { + vddpx-supply = <&pm8937_l5>; + + status = "okay"; +}; + +&wcnss_iris { + compatible = "qcom,wcn3620"; + vddxo-supply = <&pm8937_l7>; + vddrfa-supply = <&pm8937_l19>; + vddpa-supply = <&pm8937_l9>; + vdddig-supply = <&pm8937_l5>; +}; + +&wcnss_mem { + status = "okay"; +}; + +&xo_board { + clock-frequency = <19200000>; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8937-xiaomi-land.dts b/arch/arm64/boot/dts/qcom/msm8937-xiaomi-land.dts index 91837ff940f1..4f301e7c6517 100644 --- a/arch/arm64/boot/dts/qcom/msm8937-xiaomi-land.dts +++ b/arch/arm64/boot/dts/qcom/msm8937-xiaomi-land.dts @@ -178,7 +178,7 @@ qcom,num-strings = <2>; qcom,external-pfet; qcom,current-limit-microamp = <20000>; - qcom,ovp-millivolt = <29600>; + qcom,ovp-millivolt = <29500>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts b/arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts index ea90b00a2c8a..90e966242720 100644 --- a/arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts +++ b/arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts @@ -147,6 +147,20 @@ pinctrl-names = "default"; }; + light-sensor@60 { + compatible = "capella,cm36686", "vishay,vcnl4040"; + reg = <0x60>; + + interrupts-extended = <&tlmm 113 IRQ_TYPE_EDGE_FALLING>; + proximity-near-level = <30>; + + vdd-supply = <&pm8916_l8>; + vio-supply = <&pm8916_l6>; + + pinctrl-0 = <&light_int_default>; + pinctrl-names = "default"; + }; + imu@68 { compatible = "invensense,mpu6515"; reg = <0x68>; @@ -330,4 +344,11 @@ drive-strength = <2>; bias-disable; }; + + light_int_default: light-int-default-state { + pins = "gpio113"; + function = "gpio"; + drive-strength = <16>; + bias-pull-up; + }; }; diff --git a/arch/arm64/boot/dts/qcom/msm8953-xiaomi-daisy.dts b/arch/arm64/boot/dts/qcom/msm8953-xiaomi-daisy.dts index ddd7af616794..59f873a06e4d 100644 --- a/arch/arm64/boot/dts/qcom/msm8953-xiaomi-daisy.dts +++ b/arch/arm64/boot/dts/qcom/msm8953-xiaomi-daisy.dts @@ -157,7 +157,7 @@ &pmi8950_wled { qcom,current-limit-microamp = <20000>; - qcom,num-strings = <2>; + qcom,num-strings = <3>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/msm8953-xiaomi-vince.dts b/arch/arm64/boot/dts/qcom/msm8953-xiaomi-vince.dts index d46325e79917..c2a290bf493c 100644 --- a/arch/arm64/boot/dts/qcom/msm8953-xiaomi-vince.dts +++ b/arch/arm64/boot/dts/qcom/msm8953-xiaomi-vince.dts @@ -169,7 +169,7 @@ &pmi8950_wled { qcom,current-limit-microamp = <20000>; - qcom,ovp-millivolt = <29600>; + qcom,ovp-millivolt = <29500>; qcom,num-strings = <2>; qcom,external-pfet; qcom,cabc; diff --git a/arch/arm64/boot/dts/qcom/msm8996-oneplus-common.dtsi b/arch/arm64/boot/dts/qcom/msm8996-oneplus-common.dtsi index 63ab564655bc..a4dcc88bb01f 100644 --- a/arch/arm64/boot/dts/qcom/msm8996-oneplus-common.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996-oneplus-common.dtsi @@ -745,14 +745,7 @@ bias-pull-down; }; - mdss_te_active: mdss-te-active-state { - pins = "gpio10"; - function = "mdp_vsync"; - drive-strength = <2>; - bias-pull-down; - }; - - mdss_te_suspend: mdss-te-suspend-state { + mdss_te: mdss-te-state { pins = "gpio10"; function = "mdp_vsync"; drive-strength = <2>; diff --git a/arch/arm64/boot/dts/qcom/msm8996-v3.0.dtsi b/arch/arm64/boot/dts/qcom/msm8996-v3.0.dtsi deleted file mode 100644 index 929bdcd45d02..000000000000 --- a/arch/arm64/boot/dts/qcom/msm8996-v3.0.dtsi +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -/* - * Copyright (c) 2021, Konrad Dybcio - */ - -#include "msm8996.dtsi" - -/ { - qcom,msm-id = <246 0x30000>; -}; - - /* - * This revision seems to have differ GPU CPR - * parameters, GPU frequencies and some differences - * when it comes to voltage delivery to.. once again - * the GPU. Funnily enough, it's simpler to make it an - * overlay on top of 3.1 (the final one) than vice versa. - * The differences will show here as more and more - * features get enabled upstream. - */ - -gpu_opp_table_3_0: opp-table-gpu30 { - compatible = "operating-points-v2"; - - opp-624000000 { - opp-hz = /bits/ 64 <624000000>; - opp-level = <7>; - }; - - opp-560000000 { - opp-hz = /bits/ 64 <560000000>; - opp-level = <6>; - }; - - opp-510000000 { - opp-hz = /bits/ 64 <510000000>; - opp-level = <5>; - }; - - opp-401800000 { - opp-hz = /bits/ 64 <401800000>; - opp-level = <4>; - }; - - opp-315000000 { - opp-hz = /bits/ 64 <315000000>; - opp-level = <3>; - }; - - opp-214000000 { - opp-hz = /bits/ 64 <214000000>; - opp-level = <3>; - }; - - opp-133000000 { - opp-hz = /bits/ 64 <133000000>; - opp-level = <3>; - }; -}; - -&gpu { - operating-points-v2 = <&gpu_opp_table_3_0>; -}; diff --git a/arch/arm64/boot/dts/qcom/msm8996-xiaomi-common.dtsi b/arch/arm64/boot/dts/qcom/msm8996-xiaomi-common.dtsi index 0386636a29f0..77ad613590a3 100644 --- a/arch/arm64/boot/dts/qcom/msm8996-xiaomi-common.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996-xiaomi-common.dtsi @@ -247,7 +247,7 @@ pinctrl-names = "default", "sleep"; pinctrl-0 = <&mdss_dsi_default &mdss_te_default>; - pinctrl-1 = <&mdss_dsi_sleep &mdss_te_sleep>; + pinctrl-1 = <&mdss_dsi_sleep &mdss_te_default>; }; &mdss_dsi0_out { @@ -730,14 +730,7 @@ bias-pull-down; }; - mdss_te_default: mdss-te-default-state { - pins = "gpio10"; - function = "mdp_vsync"; - drive-strength = <2>; - bias-pull-down; - }; - - mdss_te_sleep: mdss-te-sleep-state { + mdss_te_default: mdss-te-state { pins = "gpio10"; function = "mdp_vsync"; drive-strength = <2>; diff --git a/arch/arm64/boot/dts/qcom/msm8996-xiaomi-gemini.dts b/arch/arm64/boot/dts/qcom/msm8996-xiaomi-gemini.dts index 3c6a40212a8d..fd3a2121465b 100644 --- a/arch/arm64/boot/dts/qcom/msm8996-xiaomi-gemini.dts +++ b/arch/arm64/boot/dts/qcom/msm8996-xiaomi-gemini.dts @@ -104,7 +104,7 @@ pinctrl-names = "default", "sleep"; pinctrl-0 = <&mdss_dsi_default &mdss_te_default>; - pinctrl-1 = <&mdss_dsi_sleep &mdss_te_sleep>; + pinctrl-1 = <&mdss_dsi_sleep &mdss_te_default>; panel: panel@0 { compatible = "jdi,fhd-r63452"; diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi index 9d4ce47578fb..2f67e665996f 100644 --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi @@ -3255,7 +3255,7 @@ bus-width = <4>; status = "disabled"; - }; + }; blsp1_dma: dma-controller@7544000 { compatible = "qcom,bam-v1.7.0"; diff --git a/arch/arm64/boot/dts/qcom/msm8996pro-xiaomi-natrium.dts b/arch/arm64/boot/dts/qcom/msm8996pro-xiaomi-natrium.dts index f8ab03f106a1..7e2ee9a4e9f0 100644 --- a/arch/arm64/boot/dts/qcom/msm8996pro-xiaomi-natrium.dts +++ b/arch/arm64/boot/dts/qcom/msm8996pro-xiaomi-natrium.dts @@ -51,7 +51,7 @@ pinctrl-names = "default", "sleep"; pinctrl-0 = <&mdss_dsi_default &mdss_te_default>; - pinctrl-1 = <&mdss_dsi_sleep &mdss_te_sleep>; + pinctrl-1 = <&mdss_dsi_sleep &mdss_te_default>; panel: panel@0 { compatible = "jdi,fhd-r63452"; diff --git a/arch/arm64/boot/dts/qcom/msm8998-xiaomi-sagit.dts b/arch/arm64/boot/dts/qcom/msm8998-xiaomi-sagit.dts index 0cac06f25a77..30222f6608da 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-xiaomi-sagit.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-xiaomi-sagit.dts @@ -543,14 +543,7 @@ bias-pull-down; }; - mdss_te_active_state: mdss-te-active-state { - pins = "gpio10"; - function = "mdp_vsync_a"; - drive-strength = <2>; - bias-pull-down; - }; - - mdss_te_suspend_state: mdss-te-suspend-state { + mdss_te_state: mdss-te-state { pins = "gpio10"; function = "mdp_vsync_a"; drive-strength = <2>; diff --git a/arch/arm64/boot/dts/qcom/pm6125.dtsi b/arch/arm64/boot/dts/qcom/pm6125.dtsi index d0db28336fa9..cb067adb7d17 100644 --- a/arch/arm64/boot/dts/qcom/pm6125.dtsi +++ b/arch/arm64/boot/dts/qcom/pm6125.dtsi @@ -138,7 +138,6 @@ reg = <0x6000>, <0x6100>; reg-names = "rtc", "alarm"; interrupts = <0x0 0x61 0x1 IRQ_TYPE_EDGE_RISING>; - status = "disabled"; }; pm6125_gpios: gpio@c000 { diff --git a/arch/arm64/boot/dts/qcom/pm8010-kaanapali.dtsi b/arch/arm64/boot/dts/qcom/pm8010-kaanapali.dtsi new file mode 100644 index 000000000000..bfc58a6589d3 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pm8010-kaanapali.dtsi @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include + +/ { + thermal-zones { + pm8010-m-thermal { + polling-delay-passive = <100>; + + thermal-sensors = <&pm8010_m_e1_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "hot"; + }; + + trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + pm8010-n-thermal { + polling-delay-passive = <100>; + + thermal-sensors = <&pm8010_n_e1_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "hot"; + }; + + trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; +}; + +&spmi_bus1 { + pm8010_m_e1: pmic@c { + compatible = "qcom,pm8010", "qcom,spmi-pmic"; + reg = <0xc SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pm8010_m_e1_temp_alarm: temp-alarm@2400 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0x2400>; + interrupts = <0xc 0x24 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + }; + + pm8010_n_e1: pmic@d { + compatible = "qcom,pm8010", "qcom,spmi-pmic"; + reg = <0xd SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pm8010_n_e1_temp_alarm: temp-alarm@2400 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0x2400>; + interrupts = <0xd 0x24 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pmcx0102.dtsi b/arch/arm64/boot/dts/qcom/pmcx0102.dtsi new file mode 100644 index 000000000000..db2da9ef4f01 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pmcx0102.dtsi @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include + +/ { + thermal-zones { + pmcx0102-c0-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmcx0102_c_e0_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + pmcx0102-c1-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmcx0102_c_e1_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + pmcx0102_d0_thermal: pmcx0102-d0-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmcx0102_d_e0_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + pmcx0102-d1-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmcx0102_d_e1_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; +}; + +&spmi_bus0 { + pmcx0102_c_e0: pmic@2 { + compatible = "qcom,pmcx0102", "qcom,spmi-pmic"; + reg = <0x2 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmcx0102_c_e0_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x2 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmcx0102_c_e0_gpios: gpio@8800 { + compatible = "qcom,pmcx0102-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmcx0102_c_e0_gpios 0 0 14>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + pmcx0102_d_e0: pmic@3 { + compatible = "qcom,pmcx0102", "qcom,spmi-pmic"; + reg = <0x3 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmcx0102_d_e0_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x3 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmcx0102_d_e0_gpios: gpio@8800 { + compatible = "qcom,pmcx0102-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmcx0102_d_e0_gpios 0 0 14>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; +}; + +&spmi_bus1 { + pmcx0102_c_e1: pmic@2 { + compatible = "qcom,pmcx0102", "qcom,spmi-pmic"; + reg = <0x2 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmcx0102_c_e1_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x2 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmcx0102_c_e1_gpios: gpio@8800 { + compatible = "qcom,pmcx0102-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmcx0102_c_e1_gpios 0 0 14>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + pmcx0102_d_e1: pmic@3 { + compatible = "qcom,pmcx0102", "qcom,spmi-pmic"; + reg = <0x3 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmcx0102_d_e1_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x3 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmcx0102_d_e1_gpios: gpio@8800 { + compatible = "qcom,pmcx0102-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmcx0102_d_e1_gpios 0 0 14>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pmd8028-kaanapali.dtsi b/arch/arm64/boot/dts/qcom/pmd8028-kaanapali.dtsi new file mode 100644 index 000000000000..db4dc16a66e7 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pmd8028-kaanapali.dtsi @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include + +/ { + thermal-zones { + pmd8028-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmd8028_e1_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "hot"; + }; + + trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; +}; + +&spmi_bus1 { + pmd8028_e1: pmic@4 { + compatible = "qcom,pmd8028", "qcom,spmi-pmic"; + reg = <0x4 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmd8028_e1_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x4 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmd8028_e1_gpios: gpio@8800 { + compatible = "qcom,pmd8028-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmd8028_e1_gpios 0 0 4>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pmh0101.dtsi b/arch/arm64/boot/dts/qcom/pmh0101.dtsi new file mode 100644 index 000000000000..b1ec41325958 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pmh0101.dtsi @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include + +/ { + thermal-zones { + pmh0101-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmh0101_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; +}; + +&spmi_bus0 { + pmic@1 { + compatible = "qcom,pmh0101", "qcom,spmi-pmic"; + reg = <0x1 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmh0101_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x1 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmh0101_gpios: gpio@8800 { + compatible = "qcom,pmh0101-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmh0101_gpios 0 0 18>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + pmh0101_flash: led-controller@ee00 { + compatible = "qcom,pmh0101-flash-led", "qcom,spmi-flash-led"; + reg = <0xee00>; + status = "disabled"; + }; + + pmh0101_pwm: pwm { + compatible = "qcom,pmh0101-pwm", "qcom,pm8350c-pwm"; + #pwm-cells = <2>; + status = "disabled"; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pmh0104-glymur.dtsi b/arch/arm64/boot/dts/qcom/pmh0104-glymur.dtsi new file mode 100644 index 000000000000..7a1e5f355c17 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pmh0104-glymur.dtsi @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include + +/{ + thermal_zones { + pmh0104_i0_thermal: pmh0104-i0-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmh0104_i_e0_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + pmh0104_j0_thermal: pmh0104-j0-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmh0104_j_e0_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + pmh0104-l1-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmh0104_l_e1_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; +}; + +&spmi_bus0 { + pmh0104_i_e0: pmic@8 { + compatible = "qcom,pmh0104", "qcom,spmi-pmic"; + reg = <0x8 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmh0104_i_e0_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x8 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmh0104_i_e0_gpios: gpio@8800 { + compatible = "qcom,pmh0104-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmh0104_i_e0_gpios 0 0 8>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + pmh0104_j_e0: pmic@9 { + compatible = "qcom,pmh0104", "qcom,spmi-pmic"; + reg = <0x9 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmh0104_j_e0_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x9 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmh0104_j_e0_gpios: gpio@8800 { + compatible = "qcom,pmh0104-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmh0104_j_e0_gpios 0 0 8>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; +}; + +&spmi_bus1 { + pmh0104_l_e1: pmic@b { + compatible = "qcom,pmh0104", "qcom,spmi-pmic"; + reg = <0xb SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmh0104_l_e1_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0xb 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmh0104_l_e1_gpios: gpio@8800 { + compatible = "qcom,pmh0104-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmh0104_l_e1_gpios 0 0 8>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pmh0104-kaanapali.dtsi b/arch/arm64/boot/dts/qcom/pmh0104-kaanapali.dtsi new file mode 100644 index 000000000000..d009c9a9f59e --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pmh0104-kaanapali.dtsi @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include + +/ { + thermal-zones { + pmh0104-thermal { + polling-delay-passive = <100>; + + thermal-sensors = <&pmh0104_j_e1_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "hot"; + }; + + trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; +}; + +&spmi_bus1 { + pmh0104_j_e1: pmic@9 { + compatible = "qcom,pmh0104", "qcom,spmi-pmic"; + reg = <0x9 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmh0104_j_e1_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x9 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmh0104_j_e1_gpios: gpio@8800 { + compatible = "qcom,pmh0104-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmh0104_j_e1_gpios 0 0 8>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pmh0110-glymur.dtsi b/arch/arm64/boot/dts/qcom/pmh0110-glymur.dtsi new file mode 100644 index 000000000000..7655bc030348 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pmh0110-glymur.dtsi @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include + +/ { + thermal-zones { + pmh0110-f0-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmh0110_f_e0_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + pmh0110-f1-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmh0110_f_e1_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + pmh0110-h0-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmh0110_h_e0_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; +}; + +&spmi_bus0 { + pmh0110_f_e0: pmic@5 { + compatible = "qcom,pmh0110", "qcom,spmi-pmic"; + reg = <0x5 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmh0110_f_e0_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x5 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmh0110_f_e0_gpios: gpio@8800 { + compatible = "qcom,pmh0110-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmh0110_f_e0_gpios 0 0 14>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + pmh0110_h_e0: pmic@7 { + compatible = "qcom,pmh0110", "qcom,spmi-pmic"; + reg = <0x7 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmh0110_h_e0_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x7 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmh0110_h_e0_gpios: gpio@8800 { + compatible = "qcom,pmh0110-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmh0110_h_e0_gpios 0 0 14>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; +}; + +&spmi_bus1 { + pmh0110_f_e1: pmic@5 { + compatible = "qcom,pmh0110", "qcom,spmi-pmic"; + reg = <0x5 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmh0110_f_e1_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x5 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmh0110_f_e1_gpios: gpio@8800 { + compatible = "qcom,pmh0110-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmh0110_f_e1_gpios 0 0 14>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pmh0110-kaanapali.dtsi b/arch/arm64/boot/dts/qcom/pmh0110-kaanapali.dtsi new file mode 100644 index 000000000000..15d9cff246b3 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pmh0110-kaanapali.dtsi @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include + +/ { + thermal-zones { + pmh0110-d-thermal { + polling-delay-passive = <100>; + + thermal-sensors = <&pmh0110_d_e0_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "hot"; + }; + + trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + pmh0110-f-thermal { + polling-delay-passive = <100>; + + thermal-sensors = <&pmh0110_f_e0_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "hot"; + }; + + trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + pmh0110-g-thermal { + polling-delay-passive = <100>; + + thermal-sensors = <&pmh0110_g_e0_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "hot"; + }; + + trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + pmh0110-i-thermal { + polling-delay-passive = <100>; + + thermal-sensors = <&pmh0110_i_e0_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "hot"; + }; + + trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; +}; + +&spmi_bus0 { + pmh0110_d_e0: pmic@3 { + compatible = "qcom,pmh0110", "qcom,spmi-pmic"; + reg = <0x3 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmh0110_d_e0_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x3 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmh0110_d_e0_gpios: gpio@8800 { + compatible = "qcom,pmh0110-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmh0110_d_e0_gpios 0 0 14>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + pmh0110_f_e0: pmic@5 { + compatible = "qcom,pmh0110", "qcom,spmi-pmic"; + reg = <0x5 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmh0110_f_e0_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x5 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmh0110_f_e0_gpios: gpio@8800 { + compatible = "qcom,pmh0110-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmh0110_f_e0_gpios 0 0 14>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + pmh0110_g_e0: pmic@6 { + compatible = "qcom,pmh0110", "qcom,spmi-pmic"; + reg = <0x6 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmh0110_g_e0_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x6 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmh0110_g_e0_gpios: gpio@8800 { + compatible = "qcom,pmh0110-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmh0110_g_e0_gpios 0 0 14>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + + pmh0110_i_e0: pmic@8 { + compatible = "qcom,pmh0110", "qcom,spmi-pmic"; + reg = <0x8 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmh0110_i_e0_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x8 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmh0110_i_e0_gpios: gpio@8800 { + compatible = "qcom,pmh0110-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmh0110_i_e0_gpios 0 0 14>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pmih0108-kaanapali.dtsi b/arch/arm64/boot/dts/qcom/pmih0108-kaanapali.dtsi new file mode 100644 index 000000000000..b73b0e82c3d3 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pmih0108-kaanapali.dtsi @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include + +/ { + thermal-zones { + pmih0108-thermal { + polling-delay-passive = <100>; + thermal-sensors = <&pmih0108_e1_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "hot"; + }; + + trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; +}; + +&spmi_bus1 { + pmih0108_e1: pmic@7 { + compatible = "qcom,pmih0108", "qcom,spmi-pmic"; + reg = <0x7 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmih0108_e1_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0x7 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmih0108_e1_gpios: gpio@8800 { + compatible = "qcom,pmih0108-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmih0108_e1_gpios 0 0 18>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + pmih0108_e1_eusb2_repeater: phy@fd00 { + compatible = "qcom,pm8550b-eusb2-repeater"; + reg = <0xfd00>; + #phy-cells = <0>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pmk8550.dtsi b/arch/arm64/boot/dts/qcom/pmk8550.dtsi index 583f61fc16ad..3049eb6b46d7 100644 --- a/arch/arm64/boot/dts/qcom/pmk8550.dtsi +++ b/arch/arm64/boot/dts/qcom/pmk8550.dtsi @@ -73,5 +73,15 @@ interrupt-controller; #interrupt-cells = <2>; }; + + pmk8550_pwm: pwm { + compatible = "qcom,pmk8550-pwm"; + + #address-cells = <1>; + #size-cells = <0>; + #pwm-cells = <2>; + + status = "disabled"; + }; }; }; diff --git a/arch/arm64/boot/dts/qcom/pmk8850.dtsi b/arch/arm64/boot/dts/qcom/pmk8850.dtsi new file mode 100644 index 000000000000..c7ba72fd48bc --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pmk8850.dtsi @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +&spmi_bus0 { + pmic@0 { + compatible = "qcom,pmk8850", "qcom,spmi-pmic"; + reg = <0x0 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmk8850_pon: pon@1300 { + compatible = "qcom,pmk8350-pon"; + reg = <0x1300>, + <0x800>; + reg-names = "hlos", + "pbs"; + + pon_pwrkey: pwrkey { + compatible = "qcom,pmk8350-pwrkey"; + interrupts = <0x0 0x13 0x7 IRQ_TYPE_EDGE_BOTH>; + linux,code = ; + }; + + pon_resin: resin { + compatible = "qcom,pmk8350-resin"; + interrupts = <0x0 0x13 0x6 IRQ_TYPE_EDGE_BOTH>; + status = "disabled"; + }; + }; + + pmk8850_gpios: gpio@b800 { + compatible = "qcom,pmk8850-gpio", "qcom,spmi-gpio"; + reg = <0xb800>; + gpio-controller; + gpio-ranges = <&pmk8850_gpios 0 0 8>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + pmk8850_rtc: rtc@6100 { + compatible = "qcom,pmk8350-rtc"; + reg = <0x6100>, + <0x6200>; + reg-names = "rtc", + "alarm"; + interrupts = <0x0 0x62 0x1 IRQ_TYPE_EDGE_RISING>; + }; + + pmk8850_sdam_2: nvram@7100 { + compatible = "qcom,spmi-sdam"; + reg = <0x7100>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x7100 0x100>; + + reboot_reason: reboot-reason@48 { + reg = <0x48 0x1>; + bits = <1 7>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/pmr735d-kaanapali.dtsi b/arch/arm64/boot/dts/qcom/pmr735d-kaanapali.dtsi new file mode 100644 index 000000000000..d0dd5e078cdc --- /dev/null +++ b/arch/arm64/boot/dts/qcom/pmr735d-kaanapali.dtsi @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include + +/ { + thermal-zones { + pmr735d-thermal { + polling-delay-passive = <100>; + + thermal-sensors = <&pmr735d_e1_temp_alarm>; + + trips { + trip0 { + temperature = <95000>; + hysteresis = <0>; + type = "passive"; + }; + + trip1 { + temperature = <115000>; + hysteresis = <0>; + type = "hot"; + }; + + trip2 { + temperature = <145000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; +}; + +&spmi_bus1 { + pmr735d_e1: pmic@a { + compatible = "qcom,pmr735d", "qcom,spmi-pmic"; + reg = <0xa SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmr735d_e1_temp_alarm: temp-alarm@a00 { + compatible = "qcom,spmi-temp-alarm"; + reg = <0xa00>; + interrupts = <0xa 0xa 0x0 IRQ_TYPE_EDGE_BOTH>; + #thermal-sensor-cells = <0>; + }; + + pmr735d_e1_gpios: gpio@8800 { + compatible = "qcom,pmr735d-gpio", "qcom,spmi-gpio"; + reg = <0x8800>; + gpio-controller; + gpio-ranges = <&pmr735d_e1_gpios 0 0 2>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/purwa-iot-evk.dts b/arch/arm64/boot/dts/qcom/purwa-iot-evk.dts new file mode 100644 index 000000000000..ad503beec1d3 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/purwa-iot-evk.dts @@ -0,0 +1,1590 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; + +#include +#include "purwa-iot-som.dtsi" +#include + +/ { + model = "Qualcomm Technologies, Inc. Purwa IoT EVK"; + compatible = "qcom,purwa-iot-evk", "qcom,purwa-iot-som", "qcom,x1p42100"; + chassis-type = "embedded"; + + aliases { + serial0 = &uart21; + serial1 = &uart14; + }; + + backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pmk8550_pwm 0 5000000>; + enable-gpios = <&pmc8380_3_gpios 4 GPIO_ACTIVE_HIGH>; + power-supply = <&vreg_edp_bl>; + + pinctrl-0 = <&edp_bl_en>, <&edp_bl_pwm>; + pinctrl-names = "default"; + }; + + wcd938x: audio-codec { + compatible = "qcom,wcd9385-codec"; + + pinctrl-0 = <&wcd_default>; + pinctrl-names = "default"; + + reset-gpios = <&tlmm 191 GPIO_ACTIVE_LOW>; + + qcom,micbias1-microvolt = <1800000>; + qcom,micbias2-microvolt = <1800000>; + qcom,micbias3-microvolt = <1800000>; + qcom,micbias4-microvolt = <1800000>; + qcom,mbhc-buttons-vthreshold-microvolt = <75000 150000 237000 500000 + 500000 500000 500000 500000>; + qcom,mbhc-headset-vthreshold-microvolt = <1700000>; + qcom,mbhc-headphone-vthreshold-microvolt = <50000>; + qcom,rx-device = <&wcd_rx>; + qcom,tx-device = <&wcd_tx>; + + vdd-buck-supply = <&vreg_l15b_1p8>; + vdd-rxtx-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l15b_1p8>; + vdd-mic-bias-supply = <&vreg_bob1>; + + #sound-dai-cells = <1>; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + connector3 { + compatible = "usb-a-connector"; + label = "USB-3-Type-A"; + power-role = "source"; + + vbus-supply = <®ulator_usb3_vbus>; + + port { + connector_3_in: endpoint { + }; + }; + }; + + connector6 { + compatible = "usb-a-connector"; + label = "USB-6-Type-A"; + power-role = "source"; + + vbus-supply = <®ulator_usb6_vbus>; + + port { + connector_4_in: endpoint { + }; + }; + }; + + pmic-glink { + compatible = "qcom,x1e80100-pmic-glink", + "qcom,sm8550-pmic-glink", + "qcom,pmic-glink"; + #address-cells = <1>; + #size-cells = <0>; + orientation-gpios = <&tlmm 121 GPIO_ACTIVE_HIGH>, + <&tlmm 123 GPIO_ACTIVE_HIGH>, + <&tlmm 125 GPIO_ACTIVE_HIGH>; + + connector@0 { + compatible = "usb-c-connector"; + reg = <0>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pmic_glink_ss0_hs_in: endpoint { + remote-endpoint = <&usb_1_ss0_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + pmic_glink_ss0_ss_in: endpoint { + remote-endpoint = <&retimer_ss0_ss_out>; + }; + }; + + port@2 { + reg = <2>; + + pmic_glink_ss0_con_sbu_in: endpoint { + remote-endpoint = <&retimer_ss0_con_sbu_out>; + }; + }; + }; + }; + + connector@1 { + compatible = "usb-c-connector"; + reg = <1>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pmic_glink_ss1_hs_in: endpoint { + remote-endpoint = <&usb_1_ss1_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + pmic_glink_ss1_ss_in: endpoint { + remote-endpoint = <&retimer_ss1_ss_out>; + }; + }; + + port@2 { + reg = <2>; + + pmic_glink_ss1_con_sbu_in: endpoint { + remote-endpoint = <&retimer_ss1_con_sbu_out>; + }; + }; + }; + }; + + connector@2 { + compatible = "usb-c-connector"; + reg = <2>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pmic_glink_ss2_hs_in: endpoint { + remote-endpoint = <&usb_1_ss2_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + pmic_glink_ss2_ss_in: endpoint { + remote-endpoint = <&retimer_ss2_ss_out>; + }; + }; + + port@2 { + reg = <2>; + + pmic_glink_ss2_con_sbu_in: endpoint { + remote-endpoint = <&retimer_ss2_con_sbu_out>; + }; + }; + }; + }; + }; + + vreg_edp_3p3: regulator-edp-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_EDP_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 70 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&edp_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_edp_bl: regulator-edp-bl { + compatible = "regulator-fixed"; + + regulator-name = "VBL9"; + regulator-min-microvolt = <3600000>; + regulator-max-microvolt = <3600000>; + + gpio = <&pmc8380_3_gpios 10 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&edp_bl_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_nvme: regulator-nvme { + compatible = "regulator-fixed"; + + regulator-name = "VREG_NVME_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 18 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&nvme_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_pcie_12v: regulator-pcie-12v { + compatible = "regulator-fixed"; + + regulator-name = "VREG_PCIE_12V"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + + gpio = <&pm8550ve_8_gpios 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&pcie_x8_12v>; + pinctrl-names = "default"; + }; + + vreg_pcie_3v3: regulator-pcie-3v3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_PCIE_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&pmc8380_3_gpios 6 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&pm_sde7_main_3p3_en>; + pinctrl-names = "default"; + }; + + vreg_pcie_3v3_aux: regulator-pcie-3v3-aux { + compatible = "regulator-fixed"; + + regulator-name = "VREG_PCIE_3P3_AUX"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&pmc8380_3_gpios 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&pm_sde7_aux_3p3_en>; + pinctrl-names = "default"; + }; + + /* Left unused as the retimer is not used on this board. */ + vreg_rtmr0_1p15: regulator-rtmr0-1p15 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR0_1P15"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1150000>; + + gpio = <&pmc8380_5_gpios 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb0_pwr_1p15_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr0_1p8: regulator-rtmr0-1p8 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR0_1P8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + gpio = <&pm8550ve_9_gpios 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb0_1p8_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr0_3p3: regulator-rtmr0-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR0_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&pm8550_gpios 11 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb0_3p3_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr1_1p15: regulator-rtmr1-1p15 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR1_1P15"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1150000>; + + gpio = <&tlmm 188 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb1_pwr_1p15_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr1_1p8: regulator-rtmr1-1p8 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR1_1P8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + gpio = <&tlmm 175 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb1_pwr_1p8_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr1_3p3: regulator-rtmr1-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR1_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 186 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb1_pwr_3p3_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr2_1p15: regulator-rtmr2-1p15 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR2_1P15"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1150000>; + + gpio = <&tlmm 189 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb2_pwr_1p15_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr2_1p8: regulator-rtmr2-1p8 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR2_1P8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + gpio = <&tlmm 126 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb2_pwr_1p8_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr2_3p3: regulator-rtmr2-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR2_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 187 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb2_pwr_3p3_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + regulator_usb3_vbus: regulator-usb3-vbus { + compatible = "regulator-fixed"; + regulator-name = "USB3_VBUS"; + gpio = <&pm8550ve_9_gpios 4 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&usb3_en>; + pinctrl-names = "default"; + enable-active-high; + regulator-always-on; + }; + + regulator_usb6_vbus: regulator-usb6-vbus { + compatible = "regulator-fixed"; + regulator-name = "USB6_VBUS"; + gpio = <&pm8550ve_9_gpios 5 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&usb6_en>; + pinctrl-names = "default"; + enable-active-high; + regulator-always-on; + }; + + vph_pwr: regulator-vph-pwr { + compatible = "regulator-fixed"; + + regulator-name = "vph_pwr"; + regulator-min-microvolt = <3700000>; + regulator-max-microvolt = <3700000>; + + regulator-always-on; + regulator-boot-on; + }; + + /* + * TODO: These two regulators are actually part of the removable M.2 + * card and not the EVK mainboard. Need to describe this differently. + * Functionally it works correctly, because all we need to do is to + * turn on the actual 3.3V supply above. + */ + vreg_wcn_0p95: regulator-wcn-0p95 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_0P95"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <950000>; + + vin-supply = <&vreg_wcn_3p3>; + }; + + vreg_wcn_1p9: regulator-wcn-1p9 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_1P9"; + regulator-min-microvolt = <1900000>; + regulator-max-microvolt = <1900000>; + + vin-supply = <&vreg_wcn_3p3>; + }; + + vreg_wcn_3p3: regulator-wcn-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 214 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&wcn_sw_en>; + pinctrl-names = "default"; + + regulator-always-on; + regulator-boot-on; + }; + + vreg_wwan: regulator-wwan { + compatible = "regulator-fixed"; + + regulator-name = "SDX_VPH_PWR"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 221 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&wwan_sw_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + sound { + compatible = "qcom,x1e80100-sndcard"; + model = "X1E80100-EVK"; + audio-routing = "WooferLeft IN", "WSA WSA_SPK1 OUT", + "TweeterLeft IN", "WSA WSA_SPK2 OUT", + "WooferRight IN", "WSA2 WSA_SPK2 OUT", + "TweeterRight IN", "WSA2 WSA_SPK2 OUT", + "IN1_HPHL", "HPHL_OUT", + "IN2_HPHR", "HPHR_OUT", + "AMIC2", "MIC BIAS2", + "VA DMIC0", "MIC BIAS3", + "VA DMIC1", "MIC BIAS3", + "VA DMIC2", "MIC BIAS1", + "VA DMIC3", "MIC BIAS1", + "TX SWR_INPUT1", "ADC2_OUTPUT"; + + wcd-playback-dai-link { + link-name = "WCD Playback"; + + codec { + sound-dai = <&wcd938x 0>, <&swr1 0>, <&lpass_rxmacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai RX_CODEC_DMA_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + wcd-capture-dai-link { + link-name = "WCD Capture"; + + codec { + sound-dai = <&wcd938x 1>, <&swr2 1>, <&lpass_txmacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai TX_CODEC_DMA_TX_3>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + wsa-dai-link { + link-name = "WSA Playback"; + + codec { + sound-dai = <&left_woofer>, + <&left_tweeter>, + <&swr0 0>, + <&lpass_wsamacro 0>, + <&right_woofer>, + <&right_tweeter>, + <&swr3 0>, + <&lpass_wsa2macro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai WSA_CODEC_DMA_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + va-dai-link { + link-name = "VA Capture"; + + codec { + sound-dai = <&lpass_vamacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai VA_CODEC_DMA_TX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + }; + + wcn7850-pmu { + compatible = "qcom,wcn7850-pmu"; + + vdd-supply = <&vreg_wcn_0p95>; + vddio-supply = <&vreg_l15b_1p8>; + vddaon-supply = <&vreg_wcn_0p95>; + vdddig-supply = <&vreg_wcn_0p95>; + vddrfa1p2-supply = <&vreg_wcn_1p9>; + vddrfa1p8-supply = <&vreg_wcn_1p9>; + + bt-enable-gpios = <&tlmm 116 GPIO_ACTIVE_HIGH>; + wlan-enable-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&wcn_bt_en>, <&wcn_wlan_en>; + pinctrl-names = "default"; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p8: ldo7 { + regulator-name = "vreg_pmu_rfa_1p8"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; +}; + +&i2c1 { + clock-frequency = <400000>; + status = "okay"; + + typec-mux@8 { + compatible = "parade,ps8830"; + reg = <0x08>; + + clocks = <&rpmhcc RPMH_RF_CLK5>; + + vdd-supply = <&vreg_rtmr2_1p15>; + vdd33-supply = <&vreg_rtmr2_3p3>; + vdd33-cap-supply = <&vreg_rtmr2_3p3>; + vddar-supply = <&vreg_rtmr2_1p15>; + vddat-supply = <&vreg_rtmr2_1p15>; + vddio-supply = <&vreg_rtmr2_1p8>; + + reset-gpios = <&tlmm 185 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&rtmr2_default>; + pinctrl-names = "default"; + + orientation-switch; + retimer-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + retimer_ss2_ss_out: endpoint { + remote-endpoint = <&pmic_glink_ss2_ss_in>; + }; + }; + + port@1 { + reg = <1>; + + retimer_ss2_ss_in: endpoint { + remote-endpoint = <&usb_1_ss2_qmpphy_out>; + }; + }; + + port@2 { + reg = <2>; + + retimer_ss2_con_sbu_out: endpoint { + remote-endpoint = <&pmic_glink_ss2_con_sbu_in>; + }; + }; + }; + }; +}; + +&i2c3 { + clock-frequency = <400000>; + + status = "okay"; + + typec-mux@8 { + compatible = "parade,ps8830"; + reg = <0x8>; + + clocks = <&rpmhcc RPMH_RF_CLK4>; + + vdd-supply = <&vreg_rtmr0_1p15>; + vdd33-supply = <&vreg_rtmr0_3p3>; + vdd33-cap-supply = <&vreg_rtmr0_3p3>; + vddar-supply = <&vreg_rtmr0_1p15>; + vddat-supply = <&vreg_rtmr0_1p15>; + vddio-supply = <&vreg_rtmr0_1p8>; + + reset-gpios = <&pm8550_gpios 10 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&rtmr0_default>; + pinctrl-names = "default"; + + retimer-switch; + orientation-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + retimer_ss0_ss_out: endpoint { + remote-endpoint = <&pmic_glink_ss0_ss_in>; + }; + }; + + port@1 { + reg = <1>; + + retimer_ss0_ss_in: endpoint { + remote-endpoint = <&usb_1_ss0_qmpphy_out>; + }; + }; + + port@2 { + reg = <2>; + + retimer_ss0_con_sbu_out: endpoint { + remote-endpoint = <&pmic_glink_ss0_con_sbu_in>; + }; + }; + }; + }; +}; + +&i2c5 { + clock-frequency = <400000>; + + status = "okay"; + + eusb3_repeater: redriver@47 { + compatible = "nxp,ptn3222"; + reg = <0x47>; + #phy-cells = <0>; + + vdd3v3-supply = <&vreg_l13b_3p0>; + vdd1v8-supply = <&vreg_l4b_1p8>; + + reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&eusb3_reset_n>; + pinctrl-names = "default"; + }; + + eusb5_repeater: redriver@43 { + compatible = "nxp,ptn3222"; + reg = <0x43>; + #phy-cells = <0>; + + vdd3v3-supply = <&vreg_l13b_3p0>; + vdd1v8-supply = <&vreg_l4b_1p8>; + + reset-gpios = <&tlmm 7 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&eusb5_reset_n>; + pinctrl-names = "default"; + }; + + eusb6_repeater: redriver@4f { + compatible = "nxp,ptn3222"; + reg = <0x4f>; + #phy-cells = <0>; + + vdd3v3-supply = <&vreg_l13b_3p0>; + vdd1v8-supply = <&vreg_l4b_1p8>; + + reset-gpios = <&tlmm 184 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&eusb6_reset_n>; + pinctrl-names = "default"; + }; +}; + +&i2c7 { + clock-frequency = <400000>; + + status = "okay"; + + typec-mux@8 { + compatible = "parade,ps8830"; + reg = <0x8>; + + clocks = <&rpmhcc RPMH_RF_CLK4>; + + vdd-supply = <&vreg_rtmr1_1p15>; + vdd33-supply = <&vreg_rtmr1_3p3>; + vdd33-cap-supply = <&vreg_rtmr1_3p3>; + vddar-supply = <&vreg_rtmr1_1p15>; + vddat-supply = <&vreg_rtmr1_1p15>; + vddio-supply = <&vreg_rtmr1_1p8>; + + reset-gpios = <&tlmm 176 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&rtmr1_default>; + pinctrl-names = "default"; + + retimer-switch; + orientation-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + retimer_ss1_ss_out: endpoint { + remote-endpoint = <&pmic_glink_ss1_ss_in>; + }; + }; + + port@1 { + reg = <1>; + + retimer_ss1_ss_in: endpoint { + remote-endpoint = <&usb_1_ss1_qmpphy_out>; + }; + }; + + port@2 { + reg = <2>; + + retimer_ss1_con_sbu_out: endpoint { + remote-endpoint = <&pmic_glink_ss1_con_sbu_in>; + }; + }; + }; + }; +}; + +&lpass_tlmm { + spkr_0_sd_n_active: spkr-0-sd-n-active-state { + pins = "gpio12"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + spkr_1_sd_n_active: spkr-1-sd-n-active-state { + pins = "gpio13"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + spkr_2_sd_n_active: spkr-2-sd-n-active-state { + pins = "gpio17"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + spkr_3_sd_n_active: spkr-3-sd-n-active-state { + pins = "gpio18"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; +}; + +&lpass_vamacro { + pinctrl-0 = <&dmic01_default>, <&dmic23_default>; + pinctrl-names = "default"; + + vdd-micb-supply = <&vreg_l1b_1p8>; + qcom,dmic-sample-rate = <4800000>; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dp0 { + status = "okay"; +}; + +&mdss_dp0_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp1 { + status = "okay"; +}; + +&mdss_dp1_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp2 { + status = "okay"; +}; + +&mdss_dp2_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp3 { + /delete-property/ #sound-dai-cells; + + pinctrl-0 = <&edp0_hpd_default>; + pinctrl-names = "default"; + + status = "okay"; + + aux-bus { + panel { + compatible = "edp-panel"; + + backlight = <&backlight>; + power-supply = <&vreg_edp_3p3>; + + port { + edp_panel_in: endpoint { + remote-endpoint = <&mdss_dp3_out>; + }; + }; + }; + }; +}; + +&mdss_dp3_out { + data-lanes = <0 1 2 3>; + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; + + remote-endpoint = <&edp_panel_in>; +}; + +&mdss_dp3_phy { + vdda-phy-supply = <&vreg_l3j_0p8>; + vdda-pll-supply = <&vreg_l2j_1p2>; + + status = "okay"; +}; + +&pcie3_port0 { + vpcie12v-supply = <&vreg_pcie_12v>; + vpcie3v3-supply = <&vreg_pcie_3v3>; + vpcie3v3aux-supply = <&vreg_pcie_3v3_aux>; + + reset-gpios = <&tlmm 143 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 145 GPIO_ACTIVE_LOW>; +}; + +&pcie4_port0 { + reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; + + wifi@0 { + compatible = "pci17cb,1107"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + }; +}; + +&pcie5 { + vddpe-3v3-supply = <&vreg_wwan>; +}; + +&pcie5_port0 { + reset-gpios = <&tlmm 149 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 151 GPIO_ACTIVE_LOW>; +}; + +&pcie6a { + vddpe-3v3-supply = <&vreg_nvme>; +}; + +&pcie6a_port0 { + reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +}; + +&pm8550_gpios { + rtmr0_default: rtmr0-reset-n-active-state { + pins = "gpio10"; + function = "normal"; + power-source = <1>; /* 1.8V */ + bias-disable; + input-disable; + output-enable; + }; + + usb0_3p3_reg_en: usb0-3p3-reg-en-state { + pins = "gpio11"; + function = "normal"; + power-source = <1>; /* 1.8V */ + bias-disable; + input-disable; + output-enable; + }; +}; + +&pm8550ve_8_gpios { + pcie_x8_12v: pcie-12v-default-state { + pins = "gpio8"; + function = "normal"; + output-enable; + output-high; + bias-pull-down; + power-source = <0>; + }; +}; + +&pm8550ve_9_gpios { + usb0_1p8_reg_en: usb0-1p8-reg-en-state { + pins = "gpio8"; + function = "normal"; + power-source = <1>; /* 1.8V */ + bias-disable; + input-disable; + output-enable; + }; + + usb3_en: usb3-en-state { + pins = "gpio4"; + function = "normal"; + qcom,drive-strength = ; + output-enable; + power-source = <0>; + }; + + usb6_en: usb6-en-state { + pins = "gpio5"; + function = "normal"; + qcom,drive-strength = ; + output-enable; + power-source = <0>; + }; +}; + +&pm8550_pwm { + status = "okay"; + + multi-led { + color = ; + function = LED_FUNCTION_STATUS; + + #address-cells = <1>; + #size-cells = <0>; + + led@1 { + reg = <1>; + color = ; + }; + + led@2 { + reg = <2>; + color = ; + }; + }; +}; + +&pmc8380_3_gpios { + edp_bl_en: edp-bl-en-state { + pins = "gpio4"; + function = "normal"; + power-source = <1>; + input-disable; + output-enable; + }; + + edp_bl_reg_en: edp-bl-reg-en-state { + pins = "gpio10"; + function = "normal"; + }; + + pm_sde7_aux_3p3_en: pcie-aux-3p3-default-state { + pins = "gpio8"; + function = "normal"; + output-enable; + bias-pull-down; + power-source = <0>; + }; + + pm_sde7_main_3p3_en: pcie-main-3p3-default-state { + pins = "gpio6"; + function = "normal"; + output-enable; + bias-pull-down; + power-source = <0>; + }; +}; + +&pmc8380_5_gpios { + usb0_pwr_1p15_reg_en: usb0-pwr-1p15-reg-en-state { + pins = "gpio8"; + function = "normal"; + power-source = <1>; /* 1.8V */ + bias-disable; + input-disable; + output-enable; + }; +}; + +&pmk8550_gpios { + edp_bl_pwm: edp-bl-pwm-state { + pins = "gpio5"; + function = "func3"; + }; +}; + +&pmk8550_pwm { + status = "okay"; +}; + +&sdhc_2 { + cd-gpios = <&tlmm 71 GPIO_ACTIVE_LOW>; + + vmmc-supply = <&vreg_l9b_2p9>; + vqmmc-supply = <&vreg_l6b_1p8>; + + no-sdio; + no-mmc; + + pinctrl-0 = <&sdc2_default &sdc2_card_det_n>; + pinctrl-1 = <&sdc2_sleep &sdc2_card_det_n>; + pinctrl-names = "default", "sleep"; + + status = "okay"; +}; + +&smb2360_0 { + status = "okay"; +}; + +&smb2360_0_eusb2_repeater { + vdd18-supply = <&vreg_l3d_1p8>; + vdd3-supply = <&vreg_l2b_3p0>; +}; + +&smb2360_1 { + status = "okay"; +}; + +&smb2360_1_eusb2_repeater { + vdd18-supply = <&vreg_l3d_1p8>; + vdd3-supply = <&vreg_l14b_3p0>; +}; + +&smb2360_2 { + status = "okay"; +}; + +&smb2360_2_eusb2_repeater { + vdd18-supply = <&vreg_l3d_1p8>; + vdd3-supply = <&vreg_l8b_3p0>; +}; + +&spi11 { + status = "okay"; + + tpm@0 { + compatible = "st,st33htpm-spi", "tcg,tpm_tis-spi"; + reg = <0>; + spi-max-frequency = <20000000>; + }; +}; + +&swr0 { + status = "okay"; + + pinctrl-0 = <&wsa_swr_active>; + pinctrl-names = "default"; + + /* WSA8845, Left Woofer */ + left_woofer: speaker@0,0 { + compatible = "sdw20217020400"; + pinctrl-0 = <&spkr_0_sd_n_active>; + pinctrl-names = "default"; + reg = <0 0>; + reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "WooferLeft"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l12b_1p2>; + qcom,port-mapping = <1 2 3 7 10 13>; + }; + + /* WSA8845, Left Tweeter */ + left_tweeter: speaker@0,1 { + compatible = "sdw20217020400"; + pinctrl-0 = <&spkr_1_sd_n_active>; + pinctrl-names = "default"; + reg = <0 1>; + reset-gpios = <&lpass_tlmm 13 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "TweeterLeft"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l12b_1p2>; + qcom,port-mapping = <4 5 6 7 11 13>; + }; +}; + +&swr1 { + status = "okay"; + + /* WCD9385 RX */ + wcd_rx: codec@0,4 { + compatible = "sdw20217010d00"; + reg = <0 4>; + qcom,rx-port-mapping = <1 2 3 4 5>; + }; +}; + +&swr2 { + status = "okay"; + + /* WCD9385 TX */ + wcd_tx: codec@0,3 { + compatible = "sdw20217010d00"; + reg = <0 3>; + qcom,tx-port-mapping = <2 2 3 4>; + }; +}; + +&swr3 { + status = "okay"; + + pinctrl-0 = <&wsa2_swr_active>; + pinctrl-names = "default"; + + /* WSA8845, Right Woofer */ + right_woofer: speaker@0,0 { + compatible = "sdw20217020400"; + pinctrl-0 = <&spkr_2_sd_n_active>; + pinctrl-names = "default"; + reg = <0 0>; + reset-gpios = <&lpass_tlmm 17 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "WooferRight"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l12b_1p2>; + qcom,port-mapping = <1 2 3 7 10 13>; + }; + + /* WSA8845, Right Tweeter */ + right_tweeter: speaker@0,1 { + compatible = "sdw20217020400"; + pinctrl-0 = <&spkr_3_sd_n_active>; + pinctrl-names = "default"; + reg = <0 1>; + reset-gpios = <&lpass_tlmm 18 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "TweeterRight"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l12b_1p2>; + qcom,port-mapping = <4 5 6 7 11 13>; + }; +}; + +&tlmm { + edp_reg_en: edp-reg-en-state { + pins = "gpio70"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + eusb3_reset_n: eusb3-reset-n-state { + pins = "gpio6"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-low; + }; + + eusb5_reset_n: eusb5-reset-n-state { + pins = "gpio7"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + output-low; + }; + + eusb6_reset_n: eusb6-reset-n-state { + pins = "gpio184"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + output-low; + }; + + nvme_reg_en: nvme-reg-en-state { + pins = "gpio18"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + rtmr1_default: rtmr1-reset-n-active-state { + pins = "gpio176"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + rtmr2_default: rtmr2-reset-n-active-state { + pins = "gpio185"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + sdc2_card_det_n: sd-card-det-n-state { + pins = "gpio71"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + usb1_pwr_1p15_reg_en: usb1-pwr-1p15-reg-en-state { + pins = "gpio188"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + usb1_pwr_1p8_reg_en: usb1-pwr-1p8-reg-en-state { + pins = "gpio175"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + usb1_pwr_3p3_reg_en: usb1-pwr-3p3-reg-en-state { + pins = "gpio186"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + usb2_pwr_1p15_reg_en: usb2-pwr-1p15-reg-en-state { + pins = "gpio189"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + usb2_pwr_1p8_reg_en: usb2-pwr-1p8-reg-en-state { + pins = "gpio126"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + usb2_pwr_3p3_reg_en: usb2-pwr-3p3-reg-en-state { + pins = "gpio187"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + usb_1_ss0_sbu_default: usb-1-ss0-sbu-state { + mode-pins { + pins = "gpio166"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + output-high; + }; + + oe-n-pins { + pins = "gpio168"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + sel-pins { + pins = "gpio167"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + }; + + wcd_default: wcd-reset-n-active-state { + pins = "gpio191"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; + + wcn_bt_en: wcn-bt-en-state { + pins = "gpio116"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wcn_wlan_en: wcn-wlan-en-state { + pins = "gpio117"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wwan_sw_en: wwan-sw-en-state { + pins = "gpio221"; + function = "gpio"; + drive-strength = <4>; + bias-disable; + }; + + wcn_sw_en: wcn-sw-en-state { + pins = "gpio214"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + /* Switches USB signal routing between the USB connector and the Wi-Fi card. */ + wcn_usb_sw_n: wcn-usb-sw-n-state { + pins = "gpio225"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-high; + }; +}; + +&uart14 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn7850-bt"; + max-speed = <3200000>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + }; +}; + +&uart21 { + compatible = "qcom,geni-debug-uart"; + + status = "okay"; +}; + +&ufs_mem_hc { + reset-gpios = <&tlmm 238 GPIO_ACTIVE_LOW>; + + vcc-supply = <&vreg_l17b_2p5>; + vcc-max-microamp = <1300000>; + vccq-supply = <&vreg_l2i_1p2>; + vccq-max-microamp = <1200000>; + + status = "okay"; +}; + +&ufs_mem_phy { + vdda-phy-supply = <&vreg_l3i_0p8>; + vdda-pll-supply = <&vreg_l3e_1p2>; + + status = "okay"; +}; + +&usb_1_ss0_dwc3_hs { + remote-endpoint = <&pmic_glink_ss0_hs_in>; +}; + +&usb_1_ss0_hsphy { + phys = <&smb2360_0_eusb2_repeater>; +}; + +&usb_1_ss0_qmpphy_out { + remote-endpoint = <&retimer_ss0_ss_in>; +}; + +&usb_1_ss1_dwc3_hs { + remote-endpoint = <&pmic_glink_ss1_hs_in>; +}; + +&usb_1_ss1_hsphy { + phys = <&smb2360_1_eusb2_repeater>; +}; + +&usb_1_ss1_qmpphy_out { + remote-endpoint = <&retimer_ss1_ss_in>; +}; + +&usb_1_ss2_dwc3_hs { + remote-endpoint = <&pmic_glink_ss2_hs_in>; +}; + +&usb_1_ss2_hsphy { + phys = <&smb2360_2_eusb2_repeater>; +}; + +&usb_1_ss2_qmpphy_out { + remote-endpoint = <&retimer_ss2_ss_in>; +}; + +&usb_2_hsphy { + phys = <&eusb5_repeater>; + + pinctrl-0 = <&wcn_usb_sw_n>; + pinctrl-names = "default"; +}; + +&usb_mp_hsphy0 { + phys = <&eusb3_repeater>; +}; + +&usb_mp_hsphy1 { + phys = <&eusb6_repeater>; +}; diff --git a/arch/arm64/boot/dts/qcom/purwa-iot-som.dtsi b/arch/arm64/boot/dts/qcom/purwa-iot-som.dtsi new file mode 100644 index 000000000000..394e65518ac5 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/purwa-iot-som.dtsi @@ -0,0 +1,677 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include "purwa.dtsi" +#include "hamoa-pmics.dtsi" +#include +#include + +/delete-node/ &pmc8380_6; +/delete-node/ &pmc8380_6_thermal; + +/ { + reserved-memory { + linux,cma { + compatible = "shared-dma-pool"; + size = <0x0 0x8000000>; + reusable; + linux,cma-default; + }; + }; +}; + +&apps_rsc { + /* PMC8380C_B */ + regulators-0 { + compatible = "qcom,pm8550-rpmh-regulators"; + qcom,pmic-id = "b"; + + vdd-bob1-supply = <&vph_pwr>; + vdd-bob2-supply = <&vph_pwr>; + vdd-l1-l4-l10-supply = <&vreg_s4c_1p8>; + vdd-l2-l13-l14-supply = <&vreg_bob1>; + vdd-l5-l16-supply = <&vreg_bob1>; + vdd-l6-l7-supply = <&vreg_bob2>; + vdd-l8-l9-supply = <&vreg_bob1>; + vdd-l12-supply = <&vreg_s5j_1p2>; + vdd-l15-supply = <&vreg_s4c_1p8>; + vdd-l17-supply = <&vreg_bob2>; + + vreg_bob1: bob1 { + regulator-name = "vreg_bob1"; + regulator-min-microvolt = <3008000>; + regulator-max-microvolt = <3960000>; + regulator-initial-mode = ; + }; + + vreg_bob2: bob2 { + regulator-name = "vreg_bob2"; + regulator-min-microvolt = <2504000>; + regulator-max-microvolt = <3008000>; + regulator-initial-mode = ; + }; + + vreg_l1b_1p8: ldo1 { + regulator-name = "vreg_l1b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l2b_3p0: ldo2 { + regulator-name = "vreg_l2b_3p0"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3100000>; + regulator-initial-mode = ; + }; + + vreg_l4b_1p8: ldo4 { + regulator-name = "vreg_l4b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l5b_3p0: ldo5 { + regulator-name = "vreg_l5b_3p0"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-initial-mode = ; + }; + + vreg_l6b_1p8: ldo6 { + regulator-name = "vreg_l6b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2960000>; + regulator-initial-mode = ; + }; + + vreg_l7b_2p8: ldo7 { + regulator-name = "vreg_l7b_2p8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-initial-mode = ; + }; + + vreg_l8b_3p0: ldo8 { + regulator-name = "vreg_l8b_3p0"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + + vreg_l9b_2p9: ldo9 { + regulator-name = "vreg_l9b_2p9"; + regulator-min-microvolt = <2960000>; + regulator-max-microvolt = <2960000>; + regulator-initial-mode = ; + }; + + vreg_l10b_1p8: ldo10 { + regulator-name = "vreg_l10b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l12b_1p2: ldo12 { + regulator-name = "vreg_l12b_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + regulator-always-on; + }; + + vreg_l13b_3p0: ldo13 { + regulator-name = "vreg_l13b_3p0"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3100000>; + regulator-initial-mode = ; + }; + + vreg_l14b_3p0: ldo14 { + regulator-name = "vreg_l14b_3p0"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + + vreg_l15b_1p8: ldo15 { + regulator-name = "vreg_l15b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + regulator-always-on; + }; + + vreg_l16b_2p9: ldo16 { + regulator-name = "vreg_l16b_2p9"; + regulator-min-microvolt = <2912000>; + regulator-max-microvolt = <2912000>; + regulator-initial-mode = ; + }; + + vreg_l17b_2p5: ldo17 { + regulator-name = "vreg_l17b_2p5"; + regulator-min-microvolt = <2504000>; + regulator-max-microvolt = <2504000>; + regulator-initial-mode = ; + }; + }; + + /* PMC8380VE_C */ + regulators-1 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + qcom,pmic-id = "c"; + + vdd-l1-supply = <&vreg_s5j_1p2>; + vdd-l2-supply = <&vreg_s1f_0p7>; + vdd-l3-supply = <&vreg_s1f_0p7>; + vdd-s4-supply = <&vph_pwr>; + + vreg_s4c_1p8: smps4 { + regulator-name = "vreg_s4c_1p8"; + regulator-min-microvolt = <1856000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = ; + }; + + vreg_l1c_1p2: ldo1 { + regulator-name = "vreg_l1c_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + + vreg_l2c_0p8: ldo2 { + regulator-name = "vreg_l2c_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + + vreg_l3c_0p8: ldo3 { + regulator-name = "vreg_l3c_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + }; + + /* PMC8380_D */ + regulators-2 { + compatible = "qcom,pmc8380-rpmh-regulators"; + qcom,pmic-id = "d"; + + vdd-l1-supply = <&vreg_s1f_0p7>; + vdd-l2-supply = <&vreg_s1f_0p7>; + vdd-l3-supply = <&vreg_s4c_1p8>; + vdd-s1-supply = <&vph_pwr>; + + vreg_l1d_0p8: ldo1 { + regulator-name = "vreg_l1d_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + + vreg_l2d_0p9: ldo2 { + regulator-name = "vreg_l2d_0p9"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + + vreg_l3d_1p8: ldo3 { + regulator-name = "vreg_l3d_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + }; + + /* PMC8380_E */ + regulators-3 { + compatible = "qcom,pmc8380-rpmh-regulators"; + qcom,pmic-id = "e"; + + vdd-l2-supply = <&vreg_s1f_0p7>; + vdd-l3-supply = <&vreg_s5j_1p2>; + + vreg_l2e_0p8: ldo2 { + regulator-name = "vreg_l2e_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + + vreg_l3e_1p2: ldo3 { + regulator-name = "vreg_l3e_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + }; + + /* PMC8380_F */ + regulators-4 { + compatible = "qcom,pmc8380-rpmh-regulators"; + qcom,pmic-id = "f"; + + vdd-l1-supply = <&vreg_s5j_1p2>; + vdd-l2-supply = <&vreg_s5j_1p2>; + vdd-l3-supply = <&vreg_s5j_1p2>; + vdd-s1-supply = <&vph_pwr>; + + vreg_s1f_0p7: smps1 { + regulator-name = "vreg_s1f_0p7"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1100000>; + regulator-initial-mode = ; + }; + + vreg_l1f_1p0: ldo1 { + regulator-name = "vreg_l1f_1p0"; + regulator-min-microvolt = <1024000>; + regulator-max-microvolt = <1024000>; + regulator-initial-mode = ; + }; + + vreg_l2f_1p0: ldo2 { + regulator-name = "vreg_l2f_1p0"; + regulator-min-microvolt = <1024000>; + regulator-max-microvolt = <1024000>; + regulator-initial-mode = ; + }; + + vreg_l3f_1p0: ldo3 { + regulator-name = "vreg_l3f_1p0"; + regulator-min-microvolt = <1024000>; + regulator-max-microvolt = <1024000>; + regulator-initial-mode = ; + }; + }; + + /* PMC8380VE_I */ + regulators-6 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + qcom,pmic-id = "i"; + + vdd-l1-supply = <&vreg_s4c_1p8>; + vdd-l2-supply = <&vreg_s5j_1p2>; + vdd-l3-supply = <&vreg_s1f_0p7>; + vdd-s1-supply = <&vph_pwr>; + vdd-s2-supply = <&vph_pwr>; + + vreg_s1i_0p9: smps1 { + regulator-name = "vreg_s1i_0p9"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + + vreg_s2i_1p0: smps2 { + regulator-name = "vreg_s2i_1p0"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1100000>; + regulator-initial-mode = ; + }; + + vreg_l1i_1p8: ldo1 { + regulator-name = "vreg_l1i_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l2i_1p2: ldo2 { + regulator-name = "vreg_l2i_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + + vreg_l3i_0p8: ldo3 { + regulator-name = "vreg_l3i_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + }; + + /* PMC8380VE_J */ + regulators-7 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + qcom,pmic-id = "j"; + + vdd-l1-supply = <&vreg_s1f_0p7>; + vdd-l2-supply = <&vreg_s5j_1p2>; + vdd-l3-supply = <&vreg_s1f_0p7>; + vdd-s5-supply = <&vph_pwr>; + + vreg_s5j_1p2: smps5 { + regulator-name = "vreg_s5j_1p2"; + regulator-min-microvolt = <1256000>; + regulator-max-microvolt = <1304000>; + regulator-initial-mode = ; + }; + + vreg_l1j_0p8: ldo1 { + regulator-name = "vreg_l1j_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + + vreg_l2j_1p2: ldo2 { + regulator-name = "vreg_l2j_1p2"; + regulator-min-microvolt = <1256000>; + regulator-max-microvolt = <1256000>; + regulator-initial-mode = ; + }; + + vreg_l3j_0p8: ldo3 { + regulator-name = "vreg_l3j_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + }; +}; + +&gpu { + status = "okay"; +}; + +&gpu_zap_shader { + firmware-name = "qcom/x1p42100/gen71500_zap.mbn"; +}; + +&pcie3 { + pinctrl-0 = <&pcie3_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie3_phy { + vdda-phy-supply = <&vreg_l3c_0p8>; + vdda-pll-supply = <&vreg_l3e_1p2>; + + status = "okay"; +}; + +&pcie4 { + pinctrl-0 = <&pcie4_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie4_phy { + vdda-phy-supply = <&vreg_l3i_0p8>; + vdda-pll-supply = <&vreg_l3e_1p2>; + + status = "okay"; +}; + +&pcie5 { + pinctrl-0 = <&pcie5_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie5_phy { + vdda-phy-supply = <&vreg_l3i_0p8>; + vdda-pll-supply = <&vreg_l3e_1p2>; + + status = "okay"; +}; + +&pcie6a { + pinctrl-0 = <&pcie6a_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie6a_phy { + vdda-phy-supply = <&vreg_l1d_0p8>; + vdda-pll-supply = <&vreg_l2j_1p2>; + + status = "okay"; +}; + +&qupv3_0 { + status = "okay"; +}; + +&qupv3_1 { + status = "okay"; +}; + +&qupv3_2 { + status = "okay"; +}; + +&remoteproc_adsp { + firmware-name = "qcom/x1e80100/adsp.mbn", + "qcom/x1e80100/adsp_dtb.mbn"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/x1e80100/cdsp.mbn", + "qcom/x1e80100/cdsp_dtb.mbn"; + + status = "okay"; +}; + +&tlmm { + gpio-reserved-ranges = <34 2>; /* TPM LP & INT */ + + pcie3_default: pcie3-default-state { + clkreq-n-pins { + pins = "gpio144"; + function = "pcie3_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio143"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio145"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie4_default: pcie4-default-state { + clkreq-n-pins { + pins = "gpio147"; + function = "pcie4_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio146"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio148"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie5_default: pcie5-default-state { + clkreq-n-pins { + pins = "gpio150"; + function = "pcie5_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio149"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio151"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie6a_default: pcie6a-default-state { + clkreq-n-pins { + pins = "gpio153"; + function = "pcie6a_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio152"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio154"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + + }; + }; +}; + +&usb_1_ss0 { + dr_mode = "otg"; + usb-role-switch; + + status = "okay"; +}; + +&usb_1_ss0_hsphy { + vdd-supply = <&vreg_l3j_0p8>; + vdda12-supply = <&vreg_l2j_1p2>; + + status = "okay"; +}; + +&usb_1_ss0_qmpphy { + vdda-phy-supply = <&vreg_l2j_1p2>; + vdda-pll-supply = <&vreg_l1j_0p8>; + + status = "okay"; +}; + +&usb_1_ss1 { + dr_mode = "otg"; + usb-role-switch; + + status = "okay"; +}; + +&usb_1_ss1_hsphy { + vdd-supply = <&vreg_l3j_0p8>; + vdda12-supply = <&vreg_l2j_1p2>; + + status = "okay"; +}; + +&usb_1_ss1_qmpphy { + vdda-phy-supply = <&vreg_l2j_1p2>; + vdda-pll-supply = <&vreg_l2d_0p9>; + + status = "okay"; +}; + +&usb_1_ss2 { + dr_mode = "otg"; + usb-role-switch; + + status = "okay"; +}; + +&usb_1_ss2_hsphy { + vdd-supply = <&vreg_l3j_0p8>; + vdda12-supply = <&vreg_l2j_1p2>; + + status = "okay"; +}; + +&usb_1_ss2_qmpphy { + vdda-phy-supply = <&vreg_l2j_1p2>; + vdda-pll-supply = <&vreg_l2d_0p9>; + + status = "okay"; +}; + +&usb_2 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_2_hsphy { + vdd-supply = <&vreg_l2e_0p8>; + vdda12-supply = <&vreg_l3e_1p2>; + + status = "okay"; +}; + +&usb_mp { + status = "okay"; +}; + +&usb_mp_hsphy0 { + vdd-supply = <&vreg_l2e_0p8>; + vdda12-supply = <&vreg_l3e_1p2>; + + status = "okay"; +}; + +&usb_mp_hsphy1 { + vdd-supply = <&vreg_l2e_0p8>; + vdda12-supply = <&vreg_l3e_1p2>; + + status = "okay"; +}; + +&usb_mp_qmpphy0 { + vdda-phy-supply = <&vreg_l3e_1p2>; + vdda-pll-supply = <&vreg_l3c_0p8>; + + status = "okay"; +}; + +&usb_mp_qmpphy1 { + vdda-phy-supply = <&vreg_l3e_1p2>; + vdda-pll-supply = <&vreg_l3c_0p8>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/purwa.dtsi b/arch/arm64/boot/dts/qcom/purwa.dtsi index 2cecd2dd0de8..9ab4f26b35f2 100644 --- a/arch/arm64/boot/dts/qcom/purwa.dtsi +++ b/arch/arm64/boot/dts/qcom/purwa.dtsi @@ -20,7 +20,21 @@ /delete-node/ &gpu_opp_table; /delete-node/ &gpu_speed_bin; /delete-node/ &pcie3_phy; -/delete-node/ &thermal_zones; +/delete-node/ &thermal_aoss3; +/delete-node/ &thermal_cpu2_0_btm; +/delete-node/ &thermal_cpu2_0_top; +/delete-node/ &thermal_cpu2_1_btm; +/delete-node/ &thermal_cpu2_1_top; +/delete-node/ &thermal_cpu2_2_btm; +/delete-node/ &thermal_cpu2_2_top; +/delete-node/ &thermal_cpu2_3_btm; +/delete-node/ &thermal_cpu2_3_top; +/delete-node/ &thermal_cpuss2_btm; +/delete-node/ &thermal_cpuss2_top; +/delete-node/ &thermal_gpuss_4; +/delete-node/ &thermal_gpuss_5; +/delete-node/ &thermal_gpuss_6; +/delete-node/ &thermal_gpuss_7; &gcc { compatible = "qcom,x1p42100-gcc", "qcom,x1e80100-gcc"; @@ -198,557 +212,47 @@ }; }; +&thermal_camera0 { + thermal-sensors = <&tsens2 9>; +}; + +&thermal_camera1 { + thermal-sensors = <&tsens2 10>; +}; + +&thermal_gpuss_0 { + thermal-sensors = <&tsens2 5>; +}; + +&thermal_gpuss_1 { + thermal-sensors = <&tsens2 6>; +}; + +&thermal_gpuss_2 { + thermal-sensors = <&tsens2 7>; +}; + +&thermal_gpuss_3 { + thermal-sensors = <&tsens2 8>; +}; + +&thermal_nsp0 { + thermal-sensors = <&tsens2 1>; +}; + +&thermal_nsp1 { + thermal-sensors = <&tsens2 2>; +}; + +&thermal_nsp2 { + thermal-sensors = <&tsens2 3>; +}; + +&thermal_nsp3 { + thermal-sensors = <&tsens2 4>; +}; + /* While physically present, this controller is left unconfigured and unused */ &tsens3 { status = "disabled"; }; - -/ { - thermal-zones { - aoss0-thermal { - thermal-sensors = <&tsens0 0>; - - trips { - trip-point0 { - temperature = <90000>; - hysteresis = <2000>; - type = "hot"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu0-0-top-thermal { - thermal-sensors = <&tsens0 1>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu0-0-btm-thermal { - thermal-sensors = <&tsens0 2>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu0-1-top-thermal { - thermal-sensors = <&tsens0 3>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu0-1-btm-thermal { - thermal-sensors = <&tsens0 4>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu0-2-top-thermal { - thermal-sensors = <&tsens0 5>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu0-2-btm-thermal { - thermal-sensors = <&tsens0 6>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu0-3-top-thermal { - thermal-sensors = <&tsens0 7>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu0-3-btm-thermal { - thermal-sensors = <&tsens0 8>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpuss0-top-thermal { - thermal-sensors = <&tsens0 9>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpuss0-btm-thermal { - thermal-sensors = <&tsens0 10>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - mem-thermal { - thermal-sensors = <&tsens0 11>; - - trips { - trip-point0 { - temperature = <90000>; - hysteresis = <2000>; - type = "hot"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <0>; - type = "critical"; - }; - }; - }; - - video-thermal { - thermal-sensors = <&tsens0 12>; - - trips { - trip-point0 { - temperature = <90000>; - hysteresis = <2000>; - type = "hot"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - aoss1-thermal { - thermal-sensors = <&tsens1 0>; - - trips { - trip-point0 { - temperature = <90000>; - hysteresis = <2000>; - type = "hot"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu1-0-top-thermal { - thermal-sensors = <&tsens1 1>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu1-0-btm-thermal { - thermal-sensors = <&tsens1 2>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu1-1-top-thermal { - thermal-sensors = <&tsens1 3>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu1-1-btm-thermal { - thermal-sensors = <&tsens1 4>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu1-2-top-thermal { - thermal-sensors = <&tsens1 5>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu1-2-btm-thermal { - thermal-sensors = <&tsens1 6>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu1-3-top-thermal { - thermal-sensors = <&tsens1 7>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpu1-3-btm-thermal { - thermal-sensors = <&tsens1 8>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpuss1-top-thermal { - thermal-sensors = <&tsens1 9>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - cpuss1-btm-thermal { - thermal-sensors = <&tsens1 10>; - - trips { - trip-point0 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - aoss2-thermal { - thermal-sensors = <&tsens2 0>; - - trips { - trip-point0 { - temperature = <90000>; - hysteresis = <2000>; - type = "hot"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - nsp0-thermal { - thermal-sensors = <&tsens2 1>; - - trips { - trip-point0 { - temperature = <90000>; - hysteresis = <2000>; - type = "hot"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - nsp1-thermal { - thermal-sensors = <&tsens2 2>; - - trips { - trip-point0 { - temperature = <90000>; - hysteresis = <2000>; - type = "hot"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - nsp2-thermal { - thermal-sensors = <&tsens2 3>; - - trips { - trip-point0 { - temperature = <90000>; - hysteresis = <2000>; - type = "hot"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - nsp3-thermal { - thermal-sensors = <&tsens2 4>; - - trips { - trip-point0 { - temperature = <90000>; - hysteresis = <2000>; - type = "hot"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - gpuss-0-thermal { - polling-delay-passive = <200>; - - thermal-sensors = <&tsens2 5>; - - cooling-maps { - map0 { - trip = <&gpuss0_alert0>; - cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - - trips { - gpuss0_alert0: trip-point0 { - temperature = <95000>; - hysteresis = <1000>; - type = "passive"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - gpuss-1-thermal { - polling-delay-passive = <200>; - - thermal-sensors = <&tsens2 6>; - - cooling-maps { - map0 { - trip = <&gpuss1_alert0>; - cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - - trips { - gpuss1_alert0: trip-point0 { - temperature = <95000>; - hysteresis = <1000>; - type = "passive"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - gpuss-2-thermal { - polling-delay-passive = <200>; - - thermal-sensors = <&tsens2 7>; - - cooling-maps { - map0 { - trip = <&gpuss2_alert0>; - cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - - trips { - gpuss2_alert0: trip-point0 { - temperature = <95000>; - hysteresis = <1000>; - type = "passive"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - gpuss-3-thermal { - polling-delay-passive = <200>; - - thermal-sensors = <&tsens2 8>; - - cooling-maps { - map0 { - trip = <&gpuss3_alert0>; - cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - - trips { - gpuss3_alert0: trip-point0 { - temperature = <95000>; - hysteresis = <1000>; - type = "passive"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - camera0-thermal { - thermal-sensors = <&tsens2 9>; - - trips { - trip-point0 { - temperature = <90000>; - hysteresis = <2000>; - type = "hot"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - - camera1-thermal { - thermal-sensors = <&tsens2 10>; - - trips { - trip-point0 { - temperature = <90000>; - hysteresis = <2000>; - type = "hot"; - }; - - trip-point1 { - temperature = <115000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; - }; -}; diff --git a/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts b/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts index 455e5c9bb072..04cb9230d29f 100644 --- a/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts +++ b/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -626,6 +627,23 @@ }; }; +&camss { + vdda-phy-supply = <&vreg_l10c>; + vdda-pll-supply = <&vreg_l6b>; + + status = "okay"; + + ports { + port@3 { + csiphy3_ep: endpoint { + data-lanes = <0 1 2 3>; + bus-type = ; + remote-endpoint = <&camera_s5kjn1_ep>; + }; + }; + }; +}; + &cci0 { status = "okay"; }; @@ -666,7 +684,34 @@ }; &cci1_i2c1 { - /* S5KJN1SQ03 @ 10 */ + camera@10 { + compatible = "samsung,s5kjn1"; + reg = <0x10>; + + vdda-supply = <&vreg_l3p>; + vddd-supply = <&vreg_l2p>; + vddio-supply = <&vreg_l6p>; + + clocks = <&camcc CAM_CC_MCLK3_CLK>; + assigned-clocks = <&camcc CAM_CC_MCLK3_CLK>; + assigned-clock-rates = <24000000>; + + reset-gpios = <&tlmm 78 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&cam_mclk3_default>; + pinctrl-names = "default"; + + orientation = <0>; /* Front facing */ + rotation = <270>; + + port { + camera_s5kjn1_ep: endpoint { + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <700000000>; + remote-endpoint = <&csiphy3_ep>; + }; + }; + }; eeprom@51 { compatible = "giantec,gt24p128f", "atmel,24c128"; @@ -1257,41 +1302,6 @@ */ gpio-reserved-ranges = <32 2>, <56 4>; - bluetooth_enable_default: bluetooth-enable-default-state { - pins = "gpio85"; - function = "gpio"; - output-low; - bias-disable; - }; - - disp_reset_n_active: disp-reset-n-active-state { - pins = "gpio44"; - function = "gpio"; - drive-strength = <8>; - bias-disable; - }; - - disp_reset_n_suspend: disp-reset-n-suspend-state { - pins = "gpio44"; - function = "gpio"; - drive-strength = <2>; - bias-pull-down; - }; - - hall_sensor_default: hall-sensor-default-state { - pins = "gpio155"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - - mdp_vsync: mdp-vsync-state { - pins = "gpio80"; - function = "mdp_vsync"; - drive-strength = <2>; - bias-pull-down; - }; - pm8008_int_default: pm8008-int-default-state { pins = "gpio25"; function = "gpio"; @@ -1345,9 +1355,17 @@ bias-pull-up; }; - sw_ctrl_default: sw-ctrl-default-state { - pins = "gpio86"; + disp_reset_n_active: disp-reset-n-active-state { + pins = "gpio44"; function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + + disp_reset_n_suspend: disp-reset-n-suspend-state { + pins = "gpio44"; + function = "gpio"; + drive-strength = <2>; bias-pull-down; }; @@ -1359,12 +1377,39 @@ output-high; }; + mdp_vsync: mdp-vsync-state { + pins = "gpio80"; + function = "mdp_vsync"; + drive-strength = <2>; + bias-pull-down; + }; + + bluetooth_enable_default: bluetooth-enable-default-state { + pins = "gpio85"; + function = "gpio"; + output-low; + bias-disable; + }; + + sw_ctrl_default: sw-ctrl-default-state { + pins = "gpio86"; + function = "gpio"; + bias-pull-down; + }; + aw86927_int_default: aw86927-int-default-state { pins = "gpio101"; function = "gpio"; drive-strength = <2>; bias-pull-up; }; + + hall_sensor_default: hall-sensor-default-state { + pins = "gpio155"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; }; &uart5 { diff --git a/arch/arm64/boot/dts/qcom/qcm6490-idp.dts b/arch/arm64/boot/dts/qcom/qcm6490-idp.dts index b2f00e107643..bdc02260f902 100644 --- a/arch/arm64/boot/dts/qcom/qcm6490-idp.dts +++ b/arch/arm64/boot/dts/qcom/qcm6490-idp.dts @@ -36,6 +36,7 @@ aliases { serial0 = &uart5; + serial1 = &uart7; }; pm8350c_pwm_backlight: backlight { @@ -194,6 +195,63 @@ #sound-dai-cells = <1>; }; + + wcn6750-pmu { + compatible = "qcom,wcn6750-pmu"; + pinctrl-0 = <&bt_en>; + pinctrl-names = "default"; + vddaon-supply = <&vreg_s7b_0p972>; + vddasd-supply = <&vreg_l11c_2p8>; + vddpmu-supply = <&vreg_s7b_0p972>; + vddrfa0p8-supply = <&vreg_s7b_0p972>; + vddrfa1p2-supply = <&vreg_s8b_1p272>; + vddrfa1p7-supply = <&vreg_s1b_1p872>; + vddrfa2p2-supply = <&vreg_s1c_2p19>; + + bt-enable-gpios = <&tlmm 85 GPIO_ACTIVE_HIGH>; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p7: ldo7 { + regulator-name = "vreg_pmu_rfa_1p7"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; }; &apps_rsc { @@ -695,6 +753,39 @@ status = "okay"; }; +&qup_uart7_cts { + /* + * Configure a bias-bus-hold on CTS to lower power + * usage when Bluetooth is turned off. Bus hold will + * maintain a low power state regardless of whether + * the Bluetooth module drives the pin in either + * direction or leaves the pin fully unpowered. + */ + bias-bus-hold; +}; + +&qup_uart7_rts { + /* We'll drive RTS, so no pull */ + drive-strength = <2>; + bias-disable; +}; + +&qup_uart7_rx { + /* + * Configure a pull-up on RX. This is needed to avoid + * garbage data when the TX pin of the Bluetooth module is + * in tri-state (module powered off or not driving the + * signal yet). + */ + bias-pull-up; +}; + +&qup_uart7_tx { + /* We'll drive TX, so no pull */ + drive-strength = <2>; + bias-disable; +}; + &qupv3_id_0 { status = "okay"; }; @@ -920,6 +1011,59 @@ gpio-reserved-ranges = <32 2>, /* ADSP */ <48 4>; /* NFC */ + bt_en: bt-en-state { + pins = "gpio85"; + function = "gpio"; + output-low; + bias-disable; + }; + + qup_uart7_sleep_cts: qup-uart7-sleep-cts-state { + pins = "gpio28"; + function = "gpio"; + /* + * Configure a bias-bus-hold on CTS to lower power + * usage when Bluetooth is turned off. Bus hold will + * maintain a low power state regardless of whether + * the Bluetooth module drives the pin in either + * direction or leaves the pin fully unpowered. + */ + bias-bus-hold; + }; + + qup_uart7_sleep_rts: qup-uart7-sleep-rts-state { + pins = "gpio29"; + function = "gpio"; + /* + * Configure pull-down on RTS. As RTS is active low + * signal, pull it low to indicate the BT SoC that it + * can wakeup the system anytime from suspend state by + * pulling RX low (by sending wakeup bytes). + */ + bias-pull-down; + }; + + qup_uart7_sleep_rx: qup-uart7-sleep-rx-state { + pins = "gpio31"; + function = "gpio"; + /* + * Configure a pull-up on RX. This is needed to avoid + * garbage data when the TX pin of the Bluetooth module + * is floating which may cause spurious wakeups. + */ + bias-pull-up; + }; + + qup_uart7_sleep_tx: qup-uart7-sleep-tx-state { + pins = "gpio30"; + function = "gpio"; + /* + * Configure pull-up on TX when it isn't actively driven + * to prevent BT SoC from receiving garbage during sleep. + */ + bias-pull-up; + }; + sd_cd: sd-cd-state { pins = "gpio91"; function = "gpio"; @@ -938,6 +1082,31 @@ status = "okay"; }; +&uart7 { + /delete-property/ interrupts; + interrupts-extended = <&intc GIC_SPI 608 IRQ_TYPE_LEVEL_HIGH>, + <&tlmm 31 IRQ_TYPE_EDGE_FALLING>; + pinctrl-1 = <&qup_uart7_sleep_cts>, + <&qup_uart7_sleep_rts>, + <&qup_uart7_sleep_tx>, + <&qup_uart7_sleep_rx>; + pinctrl-names = "default", + "sleep"; + + status = "okay"; + + bluetooth: bluetooth { + compatible = "qcom,wcn6750-bt"; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddbtcmx-supply = <&vreg_pmu_btcmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + max-speed = <3200000>; + }; +}; + &ufs_mem_hc { reset-gpios = <&tlmm 175 GPIO_ACTIVE_LOW>; vcc-supply = <&vreg_l7b_2p952>; diff --git a/arch/arm64/boot/dts/qcom/qcs615-ride.dts b/arch/arm64/boot/dts/qcom/qcs615-ride.dts index 5a24c19c415e..7e05f873194a 100644 --- a/arch/arm64/boot/dts/qcom/qcs615-ride.dts +++ b/arch/arm64/boot/dts/qcom/qcs615-ride.dts @@ -91,16 +91,6 @@ regulator-always-on; }; - vreg_12p0: regulator-vreg-12p0 { - compatible = "regulator-fixed"; - regulator-name = "VREG_12P0"; - - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <12000000>; - regulator-max-microvolt = <12000000>; - }; - vreg_1p0: regulator-vreg-1p0 { compatible = "regulator-fixed"; regulator-name = "VREG_1P0"; @@ -121,8 +111,6 @@ regulator-boot-on; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; - - vin-supply = <&vreg_5p0>; }; vreg_3p0: regulator-vreg-3p0 { @@ -133,20 +121,6 @@ regulator-boot-on; regulator-min-microvolt = <3000000>; regulator-max-microvolt = <3000000>; - - vin-supply = <&vreg_12p0>; - }; - - vreg_5p0: regulator-vreg-5p0 { - compatible = "regulator-fixed"; - regulator-name = "VREG_5P0"; - - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - - vin-supply = <&vreg_12p0>; }; wcn6855-pmu { @@ -372,6 +346,14 @@ }; }; +&gpu { + status = "okay"; +}; + +&gpu_zap_shader { + firmware-name = "qcom/qcs615/a612_zap.mbn"; +}; + &i2c2 { clock-frequency = <400000>; status = "okay"; @@ -655,14 +637,12 @@ }; &usb_1 { + dr_mode = "peripheral"; + status = "okay"; }; -&usb_1_dwc3 { - dr_mode = "peripheral"; -}; - -&usb_hsphy_2 { +&usb_2_hsphy { vdd-supply = <&vreg_l5a>; vdda-pll-supply = <&vreg_l12a>; vdda-phy-dpdm-supply = <&vreg_l13a>; @@ -671,11 +651,9 @@ }; &usb_2 { - status = "okay"; -}; - -&usb_2_dwc3 { dr_mode = "host"; + + status = "okay"; }; &ufs_mem_hc { diff --git a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso index 619a42b5ef48..83908db335af 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso +++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine.dtso @@ -5,9 +5,37 @@ /dts-v1/; /plugin/; +#include #include #include +&{/} { + + vreg_0p9: regulator-0v9 { + compatible = "regulator-fixed"; + regulator-name = "VREG_0P9"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + + regulator-always-on; + regulator-boot-on; + }; + + vreg_1p8: regulator-1v8 { + compatible = "regulator-fixed"; + regulator-name = "VREG_1P8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + regulator-always-on; + regulator-boot-on; + }; +}; + +&remoteproc_wpss { + status = "disabled"; +}; + &spi11 { #address-cells = <1>; #size-cells = <0>; @@ -19,3 +47,244 @@ spi-max-frequency = <20000000>; }; }; + +&pcie0 { + perst-gpios = <&tlmm 87 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&pcie0_reset_n>, <&pcie0_wake_n>, <&pcie0_clkreq_n>; + pinctrl-names = "default"; + + iommu-map = <0x0 &apps_smmu 0x1c00 0x1>, + <0x100 &apps_smmu 0x1c01 0x1>, + <0x208 &apps_smmu 0x1c04 0x1>, + <0x210 &apps_smmu 0x1c05 0x1>, + <0x218 &apps_smmu 0x1c06 0x1>, + <0x300 &apps_smmu 0x1c07 0x1>, + <0x400 &apps_smmu 0x1c08 0x1>, + <0x500 &apps_smmu 0x1c09 0x1>, + <0x501 &apps_smmu 0x1c10 0x1>; + + status = "okay"; +}; + +&pcie0_phy { + vdda-phy-supply = <&vreg_l10c_0p88>; + vdda-pll-supply = <&vreg_l6b_1p2>; + + status = "okay"; +}; + +&pcie0_port { + #address-cells = <3>; + #size-cells = <2>; + + pcie@0,0 { + compatible = "pci1179,0623"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x2 0xff>; + + vddc-supply = <&vreg_0p9>; + vdd18-supply = <&vreg_1p8>; + vdd09-supply = <&vreg_0p9>; + vddio1-supply = <&vreg_1p8>; + vddio2-supply = <&vreg_1p8>; + vddio18-supply = <&vreg_1p8>; + + i2c-parent = <&i2c1 0x33>; + + resx-gpios = <&tlmm 78 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&pcie0_tc9563_resx_n>; + pinctrl-names = "default"; + + pcie@1,0 { + reg = <0x20800 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x3 0xff>; + }; + + pcie@2,0 { + reg = <0x21000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x4 0xff>; + }; + + pcie@3,0 { + reg = <0x21800 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + bus-range = <0x5 0xff>; + + pci@0,0 { + reg = <0x50000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + }; + + pci@0,1 { + reg = <0x50100 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + }; + }; + + }; +}; + +&pcie1 { + iommu-map = <0x0 &apps_smmu 0x1c80 0x1>, + <0x100 &apps_smmu 0x1c81 0x1>, + <0x208 &apps_smmu 0x1c84 0x1>, + <0x210 &apps_smmu 0x1c85 0x1>, + <0x218 &apps_smmu 0x1c86 0x1>, + <0x300 &apps_smmu 0x1c87 0x1>, + <0x408 &apps_smmu 0x1c90 0x1>, + <0x410 &apps_smmu 0x1c91 0x1>, + <0x418 &apps_smmu 0x1c92 0x1>, + <0x500 &apps_smmu 0x1c93 0x1>, + <0x600 &apps_smmu 0x1c94 0x1>, + <0x700 &apps_smmu 0x1c95 0x1>, + <0x701 &apps_smmu 0x1c96 0x1>, + <0x800 &apps_smmu 0x1c97 0x1>, + <0x900 &apps_smmu 0x1c98 0x1>, + <0x901 &apps_smmu 0x1c99 0x1>; +}; + +&pcie1_switch0_dsp1 { + #address-cells = <3>; + #size-cells = <2>; + + pcie@0,0 { + compatible = "pci1179,0623"; + reg = <0x30000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x2 0xff>; + + vddc-supply = <&vdd_ntn_0p9>; + vdd18-supply = <&vdd_ntn_1p8>; + vdd09-supply = <&vdd_ntn_0p9>; + vddio1-supply = <&vdd_ntn_1p8>; + vddio2-supply = <&vdd_ntn_1p8>; + vddio18-supply = <&vdd_ntn_1p8>; + + i2c-parent = <&i2c1 0x77>; + + resx-gpios = <&tlmm 124 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&pcie1_tc9563_resx_n>; + pinctrl-names = "default"; + + pcie@1,0 { + reg = <0x40800 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x3 0xff>; + }; + + pcie@2,0 { + reg = <0x41000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x4 0xff>; + }; + + pcie@3,0 { + reg = <0x41800 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + bus-range = <0x5 0xff>; + + pci@0,0 { + reg = <0x50000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + }; + + pci@0,1 { + reg = <0x50100 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + }; + }; + }; +}; + +&tlmm { + pcie0_tc9563_resx_n: pcie0-tc9563-resx-state { + pins = "gpio78"; + function = "gpio"; + bias-disable; + input-disable; + output-enable; + }; + + pcie0_reset_n: pcie0-reset-n-state { + pins = "gpio87"; + function = "gpio"; + drive-strength = <16>; + output-low; + bias-disable; + }; + + pcie0_clkreq_n: pcie0-clkreq-n-state { + pins = "gpio88"; + function = "pcie0_clkreqn"; + drive-strength = <2>; + bias-pull-up; + }; + + pcie0_wake_n: pcie0-wake-n-state { + pins = "gpio89"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + pcie1_tc9563_resx_n: pcie1-tc9563-resx-state { + pins = "gpio124"; + function = "gpio"; + bias-disable; + input-disable; + output-enable; + }; + +}; + +&wifi { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts index e3d2f01881ae..e393ccf1884a 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts +++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts @@ -262,6 +262,28 @@ }; }; + vreg_pcie0_1p05: regulator-pcie0-1p05v { + compatible = "regulator-fixed"; + regulator-name = "PCIE0_1.05V"; + gpio = <&pm7250b_gpios 4 GPIO_ACTIVE_HIGH>; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + enable-active-high; + pinctrl-0 = <&upd_pwr_en2_state>; + pinctrl-names = "default"; + }; + + vreg_pcie0_3p3: regulator-pcie0-3p3v-dual { + compatible = "regulator-fixed"; + regulator-name = "PCIE0_3.3V_Dual"; + gpio = <&pm7250b_gpios 1 GPIO_ACTIVE_HIGH>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + enable-active-high; + pinctrl-0 = <&upd_pwr_en1_state>; + pinctrl-names = "default"; + }; + vdd_ntn_0p9: regulator-vdd-ntn-0p9 { compatible = "regulator-fixed"; regulator-name = "VDD_NTN_0P9"; @@ -852,7 +874,7 @@ pinctrl-0 = <&tc9563_resx_n>; pinctrl-names = "default"; - pcie@1,0 { + pcie1_switch0_dsp1: pcie@1,0 { reg = <0x20800 0x0 0x0 0x0 0x0>; #address-cells = <3>; #size-cells = <2>; @@ -870,6 +892,41 @@ device_type = "pci"; ranges; bus-range = <0x4 0xff>; + + /* Renesas μPD720201 PCIe USB3.0 Host Controller */ + usb-controller@0,0 { + compatible = "pci1912,0014"; + reg = <0x40000 0x0 0x0 0x0 0x0>; + + avdd33-supply = <&vreg_pcie0_3p3>; + vdd10-supply = <&vreg_pcie0_1p05>; + vdd33-supply = <&vreg_pcie0_3p3>; + + pinctrl-0 = <&upd_hub_rst_state>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + /* Genesys Logic GL3590 USB Hub Controller */ + gl3590_2_0: hub@1 { + compatible = "usb5e3,610"; + reg = <1>; + reset-gpios = <&tlmm 162 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&usb_hub_reset_state>; + pinctrl-names = "default"; + + peer-hub = <&gl3590_3_0>; + }; + + gl3590_3_0: hub@2 { + compatible = "usb5e3,625"; + reg = <2>; + + peer-hub = <&gl3590_2_0>; + }; + }; }; pcie@3,0 { @@ -1198,6 +1255,17 @@ power-source = <0>; }; + upd_hub_rst_state: upd-hub-rst-state { + pins = "gpio4"; + function = "normal"; + + bias-disable; + input-disable; + output-enable; + output-high; + power-source = <0>; + }; + tc9563_resx_n: tc9563-resx-state { pins = "gpio1"; function = "normal"; @@ -1378,6 +1446,15 @@ }; &pm7250b_gpios { + upd_pwr_en1_state: upd-pwr-en1-state { + pins = "gpio1"; + function = "normal"; + + output-enable; + input-disable; + power-source = <0>; + }; + lt9611_rst_pin: lt9611-rst-state { pins = "gpio2"; function = "normal"; @@ -1386,6 +1463,15 @@ input-disable; power-source = <0>; }; + + upd_pwr_en2_state: upd-pwr-en2-state { + pins = "gpio4"; + function = "normal"; + + output-enable; + input-disable; + power-source = <0>; + }; }; &sdc2_clk { @@ -1431,6 +1517,13 @@ function = "gpio"; bias-pull-up; }; + + usb_hub_reset_state: usb-hub-reset-state { + pins = "gpio162"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; }; &lpass_audiocc { diff --git a/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-minipc-g1iot.dts b/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-minipc-g1iot.dts new file mode 100644 index 000000000000..a5ad796cb65d --- /dev/null +++ b/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-minipc-g1iot.dts @@ -0,0 +1,1093 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2026, Roger Shimizu + */ + +/dts-v1/; + +/* PM7250B is configured to use SID8/9 */ +#define PM7250B_SID 8 +#define PM7250B_SID1 9 + +#include +#include +#include +#include "kodiak.dtsi" +#include "pm7250b.dtsi" +#include "pm7325.dtsi" +#include "pm8350c.dtsi" /* PM7350C */ +#include "pmk8350.dtsi" /* PMK7325 */ + +/delete-node/ &adsp_mem; +/delete-node/ &cdsp_mem; +/delete-node/ &ipa_fw_mem; +/delete-node/ &mpss_mem; +/delete-node/ &remoteproc_mpss; +/delete-node/ &remoteproc_wpss; +/delete-node/ &rmtfs_mem; +/delete-node/ &video_mem; +/delete-node/ &wifi; +/delete-node/ &wlan_ce_mem; +/delete-node/ &wlan_fw_mem; +/delete-node/ &wpss_mem; +/delete-node/ &xbl_mem; + +/ { + model = "Thundercomm AI Mini PC G1 IoT"; + compatible = "thundercomm,minipc-g1iot", "qcom,qcm6490"; + chassis-type = "desktop"; + + aliases { + serial0 = &uart5; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con: endpoint { + remote-endpoint = <<9611_out>; + }; + }; + }; + + pmic-glink { + compatible = "qcom,qcm6490-pmic-glink", "qcom,pmic-glink"; + + #address-cells = <1>; + #size-cells = <0>; + orientation-gpios = <&tlmm 140 GPIO_ACTIVE_HIGH>; + + connector@0 { + compatible = "usb-c-connector"; + reg = <0>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pmic_glink_hs_in: endpoint { + remote-endpoint = <&usb_1_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + pmic_glink_ss_in: endpoint { + remote-endpoint = <&redriver_usb_con_ss>; + }; + }; + + port@2 { + reg = <2>; + + pmic_glink_sbu_in: endpoint { + remote-endpoint = <&redriver_usb_con_sbu>; + }; + }; + }; + }; + }; + + lt9611_1v2: regulator-lt9611-vdd12 { + compatible = "regulator-fixed"; + regulator-name = "LT9611_1V2"; + + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + reserved-memory { + xbl_mem: xbl@80700000 { + reg = <0x0 0x80700000 0x0 0x100000>; + no-map; + }; + + cdsp_secure_heap_mem: cdsp-secure-heap@81800000 { + reg = <0x0 0x81800000 0x0 0x1e00000>; + no-map; + }; + + camera_mem: camera@84300000 { + reg = <0x0 0x84300000 0x0 0x500000>; + no-map; + }; + + adsp_mem: adsp@86100000 { + reg = <0x0 0x86100000 0x0 0x2800000>; + no-map; + }; + + cdsp_mem: cdsp@88900000 { + reg = <0x0 0x88900000 0x0 0x1e00000>; + no-map; + }; + + video_mem: video@8a700000 { + reg = <0x0 0x8a700000 0x0 0x700000>; + no-map; + }; + + cvp_mem: cvp@8ae00000 { + reg = <0x0 0x8ae00000 0x0 0x500000>; + no-map; + }; + + gpu_microcode_mem: gpu-microcode@8b31a000 { + reg = <0x0 0x8b31a000 0x0 0x2000>; + no-map; + }; + + tz_stat_mem: tz-stat@c0000000 { + reg = <0x0 0xc0000000 0x0 0x100000>; + no-map; + }; + + tags_mem: tags@c0100000 { + reg = <0x0 0xc0100000 0x0 0x1200000>; + no-map; + }; + + qtee_mem: qtee@c1300000 { + reg = <0x0 0xc1300000 0x0 0x500000>; + no-map; + }; + + trusted_apps_mem: trusted-apps@c1800000 { + reg = <0x0 0xc1800000 0x0 0x1c00000>; + no-map; + }; + + debug_vm_mem: debug-vm@d0600000 { + reg = <0x0 0xd0600000 0x0 0x100000>; + no-map; + }; + }; + + vdd_ntn_0p9: regulator-vdd-ntn-0p9 { + compatible = "regulator-fixed"; + regulator-name = "VDD_NTN_0P9"; + gpio = <&pm8350c_gpios 2 GPIO_ACTIVE_HIGH>; + regulator-min-microvolt = <899400>; + regulator-max-microvolt = <899400>; + enable-active-high; + pinctrl-0 = <&ntn_0p9_en>; + pinctrl-names = "default"; + regulator-enable-ramp-delay = <4300>; + }; + + vdd_ntn_1p8: regulator-vdd-ntn-1p8 { + compatible = "regulator-fixed"; + regulator-name = "VDD_NTN_1P8"; + gpio = <&pm8350c_gpios 3 GPIO_ACTIVE_HIGH>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + enable-active-high; + pinctrl-0 = <&ntn_1p8_en>; + pinctrl-names = "default"; + regulator-enable-ramp-delay = <10000>; + }; + + vph_pwr: regulator-vph-pwr { + compatible = "regulator-fixed"; + regulator-name = "vph_pwr"; + regulator-min-microvolt = <3700000>; + regulator-max-microvolt = <3700000>; + }; + + thermal-zones { + sdm-skin-thermal { + thermal-sensors = <&pmk8350_adc_tm 3>; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + quiet-thermal { + thermal-sensors = <&pmk8350_adc_tm 1>; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + xo-thermal { + thermal-sensors = <&pmk8350_adc_tm 0>; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + }; +}; + +&apps_rsc { + regulators-0 { + compatible = "qcom,pm7325-rpmh-regulators"; + qcom,pmic-id = "b"; + + vdd-s1-supply = <&vph_pwr>; + vdd-s2-supply = <&vph_pwr>; + vdd-s3-supply = <&vph_pwr>; + vdd-s4-supply = <&vph_pwr>; + vdd-s5-supply = <&vph_pwr>; + vdd-s6-supply = <&vph_pwr>; + vdd-s7-supply = <&vph_pwr>; + vdd-s8-supply = <&vph_pwr>; + vdd-l1-l4-l12-l15-supply = <&vreg_s7b_0p972>; + vdd-l2-l7-supply = <&vreg_bob_3p296>; + vdd-l6-l9-l10-supply = <&vreg_s8b_1p272>; + vdd-l8-supply = <&vreg_s7b_0p972>; + vdd-l11-l17-l18-l19-supply = <&vreg_s1b_1p872>; + vdd-l13-supply = <&vreg_s7b_0p972>; + vdd-l14-l16-supply = <&vreg_s8b_1p272>; + + vreg_s1b_1p872: smps1 { + regulator-name = "vreg_s1b_1p872"; + regulator-min-microvolt = <1840000>; + regulator-max-microvolt = <2040000>; + }; + + vreg_s7b_0p972: smps7 { + regulator-name = "vreg_s7b_0p972"; + regulator-min-microvolt = <535000>; + regulator-max-microvolt = <1120000>; + }; + + vreg_s8b_1p272: smps8 { + regulator-name = "vreg_s8b_1p272"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1500000>; + regulator-initial-mode = ; + }; + + vreg_l1b_0p912: ldo1 { + regulator-name = "vreg_l1b_0p912"; + regulator-min-microvolt = <825000>; + regulator-max-microvolt = <925000>; + regulator-initial-mode = ; + }; + + vreg_l2b_3p072: ldo2 { + regulator-name = "vreg_l2b_3p072"; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <3544000>; + regulator-initial-mode = ; + }; + + vreg_l3b_0p504: ldo3 { + regulator-name = "vreg_l3b_0p504"; + regulator-min-microvolt = <312000>; + regulator-max-microvolt = <650000>; + regulator-initial-mode = ; + }; + + vreg_l6b_1p2: ldo6 { + regulator-name = "vreg_l6b_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1260000>; + regulator-initial-mode = ; + }; + + vreg_l7b_2p952: ldo7 { + regulator-name = "vreg_l7b_2p952"; + regulator-min-microvolt = <2952000>; + regulator-max-microvolt = <2952000>; + regulator-initial-mode = ; + }; + + vreg_l8b_0p904: ldo8 { + regulator-name = "vreg_l8b_0p904"; + regulator-min-microvolt = <870000>; + regulator-max-microvolt = <970000>; + regulator-initial-mode = ; + }; + + vreg_l9b_1p2: ldo9 { + regulator-name = "vreg_l9b_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l11b_1p504: ldo11 { + regulator-name = "vreg_l11b_1p504"; + regulator-min-microvolt = <1776000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = ; + }; + + vreg_l12b_0p751: ldo12 { + regulator-name = "vreg_l12b_0p751"; + regulator-min-microvolt = <751000>; + regulator-max-microvolt = <824000>; + regulator-initial-mode = ; + }; + + vreg_l13b_0p53: ldo13 { + regulator-name = "vreg_l13b_0p53"; + regulator-min-microvolt = <530000>; + regulator-max-microvolt = <824000>; + regulator-initial-mode = ; + }; + + vreg_l14b_1p08: ldo14 { + regulator-name = "vreg_l14b_1p08"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1304000>; + regulator-initial-mode = ; + }; + + vreg_l15b_0p765: ldo15 { + regulator-name = "vreg_l15b_0p765"; + regulator-min-microvolt = <765000>; + regulator-max-microvolt = <1020000>; + regulator-initial-mode = ; + }; + + vreg_l16b_1p1: ldo16 { + regulator-name = "vreg_l16b_1p1"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1300000>; + regulator-initial-mode = ; + }; + + vreg_l17b_1p7: ldo17 { + regulator-name = "vreg_l17b_1p7"; + regulator-min-microvolt = <1700000>; + regulator-max-microvolt = <1900000>; + regulator-initial-mode = ; + }; + + vreg_l18b_1p8: ldo18 { + regulator-name = "vreg_l18b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = ; + }; + + vreg_l19b_1p8: ldo19 { + regulator-name = "vreg_l19b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = ; + }; + }; + + regulators-1 { + compatible = "qcom,pm8350c-rpmh-regulators"; + qcom,pmic-id = "c"; + + vdd-s1-supply = <&vph_pwr>; + vdd-s2-supply = <&vph_pwr>; + vdd-s3-supply = <&vph_pwr>; + vdd-s4-supply = <&vph_pwr>; + vdd-s5-supply = <&vph_pwr>; + vdd-s6-supply = <&vph_pwr>; + vdd-s7-supply = <&vph_pwr>; + vdd-s8-supply = <&vph_pwr>; + vdd-s9-supply = <&vph_pwr>; + vdd-s10-supply = <&vph_pwr>; + vdd-l1-l12-supply = <&vreg_s1b_1p872>; + vdd-l2-l8-supply = <&vreg_s1b_1p872>; + vdd-l3-l4-l5-l7-l13-supply = <&vreg_bob_3p296>; + vdd-l6-l9-l11-supply = <&vreg_bob_3p296>; + vdd-l10-supply = <&vreg_s7b_0p972>; + vdd-bob-supply = <&vph_pwr>; + + vreg_s1c_2p19: smps1 { + regulator-name = "vreg_s1c_2p19"; + regulator-min-microvolt = <2200000>; + regulator-max-microvolt = <2208000>; + }; + + vreg_s9c_1p084: smps9 { + regulator-name = "vreg_s9c_1p084"; + regulator-min-microvolt = <1010000>; + regulator-max-microvolt = <1170000>; + }; + + vreg_l1c_1p8: ldo1 { + regulator-name = "vreg_l1c_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1980000>; + regulator-initial-mode = ; + }; + + vreg_l2c_1p62: ldo2 { + regulator-name = "vreg_l2c_1p62"; + regulator-min-microvolt = <1620000>; + regulator-max-microvolt = <1980000>; + regulator-initial-mode = ; + }; + + vreg_l3c_2p8: ldo3 { + regulator-name = "vreg_l3c_2p8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <3540000>; + regulator-initial-mode = ; + }; + + vreg_l4c_1p62: ldo4 { + regulator-name = "vreg_l4c_1p62"; + regulator-min-microvolt = <1620000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = ; + }; + + vreg_l5c_1p62: ldo5 { + regulator-name = "vreg_l5c_1p62"; + regulator-min-microvolt = <1620000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = ; + }; + + vreg_l6c_2p96: ldo6 { + regulator-name = "vreg_l6c_2p96"; + regulator-min-microvolt = <1650000>; + regulator-max-microvolt = <2960000>; + regulator-initial-mode = ; + }; + + vreg_l7c_3p0: ldo7 { + regulator-name = "vreg_l7c_3p0"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3544000>; + regulator-initial-mode = ; + }; + + vreg_l8c_1p62: ldo8 { + regulator-name = "vreg_l8c_1p62"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = ; + }; + + vreg_l9c_2p96: ldo9 { + regulator-name = "vreg_l9c_2p96"; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <3544000>; + regulator-initial-mode = ; + }; + + vreg_l10c_0p88: ldo10 { + regulator-name = "vreg_l10c_0p88"; + regulator-min-microvolt = <720000>; + regulator-max-microvolt = <1050000>; + regulator-initial-mode = ; + }; + + vreg_l11c_2p8: ldo11 { + regulator-name = "vreg_l11c_2p8"; + regulator-min-microvolt = <1650000>; + regulator-max-microvolt = <3544000>; + regulator-initial-mode = ; + }; + + vreg_l12c_1p65: ldo12 { + regulator-name = "vreg_l12c_1p65"; + regulator-min-microvolt = <1620000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = ; + }; + + vreg_l13c_2p7: ldo13 { + regulator-name = "vreg_l13c_2p7"; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <3544000>; + regulator-initial-mode = ; + }; + + vreg_bob_3p296: bob { + regulator-name = "vreg_bob_3p296"; + regulator-min-microvolt = <3008000>; + regulator-max-microvolt = <3960000>; + }; + }; +}; + +&gcc { + protected-clocks = , + , + , + , + , + , + , + , + , + , + , + , + , + ; +}; + +&gpi_dma0 { + status = "okay"; +}; + +&gpi_dma1 { + status = "okay"; +}; + +&gpu { + status = "okay"; +}; + +&gpu_zap_shader { + firmware-name = "qcom/qcs6490/a660_zap.mbn"; +}; + +&i2c0 { + clock-frequency = <400000>; + + status = "okay"; + + lt9611_codec: hdmi-bridge@2b { + compatible = "lontium,lt9611uxc"; + reg = <0x2b>; + + interrupts-extended = <&tlmm 24 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&pm7250b_gpios 2 GPIO_ACTIVE_HIGH>; + + vdd-supply = <<9611_1v2>; + vcc-supply = <&vreg_l11c_2p8>; + + pinctrl-0 = <<9611_irq_pin <9611_rst_pin>; + pinctrl-names = "default"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + lt9611_a: endpoint { + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + + port@2 { + reg = <2>; + + lt9611_out: endpoint { + remote-endpoint = <&hdmi_con>; + }; + }; + }; + }; +}; + +&i2c1 { + clock-frequency = <100000>; + + status = "okay"; + + typec-mux@1c { + compatible = "onnn,nb7vpq904m"; + reg = <0x1c>; + + vcc-supply = <&vreg_l18b_1p8>; + + retimer-switch; + orientation-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + redriver_usb_con_ss: endpoint { + remote-endpoint = <&pmic_glink_ss_in>; + }; + }; + + port@1 { + reg = <1>; + + redriver_phy_con_ss: endpoint { + remote-endpoint = <&usb_dp_qmpphy_out>; + data-lanes = <0 1 2 3>; + }; + }; + + port@2 { + reg = <2>; + + redriver_usb_con_sbu: endpoint { + remote-endpoint = <&pmic_glink_sbu_in>; + }; + }; + }; + }; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dp { + status = "okay"; +}; + +&mdss_dp_out { + data-lanes = <0 1>; + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dsi { + vdda-supply = <&vreg_l6b_1p2>; + + status = "okay"; +}; + +&mdss_dsi0_out { + remote-endpoint = <<9611_a>; + data-lanes = <0 1 2 3>; +}; + +&mdss_dsi_phy { + vdds-supply = <&vreg_l10c_0p88>; + + status = "okay"; +}; + +&pcie0 { + perst-gpios = <&tlmm 87 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 89 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&pcie0_clkreq_n>, + <&pcie0_reset_n>, + <&pcie0_wake_n>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie0_phy { + vdda-phy-supply = <&vreg_l10c_0p88>; + vdda-pll-supply = <&vreg_l6b_1p2>; + + status = "okay"; +}; + +&pcie1 { + perst-gpios = <&tlmm 2 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 3 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&pcie1_clkreq_n>, + <&pcie1_reset_n>, + <&pcie1_wake_n>; + pinctrl-names = "default"; + + iommu-map = <0x0 &apps_smmu 0x1c80 0x1>, + <0x100 &apps_smmu 0x1c81 0x1>, + <0x208 &apps_smmu 0x1c84 0x1>, + <0x210 &apps_smmu 0x1c85 0x1>, + <0x218 &apps_smmu 0x1c86 0x1>, + <0x300 &apps_smmu 0x1c87 0x1>, + <0x400 &apps_smmu 0x1c88 0x1>, + <0x500 &apps_smmu 0x1c89 0x1>, + <0x501 &apps_smmu 0x1c90 0x1>; + + status = "okay"; +}; + +&pcie1_phy { + vdda-phy-supply = <&vreg_l10c_0p88>; + vdda-pll-supply = <&vreg_l6b_1p2>; + + status = "okay"; +}; + +&pcie1_port0 { + pcie@0,0 { + compatible = "pci1179,0623"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x2 0xff>; + + vddc-supply = <&vdd_ntn_0p9>; + vdd18-supply = <&vdd_ntn_1p8>; + vdd09-supply = <&vdd_ntn_0p9>; + vddio1-supply = <&vdd_ntn_1p8>; + vddio2-supply = <&vdd_ntn_1p8>; + vddio18-supply = <&vdd_ntn_1p8>; + + i2c-parent = <&i2c0 0x77>; + + resx-gpios = <&pm8350c_gpios 1 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&tc9563_resx_n>; + pinctrl-names = "default"; + + pcie@1,0 { + reg = <0x20800 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x3 0xff>; + }; + + pcie@2,0 { + reg = <0x21000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + ranges; + bus-range = <0x4 0xff>; + }; + + pcie@3,0 { + reg = <0x21800 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + bus-range = <0x5 0xff>; + + pci@0,0 { + reg = <0x50000 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + }; + + pci@0,1 { + reg = <0x50100 0x0 0x0 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges; + }; + }; + }; +}; + +&pm7250b_gpios { + lt9611_rst_pin: lt9611-rst-state { + pins = "gpio2"; + function = "normal"; + + output-high; + input-disable; + power-source = <0>; + }; +}; + +&pm7325_temp_alarm { + io-channels = <&pmk8350_vadc PM7325_ADC7_DIE_TEMP>; + io-channel-names = "thermal"; +}; + +&pmk8350_adc_tm { + status = "okay"; + + xo-therm@0 { + reg = <0>; + io-channels = <&pmk8350_vadc PMK8350_ADC7_AMUX_THM1_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time-us = <200>; + }; + + quiet-therm@1 { + reg = <1>; + io-channels = <&pmk8350_vadc PM7325_ADC7_AMUX_THM1_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time-us = <200>; + }; + + sdm-skin-therm@3 { + reg = <3>; + io-channels = <&pmk8350_vadc PM7325_ADC7_AMUX_THM3_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time-us = <200>; + }; +}; + +&pm8350c_gpios { + ntn_0p9_en: ntn-0p9-en-state { + pins = "gpio2"; + function = "normal"; + + bias-disable; + input-disable; + output-enable; + power-source = <0>; + }; + + ntn_1p8_en: ntn-1p8-en-state { + pins = "gpio3"; + function = "normal"; + + bias-disable; + input-disable; + output-enable; + power-source = <0>; + }; + + tc9563_resx_n: tc9563-resx-state { + pins = "gpio1"; + function = "normal"; + + bias-disable; + input-disable; + output-enable; + power-source = <0>; + }; +}; + +&pm8350c_pwm { + nvmem = <&pmk8350_sdam_21>, + <&pmk8350_sdam_22>; + nvmem-names = "lpg_chan_sdam", + "lut_sdam"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; +}; + +&pmk8350_rtc { + status = "okay"; +}; + +&pmk8350_vadc { + channel@3 { + reg = ; + label = "pmk7325_die_temp"; + qcom,pre-scaling = <1 1>; + }; + + channel@44 { + reg = ; + label = "xo_therm"; + qcom,hw-settle-time = <200>; + qcom,pre-scaling = <1 1>; + qcom,ratiometric; + }; + + channel@103 { + reg = ; + label = "pm7325_die_temp"; + qcom,pre-scaling = <1 1>; + }; + + channel@144 { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + qcom,pre-scaling = <1 1>; + label = "pm7325_quiet_therm"; + }; + + channel@146 { + reg = ; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + qcom,pre-scaling = <1 1>; + label = "pm7325_sdm_skin_therm"; + }; +}; + +&pon_pwrkey { + status = "okay"; +}; + +&pon_resin { + linux,code = ; + + status = "okay"; +}; + +&qupv3_id_0 { + firmware-name = "qcom/qcs6490/qupv3fw.elf"; + + status = "okay"; +}; + +&qupv3_id_1 { + firmware-name = "qcom/qcs6490/qupv3fw.elf"; + + status = "okay"; +}; + +&remoteproc_adsp { + firmware-name = "qcom/qcs6490/adsp.mbn"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/qcs6490/cdsp.mbn"; + + status = "okay"; +}; + +&sdc2_clk { + bias-disable; + drive-strength = <16>; +}; + +&sdc2_cmd { + bias-pull-up; + drive-strength = <10>; +}; + +&sdc2_data { + bias-pull-up; + drive-strength = <10>; +}; + +&sdhc_2 { + pinctrl-0 = <&sdc2_clk>, <&sdc2_cmd>, <&sdc2_data>, <&sd_cd>; + pinctrl-1 = <&sdc2_clk_sleep>, <&sdc2_cmd_sleep>, <&sdc2_data_sleep>, <&sd_cd>; + + vmmc-supply = <&vreg_l9c_2p96>; + vqmmc-supply = <&vreg_l6c_2p96>; + + cd-gpios = <&tlmm 91 GPIO_ACTIVE_LOW>; + + status = "okay"; +}; + +&tlmm { + gpio-reserved-ranges = <32 2>, /* ADSP */ + <48 4>; /* NFC */ + + lt9611_irq_pin: lt9611-irq-state { + pins = "gpio24"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + pcie0_reset_n: pcie0-reset-n-state { + pins = "gpio87"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + pcie0_wake_n: pcie0-wake-n-state { + pins = "gpio89"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + pcie1_reset_n: pcie1-reset-n-state { + pins = "gpio2"; + function = "gpio"; + drive-strength = <16>; + output-low; + bias-disable; + }; + + pcie1_wake_n: pcie1-wake-n-state { + pins = "gpio3"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + sd_cd: sd-cd-state { + pins = "gpio91"; + function = "gpio"; + bias-pull-up; + }; +}; + +&uart5 { + status = "okay"; +}; + +&usb_1 { + status = "okay"; +}; + +&usb_1_dwc3_hs { + remote-endpoint = <&pmic_glink_hs_in>; +}; + +&usb_1_hsphy { + vdda-pll-supply = <&vreg_l10c_0p88>; + vdda33-supply = <&vreg_l2b_3p072>; + vdda18-supply = <&vreg_l1c_1p8>; + + status = "okay"; +}; + +&usb_1_qmpphy { + vdda-phy-supply = <&vreg_l6b_1p2>; + vdda-pll-supply = <&vreg_l1b_0p912>; + + status = "okay"; +}; + +&usb_dp_qmpphy_out { + remote-endpoint = <&redriver_phy_con_ss>; +}; + +&ufs_mem_hc { + reset-gpios = <&tlmm 175 GPIO_ACTIVE_LOW>; + vcc-supply = <&vreg_l7b_2p952>; + vcc-max-microamp = <800000>; + vccq-supply = <&vreg_l9b_1p2>; + vccq-max-microamp = <900000>; + vccq2-supply = <&vreg_l9b_1p2>; + vccq2-max-microamp = <900000>; + + status = "okay"; +}; + +&ufs_mem_phy { + vdda-phy-supply = <&vreg_l10c_0p88>; + vdda-pll-supply = <&vreg_l6b_1p2>; + + status = "okay"; +}; + +&venus { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-rubikpi3.dts b/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-rubikpi3.dts index 0b64a0b91202..f47efca42d48 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-rubikpi3.dts +++ b/arch/arm64/boot/dts/qcom/qcs6490-thundercomm-rubikpi3.dts @@ -755,10 +755,10 @@ #address-cells = <1>; #size-cells = <0>; - port@0 { - reg = <0>; + port@1 { + reg = <1>; - lt9611_a: endpoint { + lt9611_b: endpoint { remote-endpoint = <&mdss_dsi0_out>; }; }; @@ -801,7 +801,7 @@ }; &mdss_dsi0_out { - remote-endpoint = <<9611_a>; + remote-endpoint = <<9611_b>; data-lanes = <0 1 2 3>; }; diff --git a/arch/arm64/boot/dts/qcom/qcs8300-ride.dts b/arch/arm64/boot/dts/qcom/qcs8300-ride.dts index c04e0ad53eec..e9a8553a8d82 100644 --- a/arch/arm64/boot/dts/qcom/qcs8300-ride.dts +++ b/arch/arm64/boot/dts/qcom/qcs8300-ride.dts @@ -18,12 +18,76 @@ aliases { serial0 = &uart7; mmc0 = &sdhc_1; + serial1 = &uart2; }; chosen { stdout-path = "serial0:115200n8"; }; + vreg_1p0: regulator-vreg-1p0 { + compatible = "regulator-fixed"; + regulator-name = "VREG_1P0"; + + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + + vin-supply = <&vreg_1p8>; + + regulator-always-on; + regulator-boot-on; + }; + + vreg_1p8: regulator-vreg-1p8 { + compatible = "regulator-fixed"; + regulator-name = "VREG_1P8"; + + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + vin-supply = <&vreg_5p0>; + + regulator-always-on; + regulator-boot-on; + }; + + vreg_3p0: regulator-vreg-3p0 { + compatible = "regulator-fixed"; + regulator-name = "VREG_3P0"; + + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + + vin-supply = <&vreg_12p0>; + + regulator-always-on; + regulator-boot-on; + }; + + vreg_5p0: regulator-vreg-5p0 { + compatible = "regulator-fixed"; + regulator-name = "VREG_5P0"; + + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + + vin-supply = <&vreg_12p0>; + + regulator-always-on; + regulator-boot-on; + }; + + vreg_12p0: regulator-vreg-12p0 { + compatible = "regulator-fixed"; + regulator-name = "VREG_12P0"; + + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + + regulator-always-on; + regulator-boot-on; + }; + dp0-connector { compatible = "dp-connector"; label = "DP0"; @@ -36,6 +100,62 @@ }; }; + dp-dsi0-connector { + compatible = "dp-connector"; + label = "DSI0"; + type = "full-size"; + + port { + dp_dsi0_connector_in: endpoint { + remote-endpoint = <&dsi2dp_bridge_out>; + }; + }; + }; + + vreg_conn_1p05: regulator-conn-1p05 { + compatible = "regulator-fixed"; + regulator-name = "vreg_conn_1p05"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + vin-supply = <&vreg_conn_1p8>; + }; + + vreg_conn_1p35: regulator-conn-1p35 { + compatible = "regulator-fixed"; + regulator-name = "vreg_conn_1p35"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + vin-supply = <&vreg_conn_1p8>; + }; + + vreg_conn_1p8: regulator-conn-1p8 { + compatible = "regulator-fixed"; + regulator-name = "vreg_conn_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <4000>; + enable-active-high; + gpio = <&pmm8650au_1_gpios 4 GPIO_ACTIVE_HIGH>; + }; + + vreg_conn_1p95: regulator-conn-1p95 { + compatible = "regulator-fixed"; + regulator-name = "vreg_conn_1p95"; + regulator-min-microvolt = <1950000>; + regulator-max-microvolt = <1950000>; + vin-supply = <&vreg_conn_1p8>; + }; + + vreg_conn_pa: regulator-conn-pa { + compatible = "regulator-fixed"; + regulator-name = "vreg_conn_pa"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us = <4000>; + enable-active-high; + gpio = <&pmm8650au_1_gpios 6 GPIO_ACTIVE_HIGH>; + }; + regulator-usb2-vbus { compatible = "regulator-fixed"; regulator-name = "USB2_VBUS"; @@ -45,6 +165,70 @@ enable-active-high; regulator-always-on; }; + + wcn6855-pmu { + compatible = "qcom,wcn6855-pmu"; + + pinctrl-0 = <&wlan_en_state>; + pinctrl-names = "default"; + + vddio-supply = <&vreg_conn_pa>; + vddaon-supply = <&vreg_conn_1p8>; + vddpmu-supply = <&vreg_conn_pa>; + vddpmumx-supply = <&vreg_conn_1p8>; + vddpmucx-supply = <&vreg_conn_pa>; + /* WLAN rails: 1.05/1.35/1.95V (nominal 0.95/1.30/1.90V) */ + vddrfa0p95-supply = <&vreg_conn_1p05>; + vddrfa1p3-supply = <&vreg_conn_1p35>; + vddrfa1p9-supply = <&vreg_conn_1p95>; + vddpcie1p3-supply = <&vreg_conn_1p35>; + vddpcie1p9-supply = <&vreg_conn_1p95>; + + bt-enable-gpios = <&tlmm 55 GPIO_ACTIVE_HIGH>; + wlan-enable-gpios = <&tlmm 54 GPIO_ACTIVE_HIGH>; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p7: ldo7 { + regulator-name = "vreg_pmu_rfa_1p7"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; }; &apps_rsc { @@ -316,6 +500,75 @@ firmware-name = "qcom/qcs8300/a623_zap.mbn"; }; +&i2c8 { + clock-frequency = <400000>; + + status = "okay"; + + io_expander: gpio@74 { + compatible = "ti,tca9539"; + reg = <0x74>; + interrupts-extended = <&tlmm 93 IRQ_TYPE_EDGE_BOTH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reset-gpios = <&tlmm 66 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&io_expander_intr_active>, + <&io_expander_reset_active>; + pinctrl-names = "default"; + }; + + i2c-mux@70 { + compatible = "nxp,pca9543"; + reg = <0x70>; + + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + bridge@58 { + compatible = "analogix,anx7625"; + reg = <0x58>; + interrupts-extended = <&io_expander 2 IRQ_TYPE_EDGE_FALLING>; + enable-gpios = <&io_expander 1 GPIO_ACTIVE_HIGH>; + reset-gpios = <&io_expander 0 GPIO_ACTIVE_HIGH>; + vdd10-supply = <&vreg_1p0>; + vdd18-supply = <&vreg_1p8>; + vdd33-supply = <&vreg_3p0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dsi2dp_bridge_in: endpoint { + + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + + dsi2dp_bridge_out: endpoint { + + remote-endpoint = <&dp_dsi0_connector_in>; + }; + }; + }; + }; + }; + }; +}; + &pmm8650au_1_gpios { usb2_en: usb2-en-state { pins = "gpio7"; @@ -363,6 +616,23 @@ &pcieport0 { reset-gpios = <&tlmm 2 GPIO_ACTIVE_LOW>; wake-gpios = <&tlmm 0 GPIO_ACTIVE_HIGH>; + + wifi@0 { + compatible = "pci17cb,1103"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + + qcom,calibration-variant = "QC_QCS8300_Ride"; + }; }; &pcie0_phy { @@ -391,10 +661,31 @@ status = "okay"; }; +&mdss_dsi0 { + vdda-supply = <&vreg_l5a>; + + status = "okay"; +}; + +&mdss_dsi0_phy { + vdds-supply = <&vreg_l4a>; + + status = "okay"; +}; + +&mdss_dsi0_out { + data-lanes = <0 1 2 3>; + remote-endpoint = <&dsi2dp_bridge_in>; +}; + &qupv3_id_0 { status = "okay"; }; +&qupv3_id_1 { + status = "okay"; +}; + &remoteproc_adsp { firmware-name = "qcom/qcs8300/adsp.mbn"; status = "okay"; @@ -436,6 +727,12 @@ }; &tlmm { + bt_en_state: bt-en-state { + pins = "gpio55"; + function = "gpio"; + bias-pull-down; + }; + pcie0_default_state: pcie0-default-state { wake-pins { pins = "gpio0"; @@ -498,11 +795,50 @@ }; }; + io_expander_reset_active: io-expander-reset-active-state { + pins = "gpio66"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + io_expander_intr_active: io-expander-intr-active-state { + pins = "gpio93"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + dp_hot_plug_det: dp-hot-plug-det-state { pins = "gpio94"; function = "edp0_hot"; bias-disable; }; + + wlan_en_state: wlan-en-state { + pins = "gpio54"; + function = "gpio"; + bias-pull-down; + }; +}; + +&uart2 { + status = "okay"; + + bluetooth: bluetooth { + compatible = "qcom,wcn6855-bt"; + firmware-name = "QCA6698/hpnv21", "QCA6698/hpbtfw21.tlv"; + max-speed = <3200000>; + + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddbtcmx-supply = <&vreg_pmu_btcmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; + }; }; &uart7 { diff --git a/arch/arm64/boot/dts/qcom/qdu1000.dtsi b/arch/arm64/boot/dts/qcom/qdu1000.dtsi index cdfe40da5d33..952d4270d118 100644 --- a/arch/arm64/boot/dts/qcom/qdu1000.dtsi +++ b/arch/arm64/boot/dts/qcom/qdu1000.dtsi @@ -1638,10 +1638,10 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - , - ; + interrupts = , + , + , + , + ; }; }; diff --git a/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts b/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts index 197ab6eb1666..bf088fa9807f 100644 --- a/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts +++ b/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts @@ -6,6 +6,7 @@ /dts-v1/; #include +#include #include "agatti.dtsi" #include "pm4125.dtsi" @@ -109,6 +110,15 @@ leds = <&ledr>, <&ledg>, <&ledb>; }; + vreg_anx_30: regulator-anx-30 { + compatible = "regulator-fixed"; + regulator-name = "anx30"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + regulator-boot-on; + }; + /* PM4125 charger out, supplied by VBAT */ vph_pwr: regulator-vph-pwr { compatible = "regulator-fixed"; @@ -142,6 +152,86 @@ clock-frequency = <100000>; status = "okay"; + + anx7625: encoder@58 { + compatible = "analogix,anx7625"; + reg = <0x58>; + interrupts-extended = <&tlmm 81 IRQ_TYPE_EDGE_FALLING>; + vdd10-supply = <&pm4125_l11>; + vdd18-supply = <&pm4125_l15>; + vdd33-supply = <&vreg_anx_30>; + analogix,audio-enable; + analogix,lane0-swing = /bits/ 8 <0x14 0x54 0x64 0x74>; + analogix,lane1-swing = /bits/ 8 <0x14 0x54 0x64 0x74>; + + pinctrl-0 = <&anx7625_int_pin>, <&anx7625_cable_det_pin>; + + connector { + compatible = "usb-c-connector"; + power-role = "sink"; + data-role = "dual"; + try-power-role = "sink"; + + pd-revision = /bits/ 8 <0x03 0x00 0x00 0x00>; + op-sink-microwatt = <15000000>; + sink-pdos = ; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + anx_hs_in: endpoint { + remote-endpoint = <&usb_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + anx_ss_in: endpoint { + remote-endpoint = <&usb_qmpphy_out>; + }; + }; + }; + }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + anx_dsi0_in: endpoint { + remote-endpoint = <&mdss_dsi0_out>; + data-lanes = <0 1 2 3>; + }; + }; + }; + }; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dsi0 { + vdda-supply = <&pm4125_l5>; + + status = "okay"; +}; + +&mdss_dsi0_out { + remote-endpoint = <&anx_dsi0_in>; + data-lanes = <0 1 2 3>; +}; + +&mdss_dsi0_phy { + status = "okay"; }; &pm4125_vbus { @@ -325,21 +415,13 @@ &spi5 { status = "okay"; - spidev@0 { - reg = <0>; + mcu@0 { compatible = "arduino,unoq-mcu"; - pinctrl-0 = <&spidev_cs>; - pinctrl-names = "default"; + reg = <0>; }; }; &tlmm { - spidev_cs: spidev-cs-state { - pins = "gpio17"; - function = "gpio"; - drive-strength = <16>; - }; - jmisc_gpio18: jmisc-gpio18-state { pins = "gpio18"; function = "gpio"; @@ -361,6 +443,22 @@ output-disable; }; + anx7625_cable_det_pin: anx7625-cable-det-pins-state { + pins = "gpio46"; + function = "gpio"; + drive-strength = <16>; + output-disable; + bias-pull-up; + }; + + anx7625_int_pin: anx7625-int-pins-state { + pins = "gpio81"; + function = "gpio"; + drive-strength = <16>; + output-disable; + bias-pull-up; + }; + key_volp_n: key-volp-n-state { pins = "gpio96"; function = "gpio"; @@ -428,6 +526,10 @@ status = "okay"; }; +&usb_dwc3_hs { + remote-endpoint = <&anx_hs_in>; +}; + &usb_hsphy { vdd-supply = <&pm4125_l12>; vdda-pll-supply = <&pm4125_l13>; @@ -443,6 +545,10 @@ status = "okay"; }; +&usb_qmpphy_out { + remote-endpoint = <&anx_ss_in>; +}; + &wifi { vdd-0.8-cx-mx-supply = <&pm4125_l7>; vdd-1.8-xo-supply = <&pm4125_l13>; diff --git a/arch/arm64/boot/dts/qcom/qrb2210-rb1.dts b/arch/arm64/boot/dts/qcom/qrb2210-rb1.dts index 9814ac4896c5..da46e9d65528 100644 --- a/arch/arm64/boot/dts/qcom/qrb2210-rb1.dts +++ b/arch/arm64/boot/dts/qcom/qrb2210-rb1.dts @@ -109,7 +109,6 @@ regulator-name = "VREG_HDMI_OUT_1P2"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; - vin-supply = <&vdc_1v2>; regulator-always-on; regulator-boot-on; }; @@ -119,39 +118,6 @@ regulator-name = "LT9611_3V3"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; - vin-supply = <&vdc_3v3>; - regulator-always-on; - regulator-boot-on; - }; - - /* Main barrel jack input */ - vdc_12v: regulator-vdc-12v { - compatible = "regulator-fixed"; - regulator-name = "DC_12V"; - regulator-min-microvolt = <12000000>; - regulator-max-microvolt = <12000000>; - regulator-always-on; - regulator-boot-on; - }; - - /* 1.2V supply stepped down from the barrel jack input */ - vdc_1v2: regulator-vdc-1v2 { - compatible = "regulator-fixed"; - regulator-name = "VDC_1V2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - vin-supply = <&vdc_12v>; - regulator-always-on; - regulator-boot-on; - }; - - /* 3.3V supply stepped down from the barrel jack input */ - vdc_3v3: regulator-vdc-3v3 { - compatible = "regulator-fixed"; - regulator-name = "VDC_3V3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - vin-supply = <&vdc_12v>; regulator-always-on; regulator-boot-on; }; @@ -167,23 +133,12 @@ regulator-boot-on; }; - /* "Battery" voltage for the SoM, stepped down from the barrel jack input */ - vdc_vbat_som: regulator-vdc-vbat { - compatible = "regulator-fixed"; - regulator-name = "VBAT_SOM"; - regulator-min-microvolt = <4200000>; - regulator-max-microvolt = <4200000>; - regulator-always-on; - regulator-boot-on; - }; - /* PM2250 charger out, supplied by VBAT */ vph_pwr: regulator-vph-pwr { compatible = "regulator-fixed"; regulator-name = "vph_pwr"; regulator-min-microvolt = <3700000>; regulator-max-microvolt = <3700000>; - vin-supply = <&vdc_vbat_som>; regulator-always-on; regulator-boot-on; @@ -235,6 +190,42 @@ }; }; }; + + wcn3950-pmu { + compatible = "qcom,wcn3950-pmu"; + + pinctrl-0 = <&sw_ctrl_default>; + pinctrl-names = "default"; + + vddio-supply = <&pm4125_l15>; + vddxo-supply = <&pm4125_l13>; + vddrf-supply = <&pm4125_l10>; + vddch0-supply = <&pm4125_l22>; + + swctrl-gpios = <&tlmm 87 GPIO_ACTIVE_HIGH>; + + regulators { + vreg_pmu_io: ldo0 { + regulator-name = "vreg_pmu_io"; + }; + + vreg_pmu_xo: ldo1 { + regulator-name = "vreg_pmu_xo"; + }; + + vreg_pmu_rf: ldo2 { + regulator-name = "vreg_pmu_rf"; + }; + + vreg_pmu_ch0: ldo3 { + regulator-name = "vreg_pmu_ch0"; + }; + + vreg_pmu_ch1: ldo4 { + regulator-name = "vreg_pmu_ch1"; + }; + }; + }; }; &cpu_pd0 { @@ -754,6 +745,12 @@ bias-disable; }; + sw_ctrl_default: sw-ctrl-default-state { + pins = "gpio87"; + function = "gpio"; + bias-pull-down; + }; + sd_det_in_on: sd-det-in-on-state { pins = "gpio88"; function = "gpio"; @@ -789,11 +786,10 @@ bluetooth { compatible = "qcom,wcn3950-bt"; - vddio-supply = <&pm4125_l15>; - vddxo-supply = <&pm4125_l13>; - vddrf-supply = <&pm4125_l10>; - vddch0-supply = <&pm4125_l22>; - enable-gpios = <&tlmm 87 GPIO_ACTIVE_HIGH>; + vddio-supply = <&vreg_pmu_io>; + vddxo-supply = <&vreg_pmu_xo>; + vddrf-supply = <&vreg_pmu_rf>; + vddch0-supply = <&vreg_pmu_ch0>; max-speed = <3200000>; }; }; @@ -834,10 +830,13 @@ }; &wifi { + /* SoC */ vdd-0.8-cx-mx-supply = <&pm4125_l7>; - vdd-1.8-xo-supply = <&pm4125_l13>; - vdd-1.3-rfa-supply = <&pm4125_l10>; - vdd-3.3-ch0-supply = <&pm4125_l22>; + + /* WiFi / BT PMU */ + vdd-1.8-xo-supply = <&vreg_pmu_xo>; + vdd-1.3-rfa-supply = <&vreg_pmu_rf>; + vdd-3.3-ch0-supply = <&vreg_pmu_ch0>; qcom,calibration-variant = "Thundercomm_RB1"; firmware-name = "qcm2290"; status = "okay"; diff --git a/arch/arm64/boot/dts/qcom/qrb4210-rb2.dts b/arch/arm64/boot/dts/qcom/qrb4210-rb2.dts index 5f8613150bdd..1203172729fa 100644 --- a/arch/arm64/boot/dts/qcom/qrb4210-rb2.dts +++ b/arch/arm64/boot/dts/qcom/qrb4210-rb2.dts @@ -158,7 +158,6 @@ regulator-name = "VREG_HDMI_OUT_1P2"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; - vin-supply = <&vdc_1v2>; regulator-always-on; regulator-boot-on; }; @@ -168,39 +167,6 @@ regulator-name = "LT9611_3V3"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; - vin-supply = <&vdc_3v3>; - regulator-always-on; - regulator-boot-on; - }; - - /* Main barrel jack input */ - vdc_12v: regulator-vdc-12v { - compatible = "regulator-fixed"; - regulator-name = "DC_12V"; - regulator-min-microvolt = <12000000>; - regulator-max-microvolt = <12000000>; - regulator-always-on; - regulator-boot-on; - }; - - /* 1.2V supply stepped down from the barrel jack input */ - vdc_1v2: regulator-vdc-1v2 { - compatible = "regulator-fixed"; - regulator-name = "VDC_1V2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - vin-supply = <&vdc_12v>; - regulator-always-on; - regulator-boot-on; - }; - - /* 3.3V supply stepped down from the barrel jack input */ - vdc_3v3: regulator-vdc-3v3 { - compatible = "regulator-fixed"; - regulator-name = "VDC_3V3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - vin-supply = <&vdc_12v>; regulator-always-on; regulator-boot-on; }; @@ -216,27 +182,52 @@ regulator-boot-on; }; - /* "Battery" voltage for the SoM, stepped down from the barrel jack input */ - vdc_vbat_som: regulator-vdc-vbat { - compatible = "regulator-fixed"; - regulator-name = "VBAT_SOM"; - regulator-min-microvolt = <4200000>; - regulator-max-microvolt = <4200000>; - regulator-always-on; - regulator-boot-on; - }; - /* PMI632 charger out, supplied by VBAT */ vph_pwr: regulator-vph-pwr { compatible = "regulator-fixed"; regulator-name = "vph_pwr"; regulator-min-microvolt = <3700000>; regulator-max-microvolt = <3700000>; - vin-supply = <&vdc_vbat_som>; regulator-always-on; regulator-boot-on; }; + + wcn3988-pmu { + compatible = "qcom,wcn3988-pmu"; + + pinctrl-0 = <&sw_ctrl_default>; + pinctrl-names = "default"; + + vddio-supply = <&vreg_l9a_1p8>; + vddxo-supply = <&vreg_l16a_1p3>; + vddrf-supply = <&vreg_l17a_1p3>; + vddch0-supply = <&vreg_l23a_3p3>; + + swctrl-gpios = <&tlmm 87 GPIO_ACTIVE_HIGH>; + + regulators { + vreg_pmu_io: ldo0 { + regulator-name = "vreg_pmu_io"; + }; + + vreg_pmu_xo: ldo1 { + regulator-name = "vreg_pmu_xo"; + }; + + vreg_pmu_rf: ldo2 { + regulator-name = "vreg_pmu_rf"; + }; + + vreg_pmu_ch0: ldo3 { + regulator-name = "vreg_pmu_ch0"; + }; + + vreg_pmu_ch1: ldo4 { + regulator-name = "vreg_pmu_ch1"; + }; + }; + }; }; &gpi_dma0 { @@ -684,6 +675,12 @@ bias-disable; }; + sw_ctrl_default: sw-ctrl-default-state { + pins = "gpio87"; + function = "gpio"; + bias-pull-down; + }; + sdc2_card_det_n: sd-card-det-n-state { pins = "gpio88"; function = "gpio"; @@ -703,11 +700,10 @@ bluetooth { compatible = "qcom,wcn3988-bt"; - vddio-supply = <&vreg_l9a_1p8>; - vddxo-supply = <&vreg_l16a_1p3>; - vddrf-supply = <&vreg_l17a_1p3>; - vddch0-supply = <&vreg_l23a_3p3>; - enable-gpios = <&tlmm 87 GPIO_ACTIVE_HIGH>; + vddio-supply = <&vreg_pmu_io>; + vddxo-supply = <&vreg_pmu_xo>; + vddrf-supply = <&vreg_pmu_rf>; + vddch0-supply = <&vreg_pmu_ch0>; max-speed = <3200000>; }; }; @@ -744,10 +740,13 @@ }; &wifi { + /* SoC */ vdd-0.8-cx-mx-supply = <&vreg_l8a_0p664>; - vdd-1.8-xo-supply = <&vreg_l16a_1p3>; - vdd-1.3-rfa-supply = <&vreg_l17a_1p3>; - vdd-3.3-ch0-supply = <&vreg_l23a_3p3>; + + /* WiFi / BT PMU */ + vdd-1.8-xo-supply = <&vreg_pmu_xo>; + vdd-1.3-rfa-supply = <&vreg_pmu_rf>; + vdd-3.3-ch0-supply = <&vreg_pmu_ch0>; qcom,calibration-variant = "Thundercomm_RB2"; firmware-name = "qrb4210"; diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts index 71b42e76f03d..54da0d759a67 100644 --- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts +++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts @@ -38,14 +38,6 @@ clock-frequency = <40000000>; }; - dc12v: dc12v-regulator { - compatible = "regulator-fixed"; - regulator-name = "DC12V"; - regulator-min-microvolt = <12000000>; - regulator-max-microvolt = <12000000>; - regulator-always-on; - }; - hdmi-out { compatible = "hdmi-connector"; type = "a"; @@ -92,7 +84,7 @@ compatible = "regulator-fixed"; regulator-name = "LT9611_1V2"; - vin-supply = <&vdc_3v3>; + vin-supply = <&vreg_l11c_3p3>; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; }; @@ -101,7 +93,7 @@ compatible = "regulator-fixed"; regulator-name = "LT9611_3V3"; - vin-supply = <&vdc_3v3>; + vin-supply = <&vreg_l11c_3p3>; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; regulator-boot-on; @@ -231,33 +223,6 @@ }; }; - vbat: vbat-regulator { - compatible = "regulator-fixed"; - regulator-name = "VBAT"; - vin-supply = <&vreg_l11c_3p3>; - regulator-min-microvolt = <4200000>; - regulator-max-microvolt = <4200000>; - regulator-always-on; - }; - - vbat_som: vbat-som-regulator { - compatible = "regulator-fixed"; - regulator-name = "VBAT_SOM"; - vin-supply = <&dc12v>; - regulator-min-microvolt = <4200000>; - regulator-max-microvolt = <4200000>; - regulator-always-on; - }; - - vdc_3v3: vdc-3v3-regulator { - compatible = "regulator-fixed"; - regulator-name = "VDC_3V3"; - vin-supply = <&vreg_l11c_3p3>; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - vdc_5v: vdc-5v-regulator { compatible = "regulator-fixed"; regulator-name = "VDC_5V"; diff --git a/arch/arm64/boot/dts/qcom/sc7180-ecs-liva-qc710.dts b/arch/arm64/boot/dts/qcom/sc7180-ecs-liva-qc710.dts new file mode 100644 index 000000000000..b84da371581b --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sc7180-ecs-liva-qc710.dts @@ -0,0 +1,616 @@ +// SPDX-License-Identifier: BSD-3-Clause + +/dts-v1/; + +#include +#include +#include + +#include "sc7180.dtsi" + +#include "pm6150.dtsi" +#include "pm6150l.dtsi" + +/delete-node/ &tz_mem; +/delete-node/ &ipa_fw_mem; + +/ { + model = "ECS LIVA QC710"; + compatible = "ecs,liva-qc710", "qcom,sc7180"; + chassis-type = "desktop"; + + aliases { + bluetooth0 = &bluetooth; + hsuart0 = &uart3; + wifi0 = &wifi; + }; + + hdmi-bridge { + compatible = "algoltek,ag6311"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + hdmi_bridge_dp_in: endpoint { + remote-endpoint = <&usb_1_qmpphy_dp_out>; + }; + }; + + port@1 { + reg = <1>; + + hdmi_bridge_tmds_out: endpoint { + remote-endpoint = <&hdmi_con>; + }; + }; + }; + }; + + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con: endpoint { + remote-endpoint = <&hdmi_bridge_tmds_out>; + }; + }; + }; + + reserved-memory { + gpu_mem: zap-shader@80840000 { + reg = <0x0 0x80840000 0 0x2000>; + no-map; + }; + + venus_mem: venus@85b00000 { + reg = <0x0 0x85b00000 0 0x500000>; + no-map; + }; + + mpss_mem: mpss@86000000 { + reg = <0x0 0x86000000 0x0 0x2000000>; + no-map; + }; + + adsp_mem: adsp@8e400000 { + reg = <0x0 0x8e400000 0x0 0x2800000>; + no-map; + }; + + wlan_mem: wlan@93900000 { + reg = <0x0 0x93900000 0x0 0x200000>; + no-map; + }; + }; + + usb_a_connector: usb-a-connector { + compatible = "usb-a-connector"; + + port { + usb_a_connector_ss_in: endpoint { + remote-endpoint = <&usb_1_qmpphy_usb_ss_out>; + }; + }; + }; +}; + +&apps_rsc { + regulators-0 { + compatible = "qcom,pm6150-rpmh-regulators"; + qcom,pmic-id = "a"; + + vreg_s1a_1p1: smps1 { + regulator-min-microvolt = <1128000>; + regulator-max-microvolt = <1128000>; + }; + + vreg_l4a_0p8: ldo4 { + regulator-min-microvolt = <824000>; + regulator-max-microvolt = <928000>; + regulator-initial-mode = ; + }; + + vreg_l9a_0p6: ldo9 { + regulator-min-microvolt = <488000>; + regulator-max-microvolt = <800000>; + regulator-initial-mode = ; + }; + + vreg_l10a_1p8: ldo10 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + regulator-always-on; + regulator-boot-on; + }; + + vreg_l11a_1p8: ldo11 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l12a_1p8: ldo12 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l13a_1p8: ldo13 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l14a_1p8: ldo14 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l15a_1p8: ldo15 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l16a_2p7: ldo16 { + regulator-min-microvolt = <2496000>; + regulator-max-microvolt = <3304000>; + regulator-initial-mode = ; + }; + + vreg_l17a_3p0: ldo17 { + regulator-min-microvolt = <2920000>; + regulator-max-microvolt = <3232000>; + regulator-initial-mode = ; + }; + + vreg_l18a_2p8: ldo18 { + regulator-min-microvolt = <2496000>; + regulator-max-microvolt = <3304000>; + regulator-initial-mode = ; + }; + + vreg_l19a_2p9: ldo19 { + regulator-min-microvolt = <2960000>; + regulator-max-microvolt = <2960000>; + regulator-initial-mode = ; + }; + }; + + regulators-1 { + compatible = "qcom,pm6150l-rpmh-regulators"; + qcom,pmic-id = "c"; + + vreg_s8c_1p3: smps8 { + regulator-min-microvolt = <1120000>; + regulator-max-microvolt = <1408000>; + }; + + vreg_l1c_1p8: ldo1 { + regulator-min-microvolt = <1616000>; + regulator-max-microvolt = <1984000>; + regulator-initial-mode = ; + }; + + vreg_l2c_1p3: ldo2 { + regulator-min-microvolt = <1168000>; + regulator-max-microvolt = <1304000>; + regulator-initial-mode = ; + }; + + vreg_l3c_1p2: ldo3 { + regulator-min-microvolt = <1144000>; + regulator-max-microvolt = <1304000>; + regulator-initial-mode = ; + }; + + vreg_l4c_1p8: ldo4 { + regulator-min-microvolt = <1648000>; + regulator-max-microvolt = <3304000>; + regulator-initial-mode = ; + }; + + vreg_l5c_1p8: ldo5 { + regulator-min-microvolt = <1648000>; + regulator-max-microvolt = <3304000>; + regulator-initial-mode = ; + }; + + vreg_l6c_2p9: ldo6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2950000>; + regulator-initial-mode = ; + }; + + vreg_l7c_3p0: ldo7 { + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3312000>; + regulator-initial-mode = ; + }; + + vreg_l8c_1p8: ldo8 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l9c_2p9: ldo9 { + regulator-min-microvolt = <2952000>; + regulator-max-microvolt = <2952000>; + regulator-initial-mode = ; + }; + + vreg_l10c_3p3: ldo10 { + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3400000>; + regulator-initial-mode = ; + }; + + vreg_l11c_3p3: ldo11 { + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3400000>; + regulator-initial-mode = ; + }; + + vreg_bob: bob { + regulator-min-microvolt = <3008000>; + regulator-max-microvolt = <3960000>; + regulator-initial-mode = ; + }; + }; +}; + +&gpu { + status = "okay"; +}; + +&gpu_zap_shader { + firmware-name = "qcom/sc7180/ecs/liva-qc710/qcdxkmsuc7180.mbn"; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dp { + pinctrl-0 = <&dp_hot_plug_det>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&mdss_dp_out { + data-lanes = <0 1>; + remote-endpoint = <&usb_1_qmpphy_dp_in>; +}; + +&pm6150_rtc { + qcom,uefi-rtc-info; + + status = "okay"; +}; + +&qfprom { + vcc-supply = <&vreg_l11a_1p8>; +}; + +&qupv3_id_0 { + status = "okay"; +}; + +&qupv3_id_1 { + status = "okay"; +}; + +&remoteproc_adsp { + memory-region = <&adsp_mem>; + firmware-name = "qcom/sc7180/ecs/liva-qc710/qcadsp7180.mbn"; + + status = "okay"; +}; + +&remoteproc_mpss { + firmware-name = "qcom/sc7180/ecs/liva-qc710/qcmpss7180_nm.mbn"; + + status = "okay"; +}; + +&sdhc_1 { + pinctrl-0 = <&sdc1_default>; + pinctrl-1 = <&sdc1_sleep>; + pinctrl-names = "default", "sleep"; + vmmc-supply = <&vreg_l19a_2p9>; + vqmmc-supply = <&vreg_l12a_1p8>; + + status = "okay"; +}; + +&sdhc_2 { + pinctrl-0 = <&sdc2_default>; + pinctrl-1 = <&sdc2_sleep>; + pinctrl-names = "default", "sleep"; + vmmc-supply = <&vreg_l9c_2p9>; + vqmmc-supply = <&vreg_l6c_2p9>; + + cd-gpios = <&tlmm 69 GPIO_ACTIVE_LOW>; + + status = "okay"; +}; + +&tlmm { + /* + * The TZ seem to protect those because some boards can have + * fingerprint sensor connected to this range. Not connected + * on this board + */ + gpio-reserved-ranges = <58 5>; + + qup_uart3_sleep: qup-uart3-sleep-state { + cts-pins { + /* + * Configure a pull-down on CTS to match the pull of + * the Bluetooth module. + */ + pins = "gpio38"; + function = "gpio"; + bias-pull-down; + }; + + rts-pins { + /* + * Configure pull-down on RTS. As RTS is active low + * signal, pull it low to indicate the BT SoC that it + * can wakeup the system anytime from suspend state by + * pulling RX low (by sending wakeup bytes). + */ + pins = "gpio39"; + function = "gpio"; + bias-pull-down; + }; + + tx-pins { + /* + * Configure pull-up on TX when it isn't actively driven + * to prevent BT SoC from receiving garbage during sleep. + */ + pins = "gpio40"; + function = "gpio"; + bias-pull-up; + }; + + rx-pins { + /* + * Configure a pull-up on RX. This is needed to avoid + * garbage data when the TX pin of the Bluetooth module + * is floating which may cause spurious wakeups. + */ + pins = "gpio41"; + function = "gpio"; + bias-pull-up; + }; + }; + + sdc1_default: sdc1-default-state { + clk-pins { + pins = "sdc1_clk"; + drive-strength = <16>; + bias-disable; + }; + + cmd-pins { + pins = "sdc1_cmd"; + drive-strength = <16>; + bias-pull-up; + }; + + data-pins { + pins = "sdc1_data"; + drive-strength = <16>; + bias-pull-up; + }; + + rclk-pins { + pins = "sdc1_rclk"; + bias-pull-down; + }; + }; + + sdc1_sleep: sdc1-sleep-state { + clk-pins { + pins = "sdc1_clk"; + drive-strength = <2>; + bias-disable; + }; + + cmd-pins { + pins = "sdc1_cmd"; + drive-strength = <2>; + bias-pull-up; + }; + + data-pins { + pins = "sdc1_data"; + drive-strength = <2>; + bias-pull-up; + }; + + rclk-pins { + pins = "sdc1_rclk"; + bias-pull-down; + }; + }; + + sdc2_default: sdc2-default-state { + clk-pins { + pins = "sdc2_clk"; + bias-disable; + drive-strength = <16>; + }; + + cmd-pins { + pins = "sdc2_cmd"; + bias-pull-up; + drive-strength = <10>; + }; + + data-pins { + pins = "sdc2_data"; + bias-pull-up; + drive-strength = <10>; + }; + + sd-cd-pins { + pins = "gpio69"; + function = "gpio"; + bias-pull-up; + drive-strength = <2>; + }; + }; + + sdc2_sleep: sdc2-sleep-state { + clk-pins { + pins = "sdc2_clk"; + bias-disable; + drive-strength = <2>; + }; + + cmd-pins { + pins = "sdc2_cmd"; + bias-pull-up; + drive-strength = <2>; + }; + + data-pins { + pins = "sdc2_data"; + bias-pull-up; + drive-strength = <2>; + }; + + sd-cd-pins { + pins = "gpio69"; + function = "gpio"; + bias-pull-up; + drive-strength = <2>; + }; + }; +}; +&uart3 { + /delete-property/ interrupts; + interrupts-extended = <&intc GIC_SPI 604 IRQ_TYPE_LEVEL_HIGH>, + <&tlmm 41 IRQ_TYPE_EDGE_FALLING>; + + pinctrl-1 = <&qup_uart3_sleep>; + pinctrl-names = "default", "sleep"; + + status = "okay"; + + bluetooth: bluetooth { + compatible = "qcom,wcn3991-bt"; + vddio-supply = <&vreg_l10a_1p8>; + vddxo-supply = <&vreg_l1c_1p8>; + vddrf-supply = <&vreg_l2c_1p3>; + vddch0-supply = <&vreg_l10c_3p3>; + max-speed = <3200000>; + }; +}; + +&usb_1 { + status = "okay"; +}; + +&usb_1_dwc3 { + dr_mode = "host"; + #address-cells = <1>; + #size-cells = <0>; + + hub@1 { + compatible = "usb5e3,608"; + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + /* @1: 3.0 Type-A port on the back + * @2: 2.0 Type-A port the side + * @3: 2.0 Type-C port on the back + */ + + ethernet@4 { + compatible = "usbbda,8152"; + reg = <4>; + }; + }; +}; + +&usb_1_hsphy { + vdd-supply = <&vreg_l4a_0p8>; + vdda-pll-supply = <&vreg_l11a_1p8>; + vdda-phy-dpdm-supply = <&vreg_l17a_3p0>; + qcom,imp-res-offset-value = <8>; + qcom,preemphasis-level = ; + qcom,preemphasis-width = ; + qcom,bias-ctrl-value = <0x22>; + qcom,charge-ctrl-value = <3>; + qcom,hsdisc-trim-value = <0>; + + status = "okay"; +}; + +&usb_1_qmpphy { + vdda-phy-supply = <&vreg_l3c_1p2>; + vdda-pll-supply = <&vreg_l4a_0p8>; + + /delete-property/ mode-switch; + /delete-property/ orientation-switch; + + status = "okay"; + + ports { + port@0 { + #address-cells = <1>; + #size-cells = <0>; + + /delete-node/ endpoint; + + usb_1_qmpphy_dp_out: endpoint@0 { + reg = <0>; + + data-lanes = <3 2>; + remote-endpoint = <&hdmi_bridge_dp_in>; + }; + + usb_1_qmpphy_usb_ss_out: endpoint@1 { + reg = <1>; + + data-lanes = <1 0>; + remote-endpoint = <&usb_a_connector_ss_in>; + }; + }; + }; +}; + +&usb_1_qmpphy_dp_in { + remote-endpoint = <&mdss_dp_out>; +}; + +&venus { + firmware-name = "qcom/sc7180/ecs/liva-qc710/qcvss7180.mbn"; +}; + +&wifi { + vdd-0.8-cx-mx-supply = <&vreg_l9a_0p6>; + vdd-1.8-xo-supply = <&vreg_l1c_1p8>; + vdd-1.3-rfa-supply = <&vreg_l2c_1p3>; + vdd-3.3-ch0-supply = <&vreg_l10c_3p3>; + vdd-3.3-ch1-supply = <&vreg_l11c_3p3>; + + qcom,calibration-variant = "ECS_QC710"; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/sc7180.dtsi b/arch/arm64/boot/dts/qcom/sc7180.dtsi index 45b9864e3304..a4b17564469e 100644 --- a/arch/arm64/boot/dts/qcom/sc7180.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7180.dtsi @@ -1646,6 +1646,8 @@ qcom,smem-state-names = "ipa-clock-enabled-valid", "ipa-clock-enabled"; + sram = <&ipa_modem_tables>; + status = "disabled"; }; @@ -3460,8 +3462,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -3587,6 +3589,10 @@ ranges = <0 0 0x14680000 0x2e000>; + ipa_modem_tables: modem-tables@28000 { + reg = <0x28000 0x2000>; + }; + pil-reloc@2a94c { compatible = "qcom,pil-reloc-info"; reg = <0x2a94c 0xc8>; diff --git a/arch/arm64/boot/dts/qcom/sc7280-chrome-common.dtsi b/arch/arm64/boot/dts/qcom/sc7280-chrome-common.dtsi index 617a39d32488..debf62baec9b 100644 --- a/arch/arm64/boot/dts/qcom/sc7280-chrome-common.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280-chrome-common.dtsi @@ -140,17 +140,6 @@ dma-coherent; }; -&venus { - iommus = <&apps_smmu 0x2180 0x20>, - <&apps_smmu 0x2184 0x20>; - - status = "okay"; - - video-firmware { - iommus = <&apps_smmu 0x21a2 0x0>; - }; -}; - &watchdog { status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/sc8180x.dtsi b/arch/arm64/boot/dts/qcom/sc8180x.dtsi index 8319d892c6e4..f45deb188c6c 100644 --- a/arch/arm64/boot/dts/qcom/sc8180x.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8180x.dtsi @@ -3322,8 +3322,8 @@ dp0_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -3404,8 +3404,8 @@ dp1_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -3480,8 +3480,8 @@ edp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index 706eb1309d3f..761f229e8f47 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -4652,13 +4653,31 @@ port@0 { reg = <0>; + mdss0_intf0_out: endpoint { remote-endpoint = <&mdss0_dp0_in>; }; }; + port@1 { + reg = <1>; + + mdss0_intf1_out: endpoint { + remote-endpoint = <&mdss0_dsi0_in>; + }; + }; + + port@2 { + reg = <2>; + + mdss0_intf2_out: endpoint { + remote-endpoint = <&mdss0_dsi1_in>; + }; + }; + port@4 { reg = <4>; + mdss0_intf4_out: endpoint { remote-endpoint = <&mdss0_dp1_in>; }; @@ -4666,6 +4685,7 @@ port@5 { reg = <5>; + mdss0_intf5_out: endpoint { remote-endpoint = <&mdss0_dp3_in>; }; @@ -4673,6 +4693,7 @@ port@6 { reg = <6>; + mdss0_intf6_out: endpoint { remote-endpoint = <&mdss0_dp2_in>; }; @@ -4769,8 +4790,8 @@ mdss0_dp0_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -4791,6 +4812,189 @@ }; }; + mdss0_dsi0: dsi@ae94000 { + compatible = "qcom,sc8280xp-dsi-ctrl", + "qcom,sa8775p-dsi-ctrl", + "qcom,mdss-dsi-ctrl"; + reg = <0 0x0ae94000 0 0x400>; + reg-names = "dsi_ctrl"; + + interrupt-parent = <&mdss0>; + interrupts = <4>; + + clocks = <&dispcc0 DISP_CC_MDSS_BYTE0_CLK>, + <&dispcc0 DISP_CC_MDSS_BYTE0_INTF_CLK>, + <&dispcc0 DISP_CC_MDSS_PCLK0_CLK>, + <&dispcc0 DISP_CC_MDSS_ESC0_CLK>, + <&dispcc0 DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus"; + + assigned-clocks = <&dispcc0 DISP_CC_MDSS_BYTE0_CLK_SRC>, + <&dispcc0 DISP_CC_MDSS_PCLK0_CLK_SRC>; + assigned-clock-parents = <&mdss0_dsi0_phy DSI_BYTE_PLL_CLK>, + <&mdss0_dsi0_phy DSI_PIXEL_PLL_CLK>; + + operating-points-v2 = <&dsi_opp_table>; + power-domains = <&rpmhpd SC8280XP_MMCX>; + + refgen-supply = <&refgen>; + + phys = <&mdss0_dsi0_phy>; + phy-names = "dsi"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss0_dsi0_in: endpoint { + remote-endpoint = <&mdss0_intf1_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss0_dsi0_out: endpoint { + }; + }; + }; + + dsi_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-187500000 { + opp-hz = /bits/ 64 <187500000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-358000000 { + opp-hz = /bits/ 64 <358000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + }; + }; + + mdss0_dsi0_phy: phy@ae94400 { + compatible = "qcom,sc8280xp-dsi-phy-5nm", + "qcom,sa8775p-dsi-phy-5nm"; + reg = <0 0x0ae94400 0 0x200>, + <0 0x0ae94600 0 0x280>, + <0 0x0ae94900 0 0x280>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&dispcc0 DISP_CC_MDSS_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", "ref"; + + status = "disabled"; + }; + + mdss0_dsi1: dsi@ae96000 { + compatible = "qcom,sc8280xp-dsi-ctrl", + "qcom,sa8775p-dsi-ctrl", + "qcom,mdss-dsi-ctrl"; + reg = <0 0x0ae96000 0 0x400>; + reg-names = "dsi_ctrl"; + + interrupt-parent = <&mdss0>; + interrupts = <5>; + + clocks = <&dispcc0 DISP_CC_MDSS_BYTE1_CLK>, + <&dispcc0 DISP_CC_MDSS_BYTE1_INTF_CLK>, + <&dispcc0 DISP_CC_MDSS_PCLK1_CLK>, + <&dispcc0 DISP_CC_MDSS_ESC1_CLK>, + <&dispcc0 DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus"; + + assigned-clocks = <&dispcc0 DISP_CC_MDSS_BYTE1_CLK_SRC>, + <&dispcc0 DISP_CC_MDSS_PCLK1_CLK_SRC>; + assigned-clock-parents = <&mdss0_dsi1_phy DSI_BYTE_PLL_CLK>, + <&mdss0_dsi1_phy DSI_PIXEL_PLL_CLK>; + + operating-points-v2 = <&dsi_opp_table>; + power-domains = <&rpmhpd SC8280XP_MMCX>; + + refgen-supply = <&refgen>; + + phys = <&mdss0_dsi1_phy>; + phy-names = "dsi"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss0_dsi1_in: endpoint { + remote-endpoint = <&mdss0_intf2_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss0_dsi1_out: endpoint { + }; + }; + }; + }; + + mdss0_dsi1_phy: phy@ae96400 { + compatible = "qcom,sc8280xp-dsi-phy-5nm", + "qcom,sa8775p-dsi-phy-5nm"; + reg = <0 0x0ae96400 0 0x200>, + <0 0x0ae96600 0 0x280>, + <0 0x0ae96900 0 0x280>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&dispcc0 DISP_CC_MDSS_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", "ref"; + + status = "disabled"; + }; + mdss0_dp1: displayport-controller@ae98000 { compatible = "qcom,sc8280xp-dp"; reg = <0 0xae98000 0 0x200>, @@ -4851,8 +5055,8 @@ mdss0_dp1_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -4931,8 +5135,8 @@ mdss0_dp2_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5006,8 +5210,8 @@ mdss0_dp3_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5080,10 +5284,10 @@ <&mdss0_dp2_phy 1>, <&mdss0_dp3_phy 0>, <&mdss0_dp3_phy 1>, - <0>, - <0>, - <0>, - <0>; + <&mdss0_dsi0_phy DSI_BYTE_PLL_CLK>, + <&mdss0_dsi0_phy DSI_PIXEL_PLL_CLK>, + <&mdss0_dsi1_phy DSI_BYTE_PLL_CLK>, + <&mdss0_dsi1_phy DSI_PIXEL_PLL_CLK>; power-domains = <&rpmhpd SC8280XP_MMCX>; #clock-cells = <1>; @@ -6011,13 +6215,31 @@ port@0 { reg = <0>; + mdss1_intf0_out: endpoint { remote-endpoint = <&mdss1_dp0_in>; }; }; + port@1 { + reg = <1>; + + mdss1_intf1_out: endpoint { + remote-endpoint = <&mdss1_dsi0_in>; + }; + }; + + port@2 { + reg = <2>; + + mdss1_intf2_out: endpoint { + remote-endpoint = <&mdss1_dsi1_in>; + }; + }; + port@4 { reg = <4>; + mdss1_intf4_out: endpoint { remote-endpoint = <&mdss1_dp1_in>; }; @@ -6025,6 +6247,7 @@ port@5 { reg = <5>; + mdss1_intf5_out: endpoint { remote-endpoint = <&mdss1_dp3_in>; }; @@ -6032,6 +6255,7 @@ port@6 { reg = <6>; + mdss1_intf6_out: endpoint { remote-endpoint = <&mdss1_dp2_in>; }; @@ -6125,8 +6349,8 @@ mdss1_dp0_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -6147,6 +6371,170 @@ }; }; + mdss1_dsi0: dsi@22094000 { + compatible = "qcom,sc8280xp-dsi-ctrl", + "qcom,sa8775p-dsi-ctrl", + "qcom,mdss-dsi-ctrl"; + reg = <0 0x22094000 0 0x400>; + reg-names = "dsi_ctrl"; + + interrupt-parent = <&mdss1>; + interrupts = <4>; + + clocks = <&dispcc1 DISP_CC_MDSS_BYTE0_CLK>, + <&dispcc1 DISP_CC_MDSS_BYTE0_INTF_CLK>, + <&dispcc1 DISP_CC_MDSS_PCLK0_CLK>, + <&dispcc1 DISP_CC_MDSS_ESC0_CLK>, + <&dispcc1 DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus"; + + assigned-clocks = <&dispcc1 DISP_CC_MDSS_BYTE0_CLK_SRC>, + <&dispcc1 DISP_CC_MDSS_PCLK0_CLK_SRC>; + assigned-clock-parents = <&mdss1_dsi1_phy DSI_BYTE_PLL_CLK>, + <&mdss1_dsi0_phy DSI_PIXEL_PLL_CLK>; + + operating-points-v2 = <&dsi_opp_table>; + power-domains = <&rpmhpd SC8280XP_MMCX>; + + refgen-supply = <&refgen>; + + phys = <&mdss1_dsi0_phy>; + phy-names = "dsi"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss1_dsi0_in: endpoint { + remote-endpoint = <&mdss1_intf1_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss1_dsi0_out: endpoint { + }; + }; + }; + }; + + mdss1_dsi0_phy: phy@22094400 { + compatible = "qcom,sc8280xp-dsi-phy-5nm", + "qcom,sa8775p-dsi-phy-5nm"; + reg = <0 0x22094400 0 0x200>, + <0 0x22094600 0 0x280>, + <0 0x22094900 0 0x280>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&dispcc1 DISP_CC_MDSS_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", "ref"; + + status = "disabled"; + }; + + mdss1_dsi1: dsi@22096000 { + compatible = "qcom,sc8280xp-dsi-ctrl", + "qcom,sa8775p-dsi-ctrl", + "qcom,mdss-dsi-ctrl"; + reg = <0 0x22096000 0 0x400>; + reg-names = "dsi_ctrl"; + + interrupt-parent = <&mdss1>; + interrupts = <5>; + + clocks = <&dispcc1 DISP_CC_MDSS_BYTE1_CLK>, + <&dispcc1 DISP_CC_MDSS_BYTE1_INTF_CLK>, + <&dispcc1 DISP_CC_MDSS_PCLK1_CLK>, + <&dispcc1 DISP_CC_MDSS_ESC1_CLK>, + <&dispcc1 DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus"; + + assigned-clocks = <&dispcc1 DISP_CC_MDSS_BYTE1_CLK_SRC>, + <&dispcc1 DISP_CC_MDSS_PCLK1_CLK_SRC>; + assigned-clock-parents = <&mdss1_dsi1_phy DSI_BYTE_PLL_CLK>, + <&mdss1_dsi1_phy DSI_PIXEL_PLL_CLK>; + + operating-points-v2 = <&dsi_opp_table>; + power-domains = <&rpmhpd SC8280XP_MMCX>; + + refgen-supply = <&refgen>; + + phys = <&mdss1_dsi1_phy>; + phy-names = "dsi"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss1_dsi1_in: endpoint { + remote-endpoint = <&mdss1_intf2_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss1_dsi1_out: endpoint { + }; + }; + }; + }; + + mdss1_dsi1_phy: phy@22096400 { + compatible = "qcom,sc8280xp-dsi-phy-5nm", + "qcom,sa8775p-dsi-phy-5nm"; + reg = <0 0x22096400 0 0x200>, + <0 0x22096600 0 0x280>, + <0 0x22096900 0 0x280>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&dispcc1 DISP_CC_MDSS_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", "ref"; + + status = "disabled"; + }; + mdss1_dp1: displayport-controller@22098000 { compatible = "qcom,sc8280xp-dp"; reg = <0 0x22098000 0 0x200>, @@ -6205,8 +6593,8 @@ mdss1_dp1_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -6285,8 +6673,8 @@ mdss1_dp2_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -6360,8 +6748,8 @@ mdss1_dp3_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -6434,10 +6822,10 @@ <&mdss1_dp2_phy 1>, <&mdss1_dp3_phy 0>, <&mdss1_dp3_phy 1>, - <0>, - <0>, - <0>, - <0>; + <&mdss1_dsi0_phy DSI_BYTE_PLL_CLK>, + <&mdss1_dsi0_phy DSI_PIXEL_PLL_CLK>, + <&mdss1_dsi1_phy DSI_BYTE_PLL_CLK>, + <&mdss1_dsi1_phy DSI_PIXEL_PLL_CLK>; power-domains = <&rpmhpd SC8280XP_MMCX>; #clock-cells = <1>; @@ -6652,9 +7040,9 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; }; diff --git a/arch/arm64/boot/dts/qcom/sda660-inforce-ifc6560.dts b/arch/arm64/boot/dts/qcom/sda660-inforce-ifc6560.dts index 74cb29cb7f1a..9e14f53b552e 100644 --- a/arch/arm64/boot/dts/qcom/sda660-inforce-ifc6560.dts +++ b/arch/arm64/boot/dts/qcom/sda660-inforce-ifc6560.dts @@ -108,6 +108,43 @@ regulator-always-on; regulator-boot-on; }; + + wcn3990-pmu { + compatible = "qcom,wcn3990-pmu"; + + pinctrl-0 = <&sw_ctrl_default>; + pinctrl-names = "default"; + + vddio-supply = <&vreg_l13a_1p8>; + vddxo-supply = <&vreg_l9a_1p8>; + vddrf-supply = <&vreg_l6a_1p3>; + vddch0-supply = <&vreg_l19a_3p3>; + vddch1-supply = <&vreg_l8b_3p3>; + + swctrl-gpios = <&pm660_gpios 5 GPIO_ACTIVE_HIGH>; + + regulators { + vreg_pmu_io: ldo0 { + regulator-name = "vreg_pmu_io"; + }; + + vreg_pmu_xo: ldo1 { + regulator-name = "vreg_pmu_xo"; + }; + + vreg_pmu_rf: ldo2 { + regulator-name = "vreg_pmu_rf"; + }; + + vreg_pmu_ch0: ldo3 { + regulator-name = "vreg_pmu_ch0"; + }; + + vreg_pmu_ch1: ldo4 { + regulator-name = "vreg_pmu_ch1"; + }; + }; + }; }; &adreno_gpu { @@ -197,10 +234,10 @@ bluetooth { compatible = "qcom,wcn3990-bt"; - vddio-supply = <&vreg_l13a_1p8>; - vddxo-supply = <&vreg_l9a_1p8>; - vddrf-supply = <&vreg_l6a_1p3>; - vddch0-supply = <&vreg_l19a_3p3>; + vddio-supply = <&vreg_pmu_io>; + vddxo-supply = <&vreg_pmu_xo>; + vddrf-supply = <&vreg_pmu_rf>; + vddch0-supply = <&vreg_pmu_ch0>; max-speed = <3200000>; }; }; @@ -238,6 +275,16 @@ linux,code = ; }; +&pm660_gpios { + sw_ctrl_default: sw-ctrl-default-state { + pins = "gpio5"; + function = "normal"; + + input-enable; + bias-pull-down; + }; +}; + &qusb2phy0 { status = "okay"; @@ -503,11 +550,14 @@ }; &wifi { + /* SoC */ vdd-0.8-cx-mx-supply = <&vreg_l5a_0p8>; - vdd-1.8-xo-supply = <&vreg_l9a_1p8>; - vdd-1.3-rfa-supply = <&vreg_l6a_1p3>; - vdd-3.3-ch0-supply = <&vreg_l19a_3p3>; - vdd-3.3-ch1-supply = <&vreg_l8b_3p3>; + + /* WiFi / BT PMU */ + vdd-1.8-xo-supply = <&vreg_pmu_xo>; + vdd-1.3-rfa-supply = <&vreg_pmu_rf>; + vdd-3.3-ch0-supply = <&vreg_pmu_ch0>; + vdd-3.3-ch1-supply = <&vreg_pmu_ch1>; qcom,calibration-variant = "Inforce_IFC6560"; diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi index f4b8e8f468f2..bef3213165d6 100644 --- a/arch/arm64/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi @@ -1013,6 +1013,20 @@ drive-strength = <2>; }; }; + + spi7_default: spi7-default-state { + pins = "gpio24", "gpio25", "gpio26", "gpio27"; + function = "blsp_spi7"; + drive-strength = <6>; + bias-disable; + }; + + spi7_sleep: spi7-sleep-state { + pins = "gpio24", "gpio25", "gpio26", "gpio27"; + function = "blsp_spi7"; + drive-strength = <6>; + bias-disable; + }; }; remoteproc_mss: remoteproc@4080000 { @@ -1950,6 +1964,26 @@ status = "disabled"; }; + blsp_spi7: spi@c1b7000 { + compatible = "qcom,spi-qup-v2.2.1"; + reg = <0x0c1b7000 0x600>; + interrupts = ; + + clocks = <&gcc GCC_BLSP2_QUP3_SPI_APPS_CLK>, + <&gcc GCC_BLSP2_AHB_CLK>; + clock-names = "core", "iface"; + + dmas = <&blsp2_dma 8>, <&blsp2_dma 9>; + dma-names = "tx", "rx"; + + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&spi7_default>; + pinctrl-1 = <&spi7_sleep>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + blsp_i2c8: i2c@c1b8000 { compatible = "qcom,i2c-qup-v2.2.1"; reg = <0x0c1b8000 0x600>; @@ -2755,10 +2789,10 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; }; diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi index 746e9deba526..c195c79c1c85 100644 --- a/arch/arm64/boot/dts/qcom/sdm670.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi @@ -1130,6 +1130,17 @@ reg = <0x0 0x00ff1000 0x0 0x60>; }; + llcc: system-cache-controller@1100000 { + compatible = "qcom,sdm670-llcc"; + reg = <0 0x01100000 0 0x50000>, + <0 0x01180000 0 0x50000>, + <0 0x01300000 0 0x50000>; + reg-names = "llcc0_base", + "llcc1_base", + "llcc_broadcast_base"; + interrupts = ; + }; + mem_noc: interconnect@1380000 { compatible = "qcom,sdm670-mem-noc"; reg = <0 0x01380000 0 0x27200>; diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts index 5118b776a9bb..02416812b6a7 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts +++ b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts @@ -276,6 +276,43 @@ vin-supply = <&vbat_som>; }; + + wcn3990-pmu { + compatible = "qcom,wcn3990-pmu"; + + pinctrl-0 = <&sw_ctrl_default>; + pinctrl-names = "default"; + + vddio-supply = <&vreg_s4a_1p8>; + vddxo-supply = <&vreg_l7a_1p8>; + vddrf-supply = <&vreg_l17a_1p3>; + vddch0-supply = <&vreg_l25a_3p3>; + vddch1-supply = <&vreg_l23a_3p3>; + + swctrl-gpios = <&pm8998_gpios 3 GPIO_ACTIVE_HIGH>; + + regulators { + vreg_pmu_io: ldo0 { + regulator-name = "vreg_pmu_io"; + }; + + vreg_pmu_xo: ldo1 { + regulator-name = "vreg_pmu_xo"; + }; + + vreg_pmu_rf: ldo2 { + regulator-name = "vreg_pmu_rf"; + }; + + vreg_pmu_ch0: ldo3 { + regulator-name = "vreg_pmu_ch0"; + }; + + vreg_pmu_ch1: ldo4 { + regulator-name = "vreg_pmu_ch1"; + }; + }; + }; }; &adsp_pas { @@ -659,6 +696,14 @@ qcom,drive-strength = ; }; + sw_ctrl_default: sw-ctrl-default-state { + pins = "gpio3"; + function = "normal"; + + input-enable; + bias-pull-down; + }; + vol_up_pin_a: vol-up-active-state { pins = "gpio6"; function = "normal"; @@ -1038,10 +1083,11 @@ bluetooth { compatible = "qcom,wcn3990-bt"; - vddio-supply = <&vreg_s4a_1p8>; - vddxo-supply = <&vreg_l7a_1p8>; - vddrf-supply = <&vreg_l17a_1p3>; - vddch0-supply = <&vreg_l25a_3p3>; + vddio-supply = <&vreg_pmu_io>; + vddxo-supply = <&vreg_pmu_xo>; + vddrf-supply = <&vreg_pmu_rf>; + vddch0-supply = <&vreg_pmu_ch0>; + max-speed = <3200000>; }; }; @@ -1155,16 +1201,19 @@ }; &wifi { - status = "okay"; - + /* SoC */ vdd-0.8-cx-mx-supply = <&vreg_l5a_0p8>; - vdd-1.8-xo-supply = <&vreg_l7a_1p8>; - vdd-1.3-rfa-supply = <&vreg_l17a_1p3>; - vdd-3.3-ch0-supply = <&vreg_l25a_3p3>; - vdd-3.3-ch1-supply = <&vreg_l23a_3p3>; + + /* WiFi / BT PMU */ + vdd-1.8-xo-supply = <&vreg_pmu_xo>; + vdd-1.3-rfa-supply = <&vreg_pmu_rf>; + vdd-3.3-ch0-supply = <&vreg_pmu_ch0>; + vdd-3.3-ch1-supply = <&vreg_pmu_ch1>; qcom,snoc-host-cap-8bit-quirk; qcom,calibration-variant = "Thundercomm_DB845C"; + + status = "okay"; }; /* PINCTRL - additions to nodes defined in sdm845.dtsi */ diff --git a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi index fd9788d5c3f5..693006685776 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi @@ -131,6 +131,33 @@ vin-supply = <&vph_pwr>; }; + + wcn3990-pmu { + compatible = "qcom,wcn3990-pmu"; + + vddio-supply = <&vreg_s4a_1p8>; + vddxo-supply = <&vreg_l7a_1p8>; + vddrf-supply = <&vreg_l17a_1p3>; + vddch0-supply = <&vreg_l25a_3p3>; + + regulators { + vreg_pmu_io: ldo0 { + regulator-name = "vreg_pmu_io"; + }; + + vreg_pmu_xo: ldo1 { + regulator-name = "vreg_pmu_xo"; + }; + + vreg_pmu_rf: ldo2 { + regulator-name = "vreg_pmu_rf"; + }; + + vreg_pmu_ch0: ldo3 { + regulator-name = "vreg_pmu_ch0"; + }; + }; + }; }; &adsp_pas { @@ -462,10 +489,11 @@ bluetooth { compatible = "qcom,wcn3990-bt"; - vddio-supply = <&vreg_s4a_1p8>; - vddxo-supply = <&vreg_l7a_1p8>; - vddrf-supply = <&vreg_l17a_1p3>; - vddch0-supply = <&vreg_l25a_3p3>; + vddio-supply = <&vreg_pmu_io>; + vddxo-supply = <&vreg_pmu_xo>; + vddrf-supply = <&vreg_pmu_rf>; + vddch0-supply = <&vreg_pmu_ch0>; + max-speed = <3200000>; }; }; @@ -526,9 +554,9 @@ &wifi { vdd-0.8-cx-mx-supply = <&vreg_l5a_0p8>; - vdd-1.8-xo-supply = <&vreg_l7a_1p8>; - vdd-1.3-rfa-supply = <&vreg_l17a_1p3>; - vdd-3.3-ch0-supply = <&vreg_l25a_3p3>; + vdd-1.8-xo-supply = <&vreg_pmu_xo>; + vdd-1.3-rfa-supply = <&vreg_pmu_rf>; + vdd-3.3-ch0-supply = <&vreg_pmu_ch0>; qcom,snoc-host-cap-8bit-quirk; diff --git a/arch/arm64/boot/dts/qcom/sdm845-lg-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-lg-common.dtsi index 0ee2f4b99fbd..71d070619ad7 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-lg-common.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845-lg-common.dtsi @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -27,10 +28,19 @@ /delete-node/ &wlan_msa_mem; / { + chassis-type = "handset"; + + aliases { + serial0 = &uart9; + serial1 = &uart6; + }; + chosen { #address-cells = <2>; #size-cells = <2>; ranges; + + stdout-path = "serial0:115200n8"; }; reserved-memory { @@ -38,11 +48,6 @@ #size-cells = <2>; ranges; - qseecom_mem: memory@b2000000 { - reg = <0 0xb2000000 0 0x1800000>; - no-map; - }; - gpu_mem: memory@8c415000 { reg = <0 0x8c415000 0 0x2000>; no-map; @@ -99,6 +104,11 @@ no-map; }; + qseecom_mem: memory@b2000000 { + reg = <0 0xb2000000 0 0x1800000>; + no-map; + }; + rmtfs_mem: rmtfs-region@f0800000 { compatible = "qcom,rmtfs-mem"; reg = <0 0xf0800000 0 0x202000>; @@ -442,10 +452,6 @@ status = "okay"; }; -&dispcc { - status = "disabled"; -}; - &gcc { protected-clocks = , , @@ -454,6 +460,14 @@ ; }; +&gpi_dma0 { + status = "okay"; +}; + +&gpi_dma1 { + status = "okay"; +}; + &gpu { status = "okay"; }; @@ -467,14 +481,76 @@ status = "okay"; }; +&pm8998_gpios { + vol_up_pin_a: vol-up-active-state { + pins = "gpio6"; + function = "normal"; + input-enable; + bias-pull-up; + qcom,drive-strength = ; + }; +}; + &pm8998_resin { linux,code = ; status = "okay"; }; -&sdhc_2 { +&pmi8998_lpg { status = "okay"; + multi-led { + color = ; + function = LED_FUNCTION_STATUS; + + #address-cells = <1>; + #size-cells = <0>; + + led@3 { + reg = <3>; + color = ; + }; + + led@4 { + reg = <4>; + color = ; + }; + + led@5 { + reg = <5>; + color = ; + }; + }; +}; + +&pmi8998_wled { + qcom,current-limit-microamp = <20000>; + qcom,ovp-millivolt = <29600>; + qcom,switching-freq = <800>; + qcom,num-strings = <3>; + qcom,cabc; + status = "okay"; +}; + +&qupv3_id_0 { + status = "okay"; +}; + +&qupv3_id_1 { + status = "okay"; +}; + +&qup_uart9_rx { + drive-strength = <2>; + bias-pull-up; +}; + +&qup_uart9_tx { + drive-strength = <2>; + bias-disable; +}; + +&sdhc_2 { cd-gpios = <&tlmm 126 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; @@ -482,55 +558,8 @@ vmmc-supply = <&vreg_l21a_2p95>; vqmmc-supply = <&vddpx_2>; -}; -/* - * UFS works partially and only with clk_ignore_unused. - * Sometimes it crashes with I/O errors. - */ -&ufs_mem_hc { status = "okay"; - - reset-gpios = <&tlmm 150 GPIO_ACTIVE_LOW>; - - vcc-supply = <&vreg_l20a_2p95>; - vcc-max-microamp = <600000>; -}; - -&ufs_mem_phy { - status = "okay"; - - vdda-phy-supply = <&vdda_ufs1_core>; - vdda-pll-supply = <&vdda_ufs1_1p2>; -}; - -&usb_1 { - status = "okay"; -}; - -&usb_1_dwc3 { - /* TODO: these devices have usb id pin */ - dr_mode = "peripheral"; -}; - -&usb_1_hsphy { - status = "okay"; - - vdd-supply = <&vdda_usb1_ss_core>; - vdda-pll-supply = <&vdda_qusb_hs0_1p8>; - vdda-phy-dpdm-supply = <&vdda_qusb_hs0_3p1>; - - qcom,imp-res-offset-value = <8>; - qcom,hstx-trim-value = ; - qcom,preemphasis-level = ; - qcom,preemphasis-width = ; -}; - -&usb_1_qmpphy { - status = "okay"; - - vdda-phy-supply = <&vdda_usb1_ss_1p2>; - vdda-pll-supply = <&vdda_usb1_ss_core>; }; /* PINCTRL - additions to nodes defined in sdm845.dtsi */ @@ -571,12 +600,85 @@ }; }; -&pm8998_gpios { - vol_up_pin_a: vol-up-active-state { - pins = "gpio6"; - function = "normal"; - input-enable; - bias-pull-up; - qcom,drive-strength = ; +&uart6 { + pinctrl-0 = <&qup_uart6_4pin>; + + status = "okay"; + + bluetooth: bluetooth { + compatible = "qcom,wcn3990-bt"; + + vddio-supply = <&vreg_s4a_1p8>; + vddxo-supply = <&vreg_l7a_1p8>; + vddrf-supply = <&vreg_l17a_1p3>; + vddch0-supply = <&vreg_l25a_3p3>; + max-speed = <3200000>; }; }; + +&uart9 { + status = "okay"; +}; + +/* + * UFS works partially and only with clk_ignore_unused. + * Sometimes it crashes with I/O errors. + */ +&ufs_mem_hc { + reset-gpios = <&tlmm 150 GPIO_ACTIVE_LOW>; + + vcc-supply = <&vreg_l20a_2p95>; + vcc-max-microamp = <600000>; + + status = "okay"; +}; + +&ufs_mem_phy { + vdda-phy-supply = <&vdda_ufs1_core>; + vdda-pll-supply = <&vdda_ufs1_1p2>; + + status = "okay"; +}; + +&usb_1 { + status = "okay"; +}; + +&usb_1_dwc3 { + /* TODO: these devices have usb id pin */ + dr_mode = "peripheral"; +}; + +&usb_1_hsphy { + vdd-supply = <&vdda_usb1_ss_core>; + vdda-pll-supply = <&vdda_qusb_hs0_1p8>; + vdda-phy-dpdm-supply = <&vdda_qusb_hs0_3p1>; + + qcom,imp-res-offset-value = <8>; + qcom,hstx-trim-value = ; + qcom,preemphasis-level = ; + qcom,preemphasis-width = ; + + status = "okay"; +}; + +&usb_1_qmpphy { + vdda-phy-supply = <&vdda_usb1_ss_1p2>; + vdda-pll-supply = <&vdda_usb1_ss_core>; + + status = "okay"; +}; + +&venus { + status = "okay"; +}; + +&wifi { + vdd-0.8-cx-mx-supply = <&vreg_l5a_0p8>; + vdd-1.8-xo-supply = <&vreg_l7a_1p8>; + vdd-1.3-rfa-supply = <&vreg_l17a_1p3>; + vdd-3.3-ch0-supply = <&vreg_l25a_3p3>; + vdd-3.3-ch1-supply = <&vreg_l23a_3p3>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/sdm845-lg-judyln.dts b/arch/arm64/boot/dts/qcom/sdm845-lg-judyln.dts index 09bfcef42402..adf41aa0146a 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-lg-judyln.dts +++ b/arch/arm64/boot/dts/qcom/sdm845-lg-judyln.dts @@ -21,8 +21,6 @@ height = <3120>; stride = <(1440 * 4)>; format = "a8r8g8b8"; - lab-supply = <&lab>; - ibb-supply = <&ibb>; }; }; @@ -37,22 +35,110 @@ interrupts = <89 IRQ_TYPE_LEVEL_LOW>; }; }; + + battery: battery { + compatible = "simple-battery"; + + charge-full-design-microamp-hours = <3000000>; + voltage-min-design-microvolt = <3200000>; + voltage-max-design-microvolt = <4400000>; + }; }; &adsp_pas { - firmware-name = "qcom/sdm845/judyln/adsp.mbn"; + firmware-name = "qcom/sdm845/LG/judyln/adsp.mbn"; +}; + +&bluetooth { + /* + * This path is relative to the qca/ + * subdir under lib/firmware. + */ + firmware-name = "judyln/crnv21.bin"; }; &cdsp_pas { - firmware-name = "qcom/sdm845/judyln/cdsp.mbn"; + firmware-name = "qcom/sdm845/LG/judyln/cdsp.mbn"; }; &gpu_zap_shader { - firmware-name = "qcom/sdm845/judyln/a630_zap.mbn"; + firmware-name = "qcom/sdm845/LG/judyln/a630_zap.mbn"; +}; + +&ipa { + firmware-name = "qcom/sdm845/LG/judyln/ipa_fws.mbn"; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dsi0 { + vdda-supply = <&vdda_mipi_dsi0_1p2>; + + status = "okay"; + + display_panel: panel@0 { + reg = <0>; + compatible = "lg,sw49410-lh609qh1", "lg,sw49410"; + + backlight = <&pmi8998_wled>; + reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>; + width-mm = <65>; + height-mm = <140>; + + vsp-supply = <&lab>; + vsn-supply = <&ibb>; + + pinctrl-0 = <&sde_dsi_active &sde_te_active_sleep>; + pinctrl-1 = <&sde_dsi_sleep &sde_te_active_sleep>; + pinctrl-names = "default", "sleep"; + + port { + panel_in: endpoint { + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + }; +}; + +&mdss_dsi0_phy { + vdds-supply = <&vdda_mipi_dsi0_pll>; + + status = "okay"; +}; + +&mdss_dsi0_out { + data-lanes = <0 1 2 3>; + remote-endpoint = <&panel_in>; + qcom,te-source = "mdp_vsync_e"; +}; + +&ibb { + regulator-min-microvolt = <5500000>; + regulator-max-microvolt = <5500000>; + regulator-over-current-protection; + regulator-pull-down; + regulator-soft-start; + qcom,discharge-resistor-kohms = <300>; +}; + +&lab { + regulator-min-microvolt = <5500000>; + regulator-max-microvolt = <5500000>; + regulator-over-current-protection; + regulator-pull-down; + regulator-soft-start; }; &mss_pil { - firmware-name = "qcom/sdm845/judyln/mba.mbn", "qcom/sdm845/judyln/modem.mbn"; + firmware-name = "qcom/sdm845/LG/judyln/mba.mbn", "qcom/sdm845/LG/judyln/modem.mbn"; +}; + +&pmi8998_charger { + monitored-battery = <&battery>; + + status = "okay"; }; &tlmm { @@ -63,4 +149,33 @@ drive-strength = <2>; bias-pull-up; }; + + sde_dsi_active: sde-dsi-active-state { + pins = "gpio6"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + + sde_dsi_sleep: sde-dsi-sleep-state { + pins = "gpio6"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + sde_te_active_sleep: sde-te-active-sleep-state { + pins = "gpio10"; + function = "mdp_vsync"; + drive-strength = <2>; + bias-pull-down; + }; +}; + +&venus { + firmware-name = "qcom/sdm845/LG/judyln/venus.mbn"; +}; + +&wifi { + qcom,calibration-variant = "lg_judyln"; }; diff --git a/arch/arm64/boot/dts/qcom/sdm845-lg-judyp.dts b/arch/arm64/boot/dts/qcom/sdm845-lg-judyp.dts index ffe1da2227f0..d244ebdd17be 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-lg-judyp.dts +++ b/arch/arm64/boot/dts/qcom/sdm845-lg-judyp.dts @@ -26,17 +26,37 @@ }; &adsp_pas { - firmware-name = "qcom/sdm845/judyp/adsp.mbn"; + firmware-name = "qcom/sdm845/LG/judyp/adsp.mbn"; +}; + +&bluetooth { + /* + * This path is relative to the qca/ + * subdir under lib/firmware. + */ + firmware-name = "judyp/crnv21.bin"; }; &cdsp_pas { - firmware-name = "qcom/sdm845/judyp/cdsp.mbn"; + firmware-name = "qcom/sdm845/LG/judyp/cdsp.mbn"; }; &gpu_zap_shader { - firmware-name = "qcom/sdm845/judyp/a630_zap.mbn"; + firmware-name = "qcom/sdm845/LG/judyp/a630_zap.mbn"; +}; + +&ipa { + firmware-name = "qcom/sdm845/LG/judyp/ipa_fws.mbn"; }; &mss_pil { - firmware-name = "qcom/sdm845/judyp/mba.mbn", "qcom/sdm845/judyp/modem.mbn"; + firmware-name = "qcom/sdm845/LG/judyp/mba.mbn", "qcom/sdm845/LG/judyp/modem.mbn"; +}; + +&venus { + firmware-name = "qcom/sdm845/LG/judyp/venus.mbn"; +}; + +&wifi { + qcom,calibration-variant = "lg_judyp"; }; diff --git a/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi index 5b121ea5520f..6b7378cf4d49 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi @@ -25,6 +25,41 @@ chassis-type = "handset"; qcom,msm-id = ; + alert-slider { + compatible = "gpio-keys"; + label = "Alert slider"; + + pinctrl-0 = <&alert_slider_default>; + pinctrl-names = "default"; + + switch-top { + label = "Silent"; + linux,input-type = ; + linux,code = ; + linux,input-value = ; + gpios = <&tlmm 126 GPIO_ACTIVE_LOW>; + linux,can-disable; + }; + + switch-middle { + label = "Vibrate"; + linux,input-type = ; + linux,code = ; + linux,input-value = ; + gpios = <&tlmm 52 GPIO_ACTIVE_LOW>; + linux,can-disable; + }; + + switch-bottom { + label = "Ring"; + linux,input-type = ; + linux,code = ; + linux,input-value = ; + gpios = <&tlmm 24 GPIO_ACTIVE_LOW>; + linux,can-disable; + }; + }; + aliases { serial0 = &uart9; serial1 = &uart6; @@ -195,6 +230,43 @@ pinctrl-names = "default"; regulator-boot-on; }; + + wcn3990-pmu { + compatible = "qcom,wcn3990-pmu"; + + pinctrl-0 = <&sw_ctrl_default>; + pinctrl-names = "default"; + + vddio-supply = <&vreg_s4a_1p8>; + vddxo-supply = <&vreg_l7a_1p8>; + vddrf-supply = <&vreg_l17a_1p3>; + vddch0-supply = <&vreg_l25a_3p3>; + vddch1-supply = <&vreg_l23a_3p3>; + + swctrl-gpios = <&pm8998_gpios 3 GPIO_ACTIVE_HIGH>; + + regulators { + vreg_pmu_io: ldo0 { + regulator-name = "vreg_pmu_io"; + }; + + vreg_pmu_xo: ldo1 { + regulator-name = "vreg_pmu_xo"; + }; + + vreg_pmu_rf: ldo2 { + regulator-name = "vreg_pmu_rf"; + }; + + vreg_pmu_ch0: ldo3 { + regulator-name = "vreg_pmu_ch0"; + }; + + vreg_pmu_ch1: ldo4 { + regulator-name = "vreg_pmu_ch1"; + }; + }; + }; }; &adsp_pas { @@ -501,6 +573,14 @@ }; &pm8998_gpios { + sw_ctrl_default: sw-ctrl-default-state { + pins = "gpio3"; + function = "normal"; + + input-enable; + bias-pull-down; + }; + volume_down_gpio: pm8998-gpio5-state { pinconf { pins = "gpio5"; @@ -769,10 +849,11 @@ */ firmware-name = "OnePlus/enchilada/crnv21.bin"; - vddio-supply = <&vreg_s4a_1p8>; - vddxo-supply = <&vreg_l7a_1p8>; - vddrf-supply = <&vreg_l17a_1p3>; - vddch0-supply = <&vreg_l25a_3p3>; + vddio-supply = <&vreg_pmu_io>; + vddxo-supply = <&vreg_pmu_xo>; + vddrf-supply = <&vreg_pmu_rf>; + vddch0-supply = <&vreg_pmu_ch0>; + max-speed = <3200000>; }; }; @@ -946,13 +1027,14 @@ }; &wifi { - status = "okay"; vdd-0.8-cx-mx-supply = <&vreg_l5a_0p8>; - vdd-1.8-xo-supply = <&vreg_l7a_1p8>; - vdd-1.3-rfa-supply = <&vreg_l17a_1p3>; - vdd-3.3-ch0-supply = <&vreg_l25a_3p3>; - vdd-3.3-ch1-supply = <&vreg_l23a_3p3>; + vdd-1.8-xo-supply = <&vreg_pmu_xo>; + vdd-1.3-rfa-supply = <&vreg_pmu_rf>; + vdd-3.3-ch0-supply = <&vreg_pmu_ch0>; + vdd-3.3-ch1-supply = <&vreg_pmu_ch1>; qcom,calibration-variant = "oneplus_sdm845"; qcom,snoc-host-cap-8bit-quirk; + + status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts b/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts index 51b041f91d3e..7d81198bc499 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts +++ b/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts @@ -432,6 +432,8 @@ }; &i2c5 { + clock-frequency = <400000>; + status = "okay"; touchscreen@38 { @@ -457,6 +459,19 @@ /* SMB1355@0x0C */ }; +&i2c11 { + clock-frequency = <400000>; + + status = "okay"; + + audio-codec@34 { + compatible = "nxp,tfa9890"; + reg = <0x34>; + vddd-supply = <&vreg_s4a_1p8>; + #sound-dai-cells = <0>; + }; +}; + &ipa { qcom,gsi-loader = "self"; memory-region = <&ipa_fw_mem>; @@ -483,8 +498,8 @@ reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>; pinctrl-names = "default", "sleep"; - pinctrl-0 = <&sde_dsi_active &sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>; + pinctrl-0 = <&sde_dsi_active &sde_te>; + pinctrl-1 = <&sde_dsi_suspend &sde_te>; port { panel_in_0: endpoint { @@ -600,6 +615,24 @@ status = "okay"; }; +&sdhc_2 { + pinctrl-0 = <&sdc2_default_state &sdc2_card_det_n>; + pinctrl-names = "default"; + + vmmc-supply = <&vreg_l21a_2p95>; + vqmmc-supply = <&vreg_l13a_2p95>; + + bus-width = <4>; + /* + * Card detection is broken, but because the battery must be removed + * to insert the card, we use this rather than the broken-cd property + * which would just waste CPU cycles polling. + */ + non-removable; + + status = "okay"; +}; + &slpi_pas { firmware-name = "qcom/sdm845/SHIFT/axolotl/slpi.mbn"; @@ -609,6 +642,32 @@ &tlmm { gpio-reserved-ranges = <0 4>, <81 4>; + sdc2_default_state: sdc2-default-state { + clk-pins { + pins = "sdc2_clk"; + drive-strength = <16>; + bias-disable; + }; + + cmd-pins { + pins = "sdc2_cmd"; + drive-strength = <10>; + bias-pull-up; + }; + + data-pins { + pins = "sdc2_data"; + drive-strength = <10>; + bias-pull-up; + }; + }; + + sdc2_card_det_n: sd-card-det-n-state { + pins = "gpio126"; + function = "gpio"; + bias-disable; + }; + sde_dsi_active: sde-dsi-active-state { pins = "gpio6", "gpio11"; function = "gpio"; @@ -623,14 +682,7 @@ bias-pull-down; }; - sde_te_active: sde-te-active-state { - pins = "gpio10"; - function = "mdp_vsync"; - drive-strength = <2>; - bias-pull-down; - }; - - sde_te_suspend: sde-te-suspend-state { + sde_te: sde-te-state { pins = "gpio10"; function = "mdp_vsync"; drive-strength = <2>; diff --git a/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi index 7dc9349eedfd..4c63286d8b04 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi @@ -17,8 +17,8 @@ qcom,board-id = <8 0>; aliases { - serial0 = &uart6; - serial1 = &uart9; + serial0 = &uart9; + serial1 = &uart6; }; chosen { @@ -755,6 +755,19 @@ &uart6 { status = "okay"; + + bluetooth { + compatible = "qcom,wcn3990-bt"; + + firmware-name = "Sony/tama/crnv21.bin"; + + vddio-supply = <&vreg_s3a_1p3>; + vddxo-supply = <&vreg_s5a_1p9>; + vddrf-supply = <&vreg_l17a_1p3>; + vddch0-supply = <&vreg_l25a_3p0>; + + max-speed = <3200000>; + }; }; &uart9 { diff --git a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi index 01b570d0880d..1298485c4214 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi @@ -148,6 +148,7 @@ regulator-min-microvolt = <880000>; regulator-max-microvolt = <880000>; regulator-initial-mode = ; + regulator-boot-on; }; vreg_l5a_0p8: ldo5 { diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi index bf2f9c04adba..4ae8627d6dbc 100644 --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi @@ -2780,6 +2780,8 @@ qcom,smem-state-names = "ipa-clock-enabled-valid", "ipa-clock-enabled"; + sram = <&ipa_modem_tables>; + status = "disabled"; }; @@ -2805,6 +2807,62 @@ gpio-ranges = <&tlmm 0 0 151>; wakeup-parent = <&pdc_intc>; + cam_mclk0_default: cam-mclk0-default-state { + pins = "gpio13"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + cam_mclk0_sleep: cam-mclk0-sleep-state { + pins = "gpio13"; + function = "cam_mclk"; + drive-strength = <2>; + bias-pull-down; + }; + + cam_mclk1_default: cam-mclk1-default-state { + pins = "gpio14"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + cam_mclk1_sleep: cam-mclk1-sleep-state { + pins = "gpio14"; + function = "cam_mclk"; + drive-strength = <2>; + bias-pull-down; + }; + + cam_mclk2_default: cam-mclk2-default-state { + pins = "gpio15"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + cam_mclk2_sleep: cam-mclk2-sleep-state { + pins = "gpio15"; + function = "cam_mclk"; + drive-strength = <2>; + bias-pull-down; + }; + + cam_mclk3_default: cam-mclk3-default-state { + pins = "gpio16"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + cam_mclk3_sleep: cam-mclk3-sleep-state { + pins = "gpio16"; + function = "cam_mclk"; + drive-strength = <2>; + bias-pull-down; + }; + cci0_default: cci0-default-state { /* SDA, SCL */ pins = "gpio17", "gpio18"; @@ -5134,6 +5192,10 @@ ranges = <0 0 0x14680000 0x40000>; + ipa_modem_tables: modem-tables@3d000 { + reg = <0x3d000 0x2000>; + }; + pil-reloc@3f94c { compatible = "qcom,pil-reloc-info"; reg = <0x3f94c 0xc8>; diff --git a/arch/arm64/boot/dts/qcom/sdx75.dtsi b/arch/arm64/boot/dts/qcom/sdx75.dtsi index eff4c9055d66..d1b61530b562 100644 --- a/arch/arm64/boot/dts/qcom/sdx75.dtsi +++ b/arch/arm64/boot/dts/qcom/sdx75.dtsi @@ -1580,9 +1580,9 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm4450.dtsi b/arch/arm64/boot/dts/qcom/sm4450.dtsi index d217d922811e..696e2e0841ad 100644 --- a/arch/arm64/boot/dts/qcom/sm4450.dtsi +++ b/arch/arm64/boot/dts/qcom/sm4450.dtsi @@ -678,9 +678,9 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm6115.dtsi b/arch/arm64/boot/dts/qcom/sm6115.dtsi index e9336adbc391..bd94eb87d6f9 100644 --- a/arch/arm64/boot/dts/qcom/sm6115.dtsi +++ b/arch/arm64/boot/dts/qcom/sm6115.dtsi @@ -1864,6 +1864,8 @@ <&gcc GCC_DISP_HF_AXI_CLK>, <&dispcc DISP_CC_MDSS_MDP_CLK>; + resets = <&dispcc DISP_CC_MDSS_CORE_BCR>; + interrupts = ; interrupt-controller; #interrupt-cells = <1>; @@ -3460,9 +3462,9 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm6125-xiaomi-ginkgo-common.dtsi b/arch/arm64/boot/dts/qcom/sm6125-xiaomi-ginkgo-common.dtsi new file mode 100644 index 000000000000..7eecd9dc3028 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm6125-xiaomi-ginkgo-common.dtsi @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2025, Gabriel Gonzales + */ + +/dts-v1/; + +#include +#include +#include +#include +#include +#include "sm6125.dtsi" +#include "pm6125.dtsi" + +/delete-node/ &adsp_pil_mem; +/delete-node/ &cont_splash_mem; +/delete-node/ &gpu_mem; +/delete-node/ &ipa_fw_mem; +/delete-node/ &ipa_gsi_mem; + +/ { + model = "Xiaomi Redmi Note 8"; + compatible = "xiaomi,ginkgo", "qcom,sm6125"; + chassis-type = "handset"; + + qcom,msm-id = ; + + aliases { + serial0 = &uart4; + }; + + chosen { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + framebuffer { + compatible = "simple-framebuffer"; + memory-region = <&framebuffer_mem>; + width = <1080>; + height = <2340>; + stride = <(1080 * 4)>; + format = "a8r8g8b8"; + }; + }; + + reserved-memory { + adsp_pil_mem: adsp_pil_mem@55300000 { + reg = <0x0 0x55300000 0x0 0x2200000>; + no-map; + }; + + ipa_fw_mem: ipa_fw_mem@57500000 { + reg = <0x0 0x57500000 0x0 0x10000>; + no-map; + }; + + ipa_gsi_mem: ipa_gsi_mem@57510000 { + reg = <0x0 0x57510000 0x0 0x5000>; + no-map; + }; + + gpu_mem: gpu_mem@57515000 { + reg = <0x0 0x57515000 0x0 0x2000>; + no-map; + }; + + framebuffer_mem: framebuffer@5c000000 { + reg = <0x0 0x5c000000 0x0 (2340 * 1080 * 4)>; + no-map; + }; + + /* Matching with recovery values to be able to get the results. */ + ramoops@61600000 { + compatible = "ramoops"; + reg = <0x0 0x61600000 0x0 0x400000>; + record-size = <0x80000>; + pmsg-size = <0x200000>; + console-size = <0x100000>; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&vol_up_n>; + pinctrl-names = "default"; + + key-volume-up { + label = "Volume Up"; + gpios = <&pm6125_gpios 5 GPIO_ACTIVE_LOW>; + linux,code = ; + debounce-interval = <15>; + linux,can-disable; + wakeup-source; + }; + }; +}; + +&pm6125_gpios { + vol_up_n: vol-up-n-state { + pins = "gpio5"; + function = "normal"; + power-source = <0>; + bias-pull-up; + input-enable; + }; +}; + +&hsusb_phy1 { + vdd-supply = <&vreg_l7a>; + vdda-pll-supply = <&vreg_l10a>; + vdda-phy-dpdm-supply = <&vreg_l15a>; + status = "okay"; +}; + +&pon_pwrkey { + status = "okay"; +}; + +&pon_resin { + linux,code = ; + status = "okay"; +}; + +&qupv3_id_0 { + status = "okay"; +}; + +&rpm_requests { + regulators-0 { + compatible = "qcom,rpm-pm6125-regulators"; + + vreg_s6a: s6 { + regulator-min-microvolt = <936000>; + regulator-max-microvolt = <1422000>; + }; + + vreg_l1a: l1 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1256000>; + }; + + vreg_l2a: l2 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1056000>; + }; + + vreg_l3a: l3 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1064000>; + }; + + vreg_l4a: l4 { + regulator-min-microvolt = <872000>; + regulator-max-microvolt = <976000>; + regulator-allow-set-load; + }; + + vreg_l5a: l5 { + regulator-min-microvolt = <1648000>; + regulator-max-microvolt = <2950000>; + regulator-allow-set-load; + }; + + vreg_l6a: l6 { + regulator-min-microvolt = <576000>; + regulator-max-microvolt = <656000>; + }; + + vreg_l7a: l7 { + regulator-min-microvolt = <872000>; + regulator-max-microvolt = <976000>; + }; + + vreg_l8a: l8 { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <728000>; + }; + + vreg_l9a: l9 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1896000>; + }; + + vreg_l10a: l10 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1896000>; + regulator-allow-set-load; + }; + + vreg_l11a: l11 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1952000>; + regulator-allow-set-load; + }; + + vreg_l12a: l12 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1996000>; + }; + + vreg_l13a: l13 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1832000>; + }; + + vreg_l14a: l14 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1904000>; + }; + + vreg_l15a: l15 { + regulator-min-microvolt = <3104000>; + regulator-max-microvolt = <3232000>; + }; + + vreg_l16a: l16 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1904000>; + }; + + vreg_l17a: l17 { + regulator-min-microvolt = <1248000>; + regulator-max-microvolt = <1304000>; + }; + + vreg_l18a: l18 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1264000>; + regulator-allow-set-load; + }; + + vreg_l19a: l19 { + regulator-min-microvolt = <1648000>; + regulator-max-microvolt = <2952000>; + }; + + vreg_l20a: l20 { + regulator-min-microvolt = <1648000>; + regulator-max-microvolt = <2952000>; + }; + + vreg_l21a: l21 { + regulator-min-microvolt = <2600000>; + regulator-max-microvolt = <2856000>; + }; + + vreg_l22a: l22 { + regulator-min-microvolt = <2944000>; + regulator-max-microvolt = <2950000>; + regulator-allow-set-load; + }; + + vreg_l23a: l23 { + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3400000>; + }; + + vreg_l24a: l24 { + regulator-min-microvolt = <2944000>; + regulator-max-microvolt = <2950000>; + regulator-allow-set-load; + }; + + }; +}; + +&sdc2_off_state { + sd-cd-pins { + pins = "gpio98"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; +}; + +&sdc2_on_state { + sd-cd-pins { + pins = "gpio98"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; +}; + +&sdhc_1 { + vmmc-supply = <&vreg_l24a>; + vqmmc-supply = <&vreg_l11a>; + status = "okay"; +}; + +&sdhc_2 { + cd-gpios = <&tlmm 98 GPIO_ACTIVE_HIGH>; + vmmc-supply = <&vreg_l22a>; + vqmmc-supply = <&vreg_l5a>; + no-sdio; + no-mmc; + status = "okay"; +}; + +&tlmm { + gpio-reserved-ranges = <0 4>, <30 4>; +}; + +&uart4 { + status = "okay"; +}; + +&usb3 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/sm6125-xiaomi-ginkgo.dts b/arch/arm64/boot/dts/qcom/sm6125-xiaomi-ginkgo.dts index 6b68e391cf3e..496f33e9d73c 100644 --- a/arch/arm64/boot/dts/qcom/sm6125-xiaomi-ginkgo.dts +++ b/arch/arm64/boot/dts/qcom/sm6125-xiaomi-ginkgo.dts @@ -2,294 +2,11 @@ /* * Copyright (c) 2025, Gabriel Gonzales */ - /dts-v1/; -#include -#include -#include -#include -#include -#include "sm6125.dtsi" -#include "pm6125.dtsi" +#include "sm6125-xiaomi-ginkgo-common.dtsi" / { model = "Xiaomi Redmi Note 8"; compatible = "xiaomi,ginkgo", "qcom,sm6125"; - chassis-type = "handset"; - - /* required for bootloader to select correct board */ - qcom,msm-id = ; - qcom,board-id = <22 0>; - - chosen { - #address-cells = <2>; - #size-cells = <2>; - ranges; - - framebuffer0: framebuffer@5c000000 { - compatible = "simple-framebuffer"; - reg = <0 0x5c000000 0 (2340 * 1080 * 4)>; - width = <1080>; - height = <2340>; - stride = <(1080 * 4)>; - format = "a8r8g8b8"; - }; - }; - - reserved-memory { - debug_mem: debug@ffb00000 { - reg = <0x0 0xffb00000 0x0 0xc0000>; - no-map; - }; - - last_log_mem: lastlog@ffbc0000 { - reg = <0x0 0xffbc0000 0x0 0x80000>; - no-map; - }; - - pstore_mem: ramoops@ffc00000 { - compatible = "ramoops"; - reg = <0x0 0xffc40000 0x0 0xc0000>; - record-size = <0x1000>; - console-size = <0x40000>; - pmsg-size = <0x20000>; - }; - - cmdline_mem: memory@ffd00000 { - reg = <0x0 0xffd40000 0x0 0x1000>; - no-map; - }; - }; - - extcon_usb: extcon-usb { - compatible = "linux,extcon-usb-gpio"; - id-gpios = <&tlmm 102 GPIO_ACTIVE_HIGH>; - }; - - gpio-keys { - compatible = "gpio-keys"; - - pinctrl-0 = <&vol_up_n>; - pinctrl-names = "default"; - - key-volume-up { - label = "Volume Up"; - gpios = <&pm6125_gpios 6 GPIO_ACTIVE_LOW>; - linux,code = ; - debounce-interval = <15>; - linux,can-disable; - wakeup-source; - }; - }; -}; - -&pm6125_gpios { - vol_up_n: vol-up-n-state { - pins = "gpio6"; - function = "normal"; - power-source = <1>; - bias-pull-up; - input-enable; - }; -}; - -&hsusb_phy1 { - vdd-supply = <&vreg_l7a>; - vdda-pll-supply = <&vreg_l10a>; - vdda-phy-dpdm-supply = <&vreg_l15a>; - status = "okay"; -}; - -&pon_pwrkey { - status = "okay"; -}; - -&pon_resin { - linux,code = ; - status = "okay"; -}; - -&rpm_requests { - regulators-0 { - compatible = "qcom,rpm-pm6125-regulators"; - - vreg_s6a: s6 { - regulator-min-microvolt = <936000>; - regulator-max-microvolt = <1422000>; - }; - - vreg_l1a: l1 { - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1256000>; - }; - - vreg_l2a: l2 { - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1056000>; - }; - - vreg_l3a: l3 { - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1064000>; - }; - - vreg_l4a: l4 { - regulator-min-microvolt = <872000>; - regulator-max-microvolt = <976000>; - regulator-allow-set-load; - }; - - vreg_l5a: l5 { - regulator-min-microvolt = <1648000>; - regulator-max-microvolt = <2950000>; - regulator-allow-set-load; - }; - - vreg_l6a: l6 { - regulator-min-microvolt = <576000>; - regulator-max-microvolt = <656000>; - }; - - vreg_l7a: l7 { - regulator-min-microvolt = <872000>; - regulator-max-microvolt = <976000>; - }; - - vreg_l8a: l8 { - regulator-min-microvolt = <400000>; - regulator-max-microvolt = <728000>; - }; - - vreg_l9a: l9 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1896000>; - }; - - vreg_l10a: l10 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1896000>; - regulator-allow-set-load; - }; - - vreg_l11a: l11 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1952000>; - regulator-allow-set-load; - }; - - vreg_l12a: l12 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1996000>; - }; - - vreg_l13a: l13 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1832000>; - }; - - vreg_l14a: l14 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1904000>; - }; - - vreg_l15a: l15 { - regulator-min-microvolt = <3104000>; - regulator-max-microvolt = <3232000>; - }; - - vreg_l16a: l16 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1904000>; - }; - - vreg_l17a: l17 { - regulator-min-microvolt = <1248000>; - regulator-max-microvolt = <1304000>; - }; - - vreg_l18a: l18 { - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1264000>; - regulator-allow-set-load; - }; - - vreg_l19a: l19 { - regulator-min-microvolt = <1648000>; - regulator-max-microvolt = <2952000>; - }; - - vreg_l20a: l20 { - regulator-min-microvolt = <1648000>; - regulator-max-microvolt = <2952000>; - }; - - vreg_l21a: l21 { - regulator-min-microvolt = <2600000>; - regulator-max-microvolt = <2856000>; - }; - - vreg_l22a: l22 { - regulator-min-microvolt = <2944000>; - regulator-max-microvolt = <2950000>; - regulator-allow-set-load; - }; - - vreg_l23a: l23 { - regulator-min-microvolt = <3000000>; - regulator-max-microvolt = <3400000>; - }; - - vreg_l24a: l24 { - regulator-min-microvolt = <2944000>; - regulator-max-microvolt = <2950000>; - regulator-allow-set-load; - }; - - }; -}; - -&sdc2_off_state { - sd-cd-pins { - pins = "gpio98"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; -}; - -&sdc2_on_state { - sd-cd-pins { - pins = "gpio98"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; -}; - -&sdhc_1 { - vmmc-supply = <&vreg_l24a>; - vqmmc-supply = <&vreg_l11a>; - status = "okay"; -}; - -&sdhc_2 { - cd-gpios = <&tlmm 98 GPIO_ACTIVE_HIGH>; - vmmc-supply = <&vreg_l22a>; - vqmmc-supply = <&vreg_l5a>; - no-sdio; - no-mmc; - status = "okay"; -}; - -&tlmm { - gpio-reserved-ranges = <22 2>, <28 6>; -}; - -&usb3 { - status = "okay"; -}; - -&usb3_dwc3 { - extcon = <&extcon_usb>; }; diff --git a/arch/arm64/boot/dts/qcom/sm6125-xiaomi-laurel-sprout.dts b/arch/arm64/boot/dts/qcom/sm6125-xiaomi-laurel-sprout.dts index 994fb0412fcb..97f64cb5d570 100644 --- a/arch/arm64/boot/dts/qcom/sm6125-xiaomi-laurel-sprout.dts +++ b/arch/arm64/boot/dts/qcom/sm6125-xiaomi-laurel-sprout.dts @@ -82,6 +82,19 @@ }; }; + ts_vdd_supply: regulator-ts-vdd { + compatible = "regulator-fixed"; + regulator-name = "ts_vdd_supply"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us = <70000>; + + enable-active-high; + gpio = <&tlmm 83 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&ts_vdd_en>; + pinctrl-names = "default"; + }; + thermal-zones { rf-pa0-thermal { thermal-sensors = <&pm6125_adc_tm 0>; @@ -128,6 +141,27 @@ status = "okay"; }; +&i2c2 { + status = "okay"; + + touchscreen@38 { + compatible = "focaltech,ft3518"; + reg = <0x38>; + interrupts-extended = <&tlmm 88 IRQ_TYPE_EDGE_FALLING>; + + vcc-supply = <&ts_vdd_supply>; + + pinctrl-0 = <&ts_int_active &ts_reset_active>; + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; + pinctrl-names = "default","sleep"; + + reset-gpios = <&tlmm 87 GPIO_ACTIVE_LOW>; + + touchscreen-size-x = <720>; + touchscreen-size-y = <1560>; + }; +}; + &pm6125_adc { pinctrl-names = "default"; pinctrl-0 = <&camera_flash_therm &emmc_ufs_therm>; @@ -220,6 +254,10 @@ status = "okay"; }; +&qupv3_id_0 { + status = "okay"; +}; + &rpm_requests { regulators-0 { compatible = "qcom,rpm-pm6125-regulators"; @@ -387,6 +425,41 @@ &tlmm { gpio-reserved-ranges = <22 2>, <28 6>; + + ts_vdd_en: ts-vdd-default-state { + pins = "gpio83"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + ts_reset_active: pmx-ts-reset-active-state { + pins = "gpio87"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + + ts_reset_suspend: pmx-ts-reset-suspend-state { + pins = "gpio87"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + ts_int_active: pmx-ts-int-active-state { + pins = "gpio88"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + + ts_int_suspend: pmx-ts-int-suspend-state { + pins = "gpio88"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; }; &ufs_mem_hc { diff --git a/arch/arm64/boot/dts/qcom/sm6125-xiaomi-willow.dts b/arch/arm64/boot/dts/qcom/sm6125-xiaomi-willow.dts new file mode 100644 index 000000000000..1231e440ba2c --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm6125-xiaomi-willow.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2026, Barnabas Czeman + */ +/dts-v1/; + +#include "sm6125-xiaomi-ginkgo-common.dtsi" + +/ { + model = "Xiaomi Redmi Note 8T"; + compatible = "xiaomi,willow", "qcom,sm6125"; + +}; + +/* Difference from Redmi Note 8 it have NFC */ diff --git a/arch/arm64/boot/dts/qcom/sm6125.dtsi b/arch/arm64/boot/dts/qcom/sm6125.dtsi index 80c42dff5399..6e84c226948c 100644 --- a/arch/arm64/boot/dts/qcom/sm6125.dtsi +++ b/arch/arm64/boot/dts/qcom/sm6125.dtsi @@ -661,6 +661,13 @@ drive-strength = <6>; bias-disable; }; + + qup_uart4_default: qup-uart4-default-state { + pins = "gpio16", "gpio17"; + function = "qup04"; + drive-strength = <2>; + bias-disable; + }; }; gcc: clock-controller@1400000 { @@ -686,6 +693,13 @@ status = "disabled"; }; + rng: rng@1b53000 { + compatible = "qcom,prng-ee"; + reg = <0x01b53000 0x1000>; + clocks = <&gcc GCC_PRNG_AHB_CLK>; + clock-names = "core"; + }; + spmi_bus: spmi@1c40000 { compatible = "qcom,spmi-pmic-arb"; reg = <0x01c40000 0x1100>, @@ -978,6 +992,17 @@ #size-cells = <0>; status = "disabled"; }; + + uart4: serial@4a90000 { + compatible = "qcom,geni-debug-uart"; + reg = <0x04a90000 0x4000>; + clocks = <&gcc GCC_QUPV3_WRAP0_S4_CLK>; + clock-names = "se"; + interrupts = ; + pinctrl-0 = <&qup_uart4_default>; + pinctrl-names = "default"; + status = "disabled"; + }; }; gpi_dma1: dma-controller@4c00000 { @@ -1238,6 +1263,8 @@ "ahb", "core"; + resets = <&dispcc DISP_CC_MDSS_CORE_BCR>; + power-domains = <&dispcc MDSS_GDSC>; iommus = <&apps_smmu 0x400 0x0>; @@ -1437,6 +1464,7 @@ power-domains = <&rpmpd RPMPD_VDDCX>; #clock-cells = <1>; + #reset-cells = <1>; #power-domain-cells = <1>; }; @@ -1592,10 +1620,10 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; clock-frequency = <19200000>; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm6350.dtsi b/arch/arm64/boot/dts/qcom/sm6350.dtsi index 9f9b9f9af0da..034545d2af2d 100644 --- a/arch/arm64/boot/dts/qcom/sm6350.dtsi +++ b/arch/arm64/boot/dts/qcom/sm6350.dtsi @@ -1307,6 +1307,8 @@ qcom,smem-state-names = "ipa-clock-enabled-valid", "ipa-clock-enabled"; + sram = <&ipa_modem_tables>; + status = "disabled"; }; @@ -2346,8 +2348,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -2731,6 +2733,20 @@ }; }; + sram@14680000 { + compatible = "qcom,sm6350-imem", "syscon", "simple-mfd"; + reg = <0 0x14680000 0 0x2e000>; + + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0 0 0x14680000 0x2e000>; + + ipa_modem_tables: modem-tables@28000 { + reg = <0x28000 0x2000>; + }; + }; + apps_smmu: iommu@15000000 { compatible = "qcom,sm6350-smmu-500", "arm,mmu-500"; reg = <0x0 0x15000000 0x0 0x100000>; @@ -3509,9 +3525,9 @@ timer { compatible = "arm,armv8-timer"; clock-frequency = <19200000>; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm6375.dtsi b/arch/arm64/boot/dts/qcom/sm6375.dtsi index 87d6600ccbd9..ccf572bb1549 100644 --- a/arch/arm64/boot/dts/qcom/sm6375.dtsi +++ b/arch/arm64/boot/dts/qcom/sm6375.dtsi @@ -2469,9 +2469,9 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts index a3c2b26736f4..3964aae47fd4 100644 --- a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts +++ b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts @@ -1019,12 +1019,14 @@ * the Bluetooth module drives the pin in either * direction or leaves the pin fully unpowered. */ + /delete-property/ bias-disable; bias-bus-hold; }; &qup_uart1_rts { /* We'll drive RTS, so no pull */ drive-strength = <2>; + /delete-property/ bias-pull-down; bias-disable; }; @@ -1035,12 +1037,14 @@ * in tri-state (module powered off or not driving the * signal yet). */ + /delete-property/ bias-disable; bias-pull-up; }; &qup_uart1_tx { /* We'll drive TX, so no pull */ drive-strength = <2>; + /delete-property/ bias-pull-up; bias-disable; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150-hdk.dts b/arch/arm64/boot/dts/qcom/sm8150-hdk.dts index 1eea9c5c6684..6ae6e07c37df 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-hdk.dts +++ b/arch/arm64/boot/dts/qcom/sm8150-hdk.dts @@ -20,6 +20,7 @@ aliases { serial0 = &uart2; + serial1 = &uart13; }; chosen { @@ -66,6 +67,43 @@ }; }; }; + + wcn3998-pmu { + compatible = "qcom,wcn3998-pmu"; + + pinctrl-0 = <&sw_ctrl_default>; + pinctrl-names = "default"; + + vddio-supply = <&vreg_s4a_1p8>; + vddxo-supply = <&vreg_l7a_1p8>; + vddrf-supply = <&vreg_l2c_1p3>; + vddch0-supply = <&vreg_l11c_3p3>; + vddch1-supply = <&vreg_l10c_3p3>; + + swctrl-gpios = <&tlmm 50 GPIO_ACTIVE_HIGH>; + + regulators { + vreg_pmu_io: ldo0 { + regulator-name = "vreg_pmu_io"; + }; + + vreg_pmu_xo: ldo1 { + regulator-name = "vreg_pmu_xo"; + }; + + vreg_pmu_rf: ldo2 { + regulator-name = "vreg_pmu_rf"; + }; + + vreg_pmu_ch0: ldo3 { + regulator-name = "vreg_pmu_ch0"; + }; + + vreg_pmu_ch1: ldo4 { + regulator-name = "vreg_pmu_ch1"; + }; + }; + }; }; &apps_rsc { @@ -598,6 +636,10 @@ status = "okay"; }; +&qupv3_id_2 { + status = "okay"; +}; + &remoteproc_adsp { status = "okay"; @@ -630,12 +672,97 @@ bias-disable; }; + qup_uart13_default: qup-uart13-default-state { + cts-pins { + pins = "gpio43"; + function = "qup13"; + drive-strength = <2>; + bias-bus-hold; + }; + + rts-pins { + pins = "gpio44"; + function = "qup13"; + drive-strength = <2>; + bias-disable; + }; + + tx-pins { + pins = "gpio45"; + function = "qup13"; + drive-strength = <2>; + bias-disable; + }; + + rx-pins { + pins = "gpio46"; + function = "qup13"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + qup_uart13_sleep: qup-uart13-sleep-state { + cts-pins { + pins = "gpio43"; + function = "gpio"; + drive-strength = <2>; + bias-bus-hold; + }; + + rts-pins { + pins = "gpio44"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + tx-pins { + pins = "gpio45"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + rx-pins { + pins = "gpio46"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + sw_ctrl_default: sw-ctrl-default-state { + pins = "gpio50"; + function = "gpio"; + bias-pull-down; + }; }; &uart2 { status = "okay"; }; +&uart13 { + /delete-property/ interrupts; + interrupts-extended = <&intc GIC_SPI 585 IRQ_TYPE_LEVEL_HIGH>, + <&tlmm 46 IRQ_TYPE_EDGE_FALLING>; + pinctrl-0 = <&qup_uart13_default>; + pinctrl-1 = <&qup_uart13_sleep>; + pinctrl-names = "default", "sleep"; + + status = "okay"; + + bluetooth { + compatible = "qcom,wcn3998-bt"; + + vddio-supply = <&vreg_pmu_io>; + vddxo-supply = <&vreg_pmu_xo>; + vddrf-supply = <&vreg_pmu_rf>; + vddch0-supply = <&vreg_pmu_ch0>; + }; +}; + &ufs_mem_hc { status = "okay"; @@ -709,12 +836,16 @@ }; &wifi { - status = "okay"; - + /* SoC */ vdd-0.8-cx-mx-supply = <&vreg_l1a_0p75>; - vdd-1.8-xo-supply = <&vreg_l7a_1p8>; - vdd-1.3-rfa-supply = <&vreg_l2c_1p3>; - vdd-3.3-ch0-supply = <&vreg_l11c_3p3>; + + /* WiFi / BT PMU */ + vdd-1.8-xo-supply = <&vreg_pmu_xo>; + vdd-1.3-rfa-supply = <&vreg_pmu_rf>; + vdd-3.3-ch0-supply = <&vreg_pmu_ch0>; + vdd-3.3-ch1-supply = <&vreg_pmu_ch1>; qcom,calibration-variant = "Qualcomm_sm8150hdk"; + + status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150.dtsi b/arch/arm64/boot/dts/qcom/sm8150.dtsi index 97ca5275d740..0e101096209a 100644 --- a/arch/arm64/boot/dts/qcom/sm8150.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150.dtsi @@ -3954,8 +3954,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi index c7dffa440074..7076720413ab 100644 --- a/arch/arm64/boot/dts/qcom/sm8250.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi @@ -665,6 +665,11 @@ opp-hz = /bits/ 64 <2841600000>; opp-peak-kBps = <8368000 51609600>; }; + + cpu7_opp21: opp-3091200000 { + opp-hz = /bits/ 64 <3091200000>; + opp-peak-kBps = <8368000 51609600>; + }; }; firmware { @@ -4826,8 +4831,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -6285,14 +6290,10 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; thermal-zones { diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi index 5c8fe213f5e4..c830953156ec 100644 --- a/arch/arm64/boot/dts/qcom/sm8350.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi @@ -1866,6 +1866,8 @@ qcom,smem-state-names = "ipa-clock-enabled-valid", "ipa-clock-enabled"; + sram = <&ipa_modem_tables>; + status = "disabled"; }; @@ -2925,8 +2927,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -3440,6 +3442,20 @@ }; }; + sram@14680000 { + compatible = "qcom,sm8350-imem", "syscon", "simple-mfd"; + reg = <0 0x14680000 0 0x40000>; + + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0 0 0x14680000 0x40000>; + + ipa_modem_tables: modem-tables@3d000 { + reg = <0x3d000 0x2000>; + }; + }; + apps_smmu: iommu@15000000 { compatible = "qcom,sm8350-smmu-500", "arm,mmu-500"; reg = <0 0x15000000 0 0x100000>; @@ -4523,9 +4539,9 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm8450.dtsi b/arch/arm64/boot/dts/qcom/sm8450.dtsi index 920a2d1c04d0..03bf30b53f28 100644 --- a/arch/arm64/boot/dts/qcom/sm8450.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8450.dtsi @@ -3508,8 +3508,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -5104,7 +5104,7 @@ gic_its: msi-controller@17140000 { compatible = "arm,gic-v3-its"; - reg = <0x0 0x17140000 0x0 0x20000>; + reg = <0x0 0x17140000 0x0 0x40000>; msi-controller; #msi-cells = <1>; }; @@ -5429,9 +5429,6 @@ bus-width = <4>; dma-coherent; - /* Forbid SDR104/SDR50 - broken hw! */ - sdhci-caps-mask = <0x3 0x0>; - status = "disabled"; sdhc2_opp_table: opp-table { @@ -6327,10 +6324,10 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; clock-frequency = <19200000>; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm8550-hdk-display-card.dtso b/arch/arm64/boot/dts/qcom/sm8550-hdk-display-card.dtso new file mode 100644 index 000000000000..7b54b084b8bd --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8550-hdk-display-card.dtso @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2024-2026, Linaro Limited + */ + +/* + * Display Card kit overlay + * This requires S5702 Switch 7 to be turned to OFF to route DSI0 to the display panel + */ + +#include +#include + +/dts-v1/; +/plugin/; + +/* Disable HDMI bridge related nodes (mutually exclusive with the display card) */ + +&i2c0 { + status = "disabled"; +}; + +<9611_1v2 { + status = "disabled"; +}; + +<9611_3v3 { + status = "disabled"; +}; + +&vreg_bob_3v3 { + status = "disabled"; +}; + +<9611_codec { + status = "disabled"; +}; + +&mdss_dsi0 { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "visionox,vtdr6130"; + reg = <0>; + + reset-gpios = <&tlmm 133 GPIO_ACTIVE_LOW>; + + vddio-supply = <&vreg_l12b_1p8>; + vci-supply = <&vreg_l13b_3p0>; + vdd-supply = <&vreg_l11b_1p2>; + + pinctrl-0 = <&disp0_reset_n_active>, <&mdp_vsync>; + pinctrl-1 = <&disp0_reset_n_suspend>, <&mdp_vsync>; + pinctrl-names = "default", "sleep"; + + port { + panel0_in: endpoint { + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + }; +}; + +&mdss_dsi0_out { + remote-endpoint = <&panel0_in>; +}; + +&spi4 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + + touchscreen@0 { + compatible = "goodix,gt9916"; + reg = <0>; + + interrupt-parent = <&tlmm>; + interrupts = <25 IRQ_TYPE_LEVEL_LOW>; + + reset-gpios = <&tlmm 24 GPIO_ACTIVE_LOW>; + + avdd-supply = <&vreg_l14b_3p2>; + + spi-max-frequency = <1000000>; + + touchscreen-size-x = <1080>; + touchscreen-size-y = <2400>; + + pinctrl-0 = <&ts_irq>, <&ts_reset>; + pinctrl-names = "default"; + }; +}; + +&tlmm { + disp0_reset_n_active: disp0-reset-n-active-state { + pins = "gpio133"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + + disp0_reset_n_suspend: disp0-reset-n-suspend-state { + pins = "gpio133"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + mdp_vsync: mdp-vsync-state { + pins = "gpio86"; + function = "mdp_vsync"; + drive-strength = <2>; + bias-pull-down; + }; + + ts_irq: ts-irq-state { + pins = "gpio25"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + output-disable; + }; + + ts_reset: ts-reset-state { + pins = "gpio24"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8550-mtp.dts b/arch/arm64/boot/dts/qcom/sm8550-mtp.dts index 94ed1c221856..5769be83cfbd 100644 --- a/arch/arm64/boot/dts/qcom/sm8550-mtp.dts +++ b/arch/arm64/boot/dts/qcom/sm8550-mtp.dts @@ -707,8 +707,8 @@ reg = <0>; pinctrl-names = "default", "sleep"; - pinctrl-0 = <&sde_dsi_active>, <&sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend>, <&sde_te_suspend>; + pinctrl-0 = <&sde_dsi_active>, <&sde_te>; + pinctrl-1 = <&sde_dsi_suspend>, <&sde_te>; vddio-supply = <&vreg_l12b_1p8>; vci-supply = <&vreg_l13b_3p0>; @@ -915,14 +915,7 @@ bias-pull-down; }; - sde_te_active: sde-te-active-state { - pins = "gpio86"; - function = "mdp_vsync"; - drive-strength = <2>; - bias-pull-down; - }; - - sde_te_suspend: sde-te-suspend-state { + sde_te: sde-te-state { pins = "gpio86"; function = "mdp_vsync"; drive-strength = <2>; diff --git a/arch/arm64/boot/dts/qcom/sm8550-qrd.dts b/arch/arm64/boot/dts/qcom/sm8550-qrd.dts index c35d4737a412..2fb2e0be5e4c 100644 --- a/arch/arm64/boot/dts/qcom/sm8550-qrd.dts +++ b/arch/arm64/boot/dts/qcom/sm8550-qrd.dts @@ -870,8 +870,8 @@ compatible = "visionox,vtdr6130"; reg = <0>; - pinctrl-0 = <&sde_dsi_active>, <&sde_te_active>; - pinctrl-1 = <&sde_dsi_suspend>, <&sde_te_suspend>; + pinctrl-0 = <&sde_dsi_active>, <&sde_te>; + pinctrl-1 = <&sde_dsi_suspend>, <&sde_te>; pinctrl-names = "default", "sleep"; vci-supply = <&vreg_l13b_3p0>; @@ -1179,14 +1179,7 @@ bias-pull-down; }; - sde_te_active: sde-te-active-state { - pins = "gpio86"; - function = "mdp_vsync"; - drive-strength = <2>; - bias-pull-down; - }; - - sde_te_suspend: sde-te-suspend-state { + sde_te: sde-te-state { pins = "gpio86"; function = "mdp_vsync"; drive-strength = <2>; diff --git a/arch/arm64/boot/dts/qcom/sm8550.dtsi b/arch/arm64/boot/dts/qcom/sm8550.dtsi index e3f93f4f412d..912525e9bca6 100644 --- a/arch/arm64/boot/dts/qcom/sm8550.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8550.dtsi @@ -76,8 +76,8 @@ power-domains = <&cpu_pd0>; power-domain-names = "psci"; qcom,freq-domain = <&cpufreq_hw 0>; - capacity-dmips-mhz = <1024>; - dynamic-power-coefficient = <100>; + capacity-dmips-mhz = <326>; + dynamic-power-coefficient = <251>; #cooling-cells = <2>; l2_0: l2-cache { compatible = "cache"; @@ -102,8 +102,8 @@ power-domains = <&cpu_pd1>; power-domain-names = "psci"; qcom,freq-domain = <&cpufreq_hw 0>; - capacity-dmips-mhz = <1024>; - dynamic-power-coefficient = <100>; + capacity-dmips-mhz = <326>; + dynamic-power-coefficient = <251>; #cooling-cells = <2>; l2_100: l2-cache { compatible = "cache"; @@ -123,8 +123,8 @@ power-domains = <&cpu_pd2>; power-domain-names = "psci"; qcom,freq-domain = <&cpufreq_hw 0>; - capacity-dmips-mhz = <1024>; - dynamic-power-coefficient = <100>; + capacity-dmips-mhz = <326>; + dynamic-power-coefficient = <251>; #cooling-cells = <2>; l2_200: l2-cache { compatible = "cache"; @@ -144,8 +144,8 @@ power-domains = <&cpu_pd3>; power-domain-names = "psci"; qcom,freq-domain = <&cpufreq_hw 1>; - capacity-dmips-mhz = <1792>; - dynamic-power-coefficient = <270>; + capacity-dmips-mhz = <693>; + dynamic-power-coefficient = <447>; #cooling-cells = <2>; l2_300: l2-cache { compatible = "cache"; @@ -165,8 +165,8 @@ power-domains = <&cpu_pd4>; power-domain-names = "psci"; qcom,freq-domain = <&cpufreq_hw 1>; - capacity-dmips-mhz = <1792>; - dynamic-power-coefficient = <270>; + capacity-dmips-mhz = <693>; + dynamic-power-coefficient = <447>; #cooling-cells = <2>; l2_400: l2-cache { compatible = "cache"; @@ -186,8 +186,8 @@ power-domains = <&cpu_pd5>; power-domain-names = "psci"; qcom,freq-domain = <&cpufreq_hw 1>; - capacity-dmips-mhz = <1792>; - dynamic-power-coefficient = <270>; + capacity-dmips-mhz = <693>; + dynamic-power-coefficient = <447>; #cooling-cells = <2>; l2_500: l2-cache { compatible = "cache"; @@ -207,8 +207,8 @@ power-domains = <&cpu_pd6>; power-domain-names = "psci"; qcom,freq-domain = <&cpufreq_hw 1>; - capacity-dmips-mhz = <1792>; - dynamic-power-coefficient = <270>; + capacity-dmips-mhz = <693>; + dynamic-power-coefficient = <447>; #cooling-cells = <2>; l2_600: l2-cache { compatible = "cache"; @@ -228,8 +228,8 @@ power-domains = <&cpu_pd7>; power-domain-names = "psci"; qcom,freq-domain = <&cpufreq_hw 2>; - capacity-dmips-mhz = <1894>; - dynamic-power-coefficient = <588>; + capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <1057>; #cooling-cells = <2>; l2_700: l2-cache { compatible = "cache"; @@ -1251,6 +1251,22 @@ #size-cells = <0>; status = "disabled"; }; + + uart15: serial@89c000 { + compatible = "qcom,geni-uart"; + reg = <0 0x0089c000 0 0x4000>; + clock-names = "se"; + clocks = <&gcc GCC_QUPV3_WRAP2_S7_CLK>; + pinctrl-names = "default"; + pinctrl-0 = <&qup_uart15_default>; + interrupts = ; + interconnects = <&clk_virt MASTER_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_QUP_2 QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "qup-core", "qup-config"; + status = "disabled"; + }; }; i2c_master_hub_0: geniqup@9c0000 { @@ -2503,48 +2519,56 @@ opp-hz = /bits/ 64 <680000000>; opp-level = ; opp-peak-kBps = <16500000>; + qcom,opp-acd-level = <0x882e5ffd>; }; opp-615000000 { opp-hz = /bits/ 64 <615000000>; opp-level = ; opp-peak-kBps = <12449218>; + qcom,opp-acd-level = <0xa82f5ffd>; }; opp-550000000 { opp-hz = /bits/ 64 <550000000>; opp-level = ; opp-peak-kBps = <10687500>; + qcom,opp-acd-level = <0xe0285ffd>; }; opp-475000000 { opp-hz = /bits/ 64 <475000000>; opp-level = ; opp-peak-kBps = <6074218>; + qcom,opp-acd-level = <0xe0285ffd>; }; opp-401000000 { opp-hz = /bits/ 64 <401000000>; opp-level = ; opp-peak-kBps = <6074218>; + qcom,opp-acd-level = <0xc02a5ffd>; }; opp-348000000 { opp-hz = /bits/ 64 <348000000>; opp-level = ; opp-peak-kBps = <6074218>; + qcom,opp-acd-level = <0xe02b5ffd>; }; opp-295000000 { opp-hz = /bits/ 64 <295000000>; opp-level = ; opp-peak-kBps = <6074218>; + qcom,opp-acd-level = <0xe02d5ffd>; }; opp-220000000 { opp-hz = /bits/ 64 <220000000>; opp-level = ; opp-peak-kBps = <2136718>; + qcom,opp-acd-level = <0xc02f5ffd>; }; }; }; @@ -2694,6 +2718,8 @@ qcom,smem-state-names = "ipa-clock-enabled-valid", "ipa-clock-enabled"; + sram = <&ipa_modem_tables>; + status = "disabled"; }; @@ -3210,7 +3236,7 @@ clocks = <&gcc GCC_SDCC2_AHB_CLK>, <&gcc GCC_SDCC2_APPS_CLK>, - <&rpmhcc RPMH_CXO_CLK>; + <&bi_tcxo_div2>; clock-names = "iface", "core", "xo"; iommus = <&apps_smmu 0x540 0>; qcom,dll-config = <0x0007642c>; @@ -3227,9 +3253,6 @@ max-sd-hs-hz = <37500000>; dma-coherent; - /* Forbid SDR104/SDR50 - broken hw! */ - sdhci-caps-mask = <0x3 0>; - status = "disabled"; sdhc2_opp_table: opp-table { @@ -3320,19 +3343,19 @@ opp-366000000 { opp-hz = /bits/ 64 <366000000>; - required-opps = <&rpmhpd_opp_svs_l1>, + required-opps = <&rpmhpd_opp_svs>, <&rpmhpd_opp_svs_l1>; }; opp-444000000 { opp-hz = /bits/ 64 <444000000>; - required-opps = <&rpmhpd_opp_nom>, + required-opps = <&rpmhpd_opp_svs_l1>, <&rpmhpd_opp_nom>; }; opp-533333334 { opp-hz = /bits/ 64 <533333334>; - required-opps = <&rpmhpd_opp_turbo>, + required-opps = <&rpmhpd_opp_nom>, <&rpmhpd_opp_turbo>; }; }; @@ -5095,6 +5118,14 @@ bias-pull-down; }; + qup_uart15_default: qup-uart15-default-state { + /* TX, RX */ + pins = "gpio74", "gpio75"; + function = "qup2_se7"; + drive-strength = <2>; + bias-pull-up; + }; + sdc2_sleep: sdc2-sleep-state { clk-pins { pins = "sdc2_clk"; @@ -5136,6 +5167,20 @@ }; }; + sram@14680000 { + compatible = "qcom,sm8550-imem", "syscon", "simple-mfd"; + reg = <0 0x14680000 0 0x2c000>; + + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0 0 0x14680000 0x2c000>; + + ipa_modem_tables: modem-tables@8000 { + reg = <0x8000 0x2000>; + }; + }; + apps_smmu: iommu@15000000 { compatible = "qcom,sm8550-smmu-500", "qcom,smmu-500", "arm,mmu-500"; reg = <0 0x15000000 0 0x100000>; @@ -5274,7 +5319,7 @@ gic_its: msi-controller@17140000 { compatible = "arm,gic-v3-its"; - reg = <0 0x17140000 0 0x20000>; + reg = <0 0x17140000 0 0x40000>; msi-controller; #msi-cells = <1>; }; @@ -6758,9 +6803,9 @@ timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; }; diff --git a/arch/arm64/boot/dts/qcom/sm8650-ayaneo-pocket-s2.dts b/arch/arm64/boot/dts/qcom/sm8650-ayaneo-pocket-s2.dts new file mode 100644 index 000000000000..0dc994f4e48d --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sm8650-ayaneo-pocket-s2.dts @@ -0,0 +1,1551 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2023, Linaro Limited + * Copyright (c) 2025, Kancy Joe + */ + +/dts-v1/; + +#include +#include +#include +#include "sm8650.dtsi" +#include "pm8550.dtsi" +#include "pm8550b.dtsi" +#define PMK8550VE_SID 8 +#include "pm8550ve.dtsi" +#include "pm8550vs.dtsi" +#include "pmk8550.dtsi" + +/delete-node/ &rmtfs_mem; +/delete-node/ &hwfence_shbuf; + +/ { + model = "AYANEO Pocket S2 (Pro)"; + compatible = "ayaneo,pocket-s2", "qcom,sm8650"; + chassis-type = "handset"; + + aliases { + serial0 = &uart15; + serial1 = &uart14; + }; + + wcd939x: audio-codec { + compatible = "qcom,wcd9395-codec", "qcom,wcd9390-codec"; + + pinctrl-0 = <&wcd_default>; + pinctrl-names = "default"; + + qcom,micbias1-microvolt = <1800000>; + qcom,micbias2-microvolt = <1800000>; + qcom,micbias3-microvolt = <1800000>; + qcom,micbias4-microvolt = <1800000>; + qcom,mbhc-buttons-vthreshold-microvolt = <75000 150000 237000 500000 500000 500000 500000 500000>; + qcom,mbhc-headset-vthreshold-microvolt = <1700000>; + qcom,mbhc-headphone-vthreshold-microvolt = <50000>; + qcom,rx-device = <&wcd_rx>; + qcom,tx-device = <&wcd_tx>; + + reset-gpios = <&tlmm 107 GPIO_ACTIVE_LOW>; + + vdd-buck-supply = <&vreg_l15b_1p8>; + vdd-rxtx-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l15b_1p8>; + vdd-mic-bias-supply = <&vreg_bob1>; + + #sound-dai-cells = <1>; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + fan: fan { + compatible = "pwm-fan"; + + interrupts-extended = <&tlmm 14 IRQ_TYPE_EDGE_FALLING>; + + pwms = <&pm8550_pwm 3 50000>; + + fan-supply = <&fan_pwr>; + + #cooling-cells = <2>; + cooling-levels = <0 16 32 45 60 80 105 130 155 180 205 230 255>; + + pinctrl-0 = <&fan_int>, <&pwm_fan_ctrl_active>; + pinctrl-1 = <&pwm_fan_ctrl_sleep>; + pinctrl-names = "default", "sleep"; + }; + + fan_pwr: fan-pwr-regulator { + compatible = "regulator-fixed"; + + regulator-name = "fan_pwr"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + + gpios = <&tlmm 125 GPIO_ACTIVE_HIGH>; + enable-active-high; + + vin-supply = <&fan_vdd>; + + pinctrl-0 = <&fan_pwr_pins>; + pinctrl-names = "default"; + }; + + fan_vdd: fan-vdd-regulator { + compatible = "regulator-fixed"; + + regulator-name = "fan_vdd"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + + gpios = <&tlmm 124 GPIO_ACTIVE_HIGH>; + enable-active-high; + + vin-supply = <&vph_pwr>; + + pinctrl-0 = <&fan_vdd_pins>; + pinctrl-names = "default"; + }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&volume_up_n>; + pinctrl-names = "default"; + + key-volume-up { + label = "Volume Up"; + linux,code = ; + gpios = <&pm8550_gpios 6 GPIO_ACTIVE_LOW>; + debounce-interval = <15>; + linux,can-disable; + wakeup-source; + }; + }; + + pmic-glink { + compatible = "qcom,sm8650-pmic-glink", + "qcom,sm8550-pmic-glink", + "qcom,pmic-glink"; + #address-cells = <1>; + #size-cells = <0>; + + orientation-gpios = <&tlmm 29 GPIO_ACTIVE_HIGH>; + + connector@0 { + compatible = "usb-c-connector"; + reg = <0>; + + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pmic_glink_hs_in: endpoint { + remote-endpoint = <&usb_1_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + pmic_glink_ss_in: endpoint { + remote-endpoint = <&redriver_ss_out>; + }; + }; + + port@2 { + reg = <2>; + + pmic_glink_sbu: endpoint { + remote-endpoint = <&wcd_usbss_sbu_mux>; + }; + }; + }; + }; + }; + + upd720201_avdd33_reg: upd720201-avdd33-regulator { + compatible = "regulator-fixed"; + + regulator-name = "upd720201_avdd33"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpios = <&tlmm 123 GPIO_ACTIVE_HIGH>; + enable-active-high; + + vin-supply = <&vph_pwr>; + + pinctrl-0 = <&upd720201_avdd33>; + pinctrl-names = "default"; + }; + + upd720201_vdd10_reg: upd720201-vdd10-regulator { + compatible = "regulator-fixed"; + + regulator-name = "upd720201_vdd10"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + + gpios = <&tlmm 122 GPIO_ACTIVE_HIGH>; + enable-active-high; + + vin-supply = <&vph_pwr>; + + pinctrl-0 = <&upd720201_vdd10>; + pinctrl-names = "default"; + }; + + upd720201_vdd33_reg: upd720201-vdd33-regulator { + compatible = "regulator-fixed"; + + regulator-name = "upd720201_vdd33"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpios = <&tlmm 121 GPIO_ACTIVE_HIGH>; + enable-active-high; + + vin-supply = <&vph_pwr>; + + pinctrl-0 = <&upd720201_vdd33>; + pinctrl-names = "default"; + }; + + sound { + compatible = "qcom,sm8650-sndcard", "qcom,sm8450-sndcard"; + model = "SM8650-APS2"; + audio-routing = "SpkrLeft IN", "WSA_SPK1 OUT", + "SpkrRight IN", "WSA_SPK2 OUT", + "IN1_HPHL", "HPHL_OUT", + "IN2_HPHR", "HPHR_OUT", + "AMIC1", "MIC BIAS1", + "AMIC2", "MIC BIAS2", + "AMIC3", "MIC BIAS3", + "AMIC4", "MIC BIAS3", + "AMIC5", "MIC BIAS4", + "TX SWR_INPUT0", "ADC1_OUTPUT", + "TX SWR_INPUT1", "ADC2_OUTPUT", + "TX SWR_INPUT7", "DMIC1_OUTPUT", + "TX SWR_INPUT8", "DMIC2_OUTPUT"; + + wcd-playback-dai-link { + link-name = "WCD Playback"; + + codec { + sound-dai = <&wcd939x 0>, + <&swr1 0>, + <&lpass_rxmacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai RX_CODEC_DMA_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + wcd-capture-dai-link { + link-name = "WCD Capture"; + codec { + sound-dai = <&wcd939x 1>, + <&swr2 0>, + <&lpass_txmacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai TX_CODEC_DMA_TX_3>; + }; + + + platform { + sound-dai = <&q6apm>; + }; + }; + + wsa-dai-link { + link-name = "WSA Playback"; + + codec { + sound-dai = <&right_spkr>, + <&left_spkr>, + <&swr3 0>, + <&lpass_wsa2macro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai WSA_CODEC_DMA_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + va-dai-link { + link-name = "VA Capture"; + + codec { + sound-dai = <&lpass_vamacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai VA_CODEC_DMA_TX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + dp-dai-link { + link-name = "DisplayPort Playback"; + + codec { + sound-dai = <&mdss_dp0>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + }; + + vph_pwr: vph-pwr-regulator { + compatible = "regulator-fixed"; + + regulator-name = "vph_pwr"; + regulator-min-microvolt = <3700000>; + regulator-max-microvolt = <3700000>; + + regulator-always-on; + regulator-boot-on; + }; + + wcn7850-pmu { + compatible = "qcom,wcn7850-pmu"; + + pinctrl-names = "default"; + pinctrl-0 = <&wlan_en>, <&bt_default>; + + wlan-enable-gpios = <&tlmm 16 GPIO_ACTIVE_HIGH>; + bt-enable-gpios = <&tlmm 17 GPIO_ACTIVE_HIGH>; + + vdd-supply = <&vreg_s4i_0p85>; + vddio-supply = <&vreg_l15b_1p8>; + vddio1p2-supply = <&vreg_l3c_1p2>; + vddaon-supply = <&vreg_s2c_0p8>; + vdddig-supply = <&vreg_s3c_0p9>; + vddrfa1p2-supply = <&vreg_s1c_1p2>; + vddrfa1p8-supply = <&vreg_s6c_1p8>; + + clocks = <&rpmhcc RPMH_RF_CLK1>; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p8: ldo7 { + regulator-name = "vreg_pmu_rfa_1p8"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; +}; + +&apps_rsc { + regulators-0 { + compatible = "qcom,pm8550-rpmh-regulators"; + + vdd-bob1-supply = <&vph_pwr>; + vdd-bob2-supply = <&vph_pwr>; + vdd-l2-l13-l14-supply = <&vreg_bob1>; + vdd-l3-supply = <&vreg_s1c_1p2>; + vdd-l5-l16-supply = <&vreg_bob1>; + vdd-l6-l7-supply = <&vreg_bob1>; + vdd-l8-l9-supply = <&vreg_bob1>; + vdd-l11-supply = <&vreg_s1c_1p2>; + vdd-l12-supply = <&vreg_s6c_1p8>; + vdd-l15-supply = <&vreg_s6c_1p8>; + vdd-l17-supply = <&vreg_bob2>; + + qcom,pmic-id = "b"; + + vreg_bob1: bob1 { + regulator-name = "vreg_bob1"; + regulator-min-microvolt = <3296000>; + regulator-max-microvolt = <3960000>; + regulator-initial-mode = ; + }; + + vreg_bob2: bob2 { + regulator-name = "vreg_bob2"; + regulator-min-microvolt = <2720000>; + regulator-max-microvolt = <3008000>; + regulator-initial-mode = ; + }; + + vreg_l2b_3p0: ldo2 { + regulator-name = "vreg_l2b_3p0"; + regulator-min-microvolt = <3008000>; + regulator-max-microvolt = <3008000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l5b_3p1: ldo5 { + regulator-name = "vreg_l5b_3p1"; + regulator-min-microvolt = <3104000>; + regulator-max-microvolt = <3104000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l6b_1p8: ldo6 { + regulator-name = "vreg_l6b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3008000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l7b_1p8: ldo7 { + regulator-name = "vreg_l7b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3008000>; + regulator-initial-mode = ; + }; + + vreg_l8b_1p8: ldo8 { + regulator-name = "vreg_l8b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3008000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l9b_2p9: ldo9 { + regulator-name = "vreg_l9b_2p9"; + regulator-min-microvolt = <2960000>; + regulator-max-microvolt = <3008000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l11b_1p2: ldo11 { + regulator-name = "vreg_l11b_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1504000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l12b_1p8: ldo12 { + regulator-name = "vreg_l12b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l13b_3p0: ldo13 { + regulator-name = "vreg_l13b_3p0"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l14b_3p2: ldo14 { + regulator-name = "vreg_l14b_3p2"; + regulator-min-microvolt = <3200000>; + regulator-max-microvolt = <3200000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l15b_1p8: ldo15 { + regulator-name = "vreg_l15b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l16b_2p8: ldo16 { + regulator-name = "vreg_l16b_2p8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + regulator-always-on; + regulator-boot-on; + }; + + vreg_l17b_2p5: ldo17 { + regulator-name = "vreg_l17b_2p5"; + regulator-min-microvolt = <2504000>; + regulator-max-microvolt = <2504000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + }; + + regulators-1 { + compatible = "qcom,pm8550vs-rpmh-regulators"; + + vdd-l1-supply = <&vreg_s1c_1p2>; + vdd-l2-supply = <&vreg_s1c_1p2>; + vdd-l3-supply = <&vreg_s1c_1p2>; + vdd-s1-supply = <&vph_pwr>; + vdd-s2-supply = <&vph_pwr>; + vdd-s3-supply = <&vph_pwr>; + vdd-s4-supply = <&vph_pwr>; + vdd-s5-supply = <&vph_pwr>; + vdd-s6-supply = <&vph_pwr>; + + qcom,pmic-id = "c"; + + vreg_s1c_1p2: smps1 { + regulator-name = "vreg_s1c_1p2"; + regulator-min-microvolt = <1256000>; + regulator-max-microvolt = <1348000>; + regulator-initial-mode = ; + }; + + vreg_s2c_0p8: smps2 { + regulator-name = "vreg_s2c_0p8"; + regulator-min-microvolt = <852000>; + regulator-max-microvolt = <1036000>; + regulator-initial-mode = ; + }; + + vreg_s3c_0p9: smps3 { + regulator-name = "vreg_s3c_0p9"; + regulator-min-microvolt = <976000>; + regulator-max-microvolt = <1064000>; + regulator-initial-mode = ; + }; + + vreg_s4c_1p2: smps4 { + regulator-name = "vreg_s4c_1p2"; + regulator-min-microvolt = <1224000>; + regulator-max-microvolt = <1280000>; + regulator-initial-mode = ; + }; + + vreg_s5c_0p7: smps5 { + regulator-name = "vreg_s5c_0p7"; + regulator-min-microvolt = <752000>; + regulator-max-microvolt = <900000>; + regulator-initial-mode = ; + }; + + vreg_s6c_1p8: smps6 { + regulator-name = "vreg_s6c_1p8"; + regulator-min-microvolt = <1856000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = ; + }; + + vreg_l1c_1p2: ldo1 { + regulator-name = "vreg_l1c_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l3c_1p2: ldo3 { + regulator-name = "vreg_l3c_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + regulator-always-on; + regulator-boot-on; + }; + }; + + regulators-2 { + compatible = "qcom,pm8550vs-rpmh-regulators"; + + vdd-l1-supply = <&vreg_s3c_0p9>; + + qcom,pmic-id = "d"; + + vreg_l1d_0p88: ldo1 { + regulator-name = "vreg_l1d_0p88"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + }; + + regulators-3 { + compatible = "qcom,pm8550vs-rpmh-regulators"; + + vdd-l3-supply = <&vreg_s3c_0p9>; + + qcom,pmic-id = "e"; + + vreg_l3e_0p9: ldo3 { + regulator-name = "vreg_l3e_0p9"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + }; + + regulators-4 { + compatible = "qcom,pm8550vs-rpmh-regulators"; + + vdd-l1-supply = <&vreg_s3c_0p9>; + vdd-l3-supply = <&vreg_s3c_0p9>; + + qcom,pmic-id = "g"; + + vreg_l1g_0p91: ldo1 { + regulator-name = "vreg_l1g_0p91"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l3g_0p91: ldo3 { + regulator-name = "vreg_l3g_0p91"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + }; + + regulators-5 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + + vdd-l1-supply = <&vreg_s3c_0p9>; + vdd-l2-supply = <&vreg_s3c_0p9>; + vdd-l3-supply = <&vreg_s1c_1p2>; + vdd-s4-supply = <&vph_pwr>; + + qcom,pmic-id = "i"; + + vreg_s4i_0p85: smps4 { + regulator-name = "vreg_s4i_0p85"; + regulator-min-microvolt = <852000>; + regulator-max-microvolt = <1004000>; + regulator-initial-mode = ; + }; + + vreg_l1i_0p88: ldo1 { + regulator-name = "vreg_l1i_0p88"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l2i_0p88: ldo2 { + regulator-name = "vreg_l2i_0p88"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l3i_1p2: ldo3 { + regulator-name = "vreg_l3i_0p91"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + }; +}; + +&cpu2_top_thermal { + trips { + cpu2_active: cpu2-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; + }; + + cooling-maps { + map { + trip = <&cpu2_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cpu3_top_thermal { + trips { + cpu3_active: cpu3-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; + }; + + cooling-maps { + map { + trip = <&cpu3_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cpu4_top_thermal { + trips { + cpu4_active: cpu4-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; + }; + + cooling-maps { + map { + trip = <&cpu4_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cpu5_top_thermal { + trips { + cpu5_active: cpu5-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; + }; + + cooling-maps { + map { + trip = <&cpu5_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cpu6_top_thermal { + trips { + cpu6_active: cpu6-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; + }; + + cooling-maps { + map { + trip = <&cpu6_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&cpu7_top_thermal { + trips { + cpu7_active: cpu7-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; + }; + + cooling-maps { + map { + trip = <&cpu7_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; +}; + +&gpi_dma1 { + status = "okay"; +}; + +&gpi_dma2 { + status = "okay"; +}; + +&gpu0_cooling_maps { + map1 { + trip = <&gpu0_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; +}; + +&gpu1_cooling_maps { + map1 { + trip = <&gpu1_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; +}; + +&gpu2_cooling_maps { + map1 { + trip = <&gpu2_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; +}; + +&gpu3_cooling_maps { + map1 { + trip = <&gpu3_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; +}; + +&gpu4_cooling_maps { + map1 { + trip = <&gpu4_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; +}; + +&gpu5_cooling_maps { + map1 { + trip = <&gpu5_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; +}; + +&gpu6_cooling_maps { + map1 { + trip = <&gpu6_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; +}; + +&gpu7_cooling_maps { + map1 { + trip = <&gpu7_active>; + cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; +}; + +&gpu0_trips { + gpu0_active: trip-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; +}; + +&gpu1_trips { + gpu1_active: trip-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; +}; + +&gpu2_trips { + gpu2_active: trip-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; +}; + +&gpu3_trips { + gpu3_active: trip-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; +}; + +&gpu4_trips { + gpu4_active: trip-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; +}; + +&gpu5_trips { + gpu5_active: trip-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; +}; + +&gpu6_trips { + gpu6_active: trip-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; + +}; + +&gpu7_trips { + gpu7_active: trip-active { + temperature = <38000>; + hysteresis = <2000>; + type = "active"; + }; +}; + +&i2c3 { + clock-frequency = <100000>; + + status = "okay"; + + wcd_usbss: typec-mux@e { + compatible = "qcom,wcd9395-usbss", "qcom,wcd9390-usbss"; + reg = <0xe>; + + vdd-supply = <&vreg_l15b_1p8>; + reset-gpios = <&tlmm 152 GPIO_ACTIVE_HIGH>; + + mode-switch; + orientation-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + wcd_usbss_sbu_mux: endpoint { + remote-endpoint = <&pmic_glink_sbu>; + }; + }; + }; + }; +}; + +&i2c6 { + clock-frequency = <100000>; + + status = "okay"; + + typec-mux@1c { + compatible = "onnn,nb7vpq904m"; + reg = <0x1c>; + + vcc-supply = <&vreg_l15b_1p8>; + + retimer-switch; + orientation-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + redriver_ss_out: endpoint { + remote-endpoint = <&pmic_glink_ss_in>; + }; + }; + + port@1 { + reg = <1>; + + redriver_ss_in: endpoint { + remote-endpoint = <&usb_dp_qmpphy_out>; + }; + }; + }; + }; +}; + +&iris { + status = "okay"; +}; + +&lpass_wsa2macro { + status = "okay"; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dp0 { + status = "okay"; +}; + +&mdss_dp0_out { + status = "okay"; +}; + +&pcie0 { + wake-gpios = <&tlmm 96 GPIO_ACTIVE_HIGH>; + perst-gpios = <&tlmm 94 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&pcie0_default_state>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcieport0 { + wifi@0 { + compatible = "pci17cb,1107"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + }; +}; + +&pcie0_phy { + vdda-phy-supply = <&vreg_l1i_0p88>; + vdda-pll-supply = <&vreg_l3i_1p2>; + + status = "okay"; +}; + +&pcie1 { + wake-gpios = <&tlmm 99 GPIO_ACTIVE_HIGH>; + perst-gpios = <&tlmm 97 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&pcie1_default_state>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie1_port0 { + /* Renesas μPD720201 PCIe USB3.0 HOST CONTROLLER */ + usb-controller@0 { + compatible = "pci1912,0014"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + avdd33-supply = <&upd720201_avdd33_reg>; + vdd10-supply = <&upd720201_vdd10_reg>; + vdd33-supply = <&upd720201_vdd33_reg>; + + pinctrl-0 = <&gamepad_pwr_en>; + pinctrl-names = "default"; + }; +}; + +&pcie1_phy { + vdda-phy-supply = <&vreg_l3e_0p9>; + vdda-pll-supply = <&vreg_l3i_1p2>; + vdda-qref-supply = <&vreg_l1i_0p88>; + + status = "okay"; +}; + +&pon_pwrkey { + status = "okay"; +}; + +&pon_resin { + linux,code = ; + + status = "okay"; +}; + +&pm8550_gpios { + volume_up_n: volume-up-n-state { + pins = "gpio6"; + function = "normal"; + bias-pull-up; + input-enable; + power-source = <1>; + }; + + pwm_fan_ctrl_active: pwm-fan-ctrl-active-state { + pins = "gpio9"; + function = "func1"; + bias-disable; + power-source = <0>; + qcom,drive-strength = ; + }; + + pwm_fan_ctrl_sleep: pwm-fan-ctrl-sleep-state { + pins = "gpio9"; + function = "normal"; + output-high; + bias-disable; + power-source = <0>; + qcom,drive-strength = ; + }; + + sdc2_card_det_n: sdc2-card-det-state { + pins = "gpio12"; + function = "normal"; + bias-pull-up; + input-enable; + output-disable; + power-source = <1>; /* 1.8 V */ + }; +}; + +&pm8550_pwm { + status = "okay"; + + multi-led { + color = ; + function = LED_FUNCTION_STATUS; + label = "Power Status"; + + #address-cells = <1>; + #size-cells = <0>; + + led@1 { + reg = <1>; + color = ; + }; + + led@2 { + reg = <2>; + color = ; + }; + + led@3 { + reg = <3>; + color = ; + }; + }; +}; + +&pm8550b_eusb2_repeater { + vdd18-supply = <&vreg_l15b_1p8>; + vdd3-supply = <&vreg_l5b_3p1>; +}; + +&qup_i2c3_data_clk { + /* Use internal I2C pull-up */ + bias-pull-up = <2200>; +}; + +&qupv3_id_0 { + status = "okay"; +}; + +&qupv3_id_1 { + status = "okay"; +}; + +&remoteproc_adsp { + firmware-name = "qcom/sm8650/ayaneo/ps2/adsp.mbn", + "qcom/sm8650/ayaneo/ps2/adsp_dtb.mbn"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/sm8650/ayaneo/ps2/cdsp.mbn", + "qcom/sm8650/ayaneo/ps2/cdsp_dtb.mbn"; + + status = "okay"; +}; + +&reserved_memory { + lost_reg_mem: lost-reg-mem { + reg = <0 0x9b09c000 0 0x4000>; + no-map; + }; + + hwfence_shbuf: hwfence-shbuf@d4e23000 { + reg = <0 0xd4e23000 0 0x2dd000>; + no-map; + }; + + splash_region: splash-region { + label = "cont_splash_region"; + reg = <0 0xd5100000 0 0x2b00000>; + no-map; + }; +}; + +&sdhc_2 { + cd-gpios = <&pm8550_gpios 12 GPIO_ACTIVE_LOW>; + + vmmc-supply = <&vreg_l9b_2p9>; + vqmmc-supply = <&vreg_l8b_1p8>; + bus-width = <4>; + no-sdio; + no-mmc; + + pinctrl-0 = <&sdc2_default>, <&sdc2_card_det_n>; + pinctrl-1 = <&sdc2_sleep>, <&sdc2_card_det_n>; + pinctrl-names = "default", "sleep"; + + status = "okay"; +}; + +&sleep_clk { + clock-frequency = <32764>; +}; + +&swr1 { + status = "okay"; + + /* WCD9395 RX */ + wcd_rx: codec@0,4 { + compatible = "sdw20217010e00"; + reg = <0 4>; + + /* + * WCD9395 RX Port 1 (HPH_L/R) <=> SWR1 Port 1 (HPH_L/R) + * WCD9395 RX Port 2 (CLSH) <=> SWR1 Port 2 (CLSH) + * WCD9395 RX Port 3 (COMP_L/R) <=> SWR1 Port 3 (COMP_L/R) + * WCD9395 RX Port 4 (LO) <=> SWR1 Port 4 (LO) + * WCD9395 RX Port 5 (DSD_L/R) <=> SWR1 Port 5 (DSD_L/R) + * WCD9395 RX Port 6 (HIFI_PCM_L/R) <=> SWR1 Port 9 (HIFI_PCM_L/R) + */ + qcom,rx-port-mapping = <1 2 3 4 5 9>; + }; +}; + +&swr2 { + status = "okay"; + + /* WCD9395 TX */ + wcd_tx: codec@0,3 { + compatible = "sdw20217010e00"; + reg = <0 3>; + + /* + * WCD9395 TX Port 1 (ADC1,2,3,4) <=> SWR2 Port 2 (TX SWR_INPUT 0,1,2,3) + * WCD9395 TX Port 2 (ADC3,4 & DMIC0,1) <=> SWR2 Port 2 (TX SWR_INPUT 0,1,2,3) + * WCD9395 TX Port 3 (DMIC0,1,2,3 & MBHC) <=> SWR2 Port 3 (TX SWR_INPUT 4,5,6,7) + * WCD9395 TX Port 4 (DMIC4,5,6,7) <=> SWR2 Port 4 (TX SWR_INPUT 8,9,10,11) + */ + qcom,tx-port-mapping = <2 2 3 4>; + }; +}; + +&swr3 { + status = "okay"; + + pinctrl-0 = <&wsa2_swr_active>, <&spkr_23_sd_n_active>; + pinctrl-names = "default"; + + /* WSA8845, Speaker Left */ + left_spkr: speaker@0,0 { + compatible = "sdw20217020400"; + reg = <0 0>; + #sound-dai-cells = <0>; + reset-gpios = <&tlmm 77 GPIO_ACTIVE_LOW>; + sound-name-prefix = "SpkrLeft"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l3c_1p2>; + + /* + * WSA8845 Port 1 (DAC) <=> SWR3 Port 1 (SPKR_L) + * WSA8845 Port 2 (COMP) <=> SWR3 Port 2 (SPKR_L_COMP) + * WSA8845 Port 3 (BOOST) <=> SWR3 Port 3 (SPKR_L_BOOST) + * WSA8845 Port 4 (PBR) <=> SWR3 Port 7 (PBR) + * WSA8845 Port 5 (VISENSE) <=> SWR3 Port 10 (SPKR_L_VI) + * WSA8845 Port 6 (CPS) <=> SWR3 Port 13 (CPS) + */ + qcom,port-mapping = <1 2 3 7 10 13>; + }; + + /* WSA8845, Speaker Right */ + right_spkr: speaker@0,1 { + compatible = "sdw20217020400"; + reg = <0 1>; + #sound-dai-cells = <0>; + reset-gpios = <&tlmm 77 GPIO_ACTIVE_LOW>; + sound-name-prefix = "SpkrRight"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l3c_1p2>; + + /* + * WSA8845 Port 1 (DAC) <=> SWR3 Port 4 (SPKR_R) + * WSA8845 Port 2 (COMP) <=> SWR3 Port 5 (SPKR_R_COMP) + * WSA8845 Port 3 (BOOST) <=> SWR3 Port 6 (SPKR_R_BOOST) + * WSA8845 Port 4 (PBR) <=> SWR3 Port 7 (PBR) + * WSA8845 Port 5 (VISENSE) <=> SWR3 Port 11 (SPKR_R_VI) + * WSA8845 Port 6 (CPS) <=> SWR3 Port 13 (CPS) + */ + qcom,port-mapping = <4 5 6 7 11 13>; + }; +}; + +&tlmm { + /* Reserved I/Os for NFC */ + gpio-reserved-ranges = <32 4>, <36 1>, <38 6>, <74 1>; + + bt_default: bt-default-state { + bt-en-pins { + pins = "gpio17"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + sw-ctrl-pins { + pins = "gpio18"; + function = "gpio"; + bias-pull-down; + }; + }; + + fan_pwr_pins: fan-pwr-state { + pins = "gpio125"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + fan_vdd_pins: fan-vdd-state { + pins = "gpio124"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + fan_int: fan-int-state { + pins = "gpio14"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + upd720201_avdd33: upd720201-avdd33-state { + pins = "gpio123"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + upd720201_vdd10: pd720201-vdd10-state { + pins = "gpio122"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + upd720201_vdd33: upd720201-vdd33-state { + pins = "gpio121"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + gamepad_pwr_en: gamepad-pwr-en-active-state { + pins = "gpio28"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-high; + }; + + spkr_23_sd_n_active: spkr-23-sd-n-active-state { + pins = "gpio77"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + spkr_01_sd_n_active: spkr-01-sd-n-active-state { + pins = "gpio21"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + wcd_default: wcd-reset-n-active-state { + pins = "gpio107"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + wlan_en: wlan-en-state { + pins = "gpio16"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; +}; + +&uart14 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn7850-bt"; + + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + + max-speed = <3200000>; + }; +}; + +&uart15 { + status = "okay"; +}; + +&ufs_mem_hc { + reset-gpios = <&tlmm 210 GPIO_ACTIVE_LOW>; + + vcc-supply = <&vreg_l17b_2p5>; + vcc-max-microamp = <1300000>; + vccq-supply = <&vreg_l1c_1p2>; + vccq-max-microamp = <1200000>; + + status = "okay"; +}; + +&ufs_mem_phy { + vdda-phy-supply = <&vreg_l1d_0p88>; + vdda-pll-supply = <&vreg_l3i_1p2>; + + status = "okay"; +}; + +/* + * DPAUX -> WCD9395 -> USB_SBU -> USB-C + * eUSB2 DP/DM -> PM85550HS -> eUSB2 DP/DM -> WCD9395 -> USB-C + * USB SS -> NB7VPQ904MMUTWG -> USB-C + */ + +&usb_1 { + dr_mode = "otg"; + usb-role-switch; + + status = "okay"; +}; + +&usb_1_dwc3_hs { + remote-endpoint = <&pmic_glink_hs_in>; +}; + +&usb_1_hsphy { + vdd-supply = <&vreg_l1i_0p88>; + vdda12-supply = <&vreg_l3i_1p2>; + + phys = <&pm8550b_eusb2_repeater>; + + status = "okay"; +}; + +&usb_dp_qmpphy { + vdda-phy-supply = <&vreg_l3i_1p2>; + vdda-pll-supply = <&vreg_l3g_0p91>; + + status = "okay"; +}; + +&usb_dp_qmpphy_out { + remote-endpoint = <&redriver_ss_in>; +}; + +&xo_board { + clock-frequency = <76800000>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8650-mtp.dts b/arch/arm64/boot/dts/qcom/sm8650-mtp.dts index bb688a5d21c2..dd6e33d2dc5d 100644 --- a/arch/arm64/boot/dts/qcom/sm8650-mtp.dts +++ b/arch/arm64/boot/dts/qcom/sm8650-mtp.dts @@ -618,8 +618,8 @@ vci-supply = <&vreg_l13b_3p0>; vdd-supply = <&vreg_l11b_1p2>; - pinctrl-0 = <&disp0_reset_n_active>, <&mdp_vsync_active>; - pinctrl-1 = <&disp0_reset_n_suspend>, <&mdp_vsync_suspend>; + pinctrl-0 = <&disp0_reset_n_active>, <&mdp_vsync>; + pinctrl-1 = <&disp0_reset_n_suspend>, <&mdp_vsync>; pinctrl-names = "default", "sleep"; port { @@ -821,14 +821,7 @@ bias-pull-down; }; - mdp_vsync_active: mdp-vsync-active-state { - pins = "gpio86"; - function = "mdp_vsync"; - drive-strength = <2>; - bias-pull-down; - }; - - mdp_vsync_suspend: mdp-vsync-suspend-state { + mdp_vsync: mdp-vsync-state { pins = "gpio86"; function = "mdp_vsync"; drive-strength = <2>; diff --git a/arch/arm64/boot/dts/qcom/sm8650-qrd.dts b/arch/arm64/boot/dts/qcom/sm8650-qrd.dts index 087828c60692..a3982ae22929 100644 --- a/arch/arm64/boot/dts/qcom/sm8650-qrd.dts +++ b/arch/arm64/boot/dts/qcom/sm8650-qrd.dts @@ -908,8 +908,8 @@ vci-supply = <&vreg_l13b_3p0>; vdd-supply = <&vreg_l11b_1p2>; - pinctrl-0 = <&disp0_reset_n_active>, <&mdp_vsync_active>; - pinctrl-1 = <&disp0_reset_n_suspend>, <&mdp_vsync_suspend>; + pinctrl-0 = <&disp0_reset_n_active>, <&mdp_vsync>; + pinctrl-1 = <&disp0_reset_n_suspend>, <&mdp_vsync>; pinctrl-names = "default", "sleep"; port { @@ -1244,14 +1244,7 @@ bias-pull-down; }; - mdp_vsync_active: mdp-vsync-active-state { - pins = "gpio86"; - function = "mdp_vsync"; - drive-strength = <2>; - bias-pull-down; - }; - - mdp_vsync_suspend: mdp-vsync-suspend-state { + mdp_vsync: mdp-vsync-state { pins = "gpio86"; function = "mdp_vsync"; drive-strength = <2>; diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi index 357e43b90740..1604bc8cff37 100644 --- a/arch/arm64/boot/dts/qcom/sm8650.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi @@ -3917,7 +3917,7 @@ }; }; - pcie@0 { + pcie1_port0: pcie@0 { device_type = "pci"; reg = <0x0 0x0 0x0 0x0 0x0>; bus-range = <0x01 0xff>; @@ -4393,6 +4393,8 @@ qcom,smem-state-names = "ipa-clock-enabled-valid", "ipa-clock-enabled"; + sram = <&ipa_modem_tables>; + status = "disabled"; }; @@ -4957,7 +4959,7 @@ clocks = <&gcc GCC_SDCC2_AHB_CLK>, <&gcc GCC_SDCC2_APPS_CLK>, - <&rpmhcc RPMH_CXO_CLK>; + <&bi_tcxo_div2>; clock-names = "iface", "core", "xo"; @@ -4976,9 +4978,6 @@ bus-width = <4>; - /* Forbid SDR104/SDR50 - broken hw! */ - sdhci-caps-mask = <0x3 0>; - qcom,dll-config = <0x0007642c>; qcom,ddr-config = <0x80040868>; @@ -5236,13 +5235,13 @@ opp-300000000 { opp-hz = /bits/ 64 <300000000>; - required-opps = <&rpmhpd_opp_low_svs>, + required-opps = <&rpmhpd_opp_svs>, <&rpmhpd_opp_low_svs>; }; opp-380000000 { opp-hz = /bits/ 64 <380000000>; - required-opps = <&rpmhpd_opp_svs>, + required-opps = <&rpmhpd_opp_svs_l1>, <&rpmhpd_opp_svs>; }; @@ -5254,13 +5253,13 @@ opp-480000000 { opp-hz = /bits/ 64 <480000000>; - required-opps = <&rpmhpd_opp_nom>, + required-opps = <&rpmhpd_opp_svs_l1>, <&rpmhpd_opp_nom>; }; opp-533333334 { opp-hz = /bits/ 64 <533333334>; - required-opps = <&rpmhpd_opp_turbo>, + required-opps = <&rpmhpd_opp_svs_l1>, <&rpmhpd_opp_turbo>; }; }; @@ -5905,6 +5904,7 @@ phy-names = "dp"; #sound-dai-cells = <0>; + sound-name-prefix = "DisplayPort0"; status = "disabled"; @@ -7078,6 +7078,20 @@ }; }; + sram@14680000 { + compatible = "qcom,sm8650-imem", "syscon", "simple-mfd"; + reg = <0 0x14680000 0 0x2c000>; + + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0 0 0x14680000 0x2c000>; + + ipa_modem_tables: modem-tables@8000 { + reg = <0x8000 0x2000>; + }; + }; + apps_smmu: iommu@15000000 { compatible = "qcom,sm8650-smmu-500", "qcom,smmu-500", "arm,mmu-500"; reg = <0 0x15000000 0 0x100000>; @@ -7219,7 +7233,7 @@ gic_its: msi-controller@17140000 { compatible = "arm,gic-v3-its"; - reg = <0 0x17140000 0 0x20000>; + reg = <0 0x17140000 0 0x40000>; msi-controller; #msi-cells = <1>; @@ -7846,7 +7860,7 @@ }; }; - cpu2-top-thermal { + cpu2_top_thermal: cpu2-top-thermal { thermal-sensors = <&tsens0 5>; trips { @@ -7870,7 +7884,7 @@ }; }; - cpu3-top-thermal { + cpu3_top_thermal: cpu3-top-thermal { thermal-sensors = <&tsens0 7>; trips { @@ -7894,7 +7908,7 @@ }; }; - cpu4-top-thermal { + cpu4_top_thermal: cpu4-top-thermal { thermal-sensors = <&tsens0 9>; trips { @@ -7918,7 +7932,7 @@ }; }; - cpu5-top-thermal { + cpu5_top_thermal: cpu5-top-thermal { thermal-sensors = <&tsens0 11>; trips { @@ -7942,7 +7956,7 @@ }; }; - cpu6-top-thermal { + cpu6_top_thermal: cpu6-top-thermal { thermal-sensors = <&tsens0 13>; trips { @@ -7984,7 +7998,7 @@ }; }; - cpu7-top-thermal { + cpu7_top_thermal: cpu7-top-thermal { thermal-sensors = <&tsens1 1>; trips { @@ -8247,14 +8261,14 @@ thermal-sensors = <&tsens2 1>; - cooling-maps { + gpu0_cooling_maps: cooling-maps { map0 { trip = <&gpu0_alert0>; cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; }; - trips { + gpu0_trips: trips { gpu0_alert0: trip-point0 { temperature = <95000>; hysteresis = <1000>; @@ -8280,14 +8294,14 @@ thermal-sensors = <&tsens2 2>; - cooling-maps { + gpu1_cooling_maps: cooling-maps { map0 { trip = <&gpu1_alert0>; cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; }; - trips { + gpu1_trips: trips { gpu1_alert0: trip-point0 { temperature = <95000>; hysteresis = <1000>; @@ -8313,14 +8327,14 @@ thermal-sensors = <&tsens2 3>; - cooling-maps { + gpu2_cooling_maps: cooling-maps { map0 { trip = <&gpu2_alert0>; cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; }; - trips { + gpu2_trips: trips { gpu2_alert0: trip-point0 { temperature = <95000>; hysteresis = <1000>; @@ -8346,14 +8360,14 @@ thermal-sensors = <&tsens2 4>; - cooling-maps { + gpu3_cooling_maps: cooling-maps { map0 { trip = <&gpu3_alert0>; cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; }; - trips { + gpu3_trips: trips { gpu3_alert0: trip-point0 { temperature = <95000>; hysteresis = <1000>; @@ -8379,14 +8393,14 @@ thermal-sensors = <&tsens2 5>; - cooling-maps { + gpu4_cooling_maps: cooling-maps { map0 { trip = <&gpu4_alert0>; cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; }; - trips { + gpu4_trips: trips { gpu4_alert0: trip-point0 { temperature = <95000>; hysteresis = <1000>; @@ -8412,14 +8426,14 @@ thermal-sensors = <&tsens2 6>; - cooling-maps { + gpu5_cooling_maps: cooling-maps { map0 { trip = <&gpu5_alert0>; cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; }; - trips { + gpu5_trips: trips { gpu5_alert0: trip-point0 { temperature = <95000>; hysteresis = <1000>; @@ -8445,14 +8459,14 @@ thermal-sensors = <&tsens2 7>; - cooling-maps { + gpu6_cooling_maps: cooling-maps { map0 { trip = <&gpu6_alert0>; cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; }; - trips { + gpu6_trips: trips { gpu6_alert0: trip-point0 { temperature = <95000>; hysteresis = <1000>; @@ -8478,14 +8492,14 @@ thermal-sensors = <&tsens2 8>; - cooling-maps { + gpu7_cooling_maps: cooling-maps { map0 { trip = <&gpu7_alert0>; cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; }; }; - trips { + gpu7_trips: trips { gpu7_alert0: trip-point0 { temperature = <95000>; hysteresis = <1000>; diff --git a/arch/arm64/boot/dts/qcom/sm8750-mtp.dts b/arch/arm64/boot/dts/qcom/sm8750-mtp.dts index cb718331496e..3837f6785320 100644 --- a/arch/arm64/boot/dts/qcom/sm8750-mtp.dts +++ b/arch/arm64/boot/dts/qcom/sm8750-mtp.dts @@ -54,6 +54,15 @@ vdd-px-supply = <&vreg_l2i_1p2>; #sound-dai-cells = <1>; + + mode-switch; + orientation-switch; + + port { + wcd_codec_headset_in: endpoint { + remote-endpoint = <&wcd_usbss_headset_out>; + }; + }; }; chosen { @@ -230,6 +239,7 @@ reg = <2>; pmic_glink_sbu: endpoint { + remote-endpoint = <&wcd_usbss_sbu_mux>; }; }; }; @@ -925,6 +935,42 @@ }; }; +&i2c3 { + status = "okay"; + + wcd_usbss: typec-mux@e { + compatible = "qcom,wcd9395-usbss", "qcom,wcd9390-usbss"; + reg = <0xe>; + + vdd-supply = <&vreg_l15b_1p8>; + reset-gpios = <&tlmm 152 GPIO_ACTIVE_HIGH>; + + mode-switch; + orientation-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + wcd_usbss_sbu_mux: endpoint { + remote-endpoint = <&pmic_glink_sbu>; + }; + }; + + port@1 { + reg = <1>; + + wcd_usbss_headset_out: endpoint { + remote-endpoint = <&wcd_codec_headset_in>; + }; + }; + }; + }; +}; + &iris { status = "okay"; }; @@ -937,6 +983,56 @@ qcom,dmic-sample-rate = <4800000>; }; +&mdss { + status = "okay"; +}; + +&mdss_dp0 { + status = "okay"; +}; + +&mdss_dp0_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dsi0 { + vdda-supply = <&vreg_l3g_1p2>; + + status = "okay"; + + panel@0 { + compatible = "novatek,nt37801"; + reg = <0>; + + reset-gpios = <&tlmm 98 GPIO_ACTIVE_LOW>; + + vddio-supply = <&vreg_l12b_1p8>; + vci-supply = <&vreg_l13b_3p0>; + vdd-supply = <&vreg_l11b_1p0>; + + pinctrl-0 = <&disp0_reset_n_active>, <&mdp_vsync>; + pinctrl-1 = <&disp0_reset_n_suspend>, <&mdp_vsync>; + pinctrl-names = "default", "sleep"; + + port { + panel0_in: endpoint { + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + }; +}; + +&mdss_dsi0_out { + remote-endpoint = <&panel0_in>; + data-lanes = <0 1 2 3>; +}; + +&mdss_dsi0_phy { + vdds-supply = <&vreg_l3i_0p88>; + + status = "okay"; +}; + &pm8550_flash { status = "okay"; @@ -1053,6 +1149,11 @@ status = "okay"; }; +&qup_i2c3_data_clk { + /* Use internal I2C pull-up */ + bias-pull-up = <2200>; +}; + &qupv3_1 { status = "okay"; }; @@ -1225,6 +1326,27 @@ bias-pull-up; }; + mdp_vsync: mdp-vsync-state { + pins = "gpio86"; + function = "mdp_vsync"; + drive-strength = <2>; + bias-pull-down; + }; + + disp0_reset_n_active: disp0-reset-n-active-state { + pins = "gpio98"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + + disp0_reset_n_suspend: disp0-reset-n-suspend-state { + pins = "gpio98"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + wcd_default: wcd-reset-n-active-state { pins = "gpio101"; function = "gpio"; diff --git a/arch/arm64/boot/dts/qcom/sm8750.dtsi b/arch/arm64/boot/dts/qcom/sm8750.dtsi index f56b1f889b85..18fb52c14acd 100644 --- a/arch/arm64/boot/dts/qcom/sm8750.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8750.dtsi @@ -3,7 +3,9 @@ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ +#include #include +#include #include #include #include @@ -20,6 +22,7 @@ #include #include #include +#include / { interrupt-parent = <&intc>; @@ -2787,6 +2790,7 @@ reg = <2>; usb_dp_qmpphy_dp_in: endpoint { + remote-endpoint = <&mdss_dp0_out>; }; }; }; @@ -2945,19 +2949,19 @@ opp-240000000 { opp-hz = /bits/ 64 <240000000>; - required-opps = <&rpmhpd_opp_low_svs_d1>, + required-opps = <&rpmhpd_opp_svs>, <&rpmhpd_opp_low_svs_d1>; }; opp-338000000 { opp-hz = /bits/ 64 <338000000>; - required-opps = <&rpmhpd_opp_low_svs>, + required-opps = <&rpmhpd_opp_svs>, <&rpmhpd_opp_low_svs>; }; opp-420000000 { opp-hz = /bits/ 64 <420000000>; - required-opps = <&rpmhpd_opp_svs>, + required-opps = <&rpmhpd_opp_svs_l1>, <&rpmhpd_opp_svs>; }; @@ -2969,19 +2973,19 @@ opp-533333334 { opp-hz = /bits/ 64 <533333334>; - required-opps = <&rpmhpd_opp_nom>, + required-opps = <&rpmhpd_opp_svs_l1>, <&rpmhpd_opp_nom>; }; opp-570000000 { opp-hz = /bits/ 64 <570000000>; - required-opps = <&rpmhpd_opp_nom_l1>, + required-opps = <&rpmhpd_opp_nom>, <&rpmhpd_opp_nom_l1>; }; opp-630000000 { opp-hz = /bits/ 64 <630000000>; - required-opps = <&rpmhpd_opp_turbo>, + required-opps = <&rpmhpd_opp_nom>, <&rpmhpd_opp_turbo>; }; }; @@ -3001,6 +3005,437 @@ #power-domain-cells = <1>; }; + mdss: display-subsystem@ae00000 { + compatible = "qcom,sm8750-mdss"; + reg = <0x0 0x0ae00000 0x0 0x1000>; + reg-names = "mdss"; + + interrupts = ; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>, + <&dispcc DISP_CC_MDSS_MDP_CLK>; + + resets = <&dispcc DISP_CC_MDSS_CORE_BCR>; + + interconnects = <&mmss_noc MASTER_MDP QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_DISPLAY_CFG QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "mdp0-mem", + "cpu-cfg"; + + power-domains = <&dispcc MDSS_GDSC>; + + iommus = <&apps_smmu 0x800 0x2>; + + interrupt-controller; + #interrupt-cells = <1>; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + status = "disabled"; + + mdss_mdp: display-controller@ae01000 { + compatible = "qcom,sm8750-dpu"; + reg = <0x0 0x0ae01000 0x0 0x93000>, + <0x0 0x0aeb0000 0x0 0x2008>; + reg-names = "mdp", + "vbif"; + + interrupts-extended = <&mdss 0>; + + clocks = <&gcc GCC_DISP_HF_AXI_CLK>, + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&dispcc DISP_CC_MDSS_MDP_LUT_CLK>, + <&dispcc DISP_CC_MDSS_MDP_CLK>, + <&dispcc DISP_CC_MDSS_VSYNC_CLK>; + clock-names = "nrt_bus", + "iface", + "lut", + "core", + "vsync"; + + assigned-clocks = <&dispcc DISP_CC_MDSS_VSYNC_CLK>; + assigned-clock-rates = <19200000>; + + operating-points-v2 = <&mdp_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dpu_intf1_out: endpoint { + remote-endpoint = <&mdss_dsi0_in>; + }; + }; + + port@1 { + reg = <1>; + + dpu_intf2_out: endpoint { + remote-endpoint = <&mdss_dsi1_in>; + }; + }; + + port@2 { + reg = <2>; + + dpu_intf0_out: endpoint { + remote-endpoint = <&mdss_dp0_in>; + }; + }; + }; + + mdp_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-156000000 { + opp-hz = /bits/ 64 <156000000>; + required-opps = <&rpmhpd_opp_low_svs_d1>; + }; + + opp-207000000 { + opp-hz = /bits/ 64 <207000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-337000000 { + opp-hz = /bits/ 64 <337000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-417000000 { + opp-hz = /bits/ 64 <417000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-532000000 { + opp-hz = /bits/ 64 <532000000>; + required-opps = <&rpmhpd_opp_nom>; + }; + + opp-575000000 { + opp-hz = /bits/ 64 <575000000>; + required-opps = <&rpmhpd_opp_nom_l1>; + }; + }; + }; + + mdss_dsi0: dsi@ae94000 { + compatible = "qcom,sm8750-dsi-ctrl", "qcom,mdss-dsi-ctrl"; + reg = <0x0 0x0ae94000 0x0 0x400>; + reg-names = "dsi_ctrl"; + + interrupts-extended = <&mdss 4>; + + clocks = <&dispcc DISP_CC_MDSS_BYTE0_CLK>, + <&dispcc DISP_CC_MDSS_BYTE0_INTF_CLK>, + <&dispcc DISP_CC_MDSS_PCLK0_CLK>, + <&dispcc DISP_CC_MDSS_ESC0_CLK>, + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>, + <&mdss_dsi0_phy DSI_PIXEL_PLL_CLK>, + <&mdss_dsi0_phy DSI_BYTE_PLL_CLK>, + <&dispcc DISP_CC_ESYNC0_CLK>, + <&dispcc DISP_CC_OSC_CLK>, + <&dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>, + <&dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus", + "dsi_pll_pixel", + "dsi_pll_byte", + "esync", + "osc", + "byte_src", + "pixel_src"; + + operating-points-v2 = <&mdss_dsi_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + phys = <&mdss_dsi0_phy>; + phy-names = "dsi"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss_dsi0_in: endpoint { + remote-endpoint = <&dpu_intf1_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dsi0_out: endpoint { + }; + }; + }; + + mdss_dsi_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-140630000 { + opp-hz = /bits/ 64 <140630000>; + required-opps = <&rpmhpd_opp_low_svs_d1>; + }; + + opp-187500000 { + opp-hz = /bits/ 64 <187500000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-358000000 { + opp-hz = /bits/ 64 <358000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + }; + }; + + mdss_dsi0_phy: phy@ae95000 { + compatible = "qcom,sm8750-dsi-phy-3nm"; + reg = <0x0 0x0ae95000 0x0 0x200>, + <0x0 0x0ae95200 0x0 0x280>, + <0x0 0x0ae95500 0x0 0x400>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&bi_tcxo_div2>; + clock-names = "iface", + "ref"; + + #clock-cells = <1>; + #phy-cells = <0>; + + status = "disabled"; + }; + + mdss_dsi1: dsi@ae96000 { + compatible = "qcom,sm8750-dsi-ctrl", "qcom,mdss-dsi-ctrl"; + reg = <0x0 0x0ae96000 0x0 0x400>; + reg-names = "dsi_ctrl"; + + interrupts-extended = <&mdss 5>; + + clocks = <&dispcc DISP_CC_MDSS_BYTE1_CLK>, + <&dispcc DISP_CC_MDSS_BYTE1_INTF_CLK>, + <&dispcc DISP_CC_MDSS_PCLK1_CLK>, + <&dispcc DISP_CC_MDSS_ESC1_CLK>, + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>, + <&mdss_dsi1_phy DSI_PIXEL_PLL_CLK>, + <&mdss_dsi1_phy DSI_BYTE_PLL_CLK>, + <&dispcc DISP_CC_ESYNC1_CLK>, + <&dispcc DISP_CC_OSC_CLK>, + <&dispcc DISP_CC_MDSS_BYTE1_CLK_SRC>, + <&dispcc DISP_CC_MDSS_PCLK1_CLK_SRC>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus", + "dsi_pll_pixel", + "dsi_pll_byte", + "esync", + "osc", + "byte_src", + "pixel_src"; + + operating-points-v2 = <&mdss_dsi_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + phys = <&mdss_dsi1_phy>; + phy-names = "dsi"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss_dsi1_in: endpoint { + remote-endpoint = <&dpu_intf2_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dsi1_out: endpoint { + }; + }; + }; + }; + + mdss_dsi1_phy: phy@ae97000 { + compatible = "qcom,sm8750-dsi-phy-3nm"; + reg = <0x0 0x0ae97000 0x0 0x200>, + <0x0 0x0ae97200 0x0 0x280>, + <0x0 0x0ae97500 0x0 0x400>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", + "ref"; + + #clock-cells = <1>; + #phy-cells = <0>; + + status = "disabled"; + }; + + mdss_dp0: displayport-controller@af54000 { + compatible = "qcom,sm8750-dp", "qcom,sm8650-dp"; + reg = <0x0 0xaf54000 0x0 0x104>, + <0x0 0xaf54200 0x0 0xc0>, + <0x0 0xaf55000 0x0 0x770>, + <0x0 0xaf56000 0x0 0x9c>, + <0x0 0xaf57000 0x0 0x9c>; + + interrupts-extended = <&mdss 12>; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&dispcc DISP_CC_MDSS_DPTX0_AUX_CLK>, + <&dispcc DISP_CC_MDSS_DPTX0_LINK_CLK>, + <&dispcc DISP_CC_MDSS_DPTX0_LINK_INTF_CLK>, + <&dispcc DISP_CC_MDSS_DPTX0_PIXEL0_CLK>, + <&dispcc DISP_CC_MDSS_DPTX0_PIXEL1_CLK>; + clock-names = "core_iface", + "core_aux", + "ctrl_link", + "ctrl_link_iface", + "stream_pixel", + "stream_1_pixel"; + + assigned-clocks = <&dispcc DISP_CC_MDSS_DPTX0_LINK_CLK_SRC>, + <&dispcc DISP_CC_MDSS_DPTX0_PIXEL0_CLK_SRC>, + <&dispcc DISP_CC_MDSS_DPTX0_PIXEL1_CLK_SRC>; + assigned-clock-parents = <&usb_dp_qmpphy QMP_USB43DP_DP_LINK_CLK>, + <&usb_dp_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, + <&usb_dp_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>; + + operating-points-v2 = <&dp_opp_table>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + + phys = <&usb_dp_qmpphy QMP_USB43DP_DP_PHY>; + phy-names = "dp"; + + #sound-dai-cells = <0>; + + status = "disabled"; + + dp_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-270000000 { + opp-hz = /bits/ 64 <270000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-540000000 { + opp-hz = /bits/ 64 <540000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-810000000 { + opp-hz = /bits/ 64 <810000000>; + required-opps = <&rpmhpd_opp_nom>; + }; + }; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss_dp0_in: endpoint { + remote-endpoint = <&dpu_intf0_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dp0_out: endpoint { + data-lanes = <0 1 2 3>; + remote-endpoint = <&usb_dp_qmpphy_dp_in>; + }; + }; + }; + }; + }; + + dispcc: clock-controller@af00000 { + compatible = "qcom,sm8750-dispcc"; + reg = <0x0 0x0af00000 0x0 0x20000>; + + clocks = <&bi_tcxo_div2>, + <&bi_tcxo_ao_div2>, + <&gcc GCC_DISP_AHB_CLK>, + <&sleep_clk>, + <&mdss_dsi0_phy DSI_BYTE_PLL_CLK>, + <&mdss_dsi0_phy DSI_PIXEL_PLL_CLK>, + <&mdss_dsi1_phy DSI_BYTE_PLL_CLK>, + <&mdss_dsi1_phy DSI_PIXEL_PLL_CLK>, + <&usb_dp_qmpphy QMP_USB43DP_DP_LINK_CLK>, + <&usb_dp_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>, + <0>, /* dp1 */ + <0>, + <0>, /* dp2 */ + <0>, + <0>, /* dp3 */ + <0>; + + power-domains = <&rpmhpd RPMHPD_MMCX>; + required-opps = <&rpmhpd_opp_low_svs>; + + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + pdc: interrupt-controller@b220000 { compatible = "qcom,sm8750-pdc", "qcom,pdc"; reg = <0x0 0x0b220000 0x0 0x10000>, <0x0 0x164400f0 0x0 0x64>; @@ -3013,6 +3448,54 @@ interrupt-controller; }; + tsens0: thermal-sensor@c228000 { + compatible = "qcom,sm8750-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c228000 0x0 0x1000>, + <0x0 0x0c222000 0x0 0x1000>; + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + #qcom,sensors = <15>; + #thermal-sensor-cells = <1>; + }; + + tsens1: thermal-sensor@c229000 { + compatible = "qcom,sm8750-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c229000 0x0 0x1000>, + <0x0 0x0c223000 0x0 0x1000>; + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + #qcom,sensors = <7>; + #thermal-sensor-cells = <1>; + }; + + tsens2: thermal-sensor@c22a000 { + compatible = "qcom,sm8750-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22a000 0x0 0x1000>, + <0x0 0x0c224000 0x0 0x1000>; + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + #qcom,sensors = <16>; + #thermal-sensor-cells = <1>; + }; + + tsens3: thermal-sensor@c22b000 { + compatible = "qcom,sm8750-tsens", "qcom,tsens-v2"; + reg = <0x0 0x0c22b000 0x0 0x1000>, + <0x0 0x0c225000 0x0 0x1000>; + interrupts = , + ; + interrupt-names = "uplow", + "critical"; + #qcom,sensors = <9>; + #thermal-sensor-cells = <1>; + }; + aoss_qmp: power-management@c300000 { compatible = "qcom,sm8750-aoss-qmp", "qcom,aoss-qmp"; reg = <0x0 0x0c300000 0x0 0x400>; @@ -4658,7 +5141,7 @@ gic_its: msi-controller@16040000 { compatible = "arm,gic-v3-its"; - reg = <0x0 0x16040000 0x0 0x20000>; + reg = <0x0 0x16040000 0x0 0x40000>; msi-controller; #msi-cells = <1>; @@ -5459,6 +5942,854 @@ }; }; + thermal-zones { + aoss0-thermal { + thermal-sensors = <&tsens0 0>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + aoss0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-0-0-thermal { + thermal-sensors = <&tsens0 1>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-0-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-0-1-thermal { + thermal-sensors = <&tsens0 2>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-0-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-1-0-thermal { + thermal-sensors = <&tsens0 3>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-1-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-1-1-thermal { + thermal-sensors = <&tsens0 4>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-1-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-2-0-thermal { + thermal-sensors = <&tsens0 5>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-2-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-2-1-thermal { + thermal-sensors = <&tsens0 6>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-2-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-3-0-thermal { + thermal-sensors = <&tsens0 7>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-3-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-3-1-thermal { + thermal-sensors = <&tsens0 8>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-3-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-4-0-thermal { + thermal-sensors = <&tsens0 9>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-4-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-4-1-thermal { + thermal-sensors = <&tsens0 10>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-4-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-5-0-thermal { + thermal-sensors = <&tsens0 11>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-5-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-0-5-1-thermal { + thermal-sensors = <&tsens0 12>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-0-5-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpuss-0-0-thermal { + thermal-sensors = <&tsens0 13>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpuss-0-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpuss-0-1-thermal { + thermal-sensors = <&tsens0 14>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpuss-0-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + aoss1-thermal { + thermal-sensors = <&tsens1 0>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + aoss1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-1-0-0-thermal { + thermal-sensors = <&tsens1 1>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-1-0-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-1-0-1-thermal { + thermal-sensors = <&tsens1 2>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-1-0-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-1-1-0-thermal { + thermal-sensors = <&tsens1 3>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-1-1-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpu-1-1-1-thermal { + thermal-sensors = <&tsens1 4>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpu-1-1-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpuss-1-0-thermal { + thermal-sensors = <&tsens1 5>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpuss-1-0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + cpuss-1-1-thermal { + thermal-sensors = <&tsens1 6>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + cpuss-1-1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + aoss2-thermal { + thermal-sensors = <&tsens2 0>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + aoss2-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss0-thermal { + thermal-sensors = <&tsens2 1>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss1-thermal { + thermal-sensors = <&tsens2 2>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss2-thermal { + thermal-sensors = <&tsens2 3>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss2-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss3-thermal { + thermal-sensors = <&tsens2 4>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss3-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss4-thermal { + thermal-sensors = <&tsens2 5>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss4-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss5-thermal { + thermal-sensors = <&tsens2 6>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss5-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss6-thermal { + thermal-sensors = <&tsens2 7>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss6-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + gpuss7-thermal { + thermal-sensors = <&tsens2 8>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + gpuss7-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + modem0-thermal { + thermal-sensors = <&tsens2 9>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + modem0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + modem1-thermal { + thermal-sensors = <&tsens2 10>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + modem1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + modem2-thermal { + thermal-sensors = <&tsens2 11>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + modem2-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + modem3-thermal { + thermal-sensors = <&tsens2 12>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + modem3-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + camera0-thermal { + thermal-sensors = <&tsens2 13>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + camera0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + camera1-thermal { + thermal-sensors = <&tsens2 14>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + camera1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + video-thermal { + thermal-sensors = <&tsens2 15>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + video-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + aoss3-thermal { + thermal-sensors = <&tsens3 0>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + aoss3-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphvx0-thermal { + thermal-sensors = <&tsens3 1>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphvx0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphvx1-thermal { + thermal-sensors = <&tsens3 2>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphvx1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphvx2-thermal { + thermal-sensors = <&tsens3 3>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphvx2-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphmx0-thermal { + thermal-sensors = <&tsens3 4>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphmx0-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphmx1-thermal { + thermal-sensors = <&tsens3 5>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphmx1-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphmx2-thermal { + thermal-sensors = <&tsens3 6>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphmx2-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + nsphmx3-thermal { + thermal-sensors = <&tsens3 7>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <5000>; + type = "hot"; + }; + + nsphmx3-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + + ddr-thermal { + thermal-sensors = <&tsens3 8>; + + trips { + trip-point0 { + temperature = <120000>; + hysteresis = <2000>; + type = "hot"; + }; + + ddr-critical { + temperature = <125000>; + hysteresis = <0>; + type = "critical"; + }; + }; + }; + }; + timer { compatible = "arm,armv8-timer"; diff --git a/arch/arm64/boot/dts/qcom/smb2370.dtsi b/arch/arm64/boot/dts/qcom/smb2370.dtsi new file mode 100644 index 000000000000..80f3fdae5705 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/smb2370.dtsi @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +&spmi_bus2 { + smb2370_j_e2: pmic@9 { + compatible = "qcom,smb2370", "qcom,spmi-pmic"; + reg = <0x9 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + smb2370_j_e2_eusb2_repeater: phy@fd00 { + compatible = "qcom,smb2370-eusb2-repeater"; + reg = <0xfd00>; + #phy-cells = <0>; + }; + }; + + smb2370_k_e2: pmic@a { + compatible = "qcom,smb2370", "qcom,spmi-pmic"; + reg = <0xa SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + smb2370_k_e2_eusb2_repeater: phy@fd00 { + compatible = "qcom,smb2370-eusb2-repeater"; + reg = <0xfd00>; + #phy-cells = <0>; + }; + }; + + smb2370_l_e2: pmic@b { + compatible = "qcom,smb2370", "qcom,spmi-pmic"; + reg = <0xb SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + smb2370_l_e2_eusb2_repeater: phy@fd00 { + compatible = "qcom,smb2370-eusb2-repeater"; + reg = <0xfd00>; + #phy-cells = <0>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/talos-el2.dtso b/arch/arm64/boot/dts/qcom/talos-el2.dtso new file mode 100644 index 000000000000..f6818c058d72 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/talos-el2.dtso @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + + * Talos specific modifications required to boot in EL2. + */ + +/dts-v1/; +/plugin/; + +&gpu_zap_shader { + status = "disabled"; +}; + +&remoteproc_adsp { + iommus = <&apps_smmu 0x1720 0x0>; +}; + +&remoteproc_cdsp { + iommus = <&apps_smmu 0x1080 0x0>; +}; + +&venus { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/qcom/talos-evk-camera-imx577.dtso b/arch/arm64/boot/dts/qcom/talos-evk-camera-imx577.dtso new file mode 100644 index 000000000000..e0c385ec53b1 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/talos-evk-camera-imx577.dtso @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&camss { + vdd-csiphy-1p2-supply = <&vreg_l11a>; + vdd-csiphy-1p8-supply = <&vreg_l12a>; + + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + + csiphy1_ep: endpoint { + data-lanes = <0 1 2 3>; + remote-endpoint = <&imx577_ep>; + }; + }; + }; +}; + +&cci { + status = "okay"; +}; + +&cci_i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + camera@1a { + compatible = "sony,imx577"; + reg = <0x1a>; + + reset-gpios = <&tlmm 29 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&cam_mclk2_default &cam1_reset_default>; + pinctrl-names = "default"; + + clocks = <&camcc CAM_CC_MCLK2_CLK>; + assigned-clocks = <&camcc CAM_CC_MCLK2_CLK>; + assigned-clock-rates = <24000000>; + + avdd-supply = <&vreg_s4a>; + + port { + imx577_ep: endpoint { + link-frequencies = /bits/ 64 <600000000>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&csiphy1_ep>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/talos-evk-lvds-auo,g133han01.dtso b/arch/arm64/boot/dts/qcom/talos-evk-lvds-auo,g133han01.dtso new file mode 100644 index 000000000000..8d16ce4a61d2 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/talos-evk-lvds-auo,g133han01.dtso @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +/dts-v1/; +/plugin/; + +#include + +&{/} { + backlight: backlight { + compatible = "gpio-backlight"; + gpios = <&tlmm 115 GPIO_ACTIVE_HIGH>; + default-on; + }; + + panel-lvds { + compatible = "auo,g133han01"; + power-supply = <&vreg_v3p3>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + /* LVDS A (Odd pixels) */ + port@0 { + reg = <0>; + dual-lvds-odd-pixels; + + lvds_panel_out_a: endpoint { + remote-endpoint = <&sn65dsi84_out_a>; + }; + }; + + /* LVDS B (Even pixels) */ + port@1 { + reg = <1>; + dual-lvds-even-pixels; + + lvds_panel_out_b: endpoint { + remote-endpoint = <&sn65dsi84_out_b>; + }; + }; + }; + }; + + vreg_v3p3: regulator-v3p3 { + compatible = "regulator-fixed"; + regulator-name = "vdd-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; +}; + +&hdmi_connector { + status = "disabled"; +}; + +&i2c1 { + clock-frequency = <400000>; + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + + hdmi_bridge: bridge@3d { + reg = <0x3d>; + status = "disabled"; + }; + + lvds_bridge: bridge@2c { + compatible = "ti,sn65dsi84"; + reg = <0x2c>; + enable-gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + sn65dsi84_in: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + + port@2 { + reg = <2>; + + sn65dsi84_out_a: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&lvds_panel_out_a>; + }; + }; + + port@3 { + reg = <3>; + + sn65dsi84_out_b: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&lvds_panel_out_b>; + }; + }; + }; + }; +}; + +&mdss_dsi0 { + vdda-supply = <&vreg_l11a>; + + status = "okay"; +}; + +&mdss_dsi0_out { + remote-endpoint = <&sn65dsi84_in>; + data-lanes = <0 1 2 3>; +}; + +&tlmm { + lcd_bklt_en: lcd-bklt-en-state { + pins = "gpio115"; + function = "gpio"; + bias-disable; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/talos-evk-som.dtsi b/arch/arm64/boot/dts/qcom/talos-evk-som.dtsi new file mode 100644 index 000000000000..294354c034c3 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/talos-evk-som.dtsi @@ -0,0 +1,617 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +/dts-v1/; + +#include +#include +#include "talos.dtsi" +#include "pm8150.dtsi" +/ { + aliases { + i2c1 = &i2c1; + i2c5 = &i2c5; + mmc0 = &sdhc_1; + serial0 = &uart0; + serial1 = &uart7; + spi6 = &spi6; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + clocks { + can_osc: can-oscillator { + compatible = "fixed-clock"; + clock-frequency = <20000000>; + #clock-cells = <0>; + }; + + sleep_clk: sleep-clk { + compatible = "fixed-clock"; + clock-frequency = <32764>; + #clock-cells = <0>; + }; + + xo_board_clk: xo-board-clk { + compatible = "fixed-clock"; + clock-frequency = <38400000>; + #clock-cells = <0>; + }; + }; + + vreg_conn_1p8: regulator-conn-1p8 { + compatible = "regulator-fixed"; + regulator-name = "vreg_conn_1p8"; + startup-delay-us = <4000>; + enable-active-high; + gpio = <&pm8150_gpios 1 GPIO_ACTIVE_HIGH>; + }; + + vreg_conn_pa: regulator-conn-pa { + compatible = "regulator-fixed"; + regulator-name = "vreg_conn_pa"; + startup-delay-us = <4000>; + enable-active-high; + gpio = <&pm8150_gpios 6 GPIO_ACTIVE_HIGH>; + }; + + regulator-usb2-vbus { + compatible = "regulator-fixed"; + regulator-name = "USB2_VBUS"; + gpio = <&pm8150_gpios 10 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&usb2_en>; + pinctrl-names = "default"; + enable-active-high; + regulator-always-on; + }; + + vreg_v3p3_can: regulator-v3p3-can { + compatible = "regulator-fixed"; + regulator-name = "vreg-v3p3-can"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + vreg_v5p0_can: regulator-v5p0-can { + compatible = "regulator-fixed"; + regulator-name = "vreg-v5p0-can"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + regulator-always-on; + }; + + wcn6855-pmu { + compatible = "qcom,wcn6855-pmu"; + + pinctrl-0 = <&bt_en_state>, <&wlan_en_state>; + pinctrl-names = "default"; + + bt-enable-gpios = <&tlmm 85 GPIO_ACTIVE_HIGH>; + wlan-enable-gpios = <&tlmm 84 GPIO_ACTIVE_HIGH>; + + vddio-supply = <&vreg_conn_pa>; + vddaon-supply = <&vreg_s5a>; + vddpmu-supply = <&vreg_conn_1p8>; + vddpmumx-supply = <&vreg_conn_1p8>; + vddpmucx-supply = <&vreg_conn_pa>; + vddrfa0p95-supply = <&vreg_s5a>; + vddrfa1p3-supply = <&vreg_s6a>; + vddrfa1p9-supply = <&vreg_l15a>; + vddpcie1p3-supply = <&vreg_s6a>; + vddpcie1p9-supply = <&vreg_l15a>; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p7: ldo7 { + regulator-name = "vreg_pmu_rfa_1p7"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; +}; + +&apps_rsc { + regulators-0 { + compatible = "qcom,pm8150-rpmh-regulators"; + qcom,pmic-id = "a"; + + vreg_s3a: smps3 { + regulator-name = "vreg_s3a"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <650000>; + regulator-initial-mode = ; + }; + + vreg_s4a: smps4 { + regulator-name = "vreg_s4a"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1829000>; + regulator-initial-mode = ; + }; + + vreg_s5a: smps5 { + regulator-name = "vreg_s5a"; + regulator-min-microvolt = <1896000>; + regulator-max-microvolt = <2040000>; + regulator-initial-mode = ; + }; + + vreg_s6a: smps6 { + regulator-name = "vreg_s6a"; + regulator-min-microvolt = <1304000>; + regulator-max-microvolt = <1404000>; + regulator-initial-mode = ; + }; + + vreg_l1a: ldo1 { + regulator-name = "vreg_l1a"; + regulator-min-microvolt = <488000>; + regulator-max-microvolt = <852000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l2a: ldo2 { + regulator-name = "vreg_l2a"; + regulator-min-microvolt = <1650000>; + regulator-max-microvolt = <3100000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l3a: ldo3 { + regulator-name = "vreg_l3a"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1248000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l5a: ldo5 { + regulator-name = "vreg_l5a"; + regulator-min-microvolt = <875000>; + regulator-max-microvolt = <975000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l7a: ldo7 { + regulator-name = "vreg_l7a"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1900000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l8a: ldo8 { + regulator-name = "vreg_l8a"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1350000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l10a: ldo10 { + regulator-name = "vreg_l10a"; + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <3312000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l11a: ldo11 { + regulator-name = "vreg_l11a"; + regulator-min-microvolt = <1232000>; + regulator-max-microvolt = <1260000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l12a: ldo12 { + regulator-name = "vreg_l12a"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1890000>; + regulator-initial-mode = ; + }; + + vreg_l13a: ldo13 { + regulator-name = "vreg_l13a"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3230000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l15a: ldo15 { + regulator-name = "vreg_l15a"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1904000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l16a: ldo16 { + regulator-name = "vreg_l16a"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3312000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + }; + + vreg_l17a: ldo17 { + regulator-name = "vreg_l17a"; + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <3312000>; + regulator-initial-mode = ; + }; + }; +}; + +&gpi_dma0 { + status = "okay"; +}; + +&gpi_dma1 { + status = "okay"; +}; + +&i2c5 { + clock-frequency = <400000>; + status = "okay"; + + eeprom@57 { + compatible = "atmel,24c02"; + reg = <0x57>; + pagesize = <16>; + }; + + eeprom@5f { + compatible = "atmel,24mac602"; + reg = <0x5f>; + pagesize = <16>; + }; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dp0 { + status = "okay"; +}; + +&mdss_dp0_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000>; + remote-endpoint = <&dp0_connector_in>; +}; + +&mdss_dsi0 { + vdda-supply = <&vreg_l11a>; + status = "okay"; +}; + +&mdss_dsi0_phy { + vcca-supply = <&vreg_l5a>; + status = "okay"; +}; + +&pcie { + perst-gpios = <&tlmm 89 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 100 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&pcie_default_state>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie_phy { + vdda-phy-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l12a>; + + status = "okay"; +}; + +&pcie_port0 { + wifi@0 { + compatible = "pci17cb,1103"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + qcom,calibration-variant = "QC_QCS615_Ride"; + + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + }; +}; + +&pm8150_gpios { + usb2_en: usb2-en-state { + pins = "gpio10"; + function = "normal"; + output-enable; + power-source = <0>; + }; +}; + +&qupv3_id_0 { + status = "okay"; +}; + +&qupv3_id_1 { + status = "okay"; +}; + +&remoteproc_adsp { + firmware-name = "qcom/qcs615/adsp.mbn"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/qcs615/cdsp.mbn"; + + status = "okay"; +}; + +&sdhc_1 { + pinctrl-0 = <&sdc1_state_on>; + pinctrl-1 = <&sdc1_state_off>; + pinctrl-names = "default", "sleep"; + + bus-width = <8>; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + vmmc-supply = <&vreg_l17a>; + vqmmc-supply = <&vreg_s4a>; + + non-removable; + no-sd; + no-sdio; + + status = "okay"; +}; + +&spi6 { + status = "okay"; + + can@0 { + compatible = "microchip,mcp2515"; + reg = <0>; + clocks = <&can_osc>; + interrupts-extended = <&tlmm 87 IRQ_TYPE_LEVEL_LOW>; + spi-max-frequency = <10000000>; + vdd-supply = <&vreg_v3p3_can>; + xceiver-supply = <&vreg_v5p0_can>; + }; +}; + +&tlmm { + bt_en_state: bt-en-state { + pins = "gpio85"; + function = "gpio"; + bias-pull-down; + }; + + cam1_reset_default: cam1-reset-default-state { + pins = "gpio29"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + pcie_default_state: pcie-default-state { + clkreq-pins { + pins = "gpio90"; + function = "pcie_clk_req"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-pins { + pins = "gpio89"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + wake-pins { + pins = "gpio100"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + wifi_reg_en_pins_state: wifi-reg-en-pins-state { + pins = "gpio91"; + function = "gpio"; + drive-strength = <8>; + output-high; + bias-pull-up; + }; + + wlan_en_state: wlan-en-state { + pins = "gpio84"; + function = "gpio"; + drive-strength = <16>; + bias-pull-up; + }; +}; + +&uart0 { + status = "okay"; +}; + +&uart7 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn6855-bt"; + firmware-name = "QCA6698/hpnv21", "QCA6698/hpbtfw21.tlv"; + + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddbtcmx-supply = <&vreg_pmu_btcmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; + }; +}; + +/* + * USB0 routing and EDL mode: + * + * The USB0 controller’s HS differential pair is switched (manually) + * between the Micro-USB port for EDL/ADB and the on-board USB 3.0 hub. + * + * During EDL (Emergency Download) mode, the HS lines are explicitly + * routed to the Micro-USB port to allow the SoC to enter device mode + * for flashing. + * + * After EDL the switch is normally toggled so the HS lines stay + * connected to the hub’s Type-A downstream ports, leaving no electrical + * path to the Micro-USB connector — therefore USB0 runs host-only in + * normal runtime and device mode must not be advertised. + * + * USB0 is configured host-only in the base device tree; a separate + * device-tree overlay enables the Micro-USB peripheral configuration for + * ADB. For ADB to work during normal runtime the DIP switch SW1 must be + * manually toggled to the off position (reconnecting the HS pair to the + * Micro-USB port). + */ + +&usb_1 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_1_hsphy { + vdd-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l12a>; + vdda-phy-dpdm-supply = <&vreg_l13a>; + + status = "okay"; +}; + +&usb_2 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_2_hsphy { + vdd-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l12a>; + vdda-phy-dpdm-supply = <&vreg_l13a>; + + status = "okay"; +}; + +&usb_qmpphy { + vdda-phy-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l12a>; + + status = "okay"; +}; + +&usb_qmpphy_2 { + vdda-phy-supply = <&vreg_l11a>; + vdda-pll-supply = <&vreg_l5a>; + + status = "okay"; +}; + +&ufs_mem_hc { + reset-gpios = <&tlmm 123 GPIO_ACTIVE_LOW>; + vcc-supply = <&vreg_l17a>; + vcc-max-microamp = <600000>; + vccq2-supply = <&vreg_s4a>; + vccq2-max-microamp = <600000>; + + status = "okay"; +}; + +&ufs_mem_phy { + vdda-phy-supply = <&vreg_l5a>; + vdda-pll-supply = <&vreg_l12a>; + + status = "okay"; +}; + +&venus { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/talos-evk-usb1-peripheral.dtso b/arch/arm64/boot/dts/qcom/talos-evk-usb1-peripheral.dtso new file mode 100644 index 000000000000..7552ecf9d7ee --- /dev/null +++ b/arch/arm64/boot/dts/qcom/talos-evk-usb1-peripheral.dtso @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +/dts-v1/; +/plugin/; + +/* + * USB0 Peripheral Mode Overlay + * + * This overlay switches USB0 from host mode to peripheral mode + * by configuring the USB controller node. + * + * Hardware requirement: + * The DIP switch SW1 must be toggled to reconnect the USB0 HS + * differential pair to the Micro-USB connector instead of the + * on-board USB 3.0 hub. + * + * Without toggling SW1, there is no electrical path to the + * Micro-USB connector and device mode will not function. + */ + +&usb_1 { + dr_mode = "peripheral"; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/talos-evk.dts b/arch/arm64/boot/dts/qcom/talos-evk.dts new file mode 100644 index 000000000000..af100e22beee --- /dev/null +++ b/arch/arm64/boot/dts/qcom/talos-evk.dts @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +/dts-v1/; + +#include "talos-evk-som.dtsi" + +/ { + model = "Qualcomm QCS615 IQ 615 EVK"; + compatible = "qcom,talos-evk", "qcom,qcs615", "qcom,sm6150"; + chassis-type = "embedded"; + + aliases { + mmc1 = &sdhc_2; + }; + + dp0-connector { + compatible = "dp-connector"; + label = "DP0"; + type = "full-size"; + + hpd-gpios = <&tlmm 104 GPIO_ACTIVE_HIGH>; + + port { + dp0_connector_in: endpoint { + remote-endpoint = <&mdss_dp0_out>; + }; + }; + }; + + hdmi_connector: hdmi-out { + compatible = "hdmi-connector"; + type = "d"; + + port { + hdmi_con_out: endpoint { + remote-endpoint = <&adv7535_out>; + }; + }; + }; + + vreg_v1p8_out: regulator-v1p8-out { + compatible = "regulator-fixed"; + regulator-name = "vreg-v1p8-out"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vin-supply = <&vreg_v5p0_out>; + regulator-boot-on; + regulator-always-on; + }; + + vreg_v3p3_out: regulator-v3p3-out { + compatible = "regulator-fixed"; + regulator-name = "vreg-v3p3-out"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vreg_v5p0_out>; + regulator-boot-on; + regulator-always-on; + }; + + vreg_v5p0_out: regulator-v5p0-out { + compatible = "regulator-fixed"; + regulator-name = "vreg-v5p0-out"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + regulator-always-on; + /* Powered by system 20V rail (USBC_VBUS_IN) */ + }; +}; + +&i2c1 { + clock-frequency = <400000>; + status = "okay"; + + hdmi_bridge: bridge@3d { + compatible = "adi,adv7535"; + reg = <0x3d>; + avdd-supply = <&vreg_v1p8_out>; + dvdd-supply = <&vreg_v1p8_out>; + pvdd-supply = <&vreg_v1p8_out>; + a2vdd-supply = <&vreg_v1p8_out>; + v3p3-supply = <&vreg_v3p3_out>; + interrupts-extended = <&tlmm 26 IRQ_TYPE_LEVEL_LOW>; + adi,dsi-lanes = <4>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + adv7535_in: endpoint { + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + + adv7535_out: endpoint { + remote-endpoint = <&hdmi_con_out>; + }; + }; + }; + }; +}; + +&mdss_dsi0_out { + remote-endpoint = <&adv7535_in>; + data-lanes = <0 1 2 3>; +}; + +&pon_pwrkey { + status = "okay"; +}; + +&pon_resin { + linux,code = ; + + status = "okay"; +}; + +&sdhc_2 { + pinctrl-0 = <&sdc2_state_on>; + pinctrl-1 = <&sdc2_state_off>; + pinctrl-names = "default", "sleep"; + + bus-width = <4>; + cd-gpios = <&tlmm 99 GPIO_ACTIVE_LOW>; + + vmmc-supply = <&vreg_l10a>; + vqmmc-supply = <&vreg_s4a>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/talos.dtsi b/arch/arm64/boot/dts/qcom/talos.dtsi index 75716b4a58d6..ff5afbfce2a4 100644 --- a/arch/arm64/boot/dts/qcom/talos.dtsi +++ b/arch/arm64/boot/dts/qcom/talos.dtsi @@ -19,6 +19,7 @@ #include #include #include +#include / { interrupt-parent = <&intc>; @@ -651,6 +652,11 @@ reg = <0x0 0x95900000 0x0 0x1e00000>; no-map; }; + + pil_gpu_mem: pil-gpu@97715000 { + reg = <0x0 0x97715000 0x0 0x2000>; + no-map; + }; }; soc: soc@0 { @@ -666,6 +672,9 @@ clocks = <&rpmhcc RPMH_CXO_CLK>, <&rpmhcc RPMH_CXO_CLK_A>, <&sleep_clk>; + clock-names = "bi_tcxo", + "bi_tcxo_ao", + "sleep_clk"; #clock-cells = <1>; #reset-cells = <1>; @@ -1234,6 +1243,10 @@ compatible = "qcom,qcs615-aggre1-noc"; #interconnect-cells = <2>; qcom,bcm-voters = <&apps_bcm_voter>; + clocks = <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>, + <&gcc GCC_AGGRE_USB2_SEC_AXI_CLK>, + <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, + <&rpmhcc RPMH_IPA_CLK>; }; mmss_noc: interconnect@1740000 { @@ -1553,6 +1566,50 @@ #interrupt-cells = <2>; wakeup-parent = <&pdc>; + cam_mclk0_default: cam-mclk0-default-state { + pins = "gpio28"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + cam_mclk1_default: cam-mclk1-default-state { + pins = "gpio29"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + cam_mclk2_default: cam-mclk2-default-state { + pins = "gpio30"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + cam_mclk3_default: cam-mclk3-default-state { + pins = "gpio31"; + function = "cam_mclk"; + drive-strength = <2>; + bias-disable; + }; + + cci_i2c0_default: cci-i2c0-default-state { + /* SDA, SCL */ + pins = "gpio32", "gpio33"; + function = "cci_i2c"; + drive-strength = <2>; + bias-pull-up; + }; + + cci_i2c1_default: cci-i2c1-default-state { + /* SDA, SCL */ + pins = "gpio34", "gpio35"; + function = "cci_i2c"; + drive-strength = <2>; + bias-pull-up; + }; + qup_i2c1_data_clk: qup-i2c1-data-clk-state { pins = "gpio4", "gpio5"; function = "qup0"; @@ -1830,6 +1887,111 @@ }; }; + gpu: gpu@5000000 { + compatible = "qcom,adreno-612.0", "qcom,adreno"; + reg = <0x0 0x05000000 0x0 0x40000>, + <0x0 0x0509e000 0x0 0x1000>, + <0x0 0x05061000 0x0 0x800>; + reg-names = "kgsl_3d0_reg_memory", + "cx_mem", + "cx_dbgc"; + + clocks = <&gpucc GPU_CC_GX_GFX3D_CLK>; + clock-names = "core"; + + interrupts = ; + + interconnects = <&gem_noc MASTER_GFX3D QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "gfx-mem"; + + iommus = <&adreno_smmu 0x0 0x401>; + + operating-points-v2 = <&gpu_opp_table>; + power-domains = <&rpmhpd RPMHPD_CX>; + + qcom,gmu = <&gmu>; + + #cooling-cells = <2>; + + status = "disabled"; + + gpu_zap_shader: zap-shader { + memory-region = <&pil_gpu_mem>; + }; + + gpu_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-845000000 { + opp-hz = /bits/ 64 <845000000>; + required-opps = <&rpmhpd_opp_turbo>; + opp-peak-kBps = <7050000>; + }; + + opp-745000000 { + opp-hz = /bits/ 64 <745000000>; + required-opps = <&rpmhpd_opp_nom_l1>; + opp-peak-kBps = <6075000>; + }; + + opp-650000000 { + opp-hz = /bits/ 64 <650000000>; + required-opps = <&rpmhpd_opp_nom>; + opp-peak-kBps = <5287500>; + }; + + opp-500000000 { + opp-hz = /bits/ 64 <500000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + opp-peak-kBps = <3975000>; + }; + + opp-435000000 { + opp-hz = /bits/ 64 <435000000>; + required-opps = <&rpmhpd_opp_svs>; + opp-peak-kBps = <3000000>; + }; + }; + }; + + gmu: gmu@506a000 { + compatible = "qcom,adreno-rgmu-612.0", "qcom,adreno-rgmu"; + reg = <0x0 0x0506d000 0x0 0x2c000>; + + clocks = <&gpucc GPU_CC_CX_GMU_CLK>, + <&gpucc GPU_CC_CXO_CLK>, + <&gcc GCC_DDRSS_GPU_AXI_CLK>, + <&gcc GCC_GPU_MEMNOC_GFX_CLK>, + <&gpucc GPU_CC_HLOS1_VOTE_GPU_SMMU_CLK>; + clock-names = "gmu", + "cxo", + "axi", + "memnoc", + "smmu_vote"; + + power-domains = <&gpucc CX_GDSC>, + <&gpucc GX_GDSC>; + power-domain-names = "cx", + "gx"; + + interrupts = , + ; + interrupt-names = "oob", + "gmu"; + + operating-points-v2 = <&gmu_opp_table>; + + gmu_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-200000000 { + opp-hz = /bits/ 64 <200000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + }; + }; + gpucc: clock-controller@5090000 { compatible = "qcom,qcs615-gpucc"; reg = <0 0x05090000 0 0x9000>; @@ -1843,6 +2005,31 @@ #power-domain-cells = <1>; }; + adreno_smmu: iommu@50a0000 { + compatible = "qcom,qcs615-smmu-500", "qcom,adreno-smmu", + "qcom,smmu-500", "arm,mmu-500"; + reg = <0x0 0x050a0000 0x0 0x40000>; + #iommu-cells = <2>; + #global-interrupts = <1>; + interrupts = , + , + , + , + , + , + , + , + ; + clocks = <&gcc GCC_GPU_MEMNOC_GFX_CLK>, + <&gpucc GPU_CC_HLOS1_VOTE_GPU_SMMU_CLK>, + <&gcc GCC_GPU_SNOC_DVM_GFX_CLK>; + clock-names = "mem", + "hlos", + "iface"; + power-domains = <&gpucc CX_GDSC>; + dma-coherent; + }; + stm@6002000 { compatible = "arm,coresight-stm", "arm,primecell"; reg = <0x0 0x06002000 0x0 0x1000>, @@ -2253,6 +2440,14 @@ #address-cells = <1>; #size-cells = <0>; + port@0 { + reg = <0>; + + replicator0_out0: endpoint { + remote-endpoint = <&tmc_etr_in>; + }; + }; + port@1 { reg = <1>; @@ -2287,6 +2482,25 @@ }; }; + tmc@6048000 { + compatible = "arm,coresight-tmc", "arm,primecell"; + reg = <0x0 0x06048000 0x0 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + iommus = <&apps_smmu 0x01e0 0x0>; + arm,scatter-gather; + + in-ports { + port { + tmc_etr_in: endpoint { + remote-endpoint = <&replicator0_out0>; + }; + }; + }; + }; + replicator@604a000 { compatible = "arm,coresight-dynamic-replicator", "arm,primecell"; reg = <0x0 0x0604a000 0x0 0x1000>; @@ -3789,6 +4003,241 @@ #power-domain-cells = <1>; }; + cci: cci@ac4a000 { + compatible = "qcom,sm6150-cci", "qcom,msm8996-cci"; + + reg = <0x0 0x0ac4a000 0x0 0x4000>; + interrupts = ; + power-domains = <&camcc TITAN_TOP_GDSC>; + clocks = <&camcc CAM_CC_SOC_AHB_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CCI_CLK>; + clock-names = "camnoc_axi", + "cpas_ahb", + "cci"; + pinctrl-0 = <&cci_i2c0_default &cci_i2c1_default>; + pinctrl-names = "default"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + cci_i2c0: i2c-bus@0 { + reg = <0>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + + cci_i2c1: i2c-bus@1 { + reg = <1>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + camss: isp@acb3000 { + compatible = "qcom,sm6150-camss"; + + reg = <0x0 0x0acb3000 0x0 0x1000>, + <0x0 0x0acba000 0x0 0x1000>, + <0x0 0x0acc8000 0x0 0x1000>, + <0x0 0x0ac65000 0x0 0x1000>, + <0x0 0x0ac66000 0x0 0x1000>, + <0x0 0x0ac67000 0x0 0x1000>, + <0x0 0x0acaf000 0x0 0x4000>, + <0x0 0x0acb6000 0x0 0x4000>, + <0x0 0x0acc4000 0x0 0x4000>, + <0x0 0x0ac6f000 0x0 0x3000>, + <0x0 0x0ac42000 0x0 0x5000>, + <0x0 0x0ac48000 0x0 0x1000>, + <0x0 0x0ac40000 0x0 0x1000>, + <0x0 0x0ac18000 0x0 0x3000>, + <0x0 0x0ac00000 0x0 0x6000>, + <0x0 0x0ac10000 0x0 0x8000>, + <0x0 0x0ac87000 0x0 0x3000>, + <0x0 0x0ac52000 0x0 0x4000>, + <0x0 0x0ac4e000 0x0 0x4000>, + <0x0 0x0ac6b000 0x0 0x0a00>; + reg-names = "csid0", + "csid1", + "csid_lite", + "csiphy0", + "csiphy1", + "csiphy2", + "vfe0", + "vfe1", + "vfe_lite", + "bps", + "camnoc", + "cpas_cdm", + "cpas_top", + "icp_csr", + "icp_qgic", + "icp_sierra", + "ipe0", + "jpeg_dma", + "jpeg_enc", + "lrme"; + + clocks = <&gcc GCC_CAMERA_AHB_CLK>, + <&gcc GCC_CAMERA_HF_AXI_CLK>, + <&camcc CAM_CC_CAMNOC_AXI_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CSIPHY0_CLK>, + <&camcc CAM_CC_CSI0PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY1_CLK>, + <&camcc CAM_CC_CSI1PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY2_CLK>, + <&camcc CAM_CC_CSI2PHYTIMER_CLK>, + <&camcc CAM_CC_SOC_AHB_CLK>, + <&camcc CAM_CC_IFE_0_CLK>, + <&camcc CAM_CC_IFE_0_AXI_CLK>, + <&camcc CAM_CC_IFE_0_CPHY_RX_CLK>, + <&camcc CAM_CC_IFE_0_CSID_CLK>, + <&camcc CAM_CC_IFE_1_CLK>, + <&camcc CAM_CC_IFE_1_AXI_CLK>, + <&camcc CAM_CC_IFE_1_CPHY_RX_CLK>, + <&camcc CAM_CC_IFE_1_CSID_CLK>, + <&camcc CAM_CC_IFE_LITE_CLK>, + <&camcc CAM_CC_IFE_LITE_CPHY_RX_CLK>, + <&camcc CAM_CC_IFE_LITE_CSID_CLK>, + <&camcc CAM_CC_BPS_CLK>, + <&camcc CAM_CC_BPS_AHB_CLK>, + <&camcc CAM_CC_BPS_AXI_CLK>, + <&camcc CAM_CC_BPS_AREG_CLK>, + <&camcc CAM_CC_ICP_CLK>, + <&camcc CAM_CC_IPE_0_CLK>, + <&camcc CAM_CC_IPE_0_AHB_CLK>, + <&camcc CAM_CC_IPE_0_AREG_CLK>, + <&camcc CAM_CC_IPE_0_AXI_CLK>, + <&camcc CAM_CC_JPEG_CLK>, + <&camcc CAM_CC_LRME_CLK>; + clock-names = "gcc_ahb", + "gcc_axi_hf", + "camnoc_axi", + "cpas_ahb", + "csiphy0", + "csiphy0_timer", + "csiphy1", + "csiphy1_timer", + "csiphy2", + "csiphy2_timer", + "soc_ahb", + "vfe0", + "vfe0_axi", + "vfe0_cphy_rx", + "vfe0_csid", + "vfe1", + "vfe1_axi", + "vfe1_cphy_rx", + "vfe1_csid", + "vfe_lite", + "vfe_lite_cphy_rx", + "vfe_lite_csid", + "bps", + "bps_ahb", + "bps_axi", + "bps_areg", + "icp", + "ipe0", + "ipe0_ahb", + "ipe0_areg", + "ipe0_axi", + "jpeg", + "lrme"; + + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_CAMNOC_HF0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_HF1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "ahb", + "hf_0", + "hf_1", + "sf_mnoc"; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "csid0", + "csid1", + "csid_lite", + "csiphy0", + "csiphy1", + "csiphy2", + "vfe0", + "vfe1", + "vfe_lite", + "camnoc", + "cdm", + "icp", + "jpeg_dma", + "jpeg_enc", + "lrme"; + + iommus = <&apps_smmu 0x0820 0x40>, + <&apps_smmu 0x0840 0x00>, + <&apps_smmu 0x0860 0x40>, + <&apps_smmu 0x0c00 0x00>, + <&apps_smmu 0x0cc0 0x00>, + <&apps_smmu 0x0c80 0x00>, + <&apps_smmu 0x0ca0 0x00>, + <&apps_smmu 0x0d00 0x00>, + <&apps_smmu 0x0d20 0x00>, + <&apps_smmu 0x0d40 0x00>, + <&apps_smmu 0x0d80 0x20>, + <&apps_smmu 0x0da0 0x20>, + <&apps_smmu 0x0de2 0x00>; + + power-domains = <&camcc IFE_0_GDSC>, + <&camcc IFE_1_GDSC>, + <&camcc TITAN_TOP_GDSC>, + <&camcc BPS_GDSC>, + <&camcc IPE_0_GDSC>; + power-domain-names = "ife0", + "ife1", + "top", + "bps", + "ipe"; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + }; + + port@1 { + reg = <1>; + }; + + port@2 { + reg = <2>; + }; + }; + }; + camcc: clock-controller@ad00000 { compatible = "qcom,qcs615-camcc"; reg = <0 0x0ad00000 0 0x10000>; @@ -3958,8 +4407,8 @@ dp_opp_table: opp-table { compatible = "operating-points-v2"; - opp-160000000 { - opp-hz = /bits/ 64 <160000000>; + opp-162000000 { + opp-hz = /bits/ 64 <162000000>; required-opps = <&rpmhpd_opp_low_svs>; }; @@ -4417,7 +4866,7 @@ status = "disabled"; }; - usb_hsphy_2: phy@88e3000 { + usb_2_hsphy: phy@88e3000 { compatible = "qcom,qcs615-qusb2-phy"; reg = <0x0 0x088e3000 0x0 0x180>; @@ -4486,9 +4935,9 @@ status = "disabled"; }; - usb_1: usb@a6f8800 { - compatible = "qcom,qcs615-dwc3", "qcom,dwc3"; - reg = <0x0 0x0a6f8800 0x0 0x400>; + usb_1: usb@a600000 { + compatible = "qcom,qcs615-dwc3", "qcom,snps-dwc3"; + reg = <0x0 0x0a600000 0x0 0xfc100>; clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, <&gcc GCC_USB30_PRIM_MASTER_CLK>, @@ -4507,52 +4956,46 @@ <&gcc GCC_USB30_PRIM_MASTER_CLK>; assigned-clock-rates = <19200000>, <200000000>; - interrupts-extended = <&intc GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH 0>, + interrupts-extended = <&intc GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH 0>, + <&intc GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH 0>, <&intc GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH 0>, <&pdc 9 IRQ_TYPE_EDGE_BOTH>, <&pdc 8 IRQ_TYPE_EDGE_BOTH>, <&pdc 6 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "pwr_event", + interrupt-names = "dwc_usb3", + "pwr_event", "hs_phy_irq", "dp_hs_phy_irq", "dm_hs_phy_irq", "ss_phy_irq"; + iommus = <&apps_smmu 0x140 0x0>; + + phys = <&usb_1_hsphy>, <&usb_qmpphy>; + phy-names = "usb2-phy", "usb3-phy"; + power-domains = <&gcc USB30_PRIM_GDSC>; required-opps = <&rpmhpd_opp_nom>; resets = <&gcc GCC_USB30_PRIM_BCR>; - #address-cells = <2>; - #size-cells = <2>; - ranges; + snps,dis-u1-entry-quirk; + snps,dis-u2-entry-quirk; + snps,dis_u2_susphy_quirk; + snps,dis_u3_susphy_quirk; + snps,dis_enblslpm_quirk; + snps,has-lpm-erratum; + snps,hird-threshold = /bits/ 8 <0x10>; + snps,usb3_lpm_capable; + + wakeup-source; status = "disabled"; - - usb_1_dwc3: usb@a600000 { - compatible = "snps,dwc3"; - reg = <0x0 0x0a600000 0x0 0xcd00>; - - iommus = <&apps_smmu 0x140 0x0>; - interrupts = ; - - phys = <&usb_1_hsphy>, <&usb_qmpphy>; - phy-names = "usb2-phy", "usb3-phy"; - - snps,dis-u1-entry-quirk; - snps,dis-u2-entry-quirk; - snps,dis_u2_susphy_quirk; - snps,dis_u3_susphy_quirk; - snps,dis_enblslpm_quirk; - snps,has-lpm-erratum; - snps,hird-threshold = /bits/ 8 <0x10>; - snps,usb3_lpm_capable; - }; }; - usb_2: usb@a8f8800 { - compatible = "qcom,qcs615-dwc3", "qcom,dwc3"; - reg = <0x0 0x0a8f8800 0x0 0x400>; + usb_2: usb@a800000 { + compatible = "qcom,qcs615-dwc3", "qcom,snps-dwc3"; + reg = <0x0 0x0a800000 0x0 0xfc100>; clocks = <&gcc GCC_CFG_NOC_USB2_SEC_AXI_CLK>, <&gcc GCC_USB20_SEC_MASTER_CLK>, @@ -4571,15 +5014,22 @@ <&gcc GCC_USB20_SEC_MASTER_CLK>; assigned-clock-rates = <19200000>, <200000000>; - interrupts-extended = <&intc GIC_SPI 663 IRQ_TYPE_LEVEL_HIGH 0>, + interrupts-extended = <&intc GIC_SPI 664 IRQ_TYPE_LEVEL_HIGH 0>, + <&intc GIC_SPI 663 IRQ_TYPE_LEVEL_HIGH 0>, <&intc GIC_SPI 662 IRQ_TYPE_LEVEL_HIGH 0>, <&pdc 11 IRQ_TYPE_EDGE_BOTH>, <&pdc 10 IRQ_TYPE_EDGE_BOTH>; - interrupt-names = "pwr_event", + interrupt-names = "dwc_usb3", + "pwr_event", "hs_phy_irq", "dp_hs_phy_irq", "dm_hs_phy_irq"; + iommus = <&apps_smmu 0xe0 0x0>; + + phys = <&usb_2_hsphy>; + phy-names = "usb2-phy"; + power-domains = <&gcc USB20_SEC_GDSC>; required-opps = <&rpmhpd_opp_nom>; @@ -4587,30 +5037,16 @@ qcom,select-utmi-as-pipe-clk; - #address-cells = <2>; - #size-cells = <2>; - ranges; + snps,dis_u2_susphy_quirk; + snps,dis_u3_susphy_quirk; + snps,dis_enblslpm_quirk; + snps,has-lpm-erratum; + snps,hird-threshold = /bits/ 8 <0x10>; + + maximum-speed = "high-speed"; + wakeup-source; status = "disabled"; - - usb_2_dwc3: usb@a800000 { - compatible = "snps,dwc3"; - reg = <0x0 0x0a800000 0x0 0xcd00>; - - iommus = <&apps_smmu 0xe0 0x0>; - interrupts = ; - - phys = <&usb_hsphy_2>; - phy-names = "usb2-phy"; - - snps,dis_u2_susphy_quirk; - snps,dis_u3_susphy_quirk; - snps,dis_enblslpm_quirk; - snps,has-lpm-erratum; - snps,hird-threshold = /bits/ 8 <0x10>; - - maximum-speed = "high-speed"; - }; }; tsens0: thermal-sensor@c263000 { @@ -4714,10 +5150,10 @@ arch_timer: timer { compatible = "arm,armv8-timer"; - interrupts = , - , - , - ; + interrupts = , + , + , + ; }; thermal-zones { @@ -4833,12 +5269,25 @@ thermal-sensors = <&tsens0 9>; trips { + gpu_alert0: trip-point0 { + temperature = <105000>; + hysteresis = <5000>; + type = "passive"; + }; + gpu-critical { temperature = <115000>; hysteresis = <1000>; type = "critical"; }; }; + + cooling-maps { + map0 { + trip = <&gpu_alert0>; + cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; }; q6-hvx-thermal { diff --git a/arch/arm64/boot/dts/qcom/x1-asus-vivobook-s15.dtsi b/arch/arm64/boot/dts/qcom/x1-asus-vivobook-s15.dtsi new file mode 100644 index 000000000000..48c4ad648354 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/x1-asus-vivobook-s15.dtsi @@ -0,0 +1,1356 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2024, Xilin Wu + */ + +#include +#include +#include +#include + +#include "hamoa-pmics.dtsi" + +/ { + chassis-type = "laptop"; + + aliases { + serial1 = &uart14; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&hall_int_n_default>; + pinctrl-names = "default"; + + switch-lid { + gpios = <&tlmm 92 GPIO_ACTIVE_LOW>; + linux,input-type = ; + linux,code = ; + wakeup-source; + wakeup-event-action = ; + }; + }; + + hdmi-bridge { + compatible = "parade,ps185hdm"; + + pinctrl-0 = <&hdmi_hpd_default>; + pinctrl-names = "default"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + hdmi_bridge_dp_in: endpoint { + remote-endpoint = <&usb_1_ss2_qmpphy_out_dp>; + }; + }; + + port@1 { + reg = <1>; + + hdmi_bridge_tmds_out: endpoint { + remote-endpoint = <&hdmi_con>; + }; + }; + }; + }; + + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con: endpoint { + remote-endpoint = <&hdmi_bridge_tmds_out>; + }; + }; + }; + + pmic-glink { + compatible = "qcom,x1e80100-pmic-glink", + "qcom,sm8550-pmic-glink", + "qcom,pmic-glink"; + orientation-gpios = <&tlmm 121 GPIO_ACTIVE_HIGH>, + <&tlmm 123 GPIO_ACTIVE_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + + /* Left-side port, closer to the screen */ + connector@0 { + compatible = "usb-c-connector"; + reg = <0>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pmic_glink_ss0_hs_in: endpoint { + remote-endpoint = <&usb_1_ss0_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + pmic_glink_ss0_ss_in: endpoint { + remote-endpoint = <&retimer_ss0_ss_out>; + }; + }; + + port@2 { + reg = <2>; + + pmic_glink_ss0_con_sbu_in: endpoint { + remote-endpoint = <&retimer_ss0_con_sbu_out>; + }; + }; + }; + }; + + /* Left-side port, farther from the screen */ + connector@1 { + compatible = "usb-c-connector"; + reg = <1>; + power-role = "dual"; + data-role = "dual"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pmic_glink_ss1_hs_in: endpoint { + remote-endpoint = <&usb_1_ss1_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + pmic_glink_ss1_ss_in: endpoint { + remote-endpoint = <&retimer_ss1_ss_out>; + }; + }; + + port@2 { + reg = <2>; + + pmic_glink_ss1_con_sbu_in: endpoint { + remote-endpoint = <&retimer_ss1_con_sbu_out>; + }; + }; + }; + }; + }; + + reserved-memory { + linux,cma { + compatible = "shared-dma-pool"; + size = <0x0 0x8000000>; + reusable; + linux,cma-default; + }; + }; + + vreg_edp_3p3: regulator-edp-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_EDP_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 70 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&edp_reg_en>; + pinctrl-names = "default"; + + regulator-always-on; + regulator-boot-on; + }; + + vreg_nvme: regulator-nvme { + compatible = "regulator-fixed"; + + regulator-name = "VREG_NVME_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 18 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&nvme_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr0_1p15: regulator-rtmr0-1p15 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR0_1P15"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1150000>; + + gpio = <&pmc8380_5_gpios 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb0_pwr_1p15_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr0_1p8: regulator-rtmr0-1p8 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR0_1P8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + gpio = <&pm8550ve_9_gpios 8 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb0_1p8_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr0_3p3: regulator-rtmr0-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR0_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&pm8550_gpios 11 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb0_3p3_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr1_1p15: regulator-rtmr1-1p15 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR1_1P15"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1150000>; + + gpio = <&tlmm 188 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb1_pwr_1p15_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr1_1p8: regulator-rtmr1-1p8 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR1_1P8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + gpio = <&tlmm 175 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb1_pwr_1p8_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_rtmr1_3p3: regulator-rtmr1-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_RTMR1_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 186 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&usb1_pwr_3p3_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vph_pwr: regulator-vph-pwr { + compatible = "regulator-fixed"; + + regulator-name = "vph_pwr"; + regulator-min-microvolt = <3700000>; + regulator-max-microvolt = <3700000>; + + regulator-always-on; + regulator-boot-on; + }; + + /* + * TODO: These two regulators are actually part of the removable M.2 + * card and not the CRD mainboard. Need to describe this differently. + * Functionally it works correctly, because all we need to do is to + * turn on the actual 3.3V supply above. + */ + vreg_wcn_0p95: regulator-wcn-0p95 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_0P95"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <950000>; + + vin-supply = <&vreg_wcn_3p3>; + }; + + vreg_wcn_1p9: regulator-wcn-1p9 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_1P9"; + regulator-min-microvolt = <1900000>; + regulator-max-microvolt = <1900000>; + + vin-supply = <&vreg_wcn_3p3>; + }; + + vreg_wcn_3p3: regulator-wcn-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 214 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&wcn_sw_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + wcn7850-pmu { + compatible = "qcom,wcn7850-pmu"; + + vdd-supply = <&vreg_wcn_0p95>; + vddio-supply = <&vreg_l15b_1p8>; + vddaon-supply = <&vreg_wcn_0p95>; + vdddig-supply = <&vreg_wcn_0p95>; + vddrfa1p2-supply = <&vreg_wcn_1p9>; + vddrfa1p8-supply = <&vreg_wcn_1p9>; + + wlan-enable-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>; + bt-enable-gpios = <&tlmm 116 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&wcn_wlan_en>, <&wcn_bt_en>; + pinctrl-names = "default"; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p8: ldo7 { + regulator-name = "vreg_pmu_rfa_1p8"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; +}; + +&apps_rsc { + regulators-0 { + compatible = "qcom,pm8550-rpmh-regulators"; + qcom,pmic-id = "b"; + + vdd-bob1-supply = <&vph_pwr>; + vdd-bob2-supply = <&vph_pwr>; + vdd-l1-l4-l10-supply = <&vreg_s4c_1p8>; + vdd-l2-l13-l14-supply = <&vreg_bob1>; + vdd-l5-l16-supply = <&vreg_bob1>; + vdd-l6-l7-supply = <&vreg_bob2>; + vdd-l8-l9-supply = <&vreg_bob1>; + vdd-l12-supply = <&vreg_s5j_1p2>; + vdd-l15-supply = <&vreg_s4c_1p8>; + vdd-l17-supply = <&vreg_bob2>; + + vreg_bob1: bob1 { + regulator-name = "vreg_bob1"; + regulator-min-microvolt = <3008000>; + regulator-max-microvolt = <3960000>; + regulator-initial-mode = ; + }; + + vreg_bob2: bob2 { + regulator-name = "vreg_bob2"; + regulator-min-microvolt = <2504000>; + regulator-max-microvolt = <3008000>; + regulator-initial-mode = ; + }; + + vreg_l2b_3p0: ldo2 { + regulator-name = "vreg_l2b_3p0"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3100000>; + regulator-initial-mode = ; + }; + + vreg_l4b_1p8: ldo4 { + regulator-name = "vreg_l4b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l13b_3p0: ldo13 { + regulator-name = "vreg_l13b_3p0"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + + vreg_l14b_3p0: ldo14 { + regulator-name = "vreg_l14b_3p0"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + + vreg_l15b_1p8: ldo15 { + regulator-name = "vreg_l15b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + }; + + regulators-1 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + qcom,pmic-id = "c"; + + vdd-l1-supply = <&vreg_s5j_1p2>; + vdd-l2-supply = <&vreg_s1f_0p7>; + vdd-l3-supply = <&vreg_s1f_0p7>; + vdd-s4-supply = <&vph_pwr>; + + vreg_l3c_0p8: ldo3 { + regulator-name = "vreg_l3c_0p8"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + }; + + vreg_s4c_1p8: smps4 { + regulator-name = "vreg_s4c_1p8"; + regulator-min-microvolt = <1856000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = ; + }; + }; + + regulators-2 { + compatible = "qcom,pmc8380-rpmh-regulators"; + qcom,pmic-id = "d"; + + vdd-l1-supply = <&vreg_s1f_0p7>; + vdd-l2-supply = <&vreg_s1f_0p7>; + vdd-l3-supply = <&vreg_s4c_1p8>; + vdd-s1-supply = <&vph_pwr>; + + vreg_l1d_0p8: ldo1 { + regulator-name = "vreg_l1d_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + + vreg_l2d_0p9: ldo2 { + regulator-name = "vreg_l2d_0p9"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + + vreg_l3d_1p8: ldo3 { + regulator-name = "vreg_l3d_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + }; + + regulators-3 { + compatible = "qcom,pmc8380-rpmh-regulators"; + qcom,pmic-id = "e"; + + vdd-l2-supply = <&vreg_s1f_0p7>; + vdd-l3-supply = <&vreg_s5j_1p2>; + + vreg_l2e_0p8: ldo2 { + regulator-name = "vreg_l2e_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + + vreg_l3e_1p2: ldo3 { + regulator-name = "vreg_l3e_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + }; + + regulators-4 { + compatible = "qcom,pmc8380-rpmh-regulators"; + qcom,pmic-id = "f"; + + vdd-l1-supply = <&vreg_s5j_1p2>; + vdd-l2-supply = <&vreg_s5j_1p2>; + vdd-l3-supply = <&vreg_s5j_1p2>; + vdd-s1-supply = <&vph_pwr>; + + vreg_s1f_0p7: smps1 { + regulator-name = "vreg_s1f_0p7"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1100000>; + regulator-initial-mode = ; + }; + }; + + regulators-6 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + qcom,pmic-id = "i"; + + vdd-l1-supply = <&vreg_s4c_1p8>; + vdd-l2-supply = <&vreg_s5j_1p2>; + vdd-l3-supply = <&vreg_s1f_0p7>; + vdd-s1-supply = <&vph_pwr>; + vdd-s2-supply = <&vph_pwr>; + + vreg_l3i_0p8: ldo3 { + regulator-name = "vreg_l3i_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + }; + + regulators-7 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + qcom,pmic-id = "j"; + + vdd-l1-supply = <&vreg_s1f_0p7>; + vdd-l2-supply = <&vreg_s5j_1p2>; + vdd-l3-supply = <&vreg_s1f_0p7>; + vdd-s5-supply = <&vph_pwr>; + + vreg_s5j_1p2: smps5 { + regulator-name = "vreg_s5j_1p2"; + regulator-min-microvolt = <1256000>; + regulator-max-microvolt = <1304000>; + regulator-initial-mode = ; + }; + + vreg_l1j_0p8: ldo1 { + regulator-name = "vreg_l1j_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + + vreg_l2j_1p2: ldo2 { + regulator-name = "vreg_l2j_1p2"; + regulator-min-microvolt = <1256000>; + regulator-max-microvolt = <1256000>; + regulator-initial-mode = ; + }; + + vreg_l3j_0p8: ldo3 { + regulator-name = "vreg_l3j_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + }; +}; + +&gpu { + status = "okay"; +}; + +&i2c0 { + clock-frequency = <400000>; + + status = "okay"; + + touchpad@15 { + compatible = "hid-over-i2c"; + reg = <0x15>; + + hid-descr-addr = <0x1>; + interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-0 = <&tpad_default>; + pinctrl-names = "default"; + + wakeup-source; + }; +}; + +&i2c1 { + clock-frequency = <400000>; + + status = "okay"; +}; + +&i2c3 { + clock-frequency = <400000>; + + status = "okay"; + + typec-mux@8 { + compatible = "parade,ps8830"; + reg = <0x08>; + + clocks = <&rpmhcc RPMH_RF_CLK3>; + + vdd-supply = <&vreg_rtmr0_1p15>; + vdd33-supply = <&vreg_rtmr0_3p3>; + vdd33-cap-supply = <&vreg_rtmr0_3p3>; + vddar-supply = <&vreg_rtmr0_1p15>; + vddat-supply = <&vreg_rtmr0_1p15>; + vddio-supply = <&vreg_rtmr0_1p8>; + + reset-gpios = <&pm8550_gpios 10 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&rtmr0_default>; + pinctrl-names = "default"; + + orientation-switch; + retimer-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + retimer_ss0_ss_out: endpoint { + remote-endpoint = <&pmic_glink_ss0_ss_in>; + }; + }; + + port@1 { + reg = <1>; + + retimer_ss0_ss_in: endpoint { + remote-endpoint = <&usb_1_ss0_qmpphy_out>; + }; + }; + + port@2 { + reg = <2>; + + retimer_ss0_con_sbu_out: endpoint { + remote-endpoint = <&pmic_glink_ss0_con_sbu_in>; + }; + }; + }; + }; +}; + +&i2c5 { + clock-frequency = <400000>; + + status = "okay"; + + keyboard@3a { + compatible = "hid-over-i2c"; + reg = <0x3a>; + + hid-descr-addr = <0x1>; + interrupts-extended = <&tlmm 67 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-0 = <&kybd_default>; + pinctrl-names = "default"; + + wakeup-source; + }; + + eusb5_repeater: redriver@43 { + compatible = "nxp,ptn3222"; + reg = <0x43>; + #phy-cells = <0>; + + vdd3v3-supply = <&vreg_l13b_3p0>; + vdd1v8-supply = <&vreg_l4b_1p8>; + + reset-gpios = <&tlmm 7 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&eusb5_reset_n>; + pinctrl-names = "default"; + }; + + eusb3_repeater: redriver@47 { + compatible = "nxp,ptn3222"; + reg = <0x47>; + #phy-cells = <0>; + + vdd3v3-supply = <&vreg_l13b_3p0>; + vdd1v8-supply = <&vreg_l4b_1p8>; + + reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&eusb3_reset_n>; + pinctrl-names = "default"; + }; + + eusb6_repeater: redriver@4f { + compatible = "nxp,ptn3222"; + reg = <0x4f>; + #phy-cells = <0>; + + vdd3v3-supply = <&vreg_l13b_3p0>; + vdd1v8-supply = <&vreg_l4b_1p8>; + + reset-gpios = <&tlmm 184 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&eusb6_reset_n>; + pinctrl-names = "default"; + }; + + /* EC @ 0x76 */ +}; + +&i2c7 { + clock-frequency = <400000>; + + status = "okay"; + + typec-mux@8 { + compatible = "parade,ps8830"; + reg = <0x8>; + + clocks = <&rpmhcc RPMH_RF_CLK4>; + + vdd-supply = <&vreg_rtmr1_1p15>; + vdd33-supply = <&vreg_rtmr1_3p3>; + vdd33-cap-supply = <&vreg_rtmr1_3p3>; + vddar-supply = <&vreg_rtmr1_1p15>; + vddat-supply = <&vreg_rtmr1_1p15>; + vddio-supply = <&vreg_rtmr1_1p8>; + + reset-gpios = <&tlmm 176 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&rtmr1_default>; + pinctrl-names = "default"; + + retimer-switch; + orientation-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + retimer_ss1_ss_out: endpoint { + remote-endpoint = <&pmic_glink_ss1_ss_in>; + }; + }; + + port@1 { + reg = <1>; + + retimer_ss1_ss_in: endpoint { + remote-endpoint = <&usb_1_ss1_qmpphy_out>; + }; + }; + + port@2 { + reg = <2>; + + retimer_ss1_con_sbu_out: endpoint { + remote-endpoint = <&pmic_glink_ss1_con_sbu_in>; + }; + }; + }; + }; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dp0 { + status = "okay"; +}; + +&mdss_dp0_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp1 { + status = "okay"; +}; + +&mdss_dp1_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp2 { + status = "okay"; +}; + +&mdss_dp2_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp3 { + /delete-property/ #sound-dai-cells; + + pinctrl-0 = <&edp0_hpd_default>; + pinctrl-names = "default"; + + status = "okay"; + + aux-bus { + panel { + compatible = "samsung,atna56ac03", "samsung,atna33xc20"; + enable-gpios = <&pmc8380_3_gpios 4 GPIO_ACTIVE_HIGH>; + power-supply = <&vreg_edp_3p3>; + + pinctrl-0 = <&edp_bl_en>; + pinctrl-names = "default"; + + port { + edp_panel_in: endpoint { + remote-endpoint = <&mdss_dp3_out>; + }; + }; + }; + }; +}; + +&mdss_dp3_out { + data-lanes = <0 1 2 3>; + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; + + remote-endpoint = <&edp_panel_in>; +}; + +&mdss_dp3_phy { + vdda-phy-supply = <&vreg_l3j_0p8>; + vdda-pll-supply = <&vreg_l2j_1p2>; + + status = "okay"; +}; + +&pcie4 { + pinctrl-0 = <&pcie4_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie4_phy { + vdda-phy-supply = <&vreg_l3i_0p8>; + vdda-pll-supply = <&vreg_l3e_1p2>; + + status = "okay"; +}; + +&pcie4_port0 { + reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; + + wifi@0 { + compatible = "pci17cb,1107"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + }; +}; + +&pcie6a { + vddpe-3v3-supply = <&vreg_nvme>; + + pinctrl-0 = <&pcie6a_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie6a_phy { + vdda-phy-supply = <&vreg_l1d_0p8>; + vdda-pll-supply = <&vreg_l2j_1p2>; + + status = "okay"; +}; + +&pcie6a_port0 { + reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +}; + +&pm8550_gpios { + rtmr0_default: rtmr0-reset-n-active-state { + pins = "gpio10"; + function = "normal"; + power-source = <1>; /* 1.8V */ + bias-disable; + input-disable; + output-enable; + }; + + usb0_3p3_reg_en: usb0-3p3-reg-en-state { + pins = "gpio11"; + function = "normal"; + power-source = <1>; /* 1.8V */ + bias-disable; + input-disable; + output-enable; + }; +}; + +&pm8550ve_9_gpios { + usb0_1p8_reg_en: usb0-1p8-reg-en-state { + pins = "gpio8"; + function = "normal"; + power-source = <1>; /* 1.8V */ + bias-disable; + input-disable; + output-enable; + }; +}; + +&pmc8380_3_gpios { + edp_bl_en: edp-bl-en-state { + pins = "gpio4"; + function = "normal"; + power-source = <1>; /* 1.8 V */ + qcom,drive-strength = ; + bias-pull-down; + input-disable; + output-enable; + }; +}; + +&pmc8380_5_gpios { + usb0_pwr_1p15_reg_en: usb0-pwr-1p15-reg-en-state { + pins = "gpio8"; + function = "normal"; + power-source = <1>; /* 1.8V */ + bias-disable; + input-disable; + output-enable; + }; +}; + +&qupv3_0 { + status = "okay"; +}; + +&qupv3_1 { + status = "okay"; +}; + +&qupv3_2 { + status = "okay"; +}; + +&smb2360_0 { + status = "okay"; +}; + +&smb2360_0_eusb2_repeater { + vdd18-supply = <&vreg_l3d_1p8>; + vdd3-supply = <&vreg_l2b_3p0>; +}; + +&smb2360_1 { + status = "okay"; +}; + +&smb2360_1_eusb2_repeater { + vdd18-supply = <&vreg_l3d_1p8>; + vdd3-supply = <&vreg_l14b_3p0>; +}; + +&tlmm { + gpio-reserved-ranges = <34 2>, /* Unused */ + <44 4>, /* SPI (TPM) */ + <238 1>; /* UFS Reset */ + + edp_reg_en: edp-reg-en-state { + pins = "gpio70"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + eusb3_reset_n: eusb3-reset-n-state { + pins = "gpio6"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + output-low; + }; + + eusb5_reset_n: eusb5-reset-n-state { + pins = "gpio7"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + output-low; + }; + + eusb6_reset_n: eusb6-reset-n-state { + pins = "gpio184"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + output-low; + }; + + hall_int_n_default: hall-int-n-state { + pins = "gpio92"; + function = "gpio"; + bias-disable; + }; + + hdmi_hpd_default: hdmi-hpd-default-state { + pins = "gpio126"; + function = "usb2_dp"; + bias-disable; + }; + + kybd_default: kybd-default-state { + pins = "gpio67"; + function = "gpio"; + bias-disable; + }; + + nvme_reg_en: nvme-reg-en-state { + pins = "gpio18"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + pcie4_default: pcie4-default-state { + clkreq-n-pins { + pins = "gpio147"; + function = "pcie4_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio146"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio148"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie6a_default: pcie6a-default-state { + clkreq-n-pins { + pins = "gpio153"; + function = "pcie6a_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio152"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio154"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + rtmr1_default: rtmr1-reset-n-active-state { + pins = "gpio176"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + tpad_default: tpad-default-state { + pins = "gpio3"; + function = "gpio"; + bias-disable; + }; + + usb1_pwr_1p15_reg_en: usb1-pwr-1p15-reg-en-state { + pins = "gpio188"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + usb1_pwr_1p8_reg_en: usb1-pwr-1p8-reg-en-state { + pins = "gpio175"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + usb1_pwr_3p3_reg_en: usb1-pwr-3p3-reg-en-state { + pins = "gpio186"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + + wcn_bt_en: wcn-bt-en-state { + pins = "gpio116"; + function = "gpio"; + drive-strength = <16>; + bias-pull-down; + }; + + wcn_sw_en: wcn-sw-en-state { + pins = "gpio214"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + wcn_wlan_en: wcn-wlan-en-state { + pins = "gpio117"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; +}; + +&uart14 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn7850-bt"; + max-speed = <3200000>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + }; +}; + +&usb_1_ss0_hsphy { + vdd-supply = <&vreg_l3j_0p8>; + vdda12-supply = <&vreg_l2j_1p2>; + + phys = <&smb2360_0_eusb2_repeater>; + + status = "okay"; +}; + +&usb_1_ss0_qmpphy { + vdda-phy-supply = <&vreg_l2j_1p2>; + vdda-pll-supply = <&vreg_l1j_0p8>; + + status = "okay"; +}; + +&usb_1_ss0 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_1_ss0_dwc3_hs { + remote-endpoint = <&pmic_glink_ss0_hs_in>; +}; + +&usb_1_ss0_qmpphy_out { + remote-endpoint = <&retimer_ss0_ss_in>; +}; + +&usb_1_ss1_hsphy { + vdd-supply = <&vreg_l3j_0p8>; + vdda12-supply = <&vreg_l2j_1p2>; + + phys = <&smb2360_1_eusb2_repeater>; + + status = "okay"; +}; + +&usb_1_ss1_qmpphy { + vdda-phy-supply = <&vreg_l2j_1p2>; + vdda-pll-supply = <&vreg_l2d_0p9>; + + status = "okay"; +}; + +&usb_1_ss1 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_1_ss1_dwc3_hs { + remote-endpoint = <&pmic_glink_ss1_hs_in>; +}; + +&usb_1_ss1_qmpphy_out { + remote-endpoint = <&retimer_ss1_ss_in>; +}; + +&usb_1_ss2_qmpphy { + vdda-phy-supply = <&vreg_l2j_1p2>; + vdda-pll-supply = <&vreg_l2d_0p9>; + + /delete-property/ mode-switch; + /delete-property/ orientation-switch; + + status = "okay"; + + ports { + port@0 { + #address-cells = <1>; + #size-cells = <0>; + + /delete-node/ endpoint; + + usb_1_ss2_qmpphy_out_dp: endpoint@0 { + reg = <0>; + + data-lanes = <3 2 1 0>; + remote-endpoint = <&hdmi_bridge_dp_in>; + }; + + /* No USB3 lanes connected */ + }; + }; +}; + +&usb_2 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_2_hsphy { + vdd-supply = <&vreg_l2e_0p8>; + vdda12-supply = <&vreg_l3e_1p2>; + + phys = <&eusb5_repeater>; + + status = "okay"; +}; + +&usb_mp { + status = "okay"; +}; + +&usb_mp_hsphy0 { + vdd-supply = <&vreg_l2e_0p8>; + vdda12-supply = <&vreg_l3e_1p2>; + + phys = <&eusb3_repeater>; + + status = "okay"; +}; + +&usb_mp_hsphy1 { + vdd-supply = <&vreg_l2e_0p8>; + vdda12-supply = <&vreg_l3e_1p2>; + + phys = <&eusb6_repeater>; + + status = "okay"; +}; + +&usb_mp_qmpphy0 { + vdda-phy-supply = <&vreg_l3e_1p2>; + vdda-pll-supply = <&vreg_l3c_0p8>; + + status = "okay"; +}; + +&usb_mp_qmpphy1 { + vdda-phy-supply = <&vreg_l3e_1p2>; + vdda-pll-supply = <&vreg_l3c_0p8>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi b/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi index 0a382cc9e643..66d566808f58 100644 --- a/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi @@ -63,6 +63,45 @@ }; }; + hdmi-bridge { + compatible = "parade,ps185hdm"; + + pinctrl-0 = <&hdmi_hpd_default>; + pinctrl-names = "default"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + hdmi_bridge_dp_in: endpoint { + remote-endpoint = <&usb_1_ss2_qmpphy_out_dp>; + }; + }; + + port@1 { + reg = <1>; + + hdmi_bridge_tmds_out: endpoint { + remote-endpoint = <&hdmi_con>; + }; + }; + }; + }; + + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con: endpoint { + remote-endpoint = <&hdmi_bridge_tmds_out>; + }; + }; + }; + leds { compatible = "gpio-leds"; @@ -995,6 +1034,14 @@ link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; }; +&mdss_dp2 { + status = "okay"; +}; + +&mdss_dp2_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + &mdss_dp3 { /delete-property/ #sound-dai-cells; @@ -1257,6 +1304,12 @@ bias-disable; }; + hdmi_hpd_default: hdmi-hpd-default-state { + pins = "gpio126"; + function = "usb2_dp"; + bias-disable; + }; + hdtl_default: hdtl-default-state { pins = "gpio95"; function = "gpio"; @@ -1408,11 +1461,9 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1440,11 +1491,9 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss1_dwc3_hs { @@ -1455,6 +1504,32 @@ remote-endpoint = <&retimer_ss1_ss_in>; }; +&usb_1_ss2_qmpphy { + vdda-phy-supply = <&vreg_l2j_1p2>; + vdda-pll-supply = <&vreg_l2d_0p9>; + + /delete-property/ mode-switch; + /delete-property/ orientation-switch; + + status = "okay"; + + ports { + port@0 { + #address-cells = <1>; + #size-cells = <0>; + + /delete-node/ endpoint; + + usb_1_ss2_qmpphy_out_dp: endpoint@0 { + reg = <0>; + + data-lanes = <3 2 1 0>; + remote-endpoint = <&hdmi_bridge_dp_in>; + }; + }; + }; +}; + &usb_mp { status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/x1-crd.dtsi b/arch/arm64/boot/dts/qcom/x1-crd.dtsi index 2fbf9ec66fb8..a9c5c523575e 100644 --- a/arch/arm64/boot/dts/qcom/x1-crd.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-crd.dtsi @@ -226,6 +226,38 @@ "VA DMIC3", "MIC BIAS1", "TX SWR_INPUT1", "ADC2_OUTPUT"; + displayport-0-dai-link { + link-name = "DisplayPort0 Playback"; + + codec { + sound-dai = <&mdss_dp0>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + displayport-1-dai-link { + link-name = "DisplayPort1 Playback"; + + codec { + sound-dai = <&mdss_dp1>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_1>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + wcd-playback-dai-link { link-name = "WCD Playback"; @@ -1709,11 +1741,9 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1741,11 +1771,9 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss1_dwc3_hs { @@ -1773,11 +1801,9 @@ }; &usb_1_ss2 { - status = "okay"; -}; - -&usb_1_ss2_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss2_dwc3_hs { diff --git a/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi b/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi index 217ca8c7d81d..0d9a324cc6cc 100644 --- a/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi @@ -1554,11 +1554,9 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1586,11 +1584,9 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss1_dwc3_hs { @@ -1618,11 +1614,9 @@ }; &usb_2 { - status = "okay"; -}; - -&usb_2_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_2_hsphy { diff --git a/arch/arm64/boot/dts/qcom/x1-el2.dtso b/arch/arm64/boot/dts/qcom/x1-el2.dtso index 175679be01eb..ee006742d6f3 100644 --- a/arch/arm64/boot/dts/qcom/x1-el2.dtso +++ b/arch/arm64/boot/dts/qcom/x1-el2.dtso @@ -52,6 +52,14 @@ status = "okay"; }; +&remoteproc_adsp { + iommus = <&apps_smmu 0x1000 0x80>; +}; + +&remoteproc_cdsp { + iommus = <&apps_smmu 0x0c00 0x0>; +}; + /* * The "SBSA watchdog" is implemented in software in Gunyah * and can't be used when running in EL2. diff --git a/arch/arm64/boot/dts/qcom/x1-hp-omnibook-x14.dtsi b/arch/arm64/boot/dts/qcom/x1-hp-omnibook-x14.dtsi index 41063948c583..b773a4976d1b 100644 --- a/arch/arm64/boot/dts/qcom/x1-hp-omnibook-x14.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-hp-omnibook-x14.dtsi @@ -1473,11 +1473,9 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1505,11 +1503,9 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss1_dwc3_hs { @@ -1521,12 +1517,10 @@ }; &usb_mp { - status = "okay"; -}; - -&usb_mp_dwc3 { phys = <&usb_mp_hsphy0>, <&usb_mp_qmpphy0>; phy-names = "usb2-0", "usb3-0"; + + status = "okay"; }; &usb_mp_hsphy0 { diff --git a/arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi b/arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi index ba6b7b5a9191..7559557610ed 100644 --- a/arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi @@ -718,11 +718,10 @@ &gpu { status = "okay"; +}; - zap-shader { - memory-region = <&gpu_microcode_mem>; - firmware-name = "qcom/x1e80100/microsoft/qcdxkmsuc8380.mbn"; - }; +&gpu_zap_shader { + firmware-name = "qcom/x1e80100/microsoft/qcdxkmsuc8380.mbn"; }; &i2c0 { @@ -1276,11 +1275,9 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1308,11 +1305,9 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss1_dwc3_hs { diff --git a/arch/arm64/boot/dts/qcom/x1e001de-devkit.dts b/arch/arm64/boot/dts/qcom/x1e001de-devkit.dts index d5a60671a383..2e38402e2c14 100644 --- a/arch/arm64/boot/dts/qcom/x1e001de-devkit.dts +++ b/arch/arm64/boot/dts/qcom/x1e001de-devkit.dts @@ -1389,12 +1389,10 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "otg"; usb-role-switch; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1422,11 +1420,9 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss1_dwc3_hs { @@ -1454,11 +1450,9 @@ }; &usb_1_ss2 { - status = "okay"; -}; - -&usb_1_ss2_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss2_dwc3_hs { diff --git a/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi b/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi index 4d7fd51f370b..5d49df41be02 100644 --- a/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi +++ b/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi @@ -1608,11 +1608,9 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1640,11 +1638,9 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss1_dwc3_hs { @@ -1684,11 +1680,9 @@ }; &usb_2 { - status = "okay"; -}; - -&usb_2_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_2_hsphy { diff --git a/arch/arm64/boot/dts/qcom/x1e80100-asus-vivobook-s15.dts b/arch/arm64/boot/dts/qcom/x1e80100-asus-vivobook-s15.dts index 17269eb0638a..519bcbc98985 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-asus-vivobook-s15.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-asus-vivobook-s15.dts @@ -6,1024 +6,22 @@ /dts-v1/; -#include -#include -#include -#include - #include "hamoa.dtsi" -#include "hamoa-pmics.dtsi" +#include "x1-asus-vivobook-s15.dtsi" / { model = "ASUS Vivobook S 15"; compatible = "asus,vivobook-s15", "qcom,x1e80100"; chassis-type = "laptop"; - - aliases { - serial1 = &uart14; - }; - - gpio-keys { - compatible = "gpio-keys"; - pinctrl-0 = <&hall_int_n_default>; - pinctrl-names = "default"; - - switch-lid { - gpios = <&tlmm 92 GPIO_ACTIVE_LOW>; - linux,input-type = ; - linux,code = ; - wakeup-source; - wakeup-event-action = ; - }; - }; - - hdmi-bridge { - compatible = "parade,ps185hdm"; - - pinctrl-0 = <&hdmi_hpd_default>; - pinctrl-names = "default"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - hdmi_bridge_dp_in: endpoint { - remote-endpoint = <&usb_1_ss2_qmpphy_out_dp>; - }; - }; - - port@1 { - reg = <1>; - - hdmi_bridge_tmds_out: endpoint { - remote-endpoint = <&hdmi_con>; - }; - }; - }; - }; - - hdmi-connector { - compatible = "hdmi-connector"; - type = "a"; - - port { - hdmi_con: endpoint { - remote-endpoint = <&hdmi_bridge_tmds_out>; - }; - }; - }; - - pmic-glink { - compatible = "qcom,x1e80100-pmic-glink", - "qcom,sm8550-pmic-glink", - "qcom,pmic-glink"; - orientation-gpios = <&tlmm 121 GPIO_ACTIVE_HIGH>, - <&tlmm 123 GPIO_ACTIVE_HIGH>; - #address-cells = <1>; - #size-cells = <0>; - - /* Left-side port, closer to the screen */ - connector@0 { - compatible = "usb-c-connector"; - reg = <0>; - power-role = "dual"; - data-role = "dual"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - pmic_glink_ss0_hs_in: endpoint { - remote-endpoint = <&usb_1_ss0_dwc3_hs>; - }; - }; - - port@1 { - reg = <1>; - - pmic_glink_ss0_ss_in: endpoint { - remote-endpoint = <&retimer_ss0_ss_out>; - }; - }; - - port@2 { - reg = <2>; - - pmic_glink_ss0_con_sbu_in: endpoint { - remote-endpoint = <&retimer_ss0_con_sbu_out>; - }; - }; - }; - }; - - /* Left-side port, farther from the screen */ - connector@1 { - compatible = "usb-c-connector"; - reg = <1>; - power-role = "dual"; - data-role = "dual"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - pmic_glink_ss1_hs_in: endpoint { - remote-endpoint = <&usb_1_ss1_dwc3_hs>; - }; - }; - - port@1 { - reg = <1>; - - pmic_glink_ss1_ss_in: endpoint { - remote-endpoint = <&retimer_ss1_ss_out>; - }; - }; - - port@2 { - reg = <2>; - - pmic_glink_ss1_con_sbu_in: endpoint { - remote-endpoint = <&retimer_ss1_con_sbu_out>; - }; - }; - }; - }; - }; - - reserved-memory { - linux,cma { - compatible = "shared-dma-pool"; - size = <0x0 0x8000000>; - reusable; - linux,cma-default; - }; - }; - - vreg_edp_3p3: regulator-edp-3p3 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_EDP_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&tlmm 70 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&edp_reg_en>; - pinctrl-names = "default"; - - regulator-always-on; - regulator-boot-on; - }; - - vreg_nvme: regulator-nvme { - compatible = "regulator-fixed"; - - regulator-name = "VREG_NVME_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&tlmm 18 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&nvme_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - }; - - vreg_rtmr0_1p15: regulator-rtmr0-1p15 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_RTMR0_1P15"; - regulator-min-microvolt = <1150000>; - regulator-max-microvolt = <1150000>; - - gpio = <&pmc8380_5_gpios 8 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&usb0_pwr_1p15_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - }; - - vreg_rtmr0_1p8: regulator-rtmr0-1p8 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_RTMR0_1P8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - - gpio = <&pm8550ve_9_gpios 8 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&usb0_1p8_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - }; - - vreg_rtmr0_3p3: regulator-rtmr0-3p3 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_RTMR0_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&pm8550_gpios 11 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&usb0_3p3_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - }; - - vreg_rtmr1_1p15: regulator-rtmr1-1p15 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_RTMR1_1P15"; - regulator-min-microvolt = <1150000>; - regulator-max-microvolt = <1150000>; - - gpio = <&tlmm 188 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&usb1_pwr_1p15_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - }; - - vreg_rtmr1_1p8: regulator-rtmr1-1p8 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_RTMR1_1P8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - - gpio = <&tlmm 175 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&usb1_pwr_1p8_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - }; - - vreg_rtmr1_3p3: regulator-rtmr1-3p3 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_RTMR1_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&tlmm 186 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&usb1_pwr_3p3_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - }; - - vph_pwr: regulator-vph-pwr { - compatible = "regulator-fixed"; - - regulator-name = "vph_pwr"; - regulator-min-microvolt = <3700000>; - regulator-max-microvolt = <3700000>; - - regulator-always-on; - regulator-boot-on; - }; - - /* - * TODO: These two regulators are actually part of the removable M.2 - * card and not the CRD mainboard. Need to describe this differently. - * Functionally it works correctly, because all we need to do is to - * turn on the actual 3.3V supply above. - */ - vreg_wcn_0p95: regulator-wcn-0p95 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_WCN_0P95"; - regulator-min-microvolt = <950000>; - regulator-max-microvolt = <950000>; - - vin-supply = <&vreg_wcn_3p3>; - }; - - vreg_wcn_1p9: regulator-wcn-1p9 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_WCN_1P9"; - regulator-min-microvolt = <1900000>; - regulator-max-microvolt = <1900000>; - - vin-supply = <&vreg_wcn_3p3>; - }; - - vreg_wcn_3p3: regulator-wcn-3p3 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_WCN_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&tlmm 214 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&wcn_sw_en>; - pinctrl-names = "default"; - - regulator-boot-on; - }; - - wcn7850-pmu { - compatible = "qcom,wcn7850-pmu"; - - vdd-supply = <&vreg_wcn_0p95>; - vddio-supply = <&vreg_l15b_1p8>; - vddaon-supply = <&vreg_wcn_0p95>; - vdddig-supply = <&vreg_wcn_0p95>; - vddrfa1p2-supply = <&vreg_wcn_1p9>; - vddrfa1p8-supply = <&vreg_wcn_1p9>; - - wlan-enable-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>; - bt-enable-gpios = <&tlmm 116 GPIO_ACTIVE_HIGH>; - - pinctrl-0 = <&wcn_wlan_en>, <&wcn_bt_en>; - pinctrl-names = "default"; - - regulators { - vreg_pmu_rfa_cmn: ldo0 { - regulator-name = "vreg_pmu_rfa_cmn"; - }; - - vreg_pmu_aon_0p59: ldo1 { - regulator-name = "vreg_pmu_aon_0p59"; - }; - - vreg_pmu_wlcx_0p8: ldo2 { - regulator-name = "vreg_pmu_wlcx_0p8"; - }; - - vreg_pmu_wlmx_0p85: ldo3 { - regulator-name = "vreg_pmu_wlmx_0p85"; - }; - - vreg_pmu_btcmx_0p85: ldo4 { - regulator-name = "vreg_pmu_btcmx_0p85"; - }; - - vreg_pmu_rfa_0p8: ldo5 { - regulator-name = "vreg_pmu_rfa_0p8"; - }; - - vreg_pmu_rfa_1p2: ldo6 { - regulator-name = "vreg_pmu_rfa_1p2"; - }; - - vreg_pmu_rfa_1p8: ldo7 { - regulator-name = "vreg_pmu_rfa_1p8"; - }; - - vreg_pmu_pcie_0p9: ldo8 { - regulator-name = "vreg_pmu_pcie_0p9"; - }; - - vreg_pmu_pcie_1p8: ldo9 { - regulator-name = "vreg_pmu_pcie_1p8"; - }; - }; - }; -}; - -&apps_rsc { - regulators-0 { - compatible = "qcom,pm8550-rpmh-regulators"; - qcom,pmic-id = "b"; - - vdd-bob1-supply = <&vph_pwr>; - vdd-bob2-supply = <&vph_pwr>; - vdd-l1-l4-l10-supply = <&vreg_s4c_1p8>; - vdd-l2-l13-l14-supply = <&vreg_bob1>; - vdd-l5-l16-supply = <&vreg_bob1>; - vdd-l6-l7-supply = <&vreg_bob2>; - vdd-l8-l9-supply = <&vreg_bob1>; - vdd-l12-supply = <&vreg_s5j_1p2>; - vdd-l15-supply = <&vreg_s4c_1p8>; - vdd-l17-supply = <&vreg_bob2>; - - vreg_bob1: bob1 { - regulator-name = "vreg_bob1"; - regulator-min-microvolt = <3008000>; - regulator-max-microvolt = <3960000>; - regulator-initial-mode = ; - }; - - vreg_bob2: bob2 { - regulator-name = "vreg_bob2"; - regulator-min-microvolt = <2504000>; - regulator-max-microvolt = <3008000>; - regulator-initial-mode = ; - }; - - vreg_l2b_3p0: ldo2 { - regulator-name = "vreg_l2b_3p0"; - regulator-min-microvolt = <3072000>; - regulator-max-microvolt = <3100000>; - regulator-initial-mode = ; - }; - - vreg_l4b_1p8: ldo4 { - regulator-name = "vreg_l4b_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - }; - - vreg_l13b_3p0: ldo13 { - regulator-name = "vreg_l13b_3p0"; - regulator-min-microvolt = <3072000>; - regulator-max-microvolt = <3072000>; - regulator-initial-mode = ; - }; - - vreg_l14b_3p0: ldo14 { - regulator-name = "vreg_l14b_3p0"; - regulator-min-microvolt = <3072000>; - regulator-max-microvolt = <3072000>; - regulator-initial-mode = ; - }; - - vreg_l15b_1p8: ldo15 { - regulator-name = "vreg_l15b_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - }; - }; - - regulators-1 { - compatible = "qcom,pm8550ve-rpmh-regulators"; - qcom,pmic-id = "c"; - - vdd-l1-supply = <&vreg_s5j_1p2>; - vdd-l2-supply = <&vreg_s1f_0p7>; - vdd-l3-supply = <&vreg_s1f_0p7>; - vdd-s4-supply = <&vph_pwr>; - - vreg_l3c_0p8: ldo3 { - regulator-name = "vreg_l3c_0p8"; - regulator-min-microvolt = <912000>; - regulator-max-microvolt = <912000>; - regulator-initial-mode = ; - }; - - vreg_s4c_1p8: smps4 { - regulator-name = "vreg_s4c_1p8"; - regulator-min-microvolt = <1856000>; - regulator-max-microvolt = <2000000>; - regulator-initial-mode = ; - }; - }; - - regulators-2 { - compatible = "qcom,pmc8380-rpmh-regulators"; - qcom,pmic-id = "d"; - - vdd-l1-supply = <&vreg_s1f_0p7>; - vdd-l2-supply = <&vreg_s1f_0p7>; - vdd-l3-supply = <&vreg_s4c_1p8>; - vdd-s1-supply = <&vph_pwr>; - - vreg_l1d_0p8: ldo1 { - regulator-name = "vreg_l1d_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <920000>; - regulator-initial-mode = ; - }; - - vreg_l2d_0p9: ldo2 { - regulator-name = "vreg_l2d_0p9"; - regulator-min-microvolt = <912000>; - regulator-max-microvolt = <920000>; - regulator-initial-mode = ; - }; - - vreg_l3d_1p8: ldo3 { - regulator-name = "vreg_l3d_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - }; - }; - - regulators-3 { - compatible = "qcom,pmc8380-rpmh-regulators"; - qcom,pmic-id = "e"; - - vdd-l2-supply = <&vreg_s1f_0p7>; - vdd-l3-supply = <&vreg_s5j_1p2>; - - vreg_l2e_0p8: ldo2 { - regulator-name = "vreg_l2e_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <920000>; - regulator-initial-mode = ; - }; - - vreg_l3e_1p2: ldo3 { - regulator-name = "vreg_l3e_1p2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-initial-mode = ; - }; - }; - - regulators-4 { - compatible = "qcom,pmc8380-rpmh-regulators"; - qcom,pmic-id = "f"; - - vdd-l1-supply = <&vreg_s5j_1p2>; - vdd-l2-supply = <&vreg_s5j_1p2>; - vdd-l3-supply = <&vreg_s5j_1p2>; - vdd-s1-supply = <&vph_pwr>; - - vreg_s1f_0p7: smps1 { - regulator-name = "vreg_s1f_0p7"; - regulator-min-microvolt = <700000>; - regulator-max-microvolt = <1100000>; - regulator-initial-mode = ; - }; - }; - - regulators-6 { - compatible = "qcom,pm8550ve-rpmh-regulators"; - qcom,pmic-id = "i"; - - vdd-l1-supply = <&vreg_s4c_1p8>; - vdd-l2-supply = <&vreg_s5j_1p2>; - vdd-l3-supply = <&vreg_s1f_0p7>; - vdd-s1-supply = <&vph_pwr>; - vdd-s2-supply = <&vph_pwr>; - - vreg_l3i_0p8: ldo3 { - regulator-name = "vreg_l3i_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <920000>; - regulator-initial-mode = ; - }; - }; - - regulators-7 { - compatible = "qcom,pm8550ve-rpmh-regulators"; - qcom,pmic-id = "j"; - - vdd-l1-supply = <&vreg_s1f_0p7>; - vdd-l2-supply = <&vreg_s5j_1p2>; - vdd-l3-supply = <&vreg_s1f_0p7>; - vdd-s5-supply = <&vph_pwr>; - - vreg_s5j_1p2: smps5 { - regulator-name = "vreg_s5j_1p2"; - regulator-min-microvolt = <1256000>; - regulator-max-microvolt = <1304000>; - regulator-initial-mode = ; - }; - - vreg_l1j_0p8: ldo1 { - regulator-name = "vreg_l1j_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <920000>; - regulator-initial-mode = ; - }; - - vreg_l2j_1p2: ldo2 { - regulator-name = "vreg_l2j_1p2"; - regulator-min-microvolt = <1256000>; - regulator-max-microvolt = <1256000>; - regulator-initial-mode = ; - }; - - vreg_l3j_0p8: ldo3 { - regulator-name = "vreg_l3j_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <920000>; - regulator-initial-mode = ; - }; - }; -}; - -&gpu { - status = "okay"; }; &gpu_zap_shader { firmware-name = "qcom/x1e80100/ASUSTeK/vivobook-s15/qcdxkmsuc8380.mbn"; }; -&i2c0 { - clock-frequency = <400000>; - status = "okay"; - - touchpad@15 { - compatible = "hid-over-i2c"; - reg = <0x15>; - - hid-descr-addr = <0x1>; - interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; - - pinctrl-0 = <&tpad_default>; - pinctrl-names = "default"; - - wakeup-source; - }; -}; - -&i2c1 { - clock-frequency = <400000>; - status = "okay"; -}; - -&i2c3 { - clock-frequency = <400000>; - status = "okay"; - - typec-mux@8 { - compatible = "parade,ps8830"; - reg = <0x08>; - - clocks = <&rpmhcc RPMH_RF_CLK3>; - - vdd-supply = <&vreg_rtmr0_1p15>; - vdd33-supply = <&vreg_rtmr0_3p3>; - vdd33-cap-supply = <&vreg_rtmr0_3p3>; - vddar-supply = <&vreg_rtmr0_1p15>; - vddat-supply = <&vreg_rtmr0_1p15>; - vddio-supply = <&vreg_rtmr0_1p8>; - - reset-gpios = <&pm8550_gpios 10 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&rtmr0_default>; - pinctrl-names = "default"; - - orientation-switch; - retimer-switch; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - retimer_ss0_ss_out: endpoint { - remote-endpoint = <&pmic_glink_ss0_ss_in>; - }; - }; - - port@1 { - reg = <1>; - - retimer_ss0_ss_in: endpoint { - remote-endpoint = <&usb_1_ss0_qmpphy_out>; - }; - }; - - port@2 { - reg = <2>; - - retimer_ss0_con_sbu_out: endpoint { - remote-endpoint = <&pmic_glink_ss0_con_sbu_in>; - }; - }; - }; - }; -}; - -&i2c5 { - clock-frequency = <400000>; - status = "okay"; - - keyboard@3a { - compatible = "hid-over-i2c"; - reg = <0x3a>; - - hid-descr-addr = <0x1>; - interrupts-extended = <&tlmm 67 IRQ_TYPE_LEVEL_LOW>; - - pinctrl-0 = <&kybd_default>; - pinctrl-names = "default"; - - wakeup-source; - }; - - eusb5_repeater: redriver@43 { - compatible = "nxp,ptn3222"; - reg = <0x43>; - #phy-cells = <0>; - - vdd3v3-supply = <&vreg_l13b_3p0>; - vdd1v8-supply = <&vreg_l4b_1p8>; - - reset-gpios = <&tlmm 7 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&eusb5_reset_n>; - pinctrl-names = "default"; - }; - - eusb3_repeater: redriver@47 { - compatible = "nxp,ptn3222"; - reg = <0x47>; - #phy-cells = <0>; - - vdd3v3-supply = <&vreg_l13b_3p0>; - vdd1v8-supply = <&vreg_l4b_1p8>; - - reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&eusb3_reset_n>; - pinctrl-names = "default"; - }; - - eusb6_repeater: redriver@4f { - compatible = "nxp,ptn3222"; - reg = <0x4f>; - #phy-cells = <0>; - - vdd3v3-supply = <&vreg_l13b_3p0>; - vdd1v8-supply = <&vreg_l4b_1p8>; - - reset-gpios = <&tlmm 184 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&eusb6_reset_n>; - pinctrl-names = "default"; - }; - - /* EC @ 0x76 */ -}; - -&i2c7 { - clock-frequency = <400000>; - status = "okay"; - - typec-mux@8 { - compatible = "parade,ps8830"; - reg = <0x8>; - - clocks = <&rpmhcc RPMH_RF_CLK4>; - - vdd-supply = <&vreg_rtmr1_1p15>; - vdd33-supply = <&vreg_rtmr1_3p3>; - vdd33-cap-supply = <&vreg_rtmr1_3p3>; - vddar-supply = <&vreg_rtmr1_1p15>; - vddat-supply = <&vreg_rtmr1_1p15>; - vddio-supply = <&vreg_rtmr1_1p8>; - - reset-gpios = <&tlmm 176 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&rtmr1_default>; - pinctrl-names = "default"; - - retimer-switch; - orientation-switch; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - retimer_ss1_ss_out: endpoint { - remote-endpoint = <&pmic_glink_ss1_ss_in>; - }; - }; - - port@1 { - reg = <1>; - - retimer_ss1_ss_in: endpoint { - remote-endpoint = <&usb_1_ss1_qmpphy_out>; - }; - }; - - port@2 { - reg = <2>; - - retimer_ss1_con_sbu_out: endpoint { - remote-endpoint = <&pmic_glink_ss1_con_sbu_in>; - }; - }; - }; - }; -}; - &iris { firmware-name = "qcom/x1e80100/ASUSTeK/vivobook-s15/qcvss8380.mbn"; - status = "okay"; -}; -&mdss { - status = "okay"; -}; - -&mdss_dp0 { - status = "okay"; -}; - -&mdss_dp0_out { - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; -}; - -&mdss_dp1 { - status = "okay"; -}; - -&mdss_dp1_out { - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; -}; - -&mdss_dp2 { - status = "okay"; -}; - -&mdss_dp2_out { - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; -}; - -&mdss_dp3 { - /delete-property/ #sound-dai-cells; - - pinctrl-0 = <&edp0_hpd_default>; - pinctrl-names = "default"; - - status = "okay"; - - aux-bus { - panel { - compatible = "samsung,atna56ac03", "samsung,atna33xc20"; - enable-gpios = <&pmc8380_3_gpios 4 GPIO_ACTIVE_HIGH>; - power-supply = <&vreg_edp_3p3>; - - pinctrl-0 = <&edp_bl_en>; - pinctrl-names = "default"; - - port { - edp_panel_in: endpoint { - remote-endpoint = <&mdss_dp3_out>; - }; - }; - }; - }; -}; - -&mdss_dp3_out { - data-lanes = <0 1 2 3>; - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; - - remote-endpoint = <&edp_panel_in>; -}; - -&mdss_dp3_phy { - vdda-phy-supply = <&vreg_l3j_0p8>; - vdda-pll-supply = <&vreg_l2j_1p2>; - - status = "okay"; -}; - -&pcie4 { - pinctrl-0 = <&pcie4_default>; - pinctrl-names = "default"; - - status = "okay"; -}; - -&pcie4_phy { - vdda-phy-supply = <&vreg_l3i_0p8>; - vdda-pll-supply = <&vreg_l3e_1p2>; - - status = "okay"; -}; - -&pcie4_port0 { - reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; - - wifi@0 { - compatible = "pci17cb,1107"; - reg = <0x10000 0x0 0x0 0x0 0x0>; - - vddaon-supply = <&vreg_pmu_aon_0p59>; - vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; - vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; - vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; - vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; - vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; - vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; - vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; - vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; - }; -}; - -&pcie6a { - vddpe-3v3-supply = <&vreg_nvme>; - - pinctrl-0 = <&pcie6a_default>; - pinctrl-names = "default"; - - status = "okay"; -}; - -&pcie6a_phy { - vdda-phy-supply = <&vreg_l1d_0p8>; - vdda-pll-supply = <&vreg_l2j_1p2>; - - status = "okay"; -}; - -&pcie6a_port0 { - reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; -}; - -&pm8550_gpios { - rtmr0_default: rtmr0-reset-n-active-state { - pins = "gpio10"; - function = "normal"; - power-source = <1>; /* 1.8V */ - bias-disable; - input-disable; - output-enable; - }; - - usb0_3p3_reg_en: usb0-3p3-reg-en-state { - pins = "gpio11"; - function = "normal"; - power-source = <1>; /* 1.8V */ - bias-disable; - input-disable; - output-enable; - }; -}; - -&pm8550ve_9_gpios { - usb0_1p8_reg_en: usb0-1p8-reg-en-state { - pins = "gpio8"; - function = "normal"; - power-source = <1>; /* 1.8V */ - bias-disable; - input-disable; - output-enable; - }; -}; - -&pmc8380_3_gpios { - edp_bl_en: edp-bl-en-state { - pins = "gpio4"; - function = "normal"; - power-source = <1>; /* 1.8 V */ - qcom,drive-strength = ; - bias-pull-down; - input-disable; - output-enable; - }; -}; - -&pmc8380_5_gpios { - usb0_pwr_1p15_reg_en: usb0-pwr-1p15-reg-en-state { - pins = "gpio8"; - function = "normal"; - power-source = <1>; /* 1.8V */ - bias-disable; - input-disable; - output-enable; - }; -}; - -&qupv3_0 { - status = "okay"; -}; - -&qupv3_1 { - status = "okay"; -}; - -&qupv3_2 { status = "okay"; }; @@ -1041,345 +39,3 @@ status = "okay"; }; -&smb2360_0 { - status = "okay"; -}; - -&smb2360_0_eusb2_repeater { - vdd18-supply = <&vreg_l3d_1p8>; - vdd3-supply = <&vreg_l2b_3p0>; -}; - -&smb2360_1 { - status = "okay"; -}; - -&smb2360_1_eusb2_repeater { - vdd18-supply = <&vreg_l3d_1p8>; - vdd3-supply = <&vreg_l14b_3p0>; -}; - -&tlmm { - gpio-reserved-ranges = <34 2>, /* Unused */ - <44 4>, /* SPI (TPM) */ - <238 1>; /* UFS Reset */ - - edp_reg_en: edp-reg-en-state { - pins = "gpio70"; - function = "gpio"; - drive-strength = <16>; - bias-disable; - }; - - eusb3_reset_n: eusb3-reset-n-state { - pins = "gpio6"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - output-low; - }; - - eusb5_reset_n: eusb5-reset-n-state { - pins = "gpio7"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - output-low; - }; - - eusb6_reset_n: eusb6-reset-n-state { - pins = "gpio184"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - output-low; - }; - - hall_int_n_default: hall-int-n-state { - pins = "gpio92"; - function = "gpio"; - bias-disable; - }; - - hdmi_hpd_default: hdmi-hpd-default-state { - pins = "gpio126"; - function = "usb2_dp"; - bias-disable; - }; - - kybd_default: kybd-default-state { - pins = "gpio67"; - function = "gpio"; - bias-disable; - }; - - nvme_reg_en: nvme-reg-en-state { - pins = "gpio18"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - - pcie4_default: pcie4-default-state { - clkreq-n-pins { - pins = "gpio147"; - function = "pcie4_clk"; - drive-strength = <2>; - bias-pull-up; - }; - - perst-n-pins { - pins = "gpio146"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - - wake-n-pins { - pins = "gpio148"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - }; - - pcie6a_default: pcie6a-default-state { - clkreq-n-pins { - pins = "gpio153"; - function = "pcie6a_clk"; - drive-strength = <2>; - bias-pull-up; - }; - - perst-n-pins { - pins = "gpio152"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - - wake-n-pins { - pins = "gpio154"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - }; - - rtmr1_default: rtmr1-reset-n-active-state { - pins = "gpio176"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - - tpad_default: tpad-default-state { - pins = "gpio3"; - function = "gpio"; - bias-disable; - }; - - usb1_pwr_1p15_reg_en: usb1-pwr-1p15-reg-en-state { - pins = "gpio188"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - - usb1_pwr_1p8_reg_en: usb1-pwr-1p8-reg-en-state { - pins = "gpio175"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - - usb1_pwr_3p3_reg_en: usb1-pwr-3p3-reg-en-state { - pins = "gpio186"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - - wcn_bt_en: wcn-bt-en-state { - pins = "gpio116"; - function = "gpio"; - drive-strength = <16>; - bias-pull-down; - }; - - wcn_sw_en: wcn-sw-en-state { - pins = "gpio214"; - function = "gpio"; - drive-strength = <16>; - bias-disable; - }; - - wcn_wlan_en: wcn-wlan-en-state { - pins = "gpio117"; - function = "gpio"; - drive-strength = <16>; - bias-disable; - }; -}; - -&uart14 { - status = "okay"; - - bluetooth { - compatible = "qcom,wcn7850-bt"; - max-speed = <3200000>; - - vddaon-supply = <&vreg_pmu_aon_0p59>; - vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; - vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; - vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; - vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; - vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; - vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; - }; -}; - -&usb_1_ss0_hsphy { - vdd-supply = <&vreg_l3j_0p8>; - vdda12-supply = <&vreg_l2j_1p2>; - - phys = <&smb2360_0_eusb2_repeater>; - - status = "okay"; -}; - -&usb_1_ss0_qmpphy { - vdda-phy-supply = <&vreg_l2j_1p2>; - vdda-pll-supply = <&vreg_l1j_0p8>; - - status = "okay"; -}; - -&usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { - dr_mode = "host"; -}; - -&usb_1_ss0_dwc3_hs { - remote-endpoint = <&pmic_glink_ss0_hs_in>; -}; - -&usb_1_ss0_qmpphy_out { - remote-endpoint = <&retimer_ss0_ss_in>; -}; - -&usb_1_ss1_hsphy { - vdd-supply = <&vreg_l3j_0p8>; - vdda12-supply = <&vreg_l2j_1p2>; - - phys = <&smb2360_1_eusb2_repeater>; - - status = "okay"; -}; - -&usb_1_ss1_qmpphy { - vdda-phy-supply = <&vreg_l2j_1p2>; - vdda-pll-supply = <&vreg_l2d_0p9>; - - status = "okay"; -}; - -&usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { - dr_mode = "host"; -}; - -&usb_1_ss1_dwc3_hs { - remote-endpoint = <&pmic_glink_ss1_hs_in>; -}; - -&usb_1_ss1_qmpphy_out { - remote-endpoint = <&retimer_ss1_ss_in>; -}; - -&usb_1_ss2_qmpphy { - vdda-phy-supply = <&vreg_l2j_1p2>; - vdda-pll-supply = <&vreg_l2d_0p9>; - - /delete-property/ mode-switch; - /delete-property/ orientation-switch; - - status = "okay"; - - ports { - port@0 { - #address-cells = <1>; - #size-cells = <0>; - - /delete-node/ endpoint; - - usb_1_ss2_qmpphy_out_dp: endpoint@0 { - reg = <0>; - - data-lanes = <3 2 1 0>; - remote-endpoint = <&hdmi_bridge_dp_in>; - }; - - /* No USB3 lanes connected */ - }; - }; -}; - -&usb_2 { - status = "okay"; -}; - -&usb_2_dwc3 { - dr_mode = "host"; -}; - -&usb_2_hsphy { - vdd-supply = <&vreg_l2e_0p8>; - vdda12-supply = <&vreg_l3e_1p2>; - - phys = <&eusb5_repeater>; - - status = "okay"; -}; - -&usb_mp { - status = "okay"; -}; - -&usb_mp_hsphy0 { - vdd-supply = <&vreg_l2e_0p8>; - vdda12-supply = <&vreg_l3e_1p2>; - - phys = <&eusb3_repeater>; - - status = "okay"; -}; - -&usb_mp_hsphy1 { - vdd-supply = <&vreg_l2e_0p8>; - vdda12-supply = <&vreg_l3e_1p2>; - - phys = <&eusb6_repeater>; - - status = "okay"; -}; - -&usb_mp_qmpphy0 { - vdda-phy-supply = <&vreg_l3e_1p2>; - vdda-pll-supply = <&vreg_l3c_0p8>; - - status = "okay"; -}; - -&usb_mp_qmpphy1 { - vdda-phy-supply = <&vreg_l3e_1p2>; - vdda-pll-supply = <&vreg_l3c_0p8>; - - status = "okay"; -}; diff --git a/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts b/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts index 4c95b1af2c64..ce7b10ea89b6 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts @@ -1293,11 +1293,9 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1325,11 +1323,9 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss1_dwc3_hs { diff --git a/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts b/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts index d7938d349205..beb1475d7fa0 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts @@ -1571,11 +1571,9 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1603,11 +1601,9 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss1_dwc3_hs { @@ -1635,11 +1631,9 @@ }; &usb_1_ss2 { - status = "okay"; -}; - -&usb_1_ss2_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss2_dwc3_hs { diff --git a/arch/arm64/boot/dts/qcom/x1e80100-medion-sprchrgd-14-s1.dts b/arch/arm64/boot/dts/qcom/x1e80100-medion-sprchrgd-14-s1.dts index eec5f2f1f75d..f95b1f9f439d 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-medion-sprchrgd-14-s1.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-medion-sprchrgd-14-s1.dts @@ -791,10 +791,10 @@ &gpu { status = "okay"; +}; - zap-shader { - firmware-name = "qcom/x1e80100/Medion/sprchrgd-14-s1/qcdxkmsuc8380.mbn"; - }; +&gpu_zap_shader { + firmware-name = "qcom/x1e80100/Medion/sprchrgd-14-s1/qcdxkmsuc8380.mbn"; }; &i2c0 { @@ -1414,11 +1414,9 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1459,11 +1457,9 @@ /* Camera */ &usb_2 { - status = "okay"; -}; - -&usb_2_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_2_hsphy { diff --git a/arch/arm64/boot/dts/qcom/x1e80100-microsoft-romulus.dtsi b/arch/arm64/boot/dts/qcom/x1e80100-microsoft-romulus.dtsi index 37539a09b76e..28342cb84ded 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-microsoft-romulus.dtsi +++ b/arch/arm64/boot/dts/qcom/x1e80100-microsoft-romulus.dtsi @@ -864,7 +864,6 @@ }; &gpu_zap_shader { - memory-region = <&gpu_microcode_mem>; firmware-name = "qcom/x1e80100/microsoft/qcdxkmsuc8380.mbn"; }; @@ -1499,11 +1498,9 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1531,11 +1528,9 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss1_dwc3_hs { diff --git a/arch/arm64/boot/dts/qcom/x1e80100-qcp.dts b/arch/arm64/boot/dts/qcom/x1e80100-qcp.dts index 1d402ef86512..8afbac349cc9 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-qcp.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-qcp.dts @@ -1423,11 +1423,9 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1455,11 +1453,9 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss1_dwc3_hs { @@ -1487,11 +1483,9 @@ }; &usb_1_ss2 { - status = "okay"; -}; - -&usb_1_ss2_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss2_dwc3_hs { diff --git a/arch/arm64/boot/dts/qcom/x1p42100-asus-vivobook-s15.dts b/arch/arm64/boot/dts/qcom/x1p42100-asus-vivobook-s15.dts new file mode 100644 index 000000000000..63e29d2cc4ab --- /dev/null +++ b/arch/arm64/boot/dts/qcom/x1p42100-asus-vivobook-s15.dts @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2024, Xilin Wu + */ + +/dts-v1/; + +#include "purwa.dtsi" +#include "x1-asus-vivobook-s15.dtsi" + +/delete-node/ &pmc8380_6; +/delete-node/ &pmc8380_6_thermal; + +/ { + model = "ASUS Vivobook S 15 X1P-42-100"; + compatible = "asus,vivobook-s15-x1p4", "qcom,x1p42100"; + chassis-type = "laptop"; +}; + +&gpu_zap_shader { + firmware-name = "qcom/x1p42100/ASUSTeK/vivobook-s15/qcdxkmsucpurwa.mbn"; +}; + +&iris { + firmware-name = "qcom/x1p42100/ASUSTeK/vivobook-s15/qcvss8380.mbn"; + + status = "okay"; +}; + +&remoteproc_adsp { + firmware-name = "qcom/x1p42100/ASUSTeK/vivobook-s15/qcadsp8380.mbn", + "qcom/x1p42100/ASUSTeK/vivobook-s15/adsp_dtbs.elf"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/x1p42100/ASUSTeK/vivobook-s15/qccdsp8380.mbn", + "qcom/x1p42100/ASUSTeK/vivobook-s15/cdsp_dtbs.elf"; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-thinkbook-16.dts b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-thinkbook-16.dts index 06747b54a38e..500809772097 100644 --- a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-thinkbook-16.dts +++ b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-thinkbook-16.dts @@ -1568,11 +1568,9 @@ }; &usb_1_ss0 { - status = "okay"; -}; - -&usb_1_ss0_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss0_dwc3_hs { @@ -1600,11 +1598,9 @@ }; &usb_1_ss1 { - status = "okay"; -}; - -&usb_1_ss1_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_1_ss1_dwc3_hs { @@ -1616,15 +1612,12 @@ }; &usb_1_ss2 { - status = "okay"; -}; - -&usb_1_ss2_dwc3 { dr_mode = "host"; maximum-speed = "high-speed"; phys = <&usb_1_ss2_hsphy>; phy-names = "usb2-phy"; + status = "okay"; /delete-property/ port@1; }; @@ -1664,11 +1657,9 @@ }; &usb_2 { - status = "okay"; -}; - -&usb_2_dwc3 { dr_mode = "host"; + + status = "okay"; }; &usb_2_hsphy { diff --git a/arch/arm64/boot/dts/renesas/Makefile b/arch/arm64/boot/dts/renesas/Makefile index 1fab1b50f20e..ca45d2857ea7 100644 --- a/arch/arm64/boot/dts/renesas/Makefile +++ b/arch/arm64/boot/dts/renesas/Makefile @@ -114,12 +114,18 @@ dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-sparrow-hawk-fan-argon40.dtb dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-sparrow-hawk-fan-pwm.dtbo r8a779g3-sparrow-hawk-fan-pwm-dtbs := r8a779g3-sparrow-hawk.dtb r8a779g3-sparrow-hawk-fan-pwm.dtbo dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-sparrow-hawk-fan-pwm.dtb +dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-sparrow-hawk-olimex-dsi-hdmi.dtbo +r8a779g3-sparrow-hawk-olimex-dsi-hdmi-dtbs := r8a779g3-sparrow-hawk.dtb r8a779g3-sparrow-hawk-olimex-dsi-hdmi.dtbo +dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-sparrow-hawk-olimex-dsi-hdmi.dtb dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-sparrow-hawk-rpi-display-2-5in.dtbo r8a779g3-sparrow-hawk-rpi-display-2-5in-dtbs := r8a779g3-sparrow-hawk.dtb r8a779g3-sparrow-hawk-rpi-display-2-5in.dtbo dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-sparrow-hawk-rpi-display-2-5in.dtb dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-sparrow-hawk-rpi-display-2-7in.dtbo r8a779g3-sparrow-hawk-rpi-display-2-7in-dtbs := r8a779g3-sparrow-hawk.dtb r8a779g3-sparrow-hawk-rpi-display-2-7in.dtbo dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-sparrow-hawk-rpi-display-2-7in.dtb +dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-sparrow-hawk-ws-display-13in.dtbo +r8a779g3-sparrow-hawk-ws-display-13in-dtbs := r8a779g3-sparrow-hawk.dtb r8a779g3-sparrow-hawk-ws-display-13in.dtbo +dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-sparrow-hawk-ws-display-13in.dtb dtb-$(CONFIG_ARCH_R8A779G0) += r8a779g3-white-hawk-single.dtb r8a779g3-white-hawk-single-ard-audio-da7212-dtbs := r8a779g3-white-hawk-single.dtb white-hawk-ard-audio-da7212.dtbo @@ -179,6 +185,8 @@ dtb-$(CONFIG_ARCH_R9A08G045) += r9a08g045s33-smarc-pmod1-type-3a.dtbo r9a08g045s33-smarc-pmod1-type-3a-dtbs := r9a08g045s33-smarc.dtb r9a08g045s33-smarc-pmod1-type-3a.dtbo dtb-$(CONFIG_ARCH_R9A08G045) += r9a08g045s33-smarc-pmod1-type-3a.dtb +dtb-$(CONFIG_ARCH_R9A08G046) += r9a08g046l48-smarc.dtb + dtb-$(CONFIG_ARCH_R9A09G011) += r9a09g011-v2mevk2.dtb dtb-$(CONFIG_ARCH_R9A09G047) += r9a09g047e57-smarc.dtb @@ -202,6 +210,9 @@ dtb-$(CONFIG_ARCH_R9A09G057) += rzv2-evk-cn15-sd.dtbo r9a09g057h44-rzv2h-evk-cn15-sd-dtbs := r9a09g057h44-rzv2h-evk.dtb rzv2-evk-cn15-sd.dtbo dtb-$(CONFIG_ARCH_R9A09G057) += r9a09g057h44-rzv2h-evk-cn15-sd.dtb dtb-$(CONFIG_ARCH_R9A09G057) += r9a09g057h48-kakip.dtb +dtb-$(CONFIG_ARCH_R9A09G057) += r9a09g057h48-kakip-pixpaper.dtbo +r9a09g057h48-kakip-pixpaper-dtbs := r9a09g057h48-kakip.dtb r9a09g057h48-kakip-pixpaper.dtbo +dtb-$(CONFIG_ARCH_R9A09G057) += r9a09g057h48-kakip-pixpaper.dtb dtb-$(CONFIG_ARCH_R9A09G077) += r9a09g077m44-rzt2h-evk.dtb diff --git a/arch/arm64/boot/dts/renesas/beacon-renesom-baseboard.dtsi b/arch/arm64/boot/dts/renesas/beacon-renesom-baseboard.dtsi index d55f2d7066ad..62ab0a3776e7 100644 --- a/arch/arm64/boot/dts/renesas/beacon-renesom-baseboard.dtsi +++ b/arch/arm64/boot/dts/renesas/beacon-renesom-baseboard.dtsi @@ -8,15 +8,6 @@ #include / { - backlight_lvds: backlight-lvds { - compatible = "pwm-backlight"; - power-supply = <®_lcd>; - enable-gpios = <&gpio_exp1 3 GPIO_ACTIVE_HIGH>; - pwms = <&pwm2 0 25000>; - brightness-levels = <0 4 8 16 32 64 128 255>; - default-brightness-level = <6>; - }; - backlight_dpi: backlight-dpi { compatible = "pwm-backlight"; power-supply = <®_lcd>; @@ -101,38 +92,6 @@ }; }; - lvds { - compatible = "panel-lvds"; - power-supply = <®_lcd_reset>; - width-mm = <223>; - height-mm = <125>; - backlight = <&backlight_lvds>; - data-mapping = "vesa-24"; - - panel-timing { - /* 800x480@60Hz */ - clock-frequency = <30000000>; - hactive = <800>; - vactive = <480>; - hsync-len = <48>; - hfront-porch = <40>; - hback-porch = <40>; - vfront-porch = <13>; - vback-porch = <29>; - vsync-len = <1>; - hsync-active = <1>; - vsync-active = <3>; - de-active = <1>; - pixelclk-active = <0>; - }; - - port { - panel_in: endpoint { - remote-endpoint = <&lvds0_out>; - }; - }; - }; - rgb { /* Different LCD with compatible timings */ compatible = "rocktech,rk070er9427"; @@ -164,16 +123,6 @@ enable-active-high; }; - reg_lcd_reset: regulator-lcd-reset { - compatible = "regulator-fixed"; - regulator-name = "nLCD_RESET"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio5 3 GPIO_ACTIVE_HIGH>; - enable-active-high; - vin-supply = <®_lcd>; - }; - reg_cam0: regulator-cam0 { compatible = "regulator-fixed"; regulator-name = "reg_cam0"; @@ -480,18 +429,6 @@ }; }; -&lvds0 { - status = "okay"; - - ports { - port@1 { - lvds0_out: endpoint { - remote-endpoint = <&panel_in>; - }; - }; - }; -}; - &msiof1 { pinctrl-0 = <&msiof1_pins>; pinctrl-names = "default"; @@ -562,11 +499,6 @@ function = "pwm0"; }; - pwm2_pins: pwm2 { - groups = "pwm2_a"; - function = "pwm2"; - }; - sdhi0_pins: sd0 { groups = "sdhi0_data4", "sdhi0_ctrl"; function = "sdhi0"; @@ -617,12 +549,6 @@ status = "okay"; }; -&pwm2 { - pinctrl-0 = <&pwm2_pins>; - pinctrl-names = "default"; - status = "okay"; -}; - &rcar_sound { pinctrl-0 = <&sound_pins>, <&sound_clk_pins>; pinctrl-names = "default"; diff --git a/arch/arm64/boot/dts/renesas/beacon-renesom-som.dtsi b/arch/arm64/boot/dts/renesas/beacon-renesom-som.dtsi index af6d15f90c65..f8442b6a85a7 100644 --- a/arch/arm64/boot/dts/renesas/beacon-renesom-som.dtsi +++ b/arch/arm64/boot/dts/renesas/beacon-renesom-som.dtsi @@ -59,8 +59,7 @@ status = "okay"; phy0: ethernet-phy@0 { - compatible = "ethernet-phy-id0022.1640", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <0>; interrupts-extended = <&gpio2 11 IRQ_TYPE_LEVEL_LOW>; reset-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>; diff --git a/arch/arm64/boot/dts/renesas/cat875.dtsi b/arch/arm64/boot/dts/renesas/cat875.dtsi index 191b051ecfd4..5815e9d2d8a9 100644 --- a/arch/arm64/boot/dts/renesas/cat875.dtsi +++ b/arch/arm64/boot/dts/renesas/cat875.dtsi @@ -22,8 +22,7 @@ status = "okay"; phy0: ethernet-phy@0 { - compatible = "ethernet-phy-id001c.c915", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id001c.c915"; reg = <0>; interrupts-extended = <&gpio2 21 IRQ_TYPE_LEVEL_LOW>; reset-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>; diff --git a/arch/arm64/boot/dts/renesas/ebisu.dtsi b/arch/arm64/boot/dts/renesas/ebisu.dtsi index 692a2b12aa03..aaedb1fb51ae 100644 --- a/arch/arm64/boot/dts/renesas/ebisu.dtsi +++ b/arch/arm64/boot/dts/renesas/ebisu.dtsi @@ -53,6 +53,12 @@ power-supply = <®_12p0v>; }; + pcie_usb_refclk: clk-x7 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + cvbs-in { compatible = "composite-video-connector"; label = "CVBS IN"; @@ -439,6 +445,13 @@ }; }; + pcie_usb_clk: clk@68 { + compatible = "renesas,9fgv0841"; + reg = <0x68>; + clocks = <&pcie_usb_refclk>; + #clock-cells = <1>; + }; + video-receiver@70 { compatible = "adi,adv7482"; reg = <0x70>; @@ -577,13 +590,30 @@ }; &pcie_bus_clk { - clock-frequency = <100000000>; + status = "disabled"; }; &pciec0 { + clocks = <&cpg CPG_MOD 319>, <&pcie_usb_clk 1>; status = "okay"; }; +&pciec0_rp { + /* + * This configuration is valid for SW49 in OFF position, + * which means the PCIe signals are routed to the PCIe slot + * and U11 9FGV0841 PCIe clock generator output 3 supplies + * clock to the PCIe slot. + * + * In case the SW49 is set to ON position, which means the + * PCIe signals are routed to the EX BT/WLAN expansion port, + * and U11 9FGV0841 PCIe clock generator output 4 supplies + * clock to the port, change clocks below to: + * clocks = <&pcie_usb_clk 4>; + */ + clocks = <&pcie_usb_clk 3>; +}; + &pfc { avb_pins: avb { groups = "avb_link", "avb_mii"; @@ -871,7 +901,18 @@ status = "okay"; }; +&usb3_phy0 { + clocks = <&pcie_usb_clk 6>; + status = "okay"; +}; + +&usb3s0_clk { + status = "disabled"; +}; + &usb3_peri0 { + phys = <&usb3_phy0>; + phy-names = "usb"; companion = <&xhci0>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/renesas/hihope-rzg2-ex.dtsi b/arch/arm64/boot/dts/renesas/hihope-rzg2-ex.dtsi index 4113710d5522..83b6c04274ac 100644 --- a/arch/arm64/boot/dts/renesas/hihope-rzg2-ex.dtsi +++ b/arch/arm64/boot/dts/renesas/hihope-rzg2-ex.dtsi @@ -24,8 +24,7 @@ status = "okay"; phy0: ethernet-phy@0 { - compatible = "ethernet-phy-id001c.c915", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id001c.c915"; reg = <0>; interrupts-extended = <&gpio2 11 IRQ_TYPE_LEVEL_LOW>; reset-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>; diff --git a/arch/arm64/boot/dts/renesas/r8a77951.dtsi b/arch/arm64/boot/dts/renesas/r8a77951.dtsi index 607f62a448d8..59a0f2e1479d 100644 --- a/arch/arm64/boot/dts/renesas/r8a77951.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77951.dtsi @@ -2814,6 +2814,16 @@ iommu-map = <0 &ipmmu_hc 0 1>; iommu-map-mask = <0>; status = "disabled"; + + /* PCIe bridge, Root Port */ + pciec0_rp: pci@0,0 { + #address-cells = <3>; + #size-cells = <2>; + reg = <0x0 0x0 0x0 0x0 0x0>; + compatible = "pciclass,0604"; + device_type = "pci"; + ranges; + }; }; pciec1: pcie@ee800000 { @@ -2843,6 +2853,16 @@ iommu-map = <0 &ipmmu_hc 1 1>; iommu-map-mask = <0>; status = "disabled"; + + /* PCIe bridge, Root Port */ + pciec1_rp: pci@0,0 { + #address-cells = <3>; + #size-cells = <2>; + reg = <0x0 0x0 0x0 0x0 0x0>; + compatible = "pciclass,0604"; + device_type = "pci"; + ranges; + }; }; pciec0_ep: pcie-ep@fe000000 { diff --git a/arch/arm64/boot/dts/renesas/r8a77960.dtsi b/arch/arm64/boot/dts/renesas/r8a77960.dtsi index e64c7b1aebc4..ad36aa8e7543 100644 --- a/arch/arm64/boot/dts/renesas/r8a77960.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77960.dtsi @@ -2619,6 +2619,16 @@ iommu-map = <0 &ipmmu_hc 0 1>; iommu-map-mask = <0>; status = "disabled"; + + /* PCIe bridge, Root Port */ + pciec0_rp: pci@0,0 { + #address-cells = <3>; + #size-cells = <2>; + reg = <0x0 0x0 0x0 0x0 0x0>; + compatible = "pciclass,0604"; + device_type = "pci"; + ranges; + }; }; pciec1: pcie@ee800000 { @@ -2648,6 +2658,16 @@ iommu-map = <0 &ipmmu_hc 1 1>; iommu-map-mask = <0>; status = "disabled"; + + /* PCIe bridge, Root Port */ + pciec1_rp: pci@0,0 { + #address-cells = <3>; + #size-cells = <2>; + reg = <0x0 0x0 0x0 0x0 0x0>; + compatible = "pciclass,0604"; + device_type = "pci"; + ranges; + }; }; imr-lx4@fe860000 { diff --git a/arch/arm64/boot/dts/renesas/r8a77961.dtsi b/arch/arm64/boot/dts/renesas/r8a77961.dtsi index 89f6c052c5e0..9d76e39eab72 100644 --- a/arch/arm64/boot/dts/renesas/r8a77961.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77961.dtsi @@ -2499,6 +2499,16 @@ iommu-map = <0 &ipmmu_hc 0 1>; iommu-map-mask = <0>; status = "disabled"; + + /* PCIe bridge, Root Port */ + pciec0_rp: pci@0,0 { + #address-cells = <3>; + #size-cells = <2>; + reg = <0x0 0x0 0x0 0x0 0x0>; + compatible = "pciclass,0604"; + device_type = "pci"; + ranges; + }; }; pciec1: pcie@ee800000 { @@ -2528,6 +2538,16 @@ iommu-map = <0 &ipmmu_hc 1 1>; iommu-map-mask = <0>; status = "disabled"; + + /* PCIe bridge, Root Port */ + pciec1_rp: pci@0,0 { + #address-cells = <3>; + #size-cells = <2>; + reg = <0x0 0x0 0x0 0x0 0x0>; + compatible = "pciclass,0604"; + device_type = "pci"; + ranges; + }; }; fcpf0: fcp@fe950000 { diff --git a/arch/arm64/boot/dts/renesas/r8a77965.dtsi b/arch/arm64/boot/dts/renesas/r8a77965.dtsi index 425561e658ca..611a9335c63a 100644 --- a/arch/arm64/boot/dts/renesas/r8a77965.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77965.dtsi @@ -2494,6 +2494,16 @@ iommu-map = <0 &ipmmu_hc 0 1>; iommu-map-mask = <0>; status = "disabled"; + + /* PCIe bridge, Root Port */ + pciec0_rp: pci@0,0 { + #address-cells = <3>; + #size-cells = <2>; + reg = <0x0 0x0 0x0 0x0 0x0>; + compatible = "pciclass,0604"; + device_type = "pci"; + ranges; + }; }; pciec1: pcie@ee800000 { @@ -2523,6 +2533,16 @@ iommu-map = <0 &ipmmu_hc 1 1>; iommu-map-mask = <0>; status = "disabled"; + + /* PCIe bridge, Root Port */ + pciec1_rp: pci@0,0 { + #address-cells = <3>; + #size-cells = <2>; + reg = <0x0 0x0 0x0 0x0 0x0>; + compatible = "pciclass,0604"; + device_type = "pci"; + ranges; + }; }; fdp1@fe940000 { diff --git a/arch/arm64/boot/dts/renesas/r8a77990.dtsi b/arch/arm64/boot/dts/renesas/r8a77990.dtsi index d3698f7e494d..fadb5f4effcf 100644 --- a/arch/arm64/boot/dts/renesas/r8a77990.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77990.dtsi @@ -1912,6 +1912,16 @@ iommu-map = <0 &ipmmu_hc 0 1>; iommu-map-mask = <0>; status = "disabled"; + + /* PCIe bridge, Root Port */ + pciec0_rp: pci@0,0 { + #address-cells = <3>; + #size-cells = <2>; + reg = <0x0 0x0 0x0 0x0 0x0>; + compatible = "pciclass,0604"; + device_type = "pci"; + ranges; + }; }; vspb0: vsp@fe960000 { @@ -2180,4 +2190,21 @@ ; interrupt-names = "sec-phys", "phys", "virt", "hyp-phys"; }; + + /* External USB clock - to be overridden by boards that provide it */ + usb3s0_clk: usb3s0-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + usb3_phy0: usb-phy { + compatible = "usb-nop-xceiv"; + clocks = <&usb3s0_clk>; + clock-names = "main_clk"; + clock-frequency = <100000000>; + power-domains = <&sysc R8A77990_PD_ALWAYS_ON>; + #phy-cells = <0>; + status = "disabled"; + }; }; diff --git a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-olimex-dsi-hdmi.dtso b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-olimex-dsi-hdmi.dtso new file mode 100644 index 000000000000..40cc5ee3b56d --- /dev/null +++ b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-olimex-dsi-hdmi.dtso @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Device Tree Overlay for Olimex MIPI-HDMI adapter connected to J4:DSI + * on R-Car V4H ES3.0 Sparrow Hawk board + * + * Copyright (C) 2026 Scott Murray + */ + +/dts-v1/; +/plugin/; + +&{/} { + hdmi-connector { + compatible = "hdmi-connector"; + label = "HDMI1"; + type = "a"; + ddc-i2c-bus = <&i2c0_mux3>; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <<8912b_out>; + }; + }; + }; + + reg_vr1: regulator-vr1 { + compatible = "regulator-fixed"; + regulator-name = "VR1-1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + vin-supply = <®_3p3v>; + }; +}; + +&i2c0_mux3 { + #address-cells = <1>; + #size-cells = <0>; + + hdmi-bridge@48 { + compatible = "lontium,lt8912b"; + reg = <0x48>; + vcchdmipll-supply = <®_vr1>; + vcchdmitx-supply = <®_vr1>; + vcclvdspll-supply = <®_vr1>; + vcclvdstx-supply = <®_vr1>; + vccmipirx-supply = <®_vr1>; + vccsysclk-supply = <®_vr1>; + vdd-supply = <®_vr1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + hdmi_out_in: endpoint { + data-lanes = <1 2>; + remote-endpoint = <&dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + + lt8912b_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; +}; + +&dsi0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + + dsi0_out: endpoint { + remote-endpoint = <&hdmi_out_in>; + data-lanes = <1 2>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-ws-display-13in.dtso b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-ws-display-13in.dtso new file mode 100644 index 000000000000..26434d4540ef --- /dev/null +++ b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-ws-display-13in.dtso @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Device Tree Overlay for the Waveshare 13.3 MIPI DSI panel connected + * to J4:DSI on R-Car V4H ES3.0 Sparrow Hawk board + * + * Copyright (C) 2025-2026 Marek Vasut + */ + +/dts-v1/; +/plugin/; + +&{/} { + panel { + compatible = "waveshare,13.3inch-panel"; + power-supply = <®_5p0v>; + + port { + panel_in: endpoint { + remote-endpoint = <&bridge_out>; + }; + }; + }; + + reg_5p0v: regulator-5p0v { + compatible = "regulator-fixed"; + regulator-name = "fixed-5.0V"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + regulator-boot-on; + }; +}; + +&i2c0_mux3 { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen@41 { + compatible = "ilitek,ili251x"; + reg = <0x41>; + }; + + bridge@45 { + compatible = "waveshare,dsi2dpi"; + reg = <0x45>; + power-supply = <®_5p0v>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + bridge_in: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + + bridge_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; +}; + +&dsi0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + + dsi0_out: endpoint { + remote-endpoint = <&bridge_in>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts index 812b133cf29e..af680290ce81 100644 --- a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts +++ b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk.dts @@ -547,6 +547,10 @@ }; }; +&otp { + bootph-all; +}; + /* Page 26 / 2230 Key M M.2 */ &pcie0_clkref { status = "disabled"; @@ -631,6 +635,7 @@ hscif0_pins: hscif0 { groups = "hscif0_data", "hscif0_ctrl"; function = "hscif0"; + bootph-all; }; /* Page 23 / DEBUG */ diff --git a/arch/arm64/boot/dts/renesas/r9a08g046.dtsi b/arch/arm64/boot/dts/renesas/r9a08g046.dtsi new file mode 100644 index 000000000000..28b0c7558748 --- /dev/null +++ b/arch/arm64/boot/dts/renesas/r9a08g046.dtsi @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Device Tree Source for the RZ/G3L SoC + * + * Copyright (C) 2026 Renesas Electronics Corp. + */ + +#include +#include + +/ { + compatible = "renesas,r9a08g046"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "arm,cortex-a55"; + reg = <0>; + device_type = "cpu"; + next-level-cache = <&L3_CA55>; + enable-method = "psci"; + }; + + cpu1: cpu@100 { + compatible = "arm,cortex-a55"; + reg = <0x100>; + device_type = "cpu"; + next-level-cache = <&L3_CA55>; + enable-method = "psci"; + }; + + cpu2: cpu@200 { + compatible = "arm,cortex-a55"; + reg = <0x200>; + device_type = "cpu"; + next-level-cache = <&L3_CA55>; + enable-method = "psci"; + }; + + cpu3: cpu@300 { + compatible = "arm,cortex-a55"; + reg = <0x300>; + device_type = "cpu"; + next-level-cache = <&L3_CA55>; + enable-method = "psci"; + }; + + L3_CA55: cache-controller-0 { + compatible = "cache"; + cache-unified; + cache-size = <0x80000>; + cache-level = <3>; + }; + }; + + eth0_txc_tx_clk: eth0-txc-tx-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board */ + clock-frequency = <0>; + }; + + eth0_rxc_rx_clk: eth0-rxc-rx-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board */ + clock-frequency = <0>; + }; + + eth1_txc_tx_clk: eth1-txc-tx-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board */ + clock-frequency = <0>; + }; + + eth1_rxc_rx_clk: eth1-rxc-rx-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board */ + clock-frequency = <0>; + }; + + extal_clk: extal-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + /* This value must be overridden by the board. */ + clock-frequency = <0>; + }; + + psci { + compatible = "arm,psci-1.0", "arm,psci-0.2"; + method = "smc"; + }; + + soc: soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + scif0: serial@100ac000 { + compatible = "renesas,scif-r9a08g046", "renesas,scif-r9a07g044"; + reg = <0 0x100ac000 0 0x400>; + interrupts = , + , + , + , + , + ; + interrupt-names = "eri", "rxi", "txi", + "bri", "dri", "tei"; + clocks = <&cpg CPG_MOD R9A08G046_SCIF0_CLK_PCK>; + clock-names = "fck"; + power-domains = <&cpg>; + resets = <&cpg R9A08G046_SCIF0_RST_SYSTEM_N>; + status = "disabled"; + }; + + i2c0: i2c@100ae000 { + reg = <0 0x100ae000 0 0x400>; + #address-cells = <1>; + #size-cells = <0>; + /* placeholder */ + }; + + canfd: can@100c0000 { + reg = <0 0x100c0000 0 0x20000>; + /* placeholder */ + }; + + cpg: clock-controller@11010000 { + compatible = "renesas,r9a08g046-cpg"; + reg = <0 0x11010000 0 0x10000>; + clocks = <&extal_clk>, + <ð0_txc_tx_clk>, <ð0_rxc_rx_clk>, + <ð1_txc_tx_clk>, <ð1_rxc_rx_clk>; + clock-names = "extal", + "eth0_txc_tx_clk", "eth0_rxc_rx_clk", + "eth1_txc_tx_clk", "eth1_rxc_rx_clk"; + #clock-cells = <2>; + #reset-cells = <1>; + #power-domain-cells = <0>; + }; + + sysc: system-controller@11020000 { + compatible = "renesas,r9a08g046-sysc"; + reg = <0 0x11020000 0 0x10000>; + interrupts = , + , + , + ; + interrupt-names = "lpm_int", "ca55stbydone_int", + "cm33stbyr_int", "ca55_deny"; + }; + + pinctrl: pinctrl@11030000 { + reg = <0 0x11030000 0 0x10000>; + gpio-controller; + #gpio-cells = <2>; + /* placeholder */ + }; + + sdhi1: mmc@11c10000 { + reg = <0x0 0x11c10000 0 0x10000>; + /* placeholder */ + }; + + pcie: pcie@11e40000 { + reg = <0 0x11e40000 0 0x10000>; + ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + /* placeholder */ + + pcie_port0: pcie@0,0 { + reg = <0x0 0x0 0x0 0x0 0x0>; + ranges; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + /* placeholder */ + }; + }; + + gic: interrupt-controller@12400000 { + compatible = "arm,gic-v3"; + reg = <0x0 0x12400000 0 0x20000>, + <0x0 0x12440000 0 0x80000>; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + interrupts = ; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + , + ; + interrupt-names = "sec-phys", "phys", "virt", "hyp-phys", "hyp-virt"; + }; +}; diff --git a/arch/arm64/boot/dts/renesas/r9a08g046l48-smarc.dts b/arch/arm64/boot/dts/renesas/r9a08g046l48-smarc.dts new file mode 100644 index 000000000000..86db86335d5e --- /dev/null +++ b/arch/arm64/boot/dts/renesas/r9a08g046l48-smarc.dts @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Device Tree Source for the RZ/G3L SMARC EVK board + * + * Copyright (C) 2026 Renesas Electronics Corp. + */ + +/dts-v1/; + +/* Add place holder to avoid compilation error with renesas-smarc2.dtsi */ +#define KEY_1_GPIO 1 +#define KEY_2_GPIO 2 +#define KEY_3_GPIO 3 + +#include +#include +#include "r9a08g046l48.dtsi" +#include "rzg3l-smarc-som.dtsi" +#include "renesas-smarc2.dtsi" + +/ { + model = "Renesas SMARC EVK version 2 based on r9a08g046l48"; + compatible = "renesas,smarc2-evk", "renesas,rzg3l-smarcm", + "renesas,r9a08g046l48", "renesas,r9a08g046"; + + aliases { + serial3 = &scif0; + }; +}; + +&keys { + status = "disabled"; + + /delete-node/ key-1; + /delete-node/ key-2; + /delete-node/ key-3; +}; diff --git a/arch/arm64/boot/dts/renesas/r9a08g046l48.dtsi b/arch/arm64/boot/dts/renesas/r9a08g046l48.dtsi new file mode 100644 index 000000000000..39b114172af5 --- /dev/null +++ b/arch/arm64/boot/dts/renesas/r9a08g046l48.dtsi @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Device Tree Source for the RZ/G3L R9A08G046L48 SoC specific parts + * + * Copyright (C) 2026 Renesas Electronics Corp. + */ + +/dts-v1/; +#include "r9a08g046.dtsi" + +/ { + compatible = "renesas,r9a08g046l48", "renesas,r9a08g046"; +}; diff --git a/arch/arm64/boot/dts/renesas/r9a09g011-v2mevk2.dts b/arch/arm64/boot/dts/renesas/r9a09g011-v2mevk2.dts index 39fe3f94991e..07147743de93 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g011-v2mevk2.dts +++ b/arch/arm64/boot/dts/renesas/r9a09g011-v2mevk2.dts @@ -100,8 +100,7 @@ status = "okay"; phy0: ethernet-phy@0 { - compatible = "ethernet-phy-id001c.c916", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id001c.c916"; reg = <0>; }; }; diff --git a/arch/arm64/boot/dts/renesas/r9a09g047.dtsi b/arch/arm64/boot/dts/renesas/r9a09g047.dtsi index cbb48ff5028f..95a4e30a064d 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g047.dtsi +++ b/arch/arm64/boot/dts/renesas/r9a09g047.dtsi @@ -591,6 +591,90 @@ }; }; + rspi0: spi@12800000 { + compatible = "renesas,r9a09g047-rspi", "renesas,r9a09g057-rspi"; + reg = <0x0 0x12800000 0x0 0x400>; + interrupts = , + , + , + , + ; + interrupt-names = "idle", "error", "end", "rx", "tx"; + clocks = <&cpg CPG_MOD 0x54>, + <&cpg CPG_MOD 0x55>, + <&cpg CPG_MOD 0x56>; + clock-names = "pclk", "pclk_sfr", "tclk"; + resets = <&cpg 0x7b>, <&cpg 0x7c>; + reset-names = "presetn", "tresetn"; + dmas = <&dmac0 0x448c>, <&dmac0 0x448d>, + <&dmac1 0x448c>, <&dmac1 0x448d>, + <&dmac2 0x448c>, <&dmac2 0x448d>, + <&dmac3 0x448c>, <&dmac3 0x448d>, + <&dmac4 0x448c>, <&dmac4 0x448d>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + rspi1: spi@12800400 { + compatible = "renesas,r9a09g047-rspi", "renesas,r9a09g057-rspi"; + reg = <0x0 0x12800400 0x0 0x400>; + interrupts = , + , + , + , + ; + interrupt-names = "idle", "error", "end", "rx", "tx"; + clocks = <&cpg CPG_MOD 0x57>, + <&cpg CPG_MOD 0x58>, + <&cpg CPG_MOD 0x59>; + clock-names = "pclk", "pclk_sfr", "tclk"; + resets = <&cpg 0x7d>, <&cpg 0x7e>; + reset-names = "presetn", "tresetn"; + dmas = <&dmac0 0x448e>, <&dmac0 0x448f>, + <&dmac1 0x448e>, <&dmac1 0x448f>, + <&dmac2 0x448e>, <&dmac2 0x448f>, + <&dmac3 0x448e>, <&dmac3 0x448f>, + <&dmac4 0x448e>, <&dmac4 0x448f>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + rspi2: spi@12800800 { + compatible = "renesas,r9a09g047-rspi", "renesas,r9a09g057-rspi"; + reg = <0x0 0x12800800 0x0 0x400>; + interrupts = , + , + , + , + ; + interrupt-names = "idle", "error", "end", "rx", "tx"; + clocks = <&cpg CPG_MOD 0x5a>, + <&cpg CPG_MOD 0x5b>, + <&cpg CPG_MOD 0x5c>; + clock-names = "pclk", "pclk_sfr", "tclk"; + resets = <&cpg 0x7f>, <&cpg 0x80>; + reset-names = "presetn", "tresetn"; + dmas = <&dmac0 0x4490>, <&dmac0 0x4491>, + <&dmac1 0x4490>, <&dmac1 0x4491>, + <&dmac2 0x4490>, <&dmac2 0x4491>, + <&dmac3 0x4490>, <&dmac3 0x4491>, + <&dmac4 0x4490>, <&dmac4 0x4491>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx", + "rx", "tx", "rx", "tx"; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + rsci0: serial@12800c00 { compatible = "renesas,r9a09g047-rsci"; reg = <0 0x12800c00 0 0x400>; @@ -841,6 +925,75 @@ status = "disabled"; }; + pcie: pcie@13400000 { + compatible = "renesas,r9a09g047-pcie"; + reg = <0 0x13400000 0 0x10000>; + ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>, + <0x43000000 4 0x40000000 4 0x40000000 6 0x00000000>; + dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 2 0x00000000>; + bus-range = <0x0 0xff>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "serr", "serr_cor", "serr_nonfatal", + "serr_fatal", "axi_err", "inta", + "intb", "intc", "intd", "msi", + "link_bandwidth", "pm_pme", "dma", + "pcie_evt", "msg", "all", + "link_equalization_request", + "turn_off_event", "pmu_poweroff", + "d3_event_f0", "d3_event_f1", + "cfg_pmcsr_writeclear_f0", + "cfg_pmcsr_writeclear_f1"; + #interrupt-cells = <1>; + interrupt-controller; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie 0 0 0 0>, /* INTA */ + <0 0 0 2 &pcie 0 0 0 1>, /* INTB */ + <0 0 0 3 &pcie 0 0 0 2>, /* INTC */ + <0 0 0 4 &pcie 0 0 0 3>; /* INTD */ + clocks = <&cpg CPG_MOD 0xc4>, <&cpg CPG_MOD 0xc5>; + clock-names = "aclk", "pmu"; + resets = <&cpg 0xb2>; + reset-names = "aresetn"; + power-domains = <&cpg>; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + renesas,sysc = <&sys>; + status = "disabled"; + + pcie_port0: pcie@0,0 { + reg = <0x0 0x0 0x0 0x0 0x0>; + ranges; + device_type = "pci"; + vendor-id = <0x1912>; + device-id = <0x0039>; + #address-cells = <3>; + #size-cells = <2>; + }; + }; + tsu: thermal@14002000 { compatible = "renesas,r9a09g047-tsu"; reg = <0 0x14002000 0 0x1000>; diff --git a/arch/arm64/boot/dts/renesas/r9a09g047e57-smarc.dts b/arch/arm64/boot/dts/renesas/r9a09g047e57-smarc.dts index 696903dc7a63..6372f582a7c4 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g047e57-smarc.dts +++ b/arch/arm64/boot/dts/renesas/r9a09g047e57-smarc.dts @@ -122,6 +122,11 @@ #endif }; +&pcie { + pinctrl-0 = <&pcie_pins>; + pinctrl-names = "default"; +}; + &pinctrl { canfd_pins: canfd { can1_pins: can1 { @@ -145,6 +150,17 @@ input-schmitt-enable; }; + pcie-clkreq-n-hog { + gpio-hog; + gpios = ; + output-low; + line-name = "PCIE_M2B_CKREQ"; + }; + + pcie_pins: pcie { + pinmux = ; /* PCIE_RST_OUT# */ + }; + rsci2_pins: rsci2 { pinmux = , /* RXD2 */ , /* TXD2 */ @@ -167,6 +183,13 @@ bias-pull-up; }; + rspi0_pins: rspi0 { + pinmux = , /* MISOA */ + , /* MOSIA */ + , /* RSPCKA */ + ; /* SSLA0 */ + }; + scif_pins: scif { pins = "SCIF_TXD", "SCIF_RXD"; renesas,output-impedance = <1>; @@ -234,6 +257,15 @@ }; #endif +&rspi0 { + pinctrl-0 = <&rspi0_pins>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; +}; + &scif0 { pinctrl-0 = <&scif_pins>; pinctrl-names = "default"; @@ -248,7 +280,13 @@ vqmmc-supply = <&vqmmc_sd1_pvdd>; }; +&usb3_phy { + status = "okay"; +}; + &xhci { pinctrl-0 = <&usb3_pins>; pinctrl-names = "default"; + + status = "okay"; }; diff --git a/arch/arm64/boot/dts/renesas/r9a09g056.dtsi b/arch/arm64/boot/dts/renesas/r9a09g056.dtsi index 9fb15ca24984..40525470194e 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g056.dtsi +++ b/arch/arm64/boot/dts/renesas/r9a09g056.dtsi @@ -599,16 +599,6 @@ status = "disabled"; }; - wdt0: watchdog@11c00400 { - compatible = "renesas,r9a09g056-wdt", "renesas,r9a09g057-wdt"; - reg = <0 0x11c00400 0 0x400>; - clocks = <&cpg CPG_MOD 0x4b>, <&cpg CPG_MOD 0x4c>; - clock-names = "pclk", "oscclk"; - resets = <&cpg 0x75>; - power-domains = <&cpg>; - status = "disabled"; - }; - wdt1: watchdog@14400000 { compatible = "renesas,r9a09g056-wdt", "renesas,r9a09g057-wdt"; reg = <0 0x14400000 0 0x400>; @@ -619,23 +609,18 @@ status = "disabled"; }; - wdt2: watchdog@13000000 { - compatible = "renesas,r9a09g056-wdt", "renesas,r9a09g057-wdt"; - reg = <0 0x13000000 0 0x400>; - clocks = <&cpg CPG_MOD 0x4f>, <&cpg CPG_MOD 0x50>; - clock-names = "pclk", "oscclk"; - resets = <&cpg 0x77>; - power-domains = <&cpg>; - status = "disabled"; - }; - - wdt3: watchdog@13000400 { - compatible = "renesas,r9a09g056-wdt", "renesas,r9a09g057-wdt"; - reg = <0 0x13000400 0 0x400>; - clocks = <&cpg CPG_MOD 0x51>, <&cpg CPG_MOD 0x52>; - clock-names = "pclk", "oscclk"; - resets = <&cpg 0x78>; + rtc: rtc@11c00800 { + compatible = "renesas,r9a09g056-rtca3", "renesas,rz-rtca3"; + reg = <0 0x11c00800 0 0x400>; + interrupts = , + , + ; + interrupt-names = "alarm", "period", "carry"; + clocks = <&cpg CPG_MOD 0x53>, <&rtxin_clk>; + clock-names = "bus", "counter"; power-domains = <&cpg>; + resets = <&cpg 0x79>, <&cpg 0x7a>; + reset-names = "rtc", "rtest"; status = "disabled"; }; @@ -769,6 +754,13 @@ clock-names = "pclk", "pclk_sfr", "tclk"; resets = <&cpg 0x7b>, <&cpg 0x7c>; reset-names = "presetn", "tresetn"; + dmas = <&dmac0 0x448c>, <&dmac0 0x448d>, + <&dmac1 0x448c>, <&dmac1 0x448d>, + <&dmac2 0x448c>, <&dmac2 0x448d>, + <&dmac3 0x448c>, <&dmac3 0x448d>, + <&dmac4 0x448c>, <&dmac4 0x448d>; + dma-names = "rx", "tx", "rx", "tx", "rx", + "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; @@ -790,6 +782,13 @@ clock-names = "pclk", "pclk_sfr", "tclk"; resets = <&cpg 0x7d>, <&cpg 0x7e>; reset-names = "presetn", "tresetn"; + dmas = <&dmac0 0x448e>, <&dmac0 0x448f>, + <&dmac1 0x448e>, <&dmac1 0x448f>, + <&dmac2 0x448e>, <&dmac2 0x448f>, + <&dmac3 0x448e>, <&dmac3 0x448f>, + <&dmac4 0x448e>, <&dmac4 0x448f>; + dma-names = "rx", "tx", "rx", "tx", "rx", + "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; @@ -811,6 +810,13 @@ clock-names = "pclk", "pclk_sfr", "tclk"; resets = <&cpg 0x7f>, <&cpg 0x80>; reset-names = "presetn", "tresetn"; + dmas = <&dmac0 0x4490>, <&dmac0 0x4491>, + <&dmac1 0x4490>, <&dmac1 0x4491>, + <&dmac2 0x4490>, <&dmac2 0x4491>, + <&dmac3 0x4490>, <&dmac3 0x4491>, + <&dmac4 0x4490>, <&dmac4 0x4491>; + dma-names = "rx", "tx", "rx", "tx", "rx", + "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm64/boot/dts/renesas/r9a09g056n48-rzv2n-evk.dts b/arch/arm64/boot/dts/renesas/r9a09g056n48-rzv2n-evk.dts index 9af50198d2f1..00e5455ea5ab 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g056n48-rzv2n-evk.dts +++ b/arch/arm64/boot/dts/renesas/r9a09g056n48-rzv2n-evk.dts @@ -260,12 +260,12 @@ &mdio0 { phy0: ethernet-phy@0 { - compatible = "ethernet-phy-id0022.1640", "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <0>; rxc-skew-psec = <0>; txc-skew-psec = <0>; rxdv-skew-psec = <0>; - txdv-skew-psec = <0>; + txen-skew-psec = <0>; rxd0-skew-psec = <0>; rxd1-skew-psec = <0>; rxd2-skew-psec = <0>; @@ -279,12 +279,12 @@ &mdio1 { phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1640", "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <0>; rxc-skew-psec = <0>; txc-skew-psec = <0>; rxdv-skew-psec = <0>; - txdv-skew-psec = <0>; + txen-skew-psec = <0>; rxd0-skew-psec = <0>; rxd1-skew-psec = <0>; rxd2-skew-psec = <0>; @@ -446,6 +446,10 @@ clock-frequency = <24000000>; }; +&rtc { + status = "okay"; +}; + &rtxin_clk { clock-frequency = <32768>; }; diff --git a/arch/arm64/boot/dts/renesas/r9a09g057.dtsi b/arch/arm64/boot/dts/renesas/r9a09g057.dtsi index 504c28386622..9581af58024e 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g057.dtsi +++ b/arch/arm64/boot/dts/renesas/r9a09g057.dtsi @@ -735,6 +735,13 @@ clock-names = "pclk", "pclk_sfr", "tclk"; resets = <&cpg 0x7b>, <&cpg 0x7c>; reset-names = "presetn", "tresetn"; + dmas = <&dmac0 0x448c>, <&dmac0 0x448d>, + <&dmac1 0x448c>, <&dmac1 0x448d>, + <&dmac2 0x448c>, <&dmac2 0x448d>, + <&dmac3 0x448c>, <&dmac3 0x448d>, + <&dmac4 0x448c>, <&dmac4 0x448d>; + dma-names = "rx", "tx", "rx", "tx", "rx", + "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; @@ -756,6 +763,13 @@ clock-names = "pclk", "pclk_sfr", "tclk"; resets = <&cpg 0x7d>, <&cpg 0x7e>; reset-names = "presetn", "tresetn"; + dmas = <&dmac0 0x448e>, <&dmac0 0x448f>, + <&dmac1 0x448e>, <&dmac1 0x448f>, + <&dmac2 0x448e>, <&dmac2 0x448f>, + <&dmac3 0x448e>, <&dmac3 0x448f>, + <&dmac4 0x448e>, <&dmac4 0x448f>; + dma-names = "rx", "tx", "rx", "tx", "rx", + "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; @@ -777,6 +791,13 @@ clock-names = "pclk", "pclk_sfr", "tclk"; resets = <&cpg 0x7f>, <&cpg 0x80>; reset-names = "presetn", "tresetn"; + dmas = <&dmac0 0x4490>, <&dmac0 0x4491>, + <&dmac1 0x4490>, <&dmac1 0x4491>, + <&dmac2 0x4490>, <&dmac2 0x4491>, + <&dmac3 0x4490>, <&dmac3 0x4491>, + <&dmac4 0x4490>, <&dmac4 0x4491>; + dma-names = "rx", "tx", "rx", "tx", "rx", + "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts b/arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts index dc4577ebf2e9..bd69109a5086 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts +++ b/arch/arm64/boot/dts/renesas/r9a09g057h44-rzv2h-evk.dts @@ -108,6 +108,12 @@ states = <3300000 0>, <1800000 1>; }; + x1: x1-clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + }; + /* 32.768kHz crystal */ x6: x6-clock { compatible = "fixed-clock"; @@ -277,16 +283,35 @@ clocks = <&x6>; clock-names = "xin"; }; + + versa3: clock-generator@69 { + compatible = "renesas,5l35023"; + reg = <0x69>; + clocks = <&x1>; + #clock-cells = <1>; + assigned-clocks = <&versa3 0>, /* qextal_clk */ + <&versa3 1>, + <&versa3 2>, /* rtxin_clk */ + <&versa3 3>, + <&versa3 4>, + <&versa3 5>; + assigned-clock-rates = <24000000>, + <24576000>, + <32768>, + <22579200>, + <100000000>, + <100000000>; + }; }; &mdio0 { phy0: ethernet-phy@0 { - compatible = "ethernet-phy-id0022.1640", "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <0>; rxc-skew-psec = <0>; txc-skew-psec = <0>; rxdv-skew-psec = <0>; - txdv-skew-psec = <0>; + txen-skew-psec = <0>; rxd0-skew-psec = <0>; rxd1-skew-psec = <0>; rxd2-skew-psec = <0>; @@ -300,12 +325,12 @@ &mdio1 { phy1: ethernet-phy@1 { - compatible = "ethernet-phy-id0022.1640", "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <0>; rxc-skew-psec = <0>; txc-skew-psec = <0>; rxdv-skew-psec = <0>; - txdv-skew-psec = <0>; + txen-skew-psec = <0>; rxd0-skew-psec = <0>; rxd1-skew-psec = <0>; rxd2-skew-psec = <0>; diff --git a/arch/arm64/boot/dts/renesas/r9a09g057h48-kakip-pixpaper.dtso b/arch/arm64/boot/dts/renesas/r9a09g057h48-kakip-pixpaper.dtso new file mode 100644 index 000000000000..7b8209494b73 --- /dev/null +++ b/arch/arm64/boot/dts/renesas/r9a09g057h48-kakip-pixpaper.dtso @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device Tree Overlay for Mayqueen (Open-EP Community) pixpaper display + * support on Renesas RZ/V2H platform (KAKIP board). + * + * Copyright (C) 2026 Wig Cheng + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&pinctrl { + rspi0_pins: rspi0 { + pinmux = , /* SPI0 MOSI */ + , /* SPI0 MISO */ + , /* SPI0 CLK */ + ; /* SPI0 CE0 */ + }; +}; + +&rspi0 { + pinctrl-0 = <&rspi0_pins>; + pinctrl-names = "default"; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + display@0 { + compatible = "mayqueen,pixpaper"; + reg = <0>; + spi-max-frequency = <1000000>; + reset-gpios = <&pinctrl RZV2H_GPIO(A, 7) GPIO_ACTIVE_HIGH>; + busy-gpios = <&pinctrl RZV2H_GPIO(B, 3) GPIO_ACTIVE_HIGH>; + dc-gpios = <&pinctrl RZV2H_GPIO(7, 4) GPIO_ACTIVE_HIGH>; + }; +}; diff --git a/arch/arm64/boot/dts/renesas/r9a09g077.dtsi b/arch/arm64/boot/dts/renesas/r9a09g077.dtsi index 9d0b4d8d3d5b..3761551c9647 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g077.dtsi +++ b/arch/arm64/boot/dts/renesas/r9a09g077.dtsi @@ -8,6 +8,24 @@ #include #include +/* The IRQ_NS lines start at offset 16 in the ICU interrupt space */ +#define RZT2H_IRQ0 16 +#define RZT2H_IRQ1 17 +#define RZT2H_IRQ2 18 +#define RZT2H_IRQ3 19 +#define RZT2H_IRQ4 20 +#define RZT2H_IRQ5 21 +#define RZT2H_IRQ6 22 +#define RZT2H_IRQ7 23 +#define RZT2H_IRQ8 24 +#define RZT2H_IRQ9 25 +#define RZT2H_IRQ10 26 +#define RZT2H_IRQ11 27 +#define RZT2H_IRQ12 28 +#define RZT2H_IRQ13 29 +#define RZT2H_IRQ14 30 +#define RZT2H_IRQ15 31 + / { compatible = "renesas,r9a09g077"; #address-cells = <2>; @@ -200,6 +218,10 @@ clocks = <&cpg CPG_CORE R9A09G077_CLK_PCLKM>, <&cpg CPG_MOD 104>; clock-names = "pclk", "pclkspi"; + dmas = <&dmac0 0x267a>, <&dmac0 0x267b>, + <&dmac1 0x267a>, <&dmac1 0x267b>, + <&dmac2 0x267a>, <&dmac2 0x267b>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; @@ -218,6 +240,10 @@ clocks = <&cpg CPG_CORE R9A09G077_CLK_PCLKM>, <&cpg CPG_MOD 105>; clock-names = "pclk", "pclkspi"; + dmas = <&dmac0 0x267f>, <&dmac0 0x2680>, + <&dmac1 0x267f>, <&dmac1 0x2680>, + <&dmac2 0x267f>, <&dmac2 0x2680>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; @@ -236,6 +262,10 @@ clocks = <&cpg CPG_CORE R9A09G077_CLK_PCLKM>, <&cpg CPG_MOD 106>; clock-names = "pclk", "pclkspi"; + dmas = <&dmac0 0x2684>, <&dmac0 0x2685>, + <&dmac1 0x2684>, <&dmac1 0x2685>, + <&dmac2 0x2684>, <&dmac2 0x2685>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; @@ -254,6 +284,10 @@ clocks = <&cpg CPG_CORE R9A09G077_CLK_PCLKM>, <&cpg CPG_MOD 602>; clock-names = "pclk", "pclkspi"; + dmas = <&dmac0 0x2689>, <&dmac0 0x268a>, + <&dmac1 0x2689>, <&dmac1 0x268a>, + <&dmac2 0x2689>, <&dmac2 0x268a>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm64/boot/dts/renesas/r9a09g077m44-rzt2h-evk.dts b/arch/arm64/boot/dts/renesas/r9a09g077m44-rzt2h-evk.dts index e9639bbb2d70..4c0e52850ca9 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g077m44-rzt2h-evk.dts +++ b/arch/arm64/boot/dts/renesas/r9a09g077m44-rzt2h-evk.dts @@ -14,12 +14,15 @@ /* * SD0 can be connected to either eMMC (IC49) or SD card slot CN31 * Lets by default enable the eMMC, note we need the below SW settings - * for eMMC. + * for eMMC. Also ensure that CN78 pins 5 and 6 are connected with a jumper + * to provide the SD0 power supply when using eMMC. * SW2[1] = ON; SW2[2] = ON * * To enable SD card and disable eMMC on SDHI0 disable the below macro - * and set the below switch setting: - * SW2[1] = OFF; SW2[2] = ON + * and set the switch as follows. Also ensure that CN78 pins 3 and 4 are connected + * with a jumper to provide the SD0 power supply when using an SD card. + * + * SW2[1] = OFF; SW2[2] = ON. */ #define SD0_EMMC 1 #define SD0_SD (!SD0_EMMC) @@ -224,10 +227,12 @@ }; &mdio1_phy { + interrupts-extended = <&icu RZT2H_IRQ3 IRQ_TYPE_EDGE_FALLING>; reset-gpios = <&pinctrl RZT2H_GPIO(32, 3) GPIO_ACTIVE_LOW>; }; &mdio2_phy { + interrupts-extended = <&icu RZT2H_IRQ13 IRQ_TYPE_EDGE_FALLING>; /* * PHY2 Reset Configuration: * @@ -248,6 +253,35 @@ ; /* CANTX0 */ }; + /* + * GMAC1 Pin Configuration: + * + * SW2[8] ON - use pins P33_2-P33_7, P34_0-P34_5, P34_7 and + * P35_0-P35_2 for Ethernet port 3 + */ + gmac1_pins: gmac1-pins { + pinmux = , /* ETH3_TXCLK */ + , /* ETH3_TXD0 */ + , /* ETH3_TXD1 */ + , /* ETH3_TXD2 */ + , /* ETH3_TXD3 */ + , /* ETH3_TXEN */ + , /* ETH3_RXCLK */ + , /* ETH3_RXD0 */ + , /* ETH3_RXD1 */ + , /* ETH3_RXD2 */ + , /* ETH3_RXD3 */ + , /* ETH3_RXDV */ + , /* ETH3_TXER */ + , /* ETH3_RXER */ + , /* ETH3_CRS */ + , /* ETH3_COL */ + , /* GMAC1_MDC */ + , /* GMAC1_MDIO */ + , /* ETH3_REFCLK */ + ; /* IRQ3 */ + }; + /* * GMAC2 Pin Configuration: * @@ -274,35 +308,8 @@ , /* ETH2_COL */ , /* GMAC2_MDC */ , /* GMAC2_MDIO */ - ; /* ETH2_REFCLK */ - }; - - /* - * GMAC1 Pin Configuration: - * - * SW2[8] ON - use pins P33_2-P33_7, P34_0-P34_5, P34_7 and - * P35_0-P35_2 for Ethernet port 3 - */ - gmac1_pins: gmac1-pins { - pinmux = , /* ETH3_TXCLK */ - , /* ETH3_TXD0 */ - , /* ETH3_TXD1 */ - , /* ETH3_TXD2 */ - , /* ETH3_TXD3 */ - , /* ETH3_TXEN */ - , /* ETH3_RXCLK */ - , /* ETH3_RXD0 */ - , /* ETH3_RXD1 */ - , /* ETH3_RXD2 */ - , /* ETH3_RXD3 */ - , /* ETH3_RXDV */ - , /* ETH3_TXER */ - , /* ETH3_RXER */ - , /* ETH3_CRS */ - , /* ETH3_COL */ - , /* GMAC1_MDC */ - , /* GMAC1_MDIO */ - ; /* ETH3_REFCLK */ + , /* ETH2_REFCLK */ + ; /* IRQ13 */ }; /* diff --git a/arch/arm64/boot/dts/renesas/r9a09g087.dtsi b/arch/arm64/boot/dts/renesas/r9a09g087.dtsi index d407c48f9966..f697e9698ed3 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g087.dtsi +++ b/arch/arm64/boot/dts/renesas/r9a09g087.dtsi @@ -8,6 +8,24 @@ #include #include +/* The IRQ_NS lines start at offset 16 in the ICU interrupt space */ +#define RZN2H_IRQ0 16 +#define RZN2H_IRQ1 17 +#define RZN2H_IRQ2 18 +#define RZN2H_IRQ3 19 +#define RZN2H_IRQ4 20 +#define RZN2H_IRQ5 21 +#define RZN2H_IRQ6 22 +#define RZN2H_IRQ7 23 +#define RZN2H_IRQ8 24 +#define RZN2H_IRQ9 25 +#define RZN2H_IRQ10 26 +#define RZN2H_IRQ11 27 +#define RZN2H_IRQ12 28 +#define RZN2H_IRQ13 29 +#define RZN2H_IRQ14 30 +#define RZN2H_IRQ15 31 + / { compatible = "renesas,r9a09g087"; #address-cells = <2>; @@ -200,6 +218,10 @@ clocks = <&cpg CPG_CORE R9A09G087_CLK_PCLKM>, <&cpg CPG_MOD 104>; clock-names = "pclk", "pclkspi"; + dmas = <&dmac0 0x267a>, <&dmac0 0x267b>, + <&dmac1 0x267a>, <&dmac1 0x267b>, + <&dmac2 0x267a>, <&dmac2 0x267b>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; @@ -218,6 +240,10 @@ clocks = <&cpg CPG_CORE R9A09G087_CLK_PCLKM>, <&cpg CPG_MOD 105>; clock-names = "pclk", "pclkspi"; + dmas = <&dmac0 0x267f>, <&dmac0 0x2680>, + <&dmac1 0x267f>, <&dmac1 0x2680>, + <&dmac2 0x267f>, <&dmac2 0x2680>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; @@ -236,6 +262,10 @@ clocks = <&cpg CPG_CORE R9A09G087_CLK_PCLKM>, <&cpg CPG_MOD 106>; clock-names = "pclk", "pclkspi"; + dmas = <&dmac0 0x2684>, <&dmac0 0x2685>, + <&dmac1 0x2684>, <&dmac1 0x2685>, + <&dmac2 0x2684>, <&dmac2 0x2685>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; @@ -254,6 +284,10 @@ clocks = <&cpg CPG_CORE R9A09G087_CLK_PCLKM>, <&cpg CPG_MOD 602>; clock-names = "pclk", "pclkspi"; + dmas = <&dmac0 0x2689>, <&dmac0 0x268a>, + <&dmac1 0x2689>, <&dmac1 0x268a>, + <&dmac2 0x2689>, <&dmac2 0x268a>; + dma-names = "rx", "tx", "rx", "tx", "rx", "tx"; power-domains = <&cpg>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm64/boot/dts/renesas/r9a09g087m44-rzn2h-evk.dts b/arch/arm64/boot/dts/renesas/r9a09g087m44-rzn2h-evk.dts index 19f0a2c06753..ef6cc7497c2c 100644 --- a/arch/arm64/boot/dts/renesas/r9a09g087m44-rzn2h-evk.dts +++ b/arch/arm64/boot/dts/renesas/r9a09g087m44-rzn2h-evk.dts @@ -14,12 +14,14 @@ /* * SD0 can be connected to either eMMC (U33) or SD card slot CN21 * Lets by default enable the eMMC, note we need the below SW settings - * for eMMC. + * for eMMC. Also ensure that JP23 pins 5 and 6 are connected with a jumper + * to provide the SD0 power supply when using eMMC. * DSW5[1] = ON; DSW5[2] = ON * DSW17[5] = OFF; DSW17[6] = ON * * To enable SD card and disable eMMC on SDHI0 disable the below macro - * and set the below switch setting: + * and set the below switch settings. Also ensure that JP23 pins 3 and 4 are + * connected with a jumper to provide the SD0 power supply when using an SD card. * DSW5[1] = OFF; DSW5[2] = ON * P22_6 = SD0_WP; DSW15[1] = OFF; DSW15[2] = ON * P22_5 = SD0_CD; DSW15[3] = OFF; DSW15[4] = ON @@ -303,6 +305,7 @@ }; &mdio1_phy { + interrupts-extended = <&icu RZN2H_IRQ15 IRQ_TYPE_EDGE_FALLING>; /* * PHY3 Reset Configuration: * @@ -312,6 +315,7 @@ }; &mdio2_phy { + interrupts-extended = <&icu RZN2H_IRQ14 IRQ_TYPE_EDGE_FALLING>; /* * PHY2 Reset Configuration: * @@ -333,37 +337,7 @@ }; /* - * GMAC2 Pin Configuration: - * - * DSW5[6] OFF - connect MDC/MDIO of Ethernet port 2 to GMAC2 - * DSW5[7] ON - use pins P29_1-P29_7, P30_0-P30_4, P30_7, - * P31_2, P31_4 and P31_5 are used for Ethernet port 2 - */ - gmac2_pins: gmac2-pins { - pinmux = , /* ETH2_TXCLK */ - , /* ETH2_TXD0 */ - , /* ETH2_TXD1 */ - , /* ETH2_TXD2 */ - , /* ETH2_TXD3 */ - , /* ETH2_TXEN */ - , /* ETH2_RXCLK */ - , /* ETH2_RXD0 */ - , /* ETH2_RXD1 */ - , /* ETH2_RXD2 */ - , /* ETH2_RXD3 */ - , /* ETH2_RXDV */ - , /* ETH2_TXER */ - , /* ETH2_RXER */ - , /* ETH2_CRS */ - , /* ETH2_COL */ - , /* GMAC2_MDC */ - , /* GMAC2_MDIO */ - ; /* ETH2_REFCLK */ - - }; - - /* - * GMAC2 Pin Configuration: + * GMAC1 Pin Configuration: * * DSW5[8] ON - use pins P00_0-P00_2, P33_2-P33_7, P34_0-P34_6 * for Ethernet port 3 @@ -388,7 +362,40 @@ , /* ETH3_COL */ , /* GMAC1_MDC */ , /* GMAC1_MDIO */ - ; /* ETH3_REFCLK */ + , /* ETH3_REFCLK */ + ; /* IRQ15 */ + }; + + /* + * GMAC2 Pin Configuration: + * + * DSW5[6] OFF - connect MDC/MDIO of Ethernet port 2 to GMAC2 + * DSW5[7] ON - use pins P29_1-P29_7, P30_0-P30_4, P30_7, + * P31_2, P31_4 and P31_5 are used for Ethernet port 2 + * DSW13[7] OFF; DSW13[8] ON - use pin P13_7 for IRQ14 + */ + gmac2_pins: gmac2-pins { + pinmux = , /* ETH2_TXCLK */ + , /* ETH2_TXD0 */ + , /* ETH2_TXD1 */ + , /* ETH2_TXD2 */ + , /* ETH2_TXD3 */ + , /* ETH2_TXEN */ + , /* ETH2_RXCLK */ + , /* ETH2_RXD0 */ + , /* ETH2_RXD1 */ + , /* ETH2_RXD2 */ + , /* ETH2_RXD3 */ + , /* ETH2_RXDV */ + , /* ETH2_TXER */ + , /* ETH2_RXER */ + , /* ETH2_CRS */ + , /* ETH2_COL */ + , /* GMAC2_MDC */ + , /* GMAC2_MDIO */ + , /* ETH2_REFCLK */ + ; /* IRQ14 */ + }; /* diff --git a/arch/arm64/boot/dts/renesas/renesas-smarc2.dtsi b/arch/arm64/boot/dts/renesas/renesas-smarc2.dtsi index b607b5d6c259..696a933af808 100644 --- a/arch/arm64/boot/dts/renesas/renesas-smarc2.dtsi +++ b/arch/arm64/boot/dts/renesas/renesas-smarc2.dtsi @@ -96,6 +96,10 @@ clock-frequency = <400000>; }; +&pcie { + status = "okay"; +}; + &scif0 { status = "okay"; }; @@ -107,11 +111,3 @@ status = "okay"; }; - -&usb3_phy { - status = "okay"; -}; - -&xhci { - status = "okay"; -}; diff --git a/arch/arm64/boot/dts/renesas/rzg2l-smarc-som.dtsi b/arch/arm64/boot/dts/renesas/rzg2l-smarc-som.dtsi index d511e152d7c6..7eccdaffb221 100644 --- a/arch/arm64/boot/dts/renesas/rzg2l-smarc-som.dtsi +++ b/arch/arm64/boot/dts/renesas/rzg2l-smarc-som.dtsi @@ -99,8 +99,7 @@ status = "okay"; phy0: ethernet-phy@7 { - compatible = "ethernet-phy-id0022.1640", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <7>; interrupts-extended = <&irqc RZG2L_IRQ2 IRQ_TYPE_LEVEL_LOW>; rxc-skew-psec = <2400>; @@ -126,8 +125,7 @@ status = "okay"; phy1: ethernet-phy@7 { - compatible = "ethernet-phy-id0022.1640", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <7>; interrupts-extended = <&irqc RZG2L_IRQ3 IRQ_TYPE_LEVEL_LOW>; rxc-skew-psec = <2400>; diff --git a/arch/arm64/boot/dts/renesas/rzg2lc-smarc-som.dtsi b/arch/arm64/boot/dts/renesas/rzg2lc-smarc-som.dtsi index 3e8909a872e3..15f2e9eaaf0b 100644 --- a/arch/arm64/boot/dts/renesas/rzg2lc-smarc-som.dtsi +++ b/arch/arm64/boot/dts/renesas/rzg2lc-smarc-som.dtsi @@ -79,8 +79,7 @@ status = "okay"; phy0: ethernet-phy@7 { - compatible = "ethernet-phy-id0022.1640", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <7>; interrupts-extended = <&irqc RZG2L_IRQ0 IRQ_TYPE_LEVEL_LOW>; rxc-skew-psec = <2400>; diff --git a/arch/arm64/boot/dts/renesas/rzg2ul-smarc-som.dtsi b/arch/arm64/boot/dts/renesas/rzg2ul-smarc-som.dtsi index cd4275d86935..0f917d7c9939 100644 --- a/arch/arm64/boot/dts/renesas/rzg2ul-smarc-som.dtsi +++ b/arch/arm64/boot/dts/renesas/rzg2ul-smarc-som.dtsi @@ -75,8 +75,7 @@ status = "okay"; phy0: ethernet-phy@7 { - compatible = "ethernet-phy-id0022.1640", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <7>; interrupts-extended = <&irqc RZG2L_IRQ2 IRQ_TYPE_LEVEL_LOW>; rxc-skew-psec = <2400>; @@ -103,8 +102,7 @@ status = "okay"; phy1: ethernet-phy@7 { - compatible = "ethernet-phy-id0022.1640", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <7>; interrupts-extended = <&irqc RZG2L_IRQ7 IRQ_TYPE_LEVEL_LOW>; rxc-skew-psec = <2400>; diff --git a/arch/arm64/boot/dts/renesas/rzg3e-smarc-som.dtsi b/arch/arm64/boot/dts/renesas/rzg3e-smarc-som.dtsi index 3b571c096752..d978619155d2 100644 --- a/arch/arm64/boot/dts/renesas/rzg3e-smarc-som.dtsi +++ b/arch/arm64/boot/dts/renesas/rzg3e-smarc-som.dtsi @@ -43,6 +43,12 @@ reg = <0x0 0x48000000 0x0 0xf8000000>; }; + pcie_refclk: pcie-ref-clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <100000000>; + }; + reg_1p8v: regulator-1p8v { compatible = "regulator-fixed"; regulator-name = "fixed-1.8V"; @@ -136,14 +142,13 @@ &mdio0 { phy0: ethernet-phy@7 { - compatible = "ethernet-phy-id0022.1640", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <7>; interrupts-extended = <&icu 3 IRQ_TYPE_LEVEL_LOW>; rxc-skew-psec = <1400>; txc-skew-psec = <1400>; rxdv-skew-psec = <0>; - txdv-skew-psec = <0>; + txen-skew-psec = <0>; rxd0-skew-psec = <0>; rxd1-skew-psec = <0>; rxd2-skew-psec = <0>; @@ -157,14 +162,13 @@ &mdio1 { phy1: ethernet-phy@7 { - compatible = "ethernet-phy-id0022.1640", - "ethernet-phy-ieee802.3-c22"; + compatible = "ethernet-phy-id0022.1640"; reg = <7>; interrupts-extended = <&icu 16 IRQ_TYPE_LEVEL_LOW>; rxc-skew-psec = <1400>; txc-skew-psec = <1400>; rxdv-skew-psec = <0>; - txdv-skew-psec = <0>; + txen-skew-psec = <0>; rxd0-skew-psec = <0>; rxd1-skew-psec = <0>; rxd2-skew-psec = <0>; @@ -176,6 +180,11 @@ }; }; +&pcie_port0 { + clocks = <&pcie_refclk>; + clock-names = "ref"; +}; + &pinctrl { eth0_pins: eth0 { clk { diff --git a/arch/arm64/boot/dts/renesas/rzg3l-smarc-som.dtsi b/arch/arm64/boot/dts/renesas/rzg3l-smarc-som.dtsi new file mode 100644 index 000000000000..ab4950671c7c --- /dev/null +++ b/arch/arm64/boot/dts/renesas/rzg3l-smarc-som.dtsi @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Device Tree Source for R9A08G046L48 SMARC SoM board. + * + * Copyright (C) 2026 Renesas Electronics Corp. + */ + +/ { + compatible = "renesas,rzg3l-smarcm", "renesas,r9a08g046l48", "renesas,r9a08g046"; + + memory@48000000 { + device_type = "memory"; + /* First 128MiB is reserved for secure area. */ + reg = <0x0 0x48000000 0x0 0x78000000>; + }; +}; + +&extal_clk { + clock-frequency = <24000000>; +}; diff --git a/arch/arm64/boot/dts/renesas/salvator-common.dtsi b/arch/arm64/boot/dts/renesas/salvator-common.dtsi index d4a921bed4c3..e505161caa67 100644 --- a/arch/arm64/boot/dts/renesas/salvator-common.dtsi +++ b/arch/arm64/boot/dts/renesas/salvator-common.dtsi @@ -75,6 +75,12 @@ enable-gpios = <&gpio6 7 GPIO_ACTIVE_HIGH>; }; + pcie_usb_refclk: clk-x7 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + cvbs-in { compatible = "composite-video-connector"; label = "CVBS IN"; @@ -523,6 +529,13 @@ #gpio-cells = <2>; }; + pcie_usb_clk: clk@68 { + compatible = "renesas,9fgv0841"; + reg = <0x68>; + clocks = <&pcie_usb_refclk>; + #clock-cells = <1>; + }; + video-receiver@70 { compatible = "adi,adv7482"; reg = <0x70 0x71 0x72 0x73 0x74 0x75 @@ -640,17 +653,27 @@ }; &pcie_bus_clk { - clock-frequency = <100000000>; + status = "disabled"; }; &pciec0 { + clocks = <&cpg CPG_MOD 319>, <&pcie_usb_clk 1>; status = "okay"; }; +&pciec0_rp { + clocks = <&pcie_usb_clk 3>; +}; + &pciec1 { + clocks = <&cpg CPG_MOD 318>, <&pcie_usb_clk 2>; status = "okay"; }; +&pciec1_rp { + clocks = <&pcie_usb_clk 4>; +}; + &pfc { pinctrl-0 = <&scif_clk_pins>; pinctrl-names = "default"; @@ -1038,11 +1061,12 @@ }; &usb3_phy0 { + clocks = <&cpg CPG_MOD 328>, <&pcie_usb_clk 6>, <&usb_extal_clk>; status = "okay"; }; &usb3s0_clk { - clock-frequency = <100000000>; + status = "disabled"; }; &vin0 { diff --git a/arch/arm64/boot/dts/renesas/ulcb-kf.dtsi b/arch/arm64/boot/dts/renesas/ulcb-kf.dtsi index 2a157d1efb3d..97014bcfbb1d 100644 --- a/arch/arm64/boot/dts/renesas/ulcb-kf.dtsi +++ b/arch/arm64/boot/dts/renesas/ulcb-kf.dtsi @@ -351,20 +351,30 @@ }; &pcie_bus_clk { - clock-frequency = <100000000>; + status = "disabled"; }; &pciec0 { + clocks = <&cpg CPG_MOD 319>, <&pcie_usb_clk 1>; status = "okay"; }; +&pciec0_rp { + clocks = <&pcie_usb_clk 3>; +}; + &pciec1 { + clocks = <&cpg CPG_MOD 318>, <&pcie_usb_clk 2>; status = "okay"; vpcie1v5-supply = <&pcie_1v5>; vpcie3v3-supply = <&pcie_3v3>; }; +&pciec1_rp { + clocks = <&pcie_usb_clk 4>; +}; + &pfc { can0_pins: can0 { groups = "can0_data_a"; @@ -475,6 +485,15 @@ status = "okay"; }; +&usb3_phy0 { + clocks = <&cpg CPG_MOD 328>, <&pcie_usb_clk 6>, <&usb_extal_clk>; + status = "okay"; +}; + +&usb3s0_clk { + status = "disabled"; +}; + &xhci0 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/renesas/ulcb.dtsi b/arch/arm64/boot/dts/renesas/ulcb.dtsi index 241caf737abb..67fd6a65db89 100644 --- a/arch/arm64/boot/dts/renesas/ulcb.dtsi +++ b/arch/arm64/boot/dts/renesas/ulcb.dtsi @@ -47,6 +47,12 @@ clock-frequency = <12288000>; }; + pcie_usb_refclk: clk-x24 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + hdmi0-out { compatible = "hdmi-connector"; type = "a"; @@ -232,6 +238,13 @@ clock-frequency = <400000>; + pcie_usb_clk: clk@68 { + compatible = "renesas,9fgv0841"; + reg = <0x68>; + clocks = <&pcie_usb_refclk>; + #clock-cells = <1>; + }; + versaclock5: clock-generator@6a { compatible = "idt,5p49v5925"; reg = <0x6a>; diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile index 4d384f153c13..cb55c6b70d0e 100644 --- a/arch/arm64/boot/dts/rockchip/Makefile +++ b/arch/arm64/boot/dts/rockchip/Makefile @@ -63,6 +63,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-khadas-edge-v.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-kobol-helios64.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-leez-p710.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopc-t4.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopc-t4-hd702e.dtbo dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopi-m4.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopi-m4b.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopi-neo4.dtb @@ -104,6 +105,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-anbernic-rg353v.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-anbernic-rg353vs.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-anbernic-rg503.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-odroid-m1s.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-onething-edge-cube.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-orangepi-3b-v1.1.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-orangepi-3b-v2.1.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-pinenote-v1.1.dtb @@ -116,6 +118,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-powkiddy-rgb30.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-powkiddy-rk2023.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-powkiddy-x55.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-qnap-ts133.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-qnap-ts133-pcb-13.dtbo dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-quartz64-a.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-quartz64-b.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-radxa-cm3-io.dtb @@ -150,7 +153,9 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-nanopi-r5s.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-odroid-m1.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-photonicat.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-qnap-ts233.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-qnap-ts233-pcb-12-11.dtbo dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-qnap-ts433.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-qnap-ts433-pcb-12-10.dtbo dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-radxa-cm3j-rpi-cm4.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-radxa-e25.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-roc-pc.dtb @@ -164,6 +169,8 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-armsom-sige5.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-armsom-sige5-v1.2-wifibt.dtbo dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-evb1-v10.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-evb1-v10-pcie1.dtbo +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-evb2-v10.dtb +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-khadas-edge-2l.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-luckfox-omni3576.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-nanopi-m5.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-nanopi-r76s.dtb @@ -245,6 +252,10 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += px30-ringneck-haikou-haikou-video-demo.dtb px30-ringneck-haikou-haikou-video-demo-dtbs := px30-ringneck-haikou.dtb \ px30-ringneck-haikou-video-demo.dtbo +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-nanopc-t4-hd702e.dtb +rk3399-nanopc-t4-hd702e-dtbs := rk3399-nanopc-t4.dtb \ + rk3399-nanopc-t4-hd702e.dtbo + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-lion-haikou-haikou-video-demo.dtb rk3368-lion-haikou-haikou-video-demo-dtbs := rk3368-lion-haikou.dtb \ rk3368-lion-haikou-video-demo.dtbo @@ -261,6 +272,18 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-rockpro64-v2-screen.dtb rk3399-rockpro64-v2-screen-dtbs := rk3399-rockpro64-v2.dtb \ rk3399-rockpro64-screen.dtbo +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3566-qnap-ts133-pcb-13.dtb +rk3566-qnap-ts133-pcb-13-dtbs := rk3566-qnap-ts133.dtb \ + rk3566-qnap-ts133-pcb-13.dtbo + +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-qnap-ts233-pcb-12-11.dtb +rk3568-qnap-ts233-pcb-12-11-dtbs := rk3568-qnap-ts233.dtb \ + rk3568-qnap-ts233-pcb-12-11.dtbo + +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-qnap-ts433-pcb-12-10.dtb +rk3568-qnap-ts433-pcb-12-10-dtbs := rk3568-qnap-ts433.dtb \ + rk3568-qnap-ts433-pcb-12-10.dtbo + dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-wolfvision-pf5-vz-2-uhd.dtb rk3568-wolfvision-pf5-vz-2-uhd-dtbs := rk3568-wolfvision-pf5.dtb \ rk3568-wolfvision-pf5-display-vz.dtbo \ diff --git a/arch/arm64/boot/dts/rockchip/rk3328-a1.dts b/arch/arm64/boot/dts/rockchip/rk3328-a1.dts index 30bdb38f0727..e810ed146451 100644 --- a/arch/arm64/boot/dts/rockchip/rk3328-a1.dts +++ b/arch/arm64/boot/dts/rockchip/rk3328-a1.dts @@ -58,24 +58,6 @@ gpios = <&gpio2 RK_PA2 GPIO_ACTIVE_LOW>; linux,rc-map-name = "rc-beelink-gs1"; }; - - spdif_dit: spdif-dit { - compatible = "linux,spdif-dit"; - #sound-dai-cells = <0>; - }; - - spdif_sound: spdif-sound { - compatible = "simple-audio-card"; - simple-audio-card,name = "SPDIF"; - - simple-audio-card,cpu { - sound-dai = <&spdif>; - }; - - simple-audio-card,codec { - sound-dai = <&spdif_dit>; - }; - }; }; &analog_sound { @@ -343,11 +325,6 @@ status = "okay"; }; -&spdif { - pinctrl-0 = <&spdifm0_tx>; - status = "okay"; -}; - &tsadc { rockchip,hw-tshut-mode = <0>; rockchip,hw-tshut-polarity = <0>; diff --git a/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2s-plus.dts b/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2s-plus.dts index f72b1518c14f..4eff0503928a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2s-plus.dts +++ b/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2s-plus.dts @@ -29,20 +29,3 @@ pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>; status = "okay"; }; - -&gmac2io { - phy-handle = <&rtl8211e>; - tx_delay = <0x24>; - rx_delay = <0x18>; - - mdio { - rtl8211e: ethernet-phy@1 { - reg = <1>; - pinctrl-0 = <ð_phy_reset_pin>; - pinctrl-names = "default"; - reset-assert-us = <10000>; - reset-deassert-us = <50000>; - reset-gpios = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; - }; - }; -}; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi index 4dcceb9136b7..49c68fe65de6 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-base.dtsi @@ -2145,8 +2145,6 @@ interrupts = ; clocks = <&cru PCLK_EDP>, <&cru PCLK_EDP_CTRL>, <&cru PCLK_VIO_GRF>; clock-names = "dp", "pclk", "grf"; - pinctrl-names = "default"; - pinctrl-0 = <&edp_hpd>; power-domains = <&power RK3399_PD_EDP>; resets = <&cru SRST_P_EDP_CTRL>; reset-names = "dp"; @@ -2384,6 +2382,7 @@ }; edp { + /omit-if-no-ref/ edp_hpd: edp-hpd { rockchip,pins = <4 RK_PC7 2 &pcfg_pull_none>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi index 9d07353df52c..3f3cb0eb5809 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-gru-chromebook.dtsi @@ -241,6 +241,8 @@ }; &edp { + pinctrl-names = "default"; + pinctrl-0 = <&edp_hpd>; status = "okay"; /* diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4-hd702e.dtso b/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4-hd702e.dtso new file mode 100644 index 000000000000..766925b63571 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4-hd702e.dtso @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * FriendlyElec HD702E LCD on NanoPC-T4 board + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +&{/} { + vdd_3_3v: regulator-vdd_3_3v { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vdd_3.3v"; + vin-supply = <&vcc12v0_sys>; + }; + + backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pwm0 0 25000 0>; + enable-gpios = <&gpio4 RK_PD5 GPIO_ACTIVE_HIGH>; + brightness-levels = <0 255>; + default-brightness-level = <200>; + num-interpolated-steps = <255>; + pinctrl-0 = <&bl_en>; + pinctrl-names = "default"; + }; +}; + +&edp { + force-hpd; + status = "okay"; + + aux-bus { + panel { + compatible = "friendlyarm,hd702e"; + backlight = <&backlight>; + no-hpd; + power-supply = <&vdd_3_3v>; + + port { + panel_in_edp: endpoint { + remote-endpoint = <&edp_out_panel>; + }; + }; + }; + }; +}; + +&edp_out { + edp_out_panel: endpoint { + remote-endpoint = <&panel_in_edp>; + }; +}; + +&i2c4 { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen@5d { + compatible = "goodix,gt9271"; + reg = <0x5d>; + interrupt-parent = <&gpio1>; + interrupts = ; + irq-gpios = <&gpio1 RK_PC4 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 RK_PB5 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&touch_int &touch_rst>; + pinctrl-names = "default"; + AVDD28-supply = <&vdd_3_3v>; + VDDIO-supply = <&vdd_3_3v>; + }; +}; + +&pinctrl { + backlight { + bl_en: bl-en { + rockchip,pins = <4 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + touchscreen { + touch_int: touch-int { + rockchip,pins = <1 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + touch_rst: touch-rst { + rockchip,pins = <1 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi index c8eb5481f43d..9e4fea84c4fb 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi @@ -206,6 +206,8 @@ }; &hdmi { + avdd-0v9-supply = <&vcca0v9_s3>; + avdd-1v8-supply = <&vcca1v8_s3>; ddc-i2c-bus = <&i2c7>; pinctrl-names = "default"; pinctrl-0 = <&hdmi_cec>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts index ae937a3afa11..b5c05928142c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts @@ -401,8 +401,6 @@ &edp { force-hpd; - pinctrl-names = "default"; - pinctrl-0 = <&edp_hpd>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-sapphire-excavator.dts b/arch/arm64/boot/dts/rockchip/rk3399-sapphire-excavator.dts index a4ceafe6dd7a..80d6ea0eda84 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-sapphire-excavator.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire-excavator.dts @@ -141,6 +141,8 @@ }; &edp { + pinctrl-names = "default"; + pinctrl-0 = <&edp_hpd>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3528.dtsi b/arch/arm64/boot/dts/rockchip/rk3528.dtsi index d402f2828814..806b8109f67d 100644 --- a/arch/arm64/boot/dts/rockchip/rk3528.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3528.dtsi @@ -1190,6 +1190,53 @@ status = "disabled"; }; + otp: efuse@ffce0000 { + compatible = "rockchip,rk3528-otp"; + reg = <0x0 0xffce0000 0x0 0x4000>; + clocks = <&cru CLK_USER_OTPC_NS>, <&cru PCLK_OTPC_NS>, + <&cru CLK_SBPI_OTPC_NS>; + clock-names = "otp", "apb_pclk", "sbpi"; + resets = <&cru SRST_USER_OTPC_NS>, <&cru SRST_P_OTPC_NS>, + <&cru SRST_SBPI_OTPC_NS>; + reset-names = "otp", "apb", "sbpi"; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + cpu_code: cpu-code@2 { + reg = <0x02 0x2>; + }; + + otp_cpu_version: cpu-version@8 { + reg = <0x08 0x1>; + bits = <3 3>; + }; + + otp_id: id@a { + reg = <0x0a 0x10>; + }; + + cpu_leakage: cpu-leakage@1a { + reg = <0x1a 0x1>; + }; + + logic_leakage: logic-leakage@1b { + reg = <0x1b 0x1>; + }; + + gpu_leakage: gpu-leakage@1c { + reg = <0x1c 0x1>; + }; + + tsadc_trim: tsadc-trim@44 { + reg = <0x44 0x2>; + bits = <0 10>; + }; + }; + }; + dmac: dma-controller@ffd60000 { compatible = "arm,pl330", "arm,primecell"; reg = <0x0 0xffd60000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3562-evb2-v10.dts b/arch/arm64/boot/dts/rockchip/rk3562-evb2-v10.dts index 6a84db154a7d..387062eea520 100644 --- a/arch/arm64/boot/dts/rockchip/rk3562-evb2-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rk3562-evb2-v10.dts @@ -13,7 +13,7 @@ #include "rk3562.dtsi" / { - model = "Rockchip RK3562 EVB V20 Board"; + model = "Rockchip RK3562 EVB2 V10 Board"; compatible = "rockchip,rk3562-evb2-v10", "rockchip,rk3562"; chosen: chosen { diff --git a/arch/arm64/boot/dts/rockchip/rk3562.dtsi b/arch/arm64/boot/dts/rockchip/rk3562.dtsi index f84676b47b27..e4816aa3dae0 100644 --- a/arch/arm64/boot/dts/rockchip/rk3562.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3562.dtsi @@ -1093,6 +1093,52 @@ status = "disabled"; }; + otp: efuse@ff930000 { + compatible = "rockchip,rk3562-otp"; + reg = <0x0 0xff930000 0x0 0x4000>; + clocks = <&cru CLK_USER_OTPC_NS>, <&cru PCLK_OTPC_NS>, + <&cru PCLK_OTPPHY>, <&cru CLK_SBPI_OTPC_NS>; + clock-names = "otp", "apb_pclk", "phy", "sbpi"; + resets = <&cru SRST_USER_OTPC_NS>, <&cru SRST_P_OTPC_NS>, + <&cru SRST_P_OTPPHY>, <&cru SRST_SBPI_OTPC_NS>; + reset-names = "otp", "apb", "phy", "sbpi"; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + cpu_code: cpu-code@2 { + reg = <0x02 0x2>; + }; + + otp_cpu_version: cpu-version@8 { + reg = <0x08 0x1>; + bits = <3 3>; + }; + + otp_id: id@a { + reg = <0x0a 0x10>; + }; + + cpu_leakage: cpu-leakage@1a { + reg = <0x1a 0x1>; + }; + + log_leakage: log-leakage@1b { + reg = <0x1b 0x1>; + }; + + npu_leakage: npu-leakage@1c { + reg = <0x1c 0x1>; + }; + + gpu_leakage: gpu-leakage@1d { + reg = <0x1d 0x1>; + }; + }; + }; + dmac: dma-controller@ff990000 { compatible = "arm,pl330", "arm,primecell"; reg = <0x0 0xff990000 0x0 0x4000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-lckfb-tspi.dts b/arch/arm64/boot/dts/rockchip/rk3566-lckfb-tspi.dts index ed65d3120444..18a560a6e2a4 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-lckfb-tspi.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-lckfb-tspi.dts @@ -635,10 +635,10 @@ status = "okay"; bluetooth: bluetooth { - compatible = "brcm,bcm43438-bt"; + compatible = "brcm,bcm43430a1-bt"; clocks = <&rk809 1>; clock-names = "lpo"; - max-speed = <3000000>; + max-speed = <1500000>; pinctrl-names = "default"; pinctrl-0 = <&bt_host_wake_l &bt_wake_l &bt_enable_h>; shutdown-gpios = <&gpio2 RK_PB7 GPIO_ACTIVE_HIGH>; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-onething-edge-cube.dts b/arch/arm64/boot/dts/rockchip/rk3566-onething-edge-cube.dts new file mode 100644 index 000000000000..b57bf7bf10f5 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3566-onething-edge-cube.dts @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +/dts-v1/; + +#include +#include +#include +#include +#include "rk3566.dtsi" + +/ { + model = "OneThing Edge Cube (OEC)/OEC Turbo"; + compatible = "onething,edge-cube", "rockchip,rk3566"; + + aliases { + ethernet0 = &gmac1; + mmc0 = &sdhci; + }; + + chosen: chosen { + stdout-path = "serial2:1500000n8"; + }; + + gmac1_clkin: external-gmac1-clock { + compatible = "fixed-clock"; + clock-frequency = <125000000>; + clock-output-names = "gmac1_clkin"; + #clock-cells = <0>; + }; + + leds { + compatible = "gpio-leds"; + + rgb_led_b: led-0 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + gpios = <&gpio3 RK_PB3 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&rgb_led_b_enable_l>; + }; + + rgb_led_g: led-1 { + color = ; + default-state = "on"; + function = LED_FUNCTION_STATUS; + gpios = <&gpio3 RK_PB4 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&rgb_led_g_enable_l>; + }; + + rgb_led_r: led-2 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + gpios = <&gpio0 RK_PC3 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&rgb_led_r_enable_l>; + }; + }; + + vcc_1v8: regulator-vcc-1v8 { + compatible = "regulator-fixed"; + regulator-name = "vcc_1v8"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vin-supply = <&vcc5v0_sys>; + }; + + vcc_3v3: regulator-vcc-3v3 { + compatible = "regulator-fixed"; + regulator-name = "vcc_3v3"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc5v0_sys>; + }; + + vcc12v0_dcin: regulator-vcc12v0-dcin { + compatible = "regulator-fixed"; + regulator-name = "vcc12v0_dcin"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + }; + + vcc3v3_sys: regulator-vcc3v3-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc12v0_dcin>; + }; + + vcc5v0_sys: regulator-vcc5v0-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc5v0_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc12v0_dcin>; + }; + + vcc5v0_usb_host: regulator-vcc5v0-usb-host { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio0 RK_PC6 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&vcc5v0_usb_host_en>; + regulator-name = "vcc5v0_usb_host"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc5v0_sys>; + }; + + vdd_cpu: regulator-vdd-cpu { + compatible = "pwm-regulator"; + pwms = <&pwm0 0 5000 PWM_POLARITY_INVERTED>; + regulator-always-on; + regulator-boot-on; + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1200000>; + regulator-settling-time-up-us = <250>; + pwm-supply = <&vcc5v0_sys>; + }; + + vdd_fixed: regulator-vdd-fixed { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-boot-on; + regulator-name = "vdd_fixed"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <950000>; + vin-supply = <&vcc5v0_sys>; + }; + + vdd_logic: regulator-vdd-logic { + compatible = "pwm-regulator"; + pwms = <&pwm1 0 5000 PWM_POLARITY_INVERTED>; + regulator-name = "vdd_logic"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1100000>; + regulator-settling-time-up-us = <250>; + pwm-supply = <&vcc5v0_sys>; + }; +}; + + +/* No hardware video output port */ +&display_subsystem { + status = "disabled"; +}; + +&combphy1 { + status = "okay"; +}; + +&combphy2 { + status = "okay"; +}; + +&cpu0 { + cpu-supply = <&vdd_cpu>; +}; + +&cpu1 { + cpu-supply = <&vdd_cpu>; +}; + +&cpu2 { + cpu-supply = <&vdd_cpu>; +}; + +&cpu3 { + cpu-supply = <&vdd_cpu>; +}; + +&gmac1 { + assigned-clocks = <&cru SCLK_GMAC1_RX_TX>, <&cru SCLK_GMAC1>; + assigned-clock-parents = <&cru SCLK_GMAC1_RGMII_SPEED>, <&gmac1_clkin>; + clock_in_out = "input"; + phy-handle = <&rgmii_phy1>; + phy-mode = "rgmii-id"; + pinctrl-names = "default"; + pinctrl-0 = <&gmac1m1_miim + &gmac1m1_tx_bus2 + &gmac1m1_rx_bus2 + &gmac1m1_rgmii_clk + &gmac1m1_rgmii_bus + &gmac1m1_clkinout>; + status = "okay"; +}; + +&gpu { + status = "okay"; +}; + +&mdio1 { + rgmii_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x1>; + reset-assert-us = <20000>; + reset-deassert-us = <100000>; + reset-gpios = <&gpio4 RK_PC2 GPIO_ACTIVE_LOW>; + }; +}; + +&pinctrl { + gmac { + eth_phy_reset_pin: eth-phy-reset-pin { + rockchip,pins = <4 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + leds { + rgb_led_b_enable_l: rgb-led-b-enable-l { + rockchip,pins = <3 RK_PB3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + rgb_led_g_enable_l: rgb-led-g-enable-l { + rockchip,pins = <3 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + rgb_led_r_enable_l: rgb-led-r-enable-l { + rockchip,pins = <0 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + usb { + vcc5v0_usb_host_en: vcc5v0-usb-host-en { + rockchip,pins = <0 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&pmu_io_domains { + pmuio1-supply = <&vcc_3v3>; + pmuio2-supply = <&vcc_3v3>; + vccio1-supply = <&vcc_1v8>; + vccio3-supply = <&vcc_3v3>; + vccio4-supply = <&vcc_1v8>; + vccio5-supply = <&vcc_3v3>; + vccio6-supply = <&vcc_1v8>; + vccio7-supply = <&vcc_3v3>; + status = "okay"; +}; + +&pwm0 { + status = "okay"; +}; + +&pwm1 { + status = "okay"; +}; + +&saradc { + vref-supply = <&vcc_1v8>; + status = "okay"; +}; + +&sata2 { + status = "okay"; +}; + +&sdhci { + bus-width = <8>; + cap-mmc-highspeed; + mmc-hs200-1_8v; + no-sd; + no-sdio; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_bus8 &emmc_clk &emmc_cmd &emmc_datastrobe>; + status = "okay"; +}; + +&tsadc { + rockchip,hw-tshut-mode = <1>; + rockchip,hw-tshut-polarity = <0>; + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&usb_host0_ohci { + status = "okay"; +}; + +&usb_host1_ehci { + status = "okay"; +}; + +&usb_host1_ohci { + status = "okay"; +}; + +&usb_host1_xhci { + status = "okay"; +}; + +&usb2phy0 { + status = "okay"; +}; + +&usb2phy0_host { + phy-supply = <&vcc5v0_usb_host>; + status = "okay"; +}; + +&usb2phy0_otg { + status = "okay"; +}; + +&usb2phy1 { + status = "okay"; +}; + +&usb2phy1_host { + phy-supply = <&vcc5v0_usb_host>; + status = "okay"; +}; + +&usb2phy1_otg { + phy-supply = <&vcc5v0_usb_host>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi index 791719acb9dd..071cbdc3f6d5 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi @@ -40,6 +40,25 @@ sound-name-prefix = "Speaker Amp"; }; + battery: battery { + compatible = "simple-battery"; + charge-full-design-microamp-hours = <4000000>; + charge-term-current-microamp = <300000>; + constant-charge-current-max-microamp = <2000000>; + constant-charge-voltage-max-microvolt = <4200000>; + factory-internal-resistance-micro-ohms = <96000>; + voltage-max-design-microvolt = <4200000>; + voltage-min-design-microvolt = <3500000>; + + ocv-capacity-celsius = <20>; + ocv-capacity-table-0 = <4168000 100>, <4109000 95>, <4066000 90>, <4023000 85>, + <3985000 80>, <3954000 75>, <3924000 70>, <3897000 65>, + <3866000 60>, <3826000 55>, <3804000 50>, <3789000 45>, + <3777000 40>, <3770000 35>, <3763000 30>, <3750000 25>, + <3732000 20>, <3710000 15>, <3680000 10>, <3670000 5>, + <3500000 0>; + }; + dmic_codec: dmic-codec { compatible = "dmic-codec"; num-channels = <6>; @@ -215,6 +234,11 @@ cpu-supply = <&vdd_cpu>; }; +&gpu { + mali-supply = <&vdd_gpu_npu>; + status = "okay"; +}; + &i2c0 { status = "okay"; @@ -260,6 +284,13 @@ vcc8-supply = <&vcc_sys>; vcc9-supply = <&dcdc_boost>; + charger { + monitored-battery = <&battery>; + rockchip,resistor-sense-micro-ohms = <10000>; + rockchip,sleep-enter-current-microamp = <150000>; + rockchip,sleep-filter-current-microamp = <100000>; + }; + regulators { vdd_logic: DCDC_REG1 { regulator-name = "vdd_logic"; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-qnap-ts133-pcb-13.dtso b/arch/arm64/boot/dts/rockchip/rk3566-qnap-ts133-pcb-13.dtso new file mode 100644 index 000000000000..f9a8194f5753 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3566-qnap-ts133-pcb-13.dtso @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) +/* + * Device tree overlay for TS133 board PCB-13 revision. + * + * Copyright (C) 2025 Heiko Stuebner + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&{/} { + /* + * The default hardware-state of this gpio causes the drive + * to be already running when entering the kernel. + * regulator-boot-on is needed to prevent one additional + * power-cycle on the drive. + * + * With regulator-boot-on we get the expected 1 cycle + * per boot, without it we end up with 2 cycles as seen + * via smartctl. + */ + hdd1_pwr: regulator-hdd1-power { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PC2 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&hdd1_power_pin>; + regulator-name = "hdd1-power"; + regulator-boot-on; + vin-supply = <&dc_12v>; + }; +}; + +&gpio2 { + hdd1-present-hog { + gpios = ; + gpio-hog; + input; + line-name = "hdd1-present"; + }; +}; + +&pinctrl { + pinctrl-names = "default"; + pinctrl-0 = <&hdd1_present_pin>; + + hdd-power { + hdd1_power_pin: hdd1-power-pin { + rockchip,pins = <0 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + hdd-present { + hdd1_present_pin: hdd1-present-pin { + rockchip,pins = <2 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; +}; + +&sata2_port0 { + target-supply = <&hdd1_pwr>; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts b/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts index 6ac1fe0d3c98..8d906ab02c5f 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts +++ b/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts @@ -354,6 +354,7 @@ compatible = "simple-audio-card"; pinctrl-0 = <&hp_det>; pinctrl-names = "default"; + simple-audio-card,aux-devs = <&aw87391_pa_l>, <&aw87391_pa_r>; simple-audio-card,format = "i2s"; simple-audio-card,hp-det-gpios = <&gpio4 RK_PC2 GPIO_ACTIVE_HIGH>; simple-audio-card,mclk-fs = <256>; @@ -363,8 +364,10 @@ "MICL", "Mic Jack", "Headphones", "HPOL", "Headphones", "HPOR", - "Internal Speakers", "HPOL", - "Internal Speakers", "HPOR"; + "Internal Speakers", "Left Amp OUT", + "Internal Speakers", "Right Amp OUT", + "Left Amp IN", "HPOL", + "Right Amp IN", "HPOR"; simple-audio-card,widgets = "Microphone", "Mic Jack", "Headphone", "Headphones", @@ -468,6 +471,18 @@ regulator-max-microvolt = <3300000>; regulator-name = "vcc_wifi"; }; + + vdd_amp: regulator-vcc-amp { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio4 RK_PC3 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&vdd_amp_h>; + pinctrl-names = "default"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vccio_acodec>; + regulator-name = "vdd_amp"; + }; }; &cpu0 { @@ -840,8 +855,22 @@ pinctrl-names = "default"; status = "okay"; - /* awinic,aw87391 at 0x58 */ - /* awinic,aw87391 at 0x5b */ + aw87391_pa_l: audio-codec@58 { + compatible = "anbernic,rgds-amp", "awinic,aw87391"; + reg = <0x58>; + vdd-supply = <&vdd_amp>; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Amp"; + }; + + aw87391_pa_r: audio-codec@5b { + compatible = "anbernic,rgds-amp", "awinic,aw87391"; + reg = <0x5b>; + vdd-supply = <&vdd_amp>; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Amp"; + }; + /* invensense,icm42607p at 0x68 */ }; @@ -1014,6 +1043,13 @@ }; }; + vdd-amp { + vdd_amp_h: vdd-amp-h { + rockchip,pins = + <4 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + vcc-lcd { vdd_lcd0_h: vdd-lcd0-h { rockchip,pins = diff --git a/arch/arm64/boot/dts/rockchip/rk3568-qnap-ts233-pcb-12-11.dtso b/arch/arm64/boot/dts/rockchip/rk3568-qnap-ts233-pcb-12-11.dtso new file mode 100644 index 000000000000..c7987171644c --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-qnap-ts233-pcb-12-11.dtso @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) +/* + * Device tree overlay for TS233 board PCBs-12-11 revision. + * + * Copyright (C) 2025 Heiko Stuebner + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&{/} { + /* + * The default hardware-state of this gpio causes the drive + * to be already running when entering the kernel. + * regulator-boot-on is needed to prevent one additional + * power-cycle on the drive. + * + * With regulator-boot-on we get the expected 1 cycle + * per boot, without it we end up with 2 cycles as seen + * via smartctl. + */ + hdd1_pwr: regulator-hdd1-power { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PC2 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&hdd1_power_pin>; + regulator-name = "hdd1-power"; + regulator-boot-on; + vin-supply = <&dc_12v>; + }; + + hdd2_pwr: regulator-hdd2-power { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PC3 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&hdd2_power_pin>; + regulator-name = "hdd2-power"; + regulator-boot-on; + vin-supply = <&dc_12v>; + }; +}; + +&gpio2 { + hdd1-present-hog { + gpios = ; + gpio-hog; + input; + line-name = "hdd1-present"; + }; + + hdd2-present-hog { + gpios = ; + gpio-hog; + input; + line-name = "hdd2-present"; + }; +}; + +&pinctrl { + pinctrl-names = "default"; + pinctrl-0 = <&hdd1_present_pin &hdd2_present_pin>; + + hdd-power { + hdd1_power_pin: hdd1-power-pin { + rockchip,pins = <0 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + hdd2_power_pin: hdd2-power-pin { + rockchip,pins = <0 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + hdd-present { + hdd1_present_pin: hdd1-present-pin { + rockchip,pins = <2 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + hdd2_present_pin: hdd2-present-pin { + rockchip,pins = <2 RK_PA1 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; +}; + +&sata1_port0 { + target-supply = <&hdd2_pwr>; +}; + +&sata2_port0 { + target-supply = <&hdd1_pwr>; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-qnap-ts433-pcb-12-10.dtso b/arch/arm64/boot/dts/rockchip/rk3568-qnap-ts433-pcb-12-10.dtso new file mode 100644 index 000000000000..ce0fdc9f2989 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3568-qnap-ts433-pcb-12-10.dtso @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR MIT) +/* + * Device tree overlay for TS433 board PCBs-12-10 revision. + * + * Copyright (C) 2025 Heiko Stuebner + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&{/} { + /* + * The default hardware-state of this gpio causes the drive + * to be already running when entering the kernel. + * regulator-boot-on is needed to prevent one additional + * power-cycle on the drive. + * + * With regulator-boot-on we get the expected 1 cycle + * per boot, without it we end up with 2 cycles as seen + * via smartctl. + */ + hdd1_pwr: regulator-hdd1-power { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PC2 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&hdd1_power_pin>; + regulator-name = "hdd1-power"; + regulator-boot-on; + vin-supply = <&dc_12v>; + }; + + hdd2_pwr: regulator-hdd2-power { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PC3 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&hdd2_power_pin>; + regulator-name = "hdd2-power"; + regulator-boot-on; + vin-supply = <&dc_12v>; + }; + + /* + * HDD3+4 are connected to ports of the PCIe SATA controller. + * Currently there is no way to attach those, so keep them + * always on. + */ + hdd3_pwr: regulator-hdd3-power { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PC4 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&hdd3_power_pin>; + regulator-name = "hdd3-power"; + regulator-always-on; + regulator-boot-on; + vin-supply = <&dc_12v>; + }; + + hdd4_pwr: regulator-hdd4-power { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PC5 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&hdd4_power_pin>; + regulator-name = "hdd4-power"; + regulator-always-on; + regulator-boot-on; + vin-supply = <&dc_12v>; + }; +}; + +&gpio2 { + hdd1-present-hog { + gpios = ; + gpio-hog; + input; + line-name = "hdd1-present"; + }; + + hdd2-present-hog { + gpios = ; + gpio-hog; + input; + line-name = "hdd2-present"; + }; + + hdd3-present-hog { + gpios = ; + gpio-hog; + input; + line-name = "hdd3-present"; + }; + + hdd4-present-hog { + gpios = ; + gpio-hog; + input; + line-name = "hdd4-present"; + }; +}; + +&pinctrl { + pinctrl-names = "default"; + pinctrl-0 = <&hdd1_present_pin &hdd2_present_pin &hdd3_present_pin + &hdd4_present_pin>; + + hdd-power { + hdd1_power_pin: hdd1-power-pin { + rockchip,pins = <0 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + hdd2_power_pin: hdd2-power-pin { + rockchip,pins = <0 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + hdd3_power_pin: hdd3-power-pin { + rockchip,pins = <0 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + hdd4_power_pin: hdd4-power-pin { + rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + hdd-present { + hdd1_present_pin: hdd1-present-pin { + rockchip,pins = <2 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + hdd2_present_pin: hdd2-present-pin { + rockchip,pins = <2 RK_PA1 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + hdd3_present_pin: hdd3-present-pin { + rockchip,pins = <2 RK_PD0 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + hdd4_present_pin: hdd4-present-pin { + rockchip,pins = <2 RK_PD1 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; +}; + +&sata1_port0 { + target-supply = <&hdd2_pwr>; +}; + +&sata2_port0 { + target-supply = <&hdd1_pwr>; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-wolfvision-pf5-io-expander.dtso b/arch/arm64/boot/dts/rockchip/rk3568-wolfvision-pf5-io-expander.dtso index 048933de2943..8cfce71dd318 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-wolfvision-pf5-io-expander.dtso +++ b/arch/arm64/boot/dts/rockchip/rk3568-wolfvision-pf5-io-expander.dtso @@ -11,6 +11,7 @@ #include #include #include +#include #include &{/} { @@ -134,3 +135,22 @@ phy-supply = <&usb_host_vbus>; status = "okay"; }; + +&vicap { + pinctrl-names = "default"; + pinctrl-0 = <&cif_clk &cif_dvp_clk &cif_dvp_bus16>; + status = "okay"; +}; + +&vicap_dvp { + vicap_dvp_input: endpoint { + bus-type = ; + bus-width = <16>; + pclk-sample = ; + rockchip,dvp-clk-delay = <10>; + }; +}; + +&vicap_mmu { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi index 658097ed6971..3bc653f027f1 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi @@ -155,9 +155,11 @@ bus-range = <0x10 0x1f>; clocks = <&cru ACLK_PCIE30X1_MST>, <&cru ACLK_PCIE30X1_SLV>, <&cru ACLK_PCIE30X1_DBI>, <&cru PCLK_PCIE30X1>, - <&cru CLK_PCIE30X1_AUX_NDFT>; + <&cru CLK_PCIE30X1_AUX_NDFT>, + <&cru CLK_PCIE30X1_PIPE_DFT>; clock-names = "aclk_mst", "aclk_slv", - "aclk_dbi", "pclk", "aux"; + "aclk_dbi", "pclk", "aux", + "pipe"; device_type = "pci"; interrupts = , , @@ -208,9 +210,11 @@ bus-range = <0x20 0x2f>; clocks = <&cru ACLK_PCIE30X2_MST>, <&cru ACLK_PCIE30X2_SLV>, <&cru ACLK_PCIE30X2_DBI>, <&cru PCLK_PCIE30X2>, - <&cru CLK_PCIE30X2_AUX_NDFT>; + <&cru CLK_PCIE30X2_AUX_NDFT>, + <&cru CLK_PCIE30X2_PIPE_DFT>; clock-names = "aclk_mst", "aclk_slv", - "aclk_dbi", "pclk", "aux"; + "aclk_dbi", "pclk", "aux", + "pipe"; device_type = "pci"; interrupts = , , diff --git a/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi b/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi index a2c4957a5899..64bdd8b7754b 100644 --- a/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi @@ -3,6 +3,7 @@ * Copyright (c) 2021 Rockchip Electronics Co., Ltd. */ +#include #include #include #include @@ -221,11 +222,20 @@ <&cru CLK_SATA1_RXOOB>; clock-names = "sata", "pmalive", "rxoob"; interrupts = ; - phys = <&combphy1 PHY_TYPE_SATA>; - phy-names = "sata-phy"; ports-implemented = <0x1>; power-domains = <&power RK3568_PD_PIPE>; + #address-cells = <1>; + #size-cells = <0>; status = "disabled"; + + sata1_port0: sata-port@0 { + reg = <0>; + hba-port-cap = ; + phys = <&combphy1 PHY_TYPE_SATA>; + phy-names = "sata-phy"; + snps,rx-ts-max = <32>; + snps,tx-ts-max = <32>; + }; }; sata2: sata@fc800000 { @@ -235,11 +245,20 @@ <&cru CLK_SATA2_RXOOB>; clock-names = "sata", "pmalive", "rxoob"; interrupts = ; - phys = <&combphy2 PHY_TYPE_SATA>; - phy-names = "sata-phy"; ports-implemented = <0x1>; power-domains = <&power RK3568_PD_PIPE>; + #address-cells = <1>; + #size-cells = <0>; status = "disabled"; + + sata2_port0: sata-port@0 { + reg = <0>; + hba-port-cap = ; + phys = <&combphy2 PHY_TYPE_SATA>; + phy-names = "sata-phy"; + snps,rx-ts-max = <32>; + snps,tx-ts-max = <32>; + }; }; usb_host0_xhci: usb@fcc00000 { @@ -1001,9 +1020,11 @@ bus-range = <0x0 0xf>; clocks = <&cru ACLK_PCIE20_MST>, <&cru ACLK_PCIE20_SLV>, <&cru ACLK_PCIE20_DBI>, <&cru PCLK_PCIE20>, - <&cru CLK_PCIE20_AUX_NDFT>; + <&cru CLK_PCIE20_AUX_NDFT>, + <&cru CLK_PCIE20_PIPE_DFT>; clock-names = "aclk_mst", "aclk_slv", - "aclk_dbi", "pclk", "aux"; + "aclk_dbi", "pclk", "aux", + "pipe"; device_type = "pci"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 7>; @@ -1104,6 +1125,52 @@ status = "disabled"; }; + otp: efuse@fe38c000 { + compatible = "rockchip,rk3568-otp"; + reg = <0x0 0xfe38c000 0x0 0x4000>; + clocks = <&cru CLK_OTPC_NS_USR>, <&cru PCLK_OTPC_NS>, + <&cru PCLK_OTPPHY>, <&cru CLK_OTPC_NS_SBPI>; + clock-names = "otp", "apb_pclk", "phy", "sbpi"; + resets = <&cru SRST_OTPC_NS_USR>, <&cru SRST_P_OTPC_NS>, + <&cru SRST_OTPPHY>, <&cru SRST_OTPC_NS_SBPI>; + reset-names = "otp", "apb", "phy", "sbpi"; + + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + cpu_code: cpu-code@2 { + reg = <0x02 0x2>; + }; + + otp_cpu_version: cpu-version@8 { + reg = <0x08 0x1>; + bits = <3 3>; + }; + + otp_id: id@a { + reg = <0x0a 0x10>; + }; + + cpu_leakage: cpu-leakage@1a { + reg = <0x1a 0x1>; + }; + + log_leakage: log-leakage@1b { + reg = <0x1b 0x1>; + }; + + npu_leakage: npu-leakage@1c { + reg = <0x1c 0x1>; + }; + + gpu_leakage: gpu-leakage@1d { + reg = <0x1d 0x1>; + }; + }; + }; + i2s0_8ch: i2s@fe400000 { compatible = "rockchip,rk3568-i2s-tdm"; reg = <0x0 0xfe400000 0x0 0x1000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5.dts b/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5.dts index d372ba252af8..1c100ffd1518 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5.dts +++ b/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5.dts @@ -21,6 +21,8 @@ aliases { ethernet0 = &gmac0; ethernet1 = &gmac1; + mmc0 = &sdhci; + mmc1 = &sdmmc; }; chosen { diff --git a/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10.dts index f5746bc2970b..fb0dd1bc5148 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10.dts @@ -270,6 +270,7 @@ simple-audio-card,frame-master = <&masterdai>; simple-audio-card,hp-det-gpios = <&gpio0 RK_PD3 GPIO_ACTIVE_LOW>; simple-audio-card,mclk-fs = <256>; + simple-audio-card,pin-switches = "Headphones", "Speaker", "Main Mic", "Headset Mic"; simple-audio-card,routing = "Headphone Power INL", "LOUT1", "Headphone Power INR", "ROUT1", diff --git a/arch/arm64/boot/dts/rockchip/rk3576-evb2-v10.dts b/arch/arm64/boot/dts/rockchip/rk3576-evb2-v10.dts new file mode 100644 index 000000000000..98d5d00d63b5 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3576-evb2-v10.dts @@ -0,0 +1,1071 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2026 Rockchip Electronics Co., Ltd. + * + */ + +/dts-v1/; + +#include +#include +#include +#include +#include "rk3576.dtsi" + +/ { + model = "Rockchip RK3576 EVB2 V10 Board"; + compatible = "rockchip,rk3576-evb2-v10", "rockchip,rk3576"; + + aliases { + ethernet0 = &gmac0; + ethernet1 = &gmac1; + mmc0 = &sdhci; + mmc1 = &sdmmc; + }; + + chosen { + stdout-path = "serial0:1500000n8"; + }; + + dp-con { + compatible = "dp-connector"; + dp-pwr-supply = <&vcc3v3_dp_port>; + label = "DP OUT"; + type = "full-size"; + + port { + dp0_con_in: endpoint { + remote-endpoint = <&dp0_out_con>; + }; + }; + }; + + hdmi-con { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + + keys { + compatible = "adc-keys"; + io-channels = <&saradc 1>; + io-channel-names = "buttons"; + keyup-threshold-microvolt = <1800000>; + poll-interval = <100>; + + button-back { + label = "back"; + linux,code = ; + press-threshold-microvolt = <1235000>; + }; + + button-menu { + label = "menu"; + linux,code = ; + press-threshold-microvolt = <890000>; + }; + + button-vol-down { + label = "volume down"; + linux,code = ; + press-threshold-microvolt = <417000>; + }; + + button-vol-up { + label = "volume up"; + linux,code = ; + press-threshold-microvolt = <17000>; + }; + }; + + leds { + compatible = "gpio-leds"; + + work_led: led-0 { + gpios = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + }; + + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_pwren>; + + /* + * On the module itself this is one of these (depending + * on the actual card populated): + * - SDIO_RESET_L_WL_REG_ON + * - PDN (power down when low) + */ + post-power-on-delay-ms = <200>; + reset-gpios = <&gpio1 RK_PC6 GPIO_ACTIVE_LOW>; + }; + + vbus5v0_usb0: regulator-vbus5v0-usb0 { + compatible = "regulator-fixed"; + regulator-name = "vcc5v0_usb3_host0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + gpio = <&gpio0 RK_PD1 GPIO_ACTIVE_HIGH>; + vin-supply = <&vcc5v0_device>; + pinctrl-names = "default"; + pinctrl-0 = <&usb_otg0_pwren>; + }; + + vbus5v0_usb1: regulator-vcc5v0-usb1 { + compatible = "regulator-fixed"; + regulator-name = "vcc5v0_usb3_host1"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + gpio = <&gpio0 RK_PC3 GPIO_ACTIVE_HIGH>; + vin-supply = <&vcc5v0_device>; + pinctrl-names = "default"; + pinctrl-0 = <&usb_host_pwren>; + }; + + vcc12v_dcin: regulator-vcc12v-dcin { + compatible = "regulator-fixed"; + regulator-name = "vcc12v_dcin"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + }; + + vcc1v2_ufs_vccq_s0: regulator-vcc1v2-ufs-vccq-s0 { + compatible = "regulator-fixed"; + regulator-name = "vcc1v2_ufs_vccq_s0"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + vin-supply = <&vcc_sys>; + }; + + vcc1v8_ufs_vccq2_s0: regulator-vcc1v8-ufs-vccq2-s0 { + compatible = "regulator-fixed"; + regulator-name = "vcc1v8_ufs_vccq2_s0"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vin-supply = <&vcc_1v8_s3>; + }; + + vcc3v3_dp_port: regulator-vcc3v3-dp-port { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_dp_port"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + enable-active-high; + gpios = <&gpio0 RK_PC2 GPIO_ACTIVE_HIGH>; + vin-supply = <&vcc_3v3_s3>; + pinctrl-names = "default"; + pinctrl-0 = <&image_pwren>; + }; + + vcc3v3_pcie1: regulator-vcc3v3-pcie1 { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_pcie1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + enable-active-high; + gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>; + startup-delay-us = <5000>; + vin-supply = <&vcc12v_dcin>; + }; + + vcc3v3_rtc_s5: regulator-vcc3v3-rtc-s5 { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_rtc_s5"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_sys>; + }; + + vcc3v3_sata_pwren: vcc3v3-sata-pwren { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_satapm"; + enable-active-high; + gpio = <&gpio4 RK_PC7 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&satapm_pwren>; + }; + + vcc3v3_sd: regulator-vcc-3v3-sd { + compatible = "regulator-fixed"; + enable-active-high; + gpios = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc_pwren>; + regulator-name = "vcc3v3_sd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_3v3_s0>; + }; + + vcc5v0_device: regulator-vcc5v0-device { + compatible = "regulator-fixed"; + regulator-name = "vcc5v0_device"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc12v_dcin>; + }; + + vcc_sys: regulator-vcc5v0-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc12v_dcin>; + }; + + vcc_1v1_nldo_s3: regulator-vcc-1v1-nldo-s3 { + compatible = "regulator-fixed"; + regulator-name = "vcc_1v1_nldo_s3"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + vin-supply = <&vcc_sys>; + }; + + vcc_1v8_s0: regulator-vcc-1v8-s0 { + compatible = "regulator-fixed"; + regulator-name = "vcc_1v8_s0"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vin-supply = <&vcc_1v8_s3>; + }; + + vcc_2v0_pldo_s3: regulator-vcc-2v0-pldo-s3 { + compatible = "regulator-fixed"; + regulator-name = "vcc_2v0_pldo_s3"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <2000000>; + regulator-max-microvolt = <2000000>; + vin-supply = <&vcc_sys>; + }; + + vcc_3v3_s0: regulator-vcc-3v3-s0 { + compatible = "regulator-fixed"; + regulator-name = "vcc_3v3_s0"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_3v3_s3>; + }; + + vcc_ufs_s0: regulator-vcc-ufs-s0 { + compatible = "regulator-fixed"; + regulator-name = "vcc_ufs_s0"; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_sys>; + }; +}; + +&cpu_l0 { + cpu-supply = <&vdd_cpu_lit_s0>; +}; + +&cpu_l1 { + cpu-supply = <&vdd_cpu_lit_s0>; +}; + +&cpu_l2 { + cpu-supply = <&vdd_cpu_lit_s0>; +}; + +&cpu_l3 { + cpu-supply = <&vdd_cpu_lit_s0>; +}; + +&cpu_b0 { + cpu-supply = <&vdd_cpu_big_s0>; +}; + +&cpu_b1 { + cpu-supply = <&vdd_cpu_big_s0>; +}; + +&cpu_b2 { + cpu-supply = <&vdd_cpu_big_s0>; +}; + +&cpu_b3 { + cpu-supply = <&vdd_cpu_big_s0>; +}; + +&combphy0_ps { + status = "okay"; +}; + +&combphy1_psu { + status = "okay"; +}; + +&dp { + pinctrl-0 = <&dpm0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&dp0_in { + dp0_in_vp1: endpoint { + remote-endpoint = <&vp1_out_dp0>; + }; +}; + +&dp0_out { + dp0_out_con: endpoint { + remote-endpoint = <&dp0_con_in>; + }; +}; + +&gmac0 { + clock_in_out = "output"; + phy-mode = "rgmii-id"; + phy-handle = <&rgmii_phy0>; + pinctrl-names = "default"; + pinctrl-0 = <ð0m1_miim + ð0m1_tx_bus2 + ð0m1_rx_bus2 + ð0m1_rgmii_clk + ð0m1_rgmii_bus>; + status = "okay"; +}; + +&gmac1 { + clock_in_out = "output"; + phy-handle = <&rgmii_phy1>; + phy-mode = "rgmii-id"; + pinctrl-names = "default"; + pinctrl-0 = <ð1m0_miim + ð1m0_tx_bus2 + ð1m0_rx_bus2 + ð1m0_rgmii_clk + ð1m0_rgmii_bus>; + status = "okay"; +}; + +&gpu { + mali-supply = <&vdd_gpu_s0>; + status = "okay"; +}; + +&hdmi { + status = "okay"; +}; + +&hdmi_in { + hdmi_in_vp0: endpoint { + remote-endpoint = <&vp0_out_hdmi>; + }; +}; + +&hdmi_out { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + +&hdmi_sound { + status = "okay"; +}; + +&hdptxphy { + status = "okay"; +}; + +&i2c1 { + status = "okay"; + + rk806: pmic@23 { + compatible = "rockchip,rk806"; + reg = <0x23>; + interrupt-parent = <&gpio0>; + interrupts = <6 IRQ_TYPE_LEVEL_LOW>; + gpio-controller; + #gpio-cells = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, + <&rk806_dvs2_null>, <&rk806_dvs3_null>; + system-power-controller; + + vcc1-supply = <&vcc_sys>; + vcc2-supply = <&vcc_sys>; + vcc3-supply = <&vcc_sys>; + vcc4-supply = <&vcc_sys>; + vcc5-supply = <&vcc_sys>; + vcc6-supply = <&vcc_sys>; + vcc7-supply = <&vcc_sys>; + vcc8-supply = <&vcc_sys>; + vcc9-supply = <&vcc_sys>; + vcc10-supply = <&vcc_sys>; + vcc11-supply = <&vcc_2v0_pldo_s3>; + vcc12-supply = <&vcc_sys>; + vcc13-supply = <&vcc_1v1_nldo_s3>; + vcc14-supply = <&vcc_1v1_nldo_s3>; + vcca-supply = <&vcc_sys>; + + rk806_dvs1_null: dvs1-null-pins { + pins = "gpio_pwrctrl1"; + function = "pin_fun0"; + }; + + rk806_dvs2_null: dvs2-null-pins { + pins = "gpio_pwrctrl2"; + function = "pin_fun0"; + }; + + rk806_dvs3_null: dvs3-null-pins { + pins = "gpio_pwrctrl3"; + function = "pin_fun0"; + }; + + rk806_dvs1_slp: dvs1-slp-pins { + pins = "gpio_pwrctrl1"; + function = "pin_fun1"; + }; + + rk806_dvs1_pwrdn: dvs1-pwrdn-pins { + pins = "gpio_pwrctrl1"; + function = "pin_fun2"; + }; + + rk806_dvs1_rst: dvs1-rst-pins { + pins = "gpio_pwrctrl1"; + function = "pin_fun3"; + }; + + rk806_dvs2_slp: dvs2-slp-pins { + pins = "gpio_pwrctrl2"; + function = "pin_fun1"; + }; + + rk806_dvs2_pwrdn: dvs2-pwrdn-pins { + pins = "gpio_pwrctrl2"; + function = "pin_fun2"; + }; + + rk806_dvs2_rst: dvs2-rst-pins { + pins = "gpio_pwrctrl2"; + function = "pin_fun3"; + }; + + rk806_dvs2_dvs: dvs2-dvs-pins { + pins = "gpio_pwrctrl2"; + function = "pin_fun4"; + }; + + rk806_dvs2_gpio: dvs2-gpio-pins { + pins = "gpio_pwrctrl2"; + function = "pin_fun5"; + }; + + rk806_dvs3_slp: dvs3-slp-pins { + pins = "gpio_pwrctrl3"; + function = "pin_fun1"; + }; + + rk806_dvs3_pwrdn: dvs3-pwrdn-pins { + pins = "gpio_pwrctrl3"; + function = "pin_fun2"; + }; + + rk806_dvs3_rst: dvs3-rst-pins { + pins = "gpio_pwrctrl3"; + function = "pin_fun3"; + }; + + rk806_dvs3_dvs: dvs3-dvs-pins { + pins = "gpio_pwrctrl3"; + function = "pin_fun4"; + }; + + rk806_dvs3_gpio: dvs3-gpio-pins { + pins = "gpio_pwrctrl3"; + function = "pin_fun5"; + }; + + regulators { + vdd_cpu_big_s0: dcdc-reg1 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <12500>; + regulator-name = "vdd_cpu_big_s0"; + regulator-enable-ramp-delay = <400>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_npu_s0: dcdc-reg2 { + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <12500>; + regulator-name = "vdd_npu_s0"; + regulator-enable-ramp-delay = <400>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_cpu_lit_s0: dcdc-reg3 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <950000>; + regulator-ramp-delay = <12500>; + regulator-name = "vdd_cpu_lit_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-suspend-microvolt = <750000>; + }; + }; + + vcc_3v3_s3: dcdc-reg4 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc_3v3_s3"; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3300000>; + }; + }; + + vdd_gpu_s0: dcdc-reg5 { + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <900000>; + regulator-ramp-delay = <12500>; + regulator-name = "vdd_gpu_s0"; + regulator-enable-ramp-delay = <400>; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-suspend-microvolt = <850000>; + }; + }; + + vddq_ddr_s0: dcdc-reg6 { + regulator-always-on; + regulator-boot-on; + regulator-name = "vddq_ddr_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_logic_s0: dcdc-reg7 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <800000>; + regulator-name = "vdd_logic_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_1v8_s3: dcdc-reg8 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc_1v8_s3"; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vdd2_ddr_s3: dcdc-reg9 { + regulator-always-on; + regulator-boot-on; + regulator-name = "vdd2_ddr_s3"; + + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vdd_ddr_s0: dcdc-reg10 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <1200000>; + regulator-name = "vdd_ddr_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcca_1v8_s0: pldo-reg1 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcca_1v8_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcca1v8_pldo2_s0: pldo-reg2 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcca1v8_pldo2_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdda_1v2_s0: pldo-reg3 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-name = "vdda_1v2_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcca_3v3_s0: pldo-reg4 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcca_3v3_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vccio_sd_s0: pldo-reg5 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vccio_sd_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcca1v8_pldo6_s3: pldo-reg6 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcca1v8_pldo6_s3"; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vdd_0v75_s3: nldo-reg1 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <550000>; + regulator-max-microvolt = <750000>; + regulator-name = "vdd_0v75_s3"; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <750000>; + }; + }; + + vdda_ddr_pll_s0: nldo-reg2 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-name = "vdda_ddr_pll_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdda0v75_hdmi_s0: nldo-reg3 { + regulator-boot-on; + regulator-min-microvolt = <837500>; + regulator-max-microvolt = <837500>; + regulator-name = "vdda0v75_hdmi_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdda_0v85_s0: nldo-reg4 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + regulator-name = "vdda_0v85_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdda_0v75_s0: nldo-reg5 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <750000>; + regulator-name = "vdda_0v75_s0"; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + }; + }; +}; + +&i2c2 { + status = "okay"; + + hym8563: rtc@51 { + compatible = "haoyu,hym8563"; + reg = <0x51>; + clock-output-names = "hym8563"; + interrupt-parent = <&gpio0>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&rtc_int>; + wakeup-source; + #clock-cells = <0>; + }; +}; + +&mdio0 { + rgmii_phy0: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x1>; + pinctrl-names = "default"; + pinctrl-0 = <&rgmii_phy0_rst>; + reset-assert-us = <20000>; + reset-deassert-us = <100000>; + reset-gpios = <&gpio3 RK_PD3 GPIO_ACTIVE_LOW>; + }; +}; + +&mdio1 { + rgmii_phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x1>; + pinctrl-names = "default"; + pinctrl-0 = <&rgmii_phy1_rst>; + reset-assert-us = <20000>; + reset-deassert-us = <100000>; + reset-gpios = <&gpio3 RK_PD6 GPIO_ACTIVE_LOW>; + }; +}; + +&pinctrl { + bluetooth { + bt_reg_on: bt-reg-on { + rockchip,pins = <1 RK_PC7 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + bt_wake_host: bt-wake-host { + rockchip,pins = <0 RK_PB1 RK_FUNC_GPIO &pcfg_pull_down>; + }; + + host_wake_bt: host-wake-bt { + rockchip,pins = <1 RK_PD4 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + dp { + image_pwren: image-pwren { + rockchip,pins = <0 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + hym8563 { + rtc_int: rtc-int { + rockchip,pins = <0 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + network { + rgmii_phy0_rst: rgmii-phy0-rst { + rockchip,pins = <3 RK_PD3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + rgmii_phy1_rst: rgmii-phy1-rst { + rockchip,pins = <3 RK_PD6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pcie1 { + pcie1_rst: pcie1-rst { + rockchip,pins = <4 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + sata { + satapm_pwren: satapm-pwren { + rockchip,pins = <4 RK_PC7 RK_FUNC_GPIO &pcfg_output_high>; + }; + }; + + sdmmc { + sdmmc_pwren: sdmmc-pwren { + rockchip,pins = <0 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + usb { + usb_host_pwren: usb-host-pwren { + rockchip,pins = <0 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + usb_hub_reset: usb-hub-reset { + rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + usb_otg0_pwren: usb-otg0-pwren { + rockchip,pins = <0 RK_PD1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + wifi { + wifi_host_wake: wifi-host-wake { + rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_down>; + }; + + wifi_pwren: wifi-pwren { + rockchip,pins = <1 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; +}; + +&sai6 { + status = "okay"; +}; + +&saradc { + vref-supply = <&vcca_1v8_s0>; + status = "okay"; +}; + +&sata0 { + target-supply = <&vcc3v3_sata_pwren>; + status = "okay"; +}; + +&sdio { + #address-cells = <1>; + #size-cells = <0>; + bus-width = <4>; + cap-sd-highspeed; + cap-sdio-irq; + keep-power-in-suspend; + max-frequency = <150000000>; + mmc-pwrseq = <&sdio_pwrseq>; + no-sd; + no-mmc; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc1m0_bus4 &sdmmc1m0_clk &sdmmc1m0_cmd>; + sd-uhs-sdr104; + status = "okay"; + + brcmf: wifi@1 { + compatible = "brcm,bcm4329-fmac"; + reg = <1>; + interrupt-parent = <&gpio0>; + interrupts = ; + interrupt-names = "host-wake"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_host_wake>; + }; +}; + +&sdhci { + bus-width = <8>; + full-pwr-cycle-in-suspend; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + no-sdio; + no-sd; + non-removable; + status = "okay"; +}; + +&sdmmc { + bus-width = <4>; + cap-sd-highspeed; + cd-gpios = <&gpio0 RK_PA7 GPIO_ACTIVE_LOW>; + disable-wp; + no-sdio; + no-mmc; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_det &sdmmc0_bus4>; + sd-uhs-sdr104; + vmmc-supply = <&vcc3v3_sd>; + vqmmc-supply = <&vccio_sd_s0>; + status = "okay"; +}; + +&u2phy0 { + status = "okay"; +}; + +&u2phy0_otg { + phy-supply = <&vbus5v0_usb0>; + status = "okay"; +}; + +&u2phy1 { + status = "okay"; +}; + +&u2phy1_otg { + phy-supply = <&vbus5v0_usb1>; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart4 { + pinctrl-0 = <&uart4m1_xfer &uart4m1_ctsn &uart4m1_rtsn>; + pinctrl-names = "default"; + uart-has-rtscts; + status = "okay"; + + bluetooth { + compatible = "brcm,bcm43438-bt"; + clocks = <&hym8563>; + clock-names = "lpo"; + device-wakeup-gpios = <&gpio1 RK_PD4 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&gpio0>; + interrupts = ; + pinctrl-0 = <&bt_reg_on &bt_wake_host &host_wake_bt>; + pinctrl-names = "default"; + shutdown-gpios = <&gpio1 RK_PC7 GPIO_ACTIVE_HIGH>; + vbat-supply = <&vcc_3v3_s3>; + vddio-supply = <&vcc_1v8_s3>; + }; +}; + +&ufshc { + vcc-supply = <&vcc_ufs_s0>; + vccq-supply = <&vcc1v2_ufs_vccq_s0>; + vccq2-supply = <&vcc1v8_ufs_vccq2_s0>; + status = "okay"; +}; + +&usbdp_phy { + rockchip,dp-lane-mux = <2 3>; + status = "okay"; +}; + +&usb_drd0_dwc3 { + dr_mode = "otg"; + extcon = <&u2phy0>; + status = "okay"; +}; + +&usb_drd1_dwc3 { + dr_mode = "host"; + pinctrl-names = "default"; + pinctrl-0 = <&usb_hub_reset>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + hub_2_0: hub@1 { + compatible = "usb1a86,8091"; + reg = <1>; + reset-gpios = <&gpio0 RK_PC5 GPIO_ACTIVE_LOW>; + v5-supply = <&vcc_3v3_s0>; + vdd33-supply = <&vcc_3v3_s0>; + }; +}; + +&vop { + /* + * If no dedicated PLL was specified, the GPLL would be automatically + * assigned as the PLL source for dclk_vp1_src. As the frequency of GPLL + * is 1188 MHz, we can only get typical clock frequencies such as + * 74.25MHz, 148.5MHz, 297MHz, 594MHz. + * + * So here we set the parent clock of VP1 to VPLL so that we can get + * any frequency. + */ + assigned-clocks = <&cru DCLK_VP1_SRC>; + assigned-clock-parents = <&cru PLL_VPLL>; + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; + +&vp0 { + vp0_out_hdmi: endpoint@ROCKCHIP_VOP2_EP_HDMI0 { + reg = ; + remote-endpoint = <&hdmi_in_vp0>; + }; +}; + +&vp1 { + vp1_out_dp0: endpoint@a { + reg = ; + remote-endpoint = <&dp0_in_vp1>; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3576-khadas-edge-2l.dts b/arch/arm64/boot/dts/rockchip/rk3576-khadas-edge-2l.dts new file mode 100644 index 000000000000..68630379af63 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3576-khadas-edge-2l.dts @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) + +/dts-v1/; + +#include +#include +#include "rk3576.dtsi" + +/ { + model = "Khadas Edge-2L"; + compatible = "khadas,edge-2l", "rockchip,rk3576"; + + aliases { + mmc0 = &sdhci; + }; + + chosen { + stdout-path = "serial0:1500000n8"; + }; +}; + +&sdhci { + bus-width = <8>; + no-sdio; + no-sd; + non-removable; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi index 49ccdf12ef7e..28175d8200d5 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi @@ -1379,6 +1379,19 @@ status = "disabled"; }; + spdif_tx2: spdif-tx@27d20000 { + compatible = "rockchip,rk3576-spdif", "rockchip,rk3568-spdif"; + reg = <0x0 0x27d20000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SPDIF_TX2>, <&cru HCLK_SPDIF_TX2>; + clock-names = "mclk", "hclk"; + dmas = <&dmac2 28>; + dma-names = "tx"; + power-domains = <&power RK3576_PD_VO0>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + sai5: sai@27d40000 { compatible = "rockchip,rk3576-sai"; reg = <0x0 0x27d40000 0x0 0x1000>; @@ -1483,6 +1496,73 @@ }; }; + dp: dp@27e40000 { + compatible = "rockchip,rk3576-dp"; + reg = <0x0 0x27e40000 0x0 0x30000>; + interrupts = ; + assigned-clocks = <&cru CLK_AUX16MHZ_0>; + assigned-clock-rates = <16000000>; + clocks = <&cru PCLK_DP0>, <&cru CLK_AUX16MHZ_0>, + <&cru ACLK_DP0>; + clock-names = "apb", "aux", "hdcp"; + resets = <&cru SRST_DP0>; + phys = <&usbdp_phy PHY_TYPE_DP>; + power-domains = <&power RK3576_PD_VO1>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + dp0_in: port@0 { + reg = <0>; + }; + + dp0_out: port@1 { + reg = <1>; + }; + }; + }; + + spdif_tx3: spdif-tx@27ea0000 { + compatible = "rockchip,rk3576-spdif", "rockchip,rk3568-spdif"; + reg = <0x0 0x27ea0000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SPDIF_TX3>, <&cru HCLK_SPDIF_TX3>; + clock-names = "mclk", "hclk"; + dmas = <&dmac2 29>; + dma-names = "tx"; + power-domains = <&power RK3576_PD_VO1>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + spdif_tx4: spdif-tx@27eb0000 { + compatible = "rockchip,rk3576-spdif", "rockchip,rk3568-spdif"; + reg = <0x0 0x27eb0000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SPDIF_TX4>, <&cru HCLK_SPDIF_TX4>; + clock-names = "mclk", "hclk"; + dmas = <&dmac1 6>; + dma-names = "tx"; + power-domains = <&power RK3576_PD_VO1>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + spdif_tx5: spdif-tx@27ec0000 { + compatible = "rockchip,rk3576-spdif", "rockchip,rk3568-spdif"; + reg = <0x0 0x27ec0000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SPDIF_TX5>, <&cru HCLK_SPDIF_TX5>; + clock-names = "mclk", "hclk"; + dmas = <&dmac0 25>; + dma-names = "tx"; + power-domains = <&power RK3576_PD_VO1>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + sai7: sai@27ed0000 { compatible = "rockchip,rk3576-sai"; reg = <0x0 0x27ed0000 0x0 0x1000>; @@ -1868,8 +1948,9 @@ pinctrl-0 = <&ufs_refclk &ufs_rstgpio>; pinctrl-names = "default"; resets = <&cru SRST_A_UFS_BIU>, <&cru SRST_A_UFS_SYS>, - <&cru SRST_A_UFS>, <&cru SRST_P_UFS_GRF>; - reset-names = "biu", "sys", "ufs", "grf"; + <&cru SRST_A_UFS>, <&cru SRST_P_UFS_GRF>, + <&cru SRST_MPHY_INIT>; + reset-names = "biu", "sys", "ufs", "grf", "mphy"; reset-gpios = <&gpio4 RK_PD0 GPIO_ACTIVE_LOW>; status = "disabled"; }; @@ -2138,6 +2219,36 @@ status = "disabled"; }; + spdif_tx0: spdif-tx@2a670000 { + compatible = "rockchip,rk3576-spdif", "rockchip,rk3568-spdif"; + reg = <0x0 0x2a670000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SPDIF_TX0>, <&cru HCLK_SPDIF_TX0>; + clock-names = "mclk", "hclk"; + dmas = <&dmac0 5>; + dma-names = "tx"; + power-domains = <&power RK3576_PD_AUDIO>; + pinctrl-names = "default"; + pinctrl-0 = <&spdifm0_tx0>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + spdif_tx1: spdif-tx@2a680000 { + compatible = "rockchip,rk3576-spdif", "rockchip,rk3568-spdif"; + reg = <0x0 0x2a680000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SPDIF_TX1>, <&cru HCLK_SPDIF_TX1>; + clock-names = "mclk", "hclk"; + dmas = <&dmac1 5>; + dma-names = "tx"; + power-domains = <&power RK3576_PD_AUDIO>; + pinctrl-names = "default"; + pinctrl-0 = <&spdifm0_tx1>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + gic: interrupt-controller@2a701000 { compatible = "arm,gic-400"; reg = <0x0 0x2a701000 0 0x10000>, diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi index 7fe9593d8c19..4fb8888c281c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi @@ -907,7 +907,7 @@ #size-cells = <0>; #power-domain-cells = <0>; - power-domain@RK3588_PD_RKVDEC0 { + pd_rkvdec0: power-domain@RK3588_PD_RKVDEC0 { reg = ; clocks = <&cru HCLK_RKVDEC0>, <&cru HCLK_VDPU_ROOT>, @@ -917,7 +917,7 @@ pm_qos = <&qos_rkvdec0>; #power-domain-cells = <0>; }; - power-domain@RK3588_PD_RKVDEC1 { + pd_rkvdec1: power-domain@RK3588_PD_RKVDEC1 { reg = ; clocks = <&cru HCLK_RKVDEC1>, <&cru HCLK_VDPU_ROOT>, @@ -926,7 +926,7 @@ pm_qos = <&qos_rkvdec1>; #power-domain-cells = <0>; }; - power-domain@RK3588_PD_VENC0 { + pd_venc0: power-domain@RK3588_PD_VENC0 { reg = ; clocks = <&cru HCLK_RKVENC0>, <&cru ACLK_RKVENC0>; @@ -937,7 +937,7 @@ #size-cells = <0>; #power-domain-cells = <0>; - power-domain@RK3588_PD_VENC1 { + pd_venc1: power-domain@RK3588_PD_VENC1 { reg = ; clocks = <&cru HCLK_RKVENC1>, <&cru HCLK_RKVENC0>, diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts index c9d284cb738b..09bc7b68dcc0 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts @@ -568,6 +568,22 @@ domain-supply = <&vdd_gpu_s0>; }; +&pd_rkvdec0 { + domain-supply = <&vdd_vdenc_s0>; +}; + +&pd_rkvdec1 { + domain-supply = <&vdd_vdenc_s0>; +}; + +&pd_venc0 { + domain-supply = <&vdd_vdenc_s0>; +}; + +&pd_venc1 { + domain-supply = <&vdd_vdenc_s0>; +}; + &pinctrl { audio { hp_detect: headphone-detect { diff --git a/arch/arm64/boot/dts/rockchip/rk3588-jaguar.dts b/arch/arm64/boot/dts/rockchip/rk3588-jaguar.dts index 952affaf455c..5f5d89a33a4a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-jaguar.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-jaguar.dts @@ -86,25 +86,16 @@ }; }; - /* - * 100MHz reference clock for PCIe peripherals from PI6C557-05BLE - * clock generator. - * The clock output is gated via the OE pin on the clock generator. - * This is modeled as a fixed-clock plus a gpio-gate-clock. - */ - pcie_refclk_gen: pcie-refclk-gen-clock { - compatible = "fixed-clock"; + /* 100MHz PCIe reference clock from PI6C557-05BLE */ + pcie_refclk: pcie-clock-generator { + compatible = "gated-fixed-clock"; #clock-cells = <0>; clock-frequency = <100000000>; - }; - - pcie_refclk: pcie-refclk-clock { - compatible = "gpio-gate-clock"; - clocks = <&pcie_refclk_gen>; - #clock-cells = <0>; + clock-output-names = "pcie-refclk-clock"; enable-gpios = <&gpio0 RK_PC6 GPIO_ACTIVE_LOW>; /* PCIE30X4_CLKREQN_M0 */ pinctrl-names = "default"; pinctrl-0 = <&pcie30x4_clkreqn_m0>; + vdd-supply = <&vcca_3v3_s0>; }; pps { @@ -588,7 +579,7 @@ pcie30x4 { pcie30x4_clkreqn_m0: pcie30x4-clkreqn-m0 { - rockchip,pins = <0 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>; + rockchip,pins = <0 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>; }; pcie30x4_perstn_m0: pcie30x4-perstn-m0 { diff --git a/arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi index 90e7fe254491..84b6b53f016a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "rk3588.dtsi" @@ -89,6 +90,14 @@ }; }; + fan: pwm-fan { + compatible = "pwm-fan"; + cooling-levels = <0 35 64 100 150 255>; + fan-supply = <&vcc5v0_sys>; + pwms = <&pwm1 0 50000 0>; + #cooling-cells = <2>; + }; + sound { compatible = "simple-audio-card"; pinctrl-names = "default"; @@ -590,6 +599,36 @@ status = "okay"; }; +&package_thermal { + polling-delay = <1000>; + + trips { + package_warm: package-warm { + temperature = <55000>; + hysteresis = <2000>; + type = "active"; + }; + + package_hot: package-hot { + temperature = <65000>; + hysteresis = <2000>; + type = "active"; + }; + }; + + cooling-maps { + map0 { + trip = <&package_warm>; + cooling-device = <&fan THERMAL_NO_LIMIT 1>; + }; + + map1 { + trip = <&package_hot>; + cooling-device = <&fan 2 THERMAL_NO_LIMIT>; + }; + }; +}; + &pcie2x1l0 { reset-gpios = <&gpio4 RK_PB3 GPIO_ACTIVE_HIGH>; vpcie3v3-supply = <&vcc_3v3_pcie20>; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5-itx.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5-itx.dts index de154adb1497..f7dd01d6fa0a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5-itx.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5-itx.dts @@ -367,6 +367,18 @@ status = "okay"; }; +&hdmi_receiver_cma { + status = "okay"; +}; + +&hdmi_receiver { + pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_det>; + pinctrl-names = "default"; + hpd-gpios = <&gpio1 RK_PC6 GPIO_ACTIVE_LOW>; + + status = "okay"; +}; + &hdptxphy1 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-5bp-5t.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-5bp-5t.dtsi index b3e76ad2d869..bf4a1d2e55ca 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-5bp-5t.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-5bp-5t.dtsi @@ -468,7 +468,8 @@ &pcie2x1l0 { pinctrl-names = "default"; - pinctrl-0 = <&pcie2_0_rst>; + pinctrl-0 = <&pcie2_0_rst>, <&pcie30x1m1_0_clkreqn>; + supports-clkreq; reset-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_HIGH>; vpcie3v3-supply = <&vcc3v3_pcie2x1l0>; status = "okay"; @@ -476,7 +477,8 @@ &pcie2x1l2 { pinctrl-names = "default"; - pinctrl-0 = <&pcie2_2_rst>; + pinctrl-0 = <&pcie2_2_rst>, <&pcie20x1m0_clkreqn>; + supports-clkreq; reset-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>; vpcie3v3-supply = <&vcc3v3_pcie2x1l2>; status = "okay"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi index 27269b7b08aa..a0e97481afb7 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi @@ -47,23 +47,16 @@ }; }; - /* - * 100MHz reference clock for PCIe peripherals from PI6C557-05BLE - * clock generator. - * The clock output is gated via the OE pin on the clock generator. - * This is modeled as a fixed-clock plus a gpio-gate-clock. - */ - pcie_refclk_gen: pcie-refclk-gen-clock { - compatible = "fixed-clock"; + /* 100MHz PCIe reference clock from PI6C557-05BLE */ + pcie_refclk: pcie-clock-generator { + compatible = "gated-fixed-clock"; #clock-cells = <0>; clock-frequency = <100000000>; - }; - - pcie_refclk: pcie-refclk-clock { - compatible = "gpio-gate-clock"; - clocks = <&pcie_refclk_gen>; - #clock-cells = <0>; + clock-output-names = "pcie-refclk-clock"; enable-gpios = <&gpio4 RK_PB4 GPIO_ACTIVE_HIGH>; /* PCIE30X4_CLKREQN_M1_L */ + pinctrl-names = "default"; + pinctrl-0 = <&pcie30x4_clkreqn_m1_l>; + vdd-supply = <&vcca_3v3_s0>; }; vcc_1v1_nldo_s3: regulator-vcc-1v1-nldo-s3 { @@ -362,6 +355,12 @@ }; }; + pcie30x4 { + pcie30x4_clkreqn_m1_l: pcie30x4-clkreqn-m1-l { + rockchip,pins = <4 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + usb3 { usb3_id: usb3-id { rockchip,pins = diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588s-evb1-v10.dts index f82050597ab3..d6b62cd1b90b 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-evb1-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-evb1-v10.dts @@ -381,6 +381,22 @@ domain-supply = <&vdd_gpu_s0>; }; +&pd_rkvdec0 { + domain-supply = <&vdd_vdenc_s0>; +}; + +&pd_rkvdec1 { + domain-supply = <&vdd_vdenc_s0>; +}; + +&pd_venc0 { + domain-supply = <&vdd_vdenc_s0>; +}; + +&pd_venc1 { + domain-supply = <&vdd_vdenc_s0>; +}; + &pinctrl { audio { hp_detect: headphone-detect { diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-gameforce-ace.dts b/arch/arm64/boot/dts/rockchip/rk3588s-gameforce-ace.dts index e8ad525ba3f9..89618394c0bf 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-gameforce-ace.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-gameforce-ace.dts @@ -60,8 +60,8 @@ reg = <0>; abs-flat = <40>; abs-fuzz = <30>; - abs-range = <0 4095>; - linux,code = ; + abs-range = <4095 0>; + linux,code = ; }; axis@1 { @@ -69,7 +69,7 @@ abs-flat = <40>; abs-fuzz = <30>; abs-range = <0 4095>; - linux,code = ; + linux,code = ; }; axis@2 { @@ -77,7 +77,7 @@ abs-flat = <40>; abs-fuzz = <30>; abs-range = <0 4095>; - linux,code = ; + linux,code = ; }; axis@3 { @@ -85,7 +85,7 @@ abs-flat = <40>; abs-fuzz = <30>; abs-range = <0 4095>; - linux,code = ; + linux,code = ; }; }; @@ -318,7 +318,7 @@ compatible = "pwm-fan"; #cooling-cells = <2>; cooling-levels = <0 120 150 180 210 240 255>; - fan-supply = <&vcc5v0_sys>; + fan-supply = <&vcc5v0_spk>; interrupt-parent = <&gpio4>; interrupts = ; pulses-per-revolution = <4>; diff --git a/arch/arm64/boot/dts/st/stm32mp211.dtsi b/arch/arm64/boot/dts/st/stm32mp211.dtsi index cd078a16065e..4bfd58b26b51 100644 --- a/arch/arm64/boot/dts/st/stm32mp211.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp211.dtsi @@ -112,6 +112,22 @@ }; }; + bsec: efuse@44000000 { + compatible = "st,stm32mp25-bsec"; + reg = <0x44000000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + part_number_otp@24 { + reg = <0x24 0x4>; + }; + + package_otp@1e8 { + reg = <0x1e8 0x1>; + bits = <0 3>; + }; + }; + syscfg: syscon@44230000 { compatible = "st,stm32mp21-syscfg", "syscon"; reg = <0x44230000 0x0 0x10000>; diff --git a/arch/arm64/boot/dts/st/stm32mp215f-dk.dts b/arch/arm64/boot/dts/st/stm32mp215f-dk.dts index 7bdaeaa5ab0f..a1285abc80ca 100644 --- a/arch/arm64/boot/dts/st/stm32mp215f-dk.dts +++ b/arch/arm64/boot/dts/st/stm32mp215f-dk.dts @@ -44,6 +44,10 @@ status = "okay"; }; +&bsec { + bootph-all; +}; + &usart2 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/st/stm32mp231.dtsi b/arch/arm64/boot/dts/st/stm32mp231.dtsi index b5d81d1ee153..9e1d240888ff 100644 --- a/arch/arm64/boot/dts/st/stm32mp231.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp231.dtsi @@ -45,6 +45,12 @@ clock-frequency = <0>; }; + clk_flexgen_27_fixed: clk-54000000 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <54000000>; + }; + clk_rcbsec: clk-64000000 { compatible = "fixed-clock"; #clock-cells = <0>; @@ -354,7 +360,7 @@ #address-cells = <1>; #size-cells = <0>; interrupt-names = "event"; - interrupts = ; + interrupts-extended = <&exti1 21 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc CK_KER_I2C1>; resets = <&rcc I2C1_R>; dmas = <&hpdma 27 0x20 0x3012>, @@ -363,6 +369,7 @@ access-controllers = <&rifsc 41>; power-domains = <&cluster_pd>; i2c-analog-filter; + wakeup-source; status = "disabled"; }; @@ -372,7 +379,7 @@ #address-cells = <1>; #size-cells = <0>; interrupt-names = "event"; - interrupts = ; + interrupts-extended = <&exti1 22 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc CK_KER_I2C2>; resets = <&rcc I2C2_R>; dmas = <&hpdma 30 0x20 0x3012>, @@ -381,6 +388,7 @@ access-controllers = <&rifsc 42>; power-domains = <&cluster_pd>; i2c-analog-filter; + wakeup-source; status = "disabled"; }; @@ -390,7 +398,7 @@ #address-cells = <1>; #size-cells = <0>; interrupt-names = "event"; - interrupts = ; + interrupts-extended = <&exti1 50 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc CK_KER_I2C7>; resets = <&rcc I2C7_R>; dmas = <&hpdma 45 0x20 0x3012>, @@ -399,6 +407,7 @@ access-controllers = <&rifsc 47>; power-domains = <&cluster_pd>; i2c-analog-filter; + wakeup-source; status = "disabled"; }; @@ -668,7 +677,7 @@ #address-cells = <1>; #size-cells = <0>; interrupt-names = "event"; - interrupts = ; + interrupts-extended = <&exti2 25 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc CK_KER_I2C8>; resets = <&rcc I2C8_R>; dmas = <&hpdma 168 0x20 0x3012>, @@ -677,6 +686,7 @@ access-controllers = <&rifsc 48>; power-domains = <&cluster_pd>; i2c-analog-filter; + wakeup-source; status = "disabled"; }; @@ -970,6 +980,7 @@ syscfg: syscon@44230000 { compatible = "st,stm32mp23-syscfg", "syscon"; reg = <0x44230000 0x10000>; + #clock-cells = <0>; }; pinctrl: pinctrl@44240000 { @@ -1193,6 +1204,18 @@ <&intc GIC_SPI 213 IRQ_TYPE_LEVEL_HIGH>; /* EXTI_70 */ }; + ltdc: display-controller@48010000 { + compatible = "st,stm32mp251-ltdc"; + reg = <0x48010000 0x400>; + interrupts = , + ; + clocks = <&rcc CK_KER_LTDC>, <&rcc CK_BUS_LTDC>; + clock-names = "lcd", "bus"; + resets = <&rcc LTDC_R>; + access-controllers = <&rifsc 80>; + status = "disabled"; + }; + intc: interrupt-controller@4ac10000 { compatible = "arm,gic-400"; reg = <0x4ac10000 0x1000>, diff --git a/arch/arm64/boot/dts/st/stm32mp235.dtsi b/arch/arm64/boot/dts/st/stm32mp235.dtsi index 2719c088dd59..7a87c344a4fe 100644 --- a/arch/arm64/boot/dts/st/stm32mp235.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp235.dtsi @@ -5,7 +5,25 @@ */ #include "stm32mp233.dtsi" +<dc { + compatible = "st,stm32mp255-ltdc"; + clocks = <&clk_flexgen_27_fixed>, <&rcc CK_BUS_LTDC>, <&syscfg>, <&lvds>; + clock-names = "lcd", "bus", "ref", "lvds"; +}; + &rifsc { + lvds: lvds@48060000 { + compatible = "st,stm32mp255-lvds", "st,stm32mp25-lvds"; + reg = <0x48060000 0x2000>; + #clock-cells = <0>; + clocks = <&rcc CK_BUS_LVDS>, <&rcc CK_KER_LVDSPHY>; + clock-names = "pclk", "ref"; + resets = <&rcc LVDS_R>; + access-controllers = <&rifsc 84>; + power-domains = <&cluster_pd>; + status = "disabled"; + }; + vdec: vdec@480d0000 { compatible = "st,stm32mp25-vdec"; reg = <0x480d0000 0x3c8>; diff --git a/arch/arm64/boot/dts/st/stm32mp235f-dk.dts b/arch/arm64/boot/dts/st/stm32mp235f-dk.dts index 5ecc5ef61590..dd4efbe5a46e 100644 --- a/arch/arm64/boot/dts/st/stm32mp235f-dk.dts +++ b/arch/arm64/boot/dts/st/stm32mp235f-dk.dts @@ -77,6 +77,42 @@ }; }; + panel_lvds: display { + compatible = "edt,etml0700z9ndha", "panel-lvds"; + enable-gpios = <&gpioi 4 GPIO_ACTIVE_HIGH>; + backlight = <&panel_lvds_backlight>; + power-supply = <&scmi_v3v3>; + width-mm = <156>; + height-mm = <92>; + data-mapping = "vesa-24"; + status = "okay"; + + panel-timing { + clock-frequency = <54000000>; + hactive = <1024>; + vactive = <600>; + hfront-porch = <150>; + hback-porch = <150>; + hsync-len = <21>; + vfront-porch = <24>; + vback-porch = <24>; + vsync-len = <21>; + }; + + port { + lvds_panel_in: endpoint { + remote-endpoint = <&lvds_out0>; + }; + }; + }; + + panel_lvds_backlight: backlight { + compatible = "gpio-backlight"; + gpios = <&gpioi 4 GPIO_ACTIVE_HIGH>; + default-on; + status = "okay"; + }; + reserved-memory { #address-cells = <2>; #size-cells = <2>; @@ -117,11 +153,105 @@ }; }; +&i2c2 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c2_pins_b>; + pinctrl-1 = <&i2c2_sleep_pins_b>; + i2c-scl-rising-time-ns = <108>; + i2c-scl-falling-time-ns = <12>; + clock-frequency = <400000>; + status = "okay"; + /* spare dmas for other usage */ + /delete-property/dmas; + /delete-property/dma-names; + + ili2511: ili2511@41 { + compatible = "ilitek,ili251x"; + reg = <0x41>; + interrupt-parent = <&gpioi>; + interrupts = <6 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpioi 0 GPIO_ACTIVE_LOW>; + status = "okay"; + }; +}; + +&i2c8 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c8_pins_a>; + pinctrl-1 = <&i2c8_sleep_pins_a>; + i2c-scl-rising-time-ns = <185>; + i2c-scl-falling-time-ns = <20>; + clock-frequency = <100000>; + status = "disabled"; + /* spare dmas for other usage */ + /delete-property/dmas; + /delete-property/dma-names; +}; + +<dc { + status = "okay"; + port { + ltdc_ep0_out: endpoint { + remote-endpoint = <&lvds_in>; + }; + }; +}; + +&lvds { + status = "okay"; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds_in: endpoint { + remote-endpoint = <<dc_ep0_out>; + }; + }; + + port@1 { + reg = <1>; + lvds_out0: endpoint { + remote-endpoint = <&lvds_panel_in>; + }; + }; + }; +}; + &scmi_regu { scmi_vddio1: regulator@0 { regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; }; + scmi_vddcore: regulator@11 { + reg = ; + regulator-name = "vddcore"; + }; + scmi_v1v8: regulator@14 { + reg = ; + regulator-name = "v1v8"; + }; + scmi_v3v3: regulator@16 { + reg = ; + regulator-name = "v3v3"; + }; + scmi_vdd_emmc: regulator@18 { + reg = ; + regulator-name = "vdd_emmc"; + }; + scmi_vdd3v3_usb: regulator@20 { + reg = ; + regulator-name = "vdd3v3_usb"; + }; + scmi_v5v_hdmi: regulator@21 { + reg = ; + regulator-name = "v5v_hdmi"; + }; + scmi_v5v_vconn: regulator@22 { + reg = ; + regulator-name = "v5v_vconn"; + }; scmi_vdd_sdcard: regulator@23 { reg = ; regulator-name = "vdd_sdcard"; diff --git a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi index c34cd33cd855..456ece7f8ebc 100644 --- a/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp25-pinctrl.dtsi @@ -6,6 +6,7 @@ #include &pinctrl { + /omit-if-no-ref/ eth1_mdio_pins_a: eth1-mdio-0 { pins1 { pinmux = ; /* ETH_MDC */ @@ -21,6 +22,7 @@ }; }; + /omit-if-no-ref/ eth1_mdio_sleep_pins_a: eth1-mdio-sleep-0 { pins1 { pinmux = , /* ETH_MDC */ @@ -28,6 +30,7 @@ }; }; + /omit-if-no-ref/ eth1_rgmii_pins_a: eth1-rgmii-0 { pins1 { pinmux = , /* ETH_RGMII_TXD0 */ @@ -62,6 +65,7 @@ }; }; + /omit-if-no-ref/ eth1_rgmii_sleep_pins_a: eth1-rgmii-sleep-0 { pins { pinmux = , /* ETH_RGMII_TXD0 */ @@ -80,6 +84,7 @@ }; }; + /omit-if-no-ref/ eth1_rgmii_pins_b: eth1-rgmii-1 { pins1 { pinmux = , /* ETH_RGMII_TXD0 */ @@ -114,6 +119,7 @@ }; }; + /omit-if-no-ref/ eth1_rgmii_sleep_pins_b: eth1-rgmii-sleep-1 { pins { pinmux = , /* ETH_RGMII_TXD0 */ @@ -134,6 +140,7 @@ }; }; + /omit-if-no-ref/ eth2_rgmii_pins_a: eth2-rgmii-0 { pins1 { pinmux = , /* ETH_RGMII_TXD0 */ @@ -175,6 +182,7 @@ }; }; + /omit-if-no-ref/ eth2_rgmii_sleep_pins_a: eth2-rgmii-sleep-0 { pins { pinmux = , /* ETH_RGMII_TXD0 */ @@ -195,6 +203,7 @@ }; }; + /omit-if-no-ref/ i2c2_pins_a: i2c2-0 { pins { pinmux = , /* I2C2_SCL */ @@ -205,6 +214,7 @@ }; }; + /omit-if-no-ref/ i2c2_sleep_pins_a: i2c2-sleep-0 { pins { pinmux = , /* I2C2_SCL */ @@ -212,6 +222,26 @@ }; }; + /omit-if-no-ref/ + i2c2_pins_b: i2c2-1 { + pins { + pinmux = , /* I2C2_SCL */ + ; /* I2C2_SDA */ + bias-disable; + drive-open-drain; + slew-rate = <0>; + }; + }; + + /omit-if-no-ref/ + i2c2_sleep_pins_b: i2c2-sleep-1 { + pins { + pinmux = , /* I2C2_SCL */ + ; /* I2C2_SDA */ + }; + }; + + /omit-if-no-ref/ ospi_port1_clk_pins_a: ospi-port1-clk-0 { pins { pinmux = ; /* OSPI1_CLK */ @@ -221,12 +251,14 @@ }; }; + /omit-if-no-ref/ ospi_port1_clk_sleep_pins_a: ospi-port1-clk-sleep-0 { pins { pinmux = ; /* OSPI1_CLK */ }; }; + /omit-if-no-ref/ ospi_port1_cs0_pins_a: ospi-port1-cs0-0 { pins { pinmux = ; /* OSPI_NCS0 */ @@ -236,12 +268,14 @@ }; }; + /omit-if-no-ref/ ospi_port1_cs0_sleep_pins_a: ospi-port1-cs0-sleep-0 { pins { pinmux = ; /* OSPI_NCS0 */ }; }; + /omit-if-no-ref/ ospi_port1_io03_pins_a: ospi-port1-io03-0 { pins { pinmux = , /* OSPI_IO0 */ @@ -254,6 +288,7 @@ }; }; + /omit-if-no-ref/ ospi_port1_io03_sleep_pins_a: ospi-port1-io03-sleep-0 { pins { pinmux = , /* OSPI_IO0 */ @@ -263,6 +298,7 @@ }; }; + /omit-if-no-ref/ pcie_pins_a: pcie-0 { pins { pinmux = ; @@ -270,6 +306,7 @@ }; }; + /omit-if-no-ref/ pcie_init_pins_a: pcie-init-0 { pins { pinmux = ; @@ -277,12 +314,14 @@ }; }; + /omit-if-no-ref/ pcie_sleep_pins_a: pcie-sleep-0 { pins { pinmux = ; }; }; + /omit-if-no-ref/ pwm3_pins_a: pwm3-0 { pins { pinmux = ; /* TIM3_CH2 */ @@ -292,12 +331,14 @@ }; }; + /omit-if-no-ref/ pwm3_sleep_pins_a: pwm3-sleep-0 { pins { pinmux = ; /* TIM3_CH2 */ }; }; + /omit-if-no-ref/ pwm8_pins_a: pwm8-0 { pins { pinmux = , /* TIM8_CH1 */ @@ -308,6 +349,7 @@ }; }; + /omit-if-no-ref/ pwm8_sleep_pins_a: pwm8-sleep-0 { pins { pinmux = , /* TIM8_CH1 */ @@ -315,6 +357,7 @@ }; }; + /omit-if-no-ref/ pwm12_pins_a: pwm12-0 { pins { pinmux = ; /* TIM12_CH2 */ @@ -324,12 +367,14 @@ }; }; + /omit-if-no-ref/ pwm12_sleep_pins_a: pwm12-sleep-0 { pins { pinmux = ; /* TIM12_CH2 */ }; }; + /omit-if-no-ref/ sdmmc1_b4_pins_a: sdmmc1-b4-0 { pins1 { pinmux = , /* SDMMC1_D0 */ @@ -349,6 +394,7 @@ }; }; + /omit-if-no-ref/ sdmmc1_b4_od_pins_a: sdmmc1-b4-od-0 { pins1 { pinmux = , /* SDMMC1_D0 */ @@ -373,6 +419,7 @@ }; }; + /omit-if-no-ref/ sdmmc1_b4_sleep_pins_a: sdmmc1-b4-sleep-0 { pins { pinmux = , /* SDMMC1_D0 */ @@ -384,6 +431,7 @@ }; }; + /omit-if-no-ref/ spi3_pins_a: spi3-0 { pins1 { pinmux = , /* SPI3_SCK */ @@ -398,6 +446,7 @@ }; }; + /omit-if-no-ref/ spi3_sleep_pins_a: spi3-sleep-0 { pins1 { pinmux = , /* SPI3_SCK */ @@ -406,6 +455,7 @@ }; }; + /omit-if-no-ref/ tim10_counter_pins_a: tim10-counter-0 { pins { pinmux = ; /* TIM10_CH1 */ @@ -413,6 +463,7 @@ }; }; + /omit-if-no-ref/ tim10_counter_sleep_pins_a: tim10-counter-sleep-0 { pins { pinmux = ; /* TIM10_CH1 */ @@ -420,6 +471,7 @@ }; }; + /omit-if-no-ref/ usart2_pins_a: usart2-0 { pins1 { pinmux = ; /* USART2_TX */ @@ -433,6 +485,7 @@ }; }; + /omit-if-no-ref/ usart2_idle_pins_a: usart2-idle-0 { pins1 { pinmux = ; /* USART2_TX */ @@ -443,6 +496,7 @@ }; }; + /omit-if-no-ref/ usart2_sleep_pins_a: usart2-sleep-0 { pins { pinmux = , /* USART2_TX */ @@ -450,6 +504,7 @@ }; }; + /omit-if-no-ref/ usart6_pins_a: usart6-0 { pins1 { pinmux = , /* USART6_TX */ @@ -465,6 +520,7 @@ }; }; + /omit-if-no-ref/ usart6_idle_pins_a: usart6-idle-0 { pins1 { pinmux = , /* USART6_TX */ @@ -482,6 +538,7 @@ }; }; + /omit-if-no-ref/ usart6_sleep_pins_a: usart6-sleep-0 { pins { pinmux = , /* USART6_TX */ @@ -493,6 +550,7 @@ }; &pinctrl_z { + /omit-if-no-ref/ i2c8_pins_a: i2c8-0 { pins { pinmux = , /* I2C8_SCL */ @@ -503,15 +561,15 @@ }; }; + /omit-if-no-ref/ i2c8_sleep_pins_a: i2c8-sleep-0 { pins { pinmux = , /* I2C8_SCL */ ; /* I2C8_SDA */ }; }; -}; -&pinctrl_z { + /omit-if-no-ref/ spi8_pins_a: spi8-0 { pins1 { pinmux = , /* SPI8_SCK */ @@ -526,6 +584,7 @@ }; }; + /omit-if-no-ref/ spi8_sleep_pins_a: spi8-sleep-0 { pins1 { pinmux = , /* SPI8_SCK */ diff --git a/arch/arm64/boot/dts/st/stm32mp251.dtsi b/arch/arm64/boot/dts/st/stm32mp251.dtsi index 8b925ed0d881..673fbc5632e6 100644 --- a/arch/arm64/boot/dts/st/stm32mp251.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp251.dtsi @@ -773,7 +773,7 @@ compatible = "st,stm32mp25-i2c"; reg = <0x40120000 0x400>; interrupt-names = "event"; - interrupts = ; + interrupts-extended = <&exti1 21 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc CK_KER_I2C1>; resets = <&rcc I2C1_R>; #address-cells = <1>; @@ -784,6 +784,7 @@ access-controllers = <&rifsc 41>; power-domains = <&CLUSTER_PD>; i2c-analog-filter; + wakeup-source; status = "disabled"; }; @@ -791,7 +792,7 @@ compatible = "st,stm32mp25-i2c"; reg = <0x40130000 0x400>; interrupt-names = "event"; - interrupts = ; + interrupts-extended = <&exti1 22 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc CK_KER_I2C2>; resets = <&rcc I2C2_R>; #address-cells = <1>; @@ -802,6 +803,7 @@ access-controllers = <&rifsc 42>; power-domains = <&CLUSTER_PD>; i2c-analog-filter; + wakeup-source; status = "disabled"; }; @@ -809,7 +811,7 @@ compatible = "st,stm32mp25-i2c"; reg = <0x40140000 0x400>; interrupt-names = "event"; - interrupts = ; + interrupts-extended = <&exti1 23 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc CK_KER_I2C3>; resets = <&rcc I2C3_R>; #address-cells = <1>; @@ -820,6 +822,7 @@ access-controllers = <&rifsc 43>; power-domains = <&CLUSTER_PD>; i2c-analog-filter; + wakeup-source; status = "disabled"; }; @@ -827,7 +830,7 @@ compatible = "st,stm32mp25-i2c"; reg = <0x40150000 0x400>; interrupt-names = "event"; - interrupts = ; + interrupts-extended = <&exti1 24 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc CK_KER_I2C4>; resets = <&rcc I2C4_R>; #address-cells = <1>; @@ -838,6 +841,7 @@ access-controllers = <&rifsc 44>; power-domains = <&CLUSTER_PD>; i2c-analog-filter; + wakeup-source; status = "disabled"; }; @@ -845,7 +849,7 @@ compatible = "st,stm32mp25-i2c"; reg = <0x40160000 0x400>; interrupt-names = "event"; - interrupts = ; + interrupts-extended = <&exti1 25 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc CK_KER_I2C5>; resets = <&rcc I2C5_R>; #address-cells = <1>; @@ -856,6 +860,7 @@ access-controllers = <&rifsc 45>; power-domains = <&CLUSTER_PD>; i2c-analog-filter; + wakeup-source; status = "disabled"; }; @@ -863,7 +868,7 @@ compatible = "st,stm32mp25-i2c"; reg = <0x40170000 0x400>; interrupt-names = "event"; - interrupts = ; + interrupts-extended = <&exti1 49 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc CK_KER_I2C6>; resets = <&rcc I2C6_R>; #address-cells = <1>; @@ -874,6 +879,7 @@ access-controllers = <&rifsc 46>; power-domains = <&CLUSTER_PD>; i2c-analog-filter; + wakeup-source; status = "disabled"; }; @@ -881,7 +887,7 @@ compatible = "st,stm32mp25-i2c"; reg = <0x40180000 0x400>; interrupt-names = "event"; - interrupts = ; + interrupts-extended = <&exti1 50 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc CK_KER_I2C7>; resets = <&rcc I2C7_R>; #address-cells = <1>; @@ -892,6 +898,7 @@ access-controllers = <&rifsc 47>; power-domains = <&CLUSTER_PD>; i2c-analog-filter; + wakeup-source; status = "disabled"; }; @@ -1473,7 +1480,7 @@ compatible = "st,stm32mp25-i2c"; reg = <0x46040000 0x400>; interrupt-names = "event"; - interrupts = ; + interrupts-extended = <&exti2 25 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc CK_KER_I2C8>; resets = <&rcc I2C8_R>; #address-cells = <1>; @@ -1484,6 +1491,7 @@ access-controllers = <&rifsc 48>; power-domains = <&CLUSTER_PD>; i2c-analog-filter; + wakeup-source; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/st/stm32mp257f-dk.dts b/arch/arm64/boot/dts/st/stm32mp257f-dk.dts index 4135e7c0d9a3..8daf3dfd5133 100644 --- a/arch/arm64/boot/dts/st/stm32mp257f-dk.dts +++ b/arch/arm64/boot/dts/st/stm32mp257f-dk.dts @@ -84,6 +84,42 @@ }; }; + panel_lvds: display { + compatible = "edt,etml0700z9ndha", "panel-lvds"; + enable-gpios = <&gpioi 4 GPIO_ACTIVE_HIGH>; + backlight = <&panel_lvds_backlight>; + power-supply = <&scmi_v3v3>; + width-mm = <156>; + height-mm = <92>; + data-mapping = "vesa-24"; + status = "okay"; + + panel-timing { + clock-frequency = <54000000>; + hactive = <1024>; + vactive = <600>; + hfront-porch = <150>; + hback-porch = <150>; + hsync-len = <21>; + vfront-porch = <24>; + vback-porch = <24>; + vsync-len = <21>; + }; + + port { + lvds_panel_in: endpoint { + remote-endpoint = <&lvds_out0>; + }; + }; + }; + + panel_lvds_backlight: backlight { + compatible = "gpio-backlight"; + gpios = <&gpioi 4 GPIO_ACTIVE_HIGH>; + default-on; + status = "okay"; + }; + reserved-memory { #address-cells = <2>; #size-cells = <2>; @@ -124,11 +160,105 @@ }; }; +&i2c2 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c2_pins_b>; + pinctrl-1 = <&i2c2_sleep_pins_b>; + i2c-scl-rising-time-ns = <108>; + i2c-scl-falling-time-ns = <12>; + clock-frequency = <400000>; + status = "okay"; + /* spare dmas for other usage */ + /delete-property/dmas; + /delete-property/dma-names; + + ili2511: ili2511@41 { + compatible = "ilitek,ili251x"; + reg = <0x41>; + interrupt-parent = <&gpioi>; + interrupts = <6 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&gpioi 0 GPIO_ACTIVE_LOW>; + status = "okay"; + }; +}; + +&i2c8 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c8_pins_a>; + pinctrl-1 = <&i2c8_sleep_pins_a>; + i2c-scl-rising-time-ns = <185>; + i2c-scl-falling-time-ns = <20>; + clock-frequency = <100000>; + status = "disabled"; + /* spare dmas for other usage */ + /delete-property/dmas; + /delete-property/dma-names; +}; + +<dc { + status = "okay"; + port { + ltdc_ep0_out: endpoint { + remote-endpoint = <&lvds_in>; + }; + }; +}; + +&lvds { + status = "okay"; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds_in: endpoint { + remote-endpoint = <<dc_ep0_out>; + }; + }; + + port@1 { + reg = <1>; + lvds_out0: endpoint { + remote-endpoint = <&lvds_panel_in>; + }; + }; + }; +}; + &scmi_regu { scmi_vddio1: regulator@0 { regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; }; + scmi_vddcore: regulator@11 { + reg = ; + regulator-name = "vddcore"; + }; + scmi_v1v8: regulator@14 { + reg = ; + regulator-name = "v1v8"; + }; + scmi_v3v3: regulator@16 { + reg = ; + regulator-name = "v3v3"; + }; + scmi_vdd_emmc: regulator@18 { + reg = ; + regulator-name = "vdd_emmc"; + }; + scmi_vdd3v3_usb: regulator@20 { + reg = ; + regulator-name = "vdd3v3_usb"; + }; + scmi_v5v_hdmi: regulator@21 { + reg = ; + regulator-name = "v5v_hdmi"; + }; + scmi_v5v_vconn: regulator@22 { + reg = ; + regulator-name = "v5v_vconn"; + }; scmi_vdd_sdcard: regulator@23 { reg = ; regulator-name = "vdd_sdcard"; diff --git a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts b/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts index 852a73b0c516..14e033f365e3 100644 --- a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts +++ b/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts @@ -261,6 +261,9 @@ i2c-scl-falling-time-ns = <13>; clock-frequency = <400000>; status = "okay"; + /* spare dmas for other usage */ + /delete-property/dmas; + /delete-property/dma-names; imx335: camera@1a { compatible = "sony,imx335"; @@ -299,6 +302,9 @@ i2c-scl-falling-time-ns = <7>; clock-frequency = <400000>; status = "disabled"; + /* spare dmas for other usage */ + /delete-property/dmas; + /delete-property/dma-names; }; &ommanager { diff --git a/arch/arm64/boot/dts/ti/Makefile b/arch/arm64/boot/dts/ti/Makefile index ba01a929e06f..5269c9619b65 100644 --- a/arch/arm64/boot/dts/ti/Makefile +++ b/arch/arm64/boot/dts/ti/Makefile @@ -19,11 +19,13 @@ dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-nonwifi-dev.dtb dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-nonwifi-ivy.dtb dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-nonwifi-mallow.dtb dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-nonwifi-yavia.dtb +dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-nonwifi-zinnia.dtb dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-wifi-dahlia.dtb dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-wifi-dev.dtb dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-wifi-ivy.dtb dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-wifi-mallow.dtb dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-wifi-yavia.dtb +dtb-$(CONFIG_ARCH_K3) += k3-am625-verdin-wifi-zinnia.dtb dtb-$(CONFIG_ARCH_K3) += k3-am62x-phyboard-lyra-gpio-fan.dtbo dtb-$(CONFIG_ARCH_K3) += k3-am62-lp-sk.dtb dtb-$(CONFIG_ARCH_K3) += k3-am62-lp-sk-nand.dtbo @@ -62,6 +64,7 @@ dtb-$(CONFIG_ARCH_K3) += k3-am62x-sk-hdmi-audio.dtbo # Boards with AM64x SoC dtb-$(CONFIG_ARCH_K3) += k3-am642-evm.dtb +dtb-$(CONFIG_ARCH_K3) += k3-am642-evm-icssg0.dtbo dtb-$(CONFIG_ARCH_K3) += k3-am642-evm-icssg1-dualemac.dtbo dtb-$(CONFIG_ARCH_K3) += k3-am642-evm-icssg1-dualemac-mii.dtbo dtb-$(CONFIG_ARCH_K3) += k3-am642-evm-pcie0-ep.dtbo @@ -132,6 +135,12 @@ dtb-$(CONFIG_ARCH_K3) += k3-j721e-sk-csi2-dual-imx219.dtbo # Boards with J721s2 SoC dtb-$(CONFIG_ARCH_K3) += k3-am68-phyboard-izar.dtb +dtb-$(CONFIG_ARCH_K3) += k3-am68-phyboard-izar-lvds-ph128800t006.dtb +k3-am68-phyboard-izar-lvds-ph128800t006-dtbs := k3-am68-phyboard-izar.dtb \ + k3-am68-phyboard-izar-lvds-ph128800t006.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-am68-phyboard-izar-peb-av-15.dtb +k3-am68-phyboard-izar-peb-av-15-dtbs := k3-am68-phyboard-izar.dtb \ + k3-am68-phyboard-izar-peb-av-15.dtbo dtb-$(CONFIG_ARCH_K3) += k3-am68-sk-base-board.dtb dtb-$(CONFIG_ARCH_K3) += k3-am68-sk-base-board-pcie1-ep.dtbo dtb-$(CONFIG_ARCH_K3) += k3-j721s2-common-proc-board.dtb @@ -142,17 +151,7 @@ dtb-$(CONFIG_ARCH_K3) += k3-j721s2-evm-pcie1-ep.dtbo dtb-$(CONFIG_ARCH_K3) += k3-j721s2-evm-usb0-type-a.dtbo # Boards with J722s SoC -k3-am67a-kontron-sa67-dtbs := k3-am67a-kontron-sa67-base.dtb \ - k3-am67a-kontron-sa67-rtc-rv8263.dtbo k3-am67a-kontron-sa67-gbe1.dtbo -k3-am67a-kontron-sa67-ads2-dtbs := k3-am67a-kontron-sa67.dtb k3-am67a-kontron-sa67-ads2.dtbo dtb-$(CONFIG_ARCH_K3) += k3-am67a-beagley-ai.dtb -dtb-$(CONFIG_ARCH_K3) += k3-am67a-kontron-sa67.dtb -dtb-$(CONFIG_ARCH_K3) += k3-am67a-kontron-sa67-base.dtb -dtb-$(CONFIG_ARCH_K3) += k3-am67a-kontron-sa67-gbe1.dtbo -dtb-$(CONFIG_ARCH_K3) += k3-am67a-kontron-sa67-gpios.dtbo -dtb-$(CONFIG_ARCH_K3) += k3-am67a-kontron-sa67-rtc-rv8263.dtbo -dtb-$(CONFIG_ARCH_K3) += k3-am67a-kontron-sa67-ads2.dtb -dtb-$(CONFIG_ARCH_K3) += k3-am67a-kontron-sa67-ads2.dtbo dtb-$(CONFIG_ARCH_K3) += k3-j722s-evm.dtb dtb-$(CONFIG_ARCH_K3) += k3-j722s-evm-csi2-quad-rpi-cam-imx219.dtbo dtb-$(CONFIG_ARCH_K3) += k3-j722s-evm-csi2-quad-tevi-ov5640.dtbo @@ -218,6 +217,8 @@ k3-am62p5-sk-csi2-ov5640-dtbs := k3-am62p5-sk.dtb \ k3-am62x-sk-csi2-ov5640.dtbo k3-am62p5-sk-csi2-tevi-ov5640-dtbs := k3-am62p5-sk.dtb \ k3-am62x-sk-csi2-tevi-ov5640.dtbo +k3-am642-evm-icssg0-dtbs := \ + k3-am642-evm.dtb k3-am642-evm-icssg0.dtbo k3-am642-evm-icssg1-dualemac-dtbs := \ k3-am642-evm.dtb k3-am642-evm-icssg1-dualemac.dtbo k3-am642-evm-icssg1-dualemac-mii-dtbs := \ @@ -244,8 +245,6 @@ k3-am642-tqma64xxl-mbax4xxl-sdcard-dtbs := \ k3-am642-tqma64xxl-mbax4xxl.dtb k3-am64-tqma64xxl-mbax4xxl-sdcard.dtbo k3-am642-tqma64xxl-mbax4xxl-wlan-dtbs := \ k3-am642-tqma64xxl-mbax4xxl.dtb k3-am64-tqma64xxl-mbax4xxl-wlan.dtbo -k3-am67a-kontron-sa67-base-gpios-dtbs := \ - k3-am67a-kontron-sa67-base.dtb k3-am67a-kontron-sa67-gpios.dtbo k3-am68-sk-base-board-csi2-dual-imx219-dtbs := k3-am68-sk-base-board.dtb \ k3-j721e-sk-csi2-dual-imx219.dtbo k3-am68-sk-base-board-pcie1-ep-dtbs := k3-am68-sk-base-board.dtb \ @@ -306,6 +305,7 @@ dtb- += k3-am625-beagleplay-csi2-ov5640.dtb \ k3-am62p5-sk-csi2-imx219.dtb \ k3-am62p5-sk-csi2-ov5640.dtb \ k3-am62p5-sk-csi2-tevi-ov5640.dtb \ + k3-am642-evm-icssg0.dtb \ k3-am642-evm-icssg1-dualemac.dtb \ k3-am642-evm-icssg1-dualemac-mii.dtb \ k3-am642-evm-pcie0-ep.dtb \ @@ -318,7 +318,8 @@ dtb- += k3-am625-beagleplay-csi2-ov5640.dtb \ k3-am642-phyboard-electra-x27-gpio1-spi1-uart3.dtb \ k3-am642-tqma64xxl-mbax4xxl-sdcard.dtb \ k3-am642-tqma64xxl-mbax4xxl-wlan.dtb \ - k3-am67a-kontron-sa67-base-gpios.dtb \ + k3-am68-phyboard-izar-lvds-ph128800t006.dtb \ + k3-am68-phyboard-izar-peb-av-15.dtb \ k3-am68-sk-base-board-csi2-dual-imx219.dtb \ k3-am68-sk-base-board-pcie1-ep.dtb \ k3-am69-sk-csi2-dual-imx219.dtb \ diff --git a/arch/arm64/boot/dts/ti/k3-am62-lp-sk.dts b/arch/arm64/boot/dts/ti/k3-am62-lp-sk.dts index 3e2d8f669535..8a556fbbe08b 100644 --- a/arch/arm64/boot/dts/ti/k3-am62-lp-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-am62-lp-sk.dts @@ -88,13 +88,13 @@ AM62X_IOPAD(0x220, PIN_INPUT, 0) /* (V3) MMC0_CMD */ AM62X_IOPAD(0x218, PIN_INPUT, 0) /* (Y1) MMC0_CLK */ AM62X_IOPAD(0x214, PIN_INPUT, 0) /* (V2) MMC0_DAT0 */ - AM62X_IOPAD(0x210, PIN_INPUT, 0) /* (V1) MMC0_DAT1 */ - AM62X_IOPAD(0x20c, PIN_INPUT, 0) /* (W2) MMC0_DAT2 */ - AM62X_IOPAD(0x208, PIN_INPUT, 0) /* (W1) MMC0_DAT3 */ - AM62X_IOPAD(0x204, PIN_INPUT, 0) /* (Y2) MMC0_DAT4 */ - AM62X_IOPAD(0x200, PIN_INPUT, 0) /* (W3) MMC0_DAT5 */ - AM62X_IOPAD(0x1fc, PIN_INPUT, 0) /* (W4) MMC0_DAT6 */ - AM62X_IOPAD(0x1f8, PIN_INPUT, 0) /* (V4) MMC0_DAT7 */ + AM62X_IOPAD(0x210, PIN_INPUT_PULLUP, 0) /* (V1) MMC0_DAT1 */ + AM62X_IOPAD(0x20c, PIN_INPUT_PULLUP, 0) /* (W2) MMC0_DAT2 */ + AM62X_IOPAD(0x208, PIN_INPUT_PULLUP, 0) /* (W1) MMC0_DAT3 */ + AM62X_IOPAD(0x204, PIN_INPUT_PULLUP, 0) /* (Y2) MMC0_DAT4 */ + AM62X_IOPAD(0x200, PIN_INPUT_PULLUP, 0) /* (W3) MMC0_DAT5 */ + AM62X_IOPAD(0x1fc, PIN_INPUT_PULLUP, 0) /* (W4) MMC0_DAT6 */ + AM62X_IOPAD(0x1f8, PIN_INPUT_PULLUP, 0) /* (V4) MMC0_DAT7 */ >; }; diff --git a/arch/arm64/boot/dts/ti/k3-am62-phycore-som.dtsi b/arch/arm64/boot/dts/ti/k3-am62-phycore-som.dtsi index e15da771bc07..3baa653257bb 100644 --- a/arch/arm64/boot/dts/ti/k3-am62-phycore-som.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62-phycore-som.dtsi @@ -37,6 +37,13 @@ #size-cells = <2>; ranges; + linux,cma { + compatible = "shared-dma-pool"; + reusable; + size = <0x00 0x8000000>; + linux,cma-default; + }; + ramoops@9c700000 { compatible = "ramoops"; reg = <0x00 0x9c700000 0x00 0x00100000>; diff --git a/arch/arm64/boot/dts/ti/k3-am62-ti-ipc-firmware.dtsi b/arch/arm64/boot/dts/ti/k3-am62-ti-ipc-firmware.dtsi index ea69fab9b52b..ad247f53fe82 100644 --- a/arch/arm64/boot/dts/ti/k3-am62-ti-ipc-firmware.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62-ti-ipc-firmware.dtsi @@ -48,5 +48,6 @@ mboxes = <&mailbox0_cluster0 &mbox_r5_0>; memory-region = <&wkup_r5fss0_core0_dma_memory_region>, <&wkup_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; diff --git a/arch/arm64/boot/dts/ti/k3-am62-verdin-zinnia.dtsi b/arch/arm64/boot/dts/ti/k3-am62-verdin-zinnia.dtsi new file mode 100644 index 000000000000..0ce8357797ed --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-am62-verdin-zinnia.dtsi @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * Common dtsi for Verdin AM62 SoM on Zinnia carrier board + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/ti-am62 + * https://www.toradex.com/products/carrier-board/zinnia-carrier-board + */ + +#include +#include + +/ { + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_zinnia_leds>; + + /* LED1 Red - SODIMM 48 - LED1_R */ + led-0 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <1>; + gpios = <&main_gpio0 33 GPIO_ACTIVE_HIGH>; + }; + + /* LED1 Blue - SODIMM 46 - LED1_B */ + led-1 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <1>; + gpios = <&main_gpio0 34 GPIO_ACTIVE_HIGH>; + }; + + /* LED3 Red - SODIMM 44 - LED3_R */ + led-2 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <3>; + gpios = <&main_gpio0 37 GPIO_ACTIVE_HIGH>; + }; + + /* LED3 Green - SODIMM 54 - LED3_G */ + led-3 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <3>; + gpios = <&main_gpio0 11 GPIO_ACTIVE_HIGH>; + }; + + /* LED3 Blue - SODIMM 36 - LED3_B */ + led-4 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <3>; + gpios = <&main_gpio1 9 GPIO_ACTIVE_HIGH>; + }; + + /* LED4 Red - SODIMM 34 - LED4_R */ + led-5 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <4>; + gpios = <&main_gpio1 10 GPIO_ACTIVE_HIGH>; + }; + + /* LED4 Green - SODIMM 32 - LED4_G */ + led-6 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <4>; + gpios = <&main_gpio1 12 GPIO_ACTIVE_HIGH>; + }; + + /* LED4 Blue - SODIMM 30 - LED4_B */ + led-7 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <4>; + gpios = <&main_gpio1 11 GPIO_ACTIVE_HIGH>; + }; + }; + + zinnia-1v8-voltage { + compatible = "voltage-divider"; + /* Verdin ADC_4 */ + io-channels = <&verdin_som_adc 4>; + full-ohms = <39000>; /* 12K + 27K */ + output-ohms = <27000>; + }; + + zinnia-3v3-voltage { + compatible = "voltage-divider"; + /* Verdin ADC_3 */ + io-channels = <&verdin_som_adc 5>; + full-ohms = <54000>; /* 27K + 27K */ + output-ohms = <27000>; + }; + + zinnia-5v-voltage { + compatible = "voltage-divider"; + /* Verdin ADC_2 */ + io-channels = <&verdin_som_adc 6>; + full-ohms = <39000>; /* 27K + 12K */ + output-ohms = <12000>; + }; + + /* Zinnia Power Supply Input Voltage */ + zinnia-input-voltage { + compatible = "voltage-divider"; + /* Verdin ADC_1 */ + io-channels = <&verdin_som_adc 7>; + full-ohms = <204700>; /* 200K + 4.7K */ + output-ohms = <4700>; + }; +}; + +&main_pmx0 { + pinctrl_zinnia_leds: zinnia-leds-default-pins { + pinctrl-single,pins = < + AM62X_IOPAD(0x019c, PIN_INPUT, 7) /* (B18) MCASP0_AXR1.GPIO1_9 */ /* SODIMM 36 */ + AM62X_IOPAD(0x01a0, PIN_INPUT, 7) /* (B20) MCASP0_AXR0.GPIO1_10 */ /* SODIMM 34 */ + AM62X_IOPAD(0x01a4, PIN_INPUT, 7) /* (A19) MCASP0_ACLKX.GPIO1_11 */ /* SODIMM 30 */ + AM62X_IOPAD(0x01a8, PIN_INPUT, 7) /* (A20) MCASP0_AFSX.GPIO1_12 */ /* SODIMM 32 */ + AM62X_IOPAD(0x0088, PIN_INPUT, 7) /* (L17) GPMC0_OEn_REn.GPIO0_33 */ /* SODIMM 48 */ + AM62X_IOPAD(0x0098, PIN_INPUT, 7) /* (R18) GPMC0_WAIT0.GPIO0_37 */ /* SODIMM 44 */ + AM62X_IOPAD(0x008c, PIN_INPUT, 7) /* (L25) GPMC0_WEn.GPIO0_34 */ /* SODIMM 46 */ + AM62X_IOPAD(0x002c, PIN_INPUT, 7) /* (F23) OSPI0_CSn0.GPIO0_11 */ /* SODIMM 54 */ + >; + }; +}; + +&mcu_pmx0 { + pinctrl_zinnia_spi1_cs0_gpio: mcu-gpio0-11-default-pins { + pinctrl-single,pins = < + AM62X_MCU_IOPAD(0x002c, PIN_INPUT, 7) /* (C6) WKUP_UART0_CTSn.GPIO0_11 */ /* SODIMM 143 */ + >; + }; +}; + +/* Verdin ETH */ +&cpsw3g { + status = "okay"; +}; + +/* MDIO, shared by Verdin ETH_1 (On-module PHY) and Verdin ETH_2_RGMII */ +&cpsw3g_mdio { + status = "okay"; + + cpsw3g_phy1: ethernet-phy@2 { + reg = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_eth2_rgmii_int>; + interrupt-parent = <&main_gpio0>; + interrupts = <38 IRQ_TYPE_EDGE_FALLING>; + ti,rx-internal-delay = ; + }; +}; + +/* Verdin ETH_1 */ +&cpsw_port1 { + status = "okay"; +}; + +/* Verdin ETH_2_RGMII */ +&cpsw_port2 { + phy-handle = <&cpsw3g_phy1>; + phy-mode = "rgmii-id"; + + status = "okay"; +}; + +&main_gpio0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ctrl_sleep_moci>, + <&pinctrl_gpio_5>, + <&pinctrl_gpio_6>, + <&pinctrl_gpio_7>, + <&pinctrl_gpio_8>, + <&pinctrl_qspi1_io0_gpio>; + gpio-line-names = + "", /* 0 */ + "", + "", + "DI3_RB", /* SODIMM 56 */ + "", + "", + "", + "", + "", + "", + "", /* 10 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 20 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 30 */ + "", + "", + "", + "", + "", + "DI2_EN", /* SODIMM 218 */ + "", + "", + "", + "DI2_RB", /* SODIMM 216 */ /* 40 */ + "DO3_EN", /* SODIMM 220 */ + "DI3_EN", /* SODIMM 222 */ + "", + "", + "", + "", + "", + "", + "", + "", /* 50 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 60 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 70 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 80 */ + "", + "", + "", + "", + "", + ""; +}; + +&main_gpio1 { + gpio-line-names = + "", /* 0 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 10 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 20 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 30 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 40 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 50 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 60 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 70 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 80 */ + "", + "", + "", + "", + "", + "", + ""; +}; + +/* Verdin I2C_1 */ +&main_i2c1 { + status = "okay"; + + temperature-sensor@4f { + compatible = "ti,tmp1075"; + reg = <0x4f>; + }; + + eeprom@57 { + compatible = "st,24c02", "atmel,24c02"; + reg = <0x57>; + pagesize = <16>; + }; +}; + +/* Verdin CAN_1 */ +&main_mcan0 { + status = "okay"; +}; + +/* Verdin SPI_1 */ +&main_spi1 { + pinctrl-0 = <&pinctrl_spi1>, + <&pinctrl_zinnia_spi1_cs0_gpio>, + <&pinctrl_spi1_cs_gpio>; + cs-gpios = <&mcu_gpio0 11 GPIO_ACTIVE_LOW>, + <&main_gpio0 7 GPIO_ACTIVE_LOW>; + + status = "okay"; + + tpm@1 { + compatible = "infineon,slb9670", "tcg,tpm_tis-spi"; + reg = <1>; + spi-max-frequency = <18500000>; + }; +}; + +/* Verdin UART_3 */ +&main_uart0 { + status = "okay"; +}; + +/* Verdin UART_1 */ +&main_uart1 { + status = "okay"; +}; + +&mcu_gpio0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio_1>, + <&pinctrl_gpio_2>, + <&pinctrl_gpio_3>, + <&pinctrl_gpio_4>, + <&pinctrl_pcie_1_reset>; + gpio-line-names = + "", + "DO1_EN", /* SODIMM 206 */ + "DI1_EN", /* SODIMM 208 */ + "DI1_RB", /* SODIMM 210 */ + "DO2_EN", /* SODIMM 212 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ""; +}; + +/* Verdin SD_1 */ +&sdhci1 { + status = "okay"; +}; + +/* Verdin USB_1 */ +&usb0 { + status = "okay"; +}; + +/* Verdin USB_2 */ +&usb1 { + status = "okay"; +}; + +/* Verdin USB_1 */ +&usbss0 { + status = "okay"; +}; + +/* Verdin USB_2 */ +&usbss1 { + status = "okay"; +}; + +/* Verdin PCIE_1_RESET# */ +&verdin_pcie_1_reset_hog { + status = "okay"; +}; + +/* Verdin UART_2 */ +&wkup_uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_wkup_uart0>, <&pinctrl_wkup_uart0_rts>; + rs485-rts-active-low; + rs485-rx-during-tx; + linux,rs485-enabled-at-boot-time; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am62-verdin.dtsi b/arch/arm64/boot/dts/ti/k3-am62-verdin.dtsi index 2a7242a2fef8..e97b2b047d10 100644 --- a/arch/arm64/boot/dts/ti/k3-am62-verdin.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62-verdin.dtsi @@ -278,7 +278,7 @@ }; /* Verdin SPI_1 CS as GPIO */ - pinctrl_qspi1_io4_gpio: main-gpio0-7-default-pins { + pinctrl_spi1_cs_gpio: main-gpio0-7-default-pins { pinctrl-single,pins = < AM62X_IOPAD(0x001c, PIN_INPUT, 7) /* (J23) OSPI0_D4.GPIO0_7 */ /* SODIMM 202 */ >; @@ -572,16 +572,16 @@ /* On-module eMMC */ pinctrl_sdhci0: main-mmc0-default-pins { pinctrl-single,pins = < - AM62X_IOPAD(0x220, PIN_INPUT, 0) /* (Y3) MMC0_CMD */ - AM62X_IOPAD(0x218, PIN_INPUT, 0) /* (AB1) MMC0_CLK */ - AM62X_IOPAD(0x214, PIN_INPUT, 0) /* (AA2) MMC0_DAT0 */ - AM62X_IOPAD(0x210, PIN_INPUT, 0) /* (AA1) MMC0_DAT1 */ - AM62X_IOPAD(0x20c, PIN_INPUT, 0) /* (AA3) MMC0_DAT2 */ - AM62X_IOPAD(0x208, PIN_INPUT, 0) /* (Y4) MMC0_DAT3 */ - AM62X_IOPAD(0x204, PIN_INPUT, 0) /* (AB2) MMC0_DAT4 */ - AM62X_IOPAD(0x200, PIN_INPUT, 0) /* (AC1) MMC0_DAT5 */ - AM62X_IOPAD(0x1fc, PIN_INPUT, 0) /* (AD2) MMC0_DAT6 */ - AM62X_IOPAD(0x1f8, PIN_INPUT, 0) /* (AC2) MMC0_DAT7 */ + AM62X_IOPAD(0x220, PIN_INPUT, 0) /* (Y3) MMC0_CMD */ + AM62X_IOPAD(0x218, PIN_INPUT, 0) /* (AB1) MMC0_CLK */ + AM62X_IOPAD(0x214, PIN_INPUT, 0) /* (AA2) MMC0_DAT0 */ + AM62X_IOPAD(0x210, PIN_INPUT_PULLUP, 0) /* (AA1) MMC0_DAT1 */ + AM62X_IOPAD(0x20c, PIN_INPUT_PULLUP, 0) /* (AA3) MMC0_DAT2 */ + AM62X_IOPAD(0x208, PIN_INPUT_PULLUP, 0) /* (Y4) MMC0_DAT3 */ + AM62X_IOPAD(0x204, PIN_INPUT_PULLUP, 0) /* (AB2) MMC0_DAT4 */ + AM62X_IOPAD(0x200, PIN_INPUT_PULLUP, 0) /* (AC1) MMC0_DAT5 */ + AM62X_IOPAD(0x1fc, PIN_INPUT_PULLUP, 0) /* (AD2) MMC0_DAT6 */ + AM62X_IOPAD(0x1f8, PIN_INPUT_PULLUP, 0) /* (AC2) MMC0_DAT7 */ >; }; @@ -820,15 +820,27 @@ >; }; - /* Verdin UART_2 */ + /* Verdin UART_2 RX/TX */ pinctrl_wkup_uart0: wkup-uart0-default-pins { pinctrl-single,pins = < - AM62X_MCU_IOPAD(0x002c, PIN_INPUT_PULLUP, 0) /* (C6) WKUP_UART0_CTSn */ /* SODIMM 143 */ - AM62X_MCU_IOPAD(0x0030, PIN_OUTPUT, 0) /* (A4) WKUP_UART0_RTSn */ /* SODIMM 141 */ AM62X_MCU_IOPAD(0x0024, PIN_INPUT_PULLUP, 0) /* (B4) WKUP_UART0_RXD */ /* SODIMM 137 */ AM62X_MCU_IOPAD(0x0028, PIN_OUTPUT, 0) /* (C5) WKUP_UART0_TXD */ /* SODIMM 139 */ >; }; + + /* Verdin UART_2 CTS */ + pinctrl_wkup_uart0_cts: wkup-uart0-cts-default-pins { + pinctrl-single,pins = < + AM62X_MCU_IOPAD(0x002c, PIN_INPUT_PULLUP, 0) /* (C6) WKUP_UART0_CTSn */ /* SODIMM 143 */ + >; + }; + + /* Verdin UART_2 RTS */ + pinctrl_wkup_uart0_rts: wkup-uart0-rts-default-pins { + pinctrl-single,pins = < + AM62X_MCU_IOPAD(0x0030, PIN_OUTPUT, 0) /* (A4) WKUP_UART0_RTSn */ /* SODIMM 141 */ + >; + }; }; /* VERDIN I2S_1_MCLK */ @@ -1502,7 +1514,7 @@ /* Verdin UART_2 */ &wkup_uart0 { pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_wkup_uart0>; + pinctrl-0 = <&pinctrl_wkup_uart0>, <&pinctrl_wkup_uart0_cts>, <&pinctrl_wkup_uart0_rts>; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/ti/k3-am625-verdin-nonwifi-zinnia.dts b/arch/arm64/boot/dts/ti/k3-am625-verdin-nonwifi-zinnia.dts new file mode 100644 index 000000000000..eba8d7a5b1bf --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-am625-verdin-nonwifi-zinnia.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/ti-am62 + * https://www.toradex.com/products/carrier-board/zinnia-carrier-board + */ + +/dts-v1/; + +#include "k3-am625.dtsi" +#include "k3-am62-verdin.dtsi" +#include "k3-am62-verdin-nonwifi.dtsi" +#include "k3-am62-verdin-zinnia.dtsi" + +/ { + model = "Toradex Verdin AM62 on Zinnia Board"; + compatible = "toradex,verdin-am62-nonwifi-zinnia", + "toradex,verdin-am62-nonwifi", + "toradex,verdin-am62", + "ti,am625"; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am625-verdin-wifi-zinnia.dts b/arch/arm64/boot/dts/ti/k3-am625-verdin-wifi-zinnia.dts new file mode 100644 index 000000000000..6f3f8eb42ed6 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-am625-verdin-wifi-zinnia.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/ti-am62 + * https://www.toradex.com/products/carrier-board/zinnia-carrier-board + */ + +/dts-v1/; + +#include "k3-am625.dtsi" +#include "k3-am62-verdin.dtsi" +#include "k3-am62-verdin-wifi.dtsi" +#include "k3-am62-verdin-zinnia.dtsi" + +/ { + model = "Toradex Verdin AM62 WB on Zinnia Board"; + compatible = "toradex,verdin-am62-wifi-zinnia", + "toradex,verdin-am62-wifi", + "toradex,verdin-am62", + "ti,am625"; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am62a-ti-ipc-firmware.dtsi b/arch/arm64/boot/dts/ti/k3-am62a-ti-ipc-firmware.dtsi index 950f4f37d477..06d4e815b167 100644 --- a/arch/arm64/boot/dts/ti/k3-am62a-ti-ipc-firmware.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62a-ti-ipc-firmware.dtsi @@ -66,6 +66,7 @@ mboxes = <&mailbox0_cluster0>, <&mbox_r5_0>; memory-region = <&wkup_r5fss0_core0_dma_memory_region>, <&wkup_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -77,6 +78,7 @@ mboxes = <&mailbox0_cluster2>, <&mbox_mcu_r5_0>; memory-region = <&mcu_r5fss0_core0_dma_memory_region>, <&mcu_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; diff --git a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts index e99bdbc2e0cb..c1e9067b3bdd 100644 --- a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts @@ -282,6 +282,13 @@ bootph-all; }; + main_uart0_pins_wakeup: main-uart0-wakeup-pins { + pinctrl-single,pins = < + AM62AX_IOPAD(0x1c8, PIN_INPUT | PIN_WKUP_EN, 0) /* (E14) UART0_RXD */ + AM62AX_IOPAD(0x1cc, PIN_OUTPUT, 0) /* (D15) UART0_TXD */ + >; + }; + main_uart1_pins_default: main-uart1-default-pins { pinctrl-single,pins = < AM62AX_IOPAD(0x01ac, PIN_INPUT, 2) /* (B21) MCASP0_AFSR.UART1_RXD */ @@ -398,7 +405,7 @@ vddshv_sdio_pins_default: vddshv-sdio-default-pins { pinctrl-single,pins = < - AM62AX_IOPAD(0x07c, PIN_OUTPUT, 7) /* (M19) GPMC0_CLK.GPIO0_31 */ + AM62AX_IOPAD(0x07c, PIN_OUTPUT, 7) /* (N22) GPMC0_CLK.GPIO0_31 */ >; }; @@ -717,8 +724,12 @@ &main_uart0 { status = "okay"; - pinctrl-names = "default"; + pinctrl-names = "default", "wakeup"; pinctrl-0 = <&main_uart0_pins_default>; + pinctrl-1 = <&main_uart0_pins_wakeup>; + wakeup-source = <&system_deep_sleep>, + <&system_mcu_only>, + <&system_standby>; bootph-all; }; diff --git a/arch/arm64/boot/dts/ti/k3-am62d2-evm.dts b/arch/arm64/boot/dts/ti/k3-am62d2-evm.dts index a5d5dc0a7bec..f5ceb6a1b5de 100644 --- a/arch/arm64/boot/dts/ti/k3-am62d2-evm.dts +++ b/arch/arm64/boot/dts/ti/k3-am62d2-evm.dts @@ -199,6 +199,42 @@ &mcu_pmx0 { status = "okay"; + mcu_mcan0_tx_pins_default: mcu-mcan0-tx-default-pins { + pinctrl-single,pins = < + AM62DX_MCU_IOPAD(0x0034, PIN_OUTPUT, 0) /* (C7) MCU_MCAN0_TX */ + >; + }; + + mcu_mcan0_rx_pins_default: mcu-mcan0-rx-default-pins { + pinctrl-single,pins = < + AM62DX_MCU_IOPAD(0x0038, PIN_INPUT, 0) /* (E8) MCU_MCAN0_RX */ + >; + }; + + mcu_mcan0_rx_pins_wakeup: mcu-mcan0-rx-wakeup-pins { + pinctrl-single,pins = < + AM62DX_MCU_IOPAD(0x0038, PIN_INPUT | PIN_WKUP_EN, 0) /* (E8) MCU_MCAN0_RX */ + >; + }; + + mcu_mcan1_tx_pins_default: mcu-mcan1-tx-default-pins { + pinctrl-single,pins = < + AM62DX_MCU_IOPAD(0x003c, PIN_OUTPUT, 0) /* (D7) MCU_MCAN1_TX */ + >; + }; + + mcu_mcan1_rx_pins_default: mcu-mcan1-rx-default-pins { + pinctrl-single,pins = < + AM62DX_MCU_IOPAD(0x0040, PIN_INPUT, 0) /* (B9) MCU_MCAN1_RX */ + >; + }; + + mcu_mcan1_rx_pins_wakeup: mcu-mcan1-rx-wakeup-pins { + pinctrl-single,pins = < + AM62DX_MCU_IOPAD(0x040, PIN_INPUT | PIN_WKUP_EN, 0) /* (B9) MCU_MCAN1_RX */ + >; + }; + pmic_irq_pins_default: pmic-irq-default-pins { pinctrl-single,pins = < AM62DX_IOPAD(0x01f4, PIN_INPUT, 7) /* (F17) EXTINTn.GPIO1_31 */ @@ -228,6 +264,10 @@ &wkup_uart0 { pinctrl-names = "default"; pinctrl-0 = <&wkup_uart0_pins_default>; + wakeup-source = <&system_io_ddr>, + <&system_deep_sleep>, + <&system_mcu_only>, + <&system_standby>; bootph-all; status = "reserved"; }; @@ -241,6 +281,13 @@ bootph-all; }; + main_uart0_pins_wakeup: main-uart0-wakeup-pins { + pinctrl-single,pins = < + AM62DX_IOPAD(0x01c8, PIN_INPUT | PIN_WKUP_EN, 0) /* (E14) UART0_RXD */ + AM62DX_IOPAD(0x01cc, PIN_OUTPUT, 0) /* (D15) UART0_TXD */ + >; + }; + main_i2c0_pins_default: main-i2c0-default-pins { pinctrl-single,pins = < AM62DX_IOPAD(0x01e0, PIN_INPUT_PULLUP, 0) /* (D17) I2C0_SCL */ @@ -587,8 +634,12 @@ }; &main_uart0 { - pinctrl-names = "default"; + pinctrl-names = "default", "wakeup"; pinctrl-0 = <&main_uart0_pins_default>; + pinctrl-1 = <&main_uart0_pins_wakeup>; + wakeup-source = <&system_deep_sleep>, + <&system_mcu_only>, + <&system_standby>; bootph-all; status = "okay"; }; @@ -737,4 +788,33 @@ firmware-name = "am62d-c71_0-fw"; }; +&mcu_mcan0 { + pinctrl-names = "default", "wakeup"; + pinctrl-0 = <&mcu_mcan0_tx_pins_default>, <&mcu_mcan0_rx_pins_default>; + pinctrl-1 = <&mcu_mcan0_tx_pins_default>, <&mcu_mcan0_rx_pins_wakeup>; + wakeup-source = <&system_partial_io>, + <&system_io_ddr>, + <&system_deep_sleep>, + <&system_mcu_only>, + <&system_standby>; +}; + +&mcu_mcan1 { + pinctrl-names = "default", "wakeup"; + pinctrl-0 = <&mcu_mcan1_tx_pins_default>, <&mcu_mcan1_rx_pins_default>; + pinctrl-1 = <&mcu_mcan1_tx_pins_default>, <&mcu_mcan1_rx_pins_wakeup>; + wakeup-source = <&system_partial_io>, + <&system_io_ddr>, + <&system_deep_sleep>, + <&system_mcu_only>, + <&system_standby>; +}; + +&mcu_uart0 { + wakeup-source = <&system_io_ddr>, + <&system_deep_sleep>, + <&system_mcu_only>, + <&system_standby>; +}; + #include "k3-am62a-ti-ipc-firmware.dtsi" diff --git a/arch/arm64/boot/dts/ti/k3-am62l-main.dtsi b/arch/arm64/boot/dts/ti/k3-am62l-main.dtsi index 883beb76ba9c..80615ca1e01a 100644 --- a/arch/arm64/boot/dts/ti/k3-am62l-main.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62l-main.dtsi @@ -564,6 +564,13 @@ status = "disabled"; }; + rng: rng@3b100000 { + compatible = "inside-secure,safexcel-eip76"; + reg = <0x00 0x3b100000 0x00 0x7d>; + interrupts = ; + status = "reserved"; /* Reserved for OP-TEE */ + }; + oc_sram: sram@70800000 { compatible = "mmio-sram"; reg = <0x00 0x70800000 0x00 0x10000>; diff --git a/arch/arm64/boot/dts/ti/k3-am62l-wakeup.dtsi b/arch/arm64/boot/dts/ti/k3-am62l-wakeup.dtsi index 61bfcdcfc66e..b5b70e71a0b7 100644 --- a/arch/arm64/boot/dts/ti/k3-am62l-wakeup.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62l-wakeup.dtsi @@ -81,7 +81,7 @@ status = "disabled"; }; - target-module@2b300050 { + wkup_uart0_target: target-module@2b300050 { compatible = "ti,sysc-omap2", "ti,sysc"; reg = <0x00 0x2b300050 0x00 0x4>, <0x00 0x2b300054 0x00 0x4>, @@ -127,6 +127,11 @@ bootph-all; }; + opp_efuse_table: syscon@18 { + compatible = "ti,am62-opp-efuse-table", "syscon"; + reg = <0x18 0x4>; + }; + cpsw_mac_syscon: ethernet-mac-syscon@2000 { compatible = "ti,am62p-cpsw-mac-efuse", "syscon"; reg = <0x2000 0x8>; diff --git a/arch/arm64/boot/dts/ti/k3-am62l.dtsi b/arch/arm64/boot/dts/ti/k3-am62l.dtsi index 23acdbb301fe..28bb6ef2194f 100644 --- a/arch/arm64/boot/dts/ti/k3-am62l.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62l.dtsi @@ -79,6 +79,7 @@ <0x00 0x31000000 0x00 0x31000000 0x00 0x00050000>, /* USB0 DWC3 Core Window */ <0x00 0x31100000 0x00 0x31100000 0x00 0x00050000>, /* USB1 DWC3 Core Window */ <0x00 0x3b000000 0x00 0x3b000000 0x00 0x00000400>, /* GPMC0 */ + <0x00 0x3b100000 0x00 0x3b100000 0x00 0x0000007d>, /* RNG */ <0x00 0x45810000 0x00 0x45810000 0x00 0x03170000>, /* DMSS */ <0x00 0x50000000 0x00 0x50000000 0x00 0x08000000>, /* GPMC DATA */ <0x00 0x60000000 0x00 0x60000000 0x00 0x08000000>, /* FSS DAT1 */ @@ -92,7 +93,7 @@ <0x00 0x00b00000 0x00 0x00b00000 0x00 0x00001400>, /* VTM */ <0x00 0x04080000 0x00 0x04080000 0x00 0x00008000>, /* PDCFG */ <0x00 0x04201000 0x00 0x04201000 0x00 0x00000100>, /* GPIO */ - <0x00 0x2b100000 0x00 0x2b100000 0x00 0x00100100>, /* Wakeup Peripheral Window */ + <0x00 0x2b100000 0x00 0x2b100000 0x00 0x00200200>, /* Wakeup Peripheral Window */ <0x00 0x40800000 0x00 0x40800000 0x00 0x00014000>, /* DMA */ <0x00 0x43000000 0x00 0x43000000 0x00 0x00080000>; /* CTRL MMRs */ #address-cells = <2>; @@ -104,7 +105,7 @@ <0x00 0x00b00000 0x00 0x00b00000 0x00 0x00001400>, /* VTM */ <0x00 0x04080000 0x00 0x04080000 0x00 0x00008000>, /* PDCFG */ <0x00 0x04201000 0x00 0x04201000 0x00 0x00000100>, /* GPIO */ - <0x00 0x2b100000 0x00 0x2b100000 0x00 0x00100100>, /* Wakeup Peripheral Window */ + <0x00 0x2b100000 0x00 0x2b100000 0x00 0x00200200>, /* Wakeup Peripheral Window */ <0x00 0x40800000 0x00 0x40800000 0x00 0x00014000>, /* DMA */ <0x00 0x43000000 0x00 0x43000000 0x00 0x00080000>; /* CTRL MMRs */ #address-cells = <2>; diff --git a/arch/arm64/boot/dts/ti/k3-am62l3-evm.dts b/arch/arm64/boot/dts/ti/k3-am62l3-evm.dts index cae04cce3373..a1af4571a815 100644 --- a/arch/arm64/boot/dts/ti/k3-am62l3-evm.dts +++ b/arch/arm64/boot/dts/ti/k3-am62l3-evm.dts @@ -272,9 +272,9 @@ AM62LX_IOPAD(0x0230, PIN_INPUT, 0) /* (Y3) MMC1_CMD */ AM62LX_IOPAD(0x0228, PIN_OUTPUT, 0) /* (Y2) MMC1_CLK */ AM62LX_IOPAD(0x0224, PIN_INPUT, 0) /* (AA1) MMC1_DAT0 */ - AM62LX_IOPAD(0x0220, PIN_INPUT_PULLUP, 0) /* (Y4) MMC1_DAT1 */ - AM62LX_IOPAD(0x021c, PIN_INPUT_PULLUP, 0) /* (AA2) MMC1_DAT2 */ - AM62LX_IOPAD(0x0218, PIN_INPUT_PULLUP, 0) /* (AB2) MMC1_DAT3 */ + AM62LX_IOPAD(0x0220, PIN_INPUT, 0) /* (Y4) MMC1_DAT1 */ + AM62LX_IOPAD(0x021c, PIN_INPUT, 0) /* (AA2) MMC1_DAT2 */ + AM62LX_IOPAD(0x0218, PIN_INPUT, 0) /* (AB2) MMC1_DAT3 */ AM62LX_IOPAD(0x0234, PIN_INPUT, 0) /* (B6) MMC1_SDCD */ >; bootph-all; @@ -288,6 +288,13 @@ bootph-all; }; + uart0_pins_wakeup: uart0-wakeup-pins { + pinctrl-single,pins = < + AM62LX_IOPAD(0x01b4, PIN_INPUT | PIN_WKUP_EN, 0) /* (D13) UART0_RXD */ + AM62LX_IOPAD(0x01b8, PIN_OUTPUT, 0) /* (C13) UART0_TXD */ + >; + }; + usb1_default_pins: usb1-default-pins { pinctrl-single,pins = < AM62LX_IOPAD(0x0248, PIN_INPUT | PIN_DS_PULLUD_ENABLE | PIN_DS_PULL_UP, 0) /* (A5) USB1_DRVVBUS */ @@ -306,6 +313,12 @@ >; }; + wkup_uart0_pins_default: wkup-uart0-default-pins { + pinctrl-single,pins = < + AM62LX_IOPAD(0x0000, PIN_INPUT, 0) /* (Y22) WKUP_UART0_RXD */ + AM62LX_IOPAD(0x0004, PIN_OUTPUT, 0) /* (AA23) WKUP_UART0_TXD */ + >; + }; }; &sdhci0 { @@ -329,7 +342,9 @@ &uart0 { pinctrl-0 = <&uart0_pins_default>; - pinctrl-names = "default"; + pinctrl-1 = <&uart0_pins_wakeup>; + pinctrl-names = "default", "wakeup"; + wakeup-source; status = "okay"; bootph-all; }; @@ -359,3 +374,12 @@ pinctrl-names = "default"; pinctrl-0 = <&usb1_default_pins>; }; + +&wkup_uart0 { + pinctrl-0 = <&wkup_uart0_pins_default>; + pinctrl-names = "default"; +}; + +&wkup_uart0_target { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am62l3.dtsi b/arch/arm64/boot/dts/ti/k3-am62l3.dtsi index da220b851512..fc514e19f57d 100644 --- a/arch/arm64/boot/dts/ti/k3-am62l3.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62l3.dtsi @@ -39,6 +39,8 @@ d-cache-line-size = <64>; d-cache-sets = <128>; next-level-cache = <&l2_0>; + operating-points-v2 = <&a53_opp_table>; + clocks = <&scmi_clk 356>; }; cpu1: cpu@1 { @@ -53,6 +55,8 @@ d-cache-line-size = <64>; d-cache-sets = <128>; next-level-cache = <&l2_0>; + operating-points-v2 = <&a53_opp_table>; + clocks = <&scmi_clk 357>; }; }; @@ -64,4 +68,47 @@ cache-line-size = <64>; cache-sets = <256>; }; + + a53_opp_table: opp-table { + compatible = "operating-points-v2-ti-cpu"; + opp-shared; + syscon = <&opp_efuse_table>; + + opp-200000000 { + opp-hz = /bits/ 64 <200000000>; + opp-supported-hw = <0x01 0x0003>; + clock-latency-ns = <6000000>; + }; + + opp-400000000 { + opp-hz = /bits/ 64 <400000000>; + opp-supported-hw = <0x01 0x0003>; + clock-latency-ns = <6000000>; + }; + + opp-600000000 { + opp-hz = /bits/ 64 <600000000>; + opp-supported-hw = <0x01 0x0003>; + clock-latency-ns = <6000000>; + }; + + opp-800000000 { + opp-hz = /bits/ 64 <800000000>; + opp-supported-hw = <0x01 0x0003>; + clock-latency-ns = <6000000>; + }; + + opp-1000000000 { + opp-hz = /bits/ 64 <1000000000>; + opp-supported-hw = <0x01 0x0002>; + clock-latency-ns = <6000000>; + }; + + opp-1250000000 { + opp-hz = /bits/ 64 <1250000000>; + opp-supported-hw = <0x01 0x0002>; + clock-latency-ns = <6000000>; + opp-suspend; + }; + }; }; diff --git a/arch/arm64/boot/dts/ti/k3-am62p-j722s-common-main.dtsi b/arch/arm64/boot/dts/ti/k3-am62p-j722s-common-main.dtsi index 0e1af2a69ca2..f130c7cb998d 100644 --- a/arch/arm64/boot/dts/ti/k3-am62p-j722s-common-main.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62p-j722s-common-main.dtsi @@ -543,10 +543,9 @@ main_gpio_intr: interrupt-controller@a00000 { compatible = "ti,sci-intr"; reg = <0x00 0x00a00000 0x00 0x800>; - ti,intr-trigger-type = <1>; interrupt-controller; interrupt-parent = <&gic500>; - #interrupt-cells = <1>; + #interrupt-cells = <2>; ti,sci = <&dmsc>; ti,sci-dev-id = <3>; ti,interrupt-ranges = <0 32 16>; @@ -558,8 +557,9 @@ gpio-controller; #gpio-cells = <2>; interrupt-parent = <&main_gpio_intr>; - interrupts = <190>, <191>, <192>, - <193>, <194>, <195>; + interrupts = <190 IRQ_TYPE_EDGE_RISING>, <191 IRQ_TYPE_EDGE_RISING>, + <192 IRQ_TYPE_EDGE_RISING>, <193 IRQ_TYPE_EDGE_RISING>, + <194 IRQ_TYPE_EDGE_RISING>, <195 IRQ_TYPE_EDGE_RISING>; interrupt-controller; #interrupt-cells = <2>; ti,davinci-gpio-unbanked = <0>; @@ -574,8 +574,9 @@ gpio-controller; #gpio-cells = <2>; interrupt-parent = <&main_gpio_intr>; - interrupts = <180>, <181>, <182>, - <183>, <184>, <185>; + interrupts = <180 IRQ_TYPE_EDGE_RISING>, <181 IRQ_TYPE_EDGE_RISING>, + <182 IRQ_TYPE_EDGE_RISING>, <183 IRQ_TYPE_EDGE_RISING>, + <184 IRQ_TYPE_EDGE_RISING>, <185 IRQ_TYPE_EDGE_RISING>; interrupt-controller; #interrupt-cells = <2>; ti,davinci-gpio-unbanked = <0>; diff --git a/arch/arm64/boot/dts/ti/k3-am62p-ti-ipc-firmware.dtsi b/arch/arm64/boot/dts/ti/k3-am62p-ti-ipc-firmware.dtsi index d29a5dbe13ef..5d7f701420e2 100644 --- a/arch/arm64/boot/dts/ti/k3-am62p-ti-ipc-firmware.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62p-ti-ipc-firmware.dtsi @@ -45,6 +45,7 @@ mboxes = <&mailbox0_cluster0 &mbox_r5_0>; memory-region = <&wkup_r5fss0_core0_dma_memory_region>, <&wkup_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -56,5 +57,6 @@ mboxes = <&mailbox0_cluster1 &mbox_mcu_r5_0>; memory-region = <&mcu_r5fss0_core0_dma_memory_region>, <&mcu_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; diff --git a/arch/arm64/boot/dts/ti/k3-am62p-verdin-zinnia.dtsi b/arch/arm64/boot/dts/ti/k3-am62p-verdin-zinnia.dtsi new file mode 100644 index 000000000000..84ae99dcdda3 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-am62p-verdin-zinnia.dtsi @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * Common dtsi for Verdin AM62P SoM on Zinnia carrier board + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/ti-am62p + * https://www.toradex.com/products/carrier-board/zinnia-carrier-board + */ + +#include +#include + +/ { + aliases { + eeprom1 = &carrier_eeprom; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_zinnia_leds>; + + /* LED1 Red - SODIMM 48 - LED1_R */ + led-0 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <1>; + gpios = <&main_gpio0 33 GPIO_ACTIVE_HIGH>; + }; + + /* LED1 Blue - SODIMM 46 - LED1_B */ + led-1 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <1>; + gpios = <&main_gpio0 34 GPIO_ACTIVE_HIGH>; + }; + + /* LED3 Red - SODIMM 44 - LED3_R */ + led-2 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <3>; + gpios = <&main_gpio0 37 GPIO_ACTIVE_HIGH>; + }; + + /* LED3 Green - SODIMM 54 - LED3_G */ + led-3 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <3>; + gpios = <&main_gpio0 11 GPIO_ACTIVE_HIGH>; + }; + + /* LED3 Blue - SODIMM 36 - LED3_B */ + led-4 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <3>; + gpios = <&main_gpio1 9 GPIO_ACTIVE_HIGH>; + }; + + /* LED4 Red - SODIMM 34 - LED4_R */ + led-5 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <4>; + gpios = <&main_gpio1 10 GPIO_ACTIVE_HIGH>; + }; + + /* LED4 Green - SODIMM 32 - LED4_G */ + led-6 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <4>; + gpios = <&main_gpio1 12 GPIO_ACTIVE_HIGH>; + }; + + /* LED4 Blue - SODIMM 30 - LED4_B */ + led-7 { + color = ; + default-state = "off"; + function = LED_FUNCTION_STATUS; + function-enumerator = <4>; + gpios = <&main_gpio1 11 GPIO_ACTIVE_HIGH>; + }; + }; + + zinnia-1v8-voltage { + compatible = "voltage-divider"; + /* Verdin ADC_4 */ + io-channels = <&som_adc 4>; + full-ohms = <39000>; /* 12K + 27K */ + output-ohms = <27000>; + }; + + zinnia-3v3-voltage { + compatible = "voltage-divider"; + /* Verdin ADC_3 */ + io-channels = <&som_adc 5>; + full-ohms = <54000>; /* 27K + 27K */ + output-ohms = <27000>; + }; + + zinnia-5v-voltage { + compatible = "voltage-divider"; + /* Verdin ADC_2 */ + io-channels = <&som_adc 6>; + full-ohms = <39000>; /* 27K + 12K */ + output-ohms = <12000>; + }; + + /* Zinnia Power Supply Input Voltage */ + zinnia-input-voltage { + compatible = "voltage-divider"; + /* Verdin ADC_1 */ + io-channels = <&som_adc 7>; + full-ohms = <204700>; /* 200K + 4.7K */ + output-ohms = <4700>; + }; +}; + +&main_pmx0 { + pinctrl_zinnia_leds: zinnia-leds-default-pins { + pinctrl-single,pins = < + AM62PX_IOPAD(0x019c, PIN_INPUT, 7) /* (E24) MCASP0_AXR1.GPIO1_9 */ /* SODIMM 36 */ + AM62PX_IOPAD(0x01a0, PIN_INPUT, 7) /* (F23) MCASP0_AXR0.GPIO1_10 */ /* SODIMM 34 */ + AM62PX_IOPAD(0x01a4, PIN_INPUT, 7) /* (F24) MCASP0_ACLKX.GPIO1_11 */ /* SODIMM 30 */ + AM62PX_IOPAD(0x01a8, PIN_INPUT, 7) /* (F25) MCASP0_AFSX.GPIO1_12 */ /* SODIMM 32 */ + AM62PX_IOPAD(0x0088, PIN_INPUT, 7) /* (R24) GPMC0_OEn_REn.GPIO0_33 */ /* SODIMM 48 */ + AM62PX_IOPAD(0x0098, PIN_INPUT, 7) /* (AA24) GPMC0_WAIT0.GPIO0_37 */ /* SODIMM 44 */ + AM62PX_IOPAD(0x008c, PIN_INPUT, 7) /* (T25) GPMC0_WEn.GPIO0_34 */ /* SODIMM 46 */ + AM62PX_IOPAD(0x002c, PIN_INPUT, 7) /* (M25) OSPI0_CSn0.GPIO0_11 */ /* SODIMM 54 */ + >; + }; +}; + +&mcu_pmx0 { + pinctrl_zinnia_spi1_cs0_gpio: mcu-gpio0-11-default-pins { + pinctrl-single,pins = < + AM62PX_MCU_IOPAD(0x002c, PIN_INPUT, 7) /* (C7) WKUP_UART0_CTSn.GPIO0_11 */ /* SODIMM 143 */ + >; + }; +}; + +/* Verdin ETHs */ +&cpsw3g { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rgmii1>, <&pinctrl_rgmii2>; + + status = "okay"; +}; + +/* MDIO, shared by Verdin ETH_1 (On-module PHY) and Verdin ETH_2_RGMII */ +&cpsw3g_mdio { + status = "okay"; + + carrier_eth_phy: ethernet-phy@2 { + reg = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_eth2_rgmii_int>; + interrupt-parent = <&main_gpio0>; + interrupts = <42 IRQ_TYPE_EDGE_FALLING>; + ti,rx-internal-delay = ; + }; +}; + +/* Verdin ETH_1 (On-module PHY) */ +&cpsw_port1 { + status = "okay"; +}; + +/* Verdin ETH_2_RGMII */ +&cpsw_port2 { + phy-handle = <&carrier_eth_phy>; + phy-mode = "rgmii-id"; + + status = "okay"; +}; + +&main_gpio0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcie_1_reset>, + <&pinctrl_gpio_5>, + <&pinctrl_gpio_6>, + <&pinctrl_gpio_7>, + <&pinctrl_gpio_8>, + <&pinctrl_qspi1_io0_gpio>; + gpio-line-names = + "", /* 0 */ + "", + "", + "DI3_RB", /* SODIMM 56 */ + "", + "", + "", + "", + "", + "", + "", /* 10 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 20 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 30 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 40 */ + "", + "", + "", + "", + "", + "", + "", + "", + "DI2_RB", /* SODIMM 216 */ + "DI2_EN", /* SODIMM 218 */ /* 50 */ + "DO3_EN", /* SODIMM 220 */ + "DI3_EN", /* SODIMM 222 */ + "", + "", + "", + "", + "", + "", + "", + "", /* 60 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 70 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 80 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 90 */ + ""; +}; + +&main_gpio1 { + gpio-line-names = + "", /* 0 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 10 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 20 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 30 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 40 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 50 */ + ""; +}; + +/* Verdin I2C_1 */ +&main_i2c0 { + status = "okay"; + + temperature-sensor@4f { + compatible = "ti,tmp1075"; + reg = <0x4f>; + }; + + carrier_eeprom: eeprom@57 { + compatible = "st,24c02", "atmel,24c02"; + reg = <0x57>; + pagesize = <16>; + }; +}; + +/* Verdin CAN_1 */ +&main_mcan0 { + status = "okay"; +}; + +/* Verdin SPI_1 */ +&main_spi1 { + pinctrl-0 = <&pinctrl_main_spi1>, + <&pinctrl_zinnia_spi1_cs0_gpio>, + <&pinctrl_spi1_cs_gpio>; + cs-gpios = <&mcu_gpio0 11 GPIO_ACTIVE_LOW>, + <&main_gpio0 7 GPIO_ACTIVE_LOW>; + + status = "okay"; + + tpm@1 { + compatible = "infineon,slb9670", "tcg,tpm_tis-spi"; + reg = <1>; + spi-max-frequency = <18500000>; + }; +}; + +/* Verdin UART_3, used as the Linux console */ +&main_uart0 { + status = "okay"; +}; + +/* Verdin UART_1 */ +&main_uart1 { + status = "okay"; +}; + +&mcu_gpio0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio_1>, + <&pinctrl_gpio_2>, + <&pinctrl_gpio_4>, + <&pinctrl_gpio_3>; + gpio-line-names = + "", + "DO1_EN", /* SODIMM 206 */ + "DI1_EN", /* SODIMM 208 */ + "DI1_RB", /* SODIMM 210 */ + "DO2_EN", /* SODIMM 212 */ + "", + "", + "", + "", + "", + "", /* 10 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", /* 20 */ + "", + "", + ""; +}; + +/* Verdin SD_1 */ +&sdhci1 { + status = "okay"; +}; + +/* Verdin USB_1 */ +&usb0 { + status = "okay"; +}; + +/* Verdin USB_2 */ +&usb1 { + status = "okay"; +}; + +/* Verdin USB_1 */ +&usbss0 { + status = "okay"; +}; + +/* Verdin USB_2 */ +&usbss1 { + status = "okay"; +}; + +/* Verdin PCIE_1_RESET# */ +&verdin_pcie_1_reset_hog { + status = "okay"; +}; + +/* Verdin UART_2 */ +&wkup_uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_wkup_uart0>, + <&pinctrl_wkup_uart0_rts>; + rs485-rts-active-low; + rs485-rx-during-tx; + linux,rs485-enabled-at-boot-time; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am62p-verdin.dtsi b/arch/arm64/boot/dts/ti/k3-am62p-verdin.dtsi index 34954df692a3..7ee894d59113 100644 --- a/arch/arm64/boot/dts/ti/k3-am62p-verdin.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62p-verdin.dtsi @@ -247,6 +247,13 @@ >; }; + /* Verdin SPI_1_CS as GPIO */ + pinctrl_spi1_cs_gpio: main-gpio0-7-default-pins { + pinctrl-single,pins = < + AM62PX_IOPAD(0x001c, PIN_OUTPUT, 7) /* (N21) OSPI0_D4.GPIO0_7 */ /* SODIMM 202 */ + >; + }; + /* Verdin QSPI_1_CS# as GPIO (conflict with Verdin QSPI_1 interface) */ pinctrl_qspi1_cs_gpio: main-gpio0-11-default-pins { pinctrl-single,pins = < @@ -767,15 +774,27 @@ >; }; - /* Verdin UART_2 */ + /* Verdin UART_2 RX/TX */ pinctrl_wkup_uart0: wkup-uart0-default-pins { pinctrl-single,pins = < - AM62PX_MCU_IOPAD(0x002c, PIN_INPUT, 0) /* (C7) WKUP_UART0_CTSn */ /* SODIMM 143 */ - AM62PX_MCU_IOPAD(0x0030, PIN_OUTPUT, 0) /* (C6) WKUP_UART0_RTSn */ /* SODIMM 141 */ AM62PX_MCU_IOPAD(0x0024, PIN_INPUT, 0) /* (D8) WKUP_UART0_RXD */ /* SODIMM 137 */ AM62PX_MCU_IOPAD(0x0028, PIN_OUTPUT, 0) /* (D7) WKUP_UART0_TXD */ /* SODIMM 139 */ >; }; + + /* Verdin UART_2 CTS */ + pinctrl_wkup_uart0_cts: wkup-uart0-cts-default-pins { + pinctrl-single,pins = < + AM62PX_MCU_IOPAD(0x002c, PIN_INPUT, 0) /* (C7) WKUP_UART0_CTSn */ /* SODIMM 143 */ + >; + }; + + /* Verdin UART_2 RTS */ + pinctrl_wkup_uart0_rts: wkup-uart0-rts-default-pins { + pinctrl-single,pins = < + AM62PX_MCU_IOPAD(0x0030, PIN_OUTPUT, 0) /* (C6) WKUP_UART0_RTSn */ /* SODIMM 141 */ + >; + }; }; /* Verdin I2S_1_MCLK */ @@ -1410,7 +1429,9 @@ /* Verdin UART_2 */ &wkup_uart0 { pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_wkup_uart0>; + pinctrl-0 = <&pinctrl_wkup_uart0>, + <&pinctrl_wkup_uart0_cts>, + <&pinctrl_wkup_uart0_rts>; uart-has-rtscts; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts b/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts index 4f7f6f95b02e..b770ed82be9d 100644 --- a/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts @@ -271,9 +271,9 @@ AM62PX_IOPAD(0x023c, PIN_INPUT, 0) /* (H20) MMC1_CMD */ AM62PX_IOPAD(0x0234, PIN_OUTPUT, 0) /* (J24) MMC1_CLK */ AM62PX_IOPAD(0x0230, PIN_INPUT, 0) /* (H21) MMC1_DAT0 */ - AM62PX_IOPAD(0x022c, PIN_INPUT_PULLUP, 0) /* (H23) MMC1_DAT1 */ - AM62PX_IOPAD(0x0228, PIN_INPUT_PULLUP, 0) /* (H22) MMC1_DAT2 */ - AM62PX_IOPAD(0x0224, PIN_INPUT_PULLUP, 0) /* (H25) MMC1_DAT3 */ + AM62PX_IOPAD(0x022c, PIN_INPUT, 0) /* (H23) MMC1_DAT1 */ + AM62PX_IOPAD(0x0228, PIN_INPUT, 0) /* (H22) MMC1_DAT2 */ + AM62PX_IOPAD(0x0224, PIN_INPUT, 0) /* (H25) MMC1_DAT3 */ AM62PX_IOPAD(0x0240, PIN_INPUT, 0) /* (D23) MMC1_SDCD */ >; bootph-all; @@ -336,6 +336,13 @@ bootph-all; }; + main_uart0_pins_wakeup: main-uart0-wakeup-pins { + pinctrl-single,pins = < + AM62PX_IOPAD(0x1c8, PIN_INPUT | PIN_WKUP_EN, 0) /* (A22) UART0_RXD */ + AM62PX_IOPAD(0x1cc, PIN_OUTPUT, 0) /* (B22) UART0_TXD */ + >; + }; + main_uart1_pins_default: main-uart1-default-pins { pinctrl-single,pins = < AM62PX_IOPAD(0x0194, PIN_INPUT, 2) /* (D25) MCASP0_AXR3.UART1_CTSn */ @@ -692,8 +699,12 @@ }; &main_uart0 { - pinctrl-names = "default"; + pinctrl-names = "default", "wakeup"; pinctrl-0 = <&main_uart0_pins_default>; + pinctrl-1 = <&main_uart0_pins_wakeup>; + wakeup-source = <&system_deep_sleep>, + <&system_mcu_only>, + <&system_standby>; status = "okay"; bootph-all; }; diff --git a/arch/arm64/boot/dts/ti/k3-am62p5-verdin-nonwifi-zinnia.dts b/arch/arm64/boot/dts/ti/k3-am62p5-verdin-nonwifi-zinnia.dts new file mode 100644 index 000000000000..8abf4fe60e99 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-am62p5-verdin-nonwifi-zinnia.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/ti-am62p + * https://www.toradex.com/products/carrier-board/zinnia-carrier-board + */ + +/dts-v1/; + +#include "k3-am62p5.dtsi" +#include "k3-am62p-verdin.dtsi" +#include "k3-am62p-verdin-nonwifi.dtsi" +#include "k3-am62p-verdin-zinnia.dtsi" + +/ { + model = "Toradex Verdin AM62P on Zinnia Board"; + compatible = "toradex,verdin-am62p-nonwifi-zinnia", + "toradex,verdin-am62p-nonwifi", + "toradex,verdin-am62p", + "ti,am62p5"; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am62p5-verdin-wifi-zinnia.dts b/arch/arm64/boot/dts/ti/k3-am62p5-verdin-wifi-zinnia.dts new file mode 100644 index 000000000000..e1df7cffdf63 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-am62p5-verdin-wifi-zinnia.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) Toradex + * + * https://www.toradex.com/computer-on-modules/verdin-arm-family/ti-am62p + * https://www.toradex.com/products/carrier-board/zinnia-carrier-board + */ + +/dts-v1/; + +#include "k3-am62p5.dtsi" +#include "k3-am62p-verdin.dtsi" +#include "k3-am62p-verdin-wifi.dtsi" +#include "k3-am62p-verdin-zinnia.dtsi" + +/ { + model = "Toradex Verdin AM62P WB on Zinnia Board"; + compatible = "toradex,verdin-am62p-wifi-zinnia", + "toradex,verdin-am62p-wifi", + "toradex,verdin-am62p", + "ti,am62p5"; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi b/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi index 50ed859ae06c..ab9e58c2d225 100644 --- a/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi @@ -151,6 +151,13 @@ >; }; + main_uart0_pins_wakeup: main-uart0-wakeup-pins { + pinctrl-single,pins = < + AM62X_IOPAD(0x1c8, PIN_INPUT | PIN_WKUP_EN, 0) /* (D14/A13) UART0_RXD */ + AM62X_IOPAD(0x1cc, PIN_OUTPUT, 0) /* (E14/E11) UART0_TXD */ + >; + }; + main_uart1_pins_default: main-uart1-default-pins { bootph-pre-ram; pinctrl-single,pins = < @@ -322,8 +329,12 @@ &main_uart0 { bootph-all; status = "okay"; - pinctrl-names = "default"; + pinctrl-names = "default", "wakeup"; pinctrl-0 = <&main_uart0_pins_default>; + pinctrl-1 = <&main_uart0_pins_wakeup>; + wakeup-source = <&system_deep_sleep>, + <&system_mcu_only>, + <&system_standby>; }; &main_uart1 { diff --git a/arch/arm64/boot/dts/ti/k3-am64-ti-ipc-firmware.dtsi b/arch/arm64/boot/dts/ti/k3-am64-ti-ipc-firmware.dtsi index 6b10646ae64a..51fd9b68f58d 100644 --- a/arch/arm64/boot/dts/ti/k3-am64-ti-ipc-firmware.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am64-ti-ipc-firmware.dtsi @@ -126,6 +126,7 @@ mboxes = <&mailbox0_cluster2 &mbox_main_r5fss0_core0>; memory-region = <&main_r5fss0_core0_dma_memory_region>, <&main_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -133,6 +134,7 @@ mboxes = <&mailbox0_cluster2 &mbox_main_r5fss0_core1>; memory-region = <&main_r5fss0_core1_dma_memory_region>, <&main_r5fss0_core1_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -144,6 +146,7 @@ mboxes = <&mailbox0_cluster4 &mbox_main_r5fss1_core0>; memory-region = <&main_r5fss1_core0_dma_memory_region>, <&main_r5fss1_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -151,6 +154,7 @@ mboxes = <&mailbox0_cluster4 &mbox_main_r5fss1_core1>; memory-region = <&main_r5fss1_core1_dma_memory_region>, <&main_r5fss1_core1_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; diff --git a/arch/arm64/boot/dts/ti/k3-am642-evm-icssg0.dtso b/arch/arm64/boot/dts/ti/k3-am642-evm-icssg0.dtso new file mode 100644 index 000000000000..0c8e245e526c --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-am642-evm-icssg0.dtso @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/** + * DT overlay for enabling ICSSG0 dual EMAC on AM642 EVM + * + * AM642 EVM Product link: https://www.ti.com/tool/TMDS64EVM + * DP83TG720 daughter card link: https://www.ti.com/tool/DP83TG720-IND-SPE-EVM + * + * Copyright (C) 2020-2026 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include "k3-pinctrl.h" + +&{/} { + icssg0_eth: icssg0-eth { + compatible = "ti,am642-icssg-prueth"; + pinctrl-names = "default"; + pinctrl-0 = <&pru_icssg0_rgmii1_pins_default>, <&pru_icssg0_rgmii2_pins_default>; + sram = <&oc_sram>; + + dmas = <&main_pktdma 0xc100 15>, /* egress slice 0 */ + <&main_pktdma 0xc101 15>, /* egress slice 0 */ + <&main_pktdma 0xc102 15>, /* egress slice 0 */ + <&main_pktdma 0xc103 15>, /* egress slice 0 */ + <&main_pktdma 0xc104 15>, /* egress slice 1 */ + <&main_pktdma 0xc105 15>, /* egress slice 1 */ + <&main_pktdma 0xc106 15>, /* egress slice 1 */ + <&main_pktdma 0xc107 15>, /* egress slice 1 */ + <&main_pktdma 0x4100 15>, /* ingress slice 0 */ + <&main_pktdma 0x4101 15>; /* ingress slice 1 */ + dma-names = "tx0-0", "tx0-1", "tx0-2", "tx0-3", + "tx1-0", "tx1-1", "tx1-2", "tx1-3", + "rx0", "rx1"; + + interrupt-parent = <&icssg0_intc>; + interrupts = <24 0 2>, <25 1 3>; + interrupt-names = "tx_ts0", "tx_ts1"; + + ti,prus = <&pru0_0>, <&rtu0_0>, <&tx_pru0_0>, <&pru0_1>, <&rtu0_1>, <&tx_pru0_1>; + firmware-name = "ti-pruss/am64x-sr2-pru0-prueth-fw.elf", + "ti-pruss/am64x-sr2-rtu0-prueth-fw.elf", + "ti-pruss/am64x-sr2-txpru0-prueth-fw.elf", + "ti-pruss/am64x-sr2-pru1-prueth-fw.elf", + "ti-pruss/am64x-sr2-rtu1-prueth-fw.elf", + "ti-pruss/am64x-sr2-txpru1-prueth-fw.elf"; + + ti,pruss-gp-mux-sel = <2>, /* MII mode */ + <2>, + <2>, + <2>, /* MII mode */ + <2>, + <2>; + + ti,mii-g-rt = <&icssg0_mii_g_rt>; + ti,mii-rt = <&icssg0_mii_rt>; + ti,iep = <&icssg0_iep0>, <&icssg0_iep1>; + ti,pa-stats = <&icssg0_pa_stats>; + + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + + icssg0_emac0: port@0 { + reg = <0>; + phy-handle = <&icssg0_phy00>; + phy-mode = "rgmii-id"; + ti,syscon-rgmii-delay = <&main_conf 0x4100>; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + + icssg0_emac1: port@1 { + reg = <1>; + phy-handle = <&icssg0_phy01>; + phy-mode = "rgmii-id"; + ti,syscon-rgmii-delay = <&main_conf 0x4104>; + /* Filled in by bootloader */ + local-mac-address = [00 00 00 00 00 00]; + }; + }; + }; +}; + +&main_pmx0 { + pru_icssg0_mdio_pins_default: pru-icssg0-mdio-pins { + pinctrl-single,pins = < + /* (P3) PRG0_MDIO0_MDC */ + AM64X_IOPAD(0x0204, PIN_OUTPUT, 0) + /* (P2) PRG0_MDIO0_MDIO */ + AM64X_IOPAD(0x0200, PIN_INPUT, 0) + /* (P16) GPIO0_32 - GPMC0_ADVn_ALE - GPIO_ETH0/1_RESETn# */ + AM64X_IOPAD(0x0084, PIN_OUTPUT, 7) + >; + }; + + pru_icssg0_rgmii1_pins_default: pru-icssg0-rgmii1-pins { + pinctrl-single,pins = < + AM64X_IOPAD(0x0160, PIN_INPUT, 2) /* (Y1) PRG0_PRU0_GPO0.PRG0_RGMII1_RD0 */ + AM64X_IOPAD(0x0164, PIN_INPUT, 2) /* (R4) PRG0_PRU0_GPO1.PRG0_RGMII1_RD1 */ + AM64X_IOPAD(0x0168, PIN_INPUT, 2) /* (U2) PRG0_PRU0_GPO2.PRG0_RGMII1_RD2 */ + AM64X_IOPAD(0x016c, PIN_INPUT, 2) /* (V2) PRG0_PRU0_GPO3.PRG0_RGMII1_RD3 */ + AM64X_IOPAD(0x0178, PIN_INPUT, 2) /* (T3) PRG0_PRU0_GPO6.PRG0_RGMII1_RXC */ + AM64X_IOPAD(0x0170, PIN_INPUT, 2) /* (AA2) PRG0_PRU0_GPO4.PRG0_RGMII1_RX_CTL */ + AM64X_IOPAD(0x018c, PIN_OUTPUT, 2) /* (Y3) PRG0_PRU0_GPO11.PRG0_RGMII1_TD0 */ + AM64X_IOPAD(0x0190, PIN_OUTPUT, 2) /* (AA3) PRG0_PRU0_GPO12.PRG0_RGMII1_TD1 */ + AM64X_IOPAD(0x0194, PIN_OUTPUT, 2) /* (R6) PRG0_PRU0_GPO13.PRG0_RGMII1_TD2 */ + AM64X_IOPAD(0x0198, PIN_OUTPUT, 2) /* (V4) PRG0_PRU0_GPO14.PRG0_RGMII1_TD3 */ + AM64X_IOPAD(0x01a0, PIN_OUTPUT, 2) /* (U4) PRG0_PRU0_GPO16.PRG0_RGMII1_TXC */ + AM64X_IOPAD(0x019c, PIN_OUTPUT, 2) /* (T5) PRG0_PRU0_GPO15.PRG0_RGMII1_TX_CTL */ + >; + }; + + pru_icssg0_rgmii2_pins_default: pru-icssg0-rgmii2-pins { + pinctrl-single,pins = < + AM64X_IOPAD(0x01b0, PIN_INPUT, 2) /* (Y2) PRG0_PRU1_GPO0.PRG0_RGMII2_RD0 */ + AM64X_IOPAD(0x01b4, PIN_INPUT, 2) /* (W2) PRG0_PRU1_GPO1.PRG0_RGMII2_RD1 */ + AM64X_IOPAD(0x01b8, PIN_INPUT, 2) /* (V3) PRG0_PRU1_GPO2.PRG0_RGMII2_RD2 */ + AM64X_IOPAD(0x01bc, PIN_INPUT, 2) /* (T4) PRG0_PRU1_GPO3.PRG0_RGMII2_RD3 */ + AM64X_IOPAD(0x01c8, PIN_INPUT, 2) /* (R5) PRG0_PRU1_GPO6.PRG0_RGMII2_RXC */ + AM64X_IOPAD(0x01c0, PIN_INPUT, 2) /* (W3) PRG0_PRU1_GPO4.PRG0_RGMII2_RX_CTL */ + AM64X_IOPAD(0x01dc, PIN_OUTPUT, 2) /* (W4) PRG0_PRU1_GPO11.PRG0_RGMII2_TD0 */ + AM64X_IOPAD(0x01e0, PIN_OUTPUT, 2) /* (Y4) PRG0_PRU1_GPO12.PRG0_RGMII2_TD1 */ + AM64X_IOPAD(0x01e4, PIN_OUTPUT, 2) /* (T6) PRG0_PRU1_GPO13.PRG0_RGMII2_TD2 */ + AM64X_IOPAD(0x01e8, PIN_OUTPUT, 2) /* (U6) PRG0_PRU1_GPO14.PRG0_RGMII2_TD3 */ + AM64X_IOPAD(0x01f0, PIN_OUTPUT, 2) /* (AA4) PRG0_PRU1_GPO16.PRG0_RGMII2_TXC */ + AM64X_IOPAD(0x01ec, PIN_OUTPUT, 2) /* (U5) PRG0_PRU1_GPO15.PRG0_RGMII2_TX_CTL */ + >; + }; + + icssg0_iep0_pins_default: icssg0-iep0-pins { + pinctrl-single,pins = < + AM64X_IOPAD(0x01ac, PIN_OUTPUT, 2) /* (W1) PRG0_PRU0_GPO19.PRG0_IEP0_EDC_SYNC_OUT0 */ + >; + }; +}; + +&icssg0_mdio { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pru_icssg0_mdio_pins_default>; + #address-cells = <1>; + #size-cells = <0>; + + icssg0_phy00: ethernet-phy@0 { + reg = <0x0>; + }; + + icssg0_phy01: ethernet-phy@a { + reg = <0xa>; + }; +}; + +&icssg0_iep0 { + pinctrl-names = "default"; + pinctrl-0 = <&icssg0_iep0_pins_default>; +}; + +&main_gpio0 { + phy-line-hog { + gpio-hog; + gpios = <32 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "phy-hog-line"; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am642-evm.dts b/arch/arm64/boot/dts/ti/k3-am642-evm.dts index 88093ab74502..bc4347cf7114 100644 --- a/arch/arm64/boot/dts/ti/k3-am642-evm.dts +++ b/arch/arm64/boot/dts/ti/k3-am642-evm.dts @@ -601,6 +601,7 @@ reg = <0>; ti,rx-internal-delay = ; ti,fifo-depth = ; + ti,min-output-impedance; }; }; diff --git a/arch/arm64/boot/dts/ti/k3-am642-sk.dts b/arch/arm64/boot/dts/ti/k3-am642-sk.dts index 34bfa99bd4b8..d28a38c87f32 100644 --- a/arch/arm64/boot/dts/ti/k3-am642-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-am642-sk.dts @@ -519,12 +519,14 @@ reg = <0>; ti,rx-internal-delay = ; ti,fifo-depth = ; + ti,min-output-impedance; }; cpsw3g_phy1: ethernet-phy@1 { reg = <1>; ti,rx-internal-delay = ; ti,fifo-depth = ; + ti,min-output-impedance; }; }; diff --git a/arch/arm64/boot/dts/ti/k3-am65-ti-ipc-firmware.dtsi b/arch/arm64/boot/dts/ti/k3-am65-ti-ipc-firmware.dtsi index 61ab0357fc0d..e5f37cfd18bc 100644 --- a/arch/arm64/boot/dts/ti/k3-am65-ti-ipc-firmware.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am65-ti-ipc-firmware.dtsi @@ -52,6 +52,7 @@ &mcu_r5fss0_core0 { memory-region = <&mcu_r5fss0_core0_dma_memory_region>, <&mcu_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core0>; status = "okay"; }; @@ -59,6 +60,7 @@ &mcu_r5fss0_core1 { memory-region = <&mcu_r5fss0_core1_dma_memory_region>, <&mcu_r5fss0_core1_memory_region>; + memory-region-names = "dma", "firmware"; mboxes = <&mailbox0_cluster1 &mbox_mcu_r5fss0_core1>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/ti/k3-am654-base-board.dts b/arch/arm64/boot/dts/ti/k3-am654-base-board.dts index e0262c2743eb..ca1300e13093 100644 --- a/arch/arm64/boot/dts/ti/k3-am654-base-board.dts +++ b/arch/arm64/boot/dts/ti/k3-am654-base-board.dts @@ -583,6 +583,7 @@ reg = <0>; ti,rx-internal-delay = ; ti,fifo-depth = ; + ti,min-output-impedance; }; }; diff --git a/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-ads2.dtso b/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-ads2.dtso deleted file mode 100644 index ae5e2b52594b..000000000000 --- a/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-ads2.dtso +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only OR MIT -/* - * Kontron SMARC-sa67 board on the Kontron Eval Carrier 2.2. - * - * Copyright (c) 2025 Kontron Europe GmbH - */ - -/dts-v1/; -/plugin/; - -#include -#include "k3-pinctrl.h" - -&{/} { - pwm-fan { - compatible = "pwm-fan"; - pinctrl-names = "default"; - pinctrl-0 = <&pwm_fan_pins_default>; - interrupts-extended = <&main_gpio1 7 IRQ_TYPE_EDGE_FALLING>; - #cooling-cells = <2>; - pwms = <&epwm2 1 4000000 0>; - cooling-levels = <1 128 192 255>; - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,widgets = - "Headphone", "Headphone Jack", - "Line", "Line Out Jack", - "Microphone", "Microphone Jack", - "Line", "Line In Jack"; - simple-audio-card,routing = - "Line Out Jack", "LINEOUTR", - "Line Out Jack", "LINEOUTL", - "Headphone Jack", "HPOUTR", - "Headphone Jack", "HPOUTL", - "IN1L", "Line In Jack", - "IN1R", "Line In Jack", - "Microphone Jack", "MICBIAS", - "IN2L", "Microphone Jack", - "IN2R", "Microphone Jack"; - simple-audio-card,mclk-fs = <256>; - simple-audio-card,format = "i2s"; - simple-audio-card,bitclock-master = <&dailink0_master>; - simple-audio-card,frame-master = <&dailink0_master>; - - simple-audio-card,cpu { - sound-dai = <&mcasp0>; - }; - - dailink0_master: simple-audio-card,codec { - sound-dai = <&wm8904>; - clocks = <&audio_refclk0>; - }; - }; - - cvcc_1p8v_i2s: regulator-carrier-0 { - compatible = "regulator-fixed"; - regulator-name = "V_1V8_S0_I2S"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - cvcc_1p8v_s0: regulator-carrier-1 { - compatible = "regulator-fixed"; - regulator-name = "V_1V8_S0"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - }; - - cvcc_3p3v_s0: regulator-carrier-2 { - compatible = "regulator-fixed"; - regulator-name = "V_3V3_S0"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - }; -}; - -&audio_refclk0 { - status = "okay"; -}; - -&epwm2 { - status = "okay"; -}; - -&main_pmx0 { - pwm_fan_pins_default: pwm-fan-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x1ec, PIN_OUTPUT, 8) /* (A22) I2C1_SDA.EHRPWM2_B */ - J722S_IOPAD(0x194, PIN_INPUT, 0) /* (A25) MCASP0_AXR3.GPIO1_7 */ - >; - }; -}; - -&mcasp0 { - #sound-dai-cells = <0>; - status = "okay"; -}; - -&mcu_i2c0 { - status = "okay"; - #address-cells = <1>; - #size-cells = <0>; - - wm8904: audio-codec@1a { - #sound-dai-cells = <0>; - compatible = "wlf,wm8904"; - reg = <0x1a>; - clocks = <&audio_refclk0>; - clock-names = "mclk"; - AVDD-supply = <&cvcc_1p8v_i2s>; - CPVDD-supply = <&cvcc_1p8v_i2s>; - DBVDD-supply = <&cvcc_1p8v_i2s>; - DCVDD-supply = <&cvcc_1p8v_i2s>; - MICVDD-supply = <&cvcc_1p8v_i2s>; - }; -}; - -&mcu_spi0 { - status = "okay"; - #address-cells = <1>; - #size-cells = <0>; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0>; - spi-max-frequency = <104000000>; - m25p,fast-read; - vcc-supply = <&cvcc_1p8v_s0>; - }; -}; - -&wkup_i2c0 { - status = "okay"; - #address-cells = <1>; - #size-cells = <0>; - - /* SMARC Carrier EEPROM */ - eeprom@57 { - compatible = "atmel,24c32"; - reg = <0x57>; - pagesize = <32>; - vcc-supply = <&cvcc_3p3v_s0>; - }; -}; diff --git a/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-base.dts b/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-base.dts deleted file mode 100644 index 95234c8460ed..000000000000 --- a/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-base.dts +++ /dev/null @@ -1,1091 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only OR MIT -/* - * Kontron SMARC-sAM67 module - * - * Copyright (c) 2025 Kontron Europe GmbH - */ - -/dts-v1/; - -#include -#include -#include -#include "k3-j722s.dtsi" -#include "k3-serdes.h" - -/ { - compatible = "kontron,sa67", "ti,j722s"; - model = "Kontron SMARC-sAM67"; - - aliases { - serial0 = &mcu_uart0; - serial1 = &main_uart0; - serial2 = &main_uart5; - serial3 = &wkup_uart0; - mmc0 = &sdhci0; - mmc1 = &sdhci1; - rtc0 = &wkup_rtc0; - }; - - lcd0_backlight: backlight-1 { - compatible = "pwm-backlight"; - pinctrl-names = "default"; - pinctrl-0 = <&lcd0_backlight_pins_default>; - pwms = <&epwm1 0 50000 0>; - brightness-levels = <0 32 64 96 128 160 192 224 255>; - default-brightness-level = <8>; - enable-gpios = <&main_gpio0 29 GPIO_ACTIVE_HIGH>; - status = "disabled"; - }; - - lcd1_backlight: backlight-2 { - compatible = "pwm-backlight"; - pinctrl-names = "default"; - pinctrl-0 = <&lcd1_backlight_pins_default>; - pwms = <&epwm1 1 50000 0>; - brightness-levels = <0 32 64 96 128 160 192 224 255>; - default-brightness-level = <8>; - enable-gpios = <&main_gpio1 18 GPIO_ACTIVE_HIGH>; - status = "disabled"; - }; - - chosen { - stdout-path = "serial1:115200n8"; - }; - - connector-1 { - compatible = "gpio-usb-b-connector", "usb-b-connector"; - pinctrl-names = "default"; - pinctrl-0 = <&usb0_connector_pins_default>; - type = "micro"; - id-gpios = <&main_gpio0 34 GPIO_ACTIVE_HIGH>; - vbus-supply = <&vcc_usb0_vbus>; - - port { - usb0_connector: endpoint { - remote-endpoint = <&usb0_hc>; - }; - }; - - }; - - memory@80000000 { - /* Filled in by bootloader */ - reg = <0x00000000 0x00000000 0x00000000 0x00000000>, - <0x00000000 0x00000000 0x00000000 0x00000000>; - device_type = "memory"; - bootph-pre-ram; - }; - - reserved_memory: reserved-memory { - #address-cells = <2>; - #size-cells = <2>; - ranges; - - linux,cma { - compatible = "shared-dma-pool"; - reusable; - size = <0x00 0x10000000>; - linux,cma-default; - }; - - secure_tfa_ddr: tfa@9e780000 { - reg = <0x00 0x9e780000 0x00 0x80000>; - no-map; - }; - - secure_ddr: optee@9e800000 { - reg = <0x00 0x9e800000 0x00 0x01800000>; - no-map; - }; - - wkup_r5fss0_core0_memory_region: r5f-memory@a0100000 { - compatible = "shared-dma-pool"; - reg = <0x00 0xa0100000 0x00 0xf00000>; - no-map; - }; - }; - - vin_5p0: regulator-1 { - compatible = "regulator-fixed"; - regulator-name = "V_3V0_5V25_IN"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - regulator-always-on; - regulator-boot-on; - bootph-all; - }; - - vcc_3p3_s5: regulator-2 { - compatible = "regulator-fixed"; - regulator-name = "V_3V3_S5"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - vin-supply = <&vin_5p0>; - regulator-always-on; - regulator-boot-on; - bootph-all; - }; - - vcc_1p8_s5: regulator-3 { - compatible = "regulator-fixed"; - regulator-name = "V_1V8_S5"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - vin-supply = <&vin_5p0>; - regulator-always-on; - regulator-boot-on; - bootph-all; - }; - - vcc_3p3_s0: regulator-4 { - compatible = "regulator-fixed"; - regulator-name = "V_3V3_S0"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - vin-supply = <&vcc_3p3_s5>; - regulator-always-on; - regulator-boot-on; - enable-active-high; - gpios = <&tps652g1 1 GPIO_ACTIVE_HIGH>; - bootph-all; - }; - - vcc_3p3_sd_s0: regulator-5 { - compatible = "regulator-fixed"; - regulator-name = "SDIO_PWR_EN"; - pinctrl-names = "default"; - pinctrl-0 = <&vcc_3p3_sd_s0_pins_default>; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - enable-active-high; - gpios = <&main_gpio0 7 GPIO_ACTIVE_HIGH>; - bootph-all; - }; - - vcc_3p3_sd_vio_s0: regulator-6 { - compatible = "regulator-gpio"; - regulator-name = "V_3V3_1V8_SD_S0"; - pinctrl-names = "default"; - pinctrl-0 = <&vcc_3p3_sd_vio_s0_pins_default>; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - vin-supply = <&vcc_3p3_s0>; - regulator-boot-on; - enable-active-high; - enable-gpios = <&main_gpio0 7 GPIO_ACTIVE_HIGH>; - gpios = <&main_gpio0 8 GPIO_ACTIVE_HIGH>; - states = <3300000 0x0>, - <1800000 0x1>; - bootph-all; - }; - - vcc_3p3_cam_s0: regulator-7 { - compatible = "regulator-fixed"; - regulator-name = "V_3V3_CAM_S0"; - pinctrl-names = "default"; - pinctrl-0 = <&vcc_3p3_cam_s0_pins_default>; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - vin-supply = <&vcc_3p3_s5>; - enable-active-high; - interrupts-extended = <&main_gpio1 30 IRQ_TYPE_EDGE_FALLING>; - bootph-all; - }; - - vcc_1p1_s0: regulator-8 { - compatible = "regulator-fixed"; - regulator-name = "V_1V1_S0"; - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1100000>; - vin-supply = <&vcc_1p1_s3>; - regulator-always-on; - regulator-boot-on; - enable-active-high; - /* shared with V_0V75_0V85_CORE_S0 */ - gpios = <&tps652g1 4 GPIO_ACTIVE_HIGH>; - bootph-all; - }; - - vcc_0p85_vcore_s0: regulator-9 { - compatible = "regulator-fixed"; - regulator-name = "V_0V75_0V85_CORE_S0"; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <850000>; - vin-supply = <&vin_5p0>; - regulator-always-on; - regulator-boot-on; - enable-active-high; - gpios = <&tps652g1 4 GPIO_ACTIVE_HIGH>; - bootph-all; - }; - - vcc_lcd0_panel: regulator-10 { - compatible = "regulator-fixed"; - regulator-name = "LCD0_VDD_EN"; - pinctrl-names = "default"; - pinctrl-0 = <&vcc_lcd0_panel_pins_default>; - enable-active-high; - gpios = <&main_gpio0 30 GPIO_ACTIVE_HIGH>; - }; - - vcc_lcd1_panel: regulator-11 { - compatible = "regulator-fixed"; - regulator-name = "LCD1_VDD_EN"; - pinctrl-names = "default"; - pinctrl-0 = <&vcc_lcd1_panel_pins_default>; - enable-active-high; - gpios = <&main_gpio1 19 GPIO_ACTIVE_HIGH>; - }; - - vcc_usb0_vbus: regulator-12 { - compatible = "regulator-fixed"; - regulator-name = "USB0_EN_OC#"; - pinctrl-names = "default"; - pinctrl-0 = <&vcc_usb0_vbus_pins_default>; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - enable-active-high; - gpios = <&main_gpio1 50 GPIO_ACTIVE_HIGH>; - }; -}; - -&audio_refclk0 { - pinctrl-names = "default"; - pinctrl-0 = <&audio_refclk0_pins_default>; - status = "disabled"; -}; - -&audio_refclk1 { - pinctrl-names = "default"; - pinctrl-0 = <&audio_refclk1_pins_default>; - status = "disabled"; -}; - -&cpsw3g { - pinctrl-names = "default"; - pinctrl-0 = <&cpsw3g_pins_default>, <&rgmii1_pins_default>, - <&rgmii2_pins_default>; - status = "okay"; -}; - -&cpsw3g_mdio { - pinctrl-names = "default"; - pinctrl-0 = <&cpsw3g_mdio_pins_default>; - status = "okay"; - - phy0: ethernet-phy@0 { - reg = <0>; - }; - - phy1: ethernet-phy@1 { - reg = <1>; - }; -}; - -&cpsw_port1 { - phy-connection-type = "rgmii-id"; - phy-handle = <&phy0>; - nvmem-cells = <&base_mac_address 0>; - nvmem-cell-names = "mac-address"; - status = "okay"; -}; - -&main_gpio0 { - gpio-line-names = - "", "", "", "", "", "", "", "SOC_SDIO_PWR_EN", "VSD_SEL", - "RESET_OUT#", "I2C_MUX_RST#", "SPI_FLASH_CS#", "QPSI_CS0#", - "QSPI_CS1#", "BOOT_SEL1", "BRDCFG0", "BRDCFG1", "BRDCFG2", - "BRDCFG3", "BRDCFG4", "", "BRDREV0", "BRDREV1", "", "", "", "", - "", "", "LCD0_BKLT_EN", "LCD0_VDD_EN", "GBE_INT#", "DSI0_TE", - "CHARGING#", "USB0_OTG_ID", "PMIC_INT#", "RTC_INT#", - "EDP_BRIDGE_EN", "EDP_BRIDGE_IRQ#", "", "CHARGER_PRSNT#", "", - "", "", "", "BOOT_SEL2#", "CAM2_RST#", "CAM2_PWR#", "", - "CAM3_RST#", "CAM3_PWR#", "GPIO0", "GPIO1", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", "", "GPIO10", "GPIO11", - "SLEEP#", "LID#"; - - bootph-all; - status = "okay"; -}; - -&main_gpio1 { - gpio-line-names = - "", "", "", "", "", "", "", "GPIO6", "GPIO7", "", "", "", "", - "GPIO8", "GPIO9", "PCIE_A_RST#", "", "BATLOW#", "LCD1_BKLT_EN", - "LCD1_VDD_EN", "", "", "", "", "GPIO2", "GPIO3", "", "", - "GPIO4", "GPIO5", "CAM_S0_FAULT#", "BOOT_SEL0#", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", "", "", "SDIO_CD#", "", - "USB0_DRVVBUS", "USB1_DRVVBUS"; - - bootph-all; - status = "okay"; -}; - -/* I2C_LOCAL */ -&main_i2c0 { - pinctrl-names = "default"; - pinctrl-0 = <&main_i2c0_pins_default>; - clock-frequency = <100000>; - bootph-all; - status = "okay"; - - tps652g1: pmic@44 { - compatible = "ti,tps652g1"; - reg = <0x44>; - ti,primary-pmic; - system-power-controller; - - gpio-controller; - #gpio-cells = <2>; - gpio-line-names = - "LPM_EN#", "EN_3V3_S0", "POWER_BTN#", "CARRIER_STBY#", - "EN_0V75_0V85_VCORE_S0", "PMIC_WAKEUP"; - - pinctrl-names = "default"; - pinctrl-0 = <&pmic_irq_pins_default>; - interrupts-extended = <&main_gpio0 35 IRQ_TYPE_EDGE_FALLING>; - - buck1-supply = <&vin_5p0>; - buck2-supply = <&vin_5p0>; - buck3-supply = <&vin_5p0>; - buck4-supply = <&vin_5p0>; - ldo1-supply = <&vin_5p0>; - ldo2-supply = <&vin_5p0>; - ldo3-supply = <&vin_5p0>; - - bootph-all; - - regulators { - vcc_0p85_s0: buck1 { - regulator-name = "V_0V85_S0"; - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <850000>; - regulator-boot-on; - regulator-always-on; - }; - - vcc_1p1_s3: buck2 { - regulator-name = "V_1V1_S3"; - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1100000>; - regulator-boot-on; - regulator-always-on; - }; - - vcc_1p8_s0: buck3 { - regulator-name = "V_1V8_S0"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-boot-on; - regulator-always-on; - }; - - vcc_1p2_s0: buck4 { - regulator-name = "V_1V2_S0"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-boot-on; - regulator-always-on; - }; - - vcc_1p8_vda_pll_s0: ldo1 { - regulator-name = "V_1V8_VDA_PLL_S0"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-boot-on; - regulator-always-on; - }; - - vcc_1p8_s3: ldo2 { - regulator-name = "V_1V8_S3"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-boot-on; - regulator-always-on; - }; - - vcc_1p8_ret_s5: ldo3 { - regulator-name = "V_1V8_RET_S5"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-boot-on; - regulator-always-on; - }; - }; - }; - - system-controller@4a { - compatible = "kontron,sa67mcu", "kontron,sl28cpld"; - reg = <0x4a>; - #address-cells = <1>; - #size-cells = <0>; - - watchdog@4 { - compatible = "kontron,sa67mcu-wdt", "kontron,sl28cpld-wdt"; - reg = <0x4>; - kontron,assert-wdt-timeout-pin; - }; - - hwmon@8 { - compatible = "kontron,sa67mcu-hwmon"; - reg = <0x8>; - }; - }; -}; - -/* I2C_CAM */ -&main_i2c2 { - pinctrl-names = "default"; - pinctrl-0 = <&main_i2c2_pins_default>; - clock-frequency = <100000>; - status = "okay"; - - i2c-mux@70 { - compatible = "nxp,pca9546"; - reg = <0x70>; - #address-cells = <1>; - #size-cells = <0>; - - pinctrl-names = "default"; - pinctrl-0 = <&i2c_mux_pins_default>; - - vdd-supply = <&vcc_1p8_s0>; - reset-gpios = <&main_gpio0 10 GPIO_ACTIVE_LOW>; - - i2c_cam0: i2c@0 { - #address-cells = <1>; - #size-cells = <0>; - reg = <0>; - }; - - i2c_cam1: i2c@1 { - #address-cells = <1>; - #size-cells = <0>; - reg = <1>; - }; - - i2c_cam2: i2c@2 { - #address-cells = <1>; - #size-cells = <0>; - reg = <2>; - }; - - i2c_cam3: i2c@3 { - #address-cells = <1>; - #size-cells = <0>; - reg = <3>; - }; - }; -}; - -/* I2C_LCD */ -&main_i2c3 { - pinctrl-names = "default"; - pinctrl-0 = <&main_i2c3_pins_default>; - clock-frequency = <100000>; - status = "okay"; -}; - -&main_pmx0 { - audio_refclk0_pins_default: audio-refclk0-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x0c4, PIN_OUTPUT, 5) /* (W23) VOUT0_DATA3.AUDIO_EXT_REFCLK0 */ - >; - }; - - audio_refclk1_pins_default: audio-refclk1-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x0a0, PIN_OUTPUT, 1) /* (N24) GPMC0_WPn.AUDIO_EXT_REFCLK1 */ - >; - }; - - cpsw3g_mdio_pins_default: cpsw3g-mdio-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x160, PIN_OUTPUT, 0) /* (AC24) MDIO0_MDC */ - J722S_IOPAD(0x15c, PIN_INPUT, 0) /* (AD25) MDIO0_MDIO */ - >; - }; - - cpsw3g_pins_default: cpsw3g-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x1b8, PIN_OUTPUT, 1) /* (C20) SPI0_CS1.CP_GEMAC_CPTS0_TS_COMP */ - >; - }; - - edp_bridge_pins_default: edp-bridge-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x098, PIN_OUTPUT, 7) /* (V21) GPMC0_WAIT0.GPIO0_37 */ - J722S_IOPAD(0x09c, PIN_INPUT, 7) /* (W26) GPMC0_WAIT1.GPIO0_38 */ - >; - }; - - i2c_mux_pins_default: i2c-mux-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x028, PIN_OUTPUT, 7) /* (M27) OSPI0_D7.GPIO0_10 */ - >; - }; - - lcd0_backlight_pins_default: lcd0-backlight-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x074, PIN_OUTPUT, 7) /* (V22) GPMC0_AD14.GPIO0_29 */ - J722S_IOPAD(0x110, PIN_OUTPUT, 4) /* (G27) MMC2_DAT1.EHRPWM1_A */ - >; - }; - - lcd1_backlight_pins_default: lcd1-backlight-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x1c0, PIN_OUTPUT, 7) /* (E19) SPI0_D0.GPIO1_18 */ - J722S_IOPAD(0x114, PIN_OUTPUT, 4) /* (G26) MMC2_DAT0.EHRPWM1_B */ - >; - }; - - main_i2c0_pins_default: main-i2c0-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x1e0, PIN_INPUT, 0) /* (D23) I2C0_SCL */ - J722S_IOPAD(0x1e4, PIN_INPUT, 0) /* (B22) I2C0_SDA */ - >; - bootph-all; - }; - - main_i2c2_pins_default: main-i2c2-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x0b0, PIN_INPUT, 1) /* (P22) GPMC0_CSn2.I2C2_SCL */ - J722S_IOPAD(0x0b4, PIN_INPUT, 1) /* (P23) GPMC0_CSn3.I2C2_SDA */ - >; - }; - - main_i2c3_pins_default: main-i2c3-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x1d0, PIN_INPUT, 2) /* (E22) UART0_CTSn.I2C3_SCL */ - J722S_IOPAD(0x1d4, PIN_INPUT, 2) /* (B21) UART0_RTSn.I2C3_SDA */ - >; - }; - - main_i2c4_pins_default: main-i2c4-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x0a8, PIN_INPUT, 1) /* (R27) GPMC0_CSn0.I2C4_SCL */ - J722S_IOPAD(0x0ac, PIN_INPUT, 1) /* (P21) GPMC0_CSn1.I2C4_SDA */ - >; - }; - - main_uart0_pins_default: main-uart0-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x1c8, PIN_INPUT, 0) /* (F19) UART0_RXD */ - J722S_IOPAD(0x1cc, PIN_OUTPUT, 0) /* (F20) UART0_TXD */ - >; - bootph-all; - }; - - main_uart5_pins_default: main-uart5-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x108, PIN_INPUT, 3) /* (J27) MMC2_DAT3.UART5_RXD */ - J722S_IOPAD(0x10c, PIN_OUTPUT, 3) /* (H27) MMC2_DAT2.UART5_TXD */ - J722S_IOPAD(0x008, PIN_INPUT, 5) /* (L22) OSPI0_DQS.UART5_CTSn */ - J722S_IOPAD(0x004, PIN_OUTPUT, 5) /* (L23) OSPI0_LBCLKO.UART5_RTSn */ - >; - }; - - mcasp0_pins_default: mcasp0-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x1a4, PIN_INPUT, 0) /* (D25) MCASP0_ACLKX */ - J722S_IOPAD(0x1a8, PIN_INPUT, 0) /* (C26) MCASP0_AFSX */ - J722S_IOPAD(0x1a0, PIN_INPUT, 0) /* (F23) MCASP0_AXR0 */ - J722S_IOPAD(0x19c, PIN_OUTPUT, 0) /* (B25) MCASP0_AXR1 */ - >; - }; - - mcasp2_pins_default: mcasp2-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x070, PIN_INPUT, 3) /* (V24) GPMC0_AD13.MCASP2_ACLKX */ - J722S_IOPAD(0x06c, PIN_INPUT, 3) /* (V26) GPMC0_AD12.MCASP2_AFSX */ - J722S_IOPAD(0x05c, PIN_INPUT, 3) /* (U27) GPMC0_AD8.MCASP2_AXR0 */ - J722S_IOPAD(0x060, PIN_OUTPUT, 3) /* (U26) GPMC0_AD9.MCASP2_AXR1 */ - >; - }; - - oldi0_pins_default: oldi0-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x260, PIN_OUTPUT, 0) /* (AF23) OLDI0_A0N */ - J722S_IOPAD(0x25c, PIN_OUTPUT, 0) /* (AG24) OLDI0_A0P */ - J722S_IOPAD(0x268, PIN_OUTPUT, 0) /* (AG22) OLDI0_A1N */ - J722S_IOPAD(0x264, PIN_OUTPUT, 0) /* (AG23) OLDI0_A1P */ - J722S_IOPAD(0x270, PIN_OUTPUT, 0) /* (AB20) OLDI0_A2N */ - J722S_IOPAD(0x26c, PIN_OUTPUT, 0) /* (AB21) OLDI0_A2P */ - J722S_IOPAD(0x278, PIN_OUTPUT, 0) /* (AG20) OLDI0_A3N */ - J722S_IOPAD(0x274, PIN_OUTPUT, 0) /* (AG21) OLDI0_A3P */ - J722S_IOPAD(0x2a0, PIN_OUTPUT, 0) /* (AF21) OLDI0_CLK0N */ - J722S_IOPAD(0x29c, PIN_OUTPUT, 0) /* (AE20) OLDI0_CLK0P */ - >; - }; - - oldi1_pins_default: oldi1-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x280, PIN_OUTPUT, 0) /* (AD21) OLDI0_A4N */ - J722S_IOPAD(0x27c, PIN_OUTPUT, 0) /* (AC21) OLDI0_A4P */ - J722S_IOPAD(0x288, PIN_OUTPUT, 0) /* (AF19) OLDI0_A5N */ - J722S_IOPAD(0x284, PIN_OUTPUT, 0) /* (AF18) OLDI0_A5P */ - J722S_IOPAD(0x290, PIN_OUTPUT, 0) /* (AG17) OLDI0_A6N */ - J722S_IOPAD(0x28c, PIN_OUTPUT, 0) /* (AG18) OLDI0_A6P */ - J722S_IOPAD(0x298, PIN_OUTPUT, 0) /* (AB19) OLDI0_A7N */ - J722S_IOPAD(0x294, PIN_OUTPUT, 0) /* (AA20) OLDI0_A7P */ - J722S_IOPAD(0x2a8, PIN_OUTPUT, 0) /* (AD20) OLDI0_CLK1N */ - J722S_IOPAD(0x2a4, PIN_OUTPUT, 0) /* (AE19) OLDI0_CLK1P */ - >; - }; - - ospi0_pins_default: ospi0-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x000, PIN_OUTPUT, 0) /* (L24) OSPI0_CLK */ - J722S_IOPAD(0x02c, PIN_OUTPUT, 0) /* (K26) OSPI0_CSn0 */ - J722S_IOPAD(0x030, PIN_OUTPUT, 0) /* (K23) OSPI0_CSn1 */ - J722S_IOPAD(0x034, PIN_OUTPUT, 0) /* (K22) OSPI0_CSn2 */ - J722S_IOPAD(0x00c, PIN_INPUT, 0) /* (K27) OSPI0_D0 */ - J722S_IOPAD(0x010, PIN_INPUT, 0) /* (L27) OSPI0_D1 */ - J722S_IOPAD(0x014, PIN_INPUT, 0) /* (L26) OSPI0_D2 */ - J722S_IOPAD(0x018, PIN_INPUT, 0) /* (L25) OSPI0_D3 */ - >; - bootph-all; - }; - - pcie0_rc_pins_default: pcie0-rc-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x2ac, PIN_OUTPUT, 0) /* (F25) PCIE0_CLKREQn */ - J722S_IOPAD(0x1b4, PIN_OUTPUT, 7) /* (B20) SPI0_CS0.GPIO1_15 */ - >; - }; - - pmic_irq_pins_default: pmic-irq-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x090, PIN_INPUT, 7) /* (P27) GPMC0_BE0n_CLE.GPIO0_35 */ - >; - }; - - rgmii1_pins_default: rgmii1-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x14c, PIN_INPUT, 0) /* (AC25) RGMII1_RD0 */ - J722S_IOPAD(0x150, PIN_INPUT, 0) /* (AD27) RGMII1_RD1 */ - J722S_IOPAD(0x154, PIN_INPUT, 0) /* (AE24) RGMII1_RD2 */ - J722S_IOPAD(0x158, PIN_INPUT, 0) /* (AE26) RGMII1_RD3 */ - J722S_IOPAD(0x148, PIN_INPUT, 0) /* (AE27) RGMII1_RXC */ - J722S_IOPAD(0x144, PIN_INPUT, 0) /* (AD23) RGMII1_RX_CTL */ - J722S_IOPAD(0x134, PIN_OUTPUT, 0) /* (AF27) RGMII1_TD0 */ - J722S_IOPAD(0x138, PIN_OUTPUT, 0) /* (AE23) RGMII1_TD1 */ - J722S_IOPAD(0x13c, PIN_OUTPUT, 0) /* (AG25) RGMII1_TD2 */ - J722S_IOPAD(0x140, PIN_OUTPUT, 0) /* (AF24) RGMII1_TD3 */ - J722S_IOPAD(0x130, PIN_OUTPUT, 0) /* (AG26) RGMII1_TXC */ - J722S_IOPAD(0x12c, PIN_OUTPUT, 0) /* (AF25) RGMII1_TX_CTL */ - >; - }; - - rgmii2_pins_default: rgmii2-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x0f8, PIN_INPUT, 2) /* (AB24) VOUT0_HSYNC.RGMII2_RD0 */ - J722S_IOPAD(0x0fc, PIN_INPUT, 2) /* (AC27) VOUT0_DE.RGMII2_RD1 */ - J722S_IOPAD(0x100, PIN_INPUT, 2) /* (AB23) VOUT0_VSYNC.RGMII2_RD2 */ - J722S_IOPAD(0x104, PIN_INPUT, 2) /* (AC26) VOUT0_PCLK.RGMII2_RD3 */ - J722S_IOPAD(0x0f4, PIN_INPUT, 2) /* (AB27) VOUT0_DATA15.RGMII2_RXC */ - J722S_IOPAD(0x0f0, PIN_INPUT, 2) /* (AB26) VOUT0_DATA14.RGMII2_RX_CTL */ - J722S_IOPAD(0x0e0, PIN_OUTPUT, 2) /* (AA25) VOUT0_DATA10.RGMII2_TD0 */ - J722S_IOPAD(0x0e4, PIN_OUTPUT, 2) /* (AB25) VOUT0_DATA11.RGMII2_TD1 */ - J722S_IOPAD(0x0e8, PIN_OUTPUT, 2) /* (AA23) VOUT0_DATA12.RGMII2_TD2 */ - J722S_IOPAD(0x0ec, PIN_OUTPUT, 2) /* (AA22) VOUT0_DATA13.RGMII2_TD3 */ - J722S_IOPAD(0x0dc, PIN_OUTPUT, 2) /* (AA27) VOUT0_DATA9.RGMII2_TXC */ - J722S_IOPAD(0x0d8, PIN_OUTPUT, 2) /* (AA24) VOUT0_DATA8.RGMII2_TX_CTL */ - >; - }; - - rtc_pins_default: rtc-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x094, PIN_INPUT, 7) /* (P26) GPMC0_BE1n.GPIO0_36 */ - >; - }; - - sdhci1_pins_default: sdhci1-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x23c, PIN_INPUT, 0) /* (H22) MMC1_CMD */ - J722S_IOPAD(0x234, PIN_OUTPUT, 0) /* (H24) MMC1_CLK */ - J722S_IOPAD(0x230, PIN_INPUT, 0) /* (H23) MMC1_DAT0 */ - J722S_IOPAD(0x22c, PIN_INPUT, 0) /* (H20) MMC1_DAT1 */ - J722S_IOPAD(0x228, PIN_INPUT, 0) /* (J23) MMC1_DAT2 */ - J722S_IOPAD(0x224, PIN_INPUT, 0) /* (H25) MMC1_DAT3 */ - J722S_IOPAD(0x240, PIN_INPUT, 0) /* (B24) MMC1_SDCD */ - J722S_IOPAD(0x244, PIN_INPUT, 0) /* (A24) MMC1_SDWP */ - >; - bootph-all; - }; - - usb0_connector_pins_default: usb0-connector-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x08c, PIN_INPUT_PULLUP, 7) /* (N23) GPMC0_WEn.GPIO0_34 */ - >; - }; - - usb1_pins_default: usb1-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x258, PIN_OUTPUT, 0) /* (B27) USB1_DRVVBUS */ - >; - }; - - vcc_3p3_sd_s0_pins_default: vcc-3p3-sd-s0-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x01c, PIN_OUTPUT, 7) /* (L21) OSPI0_D4.GPIO0_7 */ - >; - bootph-all; - }; - - vcc_3p3_sd_vio_s0_pins_default: vcc-3p3-sd-vio-s0-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x020, PIN_OUTPUT, 7) /* (M26) OSPI0_D5.GPIO0_8 */ - >; - bootph-all; - }; - - vcc_3p3_cam_s0_pins_default: vcc-3p3-cam-s0-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x1f0, PIN_OUTPUT, 7) /* (A23) EXT_REFCLK1.GPIO1_30 */ - >; - }; - - vcc_lcd0_panel_pins_default: vcc-lcd0-panel-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x078, PIN_OUTPUT, 7) /* (V23) GPMC0_AD15.GPIO0_30 */ - >; - }; - - vcc_lcd1_panel_pins_default: vcc-lcd1-panel-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x1c4, PIN_OUTPUT, 7) /* (E20) SPI0_D1.GPIO1_19 */ - >; - }; - - vcc_usb0_vbus_pins_default: vcc-usb0-vbus-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x254, PIN_OUTPUT, 7) /* (E25) USB0_DRVVBUS.GPIO1_50 */ - >; - }; -}; - -/* SER1 */ -&main_uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&main_uart0_pins_default>; - bootph-all; - status = "okay"; -}; - -/* SER2 */ -&main_uart5 { - pinctrl-names = "default"; - pinctrl-0 = <&main_uart5_pins_default>; - bootph-all; - status = "okay"; -}; - -/* I2S0 */ -&mcasp0 { - #sound-dai-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&mcasp0_pins_default>; - op-mode = <0>; /* I2S */ - tdm-slots = <2>; - serial-dir = <2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0>; -}; - -/* I2S2 */ -&mcasp2 { - #sound-dai-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&mcasp2_pins_default>; - op-mode = <0>; /* I2S */ - tdm-slots = <2>; - serial-dir = <2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0>; -}; - -/* CAN0 */ -&mcu_mcan0 { - pinctrl-names = "default"; - pinctrl-0 = <&mcu_mcan0_pins_default>; - status = "okay"; -}; - -/* CAN1 */ -&mcu_mcan1 { - pinctrl-names = "default"; - pinctrl-0 = <&mcu_mcan1_pins_default>; - status = "okay"; -}; - -&mcu_gpio0 { - gpio-line-names = - "", "", "", "", "", "", "", "", "", "", "", /* 10 */ "GPIO12", - "MCU_INT#", "", "", "", "", "", "", "", "", "", "", "GPIO13"; -}; - -/* I2C_GP */ -&mcu_i2c0 { - pinctrl-names = "default"; - pinctrl-0 = <&mcu_i2c0_pins_default>; - clock-frequency = <100000>; - status = "okay"; - - /* SMARC Module EEPROM */ - eeprom@50 { - compatible = "atmel,24c32"; - reg = <0x50>; - pagesize = <32>; - vcc-supply = <&vcc_1p8_s0>; - }; -}; - -&mcu_pmx0 { - mcu_i2c0_pins_default: mcu-i2c0-default-pins { - pinctrl-single,pins = < - J722S_MCU_IOPAD(0x044, PIN_INPUT, 0) /* (B13) MCU_I2C0_SCL */ - J722S_MCU_IOPAD(0x048, PIN_INPUT, 0) /* (E11) MCU_I2C0_SDA */ - >; - }; - mcu_mcan0_pins_default: mcu-mcan0-default-pins { - pinctrl-single,pins = < - J722S_MCU_IOPAD(0x038, PIN_INPUT, 0) /* (D8) MCU_MCAN0_RX */ - J722S_MCU_IOPAD(0x034, PIN_OUTPUT, 0) /* (B2) MCU_MCAN0_TX */ - >; - }; - - mcu_mcan1_pins_default: mcu-mcan1-default-pins { - pinctrl-single,pins = < - J722S_MCU_IOPAD(0x040, PIN_INPUT, 0) /* (B1) MCU_MCAN1_RX */ - J722S_MCU_IOPAD(0x03c, PIN_OUTPUT, 0) /* (C1) MCU_MCAN1_TX */ - >; - }; - - mcu_uart0_pins_default: mcu-uart0-default-pins { - pinctrl-single,pins = < - J722S_MCU_IOPAD(0x014, PIN_INPUT, 0) /* (B8) MCU_UART0_RXD */ - J722S_MCU_IOPAD(0x018, PIN_OUTPUT, 0) /* (B4) MCU_UART0_TXD */ - J722S_MCU_IOPAD(0x01c, PIN_INPUT, 0) /* (B5) MCU_UART0_CTSn */ - J722S_MCU_IOPAD(0x020, PIN_OUTPUT, 0) /* (C5) MCU_UART0_RTSn */ - >; - bootph-all; - }; - - mcu_spi0_pins_default: mcu-spi0-default-pins { - pinctrl-single,pins = < - J722S_MCU_IOPAD(0x008, PIN_OUTPUT, 0) /* (A9) MCU_SPI0_CLK */ - J722S_MCU_IOPAD(0x000, PIN_OUTPUT, 0) /* (C12) MCU_SPI0_CS0 */ - J722S_MCU_IOPAD(0x004, PIN_OUTPUT, 0) /* (A10) MCU_SPI0_CS1 */ - J722S_MCU_IOPAD(0x00c, PIN_INPUT, 0) /* (B12) MCU_SPI0_D0 */ - J722S_MCU_IOPAD(0x010, PIN_OUTPUT, 0) /* (C11) MCU_SPI0_D1 */ - >; - }; - - wkup_uart0_pins_default: wkup-uart0-default-pins { - pinctrl-single,pins = < - J722S_MCU_IOPAD(0x024, PIN_INPUT, 0) /* (B3) WKUP_UART0_RXD */ - J722S_MCU_IOPAD(0x028, PIN_OUTPUT, 0) /* (C8) WKUP_UART0_TXD */ - >; - bootph-all; - }; - - wkup_i2c0_pins_default: wkup-i2c0-default-pins { - pinctrl-single,pins = < - J722S_MCU_IOPAD(0x04c, PIN_INPUT, 0) /* (B9) WKUP_I2C0_SCL */ - J722S_MCU_IOPAD(0x050, PIN_INPUT, 0) /* (D11) WKUP_I2C0_SDA */ - >; - }; -}; - -/* SPI0 */ -&mcu_spi0 { - pinctrl-names = "default"; - pinctrl-0 = <&mcu_spi0_pins_default>; -}; - -/* SER0 */ -&mcu_uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&mcu_uart0_pins_default>; - bootph-all; - status = "okay"; -}; - -/* QSPI0 */ -&ospi0 { - pinctrl-0 = <&ospi0_pins_default>; - pinctrl-names = "default"; - status = "okay"; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0>; - spi-max-frequency = <104000000>; - spi-rx-bus-width = <2>; - spi-tx-bus-width = <2>; - m25p,fast-read; - cdns,tshsl-ns = <60>; - cdns,tsd2d-ns = <60>; - cdns,tchsh-ns = <60>; - cdns,tslch-ns = <60>; - cdns,read-delay = <3>; - vcc-supply = <&vcc_1p8_s0>; - bootph-all; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - reg = <0x000000 0x400000>; - label = "failsafe bootloader"; - read-only; - }; - }; - - otp-1 { - compatible = "user-otp"; - - nvmem-layout { - compatible = "kontron,sa67-vpd", "kontron,sl28-vpd"; - - serial_number: serial-number { - }; - - base_mac_address: base-mac-address { - #nvmem-cell-cells = <1>; - }; - }; - }; - }; -}; - -&pcie0_rc { - pinctrl-names = "default"; - pinctrl-0 = <&pcie0_rc_pins_default>; - - /* - * This is low active, but the driver itself is broken and already - * inverts the logic. - */ - reset-gpios = <&main_gpio1 15 GPIO_ACTIVE_HIGH>; - phys = <&serdes1_pcie>; - phy-names = "pcie-phy"; - status = "okay"; -}; - -&sdhci0 { - disable-wp; - bootph-all; - ti,driver-strength-ohm = <50>; - status = "okay"; -}; - -/* SDIO */ -&sdhci1 { - pinctrl-names = "default"; - pinctrl-0 = <&sdhci1_pins_default>; - vmmc-supply = <&vcc_3p3_sd_s0>; - vqmmc-supply = <&vcc_3p3_sd_vio_s0>; - bootph-all; - cd-gpios = <&main_gpio1 48 GPIO_ACTIVE_LOW>; - cd-debounce-delay-ms = <100>; - ti,fails-without-test-cd; - ti,driver-strength-ohm = <50>; - status = "okay"; -}; - -&serdes_ln_ctrl { - idle-states = , - ; -}; - -&serdes_wiz0 { - status = "okay"; -}; - -&serdes_wiz1 { - status = "okay"; -}; - -&serdes0 { - serdes0_usb3: phy@0 { - reg = <0>; - #phy-cells = <0>; - resets = <&serdes_wiz0 1>; - cdns,num-lanes = <1>; - cdns,phy-type = ; - }; -}; - -&serdes1 { - serdes1_pcie: phy@0 { - reg = <0>; - #phy-cells = <0>; - resets = <&serdes_wiz1 1>; - cdns,num-lanes = <1>; - cdns,phy-type = ; - }; -}; - -&usb0 { - /* dual role is implemented but not a full featured OTG */ - adp-disable; - hnp-disable; - srp-disable; - dr_mode = "otg"; - usb-role-switch; - role-switch-default-mode = "peripheral"; - status = "okay"; - - port { - usb0_hc: endpoint { - remote-endpoint = <&usb0_connector>; - }; - }; -}; - -&usb0_phy_ctrl { - /* - * Keep this node in the SPL to be able to use the USB controller to - * boot via DFU. - */ - bootph-all; -}; - -&usb1 { - pinctrl-names = "default"; - pinctrl-0 = <&usb1_pins_default>; - - dr_mode = "host"; - maximum-speed = "super-speed"; - phys = <&serdes0_usb3>; - phy-names = "cdns3,usb3-phy"; -}; - -&usbss0 { - ti,vbus-divider; - status = "okay"; -}; - -&usbss1 { - ti,vbus-divider; - status = "okay"; -}; - -/* I2C_PM */ -&wkup_i2c0 { - pinctrl-names = "default"; - pinctrl-0 = <&wkup_i2c0_pins_default>; - clock-frequency = <100000>; - status = "okay"; -}; - -/* SER3 */ -&wkup_uart0 { - /* WKUP UART0 is used by Device Manager firmware */ - pinctrl-names = "default"; - pinctrl-0 = <&wkup_uart0_pins_default>; - bootph-all; - status = "reserved"; -}; diff --git a/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-gbe1.dtso b/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-gbe1.dtso deleted file mode 100644 index 5dfb0b8f10d2..000000000000 --- a/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-gbe1.dtso +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only OR MIT -/* - * Second ethernet port GBE1. - * - * Copyright (c) 2025 Kontron Europe GmbH - */ - -/dts-v1/; -/plugin/; - -&cpsw3g_mdio { - #address-cells = <1>; - #size-cells = <0>; - - phy1: ethernet-phy@1 { - reg = <1>; - }; -}; - -&cpsw_port2 { - phy-connection-type = "rgmii-id"; - phy-handle = <&phy1>; - nvmem-cells = <&base_mac_address 1>; - nvmem-cell-names = "mac-address"; - status = "okay"; -}; diff --git a/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-gpios.dtso b/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-gpios.dtso deleted file mode 100644 index a6ae758e0b3a..000000000000 --- a/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-gpios.dtso +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only OR MIT -/* - * SMARC GPIOs. - * - * Copyright (c) 2025 Kontron Europe GmbH - */ - -/dts-v1/; -/plugin/; - -#include -#include "k3-pinctrl.h" - -&main_gpio0 { - pinctrl-names = "default"; - pinctrl-0 = <&main_gpio0_pins_default>; -}; - -&main_gpio1 { - pinctrl-names = "default"; - pinctrl-0 = <&main_gpio1_pins_default>; -}; - -&main_pmx0 { - main_gpio0_pins_default: main-gpio0-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x0d0, PIN_INPUT, 7) /* (Y26) VOUT0_DATA6.GPIO0_51 */ - J722S_IOPAD(0x0d4, PIN_INPUT, 7) /* (Y27) VOUT0_DATA7.GPIO0_52 */ - J722S_IOPAD(0x118, PIN_INPUT, 7) /* (H26) MMC2_CLK.GPIO0_69 */ - J722S_IOPAD(0x120, PIN_INPUT, 7) /* (F27) MMC2_CMD.GPIO0_70 */ - >; - }; - - main_gpio1_pins_default: main-gpio1-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x194, PIN_INPUT, 7) /* (A25) MCASP0_AXR3.GPIO1_7 */ - J722S_IOPAD(0x198, PIN_INPUT, 7) /* (A26) MCASP0_AXR2.GPIO1_8 */ - J722S_IOPAD(0x1ac, PIN_INPUT, 7) /* (C27) MCASP0_AFSR.GPIO1_13 */ - J722S_IOPAD(0x1b0, PIN_INPUT, 7) /* (F24) MCASP0_ACLKR.GPIO1_14 */ - J722S_IOPAD(0x1d8, PIN_INPUT, 7) /* (D22) MCAN0_TX.GPIO1_24 */ - J722S_IOPAD(0x1dc, PIN_INPUT, 7) /* (C22) MCAN0_RX.GPIO1_25 */ - J722S_IOPAD(0x1e8, PIN_INPUT, 7) /* (C24) I2C1_SCL.GPIO1_28 */ - J722S_IOPAD(0x1ec, PIN_INPUT, 7) /* (A22) I2C1_SDA.GPIO1_29 */ - >; - }; -}; - -&mcu_gpio0 { - pinctrl-names = "default"; - pinctrl-0 = <&mcu_gpio0_pins_default>; -}; - -&mcu_pmx0 { - mcu_gpio0_pins_default: mcu-gpio0-default-pins { - pinctrl-single,pins = < - J722S_IOPAD(0x02c, PIN_INPUT, 7) /* (C4) WKUP_UART0_CTSn.MCU_GPIO0_11 */ - J722S_IOPAD(0x084, PIN_INPUT, 7) /* (F12) WKUP_CLKOUT0.MCU_GPIO0_23 */ - >; - }; - -}; diff --git a/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-rtc-rv8263.dtso b/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-rtc-rv8263.dtso deleted file mode 100644 index 0a3e9f614c4c..000000000000 --- a/arch/arm64/boot/dts/ti/k3-am67a-kontron-sa67-rtc-rv8263.dtso +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only OR MIT -/* - * Microcrystal RV8263 RTC variant. - * - * Copyright (c) 2025 Kontron Europe GmbH - */ - -/dts-v1/; -/plugin/; - -#include - -&{/} { - aliases { - rtc0 = "/bus@f0000/i2c@20000000/rtc@51"; /* &rtc */ - rtc1 = "/bus@f0000/bus@b00000/rtc@2b1f0000"; /* &wkup_rtc0 */ - }; -}; - -&main_i2c0 { - #address-cells = <1>; - #size-cells = <0>; - - rtc: rtc@51 { - compatible = "microcrystal,rv8263"; - reg = <0x51>; - pinctrl-names = "default"; - pinctrl-0 = <&rtc_pins_default>; - interrupts-extended = <&main_gpio0 36 IRQ_TYPE_EDGE_FALLING>; - }; -}; diff --git a/arch/arm64/boot/dts/ti/k3-am68-phyboard-izar-lvds-ph128800t006.dtso b/arch/arm64/boot/dts/ti/k3-am68-phyboard-izar-lvds-ph128800t006.dtso new file mode 100644 index 000000000000..9eb28aa9e8ef --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-am68-phyboard-izar-lvds-ph128800t006.dtso @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Copyright (C) 2026 PHYTEC Messtechnik GmbH + * Author: Dominik Haller + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include "k3-pinctrl.h" + +&{/} { + backlight_lvds: backlight { + compatible = "pwm-backlight"; + brightness-levels = <0 32 64 96 128 160 192 224 255>; + default-brightness-level = <6>; + enable-gpios = <&exp2 12 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&backlight_pins_default>; + power-supply = <&bl_12v>; + pwms = <&main_ehrpwm0 1 44000 0>; + }; + + bl_12v: regulator-backlight { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&exp2 13 GPIO_ACTIVE_HIGH>; + regulator-name = "BL_12V"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + }; + + panel-lvds { + compatible = "powertip,ph128800t006-zhc01"; + backlight = <&backlight_lvds>; + power-supply = <&vcc_5v0>; + + port { + panel_in: endpoint { + remote-endpoint = <&panel_bridge_out>; + }; + }; + }; +}; + +&main_pmx0 { + backlight_pins_default: backlight-default-pins { + pinctrl-single,pins = < + J721S2_IOPAD(0x064, PIN_OUTPUT, 9) /* (W28) MCAN0_TX.EHRPWM0_B */ + >; + }; +}; + +&dphy_tx0 { + status = "okay"; +}; + +&dss { + status = "okay"; +}; + +&dss_ports { + #address-cells = <1>; + #size-cells = <0>; + + port@2 { + reg = <2>; + dpi2_out: endpoint { + remote-endpoint = <&dsi0_in>; + }; + }; +}; + +&dsi0 { + status = "okay"; +}; + +&dsi0_ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi0_out: endpoint { + remote-endpoint = <&panel_bridge_in>; + }; + }; + + port@1 { + reg = <1>; + dsi0_in: endpoint { + remote-endpoint = <&dpi2_out>; + }; + }; +}; + +&main_ehrpwm0 { + status = "okay"; +}; + +&sn65dsi83 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + panel_bridge_in: endpoint { + remote-endpoint = <&dsi0_out>; + data-lanes = <1 2 3 4>; + }; + }; + + port@2 { + reg = <2>; + panel_bridge_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am68-phyboard-izar-peb-av-15.dtso b/arch/arm64/boot/dts/ti/k3-am68-phyboard-izar-peb-av-15.dtso new file mode 100644 index 000000000000..92007bfd5d53 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-am68-phyboard-izar-peb-av-15.dtso @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Copyright (C) 2026 PHYTEC Messtechnik GmbH + * Author: Dominik Haller + */ + +/dts-v1/; +/plugin/; + +#include +#include "k3-pinctrl.h" + +&{/} { + audio_refclk1: audio-clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <19200000>; + }; + + hdmi: hdmi-connector { + compatible = "hdmi-connector"; + label = "hdmi"; + type = "a"; + ddc-i2c-bus = <&main_i2c2>; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <<8912b_out>; + }; + }; + }; + + reg_audio_3v3: regulator-audio-3v3 { + compatible = "regulator-fixed"; + regulator-name = "VCC3V3_AUDIO"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + reg_audio_1v8: regulator-audio-1v8 { + compatible = "regulator-fixed"; + regulator-name = "VCC1V8_AUDIO"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "PEB-AV-15"; + simple-audio-card,widgets = + "Headphone", "Headphone Jack", + "Microphone", "Mic Jack"; + simple-audio-card,routing = + "Headphone Jack", "HPLOUT", + "Headphone Jack", "HPROUT", + "MIC3R", "Mic Jack", + "Mic Jack", "Mic Bias"; + simple-audio-card,format = "dsp_b"; + simple-audio-card,bitclock-inversion; + simple-audio-card,bitclock-master = <&link0_codec>; + simple-audio-card,frame-master = <&link0_codec>; + + link0_cpu: simple-audio-card,cpu { + sound-dai = <&mcasp0>; + }; + + link0_codec: simple-audio-card,codec { + sound-dai = <&audio_codec>; + clocks = <&audio_refclk1>; + }; + }; + +}; + +&dphy_tx1 { + status = "okay"; +}; + +&dsi1 { + status = "okay"; +}; + +&dsi1_ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + dsi1_out: endpoint { + remote-endpoint = <<8912b_in>; + }; + }; + + port@1 { + reg = <1>; + dsi1_in: endpoint { + remote-endpoint = <&dpi3_out>; + }; + }; +}; + +&dss { + status = "okay"; +}; + +&dss_ports { + #address-cells = <1>; + #size-cells = <0>; + + port@3 { + reg = <3>; + dpi3_out: endpoint { + remote-endpoint = <&dsi1_in>; + }; + }; +}; + +&mcasp0 { + pinctrl-names = "default"; + pinctrl-0 = <&mcasp0_pins>; + + #sound-dai-cells = <0>; + + op-mode = <0>; /* MCASP_IIS_MODE */ + tdm-slots = <2>; + + /* 4 serializers */ + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 2 0 0 1 + 0 0 0 0 + 0 0 0 0 + 0 0 0 0 + >; + + tx-num-evt = <32>; + rx-num-evt = <32>; + status = "okay"; +}; + +&main_i2c2 { + #address-cells = <1>; + #size-cells = <0>; + + audio_codec: audio-codec@18 { + compatible = "ti,tlv320aic3007"; + reg = <0x18>; + #sound-dai-cells = <0>; + ai3x-micbias-vg = <2>; + AVDD-supply = <®_audio_3v3>; + IOVDD-supply = <®_audio_3v3>; + DRVDD-supply = <®_audio_3v3>; + DVDD-supply = <®_audio_1v8>; + }; + + bridge@48 { + compatible = "lontium,lt8912b"; + reg = <0x48>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lt8912b_in: endpoint { + data-lanes = <0 1 2 3>; + remote-endpoint = <&dsi1_out>; + }; + }; + + port@1 { + reg = <1>; + lt8912b_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; +}; + +&main_pmx0 { + mcasp0_pins: mcasp0-default-pins { + pinctrl-single,pins = < + J721S2_IOPAD(0x03c, PIN_INPUT, 1) /* (U27) WCLK, MCASP0_AFSX.MCASP0_AFSX */ + J721S2_IOPAD(0x038, PIN_INPUT, 1) /* (AB28) BCLK, MCASP0_ACLKX.MCASP0_ACLKX */ + J721S2_IOPAD(0x040, PIN_OUTPUT, 1) /* (AC28) DOUT, MCASP0_AXR0.MCASP0_AXR0 */ + J721S2_IOPAD(0x07c, PIN_INPUT, 1) /* (T27) DIN, MCASP0_AXR3.MCASP0_AXR3 */ + >; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-am68-phyboard-izar.dts b/arch/arm64/boot/dts/ti/k3-am68-phyboard-izar.dts index e221ccb30e95..225fe7a7803b 100644 --- a/arch/arm64/boot/dts/ti/k3-am68-phyboard-izar.dts +++ b/arch/arm64/boot/dts/ti/k3-am68-phyboard-izar.dts @@ -298,6 +298,23 @@ }; }; +&dss { + /* + * VP0 - Displayport + * VP1 - DPI0 + * VP2 - DSI0 + * VP3 - DPI1/DSI1 + */ + assigned-clocks = <&k3_clks 158 2>, + <&k3_clks 158 5>, + <&k3_clks 158 14>, + <&k3_clks 158 18>; + assigned-clock-parents = <&k3_clks 158 3>, + <&k3_clks 158 7>, + <&k3_clks 158 16>, + <&k3_clks 158 22>; +}; + &i2c_som_rtc { trickle-resistor-ohms = <3000>; }; diff --git a/arch/arm64/boot/dts/ti/k3-am68-phycore-som.dtsi b/arch/arm64/boot/dts/ti/k3-am68-phycore-som.dtsi index 0ff511028f81..ab87767419fe 100644 --- a/arch/arm64/boot/dts/ti/k3-am68-phycore-som.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am68-phycore-som.dtsi @@ -151,6 +151,12 @@ >; bootph-all; }; + + wkup_sn65dsi83_pins_default: wkup-sn65dsi83-default-pins { + pinctrl-single,pins = < + J721S2_WKUP_IOPAD(0x084, PIN_OUTPUT, 7) /* (F25) WKUP_GPIO0_11 */ + >; + }; }; &main_cpsw { @@ -183,6 +189,17 @@ pinctrl-names = "default"; pinctrl-0 = <&main_i2c0_pins_default>; + sn65dsi83: bridge@2d { + compatible = "ti,sn65dsi83"; + reg = <0x2d>; + pinctrl-names = "default"; + pinctrl-0 = <&wkup_sn65dsi83_pins_default>; + enable-gpios = <&wkup_gpio0 11 GPIO_ACTIVE_LOW>; + vcc-supply = <&bucka4>; + /* enabled in panel overlay */ + status = "disabled"; + }; + temperature-sensor@48 { compatible = "ti,tmp102"; reg = <0x48>; diff --git a/arch/arm64/boot/dts/ti/k3-am69-aquila-clover.dts b/arch/arm64/boot/dts/ti/k3-am69-aquila-clover.dts index ec8ff4587715..dc0d3cf2f985 100644 --- a/arch/arm64/boot/dts/ti/k3-am69-aquila-clover.dts +++ b/arch/arm64/boot/dts/ti/k3-am69-aquila-clover.dts @@ -26,7 +26,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpio_21_dp>; /* Aquila GPIO_21_DP (AQUILA B57) */ - gpio = <&main_gpio0 37 GPIO_ACTIVE_HIGH>; + gpio = <&main_gpio0 21 GPIO_ACTIVE_HIGH>; enable-active-high; regulator-max-microvolt = <3300000>; regulator-min-microvolt = <3300000>; diff --git a/arch/arm64/boot/dts/ti/k3-am69-aquila-dev.dts b/arch/arm64/boot/dts/ti/k3-am69-aquila-dev.dts index f48601ae38b7..d3677c2c2547 100644 --- a/arch/arm64/boot/dts/ti/k3-am69-aquila-dev.dts +++ b/arch/arm64/boot/dts/ti/k3-am69-aquila-dev.dts @@ -33,7 +33,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpio_21_dp>; /* Aquila GPIO_21_DP (AQUILA B57) */ - gpio = <&main_gpio0 37 GPIO_ACTIVE_HIGH>; + gpio = <&main_gpio0 21 GPIO_ACTIVE_HIGH>; enable-active-high; regulator-max-microvolt = <3300000>; regulator-min-microvolt = <3300000>; diff --git a/arch/arm64/boot/dts/ti/k3-j7200-common-proc-board.dts b/arch/arm64/boot/dts/ti/k3-j7200-common-proc-board.dts index 3e5efdfe87f1..54dea36b153b 100644 --- a/arch/arm64/boot/dts/ti/k3-j7200-common-proc-board.dts +++ b/arch/arm64/boot/dts/ti/k3-j7200-common-proc-board.dts @@ -331,6 +331,7 @@ reg = <0>; ti,rx-internal-delay = ; ti,fifo-depth = ; + ti,min-output-impedance; }; }; diff --git a/arch/arm64/boot/dts/ti/k3-j7200-main.dtsi b/arch/arm64/boot/dts/ti/k3-j7200-main.dtsi index 628ff89dd72f..67fde5667b8d 100644 --- a/arch/arm64/boot/dts/ti/k3-j7200-main.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j7200-main.dtsi @@ -25,9 +25,8 @@ }; }; - scm_conf: scm-conf@100000 { - compatible = "ti,j721e-system-controller", "syscon", "simple-mfd"; - reg = <0x00 0x00100000 0x00 0x1c000>; + scm_conf: bus@100000 { + compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; ranges = <0x00 0x00 0x00100000 0x1c000>; diff --git a/arch/arm64/boot/dts/ti/k3-j7200-ti-ipc-firmware.dtsi b/arch/arm64/boot/dts/ti/k3-j7200-ti-ipc-firmware.dtsi index 9477f1efbbc6..d65f68c7d432 100644 --- a/arch/arm64/boot/dts/ti/k3-j7200-ti-ipc-firmware.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j7200-ti-ipc-firmware.dtsi @@ -100,6 +100,7 @@ mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core0>; memory-region = <&mcu_r5fss0_core0_dma_memory_region>, <&mcu_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -107,6 +108,7 @@ mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core1>; memory-region = <&mcu_r5fss0_core1_dma_memory_region>, <&mcu_r5fss0_core1_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -119,6 +121,7 @@ mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core0>; memory-region = <&main_r5fss0_core0_dma_memory_region>, <&main_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -126,5 +129,6 @@ mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core1>; memory-region = <&main_r5fss0_core1_dma_memory_region>, <&main_r5fss0_core1_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-common-proc-board.dts b/arch/arm64/boot/dts/ti/k3-j721e-common-proc-board.dts index 47702fb279a4..53e7fbcef52b 100644 --- a/arch/arm64/boot/dts/ti/k3-j721e-common-proc-board.dts +++ b/arch/arm64/boot/dts/ti/k3-j721e-common-proc-board.dts @@ -777,6 +777,7 @@ reg = <0>; ti,rx-internal-delay = ; ti,fifo-depth = ; + ti,min-output-impedance; }; }; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-ti-ipc-firmware.dtsi b/arch/arm64/boot/dts/ti/k3-j721e-ti-ipc-firmware.dtsi index 40c6cc99c405..5d4fc26b413b 100644 --- a/arch/arm64/boot/dts/ti/k3-j721e-ti-ipc-firmware.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j721e-ti-ipc-firmware.dtsi @@ -219,6 +219,7 @@ mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core0>; memory-region = <&mcu_r5fss0_core0_dma_memory_region>, <&mcu_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; }; &mcu_r5fss0_core1 { @@ -226,6 +227,7 @@ mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core1>; memory-region = <&mcu_r5fss0_core1_dma_memory_region>, <&mcu_r5fss0_core1_memory_region>; + memory-region-names = "dma", "firmware"; }; &main_r5fss0 { @@ -238,6 +240,7 @@ mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core0>; memory-region = <&main_r5fss0_core0_dma_memory_region>, <&main_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; }; &main_r5fss0_core1 { @@ -245,6 +248,7 @@ mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core1>; memory-region = <&main_r5fss0_core1_dma_memory_region>, <&main_r5fss0_core1_memory_region>; + memory-region-names = "dma", "firmware"; }; &main_r5fss1 { @@ -257,6 +261,7 @@ mboxes = <&mailbox0_cluster2 &mbox_main_r5fss1_core0>; memory-region = <&main_r5fss1_core0_dma_memory_region>, <&main_r5fss1_core0_memory_region>; + memory-region-names = "dma", "firmware"; }; &main_r5fss1_core1 { @@ -264,6 +269,7 @@ mboxes = <&mailbox0_cluster2 &mbox_main_r5fss1_core1>; memory-region = <&main_r5fss1_core1_dma_memory_region>, <&main_r5fss1_core1_memory_region>; + memory-region-names = "dma", "firmware"; }; &c66_0 { diff --git a/arch/arm64/boot/dts/ti/k3-j721s2-common-proc-board.dts b/arch/arm64/boot/dts/ti/k3-j721s2-common-proc-board.dts index 4fea99519113..19c5157510b2 100644 --- a/arch/arm64/boot/dts/ti/k3-j721s2-common-proc-board.dts +++ b/arch/arm64/boot/dts/ti/k3-j721s2-common-proc-board.dts @@ -530,6 +530,48 @@ cdns,tchsh-ns = <60>; cdns,tslch-ns = <60>; cdns,read-delay = <2>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "qspi.tiboot3"; + reg = <0x0 0x80000>; + }; + + partition@80000 { + label = "qspi.tispl"; + reg = <0x80000 0x200000>; + }; + + partition@280000 { + label = "qspi.u-boot"; + reg = <0x280000 0x400000>; + }; + + partition@680000 { + label = "qspi.env"; + reg = <0x680000 0x40000>; + }; + + partition@6c0000 { + label = "qspi.env.backup"; + reg = <0x6c0000 0x40000>; + }; + + partition@800000 { + label = "qspi.rootfs"; + reg = <0x800000 0x37c0000>; + }; + + partition@3fc0000 { + label = "qspi.phypattern"; + reg = <0x3fc0000 0x40000>; + bootph-all; + }; + }; }; }; diff --git a/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi b/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi index 80c51b11ac9f..1228ac5711bf 100644 --- a/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi @@ -37,9 +37,8 @@ }; }; - scm_conf: syscon@104000 { - compatible = "ti,j721e-system-controller", "syscon", "simple-mfd"; - reg = <0x00 0x00104000 0x00 0x18000>; + scm_conf: bus@104000 { + compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; ranges = <0x00 0x00 0x00104000 0x18000>; @@ -175,7 +174,7 @@ }; main_pmx0: pinctrl@11c000 { - compatible = "pinctrl-single"; + compatible = "ti,j7200-padconf", "pinctrl-single"; /* Proxy 0 addressing */ reg = <0x0 0x11c000 0x0 0x120>; #pinctrl-cells = <1>; @@ -185,7 +184,7 @@ /* TIMERIO pad input CTRLMMR_TIMER*_CTRL registers */ main_timerio_input: pinctrl@104200 { - compatible = "pinctrl-single"; + compatible = "ti,j7200-padconf", "pinctrl-single"; reg = <0x00 0x104200 0x00 0x50>; #pinctrl-cells = <1>; pinctrl-single,register-width = <32>; @@ -194,7 +193,7 @@ /* TIMERIO pad output CTCTRLMMR_TIMERIO*_CTRL registers */ main_timerio_output: pinctrl@104280 { - compatible = "pinctrl-single"; + compatible = "ti,j7200-padconf", "pinctrl-single"; reg = <0x00 0x104280 0x00 0x20>; #pinctrl-cells = <1>; pinctrl-single,register-width = <32>; @@ -1815,6 +1814,19 @@ status = "disabled"; }; + dphy_tx1: phy@4481000 { + compatible = "ti,j721e-dphy"; + reg = <0x00 0x04481000 0x00 0x00001000>; + clocks = <&k3_clks 364 8>, <&k3_clks 364 14>; + clock-names = "psm", "pll_ref"; + #phy-cells = <0>; + power-domains = <&k3_pds 364 TI_SCI_PD_EXCLUSIVE>; + assigned-clocks = <&k3_clks 364 14>; + assigned-clock-parents = <&k3_clks 364 15>; + assigned-clock-rates = <19200000>; + status = "disabled"; + }; + dsi0: dsi@4800000 { compatible = "ti,j721e-dsi"; reg = <0x00 0x04800000 0x00 0x00100000>, @@ -1841,6 +1853,32 @@ }; }; + dsi1: dsi@4900000 { + compatible = "ti,j721e-dsi"; + reg = <0x00 0x04900000 0x00 0x00100000>, + <0x00 0x04720000 0x00 0x00000100>; + clocks = <&k3_clks 155 4>, <&k3_clks 155 1>; + clock-names = "dsi_p_clk", "dsi_sys_clk"; + power-domains = <&k3_pds 155 TI_SCI_PD_EXCLUSIVE>; + interrupts = ; + phys = <&dphy_tx1>; + phy-names = "dphy"; + status = "disabled"; + + dsi1_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + }; + + port@1 { + reg = <1>; + }; + }; + }; + dss: dss@4a00000 { compatible = "ti,j721e-dss"; reg = <0x00 0x04a00000 0x00 0x10000>, /* common_m */ diff --git a/arch/arm64/boot/dts/ti/k3-j721s2-mcu-wakeup.dtsi b/arch/arm64/boot/dts/ti/k3-j721s2-mcu-wakeup.dtsi index 32ee8031cfcb..27b8e7440246 100644 --- a/arch/arm64/boot/dts/ti/k3-j721s2-mcu-wakeup.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j721s2-mcu-wakeup.dtsi @@ -76,7 +76,7 @@ }; wkup_pmx0: pinctrl@4301c000 { - compatible = "pinctrl-single"; + compatible = "ti,j7200-padconf", "pinctrl-single"; /* Proxy 0 addressing */ reg = <0x00 0x4301c000 0x00 0x034>; #pinctrl-cells = <1>; @@ -85,7 +85,7 @@ }; wkup_pmx1: pinctrl@4301c038 { - compatible = "pinctrl-single"; + compatible = "ti,j7200-padconf", "pinctrl-single"; /* Proxy 0 addressing */ reg = <0x00 0x4301c038 0x00 0x02c>; #pinctrl-cells = <1>; @@ -94,7 +94,7 @@ }; wkup_pmx2: pinctrl@4301c068 { - compatible = "pinctrl-single"; + compatible = "ti,j7200-padconf", "pinctrl-single"; /* Proxy 0 addressing */ reg = <0x00 0x4301c068 0x00 0x120>; #pinctrl-cells = <1>; @@ -103,7 +103,7 @@ }; wkup_pmx3: pinctrl@4301c190 { - compatible = "pinctrl-single"; + compatible = "ti,j7200-padconf", "pinctrl-single"; /* Proxy 0 addressing */ reg = <0x00 0x4301c190 0x00 0x004>; #pinctrl-cells = <1>; @@ -113,7 +113,7 @@ /* MCU_TIMERIO pad input CTRLMMR_MCU_TIMER*_CTRL registers */ mcu_timerio_input: pinctrl@40f04200 { - compatible = "pinctrl-single"; + compatible = "ti,j7200-padconf", "pinctrl-single"; reg = <0x00 0x40f04200 0x00 0x28>; #pinctrl-cells = <1>; pinctrl-single,register-width = <32>; @@ -124,7 +124,7 @@ /* MCU_TIMERIO pad output CTRLMMR_MCU_TIMERIO*_CTRL registers */ mcu_timerio_output: pinctrl@40f04280 { - compatible = "pinctrl-single"; + compatible = "ti,j7200-padconf", "pinctrl-single"; reg = <0x00 0x40f04280 0x00 0x28>; #pinctrl-cells = <1>; pinctrl-single,register-width = <32>; diff --git a/arch/arm64/boot/dts/ti/k3-j721s2-ti-ipc-firmware.dtsi b/arch/arm64/boot/dts/ti/k3-j721s2-ti-ipc-firmware.dtsi index ebab0cc580bb..5253d028da09 100644 --- a/arch/arm64/boot/dts/ti/k3-j721s2-ti-ipc-firmware.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j721s2-ti-ipc-firmware.dtsi @@ -190,6 +190,7 @@ mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core0>; memory-region = <&mcu_r5fss0_core0_dma_memory_region>, <&mcu_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -197,6 +198,7 @@ mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core1>; memory-region = <&mcu_r5fss0_core1_dma_memory_region>, <&mcu_r5fss0_core1_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -209,6 +211,7 @@ mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core0>; memory-region = <&main_r5fss0_core0_dma_memory_region>, <&main_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -216,6 +219,7 @@ mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core1>; memory-region = <&main_r5fss0_core1_dma_memory_region>, <&main_r5fss0_core1_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -228,6 +232,7 @@ mboxes = <&mailbox0_cluster2 &mbox_main_r5fss1_core0>; memory-region = <&main_r5fss1_core0_dma_memory_region>, <&main_r5fss1_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -235,6 +240,7 @@ mboxes = <&mailbox0_cluster2 &mbox_main_r5fss1_core1>; memory-region = <&main_r5fss1_core1_dma_memory_region>, <&main_r5fss1_core1_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; diff --git a/arch/arm64/boot/dts/ti/k3-j722s-main.dtsi b/arch/arm64/boot/dts/ti/k3-j722s-main.dtsi index 9ee5d0c8ffd1..ddf20e44f0ea 100644 --- a/arch/arm64/boot/dts/ti/k3-j722s-main.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j722s-main.dtsi @@ -154,6 +154,19 @@ }; }; + main_i2c4: i2c@fe80000 { + compatible = "ti,am64-i2c", "ti,omap4-i2c"; + reg = <0x00 0x0fe80000 0x00 0x100>; + interrupt-parent = <&main_gpio_intr>; + interrupts = <178 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&k3_pds 257 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 257 2>; + clock-names = "fck"; + status = "disabled"; + }; + ti_csi2rx1: ticsi2rx@30122000 { compatible = "ti,j721e-csi2rx-shim"; reg = <0x00 0x30122000 0x00 0x1000>; diff --git a/arch/arm64/boot/dts/ti/k3-j722s-ti-ipc-firmware.dtsi b/arch/arm64/boot/dts/ti/k3-j722s-ti-ipc-firmware.dtsi index cb7cd385a165..a59c3648d805 100644 --- a/arch/arm64/boot/dts/ti/k3-j722s-ti-ipc-firmware.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j722s-ti-ipc-firmware.dtsi @@ -123,6 +123,7 @@ mboxes = <&mailbox0_cluster0 &mbox_wkup_r5_0>; memory-region = <&wkup_r5fss0_core0_dma_memory_region>, <&wkup_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -134,6 +135,7 @@ mboxes = <&mailbox0_cluster1 &mbox_mcu_r5_0>; memory-region = <&mcu_r5fss0_core0_dma_memory_region>, <&mcu_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; @@ -145,6 +147,7 @@ mboxes = <&mailbox0_cluster3 &mbox_main_r5_0>; memory-region = <&main_r5fss0_core0_dma_memory_region>, <&main_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; status = "okay"; }; diff --git a/arch/arm64/boot/dts/ti/k3-j722s.dtsi b/arch/arm64/boot/dts/ti/k3-j722s.dtsi index 059c65ece183..1b36dcf37925 100644 --- a/arch/arm64/boot/dts/ti/k3-j722s.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j722s.dtsi @@ -160,7 +160,8 @@ <0x00 0x0fd80000 0x00 0x0fd80000 0x00 0x00080000>, /* GPU */ <0x00 0x0fd20000 0x00 0x0fd20000 0x00 0x00000100>, /* JPEGENC0_CORE */ <0x00 0x0fd20200 0x00 0x0fd20200 0x00 0x00000200>, /* JPEGENC0_CORE_MMU */ - <0x00 0x20000000 0x00 0x20000000 0x00 0x0a008000>, /* Third peripheral window */ + <0x00 0x0fe00000 0x00 0x0fe00000 0x00 0x000f0400>, /* Third peripheral window */ + <0x00 0x20000000 0x00 0x20000000 0x00 0x0a008000>, /* Fourth peripheral window */ <0x00 0x30040000 0x00 0x30040000 0x00 0x00080000>, /* PRUSS-M */ <0x00 0x301c0000 0x00 0x301c0000 0x00 0x00001000>, /* DPHY-TX */ <0x00 0x30101000 0x00 0x30101000 0x00 0x00080100>, /* CSI window */ diff --git a/arch/arm64/boot/dts/ti/k3-j784s4-j742s2-ti-ipc-firmware-common.dtsi b/arch/arm64/boot/dts/ti/k3-j784s4-j742s2-ti-ipc-firmware-common.dtsi index 455397227d4a..d08fd3ff8a89 100644 --- a/arch/arm64/boot/dts/ti/k3-j784s4-j742s2-ti-ipc-firmware-common.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j784s4-j742s2-ti-ipc-firmware-common.dtsi @@ -262,6 +262,7 @@ mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core0>; memory-region = <&mcu_r5fss0_core0_dma_memory_region>, <&mcu_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; }; &mcu_r5fss0_core1 { @@ -269,6 +270,7 @@ mboxes = <&mailbox0_cluster0 &mbox_mcu_r5fss0_core1>; memory-region = <&mcu_r5fss0_core1_dma_memory_region>, <&mcu_r5fss0_core1_memory_region>; + memory-region-names = "dma", "firmware"; }; &main_r5fss0 { @@ -281,6 +283,7 @@ mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core0>; memory-region = <&main_r5fss0_core0_dma_memory_region>, <&main_r5fss0_core0_memory_region>; + memory-region-names = "dma", "firmware"; }; &main_r5fss0_core1 { @@ -288,6 +291,7 @@ mboxes = <&mailbox0_cluster1 &mbox_main_r5fss0_core1>; memory-region = <&main_r5fss0_core1_dma_memory_region>, <&main_r5fss0_core1_memory_region>; + memory-region-names = "dma", "firmware"; }; &main_r5fss1 { @@ -300,6 +304,7 @@ mboxes = <&mailbox0_cluster2 &mbox_main_r5fss1_core0>; memory-region = <&main_r5fss1_core0_dma_memory_region>, <&main_r5fss1_core0_memory_region>; + memory-region-names = "dma", "firmware"; }; &main_r5fss1_core1 { @@ -307,6 +312,7 @@ mboxes = <&mailbox0_cluster2 &mbox_main_r5fss1_core1>; memory-region = <&main_r5fss1_core1_dma_memory_region>, <&main_r5fss1_core1_memory_region>; + memory-region-names = "dma", "firmware"; }; &main_r5fss2 { @@ -319,6 +325,7 @@ mboxes = <&mailbox0_cluster3 &mbox_main_r5fss2_core0>; memory-region = <&main_r5fss2_core0_dma_memory_region>, <&main_r5fss2_core0_memory_region>; + memory-region-names = "dma", "firmware"; }; &main_r5fss2_core1 { @@ -326,6 +333,7 @@ mboxes = <&mailbox0_cluster3 &mbox_main_r5fss2_core1>; memory-region = <&main_r5fss2_core1_dma_memory_region>, <&main_r5fss2_core1_memory_region>; + memory-region-names = "dma", "firmware"; }; &c71_0 { diff --git a/arch/arm64/boot/dts/ti/k3-pinctrl.h b/arch/arm64/boot/dts/ti/k3-pinctrl.h index dc8e03ae74c8..4491898d8294 100644 --- a/arch/arm64/boot/dts/ti/k3-pinctrl.h +++ b/arch/arm64/boot/dts/ti/k3-pinctrl.h @@ -8,23 +8,23 @@ #ifndef DTS_ARM64_TI_K3_PINCTRL_H #define DTS_ARM64_TI_K3_PINCTRL_H -#define WKUP_LVL_EN_SHIFT (7) -#define WKUP_LVL_POL_SHIFT (8) +#define WKUP_LVL_EN_SHIFT (7) +#define WKUP_LVL_POL_SHIFT (8) +#define DEBOUNCE_SHIFT (11) #define ST_EN_SHIFT (14) +#define FORCE_DS_EN_SHIFT (15) #define PULLUDEN_SHIFT (16) #define PULLTYPESEL_SHIFT (17) #define RXACTIVE_SHIFT (18) -#define DRV_STR_SHIFT (19) -#define ISO_OVERRIDE_EN_SHIFT (22) -#define ISO_BYPASS_EN_SHIFT (23) -#define DEBOUNCE_SHIFT (11) -#define FORCE_DS_EN_SHIFT (15) +#define DRV_STR_SHIFT (19) +#define ISO_OVERRIDE_EN_SHIFT (22) +#define ISO_BYPASS_EN_SHIFT (23) #define DS_EN_SHIFT (24) #define DS_OUT_DIS_SHIFT (25) #define DS_OUT_VAL_SHIFT (26) #define DS_PULLUD_EN_SHIFT (27) #define DS_PULLTYPE_SEL_SHIFT (28) -#define WKUP_EN_SHIFT (29) +#define WKUP_EN_SHIFT (29) /* Schmitt trigger configuration */ #define ST_DISABLE (0 << ST_EN_SHIFT) @@ -39,28 +39,28 @@ #define INPUT_EN (1 << RXACTIVE_SHIFT) #define INPUT_DISABLE (0 << RXACTIVE_SHIFT) -#define DS_PULL_DISABLE (1 << DS_PULLUD_EN_SHIFT) -#define DS_PULL_ENABLE (0 << DS_PULLUD_EN_SHIFT) +#define DS_PULL_DISABLE (1 << DS_PULLUD_EN_SHIFT) +#define DS_PULL_ENABLE (0 << DS_PULLUD_EN_SHIFT) -#define DS_PULL_UP (1 << DS_PULLTYPE_SEL_SHIFT | DS_PULL_ENABLE) -#define DS_PULL_DOWN (0 << DS_PULLTYPE_SEL_SHIFT | DS_PULL_ENABLE) +#define DS_PULL_UP (1 << DS_PULLTYPE_SEL_SHIFT | DS_PULL_ENABLE) +#define DS_PULL_DOWN (0 << DS_PULLTYPE_SEL_SHIFT | DS_PULL_ENABLE) -#define DS_STATE_EN (1 << DS_EN_SHIFT) -#define DS_STATE_DISABLE (0 << DS_EN_SHIFT) +#define DS_STATE_EN (1 << DS_EN_SHIFT) +#define DS_STATE_DISABLE (0 << DS_EN_SHIFT) -#define DS_INPUT_EN (1 << DS_OUT_DIS_SHIFT | DS_STATE_EN) -#define DS_INPUT_DISABLE (0 << DS_OUT_DIS_SHIFT | DS_STATE_EN) +#define DS_INPUT_EN (1 << DS_OUT_DIS_SHIFT | DS_STATE_EN) +#define DS_INPUT_DISABLE (0 << DS_OUT_DIS_SHIFT | DS_STATE_EN) -#define DS_OUT_VALUE_ZERO (0 << DS_OUT_VAL_SHIFT) -#define DS_OUT_VALUE_ONE (1 << DS_OUT_VAL_SHIFT) +#define DS_OUT_VALUE_ZERO (0 << DS_OUT_VAL_SHIFT) +#define DS_OUT_VALUE_ONE (1 << DS_OUT_VAL_SHIFT) /* Configuration to enable wake-up on pin activity */ -#define WKUP_ENABLE (1 << WKUP_EN_SHIFT) -#define WKUP_DISABLE (0 << WKUP_EN_SHIFT) -#define WKUP_ON_LEVEL (1 << WKUP_LVL_EN_SHIFT) -#define WKUP_ON_EDGE (0 << WKUP_LVL_EN_SHIFT) -#define WKUP_LEVEL_LOW (0 << WKUP_LVL_POL_SHIFT) -#define WKUP_LEVEL_HIGH (1 << WKUP_LVL_POL_SHIFT) +#define WKUP_ENABLE (1 << WKUP_EN_SHIFT) +#define WKUP_DISABLE (0 << WKUP_EN_SHIFT) +#define WKUP_ON_LEVEL (1 << WKUP_LVL_EN_SHIFT) +#define WKUP_ON_EDGE (0 << WKUP_LVL_EN_SHIFT) +#define WKUP_LEVEL_LOW (0 << WKUP_LVL_POL_SHIFT) +#define WKUP_LEVEL_HIGH (1 << WKUP_LVL_POL_SHIFT) /* Only these macros are expected be used directly in device tree files */ #define PIN_OUTPUT (INPUT_DISABLE | PULL_DISABLE) @@ -82,14 +82,14 @@ #define PIN_DEBOUNCE_CONF5 (5 << DEBOUNCE_SHIFT) #define PIN_DEBOUNCE_CONF6 (6 << DEBOUNCE_SHIFT) -#define PIN_DRIVE_STRENGTH_NOMINAL (0 << DRV_STR_SHIFT) -#define PIN_DRIVE_STRENGTH_SLOW (1 << DRV_STR_SHIFT) -#define PIN_DRIVE_STRENGTH_FAST (2 << DRV_STR_SHIFT) +#define PIN_DRIVE_STRENGTH_NOMINAL (0 << DRV_STR_SHIFT) +#define PIN_DRIVE_STRENGTH_SLOW (1 << DRV_STR_SHIFT) +#define PIN_DRIVE_STRENGTH_FAST (2 << DRV_STR_SHIFT) #define PIN_DS_FORCE_DISABLE (0 << FORCE_DS_EN_SHIFT) #define PIN_DS_FORCE_ENABLE (1 << FORCE_DS_EN_SHIFT) -#define PIN_DS_ISO_OVERRIDE_DISABLE (0 << ISO_OVERRIDE_EN_SHIFT) -#define PIN_DS_ISO_OVERRIDE_ENABLE (1 << ISO_OVERRIDE_EN_SHIFT) +#define PIN_DS_ISO_OVERRIDE_DISABLE (0 << ISO_OVERRIDE_EN_SHIFT) +#define PIN_DS_ISO_OVERRIDE_ENABLE (1 << ISO_OVERRIDE_EN_SHIFT) #define PIN_DS_OUT_ENABLE (0 << DS_OUT_DIS_SHIFT) #define PIN_DS_OUT_DISABLE (1 << DS_OUT_DIS_SHIFT) #define PIN_DS_OUT_VALUE_ZERO (0 << DS_OUT_VAL_SHIFT) @@ -98,18 +98,18 @@ #define PIN_DS_PULLUD_DISABLE (1 << DS_PULLUD_EN_SHIFT) #define PIN_DS_PULL_DOWN (0 << DS_PULLTYPE_SEL_SHIFT) #define PIN_DS_PULL_UP (1 << DS_PULLTYPE_SEL_SHIFT) -#define PIN_DS_ISO_BYPASS (1 << ISO_BYPASS_EN_SHIFT) -#define PIN_DS_ISO_BYPASS_DISABLE (0 << ISO_BYPASS_EN_SHIFT) +#define PIN_DS_ISO_BYPASS (1 << ISO_BYPASS_EN_SHIFT) +#define PIN_DS_ISO_BYPASS_DISABLE (0 << ISO_BYPASS_EN_SHIFT) -#define PIN_DS_OUTPUT_LOW (DS_INPUT_DISABLE | DS_OUT_VALUE_ZERO) -#define PIN_DS_OUTPUT_HIGH (DS_INPUT_DISABLE | DS_OUT_VALUE_ONE) -#define PIN_DS_INPUT (DS_INPUT_EN | DS_PULL_DISABLE) -#define PIN_DS_INPUT_PULLUP (DS_INPUT_EN | DS_PULL_UP) -#define PIN_DS_INPUT_PULLDOWN (DS_INPUT_EN | DS_PULL_DOWN) +#define PIN_DS_OUTPUT_LOW (DS_INPUT_DISABLE | DS_OUT_VALUE_ZERO) +#define PIN_DS_OUTPUT_HIGH (DS_INPUT_DISABLE | DS_OUT_VALUE_ONE) +#define PIN_DS_INPUT (DS_INPUT_EN | DS_PULL_DISABLE) +#define PIN_DS_INPUT_PULLUP (DS_INPUT_EN | DS_PULL_UP) +#define PIN_DS_INPUT_PULLDOWN (DS_INPUT_EN | DS_PULL_DOWN) -#define PIN_WKUP_EN_LEVEL_LOW (WKUP_ENABLE | WKUP_ON_LEVEL | WKUP_LEVEL_LOW) -#define PIN_WKUP_EN_LEVEL_HIGH (WKUP_ENABLE | WKUP_ON_LEVEL | WKUP_LEVEL_HIGH) -#define PIN_WKUP_EN (WKUP_ENABLE | WKUP_ON_EDGE) +#define PIN_WKUP_EN_LEVEL_LOW (WKUP_ENABLE | WKUP_ON_LEVEL | WKUP_LEVEL_LOW) +#define PIN_WKUP_EN_LEVEL_HIGH (WKUP_ENABLE | WKUP_ON_LEVEL | WKUP_LEVEL_HIGH) +#define PIN_WKUP_EN (WKUP_ENABLE | WKUP_ON_EDGE) /* Default mux configuration for gpio-ranges to use with pinctrl */ #define PIN_GPIO_RANGE_IOPAD (PIN_INPUT | 7) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 570624e1d0ee..d905a0777f93 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -57,6 +57,7 @@ CONFIG_ARCH_HISI=y CONFIG_ARCH_KEEMBAY=y CONFIG_ARCH_MEDIATEK=y CONFIG_ARCH_MESON=y +CONFIG_ARCH_MICROCHIP=y CONFIG_ARCH_MVEBU=y CONFIG_ARCH_NXP=y CONFIG_ARCH_LAYERSCAPE=y @@ -258,6 +259,7 @@ CONFIG_PCIE_LAYERSCAPE_GEN4=y CONFIG_PCI_ENDPOINT=y CONFIG_PCI_ENDPOINT_CONFIGFS=y CONFIG_PCI_EPF_TEST=m +CONFIG_PCI_PWRCTRL_GENERIC=m CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_FW_LOADER_USER_HELPER=y @@ -416,10 +418,12 @@ CONFIG_MARVELL_88Q2XXX_PHY=y CONFIG_MICREL_PHY=y CONFIG_MICROSEMI_PHY=y CONFIG_AT803X_PHY=y +CONFIG_QCA808X_PHY=m CONFIG_REALTEK_PHY=y CONFIG_ROCKCHIP_PHY=y CONFIG_DP83867_PHY=y CONFIG_DP83869_PHY=m +CONFIG_DP83TG720_PHY=m CONFIG_DP83TD510_PHY=y CONFIG_VITESSE_PHY=y CONFIG_XILINX_GMII2RGMII=m @@ -482,6 +486,7 @@ CONFIG_TOUCHSCREEN_APPLE_Z2=m CONFIG_TOUCHSCREEN_ATMEL_MXT=m CONFIG_TOUCHSCREEN_GOODIX=m CONFIG_TOUCHSCREEN_GOODIX_BERLIN_SPI=m +CONFIG_TOUCHSCREEN_ILI210X=m CONFIG_TOUCHSCREEN_ELAN=m CONFIG_TOUCHSCREEN_EDT_FT5X06=m CONFIG_TOUCHSCREEN_HIMAX_HX83112B=m @@ -647,7 +652,10 @@ CONFIG_PINCTRL_IMX91=y CONFIG_PINCTRL_IMX93=y CONFIG_PINCTRL_IMX_SCMI=y CONFIG_PINCTRL_MSM=y +CONFIG_PINCTRL_ELIZA=y +CONFIG_PINCTRL_GLYMUR=y CONFIG_PINCTRL_IPQ5018=y +CONFIG_PINCTRL_IPQ5210=y CONFIG_PINCTRL_IPQ5332=y CONFIG_PINCTRL_IPQ5424=y CONFIG_PINCTRL_IPQ8074=y @@ -692,6 +700,7 @@ CONFIG_PINCTRL_SM8750=y CONFIG_PINCTRL_X1E80100=y CONFIG_PINCTRL_QCOM_SPMI_PMIC=y CONFIG_PINCTRL_LPASS_LPI=m +CONFIG_PINCTRL_MILOS_LPASS_LPI=m CONFIG_PINCTRL_SC7280_LPASS_LPI=m CONFIG_PINCTRL_SM6115_LPASS_LPI=m CONFIG_PINCTRL_SM8250_LPASS_LPI=m @@ -728,6 +737,8 @@ CONFIG_GPIO_MACSMC=m CONFIG_GPIO_MAX77620=y CONFIG_GPIO_SL28CPLD=m CONFIG_GPIO_AGGREGATOR=m +CONFIG_POWER_RESET_GPIO=y +CONFIG_POWER_RESET_GPIO_RESTART=y CONFIG_POWER_RESET_MACSMC=m CONFIG_POWER_RESET_MSM=y CONFIG_POWER_RESET_QCOM_PON=m @@ -754,8 +765,8 @@ CONFIG_SENSORS_LM75=m CONFIG_SENSORS_LM90=m CONFIG_SENSORS_PWM_FAN=m CONFIG_SENSORS_RASPBERRYPI_HWMON=m -CONFIG_SENSORS_SA67MCU=m CONFIG_SENSORS_SL28CPLD=m +CONFIG_SENSORS_AMC6821=m CONFIG_SENSORS_INA2XX=m CONFIG_SENSORS_INA3221=m CONFIG_SENSORS_TMP102=m @@ -943,6 +954,7 @@ CONFIG_VIDEO_IMX219=m CONFIG_VIDEO_IMX412=m CONFIG_VIDEO_OV5640=m CONFIG_VIDEO_OV5645=m +CONFIG_VIDEO_S5KJN1=m CONFIG_DRM=m CONFIG_DRM_I2C_NXP_TDA998X=m CONFIG_DRM_HDLCD=m @@ -977,6 +989,8 @@ CONFIG_DRM_SUN8I_DW_HDMI=m CONFIG_DRM_SUN8I_MIXER=m CONFIG_DRM_MSM=m CONFIG_DRM_TEGRA=m +CONFIG_DRM_STM=m +CONFIG_DRM_STM_LVDS=m CONFIG_DRM_PANEL_BOE_TV101WUM_NL6=m CONFIG_DRM_PANEL_LVDS=m CONFIG_DRM_PANEL_SIMPLE=m @@ -1004,6 +1018,7 @@ CONFIG_DRM_ITE_IT6263=m CONFIG_DRM_LONTIUM_LT8912B=m CONFIG_DRM_LONTIUM_LT9611=m CONFIG_DRM_LONTIUM_LT9611UXC=m +CONFIG_DRM_LONTIUM_LT8713SX=m CONFIG_DRM_ITE_IT66121=m CONFIG_DRM_NWL_MIPI_DSI=m CONFIG_DRM_PARADE_PS8640=m @@ -1053,6 +1068,7 @@ CONFIG_BACKLIGHT_PWM=m CONFIG_BACKLIGHT_APPLE_DWI=m CONFIG_BACKLIGHT_QCOM_WLED=m CONFIG_BACKLIGHT_LP855X=m +CONFIG_BACKLIGHT_GPIO=m CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set @@ -1144,6 +1160,7 @@ CONFIG_SND_SOC_ES7241=m CONFIG_SND_SOC_ES8316=m CONFIG_SND_SOC_ES8328_I2C=m CONFIG_SND_SOC_GTM601=m +CONFIG_SND_SOC_MAX98090=m CONFIG_SND_SOC_MSM8916_WCD_ANALOG=m CONFIG_SND_SOC_MSM8916_WCD_DIGITAL=m CONFIG_SND_SOC_PCM3168A_I2C=m @@ -1159,6 +1176,7 @@ CONFIG_SND_SOC_TLV320AIC32X4_I2C=m CONFIG_SND_SOC_TLV320AIC3X_I2C=m CONFIG_SND_SOC_WCD9335=m CONFIG_SND_SOC_WCD934X=m +CONFIG_SND_SOC_WCD937X_SDW=m CONFIG_SND_SOC_WCD939X_SDW=m CONFIG_SND_SOC_WM8524=m CONFIG_SND_SOC_WM8904=m @@ -1454,8 +1472,18 @@ CONFIG_COMMON_CLK_MT8192_SCP_ADSP=y CONFIG_COMMON_CLK_MT8192_VDECSYS=y CONFIG_COMMON_CLK_MT8192_VENCSYS=y CONFIG_COMMON_CLK_QCOM=y +CONFIG_CLK_ELIZA_DISPCC=m +CONFIG_CLK_ELIZA_GCC=y +CONFIG_CLK_ELIZA_TCSRCC=m +CONFIG_CLK_GLYMUR_DISPCC=m +CONFIG_CLK_GLYMUR_GCC=y +CONFIG_CLK_GLYMUR_TCSRCC=m +CONFIG_CLK_KAANAPALI_CAMCC=m +CONFIG_CLK_KAANAPALI_DISPCC=m CONFIG_CLK_KAANAPALI_GCC=y +CONFIG_CLK_KAANAPALI_GPUCC=m CONFIG_CLK_KAANAPALI_TCSRCC=m +CONFIG_CLK_KAANAPALI_VIDEOCC=m CONFIG_CLK_X1E80100_CAMCC=m CONFIG_CLK_X1E80100_DISPCC=m CONFIG_CLK_X1E80100_GCC=y @@ -1472,6 +1500,7 @@ CONFIG_IPQ_APSS_6018=y CONFIG_IPQ_APSS_5018=y CONFIG_IPQ_CMN_PLL=m CONFIG_IPQ_GCC_5018=y +CONFIG_IPQ_GCC_5210=y CONFIG_IPQ_GCC_5332=y CONFIG_IPQ_GCC_5424=y CONFIG_IPQ_GCC_6018=y @@ -1525,6 +1554,7 @@ CONFIG_SM_CAMCC_MILOS=m CONFIG_SM_CAMCC_8250=m CONFIG_SM_CAMCC_8550=m CONFIG_SM_CAMCC_8650=m +CONFIG_SM_CAMCC_8750=m CONFIG_SM_DISPCC_6115=m CONFIG_SM_DISPCC_8250=y CONFIG_SM_DISPCC_6350=m @@ -1550,6 +1580,7 @@ CONFIG_SM_GPUCC_8350=m CONFIG_SM_GPUCC_8450=m CONFIG_SM_GPUCC_8550=m CONFIG_SM_GPUCC_8650=m +CONFIG_SM_GPUCC_8750=m CONFIG_SM_TCSRCC_8550=y CONFIG_SM_TCSRCC_8650=y CONFIG_SM_TCSRCC_8750=m @@ -1558,6 +1589,7 @@ CONFIG_SM_VIDEOCC_6350=m CONFIG_SM_VIDEOCC_MILOS=m CONFIG_SM_VIDEOCC_8250=y CONFIG_SM_VIDEOCC_8550=m +CONFIG_SM_VIDEOCC_8750=m CONFIG_QCOM_HFPLL=y CONFIG_CLK_GFM_LPASS_SM8250=m CONFIG_SM_VIDEOCC_8450=m @@ -1641,13 +1673,6 @@ CONFIG_QCOM_APR=m CONFIG_QCOM_ICC_BWMON=m CONFIG_QCOM_PBS=m CONFIG_ROCKCHIP_IODOMAIN=y -CONFIG_ARCH_TEGRA_132_SOC=y -CONFIG_ARCH_TEGRA_210_SOC=y -CONFIG_ARCH_TEGRA_186_SOC=y -CONFIG_ARCH_TEGRA_194_SOC=y -CONFIG_ARCH_TEGRA_234_SOC=y -CONFIG_ARCH_TEGRA_241_SOC=y -CONFIG_ARCH_TEGRA_264_SOC=y CONFIG_TI_PRUSS=m CONFIG_OWL_PM_DOMAINS=y CONFIG_RASPBERRYPI_POWER=y @@ -1834,6 +1859,7 @@ CONFIG_FPGA_MGR_ZYNQMP_FPGA=m CONFIG_FPGA_MGR_VERSAL_FPGA=m CONFIG_TEE=y CONFIG_OPTEE=y +CONFIG_QCOMTEE=m CONFIG_MUX_GPIO=m CONFIG_MUX_MMIO=y CONFIG_SLIMBUS=m @@ -1846,6 +1872,8 @@ CONFIG_INTERCONNECT_IMX8MN=m CONFIG_INTERCONNECT_IMX8MQ=m CONFIG_INTERCONNECT_IMX8MP=y CONFIG_INTERCONNECT_QCOM=y +CONFIG_INTERCONNECT_QCOM_ELIZA=y +CONFIG_INTERCONNECT_QCOM_GLYMUR=y CONFIG_INTERCONNECT_QCOM_KAANAPALI=y CONFIG_INTERCONNECT_QCOM_MSM8916=m CONFIG_INTERCONNECT_QCOM_MSM8953=y diff --git a/arch/arm64/include/asm/Kbuild b/arch/arm64/include/asm/Kbuild index d2ff8f6c3231..31441790b808 100644 --- a/arch/arm64/include/asm/Kbuild +++ b/arch/arm64/include/asm/Kbuild @@ -17,4 +17,5 @@ generic-y += parport.h generic-y += user.h generated-y += cpucap-defs.h +generated-y += kernel-hwcap.h generated-y += sysreg-defs.h diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index 9d7c9ae5ac96..effae53e9739 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -371,14 +371,13 @@ alternative_endif * [start, end) with dcache line size explicitly provided. * * op: operation passed to dc instruction - * domain: domain used in dsb instruction * start: starting virtual address of the region * end: end virtual address of the region * linesz: dcache line size * fixup: optional label to branch to on user fault * Corrupts: start, end, tmp */ - .macro dcache_by_myline_op op, domain, start, end, linesz, tmp, fixup + .macro dcache_by_myline_op_nosync op, start, end, linesz, tmp, fixup sub \tmp, \linesz, #1 bic \start, \start, \tmp alternative_if ARM64_WORKAROUND_4311569 @@ -412,14 +411,28 @@ alternative_if ARM64_WORKAROUND_4311569 cbnz \start, .Ldcache_op\@ .endif alternative_else_nop_endif - dsb \domain _cond_uaccess_extable .Ldcache_op\@, \fixup .endm /* * Macro to perform a data cache maintenance for the interval - * [start, end) + * [start, end) without waiting for completion + * + * op: operation passed to dc instruction + * start: starting virtual address of the region + * end: end virtual address of the region + * fixup: optional label to branch to on user fault + * Corrupts: start, end, tmp1, tmp2 + */ + .macro dcache_by_line_op_nosync op, start, end, tmp1, tmp2, fixup + dcache_line_size \tmp1, \tmp2 + dcache_by_myline_op_nosync \op, \start, \end, \tmp1, \tmp2, \fixup + .endm + +/* + * Macro to perform a data cache maintenance for the interval + * [start, end) and wait for completion * * op: operation passed to dc instruction * domain: domain used in dsb instruction @@ -429,8 +442,8 @@ alternative_else_nop_endif * Corrupts: start, end, tmp1, tmp2 */ .macro dcache_by_line_op op, domain, start, end, tmp1, tmp2, fixup - dcache_line_size \tmp1, \tmp2 - dcache_by_myline_op \op, \domain, \start, \end, \tmp1, \tmp2, \fixup + dcache_by_line_op_nosync \op, \start, \end, \tmp1, \tmp2, \fixup + dsb \domain .endm /* diff --git a/arch/arm64/include/asm/cache.h b/arch/arm64/include/asm/cache.h index dd2c8586a725..10a7ffadee3d 100644 --- a/arch/arm64/include/asm/cache.h +++ b/arch/arm64/include/asm/cache.h @@ -87,6 +87,11 @@ int cache_line_size(void); #define dma_get_cache_alignment cache_line_size +static inline void arch_sync_dma_flush(void) +{ + dsb(sy); +} + /* Compress a u64 MPIDR value into 32 bits. */ static inline u64 arch_compact_of_hwid(u64 id) { diff --git a/arch/arm64/include/asm/cacheflush.h b/arch/arm64/include/asm/cacheflush.h index 28ab96e808ef..382b4ac3734d 100644 --- a/arch/arm64/include/asm/cacheflush.h +++ b/arch/arm64/include/asm/cacheflush.h @@ -74,6 +74,8 @@ extern void icache_inval_pou(unsigned long start, unsigned long end); extern void dcache_clean_inval_poc(unsigned long start, unsigned long end); extern void dcache_inval_poc(unsigned long start, unsigned long end); extern void dcache_clean_poc(unsigned long start, unsigned long end); +extern void dcache_inval_poc_nosync(unsigned long start, unsigned long end); +extern void dcache_clean_poc_nosync(unsigned long start, unsigned long end); extern void dcache_clean_pop(unsigned long start, unsigned long end); extern void dcache_clean_pou(unsigned long start, unsigned long end); extern long caches_clean_inval_user_pou(unsigned long start, unsigned long end); diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h index 6e3da333442e..d0d3cdd5763c 100644 --- a/arch/arm64/include/asm/cpucaps.h +++ b/arch/arm64/include/asm/cpucaps.h @@ -64,6 +64,8 @@ cpucap_is_possible(const unsigned int cap) return IS_ENABLED(CONFIG_ARM64_WORKAROUND_REPEAT_TLBI); case ARM64_WORKAROUND_SPECULATIVE_SSBS: return IS_ENABLED(CONFIG_ARM64_ERRATUM_3194386); + case ARM64_WORKAROUND_4193714: + return IS_ENABLED(CONFIG_ARM64_ERRATUM_4193714); case ARM64_MPAM: /* * KVM MPAM support doesn't rely on the host kernel supporting MPAM. diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index 08860d482e60..7b518e81dd15 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -98,6 +98,7 @@ #define ARM_CPU_PART_CORTEX_A725 0xD87 #define ARM_CPU_PART_CORTEX_A720AE 0xD89 #define ARM_CPU_PART_NEOVERSE_N3 0xD8E +#define ARM_CPU_PART_C1_PRO 0xD8B #define APM_CPU_PART_XGENE 0x000 #define APM_CPU_VAR_POTENZA 0x00 @@ -189,6 +190,7 @@ #define MIDR_CORTEX_A725 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A725) #define MIDR_CORTEX_A720AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A720AE) #define MIDR_NEOVERSE_N3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N3) +#define MIDR_C1_PRO MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_PRO) #define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX) #define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX) #define MIDR_THUNDERX_83XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_83XX) diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h index 4d15071a4f3f..587507a9980e 100644 --- a/arch/arm64/include/asm/el2_setup.h +++ b/arch/arm64/include/asm/el2_setup.h @@ -50,7 +50,6 @@ * effectively VHE-only or not. */ msr_hcr_el2 x0 // Setup HCR_EL2 as nVHE - isb mov x1, #1 // Write something to FAR_EL1 msr far_el1, x1 isb @@ -64,7 +63,6 @@ .LnE2H0_\@: orr x0, x0, #HCR_E2H msr_hcr_el2 x0 - isb .LnVHE_\@: .endm @@ -248,6 +246,8 @@ ICH_HFGWTR_EL2_ICC_CR0_EL1 | \ ICH_HFGWTR_EL2_ICC_APR_EL1) msr_s SYS_ICH_HFGWTR_EL2, x0 // Disable reg write traps + mov x0, #(ICH_VCTLR_EL2_En) + msr_s SYS_ICH_VCTLR_EL2, x0 // Enable vHPPI selection .Lskip_gicv5_\@: .endm diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h index 1d2e33559bd5..d9d00b45ab11 100644 --- a/arch/arm64/include/asm/fpsimd.h +++ b/arch/arm64/include/asm/fpsimd.h @@ -428,6 +428,24 @@ static inline size_t sme_state_size(struct task_struct const *task) return __sme_state_size(task_get_sme_vl(task)); } +void sme_enable_dvmsync(void); +void sme_set_active(void); +void sme_clear_active(void); + +static inline void sme_enter_from_user_mode(void) +{ + if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714) && + test_thread_flag(TIF_SME)) + sme_clear_active(); +} + +static inline void sme_exit_to_user_mode(void) +{ + if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714) && + test_thread_flag(TIF_SME)) + sme_set_active(); +} + #else static inline void sme_user_disable(void) { BUILD_BUG(); } @@ -456,6 +474,9 @@ static inline size_t sme_state_size(struct task_struct const *task) return 0; } +static inline void sme_enter_from_user_mode(void) { } +static inline void sme_exit_to_user_mode(void) { } + #endif /* ! CONFIG_ARM64_SME */ /* For use by EFI runtime services calls only */ diff --git a/arch/arm64/include/asm/irqflags.h b/arch/arm64/include/asm/irqflags.h index d4d7451c2c12..a8cb5a5c93b7 100644 --- a/arch/arm64/include/asm/irqflags.h +++ b/arch/arm64/include/asm/irqflags.h @@ -40,7 +40,7 @@ static __always_inline void __pmr_local_irq_enable(void) barrier(); } -static inline void arch_local_irq_enable(void) +static __always_inline void arch_local_irq_enable(void) { if (system_uses_irq_prio_masking()) { __pmr_local_irq_enable(); @@ -68,7 +68,7 @@ static __always_inline void __pmr_local_irq_disable(void) barrier(); } -static inline void arch_local_irq_disable(void) +static __always_inline void arch_local_irq_disable(void) { if (system_uses_irq_prio_masking()) { __pmr_local_irq_disable(); @@ -90,7 +90,7 @@ static __always_inline unsigned long __pmr_local_save_flags(void) /* * Save the current interrupt enable state. */ -static inline unsigned long arch_local_save_flags(void) +static __always_inline unsigned long arch_local_save_flags(void) { if (system_uses_irq_prio_masking()) { return __pmr_local_save_flags(); @@ -109,7 +109,7 @@ static __always_inline bool __pmr_irqs_disabled_flags(unsigned long flags) return flags != GIC_PRIO_IRQON; } -static inline bool arch_irqs_disabled_flags(unsigned long flags) +static __always_inline bool arch_irqs_disabled_flags(unsigned long flags) { if (system_uses_irq_prio_masking()) { return __pmr_irqs_disabled_flags(flags); @@ -128,7 +128,7 @@ static __always_inline bool __pmr_irqs_disabled(void) return __pmr_irqs_disabled_flags(__pmr_local_save_flags()); } -static inline bool arch_irqs_disabled(void) +static __always_inline bool arch_irqs_disabled(void) { if (system_uses_irq_prio_masking()) { return __pmr_irqs_disabled(); @@ -160,7 +160,7 @@ static __always_inline unsigned long __pmr_local_irq_save(void) return flags; } -static inline unsigned long arch_local_irq_save(void) +static __always_inline unsigned long arch_local_irq_save(void) { if (system_uses_irq_prio_masking()) { return __pmr_local_irq_save(); @@ -187,7 +187,7 @@ static __always_inline void __pmr_local_irq_restore(unsigned long flags) /* * restore saved IRQ state */ -static inline void arch_local_irq_restore(unsigned long flags) +static __always_inline void arch_local_irq_restore(unsigned long flags) { if (system_uses_irq_prio_masking()) { __pmr_local_irq_restore(flags); diff --git a/arch/arm64/include/asm/kernel-pgtable.h b/arch/arm64/include/asm/kernel-pgtable.h index 74a4f738c5f5..229ee7976f69 100644 --- a/arch/arm64/include/asm/kernel-pgtable.h +++ b/arch/arm64/include/asm/kernel-pgtable.h @@ -68,7 +68,12 @@ #define KERNEL_SEGMENT_COUNT 5 #if SWAPPER_BLOCK_SIZE > SEGMENT_ALIGN -#define EARLY_SEGMENT_EXTRA_PAGES (KERNEL_SEGMENT_COUNT + 1) +/* + * KERNEL_SEGMENT_COUNT counts the permanent kernel VMAs. The early mapping + * has one additional split, [_text, _stext). Reserve one more page for the + * SWAPPER_BLOCK_SIZE-unaligned boundaries. + */ +#define EARLY_SEGMENT_EXTRA_PAGES (KERNEL_SEGMENT_COUNT + 2) /* * The initial ID map consists of the kernel image, mapped as two separate * segments, and may appear misaligned wrt the swapper block size. This means diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index a1ad12c72ebf..043495f7fc78 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -50,8 +50,11 @@ #include +#define MARKER(m) \ + m, __after_##m = m - 1 + enum __kvm_host_smccc_func { - /* Hypercalls available only prior to pKVM finalisation */ + /* Hypercalls that are unavailable once pKVM has finalised. */ /* __KVM_HOST_SMCCC_FUNC___kvm_hyp_init */ __KVM_HOST_SMCCC_FUNC___pkvm_init = __KVM_HOST_SMCCC_FUNC___kvm_hyp_init + 1, __KVM_HOST_SMCCC_FUNC___pkvm_create_private_mapping, @@ -59,17 +62,12 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___kvm_enable_ssbs, __KVM_HOST_SMCCC_FUNC___vgic_v3_init_lrs, __KVM_HOST_SMCCC_FUNC___vgic_v3_get_gic_config, + + MARKER(__KVM_HOST_SMCCC_FUNC_MIN_PKVM), + __KVM_HOST_SMCCC_FUNC___pkvm_prot_finalize, - /* Hypercalls available after pKVM finalisation */ - __KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp, - __KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_hyp, - __KVM_HOST_SMCCC_FUNC___pkvm_host_share_guest, - __KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_guest, - __KVM_HOST_SMCCC_FUNC___pkvm_host_relax_perms_guest, - __KVM_HOST_SMCCC_FUNC___pkvm_host_wrprotect_guest, - __KVM_HOST_SMCCC_FUNC___pkvm_host_test_clear_young_guest, - __KVM_HOST_SMCCC_FUNC___pkvm_host_mkyoung_guest, + /* Hypercalls that are always available and common to [nh]VHE/pKVM. */ __KVM_HOST_SMCCC_FUNC___kvm_adjust_pc, __KVM_HOST_SMCCC_FUNC___kvm_vcpu_run, __KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context, @@ -79,16 +77,45 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_range, __KVM_HOST_SMCCC_FUNC___kvm_flush_cpu_context, __KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff, + __KVM_HOST_SMCCC_FUNC___tracing_load, + __KVM_HOST_SMCCC_FUNC___tracing_unload, + __KVM_HOST_SMCCC_FUNC___tracing_enable, + __KVM_HOST_SMCCC_FUNC___tracing_swap_reader, + __KVM_HOST_SMCCC_FUNC___tracing_update_clock, + __KVM_HOST_SMCCC_FUNC___tracing_reset, + __KVM_HOST_SMCCC_FUNC___tracing_enable_event, + __KVM_HOST_SMCCC_FUNC___tracing_write_event, __KVM_HOST_SMCCC_FUNC___vgic_v3_save_aprs, __KVM_HOST_SMCCC_FUNC___vgic_v3_restore_vmcr_aprs, + __KVM_HOST_SMCCC_FUNC___vgic_v5_save_apr, + __KVM_HOST_SMCCC_FUNC___vgic_v5_restore_vmcr_apr, + + MARKER(__KVM_HOST_SMCCC_FUNC_PKVM_ONLY), + + /* Hypercalls that are available only when pKVM has finalised. */ + __KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp, + __KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_hyp, + __KVM_HOST_SMCCC_FUNC___pkvm_host_donate_guest, + __KVM_HOST_SMCCC_FUNC___pkvm_host_share_guest, + __KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_guest, + __KVM_HOST_SMCCC_FUNC___pkvm_host_relax_perms_guest, + __KVM_HOST_SMCCC_FUNC___pkvm_host_wrprotect_guest, + __KVM_HOST_SMCCC_FUNC___pkvm_host_test_clear_young_guest, + __KVM_HOST_SMCCC_FUNC___pkvm_host_mkyoung_guest, __KVM_HOST_SMCCC_FUNC___pkvm_reserve_vm, __KVM_HOST_SMCCC_FUNC___pkvm_unreserve_vm, __KVM_HOST_SMCCC_FUNC___pkvm_init_vm, __KVM_HOST_SMCCC_FUNC___pkvm_init_vcpu, - __KVM_HOST_SMCCC_FUNC___pkvm_teardown_vm, + __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_in_poison_fault, + __KVM_HOST_SMCCC_FUNC___pkvm_force_reclaim_guest_page, + __KVM_HOST_SMCCC_FUNC___pkvm_reclaim_dying_guest_page, + __KVM_HOST_SMCCC_FUNC___pkvm_start_teardown_vm, + __KVM_HOST_SMCCC_FUNC___pkvm_finalize_teardown_vm, __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load, __KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put, __KVM_HOST_SMCCC_FUNC___pkvm_tlb_flush_vmid, + + MARKER(__KVM_HOST_SMCCC_FUNC_MAX) }; #define DECLARE_KVM_VHE_SYM(sym) extern char sym[] @@ -291,7 +318,8 @@ asmlinkage void __noreturn hyp_panic_bad_stack(void); asmlinkage void kvm_unexpected_el2_exception(void); struct kvm_cpu_context; void handle_trap(struct kvm_cpu_context *host_ctxt); -asmlinkage void __noreturn __kvm_host_psci_cpu_entry(bool is_cpu_on); +asmlinkage void __noreturn __kvm_host_psci_cpu_on_entry(void); +asmlinkage void __noreturn __kvm_host_psci_cpu_resume_entry(void); void __noreturn __pkvm_init_finalise(void); void kvm_nvhe_prepare_backtrace(unsigned long fp, unsigned long pc); void kvm_patch_vector_branch(struct alt_instr *alt, diff --git a/arch/arm64/include/asm/kvm_define_hypevents.h b/arch/arm64/include/asm/kvm_define_hypevents.h new file mode 100644 index 000000000000..77d6790252a6 --- /dev/null +++ b/arch/arm64/include/asm/kvm_define_hypevents.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#define REMOTE_EVENT_INCLUDE_FILE arch/arm64/include/asm/kvm_hypevents.h + +#define REMOTE_EVENT_SECTION "_hyp_events" + +#define HE_STRUCT(__args) __args +#define HE_PRINTK(__args...) __args +#define he_field re_field + +#define HYP_EVENT(__name, __proto, __struct, __assign, __printk) \ + REMOTE_EVENT(__name, 0, RE_STRUCT(__struct), RE_PRINTK(__printk)) + +#define HYP_EVENT_MULTI_READ +#include +#undef HYP_EVENT_MULTI_READ diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 70cb9cfd760a..65eead8362e0 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -217,6 +217,10 @@ struct kvm_s2_mmu { */ bool nested_stage2_enabled; +#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS + struct dentry *shadow_pt_debugfs_dentry; +#endif + /* * true when this MMU needs to be unmapped before being used for a new * purpose. @@ -247,7 +251,7 @@ struct kvm_smccc_features { unsigned long vendor_hyp_bmap_2; /* Function numbers 64-127 */ }; -typedef unsigned int pkvm_handle_t; +typedef u16 pkvm_handle_t; struct kvm_protected_vm { pkvm_handle_t handle; @@ -255,6 +259,13 @@ struct kvm_protected_vm { struct kvm_hyp_memcache stage2_teardown_mc; bool is_protected; bool is_created; + + /* + * True when the guest is being torn down. When in this state, the + * guest's vCPUs can't be loaded anymore, but its pages can be + * reclaimed by the host. + */ + bool is_dying; }; struct kvm_mpidr_data { @@ -287,6 +298,9 @@ enum fgt_group_id { HDFGRTR2_GROUP, HDFGWTR2_GROUP = HDFGRTR2_GROUP, HFGITR2_GROUP, + ICH_HFGRTR_GROUP, + ICH_HFGWTR_GROUP = ICH_HFGRTR_GROUP, + ICH_HFGITR_GROUP, /* Must be last */ __NR_FGT_GROUP_IDS__ @@ -405,6 +419,11 @@ struct kvm_arch { * the associated pKVM instance in the hypervisor. */ struct kvm_protected_vm pkvm; + +#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS + /* Nested virtualization info */ + struct dentry *debugfs_nv_dentry; +#endif }; struct kvm_vcpu_fault_info { @@ -431,9 +450,6 @@ struct kvm_vcpu_fault_info { r = __VNCR_START__ + ((VNCR_ ## r) / 8), \ __after_##r = __MAX__(__before_##r - 1, r) -#define MARKER(m) \ - m, __after_##m = m - 1 - enum vcpu_sysreg { __INVALID_SYSREG__, /* 0 is reserved as an invalid value */ MPIDR_EL1, /* MultiProcessor Affinity Register */ @@ -620,6 +636,10 @@ enum vcpu_sysreg { VNCR(ICH_HCR_EL2), VNCR(ICH_VMCR_EL2), + VNCR(ICH_HFGRTR_EL2), + VNCR(ICH_HFGWTR_EL2), + VNCR(ICH_HFGITR_EL2), + NR_SYS_REGS /* Nothing after this line! */ }; @@ -675,6 +695,9 @@ extern struct fgt_masks hfgwtr2_masks; extern struct fgt_masks hfgitr2_masks; extern struct fgt_masks hdfgrtr2_masks; extern struct fgt_masks hdfgwtr2_masks; +extern struct fgt_masks ich_hfgrtr_masks; +extern struct fgt_masks ich_hfgwtr_masks; +extern struct fgt_masks ich_hfgitr_masks; extern struct fgt_masks kvm_nvhe_sym(hfgrtr_masks); extern struct fgt_masks kvm_nvhe_sym(hfgwtr_masks); @@ -687,6 +710,9 @@ extern struct fgt_masks kvm_nvhe_sym(hfgwtr2_masks); extern struct fgt_masks kvm_nvhe_sym(hfgitr2_masks); extern struct fgt_masks kvm_nvhe_sym(hdfgrtr2_masks); extern struct fgt_masks kvm_nvhe_sym(hdfgwtr2_masks); +extern struct fgt_masks kvm_nvhe_sym(ich_hfgrtr_masks); +extern struct fgt_masks kvm_nvhe_sym(ich_hfgwtr_masks); +extern struct fgt_masks kvm_nvhe_sym(ich_hfgitr_masks); struct kvm_cpu_context { struct user_pt_regs regs; /* sp = sp_el0 */ @@ -768,8 +794,10 @@ struct kvm_host_data { struct kvm_guest_debug_arch regs; /* Statistical profiling extension */ u64 pmscr_el1; + u64 pmblimitr_el1; /* Self-hosted trace */ u64 trfcr_el1; + u64 trblimitr_el1; /* Values of trap registers for the host before guest entry. */ u64 mdcr_el2; u64 brbcr_el1; @@ -787,6 +815,14 @@ struct kvm_host_data { /* Last vgic_irq part of the AP list recorded in an LR */ struct vgic_irq *last_lr_irq; + + /* PPI state tracking for GICv5-based guests */ + struct { + DECLARE_BITMAP(pendr, VGIC_V5_NR_PRIVATE_IRQS); + + /* The saved state of the regs when leaving the guest */ + DECLARE_BITMAP(activer_exit, VGIC_V5_NR_PRIVATE_IRQS); + } vgic_v5_ppi_state; }; struct kvm_host_psci_config { @@ -923,6 +959,9 @@ struct kvm_vcpu_arch { /* Per-vcpu TLB for VNCR_EL2 -- NULL when !NV */ struct vncr_tlb *vncr_tlb; + + /* Hyp-readable copy of kvm_vcpu::pid */ + pid_t pid; }; /* @@ -1506,7 +1545,7 @@ static inline bool __vcpu_has_feature(const struct kvm_arch *ka, int feature) #define kvm_vcpu_has_feature(k, f) __vcpu_has_feature(&(k)->arch, (f)) #define vcpu_has_feature(v, f) __vcpu_has_feature(&(v)->kvm->arch, (f)) -#define kvm_vcpu_initialized(v) vcpu_get_flag(vcpu, VCPU_INITIALIZED) +#define kvm_vcpu_initialized(v) vcpu_get_flag(v, VCPU_INITIALIZED) int kvm_trng_call(struct kvm_vcpu *vcpu); #ifdef CONFIG_KVM @@ -1659,6 +1698,11 @@ static __always_inline enum fgt_group_id __fgt_reg_to_group_id(enum vcpu_sysreg case HDFGRTR2_EL2: case HDFGWTR2_EL2: return HDFGRTR2_GROUP; + case ICH_HFGRTR_EL2: + case ICH_HFGWTR_EL2: + return ICH_HFGRTR_GROUP; + case ICH_HFGITR_EL2: + return ICH_HFGITR_GROUP; default: BUILD_BUG_ON(1); } @@ -1673,6 +1717,7 @@ static __always_inline enum fgt_group_id __fgt_reg_to_group_id(enum vcpu_sysreg case HDFGWTR_EL2: \ case HFGWTR2_EL2: \ case HDFGWTR2_EL2: \ + case ICH_HFGWTR_EL2: \ p = &(vcpu)->arch.fgt[id].w; \ break; \ default: \ diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index 76ce2b94bd97..8d06b62e7188 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -87,6 +87,15 @@ void __vgic_v3_save_aprs(struct vgic_v3_cpu_if *cpu_if); void __vgic_v3_restore_vmcr_aprs(struct vgic_v3_cpu_if *cpu_if); int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu); +/* GICv5 */ +void __vgic_v5_save_apr(struct vgic_v5_cpu_if *cpu_if); +void __vgic_v5_restore_vmcr_apr(struct vgic_v5_cpu_if *cpu_if); +/* No hypercalls for the following */ +void __vgic_v5_save_ppi_state(struct vgic_v5_cpu_if *cpu_if); +void __vgic_v5_restore_ppi_state(struct vgic_v5_cpu_if *cpu_if); +void __vgic_v5_save_state(struct vgic_v5_cpu_if *cpu_if); +void __vgic_v5_restore_state(struct vgic_v5_cpu_if *cpu_if); + #ifdef __KVM_NVHE_HYPERVISOR__ void __timer_enable_traps(struct kvm_vcpu *vcpu); void __timer_disable_traps(struct kvm_vcpu *vcpu); @@ -129,13 +138,13 @@ void __noreturn __hyp_do_panic(struct kvm_cpu_context *host_ctxt, u64 spsr, #ifdef __KVM_NVHE_HYPERVISOR__ void __pkvm_init_switch_pgd(phys_addr_t pgd, unsigned long sp, void (*fn)(void)); -int __pkvm_init(phys_addr_t phys, unsigned long size, unsigned long nr_cpus, - unsigned long *per_cpu_base, u32 hyp_va_bits); +int __pkvm_init(phys_addr_t phys, unsigned long size, unsigned long *per_cpu_base, u32 hyp_va_bits); void __noreturn __host_enter(struct kvm_cpu_context *host_ctxt); #endif extern u64 kvm_nvhe_sym(id_aa64pfr0_el1_sys_val); extern u64 kvm_nvhe_sym(id_aa64pfr1_el1_sys_val); +extern u64 kvm_nvhe_sym(id_aa64pfr2_el1_sys_val); extern u64 kvm_nvhe_sym(id_aa64isar0_el1_sys_val); extern u64 kvm_nvhe_sym(id_aa64isar1_el1_sys_val); extern u64 kvm_nvhe_sym(id_aa64isar2_el1_sys_val); @@ -147,5 +156,6 @@ extern u64 kvm_nvhe_sym(id_aa64smfr0_el1_sys_val); extern unsigned long kvm_nvhe_sym(__icache_flags); extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits); extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl); +extern unsigned long kvm_nvhe_sym(hyp_nr_cpus); #endif /* __ARM64_KVM_HYP_H__ */ diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/asm/kvm_hypevents.h new file mode 100644 index 000000000000..743c49bd878f --- /dev/null +++ b/arch/arm64/include/asm/kvm_hypevents.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#if !defined(__ARM64_KVM_HYPEVENTS_H_) || defined(HYP_EVENT_MULTI_READ) +#define __ARM64_KVM_HYPEVENTS_H_ + +#ifdef __KVM_NVHE_HYPERVISOR__ +#include +#endif + +#ifndef __HYP_ENTER_EXIT_REASON +#define __HYP_ENTER_EXIT_REASON +enum hyp_enter_exit_reason { + HYP_REASON_SMC, + HYP_REASON_HVC, + HYP_REASON_PSCI, + HYP_REASON_HOST_ABORT, + HYP_REASON_GUEST_EXIT, + HYP_REASON_ERET_HOST, + HYP_REASON_ERET_GUEST, + HYP_REASON_UNKNOWN /* Must be last */ +}; +#endif + +HYP_EVENT(hyp_enter, + HE_PROTO(struct kvm_cpu_context *host_ctxt, u8 reason), + HE_STRUCT( + he_field(u8, reason) + he_field(pid_t, vcpu) + ), + HE_ASSIGN( + __entry->reason = reason; + __entry->vcpu = __tracing_get_vcpu_pid(host_ctxt); + ), + HE_PRINTK("reason=%s vcpu=%d", __hyp_enter_exit_reason_str(__entry->reason), __entry->vcpu) +); + +HYP_EVENT(hyp_exit, + HE_PROTO(struct kvm_cpu_context *host_ctxt, u8 reason), + HE_STRUCT( + he_field(u8, reason) + he_field(pid_t, vcpu) + ), + HE_ASSIGN( + __entry->reason = reason; + __entry->vcpu = __tracing_get_vcpu_pid(host_ctxt); + ), + HE_PRINTK("reason=%s vcpu=%d", __hyp_enter_exit_reason_str(__entry->reason), __entry->vcpu) +); + +HYP_EVENT(selftest, + HE_PROTO(u64 id), + HE_STRUCT( + he_field(u64, id) + ), + HE_ASSIGN( + __entry->id = id; + ), + RE_PRINTK("id=%llu", __entry->id) +); +#endif diff --git a/arch/arm64/include/asm/kvm_hyptrace.h b/arch/arm64/include/asm/kvm_hyptrace.h new file mode 100644 index 000000000000..de133b735f72 --- /dev/null +++ b/arch/arm64/include/asm/kvm_hyptrace.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ARM64_KVM_HYPTRACE_H_ +#define __ARM64_KVM_HYPTRACE_H_ + +#include + +struct hyp_trace_desc { + unsigned long bpages_backing_start; + size_t bpages_backing_size; + struct trace_buffer_desc trace_buffer_desc; + +}; + +struct hyp_event_id { + unsigned short id; + atomic_t enabled; +}; + +extern struct remote_event __hyp_events_start[]; +extern struct remote_event __hyp_events_end[]; + +/* hyp_event section used by the hypervisor */ +extern struct hyp_event_id __hyp_event_ids_start[]; +extern struct hyp_event_id __hyp_event_ids_end[]; + +#endif diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index d968aca0461a..01e9c72d6aa7 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -393,8 +393,12 @@ static inline bool kvm_supports_cacheable_pfnmap(void) #ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS void kvm_s2_ptdump_create_debugfs(struct kvm *kvm); +void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu); +void kvm_nested_s2_ptdump_remove_debugfs(struct kvm_s2_mmu *mmu); #else static inline void kvm_s2_ptdump_create_debugfs(struct kvm *kvm) {} +static inline void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu) {} +static inline void kvm_nested_s2_ptdump_remove_debugfs(struct kvm_s2_mmu *mmu) {} #endif /* CONFIG_PTDUMP_STAGE2_DEBUGFS */ #endif /* __ASSEMBLER__ */ diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h index 091544e6af44..dc2957662ff2 100644 --- a/arch/arm64/include/asm/kvm_nested.h +++ b/arch/arm64/include/asm/kvm_nested.h @@ -23,6 +23,7 @@ static inline u64 tcr_el2_ps_to_tcr_el1_ips(u64 tcr_el2) static inline u64 translate_tcr_el2_to_tcr_el1(u64 tcr) { return TCR_EPD1_MASK | /* disable TTBR1_EL1 */ + ((tcr & TCR_EL2_DS) ? TCR_DS : 0) | ((tcr & TCR_EL2_TBI) ? TCR_TBI0 : 0) | tcr_el2_ps_to_tcr_el1_ips(tcr) | (tcr & TCR_EL2_TG0_MASK) | diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h index c201168f2857..41a8687938eb 100644 --- a/arch/arm64/include/asm/kvm_pgtable.h +++ b/arch/arm64/include/asm/kvm_pgtable.h @@ -99,14 +99,30 @@ typedef u64 kvm_pte_t; KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W | \ KVM_PTE_LEAF_ATTR_HI_S2_XN) -#define KVM_INVALID_PTE_OWNER_MASK GENMASK(9, 2) -#define KVM_MAX_OWNER_ID 1 +/* pKVM invalid pte encodings */ +#define KVM_INVALID_PTE_TYPE_MASK GENMASK(63, 60) +#define KVM_INVALID_PTE_ANNOT_MASK ~(KVM_PTE_VALID | \ + KVM_INVALID_PTE_TYPE_MASK) -/* - * Used to indicate a pte for which a 'break-before-make' sequence is in - * progress. - */ -#define KVM_INVALID_PTE_LOCKED BIT(10) +enum kvm_invalid_pte_type { + /* + * Used to indicate a pte for which a 'break-before-make' + * sequence is in progress. + */ + KVM_INVALID_PTE_TYPE_LOCKED = 1, + + /* + * pKVM has unmapped the page from the host due to a change of + * ownership. + */ + KVM_HOST_INVALID_PTE_TYPE_DONATION, + + /* + * The page has been forcefully reclaimed from the guest by the + * host. + */ + KVM_GUEST_INVALID_PTE_TYPE_POISONED, +}; static inline bool kvm_pte_valid(kvm_pte_t pte) { @@ -658,14 +674,18 @@ int kvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size, void *mc, enum kvm_pgtable_walk_flags flags); /** - * kvm_pgtable_stage2_set_owner() - Unmap and annotate pages in the IPA space to - * track ownership. + * kvm_pgtable_stage2_annotate() - Unmap and annotate pages in the IPA space + * to track ownership (and more). * @pgt: Page-table structure initialised by kvm_pgtable_stage2_init*(). * @addr: Base intermediate physical address to annotate. * @size: Size of the annotated range. * @mc: Cache of pre-allocated and zeroed memory from which to allocate * page-table pages. - * @owner_id: Unique identifier for the owner of the page. + * @type: The type of the annotation, determining its meaning and format. + * @annotation: A 59-bit value that will be stored in the page tables. + * @annotation[0] and @annotation[63:60] must be 0. + * @annotation[59:1] is stored in the page tables, along + * with @type. * * By default, all page-tables are owned by identifier 0. This function can be * used to mark portions of the IPA space as owned by other entities. When a @@ -674,8 +694,9 @@ int kvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size, * * Return: 0 on success, negative error code on failure. */ -int kvm_pgtable_stage2_set_owner(struct kvm_pgtable *pgt, u64 addr, u64 size, - void *mc, u8 owner_id); +int kvm_pgtable_stage2_annotate(struct kvm_pgtable *pgt, u64 addr, u64 size, + void *mc, enum kvm_invalid_pte_type type, + kvm_pte_t annotation); /** * kvm_pgtable_stage2_unmap() - Remove a mapping from a guest stage-2 page-table. diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h index 757076ad4ec9..2954b311128c 100644 --- a/arch/arm64/include/asm/kvm_pkvm.h +++ b/arch/arm64/include/asm/kvm_pkvm.h @@ -17,7 +17,7 @@ #define HYP_MEMBLOCK_REGIONS 128 -int pkvm_init_host_vm(struct kvm *kvm); +int pkvm_init_host_vm(struct kvm *kvm, unsigned long type); int pkvm_create_hyp_vm(struct kvm *kvm); bool pkvm_hyp_vm_is_created(struct kvm *kvm); void pkvm_destroy_hyp_vm(struct kvm *kvm); @@ -40,8 +40,6 @@ static inline bool kvm_pkvm_ext_allowed(struct kvm *kvm, long ext) case KVM_CAP_MAX_VCPU_ID: case KVM_CAP_MSI_DEVID: case KVM_CAP_ARM_VM_IPA_SIZE: - case KVM_CAP_ARM_PMU_V3: - case KVM_CAP_ARM_SVE: case KVM_CAP_ARM_PTRAUTH_ADDRESS: case KVM_CAP_ARM_PTRAUTH_GENERIC: return true; diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 9029b81ccbe8..984f0502c9d0 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -1276,17 +1276,17 @@ static inline int pmdp_set_access_flags(struct vm_area_struct *vma, #endif #ifdef CONFIG_PAGE_TABLE_CHECK -static inline bool pte_user_accessible_page(pte_t pte, unsigned long addr) +static inline bool pte_user_accessible_page(struct mm_struct *mm, unsigned long addr, pte_t pte) { return pte_valid(pte) && (pte_user(pte) || pte_user_exec(pte)); } -static inline bool pmd_user_accessible_page(pmd_t pmd, unsigned long addr) +static inline bool pmd_user_accessible_page(struct mm_struct *mm, unsigned long addr, pmd_t pmd) { return pmd_valid(pmd) && !pmd_table(pmd) && (pmd_user(pmd) || pmd_user_exec(pmd)); } -static inline bool pud_user_accessible_page(pud_t pud, unsigned long addr) +static inline bool pud_user_accessible_page(struct mm_struct *mm, unsigned long addr, pud_t pud) { return pud_valid(pud) && !pud_table(pud) && (pud_user(pud) || pud_user_exec(pud)); } @@ -1830,6 +1830,18 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, return __ptep_get_and_clear(mm, addr, ptep); } +/* + * Note: strictly-zero compare is narrower than pte_none(), but the gap is + * harmless: a fresh kernel PTE has no software bits set. + */ +static inline bool ptep_try_set(pte_t *ptep, pte_t new_pte) +{ + pteval_t old = 0; + + return try_cmpxchg(&pte_val(*ptep), &old, pte_val(new_pte)); +} +#define ptep_try_set ptep_try_set + #define test_and_clear_young_ptes test_and_clear_young_ptes static inline bool test_and_clear_young_ptes(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, unsigned int nr) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index f4436ecc630c..7aa08d59d494 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -844,7 +844,7 @@ #define INIT_SCTLR_EL2_MMU_ON \ (SCTLR_ELx_M | SCTLR_ELx_C | SCTLR_ELx_SA | SCTLR_ELx_I | \ SCTLR_ELx_IESB | SCTLR_ELx_WXN | ENDIAN_SET_EL2 | \ - SCTLR_ELx_ITFSB | SCTLR_EL2_RES1) + SCTLR_ELx_ITFSB | SCTLR_ELx_EIS | SCTLR_ELx_EOS | SCTLR_EL2_RES1) #define INIT_SCTLR_EL2_MMU_OFF \ (SCTLR_EL2_RES1 | ENDIAN_SET_EL2) @@ -1052,6 +1052,7 @@ #define GICV5_OP_GIC_CDPRI sys_insn(1, 0, 12, 1, 2) #define GICV5_OP_GIC_CDRCFG sys_insn(1, 0, 12, 1, 5) #define GICV5_OP_GICR_CDIA sys_insn(1, 0, 12, 3, 0) +#define GICV5_OP_GICR_CDNMIA sys_insn(1, 0, 12, 3, 1) /* Definitions for GIC CDAFF */ #define GICV5_GIC_CDAFF_IAFFID_MASK GENMASK_ULL(47, 32) @@ -1098,6 +1099,12 @@ #define GICV5_GIC_CDIA_TYPE_MASK GENMASK_ULL(31, 29) #define GICV5_GIC_CDIA_ID_MASK GENMASK_ULL(23, 0) +/* Definitions for GICR CDNMIA */ +#define GICV5_GICR_CDNMIA_VALID_MASK BIT_ULL(32) +#define GICV5_GICR_CDNMIA_VALID(r) FIELD_GET(GICV5_GICR_CDNMIA_VALID_MASK, r) +#define GICV5_GICR_CDNMIA_TYPE_MASK GENMASK_ULL(31, 29) +#define GICV5_GICR_CDNMIA_ID_MASK GENMASK_ULL(23, 0) + #define gicr_insn(insn) read_sysreg_s(GICV5_OP_GICR_##insn) #define gic_insn(v, insn) write_sysreg_s(v, GICV5_OP_GIC_##insn) @@ -1114,11 +1121,9 @@ .macro msr_hcr_el2, reg #if IS_ENABLED(CONFIG_AMPERE_ERRATUM_AC04_CPU_23) dsb nsh - msr hcr_el2, \reg - isb -#else - msr hcr_el2, \reg #endif + msr hcr_el2, \reg + isb // Required by AMPERE_ERRATUM_AC04_CPU_23 .endm #else diff --git a/arch/arm64/include/asm/tlbbatch.h b/arch/arm64/include/asm/tlbbatch.h index fedb0b87b8db..6297631532e5 100644 --- a/arch/arm64/include/asm/tlbbatch.h +++ b/arch/arm64/include/asm/tlbbatch.h @@ -2,11 +2,17 @@ #ifndef _ARCH_ARM64_TLBBATCH_H #define _ARCH_ARM64_TLBBATCH_H +#include + struct arch_tlbflush_unmap_batch { +#ifdef CONFIG_ARM64_ERRATUM_4193714 /* - * For arm64, HW can do tlb shootdown, so we don't - * need to record cpumask for sending IPI + * Track CPUs that need SME DVMSync on completion of this batch. + * Otherwise, the arm64 HW can do tlb shootdown, so we don't need to + * record cpumask for sending IPI */ + cpumask_var_t cpumask; +#endif }; #endif /* _ARCH_ARM64_TLBBATCH_H */ diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index 47fa4d39a461..c0bf5b398041 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -80,6 +80,71 @@ static inline unsigned long get_trans_granule(void) } } +#ifdef CONFIG_ARM64_ERRATUM_4193714 + +void sme_do_dvmsync(const struct cpumask *mask); + +static inline void sme_dvmsync(struct mm_struct *mm) +{ + if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) + return; + + sme_do_dvmsync(mm_cpumask(mm)); +} + +static inline void sme_dvmsync_add_pending(struct arch_tlbflush_unmap_batch *batch, + struct mm_struct *mm) +{ + if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) + return; + + /* + * Order the mm_cpumask() read after the hardware DVMSync. + */ + dsb(ish); + if (cpumask_empty(mm_cpumask(mm))) + return; + + /* + * Allocate the batch cpumask on first use. Fall back to an immediate + * IPI for this mm in case of failure. + */ + if (!cpumask_available(batch->cpumask) && + !zalloc_cpumask_var(&batch->cpumask, GFP_ATOMIC)) { + sme_do_dvmsync(mm_cpumask(mm)); + return; + } + + cpumask_or(batch->cpumask, batch->cpumask, mm_cpumask(mm)); +} + +static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch) +{ + if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) + return; + + if (!cpumask_available(batch->cpumask)) + return; + + sme_do_dvmsync(batch->cpumask); + cpumask_clear(batch->cpumask); +} + +#else + +static inline void sme_dvmsync(struct mm_struct *mm) +{ +} +static inline void sme_dvmsync_add_pending(struct arch_tlbflush_unmap_batch *batch, + struct mm_struct *mm) +{ +} +static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch) +{ +} + +#endif /* CONFIG_ARM64_ERRATUM_4193714 */ + /* * Level-based TLBI operations. * @@ -213,7 +278,21 @@ do { \ * Complete broadcast TLB maintenance issued by the host which invalidates * stage 1 information in the host's own translation regime. */ -static inline void __tlbi_sync_s1ish(void) +static inline void __tlbi_sync_s1ish(struct mm_struct *mm) +{ + dsb(ish); + __repeat_tlbi_sync(vale1is, 0); + sme_dvmsync(mm); +} + +static inline void __tlbi_sync_s1ish_batch(struct arch_tlbflush_unmap_batch *batch) +{ + dsb(ish); + __repeat_tlbi_sync(vale1is, 0); + sme_dvmsync_batch(batch); +} + +static inline void __tlbi_sync_s1ish_kernel(void) { dsb(ish); __repeat_tlbi_sync(vale1is, 0); @@ -322,7 +401,7 @@ static inline void flush_tlb_all(void) { dsb(ishst); __tlbi(vmalle1is); - __tlbi_sync_s1ish(); + __tlbi_sync_s1ish_kernel(); isb(); } @@ -334,7 +413,7 @@ static inline void flush_tlb_mm(struct mm_struct *mm) asid = __TLBI_VADDR(0, ASID(mm)); __tlbi(aside1is, asid); __tlbi_user(aside1is, asid); - __tlbi_sync_s1ish(); + __tlbi_sync_s1ish(mm); mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL); } @@ -355,7 +434,7 @@ static inline bool arch_tlbbatch_should_defer(struct mm_struct *mm) */ static inline void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch) { - __tlbi_sync_s1ish(); + __tlbi_sync_s1ish_batch(batch); } /* @@ -557,7 +636,7 @@ static __always_inline void __do_flush_tlb_range(struct vm_area_struct *vma, if (!(flags & TLBF_NOSYNC)) { if (!(flags & TLBF_NOBROADCAST)) - __tlbi_sync_s1ish(); + __tlbi_sync_s1ish(mm); else dsb(nsh); } @@ -618,7 +697,7 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end dsb(ishst); __flush_s1_tlb_range_op(vaale1is, start, pages, stride, 0, TLBI_TTL_UNKNOWN); - __tlbi_sync_s1ish(); + __tlbi_sync_s1ish_kernel(); isb(); } @@ -632,7 +711,7 @@ static inline void __flush_tlb_kernel_pgtable(unsigned long kaddr) dsb(ishst); __tlbi(vaae1is, addr); - __tlbi_sync_s1ish(); + __tlbi_sync_s1ish_kernel(); isb(); } @@ -643,6 +722,7 @@ static inline void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *b __flush_tlb_range(&vma, start, end, PAGE_SIZE, 3, TLBF_NOWALKCACHE | TLBF_NOSYNC); + sme_dvmsync_add_pending(batch, mm); } static inline bool __pte_flags_need_flush(ptdesc_t oldval, ptdesc_t newval) diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h index b51ab6840f9c..b546703c3ab9 100644 --- a/arch/arm64/include/asm/virt.h +++ b/arch/arm64/include/asm/virt.h @@ -94,6 +94,15 @@ static inline bool is_pkvm_initialized(void) static_branch_likely(&kvm_protected_mode_initialized); } +#ifdef CONFIG_KVM +bool pkvm_force_reclaim_guest_page(phys_addr_t phys); +#else +static inline bool pkvm_force_reclaim_guest_page(phys_addr_t phys) +{ + return false; +} +#endif + /* Reports the availability of HYP mode */ static inline bool is_hyp_mode_available(void) { diff --git a/arch/arm64/include/asm/vncr_mapping.h b/arch/arm64/include/asm/vncr_mapping.h index c2485a862e69..14366d35ce82 100644 --- a/arch/arm64/include/asm/vncr_mapping.h +++ b/arch/arm64/include/asm/vncr_mapping.h @@ -108,5 +108,8 @@ #define VNCR_MPAMVPM5_EL2 0x968 #define VNCR_MPAMVPM6_EL2 0x970 #define VNCR_MPAMVPM7_EL2 0x978 +#define VNCR_ICH_HFGITR_EL2 0xB10 +#define VNCR_ICH_HFGRTR_EL2 0xB18 +#define VNCR_ICH_HFGWTR_EL2 0xB20 #endif /* __ARM64_VNCR_MAPPING_H__ */ diff --git a/arch/arm64/include/asm/xor.h b/arch/arm64/include/asm/xor.h deleted file mode 100644 index c38e3d017a79..000000000000 --- a/arch/arm64/include/asm/xor.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * arch/arm64/include/asm/xor.h - * - * Authors: Jackie Liu - * Copyright (C) 2018,Tianjin KYLIN Information Technology Co., Ltd. - */ - -#include -#include -#include -#include - -#ifdef CONFIG_KERNEL_MODE_NEON - -extern struct xor_block_template const xor_block_inner_neon; - -static void -xor_neon_2(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2) -{ - scoped_ksimd() - xor_block_inner_neon.do_2(bytes, p1, p2); -} - -static void -xor_neon_3(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3) -{ - scoped_ksimd() - xor_block_inner_neon.do_3(bytes, p1, p2, p3); -} - -static void -xor_neon_4(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4) -{ - scoped_ksimd() - xor_block_inner_neon.do_4(bytes, p1, p2, p3, p4); -} - -static void -xor_neon_5(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4, - const unsigned long * __restrict p5) -{ - scoped_ksimd() - xor_block_inner_neon.do_5(bytes, p1, p2, p3, p4, p5); -} - -static struct xor_block_template xor_block_arm64 = { - .name = "arm64_neon", - .do_2 = xor_neon_2, - .do_3 = xor_neon_3, - .do_4 = xor_neon_4, - .do_5 = xor_neon_5 -}; -#undef XOR_TRY_TEMPLATES -#define XOR_TRY_TEMPLATES \ - do { \ - xor_speed(&xor_block_8regs); \ - xor_speed(&xor_block_32regs); \ - if (cpu_has_neon()) { \ - xor_speed(&xor_block_arm64);\ - } \ - } while (0) - -#endif /* ! CONFIG_KERNEL_MODE_NEON */ diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index a792a599b9d6..1c13bfa2d38a 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -428,6 +428,7 @@ enum { #define KVM_DEV_ARM_ITS_RESTORE_TABLES 2 #define KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES 3 #define KVM_DEV_ARM_ITS_CTRL_RESET 4 +#define KVM_DEV_ARM_VGIC_USERSPACE_PPIS 5 /* Device Control API on vcpu fd */ #define KVM_ARM_VCPU_PMU_V3_CTRL 0 diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index 5c0ab6bfd44a..5377e4c2eba2 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -575,6 +576,23 @@ static const struct midr_range erratum_spec_ssbs_list[] = { }; #endif +#ifdef CONFIG_ARM64_ERRATUM_4193714 +static bool has_sme_dvmsync_erratum(const struct arm64_cpu_capabilities *entry, + int scope) +{ + if (!id_aa64pfr1_sme(read_sanitised_ftr_reg(SYS_ID_AA64PFR1_EL1))) + return false; + + return is_affected_midr_range(entry, scope); +} + +static void cpu_enable_sme_dvmsync(const struct arm64_cpu_capabilities *__unused) +{ + if (this_cpu_has_cap(ARM64_WORKAROUND_4193714)) + sme_enable_dvmsync(); +} +#endif + #ifdef CONFIG_AMPERE_ERRATUM_AC03_CPU_38 static const struct midr_range erratum_ac03_cpu_38_list[] = { MIDR_ALL_VERSIONS(MIDR_AMPERE1), @@ -901,6 +919,18 @@ const struct arm64_cpu_capabilities arm64_errata[] = { .matches = need_arm_si_l1_workaround_4311569, }, #endif +#ifdef CONFIG_ARM64_ERRATUM_4193714 + { + .desc = "C1-Pro SME DVMSync early acknowledgement", + .capability = ARM64_WORKAROUND_4193714, + .type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM, + .matches = has_sme_dvmsync_erratum, + .cpu_enable = cpu_enable_sme_dvmsync, + /* C1-Pro r0p0 - r1p2 (the latter only when REVIDR_EL1[0]==0) */ + .midr_range = MIDR_RANGE(MIDR_C1_PRO, 0, 0, 1, 2), + MIDR_FIXED(MIDR_CPU_VAR_REV(1, 2), BIT(0)), + }, +#endif #ifdef CONFIG_ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD { .desc = "ARM errata 2966298, 3117295", diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 29312081d2e3..6d53bb15cf7b 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -328,6 +328,7 @@ static const struct arm64_ftr_bits ftr_id_aa64pfr1[] = { static const struct arm64_ftr_bits ftr_id_aa64pfr2[] = { ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR2_EL1_FPMR_SHIFT, 4, 0), + ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR2_EL1_GCIE_SHIFT, 4, ID_AA64PFR2_EL1_GCIE_NI), ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR2_EL1_MTEFAR_SHIFT, 4, ID_AA64PFR2_EL1_MTEFAR_NI), ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR2_EL1_MTESTOREONLY_SHIFT, 4, ID_AA64PFR2_EL1_MTESTOREONLY_NI), ARM64_FTR_END, diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c index f42ce7b5c67f..c7a23f7c2212 100644 --- a/arch/arm64/kernel/entry-common.c +++ b/arch/arm64/kernel/entry-common.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +62,13 @@ static void noinstr arm64_exit_to_kernel_mode(struct pt_regs *regs, irqentry_exit_to_kernel_mode_after_preempt(regs, state); } +static __always_inline void arm64_syscall_enter_from_user_mode(struct pt_regs *regs) +{ + enter_from_user_mode(regs); + mte_disable_tco_entry(current); + sme_enter_from_user_mode(); +} + /* * Handle IRQ/context state management when entering from user mode. * Before this function is called it is not safe to call regular kernel code, @@ -69,7 +77,19 @@ static void noinstr arm64_exit_to_kernel_mode(struct pt_regs *regs, static __always_inline void arm64_enter_from_user_mode(struct pt_regs *regs) { enter_from_user_mode(regs); + rseq_note_user_irq_entry(); mte_disable_tco_entry(current); + sme_enter_from_user_mode(); +} + +static __always_inline void arm64_syscall_exit_to_user_mode(struct pt_regs *regs) +{ + local_irq_disable(); + syscall_exit_to_user_mode_prepare(regs); + local_daif_mask(); + sme_exit_to_user_mode(); + mte_check_tfsr_exit(); + exit_to_user_mode(); } /* @@ -77,19 +97,19 @@ static __always_inline void arm64_enter_from_user_mode(struct pt_regs *regs) * After this function returns it is not safe to call regular kernel code, * instrumentable code, or any code which may trigger an exception. */ - static __always_inline void arm64_exit_to_user_mode(struct pt_regs *regs) { local_irq_disable(); - exit_to_user_mode_prepare_legacy(regs); + irqentry_exit_to_user_mode_prepare(regs); local_daif_mask(); + sme_exit_to_user_mode(); mte_check_tfsr_exit(); exit_to_user_mode(); } asmlinkage void noinstr asm_exit_to_user_mode(struct pt_regs *regs) { - arm64_exit_to_user_mode(regs); + arm64_syscall_exit_to_user_mode(regs); } /* @@ -713,12 +733,12 @@ static void noinstr el0_brk64(struct pt_regs *regs, unsigned long esr) static void noinstr el0_svc(struct pt_regs *regs) { - arm64_enter_from_user_mode(regs); + arm64_syscall_enter_from_user_mode(regs); cortex_a76_erratum_1463225_svc_handler(); fpsimd_syscall_enter(); local_daif_restore(DAIF_PROCCTX); do_el0_svc(regs); - arm64_exit_to_user_mode(regs); + arm64_syscall_exit_to_user_mode(regs); fpsimd_syscall_exit(); } @@ -865,11 +885,11 @@ static void noinstr el0_cp15(struct pt_regs *regs, unsigned long esr) static void noinstr el0_svc_compat(struct pt_regs *regs) { - arm64_enter_from_user_mode(regs); + arm64_syscall_enter_from_user_mode(regs); cortex_a76_erratum_1463225_svc_handler(); local_daif_restore(DAIF_PROCCTX); do_el0_svc_compat(regs); - arm64_exit_to_user_mode(regs); + arm64_syscall_exit_to_user_mode(regs); } static void noinstr el0_bkpt32(struct pt_regs *regs, unsigned long esr) diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c index 9de1d8a604cb..60a45d600b46 100644 --- a/arch/arm64/kernel/fpsimd.c +++ b/arch/arm64/kernel/fpsimd.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -1358,6 +1360,83 @@ void do_sve_acc(unsigned long esr, struct pt_regs *regs) put_cpu_fpsimd_context(); } +#ifdef CONFIG_ARM64_ERRATUM_4193714 + +/* + * SME/CME erratum handling. + */ +static cpumask_t sme_dvmsync_cpus; + +/* + * These helpers are only called from non-preemptible contexts, so + * smp_processor_id() is safe here. + */ +void sme_set_active(void) +{ + unsigned int cpu = smp_processor_id(); + + if (!cpumask_test_cpu(cpu, &sme_dvmsync_cpus)) + return; + + cpumask_set_cpu(cpu, mm_cpumask(current->mm)); + + /* + * A subsequent (post ERET) SME access may use a stale address + * translation. On C1-Pro, a TLBI+DSB on a different CPU will wait for + * the completion of cpumask_set_cpu() above as it appears in program + * order before the SME access. The post-TLBI+DSB read of mm_cpumask() + * will lead to the IPI being issued. + * + * https://lore.kernel.org/r/ablEXwhfKyJW1i7l@J2N7QTR9R3 + */ +} + +void sme_clear_active(void) +{ + unsigned int cpu = smp_processor_id(); + + if (!cpumask_test_cpu(cpu, &sme_dvmsync_cpus)) + return; + + /* + * With SCTLR_EL1.IESB enabled, the SME memory transactions are + * completed on entering EL1. + */ + cpumask_clear_cpu(cpu, mm_cpumask(current->mm)); +} + +static void sme_dvmsync_ipi(void *unused) +{ + /* + * With SCTLR_EL1.IESB on, taking an exception is sufficient to ensure + * the completion of the SME memory accesses, so no need for an + * explicit DSB. + */ +} + +void sme_do_dvmsync(const struct cpumask *mask) +{ + /* + * This is called from the TLB maintenance functions after the DSB ISH + * to send the hardware DVMSync message. If this CPU sees the mask as + * empty, the remote CPU executing sme_set_active() would have seen + * the DVMSync and no IPI required. + */ + if (cpumask_empty(mask)) + return; + + preempt_disable(); + smp_call_function_many(mask, sme_dvmsync_ipi, NULL, true); + preempt_enable(); +} + +void sme_enable_dvmsync(void) +{ + cpumask_set_cpu(smp_processor_id(), &sme_dvmsync_cpus); +} + +#endif /* CONFIG_ARM64_ERRATUM_4193714 */ + /* * Trapped SME access * diff --git a/arch/arm64/kernel/hyp-stub.S b/arch/arm64/kernel/hyp-stub.S index 085bc9972f6b..634ddc904244 100644 --- a/arch/arm64/kernel/hyp-stub.S +++ b/arch/arm64/kernel/hyp-stub.S @@ -103,7 +103,6 @@ SYM_CODE_START_LOCAL(__finalise_el2) // Engage the VHE magic! mov_q x0, HCR_HOST_VHE_FLAGS msr_hcr_el2 x0 - isb // Use the EL1 allocated stack, per-cpu offset mrs x0, sp_el1 diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h index d7b0d12b1015..d4c7d45ae6bc 100644 --- a/arch/arm64/kernel/image-vars.h +++ b/arch/arm64/kernel/image-vars.h @@ -138,6 +138,10 @@ KVM_NVHE_ALIAS(__hyp_data_start); KVM_NVHE_ALIAS(__hyp_data_end); KVM_NVHE_ALIAS(__hyp_rodata_start); KVM_NVHE_ALIAS(__hyp_rodata_end); +#ifdef CONFIG_NVHE_EL2_TRACING +KVM_NVHE_ALIAS(__hyp_event_ids_start); +KVM_NVHE_ALIAS(__hyp_event_ids_end); +#endif /* pKVM static key */ KVM_NVHE_ALIAS(kvm_protected_mode_initialized); diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c index fba260ad87a9..e31fabed378a 100644 --- a/arch/arm64/kernel/machine_kexec_file.c +++ b/arch/arm64/kernel/machine_kexec_file.c @@ -134,6 +134,10 @@ int load_other_segments(struct kimage *image, kexec_dprintk("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n", image->elf_load_addr, kbuf.bufsz, kbuf.memsz); + + ret = crash_load_dm_crypt_keys(image); + if (ret) + goto out_err; } #endif diff --git a/arch/arm64/kernel/pi/patch-scs.c b/arch/arm64/kernel/pi/patch-scs.c index dac568e4a54f..3944ad899021 100644 --- a/arch/arm64/kernel/pi/patch-scs.c +++ b/arch/arm64/kernel/pi/patch-scs.c @@ -196,9 +196,9 @@ static int scs_handle_fde_frame(const struct eh_frame *frame, loc += *opcode++ * code_alignment_factor; loc += (*opcode++ << 8) * code_alignment_factor; loc += (*opcode++ << 16) * code_alignment_factor; - loc += (*opcode++ << 24) * code_alignment_factor; + loc += ((u64)*opcode++ << 24) * code_alignment_factor; size -= 4; - break; + break; case DW_CFA_def_cfa: case DW_CFA_offset_extended: diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index c0bf1f46cdc6..033643cd4e5e 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -340,8 +341,41 @@ void flush_thread(void) flush_gcs(); } +#ifdef CONFIG_ARM64_ERRATUM_4193714 + +static void arch_dup_tlbbatch_mask(struct task_struct *dst) +{ + /* + * Clear the inherited cpumask with memset() to cover both cases where + * cpumask_var_t is a pointer or an array. It will be allocated lazily + * in sme_dvmsync_add_pending() if CPUMASK_OFFSTACK=y. + */ + if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) + memset(&dst->tlb_ubc.arch.cpumask, 0, + sizeof(dst->tlb_ubc.arch.cpumask)); +} + +static void arch_release_tlbbatch_mask(struct task_struct *tsk) +{ + if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) + free_cpumask_var(tsk->tlb_ubc.arch.cpumask); +} + +#else + +static void arch_dup_tlbbatch_mask(struct task_struct *dst) +{ +} + +static void arch_release_tlbbatch_mask(struct task_struct *tsk) +{ +} + +#endif /* CONFIG_ARM64_ERRATUM_4193714 */ + void arch_release_task_struct(struct task_struct *tsk) { + arch_release_tlbbatch_mask(tsk); fpsimd_release_task(tsk); } @@ -357,6 +391,8 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) *dst = *src; + arch_dup_tlbbatch_mask(dst); + /* * Drop stale reference to src's sve_state and convert dst to * non-streaming FPSIMD mode. diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index ba5eab23fd90..4d08598e2891 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -983,8 +983,8 @@ static int sve_set_common(struct task_struct *target, } /* Always zero V regs, FPSR, and FPCR */ - memset(¤t->thread.uw.fpsimd_state, 0, - sizeof(current->thread.uw.fpsimd_state)); + memset(&target->thread.uw.fpsimd_state, 0, + sizeof(target->thread.uw.fpsimd_state)); /* Registers: FPSIMD-only case */ diff --git a/arch/arm64/kernel/relocate_kernel.S b/arch/arm64/kernel/relocate_kernel.S index 413f899e4ac6..6cb4209f5dab 100644 --- a/arch/arm64/kernel/relocate_kernel.S +++ b/arch/arm64/kernel/relocate_kernel.S @@ -64,7 +64,8 @@ SYM_CODE_START(arm64_relocate_new_kernel) mov x19, x13 copy_page x13, x12, x1, x2, x3, x4, x5, x6, x7, x8 add x1, x19, #PAGE_SIZE - dcache_by_myline_op civac, sy, x19, x1, x15, x20 + dcache_by_myline_op_nosync civac, x19, x1, x15, x20 + dsb sy b .Lnext .Ltest_indirection: tbz x16, IND_INDIRECTION_BIT, .Ltest_destination diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c index 08ffc5a5aea4..38e6fa204c17 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c @@ -67,6 +67,9 @@ struct rt_sigframe_user_layout { unsigned long end_offset; }; +#define TERMINATOR_SIZE round_up(sizeof(struct _aarch64_ctx), 16) +#define EXTRA_CONTEXT_SIZE round_up(sizeof(struct extra_context), 16) + /* * Holds any EL0-controlled state that influences unprivileged memory accesses. * This includes both accesses done in userspace and uaccess done in the kernel. @@ -74,13 +77,35 @@ struct rt_sigframe_user_layout { * This state needs to be carefully managed to ensure that it doesn't cause * uaccess to fail when setting up the signal frame, and the signal handler * itself also expects a well-defined state when entered. + * + * The struct should be zero-initialised. Its members should only be accessed + * via the accessors below. __valid_fields tracks which of the fields are valid + * (have been set to some value). */ struct user_access_state { - u64 por_el0; + unsigned int __valid_fields; + u64 __por_el0; }; -#define TERMINATOR_SIZE round_up(sizeof(struct _aarch64_ctx), 16) -#define EXTRA_CONTEXT_SIZE round_up(sizeof(struct extra_context), 16) +#define UA_STATE_HAS_POR_EL0 BIT(0) + +static void set_ua_state_por_el0(struct user_access_state *ua_state, + u64 por_el0) +{ + ua_state->__por_el0 = por_el0; + ua_state->__valid_fields |= UA_STATE_HAS_POR_EL0; +} + +static int get_ua_state_por_el0(const struct user_access_state *ua_state, + u64 *por_el0) +{ + if (ua_state->__valid_fields & UA_STATE_HAS_POR_EL0) { + *por_el0 = ua_state->__por_el0; + return 0; + } + + return -ENOENT; +} /* * Save the user access state into ua_state and reset it to disable any @@ -94,7 +119,7 @@ static void save_reset_user_access_state(struct user_access_state *ua_state) for (int pkey = 0; pkey < arch_max_pkey(); pkey++) por_enable_all |= POR_ELx_PERM_PREP(pkey, POE_RWX); - ua_state->por_el0 = read_sysreg_s(SYS_POR_EL0); + set_ua_state_por_el0(ua_state, read_sysreg_s(SYS_POR_EL0)); write_sysreg_s(por_enable_all, SYS_POR_EL0); /* * No ISB required as we can tolerate spurious Overlay faults - @@ -122,8 +147,10 @@ static void set_handler_user_access_state(void) */ static void restore_user_access_state(const struct user_access_state *ua_state) { - if (system_supports_poe()) - write_sysreg_s(ua_state->por_el0, SYS_POR_EL0); + u64 por_el0; + + if (get_ua_state_por_el0(ua_state, &por_el0) == 0) + write_sysreg_s(por_el0, SYS_POR_EL0); } static void init_user_layout(struct rt_sigframe_user_layout *user) @@ -333,11 +360,16 @@ static int restore_fpmr_context(struct user_ctxs *user) static int preserve_poe_context(struct poe_context __user *ctx, const struct user_access_state *ua_state) { - int err = 0; + int err; + u64 por_el0; + + err = get_ua_state_por_el0(ua_state, &por_el0); + if (WARN_ON_ONCE(err)) + return err; __put_user_error(POE_MAGIC, &ctx->head.magic, err); __put_user_error(sizeof(*ctx), &ctx->head.size, err); - __put_user_error(ua_state->por_el0, &ctx->por_el0, err); + __put_user_error(por_el0, &ctx->por_el0, err); return err; } @@ -353,7 +385,7 @@ static int restore_poe_context(struct user_ctxs *user, __get_user_error(por_el0, &(user->poe->por_el0), err); if (!err) - ua_state->por_el0 = por_el0; + set_ua_state_por_el0(ua_state, por_el0); return err; } @@ -1095,7 +1127,7 @@ SYSCALL_DEFINE0(rt_sigreturn) { struct pt_regs *regs = current_pt_regs(); struct rt_sigframe __user *frame; - struct user_access_state ua_state; + struct user_access_state ua_state = {}; /* Always make any pending restarted system calls return -EINTR */ current->restart_block.fn = do_no_restart_syscall; @@ -1507,7 +1539,7 @@ static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, { struct rt_sigframe_user_layout user; struct rt_sigframe __user *frame; - struct user_access_state ua_state; + struct user_access_state ua_state = {}; int err = 0; fpsimd_save_and_flush_current_state(); diff --git a/arch/arm64/kernel/sys_compat.c b/arch/arm64/kernel/sys_compat.c index 7e9860143add..0451f96c2c3f 100644 --- a/arch/arm64/kernel/sys_compat.c +++ b/arch/arm64/kernel/sys_compat.c @@ -37,7 +37,7 @@ __do_compat_cache_op(unsigned long start, unsigned long end) * We pick the reserved-ASID to minimise the impact. */ __tlbi(aside1is, 0UL); - __tlbi_sync_s1ish(); + __tlbi_sync_s1ish(current->mm); } ret = caches_clean_inval_user_pou(start, start + chunk); diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index 2d1e75263f03..e1ac876200a3 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -13,12 +13,23 @@ *(__kvm_ex_table) \ __stop___kvm_ex_table = .; +#ifdef CONFIG_NVHE_EL2_TRACING +#define HYPERVISOR_EVENT_IDS \ + . = ALIGN(PAGE_SIZE); \ + __hyp_event_ids_start = .; \ + *(HYP_SECTION_NAME(.event_ids)) \ + __hyp_event_ids_end = .; +#else +#define HYPERVISOR_EVENT_IDS +#endif + #define HYPERVISOR_RODATA_SECTIONS \ HYP_SECTION_NAME(.rodata) : { \ . = ALIGN(PAGE_SIZE); \ __hyp_rodata_start = .; \ *(HYP_SECTION_NAME(.data..ro_after_init)) \ *(HYP_SECTION_NAME(.rodata)) \ + HYPERVISOR_EVENT_IDS \ . = ALIGN(PAGE_SIZE); \ __hyp_rodata_end = .; \ } @@ -308,6 +319,13 @@ SECTIONS HYPERVISOR_DATA_SECTION +#ifdef CONFIG_NVHE_EL2_TRACING + .data.hyp_events : { + __hyp_events_start = .; + *(SORT(_hyp_events.*)) + __hyp_events_end = .; + } +#endif /* * Data written with the MMU off but read with the MMU on requires * cache lines to be invalidated, discarding up to a Cache Writeback diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig index 7d1f22fd490b..449154f9a485 100644 --- a/arch/arm64/kvm/Kconfig +++ b/arch/arm64/kvm/Kconfig @@ -42,32 +42,10 @@ menuconfig KVM If unsure, say N. -config NVHE_EL2_DEBUG - bool "Debug mode for non-VHE EL2 object" - depends on KVM - help - Say Y here to enable the debug mode for the non-VHE KVM EL2 object. - Failure reports will BUG() in the hypervisor. This is intended for - local EL2 hypervisor development. - - If unsure, say N. - -config PROTECTED_NVHE_STACKTRACE - bool "Protected KVM hypervisor stacktraces" - depends on NVHE_EL2_DEBUG - default n - help - Say Y here to enable pKVM hypervisor stacktraces on hyp_panic() - - If using protected nVHE mode, but cannot afford the associated - memory cost (less than 0.75 page per CPU) of pKVM stacktraces, - say N. - - If unsure, or not using protected nVHE (pKVM), say N. +if KVM config PTDUMP_STAGE2_DEBUGFS bool "Present the stage-2 pagetables to debugfs" - depends on KVM depends on DEBUG_KERNEL depends on DEBUG_FS depends on ARCH_HAS_PTDUMP @@ -82,4 +60,48 @@ config PTDUMP_STAGE2_DEBUGFS If in doubt, say N. +config NVHE_EL2_DEBUG + bool "Debug mode for non-VHE EL2 object" + default n + help + Say Y here to enable the debug mode for the non-VHE KVM EL2 object. + Failure reports will BUG() in the hypervisor. This is intended for + local EL2 hypervisor development. + + If unsure, say N. + +if NVHE_EL2_DEBUG + +config NVHE_EL2_TRACING + bool + depends on TRACING && FTRACE + select TRACE_REMOTE + default y + +config PKVM_DISABLE_STAGE2_ON_PANIC + bool "Disable the host stage-2 on panic" + default n + help + Relax the host stage-2 on hypervisor panic to allow the kernel to + unwind and symbolize the hypervisor stacktrace. This however tampers + the system security. This is intended for local EL2 hypervisor + development. + + If unsure, say N. + +config PKVM_STACKTRACE + bool "Protected KVM hypervisor stacktraces" + depends on PKVM_DISABLE_STAGE2_ON_PANIC + default y + help + Say Y here to enable pKVM hypervisor stacktraces on hyp_panic() + + If using protected nVHE mode, but cannot afford the associated + memory cost (less than 0.75 page per CPU) of pKVM stacktraces, + say N. + + If unsure, or not using protected nVHE (pKVM), say N. + +endif # NVHE_EL2_DEBUG +endif # KVM endif # VIRTUALIZATION diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index 3ebc0570345c..59612d2f277c 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -30,6 +30,8 @@ kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o pmu.o kvm-$(CONFIG_ARM64_PTR_AUTH) += pauth.o kvm-$(CONFIG_PTDUMP_STAGE2_DEBUGFS) += ptdump.o +kvm-$(CONFIG_NVHE_EL2_TRACING) += hyp_trace.o + always-y := hyp_constants.h hyp-constants.s define rule_gen_hyp_constants diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 600f250753b4..cbea4d9ee955 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -56,6 +56,12 @@ static struct irq_ops arch_timer_irq_ops = { .get_input_level = kvm_arch_timer_get_input_level, }; +static struct irq_ops arch_timer_irq_ops_vgic_v5 = { + .get_input_level = kvm_arch_timer_get_input_level, + .queue_irq_unlock = vgic_v5_ppi_queue_irq_unlock, + .set_direct_injection = vgic_v5_set_ppi_dvi, +}; + static int nr_timers(struct kvm_vcpu *vcpu) { if (!vcpu_has_nv(vcpu)) @@ -447,6 +453,17 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level, if (userspace_irqchip(vcpu->kvm)) return; + /* Skip injecting on GICv5 for directly injected (DVI'd) timers */ + if (vgic_is_v5(vcpu->kvm)) { + struct timer_map map; + + get_timer_map(vcpu, &map); + + if (map.direct_ptimer == timer_ctx || + map.direct_vtimer == timer_ctx) + return; + } + kvm_vgic_inject_irq(vcpu->kvm, vcpu, timer_irq(timer_ctx), timer_ctx->irq.level, @@ -674,6 +691,7 @@ static void kvm_timer_vcpu_load_gic(struct arch_timer_context *ctx) phys_active = kvm_vgic_map_is_active(vcpu, timer_irq(ctx)); phys_active |= ctx->irq.level; + phys_active |= vgic_is_v5(vcpu->kvm); set_timer_irq_phys_active(ctx, phys_active); } @@ -740,13 +758,11 @@ static void kvm_timer_vcpu_load_nested_switch(struct kvm_vcpu *vcpu, ret = kvm_vgic_map_phys_irq(vcpu, map->direct_vtimer->host_timer_irq, - timer_irq(map->direct_vtimer), - &arch_timer_irq_ops); + timer_irq(map->direct_vtimer)); WARN_ON_ONCE(ret); ret = kvm_vgic_map_phys_irq(vcpu, map->direct_ptimer->host_timer_irq, - timer_irq(map->direct_ptimer), - &arch_timer_irq_ops); + timer_irq(map->direct_ptimer)); WARN_ON_ONCE(ret); } } @@ -864,7 +880,8 @@ void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu) get_timer_map(vcpu, &map); if (static_branch_likely(&has_gic_active_state)) { - if (vcpu_has_nv(vcpu)) + /* We don't do NV on GICv5, yet */ + if (vcpu_has_nv(vcpu) && !vgic_is_v5(vcpu->kvm)) kvm_timer_vcpu_load_nested_switch(vcpu, &map); kvm_timer_vcpu_load_gic(map.direct_vtimer); @@ -934,6 +951,12 @@ void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu) if (kvm_vcpu_is_blocking(vcpu)) kvm_timer_blocking(vcpu); + + if (vgic_is_v5(vcpu->kvm)) { + set_timer_irq_phys_active(map.direct_vtimer, false); + if (map.direct_ptimer) + set_timer_irq_phys_active(map.direct_ptimer, false); + } } void kvm_timer_sync_nested(struct kvm_vcpu *vcpu) @@ -1097,10 +1120,19 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) HRTIMER_MODE_ABS_HARD); } +/* + * This is always called during kvm_arch_init_vm, but will also be + * called from kvm_vgic_create if we have a vGICv5. + */ void kvm_timer_init_vm(struct kvm *kvm) { + /* + * Set up the default PPIs - note that we adjust them based on + * the model of the GIC as GICv5 uses a different way to + * describing interrupts. + */ for (int i = 0; i < NR_KVM_TIMERS; i++) - kvm->arch.timer_data.ppi[i] = default_ppi[i]; + kvm->arch.timer_data.ppi[i] = get_vgic_ppi(kvm, default_ppi[i]); } void kvm_timer_cpu_up(void) @@ -1269,7 +1301,15 @@ static int timer_irq_set_irqchip_state(struct irq_data *d, static void timer_irq_eoi(struct irq_data *d) { - if (!irqd_is_forwarded_to_vcpu(d)) + /* + * On a GICv5 host, we still need to call EOI on the parent for + * PPIs. The host driver already handles irqs which are forwarded to + * vcpus, and skips the GIC CDDI while still doing the GIC CDEOI. This + * is required to emulate the EOIMode=1 on GICv5 hardware. Failure to + * call EOI unsurprisingly results in *BAD* lock-ups. + */ + if (!irqd_is_forwarded_to_vcpu(d) || + kvm_vgic_global_state.type == VGIC_V5) irq_chip_eoi_parent(d); } @@ -1333,7 +1373,8 @@ static int kvm_irq_init(struct arch_timer_kvm_info *info) host_vtimer_irq = info->virtual_irq; kvm_irq_fixup_flags(host_vtimer_irq, &host_vtimer_irq_flags); - if (kvm_vgic_global_state.no_hw_deactivation) { + if (kvm_vgic_global_state.no_hw_deactivation || + kvm_vgic_global_state.type == VGIC_V5) { struct fwnode_handle *fwnode; struct irq_data *data; @@ -1351,7 +1392,8 @@ static int kvm_irq_init(struct arch_timer_kvm_info *info) return -ENOMEM; } - arch_timer_irq_ops.flags |= VGIC_IRQ_SW_RESAMPLE; + if (kvm_vgic_global_state.no_hw_deactivation) + arch_timer_irq_ops.flags |= VGIC_IRQ_SW_RESAMPLE; WARN_ON(irq_domain_push_irq(domain, host_vtimer_irq, (void *)TIMER_VTIMER)); } @@ -1501,11 +1543,18 @@ static bool timer_irqs_are_valid(struct kvm_vcpu *vcpu) if (kvm_vgic_set_owner(vcpu, irq, ctx)) break; + /* With GICv5, the default PPI is what you get -- nothing else */ + if (vgic_is_v5(vcpu->kvm) && irq != get_vgic_ppi(vcpu->kvm, default_ppi[i])) + break; + /* - * We know by construction that we only have PPIs, so - * all values are less than 32. + * We know by construction that we only have PPIs, so all values + * are less than 32 for non-GICv5 VGICs. On GICv5, they are + * architecturally defined to be under 32 too. However, we mask + * off most of the bits as we might be presented with a GICv5 + * style PPI where the type is encoded in the top-bits. */ - ppis |= BIT(irq); + ppis |= BIT(irq & 0x1f); } valid = hweight32(ppis) == nr_timers(vcpu); @@ -1543,6 +1592,7 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = vcpu_timer(vcpu); struct timer_map map; + struct irq_ops *ops; int ret; if (timer->enabled) @@ -1563,20 +1613,22 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu) get_timer_map(vcpu, &map); + ops = vgic_is_v5(vcpu->kvm) ? &arch_timer_irq_ops_vgic_v5 : + &arch_timer_irq_ops; + + for (int i = 0; i < nr_timers(vcpu); i++) + kvm_vgic_set_irq_ops(vcpu, timer_irq(vcpu_get_timer(vcpu, i)), ops); + ret = kvm_vgic_map_phys_irq(vcpu, map.direct_vtimer->host_timer_irq, - timer_irq(map.direct_vtimer), - &arch_timer_irq_ops); + timer_irq(map.direct_vtimer)); if (ret) return ret; - if (map.direct_ptimer) { + if (map.direct_ptimer) ret = kvm_vgic_map_phys_irq(vcpu, map.direct_ptimer->host_timer_irq, - timer_irq(map.direct_ptimer), - &arch_timer_irq_ops); - } - + timer_irq(map.direct_ptimer)); if (ret) return ret; @@ -1603,15 +1655,14 @@ int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) if (get_user(irq, uaddr)) return -EFAULT; - if (!(irq_is_ppi(irq))) + if (!(irq_is_ppi(vcpu->kvm, irq))) return -EINVAL; - mutex_lock(&vcpu->kvm->arch.config_lock); + guard(mutex)(&vcpu->kvm->arch.config_lock); if (test_bit(KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE, &vcpu->kvm->arch.flags)) { - ret = -EBUSY; - goto out; + return -EBUSY; } switch (attr->attr) { @@ -1628,8 +1679,7 @@ int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) idx = TIMER_HPTIMER; break; default: - ret = -ENXIO; - goto out; + return -ENXIO; } /* @@ -1639,8 +1689,6 @@ int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) */ vcpu->kvm->arch.timer_data.ppi[idx] = irq; -out: - mutex_unlock(&vcpu->kvm->arch.config_lock); return ret; } diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 410ffd41fd73..34c9950884d5 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -4,6 +4,7 @@ * Author: Christoffer Dall */ +#include #include #include #include @@ -24,6 +25,7 @@ #define CREATE_TRACE_POINTS #include "trace_arm.h" +#include "hyp_trace.h" #include #include @@ -35,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +48,9 @@ #include #include #include +#include + +#include #include "sys_regs.h" @@ -203,6 +209,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) { int ret; + if (type & ~KVM_VM_TYPE_ARM_MASK) + return -EINVAL; + mutex_init(&kvm->arch.config_lock); #ifdef CONFIG_LOCKDEP @@ -234,9 +243,12 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) * If any failures occur after this is successful, make sure to * call __pkvm_unreserve_vm to unreserve the VM in hyp. */ - ret = pkvm_init_host_vm(kvm); + ret = pkvm_init_host_vm(kvm, type); if (ret) - goto err_free_cpumask; + goto err_uninit_mmu; + } else if (type & KVM_VM_TYPE_ARM_PROTECTED) { + ret = -EINVAL; + goto err_uninit_mmu; } kvm_vgic_early_init(kvm); @@ -252,6 +264,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) return 0; +err_uninit_mmu: + kvm_uninit_stage2_mmu(kvm); err_free_cpumask: free_cpumask_var(kvm->arch.supported_cpus); err_unshare_kvm: @@ -301,6 +315,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm) if (is_protected_kvm_enabled()) pkvm_destroy_hyp_vm(kvm); + kvm_uninit_stage2_mmu(kvm); kvm_destroy_mpidr_data(kvm); kfree(kvm->arch.sysreg_masks); @@ -613,6 +628,9 @@ static bool kvm_vcpu_should_clear_twi(struct kvm_vcpu *vcpu) if (unlikely(kvm_wfi_trap_policy != KVM_WFX_NOTRAP_SINGLE_TASK)) return kvm_wfi_trap_policy == KVM_WFX_NOTRAP; + if (vgic_is_v5(vcpu->kvm)) + return single_task_running(); + return single_task_running() && vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3 && (atomic_read(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe.vlpi_count) || @@ -705,6 +723,8 @@ nommu: if (!cpumask_test_cpu(cpu, vcpu->kvm->arch.supported_cpus)) vcpu_set_on_unsupported_cpu(vcpu); + + vcpu->arch.pid = pid_nr(vcpu->pid); } void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) @@ -805,6 +825,10 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) { bool irq_lines = *vcpu_hcr(v) & (HCR_VI | HCR_VF | HCR_VSE); + irq_lines |= (!irqchip_in_kernel(v->kvm) && + (kvm_timer_should_notify_user(v) || + kvm_pmu_should_notify_user(v))); + return ((irq_lines || kvm_vgic_vcpu_pending_irq(v)) && !kvm_arm_vcpu_stopped(v) && !v->arch.pause); } @@ -934,6 +958,10 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) return ret; } + ret = vgic_v5_finalize_ppi_state(kvm); + if (ret) + return ret; + if (is_protected_kvm_enabled()) { ret = pkvm_create_hyp_vm(kvm); if (ret) @@ -1439,10 +1467,11 @@ static int vcpu_interrupt_line(struct kvm_vcpu *vcpu, int number, bool level) int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, bool line_status) { - u32 irq = irq_level->irq; unsigned int irq_type, vcpu_id, irq_num; struct kvm_vcpu *vcpu = NULL; bool level = irq_level->level; + u32 irq = irq_level->irq; + unsigned long *mask; irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK; vcpu_id = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK; @@ -1472,16 +1501,37 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, if (!vcpu) return -EINVAL; - if (irq_num < VGIC_NR_SGIS || irq_num >= VGIC_NR_PRIVATE_IRQS) + if (vgic_is_v5(kvm)) { + if (irq_num >= VGIC_V5_NR_PRIVATE_IRQS) + return -EINVAL; + + /* + * Only allow PPIs that are explicitly exposed to + * usespace to be driven via KVM_IRQ_LINE + */ + mask = kvm->arch.vgic.gicv5_vm.userspace_ppis; + if (!test_bit(irq_num, mask)) + return -EINVAL; + + /* Build a GICv5-style IntID here */ + irq_num = vgic_v5_make_ppi(irq_num); + } else if (irq_num < VGIC_NR_SGIS || + irq_num >= VGIC_NR_PRIVATE_IRQS) { return -EINVAL; + } return kvm_vgic_inject_irq(kvm, vcpu, irq_num, level, NULL); case KVM_ARM_IRQ_TYPE_SPI: if (!irqchip_in_kernel(kvm)) return -ENXIO; - if (irq_num < VGIC_NR_PRIVATE_IRQS) - return -EINVAL; + if (vgic_is_v5(kvm)) { + /* Build a GICv5-style IntID here */ + irq_num = vgic_v5_make_spi(irq_num); + } else { + if (irq_num < VGIC_NR_PRIVATE_IRQS) + return -EINVAL; + } return kvm_vgic_inject_irq(kvm, NULL, irq_num, level, NULL); } @@ -2414,6 +2464,10 @@ static int __init init_subsystems(void) kvm_register_perf_callbacks(); + err = kvm_hyp_trace_init(); + if (err) + kvm_err("Failed to initialize Hyp tracing\n"); + out: if (err) hyp_cpu_pm_exit(); @@ -2465,7 +2519,7 @@ static int __init do_pkvm_init(u32 hyp_va_bits) preempt_disable(); cpu_hyp_init_context(); ret = kvm_call_hyp_nvhe(__pkvm_init, hyp_mem_base, hyp_mem_size, - num_possible_cpus(), kern_hyp_va(per_cpu_base), + kern_hyp_va(per_cpu_base), hyp_va_bits); cpu_hyp_init_features(); @@ -2507,6 +2561,7 @@ static void kvm_hyp_init_symbols(void) { kvm_nvhe_sym(id_aa64pfr0_el1_sys_val) = get_hyp_id_aa64pfr0_el1(); kvm_nvhe_sym(id_aa64pfr1_el1_sys_val) = read_sanitised_ftr_reg(SYS_ID_AA64PFR1_EL1); + kvm_nvhe_sym(id_aa64pfr2_el1_sys_val) = read_sanitised_ftr_reg(SYS_ID_AA64PFR2_EL1); kvm_nvhe_sym(id_aa64isar0_el1_sys_val) = read_sanitised_ftr_reg(SYS_ID_AA64ISAR0_EL1); kvm_nvhe_sym(id_aa64isar1_el1_sys_val) = read_sanitised_ftr_reg(SYS_ID_AA64ISAR1_EL1); kvm_nvhe_sym(id_aa64isar2_el1_sys_val) = read_sanitised_ftr_reg(SYS_ID_AA64ISAR2_EL1); @@ -2529,6 +2584,9 @@ static void kvm_hyp_init_symbols(void) kvm_nvhe_sym(hfgitr2_masks) = hfgitr2_masks; kvm_nvhe_sym(hdfgrtr2_masks)= hdfgrtr2_masks; kvm_nvhe_sym(hdfgwtr2_masks)= hdfgwtr2_masks; + kvm_nvhe_sym(ich_hfgrtr_masks) = ich_hfgrtr_masks; + kvm_nvhe_sym(ich_hfgwtr_masks) = ich_hfgwtr_masks; + kvm_nvhe_sym(ich_hfgitr_masks) = ich_hfgitr_masks; /* * Flush entire BSS since part of its data containing init symbols is read @@ -2581,6 +2639,22 @@ static int init_pkvm_host_sve_state(void) return 0; } +static int pkvm_check_sme_dvmsync_fw_call(void) +{ + struct arm_smccc_res res; + + if (!cpus_have_final_cap(ARM64_WORKAROUND_4193714)) + return 0; + + arm_smccc_1_1_smc(ARM_SMCCC_CPU_WORKAROUND_4193714, &res); + if (res.a0) { + kvm_err("pKVM requires firmware support for C1-Pro erratum 4193714\n"); + return -ENODEV; + } + + return 0; +} + /* * Finalizes the initialization of hyp mode, once everything else is initialized * and the initialziation process cannot fail. @@ -2674,6 +2748,8 @@ static int __init init_hyp_mode(void) kvm_nvhe_sym(kvm_arm_hyp_percpu_base)[cpu] = (unsigned long)page_addr; } + kvm_nvhe_sym(hyp_nr_cpus) = num_possible_cpus(); + /* * Map the Hyp-code called directly from the host */ @@ -2779,6 +2855,10 @@ static int __init init_hyp_mode(void) if (err) goto out_err; + err = pkvm_check_sme_dvmsync_fw_call(); + if (err) + goto out_err; + err = kvm_hyp_init_protection(hyp_va_bits); if (err) { kvm_err("Failed to init hyp memory protection\n"); diff --git a/arch/arm64/kvm/config.c b/arch/arm64/kvm/config.c index d9f553cbf9df..0622162b089e 100644 --- a/arch/arm64/kvm/config.c +++ b/arch/arm64/kvm/config.c @@ -131,7 +131,6 @@ struct reg_feat_map_desc { } #define FEAT_SPE ID_AA64DFR0_EL1, PMSVer, IMP -#define FEAT_SPE_FnE ID_AA64DFR0_EL1, PMSVer, V1P2 #define FEAT_BRBE ID_AA64DFR0_EL1, BRBE, IMP #define FEAT_TRC_SR ID_AA64DFR0_EL1, TraceVer, IMP #define FEAT_PMUv3 ID_AA64DFR0_EL1, PMUVer, IMP @@ -192,7 +191,7 @@ struct reg_feat_map_desc { #define FEAT_SRMASK ID_AA64MMFR4_EL1, SRMASK, IMP #define FEAT_PoPS ID_AA64MMFR4_EL1, PoPS, IMP #define FEAT_PFAR ID_AA64PFR1_EL1, PFAR, IMP -#define FEAT_Debugv8p9 ID_AA64DFR0_EL1, PMUVer, V3P9 +#define FEAT_Debugv8p9 ID_AA64DFR0_EL1, DebugVer, V8P9 #define FEAT_PMUv3_SS ID_AA64DFR0_EL1, PMSS, IMP #define FEAT_SEBEP ID_AA64DFR0_EL1, SEBEP, IMP #define FEAT_EBEP ID_AA64DFR1_EL1, EBEP, IMP @@ -225,6 +224,7 @@ struct reg_feat_map_desc { #define FEAT_MTPMU ID_AA64DFR0_EL1, MTPMU, IMP #define FEAT_HCX ID_AA64MMFR1_EL1, HCX, IMP #define FEAT_S2PIE ID_AA64MMFR3_EL1, S2PIE, IMP +#define FEAT_GCIE ID_AA64PFR2_EL1, GCIE, IMP static bool not_feat_aa64el3(struct kvm *kvm) { @@ -282,7 +282,7 @@ static bool feat_anerr(struct kvm *kvm) static bool feat_sme_smps(struct kvm *kvm) { /* - * Revists this if KVM ever supports SME -- this really should + * Revisit this if KVM ever supports SME -- this really should * look at the guest's view of SMIDR_EL1. Funnily enough, this * is not captured in the JSON file, but only as a note in the * ARM ARM. @@ -294,17 +294,27 @@ static bool feat_sme_smps(struct kvm *kvm) static bool feat_spe_fds(struct kvm *kvm) { /* - * Revists this if KVM ever supports SPE -- this really should + * Revisit this if KVM ever supports SPE -- this really should * look at the guest's view of PMSIDR_EL1. */ return (kvm_has_feat(kvm, FEAT_SPEv1p4) && (read_sysreg_s(SYS_PMSIDR_EL1) & PMSIDR_EL1_FDS)); } +static bool feat_spe_fne(struct kvm *kvm) +{ + /* + * Revisit this if KVM ever supports SPE -- this really should + * look at the guest's view of PMSIDR_EL1. + */ + return (kvm_has_feat(kvm, FEAT_SPEv1p2) && + (read_sysreg_s(SYS_PMSIDR_EL1) & PMSIDR_EL1_FnE)); +} + static bool feat_trbe_mpam(struct kvm *kvm) { /* - * Revists this if KVM ever supports both MPAM and TRBE -- + * Revisit this if KVM ever supports both MPAM and TRBE -- * this really should look at the guest's view of TRBIDR_EL1. */ return (kvm_has_feat(kvm, FEAT_TRBE) && @@ -536,7 +546,7 @@ static const struct reg_bits_to_feat_map hdfgrtr_feat_map[] = { HDFGRTR_EL2_PMBPTR_EL1 | HDFGRTR_EL2_PMBLIMITR_EL1, FEAT_SPE), - NEEDS_FEAT(HDFGRTR_EL2_nPMSNEVFR_EL1, FEAT_SPE_FnE), + NEEDS_FEAT(HDFGRTR_EL2_nPMSNEVFR_EL1, feat_spe_fne), NEEDS_FEAT(HDFGRTR_EL2_nBRBDATA | HDFGRTR_EL2_nBRBCTL | HDFGRTR_EL2_nBRBIDR, @@ -604,7 +614,7 @@ static const struct reg_bits_to_feat_map hdfgwtr_feat_map[] = { HDFGWTR_EL2_PMBPTR_EL1 | HDFGWTR_EL2_PMBLIMITR_EL1, FEAT_SPE), - NEEDS_FEAT(HDFGWTR_EL2_nPMSNEVFR_EL1, FEAT_SPE_FnE), + NEEDS_FEAT(HDFGWTR_EL2_nPMSNEVFR_EL1, feat_spe_fne), NEEDS_FEAT(HDFGWTR_EL2_nBRBDATA | HDFGWTR_EL2_nBRBCTL, FEAT_BRBE), @@ -1277,6 +1287,58 @@ static const struct reg_bits_to_feat_map vtcr_el2_feat_map[] = { static const DECLARE_FEAT_MAP(vtcr_el2_desc, VTCR_EL2, vtcr_el2_feat_map, FEAT_AA64EL2); +static const struct reg_bits_to_feat_map ich_hfgrtr_feat_map[] = { + NEEDS_FEAT(ICH_HFGRTR_EL2_ICC_APR_EL1 | + ICH_HFGRTR_EL2_ICC_IDRn_EL1 | + ICH_HFGRTR_EL2_ICC_CR0_EL1 | + ICH_HFGRTR_EL2_ICC_HPPIR_EL1 | + ICH_HFGRTR_EL2_ICC_PCR_EL1 | + ICH_HFGRTR_EL2_ICC_ICSR_EL1 | + ICH_HFGRTR_EL2_ICC_IAFFIDR_EL1 | + ICH_HFGRTR_EL2_ICC_PPI_HMRn_EL1 | + ICH_HFGRTR_EL2_ICC_PPI_ENABLERn_EL1 | + ICH_HFGRTR_EL2_ICC_PPI_PENDRn_EL1 | + ICH_HFGRTR_EL2_ICC_PPI_PRIORITYRn_EL1 | + ICH_HFGRTR_EL2_ICC_PPI_ACTIVERn_EL1, + FEAT_GCIE), +}; + +static const DECLARE_FEAT_MAP_FGT(ich_hfgrtr_desc, ich_hfgrtr_masks, + ich_hfgrtr_feat_map, FEAT_GCIE); + +static const struct reg_bits_to_feat_map ich_hfgwtr_feat_map[] = { + NEEDS_FEAT(ICH_HFGWTR_EL2_ICC_APR_EL1 | + ICH_HFGWTR_EL2_ICC_CR0_EL1 | + ICH_HFGWTR_EL2_ICC_PCR_EL1 | + ICH_HFGWTR_EL2_ICC_ICSR_EL1 | + ICH_HFGWTR_EL2_ICC_PPI_ENABLERn_EL1 | + ICH_HFGWTR_EL2_ICC_PPI_PENDRn_EL1 | + ICH_HFGWTR_EL2_ICC_PPI_PRIORITYRn_EL1 | + ICH_HFGWTR_EL2_ICC_PPI_ACTIVERn_EL1, + FEAT_GCIE), +}; + +static const DECLARE_FEAT_MAP_FGT(ich_hfgwtr_desc, ich_hfgwtr_masks, + ich_hfgwtr_feat_map, FEAT_GCIE); + +static const struct reg_bits_to_feat_map ich_hfgitr_feat_map[] = { + NEEDS_FEAT(ICH_HFGITR_EL2_GICCDEN | + ICH_HFGITR_EL2_GICCDDIS | + ICH_HFGITR_EL2_GICCDPRI | + ICH_HFGITR_EL2_GICCDAFF | + ICH_HFGITR_EL2_GICCDPEND | + ICH_HFGITR_EL2_GICCDRCFG | + ICH_HFGITR_EL2_GICCDHM | + ICH_HFGITR_EL2_GICCDEOI | + ICH_HFGITR_EL2_GICCDDI | + ICH_HFGITR_EL2_GICRCDIA | + ICH_HFGITR_EL2_GICRCDNMIA, + FEAT_GCIE), +}; + +static const DECLARE_FEAT_MAP_FGT(ich_hfgitr_desc, ich_hfgitr_masks, + ich_hfgitr_feat_map, FEAT_GCIE); + static void __init check_feat_map(const struct reg_bits_to_feat_map *map, int map_size, u64 resx, const char *str) { @@ -1328,6 +1390,9 @@ void __init check_feature_map(void) check_reg_desc(&sctlr_el2_desc); check_reg_desc(&mdcr_el2_desc); check_reg_desc(&vtcr_el2_desc); + check_reg_desc(&ich_hfgrtr_desc); + check_reg_desc(&ich_hfgwtr_desc); + check_reg_desc(&ich_hfgitr_desc); } static bool idreg_feat_match(struct kvm *kvm, const struct reg_bits_to_feat_map *map) @@ -1460,6 +1525,13 @@ void compute_fgu(struct kvm *kvm, enum fgt_group_id fgt) val |= compute_fgu_bits(kvm, &hdfgrtr2_desc); val |= compute_fgu_bits(kvm, &hdfgwtr2_desc); break; + case ICH_HFGRTR_GROUP: + val |= compute_fgu_bits(kvm, &ich_hfgrtr_desc); + val |= compute_fgu_bits(kvm, &ich_hfgwtr_desc); + break; + case ICH_HFGITR_GROUP: + val |= compute_fgu_bits(kvm, &ich_hfgitr_desc); + break; default: BUG(); } @@ -1531,6 +1603,15 @@ struct resx get_reg_fixed_bits(struct kvm *kvm, enum vcpu_sysreg reg) case VTCR_EL2: resx = compute_reg_resx_bits(kvm, &vtcr_el2_desc, 0, 0); break; + case ICH_HFGRTR_EL2: + resx = compute_reg_resx_bits(kvm, &ich_hfgrtr_desc, 0, 0); + break; + case ICH_HFGWTR_EL2: + resx = compute_reg_resx_bits(kvm, &ich_hfgwtr_desc, 0, 0); + break; + case ICH_HFGITR_EL2: + resx = compute_reg_resx_bits(kvm, &ich_hfgitr_desc, 0, 0); + break; default: WARN_ON_ONCE(1); resx = (typeof(resx)){}; @@ -1565,6 +1646,12 @@ static __always_inline struct fgt_masks *__fgt_reg_to_masks(enum vcpu_sysreg reg return &hdfgrtr2_masks; case HDFGWTR2_EL2: return &hdfgwtr2_masks; + case ICH_HFGRTR_EL2: + return &ich_hfgrtr_masks; + case ICH_HFGWTR_EL2: + return &ich_hfgwtr_masks; + case ICH_HFGITR_EL2: + return &ich_hfgitr_masks; default: BUILD_BUG_ON(1); } @@ -1585,8 +1672,8 @@ static __always_inline void __compute_fgt(struct kvm_vcpu *vcpu, enum vcpu_sysre clear |= ~nested & m->nmask; } - val |= set; - val &= ~clear; + val |= set | m->res1; + val &= ~(clear | m->res0); *vcpu_fgt(vcpu, reg) = val; } @@ -1606,6 +1693,32 @@ static void __compute_hdfgwtr(struct kvm_vcpu *vcpu) *vcpu_fgt(vcpu, HDFGWTR_EL2) |= HDFGWTR_EL2_MDSCR_EL1; } +static void __compute_ich_hfgrtr(struct kvm_vcpu *vcpu) +{ + __compute_fgt(vcpu, ICH_HFGRTR_EL2); + + /* + * ICC_IAFFIDR_EL1 *always* needs to be trapped when running a guest. + * + * We also trap accesses to ICC_IDR0_EL1 to allow us to completely hide + * FEAT_GCIE_LEGACY from the guest, and to (potentially) present fewer + * ID bits than the host supports. + */ + *vcpu_fgt(vcpu, ICH_HFGRTR_EL2) &= ~(ICH_HFGRTR_EL2_ICC_IAFFIDR_EL1 | + ICH_HFGRTR_EL2_ICC_IDRn_EL1); +} + +static void __compute_ich_hfgwtr(struct kvm_vcpu *vcpu) +{ + __compute_fgt(vcpu, ICH_HFGWTR_EL2); + + /* + * We present a different subset of PPIs the guest from what + * exist in real hardware. We only trap writes, not reads. + */ + *vcpu_fgt(vcpu, ICH_HFGWTR_EL2) &= ~(ICH_HFGWTR_EL2_ICC_PPI_ENABLERn_EL1); +} + void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu) { if (!cpus_have_final_cap(ARM64_HAS_FGT)) @@ -1618,12 +1731,17 @@ void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu) __compute_hdfgwtr(vcpu); __compute_fgt(vcpu, HAFGRTR_EL2); - if (!cpus_have_final_cap(ARM64_HAS_FGT2)) - return; + if (cpus_have_final_cap(ARM64_HAS_FGT2)) { + __compute_fgt(vcpu, HFGRTR2_EL2); + __compute_fgt(vcpu, HFGWTR2_EL2); + __compute_fgt(vcpu, HFGITR2_EL2); + __compute_fgt(vcpu, HDFGRTR2_EL2); + __compute_fgt(vcpu, HDFGWTR2_EL2); + } - __compute_fgt(vcpu, HFGRTR2_EL2); - __compute_fgt(vcpu, HFGWTR2_EL2); - __compute_fgt(vcpu, HFGITR2_EL2); - __compute_fgt(vcpu, HDFGRTR2_EL2); - __compute_fgt(vcpu, HDFGWTR2_EL2); + if (cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) { + __compute_ich_hfgrtr(vcpu); + __compute_ich_hfgwtr(vcpu); + __compute_fgt(vcpu, ICH_HFGITR_EL2); + } } diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index 22d497554c94..dba7ced74ca5 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2053,6 +2053,60 @@ static const struct encoding_to_trap_config encoding_to_fgt[] __initconst = { SR_FGT(SYS_AMEVCNTR0_EL0(2), HAFGRTR, AMEVCNTR02_EL0, 1), SR_FGT(SYS_AMEVCNTR0_EL0(1), HAFGRTR, AMEVCNTR01_EL0, 1), SR_FGT(SYS_AMEVCNTR0_EL0(0), HAFGRTR, AMEVCNTR00_EL0, 1), + + /* + * ICH_HFGRTR_EL2 & ICH_HFGWTR_EL2 + */ + SR_FGT(SYS_ICC_APR_EL1, ICH_HFGRTR, ICC_APR_EL1, 0), + SR_FGT(SYS_ICC_IDR0_EL1, ICH_HFGRTR, ICC_IDRn_EL1, 0), + SR_FGT(SYS_ICC_CR0_EL1, ICH_HFGRTR, ICC_CR0_EL1, 0), + SR_FGT(SYS_ICC_HPPIR_EL1, ICH_HFGRTR, ICC_HPPIR_EL1, 0), + SR_FGT(SYS_ICC_PCR_EL1, ICH_HFGRTR, ICC_PCR_EL1, 0), + SR_FGT(SYS_ICC_ICSR_EL1, ICH_HFGRTR, ICC_ICSR_EL1, 0), + SR_FGT(SYS_ICC_IAFFIDR_EL1, ICH_HFGRTR, ICC_IAFFIDR_EL1, 0), + SR_FGT(SYS_ICC_PPI_HMR0_EL1, ICH_HFGRTR, ICC_PPI_HMRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_HMR1_EL1, ICH_HFGRTR, ICC_PPI_HMRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_ENABLER0_EL1, ICH_HFGRTR, ICC_PPI_ENABLERn_EL1, 0), + SR_FGT(SYS_ICC_PPI_ENABLER1_EL1, ICH_HFGRTR, ICC_PPI_ENABLERn_EL1, 0), + SR_FGT(SYS_ICC_PPI_CPENDR0_EL1, ICH_HFGRTR, ICC_PPI_PENDRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_CPENDR1_EL1, ICH_HFGRTR, ICC_PPI_PENDRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_SPENDR0_EL1, ICH_HFGRTR, ICC_PPI_PENDRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_SPENDR1_EL1, ICH_HFGRTR, ICC_PPI_PENDRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR0_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR1_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR2_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR3_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR4_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR5_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR6_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR7_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR8_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR9_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR10_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR11_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR12_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR13_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR14_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_PRIORITYR15_EL1, ICH_HFGRTR, ICC_PPI_PRIORITYRn_EL1, 0), + SR_FGT(SYS_ICC_PPI_CACTIVER0_EL1, ICH_HFGRTR, ICC_PPI_ACTIVERn_EL1, 0), + SR_FGT(SYS_ICC_PPI_CACTIVER1_EL1, ICH_HFGRTR, ICC_PPI_ACTIVERn_EL1, 0), + SR_FGT(SYS_ICC_PPI_SACTIVER0_EL1, ICH_HFGRTR, ICC_PPI_ACTIVERn_EL1, 0), + SR_FGT(SYS_ICC_PPI_SACTIVER1_EL1, ICH_HFGRTR, ICC_PPI_ACTIVERn_EL1, 0), + + /* + * ICH_HFGITR_EL2 + */ + SR_FGT(GICV5_OP_GIC_CDEN, ICH_HFGITR, GICCDEN, 0), + SR_FGT(GICV5_OP_GIC_CDDIS, ICH_HFGITR, GICCDDIS, 0), + SR_FGT(GICV5_OP_GIC_CDPRI, ICH_HFGITR, GICCDPRI, 0), + SR_FGT(GICV5_OP_GIC_CDAFF, ICH_HFGITR, GICCDAFF, 0), + SR_FGT(GICV5_OP_GIC_CDPEND, ICH_HFGITR, GICCDPEND, 0), + SR_FGT(GICV5_OP_GIC_CDRCFG, ICH_HFGITR, GICCDRCFG, 0), + SR_FGT(GICV5_OP_GIC_CDHM, ICH_HFGITR, GICCDHM, 0), + SR_FGT(GICV5_OP_GIC_CDEOI, ICH_HFGITR, GICCDEOI, 0), + SR_FGT(GICV5_OP_GIC_CDDI, ICH_HFGITR, GICCDDI, 0), + SR_FGT(GICV5_OP_GICR_CDIA, ICH_HFGITR, GICRCDIA, 0), + SR_FGT(GICV5_OP_GICR_CDNMIA, ICH_HFGITR, GICRCDNMIA, 0), }; /* @@ -2127,6 +2181,9 @@ FGT_MASKS(hfgwtr2_masks, HFGWTR2_EL2); FGT_MASKS(hfgitr2_masks, HFGITR2_EL2); FGT_MASKS(hdfgrtr2_masks, HDFGRTR2_EL2); FGT_MASKS(hdfgwtr2_masks, HDFGWTR2_EL2); +FGT_MASKS(ich_hfgrtr_masks, ICH_HFGRTR_EL2); +FGT_MASKS(ich_hfgwtr_masks, ICH_HFGWTR_EL2); +FGT_MASKS(ich_hfgitr_masks, ICH_HFGITR_EL2); static __init bool aggregate_fgt(union trap_config tc) { @@ -2162,6 +2219,14 @@ static __init bool aggregate_fgt(union trap_config tc) rmasks = &hfgitr2_masks; wmasks = NULL; break; + case ICH_HFGRTR_GROUP: + rmasks = &ich_hfgrtr_masks; + wmasks = &ich_hfgwtr_masks; + break; + case ICH_HFGITR_GROUP: + rmasks = &ich_hfgitr_masks; + wmasks = NULL; + break; } rresx = rmasks->res0 | rmasks->res1; @@ -2232,6 +2297,9 @@ static __init int check_all_fgt_masks(int ret) &hfgitr2_masks, &hdfgrtr2_masks, &hdfgwtr2_masks, + &ich_hfgrtr_masks, + &ich_hfgwtr_masks, + &ich_hfgitr_masks, }; int err = 0; diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c index cc7d5d1709cb..54aedf93c78b 100644 --- a/arch/arm64/kvm/handle_exit.c +++ b/arch/arm64/kvm/handle_exit.c @@ -539,7 +539,7 @@ void __noreturn __cold nvhe_hyp_panic_handler(u64 esr, u64 spsr, /* All hyp bugs, including warnings, are treated as fatal. */ if (!is_protected_kvm_enabled() || - IS_ENABLED(CONFIG_NVHE_EL2_DEBUG)) { + IS_ENABLED(CONFIG_PKVM_DISABLE_STAGE2_ON_PANIC)) { struct bug_entry *bug = find_bug(elr_in_kimg); if (bug) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 0b50ddd530f3..bf0eb5e43427 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -233,7 +233,19 @@ static inline void __activate_traps_hfgxtr(struct kvm_vcpu *vcpu) __activate_fgt(hctxt, vcpu, HDFGWTR2_EL2); } -#define __deactivate_fgt(htcxt, vcpu, reg) \ +static inline void __activate_traps_ich_hfgxtr(struct kvm_vcpu *vcpu) +{ + struct kvm_cpu_context *hctxt = host_data_ptr(host_ctxt); + + if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) + return; + + __activate_fgt(hctxt, vcpu, ICH_HFGRTR_EL2); + __activate_fgt(hctxt, vcpu, ICH_HFGWTR_EL2); + __activate_fgt(hctxt, vcpu, ICH_HFGITR_EL2); +} + +#define __deactivate_fgt(hctxt, vcpu, reg) \ do { \ write_sysreg_s(ctxt_sys_reg(hctxt, reg), \ SYS_ ## reg); \ @@ -265,6 +277,19 @@ static inline void __deactivate_traps_hfgxtr(struct kvm_vcpu *vcpu) __deactivate_fgt(hctxt, vcpu, HDFGWTR2_EL2); } +static inline void __deactivate_traps_ich_hfgxtr(struct kvm_vcpu *vcpu) +{ + struct kvm_cpu_context *hctxt = host_data_ptr(host_ctxt); + + if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) + return; + + __deactivate_fgt(hctxt, vcpu, ICH_HFGRTR_EL2); + __deactivate_fgt(hctxt, vcpu, ICH_HFGWTR_EL2); + __deactivate_fgt(hctxt, vcpu, ICH_HFGITR_EL2); + +} + static inline void __activate_traps_mpam(struct kvm_vcpu *vcpu) { u64 clr = MPAM2_EL2_EnMPAMSM; @@ -332,6 +357,7 @@ static inline void __activate_traps_common(struct kvm_vcpu *vcpu) } __activate_traps_hfgxtr(vcpu); + __activate_traps_ich_hfgxtr(vcpu); __activate_traps_mpam(vcpu); } @@ -349,6 +375,7 @@ static inline void __deactivate_traps_common(struct kvm_vcpu *vcpu) write_sysreg_s(ctxt_sys_reg(hctxt, HCRX_EL2), SYS_HCRX_EL2); __deactivate_traps_hfgxtr(vcpu); + __deactivate_traps_ich_hfgxtr(vcpu); __deactivate_traps_mpam(); } diff --git a/arch/arm64/kvm/hyp/include/nvhe/arm-smccc.h b/arch/arm64/kvm/hyp/include/nvhe/arm-smccc.h new file mode 100644 index 000000000000..1258bc84477f --- /dev/null +++ b/arch/arm64/kvm/hyp/include/nvhe/arm-smccc.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ARM64_KVM_HYP_NVHE_ARM_SMCCC_H__ +#define __ARM64_KVM_HYP_NVHE_ARM_SMCCC_H__ + +#include + +#include + +#define hyp_smccc_1_1_smc(...) \ + do { \ + trace_hyp_exit(NULL, HYP_REASON_SMC); \ + arm_smccc_1_1_smc(__VA_ARGS__); \ + trace_hyp_enter(NULL, HYP_REASON_SMC); \ + } while (0) + +#define hyp_smccc_1_2_smc(...) \ + do { \ + trace_hyp_exit(NULL, HYP_REASON_SMC); \ + arm_smccc_1_2_smc(__VA_ARGS__); \ + trace_hyp_enter(NULL, HYP_REASON_SMC); \ + } while (0) + +#endif /* __ARM64_KVM_HYP_NVHE_ARM_SMCCC_H__ */ diff --git a/arch/arm64/kvm/hyp/include/nvhe/clock.h b/arch/arm64/kvm/hyp/include/nvhe/clock.h new file mode 100644 index 000000000000..9f429f5c0664 --- /dev/null +++ b/arch/arm64/kvm/hyp/include/nvhe/clock.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ARM64_KVM_HYP_NVHE_CLOCK_H +#define __ARM64_KVM_HYP_NVHE_CLOCK_H +#include + +#include + +#ifdef CONFIG_NVHE_EL2_TRACING +void trace_clock_update(u32 mult, u32 shift, u64 epoch_ns, u64 epoch_cyc); +u64 trace_clock(void); +#else +static inline void +trace_clock_update(u32 mult, u32 shift, u64 epoch_ns, u64 epoch_cyc) { } +static inline u64 trace_clock(void) { return 0; } +#endif +#endif diff --git a/arch/arm64/kvm/hyp/include/nvhe/define_events.h b/arch/arm64/kvm/hyp/include/nvhe/define_events.h new file mode 100644 index 000000000000..776d4c6cb702 --- /dev/null +++ b/arch/arm64/kvm/hyp/include/nvhe/define_events.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#undef HYP_EVENT +#define HYP_EVENT(__name, __proto, __struct, __assign, __printk) \ + struct hyp_event_id hyp_event_id_##__name \ + __section(".hyp.event_ids."#__name) = { \ + .enabled = ATOMIC_INIT(0), \ + } + +#define HYP_EVENT_MULTI_READ +#include +#undef HYP_EVENT_MULTI_READ + +#undef HYP_EVENT diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h index 5f9d56754e39..3cbfae0e3dda 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h @@ -27,18 +27,22 @@ extern struct host_mmu host_mmu; enum pkvm_component_id { PKVM_ID_HOST, PKVM_ID_HYP, - PKVM_ID_FFA, + PKVM_ID_GUEST, }; -extern unsigned long hyp_nr_cpus; - int __pkvm_prot_finalize(void); int __pkvm_host_share_hyp(u64 pfn); +int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn); +int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn); int __pkvm_host_unshare_hyp(u64 pfn); int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages); int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages); int __pkvm_host_share_ffa(u64 pfn, u64 nr_pages); int __pkvm_host_unshare_ffa(u64 pfn, u64 nr_pages); +int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu); +int __pkvm_vcpu_in_poison_fault(struct pkvm_hyp_vcpu *hyp_vcpu); +int __pkvm_host_force_reclaim_page_guest(phys_addr_t phys); +int __pkvm_host_reclaim_page_guest(u64 gfn, struct pkvm_hyp_vm *vm); int __pkvm_host_share_guest(u64 pfn, u64 gfn, u64 nr_pages, struct pkvm_hyp_vcpu *vcpu, enum kvm_pgtable_prot prot); int __pkvm_host_unshare_guest(u64 gfn, u64 nr_pages, struct pkvm_hyp_vm *hyp_vm); @@ -70,6 +74,8 @@ static __always_inline void __load_host_stage2(void) #ifdef CONFIG_NVHE_EL2_DEBUG void pkvm_ownership_selftest(void *base); +struct pkvm_hyp_vcpu *init_selftest_vm(void *virt); +void teardown_selftest_vm(void); #else static inline void pkvm_ownership_selftest(void *base) { } #endif diff --git a/arch/arm64/kvm/hyp/include/nvhe/memory.h b/arch/arm64/kvm/hyp/include/nvhe/memory.h index dee1a406b0c2..b50712d47f6d 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/memory.h +++ b/arch/arm64/kvm/hyp/include/nvhe/memory.h @@ -30,8 +30,14 @@ enum pkvm_page_state { * struct hyp_page. */ PKVM_NOPAGE = BIT(0) | BIT(1), + + /* + * 'Meta-states' which aren't encoded directly in the PTE's SW bits (or + * the hyp_vmemmap entry for the host) + */ + PKVM_POISON = BIT(2), }; -#define PKVM_PAGE_STATE_MASK (BIT(0) | BIT(1)) +#define PKVM_PAGE_STATE_VMEMMAP_MASK (BIT(0) | BIT(1)) #define PKVM_PAGE_STATE_PROT_MASK (KVM_PGTABLE_PROT_SW0 | KVM_PGTABLE_PROT_SW1) static inline enum kvm_pgtable_prot pkvm_mkstate(enum kvm_pgtable_prot prot, @@ -108,12 +114,12 @@ static inline void set_host_state(struct hyp_page *p, enum pkvm_page_state state static inline enum pkvm_page_state get_hyp_state(struct hyp_page *p) { - return p->__hyp_state_comp ^ PKVM_PAGE_STATE_MASK; + return p->__hyp_state_comp ^ PKVM_PAGE_STATE_VMEMMAP_MASK; } static inline void set_hyp_state(struct hyp_page *p, enum pkvm_page_state state) { - p->__hyp_state_comp = state ^ PKVM_PAGE_STATE_MASK; + p->__hyp_state_comp = state ^ PKVM_PAGE_STATE_VMEMMAP_MASK; } /* diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h index 184ad7a39950..c904647d2f76 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h +++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h @@ -73,8 +73,12 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva, unsigned long pgd_hva); int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu, unsigned long vcpu_hva); -int __pkvm_teardown_vm(pkvm_handle_t handle); +int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 gfn); +int __pkvm_start_teardown_vm(pkvm_handle_t handle); +int __pkvm_finalize_teardown_vm(pkvm_handle_t handle); + +struct pkvm_hyp_vm *get_vm_by_handle(pkvm_handle_t handle); struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle, unsigned int vcpu_idx); void pkvm_put_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu); @@ -84,6 +88,7 @@ struct pkvm_hyp_vm *get_pkvm_hyp_vm(pkvm_handle_t handle); struct pkvm_hyp_vm *get_np_pkvm_hyp_vm(pkvm_handle_t handle); void put_pkvm_hyp_vm(struct pkvm_hyp_vm *hyp_vm); +bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code); bool kvm_handle_pvm_sysreg(struct kvm_vcpu *vcpu, u64 *exit_code); bool kvm_handle_pvm_restricted(struct kvm_vcpu *vcpu, u64 *exit_code); void kvm_init_pvm_id_regs(struct kvm_vcpu *vcpu); diff --git a/arch/arm64/kvm/hyp/include/nvhe/trace.h b/arch/arm64/kvm/hyp/include/nvhe/trace.h new file mode 100644 index 000000000000..8813ff250f8e --- /dev/null +++ b/arch/arm64/kvm/hyp/include/nvhe/trace.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ARM64_KVM_HYP_NVHE_TRACE_H +#define __ARM64_KVM_HYP_NVHE_TRACE_H + +#include + +#include + +static inline pid_t __tracing_get_vcpu_pid(struct kvm_cpu_context *host_ctxt) +{ + struct kvm_vcpu *vcpu; + + if (!host_ctxt) + host_ctxt = host_data_ptr(host_ctxt); + + vcpu = host_ctxt->__hyp_running_vcpu; + + return vcpu ? vcpu->arch.pid : 0; +} + +#define HE_PROTO(__args...) __args +#define HE_ASSIGN(__args...) __args +#define HE_STRUCT RE_STRUCT +#define he_field re_field + +#ifdef CONFIG_NVHE_EL2_TRACING + +#define HYP_EVENT(__name, __proto, __struct, __assign, __printk) \ + REMOTE_EVENT_FORMAT(__name, __struct); \ + extern struct hyp_event_id hyp_event_id_##__name; \ + static __always_inline void trace_##__name(__proto) \ + { \ + struct remote_event_format_##__name *__entry; \ + size_t length = sizeof(*__entry); \ + \ + if (!atomic_read(&hyp_event_id_##__name.enabled)) \ + return; \ + __entry = tracing_reserve_entry(length); \ + if (!__entry) \ + return; \ + __entry->hdr.id = hyp_event_id_##__name.id; \ + __assign \ + tracing_commit_entry(); \ + } + +void *tracing_reserve_entry(unsigned long length); +void tracing_commit_entry(void); + +int __tracing_load(unsigned long desc_va, size_t desc_size); +void __tracing_unload(void); +int __tracing_enable(bool enable); +int __tracing_swap_reader(unsigned int cpu); +void __tracing_update_clock(u32 mult, u32 shift, u64 epoch_ns, u64 epoch_cyc); +int __tracing_reset(unsigned int cpu); +int __tracing_enable_event(unsigned short id, bool enable); +#else +static inline void *tracing_reserve_entry(unsigned long length) { return NULL; } +static inline void tracing_commit_entry(void) { } +#define HYP_EVENT(__name, __proto, __struct, __assign, __printk) \ + static inline void trace_##__name(__proto) {} + +static inline int __tracing_load(unsigned long desc_va, size_t desc_size) { return -ENODEV; } +static inline void __tracing_unload(void) { } +static inline int __tracing_enable(bool enable) { return -ENODEV; } +static inline int __tracing_swap_reader(unsigned int cpu) { return -ENODEV; } +static inline void __tracing_update_clock(u32 mult, u32 shift, u64 epoch_ns, u64 epoch_cyc) { } +static inline int __tracing_reset(unsigned int cpu) { return -ENODEV; } +static inline int __tracing_enable_event(unsigned short id, bool enable) { return -ENODEV; } +#endif +#endif diff --git a/arch/arm64/kvm/hyp/include/nvhe/trap_handler.h b/arch/arm64/kvm/hyp/include/nvhe/trap_handler.h index ba5382c12787..32d7b7746e8e 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/trap_handler.h +++ b/arch/arm64/kvm/hyp/include/nvhe/trap_handler.h @@ -16,4 +16,6 @@ __always_unused int ___check_reg_ ## reg; \ type name = (type)cpu_reg(ctxt, (reg)) +void inject_host_exception(u64 esr); + #endif /* __ARM64_KVM_NVHE_TRAP_HANDLER_H__ */ diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile index a244ec25f8c5..62cdfbff7562 100644 --- a/arch/arm64/kvm/hyp/nvhe/Makefile +++ b/arch/arm64/kvm/hyp/nvhe/Makefile @@ -17,7 +17,7 @@ ccflags-y += -fno-stack-protector \ hostprogs := gen-hyprel HOST_EXTRACFLAGS += -I$(objtree)/include -lib-objs := clear_page.o copy_page.o memcpy.o memset.o +lib-objs := clear_page.o copy_page.o memcpy.o memset.o tishift.o lib-objs := $(addprefix ../../../lib/, $(lib-objs)) CFLAGS_switch.nvhe.o += -Wno-override-init @@ -26,11 +26,15 @@ hyp-obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o hyp-init.o host.o hyp-main.o hyp-smp.o psci-relay.o early_alloc.o page_alloc.o \ cache.o setup.o mm.o mem_protect.o sys_regs.o pkvm.o stacktrace.o ffa.o hyp-obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \ - ../fpsimd.o ../hyp-entry.o ../exception.o ../pgtable.o + ../fpsimd.o ../hyp-entry.o ../exception.o ../pgtable.o ../vgic-v5-sr.o hyp-obj-y += ../../../kernel/smccc-call.o hyp-obj-$(CONFIG_LIST_HARDENED) += list_debug.o +hyp-obj-$(CONFIG_NVHE_EL2_TRACING) += clock.o trace.o events.o hyp-obj-y += $(lib-objs) +# Path to simple_ring_buffer.c +CFLAGS_trace.nvhe.o += -I$(srctree)/kernel/trace/ + ## ## Build rules for compiling nVHE hyp code ## Output of this folder is `kvm_nvhe.o`, a partially linked object diff --git a/arch/arm64/kvm/hyp/nvhe/clock.c b/arch/arm64/kvm/hyp/nvhe/clock.c new file mode 100644 index 000000000000..a7fc61976fd0 --- /dev/null +++ b/arch/arm64/kvm/hyp/nvhe/clock.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Google LLC + * Author: Vincent Donnefort + */ + +#include + +#include +#include + +static struct clock_data { + struct { + u32 mult; + u32 shift; + u64 epoch_ns; + u64 epoch_cyc; + u64 cyc_overflow64; + } data[2]; + u64 cur; +} trace_clock_data; + +static u64 __clock_mult_uint128(u64 cyc, u32 mult, u32 shift) +{ + __uint128_t ns = (__uint128_t)cyc * mult; + + ns >>= shift; + + return (u64)ns; +} + +/* Does not guarantee no reader on the modified bank. */ +void trace_clock_update(u32 mult, u32 shift, u64 epoch_ns, u64 epoch_cyc) +{ + struct clock_data *clock = &trace_clock_data; + u64 bank = clock->cur ^ 1; + + if (!mult || shift >= 64) + return; + + clock->data[bank].mult = mult; + clock->data[bank].shift = shift; + clock->data[bank].epoch_ns = epoch_ns; + clock->data[bank].epoch_cyc = epoch_cyc; + clock->data[bank].cyc_overflow64 = ULONG_MAX / mult; + + smp_store_release(&clock->cur, bank); +} + +/* Use untrusted host data */ +u64 trace_clock(void) +{ + struct clock_data *clock = &trace_clock_data; + u64 bank = smp_load_acquire(&clock->cur); + u64 cyc, ns; + + cyc = __arch_counter_get_cntvct() - clock->data[bank].epoch_cyc; + + if (likely(cyc < clock->data[bank].cyc_overflow64)) { + ns = cyc * clock->data[bank].mult; + ns >>= clock->data[bank].shift; + } else { + ns = __clock_mult_uint128(cyc, clock->data[bank].mult, + clock->data[bank].shift); + } + + return (u64)ns + clock->data[bank].epoch_ns; +} diff --git a/arch/arm64/kvm/hyp/nvhe/debug-sr.c b/arch/arm64/kvm/hyp/nvhe/debug-sr.c index 2a1c0f49792b..f8904391c125 100644 --- a/arch/arm64/kvm/hyp/nvhe/debug-sr.c +++ b/arch/arm64/kvm/hyp/nvhe/debug-sr.c @@ -14,20 +14,20 @@ #include #include -static void __debug_save_spe(u64 *pmscr_el1) +static void __debug_save_spe(void) { - u64 reg; + u64 *pmscr_el1, *pmblimitr_el1; - /* Clear pmscr in case of early return */ - *pmscr_el1 = 0; + pmscr_el1 = host_data_ptr(host_debug_state.pmscr_el1); + pmblimitr_el1 = host_data_ptr(host_debug_state.pmblimitr_el1); /* * At this point, we know that this CPU implements * SPE and is available to the host. * Check if the host is actually using it ? */ - reg = read_sysreg_s(SYS_PMBLIMITR_EL1); - if (!(reg & BIT(PMBLIMITR_EL1_E_SHIFT))) + *pmblimitr_el1 = read_sysreg_s(SYS_PMBLIMITR_EL1); + if (!(*pmblimitr_el1 & BIT(PMBLIMITR_EL1_E_SHIFT))) return; /* Yes; save the control register and disable data generation */ @@ -37,18 +37,29 @@ static void __debug_save_spe(u64 *pmscr_el1) /* Now drain all buffered data to memory */ psb_csync(); + dsb(nsh); + + /* And disable the profiling buffer */ + write_sysreg_s(0, SYS_PMBLIMITR_EL1); + isb(); } -static void __debug_restore_spe(u64 pmscr_el1) +static void __debug_restore_spe(void) { - if (!pmscr_el1) + u64 pmblimitr_el1 = *host_data_ptr(host_debug_state.pmblimitr_el1); + + if (!(pmblimitr_el1 & BIT(PMBLIMITR_EL1_E_SHIFT))) return; /* The host page table is installed, but not yet synchronised */ isb(); + /* Re-enable the profiling buffer. */ + write_sysreg_s(pmblimitr_el1, SYS_PMBLIMITR_EL1); + isb(); + /* Re-enable data generation */ - write_sysreg_el1(pmscr_el1, SYS_PMSCR); + write_sysreg_el1(*host_data_ptr(host_debug_state.pmscr_el1), SYS_PMSCR); } static void __trace_do_switch(u64 *saved_trfcr, u64 new_trfcr) @@ -57,12 +68,54 @@ static void __trace_do_switch(u64 *saved_trfcr, u64 new_trfcr) write_sysreg_el1(new_trfcr, SYS_TRFCR); } -static bool __trace_needs_drain(void) +static void __trace_drain_and_disable(void) { - if (is_protected_kvm_enabled() && host_data_test_flag(HAS_TRBE)) - return read_sysreg_s(SYS_TRBLIMITR_EL1) & TRBLIMITR_EL1_E; + u64 *trblimitr_el1 = host_data_ptr(host_debug_state.trblimitr_el1); + bool needs_drain = is_protected_kvm_enabled() ? + host_data_test_flag(HAS_TRBE) : + host_data_test_flag(TRBE_ENABLED); - return host_data_test_flag(TRBE_ENABLED); + if (!needs_drain) { + *trblimitr_el1 = 0; + return; + } + + *trblimitr_el1 = read_sysreg_s(SYS_TRBLIMITR_EL1); + if (*trblimitr_el1 & TRBLIMITR_EL1_E) { + /* + * The host has enabled the Trace Buffer Unit so we have + * to beat the CPU with a stick until it stops accessing + * memory. + */ + + /* First, ensure that our prior write to TRFCR has stuck. */ + isb(); + + /* Now synchronise with the trace and drain the buffer. */ + tsb_csync(); + dsb(nsh); + + /* + * With no more trace being generated, we can disable the + * Trace Buffer Unit. + */ + write_sysreg_s(0, SYS_TRBLIMITR_EL1); + if (cpus_have_final_cap(ARM64_WORKAROUND_2064142)) { + /* + * Some CPUs are so good, we have to drain 'em + * twice. + */ + tsb_csync(); + dsb(nsh); + } + + /* + * Ensure that the Trace Buffer Unit is disabled before + * we start mucking with the stage-2 and trap + * configuration. + */ + isb(); + } } static bool __trace_needs_switch(void) @@ -79,21 +132,34 @@ static void __trace_switch_to_guest(void) __trace_do_switch(host_data_ptr(host_debug_state.trfcr_el1), *host_data_ptr(trfcr_while_in_guest)); - - if (__trace_needs_drain()) { - isb(); - tsb_csync(); - } + __trace_drain_and_disable(); } static void __trace_switch_to_host(void) { + u64 trblimitr_el1 = *host_data_ptr(host_debug_state.trblimitr_el1); + + if (trblimitr_el1 & TRBLIMITR_EL1_E) { + /* Re-enable the Trace Buffer Unit for the host. */ + write_sysreg_s(trblimitr_el1, SYS_TRBLIMITR_EL1); + isb(); + if (cpus_have_final_cap(ARM64_WORKAROUND_2038923)) { + /* + * Make sure the unit is re-enabled before we + * poke TRFCR. + */ + isb(); + } + } + __trace_do_switch(host_data_ptr(trfcr_while_in_guest), *host_data_ptr(host_debug_state.trfcr_el1)); } -static void __debug_save_brbe(u64 *brbcr_el1) +static void __debug_save_brbe(void) { + u64 *brbcr_el1 = host_data_ptr(host_debug_state.brbcr_el1); + *brbcr_el1 = 0; /* Check if the BRBE is enabled */ @@ -109,8 +175,10 @@ static void __debug_save_brbe(u64 *brbcr_el1) write_sysreg_el1(0, SYS_BRBCR); } -static void __debug_restore_brbe(u64 brbcr_el1) +static void __debug_restore_brbe(void) { + u64 brbcr_el1 = *host_data_ptr(host_debug_state.brbcr_el1); + if (!brbcr_el1) return; @@ -122,11 +190,11 @@ void __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu) { /* Disable and flush SPE data generation */ if (host_data_test_flag(HAS_SPE)) - __debug_save_spe(host_data_ptr(host_debug_state.pmscr_el1)); + __debug_save_spe(); /* Disable BRBE branch records */ if (host_data_test_flag(HAS_BRBE)) - __debug_save_brbe(host_data_ptr(host_debug_state.brbcr_el1)); + __debug_save_brbe(); if (__trace_needs_switch()) __trace_switch_to_guest(); @@ -140,9 +208,9 @@ void __debug_switch_to_guest(struct kvm_vcpu *vcpu) void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu) { if (host_data_test_flag(HAS_SPE)) - __debug_restore_spe(*host_data_ptr(host_debug_state.pmscr_el1)); + __debug_restore_spe(); if (host_data_test_flag(HAS_BRBE)) - __debug_restore_brbe(*host_data_ptr(host_debug_state.brbcr_el1)); + __debug_restore_brbe(); if (__trace_needs_switch()) __trace_switch_to_host(); } diff --git a/arch/arm64/kvm/hyp/nvhe/events.c b/arch/arm64/kvm/hyp/nvhe/events.c new file mode 100644 index 000000000000..add9383aadb5 --- /dev/null +++ b/arch/arm64/kvm/hyp/nvhe/events.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Google LLC + * Author: Vincent Donnefort + */ + +#include +#include + +#include + +int __tracing_enable_event(unsigned short id, bool enable) +{ + struct hyp_event_id *event_id = &__hyp_event_ids_start[id]; + atomic_t *enabled; + + if (event_id >= __hyp_event_ids_end) + return -EINVAL; + + enabled = hyp_fixmap_map(__hyp_pa(&event_id->enabled)); + atomic_set(enabled, enable); + hyp_fixmap_unmap(); + + return 0; +} diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c index 94161ea1cd60..1af722771178 100644 --- a/arch/arm64/kvm/hyp/nvhe/ffa.c +++ b/arch/arm64/kvm/hyp/nvhe/ffa.c @@ -26,10 +26,10 @@ * the duration and are therefore serialised. */ -#include #include #include +#include #include #include #include @@ -147,7 +147,7 @@ static int ffa_map_hyp_buffers(u64 ffa_page_count) { struct arm_smccc_1_2_regs res; - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { .a0 = FFA_FN64_RXTX_MAP, .a1 = hyp_virt_to_phys(hyp_buffers.tx), .a2 = hyp_virt_to_phys(hyp_buffers.rx), @@ -161,7 +161,7 @@ static int ffa_unmap_hyp_buffers(void) { struct arm_smccc_1_2_regs res; - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { .a0 = FFA_RXTX_UNMAP, .a1 = HOST_FFA_ID, }, &res); @@ -172,7 +172,7 @@ static int ffa_unmap_hyp_buffers(void) static void ffa_mem_frag_tx(struct arm_smccc_1_2_regs *res, u32 handle_lo, u32 handle_hi, u32 fraglen, u32 endpoint_id) { - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { .a0 = FFA_MEM_FRAG_TX, .a1 = handle_lo, .a2 = handle_hi, @@ -184,7 +184,7 @@ static void ffa_mem_frag_tx(struct arm_smccc_1_2_regs *res, u32 handle_lo, static void ffa_mem_frag_rx(struct arm_smccc_1_2_regs *res, u32 handle_lo, u32 handle_hi, u32 fragoff) { - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { .a0 = FFA_MEM_FRAG_RX, .a1 = handle_lo, .a2 = handle_hi, @@ -196,7 +196,7 @@ static void ffa_mem_frag_rx(struct arm_smccc_1_2_regs *res, u32 handle_lo, static void ffa_mem_xfer(struct arm_smccc_1_2_regs *res, u64 func_id, u32 len, u32 fraglen) { - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { .a0 = func_id, .a1 = len, .a2 = fraglen, @@ -206,7 +206,7 @@ static void ffa_mem_xfer(struct arm_smccc_1_2_regs *res, u64 func_id, u32 len, static void ffa_mem_reclaim(struct arm_smccc_1_2_regs *res, u32 handle_lo, u32 handle_hi, u32 flags) { - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { .a0 = FFA_MEM_RECLAIM, .a1 = handle_lo, .a2 = handle_hi, @@ -216,7 +216,7 @@ static void ffa_mem_reclaim(struct arm_smccc_1_2_regs *res, u32 handle_lo, static void ffa_retrieve_req(struct arm_smccc_1_2_regs *res, u32 len) { - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { .a0 = FFA_FN64_MEM_RETRIEVE_REQ, .a1 = len, .a2 = len, @@ -225,7 +225,7 @@ static void ffa_retrieve_req(struct arm_smccc_1_2_regs *res, u32 len) static void ffa_rx_release(struct arm_smccc_1_2_regs *res) { - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { .a0 = FFA_RX_RELEASE, }, res); } @@ -728,7 +728,7 @@ static int hyp_ffa_post_init(void) size_t min_rxtx_sz; struct arm_smccc_1_2_regs res; - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs){ + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs){ .a0 = FFA_ID_GET, }, &res); if (res.a0 != FFA_SUCCESS) @@ -737,7 +737,7 @@ static int hyp_ffa_post_init(void) if (res.a2 != HOST_FFA_ID) return -EINVAL; - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs){ + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs){ .a0 = FFA_FEATURES, .a1 = FFA_FN64_RXTX_MAP, }, &res); @@ -788,7 +788,7 @@ static void do_ffa_version(struct arm_smccc_1_2_regs *res, * first if TEE supports it. */ if (FFA_MINOR_VERSION(ffa_req_version) < FFA_MINOR_VERSION(hyp_ffa_version)) { - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { .a0 = FFA_VERSION, .a1 = ffa_req_version, }, res); @@ -824,7 +824,7 @@ static void do_ffa_part_get(struct arm_smccc_1_2_regs *res, goto out_unlock; } - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { .a0 = FFA_PARTITION_INFO_GET, .a1 = uuid0, .a2 = uuid1, @@ -939,7 +939,7 @@ int hyp_ffa_init(void *pages) if (kvm_host_psci_config.smccc_version < ARM_SMCCC_VERSION_1_2) return 0; - arm_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { + hyp_smccc_1_2_smc(&(struct arm_smccc_1_2_regs) { .a0 = FFA_VERSION, .a1 = FFA_VERSION_1_2, }, &res); diff --git a/arch/arm64/kvm/hyp/nvhe/host.S b/arch/arm64/kvm/hyp/nvhe/host.S index eef15b374abb..f337770ec459 100644 --- a/arch/arm64/kvm/hyp/nvhe/host.S +++ b/arch/arm64/kvm/hyp/nvhe/host.S @@ -120,12 +120,11 @@ SYM_FUNC_START(__hyp_do_panic) mov x29, x0 -#ifdef CONFIG_NVHE_EL2_DEBUG +#ifdef PKVM_DISABLE_STAGE2_ON_PANIC /* Ensure host stage-2 is disabled */ mrs x0, hcr_el2 bic x0, x0, #HCR_VM msr_hcr_el2 x0 - isb tlbi vmalls12e1 dsb nsh #endif @@ -291,13 +290,3 @@ SYM_CODE_START(__kvm_hyp_host_forward_smc) ret SYM_CODE_END(__kvm_hyp_host_forward_smc) - -/* - * kvm_host_psci_cpu_entry is called through br instruction, which requires - * bti j instruction as compilers (gcc and llvm) doesn't insert bti j for external - * functions, but bti c instead. - */ -SYM_CODE_START(kvm_host_psci_cpu_entry) - bti j - b __kvm_host_psci_cpu_entry -SYM_CODE_END(kvm_host_psci_cpu_entry) diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-init.S b/arch/arm64/kvm/hyp/nvhe/hyp-init.S index 445eb0743af2..89cb553be1e5 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-init.S +++ b/arch/arm64/kvm/hyp/nvhe/hyp-init.S @@ -173,9 +173,8 @@ SYM_CODE_END(___kvm_hyp_init) * x0: struct kvm_nvhe_init_params PA */ SYM_CODE_START(kvm_hyp_cpu_entry) - mov x1, #1 // is_cpu_on = true + ldr x29, =__kvm_host_psci_cpu_on_entry b __kvm_hyp_init_cpu -SYM_CODE_END(kvm_hyp_cpu_entry) /* * PSCI CPU_SUSPEND / SYSTEM_SUSPEND entry point @@ -183,32 +182,17 @@ SYM_CODE_END(kvm_hyp_cpu_entry) * x0: struct kvm_nvhe_init_params PA */ SYM_CODE_START(kvm_hyp_cpu_resume) - mov x1, #0 // is_cpu_on = false - b __kvm_hyp_init_cpu -SYM_CODE_END(kvm_hyp_cpu_resume) + ldr x29, =__kvm_host_psci_cpu_resume_entry -/* - * Common code for CPU entry points. Initializes EL2 state and - * installs the hypervisor before handing over to a C handler. - * - * x0: struct kvm_nvhe_init_params PA - * x1: bool is_cpu_on - */ -SYM_CODE_START_LOCAL(__kvm_hyp_init_cpu) +SYM_INNER_LABEL(__kvm_hyp_init_cpu, SYM_L_LOCAL) mov x28, x0 // Stash arguments - mov x29, x1 /* Check that the core was booted in EL2. */ mrs x0, CurrentEL cmp x0, #CurrentEL_EL2 - b.eq 2f + b.ne 1f - /* The core booted in EL1. KVM cannot be initialized on it. */ -1: wfe - wfi - b 1b - -2: msr SPsel, #1 // We want to use SP_EL{1,2} + msr SPsel, #1 // We want to use SP_EL2 init_el2_hcr 0 @@ -218,11 +202,16 @@ SYM_CODE_START_LOCAL(__kvm_hyp_init_cpu) mov x0, x28 bl ___kvm_hyp_init // Clobbers x0..x2 - /* Leave idmap. */ - mov x0, x29 - ldr x1, =kvm_host_psci_cpu_entry - br x1 -SYM_CODE_END(__kvm_hyp_init_cpu) + /* Leave idmap -- using BLR is OK, LR is restored from host context */ + blr x29 + + // The core booted in EL1, or the C code unexpectedly returned. + // Either way, KVM cannot be initialized on it. +1: wfe + wfi + b 1b +SYM_CODE_END(kvm_hyp_cpu_resume) +SYM_CODE_END(kvm_hyp_cpu_entry) SYM_CODE_START(__kvm_handle_stub_hvc) /* diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index e7790097db93..06db299c37a8 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -12,12 +12,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params); @@ -136,6 +138,8 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) hyp_vcpu->vcpu.arch.vsesr_el2 = host_vcpu->arch.vsesr_el2; hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3; + + hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid; } static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) @@ -169,9 +173,6 @@ static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt) DECLARE_REG(u64, hcr_el2, host_ctxt, 3); struct pkvm_hyp_vcpu *hyp_vcpu; - if (!is_protected_kvm_enabled()) - return; - hyp_vcpu = pkvm_load_hyp_vcpu(handle, vcpu_idx); if (!hyp_vcpu) return; @@ -188,12 +189,8 @@ static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt) static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt) { - struct pkvm_hyp_vcpu *hyp_vcpu; + struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu(); - if (!is_protected_kvm_enabled()) - return; - - hyp_vcpu = pkvm_get_loaded_hyp_vcpu(); if (hyp_vcpu) pkvm_put_hyp_vcpu(hyp_vcpu); } @@ -248,6 +245,26 @@ static int pkvm_refill_memcache(struct pkvm_hyp_vcpu *hyp_vcpu) &host_vcpu->arch.pkvm_memcache); } +static void handle___pkvm_host_donate_guest(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(u64, pfn, host_ctxt, 1); + DECLARE_REG(u64, gfn, host_ctxt, 2); + struct pkvm_hyp_vcpu *hyp_vcpu; + int ret = -EINVAL; + + hyp_vcpu = pkvm_get_loaded_hyp_vcpu(); + if (!hyp_vcpu || !pkvm_hyp_vcpu_is_protected(hyp_vcpu)) + goto out; + + ret = pkvm_refill_memcache(hyp_vcpu); + if (ret) + goto out; + + ret = __pkvm_host_donate_guest(pfn, gfn, hyp_vcpu); +out: + cpu_reg(host_ctxt, 1) = ret; +} + static void handle___pkvm_host_share_guest(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(u64, pfn, host_ctxt, 1); @@ -257,9 +274,6 @@ static void handle___pkvm_host_share_guest(struct kvm_cpu_context *host_ctxt) struct pkvm_hyp_vcpu *hyp_vcpu; int ret = -EINVAL; - if (!is_protected_kvm_enabled()) - goto out; - hyp_vcpu = pkvm_get_loaded_hyp_vcpu(); if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu)) goto out; @@ -281,9 +295,6 @@ static void handle___pkvm_host_unshare_guest(struct kvm_cpu_context *host_ctxt) struct pkvm_hyp_vm *hyp_vm; int ret = -EINVAL; - if (!is_protected_kvm_enabled()) - goto out; - hyp_vm = get_np_pkvm_hyp_vm(handle); if (!hyp_vm) goto out; @@ -301,9 +312,6 @@ static void handle___pkvm_host_relax_perms_guest(struct kvm_cpu_context *host_ct struct pkvm_hyp_vcpu *hyp_vcpu; int ret = -EINVAL; - if (!is_protected_kvm_enabled()) - goto out; - hyp_vcpu = pkvm_get_loaded_hyp_vcpu(); if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu)) goto out; @@ -321,9 +329,6 @@ static void handle___pkvm_host_wrprotect_guest(struct kvm_cpu_context *host_ctxt struct pkvm_hyp_vm *hyp_vm; int ret = -EINVAL; - if (!is_protected_kvm_enabled()) - goto out; - hyp_vm = get_np_pkvm_hyp_vm(handle); if (!hyp_vm) goto out; @@ -343,9 +348,6 @@ static void handle___pkvm_host_test_clear_young_guest(struct kvm_cpu_context *ho struct pkvm_hyp_vm *hyp_vm; int ret = -EINVAL; - if (!is_protected_kvm_enabled()) - goto out; - hyp_vm = get_np_pkvm_hyp_vm(handle); if (!hyp_vm) goto out; @@ -362,9 +364,6 @@ static void handle___pkvm_host_mkyoung_guest(struct kvm_cpu_context *host_ctxt) struct pkvm_hyp_vcpu *hyp_vcpu; int ret = -EINVAL; - if (!is_protected_kvm_enabled()) - goto out; - hyp_vcpu = pkvm_get_loaded_hyp_vcpu(); if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu)) goto out; @@ -424,12 +423,8 @@ static void handle___kvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt) static void handle___pkvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1); - struct pkvm_hyp_vm *hyp_vm; + struct pkvm_hyp_vm *hyp_vm = get_np_pkvm_hyp_vm(handle); - if (!is_protected_kvm_enabled()) - return; - - hyp_vm = get_np_pkvm_hyp_vm(handle); if (!hyp_vm) return; @@ -486,17 +481,15 @@ static void handle___pkvm_init(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(phys_addr_t, phys, host_ctxt, 1); DECLARE_REG(unsigned long, size, host_ctxt, 2); - DECLARE_REG(unsigned long, nr_cpus, host_ctxt, 3); - DECLARE_REG(unsigned long *, per_cpu_base, host_ctxt, 4); - DECLARE_REG(u32, hyp_va_bits, host_ctxt, 5); + DECLARE_REG(unsigned long *, per_cpu_base, host_ctxt, 3); + DECLARE_REG(u32, hyp_va_bits, host_ctxt, 4); /* * __pkvm_init() will return only if an error occurred, otherwise it * will tail-call in __pkvm_init_finalise() which will have to deal * with the host context directly. */ - cpu_reg(host_ctxt, 1) = __pkvm_init(phys, size, nr_cpus, per_cpu_base, - hyp_va_bits); + cpu_reg(host_ctxt, 1) = __pkvm_init(phys, size, per_cpu_base, hyp_va_bits); } static void handle___pkvm_cpu_set_vector(struct kvm_cpu_context *host_ctxt) @@ -582,11 +575,115 @@ static void handle___pkvm_init_vcpu(struct kvm_cpu_context *host_ctxt) cpu_reg(host_ctxt, 1) = __pkvm_init_vcpu(handle, host_vcpu, vcpu_hva); } -static void handle___pkvm_teardown_vm(struct kvm_cpu_context *host_ctxt) +static void handle___pkvm_vcpu_in_poison_fault(struct kvm_cpu_context *host_ctxt) +{ + int ret; + struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu(); + + ret = hyp_vcpu ? __pkvm_vcpu_in_poison_fault(hyp_vcpu) : -EINVAL; + cpu_reg(host_ctxt, 1) = ret; +} + +static void handle___pkvm_force_reclaim_guest_page(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(phys_addr_t, phys, host_ctxt, 1); + + cpu_reg(host_ctxt, 1) = __pkvm_host_force_reclaim_page_guest(phys); +} + +static void handle___pkvm_reclaim_dying_guest_page(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1); + DECLARE_REG(u64, gfn, host_ctxt, 2); + + cpu_reg(host_ctxt, 1) = __pkvm_reclaim_dying_guest_page(handle, gfn); +} + +static void handle___pkvm_start_teardown_vm(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1); - cpu_reg(host_ctxt, 1) = __pkvm_teardown_vm(handle); + cpu_reg(host_ctxt, 1) = __pkvm_start_teardown_vm(handle); +} + +static void handle___pkvm_finalize_teardown_vm(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1); + + cpu_reg(host_ctxt, 1) = __pkvm_finalize_teardown_vm(handle); +} + +static void handle___tracing_load(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(unsigned long, desc_hva, host_ctxt, 1); + DECLARE_REG(size_t, desc_size, host_ctxt, 2); + + cpu_reg(host_ctxt, 1) = __tracing_load(desc_hva, desc_size); +} + +static void handle___tracing_unload(struct kvm_cpu_context *host_ctxt) +{ + __tracing_unload(); +} + +static void handle___tracing_enable(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(bool, enable, host_ctxt, 1); + + cpu_reg(host_ctxt, 1) = __tracing_enable(enable); +} + +static void handle___tracing_swap_reader(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(unsigned int, cpu, host_ctxt, 1); + + cpu_reg(host_ctxt, 1) = __tracing_swap_reader(cpu); +} + +static void handle___tracing_update_clock(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(u32, mult, host_ctxt, 1); + DECLARE_REG(u32, shift, host_ctxt, 2); + DECLARE_REG(u64, epoch_ns, host_ctxt, 3); + DECLARE_REG(u64, epoch_cyc, host_ctxt, 4); + + __tracing_update_clock(mult, shift, epoch_ns, epoch_cyc); +} + +static void handle___tracing_reset(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(unsigned int, cpu, host_ctxt, 1); + + cpu_reg(host_ctxt, 1) = __tracing_reset(cpu); +} + +static void handle___tracing_enable_event(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(unsigned short, id, host_ctxt, 1); + DECLARE_REG(bool, enable, host_ctxt, 2); + + cpu_reg(host_ctxt, 1) = __tracing_enable_event(id, enable); +} + +static void handle___tracing_write_event(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(u64, id, host_ctxt, 1); + + trace_selftest(id); +} + +static void handle___vgic_v5_save_apr(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1); + + __vgic_v5_save_apr(kern_hyp_va(cpu_if)); +} + +static void handle___vgic_v5_restore_vmcr_apr(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1); + + __vgic_v5_restore_vmcr_apr(kern_hyp_va(cpu_if)); } typedef void (*hcall_t)(struct kvm_cpu_context *); @@ -603,14 +700,6 @@ static const hcall_t host_hcall[] = { HANDLE_FUNC(__vgic_v3_get_gic_config), HANDLE_FUNC(__pkvm_prot_finalize), - HANDLE_FUNC(__pkvm_host_share_hyp), - HANDLE_FUNC(__pkvm_host_unshare_hyp), - HANDLE_FUNC(__pkvm_host_share_guest), - HANDLE_FUNC(__pkvm_host_unshare_guest), - HANDLE_FUNC(__pkvm_host_relax_perms_guest), - HANDLE_FUNC(__pkvm_host_wrprotect_guest), - HANDLE_FUNC(__pkvm_host_test_clear_young_guest), - HANDLE_FUNC(__pkvm_host_mkyoung_guest), HANDLE_FUNC(__kvm_adjust_pc), HANDLE_FUNC(__kvm_vcpu_run), HANDLE_FUNC(__kvm_flush_vm_context), @@ -620,13 +709,37 @@ static const hcall_t host_hcall[] = { HANDLE_FUNC(__kvm_tlb_flush_vmid_range), HANDLE_FUNC(__kvm_flush_cpu_context), HANDLE_FUNC(__kvm_timer_set_cntvoff), + HANDLE_FUNC(__tracing_load), + HANDLE_FUNC(__tracing_unload), + HANDLE_FUNC(__tracing_enable), + HANDLE_FUNC(__tracing_swap_reader), + HANDLE_FUNC(__tracing_update_clock), + HANDLE_FUNC(__tracing_reset), + HANDLE_FUNC(__tracing_enable_event), + HANDLE_FUNC(__tracing_write_event), HANDLE_FUNC(__vgic_v3_save_aprs), HANDLE_FUNC(__vgic_v3_restore_vmcr_aprs), + HANDLE_FUNC(__vgic_v5_save_apr), + HANDLE_FUNC(__vgic_v5_restore_vmcr_apr), + + HANDLE_FUNC(__pkvm_host_share_hyp), + HANDLE_FUNC(__pkvm_host_unshare_hyp), + HANDLE_FUNC(__pkvm_host_donate_guest), + HANDLE_FUNC(__pkvm_host_share_guest), + HANDLE_FUNC(__pkvm_host_unshare_guest), + HANDLE_FUNC(__pkvm_host_relax_perms_guest), + HANDLE_FUNC(__pkvm_host_wrprotect_guest), + HANDLE_FUNC(__pkvm_host_test_clear_young_guest), + HANDLE_FUNC(__pkvm_host_mkyoung_guest), HANDLE_FUNC(__pkvm_reserve_vm), HANDLE_FUNC(__pkvm_unreserve_vm), HANDLE_FUNC(__pkvm_init_vm), HANDLE_FUNC(__pkvm_init_vcpu), - HANDLE_FUNC(__pkvm_teardown_vm), + HANDLE_FUNC(__pkvm_vcpu_in_poison_fault), + HANDLE_FUNC(__pkvm_force_reclaim_guest_page), + HANDLE_FUNC(__pkvm_reclaim_dying_guest_page), + HANDLE_FUNC(__pkvm_start_teardown_vm), + HANDLE_FUNC(__pkvm_finalize_teardown_vm), HANDLE_FUNC(__pkvm_vcpu_load), HANDLE_FUNC(__pkvm_vcpu_put), HANDLE_FUNC(__pkvm_tlb_flush_vmid), @@ -635,9 +748,11 @@ static const hcall_t host_hcall[] = { static void handle_host_hcall(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(unsigned long, id, host_ctxt, 0); - unsigned long hcall_min = 0; + unsigned long hcall_min = 0, hcall_max = __KVM_HOST_SMCCC_FUNC_MAX; hcall_t hfn; + BUILD_BUG_ON(ARRAY_SIZE(host_hcall) != __KVM_HOST_SMCCC_FUNC_MAX); + /* * If pKVM has been initialised then reject any calls to the * early "privileged" hypercalls. Note that we cannot reject @@ -647,13 +762,16 @@ static void handle_host_hcall(struct kvm_cpu_context *host_ctxt) * basis. This is all fine, however, since __pkvm_prot_finalize * returns -EPERM after the first call for a given CPU. */ - if (static_branch_unlikely(&kvm_protected_mode_initialized)) - hcall_min = __KVM_HOST_SMCCC_FUNC___pkvm_prot_finalize; + if (static_branch_unlikely(&kvm_protected_mode_initialized)) { + hcall_min = __KVM_HOST_SMCCC_FUNC_MIN_PKVM; + } else { + hcall_max = __KVM_HOST_SMCCC_FUNC_PKVM_ONLY; + } id &= ~ARM_SMCCC_CALL_HINTS; id -= KVM_HOST_SMCCC_ID(0); - if (unlikely(id < hcall_min || id >= ARRAY_SIZE(host_hcall))) + if (unlikely(id < hcall_min || id >= hcall_max)) goto inval; hfn = host_hcall[id]; @@ -670,15 +788,27 @@ inval: static void default_host_smc_handler(struct kvm_cpu_context *host_ctxt) { + trace_hyp_exit(host_ctxt, HYP_REASON_SMC); __kvm_hyp_host_forward_smc(host_ctxt); + trace_hyp_enter(host_ctxt, HYP_REASON_SMC); } static void handle_host_smc(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(u64, func_id, host_ctxt, 0); + u64 esr = read_sysreg_el2(SYS_ESR); bool handled; + if (esr & ESR_ELx_xVC_IMM_MASK) { + cpu_reg(host_ctxt, 0) = SMCCC_RET_NOT_SUPPORTED; + goto exit_skip_instr; + } + func_id &= ~ARM_SMCCC_CALL_HINTS; + if (upper_32_bits(func_id)) { + cpu_reg(host_ctxt, 0) = SMCCC_RET_NOT_SUPPORTED; + goto exit_skip_instr; + } handled = kvm_host_psci_handler(host_ctxt, func_id); if (!handled) @@ -686,47 +816,57 @@ static void handle_host_smc(struct kvm_cpu_context *host_ctxt) if (!handled) default_host_smc_handler(host_ctxt); +exit_skip_instr: /* SMC was trapped, move ELR past the current PC. */ kvm_skip_host_instr(); } -/* - * Inject an Undefined Instruction exception into the host. - * - * This is open-coded to allow control over PSTATE construction without - * complicating the generic exception entry helpers. - */ -static void inject_undef64(void) +void inject_host_exception(u64 esr) { - u64 spsr_mask, vbar, sctlr, old_spsr, new_spsr, esr, offset; + u64 sctlr, spsr_el1, spsr_el2, exc_offset = except_type_sync; + const u64 spsr_mask = PSR_N_BIT | PSR_Z_BIT | PSR_C_BIT | + PSR_V_BIT | PSR_DIT_BIT | PSR_PAN_BIT; - spsr_mask = PSR_N_BIT | PSR_Z_BIT | PSR_C_BIT | PSR_V_BIT | PSR_DIT_BIT | PSR_PAN_BIT; + spsr_el1 = spsr_el2 = read_sysreg_el2(SYS_SPSR); + switch (spsr_el1 & (PSR_MODE_MASK | PSR_MODE32_BIT)) { + case PSR_MODE_EL0t: + exc_offset += LOWER_EL_AArch64_VECTOR; + break; + case PSR_MODE_EL0t | PSR_MODE32_BIT: + exc_offset += LOWER_EL_AArch32_VECTOR; + break; + default: + exc_offset += CURRENT_EL_SP_ELx_VECTOR; + } + + spsr_el2 &= spsr_mask; + spsr_el2 |= PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | + PSR_MODE_EL1h; - vbar = read_sysreg_el1(SYS_VBAR); sctlr = read_sysreg_el1(SYS_SCTLR); - old_spsr = read_sysreg_el2(SYS_SPSR); - - new_spsr = old_spsr & spsr_mask; - new_spsr |= PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT; - new_spsr |= PSR_MODE_EL1h; - if (!(sctlr & SCTLR_EL1_SPAN)) - new_spsr |= PSR_PAN_BIT; + spsr_el2 |= PSR_PAN_BIT; if (sctlr & SCTLR_ELx_DSSBS) - new_spsr |= PSR_SSBS_BIT; + spsr_el2 |= PSR_SSBS_BIT; if (system_supports_mte()) - new_spsr |= PSR_TCO_BIT; + spsr_el2 |= PSR_TCO_BIT; - esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT) | ESR_ELx_IL; - offset = CURRENT_EL_SP_ELx_VECTOR + except_type_sync; + if (esr_fsc_is_translation_fault(esr)) + write_sysreg_el1(read_sysreg_el2(SYS_FAR), SYS_FAR); write_sysreg_el1(esr, SYS_ESR); write_sysreg_el1(read_sysreg_el2(SYS_ELR), SYS_ELR); - write_sysreg_el1(old_spsr, SYS_SPSR); - write_sysreg_el2(vbar + offset, SYS_ELR); - write_sysreg_el2(new_spsr, SYS_SPSR); + write_sysreg_el1(spsr_el1, SYS_SPSR); + write_sysreg_el2(read_sysreg_el1(SYS_VBAR) + exc_offset, SYS_ELR); + write_sysreg_el2(spsr_el2, SYS_SPSR); +} + +static void inject_host_undef64(void) +{ + inject_host_exception((ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT) | + ESR_ELx_IL); } static bool handle_host_mte(u64 esr) @@ -749,7 +889,7 @@ static bool handle_host_mte(u64 esr) return false; } - inject_undef64(); + inject_host_undef64(); return true; } @@ -757,15 +897,19 @@ void handle_trap(struct kvm_cpu_context *host_ctxt) { u64 esr = read_sysreg_el2(SYS_ESR); + switch (ESR_ELx_EC(esr)) { case ESR_ELx_EC_HVC64: + trace_hyp_enter(host_ctxt, HYP_REASON_HVC); handle_host_hcall(host_ctxt); break; case ESR_ELx_EC_SMC64: + trace_hyp_enter(host_ctxt, HYP_REASON_SMC); handle_host_smc(host_ctxt); break; case ESR_ELx_EC_IABT_LOW: case ESR_ELx_EC_DABT_LOW: + trace_hyp_enter(host_ctxt, HYP_REASON_HOST_ABORT); handle_host_mem_abort(host_ctxt); break; case ESR_ELx_EC_SYS64: @@ -775,4 +919,6 @@ void handle_trap(struct kvm_cpu_context *host_ctxt) default: BUG(); } + + trace_hyp_exit(host_ctxt, HYP_REASON_ERET_HOST); } diff --git a/arch/arm64/kvm/hyp/nvhe/hyp.lds.S b/arch/arm64/kvm/hyp/nvhe/hyp.lds.S index d724f6d69302..7a02837203d1 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp.lds.S +++ b/arch/arm64/kvm/hyp/nvhe/hyp.lds.S @@ -16,6 +16,12 @@ SECTIONS { HYP_SECTION(.text) HYP_SECTION(.data..ro_after_init) HYP_SECTION(.rodata) +#ifdef CONFIG_NVHE_EL2_TRACING + . = ALIGN(PAGE_SIZE); + BEGIN_HYP_SECTION(.event_ids) + *(SORT(.hyp.event_ids.*)) + END_HYP_SECTION +#endif /* * .hyp..data..percpu needs to be page aligned to maintain the same diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index d815265bd374..25f04629014e 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -5,6 +5,7 @@ */ #include + #include #include #include @@ -14,10 +15,12 @@ #include +#include #include #include #include #include +#include #define KVM_HOST_S2_FLAGS (KVM_PGTABLE_S2_AS_S1 | KVM_PGTABLE_S2_IDMAP) @@ -28,6 +31,19 @@ static struct hyp_pool host_s2_pool; static DEFINE_PER_CPU(struct pkvm_hyp_vm *, __current_vm); #define current_vm (*this_cpu_ptr(&__current_vm)) +static void pkvm_sme_dvmsync_fw_call(void) +{ + if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) { + struct arm_smccc_res res; + + /* + * Ignore the return value. Probing for the workaround + * availability took place in init_hyp_mode(). + */ + hyp_smccc_1_1_smc(ARM_SMCCC_CPU_WORKAROUND_4193714, &res); + } +} + static void guest_lock_component(struct pkvm_hyp_vm *vm) { hyp_spin_lock(&vm->lock); @@ -461,8 +477,15 @@ static bool range_is_memory(u64 start, u64 end) static inline int __host_stage2_idmap(u64 start, u64 end, enum kvm_pgtable_prot prot) { + /* + * We don't make permission changes to the host idmap after + * initialisation, so we can squash -EAGAIN to save callers + * having to treat it like success in the case that they try to + * map something that is already mapped. + */ return kvm_pgtable_stage2_map(&host_mmu.pgt, start, end - start, start, - prot, &host_s2_pool, 0); + prot, &host_s2_pool, + KVM_PGTABLE_WALK_IGNORE_EAGAIN); } /* @@ -504,7 +527,7 @@ static int host_stage2_adjust_range(u64 addr, struct kvm_mem_range *range) return ret; if (kvm_pte_valid(pte)) - return -EAGAIN; + return -EEXIST; if (pte) { WARN_ON(addr_is_memory(addr) && @@ -541,24 +564,105 @@ static void __host_update_page_state(phys_addr_t addr, u64 size, enum pkvm_page_ set_host_state(page, state); } -int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id) +#define KVM_HOST_DONATION_PTE_OWNER_MASK GENMASK(3, 1) +#define KVM_HOST_DONATION_PTE_EXTRA_MASK GENMASK(59, 4) +static int host_stage2_set_owner_metadata_locked(phys_addr_t addr, u64 size, + u8 owner_id, u64 meta) { + kvm_pte_t annotation; int ret; + if (owner_id == PKVM_ID_HOST) + return -EINVAL; + if (!range_is_memory(addr, addr + size)) return -EPERM; - ret = host_stage2_try(kvm_pgtable_stage2_set_owner, &host_mmu.pgt, - addr, size, &host_s2_pool, owner_id); - if (ret) - return ret; + if (!FIELD_FIT(KVM_HOST_DONATION_PTE_OWNER_MASK, owner_id)) + return -EINVAL; - /* Don't forget to update the vmemmap tracking for the host */ - if (owner_id == PKVM_ID_HOST) - __host_update_page_state(addr, size, PKVM_PAGE_OWNED); - else + if (!FIELD_FIT(KVM_HOST_DONATION_PTE_EXTRA_MASK, meta)) + return -EINVAL; + + annotation = FIELD_PREP(KVM_HOST_DONATION_PTE_OWNER_MASK, owner_id) | + FIELD_PREP(KVM_HOST_DONATION_PTE_EXTRA_MASK, meta); + ret = host_stage2_try(kvm_pgtable_stage2_annotate, &host_mmu.pgt, + addr, size, &host_s2_pool, + KVM_HOST_INVALID_PTE_TYPE_DONATION, annotation); + if (!ret) { + /* + * After stage2 maintenance has happened, but before the page + * owner has changed. + */ + pkvm_sme_dvmsync_fw_call(); __host_update_page_state(addr, size, PKVM_NOPAGE); + } + return ret; +} + +int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id) +{ + int ret = -EINVAL; + + switch (owner_id) { + case PKVM_ID_HOST: + if (!range_is_memory(addr, addr + size)) + return -EPERM; + + ret = host_stage2_idmap_locked(addr, size, PKVM_HOST_MEM_PROT); + if (!ret) + __host_update_page_state(addr, size, PKVM_PAGE_OWNED); + break; + case PKVM_ID_HYP: + ret = host_stage2_set_owner_metadata_locked(addr, size, + owner_id, 0); + break; + } + + return ret; +} + +#define KVM_HOST_PTE_OWNER_GUEST_HANDLE_MASK GENMASK(15, 0) +/* We need 40 bits for the GFN to cover a 52-bit IPA with 4k pages and LPA2 */ +#define KVM_HOST_PTE_OWNER_GUEST_GFN_MASK GENMASK(55, 16) +static u64 host_stage2_encode_gfn_meta(struct pkvm_hyp_vm *vm, u64 gfn) +{ + pkvm_handle_t handle = vm->kvm.arch.pkvm.handle; + + BUILD_BUG_ON((pkvm_handle_t)-1 > KVM_HOST_PTE_OWNER_GUEST_HANDLE_MASK); + WARN_ON(!FIELD_FIT(KVM_HOST_PTE_OWNER_GUEST_GFN_MASK, gfn)); + + return FIELD_PREP(KVM_HOST_PTE_OWNER_GUEST_HANDLE_MASK, handle) | + FIELD_PREP(KVM_HOST_PTE_OWNER_GUEST_GFN_MASK, gfn); +} + +static int host_stage2_decode_gfn_meta(kvm_pte_t pte, struct pkvm_hyp_vm **vm, + u64 *gfn) +{ + pkvm_handle_t handle; + u64 meta; + + if (WARN_ON(kvm_pte_valid(pte))) + return -EINVAL; + + if (FIELD_GET(KVM_INVALID_PTE_TYPE_MASK, pte) != + KVM_HOST_INVALID_PTE_TYPE_DONATION) { + return -EINVAL; + } + + if (FIELD_GET(KVM_HOST_DONATION_PTE_OWNER_MASK, pte) != PKVM_ID_GUEST) + return -EPERM; + + meta = FIELD_GET(KVM_HOST_DONATION_PTE_EXTRA_MASK, pte); + handle = FIELD_GET(KVM_HOST_PTE_OWNER_GUEST_HANDLE_MASK, meta); + *vm = get_vm_by_handle(handle); + if (!*vm) { + /* We probably raced with teardown; try again */ + return -EAGAIN; + } + + *gfn = FIELD_GET(KVM_HOST_PTE_OWNER_GUEST_GFN_MASK, meta); return 0; } @@ -605,11 +709,43 @@ unlock: return ret; } +static void host_inject_mem_abort(struct kvm_cpu_context *host_ctxt) +{ + u64 ec, esr, spsr; + + esr = read_sysreg_el2(SYS_ESR); + spsr = read_sysreg_el2(SYS_SPSR); + + /* Repaint the ESR to report a same-level fault if taken from EL1 */ + if ((spsr & PSR_MODE_MASK) != PSR_MODE_EL0t) { + ec = ESR_ELx_EC(esr); + if (ec == ESR_ELx_EC_DABT_LOW) + ec = ESR_ELx_EC_DABT_CUR; + else if (ec == ESR_ELx_EC_IABT_LOW) + ec = ESR_ELx_EC_IABT_CUR; + else + WARN_ON(1); + esr &= ~ESR_ELx_EC_MASK; + esr |= ec << ESR_ELx_EC_SHIFT; + } + + /* + * Since S1PTW should only ever be set for stage-2 faults, we're pretty + * much guaranteed that it won't be set in ESR_EL1 by the hardware. So, + * let's use that bit to allow the host abort handler to differentiate + * this abort from normal userspace faults. + * + * Note: although S1PTW is RES0 at EL1, it is guaranteed by the + * architecture to be backed by flops, so it should be safe to use. + */ + esr |= ESR_ELx_S1PTW; + inject_host_exception(esr); +} + void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt) { struct kvm_vcpu_fault_info fault; u64 esr, addr; - int ret = 0; esr = read_sysreg_el2(SYS_ESR); if (!__get_fault_info(esr, &fault)) { @@ -628,8 +764,16 @@ void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt) BUG_ON(!(fault.hpfar_el2 & HPFAR_EL2_NS)); addr = FIELD_GET(HPFAR_EL2_FIPA, fault.hpfar_el2) << 12; - ret = host_stage2_idmap(addr); - BUG_ON(ret && ret != -EAGAIN); + switch (host_stage2_idmap(addr)) { + case -EPERM: + host_inject_mem_abort(host_ctxt); + fallthrough; + case -EEXIST: + case 0: + break; + default: + BUG(); + } } struct check_walk_data { @@ -707,8 +851,20 @@ static int __hyp_check_page_state_range(phys_addr_t phys, u64 size, enum pkvm_pa return 0; } +static bool guest_pte_is_poisoned(kvm_pte_t pte) +{ + if (kvm_pte_valid(pte)) + return false; + + return FIELD_GET(KVM_INVALID_PTE_TYPE_MASK, pte) == + KVM_GUEST_INVALID_PTE_TYPE_POISONED; +} + static enum pkvm_page_state guest_get_page_state(kvm_pte_t pte, u64 addr) { + if (guest_pte_is_poisoned(pte)) + return PKVM_POISON; + if (!kvm_pte_valid(pte)) return PKVM_NOPAGE; @@ -727,6 +883,77 @@ static int __guest_check_page_state_range(struct pkvm_hyp_vm *vm, u64 addr, return check_page_state_range(&vm->pgt, addr, size, &d); } +static int get_valid_guest_pte(struct pkvm_hyp_vm *vm, u64 ipa, kvm_pte_t *ptep, u64 *physp) +{ + kvm_pte_t pte; + u64 phys; + s8 level; + int ret; + + ret = kvm_pgtable_get_leaf(&vm->pgt, ipa, &pte, &level); + if (ret) + return ret; + if (guest_pte_is_poisoned(pte)) + return -EHWPOISON; + if (!kvm_pte_valid(pte)) + return -ENOENT; + if (level != KVM_PGTABLE_LAST_LEVEL) + return -E2BIG; + + phys = kvm_pte_to_phys(pte); + ret = check_range_allowed_memory(phys, phys + PAGE_SIZE); + if (WARN_ON(ret)) + return ret; + + *ptep = pte; + *physp = phys; + + return 0; +} + +int __pkvm_vcpu_in_poison_fault(struct pkvm_hyp_vcpu *hyp_vcpu) +{ + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); + kvm_pte_t pte; + s8 level; + u64 ipa; + int ret; + + switch (kvm_vcpu_trap_get_class(&hyp_vcpu->vcpu)) { + case ESR_ELx_EC_DABT_LOW: + case ESR_ELx_EC_IABT_LOW: + if (kvm_vcpu_trap_is_translation_fault(&hyp_vcpu->vcpu)) + break; + fallthrough; + default: + return -EINVAL; + } + + /* + * The host has the faulting IPA when it calls us from the guest + * fault handler but we retrieve it ourselves from the FAR so as + * to avoid exposing an "oracle" that could reveal data access + * patterns of the guest after initial donation of its pages. + */ + ipa = kvm_vcpu_get_fault_ipa(&hyp_vcpu->vcpu); + ipa |= FAR_TO_FIPA_OFFSET(kvm_vcpu_get_hfar(&hyp_vcpu->vcpu)); + + guest_lock_component(vm); + ret = kvm_pgtable_get_leaf(&vm->pgt, ipa, &pte, &level); + if (ret) + goto unlock; + + if (level != KVM_PGTABLE_LAST_LEVEL) { + ret = -EINVAL; + goto unlock; + } + + ret = guest_pte_is_poisoned(pte); +unlock: + guest_unlock_component(vm); + return ret; +} + int __pkvm_host_share_hyp(u64 pfn) { u64 phys = hyp_pfn_to_phys(pfn); @@ -753,6 +980,72 @@ unlock: return ret; } +int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn) +{ + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); + u64 phys, ipa = hyp_pfn_to_phys(gfn); + kvm_pte_t pte; + int ret; + + host_lock_component(); + guest_lock_component(vm); + + ret = get_valid_guest_pte(vm, ipa, &pte, &phys); + if (ret) + goto unlock; + + ret = -EPERM; + if (pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte)) != PKVM_PAGE_OWNED) + goto unlock; + if (__host_check_page_state_range(phys, PAGE_SIZE, PKVM_NOPAGE)) + goto unlock; + + ret = 0; + WARN_ON(kvm_pgtable_stage2_map(&vm->pgt, ipa, PAGE_SIZE, phys, + pkvm_mkstate(KVM_PGTABLE_PROT_RWX, PKVM_PAGE_SHARED_OWNED), + &vcpu->vcpu.arch.pkvm_memcache, 0)); + WARN_ON(__host_set_page_state_range(phys, PAGE_SIZE, PKVM_PAGE_SHARED_BORROWED)); +unlock: + guest_unlock_component(vm); + host_unlock_component(); + + return ret; +} + +int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 gfn) +{ + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); + u64 meta, phys, ipa = hyp_pfn_to_phys(gfn); + kvm_pte_t pte; + int ret; + + host_lock_component(); + guest_lock_component(vm); + + ret = get_valid_guest_pte(vm, ipa, &pte, &phys); + if (ret) + goto unlock; + + ret = -EPERM; + if (pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte)) != PKVM_PAGE_SHARED_OWNED) + goto unlock; + if (__host_check_page_state_range(phys, PAGE_SIZE, PKVM_PAGE_SHARED_BORROWED)) + goto unlock; + + ret = 0; + meta = host_stage2_encode_gfn_meta(vm, gfn); + WARN_ON(host_stage2_set_owner_metadata_locked(phys, PAGE_SIZE, + PKVM_ID_GUEST, meta)); + WARN_ON(kvm_pgtable_stage2_map(&vm->pgt, ipa, PAGE_SIZE, phys, + pkvm_mkstate(KVM_PGTABLE_PROT_RWX, PKVM_PAGE_OWNED), + &vcpu->vcpu.arch.pkvm_memcache, 0)); +unlock: + guest_unlock_component(vm); + host_unlock_component(); + + return ret; +} + int __pkvm_host_unshare_hyp(u64 pfn) { u64 phys = hyp_pfn_to_phys(pfn); @@ -960,6 +1253,196 @@ static int __guest_check_transition_size(u64 phys, u64 ipa, u64 nr_pages, u64 *s return 0; } +static void hyp_poison_page(phys_addr_t phys) +{ + void *addr = hyp_fixmap_map(phys); + + memset(addr, 0, PAGE_SIZE); + /* + * Prefer kvm_flush_dcache_to_poc() over __clean_dcache_guest_page() + * here as the latter may elide the CMO under the assumption that FWB + * will be enabled on CPUs that support it. This is incorrect for the + * host stage-2 and would otherwise lead to a malicious host potentially + * being able to read the contents of newly reclaimed guest pages. + */ + kvm_flush_dcache_to_poc(addr, PAGE_SIZE); + hyp_fixmap_unmap(); +} + +static int host_stage2_get_guest_info(phys_addr_t phys, struct pkvm_hyp_vm **vm, + u64 *gfn) +{ + enum pkvm_page_state state; + kvm_pte_t pte; + s8 level; + int ret; + + if (!addr_is_memory(phys)) + return -EFAULT; + + state = get_host_state(hyp_phys_to_page(phys)); + switch (state) { + case PKVM_PAGE_OWNED: + case PKVM_PAGE_SHARED_OWNED: + case PKVM_PAGE_SHARED_BORROWED: + /* The access should no longer fault; try again. */ + return -EAGAIN; + case PKVM_NOPAGE: + break; + default: + return -EPERM; + } + + ret = kvm_pgtable_get_leaf(&host_mmu.pgt, phys, &pte, &level); + if (ret) + return ret; + + if (WARN_ON(level != KVM_PGTABLE_LAST_LEVEL)) + return -EINVAL; + + return host_stage2_decode_gfn_meta(pte, vm, gfn); +} + +int __pkvm_host_force_reclaim_page_guest(phys_addr_t phys) +{ + struct pkvm_hyp_vm *vm; + u64 gfn, ipa, pa; + kvm_pte_t pte; + int ret; + + phys &= PAGE_MASK; + + hyp_spin_lock(&vm_table_lock); + host_lock_component(); + + ret = host_stage2_get_guest_info(phys, &vm, &gfn); + if (ret) + goto unlock_host; + + ipa = hyp_pfn_to_phys(gfn); + guest_lock_component(vm); + ret = get_valid_guest_pte(vm, ipa, &pte, &pa); + if (ret) + goto unlock_guest; + + WARN_ON(pa != phys); + if (guest_get_page_state(pte, ipa) != PKVM_PAGE_OWNED) { + ret = -EPERM; + goto unlock_guest; + } + + /* We really shouldn't be allocating, so don't pass a memcache */ + ret = kvm_pgtable_stage2_annotate(&vm->pgt, ipa, PAGE_SIZE, NULL, + KVM_GUEST_INVALID_PTE_TYPE_POISONED, + 0); + if (ret) + goto unlock_guest; + + hyp_poison_page(phys); + WARN_ON(host_stage2_set_owner_locked(phys, PAGE_SIZE, PKVM_ID_HOST)); +unlock_guest: + guest_unlock_component(vm); +unlock_host: + host_unlock_component(); + hyp_spin_unlock(&vm_table_lock); + + return ret; +} + +int __pkvm_host_reclaim_page_guest(u64 gfn, struct pkvm_hyp_vm *vm) +{ + u64 ipa = hyp_pfn_to_phys(gfn); + kvm_pte_t pte; + u64 phys; + int ret; + + host_lock_component(); + guest_lock_component(vm); + + ret = get_valid_guest_pte(vm, ipa, &pte, &phys); + if (ret) + goto unlock; + + switch (guest_get_page_state(pte, ipa)) { + case PKVM_PAGE_OWNED: + WARN_ON(__host_check_page_state_range(phys, PAGE_SIZE, PKVM_NOPAGE)); + hyp_poison_page(phys); + break; + case PKVM_PAGE_SHARED_OWNED: + WARN_ON(__host_check_page_state_range(phys, PAGE_SIZE, PKVM_PAGE_SHARED_BORROWED)); + break; + default: + ret = -EPERM; + goto unlock; + } + + WARN_ON(kvm_pgtable_stage2_unmap(&vm->pgt, ipa, PAGE_SIZE)); + WARN_ON(host_stage2_set_owner_locked(phys, PAGE_SIZE, PKVM_ID_HOST)); + +unlock: + guest_unlock_component(vm); + host_unlock_component(); + + /* + * -EHWPOISON implies that the page was forcefully reclaimed already + * so return success for the GUP pin to be dropped. + */ + return ret && ret != -EHWPOISON ? ret : 0; +} + +/* + * share/donate install at most one stage-2 leaf (PAGE_SIZE, or one + * KVM_PGTABLE_LAST_LEVEL - 1 block for share). kvm_mmu_cache_min_pages() + * bounds the worst-case allocation: exact for the PAGE_SIZE leaf, + * conservative by one for the block. + */ +static int __guest_check_pgtable_memcache(struct pkvm_hyp_vcpu *vcpu) +{ + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); + + if (vcpu->vcpu.arch.pkvm_memcache.nr_pages < kvm_mmu_cache_min_pages(vm->pgt.mmu)) + return -ENOMEM; + + return 0; +} + +int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu) +{ + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); + u64 phys = hyp_pfn_to_phys(pfn); + u64 ipa = hyp_pfn_to_phys(gfn); + u64 meta; + int ret; + + host_lock_component(); + guest_lock_component(vm); + + ret = __host_check_page_state_range(phys, PAGE_SIZE, PKVM_PAGE_OWNED); + if (ret) + goto unlock; + + ret = __guest_check_page_state_range(vm, ipa, PAGE_SIZE, PKVM_NOPAGE); + if (ret) + goto unlock; + + ret = __guest_check_pgtable_memcache(vcpu); + if (ret) + goto unlock; + + meta = host_stage2_encode_gfn_meta(vm, gfn); + WARN_ON(host_stage2_set_owner_metadata_locked(phys, PAGE_SIZE, + PKVM_ID_GUEST, meta)); + WARN_ON(kvm_pgtable_stage2_map(&vm->pgt, ipa, PAGE_SIZE, phys, + pkvm_mkstate(KVM_PGTABLE_PROT_RWX, PKVM_PAGE_OWNED), + &vcpu->vcpu.arch.pkvm_memcache, 0)); + +unlock: + guest_unlock_component(vm); + host_unlock_component(); + + return ret; +} + int __pkvm_host_share_guest(u64 pfn, u64 gfn, u64 nr_pages, struct pkvm_hyp_vcpu *vcpu, enum kvm_pgtable_prot prot) { @@ -1011,6 +1494,10 @@ int __pkvm_host_share_guest(u64 pfn, u64 gfn, u64 nr_pages, struct pkvm_hyp_vcpu } } + ret = __guest_check_pgtable_memcache(vcpu); + if (ret) + goto unlock; + for_each_hyp_page(page, phys, size) { set_host_state(page, PKVM_PAGE_SHARED_OWNED); page->host_share_guest_count++; @@ -1206,53 +1693,18 @@ struct pkvm_expected_state { static struct pkvm_expected_state selftest_state; static struct hyp_page *selftest_page; - -static struct pkvm_hyp_vm selftest_vm = { - .kvm = { - .arch = { - .mmu = { - .arch = &selftest_vm.kvm.arch, - .pgt = &selftest_vm.pgt, - }, - }, - }, -}; - -static struct pkvm_hyp_vcpu selftest_vcpu = { - .vcpu = { - .arch = { - .hw_mmu = &selftest_vm.kvm.arch.mmu, - }, - .kvm = &selftest_vm.kvm, - }, -}; - -static void init_selftest_vm(void *virt) -{ - struct hyp_page *p = hyp_virt_to_page(virt); - int i; - - selftest_vm.kvm.arch.mmu.vtcr = host_mmu.arch.mmu.vtcr; - WARN_ON(kvm_guest_prepare_stage2(&selftest_vm, virt)); - - for (i = 0; i < pkvm_selftest_pages(); i++) { - if (p[i].refcount) - continue; - p[i].refcount = 1; - hyp_put_page(&selftest_vm.pool, hyp_page_to_virt(&p[i])); - } -} +static struct pkvm_hyp_vcpu *selftest_vcpu; static u64 selftest_ipa(void) { - return BIT(selftest_vm.pgt.ia_bits - 1); + return BIT(selftest_vcpu->vcpu.arch.hw_mmu->pgt->ia_bits - 1); } static void assert_page_state(void) { void *virt = hyp_page_to_virt(selftest_page); u64 size = PAGE_SIZE << selftest_page->order; - struct pkvm_hyp_vcpu *vcpu = &selftest_vcpu; + struct pkvm_hyp_vcpu *vcpu = selftest_vcpu; u64 phys = hyp_virt_to_phys(virt); u64 ipa[2] = { selftest_ipa(), selftest_ipa() + PAGE_SIZE }; struct pkvm_hyp_vm *vm; @@ -1267,10 +1719,10 @@ static void assert_page_state(void) WARN_ON(__hyp_check_page_state_range(phys, size, selftest_state.hyp)); hyp_unlock_component(); - guest_lock_component(&selftest_vm); + guest_lock_component(vm); WARN_ON(__guest_check_page_state_range(vm, ipa[0], size, selftest_state.guest[0])); WARN_ON(__guest_check_page_state_range(vm, ipa[1], size, selftest_state.guest[1])); - guest_unlock_component(&selftest_vm); + guest_unlock_component(vm); } #define assert_transition_res(res, fn, ...) \ @@ -1283,14 +1735,15 @@ void pkvm_ownership_selftest(void *base) { enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_RWX; void *virt = hyp_alloc_pages(&host_s2_pool, 0); - struct pkvm_hyp_vcpu *vcpu = &selftest_vcpu; - struct pkvm_hyp_vm *vm = &selftest_vm; + struct pkvm_hyp_vcpu *vcpu; u64 phys, size, pfn, gfn; + struct pkvm_hyp_vm *vm; WARN_ON(!virt); selftest_page = hyp_virt_to_page(virt); selftest_page->refcount = 0; - init_selftest_vm(base); + selftest_vcpu = vcpu = init_selftest_vm(base); + vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); size = PAGE_SIZE << selftest_page->order; phys = hyp_virt_to_phys(virt); @@ -1309,6 +1762,7 @@ void pkvm_ownership_selftest(void *base) assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size); assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot); assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, 1, vm); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu); selftest_state.host = PKVM_PAGE_OWNED; selftest_state.hyp = PKVM_NOPAGE; @@ -1328,6 +1782,7 @@ void pkvm_ownership_selftest(void *base) assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1); assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot); assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, 1, vm); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu); assert_transition_res(0, hyp_pin_shared_mem, virt, virt + size); assert_transition_res(0, hyp_pin_shared_mem, virt, virt + size); @@ -1340,6 +1795,7 @@ void pkvm_ownership_selftest(void *base) assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1); assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot); assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, 1, vm); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu); hyp_unpin_shared_mem(virt, virt + size); assert_page_state(); @@ -1359,6 +1815,7 @@ void pkvm_ownership_selftest(void *base) assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1); assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot); assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, 1, vm); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu); assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size); selftest_state.host = PKVM_PAGE_OWNED; @@ -1375,6 +1832,7 @@ void pkvm_ownership_selftest(void *base) assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn); assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn); assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu); assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size); selftest_state.guest[1] = PKVM_PAGE_SHARED_BORROWED; @@ -1388,10 +1846,70 @@ void pkvm_ownership_selftest(void *base) selftest_state.host = PKVM_PAGE_OWNED; assert_transition_res(0, __pkvm_host_unshare_guest, gfn + 1, 1, vm); + selftest_state.host = PKVM_NOPAGE; + selftest_state.guest[0] = PKVM_PAGE_OWNED; + assert_transition_res(0, __pkvm_host_donate_guest, pfn, gfn, vcpu); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn + 1, vcpu); + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot); + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn + 1, 1, vcpu, prot); + assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1); + assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1); + assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn); + assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn); + assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1); + + selftest_state.host = PKVM_PAGE_SHARED_BORROWED; + selftest_state.guest[0] = PKVM_PAGE_SHARED_OWNED; + assert_transition_res(0, __pkvm_guest_share_host, vcpu, gfn); + assert_transition_res(-EPERM, __pkvm_guest_share_host, vcpu, gfn); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn + 1, vcpu); + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot); + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn + 1, 1, vcpu, prot); + assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1); + assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1); + assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn); + assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn); + assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1); + + selftest_state.host = PKVM_NOPAGE; + selftest_state.guest[0] = PKVM_PAGE_OWNED; + assert_transition_res(0, __pkvm_guest_unshare_host, vcpu, gfn); + assert_transition_res(-EPERM, __pkvm_guest_unshare_host, vcpu, gfn); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn + 1, vcpu); + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot); + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn + 1, 1, vcpu, prot); + assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1); + assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1); + assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn); + assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn); + assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1); + + selftest_state.host = PKVM_PAGE_OWNED; + selftest_state.guest[0] = PKVM_POISON; + assert_transition_res(0, __pkvm_host_force_reclaim_page_guest, phys); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu); + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot); + assert_transition_res(-EHWPOISON, __pkvm_guest_share_host, vcpu, gfn); + assert_transition_res(-EHWPOISON, __pkvm_guest_unshare_host, vcpu, gfn); + + selftest_state.host = PKVM_NOPAGE; + selftest_state.guest[1] = PKVM_PAGE_OWNED; + assert_transition_res(0, __pkvm_host_donate_guest, pfn, gfn + 1, vcpu); + + selftest_state.host = PKVM_PAGE_OWNED; + selftest_state.guest[1] = PKVM_NOPAGE; + assert_transition_res(0, __pkvm_host_reclaim_page_guest, gfn + 1, vm); + assert_transition_res(-EPERM, __pkvm_host_donate_guest, pfn, gfn, vcpu); + assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, 1, vcpu, prot); + selftest_state.host = PKVM_NOPAGE; selftest_state.hyp = PKVM_PAGE_OWNED; assert_transition_res(0, __pkvm_host_donate_hyp, pfn, 1); + teardown_selftest_vm(); selftest_page->refcount = 1; hyp_put_page(&host_s2_pool, virt); } diff --git a/arch/arm64/kvm/hyp/nvhe/mm.c b/arch/arm64/kvm/hyp/nvhe/mm.c index 4d8fcc7a3a41..3b0bee496bff 100644 --- a/arch/arm64/kvm/hyp/nvhe/mm.c +++ b/arch/arm64/kvm/hyp/nvhe/mm.c @@ -244,7 +244,7 @@ static void *fixmap_map_slot(struct hyp_fixmap_slot *slot, phys_addr_t phys) void *hyp_fixmap_map(phys_addr_t phys) { - return fixmap_map_slot(this_cpu_ptr(&fixmap_slots), phys); + return fixmap_map_slot(this_cpu_ptr(&fixmap_slots), phys) + offset_in_page(phys); } static void fixmap_clear_slot(struct hyp_fixmap_slot *slot) @@ -366,7 +366,7 @@ void *hyp_fixblock_map(phys_addr_t phys, size_t *size) #ifdef HAS_FIXBLOCK *size = PMD_SIZE; hyp_spin_lock(&hyp_fixblock_lock); - return fixmap_map_slot(&hyp_fixblock_slot, phys); + return fixmap_map_slot(&hyp_fixblock_slot, phys) + offset_in_page(phys); #else *size = PAGE_SIZE; return hyp_fixmap_map(phys); diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 2f029bfe4755..eb1c10120f9f 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -4,6 +4,8 @@ * Author: Fuad Tabba */ +#include + #include #include @@ -222,6 +224,7 @@ static struct pkvm_hyp_vm **vm_table; void pkvm_hyp_vm_table_init(void *tbl) { + BUILD_BUG_ON((u64)HANDLE_OFFSET + KVM_MAX_PVMS > (pkvm_handle_t)-1); WARN_ON(vm_table); vm_table = tbl; } @@ -229,10 +232,12 @@ void pkvm_hyp_vm_table_init(void *tbl) /* * Return the hyp vm structure corresponding to the handle. */ -static struct pkvm_hyp_vm *get_vm_by_handle(pkvm_handle_t handle) +struct pkvm_hyp_vm *get_vm_by_handle(pkvm_handle_t handle) { unsigned int idx = vm_handle_to_idx(handle); + hyp_assert_lock_held(&vm_table_lock); + if (unlikely(idx >= KVM_MAX_PVMS)) return NULL; @@ -255,10 +260,14 @@ struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle, hyp_spin_lock(&vm_table_lock); hyp_vm = get_vm_by_handle(handle); - if (!hyp_vm || hyp_vm->kvm.created_vcpus <= vcpu_idx) + if (!hyp_vm || hyp_vm->kvm.arch.pkvm.is_dying) goto unlock; - hyp_vcpu = hyp_vm->vcpus[vcpu_idx]; + if (hyp_vm->kvm.created_vcpus <= vcpu_idx) + goto unlock; + + /* Pairs with smp_store_release() in register_hyp_vcpu(). */ + hyp_vcpu = smp_load_acquire(&hyp_vm->vcpus[vcpu_idx]); if (!hyp_vcpu) goto unlock; @@ -719,6 +728,69 @@ void __pkvm_unreserve_vm(pkvm_handle_t handle) hyp_spin_unlock(&vm_table_lock); } +#ifdef CONFIG_NVHE_EL2_DEBUG +static struct pkvm_hyp_vm selftest_vm = { + .kvm = { + .arch = { + .mmu = { + .arch = &selftest_vm.kvm.arch, + .pgt = &selftest_vm.pgt, + }, + }, + }, +}; + +static struct pkvm_hyp_vcpu selftest_vcpu = { + .vcpu = { + .arch = { + .hw_mmu = &selftest_vm.kvm.arch.mmu, + }, + .kvm = &selftest_vm.kvm, + }, +}; + +struct pkvm_hyp_vcpu *init_selftest_vm(void *virt) +{ + struct hyp_page *p = hyp_virt_to_page(virt); + unsigned long min_pages, seeded = 0; + int i; + + selftest_vm.kvm.arch.mmu.vtcr = host_mmu.arch.mmu.vtcr; + WARN_ON(kvm_guest_prepare_stage2(&selftest_vm, virt)); + + /* + * Mirror pkvm_refill_memcache() for the share/donate pre-checks; + * the selftest invokes those functions directly and would + * otherwise see an empty memcache. + */ + min_pages = kvm_mmu_cache_min_pages(&selftest_vm.kvm.arch.mmu); + + for (i = 0; i < pkvm_selftest_pages(); i++) { + if (p[i].refcount) + continue; + p[i].refcount = 1; + if (seeded < min_pages) { + push_hyp_memcache(&selftest_vcpu.vcpu.arch.pkvm_memcache, + hyp_page_to_virt(&p[i]), hyp_virt_to_phys); + seeded++; + } else { + hyp_put_page(&selftest_vm.pool, hyp_page_to_virt(&p[i])); + } + } + + selftest_vm.kvm.arch.pkvm.handle = __pkvm_reserve_vm(); + insert_vm_table_entry(selftest_vm.kvm.arch.pkvm.handle, &selftest_vm); + return &selftest_vcpu; +} + +void teardown_selftest_vm(void) +{ + hyp_spin_lock(&vm_table_lock); + remove_vm_table_entry(selftest_vm.kvm.arch.pkvm.handle); + hyp_spin_unlock(&vm_table_lock); +} +#endif /* CONFIG_NVHE_EL2_DEBUG */ + /* * Initialize the hypervisor copy of the VM state using host-donated memory. * @@ -803,12 +875,30 @@ err_unpin_kvm: * the page-aligned size of 'struct pkvm_hyp_vcpu'. * Return 0 on success, negative error code on failure. */ +static int register_hyp_vcpu(struct pkvm_hyp_vm *hyp_vm, + struct pkvm_hyp_vcpu *hyp_vcpu) +{ + unsigned int idx = hyp_vcpu->vcpu.vcpu_idx; + + if (idx >= hyp_vm->kvm.created_vcpus) + return -EINVAL; + + if (hyp_vm->vcpus[idx]) + return -EINVAL; + + /* + * Ensure the hyp_vcpu is initialised before publishing it to + * the vCPU-load path via 'hyp_vm->vcpus[]'. + */ + smp_store_release(&hyp_vm->vcpus[idx], hyp_vcpu); + return 0; +} + int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu, unsigned long vcpu_hva) { struct pkvm_hyp_vcpu *hyp_vcpu; struct pkvm_hyp_vm *hyp_vm; - unsigned int idx; int ret; hyp_vcpu = map_donated_memory(vcpu_hva, sizeof(*hyp_vcpu)); @@ -827,18 +917,11 @@ int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu, if (ret) goto unlock; - idx = hyp_vcpu->vcpu.vcpu_idx; - if (idx >= hyp_vm->kvm.created_vcpus) { - ret = -EINVAL; - goto unlock; + ret = register_hyp_vcpu(hyp_vm, hyp_vcpu); + if (ret) { + unpin_host_vcpu(host_vcpu); + unpin_host_sve_state(hyp_vcpu); } - - if (hyp_vm->vcpus[idx]) { - ret = -EINVAL; - goto unlock; - } - - hyp_vm->vcpus[idx] = hyp_vcpu; unlock: hyp_spin_unlock(&vm_table_lock); @@ -859,7 +942,54 @@ teardown_donated_memory(struct kvm_hyp_memcache *mc, void *addr, size_t size) unmap_donated_memory_noclear(addr, size); } -int __pkvm_teardown_vm(pkvm_handle_t handle) +int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 gfn) +{ + struct pkvm_hyp_vm *hyp_vm = get_pkvm_hyp_vm(handle); + int ret = -EINVAL; + + if (!hyp_vm) + return ret; + + if (hyp_vm->kvm.arch.pkvm.is_dying) + ret = __pkvm_host_reclaim_page_guest(gfn, hyp_vm); + + put_pkvm_hyp_vm(hyp_vm); + return ret; +} + +static struct pkvm_hyp_vm *get_pkvm_unref_hyp_vm_locked(pkvm_handle_t handle) +{ + struct pkvm_hyp_vm *hyp_vm; + + hyp_assert_lock_held(&vm_table_lock); + + hyp_vm = get_vm_by_handle(handle); + if (!hyp_vm || hyp_page_count(hyp_vm)) + return NULL; + + return hyp_vm; +} + +int __pkvm_start_teardown_vm(pkvm_handle_t handle) +{ + struct pkvm_hyp_vm *hyp_vm; + int ret = 0; + + hyp_spin_lock(&vm_table_lock); + hyp_vm = get_pkvm_unref_hyp_vm_locked(handle); + if (!hyp_vm || hyp_vm->kvm.arch.pkvm.is_dying) { + ret = -EINVAL; + goto unlock; + } + + hyp_vm->kvm.arch.pkvm.is_dying = true; +unlock: + hyp_spin_unlock(&vm_table_lock); + + return ret; +} + +int __pkvm_finalize_teardown_vm(pkvm_handle_t handle) { struct kvm_hyp_memcache *mc, *stage2_mc; struct pkvm_hyp_vm *hyp_vm; @@ -869,14 +999,9 @@ int __pkvm_teardown_vm(pkvm_handle_t handle) int err; hyp_spin_lock(&vm_table_lock); - hyp_vm = get_vm_by_handle(handle); - if (!hyp_vm) { - err = -ENOENT; - goto err_unlock; - } - - if (WARN_ON(hyp_page_count(hyp_vm))) { - err = -EBUSY; + hyp_vm = get_pkvm_unref_hyp_vm_locked(handle); + if (!hyp_vm || !hyp_vm->kvm.arch.pkvm.is_dying) { + err = -EINVAL; goto err_unlock; } @@ -922,3 +1047,121 @@ err_unlock: hyp_spin_unlock(&vm_table_lock); return err; } + +static u64 __pkvm_memshare_page_req(struct kvm_vcpu *vcpu, u64 ipa) +{ + u64 elr; + + /* Fake up a data abort (level 3 translation fault on write) */ + vcpu->arch.fault.esr_el2 = (ESR_ELx_EC_DABT_LOW << ESR_ELx_EC_SHIFT) | + ESR_ELx_WNR | ESR_ELx_FSC_FAULT | + FIELD_PREP(ESR_ELx_FSC_LEVEL, 3); + + /* Shuffle the IPA around into the HPFAR */ + vcpu->arch.fault.hpfar_el2 = (HPFAR_EL2_NS | (ipa >> 8)) & HPFAR_MASK; + + /* This is a virtual address. 0's good. Let's go with 0. */ + vcpu->arch.fault.far_el2 = 0; + + /* Rewind the ELR so we return to the HVC once the IPA is mapped */ + elr = read_sysreg(elr_el2); + elr -= 4; + write_sysreg(elr, elr_el2); + + return ARM_EXCEPTION_TRAP; +} + +static bool pkvm_memshare_call(u64 *ret, struct kvm_vcpu *vcpu, u64 *exit_code) +{ + struct pkvm_hyp_vcpu *hyp_vcpu; + u64 ipa = smccc_get_arg1(vcpu); + + if (!PAGE_ALIGNED(ipa)) + goto out_guest; + + hyp_vcpu = container_of(vcpu, struct pkvm_hyp_vcpu, vcpu); + switch (__pkvm_guest_share_host(hyp_vcpu, hyp_phys_to_pfn(ipa))) { + case 0: + ret[0] = SMCCC_RET_SUCCESS; + goto out_guest; + case -ENOENT: + /* + * Convert the exception into a data abort so that the page + * being shared is mapped into the guest next time. + */ + *exit_code = __pkvm_memshare_page_req(vcpu, ipa); + goto out_host; + } + +out_guest: + return true; +out_host: + return false; +} + +static void pkvm_memunshare_call(u64 *ret, struct kvm_vcpu *vcpu) +{ + struct pkvm_hyp_vcpu *hyp_vcpu; + u64 ipa = smccc_get_arg1(vcpu); + + if (!PAGE_ALIGNED(ipa)) + return; + + hyp_vcpu = container_of(vcpu, struct pkvm_hyp_vcpu, vcpu); + if (!__pkvm_guest_unshare_host(hyp_vcpu, hyp_phys_to_pfn(ipa))) + ret[0] = SMCCC_RET_SUCCESS; +} + +/* + * Handler for protected VM HVC calls. + * + * Returns true if the hypervisor has handled the exit (and control + * should return to the guest) or false if it hasn't (and the handling + * should be performed by the host). + */ +bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code) +{ + u64 val[4] = { SMCCC_RET_INVALID_PARAMETER }; + bool handled = true; + + switch (smccc_get_function(vcpu)) { + case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID: + val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES); + val[0] |= BIT(ARM_SMCCC_KVM_FUNC_HYP_MEMINFO); + val[0] |= BIT(ARM_SMCCC_KVM_FUNC_MEM_SHARE); + val[0] |= BIT(ARM_SMCCC_KVM_FUNC_MEM_UNSHARE); + break; + case ARM_SMCCC_VENDOR_HYP_KVM_HYP_MEMINFO_FUNC_ID: + if (smccc_get_arg1(vcpu) || + smccc_get_arg2(vcpu) || + smccc_get_arg3(vcpu)) { + break; + } + + val[0] = PAGE_SIZE; + break; + case ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID: + if (smccc_get_arg2(vcpu) || + smccc_get_arg3(vcpu)) { + break; + } + + handled = pkvm_memshare_call(val, vcpu, exit_code); + break; + case ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID: + if (smccc_get_arg2(vcpu) || + smccc_get_arg3(vcpu)) { + break; + } + + pkvm_memunshare_call(val, vcpu); + break; + default: + /* Punt everything else back to the host, for now. */ + handled = false; + } + + if (handled) + smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]); + return handled; +} diff --git a/arch/arm64/kvm/hyp/nvhe/psci-relay.c b/arch/arm64/kvm/hyp/nvhe/psci-relay.c index c3e196fb8b18..e20db999e328 100644 --- a/arch/arm64/kvm/hyp/nvhe/psci-relay.c +++ b/arch/arm64/kvm/hyp/nvhe/psci-relay.c @@ -6,11 +6,12 @@ #include #include +#include #include -#include #include #include +#include #include #include @@ -65,7 +66,7 @@ static unsigned long psci_call(unsigned long fn, unsigned long arg0, { struct arm_smccc_res res; - arm_smccc_1_1_smc(fn, arg0, arg1, arg2, &res); + hyp_smccc_1_1_smc(fn, arg0, arg1, arg2, &res); return res.a0; } @@ -200,30 +201,42 @@ static int psci_system_suspend(u64 func_id, struct kvm_cpu_context *host_ctxt) __hyp_pa(init_params), 0); } -asmlinkage void __noreturn __kvm_host_psci_cpu_entry(bool is_cpu_on) +static void __noreturn __kvm_host_psci_cpu_entry(unsigned long pc, unsigned long r0) { - struct psci_boot_args *boot_args; - struct kvm_cpu_context *host_ctxt; + struct kvm_cpu_context *host_ctxt = host_data_ptr(host_ctxt); - host_ctxt = host_data_ptr(host_ctxt); + trace_hyp_enter(host_ctxt, HYP_REASON_PSCI); - if (is_cpu_on) - boot_args = this_cpu_ptr(&cpu_on_args); - else - boot_args = this_cpu_ptr(&suspend_args); - - cpu_reg(host_ctxt, 0) = boot_args->r0; - write_sysreg_el2(boot_args->pc, SYS_ELR); - - if (is_cpu_on) - release_boot_args(boot_args); + cpu_reg(host_ctxt, 0) = r0; + write_sysreg_el2(pc, SYS_ELR); write_sysreg_el1(INIT_SCTLR_EL1_MMU_OFF, SYS_SCTLR); write_sysreg(INIT_PSTATE_EL1, SPSR_EL2); + trace_hyp_exit(host_ctxt, HYP_REASON_PSCI); __host_enter(host_ctxt); } +asmlinkage void __noreturn __kvm_host_psci_cpu_on_entry(void) +{ + struct psci_boot_args *boot_args = this_cpu_ptr(&cpu_on_args); + unsigned long pc, r0; + + pc = READ_ONCE(boot_args->pc); + r0 = READ_ONCE(boot_args->r0); + + release_boot_args(boot_args); + + __kvm_host_psci_cpu_entry(pc, r0); +} + +asmlinkage void __noreturn __kvm_host_psci_cpu_resume_entry(void) +{ + struct psci_boot_args *boot_args = this_cpu_ptr(&suspend_args); + + __kvm_host_psci_cpu_entry(boot_args->pc, boot_args->r0); +} + static unsigned long psci_0_1_handler(u64 func_id, struct kvm_cpu_context *host_ctxt) { if (is_psci_0_1(cpu_off, func_id) || is_psci_0_1(migrate, func_id)) diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setup.c index 90bd014e952f..d461981616d9 100644 --- a/arch/arm64/kvm/hyp/nvhe/setup.c +++ b/arch/arm64/kvm/hyp/nvhe/setup.c @@ -312,10 +312,6 @@ void __noreturn __pkvm_init_finalise(void) }; pkvm_pgtable.mm_ops = &pkvm_pgtable_mm_ops; - ret = fix_host_ownership(); - if (ret) - goto out; - ret = fix_hyp_pgtable_refcnt(); if (ret) goto out; @@ -324,6 +320,10 @@ void __noreturn __pkvm_init_finalise(void) if (ret) goto out; + ret = fix_host_ownership(); + if (ret) + goto out; + ret = hyp_ffa_init(ffa_proxy_pages); if (ret) goto out; @@ -341,8 +341,7 @@ out: __host_enter(host_ctxt); } -int __pkvm_init(phys_addr_t phys, unsigned long size, unsigned long nr_cpus, - unsigned long *per_cpu_base, u32 hyp_va_bits) +int __pkvm_init(phys_addr_t phys, unsigned long size, unsigned long *per_cpu_base, u32 hyp_va_bits) { struct kvm_nvhe_init_params *params; void *virt = hyp_phys_to_virt(phys); @@ -355,7 +354,6 @@ int __pkvm_init(phys_addr_t phys, unsigned long size, unsigned long nr_cpus, return -EINVAL; hyp_spin_lock_init(&pkvm_pgd_lock); - hyp_nr_cpus = nr_cpus; ret = divide_memory_pool(virt, size); if (ret) diff --git a/arch/arm64/kvm/hyp/nvhe/stacktrace.c b/arch/arm64/kvm/hyp/nvhe/stacktrace.c index 5b6eeab1a774..7c832d60d22b 100644 --- a/arch/arm64/kvm/hyp/nvhe/stacktrace.c +++ b/arch/arm64/kvm/hyp/nvhe/stacktrace.c @@ -34,7 +34,7 @@ static void hyp_prepare_backtrace(unsigned long fp, unsigned long pc) stacktrace_info->pc = pc; } -#ifdef CONFIG_PROTECTED_NVHE_STACKTRACE +#ifdef CONFIG_PKVM_STACKTRACE #include DEFINE_PER_CPU(unsigned long [NVHE_STACKTRACE_SIZE/sizeof(long)], pkvm_stacktrace); @@ -134,11 +134,11 @@ static void pkvm_save_backtrace(unsigned long fp, unsigned long pc) unwind(&state, pkvm_save_backtrace_entry, &idx); } -#else /* !CONFIG_PROTECTED_NVHE_STACKTRACE */ +#else /* !CONFIG_PKVM_STACKTRACE */ static void pkvm_save_backtrace(unsigned long fp, unsigned long pc) { } -#endif /* CONFIG_PROTECTED_NVHE_STACKTRACE */ +#endif /* CONFIG_PKVM_STACKTRACE */ /* * kvm_nvhe_prepare_backtrace - prepare to dump the nVHE backtrace diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index 779089e42681..8d1df3d33595 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -21,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +44,9 @@ struct fgt_masks hfgwtr2_masks; struct fgt_masks hfgitr2_masks; struct fgt_masks hdfgrtr2_masks; struct fgt_masks hdfgwtr2_masks; +struct fgt_masks ich_hfgrtr_masks; +struct fgt_masks ich_hfgwtr_masks; +struct fgt_masks ich_hfgitr_masks; extern void kvm_nvhe_prepare_backtrace(unsigned long fp, unsigned long pc); @@ -110,6 +113,12 @@ static void __deactivate_traps(struct kvm_vcpu *vcpu) /* Save VGICv3 state on non-VHE systems */ static void __hyp_vgic_save_state(struct kvm_vcpu *vcpu) { + if (vgic_is_v5(kern_hyp_va(vcpu->kvm))) { + __vgic_v5_save_state(&vcpu->arch.vgic_cpu.vgic_v5); + __vgic_v5_save_ppi_state(&vcpu->arch.vgic_cpu.vgic_v5); + return; + } + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) { __vgic_v3_save_state(&vcpu->arch.vgic_cpu.vgic_v3); __vgic_v3_deactivate_traps(&vcpu->arch.vgic_cpu.vgic_v3); @@ -119,6 +128,12 @@ static void __hyp_vgic_save_state(struct kvm_vcpu *vcpu) /* Restore VGICv3 state on non-VHE systems */ static void __hyp_vgic_restore_state(struct kvm_vcpu *vcpu) { + if (vgic_is_v5(kern_hyp_va(vcpu->kvm))) { + __vgic_v5_restore_state(&vcpu->arch.vgic_cpu.vgic_v5); + __vgic_v5_restore_ppi_state(&vcpu->arch.vgic_cpu.vgic_v5); + return; + } + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) { __vgic_v3_activate_traps(&vcpu->arch.vgic_cpu.vgic_v3); __vgic_v3_restore_state(&vcpu->arch.vgic_cpu.vgic_v3); @@ -190,6 +205,7 @@ static const exit_handler_fn hyp_exit_handlers[] = { static const exit_handler_fn pvm_exit_handlers[] = { [0 ... ESR_ELx_EC_MAX] = NULL, + [ESR_ELx_EC_HVC64] = kvm_handle_pvm_hvc64, [ESR_ELx_EC_SYS64] = kvm_handle_pvm_sys64, [ESR_ELx_EC_SVE] = kvm_handle_pvm_restricted, [ESR_ELx_EC_FP_ASIMD] = kvm_hyp_handle_fpsimd, @@ -278,7 +294,7 @@ int __kvm_vcpu_run(struct kvm_vcpu *vcpu) * We're about to restore some new MMU state. Make sure * ongoing page-table walks that have started before we * trapped to EL2 have completed. This also synchronises the - * above disabling of BRBE, SPE and TRBE. + * above disabling of BRBE. * * See DDI0487I.a D8.1.5 "Out-of-context translation regimes", * rule R_LFHQG and subsequent information statements. @@ -308,10 +324,13 @@ int __kvm_vcpu_run(struct kvm_vcpu *vcpu) __debug_switch_to_guest(vcpu); do { + trace_hyp_exit(host_ctxt, HYP_REASON_ERET_GUEST); + /* Jump in the fire! */ exit_code = __guest_enter(vcpu); /* And we're baaack! */ + trace_hyp_enter(host_ctxt, HYP_REASON_GUEST_EXIT); } while (fixup_guest_exit(vcpu, &exit_code)); __sysreg_save_state_nvhe(guest_ctxt); diff --git a/arch/arm64/kvm/hyp/nvhe/sys_regs.c b/arch/arm64/kvm/hyp/nvhe/sys_regs.c index 06d28621722e..8c3fbb413a06 100644 --- a/arch/arm64/kvm/hyp/nvhe/sys_regs.c +++ b/arch/arm64/kvm/hyp/nvhe/sys_regs.c @@ -20,6 +20,7 @@ */ u64 id_aa64pfr0_el1_sys_val; u64 id_aa64pfr1_el1_sys_val; +u64 id_aa64pfr2_el1_sys_val; u64 id_aa64isar0_el1_sys_val; u64 id_aa64isar1_el1_sys_val; u64 id_aa64isar2_el1_sys_val; @@ -108,6 +109,11 @@ static const struct pvm_ftr_bits pvmid_aa64pfr1[] = { FEAT_END }; +static const struct pvm_ftr_bits pvmid_aa64pfr2[] = { + MAX_FEAT(ID_AA64PFR2_EL1, GCIE, NI), + FEAT_END +}; + static const struct pvm_ftr_bits pvmid_aa64mmfr0[] = { MAX_FEAT_ENUM(ID_AA64MMFR0_EL1, PARANGE, 40), MAX_FEAT_ENUM(ID_AA64MMFR0_EL1, ASIDBITS, 16), @@ -221,6 +227,8 @@ static u64 pvm_calc_id_reg(const struct kvm_vcpu *vcpu, u32 id) return get_restricted_features(vcpu, id_aa64pfr0_el1_sys_val, pvmid_aa64pfr0); case SYS_ID_AA64PFR1_EL1: return get_restricted_features(vcpu, id_aa64pfr1_el1_sys_val, pvmid_aa64pfr1); + case SYS_ID_AA64PFR2_EL1: + return get_restricted_features(vcpu, id_aa64pfr2_el1_sys_val, pvmid_aa64pfr2); case SYS_ID_AA64ISAR0_EL1: return id_aa64isar0_el1_sys_val; case SYS_ID_AA64ISAR1_EL1: @@ -392,6 +400,14 @@ static const struct sys_reg_desc pvm_sys_reg_descs[] = { /* Cache maintenance by set/way operations are restricted. */ /* Debug and Trace Registers are restricted. */ + RAZ_WI(SYS_DBGBVRn_EL1(0)), + RAZ_WI(SYS_DBGBCRn_EL1(0)), + RAZ_WI(SYS_DBGWVRn_EL1(0)), + RAZ_WI(SYS_DBGWCRn_EL1(0)), + RAZ_WI(SYS_MDSCR_EL1), + RAZ_WI(SYS_OSLAR_EL1), + RAZ_WI(SYS_OSLSR_EL1), + RAZ_WI(SYS_OSDLR_EL1), /* Group 1 ID registers */ HOST_HANDLED(SYS_REVIDR_EL1), @@ -431,7 +447,7 @@ static const struct sys_reg_desc pvm_sys_reg_descs[] = { /* CRm=4 */ AARCH64(SYS_ID_AA64PFR0_EL1), AARCH64(SYS_ID_AA64PFR1_EL1), - ID_UNALLOCATED(4,2), + AARCH64(SYS_ID_AA64PFR2_EL1), ID_UNALLOCATED(4,3), AARCH64(SYS_ID_AA64ZFR0_EL1), ID_UNALLOCATED(4,5), diff --git a/arch/arm64/kvm/hyp/nvhe/trace.c b/arch/arm64/kvm/hyp/nvhe/trace.c new file mode 100644 index 000000000000..a6ca27b18e15 --- /dev/null +++ b/arch/arm64/kvm/hyp/nvhe/trace.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Google LLC + * Author: Vincent Donnefort + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "simple_ring_buffer.c" + +static DEFINE_PER_CPU(struct simple_rb_per_cpu, __simple_rbs); + +static struct hyp_trace_buffer { + struct simple_rb_per_cpu __percpu *simple_rbs; + void *bpages_backing_start; + size_t bpages_backing_size; + hyp_spinlock_t lock; +} trace_buffer = { + .simple_rbs = &__simple_rbs, + .lock = __HYP_SPIN_LOCK_UNLOCKED, +}; + +static bool hyp_trace_buffer_loaded(struct hyp_trace_buffer *trace_buffer) +{ + return trace_buffer->bpages_backing_size > 0; +} + +void *tracing_reserve_entry(unsigned long length) +{ + return simple_ring_buffer_reserve(this_cpu_ptr(trace_buffer.simple_rbs), length, + trace_clock()); +} + +void tracing_commit_entry(void) +{ + simple_ring_buffer_commit(this_cpu_ptr(trace_buffer.simple_rbs)); +} + +static int __admit_host_mem(void *start, u64 size) +{ + if (!PAGE_ALIGNED(start) || !PAGE_ALIGNED(size) || !size) + return -EINVAL; + + if (!is_protected_kvm_enabled()) + return 0; + + return __pkvm_host_donate_hyp(hyp_virt_to_pfn(start), size >> PAGE_SHIFT); +} + +static void __release_host_mem(void *start, u64 size) +{ + if (!is_protected_kvm_enabled()) + return; + + WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn(start), size >> PAGE_SHIFT)); +} + +static int hyp_trace_buffer_load_bpage_backing(struct hyp_trace_buffer *trace_buffer, + struct hyp_trace_desc *desc) +{ + void *start = (void *)kern_hyp_va(desc->bpages_backing_start); + size_t size = desc->bpages_backing_size; + int ret; + + ret = __admit_host_mem(start, size); + if (ret) + return ret; + + memset(start, 0, size); + + trace_buffer->bpages_backing_start = start; + trace_buffer->bpages_backing_size = size; + + return 0; +} + +static void hyp_trace_buffer_unload_bpage_backing(struct hyp_trace_buffer *trace_buffer) +{ + void *start = trace_buffer->bpages_backing_start; + size_t size = trace_buffer->bpages_backing_size; + + if (!size) + return; + + memset(start, 0, size); + + __release_host_mem(start, size); + + trace_buffer->bpages_backing_start = 0; + trace_buffer->bpages_backing_size = 0; +} + +static void *__pin_shared_page(unsigned long kern_va) +{ + void *va = kern_hyp_va((void *)kern_va); + + if (!is_protected_kvm_enabled()) + return va; + + return hyp_pin_shared_mem(va, va + PAGE_SIZE) ? NULL : va; +} + +static void __unpin_shared_page(void *va) +{ + if (!is_protected_kvm_enabled()) + return; + + hyp_unpin_shared_mem(va, va + PAGE_SIZE); +} + +static void hyp_trace_buffer_unload(struct hyp_trace_buffer *trace_buffer) +{ + int cpu; + + hyp_assert_lock_held(&trace_buffer->lock); + + if (!hyp_trace_buffer_loaded(trace_buffer)) + return; + + for (cpu = 0; cpu < hyp_nr_cpus; cpu++) + simple_ring_buffer_unload_mm(per_cpu_ptr(trace_buffer->simple_rbs, cpu), + __unpin_shared_page); + + hyp_trace_buffer_unload_bpage_backing(trace_buffer); +} + +static int hyp_trace_buffer_load(struct hyp_trace_buffer *trace_buffer, + struct hyp_trace_desc *desc) +{ + struct simple_buffer_page *bpages; + struct ring_buffer_desc *rb_desc; + int ret, cpu; + + hyp_assert_lock_held(&trace_buffer->lock); + + if (hyp_trace_buffer_loaded(trace_buffer)) + return -EINVAL; + + ret = hyp_trace_buffer_load_bpage_backing(trace_buffer, desc); + if (ret) + return ret; + + bpages = trace_buffer->bpages_backing_start; + for_each_ring_buffer_desc(rb_desc, cpu, &desc->trace_buffer_desc) { + ret = simple_ring_buffer_init_mm(per_cpu_ptr(trace_buffer->simple_rbs, cpu), + bpages, rb_desc, __pin_shared_page, + __unpin_shared_page); + if (ret) + break; + + bpages += rb_desc->nr_page_va; + } + + if (ret) + hyp_trace_buffer_unload(trace_buffer); + + return ret; +} + +static bool hyp_trace_desc_validate(struct hyp_trace_desc *desc, size_t desc_size) +{ + struct ring_buffer_desc *rb_desc; + unsigned int cpu; + size_t nr_bpages; + void *desc_end; + + /* + * Both desc_size and bpages_backing_size are untrusted host-provided + * values. We rely on __pkvm_host_donate_hyp() to enforce their validity. + */ + desc_end = (void *)desc + desc_size; + nr_bpages = desc->bpages_backing_size / sizeof(struct simple_buffer_page); + + for_each_ring_buffer_desc(rb_desc, cpu, &desc->trace_buffer_desc) { + /* Can we read nr_page_va? */ + if ((void *)rb_desc + struct_size(rb_desc, page_va, 0) > desc_end) + return false; + + /* Overflow desc? */ + if ((void *)rb_desc + struct_size(rb_desc, page_va, rb_desc->nr_page_va) > desc_end) + return false; + + /* Overflow bpages backing memory? */ + if (nr_bpages < rb_desc->nr_page_va) + return false; + + if (cpu >= hyp_nr_cpus) + return false; + + if (cpu != rb_desc->cpu) + return false; + + nr_bpages -= rb_desc->nr_page_va; + } + + return true; +} + +int __tracing_load(unsigned long desc_hva, size_t desc_size) +{ + struct hyp_trace_desc *desc = (struct hyp_trace_desc *)kern_hyp_va(desc_hva); + int ret; + + ret = __admit_host_mem(desc, desc_size); + if (ret) + return ret; + + if (!hyp_trace_desc_validate(desc, desc_size)) + goto err_release_desc; + + hyp_spin_lock(&trace_buffer.lock); + + ret = hyp_trace_buffer_load(&trace_buffer, desc); + + hyp_spin_unlock(&trace_buffer.lock); + +err_release_desc: + __release_host_mem(desc, desc_size); + return ret; +} + +void __tracing_unload(void) +{ + hyp_spin_lock(&trace_buffer.lock); + hyp_trace_buffer_unload(&trace_buffer); + hyp_spin_unlock(&trace_buffer.lock); +} + +int __tracing_enable(bool enable) +{ + int cpu, ret = enable ? -EINVAL : 0; + + hyp_spin_lock(&trace_buffer.lock); + + if (!hyp_trace_buffer_loaded(&trace_buffer)) + goto unlock; + + for (cpu = 0; cpu < hyp_nr_cpus; cpu++) + simple_ring_buffer_enable_tracing(per_cpu_ptr(trace_buffer.simple_rbs, cpu), + enable); + + ret = 0; + +unlock: + hyp_spin_unlock(&trace_buffer.lock); + + return ret; +} + +int __tracing_swap_reader(unsigned int cpu) +{ + int ret = -ENODEV; + + if (cpu >= hyp_nr_cpus) + return -EINVAL; + + hyp_spin_lock(&trace_buffer.lock); + + if (hyp_trace_buffer_loaded(&trace_buffer)) + ret = simple_ring_buffer_swap_reader_page( + per_cpu_ptr(trace_buffer.simple_rbs, cpu)); + + hyp_spin_unlock(&trace_buffer.lock); + + return ret; +} + +void __tracing_update_clock(u32 mult, u32 shift, u64 epoch_ns, u64 epoch_cyc) +{ + int cpu; + + /* After this loop, all CPUs are observing the new bank... */ + for (cpu = 0; cpu < hyp_nr_cpus; cpu++) { + struct simple_rb_per_cpu *simple_rb = per_cpu_ptr(trace_buffer.simple_rbs, cpu); + + while (READ_ONCE(simple_rb->status) == SIMPLE_RB_WRITING) + ; + } + + /* ...we can now override the old one and swap. */ + trace_clock_update(mult, shift, epoch_ns, epoch_cyc); +} + +int __tracing_reset(unsigned int cpu) +{ + int ret = -ENODEV; + + if (cpu >= hyp_nr_cpus) + return -EINVAL; + + hyp_spin_lock(&trace_buffer.lock); + + if (hyp_trace_buffer_loaded(&trace_buffer)) + ret = simple_ring_buffer_reset(per_cpu_ptr(trace_buffer.simple_rbs, cpu)); + + hyp_spin_unlock(&trace_buffer.lock); + + return ret; +} diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index 30226f2d5564..0c1defa5fb0f 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -114,11 +114,6 @@ static kvm_pte_t kvm_init_valid_leaf_pte(u64 pa, kvm_pte_t attr, s8 level) return pte; } -static kvm_pte_t kvm_init_invalid_leaf_owner(u8 owner_id) -{ - return FIELD_PREP(KVM_INVALID_PTE_OWNER_MASK, owner_id); -} - static int kvm_pgtable_visitor_cb(struct kvm_pgtable_walk_data *data, const struct kvm_pgtable_visit_ctx *ctx, enum kvm_pgtable_walk_flags visit) @@ -581,7 +576,7 @@ void kvm_pgtable_hyp_destroy(struct kvm_pgtable *pgt) struct stage2_map_data { const u64 phys; kvm_pte_t attr; - u8 owner_id; + kvm_pte_t pte_annot; kvm_pte_t *anchor; kvm_pte_t *childp; @@ -798,7 +793,11 @@ static bool stage2_pte_is_counted(kvm_pte_t pte) static bool stage2_pte_is_locked(kvm_pte_t pte) { - return !kvm_pte_valid(pte) && (pte & KVM_INVALID_PTE_LOCKED); + if (kvm_pte_valid(pte)) + return false; + + return FIELD_GET(KVM_INVALID_PTE_TYPE_MASK, pte) == + KVM_INVALID_PTE_TYPE_LOCKED; } static bool stage2_try_set_pte(const struct kvm_pgtable_visit_ctx *ctx, kvm_pte_t new) @@ -829,6 +828,7 @@ static bool stage2_try_break_pte(const struct kvm_pgtable_visit_ctx *ctx, struct kvm_s2_mmu *mmu) { struct kvm_pgtable_mm_ops *mm_ops = ctx->mm_ops; + kvm_pte_t locked_pte; if (stage2_pte_is_locked(ctx->old)) { /* @@ -839,7 +839,9 @@ static bool stage2_try_break_pte(const struct kvm_pgtable_visit_ctx *ctx, return false; } - if (!stage2_try_set_pte(ctx, KVM_INVALID_PTE_LOCKED)) + locked_pte = FIELD_PREP(KVM_INVALID_PTE_TYPE_MASK, + KVM_INVALID_PTE_TYPE_LOCKED); + if (!stage2_try_set_pte(ctx, locked_pte)) return false; if (!kvm_pgtable_walk_skip_bbm_tlbi(ctx)) { @@ -964,7 +966,7 @@ static int stage2_map_walker_try_leaf(const struct kvm_pgtable_visit_ctx *ctx, if (!data->annotation) new = kvm_init_valid_leaf_pte(phys, data->attr, ctx->level); else - new = kvm_init_invalid_leaf_owner(data->owner_id); + new = data->pte_annot; /* * Skip updating the PTE if we are trying to recreate the exact @@ -1118,16 +1120,18 @@ int kvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size, return ret; } -int kvm_pgtable_stage2_set_owner(struct kvm_pgtable *pgt, u64 addr, u64 size, - void *mc, u8 owner_id) +int kvm_pgtable_stage2_annotate(struct kvm_pgtable *pgt, u64 addr, u64 size, + void *mc, enum kvm_invalid_pte_type type, + kvm_pte_t pte_annot) { int ret; struct stage2_map_data map_data = { .mmu = pgt->mmu, .memcache = mc, - .owner_id = owner_id, .force_pte = true, .annotation = true, + .pte_annot = pte_annot | + FIELD_PREP(KVM_INVALID_PTE_TYPE_MASK, type), }; struct kvm_pgtable_walker walker = { .cb = stage2_map_walker, @@ -1136,7 +1140,10 @@ int kvm_pgtable_stage2_set_owner(struct kvm_pgtable *pgt, u64 addr, u64 size, .arg = &map_data, }; - if (owner_id > KVM_MAX_OWNER_ID) + if (pte_annot & ~KVM_INVALID_PTE_ANNOT_MASK) + return -EINVAL; + + if (!type || type == KVM_INVALID_PTE_TYPE_LOCKED) return -EINVAL; ret = kvm_pgtable_walk(pgt, addr, size, &walker); diff --git a/arch/arm64/kvm/hyp/vgic-v5-sr.c b/arch/arm64/kvm/hyp/vgic-v5-sr.c new file mode 100644 index 000000000000..47e6bcd43702 --- /dev/null +++ b/arch/arm64/kvm/hyp/vgic-v5-sr.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025, 2026 - Arm Ltd + */ + +#include + +#include + +void __vgic_v5_save_apr(struct vgic_v5_cpu_if *cpu_if) +{ + cpu_if->vgic_apr = read_sysreg_s(SYS_ICH_APR_EL2); +} + +static void __vgic_v5_compat_mode_disable(void) +{ + sysreg_clear_set_s(SYS_ICH_VCTLR_EL2, ICH_VCTLR_EL2_V3, 0); + isb(); +} + +void __vgic_v5_restore_vmcr_apr(struct vgic_v5_cpu_if *cpu_if) +{ + __vgic_v5_compat_mode_disable(); + + write_sysreg_s(cpu_if->vgic_vmcr, SYS_ICH_VMCR_EL2); + write_sysreg_s(cpu_if->vgic_apr, SYS_ICH_APR_EL2); +} + +void __vgic_v5_save_ppi_state(struct vgic_v5_cpu_if *cpu_if) +{ + /* + * The following code assumes that the bitmap storage that we have for + * PPIs is either 64 (architected PPIs, only) or 128 bits (architected & + * impdef PPIs). + */ + BUILD_BUG_ON(VGIC_V5_NR_PRIVATE_IRQS % 64); + + bitmap_write(host_data_ptr(vgic_v5_ppi_state)->activer_exit, + read_sysreg_s(SYS_ICH_PPI_ACTIVER0_EL2), 0, 64); + bitmap_write(host_data_ptr(vgic_v5_ppi_state)->pendr, + read_sysreg_s(SYS_ICH_PPI_PENDR0_EL2), 0, 64); + + cpu_if->vgic_ppi_priorityr[0] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR0_EL2); + cpu_if->vgic_ppi_priorityr[1] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR1_EL2); + cpu_if->vgic_ppi_priorityr[2] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR2_EL2); + cpu_if->vgic_ppi_priorityr[3] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR3_EL2); + cpu_if->vgic_ppi_priorityr[4] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR4_EL2); + cpu_if->vgic_ppi_priorityr[5] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR5_EL2); + cpu_if->vgic_ppi_priorityr[6] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR6_EL2); + cpu_if->vgic_ppi_priorityr[7] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR7_EL2); + + if (VGIC_V5_NR_PRIVATE_IRQS == 128) { + bitmap_write(host_data_ptr(vgic_v5_ppi_state)->activer_exit, + read_sysreg_s(SYS_ICH_PPI_ACTIVER1_EL2), 64, 64); + bitmap_write(host_data_ptr(vgic_v5_ppi_state)->pendr, + read_sysreg_s(SYS_ICH_PPI_PENDR1_EL2), 64, 64); + + cpu_if->vgic_ppi_priorityr[8] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR8_EL2); + cpu_if->vgic_ppi_priorityr[9] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR9_EL2); + cpu_if->vgic_ppi_priorityr[10] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR10_EL2); + cpu_if->vgic_ppi_priorityr[11] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR11_EL2); + cpu_if->vgic_ppi_priorityr[12] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR12_EL2); + cpu_if->vgic_ppi_priorityr[13] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR13_EL2); + cpu_if->vgic_ppi_priorityr[14] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR14_EL2); + cpu_if->vgic_ppi_priorityr[15] = read_sysreg_s(SYS_ICH_PPI_PRIORITYR15_EL2); + } + + /* Now that we are done, disable DVI */ + write_sysreg_s(0, SYS_ICH_PPI_DVIR0_EL2); + write_sysreg_s(0, SYS_ICH_PPI_DVIR1_EL2); +} + +void __vgic_v5_restore_ppi_state(struct vgic_v5_cpu_if *cpu_if) +{ + DECLARE_BITMAP(pendr, VGIC_V5_NR_PRIVATE_IRQS); + + /* We assume 64 or 128 PPIs - see above comment */ + BUILD_BUG_ON(VGIC_V5_NR_PRIVATE_IRQS % 64); + + /* Enable DVI so that the guest's interrupt config takes over */ + write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_dvir, 0, 64), + SYS_ICH_PPI_DVIR0_EL2); + + write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_activer, 0, 64), + SYS_ICH_PPI_ACTIVER0_EL2); + write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_enabler, 0, 64), + SYS_ICH_PPI_ENABLER0_EL2); + + /* Update the pending state of the NON-DVI'd PPIs, only */ + bitmap_andnot(pendr, host_data_ptr(vgic_v5_ppi_state)->pendr, + cpu_if->vgic_ppi_dvir, VGIC_V5_NR_PRIVATE_IRQS); + write_sysreg_s(bitmap_read(pendr, 0, 64), SYS_ICH_PPI_PENDR0_EL2); + + write_sysreg_s(cpu_if->vgic_ppi_priorityr[0], + SYS_ICH_PPI_PRIORITYR0_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[1], + SYS_ICH_PPI_PRIORITYR1_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[2], + SYS_ICH_PPI_PRIORITYR2_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[3], + SYS_ICH_PPI_PRIORITYR3_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[4], + SYS_ICH_PPI_PRIORITYR4_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[5], + SYS_ICH_PPI_PRIORITYR5_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[6], + SYS_ICH_PPI_PRIORITYR6_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[7], + SYS_ICH_PPI_PRIORITYR7_EL2); + + if (VGIC_V5_NR_PRIVATE_IRQS == 128) { + /* Enable DVI so that the guest's interrupt config takes over */ + write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_dvir, 64, 64), + SYS_ICH_PPI_DVIR1_EL2); + + write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_activer, 64, 64), + SYS_ICH_PPI_ACTIVER1_EL2); + write_sysreg_s(bitmap_read(cpu_if->vgic_ppi_enabler, 64, 64), + SYS_ICH_PPI_ENABLER1_EL2); + write_sysreg_s(bitmap_read(pendr, 64, 64), + SYS_ICH_PPI_PENDR1_EL2); + + write_sysreg_s(cpu_if->vgic_ppi_priorityr[8], + SYS_ICH_PPI_PRIORITYR8_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[9], + SYS_ICH_PPI_PRIORITYR9_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[10], + SYS_ICH_PPI_PRIORITYR10_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[11], + SYS_ICH_PPI_PRIORITYR11_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[12], + SYS_ICH_PPI_PRIORITYR12_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[13], + SYS_ICH_PPI_PRIORITYR13_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[14], + SYS_ICH_PPI_PRIORITYR14_EL2); + write_sysreg_s(cpu_if->vgic_ppi_priorityr[15], + SYS_ICH_PPI_PRIORITYR15_EL2); + } else { + write_sysreg_s(0, SYS_ICH_PPI_DVIR1_EL2); + + write_sysreg_s(0, SYS_ICH_PPI_ACTIVER1_EL2); + write_sysreg_s(0, SYS_ICH_PPI_ENABLER1_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PENDR1_EL2); + + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR8_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR9_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR10_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR11_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR12_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR13_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR14_EL2); + write_sysreg_s(0, SYS_ICH_PPI_PRIORITYR15_EL2); + } +} + +void __vgic_v5_save_state(struct vgic_v5_cpu_if *cpu_if) +{ + cpu_if->vgic_vmcr = read_sysreg_s(SYS_ICH_VMCR_EL2); + cpu_if->vgic_icsr = read_sysreg_s(SYS_ICC_ICSR_EL1); +} + +void __vgic_v5_restore_state(struct vgic_v5_cpu_if *cpu_if) +{ + write_sysreg_s(cpu_if->vgic_icsr, SYS_ICC_ICSR_EL1); +} diff --git a/arch/arm64/kvm/hyp/vhe/Makefile b/arch/arm64/kvm/hyp/vhe/Makefile index afc4aed9231a..9695328bbd96 100644 --- a/arch/arm64/kvm/hyp/vhe/Makefile +++ b/arch/arm64/kvm/hyp/vhe/Makefile @@ -10,4 +10,4 @@ CFLAGS_switch.o += -Wno-override-init obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \ - ../fpsimd.o ../hyp-entry.o ../exception.o + ../fpsimd.o ../hyp-entry.o ../exception.o ../vgic-v5-sr.o diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c index 9db3f11a4754..1e8995add14f 100644 --- a/arch/arm64/kvm/hyp/vhe/switch.c +++ b/arch/arm64/kvm/hyp/vhe/switch.c @@ -663,7 +663,8 @@ static void __noreturn __hyp_call_panic(u64 spsr, u64 elr, u64 par) host_ctxt = host_data_ptr(host_ctxt); vcpu = host_ctxt->__hyp_running_vcpu; - __deactivate_traps(vcpu); + if (vcpu) + __deactivate_traps(vcpu); sysreg_restore_host_state_vhe(host_ctxt); panic("HYP panic:\nPS:%08llx PC:%016llx ESR:%08llx\nFAR:%016llx HPFAR:%016llx PAR:%016llx\nVCPU:%p\n", diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c new file mode 100644 index 000000000000..8b7f2bf2fba8 --- /dev/null +++ b/arch/arm64/kvm/hyp_trace.c @@ -0,0 +1,442 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Google LLC + * Author: Vincent Donnefort + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "hyp_trace.h" + +/* Same 10min used by clocksource when width is more than 32-bits */ +#define CLOCK_MAX_CONVERSION_S 600 +/* + * Time to give for the clock init. Long enough to get a good mult/shift + * estimation. Short enough to not delay the tracing start too much. + */ +#define CLOCK_INIT_MS 100 +/* + * Time between clock checks. Must be small enough to catch clock deviation when + * it is still tiny. + */ +#define CLOCK_UPDATE_MS 500 + +static struct hyp_trace_clock { + u64 cycles; + u64 cyc_overflow64; + u64 boot; + u32 mult; + u32 shift; + struct delayed_work work; + struct completion ready; + struct mutex lock; + bool running; +} hyp_clock; + +static void __hyp_clock_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct hyp_trace_clock *hyp_clock; + struct system_time_snapshot snap; + u64 rate, delta_cycles; + u64 boot, delta_boot; + + hyp_clock = container_of(dwork, struct hyp_trace_clock, work); + + ktime_get_snapshot(&snap); + boot = ktime_to_ns(snap.boot); + + delta_boot = boot - hyp_clock->boot; + delta_cycles = snap.cycles - hyp_clock->cycles; + + /* Compare hyp clock with the kernel boot clock */ + if (hyp_clock->mult) { + u64 err, cur = delta_cycles; + + if (WARN_ON_ONCE(cur >= hyp_clock->cyc_overflow64)) { + __uint128_t tmp = (__uint128_t)cur * hyp_clock->mult; + + cur = tmp >> hyp_clock->shift; + } else { + cur *= hyp_clock->mult; + cur >>= hyp_clock->shift; + } + cur += hyp_clock->boot; + + err = abs_diff(cur, boot); + /* No deviation, only update epoch if necessary */ + if (!err) { + if (delta_cycles >= (hyp_clock->cyc_overflow64 >> 1)) + goto fast_forward; + + goto resched; + } + + /* Warn if the error is above tracing precision (1us) */ + if (err > NSEC_PER_USEC) + pr_warn_ratelimited("hyp trace clock off by %lluus\n", + err / NSEC_PER_USEC); + } + + rate = div64_u64(delta_cycles * NSEC_PER_SEC, delta_boot); + + clocks_calc_mult_shift(&hyp_clock->mult, &hyp_clock->shift, + rate, NSEC_PER_SEC, CLOCK_MAX_CONVERSION_S); + + /* Add a comfortable 50% margin */ + hyp_clock->cyc_overflow64 = (U64_MAX / hyp_clock->mult) >> 1; + +fast_forward: + hyp_clock->cycles = snap.cycles; + hyp_clock->boot = boot; + kvm_call_hyp_nvhe(__tracing_update_clock, hyp_clock->mult, + hyp_clock->shift, hyp_clock->boot, hyp_clock->cycles); + complete(&hyp_clock->ready); + +resched: + schedule_delayed_work(&hyp_clock->work, + msecs_to_jiffies(CLOCK_UPDATE_MS)); +} + +static void hyp_trace_clock_enable(struct hyp_trace_clock *hyp_clock, bool enable) +{ + struct system_time_snapshot snap; + + if (hyp_clock->running == enable) + return; + + if (!enable) { + cancel_delayed_work_sync(&hyp_clock->work); + hyp_clock->running = false; + } + + ktime_get_snapshot(&snap); + + hyp_clock->boot = ktime_to_ns(snap.boot); + hyp_clock->cycles = snap.cycles; + hyp_clock->mult = 0; + + init_completion(&hyp_clock->ready); + INIT_DELAYED_WORK(&hyp_clock->work, __hyp_clock_work); + schedule_delayed_work(&hyp_clock->work, msecs_to_jiffies(CLOCK_INIT_MS)); + wait_for_completion(&hyp_clock->ready); + hyp_clock->running = true; +} + +/* Access to this struct within the trace_remote_callbacks are protected by the trace_remote lock */ +static struct hyp_trace_buffer { + struct hyp_trace_desc *desc; + size_t desc_size; +} trace_buffer; + +static int __map_hyp(void *start, size_t size) +{ + if (is_protected_kvm_enabled()) + return 0; + + return create_hyp_mappings(start, start + size, PAGE_HYP); +} + +static int __share_page(unsigned long va) +{ + return kvm_share_hyp((void *)va, (void *)va + 1); +} + +static void __unshare_page(unsigned long va) +{ + kvm_unshare_hyp((void *)va, (void *)va + 1); +} + +static int hyp_trace_buffer_alloc_bpages_backing(struct hyp_trace_buffer *trace_buffer, size_t size) +{ + int nr_bpages = (PAGE_ALIGN(size) / PAGE_SIZE) + 1; + size_t backing_size; + void *start; + + backing_size = PAGE_ALIGN(sizeof(struct simple_buffer_page) * nr_bpages * + num_possible_cpus()); + + start = alloc_pages_exact(backing_size, GFP_KERNEL_ACCOUNT); + if (!start) + return -ENOMEM; + + trace_buffer->desc->bpages_backing_start = (unsigned long)start; + trace_buffer->desc->bpages_backing_size = backing_size; + + return __map_hyp(start, backing_size); +} + +static void hyp_trace_buffer_free_bpages_backing(struct hyp_trace_buffer *trace_buffer) +{ + free_pages_exact((void *)trace_buffer->desc->bpages_backing_start, + trace_buffer->desc->bpages_backing_size); +} + +static void hyp_trace_buffer_unshare_hyp(struct hyp_trace_buffer *trace_buffer, int last_cpu) +{ + struct ring_buffer_desc *rb_desc; + int cpu, p; + + for_each_ring_buffer_desc(rb_desc, cpu, &trace_buffer->desc->trace_buffer_desc) { + if (cpu > last_cpu) + break; + + __share_page(rb_desc->meta_va); + for (p = 0; p < rb_desc->nr_page_va; p++) + __unshare_page(rb_desc->page_va[p]); + } +} + +static int hyp_trace_buffer_share_hyp(struct hyp_trace_buffer *trace_buffer) +{ + struct ring_buffer_desc *rb_desc; + int cpu, p, ret = 0; + + for_each_ring_buffer_desc(rb_desc, cpu, &trace_buffer->desc->trace_buffer_desc) { + ret = __share_page(rb_desc->meta_va); + if (ret) + break; + + for (p = 0; p < rb_desc->nr_page_va; p++) { + ret = __share_page(rb_desc->page_va[p]); + if (ret) + break; + } + + if (ret) { + for (p--; p >= 0; p--) + __unshare_page(rb_desc->page_va[p]); + break; + } + } + + if (ret) + hyp_trace_buffer_unshare_hyp(trace_buffer, cpu--); + + return ret; +} + +static struct trace_buffer_desc *hyp_trace_load(unsigned long size, void *priv) +{ + struct hyp_trace_buffer *trace_buffer = priv; + struct hyp_trace_desc *desc; + size_t desc_size; + int ret; + + if (WARN_ON(trace_buffer->desc)) + return ERR_PTR(-EINVAL); + + desc_size = trace_buffer_desc_size(size, num_possible_cpus()); + if (desc_size == SIZE_MAX) + return ERR_PTR(-E2BIG); + + desc_size = PAGE_ALIGN(desc_size); + desc = (struct hyp_trace_desc *)alloc_pages_exact(desc_size, GFP_KERNEL); + if (!desc) + return ERR_PTR(-ENOMEM); + + ret = __map_hyp(desc, desc_size); + if (ret) + goto err_free_desc; + + trace_buffer->desc = desc; + + ret = hyp_trace_buffer_alloc_bpages_backing(trace_buffer, size); + if (ret) + goto err_free_desc; + + ret = trace_remote_alloc_buffer(&desc->trace_buffer_desc, desc_size, size, + cpu_possible_mask); + if (ret) + goto err_free_backing; + + ret = hyp_trace_buffer_share_hyp(trace_buffer); + if (ret) + goto err_free_buffer; + + ret = kvm_call_hyp_nvhe(__tracing_load, (unsigned long)desc, desc_size); + if (ret) + goto err_unload_pages; + + return &desc->trace_buffer_desc; + +err_unload_pages: + hyp_trace_buffer_unshare_hyp(trace_buffer, INT_MAX); + +err_free_buffer: + trace_remote_free_buffer(&desc->trace_buffer_desc); + +err_free_backing: + hyp_trace_buffer_free_bpages_backing(trace_buffer); + +err_free_desc: + free_pages_exact(desc, desc_size); + trace_buffer->desc = NULL; + + return ERR_PTR(ret); +} + +static void hyp_trace_unload(struct trace_buffer_desc *desc, void *priv) +{ + struct hyp_trace_buffer *trace_buffer = priv; + + if (WARN_ON(desc != &trace_buffer->desc->trace_buffer_desc)) + return; + + kvm_call_hyp_nvhe(__tracing_unload); + hyp_trace_buffer_unshare_hyp(trace_buffer, INT_MAX); + trace_remote_free_buffer(desc); + hyp_trace_buffer_free_bpages_backing(trace_buffer); + free_pages_exact(trace_buffer->desc, trace_buffer->desc_size); + trace_buffer->desc = NULL; +} + +static int hyp_trace_enable_tracing(bool enable, void *priv) +{ + hyp_trace_clock_enable(&hyp_clock, enable); + + return kvm_call_hyp_nvhe(__tracing_enable, enable); +} + +static int hyp_trace_swap_reader_page(unsigned int cpu, void *priv) +{ + return kvm_call_hyp_nvhe(__tracing_swap_reader, cpu); +} + +static int hyp_trace_reset(unsigned int cpu, void *priv) +{ + return kvm_call_hyp_nvhe(__tracing_reset, cpu); +} + +static int hyp_trace_enable_event(unsigned short id, bool enable, void *priv) +{ + struct hyp_event_id *event_id = lm_alias(&__hyp_event_ids_start[id]); + struct page *page; + atomic_t *enabled; + void *map; + + if (is_protected_kvm_enabled()) + return kvm_call_hyp_nvhe(__tracing_enable_event, id, enable); + + enabled = &event_id->enabled; + page = virt_to_page(enabled); + map = vmap(&page, 1, VM_MAP, PAGE_KERNEL); + if (!map) + return -ENOMEM; + + enabled = map + offset_in_page(enabled); + atomic_set(enabled, enable); + + vunmap(map); + + return 0; +} + +static int hyp_trace_clock_show(struct seq_file *m, void *v) +{ + seq_puts(m, "[boot]\n"); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(hyp_trace_clock); + +static ssize_t hyp_trace_write_event_write(struct file *f, const char __user *ubuf, + size_t cnt, loff_t *pos) +{ + unsigned long val; + int ret; + + ret = kstrtoul_from_user(ubuf, cnt, 10, &val); + if (ret) + return ret; + + kvm_call_hyp_nvhe(__tracing_write_event, val); + + return cnt; +} + +static const struct file_operations hyp_trace_write_event_fops = { + .write = hyp_trace_write_event_write, +}; + +static int hyp_trace_init_tracefs(struct dentry *d, void *priv) +{ + if (!tracefs_create_file("write_event", 0200, d, NULL, &hyp_trace_write_event_fops)) + return -ENOMEM; + + return tracefs_create_file("trace_clock", 0440, d, NULL, &hyp_trace_clock_fops) ? + 0 : -ENOMEM; +} + +static struct trace_remote_callbacks trace_remote_callbacks = { + .init = hyp_trace_init_tracefs, + .load_trace_buffer = hyp_trace_load, + .unload_trace_buffer = hyp_trace_unload, + .enable_tracing = hyp_trace_enable_tracing, + .swap_reader_page = hyp_trace_swap_reader_page, + .reset = hyp_trace_reset, + .enable_event = hyp_trace_enable_event, +}; + +static const char *__hyp_enter_exit_reason_str(u8 reason); + +#include + +static const char *__hyp_enter_exit_reason_str(u8 reason) +{ + static const char strs[][12] = { + "smc", + "hvc", + "psci", + "host_abort", + "guest_exit", + "eret_host", + "eret_guest", + "unknown", + }; + + return strs[min(reason, HYP_REASON_UNKNOWN)]; +} + +static void __init hyp_trace_init_events(void) +{ + struct hyp_event_id *hyp_event_id = __hyp_event_ids_start; + struct remote_event *event = __hyp_events_start; + int id = 0; + + /* Events on both sides hypervisor are sorted */ + for (; event < __hyp_events_end; event++, hyp_event_id++, id++) + event->id = hyp_event_id->id = id; +} + +int __init kvm_hyp_trace_init(void) +{ + int cpu; + + if (is_kernel_in_hyp_mode()) + return 0; + + for_each_possible_cpu(cpu) { + const struct arch_timer_erratum_workaround *wa = + per_cpu(timer_unstable_counter_workaround, cpu); + + if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND) && + wa && wa->read_cntvct_el0) { + pr_warn("hyp trace can't handle CNTVCT workaround '%s'\n", wa->desc); + return -EOPNOTSUPP; + } + } + + hyp_trace_init_events(); + + return trace_remote_register("hypervisor", &trace_remote_callbacks, &trace_buffer, + __hyp_events_start, __hyp_events_end - __hyp_events_start); +} diff --git a/arch/arm64/kvm/hyp_trace.h b/arch/arm64/kvm/hyp_trace.h new file mode 100644 index 000000000000..c991b1ec65f1 --- /dev/null +++ b/arch/arm64/kvm/hyp_trace.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __ARM64_KVM_HYP_TRACE_H__ +#define __ARM64_KVM_HYP_TRACE_H__ + +#ifdef CONFIG_NVHE_EL2_TRACING +int kvm_hyp_trace_init(void); +#else +static inline int kvm_hyp_trace_init(void) { return 0; } +#endif +#endif diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 17d64a1e11e5..4da9281312eb 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -340,6 +340,9 @@ static void __unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64 void kvm_stage2_unmap_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64 size, bool may_block) { + if (kvm_vm_is_protected(kvm_s2_mmu_to_kvm(mmu))) + return; + __unmap_stage2_range(mmu, start, size, may_block); } @@ -878,9 +881,6 @@ static int kvm_init_ipa_range(struct kvm_s2_mmu *mmu, unsigned long type) u64 mmfr0, mmfr1; u32 phys_shift; - if (type & ~KVM_VM_TYPE_ARM_IPA_SIZE_MASK) - return -EINVAL; - phys_shift = KVM_VM_TYPE_ARM_IPA_SIZE(type); if (is_protected_kvm_enabled()) { phys_shift = kvm_ipa_limit; @@ -1013,6 +1013,7 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t out_destroy_pgtable: kvm_stage2_destroy(pgt); + mmu->pgt = NULL; out_free_pgtable: kfree(pgt); return err; @@ -1400,10 +1401,10 @@ static bool fault_supports_stage2_huge_mapping(struct kvm_memory_slot *memslot, */ static long transparent_hugepage_adjust(struct kvm *kvm, struct kvm_memory_slot *memslot, - unsigned long hva, kvm_pfn_t *pfnp, - phys_addr_t *ipap) + unsigned long hva, kvm_pfn_t *pfnp, gfn_t *gfnp) { kvm_pfn_t pfn = *pfnp; + gfn_t gfn = *gfnp; /* * Make sure the adjustment is done only for THP pages. Also make @@ -1419,7 +1420,8 @@ transparent_hugepage_adjust(struct kvm *kvm, struct kvm_memory_slot *memslot, if (sz < PMD_SIZE) return PAGE_SIZE; - *ipap &= PMD_MASK; + gfn &= ~(PTRS_PER_PMD - 1); + *gfnp = gfn; pfn &= ~(PTRS_PER_PMD - 1); *pfnp = pfn; @@ -1512,25 +1514,22 @@ static bool kvm_vma_is_cacheable(struct vm_area_struct *vma) } } -static int prepare_mmu_memcache(struct kvm_vcpu *vcpu, bool topup_memcache, - void **memcache) +static void *get_mmu_memcache(struct kvm_vcpu *vcpu) { - int min_pages; - if (!is_protected_kvm_enabled()) - *memcache = &vcpu->arch.mmu_page_cache; + return &vcpu->arch.mmu_page_cache; else - *memcache = &vcpu->arch.pkvm_memcache; + return &vcpu->arch.pkvm_memcache; +} - if (!topup_memcache) - return 0; - - min_pages = kvm_mmu_cache_min_pages(vcpu->arch.hw_mmu); +static int topup_mmu_memcache(struct kvm_vcpu *vcpu, void *memcache) +{ + int min_pages = kvm_mmu_cache_min_pages(vcpu->arch.hw_mmu); if (!is_protected_kvm_enabled()) - return kvm_mmu_topup_memory_cache(*memcache, min_pages); + return kvm_mmu_topup_memory_cache(memcache, min_pages); - return topup_hyp_memcache(*memcache, min_pages); + return topup_hyp_memcache(memcache, min_pages); } /* @@ -1543,54 +1542,66 @@ static int prepare_mmu_memcache(struct kvm_vcpu *vcpu, bool topup_memcache, * TLB invalidation from the guest and used to limit the invalidation scope if a * TTL hint or a range isn't provided. */ -static void adjust_nested_fault_perms(struct kvm_s2_trans *nested, - enum kvm_pgtable_prot *prot, - bool *writable) +static enum kvm_pgtable_prot adjust_nested_fault_perms(struct kvm_s2_trans *nested, + enum kvm_pgtable_prot prot) { - *writable &= kvm_s2_trans_writable(nested); + if (!kvm_s2_trans_writable(nested)) + prot &= ~KVM_PGTABLE_PROT_W; if (!kvm_s2_trans_readable(nested)) - *prot &= ~KVM_PGTABLE_PROT_R; + prot &= ~KVM_PGTABLE_PROT_R; - *prot |= kvm_encode_nested_level(nested); + return prot | kvm_encode_nested_level(nested); } -static void adjust_nested_exec_perms(struct kvm *kvm, - struct kvm_s2_trans *nested, - enum kvm_pgtable_prot *prot) +static enum kvm_pgtable_prot adjust_nested_exec_perms(struct kvm *kvm, + struct kvm_s2_trans *nested, + enum kvm_pgtable_prot prot) { if (!kvm_s2_trans_exec_el0(kvm, nested)) - *prot &= ~KVM_PGTABLE_PROT_UX; + prot &= ~KVM_PGTABLE_PROT_UX; if (!kvm_s2_trans_exec_el1(kvm, nested)) - *prot &= ~KVM_PGTABLE_PROT_PX; + prot &= ~KVM_PGTABLE_PROT_PX; + + return prot; } -static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, - struct kvm_s2_trans *nested, - struct kvm_memory_slot *memslot, bool is_perm) +struct kvm_s2_fault_desc { + struct kvm_vcpu *vcpu; + phys_addr_t fault_ipa; + struct kvm_s2_trans *nested; + struct kvm_memory_slot *memslot; + unsigned long hva; +}; + +static int gmem_abort(const struct kvm_s2_fault_desc *s2fd) { - bool write_fault, exec_fault, writable; + bool write_fault, exec_fault; + bool perm_fault = kvm_vcpu_trap_is_permission_fault(s2fd->vcpu); enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED; enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R; - struct kvm_pgtable *pgt = vcpu->arch.hw_mmu->pgt; + struct kvm_pgtable *pgt = s2fd->vcpu->arch.hw_mmu->pgt; unsigned long mmu_seq; struct page *page; - struct kvm *kvm = vcpu->kvm; - void *memcache; + struct kvm *kvm = s2fd->vcpu->kvm; + void *memcache = NULL; kvm_pfn_t pfn; gfn_t gfn; int ret; - ret = prepare_mmu_memcache(vcpu, true, &memcache); - if (ret) - return ret; + if (!perm_fault) { + memcache = get_mmu_memcache(s2fd->vcpu); + ret = topup_mmu_memcache(s2fd->vcpu, memcache); + if (ret) + return ret; + } - if (nested) - gfn = kvm_s2_trans_output(nested) >> PAGE_SHIFT; + if (s2fd->nested) + gfn = kvm_s2_trans_output(s2fd->nested) >> PAGE_SHIFT; else - gfn = fault_ipa >> PAGE_SHIFT; + gfn = s2fd->fault_ipa >> PAGE_SHIFT; - write_fault = kvm_is_write_fault(vcpu); - exec_fault = kvm_vcpu_trap_is_exec_fault(vcpu); + write_fault = kvm_is_write_fault(s2fd->vcpu); + exec_fault = kvm_vcpu_trap_is_exec_fault(s2fd->vcpu); VM_WARN_ON_ONCE(write_fault && exec_fault); @@ -1598,26 +1609,24 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, /* Pairs with the smp_wmb() in kvm_mmu_invalidate_end(). */ smp_rmb(); - ret = kvm_gmem_get_pfn(kvm, memslot, gfn, &pfn, &page, NULL); + ret = kvm_gmem_get_pfn(kvm, s2fd->memslot, gfn, &pfn, &page, NULL); if (ret) { - kvm_prepare_memory_fault_exit(vcpu, fault_ipa, PAGE_SIZE, + kvm_prepare_memory_fault_exit(s2fd->vcpu, s2fd->fault_ipa, PAGE_SIZE, write_fault, exec_fault, false); return ret; } - writable = !(memslot->flags & KVM_MEM_READONLY); - - if (nested) - adjust_nested_fault_perms(nested, &prot, &writable); - - if (writable) + if (!(s2fd->memslot->flags & KVM_MEM_READONLY)) prot |= KVM_PGTABLE_PROT_W; + if (s2fd->nested) + prot = adjust_nested_fault_perms(s2fd->nested, prot); + if (exec_fault || cpus_have_final_cap(ARM64_HAS_CACHE_DIC)) prot |= KVM_PGTABLE_PROT_X; - if (nested) - adjust_nested_exec_perms(kvm, nested, &prot); + if (s2fd->nested) + prot = adjust_nested_exec_perms(kvm, s2fd->nested, prot); kvm_fault_lock(kvm); if (mmu_invalidate_retry(kvm, mmu_seq)) { @@ -1625,85 +1634,132 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, goto out_unlock; } - ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, fault_ipa, PAGE_SIZE, - __pfn_to_phys(pfn), prot, - memcache, flags); + if (perm_fault) { + /* + * Drop the SW bits in favour of those stored in the + * PTE, which will be preserved. + */ + prot &= ~KVM_NV_GUEST_MAP_SZ; + ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, s2fd->fault_ipa, + prot, flags); + } else { + ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, s2fd->fault_ipa, PAGE_SIZE, + __pfn_to_phys(pfn), prot, + memcache, flags); + } out_unlock: - kvm_release_faultin_page(kvm, page, !!ret, writable); + kvm_release_faultin_page(kvm, page, !!ret, prot & KVM_PGTABLE_PROT_W); kvm_fault_unlock(kvm); - if (writable && !ret) - mark_page_dirty_in_slot(kvm, memslot, gfn); + if ((prot & KVM_PGTABLE_PROT_W) && !ret) + mark_page_dirty_in_slot(kvm, s2fd->memslot, gfn); return ret != -EAGAIN ? ret : 0; } -static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, - struct kvm_s2_trans *nested, - struct kvm_memory_slot *memslot, unsigned long hva, - bool fault_is_perm) +struct kvm_s2_fault_vma_info { + unsigned long mmu_seq; + long vma_pagesize; + vm_flags_t vm_flags; + unsigned long max_map_size; + struct page *page; + kvm_pfn_t pfn; + gfn_t gfn; + bool device; + bool mte_allowed; + bool is_vma_cacheable; + bool map_writable; + bool map_non_cacheable; +}; + +static int pkvm_mem_abort(const struct kvm_s2_fault_desc *s2fd) { - int ret = 0; - bool topup_memcache; - bool write_fault, writable; - bool exec_fault, mte_allowed, is_vma_cacheable; - bool s2_force_noncacheable = false, vfio_allow_any_uc = false; - unsigned long mmu_seq; - phys_addr_t ipa = fault_ipa; + unsigned int flags = FOLL_HWPOISON | FOLL_LONGTERM | FOLL_WRITE; + struct kvm_vcpu *vcpu = s2fd->vcpu; + struct kvm_pgtable *pgt = vcpu->arch.hw_mmu->pgt; + struct mm_struct *mm = current->mm; struct kvm *kvm = vcpu->kvm; - struct vm_area_struct *vma; - short vma_shift; - void *memcache; - gfn_t gfn; - kvm_pfn_t pfn; - bool logging_active = memslot_is_logging(memslot); - bool force_pte = logging_active; - long vma_pagesize, fault_granule; - enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R; - struct kvm_pgtable *pgt; + void *hyp_memcache; struct page *page; - vm_flags_t vm_flags; - enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED; + int ret; - if (fault_is_perm) - fault_granule = kvm_vcpu_trap_get_perm_fault_granule(vcpu); - write_fault = kvm_is_write_fault(vcpu); - exec_fault = kvm_vcpu_trap_is_exec_fault(vcpu); - VM_WARN_ON_ONCE(write_fault && exec_fault); + hyp_memcache = get_mmu_memcache(vcpu); + ret = topup_mmu_memcache(vcpu, hyp_memcache); + if (ret) + return -ENOMEM; - /* - * Permission faults just need to update the existing leaf entry, - * and so normally don't require allocations from the memcache. The - * only exception to this is when dirty logging is enabled at runtime - * and a write fault needs to collapse a block entry into a table. - */ - topup_memcache = !fault_is_perm || (logging_active && write_fault); - ret = prepare_mmu_memcache(vcpu, topup_memcache, &memcache); + ret = account_locked_vm(mm, 1, true); if (ret) return ret; - /* - * Let's check if we will get back a huge page backed by hugetlbfs, or - * get block mapping for device MMIO region. - */ - mmap_read_lock(current->mm); - vma = vma_lookup(current->mm, hva); - if (unlikely(!vma)) { - kvm_err("Failed to find VMA for hva 0x%lx\n", hva); - mmap_read_unlock(current->mm); - return -EFAULT; + mmap_read_lock(mm); + ret = pin_user_pages(s2fd->hva, 1, flags, &page); + mmap_read_unlock(mm); + + if (ret == -EHWPOISON) { + kvm_send_hwpoison_signal(s2fd->hva, PAGE_SHIFT); + ret = 0; + goto dec_account; + } else if (ret != 1) { + ret = -EFAULT; + goto dec_account; + } else if (!folio_test_swapbacked(page_folio(page))) { + /* + * We really can't deal with page-cache pages returned by GUP + * because (a) we may trigger writeback of a page for which we + * no longer have access and (b) page_mkclean() won't find the + * stage-2 mapping in the rmap so we can get out-of-whack with + * the filesystem when marking the page dirty during unpinning + * (see cc5095747edf ("ext4: don't BUG if someone dirty pages + * without asking ext4 first")). + * + * Ideally we'd just restrict ourselves to anonymous pages, but + * we also want to allow memfd (i.e. shmem) pages, so check for + * pages backed by swap in the knowledge that the GUP pin will + * prevent try_to_unmap() from succeeding. + */ + ret = -EIO; + goto unpin; } - if (force_pte) + write_lock(&kvm->mmu_lock); + ret = pkvm_pgtable_stage2_map(pgt, s2fd->fault_ipa, PAGE_SIZE, + page_to_phys(page), KVM_PGTABLE_PROT_RWX, + hyp_memcache, 0); + write_unlock(&kvm->mmu_lock); + if (ret) { + if (ret == -EAGAIN) + ret = 0; + goto unpin; + } + + return 0; +unpin: + unpin_user_pages(&page, 1); +dec_account: + account_locked_vm(mm, 1, false); + return ret; +} + +static short kvm_s2_resolve_vma_size(const struct kvm_s2_fault_desc *s2fd, + struct kvm_s2_fault_vma_info *s2vi, + struct vm_area_struct *vma) +{ + short vma_shift; + + if (memslot_is_logging(s2fd->memslot)) { + s2vi->max_map_size = PAGE_SIZE; vma_shift = PAGE_SHIFT; - else - vma_shift = get_vma_page_shift(vma, hva); + } else { + s2vi->max_map_size = PUD_SIZE; + vma_shift = get_vma_page_shift(vma, s2fd->hva); + } switch (vma_shift) { #ifndef __PAGETABLE_PMD_FOLDED case PUD_SHIFT: - if (fault_supports_stage2_huge_mapping(memslot, hva, PUD_SIZE)) + if (fault_supports_stage2_huge_mapping(s2fd->memslot, s2fd->hva, PUD_SIZE)) break; fallthrough; #endif @@ -1711,12 +1767,12 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, vma_shift = PMD_SHIFT; fallthrough; case PMD_SHIFT: - if (fault_supports_stage2_huge_mapping(memslot, hva, PMD_SIZE)) + if (fault_supports_stage2_huge_mapping(s2fd->memslot, s2fd->hva, PMD_SIZE)) break; fallthrough; case CONT_PTE_SHIFT: vma_shift = PAGE_SHIFT; - force_pte = true; + s2vi->max_map_size = PAGE_SIZE; fallthrough; case PAGE_SHIFT: break; @@ -1724,21 +1780,17 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, WARN_ONCE(1, "Unknown vma_shift %d", vma_shift); } - vma_pagesize = 1UL << vma_shift; - - if (nested) { + if (s2fd->nested) { unsigned long max_map_size; - max_map_size = force_pte ? PAGE_SIZE : PUD_SIZE; - - ipa = kvm_s2_trans_output(nested); + max_map_size = min(s2vi->max_map_size, PUD_SIZE); /* * If we're about to create a shadow stage 2 entry, then we * can only create a block mapping if the guest stage 2 page * table uses at least as big a mapping. */ - max_map_size = min(kvm_s2_trans_size(nested), max_map_size); + max_map_size = min(kvm_s2_trans_size(s2fd->nested), max_map_size); /* * Be careful that if the mapping size falls between @@ -1749,30 +1801,46 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, else if (max_map_size >= PAGE_SIZE && max_map_size < PMD_SIZE) max_map_size = PAGE_SIZE; - force_pte = (max_map_size == PAGE_SIZE); - vma_pagesize = min_t(long, vma_pagesize, max_map_size); - vma_shift = __ffs(vma_pagesize); + s2vi->max_map_size = max_map_size; + vma_shift = min_t(short, vma_shift, __ffs(max_map_size)); } + return vma_shift; +} + +static bool kvm_s2_fault_is_perm(const struct kvm_s2_fault_desc *s2fd) +{ + return kvm_vcpu_trap_is_permission_fault(s2fd->vcpu); +} + +static int kvm_s2_fault_get_vma_info(const struct kvm_s2_fault_desc *s2fd, + struct kvm_s2_fault_vma_info *s2vi) +{ + struct vm_area_struct *vma; + struct kvm *kvm = s2fd->vcpu->kvm; + + mmap_read_lock(current->mm); + vma = vma_lookup(current->mm, s2fd->hva); + if (unlikely(!vma)) { + kvm_err("Failed to find VMA for hva 0x%lx\n", s2fd->hva); + mmap_read_unlock(current->mm); + return -EFAULT; + } + + s2vi->vma_pagesize = BIT(kvm_s2_resolve_vma_size(s2fd, s2vi, vma)); + /* * Both the canonical IPA and fault IPA must be aligned to the * mapping size to ensure we find the right PFN and lay down the * mapping in the right place. */ - fault_ipa = ALIGN_DOWN(fault_ipa, vma_pagesize); - ipa = ALIGN_DOWN(ipa, vma_pagesize); + s2vi->gfn = ALIGN_DOWN(s2fd->fault_ipa, s2vi->vma_pagesize) >> PAGE_SHIFT; - gfn = ipa >> PAGE_SHIFT; - mte_allowed = kvm_vma_mte_allowed(vma); + s2vi->mte_allowed = kvm_vma_mte_allowed(vma); - vfio_allow_any_uc = vma->vm_flags & VM_ALLOW_ANY_UNCACHED; + s2vi->vm_flags = vma->vm_flags; - vm_flags = vma->vm_flags; - - is_vma_cacheable = kvm_vma_is_cacheable(vma); - - /* Don't use the VMA after the unlock -- it may have vanished */ - vma = NULL; + s2vi->is_vma_cacheable = kvm_vma_is_cacheable(vma); /* * Read mmu_invalidate_seq so that KVM can detect if the results of @@ -1782,24 +1850,50 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, * Rely on mmap_read_unlock() for an implicit smp_rmb(), which pairs * with the smp_wmb() in kvm_mmu_invalidate_end(). */ - mmu_seq = kvm->mmu_invalidate_seq; + s2vi->mmu_seq = kvm->mmu_invalidate_seq; mmap_read_unlock(current->mm); - pfn = __kvm_faultin_pfn(memslot, gfn, write_fault ? FOLL_WRITE : 0, - &writable, &page); - if (pfn == KVM_PFN_ERR_HWPOISON) { - kvm_send_hwpoison_signal(hva, vma_shift); - return 0; - } - if (is_error_noslot_pfn(pfn)) + return 0; +} + +static gfn_t get_canonical_gfn(const struct kvm_s2_fault_desc *s2fd, + const struct kvm_s2_fault_vma_info *s2vi) +{ + phys_addr_t ipa; + + if (!s2fd->nested) + return s2vi->gfn; + + ipa = kvm_s2_trans_output(s2fd->nested); + return ALIGN_DOWN(ipa, s2vi->vma_pagesize) >> PAGE_SHIFT; +} + +static int kvm_s2_fault_pin_pfn(const struct kvm_s2_fault_desc *s2fd, + struct kvm_s2_fault_vma_info *s2vi) +{ + int ret; + + ret = kvm_s2_fault_get_vma_info(s2fd, s2vi); + if (ret) + return ret; + + s2vi->pfn = __kvm_faultin_pfn(s2fd->memslot, get_canonical_gfn(s2fd, s2vi), + kvm_is_write_fault(s2fd->vcpu) ? FOLL_WRITE : 0, + &s2vi->map_writable, &s2vi->page); + if (unlikely(is_error_noslot_pfn(s2vi->pfn))) { + if (s2vi->pfn == KVM_PFN_ERR_HWPOISON) { + kvm_send_hwpoison_signal(s2fd->hva, __ffs(s2vi->vma_pagesize)); + return 0; + } return -EFAULT; + } /* * Check if this is non-struct page memory PFN, and cannot support * CMOs. It could potentially be unsafe to access as cacheable. */ - if (vm_flags & (VM_PFNMAP | VM_MIXEDMAP) && !pfn_is_map_memory(pfn)) { - if (is_vma_cacheable) { + if (s2vi->vm_flags & (VM_PFNMAP | VM_MIXEDMAP) && !pfn_is_map_memory(s2vi->pfn)) { + if (s2vi->is_vma_cacheable) { /* * Whilst the VMA owner expects cacheable mapping to this * PFN, hardware also has to support the FWB and CACHE DIC @@ -1812,8 +1906,10 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, * S2FWB and CACHE DIC are mandatory to avoid the need for * cache maintenance. */ - if (!kvm_supports_cacheable_pfnmap()) - ret = -EFAULT; + if (!kvm_supports_cacheable_pfnmap()) { + kvm_release_faultin_page(s2fd->vcpu->kvm, s2vi->page, true, false); + return -EFAULT; + } } else { /* * If the page was identified as device early by looking at @@ -1825,21 +1921,23 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, * In both cases, we don't let transparent_hugepage_adjust() * change things at the last minute. */ - s2_force_noncacheable = true; + s2vi->map_non_cacheable = true; } - } else if (logging_active && !write_fault) { - /* - * Only actually map the page as writable if this was a write - * fault. - */ - writable = false; + + s2vi->device = true; } - if (exec_fault && s2_force_noncacheable) - ret = -ENOEXEC; + return 1; +} - if (ret) - goto out_put_page; +static int kvm_s2_fault_compute_prot(const struct kvm_s2_fault_desc *s2fd, + const struct kvm_s2_fault_vma_info *s2vi, + enum kvm_pgtable_prot *prot) +{ + struct kvm *kvm = s2fd->vcpu->kvm; + + if (kvm_vcpu_trap_is_exec_fault(s2fd->vcpu) && s2vi->map_non_cacheable) + return -ENOEXEC; /* * Guest performs atomic/exclusive operations on memory with unsupported @@ -1847,99 +1945,167 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, * and trigger the exception here. Since the memslot is valid, inject * the fault back to the guest. */ - if (esr_fsc_is_excl_atomic_fault(kvm_vcpu_get_esr(vcpu))) { - kvm_inject_dabt_excl_atomic(vcpu, kvm_vcpu_get_hfar(vcpu)); - ret = 1; - goto out_put_page; + if (esr_fsc_is_excl_atomic_fault(kvm_vcpu_get_esr(s2fd->vcpu))) { + kvm_inject_dabt_excl_atomic(s2fd->vcpu, kvm_vcpu_get_hfar(s2fd->vcpu)); + return 1; } - if (nested) - adjust_nested_fault_perms(nested, &prot, &writable); + *prot = KVM_PGTABLE_PROT_R; + + if (s2vi->map_writable && (s2vi->device || + !memslot_is_logging(s2fd->memslot) || + kvm_is_write_fault(s2fd->vcpu))) + *prot |= KVM_PGTABLE_PROT_W; + + if (s2fd->nested) + *prot = adjust_nested_fault_perms(s2fd->nested, *prot); + + if (kvm_vcpu_trap_is_exec_fault(s2fd->vcpu)) + *prot |= KVM_PGTABLE_PROT_X; + + if (s2vi->map_non_cacheable) + *prot |= (s2vi->vm_flags & VM_ALLOW_ANY_UNCACHED) ? + KVM_PGTABLE_PROT_NORMAL_NC : KVM_PGTABLE_PROT_DEVICE; + else if (cpus_have_final_cap(ARM64_HAS_CACHE_DIC)) + *prot |= KVM_PGTABLE_PROT_X; + + if (s2fd->nested) + *prot = adjust_nested_exec_perms(kvm, s2fd->nested, *prot); + + if (!kvm_s2_fault_is_perm(s2fd) && !s2vi->map_non_cacheable && kvm_has_mte(kvm)) { + /* Check the VMM hasn't introduced a new disallowed VMA */ + if (!s2vi->mte_allowed) + return -EFAULT; + } + + return 0; +} + +static int kvm_s2_fault_map(const struct kvm_s2_fault_desc *s2fd, + const struct kvm_s2_fault_vma_info *s2vi, + enum kvm_pgtable_prot prot, + void *memcache) +{ + enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED; + bool writable = prot & KVM_PGTABLE_PROT_W; + struct kvm *kvm = s2fd->vcpu->kvm; + struct kvm_pgtable *pgt; + long perm_fault_granule; + long mapping_size; + kvm_pfn_t pfn; + gfn_t gfn; + int ret; kvm_fault_lock(kvm); - pgt = vcpu->arch.hw_mmu->pgt; - if (mmu_invalidate_retry(kvm, mmu_seq)) { - ret = -EAGAIN; + pgt = s2fd->vcpu->arch.hw_mmu->pgt; + ret = -EAGAIN; + if (mmu_invalidate_retry(kvm, s2vi->mmu_seq)) goto out_unlock; - } + + perm_fault_granule = (kvm_s2_fault_is_perm(s2fd) ? + kvm_vcpu_trap_get_perm_fault_granule(s2fd->vcpu) : 0); + mapping_size = s2vi->vma_pagesize; + pfn = s2vi->pfn; + gfn = s2vi->gfn; /* * If we are not forced to use page mapping, check if we are * backed by a THP and thus use block mapping if possible. */ - if (vma_pagesize == PAGE_SIZE && !(force_pte || s2_force_noncacheable)) { - if (fault_is_perm && fault_granule > PAGE_SIZE) - vma_pagesize = fault_granule; - else - vma_pagesize = transparent_hugepage_adjust(kvm, memslot, - hva, &pfn, - &fault_ipa); - - if (vma_pagesize < 0) { - ret = vma_pagesize; - goto out_unlock; - } - } - - if (!fault_is_perm && !s2_force_noncacheable && kvm_has_mte(kvm)) { - /* Check the VMM hasn't introduced a new disallowed VMA */ - if (mte_allowed) { - sanitise_mte_tags(kvm, pfn, vma_pagesize); + if (mapping_size == PAGE_SIZE && + !(s2vi->max_map_size == PAGE_SIZE || s2vi->map_non_cacheable)) { + if (perm_fault_granule > PAGE_SIZE) { + mapping_size = perm_fault_granule; } else { - ret = -EFAULT; - goto out_unlock; + mapping_size = transparent_hugepage_adjust(kvm, s2fd->memslot, + s2fd->hva, &pfn, + &gfn); + if (mapping_size < 0) { + ret = mapping_size; + goto out_unlock; + } } } - if (writable) - prot |= KVM_PGTABLE_PROT_W; - - if (exec_fault) - prot |= KVM_PGTABLE_PROT_X; - - if (s2_force_noncacheable) { - if (vfio_allow_any_uc) - prot |= KVM_PGTABLE_PROT_NORMAL_NC; - else - prot |= KVM_PGTABLE_PROT_DEVICE; - } else if (cpus_have_final_cap(ARM64_HAS_CACHE_DIC)) { - prot |= KVM_PGTABLE_PROT_X; - } - - if (nested) - adjust_nested_exec_perms(kvm, nested, &prot); + if (!perm_fault_granule && !s2vi->map_non_cacheable && kvm_has_mte(kvm)) + sanitise_mte_tags(kvm, pfn, mapping_size); /* * Under the premise of getting a FSC_PERM fault, we just need to relax - * permissions only if vma_pagesize equals fault_granule. Otherwise, + * permissions only if mapping_size equals perm_fault_granule. Otherwise, * kvm_pgtable_stage2_map() should be called to change block size. */ - if (fault_is_perm && vma_pagesize == fault_granule) { + if (mapping_size == perm_fault_granule) { /* * Drop the SW bits in favour of those stored in the * PTE, which will be preserved. */ prot &= ~KVM_NV_GUEST_MAP_SZ; - ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, fault_ipa, prot, flags); + ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, gfn_to_gpa(gfn), + prot, flags); } else { - ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, fault_ipa, vma_pagesize, - __pfn_to_phys(pfn), prot, - memcache, flags); + ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, gfn_to_gpa(gfn), mapping_size, + __pfn_to_phys(pfn), prot, + memcache, flags); } out_unlock: - kvm_release_faultin_page(kvm, page, !!ret, writable); + kvm_release_faultin_page(kvm, s2vi->page, !!ret, writable); kvm_fault_unlock(kvm); - /* Mark the page dirty only if the fault is handled successfully */ - if (writable && !ret) - mark_page_dirty_in_slot(kvm, memslot, gfn); + /* + * Mark the page dirty only if the fault is handled successfully, + * making sure we adjust the canonical IPA if the mapping size has + * been updated (via a THP upgrade, for example). + */ + if (writable && !ret) { + phys_addr_t ipa = gfn_to_gpa(get_canonical_gfn(s2fd, s2vi)); + ipa &= ~(mapping_size - 1); + mark_page_dirty_in_slot(kvm, s2fd->memslot, gpa_to_gfn(ipa)); + } - return ret != -EAGAIN ? ret : 0; + if (ret != -EAGAIN) + return ret; + return 0; +} -out_put_page: - kvm_release_page_unused(page); - return ret; +static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd) +{ + bool perm_fault = kvm_vcpu_trap_is_permission_fault(s2fd->vcpu); + struct kvm_s2_fault_vma_info s2vi = {}; + enum kvm_pgtable_prot prot; + void *memcache; + int ret; + + /* + * Permission faults just need to update the existing leaf entry, + * and so normally don't require allocations from the memcache. The + * only exception to this is when dirty logging is enabled at runtime + * and a write fault needs to collapse a block entry into a table. + */ + memcache = get_mmu_memcache(s2fd->vcpu); + if (!perm_fault || (memslot_is_logging(s2fd->memslot) && + kvm_is_write_fault(s2fd->vcpu))) { + ret = topup_mmu_memcache(s2fd->vcpu, memcache); + if (ret) + return ret; + } + + /* + * Let's check if we will get back a huge page backed by hugetlbfs, or + * get block mapping for device MMIO region. + */ + ret = kvm_s2_fault_pin_pfn(s2fd, &s2vi); + if (ret != 1) + return ret; + + ret = kvm_s2_fault_compute_prot(s2fd, &s2vi, &prot); + if (ret) { + kvm_release_page_unused(s2vi.page); + return ret; + } + + return kvm_s2_fault_map(s2fd, &s2vi, prot, memcache); } /* Resolve the access fault by making the page young again. */ @@ -2202,15 +2368,27 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu) goto out_unlock; } - VM_WARN_ON_ONCE(kvm_vcpu_trap_is_permission_fault(vcpu) && - !write_fault && !kvm_vcpu_trap_is_exec_fault(vcpu)); + const struct kvm_s2_fault_desc s2fd = { + .vcpu = vcpu, + .fault_ipa = fault_ipa, + .nested = nested, + .memslot = memslot, + .hva = hva, + }; + + if (kvm_vm_is_protected(vcpu->kvm)) { + ret = pkvm_mem_abort(&s2fd); + } else { + VM_WARN_ON_ONCE(kvm_vcpu_trap_is_permission_fault(vcpu) && + !write_fault && + !kvm_vcpu_trap_is_exec_fault(vcpu)); + + if (kvm_slot_has_gmem(memslot)) + ret = gmem_abort(&s2fd); + else + ret = user_mem_abort(&s2fd); + } - if (kvm_slot_has_gmem(memslot)) - ret = gmem_abort(vcpu, fault_ipa, nested, memslot, - esr_fsc_is_permission_fault(esr)); - else - ret = user_mem_abort(vcpu, fault_ipa, nested, memslot, hva, - esr_fsc_is_permission_fault(esr)); if (ret == 0) ret = 1; out: @@ -2223,7 +2401,7 @@ out_unlock: bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) { - if (!kvm->arch.mmu.pgt) + if (!kvm->arch.mmu.pgt || kvm_vm_is_protected(kvm)) return false; __unmap_stage2_range(&kvm->arch.mmu, range->start << PAGE_SHIFT, @@ -2238,7 +2416,7 @@ bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) { u64 size = (range->end - range->start) << PAGE_SHIFT; - if (!kvm->arch.mmu.pgt) + if (!kvm->arch.mmu.pgt || kvm_vm_is_protected(kvm)) return false; return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt, @@ -2254,7 +2432,7 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) { u64 size = (range->end - range->start) << PAGE_SHIFT; - if (!kvm->arch.mmu.pgt) + if (!kvm->arch.mmu.pgt || kvm_vm_is_protected(kvm)) return false; return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt, @@ -2411,6 +2589,19 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, hva_t hva, reg_end; int ret = 0; + if (kvm_vm_is_protected(kvm)) { + /* Cannot modify memslots once a pVM has run. */ + if (pkvm_hyp_vm_is_created(kvm) && + (change == KVM_MR_DELETE || change == KVM_MR_MOVE)) { + return -EPERM; + } + + if (new && + new->flags & (KVM_MEM_LOG_DIRTY_PAGES | KVM_MEM_READONLY)) { + return -EPERM; + } + } + if (change != KVM_MR_CREATE && change != KVM_MR_MOVE && change != KVM_MR_FLAGS_ONLY) return 0; diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 2c43097248b2..883b6c1008fb 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -735,8 +735,10 @@ static struct kvm_s2_mmu *get_s2_mmu_nested(struct kvm_vcpu *vcpu) kvm->arch.nested_mmus_next = (i + 1) % kvm->arch.nested_mmus_size; /* Make sure we don't forget to do the laundry */ - if (kvm_s2_mmu_valid(s2_mmu)) + if (kvm_s2_mmu_valid(s2_mmu)) { + kvm_nested_s2_ptdump_remove_debugfs(s2_mmu); s2_mmu->pending_unmap = true; + } /* * The virtual VMID (modulo CnP) will be used as a key when matching @@ -750,6 +752,8 @@ static struct kvm_s2_mmu *get_s2_mmu_nested(struct kvm_vcpu *vcpu) s2_mmu->tlb_vtcr = vcpu_read_sys_reg(vcpu, VTCR_EL2); s2_mmu->nested_stage2_enabled = vcpu_read_sys_reg(vcpu, HCR_EL2) & HCR_VM; + kvm_nested_s2_ptdump_create_debugfs(s2_mmu); + out: atomic_inc(&s2_mmu->refcnt); @@ -1558,6 +1562,11 @@ u64 limit_nv_id_reg(struct kvm *kvm, u32 reg, u64 val) ID_AA64PFR1_EL1_MTE); break; + case SYS_ID_AA64PFR2_EL1: + /* GICv5 is not yet supported for NV */ + val &= ~ID_AA64PFR2_EL1_GCIE; + break; + case SYS_ID_AA64MMFR0_EL1: /* Hide ExS, Secure Memory */ val &= ~(ID_AA64MMFR0_EL1_EXS | diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c index d7a0f69a9982..053e4f733e4b 100644 --- a/arch/arm64/kvm/pkvm.c +++ b/arch/arm64/kvm/pkvm.c @@ -88,7 +88,7 @@ void __init kvm_hyp_reserve(void) static void __pkvm_destroy_hyp_vm(struct kvm *kvm) { if (pkvm_hyp_vm_is_created(kvm)) { - WARN_ON(kvm_call_hyp_nvhe(__pkvm_teardown_vm, + WARN_ON(kvm_call_hyp_nvhe(__pkvm_finalize_teardown_vm, kvm->arch.pkvm.handle)); } else if (kvm->arch.pkvm.handle) { /* @@ -192,10 +192,16 @@ int pkvm_create_hyp_vm(struct kvm *kvm) { int ret = 0; + /* + * Synchronise with kvm_arch_prepare_memory_region(), as we + * prevent memslot modifications on a pVM that has been run. + */ + mutex_lock(&kvm->slots_lock); mutex_lock(&kvm->arch.config_lock); if (!pkvm_hyp_vm_is_created(kvm)) ret = __pkvm_create_hyp_vm(kvm); mutex_unlock(&kvm->arch.config_lock); + mutex_unlock(&kvm->slots_lock); return ret; } @@ -219,9 +225,10 @@ void pkvm_destroy_hyp_vm(struct kvm *kvm) mutex_unlock(&kvm->arch.config_lock); } -int pkvm_init_host_vm(struct kvm *kvm) +int pkvm_init_host_vm(struct kvm *kvm, unsigned long type) { int ret; + bool protected = type & KVM_VM_TYPE_ARM_PROTECTED; if (pkvm_hyp_vm_is_created(kvm)) return -EINVAL; @@ -236,6 +243,11 @@ int pkvm_init_host_vm(struct kvm *kvm) return ret; kvm->arch.pkvm.handle = ret; + kvm->arch.pkvm.is_protected = protected; + if (protected) { + pr_warn_once("kvm: protected VMs are experimental and for development only, tainting kernel\n"); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + } return 0; } @@ -322,15 +334,38 @@ int pkvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu, return 0; } -static int __pkvm_pgtable_stage2_unmap(struct kvm_pgtable *pgt, u64 start, u64 end) +static int __pkvm_pgtable_stage2_reclaim(struct kvm_pgtable *pgt, u64 start, u64 end) { struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu); pkvm_handle_t handle = kvm->arch.pkvm.handle; struct pkvm_mapping *mapping; int ret; - if (!handle) - return 0; + for_each_mapping_in_range_safe(pgt, start, end, mapping) { + struct page *page; + + ret = kvm_call_hyp_nvhe(__pkvm_reclaim_dying_guest_page, + handle, mapping->gfn); + if (WARN_ON(ret)) + continue; + + page = pfn_to_page(mapping->pfn); + WARN_ON_ONCE(mapping->nr_pages != 1); + unpin_user_pages_dirty_lock(&page, 1, true); + account_locked_vm(current->mm, 1, false); + pkvm_mapping_remove(mapping, &pgt->pkvm_mappings); + kfree(mapping); + } + + return 0; +} + +static int __pkvm_pgtable_stage2_unshare(struct kvm_pgtable *pgt, u64 start, u64 end) +{ + struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu); + pkvm_handle_t handle = kvm->arch.pkvm.handle; + struct pkvm_mapping *mapping; + int ret; for_each_mapping_in_range_safe(pgt, start, end, mapping) { ret = kvm_call_hyp_nvhe(__pkvm_host_unshare_guest, handle, mapping->gfn, @@ -347,7 +382,21 @@ static int __pkvm_pgtable_stage2_unmap(struct kvm_pgtable *pgt, u64 start, u64 e void pkvm_pgtable_stage2_destroy_range(struct kvm_pgtable *pgt, u64 addr, u64 size) { - __pkvm_pgtable_stage2_unmap(pgt, addr, addr + size); + struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu); + pkvm_handle_t handle = kvm->arch.pkvm.handle; + + if (!handle) + return; + + if (pkvm_hyp_vm_is_created(kvm) && !kvm->arch.pkvm.is_dying) { + WARN_ON(kvm_call_hyp_nvhe(__pkvm_start_teardown_vm, handle)); + kvm->arch.pkvm.is_dying = true; + } + + if (kvm_vm_is_protected(kvm)) + __pkvm_pgtable_stage2_reclaim(pgt, addr, addr + size); + else + __pkvm_pgtable_stage2_unshare(pgt, addr, addr + size); } void pkvm_pgtable_stage2_destroy_pgd(struct kvm_pgtable *pgt) @@ -365,31 +414,58 @@ int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size, struct kvm_hyp_memcache *cache = mc; u64 gfn = addr >> PAGE_SHIFT; u64 pfn = phys >> PAGE_SHIFT; + u64 end = addr + size; int ret; - if (size != PAGE_SIZE && size != PMD_SIZE) - return -EINVAL; - lockdep_assert_held_write(&kvm->mmu_lock); + mapping = pkvm_mapping_iter_first(&pgt->pkvm_mappings, addr, end - 1); - /* - * Calling stage2_map() on top of existing mappings is either happening because of a race - * with another vCPU, or because we're changing between page and block mappings. As per - * user_mem_abort(), same-size permission faults are handled in the relax_perms() path. - */ - mapping = pkvm_mapping_iter_first(&pgt->pkvm_mappings, addr, addr + size - 1); - if (mapping) { - if (size == (mapping->nr_pages * PAGE_SIZE)) - return -EAGAIN; + if (kvm_vm_is_protected(kvm)) { + /* Protected VMs are mapped using RWX page-granular mappings */ + if (WARN_ON_ONCE(size != PAGE_SIZE)) + return -EINVAL; - /* Remove _any_ pkvm_mapping overlapping with the range, bigger or smaller. */ - ret = __pkvm_pgtable_stage2_unmap(pgt, addr, addr + size); - if (ret) - return ret; - mapping = NULL; + if (WARN_ON_ONCE(prot != KVM_PGTABLE_PROT_RWX)) + return -EINVAL; + + /* + * We either raced with another vCPU or the guest PTE + * has been poisoned by an erroneous host access. + */ + if (mapping) { + ret = kvm_call_hyp_nvhe(__pkvm_vcpu_in_poison_fault); + return ret ? -EFAULT : -EAGAIN; + } + + ret = kvm_call_hyp_nvhe(__pkvm_host_donate_guest, pfn, gfn); + } else { + if (WARN_ON_ONCE(size != PAGE_SIZE && size != PMD_SIZE)) + return -EINVAL; + + /* + * We either raced with another vCPU or we're changing between + * page and block mappings. As per user_mem_abort(), same-size + * permission faults are handled in the relax_perms() path. + */ + if (mapping) { + if (size == (mapping->nr_pages * PAGE_SIZE)) + return -EAGAIN; + + /* + * Remove _any_ pkvm_mapping overlapping with the range, + * bigger or smaller. + */ + ret = __pkvm_pgtable_stage2_unshare(pgt, addr, end); + if (ret) + return ret; + + mapping = NULL; + } + + ret = kvm_call_hyp_nvhe(__pkvm_host_share_guest, pfn, gfn, + size / PAGE_SIZE, prot); } - ret = kvm_call_hyp_nvhe(__pkvm_host_share_guest, pfn, gfn, size / PAGE_SIZE, prot); if (WARN_ON(ret)) return ret; @@ -404,9 +480,14 @@ int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size, int pkvm_pgtable_stage2_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size) { - lockdep_assert_held_write(&kvm_s2_mmu_to_kvm(pgt->mmu)->mmu_lock); + struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu); - return __pkvm_pgtable_stage2_unmap(pgt, addr, addr + size); + if (WARN_ON(kvm_vm_is_protected(kvm))) + return -EPERM; + + lockdep_assert_held_write(&kvm->mmu_lock); + + return __pkvm_pgtable_stage2_unshare(pgt, addr, addr + size); } int pkvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size) @@ -416,6 +497,9 @@ int pkvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size) struct pkvm_mapping *mapping; int ret = 0; + if (WARN_ON(kvm_vm_is_protected(kvm))) + return -EPERM; + lockdep_assert_held(&kvm->mmu_lock); for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) { ret = kvm_call_hyp_nvhe(__pkvm_host_wrprotect_guest, handle, mapping->gfn, @@ -447,6 +531,9 @@ bool pkvm_pgtable_stage2_test_clear_young(struct kvm_pgtable *pgt, u64 addr, u64 struct pkvm_mapping *mapping; bool young = false; + if (WARN_ON(kvm_vm_is_protected(kvm))) + return false; + lockdep_assert_held(&kvm->mmu_lock); for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) young |= kvm_call_hyp_nvhe(__pkvm_host_test_clear_young_guest, handle, mapping->gfn, @@ -458,12 +545,18 @@ bool pkvm_pgtable_stage2_test_clear_young(struct kvm_pgtable *pgt, u64 addr, u64 int pkvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, enum kvm_pgtable_prot prot, enum kvm_pgtable_walk_flags flags) { + if (WARN_ON(kvm_vm_is_protected(kvm_s2_mmu_to_kvm(pgt->mmu)))) + return -EPERM; + return kvm_call_hyp_nvhe(__pkvm_host_relax_perms_guest, addr >> PAGE_SHIFT, prot); } void pkvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr, enum kvm_pgtable_walk_flags flags) { + if (WARN_ON(kvm_vm_is_protected(kvm_s2_mmu_to_kvm(pgt->mmu)))) + return; + WARN_ON(kvm_call_hyp_nvhe(__pkvm_host_mkyoung_guest, addr >> PAGE_SHIFT)); } @@ -485,3 +578,15 @@ int pkvm_pgtable_stage2_split(struct kvm_pgtable *pgt, u64 addr, u64 size, WARN_ON_ONCE(1); return -EINVAL; } + +/* + * Forcefully reclaim a page from the guest, zeroing its contents and + * poisoning the stage-2 pte so that pages can no longer be mapped at + * the same IPA. The page remains pinned until the guest is destroyed. + */ +bool pkvm_force_reclaim_guest_page(phys_addr_t phys) +{ + int ret = kvm_call_hyp_nvhe(__pkvm_force_reclaim_guest_page, phys); + + return !ret || ret == -EAGAIN; +} diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index 93cc9bbb5cec..e1860acae641 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -939,7 +939,8 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu) * number against the dimensions of the vgic and make sure * it's valid. */ - if (!irq_is_ppi(irq) && !vgic_valid_spi(vcpu->kvm, irq)) + if (!irq_is_ppi(vcpu->kvm, irq) && + !vgic_valid_spi(vcpu->kvm, irq)) return -EINVAL; } else if (kvm_arm_pmu_irq_initialized(vcpu)) { return -EINVAL; @@ -961,8 +962,13 @@ static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu) if (!vgic_initialized(vcpu->kvm)) return -ENODEV; - if (!kvm_arm_pmu_irq_initialized(vcpu)) - return -ENXIO; + if (!kvm_arm_pmu_irq_initialized(vcpu)) { + if (!vgic_is_v5(vcpu->kvm)) + return -ENXIO; + + /* Use the architected irq number for GICv5. */ + vcpu->arch.pmu.irq_num = KVM_ARMV8_PMU_GICV5_IRQ; + } ret = kvm_vgic_set_owner(vcpu, vcpu->arch.pmu.irq_num, &vcpu->arch.pmu); @@ -987,11 +993,15 @@ static bool pmu_irq_is_valid(struct kvm *kvm, int irq) unsigned long i; struct kvm_vcpu *vcpu; + /* On GICv5, the PMUIRQ is architecturally mandated to be PPI 23 */ + if (vgic_is_v5(kvm) && irq != KVM_ARMV8_PMU_GICV5_IRQ) + return false; + kvm_for_each_vcpu(i, vcpu, kvm) { if (!kvm_arm_pmu_irq_initialized(vcpu)) continue; - if (irq_is_ppi(irq)) { + if (irq_is_ppi(vcpu->kvm, irq)) { if (vcpu->arch.pmu.irq_num != irq) return false; } else { @@ -1142,7 +1152,7 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) return -EFAULT; /* The PMU overflow interrupt can be a PPI or a valid SPI. */ - if (!(irq_is_ppi(irq) || irq_is_spi(irq))) + if (!(irq_is_ppi(vcpu->kvm, irq) || irq_is_spi(vcpu->kvm, irq))) return -EINVAL; if (!pmu_irq_is_valid(kvm, irq)) diff --git a/arch/arm64/kvm/ptdump.c b/arch/arm64/kvm/ptdump.c index 6a8836207a79..c9140e22abcf 100644 --- a/arch/arm64/kvm/ptdump.c +++ b/arch/arm64/kvm/ptdump.c @@ -10,19 +10,20 @@ #include #include +#include #include #include #include #define MARKERS_LEN 2 #define KVM_PGTABLE_MAX_LEVELS (KVM_PGTABLE_LAST_LEVEL + 1) +#define S2FNAMESZ sizeof("0x0123456789abcdef-0x0123456789abcdef-s2-disabled") struct kvm_ptdump_guest_state { - struct kvm *kvm; + struct kvm_s2_mmu *mmu; struct ptdump_pg_state parser_state; struct addr_marker ipa_marker[MARKERS_LEN]; struct ptdump_pg_level level[KVM_PGTABLE_MAX_LEVELS]; - struct ptdump_range range[MARKERS_LEN]; }; static const struct ptdump_prot_bits stage2_pte_bits[] = { @@ -112,10 +113,9 @@ static int kvm_ptdump_build_levels(struct ptdump_pg_level *level, u32 start_lvl) return 0; } -static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm *kvm) +static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm_s2_mmu *mmu) { struct kvm_ptdump_guest_state *st; - struct kvm_s2_mmu *mmu = &kvm->arch.mmu; struct kvm_pgtable *pgtable = mmu->pgt; int ret; @@ -131,17 +131,8 @@ static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm *kvm) st->ipa_marker[0].name = "Guest IPA"; st->ipa_marker[1].start_address = BIT(pgtable->ia_bits); - st->range[0].end = BIT(pgtable->ia_bits); - - st->kvm = kvm; - st->parser_state = (struct ptdump_pg_state) { - .marker = &st->ipa_marker[0], - .level = -1, - .pg_level = &st->level[0], - .ptdump.range = &st->range[0], - .start_address = 0, - }; + st->mmu = mmu; return st; } @@ -149,16 +140,20 @@ static int kvm_ptdump_guest_show(struct seq_file *m, void *unused) { int ret; struct kvm_ptdump_guest_state *st = m->private; - struct kvm *kvm = st->kvm; - struct kvm_s2_mmu *mmu = &kvm->arch.mmu; - struct ptdump_pg_state *parser_state = &st->parser_state; + struct kvm_s2_mmu *mmu = st->mmu; + struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu); struct kvm_pgtable_walker walker = (struct kvm_pgtable_walker) { .cb = kvm_ptdump_visitor, - .arg = parser_state, + .arg = &st->parser_state, .flags = KVM_PGTABLE_WALK_LEAF, }; - parser_state->seq = m; + st->parser_state = (struct ptdump_pg_state) { + .marker = &st->ipa_marker[0], + .level = -1, + .pg_level = &st->level[0], + .seq = m, + }; write_lock(&kvm->mmu_lock); ret = kvm_pgtable_walk(mmu->pgt, 0, BIT(mmu->pgt->ia_bits), &walker); @@ -169,14 +164,15 @@ static int kvm_ptdump_guest_show(struct seq_file *m, void *unused) static int kvm_ptdump_guest_open(struct inode *m, struct file *file) { - struct kvm *kvm = m->i_private; + struct kvm_s2_mmu *mmu = m->i_private; + struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu); struct kvm_ptdump_guest_state *st; int ret; if (!kvm_get_kvm_safe(kvm)) return -ENOENT; - st = kvm_ptdump_parser_create(kvm); + st = kvm_ptdump_parser_create(mmu); if (IS_ERR(st)) { ret = PTR_ERR(st); goto err_with_kvm_ref; @@ -194,7 +190,7 @@ err_with_kvm_ref: static int kvm_ptdump_guest_close(struct inode *m, struct file *file) { - struct kvm *kvm = m->i_private; + struct kvm *kvm = kvm_s2_mmu_to_kvm(m->i_private); void *st = ((struct seq_file *)file->private_data)->private; kfree(st); @@ -229,14 +225,15 @@ static int kvm_pgtable_levels_show(struct seq_file *m, void *unused) static int kvm_pgtable_debugfs_open(struct inode *m, struct file *file, int (*show)(struct seq_file *, void *)) { - struct kvm *kvm = m->i_private; + struct kvm_s2_mmu *mmu = m->i_private; + struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu); struct kvm_pgtable *pgtable; int ret; if (!kvm_get_kvm_safe(kvm)) return -ENOENT; - pgtable = kvm->arch.mmu.pgt; + pgtable = mmu->pgt; ret = single_open(file, show, pgtable); if (ret < 0) @@ -256,7 +253,7 @@ static int kvm_pgtable_levels_open(struct inode *m, struct file *file) static int kvm_pgtable_debugfs_close(struct inode *m, struct file *file) { - struct kvm *kvm = m->i_private; + struct kvm *kvm = kvm_s2_mmu_to_kvm(m->i_private); kvm_put_kvm(kvm); return single_release(m, file); @@ -276,12 +273,36 @@ static const struct file_operations kvm_pgtable_levels_fops = { .release = kvm_pgtable_debugfs_close, }; +void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu) +{ + struct dentry *dent; + char file_name[S2FNAMESZ]; + + snprintf(file_name, sizeof(file_name), "0x%016llx-0x%016llx-s2-%sabled", + mmu->tlb_vttbr, + mmu->tlb_vtcr, + mmu->nested_stage2_enabled ? "en" : "dis"); + + dent = debugfs_create_file(file_name, 0400, + mmu->arch->debugfs_nv_dentry, mmu, + &kvm_ptdump_guest_fops); + + mmu->shadow_pt_debugfs_dentry = dent; +} + +void kvm_nested_s2_ptdump_remove_debugfs(struct kvm_s2_mmu *mmu) +{ + debugfs_remove(mmu->shadow_pt_debugfs_dentry); +} + void kvm_s2_ptdump_create_debugfs(struct kvm *kvm) { debugfs_create_file("stage2_page_tables", 0400, kvm->debugfs_dentry, - kvm, &kvm_ptdump_guest_fops); - debugfs_create_file("ipa_range", 0400, kvm->debugfs_dentry, kvm, - &kvm_pgtable_range_fops); + &kvm->arch.mmu, &kvm_ptdump_guest_fops); + debugfs_create_file("ipa_range", 0400, kvm->debugfs_dentry, + &kvm->arch.mmu, &kvm_pgtable_range_fops); debugfs_create_file("stage2_levels", 0400, kvm->debugfs_dentry, - kvm, &kvm_pgtable_levels_fops); + &kvm->arch.mmu, &kvm_pgtable_levels_fops); + if (cpus_have_final_cap(ARM64_HAS_NESTED_VIRT)) + kvm->arch.debugfs_nv_dentry = debugfs_create_dir("nested", kvm->debugfs_dentry); } diff --git a/arch/arm64/kvm/stacktrace.c b/arch/arm64/kvm/stacktrace.c index af5eec681127..9724c320126b 100644 --- a/arch/arm64/kvm/stacktrace.c +++ b/arch/arm64/kvm/stacktrace.c @@ -197,7 +197,7 @@ static void hyp_dump_backtrace(unsigned long hyp_offset) kvm_nvhe_dump_backtrace_end(); } -#ifdef CONFIG_PROTECTED_NVHE_STACKTRACE +#ifdef CONFIG_PKVM_STACKTRACE DECLARE_KVM_NVHE_PER_CPU(unsigned long [NVHE_STACKTRACE_SIZE/sizeof(long)], pkvm_stacktrace); @@ -225,12 +225,12 @@ static void pkvm_dump_backtrace(unsigned long hyp_offset) kvm_nvhe_dump_backtrace_entry((void *)hyp_offset, stacktrace[i]); kvm_nvhe_dump_backtrace_end(); } -#else /* !CONFIG_PROTECTED_NVHE_STACKTRACE */ +#else /* !CONFIG_PKVM_STACKTRACE */ static void pkvm_dump_backtrace(unsigned long hyp_offset) { - kvm_err("Cannot dump pKVM nVHE stacktrace: !CONFIG_PROTECTED_NVHE_STACKTRACE\n"); + kvm_err("Cannot dump pKVM nVHE stacktrace: !CONFIG_PKVM_STACKTRACE\n"); } -#endif /* CONFIG_PROTECTED_NVHE_STACKTRACE */ +#endif /* CONFIG_PKVM_STACKTRACE */ /* * kvm_nvhe_dump_backtrace - Dump KVM nVHE hypervisor backtrace. diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index c1e0dea903a1..148fc3400ea8 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -681,6 +681,91 @@ static bool access_gic_dir(struct kvm_vcpu *vcpu, return true; } +static bool access_gicv5_idr0(struct kvm_vcpu *vcpu, struct sys_reg_params *p, + const struct sys_reg_desc *r) +{ + if (p->is_write) + return undef_access(vcpu, p, r); + + /* + * Expose KVM's priority- and ID-bits to the guest, but not GCIE_LEGACY. + * + * Note: for GICv5 the mimic the way that the num_pri_bits and + * num_id_bits fields are used with GICv3: + * - num_pri_bits stores the actual number of priority bits, whereas the + * register field stores num_pri_bits - 1. + * - num_id_bits stores the raw field value, which is 0b0000 for 16 bits + * and 0b0001 for 24 bits. + */ + p->regval = FIELD_PREP(ICC_IDR0_EL1_PRI_BITS, vcpu->arch.vgic_cpu.num_pri_bits - 1) | + FIELD_PREP(ICC_IDR0_EL1_ID_BITS, vcpu->arch.vgic_cpu.num_id_bits); + + return true; +} + +static bool access_gicv5_iaffid(struct kvm_vcpu *vcpu, struct sys_reg_params *p, + const struct sys_reg_desc *r) +{ + if (p->is_write) + return undef_access(vcpu, p, r); + + /* + * For GICv5 VMs, the IAFFID value is the same as the VPE ID. The VPE ID + * is the same as the VCPU's ID. + */ + p->regval = FIELD_PREP(ICC_IAFFIDR_EL1_IAFFID, vcpu->vcpu_id); + + return true; +} + +static bool access_gicv5_ppi_enabler(struct kvm_vcpu *vcpu, + struct sys_reg_params *p, + const struct sys_reg_desc *r) +{ + unsigned long *mask = vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask; + struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; + int i; + + /* We never expect to get here with a read! */ + if (WARN_ON_ONCE(!p->is_write)) + return undef_access(vcpu, p, r); + + /* + * If we're only handling architected PPIs and the guest writes to the + * enable for the non-architected PPIs, we just return as there's + * nothing to do at all. We don't even allocate the storage for them in + * this case. + */ + if (VGIC_V5_NR_PRIVATE_IRQS == 64 && p->Op2 % 2) + return true; + + /* + * Merge the raw guest write into out bitmap at an offset of either 0 or + * 64, then and it with our PPI mask. + */ + bitmap_write(cpu_if->vgic_ppi_enabler, p->regval, 64 * (p->Op2 % 2), 64); + bitmap_and(cpu_if->vgic_ppi_enabler, cpu_if->vgic_ppi_enabler, mask, + VGIC_V5_NR_PRIVATE_IRQS); + + /* + * Sync the change in enable states to the vgic_irqs. We consider all + * PPIs as we don't expose many to the guest. + */ + for_each_set_bit(i, mask, VGIC_V5_NR_PRIVATE_IRQS) { + u32 intid = vgic_v5_make_ppi(i); + struct vgic_irq *irq; + + irq = vgic_get_vcpu_irq(vcpu, intid); + + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) + irq->enabled = test_bit(i, cpu_if->vgic_ppi_enabler); + + vgic_put_irq(vcpu->kvm, irq); + } + + return true; +} + static bool trap_raz_wi(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) @@ -1758,6 +1843,7 @@ static u8 pmuver_to_perfmon(u8 pmuver) static u64 sanitise_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, u64 val); static u64 sanitise_id_aa64pfr1_el1(const struct kvm_vcpu *vcpu, u64 val); +static u64 sanitise_id_aa64pfr2_el1(const struct kvm_vcpu *vcpu, u64 val); static u64 sanitise_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val); /* Read a sanitised cpufeature ID register by sys_reg_desc */ @@ -1783,10 +1869,7 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu, val = sanitise_id_aa64pfr1_el1(vcpu, val); break; case SYS_ID_AA64PFR2_EL1: - val &= ID_AA64PFR2_EL1_FPMR | - (kvm_has_mte(vcpu->kvm) ? - ID_AA64PFR2_EL1_MTEFAR | ID_AA64PFR2_EL1_MTESTOREONLY : - 0); + val = sanitise_id_aa64pfr2_el1(vcpu, val); break; case SYS_ID_AA64ISAR1_EL1: if (!vcpu_has_ptrauth(vcpu)) @@ -1985,7 +2068,7 @@ static u64 sanitise_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, u64 val) val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, CSV3, IMP); } - if (vgic_is_v3(vcpu->kvm)) { + if (vgic_host_has_gicv3()) { val &= ~ID_AA64PFR0_EL1_GIC_MASK; val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP); } @@ -2027,6 +2110,23 @@ static u64 sanitise_id_aa64pfr1_el1(const struct kvm_vcpu *vcpu, u64 val) return val; } +static u64 sanitise_id_aa64pfr2_el1(const struct kvm_vcpu *vcpu, u64 val) +{ + val &= ID_AA64PFR2_EL1_FPMR | + ID_AA64PFR2_EL1_MTEFAR | + ID_AA64PFR2_EL1_MTESTOREONLY; + + if (!kvm_has_mte(vcpu->kvm)) { + val &= ~ID_AA64PFR2_EL1_MTEFAR; + val &= ~ID_AA64PFR2_EL1_MTESTOREONLY; + } + + if (vgic_host_has_gicv5()) + val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR2_EL1, GCIE, IMP); + + return val; +} + static u64 sanitise_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val) { val = ID_REG_LIMIT_FIELD_ENUM(val, ID_AA64DFR0_EL1, DebugVer, V8P8); @@ -2177,14 +2277,6 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu, (vcpu_has_nv(vcpu) && !FIELD_GET(ID_AA64PFR0_EL1_EL2, user_val))) return -EINVAL; - /* - * If we are running on a GICv5 host and support FEAT_GCIE_LEGACY, then - * we support GICv3. Fail attempts to do anything but set that to IMP. - */ - if (vgic_is_v3_compat(vcpu->kvm) && - FIELD_GET(ID_AA64PFR0_EL1_GIC_MASK, user_val) != ID_AA64PFR0_EL1_GIC_IMP) - return -EINVAL; - return set_id_reg(vcpu, rd, user_val); } @@ -2224,6 +2316,12 @@ static int set_id_aa64pfr1_el1(struct kvm_vcpu *vcpu, return set_id_reg(vcpu, rd, user_val); } +static int set_id_aa64pfr2_el1(struct kvm_vcpu *vcpu, + const struct sys_reg_desc *rd, u64 user_val) +{ + return set_id_reg(vcpu, rd, user_val); +} + /* * Allow userspace to de-feature a stage-2 translation granule but prevent it * from claiming the impossible. @@ -3205,10 +3303,11 @@ static const struct sys_reg_desc sys_reg_descs[] = { ID_AA64PFR1_EL1_RES0 | ID_AA64PFR1_EL1_MPAM_frac | ID_AA64PFR1_EL1_MTE)), - ID_WRITABLE(ID_AA64PFR2_EL1, - ID_AA64PFR2_EL1_FPMR | - ID_AA64PFR2_EL1_MTEFAR | - ID_AA64PFR2_EL1_MTESTOREONLY), + ID_FILTERED(ID_AA64PFR2_EL1, id_aa64pfr2_el1, + (ID_AA64PFR2_EL1_FPMR | + ID_AA64PFR2_EL1_MTEFAR | + ID_AA64PFR2_EL1_MTESTOREONLY | + ID_AA64PFR2_EL1_GCIE)), ID_UNALLOCATED(4,3), ID_WRITABLE(ID_AA64ZFR0_EL1, ~ID_AA64ZFR0_EL1_RES0), ID_HIDDEN(ID_AA64SMFR0_EL1), @@ -3394,6 +3493,10 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_ICC_AP1R1_EL1), undef_access }, { SYS_DESC(SYS_ICC_AP1R2_EL1), undef_access }, { SYS_DESC(SYS_ICC_AP1R3_EL1), undef_access }, + { SYS_DESC(SYS_ICC_IDR0_EL1), access_gicv5_idr0 }, + { SYS_DESC(SYS_ICC_IAFFIDR_EL1), access_gicv5_iaffid }, + { SYS_DESC(SYS_ICC_PPI_ENABLER0_EL1), access_gicv5_ppi_enabler }, + { SYS_DESC(SYS_ICC_PPI_ENABLER1_EL1), access_gicv5_ppi_enabler }, { SYS_DESC(SYS_ICC_DIR_EL1), access_gic_dir }, { SYS_DESC(SYS_ICC_RPR_EL1), undef_access }, { SYS_DESC(SYS_ICC_SGI1R_EL1), access_gic_sgi }, @@ -5650,6 +5753,8 @@ void kvm_calculate_traps(struct kvm_vcpu *vcpu) compute_fgu(kvm, HFGRTR2_GROUP); compute_fgu(kvm, HFGITR2_GROUP); compute_fgu(kvm, HDFGRTR2_GROUP); + compute_fgu(kvm, ICH_HFGRTR_GROUP); + compute_fgu(kvm, ICH_HFGITR_GROUP); set_bit(KVM_ARCH_FLAG_FGU_INITIALIZED, &kvm->arch.flags); out: @@ -5670,25 +5775,60 @@ int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu) guard(mutex)(&kvm->arch.config_lock); - /* - * This hacks into the ID registers, so only perform it when the - * first vcpu runs, or the kvm_set_vm_id_reg() helper will scream. - */ - if (!irqchip_in_kernel(kvm) && !kvm_vm_has_ran_once(kvm)) { - u64 val; - - val = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1) & ~ID_AA64PFR0_EL1_GIC; - kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1, val); - val = kvm_read_vm_id_reg(kvm, SYS_ID_PFR1_EL1) & ~ID_PFR1_EL1_GIC; - kvm_set_vm_id_reg(kvm, SYS_ID_PFR1_EL1, val); - } - if (vcpu_has_nv(vcpu)) { int ret = kvm_init_nv_sysregs(vcpu); if (ret) return ret; } + if (kvm_vm_has_ran_once(kvm)) + return 0; + + /* + * This hacks into the ID registers, so only perform it when the + * first vcpu runs, or the kvm_set_vm_id_reg() helper will scream. + */ + if (!irqchip_in_kernel(kvm)) { + u64 val; + + val = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1) & ~ID_AA64PFR0_EL1_GIC; + kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1, val); + val = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR2_EL1) & ~ID_AA64PFR2_EL1_GCIE; + kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR2_EL1, val); + val = kvm_read_vm_id_reg(kvm, SYS_ID_PFR1_EL1) & ~ID_PFR1_EL1_GIC; + kvm_set_vm_id_reg(kvm, SYS_ID_PFR1_EL1, val); + } else { + /* + * Certain userspace software - QEMU - samples the system + * register state without creating an irqchip, then blindly + * restores the state prior to running the final guest. This + * means that it restores the virtualization & emulation + * capabilities of the host system, rather than something that + * reflects the final guest state. Moreover, it checks that the + * state was "correctly" restored (i.e., verbatim), bailing if + * it isn't, so masking off invalid state isn't an option. + * + * On GICv5 hardware that supports FEAT_GCIE_LEGACY we can run + * both GICv3- and GICv5-based guests. Therefore, we initially + * present both ID_AA64PFR0.GIC and ID_AA64PFR2.GCIE as IMP to + * reflect that userspace can create EITHER a vGICv3 or a + * vGICv5. This is an architecturally invalid combination, of + * course. Once an in-kernel GIC is created, the sysreg state is + * updated to reflect the actual, valid configuration. + * + * Setting both the GIC and GCIE features to IMP unsurprisingly + * results in guests falling over, and hence we need to fix up + * this mess in KVM. Before running for the first time we yet + * again ensure that the GIC and GCIE fields accurately reflect + * the actual hardware the guest should see. + * + * This hack allows legacy QEMU-based GICv3 guests to run + * unmodified on compatible GICv5 hosts, and avoids the inverse + * problem for GICv5-based guests in the future. + */ + kvm_vgic_finalize_idregs(kvm); + } + return 0; } diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index e9b8b5fc480c..933983bb2005 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -66,12 +66,11 @@ static int vgic_allocate_private_irqs_locked(struct kvm_vcpu *vcpu, u32 type); * or through the generic KVM_CREATE_DEVICE API ioctl. * irqchip_in_kernel() tells you if this function succeeded or not. * @kvm: kvm struct pointer - * @type: KVM_DEV_TYPE_ARM_VGIC_V[23] + * @type: KVM_DEV_TYPE_ARM_VGIC_V[235] */ int kvm_vgic_create(struct kvm *kvm, u32 type) { struct kvm_vcpu *vcpu; - u64 aa64pfr0, pfr1; unsigned long i; int ret; @@ -132,8 +131,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) if (type == KVM_DEV_TYPE_ARM_VGIC_V2) kvm->max_vcpus = VGIC_V2_MAX_CPUS; - else + else if (type == KVM_DEV_TYPE_ARM_VGIC_V3) kvm->max_vcpus = VGIC_V3_MAX_CPUS; + else if (type == KVM_DEV_TYPE_ARM_VGIC_V5) + kvm->max_vcpus = min(VGIC_V5_MAX_CPUS, + kvm_vgic_global_state.max_gic_vcpus); if (atomic_read(&kvm->online_vcpus) > kvm->max_vcpus) { ret = -E2BIG; @@ -145,19 +147,20 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) kvm->arch.vgic.implementation_rev = KVM_VGIC_IMP_REV_LATEST; kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF; - aa64pfr0 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1) & ~ID_AA64PFR0_EL1_GIC; - pfr1 = kvm_read_vm_id_reg(kvm, SYS_ID_PFR1_EL1) & ~ID_PFR1_EL1_GIC; - - if (type == KVM_DEV_TYPE_ARM_VGIC_V2) { + switch (type) { + case KVM_DEV_TYPE_ARM_VGIC_V2: kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF; - } else { + break; + case KVM_DEV_TYPE_ARM_VGIC_V3: INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions); - aa64pfr0 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP); - pfr1 |= SYS_FIELD_PREP_ENUM(ID_PFR1_EL1, GIC, GICv3); + break; } - kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1, aa64pfr0); - kvm_set_vm_id_reg(kvm, SYS_ID_PFR1_EL1, pfr1); + /* + * We've now created the GIC. Update the system register state + * to accurately reflect what we've created. + */ + kvm_vgic_finalize_idregs(kvm); kvm_for_each_vcpu(i, vcpu, kvm) { ret = vgic_allocate_private_irqs_locked(vcpu, type); @@ -179,6 +182,15 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) if (type == KVM_DEV_TYPE_ARM_VGIC_V3) kvm->arch.vgic.nassgicap = system_supports_direct_sgis(); + /* + * We now know that we have a GICv5. The Arch Timer PPI interrupts may + * have been initialised at this stage, but will have done so assuming + * that we have an older GIC, meaning that the IntIDs won't be + * correct. We init them again, and this time they will be correct. + */ + if (type == KVM_DEV_TYPE_ARM_VGIC_V5) + kvm_timer_init_vm(kvm); + out_unlock: mutex_unlock(&kvm->arch.config_lock); kvm_unlock_all_vcpus(kvm); @@ -259,9 +271,65 @@ int kvm_vgic_vcpu_nv_init(struct kvm_vcpu *vcpu) return ret; } +static void vgic_allocate_private_irq(struct kvm_vcpu *vcpu, int i, u32 type) +{ + struct vgic_irq *irq = &vcpu->arch.vgic_cpu.private_irqs[i]; + + INIT_LIST_HEAD(&irq->ap_list); + raw_spin_lock_init(&irq->irq_lock); + irq->vcpu = NULL; + irq->target_vcpu = vcpu; + refcount_set(&irq->refcount, 0); + + irq->intid = i; + if (vgic_irq_is_sgi(i)) { + /* SGIs */ + irq->enabled = 1; + irq->config = VGIC_CONFIG_EDGE; + } else { + /* PPIs */ + irq->config = VGIC_CONFIG_LEVEL; + } + + switch (type) { + case KVM_DEV_TYPE_ARM_VGIC_V3: + irq->group = 1; + irq->mpidr = kvm_vcpu_get_mpidr_aff(vcpu); + break; + case KVM_DEV_TYPE_ARM_VGIC_V2: + irq->group = 0; + irq->targets = BIT(vcpu->vcpu_id); + break; + } +} + +static void vgic_v5_allocate_private_irq(struct kvm_vcpu *vcpu, int i, u32 type) +{ + struct vgic_irq *irq = &vcpu->arch.vgic_cpu.private_irqs[i]; + u32 intid = vgic_v5_make_ppi(i); + + INIT_LIST_HEAD(&irq->ap_list); + raw_spin_lock_init(&irq->irq_lock); + irq->vcpu = NULL; + irq->target_vcpu = vcpu; + refcount_set(&irq->refcount, 0); + + irq->intid = intid; + + /* The only Edge architected PPI is the SW_PPI */ + if (i == GICV5_ARCH_PPI_SW_PPI) + irq->config = VGIC_CONFIG_EDGE; + else + irq->config = VGIC_CONFIG_LEVEL; + + /* Register the GICv5-specific PPI ops */ + vgic_v5_set_ppi_ops(vcpu, intid); +} + static int vgic_allocate_private_irqs_locked(struct kvm_vcpu *vcpu, u32 type) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + u32 num_private_irqs; int i; lockdep_assert_held(&vcpu->kvm->arch.config_lock); @@ -269,8 +337,13 @@ static int vgic_allocate_private_irqs_locked(struct kvm_vcpu *vcpu, u32 type) if (vgic_cpu->private_irqs) return 0; + if (vgic_is_v5(vcpu->kvm)) + num_private_irqs = VGIC_V5_NR_PRIVATE_IRQS; + else + num_private_irqs = VGIC_NR_PRIVATE_IRQS; + vgic_cpu->private_irqs = kzalloc_objs(struct vgic_irq, - VGIC_NR_PRIVATE_IRQS, + num_private_irqs, GFP_KERNEL_ACCOUNT); if (!vgic_cpu->private_irqs) @@ -280,34 +353,11 @@ static int vgic_allocate_private_irqs_locked(struct kvm_vcpu *vcpu, u32 type) * Enable and configure all SGIs to be edge-triggered and * configure all PPIs as level-triggered. */ - for (i = 0; i < VGIC_NR_PRIVATE_IRQS; i++) { - struct vgic_irq *irq = &vgic_cpu->private_irqs[i]; - - INIT_LIST_HEAD(&irq->ap_list); - raw_spin_lock_init(&irq->irq_lock); - irq->intid = i; - irq->vcpu = NULL; - irq->target_vcpu = vcpu; - refcount_set(&irq->refcount, 0); - if (vgic_irq_is_sgi(i)) { - /* SGIs */ - irq->enabled = 1; - irq->config = VGIC_CONFIG_EDGE; - } else { - /* PPIs */ - irq->config = VGIC_CONFIG_LEVEL; - } - - switch (type) { - case KVM_DEV_TYPE_ARM_VGIC_V3: - irq->group = 1; - irq->mpidr = kvm_vcpu_get_mpidr_aff(vcpu); - break; - case KVM_DEV_TYPE_ARM_VGIC_V2: - irq->group = 0; - irq->targets = BIT(vcpu->vcpu_id); - break; - } + for (i = 0; i < num_private_irqs; i++) { + if (vgic_is_v5(vcpu->kvm)) + vgic_v5_allocate_private_irq(vcpu, i, type); + else + vgic_allocate_private_irq(vcpu, i, type); } return 0; @@ -366,7 +416,11 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) static void kvm_vgic_vcpu_reset(struct kvm_vcpu *vcpu) { - if (kvm_vgic_global_state.type == VGIC_V2) + const struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V5) + vgic_v5_reset(vcpu); + else if (kvm_vgic_global_state.type == VGIC_V2) vgic_v2_reset(vcpu); else vgic_v3_reset(vcpu); @@ -397,22 +451,28 @@ int vgic_init(struct kvm *kvm) if (kvm->created_vcpus != atomic_read(&kvm->online_vcpus)) return -EBUSY; - /* freeze the number of spis */ - if (!dist->nr_spis) - dist->nr_spis = VGIC_NR_IRQS_LEGACY - VGIC_NR_PRIVATE_IRQS; + if (!vgic_is_v5(kvm)) { + /* freeze the number of spis */ + if (!dist->nr_spis) + dist->nr_spis = VGIC_NR_IRQS_LEGACY - VGIC_NR_PRIVATE_IRQS; - ret = kvm_vgic_dist_init(kvm, dist->nr_spis); - if (ret) - goto out; - - /* - * Ensure vPEs are allocated if direct IRQ injection (e.g. vSGIs, - * vLPIs) is supported. - */ - if (vgic_supports_direct_irqs(kvm)) { - ret = vgic_v4_init(kvm); + ret = kvm_vgic_dist_init(kvm, dist->nr_spis); if (ret) - goto out; + return ret; + + /* + * Ensure vPEs are allocated if direct IRQ injection (e.g. vSGIs, + * vLPIs) is supported. + */ + if (vgic_supports_direct_irqs(kvm)) { + ret = vgic_v4_init(kvm); + if (ret) + return ret; + } + } else { + ret = vgic_v5_init(kvm); + if (ret) + return ret; } kvm_for_each_vcpu(idx, vcpu, kvm) @@ -420,12 +480,12 @@ int vgic_init(struct kvm *kvm) ret = kvm_vgic_setup_default_irq_routing(kvm); if (ret) - goto out; + return ret; vgic_debug_init(kvm); dist->initialized = true; -out: - return ret; + + return 0; } static void kvm_vgic_dist_destroy(struct kvm *kvm) @@ -569,6 +629,7 @@ int vgic_lazy_init(struct kvm *kvm) int kvm_vgic_map_resources(struct kvm *kvm) { struct vgic_dist *dist = &kvm->arch.vgic; + bool needs_dist = true; enum vgic_type type; gpa_t dist_base; int ret = 0; @@ -587,21 +648,29 @@ int kvm_vgic_map_resources(struct kvm *kvm) if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2) { ret = vgic_v2_map_resources(kvm); type = VGIC_V2; - } else { + } else if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) { ret = vgic_v3_map_resources(kvm); type = VGIC_V3; + } else { + ret = vgic_v5_map_resources(kvm); + type = VGIC_V5; + needs_dist = false; } if (ret) goto out; - dist_base = dist->vgic_dist_base; - mutex_unlock(&kvm->arch.config_lock); + if (needs_dist) { + dist_base = dist->vgic_dist_base; + mutex_unlock(&kvm->arch.config_lock); - ret = vgic_register_dist_iodev(kvm, dist_base, type); - if (ret) { - kvm_err("Unable to register VGIC dist MMIO regions\n"); - goto out_slots; + ret = vgic_register_dist_iodev(kvm, dist_base, type); + if (ret) { + kvm_err("Unable to register VGIC dist MMIO regions\n"); + goto out_slots; + } + } else { + mutex_unlock(&kvm->arch.config_lock); } smp_store_release(&dist->ready, true); @@ -617,6 +686,35 @@ out_slots: return ret; } +void kvm_vgic_finalize_idregs(struct kvm *kvm) +{ + u32 type = kvm->arch.vgic.vgic_model; + u64 aa64pfr0, aa64pfr2, pfr1; + + aa64pfr0 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1) & ~ID_AA64PFR0_EL1_GIC; + aa64pfr2 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR2_EL1) & ~ID_AA64PFR2_EL1_GCIE; + pfr1 = kvm_read_vm_id_reg(kvm, SYS_ID_PFR1_EL1) & ~ID_PFR1_EL1_GIC; + + switch (type) { + case KVM_DEV_TYPE_ARM_VGIC_V2: + break; + case KVM_DEV_TYPE_ARM_VGIC_V3: + aa64pfr0 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP); + if (kvm_supports_32bit_el0()) + pfr1 |= SYS_FIELD_PREP_ENUM(ID_PFR1_EL1, GIC, GICv3); + break; + case KVM_DEV_TYPE_ARM_VGIC_V5: + aa64pfr2 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR2_EL1, GCIE, IMP); + break; + default: + WARN_ONCE(1, "Unknown VGIC type!!!\n"); + } + + kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1, aa64pfr0); + kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR2_EL1, aa64pfr2); + kvm_set_vm_id_reg(kvm, SYS_ID_PFR1_EL1, pfr1); +} + /* GENERIC PROBE */ void kvm_vgic_cpu_up(void) diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c index 3d1a776b716d..a96c77dccf35 100644 --- a/arch/arm64/kvm/vgic/vgic-kvm-device.c +++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c @@ -336,6 +336,10 @@ int kvm_register_vgic_device(unsigned long type) break; ret = kvm_vgic_register_its_device(); break; + case KVM_DEV_TYPE_ARM_VGIC_V5: + ret = kvm_register_device_ops(&kvm_arm_vgic_v5_ops, + KVM_DEV_TYPE_ARM_VGIC_V5); + break; } return ret; @@ -639,7 +643,7 @@ static int vgic_v3_set_attr(struct kvm_device *dev, if (vgic_initialized(dev->kvm)) return -EBUSY; - if (!irq_is_ppi(val)) + if (!irq_is_ppi(dev->kvm, val)) return -EINVAL; dev->kvm->arch.vgic.mi_intid = val; @@ -715,3 +719,104 @@ struct kvm_device_ops kvm_arm_vgic_v3_ops = { .get_attr = vgic_v3_get_attr, .has_attr = vgic_v3_has_attr, }; + +static int vgic_v5_get_userspace_ppis(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + struct vgic_v5_vm *gicv5_vm = &dev->kvm->arch.vgic.gicv5_vm; + u64 __user *uaddr = (u64 __user *)(long)attr->addr; + int ret; + + guard(mutex)(&dev->kvm->arch.config_lock); + + /* + * We either support 64 or 128 PPIs. In the former case, we need to + * return 0s for the second 64 bits as we have no storage backing those. + */ + ret = put_user(bitmap_read(gicv5_vm->userspace_ppis, 0, 64), uaddr); + if (ret) + return ret; + uaddr++; + + if (VGIC_V5_NR_PRIVATE_IRQS == 128) + ret = put_user(bitmap_read(gicv5_vm->userspace_ppis, 64, 128), uaddr); + else + ret = put_user(0, uaddr); + + return ret; +} + +static int vgic_v5_set_attr(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + switch (attr->group) { + case KVM_DEV_ARM_VGIC_GRP_ADDR: + case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: + case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: + return -ENXIO; + case KVM_DEV_ARM_VGIC_GRP_CTRL: + switch (attr->attr) { + case KVM_DEV_ARM_VGIC_CTRL_INIT: + return vgic_set_common_attr(dev, attr); + case KVM_DEV_ARM_VGIC_USERSPACE_PPIS: + default: + return -ENXIO; + } + default: + return -ENXIO; + } + +} + +static int vgic_v5_get_attr(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + switch (attr->group) { + case KVM_DEV_ARM_VGIC_GRP_ADDR: + case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: + case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: + return -ENXIO; + case KVM_DEV_ARM_VGIC_GRP_CTRL: + switch (attr->attr) { + case KVM_DEV_ARM_VGIC_CTRL_INIT: + return vgic_get_common_attr(dev, attr); + case KVM_DEV_ARM_VGIC_USERSPACE_PPIS: + return vgic_v5_get_userspace_ppis(dev, attr); + default: + return -ENXIO; + } + default: + return -ENXIO; + } +} + +static int vgic_v5_has_attr(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + switch (attr->group) { + case KVM_DEV_ARM_VGIC_GRP_ADDR: + case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: + case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: + return -ENXIO; + case KVM_DEV_ARM_VGIC_GRP_CTRL: + switch (attr->attr) { + case KVM_DEV_ARM_VGIC_CTRL_INIT: + return 0; + case KVM_DEV_ARM_VGIC_USERSPACE_PPIS: + return 0; + default: + return -ENXIO; + } + default: + return -ENXIO; + } +} + +struct kvm_device_ops kvm_arm_vgic_v5_ops = { + .name = "kvm-arm-vgic-v5", + .create = vgic_create, + .destroy = vgic_destroy, + .set_attr = vgic_v5_set_attr, + .get_attr = vgic_v5_get_attr, + .has_attr = vgic_v5_has_attr, +}; diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v2.c b/arch/arm64/kvm/vgic/vgic-mmio-v2.c index 406845b3117c..0643e333db35 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio-v2.c +++ b/arch/arm64/kvm/vgic/vgic-mmio-v2.c @@ -91,7 +91,7 @@ static int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu, * migration from old kernels to new kernels with legacy * userspace. */ - reg = FIELD_GET(GICD_IIDR_REVISION_MASK, reg); + reg = FIELD_GET(GICD_IIDR_REVISION_MASK, val); switch (reg) { case KVM_VGIC_IMP_REV_2: case KVM_VGIC_IMP_REV_3: diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c index 89edb84d1ac6..5913a20d8301 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c +++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c @@ -194,7 +194,7 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu, if ((reg ^ val) & ~GICD_IIDR_REVISION_MASK) return -EINVAL; - reg = FIELD_GET(GICD_IIDR_REVISION_MASK, reg); + reg = FIELD_GET(GICD_IIDR_REVISION_MASK, val); switch (reg) { case KVM_VGIC_IMP_REV_2: case KVM_VGIC_IMP_REV_3: diff --git a/arch/arm64/kvm/vgic/vgic-mmio.c b/arch/arm64/kvm/vgic/vgic-mmio.c index a573b1f0c6cb..74d76dec9730 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio.c +++ b/arch/arm64/kvm/vgic/vgic-mmio.c @@ -842,18 +842,46 @@ vgic_find_mmio_region(const struct vgic_register_region *regions, void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr) { - if (kvm_vgic_global_state.type == VGIC_V2) - vgic_v2_set_vmcr(vcpu, vmcr); - else + const struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + switch (dist->vgic_model) { + case KVM_DEV_TYPE_ARM_VGIC_V5: + vgic_v5_set_vmcr(vcpu, vmcr); + break; + case KVM_DEV_TYPE_ARM_VGIC_V3: vgic_v3_set_vmcr(vcpu, vmcr); + break; + case KVM_DEV_TYPE_ARM_VGIC_V2: + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) + vgic_v3_set_vmcr(vcpu, vmcr); + else + vgic_v2_set_vmcr(vcpu, vmcr); + break; + default: + BUG(); + } } void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr) { - if (kvm_vgic_global_state.type == VGIC_V2) - vgic_v2_get_vmcr(vcpu, vmcr); - else + const struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + switch (dist->vgic_model) { + case KVM_DEV_TYPE_ARM_VGIC_V5: + vgic_v5_get_vmcr(vcpu, vmcr); + break; + case KVM_DEV_TYPE_ARM_VGIC_V3: vgic_v3_get_vmcr(vcpu, vmcr); + break; + case KVM_DEV_TYPE_ARM_VGIC_V2: + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) + vgic_v3_get_vmcr(vcpu, vmcr); + else + vgic_v2_get_vmcr(vcpu, vmcr); + break; + default: + BUG(); + } } /* diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c index 6a355eca1934..9e841e7afd4a 100644 --- a/arch/arm64/kvm/vgic/vgic-v3.c +++ b/arch/arm64/kvm/vgic/vgic-v3.c @@ -499,7 +499,7 @@ void vcpu_set_ich_hcr(struct kvm_vcpu *vcpu) { struct vgic_v3_cpu_if *vgic_v3 = &vcpu->arch.vgic_cpu.vgic_v3; - if (!vgic_is_v3(vcpu->kvm)) + if (!vgic_host_has_gicv3()) return; /* Hide GICv3 sysreg if necessary */ diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c index 331651087e2c..fdd39ea7f83e 100644 --- a/arch/arm64/kvm/vgic/vgic-v5.c +++ b/arch/arm64/kvm/vgic/vgic-v5.c @@ -1,28 +1,52 @@ // SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025, 2026 Arm Ltd. + */ #include + +#include #include #include "vgic.h" +static struct vgic_v5_ppi_caps ppi_caps; + +/* + * Not all PPIs are guaranteed to be implemented for GICv5. Deterermine which + * ones are, and generate a mask. + */ +static void vgic_v5_get_implemented_ppis(void) +{ + if (!cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) + return; + + /* + * If we have KVM, we have EL2, which means that we have support for the + * EL1 and EL2 Physical & Virtual timers. + */ + __assign_bit(GICV5_ARCH_PPI_CNTHP, ppi_caps.impl_ppi_mask, 1); + __assign_bit(GICV5_ARCH_PPI_CNTV, ppi_caps.impl_ppi_mask, 1); + __assign_bit(GICV5_ARCH_PPI_CNTHV, ppi_caps.impl_ppi_mask, 1); + __assign_bit(GICV5_ARCH_PPI_CNTP, ppi_caps.impl_ppi_mask, 1); + + /* The SW_PPI should be available */ + __assign_bit(GICV5_ARCH_PPI_SW_PPI, ppi_caps.impl_ppi_mask, 1); + + /* The PMUIRQ is available if we have the PMU */ + __assign_bit(GICV5_ARCH_PPI_PMUIRQ, ppi_caps.impl_ppi_mask, system_supports_pmuv3()); +} + /* * Probe for a vGICv5 compatible interrupt controller, returning 0 on success. - * Currently only supports GICv3-based VMs on a GICv5 host, and hence only - * registers a VGIC_V3 device. */ int vgic_v5_probe(const struct gic_kvm_info *info) { + bool v5_registered = false; u64 ich_vtr_el2; int ret; - if (!cpus_have_final_cap(ARM64_HAS_GICV5_LEGACY)) - return -ENODEV; - kvm_vgic_global_state.type = VGIC_V5; - kvm_vgic_global_state.has_gcie_v3_compat = true; - - /* We only support v3 compat mode - use vGICv3 limits */ - kvm_vgic_global_state.max_gic_vcpus = VGIC_V3_MAX_CPUS; kvm_vgic_global_state.vcpu_base = 0; kvm_vgic_global_state.vctrl_base = NULL; @@ -30,6 +54,38 @@ int vgic_v5_probe(const struct gic_kvm_info *info) kvm_vgic_global_state.has_gicv4 = false; kvm_vgic_global_state.has_gicv4_1 = false; + /* + * GICv5 is currently not supported in Protected mode. Skip the + * registration of GICv5 completely to make sure no guests can create a + * GICv5-based guest. + */ + if (is_protected_kvm_enabled()) { + kvm_info("GICv5-based guests are not supported with pKVM\n"); + goto skip_v5; + } + + kvm_vgic_global_state.max_gic_vcpus = VGIC_V5_MAX_CPUS; + + vgic_v5_get_implemented_ppis(); + + ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V5); + if (ret) { + kvm_err("Cannot register GICv5 KVM device.\n"); + goto skip_v5; + } + + v5_registered = true; + kvm_info("GCIE system register CPU interface\n"); + +skip_v5: + /* If we don't support the GICv3 compat mode we're done. */ + if (!cpus_have_final_cap(ARM64_HAS_GICV5_LEGACY)) { + if (!v5_registered) + return -ENODEV; + return 0; + } + + kvm_vgic_global_state.has_gcie_v3_compat = true; ich_vtr_el2 = kvm_call_hyp_ret(__vgic_v3_get_gic_config); kvm_vgic_global_state.ich_vtr_el2 = (u32)ich_vtr_el2; @@ -45,6 +101,10 @@ int vgic_v5_probe(const struct gic_kvm_info *info) return ret; } + /* We potentially limit the max VCPUs further than we need to here */ + kvm_vgic_global_state.max_gic_vcpus = min(VGIC_V3_MAX_CPUS, + VGIC_V5_MAX_CPUS); + static_branch_enable(&kvm_vgic_global_state.gicv3_cpuif); kvm_info("GCIE legacy system register CPU interface\n"); @@ -52,3 +112,424 @@ int vgic_v5_probe(const struct gic_kvm_info *info) return 0; } + +void vgic_v5_reset(struct kvm_vcpu *vcpu) +{ + /* + * We always present 16-bits of ID space to the guest, irrespective of + * the host allowing more. + */ + vcpu->arch.vgic_cpu.num_id_bits = ICC_IDR0_EL1_ID_BITS_16BITS; + + /* + * The GICv5 architeture only supports 5-bits of priority in the + * CPUIF (but potentially fewer in the IRS). + */ + vcpu->arch.vgic_cpu.num_pri_bits = 5; +} + +int vgic_v5_init(struct kvm *kvm) +{ + struct kvm_vcpu *vcpu; + unsigned long idx; + + if (vgic_initialized(kvm)) + return 0; + + kvm_for_each_vcpu(idx, vcpu, kvm) { + if (vcpu_has_nv(vcpu)) { + kvm_err("Nested GICv5 VMs are currently unsupported\n"); + return -EINVAL; + } + } + + /* We only allow userspace to drive the SW_PPI, if it is implemented. */ + bitmap_zero(kvm->arch.vgic.gicv5_vm.userspace_ppis, + VGIC_V5_NR_PRIVATE_IRQS); + __assign_bit(GICV5_ARCH_PPI_SW_PPI, + kvm->arch.vgic.gicv5_vm.userspace_ppis, + VGIC_V5_NR_PRIVATE_IRQS); + bitmap_and(kvm->arch.vgic.gicv5_vm.userspace_ppis, + kvm->arch.vgic.gicv5_vm.userspace_ppis, + ppi_caps.impl_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS); + + return 0; +} + +int vgic_v5_map_resources(struct kvm *kvm) +{ + if (!vgic_initialized(kvm)) + return -EBUSY; + + return 0; +} + +int vgic_v5_finalize_ppi_state(struct kvm *kvm) +{ + struct kvm_vcpu *vcpu0; + int i; + + if (!vgic_is_v5(kvm)) + return 0; + + guard(mutex)(&kvm->arch.config_lock); + + /* + * If SW_PPI has been advertised, then we know we already + * initialised the whole thing, and we can return early. Yes, + * this is pretty hackish as far as state tracking goes... + */ + if (test_bit(GICV5_ARCH_PPI_SW_PPI, kvm->arch.vgic.gicv5_vm.vgic_ppi_mask)) + return 0; + + /* The PPI state for all VCPUs should be the same. Pick the first. */ + vcpu0 = kvm_get_vcpu(kvm, 0); + + bitmap_zero(kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS); + bitmap_zero(kvm->arch.vgic.gicv5_vm.vgic_ppi_hmr, VGIC_V5_NR_PRIVATE_IRQS); + + for_each_set_bit(i, ppi_caps.impl_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS) { + const u32 intid = vgic_v5_make_ppi(i); + struct vgic_irq *irq; + + irq = vgic_get_vcpu_irq(vcpu0, intid); + + /* Expose PPIs with an owner or the SW_PPI, only */ + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) { + if (irq->owner || i == GICV5_ARCH_PPI_SW_PPI) { + __assign_bit(i, kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, 1); + __assign_bit(i, kvm->arch.vgic.gicv5_vm.vgic_ppi_hmr, + irq->config == VGIC_CONFIG_LEVEL); + } + } + + vgic_put_irq(vcpu0->kvm, irq); + } + + return 0; +} + +static u32 vgic_v5_get_effective_priority_mask(struct kvm_vcpu *vcpu) +{ + struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; + u32 highest_ap, priority_mask, apr; + + /* + * If the guest's CPU has not opted to receive interrupts, then the + * effective running priority is the highest priority. Just return 0 + * (the highest priority). + */ + if (!FIELD_GET(FEAT_GCIE_ICH_VMCR_EL2_EN, cpu_if->vgic_vmcr)) + return 0; + + /* + * Counting the number of trailing zeros gives the current active + * priority. Explicitly use the 32-bit version here as we have 32 + * priorities. 32 then means that there are no active priorities. + */ + apr = cpu_if->vgic_apr; + highest_ap = apr ? __builtin_ctz(apr) : 32; + + /* + * An interrupt is of sufficient priority if it is equal to or + * greater than the priority mask. Add 1 to the priority mask + * (i.e., lower priority) to match the APR logic before taking + * the min. This gives us the lowest priority that is masked. + */ + priority_mask = FIELD_GET(FEAT_GCIE_ICH_VMCR_EL2_VPMR, cpu_if->vgic_vmcr); + + return min(highest_ap, priority_mask + 1); +} + +/* + * For GICv5, the PPIs are mostly directly managed by the hardware. We (the + * hypervisor) handle the pending, active, enable state save/restore, but don't + * need the PPIs to be queued on a per-VCPU AP list. Therefore, sanity check the + * state, unlock, and return. + */ +bool vgic_v5_ppi_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq, + unsigned long flags) + __releases(&irq->irq_lock) +{ + struct kvm_vcpu *vcpu; + + lockdep_assert_held(&irq->irq_lock); + + if (WARN_ON_ONCE(!__irq_is_ppi(KVM_DEV_TYPE_ARM_VGIC_V5, irq->intid))) + goto out_unlock_fail; + + vcpu = irq->target_vcpu; + if (WARN_ON_ONCE(!vcpu)) + goto out_unlock_fail; + + raw_spin_unlock_irqrestore(&irq->irq_lock, flags); + + /* Directly kick the target VCPU to make sure it sees the IRQ */ + kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu); + kvm_vcpu_kick(vcpu); + + return true; + +out_unlock_fail: + raw_spin_unlock_irqrestore(&irq->irq_lock, flags); + + return false; +} + +/* + * Sets/clears the corresponding bit in the ICH_PPI_DVIR register. + */ +void vgic_v5_set_ppi_dvi(struct kvm_vcpu *vcpu, struct vgic_irq *irq, bool dvi) +{ + struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; + u32 ppi; + + lockdep_assert_held(&irq->irq_lock); + + ppi = vgic_v5_get_hwirq_id(irq->intid); + __assign_bit(ppi, cpu_if->vgic_ppi_dvir, dvi); +} + +static struct irq_ops vgic_v5_ppi_irq_ops = { + .queue_irq_unlock = vgic_v5_ppi_queue_irq_unlock, + .set_direct_injection = vgic_v5_set_ppi_dvi, +}; + +void vgic_v5_set_ppi_ops(struct kvm_vcpu *vcpu, u32 vintid) +{ + kvm_vgic_set_irq_ops(vcpu, vintid, &vgic_v5_ppi_irq_ops); +} + +/* + * Sync back the PPI priorities to the vgic_irq shadow state for any interrupts + * exposed to the guest (skipping all others). + */ +static void vgic_v5_sync_ppi_priorities(struct kvm_vcpu *vcpu) +{ + struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; + u64 priorityr; + int i; + + /* + * We have up to 16 PPI Priority regs, but only have a few interrupts + * that the guest is allowed to use. Limit our sync of PPI priorities to + * those actually exposed to the guest by first iterating over the mask + * of exposed PPIs. + */ + for_each_set_bit(i, vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS) { + u32 intid = vgic_v5_make_ppi(i); + struct vgic_irq *irq; + int pri_idx, pri_reg, pri_bit; + u8 priority; + + /* + * Determine which priority register and the field within it to + * extract. + */ + pri_reg = i / 8; + pri_idx = i % 8; + pri_bit = pri_idx * 8; + + priorityr = cpu_if->vgic_ppi_priorityr[pri_reg]; + priority = field_get(GENMASK(pri_bit + 4, pri_bit), priorityr); + + irq = vgic_get_vcpu_irq(vcpu, intid); + + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) + irq->priority = priority; + + vgic_put_irq(vcpu->kvm, irq); + } +} + +bool vgic_v5_has_pending_ppi(struct kvm_vcpu *vcpu) +{ + unsigned int priority_mask; + int i; + + priority_mask = vgic_v5_get_effective_priority_mask(vcpu); + + /* + * If the combined priority mask is 0, nothing can be signalled! In the + * case where the guest has disabled interrupt delivery for the vcpu + * (via ICV_CR0_EL1.EN->ICH_VMCR_EL2.EN), we calculate the priority mask + * as 0 too (the highest possible priority). + */ + if (!priority_mask) + return false; + + for_each_set_bit(i, vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS) { + u32 intid = vgic_v5_make_ppi(i); + bool has_pending = false; + struct vgic_irq *irq; + + irq = vgic_get_vcpu_irq(vcpu, intid); + + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) + if (irq->enabled && irq->priority < priority_mask) + has_pending = irq->hw ? vgic_get_phys_line_level(irq) : irq_is_pending(irq); + + vgic_put_irq(vcpu->kvm, irq); + + if (has_pending) + return true; + } + + return false; +} + +/* + * Detect any PPIs state changes, and propagate the state with KVM's + * shadow structures. + */ +void vgic_v5_fold_ppi_state(struct kvm_vcpu *vcpu) +{ + struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; + unsigned long *activer, *pendr; + int i; + + activer = host_data_ptr(vgic_v5_ppi_state)->activer_exit; + pendr = host_data_ptr(vgic_v5_ppi_state)->pendr; + + for_each_set_bit(i, vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, + VGIC_V5_NR_PRIVATE_IRQS) { + u32 intid = vgic_v5_make_ppi(i); + struct vgic_irq *irq; + + irq = vgic_get_vcpu_irq(vcpu, intid); + + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) { + irq->active = test_bit(i, activer); + + /* This is an OR to avoid losing incoming edges! */ + if (irq->config == VGIC_CONFIG_EDGE) + irq->pending_latch |= test_bit(i, pendr); + } + + vgic_put_irq(vcpu->kvm, irq); + } + + /* + * Re-inject the exit state as entry state next time! + * + * Note that the write of the Enable state is trapped, and hence there + * is nothing to explcitly sync back here as we already have the latest + * copy by definition. + */ + bitmap_copy(cpu_if->vgic_ppi_activer, activer, VGIC_V5_NR_PRIVATE_IRQS); +} + +void vgic_v5_flush_ppi_state(struct kvm_vcpu *vcpu) +{ + DECLARE_BITMAP(pendr, VGIC_V5_NR_PRIVATE_IRQS); + int i; + + /* + * Time to enter the guest - we first need to build the guest's + * ICC_PPI_PENDRx_EL1, however. + */ + bitmap_zero(pendr, VGIC_V5_NR_PRIVATE_IRQS); + for_each_set_bit(i, vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, + VGIC_V5_NR_PRIVATE_IRQS) { + u32 intid = vgic_v5_make_ppi(i); + struct vgic_irq *irq; + + irq = vgic_get_vcpu_irq(vcpu, intid); + + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) { + __assign_bit(i, pendr, irq_is_pending(irq)); + if (irq->config == VGIC_CONFIG_EDGE) + irq->pending_latch = false; + } + + vgic_put_irq(vcpu->kvm, irq); + } + + /* + * Copy the shadow state to the pending reg that will be written to the + * ICH_PPI_PENDRx_EL2 regs. While the guest is running we track any + * incoming changes to the pending state in the vgic_irq structures. The + * incoming changes are merged with the outgoing changes on the return + * path. + */ + bitmap_copy(host_data_ptr(vgic_v5_ppi_state)->pendr, pendr, + VGIC_V5_NR_PRIVATE_IRQS); +} + +void vgic_v5_load(struct kvm_vcpu *vcpu) +{ + struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; + + /* + * On the WFI path, vgic_load is called a second time. The first is when + * scheduling in the vcpu thread again, and the second is when leaving + * WFI. Skip the second instance as it serves no purpose and just + * restores the same state again. + */ + if (cpu_if->gicv5_vpe.resident) + return; + + kvm_call_hyp(__vgic_v5_restore_vmcr_apr, cpu_if); + + cpu_if->gicv5_vpe.resident = true; +} + +void vgic_v5_put(struct kvm_vcpu *vcpu) +{ + struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; + + /* + * Do nothing if we're not resident. This can happen in the WFI path + * where we do a vgic_put in the WFI path and again later when + * descheduling the thread. We risk losing VMCR state if we sync it + * twice, so instead return early in this case. + */ + if (!cpu_if->gicv5_vpe.resident) + return; + + kvm_call_hyp(__vgic_v5_save_apr, cpu_if); + + cpu_if->gicv5_vpe.resident = false; + + /* The shadow priority is only updated on entering WFI */ + if (vcpu_get_flag(vcpu, IN_WFI)) + vgic_v5_sync_ppi_priorities(vcpu); +} + +void vgic_v5_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) +{ + struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; + u64 vmcr = cpu_if->vgic_vmcr; + + vmcrp->en = FIELD_GET(FEAT_GCIE_ICH_VMCR_EL2_EN, vmcr); + vmcrp->pmr = FIELD_GET(FEAT_GCIE_ICH_VMCR_EL2_VPMR, vmcr); +} + +void vgic_v5_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) +{ + struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; + u64 vmcr; + + vmcr = FIELD_PREP(FEAT_GCIE_ICH_VMCR_EL2_VPMR, vmcrp->pmr) | + FIELD_PREP(FEAT_GCIE_ICH_VMCR_EL2_EN, vmcrp->en); + + cpu_if->vgic_vmcr = vmcr; +} + +void vgic_v5_restore_state(struct kvm_vcpu *vcpu) +{ + struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; + + __vgic_v5_restore_state(cpu_if); + __vgic_v5_restore_ppi_state(cpu_if); + dsb(sy); +} + +void vgic_v5_save_state(struct kvm_vcpu *vcpu) +{ + struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5; + + __vgic_v5_save_state(cpu_if); + __vgic_v5_save_ppi_state(cpu_if); + dsb(sy); +} diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index e22b79cfff96..1e9fe8764584 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -86,6 +86,10 @@ static struct vgic_irq *vgic_get_lpi(struct kvm *kvm, u32 intid) */ struct vgic_irq *vgic_get_irq(struct kvm *kvm, u32 intid) { + /* Non-private IRQs are not yet implemented for GICv5 */ + if (vgic_is_v5(kvm)) + return NULL; + /* SPIs */ if (intid >= VGIC_NR_PRIVATE_IRQS && intid < (kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS)) { @@ -94,7 +98,7 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, u32 intid) } /* LPIs */ - if (intid >= VGIC_MIN_LPI) + if (irq_is_lpi(kvm, intid)) return vgic_get_lpi(kvm, intid); return NULL; @@ -105,6 +109,18 @@ struct vgic_irq *vgic_get_vcpu_irq(struct kvm_vcpu *vcpu, u32 intid) if (WARN_ON(!vcpu)) return NULL; + if (vgic_is_v5(vcpu->kvm)) { + u32 int_num, hwirq_id; + + if (!__irq_is_ppi(KVM_DEV_TYPE_ARM_VGIC_V5, intid)) + return NULL; + + hwirq_id = FIELD_GET(GICV5_HWIRQ_ID, intid); + int_num = array_index_nospec(hwirq_id, VGIC_V5_NR_PRIVATE_IRQS); + + return &vcpu->arch.vgic_cpu.private_irqs[int_num]; + } + /* SGIs and PPIs */ if (intid < VGIC_NR_PRIVATE_IRQS) { intid = array_index_nospec(intid, VGIC_NR_PRIVATE_IRQS); @@ -123,7 +139,7 @@ static void vgic_release_lpi_locked(struct vgic_dist *dist, struct vgic_irq *irq static __must_check bool __vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) { - if (irq->intid < VGIC_MIN_LPI) + if (!irq_is_lpi(kvm, irq->intid)) return false; return refcount_dec_and_test(&irq->refcount); @@ -148,7 +164,7 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) * Acquire/release it early on lockdep kernels to make locking issues * in rare release paths a bit more obvious. */ - if (IS_ENABLED(CONFIG_LOCKDEP) && irq->intid >= VGIC_MIN_LPI) { + if (IS_ENABLED(CONFIG_LOCKDEP) && irq_is_lpi(kvm, irq->intid)) { guard(spinlock_irqsave)(&dist->lpi_xa.xa_lock); } @@ -186,7 +202,7 @@ void vgic_flush_pending_lpis(struct kvm_vcpu *vcpu) raw_spin_lock_irqsave(&vgic_cpu->ap_list_lock, flags); list_for_each_entry_safe(irq, tmp, &vgic_cpu->ap_list_head, ap_list) { - if (irq->intid >= VGIC_MIN_LPI) { + if (irq_is_lpi(vcpu->kvm, irq->intid)) { raw_spin_lock(&irq->irq_lock); list_del(&irq->ap_list); irq->vcpu = NULL; @@ -404,6 +420,9 @@ bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq, lockdep_assert_held(&irq->irq_lock); + if (irq->ops && irq->ops->queue_irq_unlock) + return irq->ops->queue_irq_unlock(kvm, irq, flags); + retry: vcpu = vgic_target_oracle(irq); if (irq->vcpu || !vcpu) { @@ -521,12 +540,12 @@ int kvm_vgic_inject_irq(struct kvm *kvm, struct kvm_vcpu *vcpu, if (ret) return ret; - if (!vcpu && intid < VGIC_NR_PRIVATE_IRQS) + if (!vcpu && irq_is_private(kvm, intid)) return -EINVAL; trace_vgic_update_irq_pending(vcpu ? vcpu->vcpu_idx : 0, intid, level); - if (intid < VGIC_NR_PRIVATE_IRQS) + if (irq_is_private(kvm, intid)) irq = vgic_get_vcpu_irq(vcpu, intid); else irq = vgic_get_irq(kvm, intid); @@ -553,10 +572,27 @@ int kvm_vgic_inject_irq(struct kvm *kvm, struct kvm_vcpu *vcpu, return 0; } +void kvm_vgic_set_irq_ops(struct kvm_vcpu *vcpu, u32 vintid, + struct irq_ops *ops) +{ + struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, vintid); + + BUG_ON(!irq); + + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) + irq->ops = ops; + + vgic_put_irq(vcpu->kvm, irq); +} + +void kvm_vgic_clear_irq_ops(struct kvm_vcpu *vcpu, u32 vintid) +{ + kvm_vgic_set_irq_ops(vcpu, vintid, NULL); +} + /* @irq->irq_lock must be held */ static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq, - unsigned int host_irq, - struct irq_ops *ops) + unsigned int host_irq) { struct irq_desc *desc; struct irq_data *data; @@ -576,20 +612,25 @@ static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq, irq->hw = true; irq->host_irq = host_irq; irq->hwintid = data->hwirq; - irq->ops = ops; + + if (irq->ops && irq->ops->set_direct_injection) + irq->ops->set_direct_injection(vcpu, irq, true); + return 0; } /* @irq->irq_lock must be held */ static inline void kvm_vgic_unmap_irq(struct vgic_irq *irq) { + if (irq->ops && irq->ops->set_direct_injection) + irq->ops->set_direct_injection(irq->target_vcpu, irq, false); + irq->hw = false; irq->hwintid = 0; - irq->ops = NULL; } int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq, - u32 vintid, struct irq_ops *ops) + u32 vintid) { struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, vintid); unsigned long flags; @@ -598,7 +639,7 @@ int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq, BUG_ON(!irq); raw_spin_lock_irqsave(&irq->irq_lock, flags); - ret = kvm_vgic_map_irq(vcpu, irq, host_irq, ops); + ret = kvm_vgic_map_irq(vcpu, irq, host_irq); raw_spin_unlock_irqrestore(&irq->irq_lock, flags); vgic_put_irq(vcpu->kvm, irq); @@ -685,7 +726,7 @@ int kvm_vgic_set_owner(struct kvm_vcpu *vcpu, unsigned int intid, void *owner) return -EAGAIN; /* SGIs and LPIs cannot be wired up to any device */ - if (!irq_is_ppi(intid) && !vgic_valid_spi(vcpu->kvm, intid)) + if (!irq_is_ppi(vcpu->kvm, intid) && !vgic_valid_spi(vcpu->kvm, intid)) return -EINVAL; irq = vgic_get_vcpu_irq(vcpu, intid); @@ -812,8 +853,13 @@ retry: vgic_release_deleted_lpis(vcpu->kvm); } -static inline void vgic_fold_lr_state(struct kvm_vcpu *vcpu) +static void vgic_fold_state(struct kvm_vcpu *vcpu) { + if (vgic_is_v5(vcpu->kvm)) { + vgic_v5_fold_ppi_state(vcpu); + return; + } + if (!*host_data_ptr(last_lr_irq)) return; @@ -1002,7 +1048,10 @@ static inline bool can_access_vgic_from_kernel(void) static inline void vgic_save_state(struct kvm_vcpu *vcpu) { - if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) + /* No switch statement here. See comment in vgic_restore_state() */ + if (vgic_is_v5(vcpu->kvm)) + vgic_v5_save_state(vcpu); + else if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) vgic_v2_save_state(vcpu); else __vgic_v3_save_state(&vcpu->arch.vgic_cpu.vgic_v3); @@ -1011,20 +1060,24 @@ static inline void vgic_save_state(struct kvm_vcpu *vcpu) /* Sync back the hardware VGIC state into our emulation after a guest's run. */ void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) { - /* If nesting, emulate the HW effect from L0 to L1 */ - if (vgic_state_is_nested(vcpu)) { - vgic_v3_sync_nested(vcpu); - return; - } + if (vgic_is_v3(vcpu->kvm)) { + /* If nesting, emulate the HW effect from L0 to L1 */ + if (vgic_state_is_nested(vcpu)) { + vgic_v3_sync_nested(vcpu); + return; + } - if (vcpu_has_nv(vcpu)) - vgic_v3_nested_update_mi(vcpu); + if (vcpu_has_nv(vcpu)) + vgic_v3_nested_update_mi(vcpu); + } if (can_access_vgic_from_kernel()) vgic_save_state(vcpu); - vgic_fold_lr_state(vcpu); - vgic_prune_ap_list(vcpu); + vgic_fold_state(vcpu); + + if (!vgic_is_v5(vcpu->kvm)) + vgic_prune_ap_list(vcpu); } /* Sync interrupts that were deactivated through a DIR trap */ @@ -1040,12 +1093,34 @@ void kvm_vgic_process_async_update(struct kvm_vcpu *vcpu) static inline void vgic_restore_state(struct kvm_vcpu *vcpu) { - if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) + /* + * As nice as it would be to restructure this code into a switch + * statement as can be found elsewhere, the logic quickly gets ugly. + * + * __vgic_v3_restore_state() is doing a lot of heavy lifting here. It is + * required for GICv3-on-GICv3, GICv2-on-GICv3, GICv3-on-GICv5, and the + * no-in-kernel-irqchip case on GICv3 hardware. Hence, adding a switch + * here results in much more complex code. + */ + if (vgic_is_v5(vcpu->kvm)) + vgic_v5_restore_state(vcpu); + else if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) vgic_v2_restore_state(vcpu); else __vgic_v3_restore_state(&vcpu->arch.vgic_cpu.vgic_v3); } +static void vgic_flush_state(struct kvm_vcpu *vcpu) +{ + if (vgic_is_v5(vcpu->kvm)) { + vgic_v5_flush_ppi_state(vcpu); + return; + } + + scoped_guard(raw_spinlock, &vcpu->arch.vgic_cpu.ap_list_lock) + vgic_flush_lr_state(vcpu); +} + /* Flush our emulation state into the GIC hardware before entering the guest. */ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) { @@ -1082,42 +1157,69 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) DEBUG_SPINLOCK_BUG_ON(!irqs_disabled()); - scoped_guard(raw_spinlock, &vcpu->arch.vgic_cpu.ap_list_lock) - vgic_flush_lr_state(vcpu); + vgic_flush_state(vcpu); if (can_access_vgic_from_kernel()) vgic_restore_state(vcpu); - if (vgic_supports_direct_irqs(vcpu->kvm)) + if (vgic_supports_direct_irqs(vcpu->kvm) && kvm_vgic_global_state.has_gicv4) vgic_v4_commit(vcpu); } void kvm_vgic_load(struct kvm_vcpu *vcpu) { + const struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + if (unlikely(!irqchip_in_kernel(vcpu->kvm) || !vgic_initialized(vcpu->kvm))) { if (has_vhe() && static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) __vgic_v3_activate_traps(&vcpu->arch.vgic_cpu.vgic_v3); return; } - if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) - vgic_v2_load(vcpu); - else + switch (dist->vgic_model) { + case KVM_DEV_TYPE_ARM_VGIC_V5: + vgic_v5_load(vcpu); + break; + case KVM_DEV_TYPE_ARM_VGIC_V3: vgic_v3_load(vcpu); + break; + case KVM_DEV_TYPE_ARM_VGIC_V2: + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) + vgic_v3_load(vcpu); + else + vgic_v2_load(vcpu); + break; + default: + BUG(); + } } void kvm_vgic_put(struct kvm_vcpu *vcpu) { + const struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + if (unlikely(!irqchip_in_kernel(vcpu->kvm) || !vgic_initialized(vcpu->kvm))) { if (has_vhe() && static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) __vgic_v3_deactivate_traps(&vcpu->arch.vgic_cpu.vgic_v3); return; } - if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) - vgic_v2_put(vcpu); - else + switch (dist->vgic_model) { + case KVM_DEV_TYPE_ARM_VGIC_V5: + vgic_v5_put(vcpu); + break; + case KVM_DEV_TYPE_ARM_VGIC_V3: vgic_v3_put(vcpu); + break; + case KVM_DEV_TYPE_ARM_VGIC_V2: + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) + vgic_v3_put(vcpu); + else + vgic_v2_put(vcpu); + break; + default: + BUG(); + } } int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) @@ -1128,6 +1230,9 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) unsigned long flags; struct vgic_vmcr vmcr; + if (vgic_is_v5(vcpu->kvm)) + return vgic_v5_has_pending_ppi(vcpu); + if (!vcpu->kvm->arch.vgic.enabled) return false; diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index c9b3bb07e483..9d941241c8a2 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -187,6 +187,7 @@ static inline u64 vgic_ich_hcr_trap_bits(void) * registers regardless of the hardware backed GIC used. */ struct vgic_vmcr { + u32 en; /* GICv5-specific */ u32 grpen0; u32 grpen1; @@ -363,6 +364,19 @@ void vgic_debug_init(struct kvm *kvm); void vgic_debug_destroy(struct kvm *kvm); int vgic_v5_probe(const struct gic_kvm_info *info); +void vgic_v5_reset(struct kvm_vcpu *vcpu); +int vgic_v5_init(struct kvm *kvm); +int vgic_v5_map_resources(struct kvm *kvm); +void vgic_v5_set_ppi_ops(struct kvm_vcpu *vcpu, u32 vintid); +bool vgic_v5_has_pending_ppi(struct kvm_vcpu *vcpu); +void vgic_v5_flush_ppi_state(struct kvm_vcpu *vcpu); +void vgic_v5_fold_ppi_state(struct kvm_vcpu *vcpu); +void vgic_v5_load(struct kvm_vcpu *vcpu); +void vgic_v5_put(struct kvm_vcpu *vcpu); +void vgic_v5_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); +void vgic_v5_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); +void vgic_v5_restore_state(struct kvm_vcpu *vcpu); +void vgic_v5_save_state(struct kvm_vcpu *vcpu); static inline int vgic_v3_max_apr_idx(struct kvm_vcpu *vcpu) { @@ -425,15 +439,6 @@ void vgic_its_invalidate_all_caches(struct kvm *kvm); int vgic_its_inv_lpi(struct kvm *kvm, struct vgic_irq *irq); int vgic_its_invall(struct kvm_vcpu *vcpu); -bool system_supports_direct_sgis(void); -bool vgic_supports_direct_msis(struct kvm *kvm); -bool vgic_supports_direct_sgis(struct kvm *kvm); - -static inline bool vgic_supports_direct_irqs(struct kvm *kvm) -{ - return vgic_supports_direct_msis(kvm) || vgic_supports_direct_sgis(kvm); -} - int vgic_v4_init(struct kvm *kvm); void vgic_v4_teardown(struct kvm *kvm); void vgic_v4_configure_vsgis(struct kvm *kvm); @@ -447,6 +452,11 @@ static inline bool kvm_has_gicv3(struct kvm *kvm) return kvm_has_feat(kvm, ID_AA64PFR0_EL1, GIC, IMP); } +static inline bool kvm_has_gicv5(struct kvm *kvm) +{ + return kvm_has_feat(kvm, ID_AA64PFR2_EL1, GCIE, IMP); +} + void vgic_v3_flush_nested(struct kvm_vcpu *vcpu); void vgic_v3_sync_nested(struct kvm_vcpu *vcpu); void vgic_v3_load_nested(struct kvm_vcpu *vcpu); @@ -454,15 +464,32 @@ void vgic_v3_put_nested(struct kvm_vcpu *vcpu); void vgic_v3_handle_nested_maint_irq(struct kvm_vcpu *vcpu); void vgic_v3_nested_update_mi(struct kvm_vcpu *vcpu); -static inline bool vgic_is_v3_compat(struct kvm *kvm) +static inline bool vgic_host_has_gicv3(void) { - return cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF) && + /* + * Either the host is a native GICv3, or it is GICv5 with + * FEAT_GCIE_LEGACY. + */ + return kvm_vgic_global_state.type == VGIC_V3 || kvm_vgic_global_state.has_gcie_v3_compat; } -static inline bool vgic_is_v3(struct kvm *kvm) +static inline bool vgic_host_has_gicv5(void) { - return kvm_vgic_global_state.type == VGIC_V3 || vgic_is_v3_compat(kvm); + return kvm_vgic_global_state.type == VGIC_V5; +} + +bool system_supports_direct_sgis(void); +bool vgic_supports_direct_msis(struct kvm *kvm); +bool vgic_supports_direct_sgis(struct kvm *kvm); + +static inline bool vgic_supports_direct_irqs(struct kvm *kvm) +{ + /* GICv5 always supports direct IRQs */ + if (vgic_is_v5(kvm)) + return true; + + return vgic_supports_direct_msis(kvm) || vgic_supports_direct_sgis(kvm); } int vgic_its_debug_init(struct kvm_device *dev); diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile index 633e5223d944..448c917494f3 100644 --- a/arch/arm64/lib/Makefile +++ b/arch/arm64/lib/Makefile @@ -5,12 +5,6 @@ lib-y := clear_user.o delay.o copy_from_user.o \ memset.o memcmp.o strcmp.o strncmp.o strlen.o \ strnlen.o strchr.o strrchr.o tishift.o -ifeq ($(CONFIG_KERNEL_MODE_NEON), y) -obj-$(CONFIG_XOR_BLOCKS) += xor-neon.o -CFLAGS_xor-neon.o += $(CC_FLAGS_FPU) -CFLAGS_REMOVE_xor-neon.o += $(CC_FLAGS_NO_FPU) -endif - lib-$(CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE) += uaccess_flushcache.o obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o diff --git a/arch/arm64/lib/insn.c b/arch/arm64/lib/insn.c index cc5b40917d0d..37ce75f7f1f0 100644 --- a/arch/arm64/lib/insn.c +++ b/arch/arm64/lib/insn.c @@ -338,6 +338,8 @@ u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr, long offset; offset = label_imm_common(pc, addr, SZ_1M); + if (offset >= SZ_1M) + return AARCH64_BREAK_FAULT; insn = aarch64_insn_get_bcond_value(); diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S index 503567c864fd..ab75c050f559 100644 --- a/arch/arm64/mm/cache.S +++ b/arch/arm64/mm/cache.S @@ -132,17 +132,7 @@ alternative_else_nop_endif ret SYM_FUNC_END(dcache_clean_pou) -/* - * dcache_inval_poc(start, end) - * - * Ensure that any D-cache lines for the interval [start, end) - * are invalidated. Any partial lines at the ends of the interval are - * also cleaned to PoC to prevent data loss. - * - * - start - kernel start address of region - * - end - kernel end address of region - */ -SYM_FUNC_START(__pi_dcache_inval_poc) +.macro __dcache_inval_poc_nosync dcache_line_size x2, x3 sub x3, x2, #1 tst x1, x3 // end cache line aligned? @@ -158,11 +148,41 @@ SYM_FUNC_START(__pi_dcache_inval_poc) 3: add x0, x0, x2 cmp x0, x1 b.lo 2b +.endm + +/* + * dcache_inval_poc(start, end) + * + * Ensure that any D-cache lines for the interval [start, end) + * are invalidated. Any partial lines at the ends of the interval are + * also cleaned to PoC to prevent data loss. + * + * - start - kernel start address of region + * - end - kernel end address of region + */ +SYM_FUNC_START(__pi_dcache_inval_poc) + __dcache_inval_poc_nosync dsb sy ret SYM_FUNC_END(__pi_dcache_inval_poc) SYM_FUNC_ALIAS(dcache_inval_poc, __pi_dcache_inval_poc) +/* + * dcache_inval_poc_nosync(start, end) + * + * Issue the instructions of D-cache lines for the interval [start, end) + * for invalidation. Not necessarily cleaned to PoC till an explicit dsb + * sy is issued later + * + * - start - kernel start address of region + * - end - kernel end address of region + */ +SYM_FUNC_START(__pi_dcache_inval_poc_nosync) + __dcache_inval_poc_nosync + ret +SYM_FUNC_END(__pi_dcache_inval_poc_nosync) +SYM_FUNC_ALIAS(dcache_inval_poc_nosync, __pi_dcache_inval_poc_nosync) + /* * dcache_clean_poc(start, end) * @@ -178,6 +198,21 @@ SYM_FUNC_START(__pi_dcache_clean_poc) SYM_FUNC_END(__pi_dcache_clean_poc) SYM_FUNC_ALIAS(dcache_clean_poc, __pi_dcache_clean_poc) +/* + * dcache_clean_poc_nosync(start, end) + * + * Issue the instructions of D-cache lines for the interval [start, end). + * not necessarily cleaned to the PoC till an explicit dsb sy afterward. + * + * - start - virtual start address of region + * - end - virtual end address of region + */ +SYM_FUNC_START(__pi_dcache_clean_poc_nosync) + dcache_by_line_op_nosync cvac, x0, x1, x2, x3 + ret +SYM_FUNC_END(__pi_dcache_clean_poc_nosync) +SYM_FUNC_ALIAS(dcache_clean_poc_nosync, __pi_dcache_clean_poc_nosync) + /* * dcache_clean_pop(start, end) * diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index b2b5792b2caa..ae1ae0280eef 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -17,7 +17,7 @@ void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, { unsigned long start = (unsigned long)phys_to_virt(paddr); - dcache_clean_poc(start, start + size); + dcache_clean_poc_nosync(start, start + size); } void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, @@ -28,7 +28,7 @@ void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, if (dir == DMA_TO_DEVICE) return; - dcache_inval_poc(start, start + size); + dcache_inval_poc_nosync(start, start + size); } void arch_dma_prep_coherent(struct page *page, size_t size) diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 920a8b244d59..b4290d16ff92 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -43,6 +44,7 @@ #include #include #include +#include struct fault_info { int (*fn)(unsigned long far, unsigned long esr, @@ -289,6 +291,15 @@ static inline bool is_el1_permission_fault(unsigned long addr, unsigned long esr return false; } +static bool is_pkvm_stage2_abort(unsigned int esr) +{ + /* + * S1PTW should only ever be set in ESR_EL1 if the pkvm hypervisor + * injected a stage-2 abort -- see host_inject_mem_abort(). + */ + return is_pkvm_initialized() && (esr & ESR_ELx_S1PTW); +} + static bool __kprobes is_spurious_el1_translation_fault(unsigned long addr, unsigned long esr, struct pt_regs *regs) @@ -309,8 +320,14 @@ static bool __kprobes is_spurious_el1_translation_fault(unsigned long addr, * If we now have a valid translation, treat the translation fault as * spurious. */ - if (!(par & SYS_PAR_EL1_F)) + if (!(par & SYS_PAR_EL1_F)) { + if (is_pkvm_stage2_abort(esr)) { + par &= SYS_PAR_EL1_PA; + return pkvm_force_reclaim_guest_page(par); + } + return true; + } /* * If we got a different type of fault from the AT instruction, @@ -396,9 +413,11 @@ static void __do_kernel_fault(unsigned long addr, unsigned long esr, if (!is_el1_instruction_abort(esr) && fixup_exception(regs, esr)) return; - if (WARN_RATELIMIT(is_spurious_el1_translation_fault(addr, esr, regs), - "Ignoring spurious kernel translation fault at virtual address %016lx\n", addr)) + if (is_spurious_el1_translation_fault(addr, esr, regs)) { + WARN_RATELIMIT(!is_pkvm_stage2_abort(esr), + "Ignoring spurious kernel translation fault at virtual address %016lx\n", addr); return; + } if (is_el1_mte_sync_tag_check_fault(esr)) { do_tag_recovery(addr, esr, regs); @@ -415,10 +434,15 @@ static void __do_kernel_fault(unsigned long addr, unsigned long esr, msg = "read from unreadable memory"; } else if (addr < PAGE_SIZE) { msg = "NULL pointer dereference"; + } else if (is_pkvm_stage2_abort(esr)) { + msg = "access to hypervisor-protected memory"; } else { - if (esr_fsc_is_translation_fault(esr) && - kfence_handle_page_fault(addr, esr & ESR_ELx_WNR, regs)) - return; + if (esr_fsc_is_translation_fault(esr)) { + if (kfence_handle_page_fault(addr, esr & ESR_ELx_WNR, regs)) + return; + if (bpf_arena_handle_page_fault(addr, esr & ESR_ELx_WNR, regs->pc)) + return; + } msg = "paging request"; } @@ -641,6 +665,13 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr, addr, esr, regs); } + if (is_pkvm_stage2_abort(esr)) { + if (!user_mode(regs)) + goto no_context; + arm64_force_sig_fault(SIGSEGV, SEGV_ACCERR, far, "stage-2 fault"); + return 0; + } + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr); if (!(mm_flags & FAULT_FLAG_USER)) diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index d47eab0ba351..97987f850a33 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -397,9 +397,6 @@ void free_initmem(void) WARN_ON(!IS_ALIGNED((unsigned long)lm_init_begin, PAGE_SIZE)); WARN_ON(!IS_ALIGNED((unsigned long)lm_init_end, PAGE_SIZE)); - /* Delete __init region from memblock.reserved. */ - memblock_free(lm_init_begin, lm_init_end - lm_init_begin); - free_reserved_area(lm_init_begin, lm_init_end, POISON_FREE_INITMEM, "unused kernel"); /* diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index adf84962d579..0816c40fc7af 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -35,8 +34,8 @@ #define ARENA_VM_START (MAX_BPF_JIT_REG + 5) #define check_imm(bits, imm) do { \ - if ((((imm) > 0) && ((imm) >> (bits))) || \ - (((imm) < 0) && (~(imm) >> (bits)))) { \ + if ((((imm) > 0) && ((imm) >> ((bits) - 1))) || \ + (((imm) < 0) && (~(imm) >> ((bits) - 1)))) { \ pr_info("[%2d] imm=%d(0x%x) out of range\n", \ i, imm, imm); \ return -EINVAL; \ @@ -1198,8 +1197,8 @@ static int add_exception_handler(const struct bpf_insn *insn, * >0 - successfully JITed a 16-byte eBPF instruction. * <0 - failed to JIT. */ -static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, - bool extra_pass) +static int build_insn(const struct bpf_verifier_env *env, const struct bpf_insn *insn, + struct jit_ctx *ctx, bool extra_pass) { const u8 code = insn->code; u8 dst = bpf2a64[insn->dst_reg]; @@ -1224,6 +1223,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, int ret; bool sign_extend; + if (bpf_insn_is_indirect_target(env, ctx->prog, i)) + emit_bti(A64_BTI_J, ctx); + switch (code) { /* dst = src */ case BPF_ALU | BPF_MOV | BPF_X: @@ -1899,7 +1901,7 @@ emit_cond_jmp: return 0; } -static int build_body(struct jit_ctx *ctx, bool extra_pass) +static int build_body(struct bpf_verifier_env *env, struct jit_ctx *ctx, bool extra_pass) { const struct bpf_prog *prog = ctx->prog; int i; @@ -1918,7 +1920,7 @@ static int build_body(struct jit_ctx *ctx, bool extra_pass) int ret; ctx->offset[i] = ctx->idx; - ret = build_insn(insn, ctx, extra_pass); + ret = build_insn(env, insn, ctx, extra_pass); if (ret > 0) { i++; ctx->offset[i] = ctx->idx; @@ -1961,11 +1963,6 @@ static int validate_ctx(struct jit_ctx *ctx) return 0; } -static inline void bpf_flush_icache(void *start, void *end) -{ - flush_icache_range((unsigned long)start, (unsigned long)end); -} - static void priv_stack_init_guard(void __percpu *priv_stack_ptr, int alloc_size) { int cpu, underflow_idx = (alloc_size - PRIV_STACK_GUARD_SZ) >> 3; @@ -2006,17 +2003,15 @@ struct arm64_jit_data { struct jit_ctx ctx; }; -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) { int image_size, prog_size, extable_size, extable_align, extable_offset; - struct bpf_prog *tmp, *orig_prog = prog; struct bpf_binary_header *header; struct bpf_binary_header *ro_header = NULL; struct arm64_jit_data *jit_data; void __percpu *priv_stack_ptr = NULL; bool was_classic = bpf_prog_was_classic(prog); int priv_stack_alloc_sz; - bool tmp_blinded = false; bool extra_pass = false; struct jit_ctx ctx; u8 *image_ptr; @@ -2025,26 +2020,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) int exentry_idx; if (!prog->jit_requested) - return orig_prog; - - tmp = bpf_jit_blind_constants(prog); - /* If blinding was requested and we failed during blinding, - * we must fall back to the interpreter. - */ - if (IS_ERR(tmp)) - return orig_prog; - if (tmp != prog) { - tmp_blinded = true; - prog = tmp; - } + return prog; jit_data = prog->aux->jit_data; if (!jit_data) { jit_data = kzalloc_obj(*jit_data); - if (!jit_data) { - prog = orig_prog; - goto out; - } + if (!jit_data) + return prog; prog->aux->jit_data = jit_data; } priv_stack_ptr = prog->aux->priv_stack_ptr; @@ -2056,10 +2038,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) priv_stack_alloc_sz = round_up(prog->aux->stack_depth, 16) + 2 * PRIV_STACK_GUARD_SZ; priv_stack_ptr = __alloc_percpu_gfp(priv_stack_alloc_sz, 16, GFP_KERNEL); - if (!priv_stack_ptr) { - prog = orig_prog; + if (!priv_stack_ptr) goto out_priv_stack; - } priv_stack_init_guard(priv_stack_ptr, priv_stack_alloc_sz); prog->aux->priv_stack_ptr = priv_stack_ptr; @@ -2079,10 +2059,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) ctx.prog = prog; ctx.offset = kvzalloc_objs(int, prog->len + 1); - if (ctx.offset == NULL) { - prog = orig_prog; + if (ctx.offset == NULL) goto out_off; - } ctx.user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena); ctx.arena_vm_start = bpf_arena_get_kern_vm_start(prog->aux->arena); @@ -2095,15 +2073,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) * BPF line info needs ctx->offset[i] to be the offset of * instruction[i] in jited image, so build prologue first. */ - if (build_prologue(&ctx, was_classic)) { - prog = orig_prog; + if (build_prologue(&ctx, was_classic)) goto out_off; - } - if (build_body(&ctx, extra_pass)) { - prog = orig_prog; + if (build_body(env, &ctx, extra_pass)) goto out_off; - } ctx.epilogue_offset = ctx.idx; build_epilogue(&ctx, was_classic); @@ -2121,10 +2095,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr, sizeof(u64), &header, &image_ptr, jit_fill_hole); - if (!ro_header) { - prog = orig_prog; + if (!ro_header) goto out_off; - } /* Pass 2: Determine jited position and result for each instruction */ @@ -2152,10 +2124,8 @@ skip_init_ctx: /* Dont write body instructions to memory for now */ ctx.write = false; - if (build_body(&ctx, extra_pass)) { - prog = orig_prog; + if (build_body(env, &ctx, extra_pass)) goto out_free_hdr; - } ctx.epilogue_offset = ctx.idx; ctx.exentry_idx = exentry_idx; @@ -2163,20 +2133,16 @@ skip_init_ctx: ctx.write = true; /* Pass 3: Adjust jump offset and write final image */ - if (build_body(&ctx, extra_pass) || - WARN_ON_ONCE(ctx.idx != ctx.epilogue_offset)) { - prog = orig_prog; + if (build_body(env, &ctx, extra_pass) || + WARN_ON_ONCE(ctx.idx != ctx.epilogue_offset)) goto out_free_hdr; - } build_epilogue(&ctx, was_classic); build_plt(&ctx); /* Extra pass to validate JITed code. */ - if (validate_ctx(&ctx)) { - prog = orig_prog; + if (validate_ctx(&ctx)) goto out_free_hdr; - } /* update the real prog size */ prog_size = sizeof(u32) * ctx.idx; @@ -2193,23 +2159,14 @@ skip_init_ctx: if (extra_pass && ctx.idx > jit_data->ctx.idx) { pr_err_once("multi-func JIT bug %d > %d\n", ctx.idx, jit_data->ctx.idx); - prog->bpf_func = NULL; - prog->jited = 0; - prog->jited_len = 0; goto out_free_hdr; } if (WARN_ON(bpf_jit_binary_pack_finalize(ro_header, header))) { - /* ro_header has been freed */ + /* ro_header and header has been freed */ ro_header = NULL; - prog = orig_prog; - goto out_off; + header = NULL; + goto out_free_hdr; } - /* - * The instructions have now been copied to the ROX region from - * where they will execute. Now the data cache has to be cleaned to - * the PoU and the I-cache has to be invalidated for the VAs. - */ - bpf_flush_icache(ro_header, ctx.ro_image + ctx.idx); } else { jit_data->ctx = ctx; jit_data->ro_image = ro_image_ptr; @@ -2245,13 +2202,15 @@ out_priv_stack: kfree(jit_data); prog->aux->jit_data = NULL; } -out: - if (tmp_blinded) - bpf_jit_prog_release_other(prog, prog == orig_prog ? - tmp : orig_prog); + return prog; out_free_hdr: + if (extra_pass) { + prog->bpf_func = NULL; + prog->jited = 0; + prog->jited_len = 0; + } if (header) { bpf_arch_text_copy(&ro_header->size, &header->size, sizeof(header->size)); diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index b7286d977788..811c2479e82d 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -106,6 +106,7 @@ WORKAROUND_2077057 WORKAROUND_2457168 WORKAROUND_2645198 WORKAROUND_2658417 +WORKAROUND_4193714 WORKAROUND_4311569 WORKAROUND_AMPERE_AC03_CPU_38 WORKAROUND_AMPERE_AC04_CPU_23 diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index 9d20ec6816d4..6c3ff14e561e 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -3259,6 +3259,14 @@ UnsignedEnum 3:0 ID_BITS EndEnum EndSysreg +Sysreg ICC_HPPIR_EL1 3 0 12 10 3 +Res0 63:33 +Field 32 HPPIV +Field 31:29 TYPE +Res0 28:24 +Field 23:0 ID +EndSysreg + Sysreg ICC_ICSR_EL1 3 0 12 10 4 Res0 63:48 Field 47:32 IAFFID @@ -3273,6 +3281,11 @@ Field 1 Enabled Field 0 F EndSysreg +Sysreg ICC_IAFFIDR_EL1 3 0 12 10 5 +Res0 63:16 +Field 15:0 IAFFID +EndSysreg + SysregFields ICC_PPI_ENABLERx_EL1 Field 63 EN63 Field 62 EN62 @@ -3683,6 +3696,42 @@ Res0 12 Field 11:0 AFFINITY EndSysreg +Sysreg ICC_APR_EL1 3 1 12 0 0 +Res0 63:32 +Field 31 P31 +Field 30 P30 +Field 29 P29 +Field 28 P28 +Field 27 P27 +Field 26 P26 +Field 25 P25 +Field 24 P24 +Field 23 P23 +Field 22 P22 +Field 21 P21 +Field 20 P20 +Field 19 P19 +Field 18 P18 +Field 17 P17 +Field 16 P16 +Field 15 P15 +Field 14 P14 +Field 13 P13 +Field 12 P12 +Field 11 P11 +Field 10 P10 +Field 9 P9 +Field 8 P8 +Field 7 P7 +Field 6 P6 +Field 5 P5 +Field 4 P4 +Field 3 P3 +Field 2 P2 +Field 1 P1 +Field 0 P0 +EndSysreg + Sysreg ICC_CR0_EL1 3 1 12 0 1 Res0 63:39 Field 38 PID @@ -4707,6 +4756,42 @@ Field 31:16 PhyPARTID29 Field 15:0 PhyPARTID28 EndSysreg +Sysreg ICH_APR_EL2 3 4 12 8 4 +Res0 63:32 +Field 31 P31 +Field 30 P30 +Field 29 P29 +Field 28 P28 +Field 27 P27 +Field 26 P26 +Field 25 P25 +Field 24 P24 +Field 23 P23 +Field 22 P22 +Field 21 P21 +Field 20 P20 +Field 19 P19 +Field 18 P18 +Field 17 P17 +Field 16 P16 +Field 15 P15 +Field 14 P14 +Field 13 P13 +Field 12 P12 +Field 11 P11 +Field 10 P10 +Field 9 P9 +Field 8 P8 +Field 7 P7 +Field 6 P6 +Field 5 P5 +Field 4 P4 +Field 3 P3 +Field 2 P2 +Field 1 P1 +Field 0 P0 +EndSysreg + Sysreg ICH_HFGRTR_EL2 3 4 12 9 4 Res0 63:21 Field 20 ICC_PPI_ACTIVERn_EL1 @@ -4755,6 +4840,306 @@ Field 1 GICCDDIS Field 0 GICCDEN EndSysreg +SysregFields ICH_PPI_DVIRx_EL2 +Field 63 DVI63 +Field 62 DVI62 +Field 61 DVI61 +Field 60 DVI60 +Field 59 DVI59 +Field 58 DVI58 +Field 57 DVI57 +Field 56 DVI56 +Field 55 DVI55 +Field 54 DVI54 +Field 53 DVI53 +Field 52 DVI52 +Field 51 DVI51 +Field 50 DVI50 +Field 49 DVI49 +Field 48 DVI48 +Field 47 DVI47 +Field 46 DVI46 +Field 45 DVI45 +Field 44 DVI44 +Field 43 DVI43 +Field 42 DVI42 +Field 41 DVI41 +Field 40 DVI40 +Field 39 DVI39 +Field 38 DVI38 +Field 37 DVI37 +Field 36 DVI36 +Field 35 DVI35 +Field 34 DVI34 +Field 33 DVI33 +Field 32 DVI32 +Field 31 DVI31 +Field 30 DVI30 +Field 29 DVI29 +Field 28 DVI28 +Field 27 DVI27 +Field 26 DVI26 +Field 25 DVI25 +Field 24 DVI24 +Field 23 DVI23 +Field 22 DVI22 +Field 21 DVI21 +Field 20 DVI20 +Field 19 DVI19 +Field 18 DVI18 +Field 17 DVI17 +Field 16 DVI16 +Field 15 DVI15 +Field 14 DVI14 +Field 13 DVI13 +Field 12 DVI12 +Field 11 DVI11 +Field 10 DVI10 +Field 9 DVI9 +Field 8 DVI8 +Field 7 DVI7 +Field 6 DVI6 +Field 5 DVI5 +Field 4 DVI4 +Field 3 DVI3 +Field 2 DVI2 +Field 1 DVI1 +Field 0 DVI0 +EndSysregFields + +Sysreg ICH_PPI_DVIR0_EL2 3 4 12 10 0 +Fields ICH_PPI_DVIRx_EL2 +EndSysreg + +Sysreg ICH_PPI_DVIR1_EL2 3 4 12 10 1 +Fields ICH_PPI_DVIRx_EL2 +EndSysreg + +SysregFields ICH_PPI_ENABLERx_EL2 +Field 63 EN63 +Field 62 EN62 +Field 61 EN61 +Field 60 EN60 +Field 59 EN59 +Field 58 EN58 +Field 57 EN57 +Field 56 EN56 +Field 55 EN55 +Field 54 EN54 +Field 53 EN53 +Field 52 EN52 +Field 51 EN51 +Field 50 EN50 +Field 49 EN49 +Field 48 EN48 +Field 47 EN47 +Field 46 EN46 +Field 45 EN45 +Field 44 EN44 +Field 43 EN43 +Field 42 EN42 +Field 41 EN41 +Field 40 EN40 +Field 39 EN39 +Field 38 EN38 +Field 37 EN37 +Field 36 EN36 +Field 35 EN35 +Field 34 EN34 +Field 33 EN33 +Field 32 EN32 +Field 31 EN31 +Field 30 EN30 +Field 29 EN29 +Field 28 EN28 +Field 27 EN27 +Field 26 EN26 +Field 25 EN25 +Field 24 EN24 +Field 23 EN23 +Field 22 EN22 +Field 21 EN21 +Field 20 EN20 +Field 19 EN19 +Field 18 EN18 +Field 17 EN17 +Field 16 EN16 +Field 15 EN15 +Field 14 EN14 +Field 13 EN13 +Field 12 EN12 +Field 11 EN11 +Field 10 EN10 +Field 9 EN9 +Field 8 EN8 +Field 7 EN7 +Field 6 EN6 +Field 5 EN5 +Field 4 EN4 +Field 3 EN3 +Field 2 EN2 +Field 1 EN1 +Field 0 EN0 +EndSysregFields + +Sysreg ICH_PPI_ENABLER0_EL2 3 4 12 10 2 +Fields ICH_PPI_ENABLERx_EL2 +EndSysreg + +Sysreg ICH_PPI_ENABLER1_EL2 3 4 12 10 3 +Fields ICH_PPI_ENABLERx_EL2 +EndSysreg + +SysregFields ICH_PPI_PENDRx_EL2 +Field 63 PEND63 +Field 62 PEND62 +Field 61 PEND61 +Field 60 PEND60 +Field 59 PEND59 +Field 58 PEND58 +Field 57 PEND57 +Field 56 PEND56 +Field 55 PEND55 +Field 54 PEND54 +Field 53 PEND53 +Field 52 PEND52 +Field 51 PEND51 +Field 50 PEND50 +Field 49 PEND49 +Field 48 PEND48 +Field 47 PEND47 +Field 46 PEND46 +Field 45 PEND45 +Field 44 PEND44 +Field 43 PEND43 +Field 42 PEND42 +Field 41 PEND41 +Field 40 PEND40 +Field 39 PEND39 +Field 38 PEND38 +Field 37 PEND37 +Field 36 PEND36 +Field 35 PEND35 +Field 34 PEND34 +Field 33 PEND33 +Field 32 PEND32 +Field 31 PEND31 +Field 30 PEND30 +Field 29 PEND29 +Field 28 PEND28 +Field 27 PEND27 +Field 26 PEND26 +Field 25 PEND25 +Field 24 PEND24 +Field 23 PEND23 +Field 22 PEND22 +Field 21 PEND21 +Field 20 PEND20 +Field 19 PEND19 +Field 18 PEND18 +Field 17 PEND17 +Field 16 PEND16 +Field 15 PEND15 +Field 14 PEND14 +Field 13 PEND13 +Field 12 PEND12 +Field 11 PEND11 +Field 10 PEND10 +Field 9 PEND9 +Field 8 PEND8 +Field 7 PEND7 +Field 6 PEND6 +Field 5 PEND5 +Field 4 PEND4 +Field 3 PEND3 +Field 2 PEND2 +Field 1 PEND1 +Field 0 PEND0 +EndSysregFields + +Sysreg ICH_PPI_PENDR0_EL2 3 4 12 10 4 +Fields ICH_PPI_PENDRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PENDR1_EL2 3 4 12 10 5 +Fields ICH_PPI_PENDRx_EL2 +EndSysreg + +SysregFields ICH_PPI_ACTIVERx_EL2 +Field 63 ACTIVE63 +Field 62 ACTIVE62 +Field 61 ACTIVE61 +Field 60 ACTIVE60 +Field 59 ACTIVE59 +Field 58 ACTIVE58 +Field 57 ACTIVE57 +Field 56 ACTIVE56 +Field 55 ACTIVE55 +Field 54 ACTIVE54 +Field 53 ACTIVE53 +Field 52 ACTIVE52 +Field 51 ACTIVE51 +Field 50 ACTIVE50 +Field 49 ACTIVE49 +Field 48 ACTIVE48 +Field 47 ACTIVE47 +Field 46 ACTIVE46 +Field 45 ACTIVE45 +Field 44 ACTIVE44 +Field 43 ACTIVE43 +Field 42 ACTIVE42 +Field 41 ACTIVE41 +Field 40 ACTIVE40 +Field 39 ACTIVE39 +Field 38 ACTIVE38 +Field 37 ACTIVE37 +Field 36 ACTIVE36 +Field 35 ACTIVE35 +Field 34 ACTIVE34 +Field 33 ACTIVE33 +Field 32 ACTIVE32 +Field 31 ACTIVE31 +Field 30 ACTIVE30 +Field 29 ACTIVE29 +Field 28 ACTIVE28 +Field 27 ACTIVE27 +Field 26 ACTIVE26 +Field 25 ACTIVE25 +Field 24 ACTIVE24 +Field 23 ACTIVE23 +Field 22 ACTIVE22 +Field 21 ACTIVE21 +Field 20 ACTIVE20 +Field 19 ACTIVE19 +Field 18 ACTIVE18 +Field 17 ACTIVE17 +Field 16 ACTIVE16 +Field 15 ACTIVE15 +Field 14 ACTIVE14 +Field 13 ACTIVE13 +Field 12 ACTIVE12 +Field 11 ACTIVE11 +Field 10 ACTIVE10 +Field 9 ACTIVE9 +Field 8 ACTIVE8 +Field 7 ACTIVE7 +Field 6 ACTIVE6 +Field 5 ACTIVE5 +Field 4 ACTIVE4 +Field 3 ACTIVE3 +Field 2 ACTIVE2 +Field 1 ACTIVE1 +Field 0 ACTIVE0 +EndSysregFields + +Sysreg ICH_PPI_ACTIVER0_EL2 3 4 12 10 6 +Fields ICH_PPI_ACTIVERx_EL2 +EndSysreg + +Sysreg ICH_PPI_ACTIVER1_EL2 3 4 12 10 7 +Fields ICH_PPI_ACTIVERx_EL2 +EndSysreg + Sysreg ICH_HCR_EL2 3 4 12 11 0 Res0 63:32 Field 31:27 EOIcount @@ -4809,6 +5194,18 @@ Field 1 V3 Field 0 En EndSysreg +Sysreg ICH_CONTEXTR_EL2 3 4 12 11 6 +Field 63 V +Field 62 F +Field 61 IRICHPPIDIS +Field 60 DB +Field 59:55 DBPM +Res0 54:48 +Field 47:32 VPE +Res0 31:16 +Field 15:0 VM +EndSysreg + Sysreg ICH_VMCR_EL2 3 4 12 11 7 Prefix FEAT_GCIE Res0 63:32 @@ -4830,6 +5227,89 @@ Field 1 VENG1 Field 0 VENG0 EndSysreg +SysregFields ICH_PPI_PRIORITYRx_EL2 +Res0 63:61 +Field 60:56 Priority7 +Res0 55:53 +Field 52:48 Priority6 +Res0 47:45 +Field 44:40 Priority5 +Res0 39:37 +Field 36:32 Priority4 +Res0 31:29 +Field 28:24 Priority3 +Res0 23:21 +Field 20:16 Priority2 +Res0 15:13 +Field 12:8 Priority1 +Res0 7:5 +Field 4:0 Priority0 +EndSysregFields + +Sysreg ICH_PPI_PRIORITYR0_EL2 3 4 12 14 0 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR1_EL2 3 4 12 14 1 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR2_EL2 3 4 12 14 2 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR3_EL2 3 4 12 14 3 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR4_EL2 3 4 12 14 4 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR5_EL2 3 4 12 14 5 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR6_EL2 3 4 12 14 6 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR7_EL2 3 4 12 14 7 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR8_EL2 3 4 12 15 0 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR9_EL2 3 4 12 15 1 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR10_EL2 3 4 12 15 2 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR11_EL2 3 4 12 15 3 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR12_EL2 3 4 12 15 4 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR13_EL2 3 4 12 15 5 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR14_EL2 3 4 12 15 6 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + +Sysreg ICH_PPI_PRIORITYR15_EL2 3 4 12 15 7 +Fields ICH_PPI_PRIORITYRx_EL2 +EndSysreg + Sysreg CONTEXTIDR_EL2 3 4 13 0 1 Fields CONTEXTIDR_ELx EndSysreg diff --git a/arch/loongarch/Kbuild b/arch/loongarch/Kbuild index beb8499dd8ed..1c7a0dbe5e72 100644 --- a/arch/loongarch/Kbuild +++ b/arch/loongarch/Kbuild @@ -3,7 +3,7 @@ obj-y += mm/ obj-y += net/ obj-y += vdso/ -obj-$(CONFIG_KVM) += kvm/ +obj-$(subst m,y,$(CONFIG_KVM)) += kvm/ # for cleaning subdir- += boot diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index ac714d14133a..606597da46b8 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -20,11 +20,11 @@ config LOONGARCH select ARCH_HAS_FAST_MULTIPLIER select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_KCOV - select ARCH_HAS_KERNEL_FPU_SUPPORT if CPU_HAS_FPU + select ARCH_HAS_KERNEL_FPU_SUPPORT if 64BIT && CPU_HAS_FPU select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE select ARCH_HAS_PREEMPT_LAZY - select ARCH_HAS_PTE_SPECIAL + select ARCH_HAS_PTE_SPECIAL if 64BIT select ARCH_HAS_SET_MEMORY select ARCH_HAS_SET_DIRECT_MAP select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST @@ -59,16 +59,15 @@ config LOONGARCH select ARCH_KEEP_MEMBLOCK select ARCH_MIGHT_HAVE_PC_PARPORT select ARCH_MIGHT_HAVE_PC_SERIO - select ARCH_SPARSEMEM_ENABLE select ARCH_STACKWALK select ARCH_SUPPORTS_ACPI select ARCH_SUPPORTS_ATOMIC_RMW - select ARCH_SUPPORTS_HUGETLBFS + select ARCH_SUPPORTS_HUGETLBFS if 64BIT select ARCH_SUPPORTS_INT128 if CC_HAS_INT128 select ARCH_SUPPORTS_LTO_CLANG select ARCH_SUPPORTS_LTO_CLANG_THIN select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS - select ARCH_SUPPORTS_NUMA_BALANCING + select ARCH_SUPPORTS_NUMA_BALANCING if NUMA select ARCH_SUPPORTS_PER_VMA_LOCK select ARCH_SUPPORTS_RT select ARCH_SUPPORTS_SCHED_SMT if SMP @@ -78,10 +77,10 @@ config LOONGARCH select ARCH_USE_MEMTEST select ARCH_USE_QUEUED_RWLOCKS select ARCH_USE_QUEUED_SPINLOCKS - select ARCH_WANT_DEFAULT_BPF_JIT + select ARCH_WANT_DEFAULT_BPF_JIT if HAVE_EBPF_JIT select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT select ARCH_WANT_LD_ORPHAN_WARN - select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP + select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP if 64BIT select ARCH_WANTS_NO_INSTR select ARCH_WANTS_THP_SWAP if HAVE_ARCH_TRANSPARENT_HUGEPAGE select BUILDTIME_TABLE_SORT @@ -89,13 +88,14 @@ config LOONGARCH select CPU_PM select EDAC_SUPPORT select EFI + select GENERIC_ATOMIC64 if 32BIT select GENERIC_CLOCKEVENTS select GENERIC_CMOS_UPDATE select GENERIC_CPU_AUTOPROBE select GENERIC_CPU_DEVICES select GENERIC_CPU_VULNERABILITIES select GENERIC_ENTRY - select GENERIC_GETTIMEOFDAY + select GENERIC_GETTIMEOFDAY if 64BIT select GENERIC_IOREMAP if !ARCH_IOREMAP select GENERIC_IRQ_MATRIX_ALLOCATOR select GENERIC_IRQ_MULTI_HANDLER @@ -110,16 +110,16 @@ config LOONGARCH select GENERIC_PCI_IOMAP select GENERIC_SCHED_CLOCK select GENERIC_SMP_IDLE_THREAD - select GENERIC_TIME_VSYSCALL + select GENERIC_TIME_VSYSCALL if GENERIC_GETTIMEOFDAY select GPIOLIB select HAS_IOPORT - select HAVE_ALIGNED_STRUCT_PAGE + select HAVE_ALIGNED_STRUCT_PAGE if 64BIT select HAVE_ARCH_AUDITSYSCALL - select HAVE_ARCH_BITREVERSE + select HAVE_ARCH_BITREVERSE if 64BIT select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_JUMP_LABEL_RELATIVE - select HAVE_ARCH_KASAN - select HAVE_ARCH_KFENCE + select HAVE_ARCH_KASAN if 64BIT + select HAVE_ARCH_KFENCE if 64BIT select HAVE_ARCH_KGDB if PERF_EVENTS select HAVE_ARCH_KSTACK_ERASE select HAVE_ARCH_MMAP_RND_BITS if MMU @@ -127,8 +127,8 @@ config LOONGARCH select HAVE_ARCH_SECCOMP select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_TRACEHOOK - select HAVE_ARCH_TRANSPARENT_HUGEPAGE - select HAVE_ARCH_USERFAULTFD_MINOR if USERFAULTFD + select HAVE_ARCH_TRANSPARENT_HUGEPAGE if 64BIT + select HAVE_ARCH_USERFAULTFD_MINOR if 64BIT && USERFAULTFD select HAVE_ASM_MODVERSIONS select HAVE_CMPXCHG_DOUBLE select HAVE_CMPXCHG_LOCAL @@ -142,7 +142,7 @@ config LOONGARCH select HAVE_FTRACE_REGS_HAVING_PT_REGS select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS select HAVE_DYNAMIC_FTRACE_WITH_REGS - select HAVE_EBPF_JIT + select HAVE_EBPF_JIT if 64BIT select HAVE_EFFICIENT_UNALIGNED_ACCESS if !ARCH_STRICT_ALIGN select HAVE_EXIT_THREAD select HAVE_GENERIC_TIF_BITS @@ -165,9 +165,9 @@ config LOONGARCH select HAVE_LIVEPATCH select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI - select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS && AS_HAS_THIN_ADD_SUB + select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS && AS_HAS_THIN_ADD_SUB && 64BIT select HAVE_PCI - select HAVE_PERF_EVENTS + select HAVE_PERF_EVENTS if 64BIT select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP select HAVE_POSIX_CPU_TIMERS_TASK_WORK @@ -209,18 +209,51 @@ config LOONGARCH select SYSCTL_ARCH_UNALIGN_ALLOW select SYSCTL_ARCH_UNALIGN_NO_WARN select SYSCTL_EXCEPTION_TRACE - select SWIOTLB + select SWIOTLB if 64BIT select TRACE_IRQFLAGS_SUPPORT select USE_PERCPU_NUMA_NODE_ID select USER_STACKTRACE_SUPPORT select VDSO_GETRANDOM - select ZONE_DMA32 + select ZONE_DMA32 if 64BIT + +menu "Kernel type and options" + +choice + prompt "Kernel type" + default 64BIT # Keep existing behavior config 32BIT - bool + bool "32-bit kernel" + help + Select this option if you want to build a 32-bit kernel. config 64BIT - def_bool y + bool "64-bit kernel" + help + Select this option if you want to build a 64-bit kernel. + +endchoice + +if 32BIT + +choice + prompt "32-bit kernel sub-type" + +config 32BIT_REDUCED + bool "32-bit kernel for LA32R" + help + Select this option if you want to build a 32-bit kernel for + LoongArch32 Reduced (LA32R). + +config 32BIT_STANDARD + bool "32-bit kernel for LA32S" + help + Select this option if you want to build a 32-bit kernel for + LoongArch32 Standard (LA32S). + +endchoice + +endif config GENERIC_BUG def_bool y @@ -313,10 +346,13 @@ config RUSTC_HAS_ANNOTATE_TABLEJUMP depends on RUST def_bool $(rustc-option,-Cllvm-args=--loongarch-annotate-tablejump) -menu "Kernel type and options" - source "kernel/Kconfig.hz" +config HIGHMEM + bool "High Memory Support" + depends on 32BIT + select KMAP_LOCAL + choice prompt "Page Table Layout" default 16KB_2LEVEL if 32BIT @@ -326,8 +362,17 @@ choice of page size and page table levels. The size of virtual memory address space are determined by the page table layout. +config 4KB_2LEVEL + bool "4KB with 2 levels" + select HAVE_PAGE_SIZE_4KB + select PGTABLE_2LEVEL + help + This option selects 4KB page size with 2 level page tables, which + support a maximum of 32 bits of application virtual memory. + config 4KB_3LEVEL bool "4KB with 3 levels" + depends on 64BIT select HAVE_PAGE_SIZE_4KB select PGTABLE_3LEVEL help @@ -336,6 +381,7 @@ config 4KB_3LEVEL config 4KB_4LEVEL bool "4KB with 4 levels" + depends on 64BIT select HAVE_PAGE_SIZE_4KB select PGTABLE_4LEVEL help @@ -352,6 +398,7 @@ config 16KB_2LEVEL config 16KB_3LEVEL bool "16KB with 3 levels" + depends on 64BIT select HAVE_PAGE_SIZE_16KB select PGTABLE_3LEVEL help @@ -368,6 +415,7 @@ config 64KB_2LEVEL config 64KB_3LEVEL bool "64KB with 3 levels" + depends on 64BIT select HAVE_PAGE_SIZE_64KB select PGTABLE_3LEVEL help @@ -465,6 +513,7 @@ config EFI_STUB config SMP bool "Multi-Processing support" + depends on 64BIT help This enables support for systems with more than one CPU. If you have a system with only one CPU, say N. If you have a system with more @@ -503,6 +552,7 @@ config NR_CPUS config NUMA bool "NUMA Support" select SMP + depends on 64BIT help Say Y to compile the kernel with NUMA (Non-Uniform Memory Access) support. This option improves performance on systems with more @@ -585,7 +635,7 @@ config CPU_HAS_FPU config CPU_HAS_LSX bool "Support for the Loongson SIMD Extension" - depends on AS_HAS_LSX_EXTENSION + depends on AS_HAS_LSX_EXTENSION && 64BIT help Loongson SIMD Extension (LSX) introduces 128 bit wide vector registers and a set of SIMD instructions to operate on them. When this option @@ -600,7 +650,7 @@ config CPU_HAS_LSX config CPU_HAS_LASX bool "Support for the Loongson Advanced SIMD Extension" depends on CPU_HAS_LSX - depends on AS_HAS_LASX_EXTENSION + depends on AS_HAS_LASX_EXTENSION && 64BIT help Loongson Advanced SIMD Extension (LASX) introduces 256 bit wide vector registers and a set of SIMD instructions to operate on them. When this @@ -614,7 +664,7 @@ config CPU_HAS_LASX config CPU_HAS_LBT bool "Support for the Loongson Binary Translation Extension" - depends on AS_HAS_LBT_EXTENSION + depends on AS_HAS_LBT_EXTENSION && 64BIT help Loongson Binary Translation (LBT) introduces 4 scratch registers (SCR0 to SCR3), x86/ARM eflags (eflags) and x87 fpu stack pointer (ftop). @@ -642,13 +692,13 @@ config ARCH_SELECTS_KEXEC_FILE select HAVE_IMA_KEXEC if IMA config ARCH_SUPPORTS_CRASH_DUMP - def_bool y + def_bool 64BIT config ARCH_DEFAULT_CRASH_DUMP - def_bool y + def_bool 64BIT config ARCH_SELECTS_CRASH_DUMP - def_bool y + def_bool 64BIT depends on CRASH_DUMP select RELOCATABLE @@ -657,6 +707,7 @@ config ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION config RELOCATABLE bool "Relocatable kernel" + depends on 64BIT select ARCH_HAS_RELR help This builds the kernel as a Position Independent Executable (PIE), @@ -693,7 +744,7 @@ source "kernel/livepatch/Kconfig" config PARAVIRT bool "Enable paravirtualization code" - depends on AS_HAS_LVZ_EXTENSION + depends on AS_HAS_LVZ_EXTENSION && 64BIT select HAVE_PV_STEAL_CLOCK_GEN help This changes the kernel so it can modify itself when it is run @@ -722,7 +773,7 @@ config ARCH_FLATMEM_ENABLE depends on !NUMA config ARCH_SPARSEMEM_ENABLE - def_bool y + def_bool 64BIT select SPARSEMEM_VMEMMAP_ENABLE help Say Y to support efficient handling of sparse physical memory, @@ -739,10 +790,12 @@ config MMU default y config ARCH_MMAP_RND_BITS_MIN - default 12 + default 10 if 32BIT + default 12 if 64BIT config ARCH_MMAP_RND_BITS_MAX - default 18 + default 15 if 32BIT + default 20 if 64BIT config ARCH_SUPPORTS_UPROBES def_bool y diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index 8d45b860fe56..54fcfa1eac1f 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -25,6 +25,7 @@ endif # # Select the object file format to substitute into the linker script. # +32bit-tool-archpref = loongarch32 64bit-tool-archpref = loongarch64 32bit-bfd = elf32-loongarch 64bit-bfd = elf64-loongarch @@ -51,9 +52,14 @@ KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY CC_FLAGS_FTRACE := -fpatchable-function-entry=2 endif -ifdef CONFIG_64BIT +ifdef CONFIG_32BIT +tool-archpref = $(32bit-tool-archpref) +UTS_MACHINE := loongarch32 +cflags-y += $(call cc-option,-m32) +else tool-archpref = $(64bit-tool-archpref) UTS_MACHINE := loongarch64 +cflags-y += $(call cc-option,-m64) endif ifneq ($(SUBARCH),$(ARCH)) @@ -62,9 +68,19 @@ ifneq ($(SUBARCH),$(ARCH)) endif endif +ifdef CONFIG_32BIT +ifdef CONFIG_32BIT_STANDARD +ld-emul = $(32bit-emul) +cflags-y += -march=la32v1.0 -mabi=ilp32s -mcmodel=normal +else # CONFIG_32BIT_REDUCED +ld-emul = $(32bit-emul) +cflags-y += -march=la32rv1.0 -mabi=ilp32s -mcmodel=normal +endif +endif + ifdef CONFIG_64BIT ld-emul = $(64bit-emul) -cflags-y += -mabi=lp64s -mcmodel=normal +cflags-y += -march=loongarch64 -mabi=lp64s -mcmodel=normal endif cflags-y += -pipe $(CC_FLAGS_NO_FPU) @@ -140,7 +156,12 @@ ifndef CONFIG_KASAN cflags-y += -fno-builtin-memcpy -fno-builtin-memmove -fno-builtin-memset endif +ifdef CONFIG_32BIT +load-y = 0xa0200000 +else load-y = 0x9000000000200000 +endif + bootvars-y = VMLINUX_LOAD_ADDRESS=$(load-y) drivers-$(CONFIG_PCI) += arch/loongarch/pci/ diff --git a/arch/loongarch/boot/Makefile b/arch/loongarch/boot/Makefile index 4e1c374c5782..8b6d9b42b5f0 100644 --- a/arch/loongarch/boot/Makefile +++ b/arch/loongarch/boot/Makefile @@ -20,7 +20,13 @@ $(obj)/vmlinux.efi: vmlinux FORCE $(call if_changed,objcopy) EFI_ZBOOT_PAYLOAD := vmlinux.efi + +ifdef CONFIG_32BIT +EFI_ZBOOT_BFD_TARGET := elf32-loongarch +EFI_ZBOOT_MACH_TYPE := LOONGARCH32 +else EFI_ZBOOT_BFD_TARGET := elf64-loongarch EFI_ZBOOT_MACH_TYPE := LOONGARCH64 +endif include $(srctree)/drivers/firmware/efi/libstub/Makefile.zboot diff --git a/arch/loongarch/include/asm/asm-prototypes.h b/arch/loongarch/include/asm/asm-prototypes.h index 704066b4f736..de0c17f3f49c 100644 --- a/arch/loongarch/include/asm/asm-prototypes.h +++ b/arch/loongarch/include/asm/asm-prototypes.h @@ -20,3 +20,23 @@ asmlinkage void noinstr __no_stack_protector ret_from_kernel_thread(struct task_ struct pt_regs *regs, int (*fn)(void *), void *fn_arg); + +struct kvm_run; +struct kvm_vcpu; +struct loongarch_fpu; + +void kvm_exc_entry(void); +int kvm_enter_guest(struct kvm_run *run, struct kvm_vcpu *vcpu); + +void kvm_save_fpu(struct loongarch_fpu *fpu); +void kvm_restore_fpu(struct loongarch_fpu *fpu); + +#ifdef CONFIG_CPU_HAS_LSX +void kvm_save_lsx(struct loongarch_fpu *fpu); +void kvm_restore_lsx(struct loongarch_fpu *fpu); +#endif + +#ifdef CONFIG_CPU_HAS_LASX +void kvm_save_lasx(struct loongarch_fpu *fpu); +void kvm_restore_lasx(struct loongarch_fpu *fpu); +#endif diff --git a/arch/loongarch/include/asm/cacheflush.h b/arch/loongarch/include/asm/cacheflush.h index f8754d08a31a..190651be9546 100644 --- a/arch/loongarch/include/asm/cacheflush.h +++ b/arch/loongarch/include/asm/cacheflush.h @@ -32,8 +32,22 @@ static inline unsigned int cpu_last_level_cache_line_size(void) } asmlinkage void __flush_cache_all(void); -void local_flush_icache_range(unsigned long start, unsigned long end); +/* + * LoongArch maintains ICache/DCache coherency by hardware, + * we just need "ibar" to avoid instruction hazard here. + */ +static inline void local_flush_icache_all(void) +{ + asm volatile ("ibar\t0\n"::); +} + +static inline void local_flush_icache_range(unsigned long start, unsigned long end) +{ + asm volatile ("ibar\t0\n"::); +} + +#define flush_icache_all local_flush_icache_all #define flush_icache_range local_flush_icache_range #define flush_icache_user_range local_flush_icache_range diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h index 8eefe7a2098b..62059c5551b9 100644 --- a/arch/loongarch/include/asm/cpu-features.h +++ b/arch/loongarch/include/asm/cpu-features.h @@ -35,6 +35,7 @@ */ #define cpu_has_cpucfg cpu_opt(LOONGARCH_CPU_CPUCFG) #define cpu_has_lam cpu_opt(LOONGARCH_CPU_LAM) +#define cpu_has_lam_bh cpu_opt(LOONGARCH_CPU_LAM_BH) #define cpu_has_scq cpu_opt(LOONGARCH_CPU_SCQ) #define cpu_has_ual cpu_opt(LOONGARCH_CPU_UAL) #define cpu_has_fpu cpu_opt(LOONGARCH_CPU_FPU) diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h index 1e60ab264cd0..91b96938861e 100644 --- a/arch/loongarch/include/asm/cpu.h +++ b/arch/loongarch/include/asm/cpu.h @@ -95,40 +95,42 @@ static inline char *id_to_core_name(unsigned int id) */ #define CPU_FEATURE_CPUCFG 0 /* CPU has CPUCFG */ #define CPU_FEATURE_LAM 1 /* CPU has Atomic instructions */ -#define CPU_FEATURE_SCQ 2 /* CPU has SC.Q instruction */ -#define CPU_FEATURE_UAL 3 /* CPU supports unaligned access */ -#define CPU_FEATURE_FPU 4 /* CPU has FPU */ -#define CPU_FEATURE_LSX 5 /* CPU has LSX (128-bit SIMD) */ -#define CPU_FEATURE_LASX 6 /* CPU has LASX (256-bit SIMD) */ -#define CPU_FEATURE_CRC32 7 /* CPU has CRC32 instructions */ -#define CPU_FEATURE_COMPLEX 8 /* CPU has Complex instructions */ -#define CPU_FEATURE_CRYPTO 9 /* CPU has Crypto instructions */ -#define CPU_FEATURE_LVZ 10 /* CPU has Virtualization extension */ -#define CPU_FEATURE_LBT_X86 11 /* CPU has X86 Binary Translation */ -#define CPU_FEATURE_LBT_ARM 12 /* CPU has ARM Binary Translation */ -#define CPU_FEATURE_LBT_MIPS 13 /* CPU has MIPS Binary Translation */ -#define CPU_FEATURE_TLB 14 /* CPU has TLB */ -#define CPU_FEATURE_CSR 15 /* CPU has CSR */ -#define CPU_FEATURE_IOCSR 16 /* CPU has IOCSR */ -#define CPU_FEATURE_WATCH 17 /* CPU has watchpoint registers */ -#define CPU_FEATURE_VINT 18 /* CPU has vectored interrupts */ -#define CPU_FEATURE_CSRIPI 19 /* CPU has CSR-IPI */ -#define CPU_FEATURE_EXTIOI 20 /* CPU has EXT-IOI */ -#define CPU_FEATURE_PREFETCH 21 /* CPU has prefetch instructions */ -#define CPU_FEATURE_PMP 22 /* CPU has perfermance counter */ -#define CPU_FEATURE_SCALEFREQ 23 /* CPU supports cpufreq scaling */ -#define CPU_FEATURE_FLATMODE 24 /* CPU has flat mode */ -#define CPU_FEATURE_EIODECODE 25 /* CPU has EXTIOI interrupt pin decode mode */ -#define CPU_FEATURE_GUESTID 26 /* CPU has GuestID feature */ -#define CPU_FEATURE_HYPERVISOR 27 /* CPU has hypervisor (running in VM) */ -#define CPU_FEATURE_PTW 28 /* CPU has hardware page table walker */ -#define CPU_FEATURE_LSPW 29 /* CPU has LSPW (lddir/ldpte instructions) */ -#define CPU_FEATURE_MSGINT 30 /* CPU has MSG interrupt */ -#define CPU_FEATURE_AVECINT 31 /* CPU has AVEC interrupt */ -#define CPU_FEATURE_REDIRECTINT 32 /* CPU has interrupt remapping */ +#define CPU_FEATURE_LAM_BH 2 /* CPU has AM{SWAP/ADD}[_DB].{B/H} instructions */ +#define CPU_FEATURE_SCQ 3 /* CPU has SC.Q instruction */ +#define CPU_FEATURE_UAL 4 /* CPU supports unaligned access */ +#define CPU_FEATURE_FPU 5 /* CPU has FPU */ +#define CPU_FEATURE_LSX 6 /* CPU has LSX (128-bit SIMD) */ +#define CPU_FEATURE_LASX 7 /* CPU has LASX (256-bit SIMD) */ +#define CPU_FEATURE_CRC32 8 /* CPU has CRC32 instructions */ +#define CPU_FEATURE_COMPLEX 9 /* CPU has Complex instructions */ +#define CPU_FEATURE_CRYPTO 10 /* CPU has Crypto instructions */ +#define CPU_FEATURE_LVZ 11 /* CPU has Virtualization extension */ +#define CPU_FEATURE_LBT_X86 12 /* CPU has X86 Binary Translation */ +#define CPU_FEATURE_LBT_ARM 13 /* CPU has ARM Binary Translation */ +#define CPU_FEATURE_LBT_MIPS 14 /* CPU has MIPS Binary Translation */ +#define CPU_FEATURE_TLB 15 /* CPU has TLB */ +#define CPU_FEATURE_CSR 16 /* CPU has CSR */ +#define CPU_FEATURE_IOCSR 17 /* CPU has IOCSR */ +#define CPU_FEATURE_WATCH 18 /* CPU has watchpoint registers */ +#define CPU_FEATURE_VINT 19 /* CPU has vectored interrupts */ +#define CPU_FEATURE_CSRIPI 20 /* CPU has CSR-IPI */ +#define CPU_FEATURE_EXTIOI 21 /* CPU has EXT-IOI */ +#define CPU_FEATURE_PREFETCH 22 /* CPU has prefetch instructions */ +#define CPU_FEATURE_PMP 23 /* CPU has perfermance counter */ +#define CPU_FEATURE_SCALEFREQ 24 /* CPU supports cpufreq scaling */ +#define CPU_FEATURE_FLATMODE 25 /* CPU has flat mode */ +#define CPU_FEATURE_EIODECODE 26 /* CPU has EXTIOI interrupt pin decode mode */ +#define CPU_FEATURE_GUESTID 27 /* CPU has GuestID feature */ +#define CPU_FEATURE_HYPERVISOR 28 /* CPU has hypervisor (running in VM) */ +#define CPU_FEATURE_PTW 29 /* CPU has hardware page table walker */ +#define CPU_FEATURE_LSPW 30 /* CPU has LSPW (lddir/ldpte instructions) */ +#define CPU_FEATURE_MSGINT 31 /* CPU has MSG interrupt */ +#define CPU_FEATURE_AVECINT 32 /* CPU has AVEC interrupt */ +#define CPU_FEATURE_REDIRECTINT 33 /* CPU has interrupt remapping */ #define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG) #define LOONGARCH_CPU_LAM BIT_ULL(CPU_FEATURE_LAM) +#define LOONGARCH_CPU_LAM_BH BIT_ULL(CPU_FEATURE_LAM_BH) #define LOONGARCH_CPU_SCQ BIT_ULL(CPU_FEATURE_SCQ) #define LOONGARCH_CPU_UAL BIT_ULL(CPU_FEATURE_UAL) #define LOONGARCH_CPU_FPU BIT_ULL(CPU_FEATURE_FPU) diff --git a/arch/loongarch/include/asm/fixmap.h b/arch/loongarch/include/asm/fixmap.h index d2e55ae55bb9..dce2da6ba787 100644 --- a/arch/loongarch/include/asm/fixmap.h +++ b/arch/loongarch/include/asm/fixmap.h @@ -8,10 +8,19 @@ #ifndef _ASM_FIXMAP_H #define _ASM_FIXMAP_H +#ifdef CONFIG_HIGHMEM +#include +#include +#endif + #define NR_FIX_BTMAPS 64 enum fixed_addresses { FIX_HOLE, +#ifdef CONFIG_HIGHMEM + FIX_KMAP_BEGIN, + FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_MAX_IDX * NR_CPUS) - 1, +#endif FIX_EARLYCON_MEM_BASE, __end_of_fixed_addresses }; @@ -25,4 +34,9 @@ extern void __set_fixmap(enum fixed_addresses idx, #include +/* + * Called from pagetable_init() + */ +extern void fixrange_init(unsigned long start, unsigned long end, pgd_t *pgd_base); + #endif diff --git a/arch/loongarch/include/asm/highmem.h b/arch/loongarch/include/asm/highmem.h new file mode 100644 index 000000000000..e6d7a662d340 --- /dev/null +++ b/arch/loongarch/include/asm/highmem.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * highmem.h: virtual kernel memory mappings for high memory + * + * Used in CONFIG_HIGHMEM systems for memory pages which + * are not addressable by direct kernel virtual addresses. + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ +#ifndef _ASM_HIGHMEM_H +#define _ASM_HIGHMEM_H + +#ifdef __KERNEL__ + +#include + +#ifndef __ASSEMBLER__ + +extern pte_t *pkmap_page_table; + +#define ARCH_HAS_KMAP_FLUSH_TLB +void kmap_flush_tlb(unsigned long addr); + +#endif /* !__ASSEMBLER__ */ + +/* + * Right now we initialize only a single pte table. It can be extended + * easily, subsequent pte tables have to be allocated in one physical + * chunk of RAM. + */ +#define LAST_PKMAP 1024 +#define LAST_PKMAP_MASK (LAST_PKMAP - 1) +#define PKMAP_NR(virt) ((virt - PKMAP_BASE) >> PAGE_SHIFT) +#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT)) + +#define flush_cache_kmaps() do {} while (0) + +#define arch_kmap_local_post_map(vaddr, pteval) local_flush_tlb_one(vaddr) +#define arch_kmap_local_post_unmap(vaddr) local_flush_tlb_one(vaddr) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_HIGHMEM_H */ diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h index f9f207082d0e..76b723590023 100644 --- a/arch/loongarch/include/asm/inst.h +++ b/arch/loongarch/include/asm/inst.h @@ -36,6 +36,7 @@ enum reg0i15_op { break_op = 0x54, + dbar_op = 0x70e4, }; enum reg0i26_op { @@ -194,6 +195,10 @@ enum reg3_op { fstxs_op = 0x7070, fstxd_op = 0x7078, scq_op = 0x70ae, + amswapb_op = 0x70b8, + amswaph_op = 0x70b9, + amaddb_op = 0x70ba, + amaddh_op = 0x70bb, amswapw_op = 0x70c0, amswapd_op = 0x70c1, amaddw_op = 0x70c2, @@ -543,6 +548,7 @@ static inline void emit_##NAME(union loongarch_instruction *insn, \ } DEF_EMIT_REG0I15_FORMAT(break, break_op) +DEF_EMIT_REG0I15_FORMAT(dbar, dbar_op) /* like emit_break(imm) but returns a constant expression */ #define __emit_break(imm) ((u32)((imm) | (break_op << 15))) @@ -763,6 +769,8 @@ DEF_EMIT_REG3_FORMAT(stxb, stxb_op) DEF_EMIT_REG3_FORMAT(stxh, stxh_op) DEF_EMIT_REG3_FORMAT(stxw, stxw_op) DEF_EMIT_REG3_FORMAT(stxd, stxd_op) +DEF_EMIT_REG3_FORMAT(amaddb, amaddb_op) +DEF_EMIT_REG3_FORMAT(amaddh, amaddh_op) DEF_EMIT_REG3_FORMAT(amaddw, amaddw_op) DEF_EMIT_REG3_FORMAT(amaddd, amaddd_op) DEF_EMIT_REG3_FORMAT(amandw, amandw_op) @@ -771,6 +779,8 @@ DEF_EMIT_REG3_FORMAT(amorw, amorw_op) DEF_EMIT_REG3_FORMAT(amord, amord_op) DEF_EMIT_REG3_FORMAT(amxorw, amxorw_op) DEF_EMIT_REG3_FORMAT(amxord, amxord_op) +DEF_EMIT_REG3_FORMAT(amswapb, amswapb_op) +DEF_EMIT_REG3_FORMAT(amswaph, amswaph_op) DEF_EMIT_REG3_FORMAT(amswapw, amswapw_op) DEF_EMIT_REG3_FORMAT(amswapd, amswapd_op) diff --git a/arch/loongarch/include/asm/irq_work.h b/arch/loongarch/include/asm/irq_work.h index d63076e9160d..63aee0335d1a 100644 --- a/arch/loongarch/include/asm/irq_work.h +++ b/arch/loongarch/include/asm/irq_work.h @@ -4,7 +4,7 @@ static inline bool arch_irq_work_has_interrupt(void) { - return IS_ENABLED(CONFIG_SMP); + return IS_ENABLED(CONFIG_SMP) && cpu_opt(LOONGARCH_CPU_CSRIPI); } #endif /* _ASM_LOONGARCH_IRQ_WORK_H */ diff --git a/arch/loongarch/include/asm/jump_label.h b/arch/loongarch/include/asm/jump_label.h index dcaecf69ea5a..7ef4ae3abf08 100644 --- a/arch/loongarch/include/asm/jump_label.h +++ b/arch/loongarch/include/asm/jump_label.h @@ -13,6 +13,8 @@ #include #include +#define HAVE_JUMP_LABEL_BATCH + #define JUMP_LABEL_NOP_SIZE 4 #ifdef CONFIG_32BIT diff --git a/arch/loongarch/include/asm/kvm_dmsintc.h b/arch/loongarch/include/asm/kvm_dmsintc.h new file mode 100644 index 000000000000..5a71b9ccbe78 --- /dev/null +++ b/arch/loongarch/include/asm/kvm_dmsintc.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#ifndef __ASM_KVM_DMSINTC_H +#define __ASM_KVM_DMSINTC_H + +#include + +struct loongarch_dmsintc { + struct kvm *kvm; + uint64_t msg_addr_base; + uint64_t msg_addr_size; + uint32_t cpu_mask; +}; + +struct dmsintc_state { + atomic64_t vector_map[4]; +}; + +int kvm_loongarch_register_dmsintc_device(void); +void dmsintc_inject_irq(struct kvm_vcpu *vcpu); +int dmsintc_set_irq(struct kvm *kvm, u64 addr, int data, int level); +int dmsintc_deliver_msi_to_vcpu(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 vector, int level); + +#endif diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index 19eb5e5c3984..776bc487a705 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -86,7 +87,6 @@ struct kvm_context { struct kvm_world_switch { int (*exc_entry)(void); int (*enter_guest)(struct kvm_run *run, struct kvm_vcpu *vcpu); - unsigned long page_order; }; #define MAX_PGTABLE_LEVELS 4 @@ -133,6 +133,7 @@ struct kvm_arch { s64 time_offset; struct kvm_context __percpu *vmcs; struct loongarch_ipi *ipi; + struct loongarch_dmsintc *dmsintc; struct loongarch_eiointc *eiointc; struct loongarch_pch_pic *pch_pic; }; @@ -247,6 +248,7 @@ struct kvm_vcpu_arch { struct kvm_mp_state mp_state; /* ipi state */ struct ipi_state ipi_state; + struct dmsintc_state dmsintc_state; /* cpucfg */ u32 cpucfg[KVM_MAX_CPUCFG_REGS]; @@ -356,8 +358,6 @@ void kvm_exc_entry(void); int kvm_enter_guest(struct kvm_run *run, struct kvm_vcpu *vcpu); extern unsigned long vpid_mask; -extern const unsigned long kvm_exception_size; -extern const unsigned long kvm_enter_guest_size; extern struct kvm_world_switch *kvm_loongarch_ops; #define SW_GCSR (1 << 0) diff --git a/arch/loongarch/include/asm/kvm_pch_pic.h b/arch/loongarch/include/asm/kvm_pch_pic.h index 7f33a3039272..e74b3b742634 100644 --- a/arch/loongarch/include/asm/kvm_pch_pic.h +++ b/arch/loongarch/include/asm/kvm_pch_pic.h @@ -68,8 +68,9 @@ struct loongarch_pch_pic { uint64_t pch_pic_base; }; +struct kvm_kernel_irq_routing_entry; int kvm_loongarch_register_pch_pic_device(void); void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level); -void pch_msi_set_irq(struct kvm *kvm, int irq, int level); +int pch_msi_set_irq(struct kvm *kvm, struct kvm_kernel_irq_routing_entry *e, int level); #endif /* __ASM_KVM_PCH_PIC_H */ diff --git a/arch/loongarch/include/asm/linkage.h b/arch/loongarch/include/asm/linkage.h index a1bd6a3ee03a..ae937d1708b2 100644 --- a/arch/loongarch/include/asm/linkage.h +++ b/arch/loongarch/include/asm/linkage.h @@ -69,7 +69,7 @@ 9, 10, 11, 12, 13, 14, 15, 16, \ 17, 18, 19, 20, 21, 22, 23, 24, \ 25, 26, 27, 28, 29, 30, 31; \ - .cfi_offset \num, SC_REGS + \num * SZREG; \ + .cfi_offset \num, SC_REGS + \num * 8; \ .endr; \ \ nop; \ diff --git a/arch/loongarch/include/asm/page.h b/arch/loongarch/include/asm/page.h index 79235f4fc399..b9d61f7c032c 100644 --- a/arch/loongarch/include/asm/page.h +++ b/arch/loongarch/include/asm/page.h @@ -36,10 +36,6 @@ extern unsigned long shm_align_mask; struct page; struct vm_area_struct; -void copy_user_highpage(struct page *to, struct page *from, - unsigned long vaddr, struct vm_area_struct *vma); - -#define __HAVE_ARCH_COPY_USER_HIGHPAGE typedef struct { unsigned long pte; } pte_t; #define pte_val(x) ((x).pte) diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h index 155f70e93460..2a0b63ae421f 100644 --- a/arch/loongarch/include/asm/pgtable.h +++ b/arch/loongarch/include/asm/pgtable.h @@ -23,6 +23,10 @@ #include #endif +#ifdef CONFIG_HIGHMEM +#include +#endif + #if CONFIG_PGTABLE_LEVELS == 2 #define PGDIR_SHIFT (PAGE_SHIFT + (PAGE_SHIFT - PTRLOG)) #elif CONFIG_PGTABLE_LEVELS == 3 @@ -77,7 +81,15 @@ struct vm_area_struct; #ifdef CONFIG_32BIT #define VMALLOC_START (vm_map_base + PCI_IOSIZE + (2 * PAGE_SIZE)) + +#ifdef CONFIG_HIGHMEM +#define VMALLOC_END (PKMAP_BASE - (2 * PAGE_SIZE)) +#else #define VMALLOC_END (FIXADDR_START - (2 * PAGE_SIZE)) +#endif + +#define PKMAP_BASE (PKMAP_END - (PAGE_SIZE * LAST_PKMAP)) +#define PKMAP_END ((FIXADDR_START) & ~((LAST_PKMAP << PAGE_SHIFT)-1)) #endif diff --git a/arch/loongarch/include/asm/processor.h b/arch/loongarch/include/asm/processor.h index c3bc44b5f5b3..ce8b953f8c79 100644 --- a/arch/loongarch/include/asm/processor.h +++ b/arch/loongarch/include/asm/processor.h @@ -80,10 +80,10 @@ BUILD_FPR_ACCESS(32) BUILD_FPR_ACCESS(64) struct loongarch_fpu { + union fpureg fpr[NUM_FPU_REGS]; uint64_t fcc; /* 8x8 */ uint32_t fcsr; uint32_t ftop; - union fpureg fpr[NUM_FPU_REGS]; }; struct loongarch_lbt { diff --git a/arch/loongarch/include/asm/qspinlock.h b/arch/loongarch/include/asm/qspinlock.h index 66244801db67..0ee15b3b3937 100644 --- a/arch/loongarch/include/asm/qspinlock.h +++ b/arch/loongarch/include/asm/qspinlock.h @@ -2,11 +2,13 @@ #ifndef _ASM_LOONGARCH_QSPINLOCK_H #define _ASM_LOONGARCH_QSPINLOCK_H +#include #include #ifdef CONFIG_PARAVIRT - +DECLARE_STATIC_KEY_FALSE(virt_preempt_key); DECLARE_STATIC_KEY_FALSE(virt_spin_lock_key); +DECLARE_PER_CPU(struct kvm_steal_time, steal_time); #define virt_spin_lock virt_spin_lock @@ -34,9 +36,25 @@ __retry: return true; } -#define vcpu_is_preempted vcpu_is_preempted - -bool vcpu_is_preempted(int cpu); +/* + * Macro is better than inline function here + * With macro, parameter cpu is parsed only when it is used. + * With inline function, parameter cpu is parsed even though it is not used. + * This may cause cache line thrashing across NUMA nodes. + */ +#define vcpu_is_preempted(cpu) \ +({ \ + bool __val; \ + \ + if (!static_branch_unlikely(&virt_preempt_key)) \ + __val = false; \ + else { \ + struct kvm_steal_time *src; \ + src = &per_cpu(steal_time, cpu); \ + __val = !!(READ_ONCE(src->preempted) & KVM_VCPU_PREEMPTED); \ + } \ + __val; \ +}) #endif /* CONFIG_PARAVIRT */ diff --git a/arch/loongarch/include/asm/stackprotector.h b/arch/loongarch/include/asm/stackprotector.h index a1a965751a7b..42f6c3f69115 100644 --- a/arch/loongarch/include/asm/stackprotector.h +++ b/arch/loongarch/include/asm/stackprotector.h @@ -12,9 +12,6 @@ #ifndef _ASM_STACKPROTECTOR_H #define _ASM_STACKPROTECTOR_H -#include -#include - extern unsigned long __stack_chk_guard; /* @@ -25,11 +22,7 @@ extern unsigned long __stack_chk_guard; */ static __always_inline void boot_init_stack_canary(void) { - unsigned long canary; - - /* Try to get a semi random initial value. */ - get_random_bytes(&canary, sizeof(canary)); - canary ^= LINUX_VERSION_CODE; + unsigned long canary = get_random_canary(); current->stack_canary = canary; __stack_chk_guard = current->stack_canary; diff --git a/arch/loongarch/include/asm/syscall.h b/arch/loongarch/include/asm/syscall.h index 81d2733f7b94..df8ea223c77b 100644 --- a/arch/loongarch/include/asm/syscall.h +++ b/arch/loongarch/include/asm/syscall.h @@ -78,7 +78,11 @@ static inline void syscall_set_arguments(struct task_struct *task, static inline int syscall_get_arch(struct task_struct *task) { +#ifdef CONFIG_32BIT + return AUDIT_ARCH_LOONGARCH32; +#else return AUDIT_ARCH_LOONGARCH64; +#endif } static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs) diff --git a/arch/loongarch/include/asm/vdso/gettimeofday.h b/arch/loongarch/include/asm/vdso/gettimeofday.h index bae76767c693..18ba403e1ed9 100644 --- a/arch/loongarch/include/asm/vdso/gettimeofday.h +++ b/arch/loongarch/include/asm/vdso/gettimeofday.h @@ -85,12 +85,6 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, return count; } -static inline bool loongarch_vdso_hres_capable(void) -{ - return true; -} -#define __arch_vdso_hres_capable loongarch_vdso_hres_capable - #endif /* CONFIG_GENERIC_GETTIMEOFDAY */ #endif /* !__ASSEMBLER__ */ diff --git a/arch/loongarch/include/asm/xor.h b/arch/loongarch/include/asm/xor.h deleted file mode 100644 index 12467fffee46..000000000000 --- a/arch/loongarch/include/asm/xor.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2023 WANG Xuerui - */ -#ifndef _ASM_LOONGARCH_XOR_H -#define _ASM_LOONGARCH_XOR_H - -#include -#include - -#ifdef CONFIG_CPU_HAS_LSX -static struct xor_block_template xor_block_lsx = { - .name = "lsx", - .do_2 = xor_lsx_2, - .do_3 = xor_lsx_3, - .do_4 = xor_lsx_4, - .do_5 = xor_lsx_5, -}; - -#define XOR_SPEED_LSX() \ - do { \ - if (cpu_has_lsx) \ - xor_speed(&xor_block_lsx); \ - } while (0) -#else /* CONFIG_CPU_HAS_LSX */ -#define XOR_SPEED_LSX() -#endif /* CONFIG_CPU_HAS_LSX */ - -#ifdef CONFIG_CPU_HAS_LASX -static struct xor_block_template xor_block_lasx = { - .name = "lasx", - .do_2 = xor_lasx_2, - .do_3 = xor_lasx_3, - .do_4 = xor_lasx_4, - .do_5 = xor_lasx_5, -}; - -#define XOR_SPEED_LASX() \ - do { \ - if (cpu_has_lasx) \ - xor_speed(&xor_block_lasx); \ - } while (0) -#else /* CONFIG_CPU_HAS_LASX */ -#define XOR_SPEED_LASX() -#endif /* CONFIG_CPU_HAS_LASX */ - -/* - * For grins, also test the generic routines. - * - * More importantly: it cannot be ruled out at this point of time, that some - * future (maybe reduced) models could run the vector algorithms slower than - * the scalar ones, maybe for errata or micro-op reasons. It may be - * appropriate to revisit this after one or two more uarch generations. - */ -#include - -#undef XOR_TRY_TEMPLATES -#define XOR_TRY_TEMPLATES \ -do { \ - xor_speed(&xor_block_8regs); \ - xor_speed(&xor_block_8regs_p); \ - xor_speed(&xor_block_32regs); \ - xor_speed(&xor_block_32regs_p); \ - XOR_SPEED_LSX(); \ - XOR_SPEED_LASX(); \ -} while (0) - -#endif /* _ASM_LOONGARCH_XOR_H */ diff --git a/arch/loongarch/include/asm/xor_simd.h b/arch/loongarch/include/asm/xor_simd.h deleted file mode 100644 index 471b96332f38..000000000000 --- a/arch/loongarch/include/asm/xor_simd.h +++ /dev/null @@ -1,34 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2023 WANG Xuerui - */ -#ifndef _ASM_LOONGARCH_XOR_SIMD_H -#define _ASM_LOONGARCH_XOR_SIMD_H - -#ifdef CONFIG_CPU_HAS_LSX -void xor_lsx_2(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2); -void xor_lsx_3(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, const unsigned long * __restrict p3); -void xor_lsx_4(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, const unsigned long * __restrict p3, - const unsigned long * __restrict p4); -void xor_lsx_5(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, const unsigned long * __restrict p3, - const unsigned long * __restrict p4, const unsigned long * __restrict p5); -#endif /* CONFIG_CPU_HAS_LSX */ - -#ifdef CONFIG_CPU_HAS_LASX -void xor_lasx_2(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2); -void xor_lasx_3(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, const unsigned long * __restrict p3); -void xor_lasx_4(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, const unsigned long * __restrict p3, - const unsigned long * __restrict p4); -void xor_lasx_5(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, const unsigned long * __restrict p3, - const unsigned long * __restrict p4, const unsigned long * __restrict p5); -#endif /* CONFIG_CPU_HAS_LASX */ - -#endif /* _ASM_LOONGARCH_XOR_SIMD_H */ diff --git a/arch/loongarch/include/uapi/asm/hwcap.h b/arch/loongarch/include/uapi/asm/hwcap.h index 49519b4362c6..90e96113ba51 100644 --- a/arch/loongarch/include/uapi/asm/hwcap.h +++ b/arch/loongarch/include/uapi/asm/hwcap.h @@ -19,5 +19,6 @@ #define HWCAP_LOONGARCH_PTW (1 << 13) #define HWCAP_LOONGARCH_LSPW (1 << 14) #define HWCAP_LOONGARCH_SCQ (1 << 15) +#define HWCAP_LOONGARCH_LAM_BH (1 << 16) #endif /* _UAPI_ASM_HWCAP_H */ diff --git a/arch/loongarch/include/uapi/asm/kvm.h b/arch/loongarch/include/uapi/asm/kvm.h index 419647aacdf3..cd0b5c11ca9c 100644 --- a/arch/loongarch/include/uapi/asm/kvm.h +++ b/arch/loongarch/include/uapi/asm/kvm.h @@ -155,4 +155,8 @@ struct kvm_iocsr_entry { #define KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL 0x40000006 #define KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT 0 +#define KVM_DEV_LOONGARCH_DMSINTC_GRP_CTRL 0x40000007 +#define KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_BASE 0x0 +#define KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_SIZE 0x1 + #endif /* __UAPI_ASM_LOONGARCH_KVM_H */ diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c index 657bbae6c1c7..74d31f260dfd 100644 --- a/arch/loongarch/kernel/cpu-probe.c +++ b/arch/loongarch/kernel/cpu-probe.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -177,6 +178,10 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c) c->options |= LOONGARCH_CPU_LAM; elf_hwcap |= HWCAP_LOONGARCH_LAM; } + if (config & CPUCFG2_LAM_BH) { + c->options |= LOONGARCH_CPU_LAM_BH; + elf_hwcap |= HWCAP_LOONGARCH_LAM_BH; + } if (config & CPUCFG2_SCQ) { c->options |= LOONGARCH_CPU_SCQ; elf_hwcap |= HWCAP_LOONGARCH_SCQ; @@ -402,3 +407,9 @@ void cpu_probe(void) cpu_report(); } + +ssize_t cpu_show_spectre_v1(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "Mitigation: __user pointer sanitization\n"); +} diff --git a/arch/loongarch/kernel/fpu.S b/arch/loongarch/kernel/fpu.S index f225dcc5b530..bf7d6b8bf600 100644 --- a/arch/loongarch/kernel/fpu.S +++ b/arch/loongarch/kernel/fpu.S @@ -97,7 +97,7 @@ .endm #ifdef CONFIG_32BIT - .macro sc_save_fcc thread tmp0 tmp1 + .macro sc_save_fcc base tmp0 tmp1 movcf2gr \tmp0, $fcc0 move \tmp1, \tmp0 movcf2gr \tmp0, $fcc1 @@ -106,7 +106,7 @@ bstrins.w \tmp1, \tmp0, 23, 16 movcf2gr \tmp0, $fcc3 bstrins.w \tmp1, \tmp0, 31, 24 - EX st.w \tmp1, \thread, THREAD_FCC + EX st.w \tmp1, \base, 0 movcf2gr \tmp0, $fcc4 move \tmp1, \tmp0 movcf2gr \tmp0, $fcc5 @@ -115,11 +115,11 @@ bstrins.w \tmp1, \tmp0, 23, 16 movcf2gr \tmp0, $fcc7 bstrins.w \tmp1, \tmp0, 31, 24 - EX st.w \tmp1, \thread, (THREAD_FCC + 4) + EX st.w \tmp1, \base, 4 .endm - .macro sc_restore_fcc thread tmp0 tmp1 - EX ld.w \tmp0, \thread, THREAD_FCC + .macro sc_restore_fcc base tmp0 tmp1 + EX ld.w \tmp0, \base, 0 bstrpick.w \tmp1, \tmp0, 7, 0 movgr2cf $fcc0, \tmp1 bstrpick.w \tmp1, \tmp0, 15, 8 @@ -128,7 +128,7 @@ movgr2cf $fcc2, \tmp1 bstrpick.w \tmp1, \tmp0, 31, 24 movgr2cf $fcc3, \tmp1 - EX ld.w \tmp0, \thread, (THREAD_FCC + 4) + EX ld.w \tmp0, \base, 4 bstrpick.w \tmp1, \tmp0, 7, 0 movgr2cf $fcc4, \tmp1 bstrpick.w \tmp1, \tmp0, 15, 8 diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c index 1a728082944c..0b9228b7c13a 100644 --- a/arch/loongarch/kernel/inst.c +++ b/arch/loongarch/kernel/inst.c @@ -209,6 +209,9 @@ int larch_insn_write(void *addr, u32 insn) int ret; unsigned long flags = 0; + if ((unsigned long)addr & 3) + return -EINVAL; + raw_spin_lock_irqsave(&patch_lock, flags); ret = copy_to_kernel_nofault(addr, &insn, LOONGARCH_INSN_SIZE); raw_spin_unlock_irqrestore(&patch_lock, flags); @@ -221,9 +224,6 @@ int larch_insn_patch_text(void *addr, u32 insn) int ret; u32 *tp = addr; - if ((unsigned long)tp & 3) - return -EINVAL; - ret = larch_insn_write(tp, insn); if (!ret) flush_icache_range((unsigned long)tp, diff --git a/arch/loongarch/kernel/jump_label.c b/arch/loongarch/kernel/jump_label.c index 31891214b767..24a3f4d8540c 100644 --- a/arch/loongarch/kernel/jump_label.c +++ b/arch/loongarch/kernel/jump_label.c @@ -6,9 +6,10 @@ */ #include #include +#include #include -void arch_jump_label_transform(struct jump_entry *entry, enum jump_label_type type) +bool arch_jump_label_transform_queue(struct jump_entry *entry, enum jump_label_type type) { u32 insn; void *addr = (void *)jump_entry_code(entry); @@ -18,5 +19,12 @@ void arch_jump_label_transform(struct jump_entry *entry, enum jump_label_type ty else insn = larch_insn_gen_nop(); - larch_insn_patch_text(addr, insn); + larch_insn_write(addr, insn); + + return true; +} + +void arch_jump_label_transform_apply(void) +{ + flush_icache_all(); } diff --git a/arch/loongarch/kernel/paravirt.c b/arch/loongarch/kernel/paravirt.c index b74fe6db49ab..10821cce554c 100644 --- a/arch/loongarch/kernel/paravirt.c +++ b/arch/loongarch/kernel/paravirt.c @@ -10,9 +10,9 @@ #include static int has_steal_clock; -static DEFINE_PER_CPU(struct kvm_steal_time, steal_time) __aligned(64); -static DEFINE_STATIC_KEY_FALSE(virt_preempt_key); +DEFINE_STATIC_KEY_FALSE(virt_preempt_key); DEFINE_STATIC_KEY_FALSE(virt_spin_lock_key); +DEFINE_PER_CPU(struct kvm_steal_time, steal_time) __aligned(64); static bool steal_acc = true; @@ -260,18 +260,6 @@ static int pv_time_cpu_down_prepare(unsigned int cpu) return 0; } - -bool vcpu_is_preempted(int cpu) -{ - struct kvm_steal_time *src; - - if (!static_branch_unlikely(&virt_preempt_key)) - return false; - - src = &per_cpu(steal_time, cpu); - return !!(src->preempted & KVM_VCPU_PREEMPTED); -} -EXPORT_SYMBOL(vcpu_is_preempted); #endif static void pv_cpu_reboot(void *unused) diff --git a/arch/loongarch/kernel/proc.c b/arch/loongarch/kernel/proc.c index a8127e83da65..d4ce5b585453 100644 --- a/arch/loongarch/kernel/proc.c +++ b/arch/loongarch/kernel/proc.c @@ -64,6 +64,8 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_puts(m, " cpucfg"); if (cpu_has_lam) seq_puts(m, " lam"); + if (cpu_has_lam_bh) + seq_puts(m, " lam_bh"); if (cpu_has_scq) seq_puts(m, " scq"); if (cpu_has_ual) diff --git a/arch/loongarch/kernel/process.c b/arch/loongarch/kernel/process.c index ac3a0baa5d00..5505fc355e1b 100644 --- a/arch/loongarch/kernel/process.c +++ b/arch/loongarch/kernel/process.c @@ -136,6 +136,8 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) return 0; } + dst->thread.fpu.fcsr = src->thread.fpu.fcsr; + if (!used_math()) memcpy(dst, src, offsetof(struct task_struct, thread.fpu.fpr)); else diff --git a/arch/loongarch/kernel/relocate.c b/arch/loongarch/kernel/relocate.c index 82aa3f035927..16f6a9b39659 100644 --- a/arch/loongarch/kernel/relocate.c +++ b/arch/loongarch/kernel/relocate.c @@ -128,24 +128,28 @@ static inline __init unsigned long get_random_boot(void) static int __init nokaslr(char *p) { - pr_info("KASLR is disabled.\n"); - - return 0; /* Print a notice and silence the boot warning */ + return 0; /* Just silence the boot warning */ } early_param("nokaslr", nokaslr); +#define KASLR_DISABLED_MESSAGE "KASLR is disabled by %s in %s cmdline.\n" + static inline __init bool kaslr_disabled(void) { char *str; const char *builtin_cmdline = CONFIG_CMDLINE; str = strstr(builtin_cmdline, "nokaslr"); - if (str == builtin_cmdline || (str > builtin_cmdline && *(str - 1) == ' ')) + if (str == builtin_cmdline || (str > builtin_cmdline && *(str - 1) == ' ')) { + pr_info(KASLR_DISABLED_MESSAGE, "\'nokaslr\'", "built-in"); return true; + } str = strstr(boot_command_line, "nokaslr"); - if (str == boot_command_line || (str > boot_command_line && *(str - 1) == ' ')) + if (str == boot_command_line || (str > boot_command_line && *(str - 1) == ' ')) { + pr_info(KASLR_DISABLED_MESSAGE, "\'nokaslr\'", "bootloader"); return true; + } #ifdef CONFIG_HIBERNATION str = strstr(builtin_cmdline, "nohibernate"); @@ -165,17 +169,23 @@ static inline __init bool kaslr_disabled(void) return false; str = strstr(builtin_cmdline, "resume="); - if (str == builtin_cmdline || (str > builtin_cmdline && *(str - 1) == ' ')) + if (str == builtin_cmdline || (str > builtin_cmdline && *(str - 1) == ' ')) { + pr_info(KASLR_DISABLED_MESSAGE, "\'resume=\'", "built-in"); return true; + } str = strstr(boot_command_line, "resume="); - if (str == boot_command_line || (str > boot_command_line && *(str - 1) == ' ')) + if (str == boot_command_line || (str > boot_command_line && *(str - 1) == ' ')) { + pr_info(KASLR_DISABLED_MESSAGE, "\'resume=\'", "bootloader"); return true; + } #endif str = strstr(boot_command_line, "kexec_file"); - if (str == boot_command_line || (str > boot_command_line && *(str - 1) == ' ')) + if (str == boot_command_line || (str > boot_command_line && *(str - 1) == ' ')) { + pr_info(KASLR_DISABLED_MESSAGE, "\'kexec_file\'", "bootloader"); return true; + } return false; } diff --git a/arch/loongarch/kernel/syscall.c b/arch/loongarch/kernel/syscall.c index 85da7e050d97..94c1c3b5b0b5 100644 --- a/arch/loongarch/kernel/syscall.c +++ b/arch/loongarch/kernel/syscall.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -74,7 +75,7 @@ void noinstr __no_stack_protector do_syscall(struct pt_regs *regs) add_random_kstack_offset(); if (nr < NR_syscalls) { - syscall_fn = sys_call_table[nr]; + syscall_fn = sys_call_table[array_index_nospec(nr, NR_syscalls)]; regs->regs[4] = syscall_fn(regs->orig_a0, regs->regs[5], regs->regs[6], regs->regs[7], regs->regs[8], regs->regs[9]); } diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S index d0e1377a041d..840d944c2f73 100644 --- a/arch/loongarch/kernel/vmlinux.lds.S +++ b/arch/loongarch/kernel/vmlinux.lds.S @@ -6,7 +6,12 @@ #define PAGE_SIZE _PAGE_SIZE #define RO_EXCEPTION_TABLE_ALIGN 4 -#define PHYSADDR_MASK 0xffffffffffff /* 48-bit */ + +#ifdef CONFIG_32BIT +#define PHYSADDR_MASK 0x1fffffff /* 29-bit */ +#else +#define PHYSADDR_MASK 0xffffffffffff /* 48-bit */ +#endif /* * Put .bss..swapper_pg_dir as the first thing in .bss. This will diff --git a/arch/loongarch/kvm/Kconfig b/arch/loongarch/kvm/Kconfig index 8e5213609975..15da2d88c0c1 100644 --- a/arch/loongarch/kvm/Kconfig +++ b/arch/loongarch/kvm/Kconfig @@ -19,7 +19,7 @@ if VIRTUALIZATION config KVM tristate "Kernel-based Virtual Machine (KVM) support" - depends on AS_HAS_LVZ_EXTENSION + depends on AS_HAS_LVZ_EXTENSION && 64BIT select HAVE_KVM_DIRTY_RING_ACQ_REL select HAVE_KVM_IRQ_ROUTING select HAVE_KVM_IRQCHIP diff --git a/arch/loongarch/kvm/Makefile b/arch/loongarch/kvm/Makefile index cb41d9265662..a4d044da3aa7 100644 --- a/arch/loongarch/kvm/Makefile +++ b/arch/loongarch/kvm/Makefile @@ -7,16 +7,18 @@ include $(srctree)/virt/kvm/Makefile.kvm obj-$(CONFIG_KVM) += kvm.o +obj-y += switch.o + kvm-y += exit.o kvm-y += interrupt.o kvm-y += main.o kvm-y += mmu.o -kvm-y += switch.o kvm-y += timer.o kvm-y += tlb.o kvm-y += vcpu.o kvm-y += vm.o kvm-y += intc/ipi.o +kvm-y += intc/dmsintc.o kvm-y += intc/eiointc.o kvm-y += intc/pch_pic.o kvm-y += irqfd.o diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index da0ad89f2eb7..3b95cd0f989b 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -390,6 +390,7 @@ int kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst) run->mmio.len = 8; break; default: + ret = EMULATE_FAIL; break; } break; diff --git a/arch/loongarch/kvm/intc/dmsintc.c b/arch/loongarch/kvm/intc/dmsintc.c new file mode 100644 index 000000000000..de25735ce039 --- /dev/null +++ b/arch/loongarch/kvm/intc/dmsintc.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include + +void dmsintc_inject_irq(struct kvm_vcpu *vcpu) +{ + unsigned int i; + unsigned long vector[4], old; + struct dmsintc_state *ds = &vcpu->arch.dmsintc_state; + + if (!ds) + return; + + for (i = 0; i < 4; i++) { + old = atomic64_read(&(ds->vector_map[i])); + if (old) + vector[i] = atomic64_xchg(&(ds->vector_map[i]), 0); + } + + if (vector[0]) { + old = kvm_read_hw_gcsr(LOONGARCH_CSR_ISR0); + kvm_write_hw_gcsr(LOONGARCH_CSR_ISR0, vector[0] | old); + } + + if (vector[1]) { + old = kvm_read_hw_gcsr(LOONGARCH_CSR_ISR1); + kvm_write_hw_gcsr(LOONGARCH_CSR_ISR1, vector[1] | old); + } + + if (vector[2]) { + old = kvm_read_hw_gcsr(LOONGARCH_CSR_ISR2); + kvm_write_hw_gcsr(LOONGARCH_CSR_ISR2, vector[2] | old); + } + + if (vector[3]) { + old = kvm_read_hw_gcsr(LOONGARCH_CSR_ISR3); + kvm_write_hw_gcsr(LOONGARCH_CSR_ISR3, vector[3] | old); + } +} + +int dmsintc_deliver_msi_to_vcpu(struct kvm *kvm, + struct kvm_vcpu *vcpu, u32 vector, int level) +{ + struct kvm_interrupt vcpu_irq; + struct dmsintc_state *ds = &vcpu->arch.dmsintc_state; + + if (!level) + return 0; + if (!vcpu || vector >= 256) + return -EINVAL; + if (!ds) + return -ENODEV; + + vcpu_irq.irq = INT_AVEC; + set_bit(vector, (unsigned long *)&ds->vector_map); + kvm_vcpu_ioctl_interrupt(vcpu, &vcpu_irq); + kvm_vcpu_kick(vcpu); + + return 0; +} + +int dmsintc_set_irq(struct kvm *kvm, u64 addr, int data, int level) +{ + unsigned int irq, cpu; + struct kvm_vcpu *vcpu; + + irq = (addr >> AVEC_IRQ_SHIFT) & AVEC_IRQ_MASK; + cpu = (addr >> AVEC_CPU_SHIFT) & kvm->arch.dmsintc->cpu_mask; + if (cpu >= KVM_MAX_VCPUS) + return -EINVAL; + vcpu = kvm_get_vcpu_by_cpuid(kvm, cpu); + if (!vcpu) + return -EINVAL; + + return dmsintc_deliver_msi_to_vcpu(kvm, vcpu, irq, level); +} + +static int kvm_dmsintc_ctrl_access(struct kvm_device *dev, + struct kvm_device_attr *attr, bool is_write) +{ + int addr = attr->attr; + unsigned long cpu_bit, val; + void __user *data = (void __user *)attr->addr; + struct loongarch_dmsintc *s = dev->kvm->arch.dmsintc; + + switch (addr) { + case KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_BASE: + if (is_write) { + if (copy_from_user(&val, data, sizeof(s->msg_addr_base))) + return -EFAULT; + if (s->msg_addr_base) + return -EFAULT; /* Duplicate setting are not allowed. */ + if ((val & (BIT(AVEC_CPU_SHIFT) - 1)) != 0) + return -EINVAL; + s->msg_addr_base = val; + cpu_bit = find_first_bit((unsigned long *)&(s->msg_addr_base), 64) - AVEC_CPU_SHIFT; + cpu_bit = min(cpu_bit, AVEC_CPU_BIT); + s->cpu_mask = GENMASK(cpu_bit - 1, 0) & AVEC_CPU_MASK; + } + break; + case KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_SIZE: + if (is_write) { + if (copy_from_user(&val, data, sizeof(s->msg_addr_size))) + return -EFAULT; + if (s->msg_addr_size) + return -EFAULT; /*Duplicate setting are not allowed. */ + s->msg_addr_size = val; + } + break; + default: + kvm_err("%s: unknown dmsintc register, addr = %d\n", __func__, addr); + return -ENXIO; + } + + return 0; +} + +static int kvm_dmsintc_set_attr(struct kvm_device *dev, + struct kvm_device_attr *attr) +{ + switch (attr->group) { + case KVM_DEV_LOONGARCH_DMSINTC_GRP_CTRL: + return kvm_dmsintc_ctrl_access(dev, attr, true); + default: + kvm_err("%s: unknown group (%d)\n", __func__, attr->group); + return -EINVAL; + } +} + +static int kvm_dmsintc_create(struct kvm_device *dev, u32 type) +{ + struct kvm *kvm; + struct loongarch_dmsintc *s; + + if (!dev) { + kvm_err("%s: kvm_device ptr is invalid!\n", __func__); + return -EINVAL; + } + + kvm = dev->kvm; + if (kvm->arch.dmsintc) { + kvm_err("%s: LoongArch DMSINTC has already been created!\n", __func__); + return -EINVAL; + } + + s = kzalloc(sizeof(struct loongarch_dmsintc), GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->kvm = kvm; + kvm->arch.dmsintc = s; + + return 0; +} + +static void kvm_dmsintc_destroy(struct kvm_device *dev) +{ + + if (!dev || !dev->kvm || !dev->kvm->arch.dmsintc) + return; + + kfree(dev->kvm->arch.dmsintc); + kfree(dev); +} + +static struct kvm_device_ops kvm_dmsintc_dev_ops = { + .name = "kvm-loongarch-dmsintc", + .create = kvm_dmsintc_create, + .destroy = kvm_dmsintc_destroy, + .set_attr = kvm_dmsintc_set_attr, +}; + +int kvm_loongarch_register_dmsintc_device(void) +{ + return kvm_register_device_ops(&kvm_dmsintc_dev_ops, KVM_DEV_TYPE_LOONGARCH_DMSINTC); +} diff --git a/arch/loongarch/kvm/intc/pch_pic.c b/arch/loongarch/kvm/intc/pch_pic.c index dd7e7f8d53db..aa0ed59ae8cf 100644 --- a/arch/loongarch/kvm/intc/pch_pic.c +++ b/arch/loongarch/kvm/intc/pch_pic.c @@ -3,6 +3,7 @@ * Copyright (C) 2024 Loongson Technology Corporation Limited */ +#include #include #include #include @@ -67,9 +68,19 @@ void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level) } /* msi irq handler */ -void pch_msi_set_irq(struct kvm *kvm, int irq, int level) +int pch_msi_set_irq(struct kvm *kvm, struct kvm_kernel_irq_routing_entry *e, int level) { - eiointc_set_irq(kvm->arch.eiointc, irq, level); + u64 msg_addr = (((u64)e->msi.address_hi) << 32) | e->msi.address_lo; + + if (cpu_has_msgint && kvm->arch.dmsintc && + msg_addr >= kvm->arch.dmsintc->msg_addr_base && + msg_addr < (kvm->arch.dmsintc->msg_addr_base + kvm->arch.dmsintc->msg_addr_size)) { + return dmsintc_set_irq(kvm, msg_addr, e->msi.data, level); + } + + eiointc_set_irq(kvm->arch.eiointc, e->msi.data, level); + + return 0; } static int loongarch_pch_pic_read(struct loongarch_pch_pic *s, gpa_t addr, int len, void *val) diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c index fb704f4c8ac5..a18c60dffbba 100644 --- a/arch/loongarch/kvm/interrupt.c +++ b/arch/loongarch/kvm/interrupt.c @@ -7,6 +7,7 @@ #include #include #include +#include static unsigned int priority_to_irq[EXCCODE_INT_NUM] = { [INT_TI] = CPU_TIMER, @@ -27,22 +28,29 @@ static unsigned int priority_to_irq[EXCCODE_INT_NUM] = { static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority) { unsigned int irq = 0; + unsigned long old, new; clear_bit(priority, &vcpu->arch.irq_pending); if (priority < EXCCODE_INT_NUM) irq = priority_to_irq[priority]; - if (kvm_guest_has_msgint(&vcpu->arch) && (priority == INT_AVEC)) { - set_gcsr_estat(irq); - return 1; - } - switch (priority) { + case INT_AVEC: + if (!kvm_guest_has_msgint(&vcpu->arch)) + break; + dmsintc_inject_irq(vcpu); + fallthrough; case INT_TI: case INT_IPI: case INT_SWI0: case INT_SWI1: + old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); set_gcsr_estat(irq); + new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); + + /* Inject TI if TVAL inverted */ + if (new > old) + set_gcsr_estat(CPU_TIMER); break; case INT_HWI0 ... INT_HWI7: @@ -59,22 +67,28 @@ static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority) static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority) { unsigned int irq = 0; + unsigned long old, new; clear_bit(priority, &vcpu->arch.irq_clear); if (priority < EXCCODE_INT_NUM) irq = priority_to_irq[priority]; - if (kvm_guest_has_msgint(&vcpu->arch) && (priority == INT_AVEC)) { - clear_gcsr_estat(irq); - return 1; - } - switch (priority) { + case INT_AVEC: + if (!kvm_guest_has_msgint(&vcpu->arch)) + break; + fallthrough; case INT_TI: case INT_IPI: case INT_SWI0: case INT_SWI1: + old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); clear_gcsr_estat(irq); + new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); + + /* Inject TI if TVAL inverted */ + if (new > old) + set_gcsr_estat(CPU_TIMER); break; case INT_HWI0 ... INT_HWI7: diff --git a/arch/loongarch/kvm/irqfd.c b/arch/loongarch/kvm/irqfd.c index 9a39627aecf0..f4f953b22419 100644 --- a/arch/loongarch/kvm/irqfd.c +++ b/arch/loongarch/kvm/irqfd.c @@ -29,9 +29,7 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, if (!level) return -1; - pch_msi_set_irq(kvm, e->msi.data, level); - - return 0; + return pch_msi_set_irq(kvm, e, level); } /* @@ -71,13 +69,15 @@ int kvm_set_routing_entry(struct kvm *kvm, int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, int irq_source_id, int level, bool line_status) { + if (!level) + return -EWOULDBLOCK; + switch (e->type) { case KVM_IRQ_ROUTING_IRQCHIP: pch_pic_set_irq(kvm->arch.pch_pic, e->irqchip.pin, level); return 0; case KVM_IRQ_ROUTING_MSI: - pch_msi_set_irq(kvm, e->msi.data, level); - return 0; + return pch_msi_set_irq(kvm, e, level); default: return -EWOULDBLOCK; } diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c index 2c593ac7892f..f105a86143f5 100644 --- a/arch/loongarch/kvm/main.c +++ b/arch/loongarch/kvm/main.c @@ -271,11 +271,11 @@ void kvm_check_vpid(struct kvm_vcpu *vcpu) * memory with new address is changed on other VCPUs. */ set_gcsr_llbctl(CSR_LLBCTL_WCLLB); - } - /* Restore GSTAT(0x50).vpid */ - vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; - change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); + /* Restore GSTAT(0x50).vpid */ + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); + } } void kvm_init_vmcs(struct kvm *kvm) @@ -348,8 +348,7 @@ void kvm_arch_disable_virtualization_cpu(void) static int kvm_loongarch_env_init(void) { - int cpu, order, ret; - void *addr; + int cpu, ret; struct kvm_context *context; vmcs = alloc_percpu(struct kvm_context); @@ -365,30 +364,8 @@ static int kvm_loongarch_env_init(void) return -ENOMEM; } - /* - * PGD register is shared between root kernel and kvm hypervisor. - * So world switch entry should be in DMW area rather than TLB area - * to avoid page fault reenter. - * - * In future if hardware pagetable walking is supported, we won't - * need to copy world switch code to DMW area. - */ - order = get_order(kvm_exception_size + kvm_enter_guest_size); - addr = (void *)__get_free_pages(GFP_KERNEL, order); - if (!addr) { - free_percpu(vmcs); - vmcs = NULL; - kfree(kvm_loongarch_ops); - kvm_loongarch_ops = NULL; - return -ENOMEM; - } - - memcpy(addr, kvm_exc_entry, kvm_exception_size); - memcpy(addr + kvm_exception_size, kvm_enter_guest, kvm_enter_guest_size); - flush_icache_range((unsigned long)addr, (unsigned long)addr + kvm_exception_size + kvm_enter_guest_size); - kvm_loongarch_ops->exc_entry = addr; - kvm_loongarch_ops->enter_guest = addr + kvm_exception_size; - kvm_loongarch_ops->page_order = order; + kvm_loongarch_ops->exc_entry = (void *)kvm_exc_entry; + kvm_loongarch_ops->enter_guest = (void *)kvm_enter_guest; vpid_mask = read_csr_gstat(); vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; @@ -416,22 +393,22 @@ static int kvm_loongarch_env_init(void) /* Register LoongArch PCH-PIC interrupt controller interface. */ ret = kvm_loongarch_register_pch_pic_device(); + if (ret) + return ret; + + /* Register LoongArch DMSINTC interrupt contrroller interface */ + if (cpu_has_msgint) + ret = kvm_loongarch_register_dmsintc_device(); return ret; } static void kvm_loongarch_env_exit(void) { - unsigned long addr; - if (vmcs) free_percpu(vmcs); if (kvm_loongarch_ops) { - if (kvm_loongarch_ops->exc_entry) { - addr = (unsigned long)kvm_loongarch_ops->exc_entry; - free_pages(addr, kvm_loongarch_ops->page_order); - } kfree(kvm_loongarch_ops); } diff --git a/arch/loongarch/kvm/mmu.c b/arch/loongarch/kvm/mmu.c index a7fa458e3360..e104897aa532 100644 --- a/arch/loongarch/kvm/mmu.c +++ b/arch/loongarch/kvm/mmu.c @@ -95,7 +95,7 @@ static int kvm_flush_pte(kvm_pte_t *pte, phys_addr_t addr, kvm_ptw_ctx *ctx) else kvm->stat.pages--; - *pte = ctx->invalid_entry; + kvm_set_pte(pte, ctx->invalid_entry); return 1; } diff --git a/arch/loongarch/kvm/switch.S b/arch/loongarch/kvm/switch.S index f1768b7a6194..936e4ae3e408 100644 --- a/arch/loongarch/kvm/switch.S +++ b/arch/loongarch/kvm/switch.S @@ -4,9 +4,11 @@ */ #include +#include #include #include #include +#include #include #include @@ -100,11 +102,16 @@ * - is still in guest mode, such as pgd table/vmid registers etc, * - will fix with hw page walk enabled in future * load kvm_vcpu from reserved CSR KVM_VCPU_KS, and save a2 to KVM_TEMP_KS + * + * PGD register is shared between root kernel and kvm hypervisor. + * So world switch entry should be in DMW area rather than TLB area + * to avoid page fault re-enter. */ .text + .p2align PAGE_SHIFT .cfi_sections .debug_frame SYM_CODE_START(kvm_exc_entry) - UNWIND_HINT_UNDEFINED + UNWIND_HINT_END_OF_STACK csrwr a2, KVM_TEMP_KS csrrd a2, KVM_VCPU_KS addi.d a2, a2, KVM_VCPU_ARCH @@ -190,8 +197,8 @@ ret_to_host: kvm_restore_host_gpr a2 jr ra -SYM_INNER_LABEL(kvm_exc_entry_end, SYM_L_LOCAL) SYM_CODE_END(kvm_exc_entry) +EXPORT_SYMBOL_FOR_KVM(kvm_exc_entry) /* * int kvm_enter_guest(struct kvm_run *run, struct kvm_vcpu *vcpu) @@ -215,8 +222,8 @@ SYM_FUNC_START(kvm_enter_guest) /* Save kvm_vcpu to kscratch */ csrwr a1, KVM_VCPU_KS kvm_switch_to_guest -SYM_INNER_LABEL(kvm_enter_guest_end, SYM_L_LOCAL) SYM_FUNC_END(kvm_enter_guest) +EXPORT_SYMBOL_FOR_KVM(kvm_enter_guest) SYM_FUNC_START(kvm_save_fpu) fpu_save_csr a0 t1 @@ -224,6 +231,7 @@ SYM_FUNC_START(kvm_save_fpu) fpu_save_cc a0 t1 t2 jr ra SYM_FUNC_END(kvm_save_fpu) +EXPORT_SYMBOL_FOR_KVM(kvm_save_fpu) SYM_FUNC_START(kvm_restore_fpu) fpu_restore_double a0 t1 @@ -231,6 +239,7 @@ SYM_FUNC_START(kvm_restore_fpu) fpu_restore_cc a0 t1 t2 jr ra SYM_FUNC_END(kvm_restore_fpu) +EXPORT_SYMBOL_FOR_KVM(kvm_restore_fpu) #ifdef CONFIG_CPU_HAS_LSX SYM_FUNC_START(kvm_save_lsx) @@ -239,6 +248,7 @@ SYM_FUNC_START(kvm_save_lsx) lsx_save_data a0 t1 jr ra SYM_FUNC_END(kvm_save_lsx) +EXPORT_SYMBOL_FOR_KVM(kvm_save_lsx) SYM_FUNC_START(kvm_restore_lsx) lsx_restore_data a0 t1 @@ -246,6 +256,7 @@ SYM_FUNC_START(kvm_restore_lsx) fpu_restore_csr a0 t1 t2 jr ra SYM_FUNC_END(kvm_restore_lsx) +EXPORT_SYMBOL_FOR_KVM(kvm_restore_lsx) #endif #ifdef CONFIG_CPU_HAS_LASX @@ -255,6 +266,7 @@ SYM_FUNC_START(kvm_save_lasx) lasx_save_data a0 t1 jr ra SYM_FUNC_END(kvm_save_lasx) +EXPORT_SYMBOL_FOR_KVM(kvm_save_lasx) SYM_FUNC_START(kvm_restore_lasx) lasx_restore_data a0 t1 @@ -262,10 +274,8 @@ SYM_FUNC_START(kvm_restore_lasx) fpu_restore_csr a0 t1 t2 jr ra SYM_FUNC_END(kvm_restore_lasx) +EXPORT_SYMBOL_FOR_KVM(kvm_restore_lasx) #endif - .section ".rodata" -SYM_DATA(kvm_exception_size, .quad kvm_exc_entry_end - kvm_exc_entry) -SYM_DATA(kvm_enter_guest_size, .quad kvm_enter_guest_end - kvm_enter_guest) #ifdef CONFIG_CPU_HAS_LBT STACK_FRAME_NON_STANDARD kvm_restore_fpu diff --git a/arch/loongarch/kvm/timer.c b/arch/loongarch/kvm/timer.c index 29c2aaba63c3..8356fce0043f 100644 --- a/arch/loongarch/kvm/timer.c +++ b/arch/loongarch/kvm/timer.c @@ -96,15 +96,21 @@ void kvm_restore_timer(struct kvm_vcpu *vcpu) * and set CSR TVAL with -1 */ write_gcsr_timertick(0); - __delay(2); /* Wait cycles until timer interrupt injected */ /* * Writing CSR_TINTCLR_TI to LOONGARCH_CSR_TINTCLR will clear * timer interrupt, and CSR TVAL keeps unchanged with -1, it * avoids spurious timer interrupt */ - if (!(estat & CPU_TIMER)) + if (!(estat & CPU_TIMER)) { + __delay(2); /* Wait cycles until timer interrupt injected */ + + /* Write TVAL with max value if no TI shot */ + estat = kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT); + if (!(estat & CPU_TIMER)) + write_gcsr_timertick(CSR_TCFG_VAL); gcsr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR); + } return; } diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 831f381a8fd1..e28084c49e68 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -149,14 +149,6 @@ static void kvm_lose_pmu(struct kvm_vcpu *vcpu) kvm_restore_host_pmu(vcpu); } -static void kvm_check_pmu(struct kvm_vcpu *vcpu) -{ - if (kvm_check_request(KVM_REQ_PMU, vcpu)) { - kvm_own_pmu(vcpu); - vcpu->arch.aux_inuse |= KVM_LARCH_PMU; - } -} - static void kvm_update_stolen_time(struct kvm_vcpu *vcpu) { u32 version; @@ -232,6 +224,15 @@ static int kvm_check_requests(struct kvm_vcpu *vcpu) static void kvm_late_check_requests(struct kvm_vcpu *vcpu) { lockdep_assert_irqs_disabled(); + + if (!kvm_request_pending(vcpu)) + return; + + if (kvm_check_request(KVM_REQ_PMU, vcpu)) { + kvm_own_pmu(vcpu); + vcpu->arch.aux_inuse |= KVM_LARCH_PMU; + } + if (kvm_check_request(KVM_REQ_TLB_FLUSH_GPA, vcpu)) if (vcpu->arch.flush_gpa != INVALID_GPA) { kvm_flush_tlb_gpa(vcpu, vcpu->arch.flush_gpa); @@ -312,7 +313,6 @@ static int kvm_pre_enter_guest(struct kvm_vcpu *vcpu) /* Make sure the vcpu mode has been written */ smp_store_mb(vcpu->mode, IN_GUEST_MODE); kvm_check_vpid(vcpu); - kvm_check_pmu(vcpu); /* * Called after function kvm_check_vpid() @@ -320,7 +320,6 @@ static int kvm_pre_enter_guest(struct kvm_vcpu *vcpu) * and it may also clear KVM_REQ_TLB_FLUSH_GPA pending bit */ kvm_late_check_requests(vcpu); - vcpu->arch.host_eentry = csr_read64(LOONGARCH_CSR_EENTRY); /* Clear KVM_LARCH_SWCSR_LATEST as CSR will change when enter guest */ vcpu->arch.aux_inuse &= ~KVM_LARCH_SWCSR_LATEST; @@ -402,7 +401,7 @@ bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu) val = gcsr_read(LOONGARCH_CSR_CRMD); preempt_enable(); - return (val & CSR_PRMD_PPLV) == PLV_KERN; + return (val & CSR_CRMD_PLV) == PLV_KERN; } #ifdef CONFIG_GUEST_PERF_EVENTS @@ -1628,9 +1627,11 @@ static int _kvm_vcpu_load(struct kvm_vcpu *vcpu, int cpu) * If not, any old guest state from this vCPU will have been clobbered. */ context = per_cpu_ptr(vcpu->kvm->arch.vmcs, cpu); - if (migrated || (context->last_vcpu != vcpu)) + if (migrated || (context->last_vcpu != vcpu)) { + context->last_vcpu = vcpu; vcpu->arch.aux_inuse &= ~KVM_LARCH_HWCSR_USABLE; - context->last_vcpu = vcpu; + vcpu->arch.host_eentry = csr_read64(LOONGARCH_CSR_EENTRY); + } /* Restore timer state regardless */ kvm_restore_timer(vcpu); @@ -1698,6 +1699,7 @@ static int _kvm_vcpu_load(struct kvm_vcpu *vcpu, int cpu) /* Restore Root.GINTC from unused Guest.GINTC register */ write_csr_gintc(csr->csrs[LOONGARCH_CSR_GINTC]); + write_csr_gstat(csr->csrs[LOONGARCH_CSR_GSTAT]); /* * We should clear linked load bit to break interrupted atomics. This @@ -1793,6 +1795,7 @@ static int _kvm_vcpu_put(struct kvm_vcpu *vcpu, int cpu) kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ISR3); } + csr->csrs[LOONGARCH_CSR_GSTAT] = read_csr_gstat(); vcpu->arch.aux_inuse |= KVM_LARCH_SWCSR_LATEST; out: diff --git a/arch/loongarch/kvm/vm.c b/arch/loongarch/kvm/vm.c index 8cc5ee1c53ef..1317c718f896 100644 --- a/arch/loongarch/kvm/vm.c +++ b/arch/loongarch/kvm/vm.c @@ -125,7 +125,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) r = 1; break; case KVM_CAP_NR_VCPUS: - r = num_online_cpus(); + r = min_t(unsigned int, num_online_cpus(), KVM_MAX_VCPUS); break; case KVM_CAP_MAX_VCPUS: r = KVM_MAX_VCPUS; diff --git a/arch/loongarch/lib/Makefile b/arch/loongarch/lib/Makefile index ccea3bbd4353..aa21b3818ec9 100644 --- a/arch/loongarch/lib/Makefile +++ b/arch/loongarch/lib/Makefile @@ -3,11 +3,11 @@ # Makefile for LoongArch-specific library files. # -lib-y += delay.o memset.o memcpy.o memmove.o \ - clear_user.o copy_user.o csum.o dump_tlb.o unaligned.o +lib-y += delay.o clear_user.o copy_user.o dump_tlb.o unaligned.o + +lib-$(CONFIG_32BIT) += bswapsi.o bswapdi.o +lib-$(CONFIG_64BIT) += memset.o memcpy.o memmove.o csum.o obj-$(CONFIG_ARCH_SUPPORTS_INT128) += tishift.o -obj-$(CONFIG_CPU_HAS_LSX) += xor_simd.o xor_simd_glue.o - obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o diff --git a/arch/loongarch/lib/xor_simd_glue.c b/arch/loongarch/lib/xor_simd_glue.c deleted file mode 100644 index 393f689dbcf6..000000000000 --- a/arch/loongarch/lib/xor_simd_glue.c +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * LoongArch SIMD XOR operations - * - * Copyright (C) 2023 WANG Xuerui - */ - -#include -#include -#include -#include -#include "xor_simd.h" - -#define MAKE_XOR_GLUE_2(flavor) \ -void xor_##flavor##_2(unsigned long bytes, unsigned long * __restrict p1, \ - const unsigned long * __restrict p2) \ -{ \ - kernel_fpu_begin(); \ - __xor_##flavor##_2(bytes, p1, p2); \ - kernel_fpu_end(); \ -} \ -EXPORT_SYMBOL_GPL(xor_##flavor##_2) - -#define MAKE_XOR_GLUE_3(flavor) \ -void xor_##flavor##_3(unsigned long bytes, unsigned long * __restrict p1, \ - const unsigned long * __restrict p2, \ - const unsigned long * __restrict p3) \ -{ \ - kernel_fpu_begin(); \ - __xor_##flavor##_3(bytes, p1, p2, p3); \ - kernel_fpu_end(); \ -} \ -EXPORT_SYMBOL_GPL(xor_##flavor##_3) - -#define MAKE_XOR_GLUE_4(flavor) \ -void xor_##flavor##_4(unsigned long bytes, unsigned long * __restrict p1, \ - const unsigned long * __restrict p2, \ - const unsigned long * __restrict p3, \ - const unsigned long * __restrict p4) \ -{ \ - kernel_fpu_begin(); \ - __xor_##flavor##_4(bytes, p1, p2, p3, p4); \ - kernel_fpu_end(); \ -} \ -EXPORT_SYMBOL_GPL(xor_##flavor##_4) - -#define MAKE_XOR_GLUE_5(flavor) \ -void xor_##flavor##_5(unsigned long bytes, unsigned long * __restrict p1, \ - const unsigned long * __restrict p2, \ - const unsigned long * __restrict p3, \ - const unsigned long * __restrict p4, \ - const unsigned long * __restrict p5) \ -{ \ - kernel_fpu_begin(); \ - __xor_##flavor##_5(bytes, p1, p2, p3, p4, p5); \ - kernel_fpu_end(); \ -} \ -EXPORT_SYMBOL_GPL(xor_##flavor##_5) - -#define MAKE_XOR_GLUES(flavor) \ - MAKE_XOR_GLUE_2(flavor); \ - MAKE_XOR_GLUE_3(flavor); \ - MAKE_XOR_GLUE_4(flavor); \ - MAKE_XOR_GLUE_5(flavor) - -#ifdef CONFIG_CPU_HAS_LSX -MAKE_XOR_GLUES(lsx); -#endif - -#ifdef CONFIG_CPU_HAS_LASX -MAKE_XOR_GLUES(lasx); -#endif diff --git a/arch/loongarch/mm/Makefile b/arch/loongarch/mm/Makefile index 278be2c8fc36..2aae3773de77 100644 --- a/arch/loongarch/mm/Makefile +++ b/arch/loongarch/mm/Makefile @@ -7,6 +7,7 @@ obj-y += init.o cache.o tlb.o tlbex.o extable.o \ fault.o ioremap.o maccess.o mmap.o pgtable.o \ page.o pageattr.o +obj-$(CONFIG_HIGHMEM) += highmem.o obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o obj-$(CONFIG_KASAN) += kasan_init.o diff --git a/arch/loongarch/mm/cache.c b/arch/loongarch/mm/cache.c index 496916845ff7..06dc570eb429 100644 --- a/arch/loongarch/mm/cache.c +++ b/arch/loongarch/mm/cache.c @@ -31,16 +31,6 @@ void cache_error_setup(void) set_merr_handler(0x0, &except_vec_cex, 0x80); } -/* - * LoongArch maintains ICache/DCache coherency by hardware, - * we just need "ibar" to avoid instruction hazard here. - */ -void local_flush_icache_range(unsigned long start, unsigned long end) -{ - asm volatile ("\tibar 0\n"::); -} -EXPORT_SYMBOL(local_flush_icache_range); - static void flush_cache_leaf(unsigned int leaf) { int i, j, nr_nodes; diff --git a/arch/loongarch/mm/highmem.c b/arch/loongarch/mm/highmem.c new file mode 100644 index 000000000000..8a5789ee6842 --- /dev/null +++ b/arch/loongarch/mm/highmem.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include + +void kmap_flush_tlb(unsigned long addr) +{ + flush_tlb_one(addr); +} +EXPORT_SYMBOL(kmap_flush_tlb); diff --git a/arch/loongarch/mm/init.c b/arch/loongarch/mm/init.c index 00f3822b6e47..3f9ab54114c5 100644 --- a/arch/loongarch/mm/init.c +++ b/arch/loongarch/mm/init.c @@ -36,20 +36,6 @@ #include #include -void copy_user_highpage(struct page *to, struct page *from, - unsigned long vaddr, struct vm_area_struct *vma) -{ - void *vfrom, *vto; - - vfrom = kmap_local_page(from); - vto = kmap_local_page(to); - copy_page(vto, vfrom); - kunmap_local(vfrom); - kunmap_local(vto); - /* Make sure this page is cleared on other CPU's too before using it */ - smp_wmb(); -} - int __ref page_is_ram(unsigned long pfn) { unsigned long addr = PFN_PHYS(pfn); @@ -63,6 +49,9 @@ void __init arch_zone_limits_init(unsigned long *max_zone_pfns) max_zone_pfns[ZONE_DMA32] = MAX_DMA32_PFN; #endif max_zone_pfns[ZONE_NORMAL] = max_low_pfn; +#ifdef CONFIG_HIGHMEM + max_zone_pfns[ZONE_HIGHMEM] = max_pfn; +#endif } void __ref free_initmem(void) @@ -70,6 +59,50 @@ void __ref free_initmem(void) free_initmem_default(POISON_FREE_INITMEM); } +#ifdef CONFIG_HIGHMEM + +void __init fixrange_init(unsigned long start, unsigned long end, pgd_t *pgd_base) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + int i, j, k; + int ptrs_per_pgd; + unsigned long vaddr; + + vaddr = start; + i = pgd_index(vaddr); + j = pud_index(vaddr); + k = pmd_index(vaddr); + pgd = pgd_base + i; + ptrs_per_pgd = min((1 << (BITS_PER_LONG - PGDIR_SHIFT)), PTRS_PER_PGD); + + for ( ; (i < ptrs_per_pgd) && (vaddr < end); pgd++, i++) { + pud = (pud_t *)pgd; + for ( ; (j < PTRS_PER_PUD) && (vaddr < end); pud++, j++) { + pmd = (pmd_t *)pud; + for (; (k < PTRS_PER_PMD) && (vaddr < end); pmd++, k++) { + if (pmd_none(*pmd)) { + pte = (pte_t *) memblock_alloc_low(PAGE_SIZE, PAGE_SIZE); + if (!pte) + panic("%s: Failed to allocate %lu bytes align=%lx\n", + __func__, PAGE_SIZE, PAGE_SIZE); + + kernel_pte_init(pte); + set_pmd(pmd, __pmd((unsigned long)pte)); + BUG_ON(pte != pte_offset_kernel(pmd, 0)); + } + vaddr += PMD_SIZE; + } + k = 0; + } + j = 0; + } +} + +#endif + #ifdef CONFIG_MEMORY_HOTPLUG int arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *params) { diff --git a/arch/loongarch/mm/pgtable.c b/arch/loongarch/mm/pgtable.c index 352d9b2e02ab..4ee188e38fed 100644 --- a/arch/loongarch/mm/pgtable.c +++ b/arch/loongarch/mm/pgtable.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -144,6 +145,15 @@ void set_pmd_at(struct mm_struct *mm, unsigned long addr, void __init pagetable_init(void) { +#ifdef CONFIG_HIGHMEM + unsigned long vaddr; + pgd_t *pgd; + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; +#endif + /* Initialize the entire pgd. */ pgd_init(swapper_pg_dir); pgd_init(invalid_pg_dir); @@ -153,4 +163,21 @@ void __init pagetable_init(void) #ifndef __PAGETABLE_PMD_FOLDED pmd_init(invalid_pmd_table); #endif + +#ifdef CONFIG_HIGHMEM + /* Permanent kmaps */ + vaddr = PKMAP_BASE; + fixrange_init(vaddr & PMD_MASK, vaddr + PAGE_SIZE * LAST_PKMAP, swapper_pg_dir); + + pgd = swapper_pg_dir + pgd_index(vaddr); + p4d = p4d_offset(pgd, vaddr); + pud = pud_offset(p4d, vaddr); + pmd = pmd_offset(pud, vaddr); + pte = pte_offset_kernel(pmd, vaddr); + pkmap_page_table = pte; + + /* Fixed mappings */ + vaddr = __fix_to_virt(__end_of_fixed_addresses - 1); + fixrange_init(vaddr & PMD_MASK, vaddr + FIXADDR_SIZE, swapper_pg_dir); +#endif } diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index 9cb796e16379..24913dc7f4e8 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -344,7 +344,13 @@ toofar: #undef jmp_offset } -static void emit_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) +static void emit_store_stack_imm64(struct jit_ctx *ctx, int reg, int stack_off, u64 imm64) +{ + move_imm(ctx, reg, imm64, false); + emit_insn(ctx, std, reg, LOONGARCH_GPR_FP, stack_off); +} + +static int emit_atomic_rmw(const struct bpf_insn *insn, struct jit_ctx *ctx) { const u8 t1 = LOONGARCH_GPR_T1; const u8 t2 = LOONGARCH_GPR_T2; @@ -363,10 +369,28 @@ static void emit_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) switch (imm) { /* lock *(size *)(dst + off) = src */ case BPF_ADD: - if (isdw) - emit_insn(ctx, amaddd, t2, t1, src); - else + switch (BPF_SIZE(insn->code)) { + case BPF_B: + if (!cpu_has_lam_bh) { + pr_err_once("bpf-jit: amadd.b instruction is not supported\n"); + return -EINVAL; + } + emit_insn(ctx, amaddb, t2, t1, src); + break; + case BPF_H: + if (!cpu_has_lam_bh) { + pr_err_once("bpf-jit: amadd.h instruction is not supported\n"); + return -EINVAL; + } + emit_insn(ctx, amaddh, t2, t1, src); + break; + case BPF_W: emit_insn(ctx, amaddw, t2, t1, src); + break; + case BPF_DW: + emit_insn(ctx, amaddd, t2, t1, src); + break; + } break; case BPF_AND: if (isdw) @@ -388,11 +412,30 @@ static void emit_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) break; /* src = atomic_fetch_(dst + off, src) */ case BPF_ADD | BPF_FETCH: - if (isdw) { - emit_insn(ctx, amaddd, src, t1, t3); - } else { + switch (BPF_SIZE(insn->code)) { + case BPF_B: + if (!cpu_has_lam_bh) { + pr_err_once("bpf-jit: amadd.b instruction is not supported\n"); + return -EINVAL; + } + emit_insn(ctx, amaddb, src, t1, t3); + emit_zext_32(ctx, src, true); + break; + case BPF_H: + if (!cpu_has_lam_bh) { + pr_err_once("bpf-jit: amadd.h instruction is not supported\n"); + return -EINVAL; + } + emit_insn(ctx, amaddh, src, t1, t3); + emit_zext_32(ctx, src, true); + break; + case BPF_W: emit_insn(ctx, amaddw, src, t1, t3); emit_zext_32(ctx, src, true); + break; + case BPF_DW: + emit_insn(ctx, amaddd, src, t1, t3); + break; } break; case BPF_AND | BPF_FETCH: @@ -421,11 +464,30 @@ static void emit_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) break; /* src = atomic_xchg(dst + off, src); */ case BPF_XCHG: - if (isdw) { - emit_insn(ctx, amswapd, src, t1, t3); - } else { + switch (BPF_SIZE(insn->code)) { + case BPF_B: + if (!cpu_has_lam_bh) { + pr_err_once("bpf-jit: amswap.b instruction is not supported\n"); + return -EINVAL; + } + emit_insn(ctx, amswapb, src, t1, t3); + emit_zext_32(ctx, src, true); + break; + case BPF_H: + if (!cpu_has_lam_bh) { + pr_err_once("bpf-jit: amswap.h instruction is not supported\n"); + return -EINVAL; + } + emit_insn(ctx, amswaph, src, t1, t3); + emit_zext_32(ctx, src, true); + break; + case BPF_W: emit_insn(ctx, amswapw, src, t1, t3); emit_zext_32(ctx, src, true); + break; + case BPF_DW: + emit_insn(ctx, amswapd, src, t1, t3); + break; } break; /* r0 = atomic_cmpxchg(dst + off, r0, src); */ @@ -448,7 +510,105 @@ static void emit_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) emit_zext_32(ctx, r0, true); } break; + default: + pr_err_once("bpf-jit: invalid atomic read-modify-write opcode %02x\n", imm); + return -EINVAL; } + + return 0; +} + +static int emit_atomic_ld_st(const struct bpf_insn *insn, struct jit_ctx *ctx) +{ + const u8 t1 = LOONGARCH_GPR_T1; + const u8 src = regmap[insn->src_reg]; + const u8 dst = regmap[insn->dst_reg]; + const s16 off = insn->off; + const s32 imm = insn->imm; + + switch (imm) { + /* dst_reg = load_acquire(src_reg + off16) */ + case BPF_LOAD_ACQ: + switch (BPF_SIZE(insn->code)) { + case BPF_B: + if (is_signed_imm12(off)) { + emit_insn(ctx, ldbu, dst, src, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, ldxbu, dst, src, t1); + } + break; + case BPF_H: + if (is_signed_imm12(off)) { + emit_insn(ctx, ldhu, dst, src, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, ldxhu, dst, src, t1); + } + break; + case BPF_W: + if (is_signed_imm12(off)) { + emit_insn(ctx, ldwu, dst, src, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, ldxwu, dst, src, t1); + } + break; + case BPF_DW: + if (is_signed_imm12(off)) { + emit_insn(ctx, ldd, dst, src, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, ldxd, dst, src, t1); + } + break; + } + emit_insn(ctx, dbar, 0b10100); + break; + /* store_release(dst_reg + off16, src_reg) */ + case BPF_STORE_REL: + emit_insn(ctx, dbar, 0b10010); + switch (BPF_SIZE(insn->code)) { + case BPF_B: + if (is_signed_imm12(off)) { + emit_insn(ctx, stb, src, dst, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, stxb, src, dst, t1); + } + break; + case BPF_H: + if (is_signed_imm12(off)) { + emit_insn(ctx, sth, src, dst, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, stxh, src, dst, t1); + } + break; + case BPF_W: + if (is_signed_imm12(off)) { + emit_insn(ctx, stw, src, dst, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, stxw, src, dst, t1); + } + break; + case BPF_DW: + if (is_signed_imm12(off)) { + emit_insn(ctx, std, src, dst, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, stxd, src, dst, t1); + } + break; + } + break; + default: + pr_err_once("bpf-jit: invalid atomic load/store opcode %02x\n", imm); + return -EINVAL; + } + + return 0; } static bool is_signed_bpf_cond(u8 cond) @@ -1254,9 +1414,17 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext return ret; break; + /* Atomics */ + case BPF_STX | BPF_ATOMIC | BPF_B: + case BPF_STX | BPF_ATOMIC | BPF_H: case BPF_STX | BPF_ATOMIC | BPF_W: case BPF_STX | BPF_ATOMIC | BPF_DW: - emit_atomic(insn, ctx); + if (!bpf_atomic_is_load_store(insn)) + ret = emit_atomic_rmw(insn, ctx); + else + ret = emit_atomic_ld_st(insn, ctx); + if (ret) + return ret; break; /* Speculation barrier */ @@ -1466,26 +1634,46 @@ int bpf_arch_text_invalidate(void *dst, size_t len) return ret; } -static void store_args(struct jit_ctx *ctx, int nargs, int args_off) +static void store_args(struct jit_ctx *ctx, int nr_arg_slots, int args_off) { int i; - for (i = 0; i < nargs; i++) { - emit_insn(ctx, std, LOONGARCH_GPR_A0 + i, LOONGARCH_GPR_FP, -args_off); + for (i = 0; i < nr_arg_slots; i++) { + if (i < LOONGARCH_MAX_REG_ARGS) + emit_insn(ctx, std, LOONGARCH_GPR_A0 + i, LOONGARCH_GPR_FP, -args_off); + else { + /* Skip slots for T0 and FP of traced function */ + emit_insn(ctx, ldd, LOONGARCH_GPR_T1, LOONGARCH_GPR_FP, + 16 + (i - LOONGARCH_MAX_REG_ARGS) * 8); + emit_insn(ctx, std, LOONGARCH_GPR_T1, LOONGARCH_GPR_FP, -args_off); + } args_off -= 8; } } -static void restore_args(struct jit_ctx *ctx, int nargs, int args_off) +static void restore_args(struct jit_ctx *ctx, int nr_reg_args, int args_off) { int i; - for (i = 0; i < nargs; i++) { + for (i = 0; i < nr_reg_args; i++) { emit_insn(ctx, ldd, LOONGARCH_GPR_A0 + i, LOONGARCH_GPR_FP, -args_off); args_off -= 8; } } +static void restore_stk_args(struct jit_ctx *ctx, int nr_stk_args, int args_off, int stk_args_off) +{ + int i; + + for (i = 0; i < nr_stk_args; i++) { + emit_insn(ctx, ldd, LOONGARCH_GPR_T1, LOONGARCH_GPR_FP, + -(args_off - LOONGARCH_MAX_REG_ARGS * 8)); + emit_insn(ctx, std, LOONGARCH_GPR_T1, LOONGARCH_GPR_FP, -stk_args_off); + args_off -= 8; + stk_args_off -= 8; + } +} + static int invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, int args_off, int retval_off, int run_ctx_off, bool save_ret) { @@ -1494,12 +1682,11 @@ static int invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, struct bpf_prog *p = l->link.prog; int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie); - if (l->cookie) { - move_imm(ctx, LOONGARCH_GPR_T1, l->cookie, false); - emit_insn(ctx, std, LOONGARCH_GPR_T1, LOONGARCH_GPR_FP, -run_ctx_off + cookie_off); - } else { + if (l->cookie) + emit_store_stack_imm64(ctx, LOONGARCH_GPR_T1, + -run_ctx_off + cookie_off, l->cookie); + else emit_insn(ctx, std, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_FP, -run_ctx_off + cookie_off); - } /* arg1: prog */ move_imm(ctx, LOONGARCH_GPR_A0, (const s64)p, false); @@ -1550,18 +1737,27 @@ static int invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, return ret; } -static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, - int args_off, int retval_off, int run_ctx_off, u32 **branches) +static int invoke_bpf(struct jit_ctx *ctx, struct bpf_tramp_links *tl, + int args_off, int retval_off, int run_ctx_off, + int func_meta_off, bool save_ret, u64 func_meta, int cookie_off) { - int i; + int i, cur_cookie = (cookie_off - args_off) / 8; - emit_insn(ctx, std, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_FP, -retval_off); for (i = 0; i < tl->nr_links; i++) { - invoke_bpf_prog(ctx, tl->links[i], args_off, retval_off, run_ctx_off, true); - emit_insn(ctx, ldd, LOONGARCH_GPR_T1, LOONGARCH_GPR_FP, -retval_off); - branches[i] = (u32 *)ctx->image + ctx->idx; - emit_insn(ctx, nop); + int err; + + if (bpf_prog_calls_session_cookie(tl->links[i])) { + u64 meta = func_meta | ((u64)cur_cookie << BPF_TRAMP_COOKIE_INDEX_SHIFT); + + emit_store_stack_imm64(ctx, LOONGARCH_GPR_T1, -func_meta_off, meta); + cur_cookie--; + } + err = invoke_bpf_prog(ctx, tl->links[i], args_off, retval_off, run_ctx_off, save_ret); + if (err) + return err; } + + return 0; } void *arch_alloc_bpf_trampoline(unsigned int size) @@ -1615,8 +1811,10 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i void *func_addr, u32 flags) { int i, ret, save_ret; - int stack_size, nargs; - int retval_off, args_off, nargs_off, ip_off, run_ctx_off, sreg_off, tcc_ptr_off; + int cookie_cnt, cookie_off; + int stack_size, args_off, stk_args_off, nr_arg_slots = 0; + int retval_off, func_meta_off, ip_off, run_ctx_off, sreg_off, tcc_ptr_off; + unsigned long long func_meta; bool is_struct_ops = flags & BPF_TRAMP_F_INDIRECT; void *orig_call = func_addr; struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY]; @@ -1634,30 +1832,44 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i * FP - 16 [ FP of traced func ] frame pointer of traced * function * - * FP - retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or - * BPF_TRAMP_F_RET_FENTRY_RET - * [ argN ] - * [ ... ] - * FP - args_off [ arg1 ] + * FP - retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or + * BPF_TRAMP_F_RET_FENTRY_RET + * [ arg regN ] + * [ ... ] + * FP - args_off [ arg reg1 ] * - * FP - nargs_off [ regs count ] + * FP - func_meta_off [ regs count, etc ] * - * FP - ip_off [ traced func ] BPF_TRAMP_F_IP_ARG + * FP - ip_off [ traced func ] BPF_TRAMP_F_IP_ARG * - * FP - run_ctx_off [ bpf_tramp_run_ctx ] + * [ stack cookie N ] + * [ ... ] + * FP - cookie_off [ stack cookie 1 ] * - * FP - sreg_off [ callee saved reg ] + * FP - run_ctx_off [ bpf_tramp_run_ctx ] * - * FP - tcc_ptr_off [ tail_call_cnt_ptr ] + * FP - sreg_off [ callee saved reg ] + * + * FP - tcc_ptr_off [ tail_call_cnt_ptr ] + * + * [ stack_argN ] + * [ ... ] + * FP - stk_args_off [ stack_arg1 ] BPF_TRAMP_F_CALL_ORIG */ - if (m->nr_args > LOONGARCH_MAX_REG_ARGS) + if (m->nr_args > MAX_BPF_FUNC_ARGS) return -ENOTSUPP; - /* FIXME: No support of struct argument */ + /* Extra registers for struct arguments */ for (i = 0; i < m->nr_args; i++) { - if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) - return -ENOTSUPP; + /* + * The struct argument size is at most 16 bytes, + * enforced by the verifier. The struct argument + * may be passed in a pair of registers if its + * size is more than 8 bytes and no more than 16 + * bytes. + */ + nr_arg_slots += round_up(m->arg_size[i], 8) / 8; } if (flags & (BPF_TRAMP_F_ORIG_STACK | BPF_TRAMP_F_SHARE_IPMODIFY)) @@ -1673,13 +1885,12 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i retval_off = stack_size; /* Room of trampoline frame to store args */ - nargs = m->nr_args; - stack_size += nargs * 8; + stack_size += nr_arg_slots * 8; args_off = stack_size; - /* Room of trampoline frame to store args number */ + /* Room of function metadata, such as regs count */ stack_size += 8; - nargs_off = stack_size; + func_meta_off = stack_size; /* Room of trampoline frame to store ip address */ if (flags & BPF_TRAMP_F_IP_ARG) { @@ -1687,6 +1898,12 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i ip_off = stack_size; } + cookie_cnt = bpf_fsession_cookie_cnt(tlinks); + + /* Room for session cookies */ + stack_size += cookie_cnt * 8; + cookie_off = stack_size; + /* Room of trampoline frame to store struct bpf_tramp_run_ctx */ stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8); run_ctx_off = stack_size; @@ -1700,8 +1917,14 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i tcc_ptr_off = stack_size; } + if ((flags & BPF_TRAMP_F_CALL_ORIG) && (nr_arg_slots - LOONGARCH_MAX_REG_ARGS > 0)) + stack_size += (nr_arg_slots - LOONGARCH_MAX_REG_ARGS) * 8; + stack_size = round_up(stack_size, 16); + /* Room for args on stack must be at the top of stack */ + stk_args_off = stack_size; + if (is_struct_ops) { /* * For the trampoline called directly, just handle @@ -1737,17 +1960,24 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i emit_insn(ctx, std, LOONGARCH_GPR_S1, LOONGARCH_GPR_FP, -sreg_off); /* store ip address of the traced function */ - if (flags & BPF_TRAMP_F_IP_ARG) { - move_imm(ctx, LOONGARCH_GPR_T1, (const s64)func_addr, false); - emit_insn(ctx, std, LOONGARCH_GPR_T1, LOONGARCH_GPR_FP, -ip_off); + if (flags & BPF_TRAMP_F_IP_ARG) + emit_store_stack_imm64(ctx, LOONGARCH_GPR_T1, -ip_off, (u64)func_addr); + + /* store arg regs count */ + func_meta = nr_arg_slots; + emit_store_stack_imm64(ctx, LOONGARCH_GPR_T1, -func_meta_off, func_meta); + + store_args(ctx, nr_arg_slots, args_off); + + if (bpf_fsession_cnt(tlinks)) { + /* clear all session cookies' value */ + for (i = 0; i < cookie_cnt; i++) + emit_insn(ctx, std, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_FP, -cookie_off + 8 * i); + + /* clear return value to make sure fentry always get 0 */ + emit_insn(ctx, std, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_FP, -retval_off); } - /* store nargs number */ - move_imm(ctx, LOONGARCH_GPR_T1, nargs, false); - emit_insn(ctx, std, LOONGARCH_GPR_T1, LOONGARCH_GPR_FP, -nargs_off); - - store_args(ctx, nargs, args_off); - /* To traced function */ /* Ftrace jump skips 2 NOP instructions */ if (is_kernel_text((unsigned long)orig_call) || @@ -1764,9 +1994,9 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i return ret; } - for (i = 0; i < fentry->nr_links; i++) { - ret = invoke_bpf_prog(ctx, fentry->links[i], args_off, retval_off, - run_ctx_off, flags & BPF_TRAMP_F_RET_FENTRY_RET); + if (fentry->nr_links) { + ret = invoke_bpf(ctx, fentry, args_off, retval_off, run_ctx_off, func_meta_off, + flags & BPF_TRAMP_F_RET_FENTRY_RET, func_meta, cookie_off); if (ret) return ret; } @@ -1775,11 +2005,21 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i if (!branches) return -ENOMEM; - invoke_bpf_mod_ret(ctx, fmod_ret, args_off, retval_off, run_ctx_off, branches); + emit_insn(ctx, std, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_FP, -retval_off); + for (i = 0; i < fmod_ret->nr_links; i++) { + ret = invoke_bpf_prog(ctx, fmod_ret->links[i], + args_off, retval_off, run_ctx_off, true); + if (ret) + goto out; + emit_insn(ctx, ldd, LOONGARCH_GPR_T1, LOONGARCH_GPR_FP, -retval_off); + branches[i] = (u32 *)ctx->image + ctx->idx; + emit_insn(ctx, nop); + } } if (flags & BPF_TRAMP_F_CALL_ORIG) { - restore_args(ctx, m->nr_args, args_off); + restore_args(ctx, min_t(int, nr_arg_slots, LOONGARCH_MAX_REG_ARGS), args_off); + restore_stk_args(ctx, nr_arg_slots - LOONGARCH_MAX_REG_ARGS, args_off, stk_args_off); if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_FP, -tcc_ptr_off); @@ -1800,8 +2040,14 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i *branches[i] = larch_insn_gen_bne(LOONGARCH_GPR_T1, LOONGARCH_GPR_ZERO, offset); } - for (i = 0; i < fexit->nr_links; i++) { - ret = invoke_bpf_prog(ctx, fexit->links[i], args_off, retval_off, run_ctx_off, false); + /* Set "is_return" flag for fsession */ + func_meta |= (1ULL << BPF_TRAMP_IS_RETURN_SHIFT); + if (bpf_fsession_cnt(tlinks)) + emit_store_stack_imm64(ctx, LOONGARCH_GPR_T1, -func_meta_off, func_meta); + + if (fexit->nr_links) { + ret = invoke_bpf(ctx, fexit, args_off, retval_off, run_ctx_off, + func_meta_off, false, func_meta, cookie_off); if (ret) goto out; } @@ -1815,7 +2061,7 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i } if (flags & BPF_TRAMP_F_RESTORE_REGS) - restore_args(ctx, m->nr_args, args_off); + restore_args(ctx, min_t(int, nr_arg_slots, LOONGARCH_MAX_REG_ARGS), args_off); if (save_ret) { emit_insn(ctx, ldd, regmap[BPF_REG_0], LOONGARCH_GPR_FP, -(retval_off - 8)); @@ -1920,45 +2166,28 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, return ret < 0 ? ret : ret * LOONGARCH_INSN_SIZE; } -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) { - bool tmp_blinded = false, extra_pass = false; + bool extra_pass = false; u8 *image_ptr, *ro_image_ptr; int image_size, prog_size, extable_size; struct jit_ctx ctx; struct jit_data *jit_data; struct bpf_binary_header *header; struct bpf_binary_header *ro_header; - struct bpf_prog *tmp, *orig_prog = prog; /* * If BPF JIT was not enabled then we must fall back to * the interpreter. */ if (!prog->jit_requested) - return orig_prog; - - tmp = bpf_jit_blind_constants(prog); - /* - * If blinding was requested and we failed during blinding, - * we must fall back to the interpreter. Otherwise, we save - * the new JITed code. - */ - if (IS_ERR(tmp)) - return orig_prog; - - if (tmp != prog) { - tmp_blinded = true; - prog = tmp; - } + return prog; jit_data = prog->aux->jit_data; if (!jit_data) { jit_data = kzalloc_obj(*jit_data); - if (!jit_data) { - prog = orig_prog; - goto out; - } + if (!jit_data) + return prog; prog->aux->jit_data = jit_data; } if (jit_data->ctx.offset) { @@ -1978,17 +2207,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) ctx.user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena); ctx.offset = kvcalloc(prog->len + 1, sizeof(u32), GFP_KERNEL); - if (ctx.offset == NULL) { - prog = orig_prog; + if (ctx.offset == NULL) goto out_offset; - } /* 1. Initial fake pass to compute ctx->idx and set ctx->flags */ build_prologue(&ctx); - if (build_body(&ctx, extra_pass)) { - prog = orig_prog; + if (build_body(&ctx, extra_pass)) goto out_offset; - } ctx.epilogue_offset = ctx.idx; build_epilogue(&ctx); @@ -2004,10 +2229,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) /* Now we know the size of the structure to make */ ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr, sizeof(u32), &header, &image_ptr, jit_fill_hole); - if (!ro_header) { - prog = orig_prog; + if (!ro_header) goto out_offset; - } /* 2. Now, the actual pass to generate final JIT code */ /* @@ -2027,17 +2250,13 @@ skip_init_ctx: ctx.num_exentries = 0; build_prologue(&ctx); - if (build_body(&ctx, extra_pass)) { - prog = orig_prog; + if (build_body(&ctx, extra_pass)) goto out_free; - } build_epilogue(&ctx); /* 3. Extra pass to validate JITed code */ - if (validate_ctx(&ctx)) { - prog = orig_prog; + if (validate_ctx(&ctx)) goto out_free; - } /* And we're done */ if (bpf_jit_enable > 1) @@ -2050,9 +2269,9 @@ skip_init_ctx: goto out_free; } if (WARN_ON(bpf_jit_binary_pack_finalize(ro_header, header))) { - /* ro_header has been freed */ + /* ro_header and header have been freed */ ro_header = NULL; - prog = orig_prog; + header = NULL; goto out_free; } /* @@ -2084,13 +2303,15 @@ out_offset: prog->aux->jit_data = NULL; } -out: - if (tmp_blinded) - bpf_jit_prog_release_other(prog, prog == orig_prog ? tmp : orig_prog); - return prog; out_free: + if (extra_pass) { + prog->bpf_func = NULL; + prog->jited = 0; + prog->jited_len = 0; + } + if (header) { bpf_arch_text_copy(&ro_header->size, &header->size, sizeof(header->size)); bpf_jit_binary_pack_free(ro_header, header); @@ -2136,6 +2357,11 @@ bool bpf_jit_supports_arena(void) return true; } +bool bpf_jit_supports_fsession(void) +{ + return true; +} + /* Indicate the JIT backend supports mixing bpf2bpf and tailcalls. */ bool bpf_jit_supports_subprog_tailcalls(void) { diff --git a/arch/loongarch/pci/acpi.c b/arch/loongarch/pci/acpi.c index 0dde3ddcd544..b02698a338ee 100644 --- a/arch/loongarch/pci/acpi.c +++ b/arch/loongarch/pci/acpi.c @@ -61,11 +61,16 @@ static void acpi_release_root_info(struct acpi_pci_root_info *ci) static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci) { int status; + unsigned long long pci_h = 0; struct resource_entry *entry, *tmp; struct acpi_device *device = ci->bridge; status = acpi_pci_probe_root_resources(ci); if (status > 0) { + acpi_evaluate_integer(device->handle, "PCIH", NULL, &pci_h); + if (pci_h) + return status; + resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { if (entry->res->flags & IORESOURCE_MEM) { entry->offset = ci->root->mcfg_addr & GENMASK_ULL(63, 40); diff --git a/arch/loongarch/pci/pci.c b/arch/loongarch/pci/pci.c index d233ea2218fe..f33c7ea1443d 100644 --- a/arch/loongarch/pci/pci.c +++ b/arch/loongarch/pci/pci.c @@ -132,6 +132,9 @@ static void loongson_gpu_fixup_dma_hang(struct pci_dev *pdev, bool on) crtc_reg = regbase; crtc_offset = 0x400; break; + default: + iounmap(regbase); + return; } for (i = 0; i < CRTC_NUM_MAX; i++, crtc_reg += crtc_offset) { diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile index 42aa96249828..9c9181bb4071 100644 --- a/arch/loongarch/vdso/Makefile +++ b/arch/loongarch/vdso/Makefile @@ -12,6 +12,8 @@ obj-vdso-$(CONFIG_GENERIC_GETTIMEOFDAY) += vgettimeofday.o ccflags-vdso := \ $(filter -I%,$(KBUILD_CFLAGS)) \ $(filter -E%,$(KBUILD_CFLAGS)) \ + $(filter -m32,$(KBUILD_CFLAGS)) \ + $(filter -m64,$(KBUILD_CFLAGS)) \ $(filter -march=%,$(KBUILD_CFLAGS)) \ $(filter -m%-float,$(KBUILD_CFLAGS)) \ $(CLANG_FLAGS) \ diff --git a/arch/mips/boot/dts/econet/en751221.dtsi b/arch/mips/boot/dts/econet/en751221.dtsi index 2abeef5b744a..72cb65654c34 100644 --- a/arch/mips/boot/dts/econet/en751221.dtsi +++ b/arch/mips/boot/dts/econet/en751221.dtsi @@ -1,6 +1,8 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /dts-v1/; +#include + / { compatible = "econet,en751221"; #address-cells = <1>; @@ -30,6 +32,30 @@ #interrupt-cells = <1>; }; + chip_scu: syscon@1fa20000 { + compatible = "econet,en751221-chip-scu", "syscon"; + reg = <0x1fa20000 0x388>; + }; + + pcie_phy1: pcie-phy@1fac0000 { + compatible = "econet,en751221-pcie-gen2"; + reg = <0x1fac0000 0x1000>; + #phy-cells = <0>; + }; + + pcie_phy0: pcie-phy@1faf2000 { + compatible = "econet,en751221-pcie-gen1"; + reg = <0x1faf2000 0x1000>; + #phy-cells = <0>; + }; + + scuclk: clock-controller@1fb00000 { + compatible = "econet,en751221-scu"; + reg = <0x1fb00000 0x970>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + intc: interrupt-controller@1fb40000 { compatible = "econet,en751221-intc"; reg = <0x1fb40000 0x100>; @@ -41,6 +67,94 @@ econet,shadow-interrupts = <7 2>, <8 3>, <13 12>, <30 29>; }; + pciecfg: pciecfg@1fb80000 { + compatible = "mediatek,generic-pciecfg", "syscon"; + reg = <0x1fb80000 0x1000>; + }; + + pcie0: pcie@1fb81000 { + compatible = "econet,en7528-pcie"; + device_type = "pci"; + reg = <0x1fb81000 0x1000>; + reg-names = "port0"; + linux,pci-domain = <0>; + #address-cells = <3>; + #size-cells = <2>; + interrupt-parent = <&intc>; + interrupts = <23>; + interrupt-names = "pcie_irq"; + clocks = <&scuclk EN751221_CLK_PCIE>; + clock-names = "sys_ck0"; + phys = <&pcie_phy0>; + phy-names = "pcie-phy0"; + bus-range = <0x00 0xff>; + ranges = <0x01000000 0 0x00000000 0x1f600000 0 0x00008000>, + <0x82000000 0 0x20000000 0x20000000 0 0x08000000>; + status = "disabled"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc0 0>, + <0 0 0 2 &pcie_intc0 1>, + <0 0 0 3 &pcie_intc0 2>, + <0 0 0 4 &pcie_intc0 3>; + + pcie_intc0: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + + slot0: pcie@0,0 { + reg = <0x0000 0 0 0 0>; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + + pcie1: pcie@1fb83000 { + compatible = "econet,en7528-pcie"; + device_type = "pci"; + reg = <0x1fb83000 0x1000>; + reg-names = "port1"; + linux,pci-domain = <1>; + #address-cells = <3>; + #size-cells = <2>; + interrupt-parent = <&intc>; + interrupts = <24>; + interrupt-names = "pcie_irq"; + clocks = <&scuclk EN751221_CLK_PCIE>; + clock-names = "sys_ck1"; + phys = <&pcie_phy1>; + phy-names = "pcie-phy1"; + bus-range = <0x00 0xff>; + ranges = <0x81000000 0 0x00000000 0x1f608000 0 0x00008000>, + <0x82000000 0 0x28000000 0x28000000 0 0x08000000>; + status = "disabled"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc1 0>, + <0 0 0 2 &pcie_intc1 1>, + <0 0 0 3 &pcie_intc1 2>, + <0 0 0 4 &pcie_intc1 3>; + + pcie_intc1: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + + slot1: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + uart: serial@1fbf0000 { compatible = "ns16550"; reg = <0x1fbf0000 0x30>; diff --git a/arch/mips/boot/dts/econet/en751221_smartfiber_xp8421-b.dts b/arch/mips/boot/dts/econet/en751221_smartfiber_xp8421-b.dts index 8223c5bce67f..c633bf73add6 100644 --- a/arch/mips/boot/dts/econet/en751221_smartfiber_xp8421-b.dts +++ b/arch/mips/boot/dts/econet/en751221_smartfiber_xp8421-b.dts @@ -17,3 +17,24 @@ linux,usable-memory-range = <0x00020000 0x1bfe0000>; }; }; + +&pcie0 { + status = "okay"; +}; +&slot0 { + wifi@0,0 { + /* MT7612E */ + compatible = "mediatek,mt76"; + reg = <0x0000 0 0 0 0>; + }; +}; +&pcie1 { + status = "okay"; +}; +&slot1 { + wifi@0,0 { + /* MT7592 */ + compatible = "mediatek,mt76"; + reg = <0x0000 0 0 0 0>; + }; +}; diff --git a/arch/mips/boot/dts/loongson/loongson64g-package.dtsi b/arch/mips/boot/dts/loongson/loongson64g-package.dtsi index d4314f62ccc2..029daeedd0ab 100644 --- a/arch/mips/boot/dts/loongson/loongson64g-package.dtsi +++ b/arch/mips/boot/dts/loongson/loongson64g-package.dtsi @@ -40,7 +40,7 @@ }; cpu_uart0: serial@1fe00100 { - compatible = "ns16550a"; + compatible = "loongson,ls3a4000-uart", "loongson,ls2k1500-uart", "ns16550a"; reg = <0 0x1fe00100 0x10>; clock-frequency = <100000000>; interrupt-parent = <&liointc>; @@ -50,7 +50,7 @@ cpu_uart1: serial@1fe00110 { status = "disabled"; - compatible = "ns16550a"; + compatible = "loongson,ls3a4000-uart", "loongson,ls2k1500-uart", "ns16550a"; reg = <0 0x1fe00110 0x10>; clock-frequency = <100000000>; interrupts = <15 IRQ_TYPE_LEVEL_HIGH>; diff --git a/arch/mips/boot/dts/mobileye/Makefile b/arch/mips/boot/dts/mobileye/Makefile index 7cc89968aaac..9305dd01f4c8 100644 --- a/arch/mips/boot/dts/mobileye/Makefile +++ b/arch/mips/boot/dts/mobileye/Makefile @@ -3,3 +3,4 @@ dtb-$(CONFIG_MACH_EYEQ5) += eyeq5-epm5.dtb dtb-$(CONFIG_MACH_EYEQ6H) += eyeq6h-epm6.dtb +dtb-$(CONFIG_MACH_EYEQ6LPLUS) += eyeq6lplus-epm6.dtb diff --git a/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts b/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts index 9fc1a1b0a81b..babf52731ea6 100644 --- a/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts +++ b/arch/mips/boot/dts/mobileye/eyeq5-epm5.dts @@ -29,3 +29,29 @@ label = "U60"; }; }; + +&macb0 { + phy-mode = "sgmii"; + phy-handle = <&macb0_phy>; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + macb0_phy: ethernet-phy@e { + reg = <0xe>; + }; + }; +}; + +&macb1 { + phy-mode = "rgmii-id"; + phy-handle = <&macb1_phy>; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + macb1_phy: ethernet-phy@e { + reg = <0xe>; + }; + }; +}; diff --git a/arch/mips/boot/dts/mobileye/eyeq5.dtsi b/arch/mips/boot/dts/mobileye/eyeq5.dtsi index 36a73e8a63a1..cec5ad875228 100644 --- a/arch/mips/boot/dts/mobileye/eyeq5.dtsi +++ b/arch/mips/boot/dts/mobileye/eyeq5.dtsi @@ -77,6 +77,8 @@ serial0 = &uart0; serial1 = &uart1; serial2 = &uart2; + ethernet0 = &macb0; + ethernet1 = &macb1; }; cpu_intc: interrupt-controller { @@ -231,6 +233,7 @@ #clock-cells = <1>; clocks = <&xtal>; clock-names = "ref"; + #phy-cells = <1>; }; gic: interrupt-controller@140000 { @@ -305,6 +308,48 @@ #interrupt-cells = <2>; resets = <&olb 0 26>; }; + + iocu-bus { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + dma-coherent; + dma-ranges = <0x10 0x00000000 0x0 0x0 0x10 0>; + + macb0: ethernet@2a00000 { + compatible = "mobileye,eyeq5-gem"; + reg = <0x0 0x02a00000 0x0 0x4000>; + interrupt-parent = <&gic>; + /* One interrupt per queue */ + interrupts = , + , + , + ; + clock-names = "pclk", "hclk", "tsu_clk"; + clocks = <&pclk>, <&pclk>, <&tsu_clk>; + nvmem-cells = <ð0_mac>; + nvmem-cell-names = "mac-address"; + phys = <&olb 0>; + }; + + macb1: ethernet@2b00000 { + compatible = "mobileye,eyeq5-gem"; + reg = <0x0 0x02b00000 0x0 0x4000>; + interrupt-parent = <&gic>; + /* One interrupt per queue */ + interrupts = , + , + , + ; + clock-names = "pclk", "hclk", "tsu_clk"; + clocks = <&pclk>, <&pclk>, <&tsu_clk>; + nvmem-cells = <ð1_mac>; + nvmem-cell-names = "mac-address"; + phys = <&olb 1>; + }; + }; + }; }; diff --git a/arch/mips/boot/dts/mobileye/eyeq6lplus-epm6.dts b/arch/mips/boot/dts/mobileye/eyeq6lplus-epm6.dts new file mode 100644 index 000000000000..404d0ff09f5a --- /dev/null +++ b/arch/mips/boot/dts/mobileye/eyeq6lplus-epm6.dts @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Copyright 2025 Mobileye Vision Technologies Ltd. + */ + +/dts-v1/; + +#include "eyeq6lplus.dtsi" + +/ { + compatible = "mobileye,eyeq6lplus-epm6", "mobileye,eyeq6lplus"; + model = "Mobileye EyeQ6Lplus Evaluation board"; + + aliases { + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:921600n8"; + }; + + memory@0 { + device_type = "memory"; + reg = <0x1 0x00000000 0x0 0x80000000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* These reserved memory regions are also defined in bootmanager + * for configuring inbound translation for BARS, don't change + * these without syncing with bootmanager + */ + mhm_reserved_0: the-mhm-reserved-0 { + reg = <0x1 0x00000000 0x0 0x0000800>; + }; + bm_logs_reserved: bm-logs-reserved { + reg = <0x1 0x0000800 0x0 0x000f800>; + }; + shmem0_reserved: shmem@804000000 { + reg = <0x1 0x04000000 0x0 0x1000000>; + }; + shmem1_reserved: shmem@805000000 { + reg = <0x1 0x05000000 0x0 0x1000000>; + }; + mini_coredump0_reserved: mini-coredump0@806200000 { + reg = <0x1 0x06200000 0x0 0x100000>; + }; + mailbox_reserved: mailbox-reserved { + reg = <0x1 0x06300000 0x0 0x000300>; + }; + sys_logs_reserved: sys-logs-reserved { + reg = <0x1 0x10000000 0x0 0x800000>; + }; + csl_policy_logs_reserved: csl-policy-logs-reserved { + reg = <0x1 0x10800000 0x0 0x10000>; + }; + }; +}; + +&ospi { + status = "okay"; + flash@0 { + compatible = "spi-nand"; + reg = <0>; + spi-max-frequency = <40000000>; + cdns,read-delay = <0>; + cdns,tshsl-ns = <400>; + cdns,tsd2d-ns = <120>; + cdns,tchsh-ns = <40>; + cdns,tslch-ns = <20>; + spi-tx-bus-width = <1>; + spi-rx-bus-width = <8>; + }; +}; + +&spi0 { + pinctrl-0 = <&spi0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&spi1 { + pinctrl-0 = <&spi1_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&uart0 { + pinctrl-0 = <&uart0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&i2c0 { + status = "okay"; +}; + +&i2c1 { + status = "okay"; +}; diff --git a/arch/mips/boot/dts/mobileye/eyeq6lplus-pins.dtsi b/arch/mips/boot/dts/mobileye/eyeq6lplus-pins.dtsi new file mode 100644 index 000000000000..5cb0660f46c6 --- /dev/null +++ b/arch/mips/boot/dts/mobileye/eyeq6lplus-pins.dtsi @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +&olb { + timer0_pins: timer0-pins { + function = "timer0"; + pins = "PA0", "PA1"; + }; + timer1_pins: timer1-pins { + function = "timer1"; + pins = "PA2", "PA3"; + }; + uart_ssi_pins: uart-ssi-pins { + function = "uart_ssi"; + pins = "PA4", "PA5"; + }; + spi0_pins: spi0-pins { + function = "spi0"; + pins = "PA6", "PA7", "PA8", "PA9"; + }; + uart0_pins: uart0-pins { + function = "uart0"; + pins = "PA11", "PA12"; + }; + timer2_pins: timer2-pins { + function = "timer2"; + pins = "PA13", "PA14"; + }; + timer3_pins: timer3-pins { + function = "timer3"; + pins = "PA15", "PA16"; + }; + timer_ext0_pins: timer-ext0-pins { + function = "timer_ext0"; + pins = "PA17", "PA18", "PA19", "PA20"; + }; + timer_ext0_input_a_pins: timer-ext0-input-a-pins { + function = "timer_ext0"; + pins = "PA17"; + }; + pps0_pins: pps0-pins { + function = "timer_ext0"; + pins = "PA17"; + }; + timer_ext0_input_b_pins: timer-ext0-input-b-pins { + function = "timer_ext0"; + pins = "PA18"; + }; + timer_ext0_output_pins: timer-ext0-output-pins { + function = "timer_ext0"; + pins = "PA19", "PA20"; + }; + spi1_pins: spi1-pins { + function = "spi1"; + pins = "PA21", "PA22", "PA23", "PA24"; + }; + spi1_reduced_pins: spi1-reduced-pins { + function = "spi1"; + pins = "PA21", "PA22", "PA23"; + }; + timer_ext1_pins: timer-ext1-pins { + function = "timer_ext1"; + pins = "PA26", "PA27", "PA28", "PA29"; + }; + timer_ext1_input_a_pins: timer-ext1-input-a-pins { + function = "timer_ext1"; + pins = "PA26"; + }; + timer_ext1_input_b_pins: timer-ext1-input-b-pins { + function = "timer_ext1"; + pins = "PA27"; + }; + timer_ext1_output_pins: timer-ext1-output-pins { + function = "timer_ext1"; + pins = "PA28", "PA29"; + }; + ext_ref_clk_pins: ext-ref-clk-pins { + function = "ext_ref_clk"; + pins = "PA30"; + }; + mipi_ref_clk_pins: mipi-ref-clk-pins { + function = "mipi_ref_clk"; + pins = "PA31"; + }; +}; diff --git a/arch/mips/boot/dts/mobileye/eyeq6lplus.dtsi b/arch/mips/boot/dts/mobileye/eyeq6lplus.dtsi new file mode 100644 index 000000000000..4c72da917a95 --- /dev/null +++ b/arch/mips/boot/dts/mobileye/eyeq6lplus.dtsi @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +/* + * Copyright 2025 Mobileye Vision Technologies Ltd. + */ + +#include + +#include + +/ { + #address-cells = <2>; + #size-cells = <2>; + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu@0 { + device_type = "cpu"; + compatible = "img,i6500"; + reg = <0>; + clocks = <&olb EQ6LPC_CPU_OCC>; + }; + }; + + cpu_intc: interrupt-controller { + compatible = "mti,cpu-interrupt-controller"; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + + coherency-manager { + compatible = "mobileye,eyeq6-cm"; + }; + + xtal: clock-30000000 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <30000000>; + }; + + soc: soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + olb: system-controller@e8400000 { + compatible = "mobileye,eyeq6lplus-olb", "syscon"; + reg = <0 0xe8400000 0x0 0x80000>; + #reset-cells = <2>; + #clock-cells = <1>; + clocks = <&xtal>; + clock-names = "ref"; + }; + + ospi: spi@e8800000 { + compatible = "mobileye,eyeq5-ospi", "cdns,qspi-nor"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0 0xe8800000 0x0 0x100000>, + <0 0xb0000000 0x0 0x30000000>; + interrupt-parent = <&gic>; + interrupts = ; + cdns,fifo-depth = <128>; + cdns,fifo-width = <4>; + cdns,trigger-address = <0x00000000>; + clocks = <&olb EQ6LPC_PER_OSPI>; + status = "disabled"; + }; + + spi0: spi@eac0d000 { + compatible = "snps,dw-apb-ssi"; + reg = <0 0xeac0d000 0x0 0x1000>; + clocks = <&olb EQ6LPC_PER_SPI>; + interrupt-parent = <&gic>; + interrupts = ; + resets = <&olb 0 0>; + reset-names = "spi"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi1: spi@eac0e000 { + compatible = "snps,dw-apb-ssi"; + reg = <0 0xeac0e000 0x0 0x1000>; + spi-slave; + clocks = <&olb EQ6LPC_PER_SPI>; + interrupt-parent = <&gic>; + interrupts = ; + resets = <&olb 0 1>; + reset-names = "spi"; + #address-cells = <0>; + #size-cells = <0>; + status = "disabled"; + }; + + uart0: serial@eac10000 { + compatible = "snps,dw-apb-uart"; + reg-shift = <2>; + reg-io-width = <4>; + clocks = <&olb EQ6LPC_PER_UART>; + clock-frequency = <15625000>; + reg = <0 0xeac10000 0x0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + resets = <&olb 0 2>; + status = "disabled"; + }; + + i2c0: i2c@eac11000 { + compatible = "mobileye,eyeq6lplus-i2c", "snps,designware-i2c"; + reg = <0 0xeac11000 0x0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + clock-frequency = <400000>; + clocks = <&olb EQ6LPC_PER_I2C_SER>; + resets = <&olb 0 3>; + i2c-sda-hold-time-ns = <50>; + status = "disabled"; + }; + + i2c1: i2c@eac12000 { + compatible = "mobileye,eyeq6lplus-i2c", "snps,designware-i2c"; + reg = <0 0xeac12000 0x0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + clock-frequency = <400000>; + clocks = <&olb EQ6LPC_PER_I2C_SER>; + resets = <&olb 0 4>; + i2c-sda-hold-time-ns = <50>; + status = "disabled"; + }; + + gpio: gpio@eac14000 { + compatible = "snps,dw-apb-gpio"; + reg = <0x0 0xeac14000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&olb 0 13>; + porta: gpio-port@0 { + compatible = "snps,dw-apb-gpio-port"; + gpio-controller; + #gpio-cells = <2>; + snps,nr-gpios = <32>; + gpio-ranges = <&olb 0 0 32>; + reg = <0>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = ; + }; + }; + + gic: interrupt-controller@f0920000 { + compatible = "mti,gic"; + reg = <0x0 0xf0920000 0x0 0x20000>; + interrupt-controller; + #interrupt-cells = <3>; + interrupt-parent = <&cpu_intc>; + timer { + compatible = "mti,gic-timer"; + interrupts = ; + clocks = <&olb EQ6LPC_CPU_OCC>; + }; + }; + }; +}; + +#include "eyeq6lplus-pins.dtsi" diff --git a/arch/mips/configs/bcm47xx_defconfig b/arch/mips/configs/bcm47xx_defconfig index d10b3d4adbd1..acbab8dae53f 100644 --- a/arch/mips/configs/bcm47xx_defconfig +++ b/arch/mips/configs/bcm47xx_defconfig @@ -28,7 +28,6 @@ CONFIG_NETFILTER=y CONFIG_VLAN_8021Q=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_FQ_CODEL=y -CONFIG_HAMRADIO=y CONFIG_CFG80211=y CONFIG_MAC80211=y CONFIG_MTD=y diff --git a/arch/mips/configs/bigsur_defconfig b/arch/mips/configs/bigsur_defconfig index 3b64e151e187..aa63ada62e28 100644 --- a/arch/mips/configs/bigsur_defconfig +++ b/arch/mips/configs/bigsur_defconfig @@ -84,16 +84,6 @@ CONFIG_IP_VS_FTP=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m CONFIG_VLAN_8021Q_GVRP=y -CONFIG_HAMRADIO=y -CONFIG_AX25=m -CONFIG_NETROM=m -CONFIG_ROSE=m -CONFIG_MKISS=m -CONFIG_6PACK=m -CONFIG_BPQETHER=m -CONFIG_BAYCOM_SER_FDX=m -CONFIG_BAYCOM_SER_HDX=m -CONFIG_YAM=m CONFIG_FW_LOADER=m CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_NBD=m diff --git a/arch/mips/configs/db1xxx_defconfig b/arch/mips/configs/db1xxx_defconfig index 281dd7d0f805..49fe970c2860 100644 --- a/arch/mips/configs/db1xxx_defconfig +++ b/arch/mips/configs/db1xxx_defconfig @@ -105,7 +105,6 @@ CONFIG_PATA_PCMCIA=y CONFIG_PATA_PLATFORM=y CONFIG_NETDEVICES=y CONFIG_NLMON=y -CONFIG_PCMCIA_3C589=y CONFIG_MIPS_AU1X00_ENET=y CONFIG_SMC91X=y CONFIG_SMSC911X=y diff --git a/arch/mips/configs/eyeq6lplus_defconfig b/arch/mips/configs/eyeq6lplus_defconfig new file mode 100644 index 000000000000..39430ebf8e60 --- /dev/null +++ b/arch/mips/configs/eyeq6lplus_defconfig @@ -0,0 +1,117 @@ +CONFIG_SYSVIPC=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_SYSCALL=y +CONFIG_TASKSTATS=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_EYEQ=y +CONFIG_MACH_EYEQ6LPLUS=y +CONFIG_MIPS_CPS=y +CONFIG_CPU_HAS_MSA=y +CONFIG_NR_CPUS=16 +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_JUMP_LABEL=y +CONFIG_PAGE_SIZE_16KB=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_TRIM_UNUSED_KSYMS=y +# CONFIG_COMPAT_BRK is not set +CONFIG_USERFAULTFD=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_NETFILTER=y +CONFIG_CAN=y +CONFIG_PCI=y +CONFIG_PCI_MSI=y +CONFIG_PCI_DEBUG=y +CONFIG_PCI_ENDPOINT=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BLOCK=y +CONFIG_SCSI=y +CONFIG_NETDEVICES=y +CONFIG_MACVLAN=y +CONFIG_IPVLAN=y +CONFIG_MACB=y +CONFIG_MARVELL_PHY=y +CONFIG_MICREL_PHY=y +CONFIG_CAN_M_CAN=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_DW=y +CONFIG_HW_RANDOM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_DESIGNWARE_CORE=y +CONFIG_SPI=y +CONFIG_SPI_CADENCE_QUADSPI=y +CONFIG_SPI_DESIGNWARE=y +CONFIG_SPI_DW_MMIO=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPI_SLAVE=y +# CONFIG_PTP_1588_CLOCK is not set +CONFIG_PINCTRL=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_DWAPB=y +CONFIG_MFD_SYSCON=y +CONFIG_HID_A4TECH=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_EZKEY=y +CONFIG_HID_ITE=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_REDRAGON=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_CADENCE=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_RESET_CONTROLLER=y +# CONFIG_NVMEM is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FS_ENCRYPTION=y +CONFIG_FUSE_FS=y +CONFIG_CUSE=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_FRAME_WARN=1024 +CONFIG_DEBUG_FS=y +# CONFIG_RCU_TRACE is not set +# CONFIG_FTRACE is not set diff --git a/arch/mips/configs/gpr_defconfig b/arch/mips/configs/gpr_defconfig index fdd28a89e336..ed1a8f80f96e 100644 --- a/arch/mips/configs/gpr_defconfig +++ b/arch/mips/configs/gpr_defconfig @@ -87,9 +87,6 @@ CONFIG_BRIDGE_EBT_LOG=m CONFIG_IP_SCTP=m CONFIG_TIPC=m CONFIG_ATM=y -CONFIG_ATM_CLIP=y -CONFIG_ATM_LANE=m -CONFIG_ATM_MPOA=m CONFIG_ATM_BR2684=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m @@ -104,7 +101,6 @@ CONFIG_NET_SCHED=y CONFIG_NET_SCH_CBQ=m CONFIG_NET_SCH_HTB=m CONFIG_NET_SCH_HFSC=m -CONFIG_NET_SCH_ATM=m CONFIG_NET_SCH_PRIO=m CONFIG_NET_SCH_RED=m CONFIG_NET_SCH_SFQ=m @@ -130,17 +126,6 @@ CONFIG_NET_EMATCH_TEXT=m CONFIG_NET_CLS_ACT=y CONFIG_NET_ACT_POLICE=y CONFIG_NET_PKTGEN=m -CONFIG_HAMRADIO=y -CONFIG_AX25=m -# CONFIG_AX25_DAMA_SLAVE is not set -CONFIG_NETROM=m -CONFIG_ROSE=m -CONFIG_MKISS=m -CONFIG_6PACK=m -CONFIG_BPQETHER=m -CONFIG_BAYCOM_SER_FDX=m -CONFIG_BAYCOM_SER_HDX=m -CONFIG_YAM=m CONFIG_CFG80211=y CONFIG_MAC80211=y CONFIG_MTD=y @@ -167,15 +152,6 @@ CONFIG_SCSI_SAS_LIBSAS=m CONFIG_NETDEVICES=y CONFIG_NET_FC=y CONFIG_NETCONSOLE=m -CONFIG_ATM_TCP=m -CONFIG_ATM_LANAI=m -CONFIG_ATM_ENI=m -CONFIG_ATM_NICSTAR=m -CONFIG_ATM_IDT77252=m -CONFIG_ATM_IA=m -CONFIG_ATM_FORE200E=m -CONFIG_ATM_HE=m -CONFIG_ATM_HE_USE_SUNI=y CONFIG_MIPS_AU1X00_ENET=y CONFIG_CICADA_PHY=m CONFIG_DAVICOM_PHY=m diff --git a/arch/mips/configs/ip32_defconfig b/arch/mips/configs/ip32_defconfig index 68558d0d3f52..9020c309dcda 100644 --- a/arch/mips/configs/ip32_defconfig +++ b/arch/mips/configs/ip32_defconfig @@ -71,7 +71,6 @@ CONFIG_SERIAL_8250_CONSOLE=y CONFIG_HW_RANDOM=y CONFIG_WATCHDOG=y CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_GBE=y # CONFIG_VGA_CONSOLE is not set CONFIG_FRAMEBUFFER_CONSOLE=y diff --git a/arch/mips/configs/lemote2f_defconfig b/arch/mips/configs/lemote2f_defconfig index eb3565a3f292..b9f3e1641105 100644 --- a/arch/mips/configs/lemote2f_defconfig +++ b/arch/mips/configs/lemote2f_defconfig @@ -136,7 +136,6 @@ CONFIG_FB_SIS_300=y CONFIG_FB_SIS_315=y CONFIG_FB_SIMPLE=y CONFIG_FB_SM712=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_MODE_HELPERS=y CONFIG_FB_TILEBLITTING=y CONFIG_BACKLIGHT_CLASS_DEVICE=y diff --git a/arch/mips/configs/malta_qemu_32r6_defconfig b/arch/mips/configs/malta_qemu_32r6_defconfig index 520ee73d0b27..5687e10c1bc8 100644 --- a/arch/mips/configs/malta_qemu_32r6_defconfig +++ b/arch/mips/configs/malta_qemu_32r6_defconfig @@ -128,7 +128,6 @@ CONFIG_POWER_RESET_PIIX4_POWEROFF=y CONFIG_POWER_RESET_SYSCON=y # CONFIG_HWMON is not set CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_MATROX=y CONFIG_FB_MATROX_G=y CONFIG_USB=y diff --git a/arch/mips/configs/maltaaprp_defconfig b/arch/mips/configs/maltaaprp_defconfig index 0a3951df2ebb..abd22bff517b 100644 --- a/arch/mips/configs/maltaaprp_defconfig +++ b/arch/mips/configs/maltaaprp_defconfig @@ -129,7 +129,6 @@ CONFIG_POWER_RESET_PIIX4_POWEROFF=y CONFIG_POWER_RESET_SYSCON=y # CONFIG_HWMON is not set CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_MATROX=y CONFIG_FB_MATROX_G=y CONFIG_USB=y diff --git a/arch/mips/configs/maltasmvp_defconfig b/arch/mips/configs/maltasmvp_defconfig index 9a83b75f7c1b..3fb3def8112d 100644 --- a/arch/mips/configs/maltasmvp_defconfig +++ b/arch/mips/configs/maltasmvp_defconfig @@ -129,7 +129,6 @@ CONFIG_POWER_RESET_PIIX4_POWEROFF=y CONFIG_POWER_RESET_SYSCON=y # CONFIG_HWMON is not set CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_MATROX=y CONFIG_FB_MATROX_G=y CONFIG_USB=y diff --git a/arch/mips/configs/maltasmvp_eva_defconfig b/arch/mips/configs/maltasmvp_eva_defconfig index 8fa0f3985dcb..92e026912f68 100644 --- a/arch/mips/configs/maltasmvp_eva_defconfig +++ b/arch/mips/configs/maltasmvp_eva_defconfig @@ -132,7 +132,6 @@ CONFIG_POWER_RESET_PIIX4_POWEROFF=y CONFIG_POWER_RESET_SYSCON=y # CONFIG_HWMON is not set CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_MATROX=y CONFIG_FB_MATROX_G=y CONFIG_USB=y diff --git a/arch/mips/configs/maltaup_defconfig b/arch/mips/configs/maltaup_defconfig index 518635a22fb3..7a675fd3321f 100644 --- a/arch/mips/configs/maltaup_defconfig +++ b/arch/mips/configs/maltaup_defconfig @@ -128,7 +128,6 @@ CONFIG_POWER_RESET_PIIX4_POWEROFF=y CONFIG_POWER_RESET_SYSCON=y # CONFIG_HWMON is not set CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_MATROX=y CONFIG_FB_MATROX_G=y CONFIG_USB=y diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig index 72568f8ae653..3629afbe5d75 100644 --- a/arch/mips/configs/mtx1_defconfig +++ b/arch/mips/configs/mtx1_defconfig @@ -15,7 +15,6 @@ CONFIG_PCI=y CONFIG_PCCARD=m CONFIG_YENTA=m CONFIG_PD6729=m -CONFIG_I82092=m CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y @@ -133,9 +132,6 @@ CONFIG_BRIDGE_EBT_LOG=m CONFIG_IP_SCTP=m CONFIG_TIPC=m CONFIG_ATM=y -CONFIG_ATM_CLIP=y -CONFIG_ATM_LANE=m -CONFIG_ATM_MPOA=m CONFIG_ATM_BR2684=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m @@ -150,7 +146,6 @@ CONFIG_NET_SCHED=y CONFIG_NET_SCH_CBQ=m CONFIG_NET_SCH_HTB=m CONFIG_NET_SCH_HFSC=m -CONFIG_NET_SCH_ATM=m CONFIG_NET_SCH_PRIO=m CONFIG_NET_SCH_RED=m CONFIG_NET_SCH_SFQ=m @@ -176,17 +171,6 @@ CONFIG_NET_EMATCH_TEXT=m CONFIG_NET_CLS_ACT=y CONFIG_NET_ACT_POLICE=y CONFIG_NET_PKTGEN=m -CONFIG_HAMRADIO=y -CONFIG_AX25=m -# CONFIG_AX25_DAMA_SLAVE is not set -CONFIG_NETROM=m -CONFIG_ROSE=m -CONFIG_MKISS=m -CONFIG_6PACK=m -CONFIG_BPQETHER=m -CONFIG_BAYCOM_SER_FDX=m -CONFIG_BAYCOM_SER_HDX=m -CONFIG_YAM=m CONFIG_BT=m CONFIG_BT_RFCOMM=m CONFIG_BT_RFCOMM_TTY=y @@ -243,23 +227,11 @@ CONFIG_ARCNET_RIM_I=m CONFIG_ARCNET_COM20020=m CONFIG_ARCNET_COM20020_PCI=m CONFIG_ARCNET_COM20020_CS=m -CONFIG_ATM_TCP=m -CONFIG_ATM_LANAI=m -CONFIG_ATM_ENI=m -CONFIG_ATM_NICSTAR=m -CONFIG_ATM_IDT77252=m -CONFIG_ATM_IA=m -CONFIG_ATM_FORE200E=m -CONFIG_ATM_HE=m -CONFIG_ATM_HE_USE_SUNI=y -CONFIG_PCMCIA_3C574=m -CONFIG_PCMCIA_3C589=m CONFIG_VORTEX=m CONFIG_TYPHOON=m CONFIG_ADAPTEC_STARFIRE=m CONFIG_AMD8111_ETH=m CONFIG_PCNET32=m -CONFIG_PCMCIA_NMCLAN=m CONFIG_B44=m CONFIG_BNX2=m CONFIG_TIGON3=m @@ -273,7 +245,6 @@ CONFIG_ULI526X=m CONFIG_PCMCIA_XIRCOM=m CONFIG_DL2K=m CONFIG_SUNDANCE=m -CONFIG_PCMCIA_FMVJ18X=m CONFIG_E100=m CONFIG_E1000=m CONFIG_SKGE=m @@ -282,12 +253,9 @@ CONFIG_MYRI10GE=m CONFIG_FEALNX=m CONFIG_NATSEMI=m CONFIG_NS83820=m -CONFIG_PCMCIA_AXNET=m CONFIG_NE2K_PCI=m CONFIG_PCMCIA_PCNET=m CONFIG_FORCEDETH=m -CONFIG_HAMACHI=m -CONFIG_YELLOWFIN=m CONFIG_QLA3XXX=m CONFIG_8139CP=m CONFIG_8139TOO=m @@ -296,7 +264,6 @@ CONFIG_8139TOO_8129=y CONFIG_R8169=m CONFIG_SIS900=m CONFIG_SIS190=m -CONFIG_PCMCIA_SMC91C92=m CONFIG_EPIC100=m CONFIG_HAPPYMEAL=m CONFIG_SUNGEM=m diff --git a/arch/mips/configs/rb532_defconfig b/arch/mips/configs/rb532_defconfig index 30d18b084cda..a88322fe3935 100644 --- a/arch/mips/configs/rb532_defconfig +++ b/arch/mips/configs/rb532_defconfig @@ -95,7 +95,6 @@ CONFIG_NET_ACT_GACT=m CONFIG_GACT_PROB=y CONFIG_NET_ACT_MIRRED=m CONFIG_NET_ACT_PEDIT=m -CONFIG_HAMRADIO=y CONFIG_MTD=y CONFIG_MTD_BLOCK=y CONFIG_MTD_BLOCK2MTD=y diff --git a/arch/mips/configs/rm200_defconfig b/arch/mips/configs/rm200_defconfig index b1e67ff0c4f0..ad9fbd0cbb38 100644 --- a/arch/mips/configs/rm200_defconfig +++ b/arch/mips/configs/rm200_defconfig @@ -147,13 +147,6 @@ CONFIG_NET_CLS_FW=m CONFIG_NET_CLS_U32=m CONFIG_NET_CLS_RSVP=m CONFIG_NET_CLS_RSVP6=m -CONFIG_HAMRADIO=y -CONFIG_AX25=m -CONFIG_NETROM=m -CONFIG_ROSE=m -CONFIG_MKISS=m -CONFIG_6PACK=m -CONFIG_BPQETHER=m CONFIG_CONNECTOR=m CONFIG_PARPORT=m CONFIG_PARPORT_PC=m diff --git a/arch/mips/configs/rt305x_defconfig b/arch/mips/configs/rt305x_defconfig index 8f9701efef19..c920976bedd0 100644 --- a/arch/mips/configs/rt305x_defconfig +++ b/arch/mips/configs/rt305x_defconfig @@ -64,7 +64,6 @@ CONFIG_BRIDGE=y # CONFIG_BRIDGE_IGMP_SNOOPING is not set CONFIG_VLAN_8021Q=y CONFIG_NET_SCHED=y -CONFIG_HAMRADIO=y CONFIG_MTD=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y diff --git a/arch/mips/configs/xway_defconfig b/arch/mips/configs/xway_defconfig index aae8497b6872..f1c53bbb72e9 100644 --- a/arch/mips/configs/xway_defconfig +++ b/arch/mips/configs/xway_defconfig @@ -66,7 +66,6 @@ CONFIG_BRIDGE=y # CONFIG_BRIDGE_IGMP_SNOOPING is not set CONFIG_VLAN_8021Q=y CONFIG_NET_SCHED=y -CONFIG_HAMRADIO=y CONFIG_MTD=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y diff --git a/arch/mips/dec/ecc-berr.c b/arch/mips/dec/ecc-berr.c index 1eb356fdd832..3d41ff07d817 100644 --- a/arch/mips/dec/ecc-berr.c +++ b/arch/mips/dec/ecc-berr.c @@ -5,12 +5,13 @@ * 5000/240 (KN03), 5000/260 (KN05) and DECsystem 5900 (KN03), * 5900/260 (KN05) systems. * - * Copyright (c) 2003, 2005 Maciej W. Rozycki + * Copyright (c) 2003, 2005, 2026 Maciej W. Rozycki */ #include #include #include +#include #include #include @@ -51,6 +52,10 @@ static int dec_ecc_be_backend(struct pt_regs *regs, int is_fixup, int invoker) static const char overstr[] = "overrun"; static const char eccstr[] = "ECC error"; + static DEFINE_RATELIMIT_STATE(rs, + DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + const char *kind, *agent, *cycle, *event; const char *status = "", *xbit = "", *fmt = ""; unsigned long address; @@ -70,7 +75,7 @@ static int dec_ecc_be_backend(struct pt_regs *regs, int is_fixup, int invoker) if (!(erraddr & KN0X_EAR_VALID)) { /* No idea what happened. */ - printk(KERN_ALERT "Unidentified bus error %s\n", kind); + pr_alert_ratelimited("Unidentified bus error %s\n", kind); return action; } @@ -180,12 +185,13 @@ static int dec_ecc_be_backend(struct pt_regs *regs, int is_fixup, int invoker) } } - if (action != MIPS_BE_FIXUP) + if (action != MIPS_BE_FIXUP && __ratelimit(&rs)) { printk(KERN_ALERT "Bus error %s: %s %s %s at %#010lx\n", kind, agent, cycle, event, address); - if (action != MIPS_BE_FIXUP && erraddr & KN0X_EAR_ECCERR) - printk(fmt, " ECC syndrome ", syn, status, xbit, i); + if (erraddr & KN0X_EAR_ECCERR) + printk(fmt, " ECC syndrome ", syn, status, xbit, i); + } return action; } diff --git a/arch/mips/dec/kn01-berr.c b/arch/mips/dec/kn01-berr.c index 76efed7bc9f3..27addf7f31c7 100644 --- a/arch/mips/dec/kn01-berr.c +++ b/arch/mips/dec/kn01-berr.c @@ -4,7 +4,7 @@ * and 2100 (KN01) systems equipped with parity error detection * logic. * - * Copyright (c) 2005 Maciej W. Rozycki + * Copyright (c) 2005, 2026 Maciej W. Rozycki */ #include @@ -134,8 +134,8 @@ static int dec_kn01_be_backend(struct pt_regs *regs, int is_fixup, int invoker) action = MIPS_BE_FIXUP; if (action != MIPS_BE_FIXUP) - printk(KERN_ALERT "Bus error %s: %s %s %s at %#010lx\n", - kind, agent, cycle, event, address); + pr_alert_ratelimited("Bus error %s: %s %s %s at %#010lx\n", + kind, agent, cycle, event, address); return action; } diff --git a/arch/mips/dec/kn02xa-berr.c b/arch/mips/dec/kn02xa-berr.c index 9699fc4e6eb1..6e494db1e437 100644 --- a/arch/mips/dec/kn02xa-berr.c +++ b/arch/mips/dec/kn02xa-berr.c @@ -6,12 +6,13 @@ * DECstation/DECsystem 5000/20, /25, /33 (KN02-CA), 5000/50 * (KN04-CA) systems. * - * Copyright (c) 2005 Maciej W. Rozycki + * Copyright (c) 2005, 2026 Maciej W. Rozycki */ #include #include #include +#include #include #include @@ -50,6 +51,10 @@ static int dec_kn02xa_be_backend(struct pt_regs *regs, int is_fixup, static const char paritystr[] = "parity error"; static const char lanestat[][4] = { " OK", "BAD" }; + static DEFINE_RATELIMIT_STATE(rs, + DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + const char *kind, *agent, *cycle, *event; unsigned long address; @@ -79,18 +84,19 @@ static int dec_kn02xa_be_backend(struct pt_regs *regs, int is_fixup, if (is_fixup) action = MIPS_BE_FIXUP; - if (action != MIPS_BE_FIXUP) + if (action != MIPS_BE_FIXUP && __ratelimit(&rs)) { printk(KERN_ALERT "Bus error %s: %s %s %s at %#010lx\n", kind, agent, cycle, event, address); - if (action != MIPS_BE_FIXUP && address < 0x10000000) - printk(KERN_ALERT " Byte lane status %#3x -- " - "#3: %s, #2: %s, #1: %s, #0: %s\n", - (mer & KN02XA_MER_BYTERR) >> 8, - lanestat[(mer & KN02XA_MER_BYTERR_3) != 0], - lanestat[(mer & KN02XA_MER_BYTERR_2) != 0], - lanestat[(mer & KN02XA_MER_BYTERR_1) != 0], - lanestat[(mer & KN02XA_MER_BYTERR_0) != 0]); + if (address < 0x10000000) + printk(KERN_ALERT " Byte lane status %#3x -- " + "#3: %s, #2: %s, #1: %s, #0: %s\n", + (mer & KN02XA_MER_BYTERR) >> 8, + lanestat[(mer & KN02XA_MER_BYTERR_3) != 0], + lanestat[(mer & KN02XA_MER_BYTERR_2) != 0], + lanestat[(mer & KN02XA_MER_BYTERR_1) != 0], + lanestat[(mer & KN02XA_MER_BYTERR_0) != 0]); + } return action; } diff --git a/arch/mips/econet/Kconfig b/arch/mips/econet/Kconfig index fd69884cc9a8..b37b9d25d5a4 100644 --- a/arch/mips/econet/Kconfig +++ b/arch/mips/econet/Kconfig @@ -13,7 +13,9 @@ choice bool "EN751221 family" select COMMON_CLK select ECONET_EN751221_INTC + select HAVE_PCI select IRQ_MIPS_CPU + select PCI_DRIVERS_GENERIC select SMP select SMP_UP select SYS_SUPPORTS_SMP diff --git a/arch/mips/include/asm/mach-au1x00/gpio-au1000.h b/arch/mips/include/asm/mach-au1x00/gpio-au1000.h index d820b481ac56..e6306f6820e6 100644 --- a/arch/mips/include/asm/mach-au1x00/gpio-au1000.h +++ b/arch/mips/include/asm/mach-au1x00/gpio-au1000.h @@ -40,8 +40,6 @@ #define AU1000_GPIO2_INTENABLE 0x10 #define AU1000_GPIO2_ENABLE 0x14 -struct gpio; - static inline int au1000_gpio1_to_irq(int gpio) { return MAKE_IRQ(1, gpio - ALCHEMY_GPIO1_BASE); diff --git a/arch/mips/include/asm/mach-au1x00/gpio-au1300.h b/arch/mips/include/asm/mach-au1x00/gpio-au1300.h index 43d44f384f97..b12f37262cfa 100644 --- a/arch/mips/include/asm/mach-au1x00/gpio-au1300.h +++ b/arch/mips/include/asm/mach-au1x00/gpio-au1300.h @@ -12,7 +12,6 @@ #include #include -struct gpio; struct gpio_chip; /* with the current GPIC design, up to 128 GPIOs are possible. diff --git a/arch/mips/kernel/r4k-bugs64.c b/arch/mips/kernel/r4k-bugs64.c index 1e300330078d..83970ac7e901 100644 --- a/arch/mips/kernel/r4k-bugs64.c +++ b/arch/mips/kernel/r4k-bugs64.c @@ -91,7 +91,7 @@ void mult_sh_align_mod(long *v1, long *v2, long *w, ".set pop" : "=&r" (lv1), "=r" (lw) : "r" (m1), "r" (m2), "r" (s), "I" (0) - : "hi", "lo", "$0"); + : "hi", "lo"); /* We have to use single integers for m1 and m2 and a double * one for p to be sure the mulsidi3 gcc's RTL multiplication * instruction has the workaround applied. Older versions of diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index f9b228e33f3b..1ae6d0c0e1d6 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -541,6 +542,9 @@ static int __init bootcmdline_scan_chosen(unsigned long node, const char *uname, p = of_get_flat_dt_prop(node, "bootargs", &l); if (p != NULL && l > 0) { + if (strnlen(p, l) >= l) + return 1; + bootcmdline_append(p, min(l, COMMAND_LINE_SIZE)); *dt_bootargs = true; } diff --git a/arch/mips/mobileye/Kconfig b/arch/mips/mobileye/Kconfig index f9abb2d6e178..8a4868d2e28f 100644 --- a/arch/mips/mobileye/Kconfig +++ b/arch/mips/mobileye/Kconfig @@ -12,6 +12,9 @@ choice config MACH_EYEQ6H bool "Mobileye EyeQ6H SoC" + + config MACH_EYEQ6LPLUS + bool "Mobileye EyeQ6Lplus SoC" endchoice config FIT_IMAGE_FDT_EPM5 diff --git a/arch/mips/mobileye/Platform b/arch/mips/mobileye/Platform index 69f775bbbb1e..93b533492b58 100644 --- a/arch/mips/mobileye/Platform +++ b/arch/mips/mobileye/Platform @@ -10,6 +10,7 @@ load-$(CONFIG_MACH_EYEQ5) = 0xa800000808000000 load-$(CONFIG_MACH_EYEQ6H) = 0xa800000100800000 +load-$(CONFIG_MACH_EYEQ6LPLUS) = 0xa800000108800000 all-$(CONFIG_MACH_EYEQ5) += vmlinux.gz.itb its-y := vmlinux.its.S diff --git a/arch/mips/net/bpf_jit_comp.c b/arch/mips/net/bpf_jit_comp.c index e355dfca4400..6ee4abe6a1f7 100644 --- a/arch/mips/net/bpf_jit_comp.c +++ b/arch/mips/net/bpf_jit_comp.c @@ -909,12 +909,10 @@ bool bpf_jit_needs_zext(void) return true; } -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) { - struct bpf_prog *tmp, *orig_prog = prog; struct bpf_binary_header *header = NULL; struct jit_context ctx; - bool tmp_blinded = false; unsigned int tmp_idx; unsigned int image_size; u8 *image_ptr; @@ -925,19 +923,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) * the interpreter. */ if (!prog->jit_requested) - return orig_prog; - /* - * If constant blinding was enabled and we failed during blinding - * then we must fall back to the interpreter. Otherwise, we save - * the new JITed code. - */ - tmp = bpf_jit_blind_constants(prog); - if (IS_ERR(tmp)) - return orig_prog; - if (tmp != prog) { - tmp_blinded = true; - prog = tmp; - } + return prog; memset(&ctx, 0, sizeof(ctx)); ctx.program = prog; @@ -1025,14 +1011,10 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) prog->jited_len = image_size; out: - if (tmp_blinded) - bpf_jit_prog_release_other(prog, prog == orig_prog ? - tmp : orig_prog); kfree(ctx.descriptors); return prog; out_err: - prog = orig_prog; if (header) bpf_jit_binary_free(header); goto out; diff --git a/arch/mips/pci/pci-mt7620.c b/arch/mips/pci/pci-mt7620.c index 5c4bdf6919e5..aae844f27a7e 100644 --- a/arch/mips/pci/pci-mt7620.c +++ b/arch/mips/pci/pci-mt7620.c @@ -26,8 +26,9 @@ #define RALINK_INT_PCIE0 4 +#define RALINK_SYSCFG0 0x10 +#define RALINK_SYSCFG0_XTAL40 BIT(6) #define RALINK_CLKCFG1 0x30 -#define RALINK_GPIOMODE 0x60 #define PPLL_CFG1 0x9c #define PPLL_LD BIT(23) @@ -62,7 +63,7 @@ #define PCIEPHY0_CFG 0x90 -#define RALINK_PCIEPHY_P0_CTL_OFFSET 0x7498 +#define RALINK_PCIEPHY_P0_CTL_OFFSET 0x7000 #define RALINK_PCIE0_CLK_EN BIT(26) #define BUSY 0x80000000 @@ -87,6 +88,15 @@ static inline u32 bridge_r32(unsigned reg) return ioread32(bridge_base + reg); } +static inline void bridge_m32(u32 clr, u32 set, unsigned reg) +{ + u32 val = bridge_r32(reg); + + val &= ~clr; + val |= set; + bridge_w32(val, reg); +} + static inline void pcie_w32(u32 val, unsigned reg) { iowrite32(val, pcie_base + reg); @@ -106,6 +116,14 @@ static inline void pcie_m32(u32 clr, u32 set, unsigned reg) pcie_w32(val, reg); } +static inline void +pcie_phyctrl_set(unsigned offset, u32 b_start, u32 bits, u32 val) +{ + pcie_m32(GENMASK(b_start + bits - 1, b_start), + val << b_start, + RALINK_PCIEPHY_P0_CTL_OFFSET + offset); +} + static int wait_pciephy_busy(void) { unsigned long reg_value = 0x0, retry = 0; @@ -227,19 +245,6 @@ static int mt7620_pci_hw_init(struct platform_device *pdev) /* Elastic buffer control */ pcie_phy(0x68, 0xB4); - /* put core into reset */ - pcie_m32(0, PCIRST, RALINK_PCI_PCICFG_ADDR); - reset_control_assert(rstpcie0); - - /* disable power and all clocks */ - rt_sysc_m32(RALINK_PCIE0_CLK_EN, 0, RALINK_CLKCFG1); - rt_sysc_m32(LC_CKDRVPD, PDRV_SW_SET, PPLL_DRV); - - /* bring core out of reset */ - reset_control_deassert(rstpcie0); - rt_sysc_m32(0, RALINK_PCIE0_CLK_EN, RALINK_CLKCFG1); - mdelay(100); - if (!(rt_sysc_r32(PPLL_CFG1) & PPLL_LD)) { dev_err(&pdev->dev, "pcie PLL not locked, aborting init\n"); reset_control_assert(rstpcie0); @@ -254,27 +259,36 @@ static int mt7620_pci_hw_init(struct platform_device *pdev) return 0; } -static int mt7628_pci_hw_init(struct platform_device *pdev) +static void mt7628_pci_hw_init(struct platform_device *pdev) { - u32 val = 0; - - /* bring the core out of reset */ - rt_sysc_m32(BIT(16), 0, RALINK_GPIOMODE); - reset_control_deassert(rstpcie0); - - /* enable the pci clk */ - rt_sysc_m32(0, RALINK_PCIE0_CLK_EN, RALINK_CLKCFG1); - mdelay(100); - /* voodoo from the SDK driver */ - pcie_m32(~0xff, 0x5, RALINK_PCIEPHY_P0_CTL_OFFSET); + pcie_phyctrl_set(0x400, 8, 1, 0x1); + pcie_phyctrl_set(0x400, 9, 2, 0x0); + pcie_phyctrl_set(0x000, 4, 1, 0x1); + pcie_phyctrl_set(0x000, 5, 1, 0x0); + pcie_phyctrl_set(0x4ac, 16, 3, 0x3); - pci_config_read(NULL, 0, 0x70c, 4, &val); - val &= ~(0xff) << 8; - val |= 0x50 << 8; - pci_config_write(NULL, 0, 0x70c, 4, val); + if (rt_sysc_r32(RALINK_SYSCFG0) & RALINK_SYSCFG0_XTAL40) { + pcie_phyctrl_set(0x4bc, 24, 8, 0x7d); + pcie_phyctrl_set(0x490, 12, 4, 0x08); + pcie_phyctrl_set(0x490, 6, 2, 0x01); + pcie_phyctrl_set(0x4c0, 0, 32, 0x1f400000); + pcie_phyctrl_set(0x4a4, 0, 16, 0x013d); + pcie_phyctrl_set(0x4a8, 16, 16, 0x74); + pcie_phyctrl_set(0x4a8, 0, 16, 0x74); + } else { + pcie_phyctrl_set(0x4bc, 24, 8, 0x64); + pcie_phyctrl_set(0x490, 12, 4, 0x0a); + pcie_phyctrl_set(0x490, 6, 2, 0x00); + pcie_phyctrl_set(0x4c0, 0, 32, 0x19000000); + pcie_phyctrl_set(0x4a4, 0, 16, 0x018d); + pcie_phyctrl_set(0x4a8, 16, 16, 0x4a); + pcie_phyctrl_set(0x4a8, 0, 16, 0x4a); + } - return 0; + pcie_phyctrl_set(0x498, 0, 8, 0x5); + pcie_phyctrl_set(0x000, 5, 1, 0x1); + pcie_phyctrl_set(0x000, 4, 1, 0x0); } static int mt7620_pci_probe(struct platform_device *pdev) @@ -298,6 +312,16 @@ static int mt7620_pci_probe(struct platform_device *pdev) ioport_resource.start = 0; ioport_resource.end = ~0; + /* reset PCIe controller */ + reset_control_assert(rstpcie0); + msleep(100); + reset_control_deassert(rstpcie0); + rt_sysc_m32(0, RALINK_PCIE0_CLK_EN, RALINK_CLKCFG1); + msleep(100); + + /* assert PERST_N pin */ + bridge_m32(PCIRST, PCIRST, RALINK_PCI_PCICFG_ADDR); + /* bring up the pci core */ switch (ralink_soc) { case MT762X_SOC_MT7620A: @@ -307,19 +331,18 @@ static int mt7620_pci_probe(struct platform_device *pdev) case MT762X_SOC_MT7628AN: case MT762X_SOC_MT7688: - if (mt7628_pci_hw_init(pdev)) - return -1; + mt7628_pci_hw_init(pdev); break; default: dev_err(&pdev->dev, "pcie is not supported on this hardware\n"); return -1; } - mdelay(50); + msleep(500); - /* enable write access */ - pcie_m32(PCIRST, 0, RALINK_PCI_PCICFG_ADDR); - mdelay(100); + /* deassert PERST_N pin and wait PCIe peripheral init */ + bridge_m32(PCIRST, 0, RALINK_PCI_PCICFG_ADDR); + msleep(1000); /* check if there is a card present */ if ((pcie_r32(RALINK_PCI0_STATUS) & PCIE_LINK_UP_ST) == 0) { @@ -327,6 +350,8 @@ static int mt7620_pci_probe(struct platform_device *pdev) rt_sysc_m32(RALINK_PCIE0_CLK_EN, 0, RALINK_CLKCFG1); if (ralink_soc == MT762X_SOC_MT7620A) rt_sysc_m32(LC_CKDRVPD, PDRV_SW_SET, PPLL_DRV); + else + pcie_phyctrl_set(0x000, 0, 32, 0x10); dev_info(&pdev->dev, "PCIE0 no card, disable it(RST&CLK)\n"); return -1; } @@ -340,12 +365,17 @@ static int mt7620_pci_probe(struct platform_device *pdev) pcie_w32(0x06040001, RALINK_PCI0_CLASS); /* enable interrupts */ - pcie_m32(0, PCIINT2, RALINK_PCI_PCIENA); + bridge_m32(PCIINT2, PCIINT2, RALINK_PCI_PCIENA); /* voodoo from the SDK driver */ pci_config_read(NULL, 0, 4, 4, &val); pci_config_write(NULL, 0, 4, 4, val | 0x7); + pci_config_read(NULL, 0, 0x70c, 4, &val); + val &= ~(0xff) << 8; + val |= 0x50 << 8; + pci_config_write(NULL, 0, 0x70c, 4, val); + pci_load_of_ranges(&mt7620_controller, pdev->dev.of_node); register_pci_controller(&mt7620_controller); diff --git a/arch/mips/rb532/devices.c b/arch/mips/rb532/devices.c index 4f027efbf27b..c3d8d96d0ef5 100644 --- a/arch/mips/rb532/devices.c +++ b/arch/mips/rb532/devices.c @@ -14,10 +14,11 @@ #include #include #include -#include #include +#include #include #include +#include #include #include @@ -38,6 +39,10 @@ extern unsigned int idt_cpu_freq; static struct mpmc_device dev3; +static const struct software_node rb532_gpio0_node = { + .name = "gpio0", +}; + void set_latch_u5(unsigned char or_mask, unsigned char nand_mask) { unsigned long flags; @@ -129,12 +134,6 @@ static struct platform_device cf_slot0 = { .num_resources = ARRAY_SIZE(cf_slot0_res), }; -/* Resources and device for NAND */ -static int rb532_dev_ready(struct nand_chip *chip) -{ - return gpio_get_value(GPIO_RDY); -} - static void rb532_cmd_ctrl(struct nand_chip *chip, int cmd, unsigned int ctrl) { unsigned char orbits, nandbits; @@ -160,16 +159,23 @@ static struct resource nand_slot0_res[] = { }; static struct platform_nand_data rb532_nand_data = { - .ctrl.dev_ready = rb532_dev_ready, .ctrl.cmd_ctrl = rb532_cmd_ctrl, }; -static struct platform_device nand_slot0 = { - .name = "gen_nand", - .id = -1, - .resource = nand_slot0_res, - .num_resources = ARRAY_SIZE(nand_slot0_res), - .dev.platform_data = &rb532_nand_data, +static const struct property_entry nand0_properties[] = { + PROPERTY_ENTRY_GPIO("ready-gpios", &rb532_gpio0_node, + GPIO_RDY, GPIO_ACTIVE_HIGH), + { } +}; + +static const struct platform_device_info nand0_info __initconst = { + .name = "gen_nand", + .id = PLATFORM_DEVID_NONE, + .res = nand_slot0_res, + .num_res = ARRAY_SIZE(nand_slot0_res), + .data = &rb532_nand_data, + .size_data = sizeof(struct platform_nand_data), + .properties = nand0_properties, }; static struct mtd_partition rb532_partition_info[] = { @@ -189,11 +195,6 @@ static struct platform_device rb532_led = { .id = -1, }; -static struct platform_device rb532_button = { - .name = "rb532-button", - .id = -1, -}; - static struct resource rb532_wdt_res[] = { { .name = "rb532_wdt_res", @@ -233,14 +234,25 @@ static struct platform_device rb532_uart = { static struct platform_device *rb532_devs[] = { &korina_dev0, - &nand_slot0, &cf_slot0, &rb532_led, - &rb532_button, &rb532_uart, &rb532_wdt }; +static const struct property_entry rb532_button_properties[] = { + PROPERTY_ENTRY_GPIO("button-gpios", &rb532_gpio0_node, + GPIO_BTN_S1, GPIO_ACTIVE_LOW), + { } +}; + +static const struct platform_device_info rb532_button_info __initconst = { + .name = "rb532-button", + .id = PLATFORM_DEVID_NONE, + .properties = rb532_button_properties, +}; + + /* NAND definitions */ #define NAND_CHIP_DELAY 25 @@ -267,6 +279,9 @@ static void __init rb532_nand_setup(void) static int __init plat_setup_devices(void) { + struct platform_device *pd; + int ret; + /* Look for the CF card reader */ if (!readl(IDT434_REG_BASE + DEV1MASK)) rb532_devs[2] = NULL; /* disable cf_slot0 at index 2 */ @@ -295,7 +310,31 @@ static int __init plat_setup_devices(void) rb532_uart_res[0].uartclk = idt_cpu_freq; gpiod_add_lookup_table(&cf_slot0_gpio_table); - return platform_add_devices(rb532_devs, ARRAY_SIZE(rb532_devs)); + ret = platform_add_devices(rb532_devs, ARRAY_SIZE(rb532_devs)); + if (ret) + return ret; + + /* + * Stack devices using full info and properties here, after we + * register the node for the GPIO chip. + */ + software_node_register(&rb532_gpio0_node); + + pd = platform_device_register_full(&nand0_info); + ret = PTR_ERR_OR_ZERO(pd); + if (ret) { + pr_err("failed to create NAND slot0 device: %d\n", ret); + return ret; + } + + pd = platform_device_register_full(&rb532_button_info); + ret = PTR_ERR_OR_ZERO(pd); + if (ret) { + pr_err("failed to create RB532 button device: %d\n", ret); + return ret; + } + + return 0; } #ifdef CONFIG_NET diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 62d5a89d5c7b..d3afac2f0d9b 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -8,6 +8,7 @@ config PARISC select HAVE_FUNCTION_GRAPH_TRACER select HAVE_SYSCALL_TRACEPOINTS select ARCH_WANT_FRAME_POINTERS + select ARCH_WANTS_MODULES_TEXT_SECTIONS if !64BIT select ARCH_HAS_CPU_CACHE_ALIASING select ARCH_HAS_DMA_ALLOC if PA11 select ARCH_HAS_DMA_OPS @@ -130,6 +131,9 @@ config GENERIC_BUG config GENERIC_BUG_RELATIVE_POINTERS bool +config GENERIC_CSUM + def_bool y + config GENERIC_HWEIGHT bool default y @@ -354,7 +358,8 @@ config ARCH_SPARSEMEM_DEFAULT source "kernel/Kconfig.hz" config COMPAT - def_bool y + bool "Kernel support for 32-bit binaries" + default 64BIT depends on 64BIT config AUDIT_ARCH diff --git a/arch/parisc/Makefile b/arch/parisc/Makefile index 48ae3c79557a..4391783521bd 100644 --- a/arch/parisc/Makefile +++ b/arch/parisc/Makefile @@ -174,13 +174,21 @@ ifeq ($(KBUILD_EXTMOD),) # this hack. prepare: vdso_prepare vdso_prepare: prepare0 - $(if $(CONFIG_64BIT),$(Q)$(MAKE) \ - $(build)=arch/parisc/kernel/vdso64 include/generated/vdso64-offsets.h) +ifdef CONFIG_64BIT + $(Q)$(MAKE) $(build)=arch/parisc/kernel/vdso64 include/generated/vdso64-offsets.h + $(if $(CONFIG_COMPAT),$(Q)$(MAKE) \ + $(build)=arch/parisc/kernel/vdso32 include/generated/vdso32-offsets.h) +else $(Q)$(MAKE) $(build)=arch/parisc/kernel/vdso32 include/generated/vdso32-offsets.h endif +endif +ifdef CONFIG_64BIT +vdso-install-y += arch/parisc/kernel/vdso64/vdso64.so +vdso-install-$(CONFIG_COMPAT) += arch/parisc/kernel/vdso32/vdso32.so +else vdso-install-y += arch/parisc/kernel/vdso32/vdso32.so -vdso-install-$(CONFIG_64BIT) += arch/parisc/kernel/vdso64/vdso64.so +endif install: KBUILD_IMAGE := vmlinux zinstall: KBUILD_IMAGE := vmlinuz diff --git a/arch/parisc/include/asm/checksum.h b/arch/parisc/include/asm/checksum.h index 2aceebcd695c..382758808726 100644 --- a/arch/parisc/include/asm/checksum.h +++ b/arch/parisc/include/asm/checksum.h @@ -4,73 +4,7 @@ #include -/* - * computes the checksum of a memory block at buff, length len, - * and adds in "sum" (32-bit) - * - * returns a 32-bit number suitable for feeding into itself - * or csum_tcpudp_magic - * - * this function must be called with even lengths, except - * for the last fragment, which may be odd - * - * it's best to have buff aligned on a 32-bit boundary - */ -extern __wsum csum_partial(const void *, int, __wsum); - -/* - * Optimized for IP headers, which always checksum on 4 octet boundaries. - * - * Written by Randolph Chung , and then mucked with by - * LaMont Jones - */ -static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl) -{ - unsigned int sum; - unsigned long t0, t1, t2; - - __asm__ __volatile__ ( -" ldws,ma 4(%1), %0\n" -" addib,<= -4, %2, 2f\n" -"\n" -" ldws 4(%1), %4\n" -" ldws 8(%1), %5\n" -" add %0, %4, %0\n" -" ldws,ma 12(%1), %3\n" -" addc %0, %5, %0\n" -" addc %0, %3, %0\n" -"1: ldws,ma 4(%1), %3\n" -" addib,> -1, %2, 1b\n" -" addc %0, %3, %0\n" -"\n" -" extru %0, 31, 16, %4\n" -" extru %0, 15, 16, %5\n" -" addc %4, %5, %0\n" -" extru %0, 15, 16, %5\n" -" add %0, %5, %0\n" -" subi -1, %0, %0\n" -"2:\n" - : "=r" (sum), "=r" (iph), "=r" (ihl), "=r" (t0), "=r" (t1), "=r" (t2) - : "1" (iph), "2" (ihl) - : "memory"); - - return (__force __sum16)sum; -} - -/* - * Fold a partial checksum - */ -static inline __sum16 csum_fold(__wsum csum) -{ - u32 sum = (__force u32)csum; - /* add the swapped two 16-bit halves of sum, - a possible carry from adding the two 16-bit halves, - will carry from the lower half into the upper half, - giving us the correct sum in the upper half. */ - sum += (sum << 16) + (sum >> 16); - return (__force __sum16)(~sum >> 16); -} - +#define csum_tcpudp_nofold csum_tcpudp_nofold static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len, __u8 proto, __wsum sum) @@ -85,26 +19,7 @@ static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, return sum; } -/* - * computes the checksum of the TCP/UDP pseudo-header - * returns a 16-bit checksum, already complemented - */ -static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, - __u32 len, __u8 proto, - __wsum sum) -{ - return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum)); -} - -/* - * this routine is used for miscellaneous IP-like checksums, mainly - * in icmp.c - */ -static inline __sum16 ip_compute_csum(const void *buf, int len) -{ - return csum_fold (csum_partial(buf, len, 0)); -} - +#include #define _HAVE_ARCH_IPV6_CSUM static __inline__ __sum16 csum_ipv6_magic(const struct in6_addr *saddr, diff --git a/arch/parisc/include/asm/compat.h b/arch/parisc/include/asm/compat.h index 339d1b833fa7..13a5a55dec65 100644 --- a/arch/parisc/include/asm/compat.h +++ b/arch/parisc/include/asm/compat.h @@ -130,7 +130,7 @@ typedef compat_ulong_t compat_elf_gregset_t[COMPAT_ELF_NGREG]; static inline int __is_compat_task(struct task_struct *t) { - return test_tsk_thread_flag(t, TIF_32BIT); + return IS_ENABLED(CONFIG_COMPAT) && test_tsk_thread_flag(t, TIF_32BIT); } static inline int is_compat_task(void) diff --git a/arch/parisc/include/asm/vdso.h b/arch/parisc/include/asm/vdso.h index 81bc1d42802a..e5cca3c9c8e7 100644 --- a/arch/parisc/include/asm/vdso.h +++ b/arch/parisc/include/asm/vdso.h @@ -6,11 +6,14 @@ #ifdef CONFIG_64BIT #include -#endif -#include - #define VDSO64_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso64_offset_##name)) +#endif +#if !defined(CONFIG_64BIT) || defined(CONFIG_COMPAT) +#include #define VDSO32_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso32_offset_##name)) +#else +#define VDSO32_SYMBOL(tsk, name) 0UL +#endif #endif /* __ASSEMBLER__ */ diff --git a/arch/parisc/kernel/Makefile b/arch/parisc/kernel/Makefile index 9157bc8bdf41..49f937c2abbe 100644 --- a/arch/parisc/kernel/Makefile +++ b/arch/parisc/kernel/Makefile @@ -46,5 +46,9 @@ obj-$(CONFIG_KEXEC_FILE) += kexec_file.o # vdso obj-y += vdso.o -obj-$(CONFIG_64BIT) += vdso64/ +ifdef CONFIG_64BIT +obj-y += vdso64/ +obj-$(CONFIG_COMPAT) += vdso32/ +else obj-y += vdso32/ +endif diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c index bc47bbe3026e..b52ad704ec8a 100644 --- a/arch/parisc/kernel/drivers.c +++ b/arch/parisc/kernel/drivers.c @@ -41,9 +41,7 @@ const struct dma_map_ops *hppa_dma_ops __ro_after_init; EXPORT_SYMBOL(hppa_dma_ops); -static struct device root = { - .init_name = "parisc", -}; +static struct device *root; static inline int check_dev(struct device *dev) { @@ -89,7 +87,7 @@ static int for_each_padev(int (*fn)(struct device *, void *), void * data) .obj = data, .fn = fn, }; - return device_for_each_child(&root, &recurse_data, descend_children); + return device_for_each_child(root, &recurse_data, descend_children); } /** @@ -290,7 +288,7 @@ const struct parisc_device * find_pa_parent_type(const struct parisc_device *padev, int type) { const struct device *dev = &padev->dev; - while (dev != &root) { + while (dev != root) { struct parisc_device *candidate = to_parisc_device(dev); if (candidate->id.hw_type == type) return candidate; @@ -319,7 +317,7 @@ static void get_node_path(struct device *dev, struct hardware_path *path) dev = dev->parent; } - while (dev != &root) { + while (dev != root) { if (dev_is_pci(dev)) { unsigned int devfn = to_pci_dev(dev)->devfn; path->bc[i--] = PCI_SLOT(devfn) | (PCI_FUNC(devfn)<< 5); @@ -482,7 +480,7 @@ static struct parisc_device * __init alloc_tree_node( static struct parisc_device *create_parisc_device(struct hardware_path *modpath) { int i; - struct device *parent = &root; + struct device *parent = root; for (i = 0; i < 6; i++) { if (modpath->bc[i] == -1) continue; @@ -755,7 +753,7 @@ parse_tree_node(struct device *parent, int index, struct hardware_path *modpath) struct device *hwpath_to_device(struct hardware_path *modpath) { int i; - struct device *parent = &root; + struct device *parent = root; for (i = 0; i < 6; i++) { if (modpath->bc[i] == -1) continue; @@ -880,7 +878,7 @@ void __init walk_central_bus(void) { walk_native_bus(CENTRAL_BUS_ADDR, CENTRAL_BUS_ADDR + (MAX_NATIVE_DEVICES * NATIVE_DEVICE_OFFSET), - &root); + root); } static __init void print_parisc_device(struct parisc_device *dev) @@ -907,9 +905,10 @@ void __init init_parisc_bus(void) { if (bus_register(&parisc_bus_type)) panic("Could not register PA-RISC bus type\n"); - if (device_register(&root)) + + root = root_device_register("parisc"); + if (IS_ERR(root)) panic("Could not register PA-RISC root device\n"); - get_device(&root); } static __init void qemu_header(void) diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c index e8d27def6c52..64a62006bb15 100644 --- a/arch/parisc/kernel/signal.c +++ b/arch/parisc/kernel/signal.c @@ -80,7 +80,7 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall) sigset_t set; unsigned long usp = (regs->gr[30] & ~(0x01UL)); unsigned long sigframe_size = PARISC_RT_SIGFRAME_SIZE; -#ifdef CONFIG_64BIT +#ifdef CONFIG_COMPAT struct compat_rt_sigframe __user * compat_frame; if (is_compat_task()) @@ -96,7 +96,7 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall) regs->orig_r28 = 1; /* no restarts for sigreturn */ -#ifdef CONFIG_64BIT +#ifdef CONFIG_COMPAT compat_frame = (struct compat_rt_sigframe __user *)frame; if (is_compat_task()) { @@ -112,7 +112,7 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall) set_current_blocked(&set); /* Good thing we saved the old gr[30], eh? */ -#ifdef CONFIG_64BIT +#ifdef CONFIG_COMPAT if (is_compat_task()) { DBG(1, "%s: compat_frame->uc.uc_mcontext 0x%p\n", __func__, &compat_frame->uc.uc_mcontext); @@ -218,13 +218,13 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs, unsigned long haddr, sigframe_size; unsigned long start; int err = 0; -#ifdef CONFIG_64BIT +#ifdef CONFIG_COMPAT struct compat_rt_sigframe __user * compat_frame; #endif - + usp = (regs->gr[30] & ~(0x01UL)); sigframe_size = PARISC_RT_SIGFRAME_SIZE; -#ifdef CONFIG_64BIT +#ifdef CONFIG_COMPAT if (is_compat_task()) { /* The gcc alloca implementation leaves garbage in the upper 32 bits of sp */ usp = (compat_uint_t)usp; @@ -239,7 +239,7 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs, if (start >= TASK_SIZE_MAX - sigframe_size) return -EFAULT; -#ifdef CONFIG_64BIT +#ifdef CONFIG_COMPAT compat_frame = (struct compat_rt_sigframe __user *)frame; @@ -349,8 +349,8 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs, regs->gr[2] = rp; /* userland return pointer */ regs->gr[26] = ksig->sig; /* signal number */ - -#ifdef CONFIG_64BIT + +#ifdef CONFIG_COMPAT if (is_compat_task()) { regs->gr[25] = A(&compat_frame->info); /* siginfo pointer */ regs->gr[24] = A(&compat_frame->uc); /* ucontext pointer */ diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c index fcb0d8069139..1a676a9bf80c 100644 --- a/arch/parisc/kernel/sys_parisc.c +++ b/arch/parisc/kernel/sys_parisc.c @@ -50,9 +50,13 @@ static inline unsigned long COLOR_ALIGN(unsigned long addr, } +#ifdef CONFIG_COMPAT #define STACK_SIZE_DEFAULT (USER_WIDE_MODE \ ? (1 << 30) /* 1 GB */ \ : (CONFIG_STACK_MAX_DEFAULT_SIZE_MB*1024*1024)) +#else +#define STACK_SIZE_DEFAULT (1 << 30) +#endif unsigned long calc_max_stack_size(unsigned long stack_max) { diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S index f58c4bccfbce..e11c88c34eb2 100644 --- a/arch/parisc/kernel/syscall.S +++ b/arch/parisc/kernel/syscall.S @@ -241,7 +241,7 @@ linux_gateway_entry: /* Note! We cannot use the syscall table that is mapped nearby since the gateway page is mapped execute-only. */ -#ifdef CONFIG_64BIT +#ifdef CONFIG_COMPAT ldil L%sys_call_table, %r1 or,ev %r2,%r2,%r2 ldil L%sys_call_table64, %r1 @@ -250,7 +250,7 @@ linux_gateway_entry: ldo R%sys_call_table64(%r1), %r19 #else load32 sys_call_table, %r19 -#endif +#endif comiclr,>> __NR_Linux_syscalls, %r20, %r0 b,n .Lsyscall_nosys @@ -374,7 +374,7 @@ tracesys_next: /* Note! We cannot use the syscall table that is mapped nearby since the gateway page is mapped execute-only. */ -#ifdef CONFIG_64BIT +#ifdef CONFIG_COMPAT LDREG TASK_PT_GR30(%r1), %r19 /* get users sp back */ extrd,u %r19,63,1,%r2 /* W hidden in bottom bit */ @@ -1326,16 +1326,19 @@ ENTRY(lws_table) END(lws_table) /* End of lws table */ -#ifdef CONFIG_64BIT +#ifdef CONFIG_COMPAT #define __SYSCALL_WITH_COMPAT(nr, native, compat) __SYSCALL(nr, compat) #else #define __SYSCALL_WITH_COMPAT(nr, native, compat) __SYSCALL(nr, native) #endif #define __SYSCALL(nr, entry) ASM_ULONG_INSN entry + .align 8 ENTRY(sys_call_table) .export sys_call_table,data +#if defined(CONFIG_COMPAT) || !defined(CONFIG_64BIT) #include /* 32-bit syscalls */ +#endif END(sys_call_table) #ifdef CONFIG_64BIT diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl index f6e2d0379d57..c6331dad9461 100644 --- a/arch/parisc/kernel/syscalls/syscall.tbl +++ b/arch/parisc/kernel/syscalls/syscall.tbl @@ -154,7 +154,7 @@ # 137 was afs_syscall 138 common setfsuid sys_setfsuid 139 common setfsgid sys_setfsgid -140 common _llseek sys_llseek +140 32 _llseek sys_llseek 141 common getdents sys_getdents compat_sys_getdents 142 common _newselect sys_select compat_sys_select 143 common flock sys_flock diff --git a/arch/parisc/lib/Makefile b/arch/parisc/lib/Makefile index 7b197667faf6..d5975d1fb406 100644 --- a/arch/parisc/lib/Makefile +++ b/arch/parisc/lib/Makefile @@ -3,7 +3,7 @@ # Makefile for parisc-specific library files # -lib-y := lusercopy.o bitops.o checksum.o io.o memset.o memcpy.o \ +lib-y := lusercopy.o bitops.o io.o memset.o memcpy.o \ ucmpdi2.o delay.o obj-y := iomap.o diff --git a/arch/parisc/lib/checksum.c b/arch/parisc/lib/checksum.c deleted file mode 100644 index 59d8c15d81bd..000000000000 --- a/arch/parisc/lib/checksum.c +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * INET An implementation of the TCP/IP protocol suite for the LINUX - * operating system. INET is implemented using the BSD Socket - * interface as the means of communication with the user level. - * - * MIPS specific IP/TCP/UDP checksumming routines - * - * Authors: Ralf Baechle, - * Lots of code moved from tcp.c and ip.c; see those files - * for more names. - */ -#include -#include - -#include -#include -#include -#include - -#define addc(_t,_r) \ - __asm__ __volatile__ ( \ -" add %0, %1, %0\n" \ -" addc %0, %%r0, %0\n" \ - : "=r"(_t) \ - : "r"(_r), "0"(_t)); - -static inline unsigned int do_csum(const unsigned char * buff, int len) -{ - int odd, count; - unsigned int result = 0; - - if (len <= 0) - goto out; - odd = 1 & (unsigned long) buff; - if (odd) { - result = be16_to_cpu(*buff); - len--; - buff++; - } - count = len >> 1; /* nr of 16-bit words.. */ - if (count) { - if (2 & (unsigned long) buff) { - result += *(unsigned short *) buff; - count--; - len -= 2; - buff += 2; - } - count >>= 1; /* nr of 32-bit words.. */ - if (count) { - while (count >= 4) { - unsigned int r1, r2, r3, r4; - r1 = *(unsigned int *)(buff + 0); - r2 = *(unsigned int *)(buff + 4); - r3 = *(unsigned int *)(buff + 8); - r4 = *(unsigned int *)(buff + 12); - addc(result, r1); - addc(result, r2); - addc(result, r3); - addc(result, r4); - count -= 4; - buff += 16; - } - while (count) { - unsigned int w = *(unsigned int *) buff; - count--; - buff += 4; - addc(result, w); - } - result = (result & 0xffff) + (result >> 16); - } - if (len & 2) { - result += *(unsigned short *) buff; - buff += 2; - } - } - if (len & 1) - result += le16_to_cpu(*buff); - result = csum_from32to16(result); - if (odd) - result = swab16(result); -out: - return result; -} - -/* - * computes a partial checksum, e.g. for TCP/UDP fragments - */ -/* - * why bother folding? - */ -__wsum csum_partial(const void *buff, int len, __wsum sum) -{ - unsigned int result = do_csum(buff, len); - addc(result, sum); - return (__force __wsum)csum_from32to16(result); -} - -EXPORT_SYMBOL(csum_partial); diff --git a/arch/parisc/net/bpf_jit_core.c b/arch/parisc/net/bpf_jit_core.c index a5eb6b51e27a..172770132440 100644 --- a/arch/parisc/net/bpf_jit_core.c +++ b/arch/parisc/net/bpf_jit_core.c @@ -41,33 +41,22 @@ bool bpf_jit_needs_zext(void) return true; } -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) { unsigned int prog_size = 0, extable_size = 0; - bool tmp_blinded = false, extra_pass = false; - struct bpf_prog *tmp, *orig_prog = prog; + bool extra_pass = false; int pass = 0, prev_ninsns = 0, prologue_len, i; struct hppa_jit_data *jit_data; struct hppa_jit_context *ctx; if (!prog->jit_requested) - return orig_prog; - - tmp = bpf_jit_blind_constants(prog); - if (IS_ERR(tmp)) - return orig_prog; - if (tmp != prog) { - tmp_blinded = true; - prog = tmp; - } + return prog; jit_data = prog->aux->jit_data; if (!jit_data) { jit_data = kzalloc_obj(*jit_data); - if (!jit_data) { - prog = orig_prog; - goto out; - } + if (!jit_data) + return prog; prog->aux->jit_data = jit_data; } @@ -81,10 +70,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) ctx->prog = prog; ctx->offset = kzalloc_objs(int, prog->len); - if (!ctx->offset) { - prog = orig_prog; - goto out_offset; - } + if (!ctx->offset) + goto out_err; for (i = 0; i < prog->len; i++) { prev_ninsns += 20; ctx->offset[i] = prev_ninsns; @@ -93,10 +80,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) for (i = 0; i < NR_JIT_ITERATIONS; i++) { pass++; ctx->ninsns = 0; - if (build_body(ctx, extra_pass, ctx->offset)) { - prog = orig_prog; - goto out_offset; - } + if (build_body(ctx, extra_pass, ctx->offset)) + goto out_err; ctx->body_len = ctx->ninsns; bpf_jit_build_prologue(ctx); ctx->prologue_len = ctx->ninsns - ctx->body_len; @@ -116,10 +101,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) &jit_data->image, sizeof(long), bpf_fill_ill_insns); - if (!jit_data->header) { - prog = orig_prog; - goto out_offset; - } + if (!jit_data->header) + goto out_err; ctx->insns = (u32 *)jit_data->image; /* @@ -134,8 +117,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) pr_err("bpf-jit: image did not converge in <%d passes!\n", i); if (jit_data->header) bpf_jit_binary_free(jit_data->header); - prog = orig_prog; - goto out_offset; + goto out_err; } if (extable_size) @@ -148,8 +130,7 @@ skip_init_ctx: bpf_jit_build_prologue(ctx); if (build_body(ctx, extra_pass, NULL)) { bpf_jit_binary_free(jit_data->header); - prog = orig_prog; - goto out_offset; + goto out_err; } bpf_jit_build_epilogue(ctx); @@ -160,20 +141,19 @@ skip_init_ctx: { extern int machine_restart(char *); machine_restart(""); } } + if (!prog->is_func || extra_pass) { + if (bpf_jit_binary_lock_ro(jit_data->header)) { + bpf_jit_binary_free(jit_data->header); + goto out_err; + } + bpf_flush_icache(jit_data->header, ctx->insns + ctx->ninsns); + } + prog->bpf_func = (void *)ctx->insns; prog->jited = 1; prog->jited_len = prog_size; - bpf_flush_icache(jit_data->header, ctx->insns + ctx->ninsns); - if (!prog->is_func || extra_pass) { - if (bpf_jit_binary_lock_ro(jit_data->header)) { - bpf_jit_binary_free(jit_data->header); - prog->bpf_func = NULL; - prog->jited = 0; - prog->jited_len = 0; - goto out_offset; - } prologue_len = ctx->epilogue_offset - ctx->body_len; for (i = 0; i < prog->len; i++) ctx->offset[i] += prologue_len; @@ -183,14 +163,19 @@ out_offset: kfree(jit_data); prog->aux->jit_data = NULL; } -out: + if (HPPA_JIT_REBOOT) { extern int machine_restart(char *); machine_restart(""); } - if (tmp_blinded) - bpf_jit_prog_release_other(prog, prog == orig_prog ? - tmp : orig_prog); return prog; + +out_err: + if (extra_pass) { + prog->bpf_func = NULL; + prog->jited = 0; + prog->jited_len = 0; + } + goto out_offset; } u64 hppa_div64(u64 div, u64 divisor) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 1075d0aa0163..e93df95b79e7 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -1058,6 +1058,7 @@ config PPC_SECURE_BOOT depends on IMA_ARCH_POLICY imply IMA_SECURE_AND_OR_TRUSTED_BOOT select PSERIES_PLPKS if PPC_PSERIES + select HAVE_ARCH_GET_SECUREBOOT help Systems with firmware secure boot enabled need to define security policies to extend secure boot to the OS. This config allows a user diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index f15e5920080b..e8718bc13eeb 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -83,11 +83,10 @@ config MSI_BITMAP_SELFTEST depends on DEBUG_KERNEL config GUEST_STATE_BUFFER_TEST - def_tristate n + def_tristate KUNIT_ALL_TESTS prompt "Enable Guest State Buffer unit tests" depends on KUNIT depends on KVM_BOOK3S_HV_POSSIBLE - default KUNIT_ALL_TESTS help The Guest State Buffer is a data format specified in the PAPR. It is by hcalls to communicate the state of L2 guests between diff --git a/arch/powerpc/configs/amigaone_defconfig b/arch/powerpc/configs/amigaone_defconfig index 69ef3dc31c4b..7a515390646b 100644 --- a/arch/powerpc/configs/amigaone_defconfig +++ b/arch/powerpc/configs/amigaone_defconfig @@ -76,7 +76,6 @@ CONFIG_SERIAL_8250_CONSOLE=y # CONFIG_HW_RANDOM is not set # CONFIG_HWMON is not set CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_TILEBLITTING=y CONFIG_FB_RADEON=y CONFIG_FB_3DFX=y diff --git a/arch/powerpc/configs/chrp32_defconfig b/arch/powerpc/configs/chrp32_defconfig index b799c95480ae..66eae5b7e16c 100644 --- a/arch/powerpc/configs/chrp32_defconfig +++ b/arch/powerpc/configs/chrp32_defconfig @@ -76,7 +76,6 @@ CONFIG_SERIAL_8250_CONSOLE=y CONFIG_NVRAM=y # CONFIG_HWMON is not set CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_OF=y CONFIG_FB_MATROX=y CONFIG_FB_MATROX_MILLENIUM=y diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig index 04bbb37f5978..5ca1676e6058 100644 --- a/arch/powerpc/configs/g5_defconfig +++ b/arch/powerpc/configs/g5_defconfig @@ -85,6 +85,8 @@ CONFIG_PMAC_SMU=y CONFIG_MAC_EMUMOUSEBTN=y CONFIG_WINDFARM=y CONFIG_WINDFARM_PM81=y +CONFIG_WINDFARM_PM72=y +CONFIG_WINDFARM_RM31=y CONFIG_WINDFARM_PM91=y CONFIG_WINDFARM_PM112=y CONFIG_WINDFARM_PM121=y @@ -121,7 +123,6 @@ CONFIG_I2C_CHARDEV=y CONFIG_AGP=m CONFIG_AGP_UNINORTH=m CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_TILEBLITTING=y CONFIG_FB_OF=y CONFIG_FB_NVIDIA=y diff --git a/arch/powerpc/configs/pasemi_defconfig b/arch/powerpc/configs/pasemi_defconfig index 8bbf51b38480..89bcbeb05067 100644 --- a/arch/powerpc/configs/pasemi_defconfig +++ b/arch/powerpc/configs/pasemi_defconfig @@ -98,7 +98,6 @@ CONFIG_SENSORS_LM85=y CONFIG_SENSORS_LM90=y CONFIG_DRM=y CONFIG_DRM_RADEON=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_TILEBLITTING=y CONFIG_FB_VGA16=y CONFIG_FB_NVIDIA=y diff --git a/arch/powerpc/configs/powernv_defconfig b/arch/powerpc/configs/powernv_defconfig index cc9802420237..5d32c2767a65 100644 --- a/arch/powerpc/configs/powernv_defconfig +++ b/arch/powerpc/configs/powernv_defconfig @@ -196,7 +196,6 @@ CONFIG_I2C_CHARDEV=y # CONFIG_PTP_1588_CLOCK is not set CONFIG_DRM=y CONFIG_DRM_AST=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_OF=y CONFIG_FB_MATROX=m CONFIG_FB_MATROX_MILLENIUM=y diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 3bf518e3a573..6316ca4df25d 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -249,7 +249,6 @@ CONFIG_I2C_CHARDEV=y CONFIG_I2C_AMD8111=y CONFIG_I2C_PASEMI=y CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_OF=y CONFIG_FB_MATROX=y CONFIG_FB_MATROX_MILLENIUM=y diff --git a/arch/powerpc/configs/ppc64e_defconfig b/arch/powerpc/configs/ppc64e_defconfig index 0fd49f67331f..20cc17dce94d 100644 --- a/arch/powerpc/configs/ppc64e_defconfig +++ b/arch/powerpc/configs/ppc64e_defconfig @@ -118,7 +118,6 @@ CONFIG_SERIAL_8250_CONSOLE=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_AMD8111=y CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_OF=y CONFIG_FB_MATROX=y CONFIG_FB_MATROX_MILLENIUM=y diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index 6f40a275b7a9..ccabc6e17168 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -72,8 +72,6 @@ CONFIG_PCI_MSI=y CONFIG_PCCARD=y CONFIG_YENTA=y CONFIG_PD6729=m -CONFIG_I82092=m -CONFIG_I82365=m CONFIG_ADVANCED_OPTIONS=y CONFIG_NET=y CONFIG_PACKET=y @@ -227,8 +225,6 @@ CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m CONFIG_TIPC=m CONFIG_ATM=m -CONFIG_ATM_CLIP=m -CONFIG_ATM_LANE=m CONFIG_ATM_BR2684=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m @@ -240,7 +236,6 @@ CONFIG_NET_SCHED=y CONFIG_NET_SCH_CBQ=m CONFIG_NET_SCH_HTB=m CONFIG_NET_SCH_HFSC=m -CONFIG_NET_SCH_ATM=m CONFIG_NET_SCH_PRIO=m CONFIG_NET_SCH_MULTIQ=m CONFIG_NET_SCH_RED=m @@ -398,21 +393,11 @@ CONFIG_NETCONSOLE=m CONFIG_TUN=m CONFIG_VETH=m CONFIG_VIRTIO_NET=m -CONFIG_ATM_TCP=m -CONFIG_ATM_LANAI=m -CONFIG_ATM_ENI=m -CONFIG_ATM_NICSTAR=m -CONFIG_ATM_IDT77252=m -CONFIG_ATM_HE=m -CONFIG_EL3=m -CONFIG_PCMCIA_3C574=m -CONFIG_PCMCIA_3C589=m CONFIG_VORTEX=m CONFIG_TYPHOON=m CONFIG_ADAPTEC_STARFIRE=m CONFIG_AMD8111_ETH=m CONFIG_PCNET32=m -CONFIG_PCMCIA_NMCLAN=m CONFIG_MACE=m CONFIG_BMAC=m CONFIG_ATL1=m @@ -435,7 +420,6 @@ CONFIG_DL2K=m CONFIG_SUNDANCE=m CONFIG_FEC_MPC52xx=m CONFIG_GIANFAR=m -CONFIG_PCMCIA_FMVJ18X=m CONFIG_E100=m CONFIG_E1000=m CONFIG_E1000E=m @@ -448,14 +432,10 @@ CONFIG_MYRI10GE=m CONFIG_FEALNX=m CONFIG_NATSEMI=m CONFIG_NS83820=m -CONFIG_PCMCIA_AXNET=m CONFIG_NE2000=m CONFIG_NE2K_PCI=m CONFIG_PCMCIA_PCNET=m -CONFIG_ULTRA=m CONFIG_FORCEDETH=m -CONFIG_HAMACHI=m -CONFIG_YELLOWFIN=m CONFIG_QLA3XXX=m CONFIG_NETXEN_NIC=m CONFIG_8139CP=m @@ -468,7 +448,6 @@ CONFIG_SC92031=m CONFIG_SIS900=m CONFIG_SIS190=m CONFIG_SFC=m -CONFIG_PCMCIA_SMC91C92=m CONFIG_EPIC100=m CONFIG_HAPPYMEAL=m CONFIG_SUNGEM=m diff --git a/arch/powerpc/configs/skiroot_defconfig b/arch/powerpc/configs/skiroot_defconfig index ff1bed4b6d2c..005536ee75bb 100644 --- a/arch/powerpc/configs/skiroot_defconfig +++ b/arch/powerpc/configs/skiroot_defconfig @@ -214,7 +214,6 @@ CONFIG_SENSORS_IBMPOWERNV=m CONFIG_DRM=m CONFIG_DRM_AST=m CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y # CONFIG_VGA_CONSOLE is not set CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y diff --git a/arch/powerpc/include/asm/book3s/32/pgtable.h b/arch/powerpc/include/asm/book3s/32/pgtable.h index 4a271318dee8..e18a4fa282a1 100644 --- a/arch/powerpc/include/asm/book3s/32/pgtable.h +++ b/arch/powerpc/include/asm/book3s/32/pgtable.h @@ -438,7 +438,7 @@ static inline bool pte_access_permitted(pte_t pte, bool write) return true; } -static inline bool pte_user_accessible_page(pte_t pte, unsigned long addr) +static inline bool pte_user_accessible_page(struct mm_struct *mm, unsigned long addr, pte_t pte) { return pte_present(pte) && !is_kernel_addr(addr); } diff --git a/arch/powerpc/include/asm/book3s/64/pgtable.h b/arch/powerpc/include/asm/book3s/64/pgtable.h index a8003508bd80..e67e64ac6e8c 100644 --- a/arch/powerpc/include/asm/book3s/64/pgtable.h +++ b/arch/powerpc/include/asm/book3s/64/pgtable.h @@ -549,7 +549,7 @@ static inline bool pte_access_permitted(pte_t pte, bool write) return arch_pte_access_permitted(pte_val(pte), write, 0); } -static inline bool pte_user_accessible_page(pte_t pte, unsigned long addr) +static inline bool pte_user_accessible_page(struct mm_struct *mm, unsigned long addr, pte_t pte) { return pte_present(pte) && pte_user(pte); } @@ -925,9 +925,9 @@ static inline bool pud_access_permitted(pud_t pud, bool write) } #define pud_user_accessible_page pud_user_accessible_page -static inline bool pud_user_accessible_page(pud_t pud, unsigned long addr) +static inline bool pud_user_accessible_page(struct mm_struct *mm, unsigned long addr, pud_t pud) { - return pud_leaf(pud) && pte_user_accessible_page(pud_pte(pud), addr); + return pud_leaf(pud) && pte_user_accessible_page(mm, addr, pud_pte(pud)); } #define __p4d_raw(x) ((p4d_t) { __pgd_raw(x) }) @@ -1096,9 +1096,9 @@ static inline bool pmd_access_permitted(pmd_t pmd, bool write) } #define pmd_user_accessible_page pmd_user_accessible_page -static inline bool pmd_user_accessible_page(pmd_t pmd, unsigned long addr) +static inline bool pmd_user_accessible_page(struct mm_struct *mm, unsigned long addr, pmd_t pmd) { - return pmd_leaf(pmd) && pte_user_accessible_page(pmd_pte(pmd), addr); + return pmd_leaf(pmd) && pte_user_accessible_page(mm, addr, pmd_pte(pmd)); } #ifdef CONFIG_TRANSPARENT_HUGEPAGE diff --git a/arch/powerpc/include/asm/nohash/pgtable.h b/arch/powerpc/include/asm/nohash/pgtable.h index 3a6f20a1c800..59375f354f3b 100644 --- a/arch/powerpc/include/asm/nohash/pgtable.h +++ b/arch/powerpc/include/asm/nohash/pgtable.h @@ -249,7 +249,7 @@ static inline bool pte_access_permitted(pte_t pte, bool write) return true; } -static inline bool pte_user_accessible_page(pte_t pte, unsigned long addr) +static inline bool pte_user_accessible_page(struct mm_struct *mm, unsigned long addr, pte_t pte) { return pte_present(pte) && !is_kernel_addr(addr); } diff --git a/arch/powerpc/include/asm/pgtable.h b/arch/powerpc/include/asm/pgtable.h index 138289a92c4c..d20ff2ae02f5 100644 --- a/arch/powerpc/include/asm/pgtable.h +++ b/arch/powerpc/include/asm/pgtable.h @@ -213,11 +213,11 @@ static inline bool arch_supports_memmap_on_memory(unsigned long vmemmap_size) #endif /* CONFIG_PPC64 */ #ifndef pmd_user_accessible_page -#define pmd_user_accessible_page(pmd, addr) false +#define pmd_user_accessible_page(mm, addr, pmd) false #endif #ifndef pud_user_accessible_page -#define pud_user_accessible_page(pud, addr) false +#define pud_user_accessible_page(mm, addr, pud) false #endif #endif /* __ASSEMBLER__ */ diff --git a/arch/powerpc/include/asm/pmac_low_i2c.h b/arch/powerpc/include/asm/pmac_low_i2c.h index 21bd7297c87f..fead8fae08ab 100644 --- a/arch/powerpc/include/asm/pmac_low_i2c.h +++ b/arch/powerpc/include/asm/pmac_low_i2c.h @@ -79,10 +79,6 @@ extern int pmac_i2c_match_adapter(struct device_node *dev, struct i2c_adapter *adapter); -/* (legacy) Locking functions exposed to i2c-keywest */ -extern int pmac_low_i2c_lock(struct device_node *np); -extern int pmac_low_i2c_unlock(struct device_node *np); - /* Access functions for platform code */ extern int pmac_i2c_open(struct pmac_i2c_bus *bus, int polled); extern void pmac_i2c_close(struct pmac_i2c_bus *bus); diff --git a/arch/powerpc/include/asm/xor.h b/arch/powerpc/include/asm/xor.h deleted file mode 100644 index 37d05c11d09c..000000000000 --- a/arch/powerpc/include/asm/xor.h +++ /dev/null @@ -1,47 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * - * Copyright (C) IBM Corporation, 2012 - * - * Author: Anton Blanchard - */ -#ifndef _ASM_POWERPC_XOR_H -#define _ASM_POWERPC_XOR_H - -#ifdef CONFIG_ALTIVEC - -#include -#include -#include - -static struct xor_block_template xor_block_altivec = { - .name = "altivec", - .do_2 = xor_altivec_2, - .do_3 = xor_altivec_3, - .do_4 = xor_altivec_4, - .do_5 = xor_altivec_5, -}; - -#define XOR_SPEED_ALTIVEC() \ - do { \ - if (cpu_has_feature(CPU_FTR_ALTIVEC)) \ - xor_speed(&xor_block_altivec); \ - } while (0) -#else -#define XOR_SPEED_ALTIVEC() -#endif - -/* Also try the generic routines. */ -#include - -#undef XOR_TRY_TEMPLATES -#define XOR_TRY_TEMPLATES \ -do { \ - xor_speed(&xor_block_8regs); \ - xor_speed(&xor_block_8regs_p); \ - xor_speed(&xor_block_32regs); \ - xor_speed(&xor_block_32regs_p); \ - XOR_SPEED_ALTIVEC(); \ -} while (0) - -#endif /* _ASM_POWERPC_XOR_H */ diff --git a/arch/powerpc/include/asm/xor_altivec.h b/arch/powerpc/include/asm/xor_altivec.h deleted file mode 100644 index 294620a25f80..000000000000 --- a/arch/powerpc/include/asm/xor_altivec.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _ASM_POWERPC_XOR_ALTIVEC_H -#define _ASM_POWERPC_XOR_ALTIVEC_H - -#ifdef CONFIG_ALTIVEC -void xor_altivec_2(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2); -void xor_altivec_3(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3); -void xor_altivec_4(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4); -void xor_altivec_5(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4, - const unsigned long * __restrict p5); - -#endif -#endif /* _ASM_POWERPC_XOR_ALTIVEC_H */ diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index 4ebc333dd786..501d43bf18f3 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -775,24 +775,12 @@ void __init fadump_update_elfcore_header(char *bufp) static void *__init fadump_alloc_buffer(unsigned long size) { - unsigned long count, i; - struct page *page; - void *vaddr; - - vaddr = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); - if (!vaddr) - return NULL; - - count = PAGE_ALIGN(size) / PAGE_SIZE; - page = virt_to_page(vaddr); - for (i = 0; i < count; i++) - mark_page_reserved(page + i); - return vaddr; + return alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); } static void fadump_free_buffer(unsigned long vaddr, unsigned long size) { - free_reserved_area((void *)vaddr, (void *)(vaddr + size), -1, NULL); + free_pages_exact((void *)vaddr, size); } s32 __init fadump_setup_cpu_notes_buf(u32 num_cpus) diff --git a/arch/powerpc/kernel/ima_arch.c b/arch/powerpc/kernel/ima_arch.c index b7029beed847..17f9304855e9 100644 --- a/arch/powerpc/kernel/ima_arch.c +++ b/arch/powerpc/kernel/ima_arch.c @@ -7,11 +7,6 @@ #include #include -bool arch_ima_get_secureboot(void) -{ - return is_ppc_secureboot_enabled(); -} - /* * The "secure_rules" are enabled only on "secureboot" enabled systems. * These rules verify the file signatures against known good values. @@ -63,8 +58,7 @@ static const char *const secure_and_trusted_rules[] = { const char *const *arch_get_ima_policy(void) { if (is_ppc_secureboot_enabled()) { - if (IS_ENABLED(CONFIG_MODULE_SIG)) - set_module_sig_enforced(); + set_module_sig_enforced(); if (is_ppc_trustedboot_enabled()) return secure_and_trusted_rules; diff --git a/arch/powerpc/kernel/secure_boot.c b/arch/powerpc/kernel/secure_boot.c index 3a28795b4ed8..28436c1599e0 100644 --- a/arch/powerpc/kernel/secure_boot.c +++ b/arch/powerpc/kernel/secure_boot.c @@ -5,6 +5,7 @@ */ #include #include +#include #include #include @@ -44,6 +45,11 @@ out: return enabled; } +bool arch_get_secureboot(void) +{ + return is_ppc_secureboot_enabled(); +} + bool is_ppc_trustedboot_enabled(void) { struct device_node *node; diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 4bbeb8644d3d..b4472288e0d4 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -458,6 +458,10 @@ DEFINE_PER_CPU(u8, irq_work_pending); #endif /* 32 vs 64 bit */ +/* + * Must be called with preemption disabled since it updates + * per-CPU irq_work state and programs the local CPU decrementer. + */ void arch_irq_work_raise(void) { /* @@ -471,10 +475,8 @@ void arch_irq_work_raise(void) * which could get tangled up if we're messing with the same state * here. */ - preempt_disable(); set_irq_work_pending_flag(); set_dec(1); - preempt_enable(); } static void set_dec_or_work(u64 val) diff --git a/arch/powerpc/kernel/vdso/Makefile b/arch/powerpc/kernel/vdso/Makefile index 8834dfe9d727..368759f81708 100644 --- a/arch/powerpc/kernel/vdso/Makefile +++ b/arch/powerpc/kernel/vdso/Makefile @@ -62,6 +62,12 @@ CC32FLAGSREMOVE += -fno-stack-clash-protection # 32-bit one. clang validates the values passed to these arguments during # parsing, even when -fno-stack-protector is passed afterwards. CC32FLAGSREMOVE += -mstack-protector-guard% +# ftrace is disabled for the vdso but arch/powerpc/Makefile adds this define to +# KBUILD_CPPFLAGS, which enables use of the 'patchable_function_entry' +# attribute in the 'inline' define via 'notrace'. This attribute is not +# supported for the powerpcle target, resulting in many instances of +# -Wunknown-attributes. +CC32FLAGSREMOVE += -DCC_USING_PATCHABLE_FUNCTION_ENTRY endif LD32FLAGS := -Wl,-soname=linux-vdso32.so.1 AS32FLAGS := -D__VDSO32__ diff --git a/arch/powerpc/kexec/Makefile b/arch/powerpc/kexec/Makefile index 470eb0453e17..ec7a0eed75dc 100644 --- a/arch/powerpc/kexec/Makefile +++ b/arch/powerpc/kexec/Makefile @@ -16,4 +16,4 @@ GCOV_PROFILE_core_$(BITS).o := n KCOV_INSTRUMENT_core_$(BITS).o := n UBSAN_SANITIZE_core_$(BITS).o := n KASAN_SANITIZE_core.o := n -KASAN_SANITIZE_core_$(BITS) := n +KASAN_SANITIZE_core_$(BITS).o := n diff --git a/arch/powerpc/kexec/elf_64.c b/arch/powerpc/kexec/elf_64.c index 5d6d616404cf..ea50a072debf 100644 --- a/arch/powerpc/kexec/elf_64.c +++ b/arch/powerpc/kexec/elf_64.c @@ -79,6 +79,10 @@ static void *elf64_load(struct kimage *image, char *kernel_buf, goto out; } + ret = crash_load_dm_crypt_keys(image); + if (ret) + goto out; + /* Setup cmdline for kdump kernel case */ modified_cmdline = setup_kdump_cmdline(image, cmdline, cmdline_len); diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index bcdf387f3998..b1b7a1419653 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -72,9 +72,4 @@ obj-$(CONFIG_PPC_LIB_RHEAP) += rheap.o obj-$(CONFIG_FTR_FIXUP_SELFTEST) += feature-fixups-test.o -obj-$(CONFIG_ALTIVEC) += xor_vmx.o xor_vmx_glue.o -CFLAGS_xor_vmx.o += -mhard-float -maltivec $(call cc-option,-mabi=altivec) -# Enable -CFLAGS_xor_vmx.o += -isystem $(shell $(CC) -print-file-name=include) - obj-$(CONFIG_PPC64) += $(obj64-y) diff --git a/arch/powerpc/lib/vmx-helper.c b/arch/powerpc/lib/vmx-helper.c index 554b248002b4..57e897b60db8 100644 --- a/arch/powerpc/lib/vmx-helper.c +++ b/arch/powerpc/lib/vmx-helper.c @@ -52,7 +52,14 @@ int exit_vmx_usercopy(void) } EXPORT_SYMBOL(exit_vmx_usercopy); -int enter_vmx_ops(void) +/* + * Can be called from kexec copy_page() path with MMU off. The kexec + * code sets preempt_count to HARDIRQ_OFFSET so we return early here. + * Since in_interrupt() is always inline, __no_sanitize_address on this + * function is sufficient to avoid KASAN shadow memory accesses in real + * mode. + */ +int __no_sanitize_address enter_vmx_ops(void) { if (in_interrupt()) return 0; diff --git a/arch/powerpc/lib/xor_vmx.h b/arch/powerpc/lib/xor_vmx.h deleted file mode 100644 index 573c41d90dac..000000000000 --- a/arch/powerpc/lib/xor_vmx.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Simple interface to link xor_vmx.c and xor_vmx_glue.c - * - * Separating these file ensures that no altivec instructions are run - * outside of the enable/disable altivec block. - */ - -void __xor_altivec_2(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2); -void __xor_altivec_3(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3); -void __xor_altivec_4(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4); -void __xor_altivec_5(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4, - const unsigned long * __restrict p5); diff --git a/arch/powerpc/lib/xor_vmx_glue.c b/arch/powerpc/lib/xor_vmx_glue.c deleted file mode 100644 index 35d917ece4d1..000000000000 --- a/arch/powerpc/lib/xor_vmx_glue.c +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Altivec XOR operations - * - * Copyright 2017 IBM Corp. - */ - -#include -#include -#include -#include -#include -#include "xor_vmx.h" - -void xor_altivec_2(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2) -{ - preempt_disable(); - enable_kernel_altivec(); - __xor_altivec_2(bytes, p1, p2); - disable_kernel_altivec(); - preempt_enable(); -} -EXPORT_SYMBOL(xor_altivec_2); - -void xor_altivec_3(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3) -{ - preempt_disable(); - enable_kernel_altivec(); - __xor_altivec_3(bytes, p1, p2, p3); - disable_kernel_altivec(); - preempt_enable(); -} -EXPORT_SYMBOL(xor_altivec_3); - -void xor_altivec_4(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4) -{ - preempt_disable(); - enable_kernel_altivec(); - __xor_altivec_4(bytes, p1, p2, p3, p4); - disable_kernel_altivec(); - preempt_enable(); -} -EXPORT_SYMBOL(xor_altivec_4); - -void xor_altivec_5(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4, - const unsigned long * __restrict p5) -{ - preempt_disable(); - enable_kernel_altivec(); - __xor_altivec_5(bytes, p1, p2, p3, p4, p5); - disable_kernel_altivec(); - preempt_enable(); -} -EXPORT_SYMBOL(xor_altivec_5); diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 50103b3794fb..53ab97ad6074 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -162,7 +162,7 @@ static void priv_stack_check_guard(void __percpu *priv_stack_ptr, int alloc_size } } -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *fp) { u32 proglen; u32 alloclen; @@ -177,9 +177,6 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) void __percpu *priv_stack_ptr = NULL; struct bpf_binary_header *fhdr = NULL; struct bpf_binary_header *hdr = NULL; - struct bpf_prog *org_fp = fp; - struct bpf_prog *tmp_fp = NULL; - bool bpf_blinded = false; bool extra_pass = false; u8 *fimage = NULL; u32 *fcode_base = NULL; @@ -187,24 +184,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) u32 fixup_len; if (!fp->jit_requested) - return org_fp; - - tmp_fp = bpf_jit_blind_constants(org_fp); - if (IS_ERR(tmp_fp)) - return org_fp; - - if (tmp_fp != org_fp) { - bpf_blinded = true; - fp = tmp_fp; - } + return fp; jit_data = fp->aux->jit_data; if (!jit_data) { jit_data = kzalloc_obj(*jit_data); - if (!jit_data) { - fp = org_fp; - goto out; - } + if (!jit_data) + return fp; fp->aux->jit_data = jit_data; } @@ -219,10 +205,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) priv_stack_alloc_size = round_up(fp->aux->stack_depth, 16) + 2 * PRIV_STACK_GUARD_SZ; priv_stack_ptr = __alloc_percpu_gfp(priv_stack_alloc_size, 16, GFP_KERNEL); - if (!priv_stack_ptr) { - fp = org_fp; + if (!priv_stack_ptr) goto out_priv_stack; - } priv_stack_init_guard(priv_stack_ptr, priv_stack_alloc_size); fp->aux->priv_stack_ptr = priv_stack_ptr; @@ -249,10 +233,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) } addrs = kcalloc(flen + 1, sizeof(*addrs), GFP_KERNEL); - if (addrs == NULL) { - fp = org_fp; - goto out_addrs; - } + if (addrs == NULL) + goto out_err; memset(&cgctx, 0, sizeof(struct codegen_context)); bpf_jit_init_reg_mapping(&cgctx); @@ -279,11 +261,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) } /* Scouting faux-generate pass 0 */ - if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) { + if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) /* We hit something illegal or unsupported. */ - fp = org_fp; - goto out_addrs; - } + goto out_err; /* * If we have seen a tail call, we need a second pass. @@ -294,10 +274,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) */ if (cgctx.seen & SEEN_TAILCALL || !is_offset_in_branch_range((long)cgctx.idx * 4)) { cgctx.idx = 0; - if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) { - fp = org_fp; - goto out_addrs; - } + if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) + goto out_err; } bpf_jit_realloc_regs(&cgctx); @@ -318,10 +296,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) fhdr = bpf_jit_binary_pack_alloc(alloclen, &fimage, 4, &hdr, &image, bpf_jit_fill_ill_insns); - if (!fhdr) { - fp = org_fp; - goto out_addrs; - } + if (!fhdr) + goto out_err; if (extable_len) fp->aux->extable = (void *)fimage + FUNCTION_DESCR_SIZE + proglen + fixup_len; @@ -340,8 +316,7 @@ skip_init_ctx: extra_pass)) { bpf_arch_text_copy(&fhdr->size, &hdr->size, sizeof(hdr->size)); bpf_jit_binary_pack_free(fhdr, hdr); - fp = org_fp; - goto out_addrs; + goto out_err; } bpf_jit_build_epilogue(code_base, &cgctx); @@ -363,15 +338,16 @@ skip_init_ctx: ((u64 *)image)[1] = local_paca->kernel_toc; #endif + if (!fp->is_func || extra_pass) { + if (bpf_jit_binary_pack_finalize(fhdr, hdr)) + goto out_err; + } + fp->bpf_func = (void *)fimage; fp->jited = 1; fp->jited_len = cgctx.idx * 4 + FUNCTION_DESCR_SIZE; if (!fp->is_func || extra_pass) { - if (bpf_jit_binary_pack_finalize(fhdr, hdr)) { - fp = org_fp; - goto out_addrs; - } bpf_prog_fill_jited_linfo(fp, addrs); /* * On ABI V1, executable code starts after the function @@ -398,11 +374,15 @@ out_priv_stack: jit_data->hdr = hdr; } -out: - if (bpf_blinded) - bpf_jit_prog_release_other(fp, fp == org_fp ? tmp_fp : org_fp); - return fp; + +out_err: + if (extra_pass) { + fp->bpf_func = NULL; + fp->jited = 0; + fp->jited_len = 0; + } + goto out_addrs; } /* diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 8b0081441f85..2e6adf5b95c4 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -2242,6 +2242,7 @@ static void record_and_restart(struct perf_event *event, unsigned long val, const u64 last_period = event->hw.last_period; s64 prev, delta, left; int record = 0; + int mark_event = regs->dsisr & MMCRA_SAMPLE_ENABLE; if (event->hw.state & PERF_HES_STOPPED) { write_pmc(event->hw.idx, 0); @@ -2304,9 +2305,9 @@ static void record_and_restart(struct perf_event *event, unsigned long val, * In ISA v3.0 and before values "0" and "7" are considered reserved. * In ISA v3.1, value "7" has been used to indicate "larx/stcx". * Drop the sample if "type" has reserved values for this field with a - * ISA version check. + * ISA version check for marked events. */ - if (event->attr.sample_type & PERF_SAMPLE_DATA_SRC && + if (mark_event && event->attr.sample_type & PERF_SAMPLE_DATA_SRC && ppmu->get_mem_data_src) { val = (regs->dar & SIER_TYPE_MASK) >> SIER_TYPE_SHIFT; if (val == 0 || (val == 7 && !cpu_has_feature(CPU_FTR_ARCH_31))) { diff --git a/arch/powerpc/perf/hv-gpci.c b/arch/powerpc/perf/hv-gpci.c index 5cac2cf3bd1e..10c82cf8f5b3 100644 --- a/arch/powerpc/perf/hv-gpci.c +++ b/arch/powerpc/perf/hv-gpci.c @@ -210,7 +210,7 @@ static ssize_t processor_bus_topology_show(struct device *dev, struct device_att 0, 0, buf, &n, arg); if (!ret) - return n; + goto out_success; if (ret != H_PARAMETER) goto out; @@ -244,12 +244,14 @@ static ssize_t processor_bus_topology_show(struct device *dev, struct device_att starting_index, 0, buf, &n, arg); if (!ret) - return n; + goto out_success; if (ret != H_PARAMETER) goto out; } +out_success: + put_cpu_var(hv_gpci_reqb); return n; out: @@ -278,7 +280,7 @@ static ssize_t processor_config_show(struct device *dev, struct device_attribute 0, 0, buf, &n, arg); if (!ret) - return n; + goto out_success; if (ret != H_PARAMETER) goto out; @@ -312,12 +314,14 @@ static ssize_t processor_config_show(struct device *dev, struct device_attribute starting_index, 0, buf, &n, arg); if (!ret) - return n; + goto out_success; if (ret != H_PARAMETER) goto out; } +out_success: + put_cpu_var(hv_gpci_reqb); return n; out: @@ -346,7 +350,7 @@ static ssize_t affinity_domain_via_virtual_processor_show(struct device *dev, 0, 0, buf, &n, arg); if (!ret) - return n; + goto out_success; if (ret != H_PARAMETER) goto out; @@ -382,12 +386,14 @@ static ssize_t affinity_domain_via_virtual_processor_show(struct device *dev, starting_index, secondary_index, buf, &n, arg); if (!ret) - return n; + goto out_success; if (ret != H_PARAMETER) goto out; } +out_success: + put_cpu_var(hv_gpci_reqb); return n; out: @@ -416,7 +422,7 @@ static ssize_t affinity_domain_via_domain_show(struct device *dev, struct device 0, 0, buf, &n, arg); if (!ret) - return n; + goto out_success; if (ret != H_PARAMETER) goto out; @@ -448,12 +454,14 @@ static ssize_t affinity_domain_via_domain_show(struct device *dev, struct device starting_index, 0, buf, &n, arg); if (!ret) - return n; + goto out_success; if (ret != H_PARAMETER) goto out; } +out_success: + put_cpu_var(hv_gpci_reqb); return n; out: diff --git a/arch/powerpc/platforms/44x/warp.c b/arch/powerpc/platforms/44x/warp.c index a5001d32f978..6f674f86dc85 100644 --- a/arch/powerpc/platforms/44x/warp.c +++ b/arch/powerpc/platforms/44x/warp.c @@ -293,6 +293,8 @@ static int pika_dtm_thread(void __iomem *fpga) schedule_timeout(HZ); } + put_device(&client->dev); + return 0; } diff --git a/arch/powerpc/platforms/82xx/km82xx.c b/arch/powerpc/platforms/82xx/km82xx.c index 99f0f0f41876..4ad223525e89 100644 --- a/arch/powerpc/platforms/82xx/km82xx.c +++ b/arch/powerpc/platforms/82xx/km82xx.c @@ -27,8 +27,8 @@ static void __init km82xx_pic_init(void) { - struct device_node *np __free(device_node); - np = of_find_compatible_node(NULL, NULL, "fsl,pq2-pic"); + struct device_node *np __free(device_node) = of_find_compatible_node(NULL, + NULL, "fsl,pq2-pic"); if (!np) { pr_err("PIC init: can not find cpm-pic node\n"); diff --git a/arch/powerpc/platforms/8xx/cpm1.c b/arch/powerpc/platforms/8xx/cpm1.c index 7433be7d66ee..f00734f0590c 100644 --- a/arch/powerpc/platforms/8xx/cpm1.c +++ b/arch/powerpc/platforms/8xx/cpm1.c @@ -477,7 +477,7 @@ int cpm1_gpiochip_add16(struct device *dev) struct device_node *np = dev->of_node; struct cpm1_gpio16_chip *cpm1_gc; struct gpio_chip *gc; - u16 mask; + u32 mask; cpm1_gc = devm_kzalloc(dev, sizeof(*cpm1_gc), GFP_KERNEL); if (!cpm1_gc) @@ -485,7 +485,7 @@ int cpm1_gpiochip_add16(struct device *dev) spin_lock_init(&cpm1_gc->lock); - if (!of_property_read_u16(np, "fsl,cpm1-gpio-irq-mask", &mask)) { + if (!of_property_read_u32(np, "fsl,cpm1-gpio-irq-mask", &mask)) { int i, j; for (i = 0, j = 0; i < 16; i++) diff --git a/arch/powerpc/platforms/pasemi/pci.c b/arch/powerpc/platforms/pasemi/pci.c index 60f990a336c4..2df955274652 100644 --- a/arch/powerpc/platforms/pasemi/pci.c +++ b/arch/powerpc/platforms/pasemi/pci.c @@ -272,13 +272,12 @@ void __init pas_pci_init(void) { struct device_node *root = of_find_node_by_path("/"); struct device_node *np; - int res; pci_set_flags(PCI_SCAN_ALL_PCIE_DEVS); np = of_find_compatible_node(root, NULL, "pasemi,rootbus"); if (np) { - res = pas_add_bridge(np); + pas_add_bridge(np); of_node_put(np); } of_node_put(root); diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index 73b7f4e8c047..da72a30ab865 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -1058,40 +1058,6 @@ int pmac_i2c_match_adapter(struct device_node *dev, struct i2c_adapter *adapter) } EXPORT_SYMBOL_GPL(pmac_i2c_match_adapter); -int pmac_low_i2c_lock(struct device_node *np) -{ - struct pmac_i2c_bus *bus, *found = NULL; - - list_for_each_entry(bus, &pmac_i2c_busses, link) { - if (np == bus->controller) { - found = bus; - break; - } - } - if (!found) - return -ENODEV; - return pmac_i2c_open(bus, 0); -} -EXPORT_SYMBOL_GPL(pmac_low_i2c_lock); - -int pmac_low_i2c_unlock(struct device_node *np) -{ - struct pmac_i2c_bus *bus, *found = NULL; - - list_for_each_entry(bus, &pmac_i2c_busses, link) { - if (np == bus->controller) { - found = bus; - break; - } - } - if (!found) - return -ENODEV; - pmac_i2c_close(bus); - return 0; -} -EXPORT_SYMBOL_GPL(pmac_low_i2c_unlock); - - int pmac_i2c_open(struct pmac_i2c_bus *bus, int polled) { int rc; diff --git a/arch/powerpc/platforms/powernv/opal-core.c b/arch/powerpc/platforms/powernv/opal-core.c index e76e462f55f6..32662d30d70f 100644 --- a/arch/powerpc/platforms/powernv/opal-core.c +++ b/arch/powerpc/platforms/powernv/opal-core.c @@ -303,7 +303,6 @@ static int __init create_opalcore(void) struct device_node *dn; struct opalcore *new; loff_t opalcore_off; - struct page *page; Elf64_Phdr *phdr; Elf64_Ehdr *elf; int i, ret; @@ -328,11 +327,6 @@ static int __init create_opalcore(void) oc_conf->opalcorebuf_sz = 0; return -ENOMEM; } - count = oc_conf->opalcorebuf_sz / PAGE_SIZE; - page = virt_to_page(oc_conf->opalcorebuf); - for (i = 0; i < count; i++) - mark_page_reserved(page + i); - pr_debug("opalcorebuf = 0x%llx\n", (u64)oc_conf->opalcorebuf); /* Read OPAL related device-tree entries */ @@ -437,10 +431,7 @@ static void opalcore_cleanup(void) /* free the buffer used for setting up OPAL core */ if (oc_conf->opalcorebuf) { - void *end = (void *)((u64)oc_conf->opalcorebuf + - oc_conf->opalcorebuf_sz); - - free_reserved_area(oc_conf->opalcorebuf, end, -1, NULL); + free_pages_exact(oc_conf->opalcorebuf, oc_conf->opalcorebuf_sz); oc_conf->opalcorebuf = NULL; oc_conf->opalcorebuf_sz = 0; } diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c index 12c473768c39..9109c218a060 100644 --- a/arch/powerpc/platforms/ps3/device-init.c +++ b/arch/powerpc/platforms/ps3/device-init.c @@ -950,8 +950,6 @@ static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type) static int __init ps3_register_devices(void) { - int result; - if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) return -ENODEV; @@ -959,7 +957,7 @@ static int __init ps3_register_devices(void) /* ps3_repository_dump_bus_info(); */ - result = ps3_start_probe_thread(PS3_BUS_TYPE_STORAGE); + ps3_start_probe_thread(PS3_BUS_TYPE_STORAGE); ps3_register_vuart_devices(); diff --git a/arch/powerpc/platforms/pseries/htmdump.c b/arch/powerpc/platforms/pseries/htmdump.c index 742ec52c9d4d..489a80e87082 100644 --- a/arch/powerpc/platforms/pseries/htmdump.c +++ b/arch/powerpc/platforms/pseries/htmdump.c @@ -16,6 +16,7 @@ static void *htm_buf; static void *htm_status_buf; static void *htm_info_buf; static void *htm_caps_buf; +static void *htm_mem_buf; static u32 nodeindex; static u32 nodalchipindex; static u32 coreindexonchip; @@ -86,7 +87,7 @@ static ssize_t htm_return_check(long rc) static ssize_t htmdump_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) { - void *htm_buf = filp->private_data; + void *htm_buf_data = filp->private_data; unsigned long page, read_size, available; loff_t offset; long rc, ret; @@ -100,7 +101,7 @@ static ssize_t htmdump_read(struct file *filp, char __user *ubuf, * - last three values are address, size and offset */ rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, - htmtype, H_HTM_OP_DUMP_DATA, virt_to_phys(htm_buf), + htmtype, H_HTM_OP_DUMP_DATA, virt_to_phys(htm_buf_data), PAGE_SIZE, page); ret = htm_return_check(rc); @@ -112,7 +113,61 @@ static ssize_t htmdump_read(struct file *filp, char __user *ubuf, available = PAGE_SIZE; read_size = min(count, available); *ppos += read_size; - return simple_read_from_buffer(ubuf, count, &offset, htm_buf, available); + return simple_read_from_buffer(ubuf, count, &offset, htm_buf_data, available); +} + +static ssize_t htmsystem_mem_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *ppos) +{ + void *htm_mem_data = filp->private_data; + long rc, ret; + u64 *num_entries; + u64 to_copy = 0; + loff_t offset = 0; + u64 mem_offset = 0; + + /* + * Invoke H_HTM call with: + * - operation as htm status (H_HTM_OP_STATUS) + * - last three values as addr, size and offset. "offset" + * is value from output buffer header that points to next + * entry to dump. 0 is the first entry to dump. next entry + * is read from the output bufferbyte offset 0x8. + * + * When first time hcall is invoked, mem_offset should be + * zero because zero is the first entry. + * In the next hcall, offset of next entry to read from is + * picked from output buffer header itself. So don't fill + * mem_offset for first read. + * + * If there is no further data to read in next iteration, + * offset value from output buffer header will point to -1. + */ + if (*ppos) { + mem_offset = *(u64 *)(htm_mem_data + 0x8); + if (mem_offset == -1) + return 0; + } + rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, + htmtype, H_HTM_OP_DUMP_SYSMEM_CONF, virt_to_phys(htm_mem_data), + PAGE_SIZE, be64_to_cpu(mem_offset)); + ret = htm_return_check(rc); + if (ret <= 0) { + pr_debug("H_HTM hcall returned for op: H_HTM_OP_DUMP_SYSMEM_CONF with hcall returning %ld\n", ret); + return ret; + } + + /* + * HTM system mem buffer, start of buffer + 0x10 gives the + * number of HTM entries in the buffer. + * So total count to copy is: + * 32 bytes (for first 5 fields) + (number of HTM entries * entry size) + */ + num_entries = htm_mem_data + 0x10; + to_copy = 32 + (be64_to_cpu(*num_entries) * 32); + + *ppos += to_copy; + return simple_read_from_buffer(ubuf, count, &offset, htm_mem_data, to_copy); } static const struct file_operations htmdump_fops = { @@ -121,6 +176,12 @@ static const struct file_operations htmdump_fops = { .open = simple_open, }; +static const struct file_operations htmsystem_mem_fops = { + .llseek = NULL, + .read = htmsystem_mem_read, + .open = simple_open, +}; + static int htmconfigure_set(void *data, u64 val) { long rc, ret; @@ -226,20 +287,31 @@ static int htmstart_get(void *data, u64 *val) static ssize_t htmstatus_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) { - void *htm_status_buf = filp->private_data; + void *htm_status_data = filp->private_data; long rc, ret; u64 *num_entries; u64 to_copy; int htmstatus_flag; + loff_t offset = 0; + u64 status_offset = 0; /* * Invoke H_HTM call with: * - operation as htm status (H_HTM_OP_STATUS) - * - last three values as addr, size and offset + * - last three values as addr, size and offset. + * "offset" is value from output buffer header + * that points to next entry to dump. 0 is the first + * entry to dump. next entry is read from the output + * bufferbyte offset 0x8. */ + if (*ppos) { + status_offset = *(u64 *)(htm_status_data + 0x8); + if (status_offset == -1) + return 0; + } rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, - htmtype, H_HTM_OP_STATUS, virt_to_phys(htm_status_buf), - PAGE_SIZE, 0); + htmtype, H_HTM_OP_STATUS, virt_to_phys(htm_status_data), + PAGE_SIZE, be64_to_cpu(status_offset)); ret = htm_return_check(rc); if (ret <= 0) { @@ -255,13 +327,15 @@ static ssize_t htmstatus_read(struct file *filp, char __user *ubuf, * So total count to copy is: * 32 bytes (for first 7 fields) + (number of HTM entries * entry size) */ - num_entries = htm_status_buf + 0x10; + num_entries = htm_status_data + 0x10; if (htmtype == 0x2) htmstatus_flag = 0x8; else htmstatus_flag = 0x6; to_copy = 32 + (be64_to_cpu(*num_entries) * htmstatus_flag); - return simple_read_from_buffer(ubuf, count, ppos, htm_status_buf, to_copy); + *ppos += to_copy; + + return simple_read_from_buffer(ubuf, count, &offset, htm_status_data, to_copy); } static const struct file_operations htmstatus_fops = { @@ -273,19 +347,30 @@ static const struct file_operations htmstatus_fops = { static ssize_t htminfo_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) { - void *htm_info_buf = filp->private_data; + void *htm_info_data = filp->private_data; long rc, ret; u64 *num_entries; u64 to_copy; + loff_t offset = 0; + u64 info_offset = 0; /* * Invoke H_HTM call with: * - operation as htm status (H_HTM_OP_STATUS) * - last three values as addr, size and offset + * "offset" is value from output buffer header + * that points to next entry to dump. 0 is the first + * entry to dump. next entry is read from the output + * bufferbyte offset 0x8. */ + if (*ppos) { + info_offset = *(u64 *)(htm_info_data + 0x8); + if (info_offset == -1) + return 0; + } rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, - htmtype, H_HTM_OP_DUMP_SYSPROC_CONF, virt_to_phys(htm_info_buf), - PAGE_SIZE, 0); + htmtype, H_HTM_OP_DUMP_SYSPROC_CONF, virt_to_phys(htm_info_data), + PAGE_SIZE, be64_to_cpu(info_offset)); ret = htm_return_check(rc); if (ret <= 0) { @@ -301,15 +386,17 @@ static ssize_t htminfo_read(struct file *filp, char __user *ubuf, * So total count to copy is: * 32 bytes (for first 5 fields) + (number of HTM entries * entry size) */ - num_entries = htm_info_buf + 0x10; + num_entries = htm_info_data + 0x10; to_copy = 32 + (be64_to_cpu(*num_entries) * 16); - return simple_read_from_buffer(ubuf, count, ppos, htm_info_buf, to_copy); + + *ppos += to_copy; + return simple_read_from_buffer(ubuf, count, &offset, htm_info_data, to_copy); } static ssize_t htmcaps_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) { - void *htm_caps_buf = filp->private_data; + void *htm_caps_data = filp->private_data; long rc, ret; /* @@ -319,7 +406,7 @@ static ssize_t htmcaps_read(struct file *filp, char __user *ubuf, * and zero */ rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, - htmtype, H_HTM_OP_CAPABILITIES, virt_to_phys(htm_caps_buf), + htmtype, H_HTM_OP_CAPABILITIES, virt_to_phys(htm_caps_data), 0x80, 0); ret = htm_return_check(rc); @@ -328,7 +415,7 @@ static ssize_t htmcaps_read(struct file *filp, char __user *ubuf, return ret; } - return simple_read_from_buffer(ubuf, count, ppos, htm_caps_buf, 0x80); + return simple_read_from_buffer(ubuf, count, ppos, htm_caps_data, 0x80); } static const struct file_operations htminfo_fops = { @@ -457,9 +544,17 @@ static int htmdump_init_debugfs(void) return -ENOMEM; } + /* Memory to present HTM system memory configuration */ + htm_mem_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!htm_mem_buf) { + pr_err("Failed to allocate htm mem buf\n"); + return -ENOMEM; + } + debugfs_create_file("htmstatus", 0400, htmdump_debugfs_dir, htm_status_buf, &htmstatus_fops); debugfs_create_file("htminfo", 0400, htmdump_debugfs_dir, htm_info_buf, &htminfo_fops); debugfs_create_file("htmcaps", 0400, htmdump_debugfs_dir, htm_caps_buf, &htmcaps_fops); + debugfs_create_file("htmsystem_mem", 0400, htmdump_debugfs_dir, htm_mem_buf, &htmsystem_mem_fops); return 0; } @@ -482,6 +577,10 @@ static void __exit htmdump_exit(void) { debugfs_remove_recursive(htmdump_debugfs_dir); kfree(htm_buf); + kfree(htm_status_buf); + kfree(htm_info_buf); + kfree(htm_caps_buf); + kfree(htm_mem_buf); } module_init(htmdump_init); diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c index 14ae480d060a..0c40bdde45e2 100644 --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c @@ -190,33 +190,34 @@ static int hvpipe_rtas_recv_msg(char __user *buf, int size) return -ENOMEM; } - ret = rtas_ibm_receive_hvpipe_msg(work_area, &srcID, - &bytes_written); - if (!ret) { - /* - * Recv HVPIPE RTAS is successful. - * When releasing FD or no one is waiting on the - * specific source, issue recv HVPIPE RTAS call - * so that pipe is not blocked - this func is called - * with NULL buf. - */ - if (buf) { - if (size < bytes_written) { - pr_err("Received the payload size = %d, but the buffer size = %d\n", - bytes_written, size); - bytes_written = size; - } - ret = copy_to_user(buf, - rtas_work_area_raw_buf(work_area), - bytes_written); - if (!ret) - ret = bytes_written; - } - } else { - pr_err("ibm,receive-hvpipe-msg failed with %d\n", - ret); + /* + * Recv HVPIPE RTAS is successful. + * When releasing FD or no one is waiting on the + * specific source, issue recv HVPIPE RTAS call + * so that pipe is not blocked - this func is called + * with NULL buf. + */ + ret = rtas_ibm_receive_hvpipe_msg(work_area, &srcID, &bytes_written); + if (ret) { + pr_err("ibm,receive-hvpipe-msg failed with %d\n", ret); + goto out; } + if (!buf) + goto out; + + if (size < bytes_written) { + pr_err("Received the payload size = %d, but the buffer size = %d\n", + bytes_written, size); + bytes_written = size; + } + + if (copy_to_user(buf, rtas_work_area_raw_buf(work_area), bytes_written)) + ret = -EFAULT; + else + ret = bytes_written; + +out: rtas_work_area_free(work_area); return ret; } @@ -327,8 +328,8 @@ static ssize_t papr_hvpipe_handle_read(struct file *file, { struct hvpipe_source_info *src_info = file->private_data; - struct papr_hvpipe_hdr hdr; - long ret; + struct papr_hvpipe_hdr hdr = {}; + ssize_t ret = 0; /* * Return -ENXIO during migration @@ -376,7 +377,7 @@ static ssize_t papr_hvpipe_handle_read(struct file *file, ret = copy_to_user(buf, &hdr, HVPIPE_HDR_LEN); if (ret) - return ret; + return -EFAULT; /* * Message event has payload, so get the payload with @@ -385,19 +386,23 @@ static ssize_t papr_hvpipe_handle_read(struct file *file, if (hdr.flags & HVPIPE_MSG_AVAILABLE) { ret = hvpipe_rtas_recv_msg(buf + HVPIPE_HDR_LEN, size - HVPIPE_HDR_LEN); - if (ret > 0) { + /* + * Always clear MSG_AVAILABLE once the RTAS call has drained + * the message, regardless of whether copy_to_user succeeded. + */ + if (ret >= 0 || ret == -EFAULT) src_info->hvpipe_status &= ~HVPIPE_MSG_AVAILABLE; - ret += HVPIPE_HDR_LEN; - } } else if (hdr.flags & HVPIPE_LOST_CONNECTION) { /* * Hypervisor is closing the pipe for the specific * source. So notify user space. */ src_info->hvpipe_status &= ~HVPIPE_LOST_CONNECTION; - ret = HVPIPE_HDR_LEN; } + if (ret >= 0) + ret += HVPIPE_HDR_LEN; + return ret; } @@ -444,16 +449,18 @@ static int papr_hvpipe_handle_release(struct inode *inode, struct file *file) { struct hvpipe_source_info *src_info; + unsigned long flags; /* * Hold the lock, remove source from src_list, reset the * hvpipe status and release the lock to prevent any race * with message event IRQ. */ - spin_lock(&hvpipe_src_list_lock); + spin_lock_irqsave(&hvpipe_src_list_lock, flags); src_info = file->private_data; list_del(&src_info->list); file->private_data = NULL; + spin_unlock_irqrestore(&hvpipe_src_list_lock, flags); /* * If the pipe for this specific source has any pending * payload, issue recv HVPIPE RTAS so that pipe will not @@ -461,10 +468,8 @@ static int papr_hvpipe_handle_release(struct inode *inode, */ if (src_info->hvpipe_status & HVPIPE_MSG_AVAILABLE) { src_info->hvpipe_status = 0; - spin_unlock(&hvpipe_src_list_lock); hvpipe_rtas_recv_msg(NULL, 0); - } else - spin_unlock(&hvpipe_src_list_lock); + } kfree(src_info); return 0; @@ -479,50 +484,53 @@ static const struct file_operations papr_hvpipe_handle_ops = { static int papr_hvpipe_dev_create_handle(u32 srcID) { - struct hvpipe_source_info *src_info __free(kfree) = NULL; - - spin_lock(&hvpipe_src_list_lock); - /* - * Do not allow more than one process communicates with - * each source. - */ - src_info = hvpipe_find_source(srcID); - if (src_info) { - spin_unlock(&hvpipe_src_list_lock); - pr_err("pid(%d) is already using the source(%d)\n", - src_info->tsk->pid, srcID); - return -EALREADY; - } - spin_unlock(&hvpipe_src_list_lock); + struct hvpipe_source_info *src_info; + int fd; + unsigned long flags; src_info = kzalloc_obj(*src_info, GFP_KERNEL_ACCOUNT); if (!src_info) return -ENOMEM; src_info->srcID = srcID; - src_info->tsk = current; init_waitqueue_head(&src_info->recv_wqh); - FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC, - anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops, - (void *)src_info, O_RDWR)); - if (fdf.err) - return fdf.err; - - retain_and_null_ptr(src_info); - spin_lock(&hvpipe_src_list_lock); /* - * If two processes are executing ioctl() for the same - * source ID concurrently, prevent the second process to - * acquire FD. + * Do not allow more than one process communicates with + * each source. */ + spin_lock_irqsave(&hvpipe_src_list_lock, flags); if (hvpipe_find_source(srcID)) { - spin_unlock(&hvpipe_src_list_lock); + spin_unlock_irqrestore(&hvpipe_src_list_lock, flags); + pr_err("pid(%s:%d) could not get the source(%d)\n", + current->comm, task_pid_nr(current), srcID); + kfree(src_info); return -EALREADY; } list_add(&src_info->list, &hvpipe_src_list); - spin_unlock(&hvpipe_src_list_lock); - return fd_publish(fdf); + spin_unlock_irqrestore(&hvpipe_src_list_lock, flags); + + fd = FD_ADD(O_RDONLY | O_CLOEXEC, + anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops, + (void *)src_info, O_RDWR)); + if (fd < 0) { + spin_lock_irqsave(&hvpipe_src_list_lock, flags); + list_del(&src_info->list); + spin_unlock_irqrestore(&hvpipe_src_list_lock, flags); + /* + * if we fail to add FD, that means no userspace program is + * polling. In that case if there is a msg pending because the + * interrupt was fired after the src_info was added to the + * global list, then let's consume it here, to unblock the + * hvpipe + */ + if (src_info->hvpipe_status & HVPIPE_MSG_AVAILABLE) + hvpipe_rtas_recv_msg(NULL, 0); + kfree(src_info); + return fd; + } + + return fd; } /* @@ -685,20 +693,19 @@ static int __init enable_hvpipe_IRQ(void) struct device_node *np; hvpipe_check_exception_token = rtas_function_token(RTAS_FN_CHECK_EXCEPTION); - if (hvpipe_check_exception_token == RTAS_UNKNOWN_SERVICE) + if (hvpipe_check_exception_token == RTAS_UNKNOWN_SERVICE) return -ENODEV; /* hvpipe events */ np = of_find_node_by_path("/event-sources/ibm,hvpipe-msg-events"); - if (np != NULL) { - request_event_sources_irqs(np, hvpipe_event_interrupt, - "HPIPE_EVENT"); - of_node_put(np); - } else { - pr_err("Can not enable hvpipe event IRQ\n"); + if (!np) { + pr_err("No device node found, could not enable hvpipe event IRQ\n"); return -ENODEV; } + request_event_sources_irqs(np, hvpipe_event_interrupt, "HPIPE_EVENT"); + of_node_put(np); + return 0; } @@ -775,23 +782,29 @@ static int __init papr_hvpipe_init(void) } ret = enable_hvpipe_IRQ(); - if (!ret) { - ret = set_hvpipe_sys_param(1); - if (!ret) - ret = misc_register(&papr_hvpipe_dev); - } + if (ret) + goto out_wq; - if (!ret) { - pr_info("hvpipe feature is enabled\n"); - hvpipe_feature = true; - return 0; - } + ret = misc_register(&papr_hvpipe_dev); + if (ret) + goto out_wq; - pr_err("hvpipe feature is not enabled %d\n", ret); + ret = set_hvpipe_sys_param(1); + if (ret) + goto out_misc; + + pr_info("hvpipe feature is enabled\n"); + hvpipe_feature = true; + return 0; + +out_misc: + misc_deregister(&papr_hvpipe_dev); +out_wq: destroy_workqueue(papr_hvpipe_wq); out: kfree(papr_hvpipe_work); papr_hvpipe_work = NULL; + pr_err("hvpipe feature is not enabled %d\n", ret); return ret; } machine_device_initcall(pseries, papr_hvpipe_init); diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.h b/arch/powerpc/platforms/pseries/papr-hvpipe.h index c343f4230865..4bdf7bb2fc4d 100644 --- a/arch/powerpc/platforms/pseries/papr-hvpipe.h +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.h @@ -21,7 +21,6 @@ struct hvpipe_source_info { u32 srcID; u32 hvpipe_status; wait_queue_head_t recv_wqh; /* wake up poll() waitq */ - struct task_struct *tsk; }; /* diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 53da3457a539..c5754942cf85 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -47,8 +47,8 @@ config RISCV select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SET_DIRECT_MAP if MMU select ARCH_HAS_SET_MEMORY if MMU - select ARCH_HAS_STRICT_KERNEL_RWX if MMU && !XIP_KERNEL - select ARCH_HAS_STRICT_MODULE_RWX if MMU && !XIP_KERNEL + select ARCH_HAS_STRICT_KERNEL_RWX if MMU + select ARCH_HAS_STRICT_MODULE_RWX if MMU select ARCH_HAS_SYNC_CORE_BEFORE_USERMODE select ARCH_HAS_SYSCALL_WRAPPER select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST @@ -84,7 +84,7 @@ config RISCV select ARCH_WANT_FRAME_POINTERS select ARCH_WANT_GENERAL_HUGETLB if !RISCV_ISA_SVNAPOT select ARCH_WANT_HUGE_PMD_SHARE if 64BIT - select ARCH_WANT_LD_ORPHAN_WARN if !XIP_KERNEL + select ARCH_WANT_LD_ORPHAN_WARN select ARCH_WANT_OPTIMIZE_DAX_VMEMMAP select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP select ARCH_WANTS_NO_INSTR @@ -112,6 +112,7 @@ config RISCV select GENERIC_GETTIMEOFDAY if HAVE_GENERIC_VDSO && 64BIT select GENERIC_IDLE_POLL_SETUP select GENERIC_IOREMAP if MMU + select HAVE_IOREMAP_PROT if MMU select GENERIC_IRQ_IPI if SMP select GENERIC_IRQ_IPI_MUX if SMP select GENERIC_IRQ_MULTI_HANDLER @@ -129,13 +130,13 @@ config RISCV select HAVE_ARCH_AUDITSYSCALL select HAVE_ARCH_HUGE_VMALLOC if HAVE_ARCH_HUGE_VMAP select HAVE_ARCH_HUGE_VMAP if MMU && 64BIT - select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL - select HAVE_ARCH_JUMP_LABEL_RELATIVE if !XIP_KERNEL + select HAVE_ARCH_JUMP_LABEL + select HAVE_ARCH_JUMP_LABEL_RELATIVE select HAVE_ARCH_KASAN if MMU && 64BIT select HAVE_ARCH_KASAN_VMALLOC if MMU && 64BIT select HAVE_ARCH_KFENCE if MMU && 64BIT select HAVE_ARCH_KSTACK_ERASE - select HAVE_ARCH_KGDB if !XIP_KERNEL + select HAVE_ARCH_KGDB select HAVE_ARCH_KGDB_QXFER_PKT select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT @@ -153,7 +154,7 @@ config RISCV select HAVE_CONTEXT_TRACKING_USER select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_CONTIGUOUS if MMU - select HAVE_DYNAMIC_FTRACE if !XIP_KERNEL && MMU && (CLANG_SUPPORTS_DYNAMIC_FTRACE || GCC_SUPPORTS_DYNAMIC_FTRACE) + select HAVE_DYNAMIC_FTRACE if MMU && (CLANG_SUPPORTS_DYNAMIC_FTRACE || GCC_SUPPORTS_DYNAMIC_FTRACE) select FUNCTION_ALIGNMENT_4B if HAVE_DYNAMIC_FTRACE && RISCV_ISA_C select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS if HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS select HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS if (DYNAMIC_FTRACE_WITH_ARGS && !CFI) @@ -161,7 +162,7 @@ config RISCV select HAVE_FTRACE_GRAPH_FUNC select HAVE_FUNCTION_GRAPH_TRACER if HAVE_DYNAMIC_FTRACE_WITH_ARGS select HAVE_FUNCTION_GRAPH_FREGS - select HAVE_FUNCTION_TRACER if !XIP_KERNEL && HAVE_DYNAMIC_FTRACE + select HAVE_FUNCTION_TRACER if HAVE_DYNAMIC_FTRACE select HAVE_EBPF_JIT if MMU select HAVE_GENERIC_TIF_BITS select HAVE_GUP_FAST if MMU @@ -170,16 +171,16 @@ config RISCV select HAVE_GCC_PLUGINS select HAVE_GENERIC_VDSO if MMU select HAVE_IRQ_TIME_ACCOUNTING - select HAVE_KERNEL_BZIP2 if !XIP_KERNEL && !EFI_ZBOOT - select HAVE_KERNEL_GZIP if !XIP_KERNEL && !EFI_ZBOOT - select HAVE_KERNEL_LZ4 if !XIP_KERNEL && !EFI_ZBOOT - select HAVE_KERNEL_LZMA if !XIP_KERNEL && !EFI_ZBOOT - select HAVE_KERNEL_LZO if !XIP_KERNEL && !EFI_ZBOOT - select HAVE_KERNEL_UNCOMPRESSED if !XIP_KERNEL && !EFI_ZBOOT - select HAVE_KERNEL_ZSTD if !XIP_KERNEL && !EFI_ZBOOT - select HAVE_KERNEL_XZ if !XIP_KERNEL && !EFI_ZBOOT - select HAVE_KPROBES if !XIP_KERNEL - select HAVE_KRETPROBES if !XIP_KERNEL + select HAVE_KERNEL_BZIP2 if !EFI_ZBOOT + select HAVE_KERNEL_GZIP if !EFI_ZBOOT + select HAVE_KERNEL_LZ4 if !EFI_ZBOOT + select HAVE_KERNEL_LZMA if !EFI_ZBOOT + select HAVE_KERNEL_LZO if !EFI_ZBOOT + select HAVE_KERNEL_UNCOMPRESSED if !EFI_ZBOOT + select HAVE_KERNEL_ZSTD if !EFI_ZBOOT + select HAVE_KERNEL_XZ if !EFI_ZBOOT + select HAVE_KPROBES + select HAVE_KRETPROBES # https://github.com/ClangBuiltLinux/linux/issues/1881 select HAVE_LD_DEAD_CODE_DATA_ELIMINATION if !LD_IS_LLD select HAVE_MOVE_PMD @@ -190,9 +191,9 @@ config RISCV select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP select HAVE_POSIX_CPU_TIMERS_TASK_WORK - select HAVE_PREEMPT_DYNAMIC_KEY if !XIP_KERNEL + select HAVE_PREEMPT_DYNAMIC_KEY select HAVE_REGS_AND_STACK_ACCESS_API - select HAVE_RETHOOK if !XIP_KERNEL + select HAVE_RETHOOK select HAVE_RSEQ select HAVE_RUST if RUSTC_SUPPORTS_RISCV && CC_IS_CLANG select HAVE_SAMPLE_FTRACE_DIRECT @@ -213,7 +214,7 @@ config RISCV select PCI_ECAM if (ACPI && PCI) select PCI_MSI if PCI select RELOCATABLE if !MMU && !PHYS_RAM_BASE_FIXED - select RISCV_ALTERNATIVE if !XIP_KERNEL + select RISCV_ALTERNATIVE select RISCV_APLIC select RISCV_IMSIC select RISCV_INTC @@ -537,7 +538,6 @@ endchoice config RISCV_ALTERNATIVE bool - depends on !XIP_KERNEL help This Kconfig allows the kernel to automatically patch the erratum or cpufeature required by the execution platform at run @@ -937,6 +937,28 @@ config RISCV_VECTOR_MISALIGNED help Enable detecting support for vector misaligned loads and stores. +config RISCV_SBI_FWFT_DELEGATE_MISALIGNED + bool "Request firmware delegation of unaligned access exceptions" + depends on RISCV_SBI + depends on NONPORTABLE + help + Use SBI FWFT to request delegation of load address misaligned and + store address misaligned exceptions, if possible, and prefer Linux + kernel emulation of these accesses to firmware emulation. + + Unfortunately, Linux's emulation is still incomplete. Namely, it + currently does not handle vector instructions and KVM guest accesses. + On platforms where these accesses would have been handled by firmware, + enabling this causes unexpected kernel oopses, userspaces crashes and + KVM guest crashes. If you are sure that these are not a problem for + your platform, you can say Y here, which may improve performance. + + Saying N here will not worsen emulation support for unaligned accesses + even in the case where the firmware also has incomplete support. It + simply keeps the firmware's emulation enabled. + + If you don't know what to do here, say N. + choice prompt "Unaligned Accesses Support" default RISCV_PROBE_UNALIGNED_ACCESS @@ -1126,7 +1148,6 @@ config PARAVIRT_TIME_ACCOUNTING config RELOCATABLE bool "Build a relocatable kernel" - depends on !XIP_KERNEL select MODULE_SECTIONS if MODULES select ARCH_VMLINUX_NEEDS_RELOCS help @@ -1143,7 +1164,7 @@ config RELOCATABLE config RANDOMIZE_BASE bool "Randomize the address of the kernel image" select RELOCATABLE - depends on MMU && 64BIT && !XIP_KERNEL + depends on MMU && 64BIT help Randomizes the virtual address at which the kernel image is loaded, as a security feature that deters exploit attempts @@ -1233,7 +1254,7 @@ config EFI_STUB config EFI bool "UEFI runtime support" - depends on OF && !XIP_KERNEL + depends on OF depends on MMU default y select ARCH_SUPPORTS_ACPI if 64BIT @@ -1284,44 +1305,6 @@ config PHYS_RAM_BASE explicitly specified to run early relocations of read-write data from flash to RAM. -config XIP_KERNEL - bool "Kernel Execute-In-Place from ROM" - depends on MMU && SPARSEMEM && NONPORTABLE - # This prevents XIP from being enabled by all{yes,mod}config, which - # fail to build since XIP doesn't support large kernels. - depends on !COMPILE_TEST - select PHYS_RAM_BASE_FIXED - help - Execute-In-Place allows the kernel to run from non-volatile storage - directly addressable by the CPU, such as NOR flash. This saves RAM - space since the text section of the kernel is not loaded from flash - to RAM. Read-write sections, such as the data section and stack, - are still copied to RAM. The XIP kernel is not compressed since - it has to run directly from flash, so it will take more space to - store it. The flash address used to link the kernel object files, - and for storing it, is configuration dependent. Therefore, if you - say Y here, you must know the proper physical address where to - store the kernel image depending on your own flash memory usage. - - Also note that the make target becomes "make xipImage" rather than - "make zImage" or "make Image". The final kernel binary to put in - ROM memory will be arch/riscv/boot/xipImage. - - SPARSEMEM is required because the kernel text and rodata that are - flash resident are not backed by memmap, then any attempt to get - a struct page on those regions will trigger a fault. - - If unsure, say N. - -config XIP_PHYS_ADDR - hex "XIP Kernel Physical Location" - depends on XIP_KERNEL - default "0x21000000" - help - This is the physical address in your flash memory the kernel will - be linked for and stored to. This address is dependent on your - own flash usage. - config RISCV_ISA_FALLBACK bool "Permit falling back to parsing riscv,isa for extension support by default" default y diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index d621b85dd63b..c174ac0ec46b 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -2,7 +2,7 @@ menu "SoC selection" config ARCH_ANDES bool "Andes SoCs" - depends on MMU && !XIP_KERNEL + depends on MMU select ERRATA_ANDES help This enables support for Andes SoC platform hardware. @@ -33,7 +33,7 @@ config ARCH_RENESAS config ARCH_SIFIVE bool "SiFive SoCs" - select ERRATA_SIFIVE if !XIP_KERNEL + select ERRATA_SIFIVE help This enables support for SiFive SoC platform hardware. @@ -61,7 +61,7 @@ config SOC_STARFIVE config ARCH_SUNXI bool "Allwinner sun20i SoCs" - depends on MMU && !XIP_KERNEL + depends on MMU select ERRATA_THEAD select SUN4I_TIMER help @@ -78,7 +78,7 @@ config ARCH_TENSTORRENT config ARCH_THEAD bool "T-HEAD RISC-V SoCs" - depends on MMU && !XIP_KERNEL + depends on MMU select ERRATA_THEAD select PM_GENERIC_DOMAINS if PM help diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 371da75a47f9..ce0cc737f870 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -28,7 +28,6 @@ endif export BITS ifeq ($(CONFIG_ARCH_RV64I),y) BITS := 64 - UTS_MACHINE := riscv64 KBUILD_CFLAGS += -mabi=lp64 KBUILD_AFLAGS += -mabi=lp64 @@ -39,13 +38,14 @@ ifeq ($(CONFIG_ARCH_RV64I),y) -Cno-redzone else BITS := 32 - UTS_MACHINE := riscv32 KBUILD_CFLAGS += -mabi=ilp32 KBUILD_AFLAGS += -mabi=ilp32 KBUILD_LDFLAGS += -melf32lriscv endif +UTS_MACHINE := riscv$(BITS) + # LLVM has an issue with target-features and LTO: https://github.com/llvm/llvm-project/issues/59350 # Ensure it is aware of linker relaxation with LTO, otherwise relocations may # be incorrect: https://github.com/llvm/llvm-project/issues/65090 @@ -150,7 +150,6 @@ ifdef CONFIG_RISCV_M_MODE boot-image-$(CONFIG_SOC_CANAAN_K210) := loader.bin endif boot-image-$(CONFIG_EFI_ZBOOT) := vmlinuz.efi -boot-image-$(CONFIG_XIP_KERNEL) := xipImage KBUILD_IMAGE := $(boot)/$(boot-image-y) libs-y += arch/riscv/lib/ @@ -218,8 +217,6 @@ define archhelp echo ' Image.xz - Compressed kernel image (arch/riscv/boot/Image.xz)' echo ' vmlinuz.efi - Compressed EFI kernel image (arch/riscv/boot/vmlinuz.efi)' echo ' Default when CONFIG_EFI_ZBOOT=y' - echo ' xipImage - Execute-in-place kernel image (arch/riscv/boot/xipImage)' - echo ' Default when CONFIG_XIP_KERNEL=y' echo ' install - Install kernel using (your) ~/bin/$(INSTALLKERNEL) or' echo ' (distribution) /sbin/$(INSTALLKERNEL) or install to ' echo ' $$(INSTALL_PATH)' diff --git a/arch/riscv/boot/Makefile b/arch/riscv/boot/Makefile index 5301adf5f3f5..fcfbe3f814d6 100644 --- a/arch/riscv/boot/Makefile +++ b/arch/riscv/boot/Makefile @@ -20,17 +20,6 @@ OBJCOPYFLAGS_xipImage :=-O binary -R .note -R .note.gnu.build-id -R .comment -S targets := Image Image.* loader loader.o loader.lds loader.bin xipImage -ifeq ($(CONFIG_XIP_KERNEL),y) - -quiet_cmd_mkxip = $(quiet_cmd_objcopy) -cmd_mkxip = $(cmd_objcopy) - -$(obj)/xipImage: vmlinux FORCE - $(call if_changed,mkxip) - @$(kecho) ' Physical Address of xipImage: $(CONFIG_XIP_PHYS_ADDR)' - -endif - $(obj)/Image: vmlinux FORCE $(call if_changed,objcopy) diff --git a/arch/riscv/boot/dts/microchip/Makefile b/arch/riscv/boot/dts/microchip/Makefile index 345ed7a48cc1..43f91cbf338b 100644 --- a/arch/riscv/boot/dts/microchip/Makefile +++ b/arch/riscv/boot/dts/microchip/Makefile @@ -1,9 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 -dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-beaglev-fire.dtb -dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-disco-kit.dtb -dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-icicle-kit.dtb -dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-icicle-kit-prod.dtb -dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-m100pfsevp.dtb -dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-polarberry.dtb -dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-sev-kit.dtb -dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-tysom-m.dtb +dtb-$(CONFIG_ARCH_MICROCHIP) += mpfs-beaglev-fire.dtb +dtb-$(CONFIG_ARCH_MICROCHIP) += mpfs-disco-kit.dtb +dtb-$(CONFIG_ARCH_MICROCHIP) += mpfs-icicle-kit.dtb +dtb-$(CONFIG_ARCH_MICROCHIP) += mpfs-icicle-kit-prod.dtb +dtb-$(CONFIG_ARCH_MICROCHIP) += mpfs-m100pfsevp.dtb +dtb-$(CONFIG_ARCH_MICROCHIP) += mpfs-polarberry.dtb +dtb-$(CONFIG_ARCH_MICROCHIP) += mpfs-sev-kit.dtb +dtb-$(CONFIG_ARCH_MICROCHIP) += mpfs-tysom-m.dtb +dtb-$(CONFIG_ARCH_MICROCHIP) += pic64gx-curiosity-kit.dtb diff --git a/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts b/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts index f44ad8e6f4e4..0e1b0b8d394b 100644 --- a/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts +++ b/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts @@ -164,6 +164,35 @@ }; }; +&irqmux { + interrupt-map = <0 &plic 13>, <1 &plic 14>, <2 &plic 15>, + <3 &plic 16>, <4 &plic 17>, <5 &plic 18>, + <6 &plic 19>, <7 &plic 20>, <8 &plic 21>, + <9 &plic 22>, <10 &plic 23>, <11 &plic 24>, + <12 &plic 25>, <13 &plic 26>, + + <32 &plic 27>, <33 &plic 28>, <34 &plic 29>, + <35 &plic 30>, <36 &plic 31>, <37 &plic 32>, + <38 &plic 33>, <39 &plic 34>, <40 &plic 35>, + <41 &plic 36>, <42 &plic 37>, <43 &plic 38>, + <44 &plic 39>, <45 &plic 40>, <46 &plic 41>, + <47 &plic 42>, <48 &plic 43>, <49 &plic 44>, + <50 &plic 45>, <51 &plic 46>, <52 &plic 47>, + <53 &plic 48>, <54 &plic 49>, <55 &plic 50>, + + <64 &plic 53>, <65 &plic 53>, <66 &plic 53>, + <67 &plic 53>, <68 &plic 53>, <69 &plic 53>, + <70 &plic 53>, <71 &plic 53>, <72 &plic 53>, + <73 &plic 53>, <74 &plic 53>, <75 &plic 53>, + <76 &plic 53>, <77 &plic 53>, <78 &plic 53>, + <79 &plic 53>, <80 &plic 53>, <81 &plic 53>, + <82 &plic 53>, <83 &plic 53>, <84 &plic 53>, + <85 &plic 53>, <86 &plic 53>, <87 &plic 53>, + <88 &plic 53>, <89 &plic 53>, <90 &plic 53>, + <91 &plic 53>, <92 &plic 53>, <93 &plic 53>, + <94 &plic 53>, <95 &plic 53>; +}; + &mac0 { status = "okay"; phy-mode = "sgmii"; diff --git a/arch/riscv/boot/dts/microchip/mpfs-disco-kit.dts b/arch/riscv/boot/dts/microchip/mpfs-disco-kit.dts index c068b9bb5bfd..f769c9d5d7b4 100644 --- a/arch/riscv/boot/dts/microchip/mpfs-disco-kit.dts +++ b/arch/riscv/boot/dts/microchip/mpfs-disco-kit.dts @@ -97,24 +97,10 @@ }; &gpio1 { - interrupts = <27>, <28>, <29>, <30>, - <31>, <32>, <33>, <47>, - <35>, <36>, <37>, <38>, - <39>, <40>, <41>, <42>, - <43>, <44>, <45>, <46>, - <47>, <48>, <49>, <50>; status = "okay"; }; &gpio2 { - interrupts = <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>; status = "okay"; }; @@ -130,6 +116,35 @@ status = "okay"; }; +&irqmux { + interrupt-map = <0 &plic 13>, <1 &plic 14>, <2 &plic 15>, + <3 &plic 16>, <4 &plic 17>, <5 &plic 18>, + <6 &plic 19>, <7 &plic 20>, <8 &plic 21>, + <9 &plic 22>, <10 &plic 23>, <11 &plic 24>, + <12 &plic 25>, <13 &plic 26>, + + <32 &plic 27>, <33 &plic 28>, <34 &plic 29>, + <35 &plic 30>, <36 &plic 31>, <37 &plic 32>, + <38 &plic 33>, <39 &plic 34>, <40 &plic 35>, + <41 &plic 36>, <42 &plic 37>, <43 &plic 38>, + <44 &plic 39>, <45 &plic 40>, <46 &plic 41>, + <47 &plic 42>, <48 &plic 43>, <49 &plic 44>, + <50 &plic 45>, <51 &plic 46>, <52 &plic 47>, + <53 &plic 48>, <54 &plic 49>, <55 &plic 50>, + + <64 &plic 53>, <65 &plic 53>, <66 &plic 53>, + <67 &plic 53>, <68 &plic 53>, <69 &plic 53>, + <70 &plic 53>, <71 &plic 53>, <72 &plic 53>, + <73 &plic 53>, <74 &plic 53>, <75 &plic 53>, + <76 &plic 53>, <77 &plic 53>, <78 &plic 53>, + <79 &plic 53>, <80 &plic 53>, <81 &plic 53>, + <82 &plic 53>, <83 &plic 53>, <84 &plic 53>, + <85 &plic 53>, <86 &plic 53>, <87 &plic 53>, + <88 &plic 53>, <89 &plic 53>, <90 &plic 53>, + <91 &plic 53>, <92 &plic 53>, <93 &plic 53>, + <94 &plic 53>, <95 &plic 53>; +}; + &mac0 { phy-mode = "sgmii"; phy-handle = <&phy0>; diff --git a/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-common.dtsi b/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-common.dtsi index e01a216e6c3a..7816408343a3 100644 --- a/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-common.dtsi +++ b/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-common.dtsi @@ -3,7 +3,6 @@ /dts-v1/; -#include "mpfs.dtsi" #include "mpfs-icicle-kit-fabric.dtsi" #include #include @@ -77,14 +76,6 @@ }; &gpio2 { - interrupts = <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>; status = "okay"; }; @@ -136,6 +127,35 @@ status = "okay"; }; +&irqmux { + interrupt-map = <0 &plic 13>, <1 &plic 14>, <2 &plic 15>, + <3 &plic 16>, <4 &plic 17>, <5 &plic 18>, + <6 &plic 19>, <7 &plic 20>, <8 &plic 21>, + <9 &plic 22>, <10 &plic 23>, <11 &plic 24>, + <12 &plic 25>, <13 &plic 26>, + + <32 &plic 27>, <33 &plic 28>, <34 &plic 29>, + <35 &plic 30>, <36 &plic 31>, <37 &plic 32>, + <38 &plic 33>, <39 &plic 34>, <40 &plic 35>, + <41 &plic 36>, <42 &plic 37>, <43 &plic 38>, + <44 &plic 39>, <45 &plic 40>, <46 &plic 41>, + <47 &plic 42>, <48 &plic 43>, <49 &plic 44>, + <50 &plic 45>, <51 &plic 46>, <52 &plic 47>, + <53 &plic 48>, <54 &plic 49>, <55 &plic 50>, + + <64 &plic 53>, <65 &plic 53>, <66 &plic 53>, + <67 &plic 53>, <68 &plic 53>, <69 &plic 53>, + <70 &plic 53>, <71 &plic 53>, <72 &plic 53>, + <73 &plic 53>, <74 &plic 53>, <75 &plic 53>, + <76 &plic 53>, <77 &plic 53>, <78 &plic 53>, + <79 &plic 53>, <80 &plic 53>, <81 &plic 53>, + <82 &plic 53>, <83 &plic 53>, <84 &plic 53>, + <85 &plic 53>, <86 &plic 53>, <87 &plic 53>, + <88 &plic 53>, <89 &plic 53>, <90 &plic 53>, + <91 &plic 53>, <92 &plic 53>, <93 &plic 53>, + <94 &plic 53>, <95 &plic 53>; +}; + &mac0 { phy-mode = "sgmii"; phy-handle = <&phy0>; diff --git a/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-fabric.dtsi b/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-fabric.dtsi index 71f724325578..2d14e92f068d 100644 --- a/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-fabric.dtsi +++ b/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-fabric.dtsi @@ -1,6 +1,9 @@ // SPDX-License-Identifier: (GPL-2.0 OR MIT) /* Copyright (c) 2020-2021 Microchip Technology Inc */ +#include "mpfs.dtsi" +#include "mpfs-pinctrl.dtsi" + / { core_pwm0: pwm@40000000 { compatible = "microchip,corepwm-rtl-v4"; @@ -80,6 +83,16 @@ }; }; +&can0 { + pinctrl-names = "default"; + pinctrl-0 = <&can0_fabric>; +}; + +&can1 { + pinctrl-names = "default"; + pinctrl-0 = <&ikrd_can1_cfg>; +}; + &ccc_nw { clocks = <&refclk_ccc>, <&refclk_ccc>, <&refclk_ccc>, <&refclk_ccc>, <&refclk_ccc>, <&refclk_ccc>; @@ -87,3 +100,53 @@ "dll0_ref", "dll1_ref"; status = "okay"; }; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_fabric>; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_mssio>; +}; + +&mmuart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_fabric>; +}; + +&mmuart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_fabric>; +}; + +&mmuart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_fabric>; +}; + +&mmuart4 { + pinctrl-names = "default"; + pinctrl-0 = <&uart4_fabric>; +}; + +&mssio { + pinctrl-names = "default"; + pinctrl-0 = <&spi1_mssio>, <&can1_mssio>, <&mdio0_mssio>, <&mdio1_mssio>; +}; + +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&qspi_fabric>; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_fabric>; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&ikrd_spi1_cfg>; +}; diff --git a/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp.dts b/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp.dts index a8d623ee9fa4..86234968df48 100644 --- a/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp.dts +++ b/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp.dts @@ -52,11 +52,36 @@ status = "okay"; }; +&irqmux { + interrupt-map = <0 &plic 13>, <1 &plic 14>, <2 &plic 15>, + <3 &plic 16>, <4 &plic 17>, <5 &plic 18>, + <6 &plic 19>, <7 &plic 20>, <8 &plic 21>, + <9 &plic 22>, <10 &plic 23>, <11 &plic 24>, + <12 &plic 25>, <13 &plic 26>, + + <32 &plic 27>, <33 &plic 28>, <34 &plic 29>, + <35 &plic 30>, <36 &plic 31>, <37 &plic 32>, + <38 &plic 33>, <39 &plic 34>, <40 &plic 35>, + <41 &plic 36>, <42 &plic 37>, <43 &plic 38>, + <44 &plic 39>, <45 &plic 40>, <46 &plic 41>, + <47 &plic 42>, <48 &plic 43>, <49 &plic 44>, + <50 &plic 45>, <51 &plic 46>, <52 &plic 47>, + <53 &plic 48>, <54 &plic 49>, <55 &plic 50>, + + <64 &plic 53>, <65 &plic 53>, <66 &plic 53>, + <67 &plic 53>, <68 &plic 53>, <69 &plic 53>, + <70 &plic 53>, <71 &plic 53>, <72 &plic 53>, + <73 &plic 53>, <74 &plic 53>, <75 &plic 53>, + <76 &plic 53>, <77 &plic 53>, <78 &plic 53>, + <79 &plic 53>, <80 &plic 53>, <81 &plic 53>, + <82 &plic 53>, <83 &plic 53>, <84 &plic 53>, + <85 &plic 53>, <86 &plic 53>, <87 &plic 53>, + <88 &plic 53>, <89 &plic 53>, <90 &plic 53>, + <91 &plic 53>, <92 &plic 53>, <93 &plic 53>, + <94 &plic 53>, <95 &plic 53>; +}; + &gpio0 { - interrupts = <13>, <14>, <15>, <16>, - <17>, <18>, <19>, <20>, - <21>, <22>, <23>, <24>, - <25>, <26>; ngpios = <14>; status = "okay"; @@ -75,14 +100,6 @@ }; &gpio2 { - interrupts = <13>, <14>, <15>, <16>, - <17>, <18>, <19>, <20>, - <21>, <22>, <23>, <24>, - <25>, <26>, <27>, <28>, - <29>, <30>, <31>, <32>, - <33>, <34>, <35>, <36>, - <37>, <38>, <39>, <40>, - <41>, <42>, <43>, <44>; status = "okay"; }; diff --git a/arch/riscv/boot/dts/microchip/mpfs-pinctrl.dtsi b/arch/riscv/boot/dts/microchip/mpfs-pinctrl.dtsi new file mode 100644 index 000000000000..d37cabc6438a --- /dev/null +++ b/arch/riscv/boot/dts/microchip/mpfs-pinctrl.dtsi @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +&iomux0 { + spi0_fabric: mux-spi0-fabric { + function = "spi0"; + groups = "spi0_fabric"; + }; + + spi0_mssio: mux-spi0-mssio { + function = "spi0"; + groups = "spi0_mssio"; + }; + + spi1_fabric: mux-spi1-fabric { + function = "spi1"; + groups = "spi1_fabric"; + }; + + spi1_mssio: mux-spi1-mssio { + function = "spi1"; + groups = "spi1_mssio"; + }; + + i2c0_fabric: mux-i2c0-fabric { + function = "i2c0"; + groups = "i2c0_fabric"; + }; + + i2c0_mssio: mux-i2c0-mssio { + function = "i2c0"; + groups = "i2c0_mssio"; + }; + + i2c1_fabric: mux-i2c1-fabric { + function = "i2c1"; + groups = "i2c1_fabric"; + }; + + i2c1_mssio: mux-i2c1-mssio { + function = "i2c1"; + groups = "i2c1_mssio"; + }; + + can0_fabric: mux-can0-fabric { + function = "can0"; + groups = "can0_fabric"; + }; + + can0_mssio: mux-can0-mssio { + function = "can0"; + groups = "can0_mssio"; + }; + + can1_fabric: mux-can1-fabric { + function = "can1"; + groups = "can1_fabric"; + }; + + can1_mssio: mux-can1-mssio { + function = "can1"; + groups = "can1_mssio"; + }; + + qspi_fabric: mux-qspi-fabric { + function = "qspi"; + groups = "qspi_fabric"; + }; + + qspi_mssio: mux-qspi-mssio { + function = "qspi"; + groups = "qspi_mssio"; + }; + + uart0_fabric: mux-uart0-fabric { + function = "uart0"; + groups = "uart0_fabric"; + }; + + uart0_mssio: mux-uart0-mssio { + function = "uart0"; + groups = "uart0_mssio"; + }; + + uart1_fabric: mux-uart1-fabric { + function = "uart1"; + groups = "uart1_fabric"; + }; + + uart1_mssio: mux-uart1-mssio { + function = "uart1"; + groups = "uart1_mssio"; + }; + + uart2_fabric: mux-uart2-fabric { + function = "uart2"; + groups = "uart2_fabric"; + }; + + uart2_mssio: mux-uart2-mssio { + function = "uart2"; + groups = "uart2_mssio"; + }; + + uart3_fabric: mux-uart3-fabric { + function = "uart3"; + groups = "uart3_fabric"; + }; + + uart3_mssio: mux-uart3-mssio { + function = "uart3"; + groups = "uart3_mssio"; + }; + + uart4_fabric: mux-uart4-fabric { + function = "uart4"; + groups = "uart4_fabric"; + }; + + uart4_mssio: mux-uart4-mssio { + function = "uart4"; + groups = "uart4_mssio"; + }; + + mdio0_fabric: mux-mdio0-fabric { + function = "mdio0"; + groups = "mdio0_fabric"; + }; + + mdio0_mssio: mux-mdio0-mssio { + function = "mdio0"; + groups = "mdio0_mssio"; + }; + + mdio1_fabric: mux-mdio1-fabric { + function = "mdio1"; + groups = "mdio1_fabric"; + }; + + mdio1_mssio: mux-mdio1-mssio { + function = "mdio1"; + groups = "mdio1_mssio"; + }; +}; + +&mssio { + ikrd_can1_cfg: ikrd-can1-cfg { + can1-pins { + pins = <34>, <35>, <36>; + function = "can"; + bias-pull-up; + drive-strength = <8>; + power-source = <3300000>; + microchip,ibufmd = <0x1>; + }; + }; + + ikrd_spi1_cfg: ikrd-spi1-cfg { + spi1-pins { + pins = <30>, <31>, <32>, <33>; + function = "spi"; + bias-pull-up; + drive-strength = <8>; + power-source = <3300000>; + microchip,ibufmd = <0x1>; + }; + }; +}; diff --git a/arch/riscv/boot/dts/microchip/mpfs-polarberry.dts b/arch/riscv/boot/dts/microchip/mpfs-polarberry.dts index ea0808ab1042..510d59153cd0 100644 --- a/arch/riscv/boot/dts/microchip/mpfs-polarberry.dts +++ b/arch/riscv/boot/dts/microchip/mpfs-polarberry.dts @@ -30,6 +30,35 @@ }; }; +&irqmux { + interrupt-map = <0 &plic 13>, <1 &plic 14>, <2 &plic 15>, + <3 &plic 16>, <4 &plic 17>, <5 &plic 18>, + <6 &plic 19>, <7 &plic 20>, <8 &plic 21>, + <9 &plic 22>, <10 &plic 23>, <11 &plic 24>, + <12 &plic 25>, <13 &plic 26>, + + <32 &plic 27>, <33 &plic 28>, <34 &plic 29>, + <35 &plic 30>, <36 &plic 31>, <37 &plic 32>, + <38 &plic 33>, <39 &plic 34>, <40 &plic 35>, + <41 &plic 36>, <42 &plic 37>, <43 &plic 38>, + <44 &plic 39>, <45 &plic 40>, <46 &plic 41>, + <47 &plic 42>, <48 &plic 43>, <49 &plic 44>, + <50 &plic 45>, <51 &plic 46>, <52 &plic 47>, + <53 &plic 48>, <54 &plic 49>, <55 &plic 50>, + + <64 &plic 53>, <65 &plic 53>, <66 &plic 53>, + <67 &plic 53>, <68 &plic 53>, <69 &plic 53>, + <70 &plic 53>, <71 &plic 53>, <72 &plic 53>, + <73 &plic 53>, <74 &plic 53>, <75 &plic 53>, + <76 &plic 53>, <77 &plic 53>, <78 &plic 53>, + <79 &plic 53>, <80 &plic 53>, <81 &plic 53>, + <82 &plic 53>, <83 &plic 53>, <84 &plic 53>, + <85 &plic 53>, <86 &plic 53>, <87 &plic 53>, + <88 &plic 53>, <89 &plic 53>, <90 &plic 53>, + <91 &plic 53>, <92 &plic 53>, <93 &plic 53>, + <94 &plic 53>, <95 &plic 53>; +}; + /* * phy0 is connected to mac0, but the port itself is on the (optional) carrier * board. diff --git a/arch/riscv/boot/dts/microchip/mpfs-sev-kit.dts b/arch/riscv/boot/dts/microchip/mpfs-sev-kit.dts index f9a890579438..8f1908a10567 100644 --- a/arch/riscv/boot/dts/microchip/mpfs-sev-kit.dts +++ b/arch/riscv/boot/dts/microchip/mpfs-sev-kit.dts @@ -56,15 +56,36 @@ status = "okay"; }; +&irqmux { + interrupt-map = <0 &plic 13>, <1 &plic 14>, <2 &plic 15>, + <3 &plic 16>, <4 &plic 17>, <5 &plic 18>, + <6 &plic 19>, <7 &plic 20>, <8 &plic 21>, + <9 &plic 22>, <10 &plic 23>, <11 &plic 24>, + <12 &plic 25>, <13 &plic 26>, + + <32 &plic 27>, <33 &plic 28>, <34 &plic 29>, + <35 &plic 30>, <36 &plic 31>, <37 &plic 32>, + <38 &plic 33>, <39 &plic 34>, <40 &plic 35>, + <41 &plic 36>, <42 &plic 37>, <43 &plic 38>, + <44 &plic 39>, <45 &plic 40>, <46 &plic 41>, + <47 &plic 42>, <48 &plic 43>, <49 &plic 44>, + <50 &plic 45>, <51 &plic 46>, <52 &plic 47>, + <53 &plic 48>, <54 &plic 49>, <55 &plic 50>, + + <64 &plic 53>, <65 &plic 53>, <66 &plic 53>, + <67 &plic 53>, <68 &plic 53>, <69 &plic 53>, + <70 &plic 53>, <71 &plic 53>, <72 &plic 53>, + <73 &plic 53>, <74 &plic 53>, <75 &plic 53>, + <76 &plic 53>, <77 &plic 53>, <78 &plic 53>, + <79 &plic 53>, <80 &plic 53>, <81 &plic 53>, + <82 &plic 53>, <83 &plic 53>, <84 &plic 53>, + <85 &plic 53>, <86 &plic 53>, <87 &plic 53>, + <88 &plic 53>, <89 &plic 53>, <90 &plic 53>, + <91 &plic 53>, <92 &plic 53>, <93 &plic 53>, + <94 &plic 53>, <95 &plic 53>; +}; + &gpio2 { - interrupts = <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>; status = "okay"; }; diff --git a/arch/riscv/boot/dts/microchip/mpfs-tysom-m.dts b/arch/riscv/boot/dts/microchip/mpfs-tysom-m.dts index d1120f5f2c01..bc15530a2979 100644 --- a/arch/riscv/boot/dts/microchip/mpfs-tysom-m.dts +++ b/arch/riscv/boot/dts/microchip/mpfs-tysom-m.dts @@ -69,13 +69,36 @@ }; }; +&irqmux { + interrupt-map = <0 &plic 13>, <1 &plic 14>, <2 &plic 15>, + <3 &plic 16>, <4 &plic 17>, <5 &plic 18>, + <6 &plic 19>, <7 &plic 20>, <8 &plic 21>, + <9 &plic 22>, <10 &plic 23>, <11 &plic 24>, + <12 &plic 25>, <13 &plic 26>, + + <32 &plic 27>, <33 &plic 28>, <34 &plic 29>, + <35 &plic 30>, <36 &plic 31>, <37 &plic 32>, + <38 &plic 33>, <39 &plic 34>, <40 &plic 35>, + <41 &plic 36>, <42 &plic 37>, <43 &plic 38>, + <44 &plic 39>, <45 &plic 40>, <46 &plic 41>, + <47 &plic 42>, <48 &plic 43>, <49 &plic 44>, + <50 &plic 45>, <51 &plic 46>, <52 &plic 47>, + <53 &plic 48>, <54 &plic 49>, <55 &plic 50>, + + <64 &plic 53>, <65 &plic 53>, <66 &plic 53>, + <67 &plic 53>, <68 &plic 53>, <69 &plic 53>, + <70 &plic 53>, <71 &plic 53>, <72 &plic 53>, + <73 &plic 53>, <74 &plic 53>, <75 &plic 53>, + <76 &plic 53>, <77 &plic 53>, <78 &plic 53>, + <79 &plic 53>, <80 &plic 53>, <81 &plic 53>, + <82 &plic 53>, <83 &plic 53>, <84 &plic 53>, + <85 &plic 53>, <86 &plic 53>, <87 &plic 53>, + <88 &plic 53>, <89 &plic 53>, <90 &plic 53>, + <91 &plic 53>, <92 &plic 53>, <93 &plic 53>, + <94 &plic 53>, <95 &plic 53>; +}; + &gpio1 { - interrupts = <27>, <28>, <29>, <30>, - <31>, <32>, <33>, <47>, - <35>, <36>, <37>, <38>, - <39>, <40>, <41>, <42>, - <43>, <44>, <45>, <46>, - <47>, <48>, <49>, <50>; status = "okay"; }; diff --git a/arch/riscv/boot/dts/microchip/mpfs.dtsi b/arch/riscv/boot/dts/microchip/mpfs.dtsi index a0ffedc2d344..d535d4c72763 100644 --- a/arch/riscv/boot/dts/microchip/mpfs.dtsi +++ b/arch/riscv/boot/dts/microchip/mpfs.dtsi @@ -254,7 +254,31 @@ mss_top_sysreg: syscon@20002000 { compatible = "microchip,mpfs-mss-top-sysreg", "syscon", "simple-mfd"; reg = <0x0 0x20002000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; #reset-cells = <1>; + + irqmux: interrupt-controller@54 { + compatible = "microchip,mpfs-irqmux"; + reg = <0x54 0x4>; + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0x7f>; + }; + + iomux0: pinctrl@200 { + compatible = "microchip,mpfs-pinctrl-iomux0"; + reg = <0x200 0x4>; + pinctrl-use-default; + + }; + + mssio: pinctrl@204 { + compatible = "microchip,mpfs-pinctrl-mssio"; + reg = <0x204 0x7c>; + /* on icicle ref design at least */ + pinctrl-use-default; + }; }; sysreg_scb: syscon@20003000 { @@ -450,8 +474,8 @@ interrupt-parent = <&plic>; interrupts = <64>, <65>, <66>, <67>, <68>, <69>; local-mac-address = [00 00 00 00 00 00]; - clocks = <&clkcfg CLK_MAC0>, <&clkcfg CLK_AHB>; - clock-names = "pclk", "hclk"; + clocks = <&clkcfg CLK_MAC0>, <&clkcfg CLK_AHB>, <&refclk>; + clock-names = "pclk", "hclk", "tsu_clk"; resets = <&mss_top_sysreg CLK_MAC0>; status = "disabled"; }; @@ -464,8 +488,8 @@ interrupt-parent = <&plic>; interrupts = <70>, <71>, <72>, <73>, <74>, <75>; local-mac-address = [00 00 00 00 00 00]; - clocks = <&clkcfg CLK_MAC1>, <&clkcfg CLK_AHB>; - clock-names = "pclk", "hclk"; + clocks = <&clkcfg CLK_MAC1>, <&clkcfg CLK_AHB>, <&refclk>; + clock-names = "pclk", "hclk", "tsu_clk"; resets = <&mss_top_sysreg CLK_MAC1>; status = "disabled"; }; @@ -473,36 +497,57 @@ gpio0: gpio@20120000 { compatible = "microchip,mpfs-gpio"; reg = <0x0 0x20120000 0x0 0x1000>; - interrupt-parent = <&plic>; + interrupt-parent = <&irqmux>; interrupt-controller; #interrupt-cells = <1>; + interrupts = <0>, <1>, <2>, <3>, + <4>, <5>, <6>, <7>, + <8>, <9>, <10>, <11>, + <12>, <13>; clocks = <&clkcfg CLK_GPIO0>; gpio-controller; #gpio-cells = <2>; + ngpios = <14>; status = "disabled"; }; gpio1: gpio@20121000 { compatible = "microchip,mpfs-gpio"; reg = <0x0 0x20121000 0x0 0x1000>; - interrupt-parent = <&plic>; + interrupt-parent = <&irqmux>; interrupt-controller; #interrupt-cells = <1>; + interrupts = <32>, <33>, <34>, <35>, + <36>, <37>, <38>, <39>, + <40>, <41>, <42>, <43>, + <44>, <45>, <46>, <47>, + <48>, <49>, <50>, <51>, + <52>, <53>, <54>, <55>; clocks = <&clkcfg CLK_GPIO1>; gpio-controller; #gpio-cells = <2>; + ngpios = <24>; status = "disabled"; }; gpio2: gpio@20122000 { compatible = "microchip,mpfs-gpio"; reg = <0x0 0x20122000 0x0 0x1000>; - interrupt-parent = <&plic>; + interrupt-parent = <&irqmux>; interrupt-controller; #interrupt-cells = <1>; + interrupts = <64>, <65>, <66>, <67>, + <68>, <69>, <70>, <71>, + <72>, <73>, <74>, <75>, + <76>, <77>, <78>, <79>, + <80>, <81>, <82>, <83>, + <84>, <85>, <86>, <87>, + <88>, <89>, <90>, <91>, + <92>, <93>, <94>, <95>; clocks = <&clkcfg CLK_GPIO2>; gpio-controller; #gpio-cells = <2>; + ngpios = <32>; status = "disabled"; }; diff --git a/arch/riscv/boot/dts/microchip/pic64gx-curiosity-kit.dts b/arch/riscv/boot/dts/microchip/pic64gx-curiosity-kit.dts new file mode 100644 index 000000000000..2f2ccd77af30 --- /dev/null +++ b/arch/riscv/boot/dts/microchip/pic64gx-curiosity-kit.dts @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Device Tree Source for the PIC64GX Curiosity Kit + * + * Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + * + * Author: Pierre-Henry Moussay + * + * The Curiosity-GX10000 (PIC64GX SoC Curiosity Kit) is a compact SoC + * prototyping board featuring a Microchip PIC64GX SoC + * PIC64GX-1000. Features include: + * - 1 GB DDR4 SDRAM + * - Gigabit Ethernet + * - microSD-card slot + * + * https://www.microchip.com/en-us/development-tool/curiosity-pic64gx1000-kit-es + */ + +/dts-v1/; + +#include "pic64gx.dtsi" +#include "pic64gx-pinctrl.dtsi" + +/* Clock frequency (in Hz) of the rtcclk */ +#define RTCCLK_FREQ 1000000 + +/ { + #address-cells = <2>; + #size-cells = <2>; + model = "Microchip PIC64GX Curiosity Kit"; + compatible = "microchip,pic64gx-curiosity-kit", "microchip,pic64gx"; + + aliases { + ethernet0 = &mac0; + serial1 = &mmuart1; + serial2 = &mmuart2; + }; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + cpus { + timebase-frequency = ; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x40000000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + hss: hss-buffer@bfc00000 { + compatible = "shared-dma-pool"; + reg = <0x0 0xbfc00000 0x0 0x400000>; + no-map; + }; + }; +}; + +&gpio0 { + interrupts = <13>, <14>, <15>, <16>, + <17>, <18>, <19>, <20>, + <21>, <22>, <23>, <24>, + <25>, <26>; + status ="okay"; + gpio-line-names = + "", "", "", "", "", "", "", "", + "", "", "", "", "MIPI_CAM_RESET", "MIPI_CAM_STANDBY"; +}; + +&gpio1 { + interrupts = <27>, <28>, <29>, <30>, + <31>, <32>, <33>, <34>, + <35>, <36>, <37>, <38>, + <39>, <40>, <41>, <42>, + <43>, <44>, <45>, <46>, + <47>, <48>, <49>, <50>; + status ="okay"; + gpio-line-names = + "", "", "LED1", "LED2", "LED3", "LED4", "LED5", "LED6", + "LED7", "LED8", "", "", "", "", "", "", + "", "", "", "", "HDMI_HPD", "", "", "GPIO_1_23"; +}; + +&gpio2 { + interrupts = <53>, <53>, <53>, <53>, + <53>, <53>, <53>, <53>, + <53>, <53>, <53>, <53>, + <53>, <53>, <53>, <53>, + <53>, <53>, <53>, <53>, + <53>, <53>, <53>, <53>, + <53>, <53>, <53>, <53>, + <53>, <53>, <53>, <53>; + pinctrl-names = "default"; + pinctrl-0 = <&mdio1_gpio>, <&spi0_gpio>, <&can0_gpio>, <&pcie_gpio>, + <&qspi_gpio>, <&uart3_gpio>, <&uart4_gpio>, <&can1_gpio>; + status ="okay"; + gpio-line-names = + "", "", "", "", "", "", "SWITCH2", "USR_IO12", + "DIP1", "DIP2", "", "DIP3", "USR_IO1", "USR_IO2", "USR_IO7", "USR_IO8", + "USR_IO3", "USR_IO4", "USR_IO5", "USR_IO6", "", "", "USR_IO9", "USR_IO10", + "DIP4", "USR_IO11", "", "", "SWITCH1", "", "", ""; +}; + +&mac0 { + status = "okay"; + phy-mode = "sgmii"; + phy-handle = <&phy0>; + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_default>; + + phy0: ethernet-phy@b { + reg = <0xb>; + }; +}; + +&mbox { + status = "okay"; +}; + +&i2c0 { + status = "okay"; +}; + +&i2c1 { + status = "okay"; +}; + +&mmc { + bus-width = <4>; + disable-wp; + cap-sd-highspeed; + cap-mmc-highspeed; + sdhci-caps-mask = <0x00000007 0x00000000>; + status = "okay"; +}; + +&mmuart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_fio>; + status = "okay"; +}; + +&mmuart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_default>; + status = "okay"; +}; + +&refclk { + clock-frequency = <125000000>; +}; + +&rtc { + status = "okay"; +}; + +&syscontroller { + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/microchip/pic64gx-pinctrl.dtsi b/arch/riscv/boot/dts/microchip/pic64gx-pinctrl.dtsi new file mode 100644 index 000000000000..cf73b0556633 --- /dev/null +++ b/arch/riscv/boot/dts/microchip/pic64gx-pinctrl.dtsi @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +&iomux0 { + spi0_fio: mux-spi0-fabric { + function = "spi0"; + groups = "spi0_fabric"; + }; + + spi1_mssio: mux-spi1-mssio { + function = "spi1"; + groups = "spi1_mssio"; + }; + + i2c0_mssio: mux-i2c0-mssio { + function = "i2c0"; + groups = "i2c0_mssio"; + }; + + i2c1_mssio: mux-i2c1-mssio { + function = "i2c1"; + groups = "i2c1_mssio"; + }; + + can0_fio: mux-can0-fabric { + function = "can0"; + groups = "can0_fabric"; + }; + + can1_fio: mux-can1-fabric { + function = "can1"; + groups = "can1_fabric"; + }; + + qspi_fio: mux-qspi-fabric { + function = "qspi"; + groups = "qspi_fabric"; + }; + + uart0_mssio: mux-uart0-mssio { + function = "uart0"; + groups = "uart0_mssio"; + }; + + uart1_fio: mux-uart1-fabric { + function = "uart1"; + groups = "uart1_fabric"; + }; + + uart2_fio: mux-uart2-fabric { + function = "uart2"; + groups = "uart2_fabric"; + }; + + uart3_fio: mux-uart3-fabric { + function = "uart3"; + groups = "uart3_fabric"; + }; + + uart4_fio: mux-uart4-fabric { + function = "uart4"; + groups = "uart4_fabric"; + }; + + mdio0_fio: mux-mdio0-fabric { + function = "mdio0"; + groups = "mdio0_fabric"; + }; + + mdio1_fio: mux-mdio1-fabric { + function = "mdio1"; + groups = "mdio1_fabric"; + }; +}; + +&gpio2_pinctrl { + //TODO rethink the labels, since a bunch of these are not defaults or + //just outright remove the non-default groups + mdio0_default: mux-mac0 { + function = "mdio0"; + groups = "mdio0"; + }; + + mdio0_gpio: mux-mac0-gpio2 { + function = "gpio"; + groups = "gpio_mdio0"; + }; + + mdio1_default: mux-mac1 { + function = "mdio1"; + groups = "mdio1"; + }; + + mdio1_gpio: mux-mac1-gpio2 { + function = "gpio"; + groups = "gpio_mdio1"; + }; + + spi0_default: mux-spi0 { + function = "spi0"; + groups = "spi0"; + }; + + spi0_gpio: mux-spi0-gpio2 { + function = "gpio"; + groups = "gpio_spi0"; + }; + + can0_default: mux-can0 { + function = "can0"; + groups = "can0"; + }; + + can0_gpio: mux-can0-gpio2 { + function = "gpio"; + groups = "gpio_can0"; + }; + + pcie_default: mux-pcie { + function = "pcie"; + groups = "pcie"; + }; + + pcie_gpio: mux-pcie-gpio2 { + function = "gpio"; + groups = "gpio_pcie"; + }; + + qspi_default: mux-qspi { + function = "qspi"; + groups = "qspi"; + }; + + qspi_gpio: mux-qspi-gpio2 { + function = "gpio"; + groups = "gpio_qspi"; + }; + + uart3_default: mux-uart3 { + function = "uart3"; + groups = "uart3"; + }; + + uart3_gpio: mux-uart3-gpio2 { + function = "gpio"; + groups = "gpio_uart3"; + }; + + uart4_default: mux-uart4 { + function = "uart4"; + groups = "uart4"; + }; + + uart4_gpio: mux-uart4-gpio2 { + function = "gpio"; + groups = "gpio_uart4"; + }; + + can1_default: mux-can1 { + function = "can1"; + groups = "can1"; + }; + + can1_gpio: mux-can1-gpio2 { + function = "gpio"; + groups = "gpio_can1"; + }; + + uart2_default: mux-uart2 { + function = "uart2"; + groups = "uart2"; + }; + + uart2_gpio: mux-uart2-gpio2 { + function = "gpio"; + groups = "gpio_uart2"; + }; +}; diff --git a/arch/riscv/boot/dts/microchip/pic64gx.dtsi b/arch/riscv/boot/dts/microchip/pic64gx.dtsi new file mode 100644 index 000000000000..c164d7bc270a --- /dev/null +++ b/arch/riscv/boot/dts/microchip/pic64gx.dtsi @@ -0,0 +1,630 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Device Tree Source for the PIC64GX SoCs + * + * Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + * + * Author: Pierre-Henry Moussay + * + * PIC64GX is a series RISC-V multicore SoCs: + * https://www.microchip.com/en-us/products/microprocessors/64-bit-mpus/pic64gx + */ + +/dts-v1/; +#include "dt-bindings/clock/microchip,mpfs-clock.h" + +/ { + #address-cells = <2>; + #size-cells = <2>; + model = "Microchip PIC64GX SoC"; + compatible = "microchip,pic64gx"; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "sifive,e51", "sifive,rocket0", "riscv"; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <128>; + i-cache-size = <16384>; + reg = <0>; + riscv,isa = "rv64imac"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "c", "zicntr", "zicsr", + "zifencei", "zihpm"; + clocks = <&clkcfg CLK_CPU>; + status = "disabled"; + + cpu0_intc: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu1: cpu@1 { + compatible = "sifive,u54-mc", "sifive,rocket0", "riscv"; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <32>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <32>; + mmu-type = "riscv,sv39"; + reg = <1>; + riscv,isa = "rv64imafdc"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", + "zicsr", "zifencei", "zihpm"; + clocks = <&clkcfg CLK_CPU>; + tlb-split; + next-level-cache = <&cctrllr>; + status = "okay"; + + cpu1_intc: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu2: cpu@2 { + compatible = "sifive,u54-mc", "sifive,rocket0", "riscv"; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <32>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <32>; + mmu-type = "riscv,sv39"; + reg = <2>; + riscv,isa = "rv64imafdc"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", + "zicsr", "zifencei", "zihpm"; + clocks = <&clkcfg CLK_CPU>; + tlb-split; + next-level-cache = <&cctrllr>; + status = "okay"; + + cpu2_intc: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu3: cpu@3 { + compatible = "sifive,u54-mc", "sifive,rocket0", "riscv"; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <32>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <32>; + mmu-type = "riscv,sv39"; + reg = <3>; + riscv,isa = "rv64imafdc"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", + "zicsr", "zifencei", "zihpm"; + clocks = <&clkcfg CLK_CPU>; + tlb-split; + next-level-cache = <&cctrllr>; + status = "okay"; + + cpu3_intc: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu4: cpu@4 { + compatible = "sifive,u54-mc", "sifive,rocket0", "riscv"; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <32>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <32>; + mmu-type = "riscv,sv39"; + reg = <4>; + riscv,isa = "rv64imafdc"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", + "zicsr", "zifencei", "zihpm"; + clocks = <&clkcfg CLK_CPU>; + tlb-split; + next-level-cache = <&cctrllr>; + status = "okay"; + cpu4_intc: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu-map { + cluster0 { + core0 { + cpu = <&cpu0>; + }; + + core1 { + cpu = <&cpu1>; + }; + + core2 { + cpu = <&cpu2>; + }; + + core3 { + cpu = <&cpu3>; + }; + + core4 { + cpu = <&cpu4>; + }; + }; + }; + }; + + scbclk: clock-80000000 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <80000000>; + }; + + refclk: mssrefclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + }; + + axiclk: axi-aclk0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <125000000>; + }; + + videoclk: video-aclk0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <125000000>; + }; + + syscontroller: syscontroller { + compatible = "microchip,pic64gx-sys-controller"; + mboxes = <&mbox 0>; + }; + + soc { + #address-cells = <2>; + #size-cells = <2>; + compatible = "simple-bus"; + ranges; + + clint: clint@2000000 { + compatible = "microchip,pic64gx-clint", "sifive,clint0"; + reg = <0x0 0x2000000 0x0 0xC000>; + interrupts-extended = <&cpu0_intc 0xffffffff>, <&cpu0_intc 0xffffffff>, + <&cpu1_intc 3>, <&cpu1_intc 7>, + <&cpu2_intc 3>, <&cpu2_intc 7>, + <&cpu3_intc 3>, <&cpu3_intc 7>, + <&cpu4_intc 3>, <&cpu4_intc 7>; + }; + + cctrllr: cache-controller@2010000 { + compatible = "microchip,pic64gx-ccache", "microchip,mpfs-ccache", + "sifive,fu540-c000-ccache", "cache"; + reg = <0x0 0x2010000 0x0 0x1000>; + cache-block-size = <64>; + cache-level = <2>; + cache-sets = <1024>; + cache-size = <2097152>; + cache-unified; + interrupt-parent = <&plic>; + interrupts = <1>, <3>, <4>, <2>; + }; + + pdma: dma-controller@3000000 { + compatible = "microchip,pic64gx-pdma", "microchip,mpfs-pdma", + "sifive,pdma0"; + reg = <0x0 0x3000000 0x0 0x8000>; + interrupt-parent = <&plic>; + interrupts = <5 6>, <7 8>, <9 10>, <11 12>; + #dma-cells = <1>; + }; + + plic: interrupt-controller@c000000 { + compatible = "microchip,pic64gx-plic", "sifive,plic-1.0.0"; + reg = <0x0 0xc000000 0x0 0x4000000>; + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + interrupts-extended = <&cpu0_intc 0xffffffff>, + <&cpu1_intc 0xffffffff>, <&cpu1_intc 9>, + <&cpu2_intc 0xffffffff>, <&cpu2_intc 9>, + <&cpu3_intc 0xffffffff>, <&cpu3_intc 9>, + <&cpu4_intc 0xffffffff>, <&cpu4_intc 9>; + riscv,ndev = <186>; + }; + + mmuart0: serial@20000000 { + compatible = "ns16550a"; + reg = <0x0 0x20000000 0x0 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupt-parent = <&plic>; + interrupts = <90>; + current-speed = <115200>; + clocks = <&clkcfg CLK_MMUART0>; + pinctrl-names = "default"; + pinctrl-0 = <&uart0_mssio>; + status = "disabled"; /* Reserved for the HSS */ + }; + + mss_top_sysreg: syscon@20002000 { + compatible = "microchip,pic64gx-mss-top-sysreg", + "microchip,mpfs-mss-top-sysreg", + "syscon", "simple-mfd"; + reg = <0x0 0x20002000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + #reset-cells = <1>; + + iomux0: pinctrl@200 { + compatible = "microchip,pic64gx-pinctrl-iomux0", + "microchip,mpfs-pinctrl-iomux0"; + reg = <0x200 0x4>; + pinctrl-use-default; + }; + + mssio: pinctrl@204 { + compatible = "microchip,pic64gx-pinctrl-mssio", + "microchip,mpfs-pinctrl-mssio"; + reg = <0x204 0x7c>; + /* on icicle ref design at least */ + pinctrl-use-default; + }; + }; + + sysreg_scb: syscon@20003000 { + compatible = "microchip,pic64gx-sysreg-scb", + "microchip,mpfs-sysreg-scb", + "syscon"; + reg = <0x0 0x20003000 0x0 0x1000>; + }; + + /* Common node entry for emmc/sd */ + mmc: mmc@20008000 { + compatible = "microchip,pic64gx-sd4hc", "cdns,sd4hc"; + reg = <0x0 0x20008000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <88>; + clocks = <&clkcfg CLK_MMC>; + max-frequency = <200000000>; + status = "disabled"; + }; + + mmuart1: serial@20100000 { + compatible = "ns16550a"; + reg = <0x0 0x20100000 0x0 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupt-parent = <&plic>; + interrupts = <91>; + current-speed = <115200>; + clocks = <&clkcfg CLK_MMUART1>; + status = "disabled"; + }; + + mmuart2: serial@20102000 { + compatible = "ns16550a"; + reg = <0x0 0x20102000 0x0 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupt-parent = <&plic>; + interrupts = <92>; + current-speed = <115200>; + clocks = <&clkcfg CLK_MMUART2>; + status = "disabled"; + }; + + mmuart3: serial@20104000 { + compatible = "ns16550a"; + reg = <0x0 0x20104000 0x0 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupt-parent = <&plic>; + interrupts = <93>; + current-speed = <115200>; + clocks = <&clkcfg CLK_MMUART3>; + status = "disabled"; + }; + + mmuart4: serial@20106000 { + compatible = "ns16550a"; + reg = <0x0 0x20106000 0x0 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupt-parent = <&plic>; + interrupts = <94>; + clocks = <&clkcfg CLK_MMUART4>; + current-speed = <115200>; + status = "disabled"; + }; + + spi0: spi@20108000 { + compatible = "microchip,pic64gx-spi", "microchip,mpfs-spi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x20108000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <54>; + clocks = <&clkcfg CLK_SPI0>; + status = "disabled"; + }; + + spi1: spi@20109000 { + compatible = "microchip,pic64gx-spi", "microchip,mpfs-spi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x20109000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <55>; + clocks = <&clkcfg CLK_SPI1>; + pinctrl-names = "default"; + pinctrl-0 = <&spi1_mssio>; + status = "disabled"; + }; + + i2c0: i2c@2010a000 { + compatible = "microchip,pic64gx-i2c", "microchip,corei2c-rtl-v7"; + reg = <0x0 0x2010a000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&plic>; + interrupts = <58>; + clocks = <&clkcfg CLK_I2C0>; + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_mssio>; + status = "disabled"; + }; + + i2c1: i2c@2010b000 { + compatible = "microchip,pic64gx-i2c", "microchip,corei2c-rtl-v7"; + reg = <0x0 0x2010b000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&plic>; + interrupts = <61>; + clocks = <&clkcfg CLK_I2C1>; + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_mssio>; + status = "disabled"; + }; + + can0: can@2010c000 { + compatible = "microchip,pic64gx-can", "microchip,mpfs-can"; + reg = <0x0 0x2010c000 0x0 0x1000>; + clocks = <&clkcfg CLK_CAN0>, <&clkcfg CLK_MSSPLL3>; + interrupt-parent = <&plic>; + interrupts = <56>; + resets = <&mss_top_sysreg CLK_CAN0>; + status = "disabled"; + }; + + can1: can@2010d000 { + compatible = "microchip,pic64gx-can", "microchip,mpfs-can"; + reg = <0x0 0x2010d000 0x0 0x1000>; + clocks = <&clkcfg CLK_CAN1>, <&clkcfg CLK_MSSPLL3>; + interrupt-parent = <&plic>; + interrupts = <57>; + resets = <&mss_top_sysreg CLK_CAN1>; + status = "disabled"; + }; + + mac0: ethernet@20110000 { + compatible = "microchip,pic64gx-macb", "microchip,mpfs-macb", + "cdns,macb"; + reg = <0x0 0x20110000 0x0 0x2000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&plic>; + interrupts = <64>, <65>, <66>, <67>, <68>, <69>; + /* Filled in by a bootloader */ + local-mac-address = [00 00 00 00 00 00]; + clocks = <&clkcfg CLK_MAC0>, <&clkcfg CLK_AHB>; + clock-names = "pclk", "hclk"; + resets = <&mss_top_sysreg CLK_MAC0>; + status = "disabled"; + }; + + mac1: ethernet@20112000 { + compatible = "microchip,pic64gx-macb", "microchip,mpfs-macb", + "cdns,macb"; + reg = <0x0 0x20112000 0x0 0x2000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&plic>; + interrupts = <70>, <71>, <72>, <73>, <74>, <75>; + /* Filled in by a bootloader */ + local-mac-address = [00 00 00 00 00 00]; + clocks = <&clkcfg CLK_MAC1>, <&clkcfg CLK_AHB>; + clock-names = "pclk", "hclk"; + resets = <&mss_top_sysreg CLK_MAC1>; + status = "disabled"; + }; + + gpio0: gpio@20120000 { + compatible = "microchip,pic64gx-gpio", "microchip,mpfs-gpio"; + reg = <0x0 0x20120000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupt-controller; + #interrupt-cells = <1>; + clocks = <&clkcfg CLK_GPIO0>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <14>; + status = "disabled"; + }; + + gpio1: gpio@20121000 { + compatible = "microchip,pic64gx-gpio", "microchip,mpfs-gpio"; + reg = <0x0 0x20121000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupt-controller; + #interrupt-cells = <1>; + clocks = <&clkcfg CLK_GPIO1>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <24>; + status = "disabled"; + }; + + gpio2: gpio@20122000 { + compatible = "microchip,pic64gx-gpio", "microchip,mpfs-gpio"; + reg = <0x0 0x20122000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupt-controller; + #interrupt-cells = <1>; + clocks = <&clkcfg CLK_GPIO2>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + status = "disabled"; + }; + + rtc: rtc@20124000 { + compatible = "microchip,pic64gx-rtc", "microchip,mpfs-rtc"; + reg = <0x0 0x20124000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <80>, <81>; + clocks = <&clkcfg CLK_RTC>, <&clkcfg CLK_RTCREF>; + clock-names = "rtc", "rtcref"; + status = "disabled"; + }; + + usb: usb@20201000 { + compatible = "microchip,pic64gx-musb", "microchip,mpfs-musb"; + reg = <0x0 0x20201000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <86>, <87>; + clocks = <&clkcfg CLK_USB>; + interrupt-names = "dma", "mc"; + status = "disabled"; + }; + + qspi: spi@21000000 { + compatible = "microchip,pic64gx-qspi", "microchip,coreqspi-rtl-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x21000000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <85>; + clocks = <&clkcfg CLK_QSPI>; + status = "disabled"; + }; + + control_scb: syscon@37020000 { + compatible = "microchip,pic64gx-control-scb", + "microchip,mpfs-control-scb", + "syscon"; + reg = <0x0 0x37020000 0x0 0x100>; + }; + + syscontroller_qspi: spi@37020100 { + compatible = "microchip,pic64gx-qspi", "microchip,coreqspi-rtl-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x37020100 0x0 0x100>; + interrupt-parent = <&plic>; + interrupts = <110>; + clocks = <&scbclk>; + status = "disabled"; + }; + + mbox: mailbox@37020800 { + compatible = "microchip,pic64gx-mailbox", "microchip,mpfs-mailbox"; + reg = <0x0 0x37020800 0x0 0x100>; + interrupt-parent = <&plic>; + interrupts = <96>; + #mbox-cells = <1>; + status = "disabled"; + }; + + ccc_se: clock-controller@38010000 { + compatible = "microchip,pic64gx-ccc", "microchip,mpfs-ccc"; + reg = <0x0 0x38010000 0x0 0x1000>, <0x0 0x38020000 0x0 0x1000>, + <0x0 0x39010000 0x0 0x1000>, <0x0 0x39020000 0x0 0x1000>; + #clock-cells = <1>; + status = "disabled"; + }; + + ccc_ne: clock-controller@38040000 { + compatible = "microchip,pic64gx-ccc", "microchip,mpfs-ccc"; + reg = <0x0 0x38040000 0x0 0x1000>, <0x0 0x38080000 0x0 0x1000>, + <0x0 0x39040000 0x0 0x1000>, <0x0 0x39080000 0x0 0x1000>; + #clock-cells = <1>; + status = "disabled"; + }; + + ccc_nw: clock-controller@38100000 { + compatible = "microchip,pic64gx-ccc", "microchip,mpfs-ccc"; + reg = <0x0 0x38100000 0x0 0x1000>, <0x0 0x38200000 0x0 0x1000>, + <0x0 0x39100000 0x0 0x1000>, <0x0 0x39200000 0x0 0x1000>; + #clock-cells = <1>; + status = "disabled"; + }; + + ccc_sw: clock-controller@38400000 { + compatible = "microchip,pic64gx-ccc", "microchip,mpfs-ccc"; + reg = <0x0 0x38400000 0x0 0x1000>, <0x0 0x38800000 0x0 0x1000>, + <0x0 0x39400000 0x0 0x1000>, <0x0 0x39800000 0x0 0x1000>; + #clock-cells = <1>; + status = "disabled"; + }; + + clkcfg: clkcfg@3e001000 { + compatible = "microchip,pic64gx-clkcfg", "microchip,mpfs-clkcfg"; + reg = <0x0 0x3e001000 0x0 0x1000>; + clocks = <&refclk>; + #clock-cells = <1>; + }; + + gpio2_pinctrl: pinctrl@41000000 { + compatible = "microchip,pic64gx-pinctrl-gpio2"; + reg = <0x0 0x41000000 0x0 0x4>; + pinctrl-use-default; + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_fio>, <&mdio1_fio>, <&spi0_fio>, <&qspi_fio>, + <&uart3_fio>, <&uart4_fio>, <&can1_fio>, <&can0_fio>, + <&uart2_fio>; + }; + }; +}; diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts index 5971605754b3..5790d927b93d 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -33,7 +33,7 @@ }; }; - pcie_vcc_3v3: pcie-vcc3v3 { + pcie_vcc_3v3: regulator-pcie-vcc3v3 { compatible = "regulator-fixed"; regulator-name = "PCIE_VCC3V3"; regulator-min-microvolt = <3300000>; @@ -41,7 +41,7 @@ regulator-always-on; }; - reg_dc_in: dc-in-12v { + reg_dc_in: regulator-dc-in-12v { compatible = "regulator-fixed"; regulator-name = "dc_in_12v"; regulator-min-microvolt = <12000000>; @@ -50,7 +50,7 @@ regulator-always-on; }; - reg_vcc_4v: vcc-4v { + reg_vcc_4v: regulator-vcc-4v { compatible = "regulator-fixed"; regulator-name = "vcc_4v"; regulator-min-microvolt = <4000000>; @@ -60,7 +60,7 @@ vin-supply = <®_dc_in>; }; - usb3-vbus-5v { + regulator-usb3-vbus-5v { compatible = "regulator-fixed"; regulator-name = "USB30_VBUS"; regulator-min-microvolt = <5000000>; @@ -70,7 +70,7 @@ enable-active-high; }; - usb3_hub_5v: usb3-hub-5v { + usb3_hub_5v: regulator-usb3-hub-5v { compatible = "regulator-fixed"; regulator-name = "USB30_HUB"; regulator-min-microvolt = <5000000>; @@ -81,8 +81,6 @@ }; &combo_phy { - pinctrl-names = "default"; - pinctrl-0 = <&pcie0_3_cfg>; status = "okay"; }; @@ -190,7 +188,15 @@ compatible = "spacemit,p1"; reg = <0x41>; interrupts = <64>; - vin-supply = <®_vcc_4v>; + vin1-supply = <®_vcc_4v>; + vin2-supply = <®_vcc_4v>; + vin3-supply = <®_vcc_4v>; + vin4-supply = <®_vcc_4v>; + vin5-supply = <®_vcc_4v>; + vin6-supply = <®_vcc_4v>; + aldoin-supply = <®_vcc_4v>; + dldoin1-supply = <&buck5>; + dldoin2-supply = <&buck5>; regulators { buck1 { @@ -221,7 +227,7 @@ regulator-always-on; }; - buck5 { + buck5: buck5 { regulator-min-microvolt = <500000>; regulator-max-microvolt = <3450000>; regulator-ramp-delay = <5000>; @@ -305,6 +311,7 @@ &pcie1_port { phys = <&pcie1_phy>; + vpcie3v3-supply = <&pcie_vcc_3v3>; }; &pcie1 { @@ -320,6 +327,7 @@ &pcie2_port { phys = <&pcie2_phy>; + vpcie3v3-supply = <&pcie_vcc_3v3>; }; &pcie2 { diff --git a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts index 800a112d5d70..afaad59e6bce 100644 --- a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts +++ b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts @@ -15,13 +15,42 @@ ethernet0 = ð0; ethernet1 = ð1; serial0 = &uart0; + i2c2 = &i2c2; + i2c8 = &i2c8; }; chosen { stdout-path = "serial0"; }; - reg_dc_in: dc-in-12v { + leds { + compatible = "gpio-leds"; + + led1 { + label = "pwr-led"; + gpios = <&gpio K1_GPIO(96) GPIO_ACTIVE_HIGH>; + linux,default-trigger = "default-on"; + default-state = "on"; + }; + + led2 { + label = "hdd-led"; + gpios = <&gpio K1_GPIO(92) GPIO_ACTIVE_HIGH>; + linux,default-trigger = "disk-activity"; + }; + }; + + pcie_vcc_3v3: regulator-pcie-vcc3v3 { + compatible = "regulator-fixed"; + regulator-name = "pcie_vcc3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + vin-supply = <®_dc_in>; + }; + + reg_dc_in: regulator-dc-in-12v { compatible = "regulator-fixed"; regulator-name = "dc_in_12v"; regulator-min-microvolt = <12000000>; @@ -30,7 +59,7 @@ regulator-always-on; }; - reg_vcc_4v: vcc-4v { + reg_vcc_4v: regulator-vcc-4v { compatible = "regulator-fixed"; regulator-name = "vcc_4v"; regulator-min-microvolt = <4000000>; @@ -39,6 +68,41 @@ regulator-always-on; vin-supply = <®_dc_in>; }; + + reg_vcc_5v: regulator-vcc-5v { + compatible = "regulator-fixed"; + regulator-name = "vcc_5v"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + regulator-always-on; + vin-supply = <®_dc_in>; + }; + + regulator-usb3-vbus-5v { + compatible = "regulator-fixed"; + regulator-name = "USB30_VBUS"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + vin-supply = <®_vcc_5v>; + gpio = <&gpio K1_GPIO(97) GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + usb3_hub_5v: regulator-usb3-hub-5v { + compatible = "regulator-fixed"; + regulator-name = "USB30_HUB"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <®_vcc_5v>; + gpio = <&gpio K1_GPIO(123) GPIO_ACTIVE_HIGH>; + enable-active-high; + }; +}; + +&combo_phy { + status = "okay"; }; ð0 { @@ -91,6 +155,28 @@ status = "okay"; }; +&i2c2 { + pinctrl-0 = <&i2c2_0_cfg>; + pinctrl-names = "default"; + status = "okay"; + + eeprom@50 { + compatible = "atmel,24c04"; + reg = <0x50>; + vcc-supply = <&buck3_1v8>; /* EEPROM_VCC18 */ + pagesize = <16>; + read-only; + size = <512>; + + nvmem-layout { + compatible = "onie,tlv-layout"; + + product-name { + }; + }; + }; +}; + &i2c8 { pinctrl-0 = <&i2c8_cfg>; pinctrl-names = "default"; @@ -100,7 +186,15 @@ compatible = "spacemit,p1"; reg = <0x41>; interrupts = <64>; - vin-supply = <®_vcc_4v>; + vin1-supply = <®_vcc_4v>; + vin2-supply = <®_vcc_4v>; + vin3-supply = <®_vcc_4v>; + vin4-supply = <®_vcc_4v>; + vin5-supply = <®_vcc_4v>; + vin6-supply = <®_vcc_4v>; + aldoin-supply = <®_vcc_4v>; + dldoin1-supply = <&buck5>; + dldoin2-supply = <&buck5>; regulators { buck1 { @@ -124,14 +218,14 @@ regulator-always-on; }; - buck4 { + buck4_3v3: buck4 { regulator-min-microvolt = <500000>; regulator-max-microvolt = <3300000>; regulator-ramp-delay = <5000>; regulator-always-on; }; - buck5 { + buck5: buck5 { regulator-min-microvolt = <500000>; regulator-max-microvolt = <3450000>; regulator-ramp-delay = <5000>; @@ -207,8 +301,108 @@ }; }; +&pcie1_phy { + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_3_cfg>; + status = "okay"; +}; + +&pcie1_port { + phys = <&pcie1_phy>; + vpcie3v3-supply = <&pcie_vcc_3v3>; +}; + +&pcie1 { + vpcie3v3-supply = <&pcie_vcc_3v3>; + status = "okay"; +}; + +&pcie2_phy { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_4_cfg>; + status = "okay"; +}; + +&pcie2_port { + phys = <&pcie2_phy>; + vpcie3v3-supply = <&pcie_vcc_3v3>; +}; + +&pcie2 { + vpcie3v3-supply = <&pcie_vcc_3v3>; + status = "okay"; +}; + +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&qspi_cfg>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <26500000>; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <4>; + vcc-supply = <&buck4_3v3>; /* QSPI_VCC1833 */ + m25p,fast-read; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + bootinfo@0 { + reg = <0x0 0x10000>; + }; + private@10000 { + reg = <0x10000 0x10000>; + }; + fsbl@20000 { + reg = <0x20000 0x40000>; + }; + env@60000 { + reg = <0x60000 0x10000>; + }; + opensbi@70000 { + reg = <0x70000 0x30000>; + }; + uboot@a00000 { + reg = <0xa0000 0x760000>; + }; + }; + }; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_2_cfg>; status = "okay"; }; + +&usbphy2 { + status = "okay"; +}; + +&usb_dwc3 { + dr_mode = "host"; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + hub_2_0: hub@1 { + compatible = "usb2109,2817"; + reg = <0x1>; + vdd-supply = <&usb3_hub_5v>; + peer-hub = <&hub_3_0>; + reset-gpios = <&gpio K1_GPIO(124) GPIO_ACTIVE_LOW>; + }; + + hub_3_0: hub@2 { + compatible = "usb2109,817"; + reg = <0x2>; + vdd-supply = <&usb3_hub_5v>; + peer-hub = <&hub_2_0>; + reset-gpios = <&gpio K1_GPIO(124) GPIO_ACTIVE_LOW>; + }; +}; diff --git a/arch/riscv/boot/dts/spacemit/k1.dtsi b/arch/riscv/boot/dts/spacemit/k1.dtsi index 529ec68e9c23..f0bad6855c97 100644 --- a/arch/riscv/boot/dts/spacemit/k1.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1.dtsi @@ -359,6 +359,60 @@ #reset-cells = <1>; }; + usbphy2: phy@c0a30000 { + compatible = "spacemit,k1-usb2-phy"; + reg = <0x0 0xc0a30000 0x0 0x200>; + clocks = <&syscon_apmu CLK_USB30>; + #phy-cells = <0>; + status = "disabled"; + }; + + combo_phy: phy@c0b10000 { + compatible = "spacemit,k1-combo-phy"; + reg = <0x0 0xc0b10000 0x0 0x1000>; + clocks = <&vctcxo_24m>, + <&syscon_apmu CLK_PCIE0_DBI>, + <&syscon_apmu CLK_PCIE0_MASTER>, + <&syscon_apmu CLK_PCIE0_SLAVE>; + clock-names = "refclk", + "dbi", + "mstr", + "slv"; + resets = <&syscon_apmu RESET_PCIE0_GLOBAL>, + <&syscon_apmu RESET_PCIE0_DBI>, + <&syscon_apmu RESET_PCIE0_MASTER>, + <&syscon_apmu RESET_PCIE0_SLAVE>; + reset-names = "phy", + "dbi", + "mstr", + "slv"; + #phy-cells = <1>; + spacemit,apmu = <&syscon_apmu>; + status = "disabled"; + }; + + pcie1_phy: phy@c0c10000 { + compatible = "spacemit,k1-pcie-phy"; + reg = <0x0 0xc0c10000 0x0 0x1000>; + clocks = <&vctcxo_24m>; + clock-names = "refclk"; + resets = <&syscon_apmu RESET_PCIE1_GLOBAL>; + reset-names = "phy"; + #phy-cells = <0>; + status = "disabled"; + }; + + pcie2_phy: phy@c0d10000 { + compatible = "spacemit,k1-pcie-phy"; + reg = <0x0 0xc0d10000 0x0 0x1000>; + clocks = <&vctcxo_24m>; + clock-names = "refclk"; + resets = <&syscon_apmu RESET_PCIE2_GLOBAL>; + reset-names = "phy"; + #phy-cells = <0>; + status = "disabled"; + }; + i2c0: i2c@d4010800 { compatible = "spacemit,k1-i2c"; reg = <0x0 0xd4010800 0x0 0x38>; @@ -429,60 +483,6 @@ status = "disabled"; }; - usbphy2: phy@c0a30000 { - compatible = "spacemit,k1-usb2-phy"; - reg = <0x0 0xc0a30000 0x0 0x200>; - clocks = <&syscon_apmu CLK_USB30>; - #phy-cells = <0>; - status = "disabled"; - }; - - combo_phy: phy@c0b10000 { - compatible = "spacemit,k1-combo-phy"; - reg = <0x0 0xc0b10000 0x0 0x1000>; - clocks = <&vctcxo_24m>, - <&syscon_apmu CLK_PCIE0_DBI>, - <&syscon_apmu CLK_PCIE0_MASTER>, - <&syscon_apmu CLK_PCIE0_SLAVE>; - clock-names = "refclk", - "dbi", - "mstr", - "slv"; - resets = <&syscon_apmu RESET_PCIE0_GLOBAL>, - <&syscon_apmu RESET_PCIE0_DBI>, - <&syscon_apmu RESET_PCIE0_MASTER>, - <&syscon_apmu RESET_PCIE0_SLAVE>; - reset-names = "phy", - "dbi", - "mstr", - "slv"; - #phy-cells = <1>; - spacemit,apmu = <&syscon_apmu>; - status = "disabled"; - }; - - pcie1_phy: phy@c0c10000 { - compatible = "spacemit,k1-pcie-phy"; - reg = <0x0 0xc0c10000 0x0 0x1000>; - clocks = <&vctcxo_24m>; - clock-names = "refclk"; - resets = <&syscon_apmu RESET_PCIE1_GLOBAL>; - reset-names = "phy"; - #phy-cells = <0>; - status = "disabled"; - }; - - pcie2_phy: phy@c0d10000 { - compatible = "spacemit,k1-pcie-phy"; - reg = <0x0 0xc0d10000 0x0 0x1000>; - clocks = <&vctcxo_24m>; - clock-names = "refclk"; - resets = <&syscon_apmu RESET_PCIE2_GLOBAL>; - reset-names = "phy"; - #phy-cells = <0>; - status = "disabled"; - }; - syscon_apbc: system-controller@d4015000 { compatible = "spacemit,k1-syscon-apbc"; reg = <0x0 0xd4015000 0x0 0x1000>; @@ -1033,6 +1033,7 @@ #size-cells = <2>; dma-ranges = <0x0 0x00000000 0x0 0x00000000 0x0 0x80000000>, <0x0 0xb8000000 0x1 0x38000000 0x3 0x48000000>; + pcie0: pcie@ca000000 { device_type = "pci"; compatible = "spacemit,k1-pcie"; @@ -1044,6 +1045,7 @@ "atu", "config", "link"; + linux,pci-domain = <0>; #address-cells = <3>; #size-cells = <2>; ranges = <0x01000000 0x0 0x00000000 0x0 0x8f002000 0x0 0x00100000>, @@ -1087,6 +1089,7 @@ "atu", "config", "link"; + linux,pci-domain = <1>; #address-cells = <3>; #size-cells = <2>; ranges = <0x01000000 0x0 0x00000000 0x0 0x9f002000 0x0 0x00100000>, @@ -1130,6 +1133,7 @@ "atu", "config", "link"; + linux,pci-domain = <2>; #address-cells = <3>; #size-cells = <2>; ranges = <0x01000000 0x0 0x00000000 0x0 0xb7002000 0x0 0x00100000>, diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index b691304d4b74..4486dc1fe114 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -3,14 +3,17 @@ * Copyright (c) 2026 SpacemiT (Hangzhou) Technology Co. Ltd * Copyright (c) 2026 Guodong Xu */ +#include #include "k3.dtsi" +#include "k3-pinctrl.dtsi" / { model = "SpacemiT K3 Pico-ITX"; compatible = "spacemit,k3-pico-itx", "spacemit,k3"; aliases { + ethernet0 = ð0; serial0 = &uart0; }; @@ -22,8 +25,175 @@ device_type = "memory"; reg = <0x1 0x00000000 0x4 0x00000000>; }; + + reg_aux_vcc5v: regulator-aux-vcc5v { + compatible = "regulator-fixed"; + regulator-name = "AUX_VCC5V"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; +}; + +&i2c8 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c8_cfg>; + status = "okay"; + + p1@41 { + compatible = "spacemit,p1"; + reg = <0x41>; + interrupts = <64 IRQ_TYPE_LEVEL_HIGH>; + vin1-supply = <®_aux_vcc5v>; + vin2-supply = <®_aux_vcc5v>; + vin3-supply = <®_aux_vcc5v>; + vin4-supply = <®_aux_vcc5v>; + vin5-supply = <®_aux_vcc5v>; + vin6-supply = <®_aux_vcc5v>; + aldoin-supply = <®_aux_vcc5v>; + dldoin1-supply = <&buck4>; + dldoin2-supply = <&buck4>; + + regulators { + buck1: buck1 { + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck2: buck2 { + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck3: buck3 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck4: buck4 { + regulator-min-microvolt = <2100000>; + regulator-max-microvolt = <2100000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck5: buck5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + buck6: buck6 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <500000>; + regulator-ramp-delay = <5000>; + regulator-always-on; + }; + + aldo1: aldo1 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + aldo2: aldo2 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + aldo3: aldo3 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <3400000>; + }; + + aldo4: aldo4 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + dldo1: dldo1 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + }; + + dldo2: dldo2 { + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + regulator-always-on; + regulator-boot-on; + }; + + dldo3: dldo3 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + regulator-always-on; + regulator-boot-on; + }; + + dldo4: dldo4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + }; + + dldo5: dldo5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + dldo6: dldo6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + dldo7: dldo7 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; +}; + +ð0 { + pinctrl-names = "default"; + pinctrl-0 = <&gmac0_rgmii_0_cfg>, <&gmac0_phy_0_cfg>; + phy-mode = "rgmii-id"; + phy-handle = <&phy0>; + status = "okay"; + + mdio { + phy0: phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + reset-gpios = <&gpio 0 15 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <10000>; + }; + }; }; &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_0_cfg>; status = "okay"; }; diff --git a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi new file mode 100644 index 000000000000..23899d3f308a --- /dev/null +++ b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (c) 2026 Yixun Lan + */ + +#include + +#define K3_PADCONF(pin, func) (((pin) << 16) | (func)) + +/* Map GPIO pin to each bank's */ +#define K3_GPIO(x) (x / 32) (x % 32) + +&pinctrl { + gmac0_rgmii_0_cfg: gmac0-rgmii-0-cfg { + gmac0-rgmii-0-pins { + pinmux = , /* gmac0_rxdv */ + , /* gmac0_rx_d0 */ + , /* gmac0_rx_d1 */ + , /* gmac0_rx_clk */ + , /* gmac0_rx_d2 */ + , /* gmac0_rx_d3 */ + , /* gmac0_tx_d0 */ + , /* gmac0_tx_d1 */ + , /* gmac0_tx_clk */ + , /* gmac0_tx_d2 */ + , /* gmac0_tx_d3 */ + , /* gmac0_tx_en */ + , /* gmac0_mdc */ + ; /* gmac0_mdio */ + + bias-disable; + drive-strength = <25>; + power-source = <1800>; + }; + + }; + + gmac0_phy_0_cfg: gmac0-phy-0-cfg { + gmac0-phy-0-pins { + pinmux = ; /* gmac0_int */ + + bias-disable; + drive-strength = <25>; + power-source = <1800>; + }; + }; + + /omit-if-no-ref/ + i2c8_cfg: i2c8-cfg { + i2c8-pins { + pinmux = , /* i2c8 scl */ + ; /* i2c8 sda */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; + + /omit-if-no-ref/ + uart0_0_cfg: uart0-0-cfg { + uart0-0-pins { + pinmux = , /* uart0 tx */ + ; /* uart0 rx */ + + bias-pull-up = <0>; + drive-strength = <25>; + }; + }; +}; diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index b69cf81b5d55..815debd16409 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -4,6 +4,8 @@ * Copyright (c) 2026 Guodong Xu */ +#include +#include #include /dts-v1/; @@ -398,6 +400,36 @@ }; }; + clocks { + vctcxo_1m: clock-1m { + compatible = "fixed-clock"; + clock-frequency = <1000000>; + clock-output-names = "vctcxo_1m"; + #clock-cells = <0>; + }; + + vctcxo_24m: clock-24m { + compatible = "fixed-clock"; + clock-frequency = <24000000>; + clock-output-names = "vctcxo_24m"; + #clock-cells = <0>; + }; + + vctcxo_3m: clock-3m { + compatible = "fixed-clock"; + clock-frequency = <3000000>; + clock-output-names = "vctcxo_3m"; + #clock-cells = <0>; + }; + + osc_32k: clock-32k { + compatible = "fixed-clock"; + clock-frequency = <32000>; + clock-output-names = "osc_32k"; + #clock-cells = <0>; + }; + }; + soc: soc { compatible = "simple-bus"; interrupt-parent = <&saplic>; @@ -406,12 +438,211 @@ dma-noncoherent; ranges; + eth0: ethernet@cac80000 { + compatible = "spacemit,k3-dwmac", "snps,dwmac-5.40a"; + reg = <0x0 0xcac80000 0x0 0x2000>; + clocks = <&syscon_apmu CLK_APMU_EMAC0_BUS>, + <&syscon_apmu CLK_APMU_EMAC0_1588>, + <&syscon_apmu CLK_APMU_EMAC0_RGMII_TX>; + clock-names = "stmmaceth", "ptp_ref", "tx"; + interrupts = <131 IRQ_TYPE_LEVEL_HIGH>, + <276 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq", "eth_wake_irq"; + resets = <&syscon_apmu RESET_APMU_EMAC0>; + reset-names = "stmmaceth"; + rx-fifo-depth = <8192>; + tx-fifo-depth = <8192>; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <32>; + snps,aal; + snps,tso; + snps,txpbl = <8>; + snps,rxpbl = <8>; + snps,force_sf_dma_mode; + snps,axi-config = <&gmac0_axi_setup>; + spacemit,apmu = <&syscon_apmu 0x3e4 0x3e8>; + status = "disabled"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + gmac0_axi_setup: stmmac-axi-config { + snps,wr_osr_lmt = <0xf>; + snps,rd_osr_lmt = <0xf>; + /* max axi burst len is 256 */ + snps,blen = <256 128 64 32 16 0 0>; + }; + }; + + eth1: ethernet@cac82000 { + compatible = "spacemit,k3-dwmac", "snps,dwmac-5.40a"; + reg = <0x0 0xcac82000 0x0 0x2000>; + clocks = <&syscon_apmu CLK_APMU_EMAC1_BUS>, + <&syscon_apmu CLK_APMU_EMAC1_1588>, + <&syscon_apmu CLK_APMU_EMAC1_RGMII_TX>; + clock-names = "stmmaceth", "ptp_ref", "tx"; + interrupts = <133 IRQ_TYPE_LEVEL_HIGH>, + <277 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq", "eth_wake_irq"; + resets = <&syscon_apmu RESET_APMU_EMAC1>; + reset-names = "stmmaceth"; + rx-fifo-depth = <8192>; + tx-fifo-depth = <8192>; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <32>; + snps,aal; + snps,tso; + snps,txpbl = <8>; + snps,rxpbl = <8>; + snps,force_sf_dma_mode; + snps,axi-config = <&gmac1_axi_setup>; + spacemit,apmu = <&syscon_apmu 0x3ec 0x3f0>; + status = "disabled"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + gmac1_axi_setup: stmmac-axi-config { + snps,wr_osr_lmt = <0xf>; + snps,rd_osr_lmt = <0xf>; + /* max axi burst len is 256 */ + snps,blen = <256 128 64 32 16 0 0>; + }; + }; + + eth2: ethernet@cac8e000 { + compatible = "spacemit,k3-dwmac", "snps,dwmac-5.40a"; + reg = <0x0 0xcac8e000 0x0 0x2000>; + clocks = <&syscon_apmu CLK_APMU_EMAC2_BUS>, + <&syscon_apmu CLK_APMU_EMAC2_1588>, + <&syscon_apmu CLK_APMU_EMAC2_RGMII_TX>; + clock-names = "stmmaceth", "ptp_ref", "tx"; + interrupts = <130 IRQ_TYPE_LEVEL_HIGH>, + <278 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq", "eth_wake_irq"; + resets = <&syscon_apmu RESET_APMU_EMAC2>; + reset-names = "stmmaceth"; + rx-fifo-depth = <4096>; + tx-fifo-depth = <4096>; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <32>; + snps,aal; + snps,tso; + snps,txpbl = <8>; + snps,rxpbl = <8>; + snps,force_sf_dma_mode; + snps,axi-config = <&gmac2_axi_setup>; + spacemit,apmu = <&syscon_apmu 0x248 0x24c>; + status = "disabled"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + gmac2_axi_setup: stmmac-axi-config { + snps,wr_osr_lmt = <0xf>; + snps,rd_osr_lmt = <0xf>; + /* max axi burst len is 256 */ + snps,blen = <256 128 64 32 16 0 0>; + }; + }; + + i2c0: i2c@d4010800 { + compatible = "spacemit,k3-i2c", "spacemit,k1-i2c"; + reg = <0x0 0xd4010800 0x0 0x38>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <36 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&syscon_apbc CLK_APBC_TWSI0>, + <&syscon_apbc CLK_APBC_TWSI0_BUS>; + clock-names = "func", "bus"; + clock-frequency = <400000>; + resets = <&syscon_apbc RESET_APBC_TWSI0>; + status = "disabled"; + }; + + i2c1: i2c@d4011000 { + compatible = "spacemit,k3-i2c", "spacemit,k1-i2c"; + reg = <0x0 0xd4011000 0x0 0x38>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <37 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&syscon_apbc CLK_APBC_TWSI1>, + <&syscon_apbc CLK_APBC_TWSI1_BUS>; + clock-names = "func", "bus"; + clock-frequency = <400000>; + resets = <&syscon_apbc RESET_APBC_TWSI1>; + status = "disabled"; + }; + + i2c2: i2c@d4012000 { + compatible = "spacemit,k3-i2c", "spacemit,k1-i2c"; + reg = <0x0 0xd4012000 0x0 0x38>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <38 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&syscon_apbc CLK_APBC_TWSI2>, + <&syscon_apbc CLK_APBC_TWSI2_BUS>; + clock-names = "func", "bus"; + clock-frequency = <400000>; + resets = <&syscon_apbc RESET_APBC_TWSI2>; + status = "disabled"; + }; + + i2c4: i2c@d4012800 { + compatible = "spacemit,k3-i2c", "spacemit,k1-i2c"; + reg = <0x0 0xd4012800 0x0 0x38>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <40 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&syscon_apbc CLK_APBC_TWSI4>, + <&syscon_apbc CLK_APBC_TWSI4_BUS>; + clock-names = "func", "bus"; + clock-frequency = <400000>; + resets = <&syscon_apbc RESET_APBC_TWSI4>; + status = "disabled"; + }; + + i2c5: i2c@d4013800 { + compatible = "spacemit,k3-i2c", "spacemit,k1-i2c"; + reg = <0x0 0xd4013800 0x0 0x38>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <41 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&syscon_apbc CLK_APBC_TWSI5>, + <&syscon_apbc CLK_APBC_TWSI5_BUS>; + clock-names = "func", "bus"; + clock-frequency = <400000>; + resets = <&syscon_apbc RESET_APBC_TWSI5>; + status = "disabled"; + }; + + syscon_apbc: system-controller@d4015000 { + compatible = "spacemit,k3-syscon-apbc"; + reg = <0x0 0xd4015000 0x0 0x1000>; + clocks = <&osc_32k>, <&vctcxo_1m>, <&vctcxo_3m>, <&vctcxo_24m>; + clock-names = "osc", "vctcxo_1m", "vctcxo_3m", "vctcxo_24m"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + uart0: serial@d4017000 { compatible = "spacemit,k3-uart", "intel,xscale-uart"; reg = <0x0 0xd4017000 0x0 0x100>; reg-shift = <2>; reg-io-width = <4>; - clock-frequency = <14700000>; + clocks = <&syscon_apbc CLK_APBC_UART0>, + <&syscon_apbc CLK_APBC_UART0_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_APBC_UART0>; interrupts = <42 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; }; @@ -421,7 +652,10 @@ reg = <0x0 0xd4017100 0x0 0x100>; reg-shift = <2>; reg-io-width = <4>; - clock-frequency = <14700000>; + clocks = <&syscon_apbc CLK_APBC_UART2>, + <&syscon_apbc CLK_APBC_UART2_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_APBC_UART2>; interrupts = <44 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; }; @@ -431,7 +665,10 @@ reg = <0x0 0xd4017200 0x0 0x100>; reg-shift = <2>; reg-io-width = <4>; - clock-frequency = <14700000>; + clocks = <&syscon_apbc CLK_APBC_UART3>, + <&syscon_apbc CLK_APBC_UART3_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_APBC_UART3>; interrupts = <45 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; }; @@ -441,7 +678,10 @@ reg = <0x0 0xd4017300 0x0 0x100>; reg-shift = <2>; reg-io-width = <4>; - clock-frequency = <14700000>; + clocks = <&syscon_apbc CLK_APBC_UART4>, + <&syscon_apbc CLK_APBC_UART4_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_APBC_UART4>; interrupts = <46 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; }; @@ -451,7 +691,10 @@ reg = <0x0 0xd4017400 0x0 0x100>; reg-shift = <2>; reg-io-width = <4>; - clock-frequency = <14700000>; + clocks = <&syscon_apbc CLK_APBC_UART5>, + <&syscon_apbc CLK_APBC_UART5_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_APBC_UART5>; interrupts = <47 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; }; @@ -461,7 +704,10 @@ reg = <0x0 0xd4017500 0x0 0x100>; reg-shift = <2>; reg-io-width = <4>; - clock-frequency = <14700000>; + clocks = <&syscon_apbc CLK_APBC_UART6>, + <&syscon_apbc CLK_APBC_UART6_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_APBC_UART6>; interrupts = <48 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; }; @@ -471,7 +717,10 @@ reg = <0x0 0xd4017600 0x0 0x100>; reg-shift = <2>; reg-io-width = <4>; - clock-frequency = <14700000>; + clocks = <&syscon_apbc CLK_APBC_UART7>, + <&syscon_apbc CLK_APBC_UART7_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_APBC_UART7>; interrupts = <49 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; }; @@ -481,7 +730,10 @@ reg = <0x0 0xd4017700 0x0 0x100>; reg-shift = <2>; reg-io-width = <4>; - clock-frequency = <14700000>; + clocks = <&syscon_apbc CLK_APBC_UART8>, + <&syscon_apbc CLK_APBC_UART8_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_APBC_UART8>; interrupts = <50 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; }; @@ -491,21 +743,116 @@ reg = <0x0 0xd4017800 0x0 0x100>; reg-shift = <2>; reg-io-width = <4>; - clock-frequency = <14700000>; + clocks = <&syscon_apbc CLK_APBC_UART9>, + <&syscon_apbc CLK_APBC_UART9_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_APBC_UART9>; interrupts = <51 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; }; + i2c6: i2c@d4018800 { + compatible = "spacemit,k3-i2c", "spacemit,k1-i2c"; + reg = <0x0 0xd4018800 0x0 0x38>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <70 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&syscon_apbc CLK_APBC_TWSI6>, + <&syscon_apbc CLK_APBC_TWSI6_BUS>; + clock-names = "func", "bus"; + clock-frequency = <400000>; + resets = <&syscon_apbc RESET_APBC_TWSI6>; + status = "disabled"; + }; + + gpio: gpio@d4019000 { + compatible = "spacemit,k3-gpio"; + reg = <0x0 0xd4019000 0x0 0x100>; + clocks = <&syscon_apbc CLK_APBC_GPIO>, + <&syscon_apbc CLK_APBC_GPIO_BUS>; + clock-names = "core", "bus"; + gpio-controller; + #gpio-cells = <3>; + interrupts = <58 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + interrupt-controller; + #interrupt-cells = <3>; + gpio-ranges = <&pinctrl 0 0 0 32>, + <&pinctrl 1 0 32 32>, + <&pinctrl 2 0 64 32>, + <&pinctrl 3 0 96 32>; + }; + + i2c8: i2c@d401d800 { + compatible = "spacemit,k3-i2c", "spacemit,k1-i2c"; + reg = <0x0 0xd401d800 0x0 0x38>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <19 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&syscon_apbc CLK_APBC_TWSI8>, + <&syscon_apbc CLK_APBC_TWSI8_BUS>; + clock-names = "func", "bus"; + clock-frequency = <400000>; + resets = <&syscon_apbc RESET_APBC_TWSI8>; + status = "disabled"; + }; + + pinctrl: pinctrl@d401e000 { + compatible = "spacemit,k3-pinctrl"; + reg = <0x0 0xd401e000 0x0 0x1000>; + clocks = <&syscon_apbc CLK_APBC_AIB>, + <&syscon_apbc CLK_APBC_AIB_BUS>; + clock-names = "func", "bus"; + }; + uart10: serial@d401f000 { compatible = "spacemit,k3-uart", "intel,xscale-uart"; reg = <0x0 0xd401f000 0x0 0x100>; reg-shift = <2>; reg-io-width = <4>; - clock-frequency = <14700000>; + clocks = <&syscon_apbc CLK_APBC_UART10>, + <&syscon_apbc CLK_APBC_UART10_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_APBC_UART10>; interrupts = <281 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; }; + syscon_mpmu: system-controller@d4050000 { + compatible = "spacemit,k3-syscon-mpmu"; + reg = <0x0 0xd4050000 0x0 0x10000>; + clocks = <&osc_32k>, <&vctcxo_1m>, <&vctcxo_3m>, <&vctcxo_24m>; + clock-names = "osc", "vctcxo_1m", "vctcxo_3m", "vctcxo_24m"; + #clock-cells = <1>; + #power-domain-cells = <1>; + #reset-cells = <1>; + }; + + pll: clock-controller@d4090000 { + compatible = "spacemit,k3-pll"; + reg = <0x0 0xd4090000 0x0 0x10000>; + clocks = <&vctcxo_24m>; + spacemit,mpmu = <&syscon_mpmu>; + #clock-cells = <1>; + }; + + syscon_apmu: system-controller@d4282800 { + compatible = "spacemit,k3-syscon-apmu"; + reg = <0x0 0xd4282800 0x0 0x400>; + clocks = <&osc_32k>, <&vctcxo_1m>, <&vctcxo_3m>, <&vctcxo_24m>; + clock-names = "osc", "vctcxo_1m", "vctcxo_3m", "vctcxo_24m"; + #clock-cells = <1>; + #power-domain-cells = <1>; + #reset-cells = <1>; + }; + + syscon_dciu: system-controller@d8440000 { + compatible = "spacemit,k3-syscon-dciu"; + reg = <0x0 0xd8440000 0x0 0xc000>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + simsic: interrupt-controller@e0400000 { compatible = "spacemit,k3-imsics", "riscv,imsics"; reg = <0x0 0xe0400000 0x0 0x200000>; @@ -545,6 +892,8 @@ <&cpu7_intc 3>, <&cpu7_intc 7>; }; + /* sec_i2c3: 0xf0614000, not available from Linux */ + mimsic: interrupt-controller@f1000000 { compatible = "spacemit,k3-imsics", "riscv,imsics"; reg = <0x0 0xf1000000 0x0 0x10000>; diff --git a/arch/riscv/boot/dts/thead/th1520-beaglev-ahead.dts b/arch/riscv/boot/dts/thead/th1520-beaglev-ahead.dts index 21c33f165ba9..91f3f9b987bc 100644 --- a/arch/riscv/boot/dts/thead/th1520-beaglev-ahead.dts +++ b/arch/riscv/boot/dts/thead/th1520-beaglev-ahead.dts @@ -75,6 +75,17 @@ label = "led5"; }; }; + + hdmi-connector { + compatible = "hdmi-connector"; + type = "d"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; }; &osc { @@ -236,6 +247,20 @@ status = "okay"; }; +&dpu { + status = "okay"; +}; + +&hdmi { + status = "okay"; +}; + +&hdmi_out_port { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pins>; diff --git a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts index c58c2085ca92..7cb7d28683bc 100644 --- a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts +++ b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts @@ -29,6 +29,17 @@ stdout-path = "serial0:115200n8"; }; + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + thermal-zones { cpu-thermal { polling-delay = <1000>; @@ -121,6 +132,20 @@ }; }; +&dpu { + status = "okay"; +}; + +&hdmi { + status = "okay"; +}; + +&hdmi_out_port { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pins>; diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index bd5d33840884..5e91dc1d2b9b 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -585,6 +585,72 @@ #clock-cells = <1>; }; + hdmi: hdmi@ffef540000 { + compatible = "thead,th1520-dw-hdmi"; + reg = <0xff 0xef540000 0x0 0x40000>; + reg-io-width = <4>; + interrupts = <111 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk_vo CLK_HDMI_PCLK>, + <&clk_vo CLK_HDMI_SFR>, + <&clk_vo CLK_HDMI_CEC>, + <&clk_vo CLK_HDMI_PIXCLK>; + clock-names = "iahb", "isfr", "cec", "pix"; + resets = <&rst TH1520_RESET_ID_HDMI>, + <&rst TH1520_RESET_ID_HDMI_APB>; + reset-names = "main", "apb"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + hdmi_in: endpoint { + remote-endpoint = <&dpu_out_dp1>; + }; + }; + + hdmi_out_port: port@1 { + reg = <1>; + }; + }; + }; + + dpu: display@ffef600000 { + compatible = "thead,th1520-dc8200", "verisilicon,dc"; + reg = <0xff 0xef600000 0x0 0x100000>; + interrupts = <93 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk_vo CLK_DPU_CCLK>, + <&clk_vo CLK_DPU_ACLK>, + <&clk_vo CLK_DPU_HCLK>, + <&clk_vo CLK_DPU_PIXELCLK0>, + <&clk_vo CLK_DPU_PIXELCLK1>; + clock-names = "core", "axi", "ahb", "pix0", "pix1"; + resets = <&rst TH1520_RESET_ID_DPU_CORE>, + <&rst TH1520_RESET_ID_DPU_AXI>, + <&rst TH1520_RESET_ID_DPU_AHB>; + reset-names = "core", "axi", "ahb"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + dpu_port1: port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + dpu_out_dp1: endpoint@1 { + reg = <1>; + remote-endpoint = <&hdmi_in>; + }; + }; + }; + }; + dmac0: dma-controller@ffefc00000 { compatible = "snps,axi-dma-1.01a"; reg = <0xff 0xefc00000 0x0 0x1000>; @@ -753,6 +819,10 @@ reg-names = "common", "ts", "pd", "vm"; clocks = <&aonsys_clk>; #thermal-sensor-cells = <1>; + moortec,ts-coeff-g = <42740>; + moortec,ts-coeff-h = <220500>; + moortec,ts-coeff-j = <(-160)>; + moortec,ts-coeff-cal5 = <4094>; }; gpio@fffff52000 { diff --git a/arch/riscv/errata/mips/errata.c b/arch/riscv/errata/mips/errata.c index e984a8152208..2c3dc2259e93 100644 --- a/arch/riscv/errata/mips/errata.c +++ b/arch/riscv/errata/mips/errata.c @@ -57,7 +57,7 @@ void mips_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, } tmp = (1U << alt->patch_id); - if (cpu_req_errata && tmp) { + if (cpu_req_errata & tmp) { mutex_lock(&text_mutex); patch_text_nosync(ALT_OLD_PTR(alt), ALT_ALT_PTR(alt), alt->alt_len); diff --git a/arch/riscv/errata/thead/errata.c b/arch/riscv/errata/thead/errata.c index 0b942183f708..6f1c683f0ec0 100644 --- a/arch/riscv/errata/thead/errata.c +++ b/arch/riscv/errata/thead/errata.c @@ -153,7 +153,7 @@ static bool errata_probe_ghostwrite(unsigned int stage, * target-c9xx cores report arch_id and impid as 0 * * While ghostwrite may not affect all c9xx cores that implement - * xtheadvector, there is no futher granularity than c9xx. Assume + * xtheadvector, there is no further granularity than c9xx. Assume * vulnerable for this entire class of processors when xtheadvector is * enabled. */ diff --git a/arch/riscv/include/asm/asm-prototypes.h b/arch/riscv/include/asm/asm-prototypes.h index 41ec5cdec367..5b90ba5314ee 100644 --- a/arch/riscv/include/asm/asm-prototypes.h +++ b/arch/riscv/include/asm/asm-prototypes.h @@ -40,6 +40,7 @@ asmlinkage void riscv_v_context_nesting_end(struct pt_regs *regs); #define DECLARE_DO_ERROR_INFO(name) asmlinkage void name(struct pt_regs *regs) DECLARE_DO_ERROR_INFO(do_trap_unknown); +DECLARE_DO_ERROR_INFO(do_trap_hardware_error); DECLARE_DO_ERROR_INFO(do_trap_insn_misaligned); DECLARE_DO_ERROR_INFO(do_trap_insn_fault); DECLARE_DO_ERROR_INFO(do_trap_insn_illegal); diff --git a/arch/riscv/include/asm/atomic.h b/arch/riscv/include/asm/atomic.h index 3f33dc54f94b..616b8b332ac5 100644 --- a/arch/riscv/include/asm/atomic.h +++ b/arch/riscv/include/asm/atomic.h @@ -46,7 +46,7 @@ static __always_inline void arch_atomic64_set(atomic64_t *v, s64 i) #endif /* - * First, the atomic ops that have no ordering constraints and therefor don't + * First, the atomic ops that have no ordering constraints and therefore don't * have the AQ or RL bits set. These don't return anything, so there's only * one version to worry about. */ @@ -81,7 +81,7 @@ ATOMIC_OPS(xor, xor, i) /* * Atomic ops that have ordered, relaxed, acquire, and release variants. - * There's two flavors of these: the arithmatic ops have both fetch and return + * There's two flavors of these: the arithmetic ops have both fetch and return * versions, while the logical ops only have fetch versions. */ #define ATOMIC_FETCH_OP(op, asm_op, I, asm_type, c_type, prefix) \ diff --git a/arch/riscv/include/asm/elf.h b/arch/riscv/include/asm/elf.h index c7aea7886d22..aa4961b0e208 100644 --- a/arch/riscv/include/asm/elf.h +++ b/arch/riscv/include/asm/elf.h @@ -59,8 +59,8 @@ extern bool compat_elf_check_arch(Elf32_Ehdr *hdr); #endif /* - * Provides information on the availiable set of ISA extensions to userspace, - * via a bitmap that coorespends to each single-letter ISA extension. This is + * Provides information on the available set of ISA extensions to userspace, + * via a bitmap that corresponds to each single-letter ISA extension. This is * essentially defunct, but will remain for compatibility with userspace. */ #define ELF_HWCAP riscv_get_elf_hwcap() diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h index 595e2183173e..9c908432bc17 100644 --- a/arch/riscv/include/asm/kvm_gstage.h +++ b/arch/riscv/include/asm/kvm_gstage.h @@ -15,6 +15,7 @@ struct kvm_gstage { #define KVM_GSTAGE_FLAGS_LOCAL BIT(0) unsigned long vmid; pgd_t *pgd; + unsigned long pgd_levels; }; struct kvm_gstage_mapping { @@ -29,16 +30,22 @@ struct kvm_gstage_mapping { #define kvm_riscv_gstage_index_bits 10 #endif -extern unsigned long kvm_riscv_gstage_mode; -extern unsigned long kvm_riscv_gstage_pgd_levels; +extern unsigned long kvm_riscv_gstage_max_pgd_levels; #define kvm_riscv_gstage_pgd_xbits 2 #define kvm_riscv_gstage_pgd_size (1UL << (HGATP_PAGE_SHIFT + kvm_riscv_gstage_pgd_xbits)) -#define kvm_riscv_gstage_gpa_bits (HGATP_PAGE_SHIFT + \ - (kvm_riscv_gstage_pgd_levels * \ - kvm_riscv_gstage_index_bits) + \ - kvm_riscv_gstage_pgd_xbits) -#define kvm_riscv_gstage_gpa_size ((gpa_t)(1ULL << kvm_riscv_gstage_gpa_bits)) + +static inline unsigned long kvm_riscv_gstage_gpa_bits(unsigned long pgd_levels) +{ + return (HGATP_PAGE_SHIFT + + pgd_levels * kvm_riscv_gstage_index_bits + + kvm_riscv_gstage_pgd_xbits); +} + +static inline gpa_t kvm_riscv_gstage_gpa_size(unsigned long pgd_levels) +{ + return BIT_ULL(kvm_riscv_gstage_gpa_bits(pgd_levels)); +} bool kvm_riscv_gstage_get_leaf(struct kvm_gstage *gstage, gpa_t addr, pte_t **ptepp, u32 *ptep_level); @@ -53,6 +60,10 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage, bool page_rdonly, bool page_exec, struct kvm_gstage_mapping *out_map); +int kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage, + struct kvm_mmu_memory_cache *pcache, + gpa_t addr, u32 target_level, bool flush); + enum kvm_riscv_gstage_op { GSTAGE_OP_NOP = 0, /* Nothing */ GSTAGE_OP_CLEAR, /* Clear/Unmap */ @@ -69,4 +80,30 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end void kvm_riscv_gstage_mode_detect(void); +static inline unsigned long kvm_riscv_gstage_mode(unsigned long pgd_levels) +{ + switch (pgd_levels) { + case 2: + return HGATP_MODE_SV32X4; + case 3: + return HGATP_MODE_SV39X4; + case 4: + return HGATP_MODE_SV48X4; + case 5: + return HGATP_MODE_SV57X4; + default: + WARN_ON_ONCE(1); + return HGATP_MODE_OFF; + } +} + +static inline void kvm_riscv_gstage_init(struct kvm_gstage *gstage, struct kvm *kvm) +{ + gstage->kvm = kvm; + gstage->flags = 0; + gstage->vmid = READ_ONCE(kvm->arch.vmid.vmid); + gstage->pgd = kvm->arch.pgd; + gstage->pgd_levels = kvm->arch.pgd_levels; +} + #endif diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h index 24585304c02b..75b0a951c1bc 100644 --- a/arch/riscv/include/asm/kvm_host.h +++ b/arch/riscv/include/asm/kvm_host.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -47,18 +48,6 @@ #define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE -#define KVM_HEDELEG_DEFAULT (BIT(EXC_INST_MISALIGNED) | \ - BIT(EXC_INST_ILLEGAL) | \ - BIT(EXC_BREAKPOINT) | \ - BIT(EXC_SYSCALL) | \ - BIT(EXC_INST_PAGE_FAULT) | \ - BIT(EXC_LOAD_PAGE_FAULT) | \ - BIT(EXC_STORE_PAGE_FAULT)) - -#define KVM_HIDELEG_DEFAULT (BIT(IRQ_VS_SOFT) | \ - BIT(IRQ_VS_TIMER) | \ - BIT(IRQ_VS_EXT)) - #define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \ KVM_DIRTY_LOG_INITIALLY_SET) @@ -94,6 +83,7 @@ struct kvm_arch { /* G-stage page table */ pgd_t *pgd; phys_addr_t pgd_phys; + unsigned long pgd_levels; /* Guest Timer */ struct kvm_guest_timer timer; @@ -167,12 +157,6 @@ struct kvm_vcpu_csr { unsigned long senvcfg; }; -struct kvm_vcpu_config { - u64 henvcfg; - u64 hstateen0; - unsigned long hedeleg; -}; - struct kvm_vcpu_smstateen_csr { unsigned long sstateen0; }; @@ -273,6 +257,9 @@ struct kvm_vcpu_arch { /* 'static' configurations which are set only once */ struct kvm_vcpu_config cfg; + /* Indicates modified guest CSRs */ + bool csr_dirty; + /* SBI steal-time accounting */ struct { gpa_t shmem; diff --git a/arch/riscv/include/asm/kvm_isa.h b/arch/riscv/include/asm/kvm_isa.h new file mode 100644 index 000000000000..bc4b956d5f17 --- /dev/null +++ b/arch/riscv/include/asm/kvm_isa.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2026 Qualcomm Technologies, Inc. + */ + +#ifndef __KVM_RISCV_ISA_H +#define __KVM_RISCV_ISA_H + +#include + +unsigned long kvm_riscv_base2isa_ext(unsigned long base_ext); + +int __kvm_riscv_isa_check_host(unsigned long ext, unsigned long *base_ext); +#define kvm_riscv_isa_check_host(ext) \ + __kvm_riscv_isa_check_host(KVM_RISCV_ISA_EXT_##ext, NULL) + +bool kvm_riscv_isa_enable_allowed(unsigned long ext); +bool kvm_riscv_isa_disable_allowed(unsigned long ext); + +#endif diff --git a/arch/riscv/include/asm/kvm_vcpu_config.h b/arch/riscv/include/asm/kvm_vcpu_config.h new file mode 100644 index 000000000000..fcc15a0296b3 --- /dev/null +++ b/arch/riscv/include/asm/kvm_vcpu_config.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2026 Qualcomm Technologies, Inc. + */ + +#ifndef __KVM_VCPU_RISCV_CONFIG_H +#define __KVM_VCPU_RISCV_CONFIG_H + +#include + +struct kvm_vcpu; + +struct kvm_vcpu_config { + u64 henvcfg; + u64 hstateen0; + unsigned long hedeleg; + unsigned long hideleg; +}; + +void kvm_riscv_vcpu_config_init(struct kvm_vcpu *vcpu); +void kvm_riscv_vcpu_config_guest_debug(struct kvm_vcpu *vcpu); +void kvm_riscv_vcpu_config_ran_once(struct kvm_vcpu *vcpu); +void kvm_riscv_vcpu_config_load(struct kvm_vcpu *vcpu); + +#endif diff --git a/arch/riscv/include/asm/page.h b/arch/riscv/include/asm/page.h index c78017061b17..709a36fb4323 100644 --- a/arch/riscv/include/asm/page.h +++ b/arch/riscv/include/asm/page.h @@ -29,11 +29,7 @@ #define PAGE_OFFSET_L5 _AC(0xff60000000000000, UL) #define PAGE_OFFSET_L4 _AC(0xffffaf8000000000, UL) #define PAGE_OFFSET_L3 _AC(0xffffffd600000000, UL) -#ifdef CONFIG_XIP_KERNEL -#define PAGE_OFFSET PAGE_OFFSET_L3 -#else #define PAGE_OFFSET kernel_map.page_offset -#endif /* CONFIG_XIP_KERNEL */ #else #define PAGE_OFFSET _AC(0xc0000000, UL) #endif /* CONFIG_64BIT */ @@ -104,15 +100,8 @@ struct kernel_mapping { /* Offset between linear mapping virtual address and kernel load address */ unsigned long va_pa_offset; /* Offset between kernel mapping virtual address and kernel load address */ -#ifdef CONFIG_XIP_KERNEL - unsigned long va_kernel_xip_text_pa_offset; - unsigned long va_kernel_xip_data_pa_offset; - uintptr_t xiprom; - uintptr_t xiprom_sz; -#else unsigned long page_offset; unsigned long va_kernel_pa_offset; -#endif }; extern struct kernel_mapping kernel_map; @@ -131,16 +120,7 @@ extern unsigned long vmemmap_start_pfn; void *linear_mapping_pa_to_va(unsigned long x); #endif -#ifdef CONFIG_XIP_KERNEL -#define kernel_mapping_pa_to_va(y) ({ \ - unsigned long _y = (unsigned long)(y); \ - (_y < phys_ram_base) ? \ - (void *)(_y + kernel_map.va_kernel_xip_text_pa_offset) : \ - (void *)(_y + kernel_map.va_kernel_xip_data_pa_offset); \ - }) -#else #define kernel_mapping_pa_to_va(y) ((void *)((unsigned long)(y) + kernel_map.va_kernel_pa_offset)) -#endif #define __pa_to_va_nodebug(x) linear_mapping_pa_to_va(x) @@ -150,16 +130,7 @@ void *linear_mapping_pa_to_va(unsigned long x); phys_addr_t linear_mapping_va_to_pa(unsigned long x); #endif -#ifdef CONFIG_XIP_KERNEL -#define kernel_mapping_va_to_pa(y) ({ \ - unsigned long _y = (unsigned long)(y); \ - (_y < kernel_map.virt_addr + kernel_map.xiprom_sz) ? \ - (_y - kernel_map.va_kernel_xip_text_pa_offset) : \ - (_y - kernel_map.va_kernel_xip_data_pa_offset); \ - }) -#else #define kernel_mapping_va_to_pa(y) ((unsigned long)(y) - kernel_map.va_kernel_pa_offset) -#endif #define __va_to_pa_nodebug(x) ({ \ unsigned long _x = x; \ @@ -190,7 +161,10 @@ extern phys_addr_t __phys_addr_symbol(unsigned long x); #define sym_to_pfn(x) __phys_to_pfn(__pa_symbol(x)) -unsigned long kaslr_offset(void); +static inline unsigned long kaslr_offset(void) +{ + return kernel_map.virt_offset; +} static __always_inline void *pfn_to_kaddr(unsigned long pfn) { diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index a6e0eaba2653..a1a7c6520a09 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -134,21 +134,6 @@ #include -#ifdef CONFIG_XIP_KERNEL -#define XIP_FIXUP(addr) ({ \ - extern char _sdata[], _start[], _end[]; \ - uintptr_t __rom_start_data = CONFIG_XIP_PHYS_ADDR \ - + (uintptr_t)&_sdata - (uintptr_t)&_start; \ - uintptr_t __rom_end_data = CONFIG_XIP_PHYS_ADDR \ - + (uintptr_t)&_end - (uintptr_t)&_start; \ - uintptr_t __a = (uintptr_t)(addr); \ - (__a >= __rom_start_data && __a < __rom_end_data) ? \ - __a - __rom_start_data + CONFIG_PHYS_RAM_BASE : __a; \ - }) -#else -#define XIP_FIXUP(addr) (addr) -#endif /* CONFIG_XIP_KERNEL */ - struct pt_alloc_ops { pte_t *(*get_pte_virt)(phys_addr_t pa); phys_addr_t (*alloc_pte)(uintptr_t va); @@ -984,17 +969,17 @@ static inline void set_pud_at(struct mm_struct *mm, unsigned long addr, } #ifdef CONFIG_PAGE_TABLE_CHECK -static inline bool pte_user_accessible_page(pte_t pte, unsigned long addr) +static inline bool pte_user_accessible_page(struct mm_struct *mm, unsigned long addr, pte_t pte) { return pte_present(pte) && pte_user(pte); } -static inline bool pmd_user_accessible_page(pmd_t pmd, unsigned long addr) +static inline bool pmd_user_accessible_page(struct mm_struct *mm, unsigned long addr, pmd_t pmd) { return pmd_leaf(pmd) && pmd_user(pmd); } -static inline bool pud_user_accessible_page(pud_t pud, unsigned long addr) +static inline bool pud_user_accessible_page(struct mm_struct *mm, unsigned long addr, pud_t pud) { return pud_leaf(pud) && pud_user(pud); } @@ -1272,13 +1257,8 @@ static inline pte_t pte_swp_clear_exclusive(pte_t pte) extern char _start[]; extern void *_dtb_early_va; extern uintptr_t _dtb_early_pa; -#if defined(CONFIG_XIP_KERNEL) && defined(CONFIG_MMU) -#define dtb_early_va (*(void **)XIP_FIXUP(&_dtb_early_va)) -#define dtb_early_pa (*(uintptr_t *)XIP_FIXUP(&_dtb_early_pa)) -#else #define dtb_early_va _dtb_early_va #define dtb_early_pa _dtb_early_pa -#endif /* CONFIG_XIP_KERNEL */ extern u64 satp_mode; void paging_init(void); diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h index 4c3dd94d0f63..812517b2cec1 100644 --- a/arch/riscv/include/asm/processor.h +++ b/arch/riscv/include/asm/processor.h @@ -86,7 +86,7 @@ struct pt_regs; * preempt_v. All preempt_v context should be dropped in such case because * V-regs are caller-saved. Only sstatus.VS=ON is persisted across a * schedule() call. - * - bit 30: The in-kernel preempt_v context is saved, and requries to be + * - bit 30: The in-kernel preempt_v context is saved, and is required to be * restored when returning to the context that owns the preempt_v. * - bit 31: The in-kernel preempt_v context is dirty, as signaled by the * trap entry code. Any context switches out-of current task need to save diff --git a/arch/riscv/include/asm/scs.h b/arch/riscv/include/asm/scs.h index ab7714aa93bd..023a412fe38d 100644 --- a/arch/riscv/include/asm/scs.h +++ b/arch/riscv/include/asm/scs.h @@ -10,7 +10,6 @@ /* Load init_shadow_call_stack to gp. */ .macro scs_load_init_stack la gp, init_shadow_call_stack - XIP_FIXUP_OFFSET gp .endm /* Load the per-CPU IRQ shadow call stack to gp. */ diff --git a/arch/riscv/include/asm/set_memory.h b/arch/riscv/include/asm/set_memory.h index 87389e93325a..ef59e1716a2c 100644 --- a/arch/riscv/include/asm/set_memory.h +++ b/arch/riscv/include/asm/set_memory.h @@ -47,7 +47,7 @@ bool kernel_page_present(struct page *page); #endif /* __ASSEMBLER__ */ -#if defined(CONFIG_STRICT_KERNEL_RWX) || defined(CONFIG_XIP_KERNEL) +#if defined(CONFIG_STRICT_KERNEL_RWX) #ifdef CONFIG_64BIT #define SECTION_ALIGN (1 << 21) #else diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h index 7ac80e9f2288..0ecc67641b09 100644 --- a/arch/riscv/include/asm/smp.h +++ b/arch/riscv/include/asm/smp.h @@ -105,7 +105,7 @@ static inline void riscv_ipi_set_virq_range(int virq, int nr) #endif /* CONFIG_SMP */ -#if defined(CONFIG_HOTPLUG_CPU) && (CONFIG_SMP) +#if defined(CONFIG_HOTPLUG_CPU) bool cpu_has_hotplug(unsigned int cpu); #else static inline bool cpu_has_hotplug(unsigned int cpu) diff --git a/arch/riscv/include/asm/string.h b/arch/riscv/include/asm/string.h index 5ba77f60bf0b..764ffe8f6479 100644 --- a/arch/riscv/include/asm/string.h +++ b/arch/riscv/include/asm/string.h @@ -28,6 +28,15 @@ extern asmlinkage __kernel_size_t strlen(const char *); #define __HAVE_ARCH_STRNCMP extern asmlinkage int strncmp(const char *cs, const char *ct, size_t count); + +#define __HAVE_ARCH_STRNLEN +extern asmlinkage __kernel_size_t strnlen(const char *, size_t); + +#define __HAVE_ARCH_STRCHR +extern asmlinkage char *strchr(const char *, int); + +#define __HAVE_ARCH_STRRCHR +extern asmlinkage char *strrchr(const char *, int); #endif /* For those files which don't want to check by kasan. */ diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h index 36918c9200c9..55019fdfa9ec 100644 --- a/arch/riscv/include/asm/thread_info.h +++ b/arch/riscv/include/asm/thread_info.h @@ -120,7 +120,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); #include #define TIF_32BIT 16 /* compat-mode 32bit process */ -#define TIF_RISCV_V_DEFER_RESTORE 17 /* restore Vector before returing to user */ +#define TIF_RISCV_V_DEFER_RESTORE 17 /* restore Vector before returning to user */ #define _TIF_RISCV_V_DEFER_RESTORE BIT(TIF_RISCV_V_DEFER_RESTORE) diff --git a/arch/riscv/include/asm/xip_fixup.h b/arch/riscv/include/asm/xip_fixup.h deleted file mode 100644 index f3d56299bc22..000000000000 --- a/arch/riscv/include/asm/xip_fixup.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * XIP fixup macros, only useful in assembly. - */ -#ifndef _ASM_RISCV_XIP_FIXUP_H -#define _ASM_RISCV_XIP_FIXUP_H - -#include - -#ifdef CONFIG_XIP_KERNEL -.macro XIP_FIXUP_OFFSET reg - /* Fix-up address in Flash into address in RAM early during boot before - * MMU is up. Because generated code "thinks" data is in Flash, but it - * is actually in RAM (actually data is also in Flash, but Flash is - * read-only, thus we need to use the data residing in RAM). - * - * The start of data in Flash is _sdata and the start of data in RAM is - * CONFIG_PHYS_RAM_BASE. So this fix-up essentially does this: - * reg += CONFIG_PHYS_RAM_BASE - _start - */ - li t0, CONFIG_PHYS_RAM_BASE - add \reg, \reg, t0 - la t0, _sdata - sub \reg, \reg, t0 -.endm -.macro XIP_FIXUP_FLASH_OFFSET reg - /* In linker script, at the transition from read-only section to - * writable section, the VMA is increased while LMA remains the same. - * (See in linker script how _sdata, __data_loc and LOAD_OFFSET is - * changed) - * - * Consequently, early during boot before MMU is up, the generated code - * reads the "writable" section at wrong addresses, because VMA is used - * by compiler to generate code, but the data is located in Flash using - * LMA. - */ - la t0, _sdata - sub \reg, \reg, t0 - la t0, __data_loc - add \reg, \reg, t0 -.endm -#else -.macro XIP_FIXUP_OFFSET reg -.endm -.macro XIP_FIXUP_FLASH_OFFSET reg -.endm -#endif /* CONFIG_XIP_KERNEL */ - -#endif diff --git a/arch/riscv/include/asm/xor.h b/arch/riscv/include/asm/xor.h deleted file mode 100644 index 96011861e46b..000000000000 --- a/arch/riscv/include/asm/xor.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2021 SiFive - */ - -#include -#include -#ifdef CONFIG_RISCV_ISA_V -#include -#include -#include - -static void xor_vector_2(unsigned long bytes, unsigned long *__restrict p1, - const unsigned long *__restrict p2) -{ - kernel_vector_begin(); - xor_regs_2_(bytes, p1, p2); - kernel_vector_end(); -} - -static void xor_vector_3(unsigned long bytes, unsigned long *__restrict p1, - const unsigned long *__restrict p2, - const unsigned long *__restrict p3) -{ - kernel_vector_begin(); - xor_regs_3_(bytes, p1, p2, p3); - kernel_vector_end(); -} - -static void xor_vector_4(unsigned long bytes, unsigned long *__restrict p1, - const unsigned long *__restrict p2, - const unsigned long *__restrict p3, - const unsigned long *__restrict p4) -{ - kernel_vector_begin(); - xor_regs_4_(bytes, p1, p2, p3, p4); - kernel_vector_end(); -} - -static void xor_vector_5(unsigned long bytes, unsigned long *__restrict p1, - const unsigned long *__restrict p2, - const unsigned long *__restrict p3, - const unsigned long *__restrict p4, - const unsigned long *__restrict p5) -{ - kernel_vector_begin(); - xor_regs_5_(bytes, p1, p2, p3, p4, p5); - kernel_vector_end(); -} - -static struct xor_block_template xor_block_rvv = { - .name = "rvv", - .do_2 = xor_vector_2, - .do_3 = xor_vector_3, - .do_4 = xor_vector_4, - .do_5 = xor_vector_5 -}; - -#undef XOR_TRY_TEMPLATES -#define XOR_TRY_TEMPLATES \ - do { \ - xor_speed(&xor_block_8regs); \ - xor_speed(&xor_block_32regs); \ - if (has_vector()) { \ - xor_speed(&xor_block_rvv);\ - } \ - } while (0) -#endif diff --git a/arch/riscv/include/uapi/asm/kvm.h b/arch/riscv/include/uapi/asm/kvm.h index 6a89c1d00a72..504e73305343 100644 --- a/arch/riscv/include/uapi/asm/kvm.h +++ b/arch/riscv/include/uapi/asm/kvm.h @@ -110,6 +110,10 @@ struct kvm_riscv_timer { __u64 state; }; +/* Possible states for kvm_riscv_timer */ +#define KVM_RISCV_TIMER_STATE_OFF 0 +#define KVM_RISCV_TIMER_STATE_ON 1 + /* * ISA extension IDs specific to KVM. This is not the same as the host ISA * extension IDs as that is internal to the host and should not be exposed @@ -238,10 +242,6 @@ struct kvm_riscv_sbi_fwft { struct kvm_riscv_sbi_fwft_feature pointer_masking; }; -/* Possible states for kvm_riscv_timer */ -#define KVM_RISCV_TIMER_STATE_OFF 0 -#define KVM_RISCV_TIMER_STATE_ON 1 - /* If you need to interpret the index values, here is the key: */ #define KVM_REG_RISCV_TYPE_MASK 0x00000000FF000000 #define KVM_REG_RISCV_TYPE_SHIFT 24 diff --git a/arch/riscv/include/uapi/asm/setup.h b/arch/riscv/include/uapi/asm/setup.h index 66b13a522880..eb4f0209c696 100644 --- a/arch/riscv/include/uapi/asm/setup.h +++ b/arch/riscv/include/uapi/asm/setup.h @@ -3,6 +3,6 @@ #ifndef _UAPI_ASM_RISCV_SETUP_H #define _UAPI_ASM_RISCV_SETUP_H -#define COMMAND_LINE_SIZE 1024 +#define COMMAND_LINE_SIZE 2048 #endif /* _UAPI_ASM_RISCV_SETUP_H */ diff --git a/arch/riscv/kernel/acpi.c b/arch/riscv/kernel/acpi.c index 322ea92aa39f..068e0b404b6f 100644 --- a/arch/riscv/kernel/acpi.c +++ b/arch/riscv/kernel/acpi.c @@ -69,7 +69,7 @@ static int __init acpi_fadt_sanity_check(void) /* * FADT is required on riscv; retrieve it to check its presence - * and carry out revision and ACPI HW reduced compliancy tests + * and carry out revision and ACPI HW reduced compliance tests */ status = acpi_get_table(ACPI_SIG_FADT, 0, &table); if (ACPI_FAILURE(status)) { @@ -85,12 +85,12 @@ static int __init acpi_fadt_sanity_check(void) * The revision in the table header is the FADT's Major revision. The * FADT also has a minor revision, which is stored in the FADT itself. * - * TODO: Currently, we check for 6.5 as the minimum version to check - * for HW_REDUCED flag. However, once RISC-V updates are released in - * the ACPI spec, we need to update this check for exact minor revision + * ACPI 6.6 is required for RISC-V as it introduces RISC-V specific + * tables such as RHCT (RISC-V Hart Capabilities Table) and RIMT + * (RISC-V I/O Mapping Table). */ - if (table->revision < 6 || (table->revision == 6 && fadt->minor_revision < 5)) - pr_err(FW_BUG "Unsupported FADT revision %d.%d, should be 6.5+\n", + if (table->revision < 6 || (table->revision == 6 && fadt->minor_revision < 6)) + pr_err(FW_BUG "Unsupported FADT revision %d.%d, should be 6.6+\n", table->revision, fadt->minor_revision); if (!(fadt->flags & ACPI_FADT_HW_REDUCED)) { diff --git a/arch/riscv/kernel/compat_signal.c b/arch/riscv/kernel/compat_signal.c index 6ec4e34255a9..cf3eb33a11e4 100644 --- a/arch/riscv/kernel/compat_signal.c +++ b/arch/riscv/kernel/compat_signal.c @@ -107,6 +107,8 @@ static long compat_restore_sigcontext(struct pt_regs *regs, /* sc_regs is structured the same as the start of pt_regs */ err = __copy_from_user(&cregs, &sc->sc_regs, sizeof(sc->sc_regs)); + if (unlikely(err)) + return err; cregs_to_regs(&cregs, regs); diff --git a/arch/riscv/kernel/copy-unaligned.S b/arch/riscv/kernel/copy-unaligned.S index 2b3d9398c113..90f3549621f7 100644 --- a/arch/riscv/kernel/copy-unaligned.S +++ b/arch/riscv/kernel/copy-unaligned.S @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (C) 2023 Rivos Inc. */ +#include #include #include @@ -9,7 +10,7 @@ /* void __riscv_copy_words_unaligned(void *, const void *, size_t) */ /* Performs a memcpy without aligning buffers, using word loads and stores. */ /* Note: The size is truncated to a multiple of 8 * SZREG */ -SYM_FUNC_START(__riscv_copy_words_unaligned) +SYM_TYPED_FUNC_START(__riscv_copy_words_unaligned) andi a4, a2, ~((8*SZREG)-1) beqz a4, 2f add a3, a1, a4 @@ -41,7 +42,7 @@ SYM_FUNC_END(__riscv_copy_words_unaligned) /* void __riscv_copy_bytes_unaligned(void *, const void *, size_t) */ /* Performs a memcpy without aligning buffers, using only byte accesses. */ /* Note: The size is truncated to a multiple of 8 */ -SYM_FUNC_START(__riscv_copy_bytes_unaligned) +SYM_TYPED_FUNC_START(__riscv_copy_bytes_unaligned) andi a4, a2, ~(8-1) beqz a4, 2f add a3, a1, a4 diff --git a/arch/riscv/kernel/cpu-hotplug.c b/arch/riscv/kernel/cpu-hotplug.c index 3f50d3dd76c6..a0ee426f6d93 100644 --- a/arch/riscv/kernel/cpu-hotplug.c +++ b/arch/riscv/kernel/cpu-hotplug.c @@ -43,7 +43,6 @@ int __cpu_disable(void) return 0; } -#ifdef CONFIG_HOTPLUG_CPU /* * Called on the thread which is asking for a CPU to be shutdown, if the * CPU reported dead to the hotplug core. @@ -75,4 +74,3 @@ void __noreturn arch_cpu_idle_dead(void) /* It should never reach here */ BUG(); } -#endif diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index 1734f9a4c2fd..f46aa5602d74 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -896,10 +896,8 @@ static void __init riscv_fill_hwcap_from_isa_string(unsigned long *isa2hwcap) * CPU cores with the ratified spec will contain non-zero * marchid. */ - if (acpi_disabled && boot_vendorid == THEAD_VENDOR_ID && boot_archid == 0x0) { - this_hwcap &= ~isa2hwcap[RISCV_ISA_EXT_v]; + if (acpi_disabled && boot_vendorid == THEAD_VENDOR_ID && boot_archid == 0x0) clear_bit(RISCV_ISA_EXT_v, source_isa); - } riscv_resolve_isa(source_isa, isainfo->isa, &this_hwcap, isa2hwcap); @@ -1104,16 +1102,16 @@ early_param("riscv_isa_fallback", riscv_isa_fallback_setup); void __init riscv_fill_hwcap(void) { char print_str[NUM_ALPHA_EXTS + 1]; - unsigned long isa2hwcap[26] = {0}; + unsigned long isa2hwcap[RISCV_ISA_EXT_BASE] = {0}; int i, j; - isa2hwcap['i' - 'a'] = COMPAT_HWCAP_ISA_I; - isa2hwcap['m' - 'a'] = COMPAT_HWCAP_ISA_M; - isa2hwcap['a' - 'a'] = COMPAT_HWCAP_ISA_A; - isa2hwcap['f' - 'a'] = COMPAT_HWCAP_ISA_F; - isa2hwcap['d' - 'a'] = COMPAT_HWCAP_ISA_D; - isa2hwcap['c' - 'a'] = COMPAT_HWCAP_ISA_C; - isa2hwcap['v' - 'a'] = COMPAT_HWCAP_ISA_V; + isa2hwcap[RISCV_ISA_EXT_i] = COMPAT_HWCAP_ISA_I; + isa2hwcap[RISCV_ISA_EXT_m] = COMPAT_HWCAP_ISA_M; + isa2hwcap[RISCV_ISA_EXT_a] = COMPAT_HWCAP_ISA_A; + isa2hwcap[RISCV_ISA_EXT_f] = COMPAT_HWCAP_ISA_F; + isa2hwcap[RISCV_ISA_EXT_d] = COMPAT_HWCAP_ISA_D; + isa2hwcap[RISCV_ISA_EXT_c] = COMPAT_HWCAP_ISA_C; + isa2hwcap[RISCV_ISA_EXT_v] = COMPAT_HWCAP_ISA_V; if (!acpi_disabled) { riscv_fill_hwcap_from_isa_string(isa2hwcap); diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 60eb221296a6..d011fb51c59a 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -498,6 +498,7 @@ SYM_DATA_START_LOCAL(excp_vect_table) RISCV_PTR do_trap_unknown /* cause=16 */ RISCV_PTR do_trap_unknown /* cause=17 */ RISCV_PTR do_trap_software_check /* cause=18 is sw check exception */ + RISCV_PTR do_trap_hardware_error /* hardware error (19) */ SYM_DATA_END_LABEL(excp_vect_table, SYM_L_LOCAL, excp_vect_table_end) #ifndef CONFIG_MMU diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c index 8d18d6727f0f..b430edfb83f4 100644 --- a/arch/riscv/kernel/ftrace.c +++ b/arch/riscv/kernel/ftrace.c @@ -148,7 +148,7 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long ad /* * This is called early on, and isn't wrapped by - * ftrace_arch_code_modify_{prepare,post_process}() and therefor doesn't hold + * ftrace_arch_code_modify_{prepare,post_process}() and therefore doesn't hold * text_mutex, which triggers a lockdep failure. SMP isn't running so we could * just directly poke the text, but it's simpler to just take the lock * ourselves. diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S index 9c99c5ad6fe8..f6a8ca49e627 100644 --- a/arch/riscv/kernel/head.S +++ b/arch/riscv/kernel/head.S @@ -14,7 +14,6 @@ #include #include #include -#include #include #include "efi-header.S" @@ -75,13 +74,12 @@ pe_head_start: relocate_enable_mmu: /* Relocate return address */ la a1, kernel_map - XIP_FIXUP_OFFSET a1 REG_L a1, KERNEL_MAP_VIRT_ADDR(a1) la a2, _start sub a1, a1, a2 add ra, ra, a1 - /* Point stvec to virtual address of intruction after satp write */ + /* Point stvec to virtual address of instruction after satp write */ la a2, 1f add a2, a2, a1 csrw CSR_TVEC, a2 @@ -89,7 +87,6 @@ relocate_enable_mmu: /* Compute satp for kernel page tables, but don't load it yet */ srl a2, a0, PAGE_SHIFT la a1, satp_mode - XIP_FIXUP_OFFSET a1 REG_L a1, 0(a1) or a2, a2, a1 @@ -100,7 +97,6 @@ relocate_enable_mmu: * to ensure the new translations are in use. */ la a0, trampoline_pg_dir - XIP_FIXUP_OFFSET a0 srl a0, a0, PAGE_SHIFT or a0, a0, a1 sfence.vma @@ -154,11 +150,9 @@ secondary_start_sbi: /* a0 contains the hartid & a1 contains boot data */ li a2, SBI_HART_BOOT_TASK_PTR_OFFSET - XIP_FIXUP_OFFSET a2 add a2, a2, a1 REG_L tp, (a2) li a3, SBI_HART_BOOT_STACK_PTR_OFFSET - XIP_FIXUP_OFFSET a3 add a3, a3, a1 REG_L sp, (a3) @@ -167,7 +161,6 @@ secondary_start_sbi: #ifdef CONFIG_MMU /* Enable virtual memory and relocate to virtual address */ la a0, swapper_pg_dir - XIP_FIXUP_OFFSET a0 call relocate_enable_mmu #endif call .Lsetup_trap_vector @@ -269,40 +262,13 @@ SYM_CODE_START(_start_kernel) .Lgood_cores: /* The lottery system is only required for spinwait booting method */ -#ifndef CONFIG_XIP_KERNEL /* Pick one hart to run the main boot sequence */ la a3, hart_lottery li a2, 1 amoadd.w a3, a2, (a3) bnez a3, .Lsecondary_start - -#else - /* hart_lottery in flash contains a magic number */ - la a3, hart_lottery - mv a2, a3 - XIP_FIXUP_OFFSET a2 - XIP_FIXUP_FLASH_OFFSET a3 - lw t1, (a3) - amoswap.w t0, t1, (a2) - /* first time here if hart_lottery in RAM is not set */ - beq t0, t1, .Lsecondary_start - -#endif /* CONFIG_XIP */ #endif /* CONFIG_RISCV_BOOT_SPINWAIT */ -#ifdef CONFIG_XIP_KERNEL - la sp, _end + THREAD_SIZE - XIP_FIXUP_OFFSET sp - mv s0, a0 - mv s1, a1 - call __copy_data - - /* Restore a0 & a1 copy */ - mv a0, s0 - mv a1, s1 -#endif - -#ifndef CONFIG_XIP_KERNEL /* Clear BSS for flat non-ELF images */ la a3, __bss_start la a4, __bss_stop @@ -312,20 +278,16 @@ SYM_CODE_START(_start_kernel) add a3, a3, RISCV_SZPTR blt a3, a4, .Lclear_bss .Lclear_bss_done: -#endif la a2, boot_cpu_hartid - XIP_FIXUP_OFFSET a2 REG_S a0, (a2) /* Initialize page tables and relocate to virtual addresses */ la tp, init_task la sp, init_thread_union + THREAD_SIZE - XIP_FIXUP_OFFSET sp addi sp, sp, -PT_SIZE_ON_STACK scs_load_init_stack #ifdef CONFIG_BUILTIN_DTB la a0, __dtb_start - XIP_FIXUP_OFFSET a0 #else mv a0, a1 #endif /* CONFIG_BUILTIN_DTB */ @@ -335,7 +297,6 @@ SYM_CODE_START(_start_kernel) call setup_vm #ifdef CONFIG_MMU la a0, early_pg_dir - XIP_FIXUP_OFFSET a0 call relocate_enable_mmu #endif /* CONFIG_MMU */ @@ -374,9 +335,7 @@ SYM_CODE_START(_start_kernel) slli a3, a0, LGREG la a1, __cpu_spinwait_stack_pointer - XIP_FIXUP_OFFSET a1 la a2, __cpu_spinwait_task_pointer - XIP_FIXUP_OFFSET a2 add a1, a3, a1 add a2, a3, a2 diff --git a/arch/riscv/kernel/head.h b/arch/riscv/kernel/head.h index a556fdaafed9..05a04bef442b 100644 --- a/arch/riscv/kernel/head.h +++ b/arch/riscv/kernel/head.h @@ -11,9 +11,6 @@ extern atomic_t hart_lottery; asmlinkage void __init setup_vm(uintptr_t dtb_pa); -#ifdef CONFIG_XIP_KERNEL -asmlinkage void __init __copy_data(void); -#endif #ifdef CONFIG_RISCV_BOOT_SPINWAIT extern void *__cpu_spinwait_stack_pointer[]; diff --git a/arch/riscv/kernel/mcount-dyn.S b/arch/riscv/kernel/mcount-dyn.S index 48f6c4f7dca0..082fe0b0e3c0 100644 --- a/arch/riscv/kernel/mcount-dyn.S +++ b/arch/riscv/kernel/mcount-dyn.S @@ -66,7 +66,7 @@ * 8(sp) stores the function return address (i.e. parent IP) that * can be accessed by &(fregs)->ra in tracing function. * -* The other regs are saved at the respective localtion and accessed +* The other regs are saved at the respective location and accessed * by the respective ftrace_regs member. * * Here is the layout of stack for your reference. diff --git a/arch/riscv/kernel/module-sections.c b/arch/riscv/kernel/module-sections.c index 1675cbad8619..98eaac6f6606 100644 --- a/arch/riscv/kernel/module-sections.c +++ b/arch/riscv/kernel/module-sections.c @@ -148,7 +148,7 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, return -ENOEXEC; } - /* Calculate the maxinum number of entries */ + /* Calculate the maximum number of entries */ for (i = 0; i < ehdr->e_shnum; i++) { size_t num_relas = sechdrs[i].sh_size / sizeof(Elf_Rela); Elf_Rela *relas = (void *)ehdr + sechdrs[i].sh_offset; diff --git a/arch/riscv/kernel/probes/kprobes.c b/arch/riscv/kernel/probes/kprobes.c index 8723390c7cad..9e2afabf94e8 100644 --- a/arch/riscv/kernel/probes/kprobes.c +++ b/arch/riscv/kernel/probes/kprobes.c @@ -149,7 +149,7 @@ static void __kprobes set_current_kprobe(struct kprobe *p) /* * Interrupts need to be disabled before single-step mode is set, and not - * reenabled until after single-step mode ends. + * re-enabled until after single-step mode ends. * Without disabling interrupt on local CPU, there is a chance of * interrupt occurrence in the period of exception return and start of * out-of-line single-step, that result in wrongly single stepping diff --git a/arch/riscv/kernel/probes/uprobes.c b/arch/riscv/kernel/probes/uprobes.c index f0d0691a8688..eb177d0ce8ab 100644 --- a/arch/riscv/kernel/probes/uprobes.c +++ b/arch/riscv/kernel/probes/uprobes.c @@ -111,7 +111,7 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) current->thread.bad_cause = utask->autask.saved_cause; /* - * Task has received a fatal signal, so reset back to probbed + * Task has received a fatal signal, so reset back to probed * address. */ instruction_pointer_set(regs, utask->vaddr); diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 93de2e7a3074..793bcee46182 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -577,8 +577,8 @@ static int compat_riscv_gpr_set(struct task_struct *target, struct compat_user_regs_struct cregs; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &cregs, 0, -1); - - cregs_to_regs(&cregs, task_pt_regs(target)); + if (!ret) + cregs_to_regs(&cregs, task_pt_regs(target)); return ret; } diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index b5bc5fc65cea..c89cc272440b 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -46,11 +46,7 @@ * This is used before the kernel initializes the BSS so it can't be in the * BSS. */ -atomic_t hart_lottery __section(".sdata") -#ifdef CONFIG_XIP_KERNEL -= ATOMIC_INIT(0xC001BEEF) -#endif -; +atomic_t hart_lottery __section(".sdata"); unsigned long boot_cpu_hartid; EXPORT_SYMBOL_GPL(boot_cpu_hartid); diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index d85916a3660c..8b628580fe11 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -251,18 +251,14 @@ asmlinkage __visible void smp_callin(void) set_cpu_online(curr_cpuid, true); /* - * Remote cache and TLB flushes are ignored while the CPU is offline, - * so flush them both right now just in case. + * Remote instruction cache and TLB flushes are ignored while the CPU + * is offline, so flush them both right now just in case. */ local_flush_icache_all(); local_flush_tlb_all(); #ifndef CONFIG_HOTPLUG_PARALLEL complete(&cpu_running); #endif - /* - * Disable preemption before enabling interrupts, so we don't try to - * schedule a CPU that hasn't actually started yet. - */ local_irq_enable(); cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); } diff --git a/arch/riscv/kernel/soc.c b/arch/riscv/kernel/soc.c index a0516172a33c..7d69e3b6bab4 100644 --- a/arch/riscv/kernel/soc.c +++ b/arch/riscv/kernel/soc.c @@ -8,7 +8,7 @@ #include /* - * This is called extremly early, before parse_dtb(), to allow initializing + * This is called extremely early, before parse_dtb(), to allow initializing * SoC hardware before memory or any device driver initialization. */ void __init soc_early_init(void) diff --git a/arch/riscv/kernel/suspend.c b/arch/riscv/kernel/suspend.c index aff93090c4ef..3efbf7874f3b 100644 --- a/arch/riscv/kernel/suspend.c +++ b/arch/riscv/kernel/suspend.c @@ -78,7 +78,7 @@ int cpu_suspend(unsigned long arg, suspend_save_csrs(&context); /* - * Function graph tracer state gets incosistent when the kernel + * Function graph tracer state gets inconsistent when the kernel * calls functions that never return (aka finishers) hence disable * graph tracing during their execution. */ diff --git a/arch/riscv/kernel/suspend_entry.S b/arch/riscv/kernel/suspend_entry.S index 2d54f309c140..d71b55fd6259 100644 --- a/arch/riscv/kernel/suspend_entry.S +++ b/arch/riscv/kernel/suspend_entry.S @@ -10,7 +10,6 @@ #include #include #include -#include .text .altmacro @@ -70,7 +69,6 @@ SYM_TYPED_FUNC_START(__cpu_resume_enter) /* Enable MMU */ la a0, swapper_pg_dir - XIP_FIXUP_OFFSET a0 call relocate_enable_mmu /* Restore A0 and A1 */ diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c index 461279a7bd86..8c62c771a656 100644 --- a/arch/riscv/kernel/traps.c +++ b/arch/riscv/kernel/traps.c @@ -142,11 +142,7 @@ static void do_trap_error(struct pt_regs *regs, int signo, int code, } } -#if defined(CONFIG_XIP_KERNEL) && defined(CONFIG_RISCV_ALTERNATIVE) -#define __trap_section __noinstr_section(".xip.traps") -#else #define __trap_section noinstr -#endif #define DO_ERROR_INFO(name, signo, code, str) \ asmlinkage __visible __trap_section void name(struct pt_regs *regs) \ { \ @@ -165,6 +161,8 @@ asmlinkage __visible __trap_section void name(struct pt_regs *regs) \ DO_ERROR_INFO(do_trap_unknown, SIGILL, ILL_ILLTRP, "unknown exception"); +DO_ERROR_INFO(do_trap_hardware_error, + SIGBUS, BUS_MCEERR_AR, "hardware error"); DO_ERROR_INFO(do_trap_insn_misaligned, SIGBUS, BUS_ADRALN, "instruction address misaligned"); DO_ERROR_INFO(do_trap_insn_fault, diff --git a/arch/riscv/kernel/traps_misaligned.c b/arch/riscv/kernel/traps_misaligned.c index 2a27d3ff4ac6..81b7682e6c6d 100644 --- a/arch/riscv/kernel/traps_misaligned.c +++ b/arch/riscv/kernel/traps_misaligned.c @@ -584,7 +584,7 @@ static int cpu_online_check_unaligned_access_emulated(unsigned int cpu) static bool misaligned_traps_delegated; -#ifdef CONFIG_RISCV_SBI +#if defined(CONFIG_RISCV_SBI_FWFT_DELEGATE_MISALIGNED) static int cpu_online_sbi_unaligned_setup(unsigned int cpu) { diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index b36a6a56f404..485ab1d105d3 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -16,7 +16,7 @@ #include "copy-unaligned.h" -#define MISALIGNED_ACCESS_JIFFIES_LG2 1 +#define MISALIGNED_ACCESS_NS 8000000 #define MISALIGNED_BUFFER_SIZE 0x4000 #define MISALIGNED_BUFFER_ORDER get_order(MISALIGNED_BUFFER_SIZE) #define MISALIGNED_COPY_SIZE ((MISALIGNED_BUFFER_SIZE / 2) - 0x80) @@ -29,109 +29,125 @@ static long unaligned_vector_speed_param = RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNO static cpumask_t fast_misaligned_access; -#ifdef CONFIG_RISCV_PROBE_UNALIGNED_ACCESS -static int check_unaligned_access(void *param) +static u64 __maybe_unused +measure_cycles(void (*func)(void *dst, const void *src, size_t len), + void *dst, void *src, size_t len) { - int cpu = smp_processor_id(); - u64 start_cycles, end_cycles; - u64 word_cycles; - u64 byte_cycles; - int ratio; - unsigned long start_jiffies, now; - struct page *page = param; - void *dst; - void *src; - long speed = RISCV_HWPROBE_MISALIGNED_SCALAR_SLOW; + u64 start_cycles, end_cycles, cycles = -1ULL; + u64 start_ns; - if (per_cpu(misaligned_access_speed, cpu) != RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN) - return 0; - - /* Make an unaligned destination buffer. */ - dst = (void *)((unsigned long)page_address(page) | 0x1); - /* Unalign src as well, but differently (off by 1 + 2 = 3). */ - src = dst + (MISALIGNED_BUFFER_SIZE / 2); - src += 2; - word_cycles = -1ULL; /* Do a warmup. */ - __riscv_copy_words_unaligned(dst, src, MISALIGNED_COPY_SIZE); + func(dst, src, len); + preempt_disable(); - start_jiffies = jiffies; - while ((now = jiffies) == start_jiffies) - cpu_relax(); /* * For a fixed amount of time, repeatedly try the function, and take * the best time in cycles as the measurement. */ - while (time_before(jiffies, now + (1 << MISALIGNED_ACCESS_JIFFIES_LG2))) { + start_ns = ktime_get_mono_fast_ns(); + while (ktime_get_mono_fast_ns() < start_ns + MISALIGNED_ACCESS_NS) { start_cycles = get_cycles64(); /* Ensure the CSR read can't reorder WRT to the copy. */ mb(); - __riscv_copy_words_unaligned(dst, src, MISALIGNED_COPY_SIZE); + func(dst, src, len); /* Ensure the copy ends before the end time is snapped. */ mb(); end_cycles = get_cycles64(); - if ((end_cycles - start_cycles) < word_cycles) - word_cycles = end_cycles - start_cycles; - } - - byte_cycles = -1ULL; - __riscv_copy_bytes_unaligned(dst, src, MISALIGNED_COPY_SIZE); - start_jiffies = jiffies; - while ((now = jiffies) == start_jiffies) - cpu_relax(); - - while (time_before(jiffies, now + (1 << MISALIGNED_ACCESS_JIFFIES_LG2))) { - start_cycles = get_cycles64(); - mb(); - __riscv_copy_bytes_unaligned(dst, src, MISALIGNED_COPY_SIZE); - mb(); - end_cycles = get_cycles64(); - if ((end_cycles - start_cycles) < byte_cycles) - byte_cycles = end_cycles - start_cycles; + if ((end_cycles - start_cycles) < cycles) + cycles = end_cycles - start_cycles; } preempt_enable(); + return cycles; +} + +/* + * Return: + * 1 if unaligned accesses are fast + * 0 if unaligned accesses are slow + * -1 if check cannot be done + */ +static int __maybe_unused +compare_unaligned_access(void (*word_copy)(void *dst, const void *src, size_t len), + void (*byte_copy)(void *dst, const void *src, size_t len), + void *buf, const char *type) +{ + int cpu = smp_processor_id(); + u64 word_cycles; + u64 byte_cycles; + void *dst, *src; + bool fast; + int ratio; + + /* Make an unaligned destination buffer. */ + dst = (void *)((unsigned long)buf | 0x1); + /* Unalign src as well, but differently (off by 1 + 2 = 3). */ + src = dst + (MISALIGNED_BUFFER_SIZE / 2); + src += 2; + + word_cycles = measure_cycles(word_copy, dst, src, MISALIGNED_COPY_SIZE); + byte_cycles = measure_cycles(byte_copy, dst, src, MISALIGNED_COPY_SIZE); + /* Don't divide by zero. */ if (!word_cycles || !byte_cycles) { - pr_warn("cpu%d: rdtime lacks granularity needed to measure unaligned access speed\n", - cpu); + pr_warn("cpu%d: rdtime lacks granularity needed to measure %s unaligned access speed\n", + cpu, type); - return 0; + return -1; } - if (word_cycles < byte_cycles) - speed = RISCV_HWPROBE_MISALIGNED_SCALAR_FAST; + fast = word_cycles < byte_cycles; ratio = div_u64((byte_cycles * 100), word_cycles); - pr_info("cpu%d: Ratio of byte access time to unaligned word access is %d.%02d, unaligned accesses are %s\n", + pr_info("cpu%d: %s unaligned word access speed is %d.%02dx byte access speed (%s)\n", cpu, + type, ratio / 100, ratio % 100, - (speed == RISCV_HWPROBE_MISALIGNED_SCALAR_FAST) ? "fast" : "slow"); + fast ? "fast" : "slow"); - per_cpu(misaligned_access_speed, cpu) = speed; + return fast; +} + +#ifdef CONFIG_RISCV_PROBE_UNALIGNED_ACCESS +static int check_unaligned_access(struct page *page) +{ + void *buf = page_address(page); + int cpu = smp_processor_id(); + int ret; + + if (per_cpu(misaligned_access_speed, cpu) != RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN) + return 0; + + ret = compare_unaligned_access(__riscv_copy_words_unaligned, + __riscv_copy_bytes_unaligned, + buf, "scalar"); + if (ret < 0) + return 0; /* * Set the value of fast_misaligned_access of a CPU. These operations * are atomic to avoid race conditions. */ - if (speed == RISCV_HWPROBE_MISALIGNED_SCALAR_FAST) + if (ret) { + per_cpu(misaligned_access_speed, cpu) = RISCV_HWPROBE_MISALIGNED_SCALAR_FAST; cpumask_set_cpu(cpu, &fast_misaligned_access); - else + } else { + per_cpu(misaligned_access_speed, cpu) = RISCV_HWPROBE_MISALIGNED_SCALAR_SLOW; cpumask_clear_cpu(cpu, &fast_misaligned_access); + } return 0; } -static void __init check_unaligned_access_nonboot_cpu(void *param) +static void __init _check_unaligned_access(void *param) { unsigned int cpu = smp_processor_id(); struct page **pages = param; - if (smp_processor_id() != 0) - check_unaligned_access(pages[cpu]); + check_unaligned_access(pages[cpu]); } /* Measure unaligned access speed on all CPUs present at boot in parallel. */ @@ -158,11 +174,7 @@ static void __init check_unaligned_access_speed_all_cpus(void) } } - /* Check everybody except 0, who stays behind to tend jiffies. */ - on_each_cpu(check_unaligned_access_nonboot_cpu, bufs, 1); - - /* Check core 0. */ - smp_call_on_cpu(0, check_unaligned_access, bufs[0], true); + on_each_cpu(_check_unaligned_access, bufs, 1); out: for_each_cpu(cpu, cpu_online_mask) { @@ -281,15 +293,8 @@ static int riscv_offline_cpu(unsigned int cpu) static void check_vector_unaligned_access(struct work_struct *work __always_unused) { int cpu = smp_processor_id(); - u64 start_cycles, end_cycles; - u64 word_cycles; - u64 byte_cycles; - int ratio; - unsigned long start_jiffies, now; struct page *page; - void *dst; - void *src; - long speed = RISCV_HWPROBE_MISALIGNED_VECTOR_SLOW; + int ret; if (per_cpu(vector_misaligned_access, cpu) != RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN) return; @@ -300,76 +305,20 @@ static void check_vector_unaligned_access(struct work_struct *work __always_unus return; } - /* Make an unaligned destination buffer. */ - dst = (void *)((unsigned long)page_address(page) | 0x1); - /* Unalign src as well, but differently (off by 1 + 2 = 3). */ - src = dst + (MISALIGNED_BUFFER_SIZE / 2); - src += 2; - word_cycles = -1ULL; - - /* Do a warmup. */ kernel_vector_begin(); - __riscv_copy_vec_words_unaligned(dst, src, MISALIGNED_COPY_SIZE); - - start_jiffies = jiffies; - while ((now = jiffies) == start_jiffies) - cpu_relax(); - - /* - * For a fixed amount of time, repeatedly try the function, and take - * the best time in cycles as the measurement. - */ - while (time_before(jiffies, now + (1 << MISALIGNED_ACCESS_JIFFIES_LG2))) { - start_cycles = get_cycles64(); - /* Ensure the CSR read can't reorder WRT to the copy. */ - mb(); - __riscv_copy_vec_words_unaligned(dst, src, MISALIGNED_COPY_SIZE); - /* Ensure the copy ends before the end time is snapped. */ - mb(); - end_cycles = get_cycles64(); - if ((end_cycles - start_cycles) < word_cycles) - word_cycles = end_cycles - start_cycles; - } - - byte_cycles = -1ULL; - __riscv_copy_vec_bytes_unaligned(dst, src, MISALIGNED_COPY_SIZE); - start_jiffies = jiffies; - while ((now = jiffies) == start_jiffies) - cpu_relax(); - - while (time_before(jiffies, now + (1 << MISALIGNED_ACCESS_JIFFIES_LG2))) { - start_cycles = get_cycles64(); - /* Ensure the CSR read can't reorder WRT to the copy. */ - mb(); - __riscv_copy_vec_bytes_unaligned(dst, src, MISALIGNED_COPY_SIZE); - /* Ensure the copy ends before the end time is snapped. */ - mb(); - end_cycles = get_cycles64(); - if ((end_cycles - start_cycles) < byte_cycles) - byte_cycles = end_cycles - start_cycles; - } + ret = compare_unaligned_access(__riscv_copy_vec_words_unaligned, + __riscv_copy_vec_bytes_unaligned, + page_address(page), "vector"); kernel_vector_end(); - /* Don't divide by zero. */ - if (!word_cycles || !byte_cycles) { - pr_warn("cpu%d: rdtime lacks granularity needed to measure unaligned vector access speed\n", - cpu); - + if (ret < 0) goto free; - } - if (word_cycles < byte_cycles) - speed = RISCV_HWPROBE_MISALIGNED_VECTOR_FAST; - - ratio = div_u64((byte_cycles * 100), word_cycles); - pr_info("cpu%d: Ratio of vector byte access time to vector unaligned word access is %d.%02d, unaligned accesses are %s\n", - cpu, - ratio / 100, - ratio % 100, - (speed == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST) ? "fast" : "slow"); - - per_cpu(vector_misaligned_access, cpu) = speed; + if (ret) + per_cpu(vector_misaligned_access, cpu) = RISCV_HWPROBE_MISALIGNED_VECTOR_FAST; + else + per_cpu(vector_misaligned_access, cpu) = RISCV_HWPROBE_MISALIGNED_VECTOR_SLOW; free: __free_pages(page, MISALIGNED_BUFFER_ORDER); @@ -494,4 +443,4 @@ static int __init check_unaligned_access_all_cpus(void) return 0; } -arch_initcall(check_unaligned_access_all_cpus); +late_initcall(check_unaligned_access_all_cpus); diff --git a/arch/riscv/kernel/usercfi.c b/arch/riscv/kernel/usercfi.c index 6eaa0d94fdfe..cbfb4e495e9f 100644 --- a/arch/riscv/kernel/usercfi.c +++ b/arch/riscv/kernel/usercfi.c @@ -109,15 +109,16 @@ void set_indir_lp_lock(struct task_struct *task, bool lock) task->thread_info.user_cfi_state.ufcfi_locked = lock; } /* - * If size is 0, then to be compatible with regular stack we want it to be as big as - * regular stack. Else PAGE_ALIGN it and return back + * The shadow stack only stores the return address and not any variables + * this should be more than sufficient for most applications. + * Else PAGE_ALIGN it and return back */ static unsigned long calc_shstk_size(unsigned long size) { if (size) return PAGE_ALIGN(size); - return PAGE_ALIGN(min_t(unsigned long long, rlimit(RLIMIT_STACK), SZ_4G)); + return PAGE_ALIGN(min(rlimit(RLIMIT_STACK) / 2, SZ_2G)); } /* diff --git a/arch/riscv/kernel/vdso_cfi/.gitignore b/arch/riscv/kernel/vdso_cfi/.gitignore new file mode 100644 index 000000000000..220b6ebece4f --- /dev/null +++ b/arch/riscv/kernel/vdso_cfi/.gitignore @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copied source files from the main vdso directory +*.c +*.S +!vdso-cfi.S +vdso.lds +*.tmp +vdso-syms.S diff --git a/arch/riscv/kernel/vdso_cfi/Makefile b/arch/riscv/kernel/vdso_cfi/Makefile index 8ebd190782b0..10292498b765 100644 --- a/arch/riscv/kernel/vdso_cfi/Makefile +++ b/arch/riscv/kernel/vdso_cfi/Makefile @@ -23,3 +23,6 @@ $(vdso_c_objects): $(obj)/%.c: $(src)/%.c # Include the main VDSO Makefile which contains all the build rules and sources # The VDSO_CFI_BUILD variable will be passed to it to enable CFI compilation include $(src)/Makefile + +# Clean rules - remove the copied source files +clean-files += $(notdir $(vdso_c_sources)) $(notdir $(vdso_S_sources)) diff --git a/arch/riscv/kernel/vec-copy-unaligned.S b/arch/riscv/kernel/vec-copy-unaligned.S index 7ce4de6f6e69..361039f7b944 100644 --- a/arch/riscv/kernel/vec-copy-unaligned.S +++ b/arch/riscv/kernel/vec-copy-unaligned.S @@ -2,6 +2,7 @@ /* Copyright (C) 2024 Rivos Inc. */ #include +#include #include #include @@ -16,7 +17,7 @@ /* void __riscv_copy_vec_words_unaligned(void *, const void *, size_t) */ /* Performs a memcpy without aligning buffers, using word loads and stores. */ /* Note: The size is truncated to a multiple of WORD_EEW */ -SYM_FUNC_START(__riscv_copy_vec_words_unaligned) +SYM_TYPED_FUNC_START(__riscv_copy_vec_words_unaligned) andi a4, a2, ~(WORD_EEW-1) beqz a4, 2f add a3, a1, a4 @@ -38,7 +39,7 @@ SYM_FUNC_END(__riscv_copy_vec_words_unaligned) /* void __riscv_copy_vec_bytes_unaligned(void *, const void *, size_t) */ /* Performs a memcpy without aligning buffers, using only byte accesses. */ /* Note: The size is truncated to a multiple of 8 */ -SYM_FUNC_START(__riscv_copy_vec_bytes_unaligned) +SYM_TYPED_FUNC_START(__riscv_copy_vec_bytes_unaligned) andi a4, a2, ~(8-1) beqz a4, 2f add a3, a1, a4 diff --git a/arch/riscv/kernel/vmcore_info.c b/arch/riscv/kernel/vmcore_info.c index d5e448aa90e7..c27efceec3cc 100644 --- a/arch/riscv/kernel/vmcore_info.c +++ b/arch/riscv/kernel/vmcore_info.c @@ -3,6 +3,11 @@ #include #include +static inline u64 get_satp_value(void) +{ + return csr_read(CSR_SATP); +} + void arch_crash_save_vmcoreinfo(void) { VMCOREINFO_NUMBER(phys_ram_base); @@ -19,13 +24,8 @@ void arch_crash_save_vmcoreinfo(void) #endif #endif vmcoreinfo_append_str("NUMBER(KERNEL_LINK_ADDR)=0x%lx\n", KERNEL_LINK_ADDR); -#ifdef CONFIG_XIP_KERNEL - /* TODO: Communicate with crash-utility developers on the information to - * export. The XIP case is more complicated, because the virtual-physical - * address offset depends on whether the address is in ROM or in RAM. - */ -#else vmcoreinfo_append_str("NUMBER(va_kernel_pa_offset)=0x%lx\n", kernel_map.va_kernel_pa_offset); -#endif + vmcoreinfo_append_str("KERNELOFFSET=%lx\n", kaslr_offset()); + vmcoreinfo_append_str("NUMBER(satp)=0x%llx\n", get_satp_value()); } diff --git a/arch/riscv/kernel/vmlinux.lds.S b/arch/riscv/kernel/vmlinux.lds.S index 997f9eb3b22b..1f4f8496941a 100644 --- a/arch/riscv/kernel/vmlinux.lds.S +++ b/arch/riscv/kernel/vmlinux.lds.S @@ -7,10 +7,6 @@ #define RO_EXCEPTION_TABLE_ALIGN 4 #define RUNTIME_DISCARD_EXIT -#ifdef CONFIG_XIP_KERNEL -#include "vmlinux-xip.lds.S" -#else - #include #define LOAD_OFFSET KERNEL_LINK_ADDR @@ -176,4 +172,3 @@ SECTIONS DISCARDS } -#endif /* CONFIG_XIP_KERNEL */ diff --git a/arch/riscv/kvm/Makefile b/arch/riscv/kvm/Makefile index 3b8afb038b35..296c2ba05089 100644 --- a/arch/riscv/kvm/Makefile +++ b/arch/riscv/kvm/Makefile @@ -15,11 +15,13 @@ kvm-y += aia_aplic.o kvm-y += aia_device.o kvm-y += aia_imsic.o kvm-y += gstage.o +kvm-y += isa.o kvm-y += main.o kvm-y += mmu.o kvm-y += nacl.o kvm-y += tlb.o kvm-y += vcpu.o +kvm-y += vcpu_config.o kvm-y += vcpu_exit.o kvm-y += vcpu_fp.o kvm-y += vcpu_insn.o diff --git a/arch/riscv/kvm/aia_device.c b/arch/riscv/kvm/aia_device.c index 49c71d3cdb00..3d1e81e2a36b 100644 --- a/arch/riscv/kvm/aia_device.c +++ b/arch/riscv/kvm/aia_device.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include static int aia_create(struct kvm_device *dev, u32 type) { @@ -23,7 +23,7 @@ static int aia_create(struct kvm_device *dev, u32 type) if (irqchip_in_kernel(kvm)) return -EEXIST; - if (!riscv_isa_extension_available(NULL, SSAIA)) + if (kvm_riscv_isa_check_host(SSAIA)) return -ENODEV; ret = -EBUSY; diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c index b67d60d722c2..d9fe8be2a151 100644 --- a/arch/riscv/kvm/gstage.c +++ b/arch/riscv/kvm/gstage.c @@ -12,22 +12,21 @@ #include #ifdef CONFIG_64BIT -unsigned long kvm_riscv_gstage_mode __ro_after_init = HGATP_MODE_SV39X4; -unsigned long kvm_riscv_gstage_pgd_levels __ro_after_init = 3; +unsigned long kvm_riscv_gstage_max_pgd_levels __ro_after_init = 3; #else -unsigned long kvm_riscv_gstage_mode __ro_after_init = HGATP_MODE_SV32X4; -unsigned long kvm_riscv_gstage_pgd_levels __ro_after_init = 2; +unsigned long kvm_riscv_gstage_max_pgd_levels __ro_after_init = 2; #endif #define gstage_pte_leaf(__ptep) \ (pte_val(*(__ptep)) & (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC)) -static inline unsigned long gstage_pte_index(gpa_t addr, u32 level) +static inline unsigned long gstage_pte_index(struct kvm_gstage *gstage, + gpa_t addr, u32 level) { unsigned long mask; unsigned long shift = HGATP_PAGE_SHIFT + (kvm_riscv_gstage_index_bits * level); - if (level == (kvm_riscv_gstage_pgd_levels - 1)) + if (level == gstage->pgd_levels - 1) mask = (PTRS_PER_PTE * (1UL << kvm_riscv_gstage_pgd_xbits)) - 1; else mask = PTRS_PER_PTE - 1; @@ -40,12 +39,13 @@ static inline unsigned long gstage_pte_page_vaddr(pte_t pte) return (unsigned long)pfn_to_virt(__page_val_to_pfn(pte_val(pte))); } -static int gstage_page_size_to_level(unsigned long page_size, u32 *out_level) +static int gstage_page_size_to_level(struct kvm_gstage *gstage, unsigned long page_size, + u32 *out_level) { u32 i; unsigned long psz = 1UL << 12; - for (i = 0; i < kvm_riscv_gstage_pgd_levels; i++) { + for (i = 0; i < gstage->pgd_levels; i++) { if (page_size == (psz << (i * kvm_riscv_gstage_index_bits))) { *out_level = i; return 0; @@ -55,21 +55,23 @@ static int gstage_page_size_to_level(unsigned long page_size, u32 *out_level) return -EINVAL; } -static int gstage_level_to_page_order(u32 level, unsigned long *out_pgorder) +static int gstage_level_to_page_order(struct kvm_gstage *gstage, u32 level, + unsigned long *out_pgorder) { - if (kvm_riscv_gstage_pgd_levels < level) + if (gstage->pgd_levels < level) return -EINVAL; *out_pgorder = 12 + (level * kvm_riscv_gstage_index_bits); return 0; } -static int gstage_level_to_page_size(u32 level, unsigned long *out_pgsize) +static int gstage_level_to_page_size(struct kvm_gstage *gstage, u32 level, + unsigned long *out_pgsize) { int rc; unsigned long page_order = PAGE_SHIFT; - rc = gstage_level_to_page_order(level, &page_order); + rc = gstage_level_to_page_order(gstage, level, &page_order); if (rc) return rc; @@ -81,11 +83,11 @@ bool kvm_riscv_gstage_get_leaf(struct kvm_gstage *gstage, gpa_t addr, pte_t **ptepp, u32 *ptep_level) { pte_t *ptep; - u32 current_level = kvm_riscv_gstage_pgd_levels - 1; + u32 current_level = gstage->pgd_levels - 1; *ptep_level = current_level; ptep = (pte_t *)gstage->pgd; - ptep = &ptep[gstage_pte_index(addr, current_level)]; + ptep = &ptep[gstage_pte_index(gstage, addr, current_level)]; while (ptep && pte_val(ptep_get(ptep))) { if (gstage_pte_leaf(ptep)) { *ptep_level = current_level; @@ -97,7 +99,7 @@ bool kvm_riscv_gstage_get_leaf(struct kvm_gstage *gstage, gpa_t addr, current_level--; *ptep_level = current_level; ptep = (pte_t *)gstage_pte_page_vaddr(ptep_get(ptep)); - ptep = &ptep[gstage_pte_index(addr, current_level)]; + ptep = &ptep[gstage_pte_index(gstage, addr, current_level)]; } else { ptep = NULL; } @@ -110,7 +112,7 @@ static void gstage_tlb_flush(struct kvm_gstage *gstage, u32 level, gpa_t addr) { unsigned long order = PAGE_SHIFT; - if (gstage_level_to_page_order(level, &order)) + if (gstage_level_to_page_order(gstage, level, &order)) return; addr &= ~(BIT(order) - 1); @@ -125,9 +127,9 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage, struct kvm_mmu_memory_cache *pcache, const struct kvm_gstage_mapping *map) { - u32 current_level = kvm_riscv_gstage_pgd_levels - 1; + u32 current_level = gstage->pgd_levels - 1; pte_t *next_ptep = (pte_t *)gstage->pgd; - pte_t *ptep = &next_ptep[gstage_pte_index(map->addr, current_level)]; + pte_t *ptep = &next_ptep[gstage_pte_index(gstage, map->addr, current_level)]; if (current_level < map->level) return -EINVAL; @@ -151,7 +153,7 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage, } current_level--; - ptep = &next_ptep[gstage_pte_index(map->addr, current_level)]; + ptep = &next_ptep[gstage_pte_index(gstage, map->addr, current_level)]; } if (pte_val(*ptep) != pte_val(map->pte)) { @@ -163,19 +165,38 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage, return 0; } +static void kvm_riscv_gstage_update_pte_prot(struct kvm_gstage *gstage, u32 level, + gpa_t addr, pte_t *ptep, pgprot_t prot) +{ + pte_t new_pte; + + if (pgprot_val(pte_pgprot(ptep_get(ptep))) == pgprot_val(prot)) + return; + + new_pte = pfn_pte(pte_pfn(ptep_get(ptep)), prot); + new_pte = pte_mkdirty(new_pte); + + set_pte(ptep, new_pte); + + gstage_tlb_flush(gstage, level, addr); +} + int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage, struct kvm_mmu_memory_cache *pcache, gpa_t gpa, phys_addr_t hpa, unsigned long page_size, bool page_rdonly, bool page_exec, struct kvm_gstage_mapping *out_map) { + bool found_leaf; + u32 ptep_level; pgprot_t prot; + pte_t *ptep; int ret; out_map->addr = gpa; out_map->level = 0; - ret = gstage_page_size_to_level(page_size, &out_map->level); + ret = gstage_page_size_to_level(gstage, page_size, &out_map->level); if (ret) return ret; @@ -203,12 +224,119 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage, else prot = PAGE_WRITE; } + + found_leaf = kvm_riscv_gstage_get_leaf(gstage, gpa, &ptep, &ptep_level); + if (found_leaf) { + /* + * ptep_level is the current gstage mapping level of addr, out_map->level + * is the required mapping level during fault handling. + * + * 1) ptep_level > out_map->level + * This happens when dirty logging is enabled and huge pages are used. + * KVM must track the pages at 4K level, and split the huge mapping + * into 4K mappings. + * + * 2) ptep_level < out_map->level + * This happens when dirty logging is disabled and huge pages are used. + * The gstage is split into 4K mappings, but the out_map level is now + * back to the huge page level. Ignore the out_map level this time, and + * just update the pte prot here. Otherwise, we would fall back to mapping + * the gstage at huge page level in `kvm_riscv_gstage_set_pte`, with the + * overhead of freeing the page tables(not support now), which would slow + * down the vCPUs' performance. + * + * It is better to recover the huge page mapping in the ioctl context when + * disabling dirty logging. + * + * 3) ptep_level == out_map->level + * We already have the ptep, just update the pte prot if the pfn not change. + * There is no need to invoke `kvm_riscv_gstage_set_pte` again. + */ + if (ptep_level > out_map->level) { + kvm_riscv_gstage_split_huge(gstage, pcache, gpa, + out_map->level, true); + } else if (ALIGN_DOWN(PFN_PHYS(pte_pfn(ptep_get(ptep))), page_size) == hpa) { + kvm_riscv_gstage_update_pte_prot(gstage, ptep_level, gpa, ptep, prot); + return 0; + } + } + out_map->pte = pfn_pte(PFN_DOWN(hpa), prot); out_map->pte = pte_mkdirty(out_map->pte); return kvm_riscv_gstage_set_pte(gstage, pcache, out_map); } +static inline unsigned long make_child_pte(unsigned long huge_pte, int index, + unsigned long child_page_size) +{ + unsigned long child_pte = huge_pte; + unsigned long child_pfn_offset; + + /* + * The child_pte already has the base address of the huge page being + * split. So we just have to OR in the offset to the page at the next + * lower level for the given index. + */ + child_pfn_offset = index * (child_page_size / PAGE_SIZE); + child_pte |= pte_val(pfn_pte(child_pfn_offset, __pgprot(0))); + + return child_pte; +} + +int kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage, + struct kvm_mmu_memory_cache *pcache, + gpa_t addr, u32 target_level, bool flush) +{ + u32 current_level = gstage->pgd_levels - 1; + pte_t *next_ptep = (pte_t *)gstage->pgd; + unsigned long huge_pte, child_pte; + unsigned long child_page_size; + pte_t *ptep; + int i, ret; + + if (!pcache) + return -ENOMEM; + + while(current_level > target_level) { + ptep = (pte_t *)&next_ptep[gstage_pte_index(gstage, addr, current_level)]; + + if (!pte_val(ptep_get(ptep))) + break; + + if (!gstage_pte_leaf(ptep)) { + next_ptep = (pte_t *)gstage_pte_page_vaddr(ptep_get(ptep)); + current_level--; + continue; + } + + huge_pte = pte_val(ptep_get(ptep)); + + ret = gstage_level_to_page_size(gstage, current_level - 1, &child_page_size); + if (ret) + return ret; + + next_ptep = kvm_mmu_memory_cache_alloc(pcache); + if (!next_ptep) + return -ENOMEM; + + for (i = 0; i < PTRS_PER_PTE; i++) { + child_pte = make_child_pte(huge_pte, i, child_page_size); + set_pte((pte_t *)&next_ptep[i], __pte(child_pte)); + } + + set_pte(ptep, pfn_pte(PFN_DOWN(__pa(next_ptep)), + __pgprot(_PAGE_TABLE))); + + if (flush) + gstage_tlb_flush(gstage, current_level, addr); + + current_level--; + } + + return 0; +} + void kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr, pte_t *ptep, u32 ptep_level, enum kvm_riscv_gstage_op op) { @@ -217,7 +345,7 @@ void kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr, u32 next_ptep_level; unsigned long next_page_size, page_size; - ret = gstage_level_to_page_size(ptep_level, &page_size); + ret = gstage_level_to_page_size(gstage, ptep_level, &page_size); if (ret) return; @@ -229,7 +357,7 @@ void kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr, if (ptep_level && !gstage_pte_leaf(ptep)) { next_ptep = (pte_t *)gstage_pte_page_vaddr(ptep_get(ptep)); next_ptep_level = ptep_level - 1; - ret = gstage_level_to_page_size(next_ptep_level, &next_page_size); + ret = gstage_level_to_page_size(gstage, next_ptep_level, &next_page_size); if (ret) return; @@ -263,7 +391,7 @@ void kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage, while (addr < end) { found_leaf = kvm_riscv_gstage_get_leaf(gstage, addr, &ptep, &ptep_level); - ret = gstage_level_to_page_size(ptep_level, &page_size); + ret = gstage_level_to_page_size(gstage, ptep_level, &page_size); if (ret) break; @@ -297,17 +425,16 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end while (addr < end) { found_leaf = kvm_riscv_gstage_get_leaf(gstage, addr, &ptep, &ptep_level); - ret = gstage_level_to_page_size(ptep_level, &page_size); + ret = gstage_level_to_page_size(gstage, ptep_level, &page_size); if (ret) break; if (!found_leaf) goto next; - if (!(addr & (page_size - 1)) && ((end - addr) >= page_size)) - kvm_riscv_gstage_op_pte(gstage, addr, ptep, - ptep_level, GSTAGE_OP_WP); - + addr = ALIGN_DOWN(addr, page_size); + kvm_riscv_gstage_op_pte(gstage, addr, ptep, + ptep_level, GSTAGE_OP_WP); next: addr += page_size; } @@ -319,39 +446,34 @@ void __init kvm_riscv_gstage_mode_detect(void) /* Try Sv57x4 G-stage mode */ csr_write(CSR_HGATP, HGATP_MODE_SV57X4 << HGATP_MODE_SHIFT); if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV57X4) { - kvm_riscv_gstage_mode = HGATP_MODE_SV57X4; - kvm_riscv_gstage_pgd_levels = 5; + kvm_riscv_gstage_max_pgd_levels = 5; goto done; } /* Try Sv48x4 G-stage mode */ csr_write(CSR_HGATP, HGATP_MODE_SV48X4 << HGATP_MODE_SHIFT); if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV48X4) { - kvm_riscv_gstage_mode = HGATP_MODE_SV48X4; - kvm_riscv_gstage_pgd_levels = 4; + kvm_riscv_gstage_max_pgd_levels = 4; goto done; } /* Try Sv39x4 G-stage mode */ csr_write(CSR_HGATP, HGATP_MODE_SV39X4 << HGATP_MODE_SHIFT); if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV39X4) { - kvm_riscv_gstage_mode = HGATP_MODE_SV39X4; - kvm_riscv_gstage_pgd_levels = 3; + kvm_riscv_gstage_max_pgd_levels = 3; goto done; } #else /* CONFIG_32BIT */ /* Try Sv32x4 G-stage mode */ csr_write(CSR_HGATP, HGATP_MODE_SV32X4 << HGATP_MODE_SHIFT); if ((csr_read(CSR_HGATP) >> HGATP_MODE_SHIFT) == HGATP_MODE_SV32X4) { - kvm_riscv_gstage_mode = HGATP_MODE_SV32X4; - kvm_riscv_gstage_pgd_levels = 2; + kvm_riscv_gstage_max_pgd_levels = 2; goto done; } #endif /* KVM depends on !HGATP_MODE_OFF */ - kvm_riscv_gstage_mode = HGATP_MODE_OFF; - kvm_riscv_gstage_pgd_levels = 0; + kvm_riscv_gstage_max_pgd_levels = 0; done: csr_write(CSR_HGATP, 0); diff --git a/arch/riscv/kvm/isa.c b/arch/riscv/kvm/isa.c new file mode 100644 index 000000000000..1132d909cc25 --- /dev/null +++ b/arch/riscv/kvm/isa.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026 Qualcomm Technologies, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define KVM_ISA_EXT_ARR(ext) \ +[KVM_RISCV_ISA_EXT_##ext] = RISCV_ISA_EXT_##ext + +/* Mapping between KVM ISA Extension ID & guest ISA extension ID */ +static const unsigned long kvm_isa_ext_arr[] = { + /* Single letter extensions (alphabetically sorted) */ + [KVM_RISCV_ISA_EXT_A] = RISCV_ISA_EXT_a, + [KVM_RISCV_ISA_EXT_C] = RISCV_ISA_EXT_c, + [KVM_RISCV_ISA_EXT_D] = RISCV_ISA_EXT_d, + [KVM_RISCV_ISA_EXT_F] = RISCV_ISA_EXT_f, + [KVM_RISCV_ISA_EXT_H] = RISCV_ISA_EXT_h, + [KVM_RISCV_ISA_EXT_I] = RISCV_ISA_EXT_i, + [KVM_RISCV_ISA_EXT_M] = RISCV_ISA_EXT_m, + [KVM_RISCV_ISA_EXT_V] = RISCV_ISA_EXT_v, + /* Multi letter extensions (alphabetically sorted) */ + KVM_ISA_EXT_ARR(SMNPM), + KVM_ISA_EXT_ARR(SMSTATEEN), + KVM_ISA_EXT_ARR(SSAIA), + KVM_ISA_EXT_ARR(SSCOFPMF), + KVM_ISA_EXT_ARR(SSNPM), + KVM_ISA_EXT_ARR(SSTC), + KVM_ISA_EXT_ARR(SVADE), + KVM_ISA_EXT_ARR(SVADU), + KVM_ISA_EXT_ARR(SVINVAL), + KVM_ISA_EXT_ARR(SVNAPOT), + KVM_ISA_EXT_ARR(SVPBMT), + KVM_ISA_EXT_ARR(SVVPTC), + KVM_ISA_EXT_ARR(ZAAMO), + KVM_ISA_EXT_ARR(ZABHA), + KVM_ISA_EXT_ARR(ZACAS), + KVM_ISA_EXT_ARR(ZALASR), + KVM_ISA_EXT_ARR(ZALRSC), + KVM_ISA_EXT_ARR(ZAWRS), + KVM_ISA_EXT_ARR(ZBA), + KVM_ISA_EXT_ARR(ZBB), + KVM_ISA_EXT_ARR(ZBC), + KVM_ISA_EXT_ARR(ZBKB), + KVM_ISA_EXT_ARR(ZBKC), + KVM_ISA_EXT_ARR(ZBKX), + KVM_ISA_EXT_ARR(ZBS), + KVM_ISA_EXT_ARR(ZCA), + KVM_ISA_EXT_ARR(ZCB), + KVM_ISA_EXT_ARR(ZCD), + KVM_ISA_EXT_ARR(ZCF), + KVM_ISA_EXT_ARR(ZCLSD), + KVM_ISA_EXT_ARR(ZCMOP), + KVM_ISA_EXT_ARR(ZFA), + KVM_ISA_EXT_ARR(ZFBFMIN), + KVM_ISA_EXT_ARR(ZFH), + KVM_ISA_EXT_ARR(ZFHMIN), + KVM_ISA_EXT_ARR(ZICBOM), + KVM_ISA_EXT_ARR(ZICBOP), + KVM_ISA_EXT_ARR(ZICBOZ), + KVM_ISA_EXT_ARR(ZICCRSE), + KVM_ISA_EXT_ARR(ZICNTR), + KVM_ISA_EXT_ARR(ZICOND), + KVM_ISA_EXT_ARR(ZICSR), + KVM_ISA_EXT_ARR(ZIFENCEI), + KVM_ISA_EXT_ARR(ZIHINTNTL), + KVM_ISA_EXT_ARR(ZIHINTPAUSE), + KVM_ISA_EXT_ARR(ZIHPM), + KVM_ISA_EXT_ARR(ZILSD), + KVM_ISA_EXT_ARR(ZIMOP), + KVM_ISA_EXT_ARR(ZKND), + KVM_ISA_EXT_ARR(ZKNE), + KVM_ISA_EXT_ARR(ZKNH), + KVM_ISA_EXT_ARR(ZKR), + KVM_ISA_EXT_ARR(ZKSED), + KVM_ISA_EXT_ARR(ZKSH), + KVM_ISA_EXT_ARR(ZKT), + KVM_ISA_EXT_ARR(ZTSO), + KVM_ISA_EXT_ARR(ZVBB), + KVM_ISA_EXT_ARR(ZVBC), + KVM_ISA_EXT_ARR(ZVFBFMIN), + KVM_ISA_EXT_ARR(ZVFBFWMA), + KVM_ISA_EXT_ARR(ZVFH), + KVM_ISA_EXT_ARR(ZVFHMIN), + KVM_ISA_EXT_ARR(ZVKB), + KVM_ISA_EXT_ARR(ZVKG), + KVM_ISA_EXT_ARR(ZVKNED), + KVM_ISA_EXT_ARR(ZVKNHA), + KVM_ISA_EXT_ARR(ZVKNHB), + KVM_ISA_EXT_ARR(ZVKSED), + KVM_ISA_EXT_ARR(ZVKSH), + KVM_ISA_EXT_ARR(ZVKT), +}; + +unsigned long kvm_riscv_base2isa_ext(unsigned long base_ext) +{ + unsigned long i; + + for (i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) { + if (kvm_isa_ext_arr[i] == base_ext) + return i; + } + + return KVM_RISCV_ISA_EXT_MAX; +} + +int __kvm_riscv_isa_check_host(unsigned long kvm_ext, unsigned long *base_ext) +{ + unsigned long host_ext; + + if (kvm_ext >= KVM_RISCV_ISA_EXT_MAX || + kvm_ext >= ARRAY_SIZE(kvm_isa_ext_arr)) + return -ENOENT; + + kvm_ext = array_index_nospec(kvm_ext, ARRAY_SIZE(kvm_isa_ext_arr)); + switch (kvm_isa_ext_arr[kvm_ext]) { + case RISCV_ISA_EXT_SMNPM: + /* + * Pointer masking effective in (H)S-mode is provided by the + * Smnpm extension, so that extension is reported to the guest, + * even though the CSR bits for configuring VS-mode pointer + * masking on the host side are part of the Ssnpm extension. + */ + host_ext = RISCV_ISA_EXT_SSNPM; + break; + default: + host_ext = kvm_isa_ext_arr[kvm_ext]; + break; + } + + if (!__riscv_isa_extension_available(NULL, host_ext)) + return -ENOENT; + + if (base_ext) + *base_ext = kvm_isa_ext_arr[kvm_ext]; + + return 0; +} + +bool kvm_riscv_isa_enable_allowed(unsigned long ext) +{ + switch (ext) { + case KVM_RISCV_ISA_EXT_H: + return false; + case KVM_RISCV_ISA_EXT_SSCOFPMF: + /* Sscofpmf depends on interrupt filtering defined in ssaia */ + return !kvm_riscv_isa_check_host(SSAIA); + case KVM_RISCV_ISA_EXT_SVADU: + /* + * The henvcfg.ADUE is read-only zero if menvcfg.ADUE is zero. + * Guest OS can use Svadu only when host OS enable Svadu. + */ + return arch_has_hw_pte_young(); + case KVM_RISCV_ISA_EXT_V: + return riscv_v_vstate_ctrl_user_allowed(); + default: + break; + } + + return true; +} + +bool kvm_riscv_isa_disable_allowed(unsigned long ext) +{ + switch (ext) { + /* Extensions which don't have any mechanism to disable */ + case KVM_RISCV_ISA_EXT_A: + case KVM_RISCV_ISA_EXT_C: + case KVM_RISCV_ISA_EXT_I: + case KVM_RISCV_ISA_EXT_M: + /* There is not architectural config bit to disable sscofpmf completely */ + case KVM_RISCV_ISA_EXT_SSCOFPMF: + case KVM_RISCV_ISA_EXT_SSNPM: + case KVM_RISCV_ISA_EXT_SSTC: + case KVM_RISCV_ISA_EXT_SVINVAL: + case KVM_RISCV_ISA_EXT_SVNAPOT: + case KVM_RISCV_ISA_EXT_SVVPTC: + case KVM_RISCV_ISA_EXT_ZAAMO: + case KVM_RISCV_ISA_EXT_ZABHA: + case KVM_RISCV_ISA_EXT_ZACAS: + case KVM_RISCV_ISA_EXT_ZALASR: + case KVM_RISCV_ISA_EXT_ZALRSC: + case KVM_RISCV_ISA_EXT_ZAWRS: + case KVM_RISCV_ISA_EXT_ZBA: + case KVM_RISCV_ISA_EXT_ZBB: + case KVM_RISCV_ISA_EXT_ZBC: + case KVM_RISCV_ISA_EXT_ZBKB: + case KVM_RISCV_ISA_EXT_ZBKC: + case KVM_RISCV_ISA_EXT_ZBKX: + case KVM_RISCV_ISA_EXT_ZBS: + case KVM_RISCV_ISA_EXT_ZCA: + case KVM_RISCV_ISA_EXT_ZCB: + case KVM_RISCV_ISA_EXT_ZCD: + case KVM_RISCV_ISA_EXT_ZCF: + case KVM_RISCV_ISA_EXT_ZCMOP: + case KVM_RISCV_ISA_EXT_ZFA: + case KVM_RISCV_ISA_EXT_ZFBFMIN: + case KVM_RISCV_ISA_EXT_ZFH: + case KVM_RISCV_ISA_EXT_ZFHMIN: + case KVM_RISCV_ISA_EXT_ZICBOP: + case KVM_RISCV_ISA_EXT_ZICCRSE: + case KVM_RISCV_ISA_EXT_ZICNTR: + case KVM_RISCV_ISA_EXT_ZICOND: + case KVM_RISCV_ISA_EXT_ZICSR: + case KVM_RISCV_ISA_EXT_ZIFENCEI: + case KVM_RISCV_ISA_EXT_ZIHINTNTL: + case KVM_RISCV_ISA_EXT_ZIHINTPAUSE: + case KVM_RISCV_ISA_EXT_ZIHPM: + case KVM_RISCV_ISA_EXT_ZIMOP: + case KVM_RISCV_ISA_EXT_ZKND: + case KVM_RISCV_ISA_EXT_ZKNE: + case KVM_RISCV_ISA_EXT_ZKNH: + case KVM_RISCV_ISA_EXT_ZKR: + case KVM_RISCV_ISA_EXT_ZKSED: + case KVM_RISCV_ISA_EXT_ZKSH: + case KVM_RISCV_ISA_EXT_ZKT: + case KVM_RISCV_ISA_EXT_ZTSO: + case KVM_RISCV_ISA_EXT_ZVBB: + case KVM_RISCV_ISA_EXT_ZVBC: + case KVM_RISCV_ISA_EXT_ZVFBFMIN: + case KVM_RISCV_ISA_EXT_ZVFBFWMA: + case KVM_RISCV_ISA_EXT_ZVFH: + case KVM_RISCV_ISA_EXT_ZVFHMIN: + case KVM_RISCV_ISA_EXT_ZVKB: + case KVM_RISCV_ISA_EXT_ZVKG: + case KVM_RISCV_ISA_EXT_ZVKNED: + case KVM_RISCV_ISA_EXT_ZVKNHA: + case KVM_RISCV_ISA_EXT_ZVKNHB: + case KVM_RISCV_ISA_EXT_ZVKSED: + case KVM_RISCV_ISA_EXT_ZVKSH: + case KVM_RISCV_ISA_EXT_ZVKT: + return false; + /* Extensions which can be disabled using Smstateen */ + case KVM_RISCV_ISA_EXT_SSAIA: + return riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN); + case KVM_RISCV_ISA_EXT_SVADE: + /* + * The henvcfg.ADUE is read-only zero if menvcfg.ADUE is zero. + * Svade can't be disabled unless we support Svadu. + */ + return arch_has_hw_pte_young(); + default: + break; + } + + return true; +} diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c index 0f3fe3986fc0..cb8a65273c1f 100644 --- a/arch/riscv/kvm/main.c +++ b/arch/riscv/kvm/main.c @@ -41,8 +41,8 @@ int kvm_arch_enable_virtualization_cpu(void) if (rc) return rc; - csr_write(CSR_HEDELEG, KVM_HEDELEG_DEFAULT); - csr_write(CSR_HIDELEG, KVM_HIDELEG_DEFAULT); + csr_write(CSR_HEDELEG, 0); + csr_write(CSR_HIDELEG, 0); /* VS should access only the time counter directly. Everything else should trap */ csr_write(CSR_HCOUNTEREN, 0x02); @@ -105,17 +105,17 @@ static int __init riscv_kvm_init(void) return rc; kvm_riscv_gstage_mode_detect(); - switch (kvm_riscv_gstage_mode) { - case HGATP_MODE_SV32X4: + switch (kvm_riscv_gstage_max_pgd_levels) { + case 2: str = "Sv32x4"; break; - case HGATP_MODE_SV39X4: + case 3: str = "Sv39x4"; break; - case HGATP_MODE_SV48X4: + case 4: str = "Sv48x4"; break; - case HGATP_MODE_SV57X4: + case 5: str = "Sv57x4"; break; default: @@ -164,7 +164,7 @@ static int __init riscv_kvm_init(void) (rc) ? slist : "no features"); } - kvm_info("using %s G-stage page table format\n", str); + kvm_info("highest G-stage page table mode is %s\n", str); kvm_info("VMID %ld bits available\n", kvm_riscv_gstage_vmid_bits()); diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index 088d33ba90ed..2d3def024270 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -24,10 +24,7 @@ static void mmu_wp_memory_region(struct kvm *kvm, int slot) phys_addr_t end = (memslot->base_gfn + memslot->npages) << PAGE_SHIFT; struct kvm_gstage gstage; - gstage.kvm = kvm; - gstage.flags = 0; - gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); - gstage.pgd = kvm->arch.pgd; + kvm_riscv_gstage_init(&gstage, kvm); spin_lock(&kvm->mmu_lock); kvm_riscv_gstage_wp_range(&gstage, start, end); @@ -49,10 +46,7 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, struct kvm_gstage_mapping map; struct kvm_gstage gstage; - gstage.kvm = kvm; - gstage.flags = 0; - gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); - gstage.pgd = kvm->arch.pgd; + kvm_riscv_gstage_init(&gstage, kvm); end = (gpa + size + PAGE_SIZE - 1) & PAGE_MASK; pfn = __phys_to_pfn(hpa); @@ -67,7 +61,7 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, if (!writable) map.pte = pte_wrprotect(map.pte); - ret = kvm_mmu_topup_memory_cache(&pcache, kvm_riscv_gstage_pgd_levels); + ret = kvm_mmu_topup_memory_cache(&pcache, kvm->arch.pgd_levels); if (ret) goto out; @@ -89,10 +83,7 @@ void kvm_riscv_mmu_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size) { struct kvm_gstage gstage; - gstage.kvm = kvm; - gstage.flags = 0; - gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); - gstage.pgd = kvm->arch.pgd; + kvm_riscv_gstage_init(&gstage, kvm); spin_lock(&kvm->mmu_lock); kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false); @@ -109,10 +100,7 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm, phys_addr_t end = (base_gfn + __fls(mask) + 1) << PAGE_SHIFT; struct kvm_gstage gstage; - gstage.kvm = kvm; - gstage.flags = 0; - gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); - gstage.pgd = kvm->arch.pgd; + kvm_riscv_gstage_init(&gstage, kvm); kvm_riscv_gstage_wp_range(&gstage, start, end); } @@ -141,10 +129,7 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm, phys_addr_t size = slot->npages << PAGE_SHIFT; struct kvm_gstage gstage; - gstage.kvm = kvm; - gstage.flags = 0; - gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); - gstage.pgd = kvm->arch.pgd; + kvm_riscv_gstage_init(&gstage, kvm); spin_lock(&kvm->mmu_lock); kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false); @@ -186,7 +171,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, * space addressable by the KVM guest GPA space. */ if ((new->base_gfn + new->npages) >= - (kvm_riscv_gstage_gpa_size >> PAGE_SHIFT)) + kvm_riscv_gstage_gpa_size(kvm->arch.pgd_levels) >> PAGE_SHIFT) return -EFAULT; hva = new->userspace_addr; @@ -250,10 +235,7 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) if (!kvm->arch.pgd) return false; - gstage.kvm = kvm; - gstage.flags = 0; - gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); - gstage.pgd = kvm->arch.pgd; + kvm_riscv_gstage_init(&gstage, kvm); mmu_locked = spin_trylock(&kvm->mmu_lock); kvm_riscv_gstage_unmap_range(&gstage, range->start << PAGE_SHIFT, (range->end - range->start) << PAGE_SHIFT, @@ -275,10 +257,7 @@ bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) WARN_ON(size != PAGE_SIZE && size != PMD_SIZE && size != PUD_SIZE); - gstage.kvm = kvm; - gstage.flags = 0; - gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); - gstage.pgd = kvm->arch.pgd; + kvm_riscv_gstage_init(&gstage, kvm); if (!kvm_riscv_gstage_get_leaf(&gstage, range->start << PAGE_SHIFT, &ptep, &ptep_level)) return false; @@ -298,10 +277,7 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) WARN_ON(size != PAGE_SIZE && size != PMD_SIZE && size != PUD_SIZE); - gstage.kvm = kvm; - gstage.flags = 0; - gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); - gstage.pgd = kvm->arch.pgd; + kvm_riscv_gstage_init(&gstage, kvm); if (!kvm_riscv_gstage_get_leaf(&gstage, range->start << PAGE_SHIFT, &ptep, &ptep_level)) return false; @@ -463,16 +439,13 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, struct kvm_gstage gstage; struct page *page; - gstage.kvm = kvm; - gstage.flags = 0; - gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); - gstage.pgd = kvm->arch.pgd; + kvm_riscv_gstage_init(&gstage, kvm); /* Setup initial state of output mapping */ memset(out_map, 0, sizeof(*out_map)); /* We need minimum second+third level pages */ - ret = kvm_mmu_topup_memory_cache(pcache, kvm_riscv_gstage_pgd_levels); + ret = kvm_mmu_topup_memory_cache(pcache, kvm->arch.pgd_levels); if (ret) { kvm_err("Failed to topup G-stage cache\n"); return ret; @@ -575,6 +548,7 @@ int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm) return -ENOMEM; kvm->arch.pgd = page_to_virt(pgd_page); kvm->arch.pgd_phys = page_to_phys(pgd_page); + kvm->arch.pgd_levels = kvm_riscv_gstage_max_pgd_levels; return 0; } @@ -586,14 +560,13 @@ void kvm_riscv_mmu_free_pgd(struct kvm *kvm) spin_lock(&kvm->mmu_lock); if (kvm->arch.pgd) { - gstage.kvm = kvm; - gstage.flags = 0; - gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid); - gstage.pgd = kvm->arch.pgd; - kvm_riscv_gstage_unmap_range(&gstage, 0UL, kvm_riscv_gstage_gpa_size, false); + kvm_riscv_gstage_init(&gstage, kvm); + kvm_riscv_gstage_unmap_range(&gstage, 0UL, + kvm_riscv_gstage_gpa_size(kvm->arch.pgd_levels), false); pgd = READ_ONCE(kvm->arch.pgd); kvm->arch.pgd = NULL; kvm->arch.pgd_phys = 0; + kvm->arch.pgd_levels = 0; } spin_unlock(&kvm->mmu_lock); @@ -603,11 +576,12 @@ void kvm_riscv_mmu_free_pgd(struct kvm *kvm) void kvm_riscv_mmu_update_hgatp(struct kvm_vcpu *vcpu) { - unsigned long hgatp = kvm_riscv_gstage_mode << HGATP_MODE_SHIFT; - struct kvm_arch *k = &vcpu->kvm->arch; + struct kvm_arch *ka = &vcpu->kvm->arch; + unsigned long hgatp = kvm_riscv_gstage_mode(ka->pgd_levels) + << HGATP_MODE_SHIFT; - hgatp |= (READ_ONCE(k->vmid.vmid) << HGATP_VMID_SHIFT) & HGATP_VMID; - hgatp |= (k->pgd_phys >> PAGE_SHIFT) & HGATP_PPN; + hgatp |= (READ_ONCE(ka->vmid.vmid) << HGATP_VMID_SHIFT) & HGATP_VMID; + hgatp |= (ka->pgd_phys >> PAGE_SHIFT) & HGATP_PPN; ncsr_write(CSR_HGATP, hgatp); diff --git a/arch/riscv/kvm/tlb.c b/arch/riscv/kvm/tlb.c index ff1aeac4eb8e..993b25ea94d6 100644 --- a/arch/riscv/kvm/tlb.c +++ b/arch/riscv/kvm/tlb.c @@ -182,7 +182,7 @@ void kvm_riscv_local_tlb_sanitize(struct kvm_vcpu *vcpu) /* * Flush VS-stage TLB entries for implementation where VS-stage - * TLB does not cahce guest physical address and VMID. + * TLB does not cache guest physical address and VMID. */ if (static_branch_unlikely(&kvm_riscv_vsstage_tlb_no_gpa)) kvm_riscv_local_hfence_vvma_all(vmid); @@ -338,7 +338,8 @@ static void make_xfence_request(struct kvm *kvm, bitmap_zero(vcpu_mask, KVM_MAX_VCPUS); kvm_for_each_vcpu(i, vcpu, kvm) { if (hbase != -1UL) { - if (vcpu->vcpu_id < hbase) + if (vcpu->vcpu_id < hbase || + vcpu->vcpu_id >= hbase + BITS_PER_LONG) continue; if (!(hmask & (1UL << (vcpu->vcpu_id - hbase)))) continue; diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index fdd99ac1e714..a73690eda84b 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -24,6 +24,8 @@ #define CREATE_TRACE_POINTS #include "trace.h" +static DEFINE_PER_CPU(struct kvm_vcpu *, kvm_former_vcpu); + const struct kvm_stats_desc kvm_vcpu_stats_desc[] = { KVM_GENERIC_VCPU_STATS(), STATS_DESC_COUNTER(VCPU, ecall_exit_stat), @@ -133,10 +135,12 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) /* Mark this VCPU never ran */ vcpu->arch.ran_atleast_once = false; - vcpu->arch.cfg.hedeleg = KVM_HEDELEG_DEFAULT; vcpu->arch.mmu_page_cache.gfp_zero = __GFP_ZERO; bitmap_zero(vcpu->arch.isa, RISCV_ISA_EXT_MAX); + /* Setup VCPU config */ + kvm_riscv_vcpu_config_init(vcpu); + /* Setup ISA features available to VCPU */ kvm_riscv_vcpu_setup_isa(vcpu); @@ -529,57 +533,41 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_guest_debug *dbg) { - if (dbg->control & KVM_GUESTDBG_ENABLE) { + if (dbg->control & KVM_GUESTDBG_ENABLE) vcpu->guest_debug = dbg->control; - vcpu->arch.cfg.hedeleg &= ~BIT(EXC_BREAKPOINT); - } else { + else vcpu->guest_debug = 0; - vcpu->arch.cfg.hedeleg |= BIT(EXC_BREAKPOINT); - } return 0; } -static void kvm_riscv_vcpu_setup_config(struct kvm_vcpu *vcpu) -{ - const unsigned long *isa = vcpu->arch.isa; - struct kvm_vcpu_config *cfg = &vcpu->arch.cfg; - - if (riscv_isa_extension_available(isa, SVPBMT)) - cfg->henvcfg |= ENVCFG_PBMTE; - - if (riscv_isa_extension_available(isa, SSTC)) - cfg->henvcfg |= ENVCFG_STCE; - - if (riscv_isa_extension_available(isa, ZICBOM)) - cfg->henvcfg |= (ENVCFG_CBIE | ENVCFG_CBCFE); - - if (riscv_isa_extension_available(isa, ZICBOZ)) - cfg->henvcfg |= ENVCFG_CBZE; - - if (riscv_isa_extension_available(isa, SVADU) && - !riscv_isa_extension_available(isa, SVADE)) - cfg->henvcfg |= ENVCFG_ADUE; - - if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) { - cfg->hstateen0 |= SMSTATEEN0_HSENVCFG; - if (riscv_isa_extension_available(isa, SSAIA)) - cfg->hstateen0 |= SMSTATEEN0_AIA_IMSIC | - SMSTATEEN0_AIA | - SMSTATEEN0_AIA_ISEL; - if (riscv_isa_extension_available(isa, SMSTATEEN)) - cfg->hstateen0 |= SMSTATEEN0_SSTATEEN0; - } - - if (vcpu->guest_debug) - cfg->hedeleg &= ~BIT(EXC_BREAKPOINT); -} - void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { void *nsh; struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; - struct kvm_vcpu_config *cfg = &vcpu->arch.cfg; + + /* + * If VCPU is being reloaded on the same physical CPU and no + * other KVM VCPU has run on this CPU since it was last put, + * we can skip the expensive CSR and HGATP writes. + * + * Note: If a new CSR is added to this fast-path skip block, + * make sure that 'csr_dirty' is set to true in any + * ioctl (e.g., KVM_SET_ONE_REG) that modifies it. + */ + if (vcpu != __this_cpu_read(kvm_former_vcpu)) + __this_cpu_write(kvm_former_vcpu, vcpu); + else if (vcpu->arch.last_exit_cpu == cpu && !vcpu->arch.csr_dirty) + goto csr_restore_done; + + vcpu->arch.csr_dirty = false; + + /* + * Load VCPU config CSRs before other CSRs because + * the read/write behaviour of certain CSRs change + * based on VCPU config CSRs. + */ + kvm_riscv_vcpu_config_load(vcpu); if (kvm_riscv_nacl_sync_csr_available()) { nsh = nacl_shmem(); @@ -590,17 +578,8 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) nacl_csr_write(nsh, CSR_VSEPC, csr->vsepc); nacl_csr_write(nsh, CSR_VSCAUSE, csr->vscause); nacl_csr_write(nsh, CSR_VSTVAL, csr->vstval); - nacl_csr_write(nsh, CSR_HEDELEG, cfg->hedeleg); nacl_csr_write(nsh, CSR_HVIP, csr->hvip); nacl_csr_write(nsh, CSR_VSATP, csr->vsatp); - nacl_csr_write(nsh, CSR_HENVCFG, cfg->henvcfg); - if (IS_ENABLED(CONFIG_32BIT)) - nacl_csr_write(nsh, CSR_HENVCFGH, cfg->henvcfg >> 32); - if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) { - nacl_csr_write(nsh, CSR_HSTATEEN0, cfg->hstateen0); - if (IS_ENABLED(CONFIG_32BIT)) - nacl_csr_write(nsh, CSR_HSTATEEN0H, cfg->hstateen0 >> 32); - } } else { csr_write(CSR_VSSTATUS, csr->vsstatus); csr_write(CSR_VSIE, csr->vsie); @@ -609,21 +588,15 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) csr_write(CSR_VSEPC, csr->vsepc); csr_write(CSR_VSCAUSE, csr->vscause); csr_write(CSR_VSTVAL, csr->vstval); - csr_write(CSR_HEDELEG, cfg->hedeleg); csr_write(CSR_HVIP, csr->hvip); csr_write(CSR_VSATP, csr->vsatp); - csr_write(CSR_HENVCFG, cfg->henvcfg); - if (IS_ENABLED(CONFIG_32BIT)) - csr_write(CSR_HENVCFGH, cfg->henvcfg >> 32); - if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) { - csr_write(CSR_HSTATEEN0, cfg->hstateen0); - if (IS_ENABLED(CONFIG_32BIT)) - csr_write(CSR_HSTATEEN0H, cfg->hstateen0 >> 32); - } } kvm_riscv_mmu_update_hgatp(vcpu); + kvm_riscv_vcpu_aia_load(vcpu, cpu); + +csr_restore_done: kvm_riscv_vcpu_timer_restore(vcpu); kvm_riscv_vcpu_host_fp_save(&vcpu->arch.host_context); @@ -633,8 +606,6 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) kvm_riscv_vcpu_guest_vector_restore(&vcpu->arch.guest_context, vcpu->arch.isa); - kvm_riscv_vcpu_aia_load(vcpu, cpu); - kvm_make_request(KVM_REQ_STEAL_UPDATE, vcpu); vcpu->cpu = cpu; @@ -750,28 +721,22 @@ static __always_inline void kvm_riscv_vcpu_swap_in_guest_state(struct kvm_vcpu * { struct kvm_vcpu_smstateen_csr *smcsr = &vcpu->arch.smstateen_csr; struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; - struct kvm_vcpu_config *cfg = &vcpu->arch.cfg; vcpu->arch.host_scounteren = csr_swap(CSR_SCOUNTEREN, csr->scounteren); vcpu->arch.host_senvcfg = csr_swap(CSR_SENVCFG, csr->senvcfg); - if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN) && - (cfg->hstateen0 & SMSTATEEN0_SSTATEEN0)) - vcpu->arch.host_sstateen0 = csr_swap(CSR_SSTATEEN0, - smcsr->sstateen0); + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) + vcpu->arch.host_sstateen0 = csr_swap(CSR_SSTATEEN0, smcsr->sstateen0); } static __always_inline void kvm_riscv_vcpu_swap_in_host_state(struct kvm_vcpu *vcpu) { struct kvm_vcpu_smstateen_csr *smcsr = &vcpu->arch.smstateen_csr; struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; - struct kvm_vcpu_config *cfg = &vcpu->arch.cfg; csr->scounteren = csr_swap(CSR_SCOUNTEREN, vcpu->arch.host_scounteren); csr->senvcfg = csr_swap(CSR_SENVCFG, vcpu->arch.host_senvcfg); - if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN) && - (cfg->hstateen0 & SMSTATEEN0_SSTATEEN0)) - smcsr->sstateen0 = csr_swap(CSR_SSTATEEN0, - vcpu->arch.host_sstateen0); + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) + smcsr->sstateen0 = csr_swap(CSR_SSTATEEN0, vcpu->arch.host_sstateen0); } /* @@ -868,7 +833,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) struct kvm_run *run = vcpu->run; if (!vcpu->arch.ran_atleast_once) - kvm_riscv_vcpu_setup_config(vcpu); + kvm_riscv_vcpu_config_ran_once(vcpu); /* Mark this VCPU ran at least once */ vcpu->arch.ran_atleast_once = true; diff --git a/arch/riscv/kvm/vcpu_config.c b/arch/riscv/kvm/vcpu_config.c new file mode 100644 index 000000000000..238418fed2b9 --- /dev/null +++ b/arch/riscv/kvm/vcpu_config.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026 Qualcomm Technologies, Inc. + */ + +#include +#include + +#define KVM_HEDELEG_DEFAULT (BIT(EXC_INST_MISALIGNED) | \ + BIT(EXC_INST_ILLEGAL) | \ + BIT(EXC_BREAKPOINT) | \ + BIT(EXC_SYSCALL) | \ + BIT(EXC_INST_PAGE_FAULT) | \ + BIT(EXC_LOAD_PAGE_FAULT) | \ + BIT(EXC_STORE_PAGE_FAULT)) + +#define KVM_HIDELEG_DEFAULT (BIT(IRQ_VS_SOFT) | \ + BIT(IRQ_VS_TIMER) | \ + BIT(IRQ_VS_EXT)) + +void kvm_riscv_vcpu_config_init(struct kvm_vcpu *vcpu) +{ + vcpu->arch.cfg.hedeleg = KVM_HEDELEG_DEFAULT; + vcpu->arch.cfg.hideleg = KVM_HIDELEG_DEFAULT; +} + +void kvm_riscv_vcpu_config_guest_debug(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_config *cfg = &vcpu->arch.cfg; + + if (vcpu->guest_debug) + cfg->hedeleg &= ~BIT(EXC_BREAKPOINT); + else + cfg->hedeleg |= BIT(EXC_BREAKPOINT); + + vcpu->arch.csr_dirty = true; +} + +void kvm_riscv_vcpu_config_ran_once(struct kvm_vcpu *vcpu) +{ + const unsigned long *isa = vcpu->arch.isa; + struct kvm_vcpu_config *cfg = &vcpu->arch.cfg; + + if (riscv_isa_extension_available(isa, SVPBMT)) + cfg->henvcfg |= ENVCFG_PBMTE; + + if (riscv_isa_extension_available(isa, SSTC)) + cfg->henvcfg |= ENVCFG_STCE; + + if (riscv_isa_extension_available(isa, ZICBOM)) + cfg->henvcfg |= (ENVCFG_CBIE | ENVCFG_CBCFE); + + if (riscv_isa_extension_available(isa, ZICBOZ)) + cfg->henvcfg |= ENVCFG_CBZE; + + if (riscv_isa_extension_available(isa, SVADU) && + !riscv_isa_extension_available(isa, SVADE)) + cfg->henvcfg |= ENVCFG_ADUE; + + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) { + cfg->hstateen0 |= SMSTATEEN0_HSENVCFG; + if (riscv_isa_extension_available(isa, SSAIA)) + cfg->hstateen0 |= SMSTATEEN0_AIA_IMSIC | + SMSTATEEN0_AIA | + SMSTATEEN0_AIA_ISEL; + if (riscv_isa_extension_available(isa, SMSTATEEN)) + cfg->hstateen0 |= SMSTATEEN0_SSTATEEN0; + } + + if (vcpu->guest_debug) + cfg->hedeleg &= ~BIT(EXC_BREAKPOINT); +} + +void kvm_riscv_vcpu_config_load(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_config *cfg = &vcpu->arch.cfg; + void *nsh; + + if (kvm_riscv_nacl_sync_csr_available()) { + nsh = nacl_shmem(); + nacl_csr_write(nsh, CSR_HEDELEG, cfg->hedeleg); + nacl_csr_write(nsh, CSR_HIDELEG, cfg->hideleg); + nacl_csr_write(nsh, CSR_HENVCFG, cfg->henvcfg); + if (IS_ENABLED(CONFIG_32BIT)) + nacl_csr_write(nsh, CSR_HENVCFGH, cfg->henvcfg >> 32); + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) { + nacl_csr_write(nsh, CSR_HSTATEEN0, cfg->hstateen0); + if (IS_ENABLED(CONFIG_32BIT)) + nacl_csr_write(nsh, CSR_HSTATEEN0H, cfg->hstateen0 >> 32); + } + } else { + csr_write(CSR_HEDELEG, cfg->hedeleg); + csr_write(CSR_HIDELEG, cfg->hideleg); + csr_write(CSR_HENVCFG, cfg->henvcfg); + if (IS_ENABLED(CONFIG_32BIT)) + csr_write(CSR_HENVCFGH, cfg->henvcfg >> 32); + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN)) { + csr_write(CSR_HSTATEEN0, cfg->hstateen0); + if (IS_ENABLED(CONFIG_32BIT)) + csr_write(CSR_HSTATEEN0H, cfg->hstateen0 >> 32); + } + } +} diff --git a/arch/riscv/kvm/vcpu_fp.c b/arch/riscv/kvm/vcpu_fp.c index bd5a9e7e7165..6ad6df26a2fd 100644 --- a/arch/riscv/kvm/vcpu_fp.c +++ b/arch/riscv/kvm/vcpu_fp.c @@ -13,6 +13,7 @@ #include #include #include +#include #ifdef CONFIG_FPU void kvm_riscv_vcpu_fp_reset(struct kvm_vcpu *vcpu) @@ -60,17 +61,17 @@ void kvm_riscv_vcpu_guest_fp_restore(struct kvm_cpu_context *cntx, void kvm_riscv_vcpu_host_fp_save(struct kvm_cpu_context *cntx) { /* No need to check host sstatus as it can be modified outside */ - if (riscv_isa_extension_available(NULL, d)) + if (!kvm_riscv_isa_check_host(D)) __kvm_riscv_fp_d_save(cntx); - else if (riscv_isa_extension_available(NULL, f)) + else if (!kvm_riscv_isa_check_host(F)) __kvm_riscv_fp_f_save(cntx); } void kvm_riscv_vcpu_host_fp_restore(struct kvm_cpu_context *cntx) { - if (riscv_isa_extension_available(NULL, d)) + if (!kvm_riscv_isa_check_host(D)) __kvm_riscv_fp_d_restore(cntx); - else if (riscv_isa_extension_available(NULL, f)) + else if (!kvm_riscv_isa_check_host(F)) __kvm_riscv_fp_f_restore(cntx); } #endif diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c index 45ecc0082e90..bb920e8923c9 100644 --- a/arch/riscv/kvm/vcpu_onereg.c +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -15,259 +15,19 @@ #include #include #include +#include #include -#include -#include #define KVM_RISCV_BASE_ISA_MASK GENMASK(25, 0) -#define KVM_ISA_EXT_ARR(ext) \ -[KVM_RISCV_ISA_EXT_##ext] = RISCV_ISA_EXT_##ext - -/* Mapping between KVM ISA Extension ID & guest ISA extension ID */ -static const unsigned long kvm_isa_ext_arr[] = { - /* Single letter extensions (alphabetically sorted) */ - [KVM_RISCV_ISA_EXT_A] = RISCV_ISA_EXT_a, - [KVM_RISCV_ISA_EXT_C] = RISCV_ISA_EXT_c, - [KVM_RISCV_ISA_EXT_D] = RISCV_ISA_EXT_d, - [KVM_RISCV_ISA_EXT_F] = RISCV_ISA_EXT_f, - [KVM_RISCV_ISA_EXT_H] = RISCV_ISA_EXT_h, - [KVM_RISCV_ISA_EXT_I] = RISCV_ISA_EXT_i, - [KVM_RISCV_ISA_EXT_M] = RISCV_ISA_EXT_m, - [KVM_RISCV_ISA_EXT_V] = RISCV_ISA_EXT_v, - /* Multi letter extensions (alphabetically sorted) */ - KVM_ISA_EXT_ARR(SMNPM), - KVM_ISA_EXT_ARR(SMSTATEEN), - KVM_ISA_EXT_ARR(SSAIA), - KVM_ISA_EXT_ARR(SSCOFPMF), - KVM_ISA_EXT_ARR(SSNPM), - KVM_ISA_EXT_ARR(SSTC), - KVM_ISA_EXT_ARR(SVADE), - KVM_ISA_EXT_ARR(SVADU), - KVM_ISA_EXT_ARR(SVINVAL), - KVM_ISA_EXT_ARR(SVNAPOT), - KVM_ISA_EXT_ARR(SVPBMT), - KVM_ISA_EXT_ARR(SVVPTC), - KVM_ISA_EXT_ARR(ZAAMO), - KVM_ISA_EXT_ARR(ZABHA), - KVM_ISA_EXT_ARR(ZACAS), - KVM_ISA_EXT_ARR(ZALASR), - KVM_ISA_EXT_ARR(ZALRSC), - KVM_ISA_EXT_ARR(ZAWRS), - KVM_ISA_EXT_ARR(ZBA), - KVM_ISA_EXT_ARR(ZBB), - KVM_ISA_EXT_ARR(ZBC), - KVM_ISA_EXT_ARR(ZBKB), - KVM_ISA_EXT_ARR(ZBKC), - KVM_ISA_EXT_ARR(ZBKX), - KVM_ISA_EXT_ARR(ZBS), - KVM_ISA_EXT_ARR(ZCA), - KVM_ISA_EXT_ARR(ZCB), - KVM_ISA_EXT_ARR(ZCD), - KVM_ISA_EXT_ARR(ZCF), - KVM_ISA_EXT_ARR(ZCLSD), - KVM_ISA_EXT_ARR(ZCMOP), - KVM_ISA_EXT_ARR(ZFA), - KVM_ISA_EXT_ARR(ZFBFMIN), - KVM_ISA_EXT_ARR(ZFH), - KVM_ISA_EXT_ARR(ZFHMIN), - KVM_ISA_EXT_ARR(ZICBOM), - KVM_ISA_EXT_ARR(ZICBOP), - KVM_ISA_EXT_ARR(ZICBOZ), - KVM_ISA_EXT_ARR(ZICCRSE), - KVM_ISA_EXT_ARR(ZICNTR), - KVM_ISA_EXT_ARR(ZICOND), - KVM_ISA_EXT_ARR(ZICSR), - KVM_ISA_EXT_ARR(ZIFENCEI), - KVM_ISA_EXT_ARR(ZIHINTNTL), - KVM_ISA_EXT_ARR(ZIHINTPAUSE), - KVM_ISA_EXT_ARR(ZIHPM), - KVM_ISA_EXT_ARR(ZILSD), - KVM_ISA_EXT_ARR(ZIMOP), - KVM_ISA_EXT_ARR(ZKND), - KVM_ISA_EXT_ARR(ZKNE), - KVM_ISA_EXT_ARR(ZKNH), - KVM_ISA_EXT_ARR(ZKR), - KVM_ISA_EXT_ARR(ZKSED), - KVM_ISA_EXT_ARR(ZKSH), - KVM_ISA_EXT_ARR(ZKT), - KVM_ISA_EXT_ARR(ZTSO), - KVM_ISA_EXT_ARR(ZVBB), - KVM_ISA_EXT_ARR(ZVBC), - KVM_ISA_EXT_ARR(ZVFBFMIN), - KVM_ISA_EXT_ARR(ZVFBFWMA), - KVM_ISA_EXT_ARR(ZVFH), - KVM_ISA_EXT_ARR(ZVFHMIN), - KVM_ISA_EXT_ARR(ZVKB), - KVM_ISA_EXT_ARR(ZVKG), - KVM_ISA_EXT_ARR(ZVKNED), - KVM_ISA_EXT_ARR(ZVKNHA), - KVM_ISA_EXT_ARR(ZVKNHB), - KVM_ISA_EXT_ARR(ZVKSED), - KVM_ISA_EXT_ARR(ZVKSH), - KVM_ISA_EXT_ARR(ZVKT), -}; - -static unsigned long kvm_riscv_vcpu_base2isa_ext(unsigned long base_ext) -{ - unsigned long i; - - for (i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) { - if (kvm_isa_ext_arr[i] == base_ext) - return i; - } - - return KVM_RISCV_ISA_EXT_MAX; -} - -static int kvm_riscv_vcpu_isa_check_host(unsigned long kvm_ext, unsigned long *guest_ext) -{ - unsigned long host_ext; - - if (kvm_ext >= KVM_RISCV_ISA_EXT_MAX || - kvm_ext >= ARRAY_SIZE(kvm_isa_ext_arr)) - return -ENOENT; - - kvm_ext = array_index_nospec(kvm_ext, ARRAY_SIZE(kvm_isa_ext_arr)); - *guest_ext = kvm_isa_ext_arr[kvm_ext]; - switch (*guest_ext) { - case RISCV_ISA_EXT_SMNPM: - /* - * Pointer masking effective in (H)S-mode is provided by the - * Smnpm extension, so that extension is reported to the guest, - * even though the CSR bits for configuring VS-mode pointer - * masking on the host side are part of the Ssnpm extension. - */ - host_ext = RISCV_ISA_EXT_SSNPM; - break; - default: - host_ext = *guest_ext; - break; - } - - if (!__riscv_isa_extension_available(NULL, host_ext)) - return -ENOENT; - - return 0; -} - -static bool kvm_riscv_vcpu_isa_enable_allowed(unsigned long ext) -{ - switch (ext) { - case KVM_RISCV_ISA_EXT_H: - return false; - case KVM_RISCV_ISA_EXT_SSCOFPMF: - /* Sscofpmf depends on interrupt filtering defined in ssaia */ - return __riscv_isa_extension_available(NULL, RISCV_ISA_EXT_SSAIA); - case KVM_RISCV_ISA_EXT_SVADU: - /* - * The henvcfg.ADUE is read-only zero if menvcfg.ADUE is zero. - * Guest OS can use Svadu only when host OS enable Svadu. - */ - return arch_has_hw_pte_young(); - case KVM_RISCV_ISA_EXT_V: - return riscv_v_vstate_ctrl_user_allowed(); - default: - break; - } - - return true; -} - -static bool kvm_riscv_vcpu_isa_disable_allowed(unsigned long ext) -{ - switch (ext) { - /* Extensions which don't have any mechanism to disable */ - case KVM_RISCV_ISA_EXT_A: - case KVM_RISCV_ISA_EXT_C: - case KVM_RISCV_ISA_EXT_I: - case KVM_RISCV_ISA_EXT_M: - /* There is not architectural config bit to disable sscofpmf completely */ - case KVM_RISCV_ISA_EXT_SSCOFPMF: - case KVM_RISCV_ISA_EXT_SSNPM: - case KVM_RISCV_ISA_EXT_SSTC: - case KVM_RISCV_ISA_EXT_SVINVAL: - case KVM_RISCV_ISA_EXT_SVNAPOT: - case KVM_RISCV_ISA_EXT_SVVPTC: - case KVM_RISCV_ISA_EXT_ZAAMO: - case KVM_RISCV_ISA_EXT_ZABHA: - case KVM_RISCV_ISA_EXT_ZACAS: - case KVM_RISCV_ISA_EXT_ZALASR: - case KVM_RISCV_ISA_EXT_ZALRSC: - case KVM_RISCV_ISA_EXT_ZAWRS: - case KVM_RISCV_ISA_EXT_ZBA: - case KVM_RISCV_ISA_EXT_ZBB: - case KVM_RISCV_ISA_EXT_ZBC: - case KVM_RISCV_ISA_EXT_ZBKB: - case KVM_RISCV_ISA_EXT_ZBKC: - case KVM_RISCV_ISA_EXT_ZBKX: - case KVM_RISCV_ISA_EXT_ZBS: - case KVM_RISCV_ISA_EXT_ZCA: - case KVM_RISCV_ISA_EXT_ZCB: - case KVM_RISCV_ISA_EXT_ZCD: - case KVM_RISCV_ISA_EXT_ZCF: - case KVM_RISCV_ISA_EXT_ZCMOP: - case KVM_RISCV_ISA_EXT_ZFA: - case KVM_RISCV_ISA_EXT_ZFBFMIN: - case KVM_RISCV_ISA_EXT_ZFH: - case KVM_RISCV_ISA_EXT_ZFHMIN: - case KVM_RISCV_ISA_EXT_ZICBOP: - case KVM_RISCV_ISA_EXT_ZICCRSE: - case KVM_RISCV_ISA_EXT_ZICNTR: - case KVM_RISCV_ISA_EXT_ZICOND: - case KVM_RISCV_ISA_EXT_ZICSR: - case KVM_RISCV_ISA_EXT_ZIFENCEI: - case KVM_RISCV_ISA_EXT_ZIHINTNTL: - case KVM_RISCV_ISA_EXT_ZIHINTPAUSE: - case KVM_RISCV_ISA_EXT_ZIHPM: - case KVM_RISCV_ISA_EXT_ZIMOP: - case KVM_RISCV_ISA_EXT_ZKND: - case KVM_RISCV_ISA_EXT_ZKNE: - case KVM_RISCV_ISA_EXT_ZKNH: - case KVM_RISCV_ISA_EXT_ZKR: - case KVM_RISCV_ISA_EXT_ZKSED: - case KVM_RISCV_ISA_EXT_ZKSH: - case KVM_RISCV_ISA_EXT_ZKT: - case KVM_RISCV_ISA_EXT_ZTSO: - case KVM_RISCV_ISA_EXT_ZVBB: - case KVM_RISCV_ISA_EXT_ZVBC: - case KVM_RISCV_ISA_EXT_ZVFBFMIN: - case KVM_RISCV_ISA_EXT_ZVFBFWMA: - case KVM_RISCV_ISA_EXT_ZVFH: - case KVM_RISCV_ISA_EXT_ZVFHMIN: - case KVM_RISCV_ISA_EXT_ZVKB: - case KVM_RISCV_ISA_EXT_ZVKG: - case KVM_RISCV_ISA_EXT_ZVKNED: - case KVM_RISCV_ISA_EXT_ZVKNHA: - case KVM_RISCV_ISA_EXT_ZVKNHB: - case KVM_RISCV_ISA_EXT_ZVKSED: - case KVM_RISCV_ISA_EXT_ZVKSH: - case KVM_RISCV_ISA_EXT_ZVKT: - return false; - /* Extensions which can be disabled using Smstateen */ - case KVM_RISCV_ISA_EXT_SSAIA: - return riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN); - case KVM_RISCV_ISA_EXT_SVADE: - /* - * The henvcfg.ADUE is read-only zero if menvcfg.ADUE is zero. - * Svade can't be disabled unless we support Svadu. - */ - return arch_has_hw_pte_young(); - default: - break; - } - - return true; -} - void kvm_riscv_vcpu_setup_isa(struct kvm_vcpu *vcpu) { unsigned long guest_ext, i; - for (i = 0; i < ARRAY_SIZE(kvm_isa_ext_arr); i++) { - if (kvm_riscv_vcpu_isa_check_host(i, &guest_ext)) + for (i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) { + if (__kvm_riscv_isa_check_host(i, &guest_ext)) continue; - if (kvm_riscv_vcpu_isa_enable_allowed(i)) + if (kvm_riscv_isa_enable_allowed(i)) set_bit(guest_ext, vcpu->arch.isa); } } @@ -290,17 +50,17 @@ static int kvm_riscv_vcpu_get_reg_config(struct kvm_vcpu *vcpu, reg_val = vcpu->arch.isa[0] & KVM_RISCV_BASE_ISA_MASK; break; case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size): - if (!riscv_isa_extension_available(NULL, ZICBOM)) + if (kvm_riscv_isa_check_host(ZICBOM)) return -ENOENT; reg_val = riscv_cbom_block_size; break; case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size): - if (!riscv_isa_extension_available(NULL, ZICBOZ)) + if (kvm_riscv_isa_check_host(ZICBOZ)) return -ENOENT; reg_val = riscv_cboz_block_size; break; case KVM_REG_RISCV_CONFIG_REG(zicbop_block_size): - if (!riscv_isa_extension_available(NULL, ZICBOP)) + if (kvm_riscv_isa_check_host(ZICBOP)) return -ENOENT; reg_val = riscv_cbop_block_size; break; @@ -361,15 +121,15 @@ static int kvm_riscv_vcpu_set_reg_config(struct kvm_vcpu *vcpu, if (!vcpu->arch.ran_atleast_once) { /* Ignore the enable/disable request for certain extensions */ for (i = 0; i < RISCV_ISA_EXT_BASE; i++) { - isa_ext = kvm_riscv_vcpu_base2isa_ext(i); + isa_ext = kvm_riscv_base2isa_ext(i); if (isa_ext >= KVM_RISCV_ISA_EXT_MAX) { reg_val &= ~BIT(i); continue; } - if (!kvm_riscv_vcpu_isa_enable_allowed(isa_ext)) + if (!kvm_riscv_isa_enable_allowed(isa_ext)) if (reg_val & BIT(i)) reg_val &= ~BIT(i); - if (!kvm_riscv_vcpu_isa_disable_allowed(isa_ext)) + if (!kvm_riscv_isa_disable_allowed(isa_ext)) if (!(reg_val & BIT(i))) reg_val |= BIT(i); } @@ -384,19 +144,19 @@ static int kvm_riscv_vcpu_set_reg_config(struct kvm_vcpu *vcpu, } break; case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size): - if (!riscv_isa_extension_available(NULL, ZICBOM)) + if (kvm_riscv_isa_check_host(ZICBOM)) return -ENOENT; if (reg_val != riscv_cbom_block_size) return -EINVAL; break; case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size): - if (!riscv_isa_extension_available(NULL, ZICBOZ)) + if (kvm_riscv_isa_check_host(ZICBOZ)) return -ENOENT; if (reg_val != riscv_cboz_block_size) return -EINVAL; break; case KVM_REG_RISCV_CONFIG_REG(zicbop_block_size): - if (!riscv_isa_extension_available(NULL, ZICBOP)) + if (kvm_riscv_isa_check_host(ZICBOP)) return -ENOENT; if (reg_val != riscv_cbop_block_size) return -EINVAL; @@ -670,6 +430,8 @@ static int kvm_riscv_vcpu_set_reg_csr(struct kvm_vcpu *vcpu, if (rc) return rc; + vcpu->arch.csr_dirty = true; + return 0; } @@ -680,7 +442,7 @@ static int riscv_vcpu_get_isa_ext_single(struct kvm_vcpu *vcpu, unsigned long guest_ext; int ret; - ret = kvm_riscv_vcpu_isa_check_host(reg_num, &guest_ext); + ret = __kvm_riscv_isa_check_host(reg_num, &guest_ext); if (ret) return ret; @@ -698,7 +460,7 @@ static int riscv_vcpu_set_isa_ext_single(struct kvm_vcpu *vcpu, unsigned long guest_ext; int ret; - ret = kvm_riscv_vcpu_isa_check_host(reg_num, &guest_ext); + ret = __kvm_riscv_isa_check_host(reg_num, &guest_ext); if (ret) return ret; @@ -711,10 +473,10 @@ static int riscv_vcpu_set_isa_ext_single(struct kvm_vcpu *vcpu, * extension can be disabled */ if (reg_val == 1 && - kvm_riscv_vcpu_isa_enable_allowed(reg_num)) + kvm_riscv_isa_enable_allowed(reg_num)) set_bit(guest_ext, vcpu->arch.isa); else if (!reg_val && - kvm_riscv_vcpu_isa_disable_allowed(reg_num)) + kvm_riscv_isa_disable_allowed(reg_num)) clear_bit(guest_ext, vcpu->arch.isa); else return -EINVAL; @@ -857,13 +619,13 @@ static int copy_config_reg_indices(const struct kvm_vcpu *vcpu, * was not available. */ if (i == KVM_REG_RISCV_CONFIG_REG(zicbom_block_size) && - !riscv_isa_extension_available(NULL, ZICBOM)) + kvm_riscv_isa_check_host(ZICBOM)) continue; else if (i == KVM_REG_RISCV_CONFIG_REG(zicboz_block_size) && - !riscv_isa_extension_available(NULL, ZICBOZ)) + kvm_riscv_isa_check_host(ZICBOZ)) continue; else if (i == KVM_REG_RISCV_CONFIG_REG(zicbop_block_size) && - !riscv_isa_extension_available(NULL, ZICBOP)) + kvm_riscv_isa_check_host(ZICBOP)) continue; size = IS_ENABLED(CONFIG_32BIT) ? KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64; @@ -1084,7 +846,7 @@ static int copy_isa_ext_reg_indices(const struct kvm_vcpu *vcpu, KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64; u64 reg = KVM_REG_RISCV | size | KVM_REG_RISCV_ISA_EXT | i; - if (kvm_riscv_vcpu_isa_check_host(i, &guest_ext)) + if (__kvm_riscv_isa_check_host(i, &guest_ext)) continue; if (uindices) { diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c index e873430e596b..a935ed96bc17 100644 --- a/arch/riscv/kvm/vcpu_pmu.c +++ b/arch/riscv/kvm/vcpu_pmu.c @@ -7,16 +7,17 @@ */ #define pr_fmt(fmt) "riscv-kvm-pmu: " fmt +#include #include #include #include #include #include #include +#include #include #include #include -#include #define kvm_pmu_num_counters(pmu) ((pmu)->num_hw_ctrs + (pmu)->num_fw_ctrs) #define get_event_type(x) (((x) & SBI_PMU_EVENT_IDX_TYPE_MASK) >> 16) @@ -226,7 +227,14 @@ static int pmu_fw_ctr_read_hi(struct kvm_vcpu *vcpu, unsigned long cidx, if (pmc->cinfo.type != SBI_PMU_CTR_TYPE_FW) return -EINVAL; + if (pmc->event_idx == SBI_PMU_EVENT_IDX_INVALID) + return -EINVAL; + fevent_code = get_event_code(pmc->event_idx); + if (WARN_ONCE(fevent_code >= SBI_PMU_FW_MAX, + "Invalid firmware event code: %d\n", fevent_code)) + return -EINVAL; + pmc->counter_val = kvpmu->fw_event[fevent_code].value; *out_val = pmc->counter_val >> 32; @@ -251,7 +259,14 @@ static int pmu_ctr_read(struct kvm_vcpu *vcpu, unsigned long cidx, pmc = &kvpmu->pmc[cidx]; if (pmc->cinfo.type == SBI_PMU_CTR_TYPE_FW) { + if (pmc->event_idx == SBI_PMU_EVENT_IDX_INVALID) + return -EINVAL; + fevent_code = get_event_code(pmc->event_idx); + if (WARN_ONCE(fevent_code >= SBI_PMU_FW_MAX, + "Invalid firmware event code: %d\n", fevent_code)) + return -EINVAL; + pmc->counter_val = kvpmu->fw_event[fevent_code].value; } else if (pmc->perf_event) { pmc->counter_val += perf_event_read_value(pmc->perf_event, &enabled, &running); @@ -266,8 +281,10 @@ static int pmu_ctr_read(struct kvm_vcpu *vcpu, unsigned long cidx, static int kvm_pmu_validate_counter_mask(struct kvm_pmu *kvpmu, unsigned long ctr_base, unsigned long ctr_mask) { - /* Make sure the we have a valid counter mask requested from the caller */ - if (!ctr_mask || (ctr_base + __fls(ctr_mask) >= kvm_pmu_num_counters(kvpmu))) + unsigned long num_ctrs = kvm_pmu_num_counters(kvpmu); + + /* Make sure we have a valid counter mask requested from the caller */ + if (!ctr_mask || ctr_base >= num_ctrs || (ctr_base + __fls(ctr_mask) >= num_ctrs)) return -EINVAL; return 0; @@ -427,11 +444,12 @@ int kvm_riscv_vcpu_pmu_snapshot_set_shmem(struct kvm_vcpu *vcpu, unsigned long s saddr = saddr_low; if (saddr_high != 0) { - if (IS_ENABLED(CONFIG_32BIT)) + if (IS_ENABLED(CONFIG_32BIT)) { saddr |= ((gpa_t)saddr_high << 32); - else + } else { sbiret = SBI_ERR_INVALID_ADDRESS; - goto out; + goto out; + } } kvpmu->sdata = kzalloc(snapshot_area_size, GFP_ATOMIC); @@ -441,6 +459,7 @@ int kvm_riscv_vcpu_pmu_snapshot_set_shmem(struct kvm_vcpu *vcpu, unsigned long s /* No need to check writable slot explicitly as kvm_vcpu_write_guest does it internally */ if (kvm_vcpu_write_guest(vcpu, saddr, kvpmu->sdata, snapshot_area_size)) { kfree(kvpmu->sdata); + kvpmu->sdata = NULL; sbiret = SBI_ERR_INVALID_ADDRESS; goto out; } @@ -675,7 +694,7 @@ int kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu, unsigned long ctr_base, pmc->counter_val += perf_event_read_value(pmc->perf_event, &enabled, &running); /* - * The counter and overflow indicies in the snapshot region are w.r.to + * The counter and overflow indices in the snapshot region are w.r.to * cbase. Modify the set bit in the counter mask instead of the pmc_index * which indicates the absolute counter index. */ @@ -827,7 +846,7 @@ void kvm_riscv_vcpu_pmu_init(struct kvm_vcpu *vcpu) * filtering is available in the host. Otherwise, guest will always count * events while the execution is in hypervisor mode. */ - if (!riscv_isa_extension_available(NULL, SSCOFPMF)) + if (kvm_riscv_isa_check_host(SSCOFPMF)) return; ret = riscv_pmu_get_hpm_info(&hpm_width, &num_hw_ctrs); diff --git a/arch/riscv/kvm/vcpu_sbi_sta.c b/arch/riscv/kvm/vcpu_sbi_sta.c index afa0545c3bcf..3b834709b429 100644 --- a/arch/riscv/kvm/vcpu_sbi_sta.c +++ b/arch/riscv/kvm/vcpu_sbi_sta.c @@ -181,6 +181,7 @@ static int kvm_sbi_ext_sta_set_reg(struct kvm_vcpu *vcpu, unsigned long reg_num, unsigned long reg_size, const void *reg_val) { unsigned long value; + gpa_t new_shmem = INVALID_GPA; if (reg_size != sizeof(unsigned long)) return -EINVAL; @@ -191,18 +192,18 @@ static int kvm_sbi_ext_sta_set_reg(struct kvm_vcpu *vcpu, unsigned long reg_num, if (IS_ENABLED(CONFIG_32BIT)) { gpa_t hi = upper_32_bits(vcpu->arch.sta.shmem); - vcpu->arch.sta.shmem = value; - vcpu->arch.sta.shmem |= hi << 32; + new_shmem = value; + new_shmem |= hi << 32; } else { - vcpu->arch.sta.shmem = value; + new_shmem = value; } break; case KVM_REG_RISCV_SBI_STA_REG(shmem_hi): if (IS_ENABLED(CONFIG_32BIT)) { gpa_t lo = lower_32_bits(vcpu->arch.sta.shmem); - vcpu->arch.sta.shmem = ((gpa_t)value << 32); - vcpu->arch.sta.shmem |= lo; + new_shmem = ((gpa_t)value << 32); + new_shmem |= lo; } else if (value != 0) { return -EINVAL; } @@ -211,6 +212,11 @@ static int kvm_sbi_ext_sta_set_reg(struct kvm_vcpu *vcpu, unsigned long reg_num, return -ENOENT; } + if (new_shmem != INVALID_GPA && !IS_ALIGNED(new_shmem, 64)) + return -EINVAL; + + vcpu->arch.sta.shmem = new_shmem; + return 0; } diff --git a/arch/riscv/kvm/vcpu_timer.c b/arch/riscv/kvm/vcpu_timer.c index f36247e4c783..9817ff802821 100644 --- a/arch/riscv/kvm/vcpu_timer.c +++ b/arch/riscv/kvm/vcpu_timer.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -253,7 +254,7 @@ int kvm_riscv_vcpu_timer_init(struct kvm_vcpu *vcpu) t->next_set = false; /* Enable sstc for every vcpu if available in hardware */ - if (riscv_isa_extension_available(NULL, SSTC)) { + if (!kvm_riscv_isa_check_host(SSTC)) { t->sstc_enabled = true; hrtimer_setup(&t->hrt, kvm_riscv_vcpu_vstimer_expired, CLOCK_MONOTONIC, HRTIMER_MODE_REL); diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c index 05f3cc2d8e31..62d2fb77bb9b 100644 --- a/arch/riscv/kvm/vcpu_vector.c +++ b/arch/riscv/kvm/vcpu_vector.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -63,13 +64,13 @@ void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx, void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx) { /* No need to check host sstatus as it can be modified outside */ - if (riscv_isa_extension_available(NULL, v)) + if (!kvm_riscv_isa_check_host(V)) __kvm_riscv_vector_save(cntx); } void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx) { - if (riscv_isa_extension_available(NULL, v)) + if (!kvm_riscv_isa_check_host(V)) __kvm_riscv_vector_restore(cntx); } @@ -80,8 +81,11 @@ int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu) return -ENOMEM; vcpu->arch.host_context.vector.datap = kzalloc(riscv_v_vsize, GFP_KERNEL); - if (!vcpu->arch.host_context.vector.datap) + if (!vcpu->arch.host_context.vector.datap) { + kfree(vcpu->arch.guest_context.vector.datap); + vcpu->arch.guest_context.vector.datap = NULL; return -ENOMEM; + } return 0; } @@ -127,6 +131,7 @@ static int kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu, } else if (reg_num <= KVM_REG_RISCV_VECTOR_REG(31)) { if (reg_size != vlenb) return -EINVAL; + WARN_ON(!cntx->vector.datap); *reg_addr = cntx->vector.datap + (reg_num - KVM_REG_RISCV_VECTOR_REG(0)) * vlenb; } else { diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c index 13c63ae1a78b..a9f083feeb76 100644 --- a/arch/riscv/kvm/vm.c +++ b/arch/riscv/kvm/vm.c @@ -199,7 +199,10 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) r = KVM_USER_MEM_SLOTS; break; case KVM_CAP_VM_GPA_BITS: - r = kvm_riscv_gstage_gpa_bits; + if (!kvm) + r = kvm_riscv_gstage_gpa_bits(kvm_riscv_gstage_max_pgd_levels); + else + r = kvm_riscv_gstage_gpa_bits(kvm->arch.pgd_levels); break; default: r = 0; @@ -211,12 +214,52 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) { + if (cap->flags) + return -EINVAL; + switch (cap->cap) { case KVM_CAP_RISCV_MP_STATE_RESET: - if (cap->flags) - return -EINVAL; kvm->arch.mp_state_reset = true; return 0; + case KVM_CAP_VM_GPA_BITS: { + unsigned long gpa_bits = cap->args[0]; + unsigned long new_levels; + int r = 0; + + /* Decide target pgd levels from requested gpa_bits */ +#ifdef CONFIG_64BIT + if (gpa_bits <= 41) + new_levels = 3; /* Sv39x4 */ + else if (gpa_bits <= 50) + new_levels = 4; /* Sv48x4 */ + else if (gpa_bits <= 59) + new_levels = 5; /* Sv57x4 */ + else + return -EINVAL; +#else + /* 32-bit: only Sv32x4*/ + if (gpa_bits <= 34) + new_levels = 2; + else + return -EINVAL; +#endif + if (new_levels > kvm_riscv_gstage_max_pgd_levels) + return -EINVAL; + + /* Follow KVM's lock ordering: kvm->lock -> kvm->slots_lock. */ + mutex_lock(&kvm->lock); + mutex_lock(&kvm->slots_lock); + + if (kvm->created_vcpus || !kvm_are_all_memslots_empty(kvm)) + r = -EBUSY; + else + kvm->arch.pgd_levels = new_levels; + + mutex_unlock(&kvm->slots_lock); + mutex_unlock(&kvm->lock); + + return r; + } default: return -EINVAL; } diff --git a/arch/riscv/kvm/vmid.c b/arch/riscv/kvm/vmid.c index cf34d448289d..c15bdb1dd8be 100644 --- a/arch/riscv/kvm/vmid.c +++ b/arch/riscv/kvm/vmid.c @@ -26,7 +26,8 @@ static DEFINE_SPINLOCK(vmid_lock); void __init kvm_riscv_gstage_vmid_detect(void) { /* Figure-out number of VMID bits in HW */ - csr_write(CSR_HGATP, (kvm_riscv_gstage_mode << HGATP_MODE_SHIFT) | HGATP_VMID); + csr_write(CSR_HGATP, (kvm_riscv_gstage_mode(kvm_riscv_gstage_max_pgd_levels) << + HGATP_MODE_SHIFT) | HGATP_VMID); vmid_bits = csr_read(CSR_HGATP); vmid_bits = (vmid_bits & HGATP_VMID) >> HGATP_VMID_SHIFT; vmid_bits = fls_long(vmid_bits); diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index bbc031124974..6f767b2a349d 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -7,6 +7,9 @@ ifeq ($(CONFIG_KASAN_GENERIC)$(CONFIG_KASAN_SW_TAGS),) lib-y += strcmp.o lib-y += strlen.o lib-y += strncmp.o +lib-y += strnlen.o +lib-y += strchr.o +lib-y += strrchr.o endif lib-y += csum.o ifeq ($(CONFIG_MMU), y) @@ -16,5 +19,4 @@ lib-$(CONFIG_MMU) += uaccess.o lib-$(CONFIG_64BIT) += tishift.o lib-$(CONFIG_RISCV_ISA_ZICBOZ) += clear_page.o obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o -lib-$(CONFIG_RISCV_ISA_V) += xor.o lib-$(CONFIG_RISCV_ISA_V) += riscv_v_helpers.o diff --git a/arch/riscv/lib/csum.c b/arch/riscv/lib/csum.c index 75bd0abffd63..5d4379768e5d 100644 --- a/arch/riscv/lib/csum.c +++ b/arch/riscv/lib/csum.c @@ -269,7 +269,7 @@ unsigned int do_csum(const unsigned char *buff, int len) * on machines with fast misaligned accesses. * * There is some duplicate code between the "with_alignment" and - * "no_alignment" implmentations, but the overlap is too awkward to be + * "no_alignment" implementations, but the overlap is too awkward to be * able to fit in one function without introducing multiple static * branches. The largest chunk of overlap was delegated into the * do_csum_common function. diff --git a/arch/riscv/lib/memmove.S b/arch/riscv/lib/memmove.S index cb3e2e7ef0ba..410e598d4677 100644 --- a/arch/riscv/lib/memmove.S +++ b/arch/riscv/lib/memmove.S @@ -40,8 +40,8 @@ SYM_FUNC_START(__memmove) * Both Copy Modes: t1 - Temporary for load-store * Both Copy Modes: t2 - Temporary for load-store * Both Copy Modes: a5 - dest to src alignment offset - * Both Copy Modes: a6 - Shift ammount - * Both Copy Modes: a7 - Inverse Shift ammount + * Both Copy Modes: a6 - Shift amount + * Both Copy Modes: a7 - Inverse Shift amount * Both Copy Modes: a2 - Alternate breakpoint for unrolled loops */ diff --git a/arch/riscv/lib/strchr.S b/arch/riscv/lib/strchr.S new file mode 100644 index 000000000000..48c3a9da53e3 --- /dev/null +++ b/arch/riscv/lib/strchr.S @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Copyright (C) 2025 Feng Jiang + */ + +#include +#include + +/* char *strchr(const char *s, int c) */ +SYM_FUNC_START(strchr) + /* + * Parameters + * a0 - The string to be searched + * a1 - The character to search for + * + * Returns + * a0 - Address of first occurrence of 'c' or 0 + * + * Clobbers + * t0 + */ + andi a1, a1, 0xff +1: + lbu t0, 0(a0) + beq t0, a1, 2f + addi a0, a0, 1 + bnez t0, 1b + li a0, 0 +2: + ret +SYM_FUNC_END(strchr) + +SYM_FUNC_ALIAS_WEAK(__pi_strchr, strchr) +EXPORT_SYMBOL(strchr) diff --git a/arch/riscv/lib/strnlen.S b/arch/riscv/lib/strnlen.S new file mode 100644 index 000000000000..53afa7b5b314 --- /dev/null +++ b/arch/riscv/lib/strnlen.S @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Base on arch/riscv/lib/strlen.S + * + * Copyright (C) Feng Jiang + */ + +#include +#include +#include +#include + +/* size_t strnlen(const char *s, size_t count) */ +SYM_FUNC_START(strnlen) + + __ALTERNATIVE_CFG("nop", "j strnlen_zbb", 0, RISCV_ISA_EXT_ZBB, + IS_ENABLED(CONFIG_RISCV_ISA_ZBB) && IS_ENABLED(CONFIG_TOOLCHAIN_HAS_ZBB)) + + + /* + * Returns + * a0 - String length + * + * Parameters + * a0 - String to measure + * a1 - Max length of string + * + * Clobbers + * t0, t1, t2 + */ + addi t1, a0, -1 + add t2, a0, a1 +1: + addi t1, t1, 1 + beq t1, t2, 2f + lbu t0, 0(t1) + bnez t0, 1b +2: + sub a0, t1, a0 + ret + + +/* + * Variant of strnlen using the ZBB extension if available + */ +#if defined(CONFIG_RISCV_ISA_ZBB) && defined(CONFIG_TOOLCHAIN_HAS_ZBB) +strnlen_zbb: + +#ifdef CONFIG_CPU_BIG_ENDIAN +# define CZ clz +# define SHIFT sll +#else +# define CZ ctz +# define SHIFT srl +#endif + +.option push +.option arch,+zbb + + /* + * Returns + * a0 - String length + * + * Parameters + * a0 - String to measure + * a1 - Max length of string + * + * Clobbers + * t0, t1, t2, t3, t4 + */ + + /* If maxlen is 0, return 0. */ + beqz a1, 3f + + /* Number of irrelevant bytes in the first word. */ + andi t2, a0, SZREG-1 + + /* Align pointer. */ + andi t0, a0, -SZREG + + li t3, SZREG + sub t3, t3, t2 + slli t2, t2, 3 + + /* Aligned boundary. */ + add t4, a0, a1 + andi t4, t4, -SZREG + + /* Get the first word. */ + REG_L t1, 0(t0) + + /* + * Shift away the partial data we loaded to remove the irrelevant bytes + * preceding the string with the effect of adding NUL bytes at the + * end of the string's first word. + */ + SHIFT t1, t1, t2 + + /* Convert non-NUL into 0xff and NUL into 0x00. */ + orc.b t1, t1 + + /* Convert non-NUL into 0x00 and NUL into 0xff. */ + not t1, t1 + + /* + * Search for the first set bit (corresponding to a NUL byte in the + * original chunk). + */ + CZ t1, t1 + + /* + * The first chunk is special: compare against the number + * of valid bytes in this chunk. + */ + srli a0, t1, 3 + + /* Limit the result by maxlen. */ + minu a0, a0, a1 + + bgtu t3, a0, 2f + + /* Prepare for the word comparison loop. */ + addi t2, t0, SZREG + li t3, -1 + + /* + * Our critical loop is 4 instructions and processes data in + * 4 byte or 8 byte chunks. + */ + .p2align 3 +1: + REG_L t1, SZREG(t0) + addi t0, t0, SZREG + orc.b t1, t1 + bgeu t0, t4, 4f + beq t1, t3, 1b +4: + not t1, t1 + CZ t1, t1 + srli t1, t1, 3 + + /* Get number of processed bytes. */ + sub t2, t0, t2 + + /* Add number of characters in the first word. */ + add a0, a0, t2 + + /* Add number of characters in the last word. */ + add a0, a0, t1 + + /* Ensure the final result does not exceed maxlen. */ + minu a0, a0, a1 +2: + ret +3: + mv a0, a1 + ret + +.option pop +#endif +SYM_FUNC_END(strnlen) +SYM_FUNC_ALIAS(__pi_strnlen, strnlen) +EXPORT_SYMBOL(strnlen) diff --git a/arch/riscv/lib/strrchr.S b/arch/riscv/lib/strrchr.S new file mode 100644 index 000000000000..ac58b20ca21d --- /dev/null +++ b/arch/riscv/lib/strrchr.S @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Copyright (C) 2025 Feng Jiang + */ + +#include +#include + +/* char *strrchr(const char *s, int c) */ +SYM_FUNC_START(strrchr) + /* + * Parameters + * a0 - The string to be searched + * a1 - The character to seaerch for + * + * Returns + * a0 - Address of last occurrence of 'c' or 0 + * + * Clobbers + * t0, t1 + */ + andi a1, a1, 0xff + mv t1, a0 + li a0, 0 +1: + lbu t0, 0(t1) + bne t0, a1, 2f + mv a0, t1 +2: + addi t1, t1, 1 + bnez t0, 1b + ret +SYM_FUNC_END(strrchr) + +SYM_FUNC_ALIAS_WEAK(__pi_strrchr, strrchr) +EXPORT_SYMBOL(strrchr) diff --git a/arch/riscv/mm/cacheflush.c b/arch/riscv/mm/cacheflush.c index d83a612464f6..f8ead7cb7c7d 100644 --- a/arch/riscv/mm/cacheflush.c +++ b/arch/riscv/mm/cacheflush.c @@ -29,7 +29,7 @@ void flush_icache_all(void) * Make sure all previous writes to the D$ are ordered before making * the IPI. The RISC-V spec states that a hart must execute a data fence * before triggering a remote fence.i in order to make the modification - * visable for remote harts. + * visible for remote harts. * * IPIs on RISC-V are triggered by MMIO writes to either CLINT or * S-IMSIC, so the fence ensures previous data writes "happen before" diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index b5c50956bb8a..fa8d2f6f554b 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -41,20 +41,17 @@ u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1]; struct kernel_mapping kernel_map __ro_after_init; EXPORT_SYMBOL(kernel_map); -#ifdef CONFIG_XIP_KERNEL -#define kernel_map (*(struct kernel_mapping *)XIP_FIXUP(&kernel_map)) -#endif #ifdef CONFIG_64BIT -u64 satp_mode __ro_after_init = !IS_ENABLED(CONFIG_XIP_KERNEL) ? SATP_MODE_57 : SATP_MODE_39; +u64 satp_mode __ro_after_init = SATP_MODE_57; #else u64 satp_mode __ro_after_init = SATP_MODE_32; #endif EXPORT_SYMBOL(satp_mode); #ifdef CONFIG_64BIT -bool pgtable_l4_enabled __ro_after_init = !IS_ENABLED(CONFIG_XIP_KERNEL); -bool pgtable_l5_enabled __ro_after_init = !IS_ENABLED(CONFIG_XIP_KERNEL); +bool pgtable_l4_enabled __ro_after_init = true; +bool pgtable_l5_enabled __ro_after_init = true; EXPORT_SYMBOL(pgtable_l4_enabled); EXPORT_SYMBOL(pgtable_l5_enabled); #endif @@ -190,9 +187,6 @@ void __init arch_mm_preinit(void) /* Limit the memory size via mem. */ static phys_addr_t memory_limit; -#ifdef CONFIG_XIP_KERNEL -#define memory_limit (*(phys_addr_t *)XIP_FIXUP(&memory_limit)) -#endif /* CONFIG_XIP_KERNEL */ static int __init early_mem(char *p) { @@ -216,10 +210,7 @@ static void __init setup_bootmem(void) phys_addr_t max_mapped_addr; phys_addr_t phys_ram_end, vmlinux_start; - if (IS_ENABLED(CONFIG_XIP_KERNEL)) - vmlinux_start = __pa_symbol(&_sdata); - else - vmlinux_start = __pa_symbol(&_start); + vmlinux_start = __pa_symbol(&_start); memblock_enforce_memory_limit(memory_limit); @@ -239,12 +230,10 @@ static void __init setup_bootmem(void) * Make sure we align the start of the memory on a PMD boundary so that * at worst, we map the linear mapping with PMD mappings. */ - if (!IS_ENABLED(CONFIG_XIP_KERNEL)) { - phys_ram_base = memblock_start_of_DRAM() & PMD_MASK; + phys_ram_base = memblock_start_of_DRAM() & PMD_MASK; #ifdef CONFIG_SPARSEMEM_VMEMMAP - vmemmap_start_pfn = round_down(phys_ram_base, VMEMMAP_ADDR_ALIGN) >> PAGE_SHIFT; + vmemmap_start_pfn = round_down(phys_ram_base, VMEMMAP_ADDR_ALIGN) >> PAGE_SHIFT; #endif - } /* * In 64-bit, any use of __va/__pa before this point is wrong as we @@ -357,13 +346,6 @@ static pte_t fixmap_pte[PTRS_PER_PTE] __page_aligned_bss; pgd_t early_pg_dir[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE); -#ifdef CONFIG_XIP_KERNEL -#define pt_ops (*(struct pt_alloc_ops *)XIP_FIXUP(&pt_ops)) -#define trampoline_pg_dir ((pgd_t *)XIP_FIXUP(trampoline_pg_dir)) -#define fixmap_pte ((pte_t *)XIP_FIXUP(fixmap_pte)) -#define early_pg_dir ((pgd_t *)XIP_FIXUP(early_pg_dir)) -#endif /* CONFIG_XIP_KERNEL */ - static const pgprot_t protection_map[16] = { [VM_NONE] = PAGE_NONE, [VM_READ] = PAGE_READ, @@ -460,32 +442,14 @@ static pmd_t trampoline_pmd[PTRS_PER_PMD] __page_aligned_bss; static pmd_t fixmap_pmd[PTRS_PER_PMD] __page_aligned_bss; static pmd_t early_pmd[PTRS_PER_PMD] __initdata __aligned(PAGE_SIZE); -#ifdef CONFIG_XIP_KERNEL -#define trampoline_pmd ((pmd_t *)XIP_FIXUP(trampoline_pmd)) -#define fixmap_pmd ((pmd_t *)XIP_FIXUP(fixmap_pmd)) -#define early_pmd ((pmd_t *)XIP_FIXUP(early_pmd)) -#endif /* CONFIG_XIP_KERNEL */ - static p4d_t trampoline_p4d[PTRS_PER_P4D] __page_aligned_bss; static p4d_t fixmap_p4d[PTRS_PER_P4D] __page_aligned_bss; static p4d_t early_p4d[PTRS_PER_P4D] __initdata __aligned(PAGE_SIZE); -#ifdef CONFIG_XIP_KERNEL -#define trampoline_p4d ((p4d_t *)XIP_FIXUP(trampoline_p4d)) -#define fixmap_p4d ((p4d_t *)XIP_FIXUP(fixmap_p4d)) -#define early_p4d ((p4d_t *)XIP_FIXUP(early_p4d)) -#endif /* CONFIG_XIP_KERNEL */ - static pud_t trampoline_pud[PTRS_PER_PUD] __page_aligned_bss; static pud_t fixmap_pud[PTRS_PER_PUD] __page_aligned_bss; static pud_t early_pud[PTRS_PER_PUD] __initdata __aligned(PAGE_SIZE); -#ifdef CONFIG_XIP_KERNEL -#define trampoline_pud ((pud_t *)XIP_FIXUP(trampoline_pud)) -#define fixmap_pud ((pud_t *)XIP_FIXUP(fixmap_pud)) -#define early_pud ((pud_t *)XIP_FIXUP(early_pud)) -#endif /* CONFIG_XIP_KERNEL */ - static pmd_t *__init get_pmd_virt_early(phys_addr_t pa) { /* Before MMU is enabled */ @@ -756,21 +720,6 @@ static uintptr_t __meminit best_map_size(phys_addr_t pa, uintptr_t va, phys_addr return PAGE_SIZE; } -#ifdef CONFIG_XIP_KERNEL -#define phys_ram_base (*(phys_addr_t *)XIP_FIXUP(&phys_ram_base)) -extern char _xiprom[], _exiprom[], __data_loc; - -/* called from head.S with MMU off */ -asmlinkage void __init __copy_data(void) -{ - void *from = (void *)(&__data_loc); - void *to = (void *)CONFIG_PHYS_RAM_BASE; - size_t sz = (size_t)((uintptr_t)(&_end) - (uintptr_t)(&_sdata)); - - memcpy(to, from, sz); -} -#endif - #ifdef CONFIG_STRICT_KERNEL_RWX static __meminit pgprot_t pgprot_from_va(uintptr_t va) { @@ -806,7 +755,7 @@ static __meminit pgprot_t pgprot_from_va(uintptr_t va) } #endif /* CONFIG_STRICT_KERNEL_RWX */ -#if defined(CONFIG_64BIT) && !defined(CONFIG_XIP_KERNEL) +#if defined(CONFIG_64BIT) u64 __pi_set_satp_mode_from_cmdline(uintptr_t dtb_pa); u64 __pi_set_satp_mode_from_fdt(uintptr_t dtb_pa); @@ -843,6 +792,27 @@ static void __init set_mmap_rnd_bits_max(void) mmap_rnd_bits_max = MMAP_VA_BITS - PAGE_SHIFT - 3; } +static bool __init is_vaddr_valid(unsigned long va) +{ + unsigned long up = 0; + + switch (satp_mode) { + case SATP_MODE_39: + up = 1UL << 38; + break; + case SATP_MODE_48: + up = 1UL << 47; + break; + case SATP_MODE_57: + up = 1UL << 56; + break; + default: + return false; + } + + return (va < up) || (va >= (ULONG_MAX - up + 1)); +} + /* * There is a simple way to determine if 4-level is supported by the * underlying hardware: establish 1:1 mapping in 4-level page table mode @@ -884,6 +854,9 @@ static __init void set_satp_mode(uintptr_t dtb_pa) set_satp_mode_pmd + PMD_SIZE, PMD_SIZE, PAGE_KERNEL_EXEC); retry: + if (!is_vaddr_valid(set_satp_mode_pmd)) + goto out; + create_pgd_mapping(early_pg_dir, set_satp_mode_pmd, pgtable_l5_enabled ? @@ -906,6 +879,7 @@ retry: disable_pgtable_l4(); } +out: memset(early_pg_dir, 0, PAGE_SIZE); memset(early_p4d, 0, PAGE_SIZE); memset(early_pud, 0, PAGE_SIZE); @@ -931,28 +905,6 @@ retry: #error "setup_vm() is called from head.S before relocate so it should not use absolute addressing." #endif -#ifdef CONFIG_XIP_KERNEL -static void __init create_kernel_page_table(pgd_t *pgdir, - __always_unused bool early) -{ - uintptr_t va, start_va, end_va; - - /* Map the flash resident part */ - end_va = kernel_map.virt_addr + kernel_map.xiprom_sz; - for (va = kernel_map.virt_addr; va < end_va; va += PMD_SIZE) - create_pgd_mapping(pgdir, va, - kernel_map.xiprom + (va - kernel_map.virt_addr), - PMD_SIZE, PAGE_KERNEL_EXEC); - - /* Map the data in RAM */ - start_va = kernel_map.virt_addr + (uintptr_t)&_sdata - (uintptr_t)&_start; - end_va = kernel_map.virt_addr + kernel_map.size; - for (va = start_va; va < end_va; va += PMD_SIZE) - create_pgd_mapping(pgdir, va, - kernel_map.phys_addr + (va - start_va), - PMD_SIZE, PAGE_KERNEL); -} -#else static void __init create_kernel_page_table(pgd_t *pgdir, bool early) { uintptr_t va, end_va; @@ -965,7 +917,6 @@ static void __init create_kernel_page_table(pgd_t *pgdir, bool early) early ? PAGE_KERNEL_EXEC : pgprot_from_va(va)); } -#endif /* * Setup a 4MB mapping that encompasses the device tree: for 64-bit kernel, @@ -1075,11 +1026,6 @@ static int __init print_nokaslr(char *p) return 0; } early_param("nokaslr", print_nokaslr); - -unsigned long kaslr_offset(void) -{ - return kernel_map.virt_offset; -} #endif asmlinkage void __init setup_vm(uintptr_t dtb_pa) @@ -1107,27 +1053,11 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa) kernel_map.virt_addr = KERNEL_LINK_ADDR + kernel_map.virt_offset; -#ifdef CONFIG_XIP_KERNEL - kernel_map.xiprom = (uintptr_t)CONFIG_XIP_PHYS_ADDR; - kernel_map.xiprom_sz = (uintptr_t)(&_exiprom) - (uintptr_t)(&_xiprom); - - phys_ram_base = CONFIG_PHYS_RAM_BASE; -#ifdef CONFIG_SPARSEMEM_VMEMMAP - vmemmap_start_pfn = round_down(phys_ram_base, VMEMMAP_ADDR_ALIGN) >> PAGE_SHIFT; -#endif - kernel_map.phys_addr = (uintptr_t)CONFIG_PHYS_RAM_BASE; - kernel_map.size = (uintptr_t)(&_end) - (uintptr_t)(&_start); - - kernel_map.va_kernel_xip_text_pa_offset = kernel_map.virt_addr - kernel_map.xiprom; - kernel_map.va_kernel_xip_data_pa_offset = kernel_map.virt_addr - kernel_map.phys_addr - + (uintptr_t)&_sdata - (uintptr_t)&_start; -#else kernel_map.phys_addr = (uintptr_t)(&_start); kernel_map.size = (uintptr_t)(&_end) - kernel_map.phys_addr; kernel_map.va_kernel_pa_offset = kernel_map.virt_addr - kernel_map.phys_addr; -#endif -#if defined(CONFIG_64BIT) && !defined(CONFIG_XIP_KERNEL) +#if defined(CONFIG_64BIT) set_satp_mode(dtb_pa); set_mmap_rnd_bits_max(); #endif @@ -1200,13 +1130,8 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa) if (pgtable_l4_enabled) create_pud_mapping(trampoline_pud, kernel_map.virt_addr, (uintptr_t)trampoline_pmd, PUD_SIZE, PAGE_TABLE); -#ifdef CONFIG_XIP_KERNEL - create_pmd_mapping(trampoline_pmd, kernel_map.virt_addr, - kernel_map.xiprom, PMD_SIZE, PAGE_KERNEL_EXEC); -#else create_pmd_mapping(trampoline_pmd, kernel_map.virt_addr, kernel_map.phys_addr, PMD_SIZE, PAGE_KERNEL_EXEC); -#endif #else /* Setup trampoline PGD */ create_pgd_mapping(trampoline_pg_dir, kernel_map.virt_addr, @@ -1450,6 +1375,8 @@ int __meminit vmemmap_check_pmd(pmd_t *pmdp, int node, int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, struct vmem_altmap *altmap) { + WARN_ON((start < VMEMMAP_START) || (end > VMEMMAP_END)); + /* * Note that SPARSEMEM_VMEMMAP is only selected for rv64 and that we * can't use hugepage mappings for 2-level page table because in case of diff --git a/arch/riscv/mm/physaddr.c b/arch/riscv/mm/physaddr.c index 559d291fac5c..b1836e9481ce 100644 --- a/arch/riscv/mm/physaddr.c +++ b/arch/riscv/mm/physaddr.c @@ -9,7 +9,7 @@ phys_addr_t __virt_to_phys(unsigned long x) { /* - * Boundary checking aginst the kernel linear mapping space. + * Boundary checking against the kernel linear mapping space. */ WARN(!is_linear_mapping(x) && !is_kernel_mapping(x), "virt_to_phys used for non-linear address: %p (%pS)\n", @@ -25,7 +25,7 @@ phys_addr_t __phys_addr_symbol(unsigned long x) unsigned long kernel_end = kernel_start + kernel_map.size; /* - * Boundary checking aginst the kernel image mapping. + * Boundary checking against the kernel image mapping. * __pa_symbol should only be used on kernel symbol addresses. */ VIRTUAL_BUG_ON(x < kernel_start || x > kernel_end); diff --git a/arch/riscv/net/bpf_jit.h b/arch/riscv/net/bpf_jit.h index 632ced07bca4..da0271790244 100644 --- a/arch/riscv/net/bpf_jit.h +++ b/arch/riscv/net/bpf_jit.h @@ -11,7 +11,6 @@ #include #include -#include /* verify runtime detection extension status */ #define rv_ext_enabled(ext) \ @@ -105,11 +104,6 @@ static inline void bpf_fill_ill_insns(void *area, unsigned int size) memset(area, 0, size); } -static inline void bpf_flush_icache(void *start, void *end) -{ - flush_icache_range((unsigned long)start, (unsigned long)end); -} - /* Emit a 4-byte riscv instruction. */ static inline void emit(const u32 insn, struct rv_jit_context *ctx) { diff --git a/arch/riscv/net/bpf_jit_core.c b/arch/riscv/net/bpf_jit_core.c index b3581e926436..4365d07aaf54 100644 --- a/arch/riscv/net/bpf_jit_core.c +++ b/arch/riscv/net/bpf_jit_core.c @@ -41,32 +41,22 @@ bool bpf_jit_needs_zext(void) return true; } -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) { unsigned int prog_size = 0, extable_size = 0; - bool tmp_blinded = false, extra_pass = false; - struct bpf_prog *tmp, *orig_prog = prog; + bool extra_pass = false; int pass = 0, prev_ninsns = 0, i; struct rv_jit_data *jit_data; struct rv_jit_context *ctx; if (!prog->jit_requested) - return orig_prog; - - tmp = bpf_jit_blind_constants(prog); - if (IS_ERR(tmp)) - return orig_prog; - if (tmp != prog) { - tmp_blinded = true; - prog = tmp; - } + return prog; jit_data = prog->aux->jit_data; if (!jit_data) { jit_data = kzalloc_obj(*jit_data); if (!jit_data) { - prog = orig_prog; - goto out; + return prog; } prog->aux->jit_data = jit_data; } @@ -83,15 +73,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) ctx->user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena); ctx->prog = prog; ctx->offset = kzalloc_objs(int, prog->len); - if (!ctx->offset) { - prog = orig_prog; + if (!ctx->offset) goto out_offset; - } - if (build_body(ctx, extra_pass, NULL)) { - prog = orig_prog; + if (build_body(ctx, extra_pass, NULL)) goto out_offset; - } for (i = 0; i < prog->len; i++) { prev_ninsns += 32; @@ -105,10 +91,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) bpf_jit_build_prologue(ctx, bpf_is_subprog(prog)); ctx->prologue_len = ctx->ninsns; - if (build_body(ctx, extra_pass, ctx->offset)) { - prog = orig_prog; + if (build_body(ctx, extra_pass, ctx->offset)) goto out_offset; - } ctx->epilogue_offset = ctx->ninsns; bpf_jit_build_epilogue(ctx); @@ -126,10 +110,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) &jit_data->ro_image, sizeof(u32), &jit_data->header, &jit_data->image, bpf_fill_ill_insns); - if (!jit_data->ro_header) { - prog = orig_prog; + if (!jit_data->ro_header) goto out_offset; - } /* * Use the image(RW) for writing the JITed instructions. But also save @@ -150,7 +132,6 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) if (i == NR_JIT_ITERATIONS) { pr_err("bpf-jit: image did not converge in <%d passes!\n", i); - prog = orig_prog; goto out_free_hdr; } @@ -163,33 +144,27 @@ skip_init_ctx: ctx->nexentries = 0; bpf_jit_build_prologue(ctx, bpf_is_subprog(prog)); - if (build_body(ctx, extra_pass, NULL)) { - prog = orig_prog; + if (build_body(ctx, extra_pass, NULL)) goto out_free_hdr; - } bpf_jit_build_epilogue(ctx); if (bpf_jit_enable > 1) bpf_jit_dump(prog->len, prog_size, pass, ctx->insns); + if (!prog->is_func || extra_pass) { + if (WARN_ON(bpf_jit_binary_pack_finalize(jit_data->ro_header, jit_data->header))) { + /* ro_header has been freed */ + jit_data->ro_header = NULL; + jit_data->header = NULL; + goto out_free_hdr; + } + } + prog->bpf_func = (void *)ctx->ro_insns + cfi_get_offset(); prog->jited = 1; prog->jited_len = prog_size - cfi_get_offset(); if (!prog->is_func || extra_pass) { - if (WARN_ON(bpf_jit_binary_pack_finalize(jit_data->ro_header, jit_data->header))) { - /* ro_header has been freed */ - jit_data->ro_header = NULL; - prog = orig_prog; - goto out_offset; - } - /* - * The instructions have now been copied to the ROX region from - * where they will execute. - * Write any modified data cache blocks out to memory and - * invalidate the corresponding blocks in the instruction cache. - */ - bpf_flush_icache(jit_data->ro_header, ctx->ro_insns + ctx->ninsns); for (i = 0; i < prog->len; i++) ctx->offset[i] = ninsns_rvoff(ctx->offset[i]); bpf_prog_fill_jited_linfo(prog, ctx->offset); @@ -198,14 +173,15 @@ out_offset: kfree(jit_data); prog->aux->jit_data = NULL; } -out: - if (tmp_blinded) - bpf_jit_prog_release_other(prog, prog == orig_prog ? - tmp : orig_prog); return prog; out_free_hdr: + if (extra_pass) { + prog->bpf_func = NULL; + prog->jited = 0; + prog->jited_len = 0; + } if (jit_data->header) { bpf_arch_text_copy(&jit_data->ro_header->size, &jit_data->header->size, sizeof(jit_data->header->size)); diff --git a/arch/riscv/purgatory/Makefile b/arch/riscv/purgatory/Makefile index 530e497ca2f9..b0358a78f11a 100644 --- a/arch/riscv/purgatory/Makefile +++ b/arch/riscv/purgatory/Makefile @@ -2,7 +2,7 @@ purgatory-y := purgatory.o sha256.o entry.o string.o ctype.o memcpy.o memset.o ifeq ($(CONFIG_KASAN_GENERIC)$(CONFIG_KASAN_SW_TAGS),) -purgatory-y += strcmp.o strlen.o strncmp.o +purgatory-y += strcmp.o strlen.o strncmp.o strnlen.o strchr.o strrchr.o endif targets += $(purgatory-y) @@ -32,6 +32,15 @@ $(obj)/strncmp.o: $(srctree)/arch/riscv/lib/strncmp.S FORCE $(obj)/sha256.o: $(srctree)/lib/crypto/sha256.c FORCE $(call if_changed_rule,cc_o_c) +$(obj)/strnlen.o: $(srctree)/arch/riscv/lib/strnlen.S FORCE + $(call if_changed_rule,as_o_S) + +$(obj)/strchr.o: $(srctree)/arch/riscv/lib/strchr.S FORCE + $(call if_changed_rule,as_o_S) + +$(obj)/strrchr.o: $(srctree)/arch/riscv/lib/strrchr.S FORCE + $(call if_changed_rule,as_o_S) + CFLAGS_sha256.o := -D__DISABLE_EXPORTS -D__NO_FORTIFY CFLAGS_string.o := -D__DISABLE_EXPORTS CFLAGS_ctype.o := -D__DISABLE_EXPORTS diff --git a/arch/riscv/purgatory/kexec-purgatory.S b/arch/riscv/purgatory/kexec-purgatory.S index 0e9188815718..32c53581b8f2 100644 --- a/arch/riscv/purgatory/kexec-purgatory.S +++ b/arch/riscv/purgatory/kexec-purgatory.S @@ -6,9 +6,9 @@ kexec_purgatory: .globl kexec_purgatory .incbin "arch/riscv/purgatory/purgatory.ro" -.Lkexec_purgatroy_end: +.Lkexec_purgatory_end: .align 8 kexec_purgatory_size: .globl kexec_purgatory_size - .quad .Lkexec_purgatroy_end - kexec_purgatory + .quad .Lkexec_purgatory_end - kexec_purgatory diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index d01800962d84..ecbcbb781e40 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -80,7 +80,6 @@ config S390 # # Note: keep this list sorted alphabetically # - imply IMA_SECURE_AND_OR_TRUSTED_BOOT select ALTERNATE_USER_ADDRESS_SPACE select ARCH_32BIT_USTAT_F_TINODE select ARCH_CORRECT_STACKTRACE_ON_KRETPROBE @@ -153,6 +152,7 @@ config S390 select ARCH_SUPPORTS_INT128 if CC_HAS_INT128 && CC_IS_CLANG select ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS select ARCH_SUPPORTS_NUMA_BALANCING + select ARCH_SUPPORTS_PAGE_TABLE_CHECK select ARCH_SUPPORTS_PER_VMA_LOCK select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_CMPXCHG_LOCKREF @@ -181,6 +181,7 @@ config S390 select GENERIC_IOREMAP if PCI select HAVE_ALIGNED_STRUCT_PAGE select HAVE_ARCH_AUDITSYSCALL + select HAVE_ARCH_GET_SECUREBOOT select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_JUMP_LABEL_RELATIVE select HAVE_ARCH_KASAN @@ -1023,7 +1024,7 @@ config S390_KPROBES_SANITY_TEST config S390_MODULES_SANITY_TEST def_tristate n - depends on KUNIT + depends on KUNIT && m default KUNIT_ALL_TESTS prompt "Enable s390 specific modules tests" select S390_MODULES_SANITY_TEST_HELPERS diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index 7f3343493ab9..c1dcf8b1b579 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -440,7 +440,8 @@ static unsigned long setup_kernel_memory_layout(unsigned long kernel_size) max_mappable = max(ident_map_size, MAX_DCSS_ADDR); max_mappable = min(max_mappable, vmemmap_start); #ifdef CONFIG_RANDOMIZE_IDENTITY_BASE - __identity_base = round_down(vmemmap_start - max_mappable, rte_size); + if (kaslr_enabled()) + __identity_base = round_down(vmemmap_start - max_mappable, rte_size); #endif boot_debug("identity map: 0x%016lx-0x%016lx\n", __identity_base, __identity_base + ident_map_size); diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index 34b5ea7885f5..c28f9a7d0bd8 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -925,3 +925,5 @@ CONFIG_PERCPU_TEST=m CONFIG_ATOMIC64_SELFTEST=y CONFIG_TEST_BITOPS=m CONFIG_TEST_BPF=m +CONFIG_PAGE_TABLE_CHECK=y +CONFIG_PAGE_TABLE_CHECK_ENFORCED=y diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 3039c88daa63..8a4f4a39f7a2 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -656,6 +656,7 @@ struct kvm_arch { int user_stsi; int user_instr0; int user_operexec; + int allow_vsie_esamode; struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS]; wait_queue_head_t ipte_wq; int ipte_lock_count; diff --git a/arch/s390/include/asm/kvm_host_types.h b/arch/s390/include/asm/kvm_host_types.h index 1394d3fb648f..3f50942bdfe6 100644 --- a/arch/s390/include/asm/kvm_host_types.h +++ b/arch/s390/include/asm/kvm_host_types.h @@ -137,8 +137,7 @@ struct mcck_volatile_info { struct kvm_s390_sie_block { atomic_t cpuflags; /* 0x0000 */ __u32 : 1; /* 0x0004 */ - __u32 prefix : 18; - __u32 : 1; + __u32 prefix : 19; __u32 ibc : 12; __u8 reserved08[4]; /* 0x0008 */ #define PROG_IN_SIE (1<<0) diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index c0ff19dab580..5dcf35f0f325 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -208,6 +208,10 @@ extern const struct attribute_group zpci_ident_attr_group; &pfip_attr_group, \ &zpci_ident_attr_group, +extern const struct attribute_group zpci_slot_attr_group; + +#define ARCH_PCI_SLOT_GROUPS (&zpci_slot_attr_group) + extern unsigned int s390_pci_force_floating __initdata; extern unsigned int s390_pci_no_rid; diff --git a/arch/s390/include/asm/percpu.h b/arch/s390/include/asm/percpu.h index 5899f57f17d1..b18a96f3a334 100644 --- a/arch/s390/include/asm/percpu.h +++ b/arch/s390/include/asm/percpu.h @@ -12,6 +12,24 @@ */ #define __my_cpu_offset get_lowcore()->percpu_offset +#define arch_raw_cpu_ptr(_ptr) \ +({ \ + unsigned long lc_percpu, tcp_ptr__; \ + \ + tcp_ptr__ = (__force unsigned long)(_ptr); \ + lc_percpu = offsetof(struct lowcore, percpu_offset); \ + asm_inline volatile( \ + ALTERNATIVE("ag %[__ptr__],%[offzero](%%r0)\n", \ + "ag %[__ptr__],%[offalt](%%r0)\n", \ + ALT_FEATURE(MFEATURE_LOWCORE)) \ + : [__ptr__] "+d" (tcp_ptr__) \ + : [offzero] "i" (lc_percpu), \ + [offalt] "i" (lc_percpu + LOWCORE_ALT_ADDRESS), \ + "m" (((struct lowcore *)0)->percpu_offset) \ + : "cc"); \ + (TYPEOF_UNQUAL(*(_ptr)) __force __kernel *)tcp_ptr__; \ +}) + /* * We use a compare-and-swap loop since that uses less cpu cycles than * disabling and enabling interrupts like the generic variant would do. diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 40a6fb19dd1d..2c6cee8241e0 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -16,8 +16,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -1190,6 +1192,7 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, /* At this point the reference through the mapping is still present */ if (mm_is_protected(mm) && pte_present(res)) WARN_ON_ONCE(uv_convert_from_secure_pte(res)); + page_table_check_pte_clear(mm, addr, res); return res; } @@ -1208,6 +1211,7 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma, /* At this point the reference through the mapping is still present */ if (mm_is_protected(vma->vm_mm) && pte_present(res)) WARN_ON_ONCE(uv_convert_from_secure_pte(res)); + page_table_check_pte_clear(vma->vm_mm, addr, res); return res; } @@ -1231,6 +1235,9 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm, } else { res = ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID)); } + + page_table_check_pte_clear(mm, addr, res); + /* Nothing to do */ if (!mm_is_protected(mm) || !pte_present(res)) return res; @@ -1327,6 +1334,7 @@ static inline void set_ptes(struct mm_struct *mm, unsigned long addr, { if (pte_present(entry)) entry = clear_pte_bit(entry, __pgprot(_PAGE_UNUSED)); + page_table_check_ptes_set(mm, addr, ptep, entry, nr); for (;;) { set_pte(ptep, entry); if (--nr == 0) @@ -1703,6 +1711,7 @@ static inline bool pmdp_clear_flush_young(struct vm_area_struct *vma, static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr, pmd_t *pmdp, pmd_t entry) { + page_table_check_pmd_set(mm, addr, pmdp, entry); set_pmd(pmdp, entry); } @@ -1717,7 +1726,11 @@ static inline pmd_t pmd_mkhuge(pmd_t pmd) static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm, unsigned long addr, pmd_t *pmdp) { - return pmdp_xchg_direct(mm, addr, pmdp, __pmd(_SEGMENT_ENTRY_EMPTY)); + pmd_t pmd; + + pmd = pmdp_xchg_direct(mm, addr, pmdp, __pmd(_SEGMENT_ENTRY_EMPTY)); + page_table_check_pmd_clear(mm, addr, pmd); + return pmd; } #define __HAVE_ARCH_PMDP_HUGE_GET_AND_CLEAR_FULL @@ -1725,12 +1738,17 @@ static inline pmd_t pmdp_huge_get_and_clear_full(struct vm_area_struct *vma, unsigned long addr, pmd_t *pmdp, int full) { + pmd_t pmd; + if (full) { - pmd_t pmd = *pmdp; + pmd = *pmdp; set_pmd(pmdp, __pmd(_SEGMENT_ENTRY_EMPTY)); + page_table_check_pmd_clear(vma->vm_mm, addr, pmd); return pmd; } - return pmdp_xchg_lazy(vma->vm_mm, addr, pmdp, __pmd(_SEGMENT_ENTRY_EMPTY)); + pmd = pmdp_xchg_lazy(vma->vm_mm, addr, pmdp, __pmd(_SEGMENT_ENTRY_EMPTY)); + page_table_check_pmd_clear(vma->vm_mm, addr, pmd); + return pmd; } #define __HAVE_ARCH_PMDP_HUGE_CLEAR_FLUSH @@ -1744,11 +1762,16 @@ static inline pmd_t pmdp_huge_clear_flush(struct vm_area_struct *vma, static inline pmd_t pmdp_invalidate(struct vm_area_struct *vma, unsigned long addr, pmd_t *pmdp) { - pmd_t pmd; + pmd_t pmd = *pmdp; - VM_WARN_ON_ONCE(!pmd_present(*pmdp)); - pmd = __pmd(pmd_val(*pmdp) | _SEGMENT_ENTRY_INVALID); - return pmdp_xchg_direct(vma->vm_mm, addr, pmdp, pmd); + VM_WARN_ON_ONCE(!pmd_present(pmd)); + pmd = set_pmd_bit(pmd, __pgprot(_SEGMENT_ENTRY_INVALID)); +#ifdef CONFIG_PAGE_TABLE_CHECK + pmd = clear_pmd_bit(pmd, __pgprot(_SEGMENT_ENTRY_READ)); +#endif + page_table_check_pmd_set(vma->vm_mm, addr, pmdp, pmd); + pmd = pmdp_xchg_direct(vma->vm_mm, addr, pmdp, pmd); + return pmd; } #define __HAVE_ARCH_PMDP_SET_WRPROTECT @@ -1783,6 +1806,29 @@ static inline int has_transparent_hugepage(void) } #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ +#ifdef CONFIG_PAGE_TABLE_CHECK +static inline bool pte_user_accessible_page(struct mm_struct *mm, unsigned long addr, pte_t pte) +{ + VM_BUG_ON(mm == &init_mm); + + return pte_present(pte); +} + +static inline bool pmd_user_accessible_page(struct mm_struct *mm, unsigned long addr, pmd_t pmd) +{ + VM_BUG_ON(mm == &init_mm); + + return pmd_leaf(pmd) && (pmd_val(pmd) & _SEGMENT_ENTRY_READ); +} + +static inline bool pud_user_accessible_page(struct mm_struct *mm, unsigned long addr, pud_t pud) +{ + VM_BUG_ON(mm == &init_mm); + + return pud_leaf(pud); +} +#endif + /* * 64 bit swap entry format: * A page-table entry has some bits we have to treat in a special way. diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 7c57ac968bf6..cbf60ade741d 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -52,7 +52,6 @@ extern unsigned int zlib_dfltcc_support; #define ZLIB_DFLTCC_INFLATE_ONLY 3 #define ZLIB_DFLTCC_FULL_DEBUG 4 -extern unsigned long ident_map_size; extern unsigned long max_mappable; /* The Write Back bit position in the physaddr is given by the SLPC PCI */ diff --git a/arch/s390/include/asm/xor.h b/arch/s390/include/asm/xor.h deleted file mode 100644 index 857d6759b67f..000000000000 --- a/arch/s390/include/asm/xor.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Optimited xor routines - * - * Copyright IBM Corp. 2016 - * Author(s): Martin Schwidefsky - */ -#ifndef _ASM_S390_XOR_H -#define _ASM_S390_XOR_H - -extern struct xor_block_template xor_block_xc; - -#undef XOR_TRY_TEMPLATES -#define XOR_TRY_TEMPLATES \ -do { \ - xor_speed(&xor_block_xc); \ -} while (0) - -#define XOR_SELECT_TEMPLATE(FASTEST) (&xor_block_xc) - -#endif /* _ASM_S390_XOR_H */ diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index b522c6649fcc..137e4793ec11 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -71,7 +71,6 @@ obj-$(CONFIG_STACKPROTECTOR) += stackprotector.o obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o obj-$(CONFIG_CERT_STORE) += cert_store.o -obj-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_arch.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf.o perf_cpum_sf.o diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c index 31430e9bcfdd..7650f2adb5cf 100644 --- a/arch/s390/kernel/debug.c +++ b/arch/s390/kernel/debug.c @@ -1414,6 +1414,9 @@ static inline char *debug_get_user_string(const char __user *user_buf, { char *buffer; + if (!user_len) + return ERR_PTR(-EINVAL); + buffer = memdup_user_nul(user_buf, user_len); if (IS_ERR(buffer)) return buffer; @@ -1584,6 +1587,11 @@ static int debug_input_flush_fn(debug_info_t *id, struct debug_view *view, char input_buf[1]; int rc = user_len; + if (!user_len) { + rc = -EINVAL; + goto out; + } + if (user_len > 0x10000) user_len = 0x10000; if (*offset != 0) { diff --git a/arch/s390/kernel/ima_arch.c b/arch/s390/kernel/ima_arch.c deleted file mode 100644 index f3c3e6e1c5d3..000000000000 --- a/arch/s390/kernel/ima_arch.c +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include - -bool arch_ima_get_secureboot(void) -{ - return ipl_secure_flag; -} - -const char * const *arch_get_ima_policy(void) -{ - return NULL; -} diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 049c557c452f..3c346b02ceb9 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -2387,6 +2388,11 @@ void __no_stack_protector s390_reset_system(void) diag_amode31_ops.diag308_reset(); } +bool arch_get_secureboot(void) +{ + return ipl_secure_flag; +} + #ifdef CONFIG_KEXEC_FILE int ipl_report_add_component(struct ipl_report *report, struct kexec_buf *kbuf, diff --git a/arch/s390/kvm/dat.h b/arch/s390/kvm/dat.h index 874cc962e196..8f8278c44879 100644 --- a/arch/s390/kvm/dat.h +++ b/arch/s390/kvm/dat.h @@ -104,6 +104,11 @@ union pte { } tok; }; +#define _SEGMENT_FR_MASK (_SEGMENT_MASK >> PAGE_SHIFT) +#define _REGION3_FR_MASK (_REGION3_MASK >> PAGE_SHIFT) +#define _PAGES_PER_SEGMENT _PAGE_ENTRIES +#define _PAGES_PER_REGION3 (_PAGES_PER_SEGMENT * _CRST_ENTRIES) + /* Soft dirty, needed as macro for atomic operations on ptes */ #define _PAGE_SD 0x002 diff --git a/arch/s390/kvm/faultin.c b/arch/s390/kvm/faultin.c index e37cd18200f5..ddf0ca71f374 100644 --- a/arch/s390/kvm/faultin.c +++ b/arch/s390/kvm/faultin.c @@ -109,7 +109,7 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa scoped_guard(read_lock, &kvm->mmu_lock) { if (!mmu_invalidate_retry_gfn(kvm, inv_seq, f->gfn)) { f->valid = true; - rc = gmap_link(mc, kvm->arch.gmap, f); + rc = gmap_link(mc, kvm->arch.gmap, f, slot); kvm_release_faultin_page(kvm, f->page, !!rc, f->write_attempt); f->page = NULL; } diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index 290e03a13a95..b07accd19618 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c @@ -1471,7 +1471,7 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni lockdep_assert_held(&sg->kvm->mmu_lock); lockdep_assert_held(&sg->parent->children_lock); - gfn = f->gfn & gpa_to_gfn(is_pmd(*table) ? _SEGMENT_MASK : _REGION3_MASK); + gfn = f->gfn & (is_pmd(*table) ? _SEGMENT_FR_MASK : _REGION3_FR_MASK); scoped_guard(spinlock, &sg->host_to_rmap_lock) rc = gmap_insert_rmap(sg, gfn, gpa_to_gfn(raddr), host->h.tt); if (rc) diff --git a/arch/s390/kvm/gmap.c b/arch/s390/kvm/gmap.c index 0111d31e0386..3c26e35af0ef 100644 --- a/arch/s390/kvm/gmap.c +++ b/arch/s390/kvm/gmap.c @@ -618,14 +618,33 @@ int gmap_try_fixup_minor(struct gmap *gmap, struct guest_fault *fault) return rc; } -static inline bool gmap_2g_allowed(struct gmap *gmap, gfn_t gfn) +static inline bool gmap_2g_allowed(struct gmap *gmap, struct guest_fault *f, + struct kvm_memory_slot *slot) { return false; } -static inline bool gmap_1m_allowed(struct gmap *gmap, gfn_t gfn) +/** + * gmap_1m_allowed() - Check whether a 1M hugepage is allowed. + * @gmap: The gmap of the guest. + * @f: Describes the fault that is being resolved. + * @slot: The memslot the faulting address belongs to. + * + * The function checks whether the GMAP_FLAG_ALLOW_HPAGE_1M flag is set for + * @gmap, whether the offset of the address in the 1M virtual frame is the + * same as the offset in the physical 1M frame, and finally whether the whole + * 1M page would fit in the given memslot. + * + * Return: true if a 1M hugepage is allowed to back the faulting address, false + * otherwise. + */ +static inline bool gmap_1m_allowed(struct gmap *gmap, struct guest_fault *f, + struct kvm_memory_slot *slot) { - return test_bit(GMAP_FLAG_ALLOW_HPAGE_1M, &gmap->flags); + return test_bit(GMAP_FLAG_ALLOW_HPAGE_1M, &gmap->flags) && + !((f->gfn ^ f->pfn) & ~_SEGMENT_FR_MASK) && + slot->base_gfn <= ALIGN_DOWN(f->gfn, _PAGES_PER_SEGMENT) && + slot->base_gfn + slot->npages >= ALIGN(f->gfn + 1, _PAGES_PER_SEGMENT); } static int _gmap_link(struct kvm_s390_mmu_cache *mc, struct gmap *gmap, int level, @@ -679,7 +698,8 @@ static int _gmap_link(struct kvm_s390_mmu_cache *mc, struct gmap *gmap, int leve return rc; } -int gmap_link(struct kvm_s390_mmu_cache *mc, struct gmap *gmap, struct guest_fault *f) +int gmap_link(struct kvm_s390_mmu_cache *mc, struct gmap *gmap, struct guest_fault *f, + struct kvm_memory_slot *slot) { unsigned int order; int level; @@ -689,9 +709,9 @@ int gmap_link(struct kvm_s390_mmu_cache *mc, struct gmap *gmap, struct guest_fau level = TABLE_TYPE_PAGE_TABLE; if (f->page) { order = folio_order(page_folio(f->page)); - if (order >= get_order(_REGION3_SIZE) && gmap_2g_allowed(gmap, f->gfn)) + if (order >= get_order(_REGION3_SIZE) && gmap_2g_allowed(gmap, f, slot)) level = TABLE_TYPE_REGION3; - else if (order >= get_order(_SEGMENT_SIZE) && gmap_1m_allowed(gmap, f->gfn)) + else if (order >= get_order(_SEGMENT_SIZE) && gmap_1m_allowed(gmap, f, slot)) level = TABLE_TYPE_SEGMENT; } return _gmap_link(mc, gmap, level, f); diff --git a/arch/s390/kvm/gmap.h b/arch/s390/kvm/gmap.h index 31ea13fda142..96ee1395a592 100644 --- a/arch/s390/kvm/gmap.h +++ b/arch/s390/kvm/gmap.h @@ -91,7 +91,8 @@ struct gmap *gmap_new(struct kvm *kvm, gfn_t limit); struct gmap *gmap_new_child(struct gmap *parent, gfn_t limit); void gmap_remove_child(struct gmap *child); void gmap_dispose(struct gmap *gmap); -int gmap_link(struct kvm_s390_mmu_cache *mc, struct gmap *gmap, struct guest_fault *fault); +int gmap_link(struct kvm_s390_mmu_cache *mc, struct gmap *gmap, struct guest_fault *fault, + struct kvm_memory_slot *slot); void gmap_sync_dirty_log(struct gmap *gmap, gfn_t start, gfn_t end); int gmap_set_limit(struct gmap *gmap, gfn_t limit); int gmap_ucas_translate(struct kvm_s390_mmu_cache *mc, struct gmap *gmap, gpa_t *gaddr); diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 7cb8ce833b62..3bcdbbbb6891 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -956,6 +956,9 @@ static int __must_check __deliver_service(struct kvm_vcpu *vcpu) set_bit(IRQ_PEND_EXT_SERVICE, &fi->masked_irqs); spin_unlock(&fi->lock); + if (!ext.ext_params) + return 0; + VCPU_EVENT(vcpu, 4, "deliver: sclp parameter 0x%x", ext.ext_params); vcpu->stat.deliver_service_signal++; @@ -3307,8 +3310,7 @@ static void aen_host_forward(unsigned long si) struct zpci_gaite *gaite; struct kvm *kvm; - gaite = (struct zpci_gaite *)aift->gait + - (si * sizeof(struct zpci_gaite)); + gaite = aift->gait + si; if (gaite->count == 0) return; if (gaite->aisb != 0) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index d7838334a338..e09960c2e6ed 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -629,6 +629,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_IRQFD_RESAMPLE: case KVM_CAP_S390_USER_OPEREXEC: case KVM_CAP_S390_KEYOP: + case KVM_CAP_S390_VSIE_ESAMODE: r = 1; break; case KVM_CAP_SET_GUEST_DEBUG2: @@ -926,6 +927,11 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) icpt_operexc_on_all_vcpus(kvm); r = 0; break; + case KVM_CAP_S390_VSIE_ESAMODE: + VM_EVENT(kvm, 3, "%s", "ENABLE: CAP_S390_VSIE_ESAMODE"); + kvm->arch.allow_vsie_esamode = 1; + r = 0; + break; default: r = -EINVAL; break; @@ -5648,9 +5654,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *new, enum kvm_mr_change change) { - gpa_t size; - - if (kvm_is_ucontrol(kvm) && new->id < KVM_USER_MEM_SLOTS) + if (kvm_is_ucontrol(kvm) && new && new->id < KVM_USER_MEM_SLOTS) return -EINVAL; /* When we are protected, we should not change the memory slots */ @@ -5659,20 +5663,14 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, if (change != KVM_MR_DELETE && change != KVM_MR_FLAGS_ONLY) { /* - * A few sanity checks. We can have memory slots which have to be - * located/ended at a segment boundary (1MB). The memory in userland is - * ok to be fragmented into various different vmas. It is okay to mmap() - * and munmap() stuff in this slot after doing this call at any time + * A few sanity checks. The memory in userland is ok to be + * fragmented into various different vmas. It is okay to mmap() + * and munmap() stuff in this slot after doing this call at any + * time. */ - - if (new->userspace_addr & 0xffffful) + if (new->userspace_addr & ~PAGE_MASK) return -EINVAL; - - size = new->npages * PAGE_SIZE; - if (size & 0xffffful) - return -EINVAL; - - if ((new->base_gfn * PAGE_SIZE) + size > kvm->arch.mem_limit) + if ((new->base_gfn + new->npages) * PAGE_SIZE > kvm->arch.mem_limit) return -EINVAL; } diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index bf1d7798c1af..dc0573b7aa4b 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -122,7 +122,9 @@ static inline int kvm_is_ucontrol(struct kvm *kvm) #endif } -#define GUEST_PREFIX_SHIFT 13 +#define GUEST_PREFIX_SHIFT 12 +#define GUEST_PREFIX_MASK_ZARCH 0x7fffe +#define GUEST_PREFIX_MASK_ESA 0x7ffff static inline u32 kvm_s390_get_prefix(struct kvm_vcpu *vcpu) { return vcpu->arch.sie_block->prefix << GUEST_PREFIX_SHIFT; @@ -133,6 +135,7 @@ static inline void kvm_s390_set_prefix(struct kvm_vcpu *vcpu, u32 prefix) VCPU_EVENT(vcpu, 3, "set prefix of cpu %03u to 0x%x", vcpu->vcpu_id, prefix); vcpu->arch.sie_block->prefix = prefix >> GUEST_PREFIX_SHIFT; + vcpu->arch.sie_block->prefix &= GUEST_PREFIX_MASK_ZARCH; kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu); kvm_make_request(KVM_REQ_REFRESH_GUEST_PREFIX, vcpu); } diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 86d93e8dddae..5b075c38998e 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -166,7 +166,7 @@ static int kvm_zpci_set_airq(struct zpci_dev *zdev) fib.fmt0.noi = airq_iv_end(zdev->aibv); fib.fmt0.aibv = virt_to_phys(zdev->aibv->vector); fib.fmt0.aibvo = 0; - fib.fmt0.aisb = virt_to_phys(aift->sbv->vector + (zdev->aisb / 64) * 8); + fib.fmt0.aisb = virt_to_phys(aift->sbv->vector) + (zdev->aisb / 64) * 8; fib.fmt0.aisbo = zdev->aisb & 63; fib.gd = zdev->gisa; @@ -290,8 +290,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, phys_to_virt(fib->fmt0.aibv)); spin_lock_irq(&aift->gait_lock); - gaite = (struct zpci_gaite *)aift->gait + (zdev->aisb * - sizeof(struct zpci_gaite)); + gaite = aift->gait + zdev->aisb; /* If assist not requested, host will get all alerts */ if (assist) @@ -309,7 +308,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, /* Update guest FIB for re-issue */ fib->fmt0.aisbo = zdev->aisb & 63; - fib->fmt0.aisb = virt_to_phys(aift->sbv->vector + (zdev->aisb / 64) * 8); + fib->fmt0.aisb = virt_to_phys(aift->sbv->vector) + (zdev->aisb / 64) * 8; fib->fmt0.isc = gisc; /* Save some guest fib values in the host for later use */ @@ -357,8 +356,7 @@ static int kvm_s390_pci_aif_disable(struct zpci_dev *zdev, bool force) if (zdev->kzdev->fib.fmt0.aibv == 0) goto out; spin_lock_irq(&aift->gait_lock); - gaite = (struct zpci_gaite *)aift->gait + (zdev->aisb * - sizeof(struct zpci_gaite)); + gaite = aift->gait + zdev->aisb; isc = gaite->gisc; gaite->count--; if (gaite->count == 0) { diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index a3250ad83a8e..cc0553da14cb 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -714,12 +714,13 @@ int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu) { psw_t *gpsw = &vcpu->arch.sie_block->gpsw; psw32_t new_psw; - u64 addr; + u64 addr, iaddr; int rc; u8 ar; vcpu->stat.instruction_lpsw++; + iaddr = gpsw->addr - kvm_s390_get_ilen(vcpu); if (gpsw->mask & PSW_MASK_PSTATE) return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); @@ -737,18 +738,20 @@ int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu) gpsw->addr = new_psw.addr & ~PSW32_ADDR_AMODE; if (!is_valid_psw(gpsw)) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + vcpu->arch.sie_block->gbea = iaddr; return 0; } static int handle_lpswe(struct kvm_vcpu *vcpu) { psw_t new_psw; - u64 addr; + u64 addr, iaddr; int rc; u8 ar; vcpu->stat.instruction_lpswe++; + iaddr = vcpu->arch.sie_block->gpsw.addr - kvm_s390_get_ilen(vcpu); if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); @@ -761,6 +764,7 @@ static int handle_lpswe(struct kvm_vcpu *vcpu) vcpu->arch.sie_block->gpsw = new_psw; if (!is_valid_psw(&vcpu->arch.sie_block->gpsw)) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + vcpu->arch.sie_block->gbea = iaddr; return 0; } diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c index 72895dddc39a..e5a23f1c9749 100644 --- a/arch/s390/kvm/vsie.c +++ b/arch/s390/kvm/vsie.c @@ -125,8 +125,8 @@ static int prepare_cpuflags(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) struct kvm_s390_sie_block *scb_o = vsie_page->scb_o; int newflags, cpuflags = atomic_read(&scb_o->cpuflags); - /* we don't allow ESA/390 guests */ - if (!(cpuflags & CPUSTAT_ZARCH)) + /* we don't allow ESA/390 guests unless explicitly enabled */ + if (!(cpuflags & CPUSTAT_ZARCH) && !vcpu->kvm->arch.allow_vsie_esamode) return set_validity_icpt(scb_s, 0x0001U); if (cpuflags & (CPUSTAT_RRF | CPUSTAT_MCDS)) @@ -135,7 +135,9 @@ static int prepare_cpuflags(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) return set_validity_icpt(scb_s, 0x0007U); /* intervention requests will be set later */ - newflags = CPUSTAT_ZARCH; + newflags = 0; + if (cpuflags & CPUSTAT_ZARCH) + newflags = CPUSTAT_ZARCH; if (cpuflags & CPUSTAT_GED && test_kvm_facility(vcpu->kvm, 8)) newflags |= CPUSTAT_GED; if (cpuflags & CPUSTAT_GED2 && test_kvm_facility(vcpu->kvm, 78)) { @@ -385,6 +387,17 @@ end: return 0; } +static void shadow_esa(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) +{ + struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; + + /* Ensure these bits are indeed turned off */ + scb_s->eca &= ~ECA_VX; + scb_s->ecb &= ~(ECB_GS | ECB_TE); + scb_s->ecb3 &= ~ECB3_RI; + scb_s->ecd &= ~ECD_HOSTREGMGMT; +} + /* shadow (round up/down) the ibc to avoid validity icpt */ static void prepare_ibc(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) { @@ -466,7 +479,7 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s; /* READ_ONCE does not work on bitfields - use a temporary variable */ const uint32_t __new_prefix = scb_o->prefix; - const uint32_t new_prefix = READ_ONCE(__new_prefix); + uint32_t new_prefix = READ_ONCE(__new_prefix); const bool wants_tx = READ_ONCE(scb_o->ecb) & ECB_TE; bool had_tx = scb_s->ecb & ECB_TE; unsigned long new_mso = 0; @@ -514,6 +527,11 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) scb_s->icpua = scb_o->icpua; + if (!(atomic_read(&scb_s->cpuflags) & CPUSTAT_ZARCH)) + new_prefix &= GUEST_PREFIX_MASK_ESA; + else + new_prefix &= GUEST_PREFIX_MASK_ZARCH; + if (!(atomic_read(&scb_s->cpuflags) & CPUSTAT_SM)) new_mso = READ_ONCE(scb_o->mso) & 0xfffffffffff00000UL; /* if the hva of the prefix changes, we have to remap the prefix */ @@ -588,6 +606,9 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) scb_s->hpid = HPID_VSIE; scb_s->cpnc = scb_o->cpnc; + if (!(atomic_read(&scb_s->cpuflags) & CPUSTAT_ZARCH)) + shadow_esa(vcpu, vsie_page); + prepare_ibc(vcpu, vsie_page); rc = shadow_crycb(vcpu, vsie_page); out: diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile index f43f897d3fc0..2bf47204f6ab 100644 --- a/arch/s390/lib/Makefile +++ b/arch/s390/lib/Makefile @@ -5,7 +5,7 @@ lib-y += delay.o string.o uaccess.o find.o spinlock.o tishift.o lib-y += csum-partial.o -obj-y += mem.o xor.o +obj-y += mem.o lib-$(CONFIG_KPROBES) += probes.o lib-$(CONFIG_UPROBES) += probes.o obj-$(CONFIG_S390_KPROBES_SANITY_TEST) += test_kprobes_s390.o diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 191cc53caead..028aeb9c48d6 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -438,7 +438,7 @@ void do_secure_storage_access(struct pt_regs *regs) panic("Unexpected PGM 0x3d with TEID bit 61=0"); } if (is_kernel_fault(regs)) { - folio = phys_to_folio(addr); + folio = virt_to_folio((void *)addr); if (unlikely(!folio_try_get(folio))) return; rc = uv_convert_from_secure(folio_to_phys(folio)); diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index d08d159b6319..94128fe6be23 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c @@ -2312,38 +2312,22 @@ static struct bpf_binary_header *bpf_jit_alloc(struct bpf_jit *jit, /* * Compile eBPF program "fp" */ -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *fp) { - struct bpf_prog *tmp, *orig_fp = fp; struct bpf_binary_header *header; struct s390_jit_data *jit_data; - bool tmp_blinded = false; bool extra_pass = false; struct bpf_jit jit; int pass; if (!fp->jit_requested) - return orig_fp; - - tmp = bpf_jit_blind_constants(fp); - /* - * If blinding was requested and we failed during blinding, - * we must fall back to the interpreter. - */ - if (IS_ERR(tmp)) - return orig_fp; - if (tmp != fp) { - tmp_blinded = true; - fp = tmp; - } + return fp; jit_data = fp->aux->jit_data; if (!jit_data) { jit_data = kzalloc_obj(*jit_data); - if (!jit_data) { - fp = orig_fp; - goto out; - } + if (!jit_data) + return fp; fp->aux->jit_data = jit_data; } if (jit_data->ctx.addrs) { @@ -2356,34 +2340,27 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) memset(&jit, 0, sizeof(jit)); jit.addrs = kvcalloc(fp->len + 1, sizeof(*jit.addrs), GFP_KERNEL); - if (jit.addrs == NULL) { - fp = orig_fp; - goto free_addrs; - } + if (jit.addrs == NULL) + goto out_err; /* * Three initial passes: * - 1/2: Determine clobbered registers * - 3: Calculate program size and addrs array */ for (pass = 1; pass <= 3; pass++) { - if (bpf_jit_prog(&jit, fp, extra_pass)) { - fp = orig_fp; - goto free_addrs; - } + if (bpf_jit_prog(&jit, fp, extra_pass)) + goto out_err; } /* * Final pass: Allocate and generate program */ header = bpf_jit_alloc(&jit, fp); - if (!header) { - fp = orig_fp; - goto free_addrs; - } + if (!header) + goto out_err; skip_init_ctx: if (bpf_jit_prog(&jit, fp, extra_pass)) { bpf_jit_binary_free(header); - fp = orig_fp; - goto free_addrs; + goto out_err; } if (bpf_jit_enable > 1) { bpf_jit_dump(fp->len, jit.size, pass, jit.prg_buf); @@ -2392,8 +2369,7 @@ skip_init_ctx: if (!fp->is_func || extra_pass) { if (bpf_jit_binary_lock_ro(header)) { bpf_jit_binary_free(header); - fp = orig_fp; - goto free_addrs; + goto out_err; } } else { jit_data->header = header; @@ -2411,11 +2387,16 @@ free_addrs: kfree(jit_data); fp->aux->jit_data = NULL; } -out: - if (tmp_blinded) - bpf_jit_prog_release_other(fp, fp == orig_fp ? - tmp : orig_fp); + return fp; + +out_err: + if (extra_pass) { + fp->bpf_func = NULL; + fp->jited = 0; + fp->jited_len = 0; + } + goto free_addrs; } bool bpf_jit_supports_kfunc_call(void) diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c index c2444a23e26c..d98d97df792a 100644 --- a/arch/s390/pci/pci_sysfs.c +++ b/arch/s390/pci/pci_sysfs.c @@ -187,6 +187,17 @@ static ssize_t index_show(struct device *dev, } static DEVICE_ATTR_RO(index); +static ssize_t zpci_uid_slot_show(struct pci_slot *slot, char *buf) +{ + struct zpci_dev *zdev = container_of(slot->hotplug, struct zpci_dev, + hotplug_slot); + + return sysfs_emit(buf, "0x%x\n", zdev->uid); +} + +static struct pci_slot_attribute zpci_slot_attr_uid = + __ATTR(uid, 0444, zpci_uid_slot_show, NULL); + static umode_t zpci_index_is_visible(struct kobject *kobj, struct attribute *attr, int n) { @@ -243,6 +254,15 @@ const struct attribute_group pfip_attr_group = { .attrs = pfip_attrs, }; +static struct attribute *zpci_slot_attrs[] = { + &zpci_slot_attr_uid.attr, + NULL, +}; + +const struct attribute_group zpci_slot_attr_group = { + .attrs = zpci_slot_attrs, +}; + static struct attribute *clp_fw_attrs[] = { &uid_checking_attr.attr, NULL, diff --git a/arch/sh/configs/dreamcast_defconfig b/arch/sh/configs/dreamcast_defconfig index dd58797e8298..b31bf17fe112 100644 --- a/arch/sh/configs/dreamcast_defconfig +++ b/arch/sh/configs/dreamcast_defconfig @@ -50,7 +50,6 @@ CONFIG_HW_RANDOM=y CONFIG_WATCHDOG=y CONFIG_SH_WDT=y CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_PVR2=y CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FONTS=y diff --git a/arch/sh/configs/hp6xx_defconfig b/arch/sh/configs/hp6xx_defconfig index 04a9fcb4342a..b6116a203a27 100644 --- a/arch/sh/configs/hp6xx_defconfig +++ b/arch/sh/configs/hp6xx_defconfig @@ -35,7 +35,6 @@ CONFIG_SERIAL_SH_SCI_CONSOLE=y CONFIG_LEGACY_PTY_COUNT=64 # CONFIG_HWMON is not set CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_HIT=y CONFIG_FB_SH_MOBILE_LCDC=y CONFIG_FRAMEBUFFER_CONSOLE=y diff --git a/arch/sh/configs/se7343_defconfig b/arch/sh/configs/se7343_defconfig index 2d4d1f974f14..b3ce8a502787 100644 --- a/arch/sh/configs/se7343_defconfig +++ b/arch/sh/configs/se7343_defconfig @@ -57,7 +57,6 @@ CONFIG_I2C=y CONFIG_I2C_SH_MOBILE=y # CONFIG_HWMON is not set CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_SH_MOBILE_LCDC=m CONFIG_SOUND=y CONFIG_SND=y diff --git a/arch/sh/configs/se7780_defconfig b/arch/sh/configs/se7780_defconfig index 13fa6a59b8f1..9e96b000cb99 100644 --- a/arch/sh/configs/se7780_defconfig +++ b/arch/sh/configs/se7780_defconfig @@ -60,7 +60,6 @@ CONFIG_SERIAL_SH_SCI_CONSOLE=y # CONFIG_HW_RANDOM is not set CONFIG_THERMAL=y CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_SH_MOBILE_LCDC=m CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y diff --git a/arch/sh/drivers/platform_early.c b/arch/sh/drivers/platform_early.c index 48ddbc547bd9..ca73442a03a6 100644 --- a/arch/sh/drivers/platform_early.c +++ b/arch/sh/drivers/platform_early.c @@ -1,4 +1,4 @@ -// SPDX--License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0 #include #include diff --git a/arch/sh/include/asm/platform_early.h b/arch/sh/include/asm/platform_early.h index fc802137c37d..00b6e6dc4ac4 100644 --- a/arch/sh/include/asm/platform_early.h +++ b/arch/sh/include/asm/platform_early.h @@ -1,4 +1,4 @@ -/* SPDX--License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __PLATFORM_EARLY__ #define __PLATFORM_EARLY__ diff --git a/arch/sh/include/asm/setup.h b/arch/sh/include/asm/setup.h index 63c9efc06348..8488f76b48b4 100644 --- a/arch/sh/include/asm/setup.h +++ b/arch/sh/include/asm/setup.h @@ -7,7 +7,7 @@ /* * This is set up by the setup-routine at boot-time */ -extern unsigned char *boot_params_page; +extern unsigned char boot_params_page[]; #define PARAM boot_params_page #define MOUNT_ROOT_RDONLY (*(unsigned long *) (PARAM+0x000)) diff --git a/arch/sh/include/cpu-sh3/cpu/dac.h b/arch/sh/include/cpu-sh3/cpu/dac.h index fd02331608a8..323ec8570bcd 100644 --- a/arch/sh/include/cpu-sh3/cpu/dac.h +++ b/arch/sh/include/cpu-sh3/cpu/dac.h @@ -2,6 +2,8 @@ #ifndef __ASM_CPU_SH3_DAC_H #define __ASM_CPU_SH3_DAC_H +#include + /* * Copyright (C) 2003 Andriy Skulysh */ diff --git a/arch/sh/include/uapi/asm/auxvec.h b/arch/sh/include/uapi/asm/auxvec.h index 8eb47ede7193..63fcc39e2c6a 100644 --- a/arch/sh/include/uapi/asm/auxvec.h +++ b/arch/sh/include/uapi/asm/auxvec.h @@ -13,14 +13,10 @@ */ #define AT_FPUCW 18 /* Used FPU control word. */ -#if defined(CONFIG_VSYSCALL) || !defined(__KERNEL__) /* - * Only define this in the vsyscall case, the entry point to - * the vsyscall page gets placed here. The kernel will attempt - * to build a gate VMA we don't care about otherwise.. + * The entry point to the vsyscall page gets placed here. */ #define AT_SYSINFO_EHDR 33 -#endif /* * More complete cache descriptions than AT_[DIU]CACHEBSIZE. If the diff --git a/arch/sparc/include/asm/asm-prototypes.h b/arch/sparc/include/asm/asm-prototypes.h index 08810808ca6d..bbd1a8afaabf 100644 --- a/arch/sparc/include/asm/asm-prototypes.h +++ b/arch/sparc/include/asm/asm-prototypes.h @@ -14,7 +14,6 @@ #include #include #include -#include void *__memscan_zero(void *, size_t); void *__memscan_generic(void *, int, size_t); diff --git a/arch/sparc/include/asm/xor.h b/arch/sparc/include/asm/xor.h deleted file mode 100644 index f4c651e203c4..000000000000 --- a/arch/sparc/include/asm/xor.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef ___ASM_SPARC_XOR_H -#define ___ASM_SPARC_XOR_H -#if defined(__sparc__) && defined(__arch64__) -#include -#else -#include -#endif -#endif diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c index 30f171b7b00c..ecd6c8ae49c7 100644 --- a/arch/sparc/kernel/mdesc.c +++ b/arch/sparc/kernel/mdesc.c @@ -183,14 +183,12 @@ static struct mdesc_handle * __init mdesc_memblock_alloc(unsigned int mdesc_size static void __init mdesc_memblock_free(struct mdesc_handle *hp) { unsigned int alloc_size; - unsigned long start; BUG_ON(refcount_read(&hp->refcnt) != 0); BUG_ON(!list_empty(&hp->list)); alloc_size = PAGE_ALIGN(hp->handle_size); - start = __pa(hp); - memblock_free_late(start, alloc_size); + memblock_free(hp, alloc_size); } static struct mdesc_mem_ops memblock_mdesc_ops = { diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile index 783bdec0d7be..dd10cdd6f062 100644 --- a/arch/sparc/lib/Makefile +++ b/arch/sparc/lib/Makefile @@ -48,7 +48,7 @@ lib-$(CONFIG_SPARC64) += GENmemcpy.o GENcopy_from_user.o GENcopy_to_user.o lib-$(CONFIG_SPARC64) += GENpatch.o GENpage.o GENbzero.o lib-$(CONFIG_SPARC64) += copy_in_user.o memmove.o -lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o xor.o hweight.o ffs.o +lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o hweight.o ffs.o obj-$(CONFIG_SPARC64) += iomap.o obj-$(CONFIG_SPARC32) += atomic32.o diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c index b23d1c645ae5..2fa0e9375127 100644 --- a/arch/sparc/net/bpf_jit_comp_64.c +++ b/arch/sparc/net/bpf_jit_comp_64.c @@ -1477,39 +1477,24 @@ struct sparc64_jit_data { struct jit_ctx ctx; }; -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) { - struct bpf_prog *tmp, *orig_prog = prog; struct sparc64_jit_data *jit_data; struct bpf_binary_header *header; u32 prev_image_size, image_size; - bool tmp_blinded = false; bool extra_pass = false; struct jit_ctx ctx; u8 *image_ptr; int pass, i; if (!prog->jit_requested) - return orig_prog; - - tmp = bpf_jit_blind_constants(prog); - /* If blinding was requested and we failed during blinding, - * we must fall back to the interpreter. - */ - if (IS_ERR(tmp)) - return orig_prog; - if (tmp != prog) { - tmp_blinded = true; - prog = tmp; - } + return prog; jit_data = prog->aux->jit_data; if (!jit_data) { jit_data = kzalloc_obj(*jit_data); - if (!jit_data) { - prog = orig_prog; - goto out; - } + if (!jit_data) + return prog; prog->aux->jit_data = jit_data; } if (jit_data->ctx.offset) { @@ -1527,10 +1512,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) ctx.prog = prog; ctx.offset = kmalloc_array(prog->len, sizeof(unsigned int), GFP_KERNEL); - if (ctx.offset == NULL) { - prog = orig_prog; - goto out_off; - } + if (ctx.offset == NULL) + goto out_err; /* Longest sequence emitted is for bswap32, 12 instructions. Pre-cook * the offset array so that we converge faster. @@ -1543,10 +1526,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) ctx.idx = 0; build_prologue(&ctx); - if (build_body(&ctx)) { - prog = orig_prog; - goto out_off; - } + if (build_body(&ctx)) + goto out_err; build_epilogue(&ctx); if (bpf_jit_enable > 1) @@ -1569,10 +1550,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) image_size = sizeof(u32) * ctx.idx; header = bpf_jit_binary_alloc(image_size, &image_ptr, sizeof(u32), jit_fill_hole); - if (header == NULL) { - prog = orig_prog; - goto out_off; - } + if (header == NULL) + goto out_err; ctx.image = (u32 *)image_ptr; skip_init_ctx: @@ -1582,8 +1561,7 @@ skip_init_ctx: if (build_body(&ctx)) { bpf_jit_binary_free(header); - prog = orig_prog; - goto out_off; + goto out_err; } build_epilogue(&ctx); @@ -1592,8 +1570,7 @@ skip_init_ctx: pr_err("bpf_jit: Failed to converge, prev_size=%u size=%d\n", prev_image_size, ctx.idx * 4); bpf_jit_binary_free(header); - prog = orig_prog; - goto out_off; + goto out_err; } if (bpf_jit_enable > 1) @@ -1604,8 +1581,7 @@ skip_init_ctx: if (!prog->is_func || extra_pass) { if (bpf_jit_binary_lock_ro(header)) { bpf_jit_binary_free(header); - prog = orig_prog; - goto out_off; + goto out_err; } } else { jit_data->ctx = ctx; @@ -1624,9 +1600,14 @@ out_off: kfree(jit_data); prog->aux->jit_data = NULL; } -out: - if (tmp_blinded) - bpf_jit_prog_release_other(prog, prog == orig_prog ? - tmp : orig_prog); + return prog; + +out_err: + if (extra_pass) { + prog->bpf_func = NULL; + prog->jited = 0; + prog->jited_len = 0; + } + goto out_off; } diff --git a/arch/um/Kconfig b/arch/um/Kconfig index 098cda44db22..d9541d13d9eb 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -11,7 +11,9 @@ config UML select ARCH_HAS_CACHE_LINE_SIZE select ARCH_HAS_CPU_FINALIZE_INIT select ARCH_HAS_FORTIFY_SOURCE - select ARCH_HAS_GCOV_PROFILE_ALL + # Clang 20 & 21 miscompute __builtin_object_size() under -fprofile-arcs + # on 32-bit, causing spurious compile-time errors in check_copy_size(). + select ARCH_HAS_GCOV_PROFILE_ALL if !(!64BIT && CLANG_VERSION >= 200000 && CLANG_VERSION < 220100) select ARCH_HAS_KCOV select ARCH_HAS_STRNCPY_FROM_USER select ARCH_HAS_STRNLEN_USER diff --git a/arch/um/configs/x86_64_defconfig b/arch/um/configs/x86_64_defconfig index cf309c5406a2..af6ff784e2d3 100644 --- a/arch/um/configs/x86_64_defconfig +++ b/arch/um/configs/x86_64_defconfig @@ -60,5 +60,4 @@ CONFIG_PROC_KCORE=y CONFIG_TMPFS=y CONFIG_NLS=y CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y -CONFIG_FRAME_WARN=1024 CONFIG_DEBUG_KERNEL=y diff --git a/arch/um/drivers/cow_user.c b/arch/um/drivers/cow_user.c index 29b46581ddd1..dc1d1bcd85ec 100644 --- a/arch/um/drivers/cow_user.c +++ b/arch/um/drivers/cow_user.c @@ -15,6 +15,12 @@ #include "cow.h" #include "cow_sys.h" +/* + * arch/um/Makefile remaps strrchr to kernel_strrchr; call the kernel + * name directly to avoid glibc >= 2.43's C23 strrchr macro. + */ +char *kernel_strrchr(const char *, int); + #define PATH_LEN_V1 256 /* unsigned time_t works until year 2106 */ @@ -153,7 +159,7 @@ static int absolutize(char *to, int size, char *from) errno); return -1; } - slash = strrchr(from, '/'); + slash = kernel_strrchr(from, '/'); if (slash != NULL) { *slash = '\0'; if (chdir(from)) { diff --git a/arch/um/include/asm/pgtable.h b/arch/um/include/asm/pgtable.h index 19e0608fb649..88ea8434364d 100644 --- a/arch/um/include/asm/pgtable.h +++ b/arch/um/include/asm/pgtable.h @@ -112,13 +112,12 @@ static inline int pte_none(pte_t pte) */ static inline int pte_read(pte_t pte) { - return((pte_get_bits(pte, _PAGE_USER)) && - !(pte_get_bits(pte, _PAGE_PROTNONE))); + return !pte_get_bits(pte, _PAGE_PROTNONE); } -static inline int pte_exec(pte_t pte){ - return((pte_get_bits(pte, _PAGE_USER)) && - !(pte_get_bits(pte, _PAGE_PROTNONE))); +static inline int pte_exec(pte_t pte) +{ + return !pte_get_bits(pte, _PAGE_PROTNONE); } static inline int pte_write(pte_t pte) diff --git a/arch/um/include/asm/xor.h b/arch/um/include/asm/xor.h deleted file mode 100644 index 647fae200c5d..000000000000 --- a/arch/um/include/asm/xor.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _ASM_UM_XOR_H -#define _ASM_UM_XOR_H - -#ifdef CONFIG_64BIT -#undef CONFIG_X86_32 -#define TT_CPU_INF_XOR_DEFAULT (AVX_SELECT(&xor_block_sse_pf64)) -#else -#define CONFIG_X86_32 1 -#define TT_CPU_INF_XOR_DEFAULT (AVX_SELECT(&xor_block_8regs)) -#endif - -#include -#include <../../x86/include/asm/xor.h> -#include - -#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT -#undef XOR_SELECT_TEMPLATE -/* pick an arbitrary one - measuring isn't possible with inf-cpu */ -#define XOR_SELECT_TEMPLATE(x) \ - (time_travel_mode == TT_MODE_INFCPU ? TT_CPU_INF_XOR_DEFAULT : x) -#endif - -#endif diff --git a/arch/um/kernel/skas/stub.c b/arch/um/kernel/skas/stub.c index 67cab46a602c..e09216a20cb5 100644 --- a/arch/um/kernel/skas/stub.c +++ b/arch/um/kernel/skas/stub.c @@ -146,7 +146,7 @@ restart_wait: /* Receive the FDs */ num_fds = 0; fd_msg = msghdr.msg_control; - fd_map = (void *)&CMSG_DATA(fd_msg); + fd_map = (void *)CMSG_DATA(fd_msg); if (res == iov.iov_len && msghdr.msg_controllen > sizeof(struct cmsghdr)) num_fds = (fd_msg->cmsg_len - CMSG_LEN(0)) / sizeof(int); diff --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c index 198269e384c4..caef1deef795 100644 --- a/arch/um/kernel/skas/uaccess.c +++ b/arch/um/kernel/skas/uaccess.c @@ -170,8 +170,8 @@ static int strncpy_chunk_from_user(unsigned long from, int len, void *arg) char **to_ptr = arg, *to = *to_ptr; int n; - strncpy(to, (void *) from, len); - n = strnlen(to, len); + n = strnlen((void *) from, len); + memcpy_and_pad(to, len, (void *) from, n, 0); *to_ptr += n; if (n < len) diff --git a/arch/um/kernel/tlb.c b/arch/um/kernel/tlb.c index 39608cccf2c6..1f175716b474 100644 --- a/arch/um/kernel/tlb.c +++ b/arch/um/kernel/tlb.c @@ -29,10 +29,9 @@ static int kern_map(struct mm_id *mm_idp, unsigned long virt, unsigned long len, int prot, int phys_fd, unsigned long long offset) { - /* TODO: Why is executable needed to be always set in the kernel? */ return os_map_memory((void *)virt, phys_fd, offset, len, prot & UM_PROT_READ, prot & UM_PROT_WRITE, - 1); + prot & UM_PROT_EXEC); } static int kern_unmap(struct mm_id *mm_idp, @@ -165,6 +164,7 @@ int um_tlb_sync(struct mm_struct *mm) unsigned long addr, next; int ret = 0; + guard(spinlock_irqsave)(&mm->page_table_lock); guard(spinlock_irqsave)(&mm->context.sync_tlb_lock); if (mm->context.sync_tlb_range_to == 0) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 99bb5217649a..f3f7cb01d69d 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1885,6 +1885,7 @@ config X86_USER_SHADOW_STACK bool "X86 userspace shadow stack" depends on AS_WRUSS depends on X86_64 + depends on PER_VMA_LOCK select ARCH_USES_HIGH_VMA_FLAGS select ARCH_HAS_USER_SHADOW_STACK select X86_CET diff --git a/arch/x86/Makefile.um b/arch/x86/Makefile.um index c86cbd9cbba3..19c13afa474e 100644 --- a/arch/x86/Makefile.um +++ b/arch/x86/Makefile.um @@ -60,4 +60,6 @@ ELF_FORMAT := elf64-x86-64 LINK-$(CONFIG_LD_SCRIPT_DYN_RPATH) += -Wl,-rpath,/lib64 LINK-y += -m64 +vdso-install-y += arch/x86/um/vdso/vdso.so.dbg + endif diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c index 810ab21ffd99..4b9e105309c6 100644 --- a/arch/x86/events/core.c +++ b/arch/x86/events/core.c @@ -1294,13 +1294,16 @@ int x86_perf_rdpmc_index(struct perf_event *event) return event->hw.event_base_rdpmc; } -static inline int match_prev_assignment(struct hw_perf_event *hwc, +static inline int match_prev_assignment(struct perf_event *event, struct cpu_hw_events *cpuc, int i) { + struct hw_perf_event *hwc = &event->hw; + return hwc->idx == cpuc->assign[i] && - hwc->last_cpu == smp_processor_id() && - hwc->last_tag == cpuc->tags[i]; + hwc->last_cpu == smp_processor_id() && + hwc->last_tag == cpuc->tags[i] && + !is_acr_event_group(event); } static void x86_pmu_start(struct perf_event *event, int flags); @@ -1346,7 +1349,7 @@ static void x86_pmu_enable(struct pmu *pmu) * - no other event has used the counter since */ if (hwc->idx == -1 || - match_prev_assignment(hwc, cpuc, i)) + match_prev_assignment(event, cpuc, i)) continue; /* @@ -1367,7 +1370,7 @@ static void x86_pmu_enable(struct pmu *pmu) event = cpuc->event_list[i]; hwc = &event->hw; - if (!match_prev_assignment(hwc, cpuc, i)) + if (!match_prev_assignment(event, cpuc, i)) x86_assign_hw_event(event, cpuc, i); else if (i < n_running) continue; diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index d9488ade0f8e..dd1e3aa75ee9 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -3118,11 +3118,11 @@ static void intel_pmu_enable_fixed(struct perf_event *event) intel_set_masks(event, idx); /* - * Enable IRQ generation (0x8), if not PEBS, - * and enable ring-3 counting (0x2) and ring-0 counting (0x1) - * if requested: + * Enable IRQ generation (0x8), if not PEBS or self-reloaded + * ACR event, and enable ring-3 counting (0x2) and ring-0 + * counting (0x1) if requested: */ - if (!event->attr.precise_ip) + if (!event->attr.precise_ip && !is_acr_self_reload_event(event)) bits |= INTEL_FIXED_0_ENABLE_PMI; if (hwc->config & ARCH_PERFMON_EVENTSEL_USR) bits |= INTEL_FIXED_0_USER; @@ -3306,6 +3306,15 @@ static void intel_pmu_enable_event(struct perf_event *event) intel_set_masks(event, idx); static_call_cond(intel_pmu_enable_acr_event)(event); static_call_cond(intel_pmu_enable_event_ext)(event); + /* + * For self-reloaded ACR event, don't enable PMI since + * HW won't set overflow bit in GLOBAL_STATUS. Otherwise, + * the PMI would be recognized as a suspicious NMI. + */ + if (is_acr_self_reload_event(event)) + hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; + else if (!event->attr.precise_ip) + hwc->config |= ARCH_PERFMON_EVENTSEL_INT; __x86_pmu_enable_event(hwc, enable_mask); break; case INTEL_PMC_IDX_FIXED ... INTEL_PMC_IDX_FIXED_BTS - 1: @@ -3332,23 +3341,41 @@ static void intel_pmu_enable_event(struct perf_event *event) static void intel_pmu_acr_late_setup(struct cpu_hw_events *cpuc) { struct perf_event *event, *leader; - int i, j, idx; + int i, j, k, bit, idx; + /* + * FIXME: ACR mask parsing relies on cpuc->event_list[] (active events only). + * Disabling an ACR event causes bit-shifting errors in the acr_mask of + * remaining group members. As ACR sampling requires all events to be active, + * this limitation is acceptable for now. Revisit if independent event toggling + * is required. + */ for (i = 0; i < cpuc->n_events; i++) { leader = cpuc->event_list[i]; if (!is_acr_event_group(leader)) continue; - /* The ACR events must be contiguous. */ + /* Find the last event of the ACR group. */ for (j = i; j < cpuc->n_events; j++) { event = cpuc->event_list[j]; if (event->group_leader != leader->group_leader) break; - for_each_set_bit(idx, (unsigned long *)&event->attr.config2, X86_PMC_IDX_MAX) { - if (i + idx >= cpuc->n_events || - !is_acr_event_group(cpuc->event_list[i + idx])) - return; - __set_bit(cpuc->assign[i + idx], (unsigned long *)&event->hw.config1); + } + + /* + * Translate the user-space ACR mask (attr.config2) into the physical + * counter bitmask (hw.config1) for each ACR event in the group. + * NOTE: ACR event contiguity is guaranteed by intel_pmu_hw_config(). + */ + for (k = i; k < j; k++) { + event = cpuc->event_list[k]; + event->hw.config1 = 0; + for_each_set_bit(bit, (unsigned long *)&event->attr.config2, X86_PMC_IDX_MAX) { + idx = i + bit; + /* Event index of ACR group must locate in [i, j). */ + if (idx >= j || !is_acr_event_group(cpuc->event_list[idx])) + continue; + __set_bit(cpuc->assign[idx], (unsigned long *)&event->hw.config1); } } i = j - 1; @@ -7504,6 +7531,7 @@ static __always_inline void intel_pmu_init_pnc(struct pmu *pmu) hybrid(pmu, event_constraints) = intel_pnc_event_constraints; hybrid(pmu, pebs_constraints) = intel_pnc_pebs_event_constraints; hybrid(pmu, extra_regs) = intel_pnc_extra_regs; + static_call_update(intel_pmu_enable_acr_event, intel_pmu_enable_acr); } static __always_inline void intel_pmu_init_skt(struct pmu *pmu) diff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c index 44524a387c58..b5726b50e77d 100644 --- a/arch/x86/events/intel/pt.c +++ b/arch/x86/events/intel/pt.c @@ -1591,7 +1591,6 @@ void intel_pt_handle_vmx(int on) local_irq_restore(flags); } -EXPORT_SYMBOL_FOR_KVM(intel_pt_handle_vmx); /* * PMU callbacks diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h index fad87d3c8b2c..524668dcf4cc 100644 --- a/arch/x86/events/perf_event.h +++ b/arch/x86/events/perf_event.h @@ -137,6 +137,16 @@ static inline bool is_acr_event_group(struct perf_event *event) return check_leader_group(event->group_leader, PERF_X86_EVENT_ACR); } +static inline bool is_acr_self_reload_event(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + + if (hwc->idx < 0) + return false; + + return test_bit(hwc->idx, (unsigned long *)&hwc->config1); +} + struct amd_nb { int nb_id; /* NorthBridge id */ int refcnt; /* reference count */ diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index 86d17b195e79..1d506e5d6f46 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -473,6 +473,7 @@ #define X86_FEATURE_GP_ON_USER_CPUID (20*32+17) /* User CPUID faulting */ #define X86_FEATURE_PREFETCHI (20*32+20) /* Prefetch Data/Instruction to Cache Level */ +#define X86_FEATURE_AVX512_BMM (20*32+23) /* AVX512 Bit Matrix Multiply instructions */ #define X86_FEATURE_ERAPS (20*32+24) /* Enhanced Return Address Predictor Security */ #define X86_FEATURE_SBPB (20*32+27) /* Selective Branch Prediction Barrier */ #define X86_FEATURE_IBPB_BRTYPE (20*32+28) /* MSR_PRED_CMD[IBPB] flushes all branch type predictions */ diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 51b4cdbea061..be58b7f5c806 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -137,7 +137,8 @@ extern void __init efi_dump_pagetable(void); extern void __init efi_apply_memmap_quirks(void); extern int __init efi_reuse_config(u64 tables, int nr_tables); extern void efi_delete_dummy_variable(void); -extern void efi_crash_gracefully_on_page_fault(unsigned long phys_addr); +extern void efi_crash_gracefully_on_page_fault(unsigned long phys_addr, + const struct pt_regs *regs); extern void efi_unmap_boot_services(void); void arch_efi_call_virt_setup(void); @@ -401,9 +402,9 @@ extern int __init efi_memmap_split_count(efi_memory_desc_t *md, extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf, struct efi_mem_range *mem); -extern enum efi_secureboot_mode __x86_ima_efi_boot_mode(void); +enum efi_secureboot_mode __x86_efi_boot_mode(void); -#define arch_ima_efi_boot_mode __x86_ima_efi_boot_mode() +#define arch_efi_boot_mode __x86_efi_boot_mode() #ifdef CONFIG_EFI_RUNTIME_MAP int efi_get_runtime_map_size(void); diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h index de709fb5bd76..3776cf5382a2 100644 --- a/arch/x86/include/asm/kvm-x86-ops.h +++ b/arch/x86/include/asm/kvm-x86-ops.h @@ -1,8 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#if !defined(KVM_X86_OP) || !defined(KVM_X86_OP_OPTIONAL) -BUILD_BUG_ON(1) -#endif - +#if !defined(KVM_X86_OP) || \ + !defined(KVM_X86_OP_OPTIONAL) || \ + !defined(KVM_X86_OP_OPTIONAL_RET0) +#error Missing one or more KVM_X86_OP #defines +#else /* * KVM_X86_OP() and KVM_X86_OP_OPTIONAL() are used to help generate * both DECLARE/DEFINE_STATIC_CALL() invocations and @@ -148,6 +149,7 @@ KVM_X86_OP_OPTIONAL(alloc_apic_backing_page) KVM_X86_OP_OPTIONAL_RET0(gmem_prepare) KVM_X86_OP_OPTIONAL_RET0(gmem_max_mapping_level) KVM_X86_OP_OPTIONAL(gmem_invalidate) +#endif #undef KVM_X86_OP #undef KVM_X86_OP_OPTIONAL diff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h index f0aa6996811f..d5452b3433b7 100644 --- a/arch/x86/include/asm/kvm-x86-pmu-ops.h +++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h @@ -1,7 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#if !defined(KVM_X86_PMU_OP) || !defined(KVM_X86_PMU_OP_OPTIONAL) -BUILD_BUG_ON(1) -#endif +#if !defined(KVM_X86_PMU_OP) || \ + !defined(KVM_X86_PMU_OP_OPTIONAL) +#error Missing one or more KVM_X86_PMU_OP #defines +#else /* * KVM_X86_PMU_OP() and KVM_X86_PMU_OP_OPTIONAL() are used to help generate @@ -26,6 +27,7 @@ KVM_X86_PMU_OP_OPTIONAL(cleanup) KVM_X86_PMU_OP_OPTIONAL(write_global_ctrl) KVM_X86_PMU_OP(mediated_load) KVM_X86_PMU_OP(mediated_put) +#endif #undef KVM_X86_PMU_OP #undef KVM_X86_PMU_OP_OPTIONAL diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 6e4e3ef9b8c7..c470e40a00aa 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -40,7 +40,8 @@ #include #include #include -#include +#include + #include #define __KVM_HAVE_ARCH_VCPU_DEBUGFS @@ -1098,6 +1099,21 @@ struct kvm_vcpu_arch { */ bool pdptrs_from_userspace; + /* + * Set if an emulated nested VM-Enter to L2 is pending completion. KVM + * must not synthesize a VM-Exit to L1 before entering L2, as VM-Exits + * can only occur at instruction boundaries. The only exception is + * VMX's "notify" exits, which exist in large part to break the CPU out + * of infinite ucode loops, but can corrupt vCPU state in the process! + * + * For all intents and purposes, this is a boolean, but it's tracked as + * a u8 so that KVM can detect when userspace may have stuffed vCPU + * state and generated an architecturally-impossible VM-Exit. + */ +#define KVM_NESTED_RUN_PENDING 1 +#define KVM_NESTED_RUN_PENDING_UNTRUSTED 2 + u8 nested_run_pending; + #if IS_ENABLED(CONFIG_HYPERV) hpa_t hv_root_tdp; #endif @@ -1261,7 +1277,7 @@ struct kvm_x86_pmu_event_filter { __u32 nr_excludes; __u64 *includes; __u64 *excludes; - __u64 events[]; + __u64 events[] __counted_by(nevents); }; enum kvm_apicv_inhibit { @@ -1433,6 +1449,7 @@ struct kvm_arch { struct kvm_pit *vpit; #endif atomic_t vapics_in_nmi_mode; + struct mutex apic_map_lock; struct kvm_apic_map __rcu *apic_map; atomic_t apic_map_dirty; @@ -1440,8 +1457,22 @@ struct kvm_arch { bool apic_access_memslot_enabled; bool apic_access_memslot_inhibited; - /* Protects apicv_inhibit_reasons */ + /* + * Force apicv_update_lock and apicv_nr_irq_window_req to reside in a + * dedicated cacheline. They are write-mostly, whereas most everything + * else in kvm_arch is read-mostly. Note that apicv_inhibit_reasons is + * read-mostly: toggling VM-wide inhibits is rare; _checking_ for + * inhibits is common. + */ + ____cacheline_aligned + /* + * Protects apicv_inhibit_reasons and apicv_nr_irq_window_req (with an + * asterisk, see kvm_inc_or_dec_irq_window_inhibit() for details). + */ struct rw_semaphore apicv_update_lock; + atomic_t apicv_nr_irq_window_req; + ____cacheline_aligned + unsigned long apicv_inhibit_reasons; gpa_t wall_clock; @@ -2097,9 +2128,6 @@ void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end); int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3); -int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, - const void *val, int bytes); - extern bool tdp_enabled; u64 vcpu_tsc_khz(struct kvm_vcpu *vcpu); @@ -2316,6 +2344,18 @@ static inline void kvm_clear_apicv_inhibit(struct kvm *kvm, kvm_set_or_clear_apicv_inhibit(kvm, reason, false); } +void kvm_inc_or_dec_irq_window_inhibit(struct kvm *kvm, bool inc); + +static inline void kvm_inc_apicv_irq_window_req(struct kvm *kvm) +{ + kvm_inc_or_dec_irq_window_inhibit(kvm, true); +} + +static inline void kvm_dec_apicv_irq_window_req(struct kvm *kvm) +{ + kvm_inc_or_dec_irq_window_inhibit(kvm, false); +} + int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code, void *insn, int insn_len); void kvm_mmu_print_sptes(struct kvm_vcpu *vcpu, gpa_t gpa, const char *msg); diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 9dc6b610e4e2..86554de9a3f5 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -674,6 +674,9 @@ #define MSR_AMD64_DC_CFG 0xc0011022 #define MSR_AMD64_TW_CFG 0xc0011023 +#define MSR_AMD64_FP_CFG 0xc0011028 +#define MSR_AMD64_FP_CFG_ZEN1_DENORM_FIX_BIT 9 + #define MSR_AMD64_DE_CFG 0xc0011029 #define MSR_AMD64_DE_CFG_LFENCE_SERIALIZE_BIT 1 #define MSR_AMD64_DE_CFG_LFENCE_SERIALIZE BIT_ULL(MSR_AMD64_DE_CFG_LFENCE_SERIALIZE_BIT) @@ -800,9 +803,10 @@ #define MSR_AMD64_LBR_SELECT 0xc000010e /* Zen4 */ -#define MSR_ZEN4_BP_CFG 0xc001102e +#define MSR_ZEN4_BP_CFG 0xc001102e #define MSR_ZEN4_BP_CFG_BP_SPEC_REDUCE_BIT 4 #define MSR_ZEN4_BP_CFG_SHARED_BTB_FIX_BIT 5 +#define MSR_ZEN2_BP_CFG_BUG_FIX_BIT 33 /* Fam 19h MSRs */ #define MSR_F19H_UMC_PERF_CTL 0xc0010800 diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index 13e3e9a054cb..ac295ca6c92f 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -1284,6 +1284,18 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, } while (!try_cmpxchg((long *)&ptep->pte, (long *)&old_pte, *(long *)&new_pte)); } +/* + * Note: strictly-zero compare is narrower than pte_none(), but the gap is + * harmless: _PAGE_DIRTY and _PAGE_ACCESSED aren't set on untouched kernel PTEs. + */ +static inline bool ptep_try_set(pte_t *ptep, pte_t new_pte) +{ + pte_t old_pte = __pte(0); + + return try_cmpxchg((long *)&ptep->pte, (long *)&old_pte, *(long *)&new_pte); +} +#define ptep_try_set ptep_try_set + #define flush_tlb_fix_spurious_fault(vma, address, ptep) do { } while (0) #define __HAVE_ARCH_PMDP_SET_ACCESS_FLAGS @@ -1672,17 +1684,17 @@ static inline bool arch_has_hw_nonleaf_pmd_young(void) #endif #ifdef CONFIG_PAGE_TABLE_CHECK -static inline bool pte_user_accessible_page(pte_t pte, unsigned long addr) +static inline bool pte_user_accessible_page(struct mm_struct *mm, unsigned long addr, pte_t pte) { return (pte_val(pte) & _PAGE_PRESENT) && (pte_val(pte) & _PAGE_USER); } -static inline bool pmd_user_accessible_page(pmd_t pmd, unsigned long addr) +static inline bool pmd_user_accessible_page(struct mm_struct *mm, unsigned long addr, pmd_t pmd) { return pmd_leaf(pmd) && (pmd_val(pmd) & _PAGE_PRESENT) && (pmd_val(pmd) & _PAGE_USER); } -static inline bool pud_user_accessible_page(pud_t pud, unsigned long addr) +static inline bool pud_user_accessible_page(struct mm_struct *mm, unsigned long addr, pud_t pud) { return pud_leaf(pud) && (pud_val(pud) & _PAGE_PRESENT) && (pud_val(pud) & _PAGE_USER); } diff --git a/arch/x86/include/asm/reboot.h b/arch/x86/include/asm/reboot.h index ecd58ea9a837..a671a1145906 100644 --- a/arch/x86/include/asm/reboot.h +++ b/arch/x86/include/asm/reboot.h @@ -25,17 +25,6 @@ void __noreturn machine_real_restart(unsigned int type); #define MRR_BIOS 0 #define MRR_APM 1 -typedef void (cpu_emergency_virt_cb)(void); -#if IS_ENABLED(CONFIG_KVM_X86) -void cpu_emergency_register_virt_callback(cpu_emergency_virt_cb *callback); -void cpu_emergency_unregister_virt_callback(cpu_emergency_virt_cb *callback); -void cpu_emergency_disable_virtualization(void); -#else -static inline void cpu_emergency_register_virt_callback(cpu_emergency_virt_cb *callback) {} -static inline void cpu_emergency_unregister_virt_callback(cpu_emergency_virt_cb *callback) {} -static inline void cpu_emergency_disable_virtualization(void) {} -#endif /* CONFIG_KVM_X86 */ - typedef void (*nmi_shootdown_cb)(int, struct pt_regs*); void nmi_shootdown_cpus(nmi_shootdown_cb callback); void run_crash_ipi_callback(struct pt_regs *regs); diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h index edde36097ddc..bcfeb5e7c0ed 100644 --- a/arch/x86/include/asm/svm.h +++ b/arch/x86/include/asm/svm.h @@ -142,13 +142,13 @@ struct __attribute__ ((__packed__)) vmcb_control_area { u64 exit_info_2; u32 exit_int_info; u32 exit_int_info_err; - u64 nested_ctl; + u64 misc_ctl; u64 avic_vapic_bar; u64 ghcb_gpa; u32 event_inj; u32 event_inj_err; u64 nested_cr3; - u64 virt_ext; + u64 misc_ctl2; u32 clean; u32 reserved_5; u64 next_rip; @@ -182,6 +182,8 @@ struct __attribute__ ((__packed__)) vmcb_control_area { #define TLB_CONTROL_FLUSH_ASID 3 #define TLB_CONTROL_FLUSH_ASID_LOCAL 7 +#define TLB_CONTROL_MASK GENMASK(2, 0) + #define ERAP_CONTROL_ALLOW_LARGER_RAP BIT(0) #define ERAP_CONTROL_CLEAR_RAP BIT(1) @@ -222,8 +224,7 @@ struct __attribute__ ((__packed__)) vmcb_control_area { #define X2APIC_MODE_SHIFT 30 #define X2APIC_MODE_MASK (1 << X2APIC_MODE_SHIFT) -#define LBR_CTL_ENABLE_MASK BIT_ULL(0) -#define VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK BIT_ULL(1) +#define SVM_INT_VECTOR_MASK GENMASK(7, 0) #define SVM_INTERRUPT_SHADOW_MASK BIT_ULL(0) #define SVM_GUEST_INTERRUPT_MASK BIT_ULL(1) @@ -239,10 +240,12 @@ struct __attribute__ ((__packed__)) vmcb_control_area { #define SVM_IOIO_SIZE_MASK (7 << SVM_IOIO_SIZE_SHIFT) #define SVM_IOIO_ASIZE_MASK (7 << SVM_IOIO_ASIZE_SHIFT) -#define SVM_NESTED_CTL_NP_ENABLE BIT(0) -#define SVM_NESTED_CTL_SEV_ENABLE BIT(1) -#define SVM_NESTED_CTL_SEV_ES_ENABLE BIT(2) +#define SVM_MISC_ENABLE_NP BIT(0) +#define SVM_MISC_ENABLE_SEV BIT(1) +#define SVM_MISC_ENABLE_SEV_ES BIT(2) +#define SVM_MISC2_ENABLE_V_LBR BIT_ULL(0) +#define SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE BIT_ULL(1) #define SVM_TSC_RATIO_RSVD 0xffffff0000000000ULL #define SVM_TSC_RATIO_MIN 0x0000000000000001ULL @@ -636,6 +639,9 @@ static inline void __unused_size_checks(void) #define SVM_EVTINJ_VALID (1 << 31) #define SVM_EVTINJ_VALID_ERR (1 << 11) +#define SVM_EVTINJ_RESERVED_BITS ~(SVM_EVTINJ_VEC_MASK | SVM_EVTINJ_TYPE_MASK | \ + SVM_EVTINJ_VALID_ERR | SVM_EVTINJ_VALID) + #define SVM_EXITINTINFO_VEC_MASK SVM_EVTINJ_VEC_MASK #define SVM_EXITINTINFO_TYPE_MASK SVM_EVTINJ_TYPE_MASK diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 6b338d7f01b7..a149740b24e8 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -145,8 +145,6 @@ static __always_inline u64 sc_retry(sc_func_t func, u64 fn, #define seamcall(_fn, _args) sc_retry(__seamcall, (_fn), (_args)) #define seamcall_ret(_fn, _args) sc_retry(__seamcall_ret, (_fn), (_args)) #define seamcall_saved_ret(_fn, _args) sc_retry(__seamcall_saved_ret, (_fn), (_args)) -int tdx_cpu_enable(void); -int tdx_enable(void); const char *tdx_dump_mce_info(struct mce *m); const struct tdx_sys_info *tdx_get_sysinfo(void); @@ -223,8 +221,6 @@ u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td); u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page); #else static inline void tdx_init(void) { } -static inline int tdx_cpu_enable(void) { return -ENODEV; } -static inline int tdx_enable(void) { return -ENODEV; } static inline u32 tdx_get_nr_guest_keyids(void) { return 0; } static inline const char *tdx_dump_mce_info(struct mce *m) { return NULL; } static inline const struct tdx_sys_info *tdx_get_sysinfo(void) { return NULL; } diff --git a/arch/x86/include/asm/virt.h b/arch/x86/include/asm/virt.h new file mode 100644 index 000000000000..1558a0673d06 --- /dev/null +++ b/arch/x86/include/asm/virt.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_X86_VIRT_H +#define _ASM_X86_VIRT_H + +#include + +typedef void (cpu_emergency_virt_cb)(void); + +#if IS_ENABLED(CONFIG_KVM_X86) +extern bool virt_rebooting; + +void __init x86_virt_init(void); + +int x86_virt_get_ref(int feat); +void x86_virt_put_ref(int feat); + +int x86_virt_emergency_disable_virtualization_cpu(void); + +void x86_virt_register_emergency_callback(cpu_emergency_virt_cb *callback); +void x86_virt_unregister_emergency_callback(cpu_emergency_virt_cb *callback); +#else +static __always_inline void x86_virt_init(void) {} +static inline int x86_virt_emergency_disable_virtualization_cpu(void) { return -ENOENT; } +#endif + +#endif /* _ASM_X86_VIRT_H */ diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index b92ff87e3560..37080382df54 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -20,6 +20,17 @@ #include #include +struct vmcs_hdr { + u32 revision_id:31; + u32 shadow_vmcs:1; +}; + +struct vmcs { + struct vmcs_hdr hdr; + u32 abort; + char data[]; +}; + #define VMCS_CONTROL_BIT(x) BIT(VMX_FEATURE_##x & 0x1f) /* diff --git a/arch/x86/include/asm/xor_64.h b/arch/x86/include/asm/xor_64.h deleted file mode 100644 index 0307e4ec5044..000000000000 --- a/arch/x86/include/asm/xor_64.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _ASM_X86_XOR_64_H -#define _ASM_X86_XOR_64_H - -static struct xor_block_template xor_block_sse = { - .name = "generic_sse", - .do_2 = xor_sse_2, - .do_3 = xor_sse_3, - .do_4 = xor_sse_4, - .do_5 = xor_sse_5, -}; - - -/* Also try the AVX routines */ -#include - -/* We force the use of the SSE xor block because it can write around L2. - We may also be able to load into the L1 only depending on how the cpu - deals with a load to a line that is being prefetched. */ -#undef XOR_TRY_TEMPLATES -#define XOR_TRY_TEMPLATES \ -do { \ - AVX_XOR_SPEED; \ - xor_speed(&xor_block_sse_pf64); \ - xor_speed(&xor_block_sse); \ -} while (0) - -#endif /* _ASM_X86_XOR_64_H */ diff --git a/arch/x86/kernel/acpi/cppc.c b/arch/x86/kernel/acpi/cppc.c index d7c8ef1e354d..be4c5e9e5ff6 100644 --- a/arch/x86/kernel/acpi/cppc.c +++ b/arch/x86/kernel/acpi/cppc.c @@ -88,19 +88,19 @@ static void amd_set_max_freq_ratio(void) rc = cppc_get_perf_caps(0, &perf_caps); if (rc) { - pr_warn("Could not retrieve perf counters (%d)\n", rc); + pr_debug("Could not retrieve perf counters (%d)\n", rc); return; } rc = amd_get_boost_ratio_numerator(0, &numerator); if (rc) { - pr_warn("Could not retrieve highest performance (%d)\n", rc); + pr_debug("Could not retrieve highest performance (%d)\n", rc); return; } nominal_perf = perf_caps.nominal_perf; if (!nominal_perf) { - pr_warn("Could not retrieve nominal performance\n"); + pr_debug("Could not retrieve nominal performance\n"); return; } diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index e87da25d1236..62936a3bde19 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -2448,12 +2448,6 @@ void __init alternative_instructions(void) __smp_locks, __smp_locks_end, _text, _etext); } - - if (!uniproc_patched || num_possible_cpus() == 1) { - free_init_pages("SMP alternatives", - (unsigned long)__smp_locks, - (unsigned long)__smp_locks_end); - } #endif restart_nmi(); @@ -2462,6 +2456,24 @@ void __init alternative_instructions(void) alt_reloc_selftest(); } +#ifdef CONFIG_SMP +/* + * With CONFIG_DEFERRED_STRUCT_PAGE_INIT enabled we can free_init_pages() only + * after the deferred initialization of the memory map is complete. + */ +static int __init free_smp_locks(void) +{ + if (!uniproc_patched || num_possible_cpus() == 1) { + free_init_pages("SMP alternatives", + (unsigned long)__smp_locks, + (unsigned long)__smp_locks_end); + } + + return 0; +} +arch_initcall(free_smp_locks); +#endif + /** * text_poke_early - Update instructions on a live kernel at boot time * @addr: address to modify diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 33b740c26bef..2f8e8ff2d000 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -944,6 +944,9 @@ static void init_amd_zen1(struct cpuinfo_x86 *c) msr_clear_bit(MSR_K7_HWCR, MSR_K7_HWCR_IRPERF_EN_BIT); clear_cpu_cap(c, X86_FEATURE_IRPERF); } + + pr_notice_once("AMD Zen1 FPDSS bug detected, enabling mitigation.\n"); + msr_set_bit(MSR_AMD64_FP_CFG, MSR_AMD64_FP_CFG_ZEN1_DENORM_FIX_BIT); } static const struct x86_cpu_id amd_zenbleed_microcode[] = { @@ -986,6 +989,9 @@ static void init_amd_zen2(struct cpuinfo_x86 *c) /* Correct misconfigured CPUID on some clients. */ clear_cpu_cap(c, X86_FEATURE_INVLPGB); + + if (!cpu_has(c, X86_FEATURE_HYPERVISOR)) + msr_set_bit(MSR_ZEN4_BP_CFG, MSR_ZEN2_BP_CFG_BUG_FIX_BIT); } static void init_amd_zen3(struct cpuinfo_x86 *c) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 4e1f0c4afe3a..a4268c47f2bc 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -71,6 +71,7 @@ #include #include #include +#include #include #include @@ -2162,6 +2163,7 @@ static __init void identify_boot_cpu(void) cpu_detect_tlb(&boot_cpu_data); setup_cr_pinning(); + x86_virt_init(); tsx_init(); tdx_init(); lkgs_init(); diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index 8dd424ac5de8..f3a793e3a6c8 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -90,7 +90,6 @@ struct mca_config mca_cfg __read_mostly = { }; static DEFINE_PER_CPU(struct mce_hw_err, hw_errs_seen); -static unsigned long mce_need_notify; /* * MCA banks polled by the period polling timer for corrected events. @@ -152,8 +151,10 @@ EXPORT_PER_CPU_SYMBOL_GPL(injectm); void mce_log(struct mce_hw_err *err) { - if (mce_gen_pool_add(err)) + if (mce_gen_pool_add(err)) { + pr_info(HW_ERR "Machine check events logged\n"); irq_work_queue(&mce_irq_work); + } } EXPORT_SYMBOL_GPL(mce_log); @@ -585,28 +586,6 @@ bool mce_is_correctable(struct mce *m) } EXPORT_SYMBOL_GPL(mce_is_correctable); -/* - * Notify the user(s) about new machine check events. - * Can be called from interrupt context, but not from machine check/NMI - * context. - */ -static bool mce_notify_irq(void) -{ - /* Not more than two messages every minute */ - static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2); - - if (test_and_clear_bit(0, &mce_need_notify)) { - mce_work_trigger(); - - if (__ratelimit(&ratelimit)) - pr_info(HW_ERR "Machine check events logged\n"); - - return true; - } - - return false; -} - static int mce_early_notifier(struct notifier_block *nb, unsigned long val, void *data) { @@ -618,9 +597,7 @@ static int mce_early_notifier(struct notifier_block *nb, unsigned long val, /* Emit the trace record: */ trace_mce_record(err); - set_bit(0, &mce_need_notify); - - mce_notify_irq(); + mce_work_trigger(); return NOTIFY_DONE; } @@ -1804,7 +1781,7 @@ static void mce_timer_fn(struct timer_list *t) * Alert userspace if needed. If we logged an MCE, reduce the polling * interval, otherwise increase the polling interval. */ - if (mce_notify_irq()) + if (!mce_gen_pool_empty()) iv = max(iv / 2, (unsigned long) HZ/100); else iv = min(iv * 2, round_jiffies_relative(check_interval * HZ)); diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index a7dfc29d3470..b5b6a58b67b0 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -237,8 +237,12 @@ void hv_remove_crash_handler(void) #ifdef CONFIG_KEXEC_CORE static void hv_machine_shutdown(void) { - if (kexec_in_progress && hv_kexec_handler) - hv_kexec_handler(); + if (kexec_in_progress) { + hv_stimer_global_cleanup(); + + if (hv_kexec_handler) + hv_kexec_handler(); + } /* * Call hv_cpu_die() on all the CPUs, otherwise later the hypervisor @@ -427,6 +431,10 @@ static void __init hv_smp_prepare_cpus(unsigned int max_cpus) } #ifdef CONFIG_X86_64 + /* If AP LPs exist, we are in a kexec'd kernel and VPs already exist */ + if (num_present_cpus() == 1 || hv_lp_exists(1)) + return; + for_each_present_cpu(i) { if (i == 0) continue; @@ -434,6 +442,9 @@ static void __init hv_smp_prepare_cpus(unsigned int max_cpus) BUG_ON(ret); } + ret = hv_call_notify_all_processors_started(); + WARN_ON(ret); + for_each_present_cpu(i) { if (i == 0) continue; diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c index 335fd2ee9766..cd796818d94d 100644 --- a/arch/x86/kernel/crash.c +++ b/arch/x86/kernel/crash.c @@ -42,6 +42,7 @@ #include #include #include +#include /* Used while preparing memory map entries for second kernel */ struct crash_memmap_data { @@ -111,7 +112,7 @@ void native_machine_crash_shutdown(struct pt_regs *regs) crash_smp_send_stop(); - cpu_emergency_disable_virtualization(); + x86_virt_emergency_disable_virtualization_cpu(); /* * Disable Intel PT to stop its logging diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 2a9992758933..eb72537bc0b1 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -450,6 +450,10 @@ __init static int append_e820_table(struct boot_e820_entry *entries, u32 nr_entr { struct boot_e820_entry *entry = entries; + /* If there aren't any entries, we'll want to fall back to another source: */ + if (!nr_entries) + return -ENOENT; + while (nr_entries) { u64 start = entry->addr; u64 size = entry->size; @@ -458,7 +462,7 @@ __init static int append_e820_table(struct boot_e820_entry *entries, u32 nr_entr /* Ignore the remaining entries on 64-bit overflow: */ if (start > end && likely(size)) - return -1; + return -EINVAL; e820__range_add(start, size, type); diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c index 5630c7dca1f3..7e980ea49d8d 100644 --- a/arch/x86/kernel/kexec-bzimage64.c +++ b/arch/x86/kernel/kexec-bzimage64.c @@ -525,12 +525,8 @@ static void *bzImage64_load(struct kimage *image, char *kernel, if (ret) return ERR_PTR(ret); ret = crash_load_dm_crypt_keys(image); - if (ret == -ENOENT) { - kexec_dprintk("No dm crypt key to load\n"); - } else if (ret) { - pr_err("Failed to load dm crypt keys\n"); + if (ret) return ERR_PTR(ret); - } if (image->dm_crypt_keys_addr && cmdline_len + MAX_ELFCOREHDR_STR_LEN + MAX_DMCRYPTKEYS_STR_LEN > header->cmdline_size) { diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c index 8b1a9733d13e..96af1242454e 100644 --- a/arch/x86/kernel/kgdb.c +++ b/arch/x86/kernel/kgdb.c @@ -407,10 +407,11 @@ static void kgdb_disable_hw_debug(struct pt_regs *regs) * kgdb_roundup_cpus - Get other CPUs into a holding pattern * * On SMP systems, we need to get the attention of the other CPUs - * and get them be in a known state. This should do what is needed - * to get the other CPUs to call kgdb_wait(). Note that on some arches, - * the NMI approach is not used for rounding up all the CPUs. For example, - * in case of MIPS, smp_call_function() is used to roundup CPUs. + * and get them into a known state. This should do what is needed + * to get the other CPUs to call kgdb_handle_exception(). Note that + * on some arches, the NMI approach is not used for rounding up all + * the CPUs. For example, in case of MIPS, smp_call_function() is + * used to roundup CPUs. * * On non-SMP systems, this is not called. */ diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index ddff25a1efbe..0fed6d0d7e32 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -532,51 +533,6 @@ static inline void kb_wait(void) static inline void nmi_shootdown_cpus_on_restart(void); #if IS_ENABLED(CONFIG_KVM_X86) -/* RCU-protected callback to disable virtualization prior to reboot. */ -static cpu_emergency_virt_cb __rcu *cpu_emergency_virt_callback; - -void cpu_emergency_register_virt_callback(cpu_emergency_virt_cb *callback) -{ - if (WARN_ON_ONCE(rcu_access_pointer(cpu_emergency_virt_callback))) - return; - - rcu_assign_pointer(cpu_emergency_virt_callback, callback); -} -EXPORT_SYMBOL_FOR_KVM(cpu_emergency_register_virt_callback); - -void cpu_emergency_unregister_virt_callback(cpu_emergency_virt_cb *callback) -{ - if (WARN_ON_ONCE(rcu_access_pointer(cpu_emergency_virt_callback) != callback)) - return; - - rcu_assign_pointer(cpu_emergency_virt_callback, NULL); - synchronize_rcu(); -} -EXPORT_SYMBOL_FOR_KVM(cpu_emergency_unregister_virt_callback); - -/* - * Disable virtualization, i.e. VMX or SVM, to ensure INIT is recognized during - * reboot. VMX blocks INIT if the CPU is post-VMXON, and SVM blocks INIT if - * GIF=0, i.e. if the crash occurred between CLGI and STGI. - */ -void cpu_emergency_disable_virtualization(void) -{ - cpu_emergency_virt_cb *callback; - - /* - * IRQs must be disabled as KVM enables virtualization in hardware via - * function call IPIs, i.e. IRQs need to be disabled to guarantee - * virtualization stays disabled. - */ - lockdep_assert_irqs_disabled(); - - rcu_read_lock(); - callback = rcu_dereference(cpu_emergency_virt_callback); - if (callback) - callback(); - rcu_read_unlock(); -} - static void emergency_reboot_disable_virtualization(void) { local_irq_disable(); @@ -588,16 +544,11 @@ static void emergency_reboot_disable_virtualization(void) * We can't take any locks and we may be on an inconsistent state, so * use NMIs as IPIs to tell the other CPUs to disable VMX/SVM and halt. * - * Do the NMI shootdown even if virtualization is off on _this_ CPU, as - * other CPUs may have virtualization enabled. + * Safely force _this_ CPU out of VMX/SVM operation, and if necessary, + * blast NMIs to force other CPUs out of VMX/SVM as well.k */ - if (rcu_access_pointer(cpu_emergency_virt_callback)) { - /* Safely force _this_ CPU out of VMX/SVM operation. */ - cpu_emergency_disable_virtualization(); - - /* Disable VMX/SVM and halt on other CPUs. */ + if (!x86_virt_emergency_disable_virtualization_cpu()) nmi_shootdown_cpus_on_restart(); - } } #else static void emergency_reboot_disable_virtualization(void) { } @@ -878,10 +829,10 @@ static int crash_nmi_callback(unsigned int val, struct pt_regs *regs) shootdown_callback(cpu, regs); /* - * Prepare the CPU for reboot _after_ invoking the callback so that the - * callback can safely use virtualization instructions, e.g. VMCLEAR. + * Disable virtualization, as both VMX and SVM can block INIT and thus + * prevent AP bringup, e.g. in a kdump kernel or in firmware. */ - cpu_emergency_disable_virtualization(); + x86_virt_emergency_disable_virtualization_cpu(); atomic_dec(&waiting_for_crash_ipi); diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S index 4ffba68dc57b..eaeb77464c06 100644 --- a/arch/x86/kernel/relocate_kernel_64.S +++ b/arch/x86/kernel/relocate_kernel_64.S @@ -136,6 +136,14 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped) * %r13 original CR4 when relocate_kernel() was invoked */ + /* + * Set return address to 0 if not preserving context. The purgatory + * shipped in kexec-tools will unconditionally look for the return + * address on the stack and set a kexec_jump_back_entry= command + * line option if it's non-zero. There's no other way that it can + * tell a preserve-context (kjump) kexec from a normal one. + */ + pushq $0 /* store the start address on the stack */ pushq %rdx diff --git a/arch/x86/kernel/resource.c b/arch/x86/kernel/resource.c index 79bc8a97a083..44d4b2c538b0 100644 --- a/arch/x86/kernel/resource.c +++ b/arch/x86/kernel/resource.c @@ -62,7 +62,7 @@ void arch_remove_reservations(struct resource *avail) /* * Trim out BIOS area (high 2MB) and E820 regions. We do not remove * the low 1MB unconditionally, as this area is needed for some ISA - * cards requiring a memory range, e.g. the i82365 PCMCIA controller. + * cards requiring a memory range. */ if (avail->flags & IORESOURCE_MEM) { resource_clip(avail, BIOS_ROM_BASE, BIOS_ROM_END); diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index eebcc9db1a1b..46882ce79c3a 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -426,7 +426,7 @@ int __init ima_free_kexec_buffer(void) if (!ima_kexec_buffer_size) return -ENOENT; - memblock_free_late(ima_kexec_buffer_phys, + memblock_phys_free(ima_kexec_buffer_phys, ima_kexec_buffer_size); ima_kexec_buffer_phys = 0; diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c index 0962ae4c3017..0ca64900192f 100644 --- a/arch/x86/kernel/shstk.c +++ b/arch/x86/kernel/shstk.c @@ -326,10 +326,8 @@ static int shstk_push_sigframe(unsigned long *ssp) static int shstk_pop_sigframe(unsigned long *ssp) { - struct vm_area_struct *vma; unsigned long token_addr; - bool need_to_check_vma; - int err = 1; + unsigned int seq; /* * It is possible for the SSP to be off the end of a shadow stack by 4 @@ -340,25 +338,35 @@ static int shstk_pop_sigframe(unsigned long *ssp) if (!IS_ALIGNED(*ssp, 8)) return -EINVAL; - need_to_check_vma = PAGE_ALIGN(*ssp) == *ssp; + do { + struct vm_area_struct *vma; + bool valid_vma; + int err; - if (need_to_check_vma) if (mmap_read_lock_killable(current->mm)) return -EINTR; - err = get_shstk_data(&token_addr, (unsigned long __user *)*ssp); - if (unlikely(err)) - goto out_err; - - if (need_to_check_vma) { vma = find_vma(current->mm, *ssp); - if (!vma || !(vma->vm_flags & VM_SHADOW_STACK)) { - err = -EFAULT; - goto out_err; - } + valid_vma = vma && (vma->vm_flags & VM_SHADOW_STACK); + /* + * VMAs can change between get_shstk_data() and find_vma(). + * Watch for changes and ensure that 'token_addr' comes from + * 'vma' by recording a seqcount. + * + * Ignore the return value of mmap_lock_speculate_try_begin() + * because the mmap lock excludes the possibility of writers. + */ + mmap_lock_speculate_try_begin(current->mm, &seq); mmap_read_unlock(current->mm); - } + + if (!valid_vma) + return -EINVAL; + + err = get_shstk_data(&token_addr, (unsigned long __user *)*ssp); + if (err) + return err; + } while (mmap_lock_speculate_retry(current->mm, seq)); /* Restore SSP aligned? */ if (unlikely(!IS_ALIGNED(token_addr, 8))) @@ -371,10 +379,6 @@ static int shstk_pop_sigframe(unsigned long *ssp) *ssp = token_addr; return 0; -out_err: - if (need_to_check_vma) - mmap_read_unlock(current->mm); - return err; } int setup_signal_shadow_stack(struct ksignal *ksig) diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index b014e6d229f9..cbf95fe2b207 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -35,6 +35,7 @@ #include #include #include +#include /* * Some notes on x86 processor bugs affecting SMP operation: @@ -124,7 +125,7 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs) if (raw_smp_processor_id() == atomic_read(&stopping_cpu)) return NMI_HANDLED; - cpu_emergency_disable_virtualization(); + x86_virt_emergency_disable_virtualization_cpu(); stop_this_cpu(NULL); return NMI_HANDLED; @@ -136,7 +137,7 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs) DEFINE_IDTENTRY_SYSVEC(sysvec_reboot) { apic_eoi(); - cpu_emergency_disable_virtualization(); + x86_virt_emergency_disable_virtualization_cpu(); stop_this_cpu(NULL); } diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 8137927e7387..e69156b54cff 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -1246,11 +1246,12 @@ void kvm_initialize_cpu_caps(void) F(NULL_SEL_CLR_BASE), /* UpperAddressIgnore */ F(AUTOIBRS), - F(PREFETCHI), EMULATED_F(NO_SMM_CTL_MSR), /* PrefetchCtlMsr */ /* GpOnUserCpuid */ /* EPSF */ + F(PREFETCHI), + F(AVX512_BMM), F(ERAPS), SYNTHESIZED_F(SBPB), SYNTHESIZED_F(IBPB_BRTYPE), @@ -2160,7 +2161,8 @@ int kvm_emulate_cpuid(struct kvm_vcpu *vcpu) { u32 eax, ebx, ecx, edx; - if (cpuid_fault_enabled(vcpu) && !kvm_require_cpl(vcpu, 0)) + if (!is_smm(vcpu) && cpuid_fault_enabled(vcpu) && + !kvm_require_cpl(vcpu, 0)) return 1; eax = kvm_rax_read(vcpu); diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index c8e292e9a24d..8013dccb3110 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -1297,12 +1297,25 @@ static int read_emulated(struct x86_emulate_ctxt *ctxt, int rc; struct read_cache *mc = &ctxt->mem_read; + /* + * If the read gets a cache hit, simply copy the value from the cache. + * A "hit" here means that there is unused data in the cache, i.e. when + * re-emulating an instruction to complete a userspace exit, KVM relies + * on "no decode" to ensure the instruction is re-emulated in the same + * sequence, so that multiple reads are fulfilled in the correct order. + */ if (mc->pos < mc->end) goto read_cached; if (KVM_EMULATOR_BUG_ON((mc->end + size) >= sizeof(mc->data), ctxt)) return X86EMUL_UNHANDLEABLE; + /* + * Route all reads to the cache. This allows @dest to be an on-stack + * variable without triggering use-after-free if KVM needs to exit to + * userspace to handle an MMIO read (the MMIO fragment will point at + * the current location in the cache). + */ rc = ctxt->ops->read_emulated(ctxt, addr, mc->data + mc->end, size, &ctxt->exception); if (rc != X86EMUL_CONTINUE) @@ -3583,10 +3596,10 @@ static int em_cpuid(struct x86_emulate_ctxt *ctxt) u64 msr = 0; ctxt->ops->get_msr(ctxt, MSR_MISC_FEATURES_ENABLES, &msr); - if (msr & MSR_MISC_FEATURES_ENABLES_CPUID_FAULT && - ctxt->ops->cpl(ctxt)) { + if (!ctxt->ops->is_smm(ctxt) && + (msr & MSR_MISC_FEATURES_ENABLES_CPUID_FAULT) && + ctxt->ops->cpl(ctxt)) return emulate_gp(ctxt, 0); - } eax = reg_read(ctxt, VCPU_REGS_RAX); ecx = reg_read(ctxt, VCPU_REGS_RCX); @@ -3708,7 +3721,7 @@ static inline size_t fxstate_size(struct x86_emulate_ctxt *ctxt) */ static int em_fxsave(struct x86_emulate_ctxt *ctxt) { - struct fxregs_state fx_state; + struct fxregs_state fx_state = {}; int rc; rc = check_fxsr(ctxt); @@ -3738,7 +3751,7 @@ static int em_fxsave(struct x86_emulate_ctxt *ctxt) static noinline int fxregs_fixup(struct fxregs_state *fx_state, const size_t used_size) { - struct fxregs_state fx_tmp; + struct fxregs_state fx_tmp = {}; int rc; rc = asm_safe("fxsave %[fx]", , [fx] "+m"(fx_tmp)); @@ -3874,8 +3887,7 @@ static int check_svme_pa(struct x86_emulate_ctxt *ctxt) { u64 rax = reg_read(ctxt, VCPU_REGS_RAX); - /* Valid physical address? */ - if (rax & 0xffff000000000000ULL) + if (!ctxt->ops->page_address_valid(ctxt, rax)) return emulate_gp(ctxt, 0); return check_svme(ctxt); @@ -4469,7 +4481,7 @@ static const struct opcode opcode_map_0f_38[256] = { X16(N), X16(N), /* 0x20 - 0x2f */ X8(N), - X2(N), GP(SrcReg | DstMem | ModRM | Mov | Aligned, &pfx_0f_e7_0f_38_2a), N, N, N, N, N, + X2(N), GP(SrcMem | DstReg | ModRM | Mov | Aligned, &pfx_0f_e7_0f_38_2a), N, N, N, N, N, /* 0x30 - 0x7f */ X16(N), X16(N), X16(N), X16(N), X16(N), /* 0x80 - 0xef */ diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index 9b140bbdc1d8..4438ecac9a89 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -2040,7 +2040,7 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc) * flush). Translate the address here so the memory can be uniformly * read with kvm_read_guest(). */ - if (!hc->fast && is_guest_mode(vcpu)) { + if (!hc->fast && mmu_is_nested(vcpu)) { hc->ingpa = translate_nested_gpa(vcpu, hc->ingpa, 0, NULL); if (unlikely(hc->ingpa == INVALID_GPA)) return HV_STATUS_INVALID_HYPERCALL_INPUT; diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h index 6ce160ffa678..6301f79fcbae 100644 --- a/arch/x86/kvm/hyperv.h +++ b/arch/x86/kvm/hyperv.h @@ -305,14 +305,6 @@ static inline bool kvm_hv_has_stimer_pending(struct kvm_vcpu *vcpu) { return false; } -static inline bool kvm_hv_is_tlb_flush_hcall(struct kvm_vcpu *vcpu) -{ - return false; -} -static inline bool guest_hv_cpuid_has_l2_tlb_flush(struct kvm_vcpu *vcpu) -{ - return false; -} static inline int kvm_hv_verify_vp_assist(struct kvm_vcpu *vcpu) { return 0; diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h index fb3dab4b5a53..0abff36d0994 100644 --- a/arch/x86/kvm/kvm_emulate.h +++ b/arch/x86/kvm/kvm_emulate.h @@ -245,6 +245,8 @@ struct x86_emulate_ops { bool (*is_canonical_addr)(struct x86_emulate_ctxt *ctxt, gva_t addr, unsigned int flags); + + bool (*page_address_valid)(struct x86_emulate_ctxt *ctxt, gpa_t gpa); }; /* Type, address-of, and value of an instruction's operand. */ diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 9381c58d4c85..4078e624ca66 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -667,13 +667,15 @@ bool __kvm_apic_update_irr(unsigned long *pir, void *regs, int *max_irr) u32 *__pir = (void *)pir_vals; u32 i, vec; u32 irr_val, prev_irr_val; - int max_updated_irr; + int max_new_irr; - max_updated_irr = -1; - *max_irr = -1; - - if (!pi_harvest_pir(pir, pir_vals)) + if (!pi_harvest_pir(pir, pir_vals)) { + *max_irr = apic_find_highest_vector(regs + APIC_IRR); return false; + } + + max_new_irr = -1; + *max_irr = -1; for (i = vec = 0; i <= 7; i++, vec += 32) { u32 *p_irr = (u32 *)(regs + APIC_IRR + i * 0x10); @@ -688,25 +690,25 @@ bool __kvm_apic_update_irr(unsigned long *pir, void *regs, int *max_irr) !try_cmpxchg(p_irr, &prev_irr_val, irr_val)); if (prev_irr_val != irr_val) - max_updated_irr = __fls(irr_val ^ prev_irr_val) + vec; + max_new_irr = __fls(irr_val ^ prev_irr_val) + vec; } if (irr_val) *max_irr = __fls(irr_val) + vec; } - return ((max_updated_irr != -1) && - (max_updated_irr == *max_irr)); + return max_new_irr != -1 && max_new_irr == *max_irr; } EXPORT_SYMBOL_FOR_KVM_INTERNAL(__kvm_apic_update_irr); bool kvm_apic_update_irr(struct kvm_vcpu *vcpu, unsigned long *pir, int *max_irr) { struct kvm_lapic *apic = vcpu->arch.apic; - bool irr_updated = __kvm_apic_update_irr(pir, apic->regs, max_irr); + bool max_irr_is_from_pir; - if (unlikely(!apic->apicv_active && irr_updated)) + max_irr_is_from_pir = __kvm_apic_update_irr(pir, apic->regs, max_irr); + if (unlikely(!apic->apicv_active && max_irr_is_from_pir)) apic->irr_pending = true; - return irr_updated; + return max_irr_is_from_pir; } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_apic_update_irr); @@ -840,16 +842,16 @@ static int __pv_send_ipi(unsigned long *ipi_bitmap, struct kvm_apic_map *map, { int i, count = 0; struct kvm_vcpu *vcpu; + size_t map_index; if (min > map->max_apic_id) return 0; - min = array_index_nospec(min, map->max_apic_id + 1); - for_each_set_bit(i, ipi_bitmap, - min((u32)BITS_PER_LONG, (map->max_apic_id - min + 1))) { - if (map->phys_map[min + i]) { - vcpu = map->phys_map[min + i]->vcpu; + min((u32)BITS_PER_LONG, (map->max_apic_id - min + 1))) { + map_index = array_index_nospec(min + i, map->max_apic_id + 1); + if (map->phys_map[map_index]) { + vcpu = map->phys_map[map_index]->vcpu; count += kvm_apic_set_irq(vcpu, irq, NULL); } } @@ -2657,6 +2659,9 @@ void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset) { struct kvm_lapic *apic = vcpu->arch.apic; + if (KVM_BUG_ON(!lapic_in_kernel(vcpu), vcpu->kvm)) + return; + /* * ICR is a single 64-bit register when x2APIC is enabled, all others * registers hold 32-bit values. For legacy xAPIC, ICR writes need to diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index dd06453d5b72..f0144ae8d891 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -182,6 +182,8 @@ static struct kmem_cache *pte_list_desc_cache; struct kmem_cache *mmu_page_header_cache; static void mmu_spte_set(u64 *sptep, u64 spte); +static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp, + u64 *spte, struct list_head *invalid_list); struct kvm_mmu_role_regs { const unsigned long cr0; @@ -1287,19 +1289,6 @@ static void drop_spte(struct kvm *kvm, u64 *sptep) rmap_remove(kvm, sptep); } -static void drop_large_spte(struct kvm *kvm, u64 *sptep, bool flush) -{ - struct kvm_mmu_page *sp; - - sp = sptep_to_sp(sptep); - WARN_ON_ONCE(sp->role.level == PG_LEVEL_4K); - - drop_spte(kvm, sptep); - - if (flush) - kvm_flush_remote_tlbs_sptep(kvm, sptep); -} - /* * Write-protect on the specified @sptep, @pt_protect indicates whether * spte write-protection is caused by protecting shadow page table. @@ -2466,7 +2455,8 @@ static struct kvm_mmu_page *kvm_mmu_get_child_sp(struct kvm_vcpu *vcpu, { union kvm_mmu_page_role role; - if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep)) + if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep) && + spte_to_child_sp(*sptep) && spte_to_child_sp(*sptep)->gfn == gfn) return ERR_PTR(-EEXIST); role = kvm_mmu_child_role(sptep, direct, access); @@ -2536,6 +2526,23 @@ static void shadow_walk_next(struct kvm_shadow_walk_iterator *iterator) __shadow_walk_next(iterator, *iterator->sptep); } +/* + * Note: while normally KVM uses a "bool flush" return value to let + * the caller batch flushes, __link_shadow_page() flushes immediately + * before populating the parent PTE with the new shadow page. The + * typical callers, direct_map() and FNAME(fetch)(), are not going + * to zap more than one huge SPTE anyway. + * + * The only exception, where @flush can be false, is when a huge SPTE + * is replaced with a shadow page SPTE with a fully populated page table, + * which can happen from shadow_mmu_split_huge_page(). In this case, + * no memory is unmapped across the change to the page tables and no + * immediate flush is needed for correctness. + * + * Even in that case, calls to kvm_mmu_commit_zap_page() are not + * batched. Doing so would require adding an invalid_list argument + * all the way down to __walk_slot_rmaps(). + */ static void __link_shadow_page(struct kvm *kvm, struct kvm_mmu_memory_cache *cache, u64 *sptep, struct kvm_mmu_page *sp, bool flush) @@ -2544,13 +2551,18 @@ static void __link_shadow_page(struct kvm *kvm, BUILD_BUG_ON(VMX_EPT_WRITABLE_MASK != PT_WRITABLE_MASK); - /* - * If an SPTE is present already, it must be a leaf and therefore - * a large one. Drop it, and flush the TLB if needed, before - * installing sp. - */ - if (is_shadow_present_pte(*sptep)) - drop_large_spte(kvm, sptep, flush); + if (is_shadow_present_pte(*sptep)) { + struct kvm_mmu_page *parent_sp; + LIST_HEAD(invalid_list); + + parent_sp = sptep_to_sp(sptep); + WARN_ON_ONCE(parent_sp->role.level == PG_LEVEL_4K); + + if (mmu_page_zap_pte(kvm, parent_sp, sptep, &invalid_list)) + kvm_mmu_commit_zap_page(kvm, &invalid_list); + else if (flush) + kvm_flush_remote_tlbs_sptep(kvm, sptep); + } spte = make_nonleaf_spte(sp->spt, sp_ad_disabled(sp)); @@ -2940,6 +2952,15 @@ int mmu_try_to_unsync_pages(struct kvm *kvm, const struct kvm_memory_slot *slot, if (kvm_gfn_is_write_tracked(kvm, slot, gfn)) return -EPERM; + /* + * Only 4KiB mappings can become unsync, and KVM disallows hugepages + * when accounting 4KiB shadow pages. Upper-level gPTEs are always + * write-protected (see above), thus if the gfn can be mapped with a + * hugepage and isn't write-tracked, it can't have a shadow page. + */ + if (!lpage_info_slot(gfn, slot, PG_LEVEL_2M)->disallow_lpage) + return 0; + /* * The page is not write-tracked, mark existing shadow pages unsync * unless KVM is synchronizing an unsync SP. In that case, KVM must @@ -7490,9 +7511,14 @@ static void kvm_wake_nx_recovery_thread(struct kvm *kvm) static int get_nx_huge_pages(char *buffer, const struct kernel_param *kp) { + int val = *(int *)kp->arg; + if (nx_hugepage_mitigation_hard_disabled) return sysfs_emit(buffer, "never\n"); + if (val == -1) + return sysfs_emit(buffer, "auto\n"); + return param_get_bool(buffer, kp); } diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 9c26038f6b77..7b1102d26f9c 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -1507,7 +1507,7 @@ static struct kvm_mmu_page *tdp_mmu_alloc_sp_for_split(void) if (!sp) return NULL; - sp->spt = (void *)get_zeroed_page(GFP_KERNEL_ACCOUNT); + sp->spt = (void *)__get_free_page(GFP_KERNEL_ACCOUNT); if (!sp->spt) { kmem_cache_free(mmu_page_header_cache, sp); return NULL; diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index bd6b785cf261..e218352e3423 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -1256,7 +1256,7 @@ int kvm_vm_ioctl_set_pmu_event_filter(struct kvm *kvm, void __user *argp) r = -EFAULT; if (copy_from_user(filter->events, user_filter->events, - sizeof(filter->events[0]) * filter->nevents)) + flex_array_size(filter, events, filter->nevents))) goto cleanup; r = prepare_filter_lists(filter); diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index f7ec7914e3c4..adf211860949 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -76,23 +77,33 @@ static int avic_param_set(const char *val, const struct kernel_param *kp) return param_set_bint(val, kp); } +static int avic_param_get(char *buffer, const struct kernel_param *kp) +{ + int val = *(int *)kp->arg; + + if (val == AVIC_AUTO_MODE) + return sysfs_emit(buffer, "N\n"); + + return param_get_bool(buffer, kp); +} + static const struct kernel_param_ops avic_ops = { .flags = KERNEL_PARAM_OPS_FL_NOARG, .set = avic_param_set, - .get = param_get_bool, + .get = avic_param_get, }; /* * Enable / disable AVIC. In "auto" mode (default behavior), AVIC is enabled * for Zen4+ CPUs with x2AVIC (and all other criteria for enablement are met). */ -static int avic = AVIC_AUTO_MODE; +static int __ro_after_init avic = AVIC_AUTO_MODE; module_param_cb(avic, &avic_ops, &avic, 0444); __MODULE_PARM_TYPE(avic, "bool"); module_param(enable_ipiv, bool, 0444); -static bool force_avic; +static bool __ro_after_init force_avic; module_param_unsafe(force_avic, bool, 0444); /* Note: @@ -226,7 +237,7 @@ static void avic_deactivate_vmcb(struct vcpu_svm *svm) vmcb->control.int_ctl &= ~(AVIC_ENABLE_MASK | X2APIC_MODE_MASK); vmcb->control.avic_physical_id &= ~AVIC_PHYSICAL_MAX_INDEX_MASK; - if (!sev_es_guest(svm->vcpu.kvm)) + if (!is_sev_es_guest(&svm->vcpu)) svm_set_intercept(svm, INTERCEPT_CR8_WRITE); /* diff --git a/arch/x86/kvm/svm/hyperv.h b/arch/x86/kvm/svm/hyperv.h index d3f8bfc05832..f70d076911a6 100644 --- a/arch/x86/kvm/svm/hyperv.h +++ b/arch/x86/kvm/svm/hyperv.h @@ -41,10 +41,17 @@ static inline bool nested_svm_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu) return hv_vcpu->vp_assist_page.nested_control.features.directhypercall; } +static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu) +{ + return guest_hv_cpuid_has_l2_tlb_flush(vcpu) && + nested_svm_l2_tlb_flush_enabled(vcpu) && + kvm_hv_is_tlb_flush_hcall(vcpu); +} + void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu); #else /* CONFIG_KVM_HYPERV */ static inline void nested_svm_hv_update_vm_vp_ids(struct kvm_vcpu *vcpu) {} -static inline bool nested_svm_l2_tlb_flush_enabled(struct kvm_vcpu *vcpu) +static inline bool nested_svm_is_l2_tlb_flush_hcall(struct kvm_vcpu *vcpu) { return false; } diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index b36c33255bed..b340dc9991ad 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -116,31 +116,28 @@ static bool nested_vmcb_needs_vls_intercept(struct vcpu_svm *svm) if (!nested_npt_enabled(svm)) return true; - if (!(svm->nested.ctl.virt_ext & VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK)) + if (!(svm->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE)) return true; return false; } -void recalc_intercepts(struct vcpu_svm *svm) +void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm) { - struct vmcb_control_area *c, *h; - struct vmcb_ctrl_area_cached *g; + struct vmcb_ctrl_area_cached *vmcb12_ctrl = &svm->nested.ctl; + struct vmcb *vmcb02 = svm->nested.vmcb02.ptr; + struct vmcb *vmcb01 = svm->vmcb01.ptr; unsigned int i; - vmcb_mark_dirty(svm->vmcb, VMCB_INTERCEPTS); - - if (!is_guest_mode(&svm->vcpu)) + if (WARN_ON_ONCE(svm->vmcb != vmcb02)) return; - c = &svm->vmcb->control; - h = &svm->vmcb01.ptr->control; - g = &svm->nested.ctl; + vmcb_mark_dirty(vmcb02, VMCB_INTERCEPTS); for (i = 0; i < MAX_INTERCEPT; i++) - c->intercepts[i] = h->intercepts[i]; + vmcb02->control.intercepts[i] = vmcb01->control.intercepts[i]; - if (g->int_ctl & V_INTR_MASKING_MASK) { + if (vmcb12_ctrl->int_ctl & V_INTR_MASKING_MASK) { /* * If L2 is active and V_INTR_MASKING is enabled in vmcb12, * disable intercept of CR8 writes as L2's CR8 does not affect @@ -151,24 +148,27 @@ void recalc_intercepts(struct vcpu_svm *svm) * the effective RFLAGS.IF for L1 interrupts will never be set * while L2 is running (L2's RFLAGS.IF doesn't affect L1 IRQs). */ - vmcb_clr_intercept(c, INTERCEPT_CR8_WRITE); - if (!(svm->vmcb01.ptr->save.rflags & X86_EFLAGS_IF)) - vmcb_clr_intercept(c, INTERCEPT_VINTR); + vmcb_clr_intercept(&vmcb02->control, INTERCEPT_CR8_WRITE); + if (!(vmcb01->save.rflags & X86_EFLAGS_IF)) + vmcb_clr_intercept(&vmcb02->control, INTERCEPT_VINTR); } - /* - * We want to see VMMCALLs from a nested guest only when Hyper-V L2 TLB - * flush feature is enabled. - */ - if (!nested_svm_l2_tlb_flush_enabled(&svm->vcpu)) - vmcb_clr_intercept(c, INTERCEPT_VMMCALL); - for (i = 0; i < MAX_INTERCEPT; i++) - c->intercepts[i] |= g->intercepts[i]; + vmcb02->control.intercepts[i] |= vmcb12_ctrl->intercepts[i]; /* If SMI is not intercepted, ignore guest SMI intercept as well */ if (!intercept_smi) - vmcb_clr_intercept(c, INTERCEPT_SMI); + vmcb_clr_intercept(&vmcb02->control, INTERCEPT_SMI); + + /* + * Intercept PAUSE if and only if L1 wants to. KVM intercepts PAUSE so + * that a vCPU that may be spinning waiting for a lock can be scheduled + * out in favor of the vCPU that holds said lock. KVM doesn't support + * yielding across L2 vCPUs, as KVM has limited visilibity into which + * L2 vCPUs are in the same L2 VM, i.e. may be contending for locks. + */ + if (!vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_PAUSE)) + vmcb_clr_intercept(&vmcb02->control, INTERCEPT_PAUSE); if (nested_vmcb_needs_vls_intercept(svm)) { /* @@ -176,10 +176,10 @@ void recalc_intercepts(struct vcpu_svm *svm) * we must intercept these instructions to correctly * emulate them in case L1 doesn't intercept them. */ - vmcb_set_intercept(c, INTERCEPT_VMLOAD); - vmcb_set_intercept(c, INTERCEPT_VMSAVE); + vmcb_set_intercept(&vmcb02->control, INTERCEPT_VMLOAD); + vmcb_set_intercept(&vmcb02->control, INTERCEPT_VMSAVE); } else { - WARN_ON(!(c->virt_ext & VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK)); + WARN_ON_ONCE(!(vmcb02->control.misc_ctl2 & SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE)); } } @@ -339,8 +339,56 @@ static bool nested_svm_check_bitmap_pa(struct kvm_vcpu *vcpu, u64 pa, u32 size) kvm_vcpu_is_legal_gpa(vcpu, addr + size - 1); } -static bool __nested_vmcb_check_controls(struct kvm_vcpu *vcpu, - struct vmcb_ctrl_area_cached *control) +static bool nested_svm_event_inj_valid_exept(struct kvm_vcpu *vcpu, u8 vector) +{ + /* + * Vectors that do not correspond to a defined exception are invalid + * (including #NMI and reserved vectors). In a best effort to define + * valid exceptions based on the virtual CPU, make all exceptions always + * valid except those obviously tied to a CPU feature. + */ + switch (vector) { + case DE_VECTOR: case DB_VECTOR: case BP_VECTOR: case OF_VECTOR: + case BR_VECTOR: case UD_VECTOR: case NM_VECTOR: case DF_VECTOR: + case TS_VECTOR: case NP_VECTOR: case SS_VECTOR: case GP_VECTOR: + case PF_VECTOR: case MF_VECTOR: case AC_VECTOR: case MC_VECTOR: + case XM_VECTOR: case HV_VECTOR: case SX_VECTOR: + return true; + case CP_VECTOR: + return guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK); + case VC_VECTOR: + return guest_cpu_cap_has(vcpu, X86_FEATURE_SEV_ES); + } + return false; +} + +/* + * According to the APM, VMRUN exits with SVM_EXIT_ERR if SVM_EVTINJ_VALID is + * set and: + * - The type of event_inj is not one of the defined values. + * - The type is SVM_EVTINJ_TYPE_EXEPT, but the vector is not a valid exception. + */ +static bool nested_svm_check_event_inj(struct kvm_vcpu *vcpu, u32 event_inj) +{ + u32 type = event_inj & SVM_EVTINJ_TYPE_MASK; + u8 vector = event_inj & SVM_EVTINJ_VEC_MASK; + + if (!(event_inj & SVM_EVTINJ_VALID)) + return true; + + if (type != SVM_EVTINJ_TYPE_INTR && type != SVM_EVTINJ_TYPE_NMI && + type != SVM_EVTINJ_TYPE_EXEPT && type != SVM_EVTINJ_TYPE_SOFT) + return false; + + if (type == SVM_EVTINJ_TYPE_EXEPT && + !nested_svm_event_inj_valid_exept(vcpu, vector)) + return false; + + return true; +} + +static bool nested_vmcb_check_controls(struct kvm_vcpu *vcpu, + struct vmcb_ctrl_area_cached *control) { if (CC(!vmcb12_is_intercept(control, INTERCEPT_VMRUN))) return false; @@ -348,7 +396,8 @@ static bool __nested_vmcb_check_controls(struct kvm_vcpu *vcpu, if (CC(control->asid == 0)) return false; - if (CC((control->nested_ctl & SVM_NESTED_CTL_NP_ENABLE) && !npt_enabled)) + if (CC((control->misc_ctl & SVM_MISC_ENABLE_NP) && + !kvm_vcpu_is_legal_gpa(vcpu, control->nested_cr3))) return false; if (CC(!nested_svm_check_bitmap_pa(vcpu, control->msrpm_base_pa, @@ -363,12 +412,15 @@ static bool __nested_vmcb_check_controls(struct kvm_vcpu *vcpu, return false; } + if (CC(!nested_svm_check_event_inj(vcpu, control->event_inj))) + return false; + return true; } /* Common checks that apply to both L1 and L2 state. */ -static bool __nested_vmcb_check_save(struct kvm_vcpu *vcpu, - struct vmcb_save_area_cached *save) +static bool nested_vmcb_check_save(struct kvm_vcpu *vcpu, + struct vmcb_save_area_cached *save) { if (CC(!(save->efer & EFER_SVME))) return false; @@ -390,6 +442,10 @@ static bool __nested_vmcb_check_save(struct kvm_vcpu *vcpu, CC(!(save->cr0 & X86_CR0_PE)) || CC(!kvm_vcpu_is_legal_cr3(vcpu, save->cr3))) return false; + + if (CC((save->cs.attrib & SVM_SELECTOR_L_MASK) && + (save->cs.attrib & SVM_SELECTOR_DB_MASK))) + return false; } /* Note, SVM doesn't have any additional restrictions on CR4. */ @@ -402,26 +458,12 @@ static bool __nested_vmcb_check_save(struct kvm_vcpu *vcpu, return true; } -static bool nested_vmcb_check_save(struct kvm_vcpu *vcpu) -{ - struct vcpu_svm *svm = to_svm(vcpu); - struct vmcb_save_area_cached *save = &svm->nested.save; - - return __nested_vmcb_check_save(vcpu, save); -} - -static bool nested_vmcb_check_controls(struct kvm_vcpu *vcpu) -{ - struct vcpu_svm *svm = to_svm(vcpu); - struct vmcb_ctrl_area_cached *ctl = &svm->nested.ctl; - - return __nested_vmcb_check_controls(vcpu, ctl); -} - int nested_svm_check_cached_vmcb12(struct kvm_vcpu *vcpu) { - if (!nested_vmcb_check_save(vcpu) || - !nested_vmcb_check_controls(vcpu)) + struct vcpu_svm *svm = to_svm(vcpu); + + if (!nested_vmcb_check_save(vcpu, &svm->nested.save) || + !nested_vmcb_check_controls(vcpu, &svm->nested.ctl)) return -EINVAL; return 0; @@ -456,37 +498,39 @@ void __nested_copy_vmcb_control_to_cache(struct kvm_vcpu *vcpu, nested_svm_sanitize_intercept(vcpu, to, SKINIT); nested_svm_sanitize_intercept(vcpu, to, RDPRU); - to->iopm_base_pa = from->iopm_base_pa; - to->msrpm_base_pa = from->msrpm_base_pa; + /* Always clear SVM_MISC_ENABLE_NP if the guest cannot use NPTs */ + to->misc_ctl = from->misc_ctl; + if (!guest_cpu_cap_has(vcpu, X86_FEATURE_NPT)) + to->misc_ctl &= ~SVM_MISC_ENABLE_NP; + + to->iopm_base_pa = from->iopm_base_pa & PAGE_MASK; + to->msrpm_base_pa = from->msrpm_base_pa & PAGE_MASK; to->tsc_offset = from->tsc_offset; - to->tlb_ctl = from->tlb_ctl; + to->tlb_ctl = from->tlb_ctl & TLB_CONTROL_MASK; to->erap_ctl = from->erap_ctl; to->int_ctl = from->int_ctl; - to->int_vector = from->int_vector; - to->int_state = from->int_state; + to->int_vector = from->int_vector & SVM_INT_VECTOR_MASK; + to->int_state = from->int_state & SVM_INTERRUPT_SHADOW_MASK; to->exit_code = from->exit_code; to->exit_info_1 = from->exit_info_1; to->exit_info_2 = from->exit_info_2; to->exit_int_info = from->exit_int_info; to->exit_int_info_err = from->exit_int_info_err; - to->nested_ctl = from->nested_ctl; - to->event_inj = from->event_inj; + to->event_inj = from->event_inj & ~SVM_EVTINJ_RESERVED_BITS; to->event_inj_err = from->event_inj_err; to->next_rip = from->next_rip; to->nested_cr3 = from->nested_cr3; - to->virt_ext = from->virt_ext; + to->misc_ctl2 = from->misc_ctl2; to->pause_filter_count = from->pause_filter_count; to->pause_filter_thresh = from->pause_filter_thresh; - /* Copy asid here because nested_vmcb_check_controls will check it. */ + /* Copy asid here because nested_vmcb_check_controls() will check it */ to->asid = from->asid; - to->msrpm_base_pa &= ~0x0fffULL; - to->iopm_base_pa &= ~0x0fffULL; + to->clean = from->clean; #ifdef CONFIG_KVM_HYPERV /* Hyper-V extensions (Enlightened VMCB) */ if (kvm_hv_hypercall_enabled(vcpu)) { - to->clean = from->clean; memcpy(&to->hv_enlightenments, &from->hv_enlightenments, sizeof(to->hv_enlightenments)); } @@ -502,17 +546,34 @@ void nested_copy_vmcb_control_to_cache(struct vcpu_svm *svm, static void __nested_copy_vmcb_save_to_cache(struct vmcb_save_area_cached *to, struct vmcb_save_area *from) { - /* - * Copy only fields that are validated, as we need them - * to avoid TOC/TOU races. - */ - to->efer = from->efer; - to->cr0 = from->cr0; - to->cr3 = from->cr3; - to->cr4 = from->cr4; + to->es = from->es; + to->cs = from->cs; + to->ss = from->ss; + to->ds = from->ds; + to->gdtr = from->gdtr; + to->idtr = from->idtr; - to->dr6 = from->dr6; + to->cpl = from->cpl; + + to->efer = from->efer; + to->cr4 = from->cr4; + to->cr3 = from->cr3; + to->cr0 = from->cr0; to->dr7 = from->dr7; + to->dr6 = from->dr6; + + to->rflags = from->rflags; + to->rip = from->rip; + to->rsp = from->rsp; + + to->s_cet = from->s_cet; + to->ssp = from->ssp; + to->isst_addr = from->isst_addr; + + to->rax = from->rax; + to->cr2 = from->cr2; + + svm_copy_lbrs(to, from); } void nested_copy_vmcb_save_to_cache(struct vcpu_svm *svm, @@ -530,6 +591,7 @@ void nested_sync_control_from_vmcb02(struct vcpu_svm *svm) u32 mask; svm->nested.ctl.event_inj = svm->vmcb->control.event_inj; svm->nested.ctl.event_inj_err = svm->vmcb->control.event_inj_err; + svm->nested.ctl.int_state = svm->vmcb->control.int_state; /* Only a few fields of int_ctl are written by the processor. */ mask = V_IRQ_MASK | V_TPR_MASK; @@ -542,7 +604,7 @@ void nested_sync_control_from_vmcb02(struct vcpu_svm *svm) * int_ctl (because it was never recognized while L2 was running). */ if (svm_is_intercept(svm, INTERCEPT_VINTR) && - !test_bit(INTERCEPT_VINTR, (unsigned long *)svm->nested.ctl.intercepts)) + !vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_VINTR)) mask &= ~V_IRQ_MASK; if (nested_vgif_enabled(svm)) @@ -648,8 +710,16 @@ void nested_vmcb02_compute_g_pat(struct vcpu_svm *svm) svm->nested.vmcb02.ptr->save.g_pat = svm->vmcb01.ptr->save.g_pat; } -static void nested_vmcb02_prepare_save(struct vcpu_svm *svm, struct vmcb *vmcb12) +static bool nested_vmcb12_has_lbrv(struct kvm_vcpu *vcpu) { + return guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV) && + (to_svm(vcpu)->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR); +} + +static void nested_vmcb02_prepare_save(struct vcpu_svm *svm) +{ + struct vmcb_ctrl_area_cached *control = &svm->nested.ctl; + struct vmcb_save_area_cached *save = &svm->nested.save; bool new_vmcb12 = false; struct vmcb *vmcb01 = svm->vmcb01.ptr; struct vmcb *vmcb02 = svm->nested.vmcb02.ptr; @@ -665,64 +735,64 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm, struct vmcb *vmcb12 svm->nested.force_msr_bitmap_recalc = true; } - if (unlikely(new_vmcb12 || vmcb_is_dirty(vmcb12, VMCB_SEG))) { - vmcb02->save.es = vmcb12->save.es; - vmcb02->save.cs = vmcb12->save.cs; - vmcb02->save.ss = vmcb12->save.ss; - vmcb02->save.ds = vmcb12->save.ds; - vmcb02->save.cpl = vmcb12->save.cpl; + if (unlikely(new_vmcb12 || vmcb12_is_dirty(control, VMCB_SEG))) { + vmcb02->save.es = save->es; + vmcb02->save.cs = save->cs; + vmcb02->save.ss = save->ss; + vmcb02->save.ds = save->ds; + vmcb02->save.cpl = save->cpl; vmcb_mark_dirty(vmcb02, VMCB_SEG); } - if (unlikely(new_vmcb12 || vmcb_is_dirty(vmcb12, VMCB_DT))) { - vmcb02->save.gdtr = vmcb12->save.gdtr; - vmcb02->save.idtr = vmcb12->save.idtr; + if (unlikely(new_vmcb12 || vmcb12_is_dirty(control, VMCB_DT))) { + vmcb02->save.gdtr = save->gdtr; + vmcb02->save.idtr = save->idtr; vmcb_mark_dirty(vmcb02, VMCB_DT); } if (guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && - (unlikely(new_vmcb12 || vmcb_is_dirty(vmcb12, VMCB_CET)))) { - vmcb02->save.s_cet = vmcb12->save.s_cet; - vmcb02->save.isst_addr = vmcb12->save.isst_addr; - vmcb02->save.ssp = vmcb12->save.ssp; + (unlikely(new_vmcb12 || vmcb12_is_dirty(control, VMCB_CET)))) { + vmcb02->save.s_cet = save->s_cet; + vmcb02->save.isst_addr = save->isst_addr; + vmcb02->save.ssp = save->ssp; vmcb_mark_dirty(vmcb02, VMCB_CET); } - kvm_set_rflags(vcpu, vmcb12->save.rflags | X86_EFLAGS_FIXED); + kvm_set_rflags(vcpu, save->rflags | X86_EFLAGS_FIXED); svm_set_efer(vcpu, svm->nested.save.efer); svm_set_cr0(vcpu, svm->nested.save.cr0); svm_set_cr4(vcpu, svm->nested.save.cr4); - svm->vcpu.arch.cr2 = vmcb12->save.cr2; + svm->vcpu.arch.cr2 = save->cr2; - kvm_rax_write(vcpu, vmcb12->save.rax); - kvm_rsp_write(vcpu, vmcb12->save.rsp); - kvm_rip_write(vcpu, vmcb12->save.rip); + kvm_rax_write(vcpu, save->rax); + kvm_rsp_write(vcpu, save->rsp); + kvm_rip_write(vcpu, save->rip); /* In case we don't even reach vcpu_run, the fields are not updated */ - vmcb02->save.rax = vmcb12->save.rax; - vmcb02->save.rsp = vmcb12->save.rsp; - vmcb02->save.rip = vmcb12->save.rip; + vmcb02->save.rax = save->rax; + vmcb02->save.rsp = save->rsp; + vmcb02->save.rip = save->rip; - if (unlikely(new_vmcb12 || vmcb_is_dirty(vmcb12, VMCB_DR))) { + if (unlikely(new_vmcb12 || vmcb12_is_dirty(control, VMCB_DR))) { vmcb02->save.dr7 = svm->nested.save.dr7 | DR7_FIXED_1; svm->vcpu.arch.dr6 = svm->nested.save.dr6 | DR6_ACTIVE_LOW; vmcb_mark_dirty(vmcb02, VMCB_DR); } - if (unlikely(guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV) && - (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK))) { + if (nested_vmcb12_has_lbrv(vcpu)) { /* * Reserved bits of DEBUGCTL are ignored. Be consistent with * svm_set_msr's definition of reserved bits. */ - svm_copy_lbrs(vmcb02, vmcb12); + svm_copy_lbrs(&vmcb02->save, save); vmcb02->save.dbgctl &= ~DEBUGCTL_RESERVED_BITS; } else { - svm_copy_lbrs(vmcb02, vmcb01); + svm_copy_lbrs(&vmcb02->save, &vmcb01->save); } + vmcb_mark_dirty(vmcb02, VMCB_LBR); svm_update_lbrv(&svm->vcpu); } @@ -750,18 +820,15 @@ static bool is_evtinj_nmi(u32 evtinj) return type == SVM_EVTINJ_TYPE_NMI; } -static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, - unsigned long vmcb12_rip, - unsigned long vmcb12_csbase) +static void nested_vmcb02_prepare_control(struct vcpu_svm *svm) { u32 int_ctl_vmcb01_bits = V_INTR_MASKING_MASK; u32 int_ctl_vmcb12_bits = V_TPR_MASK | V_IRQ_INJECTION_BITS_MASK; - struct kvm_vcpu *vcpu = &svm->vcpu; - struct vmcb *vmcb01 = svm->vmcb01.ptr; + struct vmcb_ctrl_area_cached *vmcb12_ctrl = &svm->nested.ctl; struct vmcb *vmcb02 = svm->nested.vmcb02.ptr; - u32 pause_count12; - u32 pause_thresh12; + struct vmcb *vmcb01 = svm->vmcb01.ptr; + struct kvm_vcpu *vcpu = &svm->vcpu; nested_svm_transition_tlb_flush(vcpu); @@ -774,7 +841,7 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, */ if (guest_cpu_cap_has(vcpu, X86_FEATURE_VGIF) && - (svm->nested.ctl.int_ctl & V_GIF_ENABLE_MASK)) + (vmcb12_ctrl->int_ctl & V_GIF_ENABLE_MASK)) int_ctl_vmcb12_bits |= (V_GIF_MASK | V_GIF_ENABLE_MASK); else int_ctl_vmcb01_bits |= (V_GIF_MASK | V_GIF_ENABLE_MASK); @@ -790,8 +857,16 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, V_NMI_BLOCKING_MASK); } - /* Copied from vmcb01. msrpm_base can be overwritten later. */ - vmcb02->control.nested_ctl = vmcb01->control.nested_ctl; + /* + * Copied from vmcb01. msrpm_base can be overwritten later. + * + * SVM_MISC_ENABLE_NP in vmcb12 is only used for consistency checks. If + * L1 enables NPTs, KVM shadows L1's NPTs and uses those to run L2. If + * L1 disables NPT, KVM runs L2 with the same NPTs used to run L1. For + * the latter, L1 runs L2 with shadow page tables that translate L2 GVAs + * to L1 GPAs, so the same NPTs can be used for L1 and L2. + */ + vmcb02->control.misc_ctl = vmcb01->control.misc_ctl & SVM_MISC_ENABLE_NP; vmcb02->control.iopm_base_pa = vmcb01->control.iopm_base_pa; vmcb02->control.msrpm_base_pa = vmcb01->control.msrpm_base_pa; vmcb_mark_dirty(vmcb02, VMCB_PERM_MAP); @@ -818,7 +893,7 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, * L1 re-enters L2, the same instruction will trigger a VM-Exit and the * entire cycle start over. */ - if (vmcb02->save.rip && (svm->nested.ctl.bus_lock_rip == vmcb02->save.rip)) + if (vmcb02->save.rip && (svm->nested.last_bus_lock_rip == vmcb02->save.rip)) vmcb02->control.bus_lock_counter = 1; else vmcb02->control.bus_lock_counter = 0; @@ -832,10 +907,9 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, if (nested_npt_enabled(svm)) nested_svm_init_mmu_context(vcpu); - vcpu->arch.tsc_offset = kvm_calc_nested_tsc_offset( - vcpu->arch.l1_tsc_offset, - svm->nested.ctl.tsc_offset, - svm->tsc_ratio_msr); + vcpu->arch.tsc_offset = kvm_calc_nested_tsc_offset(vcpu->arch.l1_tsc_offset, + vmcb12_ctrl->tsc_offset, + svm->tsc_ratio_msr); vmcb02->control.tsc_offset = vcpu->arch.tsc_offset; @@ -844,69 +918,51 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, nested_svm_update_tsc_ratio_msr(vcpu); vmcb02->control.int_ctl = - (svm->nested.ctl.int_ctl & int_ctl_vmcb12_bits) | + (vmcb12_ctrl->int_ctl & int_ctl_vmcb12_bits) | (vmcb01->control.int_ctl & int_ctl_vmcb01_bits); - vmcb02->control.int_vector = svm->nested.ctl.int_vector; - vmcb02->control.int_state = svm->nested.ctl.int_state; - vmcb02->control.event_inj = svm->nested.ctl.event_inj; - vmcb02->control.event_inj_err = svm->nested.ctl.event_inj_err; + vmcb02->control.int_vector = vmcb12_ctrl->int_vector; + vmcb02->control.int_state = vmcb12_ctrl->int_state; + vmcb02->control.event_inj = vmcb12_ctrl->event_inj; + vmcb02->control.event_inj_err = vmcb12_ctrl->event_inj_err; /* - * next_rip is consumed on VMRUN as the return address pushed on the - * stack for injected soft exceptions/interrupts. If nrips is exposed - * to L1, take it verbatim from vmcb12. If nrips is supported in - * hardware but not exposed to L1, stuff the actual L2 RIP to emulate - * what a nrips=0 CPU would do (L1 is responsible for advancing RIP - * prior to injecting the event). + * If nrips is exposed to L1, take NextRIP as-is. Otherwise, L1 + * advances L2's RIP before VMRUN instead of using NextRIP. KVM will + * stuff the current RIP as vmcb02's NextRIP before L2 is run. After + * the first run of L2 (e.g. after save+restore), NextRIP is updated by + * the CPU and/or KVM and should be used regardless of L1's support. */ - if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS)) - vmcb02->control.next_rip = svm->nested.ctl.next_rip; - else if (boot_cpu_has(X86_FEATURE_NRIPS)) - vmcb02->control.next_rip = vmcb12_rip; + if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS) || + !vcpu->arch.nested_run_pending) + vmcb02->control.next_rip = vmcb12_ctrl->next_rip; svm->nmi_l1_to_l2 = is_evtinj_nmi(vmcb02->control.event_inj); + + /* + * soft_int_csbase, soft_int_old_rip, and soft_int_next_rip (if L1 + * doesn't have NRIPS) are initialized later, before the vCPU is run. + */ if (is_evtinj_soft(vmcb02->control.event_inj)) { svm->soft_int_injected = true; - svm->soft_int_csbase = vmcb12_csbase; - svm->soft_int_old_rip = vmcb12_rip; - if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS)) - svm->soft_int_next_rip = svm->nested.ctl.next_rip; - else - svm->soft_int_next_rip = vmcb12_rip; + if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS) || + !vcpu->arch.nested_run_pending) + svm->soft_int_next_rip = vmcb12_ctrl->next_rip; } - /* LBR_CTL_ENABLE_MASK is controlled by svm_update_lbrv() */ + /* SVM_MISC2_ENABLE_V_LBR is controlled by svm_update_lbrv() */ if (!nested_vmcb_needs_vls_intercept(svm)) - vmcb02->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK; + vmcb02->control.misc_ctl2 |= SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE; if (guest_cpu_cap_has(vcpu, X86_FEATURE_PAUSEFILTER)) - pause_count12 = svm->nested.ctl.pause_filter_count; + vmcb02->control.pause_filter_count = vmcb12_ctrl->pause_filter_count; else - pause_count12 = 0; + vmcb02->control.pause_filter_count = 0; if (guest_cpu_cap_has(vcpu, X86_FEATURE_PFTHRESHOLD)) - pause_thresh12 = svm->nested.ctl.pause_filter_thresh; + vmcb02->control.pause_filter_thresh = vmcb12_ctrl->pause_filter_thresh; else - pause_thresh12 = 0; - if (kvm_pause_in_guest(svm->vcpu.kvm)) { - /* use guest values since host doesn't intercept PAUSE */ - vmcb02->control.pause_filter_count = pause_count12; - vmcb02->control.pause_filter_thresh = pause_thresh12; - - } else { - /* start from host values otherwise */ - vmcb02->control.pause_filter_count = vmcb01->control.pause_filter_count; - vmcb02->control.pause_filter_thresh = vmcb01->control.pause_filter_thresh; - - /* ... but ensure filtering is disabled if so requested. */ - if (vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_PAUSE)) { - if (!pause_count12) - vmcb02->control.pause_filter_count = 0; - if (!pause_thresh12) - vmcb02->control.pause_filter_thresh = 0; - } - } + vmcb02->control.pause_filter_thresh = 0; /* * Take ALLOW_LARGER_RAP from vmcb12 even though it should be safe to @@ -917,7 +973,7 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, * L2 is the "guest"). */ if (guest_cpu_cap_has(vcpu, X86_FEATURE_ERAPS)) - vmcb02->control.erap_ctl = (svm->nested.ctl.erap_ctl & + vmcb02->control.erap_ctl = (vmcb12_ctrl->erap_ctl & ERAP_CONTROL_ALLOW_LARGER_RAP) | ERAP_CONTROL_CLEAR_RAP; @@ -925,7 +981,7 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, * Merge guest and host intercepts - must be called with vcpu in * guest-mode to take effect. */ - recalc_intercepts(svm); + nested_vmcb02_recalc_intercepts(svm); } static void nested_svm_copy_common_state(struct vmcb *from_vmcb, struct vmcb *to_vmcb) @@ -940,28 +996,29 @@ static void nested_svm_copy_common_state(struct vmcb *from_vmcb, struct vmcb *to to_vmcb->save.spec_ctrl = from_vmcb->save.spec_ctrl; } -int enter_svm_guest_mode(struct kvm_vcpu *vcpu, u64 vmcb12_gpa, - struct vmcb *vmcb12, bool from_vmrun) +int enter_svm_guest_mode(struct kvm_vcpu *vcpu, u64 vmcb12_gpa, bool from_vmrun) { struct vcpu_svm *svm = to_svm(vcpu); + struct vmcb_ctrl_area_cached *control = &svm->nested.ctl; + struct vmcb_save_area_cached *save = &svm->nested.save; int ret; trace_kvm_nested_vmenter(svm->vmcb->save.rip, vmcb12_gpa, - vmcb12->save.rip, - vmcb12->control.int_ctl, - vmcb12->control.event_inj, - vmcb12->control.nested_ctl, - vmcb12->control.nested_cr3, - vmcb12->save.cr3, + save->rip, + control->int_ctl, + control->event_inj, + control->misc_ctl, + control->nested_cr3, + save->cr3, KVM_ISA_SVM); - trace_kvm_nested_intercepts(vmcb12->control.intercepts[INTERCEPT_CR] & 0xffff, - vmcb12->control.intercepts[INTERCEPT_CR] >> 16, - vmcb12->control.intercepts[INTERCEPT_EXCEPTION], - vmcb12->control.intercepts[INTERCEPT_WORD3], - vmcb12->control.intercepts[INTERCEPT_WORD4], - vmcb12->control.intercepts[INTERCEPT_WORD5]); + trace_kvm_nested_intercepts(control->intercepts[INTERCEPT_CR] & 0xffff, + control->intercepts[INTERCEPT_CR] >> 16, + control->intercepts[INTERCEPT_EXCEPTION], + control->intercepts[INTERCEPT_WORD3], + control->intercepts[INTERCEPT_WORD4], + control->intercepts[INTERCEPT_WORD5]); svm->nested.vmcb12_gpa = vmcb12_gpa; @@ -971,8 +1028,8 @@ int enter_svm_guest_mode(struct kvm_vcpu *vcpu, u64 vmcb12_gpa, nested_svm_copy_common_state(svm->vmcb01.ptr, svm->nested.vmcb02.ptr); svm_switch_vmcb(svm, &svm->nested.vmcb02); - nested_vmcb02_prepare_control(svm, vmcb12->save.rip, vmcb12->save.cs.base); - nested_vmcb02_prepare_save(svm, vmcb12); + nested_vmcb02_prepare_control(svm); + nested_vmcb02_prepare_save(svm); ret = nested_svm_load_cr3(&svm->vcpu, svm->nested.save.cr3, nested_npt_enabled(svm), from_vmrun); @@ -992,12 +1049,38 @@ int enter_svm_guest_mode(struct kvm_vcpu *vcpu, u64 vmcb12_gpa, return 0; } +static int nested_svm_copy_vmcb12_to_cache(struct kvm_vcpu *vcpu, u64 vmcb12_gpa) +{ + struct vcpu_svm *svm = to_svm(vcpu); + struct kvm_host_map map; + struct vmcb *vmcb12; + int r = 0; + + if (kvm_vcpu_map(vcpu, gpa_to_gfn(vmcb12_gpa), &map)) + return -EFAULT; + + vmcb12 = map.hva; + nested_copy_vmcb_control_to_cache(svm, &vmcb12->control); + nested_copy_vmcb_save_to_cache(svm, &vmcb12->save); + + if (nested_svm_check_cached_vmcb12(vcpu) < 0) { + vmcb12->control.exit_code = SVM_EXIT_ERR; + vmcb12->control.exit_info_1 = 0; + vmcb12->control.exit_info_2 = 0; + vmcb12->control.event_inj = 0; + vmcb12->control.event_inj_err = 0; + svm_set_gif(svm, false); + r = -EINVAL; + } + + kvm_vcpu_unmap(vcpu, &map); + return r; +} + int nested_svm_vmrun(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); int ret; - struct vmcb *vmcb12; - struct kvm_host_map map; u64 vmcb12_gpa; struct vmcb *vmcb01 = svm->vmcb01.ptr; @@ -1018,32 +1101,27 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) return ret; } - vmcb12_gpa = svm->vmcb->save.rax; - ret = kvm_vcpu_map(vcpu, gpa_to_gfn(vmcb12_gpa), &map); - if (ret == -EINVAL) { - kvm_inject_gp(vcpu, 0); - return 1; - } else if (ret) { - return kvm_skip_emulated_instruction(vcpu); - } - - ret = kvm_skip_emulated_instruction(vcpu); - - vmcb12 = map.hva; - if (WARN_ON_ONCE(!svm->nested.initialized)) return -EINVAL; - nested_copy_vmcb_control_to_cache(svm, &vmcb12->control); - nested_copy_vmcb_save_to_cache(svm, &vmcb12->save); - - if (nested_svm_check_cached_vmcb12(vcpu) < 0) { - vmcb12->control.exit_code = SVM_EXIT_ERR; - vmcb12->control.exit_info_1 = 0; - vmcb12->control.exit_info_2 = 0; - goto out; + vmcb12_gpa = kvm_register_read(vcpu, VCPU_REGS_RAX); + if (!page_address_valid(vcpu, vmcb12_gpa)) { + kvm_inject_gp(vcpu, 0); + return 1; } + ret = nested_svm_copy_vmcb12_to_cache(vcpu, vmcb12_gpa); + if (ret) { + if (ret == -EFAULT) + return kvm_handle_memory_failure(vcpu, X86EMUL_IO_NEEDED, NULL); + + /* Advance RIP past VMRUN as part of the nested #VMEXIT. */ + return kvm_skip_emulated_instruction(vcpu); + } + + /* At this point, VMRUN is guaranteed to not fault; advance RIP. */ + ret = kvm_skip_emulated_instruction(vcpu); + /* * Since vmcb01 is not in use, we can use it to store some of the L1 * state. @@ -1057,27 +1135,20 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) if (!npt_enabled) vmcb01->save.cr3 = kvm_read_cr3(vcpu); - svm->nested.nested_run_pending = 1; + vcpu->arch.nested_run_pending = KVM_NESTED_RUN_PENDING; - if (enter_svm_guest_mode(vcpu, vmcb12_gpa, vmcb12, true)) - goto out_exit_err; + if (enter_svm_guest_mode(vcpu, vmcb12_gpa, true) || + !nested_svm_merge_msrpm(vcpu)) { + vcpu->arch.nested_run_pending = 0; + svm->nmi_l1_to_l2 = false; + svm->soft_int_injected = false; - if (nested_svm_merge_msrpm(vcpu)) - goto out; + svm->vmcb->control.exit_code = SVM_EXIT_ERR; + svm->vmcb->control.exit_info_1 = 0; + svm->vmcb->control.exit_info_2 = 0; -out_exit_err: - svm->nested.nested_run_pending = 0; - svm->nmi_l1_to_l2 = false; - svm->soft_int_injected = false; - - svm->vmcb->control.exit_code = SVM_EXIT_ERR; - svm->vmcb->control.exit_info_1 = 0; - svm->vmcb->control.exit_info_2 = 0; - - nested_svm_vmexit(svm); - -out: - kvm_vcpu_unmap(vcpu, &map); + nested_svm_vmexit(svm); + } return ret; } @@ -1107,6 +1178,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save, to_save->isst_addr = from_save->isst_addr; to_save->ssp = from_save->ssp; } + + if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) { + svm_copy_lbrs(to_save, from_save); + to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS; + } } void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb) @@ -1125,36 +1201,20 @@ void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb) to_vmcb->save.sysenter_eip = from_vmcb->save.sysenter_eip; } -int nested_svm_vmexit(struct vcpu_svm *svm) +static int nested_svm_vmexit_update_vmcb12(struct kvm_vcpu *vcpu) { - struct kvm_vcpu *vcpu = &svm->vcpu; - struct vmcb *vmcb01 = svm->vmcb01.ptr; + struct vcpu_svm *svm = to_svm(vcpu); struct vmcb *vmcb02 = svm->nested.vmcb02.ptr; - struct vmcb *vmcb12; struct kvm_host_map map; + struct vmcb *vmcb12; int rc; rc = kvm_vcpu_map(vcpu, gpa_to_gfn(svm->nested.vmcb12_gpa), &map); - if (rc) { - if (rc == -EINVAL) - kvm_inject_gp(vcpu, 0); - return 1; - } + if (rc) + return rc; vmcb12 = map.hva; - /* Exit Guest-Mode */ - leave_guest_mode(vcpu); - svm->nested.vmcb12_gpa = 0; - WARN_ON_ONCE(svm->nested.nested_run_pending); - - kvm_clear_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu); - - /* in case we halted in L2 */ - kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE); - - /* Give the current vmcb to the guest */ - vmcb12->save.es = vmcb02->save.es; vmcb12->save.cs = vmcb02->save.cs; vmcb12->save.ss = vmcb02->save.ss; @@ -1164,7 +1224,7 @@ int nested_svm_vmexit(struct vcpu_svm *svm) vmcb12->save.efer = svm->vcpu.arch.efer; vmcb12->save.cr0 = kvm_read_cr0(vcpu); vmcb12->save.cr3 = kvm_read_cr3(vcpu); - vmcb12->save.cr2 = vmcb02->save.cr2; + vmcb12->save.cr2 = vcpu->arch.cr2; vmcb12->save.cr4 = svm->vcpu.arch.cr4; vmcb12->save.rflags = kvm_get_rflags(vcpu); vmcb12->save.rip = kvm_rip_read(vcpu); @@ -1191,22 +1251,50 @@ int nested_svm_vmexit(struct vcpu_svm *svm) if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS)) vmcb12->control.next_rip = vmcb02->control.next_rip; + if (nested_vmcb12_has_lbrv(vcpu)) + svm_copy_lbrs(&vmcb12->save, &vmcb02->save); + + vmcb12->control.event_inj = 0; + vmcb12->control.event_inj_err = 0; vmcb12->control.int_ctl = svm->nested.ctl.int_ctl; - vmcb12->control.event_inj = svm->nested.ctl.event_inj; - vmcb12->control.event_inj_err = svm->nested.ctl.event_inj_err; - if (!kvm_pause_in_guest(vcpu->kvm)) { - vmcb01->control.pause_filter_count = vmcb02->control.pause_filter_count; - vmcb_mark_dirty(vmcb01, VMCB_INTERCEPTS); + trace_kvm_nested_vmexit_inject(vmcb12->control.exit_code, + vmcb12->control.exit_info_1, + vmcb12->control.exit_info_2, + vmcb12->control.exit_int_info, + vmcb12->control.exit_int_info_err, + KVM_ISA_SVM); - } + kvm_vcpu_unmap(vcpu, &map); + return 0; +} + +void nested_svm_vmexit(struct vcpu_svm *svm) +{ + struct kvm_vcpu *vcpu = &svm->vcpu; + struct vmcb *vmcb01 = svm->vmcb01.ptr; + struct vmcb *vmcb02 = svm->nested.vmcb02.ptr; + + if (nested_svm_vmexit_update_vmcb12(vcpu)) + kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu); + + /* Exit Guest-Mode */ + leave_guest_mode(vcpu); + svm->nested.vmcb12_gpa = 0; + + kvm_warn_on_nested_run_pending(vcpu); + + kvm_clear_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu); + + /* in case we halted in L2 */ + kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE); /* - * Invalidate bus_lock_rip unless KVM is still waiting for the guest - * to make forward progress before re-enabling bus lock detection. + * Invalidate last_bus_lock_rip unless KVM is still waiting for the + * guest to make forward progress before re-enabling bus lock detection. */ if (!vmcb02->control.bus_lock_counter) - svm->nested.ctl.bus_lock_rip = INVALID_GPA; + svm->nested.last_bus_lock_rip = INVALID_GPA; nested_svm_copy_common_state(svm->nested.vmcb02.ptr, svm->vmcb01.ptr); @@ -1239,11 +1327,10 @@ int nested_svm_vmexit(struct vcpu_svm *svm) if (!nested_exit_on_intr(svm)) kvm_make_request(KVM_REQ_EVENT, &svm->vcpu); - if (unlikely(guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV) && - (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK))) - svm_copy_lbrs(vmcb12, vmcb02); - else - svm_copy_lbrs(vmcb01, vmcb02); + if (!nested_vmcb12_has_lbrv(vcpu)) { + svm_copy_lbrs(&vmcb01->save, &vmcb02->save); + vmcb_mark_dirty(vmcb01, VMCB_LBR); + } svm_update_lbrv(vcpu); @@ -1296,22 +1383,16 @@ int nested_svm_vmexit(struct vcpu_svm *svm) svm->vcpu.arch.dr7 = DR7_FIXED_1; kvm_update_dr7(&svm->vcpu); - trace_kvm_nested_vmexit_inject(vmcb12->control.exit_code, - vmcb12->control.exit_info_1, - vmcb12->control.exit_info_2, - vmcb12->control.exit_int_info, - vmcb12->control.exit_int_info_err, - KVM_ISA_SVM); - - kvm_vcpu_unmap(vcpu, &map); - nested_svm_transition_tlb_flush(vcpu); nested_svm_uninit_mmu_context(vcpu); - rc = nested_svm_load_cr3(vcpu, vmcb01->save.cr3, false, true); - if (rc) - return 1; + if (nested_svm_load_cr3(vcpu, vmcb01->save.cr3, false, true)) + kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu); + + /* Drop tracking for L1->L2 injected NMIs and soft IRQs */ + svm->nmi_l1_to_l2 = false; + svm->soft_int_injected = false; /* * Drop what we picked up for L2 via svm_complete_interrupts() so it @@ -1336,8 +1417,6 @@ int nested_svm_vmexit(struct vcpu_svm *svm) */ if (kvm_apicv_activated(vcpu->kvm)) __kvm_vcpu_update_apicv(vcpu); - - return 0; } static void nested_svm_triple_fault(struct kvm_vcpu *vcpu) @@ -1407,7 +1486,7 @@ void svm_leave_nested(struct kvm_vcpu *vcpu) struct vcpu_svm *svm = to_svm(vcpu); if (is_guest_mode(vcpu)) { - svm->nested.nested_run_pending = 0; + vcpu->arch.nested_run_pending = 0; svm->nested.vmcb12_gpa = INVALID_GPA; leave_guest_mode(vcpu); @@ -1592,7 +1671,7 @@ static int svm_check_nested_events(struct kvm_vcpu *vcpu) * previously injected event, the pending exception occurred while said * event was being delivered and thus needs to be handled. */ - bool block_nested_exceptions = svm->nested.nested_run_pending; + bool block_nested_exceptions = vcpu->arch.nested_run_pending; /* * New events (not exceptions) are only recognized at instruction * boundaries. If an event needs reinjection, then KVM is handling a @@ -1682,9 +1761,7 @@ int nested_svm_exit_special(struct vcpu_svm *svm) } case SVM_EXIT_VMMCALL: /* Hyper-V L2 TLB flush hypercall is handled by L0 */ - if (guest_hv_cpuid_has_l2_tlb_flush(vcpu) && - nested_svm_l2_tlb_flush_enabled(vcpu) && - kvm_hv_is_tlb_flush_hcall(vcpu)) + if (nested_svm_is_l2_tlb_flush_hcall(vcpu)) return NESTED_EXIT_HOST; break; default: @@ -1729,12 +1806,12 @@ static void nested_copy_vmcb_cache_to_control(struct vmcb_control_area *dst, dst->exit_info_2 = from->exit_info_2; dst->exit_int_info = from->exit_int_info; dst->exit_int_info_err = from->exit_int_info_err; - dst->nested_ctl = from->nested_ctl; + dst->misc_ctl = from->misc_ctl; dst->event_inj = from->event_inj; dst->event_inj_err = from->event_inj_err; dst->next_rip = from->next_rip; - dst->nested_cr3 = from->nested_cr3; - dst->virt_ext = from->virt_ext; + dst->nested_cr3 = from->nested_cr3; + dst->misc_ctl2 = from->misc_ctl2; dst->pause_filter_count = from->pause_filter_count; dst->pause_filter_thresh = from->pause_filter_thresh; /* 'clean' and 'hv_enlightenments' are not changed by KVM */ @@ -1769,7 +1846,7 @@ static int svm_get_nested_state(struct kvm_vcpu *vcpu, kvm_state.size += KVM_STATE_NESTED_SVM_VMCB_SIZE; kvm_state.flags |= KVM_STATE_NESTED_GUEST_MODE; - if (svm->nested.nested_run_pending) + if (vcpu->arch.nested_run_pending) kvm_state.flags |= KVM_STATE_NESTED_RUN_PENDING; } @@ -1869,12 +1946,12 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, ret = -EINVAL; __nested_copy_vmcb_control_to_cache(vcpu, &ctl_cached, ctl); - if (!__nested_vmcb_check_controls(vcpu, &ctl_cached)) + if (!nested_vmcb_check_controls(vcpu, &ctl_cached)) goto out_free; /* * Processor state contains L2 state. Check that it is - * valid for guest mode (see nested_vmcb_check_save). + * valid for guest mode (see nested_vmcb_check_save()). */ cr0 = kvm_read_cr0(vcpu); if (((cr0 & X86_CR0_CD) == 0) && (cr0 & X86_CR0_NW)) @@ -1888,7 +1965,7 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, if (!(save->cr0 & X86_CR0_PG) || !(save->cr0 & X86_CR0_PE) || (save->rflags & X86_EFLAGS_VM) || - !__nested_vmcb_check_save(vcpu, &save_cached)) + !nested_vmcb_check_save(vcpu, &save_cached)) goto out_free; @@ -1906,8 +1983,10 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, svm_set_gif(svm, !!(kvm_state->flags & KVM_STATE_NESTED_GIF_SET)); - svm->nested.nested_run_pending = - !!(kvm_state->flags & KVM_STATE_NESTED_RUN_PENDING); + if (kvm_state->flags & KVM_STATE_NESTED_RUN_PENDING) + vcpu->arch.nested_run_pending = KVM_NESTED_RUN_PENDING_UNTRUSTED; + else + vcpu->arch.nested_run_pending = 0; svm->nested.vmcb12_gpa = kvm_state->hdr.svm.vmcb_pa; @@ -1915,7 +1994,13 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, nested_copy_vmcb_control_to_cache(svm, ctl); svm_switch_vmcb(svm, &svm->nested.vmcb02); - nested_vmcb02_prepare_control(svm, svm->vmcb->save.rip, svm->vmcb->save.cs.base); + nested_vmcb02_prepare_control(svm); + + /* + * Any previously restored state (e.g. KVM_SET_SREGS) would mark fields + * dirty in vmcb01 instead of vmcb02, so mark all of vmcb02 dirty here. + */ + vmcb_mark_all_dirty(svm->vmcb); /* * While the nested guest CR3 is already checked and set by @@ -1930,6 +2015,9 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, svm->nested.force_msr_bitmap_recalc = true; + if (kvm_vcpu_apicv_active(vcpu)) + kvm_make_request(KVM_REQ_APICV_UPDATE, vcpu); + kvm_make_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu); ret = 0; out_free: diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 3f9c1aa39a0a..c2126b3c3072 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -52,18 +52,18 @@ #define SNP_GUEST_VMM_ERR_GENERIC (~0U) /* enable/disable SEV support */ -static bool sev_enabled = true; +static bool __ro_after_init sev_enabled = true; module_param_named(sev, sev_enabled, bool, 0444); /* enable/disable SEV-ES support */ -static bool sev_es_enabled = true; +static bool __ro_after_init sev_es_enabled = true; module_param_named(sev_es, sev_es_enabled, bool, 0444); /* enable/disable SEV-SNP support */ -static bool sev_snp_enabled = true; +static bool __ro_after_init sev_snp_enabled = true; module_param_named(sev_snp, sev_snp_enabled, bool, 0444); -static unsigned int nr_ciphertext_hiding_asids; +static unsigned int __ro_after_init nr_ciphertext_hiding_asids; module_param_named(ciphertext_hiding_asids, nr_ciphertext_hiding_asids, uint, 0444); #define AP_RESET_HOLD_NONE 0 @@ -107,6 +107,45 @@ static unsigned int nr_asids; static unsigned long *sev_asid_bitmap; static unsigned long *sev_reclaim_asid_bitmap; +static __always_inline void kvm_lockdep_assert_sev_lock_held(struct kvm *kvm) +{ +#ifdef CONFIG_PROVE_LOCKING + /* + * Querying SEV+ support is safe if there are no other references, i.e. + * if concurrent initialization of SEV+ is impossible. + */ + if (!refcount_read(&kvm->users_count)) + return; + + /* + * Querying SEV+ support from vCPU context is always safe, as vCPUs can + * only be created after SEV+ is initialized (and KVM disallows all SEV + * sub-ioctls while vCPU creation is in-progress). + */ + if (kvm_get_running_vcpu()) + return; + + lockdep_assert_held(&kvm->lock); +#endif +} + +static bool sev_guest(struct kvm *kvm) +{ + kvm_lockdep_assert_sev_lock_held(kvm); + return ____sev_guest(kvm); +} +static bool sev_es_guest(struct kvm *kvm) +{ + kvm_lockdep_assert_sev_lock_held(kvm); + return ____sev_es_guest(kvm); +} + +static bool sev_snp_guest(struct kvm *kvm) +{ + kvm_lockdep_assert_sev_lock_held(kvm); + return ____sev_snp_guest(kvm); +} + static int snp_decommission_context(struct kvm *kvm); struct enc_region { @@ -198,6 +237,28 @@ static void sev_misc_cg_uncharge(struct kvm_sev_info *sev) misc_cg_uncharge(type, sev->misc_cg, 1); } +static unsigned int sev_alloc_asid(unsigned int min_asid, unsigned int max_asid) +{ + unsigned int asid; + bool retry = true; + + guard(mutex)(&sev_bitmap_lock); + +again: + asid = find_next_zero_bit(sev_asid_bitmap, max_asid + 1, min_asid); + if (asid > max_asid) { + if (retry && __sev_recycle_asids(min_asid, max_asid)) { + retry = false; + goto again; + } + + return asid; + } + + __set_bit(asid, sev_asid_bitmap); + return asid; +} + static int sev_asid_new(struct kvm_sev_info *sev, unsigned long vm_type) { /* @@ -205,7 +266,6 @@ static int sev_asid_new(struct kvm_sev_info *sev, unsigned long vm_type) * SEV-ES-enabled guest can use from 1 to min_sev_asid - 1. */ unsigned int min_asid, max_asid, asid; - bool retry = true; int ret; if (vm_type == KVM_X86_SNP_VM) { @@ -229,37 +289,24 @@ static int sev_asid_new(struct kvm_sev_info *sev, unsigned long vm_type) if (min_asid > max_asid) return -ENOTTY; - WARN_ON(sev->misc_cg); + WARN_ON_ONCE(sev->misc_cg); sev->misc_cg = get_current_misc_cg(); ret = sev_misc_cg_try_charge(sev); - if (ret) { - put_misc_cg(sev->misc_cg); - sev->misc_cg = NULL; - return ret; - } + if (ret) + goto e_put_cg; - mutex_lock(&sev_bitmap_lock); - -again: - asid = find_next_zero_bit(sev_asid_bitmap, max_asid + 1, min_asid); + asid = sev_alloc_asid(min_asid, max_asid); if (asid > max_asid) { - if (retry && __sev_recycle_asids(min_asid, max_asid)) { - retry = false; - goto again; - } - mutex_unlock(&sev_bitmap_lock); ret = -EBUSY; goto e_uncharge; } - __set_bit(asid, sev_asid_bitmap); - - mutex_unlock(&sev_bitmap_lock); - sev->asid = asid; return 0; + e_uncharge: sev_misc_cg_uncharge(sev); +e_put_cg: put_misc_cg(sev->misc_cg); sev->misc_cg = NULL; return ret; @@ -678,40 +725,42 @@ static struct page **sev_pin_memory(struct kvm *kvm, unsigned long uaddr, unsigned int flags) { struct kvm_sev_info *sev = to_kvm_sev_info(kvm); - unsigned long npages, size; - int npinned; - unsigned long locked, lock_limit; + unsigned long npages, total_npages, lock_limit; struct page **pages; - unsigned long first, last; - int ret; + int npinned, ret; lockdep_assert_held(&kvm->lock); if (ulen == 0 || uaddr + ulen < uaddr) return ERR_PTR(-EINVAL); - /* Calculate number of pages. */ - first = (uaddr & PAGE_MASK) >> PAGE_SHIFT; - last = ((uaddr + ulen - 1) & PAGE_MASK) >> PAGE_SHIFT; - npages = (last - first + 1); + /* + * Calculate the number of pages that need to be pinned to cover the + * entire range. Note! This isn't simply PFN_DOWN(ulen), as KVM + * doesn't require the incoming address+size to be page aligned! + */ + npages = PFN_DOWN(uaddr + ulen - 1) - PFN_DOWN(uaddr) + 1; + if (npages > INT_MAX) + return ERR_PTR(-EINVAL); + + total_npages = sev->pages_locked + npages; + if (total_npages > totalram_pages()) + return ERR_PTR(-EINVAL); - locked = sev->pages_locked + npages; lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - if (locked > lock_limit && !capable(CAP_IPC_LOCK)) { - pr_err("SEV: %lu locked pages exceed the lock limit of %lu.\n", locked, lock_limit); + if (total_npages > lock_limit && !capable(CAP_IPC_LOCK)) { + pr_err("SEV: %lu total pages would exceed the lock limit of %lu.\n", + total_npages, lock_limit); return ERR_PTR(-ENOMEM); } - if (WARN_ON_ONCE(npages > INT_MAX)) - return ERR_PTR(-EINVAL); - - /* Avoid using vmalloc for smaller buffers. */ - size = npages * sizeof(struct page *); - if (size > PAGE_SIZE) - pages = __vmalloc(size, GFP_KERNEL_ACCOUNT); - else - pages = kmalloc(size, GFP_KERNEL_ACCOUNT); - + /* + * Don't WARN if the kernel (rightly) thinks the total size is absurd, + * i.e. rely on the kernel to reject outrageous range sizes. The above + * check on the number of pages is purely to avoid truncation as + * pin_user_pages_fast() takes the number of pages as a 32-bit int. + */ + pages = kvzalloc_objs(*pages, npages, GFP_KERNEL_ACCOUNT | __GFP_NOWARN); if (!pages) return ERR_PTR(-ENOMEM); @@ -724,7 +773,7 @@ static struct page **sev_pin_memory(struct kvm *kvm, unsigned long uaddr, } *n = npages; - sev->pages_locked = locked; + sev->pages_locked = total_npages; return pages; @@ -882,6 +931,11 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm) u8 *d; int i; + lockdep_assert_held(&vcpu->mutex); + + if (vcpu->arch.guest_state_protected) + return -EINVAL; + /* Check some debug related fields before encrypting the VMSA */ if (svm->vcpu.guest_debug || (svm->vmcb->save.dr7 & ~DR7_FIXED_1)) return -EINVAL; @@ -1027,19 +1081,21 @@ static int sev_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp) if (!sev_es_guest(kvm)) return -ENOTTY; + if (kvm_is_vcpu_creation_in_progress(kvm)) + return -EBUSY; + + ret = kvm_lock_all_vcpus(kvm); + if (ret) + return ret; + kvm_for_each_vcpu(i, vcpu, kvm) { - ret = mutex_lock_killable(&vcpu->mutex); - if (ret) - return ret; - ret = __sev_launch_update_vmsa(kvm, vcpu, &argp->error); - - mutex_unlock(&vcpu->mutex); if (ret) - return ret; + break; } - return 0; + kvm_unlock_all_vcpus(kvm); + return ret; } static int sev_launch_measure(struct kvm *kvm, struct kvm_sev_cmd *argp) @@ -2047,8 +2103,8 @@ static int sev_check_source_vcpus(struct kvm *dst, struct kvm *src) struct kvm_vcpu *src_vcpu; unsigned long i; - if (src->created_vcpus != atomic_read(&src->online_vcpus) || - dst->created_vcpus != atomic_read(&dst->online_vcpus)) + if (kvm_is_vcpu_creation_in_progress(src) || + kvm_is_vcpu_creation_in_progress(dst)) return -EBUSY; if (!sev_es_guest(src)) @@ -2359,7 +2415,6 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp) struct kvm_memory_slot *memslot; long npages, count; void __user *src; - int ret = 0; if (!sev_snp_guest(kvm) || !sev->snp_context) return -EINVAL; @@ -2404,13 +2459,11 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp) * initial expected state and better guard against unexpected * situations. */ - mutex_lock(&kvm->slots_lock); + guard(mutex)(&kvm->slots_lock); memslot = gfn_to_memslot(kvm, params.gfn_start); - if (!kvm_slot_has_gmem(memslot)) { - ret = -EINVAL; - goto out; - } + if (!kvm_slot_has_gmem(memslot)) + return -EINVAL; sev_populate_args.sev_fd = argp->sev_fd; sev_populate_args.type = params.type; @@ -2421,22 +2474,18 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp) argp->error = sev_populate_args.fw_error; pr_debug("%s: kvm_gmem_populate failed, ret %ld (fw_error %d)\n", __func__, count, argp->error); - ret = -EIO; - } else { - params.gfn_start += count; - params.len -= count * PAGE_SIZE; - if (params.type != KVM_SEV_SNP_PAGE_TYPE_ZERO) - params.uaddr += count * PAGE_SIZE; - - ret = 0; - if (copy_to_user(u64_to_user_ptr(argp->data), ¶ms, sizeof(params))) - ret = -EFAULT; + return -EIO; } -out: - mutex_unlock(&kvm->slots_lock); + params.gfn_start += count; + params.len -= count * PAGE_SIZE; + if (params.type != KVM_SEV_SNP_PAGE_TYPE_ZERO) + params.uaddr += count * PAGE_SIZE; - return ret; + if (copy_to_user(u64_to_user_ptr(argp->data), ¶ms, sizeof(params))) + return -EFAULT; + + return 0; } static int snp_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp) @@ -2447,6 +2496,13 @@ static int snp_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp) unsigned long i; int ret; + if (kvm_is_vcpu_creation_in_progress(kvm)) + return -EBUSY; + + ret = kvm_lock_all_vcpus(kvm); + if (ret) + return ret; + data.gctx_paddr = __psp_pa(sev->snp_context); data.page_type = SNP_PAGE_TYPE_VMSA; @@ -2456,12 +2512,12 @@ static int snp_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp) ret = sev_es_sync_vmsa(svm); if (ret) - return ret; + goto out; /* Transition the VMSA page to a firmware state. */ ret = rmp_make_private(pfn, INITIAL_VMSA_GPA, PG_LEVEL_4K, sev->asid, true); if (ret) - return ret; + goto out; /* Issue the SNP command to encrypt the VMSA */ data.address = __sme_pa(svm->sev_es.vmsa); @@ -2470,7 +2526,7 @@ static int snp_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp) if (ret) { snp_page_reclaim(kvm, pfn); - return ret; + goto out; } svm->vcpu.arch.guest_state_protected = true; @@ -2484,7 +2540,9 @@ static int snp_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp) svm_enable_lbrv(vcpu); } - return 0; +out: + kvm_unlock_all_vcpus(kvm); + return ret; } static int snp_launch_finish(struct kvm *kvm, struct kvm_sev_cmd *argp) @@ -2587,30 +2645,24 @@ int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp) if (copy_from_user(&sev_cmd, argp, sizeof(struct kvm_sev_cmd))) return -EFAULT; - mutex_lock(&kvm->lock); + guard(mutex)(&kvm->lock); /* Only the enc_context_owner handles some memory enc operations. */ if (is_mirroring_enc_context(kvm) && - !is_cmd_allowed_from_mirror(sev_cmd.id)) { - r = -EINVAL; - goto out; - } + !is_cmd_allowed_from_mirror(sev_cmd.id)) + return -EINVAL; /* * Once KVM_SEV_INIT2 initializes a KVM instance as an SNP guest, only * allow the use of SNP-specific commands. */ - if (sev_snp_guest(kvm) && sev_cmd.id < KVM_SEV_SNP_LAUNCH_START) { - r = -EPERM; - goto out; - } + if (sev_snp_guest(kvm) && sev_cmd.id < KVM_SEV_SNP_LAUNCH_START) + return -EPERM; switch (sev_cmd.id) { case KVM_SEV_ES_INIT: - if (!sev_es_enabled) { - r = -ENOTTY; - goto out; - } + if (!sev_es_enabled) + return -ENOTTY; fallthrough; case KVM_SEV_INIT: r = sev_guest_init(kvm, &sev_cmd); @@ -2682,15 +2734,12 @@ int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp) r = snp_enable_certs(kvm); break; default: - r = -EINVAL; - goto out; + return -EINVAL; } if (copy_to_user(argp, &sev_cmd, sizeof(struct kvm_sev_cmd))) r = -EFAULT; -out: - mutex_unlock(&kvm->lock); return r; } @@ -2701,6 +2750,8 @@ int sev_mem_enc_register_region(struct kvm *kvm, struct enc_region *region; int ret = 0; + guard(mutex)(&kvm->lock); + if (!sev_guest(kvm)) return -ENOTTY; @@ -2708,19 +2759,14 @@ int sev_mem_enc_register_region(struct kvm *kvm, if (is_mirroring_enc_context(kvm)) return -EINVAL; - if (range->addr > ULONG_MAX || range->size > ULONG_MAX) - return -EINVAL; - region = kzalloc_obj(*region, GFP_KERNEL_ACCOUNT); if (!region) return -ENOMEM; - mutex_lock(&kvm->lock); region->pages = sev_pin_memory(kvm, range->addr, range->size, ®ion->npages, FOLL_WRITE | FOLL_LONGTERM); if (IS_ERR(region->pages)) { ret = PTR_ERR(region->pages); - mutex_unlock(&kvm->lock); goto e_free; } @@ -2738,8 +2784,6 @@ int sev_mem_enc_register_region(struct kvm *kvm, region->size = range->size; list_add_tail(®ion->list, &sev->regions_list); - mutex_unlock(&kvm->lock); - return ret; e_free: @@ -2775,35 +2819,25 @@ int sev_mem_enc_unregister_region(struct kvm *kvm, struct kvm_enc_region *range) { struct enc_region *region; - int ret; /* If kvm is mirroring encryption context it isn't responsible for it */ if (is_mirroring_enc_context(kvm)) return -EINVAL; - mutex_lock(&kvm->lock); + guard(mutex)(&kvm->lock); - if (!sev_guest(kvm)) { - ret = -ENOTTY; - goto failed; - } + if (!sev_guest(kvm)) + return -ENOTTY; region = find_enc_region(kvm, range); - if (!region) { - ret = -EINVAL; - goto failed; - } + if (!region) + return -EINVAL; sev_writeback_caches(kvm); __unregister_enc_region_locked(kvm, region); - mutex_unlock(&kvm->lock); return 0; - -failed: - mutex_unlock(&kvm->lock); - return ret; } int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd) @@ -2898,6 +2932,28 @@ static int snp_decommission_context(struct kvm *kvm) return 0; } +void sev_vm_init(struct kvm *kvm) +{ + switch (kvm->arch.vm_type) { + case KVM_X86_DEFAULT_VM: + case KVM_X86_SW_PROTECTED_VM: + break; + case KVM_X86_SNP_VM: + kvm->arch.has_private_mem = true; + fallthrough; + case KVM_X86_SEV_ES_VM: + kvm->arch.has_protected_state = true; + fallthrough; + case KVM_X86_SEV_VM: + kvm->arch.pre_fault_allowed = !kvm->arch.has_private_mem; + to_kvm_sev_info(kvm)->need_init = true; + break; + default: + WARN_ONCE(1, "Unsupported VM type %u", kvm->arch.vm_type); + break; + } +} + void sev_vm_destroy(struct kvm *kvm) { struct kvm_sev_info *sev = to_kvm_sev_info(kvm); @@ -3244,8 +3300,14 @@ void sev_guest_memory_reclaimed(struct kvm *kvm) * With SNP+gmem, private/encrypted memory is unreachable via the * hva-based mmu notifiers, i.e. these events are explicitly scoped to * shared pages, where there's no need to flush caches. + * + * Checking for SEV+ outside of kvm->lock is safe as __sev_guest_init() + * can only be done before vCPUs are created, caches can be incoherent + * if and only if a vCPU was run, and either this task will see the VM + * as being SEV+ or the vCPU won't be to access the memory (because of + * the in-progress invalidation). */ - if (!sev_guest(kvm) || sev_snp_guest(kvm)) + if (!____sev_guest(kvm) || ____sev_snp_guest(kvm)) return; sev_writeback_caches(kvm); @@ -3255,7 +3317,7 @@ void sev_free_vcpu(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm; - if (!sev_es_guest(vcpu->kvm)) + if (!is_sev_es_guest(vcpu)) return; svm = to_svm(vcpu); @@ -3265,7 +3327,7 @@ void sev_free_vcpu(struct kvm_vcpu *vcpu) * a guest-owned page. Transition the page to hypervisor state before * releasing it back to the system. */ - if (sev_snp_guest(vcpu->kvm)) { + if (is_sev_snp_guest(vcpu)) { u64 pfn = __pa(svm->sev_es.vmsa) >> PAGE_SHIFT; if (kvm_rmp_make_shared(vcpu->kvm, pfn, PG_LEVEL_4K)) @@ -3466,7 +3528,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) goto vmgexit_err; break; case SVM_VMGEXIT_AP_CREATION: - if (!sev_snp_guest(vcpu->kvm)) + if (!is_sev_snp_guest(vcpu)) goto vmgexit_err; if (lower_32_bits(control->exit_info_1) != SVM_VMGEXIT_AP_DESTROY) if (!kvm_ghcb_rax_is_valid(svm)) @@ -3480,12 +3542,12 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) case SVM_VMGEXIT_TERM_REQUEST: break; case SVM_VMGEXIT_PSC: - if (!sev_snp_guest(vcpu->kvm) || !kvm_ghcb_sw_scratch_is_valid(svm)) + if (!is_sev_snp_guest(vcpu) || !kvm_ghcb_sw_scratch_is_valid(svm)) goto vmgexit_err; break; case SVM_VMGEXIT_GUEST_REQUEST: case SVM_VMGEXIT_EXT_GUEST_REQUEST: - if (!sev_snp_guest(vcpu->kvm) || + if (!is_sev_snp_guest(vcpu) || !PAGE_ALIGNED(control->exit_info_1) || !PAGE_ALIGNED(control->exit_info_2) || control->exit_info_1 == control->exit_info_2) @@ -3559,7 +3621,8 @@ void sev_es_unmap_ghcb(struct vcpu_svm *svm) int pre_sev_run(struct vcpu_svm *svm, int cpu) { struct svm_cpu_data *sd = per_cpu_ptr(&svm_data, cpu); - struct kvm *kvm = svm->vcpu.kvm; + struct kvm_vcpu *vcpu = &svm->vcpu; + struct kvm *kvm = vcpu->kvm; unsigned int asid = sev_get_asid(kvm); /* @@ -3567,7 +3630,7 @@ int pre_sev_run(struct vcpu_svm *svm, int cpu) * VMSA, e.g. if userspace forces the vCPU to be RUNNABLE after an SNP * AP Destroy event. */ - if (sev_es_guest(kvm) && !VALID_PAGE(svm->vmcb->control.vmsa_pa)) + if (is_sev_es_guest(vcpu) && !VALID_PAGE(svm->vmcb->control.vmsa_pa)) return -EINVAL; /* @@ -4113,15 +4176,13 @@ static int snp_handle_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t resp_ sev_ret_code fw_err = 0; int ret; - if (!sev_snp_guest(kvm)) + if (!is_sev_snp_guest(&svm->vcpu)) return -EINVAL; - mutex_lock(&sev->guest_req_mutex); + guard(mutex)(&sev->guest_req_mutex); - if (kvm_read_guest(kvm, req_gpa, sev->guest_req_buf, PAGE_SIZE)) { - ret = -EIO; - goto out_unlock; - } + if (kvm_read_guest(kvm, req_gpa, sev->guest_req_buf, PAGE_SIZE)) + return -EIO; data.gctx_paddr = __psp_pa(sev->snp_context); data.req_paddr = __psp_pa(sev->guest_req_buf); @@ -4134,21 +4195,16 @@ static int snp_handle_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t resp_ */ ret = sev_issue_cmd(kvm, SEV_CMD_SNP_GUEST_REQUEST, &data, &fw_err); if (ret && !fw_err) - goto out_unlock; + return ret; - if (kvm_write_guest(kvm, resp_gpa, sev->guest_resp_buf, PAGE_SIZE)) { - ret = -EIO; - goto out_unlock; - } + if (kvm_write_guest(kvm, resp_gpa, sev->guest_resp_buf, PAGE_SIZE)) + return -EIO; /* No action is requested *from KVM* if there was a firmware error. */ svm_vmgexit_no_action(svm, SNP_GUEST_ERR(0, fw_err)); - ret = 1; /* resume guest */ - -out_unlock: - mutex_unlock(&sev->guest_req_mutex); - return ret; + /* resume guest */ + return 1; } static int snp_req_certs_err(struct vcpu_svm *svm, u32 vmm_error) @@ -4183,10 +4239,12 @@ static int snp_complete_req_certs(struct kvm_vcpu *vcpu) static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t resp_gpa) { - struct kvm *kvm = svm->vcpu.kvm; + struct kvm_vcpu *vcpu = &svm->vcpu; + struct kvm *kvm = vcpu->kvm; + u8 msg_type; - if (!sev_snp_guest(kvm)) + if (!is_sev_snp_guest(vcpu)) return -EINVAL; if (kvm_read_guest(kvm, req_gpa + offsetof(struct snp_guest_msg_hdr, msg_type), @@ -4205,7 +4263,6 @@ static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t r */ if (msg_type == SNP_MSG_REPORT_REQ) { struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; - struct kvm_vcpu *vcpu = &svm->vcpu; u64 data_npages; gpa_t data_gpa; @@ -4322,7 +4379,7 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm) GHCB_MSR_INFO_MASK, GHCB_MSR_INFO_POS); break; case GHCB_MSR_PREF_GPA_REQ: - if (!sev_snp_guest(vcpu->kvm)) + if (!is_sev_snp_guest(vcpu)) goto out_terminate; set_ghcb_msr_bits(svm, GHCB_MSR_PREF_GPA_NONE, GHCB_MSR_GPA_VALUE_MASK, @@ -4333,7 +4390,7 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm) case GHCB_MSR_REG_GPA_REQ: { u64 gfn; - if (!sev_snp_guest(vcpu->kvm)) + if (!is_sev_snp_guest(vcpu)) goto out_terminate; gfn = get_ghcb_msr_bits(svm, GHCB_MSR_GPA_VALUE_MASK, @@ -4348,7 +4405,7 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm) break; } case GHCB_MSR_PSC_REQ: - if (!sev_snp_guest(vcpu->kvm)) + if (!is_sev_snp_guest(vcpu)) goto out_terminate; ret = snp_begin_psc_msr(svm, control->ghcb_gpa); @@ -4421,7 +4478,7 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) sev_es_sync_from_ghcb(svm); /* SEV-SNP guest requires that the GHCB GPA must be registered */ - if (sev_snp_guest(svm->vcpu.kvm) && !ghcb_gpa_is_registered(svm, ghcb_gpa)) { + if (is_sev_snp_guest(vcpu) && !ghcb_gpa_is_registered(svm, ghcb_gpa)) { vcpu_unimpl(&svm->vcpu, "vmgexit: GHCB GPA [%#llx] is not registered.\n", ghcb_gpa); return -EINVAL; } @@ -4434,25 +4491,17 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) switch (control->exit_code) { case SVM_VMGEXIT_MMIO_READ: - ret = setup_vmgexit_scratch(svm, true, control->exit_info_2); + case SVM_VMGEXIT_MMIO_WRITE: { + bool is_write = control->exit_code == SVM_VMGEXIT_MMIO_WRITE; + + ret = setup_vmgexit_scratch(svm, !is_write, control->exit_info_2); if (ret) break; - ret = kvm_sev_es_mmio_read(vcpu, - control->exit_info_1, - control->exit_info_2, - svm->sev_es.ghcb_sa); - break; - case SVM_VMGEXIT_MMIO_WRITE: - ret = setup_vmgexit_scratch(svm, false, control->exit_info_2); - if (ret) - break; - - ret = kvm_sev_es_mmio_write(vcpu, - control->exit_info_1, - control->exit_info_2, - svm->sev_es.ghcb_sa); + ret = kvm_sev_es_mmio(vcpu, is_write, control->exit_info_1, + control->exit_info_2, svm->sev_es.ghcb_sa); break; + } case SVM_VMGEXIT_NMI_COMPLETE: ++vcpu->stat.nmi_window_exits; svm->nmi_masked = false; @@ -4599,7 +4648,7 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm, bool init_event) struct kvm_sev_info *sev = to_kvm_sev_info(svm->vcpu.kvm); struct vmcb *vmcb = svm->vmcb01.ptr; - svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ES_ENABLE; + svm->vmcb->control.misc_ctl |= SVM_MISC_ENABLE_SEV_ES; /* * An SEV-ES guest requires a VMSA area that is a separate from the @@ -4639,7 +4688,7 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm, bool init_event) if (!sev_vcpu_has_debug_swap(svm)) { vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_READ); vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_WRITE); - recalc_intercepts(svm); + svm_mark_intercepts_dirty(svm); } else { /* * Disable #DB intercept iff DebugSwap is enabled. KVM doesn't @@ -4670,7 +4719,7 @@ void sev_init_vmcb(struct vcpu_svm *svm, bool init_event) { struct kvm_vcpu *vcpu = &svm->vcpu; - svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ENABLE; + svm->vmcb->control.misc_ctl |= SVM_MISC_ENABLE_SEV; clr_exception_intercept(svm, UD_VECTOR); /* @@ -4679,10 +4728,10 @@ void sev_init_vmcb(struct vcpu_svm *svm, bool init_event) */ clr_exception_intercept(svm, GP_VECTOR); - if (init_event && sev_snp_guest(vcpu->kvm)) + if (init_event && is_sev_snp_guest(vcpu)) sev_snp_init_protected_guest_state(vcpu); - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) sev_es_init_vmcb(svm, init_event); } @@ -4693,7 +4742,7 @@ int sev_vcpu_create(struct kvm_vcpu *vcpu) mutex_init(&svm->sev_es.snp_vmsa_mutex); - if (!sev_es_guest(vcpu->kvm)) + if (!is_sev_es_guest(vcpu)) return 0; /* @@ -4713,8 +4762,6 @@ int sev_vcpu_create(struct kvm_vcpu *vcpu) void sev_es_prepare_switch_to_guest(struct vcpu_svm *svm, struct sev_es_save_area *hostsa) { - struct kvm *kvm = svm->vcpu.kvm; - /* * All host state for SEV-ES guests is categorized into three swap types * based on how it is handled by hardware during a world switch: @@ -4753,7 +4800,8 @@ void sev_es_prepare_switch_to_guest(struct vcpu_svm *svm, struct sev_es_save_are * loaded with the correct values *if* the CPU writes the MSRs. */ if (sev_vcpu_has_debug_swap(svm) || - (sev_snp_guest(kvm) && cpu_feature_enabled(X86_FEATURE_DEBUG_SWAP))) { + (cpu_feature_enabled(X86_FEATURE_DEBUG_SWAP) && + is_sev_snp_guest(&svm->vcpu))) { hostsa->dr0_addr_mask = amd_get_dr_addr_mask(0); hostsa->dr1_addr_mask = amd_get_dr_addr_mask(1); hostsa->dr2_addr_mask = amd_get_dr_addr_mask(2); @@ -5117,7 +5165,7 @@ struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu) int error = 0; int ret; - if (!sev_es_guest(vcpu->kvm)) + if (!is_sev_es_guest(vcpu)) return NULL; /* @@ -5130,7 +5178,7 @@ struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu) sev = to_kvm_sev_info(vcpu->kvm); /* Check if the SEV policy allows debugging */ - if (sev_snp_guest(vcpu->kvm)) { + if (is_sev_snp_guest(vcpu)) { if (!(sev->policy & SNP_POLICY_MASK_DEBUG)) return NULL; } else { @@ -5138,7 +5186,7 @@ struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu) return NULL; } - if (sev_snp_guest(vcpu->kvm)) { + if (is_sev_snp_guest(vcpu)) { struct sev_data_snp_dbg dbg = {0}; vmsa = snp_alloc_firmware_page(__GFP_ZERO); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index e6477affac9a..e02a38da5296 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -52,6 +53,7 @@ #include "svm.h" #include "svm_ops.h" +#include "hyperv.h" #include "kvm_onhyperv.h" #include "svm_onhyperv.h" @@ -77,6 +79,7 @@ static bool erratum_383_found __read_mostly; * are published and we know what the new status bits are */ static uint64_t osvw_len = 4, osvw_status; +static DEFINE_SPINLOCK(osvw_lock); static DEFINE_PER_CPU(u64, current_tsc_ratio); @@ -110,52 +113,52 @@ static DEFINE_PER_CPU(u64, current_tsc_ratio); * count only mode. */ -static unsigned short pause_filter_thresh = KVM_DEFAULT_PLE_GAP; +static unsigned short __ro_after_init pause_filter_thresh = KVM_DEFAULT_PLE_GAP; module_param(pause_filter_thresh, ushort, 0444); -static unsigned short pause_filter_count = KVM_SVM_DEFAULT_PLE_WINDOW; +static unsigned short __ro_after_init pause_filter_count = KVM_SVM_DEFAULT_PLE_WINDOW; module_param(pause_filter_count, ushort, 0444); /* Default doubles per-vcpu window every exit. */ -static unsigned short pause_filter_count_grow = KVM_DEFAULT_PLE_WINDOW_GROW; +static unsigned short __ro_after_init pause_filter_count_grow = KVM_DEFAULT_PLE_WINDOW_GROW; module_param(pause_filter_count_grow, ushort, 0444); /* Default resets per-vcpu window every exit to pause_filter_count. */ -static unsigned short pause_filter_count_shrink = KVM_DEFAULT_PLE_WINDOW_SHRINK; +static unsigned short __ro_after_init pause_filter_count_shrink = KVM_DEFAULT_PLE_WINDOW_SHRINK; module_param(pause_filter_count_shrink, ushort, 0444); /* Default is to compute the maximum so we can never overflow. */ -static unsigned short pause_filter_count_max = KVM_SVM_DEFAULT_PLE_WINDOW_MAX; +static unsigned short __ro_after_init pause_filter_count_max = KVM_SVM_DEFAULT_PLE_WINDOW_MAX; module_param(pause_filter_count_max, ushort, 0444); /* * Use nested page tables by default. Note, NPT may get forced off by * svm_hardware_setup() if it's unsupported by hardware or the host kernel. */ -bool npt_enabled = true; +bool __ro_after_init npt_enabled = true; module_param_named(npt, npt_enabled, bool, 0444); /* allow nested virtualization in KVM/SVM */ -static int nested = true; +static int __ro_after_init nested = true; module_param(nested, int, 0444); /* enable/disable Next RIP Save */ -int nrips = true; +int __ro_after_init nrips = true; module_param(nrips, int, 0444); /* enable/disable Virtual VMLOAD VMSAVE */ -static int vls = true; +static int __ro_after_init vls = true; module_param(vls, int, 0444); /* enable/disable Virtual GIF */ -int vgif = true; +int __ro_after_init vgif = true; module_param(vgif, int, 0444); /* enable/disable LBR virtualization */ -int lbrv = true; +int __ro_after_init lbrv = true; module_param(lbrv, int, 0444); -static int tsc_scaling = true; +static int __ro_after_init tsc_scaling = true; module_param(tsc_scaling, int, 0444); module_param(enable_device_posted_irqs, bool, 0444); @@ -164,19 +167,19 @@ bool __read_mostly dump_invalid_vmcb; module_param(dump_invalid_vmcb, bool, 0644); -bool intercept_smi = true; +bool __ro_after_init intercept_smi = true; module_param(intercept_smi, bool, 0444); -bool vnmi = true; +bool __ro_after_init vnmi = true; module_param(vnmi, bool, 0444); module_param(enable_mediated_pmu, bool, 0444); -static bool svm_gp_erratum_intercept = true; +static bool __ro_after_init svm_gp_erratum_intercept = true; static u8 rsm_ins_bytes[] = "\x0f\xaa"; -static unsigned long iopm_base; +static unsigned long __read_mostly iopm_base; DEFINE_PER_CPU(struct svm_cpu_data, svm_data); @@ -216,6 +219,19 @@ int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer) if ((old_efer & EFER_SVME) != (efer & EFER_SVME)) { if (!(efer & EFER_SVME)) { + /* + * Architecturally, clearing EFER.SVME while a guest is + * running yields undefined behavior, i.e. KVM can do + * literally anything. Force the vCPU back into L1 as + * that is the safest option for KVM, but synthesize a + * triple fault (for L1!) so that KVM at least doesn't + * run random L2 code in the context of L1. Do so if + * and only if the vCPU is actively running, e.g. to + * avoid positives if userspace is stuffing state. + */ + if (is_guest_mode(vcpu) && vcpu->wants_to_run) + kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu); + svm_leave_nested(vcpu); /* #GP intercept is still needed for vmware backdoor */ if (!enable_vmware_backdoor) @@ -241,9 +257,11 @@ int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer) * Never intercept #GP for SEV guests, KVM can't * decrypt guest memory to workaround the erratum. */ - if (svm_gp_erratum_intercept && !sev_guest(vcpu->kvm)) + if (svm_gp_erratum_intercept && !is_sev_guest(vcpu)) set_exception_intercept(svm, GP_VECTOR); } + + kvm_make_request(KVM_REQ_RECALC_INTERCEPTS, vcpu); } svm->vmcb->save.efer = efer | EFER_SVME; @@ -283,7 +301,7 @@ static int __svm_skip_emulated_instruction(struct kvm_vcpu *vcpu, * SEV-ES does not expose the next RIP. The RIP update is controlled by * the type of exit and the #VC handler in the guest. */ - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) goto done; if (nrips && svm->vmcb->control.next_rip != 0) { @@ -420,6 +438,48 @@ static void svm_init_osvw(struct kvm_vcpu *vcpu) vcpu->arch.osvw.status |= 1; } +static void svm_init_os_visible_workarounds(void) +{ + u64 len, status; + + /* + * Get OS-Visible Workarounds (OSVW) bits. + * + * Note that it is possible to have a system with mixed processor + * revisions and therefore different OSVW bits. If bits are not the same + * on different processors then choose the worst case (i.e. if erratum + * is present on one processor and not on another then assume that the + * erratum is present everywhere). + * + * Note #2! The OSVW MSRs are used to communciate that an erratum is + * NOT present! Software must assume erratum as present if its bit is + * set in OSVW_STATUS *or* the bit number exceeds OSVW_ID_LENGTH. If + * either RDMSR fails, simply zero out the length to treat all errata + * as being present. Similarly, use the *minimum* length across all + * CPUs, not the maximum length. + * + * If the length is zero, then is KVM already treating all errata as + * being present and there's nothing left to do. + */ + if (!osvw_len) + return; + + if (!this_cpu_has(X86_FEATURE_OSVW) || + native_read_msr_safe(MSR_AMD64_OSVW_ID_LENGTH, &len) || + native_read_msr_safe(MSR_AMD64_OSVW_STATUS, &status)) + len = status = 0; + + if (status == READ_ONCE(osvw_status) && len >= READ_ONCE(osvw_len)) + return; + + guard(spinlock)(&osvw_lock); + + if (len < osvw_len) + osvw_len = len; + osvw_status |= status; + osvw_status &= (1ULL << osvw_len) - 1; +} + static bool __kvm_is_svm_supported(void) { int cpu = smp_processor_id(); @@ -477,27 +537,9 @@ static __always_inline struct sev_es_save_area *sev_es_host_save_area(struct svm return &sd->save_area->host_sev_es_save; } -static inline void kvm_cpu_svm_disable(void) -{ - uint64_t efer; - - wrmsrq(MSR_VM_HSAVE_PA, 0); - rdmsrq(MSR_EFER, efer); - if (efer & EFER_SVME) { - /* - * Force GIF=1 prior to disabling SVM, e.g. to ensure INIT and - * NMI aren't blocked. - */ - stgi(); - wrmsrq(MSR_EFER, efer & ~EFER_SVME); - } -} - static void svm_emergency_disable_virtualization_cpu(void) { - kvm_rebooting = true; - - kvm_cpu_svm_disable(); + wrmsrq(MSR_VM_HSAVE_PA, 0); } static void svm_disable_virtualization_cpu(void) @@ -506,7 +548,8 @@ static void svm_disable_virtualization_cpu(void) if (tsc_scaling) __svm_write_tsc_multiplier(SVM_TSC_RATIO_DEFAULT); - kvm_cpu_svm_disable(); + x86_virt_put_ref(X86_FEATURE_SVM); + wrmsrq(MSR_VM_HSAVE_PA, 0); amd_pmu_disable_virt(); } @@ -515,12 +558,12 @@ static int svm_enable_virtualization_cpu(void) { struct svm_cpu_data *sd; - uint64_t efer; int me = raw_smp_processor_id(); + int r; - rdmsrq(MSR_EFER, efer); - if (efer & EFER_SVME) - return -EBUSY; + r = x86_virt_get_ref(X86_FEATURE_SVM); + if (r) + return r; sd = per_cpu_ptr(&svm_data, me); sd->asid_generation = 1; @@ -528,8 +571,6 @@ static int svm_enable_virtualization_cpu(void) sd->next_asid = sd->max_asid + 1; sd->min_asid = max_sev_asid + 1; - wrmsrq(MSR_EFER, efer | EFER_SVME); - wrmsrq(MSR_VM_HSAVE_PA, sd->save_area_pa); if (static_cpu_has(X86_FEATURE_TSCRATEMSR)) { @@ -540,34 +581,7 @@ static int svm_enable_virtualization_cpu(void) __svm_write_tsc_multiplier(SVM_TSC_RATIO_DEFAULT); } - - /* - * Get OSVW bits. - * - * Note that it is possible to have a system with mixed processor - * revisions and therefore different OSVW bits. If bits are not the same - * on different processors then choose the worst case (i.e. if erratum - * is present on one processor and not on another then assume that the - * erratum is present everywhere). - */ - if (cpu_has(&boot_cpu_data, X86_FEATURE_OSVW)) { - u64 len, status = 0; - int err; - - err = native_read_msr_safe(MSR_AMD64_OSVW_ID_LENGTH, &len); - if (!err) - err = native_read_msr_safe(MSR_AMD64_OSVW_STATUS, &status); - - if (err) - osvw_status = osvw_len = 0; - else { - if (len < osvw_len) - osvw_len = len; - osvw_status |= status; - osvw_status &= (1ULL << osvw_len) - 1; - } - } else - osvw_status = osvw_len = 0; + svm_init_os_visible_workarounds(); svm_init_erratum_383(); @@ -635,7 +649,7 @@ static void set_dr_intercepts(struct vcpu_svm *svm) vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_READ); vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_WRITE); - recalc_intercepts(svm); + svm_mark_intercepts_dirty(svm); } static void clr_dr_intercepts(struct vcpu_svm *svm) @@ -644,7 +658,7 @@ static void clr_dr_intercepts(struct vcpu_svm *svm) vmcb->control.intercepts[INTERCEPT_DR] = 0; - recalc_intercepts(svm); + svm_mark_intercepts_dirty(svm); } static bool msr_write_intercepted(struct kvm_vcpu *vcpu, u32 msr) @@ -710,7 +724,7 @@ void *svm_alloc_permissions_map(unsigned long size, gfp_t gfp_mask) static void svm_recalc_lbr_msr_intercepts(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); - bool intercept = !(svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK); + bool intercept = !(svm->vmcb->control.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR); if (intercept == svm->lbr_msrs_intercepted) return; @@ -720,7 +734,7 @@ static void svm_recalc_lbr_msr_intercepts(struct kvm_vcpu *vcpu) svm_set_intercept_for_msr(vcpu, MSR_IA32_LASTINTFROMIP, MSR_TYPE_RW, intercept); svm_set_intercept_for_msr(vcpu, MSR_IA32_LASTINTTOIP, MSR_TYPE_RW, intercept); - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) svm_set_intercept_for_msr(vcpu, MSR_IA32_DEBUGCTLMSR, MSR_TYPE_RW, intercept); svm->lbr_msrs_intercepted = intercept; @@ -830,7 +844,7 @@ static void svm_recalc_msr_intercepts(struct kvm_vcpu *vcpu) svm_set_intercept_for_msr(vcpu, MSR_IA32_PL3_SSP, MSR_TYPE_RW, !shstk_enabled); } - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) sev_es_recalc_msr_intercepts(vcpu); svm_recalc_pmu_msr_intercepts(vcpu); @@ -841,20 +855,9 @@ static void svm_recalc_msr_intercepts(struct kvm_vcpu *vcpu) */ } -void svm_copy_lbrs(struct vmcb *to_vmcb, struct vmcb *from_vmcb) -{ - to_vmcb->save.dbgctl = from_vmcb->save.dbgctl; - to_vmcb->save.br_from = from_vmcb->save.br_from; - to_vmcb->save.br_to = from_vmcb->save.br_to; - to_vmcb->save.last_excp_from = from_vmcb->save.last_excp_from; - to_vmcb->save.last_excp_to = from_vmcb->save.last_excp_to; - - vmcb_mark_dirty(to_vmcb, VMCB_LBR); -} - static void __svm_enable_lbrv(struct kvm_vcpu *vcpu) { - to_svm(vcpu)->vmcb->control.virt_ext |= LBR_CTL_ENABLE_MASK; + to_svm(vcpu)->vmcb->control.misc_ctl2 |= SVM_MISC2_ENABLE_V_LBR; } void svm_enable_lbrv(struct kvm_vcpu *vcpu) @@ -865,17 +868,17 @@ void svm_enable_lbrv(struct kvm_vcpu *vcpu) static void __svm_disable_lbrv(struct kvm_vcpu *vcpu) { - KVM_BUG_ON(sev_es_guest(vcpu->kvm), vcpu->kvm); - to_svm(vcpu)->vmcb->control.virt_ext &= ~LBR_CTL_ENABLE_MASK; + KVM_BUG_ON(is_sev_es_guest(vcpu), vcpu->kvm); + to_svm(vcpu)->vmcb->control.misc_ctl2 &= ~SVM_MISC2_ENABLE_V_LBR; } void svm_update_lbrv(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); - bool current_enable_lbrv = svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK; + bool current_enable_lbrv = svm->vmcb->control.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR; bool enable_lbrv = (svm->vmcb->save.dbgctl & DEBUGCTLMSR_LBR) || (is_guest_mode(vcpu) && guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV) && - (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK)); + (svm->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR)); if (enable_lbrv && !current_enable_lbrv) __svm_enable_lbrv(vcpu); @@ -910,7 +913,15 @@ static void grow_ple_window(struct kvm_vcpu *vcpu) struct vmcb_control_area *control = &svm->vmcb->control; int old = control->pause_filter_count; - if (kvm_pause_in_guest(vcpu->kvm)) + /* Adjusting pause_filter_count makes no sense if PLE is disabled. */ + WARN_ON_ONCE(kvm_pause_in_guest(vcpu->kvm)); + + /* + * While running L2, KVM should intercept PAUSE if and only if L1 wants + * to intercept PAUSE, and L1's intercept should take priority, i.e. + * KVM should never handle a PAUSE intercept from L2. + */ + if (WARN_ON_ONCE(is_guest_mode(vcpu))) return; control->pause_filter_count = __grow_ple_window(old, @@ -931,7 +942,10 @@ static void shrink_ple_window(struct kvm_vcpu *vcpu) struct vmcb_control_area *control = &svm->vmcb->control; int old = control->pause_filter_count; - if (kvm_pause_in_guest(vcpu->kvm)) + /* Adjusting pause_filter_count makes no sense if PLE is disabled. */ + WARN_ON_ONCE(kvm_pause_in_guest(vcpu->kvm)); + + if (is_guest_mode(vcpu)) return; control->pause_filter_count = @@ -1009,6 +1023,14 @@ void svm_write_tsc_multiplier(struct kvm_vcpu *vcpu) preempt_enable(); } +static bool svm_has_pending_gif_event(struct vcpu_svm *svm) +{ + return svm->vcpu.arch.smi_pending || + svm->vcpu.arch.nmi_pending || + kvm_cpu_has_injectable_intr(&svm->vcpu) || + kvm_apic_has_pending_init_or_sipi(&svm->vcpu); +} + /* Evaluate instruction intercepts that depend on guest CPUID features. */ static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu) { @@ -1034,22 +1056,50 @@ static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu) } /* - * No need to toggle VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK here, it is - * always set if vls is enabled. If the intercepts are set, the bit is - * meaningless anyway. + * Intercept instructions that #UD if EFER.SVME=0, as SVME must be set + * even when running the guest, i.e. hardware will only ever see + * EFER.SVME=1. + * + * No need to toggle any of the vgif/vls/etc. enable bits here, as they + * are set when the VMCB is initialized and never cleared (if the + * relevant intercepts are set, the enablements are meaningless anyway). + * + * FIXME: When #GP is not intercepted, a #GP on these instructions (e.g. + * due to CPL > 0) could be injected by hardware before the instruction + * is intercepted, leading to #GP taking precedence over #UD from the + * guest's perspective. */ - if (guest_cpuid_is_intel_compatible(vcpu)) { + if (!(vcpu->arch.efer & EFER_SVME)) { svm_set_intercept(svm, INTERCEPT_VMLOAD); svm_set_intercept(svm, INTERCEPT_VMSAVE); + svm_set_intercept(svm, INTERCEPT_CLGI); + svm_set_intercept(svm, INTERCEPT_STGI); } else { /* * If hardware supports Virtual VMLOAD VMSAVE then enable it * in VMCB and clear intercepts to avoid #VMEXIT. */ - if (vls) { + if (guest_cpuid_is_intel_compatible(vcpu)) { + svm_set_intercept(svm, INTERCEPT_VMLOAD); + svm_set_intercept(svm, INTERCEPT_VMSAVE); + } else if (vls) { svm_clr_intercept(svm, INTERCEPT_VMLOAD); svm_clr_intercept(svm, INTERCEPT_VMSAVE); } + + /* + * Process pending events when clearing STGI/CLGI intercepts if + * there's at least one pending event that is masked by GIF, so + * that KVM re-evaluates if the intercept needs to be set again + * to track when GIF is re-enabled (e.g. for NMI injection). + */ + if (vgif) { + svm_clr_intercept(svm, INTERCEPT_CLGI); + svm_clr_intercept(svm, INTERCEPT_STGI); + + if (svm_has_pending_gif_event(svm)) + kvm_make_request(KVM_REQ_EVENT, &svm->vcpu); + } } if (kvm_need_rdpmc_intercept(vcpu)) @@ -1162,7 +1212,7 @@ static void init_vmcb(struct kvm_vcpu *vcpu, bool init_event) if (npt_enabled) { /* Setup VMCB for Nested Paging */ - control->nested_ctl |= SVM_NESTED_CTL_NP_ENABLE; + control->misc_ctl |= SVM_MISC_ENABLE_NP; svm_clr_intercept(svm, INTERCEPT_INVLPG); clr_exception_intercept(svm, PF_VECTOR); svm_clr_intercept(svm, INTERCEPT_CR3_READ); @@ -1194,19 +1244,16 @@ static void init_vmcb(struct kvm_vcpu *vcpu, bool init_event) if (vnmi) svm->vmcb->control.int_ctl |= V_NMI_ENABLE_MASK; - if (vgif) { - svm_clr_intercept(svm, INTERCEPT_STGI); - svm_clr_intercept(svm, INTERCEPT_CLGI); + if (vgif) svm->vmcb->control.int_ctl |= V_GIF_ENABLE_MASK; - } if (vls) - svm->vmcb->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK; + svm->vmcb->control.misc_ctl2 |= SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE; if (vcpu->kvm->arch.bus_lock_detection_enabled) svm_set_intercept(svm, INTERCEPT_BUSLOCK); - if (sev_guest(vcpu->kvm)) + if (is_sev_guest(vcpu)) sev_init_vmcb(svm, init_event); svm_hv_init_vmcb(vmcb); @@ -1380,7 +1427,7 @@ static void svm_prepare_switch_to_guest(struct kvm_vcpu *vcpu) struct vcpu_svm *svm = to_svm(vcpu); struct svm_cpu_data *sd = per_cpu_ptr(&svm_data, vcpu->cpu); - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) sev_es_unmap_ghcb(svm); if (svm->guest_state_loaded) @@ -1391,7 +1438,7 @@ static void svm_prepare_switch_to_guest(struct kvm_vcpu *vcpu) * or subsequent vmload of host save area. */ vmsave(sd->save_area_pa); - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) sev_es_prepare_switch_to_guest(svm, sev_es_host_save_area(sd)); if (tsc_scaling) @@ -1404,7 +1451,7 @@ static void svm_prepare_switch_to_guest(struct kvm_vcpu *vcpu) * all CPUs support TSC_AUX virtualization). */ if (likely(tsc_aux_uret_slot >= 0) && - (!boot_cpu_has(X86_FEATURE_V_TSC_AUX) || !sev_es_guest(vcpu->kvm))) + (!boot_cpu_has(X86_FEATURE_V_TSC_AUX) || !is_sev_es_guest(vcpu))) kvm_set_user_return_msr(tsc_aux_uret_slot, svm->tsc_aux, -1ull); if (cpu_feature_enabled(X86_FEATURE_SRSO_BP_SPEC_REDUCE) && @@ -1471,7 +1518,7 @@ static bool svm_get_if_flag(struct kvm_vcpu *vcpu) { struct vmcb *vmcb = to_svm(vcpu)->vmcb; - return sev_es_guest(vcpu->kvm) + return is_sev_es_guest(vcpu) ? vmcb->control.int_state & SVM_GUEST_INTERRUPT_MASK : kvm_get_rflags(vcpu) & X86_EFLAGS_IF; } @@ -1705,7 +1752,7 @@ static void sev_post_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3) * contents of the VMSA, and future VMCB save area updates won't be * seen. */ - if (sev_es_guest(vcpu->kvm)) { + if (is_sev_es_guest(vcpu)) { svm->vmcb->save.cr3 = cr3; vmcb_mark_dirty(svm->vmcb, VMCB_CR); } @@ -1760,7 +1807,7 @@ void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) * SEV-ES guests must always keep the CR intercepts cleared. CR * tracking is done using the CR write traps. */ - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) return; if (hcr0 == cr0) { @@ -1871,7 +1918,7 @@ static void svm_sync_dirty_debug_regs(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); - if (WARN_ON_ONCE(sev_es_guest(vcpu->kvm))) + if (WARN_ON_ONCE(is_sev_es_guest(vcpu))) return; get_debugreg(vcpu->arch.db[0], 0); @@ -1950,7 +1997,7 @@ static int npf_interception(struct kvm_vcpu *vcpu) } } - if (sev_snp_guest(vcpu->kvm) && (error_code & PFERR_GUEST_ENC_MASK)) + if (is_sev_snp_guest(vcpu) && (error_code & PFERR_GUEST_ENC_MASK)) error_code |= PFERR_PRIVATE_ACCESS; trace_kvm_page_fault(vcpu, gpa, error_code); @@ -2095,7 +2142,7 @@ static int shutdown_interception(struct kvm_vcpu *vcpu) * The VM save area for SEV-ES guests has already been encrypted so it * cannot be reinitialized, i.e. synthesizing INIT is futile. */ - if (!sev_es_guest(vcpu->kvm)) { + if (!is_sev_es_guest(vcpu)) { clear_page(svm->vmcb); #ifdef CONFIG_KVM_SMM if (is_smm(vcpu)) @@ -2122,7 +2169,7 @@ static int io_interception(struct kvm_vcpu *vcpu) size = (io_info & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT; if (string) { - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) return sev_es_string_io(svm, size, port, in); else return kvm_emulate_instruction(vcpu, 0); @@ -2151,6 +2198,7 @@ static int intr_interception(struct kvm_vcpu *vcpu) static int vmload_vmsave_interception(struct kvm_vcpu *vcpu, bool vmload) { + u64 vmcb12_gpa = kvm_register_read(vcpu, VCPU_REGS_RAX); struct vcpu_svm *svm = to_svm(vcpu); struct vmcb *vmcb12; struct kvm_host_map map; @@ -2159,13 +2207,14 @@ static int vmload_vmsave_interception(struct kvm_vcpu *vcpu, bool vmload) if (nested_svm_check_permissions(vcpu)) return 1; - ret = kvm_vcpu_map(vcpu, gpa_to_gfn(svm->vmcb->save.rax), &map); - if (ret) { - if (ret == -EINVAL) - kvm_inject_gp(vcpu, 0); + if (!page_address_valid(vcpu, vmcb12_gpa)) { + kvm_inject_gp(vcpu, 0); return 1; } + if (kvm_vcpu_map(vcpu, gpa_to_gfn(vmcb12_gpa), &map)) + return kvm_handle_memory_failure(vcpu, X86EMUL_IO_NEEDED, NULL); + vmcb12 = map.hva; ret = kvm_skip_emulated_instruction(vcpu); @@ -2202,58 +2251,28 @@ static int vmrun_interception(struct kvm_vcpu *vcpu) return nested_svm_vmrun(vcpu); } -enum { - NONE_SVM_INSTR, - SVM_INSTR_VMRUN, - SVM_INSTR_VMLOAD, - SVM_INSTR_VMSAVE, -}; - -/* Return NONE_SVM_INSTR if not SVM instrs, otherwise return decode result */ -static int svm_instr_opcode(struct kvm_vcpu *vcpu) +/* Return 0 if not SVM instr, otherwise return associated exit_code */ +static u64 svm_get_decoded_instr_exit_code(struct kvm_vcpu *vcpu) { struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; if (ctxt->b != 0x1 || ctxt->opcode_len != 2) - return NONE_SVM_INSTR; + return 0; + + BUILD_BUG_ON(!SVM_EXIT_VMRUN || !SVM_EXIT_VMLOAD || !SVM_EXIT_VMSAVE); switch (ctxt->modrm) { case 0xd8: /* VMRUN */ - return SVM_INSTR_VMRUN; + return SVM_EXIT_VMRUN; case 0xda: /* VMLOAD */ - return SVM_INSTR_VMLOAD; + return SVM_EXIT_VMLOAD; case 0xdb: /* VMSAVE */ - return SVM_INSTR_VMSAVE; + return SVM_EXIT_VMSAVE; default: break; } - return NONE_SVM_INSTR; -} - -static int emulate_svm_instr(struct kvm_vcpu *vcpu, int opcode) -{ - const int guest_mode_exit_codes[] = { - [SVM_INSTR_VMRUN] = SVM_EXIT_VMRUN, - [SVM_INSTR_VMLOAD] = SVM_EXIT_VMLOAD, - [SVM_INSTR_VMSAVE] = SVM_EXIT_VMSAVE, - }; - int (*const svm_instr_handlers[])(struct kvm_vcpu *vcpu) = { - [SVM_INSTR_VMRUN] = vmrun_interception, - [SVM_INSTR_VMLOAD] = vmload_interception, - [SVM_INSTR_VMSAVE] = vmsave_interception, - }; - struct vcpu_svm *svm = to_svm(vcpu); - int ret; - - if (is_guest_mode(vcpu)) { - /* Returns '1' or -errno on failure, '0' on success. */ - ret = nested_svm_simple_vmexit(svm, guest_mode_exit_codes[opcode]); - if (ret) - return ret; - return 1; - } - return svm_instr_handlers[opcode](vcpu); + return 0; } /* @@ -2268,7 +2287,7 @@ static int gp_interception(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); u32 error_code = svm->vmcb->control.exit_info_1; - int opcode; + u64 svm_exit_code; /* Both #GP cases have zero error_code */ if (error_code) @@ -2278,27 +2297,37 @@ static int gp_interception(struct kvm_vcpu *vcpu) if (x86_decode_emulated_instruction(vcpu, 0, NULL, 0) != EMULATION_OK) goto reinject; - opcode = svm_instr_opcode(vcpu); + /* FIXME: Handle SVM instructions through the emulator */ + svm_exit_code = svm_get_decoded_instr_exit_code(vcpu); + if (svm_exit_code) { + if (!is_guest_mode(vcpu)) + return svm_invoke_exit_handler(vcpu, svm_exit_code); - if (opcode == NONE_SVM_INSTR) { - if (!enable_vmware_backdoor) + if (nested_svm_check_permissions(vcpu)) + return 1; + + if (!page_address_valid(vcpu, kvm_register_read(vcpu, VCPU_REGS_RAX))) goto reinject; /* - * VMware backdoor emulation on #GP interception only handles - * IN{S}, OUT{S}, and RDPMC. + * FIXME: Only synthesize a #VMEXIT if L1 sets the intercept, + * but only after the VMLOAD/VMSAVE exit handlers can properly + * handle VMLOAD/VMSAVE from L2 with VLS enabled in L1 (i.e. + * RAX is an L2 GPA that needs translation through L1's NPT). */ - if (!is_guest_mode(vcpu)) - return kvm_emulate_instruction(vcpu, - EMULTYPE_VMWARE_GP | EMULTYPE_NO_DECODE); - } else { - /* All SVM instructions expect page aligned RAX */ - if (svm->vmcb->save.rax & ~PAGE_MASK) - goto reinject; - - return emulate_svm_instr(vcpu, opcode); + nested_svm_simple_vmexit(svm, svm_exit_code); + return 1; } + /* + * VMware backdoor emulation on #GP interception only handles + * IN{S}, OUT{S}, and RDPMC, and only for L1. + */ + if (!enable_vmware_backdoor || is_guest_mode(vcpu)) + goto reinject; + + return kvm_emulate_instruction(vcpu, EMULTYPE_VMWARE_GP | EMULTYPE_NO_DECODE); + reinject: kvm_queue_exception_e(vcpu, GP_VECTOR, error_code); return 1; @@ -2319,10 +2348,7 @@ void svm_set_gif(struct vcpu_svm *svm, bool value) svm_clear_vintr(svm); enable_gif(svm); - if (svm->vcpu.arch.smi_pending || - svm->vcpu.arch.nmi_pending || - kvm_cpu_has_injectable_intr(&svm->vcpu) || - kvm_apic_has_pending_init_or_sipi(&svm->vcpu)) + if (svm_has_pending_gif_event(svm)) kvm_make_request(KVM_REQ_EVENT, &svm->vcpu); } else { disable_gif(svm); @@ -2366,6 +2392,9 @@ static int invlpga_interception(struct kvm_vcpu *vcpu) gva_t gva = kvm_rax_read(vcpu); u32 asid = kvm_rcx_read(vcpu); + if (nested_svm_check_permissions(vcpu)) + return 1; + /* FIXME: Handle an address size prefix. */ if (!is_long_mode(vcpu)) gva = (u32)gva; @@ -2454,13 +2483,13 @@ static int task_switch_interception(struct kvm_vcpu *vcpu) static void svm_clr_iret_intercept(struct vcpu_svm *svm) { - if (!sev_es_guest(svm->vcpu.kvm)) + if (!is_sev_es_guest(&svm->vcpu)) svm_clr_intercept(svm, INTERCEPT_IRET); } static void svm_set_iret_intercept(struct vcpu_svm *svm) { - if (!sev_es_guest(svm->vcpu.kvm)) + if (!is_sev_es_guest(&svm->vcpu)) svm_set_intercept(svm, INTERCEPT_IRET); } @@ -2468,7 +2497,7 @@ static int iret_interception(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); - WARN_ON_ONCE(sev_es_guest(vcpu->kvm)); + WARN_ON_ONCE(is_sev_es_guest(vcpu)); ++vcpu->stat.nmi_window_exits; svm->awaiting_iret_completion = true; @@ -2642,7 +2671,7 @@ static int dr_interception(struct kvm_vcpu *vcpu) * SEV-ES intercepts DR7 only to disable guest debugging and the guest issues a VMGEXIT * for DR7 write only. KVM cannot change DR7 (always swapped as type 'A') so return early. */ - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) return 1; if (vcpu->guest_debug == 0) { @@ -2723,10 +2752,28 @@ static int svm_get_feature_msr(u32 msr, u64 *data) return 0; } +static u64 *svm_vmcb_lbr(struct vcpu_svm *svm, u32 msr) +{ + switch (msr) { + case MSR_IA32_LASTBRANCHFROMIP: + return &svm->vmcb->save.br_from; + case MSR_IA32_LASTBRANCHTOIP: + return &svm->vmcb->save.br_to; + case MSR_IA32_LASTINTFROMIP: + return &svm->vmcb->save.last_excp_from; + case MSR_IA32_LASTINTTOIP: + return &svm->vmcb->save.last_excp_to; + default: + break; + } + KVM_BUG_ON(1, svm->vcpu.kvm); + return &svm->vmcb->save.br_from; +} + static bool sev_es_prevent_msr_access(struct kvm_vcpu *vcpu, struct msr_data *msr_info) { - return sev_es_guest(vcpu->kvm) && vcpu->arch.guest_state_protected && + return is_sev_es_guest(vcpu) && vcpu->arch.guest_state_protected && msr_info->index != MSR_IA32_XSS && !msr_write_intercepted(vcpu, msr_info->index); } @@ -2796,19 +2843,13 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) msr_info->data = svm->tsc_aux; break; case MSR_IA32_DEBUGCTLMSR: - msr_info->data = svm->vmcb->save.dbgctl; + msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0; break; case MSR_IA32_LASTBRANCHFROMIP: - msr_info->data = svm->vmcb->save.br_from; - break; case MSR_IA32_LASTBRANCHTOIP: - msr_info->data = svm->vmcb->save.br_to; - break; case MSR_IA32_LASTINTFROMIP: - msr_info->data = svm->vmcb->save.last_excp_from; - break; case MSR_IA32_LASTINTTOIP: - msr_info->data = svm->vmcb->save.last_excp_to; + msr_info->data = lbrv ? *svm_vmcb_lbr(svm, msr_info->index) : 0; break; case MSR_VM_HSAVE_PA: msr_info->data = svm->nested.hsave_msr; @@ -2862,7 +2903,7 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) static int svm_complete_emulated_msr(struct kvm_vcpu *vcpu, int err) { struct vcpu_svm *svm = to_svm(vcpu); - if (!err || !sev_es_guest(vcpu->kvm) || WARN_ON_ONCE(!svm->sev_es.ghcb)) + if (!err || !is_sev_es_guest(vcpu) || WARN_ON_ONCE(!svm->sev_es.ghcb)) return kvm_complete_insn_gp(vcpu, err); svm_vmgexit_inject_exception(svm, X86_TRAP_GP); @@ -3043,7 +3084,7 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) * required in this case because TSC_AUX is restored on #VMEXIT * from the host save area. */ - if (boot_cpu_has(X86_FEATURE_V_TSC_AUX) && sev_es_guest(vcpu->kvm)) + if (boot_cpu_has(X86_FEATURE_V_TSC_AUX) && is_sev_es_guest(vcpu)) break; /* @@ -3083,6 +3124,17 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) vmcb_mark_dirty(svm->vmcb, VMCB_LBR); svm_update_lbrv(vcpu); break; + case MSR_IA32_LASTBRANCHFROMIP: + case MSR_IA32_LASTBRANCHTOIP: + case MSR_IA32_LASTINTFROMIP: + case MSR_IA32_LASTINTTOIP: + if (!lbrv) + return KVM_MSR_RET_UNSUPPORTED; + if (!msr->host_initiated) + return 1; + *svm_vmcb_lbr(svm, ecx) = data; + vmcb_mark_dirty(svm->vmcb, VMCB_LBR); + break; case MSR_VM_HSAVE_PA: /* * Old kernels did not validate the value written to @@ -3131,20 +3183,6 @@ static int interrupt_window_interception(struct kvm_vcpu *vcpu) kvm_make_request(KVM_REQ_EVENT, vcpu); svm_clear_vintr(to_svm(vcpu)); - /* - * If not running nested, for AVIC, the only reason to end up here is ExtINTs. - * In this case AVIC was temporarily disabled for - * requesting the IRQ window and we have to re-enable it. - * - * If running nested, still remove the VM wide AVIC inhibit to - * support case in which the interrupt window was requested when the - * vCPU was not running nested. - - * All vCPUs which run still run nested, will remain to have their - * AVIC still inhibited due to per-cpu AVIC inhibition. - */ - kvm_clear_apicv_inhibit(vcpu->kvm, APICV_INHIBIT_REASON_IRQWIN); - ++vcpu->stat.irq_window_exits; return 1; } @@ -3157,7 +3195,7 @@ static int pause_interception(struct kvm_vcpu *vcpu) * vcpu->arch.preempted_in_kernel can never be true. Just * set in_kernel to false as well. */ - in_kernel = !sev_es_guest(vcpu->kvm) && svm_get_cpl(vcpu) == 0; + in_kernel = !is_sev_es_guest(vcpu) && svm_get_cpl(vcpu) == 0; grow_ple_window(vcpu); @@ -3224,11 +3262,27 @@ static int bus_lock_exit(struct kvm_vcpu *vcpu) vcpu->arch.complete_userspace_io = complete_userspace_buslock; if (is_guest_mode(vcpu)) - svm->nested.ctl.bus_lock_rip = vcpu->arch.cui_linear_rip; + svm->nested.last_bus_lock_rip = vcpu->arch.cui_linear_rip; return 0; } +static int vmmcall_interception(struct kvm_vcpu *vcpu) +{ + /* + * Inject a #UD if L2 is active and the VMMCALL isn't a Hyper-V TLB + * hypercall, as VMMCALL #UDs if it's not intercepted, and this path is + * reachable if and only if L1 doesn't want to intercept VMMCALL or has + * enabled L0 (KVM) handling of Hyper-V L2 TLB flush hypercalls. + */ + if (is_guest_mode(vcpu) && !nested_svm_is_l2_tlb_flush_hcall(vcpu)) { + kvm_queue_exception(vcpu, UD_VECTOR); + return 1; + } + + return kvm_emulate_hypercall(vcpu); +} + static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = { [SVM_EXIT_READ_CR0] = cr_interception, [SVM_EXIT_READ_CR3] = cr_interception, @@ -3279,7 +3333,7 @@ static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = { [SVM_EXIT_TASK_SWITCH] = task_switch_interception, [SVM_EXIT_SHUTDOWN] = shutdown_interception, [SVM_EXIT_VMRUN] = vmrun_interception, - [SVM_EXIT_VMMCALL] = kvm_emulate_hypercall, + [SVM_EXIT_VMMCALL] = vmmcall_interception, [SVM_EXIT_VMLOAD] = vmload_interception, [SVM_EXIT_VMSAVE] = vmsave_interception, [SVM_EXIT_STGI] = stgi_interception, @@ -3322,9 +3376,9 @@ static void dump_vmcb(struct kvm_vcpu *vcpu) guard(mutex)(&vmcb_dump_mutex); - vm_type = sev_snp_guest(vcpu->kvm) ? "SEV-SNP" : - sev_es_guest(vcpu->kvm) ? "SEV-ES" : - sev_guest(vcpu->kvm) ? "SEV" : "SVM"; + vm_type = is_sev_snp_guest(vcpu) ? "SEV-SNP" : + is_sev_es_guest(vcpu) ? "SEV-ES" : + is_sev_guest(vcpu) ? "SEV" : "SVM"; pr_err("%s vCPU%u VMCB %p, last attempted VMRUN on CPU %d\n", vm_type, vcpu->vcpu_id, svm->current_vmcb->ptr, vcpu->arch.last_vmentry_cpu); @@ -3354,13 +3408,13 @@ static void dump_vmcb(struct kvm_vcpu *vcpu) pr_err("%-20s%016llx\n", "exit_info2:", control->exit_info_2); pr_err("%-20s%08x\n", "exit_int_info:", control->exit_int_info); pr_err("%-20s%08x\n", "exit_int_info_err:", control->exit_int_info_err); - pr_err("%-20s%lld\n", "nested_ctl:", control->nested_ctl); + pr_err("%-20s%lld\n", "misc_ctl:", control->misc_ctl); pr_err("%-20s%016llx\n", "nested_cr3:", control->nested_cr3); pr_err("%-20s%016llx\n", "avic_vapic_bar:", control->avic_vapic_bar); pr_err("%-20s%016llx\n", "ghcb:", control->ghcb_gpa); pr_err("%-20s%08x\n", "event_inj:", control->event_inj); pr_err("%-20s%08x\n", "event_inj_err:", control->event_inj_err); - pr_err("%-20s%lld\n", "virt_ext:", control->virt_ext); + pr_err("%-20s%lld\n", "misc_ctl2:", control->misc_ctl2); pr_err("%-20s%016llx\n", "next_rip:", control->next_rip); pr_err("%-20s%016llx\n", "avic_backing_page:", control->avic_backing_page); pr_err("%-20s%016llx\n", "avic_logical_id:", control->avic_logical_id); @@ -3369,7 +3423,7 @@ static void dump_vmcb(struct kvm_vcpu *vcpu) pr_err("%-20s%016llx\n", "allowed_sev_features:", control->allowed_sev_features); pr_err("%-20s%016llx\n", "guest_sev_features:", control->guest_sev_features); - if (sev_es_guest(vcpu->kvm)) { + if (is_sev_es_guest(vcpu)) { save = sev_decrypt_vmsa(vcpu); if (!save) goto no_vmsa; @@ -3452,7 +3506,7 @@ static void dump_vmcb(struct kvm_vcpu *vcpu) "excp_from:", save->last_excp_from, "excp_to:", save->last_excp_to); - if (sev_es_guest(vcpu->kvm)) { + if (is_sev_es_guest(vcpu)) { struct sev_es_save_area *vmsa = (struct sev_es_save_area *)save; pr_err("%-15s %016llx\n", @@ -3513,7 +3567,7 @@ static void dump_vmcb(struct kvm_vcpu *vcpu) } no_vmsa: - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) sev_free_decrypted_vmsa(vcpu, save); } @@ -3602,7 +3656,7 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) struct kvm_run *kvm_run = vcpu->run; /* SEV-ES guests must use the CR write traps to track CR registers. */ - if (!sev_es_guest(vcpu->kvm)) { + if (!is_sev_es_guest(vcpu)) { if (!svm_is_intercept(svm, INTERCEPT_CR0_WRITE)) vcpu->arch.cr0 = svm->vmcb->save.cr0; if (npt_enabled) @@ -3638,6 +3692,16 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) return svm_invoke_exit_handler(vcpu, svm->vmcb->control.exit_code); } +static void svm_set_nested_run_soft_int_state(struct kvm_vcpu *vcpu) +{ + struct vcpu_svm *svm = to_svm(vcpu); + + svm->soft_int_csbase = svm->vmcb->save.cs.base; + svm->soft_int_old_rip = kvm_rip_read(vcpu); + if (!guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS)) + svm->soft_int_next_rip = kvm_rip_read(vcpu); +} + static int pre_svm_run(struct kvm_vcpu *vcpu) { struct svm_cpu_data *sd = per_cpu_ptr(&svm_data, vcpu->cpu); @@ -3654,7 +3718,7 @@ static int pre_svm_run(struct kvm_vcpu *vcpu) svm->current_vmcb->cpu = vcpu->cpu; } - if (sev_guest(vcpu->kvm)) + if (is_sev_guest(vcpu)) return pre_sev_run(svm, vcpu->cpu); /* FIXME: handle wraparound of asid_generation */ @@ -3733,12 +3797,59 @@ static void svm_inject_irq(struct kvm_vcpu *vcpu, bool reinjected) type = SVM_EVTINJ_TYPE_INTR; } + /* + * If AVIC was inhibited in order to detect an IRQ window, and there's + * no other injectable interrupts pending or L2 is active (see below), + * then drop the inhibit as the window has served its purpose. + * + * If L2 is active, this path is reachable if L1 is not intercepting + * IRQs, i.e. if KVM is injecting L1 IRQs into L2. AVIC is locally + * inhibited while L2 is active; drop the VM-wide inhibit to optimize + * the case in which the interrupt window was requested while L1 was + * active (the vCPU was not running nested). + */ + if (svm->avic_irq_window && + (!kvm_cpu_has_injectable_intr(vcpu) || is_guest_mode(vcpu))) { + svm->avic_irq_window = false; + kvm_dec_apicv_irq_window_req(svm->vcpu.kvm); + } + trace_kvm_inj_virq(intr->nr, intr->soft, reinjected); ++vcpu->stat.irq_injections; svm->vmcb->control.event_inj = intr->nr | SVM_EVTINJ_VALID | type; } +static void svm_fixup_nested_rips(struct kvm_vcpu *vcpu) +{ + struct vcpu_svm *svm = to_svm(vcpu); + + if (!is_guest_mode(vcpu) || !vcpu->arch.nested_run_pending) + return; + + /* + * If nrips is supported in hardware but not exposed to L1, stuff the + * actual L2 RIP to emulate what a nrips=0 CPU would do (L1 is + * responsible for advancing RIP prior to injecting the event). Once L2 + * runs after L1 executes VMRUN, NextRIP is updated by the CPU and/or + * KVM, and this is no longer needed. + * + * This is done here (as opposed to when preparing vmcb02) to use the + * most up-to-date value of RIP regardless of the order of restoring + * registers and nested state in the vCPU save+restore path. + */ + if (boot_cpu_has(X86_FEATURE_NRIPS) && + !guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS)) + svm->vmcb->control.next_rip = kvm_rip_read(vcpu); + + /* + * Simiarly, initialize the soft int metadata here to use the most + * up-to-date values of RIP and CS base, regardless of restore order. + */ + if (svm->soft_int_injected) + svm_set_nested_run_soft_int_state(vcpu); +} + void svm_complete_interrupt_delivery(struct kvm_vcpu *vcpu, int delivery_mode, int trig_mode, int vector) { @@ -3797,7 +3908,7 @@ static void svm_update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr) * SEV-ES guests must always keep the CR intercepts cleared. CR * tracking is done using the CR write traps. */ - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) return; if (nested_svm_virtualize_tpr(vcpu)) @@ -3861,7 +3972,7 @@ bool svm_nmi_blocked(struct kvm_vcpu *vcpu) static int svm_nmi_allowed(struct kvm_vcpu *vcpu, bool for_injection) { struct vcpu_svm *svm = to_svm(vcpu); - if (svm->nested.nested_run_pending) + if (vcpu->arch.nested_run_pending) return -EBUSY; if (svm_nmi_blocked(vcpu)) @@ -3903,7 +4014,7 @@ static int svm_interrupt_allowed(struct kvm_vcpu *vcpu, bool for_injection) { struct vcpu_svm *svm = to_svm(vcpu); - if (svm->nested.nested_run_pending) + if (vcpu->arch.nested_run_pending) return -EBUSY; if (svm_interrupt_blocked(vcpu)) @@ -3933,17 +4044,28 @@ static void svm_enable_irq_window(struct kvm_vcpu *vcpu) */ if (vgif || gif_set(svm)) { /* - * IRQ window is not needed when AVIC is enabled, - * unless we have pending ExtINT since it cannot be injected - * via AVIC. In such case, KVM needs to temporarily disable AVIC, - * and fallback to injecting IRQ via V_IRQ. + * KVM only enables IRQ windows when AVIC is enabled if there's + * pending ExtINT since it cannot be injected via AVIC (ExtINT + * bypasses the local APIC). V_IRQ is ignored by hardware when + * AVIC is enabled, and so KVM needs to temporarily disable + * AVIC in order to detect when it's ok to inject the ExtINT. * - * If running nested, AVIC is already locally inhibited - * on this vCPU, therefore there is no need to request - * the VM wide AVIC inhibition. + * If running nested, AVIC is already locally inhibited on this + * vCPU (L2 vCPUs use a different MMU that never maps the AVIC + * backing page), therefore there is no need to increment the + * VM-wide AVIC inhibit. KVM will re-evaluate events when the + * vCPU exits to L1 and enable an IRQ window if the ExtINT is + * still pending. + * + * Note, the IRQ window inhibit needs to be updated even if + * AVIC is inhibited for a different reason, as KVM needs to + * keep AVIC inhibited if the other reason is cleared and there + * is still an injectable interrupt pending. */ - if (!is_guest_mode(vcpu)) - kvm_set_apicv_inhibit(vcpu->kvm, APICV_INHIBIT_REASON_IRQWIN); + if (enable_apicv && !svm->avic_irq_window && !is_guest_mode(vcpu)) { + svm->avic_irq_window = true; + kvm_inc_apicv_irq_window_req(vcpu->kvm); + } svm_set_vintr(svm); } @@ -3986,7 +4108,7 @@ static void svm_enable_nmi_window(struct kvm_vcpu *vcpu) * ignores SEV-ES guest writes to EFER.SVME *and* CLGI/STGI are not * supported NAEs in the GHCB protocol. */ - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) return; if (!gif_set(svm)) { @@ -4106,6 +4228,18 @@ static void svm_complete_soft_interrupt(struct kvm_vcpu *vcpu, u8 vector, bool is_soft = (type == SVM_EXITINTINFO_TYPE_SOFT); struct vcpu_svm *svm = to_svm(vcpu); + /* + * Initialize the soft int fields *before* reading them below if KVM + * aborted entry to the guest with a nested VMRUN pending. To ensure + * KVM uses up-to-date values for RIP and CS base across save/restore, + * regardless of restore order, KVM waits to set the soft int fields + * until VMRUN is imminent. But when canceling injection, KVM requeues + * the soft int and will reinject it via the standard injection flow, + * and so KVM needs to grab the state from the pending nested VMRUN. + */ + if (is_guest_mode(vcpu) && vcpu->arch.nested_run_pending) + svm_set_nested_run_soft_int_state(vcpu); + /* * If NRIPS is enabled, KVM must snapshot the pre-VMRUN next_rip that's * associated with the original soft exception/interrupt. next_rip is @@ -4216,8 +4350,10 @@ static void svm_cancel_injection(struct kvm_vcpu *vcpu) static int svm_vcpu_pre_run(struct kvm_vcpu *vcpu) { +#ifdef CONFIG_KVM_AMD_SEV if (to_kvm_sev_info(vcpu->kvm)->need_init) return -EINVAL; +#endif return 1; } @@ -4274,7 +4410,7 @@ static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, bool spec_ctrl_in amd_clear_divider(); - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) __svm_sev_es_vcpu_run(svm, spec_ctrl_intercepted, sev_es_host_save_area(sd)); else @@ -4335,6 +4471,8 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) kvm_register_is_dirty(vcpu, VCPU_EXREG_ERAPS)) svm->vmcb->control.erap_ctl |= ERAP_CONTROL_CLEAR_RAP; + svm_fixup_nested_rips(vcpu); + svm_hv_update_vp_id(svm->vmcb, vcpu); /* @@ -4355,7 +4493,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) * VM-Exit), as running with the host's DEBUGCTL can negatively affect * guest state and can even be fatal, e.g. due to Bus Lock Detect. */ - if (!(svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK) && + if (!(svm->vmcb->control.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR) && vcpu->arch.host_debugctl != svm->vmcb->save.dbgctl) update_debugctlmsr(svm->vmcb->save.dbgctl); @@ -4375,7 +4513,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) if (!static_cpu_has(X86_FEATURE_V_SPEC_CTRL)) x86_spec_ctrl_restore_host(svm->virt_spec_ctrl); - if (!sev_es_guest(vcpu->kvm)) { + if (!is_sev_es_guest(vcpu)) { vcpu->arch.cr2 = svm->vmcb->save.cr2; vcpu->arch.regs[VCPU_REGS_RAX] = svm->vmcb->save.rax; vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp; @@ -4386,7 +4524,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) if (unlikely(svm->vmcb->control.exit_code == SVM_EXIT_NMI)) kvm_before_interrupt(vcpu, KVM_HANDLING_NMI); - if (!(svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK) && + if (!(svm->vmcb->control.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR) && vcpu->arch.host_debugctl != svm->vmcb->save.dbgctl) update_debugctlmsr(vcpu->arch.host_debugctl); @@ -4404,11 +4542,11 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) nested_sync_control_from_vmcb02(svm); /* Track VMRUNs that have made past consistency checking */ - if (svm->nested.nested_run_pending && + if (vcpu->arch.nested_run_pending && !svm_is_vmrun_failure(svm->vmcb->control.exit_code)) ++vcpu->stat.nested_run; - svm->nested.nested_run_pending = 0; + vcpu->arch.nested_run_pending = 0; } svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING; @@ -4436,6 +4574,16 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) svm_complete_interrupts(vcpu); + /* + * Update the cache after completing interrupts to get an accurate + * NextRIP, e.g. when re-injecting a soft interrupt. + * + * FIXME: Rework svm_get_nested_state() to not pull data from the + * cache (except for maybe int_ctl). + */ + if (is_guest_mode(vcpu)) + svm->nested.ctl.next_rip = svm->vmcb->control.next_rip; + return svm_exit_handlers_fastpath(vcpu); } @@ -4488,9 +4636,17 @@ static bool svm_has_emulated_msr(struct kvm *kvm, u32 index) case MSR_IA32_SMBASE: if (!IS_ENABLED(CONFIG_KVM_SMM)) return false; - /* SEV-ES guests do not support SMM, so report false */ - if (kvm && sev_es_guest(kvm)) + +#ifdef CONFIG_KVM_AMD_SEV + /* + * KVM can't access register state to emulate SMM for SEV-ES + * guests. Conusming stale data here is "fine", as KVM only + * checks for MSR_IA32_SMBASE support without a vCPU when + * userspace is querying KVM_CAP_X86_SMM. + */ + if (kvm && ____sev_es_guest(kvm)) return false; +#endif break; default: break; @@ -4525,7 +4681,7 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) if (guest_cpuid_is_intel_compatible(vcpu)) guest_cpu_cap_clear(vcpu, X86_FEATURE_V_VMSAVE_VMLOAD); - if (sev_guest(vcpu->kvm)) + if (is_sev_guest(vcpu)) sev_vcpu_after_set_cpuid(svm); } @@ -4767,7 +4923,7 @@ bool svm_smi_blocked(struct kvm_vcpu *vcpu) static int svm_smi_allowed(struct kvm_vcpu *vcpu, bool for_injection) { struct vcpu_svm *svm = to_svm(vcpu); - if (svm->nested.nested_run_pending) + if (vcpu->arch.nested_run_pending) return -EBUSY; if (svm_smi_blocked(vcpu)) @@ -4784,7 +4940,6 @@ static int svm_enter_smm(struct kvm_vcpu *vcpu, union kvm_smram *smram) { struct vcpu_svm *svm = to_svm(vcpu); struct kvm_host_map map_save; - int ret; if (!is_guest_mode(vcpu)) return 0; @@ -4804,9 +4959,7 @@ static int svm_enter_smm(struct kvm_vcpu *vcpu, union kvm_smram *smram) svm->vmcb->save.rsp = vcpu->arch.regs[VCPU_REGS_RSP]; svm->vmcb->save.rip = vcpu->arch.regs[VCPU_REGS_RIP]; - ret = nested_svm_simple_vmexit(svm, SVM_EXIT_SW); - if (ret) - return ret; + nested_svm_simple_vmexit(svm, SVM_EXIT_SW); /* * KVM uses VMCB01 to store L1 host state while L2 runs but @@ -4884,12 +5037,11 @@ static int svm_leave_smm(struct kvm_vcpu *vcpu, const union kvm_smram *smram) if (nested_svm_check_cached_vmcb12(vcpu) < 0) goto unmap_save; - if (enter_svm_guest_mode(vcpu, smram64->svm_guest_vmcb_gpa, - vmcb12, false) != 0) + if (enter_svm_guest_mode(vcpu, smram64->svm_guest_vmcb_gpa, false) != 0) goto unmap_save; ret = 0; - svm->nested.nested_run_pending = 1; + vcpu->arch.nested_run_pending = KVM_NESTED_RUN_PENDING; unmap_save: kvm_vcpu_unmap(vcpu, &map_save); @@ -4925,7 +5077,7 @@ static int svm_check_emulate_instruction(struct kvm_vcpu *vcpu, int emul_type, return X86EMUL_UNHANDLEABLE_VECTORING; /* Emulation is always possible when KVM has access to all guest state. */ - if (!sev_guest(vcpu->kvm)) + if (!is_sev_guest(vcpu)) return X86EMUL_CONTINUE; /* #UD and #GP should never be intercepted for SEV guests. */ @@ -4937,7 +5089,7 @@ static int svm_check_emulate_instruction(struct kvm_vcpu *vcpu, int emul_type, * Emulation is impossible for SEV-ES guests as KVM doesn't have access * to guest register state. */ - if (sev_es_guest(vcpu->kvm)) + if (is_sev_es_guest(vcpu)) return X86EMUL_RETRY_INSTR; /* @@ -5074,7 +5226,7 @@ static bool svm_apic_init_signal_blocked(struct kvm_vcpu *vcpu) static void svm_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector) { - if (!sev_es_guest(vcpu->kvm)) + if (!is_sev_es_guest(vcpu)) return kvm_vcpu_deliver_sipi_vector(vcpu, vector); sev_vcpu_deliver_sipi_vector(vcpu, vector); @@ -5090,17 +5242,7 @@ static void svm_vm_destroy(struct kvm *kvm) static int svm_vm_init(struct kvm *kvm) { - int type = kvm->arch.vm_type; - - if (type != KVM_X86_DEFAULT_VM && - type != KVM_X86_SW_PROTECTED_VM) { - kvm->arch.has_protected_state = - (type == KVM_X86_SEV_ES_VM || type == KVM_X86_SNP_VM); - to_kvm_sev_info(kvm)->need_init = true; - - kvm->arch.has_private_mem = (type == KVM_X86_SNP_VM); - kvm->arch.pre_fault_allowed = !kvm->arch.has_private_mem; - } + sev_vm_init(kvm); if (!pause_filter_count || !pause_filter_thresh) kvm_disable_exits(kvm, KVM_X86_DISABLE_EXITS_PAUSE); @@ -5410,14 +5552,10 @@ static __init int svm_hardware_setup(void) pr_err_ratelimited("NX (Execute Disable) not supported\n"); return -EOPNOTSUPP; } - kvm_enable_efer_bits(EFER_NX); kvm_caps.supported_xcr0 &= ~(XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR); - if (boot_cpu_has(X86_FEATURE_FXSR_OPT)) - kvm_enable_efer_bits(EFER_FFXSR); - if (tsc_scaling) { if (!boot_cpu_has(X86_FEATURE_TSCRATEMSR)) { tsc_scaling = false; @@ -5431,9 +5569,6 @@ static __init int svm_hardware_setup(void) tsc_aux_uret_slot = kvm_add_user_return_msr(MSR_TSC_AUX); - if (boot_cpu_has(X86_FEATURE_AUTOIBRS)) - kvm_enable_efer_bits(EFER_AUTOIBRS); - /* Check for pause filtering support */ if (!boot_cpu_has(X86_FEATURE_PAUSEFILTER)) { pause_filter_count = 0; diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 6942e6b0eda6..a10668d17a16 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -92,6 +92,7 @@ enum { /* TPR and CR2 are always written before VMRUN */ #define VMCB_ALWAYS_DIRTY_MASK ((1U << VMCB_INTR) | (1U << VMCB_CR2)) +#ifdef CONFIG_KVM_AMD_SEV struct kvm_sev_info { bool active; /* SEV enabled guest */ bool es_active; /* SEV-ES enabled guest */ @@ -117,6 +118,7 @@ struct kvm_sev_info { cpumask_var_t have_run_cpus; /* CPUs that have done VMRUN for this VM. */ bool snp_certs_enabled; /* SNP certificate-fetching support. */ }; +#endif struct kvm_svm { struct kvm kvm; @@ -127,7 +129,9 @@ struct kvm_svm { u64 *avic_physical_id_table; struct hlist_node hnode; +#ifdef CONFIG_KVM_AMD_SEV struct kvm_sev_info sev_info; +#endif }; struct kvm_vcpu; @@ -140,12 +144,32 @@ struct kvm_vmcb_info { }; struct vmcb_save_area_cached { + struct vmcb_seg es; + struct vmcb_seg cs; + struct vmcb_seg ss; + struct vmcb_seg ds; + struct vmcb_seg gdtr; + struct vmcb_seg idtr; + u8 cpl; u64 efer; u64 cr4; u64 cr3; u64 cr0; u64 dr7; u64 dr6; + u64 rflags; + u64 rip; + u64 rsp; + u64 s_cet; + u64 ssp; + u64 isst_addr; + u64 rax; + u64 cr2; + u64 dbgctl; + u64 br_from; + u64 br_to; + u64 last_excp_from; + u64 last_excp_to; }; struct vmcb_ctrl_area_cached { @@ -166,14 +190,13 @@ struct vmcb_ctrl_area_cached { u64 exit_info_2; u32 exit_int_info; u32 exit_int_info_err; - u64 nested_ctl; + u64 misc_ctl; u32 event_inj; u32 event_inj_err; u64 next_rip; u64 nested_cr3; - u64 virt_ext; + u64 misc_ctl2; u32 clean; - u64 bus_lock_rip; union { #if IS_ENABLED(CONFIG_HYPERV) || IS_ENABLED(CONFIG_KVM_HYPERV) struct hv_vmcb_enlightenments hv_enlightenments; @@ -188,6 +211,7 @@ struct svm_nested_state { u64 vm_cr_msr; u64 vmcb12_gpa; u64 last_vmcb12_gpa; + u64 last_bus_lock_rip; /* * The MSR permissions map used for vmcb02, which is the merge result @@ -195,10 +219,6 @@ struct svm_nested_state { */ void *msrpm; - /* A VMRUN has started but has not yet been performed, so - * we cannot inject a nested vmexit yet. */ - bool nested_run_pending; - /* cache for control fields of the guest */ struct vmcb_ctrl_area_cached ctl; @@ -333,6 +353,7 @@ struct vcpu_svm { bool guest_state_loaded; + bool avic_irq_window; bool x2avic_msrs_intercepted; bool lbr_msrs_intercepted; @@ -357,41 +378,63 @@ struct svm_cpu_data { DECLARE_PER_CPU(struct svm_cpu_data, svm_data); -void recalc_intercepts(struct vcpu_svm *svm); - static __always_inline struct kvm_svm *to_kvm_svm(struct kvm *kvm) { return container_of(kvm, struct kvm_svm, kvm); } +#ifdef CONFIG_KVM_AMD_SEV static __always_inline struct kvm_sev_info *to_kvm_sev_info(struct kvm *kvm) { return &to_kvm_svm(kvm)->sev_info; } -#ifdef CONFIG_KVM_AMD_SEV -static __always_inline bool sev_guest(struct kvm *kvm) +static __always_inline bool ____sev_guest(struct kvm *kvm) { return to_kvm_sev_info(kvm)->active; } -static __always_inline bool sev_es_guest(struct kvm *kvm) +static __always_inline bool ____sev_es_guest(struct kvm *kvm) { struct kvm_sev_info *sev = to_kvm_sev_info(kvm); return sev->es_active && !WARN_ON_ONCE(!sev->active); } -static __always_inline bool sev_snp_guest(struct kvm *kvm) +static __always_inline bool ____sev_snp_guest(struct kvm *kvm) { struct kvm_sev_info *sev = to_kvm_sev_info(kvm); return (sev->vmsa_features & SVM_SEV_FEAT_SNP_ACTIVE) && - !WARN_ON_ONCE(!sev_es_guest(kvm)); + !WARN_ON_ONCE(!____sev_es_guest(kvm)); +} + +static __always_inline bool is_sev_guest(struct kvm_vcpu *vcpu) +{ + return ____sev_guest(vcpu->kvm); +} +static __always_inline bool is_sev_es_guest(struct kvm_vcpu *vcpu) +{ + return ____sev_es_guest(vcpu->kvm); +} + +static __always_inline bool is_sev_snp_guest(struct kvm_vcpu *vcpu) +{ + return ____sev_snp_guest(vcpu->kvm); } #else -#define sev_guest(kvm) false -#define sev_es_guest(kvm) false -#define sev_snp_guest(kvm) false +static __always_inline bool is_sev_guest(struct kvm_vcpu *vcpu) +{ + return false; +} +static __always_inline bool is_sev_es_guest(struct kvm_vcpu *vcpu) +{ + return false; +} + +static __always_inline bool is_sev_snp_guest(struct kvm_vcpu *vcpu) +{ + return false; +} #endif static inline bool ghcb_gpa_is_registered(struct vcpu_svm *svm, u64 val) @@ -415,9 +458,9 @@ static inline void vmcb_mark_dirty(struct vmcb *vmcb, int bit) vmcb->control.clean &= ~(1 << bit); } -static inline bool vmcb_is_dirty(struct vmcb *vmcb, int bit) +static inline bool vmcb12_is_dirty(struct vmcb_ctrl_area_cached *control, int bit) { - return !test_bit(bit, (unsigned long *)&vmcb->control.clean); + return !test_bit(bit, (unsigned long *)&control->clean); } static __always_inline struct vcpu_svm *to_svm(struct kvm_vcpu *vcpu) @@ -486,6 +529,22 @@ static inline bool vmcb12_is_intercept(struct vmcb_ctrl_area_cached *control, u3 return __vmcb_is_intercept((unsigned long *)&control->intercepts, bit); } +void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm); + +static inline void svm_mark_intercepts_dirty(struct vcpu_svm *svm) +{ + vmcb_mark_dirty(svm->vmcb01.ptr, VMCB_INTERCEPTS); + + /* + * If L2 is active, recalculate the intercepts for vmcb02 to account + * for the changes made to vmcb01. All intercept configuration is done + * for vmcb01 and then propagated to vmcb02 to combine KVM's intercepts + * with L1's intercepts (from the vmcb12 snapshot). + */ + if (is_guest_mode(&svm->vcpu)) + nested_vmcb02_recalc_intercepts(svm); +} + static inline void set_exception_intercept(struct vcpu_svm *svm, u32 bit) { struct vmcb *vmcb = svm->vmcb01.ptr; @@ -493,7 +552,7 @@ static inline void set_exception_intercept(struct vcpu_svm *svm, u32 bit) WARN_ON_ONCE(bit >= 32); vmcb_set_intercept(&vmcb->control, INTERCEPT_EXCEPTION_OFFSET + bit); - recalc_intercepts(svm); + svm_mark_intercepts_dirty(svm); } static inline void clr_exception_intercept(struct vcpu_svm *svm, u32 bit) @@ -503,7 +562,7 @@ static inline void clr_exception_intercept(struct vcpu_svm *svm, u32 bit) WARN_ON_ONCE(bit >= 32); vmcb_clr_intercept(&vmcb->control, INTERCEPT_EXCEPTION_OFFSET + bit); - recalc_intercepts(svm); + svm_mark_intercepts_dirty(svm); } static inline void svm_set_intercept(struct vcpu_svm *svm, int bit) @@ -512,7 +571,7 @@ static inline void svm_set_intercept(struct vcpu_svm *svm, int bit) vmcb_set_intercept(&vmcb->control, bit); - recalc_intercepts(svm); + svm_mark_intercepts_dirty(svm); } static inline void svm_clr_intercept(struct vcpu_svm *svm, int bit) @@ -521,7 +580,7 @@ static inline void svm_clr_intercept(struct vcpu_svm *svm, int bit) vmcb_clr_intercept(&vmcb->control, bit); - recalc_intercepts(svm); + svm_mark_intercepts_dirty(svm); } static inline bool svm_is_intercept(struct vcpu_svm *svm, int bit) @@ -578,7 +637,7 @@ static inline bool gif_set(struct vcpu_svm *svm) static inline bool nested_npt_enabled(struct vcpu_svm *svm) { - return svm->nested.ctl.nested_ctl & SVM_NESTED_CTL_NP_ENABLE; + return svm->nested.ctl.misc_ctl & SVM_MISC_ENABLE_NP; } static inline bool nested_vnmi_enabled(struct vcpu_svm *svm) @@ -713,8 +772,16 @@ static inline void *svm_vcpu_alloc_msrpm(void) return svm_alloc_permissions_map(MSRPM_SIZE, GFP_KERNEL_ACCOUNT); } +#define svm_copy_lbrs(to, from) \ +do { \ + (to)->dbgctl = (from)->dbgctl; \ + (to)->br_from = (from)->br_from; \ + (to)->br_to = (from)->br_to; \ + (to)->last_excp_from = (from)->last_excp_from; \ + (to)->last_excp_to = (from)->last_excp_to; \ +} while (0) + void svm_vcpu_free_msrpm(void *msrpm); -void svm_copy_lbrs(struct vmcb *to_vmcb, struct vmcb *from_vmcb); void svm_enable_lbrv(struct kvm_vcpu *vcpu); void svm_update_lbrv(struct kvm_vcpu *vcpu); @@ -776,8 +843,7 @@ static inline bool nested_exit_on_nmi(struct vcpu_svm *svm) int __init nested_svm_init_msrpm_merge_offsets(void); -int enter_svm_guest_mode(struct kvm_vcpu *vcpu, - u64 vmcb_gpa, struct vmcb *vmcb12, bool from_vmrun); +int enter_svm_guest_mode(struct kvm_vcpu *vcpu, u64 vmcb_gpa, bool from_vmrun); void svm_leave_nested(struct kvm_vcpu *vcpu); void svm_free_nested(struct vcpu_svm *svm); int svm_allocate_nested(struct vcpu_svm *svm); @@ -785,14 +851,14 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu); void svm_copy_vmrun_state(struct vmcb_save_area *to_save, struct vmcb_save_area *from_save); void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb); -int nested_svm_vmexit(struct vcpu_svm *svm); +void nested_svm_vmexit(struct vcpu_svm *svm); -static inline int nested_svm_simple_vmexit(struct vcpu_svm *svm, u32 exit_code) +static inline void nested_svm_simple_vmexit(struct vcpu_svm *svm, u32 exit_code) { svm->vmcb->control.exit_code = exit_code; svm->vmcb->control.exit_info_1 = 0; svm->vmcb->control.exit_info_2 = 0; - return nested_svm_vmexit(svm); + nested_svm_vmexit(svm); } int nested_svm_exit_handled(struct vcpu_svm *svm); @@ -886,6 +952,7 @@ static inline struct page *snp_safe_alloc_page(void) int sev_vcpu_create(struct kvm_vcpu *vcpu); void sev_free_vcpu(struct kvm_vcpu *vcpu); +void sev_vm_init(struct kvm *kvm); void sev_vm_destroy(struct kvm *kvm); void __init sev_set_cpu_caps(void); void __init sev_hardware_setup(void); @@ -912,6 +979,7 @@ static inline struct page *snp_safe_alloc_page(void) static inline int sev_vcpu_create(struct kvm_vcpu *vcpu) { return 0; } static inline void sev_free_vcpu(struct kvm_vcpu *vcpu) {} +static inline void sev_vm_init(struct kvm *kvm) {} static inline void sev_vm_destroy(struct kvm *kvm) {} static inline void __init sev_set_cpu_caps(void) {} static inline void __init sev_hardware_setup(void) {} diff --git a/arch/x86/kvm/svm/vmenter.S b/arch/x86/kvm/svm/vmenter.S index 3392bcadfb89..d47c5c93c991 100644 --- a/arch/x86/kvm/svm/vmenter.S +++ b/arch/x86/kvm/svm/vmenter.S @@ -298,16 +298,16 @@ SYM_FUNC_START(__svm_vcpu_run) RESTORE_GUEST_SPEC_CTRL_BODY RESTORE_HOST_SPEC_CTRL_BODY (%_ASM_SP) -10: cmpb $0, _ASM_RIP(kvm_rebooting) +10: cmpb $0, _ASM_RIP(virt_rebooting) jne 2b ud2 -30: cmpb $0, _ASM_RIP(kvm_rebooting) +30: cmpb $0, _ASM_RIP(virt_rebooting) jne 4b ud2 -50: cmpb $0, _ASM_RIP(kvm_rebooting) +50: cmpb $0, _ASM_RIP(virt_rebooting) jne 6b ud2 -70: cmpb $0, _ASM_RIP(kvm_rebooting) +70: cmpb $0, _ASM_RIP(virt_rebooting) jne 8b ud2 @@ -394,7 +394,7 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run) RESTORE_GUEST_SPEC_CTRL_BODY RESTORE_HOST_SPEC_CTRL_BODY %sil -3: cmpb $0, kvm_rebooting(%rip) +3: cmpb $0, virt_rebooting(%rip) jne 2b ud2 diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h index e7fdbe9efc90..0db25bba17f6 100644 --- a/arch/x86/kvm/trace.h +++ b/arch/x86/kvm/trace.h @@ -154,7 +154,7 @@ TRACE_EVENT(kvm_xen_hypercall, __entry->a2 = a2; __entry->a3 = a3; __entry->a4 = a4; - __entry->a4 = a5; + __entry->a5 = a5; ), TP_printk("cpl %d nr 0x%lx a0 0x%lx a1 0x%lx a2 0x%lx a3 0x%lx a4 0x%lx a5 %lx", diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h index 4e371c93ae16..31568274d8bb 100644 --- a/arch/x86/kvm/vmx/capabilities.h +++ b/arch/x86/kvm/vmx/capabilities.h @@ -14,6 +14,7 @@ extern bool __read_mostly flexpriority_enabled; extern bool __read_mostly enable_ept; extern bool __read_mostly enable_unrestricted_guest; extern bool __read_mostly enable_ept_ad_bits; +extern bool __read_mostly enable_cet; extern bool __read_mostly enable_pml; extern int __read_mostly pt_mode; @@ -107,7 +108,7 @@ static inline bool cpu_has_load_perf_global_ctrl(void) static inline bool cpu_has_load_cet_ctrl(void) { - return (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_CET_STATE); + return vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_CET_STATE; } static inline bool cpu_has_save_perf_global_ctrl(void) diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c index a46ccd670785..dbebddf648be 100644 --- a/arch/x86/kvm/vmx/main.c +++ b/arch/x86/kvm/vmx/main.c @@ -29,10 +29,15 @@ static __init int vt_hardware_setup(void) if (ret) return ret; - if (enable_tdx) - tdx_hardware_setup(); + return enable_tdx ? tdx_hardware_setup() : 0; +} - return 0; +static void vt_hardware_unsetup(void) +{ + if (enable_tdx) + tdx_hardware_unsetup(); + + vmx_hardware_unsetup(); } static int vt_vm_init(struct kvm *kvm) @@ -869,7 +874,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .check_processor_compatibility = vmx_check_processor_compat, - .hardware_unsetup = vmx_hardware_unsetup, + .hardware_unsetup = vt_op(hardware_unsetup), .enable_virtualization_cpu = vmx_enable_virtualization_cpu, .disable_virtualization_cpu = vt_op(disable_virtualization_cpu), @@ -1029,7 +1034,6 @@ struct kvm_x86_init_ops vt_init_ops __initdata = { static void __exit vt_exit(void) { kvm_exit(); - tdx_cleanup(); vmx_exit(); } module_exit(vt_exit); @@ -1043,11 +1047,6 @@ static int __init vt_init(void) if (r) return r; - /* tdx_init() has been taken */ - r = tdx_bringup(); - if (r) - goto err_tdx_bringup; - /* * TDX and VMX have different vCPU structures. Calculate the * maximum size/align so that kvm_init() can use the larger @@ -1074,8 +1073,6 @@ static int __init vt_init(void) return 0; err_kvm_init: - tdx_cleanup(); -err_tdx_bringup: vmx_exit(); return r; } diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 937aeb474af7..3fe88f29be7a 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2273,7 +2273,7 @@ static void vmx_start_preemption_timer(struct kvm_vcpu *vcpu, static u64 nested_vmx_calc_efer(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) { - if (vmx->nested.nested_run_pending && + if (vmx->vcpu.arch.nested_run_pending && (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_EFER)) return vmcs12->guest_ia32_efer; else if (vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) @@ -2513,7 +2513,7 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 /* * Interrupt/Exception Fields */ - if (vmx->nested.nested_run_pending) { + if (vmx->vcpu.arch.nested_run_pending) { vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, vmcs12->vm_entry_intr_info_field); vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE, @@ -2621,7 +2621,7 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) vmcs_write64(GUEST_PDPTR3, vmcs12->guest_pdptr3); } - if (kvm_mpx_supported() && vmx->nested.nested_run_pending && + if (kvm_mpx_supported() && vmx->vcpu.arch.nested_run_pending && (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS)) vmcs_write64(GUEST_BNDCFGS, vmcs12->guest_bndcfgs); } @@ -2718,7 +2718,7 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, !(evmcs->hv_clean_fields & HV_VMX_ENLIGHTENED_CLEAN_FIELD_GUEST_GRP1); } - if (vmx->nested.nested_run_pending && + if (vcpu->arch.nested_run_pending && (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_DEBUG_CONTROLS)) { kvm_set_dr(vcpu, 7, vmcs12->guest_dr7); vmx_guest_debugctl_write(vcpu, vmcs12->guest_ia32_debugctl & @@ -2728,13 +2728,13 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, vmx_guest_debugctl_write(vcpu, vmx->nested.pre_vmenter_debugctl); } - if (!vmx->nested.nested_run_pending || + if (!vcpu->arch.nested_run_pending || !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE)) vmcs_write_cet_state(vcpu, vmx->nested.pre_vmenter_s_cet, vmx->nested.pre_vmenter_ssp, vmx->nested.pre_vmenter_ssp_tbl); - if (kvm_mpx_supported() && (!vmx->nested.nested_run_pending || + if (kvm_mpx_supported() && (!vcpu->arch.nested_run_pending || !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) vmcs_write64(GUEST_BNDCFGS, vmx->nested.pre_vmenter_bndcfgs); vmx_set_rflags(vcpu, vmcs12->guest_rflags); @@ -2747,7 +2747,7 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, vcpu->arch.cr0_guest_owned_bits &= ~vmcs12->cr0_guest_host_mask; vmcs_writel(CR0_GUEST_HOST_MASK, ~vcpu->arch.cr0_guest_owned_bits); - if (vmx->nested.nested_run_pending && + if (vcpu->arch.nested_run_pending && (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_PAT)) { vmcs_write64(GUEST_IA32_PAT, vmcs12->guest_ia32_pat); vcpu->arch.pat = vmcs12->guest_ia32_pat; @@ -3349,7 +3349,7 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu, * to bit 8 (LME) if bit 31 in the CR0 field (corresponding to * CR0.PG) is 1. */ - if (to_vmx(vcpu)->nested.nested_run_pending && + if (vcpu->arch.nested_run_pending && (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_EFER)) { if (CC(!kvm_valid_efer(vcpu, vmcs12->guest_ia32_efer)) || CC(ia32e != !!(vmcs12->guest_ia32_efer & EFER_LMA)) || @@ -3627,15 +3627,15 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, kvm_service_local_tlb_flush_requests(vcpu); - if (!vmx->nested.nested_run_pending || + if (!vcpu->arch.nested_run_pending || !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_DEBUG_CONTROLS)) vmx->nested.pre_vmenter_debugctl = vmx_guest_debugctl_read(); if (kvm_mpx_supported() && - (!vmx->nested.nested_run_pending || + (!vcpu->arch.nested_run_pending || !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) vmx->nested.pre_vmenter_bndcfgs = vmcs_read64(GUEST_BNDCFGS); - if (!vmx->nested.nested_run_pending || + if (!vcpu->arch.nested_run_pending || !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE)) vmcs_read_cet_state(vcpu, &vmx->nested.pre_vmenter_s_cet, &vmx->nested.pre_vmenter_ssp, @@ -3844,7 +3844,7 @@ static int nested_vmx_run(struct kvm_vcpu *vcpu, bool launch) * We're finally done with prerequisite checking, and can start with * the nested entry. */ - vmx->nested.nested_run_pending = 1; + vcpu->arch.nested_run_pending = KVM_NESTED_RUN_PENDING; vmx->nested.has_preemption_timer_deadline = false; status = nested_vmx_enter_non_root_mode(vcpu, true); if (unlikely(status != NVMX_VMENTRY_SUCCESS)) @@ -3876,12 +3876,12 @@ static int nested_vmx_run(struct kvm_vcpu *vcpu, bool launch) !nested_cpu_has(vmcs12, CPU_BASED_NMI_WINDOW_EXITING) && !(nested_cpu_has(vmcs12, CPU_BASED_INTR_WINDOW_EXITING) && (vmcs12->guest_rflags & X86_EFLAGS_IF))) { - vmx->nested.nested_run_pending = 0; + vcpu->arch.nested_run_pending = 0; return kvm_emulate_halt_noskip(vcpu); } break; case GUEST_ACTIVITY_WAIT_SIPI: - vmx->nested.nested_run_pending = 0; + vcpu->arch.nested_run_pending = 0; kvm_set_mp_state(vcpu, KVM_MP_STATE_INIT_RECEIVED); break; default: @@ -3891,7 +3891,7 @@ static int nested_vmx_run(struct kvm_vcpu *vcpu, bool launch) return 1; vmentry_failed: - vmx->nested.nested_run_pending = 0; + vcpu->arch.nested_run_pending = 0; if (status == NVMX_VMENTRY_KVM_INTERNAL_ERROR) return 0; if (status == NVMX_VMENTRY_VMEXIT) @@ -4288,7 +4288,7 @@ static int vmx_check_nested_events(struct kvm_vcpu *vcpu) * previously injected event, the pending exception occurred while said * event was being delivered and thus needs to be handled. */ - bool block_nested_exceptions = vmx->nested.nested_run_pending; + bool block_nested_exceptions = vcpu->arch.nested_run_pending; /* * Events that don't require injection, i.e. that are virtualized by * hardware, aren't blocked by a pending VM-Enter as KVM doesn't need @@ -4657,7 +4657,7 @@ static void sync_vmcs02_to_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) if (nested_cpu_has_preemption_timer(vmcs12) && vmcs12->vm_exit_controls & VM_EXIT_SAVE_VMX_PREEMPTION_TIMER && - !vmx->nested.nested_run_pending) + !vcpu->arch.nested_run_pending) vmcs12->vmx_preemption_timer_value = vmx_get_preemption_timer_value(vcpu); @@ -5056,7 +5056,7 @@ void __nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason, vmx->nested.mtf_pending = false; /* trying to cancel vmlaunch/vmresume is a bug */ - WARN_ON_ONCE(vmx->nested.nested_run_pending); + kvm_warn_on_nested_run_pending(vcpu); #ifdef CONFIG_KVM_HYPERV if (kvm_check_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu)) { @@ -6679,7 +6679,7 @@ bool nested_vmx_reflect_vmexit(struct kvm_vcpu *vcpu) unsigned long exit_qual; u32 exit_intr_info; - WARN_ON_ONCE(vmx->nested.nested_run_pending); + kvm_warn_on_nested_run_pending(vcpu); /* * Late nested VM-Fail shares the same flow as nested VM-Exit since KVM @@ -6775,7 +6775,7 @@ static int vmx_get_nested_state(struct kvm_vcpu *vcpu, if (is_guest_mode(vcpu)) { kvm_state.flags |= KVM_STATE_NESTED_GUEST_MODE; - if (vmx->nested.nested_run_pending) + if (vcpu->arch.nested_run_pending) kvm_state.flags |= KVM_STATE_NESTED_RUN_PENDING; if (vmx->nested.mtf_pending) @@ -6850,7 +6850,7 @@ out: void vmx_leave_nested(struct kvm_vcpu *vcpu) { if (is_guest_mode(vcpu)) { - to_vmx(vcpu)->nested.nested_run_pending = 0; + vcpu->arch.nested_run_pending = 0; nested_vmx_vmexit(vcpu, -1, 0, 0); } free_nested(vcpu); @@ -7008,8 +7008,10 @@ static int vmx_set_nested_state(struct kvm_vcpu *vcpu, if (!(kvm_state->flags & KVM_STATE_NESTED_GUEST_MODE)) return 0; - vmx->nested.nested_run_pending = - !!(kvm_state->flags & KVM_STATE_NESTED_RUN_PENDING); + if (kvm_state->flags & KVM_STATE_NESTED_RUN_PENDING) + vcpu->arch.nested_run_pending = KVM_NESTED_RUN_PENDING_UNTRUSTED; + else + vcpu->arch.nested_run_pending = 0; vmx->nested.mtf_pending = !!(kvm_state->flags & KVM_STATE_NESTED_MTF_PENDING); @@ -7054,7 +7056,7 @@ static int vmx_set_nested_state(struct kvm_vcpu *vcpu, return 0; error_guest_mode: - vmx->nested.nested_run_pending = 0; + vcpu->arch.nested_run_pending = 0; return ret; } diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index eaeda1cfb227..04ce321ebdf3 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "capabilities.h" #include "mmu.h" #include "x86_ops.h" @@ -58,8 +59,6 @@ module_param_named(tdx, enable_tdx, bool, 0444); #define TDX_SHARED_BIT_PWL_5 gpa_to_gfn(BIT_ULL(51)) #define TDX_SHARED_BIT_PWL_4 gpa_to_gfn(BIT_ULL(47)) -static enum cpuhp_state tdx_cpuhp_state; - static const struct tdx_sys_info *tdx_sysinfo; void tdh_vp_rd_failed(struct vcpu_tdx *tdx, char *uclass, u32 field, u64 err) @@ -218,8 +217,6 @@ static int init_kvm_tdx_caps(const struct tdx_sys_info_td_conf *td_conf, */ static DEFINE_MUTEX(tdx_lock); -static atomic_t nr_configured_hkid; - static bool tdx_operand_busy(u64 err) { return (err & TDX_SEAMCALL_STATUS_MASK) == TDX_OPERAND_BUSY; @@ -267,7 +264,6 @@ static inline void tdx_hkid_free(struct kvm_tdx *kvm_tdx) { tdx_guest_keyid_free(kvm_tdx->hkid); kvm_tdx->hkid = -1; - atomic_dec(&nr_configured_hkid); misc_cg_uncharge(MISC_CG_RES_TDX, kvm_tdx->misc_cg, 1); put_misc_cg(kvm_tdx->misc_cg); kvm_tdx->misc_cg = NULL; @@ -1467,17 +1463,11 @@ static int tdx_emulate_mmio(struct kvm_vcpu *vcpu) /* Request the device emulation to userspace device model. */ vcpu->mmio_is_write = write; - if (!write) + + __kvm_prepare_emulated_mmio_exit(vcpu, gpa, size, &val, write); + + if (!write) { vcpu->arch.complete_userspace_io = tdx_complete_mmio_read; - - vcpu->run->mmio.phys_addr = gpa; - vcpu->run->mmio.len = size; - vcpu->run->mmio.is_write = write; - vcpu->run->exit_reason = KVM_EXIT_MMIO; - - if (write) { - memcpy(vcpu->run->mmio.data, &val, size); - } else { vcpu->mmio_fragments[0].gpa = gpa; vcpu->mmio_fragments[0].len = size; trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, size, gpa, NULL); @@ -1994,7 +1984,7 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) * TDX_SEAMCALL_VMFAILINVALID. */ if (unlikely((vp_enter_ret & TDX_SW_ERROR) == TDX_SW_ERROR)) { - KVM_BUG_ON(!kvm_rebooting, vcpu->kvm); + KVM_BUG_ON(!virt_rebooting, vcpu->kvm); goto unhandled_exit; } @@ -2397,8 +2387,6 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, ret = -ENOMEM; - atomic_inc(&nr_configured_hkid); - tdr_page = alloc_page(GFP_KERNEL); if (!tdr_page) goto free_hkid; @@ -3290,106 +3278,15 @@ int tdx_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private) return PG_LEVEL_4K; } -static int tdx_online_cpu(unsigned int cpu) +void tdx_hardware_unsetup(void) { - unsigned long flags; - int r; - - /* Sanity check CPU is already in post-VMXON */ - WARN_ON_ONCE(!(cr4_read_shadow() & X86_CR4_VMXE)); - - local_irq_save(flags); - r = tdx_cpu_enable(); - local_irq_restore(flags); - - return r; + misc_cg_set_capacity(MISC_CG_RES_TDX, 0); } -static int tdx_offline_cpu(unsigned int cpu) -{ - int i; - - /* No TD is running. Allow any cpu to be offline. */ - if (!atomic_read(&nr_configured_hkid)) - return 0; - - /* - * In order to reclaim TDX HKID, (i.e. when deleting guest TD), need to - * call TDH.PHYMEM.PAGE.WBINVD on all packages to program all memory - * controller with pconfig. If we have active TDX HKID, refuse to - * offline the last online cpu. - */ - for_each_online_cpu(i) { - /* - * Found another online cpu on the same package. - * Allow to offline. - */ - if (i != cpu && topology_physical_package_id(i) == - topology_physical_package_id(cpu)) - return 0; - } - - /* - * This is the last cpu of this package. Don't offline it. - * - * Because it's hard for human operator to understand the - * reason, warn it. - */ -#define MSG_ALLPKG_ONLINE \ - "TDX requires all packages to have an online CPU. Delete all TDs in order to offline all CPUs of a package.\n" - pr_warn_ratelimited(MSG_ALLPKG_ONLINE); - return -EBUSY; -} - -static void __do_tdx_cleanup(void) -{ - /* - * Once TDX module is initialized, it cannot be disabled and - * re-initialized again w/o runtime update (which isn't - * supported by kernel). Only need to remove the cpuhp here. - * The TDX host core code tracks TDX status and can handle - * 'multiple enabling' scenario. - */ - WARN_ON_ONCE(!tdx_cpuhp_state); - cpuhp_remove_state_nocalls_cpuslocked(tdx_cpuhp_state); - tdx_cpuhp_state = 0; -} - -static void __tdx_cleanup(void) -{ - cpus_read_lock(); - __do_tdx_cleanup(); - cpus_read_unlock(); -} - -static int __init __do_tdx_bringup(void) -{ - int r; - - /* - * TDX-specific cpuhp callback to call tdx_cpu_enable() on all - * online CPUs before calling tdx_enable(), and on any new - * going-online CPU to make sure it is ready for TDX guest. - */ - r = cpuhp_setup_state_cpuslocked(CPUHP_AP_ONLINE_DYN, - "kvm/cpu/tdx:online", - tdx_online_cpu, tdx_offline_cpu); - if (r < 0) - return r; - - tdx_cpuhp_state = r; - - r = tdx_enable(); - if (r) - __do_tdx_cleanup(); - - return r; -} - -static int __init __tdx_bringup(void) +static int __init __tdx_hardware_setup(void) { const struct tdx_sys_info_td_conf *td_conf; - int r, i; + int i; for (i = 0; i < ARRAY_SIZE(tdx_uret_msrs); i++) { /* @@ -3405,34 +3302,18 @@ static int __init __tdx_bringup(void) } } - /* - * Enabling TDX requires enabling hardware virtualization first, - * as making SEAMCALLs requires CPU being in post-VMXON state. - */ - r = kvm_enable_virtualization(); - if (r) - return r; - - cpus_read_lock(); - r = __do_tdx_bringup(); - cpus_read_unlock(); - - if (r) - goto tdx_bringup_err; - - r = -EINVAL; /* Get TDX global information for later use */ tdx_sysinfo = tdx_get_sysinfo(); - if (WARN_ON_ONCE(!tdx_sysinfo)) - goto get_sysinfo_err; + if (!tdx_sysinfo) + return -ENODEV; /* Check TDX module and KVM capabilities */ if (!tdx_get_supported_attrs(&tdx_sysinfo->td_conf) || !tdx_get_supported_xfam(&tdx_sysinfo->td_conf)) - goto get_sysinfo_err; + return -EINVAL; if (!(tdx_sysinfo->features.tdx_features0 & MD_FIELD_ID_FEATURES0_TOPOLOGY_ENUM)) - goto get_sysinfo_err; + return -EINVAL; /* * TDX has its own limit of maximum vCPUs it can support for all @@ -3467,35 +3348,16 @@ static int __init __tdx_bringup(void) if (td_conf->max_vcpus_per_td < num_present_cpus()) { pr_err("Disable TDX: MAX_VCPU_PER_TD (%u) smaller than number of logical CPUs (%u).\n", td_conf->max_vcpus_per_td, num_present_cpus()); - goto get_sysinfo_err; + return -EINVAL; } if (misc_cg_set_capacity(MISC_CG_RES_TDX, tdx_get_nr_guest_keyids())) - goto get_sysinfo_err; + return -EINVAL; - /* - * Leave hardware virtualization enabled after TDX is enabled - * successfully. TDX CPU hotplug depends on this. - */ return 0; - -get_sysinfo_err: - __tdx_cleanup(); -tdx_bringup_err: - kvm_disable_virtualization(); - return r; } -void tdx_cleanup(void) -{ - if (enable_tdx) { - misc_cg_set_capacity(MISC_CG_RES_TDX, 0); - __tdx_cleanup(); - kvm_disable_virtualization(); - } -} - -int __init tdx_bringup(void) +int __init tdx_hardware_setup(void) { int r, i; @@ -3526,40 +3388,12 @@ int __init tdx_bringup(void) goto success_disable_tdx; } - if (!cpu_feature_enabled(X86_FEATURE_MOVDIR64B)) { - pr_err("tdx: MOVDIR64B is required for TDX\n"); - goto success_disable_tdx; - } - - if (!cpu_feature_enabled(X86_FEATURE_SELFSNOOP)) { - pr_err("Self-snoop is required for TDX\n"); - goto success_disable_tdx; - } - if (!cpu_feature_enabled(X86_FEATURE_TDX_HOST_PLATFORM)) { - pr_err("tdx: no TDX private KeyIDs available\n"); + pr_err("TDX not supported by the host platform\n"); goto success_disable_tdx; } - if (!enable_virt_at_load) { - pr_err("tdx: tdx requires kvm.enable_virt_at_load=1\n"); - goto success_disable_tdx; - } - - /* - * Ideally KVM should probe whether TDX module has been loaded - * first and then try to bring it up. But TDX needs to use SEAMCALL - * to probe whether the module is loaded (there is no CPUID or MSR - * for that), and making SEAMCALL requires enabling virtualization - * first, just like the rest steps of bringing up TDX module. - * - * So, for simplicity do everything in __tdx_bringup(); the first - * SEAMCALL will return -ENODEV when the module is not loaded. The - * only complication is having to make sure that initialization - * SEAMCALLs don't return TDX_SEAMCALL_VMFAILINVALID in other - * cases. - */ - r = __tdx_bringup(); + r = __tdx_hardware_setup(); if (r) { /* * Disable TDX only but don't fail to load module if the TDX @@ -3574,24 +3408,11 @@ int __init tdx_bringup(void) if (r == -ENODEV) goto success_disable_tdx; - enable_tdx = 0; + return r; } - return r; - -success_disable_tdx: - enable_tdx = 0; - return 0; -} - -void __init tdx_hardware_setup(void) -{ KVM_SANITY_CHECK_VM_STRUCT_SIZE(kvm_tdx); - /* - * Note, if the TDX module can't be loaded, KVM TDX support will be - * disabled but KVM will continue loading (see tdx_bringup()). - */ vt_x86_ops.vm_size = max_t(unsigned int, vt_x86_ops.vm_size, sizeof(struct kvm_tdx)); vt_x86_ops.link_external_spt = tdx_sept_link_private_spt; @@ -3599,4 +3420,9 @@ void __init tdx_hardware_setup(void) vt_x86_ops.free_external_spt = tdx_sept_free_private_spt; vt_x86_ops.remove_external_spte = tdx_sept_remove_private_spte; vt_x86_ops.protected_apic_has_interrupt = tdx_protected_apic_has_interrupt; + return 0; + +success_disable_tdx: + enable_tdx = 0; + return 0; } diff --git a/arch/x86/kvm/vmx/tdx.h b/arch/x86/kvm/vmx/tdx.h index 45b5183ccb36..b5cd2ffb303e 100644 --- a/arch/x86/kvm/vmx/tdx.h +++ b/arch/x86/kvm/vmx/tdx.h @@ -8,9 +8,8 @@ #ifdef CONFIG_KVM_INTEL_TDX #include "common.h" -void tdx_hardware_setup(void); -int tdx_bringup(void); -void tdx_cleanup(void); +int tdx_hardware_setup(void); +void tdx_hardware_unsetup(void); extern bool enable_tdx; @@ -187,9 +186,6 @@ TDX_BUILD_TDVPS_ACCESSORS(8, MANAGEMENT, management); TDX_BUILD_TDVPS_ACCESSORS(64, STATE_NON_ARCH, state_non_arch); #else -static inline int tdx_bringup(void) { return 0; } -static inline void tdx_cleanup(void) {} - #define enable_tdx 0 struct kvm_tdx { diff --git a/arch/x86/kvm/vmx/vmcs.h b/arch/x86/kvm/vmx/vmcs.h index 66d747e265b1..1f16ddeae9cb 100644 --- a/arch/x86/kvm/vmx/vmcs.h +++ b/arch/x86/kvm/vmx/vmcs.h @@ -22,17 +22,6 @@ #define VMCS12_IDX_TO_ENC(idx) ROL16(idx, 10) #define ENC_TO_VMCS12_IDX(enc) ROL16(enc, 6) -struct vmcs_hdr { - u32 revision_id:31; - u32 shadow_vmcs:1; -}; - -struct vmcs { - struct vmcs_hdr hdr; - u32 abort; - char data[]; -}; - DECLARE_PER_CPU(struct vmcs *, current_vmcs); /* diff --git a/arch/x86/kvm/vmx/vmcs_shadow_fields.h b/arch/x86/kvm/vmx/vmcs_shadow_fields.h index cad128d1657b..67e821c2be6d 100644 --- a/arch/x86/kvm/vmx/vmcs_shadow_fields.h +++ b/arch/x86/kvm/vmx/vmcs_shadow_fields.h @@ -1,6 +1,6 @@ #if !defined(SHADOW_FIELD_RO) && !defined(SHADOW_FIELD_RW) -BUILD_BUG_ON(1) -#endif +#error Must #define at least one of SHADOW_FIELD_RO or SHADOW_FIELD_RW +#else #ifndef SHADOW_FIELD_RO #define SHADOW_FIELD_RO(x, y) @@ -74,6 +74,7 @@ SHADOW_FIELD_RW(HOST_GS_BASE, host_gs_base) /* 64-bit */ SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS, guest_physical_address) SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS_HIGH, guest_physical_address) +#endif #undef SHADOW_FIELD_RO #undef SHADOW_FIELD_RW diff --git a/arch/x86/kvm/vmx/vmenter.S b/arch/x86/kvm/vmx/vmenter.S index 4426d34811fc..8a481dae9cae 100644 --- a/arch/x86/kvm/vmx/vmenter.S +++ b/arch/x86/kvm/vmx/vmenter.S @@ -310,7 +310,7 @@ SYM_INNER_LABEL_ALIGN(vmx_vmexit, SYM_L_GLOBAL) RET .Lfixup: - cmpb $0, _ASM_RIP(kvm_rebooting) + cmpb $0, _ASM_RIP(virt_rebooting) jne .Lvmfail ud2 .Lvmfail: diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 8b24e682535b..49feecb286b2 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -107,6 +108,9 @@ module_param_named(unrestricted_guest, bool __read_mostly enable_ept_ad_bits = 1; module_param_named(eptad, enable_ept_ad_bits, bool, 0444); +bool __read_mostly enable_cet = 1; +module_param_named(cet, enable_cet, bool, 0444); + static bool __read_mostly emulate_invalid_guest_state = true; module_param(emulate_invalid_guest_state, bool, 0444); @@ -579,7 +583,6 @@ noinline void invept_error(unsigned long ext, u64 eptp) vmx_insn_failed("invept failed: ext=0x%lx eptp=%llx\n", ext, eptp); } -static DEFINE_PER_CPU(struct vmcs *, vmxarea); DEFINE_PER_CPU(struct vmcs *, current_vmcs); /* * We maintain a per-CPU linked-list of VMCS loaded on that CPU. This is needed @@ -786,53 +789,17 @@ static int vmx_set_guest_uret_msr(struct vcpu_vmx *vmx, return ret; } -/* - * Disable VMX and clear CR4.VMXE (even if VMXOFF faults) - * - * Note, VMXOFF causes a #UD if the CPU is !post-VMXON, but it's impossible to - * atomically track post-VMXON state, e.g. this may be called in NMI context. - * Eat all faults as all other faults on VMXOFF faults are mode related, i.e. - * faults are guaranteed to be due to the !post-VMXON check unless the CPU is - * magically in RM, VM86, compat mode, or at CPL>0. - */ -static int kvm_cpu_vmxoff(void) -{ - asm goto("1: vmxoff\n\t" - _ASM_EXTABLE(1b, %l[fault]) - ::: "cc", "memory" : fault); - - cr4_clear_bits(X86_CR4_VMXE); - return 0; - -fault: - cr4_clear_bits(X86_CR4_VMXE); - return -EIO; -} - void vmx_emergency_disable_virtualization_cpu(void) { int cpu = raw_smp_processor_id(); struct loaded_vmcs *v; - kvm_rebooting = true; - - /* - * Note, CR4.VMXE can be _cleared_ in NMI context, but it can only be - * set in task context. If this races with VMX is disabled by an NMI, - * VMCLEAR and VMXOFF may #UD, but KVM will eat those faults due to - * kvm_rebooting set. - */ - if (!(__read_cr4() & X86_CR4_VMXE)) - return; - list_for_each_entry(v, &per_cpu(loaded_vmcss_on_cpu, cpu), loaded_vmcss_on_cpu_link) { vmcs_clear(v->vmcs); if (v->shadow_vmcs) vmcs_clear(v->shadow_vmcs); } - - kvm_cpu_vmxoff(); } static void __loaded_vmcs_clear(void *arg) @@ -2927,12 +2894,16 @@ static bool __kvm_is_vmx_supported(void) return false; } - if (!this_cpu_has(X86_FEATURE_MSR_IA32_FEAT_CTL) || - !this_cpu_has(X86_FEATURE_VMX)) { + if (!this_cpu_has(X86_FEATURE_MSR_IA32_FEAT_CTL)) { pr_err("VMX not enabled (by BIOS) in MSR_IA32_FEAT_CTL on CPU %d\n", cpu); return false; } + if (!this_cpu_has(X86_FEATURE_VMX)) { + pr_err("VMX not fully enabled on CPU %d. Check kernel logs and/or BIOS\n", cpu); + return false; + } + return true; } @@ -2984,34 +2955,9 @@ int vmx_check_processor_compat(void) return 0; } -static int kvm_cpu_vmxon(u64 vmxon_pointer) -{ - u64 msr; - - cr4_set_bits(X86_CR4_VMXE); - - asm goto("1: vmxon %[vmxon_pointer]\n\t" - _ASM_EXTABLE(1b, %l[fault]) - : : [vmxon_pointer] "m"(vmxon_pointer) - : : fault); - return 0; - -fault: - WARN_ONCE(1, "VMXON faulted, MSR_IA32_FEAT_CTL (0x3a) = 0x%llx\n", - rdmsrq_safe(MSR_IA32_FEAT_CTL, &msr) ? 0xdeadbeef : msr); - cr4_clear_bits(X86_CR4_VMXE); - - return -EFAULT; -} - int vmx_enable_virtualization_cpu(void) { int cpu = raw_smp_processor_id(); - u64 phys_addr = __pa(per_cpu(vmxarea, cpu)); - int r; - - if (cr4_read_shadow() & X86_CR4_VMXE) - return -EBUSY; /* * This can happen if we hot-added a CPU but failed to allocate @@ -3020,15 +2966,7 @@ int vmx_enable_virtualization_cpu(void) if (kvm_is_using_evmcs() && !hv_get_vp_assist_page(cpu)) return -EFAULT; - intel_pt_handle_vmx(1); - - r = kvm_cpu_vmxon(phys_addr); - if (r) { - intel_pt_handle_vmx(0); - return r; - } - - return 0; + return x86_virt_get_ref(X86_FEATURE_VMX); } static void vmclear_local_loaded_vmcss(void) @@ -3045,12 +2983,9 @@ void vmx_disable_virtualization_cpu(void) { vmclear_local_loaded_vmcss(); - if (kvm_cpu_vmxoff()) - kvm_spurious_fault(); + x86_virt_put_ref(X86_FEATURE_VMX); hv_reset_evmcs(); - - intel_pt_handle_vmx(0); } struct vmcs *alloc_vmcs_cpu(bool shadow, int cpu, gfp_t flags) @@ -3128,47 +3063,6 @@ out_vmcs: return -ENOMEM; } -static void free_kvm_area(void) -{ - int cpu; - - for_each_possible_cpu(cpu) { - free_vmcs(per_cpu(vmxarea, cpu)); - per_cpu(vmxarea, cpu) = NULL; - } -} - -static __init int alloc_kvm_area(void) -{ - int cpu; - - for_each_possible_cpu(cpu) { - struct vmcs *vmcs; - - vmcs = alloc_vmcs_cpu(false, cpu, GFP_KERNEL); - if (!vmcs) { - free_kvm_area(); - return -ENOMEM; - } - - /* - * When eVMCS is enabled, alloc_vmcs_cpu() sets - * vmcs->revision_id to KVM_EVMCS_VERSION instead of - * revision_id reported by MSR_IA32_VMX_BASIC. - * - * However, even though not explicitly documented by - * TLFS, VMXArea passed as VMXON argument should - * still be marked with revision_id reported by - * physical CPU. - */ - if (kvm_is_using_evmcs()) - vmcs->hdr.revision_id = vmx_basic_vmcs_revision_id(vmcs_config.basic); - - per_cpu(vmxarea, cpu) = vmcs; - } - return 0; -} - static void fix_pmode_seg(struct kvm_vcpu *vcpu, int seg, struct kvm_segment *save) { @@ -4585,7 +4479,7 @@ void vmx_set_constant_host_state(struct vcpu_vmx *vmx) * SSP is reloaded from IA32_PL3_SSP. Check SDM Vol.2A/B Chapter * 3 and 4 for details. */ - if (cpu_has_load_cet_ctrl()) { + if (enable_cet) { vmcs_writel(HOST_S_CET, kvm_host.s_cet); vmcs_writel(HOST_SSP, 0); vmcs_writel(HOST_INTR_SSP_TABLE, 0); @@ -4641,6 +4535,10 @@ static u32 vmx_get_initial_vmentry_ctrl(void) if (vmx_pt_mode_is_system()) vmentry_ctrl &= ~(VM_ENTRY_PT_CONCEAL_PIP | VM_ENTRY_LOAD_IA32_RTIT_CTL); + + if (!enable_cet) + vmentry_ctrl &= ~VM_ENTRY_LOAD_CET_STATE; + /* * IA32e mode, and loading of EFER and PERF_GLOBAL_CTRL are toggled dynamically. */ @@ -4655,6 +4553,9 @@ static u32 vmx_get_initial_vmexit_ctrl(void) { u32 vmexit_ctrl = vmcs_config.vmexit_ctrl; + if (!enable_cet) + vmexit_ctrl &= ~VM_EXIT_LOAD_CET_STATE; + /* * Not used by KVM and never set in vmcs01 or vmcs02, but emulated for * nested virtualization and thus allowed to be set in vmcs12. @@ -5279,7 +5180,7 @@ bool vmx_nmi_blocked(struct kvm_vcpu *vcpu) int vmx_nmi_allowed(struct kvm_vcpu *vcpu, bool for_injection) { - if (to_vmx(vcpu)->nested.nested_run_pending) + if (vcpu->arch.nested_run_pending) return -EBUSY; /* An NMI must not be injected into L2 if it's supposed to VM-Exit. */ @@ -5306,7 +5207,7 @@ bool vmx_interrupt_blocked(struct kvm_vcpu *vcpu) int vmx_interrupt_allowed(struct kvm_vcpu *vcpu, bool for_injection) { - if (to_vmx(vcpu)->nested.nested_run_pending) + if (vcpu->arch.nested_run_pending) return -EBUSY; /* @@ -6118,7 +6019,7 @@ static bool vmx_unhandleable_emulation_required(struct kvm_vcpu *vcpu) * only reachable if userspace modifies L2 guest state after KVM has * performed the nested VM-Enter consistency checks. */ - if (vmx->nested.nested_run_pending) + if (vcpu->arch.nested_run_pending) return true; /* @@ -6802,7 +6703,7 @@ static int __vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) * invalid guest state should never happen as that means KVM knowingly * allowed a nested VM-Enter with an invalid vmcs12. More below. */ - if (KVM_BUG_ON(vmx->nested.nested_run_pending, vcpu->kvm)) + if (KVM_BUG_ON(vcpu->arch.nested_run_pending, vcpu->kvm)) return -EIO; if (is_guest_mode(vcpu)) { @@ -7138,8 +7039,8 @@ static void vmx_set_rvi(int vector) int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu) { struct vcpu_vt *vt = to_vt(vcpu); + bool max_irr_is_from_pir; int max_irr; - bool got_posted_interrupt; if (KVM_BUG_ON(!enable_apicv, vcpu->kvm)) return -EIO; @@ -7151,17 +7052,22 @@ int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu) * But on x86 this is just a compiler barrier anyway. */ smp_mb__after_atomic(); - got_posted_interrupt = - kvm_apic_update_irr(vcpu, vt->pi_desc.pir, &max_irr); + max_irr_is_from_pir = kvm_apic_update_irr(vcpu, vt->pi_desc.pir, + &max_irr); } else { max_irr = kvm_lapic_find_highest_irr(vcpu); - got_posted_interrupt = false; + max_irr_is_from_pir = false; } /* - * Newly recognized interrupts are injected via either virtual interrupt - * delivery (RVI) or KVM_REQ_EVENT. Virtual interrupt delivery is - * disabled in two cases: + * If APICv is enabled and L2 is not active, then update the Requesting + * Virtual Interrupt (RVI) portion of vmcs01.GUEST_INTR_STATUS with the + * highest priority IRR to deliver the IRQ via Virtual Interrupt + * Delivery. Note, this is required even if the highest priority IRQ + * was already pending in the IRR, as RVI isn't updated in lockstep with + * the IRR (unlike apic->irr_pending). + * + * For the cases where Virtual Interrupt Delivery can't be used: * * 1) If L2 is running and the vCPU has a new pending interrupt. If L1 * wants to exit on interrupts, KVM_REQ_EVENT is needed to synthesize a @@ -7172,10 +7078,29 @@ int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu) * 2) If APICv is disabled for this vCPU, assigned devices may still * attempt to post interrupts. The posted interrupt vector will cause * a VM-Exit and the subsequent entry will call sync_pir_to_irr. + * + * In both cases, set KVM_REQ_EVENT if and only if the highest priority + * pending IRQ came from the PIR, as setting KVM_REQ_EVENT if any IRQ + * is pending may put the vCPU into an infinite loop, e.g. if the IRQ + * is blocked, then it will stay pending until an IRQ window is opened. + * + * Note! It's possible that one or more IRQs were moved from the PIR + * to the IRR _without_ max_irr_is_from_pir being true! I.e. if there + * was a higher priority IRQ already pending in the IRR. Not setting + * KVM_REQ_EVENT in this case is intentional and safe. If APICv is + * inactive, or L2 is running with exit-on-interrupt off (in vmcs12), + * i.e. without nested virtual interrupt delivery, then there's no need + * to request an IRQ window as the lower priority IRQ only needs to be + * delivered when the higher priority IRQ is dismissed from the ISR, + * i.e. on the next EOI, and EOIs are always intercepted if APICv is + * disabled or if L2 is running without nested VID. If L2 is running + * exit-on-interrupt on (in vmcs12), then the higher priority IRQ will + * trigger a nested VM-Exit, at which point KVM will re-evaluate L1's + * pending IRQs. */ if (!is_guest_mode(vcpu) && kvm_vcpu_apicv_active(vcpu)) vmx_set_rvi(max_irr); - else if (got_posted_interrupt) + else if (max_irr_is_from_pir) kvm_make_request(KVM_REQ_EVENT, vcpu); return max_irr; @@ -7730,11 +7655,11 @@ fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) * Track VMLAUNCH/VMRESUME that have made past guest state * checking. */ - if (vmx->nested.nested_run_pending && + if (vcpu->arch.nested_run_pending && !vmx_get_exit_reason(vcpu).failed_vmentry) ++vcpu->stat.nested_run; - vmx->nested.nested_run_pending = 0; + vcpu->arch.nested_run_pending = 0; } if (unlikely(vmx->fail)) @@ -8240,7 +8165,7 @@ static __init void vmx_set_cpu_caps(void) * VMX_BASIC[bit56] == 0, inject #CP at VMX entry with error code * fails, so disable CET in this case too. */ - if (!cpu_has_load_cet_ctrl() || !enable_unrestricted_guest || + if (!enable_cet || !enable_unrestricted_guest || !cpu_has_vmx_basic_no_hw_errcode_cc()) { kvm_cpu_cap_clear(X86_FEATURE_SHSTK); kvm_cpu_cap_clear(X86_FEATURE_IBT); @@ -8491,7 +8416,7 @@ void vmx_setup_mce(struct kvm_vcpu *vcpu) int vmx_smi_allowed(struct kvm_vcpu *vcpu, bool for_injection) { /* we need a nested vmexit to enter SMM, postpone if run is pending */ - if (to_vmx(vcpu)->nested.nested_run_pending) + if (vcpu->arch.nested_run_pending) return -EBUSY; return !is_smm(vcpu); } @@ -8536,7 +8461,7 @@ int vmx_leave_smm(struct kvm_vcpu *vcpu, const union kvm_smram *smram) if (ret != NVMX_VMENTRY_SUCCESS) return 1; - vmx->nested.nested_run_pending = 1; + vcpu->arch.nested_run_pending = KVM_NESTED_RUN_PENDING; vmx->nested.smm.guest_mode = false; } return 0; @@ -8569,8 +8494,6 @@ void vmx_hardware_unsetup(void) if (nested) nested_vmx_hardware_unsetup(); - - free_kvm_area(); } void vmx_vm_destroy(struct kvm *kvm) @@ -8698,10 +8621,6 @@ __init int vmx_hardware_setup(void) vmx_setup_user_return_msrs(); - - if (boot_cpu_has(X86_FEATURE_NX)) - kvm_enable_efer_bits(EFER_NX); - if (boot_cpu_has(X86_FEATURE_MPX)) { rdmsrq(MSR_IA32_BNDCFGS, host_bndcfgs); WARN_ONCE(host_bndcfgs, "BNDCFGS in host will be lost"); @@ -8721,6 +8640,9 @@ __init int vmx_hardware_setup(void) !cpu_has_vmx_invept_global()) enable_ept = 0; + if (!cpu_has_load_cet_ctrl()) + enable_cet = 0; + /* NX support is required for shadow paging. */ if (!enable_ept && !boot_cpu_has(X86_FEATURE_NX)) { pr_err_ratelimited("NX (Execute Disable) not supported\n"); @@ -8873,10 +8795,6 @@ __init int vmx_hardware_setup(void) return r; } - r = alloc_kvm_area(); - if (r) - goto err_kvm_area; - kvm_set_posted_intr_wakeup_handler(pi_wakeup_handler); /* @@ -8903,11 +8821,6 @@ __init int vmx_hardware_setup(void) kvm_caps.inapplicable_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT; return 0; - -err_kvm_area: - if (nested) - nested_vmx_hardware_unsetup(); - return r; } void vmx_exit(void) diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 70bfe81dea54..db84e8001da5 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -138,9 +138,6 @@ struct nested_vmx { */ bool enlightened_vmcs_enabled; - /* L2 must run next, and mustn't decide to exit to L1. */ - bool nested_run_pending; - /* Pending MTF VM-exit into L1. */ bool mtf_pending; diff --git a/arch/x86/kvm/vmx/vmx_ops.h b/arch/x86/kvm/vmx/vmx_ops.h index 96677576c836..81784befaaf4 100644 --- a/arch/x86/kvm/vmx/vmx_ops.h +++ b/arch/x86/kvm/vmx/vmx_ops.h @@ -119,7 +119,6 @@ do_exception: #else /* !CONFIG_CC_HAS_ASM_GOTO_OUTPUT */ asm volatile("1: vmread %[field], %[output]\n\t" - ".byte 0x3e\n\t" /* branch taken hint */ "ja 3f\n\t" /* @@ -191,7 +190,6 @@ static __always_inline unsigned long vmcs_readl(unsigned long field) #define vmx_asm1(insn, op1, error_args...) \ do { \ asm goto("1: " __stringify(insn) " %0\n\t" \ - ".byte 0x2e\n\t" /* branch not taken hint */ \ "jna %l[error]\n\t" \ _ASM_EXTABLE(1b, %l[fault]) \ : : op1 : "cc" : error, fault); \ @@ -208,7 +206,6 @@ fault: \ #define vmx_asm2(insn, op1, op2, error_args...) \ do { \ asm goto("1: " __stringify(insn) " %1, %0\n\t" \ - ".byte 0x2e\n\t" /* branch not taken hint */ \ "jna %l[error]\n\t" \ _ASM_EXTABLE(1b, %l[fault]) \ : : op1, op2 : "cc" : error, fault); \ @@ -224,7 +221,7 @@ fault: \ static __always_inline void __vmcs_writel(unsigned long field, unsigned long value) { - vmx_asm2(vmwrite, "r"(field), "rm"(value), field, value); + vmx_asm2(vmwrite, "r" (field), ASM_INPUT_RM (value), field, value); } static __always_inline void vmcs_write16(unsigned long field, u16 value) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index fd1c4a36b593..0a1b63c63d1a 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -83,6 +83,8 @@ #include #include #include +#include + #include #define CREATE_TRACE_POINTS @@ -351,6 +353,9 @@ static const u32 msrs_to_save_base[] = { MSR_IA32_U_CET, MSR_IA32_S_CET, MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP, MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB, + MSR_IA32_DEBUGCTLMSR, + MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP, + MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP, }; static const u32 msrs_to_save_pmu[] = { @@ -710,7 +715,7 @@ static void drop_user_return_notifiers(void) noinstr void kvm_spurious_fault(void) { /* Fault while not rebooting. We want the trace. */ - BUG_ON(!kvm_rebooting); + BUG_ON(!virt_rebooting); } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_spurious_fault); @@ -864,9 +869,6 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr, vcpu->arch.exception.error_code = error_code; vcpu->arch.exception.has_payload = has_payload; vcpu->arch.exception.payload = payload; - if (!is_guest_mode(vcpu)) - kvm_deliver_exception_payload(vcpu, - &vcpu->arch.exception); return; } @@ -5531,18 +5533,8 @@ static int kvm_vcpu_ioctl_x86_set_mce(struct kvm_vcpu *vcpu, return 0; } -static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, - struct kvm_vcpu_events *events) +static struct kvm_queued_exception *kvm_get_exception_to_save(struct kvm_vcpu *vcpu) { - struct kvm_queued_exception *ex; - - process_nmi(vcpu); - -#ifdef CONFIG_KVM_SMM - if (kvm_check_request(KVM_REQ_SMI, vcpu)) - process_smi(vcpu); -#endif - /* * KVM's ABI only allows for one exception to be migrated. Luckily, * the only time there can be two queued exceptions is if there's a @@ -5553,21 +5545,46 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, if (vcpu->arch.exception_vmexit.pending && !vcpu->arch.exception.pending && !vcpu->arch.exception.injected) - ex = &vcpu->arch.exception_vmexit; - else - ex = &vcpu->arch.exception; + return &vcpu->arch.exception_vmexit; + + return &vcpu->arch.exception; +} + +static void kvm_handle_exception_payload_quirk(struct kvm_vcpu *vcpu) +{ + struct kvm_queued_exception *ex = kvm_get_exception_to_save(vcpu); /* - * In guest mode, payload delivery should be deferred if the exception - * will be intercepted by L1, e.g. KVM should not modifying CR2 if L1 - * intercepts #PF, ditto for DR6 and #DBs. If the per-VM capability, - * KVM_CAP_EXCEPTION_PAYLOAD, is not set, userspace may or may not - * propagate the payload and so it cannot be safely deferred. Deliver - * the payload if the capability hasn't been requested. + * If KVM_CAP_EXCEPTION_PAYLOAD is disabled, then (prematurely) deliver + * the pending exception payload when userspace saves *any* vCPU state + * that interacts with exception payloads to avoid breaking userspace. + * + * Architecturally, KVM must not deliver an exception payload until the + * exception is actually injected, e.g. to avoid losing pending #DB + * information (which VMX tracks in the VMCS), and to avoid clobbering + * state if the exception is never injected for whatever reason. But + * if KVM_CAP_EXCEPTION_PAYLOAD isn't enabled, then userspace may or + * may not propagate the payload across save+restore, and so KVM can't + * safely defer delivery of the payload. */ if (!vcpu->kvm->arch.exception_payload_enabled && ex->pending && ex->has_payload) kvm_deliver_exception_payload(vcpu, ex); +} + +static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, + struct kvm_vcpu_events *events) +{ + struct kvm_queued_exception *ex = kvm_get_exception_to_save(vcpu); + + process_nmi(vcpu); + +#ifdef CONFIG_KVM_SMM + if (kvm_check_request(KVM_REQ_SMI, vcpu)) + process_smi(vcpu); +#endif + + kvm_handle_exception_payload_quirk(vcpu); memset(events, 0, sizeof(*events)); @@ -5746,6 +5763,8 @@ static int kvm_vcpu_ioctl_x86_get_debugregs(struct kvm_vcpu *vcpu, vcpu->arch.guest_state_protected) return -EINVAL; + kvm_handle_exception_payload_quirk(vcpu); + memset(dbgregs, 0, sizeof(*dbgregs)); BUILD_BUG_ON(ARRAY_SIZE(vcpu->arch.db) != ARRAY_SIZE(dbgregs->db)); @@ -7768,11 +7787,14 @@ static void kvm_init_msr_lists(void) } static int vcpu_mmio_write(struct kvm_vcpu *vcpu, gpa_t addr, int len, - const void *v) + void *__v) { + const void *v = __v; int handled = 0; int n; + trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, addr, __v); + do { n = min(len, 8); if (!(lapic_in_kernel(vcpu) && @@ -7807,6 +7829,9 @@ static int vcpu_mmio_read(struct kvm_vcpu *vcpu, gpa_t addr, int len, void *v) v += n; } while (len); + if (len) + trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, len, addr, NULL); + return handled; } @@ -8095,8 +8120,22 @@ static int vcpu_mmio_gva_to_gpa(struct kvm_vcpu *vcpu, unsigned long gva, return vcpu_is_mmio_gpa(vcpu, gva, *gpa, write); } -int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, - const void *val, int bytes) +struct read_write_emulator_ops { + int (*read_write_guest)(struct kvm_vcpu *vcpu, gpa_t gpa, + void *val, int bytes); + int (*read_write_mmio)(struct kvm_vcpu *vcpu, gpa_t gpa, + int bytes, void *val); + bool write; +}; + +static int emulator_read_guest(struct kvm_vcpu *vcpu, gpa_t gpa, + void *val, int bytes) +{ + return !kvm_vcpu_read_guest(vcpu, gpa, val, bytes); +} + +static int emulator_write_guest(struct kvm_vcpu *vcpu, gpa_t gpa, + void *val, int bytes) { int ret; @@ -8107,78 +8146,6 @@ int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, return 1; } -struct read_write_emulator_ops { - int (*read_write_prepare)(struct kvm_vcpu *vcpu, void *val, - int bytes); - int (*read_write_emulate)(struct kvm_vcpu *vcpu, gpa_t gpa, - void *val, int bytes); - int (*read_write_mmio)(struct kvm_vcpu *vcpu, gpa_t gpa, - int bytes, void *val); - int (*read_write_exit_mmio)(struct kvm_vcpu *vcpu, gpa_t gpa, - void *val, int bytes); - bool write; -}; - -static int read_prepare(struct kvm_vcpu *vcpu, void *val, int bytes) -{ - if (vcpu->mmio_read_completed) { - trace_kvm_mmio(KVM_TRACE_MMIO_READ, bytes, - vcpu->mmio_fragments[0].gpa, val); - vcpu->mmio_read_completed = 0; - return 1; - } - - return 0; -} - -static int read_emulate(struct kvm_vcpu *vcpu, gpa_t gpa, - void *val, int bytes) -{ - return !kvm_vcpu_read_guest(vcpu, gpa, val, bytes); -} - -static int write_emulate(struct kvm_vcpu *vcpu, gpa_t gpa, - void *val, int bytes) -{ - return emulator_write_phys(vcpu, gpa, val, bytes); -} - -static int write_mmio(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes, void *val) -{ - trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, val); - return vcpu_mmio_write(vcpu, gpa, bytes, val); -} - -static int read_exit_mmio(struct kvm_vcpu *vcpu, gpa_t gpa, - void *val, int bytes) -{ - trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, bytes, gpa, NULL); - return X86EMUL_IO_NEEDED; -} - -static int write_exit_mmio(struct kvm_vcpu *vcpu, gpa_t gpa, - void *val, int bytes) -{ - struct kvm_mmio_fragment *frag = &vcpu->mmio_fragments[0]; - - memcpy(vcpu->run->mmio.data, frag->data, min(8u, frag->len)); - return X86EMUL_CONTINUE; -} - -static const struct read_write_emulator_ops read_emultor = { - .read_write_prepare = read_prepare, - .read_write_emulate = read_emulate, - .read_write_mmio = vcpu_mmio_read, - .read_write_exit_mmio = read_exit_mmio, -}; - -static const struct read_write_emulator_ops write_emultor = { - .read_write_emulate = write_emulate, - .read_write_mmio = write_mmio, - .read_write_exit_mmio = write_exit_mmio, - .write = true, -}; - static int emulator_read_write_onepage(unsigned long addr, void *val, unsigned int bytes, struct x86_exception *exception, @@ -8208,11 +8175,22 @@ static int emulator_read_write_onepage(unsigned long addr, void *val, return X86EMUL_PROPAGATE_FAULT; } - if (!ret && ops->read_write_emulate(vcpu, gpa, val, bytes)) + /* + * If the memory is not _known_ to be emulated MMIO, attempt to access + * guest memory. If accessing guest memory fails, e.g. because there's + * no memslot, then handle the access as MMIO. Note, treating the + * access as emulated MMIO is technically wrong if there is a memslot, + * i.e. if accessing host user memory failed, but this has been KVM's + * historical ABI for decades. + */ + if (!ret && ops->read_write_guest(vcpu, gpa, val, bytes)) return X86EMUL_CONTINUE; /* - * Is this MMIO handled locally? + * Attempt to handle emulated MMIO within the kernel, e.g. for accesses + * to an in-kernel local or I/O APIC, or to an ioeventfd range attached + * to MMIO bus. If the access isn't fully resolved, insert an MMIO + * fragment with the relevant details. */ handled = ops->read_write_mmio(vcpu, gpa, bytes, val); if (handled == bytes) @@ -8225,8 +8203,21 @@ static int emulator_read_write_onepage(unsigned long addr, void *val, WARN_ON(vcpu->mmio_nr_fragments >= KVM_MAX_MMIO_FRAGMENTS); frag = &vcpu->mmio_fragments[vcpu->mmio_nr_fragments++]; frag->gpa = gpa; - frag->data = val; + if (write && bytes <= 8u) { + frag->val = 0; + frag->data = &frag->val; + memcpy(&frag->val, val, bytes); + } else { + frag->data = val; + } frag->len = bytes; + + /* + * Continue emulating, even though KVM needs to (eventually) do an MMIO + * exit to userspace. If the access splits multiple pages, then KVM + * needs to exit to userspace only after emulating both parts of the + * access. + */ return X86EMUL_CONTINUE; } @@ -8237,12 +8228,33 @@ static int emulator_read_write(struct x86_emulate_ctxt *ctxt, const struct read_write_emulator_ops *ops) { struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); - gpa_t gpa; int rc; - if (ops->read_write_prepare && - ops->read_write_prepare(vcpu, val, bytes)) + if (WARN_ON_ONCE((bytes > 8u || !ops->write) && object_is_on_stack(val))) + return X86EMUL_UNHANDLEABLE; + + /* + * If the read was already completed via a userspace MMIO exit, there's + * nothing left to do except trace the MMIO read. When completing MMIO + * reads, KVM re-emulates the instruction to propagate the value into + * the correct destination, e.g. into the correct register, but the + * value itself has already been copied to the read cache. + * + * Note! This is *tightly* coupled to read_emulated() satisfying reads + * from the emulator's mem_read cache, so that the MMIO fragment data + * is copied to the correct chunk of the correct operand. + */ + if (!ops->write && vcpu->mmio_read_completed) { + /* + * For simplicity, trace the entire MMIO read in one shot, even + * though the GPA might be incorrect if there are two fragments + * that aren't contiguous in the GPA space. + */ + trace_kvm_mmio(KVM_TRACE_MMIO_READ, bytes, + vcpu->mmio_fragments[0].gpa, val); + vcpu->mmio_read_completed = 0; return X86EMUL_CONTINUE; + } vcpu->mmio_nr_fragments = 0; @@ -8271,17 +8283,21 @@ static int emulator_read_write(struct x86_emulate_ctxt *ctxt, if (!vcpu->mmio_nr_fragments) return X86EMUL_CONTINUE; - gpa = vcpu->mmio_fragments[0].gpa; - vcpu->mmio_needed = 1; vcpu->mmio_cur_fragment = 0; + vcpu->mmio_is_write = ops->write; - vcpu->run->mmio.len = min(8u, vcpu->mmio_fragments[0].len); - vcpu->run->mmio.is_write = vcpu->mmio_is_write = ops->write; - vcpu->run->exit_reason = KVM_EXIT_MMIO; - vcpu->run->mmio.phys_addr = gpa; + kvm_prepare_emulated_mmio_exit(vcpu, &vcpu->mmio_fragments[0]); - return ops->read_write_exit_mmio(vcpu, gpa, val, bytes); + /* + * For MMIO reads, stop emulating and immediately exit to userspace, as + * KVM needs the value to correctly emulate the instruction. For MMIO + * writes, continue emulating as the write to MMIO is a side effect for + * all intents and purposes. KVM will still exit to userspace, but + * after completing emulation (see the check on vcpu->mmio_needed in + * x86_emulate_instruction()). + */ + return ops->write ? X86EMUL_CONTINUE : X86EMUL_IO_NEEDED; } static int emulator_read_emulated(struct x86_emulate_ctxt *ctxt, @@ -8290,8 +8306,13 @@ static int emulator_read_emulated(struct x86_emulate_ctxt *ctxt, unsigned int bytes, struct x86_exception *exception) { - return emulator_read_write(ctxt, addr, val, bytes, - exception, &read_emultor); + static const struct read_write_emulator_ops ops = { + .read_write_guest = emulator_read_guest, + .read_write_mmio = vcpu_mmio_read, + .write = false, + }; + + return emulator_read_write(ctxt, addr, val, bytes, exception, &ops); } static int emulator_write_emulated(struct x86_emulate_ctxt *ctxt, @@ -8300,8 +8321,13 @@ static int emulator_write_emulated(struct x86_emulate_ctxt *ctxt, unsigned int bytes, struct x86_exception *exception) { - return emulator_read_write(ctxt, addr, (void *)val, bytes, - exception, &write_emultor); + static const struct read_write_emulator_ops ops = { + .read_write_guest = emulator_write_guest, + .read_write_mmio = vcpu_mmio_write, + .write = true, + }; + + return emulator_read_write(ctxt, addr, (void *)val, bytes, exception, &ops); } #define emulator_try_cmpxchg_user(t, ptr, old, new) \ @@ -8890,6 +8916,11 @@ static bool emulator_is_canonical_addr(struct x86_emulate_ctxt *ctxt, return !is_noncanonical_address(addr, emul_to_vcpu(ctxt), flags); } +static bool emulator_page_address_valid(struct x86_emulate_ctxt *ctxt, gpa_t gpa) +{ + return page_address_valid(emul_to_vcpu(ctxt), gpa); +} + static const struct x86_emulate_ops emulate_ops = { .vm_bugged = emulator_vm_bugged, .read_gpr = emulator_read_gpr, @@ -8937,6 +8968,7 @@ static const struct x86_emulate_ops emulate_ops = { .set_xcr = emulator_set_xcr, .get_untagged_addr = emulator_get_untagged_addr, .is_canonical_addr = emulator_is_canonical_addr, + .page_address_valid = emulator_page_address_valid, }; static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask) @@ -9694,7 +9726,8 @@ static int complete_fast_pio_in(struct kvm_vcpu *vcpu) unsigned long val; /* We should only ever be called with arch.pio.count equal to 1 */ - BUG_ON(vcpu->arch.pio.count != 1); + if (KVM_BUG_ON(vcpu->arch.pio.count != 1, vcpu->kvm)) + return -EIO; if (unlikely(!kvm_is_linear_rip(vcpu, vcpu->arch.cui_linear_rip))) { vcpu->arch.pio.count = 0; @@ -9998,6 +10031,18 @@ void kvm_setup_xss_caps(void) } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_setup_xss_caps); +static void kvm_setup_efer_caps(void) +{ + if (kvm_cpu_cap_has(X86_FEATURE_NX)) + kvm_enable_efer_bits(EFER_NX); + + if (kvm_cpu_cap_has(X86_FEATURE_FXSR_OPT)) + kvm_enable_efer_bits(EFER_FFXSR); + + if (kvm_cpu_cap_has(X86_FEATURE_AUTOIBRS)) + kvm_enable_efer_bits(EFER_AUTOIBRS); +} + static inline void kvm_ops_update(struct kvm_x86_init_ops *ops) { memcpy(&kvm_x86_ops, ops->runtime_ops, sizeof(kvm_x86_ops)); @@ -10134,6 +10179,8 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) if (r != 0) goto out_mmu_exit; + kvm_setup_efer_caps(); + enable_device_posted_irqs &= enable_apicv && irq_remapping_cap(IRQ_POSTING_CAP); @@ -10736,12 +10783,10 @@ static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu, __kvm_set_rflags(vcpu, kvm_get_rflags(vcpu) | X86_EFLAGS_RF); - if (vcpu->arch.exception.vector == DB_VECTOR) { - kvm_deliver_exception_payload(vcpu, &vcpu->arch.exception); - if (vcpu->arch.dr7 & DR7_GD) { - vcpu->arch.dr7 &= ~DR7_GD; - kvm_update_dr7(vcpu); - } + if (vcpu->arch.exception.vector == DB_VECTOR && + vcpu->arch.dr7 & DR7_GD) { + vcpu->arch.dr7 &= ~DR7_GD; + kvm_update_dr7(vcpu); } kvm_inject_exception(vcpu); @@ -10973,7 +11018,11 @@ void __kvm_set_or_clear_apicv_inhibit(struct kvm *kvm, old = new = kvm->arch.apicv_inhibit_reasons; - set_or_clear_apicv_inhibit(&new, reason, set); + if (reason != APICV_INHIBIT_REASON_IRQWIN) + set_or_clear_apicv_inhibit(&new, reason, set); + + set_or_clear_apicv_inhibit(&new, APICV_INHIBIT_REASON_IRQWIN, + atomic_read(&kvm->arch.apicv_nr_irq_window_req)); if (!!old != !!new) { /* @@ -11014,6 +11063,45 @@ void kvm_set_or_clear_apicv_inhibit(struct kvm *kvm, } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_set_or_clear_apicv_inhibit); +void kvm_inc_or_dec_irq_window_inhibit(struct kvm *kvm, bool inc) +{ + int add = inc ? 1 : -1; + + if (!enable_apicv) + return; + + /* + * IRQ windows are requested either because of ExtINT injections, or + * because APICv is already disabled/inhibited for another reason. + * While ExtINT injections are rare and should not happen while the + * vCPU is running its actual workload, it's worth avoiding thrashing + * if the IRQ window is being requested because APICv is already + * inhibited. So, toggle the actual inhibit (which requires taking + * the lock for write) if and only if there's no other inhibit. + * kvm_set_or_clear_apicv_inhibit() always evaluates the IRQ window + * count; thus the IRQ window inhibit call _will_ be lazily updated on + * the next call, if it ever happens. + */ + if (READ_ONCE(kvm->arch.apicv_inhibit_reasons) & ~BIT(APICV_INHIBIT_REASON_IRQWIN)) { + guard(rwsem_read)(&kvm->arch.apicv_update_lock); + if (READ_ONCE(kvm->arch.apicv_inhibit_reasons) & ~BIT(APICV_INHIBIT_REASON_IRQWIN)) { + atomic_add(add, &kvm->arch.apicv_nr_irq_window_req); + return; + } + } + + /* + * Strictly speaking, the lock is only needed if going 0->1 or 1->0, + * a la atomic_dec_and_mutex_lock. However, ExtINTs are rare and + * only target a single CPU, so that is the common case; do not + * bother eliding the down_write()/up_write() pair. + */ + guard(rwsem_write)(&kvm->arch.apicv_update_lock); + if (atomic_add_return(add, &kvm->arch.apicv_nr_irq_window_req) == inc) + __kvm_set_or_clear_apicv_inhibit(kvm, APICV_INHIBIT_REASON_IRQWIN, inc); +} +EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_inc_or_dec_irq_window_inhibit); + static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu) { if (!kvm_apic_present(vcpu)) @@ -11804,7 +11892,8 @@ static inline int complete_emulated_io(struct kvm_vcpu *vcpu) static int complete_emulated_pio(struct kvm_vcpu *vcpu) { - BUG_ON(!vcpu->arch.pio.count); + if (KVM_BUG_ON(!vcpu->arch.pio.count, vcpu->kvm)) + return -EIO; return complete_emulated_io(vcpu); } @@ -11833,7 +11922,8 @@ static int complete_emulated_mmio(struct kvm_vcpu *vcpu) struct kvm_mmio_fragment *frag; unsigned len; - BUG_ON(!vcpu->mmio_needed); + if (KVM_BUG_ON(!vcpu->mmio_needed, vcpu->kvm)) + return -EIO; /* Complete previous fragment */ frag = &vcpu->mmio_fragments[vcpu->mmio_cur_fragment]; @@ -11846,6 +11936,9 @@ static int complete_emulated_mmio(struct kvm_vcpu *vcpu) frag++; vcpu->mmio_cur_fragment++; } else { + if (WARN_ON_ONCE(frag->data == &frag->val)) + return -EIO; + /* Go forward to the next mmio piece. */ frag->data += len; frag->gpa += len; @@ -11862,12 +11955,7 @@ static int complete_emulated_mmio(struct kvm_vcpu *vcpu) return complete_emulated_io(vcpu); } - run->exit_reason = KVM_EXIT_MMIO; - run->mmio.phys_addr = frag->gpa; - if (vcpu->mmio_is_write) - memcpy(run->mmio.data, frag->data, min(8u, frag->len)); - run->mmio.len = min(8u, frag->len); - run->mmio.is_write = vcpu->mmio_is_write; + kvm_prepare_emulated_mmio_exit(vcpu, frag); vcpu->arch.complete_userspace_io = complete_emulated_mmio; return 0; } @@ -11896,6 +11984,13 @@ static void kvm_put_guest_fpu(struct kvm_vcpu *vcpu) static int kvm_x86_vcpu_pre_run(struct kvm_vcpu *vcpu) { + /* + * Userspace may have modified vCPU state, mark nested_run_pending as + * "untrusted" to avoid triggering false-positive WARNs. + */ + if (vcpu->arch.nested_run_pending == KVM_NESTED_RUN_PENDING) + vcpu->arch.nested_run_pending = KVM_NESTED_RUN_PENDING_UNTRUSTED; + /* * SIPI_RECEIVED is obsolete; KVM leaves the vCPU in Wait-For-SIPI and * tracks the pending SIPI separately. SIPI_RECEIVED is still accepted @@ -12136,6 +12231,8 @@ static void __get_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) if (vcpu->arch.guest_state_protected) goto skip_protected_regs; + kvm_handle_exception_payload_quirk(vcpu); + kvm_get_segment(vcpu, &sregs->cs, VCPU_SREG_CS); kvm_get_segment(vcpu, &sregs->ds, VCPU_SREG_DS); kvm_get_segment(vcpu, &sregs->es, VCPU_SREG_ES); @@ -12529,7 +12626,7 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, if (dbg->control & (KVM_GUESTDBG_INJECT_DB | KVM_GUESTDBG_INJECT_BP)) { r = -EBUSY; - if (kvm_is_exception_pending(vcpu)) + if (kvm_is_exception_pending(vcpu) || vcpu->arch.exception.injected) goto out; if (dbg->control & KVM_GUESTDBG_INJECT_DB) kvm_queue_exception(vcpu, DB_VECTOR); @@ -13073,12 +13170,12 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_vcpu_deliver_sipi_vector); void kvm_arch_enable_virtualization(void) { - cpu_emergency_register_virt_callback(kvm_x86_ops.emergency_disable_virtualization_cpu); + x86_virt_register_emergency_callback(kvm_x86_ops.emergency_disable_virtualization_cpu); } void kvm_arch_disable_virtualization(void) { - cpu_emergency_unregister_virt_callback(kvm_x86_ops.emergency_disable_virtualization_cpu); + x86_virt_unregister_emergency_callback(kvm_x86_ops.emergency_disable_virtualization_cpu); } int kvm_arch_enable_virtualization_cpu(void) @@ -13177,6 +13274,25 @@ int kvm_arch_enable_virtualization_cpu(void) return 0; } +void kvm_arch_shutdown(void) +{ + /* + * Set virt_rebooting to indicate that KVM has asynchronously disabled + * hardware virtualization, i.e. that errors and/or exceptions on SVM + * and VMX instructions are expected and should be ignored. + */ + virt_rebooting = true; + + /* + * Ensure virt_rebooting is visible before IPIs are sent to other CPUs + * to disable virtualization. Effectively pairs with the reception of + * the IPI (virt_rebooting is read in task/exception context, but only + * _needs_ to be read as %true after the IPI function callback disables + * virtualization). + */ + smp_wmb(); +} + void kvm_arch_disable_virtualization_cpu(void) { kvm_x86_call(disable_virtualization_cpu)(); @@ -13191,7 +13307,7 @@ void kvm_arch_disable_virtualization_cpu(void) * disable virtualization arrives. Handle the extreme edge case here * instead of trying to account for it in the normal flows. */ - if (in_task() || WARN_ON_ONCE(!kvm_rebooting)) + if (in_task() || WARN_ON_ONCE(!virt_rebooting)) drop_user_return_notifiers(); else __module_get(THIS_MODULE); @@ -14243,7 +14359,8 @@ static int complete_sev_es_emulated_mmio(struct kvm_vcpu *vcpu) struct kvm_mmio_fragment *frag; unsigned int len; - BUG_ON(!vcpu->mmio_needed); + if (KVM_BUG_ON(!vcpu->mmio_needed, vcpu->kvm)) + return -EIO; /* Complete previous fragment */ frag = &vcpu->mmio_fragments[vcpu->mmio_cur_fragment]; @@ -14265,34 +14382,32 @@ static int complete_sev_es_emulated_mmio(struct kvm_vcpu *vcpu) if (vcpu->mmio_cur_fragment >= vcpu->mmio_nr_fragments) { vcpu->mmio_needed = 0; - // VMG change, at this point, we're always done - // RIP has already been advanced + /* + * All done, as frag->data always points at the GHCB scratch + * area and VMGEXIT is trap-like (RIP is advanced by hardware). + */ return 1; } // More MMIO is needed - run->mmio.phys_addr = frag->gpa; - run->mmio.len = min(8u, frag->len); - run->mmio.is_write = vcpu->mmio_is_write; - if (run->mmio.is_write) - memcpy(run->mmio.data, frag->data, min(8u, frag->len)); - run->exit_reason = KVM_EXIT_MMIO; - + kvm_prepare_emulated_mmio_exit(vcpu, frag); vcpu->arch.complete_userspace_io = complete_sev_es_emulated_mmio; - return 0; } -int kvm_sev_es_mmio_write(struct kvm_vcpu *vcpu, gpa_t gpa, unsigned int bytes, - void *data) +int kvm_sev_es_mmio(struct kvm_vcpu *vcpu, bool is_write, gpa_t gpa, + unsigned int bytes, void *data) { - int handled; struct kvm_mmio_fragment *frag; + int handled; - if (!data) + if (!data || WARN_ON_ONCE(object_is_on_stack(data))) return -EINVAL; - handled = write_emultor.read_write_mmio(vcpu, gpa, bytes, data); + if (is_write) + handled = vcpu_mmio_write(vcpu, gpa, bytes, data); + else + handled = vcpu_mmio_read(vcpu, gpa, bytes, data); if (handled == bytes) return 1; @@ -14300,65 +14415,25 @@ int kvm_sev_es_mmio_write(struct kvm_vcpu *vcpu, gpa_t gpa, unsigned int bytes, gpa += handled; data += handled; - /*TODO: Check if need to increment number of frags */ + /* + * TODO: Determine whether or not userspace plays nice with MMIO + * requests that split a page boundary. + */ frag = vcpu->mmio_fragments; - vcpu->mmio_nr_fragments = 1; frag->len = bytes; frag->gpa = gpa; frag->data = data; vcpu->mmio_needed = 1; vcpu->mmio_cur_fragment = 0; - - vcpu->run->mmio.phys_addr = gpa; - vcpu->run->mmio.len = min(8u, frag->len); - vcpu->run->mmio.is_write = 1; - memcpy(vcpu->run->mmio.data, frag->data, min(8u, frag->len)); - vcpu->run->exit_reason = KVM_EXIT_MMIO; - - vcpu->arch.complete_userspace_io = complete_sev_es_emulated_mmio; - - return 0; -} -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_sev_es_mmio_write); - -int kvm_sev_es_mmio_read(struct kvm_vcpu *vcpu, gpa_t gpa, unsigned int bytes, - void *data) -{ - int handled; - struct kvm_mmio_fragment *frag; - - if (!data) - return -EINVAL; - - handled = read_emultor.read_write_mmio(vcpu, gpa, bytes, data); - if (handled == bytes) - return 1; - - bytes -= handled; - gpa += handled; - data += handled; - - /*TODO: Check if need to increment number of frags */ - frag = vcpu->mmio_fragments; vcpu->mmio_nr_fragments = 1; - frag->len = bytes; - frag->gpa = gpa; - frag->data = data; - - vcpu->mmio_needed = 1; - vcpu->mmio_cur_fragment = 0; - - vcpu->run->mmio.phys_addr = gpa; - vcpu->run->mmio.len = min(8u, frag->len); - vcpu->run->mmio.is_write = 0; - vcpu->run->exit_reason = KVM_EXIT_MMIO; + vcpu->mmio_is_write = is_write; + kvm_prepare_emulated_mmio_exit(vcpu, frag); vcpu->arch.complete_userspace_io = complete_sev_es_emulated_mmio; - return 0; } -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_sev_es_mmio_read); +EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_sev_es_mmio); static void advance_sev_es_emulated_pio(struct kvm_vcpu *vcpu, unsigned count, int size) { diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 94d4f07aaaa0..38a905fa86de 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -188,6 +188,16 @@ static inline bool kvm_can_set_cpuid_and_feature_msrs(struct kvm_vcpu *vcpu) return vcpu->arch.last_vmentry_cpu == -1 && !is_guest_mode(vcpu); } +/* + * WARN if a nested VM-Enter is pending completion, and userspace hasn't gained + * control since the nested VM-Enter was initiated (in which case, userspace + * may have modified vCPU state to induce an architecturally invalid VM-Exit). + */ +static inline void kvm_warn_on_nested_run_pending(struct kvm_vcpu *vcpu) +{ + WARN_ON_ONCE(vcpu->arch.nested_run_pending == KVM_NESTED_RUN_PENDING); +} + static inline void kvm_set_mp_state(struct kvm_vcpu *vcpu, int mp_state) { vcpu->arch.mp_state = mp_state; @@ -712,14 +722,38 @@ static inline bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) __reserved_bits; \ }) -int kvm_sev_es_mmio_write(struct kvm_vcpu *vcpu, gpa_t src, unsigned int bytes, - void *dst); -int kvm_sev_es_mmio_read(struct kvm_vcpu *vcpu, gpa_t src, unsigned int bytes, - void *dst); +int kvm_sev_es_mmio(struct kvm_vcpu *vcpu, bool is_write, gpa_t gpa, + unsigned int bytes, void *data); int kvm_sev_es_string_io(struct kvm_vcpu *vcpu, unsigned int size, unsigned int port, void *data, unsigned int count, int in); +static inline void __kvm_prepare_emulated_mmio_exit(struct kvm_vcpu *vcpu, + gpa_t gpa, unsigned int len, + const void *data, + bool is_write) +{ + struct kvm_run *run = vcpu->run; + + KVM_BUG_ON(len > 8, vcpu->kvm); + + run->mmio.len = len; + run->mmio.is_write = is_write; + run->exit_reason = KVM_EXIT_MMIO; + run->mmio.phys_addr = gpa; + if (is_write) + memcpy(run->mmio.data, data, len); +} + +static inline void kvm_prepare_emulated_mmio_exit(struct kvm_vcpu *vcpu, + struct kvm_mmio_fragment *frag) +{ + WARN_ON_ONCE(!vcpu->mmio_needed || !vcpu->mmio_nr_fragments); + + __kvm_prepare_emulated_mmio_exit(vcpu, frag->gpa, min(8u, frag->len), + frag->data, vcpu->mmio_is_write); +} + static inline bool user_exit_on_hypercall(struct kvm *kvm, unsigned long hc_nr) { return kvm->arch.hypercall_exit_enabled & BIT(hc_nr); diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index f0e77e084482..7ea6a9362173 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -8,6 +8,7 @@ #include /* task_stack_*(), ... */ #include /* oops_begin/end, ... */ #include /* max_low_pfn */ +#include /* bpf_arena_handle_page_fault */ #include /* kfence_handle_page_fault */ #include /* NOKPROBE_SYMBOL, ... */ #include /* kmmio_handler, ... */ @@ -686,12 +687,15 @@ page_fault_oops(struct pt_regs *regs, unsigned long error_code, * avoid hanging the system. */ if (IS_ENABLED(CONFIG_EFI)) - efi_crash_gracefully_on_page_fault(address); + efi_crash_gracefully_on_page_fault(address, regs); - /* Only not-present faults should be handled by KFENCE. */ - if (!(error_code & X86_PF_PROT) && - kfence_handle_page_fault(address, error_code & X86_PF_WRITE, regs)) - return; + /* Only not-present faults should be handled by KFENCE or BPF arena. */ + if (!(error_code & X86_PF_PROT)) { + if (kfence_handle_page_fault(address, error_code & X86_PF_WRITE, regs)) + return; + if (bpf_arena_handle_page_fault(address, error_code & X86_PF_WRITE, regs->ip)) + return; + } oops: /* diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c index cba907c39718..d023a40a1e03 100644 --- a/arch/x86/mm/pat/set_memory.c +++ b/arch/x86/mm/pat/set_memory.c @@ -399,6 +399,15 @@ static void cpa_flush_all(unsigned long cache) on_each_cpu(__cpa_flush_all, (void *) cache, 1); } +static void __cpa_flush_tlb(void *data) +{ + struct cpa_data *cpa = data; + unsigned int i; + + for (i = 0; i < cpa->numpages; i++) + flush_tlb_one_kernel(fix_addr(__cpa_addr(cpa, i))); +} + static int collapse_large_pages(unsigned long addr, struct list_head *pgtables); static void cpa_collapse_large_pages(struct cpa_data *cpa) @@ -435,7 +444,6 @@ static void cpa_collapse_large_pages(struct cpa_data *cpa) static void cpa_flush(struct cpa_data *cpa, int cache) { - unsigned long start, end; unsigned int i; BUG_ON(irqs_disabled() && !early_boot_irqs_disabled); @@ -445,12 +453,10 @@ static void cpa_flush(struct cpa_data *cpa, int cache) goto collapse_large_pages; } - start = fix_addr(__cpa_addr(cpa, 0)); - end = start + cpa->numpages * PAGE_SIZE; - if (cpa->force_flush_all) - end = TLB_FLUSH_ALL; - - flush_tlb_kernel_range(start, end); + if (cpa->force_flush_all || cpa->numpages > tlb_single_page_flush_ceiling) + flush_tlb_all(); + else + on_each_cpu(__cpa_flush_tlb, cpa, 1); if (!cache) goto collapse_large_pages; diff --git a/arch/x86/mm/pti.c b/arch/x86/mm/pti.c index f7546e9e8e89..631f0375bd42 100644 --- a/arch/x86/mm/pti.c +++ b/arch/x86/mm/pti.c @@ -105,6 +105,11 @@ void __init pti_check_boottime_disable(void) pr_debug("PTI enabled, disabling INVLPGB\n"); setup_clear_cpu_cap(X86_FEATURE_INVLPGB); } + + if (cpu_feature_enabled(X86_FEATURE_FRED)) { + pr_debug("PTI enabled, disabling FRED\n"); + setup_clear_cpu_cap(X86_FEATURE_FRED); + } } static int __init pti_parse_cmdline(char *arg) diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index e9b78040d703..ea9e707e8abf 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -58,8 +58,8 @@ static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len) #define EMIT_ENDBR() EMIT(gen_endbr(), 4) #define EMIT_ENDBR_POISON() EMIT(gen_endbr_poison(), 4) #else -#define EMIT_ENDBR() -#define EMIT_ENDBR_POISON() +#define EMIT_ENDBR() do { } while (0) +#define EMIT_ENDBR_POISON() do { } while (0) #endif static bool is_imm8(int value) @@ -1649,8 +1649,8 @@ static int emit_spectre_bhb_barrier(u8 **pprog, u8 *ip, return 0; } -static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image, - int oldproglen, struct jit_context *ctx, bool jmp_padding) +static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *addrs, u8 *image, + u8 *rw_image, int oldproglen, struct jit_context *ctx, bool jmp_padding) { bool tail_call_reachable = bpf_prog->aux->tail_call_reachable; struct bpf_insn *insn = bpf_prog->insnsi; @@ -1663,7 +1663,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image void __percpu *priv_stack_ptr; int i, excnt = 0; int ilen, proglen = 0; - u8 *prog = temp; + u8 *ip, *prog = temp; u32 stack_depth; int err; @@ -1734,6 +1734,11 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image dst_reg = X86_REG_R9; } + if (bpf_insn_is_indirect_target(env, bpf_prog, i - 1)) + EMIT_ENDBR(); + + ip = image + addrs[i - 1] + (prog - temp); + switch (insn->code) { /* ALU */ case BPF_ALU | BPF_ADD | BPF_X: @@ -2440,8 +2445,6 @@ populate_extable: /* call */ case BPF_JMP | BPF_CALL: { - u8 *ip = image + addrs[i - 1]; - func = (u8 *) __bpf_call_base + imm32; if (src_reg == BPF_PSEUDO_CALL && tail_call_reachable) { LOAD_TAIL_CALL_CNT_PTR(stack_depth); @@ -2465,7 +2468,8 @@ populate_extable: if (imm32) emit_bpf_tail_call_direct(bpf_prog, &bpf_prog->aux->poke_tab[imm32 - 1], - &prog, image + addrs[i - 1], + &prog, + ip, callee_regs_used, stack_depth, ctx); @@ -2474,7 +2478,7 @@ populate_extable: &prog, callee_regs_used, stack_depth, - image + addrs[i - 1], + ip, ctx); break; @@ -2639,7 +2643,7 @@ emit_cond_jmp: /* Convert BPF opcode to x86 */ break; case BPF_JMP | BPF_JA | BPF_X: - emit_indirect_jump(&prog, insn->dst_reg, image + addrs[i - 1]); + emit_indirect_jump(&prog, insn->dst_reg, ip); break; case BPF_JMP | BPF_JA: case BPF_JMP32 | BPF_JA: @@ -2729,8 +2733,6 @@ emit_jmp: ctx->cleanup_addr = proglen; if (bpf_prog_was_classic(bpf_prog) && !ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN)) { - u8 *ip = image + addrs[i - 1]; - if (emit_spectre_bhb_barrier(&prog, ip, bpf_prog)) return -EINVAL; } @@ -3713,17 +3715,15 @@ struct x64_jit_data { #define MAX_PASSES 20 #define PADDING_PASSES (MAX_PASSES - 5) -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) { struct bpf_binary_header *rw_header = NULL; struct bpf_binary_header *header = NULL; - struct bpf_prog *tmp, *orig_prog = prog; void __percpu *priv_stack_ptr = NULL; struct x64_jit_data *jit_data; int priv_stack_alloc_sz; int proglen, oldproglen = 0; struct jit_context ctx = {}; - bool tmp_blinded = false; bool extra_pass = false; bool padding = false; u8 *rw_image = NULL; @@ -3733,27 +3733,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) int i; if (!prog->jit_requested) - return orig_prog; - - tmp = bpf_jit_blind_constants(prog); - /* - * If blinding was requested and we failed during blinding, - * we must fall back to the interpreter. - */ - if (IS_ERR(tmp)) - return orig_prog; - if (tmp != prog) { - tmp_blinded = true; - prog = tmp; - } + return prog; jit_data = prog->aux->jit_data; if (!jit_data) { jit_data = kzalloc_obj(*jit_data); - if (!jit_data) { - prog = orig_prog; - goto out; - } + if (!jit_data) + return prog; prog->aux->jit_data = jit_data; } priv_stack_ptr = prog->aux->priv_stack_ptr; @@ -3765,10 +3751,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) priv_stack_alloc_sz = round_up(prog->aux->stack_depth, 8) + 2 * PRIV_STACK_GUARD_SZ; priv_stack_ptr = __alloc_percpu_gfp(priv_stack_alloc_sz, 8, GFP_KERNEL); - if (!priv_stack_ptr) { - prog = orig_prog; + if (!priv_stack_ptr) goto out_priv_stack; - } priv_stack_init_guard(priv_stack_ptr, priv_stack_alloc_sz); prog->aux->priv_stack_ptr = priv_stack_ptr; @@ -3786,10 +3770,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) goto skip_init_addrs; } addrs = kvmalloc_objs(*addrs, prog->len + 1); - if (!addrs) { - prog = orig_prog; + if (!addrs) goto out_addrs; - } /* * Before first pass, make a rough estimation of addrs[] @@ -3811,7 +3793,7 @@ skip_init_addrs: for (pass = 0; pass < MAX_PASSES || image; pass++) { if (!padding && pass >= PADDING_PASSES) padding = true; - proglen = do_jit(prog, addrs, image, rw_image, oldproglen, &ctx, padding); + proglen = do_jit(env, prog, addrs, image, rw_image, oldproglen, &ctx, padding); if (proglen <= 0) { out_image: image = NULL; @@ -3820,8 +3802,6 @@ out_image: sizeof(rw_header->size)); bpf_jit_binary_pack_free(header, rw_header); } - /* Fall back to interpreter mode */ - prog = orig_prog; if (extra_pass) { prog->bpf_func = NULL; prog->jited = 0; @@ -3852,10 +3832,8 @@ out_image: header = bpf_jit_binary_pack_alloc(roundup(proglen, align) + extable_size, &image, align, &rw_header, &rw_image, jit_fill_hole); - if (!header) { - prog = orig_prog; + if (!header) goto out_addrs; - } prog->aux->extable = (void *) image + roundup(proglen, align); } oldproglen = proglen; @@ -3908,8 +3886,6 @@ out_image: prog->bpf_func = (void *)image + cfi_get_offset(); prog->jited = 1; prog->jited_len = proglen - cfi_get_offset(); - } else { - prog = orig_prog; } if (!image || !prog->is_func || extra_pass) { @@ -3925,10 +3901,7 @@ out_priv_stack: kfree(jit_data); prog->aux->jit_data = NULL; } -out: - if (tmp_blinded) - bpf_jit_prog_release_other(prog, prog == orig_prog ? - tmp : orig_prog); + return prog; } diff --git a/arch/x86/net/bpf_jit_comp32.c b/arch/x86/net/bpf_jit_comp32.c index dda423025c3d..852baf2e4db4 100644 --- a/arch/x86/net/bpf_jit_comp32.c +++ b/arch/x86/net/bpf_jit_comp32.c @@ -2518,38 +2518,22 @@ bool bpf_jit_needs_zext(void) return true; } -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) { struct bpf_binary_header *header = NULL; - struct bpf_prog *tmp, *orig_prog = prog; int proglen, oldproglen = 0; struct jit_context ctx = {}; - bool tmp_blinded = false; u8 *image = NULL; int *addrs; int pass; int i; if (!prog->jit_requested) - return orig_prog; - - tmp = bpf_jit_blind_constants(prog); - /* - * If blinding was requested and we failed during blinding, - * we must fall back to the interpreter. - */ - if (IS_ERR(tmp)) - return orig_prog; - if (tmp != prog) { - tmp_blinded = true; - prog = tmp; - } + return prog; addrs = kmalloc_objs(*addrs, prog->len); - if (!addrs) { - prog = orig_prog; - goto out; - } + if (!addrs) + return prog; /* * Before first pass, make a rough estimation of addrs[] @@ -2574,7 +2558,6 @@ out_image: image = NULL; if (header) bpf_jit_binary_free(header); - prog = orig_prog; goto out_addrs; } if (image) { @@ -2588,10 +2571,8 @@ out_image: if (proglen == oldproglen) { header = bpf_jit_binary_alloc(proglen, &image, 1, jit_fill_hole); - if (!header) { - prog = orig_prog; + if (!header) goto out_addrs; - } } oldproglen = proglen; cond_resched(); @@ -2604,16 +2585,10 @@ out_image: prog->bpf_func = (void *)image; prog->jited = 1; prog->jited_len = proglen; - } else { - prog = orig_prog; } out_addrs: kfree(addrs); -out: - if (tmp_blinded) - bpf_jit_prog_release_other(prog, prog == orig_prog ? - tmp : orig_prog); return prog; } diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index d84c6020dda1..0c39adb96b91 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -920,7 +920,7 @@ umode_t efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) return attr->mode; } -enum efi_secureboot_mode __x86_ima_efi_boot_mode(void) +enum efi_secureboot_mode __x86_efi_boot_mode(void) { return boot_params.secure_boot; } diff --git a/arch/x86/platform/efi/memmap.c b/arch/x86/platform/efi/memmap.c index 023697c88910..697a9a26a005 100644 --- a/arch/x86/platform/efi/memmap.c +++ b/arch/x86/platform/efi/memmap.c @@ -34,10 +34,7 @@ static void __init __efi_memmap_free(u64 phys, unsigned long size, unsigned long flags) { if (flags & EFI_MEMMAP_MEMBLOCK) { - if (slab_is_available()) - memblock_free_late(phys, size); - else - memblock_phys_free(phys, size); + memblock_phys_free(phys, size); } else if (flags & EFI_MEMMAP_SLAB) { struct page *p = pfn_to_page(PHYS_PFN(phys)); unsigned int order = get_order(size); diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 79f0818131e8..90a065fcb1fa 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c @@ -372,7 +372,7 @@ void __init efi_reserve_boot_services(void) * doesn't make sense as far as the firmware is * concerned, but it does provide us with a way to tag * those regions that must not be paired with - * memblock_free_late(). + * memblock_phys_free(). */ md->attribute |= EFI_MEMORY_RUNTIME; } @@ -761,7 +761,8 @@ int efi_capsule_setup_info(struct capsule_info *cap_info, void *kbuff, * @return: Returns, if the page fault is not handled. This function * will never return if the page fault is handled successfully. */ -void efi_crash_gracefully_on_page_fault(unsigned long phys_addr) +void efi_crash_gracefully_on_page_fault(unsigned long phys_addr, + const struct pt_regs *regs) { if (!IS_ENABLED(CONFIG_X86_64)) return; @@ -770,7 +771,7 @@ void efi_crash_gracefully_on_page_fault(unsigned long phys_addr) * If we get an interrupt/NMI while processing an EFI runtime service * then this is a regular OOPS, not an EFI failure. */ - if (in_interrupt()) + if (!in_task()) return; /* @@ -810,6 +811,14 @@ void efi_crash_gracefully_on_page_fault(unsigned long phys_addr) return; } + /* + * The API does not permit entering a kernel mode FPU section with + * interrupts enabled and leaving it with interrupts disabled. So + * re-enable interrupts now if they were enabled when the page fault + * occurred. + */ + local_irq_restore(regs->flags); + /* * Before calling EFI Runtime Service, the kernel has switched the * calling process to efi_mm. Hence, switch back to task_mm. diff --git a/arch/x86/um/os-Linux/mcontext.c b/arch/x86/um/os-Linux/mcontext.c index a21403df6663..b1580df80b3f 100644 --- a/arch/x86/um/os-Linux/mcontext.c +++ b/arch/x86/um/os-Linux/mcontext.c @@ -4,7 +4,13 @@ #include #include #include +/* + * musl defines struct sigcontext in . Rename the kernel's + * copy to avoid redefinition while keeping the FP-state types available. + */ +#define sigcontext __kernel_sigcontext #include +#undef sigcontext #include #include #include diff --git a/arch/x86/um/vdso/Makefile b/arch/x86/um/vdso/Makefile index 8a7c8b37cb6e..7664cbedbe30 100644 --- a/arch/x86/um/vdso/Makefile +++ b/arch/x86/um/vdso/Makefile @@ -3,8 +3,6 @@ # Building vDSO images for x86. # -vdso-install-y += vdso.so - # files to link into the vdso vobjs-y := vdso-note.o um_vdso.o diff --git a/arch/x86/virt/Makefile b/arch/x86/virt/Makefile index ea343fc392dc..6e485751650c 100644 --- a/arch/x86/virt/Makefile +++ b/arch/x86/virt/Makefile @@ -1,2 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += svm/ vmx/ + +obj-$(subst m,y,$(CONFIG_KVM_X86)) += hw.o \ No newline at end of file diff --git a/arch/x86/virt/hw.c b/arch/x86/virt/hw.c new file mode 100644 index 000000000000..f647557d38ac --- /dev/null +++ b/arch/x86/virt/hw.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct x86_virt_ops { + int feature; + int (*enable_virtualization_cpu)(void); + int (*disable_virtualization_cpu)(void); + void (*emergency_disable_virtualization_cpu)(void); +}; +static struct x86_virt_ops virt_ops __ro_after_init; + +__visible bool virt_rebooting; +EXPORT_SYMBOL_FOR_KVM(virt_rebooting); + +static DEFINE_PER_CPU(int, virtualization_nr_users); + +static cpu_emergency_virt_cb __rcu *kvm_emergency_callback; + +void x86_virt_register_emergency_callback(cpu_emergency_virt_cb *callback) +{ + if (WARN_ON_ONCE(rcu_access_pointer(kvm_emergency_callback))) + return; + + rcu_assign_pointer(kvm_emergency_callback, callback); +} +EXPORT_SYMBOL_FOR_KVM(x86_virt_register_emergency_callback); + +void x86_virt_unregister_emergency_callback(cpu_emergency_virt_cb *callback) +{ + if (WARN_ON_ONCE(rcu_access_pointer(kvm_emergency_callback) != callback)) + return; + + rcu_assign_pointer(kvm_emergency_callback, NULL); + synchronize_rcu(); +} +EXPORT_SYMBOL_FOR_KVM(x86_virt_unregister_emergency_callback); + +static void x86_virt_invoke_kvm_emergency_callback(void) +{ + cpu_emergency_virt_cb *kvm_callback; + + kvm_callback = rcu_dereference(kvm_emergency_callback); + if (kvm_callback) + kvm_callback(); +} + +#if IS_ENABLED(CONFIG_KVM_INTEL) +static DEFINE_PER_CPU(struct vmcs *, root_vmcs); + +static int x86_virt_cpu_vmxon(void) +{ + u64 vmxon_pointer = __pa(per_cpu(root_vmcs, raw_smp_processor_id())); + u64 msr; + + cr4_set_bits(X86_CR4_VMXE); + + asm goto("1: vmxon %[vmxon_pointer]\n\t" + _ASM_EXTABLE(1b, %l[fault]) + : : [vmxon_pointer] "m"(vmxon_pointer) + : : fault); + return 0; + +fault: + WARN_ONCE(1, "VMXON faulted, MSR_IA32_FEAT_CTL (0x3a) = 0x%llx\n", + rdmsrq_safe(MSR_IA32_FEAT_CTL, &msr) ? 0xdeadbeef : msr); + cr4_clear_bits(X86_CR4_VMXE); + + return -EFAULT; +} + +static int x86_vmx_enable_virtualization_cpu(void) +{ + int r; + + if (cr4_read_shadow() & X86_CR4_VMXE) + return -EBUSY; + + intel_pt_handle_vmx(1); + + r = x86_virt_cpu_vmxon(); + if (r) { + intel_pt_handle_vmx(0); + return r; + } + + return 0; +} + +/* + * Disable VMX and clear CR4.VMXE (even if VMXOFF faults) + * + * Note, VMXOFF causes a #UD if the CPU is !post-VMXON, but it's impossible to + * atomically track post-VMXON state, e.g. this may be called in NMI context. + * Eat all faults as all other faults on VMXOFF faults are mode related, i.e. + * faults are guaranteed to be due to the !post-VMXON check unless the CPU is + * magically in RM, VM86, compat mode, or at CPL>0. + */ +static int x86_vmx_disable_virtualization_cpu(void) +{ + int r = -EIO; + + asm goto("1: vmxoff\n\t" + _ASM_EXTABLE(1b, %l[fault]) + ::: "cc", "memory" : fault); + r = 0; + +fault: + cr4_clear_bits(X86_CR4_VMXE); + intel_pt_handle_vmx(0); + return r; +} + +static void x86_vmx_emergency_disable_virtualization_cpu(void) +{ + virt_rebooting = true; + + /* + * Note, CR4.VMXE can be _cleared_ in NMI context, but it can only be + * set in task context. If this races with _another_ emergency call + * from NMI context, VMCLEAR (in KVM) and VMXOFF may #UD, but KVM and + * the kernel will eat those faults due to virt_rebooting being set by + * the interrupting NMI callback. + */ + if (!(__read_cr4() & X86_CR4_VMXE)) + return; + + x86_virt_invoke_kvm_emergency_callback(); + + x86_vmx_disable_virtualization_cpu(); +} + +static __init void x86_vmx_exit(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + free_page((unsigned long)per_cpu(root_vmcs, cpu)); + per_cpu(root_vmcs, cpu) = NULL; + } +} + +static __init int __x86_vmx_init(void) +{ + const struct x86_virt_ops vmx_ops = { + .feature = X86_FEATURE_VMX, + .enable_virtualization_cpu = x86_vmx_enable_virtualization_cpu, + .disable_virtualization_cpu = x86_vmx_disable_virtualization_cpu, + .emergency_disable_virtualization_cpu = x86_vmx_emergency_disable_virtualization_cpu, + }; + + u64 basic_msr; + u32 rev_id; + int cpu; + + if (!cpu_feature_enabled(X86_FEATURE_VMX)) + return -EOPNOTSUPP; + + rdmsrq(MSR_IA32_VMX_BASIC, basic_msr); + + /* IA-32 SDM Vol 3B: VMCS size is never greater than 4kB. */ + if (WARN_ON_ONCE(vmx_basic_vmcs_size(basic_msr) > PAGE_SIZE)) + return -EIO; + + /* + * Even if eVMCS is enabled (or will be enabled?), and even though not + * explicitly documented by TLFS, the root VMCS passed to VMXON should + * still be marked with the revision_id reported by the physical CPU. + */ + rev_id = vmx_basic_vmcs_revision_id(basic_msr); + + for_each_possible_cpu(cpu) { + int node = cpu_to_node(cpu); + struct page *page; + struct vmcs *vmcs; + + page = __alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0); + if (WARN_ON_ONCE(!page)) { + x86_vmx_exit(); + return -ENOMEM; + } + + vmcs = page_address(page); + vmcs->hdr.revision_id = rev_id; + per_cpu(root_vmcs, cpu) = vmcs; + } + + memcpy(&virt_ops, &vmx_ops, sizeof(virt_ops)); + return 0; +} + +static __init int x86_vmx_init(void) +{ + int r; + + r = __x86_vmx_init(); + if (r) + setup_clear_cpu_cap(X86_FEATURE_VMX); + return r; +} +#else +static __init int x86_vmx_init(void) { return -EOPNOTSUPP; } +static __init void x86_vmx_exit(void) { } +#endif + +#if IS_ENABLED(CONFIG_KVM_AMD) +static int x86_svm_enable_virtualization_cpu(void) +{ + u64 efer; + + rdmsrq(MSR_EFER, efer); + if (efer & EFER_SVME) + return -EBUSY; + + wrmsrq(MSR_EFER, efer | EFER_SVME); + return 0; +} + +static int x86_svm_disable_virtualization_cpu(void) +{ + int r = -EIO; + u64 efer; + + /* + * Force GIF=1 prior to disabling SVM, e.g. to ensure INIT and + * NMI aren't blocked. + */ + asm goto("1: stgi\n\t" + _ASM_EXTABLE(1b, %l[fault]) + ::: "memory" : fault); + r = 0; + +fault: + rdmsrq(MSR_EFER, efer); + wrmsrq(MSR_EFER, efer & ~EFER_SVME); + return r; +} + +static void x86_svm_emergency_disable_virtualization_cpu(void) +{ + u64 efer; + + virt_rebooting = true; + + rdmsrq(MSR_EFER, efer); + if (!(efer & EFER_SVME)) + return; + + x86_virt_invoke_kvm_emergency_callback(); + + x86_svm_disable_virtualization_cpu(); +} + +static __init int x86_svm_init(void) +{ + const struct x86_virt_ops svm_ops = { + .feature = X86_FEATURE_SVM, + .enable_virtualization_cpu = x86_svm_enable_virtualization_cpu, + .disable_virtualization_cpu = x86_svm_disable_virtualization_cpu, + .emergency_disable_virtualization_cpu = x86_svm_emergency_disable_virtualization_cpu, + }; + + if (!cpu_feature_enabled(X86_FEATURE_SVM) || + cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) + return -EOPNOTSUPP; + + memcpy(&virt_ops, &svm_ops, sizeof(virt_ops)); + return 0; +} +#else +static __init int x86_svm_init(void) { return -EOPNOTSUPP; } +#endif + +int x86_virt_get_ref(int feat) +{ + int r; + + /* Ensure the !feature check can't get false positives. */ + BUILD_BUG_ON(!X86_FEATURE_SVM || !X86_FEATURE_VMX); + + if (!virt_ops.feature || virt_ops.feature != feat) + return -EOPNOTSUPP; + + guard(preempt)(); + + if (this_cpu_inc_return(virtualization_nr_users) > 1) + return 0; + + r = virt_ops.enable_virtualization_cpu(); + if (r) + WARN_ON_ONCE(this_cpu_dec_return(virtualization_nr_users)); + + return r; +} +EXPORT_SYMBOL_FOR_KVM(x86_virt_get_ref); + +void x86_virt_put_ref(int feat) +{ + guard(preempt)(); + + if (WARN_ON_ONCE(!this_cpu_read(virtualization_nr_users)) || + this_cpu_dec_return(virtualization_nr_users)) + return; + + BUG_ON(virt_ops.disable_virtualization_cpu() && !virt_rebooting); +} +EXPORT_SYMBOL_FOR_KVM(x86_virt_put_ref); + +/* + * Disable virtualization, i.e. VMX or SVM, to ensure INIT is recognized during + * reboot. VMX blocks INIT if the CPU is post-VMXON, and SVM blocks INIT if + * GIF=0, i.e. if the crash occurred between CLGI and STGI. + */ +int x86_virt_emergency_disable_virtualization_cpu(void) +{ + if (!virt_ops.feature) + return -EOPNOTSUPP; + + /* + * IRQs must be disabled as virtualization is enabled in hardware via + * function call IPIs, i.e. IRQs need to be disabled to guarantee + * virtualization stays disabled. + */ + lockdep_assert_irqs_disabled(); + + /* + * Do the NMI shootdown even if virtualization is off on _this_ CPU, as + * other CPUs may have virtualization enabled. + * + * TODO: Track whether or not virtualization might be enabled on other + * CPUs? May not be worth avoiding the NMI shootdown... + */ + virt_ops.emergency_disable_virtualization_cpu(); + return 0; +} + +void __init x86_virt_init(void) +{ + /* + * Attempt to initialize both SVM and VMX, and simply use whichever one + * is present. Rsefuse to enable/use SVM or VMX if both are somehow + * supported. No known CPU supports both SVM and VMX. + */ + bool has_vmx = !x86_vmx_init(); + bool has_svm = !x86_svm_init(); + + if (WARN_ON_ONCE(has_vmx && has_svm)) { + x86_vmx_exit(); + memset(&virt_ops, 0, sizeof(virt_ops)); + } +} diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index 8b8e165a2001..cb9b3210ab71 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include #include #include +#include #include "tdx.h" static u32 tdx_global_keyid __ro_after_init; @@ -51,13 +53,11 @@ static DEFINE_PER_CPU(bool, tdx_lp_initialized); static struct tdmr_info_list tdx_tdmr_list; -static enum tdx_module_status_t tdx_module_status; -static DEFINE_MUTEX(tdx_module_lock); - /* All TDX-usable memory regions. Protected by mem_hotplug_lock. */ static LIST_HEAD(tdx_memlist); -static struct tdx_sys_info tdx_sysinfo; +static struct tdx_sys_info tdx_sysinfo __ro_after_init; +static bool tdx_module_initialized __ro_after_init; typedef void (*sc_err_func_t)(u64 fn, u64 err, struct tdx_module_args *args); @@ -106,8 +106,7 @@ static __always_inline int sc_retry_prerr(sc_func_t func, /* * Do the module global initialization once and return its result. - * It can be done on any cpu. It's always called with interrupts - * disabled. + * It can be done on any cpu, and from task or IRQ context. */ static int try_init_module_global(void) { @@ -116,8 +115,6 @@ static int try_init_module_global(void) static bool sysinit_done; static int sysinit_ret; - lockdep_assert_irqs_disabled(); - raw_spin_lock(&sysinit_lock); if (sysinit_done) @@ -142,26 +139,15 @@ out: } /** - * tdx_cpu_enable - Enable TDX on local cpu - * - * Do one-time TDX module per-cpu initialization SEAMCALL (and TDX module - * global initialization SEAMCALL if not done) on local cpu to make this - * cpu be ready to run any other SEAMCALLs. - * - * Always call this function via IPI function calls. - * - * Return 0 on success, otherwise errors. + * Enable VMXON and then do one-time TDX module per-cpu initialization SEAMCALL + * (and TDX module global initialization SEAMCALL if not done) on local cpu to + * make this cpu be ready to run any other SEAMCALLs. */ -int tdx_cpu_enable(void) +static int tdx_cpu_enable(void) { struct tdx_module_args args = {}; int ret; - if (!boot_cpu_has(X86_FEATURE_TDX_HOST_PLATFORM)) - return -ENODEV; - - lockdep_assert_irqs_disabled(); - if (__this_cpu_read(tdx_lp_initialized)) return 0; @@ -182,15 +168,101 @@ int tdx_cpu_enable(void) return 0; } -EXPORT_SYMBOL_FOR_KVM(tdx_cpu_enable); + +static int tdx_online_cpu(unsigned int cpu) +{ + int ret; + + ret = x86_virt_get_ref(X86_FEATURE_VMX); + if (ret) + return ret; + + ret = tdx_cpu_enable(); + if (ret) + x86_virt_put_ref(X86_FEATURE_VMX); + + return ret; +} + +static int tdx_offline_cpu(unsigned int cpu) +{ + int i; + + /* No TD is running. Allow any cpu to be offline. */ + if (ida_is_empty(&tdx_guest_keyid_pool)) + goto done; + + /* + * In order to reclaim TDX HKID, (i.e. when deleting guest TD), need to + * call TDH.PHYMEM.PAGE.WBINVD on all packages to program all memory + * controller with pconfig. If we have active TDX HKID, refuse to + * offline the last online cpu. + */ + for_each_online_cpu(i) { + /* + * Found another online cpu on the same package. + * Allow to offline. + */ + if (i != cpu && topology_physical_package_id(i) == + topology_physical_package_id(cpu)) + goto done; + } + + /* + * This is the last cpu of this package. Don't offline it. + * + * Because it's hard for human operator to understand the + * reason, warn it. + */ +#define MSG_ALLPKG_ONLINE \ + "TDX requires all packages to have an online CPU. Delete all TDs in order to offline all CPUs of a package.\n" + pr_warn_ratelimited(MSG_ALLPKG_ONLINE); + return -EBUSY; + +done: + x86_virt_put_ref(X86_FEATURE_VMX); + return 0; +} + +static void tdx_shutdown_cpu(void *ign) +{ + x86_virt_put_ref(X86_FEATURE_VMX); +} + +static void tdx_shutdown(void *ign) +{ + on_each_cpu(tdx_shutdown_cpu, NULL, 1); +} + +static int tdx_suspend(void *ign) +{ + x86_virt_put_ref(X86_FEATURE_VMX); + return 0; +} + +static void tdx_resume(void *ign) +{ + WARN_ON_ONCE(x86_virt_get_ref(X86_FEATURE_VMX)); +} + +static const struct syscore_ops tdx_syscore_ops = { + .suspend = tdx_suspend, + .resume = tdx_resume, + .shutdown = tdx_shutdown, +}; + +static struct syscore tdx_syscore = { + .ops = &tdx_syscore_ops, +}; /* * Add a memory region as a TDX memory block. The caller must make sure * all memory regions are added in address ascending order and don't * overlap. */ -static int add_tdx_memblock(struct list_head *tmb_list, unsigned long start_pfn, - unsigned long end_pfn, int nid) +static __init int add_tdx_memblock(struct list_head *tmb_list, + unsigned long start_pfn, + unsigned long end_pfn, int nid) { struct tdx_memblock *tmb; @@ -208,7 +280,7 @@ static int add_tdx_memblock(struct list_head *tmb_list, unsigned long start_pfn, return 0; } -static void free_tdx_memlist(struct list_head *tmb_list) +static __init void free_tdx_memlist(struct list_head *tmb_list) { /* @tmb_list is protected by mem_hotplug_lock */ while (!list_empty(tmb_list)) { @@ -226,7 +298,7 @@ static void free_tdx_memlist(struct list_head *tmb_list) * ranges off in a secondary structure because memblock is modified * in memory hotplug while TDX memory regions are fixed. */ -static int build_tdx_memlist(struct list_head *tmb_list) +static __init int build_tdx_memlist(struct list_head *tmb_list) { unsigned long start_pfn, end_pfn; int i, nid, ret; @@ -258,7 +330,7 @@ err: return ret; } -static int read_sys_metadata_field(u64 field_id, u64 *data) +static __init int read_sys_metadata_field(u64 field_id, u64 *data) { struct tdx_module_args args = {}; int ret; @@ -280,7 +352,7 @@ static int read_sys_metadata_field(u64 field_id, u64 *data) #include "tdx_global_metadata.c" -static int check_features(struct tdx_sys_info *sysinfo) +static __init int check_features(struct tdx_sys_info *sysinfo) { u64 tdx_features0 = sysinfo->features.tdx_features0; @@ -293,7 +365,7 @@ static int check_features(struct tdx_sys_info *sysinfo) } /* Calculate the actual TDMR size */ -static int tdmr_size_single(u16 max_reserved_per_tdmr) +static __init int tdmr_size_single(u16 max_reserved_per_tdmr) { int tdmr_sz; @@ -307,8 +379,8 @@ static int tdmr_size_single(u16 max_reserved_per_tdmr) return ALIGN(tdmr_sz, TDMR_INFO_ALIGNMENT); } -static int alloc_tdmr_list(struct tdmr_info_list *tdmr_list, - struct tdx_sys_info_tdmr *sysinfo_tdmr) +static __init int alloc_tdmr_list(struct tdmr_info_list *tdmr_list, + struct tdx_sys_info_tdmr *sysinfo_tdmr) { size_t tdmr_sz, tdmr_array_sz; void *tdmr_array; @@ -339,7 +411,7 @@ static int alloc_tdmr_list(struct tdmr_info_list *tdmr_list, return 0; } -static void free_tdmr_list(struct tdmr_info_list *tdmr_list) +static __init void free_tdmr_list(struct tdmr_info_list *tdmr_list) { free_pages_exact(tdmr_list->tdmrs, tdmr_list->max_tdmrs * tdmr_list->tdmr_sz); @@ -368,8 +440,8 @@ static inline u64 tdmr_end(struct tdmr_info *tdmr) * preallocated @tdmr_list, following all the special alignment * and size rules for TDMR. */ -static int fill_out_tdmrs(struct list_head *tmb_list, - struct tdmr_info_list *tdmr_list) +static __init int fill_out_tdmrs(struct list_head *tmb_list, + struct tdmr_info_list *tdmr_list) { struct tdx_memblock *tmb; int tdmr_idx = 0; @@ -445,8 +517,8 @@ static int fill_out_tdmrs(struct list_head *tmb_list, * Calculate PAMT size given a TDMR and a page size. The returned * PAMT size is always aligned up to 4K page boundary. */ -static unsigned long tdmr_get_pamt_sz(struct tdmr_info *tdmr, int pgsz, - u16 pamt_entry_size) +static __init unsigned long tdmr_get_pamt_sz(struct tdmr_info *tdmr, int pgsz, + u16 pamt_entry_size) { unsigned long pamt_sz, nr_pamt_entries; @@ -477,7 +549,7 @@ static unsigned long tdmr_get_pamt_sz(struct tdmr_info *tdmr, int pgsz, * PAMT. This node will have some memory covered by the TDMR. The * relative amount of memory covered is not considered. */ -static int tdmr_get_nid(struct tdmr_info *tdmr, struct list_head *tmb_list) +static __init int tdmr_get_nid(struct tdmr_info *tdmr, struct list_head *tmb_list) { struct tdx_memblock *tmb; @@ -506,9 +578,9 @@ static int tdmr_get_nid(struct tdmr_info *tdmr, struct list_head *tmb_list) * Allocate PAMTs from the local NUMA node of some memory in @tmb_list * within @tdmr, and set up PAMTs for @tdmr. */ -static int tdmr_set_up_pamt(struct tdmr_info *tdmr, - struct list_head *tmb_list, - u16 pamt_entry_size[]) +static __init int tdmr_set_up_pamt(struct tdmr_info *tdmr, + struct list_head *tmb_list, + u16 pamt_entry_size[]) { unsigned long pamt_base[TDX_PS_NR]; unsigned long pamt_size[TDX_PS_NR]; @@ -578,7 +650,7 @@ static void tdmr_get_pamt(struct tdmr_info *tdmr, unsigned long *pamt_base, *pamt_size = pamt_sz; } -static void tdmr_do_pamt_func(struct tdmr_info *tdmr, +static __init void tdmr_do_pamt_func(struct tdmr_info *tdmr, void (*pamt_func)(unsigned long base, unsigned long size)) { unsigned long pamt_base, pamt_size; @@ -595,17 +667,17 @@ static void tdmr_do_pamt_func(struct tdmr_info *tdmr, pamt_func(pamt_base, pamt_size); } -static void free_pamt(unsigned long pamt_base, unsigned long pamt_size) +static __init void free_pamt(unsigned long pamt_base, unsigned long pamt_size) { free_contig_range(pamt_base >> PAGE_SHIFT, pamt_size >> PAGE_SHIFT); } -static void tdmr_free_pamt(struct tdmr_info *tdmr) +static __init void tdmr_free_pamt(struct tdmr_info *tdmr) { tdmr_do_pamt_func(tdmr, free_pamt); } -static void tdmrs_free_pamt_all(struct tdmr_info_list *tdmr_list) +static __init void tdmrs_free_pamt_all(struct tdmr_info_list *tdmr_list) { int i; @@ -614,9 +686,9 @@ static void tdmrs_free_pamt_all(struct tdmr_info_list *tdmr_list) } /* Allocate and set up PAMTs for all TDMRs */ -static int tdmrs_set_up_pamt_all(struct tdmr_info_list *tdmr_list, - struct list_head *tmb_list, - u16 pamt_entry_size[]) +static __init int tdmrs_set_up_pamt_all(struct tdmr_info_list *tdmr_list, + struct list_head *tmb_list, + u16 pamt_entry_size[]) { int i, ret = 0; @@ -665,12 +737,13 @@ void tdx_quirk_reset_page(struct page *page) } EXPORT_SYMBOL_FOR_KVM(tdx_quirk_reset_page); -static void tdmr_quirk_reset_pamt(struct tdmr_info *tdmr) +static __init void tdmr_quirk_reset_pamt(struct tdmr_info *tdmr) + { tdmr_do_pamt_func(tdmr, tdx_quirk_reset_paddr); } -static void tdmrs_quirk_reset_pamt_all(struct tdmr_info_list *tdmr_list) +static __init void tdmrs_quirk_reset_pamt_all(struct tdmr_info_list *tdmr_list) { int i; @@ -678,7 +751,7 @@ static void tdmrs_quirk_reset_pamt_all(struct tdmr_info_list *tdmr_list) tdmr_quirk_reset_pamt(tdmr_entry(tdmr_list, i)); } -static unsigned long tdmrs_count_pamt_kb(struct tdmr_info_list *tdmr_list) +static __init unsigned long tdmrs_count_pamt_kb(struct tdmr_info_list *tdmr_list) { unsigned long pamt_size = 0; int i; @@ -693,8 +766,8 @@ static unsigned long tdmrs_count_pamt_kb(struct tdmr_info_list *tdmr_list) return pamt_size / 1024; } -static int tdmr_add_rsvd_area(struct tdmr_info *tdmr, int *p_idx, u64 addr, - u64 size, u16 max_reserved_per_tdmr) +static __init int tdmr_add_rsvd_area(struct tdmr_info *tdmr, int *p_idx, + u64 addr, u64 size, u16 max_reserved_per_tdmr) { struct tdmr_reserved_area *rsvd_areas = tdmr->reserved_areas; int idx = *p_idx; @@ -727,10 +800,10 @@ static int tdmr_add_rsvd_area(struct tdmr_info *tdmr, int *p_idx, u64 addr, * those holes fall within @tdmr, set up a TDMR reserved area to cover * the hole. */ -static int tdmr_populate_rsvd_holes(struct list_head *tmb_list, - struct tdmr_info *tdmr, - int *rsvd_idx, - u16 max_reserved_per_tdmr) +static __init int tdmr_populate_rsvd_holes(struct list_head *tmb_list, + struct tdmr_info *tdmr, + int *rsvd_idx, + u16 max_reserved_per_tdmr) { struct tdx_memblock *tmb; u64 prev_end; @@ -791,10 +864,10 @@ static int tdmr_populate_rsvd_holes(struct list_head *tmb_list, * overlaps with @tdmr, set up a TDMR reserved area to cover the * overlapping part. */ -static int tdmr_populate_rsvd_pamts(struct tdmr_info_list *tdmr_list, - struct tdmr_info *tdmr, - int *rsvd_idx, - u16 max_reserved_per_tdmr) +static __init int tdmr_populate_rsvd_pamts(struct tdmr_info_list *tdmr_list, + struct tdmr_info *tdmr, + int *rsvd_idx, + u16 max_reserved_per_tdmr) { int i, ret; @@ -829,7 +902,7 @@ static int tdmr_populate_rsvd_pamts(struct tdmr_info_list *tdmr_list, } /* Compare function called by sort() for TDMR reserved areas */ -static int rsvd_area_cmp_func(const void *a, const void *b) +static __init int rsvd_area_cmp_func(const void *a, const void *b) { struct tdmr_reserved_area *r1 = (struct tdmr_reserved_area *)a; struct tdmr_reserved_area *r2 = (struct tdmr_reserved_area *)b; @@ -848,10 +921,10 @@ static int rsvd_area_cmp_func(const void *a, const void *b) * Populate reserved areas for the given @tdmr, including memory holes * (via @tmb_list) and PAMTs (via @tdmr_list). */ -static int tdmr_populate_rsvd_areas(struct tdmr_info *tdmr, - struct list_head *tmb_list, - struct tdmr_info_list *tdmr_list, - u16 max_reserved_per_tdmr) +static __init int tdmr_populate_rsvd_areas(struct tdmr_info *tdmr, + struct list_head *tmb_list, + struct tdmr_info_list *tdmr_list, + u16 max_reserved_per_tdmr) { int ret, rsvd_idx = 0; @@ -876,9 +949,9 @@ static int tdmr_populate_rsvd_areas(struct tdmr_info *tdmr, * Populate reserved areas for all TDMRs in @tdmr_list, including memory * holes (via @tmb_list) and PAMTs. */ -static int tdmrs_populate_rsvd_areas_all(struct tdmr_info_list *tdmr_list, - struct list_head *tmb_list, - u16 max_reserved_per_tdmr) +static __init int tdmrs_populate_rsvd_areas_all(struct tdmr_info_list *tdmr_list, + struct list_head *tmb_list, + u16 max_reserved_per_tdmr) { int i; @@ -899,9 +972,9 @@ static int tdmrs_populate_rsvd_areas_all(struct tdmr_info_list *tdmr_list, * to cover all TDX memory regions in @tmb_list based on the TDX module * TDMR global information in @sysinfo_tdmr. */ -static int construct_tdmrs(struct list_head *tmb_list, - struct tdmr_info_list *tdmr_list, - struct tdx_sys_info_tdmr *sysinfo_tdmr) +static __init int construct_tdmrs(struct list_head *tmb_list, + struct tdmr_info_list *tdmr_list, + struct tdx_sys_info_tdmr *sysinfo_tdmr) { u16 pamt_entry_size[TDX_PS_NR] = { sysinfo_tdmr->pamt_4k_entry_size, @@ -933,7 +1006,8 @@ static int construct_tdmrs(struct list_head *tmb_list, return ret; } -static int config_tdx_module(struct tdmr_info_list *tdmr_list, u64 global_keyid) +static __init int config_tdx_module(struct tdmr_info_list *tdmr_list, + u64 global_keyid) { struct tdx_module_args args = {}; u64 *tdmr_pa_array; @@ -968,7 +1042,7 @@ static int config_tdx_module(struct tdmr_info_list *tdmr_list, u64 global_keyid) return ret; } -static int do_global_key_config(void *unused) +static __init int do_global_key_config(void *unused) { struct tdx_module_args args = {}; @@ -986,7 +1060,7 @@ static int do_global_key_config(void *unused) * KVM) can ensure success by ensuring sufficient CPUs are online and * can run SEAMCALLs. */ -static int config_global_keyid(void) +static __init int config_global_keyid(void) { cpumask_var_t packages; int cpu, ret = -EINVAL; @@ -1026,7 +1100,7 @@ static int config_global_keyid(void) return ret; } -static int init_tdmr(struct tdmr_info *tdmr) +static __init int init_tdmr(struct tdmr_info *tdmr) { u64 next; @@ -1057,7 +1131,7 @@ static int init_tdmr(struct tdmr_info *tdmr) return 0; } -static int init_tdmrs(struct tdmr_info_list *tdmr_list) +static __init int init_tdmrs(struct tdmr_info_list *tdmr_list) { int i; @@ -1076,7 +1150,7 @@ static int init_tdmrs(struct tdmr_info_list *tdmr_list) return 0; } -static int init_tdx_module(void) +static __init int init_tdx_module(void) { int ret; @@ -1157,67 +1231,50 @@ err_free_tdxmem: goto out_put_tdxmem; } -static int __tdx_enable(void) +static __init int tdx_enable(void) { + enum cpuhp_state state; int ret; + if (!cpu_feature_enabled(X86_FEATURE_TDX_HOST_PLATFORM)) { + pr_err("TDX not supported by the host platform\n"); + return -ENODEV; + } + + if (!cpu_feature_enabled(X86_FEATURE_XSAVE)) { + pr_err("XSAVE is required for TDX\n"); + return -EINVAL; + } + + if (!cpu_feature_enabled(X86_FEATURE_MOVDIR64B)) { + pr_err("MOVDIR64B is required for TDX\n"); + return -EINVAL; + } + + if (!cpu_feature_enabled(X86_FEATURE_SELFSNOOP)) { + pr_err("Self-snoop is required for TDX\n"); + return -ENODEV; + } + + state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "virt/tdx:online", + tdx_online_cpu, tdx_offline_cpu); + if (state < 0) + return state; + ret = init_tdx_module(); if (ret) { - pr_err("module initialization failed (%d)\n", ret); - tdx_module_status = TDX_MODULE_ERROR; + pr_err("TDX-Module initialization failed (%d)\n", ret); + cpuhp_remove_state(state); return ret; } - pr_info("module initialized\n"); - tdx_module_status = TDX_MODULE_INITIALIZED; + register_syscore(&tdx_syscore); + tdx_module_initialized = true; + pr_info("TDX-Module initialized\n"); return 0; } - -/** - * tdx_enable - Enable TDX module to make it ready to run TDX guests - * - * This function assumes the caller has: 1) held read lock of CPU hotplug - * lock to prevent any new cpu from becoming online; 2) done both VMXON - * and tdx_cpu_enable() on all online cpus. - * - * This function requires there's at least one online cpu for each CPU - * package to succeed. - * - * This function can be called in parallel by multiple callers. - * - * Return 0 if TDX is enabled successfully, otherwise error. - */ -int tdx_enable(void) -{ - int ret; - - if (!boot_cpu_has(X86_FEATURE_TDX_HOST_PLATFORM)) - return -ENODEV; - - lockdep_assert_cpus_held(); - - mutex_lock(&tdx_module_lock); - - switch (tdx_module_status) { - case TDX_MODULE_UNINITIALIZED: - ret = __tdx_enable(); - break; - case TDX_MODULE_INITIALIZED: - /* Already initialized, great, tell the caller. */ - ret = 0; - break; - default: - /* Failed to initialize in the previous attempts */ - ret = -EINVAL; - break; - } - - mutex_unlock(&tdx_module_lock); - - return ret; -} -EXPORT_SYMBOL_FOR_KVM(tdx_enable); +subsys_initcall(tdx_enable); static bool is_pamt_page(unsigned long phys) { @@ -1468,15 +1525,10 @@ void __init tdx_init(void) const struct tdx_sys_info *tdx_get_sysinfo(void) { - const struct tdx_sys_info *p = NULL; + if (!tdx_module_initialized) + return NULL; - /* Make sure all fields in @tdx_sysinfo have been populated */ - mutex_lock(&tdx_module_lock); - if (tdx_module_status == TDX_MODULE_INITIALIZED) - p = (const struct tdx_sys_info *)&tdx_sysinfo; - mutex_unlock(&tdx_module_lock); - - return p; + return (const struct tdx_sys_info *)&tdx_sysinfo; } EXPORT_SYMBOL_FOR_KVM(tdx_get_sysinfo); diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h index 82bb82be8567..dde219c823b4 100644 --- a/arch/x86/virt/vmx/tdx/tdx.h +++ b/arch/x86/virt/vmx/tdx/tdx.h @@ -91,14 +91,6 @@ struct tdmr_info { * Do not put any hardware-defined TDX structure representations below * this comment! */ - -/* Kernel defined TDX module status during module initialization. */ -enum tdx_module_status_t { - TDX_MODULE_UNINITIALIZED, - TDX_MODULE_INITIALIZED, - TDX_MODULE_ERROR -}; - struct tdx_memblock { struct list_head list; unsigned long start_pfn; diff --git a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c index 4c9917a9c2c3..c7db393a9cfb 100644 --- a/arch/x86/virt/vmx/tdx/tdx_global_metadata.c +++ b/arch/x86/virt/vmx/tdx/tdx_global_metadata.c @@ -7,7 +7,7 @@ * Include this file to other C file instead. */ -static int get_tdx_sys_info_version(struct tdx_sys_info_version *sysinfo_version) +static __init int get_tdx_sys_info_version(struct tdx_sys_info_version *sysinfo_version) { int ret = 0; u64 val; @@ -22,7 +22,7 @@ static int get_tdx_sys_info_version(struct tdx_sys_info_version *sysinfo_version return ret; } -static int get_tdx_sys_info_features(struct tdx_sys_info_features *sysinfo_features) +static __init int get_tdx_sys_info_features(struct tdx_sys_info_features *sysinfo_features) { int ret = 0; u64 val; @@ -33,7 +33,7 @@ static int get_tdx_sys_info_features(struct tdx_sys_info_features *sysinfo_featu return ret; } -static int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr *sysinfo_tdmr) +static __init int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr *sysinfo_tdmr) { int ret = 0; u64 val; @@ -52,7 +52,7 @@ static int get_tdx_sys_info_tdmr(struct tdx_sys_info_tdmr *sysinfo_tdmr) return ret; } -static int get_tdx_sys_info_td_ctrl(struct tdx_sys_info_td_ctrl *sysinfo_td_ctrl) +static __init int get_tdx_sys_info_td_ctrl(struct tdx_sys_info_td_ctrl *sysinfo_td_ctrl) { int ret = 0; u64 val; @@ -67,7 +67,7 @@ static int get_tdx_sys_info_td_ctrl(struct tdx_sys_info_td_ctrl *sysinfo_td_ctrl return ret; } -static int get_tdx_sys_info_td_conf(struct tdx_sys_info_td_conf *sysinfo_td_conf) +static __init int get_tdx_sys_info_td_conf(struct tdx_sys_info_td_conf *sysinfo_td_conf) { int ret = 0; u64 val; @@ -100,7 +100,7 @@ static int get_tdx_sys_info_td_conf(struct tdx_sys_info_td_conf *sysinfo_td_conf return ret; } -static int get_tdx_sys_info(struct tdx_sys_info *sysinfo) +static __init int get_tdx_sys_info(struct tdx_sys_info *sysinfo) { int ret = 0; diff --git a/arch/x86/xen/mmu_pv.c b/arch/x86/xen/mmu_pv.c index c80d0058efd1..3eee5f84f8a7 100644 --- a/arch/x86/xen/mmu_pv.c +++ b/arch/x86/xen/mmu_pv.c @@ -2145,7 +2145,10 @@ static void xen_set_fixmap(unsigned idx, phys_addr_t phys, pgprot_t prot) static void xen_enter_lazy_mmu(void) { - enter_lazy(XEN_LAZY_MMU); + preempt_disable(); + if (xen_get_lazy_mode() != XEN_LAZY_MMU) + enter_lazy(XEN_LAZY_MMU); + preempt_enable(); } static void xen_flush_lazy_mmu(void) @@ -2182,7 +2185,8 @@ static void xen_leave_lazy_mmu(void) { preempt_disable(); xen_mc_flush(); - leave_lazy(XEN_LAZY_MMU); + if (xen_get_lazy_mode() != XEN_LAZY_NONE) + leave_lazy(XEN_LAZY_MMU); preempt_enable(); } diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index ac8021c3a997..41251d4cf953 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -655,7 +655,7 @@ static void __init xen_e820_swap_entry_with_ram(struct e820_entry *swap_entry) /* Fill new entry (keep size and page offset). */ entry->type = swap_entry->type; entry->addr = entry_end - swap_size + - swap_addr - swap_entry->addr; + swap_entry->addr - swap_addr; entry->size = swap_entry->size; /* Convert old entry to RAM, align to pages. */ @@ -695,17 +695,22 @@ static void __init xen_e820_resolve_conflicts(phys_addr_t start, return; end = start + size; - entry = xen_e820_table.entries; + mapcnt = 0; - for (mapcnt = 0; mapcnt < xen_e820_table.nr_entries; mapcnt++) { + while (mapcnt < xen_e820_table.nr_entries) { + entry = xen_e820_table.entries + mapcnt; if (entry->addr >= end) return; if (entry->addr + entry->size > start && - entry->type == E820_TYPE_NVS) + entry->type == E820_TYPE_NVS) { xen_e820_swap_entry_with_ram(entry); + /* E820 map has been changed, restart loop! */ + mapcnt = 0; + continue; + } - entry++; + mapcnt++; } } diff --git a/arch/xtensa/include/asm/ucontext.h b/arch/xtensa/include/asm/ucontext.h deleted file mode 100644 index 94c94ed3e00a..000000000000 --- a/arch/xtensa/include/asm/ucontext.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * include/asm-xtensa/ucontext.h - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2001 - 2005 Tensilica Inc. - */ - -#ifndef _XTENSA_UCONTEXT_H -#define _XTENSA_UCONTEXT_H - -struct ucontext { - unsigned long uc_flags; - struct ucontext *uc_link; - stack_t uc_stack; - struct sigcontext uc_mcontext; - sigset_t uc_sigmask; /* mask last for extensibility */ -}; - -#endif /* _XTENSA_UCONTEXT_H */ diff --git a/arch/xtensa/include/uapi/asm/Kbuild b/arch/xtensa/include/uapi/asm/Kbuild index b97c552db3c5..da95c5a0301f 100644 --- a/arch/xtensa/include/uapi/asm/Kbuild +++ b/arch/xtensa/include/uapi/asm/Kbuild @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 generated-y += unistd_32.h +generic-y += ucontext.h diff --git a/arch/xtensa/platforms/iss/setup.c b/arch/xtensa/platforms/iss/setup.c index 0f1fe132691e..21283acab1a8 100644 --- a/arch/xtensa/platforms/iss/setup.c +++ b/arch/xtensa/platforms/iss/setup.c @@ -32,8 +32,7 @@ static int iss_power_off(struct sys_off_data *unused) return NOTIFY_DONE; } -static int iss_restart(struct notifier_block *this, - unsigned long event, void *ptr) +static int iss_restart(struct sys_off_data *unused) { /* Flush and reset the mmu, simulate a processor reset, and * jump to the reset vector. */ @@ -42,10 +41,6 @@ static int iss_restart(struct notifier_block *this, return NOTIFY_DONE; } -static struct notifier_block iss_restart_block = { - .notifier_call = iss_restart, -}; - static int iss_panic_event(struct notifier_block *this, unsigned long event, void *ptr) { @@ -84,7 +79,9 @@ void __init platform_setup(char **p_cmdline) } atomic_notifier_chain_register(&panic_notifier_list, &iss_panic_block); - register_restart_handler(&iss_restart_block); + register_sys_off_handler(SYS_OFF_MODE_RESTART, + SYS_OFF_PRIO_PLATFORM, + iss_restart, NULL); register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_PLATFORM, iss_power_off, NULL); diff --git a/arch/xtensa/platforms/xt2000/setup.c b/arch/xtensa/platforms/xt2000/setup.c index 258e01a51fd8..eda4c15c6826 100644 --- a/arch/xtensa/platforms/xt2000/setup.c +++ b/arch/xtensa/platforms/xt2000/setup.c @@ -50,8 +50,7 @@ static int xt2000_power_off(struct sys_off_data *unused) return NOTIFY_DONE; } -static int xt2000_restart(struct notifier_block *this, - unsigned long event, void *ptr) +static int xt2000_restart(struct sys_off_data *unused) { /* Flush and reset the mmu, simulate a processor reset, and * jump to the reset vector. */ @@ -60,10 +59,6 @@ static int xt2000_restart(struct notifier_block *this, return NOTIFY_DONE; } -static struct notifier_block xt2000_restart_block = { - .notifier_call = xt2000_restart, -}; - void __init platform_setup(char** cmdline) { led_print (0, "LINUX "); @@ -140,7 +135,9 @@ static int __init xt2000_setup_devinit(void) platform_device_register(&xt2000_serial8250_device); platform_device_register(&xt2000_sonic_device); mod_timer(&heartbeat_timer, jiffies + HZ / 2); - register_restart_handler(&xt2000_restart_block); + register_sys_off_handler(SYS_OFF_MODE_RESTART, + SYS_OFF_PRIO_PLATFORM, + xt2000_restart, NULL); register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_DEFAULT, xt2000_power_off, NULL); diff --git a/arch/xtensa/platforms/xtfpga/setup.c b/arch/xtensa/platforms/xtfpga/setup.c index a2432f081710..67d7f37f1802 100644 --- a/arch/xtensa/platforms/xtfpga/setup.c +++ b/arch/xtensa/platforms/xtfpga/setup.c @@ -42,8 +42,7 @@ static int xtfpga_power_off(struct sys_off_data *unused) return NOTIFY_DONE; } -static int xtfpga_restart(struct notifier_block *this, - unsigned long event, void *ptr) +static int xtfpga_restart(struct sys_off_data *unused) { /* Try software reset first. */ WRITE_ONCE(*(u32 *)XTFPGA_SWRST_VADDR, 0xdead); @@ -56,10 +55,6 @@ static int xtfpga_restart(struct notifier_block *this, return NOTIFY_DONE; } -static struct notifier_block xtfpga_restart_block = { - .notifier_call = xtfpga_restart, -}; - #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT void __init platform_calibrate_ccount(void) @@ -71,7 +66,9 @@ void __init platform_calibrate_ccount(void) static void __init xtfpga_register_handlers(void) { - register_restart_handler(&xtfpga_restart_block); + register_sys_off_handler(SYS_OFF_MODE_RESTART, + SYS_OFF_PRIO_PLATFORM, + xtfpga_restart, NULL); register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_DEFAULT, xtfpga_power_off, NULL); diff --git a/block/bio-integrity-auto.c b/block/bio-integrity-auto.c index ebd17f47e0f9..353eed632fcc 100644 --- a/block/bio-integrity-auto.c +++ b/block/bio-integrity-auto.c @@ -125,7 +125,7 @@ static int __init blk_integrity_auto_init(void) * Make it highpri CPU intensive wq with max concurrency of 1. */ kintegrityd_wq = alloc_workqueue("kintegrityd", WQ_MEM_RECLAIM | - WQ_HIGHPRI | WQ_CPU_INTENSIVE, 1); + WQ_HIGHPRI | WQ_CPU_INTENSIVE | WQ_PERCPU, 1); if (!kintegrityd_wq) panic("Failed to create kintegrityd\n"); return 0; diff --git a/block/bio-integrity.c b/block/bio-integrity.c index e79eaf047794..e796de1a749e 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -231,10 +231,10 @@ int bio_integrity_add_page(struct bio *bio, struct page *page, if (bip->bip_vcnt > 0) { struct bio_vec *bv = &bip->bip_vec[bip->bip_vcnt - 1]; - if (!zone_device_pages_have_same_pgmap(bv->bv_page, page)) + if (!zone_device_pages_compatible(bv->bv_page, page)) return 0; - - if (bvec_try_merge_hw_page(q, bv, page, len, offset)) { + if (zone_device_pages_have_same_pgmap(bv->bv_page, page) && + bvec_try_merge_hw_page(q, bv, page, len, offset)) { bip->bip_iter.bi_size += len; return len; } @@ -308,7 +308,6 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, } bip->bip_flags |= BIP_COPY_USER; - bip->bip_vcnt = nr_vecs; return 0; free_bip: bio_integrity_free(bio); @@ -403,6 +402,24 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) if (unlikely(ret < 0)) goto free_bvec; + /* + * Handle partial pinning. This can happen when pin_user_pages_fast() + * returns fewer pages than requested. + */ + if (user_backed_iter(iter) && unlikely(ret != bytes)) { + if (ret > 0) { + int npinned = DIV_ROUND_UP(offset + ret, PAGE_SIZE); + int i; + + for (i = 0; i < npinned; i++) + unpin_user_page(pages[i]); + } + if (pages != stack_pages) + kvfree(pages); + ret = -EFAULT; + goto free_bvec; + } + nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset, &is_p2p); if (pages != stack_pages) diff --git a/block/bio.c b/block/bio.c index 641ef0928d73..5f10900b3f42 100644 --- a/block/bio.c +++ b/block/bio.c @@ -544,7 +544,8 @@ struct bio *bio_alloc_bioset(struct block_device *bdev, unsigned short nr_vecs, if (WARN_ON_ONCE(!mempool_initialized(&bs->bvec_pool) && nr_vecs > 0)) return NULL; - gfp = try_alloc_gfp(gfp); + if (saved_gfp & __GFP_DIRECT_RECLAIM) + gfp = try_alloc_gfp(gfp); if (bs->cache && nr_vecs <= BIO_INLINE_VECS) { /* * Set REQ_ALLOC_CACHE even if no cached bio is available to @@ -1048,10 +1049,10 @@ int bio_add_page(struct bio *bio, struct page *page, if (bio->bi_vcnt > 0) { struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1]; - if (!zone_device_pages_have_same_pgmap(bv->bv_page, page)) + if (!zone_device_pages_compatible(bv->bv_page, page)) return 0; - - if (bvec_try_merge_page(bv, page, len, offset)) { + if (zone_device_pages_have_same_pgmap(bv->bv_page, page) && + bvec_try_merge_page(bv, page, len, offset)) { bio->bi_iter.bi_size += len; return len; } @@ -1278,11 +1279,12 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, return bio_iov_iter_align_down(bio, iter, len_align_mask); } -static struct folio *folio_alloc_greedy(gfp_t gfp, size_t *size) +static struct folio *folio_alloc_greedy(gfp_t gfp, size_t *size, + size_t minsize) { struct folio *folio; - while (*size > PAGE_SIZE) { + while (*size > minsize) { folio = folio_alloc(gfp | __GFP_NORETRY, get_order(*size)); if (folio) return folio; @@ -1306,7 +1308,7 @@ static void bio_free_folios(struct bio *bio) } static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, - size_t maxlen) + size_t maxlen, size_t minsize) { size_t total_len = min(maxlen, iov_iter_count(iter)); @@ -1321,13 +1323,13 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, size_t this_len = min(total_len, SZ_1M); struct folio *folio; - if (this_len > PAGE_SIZE * 2) + if (this_len > minsize * 2) this_len = rounddown_pow_of_two(this_len); if (bio->bi_iter.bi_size > BIO_MAX_SIZE - this_len) break; - folio = folio_alloc_greedy(GFP_KERNEL, &this_len); + folio = folio_alloc_greedy(GFP_KERNEL, &this_len, minsize); if (!folio) break; bio_add_folio_nofail(bio, folio, this_len, 0); @@ -1343,16 +1345,16 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, if (!bio->bi_iter.bi_size) return -ENOMEM; - return 0; + return bio_iov_iter_align_down(bio, iter, minsize - 1); } static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter, - size_t maxlen) + size_t maxlen, size_t minsize) { size_t len = min3(iov_iter_count(iter), maxlen, SZ_1M); struct folio *folio; - folio = folio_alloc_greedy(GFP_KERNEL, &len); + folio = folio_alloc_greedy(GFP_KERNEL, &len, minsize); if (!folio) return -ENOMEM; @@ -1381,7 +1383,7 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter, bvec_set_folio(&bio->bi_io_vec[0], folio, bio->bi_iter.bi_size, 0); if (iov_iter_extract_will_pin(iter)) bio_set_flag(bio, BIO_PAGE_PINNED); - return 0; + return bio_iov_iter_align_down(bio, iter, minsize - 1); } /** @@ -1389,6 +1391,7 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter, * @bio: bio to send * @iter: iter to read from / write into * @maxlen: maximum size to bounce + * @minsize: minimum folio allocation size * * Helper for direct I/O implementations that need to bounce buffer because * we need to checksum the data or perform other operations that require @@ -1396,11 +1399,12 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter, * copies the data into it. Needs to be paired with bio_iov_iter_unbounce() * called on completion. */ -int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter, size_t maxlen) +int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter, size_t maxlen, + size_t minsize) { if (op_is_write(bio_op(bio))) - return bio_iov_iter_bounce_write(bio, iter, maxlen); - return bio_iov_iter_bounce_read(bio, iter, maxlen); + return bio_iov_iter_bounce_write(bio, iter, maxlen, minsize); + return bio_iov_iter_bounce_read(bio, iter, maxlen, minsize); } static void bvec_unpin(struct bio_vec *bv, bool mark_dirty) @@ -1958,7 +1962,7 @@ int bioset_init(struct bio_set *bs, if (flags & BIOSET_NEED_RESCUER) { bs->rescue_workqueue = alloc_workqueue("bioset", - WQ_MEM_RECLAIM, 0); + WQ_MEM_RECLAIM | WQ_PERCPU, 0); if (!bs->rescue_workqueue) goto bad; } diff --git a/block/blk-core.c b/block/blk-core.c index 474700ffaa1c..17450058ea6d 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1282,7 +1282,7 @@ int __init blk_dev_init(void) /* used for unplugging and affects IO latency/throughput - HIGHPRI */ kblockd_workqueue = alloc_workqueue("kblockd", - WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); + WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU, 0); if (!kblockd_workqueue) panic("Failed to create kblockd\n"); diff --git a/block/blk-mq.c b/block/blk-mq.c index 4c5c16cce4f8..d0c37daf568f 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -3307,6 +3307,25 @@ blk_status_t blk_insert_cloned_request(struct request *rq) return BLK_STS_IOERR; } + /* + * Integrity segment counting depends on the same queue limits + * (virt_boundary_mask, seg_boundary_mask, max_segment_size) that + * vary across stacked queues, so recompute against the bottom + * queue just like nr_phys_segments above. + */ + if (blk_integrity_rq(rq) && rq->bio) { + unsigned short max_int_segs = queue_max_integrity_segments(q); + + rq->nr_integrity_segments = + blk_rq_count_integrity_sg(rq->q, rq->bio); + if (rq->nr_integrity_segments > max_int_segs) { + printk(KERN_ERR "%s: over max integrity segments limit. (%u > %u)\n", + __func__, rq->nr_integrity_segments, + max_int_segs); + return BLK_STS_IOERR; + } + } + if (q->disk && should_fail_request(q->disk->part0, blk_rq_bytes(rq))) return BLK_STS_IOERR; diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 97188a795848..cabf91f0d0dc 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1839,7 +1839,7 @@ void blk_throtl_exit(struct gendisk *disk) static int __init throtl_init(void) { - kthrotld_workqueue = alloc_workqueue("kthrotld", WQ_MEM_RECLAIM, 0); + kthrotld_workqueue = alloc_workqueue("kthrotld", WQ_MEM_RECLAIM | WQ_PERCPU, 0); if (!kthrotld_workqueue) panic("Failed to create kthrotld\n"); diff --git a/block/blk-zoned.c b/block/blk-zoned.c index 30cad2bb9291..42ef830054dc 100644 --- a/block/blk-zoned.c +++ b/block/blk-zoned.c @@ -623,6 +623,28 @@ static void disk_mark_zone_wplug_dead(struct blk_zone_wplug *zwplug) } } +static inline bool disk_check_zone_wplug_dead(struct blk_zone_wplug *zwplug) +{ + if (!(zwplug->flags & BLK_ZONE_WPLUG_DEAD)) + return false; + + /* + * If a new write is received right after a zone reset completes and + * while the disk_zone_wplugs_worker() thread has not yet released the + * reference on the zone write plug after processing the last write to + * the zone, then the new write BIO will see the zone write plug marked + * as dead. This case is however a false positive and a perfectly valid + * pattern. In such case, restore the zone write plug to a live one. + */ + if (!zwplug->wp_offset && bio_list_empty(&zwplug->bio_list)) { + zwplug->flags &= ~BLK_ZONE_WPLUG_DEAD; + refcount_inc(&zwplug->ref); + return false; + } + + return true; +} + static bool disk_zone_wplug_submit_bio(struct gendisk *disk, struct blk_zone_wplug *zwplug); @@ -1444,12 +1466,12 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs) spin_lock_irqsave(&zwplug->lock, flags); /* - * If we got a zone write plug marked as dead, then the user is issuing - * writes to a full zone, or without synchronizing with zone reset or - * zone finish operations. In such case, fail the BIO to signal this - * invalid usage. + * Check if we got a zone write plug marked as dead. If yes, then the + * user is likely issuing writes to a full zone, or without + * synchronizing with zone reset or zone finish operations. In such + * case, fail the BIO to signal this invalid usage. */ - if (zwplug->flags & BLK_ZONE_WPLUG_DEAD) { + if (disk_check_zone_wplug_dead(zwplug)) { spin_unlock_irqrestore(&zwplug->lock, flags); disk_put_zone_wplug(zwplug); bio_io_error(bio); diff --git a/block/blk.h b/block/blk.h index ec4674cdf2ea..b998a7761faf 100644 --- a/block/blk.h +++ b/block/blk.h @@ -127,6 +127,8 @@ static inline bool biovec_phys_mergeable(struct request_queue *q, if (addr1 + vec1->bv_len != addr2) return false; + if (!zone_device_pages_have_same_pgmap(vec1->bv_page, vec2->bv_page)) + return false; if (xen_domain() && !xen_biovec_phys_mergeable(vec1, vec2->bv_page)) return false; if ((addr1 | mask) != ((addr2 + vec2->bv_len - 1) | mask)) @@ -134,6 +136,25 @@ static inline bool biovec_phys_mergeable(struct request_queue *q, return true; } +/* + * Check if two pages from potentially different zone device pgmaps can + * coexist as separate bvec entries in the same bio. + * + * The block DMA iterator (blk_dma_map_iter_start) caches the P2PDMA mapping + * state from the first segment and applies it to all subsequent segments, so + * P2PDMA pages from different pgmaps must not be mixed in the same bio. + * + * Other zone device types (FS_DAX, GENERIC) use the same dma_map_phys() path + * as normal RAM. PRIVATE and COHERENT pages never appear in bios. + */ +static inline bool zone_device_pages_compatible(const struct page *a, + const struct page *b) +{ + if (is_pci_p2pdma_page(a) || is_pci_p2pdma_page(b)) + return zone_device_pages_have_same_pgmap(a, b); + return true; +} + static inline bool __bvec_gap_to_prev(const struct queue_limits *lim, struct bio_vec *bprv, unsigned int offset) { diff --git a/block/ioctl.c b/block/ioctl.c index fc3be0549aa7..ab2c9ed79946 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -857,6 +857,8 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) #endif struct blk_iou_cmd { + u64 start; + u64 len; int res; bool nowait; }; @@ -946,23 +948,27 @@ int blkdev_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) { struct block_device *bdev = I_BDEV(cmd->file->f_mapping->host); struct blk_iou_cmd *bic = io_uring_cmd_to_pdu(cmd, struct blk_iou_cmd); - const struct io_uring_sqe *sqe = cmd->sqe; u32 cmd_op = cmd->cmd_op; - uint64_t start, len; - if (unlikely(sqe->ioprio || sqe->__pad1 || sqe->len || - sqe->rw_flags || sqe->file_index)) - return -EINVAL; + /* Read what we need from the SQE on the first issue */ + if (!(issue_flags & IORING_URING_CMD_REISSUE)) { + const struct io_uring_sqe *sqe = cmd->sqe; + + if (unlikely(sqe->ioprio || sqe->__pad1 || sqe->len || + sqe->rw_flags || sqe->file_index)) + return -EINVAL; + + bic->start = READ_ONCE(sqe->addr); + bic->len = READ_ONCE(sqe->addr3); + } bic->res = 0; bic->nowait = issue_flags & IO_URING_F_NONBLOCK; - start = READ_ONCE(sqe->addr); - len = READ_ONCE(sqe->addr3); - switch (cmd_op) { case BLOCK_URING_CMD_DISCARD: - return blkdev_cmd_discard(cmd, bdev, start, len, bic->nowait); + return blkdev_cmd_discard(cmd, bdev, bic->start, bic->len, + bic->nowait); } return -EINVAL; } diff --git a/certs/extract-cert.c b/certs/extract-cert.c index 8c762f908443..f3eb2c492884 100644 --- a/certs/extract-cert.c +++ b/certs/extract-cert.c @@ -41,7 +41,9 @@ void format(void) exit(2); } +#ifdef USE_PKCS11_ENGINE static const char *key_pass; +#endif static BIO *wb; static char *cert_dst; static bool verbose; @@ -133,7 +135,9 @@ int main(int argc, char **argv) if (verbose_env && strchr(verbose_env, '1')) verbose = true; - key_pass = getenv("KBUILD_SIGN_PIN"); +#ifdef USE_PKCS11_ENGINE + key_pass = getenv("KBUILD_SIGN_PIN"); +#endif if (argc != 3) format(); diff --git a/crypto/Kconfig b/crypto/Kconfig index b54a1bef6ade..103d1f58cb7c 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -2,8 +2,6 @@ # # Generic algorithms support # -config XOR_BLOCKS - tristate # # async_tx api: hardware offloaded memory transfer/transform support diff --git a/crypto/Makefile b/crypto/Makefile index d9bec7c6dc41..162242593c7c 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -193,7 +193,6 @@ obj-$(CONFIG_CRYPTO_ECRDSA) += ecrdsa_generic.o # # generic algorithms and the async_tx api # -obj-$(CONFIG_XOR_BLOCKS) += xor.o obj-$(CONFIG_ASYNC_CORE) += async_tx/ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys/ crypto_simd-y := simd.o diff --git a/crypto/acompress.c b/crypto/acompress.c index 1f9cb04b447f..6025c1acce49 100644 --- a/crypto/acompress.c +++ b/crypto/acompress.c @@ -169,15 +169,13 @@ static void acomp_save_req(struct acomp_req *req, crypto_completion_t cplt) state->compl = req->base.complete; state->data = req->base.data; req->base.complete = cplt; - req->base.data = state; + req->base.data = req; } static void acomp_restore_req(struct acomp_req *req) { - struct acomp_req_chain *state = req->base.data; - - req->base.complete = state->compl; - req->base.data = state->data; + req->base.complete = req->chain.compl; + req->base.data = req->chain.data; } static void acomp_reqchain_virt(struct acomp_req *req) diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index f8bd45f7dc83..cb651ab58d62 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -72,8 +72,10 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, struct af_alg_ctx *ctx = ask->private; struct crypto_aead *tfm = pask->private; unsigned int as = crypto_aead_authsize(tfm); + unsigned int ivsize = crypto_aead_ivsize(tfm); struct af_alg_async_req *areq; struct scatterlist *rsgl_src, *tsgl_src = NULL; + void *iv; int err = 0; size_t used = 0; /* [in] TX bufs to be en/decrypted */ size_t outlen = 0; /* [out] RX bufs produced by kernel */ @@ -125,10 +127,14 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, /* Allocate cipher request for current operation. */ areq = af_alg_alloc_areq(sk, sizeof(struct af_alg_async_req) + - crypto_aead_reqsize(tfm)); + crypto_aead_reqsize(tfm) + ivsize); if (IS_ERR(areq)) return PTR_ERR(areq); + iv = (u8 *)aead_request_ctx(&areq->cra_u.aead_req) + + crypto_aead_reqsize(tfm); + memcpy(iv, ctx->iv, ivsize); + /* convert iovecs of output buffers into RX SGL */ err = af_alg_get_rsgl(sk, msg, flags, areq, outlen, &usedpages); if (err) @@ -187,7 +193,7 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, /* Initialize the crypto operation */ aead_request_set_crypt(&areq->cra_u.aead_req, tsgl_src, - areq->first_rsgl.sgl.sgt.sgl, used, ctx->iv); + areq->first_rsgl.sgl.sgt.sgl, used, iv); aead_request_set_ad(&areq->cra_u.aead_req, ctx->aead_assoclen); aead_request_set_tfm(&areq->cra_u.aead_req, tfm); diff --git a/crypto/async_tx/async_xor.c b/crypto/async_tx/async_xor.c index 2c499654a36c..84458375b202 100644 --- a/crypto/async_tx/async_xor.c +++ b/crypto/async_tx/async_xor.c @@ -103,7 +103,6 @@ do_sync_xor_offs(struct page *dest, unsigned int offset, { int i; int xor_src_cnt = 0; - int src_off = 0; void *dest_buf; void **srcs; @@ -117,23 +116,12 @@ do_sync_xor_offs(struct page *dest, unsigned int offset, if (src_list[i]) srcs[xor_src_cnt++] = page_address(src_list[i]) + (src_offs ? src_offs[i] : offset); - src_cnt = xor_src_cnt; + /* set destination address */ dest_buf = page_address(dest) + offset; - if (submit->flags & ASYNC_TX_XOR_ZERO_DST) memset(dest_buf, 0, len); - - while (src_cnt > 0) { - /* process up to 'MAX_XOR_BLOCKS' sources */ - xor_src_cnt = min(src_cnt, MAX_XOR_BLOCKS); - xor_blocks(xor_src_cnt, len, dest_buf, &srcs[src_off]); - - /* drop completed sources */ - src_cnt -= xor_src_cnt; - src_off += xor_src_cnt; - } - + xor_gen(dest_buf, srcs, xor_src_cnt, len); async_tx_sync_epilog(submit); } @@ -168,11 +156,10 @@ dma_xor_aligned_offsets(struct dma_device *device, unsigned int offset, * * honored flags: ASYNC_TX_ACK, ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DST * - * xor_blocks always uses the dest as a source so the - * ASYNC_TX_XOR_ZERO_DST flag must be set to not include dest data in - * the calculation. The assumption with dma engines is that they only - * use the destination buffer as a source when it is explicitly specified - * in the source list. + * xor_gen always uses the dest as a source so the ASYNC_TX_XOR_ZERO_DST flag + * must be set to not include dest data in the calculation. The assumption with + * dma engines is that they only use the destination buffer as a source when it + * is explicitly specified in the source list. * * src_list note: if the dest is also a source it must be at index zero. * The contents of this array will be overwritten if a scribble region @@ -259,11 +246,10 @@ EXPORT_SYMBOL_GPL(async_xor_offs); * * honored flags: ASYNC_TX_ACK, ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DST * - * xor_blocks always uses the dest as a source so the - * ASYNC_TX_XOR_ZERO_DST flag must be set to not include dest data in - * the calculation. The assumption with dma engines is that they only - * use the destination buffer as a source when it is explicitly specified - * in the source list. + * xor_gen always uses the dest as a source so the ASYNC_TX_XOR_ZERO_DST flag + * must be set to not include dest data in the calculation. The assumption with + * dma engines is that they only use the destination buffer as a source when it + * is explicitly specified in the source list. * * src_list note: if the dest is also a source it must be at index zero. * The contents of this array will be overwritten if a scribble region diff --git a/crypto/authencesn.c b/crypto/authencesn.c index c0a01d738d9b..522df41365d8 100644 --- a/crypto/authencesn.c +++ b/crypto/authencesn.c @@ -228,9 +228,11 @@ static int crypto_authenc_esn_decrypt_tail(struct aead_request *req, decrypt: - if (src != dst) - src = scatterwalk_ffwd(areq_ctx->src, src, assoclen); dst = scatterwalk_ffwd(areq_ctx->dst, dst, assoclen); + if (req->src == req->dst) + src = dst; + else + src = scatterwalk_ffwd(areq_ctx->src, src, assoclen); skcipher_request_set_tfm(skreq, ctx->enc); skcipher_request_set_callback(skreq, flags, @@ -388,6 +390,11 @@ static int crypto_authenc_esn_create(struct crypto_template *tmpl, auth = crypto_spawn_ahash_alg(&ctx->auth); auth_base = &auth->base; + if (auth->digestsize > 0 && auth->digestsize < 4) { + err = -EINVAL; + goto err_free_inst; + } + err = crypto_grab_skcipher(&ctx->enc, aead_crypto_instance(inst), crypto_attr_alg_name(tb[2]), 0, mask); if (err) diff --git a/crypto/krb5enc.c b/crypto/krb5enc.c index 1bfe8370cf94..fefa8d2c7532 100644 --- a/crypto/krb5enc.c +++ b/crypto/krb5enc.c @@ -39,12 +39,6 @@ struct krb5enc_request_ctx { char tail[]; }; -static void krb5enc_request_complete(struct aead_request *req, int err) -{ - if (err != -EINPROGRESS) - aead_request_complete(req, err); -} - /** * crypto_krb5enc_extractkeys - Extract Ke and Ki keys from the key blob. * @keys: Where to put the key sizes and pointers @@ -127,7 +121,7 @@ static void krb5enc_encrypt_done(void *data, int err) { struct aead_request *req = data; - krb5enc_request_complete(req, err); + aead_request_complete(req, err); } /* @@ -188,14 +182,16 @@ static void krb5enc_encrypt_ahash_done(void *data, int err) struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff); if (err) - return krb5enc_request_complete(req, err); + goto out; krb5enc_insert_checksum(req, ahreq->result); - err = krb5enc_dispatch_encrypt(req, - aead_request_flags(req) & ~CRYPTO_TFM_REQ_MAY_SLEEP); - if (err != -EINPROGRESS) - aead_request_complete(req, err); + err = krb5enc_dispatch_encrypt(req, 0); + if (err == -EINPROGRESS) + return; + +out: + aead_request_complete(req, err); } /* @@ -265,17 +261,16 @@ static void krb5enc_decrypt_hash_done(void *data, int err) { struct aead_request *req = data; - if (err) - return krb5enc_request_complete(req, err); - - err = krb5enc_verify_hash(req); - krb5enc_request_complete(req, err); + if (!err) + err = krb5enc_verify_hash(req); + aead_request_complete(req, err); } /* * Dispatch the hashing of the plaintext after we've done the decryption. */ -static int krb5enc_dispatch_decrypt_hash(struct aead_request *req) +static int krb5enc_dispatch_decrypt_hash(struct aead_request *req, + unsigned int flags) { struct crypto_aead *krb5enc = crypto_aead_reqtfm(req); struct aead_instance *inst = aead_alg_instance(krb5enc); @@ -291,7 +286,7 @@ static int krb5enc_dispatch_decrypt_hash(struct aead_request *req) ahash_request_set_tfm(ahreq, auth); ahash_request_set_crypt(ahreq, req->dst, hash, req->assoclen + req->cryptlen - authsize); - ahash_request_set_callback(ahreq, aead_request_flags(req), + ahash_request_set_callback(ahreq, flags, krb5enc_decrypt_hash_done, req); err = crypto_ahash_digest(ahreq); @@ -301,6 +296,21 @@ static int krb5enc_dispatch_decrypt_hash(struct aead_request *req) return krb5enc_verify_hash(req); } +static void krb5enc_decrypt_done(void *data, int err) +{ + struct aead_request *req = data; + + if (err) + goto out; + + err = krb5enc_dispatch_decrypt_hash(req, 0); + if (err == -EINPROGRESS) + return; + +out: + aead_request_complete(req, err); +} + /* * Dispatch the decryption of the ciphertext. */ @@ -324,7 +334,7 @@ static int krb5enc_dispatch_decrypt(struct aead_request *req) skcipher_request_set_tfm(skreq, ctx->enc); skcipher_request_set_callback(skreq, aead_request_flags(req), - req->base.complete, req->base.data); + krb5enc_decrypt_done, req); skcipher_request_set_crypt(skreq, src, dst, req->cryptlen - authsize, req->iv); @@ -339,7 +349,7 @@ static int krb5enc_decrypt(struct aead_request *req) if (err < 0) return err; - return krb5enc_dispatch_decrypt_hash(req); + return krb5enc_dispatch_decrypt_hash(req, aead_request_flags(req)); } static int krb5enc_init_tfm(struct crypto_aead *tfm) diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c index c3a9d4f2995c..ed0feaba2383 100644 --- a/crypto/pcrypt.c +++ b/crypto/pcrypt.c @@ -69,6 +69,9 @@ static void pcrypt_aead_done(void *data, int err) struct pcrypt_request *preq = aead_request_ctx(req); struct padata_priv *padata = pcrypt_request_padata(preq); + if (err == -EINPROGRESS) + return; + padata->info = err; padata_do_serial(padata); @@ -82,7 +85,7 @@ static void pcrypt_aead_enc(struct padata_priv *padata) ret = crypto_aead_encrypt(req); - if (ret == -EINPROGRESS) + if (ret == -EINPROGRESS || ret == -EBUSY) return; padata->info = ret; @@ -133,7 +136,7 @@ static void pcrypt_aead_dec(struct padata_priv *padata) ret = crypto_aead_decrypt(req); - if (ret == -EINPROGRESS) + if (ret == -EINPROGRESS || ret == -EBUSY) return; padata->info = ret; diff --git a/crypto/xor.c b/crypto/xor.c deleted file mode 100644 index f39621a57bb3..000000000000 --- a/crypto/xor.c +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * xor.c : Multiple Devices driver for Linux - * - * Copyright (C) 1996, 1997, 1998, 1999, 2000, - * Ingo Molnar, Matti Aarnio, Jakub Jelinek, Richard Henderson. - * - * Dispatch optimized RAID-5 checksumming functions. - */ - -#define BH_TRACE 0 -#include -#include -#include -#include -#include -#include - -#ifndef XOR_SELECT_TEMPLATE -#define XOR_SELECT_TEMPLATE(x) (x) -#endif - -/* The xor routines to use. */ -static struct xor_block_template *active_template; - -void -xor_blocks(unsigned int src_count, unsigned int bytes, void *dest, void **srcs) -{ - unsigned long *p1, *p2, *p3, *p4; - - p1 = (unsigned long *) srcs[0]; - if (src_count == 1) { - active_template->do_2(bytes, dest, p1); - return; - } - - p2 = (unsigned long *) srcs[1]; - if (src_count == 2) { - active_template->do_3(bytes, dest, p1, p2); - return; - } - - p3 = (unsigned long *) srcs[2]; - if (src_count == 3) { - active_template->do_4(bytes, dest, p1, p2, p3); - return; - } - - p4 = (unsigned long *) srcs[3]; - active_template->do_5(bytes, dest, p1, p2, p3, p4); -} -EXPORT_SYMBOL(xor_blocks); - -/* Set of all registered templates. */ -static struct xor_block_template *__initdata template_list; - -#ifndef MODULE -static void __init do_xor_register(struct xor_block_template *tmpl) -{ - tmpl->next = template_list; - template_list = tmpl; -} - -static int __init register_xor_blocks(void) -{ - active_template = XOR_SELECT_TEMPLATE(NULL); - - if (!active_template) { -#define xor_speed do_xor_register - // register all the templates and pick the first as the default - XOR_TRY_TEMPLATES; -#undef xor_speed - active_template = template_list; - } - return 0; -} -#endif - -#define BENCH_SIZE 4096 -#define REPS 800U - -static void __init -do_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2) -{ - int speed; - unsigned long reps; - ktime_t min, start, t0; - - tmpl->next = template_list; - template_list = tmpl; - - preempt_disable(); - - reps = 0; - t0 = ktime_get(); - /* delay start until time has advanced */ - while ((start = ktime_get()) == t0) - cpu_relax(); - do { - mb(); /* prevent loop optimization */ - tmpl->do_2(BENCH_SIZE, b1, b2); - mb(); - } while (reps++ < REPS || (t0 = ktime_get()) == start); - min = ktime_sub(t0, start); - - preempt_enable(); - - // bytes/ns == GB/s, multiply by 1000 to get MB/s [not MiB/s] - speed = (1000 * reps * BENCH_SIZE) / (unsigned int)ktime_to_ns(min); - tmpl->speed = speed; - - pr_info(" %-16s: %5d MB/sec\n", tmpl->name, speed); -} - -static int __init -calibrate_xor_blocks(void) -{ - void *b1, *b2; - struct xor_block_template *f, *fastest; - - fastest = XOR_SELECT_TEMPLATE(NULL); - - if (fastest) { - printk(KERN_INFO "xor: automatically using best " - "checksumming function %-10s\n", - fastest->name); - goto out; - } - - b1 = (void *) __get_free_pages(GFP_KERNEL, 2); - if (!b1) { - printk(KERN_WARNING "xor: Yikes! No memory available.\n"); - return -ENOMEM; - } - b2 = b1 + 2*PAGE_SIZE + BENCH_SIZE; - - /* - * If this arch/cpu has a short-circuited selection, don't loop through - * all the possible functions, just test the best one - */ - -#define xor_speed(templ) do_xor_speed((templ), b1, b2) - - printk(KERN_INFO "xor: measuring software checksum speed\n"); - template_list = NULL; - XOR_TRY_TEMPLATES; - fastest = template_list; - for (f = fastest; f; f = f->next) - if (f->speed > fastest->speed) - fastest = f; - - pr_info("xor: using function: %s (%d MB/sec)\n", - fastest->name, fastest->speed); - -#undef xor_speed - - free_pages((unsigned long)b1, 2); -out: - active_template = fastest; - return 0; -} - -static __exit void xor_exit(void) { } - -MODULE_DESCRIPTION("RAID-5 checksumming functions"); -MODULE_LICENSE("GPL"); - -#ifndef MODULE -/* when built-in xor.o must initialize before drivers/md/md.o */ -core_initcall(register_xor_blocks); -#endif - -module_init(calibrate_xor_blocks); -module_exit(xor_exit); diff --git a/drivers/Kconfig b/drivers/Kconfig index c0f1fb893ec0..f2bed2ddeb66 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -61,8 +61,6 @@ source "drivers/macintosh/Kconfig" source "drivers/net/Kconfig" -source "drivers/isdn/Kconfig" - # input before char - char/joystick depends on it. As does USB. source "drivers/input/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 53fbd2e0acdd..0841ea851847 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -124,7 +124,6 @@ obj-$(CONFIG_WATCHDOG) += watchdog/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_ACCESSIBILITY) += accessibility/ -obj-$(CONFIG_ISDN) += isdn/ obj-$(CONFIG_EDAC) += edac/ obj-$(CONFIG_EISA) += eisa/ obj-$(CONFIG_PM_OPP) += opp/ diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index 2801378e3e19..3b7b008bccfe 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -537,6 +537,26 @@ static const struct file_operations ivpu_fops = { #endif }; +static int ivpu_gem_prime_handle_to_fd(struct drm_device *dev, struct drm_file *file_priv, + u32 handle, u32 flags, int *prime_fd) +{ + struct drm_gem_object *obj; + + obj = drm_gem_object_lookup(file_priv, handle); + if (!obj) + return -ENOENT; + + if (drm_gem_is_imported(obj)) { + /* Do not allow re-exporting */ + drm_gem_object_put(obj); + return -EOPNOTSUPP; + } + + drm_gem_object_put(obj); + + return drm_gem_prime_handle_to_fd(dev, file_priv, handle, flags, prime_fd); +} + static const struct drm_driver driver = { .driver_features = DRIVER_GEM | DRIVER_COMPUTE_ACCEL, @@ -545,6 +565,7 @@ static const struct drm_driver driver = { .gem_create_object = ivpu_gem_create_object, .gem_prime_import = ivpu_gem_prime_import, + .prime_handle_to_fd = ivpu_gem_prime_handle_to_fd, .ioctls = ivpu_drm_ioctls, .num_ioctls = ARRAY_SIZE(ivpu_drm_ioctls), diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index 95300c2f7d8a..1e4c579d2725 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -606,8 +606,11 @@ static const struct vm_operations_struct drm_vm_ops = { static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) { struct qaic_bo *bo = to_qaic_bo(obj); + unsigned long remap_start; unsigned long offset = 0; + unsigned long remap_end; struct scatterlist *sg; + unsigned long length; int ret = 0; if (drm_gem_is_imported(obj)) @@ -615,11 +618,27 @@ static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struc for (sg = bo->sgt->sgl; sg; sg = sg_next(sg)) { if (sg_page(sg)) { + /* if sg is too large for the VMA, so truncate it to fit */ + if (check_add_overflow(vma->vm_start, offset, &remap_start)) + return -EINVAL; + if (check_add_overflow(remap_start, sg->length, &remap_end)) + return -EINVAL; + + if (remap_end > vma->vm_end) { + if (check_sub_overflow(vma->vm_end, remap_start, &length)) + return -EINVAL; + } else { + length = sg->length; + } + + if (length == 0) + goto out; + ret = remap_pfn_range(vma, vma->vm_start + offset, page_to_pfn(sg_page(sg)), - sg->length, vma->vm_page_prot); + length, vma->vm_page_prot); if (ret) goto out; - offset += sg->length; + offset += length; } } diff --git a/drivers/accel/qaic/qaic_ras.c b/drivers/accel/qaic/qaic_ras.c index cc0b75461e1a..6791af366cba 100644 --- a/drivers/accel/qaic/qaic_ras.c +++ b/drivers/accel/qaic/qaic_ras.c @@ -497,11 +497,11 @@ static void decode_ras_msg(struct qaic_device *qdev, struct ras_data *msg) qdev->ce_count++; break; case UE: - if (qdev->ce_count != UINT_MAX) + if (qdev->ue_count != UINT_MAX) qdev->ue_count++; break; case UE_NF: - if (qdev->ce_count != UINT_MAX) + if (qdev->ue_nf_count != UINT_MAX) qdev->ue_nf_count++; break; default: diff --git a/drivers/accel/rocket/rocket_gem.c b/drivers/accel/rocket/rocket_gem.c index b6a385d2edfc..c8084719208a 100644 --- a/drivers/accel/rocket/rocket_gem.c +++ b/drivers/accel/rocket/rocket_gem.c @@ -145,6 +145,8 @@ int rocket_ioctl_prep_bo(struct drm_device *dev, void *data, struct drm_file *fi ret = dma_resv_wait_timeout(gem_obj->resv, DMA_RESV_USAGE_WRITE, true, timeout); if (!ret) ret = timeout ? -ETIMEDOUT : -EBUSY; + else if (ret > 0) + ret = 0; shmem_obj = &to_rocket_bo(gem_obj)->base; diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index e9e970fd8f33..27f31744f29e 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -192,11 +192,15 @@ static const struct dmi_system_id ac_dmi_table[] __initconst = { static int acpi_ac_probe(struct platform_device *pdev) { - struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); struct power_supply_config psy_cfg = {}; + struct acpi_device *adev; struct acpi_ac *ac; int result; + adev = ACPI_COMPANION(&pdev->dev); + if (!adev) + return -ENODEV; + ac = kzalloc_obj(struct acpi_ac); if (!ac) return -ENOMEM; diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index 0a8e02bc8c8b..ec94b09bb747 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -423,7 +423,11 @@ static void acpi_pad_notify(acpi_handle handle, u32 event, void *data) static int acpi_pad_probe(struct platform_device *pdev) { - struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); + struct acpi_device *adev; + + adev = ACPI_COMPANION(&pdev->dev); + if (!adev) + return -ENODEV; return acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, acpi_pad_notify, adev); diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index b406d7a98996..386fc1abcbdc 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -605,15 +605,12 @@ static umode_t acpi_tad_attr_is_visible(struct kobject *kobj, return 0; } -static const struct attribute_group acpi_tad_attr_group = { +static const struct attribute_group acpi_tad_group = { .attrs = acpi_tad_attrs, .is_visible = acpi_tad_attr_is_visible, }; -static const struct attribute_group *acpi_tad_attr_groups[] = { - &acpi_tad_attr_group, - NULL, -}; +__ATTRIBUTE_GROUPS(acpi_tad); #ifdef CONFIG_RTC_CLASS /* RTC class device interface */ @@ -683,9 +680,8 @@ static int acpi_tad_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t) acpi_tad_rt_to_tm(&rt, &tm_now); - value = ktime_divns(ktime_sub(rtc_tm_to_ktime(t->time), - rtc_tm_to_ktime(tm_now)), NSEC_PER_SEC); - if (value <= 0 || value > U32_MAX) + value = rtc_tm_to_time64(&t->time) - rtc_tm_to_time64(&tm_now); + if (value <= 0 || value >= U32_MAX) return -EINVAL; } @@ -748,8 +744,7 @@ static int acpi_tad_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *t) if (retval != ACPI_TAD_WAKE_DISABLED) { t->enabled = 1; - t->time = rtc_ktime_to_tm(ktime_add_ns(rtc_tm_to_ktime(tm_now), - (u64)retval * NSEC_PER_SEC)); + rtc_time64_to_tm(rtc_tm_to_time64(&tm_now) + retval, &t->time); } else { t->enabled = 0; t->time = tm_now; @@ -795,9 +790,9 @@ static int acpi_tad_disable_timer(struct device *dev, u32 timer_id) return acpi_tad_wake_set(dev, "_STV", timer_id, ACPI_TAD_WAKE_DISABLED); } -static void acpi_tad_remove(struct platform_device *pdev) +static void acpi_tad_remove(void *data) { - struct device *dev = &pdev->dev; + struct device *dev = data; struct acpi_tad_driver_data *dd = dev_get_drvdata(dev); device_init_wakeup(dev, false); @@ -820,10 +815,15 @@ static void acpi_tad_remove(struct platform_device *pdev) static int acpi_tad_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - acpi_handle handle = ACPI_HANDLE(dev); struct acpi_tad_driver_data *dd; + acpi_handle handle; acpi_status status; unsigned long long caps; + int ret; + + handle = ACPI_HANDLE(dev); + if (!handle) + return -ENODEV; /* * Initialization failure messages are mostly about firmware issues, so @@ -863,13 +863,21 @@ static int acpi_tad_probe(struct platform_device *pdev) } /* - * The platform bus type layer tells the ACPI PM domain powers up the - * device, so set the runtime PM status of it to "active". + * The platform bus type probe callback tells the ACPI PM domain to + * power up the device, so set the runtime PM status of it to "active". */ pm_runtime_set_active(dev); pm_runtime_enable(dev); pm_runtime_suspend(dev); + /* + * acpi_tad_remove() needs to run after unregistering the RTC class + * device to avoid racing with the latter's callbacks. + */ + ret = devm_add_action_or_reset(&pdev->dev, acpi_tad_remove, &pdev->dev); + if (ret) + return ret; + if (caps & ACPI_TAD_RT) acpi_tad_register_rtc(dev, caps); @@ -885,10 +893,9 @@ static struct platform_driver acpi_tad_driver = { .driver = { .name = "acpi-tad", .acpi_match_table = acpi_tad_ids, - .dev_groups = acpi_tad_attr_groups, + .dev_groups = acpi_tad_groups, }, .probe = acpi_tad_probe, - .remove = acpi_tad_remove, }; MODULE_DEVICE_TABLE(acpi, acpi_tad_ids); diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index a9248af078f6..1f3fa2278584 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -401,8 +401,18 @@ static struct acpi_generic_address *einj_get_trigger_parameter_region( return NULL; } + +static bool is_memory_injection(u32 type, u32 flags) +{ + if (flags & SETWA_FLAGS_EINJV2) + return !!(type & ACPI_EINJV2_MEMORY); + if (type & ACPI5_VENDOR_BIT) + return !!(vendor_flags & SETWA_FLAGS_MEM); + return !!(type & MEM_ERROR_MASK) || !!(flags & SETWA_FLAGS_MEM); +} + /* Execute instructions in trigger error action table */ -static int __einj_error_trigger(u64 trigger_paddr, u32 type, +static int __einj_error_trigger(u64 trigger_paddr, u32 type, u32 flags, u64 param1, u64 param2) { struct acpi_einj_trigger trigger_tab; @@ -480,7 +490,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, * This will cause resource conflict with regular memory. So * remove it from trigger table resources. */ - if ((param_extension || acpi5) && (type & MEM_ERROR_MASK) && param2) { + if ((param_extension || acpi5) && is_memory_injection(type, flags)) { struct apei_resources addr_resources; apei_resources_init(&addr_resources); @@ -660,7 +670,7 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, return rc; trigger_paddr = apei_exec_ctx_get_output(&ctx); if (notrigger == 0) { - rc = __einj_error_trigger(trigger_paddr, type, param1, param2); + rc = __einj_error_trigger(trigger_paddr, type, flags, param1, param2); if (rc) return rc; } @@ -718,28 +728,6 @@ int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3, SETWA_FLAGS_PCIE_SBDF | SETWA_FLAGS_EINJV2))) return -EINVAL; - /* check if type is a valid EINJv2 error type */ - if (is_v2) { - if (!(type & available_error_type_v2)) - return -EINVAL; - } - /* - * We need extra sanity checks for memory errors. - * Other types leap directly to injection. - */ - - /* ensure param1/param2 existed */ - if (!(param_extension || acpi5)) - goto inject; - - /* ensure injection is memory related */ - if (type & ACPI5_VENDOR_BIT) { - if (vendor_flags != SETWA_FLAGS_MEM) - goto inject; - } else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM)) { - goto inject; - } - /* * Injections targeting a CXL 1.0/1.1 port have to be injected * via the einj_cxl_rch_error_inject() path as that does the proper @@ -748,6 +736,23 @@ int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3, if (einj_is_cxl_error_type(type) && (flags & SETWA_FLAGS_MEM)) return -EINVAL; + /* check if type is a valid EINJv2 error type */ + if (is_v2) { + if (!(type & available_error_type_v2)) + return -EINVAL; + } + + /* ensure param1/param2 existed */ + if (!(param_extension || acpi5)) + goto inject; + + /* + * We need extra sanity checks for memory errors. + * Other types leap directly to injection. + */ + if (!is_memory_injection(type, flags)) + goto inject; + /* * Disallow crazy address masks that give BIOS leeway to pick * injection address almost anywhere. Insist on page or diff --git a/drivers/acpi/arm64/cpuidle.c b/drivers/acpi/arm64/cpuidle.c index 801f9c450142..c68a5db8ebba 100644 --- a/drivers/acpi/arm64/cpuidle.c +++ b/drivers/acpi/arm64/cpuidle.c @@ -16,7 +16,7 @@ static int psci_acpi_cpu_init_idle(unsigned int cpu) { - int i, count; + int i; struct acpi_lpi_state *lpi; struct acpi_processor *pr = per_cpu(processors, cpu); @@ -30,14 +30,10 @@ static int psci_acpi_cpu_init_idle(unsigned int cpu) if (!psci_ops.cpu_suspend) return -EOPNOTSUPP; - count = pr->power.count - 1; - if (count <= 0) - return -ENODEV; - - for (i = 0; i < count; i++) { + for (i = 1; i < pr->power.count; i++) { u32 state; - lpi = &pr->power.lpi_states[i + 1]; + lpi = &pr->power.lpi_states[i]; /* * Only bits[31:0] represent a PSCI power_state while * bits[63:32] must be 0x0 as per ARM ACPI FFH Specification diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index b4c25474f42f..d4ae21a71007 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1214,10 +1214,14 @@ static void sysfs_battery_cleanup(struct acpi_battery *battery) static int acpi_battery_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_battery *battery; + struct acpi_device *device; int result; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + if (device->dep_unmet) return -EPROBE_DEFER; diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index dc064a388c23..b47301ee4c8a 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -531,15 +531,20 @@ static int acpi_lid_input_open(struct input_dev *input) static int acpi_button_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_notify_handler handler; + struct acpi_device *device; struct acpi_button *button; struct input_dev *input; - const char *hid = acpi_device_hid(device); acpi_status status; char *name, *class; + const char *hid; int error = 0; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + + hid = acpi_device_hid(device); if (!strcmp(hid, ACPI_BUTTON_HID_LID) && lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED) return -ENODEV; diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 2e91c5a97761..f370be8715ae 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -362,7 +362,7 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd) end: if (cmd == CMD_WRITE) { if (unlikely(ret)) { - for_each_online_cpu(i) { + for_each_possible_cpu(i) { struct cpc_desc *desc = per_cpu(cpc_desc_ptr, i); if (!desc) @@ -524,13 +524,13 @@ int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data) else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY) cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ANY; - for_each_online_cpu(i) { + for_each_possible_cpu(i) { if (i == cpu) continue; match_cpc_ptr = per_cpu(cpc_desc_ptr, i); if (!match_cpc_ptr) - goto err_fault; + continue; match_pdomain = &(match_cpc_ptr->domain_info); if (match_pdomain->domain != pdomain->domain) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 45204538ed87..64ad4cfa6208 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1676,10 +1676,14 @@ static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device, bool ca static int acpi_ec_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_device *device; struct acpi_ec *ec; int ret; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + if (boot_ec && (boot_ec->handle == device->handle || !strcmp(acpi_device_hid(device), ACPI_ECDT_HID))) { /* Fast path: this device corresponds to the boot EC. */ diff --git a/drivers/acpi/hed.c b/drivers/acpi/hed.c index 4d5e12ed6f3c..060e8d670f5d 100644 --- a/drivers/acpi/hed.c +++ b/drivers/acpi/hed.c @@ -50,9 +50,13 @@ static void acpi_hed_notify(acpi_handle handle, u32 event, void *data) static int acpi_hed_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_device *device; int err; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + /* Only one hardware error device */ if (hed_handle) return -EINVAL; diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index d13264fb9e02..9304ac996d41 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -3341,12 +3341,16 @@ static int acpi_nfit_probe(struct platform_device *pdev) struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_nfit_desc *acpi_desc; struct device *dev = &pdev->dev; - struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_table_header *tbl; + struct acpi_device *adev; acpi_status status = AE_OK; acpi_size sz; int rc = 0; + adev = ACPI_COMPANION(&pdev->dev); + if (!adev) + return -ENODEV; + rc = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, acpi_nfit_notify, dev); if (rc) diff --git a/drivers/acpi/numa/srat.c b/drivers/acpi/numa/srat.c index aa87ee1583a4..62d4a8df0b8c 100644 --- a/drivers/acpi/numa/srat.c +++ b/drivers/acpi/numa/srat.c @@ -654,8 +654,11 @@ int __init acpi_numa_init(void) } last_real_pxm = fake_pxm; fake_pxm++; - acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, acpi_parse_cfmws, - &fake_pxm); + + /* No need to expand numa nodes if CXL is disabled */ + if (IS_ENABLED(CONFIG_CXL_ACPI)) + acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, acpi_parse_cfmws, + &fake_pxm); if (cnt < 0) return cnt; diff --git a/drivers/acpi/pfr_telemetry.c b/drivers/acpi/pfr_telemetry.c index 32bdf8cbe8f2..2387376832a1 100644 --- a/drivers/acpi/pfr_telemetry.c +++ b/drivers/acpi/pfr_telemetry.c @@ -360,10 +360,14 @@ static void pfrt_log_put_idx(void *data) static int acpi_pfrt_log_probe(struct platform_device *pdev) { - acpi_handle handle = ACPI_HANDLE(&pdev->dev); struct pfrt_log_device *pfrt_log_dev; + acpi_handle handle; int ret; + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; + if (!acpi_has_method(handle, "_DSM")) { dev_dbg(&pdev->dev, "Missing _DSM\n"); return -ENODEV; diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c index 11b1c2828005..6283105bb0e8 100644 --- a/drivers/acpi/pfr_update.c +++ b/drivers/acpi/pfr_update.c @@ -538,10 +538,14 @@ static void pfru_put_idx(void *data) static int acpi_pfru_probe(struct platform_device *pdev) { - acpi_handle handle = ACPI_HANDLE(&pdev->dev); struct pfru_device *pfru_dev; + acpi_handle handle; int ret; + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; + if (!acpi_has_method(handle, "_DSM")) { dev_dbg(&pdev->dev, "Missing _DSM\n"); return -ENODEV; diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 6b1680ec3694..d4131c184be8 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -987,7 +987,7 @@ struct acpi_device *acpi_add_power_resource(acpi_handle handle) return device; err: - acpi_release_power_resource(&device->dev); + acpi_dev_put(device); return NULL; } diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 440f1d69aca8..86b7c7975852 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -629,11 +629,15 @@ static void acpi_sbs_callback(void *context) static int acpi_sbs_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_device *device; struct acpi_sbs *sbs; int result = 0; int id; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + sbs = kzalloc_obj(struct acpi_sbs); if (!sbs) { result = -ENOMEM; diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c index f413270415b6..c0ffa267f96c 100644 --- a/drivers/acpi/sbshc.c +++ b/drivers/acpi/sbshc.c @@ -237,11 +237,15 @@ static int smbus_alarm(void *context) static int acpi_smbus_hc_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_device *device; int status; unsigned long long val; struct acpi_smb_hc *hc; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val); if (ACPI_FAILURE(status)) { pr_err("error obtaining _EC.\n"); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index e8cdbdb46fdb..530547cda8b2 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1900,7 +1900,7 @@ static int acpi_add_single_object(struct acpi_device **child, result = acpi_device_add(device); if (result) { - acpi_device_release(&device->dev); + acpi_dev_put(device); return result; } diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index b8b487d89d25..dfc7daa809b5 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -789,7 +789,7 @@ static int acpi_thermal_probe(struct platform_device *pdev) int i; if (!device) - return -EINVAL; + return -ENODEV; tz = kzalloc_obj(struct acpi_thermal); if (!tz) diff --git a/drivers/acpi/tiny-power-button.c b/drivers/acpi/tiny-power-button.c index 531e65b01bcb..92516ef84b02 100644 --- a/drivers/acpi/tiny-power-button.c +++ b/drivers/acpi/tiny-power-button.c @@ -38,9 +38,13 @@ static u32 acpi_tiny_power_button_event(void *not_used) static int acpi_tiny_power_button_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_device *device; acpi_status status; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) { status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, acpi_tiny_power_button_event, diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 4cf74f173c78..458efa4fe9d4 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -878,6 +878,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 7760 AIO"), }, }, + { + .callback = video_detect_force_native, + /* Dell OptiPlex 7770 AIO */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 7770 AIO"), + }, + }, /* * Models which have nvidia-ec-wmi support, but should not use it. @@ -899,6 +907,23 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 15 3535"), }, }, + { + /* https://gitlab.freedesktop.org/drm/amd/-/issues/4512 */ + .callback = video_detect_force_native, + /* Lenovo Legion S7 15ACH6 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "82K8"), + }, + }, + { + .callback = video_detect_force_native, + /* HP OMEN Gaming Laptop 16-n0xxx */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16-n0xxx"), + }, + }, /* * x86 android tablets which directly control the backlight through @@ -948,14 +973,6 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), }, }, - /* https://gitlab.freedesktop.org/drm/amd/-/issues/4512 */ - { - .callback = video_detect_force_native, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "82K8"), - }, - }, { }, }; diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 21f91d9f2fbc..9e6194224593 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3117,7 +3117,7 @@ static void binder_transaction(struct binder_proc *proc, t->start_time = t_start_time; t->from_pid = proc->pid; t->from_tid = thread->pid; - t->sender_euid = task_euid(proc->tsk); + t->sender_euid = current_euid(); t->code = tr->code; t->flags = tr->flags; t->priority = task_nice(current); diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index 7f65a9c3a0e5..0cab959e4b7e 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -56,7 +56,6 @@ pub(crate) struct Allocation { pub(crate) process: Arc, allocation_info: Option, free_on_drop: bool, - pub(crate) oneway_spam_detected: bool, #[allow(dead_code)] pub(crate) debug_id: usize, } @@ -68,7 +67,6 @@ impl Allocation { offset: usize, size: usize, ptr: usize, - oneway_spam_detected: bool, ) -> Self { Self { process, @@ -76,7 +74,6 @@ impl Allocation { size, ptr, debug_id, - oneway_spam_detected, allocation_info: None, free_on_drop: true, } @@ -208,6 +205,7 @@ impl Allocation { let res = FileDescriptorReservation::get_unused_fd_flags(bindings::O_CLOEXEC)?; let fd = res.reserved_fd(); self.write::(file_info.buffer_offset, &fd)?; + crate::trace::trace_transaction_fd_recv(self.debug_id, fd, file_info.buffer_offset); reservations.push( Reservation { @@ -260,19 +258,22 @@ impl Drop for Allocation { } } - for &fd in &info.file_list.close_on_free { - let closer = match DeferredFdCloser::new(GFP_KERNEL) { - Ok(closer) => closer, - Err(kernel::alloc::AllocError) => { - // Ignore allocation failures. - break; - } - }; + if self.process.task == kernel::current!().group_leader() { + for &fd in &info.file_list.close_on_free { + let closer = match DeferredFdCloser::new(GFP_KERNEL) { + Ok(closer) => closer, + Err(kernel::alloc::AllocError) => { + // Ignore allocation failures. + break; + } + }; - // Here, we ignore errors. The operation can fail if the fd is not valid, or if the - // method is called from a kthread. However, this is always called from a syscall, - // so the latter case cannot happen, and we don't care about the first case. - let _ = closer.close_fd(fd); + // Here, we ignore errors. The operation can fail if the fd is not valid, or if + // the method is called from a kthread. However, this is always called from a + // syscall, so the latter case cannot happen, and we don't care about the first + // case. + let _ = closer.close_fd(fd); + } } if info.clear_on_free { diff --git a/drivers/android/binder/context.rs b/drivers/android/binder/context.rs index 9cf437c025a2..ddddb66b3557 100644 --- a/drivers/android/binder/context.rs +++ b/drivers/android/binder/context.rs @@ -94,6 +94,17 @@ impl Context { } let mut manager = self.manager.lock(); manager.all_procs.retain(|p| !Arc::ptr_eq(p, proc)); + + // Shrink the vector if it has significant unused capacity to avoid memory waste, + // but use a conservative strategy to prevent shrink-then-regrow oscillation. + // Only shrink when length drops below 1/4 of capacity, and shrink to twice the length. + let len = manager.all_procs.len(); + let cap = manager.all_procs.capacity(); + if len < cap / 4 { + // Shrink to twice the current length. Ignore allocation failures since this + // is just an optimization; the vector remains valid even if shrinking fails. + let _ = manager.all_procs.shrink_to(len * 2, GFP_KERNEL); + } } pub(crate) fn set_manager_node(&self, node_ref: NodeRef) -> Result { diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error.rs index b24497cfa292..45d85d4c2815 100644 --- a/drivers/android/binder/error.rs +++ b/drivers/android/binder/error.rs @@ -13,7 +13,7 @@ pub(crate) type BinderResult = core::result::Result; /// errno. pub(crate) struct BinderError { pub(crate) reply: u32, - source: Option, + pub(crate) source: Option, } impl BinderError { @@ -41,14 +41,6 @@ impl BinderError { pub(crate) fn is_dead(&self) -> bool { self.reply == BR_DEAD_REPLY } - - pub(crate) fn as_errno(&self) -> kernel::ffi::c_int { - self.source.unwrap_or(EINVAL).to_errno() - } - - pub(crate) fn should_pr_warn(&self) -> bool { - self.source.is_some() - } } /// Convert an errno into a `BinderError` and store the errno used to construct it. The errno diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index f06498129aa9..820cbd541435 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -48,6 +48,7 @@ use crate::{ range_alloc::{RangeAllocator, ReserveNew, ReserveNewArgs}, stats::BinderStats, thread::{PushWorkRes, Thread}, + transaction::TransactionInfo, BinderfsProcFile, DArc, DLArc, DTRWrap, DeliverToRead, }; @@ -681,7 +682,7 @@ impl Process { fn get_current_thread(self: ArcBorrow<'_, Self>) -> Result> { let id = { let current = kernel::current!(); - if !core::ptr::eq(current.group_leader(), &*self.task) { + if self.task != current.group_leader() { pr_err!("get_current_thread was called from the wrong process."); return Err(EINVAL); } @@ -1003,16 +1004,15 @@ impl Process { self: &Arc, debug_id: usize, size: usize, - is_oneway: bool, - from_pid: i32, + info: &mut TransactionInfo, ) -> BinderResult { use kernel::page::PAGE_SIZE; let mut reserve_new_args = ReserveNewArgs { debug_id, size, - is_oneway, - pid: from_pid, + is_oneway: info.is_oneway(), + pid: info.from_pid, ..ReserveNewArgs::default() }; @@ -1028,13 +1028,13 @@ impl Process { reserve_new_args = alloc_request.make_alloc()?; }; + info.oneway_spam_suspect = new_alloc.oneway_spam_detected; let res = Allocation::new( self.clone(), debug_id, new_alloc.offset, size, addr + new_alloc.offset, - new_alloc.oneway_spam_detected, ); // This allocation will be marked as in use until the `Allocation` is used to free it. @@ -1066,7 +1066,7 @@ impl Process { let mapping = inner.mapping.as_mut()?; let offset = ptr.checked_sub(mapping.address)?; let (size, debug_id, odata) = mapping.alloc.reserve_existing(offset).ok()?; - let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr, false); + let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr); if let Some(data) = odata { alloc.set_info(data); } @@ -1414,8 +1414,7 @@ impl Process { .alloc .take_for_each(|offset, size, debug_id, odata| { let ptr = offset + address; - let mut alloc = - Allocation::new(self.clone(), debug_id, offset, size, ptr, false); + let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr); if let Some(data) = odata { alloc.set_info(data); } @@ -1443,6 +1442,9 @@ impl Process { } } + // #[export_name] is a temporary workaround so that ps output does not become unreadable from + // mangled symbol names. + #[export_name = "rust_binder_freeze"] pub(crate) fn ioctl_freeze(&self, info: &BinderFreezeInfo) -> Result { if info.enable == 0 { let msgs = self.prepare_freeze_messages()?; @@ -1657,11 +1659,14 @@ impl Process { const _IOC_READ_WRITE: u32 = _IOC_READ | _IOC_WRITE; - match _IOC_DIR(cmd) { + let res = match _IOC_DIR(cmd) { _IOC_WRITE => Self::ioctl_write_only(this, file, cmd, &mut user_slice.reader()), _IOC_READ_WRITE => Self::ioctl_write_read(this, file, cmd, user_slice), _ => Err(EINVAL), - } + }; + + crate::trace::trace_ioctl_done(res); + res } pub(crate) fn mmap( @@ -1670,7 +1675,7 @@ impl Process { vma: &mm::virt::VmaNew, ) -> Result { // We don't allow mmap to be used in a different process. - if !core::ptr::eq(kernel::current!().group_leader(), &*this.task) { + if this.task != kernel::current!().group_leader() { return Err(EINVAL); } if vma.start() == 0 { diff --git a/drivers/android/binder/range_alloc/array.rs b/drivers/android/binder/range_alloc/array.rs index ada1d1b4302e..081d19b09d4b 100644 --- a/drivers/android/binder/range_alloc/array.rs +++ b/drivers/android/binder/range_alloc/array.rs @@ -204,7 +204,6 @@ impl ArrayRangeAllocator { // caller will mark them as unused, which means that they can be freed if the system comes // under memory pressure. let mut freed_range = FreedRange::interior_pages(offset, size); - #[expect(clippy::collapsible_if)] // reads better like this if offset % PAGE_SIZE != 0 { if i == 0 || self.ranges[i - 1].endpoint() <= (offset & PAGE_MASK) { freed_range.start_page_idx -= 1; diff --git a/drivers/android/binder/rust_binder.h b/drivers/android/binder/rust_binder.h index d2284726c025..3936b9d0b8cd 100644 --- a/drivers/android/binder/rust_binder.h +++ b/drivers/android/binder/rust_binder.h @@ -99,4 +99,9 @@ static inline size_t rust_binder_node_debug_id(rust_binder_node t) return *(size_t *) (t + RUST_BINDER_LAYOUT.n.debug_id); } +static inline binder_uintptr_t rust_binder_node_ptr(rust_binder_node t) +{ + return *(binder_uintptr_t *) (t + RUST_BINDER_LAYOUT.n.ptr); +} + #endif diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index 8ad785c6bd0f..1a446787c8b3 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -15,7 +15,7 @@ #include -TRACE_EVENT(rust_binder_ioctl, +TRACE_EVENT(binder_ioctl, TP_PROTO(unsigned int cmd, unsigned long arg), TP_ARGS(cmd, arg), @@ -30,7 +30,46 @@ TRACE_EVENT(rust_binder_ioctl, TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg) ); -TRACE_EVENT(rust_binder_transaction, +DECLARE_EVENT_CLASS(binder_function_return_class, + TP_PROTO(int ret), + TP_ARGS(ret), + TP_STRUCT__entry( + __field(int, ret) + ), + TP_fast_assign( + __entry->ret = ret; + ), + TP_printk("ret=%d", __entry->ret) +); + +#define DEFINE_RBINDER_FUNCTION_RETURN_EVENT(name) \ +DEFINE_EVENT(binder_function_return_class, name, \ + TP_PROTO(int ret), \ + TP_ARGS(ret)) + +DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done); +DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_read_done); +DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_write_done); + +TRACE_EVENT(binder_wait_for_work, + TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo), + TP_ARGS(proc_work, transaction_stack, thread_todo), + TP_STRUCT__entry( + __field(bool, proc_work) + __field(bool, transaction_stack) + __field(bool, thread_todo) + ), + TP_fast_assign( + __entry->proc_work = proc_work; + __entry->transaction_stack = transaction_stack; + __entry->thread_todo = thread_todo; + ), + TP_printk("proc_work=%d transaction_stack=%d thread_todo=%d", + __entry->proc_work, __entry->transaction_stack, + __entry->thread_todo) +); + +TRACE_EVENT(binder_transaction, TP_PROTO(bool reply, rust_binder_transaction t, struct task_struct *thread), TP_ARGS(reply, t, thread), TP_STRUCT__entry( @@ -60,6 +99,84 @@ TRACE_EVENT(rust_binder_transaction, __entry->reply, __entry->flags, __entry->code) ); +TRACE_EVENT(binder_transaction_received, + TP_PROTO(rust_binder_transaction t), + TP_ARGS(t), + TP_STRUCT__entry( + __field(int, debug_id) + ), + TP_fast_assign( + __entry->debug_id = rust_binder_transaction_debug_id(t); + ), + TP_printk("transaction=%d", __entry->debug_id) +); + +TRACE_EVENT(binder_transaction_fd_send, + TP_PROTO(int t_debug_id, int fd, size_t offset), + TP_ARGS(t_debug_id, fd, offset), + TP_STRUCT__entry( + __field(int, debug_id) + __field(int, fd) + __field(size_t, offset) + ), + TP_fast_assign( + __entry->debug_id = t_debug_id; + __entry->fd = fd; + __entry->offset = offset; + ), + TP_printk("transaction=%d src_fd=%d offset=%zu", + __entry->debug_id, __entry->fd, __entry->offset) +); + +TRACE_EVENT(binder_transaction_fd_recv, + TP_PROTO(int t_debug_id, int fd, size_t offset), + TP_ARGS(t_debug_id, fd, offset), + TP_STRUCT__entry( + __field(int, debug_id) + __field(int, fd) + __field(size_t, offset) + ), + TP_fast_assign( + __entry->debug_id = t_debug_id; + __entry->fd = fd; + __entry->offset = offset; + ), + TP_printk("transaction=%d dest_fd=%d offset=%zu", + __entry->debug_id, __entry->fd, __entry->offset) +); + +TRACE_EVENT(binder_command, + TP_PROTO(uint32_t cmd), + TP_ARGS(cmd), + TP_STRUCT__entry( + __field(uint32_t, cmd) + ), + TP_fast_assign( + __entry->cmd = cmd; + ), + TP_printk("cmd=0x%x %s", + __entry->cmd, + _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_command_strings) ? + binder_command_strings[_IOC_NR(__entry->cmd)] : + "unknown") +); + +TRACE_EVENT(binder_return, + TP_PROTO(uint32_t cmd), + TP_ARGS(cmd), + TP_STRUCT__entry( + __field(uint32_t, cmd) + ), + TP_fast_assign( + __entry->cmd = cmd; + ), + TP_printk("cmd=0x%x %s", + __entry->cmd, + _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_return_strings) ? + binder_return_strings[_IOC_NR(__entry->cmd)] : + "unknown") +); + #endif /* _RUST_BINDER_TRACE_H */ /* This part must be outside protection */ diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index 632b54714e44..dc1941cd2407 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -118,6 +118,7 @@ impl<'a> BinderReturnWriter<'a> { /// Write a return code back to user space. /// Should be a `BR_` constant from [`defs`] e.g. [`defs::BR_TRANSACTION_COMPLETE`]. fn write_code(&mut self, code: u32) -> Result { + crate::trace::trace_return(code); stats::GLOBAL_STATS.inc_br(code); self.thread.process.stats.inc_br(code); self.writer.write(&code) @@ -294,8 +295,6 @@ impl kernel::Module for BinderModule { // SAFETY: The module initializer never runs twice, so we only call this once. unsafe { crate::context::CONTEXTS.init() }; - pr_warn!("Loaded Rust Binder."); - BINDER_SHRINKER.register(c"android-binder")?; // SAFETY: The module is being loaded, so we can initialize binderfs. diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index c004214b1662..97d5f31e8fe3 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -19,7 +19,7 @@ use kernel::{ sync::poll::{PollCondVar, PollTable}, sync::{aref::ARef, Arc, SpinLock}, task::Task, - uaccess::UserSlice, + uaccess::{UserPtr, UserSlice, UserSliceReader}, uapi, }; @@ -30,7 +30,7 @@ use crate::{ process::{GetWorkOrRegister, Process}, ptr_align, stats::GLOBAL_STATS, - transaction::Transaction, + transaction::{Transaction, TransactionInfo}, BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead, }; @@ -513,6 +513,9 @@ impl Thread { /// Attempts to fetch a work item from the thread-local queue. The behaviour if the queue is /// empty depends on `wait`: if it is true, the function waits for some work to be queued (or a /// signal); otherwise it returns indicating that none is available. + // #[export_name] is a temporary workaround so that ps output does not become unreadable from + // mangled symbol names. + #[export_name = "rust_binder_waitlcl"] fn get_work_local(self: &Arc, wait: bool) -> Result>> { { let mut inner = self.inner.lock(); @@ -551,6 +554,9 @@ impl Thread { /// /// This must only be called when the thread is not participating in a transaction chain. If it /// is, the local version (`get_work_local`) should be used instead. + // #[export_name] is a temporary workaround so that ps output does not become unreadable from + // mangled symbol names. + #[export_name = "rust_binder_wait"] fn get_work(self: &Arc, wait: bool) -> Result>> { // Try to get work from the thread's work queue, using only a local lock. { @@ -706,6 +712,7 @@ impl Thread { core::mem::offset_of!(uapi::binder_fd_object, __bindgen_anon_1.fd); let field_offset = offset + FD_FIELD_OFFSET; + crate::trace::trace_transaction_fd_send(view.alloc.debug_id, fd, field_offset); view.alloc.info_add_fd(file, field_offset, false)?; } @@ -945,13 +952,11 @@ impl Thread { pub(crate) fn copy_transaction_data( &self, to_process: Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, debug_id: usize, allow_fds: bool, txn_security_ctx_offset: Option<&mut usize>, ) -> BinderResult { - let trd = &tr.transaction_data; - let is_oneway = trd.flags & TF_ONE_WAY != 0; let mut secctx = if let Some(offset) = txn_security_ctx_offset { let secid = self.process.cred.get_secid(); let ctx = match security::SecurityCtx::from_secid(secid) { @@ -966,10 +971,10 @@ impl Thread { None }; - let data_size = trd.data_size.try_into().map_err(|_| EINVAL)?; + let data_size = info.data_size; let aligned_data_size = ptr_align(data_size).ok_or(EINVAL)?; - let offsets_size: usize = trd.offsets_size.try_into().map_err(|_| EINVAL)?; - let buffers_size: usize = tr.buffers_size.try_into().map_err(|_| EINVAL)?; + let offsets_size = info.offsets_size; + let buffers_size = info.buffers_size; let aligned_secctx_size = match secctx.as_ref() { Some((_offset, ctx)) => ptr_align(ctx.len()).ok_or(EINVAL)?, None => 0, @@ -992,32 +997,25 @@ impl Thread { size_of::(), ); let secctx_off = aligned_data_size + offsets_size + buffers_size; - let mut alloc = - match to_process.buffer_alloc(debug_id, len, is_oneway, self.process.task.pid()) { - Ok(alloc) => alloc, - Err(err) => { - pr_warn!( - "Failed to allocate buffer. len:{}, is_oneway:{}", - len, - is_oneway - ); - return Err(err); - } - }; + let mut alloc = match to_process.buffer_alloc(debug_id, len, info) { + Ok(alloc) => alloc, + Err(err) => { + pr_warn!( + "Failed to allocate buffer. len:{}, is_oneway:{}", + len, + info.is_oneway(), + ); + return Err(err); + } + }; - // SAFETY: This accesses a union field, but it's okay because the field's type is valid for - // all bit-patterns. - let trd_data_ptr = unsafe { &trd.data.ptr }; - let mut buffer_reader = - UserSlice::new(UserPtr::from_addr(trd_data_ptr.buffer as _), data_size).reader(); + let mut buffer_reader = UserSlice::new(info.data_ptr, data_size).reader(); let mut end_of_previous_object = 0; let mut sg_state = None; // Copy offsets if there are any. if offsets_size > 0 { - let mut offsets_reader = - UserSlice::new(UserPtr::from_addr(trd_data_ptr.offsets as _), offsets_size) - .reader(); + let mut offsets_reader = UserSlice::new(info.offsets_ptr, offsets_size).reader(); let offsets_start = aligned_data_size; let offsets_end = aligned_data_size + offsets_size; @@ -1192,37 +1190,92 @@ impl Thread { } } - fn transaction(self: &Arc, tr: &BinderTransactionDataSg, inner: T) - where - T: FnOnce(&Arc, &BinderTransactionDataSg) -> BinderResult, - { - if let Err(err) = inner(self, tr) { - if err.should_pr_warn() { - let mut ee = self.inner.lock().extended_error; - ee.command = err.reply; - ee.param = err.as_errno(); - pr_warn!( - "Transaction failed: {:?} my_pid:{}", - err, - self.process.pid_in_current_ns() - ); + // No inlining avoids allocating stack space for `BinderTransactionData` for the entire + // duration of `transaction()`. + #[inline(never)] + fn read_transaction_info( + &self, + cmd: u32, + reader: &mut UserSliceReader, + info: &mut TransactionInfo, + ) -> Result<()> { + let td = match cmd { + BC_TRANSACTION | BC_REPLY => { + reader.read::()?.with_buffers_size(0) + } + BC_TRANSACTION_SG | BC_REPLY_SG => reader.read::()?, + _ => return Err(EINVAL), + }; + + // SAFETY: Above `read` call initializes all bytes, so this union read is ok. + let trd_data_ptr = unsafe { &td.transaction_data.data.ptr }; + + info.is_reply = matches!(cmd, BC_REPLY | BC_REPLY_SG); + info.from_pid = self.process.task.pid(); + info.from_tid = self.id; + info.code = td.transaction_data.code; + info.flags = td.transaction_data.flags; + info.data_ptr = UserPtr::from_addr(trd_data_ptr.buffer as usize); + info.data_size = td.transaction_data.data_size as usize; + info.offsets_ptr = UserPtr::from_addr(trd_data_ptr.offsets as usize); + info.offsets_size = td.transaction_data.offsets_size as usize; + info.buffers_size = td.buffers_size as usize; + // SAFETY: Above `read` call initializes all bytes, so this union read is ok. + info.target_handle = unsafe { td.transaction_data.target.handle }; + Ok(()) + } + + #[inline(never)] + fn transaction(self: &Arc, cmd: u32, reader: &mut UserSliceReader) -> Result<()> { + let mut info = TransactionInfo::zeroed(); + self.read_transaction_info(cmd, reader, &mut info)?; + + let ret = if info.is_reply { + self.reply_inner(&mut info) + } else if info.is_oneway() { + self.oneway_transaction_inner(&mut info) + } else { + self.transaction_inner(&mut info) + }; + + if let Err(err) = ret { + if err.reply != BR_TRANSACTION_COMPLETE { + info.reply = err.reply; } self.push_return_work(err.reply); + if let Some(source) = &err.source { + info.errno = source.to_errno(); + info.reply = err.reply; + + { + let mut ee = self.inner.lock().extended_error; + ee.command = err.reply; + ee.param = source.to_errno(); + } + + pr_warn!( + "{}:{} transaction to {} failed: {source:?}", + info.from_pid, + info.from_tid, + info.to_pid + ); + } } + + Ok(()) } - fn transaction_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { - // SAFETY: Handle's type has no invalid bit patterns. - let handle = unsafe { tr.transaction_data.target.handle }; - let node_ref = self.process.get_transaction_node(handle)?; + fn transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { + let node_ref = self.process.get_transaction_node(info.target_handle)?; + info.to_pid = node_ref.node.owner.task.pid(); security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?; // TODO: We need to ensure that there isn't a pending transaction in the work queue. How // could this happen? let top = self.top_of_transaction_stack()?; let list_completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; let completion = list_completion.clone_arc(); - let transaction = Transaction::new(node_ref, top, self, tr)?; + let transaction = Transaction::new(node_ref, top, self, info)?; // Check that the transaction stack hasn't changed while the lock was released, then update // it with the new transaction. @@ -1238,7 +1291,7 @@ impl Thread { inner.push_work_deferred(list_completion); } - if let Err(e) = transaction.submit() { + if let Err(e) = transaction.submit(info) { completion.skip(); // Define `transaction` first to drop it after `inner`. let transaction; @@ -1251,18 +1304,21 @@ impl Thread { } } - fn reply_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { + fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { let orig = self.inner.lock().pop_transaction_to_reply(self)?; if !orig.from.is_current_transaction(&orig) { return Err(EINVAL.into()); } + info.to_tid = orig.from.id; + info.to_pid = orig.from.process.task.pid(); + // We need to complete the transaction even if we cannot complete building the reply. let out = (|| -> BinderResult<_> { let completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; let process = orig.from.process.clone(); let allow_fds = orig.flags & TF_ACCEPT_FDS != 0; - let reply = Transaction::new_reply(self, process, tr, allow_fds)?; + let reply = Transaction::new_reply(self, process, info, allow_fds)?; self.inner.lock().push_work(completion); orig.from.deliver_reply(Ok(reply), &orig); Ok(()) @@ -1283,16 +1339,12 @@ impl Thread { out } - fn oneway_transaction_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { - // SAFETY: The `handle` field is valid for all possible byte values, so reading from the - // union is okay. - let handle = unsafe { tr.transaction_data.target.handle }; - let node_ref = self.process.get_transaction_node(handle)?; + fn oneway_transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { + let node_ref = self.process.get_transaction_node(info.target_handle)?; + info.to_pid = node_ref.node.owner.task.pid(); security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?; - let transaction = Transaction::new(node_ref, None, self, tr)?; - let code = if self.process.is_oneway_spam_detection_enabled() - && transaction.oneway_spam_detected - { + let transaction = Transaction::new(node_ref, None, self, info)?; + let code = if self.process.is_oneway_spam_detection_enabled() && info.oneway_spam_suspect { BR_ONEWAY_SPAM_SUSPECT } else { BR_TRANSACTION_COMPLETE @@ -1300,7 +1352,7 @@ impl Thread { let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?; let completion = list_completion.clone_arc(); self.inner.lock().push_work(list_completion); - match transaction.submit() { + match transaction.submit(info) { Ok(()) => Ok(()), Err(err) => { completion.skip(); @@ -1318,32 +1370,12 @@ impl Thread { while reader.len() >= size_of::() && self.inner.lock().return_work.is_unused() { let before = reader.len(); let cmd = reader.read::()?; + crate::trace::trace_command(cmd); GLOBAL_STATS.inc_bc(cmd); self.process.stats.inc_bc(cmd); match cmd { - BC_TRANSACTION => { - let tr = reader.read::()?.with_buffers_size(0); - if tr.transaction_data.flags & TF_ONE_WAY != 0 { - self.transaction(&tr, Self::oneway_transaction_inner); - } else { - self.transaction(&tr, Self::transaction_inner); - } - } - BC_TRANSACTION_SG => { - let tr = reader.read::()?; - if tr.transaction_data.flags & TF_ONE_WAY != 0 { - self.transaction(&tr, Self::oneway_transaction_inner); - } else { - self.transaction(&tr, Self::transaction_inner); - } - } - BC_REPLY => { - let tr = reader.read::()?.with_buffers_size(0); - self.transaction(&tr, Self::reply_inner) - } - BC_REPLY_SG => { - let tr = reader.read::()?; - self.transaction(&tr, Self::reply_inner) + BC_TRANSACTION | BC_TRANSACTION_SG | BC_REPLY | BC_REPLY_SG => { + self.transaction(cmd, &mut reader)?; } BC_FREE_BUFFER => { let buffer = self.process.buffer_get(reader.read()?); @@ -1407,11 +1439,18 @@ impl Thread { UserSlice::new(UserPtr::from_addr(read_start as _), read_len as _).writer(), self, ); - let (in_pool, use_proc_queue) = { + let (in_pool, has_transaction, thread_todo, use_proc_queue) = { let inner = self.inner.lock(); - (inner.is_looper(), inner.should_use_process_work_queue()) + ( + inner.is_looper(), + inner.current_transaction.is_some(), + !inner.work_list.is_empty(), + inner.should_use_process_work_queue(), + ) }; + crate::trace::trace_wait_for_work(use_proc_queue, has_transaction, thread_todo); + let getter = if use_proc_queue { Self::get_work } else { @@ -1477,6 +1516,7 @@ impl Thread { let mut ret = Ok(()); if req.write_size > 0 { ret = self.write(&mut req); + crate::trace::trace_write_done(ret); if let Err(err) = ret { pr_warn!( "Write failure {:?} in pid:{}", @@ -1493,6 +1533,7 @@ impl Thread { // Go through the work queue. if req.read_size > 0 { ret = self.read(&mut req, wait); + crate::trace::trace_read_done(ret); if ret.is_err() && ret != Err(EINTR) { pr_warn!( "Read failure {:?} in pid:{}", diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index 9839901c7151..5539672d7285 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -5,13 +5,23 @@ use crate::transaction::Transaction; use kernel::bindings::{rust_binder_transaction, task_struct}; -use kernel::ffi::{c_uint, c_ulong}; +use kernel::error::Result; +use kernel::ffi::{c_int, c_uint, c_ulong}; use kernel::task::Task; use kernel::tracepoint::declare_trace; declare_trace! { - unsafe fn rust_binder_ioctl(cmd: c_uint, arg: c_ulong); - unsafe fn rust_binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); + unsafe fn binder_ioctl(cmd: c_uint, arg: c_ulong); + unsafe fn binder_ioctl_done(ret: c_int); + unsafe fn binder_read_done(ret: c_int); + unsafe fn binder_write_done(ret: c_int); + unsafe fn binder_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool); + unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); + unsafe fn binder_transaction_received(t: rust_binder_transaction); + unsafe fn binder_transaction_fd_send(t_debug_id: c_int, fd: c_int, offset: usize); + unsafe fn binder_transaction_fd_recv(t_debug_id: c_int, fd: c_int, offset: usize); + unsafe fn binder_command(cmd: u32); + unsafe fn binder_return(ret: u32); } #[inline] @@ -19,10 +29,40 @@ fn raw_transaction(t: &Transaction) -> rust_binder_transaction { t as *const Transaction as rust_binder_transaction } +#[inline] +fn to_errno(ret: Result) -> i32 { + match ret { + Ok(()) => 0, + Err(err) => err.to_errno(), + } +} + #[inline] pub(crate) fn trace_ioctl(cmd: u32, arg: usize) { // SAFETY: Always safe to call. - unsafe { rust_binder_ioctl(cmd, arg as c_ulong) } + unsafe { binder_ioctl(cmd, arg as c_ulong) } +} + +#[inline] +pub(crate) fn trace_ioctl_done(ret: Result) { + // SAFETY: Always safe to call. + unsafe { binder_ioctl_done(to_errno(ret)) } +} +#[inline] +pub(crate) fn trace_read_done(ret: Result) { + // SAFETY: Always safe to call. + unsafe { binder_read_done(to_errno(ret)) } +} +#[inline] +pub(crate) fn trace_write_done(ret: Result) { + // SAFETY: Always safe to call. + unsafe { binder_write_done(to_errno(ret)) } +} + +#[inline] +pub(crate) fn trace_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool) { + // SAFETY: Always safe to call. + unsafe { binder_wait_for_work(proc_work, transaction_stack, thread_todo) } } #[inline] @@ -33,5 +73,33 @@ pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Ta }; // SAFETY: The raw transaction is valid for the duration of this call. The thread pointer is // valid or null. - unsafe { rust_binder_transaction(reply, raw_transaction(t), thread) } + unsafe { binder_transaction(reply, raw_transaction(t), thread) } +} + +#[inline] +pub(crate) fn trace_transaction_received(t: &Transaction) { + // SAFETY: The raw transaction is valid for the duration of this call. + unsafe { binder_transaction_received(raw_transaction(t)) } +} + +#[inline] +pub(crate) fn trace_transaction_fd_send(t_debug_id: usize, fd: u32, offset: usize) { + // SAFETY: This function is always safe to call. + unsafe { binder_transaction_fd_send(t_debug_id as c_int, fd as c_int, offset) } +} +#[inline] +pub(crate) fn trace_transaction_fd_recv(t_debug_id: usize, fd: u32, offset: usize) { + // SAFETY: This function is always safe to call. + unsafe { binder_transaction_fd_recv(t_debug_id as c_int, fd as c_int, offset) } +} + +#[inline] +pub(crate) fn trace_command(cmd: u32) { + // SAFETY: This function is always safe to call. + unsafe { binder_command(cmd) } +} +#[inline] +pub(crate) fn trace_return(ret: u32) { + // SAFETY: This function is always safe to call. + unsafe { binder_return(ret) } } diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 75e6f5fbaaae..47d5e4d88b07 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -8,7 +8,7 @@ use kernel::{ seq_print, sync::atomic::{ordering::Relaxed, Atomic}, sync::{Arc, SpinLock}, - task::Kuid, + task::{Kuid, Pid}, time::{Instant, Monotonic}, types::ScopeGuard, }; @@ -24,6 +24,33 @@ use crate::{ BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead, }; +#[derive(Zeroable)] +pub(crate) struct TransactionInfo { + pub(crate) from_pid: Pid, + pub(crate) from_tid: Pid, + pub(crate) to_pid: Pid, + pub(crate) to_tid: Pid, + pub(crate) code: u32, + pub(crate) flags: u32, + pub(crate) data_ptr: UserPtr, + pub(crate) data_size: usize, + pub(crate) offsets_ptr: UserPtr, + pub(crate) offsets_size: usize, + pub(crate) buffers_size: usize, + pub(crate) target_handle: u32, + pub(crate) errno: i32, + pub(crate) reply: u32, + pub(crate) oneway_spam_suspect: bool, + pub(crate) is_reply: bool, +} + +impl TransactionInfo { + #[inline] + pub(crate) fn is_oneway(&self) -> bool { + self.flags & TF_ONE_WAY != 0 + } +} + use core::mem::offset_of; use kernel::bindings::rb_transaction_layout; pub(crate) const TRANSACTION_LAYOUT: rb_transaction_layout = rb_transaction_layout { @@ -52,7 +79,6 @@ pub(crate) struct Transaction { data_address: usize, sender_euid: Kuid, txn_security_ctx_off: Option, - pub(crate) oneway_spam_detected: bool, start_time: Instant, } @@ -65,17 +91,16 @@ impl Transaction { node_ref: NodeRef, from_parent: Option>, from: &Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, ) -> BinderResult> { let debug_id = super::next_debug_id(); - let trd = &tr.transaction_data; let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0; let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0; let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None }; let to = node_ref.node.owner.clone(); let mut alloc = match from.copy_transaction_data( to.clone(), - tr, + info, debug_id, allow_fds, txn_security_ctx_off.as_mut(), @@ -88,15 +113,14 @@ impl Transaction { return Err(err); } }; - let oneway_spam_detected = alloc.oneway_spam_detected; - if trd.flags & TF_ONE_WAY != 0 { + if info.is_oneway() { if from_parent.is_some() { pr_warn!("Oneway transaction should not be in a transaction stack."); return Err(EINVAL.into()); } alloc.set_info_oneway_node(node_ref.node.clone()); } - if trd.flags & TF_CLEAR_BUF != 0 { + if info.flags & TF_CLEAR_BUF != 0 { alloc.set_info_clear_on_drop(); } let target_node = node_ref.node.clone(); @@ -107,18 +131,17 @@ impl Transaction { debug_id, target_node: Some(target_node), from_parent, - sender_euid: from.process.task.euid(), + sender_euid: Kuid::current_euid(), from: from.clone(), to, - code: trd.code, - flags: trd.flags, - data_size: trd.data_size as _, - offsets_size: trd.offsets_size as _, + code: info.code, + flags: info.flags, + data_size: info.data_size, + offsets_size: info.offsets_size, data_address, allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"), is_outstanding: Atomic::new(false), txn_security_ctx_off, - oneway_spam_detected, start_time: Instant::now(), }))?) } @@ -126,39 +149,36 @@ impl Transaction { pub(crate) fn new_reply( from: &Arc, to: Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, allow_fds: bool, ) -> BinderResult> { let debug_id = super::next_debug_id(); - let trd = &tr.transaction_data; - let mut alloc = match from.copy_transaction_data(to.clone(), tr, debug_id, allow_fds, None) - { - Ok(alloc) => alloc, - Err(err) => { - pr_warn!("Failure in copy_transaction_data: {:?}", err); - return Err(err); - } - }; - let oneway_spam_detected = alloc.oneway_spam_detected; - if trd.flags & TF_CLEAR_BUF != 0 { + let mut alloc = + match from.copy_transaction_data(to.clone(), info, debug_id, allow_fds, None) { + Ok(alloc) => alloc, + Err(err) => { + pr_warn!("Failure in copy_transaction_data: {:?}", err); + return Err(err); + } + }; + if info.flags & TF_CLEAR_BUF != 0 { alloc.set_info_clear_on_drop(); } Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { debug_id, target_node: None, from_parent: None, - sender_euid: from.process.task.euid(), + sender_euid: Kuid::current_euid(), from: from.clone(), to, - code: trd.code, - flags: trd.flags, - data_size: trd.data_size as _, - offsets_size: trd.offsets_size as _, + code: info.code, + flags: info.flags, + data_size: info.data_size, + offsets_size: info.offsets_size, data_address: alloc.ptr, allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"), is_outstanding: Atomic::new(false), txn_security_ctx_off: None, - oneway_spam_detected, start_time: Instant::now(), }))?) } @@ -248,7 +268,7 @@ impl Transaction { /// stack, otherwise uses the destination process. /// /// Not used for replies. - pub(crate) fn submit(self: DLArc) -> BinderResult { + pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) -> BinderResult { // Defined before `process_inner` so that the destructor runs after releasing the lock. let mut _t_outdated; @@ -298,6 +318,7 @@ impl Transaction { } let res = if let Some(thread) = self.find_target_thread() { + info.to_tid = thread.id; crate::trace::trace_transaction(false, &self, Some(&thread.task)); match thread.push_work(self) { PushWorkRes::Ok => Ok(()), @@ -430,6 +451,8 @@ impl DeliverToRead for Transaction { self.drop_outstanding_txn(); + crate::trace::trace_transaction_received(&self); + // When this is not a reply and not a oneway transaction, update `current_transaction`. If // it's a reply, `current_transaction` has already been updated appropriately. if self.target_node.is_some() && tr_sec.transaction_data.flags & TF_ONE_WAY == 0 { diff --git a/drivers/ata/pata_parport/pata_parport.c b/drivers/ata/pata_parport/pata_parport.c index a5b959891cb7..40baeac594a9 100644 --- a/drivers/ata/pata_parport/pata_parport.c +++ b/drivers/ata/pata_parport/pata_parport.c @@ -459,19 +459,11 @@ static void pata_parport_dev_release(struct device *dev) kfree(pi); } -static void pata_parport_bus_release(struct device *dev) -{ - /* nothing to do here but required to avoid warning on device removal */ -} - static const struct bus_type pata_parport_bus_type = { .name = DRV_NAME, }; -static struct device pata_parport_bus = { - .init_name = DRV_NAME, - .release = pata_parport_bus_release, -}; +static struct device *pata_parport_bus; static const struct scsi_host_template pata_parport_sht = { PATA_PARPORT_SHT("pata_parport") @@ -518,7 +510,7 @@ static struct pi_adapter *pi_init_one(struct parport *parport, } /* set up pi->dev before pi_probe_unit() so it can use dev_printk() */ - pi->dev.parent = &pata_parport_bus; + pi->dev.parent = pata_parport_bus; pi->dev.bus = &pata_parport_bus_type; pi->dev.driver = &pr->driver; pi->dev.release = pata_parport_dev_release; @@ -780,8 +772,9 @@ static __init int pata_parport_init(void) return error; } - error = device_register(&pata_parport_bus); - if (error) { + pata_parport_bus = root_device_register(DRV_NAME); + if (IS_ERR(pata_parport_bus)) { + error = PTR_ERR(pata_parport_bus); pr_err("failed to register pata_parport bus, error: %d\n", error); goto out_unregister_bus; } @@ -811,7 +804,7 @@ out_remove_del: out_remove_new: bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device); out_unregister_dev: - device_unregister(&pata_parport_bus); + root_device_unregister(pata_parport_bus); out_unregister_bus: bus_unregister(&pata_parport_bus_type); return error; @@ -822,7 +815,7 @@ static __exit void pata_parport_exit(void) parport_unregister_driver(&pata_parport_driver); bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device); bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device); - device_unregister(&pata_parport_bus); + root_device_unregister(pata_parport_bus); bus_unregister(&pata_parport_bus_type); } diff --git a/drivers/atm/.gitignore b/drivers/atm/.gitignore deleted file mode 100644 index ddd374e91965..000000000000 --- a/drivers/atm/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -fore200e_mkfirm -fore200e_pca_fw.c -pca200e.bin -pca200e_ecd.bin2 diff --git a/drivers/atm/Kconfig b/drivers/atm/Kconfig index 63cdb46a3439..cb1dd7789ff1 100644 --- a/drivers/atm/Kconfig +++ b/drivers/atm/Kconfig @@ -15,306 +15,6 @@ menuconfig ATM_DRIVERS if ATM_DRIVERS && NETDEVICES && ATM -config ATM_DUMMY - tristate "Dummy ATM driver" - help - Dummy ATM driver. Useful for proxy signalling, testing, - and development. If unsure, say N. - -config ATM_TCP - tristate "ATM over TCP" - depends on INET - help - ATM over TCP driver. Useful mainly for development and for - experiments. If unsure, say N. - -config ATM_LANAI - tristate "Efficient Networks Speedstream 3010" - depends on PCI && ATM - help - Supports ATM cards based on the Efficient Networks "Lanai" - chipset such as the Speedstream 3010 and the ENI-25p. The - Speedstream 3060 is currently not supported since we don't - have the code to drive the on-board Alcatel DSL chipset (yet). - -config ATM_ENI - tristate "Efficient Networks ENI155P" - depends on PCI - help - Driver for the Efficient Networks ENI155p series and SMC ATM - Power155 155 Mbps ATM adapters. Both, the versions with 512KB and - 2MB on-board RAM (Efficient calls them "C" and "S", respectively), - and the FPGA and the ASIC Tonga versions of the board are supported. - The driver works with MMF (-MF or ...F) and UTP-5 (-U5 or ...D) - adapters. - - To compile this driver as a module, choose M here: the module will - be called eni. - -config ATM_ENI_DEBUG - bool "Enable extended debugging" - depends on ATM_ENI - help - Extended debugging records various events and displays that list - when an inconsistency is detected. This mechanism is faster than - generally using printks, but still has some impact on performance. - Note that extended debugging may create certain race conditions - itself. Enable this ONLY if you suspect problems with the driver. - -config ATM_ENI_TUNE_BURST - bool "Fine-tune burst settings" - depends on ATM_ENI - help - In order to obtain good throughput, the ENI NIC can transfer - multiple words of data per PCI bus access cycle. Such a multi-word - transfer is called a burst. - - The default settings for the burst sizes are suitable for most PCI - chipsets. However, in some cases, large bursts may overrun buffers - in the PCI chipset and cause data corruption. In such cases, large - bursts must be disabled and only (slower) small bursts can be used. - The burst sizes can be set independently in the send (TX) and - receive (RX) direction. - - Note that enabling many different burst sizes in the same direction - may increase the cost of setting up a transfer such that the - resulting throughput is lower than when using only the largest - available burst size. - - Also, sometimes larger bursts lead to lower throughput, e.g. on an - Intel 440FX board, a drop from 135 Mbps to 103 Mbps was observed - when going from 8W to 16W bursts. - -config ATM_ENI_BURST_TX_16W - bool "Enable 16W TX bursts (discouraged)" - depends on ATM_ENI_TUNE_BURST - help - Burst sixteen words at once in the send direction. This may work - with recent PCI chipsets, but is known to fail with older chipsets. - -config ATM_ENI_BURST_TX_8W - bool "Enable 8W TX bursts (recommended)" - depends on ATM_ENI_TUNE_BURST - help - Burst eight words at once in the send direction. This is the default - setting. - -config ATM_ENI_BURST_TX_4W - bool "Enable 4W TX bursts (optional)" - depends on ATM_ENI_TUNE_BURST - help - Burst four words at once in the send direction. You may want to try - this if you have disabled 8W bursts. Enabling 4W if 8W is also set - may or may not improve throughput. - -config ATM_ENI_BURST_TX_2W - bool "Enable 2W TX bursts (optional)" - depends on ATM_ENI_TUNE_BURST - help - Burst two words at once in the send direction. You may want to try - this if you have disabled 4W and 8W bursts. Enabling 2W if 4W or 8W - are also set may or may not improve throughput. - -config ATM_ENI_BURST_RX_16W - bool "Enable 16W RX bursts (discouraged)" - depends on ATM_ENI_TUNE_BURST - help - Burst sixteen words at once in the receive direction. This may work - with recent PCI chipsets, but is known to fail with older chipsets. - -config ATM_ENI_BURST_RX_8W - bool "Enable 8W RX bursts (discouraged)" - depends on ATM_ENI_TUNE_BURST - help - Burst eight words at once in the receive direction. This may work - with recent PCI chipsets, but is known to fail with older chipsets, - such as the Intel Neptune series. - -config ATM_ENI_BURST_RX_4W - bool "Enable 4W RX bursts (recommended)" - depends on ATM_ENI_TUNE_BURST - help - Burst four words at once in the receive direction. This is the - default setting. Enabling 4W if 8W is also set may or may not - improve throughput. - -config ATM_ENI_BURST_RX_2W - bool "Enable 2W RX bursts (optional)" - depends on ATM_ENI_TUNE_BURST - help - Burst two words at once in the receive direction. You may want to - try this if you have disabled 4W and 8W bursts. Enabling 2W if 4W or - 8W are also set may or may not improve throughput. - -config ATM_NICSTAR - tristate "IDT 77201 (NICStAR) (ForeRunnerLE)" - depends on PCI - help - The NICStAR chipset family is used in a large number of ATM NICs for - 25 and for 155 Mbps, including IDT cards and the Fore ForeRunnerLE - series. Say Y if you have one of those. - - To compile this driver as a module, choose M here: the module will - be called nicstar. - -config ATM_NICSTAR_USE_SUNI - bool "Use suni PHY driver (155Mbps)" - depends on ATM_NICSTAR - help - Support for the S-UNI and compatible PHYsical layer chips. These are - found in most 155Mbps NICStAR based ATM cards, namely in the - ForeRunner LE155 cards. This driver provides detection of cable~ - removal and reinsertion and provides some statistics. This driver - doesn't have removal capability when compiled as a module, so if you - need that capability don't include S-UNI support (it's not needed to - make the card work). - -config ATM_NICSTAR_USE_IDT77105 - bool "Use IDT77105 PHY driver (25Mbps)" - depends on ATM_NICSTAR - help - Support for the PHYsical layer chip in ForeRunner LE25 cards. In - addition to cable removal/reinsertion detection, this driver allows - you to control the loopback mode of the chip via a dedicated IOCTL. - This driver is required for proper handling of temporary carrier - loss, so if you have a 25Mbps NICStAR based ATM card you must say Y. - -config ATM_IDT77252 - tristate "IDT 77252 (NICStAR II)" - depends on PCI - help - Driver for the IDT 77252 ATM PCI chips. - - To compile this driver as a module, choose M here: the module will - be called idt77252. - -config ATM_IDT77252_DEBUG - bool "Enable debugging messages" - depends on ATM_IDT77252 - help - Somewhat useful debugging messages are available. The choice of - messages is controlled by a bitmap. This may be specified as a - module argument. See the file for - the meanings of the bits in the mask. - - When active, these messages can have a significant impact on the - speed of the driver, and the size of your syslog files! When - inactive, they will have only a modest impact on performance. - -config ATM_IDT77252_RCV_ALL - bool "Receive ALL cells in raw queue" - depends on ATM_IDT77252 - help - Enable receiving of all cells on the ATM link, that do not match - an open connection in the raw cell queue of the driver. Useful - for debugging or special applications only, so the safe answer is N. - -config ATM_IDT77252_USE_SUNI - bool - depends on ATM_IDT77252 - default y - -config ATM_IA - tristate "Interphase ATM PCI x575/x525/x531" - depends on PCI - help - This is a driver for the Interphase (i)ChipSAR adapter cards - which include a variety of variants in term of the size of the - control memory (128K-1KVC, 512K-4KVC), the size of the packet - memory (128K, 512K, 1M), and the PHY type (Single/Multi mode OC3, - UTP155, UTP25, DS3 and E3). Go to: - - for more info about the cards. Say Y (or M to compile as a module - named iphase) here if you have one of these cards. - - See the file - - for further details. - -config ATM_IA_DEBUG - bool "Enable debugging messages" - depends on ATM_IA - help - Somewhat useful debugging messages are available. The choice of - messages is controlled by a bitmap. This may be specified as a - module argument (kernel command line argument as well?), changed - dynamically using an ioctl (Get the debug utility, iadbg, from - ). - - See the file for the meanings of the - bits in the mask. - - When active, these messages can have a significant impact on the - speed of the driver, and the size of your syslog files! When - inactive, they will have only a modest impact on performance. - -config ATM_FORE200E - tristate "FORE Systems 200E-series" - depends on (PCI || SBUS) - select FW_LOADER - help - This is a driver for the FORE Systems 200E-series ATM adapter - cards. It simultaneously supports PCA-200E and SBA-200E models - on PCI and SBUS hosts. Say Y (or M to compile as a module - named fore_200e) here if you have one of these ATM adapters. - - See the file - for - further details. - -config ATM_FORE200E_USE_TASKLET - bool "Defer interrupt work to a tasklet" - depends on ATM_FORE200E - default n - help - This defers work to be done by the interrupt handler to a - tasklet instead of handling everything at interrupt time. This - may improve the responsive of the host. - -config ATM_FORE200E_TX_RETRY - int "Maximum number of tx retries" - depends on ATM_FORE200E - default "16" - help - Specifies the number of times the driver attempts to transmit - a message before giving up, if the transmit queue of the ATM card - is transiently saturated. - - Saturation of the transmit queue may occur only under extreme - conditions, e.g. when a fast host continuously submits very small - frames (<64 bytes) or raw AAL0 cells (48 bytes) to the ATM adapter. - - Note that under common conditions, it is unlikely that you encounter - a saturation of the transmit queue, so the retry mechanism never - comes into play. - -config ATM_FORE200E_DEBUG - int "Debugging level (0-3)" - depends on ATM_FORE200E - default "0" - help - Specifies the level of debugging messages issued by the driver. - The verbosity of the driver increases with the value of this - parameter. - - When active, these messages can have a significant impact on - the performances of the driver, and the size of your syslog files! - Keep the debugging level to 0 during normal operations. - -config ATM_HE - tristate "ForeRunner HE Series" - depends on PCI - help - This is a driver for the Marconi ForeRunner HE-series ATM adapter - cards. It simultaneously supports the 155 and 622 versions. - -config ATM_HE_USE_SUNI - bool "Use S/UNI PHY driver" - depends on ATM_HE - help - Support for the S/UNI-Ultra and S/UNI-622 found in the ForeRunner - HE cards. This driver provides carrier detection some statistics. - config ATM_SOLOS tristate "Solos ADSL2+ PCI Multiport card driver" depends on PCI diff --git a/drivers/atm/Makefile b/drivers/atm/Makefile index c9eade92019b..b95125a0bf12 100644 --- a/drivers/atm/Makefile +++ b/drivers/atm/Makefile @@ -1,32 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the Linux network (ATM) device drivers. -# -fore_200e-y := fore200e.o - -obj-$(CONFIG_ATM_NICSTAR) += nicstar.o -obj-$(CONFIG_ATM_IA) += iphase.o suni.o -obj-$(CONFIG_ATM_FORE200E) += fore_200e.o -obj-$(CONFIG_ATM_ENI) += eni.o suni.o -obj-$(CONFIG_ATM_IDT77252) += idt77252.o obj-$(CONFIG_ATM_SOLOS) += solos-pci.o - -ifeq ($(CONFIG_ATM_NICSTAR_USE_SUNI),y) - obj-$(CONFIG_ATM_NICSTAR) += suni.o -endif -ifeq ($(CONFIG_ATM_NICSTAR_USE_IDT77105),y) - obj-$(CONFIG_ATM_NICSTAR) += idt77105.o -endif -ifeq ($(CONFIG_ATM_IDT77252_USE_SUNI),y) - obj-$(CONFIG_ATM_IDT77252) += suni.o -endif - -obj-$(CONFIG_ATM_DUMMY) += adummy.o -obj-$(CONFIG_ATM_TCP) += atmtcp.o -obj-$(CONFIG_ATM_LANAI) += lanai.o - -obj-$(CONFIG_ATM_HE) += he.o -ifeq ($(CONFIG_ATM_HE_USE_SUNI),y) - obj-$(CONFIG_ATM_HE) += suni.o -endif diff --git a/drivers/atm/adummy.c b/drivers/atm/adummy.c deleted file mode 100644 index c8d00537d236..000000000000 --- a/drivers/atm/adummy.c +++ /dev/null @@ -1,202 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * adummy.c: a dummy ATM driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* version definition */ - -#define DRV_VERSION "1.0" - -#define DEV_LABEL "adummy" - -#define ADUMMY_DEV(dev) ((struct adummy_dev *) (dev)->dev_data) - -struct adummy_dev { - struct atm_dev *atm_dev; - - struct list_head entry; -}; - -/* globals */ - -static LIST_HEAD(adummy_devs); - -static ssize_t __set_signal(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct atm_dev *atm_dev = container_of(dev, struct atm_dev, class_dev); - int signal; - - if (sscanf(buf, "%d", &signal) == 1) { - - if (signal < ATM_PHY_SIG_LOST || signal > ATM_PHY_SIG_FOUND) - signal = ATM_PHY_SIG_UNKNOWN; - - atm_dev_signal_change(atm_dev, signal); - return 1; - } - return -EINVAL; -} - -static ssize_t __show_signal(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct atm_dev *atm_dev = container_of(dev, struct atm_dev, class_dev); - return sprintf(buf, "%d\n", atm_dev->signal); -} -static DEVICE_ATTR(signal, 0644, __show_signal, __set_signal); - -static struct attribute *adummy_attrs[] = { - &dev_attr_signal.attr, - NULL -}; - -static const struct attribute_group adummy_group_attrs = { - .name = NULL, /* We want them in dev's root folder */ - .attrs = adummy_attrs -}; - -static int __init -adummy_start(struct atm_dev *dev) -{ - dev->ci_range.vpi_bits = 4; - dev->ci_range.vci_bits = 12; - - return 0; -} - -static int -adummy_open(struct atm_vcc *vcc) -{ - short vpi = vcc->vpi; - int vci = vcc->vci; - - if (vci == ATM_VCI_UNSPEC || vpi == ATM_VPI_UNSPEC) - return 0; - - set_bit(ATM_VF_ADDR, &vcc->flags); - set_bit(ATM_VF_READY, &vcc->flags); - - return 0; -} - -static void -adummy_close(struct atm_vcc *vcc) -{ - clear_bit(ATM_VF_READY, &vcc->flags); - clear_bit(ATM_VF_ADDR, &vcc->flags); -} - -static int -adummy_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb_any(skb); - atomic_inc(&vcc->stats->tx); - - return 0; -} - -static int -adummy_proc_read(struct atm_dev *dev, loff_t *pos, char *page) -{ - int left = *pos; - - if (!left--) - return sprintf(page, "version %s\n", DRV_VERSION); - - return 0; -} - -static const struct atmdev_ops adummy_ops = -{ - .open = adummy_open, - .close = adummy_close, - .send = adummy_send, - .proc_read = adummy_proc_read, - .owner = THIS_MODULE -}; - -static int __init adummy_init(void) -{ - struct atm_dev *atm_dev; - struct adummy_dev *adummy_dev; - int err = 0; - - printk(KERN_ERR "adummy: version %s\n", DRV_VERSION); - - adummy_dev = kzalloc_obj(struct adummy_dev); - if (!adummy_dev) { - printk(KERN_ERR DEV_LABEL ": kzalloc() failed\n"); - err = -ENOMEM; - goto out; - } - atm_dev = atm_dev_register(DEV_LABEL, NULL, &adummy_ops, -1, NULL); - if (!atm_dev) { - printk(KERN_ERR DEV_LABEL ": atm_dev_register() failed\n"); - err = -ENODEV; - goto out_kfree; - } - - adummy_dev->atm_dev = atm_dev; - atm_dev->dev_data = adummy_dev; - - if (sysfs_create_group(&atm_dev->class_dev.kobj, &adummy_group_attrs)) - dev_err(&atm_dev->class_dev, "Could not register attrs for adummy\n"); - - if (adummy_start(atm_dev)) { - printk(KERN_ERR DEV_LABEL ": adummy_start() failed\n"); - err = -ENODEV; - goto out_unregister; - } - - list_add(&adummy_dev->entry, &adummy_devs); -out: - return err; - -out_unregister: - atm_dev_deregister(atm_dev); -out_kfree: - kfree(adummy_dev); - goto out; -} - -static void __exit adummy_cleanup(void) -{ - struct adummy_dev *adummy_dev, *next; - - list_for_each_entry_safe(adummy_dev, next, &adummy_devs, entry) { - atm_dev_deregister(adummy_dev->atm_dev); - kfree(adummy_dev); - } -} - -module_init(adummy_init); -module_exit(adummy_cleanup); - -MODULE_AUTHOR("chas williams "); -MODULE_DESCRIPTION("dummy ATM driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c deleted file mode 100644 index 96719851ae2a..000000000000 --- a/drivers/atm/atmtcp.c +++ /dev/null @@ -1,513 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* drivers/atm/atmtcp.c - ATM over TCP "device" driver */ - -/* Written 1997-2000 by Werner Almesberger, EPFL LRC/ICA */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -extern int atm_init_aal5(struct atm_vcc *vcc); /* "raw" AAL5 transport */ - - -#define PRIV(dev) ((struct atmtcp_dev_data *) ((dev)->dev_data)) - - -struct atmtcp_dev_data { - struct atm_vcc *vcc; /* control VCC; NULL if detached */ - int persist; /* non-zero if persistent */ -}; - - -#define DEV_LABEL "atmtcp" - -#define MAX_VPI_BITS 8 /* simplifies life */ -#define MAX_VCI_BITS 16 - - -/* - * Hairy code ahead: the control VCC may be closed while we're still - * waiting for an answer, so we need to re-validate out_vcc every once - * in a while. - */ - - -static int atmtcp_send_control(struct atm_vcc *vcc,int type, - const struct atmtcp_control *msg,int flag) -{ - DECLARE_WAITQUEUE(wait,current); - struct atm_vcc *out_vcc; - struct sk_buff *skb; - struct atmtcp_control *new_msg; - int old_test; - int error = 0; - - out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL; - if (!out_vcc) return -EUNATCH; - skb = alloc_skb(sizeof(*msg),GFP_KERNEL); - if (!skb) return -ENOMEM; - mb(); - out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL; - if (!out_vcc) { - dev_kfree_skb(skb); - return -EUNATCH; - } - atm_force_charge(out_vcc,skb->truesize); - new_msg = skb_put(skb, sizeof(*new_msg)); - *new_msg = *msg; - new_msg->hdr.length = ATMTCP_HDR_MAGIC; - new_msg->type = type; - memset(&new_msg->vcc,0,sizeof(atm_kptr_t)); - *(struct atm_vcc **) &new_msg->vcc = vcc; - old_test = test_bit(flag,&vcc->flags); - out_vcc->push(out_vcc,skb); - add_wait_queue(sk_sleep(sk_atm(vcc)), &wait); - while (test_bit(flag,&vcc->flags) == old_test) { - mb(); - out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL; - if (!out_vcc) { - error = -EUNATCH; - break; - } - set_current_state(TASK_UNINTERRUPTIBLE); - schedule(); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk_atm(vcc)), &wait); - return error; -} - - -static int atmtcp_recv_control(const struct atmtcp_control *msg) -{ - struct atm_vcc *vcc = *(struct atm_vcc **) &msg->vcc; - - vcc->vpi = msg->addr.sap_addr.vpi; - vcc->vci = msg->addr.sap_addr.vci; - vcc->qos = msg->qos; - sk_atm(vcc)->sk_err = -msg->result; - switch (msg->type) { - case ATMTCP_CTRL_OPEN: - change_bit(ATM_VF_READY,&vcc->flags); - break; - case ATMTCP_CTRL_CLOSE: - change_bit(ATM_VF_ADDR,&vcc->flags); - break; - default: - printk(KERN_ERR "atmtcp_recv_control: unknown type %d\n", - msg->type); - return -EINVAL; - } - wake_up(sk_sleep(sk_atm(vcc))); - return 0; -} - - -static void atmtcp_v_dev_close(struct atm_dev *dev) -{ - /* Nothing.... Isn't this simple :-) -- REW */ -} - - -static int atmtcp_v_open(struct atm_vcc *vcc) -{ - struct atmtcp_control msg; - int error; - short vpi = vcc->vpi; - int vci = vcc->vci; - - memset(&msg,0,sizeof(msg)); - msg.addr.sap_family = AF_ATMPVC; - msg.hdr.vpi = htons(vpi); - msg.addr.sap_addr.vpi = vpi; - msg.hdr.vci = htons(vci); - msg.addr.sap_addr.vci = vci; - if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) return 0; - msg.type = ATMTCP_CTRL_OPEN; - msg.qos = vcc->qos; - set_bit(ATM_VF_ADDR,&vcc->flags); - clear_bit(ATM_VF_READY,&vcc->flags); /* just in case ... */ - error = atmtcp_send_control(vcc,ATMTCP_CTRL_OPEN,&msg,ATM_VF_READY); - if (error) return error; - return -sk_atm(vcc)->sk_err; -} - - -static void atmtcp_v_close(struct atm_vcc *vcc) -{ - struct atmtcp_control msg; - - memset(&msg,0,sizeof(msg)); - msg.addr.sap_family = AF_ATMPVC; - msg.addr.sap_addr.vpi = vcc->vpi; - msg.addr.sap_addr.vci = vcc->vci; - clear_bit(ATM_VF_READY,&vcc->flags); - (void) atmtcp_send_control(vcc,ATMTCP_CTRL_CLOSE,&msg,ATM_VF_ADDR); -} - - -static int atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) -{ - struct atm_cirange ci; - struct atm_vcc *vcc; - struct sock *s; - int i; - - if (cmd != ATM_SETCIRANGE) return -ENOIOCTLCMD; - if (copy_from_user(&ci, arg,sizeof(ci))) return -EFAULT; - if (ci.vpi_bits == ATM_CI_MAX) ci.vpi_bits = MAX_VPI_BITS; - if (ci.vci_bits == ATM_CI_MAX) ci.vci_bits = MAX_VCI_BITS; - if (ci.vpi_bits > MAX_VPI_BITS || ci.vpi_bits < 0 || - ci.vci_bits > MAX_VCI_BITS || ci.vci_bits < 0) return -EINVAL; - read_lock(&vcc_sklist_lock); - for(i = 0; i < VCC_HTABLE_SIZE; ++i) { - struct hlist_head *head = &vcc_hash[i]; - - sk_for_each(s, head) { - vcc = atm_sk(s); - if (vcc->dev != dev) - continue; - if ((vcc->vpi >> ci.vpi_bits) || - (vcc->vci >> ci.vci_bits)) { - read_unlock(&vcc_sklist_lock); - return -EBUSY; - } - } - } - read_unlock(&vcc_sklist_lock); - dev->ci_range = ci; - return 0; -} - - -static int atmtcp_v_send(struct atm_vcc *vcc,struct sk_buff *skb) -{ - struct atmtcp_dev_data *dev_data; - struct atm_vcc *out_vcc=NULL; /* Initializer quietens GCC warning */ - struct sk_buff *new_skb; - struct atmtcp_hdr *hdr; - int size; - - if (vcc->qos.txtp.traffic_class == ATM_NONE) { - if (vcc->pop) vcc->pop(vcc,skb); - else dev_kfree_skb(skb); - return -EINVAL; - } - dev_data = PRIV(vcc->dev); - if (dev_data) out_vcc = dev_data->vcc; - if (!dev_data || !out_vcc) { - if (vcc->pop) vcc->pop(vcc,skb); - else dev_kfree_skb(skb); - if (dev_data) return 0; - atomic_inc(&vcc->stats->tx_err); - return -ENOLINK; - } - size = skb->len+sizeof(struct atmtcp_hdr); - new_skb = atm_alloc_charge(out_vcc,size,GFP_ATOMIC); - if (!new_skb) { - if (vcc->pop) vcc->pop(vcc,skb); - else dev_kfree_skb(skb); - atomic_inc(&vcc->stats->tx_err); - return -ENOBUFS; - } - hdr = skb_put(new_skb, sizeof(struct atmtcp_hdr)); - hdr->vpi = htons(vcc->vpi); - hdr->vci = htons(vcc->vci); - hdr->length = htonl(skb->len); - skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len); - if (vcc->pop) vcc->pop(vcc,skb); - else dev_kfree_skb(skb); - out_vcc->push(out_vcc,new_skb); - atomic_inc(&vcc->stats->tx); - atomic_inc(&out_vcc->stats->rx); - return 0; -} - - -static int atmtcp_v_proc(struct atm_dev *dev,loff_t *pos,char *page) -{ - struct atmtcp_dev_data *dev_data = PRIV(dev); - - if (*pos) return 0; - if (!dev_data->persist) return sprintf(page,"ephemeral\n"); - return sprintf(page,"persistent, %sconnected\n", - dev_data->vcc ? "" : "dis"); -} - - -static void atmtcp_c_close(struct atm_vcc *vcc) -{ - struct atm_dev *atmtcp_dev; - struct atmtcp_dev_data *dev_data; - - atmtcp_dev = (struct atm_dev *) vcc->dev_data; - dev_data = PRIV(atmtcp_dev); - dev_data->vcc = NULL; - if (dev_data->persist) return; - atmtcp_dev->dev_data = NULL; - kfree(dev_data); - atm_dev_deregister(atmtcp_dev); - vcc->dev_data = NULL; - module_put(THIS_MODULE); -} - - -static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci) -{ - struct hlist_head *head; - struct atm_vcc *vcc; - struct sock *s; - - head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; - - sk_for_each(s, head) { - vcc = atm_sk(s); - if (vcc->dev == dev && - vcc->vci == vci && vcc->vpi == vpi && - vcc->qos.rxtp.traffic_class != ATM_NONE) { - return vcc; - } - } - return NULL; -} - -static int atmtcp_c_pre_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct atmtcp_hdr *hdr; - - if (skb->len < sizeof(struct atmtcp_hdr)) - return -EINVAL; - - hdr = (struct atmtcp_hdr *)skb->data; - if (hdr->length == ATMTCP_HDR_MAGIC) - return -EINVAL; - - return 0; -} - -static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb) -{ - struct atm_dev *dev; - struct atmtcp_hdr *hdr; - struct atm_vcc *out_vcc; - struct sk_buff *new_skb; - int result = 0; - - dev = vcc->dev_data; - hdr = (struct atmtcp_hdr *) skb->data; - if (hdr->length == ATMTCP_HDR_MAGIC) { - result = atmtcp_recv_control( - (struct atmtcp_control *) skb->data); - goto done; - } - read_lock(&vcc_sklist_lock); - out_vcc = find_vcc(dev, ntohs(hdr->vpi), ntohs(hdr->vci)); - read_unlock(&vcc_sklist_lock); - if (!out_vcc) { - result = -EUNATCH; - atomic_inc(&vcc->stats->tx_err); - goto done; - } - skb_pull(skb,sizeof(struct atmtcp_hdr)); - new_skb = atm_alloc_charge(out_vcc,skb->len,GFP_KERNEL); - if (!new_skb) { - result = -ENOBUFS; - goto done; - } - __net_timestamp(new_skb); - skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len); - out_vcc->push(out_vcc,new_skb); - atomic_inc(&vcc->stats->tx); - atomic_inc(&out_vcc->stats->rx); -done: - if (vcc->pop) vcc->pop(vcc,skb); - else dev_kfree_skb(skb); - return result; -} - - -/* - * Device operations for the virtual ATM devices created by ATMTCP. - */ - - -static const struct atmdev_ops atmtcp_v_dev_ops = { - .dev_close = atmtcp_v_dev_close, - .open = atmtcp_v_open, - .close = atmtcp_v_close, - .ioctl = atmtcp_v_ioctl, - .send = atmtcp_v_send, - .proc_read = atmtcp_v_proc, - .owner = THIS_MODULE -}; - - -/* - * Device operations for the ATMTCP control device. - */ - - -static const struct atmdev_ops atmtcp_c_dev_ops = { - .close = atmtcp_c_close, - .pre_send = atmtcp_c_pre_send, - .send = atmtcp_c_send -}; - - -static struct atm_dev atmtcp_control_dev = { - .ops = &atmtcp_c_dev_ops, - .type = "atmtcp", - .number = 999, - .lock = __SPIN_LOCK_UNLOCKED(atmtcp_control_dev.lock) -}; - - -static int atmtcp_create(int itf,int persist,struct atm_dev **result) -{ - struct atmtcp_dev_data *dev_data; - struct atm_dev *dev; - - dev_data = kmalloc_obj(*dev_data); - if (!dev_data) - return -ENOMEM; - - dev = atm_dev_register(DEV_LABEL,NULL,&atmtcp_v_dev_ops,itf,NULL); - if (!dev) { - kfree(dev_data); - return itf == -1 ? -ENOMEM : -EBUSY; - } - dev->ci_range.vpi_bits = MAX_VPI_BITS; - dev->ci_range.vci_bits = MAX_VCI_BITS; - dev->dev_data = dev_data; - PRIV(dev)->vcc = NULL; - PRIV(dev)->persist = persist; - if (result) *result = dev; - return 0; -} - - -static int atmtcp_attach(struct atm_vcc *vcc,int itf) -{ - struct atm_dev *dev; - - dev = NULL; - if (itf != -1) dev = atm_dev_lookup(itf); - if (dev) { - if (dev->ops != &atmtcp_v_dev_ops) { - atm_dev_put(dev); - return -EMEDIUMTYPE; - } - if (PRIV(dev)->vcc) { - atm_dev_put(dev); - return -EBUSY; - } - } - else { - int error; - - error = atmtcp_create(itf,0,&dev); - if (error) return error; - } - PRIV(dev)->vcc = vcc; - vcc->dev = &atmtcp_control_dev; - vcc_insert_socket(sk_atm(vcc)); - set_bit(ATM_VF_META,&vcc->flags); - set_bit(ATM_VF_READY,&vcc->flags); - vcc->dev_data = dev; - (void) atm_init_aal5(vcc); /* @@@ losing AAL in transit ... */ - vcc->stats = &atmtcp_control_dev.stats.aal5; - return dev->number; -} - - -static int atmtcp_create_persistent(int itf) -{ - return atmtcp_create(itf,1,NULL); -} - - -static int atmtcp_remove_persistent(int itf) -{ - struct atm_dev *dev; - struct atmtcp_dev_data *dev_data; - - dev = atm_dev_lookup(itf); - if (!dev) return -ENODEV; - if (dev->ops != &atmtcp_v_dev_ops) { - atm_dev_put(dev); - return -EMEDIUMTYPE; - } - dev_data = PRIV(dev); - if (!dev_data->persist) { - atm_dev_put(dev); - return 0; - } - dev_data->persist = 0; - if (PRIV(dev)->vcc) { - atm_dev_put(dev); - return 0; - } - kfree(dev_data); - atm_dev_put(dev); - atm_dev_deregister(dev); - return 0; -} - -static int atmtcp_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - int err = 0; - struct atm_vcc *vcc = ATM_SD(sock); - - if (cmd != SIOCSIFATMTCP && cmd != ATMTCP_CREATE && cmd != ATMTCP_REMOVE) - return -ENOIOCTLCMD; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch (cmd) { - case SIOCSIFATMTCP: - err = atmtcp_attach(vcc, (int) arg); - if (err >= 0) { - sock->state = SS_CONNECTED; - __module_get(THIS_MODULE); - } - break; - case ATMTCP_CREATE: - err = atmtcp_create_persistent((int) arg); - break; - case ATMTCP_REMOVE: - err = atmtcp_remove_persistent((int) arg); - break; - } - return err; -} - -static struct atm_ioctl atmtcp_ioctl_ops = { - .owner = THIS_MODULE, - .ioctl = atmtcp_ioctl, -}; - -static __init int atmtcp_init(void) -{ - register_atm_ioctl(&atmtcp_ioctl_ops); - return 0; -} - - -static void __exit atmtcp_exit(void) -{ - deregister_atm_ioctl(&atmtcp_ioctl_ops); -} - -MODULE_DESCRIPTION("ATM over TCP"); -MODULE_LICENSE("GPL"); -module_init(atmtcp_init); -module_exit(atmtcp_exit); diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c deleted file mode 100644 index 12cb3aa588bc..000000000000 --- a/drivers/atm/eni.c +++ /dev/null @@ -1,2321 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tonga.h" -#include "midway.h" -#include "suni.h" -#include "eni.h" - -/* - * TODO: - * - * Show stoppers - * none - * - * Minor - * - OAM support - * - fix bugs listed below - */ - -/* - * KNOWN BUGS: - * - * - may run into JK-JK bug and deadlock - * - should allocate UBR channel first - * - buffer space allocation algorithm is stupid - * (RX: should be maxSDU+maxdelay*rate - * TX: should be maxSDU+min(maxSDU,maxdelay*rate) ) - * - doesn't support OAM cells - * - eni_put_free may hang if not putting memory fragments that _complete_ - * 2^n block (never happens in real life, though) - */ - - -#if 0 -#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) -#else -#define DPRINTK(format,args...) -#endif - - -#ifndef CONFIG_ATM_ENI_TUNE_BURST -#define CONFIG_ATM_ENI_BURST_TX_8W -#define CONFIG_ATM_ENI_BURST_RX_4W -#endif - - -#ifndef CONFIG_ATM_ENI_DEBUG - - -#define NULLCHECK(x) - -#define EVENT(s,a,b) - - -static void event_dump(void) -{ -} - - -#else - - -/* - * NULL pointer checking - */ - -#define NULLCHECK(x) \ - if ((unsigned long) (x) < 0x30) \ - printk(KERN_CRIT #x "==0x%lx\n",(unsigned long) (x)) - -/* - * Very extensive activity logging. Greatly improves bug detection speed but - * costs a few Mbps if enabled. - */ - -#define EV 64 - -static const char *ev[EV]; -static unsigned long ev_a[EV],ev_b[EV]; -static int ec = 0; - - -static void EVENT(const char *s,unsigned long a,unsigned long b) -{ - ev[ec] = s; - ev_a[ec] = a; - ev_b[ec] = b; - ec = (ec+1) % EV; -} - - -static void event_dump(void) -{ - int n,i; - - for (n = 0; n < EV; n++) { - i = (ec+n) % EV; - printk(KERN_NOTICE); - printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]); - } -} - - -#endif /* CONFIG_ATM_ENI_DEBUG */ - - -/* - * NExx must not be equal at end - * EExx may be equal at end - * xxPJOK verify validity of pointer jumps - * xxPMOK operating on a circular buffer of "c" words - */ - -#define NEPJOK(a0,a1,b) \ - ((a0) < (a1) ? (b) <= (a0) || (b) > (a1) : (b) <= (a0) && (b) > (a1)) -#define EEPJOK(a0,a1,b) \ - ((a0) < (a1) ? (b) < (a0) || (b) >= (a1) : (b) < (a0) && (b) >= (a1)) -#define NEPMOK(a0,d,b,c) NEPJOK(a0,(a0+d) & (c-1),b) -#define EEPMOK(a0,d,b,c) EEPJOK(a0,(a0+d) & (c-1),b) - - -static int tx_complete = 0,dma_complete = 0,queued = 0,requeued = 0, - backlogged = 0,rx_enqueued = 0,rx_dequeued = 0,pushed = 0,submitted = 0, - putting = 0; - -static struct atm_dev *eni_boards = NULL; - -/* Read/write registers on card */ -#define eni_in(r) readl(eni_dev->reg+(r)*4) -#define eni_out(v,r) writel((v),eni_dev->reg+(r)*4) - - -/*-------------------------------- utilities --------------------------------*/ - - -static void dump_mem(struct eni_dev *eni_dev) -{ - int i; - - for (i = 0; i < eni_dev->free_len; i++) - printk(KERN_DEBUG " %d: %p %d\n",i, - eni_dev->free_list[i].start, - 1 << eni_dev->free_list[i].order); -} - - -static void dump(struct atm_dev *dev) -{ - struct eni_dev *eni_dev; - - int i; - - eni_dev = ENI_DEV(dev); - printk(KERN_NOTICE "Free memory\n"); - dump_mem(eni_dev); - printk(KERN_NOTICE "TX buffers\n"); - for (i = 0; i < NR_CHAN; i++) - if (eni_dev->tx[i].send) - printk(KERN_NOTICE " TX %d @ %p: %ld\n",i, - eni_dev->tx[i].send,eni_dev->tx[i].words*4); - printk(KERN_NOTICE "RX buffers\n"); - for (i = 0; i < 1024; i++) - if (eni_dev->rx_map[i] && ENI_VCC(eni_dev->rx_map[i])->rx) - printk(KERN_NOTICE " RX %d @ %p: %ld\n",i, - ENI_VCC(eni_dev->rx_map[i])->recv, - ENI_VCC(eni_dev->rx_map[i])->words*4); - printk(KERN_NOTICE "----\n"); -} - - -static void eni_put_free(struct eni_dev *eni_dev, void __iomem *start, - unsigned long size) -{ - struct eni_free *list; - int len,order; - - DPRINTK("init 0x%lx+%ld(0x%lx)\n",start,size,size); - start += eni_dev->base_diff; - list = eni_dev->free_list; - len = eni_dev->free_len; - while (size) { - if (len >= eni_dev->free_list_size) { - printk(KERN_CRIT "eni_put_free overflow (%p,%ld)\n", - start,size); - break; - } - for (order = 0; !(((unsigned long)start | size) & (1 << order)); order++); - if (MID_MIN_BUF_SIZE > (1 << order)) { - printk(KERN_CRIT "eni_put_free: order %d too small\n", - order); - break; - } - list[len].start = (void __iomem *) start; - list[len].order = order; - len++; - start += 1 << order; - size -= 1 << order; - } - eni_dev->free_len = len; - /*dump_mem(eni_dev);*/ -} - - -static void __iomem *eni_alloc_mem(struct eni_dev *eni_dev, unsigned long *size) -{ - struct eni_free *list; - void __iomem *start; - int len,i,order,best_order,index; - - list = eni_dev->free_list; - len = eni_dev->free_len; - if (*size < MID_MIN_BUF_SIZE) *size = MID_MIN_BUF_SIZE; - if (*size > MID_MAX_BUF_SIZE) return NULL; - for (order = 0; (1 << order) < *size; order++) - ; - DPRINTK("trying: %ld->%d\n",*size,order); - best_order = 65; /* we don't have more than 2^64 of anything ... */ - index = 0; /* silence GCC */ - for (i = 0; i < len; i++) - if (list[i].order == order) { - best_order = order; - index = i; - break; - } - else if (best_order > list[i].order && list[i].order > order) { - best_order = list[i].order; - index = i; - } - if (best_order == 65) return NULL; - start = list[index].start-eni_dev->base_diff; - list[index] = list[--len]; - eni_dev->free_len = len; - *size = 1 << order; - eni_put_free(eni_dev,start+*size,(1 << best_order)-*size); - DPRINTK("%ld bytes (order %d) at 0x%lx\n",*size,order,start); - memset_io(start,0,*size); /* never leak data */ - /*dump_mem(eni_dev);*/ - return start; -} - - -static void eni_free_mem(struct eni_dev *eni_dev, void __iomem *start, - unsigned long size) -{ - struct eni_free *list; - int len,i,order; - - start += eni_dev->base_diff; - list = eni_dev->free_list; - len = eni_dev->free_len; - for (order = -1; size; order++) size >>= 1; - DPRINTK("eni_free_mem: %p+0x%lx (order %d)\n",start,size,order); - for (i = 0; i < len; i++) - if (((unsigned long) list[i].start) == ((unsigned long)start^(1 << order)) && - list[i].order == order) { - DPRINTK("match[%d]: 0x%lx/0x%lx(0x%x), %d/%d\n",i, - list[i].start,start,1 << order,list[i].order,order); - list[i] = list[--len]; - start = (void __iomem *) ((unsigned long) start & ~(unsigned long) (1 << order)); - order++; - i = -1; - continue; - } - if (len >= eni_dev->free_list_size) { - printk(KERN_ALERT "eni_free_mem overflow (%p,%d)\n",start, - order); - return; - } - list[len].start = start; - list[len].order = order; - eni_dev->free_len = len+1; - /*dump_mem(eni_dev);*/ -} - - -/*----------------------------------- RX ------------------------------------*/ - - -#define ENI_VCC_NOS ((struct atm_vcc *) 1) - - -static void rx_ident_err(struct atm_vcc *vcc) -{ - struct atm_dev *dev; - struct eni_dev *eni_dev; - struct eni_vcc *eni_vcc; - - dev = vcc->dev; - eni_dev = ENI_DEV(dev); - /* immediately halt adapter */ - eni_out(eni_in(MID_MC_S) & - ~(MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE),MID_MC_S); - /* dump useful information */ - eni_vcc = ENI_VCC(vcc); - printk(KERN_ALERT DEV_LABEL "(itf %d): driver error - RX ident " - "mismatch\n",dev->number); - printk(KERN_ALERT " VCI %d, rxing %d, words %ld\n",vcc->vci, - eni_vcc->rxing,eni_vcc->words); - printk(KERN_ALERT " host descr 0x%lx, rx pos 0x%lx, descr value " - "0x%x\n",eni_vcc->descr,eni_vcc->rx_pos, - (unsigned) readl(eni_vcc->recv+eni_vcc->descr*4)); - printk(KERN_ALERT " last %p, servicing %d\n",eni_vcc->last, - eni_vcc->servicing); - EVENT("---dump ends here---\n",0,0); - printk(KERN_NOTICE "---recent events---\n"); - event_dump(); - ENI_DEV(dev)->fast = NULL; /* really stop it */ - ENI_DEV(dev)->slow = NULL; - skb_queue_head_init(&ENI_DEV(dev)->rx_queue); -} - - -static int do_rx_dma(struct atm_vcc *vcc,struct sk_buff *skb, - unsigned long skip,unsigned long size,unsigned long eff) -{ - struct eni_dev *eni_dev; - struct eni_vcc *eni_vcc; - u32 dma_rd,dma_wr; - u32 dma[RX_DMA_BUF*2]; - dma_addr_t paddr; - unsigned long here; - int i,j; - - eni_dev = ENI_DEV(vcc->dev); - eni_vcc = ENI_VCC(vcc); - paddr = 0; /* GCC, shut up */ - if (skb) { - paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(&eni_dev->pci_dev->dev, paddr)) - goto dma_map_error; - ENI_PRV_PADDR(skb) = paddr; - if (paddr & 3) - printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %d has " - "mis-aligned RX data (0x%lx)\n",vcc->dev->number, - vcc->vci,(unsigned long) paddr); - ENI_PRV_SIZE(skb) = size+skip; - /* PDU plus descriptor */ - ATM_SKB(skb)->vcc = vcc; - } - j = 0; - if ((eff && skip) || 1) { /* @@@ actually, skip is always == 1 ... */ - here = (eni_vcc->descr+skip) & (eni_vcc->words-1); - dma[j++] = (here << MID_DMA_COUNT_SHIFT) | (vcc->vci - << MID_DMA_VCI_SHIFT) | MID_DT_JK; - dma[j++] = 0; - } - here = (eni_vcc->descr+size+skip) & (eni_vcc->words-1); - if (!eff) size += skip; - else { - unsigned long words; - - if (!size) { - DPRINTK("strange things happen ...\n"); - EVENT("strange things happen ... (skip=%ld,eff=%ld)\n", - size,eff); - } - words = eff; - if (paddr & 15) { - unsigned long init; - - init = 4-((paddr & 15) >> 2); - if (init > words) init = words; - dma[j++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) | - (vcc->vci << MID_DMA_VCI_SHIFT); - dma[j++] = paddr; - paddr += init << 2; - words -= init; - } -#ifdef CONFIG_ATM_ENI_BURST_RX_16W /* may work with some PCI chipsets ... */ - if (words & ~15) { - dma[j++] = MID_DT_16W | ((words >> 4) << - MID_DMA_COUNT_SHIFT) | (vcc->vci << - MID_DMA_VCI_SHIFT); - dma[j++] = paddr; - paddr += (words & ~15) << 2; - words &= 15; - } -#endif -#ifdef CONFIG_ATM_ENI_BURST_RX_8W /* works only with *some* PCI chipsets ... */ - if (words & ~7) { - dma[j++] = MID_DT_8W | ((words >> 3) << - MID_DMA_COUNT_SHIFT) | (vcc->vci << - MID_DMA_VCI_SHIFT); - dma[j++] = paddr; - paddr += (words & ~7) << 2; - words &= 7; - } -#endif -#ifdef CONFIG_ATM_ENI_BURST_RX_4W /* recommended */ - if (words & ~3) { - dma[j++] = MID_DT_4W | ((words >> 2) << - MID_DMA_COUNT_SHIFT) | (vcc->vci << - MID_DMA_VCI_SHIFT); - dma[j++] = paddr; - paddr += (words & ~3) << 2; - words &= 3; - } -#endif -#ifdef CONFIG_ATM_ENI_BURST_RX_2W /* probably useless if RX_4W, RX_8W, ... */ - if (words & ~1) { - dma[j++] = MID_DT_2W | ((words >> 1) << - MID_DMA_COUNT_SHIFT) | (vcc->vci << - MID_DMA_VCI_SHIFT); - dma[j++] = paddr; - paddr += (words & ~1) << 2; - words &= 1; - } -#endif - if (words) { - dma[j++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) - | (vcc->vci << MID_DMA_VCI_SHIFT); - dma[j++] = paddr; - } - } - if (size != eff) { - dma[j++] = (here << MID_DMA_COUNT_SHIFT) | - (vcc->vci << MID_DMA_VCI_SHIFT) | MID_DT_JK; - dma[j++] = 0; - } - if (!j || j > 2*RX_DMA_BUF) { - printk(KERN_CRIT DEV_LABEL "!j or j too big!!!\n"); - goto trouble; - } - dma[j-2] |= MID_DMA_END; - j = j >> 1; - dma_wr = eni_in(MID_DMA_WR_RX); - dma_rd = eni_in(MID_DMA_RD_RX); - /* - * Can I move the dma_wr pointer by 2j+1 positions without overwriting - * data that hasn't been read (position of dma_rd) yet ? - */ - if (!NEPMOK(dma_wr,j+j+1,dma_rd,NR_DMA_RX)) { /* @@@ +1 is ugly */ - printk(KERN_WARNING DEV_LABEL "(itf %d): RX DMA full\n", - vcc->dev->number); - goto trouble; - } - for (i = 0; i < j; i++) { - writel(dma[i*2],eni_dev->rx_dma+dma_wr*8); - writel(dma[i*2+1],eni_dev->rx_dma+dma_wr*8+4); - dma_wr = (dma_wr+1) & (NR_DMA_RX-1); - } - if (skb) { - ENI_PRV_POS(skb) = eni_vcc->descr+size+1; - skb_queue_tail(&eni_dev->rx_queue,skb); - eni_vcc->last = skb; - rx_enqueued++; - } - eni_vcc->descr = here; - eni_out(dma_wr,MID_DMA_WR_RX); - return 0; - -trouble: - if (paddr) - dma_unmap_single(&eni_dev->pci_dev->dev,paddr,skb->len, - DMA_FROM_DEVICE); -dma_map_error: - if (skb) dev_kfree_skb_irq(skb); - return -1; -} - - -static void discard(struct atm_vcc *vcc,unsigned long size) -{ - struct eni_vcc *eni_vcc; - - eni_vcc = ENI_VCC(vcc); - EVENT("discard (size=%ld)\n",size,0); - while (do_rx_dma(vcc,NULL,1,size,0)) EVENT("BUSY LOOP",0,0); - /* could do a full fallback, but that might be more expensive */ - if (eni_vcc->rxing) ENI_PRV_POS(eni_vcc->last) += size+1; - else eni_vcc->rx_pos = (eni_vcc->rx_pos+size+1) & (eni_vcc->words-1); -} - - -/* - * TODO: should check whether direct copies (without DMA setup, dequeuing on - * interrupt, etc.) aren't much faster for AAL0 - */ - -static int rx_aal0(struct atm_vcc *vcc) -{ - struct eni_vcc *eni_vcc; - unsigned long descr; - unsigned long length; - struct sk_buff *skb; - - DPRINTK(">rx_aal0\n"); - eni_vcc = ENI_VCC(vcc); - descr = readl(eni_vcc->recv+eni_vcc->descr*4); - if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) { - rx_ident_err(vcc); - return 1; - } - if (descr & MID_RED_T) { - DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n", - vcc->dev->number); - length = 0; - atomic_inc(&vcc->stats->rx_err); - } - else { - length = ATM_CELL_SIZE-1; /* no HEC */ - } - skb = length ? atm_alloc_charge(vcc,length,GFP_ATOMIC) : NULL; - if (!skb) { - discard(vcc,length >> 2); - return 0; - } - skb_put(skb,length); - skb->tstamp = eni_vcc->timestamp; - DPRINTK("got len %ld\n",length); - if (do_rx_dma(vcc,skb,1,length >> 2,length >> 2)) return 1; - eni_vcc->rxing++; - return 0; -} - - -static int rx_aal5(struct atm_vcc *vcc) -{ - struct eni_vcc *eni_vcc; - unsigned long descr; - unsigned long size,eff,length; - struct sk_buff *skb; - - EVENT("rx_aal5\n",0,0); - DPRINTK(">rx_aal5\n"); - eni_vcc = ENI_VCC(vcc); - descr = readl(eni_vcc->recv+eni_vcc->descr*4); - if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) { - rx_ident_err(vcc); - return 1; - } - if (descr & (MID_RED_T | MID_RED_CRC_ERR)) { - if (descr & MID_RED_T) { - EVENT("empty cell (descr=0x%lx)\n",descr,0); - DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n", - vcc->dev->number); - size = 0; - } - else { - static unsigned long silence = 0; - - if (time_after(jiffies, silence) || silence == 0) { - printk(KERN_WARNING DEV_LABEL "(itf %d): " - "discarding PDU(s) with CRC error\n", - vcc->dev->number); - silence = (jiffies+2*HZ)|1; - } - size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2); - EVENT("CRC error (descr=0x%lx,size=%ld)\n",descr, - size); - } - eff = length = 0; - atomic_inc(&vcc->stats->rx_err); - } - else { - size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2); - DPRINTK("size=%ld\n",size); - length = readl(eni_vcc->recv+(((eni_vcc->descr+size-1) & - (eni_vcc->words-1)))*4) & 0xffff; - /* -trailer(2)+header(1) */ - if (length && length <= (size << 2)-8 && length <= - ATM_MAX_AAL5_PDU) eff = (length+3) >> 2; - else { /* ^ trailer length (8) */ - EVENT("bad PDU (descr=0x08%lx,length=%ld)\n",descr, - length); - printk(KERN_ERR DEV_LABEL "(itf %d): bad AAL5 PDU " - "(VCI=%d,length=%ld,size=%ld (descr 0x%lx))\n", - vcc->dev->number,vcc->vci,length,size << 2,descr); - length = eff = 0; - atomic_inc(&vcc->stats->rx_err); - } - } - skb = eff ? atm_alloc_charge(vcc,eff << 2,GFP_ATOMIC) : NULL; - if (!skb) { - discard(vcc,size); - return 0; - } - skb_put(skb,length); - DPRINTK("got len %ld\n",length); - if (do_rx_dma(vcc,skb,1,size,eff)) return 1; - eni_vcc->rxing++; - return 0; -} - - -static inline int rx_vcc(struct atm_vcc *vcc) -{ - void __iomem *vci_dsc; - unsigned long tmp; - struct eni_vcc *eni_vcc; - - eni_vcc = ENI_VCC(vcc); - vci_dsc = ENI_DEV(vcc->dev)->vci+vcc->vci*16; - EVENT("rx_vcc(1)\n",0,0); - while (eni_vcc->descr != (tmp = (readl(vci_dsc+4) & MID_VCI_DESCR) >> - MID_VCI_DESCR_SHIFT)) { - EVENT("rx_vcc(2: host dsc=0x%lx, nic dsc=0x%lx)\n", - eni_vcc->descr,tmp); - DPRINTK("CB_DESCR %ld REG_DESCR %d\n",ENI_VCC(vcc)->descr, - (((unsigned) readl(vci_dsc+4) & MID_VCI_DESCR) >> - MID_VCI_DESCR_SHIFT)); - if (ENI_VCC(vcc)->rx(vcc)) return 1; - } - /* clear IN_SERVICE flag */ - writel(readl(vci_dsc) & ~MID_VCI_IN_SERVICE,vci_dsc); - /* - * If new data has arrived between evaluating the while condition and - * clearing IN_SERVICE, we wouldn't be notified until additional data - * follows. So we have to loop again to be sure. - */ - EVENT("rx_vcc(3)\n",0,0); - while (ENI_VCC(vcc)->descr != (tmp = (readl(vci_dsc+4) & MID_VCI_DESCR) - >> MID_VCI_DESCR_SHIFT)) { - EVENT("rx_vcc(4: host dsc=0x%lx, nic dsc=0x%lx)\n", - eni_vcc->descr,tmp); - DPRINTK("CB_DESCR %ld REG_DESCR %d\n",ENI_VCC(vcc)->descr, - (((unsigned) readl(vci_dsc+4) & MID_VCI_DESCR) >> - MID_VCI_DESCR_SHIFT)); - if (ENI_VCC(vcc)->rx(vcc)) return 1; - } - return 0; -} - - -static void poll_rx(struct atm_dev *dev) -{ - struct eni_dev *eni_dev; - struct atm_vcc *curr; - - eni_dev = ENI_DEV(dev); - while ((curr = eni_dev->fast)) { - EVENT("poll_rx.fast\n",0,0); - if (rx_vcc(curr)) return; - eni_dev->fast = ENI_VCC(curr)->next; - ENI_VCC(curr)->next = ENI_VCC_NOS; - barrier(); - ENI_VCC(curr)->servicing--; - } - while ((curr = eni_dev->slow)) { - EVENT("poll_rx.slow\n",0,0); - if (rx_vcc(curr)) return; - eni_dev->slow = ENI_VCC(curr)->next; - ENI_VCC(curr)->next = ENI_VCC_NOS; - barrier(); - ENI_VCC(curr)->servicing--; - } -} - - -static void get_service(struct atm_dev *dev) -{ - struct eni_dev *eni_dev; - struct atm_vcc *vcc; - unsigned long vci; - - DPRINTK(">get_service\n"); - eni_dev = ENI_DEV(dev); - while (eni_in(MID_SERV_WRITE) != eni_dev->serv_read) { - vci = readl(eni_dev->service+eni_dev->serv_read*4); - eni_dev->serv_read = (eni_dev->serv_read+1) & (NR_SERVICE-1); - vcc = eni_dev->rx_map[vci & 1023]; - if (!vcc) { - printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %ld not " - "found\n",dev->number,vci); - continue; /* nasty but we try to go on anyway */ - /* @@@ nope, doesn't work */ - } - EVENT("getting from service\n",0,0); - if (ENI_VCC(vcc)->next != ENI_VCC_NOS) { - EVENT("double service\n",0,0); - DPRINTK("Grr, servicing VCC %ld twice\n",vci); - continue; - } - ENI_VCC(vcc)->timestamp = ktime_get_real(); - ENI_VCC(vcc)->next = NULL; - if (vcc->qos.rxtp.traffic_class == ATM_CBR) { - if (eni_dev->fast) - ENI_VCC(eni_dev->last_fast)->next = vcc; - else eni_dev->fast = vcc; - eni_dev->last_fast = vcc; - } - else { - if (eni_dev->slow) - ENI_VCC(eni_dev->last_slow)->next = vcc; - else eni_dev->slow = vcc; - eni_dev->last_slow = vcc; - } - putting++; - ENI_VCC(vcc)->servicing++; - } -} - - -static void dequeue_rx(struct atm_dev *dev) -{ - struct eni_dev *eni_dev; - struct eni_vcc *eni_vcc; - struct atm_vcc *vcc; - struct sk_buff *skb; - void __iomem *vci_dsc; - int first; - - eni_dev = ENI_DEV(dev); - first = 1; - while (1) { - skb = skb_dequeue(&eni_dev->rx_queue); - if (!skb) { - if (first) { - DPRINTK(DEV_LABEL "(itf %d): RX but not " - "rxing\n",dev->number); - EVENT("nothing to dequeue\n",0,0); - } - break; - } - EVENT("dequeued (size=%ld,pos=0x%lx)\n",ENI_PRV_SIZE(skb), - ENI_PRV_POS(skb)); - rx_dequeued++; - vcc = ATM_SKB(skb)->vcc; - eni_vcc = ENI_VCC(vcc); - first = 0; - vci_dsc = eni_dev->vci+vcc->vci*16; - if (!EEPMOK(eni_vcc->rx_pos,ENI_PRV_SIZE(skb), - (readl(vci_dsc+4) & MID_VCI_READ) >> MID_VCI_READ_SHIFT, - eni_vcc->words)) { - EVENT("requeuing\n",0,0); - skb_queue_head(&eni_dev->rx_queue,skb); - break; - } - eni_vcc->rxing--; - eni_vcc->rx_pos = ENI_PRV_POS(skb) & (eni_vcc->words-1); - dma_unmap_single(&eni_dev->pci_dev->dev,ENI_PRV_PADDR(skb),skb->len, - DMA_TO_DEVICE); - if (!skb->len) dev_kfree_skb_irq(skb); - else { - EVENT("pushing (len=%ld)\n",skb->len,0); - if (vcc->qos.aal == ATM_AAL0) - *(unsigned long *) skb->data = - ntohl(*(unsigned long *) skb->data); - memset(skb->cb,0,sizeof(struct eni_skb_prv)); - vcc->push(vcc,skb); - pushed++; - } - atomic_inc(&vcc->stats->rx); - } - wake_up(&eni_dev->rx_wait); -} - - -static int open_rx_first(struct atm_vcc *vcc) -{ - struct eni_dev *eni_dev; - struct eni_vcc *eni_vcc; - unsigned long size; - - DPRINTK("open_rx_first\n"); - eni_dev = ENI_DEV(vcc->dev); - eni_vcc = ENI_VCC(vcc); - eni_vcc->rx = NULL; - if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0; - size = vcc->qos.rxtp.max_sdu*eni_dev->rx_mult/100; - if (size > MID_MAX_BUF_SIZE && vcc->qos.rxtp.max_sdu <= - MID_MAX_BUF_SIZE) - size = MID_MAX_BUF_SIZE; - eni_vcc->recv = eni_alloc_mem(eni_dev,&size); - DPRINTK("rx at 0x%lx\n",eni_vcc->recv); - eni_vcc->words = size >> 2; - if (!eni_vcc->recv) return -ENOBUFS; - eni_vcc->rx = vcc->qos.aal == ATM_AAL5 ? rx_aal5 : rx_aal0; - eni_vcc->descr = 0; - eni_vcc->rx_pos = 0; - eni_vcc->rxing = 0; - eni_vcc->servicing = 0; - eni_vcc->next = ENI_VCC_NOS; - return 0; -} - - -static int open_rx_second(struct atm_vcc *vcc) -{ - void __iomem *here; - struct eni_dev *eni_dev; - struct eni_vcc *eni_vcc; - unsigned long size; - int order; - - DPRINTK("open_rx_second\n"); - eni_dev = ENI_DEV(vcc->dev); - eni_vcc = ENI_VCC(vcc); - if (!eni_vcc->rx) return 0; - /* set up VCI descriptor */ - here = eni_dev->vci+vcc->vci*16; - DPRINTK("loc 0x%x\n",(unsigned) (eni_vcc->recv-eni_dev->ram)/4); - size = eni_vcc->words >> 8; - for (order = -1; size; order++) size >>= 1; - writel(0,here+4); /* descr, read = 0 */ - writel(0,here+8); /* write, state, count = 0 */ - if (eni_dev->rx_map[vcc->vci]) - printk(KERN_CRIT DEV_LABEL "(itf %d): BUG - VCI %d already " - "in use\n",vcc->dev->number,vcc->vci); - eni_dev->rx_map[vcc->vci] = vcc; /* now it counts */ - writel(((vcc->qos.aal != ATM_AAL5 ? MID_MODE_RAW : MID_MODE_AAL5) << - MID_VCI_MODE_SHIFT) | MID_VCI_PTI_MODE | - (((eni_vcc->recv-eni_dev->ram) >> (MID_LOC_SKIP+2)) << - MID_VCI_LOCATION_SHIFT) | (order << MID_VCI_SIZE_SHIFT),here); - return 0; -} - - -static void close_rx(struct atm_vcc *vcc) -{ - DECLARE_WAITQUEUE(wait,current); - void __iomem *here; - struct eni_dev *eni_dev; - struct eni_vcc *eni_vcc; - - eni_vcc = ENI_VCC(vcc); - if (!eni_vcc->rx) return; - eni_dev = ENI_DEV(vcc->dev); - if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) { - here = eni_dev->vci+vcc->vci*16; - /* block receiver */ - writel((readl(here) & ~MID_VCI_MODE) | (MID_MODE_TRASH << - MID_VCI_MODE_SHIFT),here); - /* wait for receiver to become idle */ - udelay(27); - /* discard pending cell */ - writel(readl(here) & ~MID_VCI_IN_SERVICE,here); - /* don't accept any new ones */ - eni_dev->rx_map[vcc->vci] = NULL; - /* wait for RX queue to drain */ - DPRINTK("eni_close: waiting for RX ...\n"); - EVENT("RX closing\n",0,0); - add_wait_queue(&eni_dev->rx_wait,&wait); - set_current_state(TASK_UNINTERRUPTIBLE); - barrier(); - for (;;) { - /* transition service->rx: rxing++, servicing-- */ - if (!eni_vcc->servicing) { - barrier(); - if (!eni_vcc->rxing) break; - } - EVENT("drain PDUs (rx %ld, serv %ld)\n",eni_vcc->rxing, - eni_vcc->servicing); - printk(KERN_INFO "%d+%d RX left\n",eni_vcc->servicing, - eni_vcc->rxing); - schedule(); - set_current_state(TASK_UNINTERRUPTIBLE); - } - for (;;) { - int at_end; - u32 tmp; - - tasklet_disable(&eni_dev->task); - tmp = readl(eni_dev->vci+vcc->vci*16+4) & MID_VCI_READ; - at_end = eni_vcc->rx_pos == tmp >> MID_VCI_READ_SHIFT; - tasklet_enable(&eni_dev->task); - if (at_end) break; - EVENT("drain discard (host 0x%lx, nic 0x%lx)\n", - eni_vcc->rx_pos,tmp); - printk(KERN_INFO "draining RX: host 0x%lx, nic 0x%x\n", - eni_vcc->rx_pos,tmp); - schedule(); - set_current_state(TASK_UNINTERRUPTIBLE); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&eni_dev->rx_wait,&wait); - } - eni_free_mem(eni_dev,eni_vcc->recv,eni_vcc->words << 2); - eni_vcc->rx = NULL; -} - - -static int start_rx(struct atm_dev *dev) -{ - struct eni_dev *eni_dev; - - eni_dev = ENI_DEV(dev); - eni_dev->rx_map = (struct atm_vcc **) get_zeroed_page(GFP_KERNEL); - if (!eni_dev->rx_map) { - printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n", - dev->number); - free_page((unsigned long) eni_dev->free_list); - return -ENOMEM; - } - eni_dev->rx_mult = DEFAULT_RX_MULT; - eni_dev->fast = eni_dev->last_fast = NULL; - eni_dev->slow = eni_dev->last_slow = NULL; - init_waitqueue_head(&eni_dev->rx_wait); - skb_queue_head_init(&eni_dev->rx_queue); - eni_dev->serv_read = eni_in(MID_SERV_WRITE); - eni_out(0,MID_DMA_WR_RX); - return 0; -} - - -/*----------------------------------- TX ------------------------------------*/ - - -enum enq_res { enq_ok,enq_next,enq_jam }; - - -static inline void put_dma(int chan,u32 *dma,int *j,dma_addr_t paddr, - u32 size) -{ - u32 init,words; - - DPRINTK("put_dma: 0x%lx+0x%x\n",(unsigned long) paddr,size); - EVENT("put_dma: 0x%lx+0x%lx\n",(unsigned long) paddr,size); -#if 0 /* don't complain anymore */ - if (paddr & 3) - printk(KERN_ERR "put_dma: unaligned addr (0x%lx)\n",paddr); - if (size & 3) - printk(KERN_ERR "put_dma: unaligned size (0x%lx)\n",size); -#endif - if (paddr & 3) { - init = 4-(paddr & 3); - if (init > size || size < 7) init = size; - DPRINTK("put_dma: %lx DMA: %d/%d bytes\n", - (unsigned long) paddr,init,size); - dma[(*j)++] = MID_DT_BYTE | (init << MID_DMA_COUNT_SHIFT) | - (chan << MID_DMA_CHAN_SHIFT); - dma[(*j)++] = paddr; - paddr += init; - size -= init; - } - words = size >> 2; - size &= 3; - if (words && (paddr & 31)) { - init = 8-((paddr & 31) >> 2); - if (init > words) init = words; - DPRINTK("put_dma: %lx DMA: %d/%d words\n", - (unsigned long) paddr,init,words); - dma[(*j)++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) | - (chan << MID_DMA_CHAN_SHIFT); - dma[(*j)++] = paddr; - paddr += init << 2; - words -= init; - } -#ifdef CONFIG_ATM_ENI_BURST_TX_16W /* may work with some PCI chipsets ... */ - if (words & ~15) { - DPRINTK("put_dma: %lx DMA: %d*16/%d words\n", - (unsigned long) paddr,words >> 4,words); - dma[(*j)++] = MID_DT_16W | ((words >> 4) << MID_DMA_COUNT_SHIFT) - | (chan << MID_DMA_CHAN_SHIFT); - dma[(*j)++] = paddr; - paddr += (words & ~15) << 2; - words &= 15; - } -#endif -#ifdef CONFIG_ATM_ENI_BURST_TX_8W /* recommended */ - if (words & ~7) { - DPRINTK("put_dma: %lx DMA: %d*8/%d words\n", - (unsigned long) paddr,words >> 3,words); - dma[(*j)++] = MID_DT_8W | ((words >> 3) << MID_DMA_COUNT_SHIFT) - | (chan << MID_DMA_CHAN_SHIFT); - dma[(*j)++] = paddr; - paddr += (words & ~7) << 2; - words &= 7; - } -#endif -#ifdef CONFIG_ATM_ENI_BURST_TX_4W /* probably useless if TX_8W or TX_16W */ - if (words & ~3) { - DPRINTK("put_dma: %lx DMA: %d*4/%d words\n", - (unsigned long) paddr,words >> 2,words); - dma[(*j)++] = MID_DT_4W | ((words >> 2) << MID_DMA_COUNT_SHIFT) - | (chan << MID_DMA_CHAN_SHIFT); - dma[(*j)++] = paddr; - paddr += (words & ~3) << 2; - words &= 3; - } -#endif -#ifdef CONFIG_ATM_ENI_BURST_TX_2W /* probably useless if TX_4W, TX_8W, ... */ - if (words & ~1) { - DPRINTK("put_dma: %lx DMA: %d*2/%d words\n", - (unsigned long) paddr,words >> 1,words); - dma[(*j)++] = MID_DT_2W | ((words >> 1) << MID_DMA_COUNT_SHIFT) - | (chan << MID_DMA_CHAN_SHIFT); - dma[(*j)++] = paddr; - paddr += (words & ~1) << 2; - words &= 1; - } -#endif - if (words) { - DPRINTK("put_dma: %lx DMA: %d words\n",(unsigned long) paddr, - words); - dma[(*j)++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) | - (chan << MID_DMA_CHAN_SHIFT); - dma[(*j)++] = paddr; - paddr += words << 2; - } - if (size) { - DPRINTK("put_dma: %lx DMA: %d bytes\n",(unsigned long) paddr, - size); - dma[(*j)++] = MID_DT_BYTE | (size << MID_DMA_COUNT_SHIFT) | - (chan << MID_DMA_CHAN_SHIFT); - dma[(*j)++] = paddr; - } -} - - -static enum enq_res do_tx(struct sk_buff *skb) -{ - struct atm_vcc *vcc; - struct eni_dev *eni_dev; - struct eni_vcc *eni_vcc; - struct eni_tx *tx; - dma_addr_t paddr; - u32 dma_rd,dma_wr; - u32 size; /* in words */ - int aal5,dma_size,i,j; - unsigned char skb_data3; - - DPRINTK(">do_tx\n"); - NULLCHECK(skb); - EVENT("do_tx: skb=0x%lx, %ld bytes\n",(unsigned long) skb,skb->len); - vcc = ATM_SKB(skb)->vcc; - NULLCHECK(vcc); - eni_dev = ENI_DEV(vcc->dev); - NULLCHECK(eni_dev); - eni_vcc = ENI_VCC(vcc); - tx = eni_vcc->tx; - NULLCHECK(tx); -#if 0 /* Enable this for testing with the "align" program */ - { - unsigned int hack = *((char *) skb->data)-'0'; - - if (hack < 8) { - skb->data += hack; - skb->len -= hack; - } - } -#endif -#if 0 /* should work now */ - if ((unsigned long) skb->data & 3) - printk(KERN_ERR DEV_LABEL "(itf %d): VCI %d has mis-aligned " - "TX data\n",vcc->dev->number,vcc->vci); -#endif - /* - * Potential future IP speedup: make hard_header big enough to put - * segmentation descriptor directly into PDU. Saves: 4 slave writes, - * 1 DMA xfer & 2 DMA'ed bytes (protocol layering is for wimps :-) - */ - - aal5 = vcc->qos.aal == ATM_AAL5; - /* check space in buffer */ - if (!aal5) - size = (ATM_CELL_PAYLOAD >> 2)+TX_DESCR_SIZE; - /* cell without HEC plus segmentation header (includes - four-byte cell header) */ - else { - size = skb->len+4*AAL5_TRAILER+ATM_CELL_PAYLOAD-1; - /* add AAL5 trailer */ - size = ((size-(size % ATM_CELL_PAYLOAD)) >> 2)+TX_DESCR_SIZE; - /* add segmentation header */ - } - /* - * Can I move tx_pos by size bytes without getting closer than TX_GAP - * to the read pointer ? TX_GAP means to leave some space for what - * the manual calls "too close". - */ - if (!NEPMOK(tx->tx_pos,size+TX_GAP, - eni_in(MID_TX_RDPTR(tx->index)),tx->words)) { - DPRINTK(DEV_LABEL "(itf %d): TX full (size %d)\n", - vcc->dev->number,size); - return enq_next; - } - /* check DMA */ - dma_wr = eni_in(MID_DMA_WR_TX); - dma_rd = eni_in(MID_DMA_RD_TX); - dma_size = 3; /* JK for descriptor and final fill, plus final size - mis-alignment fix */ -DPRINTK("iovcnt = %d\n",skb_shinfo(skb)->nr_frags); - if (!skb_shinfo(skb)->nr_frags) dma_size += 5; - else dma_size += 5*(skb_shinfo(skb)->nr_frags+1); - if (dma_size > TX_DMA_BUF) { - printk(KERN_CRIT DEV_LABEL "(itf %d): needs %d DMA entries " - "(got only %d)\n",vcc->dev->number,dma_size,TX_DMA_BUF); - } - DPRINTK("dma_wr is %d, tx_pos is %ld\n",dma_wr,tx->tx_pos); - if (dma_wr != dma_rd && ((dma_rd+NR_DMA_TX-dma_wr) & (NR_DMA_TX-1)) < - dma_size) { - printk(KERN_WARNING DEV_LABEL "(itf %d): TX DMA full\n", - vcc->dev->number); - return enq_jam; - } - skb_data3 = skb->data[3]; - paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len, - DMA_TO_DEVICE); - if (dma_mapping_error(&eni_dev->pci_dev->dev, paddr)) - return enq_next; - ENI_PRV_PADDR(skb) = paddr; - /* prepare DMA queue entries */ - j = 0; - eni_dev->dma[j++] = (((tx->tx_pos+TX_DESCR_SIZE) & (tx->words-1)) << - MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) | - MID_DT_JK; - j++; - if (!skb_shinfo(skb)->nr_frags) - if (aal5) put_dma(tx->index,eni_dev->dma,&j,paddr,skb->len); - else put_dma(tx->index,eni_dev->dma,&j,paddr+4,skb->len-4); - else { -DPRINTK("doing direct send\n"); /* @@@ well, this doesn't work anyway */ - for (i = -1; i < skb_shinfo(skb)->nr_frags; i++) - if (i == -1) - put_dma(tx->index,eni_dev->dma,&j,(unsigned long) - skb->data, - skb_headlen(skb)); - else - put_dma(tx->index,eni_dev->dma,&j,(unsigned long) - skb_frag_page(&skb_shinfo(skb)->frags[i]) + - skb_frag_off(&skb_shinfo(skb)->frags[i]), - skb_frag_size(&skb_shinfo(skb)->frags[i])); - } - if (skb->len & 3) { - put_dma(tx->index, eni_dev->dma, &j, eni_dev->zero.dma, - 4 - (skb->len & 3)); - } - /* JK for AAL5 trailer - AAL0 doesn't need it, but who cares ... */ - eni_dev->dma[j++] = (((tx->tx_pos+size) & (tx->words-1)) << - MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) | - MID_DMA_END | MID_DT_JK; - j++; - DPRINTK("DMA at end: %d\n",j); - /* store frame */ - writel((MID_SEG_TX_ID << MID_SEG_ID_SHIFT) | - (aal5 ? MID_SEG_AAL5 : 0) | (tx->prescaler << MID_SEG_PR_SHIFT) | - (tx->resolution << MID_SEG_RATE_SHIFT) | - (size/(ATM_CELL_PAYLOAD/4)),tx->send+tx->tx_pos*4); -/*printk("dsc = 0x%08lx\n",(unsigned long) readl(tx->send+tx->tx_pos*4));*/ - writel((vcc->vci << MID_SEG_VCI_SHIFT) | - (aal5 ? 0 : (skb_data3 & 0xf)) | - (ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ? MID_SEG_CLP : 0), - tx->send+((tx->tx_pos+1) & (tx->words-1))*4); - DPRINTK("size: %d, len:%d\n",size,skb->len); - if (aal5) - writel(skb->len,tx->send+ - ((tx->tx_pos+size-AAL5_TRAILER) & (tx->words-1))*4); - j = j >> 1; - for (i = 0; i < j; i++) { - writel(eni_dev->dma[i*2],eni_dev->tx_dma+dma_wr*8); - writel(eni_dev->dma[i*2+1],eni_dev->tx_dma+dma_wr*8+4); - dma_wr = (dma_wr+1) & (NR_DMA_TX-1); - } - ENI_PRV_POS(skb) = tx->tx_pos; - ENI_PRV_SIZE(skb) = size; - ENI_VCC(vcc)->txing += size; - tx->tx_pos = (tx->tx_pos+size) & (tx->words-1); - DPRINTK("dma_wr set to %d, tx_pos is now %ld\n",dma_wr,tx->tx_pos); - eni_out(dma_wr,MID_DMA_WR_TX); - skb_queue_tail(&eni_dev->tx_queue,skb); - queued++; - return enq_ok; -} - - -static void poll_tx(struct atm_dev *dev) -{ - struct eni_tx *tx; - struct sk_buff *skb; - enum enq_res res; - int i; - - DPRINTK(">poll_tx\n"); - for (i = NR_CHAN-1; i >= 0; i--) { - tx = &ENI_DEV(dev)->tx[i]; - if (tx->send) - while ((skb = skb_dequeue(&tx->backlog))) { - res = do_tx(skb); - if (res == enq_ok) continue; - DPRINTK("re-queuing TX PDU\n"); - skb_queue_head(&tx->backlog,skb); - requeued++; - if (res == enq_jam) return; - break; - } - } -} - - -static void dequeue_tx(struct atm_dev *dev) -{ - struct eni_dev *eni_dev; - struct atm_vcc *vcc; - struct sk_buff *skb; - struct eni_tx *tx; - - NULLCHECK(dev); - eni_dev = ENI_DEV(dev); - NULLCHECK(eni_dev); - while ((skb = skb_dequeue(&eni_dev->tx_queue))) { - vcc = ATM_SKB(skb)->vcc; - NULLCHECK(vcc); - tx = ENI_VCC(vcc)->tx; - NULLCHECK(ENI_VCC(vcc)->tx); - DPRINTK("dequeue_tx: next 0x%lx curr 0x%x\n",ENI_PRV_POS(skb), - (unsigned) eni_in(MID_TX_DESCRSTART(tx->index))); - if (ENI_VCC(vcc)->txing < tx->words && ENI_PRV_POS(skb) == - eni_in(MID_TX_DESCRSTART(tx->index))) { - skb_queue_head(&eni_dev->tx_queue,skb); - break; - } - ENI_VCC(vcc)->txing -= ENI_PRV_SIZE(skb); - dma_unmap_single(&eni_dev->pci_dev->dev,ENI_PRV_PADDR(skb),skb->len, - DMA_TO_DEVICE); - if (vcc->pop) vcc->pop(vcc,skb); - else dev_kfree_skb_irq(skb); - atomic_inc(&vcc->stats->tx); - wake_up(&eni_dev->tx_wait); - dma_complete++; - } -} - - -static struct eni_tx *alloc_tx(struct eni_dev *eni_dev,int ubr) -{ - int i; - - for (i = !ubr; i < NR_CHAN; i++) - if (!eni_dev->tx[i].send) return eni_dev->tx+i; - return NULL; -} - - -static int comp_tx(struct eni_dev *eni_dev,int *pcr,int reserved,int *pre, - int *res,int unlimited) -{ - static const int pre_div[] = { 4,16,128,2048 }; - /* 2^(((x+2)^2-(x+2))/2+1) */ - - if (unlimited) *pre = *res = 0; - else { - if (*pcr > 0) { - int div; - - for (*pre = 0; *pre < 3; (*pre)++) - if (TS_CLOCK/pre_div[*pre]/64 <= *pcr) break; - div = pre_div[*pre]**pcr; - DPRINTK("min div %d\n",div); - *res = TS_CLOCK/div-1; - } - else { - int div; - - if (!*pcr) *pcr = eni_dev->tx_bw+reserved; - for (*pre = 3; *pre >= 0; (*pre)--) - if (TS_CLOCK/pre_div[*pre]/64 > -*pcr) break; - if (*pre < 3) (*pre)++; /* else fail later */ - div = pre_div[*pre]*-*pcr; - DPRINTK("max div %d\n",div); - *res = DIV_ROUND_UP(TS_CLOCK, div)-1; - } - if (*res < 0) *res = 0; - if (*res > MID_SEG_MAX_RATE) *res = MID_SEG_MAX_RATE; - } - *pcr = TS_CLOCK/pre_div[*pre]/(*res+1); - DPRINTK("out pcr: %d (%d:%d)\n",*pcr,*pre,*res); - return 0; -} - - -static int reserve_or_set_tx(struct atm_vcc *vcc,struct atm_trafprm *txtp, - int set_rsv,int set_shp) -{ - struct eni_dev *eni_dev = ENI_DEV(vcc->dev); - struct eni_vcc *eni_vcc = ENI_VCC(vcc); - struct eni_tx *tx; - unsigned long size; - void __iomem *mem; - int rate,ubr,unlimited,new_tx; - int pre,res,order; - int error; - - rate = atm_pcr_goal(txtp); - ubr = txtp->traffic_class == ATM_UBR; - unlimited = ubr && (!rate || rate <= -ATM_OC3_PCR || - rate >= ATM_OC3_PCR); - if (!unlimited) { - size = txtp->max_sdu*eni_dev->tx_mult/100; - if (size > MID_MAX_BUF_SIZE && txtp->max_sdu <= - MID_MAX_BUF_SIZE) - size = MID_MAX_BUF_SIZE; - } - else { - if (eni_dev->ubr) { - eni_vcc->tx = eni_dev->ubr; - txtp->pcr = ATM_OC3_PCR; - return 0; - } - size = UBR_BUFFER; - } - new_tx = !eni_vcc->tx; - mem = NULL; /* for gcc */ - if (!new_tx) tx = eni_vcc->tx; - else { - mem = eni_alloc_mem(eni_dev,&size); - if (!mem) return -ENOBUFS; - tx = alloc_tx(eni_dev,unlimited); - if (!tx) { - eni_free_mem(eni_dev,mem,size); - return -EBUSY; - } - DPRINTK("got chan %d\n",tx->index); - tx->reserved = tx->shaping = 0; - tx->send = mem; - tx->words = size >> 2; - skb_queue_head_init(&tx->backlog); - for (order = 0; size > (1 << (order+10)); order++); - eni_out((order << MID_SIZE_SHIFT) | - ((tx->send-eni_dev->ram) >> (MID_LOC_SKIP+2)), - MID_TX_PLACE(tx->index)); - tx->tx_pos = eni_in(MID_TX_DESCRSTART(tx->index)) & - MID_DESCR_START; - } - error = comp_tx(eni_dev,&rate,tx->reserved,&pre,&res,unlimited); - if (!error && txtp->min_pcr > rate) error = -EINVAL; - if (!error && txtp->max_pcr && txtp->max_pcr != ATM_MAX_PCR && - txtp->max_pcr < rate) error = -EINVAL; - if (!error && !ubr && rate > eni_dev->tx_bw+tx->reserved) - error = -EINVAL; - if (!error && set_rsv && !set_shp && rate < tx->shaping) - error = -EINVAL; - if (!error && !set_rsv && rate > tx->reserved && !ubr) - error = -EINVAL; - if (error) { - if (new_tx) { - tx->send = NULL; - eni_free_mem(eni_dev,mem,size); - } - return error; - } - txtp->pcr = rate; - if (set_rsv && !ubr) { - eni_dev->tx_bw += tx->reserved; - tx->reserved = rate; - eni_dev->tx_bw -= rate; - } - if (set_shp || (unlimited && new_tx)) { - if (unlimited && new_tx) eni_dev->ubr = tx; - tx->prescaler = pre; - tx->resolution = res; - tx->shaping = rate; - } - if (set_shp) eni_vcc->tx = tx; - DPRINTK("rsv %d shp %d\n",tx->reserved,tx->shaping); - return 0; -} - - -static int open_tx_first(struct atm_vcc *vcc) -{ - ENI_VCC(vcc)->tx = NULL; - if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0; - ENI_VCC(vcc)->txing = 0; - return reserve_or_set_tx(vcc,&vcc->qos.txtp,1,1); -} - - -static int open_tx_second(struct atm_vcc *vcc) -{ - return 0; /* nothing to do */ -} - - -static void close_tx(struct atm_vcc *vcc) -{ - DECLARE_WAITQUEUE(wait,current); - struct eni_dev *eni_dev; - struct eni_vcc *eni_vcc; - - eni_vcc = ENI_VCC(vcc); - if (!eni_vcc->tx) return; - eni_dev = ENI_DEV(vcc->dev); - /* wait for TX queue to drain */ - DPRINTK("eni_close: waiting for TX ...\n"); - add_wait_queue(&eni_dev->tx_wait,&wait); - set_current_state(TASK_UNINTERRUPTIBLE); - for (;;) { - int txing; - - tasklet_disable(&eni_dev->task); - txing = skb_peek(&eni_vcc->tx->backlog) || eni_vcc->txing; - tasklet_enable(&eni_dev->task); - if (!txing) break; - DPRINTK("%d TX left\n",eni_vcc->txing); - schedule(); - set_current_state(TASK_UNINTERRUPTIBLE); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&eni_dev->tx_wait,&wait); - if (eni_vcc->tx != eni_dev->ubr) { - /* - * Looping a few times in here is probably far cheaper than - * keeping track of TX completions all the time, so let's poll - * a bit ... - */ - while (eni_in(MID_TX_RDPTR(eni_vcc->tx->index)) != - eni_in(MID_TX_DESCRSTART(eni_vcc->tx->index))) - schedule(); - eni_free_mem(eni_dev,eni_vcc->tx->send,eni_vcc->tx->words << 2); - eni_vcc->tx->send = NULL; - eni_dev->tx_bw += eni_vcc->tx->reserved; - } - eni_vcc->tx = NULL; -} - - -static int start_tx(struct atm_dev *dev) -{ - struct eni_dev *eni_dev; - int i; - - eni_dev = ENI_DEV(dev); - eni_dev->lost = 0; - eni_dev->tx_bw = ATM_OC3_PCR; - eni_dev->tx_mult = DEFAULT_TX_MULT; - init_waitqueue_head(&eni_dev->tx_wait); - eni_dev->ubr = NULL; - skb_queue_head_init(&eni_dev->tx_queue); - eni_out(0,MID_DMA_WR_TX); - for (i = 0; i < NR_CHAN; i++) { - eni_dev->tx[i].send = NULL; - eni_dev->tx[i].index = i; - } - return 0; -} - - -/*--------------------------------- common ----------------------------------*/ - - -#if 0 /* may become useful again when tuning things */ - -static void foo(void) -{ -printk(KERN_INFO - "tx_complete=%d,dma_complete=%d,queued=%d,requeued=%d,sub=%d,\n" - "backlogged=%d,rx_enqueued=%d,rx_dequeued=%d,putting=%d,pushed=%d\n", - tx_complete,dma_complete,queued,requeued,submitted,backlogged, - rx_enqueued,rx_dequeued,putting,pushed); -if (eni_boards) printk(KERN_INFO "loss: %ld\n",ENI_DEV(eni_boards)->lost); -} - -#endif - - -static void bug_int(struct atm_dev *dev,unsigned long reason) -{ - DPRINTK(">bug_int\n"); - if (reason & MID_DMA_ERR_ACK) - printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA " - "error\n",dev->number); - if (reason & MID_TX_IDENT_MISM) - printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - ident " - "mismatch\n",dev->number); - if (reason & MID_TX_DMA_OVFL) - printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA " - "overflow\n",dev->number); - EVENT("---dump ends here---\n",0,0); - printk(KERN_NOTICE "---recent events---\n"); - event_dump(); -} - - -static irqreturn_t eni_int(int irq,void *dev_id) -{ - struct atm_dev *dev; - struct eni_dev *eni_dev; - u32 reason; - - DPRINTK(">eni_int\n"); - dev = dev_id; - eni_dev = ENI_DEV(dev); - reason = eni_in(MID_ISA); - DPRINTK(DEV_LABEL ": int 0x%lx\n",(unsigned long) reason); - /* - * Must handle these two right now, because reading ISA doesn't clear - * them, so they re-occur and we never make it to the tasklet. Since - * they're rare, we don't mind the occasional invocation of eni_tasklet - * with eni_dev->events == 0. - */ - if (reason & MID_STAT_OVFL) { - EVENT("stat overflow\n",0,0); - eni_dev->lost += eni_in(MID_STAT) & MID_OVFL_TRASH; - } - if (reason & MID_SUNI_INT) { - EVENT("SUNI int\n",0,0); - dev->phy->interrupt(dev); -#if 0 - foo(); -#endif - } - spin_lock(&eni_dev->lock); - eni_dev->events |= reason; - spin_unlock(&eni_dev->lock); - tasklet_schedule(&eni_dev->task); - return IRQ_HANDLED; -} - - -static void eni_tasklet(unsigned long data) -{ - struct atm_dev *dev = (struct atm_dev *) data; - struct eni_dev *eni_dev = ENI_DEV(dev); - unsigned long flags; - u32 events; - - DPRINTK("eni_tasklet (dev %p)\n",dev); - spin_lock_irqsave(&eni_dev->lock,flags); - events = xchg(&eni_dev->events,0); - spin_unlock_irqrestore(&eni_dev->lock,flags); - if (events & MID_RX_DMA_COMPLETE) { - EVENT("INT: RX DMA complete, starting dequeue_rx\n",0,0); - dequeue_rx(dev); - EVENT("dequeue_rx done, starting poll_rx\n",0,0); - poll_rx(dev); - EVENT("poll_rx done\n",0,0); - /* poll_tx ? */ - } - if (events & MID_SERVICE) { - EVENT("INT: service, starting get_service\n",0,0); - get_service(dev); - EVENT("get_service done, starting poll_rx\n",0,0); - poll_rx(dev); - EVENT("poll_rx done\n",0,0); - } - if (events & MID_TX_DMA_COMPLETE) { - EVENT("INT: TX DMA COMPLETE\n",0,0); - dequeue_tx(dev); - } - if (events & MID_TX_COMPLETE) { - EVENT("INT: TX COMPLETE\n",0,0); - tx_complete++; - wake_up(&eni_dev->tx_wait); - /* poll_rx ? */ - } - if (events & (MID_DMA_ERR_ACK | MID_TX_IDENT_MISM | MID_TX_DMA_OVFL)) { - EVENT("bug interrupt\n",0,0); - bug_int(dev,events); - } - poll_tx(dev); -} - - -/*--------------------------------- entries ---------------------------------*/ - - -static char * const media_name[] = { - "MMF", "SMF", "MMF", "03?", /* 0- 3 */ - "UTP", "05?", "06?", "07?", /* 4- 7 */ - "TAXI","09?", "10?", "11?", /* 8-11 */ - "12?", "13?", "14?", "15?", /* 12-15 */ - "MMF", "SMF", "18?", "19?", /* 16-19 */ - "UTP", "21?", "22?", "23?", /* 20-23 */ - "24?", "25?", "26?", "27?", /* 24-27 */ - "28?", "29?", "30?", "31?" /* 28-31 */ -}; - - -#define SET_SEPROM \ - ({ if (!error && !pci_error) { \ - pci_error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,tonga); \ - udelay(10); /* 10 usecs */ \ - } }) -#define GET_SEPROM \ - ({ if (!error && !pci_error) { \ - pci_error = pci_read_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,&tonga); \ - udelay(10); /* 10 usecs */ \ - } }) - - -static int get_esi_asic(struct atm_dev *dev) -{ - struct eni_dev *eni_dev; - unsigned char tonga; - int error,failed,pci_error; - int address,i,j; - - eni_dev = ENI_DEV(dev); - error = pci_error = 0; - tonga = SEPROM_MAGIC | SEPROM_DATA | SEPROM_CLK; - SET_SEPROM; - for (i = 0; i < ESI_LEN && !error && !pci_error; i++) { - /* start operation */ - tonga |= SEPROM_DATA; - SET_SEPROM; - tonga |= SEPROM_CLK; - SET_SEPROM; - tonga &= ~SEPROM_DATA; - SET_SEPROM; - tonga &= ~SEPROM_CLK; - SET_SEPROM; - /* send address */ - address = ((i+SEPROM_ESI_BASE) << 1)+1; - for (j = 7; j >= 0; j--) { - tonga = (address >> j) & 1 ? tonga | SEPROM_DATA : - tonga & ~SEPROM_DATA; - SET_SEPROM; - tonga |= SEPROM_CLK; - SET_SEPROM; - tonga &= ~SEPROM_CLK; - SET_SEPROM; - } - /* get ack */ - tonga |= SEPROM_DATA; - SET_SEPROM; - tonga |= SEPROM_CLK; - SET_SEPROM; - GET_SEPROM; - failed = tonga & SEPROM_DATA; - tonga &= ~SEPROM_CLK; - SET_SEPROM; - tonga |= SEPROM_DATA; - SET_SEPROM; - if (failed) error = -EIO; - else { - dev->esi[i] = 0; - for (j = 7; j >= 0; j--) { - dev->esi[i] <<= 1; - tonga |= SEPROM_DATA; - SET_SEPROM; - tonga |= SEPROM_CLK; - SET_SEPROM; - GET_SEPROM; - if (tonga & SEPROM_DATA) dev->esi[i] |= 1; - tonga &= ~SEPROM_CLK; - SET_SEPROM; - tonga |= SEPROM_DATA; - SET_SEPROM; - } - /* get ack */ - tonga |= SEPROM_DATA; - SET_SEPROM; - tonga |= SEPROM_CLK; - SET_SEPROM; - GET_SEPROM; - if (!(tonga & SEPROM_DATA)) error = -EIO; - tonga &= ~SEPROM_CLK; - SET_SEPROM; - tonga |= SEPROM_DATA; - SET_SEPROM; - } - /* stop operation */ - tonga &= ~SEPROM_DATA; - SET_SEPROM; - tonga |= SEPROM_CLK; - SET_SEPROM; - tonga |= SEPROM_DATA; - SET_SEPROM; - } - if (pci_error) { - printk(KERN_ERR DEV_LABEL "(itf %d): error reading ESI " - "(0x%02x)\n",dev->number,pci_error); - error = -EIO; - } - return error; -} - - -#undef SET_SEPROM -#undef GET_SEPROM - - -static int get_esi_fpga(struct atm_dev *dev, void __iomem *base) -{ - void __iomem *mac_base; - int i; - - mac_base = base+EPROM_SIZE-sizeof(struct midway_eprom); - for (i = 0; i < ESI_LEN; i++) dev->esi[i] = readb(mac_base+(i^3)); - return 0; -} - - -static int eni_do_init(struct atm_dev *dev) -{ - struct midway_eprom __iomem *eprom; - struct eni_dev *eni_dev; - struct pci_dev *pci_dev; - unsigned long real_base; - void __iomem *base; - int error,i,last; - - DPRINTK(">eni_init\n"); - dev->ci_range.vpi_bits = 0; - dev->ci_range.vci_bits = NR_VCI_LD; - dev->link_rate = ATM_OC3_PCR; - eni_dev = ENI_DEV(dev); - pci_dev = eni_dev->pci_dev; - real_base = pci_resource_start(pci_dev, 0); - eni_dev->irq = pci_dev->irq; - if ((error = pci_write_config_word(pci_dev,PCI_COMMAND, - PCI_COMMAND_MEMORY | - (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) { - printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory " - "(0x%02x)\n",dev->number,error); - return -EIO; - } - printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%lx,irq=%d,", - dev->number,pci_dev->revision,real_base,eni_dev->irq); - if (!(base = ioremap(real_base,MAP_MAX_SIZE))) { - printk("\n"); - printk(KERN_ERR DEV_LABEL "(itf %d): can't set up page " - "mapping\n",dev->number); - return -ENOMEM; - } - eni_dev->ioaddr = base; - eni_dev->base_diff = real_base - (unsigned long) base; - /* id may not be present in ASIC Tonga boards - check this @@@ */ - if (!eni_dev->asic) { - eprom = (base+EPROM_SIZE-sizeof(struct midway_eprom)); - if (readl(&eprom->magic) != ENI155_MAGIC) { - printk("\n"); - printk(KERN_ERR DEV_LABEL - "(itf %d): bad magic - expected 0x%x, got 0x%x\n", - dev->number, ENI155_MAGIC, - (unsigned)readl(&eprom->magic)); - error = -EINVAL; - goto unmap; - } - } - eni_dev->phy = base+PHY_BASE; - eni_dev->reg = base+REG_BASE; - eni_dev->ram = base+RAM_BASE; - last = MAP_MAX_SIZE-RAM_BASE; - for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) { - writel(0x55555555,eni_dev->ram+i); - if (readl(eni_dev->ram+i) != 0x55555555) last = i; - else { - writel(0xAAAAAAAA,eni_dev->ram+i); - if (readl(eni_dev->ram+i) != 0xAAAAAAAA) last = i; - else writel(i,eni_dev->ram+i); - } - } - for (i = 0; i < last; i += RAM_INCREMENT) - if (readl(eni_dev->ram+i) != i) break; - eni_dev->mem = i; - memset_io(eni_dev->ram,0,eni_dev->mem); - /* TODO: should shrink allocation now */ - printk("mem=%dkB (",eni_dev->mem >> 10); - /* TODO: check for non-SUNI, check for TAXI ? */ - if (!(eni_in(MID_RES_ID_MCON) & 0x200) != !eni_dev->asic) { - printk(")\n"); - printk(KERN_ERR DEV_LABEL "(itf %d): ERROR - wrong id 0x%x\n", - dev->number,(unsigned) eni_in(MID_RES_ID_MCON)); - error = -EINVAL; - goto unmap; - } - error = eni_dev->asic ? get_esi_asic(dev) : get_esi_fpga(dev,base); - if (error) - goto unmap; - for (i = 0; i < ESI_LEN; i++) - printk("%s%02X",i ? "-" : "",dev->esi[i]); - printk(")\n"); - printk(KERN_NOTICE DEV_LABEL "(itf %d): %s,%s\n",dev->number, - eni_in(MID_RES_ID_MCON) & 0x200 ? "ASIC" : "FPGA", - media_name[eni_in(MID_RES_ID_MCON) & DAUGHTER_ID]); - - error = suni_init(dev); - if (error) - goto unmap; -out: - return error; -unmap: - iounmap(base); - goto out; -} - -static void eni_do_release(struct atm_dev *dev) -{ - struct eni_dev *ed = ENI_DEV(dev); - - dev->phy->stop(dev); - dev->phy = NULL; - iounmap(ed->ioaddr); -} - -static int eni_start(struct atm_dev *dev) -{ - struct eni_dev *eni_dev; - - void __iomem *buf; - unsigned long buffer_mem; - int error; - - DPRINTK(">eni_start\n"); - eni_dev = ENI_DEV(dev); - if (request_irq(eni_dev->irq,&eni_int,IRQF_SHARED,DEV_LABEL,dev)) { - printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n", - dev->number,eni_dev->irq); - error = -EAGAIN; - goto out; - } - pci_set_master(eni_dev->pci_dev); - if ((error = pci_write_config_word(eni_dev->pci_dev,PCI_COMMAND, - PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | - (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) { - printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory+" - "master (0x%02x)\n",dev->number,error); - goto free_irq; - } - if ((error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL, - END_SWAP_DMA))) { - printk(KERN_ERR DEV_LABEL "(itf %d): can't set endian swap " - "(0x%02x)\n",dev->number,error); - goto free_irq; - } - /* determine addresses of internal tables */ - eni_dev->vci = eni_dev->ram; - eni_dev->rx_dma = eni_dev->ram+NR_VCI*16; - eni_dev->tx_dma = eni_dev->rx_dma+NR_DMA_RX*8; - eni_dev->service = eni_dev->tx_dma+NR_DMA_TX*8; - buf = eni_dev->service+NR_SERVICE*4; - DPRINTK("vci 0x%lx,rx 0x%lx, tx 0x%lx,srv 0x%lx,buf 0x%lx\n", - eni_dev->vci,eni_dev->rx_dma,eni_dev->tx_dma, - eni_dev->service,buf); - spin_lock_init(&eni_dev->lock); - tasklet_init(&eni_dev->task,eni_tasklet,(unsigned long) dev); - eni_dev->events = 0; - /* initialize memory management */ - buffer_mem = eni_dev->mem - (buf - eni_dev->ram); - eni_dev->free_list_size = buffer_mem/MID_MIN_BUF_SIZE/2; - eni_dev->free_list = kmalloc_objs(*eni_dev->free_list, - eni_dev->free_list_size + 1); - if (!eni_dev->free_list) { - printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n", - dev->number); - error = -ENOMEM; - goto free_irq; - } - eni_dev->free_len = 0; - eni_put_free(eni_dev,buf,buffer_mem); - memset_io(eni_dev->vci,0,16*NR_VCI); /* clear VCI table */ - /* - * byte_addr free (k) - * 0x00000000 512 VCI table - * 0x00004000 496 RX DMA - * 0x00005000 492 TX DMA - * 0x00006000 488 service list - * 0x00007000 484 buffers - * 0x00080000 0 end (512kB) - */ - eni_out(0xffffffff,MID_IE); - error = start_tx(dev); - if (error) goto free_list; - error = start_rx(dev); - if (error) goto free_list; - error = dev->phy->start(dev); - if (error) goto free_list; - eni_out(eni_in(MID_MC_S) | (1 << MID_INT_SEL_SHIFT) | - MID_TX_LOCK_MODE | MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE, - MID_MC_S); - /* Tonga uses SBus INTReq1 */ - (void) eni_in(MID_ISA); /* clear Midway interrupts */ - return 0; - -free_list: - kfree(eni_dev->free_list); - -free_irq: - free_irq(eni_dev->irq, dev); - -out: - return error; -} - - -static void eni_close(struct atm_vcc *vcc) -{ - DPRINTK(">eni_close\n"); - if (!ENI_VCC(vcc)) return; - clear_bit(ATM_VF_READY,&vcc->flags); - close_rx(vcc); - close_tx(vcc); - DPRINTK("eni_close: done waiting\n"); - /* deallocate memory */ - kfree(ENI_VCC(vcc)); - vcc->dev_data = NULL; - clear_bit(ATM_VF_ADDR,&vcc->flags); - /*foo();*/ -} - - -static int eni_open(struct atm_vcc *vcc) -{ - struct eni_vcc *eni_vcc; - int error; - short vpi = vcc->vpi; - int vci = vcc->vci; - - DPRINTK(">eni_open\n"); - EVENT("eni_open\n",0,0); - if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) - vcc->dev_data = NULL; - if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC) - set_bit(ATM_VF_ADDR,&vcc->flags); - if (vcc->qos.aal != ATM_AAL0 && vcc->qos.aal != ATM_AAL5) - return -EINVAL; - DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi, - vcc->vci); - if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) { - eni_vcc = kmalloc_obj(struct eni_vcc); - if (!eni_vcc) return -ENOMEM; - vcc->dev_data = eni_vcc; - eni_vcc->tx = NULL; /* for eni_close after open_rx */ - if ((error = open_rx_first(vcc))) { - eni_close(vcc); - return error; - } - if ((error = open_tx_first(vcc))) { - eni_close(vcc); - return error; - } - } - if (vci == ATM_VPI_UNSPEC || vpi == ATM_VCI_UNSPEC) return 0; - if ((error = open_rx_second(vcc))) { - eni_close(vcc); - return error; - } - if ((error = open_tx_second(vcc))) { - eni_close(vcc); - return error; - } - set_bit(ATM_VF_READY,&vcc->flags); - /* should power down SUNI while !ref_count @@@ */ - return 0; -} - - -static int eni_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flgs) -{ - struct eni_dev *eni_dev = ENI_DEV(vcc->dev); - struct eni_tx *tx = ENI_VCC(vcc)->tx; - struct sk_buff *skb; - int error,rate,rsv,shp; - - if (qos->txtp.traffic_class == ATM_NONE) return 0; - if (tx == eni_dev->ubr) return -EBADFD; - rate = atm_pcr_goal(&qos->txtp); - if (rate < 0) rate = -rate; - rsv = shp = 0; - if ((flgs & ATM_MF_DEC_RSV) && rate && rate < tx->reserved) rsv = 1; - if ((flgs & ATM_MF_INC_RSV) && (!rate || rate > tx->reserved)) rsv = 1; - if ((flgs & ATM_MF_DEC_SHP) && rate && rate < tx->shaping) shp = 1; - if ((flgs & ATM_MF_INC_SHP) && (!rate || rate > tx->shaping)) shp = 1; - if (!rsv && !shp) return 0; - error = reserve_or_set_tx(vcc,&qos->txtp,rsv,shp); - if (error) return error; - if (shp && !(flgs & ATM_MF_IMMED)) return 0; - /* - * Walk through the send buffer and patch the rate information in all - * segmentation buffer descriptors of this VCC. - */ - tasklet_disable(&eni_dev->task); - skb_queue_walk(&eni_dev->tx_queue, skb) { - void __iomem *dsc; - - if (ATM_SKB(skb)->vcc != vcc) continue; - dsc = tx->send+ENI_PRV_POS(skb)*4; - writel((readl(dsc) & ~(MID_SEG_RATE | MID_SEG_PR)) | - (tx->prescaler << MID_SEG_PR_SHIFT) | - (tx->resolution << MID_SEG_RATE_SHIFT), dsc); - } - tasklet_enable(&eni_dev->task); - return 0; -} - - -static int eni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) -{ - struct eni_dev *eni_dev = ENI_DEV(dev); - - if (cmd == ENI_MEMDUMP) { - if (!capable(CAP_NET_ADMIN)) return -EPERM; - printk(KERN_WARNING "Please use /proc/atm/" DEV_LABEL ":%d " - "instead of obsolete ioctl ENI_MEMDUMP\n",dev->number); - dump(dev); - return 0; - } - if (cmd == ENI_SETMULT) { - struct eni_multipliers mult; - - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (copy_from_user(&mult, arg, - sizeof(struct eni_multipliers))) - return -EFAULT; - if ((mult.tx && mult.tx <= 100) || (mult.rx &&mult.rx <= 100) || - mult.tx > 65536 || mult.rx > 65536) - return -EINVAL; - if (mult.tx) eni_dev->tx_mult = mult.tx; - if (mult.rx) eni_dev->rx_mult = mult.rx; - return 0; - } - if (cmd == ATM_SETCIRANGE) { - struct atm_cirange ci; - - if (copy_from_user(&ci, arg,sizeof(struct atm_cirange))) - return -EFAULT; - if ((ci.vpi_bits == 0 || ci.vpi_bits == ATM_CI_MAX) && - (ci.vci_bits == NR_VCI_LD || ci.vpi_bits == ATM_CI_MAX)) - return 0; - return -EINVAL; - } - if (!dev->phy->ioctl) return -ENOIOCTLCMD; - return dev->phy->ioctl(dev,cmd,arg); -} - -static int eni_send(struct atm_vcc *vcc,struct sk_buff *skb) -{ - enum enq_res res; - - DPRINTK(">eni_send\n"); - if (!ENI_VCC(vcc)->tx) { - if (vcc->pop) vcc->pop(vcc,skb); - else dev_kfree_skb(skb); - return -EINVAL; - } - if (!skb) { - printk(KERN_CRIT "!skb in eni_send ?\n"); - if (vcc->pop) vcc->pop(vcc,skb); - return -EINVAL; - } - if (vcc->qos.aal == ATM_AAL0) { - if (skb->len != ATM_CELL_SIZE-1) { - if (vcc->pop) vcc->pop(vcc,skb); - else dev_kfree_skb(skb); - return -EINVAL; - } - *(u32 *) skb->data = htonl(*(u32 *) skb->data); - } - submitted++; - ATM_SKB(skb)->vcc = vcc; - tasklet_disable_in_atomic(&ENI_DEV(vcc->dev)->task); - res = do_tx(skb); - tasklet_enable(&ENI_DEV(vcc->dev)->task); - if (res == enq_ok) return 0; - skb_queue_tail(&ENI_VCC(vcc)->tx->backlog,skb); - backlogged++; - tasklet_schedule(&ENI_DEV(vcc->dev)->task); - return 0; -} - -static void eni_phy_put(struct atm_dev *dev,unsigned char value, - unsigned long addr) -{ - writel(value,ENI_DEV(dev)->phy+addr*4); -} - - - -static unsigned char eni_phy_get(struct atm_dev *dev,unsigned long addr) -{ - return readl(ENI_DEV(dev)->phy+addr*4); -} - - -static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) -{ - struct sock *s; - static const char *signal[] = { "LOST","unknown","okay" }; - struct eni_dev *eni_dev = ENI_DEV(dev); - struct atm_vcc *vcc; - int left,i; - - left = *pos; - if (!left) - return sprintf(page,DEV_LABEL "(itf %d) signal %s, %dkB, " - "%d cps remaining\n",dev->number,signal[(int) dev->signal], - eni_dev->mem >> 10,eni_dev->tx_bw); - if (!--left) - return sprintf(page,"%4sBursts: TX" -#if !defined(CONFIG_ATM_ENI_BURST_TX_16W) && \ - !defined(CONFIG_ATM_ENI_BURST_TX_8W) && \ - !defined(CONFIG_ATM_ENI_BURST_TX_4W) && \ - !defined(CONFIG_ATM_ENI_BURST_TX_2W) - " none" -#endif -#ifdef CONFIG_ATM_ENI_BURST_TX_16W - " 16W" -#endif -#ifdef CONFIG_ATM_ENI_BURST_TX_8W - " 8W" -#endif -#ifdef CONFIG_ATM_ENI_BURST_TX_4W - " 4W" -#endif -#ifdef CONFIG_ATM_ENI_BURST_TX_2W - " 2W" -#endif - ", RX" -#if !defined(CONFIG_ATM_ENI_BURST_RX_16W) && \ - !defined(CONFIG_ATM_ENI_BURST_RX_8W) && \ - !defined(CONFIG_ATM_ENI_BURST_RX_4W) && \ - !defined(CONFIG_ATM_ENI_BURST_RX_2W) - " none" -#endif -#ifdef CONFIG_ATM_ENI_BURST_RX_16W - " 16W" -#endif -#ifdef CONFIG_ATM_ENI_BURST_RX_8W - " 8W" -#endif -#ifdef CONFIG_ATM_ENI_BURST_RX_4W - " 4W" -#endif -#ifdef CONFIG_ATM_ENI_BURST_RX_2W - " 2W" -#endif -#ifndef CONFIG_ATM_ENI_TUNE_BURST - " (default)" -#endif - "\n",""); - if (!--left) - return sprintf(page,"%4sBuffer multipliers: tx %d%%, rx %d%%\n", - "",eni_dev->tx_mult,eni_dev->rx_mult); - for (i = 0; i < NR_CHAN; i++) { - struct eni_tx *tx = eni_dev->tx+i; - - if (!tx->send) continue; - if (!--left) { - return sprintf(page, "tx[%d]: 0x%lx-0x%lx " - "(%6ld bytes), rsv %d cps, shp %d cps%s\n",i, - (unsigned long) (tx->send - eni_dev->ram), - tx->send-eni_dev->ram+tx->words*4-1,tx->words*4, - tx->reserved,tx->shaping, - tx == eni_dev->ubr ? " (UBR)" : ""); - } - if (--left) continue; - return sprintf(page,"%10sbacklog %u packets\n","", - skb_queue_len(&tx->backlog)); - } - read_lock(&vcc_sklist_lock); - for(i = 0; i < VCC_HTABLE_SIZE; ++i) { - struct hlist_head *head = &vcc_hash[i]; - - sk_for_each(s, head) { - struct eni_vcc *eni_vcc; - int length; - - vcc = atm_sk(s); - if (vcc->dev != dev) - continue; - eni_vcc = ENI_VCC(vcc); - if (--left) continue; - length = sprintf(page,"vcc %4d: ",vcc->vci); - if (eni_vcc->rx) { - length += sprintf(page+length, "0x%lx-0x%lx " - "(%6ld bytes)", - (unsigned long) (eni_vcc->recv - eni_dev->ram), - eni_vcc->recv-eni_dev->ram+eni_vcc->words*4-1, - eni_vcc->words*4); - if (eni_vcc->tx) length += sprintf(page+length,", "); - } - if (eni_vcc->tx) - length += sprintf(page+length,"tx[%d], txing %d bytes", - eni_vcc->tx->index,eni_vcc->txing); - page[length] = '\n'; - read_unlock(&vcc_sklist_lock); - return length+1; - } - } - read_unlock(&vcc_sklist_lock); - for (i = 0; i < eni_dev->free_len; i++) { - struct eni_free *fe = eni_dev->free_list+i; - unsigned long offset; - - if (--left) continue; - offset = (unsigned long) eni_dev->ram+eni_dev->base_diff; - return sprintf(page,"free %p-%p (%6d bytes)\n", - fe->start-offset,fe->start-offset+(1 << fe->order)-1, - 1 << fe->order); - } - return 0; -} - - -static const struct atmdev_ops ops = { - .open = eni_open, - .close = eni_close, - .ioctl = eni_ioctl, - .send = eni_send, - .phy_put = eni_phy_put, - .phy_get = eni_phy_get, - .change_qos = eni_change_qos, - .proc_read = eni_proc_read -}; - - -static int eni_init_one(struct pci_dev *pci_dev, - const struct pci_device_id *ent) -{ - struct atm_dev *dev; - struct eni_dev *eni_dev; - struct eni_zero *zero; - int rc; - - rc = pci_enable_device(pci_dev); - if (rc < 0) - goto out; - - rc = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32)); - if (rc < 0) - goto err_disable; - - rc = -ENOMEM; - eni_dev = kmalloc_obj(struct eni_dev); - if (!eni_dev) - goto err_disable; - - zero = &eni_dev->zero; - zero->addr = dma_alloc_coherent(&pci_dev->dev, - ENI_ZEROES_SIZE, &zero->dma, GFP_KERNEL); - if (!zero->addr) - goto err_kfree; - - dev = atm_dev_register(DEV_LABEL, &pci_dev->dev, &ops, -1, NULL); - if (!dev) - goto err_free_consistent; - - dev->dev_data = eni_dev; - pci_set_drvdata(pci_dev, dev); - eni_dev->pci_dev = pci_dev; - eni_dev->asic = ent->driver_data; - - rc = eni_do_init(dev); - if (rc < 0) - goto err_unregister; - - rc = eni_start(dev); - if (rc < 0) - goto err_eni_release; - - eni_dev->more = eni_boards; - eni_boards = dev; -out: - return rc; - -err_eni_release: - dev->phy = NULL; - iounmap(ENI_DEV(dev)->ioaddr); -err_unregister: - atm_dev_deregister(dev); -err_free_consistent: - dma_free_coherent(&pci_dev->dev, ENI_ZEROES_SIZE, zero->addr, zero->dma); -err_kfree: - kfree(eni_dev); -err_disable: - pci_disable_device(pci_dev); - goto out; -} - - -static const struct pci_device_id eni_pci_tbl[] = { - { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_FPGA), 0 /* FPGA */ }, - { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_ASIC), 1 /* ASIC */ }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci,eni_pci_tbl); - - -static void eni_remove_one(struct pci_dev *pdev) -{ - struct atm_dev *dev = pci_get_drvdata(pdev); - struct eni_dev *ed = ENI_DEV(dev); - struct eni_zero *zero = &ed->zero; - - eni_do_release(dev); - atm_dev_deregister(dev); - dma_free_coherent(&pdev->dev, ENI_ZEROES_SIZE, zero->addr, zero->dma); - kfree(ed); - pci_disable_device(pdev); -} - - -static struct pci_driver eni_driver = { - .name = DEV_LABEL, - .id_table = eni_pci_tbl, - .probe = eni_init_one, - .remove = eni_remove_one, -}; - - -static int __init eni_init(void) -{ - struct sk_buff *skb; /* dummy for sizeof */ - - BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct eni_skb_prv)); - return pci_register_driver(&eni_driver); -} - - -module_init(eni_init); -/* @@@ since exit routine not defined, this module can not be unloaded */ - -MODULE_DESCRIPTION("Efficient Networks ENI155P ATM NIC driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/atm/eni.h b/drivers/atm/eni.h deleted file mode 100644 index de1ed802cbf8..000000000000 --- a/drivers/atm/eni.h +++ /dev/null @@ -1,136 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* drivers/atm/eni.h - Efficient Networks ENI155P device driver declarations */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - - -#ifndef DRIVER_ATM_ENI_H -#define DRIVER_ATM_ENI_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "midway.h" - - -#define DEV_LABEL "eni" - -#define UBR_BUFFER (128*1024) /* UBR buffer size */ - -#define RX_DMA_BUF 8 /* burst and skip a few things */ -#define TX_DMA_BUF 100 /* should be enough for 64 kB */ - -#define DEFAULT_RX_MULT 300 /* max_sdu*3 */ -#define DEFAULT_TX_MULT 300 /* max_sdu*3 */ - -#define ENI_ZEROES_SIZE 4 /* need that many DMA-able zero bytes */ - - -struct eni_free { - void __iomem *start; /* counting in bytes */ - int order; -}; - -struct eni_tx { - void __iomem *send; /* base, 0 if unused */ - int prescaler; /* shaping prescaler */ - int resolution; /* shaping divider */ - unsigned long tx_pos; /* current TX write position */ - unsigned long words; /* size of TX queue */ - int index; /* TX channel number */ - int reserved; /* reserved peak cell rate */ - int shaping; /* shaped peak cell rate */ - struct sk_buff_head backlog; /* queue of waiting TX buffers */ -}; - -struct eni_vcc { - int (*rx)(struct atm_vcc *vcc); /* RX function, NULL if none */ - void __iomem *recv; /* receive buffer */ - unsigned long words; /* its size in words */ - unsigned long descr; /* next descriptor (RX) */ - unsigned long rx_pos; /* current RX descriptor pos */ - struct eni_tx *tx; /* TXer, NULL if none */ - int rxing; /* number of pending PDUs */ - int servicing; /* number of waiting VCs (0 or 1) */ - int txing; /* number of pending TX bytes */ - ktime_t timestamp; /* for RX timing */ - struct atm_vcc *next; /* next pending RX */ - struct sk_buff *last; /* last PDU being DMAed (used to carry - discard information) */ -}; - -struct eni_dev { - /*-------------------------------- spinlock */ - spinlock_t lock; /* sync with interrupt */ - struct tasklet_struct task; /* tasklet for interrupt work */ - u32 events; /* pending events */ - /*-------------------------------- base pointers into Midway address - space */ - void __iomem *ioaddr; - void __iomem *phy; /* PHY interface chip registers */ - void __iomem *reg; /* register base */ - void __iomem *ram; /* RAM base */ - void __iomem *vci; /* VCI table */ - void __iomem *rx_dma; /* RX DMA queue */ - void __iomem *tx_dma; /* TX DMA queue */ - void __iomem *service; /* service list */ - /*-------------------------------- TX part */ - struct eni_tx tx[NR_CHAN]; /* TX channels */ - struct eni_tx *ubr; /* UBR channel */ - struct sk_buff_head tx_queue; /* PDUs currently being TX DMAed*/ - wait_queue_head_t tx_wait; /* for close */ - int tx_bw; /* remaining bandwidth */ - u32 dma[TX_DMA_BUF*2]; /* DMA request scratch area */ - struct eni_zero { /* aligned "magic" zeroes */ - u32 *addr; - dma_addr_t dma; - } zero; - int tx_mult; /* buffer size multiplier (percent) */ - /*-------------------------------- RX part */ - u32 serv_read; /* host service read index */ - struct atm_vcc *fast,*last_fast;/* queues of VCCs with pending PDUs */ - struct atm_vcc *slow,*last_slow; - struct atm_vcc **rx_map; /* for fast lookups */ - struct sk_buff_head rx_queue; /* PDUs currently being RX-DMAed */ - wait_queue_head_t rx_wait; /* for close */ - int rx_mult; /* buffer size multiplier (percent) */ - /*-------------------------------- statistics */ - unsigned long lost; /* number of lost cells (RX) */ - /*-------------------------------- memory management */ - unsigned long base_diff; /* virtual-real base address */ - int free_len; /* free list length */ - struct eni_free *free_list; /* free list */ - int free_list_size; /* maximum size of free list */ - /*-------------------------------- ENI links */ - struct atm_dev *more; /* other ENI devices */ - /*-------------------------------- general information */ - int mem; /* RAM on board (in bytes) */ - int asic; /* PCI interface type, 0 for FPGA */ - unsigned int irq; /* IRQ */ - struct pci_dev *pci_dev; /* PCI stuff */ -}; - - -#define ENI_DEV(d) ((struct eni_dev *) (d)->dev_data) -#define ENI_VCC(d) ((struct eni_vcc *) (d)->dev_data) - - -struct eni_skb_prv { - struct atm_skb_data _; /* reserved */ - unsigned long pos; /* position of next descriptor */ - int size; /* PDU size in reassembly buffer */ - dma_addr_t paddr; /* DMA handle */ -}; - -#define ENI_PRV_SIZE(skb) (((struct eni_skb_prv *) (skb)->cb)->size) -#define ENI_PRV_POS(skb) (((struct eni_skb_prv *) (skb)->cb)->pos) -#define ENI_PRV_PADDR(skb) (((struct eni_skb_prv *) (skb)->cb)->paddr) - -#endif diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c deleted file mode 100644 index 2423eed506c1..000000000000 --- a/drivers/atm/fore200e.c +++ /dev/null @@ -1,3012 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - A FORE Systems 200E-series driver for ATM on Linux. - Christophe Lizzi (lizzi@cnam.fr), October 1999-March 2003. - - Based on the PCA-200E driver from Uwe Dannowski (Uwe.Dannowski@inf.tu-dresden.de). - - This driver simultaneously supports PCA-200E and SBA-200E adapters - on i386, alpha (untested), powerpc, sparc and sparc64 architectures. - -*/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_SBUS -#include -#include -#include -#include -#include -#endif - -#if defined(CONFIG_ATM_FORE200E_USE_TASKLET) /* defer interrupt work to a tasklet */ -#define FORE200E_USE_TASKLET -#endif - -#if 0 /* enable the debugging code of the buffer supply queues */ -#define FORE200E_BSQ_DEBUG -#endif - -#if 1 /* ensure correct handling of 52-byte AAL0 SDUs expected by atmdump-like apps */ -#define FORE200E_52BYTE_AAL0_SDU -#endif - -#include "fore200e.h" -#include "suni.h" - -#define FORE200E_VERSION "0.3e" - -#define FORE200E "fore200e: " - -#if 0 /* override .config */ -#define CONFIG_ATM_FORE200E_DEBUG 1 -#endif -#if defined(CONFIG_ATM_FORE200E_DEBUG) && (CONFIG_ATM_FORE200E_DEBUG > 0) -#define DPRINTK(level, format, args...) do { if (CONFIG_ATM_FORE200E_DEBUG >= (level)) \ - printk(FORE200E format, ##args); } while (0) -#else -#define DPRINTK(level, format, args...) do {} while (0) -#endif - - -#define FORE200E_ALIGN(addr, alignment) \ - ((((unsigned long)(addr) + (alignment - 1)) & ~(alignment - 1)) - (unsigned long)(addr)) - -#define FORE200E_DMA_INDEX(dma_addr, type, index) ((dma_addr) + (index) * sizeof(type)) - -#define FORE200E_INDEX(virt_addr, type, index) (&((type *)(virt_addr))[ index ]) - -#define FORE200E_NEXT_ENTRY(index, modulo) (index = ((index) + 1) % (modulo)) - -#if 1 -#define ASSERT(expr) if (!(expr)) { \ - printk(FORE200E "assertion failed! %s[%d]: %s\n", \ - __func__, __LINE__, #expr); \ - panic(FORE200E "%s", __func__); \ - } -#else -#define ASSERT(expr) do {} while (0) -#endif - - -static const struct atmdev_ops fore200e_ops; - -MODULE_AUTHOR("Christophe Lizzi - credits to Uwe Dannowski and Heikki Vatiainen"); -MODULE_DESCRIPTION("FORE Systems 200E-series ATM driver - version " FORE200E_VERSION); - -static const int fore200e_rx_buf_nbr[ BUFFER_SCHEME_NBR ][ BUFFER_MAGN_NBR ] = { - { BUFFER_S1_NBR, BUFFER_L1_NBR }, - { BUFFER_S2_NBR, BUFFER_L2_NBR } -}; - -static const int fore200e_rx_buf_size[ BUFFER_SCHEME_NBR ][ BUFFER_MAGN_NBR ] = { - { BUFFER_S1_SIZE, BUFFER_L1_SIZE }, - { BUFFER_S2_SIZE, BUFFER_L2_SIZE } -}; - - -#if defined(CONFIG_ATM_FORE200E_DEBUG) && (CONFIG_ATM_FORE200E_DEBUG > 0) -static const char* fore200e_traffic_class[] = { "NONE", "UBR", "CBR", "VBR", "ABR", "ANY" }; -#endif - - -#if 0 /* currently unused */ -static int -fore200e_fore2atm_aal(enum fore200e_aal aal) -{ - switch(aal) { - case FORE200E_AAL0: return ATM_AAL0; - case FORE200E_AAL34: return ATM_AAL34; - case FORE200E_AAL5: return ATM_AAL5; - } - - return -EINVAL; -} -#endif - - -static enum fore200e_aal -fore200e_atm2fore_aal(int aal) -{ - switch(aal) { - case ATM_AAL0: return FORE200E_AAL0; - case ATM_AAL34: return FORE200E_AAL34; - case ATM_AAL1: - case ATM_AAL2: - case ATM_AAL5: return FORE200E_AAL5; - } - - return -EINVAL; -} - - -static char* -fore200e_irq_itoa(int irq) -{ - static char str[8]; - sprintf(str, "%d", irq); - return str; -} - - -/* allocate and align a chunk of memory intended to hold the data behing exchanged - between the driver and the adapter (using streaming DVMA) */ - -static int -fore200e_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk, int size, int alignment, int direction) -{ - unsigned long offset = 0; - - if (alignment <= sizeof(int)) - alignment = 0; - - chunk->alloc_size = size + alignment; - chunk->direction = direction; - - chunk->alloc_addr = kzalloc(chunk->alloc_size, GFP_KERNEL); - if (chunk->alloc_addr == NULL) - return -ENOMEM; - - if (alignment > 0) - offset = FORE200E_ALIGN(chunk->alloc_addr, alignment); - - chunk->align_addr = chunk->alloc_addr + offset; - - chunk->dma_addr = dma_map_single(fore200e->dev, chunk->align_addr, - size, direction); - if (dma_mapping_error(fore200e->dev, chunk->dma_addr)) { - kfree(chunk->alloc_addr); - return -ENOMEM; - } - return 0; -} - - -/* free a chunk of memory */ - -static void -fore200e_chunk_free(struct fore200e* fore200e, struct chunk* chunk) -{ - dma_unmap_single(fore200e->dev, chunk->dma_addr, chunk->dma_size, - chunk->direction); - kfree(chunk->alloc_addr); -} - -/* - * Allocate a DMA consistent chunk of memory intended to act as a communication - * mechanism (to hold descriptors, status, queues, etc.) shared by the driver - * and the adapter. - */ -static int -fore200e_dma_chunk_alloc(struct fore200e *fore200e, struct chunk *chunk, - int size, int nbr, int alignment) -{ - /* returned chunks are page-aligned */ - chunk->alloc_size = size * nbr; - chunk->alloc_addr = dma_alloc_coherent(fore200e->dev, chunk->alloc_size, - &chunk->dma_addr, GFP_KERNEL); - if (!chunk->alloc_addr) - return -ENOMEM; - chunk->align_addr = chunk->alloc_addr; - return 0; -} - -/* - * Free a DMA consistent chunk of memory. - */ -static void -fore200e_dma_chunk_free(struct fore200e* fore200e, struct chunk* chunk) -{ - dma_free_coherent(fore200e->dev, chunk->alloc_size, chunk->alloc_addr, - chunk->dma_addr); -} - -static void -fore200e_spin(int msecs) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(msecs); - while (time_before(jiffies, timeout)); -} - - -static int -fore200e_poll(struct fore200e* fore200e, volatile u32* addr, u32 val, int msecs) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(msecs); - int ok; - - mb(); - do { - if ((ok = (*addr == val)) || (*addr & STATUS_ERROR)) - break; - - } while (time_before(jiffies, timeout)); - -#if 1 - if (!ok) { - printk(FORE200E "cmd polling failed, got status 0x%08x, expected 0x%08x\n", - *addr, val); - } -#endif - - return ok; -} - - -static int -fore200e_io_poll(struct fore200e* fore200e, volatile u32 __iomem *addr, u32 val, int msecs) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(msecs); - int ok; - - do { - if ((ok = (fore200e->bus->read(addr) == val))) - break; - - } while (time_before(jiffies, timeout)); - -#if 1 - if (!ok) { - printk(FORE200E "I/O polling failed, got status 0x%08x, expected 0x%08x\n", - fore200e->bus->read(addr), val); - } -#endif - - return ok; -} - - -static void -fore200e_free_rx_buf(struct fore200e* fore200e) -{ - int scheme, magn, nbr; - struct buffer* buffer; - - for (scheme = 0; scheme < BUFFER_SCHEME_NBR; scheme++) { - for (magn = 0; magn < BUFFER_MAGN_NBR; magn++) { - - if ((buffer = fore200e->host_bsq[ scheme ][ magn ].buffer) != NULL) { - - for (nbr = 0; nbr < fore200e_rx_buf_nbr[ scheme ][ magn ]; nbr++) { - - struct chunk* data = &buffer[ nbr ].data; - - if (data->alloc_addr != NULL) - fore200e_chunk_free(fore200e, data); - } - } - } - } -} - - -static void -fore200e_uninit_bs_queue(struct fore200e* fore200e) -{ - int scheme, magn; - - for (scheme = 0; scheme < BUFFER_SCHEME_NBR; scheme++) { - for (magn = 0; magn < BUFFER_MAGN_NBR; magn++) { - - struct chunk* status = &fore200e->host_bsq[ scheme ][ magn ].status; - struct chunk* rbd_block = &fore200e->host_bsq[ scheme ][ magn ].rbd_block; - - if (status->alloc_addr) - fore200e_dma_chunk_free(fore200e, status); - - if (rbd_block->alloc_addr) - fore200e_dma_chunk_free(fore200e, rbd_block); - } - } -} - - -static int -fore200e_reset(struct fore200e* fore200e, int diag) -{ - int ok; - - fore200e->cp_monitor = fore200e->virt_base + FORE200E_CP_MONITOR_OFFSET; - - fore200e->bus->write(BSTAT_COLD_START, &fore200e->cp_monitor->bstat); - - fore200e->bus->reset(fore200e); - - if (diag) { - ok = fore200e_io_poll(fore200e, &fore200e->cp_monitor->bstat, BSTAT_SELFTEST_OK, 1000); - if (ok == 0) { - - printk(FORE200E "device %s self-test failed\n", fore200e->name); - return -ENODEV; - } - - printk(FORE200E "device %s self-test passed\n", fore200e->name); - - fore200e->state = FORE200E_STATE_RESET; - } - - return 0; -} - - -static void -fore200e_shutdown(struct fore200e* fore200e) -{ - printk(FORE200E "removing device %s at 0x%lx, IRQ %s\n", - fore200e->name, fore200e->phys_base, - fore200e_irq_itoa(fore200e->irq)); - - if (fore200e->state > FORE200E_STATE_RESET) { - /* first, reset the board to prevent further interrupts or data transfers */ - fore200e_reset(fore200e, 0); - } - - /* then, release all allocated resources */ - switch(fore200e->state) { - - case FORE200E_STATE_COMPLETE: - kfree(fore200e->stats); - - fallthrough; - case FORE200E_STATE_IRQ: - free_irq(fore200e->irq, fore200e->atm_dev); -#ifdef FORE200E_USE_TASKLET - tasklet_kill(&fore200e->tx_tasklet); - tasklet_kill(&fore200e->rx_tasklet); -#endif - - fallthrough; - case FORE200E_STATE_ALLOC_BUF: - fore200e_free_rx_buf(fore200e); - - fallthrough; - case FORE200E_STATE_INIT_BSQ: - fore200e_uninit_bs_queue(fore200e); - - fallthrough; - case FORE200E_STATE_INIT_RXQ: - fore200e_dma_chunk_free(fore200e, &fore200e->host_rxq.status); - fore200e_dma_chunk_free(fore200e, &fore200e->host_rxq.rpd); - - fallthrough; - case FORE200E_STATE_INIT_TXQ: - fore200e_dma_chunk_free(fore200e, &fore200e->host_txq.status); - fore200e_dma_chunk_free(fore200e, &fore200e->host_txq.tpd); - - fallthrough; - case FORE200E_STATE_INIT_CMDQ: - fore200e_dma_chunk_free(fore200e, &fore200e->host_cmdq.status); - - fallthrough; - case FORE200E_STATE_INITIALIZE: - /* nothing to do for that state */ - - case FORE200E_STATE_START_FW: - /* nothing to do for that state */ - - case FORE200E_STATE_RESET: - /* nothing to do for that state */ - - case FORE200E_STATE_MAP: - fore200e->bus->unmap(fore200e); - - fallthrough; - case FORE200E_STATE_CONFIGURE: - /* nothing to do for that state */ - - case FORE200E_STATE_REGISTER: - /* XXX shouldn't we *start* by deregistering the device? */ - atm_dev_deregister(fore200e->atm_dev); - - fallthrough; - case FORE200E_STATE_BLANK: - /* nothing to do for that state */ - break; - } -} - - -#ifdef CONFIG_PCI - -static u32 fore200e_pca_read(volatile u32 __iomem *addr) -{ - /* on big-endian hosts, the board is configured to convert - the endianess of slave RAM accesses */ - return le32_to_cpu(readl(addr)); -} - - -static void fore200e_pca_write(u32 val, volatile u32 __iomem *addr) -{ - /* on big-endian hosts, the board is configured to convert - the endianess of slave RAM accesses */ - writel(cpu_to_le32(val), addr); -} - -static int -fore200e_pca_irq_check(struct fore200e* fore200e) -{ - /* this is a 1 bit register */ - int irq_posted = readl(fore200e->regs.pca.psr); - -#if defined(CONFIG_ATM_FORE200E_DEBUG) && (CONFIG_ATM_FORE200E_DEBUG == 2) - if (irq_posted && (readl(fore200e->regs.pca.hcr) & PCA200E_HCR_OUTFULL)) { - DPRINTK(2,"FIFO OUT full, device %d\n", fore200e->atm_dev->number); - } -#endif - - return irq_posted; -} - - -static void -fore200e_pca_irq_ack(struct fore200e* fore200e) -{ - writel(PCA200E_HCR_CLRINTR, fore200e->regs.pca.hcr); -} - - -static void -fore200e_pca_reset(struct fore200e* fore200e) -{ - writel(PCA200E_HCR_RESET, fore200e->regs.pca.hcr); - fore200e_spin(10); - writel(0, fore200e->regs.pca.hcr); -} - - -static int fore200e_pca_map(struct fore200e* fore200e) -{ - DPRINTK(2, "device %s being mapped in memory\n", fore200e->name); - - fore200e->virt_base = ioremap(fore200e->phys_base, PCA200E_IOSPACE_LENGTH); - - if (fore200e->virt_base == NULL) { - printk(FORE200E "can't map device %s\n", fore200e->name); - return -EFAULT; - } - - DPRINTK(1, "device %s mapped to 0x%p\n", fore200e->name, fore200e->virt_base); - - /* gain access to the PCA specific registers */ - fore200e->regs.pca.hcr = fore200e->virt_base + PCA200E_HCR_OFFSET; - fore200e->regs.pca.imr = fore200e->virt_base + PCA200E_IMR_OFFSET; - fore200e->regs.pca.psr = fore200e->virt_base + PCA200E_PSR_OFFSET; - - fore200e->state = FORE200E_STATE_MAP; - return 0; -} - - -static void -fore200e_pca_unmap(struct fore200e* fore200e) -{ - DPRINTK(2, "device %s being unmapped from memory\n", fore200e->name); - - if (fore200e->virt_base != NULL) - iounmap(fore200e->virt_base); -} - - -static int fore200e_pca_configure(struct fore200e *fore200e) -{ - struct pci_dev *pci_dev = to_pci_dev(fore200e->dev); - u8 master_ctrl, latency; - - DPRINTK(2, "device %s being configured\n", fore200e->name); - - if ((pci_dev->irq == 0) || (pci_dev->irq == 0xFF)) { - printk(FORE200E "incorrect IRQ setting - misconfigured PCI-PCI bridge?\n"); - return -EIO; - } - - pci_read_config_byte(pci_dev, PCA200E_PCI_MASTER_CTRL, &master_ctrl); - - master_ctrl = master_ctrl -#if defined(__BIG_ENDIAN) - /* request the PCA board to convert the endianess of slave RAM accesses */ - | PCA200E_CTRL_CONVERT_ENDIAN -#endif -#if 0 - | PCA200E_CTRL_DIS_CACHE_RD - | PCA200E_CTRL_DIS_WRT_INVAL - | PCA200E_CTRL_ENA_CONT_REQ_MODE - | PCA200E_CTRL_2_CACHE_WRT_INVAL -#endif - | PCA200E_CTRL_LARGE_PCI_BURSTS; - - pci_write_config_byte(pci_dev, PCA200E_PCI_MASTER_CTRL, master_ctrl); - - /* raise latency from 32 (default) to 192, as this seems to prevent NIC - lockups (under heavy rx loads) due to continuous 'FIFO OUT full' condition. - this may impact the performances of other PCI devices on the same bus, though */ - latency = 192; - pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency); - - fore200e->state = FORE200E_STATE_CONFIGURE; - return 0; -} - - -static int __init -fore200e_pca_prom_read(struct fore200e* fore200e, struct prom_data* prom) -{ - struct host_cmdq* cmdq = &fore200e->host_cmdq; - struct host_cmdq_entry* entry = &cmdq->host_entry[ cmdq->head ]; - struct prom_opcode opcode; - int ok; - u32 prom_dma; - - FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD); - - opcode.opcode = OPCODE_GET_PROM; - opcode.pad = 0; - - prom_dma = dma_map_single(fore200e->dev, prom, sizeof(struct prom_data), - DMA_FROM_DEVICE); - if (dma_mapping_error(fore200e->dev, prom_dma)) - return -ENOMEM; - - fore200e->bus->write(prom_dma, &entry->cp_entry->cmd.prom_block.prom_haddr); - - *entry->status = STATUS_PENDING; - - fore200e->bus->write(*(u32*)&opcode, (u32 __iomem *)&entry->cp_entry->cmd.prom_block.opcode); - - ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 400); - - *entry->status = STATUS_FREE; - - dma_unmap_single(fore200e->dev, prom_dma, sizeof(struct prom_data), DMA_FROM_DEVICE); - - if (ok == 0) { - printk(FORE200E "unable to get PROM data from device %s\n", fore200e->name); - return -EIO; - } - -#if defined(__BIG_ENDIAN) - -#define swap_here(addr) (*((u32*)(addr)) = swab32( *((u32*)(addr)) )) - - /* MAC address is stored as little-endian */ - swap_here(&prom->mac_addr[0]); - swap_here(&prom->mac_addr[4]); -#endif - - return 0; -} - - -static int -fore200e_pca_proc_read(struct fore200e* fore200e, char *page) -{ - struct pci_dev *pci_dev = to_pci_dev(fore200e->dev); - - return sprintf(page, " PCI bus/slot/function:\t%d/%d/%d\n", - pci_dev->bus->number, PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); -} - -static const struct fore200e_bus fore200e_pci_ops = { - .model_name = "PCA-200E", - .proc_name = "pca200e", - .descr_alignment = 32, - .buffer_alignment = 4, - .status_alignment = 32, - .read = fore200e_pca_read, - .write = fore200e_pca_write, - .configure = fore200e_pca_configure, - .map = fore200e_pca_map, - .reset = fore200e_pca_reset, - .prom_read = fore200e_pca_prom_read, - .unmap = fore200e_pca_unmap, - .irq_check = fore200e_pca_irq_check, - .irq_ack = fore200e_pca_irq_ack, - .proc_read = fore200e_pca_proc_read, -}; -#endif /* CONFIG_PCI */ - -#ifdef CONFIG_SBUS - -static u32 fore200e_sba_read(volatile u32 __iomem *addr) -{ - return sbus_readl(addr); -} - -static void fore200e_sba_write(u32 val, volatile u32 __iomem *addr) -{ - sbus_writel(val, addr); -} - -static void fore200e_sba_irq_enable(struct fore200e *fore200e) -{ - u32 hcr = fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_STICKY; - fore200e->bus->write(hcr | SBA200E_HCR_INTR_ENA, fore200e->regs.sba.hcr); -} - -static int fore200e_sba_irq_check(struct fore200e *fore200e) -{ - return fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_INTR_REQ; -} - -static void fore200e_sba_irq_ack(struct fore200e *fore200e) -{ - u32 hcr = fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_STICKY; - fore200e->bus->write(hcr | SBA200E_HCR_INTR_CLR, fore200e->regs.sba.hcr); -} - -static void fore200e_sba_reset(struct fore200e *fore200e) -{ - fore200e->bus->write(SBA200E_HCR_RESET, fore200e->regs.sba.hcr); - fore200e_spin(10); - fore200e->bus->write(0, fore200e->regs.sba.hcr); -} - -static int __init fore200e_sba_map(struct fore200e *fore200e) -{ - struct platform_device *op = to_platform_device(fore200e->dev); - unsigned int bursts; - - /* gain access to the SBA specific registers */ - fore200e->regs.sba.hcr = of_ioremap(&op->resource[0], 0, SBA200E_HCR_LENGTH, "SBA HCR"); - fore200e->regs.sba.bsr = of_ioremap(&op->resource[1], 0, SBA200E_BSR_LENGTH, "SBA BSR"); - fore200e->regs.sba.isr = of_ioremap(&op->resource[2], 0, SBA200E_ISR_LENGTH, "SBA ISR"); - fore200e->virt_base = of_ioremap(&op->resource[3], 0, SBA200E_RAM_LENGTH, "SBA RAM"); - - if (!fore200e->virt_base) { - printk(FORE200E "unable to map RAM of device %s\n", fore200e->name); - return -EFAULT; - } - - DPRINTK(1, "device %s mapped to 0x%p\n", fore200e->name, fore200e->virt_base); - - fore200e->bus->write(0x02, fore200e->regs.sba.isr); /* XXX hardwired interrupt level */ - - /* get the supported DVMA burst sizes */ - bursts = of_getintprop_default(op->dev.of_node->parent, "burst-sizes", 0x00); - - if (sbus_can_dma_64bit()) - sbus_set_sbus64(&op->dev, bursts); - - fore200e->state = FORE200E_STATE_MAP; - return 0; -} - -static void fore200e_sba_unmap(struct fore200e *fore200e) -{ - struct platform_device *op = to_platform_device(fore200e->dev); - - of_iounmap(&op->resource[0], fore200e->regs.sba.hcr, SBA200E_HCR_LENGTH); - of_iounmap(&op->resource[1], fore200e->regs.sba.bsr, SBA200E_BSR_LENGTH); - of_iounmap(&op->resource[2], fore200e->regs.sba.isr, SBA200E_ISR_LENGTH); - of_iounmap(&op->resource[3], fore200e->virt_base, SBA200E_RAM_LENGTH); -} - -static int __init fore200e_sba_configure(struct fore200e *fore200e) -{ - fore200e->state = FORE200E_STATE_CONFIGURE; - return 0; -} - -static int __init fore200e_sba_prom_read(struct fore200e *fore200e, struct prom_data *prom) -{ - struct platform_device *op = to_platform_device(fore200e->dev); - const u8 *prop; - int len; - - prop = of_get_property(op->dev.of_node, "madaddrlo2", &len); - if (!prop) - return -ENODEV; - memcpy(&prom->mac_addr[4], prop, 4); - - prop = of_get_property(op->dev.of_node, "madaddrhi4", &len); - if (!prop) - return -ENODEV; - memcpy(&prom->mac_addr[2], prop, 4); - - prom->serial_number = of_getintprop_default(op->dev.of_node, - "serialnumber", 0); - prom->hw_revision = of_getintprop_default(op->dev.of_node, - "promversion", 0); - - return 0; -} - -static int fore200e_sba_proc_read(struct fore200e *fore200e, char *page) -{ - struct platform_device *op = to_platform_device(fore200e->dev); - const struct linux_prom_registers *regs; - - regs = of_get_property(op->dev.of_node, "reg", NULL); - - return sprintf(page, " SBUS slot/device:\t\t%d/'%pOFn'\n", - (regs ? regs->which_io : 0), op->dev.of_node); -} - -static const struct fore200e_bus fore200e_sbus_ops = { - .model_name = "SBA-200E", - .proc_name = "sba200e", - .descr_alignment = 32, - .buffer_alignment = 64, - .status_alignment = 32, - .read = fore200e_sba_read, - .write = fore200e_sba_write, - .configure = fore200e_sba_configure, - .map = fore200e_sba_map, - .reset = fore200e_sba_reset, - .prom_read = fore200e_sba_prom_read, - .unmap = fore200e_sba_unmap, - .irq_enable = fore200e_sba_irq_enable, - .irq_check = fore200e_sba_irq_check, - .irq_ack = fore200e_sba_irq_ack, - .proc_read = fore200e_sba_proc_read, -}; -#endif /* CONFIG_SBUS */ - -static void -fore200e_tx_irq(struct fore200e* fore200e) -{ - struct host_txq* txq = &fore200e->host_txq; - struct host_txq_entry* entry; - struct atm_vcc* vcc; - struct fore200e_vc_map* vc_map; - - if (fore200e->host_txq.txing == 0) - return; - - for (;;) { - - entry = &txq->host_entry[ txq->tail ]; - - if ((*entry->status & STATUS_COMPLETE) == 0) { - break; - } - - DPRINTK(3, "TX COMPLETED: entry = %p [tail = %d], vc_map = %p, skb = %p\n", - entry, txq->tail, entry->vc_map, entry->skb); - - /* free copy of misaligned data */ - kfree(entry->data); - - /* remove DMA mapping */ - dma_unmap_single(fore200e->dev, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length, - DMA_TO_DEVICE); - - vc_map = entry->vc_map; - - /* vcc closed since the time the entry was submitted for tx? */ - if ((vc_map->vcc == NULL) || - (test_bit(ATM_VF_READY, &vc_map->vcc->flags) == 0)) { - - DPRINTK(1, "no ready vcc found for PDU sent on device %d\n", - fore200e->atm_dev->number); - - dev_kfree_skb_any(entry->skb); - } - else { - ASSERT(vc_map->vcc); - - /* vcc closed then immediately re-opened? */ - if (vc_map->incarn != entry->incarn) { - - /* when a vcc is closed, some PDUs may be still pending in the tx queue. - if the same vcc is immediately re-opened, those pending PDUs must - not be popped after the completion of their emission, as they refer - to the prior incarnation of that vcc. otherwise, sk_atm(vcc)->sk_wmem_alloc - would be decremented by the size of the (unrelated) skb, possibly - leading to a negative sk->sk_wmem_alloc count, ultimately freezing the vcc. - we thus bind the tx entry to the current incarnation of the vcc - when the entry is submitted for tx. When the tx later completes, - if the incarnation number of the tx entry does not match the one - of the vcc, then this implies that the vcc has been closed then re-opened. - we thus just drop the skb here. */ - - DPRINTK(1, "vcc closed-then-re-opened; dropping PDU sent on device %d\n", - fore200e->atm_dev->number); - - dev_kfree_skb_any(entry->skb); - } - else { - vcc = vc_map->vcc; - ASSERT(vcc); - - /* notify tx completion */ - if (vcc->pop) { - vcc->pop(vcc, entry->skb); - } - else { - dev_kfree_skb_any(entry->skb); - } - - /* check error condition */ - if (*entry->status & STATUS_ERROR) - atomic_inc(&vcc->stats->tx_err); - else - atomic_inc(&vcc->stats->tx); - } - } - - *entry->status = STATUS_FREE; - - fore200e->host_txq.txing--; - - FORE200E_NEXT_ENTRY(txq->tail, QUEUE_SIZE_TX); - } -} - - -#ifdef FORE200E_BSQ_DEBUG -int bsq_audit(int where, struct host_bsq* bsq, int scheme, int magn) -{ - struct buffer* buffer; - int count = 0; - - buffer = bsq->freebuf; - while (buffer) { - - if (buffer->supplied) { - printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld supplied but in free list!\n", - where, scheme, magn, buffer->index); - } - - if (buffer->magn != magn) { - printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld, unexpected magn = %d\n", - where, scheme, magn, buffer->index, buffer->magn); - } - - if (buffer->scheme != scheme) { - printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld, unexpected scheme = %d\n", - where, scheme, magn, buffer->index, buffer->scheme); - } - - if ((buffer->index < 0) || (buffer->index >= fore200e_rx_buf_nbr[ scheme ][ magn ])) { - printk(FORE200E "bsq_audit(%d): queue %d.%d, out of range buffer index = %ld !\n", - where, scheme, magn, buffer->index); - } - - count++; - buffer = buffer->next; - } - - if (count != bsq->freebuf_count) { - printk(FORE200E "bsq_audit(%d): queue %d.%d, %d bufs in free list, but freebuf_count = %d\n", - where, scheme, magn, count, bsq->freebuf_count); - } - return 0; -} -#endif - - -static void -fore200e_supply(struct fore200e* fore200e) -{ - int scheme, magn, i; - - struct host_bsq* bsq; - struct host_bsq_entry* entry; - struct buffer* buffer; - - for (scheme = 0; scheme < BUFFER_SCHEME_NBR; scheme++) { - for (magn = 0; magn < BUFFER_MAGN_NBR; magn++) { - - bsq = &fore200e->host_bsq[ scheme ][ magn ]; - -#ifdef FORE200E_BSQ_DEBUG - bsq_audit(1, bsq, scheme, magn); -#endif - while (bsq->freebuf_count >= RBD_BLK_SIZE) { - - DPRINTK(2, "supplying %d rx buffers to queue %d / %d, freebuf_count = %d\n", - RBD_BLK_SIZE, scheme, magn, bsq->freebuf_count); - - entry = &bsq->host_entry[ bsq->head ]; - - for (i = 0; i < RBD_BLK_SIZE; i++) { - - /* take the first buffer in the free buffer list */ - buffer = bsq->freebuf; - if (!buffer) { - printk(FORE200E "no more free bufs in queue %d.%d, but freebuf_count = %d\n", - scheme, magn, bsq->freebuf_count); - return; - } - bsq->freebuf = buffer->next; - -#ifdef FORE200E_BSQ_DEBUG - if (buffer->supplied) - printk(FORE200E "queue %d.%d, buffer %lu already supplied\n", - scheme, magn, buffer->index); - buffer->supplied = 1; -#endif - entry->rbd_block->rbd[ i ].buffer_haddr = buffer->data.dma_addr; - entry->rbd_block->rbd[ i ].handle = FORE200E_BUF2HDL(buffer); - } - - FORE200E_NEXT_ENTRY(bsq->head, QUEUE_SIZE_BS); - - /* decrease accordingly the number of free rx buffers */ - bsq->freebuf_count -= RBD_BLK_SIZE; - - *entry->status = STATUS_PENDING; - fore200e->bus->write(entry->rbd_block_dma, &entry->cp_entry->rbd_block_haddr); - } - } - } -} - - -static int -fore200e_push_rpd(struct fore200e* fore200e, struct atm_vcc* vcc, struct rpd* rpd) -{ - struct sk_buff* skb; - struct buffer* buffer; - struct fore200e_vcc* fore200e_vcc; - int i, pdu_len = 0; -#ifdef FORE200E_52BYTE_AAL0_SDU - u32 cell_header = 0; -#endif - - ASSERT(vcc); - - fore200e_vcc = FORE200E_VCC(vcc); - ASSERT(fore200e_vcc); - -#ifdef FORE200E_52BYTE_AAL0_SDU - if ((vcc->qos.aal == ATM_AAL0) && (vcc->qos.rxtp.max_sdu == ATM_AAL0_SDU)) { - - cell_header = (rpd->atm_header.gfc << ATM_HDR_GFC_SHIFT) | - (rpd->atm_header.vpi << ATM_HDR_VPI_SHIFT) | - (rpd->atm_header.vci << ATM_HDR_VCI_SHIFT) | - (rpd->atm_header.plt << ATM_HDR_PTI_SHIFT) | - rpd->atm_header.clp; - pdu_len = 4; - } -#endif - - /* compute total PDU length */ - for (i = 0; i < rpd->nseg; i++) - pdu_len += rpd->rsd[ i ].length; - - skb = alloc_skb(pdu_len, GFP_ATOMIC); - if (skb == NULL) { - DPRINTK(2, "unable to alloc new skb, rx PDU length = %d\n", pdu_len); - - atomic_inc(&vcc->stats->rx_drop); - return -ENOMEM; - } - - __net_timestamp(skb); - -#ifdef FORE200E_52BYTE_AAL0_SDU - if (cell_header) { - *((u32*)skb_put(skb, 4)) = cell_header; - } -#endif - - /* reassemble segments */ - for (i = 0; i < rpd->nseg; i++) { - - /* rebuild rx buffer address from rsd handle */ - buffer = FORE200E_HDL2BUF(rpd->rsd[ i ].handle); - - /* Make device DMA transfer visible to CPU. */ - dma_sync_single_for_cpu(fore200e->dev, buffer->data.dma_addr, - rpd->rsd[i].length, DMA_FROM_DEVICE); - - skb_put_data(skb, buffer->data.align_addr, rpd->rsd[i].length); - - /* Now let the device get at it again. */ - dma_sync_single_for_device(fore200e->dev, buffer->data.dma_addr, - rpd->rsd[i].length, DMA_FROM_DEVICE); - } - - DPRINTK(3, "rx skb: len = %d, truesize = %d\n", skb->len, skb->truesize); - - if (pdu_len < fore200e_vcc->rx_min_pdu) - fore200e_vcc->rx_min_pdu = pdu_len; - if (pdu_len > fore200e_vcc->rx_max_pdu) - fore200e_vcc->rx_max_pdu = pdu_len; - fore200e_vcc->rx_pdu++; - - /* push PDU */ - if (atm_charge(vcc, skb->truesize) == 0) { - - DPRINTK(2, "receive buffers saturated for %d.%d.%d - PDU dropped\n", - vcc->itf, vcc->vpi, vcc->vci); - - dev_kfree_skb_any(skb); - - atomic_inc(&vcc->stats->rx_drop); - return -ENOMEM; - } - - vcc->push(vcc, skb); - atomic_inc(&vcc->stats->rx); - - return 0; -} - - -static void -fore200e_collect_rpd(struct fore200e* fore200e, struct rpd* rpd) -{ - struct host_bsq* bsq; - struct buffer* buffer; - int i; - - for (i = 0; i < rpd->nseg; i++) { - - /* rebuild rx buffer address from rsd handle */ - buffer = FORE200E_HDL2BUF(rpd->rsd[ i ].handle); - - bsq = &fore200e->host_bsq[ buffer->scheme ][ buffer->magn ]; - -#ifdef FORE200E_BSQ_DEBUG - bsq_audit(2, bsq, buffer->scheme, buffer->magn); - - if (buffer->supplied == 0) - printk(FORE200E "queue %d.%d, buffer %ld was not supplied\n", - buffer->scheme, buffer->magn, buffer->index); - buffer->supplied = 0; -#endif - - /* re-insert the buffer into the free buffer list */ - buffer->next = bsq->freebuf; - bsq->freebuf = buffer; - - /* then increment the number of free rx buffers */ - bsq->freebuf_count++; - } -} - - -static void -fore200e_rx_irq(struct fore200e* fore200e) -{ - struct host_rxq* rxq = &fore200e->host_rxq; - struct host_rxq_entry* entry; - struct atm_vcc* vcc; - struct fore200e_vc_map* vc_map; - - for (;;) { - - entry = &rxq->host_entry[ rxq->head ]; - - /* no more received PDUs */ - if ((*entry->status & STATUS_COMPLETE) == 0) - break; - - vc_map = FORE200E_VC_MAP(fore200e, entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci); - - if ((vc_map->vcc == NULL) || - (test_bit(ATM_VF_READY, &vc_map->vcc->flags) == 0)) { - - DPRINTK(1, "no ready VC found for PDU received on %d.%d.%d\n", - fore200e->atm_dev->number, - entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci); - } - else { - vcc = vc_map->vcc; - ASSERT(vcc); - - if ((*entry->status & STATUS_ERROR) == 0) { - - fore200e_push_rpd(fore200e, vcc, entry->rpd); - } - else { - DPRINTK(2, "damaged PDU on %d.%d.%d\n", - fore200e->atm_dev->number, - entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci); - atomic_inc(&vcc->stats->rx_err); - } - } - - FORE200E_NEXT_ENTRY(rxq->head, QUEUE_SIZE_RX); - - fore200e_collect_rpd(fore200e, entry->rpd); - - /* rewrite the rpd address to ack the received PDU */ - fore200e->bus->write(entry->rpd_dma, &entry->cp_entry->rpd_haddr); - *entry->status = STATUS_FREE; - - fore200e_supply(fore200e); - } -} - - -#ifndef FORE200E_USE_TASKLET -static void -fore200e_irq(struct fore200e* fore200e) -{ - unsigned long flags; - - spin_lock_irqsave(&fore200e->q_lock, flags); - fore200e_rx_irq(fore200e); - spin_unlock_irqrestore(&fore200e->q_lock, flags); - - spin_lock_irqsave(&fore200e->q_lock, flags); - fore200e_tx_irq(fore200e); - spin_unlock_irqrestore(&fore200e->q_lock, flags); -} -#endif - - -static irqreturn_t -fore200e_interrupt(int irq, void* dev) -{ - struct fore200e* fore200e = FORE200E_DEV((struct atm_dev*)dev); - - if (fore200e->bus->irq_check(fore200e) == 0) { - - DPRINTK(3, "interrupt NOT triggered by device %d\n", fore200e->atm_dev->number); - return IRQ_NONE; - } - DPRINTK(3, "interrupt triggered by device %d\n", fore200e->atm_dev->number); - -#ifdef FORE200E_USE_TASKLET - tasklet_schedule(&fore200e->tx_tasklet); - tasklet_schedule(&fore200e->rx_tasklet); -#else - fore200e_irq(fore200e); -#endif - - fore200e->bus->irq_ack(fore200e); - return IRQ_HANDLED; -} - - -#ifdef FORE200E_USE_TASKLET -static void -fore200e_tx_tasklet(unsigned long data) -{ - struct fore200e* fore200e = (struct fore200e*) data; - unsigned long flags; - - DPRINTK(3, "tx tasklet scheduled for device %d\n", fore200e->atm_dev->number); - - spin_lock_irqsave(&fore200e->q_lock, flags); - fore200e_tx_irq(fore200e); - spin_unlock_irqrestore(&fore200e->q_lock, flags); -} - - -static void -fore200e_rx_tasklet(unsigned long data) -{ - struct fore200e* fore200e = (struct fore200e*) data; - unsigned long flags; - - DPRINTK(3, "rx tasklet scheduled for device %d\n", fore200e->atm_dev->number); - - spin_lock_irqsave(&fore200e->q_lock, flags); - fore200e_rx_irq((struct fore200e*) data); - spin_unlock_irqrestore(&fore200e->q_lock, flags); -} -#endif - - -static int -fore200e_select_scheme(struct atm_vcc* vcc) -{ - /* fairly balance the VCs over (identical) buffer schemes */ - int scheme = vcc->vci % 2 ? BUFFER_SCHEME_ONE : BUFFER_SCHEME_TWO; - - DPRINTK(1, "VC %d.%d.%d uses buffer scheme %d\n", - vcc->itf, vcc->vpi, vcc->vci, scheme); - - return scheme; -} - - -static int -fore200e_activate_vcin(struct fore200e* fore200e, int activate, struct atm_vcc* vcc, int mtu) -{ - struct host_cmdq* cmdq = &fore200e->host_cmdq; - struct host_cmdq_entry* entry = &cmdq->host_entry[ cmdq->head ]; - struct activate_opcode activ_opcode; - struct deactivate_opcode deactiv_opcode; - struct vpvc vpvc; - int ok; - enum fore200e_aal aal = fore200e_atm2fore_aal(vcc->qos.aal); - - FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD); - - if (activate) { - FORE200E_VCC(vcc)->scheme = fore200e_select_scheme(vcc); - - activ_opcode.opcode = OPCODE_ACTIVATE_VCIN; - activ_opcode.aal = aal; - activ_opcode.scheme = FORE200E_VCC(vcc)->scheme; - activ_opcode.pad = 0; - } - else { - deactiv_opcode.opcode = OPCODE_DEACTIVATE_VCIN; - deactiv_opcode.pad = 0; - } - - vpvc.vci = vcc->vci; - vpvc.vpi = vcc->vpi; - - *entry->status = STATUS_PENDING; - - if (activate) { - -#ifdef FORE200E_52BYTE_AAL0_SDU - mtu = 48; -#endif - /* the MTU is not used by the cp, except in the case of AAL0 */ - fore200e->bus->write(mtu, &entry->cp_entry->cmd.activate_block.mtu); - fore200e->bus->write(*(u32*)&vpvc, (u32 __iomem *)&entry->cp_entry->cmd.activate_block.vpvc); - fore200e->bus->write(*(u32*)&activ_opcode, (u32 __iomem *)&entry->cp_entry->cmd.activate_block.opcode); - } - else { - fore200e->bus->write(*(u32*)&vpvc, (u32 __iomem *)&entry->cp_entry->cmd.deactivate_block.vpvc); - fore200e->bus->write(*(u32*)&deactiv_opcode, (u32 __iomem *)&entry->cp_entry->cmd.deactivate_block.opcode); - } - - ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 400); - - *entry->status = STATUS_FREE; - - if (ok == 0) { - printk(FORE200E "unable to %s VC %d.%d.%d\n", - activate ? "open" : "close", vcc->itf, vcc->vpi, vcc->vci); - return -EIO; - } - - DPRINTK(1, "VC %d.%d.%d %sed\n", vcc->itf, vcc->vpi, vcc->vci, - activate ? "open" : "clos"); - - return 0; -} - - -#define FORE200E_MAX_BACK2BACK_CELLS 255 /* XXX depends on CDVT */ - -static void -fore200e_rate_ctrl(struct atm_qos* qos, struct tpd_rate* rate) -{ - if (qos->txtp.max_pcr < ATM_OC3_PCR) { - - /* compute the data cells to idle cells ratio from the tx PCR */ - rate->data_cells = qos->txtp.max_pcr * FORE200E_MAX_BACK2BACK_CELLS / ATM_OC3_PCR; - rate->idle_cells = FORE200E_MAX_BACK2BACK_CELLS - rate->data_cells; - } - else { - /* disable rate control */ - rate->data_cells = rate->idle_cells = 0; - } -} - - -static int -fore200e_open(struct atm_vcc *vcc) -{ - struct fore200e* fore200e = FORE200E_DEV(vcc->dev); - struct fore200e_vcc* fore200e_vcc; - struct fore200e_vc_map* vc_map; - unsigned long flags; - int vci = vcc->vci; - short vpi = vcc->vpi; - - ASSERT((vpi >= 0) && (vpi < 1<= 0) && (vci < 1<q_lock, flags); - - vc_map = FORE200E_VC_MAP(fore200e, vpi, vci); - if (vc_map->vcc) { - - spin_unlock_irqrestore(&fore200e->q_lock, flags); - - printk(FORE200E "VC %d.%d.%d already in use\n", - fore200e->atm_dev->number, vpi, vci); - - return -EINVAL; - } - - vc_map->vcc = vcc; - - spin_unlock_irqrestore(&fore200e->q_lock, flags); - - fore200e_vcc = kzalloc_obj(struct fore200e_vcc, GFP_ATOMIC); - if (fore200e_vcc == NULL) { - vc_map->vcc = NULL; - return -ENOMEM; - } - - DPRINTK(2, "opening %d.%d.%d:%d QoS = (tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; " - "rx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d)\n", - vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal), - fore200e_traffic_class[ vcc->qos.txtp.traffic_class ], - vcc->qos.txtp.min_pcr, vcc->qos.txtp.max_pcr, vcc->qos.txtp.max_cdv, vcc->qos.txtp.max_sdu, - fore200e_traffic_class[ vcc->qos.rxtp.traffic_class ], - vcc->qos.rxtp.min_pcr, vcc->qos.rxtp.max_pcr, vcc->qos.rxtp.max_cdv, vcc->qos.rxtp.max_sdu); - - /* pseudo-CBR bandwidth requested? */ - if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) { - - mutex_lock(&fore200e->rate_mtx); - if (fore200e->available_cell_rate < vcc->qos.txtp.max_pcr) { - mutex_unlock(&fore200e->rate_mtx); - - kfree(fore200e_vcc); - vc_map->vcc = NULL; - return -EAGAIN; - } - - /* reserve bandwidth */ - fore200e->available_cell_rate -= vcc->qos.txtp.max_pcr; - mutex_unlock(&fore200e->rate_mtx); - } - - vcc->itf = vcc->dev->number; - - set_bit(ATM_VF_PARTIAL,&vcc->flags); - set_bit(ATM_VF_ADDR, &vcc->flags); - - vcc->dev_data = fore200e_vcc; - - if (fore200e_activate_vcin(fore200e, 1, vcc, vcc->qos.rxtp.max_sdu) < 0) { - - vc_map->vcc = NULL; - - clear_bit(ATM_VF_ADDR, &vcc->flags); - clear_bit(ATM_VF_PARTIAL,&vcc->flags); - - vcc->dev_data = NULL; - - mutex_lock(&fore200e->rate_mtx); - fore200e->available_cell_rate += vcc->qos.txtp.max_pcr; - mutex_unlock(&fore200e->rate_mtx); - - kfree(fore200e_vcc); - return -EINVAL; - } - - /* compute rate control parameters */ - if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) { - - fore200e_rate_ctrl(&vcc->qos, &fore200e_vcc->rate); - set_bit(ATM_VF_HASQOS, &vcc->flags); - - DPRINTK(3, "tx on %d.%d.%d:%d, tx PCR = %d, rx PCR = %d, data_cells = %u, idle_cells = %u\n", - vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal), - vcc->qos.txtp.max_pcr, vcc->qos.rxtp.max_pcr, - fore200e_vcc->rate.data_cells, fore200e_vcc->rate.idle_cells); - } - - fore200e_vcc->tx_min_pdu = fore200e_vcc->rx_min_pdu = MAX_PDU_SIZE + 1; - fore200e_vcc->tx_max_pdu = fore200e_vcc->rx_max_pdu = 0; - fore200e_vcc->tx_pdu = fore200e_vcc->rx_pdu = 0; - - /* new incarnation of the vcc */ - vc_map->incarn = ++fore200e->incarn_count; - - /* VC unusable before this flag is set */ - set_bit(ATM_VF_READY, &vcc->flags); - - return 0; -} - - -static void -fore200e_close(struct atm_vcc* vcc) -{ - struct fore200e_vcc* fore200e_vcc; - struct fore200e* fore200e; - struct fore200e_vc_map* vc_map; - unsigned long flags; - - ASSERT(vcc); - fore200e = FORE200E_DEV(vcc->dev); - - ASSERT((vcc->vpi >= 0) && (vcc->vpi < 1<vci >= 0) && (vcc->vci < 1<itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal)); - - clear_bit(ATM_VF_READY, &vcc->flags); - - fore200e_activate_vcin(fore200e, 0, vcc, 0); - - spin_lock_irqsave(&fore200e->q_lock, flags); - - vc_map = FORE200E_VC_MAP(fore200e, vcc->vpi, vcc->vci); - - /* the vc is no longer considered as "in use" by fore200e_open() */ - vc_map->vcc = NULL; - - vcc->itf = vcc->vci = vcc->vpi = 0; - - fore200e_vcc = FORE200E_VCC(vcc); - vcc->dev_data = NULL; - - spin_unlock_irqrestore(&fore200e->q_lock, flags); - - /* release reserved bandwidth, if any */ - if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) { - - mutex_lock(&fore200e->rate_mtx); - fore200e->available_cell_rate += vcc->qos.txtp.max_pcr; - mutex_unlock(&fore200e->rate_mtx); - - clear_bit(ATM_VF_HASQOS, &vcc->flags); - } - - clear_bit(ATM_VF_ADDR, &vcc->flags); - clear_bit(ATM_VF_PARTIAL,&vcc->flags); - - ASSERT(fore200e_vcc); - kfree(fore200e_vcc); -} - - -static int -fore200e_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct fore200e* fore200e; - struct fore200e_vcc* fore200e_vcc; - struct fore200e_vc_map* vc_map; - struct host_txq* txq; - struct host_txq_entry* entry; - struct tpd* tpd; - struct tpd_haddr tpd_haddr; - int retry = CONFIG_ATM_FORE200E_TX_RETRY; - int tx_copy = 0; - int tx_len = skb->len; - u32* cell_header = NULL; - unsigned char* skb_data; - int skb_len; - unsigned char* data; - unsigned long flags; - - if (!vcc) - return -EINVAL; - - fore200e = FORE200E_DEV(vcc->dev); - fore200e_vcc = FORE200E_VCC(vcc); - - if (!fore200e) - return -EINVAL; - - txq = &fore200e->host_txq; - if (!fore200e_vcc) - return -EINVAL; - - if (!test_bit(ATM_VF_READY, &vcc->flags)) { - DPRINTK(1, "VC %d.%d.%d not ready for tx\n", vcc->itf, vcc->vpi, vcc->vpi); - dev_kfree_skb_any(skb); - return -EINVAL; - } - -#ifdef FORE200E_52BYTE_AAL0_SDU - if ((vcc->qos.aal == ATM_AAL0) && (vcc->qos.txtp.max_sdu == ATM_AAL0_SDU)) { - cell_header = (u32*) skb->data; - skb_data = skb->data + 4; /* skip 4-byte cell header */ - skb_len = tx_len = skb->len - 4; - - DPRINTK(3, "user-supplied cell header = 0x%08x\n", *cell_header); - } - else -#endif - { - skb_data = skb->data; - skb_len = skb->len; - } - - if (((unsigned long)skb_data) & 0x3) { - - DPRINTK(2, "misaligned tx PDU on device %s\n", fore200e->name); - tx_copy = 1; - tx_len = skb_len; - } - - if ((vcc->qos.aal == ATM_AAL0) && (skb_len % ATM_CELL_PAYLOAD)) { - - /* this simply NUKES the PCA board */ - DPRINTK(2, "incomplete tx AAL0 PDU on device %s\n", fore200e->name); - tx_copy = 1; - tx_len = ((skb_len / ATM_CELL_PAYLOAD) + 1) * ATM_CELL_PAYLOAD; - } - - if (tx_copy) { - data = kmalloc(tx_len, GFP_ATOMIC); - if (data == NULL) { - if (vcc->pop) { - vcc->pop(vcc, skb); - } - else { - dev_kfree_skb_any(skb); - } - return -ENOMEM; - } - - memcpy(data, skb_data, skb_len); - if (skb_len < tx_len) - memset(data + skb_len, 0x00, tx_len - skb_len); - } - else { - data = skb_data; - } - - vc_map = FORE200E_VC_MAP(fore200e, vcc->vpi, vcc->vci); - ASSERT(vc_map->vcc == vcc); - - retry_here: - - spin_lock_irqsave(&fore200e->q_lock, flags); - - entry = &txq->host_entry[ txq->head ]; - - if ((*entry->status != STATUS_FREE) || (txq->txing >= QUEUE_SIZE_TX - 2)) { - - /* try to free completed tx queue entries */ - fore200e_tx_irq(fore200e); - - if (*entry->status != STATUS_FREE) { - - spin_unlock_irqrestore(&fore200e->q_lock, flags); - - /* retry once again? */ - if (--retry > 0) { - udelay(50); - goto retry_here; - } - - atomic_inc(&vcc->stats->tx_err); - - fore200e->tx_sat++; - DPRINTK(2, "tx queue of device %s is saturated, PDU dropped - heartbeat is %08x\n", - fore200e->name, fore200e->cp_queues->heartbeat); - if (vcc->pop) { - vcc->pop(vcc, skb); - } - else { - dev_kfree_skb_any(skb); - } - - if (tx_copy) - kfree(data); - - return -ENOBUFS; - } - } - - entry->incarn = vc_map->incarn; - entry->vc_map = vc_map; - entry->skb = skb; - entry->data = tx_copy ? data : NULL; - - tpd = entry->tpd; - tpd->tsd[ 0 ].buffer = dma_map_single(fore200e->dev, data, tx_len, - DMA_TO_DEVICE); - if (dma_mapping_error(fore200e->dev, tpd->tsd[0].buffer)) { - if (tx_copy) - kfree(data); - spin_unlock_irqrestore(&fore200e->q_lock, flags); - return -ENOMEM; - } - tpd->tsd[ 0 ].length = tx_len; - - FORE200E_NEXT_ENTRY(txq->head, QUEUE_SIZE_TX); - txq->txing++; - - /* The dma_map call above implies a dma_sync so the device can use it, - * thus no explicit dma_sync call is necessary here. - */ - - DPRINTK(3, "tx on %d.%d.%d:%d, len = %u (%u)\n", - vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal), - tpd->tsd[0].length, skb_len); - - if (skb_len < fore200e_vcc->tx_min_pdu) - fore200e_vcc->tx_min_pdu = skb_len; - if (skb_len > fore200e_vcc->tx_max_pdu) - fore200e_vcc->tx_max_pdu = skb_len; - fore200e_vcc->tx_pdu++; - - /* set tx rate control information */ - tpd->rate.data_cells = fore200e_vcc->rate.data_cells; - tpd->rate.idle_cells = fore200e_vcc->rate.idle_cells; - - if (cell_header) { - tpd->atm_header.clp = (*cell_header & ATM_HDR_CLP); - tpd->atm_header.plt = (*cell_header & ATM_HDR_PTI_MASK) >> ATM_HDR_PTI_SHIFT; - tpd->atm_header.vci = (*cell_header & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT; - tpd->atm_header.vpi = (*cell_header & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT; - tpd->atm_header.gfc = (*cell_header & ATM_HDR_GFC_MASK) >> ATM_HDR_GFC_SHIFT; - } - else { - /* set the ATM header, common to all cells conveying the PDU */ - tpd->atm_header.clp = 0; - tpd->atm_header.plt = 0; - tpd->atm_header.vci = vcc->vci; - tpd->atm_header.vpi = vcc->vpi; - tpd->atm_header.gfc = 0; - } - - tpd->spec.length = tx_len; - tpd->spec.nseg = 1; - tpd->spec.aal = fore200e_atm2fore_aal(vcc->qos.aal); - tpd->spec.intr = 1; - - tpd_haddr.size = sizeof(struct tpd) / (1<tpd_dma >> TPD_HADDR_SHIFT; /* shift the address, as we are in a bitfield */ - - *entry->status = STATUS_PENDING; - fore200e->bus->write(*(u32*)&tpd_haddr, (u32 __iomem *)&entry->cp_entry->tpd_haddr); - - spin_unlock_irqrestore(&fore200e->q_lock, flags); - - return 0; -} - - -static int -fore200e_getstats(struct fore200e* fore200e) -{ - struct host_cmdq* cmdq = &fore200e->host_cmdq; - struct host_cmdq_entry* entry = &cmdq->host_entry[ cmdq->head ]; - struct stats_opcode opcode; - int ok; - u32 stats_dma_addr; - - if (fore200e->stats == NULL) { - fore200e->stats = kzalloc_obj(struct stats); - if (fore200e->stats == NULL) - return -ENOMEM; - } - - stats_dma_addr = dma_map_single(fore200e->dev, fore200e->stats, - sizeof(struct stats), DMA_FROM_DEVICE); - if (dma_mapping_error(fore200e->dev, stats_dma_addr)) - return -ENOMEM; - - FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD); - - opcode.opcode = OPCODE_GET_STATS; - opcode.pad = 0; - - fore200e->bus->write(stats_dma_addr, &entry->cp_entry->cmd.stats_block.stats_haddr); - - *entry->status = STATUS_PENDING; - - fore200e->bus->write(*(u32*)&opcode, (u32 __iomem *)&entry->cp_entry->cmd.stats_block.opcode); - - ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 400); - - *entry->status = STATUS_FREE; - - dma_unmap_single(fore200e->dev, stats_dma_addr, sizeof(struct stats), DMA_FROM_DEVICE); - - if (ok == 0) { - printk(FORE200E "unable to get statistics from device %s\n", fore200e->name); - return -EIO; - } - - return 0; -} - -#if 0 /* currently unused */ -static int -fore200e_get_oc3(struct fore200e* fore200e, struct oc3_regs* regs) -{ - struct host_cmdq* cmdq = &fore200e->host_cmdq; - struct host_cmdq_entry* entry = &cmdq->host_entry[ cmdq->head ]; - struct oc3_opcode opcode; - int ok; - u32 oc3_regs_dma_addr; - - oc3_regs_dma_addr = fore200e->bus->dma_map(fore200e, regs, sizeof(struct oc3_regs), DMA_FROM_DEVICE); - - FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD); - - opcode.opcode = OPCODE_GET_OC3; - opcode.reg = 0; - opcode.value = 0; - opcode.mask = 0; - - fore200e->bus->write(oc3_regs_dma_addr, &entry->cp_entry->cmd.oc3_block.regs_haddr); - - *entry->status = STATUS_PENDING; - - fore200e->bus->write(*(u32*)&opcode, (u32*)&entry->cp_entry->cmd.oc3_block.opcode); - - ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 400); - - *entry->status = STATUS_FREE; - - fore200e->bus->dma_unmap(fore200e, oc3_regs_dma_addr, sizeof(struct oc3_regs), DMA_FROM_DEVICE); - - if (ok == 0) { - printk(FORE200E "unable to get OC-3 regs of device %s\n", fore200e->name); - return -EIO; - } - - return 0; -} -#endif - - -static int -fore200e_set_oc3(struct fore200e* fore200e, u32 reg, u32 value, u32 mask) -{ - struct host_cmdq* cmdq = &fore200e->host_cmdq; - struct host_cmdq_entry* entry = &cmdq->host_entry[ cmdq->head ]; - struct oc3_opcode opcode; - int ok; - - DPRINTK(2, "set OC-3 reg = 0x%02x, value = 0x%02x, mask = 0x%02x\n", reg, value, mask); - - FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD); - - opcode.opcode = OPCODE_SET_OC3; - opcode.reg = reg; - opcode.value = value; - opcode.mask = mask; - - fore200e->bus->write(0, &entry->cp_entry->cmd.oc3_block.regs_haddr); - - *entry->status = STATUS_PENDING; - - fore200e->bus->write(*(u32*)&opcode, (u32 __iomem *)&entry->cp_entry->cmd.oc3_block.opcode); - - ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 400); - - *entry->status = STATUS_FREE; - - if (ok == 0) { - printk(FORE200E "unable to set OC-3 reg 0x%02x of device %s\n", reg, fore200e->name); - return -EIO; - } - - return 0; -} - - -static int -fore200e_setloop(struct fore200e* fore200e, int loop_mode) -{ - u32 mct_value, mct_mask; - int error; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch (loop_mode) { - - case ATM_LM_NONE: - mct_value = 0; - mct_mask = SUNI_MCT_DLE | SUNI_MCT_LLE; - break; - - case ATM_LM_LOC_PHY: - mct_value = mct_mask = SUNI_MCT_DLE; - break; - - case ATM_LM_RMT_PHY: - mct_value = mct_mask = SUNI_MCT_LLE; - break; - - default: - return -EINVAL; - } - - error = fore200e_set_oc3(fore200e, SUNI_MCT, mct_value, mct_mask); - if (error == 0) - fore200e->loop_mode = loop_mode; - - return error; -} - - -static int -fore200e_fetch_stats(struct fore200e* fore200e, struct sonet_stats __user *arg) -{ - struct sonet_stats tmp; - - if (fore200e_getstats(fore200e) < 0) - return -EIO; - - tmp.section_bip = be32_to_cpu(fore200e->stats->oc3.section_bip8_errors); - tmp.line_bip = be32_to_cpu(fore200e->stats->oc3.line_bip24_errors); - tmp.path_bip = be32_to_cpu(fore200e->stats->oc3.path_bip8_errors); - tmp.line_febe = be32_to_cpu(fore200e->stats->oc3.line_febe_errors); - tmp.path_febe = be32_to_cpu(fore200e->stats->oc3.path_febe_errors); - tmp.corr_hcs = be32_to_cpu(fore200e->stats->oc3.corr_hcs_errors); - tmp.uncorr_hcs = be32_to_cpu(fore200e->stats->oc3.ucorr_hcs_errors); - tmp.tx_cells = be32_to_cpu(fore200e->stats->aal0.cells_transmitted) + - be32_to_cpu(fore200e->stats->aal34.cells_transmitted) + - be32_to_cpu(fore200e->stats->aal5.cells_transmitted); - tmp.rx_cells = be32_to_cpu(fore200e->stats->aal0.cells_received) + - be32_to_cpu(fore200e->stats->aal34.cells_received) + - be32_to_cpu(fore200e->stats->aal5.cells_received); - - if (arg) - return copy_to_user(arg, &tmp, sizeof(struct sonet_stats)) ? -EFAULT : 0; - - return 0; -} - - -static int -fore200e_ioctl(struct atm_dev* dev, unsigned int cmd, void __user * arg) -{ - struct fore200e* fore200e = FORE200E_DEV(dev); - - DPRINTK(2, "ioctl cmd = 0x%x (%u), arg = 0x%p (%lu)\n", cmd, cmd, arg, (unsigned long)arg); - - switch (cmd) { - - case SONET_GETSTAT: - return fore200e_fetch_stats(fore200e, (struct sonet_stats __user *)arg); - - case SONET_GETDIAG: - return put_user(0, (int __user *)arg) ? -EFAULT : 0; - - case ATM_SETLOOP: - return fore200e_setloop(fore200e, (int)(unsigned long)arg); - - case ATM_GETLOOP: - return put_user(fore200e->loop_mode, (int __user *)arg) ? -EFAULT : 0; - - case ATM_QUERYLOOP: - return put_user(ATM_LM_LOC_PHY | ATM_LM_RMT_PHY, (int __user *)arg) ? -EFAULT : 0; - } - - return -ENOSYS; /* not implemented */ -} - - -static int -fore200e_change_qos(struct atm_vcc* vcc,struct atm_qos* qos, int flags) -{ - struct fore200e_vcc* fore200e_vcc = FORE200E_VCC(vcc); - struct fore200e* fore200e = FORE200E_DEV(vcc->dev); - - if (!test_bit(ATM_VF_READY, &vcc->flags)) { - DPRINTK(1, "VC %d.%d.%d not ready for QoS change\n", vcc->itf, vcc->vpi, vcc->vpi); - return -EINVAL; - } - - DPRINTK(2, "change_qos %d.%d.%d, " - "(tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; " - "rx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d), flags = 0x%x\n" - "available_cell_rate = %u", - vcc->itf, vcc->vpi, vcc->vci, - fore200e_traffic_class[ qos->txtp.traffic_class ], - qos->txtp.min_pcr, qos->txtp.max_pcr, qos->txtp.max_cdv, qos->txtp.max_sdu, - fore200e_traffic_class[ qos->rxtp.traffic_class ], - qos->rxtp.min_pcr, qos->rxtp.max_pcr, qos->rxtp.max_cdv, qos->rxtp.max_sdu, - flags, fore200e->available_cell_rate); - - if ((qos->txtp.traffic_class == ATM_CBR) && (qos->txtp.max_pcr > 0)) { - - mutex_lock(&fore200e->rate_mtx); - if (fore200e->available_cell_rate + vcc->qos.txtp.max_pcr < qos->txtp.max_pcr) { - mutex_unlock(&fore200e->rate_mtx); - return -EAGAIN; - } - - fore200e->available_cell_rate += vcc->qos.txtp.max_pcr; - fore200e->available_cell_rate -= qos->txtp.max_pcr; - - mutex_unlock(&fore200e->rate_mtx); - - memcpy(&vcc->qos, qos, sizeof(struct atm_qos)); - - /* update rate control parameters */ - fore200e_rate_ctrl(qos, &fore200e_vcc->rate); - - set_bit(ATM_VF_HASQOS, &vcc->flags); - - return 0; - } - - return -EINVAL; -} - - -static int fore200e_irq_request(struct fore200e *fore200e) -{ - if (request_irq(fore200e->irq, fore200e_interrupt, IRQF_SHARED, fore200e->name, fore200e->atm_dev) < 0) { - - printk(FORE200E "unable to reserve IRQ %s for device %s\n", - fore200e_irq_itoa(fore200e->irq), fore200e->name); - return -EBUSY; - } - - printk(FORE200E "IRQ %s reserved for device %s\n", - fore200e_irq_itoa(fore200e->irq), fore200e->name); - -#ifdef FORE200E_USE_TASKLET - tasklet_init(&fore200e->tx_tasklet, fore200e_tx_tasklet, (unsigned long)fore200e); - tasklet_init(&fore200e->rx_tasklet, fore200e_rx_tasklet, (unsigned long)fore200e); -#endif - - fore200e->state = FORE200E_STATE_IRQ; - return 0; -} - - -static int fore200e_get_esi(struct fore200e *fore200e) -{ - struct prom_data* prom = kzalloc_obj(struct prom_data); - int ok, i; - - if (!prom) - return -ENOMEM; - - ok = fore200e->bus->prom_read(fore200e, prom); - if (ok < 0) { - kfree(prom); - return -EBUSY; - } - - printk(FORE200E "device %s, rev. %c, S/N: %d, ESI: %pM\n", - fore200e->name, - (prom->hw_revision & 0xFF) + '@', /* probably meaningless with SBA boards */ - prom->serial_number & 0xFFFF, &prom->mac_addr[2]); - - for (i = 0; i < ESI_LEN; i++) { - fore200e->esi[ i ] = fore200e->atm_dev->esi[ i ] = prom->mac_addr[ i + 2 ]; - } - - kfree(prom); - - return 0; -} - - -static int fore200e_alloc_rx_buf(struct fore200e *fore200e) -{ - int scheme, magn, nbr, size, i; - - struct host_bsq* bsq; - struct buffer* buffer; - - for (scheme = 0; scheme < BUFFER_SCHEME_NBR; scheme++) { - for (magn = 0; magn < BUFFER_MAGN_NBR; magn++) { - - bsq = &fore200e->host_bsq[ scheme ][ magn ]; - - nbr = fore200e_rx_buf_nbr[ scheme ][ magn ]; - size = fore200e_rx_buf_size[ scheme ][ magn ]; - - DPRINTK(2, "rx buffers %d / %d are being allocated\n", scheme, magn); - - /* allocate the array of receive buffers */ - buffer = bsq->buffer = kzalloc_objs(struct buffer, nbr); - - if (buffer == NULL) - return -ENOMEM; - - bsq->freebuf = NULL; - - for (i = 0; i < nbr; i++) { - - buffer[ i ].scheme = scheme; - buffer[ i ].magn = magn; -#ifdef FORE200E_BSQ_DEBUG - buffer[ i ].index = i; - buffer[ i ].supplied = 0; -#endif - - /* allocate the receive buffer body */ - if (fore200e_chunk_alloc(fore200e, - &buffer[ i ].data, size, fore200e->bus->buffer_alignment, - DMA_FROM_DEVICE) < 0) { - - while (i > 0) - fore200e_chunk_free(fore200e, &buffer[ --i ].data); - kfree(buffer); - - return -ENOMEM; - } - - /* insert the buffer into the free buffer list */ - buffer[ i ].next = bsq->freebuf; - bsq->freebuf = &buffer[ i ]; - } - /* all the buffers are free, initially */ - bsq->freebuf_count = nbr; - -#ifdef FORE200E_BSQ_DEBUG - bsq_audit(3, bsq, scheme, magn); -#endif - } - } - - fore200e->state = FORE200E_STATE_ALLOC_BUF; - return 0; -} - - -static int fore200e_init_bs_queue(struct fore200e *fore200e) -{ - int scheme, magn, i; - - struct host_bsq* bsq; - struct cp_bsq_entry __iomem * cp_entry; - - for (scheme = 0; scheme < BUFFER_SCHEME_NBR; scheme++) { - for (magn = 0; magn < BUFFER_MAGN_NBR; magn++) { - - DPRINTK(2, "buffer supply queue %d / %d is being initialized\n", scheme, magn); - - bsq = &fore200e->host_bsq[ scheme ][ magn ]; - - /* allocate and align the array of status words */ - if (fore200e_dma_chunk_alloc(fore200e, - &bsq->status, - sizeof(enum status), - QUEUE_SIZE_BS, - fore200e->bus->status_alignment) < 0) { - return -ENOMEM; - } - - /* allocate and align the array of receive buffer descriptors */ - if (fore200e_dma_chunk_alloc(fore200e, - &bsq->rbd_block, - sizeof(struct rbd_block), - QUEUE_SIZE_BS, - fore200e->bus->descr_alignment) < 0) { - - fore200e_dma_chunk_free(fore200e, &bsq->status); - return -ENOMEM; - } - - /* get the base address of the cp resident buffer supply queue entries */ - cp_entry = fore200e->virt_base + - fore200e->bus->read(&fore200e->cp_queues->cp_bsq[ scheme ][ magn ]); - - /* fill the host resident and cp resident buffer supply queue entries */ - for (i = 0; i < QUEUE_SIZE_BS; i++) { - - bsq->host_entry[ i ].status = - FORE200E_INDEX(bsq->status.align_addr, enum status, i); - bsq->host_entry[ i ].rbd_block = - FORE200E_INDEX(bsq->rbd_block.align_addr, struct rbd_block, i); - bsq->host_entry[ i ].rbd_block_dma = - FORE200E_DMA_INDEX(bsq->rbd_block.dma_addr, struct rbd_block, i); - bsq->host_entry[ i ].cp_entry = &cp_entry[ i ]; - - *bsq->host_entry[ i ].status = STATUS_FREE; - - fore200e->bus->write(FORE200E_DMA_INDEX(bsq->status.dma_addr, enum status, i), - &cp_entry[ i ].status_haddr); - } - } - } - - fore200e->state = FORE200E_STATE_INIT_BSQ; - return 0; -} - - -static int fore200e_init_rx_queue(struct fore200e *fore200e) -{ - struct host_rxq* rxq = &fore200e->host_rxq; - struct cp_rxq_entry __iomem * cp_entry; - int i; - - DPRINTK(2, "receive queue is being initialized\n"); - - /* allocate and align the array of status words */ - if (fore200e_dma_chunk_alloc(fore200e, - &rxq->status, - sizeof(enum status), - QUEUE_SIZE_RX, - fore200e->bus->status_alignment) < 0) { - return -ENOMEM; - } - - /* allocate and align the array of receive PDU descriptors */ - if (fore200e_dma_chunk_alloc(fore200e, - &rxq->rpd, - sizeof(struct rpd), - QUEUE_SIZE_RX, - fore200e->bus->descr_alignment) < 0) { - - fore200e_dma_chunk_free(fore200e, &rxq->status); - return -ENOMEM; - } - - /* get the base address of the cp resident rx queue entries */ - cp_entry = fore200e->virt_base + fore200e->bus->read(&fore200e->cp_queues->cp_rxq); - - /* fill the host resident and cp resident rx entries */ - for (i=0; i < QUEUE_SIZE_RX; i++) { - - rxq->host_entry[ i ].status = - FORE200E_INDEX(rxq->status.align_addr, enum status, i); - rxq->host_entry[ i ].rpd = - FORE200E_INDEX(rxq->rpd.align_addr, struct rpd, i); - rxq->host_entry[ i ].rpd_dma = - FORE200E_DMA_INDEX(rxq->rpd.dma_addr, struct rpd, i); - rxq->host_entry[ i ].cp_entry = &cp_entry[ i ]; - - *rxq->host_entry[ i ].status = STATUS_FREE; - - fore200e->bus->write(FORE200E_DMA_INDEX(rxq->status.dma_addr, enum status, i), - &cp_entry[ i ].status_haddr); - - fore200e->bus->write(FORE200E_DMA_INDEX(rxq->rpd.dma_addr, struct rpd, i), - &cp_entry[ i ].rpd_haddr); - } - - /* set the head entry of the queue */ - rxq->head = 0; - - fore200e->state = FORE200E_STATE_INIT_RXQ; - return 0; -} - - -static int fore200e_init_tx_queue(struct fore200e *fore200e) -{ - struct host_txq* txq = &fore200e->host_txq; - struct cp_txq_entry __iomem * cp_entry; - int i; - - DPRINTK(2, "transmit queue is being initialized\n"); - - /* allocate and align the array of status words */ - if (fore200e_dma_chunk_alloc(fore200e, - &txq->status, - sizeof(enum status), - QUEUE_SIZE_TX, - fore200e->bus->status_alignment) < 0) { - return -ENOMEM; - } - - /* allocate and align the array of transmit PDU descriptors */ - if (fore200e_dma_chunk_alloc(fore200e, - &txq->tpd, - sizeof(struct tpd), - QUEUE_SIZE_TX, - fore200e->bus->descr_alignment) < 0) { - - fore200e_dma_chunk_free(fore200e, &txq->status); - return -ENOMEM; - } - - /* get the base address of the cp resident tx queue entries */ - cp_entry = fore200e->virt_base + fore200e->bus->read(&fore200e->cp_queues->cp_txq); - - /* fill the host resident and cp resident tx entries */ - for (i=0; i < QUEUE_SIZE_TX; i++) { - - txq->host_entry[ i ].status = - FORE200E_INDEX(txq->status.align_addr, enum status, i); - txq->host_entry[ i ].tpd = - FORE200E_INDEX(txq->tpd.align_addr, struct tpd, i); - txq->host_entry[ i ].tpd_dma = - FORE200E_DMA_INDEX(txq->tpd.dma_addr, struct tpd, i); - txq->host_entry[ i ].cp_entry = &cp_entry[ i ]; - - *txq->host_entry[ i ].status = STATUS_FREE; - - fore200e->bus->write(FORE200E_DMA_INDEX(txq->status.dma_addr, enum status, i), - &cp_entry[ i ].status_haddr); - - /* although there is a one-to-one mapping of tx queue entries and tpds, - we do not write here the DMA (physical) base address of each tpd into - the related cp resident entry, because the cp relies on this write - operation to detect that a new pdu has been submitted for tx */ - } - - /* set the head and tail entries of the queue */ - txq->head = 0; - txq->tail = 0; - - fore200e->state = FORE200E_STATE_INIT_TXQ; - return 0; -} - - -static int fore200e_init_cmd_queue(struct fore200e *fore200e) -{ - struct host_cmdq* cmdq = &fore200e->host_cmdq; - struct cp_cmdq_entry __iomem * cp_entry; - int i; - - DPRINTK(2, "command queue is being initialized\n"); - - /* allocate and align the array of status words */ - if (fore200e_dma_chunk_alloc(fore200e, - &cmdq->status, - sizeof(enum status), - QUEUE_SIZE_CMD, - fore200e->bus->status_alignment) < 0) { - return -ENOMEM; - } - - /* get the base address of the cp resident cmd queue entries */ - cp_entry = fore200e->virt_base + fore200e->bus->read(&fore200e->cp_queues->cp_cmdq); - - /* fill the host resident and cp resident cmd entries */ - for (i=0; i < QUEUE_SIZE_CMD; i++) { - - cmdq->host_entry[ i ].status = - FORE200E_INDEX(cmdq->status.align_addr, enum status, i); - cmdq->host_entry[ i ].cp_entry = &cp_entry[ i ]; - - *cmdq->host_entry[ i ].status = STATUS_FREE; - - fore200e->bus->write(FORE200E_DMA_INDEX(cmdq->status.dma_addr, enum status, i), - &cp_entry[ i ].status_haddr); - } - - /* set the head entry of the queue */ - cmdq->head = 0; - - fore200e->state = FORE200E_STATE_INIT_CMDQ; - return 0; -} - - -static void fore200e_param_bs_queue(struct fore200e *fore200e, - enum buffer_scheme scheme, - enum buffer_magn magn, int queue_length, - int pool_size, int supply_blksize) -{ - struct bs_spec __iomem * bs_spec = &fore200e->cp_queues->init.bs_spec[ scheme ][ magn ]; - - fore200e->bus->write(queue_length, &bs_spec->queue_length); - fore200e->bus->write(fore200e_rx_buf_size[ scheme ][ magn ], &bs_spec->buffer_size); - fore200e->bus->write(pool_size, &bs_spec->pool_size); - fore200e->bus->write(supply_blksize, &bs_spec->supply_blksize); -} - - -static int fore200e_initialize(struct fore200e *fore200e) -{ - struct cp_queues __iomem * cpq; - int ok, scheme, magn; - - DPRINTK(2, "device %s being initialized\n", fore200e->name); - - mutex_init(&fore200e->rate_mtx); - spin_lock_init(&fore200e->q_lock); - - cpq = fore200e->cp_queues = fore200e->virt_base + FORE200E_CP_QUEUES_OFFSET; - - /* enable cp to host interrupts */ - fore200e->bus->write(1, &cpq->imask); - - if (fore200e->bus->irq_enable) - fore200e->bus->irq_enable(fore200e); - - fore200e->bus->write(NBR_CONNECT, &cpq->init.num_connect); - - fore200e->bus->write(QUEUE_SIZE_CMD, &cpq->init.cmd_queue_len); - fore200e->bus->write(QUEUE_SIZE_RX, &cpq->init.rx_queue_len); - fore200e->bus->write(QUEUE_SIZE_TX, &cpq->init.tx_queue_len); - - fore200e->bus->write(RSD_EXTENSION, &cpq->init.rsd_extension); - fore200e->bus->write(TSD_EXTENSION, &cpq->init.tsd_extension); - - for (scheme = 0; scheme < BUFFER_SCHEME_NBR; scheme++) - for (magn = 0; magn < BUFFER_MAGN_NBR; magn++) - fore200e_param_bs_queue(fore200e, scheme, magn, - QUEUE_SIZE_BS, - fore200e_rx_buf_nbr[ scheme ][ magn ], - RBD_BLK_SIZE); - - /* issue the initialize command */ - fore200e->bus->write(STATUS_PENDING, &cpq->init.status); - fore200e->bus->write(OPCODE_INITIALIZE, &cpq->init.opcode); - - ok = fore200e_io_poll(fore200e, &cpq->init.status, STATUS_COMPLETE, 3000); - if (ok == 0) { - printk(FORE200E "device %s initialization failed\n", fore200e->name); - return -ENODEV; - } - - printk(FORE200E "device %s initialized\n", fore200e->name); - - fore200e->state = FORE200E_STATE_INITIALIZE; - return 0; -} - - -static void fore200e_monitor_putc(struct fore200e *fore200e, char c) -{ - struct cp_monitor __iomem * monitor = fore200e->cp_monitor; - -#if 0 - printk("%c", c); -#endif - fore200e->bus->write(((u32) c) | FORE200E_CP_MONITOR_UART_AVAIL, &monitor->soft_uart.send); -} - - -static int fore200e_monitor_getc(struct fore200e *fore200e) -{ - struct cp_monitor __iomem * monitor = fore200e->cp_monitor; - unsigned long timeout = jiffies + msecs_to_jiffies(50); - int c; - - while (time_before(jiffies, timeout)) { - - c = (int) fore200e->bus->read(&monitor->soft_uart.recv); - - if (c & FORE200E_CP_MONITOR_UART_AVAIL) { - - fore200e->bus->write(FORE200E_CP_MONITOR_UART_FREE, &monitor->soft_uart.recv); -#if 0 - printk("%c", c & 0xFF); -#endif - return c & 0xFF; - } - } - - return -1; -} - - -static void fore200e_monitor_puts(struct fore200e *fore200e, char *str) -{ - while (*str) { - - /* the i960 monitor doesn't accept any new character if it has something to say */ - while (fore200e_monitor_getc(fore200e) >= 0); - - fore200e_monitor_putc(fore200e, *str++); - } - - while (fore200e_monitor_getc(fore200e) >= 0); -} - -#ifdef __LITTLE_ENDIAN -#define FW_EXT ".bin" -#else -#define FW_EXT "_ecd.bin2" -#endif - -static int fore200e_load_and_start_fw(struct fore200e *fore200e) -{ - const struct firmware *firmware; - const struct fw_header *fw_header; - const __le32 *fw_data; - u32 fw_size; - u32 __iomem *load_addr; - char buf[48]; - int err; - - sprintf(buf, "%s%s", fore200e->bus->proc_name, FW_EXT); - if ((err = request_firmware(&firmware, buf, fore200e->dev)) < 0) { - printk(FORE200E "problem loading firmware image %s\n", fore200e->bus->model_name); - return err; - } - - fw_data = (const __le32 *)firmware->data; - fw_size = firmware->size / sizeof(u32); - fw_header = (const struct fw_header *)firmware->data; - load_addr = fore200e->virt_base + le32_to_cpu(fw_header->load_offset); - - DPRINTK(2, "device %s firmware being loaded at 0x%p (%d words)\n", - fore200e->name, load_addr, fw_size); - - if (le32_to_cpu(fw_header->magic) != FW_HEADER_MAGIC) { - printk(FORE200E "corrupted %s firmware image\n", fore200e->bus->model_name); - goto release; - } - - for (; fw_size--; fw_data++, load_addr++) - fore200e->bus->write(le32_to_cpu(*fw_data), load_addr); - - DPRINTK(2, "device %s firmware being started\n", fore200e->name); - -#if defined(__sparc_v9__) - /* reported to be required by SBA cards on some sparc64 hosts */ - fore200e_spin(100); -#endif - - sprintf(buf, "\rgo %x\r", le32_to_cpu(fw_header->start_offset)); - fore200e_monitor_puts(fore200e, buf); - - if (fore200e_io_poll(fore200e, &fore200e->cp_monitor->bstat, BSTAT_CP_RUNNING, 1000) == 0) { - printk(FORE200E "device %s firmware didn't start\n", fore200e->name); - goto release; - } - - printk(FORE200E "device %s firmware started\n", fore200e->name); - - fore200e->state = FORE200E_STATE_START_FW; - err = 0; - -release: - release_firmware(firmware); - return err; -} - - -static int fore200e_register(struct fore200e *fore200e, struct device *parent) -{ - struct atm_dev* atm_dev; - - DPRINTK(2, "device %s being registered\n", fore200e->name); - - atm_dev = atm_dev_register(fore200e->bus->proc_name, parent, &fore200e_ops, - -1, NULL); - if (atm_dev == NULL) { - printk(FORE200E "unable to register device %s\n", fore200e->name); - return -ENODEV; - } - - atm_dev->dev_data = fore200e; - fore200e->atm_dev = atm_dev; - - atm_dev->ci_range.vpi_bits = FORE200E_VPI_BITS; - atm_dev->ci_range.vci_bits = FORE200E_VCI_BITS; - - fore200e->available_cell_rate = ATM_OC3_PCR; - - fore200e->state = FORE200E_STATE_REGISTER; - return 0; -} - - -static int fore200e_init(struct fore200e *fore200e, struct device *parent) -{ - if (fore200e_register(fore200e, parent) < 0) - return -ENODEV; - - if (fore200e->bus->configure(fore200e) < 0) - return -ENODEV; - - if (fore200e->bus->map(fore200e) < 0) - return -ENODEV; - - if (fore200e_reset(fore200e, 1) < 0) - return -ENODEV; - - if (fore200e_load_and_start_fw(fore200e) < 0) - return -ENODEV; - - if (fore200e_initialize(fore200e) < 0) - return -ENODEV; - - if (fore200e_init_cmd_queue(fore200e) < 0) - return -ENOMEM; - - if (fore200e_init_tx_queue(fore200e) < 0) - return -ENOMEM; - - if (fore200e_init_rx_queue(fore200e) < 0) - return -ENOMEM; - - if (fore200e_init_bs_queue(fore200e) < 0) - return -ENOMEM; - - if (fore200e_alloc_rx_buf(fore200e) < 0) - return -ENOMEM; - - if (fore200e_get_esi(fore200e) < 0) - return -EIO; - - if (fore200e_irq_request(fore200e) < 0) - return -EBUSY; - - fore200e_supply(fore200e); - - /* all done, board initialization is now complete */ - fore200e->state = FORE200E_STATE_COMPLETE; - return 0; -} - -#ifdef CONFIG_SBUS -static int fore200e_sba_probe(struct platform_device *op) -{ - struct fore200e *fore200e; - static int index = 0; - int err; - - fore200e = kzalloc_obj(struct fore200e); - if (!fore200e) - return -ENOMEM; - - fore200e->bus = &fore200e_sbus_ops; - fore200e->dev = &op->dev; - fore200e->irq = op->archdata.irqs[0]; - fore200e->phys_base = op->resource[0].start; - - sprintf(fore200e->name, "SBA-200E-%d", index); - - err = fore200e_init(fore200e, &op->dev); - if (err < 0) { - fore200e_shutdown(fore200e); - kfree(fore200e); - return err; - } - - index++; - dev_set_drvdata(&op->dev, fore200e); - - return 0; -} - -static void fore200e_sba_remove(struct platform_device *op) -{ - struct fore200e *fore200e = dev_get_drvdata(&op->dev); - - fore200e_shutdown(fore200e); - kfree(fore200e); -} - -static const struct of_device_id fore200e_sba_match[] = { - { - .name = SBA200E_PROM_NAME, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, fore200e_sba_match); - -static struct platform_driver fore200e_sba_driver = { - .driver = { - .name = "fore_200e", - .of_match_table = fore200e_sba_match, - }, - .probe = fore200e_sba_probe, - .remove = fore200e_sba_remove, -}; -#endif - -#ifdef CONFIG_PCI -static int fore200e_pca_detect(struct pci_dev *pci_dev, - const struct pci_device_id *pci_ent) -{ - struct fore200e* fore200e; - int err = 0; - static int index = 0; - - if (pci_enable_device(pci_dev)) { - err = -EINVAL; - goto out; - } - - if (dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32))) { - err = -EINVAL; - goto out; - } - - fore200e = kzalloc_obj(struct fore200e); - if (fore200e == NULL) { - err = -ENOMEM; - goto out_disable; - } - - fore200e->bus = &fore200e_pci_ops; - fore200e->dev = &pci_dev->dev; - fore200e->irq = pci_dev->irq; - fore200e->phys_base = pci_resource_start(pci_dev, 0); - - sprintf(fore200e->name, "PCA-200E-%d", index - 1); - - pci_set_master(pci_dev); - - printk(FORE200E "device PCA-200E found at 0x%lx, IRQ %s\n", - fore200e->phys_base, fore200e_irq_itoa(fore200e->irq)); - - sprintf(fore200e->name, "PCA-200E-%d", index); - - err = fore200e_init(fore200e, &pci_dev->dev); - if (err < 0) { - fore200e_shutdown(fore200e); - goto out_free; - } - - ++index; - pci_set_drvdata(pci_dev, fore200e); - -out: - return err; - -out_free: - kfree(fore200e); -out_disable: - pci_disable_device(pci_dev); - goto out; -} - - -static void fore200e_pca_remove_one(struct pci_dev *pci_dev) -{ - struct fore200e *fore200e; - - fore200e = pci_get_drvdata(pci_dev); - - fore200e_shutdown(fore200e); - kfree(fore200e); - pci_disable_device(pci_dev); -} - - -static const struct pci_device_id fore200e_pca_tbl[] = { - { PCI_VENDOR_ID_FORE, PCI_DEVICE_ID_FORE_PCA200E, PCI_ANY_ID, PCI_ANY_ID }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, fore200e_pca_tbl); - -static struct pci_driver fore200e_pca_driver = { - .name = "fore_200e", - .probe = fore200e_pca_detect, - .remove = fore200e_pca_remove_one, - .id_table = fore200e_pca_tbl, -}; -#endif - -static int __init fore200e_module_init(void) -{ - int err = 0; - - printk(FORE200E "FORE Systems 200E-series ATM driver - version " FORE200E_VERSION "\n"); - -#ifdef CONFIG_SBUS - err = platform_driver_register(&fore200e_sba_driver); - if (err) - return err; -#endif - -#ifdef CONFIG_PCI - err = pci_register_driver(&fore200e_pca_driver); -#endif - -#ifdef CONFIG_SBUS - if (err) - platform_driver_unregister(&fore200e_sba_driver); -#endif - - return err; -} - -static void __exit fore200e_module_cleanup(void) -{ -#ifdef CONFIG_PCI - pci_unregister_driver(&fore200e_pca_driver); -#endif -#ifdef CONFIG_SBUS - platform_driver_unregister(&fore200e_sba_driver); -#endif -} - -static int -fore200e_proc_read(struct atm_dev *dev, loff_t* pos, char* page) -{ - struct fore200e* fore200e = FORE200E_DEV(dev); - struct fore200e_vcc* fore200e_vcc; - struct atm_vcc* vcc; - int i, len, left = *pos; - unsigned long flags; - - if (!left--) { - - if (fore200e_getstats(fore200e) < 0) - return -EIO; - - len = sprintf(page,"\n" - " device:\n" - " internal name:\t\t%s\n", fore200e->name); - - /* print bus-specific information */ - if (fore200e->bus->proc_read) - len += fore200e->bus->proc_read(fore200e, page + len); - - len += sprintf(page + len, - " interrupt line:\t\t%s\n" - " physical base address:\t0x%p\n" - " virtual base address:\t0x%p\n" - " factory address (ESI):\t%pM\n" - " board serial number:\t\t%d\n\n", - fore200e_irq_itoa(fore200e->irq), - (void*)fore200e->phys_base, - fore200e->virt_base, - fore200e->esi, - fore200e->esi[4] * 256 + fore200e->esi[5]); - - return len; - } - - if (!left--) - return sprintf(page, - " free small bufs, scheme 1:\t%d\n" - " free large bufs, scheme 1:\t%d\n" - " free small bufs, scheme 2:\t%d\n" - " free large bufs, scheme 2:\t%d\n", - fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_SMALL ].freebuf_count, - fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_LARGE ].freebuf_count, - fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_SMALL ].freebuf_count, - fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_LARGE ].freebuf_count); - - if (!left--) { - u32 hb = fore200e->bus->read(&fore200e->cp_queues->heartbeat); - - len = sprintf(page,"\n\n" - " cell processor:\n" - " heartbeat state:\t\t"); - - if (hb >> 16 != 0xDEAD) - len += sprintf(page + len, "0x%08x\n", hb); - else - len += sprintf(page + len, "*** FATAL ERROR %04x ***\n", hb & 0xFFFF); - - return len; - } - - if (!left--) { - static const char* media_name[] = { - "unshielded twisted pair", - "multimode optical fiber ST", - "multimode optical fiber SC", - "single-mode optical fiber ST", - "single-mode optical fiber SC", - "unknown" - }; - - static const char* oc3_mode[] = { - "normal operation", - "diagnostic loopback", - "line loopback", - "unknown" - }; - - u32 fw_release = fore200e->bus->read(&fore200e->cp_queues->fw_release); - u32 mon960_release = fore200e->bus->read(&fore200e->cp_queues->mon960_release); - u32 oc3_revision = fore200e->bus->read(&fore200e->cp_queues->oc3_revision); - u32 media_index = FORE200E_MEDIA_INDEX(fore200e->bus->read(&fore200e->cp_queues->media_type)); - u32 oc3_index; - - if (media_index > 4) - media_index = 5; - - switch (fore200e->loop_mode) { - case ATM_LM_NONE: oc3_index = 0; - break; - case ATM_LM_LOC_PHY: oc3_index = 1; - break; - case ATM_LM_RMT_PHY: oc3_index = 2; - break; - default: oc3_index = 3; - } - - return sprintf(page, - " firmware release:\t\t%d.%d.%d\n" - " monitor release:\t\t%d.%d\n" - " media type:\t\t\t%s\n" - " OC-3 revision:\t\t0x%x\n" - " OC-3 mode:\t\t\t%s", - fw_release >> 16, fw_release << 16 >> 24, fw_release << 24 >> 24, - mon960_release >> 16, mon960_release << 16 >> 16, - media_name[ media_index ], - oc3_revision, - oc3_mode[ oc3_index ]); - } - - if (!left--) { - struct cp_monitor __iomem * cp_monitor = fore200e->cp_monitor; - - return sprintf(page, - "\n\n" - " monitor:\n" - " version number:\t\t%d\n" - " boot status word:\t\t0x%08x\n", - fore200e->bus->read(&cp_monitor->mon_version), - fore200e->bus->read(&cp_monitor->bstat)); - } - - if (!left--) - return sprintf(page, - "\n" - " device statistics:\n" - " 4b5b:\n" - " crc_header_errors:\t\t%10u\n" - " framing_errors:\t\t%10u\n", - be32_to_cpu(fore200e->stats->phy.crc_header_errors), - be32_to_cpu(fore200e->stats->phy.framing_errors)); - - if (!left--) - return sprintf(page, "\n" - " OC-3:\n" - " section_bip8_errors:\t%10u\n" - " path_bip8_errors:\t\t%10u\n" - " line_bip24_errors:\t\t%10u\n" - " line_febe_errors:\t\t%10u\n" - " path_febe_errors:\t\t%10u\n" - " corr_hcs_errors:\t\t%10u\n" - " ucorr_hcs_errors:\t\t%10u\n", - be32_to_cpu(fore200e->stats->oc3.section_bip8_errors), - be32_to_cpu(fore200e->stats->oc3.path_bip8_errors), - be32_to_cpu(fore200e->stats->oc3.line_bip24_errors), - be32_to_cpu(fore200e->stats->oc3.line_febe_errors), - be32_to_cpu(fore200e->stats->oc3.path_febe_errors), - be32_to_cpu(fore200e->stats->oc3.corr_hcs_errors), - be32_to_cpu(fore200e->stats->oc3.ucorr_hcs_errors)); - - if (!left--) - return sprintf(page,"\n" - " ATM:\t\t\t\t cells\n" - " TX:\t\t\t%10u\n" - " RX:\t\t\t%10u\n" - " vpi out of range:\t\t%10u\n" - " vpi no conn:\t\t%10u\n" - " vci out of range:\t\t%10u\n" - " vci no conn:\t\t%10u\n", - be32_to_cpu(fore200e->stats->atm.cells_transmitted), - be32_to_cpu(fore200e->stats->atm.cells_received), - be32_to_cpu(fore200e->stats->atm.vpi_bad_range), - be32_to_cpu(fore200e->stats->atm.vpi_no_conn), - be32_to_cpu(fore200e->stats->atm.vci_bad_range), - be32_to_cpu(fore200e->stats->atm.vci_no_conn)); - - if (!left--) - return sprintf(page,"\n" - " AAL0:\t\t\t cells\n" - " TX:\t\t\t%10u\n" - " RX:\t\t\t%10u\n" - " dropped:\t\t\t%10u\n", - be32_to_cpu(fore200e->stats->aal0.cells_transmitted), - be32_to_cpu(fore200e->stats->aal0.cells_received), - be32_to_cpu(fore200e->stats->aal0.cells_dropped)); - - if (!left--) - return sprintf(page,"\n" - " AAL3/4:\n" - " SAR sublayer:\t\t cells\n" - " TX:\t\t\t%10u\n" - " RX:\t\t\t%10u\n" - " dropped:\t\t\t%10u\n" - " CRC errors:\t\t%10u\n" - " protocol errors:\t\t%10u\n\n" - " CS sublayer:\t\t PDUs\n" - " TX:\t\t\t%10u\n" - " RX:\t\t\t%10u\n" - " dropped:\t\t\t%10u\n" - " protocol errors:\t\t%10u\n", - be32_to_cpu(fore200e->stats->aal34.cells_transmitted), - be32_to_cpu(fore200e->stats->aal34.cells_received), - be32_to_cpu(fore200e->stats->aal34.cells_dropped), - be32_to_cpu(fore200e->stats->aal34.cells_crc_errors), - be32_to_cpu(fore200e->stats->aal34.cells_protocol_errors), - be32_to_cpu(fore200e->stats->aal34.cspdus_transmitted), - be32_to_cpu(fore200e->stats->aal34.cspdus_received), - be32_to_cpu(fore200e->stats->aal34.cspdus_dropped), - be32_to_cpu(fore200e->stats->aal34.cspdus_protocol_errors)); - - if (!left--) - return sprintf(page,"\n" - " AAL5:\n" - " SAR sublayer:\t\t cells\n" - " TX:\t\t\t%10u\n" - " RX:\t\t\t%10u\n" - " dropped:\t\t\t%10u\n" - " congestions:\t\t%10u\n\n" - " CS sublayer:\t\t PDUs\n" - " TX:\t\t\t%10u\n" - " RX:\t\t\t%10u\n" - " dropped:\t\t\t%10u\n" - " CRC errors:\t\t%10u\n" - " protocol errors:\t\t%10u\n", - be32_to_cpu(fore200e->stats->aal5.cells_transmitted), - be32_to_cpu(fore200e->stats->aal5.cells_received), - be32_to_cpu(fore200e->stats->aal5.cells_dropped), - be32_to_cpu(fore200e->stats->aal5.congestion_experienced), - be32_to_cpu(fore200e->stats->aal5.cspdus_transmitted), - be32_to_cpu(fore200e->stats->aal5.cspdus_received), - be32_to_cpu(fore200e->stats->aal5.cspdus_dropped), - be32_to_cpu(fore200e->stats->aal5.cspdus_crc_errors), - be32_to_cpu(fore200e->stats->aal5.cspdus_protocol_errors)); - - if (!left--) - return sprintf(page,"\n" - " AUX:\t\t allocation failures\n" - " small b1:\t\t\t%10u\n" - " large b1:\t\t\t%10u\n" - " small b2:\t\t\t%10u\n" - " large b2:\t\t\t%10u\n" - " RX PDUs:\t\t\t%10u\n" - " TX PDUs:\t\t\t%10lu\n", - be32_to_cpu(fore200e->stats->aux.small_b1_failed), - be32_to_cpu(fore200e->stats->aux.large_b1_failed), - be32_to_cpu(fore200e->stats->aux.small_b2_failed), - be32_to_cpu(fore200e->stats->aux.large_b2_failed), - be32_to_cpu(fore200e->stats->aux.rpd_alloc_failed), - fore200e->tx_sat); - - if (!left--) - return sprintf(page,"\n" - " receive carrier:\t\t\t%s\n", - fore200e->stats->aux.receive_carrier ? "ON" : "OFF!"); - - if (!left--) { - return sprintf(page,"\n" - " VCCs:\n address VPI VCI AAL " - "TX PDUs TX min/max size RX PDUs RX min/max size\n"); - } - - for (i = 0; i < NBR_CONNECT; i++) { - - vcc = fore200e->vc_map[i].vcc; - - if (vcc == NULL) - continue; - - spin_lock_irqsave(&fore200e->q_lock, flags); - - if (vcc && test_bit(ATM_VF_READY, &vcc->flags) && !left--) { - - fore200e_vcc = FORE200E_VCC(vcc); - ASSERT(fore200e_vcc); - - len = sprintf(page, - " %pK %03d %05d %1d %09lu %05d/%05d %09lu %05d/%05d\n", - vcc, - vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal), - fore200e_vcc->tx_pdu, - fore200e_vcc->tx_min_pdu > 0xFFFF ? 0 : fore200e_vcc->tx_min_pdu, - fore200e_vcc->tx_max_pdu, - fore200e_vcc->rx_pdu, - fore200e_vcc->rx_min_pdu > 0xFFFF ? 0 : fore200e_vcc->rx_min_pdu, - fore200e_vcc->rx_max_pdu); - - spin_unlock_irqrestore(&fore200e->q_lock, flags); - return len; - } - - spin_unlock_irqrestore(&fore200e->q_lock, flags); - } - - return 0; -} - -module_init(fore200e_module_init); -module_exit(fore200e_module_cleanup); - - -static const struct atmdev_ops fore200e_ops = { - .open = fore200e_open, - .close = fore200e_close, - .ioctl = fore200e_ioctl, - .send = fore200e_send, - .change_qos = fore200e_change_qos, - .proc_read = fore200e_proc_read, - .owner = THIS_MODULE -}; - -MODULE_LICENSE("GPL"); -#ifdef CONFIG_PCI -#ifdef __LITTLE_ENDIAN__ -MODULE_FIRMWARE("pca200e.bin"); -#else -MODULE_FIRMWARE("pca200e_ecd.bin2"); -#endif -#endif /* CONFIG_PCI */ -#ifdef CONFIG_SBUS -MODULE_FIRMWARE("sba200e_ecd.bin2"); -#endif diff --git a/drivers/atm/fore200e.h b/drivers/atm/fore200e.h deleted file mode 100644 index 5d95fe9fd836..000000000000 --- a/drivers/atm/fore200e.h +++ /dev/null @@ -1,973 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _FORE200E_H -#define _FORE200E_H - -#ifdef __KERNEL__ - -/* rx buffer sizes */ - -#define SMALL_BUFFER_SIZE 384 /* size of small buffers (multiple of 48 (PCA) and 64 (SBA) bytes) */ -#define LARGE_BUFFER_SIZE 4032 /* size of large buffers (multiple of 48 (PCA) and 64 (SBA) bytes) */ - - -#define RBD_BLK_SIZE 32 /* nbr of supplied rx buffers per rbd */ - - -#define MAX_PDU_SIZE 65535 /* maximum PDU size supported by AALs */ - - -#define BUFFER_S1_SIZE SMALL_BUFFER_SIZE /* size of small buffers, scheme 1 */ -#define BUFFER_L1_SIZE LARGE_BUFFER_SIZE /* size of large buffers, scheme 1 */ - -#define BUFFER_S2_SIZE SMALL_BUFFER_SIZE /* size of small buffers, scheme 2 */ -#define BUFFER_L2_SIZE LARGE_BUFFER_SIZE /* size of large buffers, scheme 2 */ - -#define BUFFER_S1_NBR (RBD_BLK_SIZE * 6) -#define BUFFER_L1_NBR (RBD_BLK_SIZE * 4) - -#define BUFFER_S2_NBR (RBD_BLK_SIZE * 6) -#define BUFFER_L2_NBR (RBD_BLK_SIZE * 4) - - -#define QUEUE_SIZE_CMD 16 /* command queue capacity */ -#define QUEUE_SIZE_RX 64 /* receive queue capacity */ -#define QUEUE_SIZE_TX 256 /* transmit queue capacity */ -#define QUEUE_SIZE_BS 32 /* buffer supply queue capacity */ - -#define FORE200E_VPI_BITS 0 -#define FORE200E_VCI_BITS 10 -#define NBR_CONNECT (1 << (FORE200E_VPI_BITS + FORE200E_VCI_BITS)) /* number of connections */ - - -#define TSD_FIXED 2 -#define TSD_EXTENSION 0 -#define TSD_NBR (TSD_FIXED + TSD_EXTENSION) - - -/* the cp starts putting a received PDU into one *small* buffer, - then it uses a number of *large* buffers for the trailing data. - we compute here the total number of receive segment descriptors - required to hold the largest possible PDU */ - -#define RSD_REQUIRED (((MAX_PDU_SIZE - SMALL_BUFFER_SIZE + LARGE_BUFFER_SIZE) / LARGE_BUFFER_SIZE) + 1) - -#define RSD_FIXED 3 - -/* RSD_REQUIRED receive segment descriptors are enough to describe a max-sized PDU, - but we have to keep the size of the receive PDU descriptor multiple of 32 bytes, - so we add one extra RSD to RSD_EXTENSION - (WARNING: THIS MAY CHANGE IF BUFFER SIZES ARE MODIFIED) */ - -#define RSD_EXTENSION ((RSD_REQUIRED - RSD_FIXED) + 1) -#define RSD_NBR (RSD_FIXED + RSD_EXTENSION) - - -#define FORE200E_DEV(d) ((struct fore200e*)((d)->dev_data)) -#define FORE200E_VCC(d) ((struct fore200e_vcc*)((d)->dev_data)) - -/* bitfields endian games */ - -#if defined(__LITTLE_ENDIAN_BITFIELD) -#define BITFIELD2(b1, b2) b1; b2; -#define BITFIELD3(b1, b2, b3) b1; b2; b3; -#define BITFIELD4(b1, b2, b3, b4) b1; b2; b3; b4; -#define BITFIELD5(b1, b2, b3, b4, b5) b1; b2; b3; b4; b5; -#define BITFIELD6(b1, b2, b3, b4, b5, b6) b1; b2; b3; b4; b5; b6; -#elif defined(__BIG_ENDIAN_BITFIELD) -#define BITFIELD2(b1, b2) b2; b1; -#define BITFIELD3(b1, b2, b3) b3; b2; b1; -#define BITFIELD4(b1, b2, b3, b4) b4; b3; b2; b1; -#define BITFIELD5(b1, b2, b3, b4, b5) b5; b4; b3; b2; b1; -#define BITFIELD6(b1, b2, b3, b4, b5, b6) b6; b5; b4; b3; b2; b1; -#else -#error unknown bitfield endianess -#endif - - -/* ATM cell header (minus HEC byte) */ - -typedef struct atm_header { - BITFIELD5( - u32 clp : 1, /* cell loss priority */ - u32 plt : 3, /* payload type */ - u32 vci : 16, /* virtual channel identifier */ - u32 vpi : 8, /* virtual path identifier */ - u32 gfc : 4 /* generic flow control */ - ) -} atm_header_t; - - -/* ATM adaptation layer id */ - -typedef enum fore200e_aal { - FORE200E_AAL0 = 0, - FORE200E_AAL34 = 4, - FORE200E_AAL5 = 5, -} fore200e_aal_t; - - -/* transmit PDU descriptor specification */ - -typedef struct tpd_spec { - BITFIELD4( - u32 length : 16, /* total PDU length */ - u32 nseg : 8, /* number of transmit segments */ - enum fore200e_aal aal : 4, /* adaptation layer */ - u32 intr : 4 /* interrupt requested */ - ) -} tpd_spec_t; - - -/* transmit PDU rate control */ - -typedef struct tpd_rate -{ - BITFIELD2( - u32 idle_cells : 16, /* number of idle cells to insert */ - u32 data_cells : 16 /* number of data cells to transmit */ - ) -} tpd_rate_t; - - -/* transmit segment descriptor */ - -typedef struct tsd { - u32 buffer; /* transmit buffer DMA address */ - u32 length; /* number of bytes in buffer */ -} tsd_t; - - -/* transmit PDU descriptor */ - -typedef struct tpd { - struct atm_header atm_header; /* ATM header minus HEC byte */ - struct tpd_spec spec; /* tpd specification */ - struct tpd_rate rate; /* tpd rate control */ - u32 pad; /* reserved */ - struct tsd tsd[ TSD_NBR ]; /* transmit segment descriptors */ -} tpd_t; - - -/* receive segment descriptor */ - -typedef struct rsd { - u32 handle; /* host supplied receive buffer handle */ - u32 length; /* number of bytes in buffer */ -} rsd_t; - - -/* receive PDU descriptor */ - -typedef struct rpd { - struct atm_header atm_header; /* ATM header minus HEC byte */ - u32 nseg; /* number of receive segments */ - struct rsd rsd[ RSD_NBR ]; /* receive segment descriptors */ -} rpd_t; - - -/* buffer scheme */ - -typedef enum buffer_scheme { - BUFFER_SCHEME_ONE, - BUFFER_SCHEME_TWO, - BUFFER_SCHEME_NBR /* always last */ -} buffer_scheme_t; - - -/* buffer magnitude */ - -typedef enum buffer_magn { - BUFFER_MAGN_SMALL, - BUFFER_MAGN_LARGE, - BUFFER_MAGN_NBR /* always last */ -} buffer_magn_t; - - -/* receive buffer descriptor */ - -typedef struct rbd { - u32 handle; /* host supplied handle */ - u32 buffer_haddr; /* host DMA address of host buffer */ -} rbd_t; - - -/* receive buffer descriptor block */ - -typedef struct rbd_block { - struct rbd rbd[ RBD_BLK_SIZE ]; /* receive buffer descriptor */ -} rbd_block_t; - - -/* tpd DMA address */ - -typedef struct tpd_haddr { - BITFIELD3( - u32 size : 4, /* tpd size expressed in 32 byte blocks */ - u32 pad : 1, /* reserved */ - u32 haddr : 27 /* tpd DMA addr aligned on 32 byte boundary */ - ) -} tpd_haddr_t; - -#define TPD_HADDR_SHIFT 5 /* addr aligned on 32 byte boundary */ - -/* cp resident transmit queue entry */ - -typedef struct cp_txq_entry { - struct tpd_haddr tpd_haddr; /* host DMA address of tpd */ - u32 status_haddr; /* host DMA address of completion status */ -} cp_txq_entry_t; - - -/* cp resident receive queue entry */ - -typedef struct cp_rxq_entry { - u32 rpd_haddr; /* host DMA address of rpd */ - u32 status_haddr; /* host DMA address of completion status */ -} cp_rxq_entry_t; - - -/* cp resident buffer supply queue entry */ - -typedef struct cp_bsq_entry { - u32 rbd_block_haddr; /* host DMA address of rbd block */ - u32 status_haddr; /* host DMA address of completion status */ -} cp_bsq_entry_t; - - -/* completion status */ - -typedef volatile enum status { - STATUS_PENDING = (1<<0), /* initial status (written by host) */ - STATUS_COMPLETE = (1<<1), /* completion status (written by cp) */ - STATUS_FREE = (1<<2), /* initial status (written by host) */ - STATUS_ERROR = (1<<3) /* completion status (written by cp) */ -} status_t; - - -/* cp operation code */ - -typedef enum opcode { - OPCODE_INITIALIZE = 1, /* initialize board */ - OPCODE_ACTIVATE_VCIN, /* activate incoming VCI */ - OPCODE_ACTIVATE_VCOUT, /* activate outgoing VCI */ - OPCODE_DEACTIVATE_VCIN, /* deactivate incoming VCI */ - OPCODE_DEACTIVATE_VCOUT, /* deactivate incoing VCI */ - OPCODE_GET_STATS, /* get board statistics */ - OPCODE_SET_OC3, /* set OC-3 registers */ - OPCODE_GET_OC3, /* get OC-3 registers */ - OPCODE_RESET_STATS, /* reset board statistics */ - OPCODE_GET_PROM, /* get expansion PROM data (PCI specific) */ - OPCODE_SET_VPI_BITS, /* set x bits of those decoded by the - firmware to be low order bits from - the VPI field of the ATM cell header */ - OPCODE_REQUEST_INTR = (1<<7) /* request interrupt */ -} opcode_t; - - -/* virtual path / virtual channel identifiers */ - -typedef struct vpvc { - BITFIELD3( - u32 vci : 16, /* virtual channel identifier */ - u32 vpi : 8, /* virtual path identifier */ - u32 pad : 8 /* reserved */ - ) -} vpvc_t; - - -/* activate VC command opcode */ - -typedef struct activate_opcode { - BITFIELD4( - enum opcode opcode : 8, /* cp opcode */ - enum fore200e_aal aal : 8, /* adaptation layer */ - enum buffer_scheme scheme : 8, /* buffer scheme */ - u32 pad : 8 /* reserved */ - ) -} activate_opcode_t; - - -/* activate VC command block */ - -typedef struct activate_block { - struct activate_opcode opcode; /* activate VC command opcode */ - struct vpvc vpvc; /* VPI/VCI */ - u32 mtu; /* for AAL0 only */ - -} activate_block_t; - - -/* deactivate VC command opcode */ - -typedef struct deactivate_opcode { - BITFIELD2( - enum opcode opcode : 8, /* cp opcode */ - u32 pad : 24 /* reserved */ - ) -} deactivate_opcode_t; - - -/* deactivate VC command block */ - -typedef struct deactivate_block { - struct deactivate_opcode opcode; /* deactivate VC command opcode */ - struct vpvc vpvc; /* VPI/VCI */ -} deactivate_block_t; - - -/* OC-3 registers */ - -typedef struct oc3_regs { - u32 reg[ 128 ]; /* see the PMC Sierra PC5346 S/UNI-155-Lite - Saturn User Network Interface documentation - for a description of the OC-3 chip registers */ -} oc3_regs_t; - - -/* set/get OC-3 regs command opcode */ - -typedef struct oc3_opcode { - BITFIELD4( - enum opcode opcode : 8, /* cp opcode */ - u32 reg : 8, /* register index */ - u32 value : 8, /* register value */ - u32 mask : 8 /* register mask that specifies which - bits of the register value field - are significant */ - ) -} oc3_opcode_t; - - -/* set/get OC-3 regs command block */ - -typedef struct oc3_block { - struct oc3_opcode opcode; /* set/get OC-3 regs command opcode */ - u32 regs_haddr; /* host DMA address of OC-3 regs buffer */ -} oc3_block_t; - - -/* physical encoding statistics */ - -typedef struct stats_phy { - __be32 crc_header_errors; /* cells received with bad header CRC */ - __be32 framing_errors; /* cells received with bad framing */ - __be32 pad[ 2 ]; /* i960 padding */ -} stats_phy_t; - - -/* OC-3 statistics */ - -typedef struct stats_oc3 { - __be32 section_bip8_errors; /* section 8 bit interleaved parity */ - __be32 path_bip8_errors; /* path 8 bit interleaved parity */ - __be32 line_bip24_errors; /* line 24 bit interleaved parity */ - __be32 line_febe_errors; /* line far end block errors */ - __be32 path_febe_errors; /* path far end block errors */ - __be32 corr_hcs_errors; /* correctable header check sequence */ - __be32 ucorr_hcs_errors; /* uncorrectable header check sequence */ - __be32 pad[ 1 ]; /* i960 padding */ -} stats_oc3_t; - - -/* ATM statistics */ - -typedef struct stats_atm { - __be32 cells_transmitted; /* cells transmitted */ - __be32 cells_received; /* cells received */ - __be32 vpi_bad_range; /* cell drops: VPI out of range */ - __be32 vpi_no_conn; /* cell drops: no connection for VPI */ - __be32 vci_bad_range; /* cell drops: VCI out of range */ - __be32 vci_no_conn; /* cell drops: no connection for VCI */ - __be32 pad[ 2 ]; /* i960 padding */ -} stats_atm_t; - -/* AAL0 statistics */ - -typedef struct stats_aal0 { - __be32 cells_transmitted; /* cells transmitted */ - __be32 cells_received; /* cells received */ - __be32 cells_dropped; /* cells dropped */ - __be32 pad[ 1 ]; /* i960 padding */ -} stats_aal0_t; - - -/* AAL3/4 statistics */ - -typedef struct stats_aal34 { - __be32 cells_transmitted; /* cells transmitted from segmented PDUs */ - __be32 cells_received; /* cells reassembled into PDUs */ - __be32 cells_crc_errors; /* payload CRC error count */ - __be32 cells_protocol_errors; /* SAR or CS layer protocol errors */ - __be32 cells_dropped; /* cells dropped: partial reassembly */ - __be32 cspdus_transmitted; /* CS PDUs transmitted */ - __be32 cspdus_received; /* CS PDUs received */ - __be32 cspdus_protocol_errors; /* CS layer protocol errors */ - __be32 cspdus_dropped; /* reassembled PDUs drop'd (in cells) */ - __be32 pad[ 3 ]; /* i960 padding */ -} stats_aal34_t; - - -/* AAL5 statistics */ - -typedef struct stats_aal5 { - __be32 cells_transmitted; /* cells transmitted from segmented SDUs */ - __be32 cells_received; /* cells reassembled into SDUs */ - __be32 cells_dropped; /* reassembled PDUs dropped (in cells) */ - __be32 congestion_experienced; /* CRC error and length wrong */ - __be32 cspdus_transmitted; /* CS PDUs transmitted */ - __be32 cspdus_received; /* CS PDUs received */ - __be32 cspdus_crc_errors; /* CS PDUs CRC errors */ - __be32 cspdus_protocol_errors; /* CS layer protocol errors */ - __be32 cspdus_dropped; /* reassembled PDUs dropped */ - __be32 pad[ 3 ]; /* i960 padding */ -} stats_aal5_t; - - -/* auxiliary statistics */ - -typedef struct stats_aux { - __be32 small_b1_failed; /* receive BD allocation failures */ - __be32 large_b1_failed; /* receive BD allocation failures */ - __be32 small_b2_failed; /* receive BD allocation failures */ - __be32 large_b2_failed; /* receive BD allocation failures */ - __be32 rpd_alloc_failed; /* receive PDU allocation failures */ - __be32 receive_carrier; /* no carrier = 0, carrier = 1 */ - __be32 pad[ 2 ]; /* i960 padding */ -} stats_aux_t; - - -/* whole statistics buffer */ - -typedef struct stats { - struct stats_phy phy; /* physical encoding statistics */ - struct stats_oc3 oc3; /* OC-3 statistics */ - struct stats_atm atm; /* ATM statistics */ - struct stats_aal0 aal0; /* AAL0 statistics */ - struct stats_aal34 aal34; /* AAL3/4 statistics */ - struct stats_aal5 aal5; /* AAL5 statistics */ - struct stats_aux aux; /* auxiliary statistics */ -} stats_t; - - -/* get statistics command opcode */ - -typedef struct stats_opcode { - BITFIELD2( - enum opcode opcode : 8, /* cp opcode */ - u32 pad : 24 /* reserved */ - ) -} stats_opcode_t; - - -/* get statistics command block */ - -typedef struct stats_block { - struct stats_opcode opcode; /* get statistics command opcode */ - u32 stats_haddr; /* host DMA address of stats buffer */ -} stats_block_t; - - -/* expansion PROM data (PCI specific) */ - -typedef struct prom_data { - u32 hw_revision; /* hardware revision */ - u32 serial_number; /* board serial number */ - u8 mac_addr[ 8 ]; /* board MAC address */ -} prom_data_t; - - -/* get expansion PROM data command opcode */ - -typedef struct prom_opcode { - BITFIELD2( - enum opcode opcode : 8, /* cp opcode */ - u32 pad : 24 /* reserved */ - ) -} prom_opcode_t; - - -/* get expansion PROM data command block */ - -typedef struct prom_block { - struct prom_opcode opcode; /* get PROM data command opcode */ - u32 prom_haddr; /* host DMA address of PROM buffer */ -} prom_block_t; - - -/* cp command */ - -typedef union cmd { - enum opcode opcode; /* operation code */ - struct activate_block activate_block; /* activate VC */ - struct deactivate_block deactivate_block; /* deactivate VC */ - struct stats_block stats_block; /* get statistics */ - struct prom_block prom_block; /* get expansion PROM data */ - struct oc3_block oc3_block; /* get/set OC-3 registers */ - u32 pad[ 4 ]; /* i960 padding */ -} cmd_t; - - -/* cp resident command queue */ - -typedef struct cp_cmdq_entry { - union cmd cmd; /* command */ - u32 status_haddr; /* host DMA address of completion status */ - u32 pad[ 3 ]; /* i960 padding */ -} cp_cmdq_entry_t; - - -/* host resident transmit queue entry */ - -typedef struct host_txq_entry { - struct cp_txq_entry __iomem *cp_entry; /* addr of cp resident tx queue entry */ - enum status* status; /* addr of host resident status */ - struct tpd* tpd; /* addr of transmit PDU descriptor */ - u32 tpd_dma; /* DMA address of tpd */ - struct sk_buff* skb; /* related skb */ - void* data; /* copy of misaligned data */ - unsigned long incarn; /* vc_map incarnation when submitted for tx */ - struct fore200e_vc_map* vc_map; - -} host_txq_entry_t; - - -/* host resident receive queue entry */ - -typedef struct host_rxq_entry { - struct cp_rxq_entry __iomem *cp_entry; /* addr of cp resident rx queue entry */ - enum status* status; /* addr of host resident status */ - struct rpd* rpd; /* addr of receive PDU descriptor */ - u32 rpd_dma; /* DMA address of rpd */ -} host_rxq_entry_t; - - -/* host resident buffer supply queue entry */ - -typedef struct host_bsq_entry { - struct cp_bsq_entry __iomem *cp_entry; /* addr of cp resident buffer supply queue entry */ - enum status* status; /* addr of host resident status */ - struct rbd_block* rbd_block; /* addr of receive buffer descriptor block */ - u32 rbd_block_dma; /* DMA address od rdb */ -} host_bsq_entry_t; - - -/* host resident command queue entry */ - -typedef struct host_cmdq_entry { - struct cp_cmdq_entry __iomem *cp_entry; /* addr of cp resident cmd queue entry */ - enum status *status; /* addr of host resident status */ -} host_cmdq_entry_t; - - -/* chunk of memory */ - -typedef struct chunk { - void* alloc_addr; /* base address of allocated chunk */ - void* align_addr; /* base address of aligned chunk */ - dma_addr_t dma_addr; /* DMA address of aligned chunk */ - int direction; /* direction of DMA mapping */ - u32 alloc_size; /* length of allocated chunk */ - u32 align_size; /* length of aligned chunk */ -} chunk_t; - -#define dma_size align_size /* DMA useable size */ - - -/* host resident receive buffer */ - -typedef struct buffer { - struct buffer* next; /* next receive buffer */ - enum buffer_scheme scheme; /* buffer scheme */ - enum buffer_magn magn; /* buffer magnitude */ - struct chunk data; /* data buffer */ -#ifdef FORE200E_BSQ_DEBUG - unsigned long index; /* buffer # in queue */ - int supplied; /* 'buffer supplied' flag */ -#endif -} buffer_t; - - -#if (BITS_PER_LONG == 32) -#define FORE200E_BUF2HDL(buffer) ((u32)(buffer)) -#define FORE200E_HDL2BUF(handle) ((struct buffer*)(handle)) -#else /* deal with 64 bit pointers */ -#define FORE200E_BUF2HDL(buffer) ((u32)((u64)(buffer))) -#define FORE200E_HDL2BUF(handle) ((struct buffer*)(((u64)(handle)) | PAGE_OFFSET)) -#endif - - -/* host resident command queue */ - -typedef struct host_cmdq { - struct host_cmdq_entry host_entry[ QUEUE_SIZE_CMD ]; /* host resident cmd queue entries */ - int head; /* head of cmd queue */ - struct chunk status; /* array of completion status */ -} host_cmdq_t; - - -/* host resident transmit queue */ - -typedef struct host_txq { - struct host_txq_entry host_entry[ QUEUE_SIZE_TX ]; /* host resident tx queue entries */ - int head; /* head of tx queue */ - int tail; /* tail of tx queue */ - struct chunk tpd; /* array of tpds */ - struct chunk status; /* arry of completion status */ - int txing; /* number of pending PDUs in tx queue */ -} host_txq_t; - - -/* host resident receive queue */ - -typedef struct host_rxq { - struct host_rxq_entry host_entry[ QUEUE_SIZE_RX ]; /* host resident rx queue entries */ - int head; /* head of rx queue */ - struct chunk rpd; /* array of rpds */ - struct chunk status; /* array of completion status */ -} host_rxq_t; - - -/* host resident buffer supply queues */ - -typedef struct host_bsq { - struct host_bsq_entry host_entry[ QUEUE_SIZE_BS ]; /* host resident buffer supply queue entries */ - int head; /* head of buffer supply queue */ - struct chunk rbd_block; /* array of rbds */ - struct chunk status; /* array of completion status */ - struct buffer* buffer; /* array of rx buffers */ - struct buffer* freebuf; /* list of free rx buffers */ - volatile int freebuf_count; /* count of free rx buffers */ -} host_bsq_t; - - -/* header of the firmware image */ - -typedef struct fw_header { - __le32 magic; /* magic number */ - __le32 version; /* firmware version id */ - __le32 load_offset; /* fw load offset in board memory */ - __le32 start_offset; /* fw execution start address in board memory */ -} fw_header_t; - -#define FW_HEADER_MAGIC 0x65726f66 /* 'fore' */ - - -/* receive buffer supply queues scheme specification */ - -typedef struct bs_spec { - u32 queue_length; /* queue capacity */ - u32 buffer_size; /* host buffer size */ - u32 pool_size; /* number of rbds */ - u32 supply_blksize; /* num of rbds in I/O block (multiple - of 4 between 4 and 124 inclusive) */ -} bs_spec_t; - - -/* initialization command block (one-time command, not in cmd queue) */ - -typedef struct init_block { - enum opcode opcode; /* initialize command */ - enum status status; /* related status word */ - u32 receive_threshold; /* not used */ - u32 num_connect; /* ATM connections */ - u32 cmd_queue_len; /* length of command queue */ - u32 tx_queue_len; /* length of transmit queue */ - u32 rx_queue_len; /* length of receive queue */ - u32 rsd_extension; /* number of extra 32 byte blocks */ - u32 tsd_extension; /* number of extra 32 byte blocks */ - u32 conless_vpvc; /* not used */ - u32 pad[ 2 ]; /* force quad alignment */ - struct bs_spec bs_spec[ BUFFER_SCHEME_NBR ][ BUFFER_MAGN_NBR ]; /* buffer supply queues spec */ -} init_block_t; - - -typedef enum media_type { - MEDIA_TYPE_CAT5_UTP = 0x06, /* unshielded twisted pair */ - MEDIA_TYPE_MM_OC3_ST = 0x16, /* multimode fiber ST */ - MEDIA_TYPE_MM_OC3_SC = 0x26, /* multimode fiber SC */ - MEDIA_TYPE_SM_OC3_ST = 0x36, /* single-mode fiber ST */ - MEDIA_TYPE_SM_OC3_SC = 0x46 /* single-mode fiber SC */ -} media_type_t; - -#define FORE200E_MEDIA_INDEX(media_type) ((media_type)>>4) - - -/* cp resident queues */ - -typedef struct cp_queues { - u32 cp_cmdq; /* command queue */ - u32 cp_txq; /* transmit queue */ - u32 cp_rxq; /* receive queue */ - u32 cp_bsq[ BUFFER_SCHEME_NBR ][ BUFFER_MAGN_NBR ]; /* buffer supply queues */ - u32 imask; /* 1 enables cp to host interrupts */ - u32 istat; /* 1 for interrupt posted */ - u32 heap_base; /* offset form beginning of ram */ - u32 heap_size; /* space available for queues */ - u32 hlogger; /* non zero for host logging */ - u32 heartbeat; /* cp heartbeat */ - u32 fw_release; /* firmware version */ - u32 mon960_release; /* i960 monitor version */ - u32 tq_plen; /* transmit throughput measurements */ - /* make sure the init block remains on a quad word boundary */ - struct init_block init; /* one time cmd, not in cmd queue */ - enum media_type media_type; /* media type id */ - u32 oc3_revision; /* OC-3 revision number */ -} cp_queues_t; - - -/* boot status */ - -typedef enum boot_status { - BSTAT_COLD_START = (u32) 0xc01dc01d, /* cold start */ - BSTAT_SELFTEST_OK = (u32) 0x02201958, /* self-test ok */ - BSTAT_SELFTEST_FAIL = (u32) 0xadbadbad, /* self-test failed */ - BSTAT_CP_RUNNING = (u32) 0xce11feed, /* cp is running */ - BSTAT_MON_TOO_BIG = (u32) 0x10aded00 /* i960 monitor is too big */ -} boot_status_t; - - -/* software UART */ - -typedef struct soft_uart { - u32 send; /* write register */ - u32 recv; /* read register */ -} soft_uart_t; - -#define FORE200E_CP_MONITOR_UART_FREE 0x00000000 -#define FORE200E_CP_MONITOR_UART_AVAIL 0x01000000 - - -/* i960 monitor */ - -typedef struct cp_monitor { - struct soft_uart soft_uart; /* software UART */ - enum boot_status bstat; /* boot status */ - u32 app_base; /* application base offset */ - u32 mon_version; /* i960 monitor version */ -} cp_monitor_t; - - -/* device state */ - -typedef enum fore200e_state { - FORE200E_STATE_BLANK, /* initial state */ - FORE200E_STATE_REGISTER, /* device registered */ - FORE200E_STATE_CONFIGURE, /* bus interface configured */ - FORE200E_STATE_MAP, /* board space mapped in host memory */ - FORE200E_STATE_RESET, /* board resetted */ - FORE200E_STATE_START_FW, /* firmware started */ - FORE200E_STATE_INITIALIZE, /* initialize command successful */ - FORE200E_STATE_INIT_CMDQ, /* command queue initialized */ - FORE200E_STATE_INIT_TXQ, /* transmit queue initialized */ - FORE200E_STATE_INIT_RXQ, /* receive queue initialized */ - FORE200E_STATE_INIT_BSQ, /* buffer supply queue initialized */ - FORE200E_STATE_ALLOC_BUF, /* receive buffers allocated */ - FORE200E_STATE_IRQ, /* host interrupt requested */ - FORE200E_STATE_COMPLETE /* initialization completed */ -} fore200e_state; - - -/* PCA-200E registers */ - -typedef struct fore200e_pca_regs { - volatile u32 __iomem * hcr; /* address of host control register */ - volatile u32 __iomem * imr; /* address of host interrupt mask register */ - volatile u32 __iomem * psr; /* address of PCI specific register */ -} fore200e_pca_regs_t; - - -/* SBA-200E registers */ - -typedef struct fore200e_sba_regs { - u32 __iomem *hcr; /* address of host control register */ - u32 __iomem *bsr; /* address of burst transfer size register */ - u32 __iomem *isr; /* address of interrupt level selection register */ -} fore200e_sba_regs_t; - - -/* model-specific registers */ - -typedef union fore200e_regs { - struct fore200e_pca_regs pca; /* PCA-200E registers */ - struct fore200e_sba_regs sba; /* SBA-200E registers */ -} fore200e_regs; - - -struct fore200e; - -/* bus-dependent data */ - -typedef struct fore200e_bus { - char* model_name; /* board model name */ - char* proc_name; /* board name under /proc/atm */ - int descr_alignment; /* tpd/rpd/rbd DMA alignment requirement */ - int buffer_alignment; /* rx buffers DMA alignment requirement */ - int status_alignment; /* status words DMA alignment requirement */ - u32 (*read)(volatile u32 __iomem *); - void (*write)(u32, volatile u32 __iomem *); - int (*configure)(struct fore200e*); - int (*map)(struct fore200e*); - void (*reset)(struct fore200e*); - int (*prom_read)(struct fore200e*, struct prom_data*); - void (*unmap)(struct fore200e*); - void (*irq_enable)(struct fore200e*); - int (*irq_check)(struct fore200e*); - void (*irq_ack)(struct fore200e*); - int (*proc_read)(struct fore200e*, char*); -} fore200e_bus_t; - -/* vc mapping */ - -typedef struct fore200e_vc_map { - struct atm_vcc* vcc; /* vcc entry */ - unsigned long incarn; /* vcc incarnation number */ -} fore200e_vc_map_t; - -#define FORE200E_VC_MAP(fore200e, vpi, vci) \ - (& (fore200e)->vc_map[ ((vpi) << FORE200E_VCI_BITS) | (vci) ]) - - -/* per-device data */ - -typedef struct fore200e { - const struct fore200e_bus* bus; /* bus-dependent code and data */ - union fore200e_regs regs; /* bus-dependent registers */ - struct atm_dev* atm_dev; /* ATM device */ - - enum fore200e_state state; /* device state */ - - char name[16]; /* device name */ - struct device *dev; - int irq; /* irq number */ - unsigned long phys_base; /* physical base address */ - void __iomem * virt_base; /* virtual base address */ - - unsigned char esi[ ESI_LEN ]; /* end system identifier */ - - struct cp_monitor __iomem * cp_monitor; /* i960 monitor address */ - struct cp_queues __iomem * cp_queues; /* cp resident queues */ - struct host_cmdq host_cmdq; /* host resident cmd queue */ - struct host_txq host_txq; /* host resident tx queue */ - struct host_rxq host_rxq; /* host resident rx queue */ - /* host resident buffer supply queues */ - struct host_bsq host_bsq[ BUFFER_SCHEME_NBR ][ BUFFER_MAGN_NBR ]; - - u32 available_cell_rate; /* remaining pseudo-CBR bw on link */ - - int loop_mode; /* S/UNI loopback mode */ - - struct stats* stats; /* last snapshot of the stats */ - - struct mutex rate_mtx; /* protects rate reservation ops */ - spinlock_t q_lock; /* protects queue ops */ -#ifdef FORE200E_USE_TASKLET - struct tasklet_struct tx_tasklet; /* performs tx interrupt work */ - struct tasklet_struct rx_tasklet; /* performs rx interrupt work */ -#endif - unsigned long tx_sat; /* tx queue saturation count */ - - unsigned long incarn_count; - struct fore200e_vc_map vc_map[ NBR_CONNECT ]; /* vc mapping */ -} fore200e_t; - - -/* per-vcc data */ - -typedef struct fore200e_vcc { - enum buffer_scheme scheme; /* rx buffer scheme */ - struct tpd_rate rate; /* tx rate control data */ - int rx_min_pdu; /* size of smallest PDU received */ - int rx_max_pdu; /* size of largest PDU received */ - int tx_min_pdu; /* size of smallest PDU transmitted */ - int tx_max_pdu; /* size of largest PDU transmitted */ - unsigned long tx_pdu; /* nbr of tx pdus */ - unsigned long rx_pdu; /* nbr of rx pdus */ -} fore200e_vcc_t; - - - -/* 200E-series common memory layout */ - -#define FORE200E_CP_MONITOR_OFFSET 0x00000400 /* i960 monitor interface */ -#define FORE200E_CP_QUEUES_OFFSET 0x00004d40 /* cp resident queues */ - - -/* PCA-200E memory layout */ - -#define PCA200E_IOSPACE_LENGTH 0x00200000 - -#define PCA200E_HCR_OFFSET 0x00100000 /* board control register */ -#define PCA200E_IMR_OFFSET 0x00100004 /* host IRQ mask register */ -#define PCA200E_PSR_OFFSET 0x00100008 /* PCI specific register */ - - -/* PCA-200E host control register */ - -#define PCA200E_HCR_RESET (1<<0) /* read / write */ -#define PCA200E_HCR_HOLD_LOCK (1<<1) /* read / write */ -#define PCA200E_HCR_I960FAIL (1<<2) /* read */ -#define PCA200E_HCR_INTRB (1<<2) /* write */ -#define PCA200E_HCR_HOLD_ACK (1<<3) /* read */ -#define PCA200E_HCR_INTRA (1<<3) /* write */ -#define PCA200E_HCR_OUTFULL (1<<4) /* read */ -#define PCA200E_HCR_CLRINTR (1<<4) /* write */ -#define PCA200E_HCR_ESPHOLD (1<<5) /* read */ -#define PCA200E_HCR_INFULL (1<<6) /* read */ -#define PCA200E_HCR_TESTMODE (1<<7) /* read */ - - -/* PCA-200E PCI bus interface regs (offsets in PCI config space) */ - -#define PCA200E_PCI_LATENCY 0x40 /* maximum slave latenty */ -#define PCA200E_PCI_MASTER_CTRL 0x41 /* master control */ -#define PCA200E_PCI_THRESHOLD 0x42 /* burst / continuous req threshold */ - -/* PBI master control register */ - -#define PCA200E_CTRL_DIS_CACHE_RD (1<<0) /* disable cache-line reads */ -#define PCA200E_CTRL_DIS_WRT_INVAL (1<<1) /* disable writes and invalidates */ -#define PCA200E_CTRL_2_CACHE_WRT_INVAL (1<<2) /* require 2 cache-lines for writes and invalidates */ -#define PCA200E_CTRL_IGN_LAT_TIMER (1<<3) /* ignore the latency timer */ -#define PCA200E_CTRL_ENA_CONT_REQ_MODE (1<<4) /* enable continuous request mode */ -#define PCA200E_CTRL_LARGE_PCI_BURSTS (1<<5) /* force large PCI bus bursts */ -#define PCA200E_CTRL_CONVERT_ENDIAN (1<<6) /* convert endianess of slave RAM accesses */ - - - -#define SBA200E_PROM_NAME "FORE,sba-200e" /* device name in openprom tree */ - - -/* size of SBA-200E registers */ - -#define SBA200E_HCR_LENGTH 4 -#define SBA200E_BSR_LENGTH 4 -#define SBA200E_ISR_LENGTH 4 -#define SBA200E_RAM_LENGTH 0x40000 - - -/* SBA-200E SBUS burst transfer size register */ - -#define SBA200E_BSR_BURST4 0x04 -#define SBA200E_BSR_BURST8 0x08 -#define SBA200E_BSR_BURST16 0x10 - - -/* SBA-200E host control register */ - -#define SBA200E_HCR_RESET (1<<0) /* read / write (sticky) */ -#define SBA200E_HCR_HOLD_LOCK (1<<1) /* read / write (sticky) */ -#define SBA200E_HCR_I960FAIL (1<<2) /* read */ -#define SBA200E_HCR_I960SETINTR (1<<2) /* write */ -#define SBA200E_HCR_OUTFULL (1<<3) /* read */ -#define SBA200E_HCR_INTR_CLR (1<<3) /* write */ -#define SBA200E_HCR_INTR_ENA (1<<4) /* read / write (sticky) */ -#define SBA200E_HCR_ESPHOLD (1<<5) /* read */ -#define SBA200E_HCR_INFULL (1<<6) /* read */ -#define SBA200E_HCR_TESTMODE (1<<7) /* read */ -#define SBA200E_HCR_INTR_REQ (1<<8) /* read */ - -#define SBA200E_HCR_STICKY (SBA200E_HCR_RESET | SBA200E_HCR_HOLD_LOCK | SBA200E_HCR_INTR_ENA) - - -#endif /* __KERNEL__ */ -#endif /* _FORE200E_H */ diff --git a/drivers/atm/he.c b/drivers/atm/he.c deleted file mode 100644 index bb9cb00f9585..000000000000 --- a/drivers/atm/he.c +++ /dev/null @@ -1,2861 +0,0 @@ -/* - - he.c - - ForeRunnerHE ATM Adapter driver for ATM on Linux - Copyright (C) 1999-2001 Naval Research Laboratory - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ - -/* - - he.c - - ForeRunnerHE ATM Adapter driver for ATM on Linux - Copyright (C) 1999-2001 Naval Research Laboratory - - Permission to use, copy, modify and distribute this software and its - documentation is hereby granted, provided that both the copyright - notice and this permission notice appear in all copies of the software, - derivative works or modified versions, and any portions thereof, and - that both notices appear in supporting documentation. - - NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND - DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER - RESULTING FROM THE USE OF THIS SOFTWARE. - - This driver was written using the "Programmer's Reference Manual for - ForeRunnerHE(tm)", MANU0361-01 - Rev. A, 08/21/98. - - AUTHORS: - chas williams - eric kinzie - - NOTES: - 4096 supported 'connections' - group 0 is used for all traffic - interrupt queue 0 is used for all interrupts - aal0 support (based on work from ulrich.u.muller@nokia.com) - - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#undef USE_SCATTERGATHER -#undef USE_CHECKSUM_HW /* still confused about this */ -/* #undef HE_DEBUG */ - -#include "he.h" -#include "suni.h" -#include - -#define hprintk(fmt,args...) printk(KERN_ERR DEV_LABEL "%d: " fmt, he_dev->number , ##args) - -#ifdef HE_DEBUG -#define HPRINTK(fmt,args...) printk(KERN_DEBUG DEV_LABEL "%d: " fmt, he_dev->number , ##args) -#else /* !HE_DEBUG */ -#define HPRINTK(fmt,args...) do { } while (0) -#endif /* HE_DEBUG */ - -/* declarations */ - -static int he_open(struct atm_vcc *vcc); -static void he_close(struct atm_vcc *vcc); -static int he_send(struct atm_vcc *vcc, struct sk_buff *skb); -static int he_ioctl(struct atm_dev *dev, unsigned int cmd, void __user *arg); -static irqreturn_t he_irq_handler(int irq, void *dev_id); -static void he_tasklet(unsigned long data); -static int he_proc_read(struct atm_dev *dev,loff_t *pos,char *page); -static int he_start(struct atm_dev *dev); -static void he_stop(struct he_dev *dev); -static void he_phy_put(struct atm_dev *, unsigned char, unsigned long); -static unsigned char he_phy_get(struct atm_dev *, unsigned long); - -static u8 read_prom_byte(struct he_dev *he_dev, int addr); - -/* globals */ - -static struct he_dev *he_devs; -static bool disable64; -static short nvpibits = -1; -static short nvcibits = -1; -static short rx_skb_reserve = 16; -static bool irq_coalesce = true; -static bool sdh; - -/* Read from EEPROM = 0000 0011b */ -static unsigned int readtab[] = { - CS_HIGH | CLK_HIGH, - CS_LOW | CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW | SI_HIGH, - CLK_HIGH | SI_HIGH, /* 1 */ - CLK_LOW | SI_HIGH, - CLK_HIGH | SI_HIGH /* 1 */ -}; - -/* Clock to read from/write to the EEPROM */ -static unsigned int clocktab[] = { - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW -}; - -static const struct atmdev_ops he_ops = -{ - .open = he_open, - .close = he_close, - .ioctl = he_ioctl, - .send = he_send, - .phy_put = he_phy_put, - .phy_get = he_phy_get, - .proc_read = he_proc_read, - .owner = THIS_MODULE -}; - -#define he_writel(dev, val, reg) do { writel(val, (dev)->membase + (reg)); wmb(); } while (0) -#define he_readl(dev, reg) readl((dev)->membase + (reg)) - -/* section 2.12 connection memory access */ - -static __inline__ void -he_writel_internal(struct he_dev *he_dev, unsigned val, unsigned addr, - unsigned flags) -{ - he_writel(he_dev, val, CON_DAT); - (void) he_readl(he_dev, CON_DAT); /* flush posted writes */ - he_writel(he_dev, flags | CON_CTL_WRITE | CON_CTL_ADDR(addr), CON_CTL); - while (he_readl(he_dev, CON_CTL) & CON_CTL_BUSY); -} - -#define he_writel_rcm(dev, val, reg) \ - he_writel_internal(dev, val, reg, CON_CTL_RCM) - -#define he_writel_tcm(dev, val, reg) \ - he_writel_internal(dev, val, reg, CON_CTL_TCM) - -#define he_writel_mbox(dev, val, reg) \ - he_writel_internal(dev, val, reg, CON_CTL_MBOX) - -static unsigned -he_readl_internal(struct he_dev *he_dev, unsigned addr, unsigned flags) -{ - he_writel(he_dev, flags | CON_CTL_READ | CON_CTL_ADDR(addr), CON_CTL); - while (he_readl(he_dev, CON_CTL) & CON_CTL_BUSY); - return he_readl(he_dev, CON_DAT); -} - -#define he_readl_rcm(dev, reg) \ - he_readl_internal(dev, reg, CON_CTL_RCM) - -#define he_readl_tcm(dev, reg) \ - he_readl_internal(dev, reg, CON_CTL_TCM) - -#define he_readl_mbox(dev, reg) \ - he_readl_internal(dev, reg, CON_CTL_MBOX) - - -/* figure 2.2 connection id */ - -#define he_mkcid(dev, vpi, vci) (((vpi << (dev)->vcibits) | vci) & 0x1fff) - -/* 2.5.1 per connection transmit state registers */ - -#define he_writel_tsr0(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 0) -#define he_readl_tsr0(dev, cid) \ - he_readl_tcm(dev, CONFIG_TSRA | (cid << 3) | 0) - -#define he_writel_tsr1(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 1) - -#define he_writel_tsr2(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 2) - -#define he_writel_tsr3(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 3) - -#define he_writel_tsr4(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 4) - - /* from page 2-20 - * - * NOTE While the transmit connection is active, bits 23 through 0 - * of this register must not be written by the host. Byte - * enables should be used during normal operation when writing - * the most significant byte. - */ - -#define he_writel_tsr4_upper(dev, val, cid) \ - he_writel_internal(dev, val, CONFIG_TSRA | (cid << 3) | 4, \ - CON_CTL_TCM \ - | CON_BYTE_DISABLE_2 \ - | CON_BYTE_DISABLE_1 \ - | CON_BYTE_DISABLE_0) - -#define he_readl_tsr4(dev, cid) \ - he_readl_tcm(dev, CONFIG_TSRA | (cid << 3) | 4) - -#define he_writel_tsr5(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 5) - -#define he_writel_tsr6(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 6) - -#define he_writel_tsr7(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRA | (cid << 3) | 7) - - -#define he_writel_tsr8(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRB | (cid << 2) | 0) - -#define he_writel_tsr9(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRB | (cid << 2) | 1) - -#define he_writel_tsr10(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRB | (cid << 2) | 2) - -#define he_writel_tsr11(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRB | (cid << 2) | 3) - - -#define he_writel_tsr12(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRC | (cid << 1) | 0) - -#define he_writel_tsr13(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRC | (cid << 1) | 1) - - -#define he_writel_tsr14(dev, val, cid) \ - he_writel_tcm(dev, val, CONFIG_TSRD | cid) - -#define he_writel_tsr14_upper(dev, val, cid) \ - he_writel_internal(dev, val, CONFIG_TSRD | cid, \ - CON_CTL_TCM \ - | CON_BYTE_DISABLE_2 \ - | CON_BYTE_DISABLE_1 \ - | CON_BYTE_DISABLE_0) - -/* 2.7.1 per connection receive state registers */ - -#define he_writel_rsr0(dev, val, cid) \ - he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 0) -#define he_readl_rsr0(dev, cid) \ - he_readl_rcm(dev, 0x00000 | (cid << 3) | 0) - -#define he_writel_rsr1(dev, val, cid) \ - he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 1) - -#define he_writel_rsr2(dev, val, cid) \ - he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 2) - -#define he_writel_rsr3(dev, val, cid) \ - he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 3) - -#define he_writel_rsr4(dev, val, cid) \ - he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 4) - -#define he_writel_rsr5(dev, val, cid) \ - he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 5) - -#define he_writel_rsr6(dev, val, cid) \ - he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 6) - -#define he_writel_rsr7(dev, val, cid) \ - he_writel_rcm(dev, val, 0x00000 | (cid << 3) | 7) - -static __inline__ struct atm_vcc* -__find_vcc(struct he_dev *he_dev, unsigned cid) -{ - struct hlist_head *head; - struct atm_vcc *vcc; - struct sock *s; - short vpi; - int vci; - - vpi = cid >> he_dev->vcibits; - vci = cid & ((1 << he_dev->vcibits) - 1); - head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; - - sk_for_each(s, head) { - vcc = atm_sk(s); - if (vcc->dev == he_dev->atm_dev && - vcc->vci == vci && vcc->vpi == vpi && - vcc->qos.rxtp.traffic_class != ATM_NONE) { - return vcc; - } - } - return NULL; -} - -static int he_init_one(struct pci_dev *pci_dev, - const struct pci_device_id *pci_ent) -{ - struct atm_dev *atm_dev = NULL; - struct he_dev *he_dev = NULL; - int err = 0; - - printk(KERN_INFO "ATM he driver\n"); - - if (pci_enable_device(pci_dev)) - return -EIO; - if (dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32)) != 0) { - printk(KERN_WARNING "he: no suitable dma available\n"); - err = -EIO; - goto init_one_failure; - } - - atm_dev = atm_dev_register(DEV_LABEL, &pci_dev->dev, &he_ops, -1, NULL); - if (!atm_dev) { - err = -ENODEV; - goto init_one_failure; - } - pci_set_drvdata(pci_dev, atm_dev); - - he_dev = kzalloc_obj(struct he_dev); - if (!he_dev) { - err = -ENOMEM; - goto init_one_failure; - } - he_dev->pci_dev = pci_dev; - he_dev->atm_dev = atm_dev; - he_dev->atm_dev->dev_data = he_dev; - atm_dev->dev_data = he_dev; - he_dev->number = atm_dev->number; - tasklet_init(&he_dev->tasklet, he_tasklet, (unsigned long) he_dev); - spin_lock_init(&he_dev->global_lock); - - if (he_start(atm_dev)) { - he_stop(he_dev); - err = -ENODEV; - goto init_one_failure; - } - he_dev->next = NULL; - if (he_devs) - he_dev->next = he_devs; - he_devs = he_dev; - return 0; - -init_one_failure: - if (atm_dev) - atm_dev_deregister(atm_dev); - kfree(he_dev); - pci_disable_device(pci_dev); - return err; -} - -static void he_remove_one(struct pci_dev *pci_dev) -{ - struct atm_dev *atm_dev; - struct he_dev *he_dev; - - atm_dev = pci_get_drvdata(pci_dev); - he_dev = HE_DEV(atm_dev); - - /* need to remove from he_devs */ - - he_stop(he_dev); - atm_dev_deregister(atm_dev); - kfree(he_dev); - - pci_disable_device(pci_dev); -} - - -static unsigned -rate_to_atmf(unsigned rate) /* cps to atm forum format */ -{ -#define NONZERO (1 << 14) - - unsigned exp = 0; - - if (rate == 0) - return 0; - - rate <<= 9; - while (rate > 0x3ff) { - ++exp; - rate >>= 1; - } - - return (NONZERO | (exp << 9) | (rate & 0x1ff)); -} - -static void he_init_rx_lbfp0(struct he_dev *he_dev) -{ - unsigned i, lbm_offset, lbufd_index, lbuf_addr, lbuf_count; - unsigned lbufs_per_row = he_dev->cells_per_row / he_dev->cells_per_lbuf; - unsigned lbuf_bufsize = he_dev->cells_per_lbuf * ATM_CELL_PAYLOAD; - unsigned row_offset = he_dev->r0_startrow * he_dev->bytes_per_row; - - lbufd_index = 0; - lbm_offset = he_readl(he_dev, RCMLBM_BA); - - he_writel(he_dev, lbufd_index, RLBF0_H); - - for (i = 0, lbuf_count = 0; i < he_dev->r0_numbuffs; ++i) { - lbufd_index += 2; - lbuf_addr = (row_offset + (lbuf_count * lbuf_bufsize)) / 32; - - he_writel_rcm(he_dev, lbuf_addr, lbm_offset); - he_writel_rcm(he_dev, lbufd_index, lbm_offset + 1); - - if (++lbuf_count == lbufs_per_row) { - lbuf_count = 0; - row_offset += he_dev->bytes_per_row; - } - lbm_offset += 4; - } - - he_writel(he_dev, lbufd_index - 2, RLBF0_T); - he_writel(he_dev, he_dev->r0_numbuffs, RLBF0_C); -} - -static void he_init_rx_lbfp1(struct he_dev *he_dev) -{ - unsigned i, lbm_offset, lbufd_index, lbuf_addr, lbuf_count; - unsigned lbufs_per_row = he_dev->cells_per_row / he_dev->cells_per_lbuf; - unsigned lbuf_bufsize = he_dev->cells_per_lbuf * ATM_CELL_PAYLOAD; - unsigned row_offset = he_dev->r1_startrow * he_dev->bytes_per_row; - - lbufd_index = 1; - lbm_offset = he_readl(he_dev, RCMLBM_BA) + (2 * lbufd_index); - - he_writel(he_dev, lbufd_index, RLBF1_H); - - for (i = 0, lbuf_count = 0; i < he_dev->r1_numbuffs; ++i) { - lbufd_index += 2; - lbuf_addr = (row_offset + (lbuf_count * lbuf_bufsize)) / 32; - - he_writel_rcm(he_dev, lbuf_addr, lbm_offset); - he_writel_rcm(he_dev, lbufd_index, lbm_offset + 1); - - if (++lbuf_count == lbufs_per_row) { - lbuf_count = 0; - row_offset += he_dev->bytes_per_row; - } - lbm_offset += 4; - } - - he_writel(he_dev, lbufd_index - 2, RLBF1_T); - he_writel(he_dev, he_dev->r1_numbuffs, RLBF1_C); -} - -static void he_init_tx_lbfp(struct he_dev *he_dev) -{ - unsigned i, lbm_offset, lbufd_index, lbuf_addr, lbuf_count; - unsigned lbufs_per_row = he_dev->cells_per_row / he_dev->cells_per_lbuf; - unsigned lbuf_bufsize = he_dev->cells_per_lbuf * ATM_CELL_PAYLOAD; - unsigned row_offset = he_dev->tx_startrow * he_dev->bytes_per_row; - - lbufd_index = he_dev->r0_numbuffs + he_dev->r1_numbuffs; - lbm_offset = he_readl(he_dev, RCMLBM_BA) + (2 * lbufd_index); - - he_writel(he_dev, lbufd_index, TLBF_H); - - for (i = 0, lbuf_count = 0; i < he_dev->tx_numbuffs; ++i) { - lbufd_index += 1; - lbuf_addr = (row_offset + (lbuf_count * lbuf_bufsize)) / 32; - - he_writel_rcm(he_dev, lbuf_addr, lbm_offset); - he_writel_rcm(he_dev, lbufd_index, lbm_offset + 1); - - if (++lbuf_count == lbufs_per_row) { - lbuf_count = 0; - row_offset += he_dev->bytes_per_row; - } - lbm_offset += 2; - } - - he_writel(he_dev, lbufd_index - 1, TLBF_T); -} - -static int he_init_tpdrq(struct he_dev *he_dev) -{ - he_dev->tpdrq_base = dma_alloc_coherent(&he_dev->pci_dev->dev, - CONFIG_TPDRQ_SIZE * sizeof(struct he_tpdrq), - &he_dev->tpdrq_phys, - GFP_KERNEL); - if (he_dev->tpdrq_base == NULL) { - hprintk("failed to alloc tpdrq\n"); - return -ENOMEM; - } - - he_dev->tpdrq_tail = he_dev->tpdrq_base; - he_dev->tpdrq_head = he_dev->tpdrq_base; - - he_writel(he_dev, he_dev->tpdrq_phys, TPDRQ_B_H); - he_writel(he_dev, 0, TPDRQ_T); - he_writel(he_dev, CONFIG_TPDRQ_SIZE - 1, TPDRQ_S); - - return 0; -} - -static void he_init_cs_block(struct he_dev *he_dev) -{ - unsigned clock, rate, delta; - int reg; - - /* 5.1.7 cs block initialization */ - - for (reg = 0; reg < 0x20; ++reg) - he_writel_mbox(he_dev, 0x0, CS_STTIM0 + reg); - - /* rate grid timer reload values */ - - clock = he_is622(he_dev) ? 66667000 : 50000000; - rate = he_dev->atm_dev->link_rate; - delta = rate / 16 / 2; - - for (reg = 0; reg < 0x10; ++reg) { - /* 2.4 internal transmit function - * - * we initialize the first row in the rate grid. - * values are period (in clock cycles) of timer - */ - unsigned period = clock / rate; - - he_writel_mbox(he_dev, period, CS_TGRLD0 + reg); - rate -= delta; - } - - if (he_is622(he_dev)) { - /* table 5.2 (4 cells per lbuf) */ - he_writel_mbox(he_dev, 0x000800fa, CS_ERTHR0); - he_writel_mbox(he_dev, 0x000c33cb, CS_ERTHR1); - he_writel_mbox(he_dev, 0x0010101b, CS_ERTHR2); - he_writel_mbox(he_dev, 0x00181dac, CS_ERTHR3); - he_writel_mbox(he_dev, 0x00280600, CS_ERTHR4); - - /* table 5.3, 5.4, 5.5, 5.6, 5.7 */ - he_writel_mbox(he_dev, 0x023de8b3, CS_ERCTL0); - he_writel_mbox(he_dev, 0x1801, CS_ERCTL1); - he_writel_mbox(he_dev, 0x68b3, CS_ERCTL2); - he_writel_mbox(he_dev, 0x1280, CS_ERSTAT0); - he_writel_mbox(he_dev, 0x68b3, CS_ERSTAT1); - he_writel_mbox(he_dev, 0x14585, CS_RTFWR); - - he_writel_mbox(he_dev, 0x4680, CS_RTATR); - - /* table 5.8 */ - he_writel_mbox(he_dev, 0x00159ece, CS_TFBSET); - he_writel_mbox(he_dev, 0x68b3, CS_WCRMAX); - he_writel_mbox(he_dev, 0x5eb3, CS_WCRMIN); - he_writel_mbox(he_dev, 0xe8b3, CS_WCRINC); - he_writel_mbox(he_dev, 0xdeb3, CS_WCRDEC); - he_writel_mbox(he_dev, 0x68b3, CS_WCRCEIL); - - /* table 5.9 */ - he_writel_mbox(he_dev, 0x5, CS_OTPPER); - he_writel_mbox(he_dev, 0x14, CS_OTWPER); - } else { - /* table 5.1 (4 cells per lbuf) */ - he_writel_mbox(he_dev, 0x000400ea, CS_ERTHR0); - he_writel_mbox(he_dev, 0x00063388, CS_ERTHR1); - he_writel_mbox(he_dev, 0x00081018, CS_ERTHR2); - he_writel_mbox(he_dev, 0x000c1dac, CS_ERTHR3); - he_writel_mbox(he_dev, 0x0014051a, CS_ERTHR4); - - /* table 5.3, 5.4, 5.5, 5.6, 5.7 */ - he_writel_mbox(he_dev, 0x0235e4b1, CS_ERCTL0); - he_writel_mbox(he_dev, 0x4701, CS_ERCTL1); - he_writel_mbox(he_dev, 0x64b1, CS_ERCTL2); - he_writel_mbox(he_dev, 0x1280, CS_ERSTAT0); - he_writel_mbox(he_dev, 0x64b1, CS_ERSTAT1); - he_writel_mbox(he_dev, 0xf424, CS_RTFWR); - - he_writel_mbox(he_dev, 0x4680, CS_RTATR); - - /* table 5.8 */ - he_writel_mbox(he_dev, 0x000563b7, CS_TFBSET); - he_writel_mbox(he_dev, 0x64b1, CS_WCRMAX); - he_writel_mbox(he_dev, 0x5ab1, CS_WCRMIN); - he_writel_mbox(he_dev, 0xe4b1, CS_WCRINC); - he_writel_mbox(he_dev, 0xdab1, CS_WCRDEC); - he_writel_mbox(he_dev, 0x64b1, CS_WCRCEIL); - - /* table 5.9 */ - he_writel_mbox(he_dev, 0x6, CS_OTPPER); - he_writel_mbox(he_dev, 0x1e, CS_OTWPER); - } - - he_writel_mbox(he_dev, 0x8, CS_OTTLIM); - - for (reg = 0; reg < 0x8; ++reg) - he_writel_mbox(he_dev, 0x0, CS_HGRRT0 + reg); - -} - -static int he_init_cs_block_rcm(struct he_dev *he_dev) -{ - unsigned (*rategrid)[16][16]; - unsigned rate, delta; - int i, j, reg; - - unsigned rate_atmf, exp, man; - unsigned long long rate_cps; - int mult, buf, buf_limit = 4; - - rategrid = kmalloc( sizeof(unsigned) * 16 * 16, GFP_KERNEL); - if (!rategrid) - return -ENOMEM; - - /* initialize rate grid group table */ - - for (reg = 0x0; reg < 0xff; ++reg) - he_writel_rcm(he_dev, 0x0, CONFIG_RCMABR + reg); - - /* initialize rate controller groups */ - - for (reg = 0x100; reg < 0x1ff; ++reg) - he_writel_rcm(he_dev, 0x0, CONFIG_RCMABR + reg); - - /* initialize tNrm lookup table */ - - /* the manual makes reference to a routine in a sample driver - for proper configuration; fortunately, we only need this - in order to support abr connection */ - - /* initialize rate to group table */ - - rate = he_dev->atm_dev->link_rate; - delta = rate / 32; - - /* - * 2.4 transmit internal functions - * - * we construct a copy of the rate grid used by the scheduler - * in order to construct the rate to group table below - */ - - for (j = 0; j < 16; j++) { - (*rategrid)[0][j] = rate; - rate -= delta; - } - - for (i = 1; i < 16; i++) - for (j = 0; j < 16; j++) - if (i > 14) - (*rategrid)[i][j] = (*rategrid)[i - 1][j] / 4; - else - (*rategrid)[i][j] = (*rategrid)[i - 1][j] / 2; - - /* - * 2.4 transmit internal function - * - * this table maps the upper 5 bits of exponent and mantissa - * of the atm forum representation of the rate into an index - * on rate grid - */ - - rate_atmf = 0; - while (rate_atmf < 0x400) { - man = (rate_atmf & 0x1f) << 4; - exp = rate_atmf >> 5; - - /* - instead of '/ 512', use '>> 9' to prevent a call - to divdu3 on x86 platforms - */ - rate_cps = (unsigned long long) (1UL << exp) * (man + 512) >> 9; - - if (rate_cps < 10) - rate_cps = 10; /* 2.2.1 minimum payload rate is 10 cps */ - - for (i = 255; i > 0; i--) - if ((*rategrid)[i/16][i%16] >= rate_cps) - break; /* pick nearest rate instead? */ - - /* - * each table entry is 16 bits: (rate grid index (8 bits) - * and a buffer limit (8 bits) - * there are two table entries in each 32-bit register - */ - -#ifdef notdef - buf = rate_cps * he_dev->tx_numbuffs / - (he_dev->atm_dev->link_rate * 2); -#else - /* this is pretty, but avoids _divdu3 and is mostly correct */ - mult = he_dev->atm_dev->link_rate / ATM_OC3_PCR; - if (rate_cps > (272ULL * mult)) - buf = 4; - else if (rate_cps > (204ULL * mult)) - buf = 3; - else if (rate_cps > (136ULL * mult)) - buf = 2; - else if (rate_cps > (68ULL * mult)) - buf = 1; - else - buf = 0; -#endif - if (buf > buf_limit) - buf = buf_limit; - reg = (reg << 16) | ((i << 8) | buf); - -#define RTGTBL_OFFSET 0x400 - - if (rate_atmf & 0x1) - he_writel_rcm(he_dev, reg, - CONFIG_RCMABR + RTGTBL_OFFSET + (rate_atmf >> 1)); - - ++rate_atmf; - } - - kfree(rategrid); - return 0; -} - -static int he_init_group(struct he_dev *he_dev, int group) -{ - struct he_buff *heb, *next; - dma_addr_t mapping; - int i; - - he_writel(he_dev, 0x0, G0_RBPS_S + (group * 32)); - he_writel(he_dev, 0x0, G0_RBPS_T + (group * 32)); - he_writel(he_dev, 0x0, G0_RBPS_QI + (group * 32)); - he_writel(he_dev, RBP_THRESH(0x1) | RBP_QSIZE(0x0), - G0_RBPS_BS + (group * 32)); - - /* bitmap table */ - he_dev->rbpl_table = bitmap_zalloc(RBPL_TABLE_SIZE, GFP_KERNEL); - if (!he_dev->rbpl_table) { - hprintk("unable to allocate rbpl bitmap table\n"); - return -ENOMEM; - } - - /* rbpl_virt 64-bit pointers */ - he_dev->rbpl_virt = kmalloc_objs(*he_dev->rbpl_virt, RBPL_TABLE_SIZE); - if (!he_dev->rbpl_virt) { - hprintk("unable to allocate rbpl virt table\n"); - goto out_free_rbpl_table; - } - - /* large buffer pool */ - he_dev->rbpl_pool = dma_pool_create("rbpl", &he_dev->pci_dev->dev, - CONFIG_RBPL_BUFSIZE, 64, 0); - if (he_dev->rbpl_pool == NULL) { - hprintk("unable to create rbpl pool\n"); - goto out_free_rbpl_virt; - } - - he_dev->rbpl_base = dma_alloc_coherent(&he_dev->pci_dev->dev, - CONFIG_RBPL_SIZE * sizeof(struct he_rbp), - &he_dev->rbpl_phys, GFP_KERNEL); - if (he_dev->rbpl_base == NULL) { - hprintk("failed to alloc rbpl_base\n"); - goto out_destroy_rbpl_pool; - } - - INIT_LIST_HEAD(&he_dev->rbpl_outstanding); - - for (i = 0; i < CONFIG_RBPL_SIZE; ++i) { - - heb = dma_pool_alloc(he_dev->rbpl_pool, GFP_KERNEL, &mapping); - if (!heb) - goto out_free_rbpl; - heb->mapping = mapping; - list_add(&heb->entry, &he_dev->rbpl_outstanding); - - set_bit(i, he_dev->rbpl_table); - he_dev->rbpl_virt[i] = heb; - he_dev->rbpl_hint = i + 1; - he_dev->rbpl_base[i].idx = i << RBP_IDX_OFFSET; - he_dev->rbpl_base[i].phys = mapping + offsetof(struct he_buff, data); - } - he_dev->rbpl_tail = &he_dev->rbpl_base[CONFIG_RBPL_SIZE - 1]; - - he_writel(he_dev, he_dev->rbpl_phys, G0_RBPL_S + (group * 32)); - he_writel(he_dev, RBPL_MASK(he_dev->rbpl_tail), - G0_RBPL_T + (group * 32)); - he_writel(he_dev, (CONFIG_RBPL_BUFSIZE - sizeof(struct he_buff))/4, - G0_RBPL_BS + (group * 32)); - he_writel(he_dev, - RBP_THRESH(CONFIG_RBPL_THRESH) | - RBP_QSIZE(CONFIG_RBPL_SIZE - 1) | - RBP_INT_ENB, - G0_RBPL_QI + (group * 32)); - - /* rx buffer ready queue */ - - he_dev->rbrq_base = dma_alloc_coherent(&he_dev->pci_dev->dev, - CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq), - &he_dev->rbrq_phys, GFP_KERNEL); - if (he_dev->rbrq_base == NULL) { - hprintk("failed to allocate rbrq\n"); - goto out_free_rbpl; - } - - he_dev->rbrq_head = he_dev->rbrq_base; - he_writel(he_dev, he_dev->rbrq_phys, G0_RBRQ_ST + (group * 16)); - he_writel(he_dev, 0, G0_RBRQ_H + (group * 16)); - he_writel(he_dev, - RBRQ_THRESH(CONFIG_RBRQ_THRESH) | RBRQ_SIZE(CONFIG_RBRQ_SIZE - 1), - G0_RBRQ_Q + (group * 16)); - if (irq_coalesce) { - hprintk("coalescing interrupts\n"); - he_writel(he_dev, RBRQ_TIME(768) | RBRQ_COUNT(7), - G0_RBRQ_I + (group * 16)); - } else - he_writel(he_dev, RBRQ_TIME(0) | RBRQ_COUNT(1), - G0_RBRQ_I + (group * 16)); - - /* tx buffer ready queue */ - - he_dev->tbrq_base = dma_alloc_coherent(&he_dev->pci_dev->dev, - CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), - &he_dev->tbrq_phys, GFP_KERNEL); - if (he_dev->tbrq_base == NULL) { - hprintk("failed to allocate tbrq\n"); - goto out_free_rbpq_base; - } - - he_dev->tbrq_head = he_dev->tbrq_base; - - he_writel(he_dev, he_dev->tbrq_phys, G0_TBRQ_B_T + (group * 16)); - he_writel(he_dev, 0, G0_TBRQ_H + (group * 16)); - he_writel(he_dev, CONFIG_TBRQ_SIZE - 1, G0_TBRQ_S + (group * 16)); - he_writel(he_dev, CONFIG_TBRQ_THRESH, G0_TBRQ_THRESH + (group * 16)); - - return 0; - -out_free_rbpq_base: - dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBRQ_SIZE * - sizeof(struct he_rbrq), he_dev->rbrq_base, - he_dev->rbrq_phys); -out_free_rbpl: - list_for_each_entry_safe(heb, next, &he_dev->rbpl_outstanding, entry) - dma_pool_free(he_dev->rbpl_pool, heb, heb->mapping); - - dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBPL_SIZE * - sizeof(struct he_rbp), he_dev->rbpl_base, - he_dev->rbpl_phys); -out_destroy_rbpl_pool: - dma_pool_destroy(he_dev->rbpl_pool); -out_free_rbpl_virt: - kfree(he_dev->rbpl_virt); -out_free_rbpl_table: - bitmap_free(he_dev->rbpl_table); - - return -ENOMEM; -} - -static int he_init_irq(struct he_dev *he_dev) -{ - int i; - - /* 2.9.3.5 tail offset for each interrupt queue is located after the - end of the interrupt queue */ - - he_dev->irq_base = dma_alloc_coherent(&he_dev->pci_dev->dev, - (CONFIG_IRQ_SIZE + 1) * sizeof(struct he_irq), - &he_dev->irq_phys, GFP_KERNEL); - if (he_dev->irq_base == NULL) { - hprintk("failed to allocate irq\n"); - return -ENOMEM; - } - he_dev->irq_tailoffset = (unsigned *) - &he_dev->irq_base[CONFIG_IRQ_SIZE]; - *he_dev->irq_tailoffset = 0; - he_dev->irq_head = he_dev->irq_base; - he_dev->irq_tail = he_dev->irq_base; - - for (i = 0; i < CONFIG_IRQ_SIZE; ++i) - he_dev->irq_base[i].isw = ITYPE_INVALID; - - he_writel(he_dev, he_dev->irq_phys, IRQ0_BASE); - he_writel(he_dev, - IRQ_SIZE(CONFIG_IRQ_SIZE) | IRQ_THRESH(CONFIG_IRQ_THRESH), - IRQ0_HEAD); - he_writel(he_dev, IRQ_INT_A | IRQ_TYPE_LINE, IRQ0_CNTL); - he_writel(he_dev, 0x0, IRQ0_DATA); - - he_writel(he_dev, 0x0, IRQ1_BASE); - he_writel(he_dev, 0x0, IRQ1_HEAD); - he_writel(he_dev, 0x0, IRQ1_CNTL); - he_writel(he_dev, 0x0, IRQ1_DATA); - - he_writel(he_dev, 0x0, IRQ2_BASE); - he_writel(he_dev, 0x0, IRQ2_HEAD); - he_writel(he_dev, 0x0, IRQ2_CNTL); - he_writel(he_dev, 0x0, IRQ2_DATA); - - he_writel(he_dev, 0x0, IRQ3_BASE); - he_writel(he_dev, 0x0, IRQ3_HEAD); - he_writel(he_dev, 0x0, IRQ3_CNTL); - he_writel(he_dev, 0x0, IRQ3_DATA); - - /* 2.9.3.2 interrupt queue mapping registers */ - - he_writel(he_dev, 0x0, GRP_10_MAP); - he_writel(he_dev, 0x0, GRP_32_MAP); - he_writel(he_dev, 0x0, GRP_54_MAP); - he_writel(he_dev, 0x0, GRP_76_MAP); - - if (request_irq(he_dev->pci_dev->irq, - he_irq_handler, IRQF_SHARED, DEV_LABEL, he_dev)) { - hprintk("irq %d already in use\n", he_dev->pci_dev->irq); - return -EINVAL; - } - - he_dev->irq = he_dev->pci_dev->irq; - - return 0; -} - -static int he_start(struct atm_dev *dev) -{ - struct he_dev *he_dev; - struct pci_dev *pci_dev; - unsigned long membase; - - u16 command; - u32 gen_cntl_0, host_cntl, lb_swap; - u8 cache_size, timer; - - unsigned err; - unsigned int status, reg; - int i, group; - - he_dev = HE_DEV(dev); - pci_dev = he_dev->pci_dev; - - membase = pci_resource_start(pci_dev, 0); - HPRINTK("membase = 0x%lx irq = %d.\n", membase, pci_dev->irq); - - /* - * pci bus controller initialization - */ - - /* 4.3 pci bus controller-specific initialization */ - if (pci_read_config_dword(pci_dev, GEN_CNTL_0, &gen_cntl_0) != 0) { - hprintk("can't read GEN_CNTL_0\n"); - return -EINVAL; - } - gen_cntl_0 |= (MRL_ENB | MRM_ENB | IGNORE_TIMEOUT); - if (pci_write_config_dword(pci_dev, GEN_CNTL_0, gen_cntl_0) != 0) { - hprintk("can't write GEN_CNTL_0.\n"); - return -EINVAL; - } - - if (pci_read_config_word(pci_dev, PCI_COMMAND, &command) != 0) { - hprintk("can't read PCI_COMMAND.\n"); - return -EINVAL; - } - - command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE); - if (pci_write_config_word(pci_dev, PCI_COMMAND, command) != 0) { - hprintk("can't enable memory.\n"); - return -EINVAL; - } - - if (pci_read_config_byte(pci_dev, PCI_CACHE_LINE_SIZE, &cache_size)) { - hprintk("can't read cache line size?\n"); - return -EINVAL; - } - - if (cache_size < 16) { - cache_size = 16; - if (pci_write_config_byte(pci_dev, PCI_CACHE_LINE_SIZE, cache_size)) - hprintk("can't set cache line size to %d\n", cache_size); - } - - if (pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &timer)) { - hprintk("can't read latency timer?\n"); - return -EINVAL; - } - - /* from table 3.9 - * - * LAT_TIMER = 1 + AVG_LAT + BURST_SIZE/BUS_SIZE - * - * AVG_LAT: The average first data read/write latency [maximum 16 clock cycles] - * BURST_SIZE: 1536 bytes (read) for 622, 768 bytes (read) for 155 [192 clock cycles] - * - */ -#define LAT_TIMER 209 - if (timer < LAT_TIMER) { - HPRINTK("latency timer was %d, setting to %d\n", timer, LAT_TIMER); - timer = LAT_TIMER; - if (pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, timer)) - hprintk("can't set latency timer to %d\n", timer); - } - - if (!(he_dev->membase = ioremap(membase, HE_REGMAP_SIZE))) { - hprintk("can't set up page mapping\n"); - return -EINVAL; - } - - /* 4.4 card reset */ - he_writel(he_dev, 0x0, RESET_CNTL); - he_writel(he_dev, 0xff, RESET_CNTL); - - msleep(16); /* 16 ms */ - status = he_readl(he_dev, RESET_CNTL); - if ((status & BOARD_RST_STATUS) == 0) { - hprintk("reset failed\n"); - return -EINVAL; - } - - /* 4.5 set bus width */ - host_cntl = he_readl(he_dev, HOST_CNTL); - if (host_cntl & PCI_BUS_SIZE64) - gen_cntl_0 |= ENBL_64; - else - gen_cntl_0 &= ~ENBL_64; - - if (disable64 == 1) { - hprintk("disabling 64-bit pci bus transfers\n"); - gen_cntl_0 &= ~ENBL_64; - } - - if (gen_cntl_0 & ENBL_64) - hprintk("64-bit transfers enabled\n"); - - pci_write_config_dword(pci_dev, GEN_CNTL_0, gen_cntl_0); - - /* 4.7 read prom contents */ - for (i = 0; i < PROD_ID_LEN; ++i) - he_dev->prod_id[i] = read_prom_byte(he_dev, PROD_ID + i); - - he_dev->media = read_prom_byte(he_dev, MEDIA); - - for (i = 0; i < 6; ++i) - dev->esi[i] = read_prom_byte(he_dev, MAC_ADDR + i); - - hprintk("%s%s, %pM\n", he_dev->prod_id, - he_dev->media & 0x40 ? "SM" : "MM", dev->esi); - he_dev->atm_dev->link_rate = he_is622(he_dev) ? - ATM_OC12_PCR : ATM_OC3_PCR; - - /* 4.6 set host endianess */ - lb_swap = he_readl(he_dev, LB_SWAP); - if (he_is622(he_dev)) - lb_swap &= ~XFER_SIZE; /* 4 cells */ - else - lb_swap |= XFER_SIZE; /* 8 cells */ -#ifdef __BIG_ENDIAN - lb_swap |= DESC_WR_SWAP | INTR_SWAP | BIG_ENDIAN_HOST; -#else - lb_swap &= ~(DESC_WR_SWAP | INTR_SWAP | BIG_ENDIAN_HOST | - DATA_WR_SWAP | DATA_RD_SWAP | DESC_RD_SWAP); -#endif /* __BIG_ENDIAN */ - he_writel(he_dev, lb_swap, LB_SWAP); - - /* 4.8 sdram controller initialization */ - he_writel(he_dev, he_is622(he_dev) ? LB_64_ENB : 0x0, SDRAM_CTL); - - /* 4.9 initialize rnum value */ - lb_swap |= SWAP_RNUM_MAX(0xf); - he_writel(he_dev, lb_swap, LB_SWAP); - - /* 4.10 initialize the interrupt queues */ - if ((err = he_init_irq(he_dev)) != 0) - return err; - - /* 4.11 enable pci bus controller state machines */ - host_cntl |= (OUTFF_ENB | CMDFF_ENB | - QUICK_RD_RETRY | QUICK_WR_RETRY | PERR_INT_ENB); - he_writel(he_dev, host_cntl, HOST_CNTL); - - gen_cntl_0 |= INT_PROC_ENBL|INIT_ENB; - pci_write_config_dword(pci_dev, GEN_CNTL_0, gen_cntl_0); - - /* - * atm network controller initialization - */ - - /* 5.1.1 generic configuration state */ - - /* - * local (cell) buffer memory map - * - * HE155 HE622 - * - * 0 ____________1023 bytes 0 _______________________2047 bytes - * | | | | | - * | utility | | rx0 | | - * 5|____________| 255|___________________| u | - * 6| | 256| | t | - * | | | | i | - * | rx0 | row | tx | l | - * | | | | i | - * | | 767|___________________| t | - * 517|____________| 768| | y | - * row 518| | | rx1 | | - * | | 1023|___________________|___| - * | | - * | tx | - * | | - * | | - * 1535|____________| - * 1536| | - * | rx1 | - * 2047|____________| - * - */ - - /* total 4096 connections */ - he_dev->vcibits = CONFIG_DEFAULT_VCIBITS; - he_dev->vpibits = CONFIG_DEFAULT_VPIBITS; - - if (nvpibits != -1 && nvcibits != -1 && nvpibits+nvcibits != HE_MAXCIDBITS) { - hprintk("nvpibits + nvcibits != %d\n", HE_MAXCIDBITS); - return -ENODEV; - } - - if (nvpibits != -1) { - he_dev->vpibits = nvpibits; - he_dev->vcibits = HE_MAXCIDBITS - nvpibits; - } - - if (nvcibits != -1) { - he_dev->vcibits = nvcibits; - he_dev->vpibits = HE_MAXCIDBITS - nvcibits; - } - - - if (he_is622(he_dev)) { - he_dev->cells_per_row = 40; - he_dev->bytes_per_row = 2048; - he_dev->r0_numrows = 256; - he_dev->tx_numrows = 512; - he_dev->r1_numrows = 256; - he_dev->r0_startrow = 0; - he_dev->tx_startrow = 256; - he_dev->r1_startrow = 768; - } else { - he_dev->cells_per_row = 20; - he_dev->bytes_per_row = 1024; - he_dev->r0_numrows = 512; - he_dev->tx_numrows = 1018; - he_dev->r1_numrows = 512; - he_dev->r0_startrow = 6; - he_dev->tx_startrow = 518; - he_dev->r1_startrow = 1536; - } - - he_dev->cells_per_lbuf = 4; - he_dev->buffer_limit = 4; - he_dev->r0_numbuffs = he_dev->r0_numrows * - he_dev->cells_per_row / he_dev->cells_per_lbuf; - if (he_dev->r0_numbuffs > 2560) - he_dev->r0_numbuffs = 2560; - - he_dev->r1_numbuffs = he_dev->r1_numrows * - he_dev->cells_per_row / he_dev->cells_per_lbuf; - if (he_dev->r1_numbuffs > 2560) - he_dev->r1_numbuffs = 2560; - - he_dev->tx_numbuffs = he_dev->tx_numrows * - he_dev->cells_per_row / he_dev->cells_per_lbuf; - if (he_dev->tx_numbuffs > 5120) - he_dev->tx_numbuffs = 5120; - - /* 5.1.2 configure hardware dependent registers */ - - he_writel(he_dev, - SLICE_X(0x2) | ARB_RNUM_MAX(0xf) | TH_PRTY(0x3) | - RH_PRTY(0x3) | TL_PRTY(0x2) | RL_PRTY(0x1) | - (he_is622(he_dev) ? BUS_MULTI(0x28) : BUS_MULTI(0x46)) | - (he_is622(he_dev) ? NET_PREF(0x50) : NET_PREF(0x8c)), - LBARB); - - he_writel(he_dev, BANK_ON | - (he_is622(he_dev) ? (REF_RATE(0x384) | WIDE_DATA) : REF_RATE(0x150)), - SDRAMCON); - - he_writel(he_dev, - (he_is622(he_dev) ? RM_BANK_WAIT(1) : RM_BANK_WAIT(0)) | - RM_RW_WAIT(1), RCMCONFIG); - he_writel(he_dev, - (he_is622(he_dev) ? TM_BANK_WAIT(2) : TM_BANK_WAIT(1)) | - TM_RW_WAIT(1), TCMCONFIG); - - he_writel(he_dev, he_dev->cells_per_lbuf * ATM_CELL_PAYLOAD, LB_CONFIG); - - he_writel(he_dev, - (he_is622(he_dev) ? UT_RD_DELAY(8) : UT_RD_DELAY(0)) | - (he_is622(he_dev) ? RC_UT_MODE(0) : RC_UT_MODE(1)) | - RX_VALVP(he_dev->vpibits) | - RX_VALVC(he_dev->vcibits), RC_CONFIG); - - he_writel(he_dev, DRF_THRESH(0x20) | - (he_is622(he_dev) ? TX_UT_MODE(0) : TX_UT_MODE(1)) | - TX_VCI_MASK(he_dev->vcibits) | - LBFREE_CNT(he_dev->tx_numbuffs), TX_CONFIG); - - he_writel(he_dev, 0x0, TXAAL5_PROTO); - - he_writel(he_dev, PHY_INT_ENB | - (he_is622(he_dev) ? PTMR_PRE(67 - 1) : PTMR_PRE(50 - 1)), - RH_CONFIG); - - /* 5.1.3 initialize connection memory */ - - for (i = 0; i < TCM_MEM_SIZE; ++i) - he_writel_tcm(he_dev, 0, i); - - for (i = 0; i < RCM_MEM_SIZE; ++i) - he_writel_rcm(he_dev, 0, i); - - /* - * transmit connection memory map - * - * tx memory - * 0x0 ___________________ - * | | - * | | - * | TSRa | - * | | - * | | - * 0x8000|___________________| - * | | - * | TSRb | - * 0xc000|___________________| - * | | - * | TSRc | - * 0xe000|___________________| - * | TSRd | - * 0xf000|___________________| - * | tmABR | - * 0x10000|___________________| - * | | - * | tmTPD | - * |___________________| - * | | - * .... - * 0x1ffff|___________________| - * - * - */ - - he_writel(he_dev, CONFIG_TSRB, TSRB_BA); - he_writel(he_dev, CONFIG_TSRC, TSRC_BA); - he_writel(he_dev, CONFIG_TSRD, TSRD_BA); - he_writel(he_dev, CONFIG_TMABR, TMABR_BA); - he_writel(he_dev, CONFIG_TPDBA, TPD_BA); - - - /* - * receive connection memory map - * - * 0x0 ___________________ - * | | - * | | - * | RSRa | - * | | - * | | - * 0x8000|___________________| - * | | - * | rx0/1 | - * | LBM | link lists of local - * | tx | buffer memory - * | | - * 0xd000|___________________| - * | | - * | rmABR | - * 0xe000|___________________| - * | | - * | RSRb | - * |___________________| - * | | - * .... - * 0xffff|___________________| - */ - - he_writel(he_dev, 0x08000, RCMLBM_BA); - he_writel(he_dev, 0x0e000, RCMRSRB_BA); - he_writel(he_dev, 0x0d800, RCMABR_BA); - - /* 5.1.4 initialize local buffer free pools linked lists */ - - he_init_rx_lbfp0(he_dev); - he_init_rx_lbfp1(he_dev); - - he_writel(he_dev, 0x0, RLBC_H); - he_writel(he_dev, 0x0, RLBC_T); - he_writel(he_dev, 0x0, RLBC_H2); - - he_writel(he_dev, 512, RXTHRSH); /* 10% of r0+r1 buffers */ - he_writel(he_dev, 256, LITHRSH); /* 5% of r0+r1 buffers */ - - he_init_tx_lbfp(he_dev); - - he_writel(he_dev, he_is622(he_dev) ? 0x104780 : 0x800, UBUFF_BA); - - /* 5.1.5 initialize intermediate receive queues */ - - if (he_is622(he_dev)) { - he_writel(he_dev, 0x000f, G0_INMQ_S); - he_writel(he_dev, 0x200f, G0_INMQ_L); - - he_writel(he_dev, 0x001f, G1_INMQ_S); - he_writel(he_dev, 0x201f, G1_INMQ_L); - - he_writel(he_dev, 0x002f, G2_INMQ_S); - he_writel(he_dev, 0x202f, G2_INMQ_L); - - he_writel(he_dev, 0x003f, G3_INMQ_S); - he_writel(he_dev, 0x203f, G3_INMQ_L); - - he_writel(he_dev, 0x004f, G4_INMQ_S); - he_writel(he_dev, 0x204f, G4_INMQ_L); - - he_writel(he_dev, 0x005f, G5_INMQ_S); - he_writel(he_dev, 0x205f, G5_INMQ_L); - - he_writel(he_dev, 0x006f, G6_INMQ_S); - he_writel(he_dev, 0x206f, G6_INMQ_L); - - he_writel(he_dev, 0x007f, G7_INMQ_S); - he_writel(he_dev, 0x207f, G7_INMQ_L); - } else { - he_writel(he_dev, 0x0000, G0_INMQ_S); - he_writel(he_dev, 0x0008, G0_INMQ_L); - - he_writel(he_dev, 0x0001, G1_INMQ_S); - he_writel(he_dev, 0x0009, G1_INMQ_L); - - he_writel(he_dev, 0x0002, G2_INMQ_S); - he_writel(he_dev, 0x000a, G2_INMQ_L); - - he_writel(he_dev, 0x0003, G3_INMQ_S); - he_writel(he_dev, 0x000b, G3_INMQ_L); - - he_writel(he_dev, 0x0004, G4_INMQ_S); - he_writel(he_dev, 0x000c, G4_INMQ_L); - - he_writel(he_dev, 0x0005, G5_INMQ_S); - he_writel(he_dev, 0x000d, G5_INMQ_L); - - he_writel(he_dev, 0x0006, G6_INMQ_S); - he_writel(he_dev, 0x000e, G6_INMQ_L); - - he_writel(he_dev, 0x0007, G7_INMQ_S); - he_writel(he_dev, 0x000f, G7_INMQ_L); - } - - /* 5.1.6 application tunable parameters */ - - he_writel(he_dev, 0x0, MCC); - he_writel(he_dev, 0x0, OEC); - he_writel(he_dev, 0x0, DCC); - he_writel(he_dev, 0x0, CEC); - - /* 5.1.7 cs block initialization */ - - he_init_cs_block(he_dev); - - /* 5.1.8 cs block connection memory initialization */ - - if (he_init_cs_block_rcm(he_dev) < 0) - return -ENOMEM; - - /* 5.1.10 initialize host structures */ - - he_init_tpdrq(he_dev); - - he_dev->tpd_pool = dma_pool_create("tpd", &he_dev->pci_dev->dev, - sizeof(struct he_tpd), TPD_ALIGNMENT, 0); - if (he_dev->tpd_pool == NULL) { - hprintk("unable to create tpd dma_pool\n"); - return -ENOMEM; - } - - INIT_LIST_HEAD(&he_dev->outstanding_tpds); - - if (he_init_group(he_dev, 0) != 0) - return -ENOMEM; - - for (group = 1; group < HE_NUM_GROUPS; ++group) { - he_writel(he_dev, 0x0, G0_RBPS_S + (group * 32)); - he_writel(he_dev, 0x0, G0_RBPS_T + (group * 32)); - he_writel(he_dev, 0x0, G0_RBPS_QI + (group * 32)); - he_writel(he_dev, RBP_THRESH(0x1) | RBP_QSIZE(0x0), - G0_RBPS_BS + (group * 32)); - - he_writel(he_dev, 0x0, G0_RBPL_S + (group * 32)); - he_writel(he_dev, 0x0, G0_RBPL_T + (group * 32)); - he_writel(he_dev, RBP_THRESH(0x1) | RBP_QSIZE(0x0), - G0_RBPL_QI + (group * 32)); - he_writel(he_dev, 0x0, G0_RBPL_BS + (group * 32)); - - he_writel(he_dev, 0x0, G0_RBRQ_ST + (group * 16)); - he_writel(he_dev, 0x0, G0_RBRQ_H + (group * 16)); - he_writel(he_dev, RBRQ_THRESH(0x1) | RBRQ_SIZE(0x0), - G0_RBRQ_Q + (group * 16)); - he_writel(he_dev, 0x0, G0_RBRQ_I + (group * 16)); - - he_writel(he_dev, 0x0, G0_TBRQ_B_T + (group * 16)); - he_writel(he_dev, 0x0, G0_TBRQ_H + (group * 16)); - he_writel(he_dev, TBRQ_THRESH(0x1), - G0_TBRQ_THRESH + (group * 16)); - he_writel(he_dev, 0x0, G0_TBRQ_S + (group * 16)); - } - - /* host status page */ - - he_dev->hsp = dma_alloc_coherent(&he_dev->pci_dev->dev, - sizeof(struct he_hsp), - &he_dev->hsp_phys, GFP_KERNEL); - if (he_dev->hsp == NULL) { - hprintk("failed to allocate host status page\n"); - return -ENOMEM; - } - he_writel(he_dev, he_dev->hsp_phys, HSP_BA); - - /* initialize framer */ - -#ifdef CONFIG_ATM_HE_USE_SUNI - if (he_isMM(he_dev)) - suni_init(he_dev->atm_dev); - if (he_dev->atm_dev->phy && he_dev->atm_dev->phy->start) - he_dev->atm_dev->phy->start(he_dev->atm_dev); -#endif /* CONFIG_ATM_HE_USE_SUNI */ - - if (sdh) { - /* this really should be in suni.c but for now... */ - int val; - - val = he_phy_get(he_dev->atm_dev, SUNI_TPOP_APM); - val = (val & ~SUNI_TPOP_APM_S) | (SUNI_TPOP_S_SDH << SUNI_TPOP_APM_S_SHIFT); - he_phy_put(he_dev->atm_dev, val, SUNI_TPOP_APM); - he_phy_put(he_dev->atm_dev, SUNI_TACP_IUCHP_CLP, SUNI_TACP_IUCHP); - } - - /* 5.1.12 enable transmit and receive */ - - reg = he_readl_mbox(he_dev, CS_ERCTL0); - reg |= TX_ENABLE|ER_ENABLE; - he_writel_mbox(he_dev, reg, CS_ERCTL0); - - reg = he_readl(he_dev, RC_CONFIG); - reg |= RX_ENABLE; - he_writel(he_dev, reg, RC_CONFIG); - - for (i = 0; i < HE_NUM_CS_STPER; ++i) { - he_dev->cs_stper[i].inuse = 0; - he_dev->cs_stper[i].pcr = -1; - } - he_dev->total_bw = 0; - - - /* atm linux initialization */ - - he_dev->atm_dev->ci_range.vpi_bits = he_dev->vpibits; - he_dev->atm_dev->ci_range.vci_bits = he_dev->vcibits; - - he_dev->irq_peak = 0; - he_dev->rbrq_peak = 0; - he_dev->rbpl_peak = 0; - he_dev->tbrq_peak = 0; - - HPRINTK("hell bent for leather!\n"); - - return 0; -} - -static void -he_stop(struct he_dev *he_dev) -{ - struct he_buff *heb, *next; - struct pci_dev *pci_dev; - u32 gen_cntl_0, reg; - u16 command; - - pci_dev = he_dev->pci_dev; - - /* disable interrupts */ - - if (he_dev->membase) { - pci_read_config_dword(pci_dev, GEN_CNTL_0, &gen_cntl_0); - gen_cntl_0 &= ~(INT_PROC_ENBL | INIT_ENB); - pci_write_config_dword(pci_dev, GEN_CNTL_0, gen_cntl_0); - - tasklet_disable(&he_dev->tasklet); - - /* disable recv and transmit */ - - reg = he_readl_mbox(he_dev, CS_ERCTL0); - reg &= ~(TX_ENABLE|ER_ENABLE); - he_writel_mbox(he_dev, reg, CS_ERCTL0); - - reg = he_readl(he_dev, RC_CONFIG); - reg &= ~(RX_ENABLE); - he_writel(he_dev, reg, RC_CONFIG); - } - -#ifdef CONFIG_ATM_HE_USE_SUNI - if (he_dev->atm_dev->phy && he_dev->atm_dev->phy->stop) - he_dev->atm_dev->phy->stop(he_dev->atm_dev); -#endif /* CONFIG_ATM_HE_USE_SUNI */ - - if (he_dev->irq) - free_irq(he_dev->irq, he_dev); - - if (he_dev->irq_base) - dma_free_coherent(&he_dev->pci_dev->dev, (CONFIG_IRQ_SIZE + 1) - * sizeof(struct he_irq), he_dev->irq_base, he_dev->irq_phys); - - if (he_dev->hsp) - dma_free_coherent(&he_dev->pci_dev->dev, sizeof(struct he_hsp), - he_dev->hsp, he_dev->hsp_phys); - - if (he_dev->rbpl_base) { - list_for_each_entry_safe(heb, next, &he_dev->rbpl_outstanding, entry) - dma_pool_free(he_dev->rbpl_pool, heb, heb->mapping); - - dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBPL_SIZE - * sizeof(struct he_rbp), he_dev->rbpl_base, he_dev->rbpl_phys); - } - - kfree(he_dev->rbpl_virt); - bitmap_free(he_dev->rbpl_table); - dma_pool_destroy(he_dev->rbpl_pool); - - if (he_dev->rbrq_base) - dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq), - he_dev->rbrq_base, he_dev->rbrq_phys); - - if (he_dev->tbrq_base) - dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), - he_dev->tbrq_base, he_dev->tbrq_phys); - - if (he_dev->tpdrq_base) - dma_free_coherent(&he_dev->pci_dev->dev, - CONFIG_TPDRQ_SIZE * sizeof(struct he_tpdrq), - he_dev->tpdrq_base, he_dev->tpdrq_phys); - - dma_pool_destroy(he_dev->tpd_pool); - - if (he_dev->pci_dev) { - pci_read_config_word(he_dev->pci_dev, PCI_COMMAND, &command); - command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - pci_write_config_word(he_dev->pci_dev, PCI_COMMAND, command); - } - - if (he_dev->membase) - iounmap(he_dev->membase); -} - -static struct he_tpd * -__alloc_tpd(struct he_dev *he_dev) -{ - struct he_tpd *tpd; - dma_addr_t mapping; - - tpd = dma_pool_alloc(he_dev->tpd_pool, GFP_ATOMIC, &mapping); - if (tpd == NULL) - return NULL; - - tpd->status = TPD_ADDR(mapping); - tpd->reserved = 0; - tpd->iovec[0].addr = 0; tpd->iovec[0].len = 0; - tpd->iovec[1].addr = 0; tpd->iovec[1].len = 0; - tpd->iovec[2].addr = 0; tpd->iovec[2].len = 0; - - return tpd; -} - -#define AAL5_LEN(buf,len) \ - ((((unsigned char *)(buf))[(len)-6] << 8) | \ - (((unsigned char *)(buf))[(len)-5])) - -/* 2.10.1.2 receive - * - * aal5 packets can optionally return the tcp checksum in the lower - * 16 bits of the crc (RSR0_TCP_CKSUM) - */ - -#define TCP_CKSUM(buf,len) \ - ((((unsigned char *)(buf))[(len)-2] << 8) | \ - (((unsigned char *)(buf))[(len-1)])) - -static int -he_service_rbrq(struct he_dev *he_dev, int group) -{ - struct he_rbrq *rbrq_tail = (struct he_rbrq *) - ((unsigned long)he_dev->rbrq_base | - he_dev->hsp->group[group].rbrq_tail); - unsigned cid, lastcid = -1; - struct sk_buff *skb; - struct atm_vcc *vcc = NULL; - struct he_vcc *he_vcc; - struct he_buff *heb, *next; - int i; - int pdus_assembled = 0; - int updated = 0; - - read_lock(&vcc_sklist_lock); - while (he_dev->rbrq_head != rbrq_tail) { - ++updated; - - HPRINTK("%p rbrq%d 0x%x len=%d cid=0x%x %s%s%s%s%s%s\n", - he_dev->rbrq_head, group, - RBRQ_ADDR(he_dev->rbrq_head), - RBRQ_BUFLEN(he_dev->rbrq_head), - RBRQ_CID(he_dev->rbrq_head), - RBRQ_CRC_ERR(he_dev->rbrq_head) ? " CRC_ERR" : "", - RBRQ_LEN_ERR(he_dev->rbrq_head) ? " LEN_ERR" : "", - RBRQ_END_PDU(he_dev->rbrq_head) ? " END_PDU" : "", - RBRQ_AAL5_PROT(he_dev->rbrq_head) ? " AAL5_PROT" : "", - RBRQ_CON_CLOSED(he_dev->rbrq_head) ? " CON_CLOSED" : "", - RBRQ_HBUF_ERR(he_dev->rbrq_head) ? " HBUF_ERR" : ""); - - i = RBRQ_ADDR(he_dev->rbrq_head) >> RBP_IDX_OFFSET; - heb = he_dev->rbpl_virt[i]; - - cid = RBRQ_CID(he_dev->rbrq_head); - if (cid != lastcid) - vcc = __find_vcc(he_dev, cid); - lastcid = cid; - - if (vcc == NULL || (he_vcc = HE_VCC(vcc)) == NULL) { - hprintk("vcc/he_vcc == NULL (cid 0x%x)\n", cid); - if (!RBRQ_HBUF_ERR(he_dev->rbrq_head)) { - clear_bit(i, he_dev->rbpl_table); - list_del(&heb->entry); - dma_pool_free(he_dev->rbpl_pool, heb, heb->mapping); - } - - goto next_rbrq_entry; - } - - if (RBRQ_HBUF_ERR(he_dev->rbrq_head)) { - hprintk("HBUF_ERR! (cid 0x%x)\n", cid); - atomic_inc(&vcc->stats->rx_drop); - goto return_host_buffers; - } - - heb->len = RBRQ_BUFLEN(he_dev->rbrq_head) * 4; - clear_bit(i, he_dev->rbpl_table); - list_move_tail(&heb->entry, &he_vcc->buffers); - he_vcc->pdu_len += heb->len; - - if (RBRQ_CON_CLOSED(he_dev->rbrq_head)) { - lastcid = -1; - HPRINTK("wake_up rx_waitq (cid 0x%x)\n", cid); - wake_up(&he_vcc->rx_waitq); - goto return_host_buffers; - } - - if (!RBRQ_END_PDU(he_dev->rbrq_head)) - goto next_rbrq_entry; - - if (RBRQ_LEN_ERR(he_dev->rbrq_head) - || RBRQ_CRC_ERR(he_dev->rbrq_head)) { - HPRINTK("%s%s (%d.%d)\n", - RBRQ_CRC_ERR(he_dev->rbrq_head) - ? "CRC_ERR " : "", - RBRQ_LEN_ERR(he_dev->rbrq_head) - ? "LEN_ERR" : "", - vcc->vpi, vcc->vci); - atomic_inc(&vcc->stats->rx_err); - goto return_host_buffers; - } - - skb = atm_alloc_charge(vcc, he_vcc->pdu_len + rx_skb_reserve, - GFP_ATOMIC); - if (!skb) { - HPRINTK("charge failed (%d.%d)\n", vcc->vpi, vcc->vci); - goto return_host_buffers; - } - - if (rx_skb_reserve > 0) - skb_reserve(skb, rx_skb_reserve); - - __net_timestamp(skb); - - list_for_each_entry(heb, &he_vcc->buffers, entry) - skb_put_data(skb, &heb->data, heb->len); - - switch (vcc->qos.aal) { - case ATM_AAL0: - /* 2.10.1.5 raw cell receive */ - skb->len = ATM_AAL0_SDU; - skb_set_tail_pointer(skb, skb->len); - break; - case ATM_AAL5: - /* 2.10.1.2 aal5 receive */ - - skb->len = AAL5_LEN(skb->data, he_vcc->pdu_len); - skb_set_tail_pointer(skb, skb->len); -#ifdef USE_CHECKSUM_HW - if (vcc->vpi == 0 && vcc->vci >= ATM_NOT_RSV_VCI) { - skb->ip_summed = CHECKSUM_COMPLETE; - skb->csum = TCP_CKSUM(skb->data, - he_vcc->pdu_len); - } -#endif - break; - } - -#ifdef should_never_happen - if (skb->len > vcc->qos.rxtp.max_sdu) - hprintk("pdu_len (%d) > vcc->qos.rxtp.max_sdu (%d)! cid 0x%x\n", skb->len, vcc->qos.rxtp.max_sdu, cid); -#endif - -#ifdef notdef - ATM_SKB(skb)->vcc = vcc; -#endif - spin_unlock(&he_dev->global_lock); - vcc->push(vcc, skb); - spin_lock(&he_dev->global_lock); - - atomic_inc(&vcc->stats->rx); - -return_host_buffers: - ++pdus_assembled; - - list_for_each_entry_safe(heb, next, &he_vcc->buffers, entry) - dma_pool_free(he_dev->rbpl_pool, heb, heb->mapping); - INIT_LIST_HEAD(&he_vcc->buffers); - he_vcc->pdu_len = 0; - -next_rbrq_entry: - he_dev->rbrq_head = (struct he_rbrq *) - ((unsigned long) he_dev->rbrq_base | - RBRQ_MASK(he_dev->rbrq_head + 1)); - - } - read_unlock(&vcc_sklist_lock); - - if (updated) { - if (updated > he_dev->rbrq_peak) - he_dev->rbrq_peak = updated; - - he_writel(he_dev, RBRQ_MASK(he_dev->rbrq_head), - G0_RBRQ_H + (group * 16)); - } - - return pdus_assembled; -} - -static void -he_service_tbrq(struct he_dev *he_dev, int group) -{ - struct he_tbrq *tbrq_tail = (struct he_tbrq *) - ((unsigned long)he_dev->tbrq_base | - he_dev->hsp->group[group].tbrq_tail); - struct he_tpd *tpd; - int slot, updated = 0; - struct he_tpd *__tpd; - - /* 2.1.6 transmit buffer return queue */ - - while (he_dev->tbrq_head != tbrq_tail) { - ++updated; - - HPRINTK("tbrq%d 0x%x%s%s\n", - group, - TBRQ_TPD(he_dev->tbrq_head), - TBRQ_EOS(he_dev->tbrq_head) ? " EOS" : "", - TBRQ_MULTIPLE(he_dev->tbrq_head) ? " MULTIPLE" : ""); - tpd = NULL; - list_for_each_entry(__tpd, &he_dev->outstanding_tpds, entry) { - if (TPD_ADDR(__tpd->status) == TBRQ_TPD(he_dev->tbrq_head)) { - tpd = __tpd; - list_del(&__tpd->entry); - break; - } - } - - if (tpd == NULL) { - hprintk("unable to locate tpd for dma buffer %x\n", - TBRQ_TPD(he_dev->tbrq_head)); - goto next_tbrq_entry; - } - - if (TBRQ_EOS(he_dev->tbrq_head)) { - HPRINTK("wake_up(tx_waitq) cid 0x%x\n", - he_mkcid(he_dev, tpd->vcc->vpi, tpd->vcc->vci)); - if (tpd->vcc) - wake_up(&HE_VCC(tpd->vcc)->tx_waitq); - - goto next_tbrq_entry; - } - - for (slot = 0; slot < TPD_MAXIOV; ++slot) { - if (tpd->iovec[slot].addr) - dma_unmap_single(&he_dev->pci_dev->dev, - tpd->iovec[slot].addr, - tpd->iovec[slot].len & TPD_LEN_MASK, - DMA_TO_DEVICE); - if (tpd->iovec[slot].len & TPD_LST) - break; - - } - - if (tpd->skb) { /* && !TBRQ_MULTIPLE(he_dev->tbrq_head) */ - if (tpd->vcc && tpd->vcc->pop) - tpd->vcc->pop(tpd->vcc, tpd->skb); - else - dev_kfree_skb_any(tpd->skb); - } - -next_tbrq_entry: - if (tpd) - dma_pool_free(he_dev->tpd_pool, tpd, TPD_ADDR(tpd->status)); - he_dev->tbrq_head = (struct he_tbrq *) - ((unsigned long) he_dev->tbrq_base | - TBRQ_MASK(he_dev->tbrq_head + 1)); - } - - if (updated) { - if (updated > he_dev->tbrq_peak) - he_dev->tbrq_peak = updated; - - he_writel(he_dev, TBRQ_MASK(he_dev->tbrq_head), - G0_TBRQ_H + (group * 16)); - } -} - -static void -he_service_rbpl(struct he_dev *he_dev, int group) -{ - struct he_rbp *new_tail; - struct he_rbp *rbpl_head; - struct he_buff *heb; - dma_addr_t mapping; - int i; - int moved = 0; - - rbpl_head = (struct he_rbp *) ((unsigned long)he_dev->rbpl_base | - RBPL_MASK(he_readl(he_dev, G0_RBPL_S))); - - for (;;) { - new_tail = (struct he_rbp *) ((unsigned long)he_dev->rbpl_base | - RBPL_MASK(he_dev->rbpl_tail+1)); - - /* table 3.42 -- rbpl_tail should never be set to rbpl_head */ - if (new_tail == rbpl_head) - break; - - i = find_next_zero_bit(he_dev->rbpl_table, RBPL_TABLE_SIZE, he_dev->rbpl_hint); - if (i > (RBPL_TABLE_SIZE - 1)) { - i = find_first_zero_bit(he_dev->rbpl_table, RBPL_TABLE_SIZE); - if (i > (RBPL_TABLE_SIZE - 1)) - break; - } - he_dev->rbpl_hint = i + 1; - - heb = dma_pool_alloc(he_dev->rbpl_pool, GFP_ATOMIC, &mapping); - if (!heb) - break; - heb->mapping = mapping; - list_add(&heb->entry, &he_dev->rbpl_outstanding); - he_dev->rbpl_virt[i] = heb; - set_bit(i, he_dev->rbpl_table); - new_tail->idx = i << RBP_IDX_OFFSET; - new_tail->phys = mapping + offsetof(struct he_buff, data); - - he_dev->rbpl_tail = new_tail; - ++moved; - } - - if (moved) - he_writel(he_dev, RBPL_MASK(he_dev->rbpl_tail), G0_RBPL_T); -} - -static void -he_tasklet(unsigned long data) -{ - unsigned long flags; - struct he_dev *he_dev = (struct he_dev *) data; - int group, type; - int updated = 0; - - HPRINTK("tasklet (0x%lx)\n", data); - spin_lock_irqsave(&he_dev->global_lock, flags); - - while (he_dev->irq_head != he_dev->irq_tail) { - ++updated; - - type = ITYPE_TYPE(he_dev->irq_head->isw); - group = ITYPE_GROUP(he_dev->irq_head->isw); - - switch (type) { - case ITYPE_RBRQ_THRESH: - HPRINTK("rbrq%d threshold\n", group); - fallthrough; - case ITYPE_RBRQ_TIMER: - if (he_service_rbrq(he_dev, group)) - he_service_rbpl(he_dev, group); - break; - case ITYPE_TBRQ_THRESH: - HPRINTK("tbrq%d threshold\n", group); - fallthrough; - case ITYPE_TPD_COMPLETE: - he_service_tbrq(he_dev, group); - break; - case ITYPE_RBPL_THRESH: - he_service_rbpl(he_dev, group); - break; - case ITYPE_RBPS_THRESH: - /* shouldn't happen unless small buffers enabled */ - break; - case ITYPE_PHY: - HPRINTK("phy interrupt\n"); -#ifdef CONFIG_ATM_HE_USE_SUNI - spin_unlock_irqrestore(&he_dev->global_lock, flags); - if (he_dev->atm_dev->phy && he_dev->atm_dev->phy->interrupt) - he_dev->atm_dev->phy->interrupt(he_dev->atm_dev); - spin_lock_irqsave(&he_dev->global_lock, flags); -#endif - break; - case ITYPE_OTHER: - switch (type|group) { - case ITYPE_PARITY: - hprintk("parity error\n"); - break; - case ITYPE_ABORT: - hprintk("abort 0x%x\n", he_readl(he_dev, ABORT_ADDR)); - break; - } - break; - case ITYPE_TYPE(ITYPE_INVALID): - /* see 8.1.1 -- check all queues */ - - HPRINTK("isw not updated 0x%x\n", he_dev->irq_head->isw); - - he_service_rbrq(he_dev, 0); - he_service_rbpl(he_dev, 0); - he_service_tbrq(he_dev, 0); - break; - default: - hprintk("bad isw 0x%x?\n", he_dev->irq_head->isw); - } - - he_dev->irq_head->isw = ITYPE_INVALID; - - he_dev->irq_head = (struct he_irq *) NEXT_ENTRY(he_dev->irq_base, he_dev->irq_head, IRQ_MASK); - } - - if (updated) { - if (updated > he_dev->irq_peak) - he_dev->irq_peak = updated; - - he_writel(he_dev, - IRQ_SIZE(CONFIG_IRQ_SIZE) | - IRQ_THRESH(CONFIG_IRQ_THRESH) | - IRQ_TAIL(he_dev->irq_tail), IRQ0_HEAD); - (void) he_readl(he_dev, INT_FIFO); /* 8.1.2 controller errata; flush posted writes */ - } - spin_unlock_irqrestore(&he_dev->global_lock, flags); -} - -static irqreturn_t -he_irq_handler(int irq, void *dev_id) -{ - unsigned long flags; - struct he_dev *he_dev = (struct he_dev * )dev_id; - int handled = 0; - - if (he_dev == NULL) - return IRQ_NONE; - - spin_lock_irqsave(&he_dev->global_lock, flags); - - he_dev->irq_tail = (struct he_irq *) (((unsigned long)he_dev->irq_base) | - (*he_dev->irq_tailoffset << 2)); - - if (he_dev->irq_tail == he_dev->irq_head) { - HPRINTK("tailoffset not updated?\n"); - he_dev->irq_tail = (struct he_irq *) ((unsigned long)he_dev->irq_base | - ((he_readl(he_dev, IRQ0_BASE) & IRQ_MASK) << 2)); - (void) he_readl(he_dev, INT_FIFO); /* 8.1.2 controller errata */ - } - -#ifdef DEBUG - if (he_dev->irq_head == he_dev->irq_tail /* && !IRQ_PENDING */) - hprintk("spurious (or shared) interrupt?\n"); -#endif - - if (he_dev->irq_head != he_dev->irq_tail) { - handled = 1; - tasklet_schedule(&he_dev->tasklet); - he_writel(he_dev, INT_CLEAR_A, INT_FIFO); /* clear interrupt */ - (void) he_readl(he_dev, INT_FIFO); /* flush posted writes */ - } - spin_unlock_irqrestore(&he_dev->global_lock, flags); - return IRQ_RETVAL(handled); - -} - -static __inline__ void -__enqueue_tpd(struct he_dev *he_dev, struct he_tpd *tpd, unsigned cid) -{ - struct he_tpdrq *new_tail; - - HPRINTK("tpdrq %p cid 0x%x -> tpdrq_tail %p\n", - tpd, cid, he_dev->tpdrq_tail); - - /* new_tail = he_dev->tpdrq_tail; */ - new_tail = (struct he_tpdrq *) ((unsigned long) he_dev->tpdrq_base | - TPDRQ_MASK(he_dev->tpdrq_tail+1)); - - /* - * check to see if we are about to set the tail == head - * if true, update the head pointer from the adapter - * to see if this is really the case (reading the queue - * head for every enqueue would be unnecessarily slow) - */ - - if (new_tail == he_dev->tpdrq_head) { - he_dev->tpdrq_head = (struct he_tpdrq *) - (((unsigned long)he_dev->tpdrq_base) | - TPDRQ_MASK(he_readl(he_dev, TPDRQ_B_H))); - - if (new_tail == he_dev->tpdrq_head) { - int slot; - - hprintk("tpdrq full (cid 0x%x)\n", cid); - /* - * FIXME - * push tpd onto a transmit backlog queue - * after service_tbrq, service the backlog - * for now, we just drop the pdu - */ - for (slot = 0; slot < TPD_MAXIOV; ++slot) { - if (tpd->iovec[slot].addr) - dma_unmap_single(&he_dev->pci_dev->dev, - tpd->iovec[slot].addr, - tpd->iovec[slot].len & TPD_LEN_MASK, - DMA_TO_DEVICE); - } - if (tpd->skb) { - if (tpd->vcc->pop) - tpd->vcc->pop(tpd->vcc, tpd->skb); - else - dev_kfree_skb_any(tpd->skb); - atomic_inc(&tpd->vcc->stats->tx_err); - } - dma_pool_free(he_dev->tpd_pool, tpd, TPD_ADDR(tpd->status)); - return; - } - } - - /* 2.1.5 transmit packet descriptor ready queue */ - list_add_tail(&tpd->entry, &he_dev->outstanding_tpds); - he_dev->tpdrq_tail->tpd = TPD_ADDR(tpd->status); - he_dev->tpdrq_tail->cid = cid; - wmb(); - - he_dev->tpdrq_tail = new_tail; - - he_writel(he_dev, TPDRQ_MASK(he_dev->tpdrq_tail), TPDRQ_T); - (void) he_readl(he_dev, TPDRQ_T); /* flush posted writes */ -} - -static int -he_open(struct atm_vcc *vcc) -{ - unsigned long flags; - struct he_dev *he_dev = HE_DEV(vcc->dev); - struct he_vcc *he_vcc; - int err = 0; - unsigned cid, rsr0, rsr1, rsr4, tsr0, tsr0_aal, tsr4, period, reg, clock; - short vpi = vcc->vpi; - int vci = vcc->vci; - - if (vci == ATM_VCI_UNSPEC || vpi == ATM_VPI_UNSPEC) - return 0; - - HPRINTK("open vcc %p %d.%d\n", vcc, vpi, vci); - - set_bit(ATM_VF_ADDR, &vcc->flags); - - cid = he_mkcid(he_dev, vpi, vci); - - he_vcc = kmalloc_obj(struct he_vcc, GFP_ATOMIC); - if (he_vcc == NULL) { - hprintk("unable to allocate he_vcc during open\n"); - return -ENOMEM; - } - - INIT_LIST_HEAD(&he_vcc->buffers); - he_vcc->pdu_len = 0; - he_vcc->rc_index = -1; - - init_waitqueue_head(&he_vcc->rx_waitq); - init_waitqueue_head(&he_vcc->tx_waitq); - - vcc->dev_data = he_vcc; - - if (vcc->qos.txtp.traffic_class != ATM_NONE) { - int pcr_goal; - - pcr_goal = atm_pcr_goal(&vcc->qos.txtp); - if (pcr_goal == 0) - pcr_goal = he_dev->atm_dev->link_rate; - if (pcr_goal < 0) /* means round down, technically */ - pcr_goal = -pcr_goal; - - HPRINTK("open tx cid 0x%x pcr_goal %d\n", cid, pcr_goal); - - switch (vcc->qos.aal) { - case ATM_AAL5: - tsr0_aal = TSR0_AAL5; - tsr4 = TSR4_AAL5; - break; - case ATM_AAL0: - tsr0_aal = TSR0_AAL0_SDU; - tsr4 = TSR4_AAL0_SDU; - break; - default: - err = -EINVAL; - goto open_failed; - } - - spin_lock_irqsave(&he_dev->global_lock, flags); - tsr0 = he_readl_tsr0(he_dev, cid); - spin_unlock_irqrestore(&he_dev->global_lock, flags); - - if (TSR0_CONN_STATE(tsr0) != 0) { - hprintk("cid 0x%x not idle (tsr0 = 0x%x)\n", cid, tsr0); - err = -EBUSY; - goto open_failed; - } - - switch (vcc->qos.txtp.traffic_class) { - case ATM_UBR: - /* 2.3.3.1 open connection ubr */ - - tsr0 = TSR0_UBR | TSR0_GROUP(0) | tsr0_aal | - TSR0_USE_WMIN | TSR0_UPDATE_GER; - break; - - case ATM_CBR: - /* 2.3.3.2 open connection cbr */ - - /* 8.2.3 cbr scheduler wrap problem -- limit to 90% total link rate */ - if ((he_dev->total_bw + pcr_goal) - > (he_dev->atm_dev->link_rate * 9 / 10)) - { - err = -EBUSY; - goto open_failed; - } - - spin_lock_irqsave(&he_dev->global_lock, flags); /* also protects he_dev->cs_stper[] */ - - /* find an unused cs_stper register */ - for (reg = 0; reg < HE_NUM_CS_STPER; ++reg) - if (he_dev->cs_stper[reg].inuse == 0 || - he_dev->cs_stper[reg].pcr == pcr_goal) - break; - - if (reg == HE_NUM_CS_STPER) { - err = -EBUSY; - spin_unlock_irqrestore(&he_dev->global_lock, flags); - goto open_failed; - } - - he_dev->total_bw += pcr_goal; - - he_vcc->rc_index = reg; - ++he_dev->cs_stper[reg].inuse; - he_dev->cs_stper[reg].pcr = pcr_goal; - - clock = he_is622(he_dev) ? 66667000 : 50000000; - period = clock / pcr_goal; - - HPRINTK("rc_index = %d period = %d\n", - reg, period); - - he_writel_mbox(he_dev, rate_to_atmf(period/2), - CS_STPER0 + reg); - spin_unlock_irqrestore(&he_dev->global_lock, flags); - - tsr0 = TSR0_CBR | TSR0_GROUP(0) | tsr0_aal | - TSR0_RC_INDEX(reg); - - break; - default: - err = -EINVAL; - goto open_failed; - } - - spin_lock_irqsave(&he_dev->global_lock, flags); - - he_writel_tsr0(he_dev, tsr0, cid); - he_writel_tsr4(he_dev, tsr4 | 1, cid); - he_writel_tsr1(he_dev, TSR1_MCR(rate_to_atmf(0)) | - TSR1_PCR(rate_to_atmf(pcr_goal)), cid); - he_writel_tsr2(he_dev, TSR2_ACR(rate_to_atmf(pcr_goal)), cid); - he_writel_tsr9(he_dev, TSR9_OPEN_CONN, cid); - - he_writel_tsr3(he_dev, 0x0, cid); - he_writel_tsr5(he_dev, 0x0, cid); - he_writel_tsr6(he_dev, 0x0, cid); - he_writel_tsr7(he_dev, 0x0, cid); - he_writel_tsr8(he_dev, 0x0, cid); - he_writel_tsr10(he_dev, 0x0, cid); - he_writel_tsr11(he_dev, 0x0, cid); - he_writel_tsr12(he_dev, 0x0, cid); - he_writel_tsr13(he_dev, 0x0, cid); - he_writel_tsr14(he_dev, 0x0, cid); - (void) he_readl_tsr0(he_dev, cid); /* flush posted writes */ - spin_unlock_irqrestore(&he_dev->global_lock, flags); - } - - if (vcc->qos.rxtp.traffic_class != ATM_NONE) { - unsigned aal; - - HPRINTK("open rx cid 0x%x (rx_waitq %p)\n", cid, - &HE_VCC(vcc)->rx_waitq); - - switch (vcc->qos.aal) { - case ATM_AAL5: - aal = RSR0_AAL5; - break; - case ATM_AAL0: - aal = RSR0_RAWCELL; - break; - default: - err = -EINVAL; - goto open_failed; - } - - spin_lock_irqsave(&he_dev->global_lock, flags); - - rsr0 = he_readl_rsr0(he_dev, cid); - if (rsr0 & RSR0_OPEN_CONN) { - spin_unlock_irqrestore(&he_dev->global_lock, flags); - - hprintk("cid 0x%x not idle (rsr0 = 0x%x)\n", cid, rsr0); - err = -EBUSY; - goto open_failed; - } - - rsr1 = RSR1_GROUP(0) | RSR1_RBPL_ONLY; - rsr4 = RSR4_GROUP(0) | RSR4_RBPL_ONLY; - rsr0 = vcc->qos.rxtp.traffic_class == ATM_UBR ? - (RSR0_EPD_ENABLE|RSR0_PPD_ENABLE) : 0; - -#ifdef USE_CHECKSUM_HW - if (vpi == 0 && vci >= ATM_NOT_RSV_VCI) - rsr0 |= RSR0_TCP_CKSUM; -#endif - - he_writel_rsr4(he_dev, rsr4, cid); - he_writel_rsr1(he_dev, rsr1, cid); - /* 5.1.11 last parameter initialized should be - the open/closed indication in rsr0 */ - he_writel_rsr0(he_dev, - rsr0 | RSR0_START_PDU | RSR0_OPEN_CONN | aal, cid); - (void) he_readl_rsr0(he_dev, cid); /* flush posted writes */ - - spin_unlock_irqrestore(&he_dev->global_lock, flags); - } - -open_failed: - - if (err) { - kfree(he_vcc); - clear_bit(ATM_VF_ADDR, &vcc->flags); - } - else - set_bit(ATM_VF_READY, &vcc->flags); - - return err; -} - -static void -he_close(struct atm_vcc *vcc) -{ - unsigned long flags; - DECLARE_WAITQUEUE(wait, current); - struct he_dev *he_dev = HE_DEV(vcc->dev); - struct he_tpd *tpd; - unsigned cid; - struct he_vcc *he_vcc = HE_VCC(vcc); -#define MAX_RETRY 30 - int retry = 0, sleep = 1, tx_inuse; - - HPRINTK("close vcc %p %d.%d\n", vcc, vcc->vpi, vcc->vci); - - clear_bit(ATM_VF_READY, &vcc->flags); - cid = he_mkcid(he_dev, vcc->vpi, vcc->vci); - - if (vcc->qos.rxtp.traffic_class != ATM_NONE) { - int timeout; - - HPRINTK("close rx cid 0x%x\n", cid); - - /* 2.7.2.2 close receive operation */ - - /* wait for previous close (if any) to finish */ - - spin_lock_irqsave(&he_dev->global_lock, flags); - while (he_readl(he_dev, RCC_STAT) & RCC_BUSY) { - HPRINTK("close cid 0x%x RCC_BUSY\n", cid); - udelay(250); - } - - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&he_vcc->rx_waitq, &wait); - - he_writel_rsr0(he_dev, RSR0_CLOSE_CONN, cid); - (void) he_readl_rsr0(he_dev, cid); /* flush posted writes */ - he_writel_mbox(he_dev, cid, RXCON_CLOSE); - spin_unlock_irqrestore(&he_dev->global_lock, flags); - - timeout = schedule_timeout(30*HZ); - - remove_wait_queue(&he_vcc->rx_waitq, &wait); - set_current_state(TASK_RUNNING); - - if (timeout == 0) - hprintk("close rx timeout cid 0x%x\n", cid); - - HPRINTK("close rx cid 0x%x complete\n", cid); - - } - - if (vcc->qos.txtp.traffic_class != ATM_NONE) { - volatile unsigned tsr4, tsr0; - int timeout; - - HPRINTK("close tx cid 0x%x\n", cid); - - /* 2.1.2 - * - * ... the host must first stop queueing packets to the TPDRQ - * on the connection to be closed, then wait for all outstanding - * packets to be transmitted and their buffers returned to the - * TBRQ. When the last packet on the connection arrives in the - * TBRQ, the host issues the close command to the adapter. - */ - - while (((tx_inuse = refcount_read(&sk_atm(vcc)->sk_wmem_alloc)) > 1) && - (retry < MAX_RETRY)) { - msleep(sleep); - if (sleep < 250) - sleep = sleep * 2; - - ++retry; - } - - if (tx_inuse > 1) - hprintk("close tx cid 0x%x tx_inuse = %d\n", cid, tx_inuse); - - /* 2.3.1.1 generic close operations with flush */ - - spin_lock_irqsave(&he_dev->global_lock, flags); - he_writel_tsr4_upper(he_dev, TSR4_FLUSH_CONN, cid); - /* also clears TSR4_SESSION_ENDED */ - - switch (vcc->qos.txtp.traffic_class) { - case ATM_UBR: - he_writel_tsr1(he_dev, - TSR1_MCR(rate_to_atmf(200000)) - | TSR1_PCR(0), cid); - break; - case ATM_CBR: - he_writel_tsr14_upper(he_dev, TSR14_DELETE, cid); - break; - } - (void) he_readl_tsr4(he_dev, cid); /* flush posted writes */ - - tpd = __alloc_tpd(he_dev); - if (tpd == NULL) { - hprintk("close tx he_alloc_tpd failed cid 0x%x\n", cid); - goto close_tx_incomplete; - } - tpd->status |= TPD_EOS | TPD_INT; - tpd->skb = NULL; - tpd->vcc = vcc; - wmb(); - - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&he_vcc->tx_waitq, &wait); - __enqueue_tpd(he_dev, tpd, cid); - spin_unlock_irqrestore(&he_dev->global_lock, flags); - - timeout = schedule_timeout(30*HZ); - - remove_wait_queue(&he_vcc->tx_waitq, &wait); - set_current_state(TASK_RUNNING); - - spin_lock_irqsave(&he_dev->global_lock, flags); - - if (timeout == 0) { - hprintk("close tx timeout cid 0x%x\n", cid); - goto close_tx_incomplete; - } - - while (!((tsr4 = he_readl_tsr4(he_dev, cid)) & TSR4_SESSION_ENDED)) { - HPRINTK("close tx cid 0x%x !TSR4_SESSION_ENDED (tsr4 = 0x%x)\n", cid, tsr4); - udelay(250); - } - - while (TSR0_CONN_STATE(tsr0 = he_readl_tsr0(he_dev, cid)) != 0) { - HPRINTK("close tx cid 0x%x TSR0_CONN_STATE != 0 (tsr0 = 0x%x)\n", cid, tsr0); - udelay(250); - } - -close_tx_incomplete: - - if (vcc->qos.txtp.traffic_class == ATM_CBR) { - int reg = he_vcc->rc_index; - - HPRINTK("cs_stper reg = %d\n", reg); - - if (he_dev->cs_stper[reg].inuse == 0) - hprintk("cs_stper[%d].inuse = 0!\n", reg); - else - --he_dev->cs_stper[reg].inuse; - - he_dev->total_bw -= he_dev->cs_stper[reg].pcr; - } - spin_unlock_irqrestore(&he_dev->global_lock, flags); - - HPRINTK("close tx cid 0x%x complete\n", cid); - } - - kfree(he_vcc); - - clear_bit(ATM_VF_ADDR, &vcc->flags); -} - -static int -he_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - unsigned long flags; - struct he_dev *he_dev = HE_DEV(vcc->dev); - unsigned cid = he_mkcid(he_dev, vcc->vpi, vcc->vci); - struct he_tpd *tpd; -#ifdef USE_SCATTERGATHER - int i, slot = 0; -#endif - -#define HE_TPD_BUFSIZE 0xffff - - HPRINTK("send %d.%d\n", vcc->vpi, vcc->vci); - - if ((skb->len > HE_TPD_BUFSIZE) || - ((vcc->qos.aal == ATM_AAL0) && (skb->len != ATM_AAL0_SDU))) { - hprintk("buffer too large (or small) -- %d bytes\n", skb->len ); - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb_any(skb); - atomic_inc(&vcc->stats->tx_err); - return -EINVAL; - } - -#ifndef USE_SCATTERGATHER - if (skb_shinfo(skb)->nr_frags) { - hprintk("no scatter/gather support\n"); - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb_any(skb); - atomic_inc(&vcc->stats->tx_err); - return -EINVAL; - } -#endif - spin_lock_irqsave(&he_dev->global_lock, flags); - - tpd = __alloc_tpd(he_dev); - if (tpd == NULL) { - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb_any(skb); - atomic_inc(&vcc->stats->tx_err); - spin_unlock_irqrestore(&he_dev->global_lock, flags); - return -ENOMEM; - } - - if (vcc->qos.aal == ATM_AAL5) - tpd->status |= TPD_CELLTYPE(TPD_USERCELL); - else { - char *pti_clp = (void *) (skb->data + 3); - int clp, pti; - - pti = (*pti_clp & ATM_HDR_PTI_MASK) >> ATM_HDR_PTI_SHIFT; - clp = (*pti_clp & ATM_HDR_CLP); - tpd->status |= TPD_CELLTYPE(pti); - if (clp) - tpd->status |= TPD_CLP; - - skb_pull(skb, ATM_AAL0_SDU - ATM_CELL_PAYLOAD); - } - -#ifdef USE_SCATTERGATHER - tpd->iovec[slot].addr = dma_map_single(&he_dev->pci_dev->dev, skb->data, - skb_headlen(skb), DMA_TO_DEVICE); - tpd->iovec[slot].len = skb_headlen(skb); - ++slot; - - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - - if (slot == TPD_MAXIOV) { /* queue tpd; start new tpd */ - tpd->vcc = vcc; - tpd->skb = NULL; /* not the last fragment - so dont ->push() yet */ - wmb(); - - __enqueue_tpd(he_dev, tpd, cid); - tpd = __alloc_tpd(he_dev); - if (tpd == NULL) { - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb_any(skb); - atomic_inc(&vcc->stats->tx_err); - spin_unlock_irqrestore(&he_dev->global_lock, flags); - return -ENOMEM; - } - tpd->status |= TPD_USERCELL; - slot = 0; - } - - tpd->iovec[slot].addr = skb_frag_dma_map(&he_dev->pci_dev->dev, - frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); - tpd->iovec[slot].len = skb_frag_size(frag); - ++slot; - - } - - tpd->iovec[slot - 1].len |= TPD_LST; -#else - tpd->address0 = dma_map_single(&he_dev->pci_dev->dev, skb->data, skb->len, DMA_TO_DEVICE); - tpd->length0 = skb->len | TPD_LST; -#endif - tpd->status |= TPD_INT; - - tpd->vcc = vcc; - tpd->skb = skb; - wmb(); - ATM_SKB(skb)->vcc = vcc; - - __enqueue_tpd(he_dev, tpd, cid); - spin_unlock_irqrestore(&he_dev->global_lock, flags); - - atomic_inc(&vcc->stats->tx); - - return 0; -} - -static int -he_ioctl(struct atm_dev *atm_dev, unsigned int cmd, void __user *arg) -{ - unsigned long flags; - struct he_dev *he_dev = HE_DEV(atm_dev); - struct he_ioctl_reg reg; - int err = 0; - - switch (cmd) { - case HE_GET_REG: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (copy_from_user(®, arg, - sizeof(struct he_ioctl_reg))) - return -EFAULT; - - spin_lock_irqsave(&he_dev->global_lock, flags); - switch (reg.type) { - case HE_REGTYPE_PCI: - if (reg.addr >= HE_REGMAP_SIZE) { - err = -EINVAL; - break; - } - - reg.val = he_readl(he_dev, reg.addr); - break; - case HE_REGTYPE_RCM: - reg.val = - he_readl_rcm(he_dev, reg.addr); - break; - case HE_REGTYPE_TCM: - reg.val = - he_readl_tcm(he_dev, reg.addr); - break; - case HE_REGTYPE_MBOX: - reg.val = - he_readl_mbox(he_dev, reg.addr); - break; - default: - err = -EINVAL; - break; - } - spin_unlock_irqrestore(&he_dev->global_lock, flags); - if (err == 0) - if (copy_to_user(arg, ®, - sizeof(struct he_ioctl_reg))) - return -EFAULT; - break; - default: -#ifdef CONFIG_ATM_HE_USE_SUNI - if (atm_dev->phy && atm_dev->phy->ioctl) - err = atm_dev->phy->ioctl(atm_dev, cmd, arg); -#else /* CONFIG_ATM_HE_USE_SUNI */ - err = -EINVAL; -#endif /* CONFIG_ATM_HE_USE_SUNI */ - break; - } - - return err; -} - -static void -he_phy_put(struct atm_dev *atm_dev, unsigned char val, unsigned long addr) -{ - unsigned long flags; - struct he_dev *he_dev = HE_DEV(atm_dev); - - HPRINTK("phy_put(val 0x%x, addr 0x%lx)\n", val, addr); - - spin_lock_irqsave(&he_dev->global_lock, flags); - he_writel(he_dev, val, FRAMER + (addr*4)); - (void) he_readl(he_dev, FRAMER + (addr*4)); /* flush posted writes */ - spin_unlock_irqrestore(&he_dev->global_lock, flags); -} - - -static unsigned char -he_phy_get(struct atm_dev *atm_dev, unsigned long addr) -{ - unsigned long flags; - struct he_dev *he_dev = HE_DEV(atm_dev); - unsigned reg; - - spin_lock_irqsave(&he_dev->global_lock, flags); - reg = he_readl(he_dev, FRAMER + (addr*4)); - spin_unlock_irqrestore(&he_dev->global_lock, flags); - - HPRINTK("phy_get(addr 0x%lx) =0x%x\n", addr, reg); - return reg; -} - -static int -he_proc_read(struct atm_dev *dev, loff_t *pos, char *page) -{ - unsigned long flags; - struct he_dev *he_dev = HE_DEV(dev); - int left, i; -#ifdef notdef - struct he_rbrq *rbrq_tail; - struct he_tpdrq *tpdrq_head; - int rbpl_head, rbpl_tail; -#endif - static long mcc = 0, oec = 0, dcc = 0, cec = 0; - - - left = *pos; - if (!left--) - return sprintf(page, "ATM he driver\n"); - - if (!left--) - return sprintf(page, "%s%s\n\n", - he_dev->prod_id, he_dev->media & 0x40 ? "SM" : "MM"); - - if (!left--) - return sprintf(page, "Mismatched Cells VPI/VCI Not Open Dropped Cells RCM Dropped Cells\n"); - - spin_lock_irqsave(&he_dev->global_lock, flags); - mcc += he_readl(he_dev, MCC); - oec += he_readl(he_dev, OEC); - dcc += he_readl(he_dev, DCC); - cec += he_readl(he_dev, CEC); - spin_unlock_irqrestore(&he_dev->global_lock, flags); - - if (!left--) - return sprintf(page, "%16ld %16ld %13ld %17ld\n\n", - mcc, oec, dcc, cec); - - if (!left--) - return sprintf(page, "irq_size = %d inuse = ? peak = %d\n", - CONFIG_IRQ_SIZE, he_dev->irq_peak); - - if (!left--) - return sprintf(page, "tpdrq_size = %d inuse = ?\n", - CONFIG_TPDRQ_SIZE); - - if (!left--) - return sprintf(page, "rbrq_size = %d inuse = ? peak = %d\n", - CONFIG_RBRQ_SIZE, he_dev->rbrq_peak); - - if (!left--) - return sprintf(page, "tbrq_size = %d peak = %d\n", - CONFIG_TBRQ_SIZE, he_dev->tbrq_peak); - - -#ifdef notdef - rbpl_head = RBPL_MASK(he_readl(he_dev, G0_RBPL_S)); - rbpl_tail = RBPL_MASK(he_readl(he_dev, G0_RBPL_T)); - - inuse = rbpl_head - rbpl_tail; - if (inuse < 0) - inuse += CONFIG_RBPL_SIZE * sizeof(struct he_rbp); - inuse /= sizeof(struct he_rbp); - - if (!left--) - return sprintf(page, "rbpl_size = %d inuse = %d\n\n", - CONFIG_RBPL_SIZE, inuse); -#endif - - if (!left--) - return sprintf(page, "rate controller periods (cbr)\n pcr #vc\n"); - - for (i = 0; i < HE_NUM_CS_STPER; ++i) - if (!left--) - return sprintf(page, "cs_stper%-2d %8ld %3d\n", i, - he_dev->cs_stper[i].pcr, - he_dev->cs_stper[i].inuse); - - if (!left--) - return sprintf(page, "total bw (cbr): %d (limit %d)\n", - he_dev->total_bw, he_dev->atm_dev->link_rate * 10 / 9); - - return 0; -} - -/* eeprom routines -- see 4.7 */ - -static u8 read_prom_byte(struct he_dev *he_dev, int addr) -{ - u32 val = 0, tmp_read = 0; - int i, j = 0; - u8 byte_read = 0; - - val = readl(he_dev->membase + HOST_CNTL); - val &= 0xFFFFE0FF; - - /* Turn on write enable */ - val |= 0x800; - he_writel(he_dev, val, HOST_CNTL); - - /* Send READ instruction */ - for (i = 0; i < ARRAY_SIZE(readtab); i++) { - he_writel(he_dev, val | readtab[i], HOST_CNTL); - udelay(EEPROM_DELAY); - } - - /* Next, we need to send the byte address to read from */ - for (i = 7; i >= 0; i--) { - he_writel(he_dev, val | clocktab[j++] | (((addr >> i) & 1) << 9), HOST_CNTL); - udelay(EEPROM_DELAY); - he_writel(he_dev, val | clocktab[j++] | (((addr >> i) & 1) << 9), HOST_CNTL); - udelay(EEPROM_DELAY); - } - - j = 0; - - val &= 0xFFFFF7FF; /* Turn off write enable */ - he_writel(he_dev, val, HOST_CNTL); - - /* Now, we can read data from the EEPROM by clocking it in */ - for (i = 7; i >= 0; i--) { - he_writel(he_dev, val | clocktab[j++], HOST_CNTL); - udelay(EEPROM_DELAY); - tmp_read = he_readl(he_dev, HOST_CNTL); - byte_read |= (unsigned char) - ((tmp_read & ID_DOUT) >> ID_DOFFSET << i); - he_writel(he_dev, val | clocktab[j++], HOST_CNTL); - udelay(EEPROM_DELAY); - } - - he_writel(he_dev, val | ID_CS, HOST_CNTL); - udelay(EEPROM_DELAY); - - return byte_read; -} - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("chas williams "); -MODULE_DESCRIPTION("ForeRunnerHE ATM Adapter driver"); -module_param(disable64, bool, 0); -MODULE_PARM_DESC(disable64, "disable 64-bit pci bus transfers"); -module_param(nvpibits, short, 0); -MODULE_PARM_DESC(nvpibits, "numbers of bits for vpi (default 0)"); -module_param(nvcibits, short, 0); -MODULE_PARM_DESC(nvcibits, "numbers of bits for vci (default 12)"); -module_param(rx_skb_reserve, short, 0); -MODULE_PARM_DESC(rx_skb_reserve, "padding for receive skb (default 16)"); -module_param(irq_coalesce, bool, 0); -MODULE_PARM_DESC(irq_coalesce, "use interrupt coalescing (default 1)"); -module_param(sdh, bool, 0); -MODULE_PARM_DESC(sdh, "use SDH framing (default 0)"); - -static const struct pci_device_id he_pci_tbl[] = { - { PCI_VDEVICE(FORE, PCI_DEVICE_ID_FORE_HE), 0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, he_pci_tbl); - -static struct pci_driver he_driver = { - .name = "he", - .probe = he_init_one, - .remove = he_remove_one, - .id_table = he_pci_tbl, -}; - -module_pci_driver(he_driver); diff --git a/drivers/atm/he.h b/drivers/atm/he.h deleted file mode 100644 index f3f53674ef3f..000000000000 --- a/drivers/atm/he.h +++ /dev/null @@ -1,845 +0,0 @@ -/* - - he.h - - ForeRunnerHE ATM Adapter driver for ATM on Linux - Copyright (C) 1999-2001 Naval Research Laboratory - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ - -/* - - he.h - - ForeRunnerHE ATM Adapter driver for ATM on Linux - Copyright (C) 1999-2000 Naval Research Laboratory - - Permission to use, copy, modify and distribute this software and its - documentation is hereby granted, provided that both the copyright - notice and this permission notice appear in all copies of the software, - derivative works or modified versions, and any portions thereof, and - that both notices appear in supporting documentation. - - NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND - DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER - RESULTING FROM THE USE OF THIS SOFTWARE. - - */ - -#ifndef _HE_H_ -#define _HE_H_ - -#define DEV_LABEL "he" - -#define CONFIG_DEFAULT_VCIBITS 12 -#define CONFIG_DEFAULT_VPIBITS 0 - -#define CONFIG_IRQ_SIZE 128 -#define CONFIG_IRQ_THRESH (CONFIG_IRQ_SIZE/2) - -#define CONFIG_TPDRQ_SIZE 512 -#define TPDRQ_MASK(x) (((unsigned long)(x))&((CONFIG_TPDRQ_SIZE<<3)-1)) - -#define CONFIG_RBRQ_SIZE 512 -#define CONFIG_RBRQ_THRESH 400 -#define RBRQ_MASK(x) (((unsigned long)(x))&((CONFIG_RBRQ_SIZE<<3)-1)) - -#define CONFIG_TBRQ_SIZE 512 -#define CONFIG_TBRQ_THRESH 400 -#define TBRQ_MASK(x) (((unsigned long)(x))&((CONFIG_TBRQ_SIZE<<2)-1)) - -#define CONFIG_RBPL_SIZE 512 -#define CONFIG_RBPL_THRESH 64 -#define CONFIG_RBPL_BUFSIZE 4096 -#define RBPL_MASK(x) (((unsigned long)(x))&((CONFIG_RBPL_SIZE<<3)-1)) - -/* 5.1.3 initialize connection memory */ - -#define CONFIG_RSRA 0x00000 -#define CONFIG_RCMLBM 0x08000 -#define CONFIG_RCMABR 0x0d800 -#define CONFIG_RSRB 0x0e000 - -#define CONFIG_TSRA 0x00000 -#define CONFIG_TSRB 0x08000 -#define CONFIG_TSRC 0x0c000 -#define CONFIG_TSRD 0x0e000 -#define CONFIG_TMABR 0x0f000 -#define CONFIG_TPDBA 0x10000 - -#define HE_MAXCIDBITS 12 - -/* 2.9.3.3 interrupt encodings */ - -struct he_irq { - volatile u32 isw; -}; - -#define IRQ_ALIGNMENT 0x1000 - -#define NEXT_ENTRY(base, tail, mask) \ - (((unsigned long)base)|(((unsigned long)(tail+1))&mask)) - -#define ITYPE_INVALID 0xffffffff -#define ITYPE_TBRQ_THRESH (0<<3) -#define ITYPE_TPD_COMPLETE (1<<3) -#define ITYPE_RBPS_THRESH (2<<3) -#define ITYPE_RBPL_THRESH (3<<3) -#define ITYPE_RBRQ_THRESH (4<<3) -#define ITYPE_RBRQ_TIMER (5<<3) -#define ITYPE_PHY (6<<3) -#define ITYPE_OTHER 0x80 -#define ITYPE_PARITY 0x81 -#define ITYPE_ABORT 0x82 - -#define ITYPE_GROUP(x) (x & 0x7) -#define ITYPE_TYPE(x) (x & 0xf8) - -#define HE_NUM_GROUPS 8 - -/* 2.1.4 transmit packet descriptor */ - -struct he_tpd { - - /* read by the adapter */ - - volatile u32 status; - volatile u32 reserved; - -#define TPD_MAXIOV 3 - struct { - u32 addr, len; - } iovec[TPD_MAXIOV]; - -#define address0 iovec[0].addr -#define length0 iovec[0].len - - /* linux-atm extensions */ - - struct sk_buff *skb; - struct atm_vcc *vcc; - - struct list_head entry; -}; - -#define TPD_ALIGNMENT 64 -#define TPD_LEN_MASK 0xffff - -#define TPD_ADDR_SHIFT 6 -#define TPD_MASK 0xffffffc0 -#define TPD_ADDR(x) ((x) & TPD_MASK) -#define TPD_INDEX(x) (TPD_ADDR(x) >> TPD_ADDR_SHIFT) - - -/* table 2.3 transmit buffer return elements */ - -struct he_tbrq { - volatile u32 tbre; -}; - -#define TBRQ_ALIGNMENT CONFIG_TBRQ_SIZE - -#define TBRQ_TPD(tbrq) ((tbrq)->tbre & 0xffffffc0) -#define TBRQ_EOS(tbrq) ((tbrq)->tbre & (1<<3)) -#define TBRQ_MULTIPLE(tbrq) ((tbrq)->tbre & (1)) - -/* table 2.21 receive buffer return queue element field organization */ - -struct he_rbrq { - volatile u32 addr; - volatile u32 cidlen; -}; - -#define RBRQ_ALIGNMENT CONFIG_RBRQ_SIZE - -#define RBRQ_ADDR(rbrq) ((rbrq)->addr & 0xffffffc0) -#define RBRQ_CRC_ERR(rbrq) ((rbrq)->addr & (1<<5)) -#define RBRQ_LEN_ERR(rbrq) ((rbrq)->addr & (1<<4)) -#define RBRQ_END_PDU(rbrq) ((rbrq)->addr & (1<<3)) -#define RBRQ_AAL5_PROT(rbrq) ((rbrq)->addr & (1<<2)) -#define RBRQ_CON_CLOSED(rbrq) ((rbrq)->addr & (1<<1)) -#define RBRQ_HBUF_ERR(rbrq) ((rbrq)->addr & 1) -#define RBRQ_CID(rbrq) (((rbrq)->cidlen >> 16) & 0x1fff) -#define RBRQ_BUFLEN(rbrq) ((rbrq)->cidlen & 0xffff) - -/* figure 2.3 transmit packet descriptor ready queue */ - -struct he_tpdrq { - volatile u32 tpd; - volatile u32 cid; -}; - -#define TPDRQ_ALIGNMENT CONFIG_TPDRQ_SIZE - -/* table 2.30 host status page detail */ - -#define HSP_ALIGNMENT 0x400 /* must align on 1k boundary */ - -struct he_hsp { - struct he_hsp_entry { - volatile u32 tbrq_tail; - volatile u32 reserved1[15]; - volatile u32 rbrq_tail; - volatile u32 reserved2[15]; - } group[HE_NUM_GROUPS]; -}; - -/* - * figure 2.9 receive buffer pools - * - * since a virtual address might be more than 32 bits, we store an index - * in the virt member of he_rbp. NOTE: the lower six bits in the rbrq - * addr member are used for buffer status further limiting us to 26 bits. - */ - -struct he_rbp { - volatile u32 phys; - volatile u32 idx; /* virt */ -}; - -#define RBP_IDX_OFFSET 6 - -/* - * the he dma engine will try to hold an extra 16 buffers in its local - * caches. and add a couple buffers for safety. - */ - -#define RBPL_TABLE_SIZE (CONFIG_RBPL_SIZE + 16 + 2) - -struct he_buff { - struct list_head entry; - dma_addr_t mapping; - unsigned long len; - u8 data[]; -}; - -#ifdef notyet -struct he_group { - u32 rpbl_size, rpbl_qsize; - struct he_rpb_entry *rbpl_ba; -}; -#endif - -#define HE_LOOKUP_VCC(dev, cid) ((dev)->he_vcc_table[(cid)].vcc) - -struct he_vcc_table -{ - struct atm_vcc *vcc; -}; - -struct he_cs_stper -{ - long pcr; - int inuse; -}; - -#define HE_NUM_CS_STPER 16 - -struct he_dev { - unsigned int number; - unsigned int irq; - void __iomem *membase; - - char prod_id[30]; - char mac_addr[6]; - int media; - - unsigned int vcibits, vpibits; - unsigned int cells_per_row; - unsigned int bytes_per_row; - unsigned int cells_per_lbuf; - unsigned int r0_numrows, r0_startrow, r0_numbuffs; - unsigned int r1_numrows, r1_startrow, r1_numbuffs; - unsigned int tx_numrows, tx_startrow, tx_numbuffs; - unsigned int buffer_limit; - - struct he_vcc_table *he_vcc_table; - -#ifdef notyet - struct he_group group[HE_NUM_GROUPS]; -#endif - struct he_cs_stper cs_stper[HE_NUM_CS_STPER]; - unsigned total_bw; - - dma_addr_t irq_phys; - struct he_irq *irq_base, *irq_head, *irq_tail; - volatile unsigned *irq_tailoffset; - int irq_peak; - - struct tasklet_struct tasklet; - struct dma_pool *tpd_pool; - struct list_head outstanding_tpds; - - dma_addr_t tpdrq_phys; - struct he_tpdrq *tpdrq_base, *tpdrq_tail, *tpdrq_head; - - spinlock_t global_lock; /* 8.1.5 pci transaction ordering - error problem */ - dma_addr_t rbrq_phys; - struct he_rbrq *rbrq_base, *rbrq_head; - int rbrq_peak; - - struct he_buff **rbpl_virt; - unsigned long *rbpl_table; - unsigned long rbpl_hint; - struct dma_pool *rbpl_pool; - dma_addr_t rbpl_phys; - struct he_rbp *rbpl_base, *rbpl_tail; - struct list_head rbpl_outstanding; - int rbpl_peak; - - dma_addr_t tbrq_phys; - struct he_tbrq *tbrq_base, *tbrq_head; - int tbrq_peak; - - dma_addr_t hsp_phys; - struct he_hsp *hsp; - - struct pci_dev *pci_dev; - struct atm_dev *atm_dev; - struct he_dev *next; -}; - -#define HE_MAXIOV 20 - -struct he_vcc -{ - struct list_head buffers; - int pdu_len; - int rc_index; - - wait_queue_head_t rx_waitq; - wait_queue_head_t tx_waitq; -}; - -#define HE_VCC(vcc) ((struct he_vcc *)(vcc->dev_data)) - -#define PCI_VENDOR_ID_FORE 0x1127 -#define PCI_DEVICE_ID_FORE_HE 0x400 - -#define GEN_CNTL_0 0x40 -#define INT_PROC_ENBL (1<<25) -#define SLAVE_ENDIAN_MODE (1<<16) -#define MRL_ENB (1<<5) -#define MRM_ENB (1<<4) -#define INIT_ENB (1<<2) -#define IGNORE_TIMEOUT (1<<1) -#define ENBL_64 (1<<0) - -#define MIN_PCI_LATENCY 32 /* errata 8.1.3 */ - -#define HE_DEV(dev) ((struct he_dev *) (dev)->dev_data) - -#define he_is622(dev) ((dev)->media & 0x1) -#define he_isMM(dev) ((dev)->media & 0x20) - -#define HE_REGMAP_SIZE 0x100000 - -#define RESET_CNTL 0x80000 -#define BOARD_RST_STATUS (1<<6) - -#define HOST_CNTL 0x80004 -#define PCI_BUS_SIZE64 (1<<27) -#define DESC_RD_STATIC_64 (1<<26) -#define DATA_RD_STATIC_64 (1<<25) -#define DATA_WR_STATIC_64 (1<<24) -#define ID_CS (1<<12) -#define ID_WREN (1<<11) -#define ID_DOUT (1<<10) -#define ID_DOFFSET 10 -#define ID_DIN (1<<9) -#define ID_CLOCK (1<<8) -#define QUICK_RD_RETRY (1<<7) -#define QUICK_WR_RETRY (1<<6) -#define OUTFF_ENB (1<<5) -#define CMDFF_ENB (1<<4) -#define PERR_INT_ENB (1<<2) -#define IGNORE_INTR (1<<0) - -#define LB_SWAP 0x80008 -#define SWAP_RNUM_MAX(x) (x<<27) -#define DATA_WR_SWAP (1<<20) -#define DESC_RD_SWAP (1<<19) -#define DATA_RD_SWAP (1<<18) -#define INTR_SWAP (1<<17) -#define DESC_WR_SWAP (1<<16) -#define SDRAM_INIT (1<<15) -#define BIG_ENDIAN_HOST (1<<14) -#define XFER_SIZE (1<<7) - -#define LB_MEM_ADDR 0x8000c -#define LB_MEM_DATA 0x80010 - -#define LB_MEM_ACCESS 0x80014 -#define LB_MEM_HNDSHK (1<<30) -#define LM_MEM_WRITE (0x7) -#define LM_MEM_READ (0x3) - -#define SDRAM_CTL 0x80018 -#define LB_64_ENB (1<<3) -#define LB_TWR (1<<2) -#define LB_TRP (1<<1) -#define LB_TRAS (1<<0) - -#define INT_FIFO 0x8001c -#define INT_MASK_D (1<<15) -#define INT_MASK_C (1<<14) -#define INT_MASK_B (1<<13) -#define INT_MASK_A (1<<12) -#define INT_CLEAR_D (1<<11) -#define INT_CLEAR_C (1<<10) -#define INT_CLEAR_B (1<<9) -#define INT_CLEAR_A (1<<8) - -#define ABORT_ADDR 0x80020 - -#define IRQ0_BASE 0x80080 -#define IRQ_BASE(x) (x<<12) -#define IRQ_MASK ((CONFIG_IRQ_SIZE<<2)-1) /* was 0x3ff */ -#define IRQ_TAIL(x) (((unsigned long)(x)) & IRQ_MASK) -#define IRQ0_HEAD 0x80084 -#define IRQ_SIZE(x) (x<<22) -#define IRQ_THRESH(x) (x<<12) -#define IRQ_HEAD(x) (x<<2) -/* #define IRQ_PENDING (1) conflict with linux/irq.h */ -#define IRQ0_CNTL 0x80088 -#define IRQ_ADDRSEL(x) (x<<2) -#define IRQ_INT_A (0<<2) -#define IRQ_INT_B (1<<2) -#define IRQ_INT_C (2<<2) -#define IRQ_INT_D (3<<2) -#define IRQ_TYPE_ADDR 0x1 -#define IRQ_TYPE_LINE 0x0 -#define IRQ0_DATA 0x8008c - -#define IRQ1_BASE 0x80090 -#define IRQ1_HEAD 0x80094 -#define IRQ1_CNTL 0x80098 -#define IRQ1_DATA 0x8009c - -#define IRQ2_BASE 0x800a0 -#define IRQ2_HEAD 0x800a4 -#define IRQ2_CNTL 0x800a8 -#define IRQ2_DATA 0x800ac - -#define IRQ3_BASE 0x800b0 -#define IRQ3_HEAD 0x800b4 -#define IRQ3_CNTL 0x800b8 -#define IRQ3_DATA 0x800bc - -#define GRP_10_MAP 0x800c0 -#define GRP_32_MAP 0x800c4 -#define GRP_54_MAP 0x800c8 -#define GRP_76_MAP 0x800cc - -#define G0_RBPS_S 0x80400 -#define G0_RBPS_T 0x80404 -#define RBP_TAIL(x) ((x)<<3) -#define RBP_MASK(x) ((x)|0x1fff) -#define G0_RBPS_QI 0x80408 -#define RBP_QSIZE(x) ((x)<<14) -#define RBP_INT_ENB (1<<13) -#define RBP_THRESH(x) (x) -#define G0_RBPS_BS 0x8040c -#define G0_RBPL_S 0x80410 -#define G0_RBPL_T 0x80414 -#define G0_RBPL_QI 0x80418 -#define G0_RBPL_BS 0x8041c - -#define G1_RBPS_S 0x80420 -#define G1_RBPS_T 0x80424 -#define G1_RBPS_QI 0x80428 -#define G1_RBPS_BS 0x8042c -#define G1_RBPL_S 0x80430 -#define G1_RBPL_T 0x80434 -#define G1_RBPL_QI 0x80438 -#define G1_RBPL_BS 0x8043c - -#define G2_RBPS_S 0x80440 -#define G2_RBPS_T 0x80444 -#define G2_RBPS_QI 0x80448 -#define G2_RBPS_BS 0x8044c -#define G2_RBPL_S 0x80450 -#define G2_RBPL_T 0x80454 -#define G2_RBPL_QI 0x80458 -#define G2_RBPL_BS 0x8045c - -#define G3_RBPS_S 0x80460 -#define G3_RBPS_T 0x80464 -#define G3_RBPS_QI 0x80468 -#define G3_RBPS_BS 0x8046c -#define G3_RBPL_S 0x80470 -#define G3_RBPL_T 0x80474 -#define G3_RBPL_QI 0x80478 -#define G3_RBPL_BS 0x8047c - -#define G4_RBPS_S 0x80480 -#define G4_RBPS_T 0x80484 -#define G4_RBPS_QI 0x80488 -#define G4_RBPS_BS 0x8048c -#define G4_RBPL_S 0x80490 -#define G4_RBPL_T 0x80494 -#define G4_RBPL_QI 0x80498 -#define G4_RBPL_BS 0x8049c - -#define G5_RBPS_S 0x804a0 -#define G5_RBPS_T 0x804a4 -#define G5_RBPS_QI 0x804a8 -#define G5_RBPS_BS 0x804ac -#define G5_RBPL_S 0x804b0 -#define G5_RBPL_T 0x804b4 -#define G5_RBPL_QI 0x804b8 -#define G5_RBPL_BS 0x804bc - -#define G6_RBPS_S 0x804c0 -#define G6_RBPS_T 0x804c4 -#define G6_RBPS_QI 0x804c8 -#define G6_RBPS_BS 0x804cc -#define G6_RBPL_S 0x804d0 -#define G6_RBPL_T 0x804d4 -#define G6_RBPL_QI 0x804d8 -#define G6_RBPL_BS 0x804dc - -#define G7_RBPS_S 0x804e0 -#define G7_RBPS_T 0x804e4 -#define G7_RBPS_QI 0x804e8 -#define G7_RBPS_BS 0x804ec - -#define G7_RBPL_S 0x804f0 -#define G7_RBPL_T 0x804f4 -#define G7_RBPL_QI 0x804f8 -#define G7_RBPL_BS 0x804fc - -#define G0_RBRQ_ST 0x80500 -#define G0_RBRQ_H 0x80504 -#define G0_RBRQ_Q 0x80508 -#define RBRQ_THRESH(x) ((x)<<13) -#define RBRQ_SIZE(x) (x) -#define G0_RBRQ_I 0x8050c -#define RBRQ_TIME(x) ((x)<<8) -#define RBRQ_COUNT(x) (x) - -/* fill in 1 ... 7 later */ - -#define G0_TBRQ_B_T 0x80600 -#define G0_TBRQ_H 0x80604 -#define G0_TBRQ_S 0x80608 -#define G0_TBRQ_THRESH 0x8060c -#define TBRQ_THRESH(x) (x) - -/* fill in 1 ... 7 later */ - -#define RH_CONFIG 0x805c0 -#define PHY_INT_ENB (1<<10) -#define OAM_GID(x) (x<<7) -#define PTMR_PRE(x) (x) - -#define G0_INMQ_S 0x80580 -#define G0_INMQ_L 0x80584 -#define G1_INMQ_S 0x80588 -#define G1_INMQ_L 0x8058c -#define G2_INMQ_S 0x80590 -#define G2_INMQ_L 0x80594 -#define G3_INMQ_S 0x80598 -#define G3_INMQ_L 0x8059c -#define G4_INMQ_S 0x805a0 -#define G4_INMQ_L 0x805a4 -#define G5_INMQ_S 0x805a8 -#define G5_INMQ_L 0x805ac -#define G6_INMQ_S 0x805b0 -#define G6_INMQ_L 0x805b4 -#define G7_INMQ_S 0x805b8 -#define G7_INMQ_L 0x805bc - -#define TPDRQ_B_H 0x80680 -#define TPDRQ_T 0x80684 -#define TPDRQ_S 0x80688 - -#define UBUFF_BA 0x8068c - -#define RLBF0_H 0x806c0 -#define RLBF0_T 0x806c4 -#define RLBF1_H 0x806c8 -#define RLBF1_T 0x806cc -#define RLBC_H 0x806d0 -#define RLBC_T 0x806d4 -#define RLBC_H2 0x806d8 -#define TLBF_H 0x806e0 -#define TLBF_T 0x806e4 -#define RLBF0_C 0x806e8 -#define RLBF1_C 0x806ec -#define RXTHRSH 0x806f0 -#define LITHRSH 0x806f4 - -#define LBARB 0x80700 -#define SLICE_X(x) (x<<28) -#define ARB_RNUM_MAX(x) (x<<23) -#define TH_PRTY(x) (x<<21) -#define RH_PRTY(x) (x<<19) -#define TL_PRTY(x) (x<<17) -#define RL_PRTY(x) (x<<15) -#define BUS_MULTI(x) (x<<8) -#define NET_PREF(x) (x) - -#define SDRAMCON 0x80704 -#define BANK_ON (1<<14) -#define WIDE_DATA (1<<13) -#define TWR_WAIT (1<<12) -#define TRP_WAIT (1<<11) -#define TRAS_WAIT (1<<10) -#define REF_RATE(x) (x) - -#define LBSTAT 0x80708 - -#define RCC_STAT 0x8070c -#define RCC_BUSY (1) - -#define TCMCONFIG 0x80740 -#define TM_DESL2 (1<<10) -#define TM_BANK_WAIT(x) (x<<6) -#define TM_ADD_BANK4(x) (x<<4) -#define TM_PAR_CHECK(x) (x<<3) -#define TM_RW_WAIT(x) (x<<2) -#define TM_SRAM_TYPE(x) (x) - -#define TSRB_BA 0x80744 -#define TSRC_BA 0x80748 -#define TMABR_BA 0x8074c -#define TPD_BA 0x80750 -#define TSRD_BA 0x80758 - -#define TX_CONFIG 0x80760 -#define DRF_THRESH(x) (x<<22) -#define TX_UT_MODE(x) (x<<21) -#define TX_VCI_MASK(x) (x<<17) -#define LBFREE_CNT(x) (x) - -#define TXAAL5_PROTO 0x80764 -#define CPCS_UU(x) (x<<8) -#define CPI(x) (x) - -#define RCMCONFIG 0x80780 -#define RM_DESL2(x) (x<<10) -#define RM_BANK_WAIT(x) (x<<6) -#define RM_ADD_BANK(x) (x<<4) -#define RM_PAR_CHECK(x) (x<<3) -#define RM_RW_WAIT(x) (x<<2) -#define RM_SRAM_TYPE(x) (x) - -#define RCMRSRB_BA 0x80784 -#define RCMLBM_BA 0x80788 -#define RCMABR_BA 0x8078c - -#define RC_CONFIG 0x807c0 -#define UT_RD_DELAY(x) (x<<11) -#define WRAP_MODE(x) (x<<10) -#define RC_UT_MODE(x) (x<<9) -#define RX_ENABLE (1<<8) -#define RX_VALVP(x) (x<<4) -#define RX_VALVC(x) (x) - -#define MCC 0x807c4 -#define OEC 0x807c8 -#define DCC 0x807cc -#define CEC 0x807d0 - -#define HSP_BA 0x807f0 - -#define LB_CONFIG 0x807f4 -#define LB_SIZE(x) (x) - -#define CON_DAT 0x807f8 -#define CON_CTL 0x807fc -#define CON_CTL_MBOX (2<<30) -#define CON_CTL_TCM (1<<30) -#define CON_CTL_RCM (0<<30) -#define CON_CTL_WRITE (1<<29) -#define CON_CTL_READ (0<<29) -#define CON_CTL_BUSY (1<<28) -#define CON_BYTE_DISABLE_3 (1<<22) /* 24..31 */ -#define CON_BYTE_DISABLE_2 (1<<21) /* 16..23 */ -#define CON_BYTE_DISABLE_1 (1<<20) /* 8..15 */ -#define CON_BYTE_DISABLE_0 (1<<19) /* 0..7 */ -#define CON_CTL_ADDR(x) (x) - -#define FRAMER 0x80800 /* to 0x80bfc */ - -/* 3.3 network controller (internal) mailbox registers */ - -#define CS_STPER0 0x0 - /* ... */ -#define CS_STPER31 0x01f - -#define CS_STTIM0 0x020 - /* ... */ -#define CS_STTIM31 0x03f - -#define CS_TGRLD0 0x040 - /* ... */ -#define CS_TGRLD15 0x04f - -#define CS_ERTHR0 0x050 -#define CS_ERTHR1 0x051 -#define CS_ERTHR2 0x052 -#define CS_ERTHR3 0x053 -#define CS_ERTHR4 0x054 -#define CS_ERCTL0 0x055 -#define TX_ENABLE (1<<28) -#define ER_ENABLE (1<<27) -#define CS_ERCTL1 0x056 -#define CS_ERCTL2 0x057 -#define CS_ERSTAT0 0x058 -#define CS_ERSTAT1 0x059 - -#define CS_RTCCT 0x060 -#define CS_RTFWC 0x061 -#define CS_RTFWR 0x062 -#define CS_RTFTC 0x063 -#define CS_RTATR 0x064 - -#define CS_TFBSET 0x070 -#define CS_TFBADD 0x071 -#define CS_TFBSUB 0x072 -#define CS_WCRMAX 0x073 -#define CS_WCRMIN 0x074 -#define CS_WCRINC 0x075 -#define CS_WCRDEC 0x076 -#define CS_WCRCEIL 0x077 -#define CS_BWDCNT 0x078 - -#define CS_OTPPER 0x080 -#define CS_OTWPER 0x081 -#define CS_OTTLIM 0x082 -#define CS_OTTCNT 0x083 - -#define CS_HGRRT0 0x090 - /* ... */ -#define CS_HGRRT7 0x097 - -#define CS_ORPTRS 0x0a0 - -#define RXCON_CLOSE 0x100 - - -#define RCM_MEM_SIZE 0x10000 /* 1M of 32-bit registers */ -#define TCM_MEM_SIZE 0x20000 /* 2M of 32-bit registers */ - -/* 2.5 transmit connection memory registers */ - -#define TSR0_CONN_STATE(x) ((x>>28) & 0x7) -#define TSR0_USE_WMIN (1<<23) -#define TSR0_GROUP(x) ((x & 0x7)<<18) -#define TSR0_ABR (2<<16) -#define TSR0_UBR (1<<16) -#define TSR0_CBR (0<<16) -#define TSR0_PROT (1<<15) -#define TSR0_AAL0_SDU (2<<12) -#define TSR0_AAL0 (1<<12) -#define TSR0_AAL5 (0<<12) -#define TSR0_HALT_ER (1<<11) -#define TSR0_MARK_CI (1<<10) -#define TSR0_MARK_ER (1<<9) -#define TSR0_UPDATE_GER (1<<8) -#define TSR0_RC_INDEX(x) (x & 0x1F) - -#define TSR1_PCR(x) ((x & 0x7FFF)<<16) -#define TSR1_MCR(x) (x & 0x7FFF) - -#define TSR2_ACR(x) ((x & 0x7FFF)<<16) - -#define TSR3_NRM_CNT(x) ((x & 0xFF)<<24) -#define TSR3_CRM_CNT(x) (x & 0xFFFF) - -#define TSR4_FLUSH_CONN (1<<31) -#define TSR4_SESSION_ENDED (1<<30) -#define TSR4_CRC10 (1<<28) -#define TSR4_NULL_CRC10 (1<<27) -#define TSR4_PROT (1<<26) -#define TSR4_AAL0_SDU (2<<23) -#define TSR4_AAL0 (1<<23) -#define TSR4_AAL5 (0<<23) - -#define TSR9_OPEN_CONN (1<<20) - -#define TSR11_ICR(x) ((x & 0x7FFF)<<16) -#define TSR11_TRM(x) ((x & 0x7)<<13) -#define TSR11_NRM(x) ((x & 0x7)<<10) -#define TSR11_ADTF(x) (x & 0x3FF) - -#define TSR13_RDF(x) ((x & 0xF)<<23) -#define TSR13_RIF(x) ((x & 0xF)<<19) -#define TSR13_CDF(x) ((x & 0x7)<<16) -#define TSR13_CRM(x) (x & 0xFFFF) - -#define TSR14_DELETE (1<<31) -#define TSR14_ABR_CLOSE (1<<16) - -/* 2.7.1 per connection receieve state registers */ - -#define RSR0_START_PDU (1<<10) -#define RSR0_OPEN_CONN (1<<6) -#define RSR0_CLOSE_CONN (0<<6) -#define RSR0_PPD_ENABLE (1<<5) -#define RSR0_EPD_ENABLE (1<<4) -#define RSR0_TCP_CKSUM (1<<3) -#define RSR0_AAL5 (0) -#define RSR0_AAL0 (1) -#define RSR0_AAL0_SDU (2) -#define RSR0_RAWCELL (3) -#define RSR0_RAWCELL_CRC10 (4) - -#define RSR1_AQI_ENABLE (1<<20) -#define RSR1_RBPL_ONLY (1<<19) -#define RSR1_GROUP(x) ((x)<<16) - -#define RSR4_AQI_ENABLE (1<<30) -#define RSR4_GROUP(x) ((x)<<27) -#define RSR4_RBPL_ONLY (1<<26) - -/* 2.1.4 transmit packet descriptor */ - -#define TPD_USERCELL 0x0 -#define TPD_SEGMENT_OAMF5 0x4 -#define TPD_END2END_OAMF5 0x5 -#define TPD_RMCELL 0x6 -#define TPD_CELLTYPE(x) (x<<3) -#define TPD_EOS (1<<2) -#define TPD_CLP (1<<1) -#define TPD_INT (1<<0) -#define TPD_LST (1<<31) - -/* table 4.3 serial eeprom information */ - -#define PROD_ID 0x08 /* char[] */ -#define PROD_ID_LEN 30 -#define HW_REV 0x26 /* char[] */ -#define M_SN 0x3a /* integer */ -#define MEDIA 0x3e /* integer */ -#define HE155MM 0x26 -#define HE622MM 0x27 -#define HE155SM 0x46 -#define HE622SM 0x47 -#define MAC_ADDR 0x42 /* char[] */ - -#define CS_LOW 0x0 -#define CS_HIGH ID_CS /* HOST_CNTL_ID_PROM_SEL */ -#define CLK_LOW 0x0 -#define CLK_HIGH ID_CLOCK /* HOST_CNTL_ID_PROM_CLOCK */ -#define SI_HIGH ID_DIN /* HOST_CNTL_ID_PROM_DATA_IN */ -#define EEPROM_DELAY 400 /* microseconds */ - -#endif /* _HE_H_ */ diff --git a/drivers/atm/idt77105.c b/drivers/atm/idt77105.c deleted file mode 100644 index 4bbcca7f77c8..000000000000 --- a/drivers/atm/idt77105.c +++ /dev/null @@ -1,376 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* drivers/atm/idt77105.c - IDT77105 (PHY) driver */ - -/* Written 1999 by Greg Banks, NEC Australia . Based on suni.c */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "idt77105.h" - -#undef GENERAL_DEBUG - -#ifdef GENERAL_DEBUG -#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) -#else -#define DPRINTK(format,args...) -#endif - - -struct idt77105_priv { - struct idt77105_stats stats; /* link diagnostics */ - struct atm_dev *dev; /* device back-pointer */ - struct idt77105_priv *next; - int loop_mode; - unsigned char old_mcr; /* storage of MCR reg while signal lost */ -}; - -static DEFINE_SPINLOCK(idt77105_priv_lock); - -#define PRIV(dev) ((struct idt77105_priv *) dev->phy_data) - -#define PUT(val,reg) dev->ops->phy_put(dev,val,IDT77105_##reg) -#define GET(reg) dev->ops->phy_get(dev,IDT77105_##reg) - -static void idt77105_stats_timer_func(struct timer_list *); -static void idt77105_restart_timer_func(struct timer_list *); - - -static DEFINE_TIMER(stats_timer, idt77105_stats_timer_func); -static DEFINE_TIMER(restart_timer, idt77105_restart_timer_func); -static int start_timer = 1; -static struct idt77105_priv *idt77105_all = NULL; - -/* - * Retrieve the value of one of the IDT77105's counters. - * `counter' is one of the IDT77105_CTRSEL_* constants. - */ -static u16 get_counter(struct atm_dev *dev, int counter) -{ - u16 val; - - /* write the counter bit into PHY register 6 */ - PUT(counter, CTRSEL); - /* read the low 8 bits from register 4 */ - val = GET(CTRLO); - /* read the high 8 bits from register 5 */ - val |= GET(CTRHI)<<8; - - return val; -} - -/* - * Timer function called every second to gather statistics - * from the 77105. This is done because the h/w registers - * will overflow if not read at least once per second. The - * kernel's stats are much higher precision. Also, having - * a separate copy of the stats allows implementation of - * an ioctl which gathers the stats *without* zero'ing them. - */ -static void idt77105_stats_timer_func(struct timer_list *unused) -{ - struct idt77105_priv *walk; - struct atm_dev *dev; - struct idt77105_stats *stats; - - DPRINTK("IDT77105 gathering statistics\n"); - for (walk = idt77105_all; walk; walk = walk->next) { - dev = walk->dev; - - stats = &walk->stats; - stats->symbol_errors += get_counter(dev, IDT77105_CTRSEL_SEC); - stats->tx_cells += get_counter(dev, IDT77105_CTRSEL_TCC); - stats->rx_cells += get_counter(dev, IDT77105_CTRSEL_RCC); - stats->rx_hec_errors += get_counter(dev, IDT77105_CTRSEL_RHEC); - } - if (!start_timer) mod_timer(&stats_timer,jiffies+IDT77105_STATS_TIMER_PERIOD); -} - - -/* - * A separate timer func which handles restarting PHY chips which - * have had the cable re-inserted after being pulled out. This is - * done by polling the Good Signal Bit in the Interrupt Status - * register every 5 seconds. The other technique (checking Good - * Signal Bit in the interrupt handler) cannot be used because PHY - * interrupts need to be disabled when the cable is pulled out - * to avoid lots of spurious cell error interrupts. - */ -static void idt77105_restart_timer_func(struct timer_list *unused) -{ - struct idt77105_priv *walk; - struct atm_dev *dev; - unsigned char istat; - - DPRINTK("IDT77105 checking for cable re-insertion\n"); - for (walk = idt77105_all; walk; walk = walk->next) { - dev = walk->dev; - - if (dev->signal != ATM_PHY_SIG_LOST) - continue; - - istat = GET(ISTAT); /* side effect: clears all interrupt status bits */ - if (istat & IDT77105_ISTAT_GOODSIG) { - /* Found signal again */ - atm_dev_signal_change(dev, ATM_PHY_SIG_FOUND); - printk(KERN_NOTICE "%s(itf %d): signal detected again\n", - dev->type,dev->number); - /* flush the receive FIFO */ - PUT( GET(DIAG) | IDT77105_DIAG_RFLUSH, DIAG); - /* re-enable interrupts */ - PUT( walk->old_mcr ,MCR); - } - } - if (!start_timer) mod_timer(&restart_timer,jiffies+IDT77105_RESTART_TIMER_PERIOD); -} - - -static int fetch_stats(struct atm_dev *dev,struct idt77105_stats __user *arg,int zero) -{ - unsigned long flags; - struct idt77105_stats stats; - - spin_lock_irqsave(&idt77105_priv_lock, flags); - memcpy(&stats, &PRIV(dev)->stats, sizeof(struct idt77105_stats)); - if (zero) - memset(&PRIV(dev)->stats, 0, sizeof(struct idt77105_stats)); - spin_unlock_irqrestore(&idt77105_priv_lock, flags); - if (arg == NULL) - return 0; - return copy_to_user(arg, &stats, - sizeof(struct idt77105_stats)) ? -EFAULT : 0; -} - - -static int set_loopback(struct atm_dev *dev,int mode) -{ - int diag; - - diag = GET(DIAG) & ~IDT77105_DIAG_LCMASK; - switch (mode) { - case ATM_LM_NONE: - break; - case ATM_LM_LOC_ATM: - diag |= IDT77105_DIAG_LC_PHY_LOOPBACK; - break; - case ATM_LM_RMT_ATM: - diag |= IDT77105_DIAG_LC_LINE_LOOPBACK; - break; - default: - return -EINVAL; - } - PUT(diag,DIAG); - printk(KERN_NOTICE "%s(%d) Loopback mode is: %s\n", dev->type, - dev->number, - (mode == ATM_LM_NONE ? "NONE" : - (mode == ATM_LM_LOC_ATM ? "DIAG (local)" : - (mode == IDT77105_DIAG_LC_LINE_LOOPBACK ? "LOOP (remote)" : - "unknown"))) - ); - PRIV(dev)->loop_mode = mode; - return 0; -} - - -static int idt77105_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) -{ - printk(KERN_NOTICE "%s(%d) idt77105_ioctl() called\n",dev->type,dev->number); - switch (cmd) { - case IDT77105_GETSTATZ: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - fallthrough; - case IDT77105_GETSTAT: - return fetch_stats(dev, arg, cmd == IDT77105_GETSTATZ); - case ATM_SETLOOP: - return set_loopback(dev,(int)(unsigned long) arg); - case ATM_GETLOOP: - return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ? - -EFAULT : 0; - case ATM_QUERYLOOP: - return put_user(ATM_LM_LOC_ATM | ATM_LM_RMT_ATM, - (int __user *) arg) ? -EFAULT : 0; - default: - return -ENOIOCTLCMD; - } -} - - - -static void idt77105_int(struct atm_dev *dev) -{ - unsigned char istat; - - istat = GET(ISTAT); /* side effect: clears all interrupt status bits */ - - DPRINTK("IDT77105 generated an interrupt, istat=%02x\n", (unsigned)istat); - - if (istat & IDT77105_ISTAT_RSCC) { - /* Rx Signal Condition Change - line went up or down */ - if (istat & IDT77105_ISTAT_GOODSIG) { /* signal detected again */ - /* This should not happen (restart timer does it) but JIC */ - atm_dev_signal_change(dev, ATM_PHY_SIG_FOUND); - } else { /* signal lost */ - /* - * Disable interrupts and stop all transmission and - * reception - the restart timer will restore these. - */ - PRIV(dev)->old_mcr = GET(MCR); - PUT( - (PRIV(dev)->old_mcr| - IDT77105_MCR_DREC| - IDT77105_MCR_DRIC| - IDT77105_MCR_HALTTX - ) & ~IDT77105_MCR_EIP, MCR); - atm_dev_signal_change(dev, ATM_PHY_SIG_LOST); - printk(KERN_NOTICE "%s(itf %d): signal lost\n", - dev->type,dev->number); - } - } - - if (istat & IDT77105_ISTAT_RFO) { - /* Rx FIFO Overrun -- perform a FIFO flush */ - PUT( GET(DIAG) | IDT77105_DIAG_RFLUSH, DIAG); - printk(KERN_NOTICE "%s(itf %d): receive FIFO overrun\n", - dev->type,dev->number); - } -#ifdef GENERAL_DEBUG - if (istat & (IDT77105_ISTAT_HECERR | IDT77105_ISTAT_SCR | - IDT77105_ISTAT_RSE)) { - /* normally don't care - just report in stats */ - printk(KERN_NOTICE "%s(itf %d): received cell with error\n", - dev->type,dev->number); - } -#endif -} - - -static int idt77105_start(struct atm_dev *dev) -{ - unsigned long flags; - - if (!(dev->phy_data = kmalloc_obj(struct idt77105_priv))) - return -ENOMEM; - PRIV(dev)->dev = dev; - spin_lock_irqsave(&idt77105_priv_lock, flags); - PRIV(dev)->next = idt77105_all; - idt77105_all = PRIV(dev); - spin_unlock_irqrestore(&idt77105_priv_lock, flags); - memset(&PRIV(dev)->stats,0,sizeof(struct idt77105_stats)); - - /* initialise dev->signal from Good Signal Bit */ - atm_dev_signal_change(dev, - GET(ISTAT) & IDT77105_ISTAT_GOODSIG ? - ATM_PHY_SIG_FOUND : ATM_PHY_SIG_LOST); - if (dev->signal == ATM_PHY_SIG_LOST) - printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type, - dev->number); - - /* initialise loop mode from hardware */ - switch ( GET(DIAG) & IDT77105_DIAG_LCMASK ) { - case IDT77105_DIAG_LC_NORMAL: - PRIV(dev)->loop_mode = ATM_LM_NONE; - break; - case IDT77105_DIAG_LC_PHY_LOOPBACK: - PRIV(dev)->loop_mode = ATM_LM_LOC_ATM; - break; - case IDT77105_DIAG_LC_LINE_LOOPBACK: - PRIV(dev)->loop_mode = ATM_LM_RMT_ATM; - break; - } - - /* enable interrupts, e.g. on loss of signal */ - PRIV(dev)->old_mcr = GET(MCR); - if (dev->signal == ATM_PHY_SIG_FOUND) { - PRIV(dev)->old_mcr |= IDT77105_MCR_EIP; - PUT(PRIV(dev)->old_mcr, MCR); - } - - - idt77105_stats_timer_func(0); /* clear 77105 counters */ - (void) fetch_stats(dev,NULL,1); /* clear kernel counters */ - - spin_lock_irqsave(&idt77105_priv_lock, flags); - if (start_timer) { - start_timer = 0; - - stats_timer.expires = jiffies+IDT77105_STATS_TIMER_PERIOD; - add_timer(&stats_timer); - - restart_timer.expires = jiffies+IDT77105_RESTART_TIMER_PERIOD; - add_timer(&restart_timer); - } - spin_unlock_irqrestore(&idt77105_priv_lock, flags); - return 0; -} - - -static int idt77105_stop(struct atm_dev *dev) -{ - struct idt77105_priv *walk, *prev; - - DPRINTK("%s(itf %d): stopping IDT77105\n",dev->type,dev->number); - - /* disable interrupts */ - PUT( GET(MCR) & ~IDT77105_MCR_EIP, MCR ); - - /* detach private struct from atm_dev & free */ - for (prev = NULL, walk = idt77105_all ; - walk != NULL; - prev = walk, walk = walk->next) { - if (walk->dev == dev) { - if (prev != NULL) - prev->next = walk->next; - else - idt77105_all = walk->next; - dev->phy = NULL; - dev->phy_data = NULL; - kfree(walk); - break; - } - } - - return 0; -} - - -static const struct atmphy_ops idt77105_ops = { - .start = idt77105_start, - .ioctl = idt77105_ioctl, - .interrupt = idt77105_int, - .stop = idt77105_stop, -}; - - -int idt77105_init(struct atm_dev *dev) -{ - dev->phy = &idt77105_ops; - return 0; -} - -EXPORT_SYMBOL(idt77105_init); - -static void __exit idt77105_exit(void) -{ - /* turn off timers */ - timer_delete_sync(&stats_timer); - timer_delete_sync(&restart_timer); -} - -module_exit(idt77105_exit); - -MODULE_DESCRIPTION("IDT77105 PHY driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/atm/idt77105.h b/drivers/atm/idt77105.h deleted file mode 100644 index 8dfea9e361de..000000000000 --- a/drivers/atm/idt77105.h +++ /dev/null @@ -1,92 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* drivers/atm/idt77105.h - IDT77105 (PHY) declarations */ - -/* Written 1999 by Greg Banks, NEC Australia . Based on suni.h */ - - -#ifndef DRIVER_ATM_IDT77105_H -#define DRIVER_ATM_IDT77105_H - -#include -#include - - -/* IDT77105 registers */ - -#define IDT77105_MCR 0x0 /* Master Control Register */ -#define IDT77105_ISTAT 0x1 /* Interrupt Status */ -#define IDT77105_DIAG 0x2 /* Diagnostic Control */ -#define IDT77105_LEDHEC 0x3 /* LED Driver & HEC Status/Control */ -#define IDT77105_CTRLO 0x4 /* Low Byte Counter Register */ -#define IDT77105_CTRHI 0x5 /* High Byte Counter Register */ -#define IDT77105_CTRSEL 0x6 /* Counter Register Read Select */ - -/* IDT77105 register values */ - -/* MCR */ -#define IDT77105_MCR_UPLO 0x80 /* R/W, User Prog'le Output Latch */ -#define IDT77105_MCR_DREC 0x40 /* R/W, Discard Receive Error Cells */ -#define IDT77105_MCR_ECEIO 0x20 /* R/W, Enable Cell Error Interrupts - * Only */ -#define IDT77105_MCR_TDPC 0x10 /* R/W, Transmit Data Parity Check */ -#define IDT77105_MCR_DRIC 0x08 /* R/W, Discard Received Idle Cells */ -#define IDT77105_MCR_HALTTX 0x04 /* R/W, Halt Tx */ -#define IDT77105_MCR_UMODE 0x02 /* R/W, Utopia (cell/byte) Mode */ -#define IDT77105_MCR_EIP 0x01 /* R/W, Enable Interrupt Pin */ - -/* ISTAT */ -#define IDT77105_ISTAT_GOODSIG 0x40 /* R, Good Signal Bit */ -#define IDT77105_ISTAT_HECERR 0x20 /* sticky, HEC Error*/ -#define IDT77105_ISTAT_SCR 0x10 /* sticky, Short Cell Received */ -#define IDT77105_ISTAT_TPE 0x08 /* sticky, Transmit Parity Error */ -#define IDT77105_ISTAT_RSCC 0x04 /* sticky, Rx Signal Condition Change */ -#define IDT77105_ISTAT_RSE 0x02 /* sticky, Rx Symbol Error */ -#define IDT77105_ISTAT_RFO 0x01 /* sticky, Rx FIFO Overrun */ - -/* DIAG */ -#define IDT77105_DIAG_FTD 0x80 /* R/W, Force TxClav deassert */ -#define IDT77105_DIAG_ROS 0x40 /* R/W, RxClav operation select */ -#define IDT77105_DIAG_MPCS 0x20 /* R/W, Multi-PHY config'n select */ -#define IDT77105_DIAG_RFLUSH 0x10 /* R/W, clear receive FIFO */ -#define IDT77105_DIAG_ITPE 0x08 /* R/W, Insert Tx payload error */ -#define IDT77105_DIAG_ITHE 0x04 /* R/W, Insert Tx HEC error */ -#define IDT77105_DIAG_UMODE 0x02 /* R/W, Utopia (cell/byte) Mode */ -#define IDT77105_DIAG_LCMASK 0x03 /* R/W, Loopback Control */ - -#define IDT77105_DIAG_LC_NORMAL 0x00 /* Receive from network */ -#define IDT77105_DIAG_LC_PHY_LOOPBACK 0x02 -#define IDT77105_DIAG_LC_LINE_LOOPBACK 0x03 - -/* LEDHEC */ -#define IDT77105_LEDHEC_DRHC 0x40 /* R/W, Disable Rx HEC check */ -#define IDT77105_LEDHEC_DTHC 0x20 /* R/W, Disable Tx HEC calculation */ -#define IDT77105_LEDHEC_RPWMASK 0x18 /* R/W, RxRef pulse width select */ -#define IDT77105_LEDHEC_TFS 0x04 /* R, Tx FIFO Status (1=empty) */ -#define IDT77105_LEDHEC_TLS 0x02 /* R, Tx LED Status (1=lit) */ -#define IDT77105_LEDHEC_RLS 0x01 /* R, Rx LED Status (1=lit) */ - -#define IDT77105_LEDHEC_RPW_1 0x00 /* RxRef active for 1 RxClk cycle */ -#define IDT77105_LEDHEC_RPW_2 0x08 /* RxRef active for 2 RxClk cycle */ -#define IDT77105_LEDHEC_RPW_4 0x10 /* RxRef active for 4 RxClk cycle */ -#define IDT77105_LEDHEC_RPW_8 0x18 /* RxRef active for 8 RxClk cycle */ - -/* CTRSEL */ -#define IDT77105_CTRSEL_SEC 0x08 /* W, Symbol Error Counter */ -#define IDT77105_CTRSEL_TCC 0x04 /* W, Tx Cell Counter */ -#define IDT77105_CTRSEL_RCC 0x02 /* W, Rx Cell Counter */ -#define IDT77105_CTRSEL_RHEC 0x01 /* W, Rx HEC Error Counter */ - -#ifdef __KERNEL__ -int idt77105_init(struct atm_dev *dev); -#endif - -/* - * Tunable parameters - */ - -/* Time between samples of the hardware cell counters. Should be <= 1 sec */ -#define IDT77105_STATS_TIMER_PERIOD (HZ) -/* Time between checks to see if the signal has been found again */ -#define IDT77105_RESTART_TIMER_PERIOD (5 * HZ) - -#endif diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c deleted file mode 100644 index 7f8aaf5e6e43..000000000000 --- a/drivers/atm/idt77252.c +++ /dev/null @@ -1,3797 +0,0 @@ -/******************************************************************* - * - * Copyright (c) 2000 ATecoM GmbH - * - * The author may be reached at ecd@atecom.com. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - *******************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifdef CONFIG_ATM_IDT77252_USE_SUNI -#include "suni.h" -#endif /* CONFIG_ATM_IDT77252_USE_SUNI */ - - -#include "idt77252.h" -#include "idt77252_tables.h" - -static unsigned int vpibits = 1; - - -#define ATM_IDT77252_SEND_IDLE 1 - - -/* - * Debug HACKs. - */ -#define DEBUG_MODULE 1 -#undef HAVE_EEPROM /* does not work, yet. */ - -#ifdef CONFIG_ATM_IDT77252_DEBUG -static unsigned long debug = DBG_GENERAL; -#endif - - -#define SAR_RX_DELAY (SAR_CFG_RXINT_NODELAY) - - -/* - * SCQ Handling. - */ -static struct scq_info *alloc_scq(struct idt77252_dev *, int); -static void free_scq(struct idt77252_dev *, struct scq_info *); -static int queue_skb(struct idt77252_dev *, struct vc_map *, - struct sk_buff *, int oam); -static void drain_scq(struct idt77252_dev *, struct vc_map *); -static unsigned long get_free_scd(struct idt77252_dev *, struct vc_map *); -static void fill_scd(struct idt77252_dev *, struct scq_info *, int); - -/* - * FBQ Handling. - */ -static int push_rx_skb(struct idt77252_dev *, - struct sk_buff *, int queue); -static void recycle_rx_skb(struct idt77252_dev *, struct sk_buff *); -static void flush_rx_pool(struct idt77252_dev *, struct rx_pool *); -static void recycle_rx_pool_skb(struct idt77252_dev *, - struct rx_pool *); -static void add_rx_skb(struct idt77252_dev *, int queue, - unsigned int size, unsigned int count); - -/* - * RSQ Handling. - */ -static int init_rsq(struct idt77252_dev *); -static void deinit_rsq(struct idt77252_dev *); -static void idt77252_rx(struct idt77252_dev *); - -/* - * TSQ handling. - */ -static int init_tsq(struct idt77252_dev *); -static void deinit_tsq(struct idt77252_dev *); -static void idt77252_tx(struct idt77252_dev *); - - -/* - * ATM Interface. - */ -static void idt77252_dev_close(struct atm_dev *dev); -static int idt77252_open(struct atm_vcc *vcc); -static void idt77252_close(struct atm_vcc *vcc); -static int idt77252_send(struct atm_vcc *vcc, struct sk_buff *skb); -static int idt77252_send_oam(struct atm_vcc *vcc, void *cell, - int flags); -static void idt77252_phy_put(struct atm_dev *dev, unsigned char value, - unsigned long addr); -static unsigned char idt77252_phy_get(struct atm_dev *dev, unsigned long addr); -static int idt77252_change_qos(struct atm_vcc *vcc, struct atm_qos *qos, - int flags); -static int idt77252_proc_read(struct atm_dev *dev, loff_t * pos, - char *page); -static void idt77252_softint(struct work_struct *work); - - -static const struct atmdev_ops idt77252_ops = -{ - .dev_close = idt77252_dev_close, - .open = idt77252_open, - .close = idt77252_close, - .send = idt77252_send, - .send_oam = idt77252_send_oam, - .phy_put = idt77252_phy_put, - .phy_get = idt77252_phy_get, - .change_qos = idt77252_change_qos, - .proc_read = idt77252_proc_read, - .owner = THIS_MODULE -}; - -static struct idt77252_dev *idt77252_chain = NULL; -static unsigned int idt77252_sram_write_errors = 0; - -/*****************************************************************************/ -/* */ -/* I/O and Utility Bus */ -/* */ -/*****************************************************************************/ - -static void -waitfor_idle(struct idt77252_dev *card) -{ - u32 stat; - - stat = readl(SAR_REG_STAT); - while (stat & SAR_STAT_CMDBZ) - stat = readl(SAR_REG_STAT); -} - -static u32 -read_sram(struct idt77252_dev *card, unsigned long addr) -{ - unsigned long flags; - u32 value; - - spin_lock_irqsave(&card->cmd_lock, flags); - writel(SAR_CMD_READ_SRAM | (addr << 2), SAR_REG_CMD); - waitfor_idle(card); - value = readl(SAR_REG_DR0); - spin_unlock_irqrestore(&card->cmd_lock, flags); - return value; -} - -static void -write_sram(struct idt77252_dev *card, unsigned long addr, u32 value) -{ - unsigned long flags; - - if ((idt77252_sram_write_errors == 0) && - (((addr > card->tst[0] + card->tst_size - 2) && - (addr < card->tst[0] + card->tst_size)) || - ((addr > card->tst[1] + card->tst_size - 2) && - (addr < card->tst[1] + card->tst_size)))) { - printk("%s: ERROR: TST JMP section at %08lx written: %08x\n", - card->name, addr, value); - } - - spin_lock_irqsave(&card->cmd_lock, flags); - writel(value, SAR_REG_DR0); - writel(SAR_CMD_WRITE_SRAM | (addr << 2), SAR_REG_CMD); - waitfor_idle(card); - spin_unlock_irqrestore(&card->cmd_lock, flags); -} - -static u8 -read_utility(void *dev, unsigned long ubus_addr) -{ - struct idt77252_dev *card = dev; - unsigned long flags; - u8 value; - - if (!card) { - printk("Error: No such device.\n"); - return -1; - } - - spin_lock_irqsave(&card->cmd_lock, flags); - writel(SAR_CMD_READ_UTILITY + ubus_addr, SAR_REG_CMD); - waitfor_idle(card); - value = readl(SAR_REG_DR0); - spin_unlock_irqrestore(&card->cmd_lock, flags); - return value; -} - -static void -write_utility(void *dev, unsigned long ubus_addr, u8 value) -{ - struct idt77252_dev *card = dev; - unsigned long flags; - - if (!card) { - printk("Error: No such device.\n"); - return; - } - - spin_lock_irqsave(&card->cmd_lock, flags); - writel((u32) value, SAR_REG_DR0); - writel(SAR_CMD_WRITE_UTILITY + ubus_addr, SAR_REG_CMD); - waitfor_idle(card); - spin_unlock_irqrestore(&card->cmd_lock, flags); -} - -#ifdef HAVE_EEPROM -static u32 rdsrtab[] = -{ - SAR_GP_EECS | SAR_GP_EESCLK, - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - SAR_GP_EEDO, - SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */ - 0, - SAR_GP_EESCLK, /* 0 */ - SAR_GP_EEDO, - SAR_GP_EESCLK | SAR_GP_EEDO /* 1 */ -}; - -static u32 wrentab[] = -{ - SAR_GP_EECS | SAR_GP_EESCLK, - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - SAR_GP_EEDO, - SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */ - SAR_GP_EEDO, - SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK /* 0 */ -}; - -static u32 rdtab[] = -{ - SAR_GP_EECS | SAR_GP_EESCLK, - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - SAR_GP_EEDO, - SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */ - SAR_GP_EEDO, - SAR_GP_EESCLK | SAR_GP_EEDO /* 1 */ -}; - -static u32 wrtab[] = -{ - SAR_GP_EECS | SAR_GP_EESCLK, - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - 0, - SAR_GP_EESCLK, /* 0 */ - SAR_GP_EEDO, - SAR_GP_EESCLK | SAR_GP_EEDO, /* 1 */ - 0, - SAR_GP_EESCLK /* 0 */ -}; - -static u32 clktab[] = -{ - 0, - SAR_GP_EESCLK, - 0, - SAR_GP_EESCLK, - 0, - SAR_GP_EESCLK, - 0, - SAR_GP_EESCLK, - 0, - SAR_GP_EESCLK, - 0, - SAR_GP_EESCLK, - 0, - SAR_GP_EESCLK, - 0, - SAR_GP_EESCLK, - 0 -}; - -static u32 -idt77252_read_gp(struct idt77252_dev *card) -{ - u32 gp; - - gp = readl(SAR_REG_GP); -#if 0 - printk("RD: %s\n", gp & SAR_GP_EEDI ? "1" : "0"); -#endif - return gp; -} - -static void -idt77252_write_gp(struct idt77252_dev *card, u32 value) -{ - unsigned long flags; - -#if 0 - printk("WR: %s %s %s\n", value & SAR_GP_EECS ? " " : "/CS", - value & SAR_GP_EESCLK ? "HIGH" : "LOW ", - value & SAR_GP_EEDO ? "1" : "0"); -#endif - - spin_lock_irqsave(&card->cmd_lock, flags); - waitfor_idle(card); - writel(value, SAR_REG_GP); - spin_unlock_irqrestore(&card->cmd_lock, flags); -} - -static u8 -idt77252_eeprom_read_status(struct idt77252_dev *card) -{ - u8 byte; - u32 gp; - int i, j; - - gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO); - - for (i = 0; i < ARRAY_SIZE(rdsrtab); i++) { - idt77252_write_gp(card, gp | rdsrtab[i]); - udelay(5); - } - idt77252_write_gp(card, gp | SAR_GP_EECS); - udelay(5); - - byte = 0; - for (i = 0, j = 0; i < 8; i++) { - byte <<= 1; - - idt77252_write_gp(card, gp | clktab[j++]); - udelay(5); - - byte |= idt77252_read_gp(card) & SAR_GP_EEDI ? 1 : 0; - - idt77252_write_gp(card, gp | clktab[j++]); - udelay(5); - } - idt77252_write_gp(card, gp | SAR_GP_EECS); - udelay(5); - - return byte; -} - -static u8 -idt77252_eeprom_read_byte(struct idt77252_dev *card, u8 offset) -{ - u8 byte; - u32 gp; - int i, j; - - gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO); - - for (i = 0; i < ARRAY_SIZE(rdtab); i++) { - idt77252_write_gp(card, gp | rdtab[i]); - udelay(5); - } - idt77252_write_gp(card, gp | SAR_GP_EECS); - udelay(5); - - for (i = 0, j = 0; i < 8; i++) { - idt77252_write_gp(card, gp | clktab[j++] | - (offset & 1 ? SAR_GP_EEDO : 0)); - udelay(5); - - idt77252_write_gp(card, gp | clktab[j++] | - (offset & 1 ? SAR_GP_EEDO : 0)); - udelay(5); - - offset >>= 1; - } - idt77252_write_gp(card, gp | SAR_GP_EECS); - udelay(5); - - byte = 0; - for (i = 0, j = 0; i < 8; i++) { - byte <<= 1; - - idt77252_write_gp(card, gp | clktab[j++]); - udelay(5); - - byte |= idt77252_read_gp(card) & SAR_GP_EEDI ? 1 : 0; - - idt77252_write_gp(card, gp | clktab[j++]); - udelay(5); - } - idt77252_write_gp(card, gp | SAR_GP_EECS); - udelay(5); - - return byte; -} - -static void -idt77252_eeprom_write_byte(struct idt77252_dev *card, u8 offset, u8 data) -{ - u32 gp; - int i, j; - - gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO); - - for (i = 0; i < ARRAY_SIZE(wrentab); i++) { - idt77252_write_gp(card, gp | wrentab[i]); - udelay(5); - } - idt77252_write_gp(card, gp | SAR_GP_EECS); - udelay(5); - - for (i = 0; i < ARRAY_SIZE(wrtab); i++) { - idt77252_write_gp(card, gp | wrtab[i]); - udelay(5); - } - idt77252_write_gp(card, gp | SAR_GP_EECS); - udelay(5); - - for (i = 0, j = 0; i < 8; i++) { - idt77252_write_gp(card, gp | clktab[j++] | - (offset & 1 ? SAR_GP_EEDO : 0)); - udelay(5); - - idt77252_write_gp(card, gp | clktab[j++] | - (offset & 1 ? SAR_GP_EEDO : 0)); - udelay(5); - - offset >>= 1; - } - idt77252_write_gp(card, gp | SAR_GP_EECS); - udelay(5); - - for (i = 0, j = 0; i < 8; i++) { - idt77252_write_gp(card, gp | clktab[j++] | - (data & 1 ? SAR_GP_EEDO : 0)); - udelay(5); - - idt77252_write_gp(card, gp | clktab[j++] | - (data & 1 ? SAR_GP_EEDO : 0)); - udelay(5); - - data >>= 1; - } - idt77252_write_gp(card, gp | SAR_GP_EECS); - udelay(5); -} - -static void -idt77252_eeprom_init(struct idt77252_dev *card) -{ - u32 gp; - - gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO); - - idt77252_write_gp(card, gp | SAR_GP_EECS | SAR_GP_EESCLK); - udelay(5); - idt77252_write_gp(card, gp | SAR_GP_EECS); - udelay(5); - idt77252_write_gp(card, gp | SAR_GP_EECS | SAR_GP_EESCLK); - udelay(5); - idt77252_write_gp(card, gp | SAR_GP_EECS); - udelay(5); -} -#endif /* HAVE_EEPROM */ - - -#ifdef CONFIG_ATM_IDT77252_DEBUG -static void -dump_tct(struct idt77252_dev *card, int index) -{ - unsigned long tct; - int i; - - tct = (unsigned long) (card->tct_base + index * SAR_SRAM_TCT_SIZE); - - printk("%s: TCT %x:", card->name, index); - for (i = 0; i < 8; i++) { - printk(" %08x", read_sram(card, tct + i)); - } - printk("\n"); -} - -static void -idt77252_tx_dump(struct idt77252_dev *card) -{ - struct atm_vcc *vcc; - struct vc_map *vc; - int i; - - printk("%s\n", __func__); - for (i = 0; i < card->tct_size; i++) { - vc = card->vcs[i]; - if (!vc) - continue; - - vcc = NULL; - if (vc->rx_vcc) - vcc = vc->rx_vcc; - else if (vc->tx_vcc) - vcc = vc->tx_vcc; - - if (!vcc) - continue; - - printk("%s: Connection %d:\n", card->name, vc->index); - dump_tct(card, vc->index); - } -} -#endif - - -/*****************************************************************************/ -/* */ -/* SCQ Handling */ -/* */ -/*****************************************************************************/ - -static int -sb_pool_add(struct idt77252_dev *card, struct sk_buff *skb, int queue) -{ - struct sb_pool *pool = &card->sbpool[queue]; - int index; - - index = pool->index; - while (pool->skb[index]) { - index = (index + 1) & FBQ_MASK; - if (index == pool->index) - return -ENOBUFS; - } - - pool->skb[index] = skb; - IDT77252_PRV_POOL(skb) = POOL_HANDLE(queue, index); - - pool->index = (index + 1) & FBQ_MASK; - return 0; -} - -static void -sb_pool_remove(struct idt77252_dev *card, struct sk_buff *skb) -{ - unsigned int queue, index; - u32 handle; - - handle = IDT77252_PRV_POOL(skb); - - queue = POOL_QUEUE(handle); - if (queue > 3) - return; - - index = POOL_INDEX(handle); - if (index > FBQ_SIZE - 1) - return; - - card->sbpool[queue].skb[index] = NULL; -} - -static struct sk_buff * -sb_pool_skb(struct idt77252_dev *card, u32 handle) -{ - unsigned int queue, index; - - queue = POOL_QUEUE(handle); - if (queue > 3) - return NULL; - - index = POOL_INDEX(handle); - if (index > FBQ_SIZE - 1) - return NULL; - - return card->sbpool[queue].skb[index]; -} - -static struct scq_info * -alloc_scq(struct idt77252_dev *card, int class) -{ - struct scq_info *scq; - - scq = kzalloc_obj(struct scq_info); - if (!scq) - return NULL; - scq->base = dma_alloc_coherent(&card->pcidev->dev, SCQ_SIZE, - &scq->paddr, GFP_KERNEL); - if (scq->base == NULL) { - kfree(scq); - return NULL; - } - - scq->next = scq->base; - scq->last = scq->base + (SCQ_ENTRIES - 1); - atomic_set(&scq->used, 0); - - spin_lock_init(&scq->lock); - spin_lock_init(&scq->skblock); - - skb_queue_head_init(&scq->transmit); - skb_queue_head_init(&scq->pending); - - TXPRINTK("idt77252: SCQ: base 0x%p, next 0x%p, last 0x%p, paddr %08llx\n", - scq->base, scq->next, scq->last, (unsigned long long)scq->paddr); - - return scq; -} - -static void -free_scq(struct idt77252_dev *card, struct scq_info *scq) -{ - struct sk_buff *skb; - struct atm_vcc *vcc; - - dma_free_coherent(&card->pcidev->dev, SCQ_SIZE, - scq->base, scq->paddr); - - while ((skb = skb_dequeue(&scq->transmit))) { - dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), - skb->len, DMA_TO_DEVICE); - - vcc = ATM_SKB(skb)->vcc; - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb(skb); - } - - while ((skb = skb_dequeue(&scq->pending))) { - dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), - skb->len, DMA_TO_DEVICE); - - vcc = ATM_SKB(skb)->vcc; - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb(skb); - } - - kfree(scq); -} - - -static int -push_on_scq(struct idt77252_dev *card, struct vc_map *vc, struct sk_buff *skb) -{ - struct scq_info *scq = vc->scq; - unsigned long flags; - struct scqe *tbd; - int entries; - - TXPRINTK("%s: SCQ: next 0x%p\n", card->name, scq->next); - - atomic_inc(&scq->used); - entries = atomic_read(&scq->used); - if (entries > (SCQ_ENTRIES - 1)) { - atomic_dec(&scq->used); - goto out; - } - - skb_queue_tail(&scq->transmit, skb); - - spin_lock_irqsave(&vc->lock, flags); - if (vc->estimator) { - struct atm_vcc *vcc = vc->tx_vcc; - struct sock *sk = sk_atm(vcc); - - vc->estimator->cells += (skb->len + 47) / 48; - if (refcount_read(&sk->sk_wmem_alloc) > - (sk->sk_sndbuf >> 1)) { - u32 cps = vc->estimator->maxcps; - - vc->estimator->cps = cps; - vc->estimator->avcps = cps << 5; - if (vc->lacr < vc->init_er) { - vc->lacr = vc->init_er; - writel(TCMDQ_LACR | (vc->lacr << 16) | - vc->index, SAR_REG_TCMDQ); - } - } - } - spin_unlock_irqrestore(&vc->lock, flags); - - tbd = &IDT77252_PRV_TBD(skb); - - spin_lock_irqsave(&scq->lock, flags); - scq->next->word_1 = cpu_to_le32(tbd->word_1 | - SAR_TBD_TSIF | SAR_TBD_GTSI); - scq->next->word_2 = cpu_to_le32(tbd->word_2); - scq->next->word_3 = cpu_to_le32(tbd->word_3); - scq->next->word_4 = cpu_to_le32(tbd->word_4); - - if (scq->next == scq->last) - scq->next = scq->base; - else - scq->next++; - - write_sram(card, scq->scd, - scq->paddr + - (u32)((unsigned long)scq->next - (unsigned long)scq->base)); - spin_unlock_irqrestore(&scq->lock, flags); - - scq->trans_start = jiffies; - - if (test_and_clear_bit(VCF_IDLE, &vc->flags)) { - writel(TCMDQ_START_LACR | (vc->lacr << 16) | vc->index, - SAR_REG_TCMDQ); - } - - TXPRINTK("%d entries in SCQ used (push).\n", atomic_read(&scq->used)); - - XPRINTK("%s: SCQ (after push %2d) head = 0x%x, next = 0x%p.\n", - card->name, atomic_read(&scq->used), - read_sram(card, scq->scd + 1), scq->next); - - return 0; - -out: - if (time_after(jiffies, scq->trans_start + HZ)) { - printk("%s: Error pushing TBD for %d.%d\n", - card->name, vc->tx_vcc->vpi, vc->tx_vcc->vci); -#ifdef CONFIG_ATM_IDT77252_DEBUG - idt77252_tx_dump(card); -#endif - scq->trans_start = jiffies; - } - - return -ENOBUFS; -} - - -static void -drain_scq(struct idt77252_dev *card, struct vc_map *vc) -{ - struct scq_info *scq = vc->scq; - struct sk_buff *skb; - struct atm_vcc *vcc; - - TXPRINTK("%s: SCQ (before drain %2d) next = 0x%p.\n", - card->name, atomic_read(&scq->used), scq->next); - - skb = skb_dequeue(&scq->transmit); - if (skb) { - TXPRINTK("%s: freeing skb at %p.\n", card->name, skb); - - dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), - skb->len, DMA_TO_DEVICE); - - vcc = ATM_SKB(skb)->vcc; - - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb(skb); - - atomic_inc(&vcc->stats->tx); - } - - atomic_dec(&scq->used); - - spin_lock(&scq->skblock); - while ((skb = skb_dequeue(&scq->pending))) { - if (push_on_scq(card, vc, skb)) { - skb_queue_head(&vc->scq->pending, skb); - break; - } - } - spin_unlock(&scq->skblock); -} - -static int -queue_skb(struct idt77252_dev *card, struct vc_map *vc, - struct sk_buff *skb, int oam) -{ - struct atm_vcc *vcc; - struct scqe *tbd; - unsigned long flags; - int error; - int aal; - u32 word4; - - if (skb->len == 0) { - printk("%s: invalid skb->len (%d)\n", card->name, skb->len); - return -EINVAL; - } - - TXPRINTK("%s: Sending %d bytes of data.\n", - card->name, skb->len); - - tbd = &IDT77252_PRV_TBD(skb); - vcc = ATM_SKB(skb)->vcc; - word4 = (skb->data[0] << 24) | (skb->data[1] << 16) | - (skb->data[2] << 8) | (skb->data[3] << 0); - - IDT77252_PRV_PADDR(skb) = dma_map_single(&card->pcidev->dev, skb->data, - skb->len, DMA_TO_DEVICE); - if (dma_mapping_error(&card->pcidev->dev, IDT77252_PRV_PADDR(skb))) - return -ENOMEM; - - error = -EINVAL; - - if (oam) { - if (skb->len != 52) - goto errout; - - tbd->word_1 = SAR_TBD_OAM | ATM_CELL_PAYLOAD | SAR_TBD_EPDU; - tbd->word_2 = IDT77252_PRV_PADDR(skb) + 4; - tbd->word_3 = 0x00000000; - tbd->word_4 = word4; - - if (test_bit(VCF_RSV, &vc->flags)) - vc = card->vcs[0]; - - goto done; - } - - if (test_bit(VCF_RSV, &vc->flags)) { - printk("%s: Trying to transmit on reserved VC\n", card->name); - goto errout; - } - - aal = vcc->qos.aal; - - switch (aal) { - case ATM_AAL0: - case ATM_AAL34: - if (skb->len > 52) - goto errout; - - if (aal == ATM_AAL0) - tbd->word_1 = SAR_TBD_EPDU | SAR_TBD_AAL0 | - ATM_CELL_PAYLOAD; - else - tbd->word_1 = SAR_TBD_EPDU | SAR_TBD_AAL34 | - ATM_CELL_PAYLOAD; - - tbd->word_2 = IDT77252_PRV_PADDR(skb) + 4; - tbd->word_3 = 0x00000000; - tbd->word_4 = word4; - break; - - case ATM_AAL5: - tbd->word_1 = SAR_TBD_EPDU | SAR_TBD_AAL5 | skb->len; - tbd->word_2 = IDT77252_PRV_PADDR(skb); - tbd->word_3 = skb->len; - tbd->word_4 = (vcc->vpi << SAR_TBD_VPI_SHIFT) | - (vcc->vci << SAR_TBD_VCI_SHIFT); - break; - - case ATM_AAL1: - case ATM_AAL2: - default: - printk("%s: Traffic type not supported.\n", card->name); - error = -EPROTONOSUPPORT; - goto errout; - } - -done: - spin_lock_irqsave(&vc->scq->skblock, flags); - skb_queue_tail(&vc->scq->pending, skb); - - while ((skb = skb_dequeue(&vc->scq->pending))) { - if (push_on_scq(card, vc, skb)) { - skb_queue_head(&vc->scq->pending, skb); - break; - } - } - spin_unlock_irqrestore(&vc->scq->skblock, flags); - - return 0; - -errout: - dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), - skb->len, DMA_TO_DEVICE); - return error; -} - -static unsigned long -get_free_scd(struct idt77252_dev *card, struct vc_map *vc) -{ - int i; - - for (i = 0; i < card->scd_size; i++) { - if (!card->scd2vc[i]) { - card->scd2vc[i] = vc; - vc->scd_index = i; - return card->scd_base + i * SAR_SRAM_SCD_SIZE; - } - } - return 0; -} - -static void -fill_scd(struct idt77252_dev *card, struct scq_info *scq, int class) -{ - write_sram(card, scq->scd, scq->paddr); - write_sram(card, scq->scd + 1, 0x00000000); - write_sram(card, scq->scd + 2, 0xffffffff); - write_sram(card, scq->scd + 3, 0x00000000); -} - -static void -clear_scd(struct idt77252_dev *card, struct scq_info *scq, int class) -{ - return; -} - -/*****************************************************************************/ -/* */ -/* RSQ Handling */ -/* */ -/*****************************************************************************/ - -static int -init_rsq(struct idt77252_dev *card) -{ - struct rsq_entry *rsqe; - - card->rsq.base = dma_alloc_coherent(&card->pcidev->dev, RSQSIZE, - &card->rsq.paddr, GFP_KERNEL); - if (card->rsq.base == NULL) { - printk("%s: can't allocate RSQ.\n", card->name); - return -1; - } - - card->rsq.last = card->rsq.base + RSQ_NUM_ENTRIES - 1; - card->rsq.next = card->rsq.last; - for (rsqe = card->rsq.base; rsqe <= card->rsq.last; rsqe++) - rsqe->word_4 = 0; - - writel((unsigned long) card->rsq.last - (unsigned long) card->rsq.base, - SAR_REG_RSQH); - writel(card->rsq.paddr, SAR_REG_RSQB); - - IPRINTK("%s: RSQ base at 0x%lx (0x%x).\n", card->name, - (unsigned long) card->rsq.base, - readl(SAR_REG_RSQB)); - IPRINTK("%s: RSQ head = 0x%x, base = 0x%x, tail = 0x%x.\n", - card->name, - readl(SAR_REG_RSQH), - readl(SAR_REG_RSQB), - readl(SAR_REG_RSQT)); - - return 0; -} - -static void -deinit_rsq(struct idt77252_dev *card) -{ - dma_free_coherent(&card->pcidev->dev, RSQSIZE, - card->rsq.base, card->rsq.paddr); -} - -static void -dequeue_rx(struct idt77252_dev *card, struct rsq_entry *rsqe) -{ - struct atm_vcc *vcc; - struct sk_buff *skb; - struct rx_pool *rpp; - struct vc_map *vc; - u32 header, vpi, vci; - u32 stat; - int i; - - stat = le32_to_cpu(rsqe->word_4); - - if (stat & SAR_RSQE_IDLE) { - RXPRINTK("%s: message about inactive connection.\n", - card->name); - return; - } - - skb = sb_pool_skb(card, le32_to_cpu(rsqe->word_2)); - if (skb == NULL) { - printk("%s: NULL skb in %s, rsqe: %08x %08x %08x %08x\n", - card->name, __func__, - le32_to_cpu(rsqe->word_1), le32_to_cpu(rsqe->word_2), - le32_to_cpu(rsqe->word_3), le32_to_cpu(rsqe->word_4)); - return; - } - - header = le32_to_cpu(rsqe->word_1); - vpi = (header >> 16) & 0x00ff; - vci = (header >> 0) & 0xffff; - - RXPRINTK("%s: SDU for %d.%d received in buffer 0x%p (data 0x%p).\n", - card->name, vpi, vci, skb, skb->data); - - if ((vpi >= (1 << card->vpibits)) || (vci != (vci & card->vcimask))) { - printk("%s: SDU received for out-of-range vc %u.%u\n", - card->name, vpi, vci); - recycle_rx_skb(card, skb); - return; - } - - vc = card->vcs[VPCI2VC(card, vpi, vci)]; - if (!vc || !test_bit(VCF_RX, &vc->flags)) { - printk("%s: SDU received on non RX vc %u.%u\n", - card->name, vpi, vci); - recycle_rx_skb(card, skb); - return; - } - - vcc = vc->rx_vcc; - - dma_sync_single_for_cpu(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), - skb_end_pointer(skb) - skb->data, - DMA_FROM_DEVICE); - - if ((vcc->qos.aal == ATM_AAL0) || - (vcc->qos.aal == ATM_AAL34)) { - struct sk_buff *sb; - unsigned char *cell; - u32 aal0; - - cell = skb->data; - for (i = (stat & SAR_RSQE_CELLCNT); i; i--) { - if ((sb = dev_alloc_skb(64)) == NULL) { - printk("%s: Can't allocate buffers for aal0.\n", - card->name); - atomic_add(i, &vcc->stats->rx_drop); - break; - } - if (!atm_charge(vcc, sb->truesize)) { - RXPRINTK("%s: atm_charge() dropped aal0 packets.\n", - card->name); - atomic_add(i - 1, &vcc->stats->rx_drop); - dev_kfree_skb(sb); - break; - } - aal0 = (vpi << ATM_HDR_VPI_SHIFT) | - (vci << ATM_HDR_VCI_SHIFT); - aal0 |= (stat & SAR_RSQE_EPDU) ? 0x00000002 : 0; - aal0 |= (stat & SAR_RSQE_CLP) ? 0x00000001 : 0; - - *((u32 *) sb->data) = aal0; - skb_put(sb, sizeof(u32)); - skb_put_data(sb, cell, ATM_CELL_PAYLOAD); - - ATM_SKB(sb)->vcc = vcc; - __net_timestamp(sb); - vcc->push(vcc, sb); - atomic_inc(&vcc->stats->rx); - - cell += ATM_CELL_PAYLOAD; - } - - recycle_rx_skb(card, skb); - return; - } - if (vcc->qos.aal != ATM_AAL5) { - printk("%s: Unexpected AAL type in dequeue_rx(): %d.\n", - card->name, vcc->qos.aal); - recycle_rx_skb(card, skb); - return; - } - skb->len = (stat & SAR_RSQE_CELLCNT) * ATM_CELL_PAYLOAD; - - rpp = &vc->rcv.rx_pool; - - __skb_queue_tail(&rpp->queue, skb); - rpp->len += skb->len; - - if (stat & SAR_RSQE_EPDU) { - unsigned int len, truesize; - unsigned char *l1l2; - - l1l2 = (unsigned char *) ((unsigned long) skb->data + skb->len - 6); - - len = (l1l2[0] << 8) | l1l2[1]; - len = len ? len : 0x10000; - - RXPRINTK("%s: PDU has %d bytes.\n", card->name, len); - - if ((len + 8 > rpp->len) || (len + (47 + 8) < rpp->len)) { - RXPRINTK("%s: AAL5 PDU size mismatch: %d != %d. " - "(CDC: %08x)\n", - card->name, len, rpp->len, readl(SAR_REG_CDC)); - recycle_rx_pool_skb(card, rpp); - atomic_inc(&vcc->stats->rx_err); - return; - } - if (stat & SAR_RSQE_CRC) { - RXPRINTK("%s: AAL5 CRC error.\n", card->name); - recycle_rx_pool_skb(card, rpp); - atomic_inc(&vcc->stats->rx_err); - return; - } - if (skb_queue_len(&rpp->queue) > 1) { - struct sk_buff *sb; - - skb = dev_alloc_skb(rpp->len); - if (!skb) { - RXPRINTK("%s: Can't alloc RX skb.\n", - card->name); - recycle_rx_pool_skb(card, rpp); - atomic_inc(&vcc->stats->rx_err); - return; - } - if (!atm_charge(vcc, skb->truesize)) { - recycle_rx_pool_skb(card, rpp); - dev_kfree_skb(skb); - return; - } - skb_queue_walk(&rpp->queue, sb) - skb_put_data(skb, sb->data, sb->len); - - recycle_rx_pool_skb(card, rpp); - - skb_trim(skb, len); - ATM_SKB(skb)->vcc = vcc; - __net_timestamp(skb); - - vcc->push(vcc, skb); - atomic_inc(&vcc->stats->rx); - - return; - } - - flush_rx_pool(card, rpp); - - if (!atm_charge(vcc, skb->truesize)) { - recycle_rx_skb(card, skb); - return; - } - - dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), - skb_end_pointer(skb) - skb->data, - DMA_FROM_DEVICE); - sb_pool_remove(card, skb); - - skb_trim(skb, len); - ATM_SKB(skb)->vcc = vcc; - __net_timestamp(skb); - - truesize = skb->truesize; - vcc->push(vcc, skb); - atomic_inc(&vcc->stats->rx); - - if (truesize > SAR_FB_SIZE_3) - add_rx_skb(card, 3, SAR_FB_SIZE_3, 1); - else if (truesize > SAR_FB_SIZE_2) - add_rx_skb(card, 2, SAR_FB_SIZE_2, 1); - else if (truesize > SAR_FB_SIZE_1) - add_rx_skb(card, 1, SAR_FB_SIZE_1, 1); - else - add_rx_skb(card, 0, SAR_FB_SIZE_0, 1); - return; - } -} - -static void -idt77252_rx(struct idt77252_dev *card) -{ - struct rsq_entry *rsqe; - - if (card->rsq.next == card->rsq.last) - rsqe = card->rsq.base; - else - rsqe = card->rsq.next + 1; - - if (!(le32_to_cpu(rsqe->word_4) & SAR_RSQE_VALID)) { - RXPRINTK("%s: no entry in RSQ.\n", card->name); - return; - } - - do { - dequeue_rx(card, rsqe); - rsqe->word_4 = 0; - card->rsq.next = rsqe; - if (card->rsq.next == card->rsq.last) - rsqe = card->rsq.base; - else - rsqe = card->rsq.next + 1; - } while (le32_to_cpu(rsqe->word_4) & SAR_RSQE_VALID); - - writel((unsigned long) card->rsq.next - (unsigned long) card->rsq.base, - SAR_REG_RSQH); -} - -static void -idt77252_rx_raw(struct idt77252_dev *card) -{ - struct sk_buff *queue; - u32 head, tail; - struct atm_vcc *vcc; - struct vc_map *vc; - struct sk_buff *sb; - - if (card->raw_cell_head == NULL) { - u32 handle = le32_to_cpu(*(card->raw_cell_hnd + 1)); - card->raw_cell_head = sb_pool_skb(card, handle); - } - - queue = card->raw_cell_head; - if (!queue) - return; - - head = IDT77252_PRV_PADDR(queue) + (queue->data - queue->head - 16); - tail = readl(SAR_REG_RAWCT); - - dma_sync_single_for_cpu(&card->pcidev->dev, IDT77252_PRV_PADDR(queue), - skb_end_offset(queue) - 16, - DMA_FROM_DEVICE); - - while (head != tail) { - unsigned int vpi, vci; - u32 header; - - header = le32_to_cpu(*(u32 *) &queue->data[0]); - - vpi = (header & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT; - vci = (header & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT; - -#ifdef CONFIG_ATM_IDT77252_DEBUG - if (debug & DBG_RAW_CELL) { - int i; - - printk("%s: raw cell %x.%02x.%04x.%x.%x\n", - card->name, (header >> 28) & 0x000f, - (header >> 20) & 0x00ff, - (header >> 4) & 0xffff, - (header >> 1) & 0x0007, - (header >> 0) & 0x0001); - for (i = 16; i < 64; i++) - printk(" %02x", queue->data[i]); - printk("\n"); - } -#endif - - if (vpi >= (1<vpibits) || vci >= (1<vcibits)) { - RPRINTK("%s: SDU received for out-of-range vc %u.%u\n", - card->name, vpi, vci); - goto drop; - } - - vc = card->vcs[VPCI2VC(card, vpi, vci)]; - if (!vc || !test_bit(VCF_RX, &vc->flags)) { - RPRINTK("%s: SDU received on non RX vc %u.%u\n", - card->name, vpi, vci); - goto drop; - } - - vcc = vc->rx_vcc; - - if (vcc->qos.aal != ATM_AAL0) { - RPRINTK("%s: raw cell for non AAL0 vc %u.%u\n", - card->name, vpi, vci); - atomic_inc(&vcc->stats->rx_drop); - goto drop; - } - - if ((sb = dev_alloc_skb(64)) == NULL) { - printk("%s: Can't allocate buffers for AAL0.\n", - card->name); - atomic_inc(&vcc->stats->rx_err); - goto drop; - } - - if (!atm_charge(vcc, sb->truesize)) { - RXPRINTK("%s: atm_charge() dropped AAL0 packets.\n", - card->name); - dev_kfree_skb(sb); - goto drop; - } - - *((u32 *) sb->data) = header; - skb_put(sb, sizeof(u32)); - skb_put_data(sb, &(queue->data[16]), ATM_CELL_PAYLOAD); - - ATM_SKB(sb)->vcc = vcc; - __net_timestamp(sb); - vcc->push(vcc, sb); - atomic_inc(&vcc->stats->rx); - -drop: - skb_pull(queue, 64); - - head = IDT77252_PRV_PADDR(queue) - + (queue->data - queue->head - 16); - - if (queue->len < 128) { - struct sk_buff *next; - u32 handle; - - head = le32_to_cpu(*(u32 *) &queue->data[0]); - handle = le32_to_cpu(*(u32 *) &queue->data[4]); - - next = sb_pool_skb(card, handle); - recycle_rx_skb(card, queue); - - if (next) { - card->raw_cell_head = next; - queue = card->raw_cell_head; - dma_sync_single_for_cpu(&card->pcidev->dev, - IDT77252_PRV_PADDR(queue), - (skb_end_pointer(queue) - - queue->data), - DMA_FROM_DEVICE); - } else { - card->raw_cell_head = NULL; - printk("%s: raw cell queue overrun\n", - card->name); - break; - } - } - } -} - - -/*****************************************************************************/ -/* */ -/* TSQ Handling */ -/* */ -/*****************************************************************************/ - -static int -init_tsq(struct idt77252_dev *card) -{ - struct tsq_entry *tsqe; - - card->tsq.base = dma_alloc_coherent(&card->pcidev->dev, RSQSIZE, - &card->tsq.paddr, GFP_KERNEL); - if (card->tsq.base == NULL) { - printk("%s: can't allocate TSQ.\n", card->name); - return -1; - } - - card->tsq.last = card->tsq.base + TSQ_NUM_ENTRIES - 1; - card->tsq.next = card->tsq.last; - for (tsqe = card->tsq.base; tsqe <= card->tsq.last; tsqe++) - tsqe->word_2 = cpu_to_le32(SAR_TSQE_INVALID); - - writel(card->tsq.paddr, SAR_REG_TSQB); - writel((unsigned long) card->tsq.next - (unsigned long) card->tsq.base, - SAR_REG_TSQH); - - return 0; -} - -static void -deinit_tsq(struct idt77252_dev *card) -{ - dma_free_coherent(&card->pcidev->dev, TSQSIZE, - card->tsq.base, card->tsq.paddr); -} - -static void -idt77252_tx(struct idt77252_dev *card) -{ - struct tsq_entry *tsqe; - unsigned int vpi, vci; - struct vc_map *vc; - u32 conn, stat; - - if (card->tsq.next == card->tsq.last) - tsqe = card->tsq.base; - else - tsqe = card->tsq.next + 1; - - TXPRINTK("idt77252_tx: tsq %p: base %p, next %p, last %p\n", tsqe, - card->tsq.base, card->tsq.next, card->tsq.last); - TXPRINTK("idt77252_tx: tsqb %08x, tsqt %08x, tsqh %08x, \n", - readl(SAR_REG_TSQB), - readl(SAR_REG_TSQT), - readl(SAR_REG_TSQH)); - - stat = le32_to_cpu(tsqe->word_2); - - if (stat & SAR_TSQE_INVALID) - return; - - do { - TXPRINTK("tsqe: 0x%p [0x%08x 0x%08x]\n", tsqe, - le32_to_cpu(tsqe->word_1), - le32_to_cpu(tsqe->word_2)); - - switch (stat & SAR_TSQE_TYPE) { - case SAR_TSQE_TYPE_TIMER: - TXPRINTK("%s: Timer RollOver detected.\n", card->name); - break; - - case SAR_TSQE_TYPE_IDLE: - - conn = le32_to_cpu(tsqe->word_1); - - if (SAR_TSQE_TAG(stat) == 0x10) { -#ifdef NOTDEF - printk("%s: Connection %d halted.\n", - card->name, - le32_to_cpu(tsqe->word_1) & 0x1fff); -#endif - break; - } - - vc = card->vcs[conn & 0x1fff]; - if (!vc) { - printk("%s: could not find VC from conn %d\n", - card->name, conn & 0x1fff); - break; - } - - printk("%s: Connection %d IDLE.\n", - card->name, vc->index); - - set_bit(VCF_IDLE, &vc->flags); - break; - - case SAR_TSQE_TYPE_TSR: - - conn = le32_to_cpu(tsqe->word_1); - - vc = card->vcs[conn & 0x1fff]; - if (!vc) { - printk("%s: no VC at index %d\n", - card->name, - le32_to_cpu(tsqe->word_1) & 0x1fff); - break; - } - - drain_scq(card, vc); - break; - - case SAR_TSQE_TYPE_TBD_COMP: - - conn = le32_to_cpu(tsqe->word_1); - - vpi = (conn >> SAR_TBD_VPI_SHIFT) & 0x00ff; - vci = (conn >> SAR_TBD_VCI_SHIFT) & 0xffff; - - if (vpi >= (1 << card->vpibits) || - vci >= (1 << card->vcibits)) { - printk("%s: TBD complete: " - "out of range VPI.VCI %u.%u\n", - card->name, vpi, vci); - break; - } - - vc = card->vcs[VPCI2VC(card, vpi, vci)]; - if (!vc) { - printk("%s: TBD complete: " - "no VC at VPI.VCI %u.%u\n", - card->name, vpi, vci); - break; - } - - drain_scq(card, vc); - break; - } - - tsqe->word_2 = cpu_to_le32(SAR_TSQE_INVALID); - - card->tsq.next = tsqe; - if (card->tsq.next == card->tsq.last) - tsqe = card->tsq.base; - else - tsqe = card->tsq.next + 1; - - TXPRINTK("tsqe: %p: base %p, next %p, last %p\n", tsqe, - card->tsq.base, card->tsq.next, card->tsq.last); - - stat = le32_to_cpu(tsqe->word_2); - - } while (!(stat & SAR_TSQE_INVALID)); - - writel((unsigned long)card->tsq.next - (unsigned long)card->tsq.base, - SAR_REG_TSQH); - - XPRINTK("idt77252_tx-after writel%d: TSQ head = 0x%x, tail = 0x%x, next = 0x%p.\n", - card->index, readl(SAR_REG_TSQH), - readl(SAR_REG_TSQT), card->tsq.next); -} - - -static void -tst_timer(struct timer_list *t) -{ - struct idt77252_dev *card = timer_container_of(card, t, tst_timer); - unsigned long base, idle, jump; - unsigned long flags; - u32 pc; - int e; - - spin_lock_irqsave(&card->tst_lock, flags); - - base = card->tst[card->tst_index]; - idle = card->tst[card->tst_index ^ 1]; - - if (test_bit(TST_SWITCH_WAIT, &card->tst_state)) { - jump = base + card->tst_size - 2; - - pc = readl(SAR_REG_NOW) >> 2; - if ((pc ^ idle) & ~(card->tst_size - 1)) { - mod_timer(&card->tst_timer, jiffies + 1); - goto out; - } - - clear_bit(TST_SWITCH_WAIT, &card->tst_state); - - card->tst_index ^= 1; - write_sram(card, jump, TSTE_OPC_JMP | (base << 2)); - - base = card->tst[card->tst_index]; - idle = card->tst[card->tst_index ^ 1]; - - for (e = 0; e < card->tst_size - 2; e++) { - if (card->soft_tst[e].tste & TSTE_PUSH_IDLE) { - write_sram(card, idle + e, - card->soft_tst[e].tste & TSTE_MASK); - card->soft_tst[e].tste &= ~(TSTE_PUSH_IDLE); - } - } - } - - if (test_and_clear_bit(TST_SWITCH_PENDING, &card->tst_state)) { - - for (e = 0; e < card->tst_size - 2; e++) { - if (card->soft_tst[e].tste & TSTE_PUSH_ACTIVE) { - write_sram(card, idle + e, - card->soft_tst[e].tste & TSTE_MASK); - card->soft_tst[e].tste &= ~(TSTE_PUSH_ACTIVE); - card->soft_tst[e].tste |= TSTE_PUSH_IDLE; - } - } - - jump = base + card->tst_size - 2; - - write_sram(card, jump, TSTE_OPC_NULL); - set_bit(TST_SWITCH_WAIT, &card->tst_state); - - mod_timer(&card->tst_timer, jiffies + 1); - } - -out: - spin_unlock_irqrestore(&card->tst_lock, flags); -} - -static int -__fill_tst(struct idt77252_dev *card, struct vc_map *vc, - int n, unsigned int opc) -{ - unsigned long cl, avail; - unsigned long idle; - int e, r; - u32 data; - - avail = card->tst_size - 2; - for (e = 0; e < avail; e++) { - if (card->soft_tst[e].vc == NULL) - break; - } - if (e >= avail) { - printk("%s: No free TST entries found\n", card->name); - return -1; - } - - NPRINTK("%s: conn %d: first TST entry at %d.\n", - card->name, vc ? vc->index : -1, e); - - r = n; - cl = avail; - data = opc & TSTE_OPC_MASK; - if (vc && (opc != TSTE_OPC_NULL)) - data = opc | vc->index; - - idle = card->tst[card->tst_index ^ 1]; - - /* - * Fill Soft TST. - */ - while (r > 0) { - if ((cl >= avail) && (card->soft_tst[e].vc == NULL)) { - if (vc) - card->soft_tst[e].vc = vc; - else - card->soft_tst[e].vc = (void *)-1; - - card->soft_tst[e].tste = data; - if (timer_pending(&card->tst_timer)) - card->soft_tst[e].tste |= TSTE_PUSH_ACTIVE; - else { - write_sram(card, idle + e, data); - card->soft_tst[e].tste |= TSTE_PUSH_IDLE; - } - - cl -= card->tst_size; - r--; - } - - if (++e == avail) - e = 0; - cl += n; - } - - return 0; -} - -static int -fill_tst(struct idt77252_dev *card, struct vc_map *vc, int n, unsigned int opc) -{ - unsigned long flags; - int res; - - spin_lock_irqsave(&card->tst_lock, flags); - - res = __fill_tst(card, vc, n, opc); - - set_bit(TST_SWITCH_PENDING, &card->tst_state); - if (!timer_pending(&card->tst_timer)) - mod_timer(&card->tst_timer, jiffies + 1); - - spin_unlock_irqrestore(&card->tst_lock, flags); - return res; -} - -static int -__clear_tst(struct idt77252_dev *card, struct vc_map *vc) -{ - unsigned long idle; - int e; - - idle = card->tst[card->tst_index ^ 1]; - - for (e = 0; e < card->tst_size - 2; e++) { - if (card->soft_tst[e].vc == vc) { - card->soft_tst[e].vc = NULL; - - card->soft_tst[e].tste = TSTE_OPC_VAR; - if (timer_pending(&card->tst_timer)) - card->soft_tst[e].tste |= TSTE_PUSH_ACTIVE; - else { - write_sram(card, idle + e, TSTE_OPC_VAR); - card->soft_tst[e].tste |= TSTE_PUSH_IDLE; - } - } - } - - return 0; -} - -static int -clear_tst(struct idt77252_dev *card, struct vc_map *vc) -{ - unsigned long flags; - int res; - - spin_lock_irqsave(&card->tst_lock, flags); - - res = __clear_tst(card, vc); - - set_bit(TST_SWITCH_PENDING, &card->tst_state); - if (!timer_pending(&card->tst_timer)) - mod_timer(&card->tst_timer, jiffies + 1); - - spin_unlock_irqrestore(&card->tst_lock, flags); - return res; -} - -static int -change_tst(struct idt77252_dev *card, struct vc_map *vc, - int n, unsigned int opc) -{ - unsigned long flags; - int res; - - spin_lock_irqsave(&card->tst_lock, flags); - - __clear_tst(card, vc); - res = __fill_tst(card, vc, n, opc); - - set_bit(TST_SWITCH_PENDING, &card->tst_state); - if (!timer_pending(&card->tst_timer)) - mod_timer(&card->tst_timer, jiffies + 1); - - spin_unlock_irqrestore(&card->tst_lock, flags); - return res; -} - - -static int -set_tct(struct idt77252_dev *card, struct vc_map *vc) -{ - unsigned long tct; - - tct = (unsigned long) (card->tct_base + vc->index * SAR_SRAM_TCT_SIZE); - - switch (vc->class) { - case SCHED_CBR: - OPRINTK("%s: writing TCT at 0x%lx, SCD 0x%lx.\n", - card->name, tct, vc->scq->scd); - - write_sram(card, tct + 0, TCT_CBR | vc->scq->scd); - write_sram(card, tct + 1, 0); - write_sram(card, tct + 2, 0); - write_sram(card, tct + 3, 0); - write_sram(card, tct + 4, 0); - write_sram(card, tct + 5, 0); - write_sram(card, tct + 6, 0); - write_sram(card, tct + 7, 0); - break; - - case SCHED_UBR: - OPRINTK("%s: writing TCT at 0x%lx, SCD 0x%lx.\n", - card->name, tct, vc->scq->scd); - - write_sram(card, tct + 0, TCT_UBR | vc->scq->scd); - write_sram(card, tct + 1, 0); - write_sram(card, tct + 2, TCT_TSIF); - write_sram(card, tct + 3, TCT_HALT | TCT_IDLE); - write_sram(card, tct + 4, 0); - write_sram(card, tct + 5, vc->init_er); - write_sram(card, tct + 6, 0); - write_sram(card, tct + 7, TCT_FLAG_UBR); - break; - - case SCHED_VBR: - case SCHED_ABR: - default: - return -ENOSYS; - } - - return 0; -} - -/*****************************************************************************/ -/* */ -/* FBQ Handling */ -/* */ -/*****************************************************************************/ - -static __inline__ int -idt77252_fbq_full(struct idt77252_dev *card, int queue) -{ - return (readl(SAR_REG_STAT) >> (16 + (queue << 2))) == 0x0f; -} - -static int -push_rx_skb(struct idt77252_dev *card, struct sk_buff *skb, int queue) -{ - unsigned long flags; - u32 handle; - u32 addr; - - skb->data = skb->head; - skb_reset_tail_pointer(skb); - skb->len = 0; - - skb_reserve(skb, 16); - - switch (queue) { - case 0: - skb_put(skb, SAR_FB_SIZE_0); - break; - case 1: - skb_put(skb, SAR_FB_SIZE_1); - break; - case 2: - skb_put(skb, SAR_FB_SIZE_2); - break; - case 3: - skb_put(skb, SAR_FB_SIZE_3); - break; - default: - return -1; - } - - if (idt77252_fbq_full(card, queue)) - return -1; - - memset(&skb->data[(skb->len & ~(0x3f)) - 64], 0, 2 * sizeof(u32)); - - handle = IDT77252_PRV_POOL(skb); - addr = IDT77252_PRV_PADDR(skb); - - spin_lock_irqsave(&card->cmd_lock, flags); - writel(handle, card->fbq[queue]); - writel(addr, card->fbq[queue]); - spin_unlock_irqrestore(&card->cmd_lock, flags); - - return 0; -} - -static void -add_rx_skb(struct idt77252_dev *card, int queue, - unsigned int size, unsigned int count) -{ - struct sk_buff *skb; - dma_addr_t paddr; - - while (count--) { - skb = dev_alloc_skb(size); - if (!skb) - return; - - if (sb_pool_add(card, skb, queue)) { - printk("%s: SB POOL full\n", __func__); - goto outfree; - } - - paddr = dma_map_single(&card->pcidev->dev, skb->data, - skb_end_pointer(skb) - skb->data, - DMA_FROM_DEVICE); - if (dma_mapping_error(&card->pcidev->dev, paddr)) - goto outpoolrm; - IDT77252_PRV_PADDR(skb) = paddr; - - if (push_rx_skb(card, skb, queue)) { - printk("%s: FB QUEUE full\n", __func__); - goto outunmap; - } - } - - return; - -outunmap: - dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), - skb_end_pointer(skb) - skb->data, DMA_FROM_DEVICE); - -outpoolrm: - sb_pool_remove(card, skb); - -outfree: - dev_kfree_skb(skb); -} - - -static void -recycle_rx_skb(struct idt77252_dev *card, struct sk_buff *skb) -{ - u32 handle = IDT77252_PRV_POOL(skb); - int err; - - dma_sync_single_for_device(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), - skb_end_pointer(skb) - skb->data, - DMA_FROM_DEVICE); - - err = push_rx_skb(card, skb, POOL_QUEUE(handle)); - if (err) { - dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), - skb_end_pointer(skb) - skb->data, - DMA_FROM_DEVICE); - sb_pool_remove(card, skb); - dev_kfree_skb(skb); - } -} - -static void -flush_rx_pool(struct idt77252_dev *card, struct rx_pool *rpp) -{ - skb_queue_head_init(&rpp->queue); - rpp->len = 0; -} - -static void -recycle_rx_pool_skb(struct idt77252_dev *card, struct rx_pool *rpp) -{ - struct sk_buff *skb, *tmp; - - skb_queue_walk_safe(&rpp->queue, skb, tmp) - recycle_rx_skb(card, skb); - - flush_rx_pool(card, rpp); -} - -/*****************************************************************************/ -/* */ -/* ATM Interface */ -/* */ -/*****************************************************************************/ - -static void -idt77252_phy_put(struct atm_dev *dev, unsigned char value, unsigned long addr) -{ - write_utility(dev->dev_data, 0x100 + (addr & 0x1ff), value); -} - -static unsigned char -idt77252_phy_get(struct atm_dev *dev, unsigned long addr) -{ - return read_utility(dev->dev_data, 0x100 + (addr & 0x1ff)); -} - -static inline int -idt77252_send_skb(struct atm_vcc *vcc, struct sk_buff *skb, int oam) -{ - struct atm_dev *dev = vcc->dev; - struct idt77252_dev *card = dev->dev_data; - struct vc_map *vc = vcc->dev_data; - int err; - - if (vc == NULL) { - printk("%s: NULL connection in send().\n", card->name); - atomic_inc(&vcc->stats->tx_err); - dev_kfree_skb(skb); - return -EINVAL; - } - if (!test_bit(VCF_TX, &vc->flags)) { - printk("%s: Trying to transmit on a non-tx VC.\n", card->name); - atomic_inc(&vcc->stats->tx_err); - dev_kfree_skb(skb); - return -EINVAL; - } - - switch (vcc->qos.aal) { - case ATM_AAL0: - case ATM_AAL1: - case ATM_AAL5: - break; - default: - printk("%s: Unsupported AAL: %d\n", card->name, vcc->qos.aal); - atomic_inc(&vcc->stats->tx_err); - dev_kfree_skb(skb); - return -EINVAL; - } - - if (skb_shinfo(skb)->nr_frags != 0) { - printk("%s: No scatter-gather yet.\n", card->name); - atomic_inc(&vcc->stats->tx_err); - dev_kfree_skb(skb); - return -EINVAL; - } - ATM_SKB(skb)->vcc = vcc; - - err = queue_skb(card, vc, skb, oam); - if (err) { - atomic_inc(&vcc->stats->tx_err); - dev_kfree_skb(skb); - return err; - } - - return 0; -} - -static int idt77252_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - return idt77252_send_skb(vcc, skb, 0); -} - -static int -idt77252_send_oam(struct atm_vcc *vcc, void *cell, int flags) -{ - struct atm_dev *dev = vcc->dev; - struct idt77252_dev *card = dev->dev_data; - struct sk_buff *skb; - - skb = dev_alloc_skb(64); - if (!skb) { - printk("%s: Out of memory in send_oam().\n", card->name); - atomic_inc(&vcc->stats->tx_err); - return -ENOMEM; - } - refcount_add(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc); - - skb_put_data(skb, cell, 52); - - return idt77252_send_skb(vcc, skb, 1); -} - -static __inline__ unsigned int -idt77252_fls(unsigned int x) -{ - int r = 1; - - if (x == 0) - return 0; - if (x & 0xffff0000) { - x >>= 16; - r += 16; - } - if (x & 0xff00) { - x >>= 8; - r += 8; - } - if (x & 0xf0) { - x >>= 4; - r += 4; - } - if (x & 0xc) { - x >>= 2; - r += 2; - } - if (x & 0x2) - r += 1; - return r; -} - -static u16 -idt77252_int_to_atmfp(unsigned int rate) -{ - u16 m, e; - - if (rate == 0) - return 0; - e = idt77252_fls(rate) - 1; - if (e < 9) - m = (rate - (1 << e)) << (9 - e); - else if (e == 9) - m = (rate - (1 << e)); - else /* e > 9 */ - m = (rate - (1 << e)) >> (e - 9); - return 0x4000 | (e << 9) | m; -} - -static u8 -idt77252_rate_logindex(struct idt77252_dev *card, int pcr) -{ - u16 afp; - - afp = idt77252_int_to_atmfp(pcr < 0 ? -pcr : pcr); - if (pcr < 0) - return rate_to_log[(afp >> 5) & 0x1ff]; - return rate_to_log[((afp >> 5) + 1) & 0x1ff]; -} - -static void -idt77252_est_timer(struct timer_list *t) -{ - struct rate_estimator *est = timer_container_of(est, t, timer); - struct vc_map *vc = est->vc; - struct idt77252_dev *card = vc->card; - unsigned long flags; - u32 rate, cps; - u64 ncells; - u8 lacr; - - spin_lock_irqsave(&vc->lock, flags); - if (!vc->estimator) - goto out; - ncells = est->cells; - - rate = ((u32)(ncells - est->last_cells)) << (7 - est->interval); - est->last_cells = ncells; - est->avcps += ((long)rate - (long)est->avcps) >> est->ewma_log; - est->cps = (est->avcps + 0x1f) >> 5; - - cps = est->cps; - if (cps < (est->maxcps >> 4)) - cps = est->maxcps >> 4; - - lacr = idt77252_rate_logindex(card, cps); - if (lacr > vc->max_er) - lacr = vc->max_er; - - if (lacr != vc->lacr) { - vc->lacr = lacr; - writel(TCMDQ_LACR|(vc->lacr << 16)|vc->index, SAR_REG_TCMDQ); - } - - est->timer.expires = jiffies + ((HZ / 4) << est->interval); - add_timer(&est->timer); - -out: - spin_unlock_irqrestore(&vc->lock, flags); -} - -static struct rate_estimator * -idt77252_init_est(struct vc_map *vc, int pcr) -{ - struct rate_estimator *est; - - est = kzalloc_obj(struct rate_estimator); - if (!est) - return NULL; - est->maxcps = pcr < 0 ? -pcr : pcr; - est->cps = est->maxcps; - est->avcps = est->cps << 5; - est->vc = vc; - - est->interval = 2; /* XXX: make this configurable */ - est->ewma_log = 2; /* XXX: make this configurable */ - timer_setup(&est->timer, idt77252_est_timer, 0); - mod_timer(&est->timer, jiffies + ((HZ / 4) << est->interval)); - - return est; -} - -static int -idt77252_init_cbr(struct idt77252_dev *card, struct vc_map *vc, - struct atm_vcc *vcc, struct atm_qos *qos) -{ - int tst_free, tst_used, tst_entries; - unsigned long tmpl, modl; - int tcr, tcra; - - if ((qos->txtp.max_pcr == 0) && - (qos->txtp.pcr == 0) && (qos->txtp.min_pcr == 0)) { - printk("%s: trying to open a CBR VC with cell rate = 0\n", - card->name); - return -EINVAL; - } - - tst_used = 0; - tst_free = card->tst_free; - if (test_bit(VCF_TX, &vc->flags)) - tst_used = vc->ntste; - tst_free += tst_used; - - tcr = atm_pcr_goal(&qos->txtp); - tcra = tcr >= 0 ? tcr : -tcr; - - TXPRINTK("%s: CBR target cell rate = %d\n", card->name, tcra); - - tmpl = (unsigned long) tcra * ((unsigned long) card->tst_size - 2); - modl = tmpl % (unsigned long)card->utopia_pcr; - - tst_entries = (int) (tmpl / card->utopia_pcr); - if (tcr > 0) { - if (modl > 0) - tst_entries++; - } else if (tcr == 0) { - tst_entries = tst_free - SAR_TST_RESERVED; - if (tst_entries <= 0) { - printk("%s: no CBR bandwidth free.\n", card->name); - return -ENOSR; - } - } - - if (tst_entries == 0) { - printk("%s: selected CBR bandwidth < granularity.\n", - card->name); - return -EINVAL; - } - - if (tst_entries > (tst_free - SAR_TST_RESERVED)) { - printk("%s: not enough CBR bandwidth free.\n", card->name); - return -ENOSR; - } - - vc->ntste = tst_entries; - - card->tst_free = tst_free - tst_entries; - if (test_bit(VCF_TX, &vc->flags)) { - if (tst_used == tst_entries) - return 0; - - OPRINTK("%s: modify %d -> %d entries in TST.\n", - card->name, tst_used, tst_entries); - change_tst(card, vc, tst_entries, TSTE_OPC_CBR); - return 0; - } - - OPRINTK("%s: setting %d entries in TST.\n", card->name, tst_entries); - fill_tst(card, vc, tst_entries, TSTE_OPC_CBR); - return 0; -} - -static int -idt77252_init_ubr(struct idt77252_dev *card, struct vc_map *vc, - struct atm_vcc *vcc, struct atm_qos *qos) -{ - struct rate_estimator *est = NULL; - unsigned long flags; - int tcr; - - spin_lock_irqsave(&vc->lock, flags); - if (vc->estimator) { - est = vc->estimator; - vc->estimator = NULL; - } - spin_unlock_irqrestore(&vc->lock, flags); - if (est) { - timer_shutdown_sync(&est->timer); - kfree(est); - } - - tcr = atm_pcr_goal(&qos->txtp); - if (tcr == 0) - tcr = card->link_pcr; - - vc->estimator = idt77252_init_est(vc, tcr); - - vc->class = SCHED_UBR; - vc->init_er = idt77252_rate_logindex(card, tcr); - vc->lacr = vc->init_er; - if (tcr < 0) - vc->max_er = vc->init_er; - else - vc->max_er = 0xff; - - return 0; -} - -static int -idt77252_init_tx(struct idt77252_dev *card, struct vc_map *vc, - struct atm_vcc *vcc, struct atm_qos *qos) -{ - int error; - - if (test_bit(VCF_TX, &vc->flags)) - return -EBUSY; - - switch (qos->txtp.traffic_class) { - case ATM_CBR: - vc->class = SCHED_CBR; - break; - - case ATM_UBR: - vc->class = SCHED_UBR; - break; - - case ATM_VBR: - case ATM_ABR: - default: - return -EPROTONOSUPPORT; - } - - vc->scq = alloc_scq(card, vc->class); - if (!vc->scq) { - printk("%s: can't get SCQ.\n", card->name); - return -ENOMEM; - } - - vc->scq->scd = get_free_scd(card, vc); - if (vc->scq->scd == 0) { - printk("%s: no SCD available.\n", card->name); - free_scq(card, vc->scq); - return -ENOMEM; - } - - fill_scd(card, vc->scq, vc->class); - - if (set_tct(card, vc)) { - printk("%s: class %d not supported.\n", - card->name, qos->txtp.traffic_class); - - card->scd2vc[vc->scd_index] = NULL; - free_scq(card, vc->scq); - return -EPROTONOSUPPORT; - } - - switch (vc->class) { - case SCHED_CBR: - error = idt77252_init_cbr(card, vc, vcc, qos); - if (error) { - card->scd2vc[vc->scd_index] = NULL; - free_scq(card, vc->scq); - return error; - } - - clear_bit(VCF_IDLE, &vc->flags); - writel(TCMDQ_START | vc->index, SAR_REG_TCMDQ); - break; - - case SCHED_UBR: - error = idt77252_init_ubr(card, vc, vcc, qos); - if (error) { - card->scd2vc[vc->scd_index] = NULL; - free_scq(card, vc->scq); - return error; - } - - set_bit(VCF_IDLE, &vc->flags); - break; - } - - vc->tx_vcc = vcc; - set_bit(VCF_TX, &vc->flags); - return 0; -} - -static int -idt77252_init_rx(struct idt77252_dev *card, struct vc_map *vc, - struct atm_vcc *vcc, struct atm_qos *qos) -{ - unsigned long flags; - unsigned long addr; - u32 rcte = 0; - - if (test_bit(VCF_RX, &vc->flags)) - return -EBUSY; - - vc->rx_vcc = vcc; - set_bit(VCF_RX, &vc->flags); - - if ((vcc->vci == 3) || (vcc->vci == 4)) - return 0; - - flush_rx_pool(card, &vc->rcv.rx_pool); - - rcte |= SAR_RCTE_CONNECTOPEN; - rcte |= SAR_RCTE_RAWCELLINTEN; - - switch (qos->aal) { - case ATM_AAL0: - rcte |= SAR_RCTE_RCQ; - break; - case ATM_AAL1: - rcte |= SAR_RCTE_OAM; /* Let SAR drop Video */ - break; - case ATM_AAL34: - rcte |= SAR_RCTE_AAL34; - break; - case ATM_AAL5: - rcte |= SAR_RCTE_AAL5; - break; - default: - rcte |= SAR_RCTE_RCQ; - break; - } - - if (qos->aal != ATM_AAL5) - rcte |= SAR_RCTE_FBP_1; - else if (qos->rxtp.max_sdu > SAR_FB_SIZE_2) - rcte |= SAR_RCTE_FBP_3; - else if (qos->rxtp.max_sdu > SAR_FB_SIZE_1) - rcte |= SAR_RCTE_FBP_2; - else if (qos->rxtp.max_sdu > SAR_FB_SIZE_0) - rcte |= SAR_RCTE_FBP_1; - else - rcte |= SAR_RCTE_FBP_01; - - addr = card->rct_base + (vc->index << 2); - - OPRINTK("%s: writing RCT at 0x%lx\n", card->name, addr); - write_sram(card, addr, rcte); - - spin_lock_irqsave(&card->cmd_lock, flags); - writel(SAR_CMD_OPEN_CONNECTION | (addr << 2), SAR_REG_CMD); - waitfor_idle(card); - spin_unlock_irqrestore(&card->cmd_lock, flags); - - return 0; -} - -static int -idt77252_open(struct atm_vcc *vcc) -{ - struct atm_dev *dev = vcc->dev; - struct idt77252_dev *card = dev->dev_data; - struct vc_map *vc; - unsigned int index; - unsigned int inuse; - int error; - int vci = vcc->vci; - short vpi = vcc->vpi; - - if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) - return 0; - - if (vpi >= (1 << card->vpibits)) { - printk("%s: unsupported VPI: %d\n", card->name, vpi); - return -EINVAL; - } - - if (vci >= (1 << card->vcibits)) { - printk("%s: unsupported VCI: %d\n", card->name, vci); - return -EINVAL; - } - - set_bit(ATM_VF_ADDR, &vcc->flags); - - mutex_lock(&card->mutex); - - OPRINTK("%s: opening vpi.vci: %d.%d\n", card->name, vpi, vci); - - switch (vcc->qos.aal) { - case ATM_AAL0: - case ATM_AAL1: - case ATM_AAL5: - break; - default: - printk("%s: Unsupported AAL: %d\n", card->name, vcc->qos.aal); - mutex_unlock(&card->mutex); - return -EPROTONOSUPPORT; - } - - index = VPCI2VC(card, vpi, vci); - if (!card->vcs[index]) { - card->vcs[index] = kzalloc_obj(struct vc_map); - if (!card->vcs[index]) { - printk("%s: can't alloc vc in open()\n", card->name); - mutex_unlock(&card->mutex); - return -ENOMEM; - } - card->vcs[index]->card = card; - card->vcs[index]->index = index; - - spin_lock_init(&card->vcs[index]->lock); - } - vc = card->vcs[index]; - - vcc->dev_data = vc; - - IPRINTK("%s: idt77252_open: vc = %d (%d.%d) %s/%s (max RX SDU: %u)\n", - card->name, vc->index, vcc->vpi, vcc->vci, - vcc->qos.rxtp.traffic_class != ATM_NONE ? "rx" : "--", - vcc->qos.txtp.traffic_class != ATM_NONE ? "tx" : "--", - vcc->qos.rxtp.max_sdu); - - inuse = 0; - if (vcc->qos.txtp.traffic_class != ATM_NONE && - test_bit(VCF_TX, &vc->flags)) - inuse = 1; - if (vcc->qos.rxtp.traffic_class != ATM_NONE && - test_bit(VCF_RX, &vc->flags)) - inuse += 2; - - if (inuse) { - printk("%s: %s vci already in use.\n", card->name, - inuse == 1 ? "tx" : inuse == 2 ? "rx" : "tx and rx"); - mutex_unlock(&card->mutex); - return -EADDRINUSE; - } - - if (vcc->qos.txtp.traffic_class != ATM_NONE) { - error = idt77252_init_tx(card, vc, vcc, &vcc->qos); - if (error) { - mutex_unlock(&card->mutex); - return error; - } - } - - if (vcc->qos.rxtp.traffic_class != ATM_NONE) { - error = idt77252_init_rx(card, vc, vcc, &vcc->qos); - if (error) { - mutex_unlock(&card->mutex); - return error; - } - } - - set_bit(ATM_VF_READY, &vcc->flags); - - mutex_unlock(&card->mutex); - return 0; -} - -static void -idt77252_close(struct atm_vcc *vcc) -{ - struct atm_dev *dev = vcc->dev; - struct idt77252_dev *card = dev->dev_data; - struct vc_map *vc = vcc->dev_data; - unsigned long flags; - unsigned long addr; - unsigned long timeout; - - mutex_lock(&card->mutex); - - IPRINTK("%s: idt77252_close: vc = %d (%d.%d)\n", - card->name, vc->index, vcc->vpi, vcc->vci); - - clear_bit(ATM_VF_READY, &vcc->flags); - - if (vcc->qos.rxtp.traffic_class != ATM_NONE) { - - spin_lock_irqsave(&vc->lock, flags); - clear_bit(VCF_RX, &vc->flags); - vc->rx_vcc = NULL; - spin_unlock_irqrestore(&vc->lock, flags); - - if ((vcc->vci == 3) || (vcc->vci == 4)) - goto done; - - addr = card->rct_base + vc->index * SAR_SRAM_RCT_SIZE; - - spin_lock_irqsave(&card->cmd_lock, flags); - writel(SAR_CMD_CLOSE_CONNECTION | (addr << 2), SAR_REG_CMD); - waitfor_idle(card); - spin_unlock_irqrestore(&card->cmd_lock, flags); - - if (skb_queue_len(&vc->rcv.rx_pool.queue) != 0) { - DPRINTK("%s: closing a VC with pending rx buffers.\n", - card->name); - - recycle_rx_pool_skb(card, &vc->rcv.rx_pool); - } - } - -done: - if (vcc->qos.txtp.traffic_class != ATM_NONE) { - - spin_lock_irqsave(&vc->lock, flags); - clear_bit(VCF_TX, &vc->flags); - clear_bit(VCF_IDLE, &vc->flags); - clear_bit(VCF_RSV, &vc->flags); - vc->tx_vcc = NULL; - - if (vc->estimator) { - timer_shutdown(&vc->estimator->timer); - kfree(vc->estimator); - vc->estimator = NULL; - } - spin_unlock_irqrestore(&vc->lock, flags); - - timeout = 5 * 1000; - while (atomic_read(&vc->scq->used) > 0) { - timeout = msleep_interruptible(timeout); - if (!timeout) { - pr_warn("%s: SCQ drain timeout: %u used\n", - card->name, atomic_read(&vc->scq->used)); - break; - } - } - - writel(TCMDQ_HALT | vc->index, SAR_REG_TCMDQ); - clear_scd(card, vc->scq, vc->class); - - if (vc->class == SCHED_CBR) { - clear_tst(card, vc); - card->tst_free += vc->ntste; - vc->ntste = 0; - } - - card->scd2vc[vc->scd_index] = NULL; - free_scq(card, vc->scq); - } - - mutex_unlock(&card->mutex); -} - -static int -idt77252_change_qos(struct atm_vcc *vcc, struct atm_qos *qos, int flags) -{ - struct atm_dev *dev = vcc->dev; - struct idt77252_dev *card = dev->dev_data; - struct vc_map *vc = vcc->dev_data; - int error = 0; - - mutex_lock(&card->mutex); - - if (qos->txtp.traffic_class != ATM_NONE) { - if (!test_bit(VCF_TX, &vc->flags)) { - error = idt77252_init_tx(card, vc, vcc, qos); - if (error) - goto out; - } else { - switch (qos->txtp.traffic_class) { - case ATM_CBR: - error = idt77252_init_cbr(card, vc, vcc, qos); - if (error) - goto out; - break; - - case ATM_UBR: - error = idt77252_init_ubr(card, vc, vcc, qos); - if (error) - goto out; - - if (!test_bit(VCF_IDLE, &vc->flags)) { - writel(TCMDQ_LACR | (vc->lacr << 16) | - vc->index, SAR_REG_TCMDQ); - } - break; - - case ATM_VBR: - case ATM_ABR: - error = -EOPNOTSUPP; - goto out; - } - } - } - - if ((qos->rxtp.traffic_class != ATM_NONE) && - !test_bit(VCF_RX, &vc->flags)) { - error = idt77252_init_rx(card, vc, vcc, qos); - if (error) - goto out; - } - - memcpy(&vcc->qos, qos, sizeof(struct atm_qos)); - - set_bit(ATM_VF_HASQOS, &vcc->flags); - -out: - mutex_unlock(&card->mutex); - return error; -} - -static int -idt77252_proc_read(struct atm_dev *dev, loff_t * pos, char *page) -{ - struct idt77252_dev *card = dev->dev_data; - int i, left; - - left = (int) *pos; - if (!left--) - return sprintf(page, "IDT77252 Interrupts:\n"); - if (!left--) - return sprintf(page, "TSIF: %lu\n", card->irqstat[15]); - if (!left--) - return sprintf(page, "TXICP: %lu\n", card->irqstat[14]); - if (!left--) - return sprintf(page, "TSQF: %lu\n", card->irqstat[12]); - if (!left--) - return sprintf(page, "TMROF: %lu\n", card->irqstat[11]); - if (!left--) - return sprintf(page, "PHYI: %lu\n", card->irqstat[10]); - if (!left--) - return sprintf(page, "FBQ3A: %lu\n", card->irqstat[8]); - if (!left--) - return sprintf(page, "FBQ2A: %lu\n", card->irqstat[7]); - if (!left--) - return sprintf(page, "RSQF: %lu\n", card->irqstat[6]); - if (!left--) - return sprintf(page, "EPDU: %lu\n", card->irqstat[5]); - if (!left--) - return sprintf(page, "RAWCF: %lu\n", card->irqstat[4]); - if (!left--) - return sprintf(page, "FBQ1A: %lu\n", card->irqstat[3]); - if (!left--) - return sprintf(page, "FBQ0A: %lu\n", card->irqstat[2]); - if (!left--) - return sprintf(page, "RSQAF: %lu\n", card->irqstat[1]); - if (!left--) - return sprintf(page, "IDT77252 Transmit Connection Table:\n"); - - for (i = 0; i < card->tct_size; i++) { - unsigned long tct; - struct atm_vcc *vcc; - struct vc_map *vc; - char *p; - - vc = card->vcs[i]; - if (!vc) - continue; - - vcc = NULL; - if (vc->tx_vcc) - vcc = vc->tx_vcc; - if (!vcc) - continue; - if (left--) - continue; - - p = page; - p += sprintf(p, " %4u: %u.%u: ", i, vcc->vpi, vcc->vci); - tct = (unsigned long) (card->tct_base + i * SAR_SRAM_TCT_SIZE); - - for (i = 0; i < 8; i++) - p += sprintf(p, " %08x", read_sram(card, tct + i)); - p += sprintf(p, "\n"); - return p - page; - } - return 0; -} - -/*****************************************************************************/ -/* */ -/* Interrupt handler */ -/* */ -/*****************************************************************************/ - -static void -idt77252_collect_stat(struct idt77252_dev *card) -{ - (void) readl(SAR_REG_CDC); - (void) readl(SAR_REG_VPEC); - (void) readl(SAR_REG_ICC); - -} - -static irqreturn_t -idt77252_interrupt(int irq, void *dev_id) -{ - struct idt77252_dev *card = dev_id; - u32 stat; - - stat = readl(SAR_REG_STAT) & 0xffff; - if (!stat) /* no interrupt for us */ - return IRQ_NONE; - - if (test_and_set_bit(IDT77252_BIT_INTERRUPT, &card->flags)) { - printk("%s: Re-entering irq_handler()\n", card->name); - goto out; - } - - writel(stat, SAR_REG_STAT); /* reset interrupt */ - - if (stat & SAR_STAT_TSIF) { /* entry written to TSQ */ - INTPRINTK("%s: TSIF\n", card->name); - card->irqstat[15]++; - idt77252_tx(card); - } - if (stat & SAR_STAT_TXICP) { /* Incomplete CS-PDU has */ - INTPRINTK("%s: TXICP\n", card->name); - card->irqstat[14]++; -#ifdef CONFIG_ATM_IDT77252_DEBUG - idt77252_tx_dump(card); -#endif - } - if (stat & SAR_STAT_TSQF) { /* TSQ 7/8 full */ - INTPRINTK("%s: TSQF\n", card->name); - card->irqstat[12]++; - idt77252_tx(card); - } - if (stat & SAR_STAT_TMROF) { /* Timer overflow */ - INTPRINTK("%s: TMROF\n", card->name); - card->irqstat[11]++; - idt77252_collect_stat(card); - } - - if (stat & SAR_STAT_EPDU) { /* Got complete CS-PDU */ - INTPRINTK("%s: EPDU\n", card->name); - card->irqstat[5]++; - idt77252_rx(card); - } - if (stat & SAR_STAT_RSQAF) { /* RSQ is 7/8 full */ - INTPRINTK("%s: RSQAF\n", card->name); - card->irqstat[1]++; - idt77252_rx(card); - } - if (stat & SAR_STAT_RSQF) { /* RSQ is full */ - INTPRINTK("%s: RSQF\n", card->name); - card->irqstat[6]++; - idt77252_rx(card); - } - if (stat & SAR_STAT_RAWCF) { /* Raw cell received */ - INTPRINTK("%s: RAWCF\n", card->name); - card->irqstat[4]++; - idt77252_rx_raw(card); - } - - if (stat & SAR_STAT_PHYI) { /* PHY device interrupt */ - INTPRINTK("%s: PHYI", card->name); - card->irqstat[10]++; - if (card->atmdev->phy && card->atmdev->phy->interrupt) - card->atmdev->phy->interrupt(card->atmdev); - } - - if (stat & (SAR_STAT_FBQ0A | SAR_STAT_FBQ1A | - SAR_STAT_FBQ2A | SAR_STAT_FBQ3A)) { - - writel(readl(SAR_REG_CFG) & ~(SAR_CFG_FBIE), SAR_REG_CFG); - - INTPRINTK("%s: FBQA: %04x\n", card->name, stat); - - if (stat & SAR_STAT_FBQ0A) - card->irqstat[2]++; - if (stat & SAR_STAT_FBQ1A) - card->irqstat[3]++; - if (stat & SAR_STAT_FBQ2A) - card->irqstat[7]++; - if (stat & SAR_STAT_FBQ3A) - card->irqstat[8]++; - - schedule_work(&card->tqueue); - } - -out: - clear_bit(IDT77252_BIT_INTERRUPT, &card->flags); - return IRQ_HANDLED; -} - -static void -idt77252_softint(struct work_struct *work) -{ - struct idt77252_dev *card = - container_of(work, struct idt77252_dev, tqueue); - u32 stat; - int done; - - for (done = 1; ; done = 1) { - stat = readl(SAR_REG_STAT) >> 16; - - if ((stat & 0x0f) < SAR_FBQ0_HIGH) { - add_rx_skb(card, 0, SAR_FB_SIZE_0, 32); - done = 0; - } - - stat >>= 4; - if ((stat & 0x0f) < SAR_FBQ1_HIGH) { - add_rx_skb(card, 1, SAR_FB_SIZE_1, 32); - done = 0; - } - - stat >>= 4; - if ((stat & 0x0f) < SAR_FBQ2_HIGH) { - add_rx_skb(card, 2, SAR_FB_SIZE_2, 32); - done = 0; - } - - stat >>= 4; - if ((stat & 0x0f) < SAR_FBQ3_HIGH) { - add_rx_skb(card, 3, SAR_FB_SIZE_3, 32); - done = 0; - } - - if (done) - break; - } - - writel(readl(SAR_REG_CFG) | SAR_CFG_FBIE, SAR_REG_CFG); -} - - -static int -open_card_oam(struct idt77252_dev *card) -{ - unsigned long flags; - unsigned long addr; - struct vc_map *vc; - int vpi, vci; - int index; - u32 rcte; - - for (vpi = 0; vpi < (1 << card->vpibits); vpi++) { - for (vci = 3; vci < 5; vci++) { - index = VPCI2VC(card, vpi, vci); - - vc = kzalloc_obj(struct vc_map); - if (!vc) { - printk("%s: can't alloc vc\n", card->name); - return -ENOMEM; - } - vc->index = index; - card->vcs[index] = vc; - - flush_rx_pool(card, &vc->rcv.rx_pool); - - rcte = SAR_RCTE_CONNECTOPEN | - SAR_RCTE_RAWCELLINTEN | - SAR_RCTE_RCQ | - SAR_RCTE_FBP_1; - - addr = card->rct_base + (vc->index << 2); - write_sram(card, addr, rcte); - - spin_lock_irqsave(&card->cmd_lock, flags); - writel(SAR_CMD_OPEN_CONNECTION | (addr << 2), - SAR_REG_CMD); - waitfor_idle(card); - spin_unlock_irqrestore(&card->cmd_lock, flags); - } - } - - return 0; -} - -static void -close_card_oam(struct idt77252_dev *card) -{ - unsigned long flags; - unsigned long addr; - struct vc_map *vc; - int vpi, vci; - int index; - - for (vpi = 0; vpi < (1 << card->vpibits); vpi++) { - for (vci = 3; vci < 5; vci++) { - index = VPCI2VC(card, vpi, vci); - vc = card->vcs[index]; - - addr = card->rct_base + vc->index * SAR_SRAM_RCT_SIZE; - - spin_lock_irqsave(&card->cmd_lock, flags); - writel(SAR_CMD_CLOSE_CONNECTION | (addr << 2), - SAR_REG_CMD); - waitfor_idle(card); - spin_unlock_irqrestore(&card->cmd_lock, flags); - - if (skb_queue_len(&vc->rcv.rx_pool.queue) != 0) { - DPRINTK("%s: closing a VC " - "with pending rx buffers.\n", - card->name); - - recycle_rx_pool_skb(card, &vc->rcv.rx_pool); - } - kfree(vc); - } - } -} - -static int -open_card_ubr0(struct idt77252_dev *card) -{ - struct vc_map *vc; - - vc = kzalloc_obj(struct vc_map); - if (!vc) { - printk("%s: can't alloc vc\n", card->name); - return -ENOMEM; - } - card->vcs[0] = vc; - vc->class = SCHED_UBR0; - - vc->scq = alloc_scq(card, vc->class); - if (!vc->scq) { - printk("%s: can't get SCQ.\n", card->name); - kfree(card->vcs[0]); - card->vcs[0] = NULL; - return -ENOMEM; - } - - card->scd2vc[0] = vc; - vc->scd_index = 0; - vc->scq->scd = card->scd_base; - - fill_scd(card, vc->scq, vc->class); - - write_sram(card, card->tct_base + 0, TCT_UBR | card->scd_base); - write_sram(card, card->tct_base + 1, 0); - write_sram(card, card->tct_base + 2, 0); - write_sram(card, card->tct_base + 3, 0); - write_sram(card, card->tct_base + 4, 0); - write_sram(card, card->tct_base + 5, 0); - write_sram(card, card->tct_base + 6, 0); - write_sram(card, card->tct_base + 7, TCT_FLAG_UBR); - - clear_bit(VCF_IDLE, &vc->flags); - writel(TCMDQ_START | 0, SAR_REG_TCMDQ); - return 0; -} - -static void -close_card_ubr0(struct idt77252_dev *card) -{ - struct vc_map *vc = card->vcs[0]; - - free_scq(card, vc->scq); - kfree(vc); -} - -static int -idt77252_dev_open(struct idt77252_dev *card) -{ - u32 conf; - - if (!test_bit(IDT77252_BIT_INIT, &card->flags)) { - printk("%s: SAR not yet initialized.\n", card->name); - return -1; - } - - conf = SAR_CFG_RXPTH| /* enable receive path */ - SAR_RX_DELAY | /* interrupt on complete PDU */ - SAR_CFG_RAWIE | /* interrupt enable on raw cells */ - SAR_CFG_RQFIE | /* interrupt on RSQ almost full */ - SAR_CFG_TMOIE | /* interrupt on timer overflow */ - SAR_CFG_FBIE | /* interrupt on low free buffers */ - SAR_CFG_TXEN | /* transmit operation enable */ - SAR_CFG_TXINT | /* interrupt on transmit status */ - SAR_CFG_TXUIE | /* interrupt on transmit underrun */ - SAR_CFG_TXSFI | /* interrupt on TSQ almost full */ - SAR_CFG_PHYIE /* enable PHY interrupts */ - ; - -#ifdef CONFIG_ATM_IDT77252_RCV_ALL - /* Test RAW cell receive. */ - conf |= SAR_CFG_VPECA; -#endif - - writel(readl(SAR_REG_CFG) | conf, SAR_REG_CFG); - - if (open_card_oam(card)) { - printk("%s: Error initializing OAM.\n", card->name); - return -1; - } - - if (open_card_ubr0(card)) { - printk("%s: Error initializing UBR0.\n", card->name); - return -1; - } - - IPRINTK("%s: opened IDT77252 ABR SAR.\n", card->name); - return 0; -} - -static void idt77252_dev_close(struct atm_dev *dev) -{ - struct idt77252_dev *card = dev->dev_data; - u32 conf; - - close_card_ubr0(card); - close_card_oam(card); - - conf = SAR_CFG_RXPTH | /* enable receive path */ - SAR_RX_DELAY | /* interrupt on complete PDU */ - SAR_CFG_RAWIE | /* interrupt enable on raw cells */ - SAR_CFG_RQFIE | /* interrupt on RSQ almost full */ - SAR_CFG_TMOIE | /* interrupt on timer overflow */ - SAR_CFG_FBIE | /* interrupt on low free buffers */ - SAR_CFG_TXEN | /* transmit operation enable */ - SAR_CFG_TXINT | /* interrupt on transmit status */ - SAR_CFG_TXUIE | /* interrupt on xmit underrun */ - SAR_CFG_TXSFI /* interrupt on TSQ almost full */ - ; - - writel(readl(SAR_REG_CFG) & ~(conf), SAR_REG_CFG); - - DIPRINTK("%s: closed IDT77252 ABR SAR.\n", card->name); -} - - -/*****************************************************************************/ -/* */ -/* Initialisation and Deinitialization of IDT77252 */ -/* */ -/*****************************************************************************/ - - -static void -deinit_card(struct idt77252_dev *card) -{ - struct sk_buff *skb; - int i, j; - - if (!test_bit(IDT77252_BIT_INIT, &card->flags)) { - printk("%s: SAR not yet initialized.\n", card->name); - return; - } - DIPRINTK("idt77252: deinitialize card %u\n", card->index); - - writel(0, SAR_REG_CFG); - - if (card->atmdev) - atm_dev_deregister(card->atmdev); - - for (i = 0; i < 4; i++) { - for (j = 0; j < FBQ_SIZE; j++) { - skb = card->sbpool[i].skb[j]; - if (skb) { - dma_unmap_single(&card->pcidev->dev, - IDT77252_PRV_PADDR(skb), - (skb_end_pointer(skb) - - skb->data), - DMA_FROM_DEVICE); - card->sbpool[i].skb[j] = NULL; - dev_kfree_skb(skb); - } - } - } - - vfree(card->soft_tst); - - vfree(card->scd2vc); - - vfree(card->vcs); - - if (card->raw_cell_hnd) { - dma_free_coherent(&card->pcidev->dev, 2 * sizeof(u32), - card->raw_cell_hnd, card->raw_cell_paddr); - } - - if (card->rsq.base) { - DIPRINTK("%s: Release RSQ ...\n", card->name); - deinit_rsq(card); - } - - if (card->tsq.base) { - DIPRINTK("%s: Release TSQ ...\n", card->name); - deinit_tsq(card); - } - - DIPRINTK("idt77252: Release IRQ.\n"); - free_irq(card->pcidev->irq, card); - - for (i = 0; i < 4; i++) { - if (card->fbq[i]) - iounmap(card->fbq[i]); - } - - if (card->membase) - iounmap(card->membase); - - clear_bit(IDT77252_BIT_INIT, &card->flags); - DIPRINTK("%s: Card deinitialized.\n", card->name); -} - - -static void init_sram(struct idt77252_dev *card) -{ - int i; - - for (i = 0; i < card->sramsize; i += 4) - write_sram(card, (i >> 2), 0); - - /* set SRAM layout for THIS card */ - if (card->sramsize == (512 * 1024)) { - card->tct_base = SAR_SRAM_TCT_128_BASE; - card->tct_size = (SAR_SRAM_TCT_128_TOP - card->tct_base + 1) - / SAR_SRAM_TCT_SIZE; - card->rct_base = SAR_SRAM_RCT_128_BASE; - card->rct_size = (SAR_SRAM_RCT_128_TOP - card->rct_base + 1) - / SAR_SRAM_RCT_SIZE; - card->rt_base = SAR_SRAM_RT_128_BASE; - card->scd_base = SAR_SRAM_SCD_128_BASE; - card->scd_size = (SAR_SRAM_SCD_128_TOP - card->scd_base + 1) - / SAR_SRAM_SCD_SIZE; - card->tst[0] = SAR_SRAM_TST1_128_BASE; - card->tst[1] = SAR_SRAM_TST2_128_BASE; - card->tst_size = SAR_SRAM_TST1_128_TOP - card->tst[0] + 1; - card->abrst_base = SAR_SRAM_ABRSTD_128_BASE; - card->abrst_size = SAR_ABRSTD_SIZE_8K; - card->fifo_base = SAR_SRAM_FIFO_128_BASE; - card->fifo_size = SAR_RXFD_SIZE_32K; - } else { - card->tct_base = SAR_SRAM_TCT_32_BASE; - card->tct_size = (SAR_SRAM_TCT_32_TOP - card->tct_base + 1) - / SAR_SRAM_TCT_SIZE; - card->rct_base = SAR_SRAM_RCT_32_BASE; - card->rct_size = (SAR_SRAM_RCT_32_TOP - card->rct_base + 1) - / SAR_SRAM_RCT_SIZE; - card->rt_base = SAR_SRAM_RT_32_BASE; - card->scd_base = SAR_SRAM_SCD_32_BASE; - card->scd_size = (SAR_SRAM_SCD_32_TOP - card->scd_base + 1) - / SAR_SRAM_SCD_SIZE; - card->tst[0] = SAR_SRAM_TST1_32_BASE; - card->tst[1] = SAR_SRAM_TST2_32_BASE; - card->tst_size = (SAR_SRAM_TST1_32_TOP - card->tst[0] + 1); - card->abrst_base = SAR_SRAM_ABRSTD_32_BASE; - card->abrst_size = SAR_ABRSTD_SIZE_1K; - card->fifo_base = SAR_SRAM_FIFO_32_BASE; - card->fifo_size = SAR_RXFD_SIZE_4K; - } - - /* Initialize TCT */ - for (i = 0; i < card->tct_size; i++) { - write_sram(card, i * SAR_SRAM_TCT_SIZE + 0, 0); - write_sram(card, i * SAR_SRAM_TCT_SIZE + 1, 0); - write_sram(card, i * SAR_SRAM_TCT_SIZE + 2, 0); - write_sram(card, i * SAR_SRAM_TCT_SIZE + 3, 0); - write_sram(card, i * SAR_SRAM_TCT_SIZE + 4, 0); - write_sram(card, i * SAR_SRAM_TCT_SIZE + 5, 0); - write_sram(card, i * SAR_SRAM_TCT_SIZE + 6, 0); - write_sram(card, i * SAR_SRAM_TCT_SIZE + 7, 0); - } - - /* Initialize RCT */ - for (i = 0; i < card->rct_size; i++) { - write_sram(card, card->rct_base + i * SAR_SRAM_RCT_SIZE, - (u32) SAR_RCTE_RAWCELLINTEN); - write_sram(card, card->rct_base + i * SAR_SRAM_RCT_SIZE + 1, - (u32) 0); - write_sram(card, card->rct_base + i * SAR_SRAM_RCT_SIZE + 2, - (u32) 0); - write_sram(card, card->rct_base + i * SAR_SRAM_RCT_SIZE + 3, - (u32) 0xffffffff); - } - - writel((SAR_FBQ0_LOW << 28) | (SAR_FB_SIZE_0 / 48), SAR_REG_FBQS0); - writel((SAR_FBQ1_LOW << 28) | (SAR_FB_SIZE_1 / 48), SAR_REG_FBQS1); - writel((SAR_FBQ2_LOW << 28) | (SAR_FB_SIZE_2 / 48), SAR_REG_FBQS2); - writel((SAR_FBQ3_LOW << 28) | (SAR_FB_SIZE_3 / 48), SAR_REG_FBQS3); - - /* Initialize rate table */ - for (i = 0; i < 256; i++) { - write_sram(card, card->rt_base + i, log_to_rate[i]); - } - - for (i = 0; i < 128; i++) { - unsigned int tmp; - - tmp = rate_to_log[(i << 2) + 0] << 0; - tmp |= rate_to_log[(i << 2) + 1] << 8; - tmp |= rate_to_log[(i << 2) + 2] << 16; - tmp |= rate_to_log[(i << 2) + 3] << 24; - write_sram(card, card->rt_base + 256 + i, tmp); - } - -#if 0 /* Fill RDF and AIR tables. */ - for (i = 0; i < 128; i++) { - unsigned int tmp; - - tmp = RDF[0][(i << 1) + 0] << 16; - tmp |= RDF[0][(i << 1) + 1] << 0; - write_sram(card, card->rt_base + 512 + i, tmp); - } - - for (i = 0; i < 128; i++) { - unsigned int tmp; - - tmp = AIR[0][(i << 1) + 0] << 16; - tmp |= AIR[0][(i << 1) + 1] << 0; - write_sram(card, card->rt_base + 640 + i, tmp); - } -#endif - - IPRINTK("%s: initialize rate table ...\n", card->name); - writel(card->rt_base << 2, SAR_REG_RTBL); - - /* Initialize TSTs */ - IPRINTK("%s: initialize TST ...\n", card->name); - card->tst_free = card->tst_size - 2; /* last two are jumps */ - - for (i = card->tst[0]; i < card->tst[0] + card->tst_size - 2; i++) - write_sram(card, i, TSTE_OPC_VAR); - write_sram(card, i++, TSTE_OPC_JMP | (card->tst[0] << 2)); - idt77252_sram_write_errors = 1; - write_sram(card, i++, TSTE_OPC_JMP | (card->tst[1] << 2)); - idt77252_sram_write_errors = 0; - for (i = card->tst[1]; i < card->tst[1] + card->tst_size - 2; i++) - write_sram(card, i, TSTE_OPC_VAR); - write_sram(card, i++, TSTE_OPC_JMP | (card->tst[1] << 2)); - idt77252_sram_write_errors = 1; - write_sram(card, i++, TSTE_OPC_JMP | (card->tst[0] << 2)); - idt77252_sram_write_errors = 0; - - card->tst_index = 0; - writel(card->tst[0] << 2, SAR_REG_TSTB); - - /* Initialize ABRSTD and Receive FIFO */ - IPRINTK("%s: initialize ABRSTD ...\n", card->name); - writel(card->abrst_size | (card->abrst_base << 2), - SAR_REG_ABRSTD); - - IPRINTK("%s: initialize receive fifo ...\n", card->name); - writel(card->fifo_size | (card->fifo_base << 2), - SAR_REG_RXFD); - - IPRINTK("%s: SRAM initialization complete.\n", card->name); -} - -static int init_card(struct atm_dev *dev) -{ - struct idt77252_dev *card = dev->dev_data; - struct pci_dev *pcidev = card->pcidev; - unsigned long tmpl, modl; - unsigned int linkrate, rsvdcr; - unsigned int tst_entries; - struct net_device *tmp; - char tname[10]; - - u32 size; - u_char pci_byte; - u32 conf; - int i, k; - - if (test_bit(IDT77252_BIT_INIT, &card->flags)) { - printk("Error: SAR already initialized.\n"); - return -1; - } - -/*****************************************************************/ -/* P C I C O N F I G U R A T I O N */ -/*****************************************************************/ - - /* Set PCI Retry-Timeout and TRDY timeout */ - IPRINTK("%s: Checking PCI retries.\n", card->name); - if (pci_read_config_byte(pcidev, 0x40, &pci_byte) != 0) { - printk("%s: can't read PCI retry timeout.\n", card->name); - deinit_card(card); - return -1; - } - if (pci_byte != 0) { - IPRINTK("%s: PCI retry timeout: %d, set to 0.\n", - card->name, pci_byte); - if (pci_write_config_byte(pcidev, 0x40, 0) != 0) { - printk("%s: can't set PCI retry timeout.\n", - card->name); - deinit_card(card); - return -1; - } - } - IPRINTK("%s: Checking PCI TRDY.\n", card->name); - if (pci_read_config_byte(pcidev, 0x41, &pci_byte) != 0) { - printk("%s: can't read PCI TRDY timeout.\n", card->name); - deinit_card(card); - return -1; - } - if (pci_byte != 0) { - IPRINTK("%s: PCI TRDY timeout: %d, set to 0.\n", - card->name, pci_byte); - if (pci_write_config_byte(pcidev, 0x41, 0) != 0) { - printk("%s: can't set PCI TRDY timeout.\n", card->name); - deinit_card(card); - return -1; - } - } - /* Reset Timer register */ - if (readl(SAR_REG_STAT) & SAR_STAT_TMROF) { - printk("%s: resetting timer overflow.\n", card->name); - writel(SAR_STAT_TMROF, SAR_REG_STAT); - } - IPRINTK("%s: Request IRQ ... ", card->name); - if (request_irq(pcidev->irq, idt77252_interrupt, IRQF_SHARED, - card->name, card) != 0) { - printk("%s: can't allocate IRQ.\n", card->name); - deinit_card(card); - return -1; - } - IPRINTK("got %d.\n", pcidev->irq); - -/*****************************************************************/ -/* C H E C K A N D I N I T S R A M */ -/*****************************************************************/ - - IPRINTK("%s: Initializing SRAM\n", card->name); - - /* preset size of connecton table, so that init_sram() knows about it */ - conf = SAR_CFG_TX_FIFO_SIZE_9 | /* Use maximum fifo size */ - SAR_CFG_RXSTQ_SIZE_8k | /* Receive Status Queue is 8k */ - SAR_CFG_IDLE_CLP | /* Set CLP on idle cells */ -#ifndef ATM_IDT77252_SEND_IDLE - SAR_CFG_NO_IDLE | /* Do not send idle cells */ -#endif - 0; - - if (card->sramsize == (512 * 1024)) - conf |= SAR_CFG_CNTBL_1k; - else - conf |= SAR_CFG_CNTBL_512; - - switch (vpibits) { - case 0: - conf |= SAR_CFG_VPVCS_0; - break; - default: - case 1: - conf |= SAR_CFG_VPVCS_1; - break; - case 2: - conf |= SAR_CFG_VPVCS_2; - break; - case 8: - conf |= SAR_CFG_VPVCS_8; - break; - } - - writel(readl(SAR_REG_CFG) | conf, SAR_REG_CFG); - - init_sram(card); - -/********************************************************************/ -/* A L L O C R A M A N D S E T V A R I O U S T H I N G S */ -/********************************************************************/ - /* Initialize TSQ */ - if (0 != init_tsq(card)) { - deinit_card(card); - return -1; - } - /* Initialize RSQ */ - if (0 != init_rsq(card)) { - deinit_card(card); - return -1; - } - - card->vpibits = vpibits; - if (card->sramsize == (512 * 1024)) { - card->vcibits = 10 - card->vpibits; - } else { - card->vcibits = 9 - card->vpibits; - } - - card->vcimask = 0; - for (k = 0, i = 1; k < card->vcibits; k++) { - card->vcimask |= i; - i <<= 1; - } - - IPRINTK("%s: Setting VPI/VCI mask to zero.\n", card->name); - writel(0, SAR_REG_VPM); - - /* Little Endian Order */ - writel(0, SAR_REG_GP); - - /* Initialize RAW Cell Handle Register */ - card->raw_cell_hnd = dma_alloc_coherent(&card->pcidev->dev, - 2 * sizeof(u32), - &card->raw_cell_paddr, - GFP_KERNEL); - if (!card->raw_cell_hnd) { - printk("%s: memory allocation failure.\n", card->name); - deinit_card(card); - return -1; - } - writel(card->raw_cell_paddr, SAR_REG_RAWHND); - IPRINTK("%s: raw cell handle is at 0x%p.\n", card->name, - card->raw_cell_hnd); - - size = sizeof(struct vc_map *) * card->tct_size; - IPRINTK("%s: allocate %d byte for VC map.\n", card->name, size); - card->vcs = vzalloc(size); - if (!card->vcs) { - printk("%s: memory allocation failure.\n", card->name); - deinit_card(card); - return -1; - } - - size = sizeof(struct vc_map *) * card->scd_size; - IPRINTK("%s: allocate %d byte for SCD to VC mapping.\n", - card->name, size); - card->scd2vc = vzalloc(size); - if (!card->scd2vc) { - printk("%s: memory allocation failure.\n", card->name); - deinit_card(card); - return -1; - } - - size = sizeof(struct tst_info) * (card->tst_size - 2); - IPRINTK("%s: allocate %d byte for TST to VC mapping.\n", - card->name, size); - card->soft_tst = vmalloc(size); - if (!card->soft_tst) { - printk("%s: memory allocation failure.\n", card->name); - deinit_card(card); - return -1; - } - for (i = 0; i < card->tst_size - 2; i++) { - card->soft_tst[i].tste = TSTE_OPC_VAR; - card->soft_tst[i].vc = NULL; - } - - if (dev->phy == NULL) { - printk("%s: No LT device defined.\n", card->name); - deinit_card(card); - return -1; - } - if (dev->phy->ioctl == NULL) { - printk("%s: LT had no IOCTL function defined.\n", card->name); - deinit_card(card); - return -1; - } - -#ifdef CONFIG_ATM_IDT77252_USE_SUNI - /* - * this is a jhs hack to get around special functionality in the - * phy driver for the atecom hardware; the functionality doesn't - * exist in the linux atm suni driver - * - * it isn't the right way to do things, but as the guy from NIST - * said, talking about their measurement of the fine structure - * constant, "it's good enough for government work." - */ - linkrate = 149760000; -#endif - - card->link_pcr = (linkrate / 8 / 53); - printk("%s: Linkrate on ATM line : %u bit/s, %u cell/s.\n", - card->name, linkrate, card->link_pcr); - -#ifdef ATM_IDT77252_SEND_IDLE - card->utopia_pcr = card->link_pcr; -#else - card->utopia_pcr = (160000000 / 8 / 54); -#endif - - rsvdcr = 0; - if (card->utopia_pcr > card->link_pcr) - rsvdcr = card->utopia_pcr - card->link_pcr; - - tmpl = (unsigned long) rsvdcr * ((unsigned long) card->tst_size - 2); - modl = tmpl % (unsigned long)card->utopia_pcr; - tst_entries = (int) (tmpl / (unsigned long)card->utopia_pcr); - if (modl) - tst_entries++; - card->tst_free -= tst_entries; - fill_tst(card, NULL, tst_entries, TSTE_OPC_NULL); - -#ifdef HAVE_EEPROM - idt77252_eeprom_init(card); - printk("%s: EEPROM: %02x:", card->name, - idt77252_eeprom_read_status(card)); - - for (i = 0; i < 0x80; i++) { - printk(" %02x", - idt77252_eeprom_read_byte(card, i) - ); - } - printk("\n"); -#endif /* HAVE_EEPROM */ - - /* - * XXX: - */ - sprintf(tname, "eth%d", card->index); - tmp = dev_get_by_name(&init_net, tname); /* jhs: was "tmp = dev_get(tname);" */ - if (tmp) { - memcpy(card->atmdev->esi, tmp->dev_addr, 6); - dev_put(tmp); - printk("%s: ESI %pM\n", card->name, card->atmdev->esi); - } - /* - * XXX: - */ - - /* Set Maximum Deficit Count for now. */ - writel(0xffff, SAR_REG_MDFCT); - - set_bit(IDT77252_BIT_INIT, &card->flags); - - XPRINTK("%s: IDT77252 ABR SAR initialization complete.\n", card->name); - return 0; -} - - -/*****************************************************************************/ -/* */ -/* Probing of IDT77252 ABR SAR */ -/* */ -/*****************************************************************************/ - - -static int idt77252_preset(struct idt77252_dev *card) -{ - u16 pci_command; - -/*****************************************************************/ -/* P C I C O N F I G U R A T I O N */ -/*****************************************************************/ - - XPRINTK("%s: Enable PCI master and memory access for SAR.\n", - card->name); - if (pci_read_config_word(card->pcidev, PCI_COMMAND, &pci_command)) { - printk("%s: can't read PCI_COMMAND.\n", card->name); - deinit_card(card); - return -1; - } - if (!(pci_command & PCI_COMMAND_IO)) { - printk("%s: PCI_COMMAND: %04x (?)\n", - card->name, pci_command); - deinit_card(card); - return (-1); - } - pci_command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - if (pci_write_config_word(card->pcidev, PCI_COMMAND, pci_command)) { - printk("%s: can't write PCI_COMMAND.\n", card->name); - deinit_card(card); - return -1; - } -/*****************************************************************/ -/* G E N E R I C R E S E T */ -/*****************************************************************/ - - /* Software reset */ - writel(SAR_CFG_SWRST, SAR_REG_CFG); - mdelay(1); - writel(0, SAR_REG_CFG); - - IPRINTK("%s: Software resetted.\n", card->name); - return 0; -} - - -static unsigned long probe_sram(struct idt77252_dev *card) -{ - u32 data, addr; - - writel(0, SAR_REG_DR0); - writel(SAR_CMD_WRITE_SRAM | (0 << 2), SAR_REG_CMD); - - for (addr = 0x4000; addr < 0x80000; addr += 0x4000) { - writel(ATM_POISON, SAR_REG_DR0); - writel(SAR_CMD_WRITE_SRAM | (addr << 2), SAR_REG_CMD); - - writel(SAR_CMD_READ_SRAM | (0 << 2), SAR_REG_CMD); - data = readl(SAR_REG_DR0); - - if (data != 0) - break; - } - - return addr * sizeof(u32); -} - -static int idt77252_init_one(struct pci_dev *pcidev, - const struct pci_device_id *id) -{ - static struct idt77252_dev **last = &idt77252_chain; - static int index = 0; - - unsigned long membase, srambase; - struct idt77252_dev *card; - struct atm_dev *dev; - int i, err; - - - if ((err = pci_enable_device(pcidev))) { - printk("idt77252: can't enable PCI device at %s\n", pci_name(pcidev)); - return err; - } - - if ((err = dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32)))) { - printk("idt77252: can't enable DMA for PCI device at %s\n", pci_name(pcidev)); - goto err_out_disable_pdev; - } - - card = kzalloc_obj(struct idt77252_dev); - if (!card) { - printk("idt77252-%d: can't allocate private data\n", index); - err = -ENOMEM; - goto err_out_disable_pdev; - } - card->revision = pcidev->revision; - card->index = index; - card->pcidev = pcidev; - sprintf(card->name, "idt77252-%d", card->index); - - INIT_WORK(&card->tqueue, idt77252_softint); - - membase = pci_resource_start(pcidev, 1); - srambase = pci_resource_start(pcidev, 2); - - mutex_init(&card->mutex); - spin_lock_init(&card->cmd_lock); - spin_lock_init(&card->tst_lock); - - timer_setup(&card->tst_timer, tst_timer, 0); - - /* Do the I/O remapping... */ - card->membase = ioremap(membase, 1024); - if (!card->membase) { - printk("%s: can't ioremap() membase\n", card->name); - err = -EIO; - goto err_out_free_card; - } - - if (idt77252_preset(card)) { - printk("%s: preset failed\n", card->name); - err = -EIO; - goto err_out_iounmap; - } - - dev = atm_dev_register("idt77252", &pcidev->dev, &idt77252_ops, -1, - NULL); - if (!dev) { - printk("%s: can't register atm device\n", card->name); - err = -EIO; - goto err_out_iounmap; - } - dev->dev_data = card; - card->atmdev = dev; - -#ifdef CONFIG_ATM_IDT77252_USE_SUNI - suni_init(dev); - if (!dev->phy) { - printk("%s: can't init SUNI\n", card->name); - err = -EIO; - goto err_out_deinit_card; - } -#endif /* CONFIG_ATM_IDT77252_USE_SUNI */ - - card->sramsize = probe_sram(card); - - for (i = 0; i < 4; i++) { - card->fbq[i] = ioremap(srambase | 0x200000 | (i << 18), 4); - if (!card->fbq[i]) { - printk("%s: can't ioremap() FBQ%d\n", card->name, i); - err = -EIO; - goto err_out_deinit_card; - } - } - - printk("%s: ABR SAR (Rev %c): MEM %08lx SRAM %08lx [%u KB]\n", - card->name, ((card->revision > 1) && (card->revision < 25)) ? - 'A' + card->revision - 1 : '?', membase, srambase, - card->sramsize / 1024); - - if (init_card(dev)) { - printk("%s: init_card failed\n", card->name); - err = -EIO; - goto err_out_deinit_card; - } - - dev->ci_range.vpi_bits = card->vpibits; - dev->ci_range.vci_bits = card->vcibits; - dev->link_rate = card->link_pcr; - - if (dev->phy->start) - dev->phy->start(dev); - - if (idt77252_dev_open(card)) { - printk("%s: dev_open failed\n", card->name); - err = -EIO; - goto err_out_stop; - } - - *last = card; - last = &card->next; - index++; - - return 0; - -err_out_stop: - if (dev->phy->stop) - dev->phy->stop(dev); - -err_out_deinit_card: - deinit_card(card); - -err_out_iounmap: - iounmap(card->membase); - -err_out_free_card: - kfree(card); - -err_out_disable_pdev: - pci_disable_device(pcidev); - return err; -} - -static const struct pci_device_id idt77252_pci_tbl[] = -{ - { PCI_VDEVICE(IDT, PCI_DEVICE_ID_IDT_IDT77252), 0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, idt77252_pci_tbl); - -static struct pci_driver idt77252_driver = { - .name = "idt77252", - .id_table = idt77252_pci_tbl, - .probe = idt77252_init_one, -}; - -static int __init idt77252_init(void) -{ - struct sk_buff *skb; - - printk("%s: at %p\n", __func__, idt77252_init); - BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct idt77252_skb_prv) + sizeof(struct atm_skb_data)); - return pci_register_driver(&idt77252_driver); -} - -static void __exit idt77252_exit(void) -{ - struct idt77252_dev *card; - struct atm_dev *dev; - - pci_unregister_driver(&idt77252_driver); - - while (idt77252_chain) { - card = idt77252_chain; - dev = card->atmdev; - idt77252_chain = card->next; - timer_shutdown_sync(&card->tst_timer); - - if (dev->phy->stop) - dev->phy->stop(dev); - deinit_card(card); - pci_disable_device(card->pcidev); - kfree(card); - } - - DIPRINTK("idt77252: finished cleanup-module().\n"); -} - -module_init(idt77252_init); -module_exit(idt77252_exit); - -MODULE_LICENSE("GPL"); - -module_param(vpibits, uint, 0); -MODULE_PARM_DESC(vpibits, "number of VPI bits supported (0, 1, or 2)"); -#ifdef CONFIG_ATM_IDT77252_DEBUG -module_param(debug, ulong, 0644); -MODULE_PARM_DESC(debug, "debug bitmap, see drivers/atm/idt77252.h"); -#endif - -MODULE_AUTHOR("Eddie C. Dost "); -MODULE_DESCRIPTION("IDT77252 ABR SAR Driver"); diff --git a/drivers/atm/idt77252.h b/drivers/atm/idt77252.h deleted file mode 100644 index b059d31364dd..000000000000 --- a/drivers/atm/idt77252.h +++ /dev/null @@ -1,816 +0,0 @@ -/******************************************************************* - * - * Copyright (c) 2000 ATecoM GmbH - * - * The author may be reached at ecd@atecom.com. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - *******************************************************************/ - -#ifndef _IDT77252_H -#define _IDT77252_H 1 - - -#include -#include -#include -#include - -/*****************************************************************************/ -/* */ -/* Makros */ -/* */ -/*****************************************************************************/ -#define VPCI2VC(card, vpi, vci) \ - (((vpi) << card->vcibits) | ((vci) & card->vcimask)) - -/*****************************************************************************/ -/* */ -/* DEBUGGING definitions */ -/* */ -/*****************************************************************************/ - -#define DBG_RAW_CELL 0x00000400 -#define DBG_TINY 0x00000200 -#define DBG_GENERAL 0x00000100 -#define DBG_XGENERAL 0x00000080 -#define DBG_INIT 0x00000040 -#define DBG_DEINIT 0x00000020 -#define DBG_INTERRUPT 0x00000010 -#define DBG_OPEN_CONN 0x00000008 -#define DBG_CLOSE_CONN 0x00000004 -#define DBG_RX_DATA 0x00000002 -#define DBG_TX_DATA 0x00000001 - -#ifdef CONFIG_ATM_IDT77252_DEBUG - -#define CPRINTK(args...) do { if (debug & DBG_CLOSE_CONN) printk(args); } while(0) -#define OPRINTK(args...) do { if (debug & DBG_OPEN_CONN) printk(args); } while(0) -#define IPRINTK(args...) do { if (debug & DBG_INIT) printk(args); } while(0) -#define INTPRINTK(args...) do { if (debug & DBG_INTERRUPT) printk(args); } while(0) -#define DIPRINTK(args...) do { if (debug & DBG_DEINIT) printk(args); } while(0) -#define TXPRINTK(args...) do { if (debug & DBG_TX_DATA) printk(args); } while(0) -#define RXPRINTK(args...) do { if (debug & DBG_RX_DATA) printk(args); } while(0) -#define XPRINTK(args...) do { if (debug & DBG_XGENERAL) printk(args); } while(0) -#define DPRINTK(args...) do { if (debug & DBG_GENERAL) printk(args); } while(0) -#define NPRINTK(args...) do { if (debug & DBG_TINY) printk(args); } while(0) -#define RPRINTK(args...) do { if (debug & DBG_RAW_CELL) printk(args); } while(0) - -#else - -#define CPRINTK(args...) do { } while(0) -#define OPRINTK(args...) do { } while(0) -#define IPRINTK(args...) do { } while(0) -#define INTPRINTK(args...) do { } while(0) -#define DIPRINTK(args...) do { } while(0) -#define TXPRINTK(args...) do { } while(0) -#define RXPRINTK(args...) do { } while(0) -#define XPRINTK(args...) do { } while(0) -#define DPRINTK(args...) do { } while(0) -#define NPRINTK(args...) do { } while(0) -#define RPRINTK(args...) do { } while(0) - -#endif - -#define SCHED_UBR0 0 -#define SCHED_UBR 1 -#define SCHED_VBR 2 -#define SCHED_ABR 3 -#define SCHED_CBR 4 - -#define SCQFULL_TIMEOUT HZ - -/*****************************************************************************/ -/* */ -/* Free Buffer Queue Layout */ -/* */ -/*****************************************************************************/ -#define SAR_FB_SIZE_0 (2048 - 256) -#define SAR_FB_SIZE_1 (4096 - 256) -#define SAR_FB_SIZE_2 (8192 - 256) -#define SAR_FB_SIZE_3 (16384 - 256) - -#define SAR_FBQ0_LOW 4 -#define SAR_FBQ0_HIGH 8 -#define SAR_FBQ1_LOW 2 -#define SAR_FBQ1_HIGH 4 -#define SAR_FBQ2_LOW 1 -#define SAR_FBQ2_HIGH 2 -#define SAR_FBQ3_LOW 1 -#define SAR_FBQ3_HIGH 2 - -#if 0 -#define SAR_TST_RESERVED 44 /* Num TST reserved for UBR/ABR/VBR */ -#else -#define SAR_TST_RESERVED 0 /* Num TST reserved for UBR/ABR/VBR */ -#endif - -#define TCT_CBR 0x00000000 -#define TCT_UBR 0x00000000 -#define TCT_VBR 0x40000000 -#define TCT_ABR 0x80000000 -#define TCT_TYPE 0xc0000000 - -#define TCT_RR 0x20000000 -#define TCT_LMCR 0x08000000 -#define TCT_SCD_MASK 0x0007ffff - -#define TCT_TSIF 0x00004000 -#define TCT_HALT 0x80000000 -#define TCT_IDLE 0x40000000 -#define TCT_FLAG_UBR 0x80000000 - -/*****************************************************************************/ -/* */ -/* Structure describing an IDT77252 */ -/* */ -/*****************************************************************************/ - -struct scqe -{ - u32 word_1; - u32 word_2; - u32 word_3; - u32 word_4; -}; - -#define SCQ_ENTRIES 64 -#define SCQ_SIZE (SCQ_ENTRIES * sizeof(struct scqe)) -#define SCQ_MASK (SCQ_SIZE - 1) - -struct scq_info -{ - struct scqe *base; - struct scqe *next; - struct scqe *last; - dma_addr_t paddr; - spinlock_t lock; - atomic_t used; - unsigned long trans_start; - unsigned long scd; - spinlock_t skblock; - struct sk_buff_head transmit; - struct sk_buff_head pending; -}; - -struct rx_pool { - struct sk_buff_head queue; - unsigned int len; -}; - -struct aal1 { - unsigned int total; - unsigned int count; - struct sk_buff *data; - unsigned char sequence; -}; - -struct vc_map; - -struct rate_estimator { - struct timer_list timer; - unsigned int interval; - unsigned int ewma_log; - u64 cells; - u64 last_cells; - long avcps; - u32 cps; - u32 maxcps; - struct vc_map *vc; -}; - -struct vc_map { - unsigned int index; - unsigned long flags; -#define VCF_TX 0 -#define VCF_RX 1 -#define VCF_IDLE 2 -#define VCF_RSV 3 - unsigned int class; - u8 init_er; - u8 lacr; - u8 max_er; - unsigned int ntste; - spinlock_t lock; - struct atm_vcc *tx_vcc; - struct atm_vcc *rx_vcc; - struct idt77252_dev *card; - struct scq_info *scq; /* To keep track of the SCQ */ - struct rate_estimator *estimator; - int scd_index; - union { - struct rx_pool rx_pool; - struct aal1 aal1; - } rcv; -}; - -/*****************************************************************************/ -/* */ -/* RCTE - Receive Connection Table Entry */ -/* */ -/*****************************************************************************/ - -struct rct_entry -{ - u32 word_1; - u32 buffer_handle; - u32 dma_address; - u32 aal5_crc32; -}; - -/*****************************************************************************/ -/* */ -/* RSQ - Receive Status Queue */ -/* */ -/*****************************************************************************/ - -#define SAR_RSQE_VALID 0x80000000 -#define SAR_RSQE_IDLE 0x40000000 -#define SAR_RSQE_BUF_MASK 0x00030000 -#define SAR_RSQE_BUF_ASGN 0x00008000 -#define SAR_RSQE_NZGFC 0x00004000 -#define SAR_RSQE_EPDU 0x00002000 -#define SAR_RSQE_BUF_CONT 0x00001000 -#define SAR_RSQE_EFCIE 0x00000800 -#define SAR_RSQE_CLP 0x00000400 -#define SAR_RSQE_CRC 0x00000200 -#define SAR_RSQE_CELLCNT 0x000001FF - - -#define RSQSIZE 8192 -#define RSQ_NUM_ENTRIES (RSQSIZE / 16) -#define RSQ_ALIGNMENT 8192 - -struct rsq_entry { - u32 word_1; - u32 word_2; - u32 word_3; - u32 word_4; -}; - -struct rsq_info { - struct rsq_entry *base; - struct rsq_entry *next; - struct rsq_entry *last; - dma_addr_t paddr; -}; - - -/*****************************************************************************/ -/* */ -/* TSQ - Transmit Status Queue */ -/* */ -/*****************************************************************************/ - -#define SAR_TSQE_INVALID 0x80000000 -#define SAR_TSQE_TIMESTAMP 0x00FFFFFF -#define SAR_TSQE_TYPE 0x60000000 -#define SAR_TSQE_TYPE_TIMER 0x00000000 -#define SAR_TSQE_TYPE_TSR 0x20000000 -#define SAR_TSQE_TYPE_IDLE 0x40000000 -#define SAR_TSQE_TYPE_TBD_COMP 0x60000000 - -#define SAR_TSQE_TAG(stat) (((stat) >> 24) & 0x1f) - -#define TSQSIZE 8192 -#define TSQ_NUM_ENTRIES 1024 -#define TSQ_ALIGNMENT 8192 - -struct tsq_entry -{ - u32 word_1; - u32 word_2; -}; - -struct tsq_info -{ - struct tsq_entry *base; - struct tsq_entry *next; - struct tsq_entry *last; - dma_addr_t paddr; -}; - -struct tst_info -{ - struct vc_map *vc; - u32 tste; -}; - -#define TSTE_MASK 0x601fffff - -#define TSTE_OPC_MASK 0x60000000 -#define TSTE_OPC_NULL 0x00000000 -#define TSTE_OPC_CBR 0x20000000 -#define TSTE_OPC_VAR 0x40000000 -#define TSTE_OPC_JMP 0x60000000 - -#define TSTE_PUSH_IDLE 0x01000000 -#define TSTE_PUSH_ACTIVE 0x02000000 - -#define TST_SWITCH_DONE 0 -#define TST_SWITCH_PENDING 1 -#define TST_SWITCH_WAIT 2 - -#define FBQ_SHIFT 9 -#define FBQ_SIZE (1 << FBQ_SHIFT) -#define FBQ_MASK (FBQ_SIZE - 1) - -struct sb_pool -{ - unsigned int index; - struct sk_buff *skb[FBQ_SIZE]; -}; - -#define POOL_HANDLE(queue, index) (((queue + 1) << 16) | (index)) -#define POOL_QUEUE(handle) (((handle) >> 16) - 1) -#define POOL_INDEX(handle) ((handle) & 0xffff) - -struct idt77252_dev -{ - struct tsq_info tsq; /* Transmit Status Queue */ - struct rsq_info rsq; /* Receive Status Queue */ - - struct pci_dev *pcidev; /* PCI handle (desriptor) */ - struct atm_dev *atmdev; /* ATM device desriptor */ - - void __iomem *membase; /* SAR's memory base address */ - unsigned long srambase; /* SAR's sram base address */ - void __iomem *fbq[4]; /* FBQ fill addresses */ - - struct mutex mutex; - spinlock_t cmd_lock; /* for r/w utility/sram */ - - unsigned long softstat; - unsigned long flags; /* see blow */ - - struct work_struct tqueue; - - unsigned long tct_base; /* TCT base address in SRAM */ - unsigned long rct_base; /* RCT base address in SRAM */ - unsigned long rt_base; /* Rate Table base in SRAM */ - unsigned long scd_base; /* SCD base address in SRAM */ - unsigned long tst[2]; /* TST base address in SRAM */ - unsigned long abrst_base; /* ABRST base address in SRAM */ - unsigned long fifo_base; /* RX FIFO base in SRAM */ - - unsigned long irqstat[16]; - - unsigned int sramsize; /* SAR's sram size */ - - unsigned int tct_size; /* total TCT entries */ - unsigned int rct_size; /* total RCT entries */ - unsigned int scd_size; /* length of SCD */ - unsigned int tst_size; /* total TST entries */ - unsigned int tst_free; /* free TSTEs in TST */ - unsigned int abrst_size; /* size of ABRST in words */ - unsigned int fifo_size; /* size of RX FIFO in words */ - - unsigned int vpibits; /* Bits used for VPI index */ - unsigned int vcibits; /* Bits used for VCI index */ - unsigned int vcimask; /* Mask for VCI index */ - - unsigned int utopia_pcr; /* Utopia Itf's Cell Rate */ - unsigned int link_pcr; /* PHY's Peek Cell Rate */ - - struct vc_map **vcs; /* Open Connections */ - struct vc_map **scd2vc; /* SCD to Connection map */ - - struct tst_info *soft_tst; /* TST to Connection map */ - unsigned int tst_index; /* Current TST in use */ - struct timer_list tst_timer; - spinlock_t tst_lock; - unsigned long tst_state; - - struct sb_pool sbpool[4]; /* Pool of RX skbuffs */ - struct sk_buff *raw_cell_head; /* Pointer to raw cell queue */ - u32 *raw_cell_hnd; /* Pointer to RCQ handle */ - dma_addr_t raw_cell_paddr; - - int index; /* SAR's ID */ - int revision; /* chip revision */ - - char name[16]; /* Device name */ - - struct idt77252_dev *next; -}; - - -/* definition for flag field above */ -#define IDT77252_BIT_INIT 1 -#define IDT77252_BIT_INTERRUPT 2 - - -#define ATM_CELL_PAYLOAD 48 - -#define FREEBUF_ALIGNMENT 16 - -/*****************************************************************************/ -/* */ -/* Makros */ -/* */ -/*****************************************************************************/ -#define ALIGN_ADDRESS(addr, alignment) \ - ((((u32)(addr)) + (((u32)(alignment))-1)) & ~(((u32)(alignment)) - 1)) - - -/*****************************************************************************/ -/* */ -/* ABR SAR Network operation Register */ -/* */ -/*****************************************************************************/ - -#define SAR_REG_DR0 (card->membase + 0x00) -#define SAR_REG_DR1 (card->membase + 0x04) -#define SAR_REG_DR2 (card->membase + 0x08) -#define SAR_REG_DR3 (card->membase + 0x0C) -#define SAR_REG_CMD (card->membase + 0x10) -#define SAR_REG_CFG (card->membase + 0x14) -#define SAR_REG_STAT (card->membase + 0x18) -#define SAR_REG_RSQB (card->membase + 0x1C) -#define SAR_REG_RSQT (card->membase + 0x20) -#define SAR_REG_RSQH (card->membase + 0x24) -#define SAR_REG_CDC (card->membase + 0x28) -#define SAR_REG_VPEC (card->membase + 0x2C) -#define SAR_REG_ICC (card->membase + 0x30) -#define SAR_REG_RAWCT (card->membase + 0x34) -#define SAR_REG_TMR (card->membase + 0x38) -#define SAR_REG_TSTB (card->membase + 0x3C) -#define SAR_REG_TSQB (card->membase + 0x40) -#define SAR_REG_TSQT (card->membase + 0x44) -#define SAR_REG_TSQH (card->membase + 0x48) -#define SAR_REG_GP (card->membase + 0x4C) -#define SAR_REG_VPM (card->membase + 0x50) -#define SAR_REG_RXFD (card->membase + 0x54) -#define SAR_REG_RXFT (card->membase + 0x58) -#define SAR_REG_RXFH (card->membase + 0x5C) -#define SAR_REG_RAWHND (card->membase + 0x60) -#define SAR_REG_RXSTAT (card->membase + 0x64) -#define SAR_REG_ABRSTD (card->membase + 0x68) -#define SAR_REG_ABRRQ (card->membase + 0x6C) -#define SAR_REG_VBRRQ (card->membase + 0x70) -#define SAR_REG_RTBL (card->membase + 0x74) -#define SAR_REG_MDFCT (card->membase + 0x78) -#define SAR_REG_TXSTAT (card->membase + 0x7C) -#define SAR_REG_TCMDQ (card->membase + 0x80) -#define SAR_REG_IRCP (card->membase + 0x84) -#define SAR_REG_FBQP0 (card->membase + 0x88) -#define SAR_REG_FBQP1 (card->membase + 0x8C) -#define SAR_REG_FBQP2 (card->membase + 0x90) -#define SAR_REG_FBQP3 (card->membase + 0x94) -#define SAR_REG_FBQS0 (card->membase + 0x98) -#define SAR_REG_FBQS1 (card->membase + 0x9C) -#define SAR_REG_FBQS2 (card->membase + 0xA0) -#define SAR_REG_FBQS3 (card->membase + 0xA4) -#define SAR_REG_FBQWP0 (card->membase + 0xA8) -#define SAR_REG_FBQWP1 (card->membase + 0xAC) -#define SAR_REG_FBQWP2 (card->membase + 0xB0) -#define SAR_REG_FBQWP3 (card->membase + 0xB4) -#define SAR_REG_NOW (card->membase + 0xB8) - - -/*****************************************************************************/ -/* */ -/* Commands */ -/* */ -/*****************************************************************************/ - -#define SAR_CMD_NO_OPERATION 0x00000000 -#define SAR_CMD_OPENCLOSE_CONNECTION 0x20000000 -#define SAR_CMD_WRITE_SRAM 0x40000000 -#define SAR_CMD_READ_SRAM 0x50000000 -#define SAR_CMD_READ_UTILITY 0x80000000 -#define SAR_CMD_WRITE_UTILITY 0x90000000 - -#define SAR_CMD_OPEN_CONNECTION (SAR_CMD_OPENCLOSE_CONNECTION | 0x00080000) -#define SAR_CMD_CLOSE_CONNECTION SAR_CMD_OPENCLOSE_CONNECTION - - -/*****************************************************************************/ -/* */ -/* Configuration Register bits */ -/* */ -/*****************************************************************************/ - -#define SAR_CFG_SWRST 0x80000000 /* Software reset */ -#define SAR_CFG_LOOP 0x40000000 /* Internal Loopback */ -#define SAR_CFG_RXPTH 0x20000000 /* Receive Path Enable */ -#define SAR_CFG_IDLE_CLP 0x10000000 /* SAR set CLP Bits of Null Cells */ -#define SAR_CFG_TX_FIFO_SIZE_1 0x04000000 /* TX FIFO Size = 1 cell */ -#define SAR_CFG_TX_FIFO_SIZE_2 0x08000000 /* TX FIFO Size = 2 cells */ -#define SAR_CFG_TX_FIFO_SIZE_4 0x0C000000 /* TX FIFO Size = 4 cells */ -#define SAR_CFG_TX_FIFO_SIZE_9 0x00000000 /* TX FIFO Size = 9 cells (full) */ -#define SAR_CFG_NO_IDLE 0x02000000 /* SAR sends no Null Cells */ -#define SAR_CFG_RSVD1 0x01000000 /* Reserved */ -#define SAR_CFG_RXSTQ_SIZE_2k 0x00000000 /* RX Stat Queue Size = 2048 byte */ -#define SAR_CFG_RXSTQ_SIZE_4k 0x00400000 /* RX Stat Queue Size = 4096 byte */ -#define SAR_CFG_RXSTQ_SIZE_8k 0x00800000 /* RX Stat Queue Size = 8192 byte */ -#define SAR_CFG_RXSTQ_SIZE_R 0x00C00000 /* RX Stat Queue Size = reserved */ -#define SAR_CFG_ICAPT 0x00200000 /* accept Invalid Cells */ -#define SAR_CFG_IGGFC 0x00100000 /* Ignore GFC */ -#define SAR_CFG_VPVCS_0 0x00000000 /* VPI/VCI Select bit range */ -#define SAR_CFG_VPVCS_1 0x00040000 /* VPI/VCI Select bit range */ -#define SAR_CFG_VPVCS_2 0x00080000 /* VPI/VCI Select bit range */ -#define SAR_CFG_VPVCS_8 0x000C0000 /* VPI/VCI Select bit range */ -#define SAR_CFG_CNTBL_1k 0x00000000 /* Connection Table Size */ -#define SAR_CFG_CNTBL_4k 0x00010000 /* Connection Table Size */ -#define SAR_CFG_CNTBL_16k 0x00020000 /* Connection Table Size */ -#define SAR_CFG_CNTBL_512 0x00030000 /* Connection Table Size */ -#define SAR_CFG_VPECA 0x00008000 /* VPI/VCI Error Cell Accept */ -#define SAR_CFG_RXINT_NOINT 0x00000000 /* No Interrupt on PDU received */ -#define SAR_CFG_RXINT_NODELAY 0x00001000 /* Interrupt without delay to host*/ -#define SAR_CFG_RXINT_256US 0x00002000 /* Interrupt with delay 256 usec */ -#define SAR_CFG_RXINT_505US 0x00003000 /* Interrupt with delay 505 usec */ -#define SAR_CFG_RXINT_742US 0x00004000 /* Interrupt with delay 742 usec */ -#define SAR_CFG_RAWIE 0x00000800 /* Raw Cell Queue Interrupt Enable*/ -#define SAR_CFG_RQFIE 0x00000400 /* RSQ Almost Full Int Enable */ -#define SAR_CFG_RSVD2 0x00000200 /* Reserved */ -#define SAR_CFG_CACHE 0x00000100 /* DMA on Cache Line Boundary */ -#define SAR_CFG_TMOIE 0x00000080 /* Timer Roll Over Int Enable */ -#define SAR_CFG_FBIE 0x00000040 /* Free Buffer Queue Int Enable */ -#define SAR_CFG_TXEN 0x00000020 /* Transmit Operation Enable */ -#define SAR_CFG_TXINT 0x00000010 /* Transmit status Int Enable */ -#define SAR_CFG_TXUIE 0x00000008 /* Transmit underrun Int Enable */ -#define SAR_CFG_UMODE 0x00000004 /* Utopia Mode Select */ -#define SAR_CFG_TXSFI 0x00000002 /* Transmit status Full Int Enable*/ -#define SAR_CFG_PHYIE 0x00000001 /* PHY Interrupt Enable */ - -#define SAR_CFG_TX_FIFO_SIZE_MASK 0x0C000000 /* TX FIFO Size Mask */ -#define SAR_CFG_RXSTQSIZE_MASK 0x00C00000 -#define SAR_CFG_CNTBL_MASK 0x00030000 -#define SAR_CFG_RXINT_MASK 0x00007000 - - -/*****************************************************************************/ -/* */ -/* Status Register bits */ -/* */ -/*****************************************************************************/ - -#define SAR_STAT_FRAC_3 0xF0000000 /* Fraction of Free Buffer Queue 3 */ -#define SAR_STAT_FRAC_2 0x0F000000 /* Fraction of Free Buffer Queue 2 */ -#define SAR_STAT_FRAC_1 0x00F00000 /* Fraction of Free Buffer Queue 1 */ -#define SAR_STAT_FRAC_0 0x000F0000 /* Fraction of Free Buffer Queue 0 */ -#define SAR_STAT_TSIF 0x00008000 /* Transmit Status Indicator */ -#define SAR_STAT_TXICP 0x00004000 /* Transmit Status Indicator */ -#define SAR_STAT_RSVD1 0x00002000 /* Reserved */ -#define SAR_STAT_TSQF 0x00001000 /* Transmit Status Queue full */ -#define SAR_STAT_TMROF 0x00000800 /* Timer overflow */ -#define SAR_STAT_PHYI 0x00000400 /* PHY device Interrupt flag */ -#define SAR_STAT_CMDBZ 0x00000200 /* ABR SAR Command Busy Flag */ -#define SAR_STAT_FBQ3A 0x00000100 /* Free Buffer Queue 3 Attention */ -#define SAR_STAT_FBQ2A 0x00000080 /* Free Buffer Queue 2 Attention */ -#define SAR_STAT_RSQF 0x00000040 /* Receive Status Queue full */ -#define SAR_STAT_EPDU 0x00000020 /* End Of PDU Flag */ -#define SAR_STAT_RAWCF 0x00000010 /* Raw Cell Flag */ -#define SAR_STAT_FBQ1A 0x00000008 /* Free Buffer Queue 1 Attention */ -#define SAR_STAT_FBQ0A 0x00000004 /* Free Buffer Queue 0 Attention */ -#define SAR_STAT_RSQAF 0x00000002 /* Receive Status Queue almost full*/ -#define SAR_STAT_RSVD2 0x00000001 /* Reserved */ - - -/*****************************************************************************/ -/* */ -/* General Purpose Register bits */ -/* */ -/*****************************************************************************/ - -#define SAR_GP_TXNCC_MASK 0xff000000 /* Transmit Negative Credit Count */ -#define SAR_GP_EEDI 0x00010000 /* EEPROM Data In */ -#define SAR_GP_BIGE 0x00008000 /* Big Endian Operation */ -#define SAR_GP_RM_NORMAL 0x00000000 /* Normal handling of RM cells */ -#define SAR_GP_RM_TO_RCQ 0x00002000 /* put RM cells into Raw Cell Queue */ -#define SAR_GP_RM_RSVD 0x00004000 /* Reserved */ -#define SAR_GP_RM_INHIBIT 0x00006000 /* Inhibit update of Connection tab */ -#define SAR_GP_PHY_RESET 0x00000008 /* PHY Reset */ -#define SAR_GP_EESCLK 0x00000004 /* EEPROM SCLK */ -#define SAR_GP_EECS 0x00000002 /* EEPROM Chip Select */ -#define SAR_GP_EEDO 0x00000001 /* EEPROM Data Out */ - - -/*****************************************************************************/ -/* */ -/* SAR local SRAM layout for 128k work SRAM */ -/* */ -/*****************************************************************************/ - -#define SAR_SRAM_SCD_SIZE 12 -#define SAR_SRAM_TCT_SIZE 8 -#define SAR_SRAM_RCT_SIZE 4 - -#define SAR_SRAM_TCT_128_BASE 0x00000 -#define SAR_SRAM_TCT_128_TOP 0x01fff -#define SAR_SRAM_RCT_128_BASE 0x02000 -#define SAR_SRAM_RCT_128_TOP 0x02fff -#define SAR_SRAM_FB0_128_BASE 0x03000 -#define SAR_SRAM_FB0_128_TOP 0x033ff -#define SAR_SRAM_FB1_128_BASE 0x03400 -#define SAR_SRAM_FB1_128_TOP 0x037ff -#define SAR_SRAM_FB2_128_BASE 0x03800 -#define SAR_SRAM_FB2_128_TOP 0x03bff -#define SAR_SRAM_FB3_128_BASE 0x03c00 -#define SAR_SRAM_FB3_128_TOP 0x03fff -#define SAR_SRAM_SCD_128_BASE 0x04000 -#define SAR_SRAM_SCD_128_TOP 0x07fff -#define SAR_SRAM_TST1_128_BASE 0x08000 -#define SAR_SRAM_TST1_128_TOP 0x0bfff -#define SAR_SRAM_TST2_128_BASE 0x0c000 -#define SAR_SRAM_TST2_128_TOP 0x0ffff -#define SAR_SRAM_ABRSTD_128_BASE 0x10000 -#define SAR_SRAM_ABRSTD_128_TOP 0x13fff -#define SAR_SRAM_RT_128_BASE 0x14000 -#define SAR_SRAM_RT_128_TOP 0x15fff - -#define SAR_SRAM_FIFO_128_BASE 0x18000 -#define SAR_SRAM_FIFO_128_TOP 0x1ffff - - -/*****************************************************************************/ -/* */ -/* SAR local SRAM layout for 32k work SRAM */ -/* */ -/*****************************************************************************/ - -#define SAR_SRAM_TCT_32_BASE 0x00000 -#define SAR_SRAM_TCT_32_TOP 0x00fff -#define SAR_SRAM_RCT_32_BASE 0x01000 -#define SAR_SRAM_RCT_32_TOP 0x017ff -#define SAR_SRAM_FB0_32_BASE 0x01800 -#define SAR_SRAM_FB0_32_TOP 0x01bff -#define SAR_SRAM_FB1_32_BASE 0x01c00 -#define SAR_SRAM_FB1_32_TOP 0x01fff -#define SAR_SRAM_FB2_32_BASE 0x02000 -#define SAR_SRAM_FB2_32_TOP 0x023ff -#define SAR_SRAM_FB3_32_BASE 0x02400 -#define SAR_SRAM_FB3_32_TOP 0x027ff -#define SAR_SRAM_SCD_32_BASE 0x02800 -#define SAR_SRAM_SCD_32_TOP 0x03fff -#define SAR_SRAM_TST1_32_BASE 0x04000 -#define SAR_SRAM_TST1_32_TOP 0x04fff -#define SAR_SRAM_TST2_32_BASE 0x05000 -#define SAR_SRAM_TST2_32_TOP 0x05fff -#define SAR_SRAM_ABRSTD_32_BASE 0x06000 -#define SAR_SRAM_ABRSTD_32_TOP 0x067ff -#define SAR_SRAM_RT_32_BASE 0x06800 -#define SAR_SRAM_RT_32_TOP 0x06fff -#define SAR_SRAM_FIFO_32_BASE 0x07000 -#define SAR_SRAM_FIFO_32_TOP 0x07fff - - -/*****************************************************************************/ -/* */ -/* TSR - Transmit Status Request */ -/* */ -/*****************************************************************************/ - -#define SAR_TSR_TYPE_TSR 0x80000000 -#define SAR_TSR_TYPE_TBD 0x00000000 -#define SAR_TSR_TSIF 0x20000000 -#define SAR_TSR_TAG_MASK 0x01F00000 - - -/*****************************************************************************/ -/* */ -/* TBD - Transmit Buffer Descriptor */ -/* */ -/*****************************************************************************/ - -#define SAR_TBD_EPDU 0x40000000 -#define SAR_TBD_TSIF 0x20000000 -#define SAR_TBD_OAM 0x10000000 -#define SAR_TBD_AAL0 0x00000000 -#define SAR_TBD_AAL34 0x04000000 -#define SAR_TBD_AAL5 0x08000000 -#define SAR_TBD_GTSI 0x02000000 -#define SAR_TBD_TAG_MASK 0x01F00000 - -#define SAR_TBD_VPI_MASK 0x0FF00000 -#define SAR_TBD_VCI_MASK 0x000FFFF0 -#define SAR_TBD_VC_MASK (SAR_TBD_VPI_MASK | SAR_TBD_VCI_MASK) - -#define SAR_TBD_VPI_SHIFT 20 -#define SAR_TBD_VCI_SHIFT 4 - - -/*****************************************************************************/ -/* */ -/* RXFD - Receive FIFO Descriptor */ -/* */ -/*****************************************************************************/ - -#define SAR_RXFD_SIZE_MASK 0x0F000000 -#define SAR_RXFD_SIZE_512 0x00000000 /* 512 words */ -#define SAR_RXFD_SIZE_1K 0x01000000 /* 1k words */ -#define SAR_RXFD_SIZE_2K 0x02000000 /* 2k words */ -#define SAR_RXFD_SIZE_4K 0x03000000 /* 4k words */ -#define SAR_RXFD_SIZE_8K 0x04000000 /* 8k words */ -#define SAR_RXFD_SIZE_16K 0x05000000 /* 16k words */ -#define SAR_RXFD_SIZE_32K 0x06000000 /* 32k words */ -#define SAR_RXFD_SIZE_64K 0x07000000 /* 64k words */ -#define SAR_RXFD_SIZE_128K 0x08000000 /* 128k words */ -#define SAR_RXFD_SIZE_256K 0x09000000 /* 256k words */ -#define SAR_RXFD_ADDR_MASK 0x001ffc00 - - -/*****************************************************************************/ -/* */ -/* ABRSTD - ABR + VBR Schedule Tables */ -/* */ -/*****************************************************************************/ - -#define SAR_ABRSTD_SIZE_MASK 0x07000000 -#define SAR_ABRSTD_SIZE_512 0x00000000 /* 512 words */ -#define SAR_ABRSTD_SIZE_1K 0x01000000 /* 1k words */ -#define SAR_ABRSTD_SIZE_2K 0x02000000 /* 2k words */ -#define SAR_ABRSTD_SIZE_4K 0x03000000 /* 4k words */ -#define SAR_ABRSTD_SIZE_8K 0x04000000 /* 8k words */ -#define SAR_ABRSTD_SIZE_16K 0x05000000 /* 16k words */ -#define SAR_ABRSTD_ADDR_MASK 0x001ffc00 - - -/*****************************************************************************/ -/* */ -/* RCTE - Receive Connection Table Entry */ -/* */ -/*****************************************************************************/ - -#define SAR_RCTE_IL_MASK 0xE0000000 /* inactivity limit */ -#define SAR_RCTE_IC_MASK 0x1C000000 /* inactivity count */ -#define SAR_RCTE_RSVD 0x02000000 /* reserved */ -#define SAR_RCTE_LCD 0x01000000 /* last cell data */ -#define SAR_RCTE_CI_VC 0x00800000 /* EFCI in previous cell of VC */ -#define SAR_RCTE_FBP_01 0x00000000 /* 1. cell->FBQ0, others->FBQ1 */ -#define SAR_RCTE_FBP_1 0x00200000 /* use FBQ 1 for all cells */ -#define SAR_RCTE_FBP_2 0x00400000 /* use FBQ 2 for all cells */ -#define SAR_RCTE_FBP_3 0x00600000 /* use FBQ 3 for all cells */ -#define SAR_RCTE_NZ_GFC 0x00100000 /* non zero GFC in all cell of VC */ -#define SAR_RCTE_CONNECTOPEN 0x00080000 /* VC is open */ -#define SAR_RCTE_AAL_MASK 0x00070000 /* mask for AAL type field s.b. */ -#define SAR_RCTE_RAWCELLINTEN 0x00008000 /* raw cell interrupt enable */ -#define SAR_RCTE_RXCONCELLADDR 0x00004000 /* RX constant cell address */ -#define SAR_RCTE_BUFFSTAT_MASK 0x00003000 /* buffer status */ -#define SAR_RCTE_EFCI 0x00000800 /* EFCI Congestion flag */ -#define SAR_RCTE_CLP 0x00000400 /* Cell Loss Priority flag */ -#define SAR_RCTE_CRC 0x00000200 /* Received CRC Error */ -#define SAR_RCTE_CELLCNT_MASK 0x000001FF /* cell Count */ - -#define SAR_RCTE_AAL0 0x00000000 /* AAL types for ALL field */ -#define SAR_RCTE_AAL34 0x00010000 -#define SAR_RCTE_AAL5 0x00020000 -#define SAR_RCTE_RCQ 0x00030000 -#define SAR_RCTE_OAM 0x00040000 - -#define TCMDQ_START 0x01000000 -#define TCMDQ_LACR 0x02000000 -#define TCMDQ_START_LACR 0x03000000 -#define TCMDQ_INIT_ER 0x04000000 -#define TCMDQ_HALT 0x05000000 - - -struct idt77252_skb_prv { - struct scqe tbd; /* Transmit Buffer Descriptor */ - dma_addr_t paddr; /* DMA handle */ - u32 pool; /* sb_pool handle */ -} __packed; - -#define IDT77252_PRV_TBD(skb) \ - (((struct idt77252_skb_prv *)(ATM_SKB(skb)+1))->tbd) -#define IDT77252_PRV_PADDR(skb) \ - (((struct idt77252_skb_prv *)(ATM_SKB(skb)+1))->paddr) -#define IDT77252_PRV_POOL(skb) \ - (((struct idt77252_skb_prv *)(ATM_SKB(skb)+1))->pool) - -/*****************************************************************************/ -/* */ -/* PCI related items */ -/* */ -/*****************************************************************************/ - -#ifndef PCI_VENDOR_ID_IDT -#define PCI_VENDOR_ID_IDT 0x111D -#endif /* PCI_VENDOR_ID_IDT */ - -#ifndef PCI_DEVICE_ID_IDT_IDT77252 -#define PCI_DEVICE_ID_IDT_IDT77252 0x0003 -#endif /* PCI_DEVICE_ID_IDT_IDT772052 */ - - -#endif /* !(_IDT77252_H) */ diff --git a/drivers/atm/idt77252_tables.h b/drivers/atm/idt77252_tables.h deleted file mode 100644 index 12b81e046a7b..000000000000 --- a/drivers/atm/idt77252_tables.h +++ /dev/null @@ -1,781 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Do not edit, automatically generated by `./genrtbl'. - * - * Cell Line Rate: 353207.55 (155520000 bps) - */ - -static unsigned int log_to_rate[] = -{ -/* 000 */ 0x8d022e27, /* cps = 10.02, nrm = 3, interval = 35264.00 */ -/* 001 */ 0x8d362e11, /* cps = 10.42, nrm = 3, interval = 33856.00 */ -/* 002 */ 0x8d6e2bf8, /* cps = 10.86, nrm = 3, interval = 32512.00 */ -/* 003 */ 0x8da82bcf, /* cps = 11.31, nrm = 3, interval = 31200.00 */ -/* 004 */ 0x8de42ba8, /* cps = 11.78, nrm = 3, interval = 29952.00 */ -/* 005 */ 0x8e242b82, /* cps = 12.28, nrm = 3, interval = 28736.00 */ -/* 006 */ 0x8e662b5e, /* cps = 12.80, nrm = 3, interval = 27584.00 */ -/* 007 */ 0x8eaa2b3c, /* cps = 13.33, nrm = 3, interval = 26496.00 */ -/* 008 */ 0x8ef22b1a, /* cps = 13.89, nrm = 3, interval = 25408.00 */ -/* 009 */ 0x8f3e2afa, /* cps = 14.48, nrm = 3, interval = 24384.00 */ -/* 010 */ 0x8f8a2adc, /* cps = 15.08, nrm = 3, interval = 23424.00 */ -/* 011 */ 0x8fdc2abe, /* cps = 15.72, nrm = 3, interval = 22464.00 */ -/* 012 */ 0x90182aa2, /* cps = 16.38, nrm = 3, interval = 21568.00 */ -/* 013 */ 0x90422a87, /* cps = 17.03, nrm = 3, interval = 20704.00 */ -/* 014 */ 0x90702a6d, /* cps = 17.75, nrm = 3, interval = 19872.00 */ -/* 015 */ 0x90a02a54, /* cps = 18.50, nrm = 3, interval = 19072.00 */ -/* 016 */ 0x90d22a3c, /* cps = 19.28, nrm = 3, interval = 18304.00 */ -/* 017 */ 0x91062a25, /* cps = 20.09, nrm = 3, interval = 17568.00 */ -/* 018 */ 0x913c2a0f, /* cps = 20.94, nrm = 3, interval = 16864.00 */ -/* 019 */ 0x917427f3, /* cps = 21.81, nrm = 3, interval = 16176.00 */ -/* 020 */ 0x91b027ca, /* cps = 22.75, nrm = 3, interval = 15520.00 */ -/* 021 */ 0x91ec27a3, /* cps = 23.69, nrm = 3, interval = 14896.00 */ -/* 022 */ 0x922c277e, /* cps = 24.69, nrm = 3, interval = 14304.00 */ -/* 023 */ 0x926e275a, /* cps = 25.72, nrm = 3, interval = 13728.00 */ -/* 024 */ 0x92b42737, /* cps = 26.81, nrm = 3, interval = 13168.00 */ -/* 025 */ 0x92fc2716, /* cps = 27.94, nrm = 3, interval = 12640.00 */ -/* 026 */ 0x934626f6, /* cps = 29.09, nrm = 3, interval = 12128.00 */ -/* 027 */ 0x939426d8, /* cps = 30.31, nrm = 3, interval = 11648.00 */ -/* 028 */ 0x93e426bb, /* cps = 31.56, nrm = 3, interval = 11184.00 */ -/* 029 */ 0x941e269e, /* cps = 32.94, nrm = 3, interval = 10720.00 */ -/* 030 */ 0x944a2683, /* cps = 34.31, nrm = 3, interval = 10288.00 */ -/* 031 */ 0x9476266a, /* cps = 35.69, nrm = 3, interval = 9888.00 */ -/* 032 */ 0x94a62651, /* cps = 37.19, nrm = 3, interval = 9488.00 */ -/* 033 */ 0x94d82639, /* cps = 38.75, nrm = 3, interval = 9104.00 */ -/* 034 */ 0x950c6622, /* cps = 40.38, nrm = 4, interval = 8736.00 */ -/* 035 */ 0x9544660c, /* cps = 42.12, nrm = 4, interval = 8384.00 */ -/* 036 */ 0x957c63ee, /* cps = 43.88, nrm = 4, interval = 8048.00 */ -/* 037 */ 0x95b663c6, /* cps = 45.69, nrm = 4, interval = 7728.00 */ -/* 038 */ 0x95f4639f, /* cps = 47.62, nrm = 4, interval = 7416.00 */ -/* 039 */ 0x96346379, /* cps = 49.62, nrm = 4, interval = 7112.00 */ -/* 040 */ 0x96766356, /* cps = 51.69, nrm = 4, interval = 6832.00 */ -/* 041 */ 0x96bc6333, /* cps = 53.88, nrm = 4, interval = 6552.00 */ -/* 042 */ 0x97046312, /* cps = 56.12, nrm = 4, interval = 6288.00 */ -/* 043 */ 0x974e62f3, /* cps = 58.44, nrm = 4, interval = 6040.00 */ -/* 044 */ 0x979e62d4, /* cps = 60.94, nrm = 4, interval = 5792.00 */ -/* 045 */ 0x97f062b7, /* cps = 63.50, nrm = 4, interval = 5560.00 */ -/* 046 */ 0x9822629b, /* cps = 66.12, nrm = 4, interval = 5336.00 */ -/* 047 */ 0x984e6280, /* cps = 68.88, nrm = 4, interval = 5120.00 */ -/* 048 */ 0x987e6266, /* cps = 71.88, nrm = 4, interval = 4912.00 */ -/* 049 */ 0x98ac624e, /* cps = 74.75, nrm = 4, interval = 4720.00 */ -/* 050 */ 0x98e06236, /* cps = 78.00, nrm = 4, interval = 4528.00 */ -/* 051 */ 0x9914a21f, /* cps = 81.25, nrm = 8, interval = 4344.00 */ -/* 052 */ 0x994aa209, /* cps = 84.62, nrm = 8, interval = 4168.00 */ -/* 053 */ 0x99829fe9, /* cps = 88.12, nrm = 8, interval = 4004.00 */ -/* 054 */ 0x99be9fc1, /* cps = 91.88, nrm = 8, interval = 3844.00 */ -/* 055 */ 0x99fc9f9a, /* cps = 95.75, nrm = 8, interval = 3688.00 */ -/* 056 */ 0x9a3c9f75, /* cps = 99.75, nrm = 8, interval = 3540.00 */ -/* 057 */ 0x9a809f51, /* cps = 104.00, nrm = 8, interval = 3396.00 */ -/* 058 */ 0x9ac49f2f, /* cps = 108.25, nrm = 8, interval = 3260.00 */ -/* 059 */ 0x9b0e9f0e, /* cps = 112.88, nrm = 8, interval = 3128.00 */ -/* 060 */ 0x9b589eef, /* cps = 117.50, nrm = 8, interval = 3004.00 */ -/* 061 */ 0x9ba69ed1, /* cps = 122.38, nrm = 8, interval = 2884.00 */ -/* 062 */ 0x9bf89eb4, /* cps = 127.50, nrm = 8, interval = 2768.00 */ -/* 063 */ 0x9c269e98, /* cps = 132.75, nrm = 8, interval = 2656.00 */ -/* 064 */ 0x9c549e7d, /* cps = 138.50, nrm = 8, interval = 2548.00 */ -/* 065 */ 0x9c849e63, /* cps = 144.50, nrm = 8, interval = 2444.00 */ -/* 066 */ 0x9cb29e4b, /* cps = 150.25, nrm = 8, interval = 2348.00 */ -/* 067 */ 0x9ce69e33, /* cps = 156.75, nrm = 8, interval = 2252.00 */ -/* 068 */ 0x9d1cde1c, /* cps = 163.50, nrm = 16, interval = 2160.00 */ -/* 069 */ 0x9d50de07, /* cps = 170.00, nrm = 16, interval = 2076.00 */ -/* 070 */ 0x9d8adbe4, /* cps = 177.25, nrm = 16, interval = 1992.00 */ -/* 071 */ 0x9dc4dbbc, /* cps = 184.50, nrm = 16, interval = 1912.00 */ -/* 072 */ 0x9e02db96, /* cps = 192.25, nrm = 16, interval = 1836.00 */ -/* 073 */ 0x9e42db71, /* cps = 200.25, nrm = 16, interval = 1762.00 */ -/* 074 */ 0x9e86db4d, /* cps = 208.75, nrm = 16, interval = 1690.00 */ -/* 075 */ 0x9ecedb2b, /* cps = 217.75, nrm = 16, interval = 1622.00 */ -/* 076 */ 0x9f16db0a, /* cps = 226.75, nrm = 16, interval = 1556.00 */ -/* 077 */ 0x9f62daeb, /* cps = 236.25, nrm = 16, interval = 1494.00 */ -/* 078 */ 0x9fb2dacd, /* cps = 246.25, nrm = 16, interval = 1434.00 */ -/* 079 */ 0xa002dab0, /* cps = 256.50, nrm = 16, interval = 1376.00 */ -/* 080 */ 0xa02eda94, /* cps = 267.50, nrm = 16, interval = 1320.00 */ -/* 081 */ 0xa05ada7a, /* cps = 278.50, nrm = 16, interval = 1268.00 */ -/* 082 */ 0xa088da60, /* cps = 290.00, nrm = 16, interval = 1216.00 */ -/* 083 */ 0xa0b8da48, /* cps = 302.00, nrm = 16, interval = 1168.00 */ -/* 084 */ 0xa0ecda30, /* cps = 315.00, nrm = 16, interval = 1120.00 */ -/* 085 */ 0xa1211a1a, /* cps = 328.00, nrm = 32, interval = 1076.00 */ -/* 086 */ 0xa1591a04, /* cps = 342.00, nrm = 32, interval = 1032.00 */ -/* 087 */ 0xa19117df, /* cps = 356.00, nrm = 32, interval = 991.00 */ -/* 088 */ 0xa1cd17b7, /* cps = 371.00, nrm = 32, interval = 951.00 */ -/* 089 */ 0xa20b1791, /* cps = 386.50, nrm = 32, interval = 913.00 */ -/* 090 */ 0xa24d176c, /* cps = 403.00, nrm = 32, interval = 876.00 */ -/* 091 */ 0xa28f1749, /* cps = 419.50, nrm = 32, interval = 841.00 */ -/* 092 */ 0xa2d71727, /* cps = 437.50, nrm = 32, interval = 807.00 */ -/* 093 */ 0xa31f1707, /* cps = 455.50, nrm = 32, interval = 775.00 */ -/* 094 */ 0xa36d16e7, /* cps = 475.00, nrm = 32, interval = 743.00 */ -/* 095 */ 0xa3bd16c9, /* cps = 495.00, nrm = 32, interval = 713.00 */ -/* 096 */ 0xa40716ad, /* cps = 515.00, nrm = 32, interval = 685.00 */ -/* 097 */ 0xa4331691, /* cps = 537.00, nrm = 32, interval = 657.00 */ -/* 098 */ 0xa45f1677, /* cps = 559.00, nrm = 32, interval = 631.00 */ -/* 099 */ 0xa48f165d, /* cps = 583.00, nrm = 32, interval = 605.00 */ -/* 100 */ 0xa4bf1645, /* cps = 607.00, nrm = 32, interval = 581.00 */ -/* 101 */ 0xa4f1162e, /* cps = 632.00, nrm = 32, interval = 558.00 */ -/* 102 */ 0xa5291617, /* cps = 660.00, nrm = 32, interval = 535.00 */ -/* 103 */ 0xa55f1602, /* cps = 687.00, nrm = 32, interval = 514.00 */ -/* 104 */ 0xa59913da, /* cps = 716.00, nrm = 32, interval = 493.00 */ -/* 105 */ 0xa5d513b2, /* cps = 746.00, nrm = 32, interval = 473.00 */ -/* 106 */ 0xa613138c, /* cps = 777.00, nrm = 32, interval = 454.00 */ -/* 107 */ 0xa6551368, /* cps = 810.00, nrm = 32, interval = 436.00 */ -/* 108 */ 0xa6971345, /* cps = 843.00, nrm = 32, interval = 418.50 */ -/* 109 */ 0xa6df1323, /* cps = 879.00, nrm = 32, interval = 401.50 */ -/* 110 */ 0xa7291303, /* cps = 916.00, nrm = 32, interval = 385.50 */ -/* 111 */ 0xa77512e4, /* cps = 954.00, nrm = 32, interval = 370.00 */ -/* 112 */ 0xa7c512c6, /* cps = 994.00, nrm = 32, interval = 355.00 */ -/* 113 */ 0xa80d12a9, /* cps = 1036.00, nrm = 32, interval = 340.50 */ -/* 114 */ 0xa839128e, /* cps = 1080.00, nrm = 32, interval = 327.00 */ -/* 115 */ 0xa8651274, /* cps = 1124.00, nrm = 32, interval = 314.00 */ -/* 116 */ 0xa895125a, /* cps = 1172.00, nrm = 32, interval = 301.00 */ -/* 117 */ 0xa8c71242, /* cps = 1222.00, nrm = 32, interval = 289.00 */ -/* 118 */ 0xa8f9122b, /* cps = 1272.00, nrm = 32, interval = 277.50 */ -/* 119 */ 0xa92f1214, /* cps = 1326.00, nrm = 32, interval = 266.00 */ -/* 120 */ 0xa9670ffe, /* cps = 1382.00, nrm = 32, interval = 255.50 */ -/* 121 */ 0xa9a10fd5, /* cps = 1440.00, nrm = 32, interval = 245.25 */ -/* 122 */ 0xa9db0fae, /* cps = 1498.00, nrm = 32, interval = 235.50 */ -/* 123 */ 0xaa1b0f88, /* cps = 1562.00, nrm = 32, interval = 226.00 */ -/* 124 */ 0xaa5d0f63, /* cps = 1628.00, nrm = 32, interval = 216.75 */ -/* 125 */ 0xaaa10f41, /* cps = 1696.00, nrm = 32, interval = 208.25 */ -/* 126 */ 0xaae90f1f, /* cps = 1768.00, nrm = 32, interval = 199.75 */ -/* 127 */ 0xab330eff, /* cps = 1842.00, nrm = 32, interval = 191.75 */ -/* 128 */ 0xab7f0ee0, /* cps = 1918.00, nrm = 32, interval = 184.00 */ -/* 129 */ 0xabd10ec2, /* cps = 2000.00, nrm = 32, interval = 176.50 */ -/* 130 */ 0xac110ea6, /* cps = 2080.00, nrm = 32, interval = 169.50 */ -/* 131 */ 0xac3d0e8b, /* cps = 2168.00, nrm = 32, interval = 162.75 */ -/* 132 */ 0xac6d0e70, /* cps = 2264.00, nrm = 32, interval = 156.00 */ -/* 133 */ 0xac9b0e57, /* cps = 2356.00, nrm = 32, interval = 149.75 */ -/* 134 */ 0xaccd0e3f, /* cps = 2456.00, nrm = 32, interval = 143.75 */ -/* 135 */ 0xacff0e28, /* cps = 2556.00, nrm = 32, interval = 138.00 */ -/* 136 */ 0xad350e12, /* cps = 2664.00, nrm = 32, interval = 132.50 */ -/* 137 */ 0xad6d0bf9, /* cps = 2776.00, nrm = 32, interval = 127.12 */ -/* 138 */ 0xada70bd0, /* cps = 2892.00, nrm = 32, interval = 122.00 */ -/* 139 */ 0xade30ba9, /* cps = 3012.00, nrm = 32, interval = 117.12 */ -/* 140 */ 0xae230b83, /* cps = 3140.00, nrm = 32, interval = 112.38 */ -/* 141 */ 0xae650b5f, /* cps = 3272.00, nrm = 32, interval = 107.88 */ -/* 142 */ 0xaeab0b3c, /* cps = 3412.00, nrm = 32, interval = 103.50 */ -/* 143 */ 0xaef10b1b, /* cps = 3552.00, nrm = 32, interval = 99.38 */ -/* 144 */ 0xaf3b0afb, /* cps = 3700.00, nrm = 32, interval = 95.38 */ -/* 145 */ 0xaf8b0adc, /* cps = 3860.00, nrm = 32, interval = 91.50 */ -/* 146 */ 0xafd90abf, /* cps = 4016.00, nrm = 32, interval = 87.88 */ -/* 147 */ 0xb0170aa3, /* cps = 4184.00, nrm = 32, interval = 84.38 */ -/* 148 */ 0xb0430a87, /* cps = 4360.00, nrm = 32, interval = 80.88 */ -/* 149 */ 0xb0710a6d, /* cps = 4544.00, nrm = 32, interval = 77.62 */ -/* 150 */ 0xb0a10a54, /* cps = 4736.00, nrm = 32, interval = 74.50 */ -/* 151 */ 0xb0d30a3c, /* cps = 4936.00, nrm = 32, interval = 71.50 */ -/* 152 */ 0xb1070a25, /* cps = 5144.00, nrm = 32, interval = 68.62 */ -/* 153 */ 0xb13d0a0f, /* cps = 5360.00, nrm = 32, interval = 65.88 */ -/* 154 */ 0xb17507f4, /* cps = 5584.00, nrm = 32, interval = 63.25 */ -/* 155 */ 0xb1af07cb, /* cps = 5816.00, nrm = 32, interval = 60.69 */ -/* 156 */ 0xb1eb07a4, /* cps = 6056.00, nrm = 32, interval = 58.25 */ -/* 157 */ 0xb22b077f, /* cps = 6312.00, nrm = 32, interval = 55.94 */ -/* 158 */ 0xb26d075b, /* cps = 6576.00, nrm = 32, interval = 53.69 */ -/* 159 */ 0xb2b30738, /* cps = 6856.00, nrm = 32, interval = 51.50 */ -/* 160 */ 0xb2fb0717, /* cps = 7144.00, nrm = 32, interval = 49.44 */ -/* 161 */ 0xb34506f7, /* cps = 7440.00, nrm = 32, interval = 47.44 */ -/* 162 */ 0xb39306d9, /* cps = 7752.00, nrm = 32, interval = 45.56 */ -/* 163 */ 0xb3e506bb, /* cps = 8080.00, nrm = 32, interval = 43.69 */ -/* 164 */ 0xb41d069f, /* cps = 8416.00, nrm = 32, interval = 41.94 */ -/* 165 */ 0xb4490684, /* cps = 8768.00, nrm = 32, interval = 40.25 */ -/* 166 */ 0xb477066a, /* cps = 9136.00, nrm = 32, interval = 38.62 */ -/* 167 */ 0xb4a70651, /* cps = 9520.00, nrm = 32, interval = 37.06 */ -/* 168 */ 0xb4d90639, /* cps = 9920.00, nrm = 32, interval = 35.56 */ -/* 169 */ 0xb50d0622, /* cps = 10336.00, nrm = 32, interval = 34.12 */ -/* 170 */ 0xb545060c, /* cps = 10784.00, nrm = 32, interval = 32.75 */ -/* 171 */ 0xb57b03ef, /* cps = 11216.00, nrm = 32, interval = 31.47 */ -/* 172 */ 0xb5b503c7, /* cps = 11680.00, nrm = 32, interval = 30.22 */ -/* 173 */ 0xb5f303a0, /* cps = 12176.00, nrm = 32, interval = 29.00 */ -/* 174 */ 0xb633037a, /* cps = 12688.00, nrm = 32, interval = 27.81 */ -/* 175 */ 0xb6750357, /* cps = 13216.00, nrm = 32, interval = 26.72 */ -/* 176 */ 0xb6bb0334, /* cps = 13776.00, nrm = 32, interval = 25.62 */ -/* 177 */ 0xb7030313, /* cps = 14352.00, nrm = 32, interval = 24.59 */ -/* 178 */ 0xb74f02f3, /* cps = 14960.00, nrm = 32, interval = 23.59 */ -/* 179 */ 0xb79d02d5, /* cps = 15584.00, nrm = 32, interval = 22.66 */ -/* 180 */ 0xb7ed02b8, /* cps = 16224.00, nrm = 32, interval = 21.75 */ -/* 181 */ 0xb821029c, /* cps = 16896.00, nrm = 32, interval = 20.88 */ -/* 182 */ 0xb84f0281, /* cps = 17632.00, nrm = 32, interval = 20.03 */ -/* 183 */ 0xb87d0267, /* cps = 18368.00, nrm = 32, interval = 19.22 */ -/* 184 */ 0xb8ad024e, /* cps = 19136.00, nrm = 32, interval = 18.44 */ -/* 185 */ 0xb8dd0237, /* cps = 19904.00, nrm = 32, interval = 17.72 */ -/* 186 */ 0xb9130220, /* cps = 20768.00, nrm = 32, interval = 17.00 */ -/* 187 */ 0xb949020a, /* cps = 21632.00, nrm = 32, interval = 16.31 */ -/* 188 */ 0xb98301f5, /* cps = 22560.00, nrm = 32, interval = 15.66 */ -/* 189 */ 0xb9bd01e1, /* cps = 23488.00, nrm = 32, interval = 15.03 */ -/* 190 */ 0xb9fd01cd, /* cps = 24512.00, nrm = 32, interval = 14.41 */ -/* 191 */ 0xba3b01bb, /* cps = 25504.00, nrm = 32, interval = 13.84 */ -/* 192 */ 0xba7f01a9, /* cps = 26592.00, nrm = 32, interval = 13.28 */ -/* 193 */ 0xbac30198, /* cps = 27680.00, nrm = 32, interval = 12.75 */ -/* 194 */ 0xbb0f0187, /* cps = 28896.00, nrm = 32, interval = 12.22 */ -/* 195 */ 0xbb570178, /* cps = 30048.00, nrm = 32, interval = 11.75 */ -/* 196 */ 0xbbab0168, /* cps = 31392.00, nrm = 32, interval = 11.25 */ -/* 197 */ 0xbbf9015a, /* cps = 32640.00, nrm = 32, interval = 10.81 */ -/* 198 */ 0xbc27014c, /* cps = 33984.00, nrm = 32, interval = 10.38 */ -/* 199 */ 0xbc53013f, /* cps = 35392.00, nrm = 32, interval = 9.97 */ -/* 200 */ 0xbc830132, /* cps = 36928.00, nrm = 32, interval = 9.56 */ -/* 201 */ 0xbcb50125, /* cps = 38528.00, nrm = 32, interval = 9.16 */ -/* 202 */ 0xbce5011a, /* cps = 40064.00, nrm = 32, interval = 8.81 */ -/* 203 */ 0xbd1d010e, /* cps = 41856.00, nrm = 32, interval = 8.44 */ -/* 204 */ 0xbd530103, /* cps = 43584.00, nrm = 32, interval = 8.09 */ -/* 205 */ 0xbd8b00f9, /* cps = 45376.00, nrm = 32, interval = 7.78 */ -/* 206 */ 0xbdc500ef, /* cps = 47232.00, nrm = 32, interval = 7.47 */ -/* 207 */ 0xbe0700e5, /* cps = 49344.00, nrm = 32, interval = 7.16 */ -/* 208 */ 0xbe4500dc, /* cps = 51328.00, nrm = 32, interval = 6.88 */ -/* 209 */ 0xbe8900d3, /* cps = 53504.00, nrm = 32, interval = 6.59 */ -/* 210 */ 0xbecb00cb, /* cps = 55616.00, nrm = 32, interval = 6.34 */ -/* 211 */ 0xbf1d00c2, /* cps = 58240.00, nrm = 32, interval = 6.06 */ -/* 212 */ 0xbf6100bb, /* cps = 60416.00, nrm = 32, interval = 5.84 */ -/* 213 */ 0xbfb500b3, /* cps = 63104.00, nrm = 32, interval = 5.59 */ -/* 214 */ 0xc00300ac, /* cps = 65664.00, nrm = 32, interval = 5.38 */ -/* 215 */ 0xc02f00a5, /* cps = 68480.00, nrm = 32, interval = 5.16 */ -/* 216 */ 0xc05d009e, /* cps = 71424.00, nrm = 32, interval = 4.94 */ -/* 217 */ 0xc0890098, /* cps = 74240.00, nrm = 32, interval = 4.75 */ -/* 218 */ 0xc0b90092, /* cps = 77312.00, nrm = 32, interval = 4.56 */ -/* 219 */ 0xc0ed008c, /* cps = 80640.00, nrm = 32, interval = 4.38 */ -/* 220 */ 0xc1250086, /* cps = 84224.00, nrm = 32, interval = 4.19 */ -/* 221 */ 0xc1590081, /* cps = 87552.00, nrm = 32, interval = 4.03 */ -/* 222 */ 0xc191007c, /* cps = 91136.00, nrm = 32, interval = 3.88 */ -/* 223 */ 0xc1cd0077, /* cps = 94976.00, nrm = 32, interval = 3.72 */ -/* 224 */ 0xc20d0072, /* cps = 99072.00, nrm = 32, interval = 3.56 */ -/* 225 */ 0xc255006d, /* cps = 103680.00, nrm = 32, interval = 3.41 */ -/* 226 */ 0xc2910069, /* cps = 107520.00, nrm = 32, interval = 3.28 */ -/* 227 */ 0xc2d50065, /* cps = 111872.00, nrm = 32, interval = 3.16 */ -/* 228 */ 0xc32f0060, /* cps = 117632.00, nrm = 32, interval = 3.00 */ -/* 229 */ 0xc36b005d, /* cps = 121472.00, nrm = 32, interval = 2.91 */ -/* 230 */ 0xc3c10059, /* cps = 126976.00, nrm = 32, interval = 2.78 */ -/* 231 */ 0xc40f0055, /* cps = 132864.00, nrm = 32, interval = 2.66 */ -/* 232 */ 0xc4350052, /* cps = 137728.00, nrm = 32, interval = 2.56 */ -/* 233 */ 0xc46d004e, /* cps = 144896.00, nrm = 32, interval = 2.44 */ -/* 234 */ 0xc499004b, /* cps = 150528.00, nrm = 32, interval = 2.34 */ -/* 235 */ 0xc4cb0048, /* cps = 156928.00, nrm = 32, interval = 2.25 */ -/* 236 */ 0xc4ff0045, /* cps = 163584.00, nrm = 32, interval = 2.16 */ -/* 237 */ 0xc5250043, /* cps = 168448.00, nrm = 32, interval = 2.09 */ -/* 238 */ 0xc5630040, /* cps = 176384.00, nrm = 32, interval = 2.00 */ -/* 239 */ 0xc5a7003d, /* cps = 185088.00, nrm = 32, interval = 1.91 */ -/* 240 */ 0xc5d9003b, /* cps = 191488.00, nrm = 32, interval = 1.84 */ -/* 241 */ 0xc6290038, /* cps = 201728.00, nrm = 32, interval = 1.75 */ -/* 242 */ 0xc6630036, /* cps = 209152.00, nrm = 32, interval = 1.69 */ -/* 243 */ 0xc6a30034, /* cps = 217344.00, nrm = 32, interval = 1.62 */ -/* 244 */ 0xc6e70032, /* cps = 226048.00, nrm = 32, interval = 1.56 */ -/* 245 */ 0xc72f0030, /* cps = 235264.00, nrm = 32, interval = 1.50 */ -/* 246 */ 0xc77f002e, /* cps = 245504.00, nrm = 32, interval = 1.44 */ -/* 247 */ 0xc7d7002c, /* cps = 256768.00, nrm = 32, interval = 1.38 */ -/* 248 */ 0xc81b002a, /* cps = 268800.00, nrm = 32, interval = 1.31 */ -/* 249 */ 0xc84f0028, /* cps = 282112.00, nrm = 32, interval = 1.25 */ -/* 250 */ 0xc86d0027, /* cps = 289792.00, nrm = 32, interval = 1.22 */ -/* 251 */ 0xc8a90025, /* cps = 305152.00, nrm = 32, interval = 1.16 */ -/* 252 */ 0xc8cb0024, /* cps = 313856.00, nrm = 32, interval = 1.12 */ -/* 253 */ 0xc9130022, /* cps = 332288.00, nrm = 32, interval = 1.06 */ -/* 254 */ 0xc9390021, /* cps = 342016.00, nrm = 32, interval = 1.03 */ -/* 255 */ 0xc9630020, /* cps = 352768.00, nrm = 32, interval = 1.00 */ -}; - -static unsigned char rate_to_log[] = -{ -/* 1.00 => 0 */ 0x00, /* => 10.02 */ -/* 1.06 => 0 */ 0x00, /* => 10.02 */ -/* 1.12 => 0 */ 0x00, /* => 10.02 */ -/* 1.19 => 0 */ 0x00, /* => 10.02 */ -/* 1.25 => 0 */ 0x00, /* => 10.02 */ -/* 1.31 => 0 */ 0x00, /* => 10.02 */ -/* 1.38 => 0 */ 0x00, /* => 10.02 */ -/* 1.44 => 0 */ 0x00, /* => 10.02 */ -/* 1.50 => 0 */ 0x00, /* => 10.02 */ -/* 1.56 => 0 */ 0x00, /* => 10.02 */ -/* 1.62 => 0 */ 0x00, /* => 10.02 */ -/* 1.69 => 0 */ 0x00, /* => 10.02 */ -/* 1.75 => 0 */ 0x00, /* => 10.02 */ -/* 1.81 => 0 */ 0x00, /* => 10.02 */ -/* 1.88 => 0 */ 0x00, /* => 10.02 */ -/* 1.94 => 0 */ 0x00, /* => 10.02 */ -/* 2.00 => 0 */ 0x00, /* => 10.02 */ -/* 2.12 => 0 */ 0x00, /* => 10.02 */ -/* 2.25 => 0 */ 0x00, /* => 10.02 */ -/* 2.38 => 0 */ 0x00, /* => 10.02 */ -/* 2.50 => 0 */ 0x00, /* => 10.02 */ -/* 2.62 => 0 */ 0x00, /* => 10.02 */ -/* 2.75 => 0 */ 0x00, /* => 10.02 */ -/* 2.88 => 0 */ 0x00, /* => 10.02 */ -/* 3.00 => 0 */ 0x00, /* => 10.02 */ -/* 3.12 => 0 */ 0x00, /* => 10.02 */ -/* 3.25 => 0 */ 0x00, /* => 10.02 */ -/* 3.38 => 0 */ 0x00, /* => 10.02 */ -/* 3.50 => 0 */ 0x00, /* => 10.02 */ -/* 3.62 => 0 */ 0x00, /* => 10.02 */ -/* 3.75 => 0 */ 0x00, /* => 10.02 */ -/* 3.88 => 0 */ 0x00, /* => 10.02 */ -/* 4.00 => 0 */ 0x00, /* => 10.02 */ -/* 4.25 => 0 */ 0x00, /* => 10.02 */ -/* 4.50 => 0 */ 0x00, /* => 10.02 */ -/* 4.75 => 0 */ 0x00, /* => 10.02 */ -/* 5.00 => 0 */ 0x00, /* => 10.02 */ -/* 5.25 => 0 */ 0x00, /* => 10.02 */ -/* 5.50 => 0 */ 0x00, /* => 10.02 */ -/* 5.75 => 0 */ 0x00, /* => 10.02 */ -/* 6.00 => 0 */ 0x00, /* => 10.02 */ -/* 6.25 => 0 */ 0x00, /* => 10.02 */ -/* 6.50 => 0 */ 0x00, /* => 10.02 */ -/* 6.75 => 0 */ 0x00, /* => 10.02 */ -/* 7.00 => 0 */ 0x00, /* => 10.02 */ -/* 7.25 => 0 */ 0x00, /* => 10.02 */ -/* 7.50 => 0 */ 0x00, /* => 10.02 */ -/* 7.75 => 0 */ 0x00, /* => 10.02 */ -/* 8.00 => 0 */ 0x00, /* => 10.02 */ -/* 8.50 => 0 */ 0x00, /* => 10.02 */ -/* 9.00 => 0 */ 0x00, /* => 10.02 */ -/* 9.50 => 0 */ 0x00, /* => 10.02 */ -/* 10.00 => 0 */ 0x00, /* => 10.02 */ -/* 10.50 => 1 */ 0x01, /* => 10.42 */ -/* 11.00 => 2 */ 0x02, /* => 10.86 */ -/* 11.50 => 3 */ 0x03, /* => 11.31 */ -/* 12.00 => 4 */ 0x04, /* => 11.78 */ -/* 12.50 => 5 */ 0x05, /* => 12.28 */ -/* 13.00 => 6 */ 0x06, /* => 12.80 */ -/* 13.50 => 7 */ 0x07, /* => 13.33 */ -/* 14.00 => 8 */ 0x08, /* => 13.89 */ -/* 14.50 => 9 */ 0x09, /* => 14.48 */ -/* 15.00 => 9 */ 0x09, /* => 14.48 */ -/* 15.50 => 10 */ 0x0a, /* => 15.08 */ -/* 16.00 => 11 */ 0x0b, /* => 15.72 */ -/* 17.00 => 12 */ 0x0c, /* => 16.38 */ -/* 18.00 => 14 */ 0x0e, /* => 17.75 */ -/* 19.00 => 15 */ 0x0f, /* => 18.50 */ -/* 20.00 => 16 */ 0x10, /* => 19.28 */ -/* 21.00 => 18 */ 0x12, /* => 20.94 */ -/* 22.00 => 19 */ 0x13, /* => 21.81 */ -/* 23.00 => 20 */ 0x14, /* => 22.75 */ -/* 24.00 => 21 */ 0x15, /* => 23.69 */ -/* 25.00 => 22 */ 0x16, /* => 24.69 */ -/* 26.00 => 23 */ 0x17, /* => 25.72 */ -/* 27.00 => 24 */ 0x18, /* => 26.81 */ -/* 28.00 => 25 */ 0x19, /* => 27.94 */ -/* 29.00 => 25 */ 0x19, /* => 27.94 */ -/* 30.00 => 26 */ 0x1a, /* => 29.09 */ -/* 31.00 => 27 */ 0x1b, /* => 30.31 */ -/* 32.00 => 28 */ 0x1c, /* => 31.56 */ -/* 34.00 => 29 */ 0x1d, /* => 32.94 */ -/* 36.00 => 31 */ 0x1f, /* => 35.69 */ -/* 38.00 => 32 */ 0x20, /* => 37.19 */ -/* 40.00 => 33 */ 0x21, /* => 38.75 */ -/* 42.00 => 34 */ 0x22, /* => 40.38 */ -/* 44.00 => 36 */ 0x24, /* => 43.88 */ -/* 46.00 => 37 */ 0x25, /* => 45.69 */ -/* 48.00 => 38 */ 0x26, /* => 47.62 */ -/* 50.00 => 39 */ 0x27, /* => 49.62 */ -/* 52.00 => 40 */ 0x28, /* => 51.69 */ -/* 54.00 => 41 */ 0x29, /* => 53.88 */ -/* 56.00 => 41 */ 0x29, /* => 53.88 */ -/* 58.00 => 42 */ 0x2a, /* => 56.12 */ -/* 60.00 => 43 */ 0x2b, /* => 58.44 */ -/* 62.00 => 44 */ 0x2c, /* => 60.94 */ -/* 64.00 => 45 */ 0x2d, /* => 63.50 */ -/* 68.00 => 46 */ 0x2e, /* => 66.12 */ -/* 72.00 => 48 */ 0x30, /* => 71.88 */ -/* 76.00 => 49 */ 0x31, /* => 74.75 */ -/* 80.00 => 50 */ 0x32, /* => 78.00 */ -/* 84.00 => 51 */ 0x33, /* => 81.25 */ -/* 88.00 => 52 */ 0x34, /* => 84.62 */ -/* 92.00 => 54 */ 0x36, /* => 91.88 */ -/* 96.00 => 55 */ 0x37, /* => 95.75 */ -/* 100.00 => 56 */ 0x38, /* => 99.75 */ -/* 104.00 => 56 */ 0x38, /* => 99.75 */ -/* 108.00 => 57 */ 0x39, /* => 104.00 */ -/* 112.00 => 58 */ 0x3a, /* => 108.25 */ -/* 116.00 => 59 */ 0x3b, /* => 112.88 */ -/* 120.00 => 60 */ 0x3c, /* => 117.50 */ -/* 124.00 => 61 */ 0x3d, /* => 122.38 */ -/* 128.00 => 62 */ 0x3e, /* => 127.50 */ -/* 136.00 => 63 */ 0x3f, /* => 132.75 */ -/* 144.00 => 64 */ 0x40, /* => 138.50 */ -/* 152.00 => 66 */ 0x42, /* => 150.25 */ -/* 160.00 => 67 */ 0x43, /* => 156.75 */ -/* 168.00 => 68 */ 0x44, /* => 163.50 */ -/* 176.00 => 69 */ 0x45, /* => 170.00 */ -/* 184.00 => 70 */ 0x46, /* => 177.25 */ -/* 192.00 => 71 */ 0x47, /* => 184.50 */ -/* 200.00 => 72 */ 0x48, /* => 192.25 */ -/* 208.00 => 73 */ 0x49, /* => 200.25 */ -/* 216.00 => 74 */ 0x4a, /* => 208.75 */ -/* 224.00 => 75 */ 0x4b, /* => 217.75 */ -/* 232.00 => 76 */ 0x4c, /* => 226.75 */ -/* 240.00 => 77 */ 0x4d, /* => 236.25 */ -/* 248.00 => 78 */ 0x4e, /* => 246.25 */ -/* 256.00 => 78 */ 0x4e, /* => 246.25 */ -/* 272.00 => 80 */ 0x50, /* => 267.50 */ -/* 288.00 => 81 */ 0x51, /* => 278.50 */ -/* 304.00 => 83 */ 0x53, /* => 302.00 */ -/* 320.00 => 84 */ 0x54, /* => 315.00 */ -/* 336.00 => 85 */ 0x55, /* => 328.00 */ -/* 352.00 => 86 */ 0x56, /* => 342.00 */ -/* 368.00 => 87 */ 0x57, /* => 356.00 */ -/* 384.00 => 88 */ 0x58, /* => 371.00 */ -/* 400.00 => 89 */ 0x59, /* => 386.50 */ -/* 416.00 => 90 */ 0x5a, /* => 403.00 */ -/* 432.00 => 91 */ 0x5b, /* => 419.50 */ -/* 448.00 => 92 */ 0x5c, /* => 437.50 */ -/* 464.00 => 93 */ 0x5d, /* => 455.50 */ -/* 480.00 => 94 */ 0x5e, /* => 475.00 */ -/* 496.00 => 95 */ 0x5f, /* => 495.00 */ -/* 512.00 => 95 */ 0x5f, /* => 495.00 */ -/* 544.00 => 97 */ 0x61, /* => 537.00 */ -/* 576.00 => 98 */ 0x62, /* => 559.00 */ -/* 608.00 => 100 */ 0x64, /* => 607.00 */ -/* 640.00 => 101 */ 0x65, /* => 632.00 */ -/* 672.00 => 102 */ 0x66, /* => 660.00 */ -/* 704.00 => 103 */ 0x67, /* => 687.00 */ -/* 736.00 => 104 */ 0x68, /* => 716.00 */ -/* 768.00 => 105 */ 0x69, /* => 746.00 */ -/* 800.00 => 106 */ 0x6a, /* => 777.00 */ -/* 832.00 => 107 */ 0x6b, /* => 810.00 */ -/* 864.00 => 108 */ 0x6c, /* => 843.00 */ -/* 896.00 => 109 */ 0x6d, /* => 879.00 */ -/* 928.00 => 110 */ 0x6e, /* => 916.00 */ -/* 960.00 => 111 */ 0x6f, /* => 954.00 */ -/* 992.00 => 111 */ 0x6f, /* => 954.00 */ -/* 1024.00 => 112 */ 0x70, /* => 994.00 */ -/* 1088.00 => 114 */ 0x72, /* => 1080.00 */ -/* 1152.00 => 115 */ 0x73, /* => 1124.00 */ -/* 1216.00 => 116 */ 0x74, /* => 1172.00 */ -/* 1280.00 => 118 */ 0x76, /* => 1272.00 */ -/* 1344.00 => 119 */ 0x77, /* => 1326.00 */ -/* 1408.00 => 120 */ 0x78, /* => 1382.00 */ -/* 1472.00 => 121 */ 0x79, /* => 1440.00 */ -/* 1536.00 => 122 */ 0x7a, /* => 1498.00 */ -/* 1600.00 => 123 */ 0x7b, /* => 1562.00 */ -/* 1664.00 => 124 */ 0x7c, /* => 1628.00 */ -/* 1728.00 => 125 */ 0x7d, /* => 1696.00 */ -/* 1792.00 => 126 */ 0x7e, /* => 1768.00 */ -/* 1856.00 => 127 */ 0x7f, /* => 1842.00 */ -/* 1920.00 => 128 */ 0x80, /* => 1918.00 */ -/* 1984.00 => 128 */ 0x80, /* => 1918.00 */ -/* 2048.00 => 129 */ 0x81, /* => 2000.00 */ -/* 2176.00 => 131 */ 0x83, /* => 2168.00 */ -/* 2304.00 => 132 */ 0x84, /* => 2264.00 */ -/* 2432.00 => 133 */ 0x85, /* => 2356.00 */ -/* 2560.00 => 135 */ 0x87, /* => 2556.00 */ -/* 2688.00 => 136 */ 0x88, /* => 2664.00 */ -/* 2816.00 => 137 */ 0x89, /* => 2776.00 */ -/* 2944.00 => 138 */ 0x8a, /* => 2892.00 */ -/* 3072.00 => 139 */ 0x8b, /* => 3012.00 */ -/* 3200.00 => 140 */ 0x8c, /* => 3140.00 */ -/* 3328.00 => 141 */ 0x8d, /* => 3272.00 */ -/* 3456.00 => 142 */ 0x8e, /* => 3412.00 */ -/* 3584.00 => 143 */ 0x8f, /* => 3552.00 */ -/* 3712.00 => 144 */ 0x90, /* => 3700.00 */ -/* 3840.00 => 144 */ 0x90, /* => 3700.00 */ -/* 3968.00 => 145 */ 0x91, /* => 3860.00 */ -/* 4096.00 => 146 */ 0x92, /* => 4016.00 */ -/* 4352.00 => 147 */ 0x93, /* => 4184.00 */ -/* 4608.00 => 149 */ 0x95, /* => 4544.00 */ -/* 4864.00 => 150 */ 0x96, /* => 4736.00 */ -/* 5120.00 => 151 */ 0x97, /* => 4936.00 */ -/* 5376.00 => 153 */ 0x99, /* => 5360.00 */ -/* 5632.00 => 154 */ 0x9a, /* => 5584.00 */ -/* 5888.00 => 155 */ 0x9b, /* => 5816.00 */ -/* 6144.00 => 156 */ 0x9c, /* => 6056.00 */ -/* 6400.00 => 157 */ 0x9d, /* => 6312.00 */ -/* 6656.00 => 158 */ 0x9e, /* => 6576.00 */ -/* 6912.00 => 159 */ 0x9f, /* => 6856.00 */ -/* 7168.00 => 160 */ 0xa0, /* => 7144.00 */ -/* 7424.00 => 160 */ 0xa0, /* => 7144.00 */ -/* 7680.00 => 161 */ 0xa1, /* => 7440.00 */ -/* 7936.00 => 162 */ 0xa2, /* => 7752.00 */ -/* 8192.00 => 163 */ 0xa3, /* => 8080.00 */ -/* 8704.00 => 164 */ 0xa4, /* => 8416.00 */ -/* 9216.00 => 166 */ 0xa6, /* => 9136.00 */ -/* 9728.00 => 167 */ 0xa7, /* => 9520.00 */ -/* 10240.00 => 168 */ 0xa8, /* => 9920.00 */ -/* 10752.00 => 169 */ 0xa9, /* => 10336.00 */ -/* 11264.00 => 171 */ 0xab, /* => 11216.00 */ -/* 11776.00 => 172 */ 0xac, /* => 11680.00 */ -/* 12288.00 => 173 */ 0xad, /* => 12176.00 */ -/* 12800.00 => 174 */ 0xae, /* => 12688.00 */ -/* 13312.00 => 175 */ 0xaf, /* => 13216.00 */ -/* 13824.00 => 176 */ 0xb0, /* => 13776.00 */ -/* 14336.00 => 176 */ 0xb0, /* => 13776.00 */ -/* 14848.00 => 177 */ 0xb1, /* => 14352.00 */ -/* 15360.00 => 178 */ 0xb2, /* => 14960.00 */ -/* 15872.00 => 179 */ 0xb3, /* => 15584.00 */ -/* 16384.00 => 180 */ 0xb4, /* => 16224.00 */ -/* 17408.00 => 181 */ 0xb5, /* => 16896.00 */ -/* 18432.00 => 183 */ 0xb7, /* => 18368.00 */ -/* 19456.00 => 184 */ 0xb8, /* => 19136.00 */ -/* 20480.00 => 185 */ 0xb9, /* => 19904.00 */ -/* 21504.00 => 186 */ 0xba, /* => 20768.00 */ -/* 22528.00 => 187 */ 0xbb, /* => 21632.00 */ -/* 23552.00 => 189 */ 0xbd, /* => 23488.00 */ -/* 24576.00 => 190 */ 0xbe, /* => 24512.00 */ -/* 25600.00 => 191 */ 0xbf, /* => 25504.00 */ -/* 26624.00 => 192 */ 0xc0, /* => 26592.00 */ -/* 27648.00 => 192 */ 0xc0, /* => 26592.00 */ -/* 28672.00 => 193 */ 0xc1, /* => 27680.00 */ -/* 29696.00 => 194 */ 0xc2, /* => 28896.00 */ -/* 30720.00 => 195 */ 0xc3, /* => 30048.00 */ -/* 31744.00 => 196 */ 0xc4, /* => 31392.00 */ -/* 32768.00 => 197 */ 0xc5, /* => 32640.00 */ -/* 34816.00 => 198 */ 0xc6, /* => 33984.00 */ -/* 36864.00 => 199 */ 0xc7, /* => 35392.00 */ -/* 38912.00 => 201 */ 0xc9, /* => 38528.00 */ -/* 40960.00 => 202 */ 0xca, /* => 40064.00 */ -/* 43008.00 => 203 */ 0xcb, /* => 41856.00 */ -/* 45056.00 => 204 */ 0xcc, /* => 43584.00 */ -/* 47104.00 => 205 */ 0xcd, /* => 45376.00 */ -/* 49152.00 => 206 */ 0xce, /* => 47232.00 */ -/* 51200.00 => 207 */ 0xcf, /* => 49344.00 */ -/* 53248.00 => 208 */ 0xd0, /* => 51328.00 */ -/* 55296.00 => 209 */ 0xd1, /* => 53504.00 */ -/* 57344.00 => 210 */ 0xd2, /* => 55616.00 */ -/* 59392.00 => 211 */ 0xd3, /* => 58240.00 */ -/* 61440.00 => 212 */ 0xd4, /* => 60416.00 */ -/* 63488.00 => 213 */ 0xd5, /* => 63104.00 */ -/* 65536.00 => 213 */ 0xd5, /* => 63104.00 */ -/* 69632.00 => 215 */ 0xd7, /* => 68480.00 */ -/* 73728.00 => 216 */ 0xd8, /* => 71424.00 */ -/* 77824.00 => 218 */ 0xda, /* => 77312.00 */ -/* 81920.00 => 219 */ 0xdb, /* => 80640.00 */ -/* 86016.00 => 220 */ 0xdc, /* => 84224.00 */ -/* 90112.00 => 221 */ 0xdd, /* => 87552.00 */ -/* 94208.00 => 222 */ 0xde, /* => 91136.00 */ -/* 98304.00 => 223 */ 0xdf, /* => 94976.00 */ -/* 102400.00 => 224 */ 0xe0, /* => 99072.00 */ -/* 106496.00 => 225 */ 0xe1, /* => 103680.00 */ -/* 110592.00 => 226 */ 0xe2, /* => 107520.00 */ -/* 114688.00 => 227 */ 0xe3, /* => 111872.00 */ -/* 118784.00 => 228 */ 0xe4, /* => 117632.00 */ -/* 122880.00 => 229 */ 0xe5, /* => 121472.00 */ -/* 126976.00 => 229 */ 0xe5, /* => 121472.00 */ -/* 131072.00 => 230 */ 0xe6, /* => 126976.00 */ -/* 139264.00 => 232 */ 0xe8, /* => 137728.00 */ -/* 147456.00 => 233 */ 0xe9, /* => 144896.00 */ -/* 155648.00 => 234 */ 0xea, /* => 150528.00 */ -/* 163840.00 => 236 */ 0xec, /* => 163584.00 */ -/* 172032.00 => 237 */ 0xed, /* => 168448.00 */ -/* 180224.00 => 238 */ 0xee, /* => 176384.00 */ -/* 188416.00 => 239 */ 0xef, /* => 185088.00 */ -/* 196608.00 => 240 */ 0xf0, /* => 191488.00 */ -/* 204800.00 => 241 */ 0xf1, /* => 201728.00 */ -/* 212992.00 => 242 */ 0xf2, /* => 209152.00 */ -/* 221184.00 => 243 */ 0xf3, /* => 217344.00 */ -/* 229376.00 => 244 */ 0xf4, /* => 226048.00 */ -/* 237568.00 => 245 */ 0xf5, /* => 235264.00 */ -/* 245760.00 => 246 */ 0xf6, /* => 245504.00 */ -/* 253952.00 => 246 */ 0xf6, /* => 245504.00 */ -/* 262144.00 => 247 */ 0xf7, /* => 256768.00 */ -/* 278528.00 => 248 */ 0xf8, /* => 268800.00 */ -/* 294912.00 => 250 */ 0xfa, /* => 289792.00 */ -/* 311296.00 => 251 */ 0xfb, /* => 305152.00 */ -/* 327680.00 => 252 */ 0xfc, /* => 313856.00 */ -/* 344064.00 => 254 */ 0xfe, /* => 342016.00 */ -/* 360448.00 => 255 */ 0xff, /* => 352768.00 */ -/* 376832.00 => 255 */ 0xff, /* => 352768.00 */ -/* 393216.00 => 255 */ 0xff, /* => 352768.00 */ -/* 409600.00 => 255 */ 0xff, /* => 352768.00 */ -/* 425984.00 => 255 */ 0xff, /* => 352768.00 */ -/* 442368.00 => 255 */ 0xff, /* => 352768.00 */ -/* 458752.00 => 255 */ 0xff, /* => 352768.00 */ -/* 475136.00 => 255 */ 0xff, /* => 352768.00 */ -/* 491520.00 => 255 */ 0xff, /* => 352768.00 */ -/* 507904.00 => 255 */ 0xff, /* => 352768.00 */ -/* 524288.00 => 255 */ 0xff, /* => 352768.00 */ -/* 557056.00 => 255 */ 0xff, /* => 352768.00 */ -/* 589824.00 => 255 */ 0xff, /* => 352768.00 */ -/* 622592.00 => 255 */ 0xff, /* => 352768.00 */ -/* 655360.00 => 255 */ 0xff, /* => 352768.00 */ -/* 688128.00 => 255 */ 0xff, /* => 352768.00 */ -/* 720896.00 => 255 */ 0xff, /* => 352768.00 */ -/* 753664.00 => 255 */ 0xff, /* => 352768.00 */ -/* 786432.00 => 255 */ 0xff, /* => 352768.00 */ -/* 819200.00 => 255 */ 0xff, /* => 352768.00 */ -/* 851968.00 => 255 */ 0xff, /* => 352768.00 */ -/* 884736.00 => 255 */ 0xff, /* => 352768.00 */ -/* 917504.00 => 255 */ 0xff, /* => 352768.00 */ -/* 950272.00 => 255 */ 0xff, /* => 352768.00 */ -/* 983040.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1015808.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1048576.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1114112.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1179648.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1245184.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1310720.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1376256.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1441792.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1507328.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1572864.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1638400.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1703936.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1769472.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1835008.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1900544.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1966080.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2031616.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2097152.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2228224.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2359296.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2490368.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2621440.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2752512.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2883584.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3014656.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3145728.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3276800.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3407872.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3538944.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3670016.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3801088.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3932160.00 => 255 */ 0xff, /* => 352768.00 */ -/* 4063232.00 => 255 */ 0xff, /* => 352768.00 */ -/* 4194304.00 => 255 */ 0xff, /* => 352768.00 */ -/* 4456448.00 => 255 */ 0xff, /* => 352768.00 */ -/* 4718592.00 => 255 */ 0xff, /* => 352768.00 */ -/* 4980736.00 => 255 */ 0xff, /* => 352768.00 */ -/* 5242880.00 => 255 */ 0xff, /* => 352768.00 */ -/* 5505024.00 => 255 */ 0xff, /* => 352768.00 */ -/* 5767168.00 => 255 */ 0xff, /* => 352768.00 */ -/* 6029312.00 => 255 */ 0xff, /* => 352768.00 */ -/* 6291456.00 => 255 */ 0xff, /* => 352768.00 */ -/* 6553600.00 => 255 */ 0xff, /* => 352768.00 */ -/* 6815744.00 => 255 */ 0xff, /* => 352768.00 */ -/* 7077888.00 => 255 */ 0xff, /* => 352768.00 */ -/* 7340032.00 => 255 */ 0xff, /* => 352768.00 */ -/* 7602176.00 => 255 */ 0xff, /* => 352768.00 */ -/* 7864320.00 => 255 */ 0xff, /* => 352768.00 */ -/* 8126464.00 => 255 */ 0xff, /* => 352768.00 */ -/* 8388608.00 => 255 */ 0xff, /* => 352768.00 */ -/* 8912896.00 => 255 */ 0xff, /* => 352768.00 */ -/* 9437184.00 => 255 */ 0xff, /* => 352768.00 */ -/* 9961472.00 => 255 */ 0xff, /* => 352768.00 */ -/* 10485760.00 => 255 */ 0xff, /* => 352768.00 */ -/* 11010048.00 => 255 */ 0xff, /* => 352768.00 */ -/* 11534336.00 => 255 */ 0xff, /* => 352768.00 */ -/* 12058624.00 => 255 */ 0xff, /* => 352768.00 */ -/* 12582912.00 => 255 */ 0xff, /* => 352768.00 */ -/* 13107200.00 => 255 */ 0xff, /* => 352768.00 */ -/* 13631488.00 => 255 */ 0xff, /* => 352768.00 */ -/* 14155776.00 => 255 */ 0xff, /* => 352768.00 */ -/* 14680064.00 => 255 */ 0xff, /* => 352768.00 */ -/* 15204352.00 => 255 */ 0xff, /* => 352768.00 */ -/* 15728640.00 => 255 */ 0xff, /* => 352768.00 */ -/* 16252928.00 => 255 */ 0xff, /* => 352768.00 */ -/* 16777216.00 => 255 */ 0xff, /* => 352768.00 */ -/* 17825792.00 => 255 */ 0xff, /* => 352768.00 */ -/* 18874368.00 => 255 */ 0xff, /* => 352768.00 */ -/* 19922944.00 => 255 */ 0xff, /* => 352768.00 */ -/* 20971520.00 => 255 */ 0xff, /* => 352768.00 */ -/* 22020096.00 => 255 */ 0xff, /* => 352768.00 */ -/* 23068672.00 => 255 */ 0xff, /* => 352768.00 */ -/* 24117248.00 => 255 */ 0xff, /* => 352768.00 */ -/* 25165824.00 => 255 */ 0xff, /* => 352768.00 */ -/* 26214400.00 => 255 */ 0xff, /* => 352768.00 */ -/* 27262976.00 => 255 */ 0xff, /* => 352768.00 */ -/* 28311552.00 => 255 */ 0xff, /* => 352768.00 */ -/* 29360128.00 => 255 */ 0xff, /* => 352768.00 */ -/* 30408704.00 => 255 */ 0xff, /* => 352768.00 */ -/* 31457280.00 => 255 */ 0xff, /* => 352768.00 */ -/* 32505856.00 => 255 */ 0xff, /* => 352768.00 */ -/* 33554432.00 => 255 */ 0xff, /* => 352768.00 */ -/* 35651584.00 => 255 */ 0xff, /* => 352768.00 */ -/* 37748736.00 => 255 */ 0xff, /* => 352768.00 */ -/* 39845888.00 => 255 */ 0xff, /* => 352768.00 */ -/* 41943040.00 => 255 */ 0xff, /* => 352768.00 */ -/* 44040192.00 => 255 */ 0xff, /* => 352768.00 */ -/* 46137344.00 => 255 */ 0xff, /* => 352768.00 */ -/* 48234496.00 => 255 */ 0xff, /* => 352768.00 */ -/* 50331648.00 => 255 */ 0xff, /* => 352768.00 */ -/* 52428800.00 => 255 */ 0xff, /* => 352768.00 */ -/* 54525952.00 => 255 */ 0xff, /* => 352768.00 */ -/* 56623104.00 => 255 */ 0xff, /* => 352768.00 */ -/* 58720256.00 => 255 */ 0xff, /* => 352768.00 */ -/* 60817408.00 => 255 */ 0xff, /* => 352768.00 */ -/* 62914560.00 => 255 */ 0xff, /* => 352768.00 */ -/* 65011712.00 => 255 */ 0xff, /* => 352768.00 */ -/* 67108864.00 => 255 */ 0xff, /* => 352768.00 */ -/* 71303168.00 => 255 */ 0xff, /* => 352768.00 */ -/* 75497472.00 => 255 */ 0xff, /* => 352768.00 */ -/* 79691776.00 => 255 */ 0xff, /* => 352768.00 */ -/* 83886080.00 => 255 */ 0xff, /* => 352768.00 */ -/* 88080384.00 => 255 */ 0xff, /* => 352768.00 */ -/* 92274688.00 => 255 */ 0xff, /* => 352768.00 */ -/* 96468992.00 => 255 */ 0xff, /* => 352768.00 */ -/* 100663296.00 => 255 */ 0xff, /* => 352768.00 */ -/* 104857600.00 => 255 */ 0xff, /* => 352768.00 */ -/* 109051904.00 => 255 */ 0xff, /* => 352768.00 */ -/* 113246208.00 => 255 */ 0xff, /* => 352768.00 */ -/* 117440512.00 => 255 */ 0xff, /* => 352768.00 */ -/* 121634816.00 => 255 */ 0xff, /* => 352768.00 */ -/* 125829120.00 => 255 */ 0xff, /* => 352768.00 */ -/* 130023424.00 => 255 */ 0xff, /* => 352768.00 */ -/* 134217728.00 => 255 */ 0xff, /* => 352768.00 */ -/* 142606336.00 => 255 */ 0xff, /* => 352768.00 */ -/* 150994944.00 => 255 */ 0xff, /* => 352768.00 */ -/* 159383552.00 => 255 */ 0xff, /* => 352768.00 */ -/* 167772160.00 => 255 */ 0xff, /* => 352768.00 */ -/* 176160768.00 => 255 */ 0xff, /* => 352768.00 */ -/* 184549376.00 => 255 */ 0xff, /* => 352768.00 */ -/* 192937984.00 => 255 */ 0xff, /* => 352768.00 */ -/* 201326592.00 => 255 */ 0xff, /* => 352768.00 */ -/* 209715200.00 => 255 */ 0xff, /* => 352768.00 */ -/* 218103808.00 => 255 */ 0xff, /* => 352768.00 */ -/* 226492416.00 => 255 */ 0xff, /* => 352768.00 */ -/* 234881024.00 => 255 */ 0xff, /* => 352768.00 */ -/* 243269632.00 => 255 */ 0xff, /* => 352768.00 */ -/* 251658240.00 => 255 */ 0xff, /* => 352768.00 */ -/* 260046848.00 => 255 */ 0xff, /* => 352768.00 */ -/* 268435456.00 => 255 */ 0xff, /* => 352768.00 */ -/* 285212672.00 => 255 */ 0xff, /* => 352768.00 */ -/* 301989888.00 => 255 */ 0xff, /* => 352768.00 */ -/* 318767104.00 => 255 */ 0xff, /* => 352768.00 */ -/* 335544320.00 => 255 */ 0xff, /* => 352768.00 */ -/* 352321536.00 => 255 */ 0xff, /* => 352768.00 */ -/* 369098752.00 => 255 */ 0xff, /* => 352768.00 */ -/* 385875968.00 => 255 */ 0xff, /* => 352768.00 */ -/* 402653184.00 => 255 */ 0xff, /* => 352768.00 */ -/* 419430400.00 => 255 */ 0xff, /* => 352768.00 */ -/* 436207616.00 => 255 */ 0xff, /* => 352768.00 */ -/* 452984832.00 => 255 */ 0xff, /* => 352768.00 */ -/* 469762048.00 => 255 */ 0xff, /* => 352768.00 */ -/* 486539264.00 => 255 */ 0xff, /* => 352768.00 */ -/* 503316480.00 => 255 */ 0xff, /* => 352768.00 */ -/* 520093696.00 => 255 */ 0xff, /* => 352768.00 */ -/* 536870912.00 => 255 */ 0xff, /* => 352768.00 */ -/* 570425344.00 => 255 */ 0xff, /* => 352768.00 */ -/* 603979776.00 => 255 */ 0xff, /* => 352768.00 */ -/* 637534208.00 => 255 */ 0xff, /* => 352768.00 */ -/* 671088640.00 => 255 */ 0xff, /* => 352768.00 */ -/* 704643072.00 => 255 */ 0xff, /* => 352768.00 */ -/* 738197504.00 => 255 */ 0xff, /* => 352768.00 */ -/* 771751936.00 => 255 */ 0xff, /* => 352768.00 */ -/* 805306368.00 => 255 */ 0xff, /* => 352768.00 */ -/* 838860800.00 => 255 */ 0xff, /* => 352768.00 */ -/* 872415232.00 => 255 */ 0xff, /* => 352768.00 */ -/* 905969664.00 => 255 */ 0xff, /* => 352768.00 */ -/* 939524096.00 => 255 */ 0xff, /* => 352768.00 */ -/* 973078528.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1006632960.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1040187392.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1073741824.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1140850688.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1207959552.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1275068416.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1342177280.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1409286144.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1476395008.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1543503872.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1610612736.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1677721600.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1744830464.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1811939328.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1879048192.00 => 255 */ 0xff, /* => 352768.00 */ -/* 1946157056.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2013265920.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2080374784.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2147483648.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2281701376.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2415919104.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2550136832.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2684354560.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2818572288.00 => 255 */ 0xff, /* => 352768.00 */ -/* 2952790016.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3087007744.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3221225472.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3355443200.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3489660928.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3623878656.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3758096384.00 => 255 */ 0xff, /* => 352768.00 */ -/* 3892314112.00 => 255 */ 0xff, /* => 352768.00 */ -/* 4026531840.00 => 255 */ 0xff, /* => 352768.00 */ -/* 4160749568.00 => 255 */ 0xff, /* => 352768.00 */ -}; diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c deleted file mode 100644 index 0d38e93772c2..000000000000 --- a/drivers/atm/iphase.c +++ /dev/null @@ -1,3283 +0,0 @@ -/****************************************************************************** - iphase.c: Device driver for Interphase ATM PCI adapter cards - Author: Peter Wang - Some fixes: Arnaldo Carvalho de Melo - Interphase Corporation - Version: 1.0 -******************************************************************************* - - This software may be used and distributed according to the terms - of the GNU General Public License (GPL), incorporated herein by reference. - Drivers based on this skeleton fall under the GPL and must retain - the authorship (implicit copyright) notice. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - Modified from an incomplete driver for Interphase 5575 1KVC 1M card which - was originally written by Monalisa Agrawal at UNH. Now this driver - supports a variety of varients of Interphase ATM PCI (i)Chip adapter - card family (See www.iphase.com/products/ClassSheet.cfm?ClassID=ATM) - in terms of PHY type, the size of control memory and the size of - packet memory. The following are the change log and history: - - Bugfix the Mona's UBR driver. - Modify the basic memory allocation and dma logic. - Port the driver to the latest kernel from 2.0.46. - Complete the ABR logic of the driver, and added the ABR work- - around for the hardware anormalies. - Add the CBR support. - Add the flow control logic to the driver to allow rate-limit VC. - Add 4K VC support to the board with 512K control memory. - Add the support of all the variants of the Interphase ATM PCI - (i)Chip adapter cards including x575 (155M OC3 and UTP155), x525 - (25M UTP25) and x531 (DS3 and E3). - Add SMP support. - - Support and updates available at: ftp://ftp.iphase.com/pub/atm - -*******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "iphase.h" -#include "suni.h" -#define swap_byte_order(x) (((x & 0xff) << 8) | ((x & 0xff00) >> 8)) - -#define PRIV(dev) ((struct suni_priv *) dev->phy_data) - -static unsigned char ia_phy_get(struct atm_dev *dev, unsigned long addr); -static void desc_dbg(IADEV *iadev); - -static IADEV *ia_dev[8]; -static struct atm_dev *_ia_dev[8]; -static int iadev_count; -static void ia_led_timer(struct timer_list *unused); -static DEFINE_TIMER(ia_timer, ia_led_timer); -static int IA_TX_BUF = DFL_TX_BUFFERS, IA_TX_BUF_SZ = DFL_TX_BUF_SZ; -static int IA_RX_BUF = DFL_RX_BUFFERS, IA_RX_BUF_SZ = DFL_RX_BUF_SZ; -static uint IADebugFlag = /* IF_IADBG_ERR | IF_IADBG_CBR| IF_IADBG_INIT_ADAPTER - |IF_IADBG_ABR | IF_IADBG_EVENT*/ 0; - -module_param(IA_TX_BUF, int, 0); -module_param(IA_TX_BUF_SZ, int, 0); -module_param(IA_RX_BUF, int, 0); -module_param(IA_RX_BUF_SZ, int, 0); -module_param(IADebugFlag, uint, 0644); - -MODULE_DESCRIPTION("Driver for Interphase ATM PCI NICs"); -MODULE_LICENSE("GPL"); - -/**************************** IA_LIB **********************************/ - -static void ia_init_rtn_q (IARTN_Q *que) -{ - que->next = NULL; - que->tail = NULL; -} - -static void ia_enque_head_rtn_q (IARTN_Q *que, IARTN_Q * data) -{ - data->next = NULL; - if (que->next == NULL) - que->next = que->tail = data; - else { - data->next = que->next; - que->next = data; - } - return; -} - -static int ia_enque_rtn_q (IARTN_Q *que, struct desc_tbl_t data) { - IARTN_Q *entry = kmalloc_obj(*entry, GFP_ATOMIC); - if (!entry) - return -ENOMEM; - entry->data = data; - entry->next = NULL; - if (que->next == NULL) - que->next = que->tail = entry; - else { - que->tail->next = entry; - que->tail = que->tail->next; - } - return 1; -} - -static IARTN_Q * ia_deque_rtn_q (IARTN_Q *que) { - IARTN_Q *tmpdata; - if (que->next == NULL) - return NULL; - tmpdata = que->next; - if ( que->next == que->tail) - que->next = que->tail = NULL; - else - que->next = que->next->next; - return tmpdata; -} - -static void ia_hack_tcq(IADEV *dev) { - - u_short desc1; - u_short tcq_wr; - struct ia_vcc *iavcc_r = NULL; - - tcq_wr = readl(dev->seg_reg+TCQ_WR_PTR) & 0xffff; - while (dev->host_tcq_wr != tcq_wr) { - desc1 = *(u_short *)(dev->seg_ram + dev->host_tcq_wr); - if (!desc1) ; - else if (!dev->desc_tbl[desc1 -1].timestamp) { - IF_ABR(printk(" Desc %d is reset at %ld\n", desc1 -1, jiffies);) - *(u_short *) (dev->seg_ram + dev->host_tcq_wr) = 0; - } - else if (dev->desc_tbl[desc1 -1].timestamp) { - if (!(iavcc_r = dev->desc_tbl[desc1 -1].iavcc)) { - printk("IA: Fatal err in get_desc\n"); - continue; - } - iavcc_r->vc_desc_cnt--; - dev->desc_tbl[desc1 -1].timestamp = 0; - IF_EVENT(printk("ia_hack: return_q skb = 0x%p desc = %d\n", - dev->desc_tbl[desc1 -1].txskb, desc1);) - if (iavcc_r->pcr < dev->rate_limit) { - IA_SKB_STATE (dev->desc_tbl[desc1-1].txskb) |= IA_TX_DONE; - if (ia_enque_rtn_q(&dev->tx_return_q, dev->desc_tbl[desc1 -1]) < 0) - printk("ia_hack_tcq: No memory available\n"); - } - dev->desc_tbl[desc1 -1].iavcc = NULL; - dev->desc_tbl[desc1 -1].txskb = NULL; - } - dev->host_tcq_wr += 2; - if (dev->host_tcq_wr > dev->ffL.tcq_ed) - dev->host_tcq_wr = dev->ffL.tcq_st; - } -} /* ia_hack_tcq */ - -static u16 get_desc (IADEV *dev, struct ia_vcc *iavcc) { - u_short desc_num, i; - struct ia_vcc *iavcc_r = NULL; - unsigned long delta; - static unsigned long timer = 0; - int ltimeout; - - ia_hack_tcq (dev); - if((time_after(jiffies,timer+50)) || ((dev->ffL.tcq_rd==dev->host_tcq_wr))) { - timer = jiffies; - i=0; - while (i < dev->num_tx_desc) { - if (!dev->desc_tbl[i].timestamp) { - i++; - continue; - } - ltimeout = dev->desc_tbl[i].iavcc->ltimeout; - delta = jiffies - dev->desc_tbl[i].timestamp; - if (delta >= ltimeout) { - IF_ABR(printk("RECOVER run!! desc_tbl %d = %d delta = %ld, time = %ld\n", i,dev->desc_tbl[i].timestamp, delta, jiffies);) - if (dev->ffL.tcq_rd == dev->ffL.tcq_st) - dev->ffL.tcq_rd = dev->ffL.tcq_ed; - else - dev->ffL.tcq_rd -= 2; - *(u_short *)(dev->seg_ram + dev->ffL.tcq_rd) = i+1; - if (!dev->desc_tbl[i].txskb || !(iavcc_r = dev->desc_tbl[i].iavcc)) - printk("Fatal err, desc table vcc or skb is NULL\n"); - else - iavcc_r->vc_desc_cnt--; - dev->desc_tbl[i].timestamp = 0; - dev->desc_tbl[i].iavcc = NULL; - dev->desc_tbl[i].txskb = NULL; - } - i++; - } /* while */ - } - if (dev->ffL.tcq_rd == dev->host_tcq_wr) - return 0xFFFF; - - /* Get the next available descriptor number from TCQ */ - desc_num = *(u_short *)(dev->seg_ram + dev->ffL.tcq_rd); - - while (!desc_num || (dev->desc_tbl[desc_num -1]).timestamp) { - dev->ffL.tcq_rd += 2; - if (dev->ffL.tcq_rd > dev->ffL.tcq_ed) - dev->ffL.tcq_rd = dev->ffL.tcq_st; - if (dev->ffL.tcq_rd == dev->host_tcq_wr) - return 0xFFFF; - desc_num = *(u_short *)(dev->seg_ram + dev->ffL.tcq_rd); - } - - /* get system time */ - dev->desc_tbl[desc_num -1].timestamp = jiffies; - return desc_num; -} - -static void clear_lockup (struct atm_vcc *vcc, IADEV *dev) { - u_char foundLockUp; - vcstatus_t *vcstatus; - u_short *shd_tbl; - u_short tempCellSlot, tempFract; - struct main_vc *abr_vc = (struct main_vc *)dev->MAIN_VC_TABLE_ADDR; - struct ext_vc *eabr_vc = (struct ext_vc *)dev->EXT_VC_TABLE_ADDR; - u_int i; - - if (vcc->qos.txtp.traffic_class == ATM_ABR) { - vcstatus = (vcstatus_t *) &(dev->testTable[vcc->vci]->vc_status); - vcstatus->cnt++; - foundLockUp = 0; - if( vcstatus->cnt == 0x05 ) { - abr_vc += vcc->vci; - eabr_vc += vcc->vci; - if( eabr_vc->last_desc ) { - if( (abr_vc->status & 0x07) == ABR_STATE /* 0x2 */ ) { - /* Wait for 10 Micro sec */ - udelay(10); - if ((eabr_vc->last_desc)&&((abr_vc->status & 0x07)==ABR_STATE)) - foundLockUp = 1; - } - else { - tempCellSlot = abr_vc->last_cell_slot; - tempFract = abr_vc->fraction; - if((tempCellSlot == dev->testTable[vcc->vci]->lastTime) - && (tempFract == dev->testTable[vcc->vci]->fract)) - foundLockUp = 1; - dev->testTable[vcc->vci]->lastTime = tempCellSlot; - dev->testTable[vcc->vci]->fract = tempFract; - } - } /* last descriptor */ - vcstatus->cnt = 0; - } /* vcstatus->cnt */ - - if (foundLockUp) { - IF_ABR(printk("LOCK UP found\n");) - writew(0xFFFD, dev->seg_reg+MODE_REG_0); - /* Wait for 10 Micro sec */ - udelay(10); - abr_vc->status &= 0xFFF8; - abr_vc->status |= 0x0001; /* state is idle */ - shd_tbl = (u_short *)dev->ABR_SCHED_TABLE_ADDR; - for( i = 0; ((i < dev->num_vc) && (shd_tbl[i])); i++ ); - if (i < dev->num_vc) - shd_tbl[i] = vcc->vci; - else - IF_ERR(printk("ABR Seg. may not continue on VC %x\n",vcc->vci);) - writew(T_ONLINE, dev->seg_reg+MODE_REG_0); - writew(~(TRANSMIT_DONE|TCQ_NOT_EMPTY), dev->seg_reg+SEG_MASK_REG); - writew(TRANSMIT_DONE, dev->seg_reg+SEG_INTR_STATUS_REG); - vcstatus->cnt = 0; - } /* foundLockUp */ - - } /* if an ABR VC */ - - -} - -/* -** Conversion of 24-bit cellrate (cells/sec) to 16-bit floating point format. -** -** +----+----+------------------+-------------------------------+ -** | R | NZ | 5-bit exponent | 9-bit mantissa | -** +----+----+------------------+-------------------------------+ -** -** R = reserved (written as 0) -** NZ = 0 if 0 cells/sec; 1 otherwise -** -** if NZ = 1, rate = 1.mmmmmmmmm x 2^(eeeee) cells/sec -*/ -static u16 -cellrate_to_float(u32 cr) -{ - -#define NZ 0x4000 -#define M_BITS 9 /* Number of bits in mantissa */ -#define E_BITS 5 /* Number of bits in exponent */ -#define M_MASK 0x1ff -#define E_MASK 0x1f - u16 flot; - u32 tmp = cr & 0x00ffffff; - int i = 0; - if (cr == 0) - return 0; - while (tmp != 1) { - tmp >>= 1; - i++; - } - if (i == M_BITS) - flot = NZ | (i << M_BITS) | (cr & M_MASK); - else if (i < M_BITS) - flot = NZ | (i << M_BITS) | ((cr << (M_BITS - i)) & M_MASK); - else - flot = NZ | (i << M_BITS) | ((cr >> (i - M_BITS)) & M_MASK); - return flot; -} - -#if 0 -/* -** Conversion of 16-bit floating point format to 24-bit cellrate (cells/sec). -*/ -static u32 -float_to_cellrate(u16 rate) -{ - u32 exp, mantissa, cps; - if ((rate & NZ) == 0) - return 0; - exp = (rate >> M_BITS) & E_MASK; - mantissa = rate & M_MASK; - if (exp == 0) - return 1; - cps = (1 << M_BITS) | mantissa; - if (exp == M_BITS) - cps = cps; - else if (exp > M_BITS) - cps <<= (exp - M_BITS); - else - cps >>= (M_BITS - exp); - return cps; -} -#endif - -static void init_abr_vc (IADEV *dev, srv_cls_param_t *srv_p) { - srv_p->class_type = ATM_ABR; - srv_p->pcr = dev->LineRate; - srv_p->mcr = 0; - srv_p->icr = 0x055cb7; - srv_p->tbe = 0xffffff; - srv_p->frtt = 0x3a; - srv_p->rif = 0xf; - srv_p->rdf = 0xb; - srv_p->nrm = 0x4; - srv_p->trm = 0x7; - srv_p->cdf = 0x3; - srv_p->adtf = 50; -} - -static int -ia_open_abr_vc(IADEV *dev, srv_cls_param_t *srv_p, - struct atm_vcc *vcc, u8 flag) -{ - f_vc_abr_entry *f_abr_vc; - r_vc_abr_entry *r_abr_vc; - u32 icr; - u8 trm, nrm, crm; - u16 adtf, air, *ptr16; - f_abr_vc =(f_vc_abr_entry *)dev->MAIN_VC_TABLE_ADDR; - f_abr_vc += vcc->vci; - switch (flag) { - case 1: /* FFRED initialization */ -#if 0 /* sanity check */ - if (srv_p->pcr == 0) - return INVALID_PCR; - if (srv_p->pcr > dev->LineRate) - srv_p->pcr = dev->LineRate; - if ((srv_p->mcr + dev->sum_mcr) > dev->LineRate) - return MCR_UNAVAILABLE; - if (srv_p->mcr > srv_p->pcr) - return INVALID_MCR; - if (!(srv_p->icr)) - srv_p->icr = srv_p->pcr; - if ((srv_p->icr < srv_p->mcr) || (srv_p->icr > srv_p->pcr)) - return INVALID_ICR; - if ((srv_p->tbe < MIN_TBE) || (srv_p->tbe > MAX_TBE)) - return INVALID_TBE; - if ((srv_p->frtt < MIN_FRTT) || (srv_p->frtt > MAX_FRTT)) - return INVALID_FRTT; - if (srv_p->nrm > MAX_NRM) - return INVALID_NRM; - if (srv_p->trm > MAX_TRM) - return INVALID_TRM; - if (srv_p->adtf > MAX_ADTF) - return INVALID_ADTF; - else if (srv_p->adtf == 0) - srv_p->adtf = 1; - if (srv_p->cdf > MAX_CDF) - return INVALID_CDF; - if (srv_p->rif > MAX_RIF) - return INVALID_RIF; - if (srv_p->rdf > MAX_RDF) - return INVALID_RDF; -#endif - memset ((caddr_t)f_abr_vc, 0, sizeof(*f_abr_vc)); - f_abr_vc->f_vc_type = ABR; - nrm = 2 << srv_p->nrm; /* (2 ** (srv_p->nrm +1)) */ - /* i.e 2**n = 2 << (n-1) */ - f_abr_vc->f_nrm = nrm << 8 | nrm; - trm = 100000/(2 << (16 - srv_p->trm)); - if ( trm == 0) trm = 1; - f_abr_vc->f_nrmexp =(((srv_p->nrm +1) & 0x0f) << 12)|(MRM << 8) | trm; - crm = srv_p->tbe / nrm; - if (crm == 0) crm = 1; - f_abr_vc->f_crm = crm & 0xff; - f_abr_vc->f_pcr = cellrate_to_float(srv_p->pcr); - icr = min( srv_p->icr, (srv_p->tbe > srv_p->frtt) ? - ((srv_p->tbe/srv_p->frtt)*1000000) : - (1000000/(srv_p->frtt/srv_p->tbe))); - f_abr_vc->f_icr = cellrate_to_float(icr); - adtf = (10000 * srv_p->adtf)/8192; - if (adtf == 0) adtf = 1; - f_abr_vc->f_cdf = ((7 - srv_p->cdf) << 12 | adtf) & 0xfff; - f_abr_vc->f_mcr = cellrate_to_float(srv_p->mcr); - f_abr_vc->f_acr = f_abr_vc->f_icr; - f_abr_vc->f_status = 0x0042; - break; - case 0: /* RFRED initialization */ - ptr16 = (u_short *)(dev->reass_ram + REASS_TABLE*dev->memSize); - *(ptr16 + vcc->vci) = NO_AAL5_PKT | REASS_ABR; - r_abr_vc = (r_vc_abr_entry*)(dev->reass_ram+ABR_VC_TABLE*dev->memSize); - r_abr_vc += vcc->vci; - r_abr_vc->r_status_rdf = (15 - srv_p->rdf) & 0x000f; - air = srv_p->pcr << (15 - srv_p->rif); - if (air == 0) air = 1; - r_abr_vc->r_air = cellrate_to_float(air); - dev->testTable[vcc->vci]->vc_status = VC_ACTIVE | VC_ABR; - dev->sum_mcr += srv_p->mcr; - dev->n_abr++; - break; - default: - break; - } - return 0; -} -static int ia_cbr_setup (IADEV *dev, struct atm_vcc *vcc) { - u32 rateLow=0, rateHigh, rate; - int entries; - struct ia_vcc *ia_vcc; - - int idealSlot =0, testSlot, toBeAssigned, inc; - u32 spacing; - u16 *SchedTbl, *TstSchedTbl; - u16 cbrVC, vcIndex; - u32 fracSlot = 0; - u32 sp_mod = 0; - u32 sp_mod2 = 0; - - /* IpAdjustTrafficParams */ - if (vcc->qos.txtp.max_pcr <= 0) { - IF_ERR(printk("PCR for CBR not defined\n");) - return -1; - } - rate = vcc->qos.txtp.max_pcr; - entries = rate / dev->Granularity; - IF_CBR(printk("CBR: CBR entries=0x%x for rate=0x%x & Gran=0x%x\n", - entries, rate, dev->Granularity);) - if (entries < 1) - IF_CBR(printk("CBR: Bandwidth smaller than granularity of CBR table\n");) - rateLow = entries * dev->Granularity; - rateHigh = (entries + 1) * dev->Granularity; - if (3*(rate - rateLow) > (rateHigh - rate)) - entries++; - if (entries > dev->CbrRemEntries) { - IF_CBR(printk("CBR: Not enough bandwidth to support this PCR.\n");) - IF_CBR(printk("Entries = 0x%x, CbrRemEntries = 0x%x.\n", - entries, dev->CbrRemEntries);) - return -EBUSY; - } - - ia_vcc = INPH_IA_VCC(vcc); - ia_vcc->NumCbrEntry = entries; - dev->sum_mcr += entries * dev->Granularity; - /* IaFFrednInsertCbrSched */ - // Starting at an arbitrary location, place the entries into the table - // as smoothly as possible - cbrVC = 0; - spacing = dev->CbrTotEntries / entries; - sp_mod = dev->CbrTotEntries % entries; // get modulo - toBeAssigned = entries; - fracSlot = 0; - vcIndex = vcc->vci; - IF_CBR(printk("Vci=0x%x,Spacing=0x%x,Sp_mod=0x%x\n",vcIndex,spacing,sp_mod);) - while (toBeAssigned) - { - // If this is the first time, start the table loading for this connection - // as close to entryPoint as possible. - if (toBeAssigned == entries) - { - idealSlot = dev->CbrEntryPt; - dev->CbrEntryPt += 2; // Adding 2 helps to prevent clumping - if (dev->CbrEntryPt >= dev->CbrTotEntries) - dev->CbrEntryPt -= dev->CbrTotEntries;// Wrap if necessary - } else { - idealSlot += (u32)(spacing + fracSlot); // Point to the next location - // in the table that would be smoothest - fracSlot = ((sp_mod + sp_mod2) / entries); // get new integer part - sp_mod2 = ((sp_mod + sp_mod2) % entries); // calc new fractional part - } - if (idealSlot >= (int)dev->CbrTotEntries) - idealSlot -= dev->CbrTotEntries; - // Continuously check around this ideal value until a null - // location is encountered. - SchedTbl = (u16*)(dev->seg_ram+CBR_SCHED_TABLE*dev->memSize); - inc = 0; - testSlot = idealSlot; - TstSchedTbl = (u16*)(SchedTbl+testSlot); //set index and read in value - IF_CBR(printk("CBR Testslot 0x%x AT Location 0x%p, NumToAssign=%d\n", - testSlot, TstSchedTbl,toBeAssigned);) - memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(cbrVC)); - while (cbrVC) // If another VC at this location, we have to keep looking - { - inc++; - testSlot = idealSlot - inc; - if (testSlot < 0) { // Wrap if necessary - testSlot += dev->CbrTotEntries; - IF_CBR(printk("Testslot Wrap. STable Start=0x%p,Testslot=%d\n", - SchedTbl,testSlot);) - } - TstSchedTbl = (u16 *)(SchedTbl + testSlot); // set table index - memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(cbrVC)); - if (!cbrVC) - break; - testSlot = idealSlot + inc; - if (testSlot >= (int)dev->CbrTotEntries) { // Wrap if necessary - testSlot -= dev->CbrTotEntries; - IF_CBR(printk("TotCbrEntries=%d",dev->CbrTotEntries);) - IF_CBR(printk(" Testslot=0x%x ToBeAssgned=%d\n", - testSlot, toBeAssigned);) - } - // set table index and read in value - TstSchedTbl = (u16*)(SchedTbl + testSlot); - IF_CBR(printk("Reading CBR Tbl from 0x%p, CbrVal=0x%x Iteration %d\n", - TstSchedTbl,cbrVC,inc);) - memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(cbrVC)); - } /* while */ - // Move this VCI number into this location of the CBR Sched table. - memcpy((caddr_t)TstSchedTbl, (caddr_t)&vcIndex, sizeof(*TstSchedTbl)); - dev->CbrRemEntries--; - toBeAssigned--; - } /* while */ - - /* IaFFrednCbrEnable */ - dev->NumEnabledCBR++; - if (dev->NumEnabledCBR == 1) { - writew((CBR_EN | UBR_EN | ABR_EN | (0x23 << 2)), dev->seg_reg+STPARMS); - IF_CBR(printk("CBR is enabled\n");) - } - return 0; -} -static void ia_cbrVc_close (struct atm_vcc *vcc) { - IADEV *iadev; - u16 *SchedTbl, NullVci = 0; - u32 i, NumFound; - - iadev = INPH_IA_DEV(vcc->dev); - iadev->NumEnabledCBR--; - SchedTbl = (u16*)(iadev->seg_ram+CBR_SCHED_TABLE*iadev->memSize); - if (iadev->NumEnabledCBR == 0) { - writew((UBR_EN | ABR_EN | (0x23 << 2)), iadev->seg_reg+STPARMS); - IF_CBR (printk("CBR support disabled\n");) - } - NumFound = 0; - for (i=0; i < iadev->CbrTotEntries; i++) - { - if (*SchedTbl == vcc->vci) { - iadev->CbrRemEntries++; - *SchedTbl = NullVci; - IF_CBR(NumFound++;) - } - SchedTbl++; - } - IF_CBR(printk("Exit ia_cbrVc_close, NumRemoved=%d\n",NumFound);) -} - -static int ia_avail_descs(IADEV *iadev) { - int tmp = 0; - ia_hack_tcq(iadev); - if (iadev->host_tcq_wr >= iadev->ffL.tcq_rd) - tmp = (iadev->host_tcq_wr - iadev->ffL.tcq_rd) / 2; - else - tmp = (iadev->ffL.tcq_ed - iadev->ffL.tcq_rd + 2 + iadev->host_tcq_wr - - iadev->ffL.tcq_st) / 2; - return tmp; -} - -static int ia_pkt_tx (struct atm_vcc *vcc, struct sk_buff *skb); - -static int ia_que_tx (IADEV *iadev) { - struct sk_buff *skb; - int num_desc; - struct atm_vcc *vcc; - num_desc = ia_avail_descs(iadev); - - while (num_desc && (skb = skb_dequeue(&iadev->tx_backlog))) { - if (!(vcc = ATM_SKB(skb)->vcc)) { - dev_kfree_skb_any(skb); - printk("ia_que_tx: Null vcc\n"); - break; - } - if (!test_bit(ATM_VF_READY,&vcc->flags)) { - dev_kfree_skb_any(skb); - printk("Free the SKB on closed vci %d \n", vcc->vci); - break; - } - if (ia_pkt_tx (vcc, skb)) { - skb_queue_head(&iadev->tx_backlog, skb); - } - num_desc--; - } - return 0; -} - -static void ia_tx_poll (IADEV *iadev) { - struct atm_vcc *vcc = NULL; - struct sk_buff *skb = NULL, *skb1 = NULL; - struct ia_vcc *iavcc; - IARTN_Q * rtne; - - ia_hack_tcq(iadev); - while ( (rtne = ia_deque_rtn_q(&iadev->tx_return_q))) { - skb = rtne->data.txskb; - if (!skb) { - printk("ia_tx_poll: skb is null\n"); - goto out; - } - vcc = ATM_SKB(skb)->vcc; - if (!vcc) { - printk("ia_tx_poll: vcc is null\n"); - dev_kfree_skb_any(skb); - goto out; - } - - iavcc = INPH_IA_VCC(vcc); - if (!iavcc) { - printk("ia_tx_poll: iavcc is null\n"); - dev_kfree_skb_any(skb); - goto out; - } - - skb1 = skb_dequeue(&iavcc->txing_skb); - while (skb1 && (skb1 != skb)) { - if (!(IA_SKB_STATE(skb1) & IA_TX_DONE)) { - printk("IA_tx_intr: Vci %d lost pkt!!!\n", vcc->vci); - } - IF_ERR(printk("Release the SKB not match\n");) - if ((vcc->pop) && (skb1->len != 0)) - { - vcc->pop(vcc, skb1); - IF_EVENT(printk("Transmit Done - skb 0x%lx return\n", - (long)skb1);) - } - else - dev_kfree_skb_any(skb1); - skb1 = skb_dequeue(&iavcc->txing_skb); - } - if (!skb1) { - IF_EVENT(printk("IA: Vci %d - skb not found requeued\n",vcc->vci);) - ia_enque_head_rtn_q (&iadev->tx_return_q, rtne); - break; - } - if ((vcc->pop) && (skb->len != 0)) - { - vcc->pop(vcc, skb); - IF_EVENT(printk("Tx Done - skb 0x%lx return\n",(long)skb);) - } - else - dev_kfree_skb_any(skb); - kfree(rtne); - } - ia_que_tx(iadev); -out: - return; -} -#if 0 -static void ia_eeprom_put (IADEV *iadev, u32 addr, u_short val) -{ - u32 t; - int i; - /* - * Issue a command to enable writes to the NOVRAM - */ - NVRAM_CMD (EXTEND + EWEN); - NVRAM_CLR_CE; - /* - * issue the write command - */ - NVRAM_CMD(IAWRITE + addr); - /* - * Send the data, starting with D15, then D14, and so on for 16 bits - */ - for (i=15; i>=0; i--) { - NVRAM_CLKOUT (val & 0x8000); - val <<= 1; - } - NVRAM_CLR_CE; - CFG_OR(NVCE); - t = readl(iadev->reg+IPHASE5575_EEPROM_ACCESS); - while (!(t & NVDO)) - t = readl(iadev->reg+IPHASE5575_EEPROM_ACCESS); - - NVRAM_CLR_CE; - /* - * disable writes again - */ - NVRAM_CMD(EXTEND + EWDS) - NVRAM_CLR_CE; - CFG_AND(~NVDI); -} -#endif - -static u16 ia_eeprom_get (IADEV *iadev, u32 addr) -{ - u_short val; - u32 t; - int i; - /* - * Read the first bit that was clocked with the falling edge of - * the last command data clock - */ - NVRAM_CMD(IAREAD + addr); - /* - * Now read the rest of the bits, the next bit read is D14, then D13, - * and so on. - */ - val = 0; - for (i=15; i>=0; i--) { - NVRAM_CLKIN(t); - val |= (t << i); - } - NVRAM_CLR_CE; - CFG_AND(~NVDI); - return val; -} - -static void ia_hw_type(IADEV *iadev) { - u_short memType = ia_eeprom_get(iadev, 25); - iadev->memType = memType; - if ((memType & MEM_SIZE_MASK) == MEM_SIZE_1M) { - iadev->num_tx_desc = IA_TX_BUF; - iadev->tx_buf_sz = IA_TX_BUF_SZ; - iadev->num_rx_desc = IA_RX_BUF; - iadev->rx_buf_sz = IA_RX_BUF_SZ; - } else if ((memType & MEM_SIZE_MASK) == MEM_SIZE_512K) { - if (IA_TX_BUF == DFL_TX_BUFFERS) - iadev->num_tx_desc = IA_TX_BUF / 2; - else - iadev->num_tx_desc = IA_TX_BUF; - iadev->tx_buf_sz = IA_TX_BUF_SZ; - if (IA_RX_BUF == DFL_RX_BUFFERS) - iadev->num_rx_desc = IA_RX_BUF / 2; - else - iadev->num_rx_desc = IA_RX_BUF; - iadev->rx_buf_sz = IA_RX_BUF_SZ; - } - else { - if (IA_TX_BUF == DFL_TX_BUFFERS) - iadev->num_tx_desc = IA_TX_BUF / 8; - else - iadev->num_tx_desc = IA_TX_BUF; - iadev->tx_buf_sz = IA_TX_BUF_SZ; - if (IA_RX_BUF == DFL_RX_BUFFERS) - iadev->num_rx_desc = IA_RX_BUF / 8; - else - iadev->num_rx_desc = IA_RX_BUF; - iadev->rx_buf_sz = IA_RX_BUF_SZ; - } - iadev->rx_pkt_ram = TX_PACKET_RAM + (iadev->num_tx_desc * iadev->tx_buf_sz); - IF_INIT(printk("BUF: tx=%d,sz=%d rx=%d sz= %d rx_pkt_ram=%d\n", - iadev->num_tx_desc, iadev->tx_buf_sz, iadev->num_rx_desc, - iadev->rx_buf_sz, iadev->rx_pkt_ram);) - -#if 0 - if ((memType & FE_MASK) == FE_SINGLE_MODE) { - iadev->phy_type = PHY_OC3C_S; - else if ((memType & FE_MASK) == FE_UTP_OPTION) - iadev->phy_type = PHY_UTP155; - else - iadev->phy_type = PHY_OC3C_M; -#endif - - iadev->phy_type = memType & FE_MASK; - IF_INIT(printk("memType = 0x%x iadev->phy_type = 0x%x\n", - memType,iadev->phy_type);) - if (iadev->phy_type == FE_25MBIT_PHY) - iadev->LineRate = (u32)(((25600000/8)*26)/(27*53)); - else if (iadev->phy_type == FE_DS3_PHY) - iadev->LineRate = (u32)(((44736000/8)*26)/(27*53)); - else if (iadev->phy_type == FE_E3_PHY) - iadev->LineRate = (u32)(((34368000/8)*26)/(27*53)); - else - iadev->LineRate = (u32)(ATM_OC3_PCR); - IF_INIT(printk("iadev->LineRate = %d \n", iadev->LineRate);) - -} - -static u32 ia_phy_read32(struct iadev_priv *ia, unsigned int reg) -{ - return readl(ia->phy + (reg >> 2)); -} - -static void ia_phy_write32(struct iadev_priv *ia, unsigned int reg, u32 val) -{ - writel(val, ia->phy + (reg >> 2)); -} - -static void ia_frontend_intr(struct iadev_priv *iadev) -{ - u32 status; - - if (iadev->phy_type & FE_25MBIT_PHY) { - status = ia_phy_read32(iadev, MB25_INTR_STATUS); - iadev->carrier_detect = (status & MB25_IS_GSB) ? 1 : 0; - } else if (iadev->phy_type & FE_DS3_PHY) { - ia_phy_read32(iadev, SUNI_DS3_FRM_INTR_STAT); - status = ia_phy_read32(iadev, SUNI_DS3_FRM_STAT); - iadev->carrier_detect = (status & SUNI_DS3_LOSV) ? 0 : 1; - } else if (iadev->phy_type & FE_E3_PHY) { - ia_phy_read32(iadev, SUNI_E3_FRM_MAINT_INTR_IND); - status = ia_phy_read32(iadev, SUNI_E3_FRM_FRAM_INTR_IND_STAT); - iadev->carrier_detect = (status & SUNI_E3_LOS) ? 0 : 1; - } else { - status = ia_phy_read32(iadev, SUNI_RSOP_STATUS); - iadev->carrier_detect = (status & SUNI_LOSV) ? 0 : 1; - } - - printk(KERN_INFO "IA: SUNI carrier %s\n", - iadev->carrier_detect ? "detected" : "lost signal"); -} - -static void ia_mb25_init(struct iadev_priv *iadev) -{ -#if 0 - mb25->mb25_master_ctrl = MB25_MC_DRIC | MB25_MC_DREC | MB25_MC_ENABLED; -#endif - ia_phy_write32(iadev, MB25_MASTER_CTRL, MB25_MC_DRIC | MB25_MC_DREC); - ia_phy_write32(iadev, MB25_DIAG_CONTROL, 0); - - iadev->carrier_detect = - (ia_phy_read32(iadev, MB25_INTR_STATUS) & MB25_IS_GSB) ? 1 : 0; -} - -struct ia_reg { - u16 reg; - u16 val; -}; - -static void ia_phy_write(struct iadev_priv *iadev, - const struct ia_reg *regs, int len) -{ - while (len--) { - ia_phy_write32(iadev, regs->reg, regs->val); - regs++; - } -} - -static void ia_suni_pm7345_init_ds3(struct iadev_priv *iadev) -{ - static const struct ia_reg suni_ds3_init[] = { - { SUNI_DS3_FRM_INTR_ENBL, 0x17 }, - { SUNI_DS3_FRM_CFG, 0x01 }, - { SUNI_DS3_TRAN_CFG, 0x01 }, - { SUNI_CONFIG, 0 }, - { SUNI_SPLR_CFG, 0 }, - { SUNI_SPLT_CFG, 0 } - }; - u32 status; - - status = ia_phy_read32(iadev, SUNI_DS3_FRM_STAT); - iadev->carrier_detect = (status & SUNI_DS3_LOSV) ? 0 : 1; - - ia_phy_write(iadev, suni_ds3_init, ARRAY_SIZE(suni_ds3_init)); -} - -static void ia_suni_pm7345_init_e3(struct iadev_priv *iadev) -{ - static const struct ia_reg suni_e3_init[] = { - { SUNI_E3_FRM_FRAM_OPTIONS, 0x04 }, - { SUNI_E3_FRM_MAINT_OPTIONS, 0x20 }, - { SUNI_E3_FRM_FRAM_INTR_ENBL, 0x1d }, - { SUNI_E3_FRM_MAINT_INTR_ENBL, 0x30 }, - { SUNI_E3_TRAN_STAT_DIAG_OPTIONS, 0 }, - { SUNI_E3_TRAN_FRAM_OPTIONS, 0x01 }, - { SUNI_CONFIG, SUNI_PM7345_E3ENBL }, - { SUNI_SPLR_CFG, 0x41 }, - { SUNI_SPLT_CFG, 0x41 } - }; - u32 status; - - status = ia_phy_read32(iadev, SUNI_E3_FRM_FRAM_INTR_IND_STAT); - iadev->carrier_detect = (status & SUNI_E3_LOS) ? 0 : 1; - ia_phy_write(iadev, suni_e3_init, ARRAY_SIZE(suni_e3_init)); -} - -static void ia_suni_pm7345_init(struct iadev_priv *iadev) -{ - static const struct ia_reg suni_init[] = { - /* Enable RSOP loss of signal interrupt. */ - { SUNI_INTR_ENBL, 0x28 }, - /* Clear error counters. */ - { SUNI_ID_RESET, 0 }, - /* Clear "PMCTST" in master test register. */ - { SUNI_MASTER_TEST, 0 }, - - { SUNI_RXCP_CTRL, 0x2c }, - { SUNI_RXCP_FCTRL, 0x81 }, - - { SUNI_RXCP_IDLE_PAT_H1, 0 }, - { SUNI_RXCP_IDLE_PAT_H2, 0 }, - { SUNI_RXCP_IDLE_PAT_H3, 0 }, - { SUNI_RXCP_IDLE_PAT_H4, 0x01 }, - - { SUNI_RXCP_IDLE_MASK_H1, 0xff }, - { SUNI_RXCP_IDLE_MASK_H2, 0xff }, - { SUNI_RXCP_IDLE_MASK_H3, 0xff }, - { SUNI_RXCP_IDLE_MASK_H4, 0xfe }, - - { SUNI_RXCP_CELL_PAT_H1, 0 }, - { SUNI_RXCP_CELL_PAT_H2, 0 }, - { SUNI_RXCP_CELL_PAT_H3, 0 }, - { SUNI_RXCP_CELL_PAT_H4, 0x01 }, - - { SUNI_RXCP_CELL_MASK_H1, 0xff }, - { SUNI_RXCP_CELL_MASK_H2, 0xff }, - { SUNI_RXCP_CELL_MASK_H3, 0xff }, - { SUNI_RXCP_CELL_MASK_H4, 0xff }, - - { SUNI_TXCP_CTRL, 0xa4 }, - { SUNI_TXCP_INTR_EN_STS, 0x10 }, - { SUNI_TXCP_IDLE_PAT_H5, 0x55 } - }; - - if (iadev->phy_type & FE_DS3_PHY) - ia_suni_pm7345_init_ds3(iadev); - else - ia_suni_pm7345_init_e3(iadev); - - ia_phy_write(iadev, suni_init, ARRAY_SIZE(suni_init)); - - ia_phy_write32(iadev, SUNI_CONFIG, ia_phy_read32(iadev, SUNI_CONFIG) & - ~(SUNI_PM7345_LLB | SUNI_PM7345_CLB | - SUNI_PM7345_DLB | SUNI_PM7345_PLB)); -#ifdef __SNMP__ - suni_pm7345->suni_rxcp_intr_en_sts |= SUNI_OOCDE; -#endif /* __SNMP__ */ - return; -} - - -/***************************** IA_LIB END *****************************/ - -#ifdef CONFIG_ATM_IA_DEBUG -static int tcnter = 0; -static void xdump( u_char* cp, int length, char* prefix ) -{ - int col, count; - u_char prntBuf[120]; - u_char* pBuf = prntBuf; - count = 0; - while(count < length){ - pBuf += sprintf( pBuf, "%s", prefix ); - for(col = 0;count + col < length && col < 16; col++){ - if (col != 0 && (col % 4) == 0) - pBuf += sprintf( pBuf, " " ); - pBuf += sprintf( pBuf, "%02X ", cp[count + col] ); - } - while(col++ < 16){ /* pad end of buffer with blanks */ - if ((col % 4) == 0) - sprintf( pBuf, " " ); - pBuf += sprintf( pBuf, " " ); - } - pBuf += sprintf( pBuf, " " ); - for(col = 0;count + col < length && col < 16; col++){ - u_char c = cp[count + col]; - - if (isascii(c) && isprint(c)) - pBuf += sprintf(pBuf, "%c", c); - else - pBuf += sprintf(pBuf, "."); - } - printk("%s\n", prntBuf); - count += col; - pBuf = prntBuf; - } - -} /* close xdump(... */ -#endif /* CONFIG_ATM_IA_DEBUG */ - - -static struct atm_dev *ia_boards = NULL; - -#define ACTUAL_RAM_BASE \ - RAM_BASE*((iadev->mem)/(128 * 1024)) -#define ACTUAL_SEG_RAM_BASE \ - IPHASE5575_FRAG_CONTROL_RAM_BASE*((iadev->mem)/(128 * 1024)) -#define ACTUAL_REASS_RAM_BASE \ - IPHASE5575_REASS_CONTROL_RAM_BASE*((iadev->mem)/(128 * 1024)) - - -/*-- some utilities and memory allocation stuff will come here -------------*/ - -static void desc_dbg(IADEV *iadev) { - - u_short tcq_wr_ptr, tcq_st_ptr, tcq_ed_ptr; - u32 i; - void __iomem *tmp; - // regval = readl((u32)ia_cmds->maddr); - tcq_wr_ptr = readw(iadev->seg_reg+TCQ_WR_PTR); - printk("B_tcq_wr = 0x%x desc = %d last desc = %d\n", - tcq_wr_ptr, readw(iadev->seg_ram+tcq_wr_ptr), - readw(iadev->seg_ram+tcq_wr_ptr-2)); - printk(" host_tcq_wr = 0x%x host_tcq_rd = 0x%x \n", iadev->host_tcq_wr, - iadev->ffL.tcq_rd); - tcq_st_ptr = readw(iadev->seg_reg+TCQ_ST_ADR); - tcq_ed_ptr = readw(iadev->seg_reg+TCQ_ED_ADR); - printk("tcq_st_ptr = 0x%x tcq_ed_ptr = 0x%x \n", tcq_st_ptr, tcq_ed_ptr); - i = 0; - while (tcq_st_ptr != tcq_ed_ptr) { - tmp = iadev->seg_ram+tcq_st_ptr; - printk("TCQ slot %d desc = %d Addr = %p\n", i++, readw(tmp), tmp); - tcq_st_ptr += 2; - } - for(i=0; i num_tx_desc; i++) - printk("Desc_tbl[%d] = %d \n", i, iadev->desc_tbl[i].timestamp); -} - - -/*----------------------------- Receiving side stuff --------------------------*/ - -static void rx_excp_rcvd(struct atm_dev *dev) -{ -#if 0 /* closing the receiving size will cause too many excp int */ - IADEV *iadev; - u_short state; - u_short excpq_rd_ptr; - //u_short *ptr; - int vci, error = 1; - iadev = INPH_IA_DEV(dev); - state = readl(iadev->reass_reg + STATE_REG) & 0xffff; - while((state & EXCPQ_EMPTY) != EXCPQ_EMPTY) - { printk("state = %x \n", state); - excpq_rd_ptr = readw(iadev->reass_reg + EXCP_Q_RD_PTR) & 0xffff; - printk("state = %x excpq_rd_ptr = %x \n", state, excpq_rd_ptr); - if (excpq_rd_ptr == *(u16*)(iadev->reass_reg + EXCP_Q_WR_PTR)) - IF_ERR(printk("excpq_rd_ptr is wrong!!!\n");) - // TODO: update exception stat - vci = readw(iadev->reass_ram+excpq_rd_ptr); - error = readw(iadev->reass_ram+excpq_rd_ptr+2) & 0x0007; - // pwang_test - excpq_rd_ptr += 4; - if (excpq_rd_ptr > (readw(iadev->reass_reg + EXCP_Q_ED_ADR)& 0xffff)) - excpq_rd_ptr = readw(iadev->reass_reg + EXCP_Q_ST_ADR)& 0xffff; - writew( excpq_rd_ptr, iadev->reass_reg + EXCP_Q_RD_PTR); - state = readl(iadev->reass_reg + STATE_REG) & 0xffff; - } -#endif -} - -static void free_desc(struct atm_dev *dev, int desc) -{ - IADEV *iadev; - iadev = INPH_IA_DEV(dev); - writew(desc, iadev->reass_ram+iadev->rfL.fdq_wr); - iadev->rfL.fdq_wr +=2; - if (iadev->rfL.fdq_wr > iadev->rfL.fdq_ed) - iadev->rfL.fdq_wr = iadev->rfL.fdq_st; - writew(iadev->rfL.fdq_wr, iadev->reass_reg+FREEQ_WR_PTR); -} - - -static int rx_pkt(struct atm_dev *dev) -{ - IADEV *iadev; - struct atm_vcc *vcc; - unsigned short status; - struct rx_buf_desc __iomem *buf_desc_ptr; - int desc; - struct dle* wr_ptr; - int len; - struct sk_buff *skb; - u_int buf_addr, dma_addr; - - iadev = INPH_IA_DEV(dev); - if (iadev->rfL.pcq_rd == (readw(iadev->reass_reg+PCQ_WR_PTR)&0xffff)) - { - printk(KERN_ERR DEV_LABEL "(itf %d) Receive queue empty\n", dev->number); - return -EINVAL; - } - /* mask 1st 3 bits to get the actual descno. */ - desc = readw(iadev->reass_ram+iadev->rfL.pcq_rd) & 0x1fff; - IF_RX(printk("reass_ram = %p iadev->rfL.pcq_rd = 0x%x desc = %d\n", - iadev->reass_ram, iadev->rfL.pcq_rd, desc); - printk(" pcq_wr_ptr = 0x%x\n", - readw(iadev->reass_reg+PCQ_WR_PTR)&0xffff);) - /* update the read pointer - maybe we shud do this in the end*/ - if ( iadev->rfL.pcq_rd== iadev->rfL.pcq_ed) - iadev->rfL.pcq_rd = iadev->rfL.pcq_st; - else - iadev->rfL.pcq_rd += 2; - writew(iadev->rfL.pcq_rd, iadev->reass_reg+PCQ_RD_PTR); - - /* get the buffer desc entry. - update stuff. - doesn't seem to be any update necessary - */ - buf_desc_ptr = iadev->RX_DESC_BASE_ADDR; - /* make the ptr point to the corresponding buffer desc entry */ - buf_desc_ptr += desc; - if (!desc || (desc > iadev->num_rx_desc) || - ((buf_desc_ptr->vc_index & 0xffff) >= iadev->num_vc)) { - free_desc(dev, desc); - IF_ERR(printk("IA: bad descriptor desc = %d \n", desc);) - return -1; - } - vcc = iadev->rx_open[buf_desc_ptr->vc_index & 0xffff]; - if (!vcc) - { - free_desc(dev, desc); - printk("IA: null vcc, drop PDU\n"); - return -1; - } - - - /* might want to check the status bits for errors */ - status = (u_short) (buf_desc_ptr->desc_mode); - if (status & (RX_CER | RX_PTE | RX_OFL)) - { - atomic_inc(&vcc->stats->rx_err); - IF_ERR(printk("IA: bad packet, dropping it");) - if (status & RX_CER) { - IF_ERR(printk(" cause: packet CRC error\n");) - } - else if (status & RX_PTE) { - IF_ERR(printk(" cause: packet time out\n");) - } - else { - IF_ERR(printk(" cause: buffer overflow\n");) - } - goto out_free_desc; - } - - /* - build DLE. - */ - - buf_addr = (buf_desc_ptr->buf_start_hi << 16) | buf_desc_ptr->buf_start_lo; - dma_addr = (buf_desc_ptr->dma_start_hi << 16) | buf_desc_ptr->dma_start_lo; - len = dma_addr - buf_addr; - if (len > iadev->rx_buf_sz) { - printk("Over %d bytes sdu received, dropped!!!\n", iadev->rx_buf_sz); - atomic_inc(&vcc->stats->rx_err); - goto out_free_desc; - } - - if (!(skb = atm_alloc_charge(vcc, len, GFP_ATOMIC))) { - if (vcc->vci < 32) - printk("Drop control packets\n"); - goto out_free_desc; - } - skb_put(skb,len); - // pwang_test - ATM_SKB(skb)->vcc = vcc; - ATM_DESC(skb) = desc; - skb_queue_tail(&iadev->rx_dma_q, skb); - - /* Build the DLE structure */ - wr_ptr = iadev->rx_dle_q.write; - wr_ptr->sys_pkt_addr = dma_map_single(&iadev->pci->dev, skb->data, - len, DMA_FROM_DEVICE); - wr_ptr->local_pkt_addr = buf_addr; - wr_ptr->bytes = len; /* We don't know this do we ?? */ - wr_ptr->mode = DMA_INT_ENABLE; - - /* shud take care of wrap around here too. */ - if(++wr_ptr == iadev->rx_dle_q.end) - wr_ptr = iadev->rx_dle_q.start; - iadev->rx_dle_q.write = wr_ptr; - udelay(1); - /* Increment transaction counter */ - writel(1, iadev->dma+IPHASE5575_RX_COUNTER); -out: return 0; -out_free_desc: - free_desc(dev, desc); - goto out; -} - -static void rx_intr(struct atm_dev *dev) -{ - IADEV *iadev; - u_short status; - u_short state, i; - - iadev = INPH_IA_DEV(dev); - status = readl(iadev->reass_reg+REASS_INTR_STATUS_REG) & 0xffff; - IF_EVENT(printk("rx_intr: status = 0x%x\n", status);) - if (status & RX_PKT_RCVD) - { - /* do something */ - /* Basically recvd an interrupt for receiving a packet. - A descriptor would have been written to the packet complete - queue. Get all the descriptors and set up dma to move the - packets till the packet complete queue is empty.. - */ - state = readl(iadev->reass_reg + STATE_REG) & 0xffff; - IF_EVENT(printk("Rx intr status: RX_PKT_RCVD %08x\n", status);) - while(!(state & PCQ_EMPTY)) - { - rx_pkt(dev); - state = readl(iadev->reass_reg + STATE_REG) & 0xffff; - } - iadev->rxing = 1; - } - if (status & RX_FREEQ_EMPT) - { - if (iadev->rxing) { - iadev->rx_tmp_cnt = iadev->rx_pkt_cnt; - iadev->rx_tmp_jif = jiffies; - iadev->rxing = 0; - } - else if ((time_after(jiffies, iadev->rx_tmp_jif + 50)) && - ((iadev->rx_pkt_cnt - iadev->rx_tmp_cnt) == 0)) { - for (i = 1; i <= iadev->num_rx_desc; i++) - free_desc(dev, i); -printk("Test logic RUN!!!!\n"); - writew( ~(RX_FREEQ_EMPT|RX_EXCP_RCVD),iadev->reass_reg+REASS_MASK_REG); - iadev->rxing = 1; - } - IF_EVENT(printk("Rx intr status: RX_FREEQ_EMPT %08x\n", status);) - } - - if (status & RX_EXCP_RCVD) - { - /* probably need to handle the exception queue also. */ - IF_EVENT(printk("Rx intr status: RX_EXCP_RCVD %08x\n", status);) - rx_excp_rcvd(dev); - } - - - if (status & RX_RAW_RCVD) - { - /* need to handle the raw incoming cells. This deepnds on - whether we have programmed to receive the raw cells or not. - Else ignore. */ - IF_EVENT(printk("Rx intr status: RX_RAW_RCVD %08x\n", status);) - } -} - - -static void rx_dle_intr(struct atm_dev *dev) -{ - IADEV *iadev; - struct atm_vcc *vcc; - struct sk_buff *skb; - int desc; - u_short state; - struct dle *dle, *cur_dle; - u_int dle_lp; - int len; - iadev = INPH_IA_DEV(dev); - - /* free all the dles done, that is just update our own dle read pointer - - do we really need to do this. Think not. */ - /* DMA is done, just get all the recevie buffers from the rx dma queue - and push them up to the higher layer protocol. Also free the desc - associated with the buffer. */ - dle = iadev->rx_dle_q.read; - dle_lp = readl(iadev->dma+IPHASE5575_RX_LIST_ADDR) & (sizeof(struct dle)*DLE_ENTRIES - 1); - cur_dle = (struct dle*)(iadev->rx_dle_q.start + (dle_lp >> 4)); - while(dle != cur_dle) - { - /* free the DMAed skb */ - skb = skb_dequeue(&iadev->rx_dma_q); - if (!skb) - goto INCR_DLE; - desc = ATM_DESC(skb); - free_desc(dev, desc); - - if (!(len = skb->len)) - { - printk("rx_dle_intr: skb len 0\n"); - dev_kfree_skb_any(skb); - } - else - { - struct cpcs_trailer *trailer; - u_short length; - struct ia_vcc *ia_vcc; - - dma_unmap_single(&iadev->pci->dev, iadev->rx_dle_q.write->sys_pkt_addr, - len, DMA_FROM_DEVICE); - /* no VCC related housekeeping done as yet. lets see */ - vcc = ATM_SKB(skb)->vcc; - if (!vcc) { - printk("IA: null vcc\n"); - dev_kfree_skb_any(skb); - goto INCR_DLE; - } - ia_vcc = INPH_IA_VCC(vcc); - if (ia_vcc == NULL) - { - atomic_inc(&vcc->stats->rx_err); - atm_return(vcc, skb->truesize); - dev_kfree_skb_any(skb); - goto INCR_DLE; - } - // get real pkt length pwang_test - trailer = (struct cpcs_trailer*)((u_char *)skb->data + - skb->len - sizeof(*trailer)); - length = swap_byte_order(trailer->length); - if ((length > iadev->rx_buf_sz) || (length > - (skb->len - sizeof(struct cpcs_trailer)))) - { - atomic_inc(&vcc->stats->rx_err); - IF_ERR(printk("rx_dle_intr: Bad AAL5 trailer %d (skb len %d)", - length, skb->len);) - atm_return(vcc, skb->truesize); - dev_kfree_skb_any(skb); - goto INCR_DLE; - } - skb_trim(skb, length); - - /* Display the packet */ - IF_RXPKT(printk("\nDmad Recvd data: len = %d \n", skb->len); - xdump(skb->data, skb->len, "RX: "); - printk("\n");) - - IF_RX(printk("rx_dle_intr: skb push");) - vcc->push(vcc,skb); - atomic_inc(&vcc->stats->rx); - iadev->rx_pkt_cnt++; - } -INCR_DLE: - if (++dle == iadev->rx_dle_q.end) - dle = iadev->rx_dle_q.start; - } - iadev->rx_dle_q.read = dle; - - /* if the interrupts are masked because there were no free desc available, - unmask them now. */ - if (!iadev->rxing) { - state = readl(iadev->reass_reg + STATE_REG) & 0xffff; - if (!(state & FREEQ_EMPTY)) { - state = readl(iadev->reass_reg + REASS_MASK_REG) & 0xffff; - writel(state & ~(RX_FREEQ_EMPT |/* RX_EXCP_RCVD |*/ RX_PKT_RCVD), - iadev->reass_reg+REASS_MASK_REG); - iadev->rxing++; - } - } -} - - -static int open_rx(struct atm_vcc *vcc) -{ - IADEV *iadev; - u_short __iomem *vc_table; - u_short __iomem *reass_ptr; - IF_EVENT(printk("iadev: open_rx %d.%d\n", vcc->vpi, vcc->vci);) - - if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0; - iadev = INPH_IA_DEV(vcc->dev); - if (vcc->qos.rxtp.traffic_class == ATM_ABR) { - if (iadev->phy_type & FE_25MBIT_PHY) { - printk("IA: ABR not support\n"); - return -EINVAL; - } - } - /* Make only this VCI in the vc table valid and let all - others be invalid entries */ - vc_table = iadev->reass_ram+RX_VC_TABLE*iadev->memSize; - vc_table += vcc->vci; - /* mask the last 6 bits and OR it with 3 for 1K VCs */ - - *vc_table = vcc->vci << 6; - /* Also keep a list of open rx vcs so that we can attach them with - incoming PDUs later. */ - if ((vcc->qos.rxtp.traffic_class == ATM_ABR) || - (vcc->qos.txtp.traffic_class == ATM_ABR)) - { - srv_cls_param_t srv_p; - init_abr_vc(iadev, &srv_p); - ia_open_abr_vc(iadev, &srv_p, vcc, 0); - } - else { /* for UBR later may need to add CBR logic */ - reass_ptr = iadev->reass_ram+REASS_TABLE*iadev->memSize; - reass_ptr += vcc->vci; - *reass_ptr = NO_AAL5_PKT; - } - - if (iadev->rx_open[vcc->vci]) - printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %d already open\n", - vcc->dev->number, vcc->vci); - iadev->rx_open[vcc->vci] = vcc; - return 0; -} - -static int rx_init(struct atm_dev *dev) -{ - IADEV *iadev; - struct rx_buf_desc __iomem *buf_desc_ptr; - unsigned long rx_pkt_start = 0; - void *dle_addr; - struct abr_vc_table *abr_vc_table; - u16 *vc_table; - u16 *reass_table; - int i,j, vcsize_sel; - u_short freeq_st_adr; - u_short *freeq_start; - - iadev = INPH_IA_DEV(dev); - // spin_lock_init(&iadev->rx_lock); - - /* Allocate 4k bytes - more aligned than needed (4k boundary) */ - dle_addr = dma_alloc_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, - &iadev->rx_dle_dma, GFP_KERNEL); - if (!dle_addr) { - printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n"); - goto err_out; - } - iadev->rx_dle_q.start = (struct dle *)dle_addr; - iadev->rx_dle_q.read = iadev->rx_dle_q.start; - iadev->rx_dle_q.write = iadev->rx_dle_q.start; - iadev->rx_dle_q.end = (struct dle*)((unsigned long)dle_addr+sizeof(struct dle)*DLE_ENTRIES); - /* the end of the dle q points to the entry after the last - DLE that can be used. */ - - /* write the upper 20 bits of the start address to rx list address register */ - /* We know this is 32bit bus addressed so the following is safe */ - writel(iadev->rx_dle_dma & 0xfffff000, - iadev->dma + IPHASE5575_RX_LIST_ADDR); - IF_INIT(printk("Tx Dle list addr: 0x%p value: 0x%0x\n", - iadev->dma+IPHASE5575_TX_LIST_ADDR, - readl(iadev->dma + IPHASE5575_TX_LIST_ADDR)); - printk("Rx Dle list addr: 0x%p value: 0x%0x\n", - iadev->dma+IPHASE5575_RX_LIST_ADDR, - readl(iadev->dma + IPHASE5575_RX_LIST_ADDR));) - - writew(0xffff, iadev->reass_reg+REASS_MASK_REG); - writew(0, iadev->reass_reg+MODE_REG); - writew(RESET_REASS, iadev->reass_reg+REASS_COMMAND_REG); - - /* Receive side control memory map - ------------------------------- - - Buffer descr 0x0000 (736 - 23K) - VP Table 0x5c00 (256 - 512) - Except q 0x5e00 (128 - 512) - Free buffer q 0x6000 (1K - 2K) - Packet comp q 0x6800 (1K - 2K) - Reass Table 0x7000 (1K - 2K) - VC Table 0x7800 (1K - 2K) - ABR VC Table 0x8000 (1K - 32K) - */ - - /* Base address for Buffer Descriptor Table */ - writew(RX_DESC_BASE >> 16, iadev->reass_reg+REASS_DESC_BASE); - /* Set the buffer size register */ - writew(iadev->rx_buf_sz, iadev->reass_reg+BUF_SIZE); - - /* Initialize each entry in the Buffer Descriptor Table */ - iadev->RX_DESC_BASE_ADDR = iadev->reass_ram+RX_DESC_BASE*iadev->memSize; - buf_desc_ptr = iadev->RX_DESC_BASE_ADDR; - memset_io(buf_desc_ptr, 0, sizeof(*buf_desc_ptr)); - buf_desc_ptr++; - rx_pkt_start = iadev->rx_pkt_ram; - for(i=1; i<=iadev->num_rx_desc; i++) - { - memset_io(buf_desc_ptr, 0, sizeof(*buf_desc_ptr)); - buf_desc_ptr->buf_start_hi = rx_pkt_start >> 16; - buf_desc_ptr->buf_start_lo = rx_pkt_start & 0x0000ffff; - buf_desc_ptr++; - rx_pkt_start += iadev->rx_buf_sz; - } - IF_INIT(printk("Rx Buffer desc ptr: 0x%p\n", buf_desc_ptr);) - i = FREE_BUF_DESC_Q*iadev->memSize; - writew(i >> 16, iadev->reass_reg+REASS_QUEUE_BASE); - writew(i, iadev->reass_reg+FREEQ_ST_ADR); - writew(i+iadev->num_rx_desc*sizeof(u_short), - iadev->reass_reg+FREEQ_ED_ADR); - writew(i, iadev->reass_reg+FREEQ_RD_PTR); - writew(i+iadev->num_rx_desc*sizeof(u_short), - iadev->reass_reg+FREEQ_WR_PTR); - /* Fill the FREEQ with all the free descriptors. */ - freeq_st_adr = readw(iadev->reass_reg+FREEQ_ST_ADR); - freeq_start = (u_short *)(iadev->reass_ram+freeq_st_adr); - for(i=1; i<=iadev->num_rx_desc; i++) - { - *freeq_start = (u_short)i; - freeq_start++; - } - IF_INIT(printk("freeq_start: 0x%p\n", freeq_start);) - /* Packet Complete Queue */ - i = (PKT_COMP_Q * iadev->memSize) & 0xffff; - writew(i, iadev->reass_reg+PCQ_ST_ADR); - writew(i+iadev->num_vc*sizeof(u_short), iadev->reass_reg+PCQ_ED_ADR); - writew(i, iadev->reass_reg+PCQ_RD_PTR); - writew(i, iadev->reass_reg+PCQ_WR_PTR); - - /* Exception Queue */ - i = (EXCEPTION_Q * iadev->memSize) & 0xffff; - writew(i, iadev->reass_reg+EXCP_Q_ST_ADR); - writew(i + NUM_RX_EXCP * sizeof(RX_ERROR_Q), - iadev->reass_reg+EXCP_Q_ED_ADR); - writew(i, iadev->reass_reg+EXCP_Q_RD_PTR); - writew(i, iadev->reass_reg+EXCP_Q_WR_PTR); - - /* Load local copy of FREEQ and PCQ ptrs */ - iadev->rfL.fdq_st = readw(iadev->reass_reg+FREEQ_ST_ADR) & 0xffff; - iadev->rfL.fdq_ed = readw(iadev->reass_reg+FREEQ_ED_ADR) & 0xffff ; - iadev->rfL.fdq_rd = readw(iadev->reass_reg+FREEQ_RD_PTR) & 0xffff; - iadev->rfL.fdq_wr = readw(iadev->reass_reg+FREEQ_WR_PTR) & 0xffff; - iadev->rfL.pcq_st = readw(iadev->reass_reg+PCQ_ST_ADR) & 0xffff; - iadev->rfL.pcq_ed = readw(iadev->reass_reg+PCQ_ED_ADR) & 0xffff; - iadev->rfL.pcq_rd = readw(iadev->reass_reg+PCQ_RD_PTR) & 0xffff; - iadev->rfL.pcq_wr = readw(iadev->reass_reg+PCQ_WR_PTR) & 0xffff; - - IF_INIT(printk("INIT:pcq_st:0x%x pcq_ed:0x%x pcq_rd:0x%x pcq_wr:0x%x", - iadev->rfL.pcq_st, iadev->rfL.pcq_ed, iadev->rfL.pcq_rd, - iadev->rfL.pcq_wr);) - /* just for check - no VP TBL */ - /* VP Table */ - /* writew(0x0b80, iadev->reass_reg+VP_LKUP_BASE); */ - /* initialize VP Table for invalid VPIs - - I guess we can write all 1s or 0x000f in the entire memory - space or something similar. - */ - - /* This seems to work and looks right to me too !!! */ - i = REASS_TABLE * iadev->memSize; - writew((i >> 3), iadev->reass_reg+REASS_TABLE_BASE); - /* initialize Reassembly table to I don't know what ???? */ - reass_table = (u16 *)(iadev->reass_ram+i); - j = REASS_TABLE_SZ * iadev->memSize; - for(i=0; i < j; i++) - *reass_table++ = NO_AAL5_PKT; - i = 8*1024; - vcsize_sel = 0; - while (i != iadev->num_vc) { - i /= 2; - vcsize_sel++; - } - i = RX_VC_TABLE * iadev->memSize; - writew(((i>>3) & 0xfff8) | vcsize_sel, iadev->reass_reg+VC_LKUP_BASE); - vc_table = (u16 *)(iadev->reass_ram+RX_VC_TABLE*iadev->memSize); - j = RX_VC_TABLE_SZ * iadev->memSize; - for(i = 0; i < j; i++) - { - /* shift the reassembly pointer by 3 + lower 3 bits of - vc_lkup_base register (=3 for 1K VCs) and the last byte - is those low 3 bits. - Shall program this later. - */ - *vc_table = (i << 6) | 15; /* for invalid VCI */ - vc_table++; - } - /* ABR VC table */ - i = ABR_VC_TABLE * iadev->memSize; - writew(i >> 3, iadev->reass_reg+ABR_LKUP_BASE); - - i = ABR_VC_TABLE * iadev->memSize; - abr_vc_table = (struct abr_vc_table *)(iadev->reass_ram+i); - j = REASS_TABLE_SZ * iadev->memSize; - memset ((char*)abr_vc_table, 0, j * sizeof(*abr_vc_table)); - for(i = 0; i < j; i++) { - abr_vc_table->rdf = 0x0003; - abr_vc_table->air = 0x5eb1; - abr_vc_table++; - } - - /* Initialize other registers */ - - /* VP Filter Register set for VC Reassembly only */ - writew(0xff00, iadev->reass_reg+VP_FILTER); - writew(0, iadev->reass_reg+XTRA_RM_OFFSET); - writew(0x1, iadev->reass_reg+PROTOCOL_ID); - - /* Packet Timeout Count related Registers : - Set packet timeout to occur in about 3 seconds - Set Packet Aging Interval count register to overflow in about 4 us - */ - writew(0xF6F8, iadev->reass_reg+PKT_TM_CNT ); - - i = (j >> 6) & 0xFF; - j += 2 * (j - 1); - i |= ((j << 2) & 0xFF00); - writew(i, iadev->reass_reg+TMOUT_RANGE); - - /* initiate the desc_tble */ - for(i=0; inum_tx_desc;i++) - iadev->desc_tbl[i].timestamp = 0; - - /* to clear the interrupt status register - read it */ - readw(iadev->reass_reg+REASS_INTR_STATUS_REG); - - /* Mask Register - clear it */ - writew(~(RX_FREEQ_EMPT|RX_PKT_RCVD), iadev->reass_reg+REASS_MASK_REG); - - skb_queue_head_init(&iadev->rx_dma_q); - iadev->rx_free_desc_qhead = NULL; - - iadev->rx_open = kcalloc(iadev->num_vc, sizeof(void *), GFP_KERNEL); - if (!iadev->rx_open) { - printk(KERN_ERR DEV_LABEL "itf %d couldn't get free page\n", - dev->number); - goto err_free_dle; - } - - iadev->rxing = 1; - iadev->rx_pkt_cnt = 0; - /* Mode Register */ - writew(R_ONLINE, iadev->reass_reg+MODE_REG); - return 0; - -err_free_dle: - dma_free_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, iadev->rx_dle_q.start, - iadev->rx_dle_dma); -err_out: - return -ENOMEM; -} - - -/* - The memory map suggested in appendix A and the coding for it. - Keeping it around just in case we change our mind later. - - Buffer descr 0x0000 (128 - 4K) - UBR sched 0x1000 (1K - 4K) - UBR Wait q 0x2000 (1K - 4K) - Commn queues 0x3000 Packet Ready, Trasmit comp(0x3100) - (128 - 256) each - extended VC 0x4000 (1K - 8K) - ABR sched 0x6000 and ABR wait queue (1K - 2K) each - CBR sched 0x7000 (as needed) - VC table 0x8000 (1K - 32K) -*/ - -static void tx_intr(struct atm_dev *dev) -{ - IADEV *iadev; - unsigned short status; - unsigned long flags; - - iadev = INPH_IA_DEV(dev); - - status = readl(iadev->seg_reg+SEG_INTR_STATUS_REG); - if (status & TRANSMIT_DONE){ - - IF_EVENT(printk("Transmit Done Intr logic run\n");) - spin_lock_irqsave(&iadev->tx_lock, flags); - ia_tx_poll(iadev); - spin_unlock_irqrestore(&iadev->tx_lock, flags); - writew(TRANSMIT_DONE, iadev->seg_reg+SEG_INTR_STATUS_REG); - if (iadev->close_pending) - wake_up(&iadev->close_wait); - } - if (status & TCQ_NOT_EMPTY) - { - IF_EVENT(printk("TCQ_NOT_EMPTY int received\n");) - } -} - -static void tx_dle_intr(struct atm_dev *dev) -{ - IADEV *iadev; - struct dle *dle, *cur_dle; - struct sk_buff *skb; - struct atm_vcc *vcc; - struct ia_vcc *iavcc; - u_int dle_lp; - unsigned long flags; - - iadev = INPH_IA_DEV(dev); - spin_lock_irqsave(&iadev->tx_lock, flags); - dle = iadev->tx_dle_q.read; - dle_lp = readl(iadev->dma+IPHASE5575_TX_LIST_ADDR) & - (sizeof(struct dle)*DLE_ENTRIES - 1); - cur_dle = (struct dle*)(iadev->tx_dle_q.start + (dle_lp >> 4)); - while (dle != cur_dle) - { - /* free the DMAed skb */ - skb = skb_dequeue(&iadev->tx_dma_q); - if (!skb) break; - - /* Revenge of the 2 dle (skb + trailer) used in ia_pkt_tx() */ - if (!((dle - iadev->tx_dle_q.start)%(2*sizeof(struct dle)))) { - dma_unmap_single(&iadev->pci->dev, dle->sys_pkt_addr, skb->len, - DMA_TO_DEVICE); - } - vcc = ATM_SKB(skb)->vcc; - if (!vcc) { - printk("tx_dle_intr: vcc is null\n"); - spin_unlock_irqrestore(&iadev->tx_lock, flags); - dev_kfree_skb_any(skb); - - return; - } - iavcc = INPH_IA_VCC(vcc); - if (!iavcc) { - printk("tx_dle_intr: iavcc is null\n"); - spin_unlock_irqrestore(&iadev->tx_lock, flags); - dev_kfree_skb_any(skb); - return; - } - if (vcc->qos.txtp.pcr >= iadev->rate_limit) { - if ((vcc->pop) && (skb->len != 0)) - { - vcc->pop(vcc, skb); - } - else { - dev_kfree_skb_any(skb); - } - } - else { /* Hold the rate-limited skb for flow control */ - IA_SKB_STATE(skb) |= IA_DLED; - skb_queue_tail(&iavcc->txing_skb, skb); - } - IF_EVENT(printk("tx_dle_intr: enque skb = 0x%p \n", skb);) - if (++dle == iadev->tx_dle_q.end) - dle = iadev->tx_dle_q.start; - } - iadev->tx_dle_q.read = dle; - spin_unlock_irqrestore(&iadev->tx_lock, flags); -} - -static int open_tx(struct atm_vcc *vcc) -{ - struct ia_vcc *ia_vcc; - IADEV *iadev; - struct main_vc *vc; - struct ext_vc *evc; - int ret; - IF_EVENT(printk("iadev: open_tx entered vcc->vci = %d\n", vcc->vci);) - if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0; - iadev = INPH_IA_DEV(vcc->dev); - - if (iadev->phy_type & FE_25MBIT_PHY) { - if (vcc->qos.txtp.traffic_class == ATM_ABR) { - printk("IA: ABR not support\n"); - return -EINVAL; - } - if (vcc->qos.txtp.traffic_class == ATM_CBR) { - printk("IA: CBR not support\n"); - return -EINVAL; - } - } - ia_vcc = INPH_IA_VCC(vcc); - memset((caddr_t)ia_vcc, 0, sizeof(*ia_vcc)); - if (vcc->qos.txtp.max_sdu > - (iadev->tx_buf_sz - sizeof(struct cpcs_trailer))){ - printk("IA: SDU size over (%d) the configured SDU size %d\n", - vcc->qos.txtp.max_sdu,iadev->tx_buf_sz); - vcc->dev_data = NULL; - kfree(ia_vcc); - return -EINVAL; - } - ia_vcc->vc_desc_cnt = 0; - ia_vcc->txing = 1; - - /* find pcr */ - if (vcc->qos.txtp.max_pcr == ATM_MAX_PCR) - vcc->qos.txtp.pcr = iadev->LineRate; - else if ((vcc->qos.txtp.max_pcr == 0)&&( vcc->qos.txtp.pcr <= 0)) - vcc->qos.txtp.pcr = iadev->LineRate; - else if ((vcc->qos.txtp.max_pcr > vcc->qos.txtp.pcr) && (vcc->qos.txtp.max_pcr> 0)) - vcc->qos.txtp.pcr = vcc->qos.txtp.max_pcr; - if (vcc->qos.txtp.pcr > iadev->LineRate) - vcc->qos.txtp.pcr = iadev->LineRate; - ia_vcc->pcr = vcc->qos.txtp.pcr; - - if (ia_vcc->pcr > (iadev->LineRate / 6) ) ia_vcc->ltimeout = HZ / 10; - else if (ia_vcc->pcr > (iadev->LineRate / 130)) ia_vcc->ltimeout = HZ; - else if (ia_vcc->pcr <= 170) ia_vcc->ltimeout = 16 * HZ; - else ia_vcc->ltimeout = 2700 * HZ / ia_vcc->pcr; - if (ia_vcc->pcr < iadev->rate_limit) - skb_queue_head_init (&ia_vcc->txing_skb); - if (ia_vcc->pcr < iadev->rate_limit) { - struct sock *sk = sk_atm(vcc); - - if (vcc->qos.txtp.max_sdu != 0) { - if (ia_vcc->pcr > 60000) - sk->sk_sndbuf = vcc->qos.txtp.max_sdu * 5; - else if (ia_vcc->pcr > 2000) - sk->sk_sndbuf = vcc->qos.txtp.max_sdu * 4; - else - sk->sk_sndbuf = vcc->qos.txtp.max_sdu * 3; - } - else - sk->sk_sndbuf = 24576; - } - - vc = (struct main_vc *)iadev->MAIN_VC_TABLE_ADDR; - evc = (struct ext_vc *)iadev->EXT_VC_TABLE_ADDR; - vc += vcc->vci; - evc += vcc->vci; - memset((caddr_t)vc, 0, sizeof(*vc)); - memset((caddr_t)evc, 0, sizeof(*evc)); - - /* store the most significant 4 bits of vci as the last 4 bits - of first part of atm header. - store the last 12 bits of vci as first 12 bits of the second - part of the atm header. - */ - evc->atm_hdr1 = (vcc->vci >> 12) & 0x000f; - evc->atm_hdr2 = (vcc->vci & 0x0fff) << 4; - - /* check the following for different traffic classes */ - if (vcc->qos.txtp.traffic_class == ATM_UBR) - { - vc->type = UBR; - vc->status = CRC_APPEND; - vc->acr = cellrate_to_float(iadev->LineRate); - if (vcc->qos.txtp.pcr > 0) - vc->acr = cellrate_to_float(vcc->qos.txtp.pcr); - IF_UBR(printk("UBR: txtp.pcr = 0x%x f_rate = 0x%x\n", - vcc->qos.txtp.max_pcr,vc->acr);) - } - else if (vcc->qos.txtp.traffic_class == ATM_ABR) - { srv_cls_param_t srv_p; - IF_ABR(printk("Tx ABR VCC\n");) - init_abr_vc(iadev, &srv_p); - if (vcc->qos.txtp.pcr > 0) - srv_p.pcr = vcc->qos.txtp.pcr; - if (vcc->qos.txtp.min_pcr > 0) { - int tmpsum = iadev->sum_mcr+iadev->sum_cbr+vcc->qos.txtp.min_pcr; - if (tmpsum > iadev->LineRate) - return -EBUSY; - srv_p.mcr = vcc->qos.txtp.min_pcr; - iadev->sum_mcr += vcc->qos.txtp.min_pcr; - } - else srv_p.mcr = 0; - if (vcc->qos.txtp.icr) - srv_p.icr = vcc->qos.txtp.icr; - if (vcc->qos.txtp.tbe) - srv_p.tbe = vcc->qos.txtp.tbe; - if (vcc->qos.txtp.frtt) - srv_p.frtt = vcc->qos.txtp.frtt; - if (vcc->qos.txtp.rif) - srv_p.rif = vcc->qos.txtp.rif; - if (vcc->qos.txtp.rdf) - srv_p.rdf = vcc->qos.txtp.rdf; - if (vcc->qos.txtp.nrm_pres) - srv_p.nrm = vcc->qos.txtp.nrm; - if (vcc->qos.txtp.trm_pres) - srv_p.trm = vcc->qos.txtp.trm; - if (vcc->qos.txtp.adtf_pres) - srv_p.adtf = vcc->qos.txtp.adtf; - if (vcc->qos.txtp.cdf_pres) - srv_p.cdf = vcc->qos.txtp.cdf; - if (srv_p.icr > srv_p.pcr) - srv_p.icr = srv_p.pcr; - IF_ABR(printk("ABR:vcc->qos.txtp.max_pcr = %d mcr = %d\n", - srv_p.pcr, srv_p.mcr);) - ia_open_abr_vc(iadev, &srv_p, vcc, 1); - } else if (vcc->qos.txtp.traffic_class == ATM_CBR) { - if (iadev->phy_type & FE_25MBIT_PHY) { - printk("IA: CBR not support\n"); - return -EINVAL; - } - if (vcc->qos.txtp.max_pcr > iadev->LineRate) { - IF_CBR(printk("PCR is not available\n");) - return -1; - } - vc->type = CBR; - vc->status = CRC_APPEND; - if ((ret = ia_cbr_setup (iadev, vcc)) < 0) { - return ret; - } - } else { - printk("iadev: Non UBR, ABR and CBR traffic not supported\n"); - } - - iadev->testTable[vcc->vci]->vc_status |= VC_ACTIVE; - IF_EVENT(printk("ia open_tx returning \n");) - return 0; -} - - -static int tx_init(struct atm_dev *dev) -{ - IADEV *iadev; - struct tx_buf_desc *buf_desc_ptr; - unsigned int tx_pkt_start; - void *dle_addr; - int i; - u_short tcq_st_adr; - u_short *tcq_start; - u_short prq_st_adr; - u_short *prq_start; - struct main_vc *vc; - struct ext_vc *evc; - u_short tmp16; - u32 vcsize_sel; - - iadev = INPH_IA_DEV(dev); - spin_lock_init(&iadev->tx_lock); - - IF_INIT(printk("Tx MASK REG: 0x%0x\n", - readw(iadev->seg_reg+SEG_MASK_REG));) - - /* Allocate 4k (boundary aligned) bytes */ - dle_addr = dma_alloc_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, - &iadev->tx_dle_dma, GFP_KERNEL); - if (!dle_addr) { - printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n"); - goto err_out; - } - iadev->tx_dle_q.start = (struct dle*)dle_addr; - iadev->tx_dle_q.read = iadev->tx_dle_q.start; - iadev->tx_dle_q.write = iadev->tx_dle_q.start; - iadev->tx_dle_q.end = (struct dle*)((unsigned long)dle_addr+sizeof(struct dle)*DLE_ENTRIES); - - /* write the upper 20 bits of the start address to tx list address register */ - writel(iadev->tx_dle_dma & 0xfffff000, - iadev->dma + IPHASE5575_TX_LIST_ADDR); - writew(0xffff, iadev->seg_reg+SEG_MASK_REG); - writew(0, iadev->seg_reg+MODE_REG_0); - writew(RESET_SEG, iadev->seg_reg+SEG_COMMAND_REG); - iadev->MAIN_VC_TABLE_ADDR = iadev->seg_ram+MAIN_VC_TABLE*iadev->memSize; - iadev->EXT_VC_TABLE_ADDR = iadev->seg_ram+EXT_VC_TABLE*iadev->memSize; - iadev->ABR_SCHED_TABLE_ADDR=iadev->seg_ram+ABR_SCHED_TABLE*iadev->memSize; - - /* - Transmit side control memory map - -------------------------------- - Buffer descr 0x0000 (128 - 4K) - Commn queues 0x1000 Transmit comp, Packet ready(0x1400) - (512 - 1K) each - TCQ - 4K, PRQ - 5K - CBR Table 0x1800 (as needed) - 6K - UBR Table 0x3000 (1K - 4K) - 12K - UBR Wait queue 0x4000 (1K - 4K) - 16K - ABR sched 0x5000 and ABR wait queue (1K - 2K) each - ABR Tbl - 20K, ABR Wq - 22K - extended VC 0x6000 (1K - 8K) - 24K - VC Table 0x8000 (1K - 32K) - 32K - - Between 0x2000 (8K) and 0x3000 (12K) there is 4K space left for VBR Tbl - and Wait q, which can be allotted later. - */ - - /* Buffer Descriptor Table Base address */ - writew(TX_DESC_BASE, iadev->seg_reg+SEG_DESC_BASE); - - /* initialize each entry in the buffer descriptor table */ - buf_desc_ptr =(struct tx_buf_desc *)(iadev->seg_ram+TX_DESC_BASE); - memset((caddr_t)buf_desc_ptr, 0, sizeof(*buf_desc_ptr)); - buf_desc_ptr++; - tx_pkt_start = TX_PACKET_RAM; - for(i=1; i<=iadev->num_tx_desc; i++) - { - memset((caddr_t)buf_desc_ptr, 0, sizeof(*buf_desc_ptr)); - buf_desc_ptr->desc_mode = AAL5; - buf_desc_ptr->buf_start_hi = tx_pkt_start >> 16; - buf_desc_ptr->buf_start_lo = tx_pkt_start & 0x0000ffff; - buf_desc_ptr++; - tx_pkt_start += iadev->tx_buf_sz; - } - iadev->tx_buf = kmalloc_objs(*iadev->tx_buf, iadev->num_tx_desc); - if (!iadev->tx_buf) { - printk(KERN_ERR DEV_LABEL " couldn't get mem\n"); - goto err_free_dle; - } - for (i= 0; i< iadev->num_tx_desc; i++) - { - struct cpcs_trailer *cpcs; - - cpcs = kmalloc_obj(*cpcs, GFP_KERNEL | GFP_DMA); - if(!cpcs) { - printk(KERN_ERR DEV_LABEL " couldn't get freepage\n"); - goto err_free_tx_bufs; - } - iadev->tx_buf[i].cpcs = cpcs; - iadev->tx_buf[i].dma_addr = dma_map_single(&iadev->pci->dev, - cpcs, - sizeof(*cpcs), - DMA_TO_DEVICE); - } - iadev->desc_tbl = kmalloc_objs(*iadev->desc_tbl, iadev->num_tx_desc); - if (!iadev->desc_tbl) { - printk(KERN_ERR DEV_LABEL " couldn't get mem\n"); - goto err_free_all_tx_bufs; - } - - /* Communication Queues base address */ - i = TX_COMP_Q * iadev->memSize; - writew(i >> 16, iadev->seg_reg+SEG_QUEUE_BASE); - - /* Transmit Complete Queue */ - writew(i, iadev->seg_reg+TCQ_ST_ADR); - writew(i, iadev->seg_reg+TCQ_RD_PTR); - writew(i+iadev->num_tx_desc*sizeof(u_short),iadev->seg_reg+TCQ_WR_PTR); - iadev->host_tcq_wr = i + iadev->num_tx_desc*sizeof(u_short); - writew(i+2 * iadev->num_tx_desc * sizeof(u_short), - iadev->seg_reg+TCQ_ED_ADR); - /* Fill the TCQ with all the free descriptors. */ - tcq_st_adr = readw(iadev->seg_reg+TCQ_ST_ADR); - tcq_start = (u_short *)(iadev->seg_ram+tcq_st_adr); - for(i=1; i<=iadev->num_tx_desc; i++) - { - *tcq_start = (u_short)i; - tcq_start++; - } - - /* Packet Ready Queue */ - i = PKT_RDY_Q * iadev->memSize; - writew(i, iadev->seg_reg+PRQ_ST_ADR); - writew(i+2 * iadev->num_tx_desc * sizeof(u_short), - iadev->seg_reg+PRQ_ED_ADR); - writew(i, iadev->seg_reg+PRQ_RD_PTR); - writew(i, iadev->seg_reg+PRQ_WR_PTR); - - /* Load local copy of PRQ and TCQ ptrs */ - iadev->ffL.prq_st = readw(iadev->seg_reg+PRQ_ST_ADR) & 0xffff; - iadev->ffL.prq_ed = readw(iadev->seg_reg+PRQ_ED_ADR) & 0xffff; - iadev->ffL.prq_wr = readw(iadev->seg_reg+PRQ_WR_PTR) & 0xffff; - - iadev->ffL.tcq_st = readw(iadev->seg_reg+TCQ_ST_ADR) & 0xffff; - iadev->ffL.tcq_ed = readw(iadev->seg_reg+TCQ_ED_ADR) & 0xffff; - iadev->ffL.tcq_rd = readw(iadev->seg_reg+TCQ_RD_PTR) & 0xffff; - - /* Just for safety initializing the queue to have desc 1 always */ - /* Fill the PRQ with all the free descriptors. */ - prq_st_adr = readw(iadev->seg_reg+PRQ_ST_ADR); - prq_start = (u_short *)(iadev->seg_ram+prq_st_adr); - for(i=1; i<=iadev->num_tx_desc; i++) - { - *prq_start = (u_short)0; /* desc 1 in all entries */ - prq_start++; - } - /* CBR Table */ - IF_INIT(printk("Start CBR Init\n");) -#if 1 /* for 1K VC board, CBR_PTR_BASE is 0 */ - writew(0,iadev->seg_reg+CBR_PTR_BASE); -#else /* Charlie's logic is wrong ? */ - tmp16 = (iadev->seg_ram+CBR_SCHED_TABLE*iadev->memSize)>>17; - IF_INIT(printk("cbr_ptr_base = 0x%x ", tmp16);) - writew(tmp16,iadev->seg_reg+CBR_PTR_BASE); -#endif - - IF_INIT(printk("value in register = 0x%x\n", - readw(iadev->seg_reg+CBR_PTR_BASE));) - tmp16 = (CBR_SCHED_TABLE*iadev->memSize) >> 1; - writew(tmp16, iadev->seg_reg+CBR_TAB_BEG); - IF_INIT(printk("cbr_tab_beg = 0x%x in reg = 0x%x \n", tmp16, - readw(iadev->seg_reg+CBR_TAB_BEG));) - writew(tmp16, iadev->seg_reg+CBR_TAB_END+1); // CBR_PTR; - tmp16 = (CBR_SCHED_TABLE*iadev->memSize + iadev->num_vc*6 - 2) >> 1; - writew(tmp16, iadev->seg_reg+CBR_TAB_END); - IF_INIT(printk("iadev->seg_reg = 0x%p CBR_PTR_BASE = 0x%x\n", - iadev->seg_reg, readw(iadev->seg_reg+CBR_PTR_BASE));) - IF_INIT(printk("CBR_TAB_BEG = 0x%x, CBR_TAB_END = 0x%x, CBR_PTR = 0x%x\n", - readw(iadev->seg_reg+CBR_TAB_BEG), readw(iadev->seg_reg+CBR_TAB_END), - readw(iadev->seg_reg+CBR_TAB_END+1));) - - /* Initialize the CBR Schedualing Table */ - memset_io(iadev->seg_ram+CBR_SCHED_TABLE*iadev->memSize, - 0, iadev->num_vc*6); - iadev->CbrRemEntries = iadev->CbrTotEntries = iadev->num_vc*3; - iadev->CbrEntryPt = 0; - iadev->Granularity = MAX_ATM_155 / iadev->CbrTotEntries; - iadev->NumEnabledCBR = 0; - - /* UBR scheduling Table and wait queue */ - /* initialize all bytes of UBR scheduler table and wait queue to 0 - - SCHEDSZ is 1K (# of entries). - - UBR Table size is 4K - - UBR wait queue is 4K - since the table and wait queues are contiguous, all the bytes - can be initialized by one memeset. - */ - - vcsize_sel = 0; - i = 8*1024; - while (i != iadev->num_vc) { - i /= 2; - vcsize_sel++; - } - - i = MAIN_VC_TABLE * iadev->memSize; - writew(vcsize_sel | ((i >> 8) & 0xfff8),iadev->seg_reg+VCT_BASE); - i = EXT_VC_TABLE * iadev->memSize; - writew((i >> 8) & 0xfffe, iadev->seg_reg+VCTE_BASE); - i = UBR_SCHED_TABLE * iadev->memSize; - writew((i & 0xffff) >> 11, iadev->seg_reg+UBR_SBPTR_BASE); - i = UBR_WAIT_Q * iadev->memSize; - writew((i >> 7) & 0xffff, iadev->seg_reg+UBRWQ_BASE); - memset((caddr_t)(iadev->seg_ram+UBR_SCHED_TABLE*iadev->memSize), - 0, iadev->num_vc*8); - /* ABR scheduling Table(0x5000-0x57ff) and wait queue(0x5800-0x5fff)*/ - /* initialize all bytes of ABR scheduler table and wait queue to 0 - - SCHEDSZ is 1K (# of entries). - - ABR Table size is 2K - - ABR wait queue is 2K - since the table and wait queues are contiguous, all the bytes - can be initialized by one memeset. - */ - i = ABR_SCHED_TABLE * iadev->memSize; - writew((i >> 11) & 0xffff, iadev->seg_reg+ABR_SBPTR_BASE); - i = ABR_WAIT_Q * iadev->memSize; - writew((i >> 7) & 0xffff, iadev->seg_reg+ABRWQ_BASE); - - i = ABR_SCHED_TABLE*iadev->memSize; - memset((caddr_t)(iadev->seg_ram+i), 0, iadev->num_vc*4); - vc = (struct main_vc *)iadev->MAIN_VC_TABLE_ADDR; - evc = (struct ext_vc *)iadev->EXT_VC_TABLE_ADDR; - iadev->testTable = kmalloc_objs(*iadev->testTable, iadev->num_vc); - if (!iadev->testTable) { - printk("Get freepage failed\n"); - goto err_free_desc_tbl; - } - for(i=0; inum_vc; i++) - { - memset((caddr_t)vc, 0, sizeof(*vc)); - memset((caddr_t)evc, 0, sizeof(*evc)); - iadev->testTable[i] = kmalloc_obj(struct testTable_t); - if (!iadev->testTable[i]) - goto err_free_test_tables; - iadev->testTable[i]->lastTime = 0; - iadev->testTable[i]->fract = 0; - iadev->testTable[i]->vc_status = VC_UBR; - vc++; - evc++; - } - - /* Other Initialization */ - - /* Max Rate Register */ - if (iadev->phy_type & FE_25MBIT_PHY) { - writew(RATE25, iadev->seg_reg+MAXRATE); - writew((UBR_EN | (0x23 << 2)), iadev->seg_reg+STPARMS); - } - else { - writew(cellrate_to_float(iadev->LineRate),iadev->seg_reg+MAXRATE); - writew((UBR_EN | ABR_EN | (0x23 << 2)), iadev->seg_reg+STPARMS); - } - /* Set Idle Header Reigisters to be sure */ - writew(0, iadev->seg_reg+IDLEHEADHI); - writew(0, iadev->seg_reg+IDLEHEADLO); - - /* Program ABR UBR Priority Register as PRI_ABR_UBR_EQUAL */ - writew(0xaa00, iadev->seg_reg+ABRUBR_ARB); - - iadev->close_pending = 0; - init_waitqueue_head(&iadev->close_wait); - init_waitqueue_head(&iadev->timeout_wait); - skb_queue_head_init(&iadev->tx_dma_q); - ia_init_rtn_q(&iadev->tx_return_q); - - /* RM Cell Protocol ID and Message Type */ - writew(RM_TYPE_4_0, iadev->seg_reg+RM_TYPE); - skb_queue_head_init (&iadev->tx_backlog); - - /* Mode Register 1 */ - writew(MODE_REG_1_VAL, iadev->seg_reg+MODE_REG_1); - - /* Mode Register 0 */ - writew(T_ONLINE, iadev->seg_reg+MODE_REG_0); - - /* Interrupt Status Register - read to clear */ - readw(iadev->seg_reg+SEG_INTR_STATUS_REG); - - /* Interrupt Mask Reg- don't mask TCQ_NOT_EMPTY interrupt generation */ - writew(~(TRANSMIT_DONE | TCQ_NOT_EMPTY), iadev->seg_reg+SEG_MASK_REG); - writew(TRANSMIT_DONE, iadev->seg_reg+SEG_INTR_STATUS_REG); - iadev->tx_pkt_cnt = 0; - iadev->rate_limit = iadev->LineRate / 3; - - return 0; - -err_free_test_tables: - while (--i >= 0) - kfree(iadev->testTable[i]); - kfree(iadev->testTable); -err_free_desc_tbl: - kfree(iadev->desc_tbl); -err_free_all_tx_bufs: - i = iadev->num_tx_desc; -err_free_tx_bufs: - while (--i >= 0) { - struct cpcs_trailer_desc *desc = iadev->tx_buf + i; - - dma_unmap_single(&iadev->pci->dev, desc->dma_addr, - sizeof(*desc->cpcs), DMA_TO_DEVICE); - kfree(desc->cpcs); - } - kfree(iadev->tx_buf); -err_free_dle: - dma_free_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, iadev->tx_dle_q.start, - iadev->tx_dle_dma); -err_out: - return -ENOMEM; -} - -static irqreturn_t ia_int(int irq, void *dev_id) -{ - struct atm_dev *dev; - IADEV *iadev; - unsigned int status; - int handled = 0; - - dev = dev_id; - iadev = INPH_IA_DEV(dev); - while( (status = readl(iadev->reg+IPHASE5575_BUS_STATUS_REG) & 0x7f)) - { - handled = 1; - IF_EVENT(printk("ia_int: status = 0x%x\n", status);) - if (status & STAT_REASSINT) - { - /* do something */ - IF_EVENT(printk("REASSINT Bus status reg: %08x\n", status);) - rx_intr(dev); - } - if (status & STAT_DLERINT) - { - /* Clear this bit by writing a 1 to it. */ - writel(STAT_DLERINT, iadev->reg + IPHASE5575_BUS_STATUS_REG); - rx_dle_intr(dev); - } - if (status & STAT_SEGINT) - { - /* do something */ - IF_EVENT(printk("IA: tx_intr \n");) - tx_intr(dev); - } - if (status & STAT_DLETINT) - { - writel(STAT_DLETINT, iadev->reg + IPHASE5575_BUS_STATUS_REG); - tx_dle_intr(dev); - } - if (status & (STAT_FEINT | STAT_ERRINT | STAT_MARKINT)) - { - if (status & STAT_FEINT) - ia_frontend_intr(iadev); - } - } - return IRQ_RETVAL(handled); -} - - - -/*----------------------------- entries --------------------------------*/ -static int get_esi(struct atm_dev *dev) -{ - IADEV *iadev; - int i; - u32 mac1; - u16 mac2; - - iadev = INPH_IA_DEV(dev); - mac1 = cpu_to_be32(le32_to_cpu(readl( - iadev->reg+IPHASE5575_MAC1))); - mac2 = cpu_to_be16(le16_to_cpu(readl(iadev->reg+IPHASE5575_MAC2))); - IF_INIT(printk("ESI: 0x%08x%04x\n", mac1, mac2);) - for (i=0; iesi[i] = mac1 >>(8*(MAC1_LEN-1-i)); - - for (i=0; iesi[i+MAC1_LEN] = mac2 >>(8*(MAC2_LEN - 1 -i)); - return 0; -} - -static int reset_sar(struct atm_dev *dev) -{ - IADEV *iadev; - int i, error; - unsigned int pci[64]; - - iadev = INPH_IA_DEV(dev); - for (i = 0; i < 64; i++) { - error = pci_read_config_dword(iadev->pci, i * 4, &pci[i]); - if (error != PCIBIOS_SUCCESSFUL) - return error; - } - writel(0, iadev->reg+IPHASE5575_EXT_RESET); - for (i = 0; i < 64; i++) { - error = pci_write_config_dword(iadev->pci, i * 4, pci[i]); - if (error != PCIBIOS_SUCCESSFUL) - return error; - } - udelay(5); - return 0; -} - - -static int ia_init(struct atm_dev *dev) -{ - IADEV *iadev; - unsigned long real_base; - void __iomem *base; - unsigned short command; - int error, i; - - /* The device has been identified and registered. Now we read - necessary configuration info like memory base address, - interrupt number etc */ - - IF_INIT(printk(">ia_init\n");) - dev->ci_range.vpi_bits = 0; - dev->ci_range.vci_bits = NR_VCI_LD; - - iadev = INPH_IA_DEV(dev); - real_base = pci_resource_start (iadev->pci, 0); - iadev->irq = iadev->pci->irq; - - error = pci_read_config_word(iadev->pci, PCI_COMMAND, &command); - if (error) { - printk(KERN_ERR DEV_LABEL "(itf %d): init error 0x%x\n", - dev->number,error); - return -EINVAL; - } - IF_INIT(printk(DEV_LABEL "(itf %d): rev.%d,realbase=0x%lx,irq=%d\n", - dev->number, iadev->pci->revision, real_base, iadev->irq);) - - /* find mapping size of board */ - - iadev->pci_map_size = pci_resource_len(iadev->pci, 0); - - if (iadev->pci_map_size == 0x100000){ - iadev->num_vc = 4096; - dev->ci_range.vci_bits = NR_VCI_4K_LD; - iadev->memSize = 4; - } - else if (iadev->pci_map_size == 0x40000) { - iadev->num_vc = 1024; - iadev->memSize = 1; - } - else { - printk("Unknown pci_map_size = 0x%x\n", iadev->pci_map_size); - return -EINVAL; - } - IF_INIT(printk (DEV_LABEL "map size: %i\n", iadev->pci_map_size);) - - /* enable bus mastering */ - pci_set_master(iadev->pci); - - /* - * Delay at least 1us before doing any mem accesses (how 'bout 10?) - */ - udelay(10); - - /* mapping the physical address to a virtual address in address space */ - base = ioremap(real_base,iadev->pci_map_size); /* ioremap is not resolved ??? */ - - if (!base) - { - printk(DEV_LABEL " (itf %d): can't set up page mapping\n", - dev->number); - return -ENOMEM; - } - IF_INIT(printk(DEV_LABEL " (itf %d): rev.%d,base=%p,irq=%d\n", - dev->number, iadev->pci->revision, base, iadev->irq);) - - /* filling the iphase dev structure */ - iadev->mem = iadev->pci_map_size /2; - iadev->real_base = real_base; - iadev->base = base; - - /* Bus Interface Control Registers */ - iadev->reg = base + REG_BASE; - /* Segmentation Control Registers */ - iadev->seg_reg = base + SEG_BASE; - /* Reassembly Control Registers */ - iadev->reass_reg = base + REASS_BASE; - /* Front end/ DMA control registers */ - iadev->phy = base + PHY_BASE; - iadev->dma = base + PHY_BASE; - /* RAM - Segmentation RAm and Reassembly RAM */ - iadev->ram = base + ACTUAL_RAM_BASE; - iadev->seg_ram = base + ACTUAL_SEG_RAM_BASE; - iadev->reass_ram = base + ACTUAL_REASS_RAM_BASE; - - /* lets print out the above */ - IF_INIT(printk("Base addrs: %p %p %p \n %p %p %p %p\n", - iadev->reg,iadev->seg_reg,iadev->reass_reg, - iadev->phy, iadev->ram, iadev->seg_ram, - iadev->reass_ram);) - - /* lets try reading the MAC address */ - error = get_esi(dev); - if (error) { - iounmap(iadev->base); - return error; - } - printk("IA: "); - for (i=0; i < ESI_LEN; i++) - printk("%s%02X",i ? "-" : "",dev->esi[i]); - printk("\n"); - - /* reset SAR */ - if (reset_sar(dev)) { - iounmap(iadev->base); - printk("IA: reset SAR fail, please try again\n"); - return 1; - } - return 0; -} - -static void ia_update_stats(IADEV *iadev) { - if (!iadev->carrier_detect) - return; - iadev->rx_cell_cnt += readw(iadev->reass_reg+CELL_CTR0)&0xffff; - iadev->rx_cell_cnt += (readw(iadev->reass_reg+CELL_CTR1) & 0xffff) << 16; - iadev->drop_rxpkt += readw(iadev->reass_reg + DRP_PKT_CNTR ) & 0xffff; - iadev->drop_rxcell += readw(iadev->reass_reg + ERR_CNTR) & 0xffff; - iadev->tx_cell_cnt += readw(iadev->seg_reg + CELL_CTR_LO_AUTO)&0xffff; - iadev->tx_cell_cnt += (readw(iadev->seg_reg+CELL_CTR_HIGH_AUTO)&0xffff)<<16; - return; -} - -static void ia_led_timer(struct timer_list *unused) { - unsigned long flags; - static u_char blinking[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - u_char i; - static u32 ctrl_reg; - for (i = 0; i < iadev_count; i++) { - if (ia_dev[i]) { - ctrl_reg = readl(ia_dev[i]->reg+IPHASE5575_BUS_CONTROL_REG); - if (blinking[i] == 0) { - blinking[i]++; - ctrl_reg &= (~CTRL_LED); - writel(ctrl_reg, ia_dev[i]->reg+IPHASE5575_BUS_CONTROL_REG); - ia_update_stats(ia_dev[i]); - } - else { - blinking[i] = 0; - ctrl_reg |= CTRL_LED; - writel(ctrl_reg, ia_dev[i]->reg+IPHASE5575_BUS_CONTROL_REG); - spin_lock_irqsave(&ia_dev[i]->tx_lock, flags); - if (ia_dev[i]->close_pending) - wake_up(&ia_dev[i]->close_wait); - ia_tx_poll(ia_dev[i]); - spin_unlock_irqrestore(&ia_dev[i]->tx_lock, flags); - } - } - } - mod_timer(&ia_timer, jiffies + HZ / 4); - return; -} - -static void ia_phy_put(struct atm_dev *dev, unsigned char value, - unsigned long addr) -{ - writel(value, INPH_IA_DEV(dev)->phy+addr); -} - -static unsigned char ia_phy_get(struct atm_dev *dev, unsigned long addr) -{ - return readl(INPH_IA_DEV(dev)->phy+addr); -} - -static void ia_free_tx(IADEV *iadev) -{ - int i; - - kfree(iadev->desc_tbl); - for (i = 0; i < iadev->num_vc; i++) - kfree(iadev->testTable[i]); - kfree(iadev->testTable); - for (i = 0; i < iadev->num_tx_desc; i++) { - struct cpcs_trailer_desc *desc = iadev->tx_buf + i; - - dma_unmap_single(&iadev->pci->dev, desc->dma_addr, - sizeof(*desc->cpcs), DMA_TO_DEVICE); - kfree(desc->cpcs); - } - kfree(iadev->tx_buf); - dma_free_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, iadev->tx_dle_q.start, - iadev->tx_dle_dma); -} - -static void ia_free_rx(IADEV *iadev) -{ - kfree(iadev->rx_open); - dma_free_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, iadev->rx_dle_q.start, - iadev->rx_dle_dma); -} - -static int ia_start(struct atm_dev *dev) -{ - IADEV *iadev; - int error; - unsigned char phy; - u32 ctrl_reg; - IF_EVENT(printk(">ia_start\n");) - iadev = INPH_IA_DEV(dev); - if (request_irq(iadev->irq, &ia_int, IRQF_SHARED, DEV_LABEL, dev)) { - printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n", - dev->number, iadev->irq); - error = -EAGAIN; - goto err_out; - } - /* @@@ should release IRQ on error */ - /* enabling memory + master */ - if ((error = pci_write_config_word(iadev->pci, - PCI_COMMAND, - PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER ))) - { - printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory+" - "master (0x%x)\n",dev->number, error); - error = -EIO; - goto err_free_irq; - } - udelay(10); - - /* Maybe we should reset the front end, initialize Bus Interface Control - Registers and see. */ - - IF_INIT(printk("Bus ctrl reg: %08x\n", - readl(iadev->reg+IPHASE5575_BUS_CONTROL_REG));) - ctrl_reg = readl(iadev->reg+IPHASE5575_BUS_CONTROL_REG); - ctrl_reg = (ctrl_reg & (CTRL_LED | CTRL_FE_RST)) - | CTRL_B8 - | CTRL_B16 - | CTRL_B32 - | CTRL_B48 - | CTRL_B64 - | CTRL_B128 - | CTRL_ERRMASK - | CTRL_DLETMASK /* shud be removed l8r */ - | CTRL_DLERMASK - | CTRL_SEGMASK - | CTRL_REASSMASK - | CTRL_FEMASK - | CTRL_CSPREEMPT; - - writel(ctrl_reg, iadev->reg+IPHASE5575_BUS_CONTROL_REG); - - IF_INIT(printk("Bus ctrl reg after initializing: %08x\n", - readl(iadev->reg+IPHASE5575_BUS_CONTROL_REG)); - printk("Bus status reg after init: %08x\n", - readl(iadev->reg+IPHASE5575_BUS_STATUS_REG));) - - ia_hw_type(iadev); - error = tx_init(dev); - if (error) - goto err_free_irq; - error = rx_init(dev); - if (error) - goto err_free_tx; - - ctrl_reg = readl(iadev->reg+IPHASE5575_BUS_CONTROL_REG); - writel(ctrl_reg | CTRL_FE_RST, iadev->reg+IPHASE5575_BUS_CONTROL_REG); - IF_INIT(printk("Bus ctrl reg after initializing: %08x\n", - readl(iadev->reg+IPHASE5575_BUS_CONTROL_REG));) - phy = 0; /* resolve compiler complaint */ - IF_INIT ( - if ((phy=ia_phy_get(dev,0)) == 0x30) - printk("IA: pm5346,rev.%d\n",phy&0x0f); - else - printk("IA: utopia,rev.%0x\n",phy);) - - if (iadev->phy_type & FE_25MBIT_PHY) - ia_mb25_init(iadev); - else if (iadev->phy_type & (FE_DS3_PHY | FE_E3_PHY)) - ia_suni_pm7345_init(iadev); - else { - error = suni_init(dev); - if (error) - goto err_free_rx; - if (dev->phy->start) { - error = dev->phy->start(dev); - if (error) - goto err_free_rx; - } - /* Get iadev->carrier_detect status */ - ia_frontend_intr(iadev); - } - return 0; - -err_free_rx: - ia_free_rx(iadev); -err_free_tx: - ia_free_tx(iadev); -err_free_irq: - free_irq(iadev->irq, dev); -err_out: - return error; -} - -static void ia_close(struct atm_vcc *vcc) -{ - DEFINE_WAIT(wait); - u16 *vc_table; - IADEV *iadev; - struct ia_vcc *ia_vcc; - struct sk_buff *skb = NULL; - struct sk_buff_head tmp_tx_backlog, tmp_vcc_backlog; - unsigned long closetime, flags; - - iadev = INPH_IA_DEV(vcc->dev); - ia_vcc = INPH_IA_VCC(vcc); - if (!ia_vcc) return; - - IF_EVENT(printk("ia_close: ia_vcc->vc_desc_cnt = %d vci = %d\n", - ia_vcc->vc_desc_cnt,vcc->vci);) - clear_bit(ATM_VF_READY,&vcc->flags); - skb_queue_head_init (&tmp_tx_backlog); - skb_queue_head_init (&tmp_vcc_backlog); - if (vcc->qos.txtp.traffic_class != ATM_NONE) { - iadev->close_pending++; - prepare_to_wait(&iadev->timeout_wait, &wait, TASK_UNINTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(500)); - finish_wait(&iadev->timeout_wait, &wait); - spin_lock_irqsave(&iadev->tx_lock, flags); - while((skb = skb_dequeue(&iadev->tx_backlog))) { - if (ATM_SKB(skb)->vcc == vcc){ - if (vcc->pop) vcc->pop(vcc, skb); - else dev_kfree_skb_any(skb); - } - else - skb_queue_tail(&tmp_tx_backlog, skb); - } - while((skb = skb_dequeue(&tmp_tx_backlog))) - skb_queue_tail(&iadev->tx_backlog, skb); - IF_EVENT(printk("IA TX Done decs_cnt = %d\n", ia_vcc->vc_desc_cnt);) - closetime = 300000 / ia_vcc->pcr; - if (closetime == 0) - closetime = 1; - spin_unlock_irqrestore(&iadev->tx_lock, flags); - wait_event_timeout(iadev->close_wait, (ia_vcc->vc_desc_cnt <= 0), closetime); - spin_lock_irqsave(&iadev->tx_lock, flags); - iadev->close_pending--; - iadev->testTable[vcc->vci]->lastTime = 0; - iadev->testTable[vcc->vci]->fract = 0; - iadev->testTable[vcc->vci]->vc_status = VC_UBR; - if (vcc->qos.txtp.traffic_class == ATM_ABR) { - if (vcc->qos.txtp.min_pcr > 0) - iadev->sum_mcr -= vcc->qos.txtp.min_pcr; - } - if (vcc->qos.txtp.traffic_class == ATM_CBR) { - ia_vcc = INPH_IA_VCC(vcc); - iadev->sum_mcr -= ia_vcc->NumCbrEntry*iadev->Granularity; - ia_cbrVc_close (vcc); - } - spin_unlock_irqrestore(&iadev->tx_lock, flags); - } - - if (vcc->qos.rxtp.traffic_class != ATM_NONE) { - // reset reass table - vc_table = (u16 *)(iadev->reass_ram+REASS_TABLE*iadev->memSize); - vc_table += vcc->vci; - *vc_table = NO_AAL5_PKT; - // reset vc table - vc_table = (u16 *)(iadev->reass_ram+RX_VC_TABLE*iadev->memSize); - vc_table += vcc->vci; - *vc_table = (vcc->vci << 6) | 15; - if (vcc->qos.rxtp.traffic_class == ATM_ABR) { - struct abr_vc_table __iomem *abr_vc_table = - (iadev->reass_ram+ABR_VC_TABLE*iadev->memSize); - abr_vc_table += vcc->vci; - abr_vc_table->rdf = 0x0003; - abr_vc_table->air = 0x5eb1; - } - // Drain the packets - rx_dle_intr(vcc->dev); - iadev->rx_open[vcc->vci] = NULL; - } - kfree(INPH_IA_VCC(vcc)); - ia_vcc = NULL; - vcc->dev_data = NULL; - clear_bit(ATM_VF_ADDR,&vcc->flags); - return; -} - -static int ia_open(struct atm_vcc *vcc) -{ - struct ia_vcc *ia_vcc; - int error; - if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) - { - IF_EVENT(printk("ia: not partially allocated resources\n");) - vcc->dev_data = NULL; - } - if (vcc->vci != ATM_VPI_UNSPEC && vcc->vpi != ATM_VCI_UNSPEC) - { - IF_EVENT(printk("iphase open: unspec part\n");) - set_bit(ATM_VF_ADDR,&vcc->flags); - } - if (vcc->qos.aal != ATM_AAL5) - return -EINVAL; - IF_EVENT(printk(DEV_LABEL "(itf %d): open %d.%d\n", - vcc->dev->number, vcc->vpi, vcc->vci);) - - /* Device dependent initialization */ - ia_vcc = kmalloc_obj(*ia_vcc); - if (!ia_vcc) return -ENOMEM; - vcc->dev_data = ia_vcc; - - if ((error = open_rx(vcc))) - { - IF_EVENT(printk("iadev: error in open_rx, closing\n");) - ia_close(vcc); - return error; - } - - if ((error = open_tx(vcc))) - { - IF_EVENT(printk("iadev: error in open_tx, closing\n");) - ia_close(vcc); - return error; - } - - set_bit(ATM_VF_READY,&vcc->flags); - -#if 0 - { - static u8 first = 1; - if (first) { - ia_timer.expires = jiffies + 3*HZ; - add_timer(&ia_timer); - first = 0; - } - } -#endif - IF_EVENT(printk("ia open returning\n");) - return 0; -} - -static int ia_change_qos(struct atm_vcc *vcc, struct atm_qos *qos, int flags) -{ - IF_EVENT(printk(">ia_change_qos\n");) - return 0; -} - -static int ia_ioctl(struct atm_dev *dev, unsigned int cmd, void __user *arg) -{ - IA_CMDBUF ia_cmds; - IADEV *iadev; - int i, board; - u16 __user *tmps; - IF_EVENT(printk(">ia_ioctl\n");) - if (cmd != IA_CMD) { - if (!dev->phy->ioctl) return -EINVAL; - return dev->phy->ioctl(dev,cmd,arg); - } - if (copy_from_user(&ia_cmds, arg, sizeof ia_cmds)) return -EFAULT; - board = ia_cmds.status; - - if ((board < 0) || (board > iadev_count)) - board = 0; - board = array_index_nospec(board, iadev_count + 1); - - iadev = ia_dev[board]; - switch (ia_cmds.cmd) { - case MEMDUMP: - { - switch (ia_cmds.sub_cmd) { - case MEMDUMP_SEGREG: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - tmps = (u16 __user *)ia_cmds.buf; - for(i=0; i<0x80; i+=2, tmps++) - if(put_user((u16)(readl(iadev->seg_reg+i) & 0xffff), tmps)) return -EFAULT; - ia_cmds.status = 0; - ia_cmds.len = 0x80; - break; - case MEMDUMP_REASSREG: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - tmps = (u16 __user *)ia_cmds.buf; - for(i=0; i<0x80; i+=2, tmps++) - if(put_user((u16)(readl(iadev->reass_reg+i) & 0xffff), tmps)) return -EFAULT; - ia_cmds.status = 0; - ia_cmds.len = 0x80; - break; - case MEMDUMP_FFL: - { - ia_regs_t *regs_local; - ffredn_t *ffL; - rfredn_t *rfL; - - if (!capable(CAP_NET_ADMIN)) return -EPERM; - regs_local = kmalloc_obj(*regs_local); - if (!regs_local) return -ENOMEM; - ffL = ®s_local->ffredn; - rfL = ®s_local->rfredn; - /* Copy real rfred registers into the local copy */ - for (i=0; i<(sizeof (rfredn_t))/4; i++) - ((u_int *)rfL)[i] = readl(iadev->reass_reg + i) & 0xffff; - /* Copy real ffred registers into the local copy */ - for (i=0; i<(sizeof (ffredn_t))/4; i++) - ((u_int *)ffL)[i] = readl(iadev->seg_reg + i) & 0xffff; - - if (copy_to_user(ia_cmds.buf, regs_local,sizeof(ia_regs_t))) { - kfree(regs_local); - return -EFAULT; - } - kfree(regs_local); - printk("Board %d registers dumped\n", board); - ia_cmds.status = 0; - } - break; - case READ_REG: - { - if (!capable(CAP_NET_ADMIN)) return -EPERM; - desc_dbg(iadev); - ia_cmds.status = 0; - } - break; - case 0x6: - { - ia_cmds.status = 0; - printk("skb = 0x%p\n", skb_peek(&iadev->tx_backlog)); - printk("rtn_q: 0x%p\n",ia_deque_rtn_q(&iadev->tx_return_q)); - } - break; - case 0x8: - { - struct k_sonet_stats *stats; - stats = &PRIV(_ia_dev[board])->sonet_stats; - printk("section_bip: %d\n", atomic_read(&stats->section_bip)); - printk("line_bip : %d\n", atomic_read(&stats->line_bip)); - printk("path_bip : %d\n", atomic_read(&stats->path_bip)); - printk("line_febe : %d\n", atomic_read(&stats->line_febe)); - printk("path_febe : %d\n", atomic_read(&stats->path_febe)); - printk("corr_hcs : %d\n", atomic_read(&stats->corr_hcs)); - printk("uncorr_hcs : %d\n", atomic_read(&stats->uncorr_hcs)); - printk("tx_cells : %d\n", atomic_read(&stats->tx_cells)); - printk("rx_cells : %d\n", atomic_read(&stats->rx_cells)); - } - ia_cmds.status = 0; - break; - case 0x9: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - for (i = 1; i <= iadev->num_rx_desc; i++) - free_desc(_ia_dev[board], i); - writew( ~(RX_FREEQ_EMPT | RX_EXCP_RCVD), - iadev->reass_reg+REASS_MASK_REG); - iadev->rxing = 1; - - ia_cmds.status = 0; - break; - - case 0xb: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - ia_frontend_intr(iadev); - break; - case 0xa: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - { - ia_cmds.status = 0; - IADebugFlag = ia_cmds.maddr; - printk("New debug option loaded\n"); - } - break; - default: - ia_cmds.status = 0; - break; - } - } - break; - default: - break; - - } - return 0; -} - -static int ia_pkt_tx (struct atm_vcc *vcc, struct sk_buff *skb) { - IADEV *iadev; - struct dle *wr_ptr; - struct tx_buf_desc __iomem *buf_desc_ptr; - int desc; - int comp_code; - int total_len; - struct cpcs_trailer *trailer; - struct ia_vcc *iavcc; - - iadev = INPH_IA_DEV(vcc->dev); - iavcc = INPH_IA_VCC(vcc); - if (!iavcc->txing) { - printk("discard packet on closed VC\n"); - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb_any(skb); - return 0; - } - - if (skb->len > iadev->tx_buf_sz - 8) { - printk("Transmit size over tx buffer size\n"); - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb_any(skb); - return 0; - } - if ((unsigned long)skb->data & 3) { - printk("Misaligned SKB\n"); - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb_any(skb); - return 0; - } - /* Get a descriptor number from our free descriptor queue - We get the descr number from the TCQ now, since I am using - the TCQ as a free buffer queue. Initially TCQ will be - initialized with all the descriptors and is hence, full. - */ - desc = get_desc (iadev, iavcc); - if (desc == 0xffff) - return 1; - comp_code = desc >> 13; - desc &= 0x1fff; - - if ((desc == 0) || (desc > iadev->num_tx_desc)) - { - IF_ERR(printk(DEV_LABEL "invalid desc for send: %d\n", desc);) - atomic_inc(&vcc->stats->tx); - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb_any(skb); - return 0; /* return SUCCESS */ - } - - if (comp_code) - { - IF_ERR(printk(DEV_LABEL "send desc:%d completion code %d error\n", - desc, comp_code);) - } - - /* remember the desc and vcc mapping */ - iavcc->vc_desc_cnt++; - iadev->desc_tbl[desc-1].iavcc = iavcc; - iadev->desc_tbl[desc-1].txskb = skb; - IA_SKB_STATE(skb) = 0; - - iadev->ffL.tcq_rd += 2; - if (iadev->ffL.tcq_rd > iadev->ffL.tcq_ed) - iadev->ffL.tcq_rd = iadev->ffL.tcq_st; - writew(iadev->ffL.tcq_rd, iadev->seg_reg+TCQ_RD_PTR); - - /* Put the descriptor number in the packet ready queue - and put the updated write pointer in the DLE field - */ - *(u16*)(iadev->seg_ram+iadev->ffL.prq_wr) = desc; - - iadev->ffL.prq_wr += 2; - if (iadev->ffL.prq_wr > iadev->ffL.prq_ed) - iadev->ffL.prq_wr = iadev->ffL.prq_st; - - /* Figure out the exact length of the packet and padding required to - make it aligned on a 48 byte boundary. */ - total_len = skb->len + sizeof(struct cpcs_trailer); - total_len = ((total_len + 47) / 48) * 48; - IF_TX(printk("ia packet len:%d padding:%d\n", total_len, total_len - skb->len);) - - /* Put the packet in a tx buffer */ - trailer = iadev->tx_buf[desc-1].cpcs; - IF_TX(printk("Sent: skb = 0x%p skb->data: 0x%p len: %d, desc: %d\n", - skb, skb->data, skb->len, desc);) - trailer->control = 0; - /*big endian*/ - trailer->length = ((skb->len & 0xff) << 8) | ((skb->len & 0xff00) >> 8); - trailer->crc32 = 0; /* not needed - dummy bytes */ - - /* Display the packet */ - IF_TXPKT(printk("Sent data: len = %d MsgNum = %d\n", - skb->len, tcnter++); - xdump(skb->data, skb->len, "TX: "); - printk("\n");) - - /* Build the buffer descriptor */ - buf_desc_ptr = iadev->seg_ram+TX_DESC_BASE; - buf_desc_ptr += desc; /* points to the corresponding entry */ - buf_desc_ptr->desc_mode = AAL5 | EOM_EN | APP_CRC32 | CMPL_INT; - /* Huh ? p.115 of users guide describes this as a read-only register */ - writew(TRANSMIT_DONE, iadev->seg_reg+SEG_INTR_STATUS_REG); - buf_desc_ptr->vc_index = vcc->vci; - buf_desc_ptr->bytes = total_len; - - if (vcc->qos.txtp.traffic_class == ATM_ABR) - clear_lockup (vcc, iadev); - - /* Build the DLE structure */ - wr_ptr = iadev->tx_dle_q.write; - memset((caddr_t)wr_ptr, 0, sizeof(*wr_ptr)); - wr_ptr->sys_pkt_addr = dma_map_single(&iadev->pci->dev, skb->data, - skb->len, DMA_TO_DEVICE); - wr_ptr->local_pkt_addr = (buf_desc_ptr->buf_start_hi << 16) | - buf_desc_ptr->buf_start_lo; - /* wr_ptr->bytes = swap_byte_order(total_len); didn't seem to affect?? */ - wr_ptr->bytes = skb->len; - - /* hw bug - DLEs of 0x2d, 0x2e, 0x2f cause DMA lockup */ - if ((wr_ptr->bytes >> 2) == 0xb) - wr_ptr->bytes = 0x30; - - wr_ptr->mode = TX_DLE_PSI; - wr_ptr->prq_wr_ptr_data = 0; - - /* end is not to be used for the DLE q */ - if (++wr_ptr == iadev->tx_dle_q.end) - wr_ptr = iadev->tx_dle_q.start; - - /* Build trailer dle */ - wr_ptr->sys_pkt_addr = iadev->tx_buf[desc-1].dma_addr; - wr_ptr->local_pkt_addr = ((buf_desc_ptr->buf_start_hi << 16) | - buf_desc_ptr->buf_start_lo) + total_len - sizeof(struct cpcs_trailer); - - wr_ptr->bytes = sizeof(struct cpcs_trailer); - wr_ptr->mode = DMA_INT_ENABLE; - wr_ptr->prq_wr_ptr_data = iadev->ffL.prq_wr; - - /* end is not to be used for the DLE q */ - if (++wr_ptr == iadev->tx_dle_q.end) - wr_ptr = iadev->tx_dle_q.start; - - iadev->tx_dle_q.write = wr_ptr; - ATM_DESC(skb) = vcc->vci; - skb_queue_tail(&iadev->tx_dma_q, skb); - - atomic_inc(&vcc->stats->tx); - iadev->tx_pkt_cnt++; - /* Increment transaction counter */ - writel(2, iadev->dma+IPHASE5575_TX_COUNTER); - -#if 0 - /* add flow control logic */ - if (atomic_read(&vcc->stats->tx) % 20 == 0) { - if (iavcc->vc_desc_cnt > 10) { - vcc->tx_quota = vcc->tx_quota * 3 / 4; - printk("Tx1: vcc->tx_quota = %d \n", (u32)vcc->tx_quota ); - iavcc->flow_inc = -1; - iavcc->saved_tx_quota = vcc->tx_quota; - } else if ((iavcc->flow_inc < 0) && (iavcc->vc_desc_cnt < 3)) { - // vcc->tx_quota = 3 * iavcc->saved_tx_quota / 4; - printk("Tx2: vcc->tx_quota = %d \n", (u32)vcc->tx_quota ); - iavcc->flow_inc = 0; - } - } -#endif - IF_TX(printk("ia send done\n");) - return 0; -} - -static int ia_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - IADEV *iadev; - unsigned long flags; - - iadev = INPH_IA_DEV(vcc->dev); - if ((!skb)||(skb->len>(iadev->tx_buf_sz-sizeof(struct cpcs_trailer)))) - { - if (!skb) - printk(KERN_CRIT "null skb in ia_send\n"); - else dev_kfree_skb_any(skb); - return -EINVAL; - } - spin_lock_irqsave(&iadev->tx_lock, flags); - if (!test_bit(ATM_VF_READY,&vcc->flags)){ - dev_kfree_skb_any(skb); - spin_unlock_irqrestore(&iadev->tx_lock, flags); - return -EINVAL; - } - ATM_SKB(skb)->vcc = vcc; - - if (skb_peek(&iadev->tx_backlog)) { - skb_queue_tail(&iadev->tx_backlog, skb); - } - else { - if (ia_pkt_tx (vcc, skb)) { - skb_queue_tail(&iadev->tx_backlog, skb); - } - } - spin_unlock_irqrestore(&iadev->tx_lock, flags); - return 0; - -} - -static int ia_proc_read(struct atm_dev *dev,loff_t *pos,char *page) -{ - int left = *pos, n; - char *tmpPtr; - IADEV *iadev = INPH_IA_DEV(dev); - if(!left--) { - if (iadev->phy_type == FE_25MBIT_PHY) { - n = sprintf(page, " Board Type : Iphase5525-1KVC-128K\n"); - return n; - } - if (iadev->phy_type == FE_DS3_PHY) - n = sprintf(page, " Board Type : Iphase-ATM-DS3"); - else if (iadev->phy_type == FE_E3_PHY) - n = sprintf(page, " Board Type : Iphase-ATM-E3"); - else if (iadev->phy_type == FE_UTP_OPTION) - n = sprintf(page, " Board Type : Iphase-ATM-UTP155"); - else - n = sprintf(page, " Board Type : Iphase-ATM-OC3"); - tmpPtr = page + n; - if (iadev->pci_map_size == 0x40000) - n += sprintf(tmpPtr, "-1KVC-"); - else - n += sprintf(tmpPtr, "-4KVC-"); - tmpPtr = page + n; - if ((iadev->memType & MEM_SIZE_MASK) == MEM_SIZE_1M) - n += sprintf(tmpPtr, "1M \n"); - else if ((iadev->memType & MEM_SIZE_MASK) == MEM_SIZE_512K) - n += sprintf(tmpPtr, "512K\n"); - else - n += sprintf(tmpPtr, "128K\n"); - return n; - } - if (!left) { - return sprintf(page, " Number of Tx Buffer: %u\n" - " Size of Tx Buffer : %u\n" - " Number of Rx Buffer: %u\n" - " Size of Rx Buffer : %u\n" - " Packets Received : %u\n" - " Packets Transmitted: %u\n" - " Cells Received : %u\n" - " Cells Transmitted : %u\n" - " Board Dropped Cells: %u\n" - " Board Dropped Pkts : %u\n", - iadev->num_tx_desc, iadev->tx_buf_sz, - iadev->num_rx_desc, iadev->rx_buf_sz, - iadev->rx_pkt_cnt, iadev->tx_pkt_cnt, - iadev->rx_cell_cnt, iadev->tx_cell_cnt, - iadev->drop_rxcell, iadev->drop_rxpkt); - } - return 0; -} - -static const struct atmdev_ops ops = { - .open = ia_open, - .close = ia_close, - .ioctl = ia_ioctl, - .send = ia_send, - .phy_put = ia_phy_put, - .phy_get = ia_phy_get, - .change_qos = ia_change_qos, - .proc_read = ia_proc_read, - .owner = THIS_MODULE, -}; - -static int ia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - struct atm_dev *dev; - IADEV *iadev; - int ret; - - iadev = kzalloc_obj(*iadev); - if (!iadev) { - ret = -ENOMEM; - goto err_out; - } - - iadev->pci = pdev; - - IF_INIT(printk("ia detected at bus:%d dev: %d function:%d\n", - pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));) - if (pci_enable_device(pdev)) { - ret = -ENODEV; - goto err_out_free_iadev; - } - dev = atm_dev_register(DEV_LABEL, &pdev->dev, &ops, -1, NULL); - if (!dev) { - ret = -ENOMEM; - goto err_out_disable_dev; - } - dev->dev_data = iadev; - IF_INIT(printk(DEV_LABEL "registered at (itf :%d)\n", dev->number);) - IF_INIT(printk("dev_id = 0x%p iadev->LineRate = %d \n", dev, - iadev->LineRate);) - - pci_set_drvdata(pdev, dev); - - ia_dev[iadev_count] = iadev; - _ia_dev[iadev_count] = dev; - iadev_count++; - if (ia_init(dev) || ia_start(dev)) { - IF_INIT(printk("IA register failed!\n");) - iadev_count--; - ia_dev[iadev_count] = NULL; - _ia_dev[iadev_count] = NULL; - ret = -EINVAL; - goto err_out_deregister_dev; - } - IF_EVENT(printk("iadev_count = %d\n", iadev_count);) - - iadev->next_board = ia_boards; - ia_boards = dev; - - return 0; - -err_out_deregister_dev: - atm_dev_deregister(dev); -err_out_disable_dev: - pci_disable_device(pdev); -err_out_free_iadev: - kfree(iadev); -err_out: - return ret; -} - -static void ia_remove_one(struct pci_dev *pdev) -{ - struct atm_dev *dev = pci_get_drvdata(pdev); - IADEV *iadev = INPH_IA_DEV(dev); - - /* Disable phy interrupts */ - ia_phy_put(dev, ia_phy_get(dev, SUNI_RSOP_CIE) & ~(SUNI_RSOP_CIE_LOSE), - SUNI_RSOP_CIE); - udelay(1); - - if (dev->phy && dev->phy->stop) - dev->phy->stop(dev); - - /* De-register device */ - free_irq(iadev->irq, dev); - iadev_count--; - ia_dev[iadev_count] = NULL; - _ia_dev[iadev_count] = NULL; - IF_EVENT(printk("deregistering iav at (itf:%d)\n", dev->number);) - atm_dev_deregister(dev); - - iounmap(iadev->base); - pci_disable_device(pdev); - - ia_free_rx(iadev); - ia_free_tx(iadev); - - kfree(iadev); -} - -static const struct pci_device_id ia_pci_tbl[] = { - { PCI_VENDOR_ID_IPHASE, 0x0008, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_IPHASE, 0x0009, PCI_ANY_ID, PCI_ANY_ID, }, - { 0,} -}; -MODULE_DEVICE_TABLE(pci, ia_pci_tbl); - -static struct pci_driver ia_driver = { - .name = DEV_LABEL, - .id_table = ia_pci_tbl, - .probe = ia_init_one, - .remove = ia_remove_one, -}; - -static int __init ia_module_init(void) -{ - int ret; - - ret = pci_register_driver(&ia_driver); - if (ret >= 0) { - ia_timer.expires = jiffies + 3*HZ; - add_timer(&ia_timer); - } else - printk(KERN_ERR DEV_LABEL ": no adapter found\n"); - return ret; -} - -static void __exit ia_module_exit(void) -{ - pci_unregister_driver(&ia_driver); - - timer_delete_sync(&ia_timer); -} - -module_init(ia_module_init); -module_exit(ia_module_exit); diff --git a/drivers/atm/iphase.h b/drivers/atm/iphase.h deleted file mode 100644 index 2f5f8875cbd1..000000000000 --- a/drivers/atm/iphase.h +++ /dev/null @@ -1,1452 +0,0 @@ -/****************************************************************************** - Device driver for Interphase ATM PCI adapter cards - Author: Peter Wang - Interphase Corporation - Version: 1.0 - iphase.h: This is the header file for iphase.c. -******************************************************************************* - - This software may be used and distributed according to the terms - of the GNU General Public License (GPL), incorporated herein by reference. - Drivers based on this skeleton fall under the GPL and must retain - the authorship (implicit copyright) notice. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - Modified from an incomplete driver for Interphase 5575 1KVC 1M card which - was originally written by Monalisa Agrawal at UNH. Now this driver - supports a variety of varients of Interphase ATM PCI (i)Chip adapter - card family (See www.iphase.com/products/ClassSheet.cfm?ClassID=ATM) - in terms of PHY type, the size of control memory and the size of - packet memory. The following are the change log and history: - - Bugfix the Mona's UBR driver. - Modify the basic memory allocation and dma logic. - Port the driver to the latest kernel from 2.0.46. - Complete the ABR logic of the driver, and added the ABR work- - around for the hardware anormalies. - Add the CBR support. - Add the flow control logic to the driver to allow rate-limit VC. - Add 4K VC support to the board with 512K control memory. - Add the support of all the variants of the Interphase ATM PCI - (i)Chip adapter cards including x575 (155M OC3 and UTP155), x525 - (25M UTP25) and x531 (DS3 and E3). - Add SMP support. - - Support and updates available at: ftp://ftp.iphase.com/pub/atm - -*******************************************************************************/ - -#ifndef IPHASE_H -#define IPHASE_H - - -/************************ IADBG DEFINE *********************************/ -/* IADebugFlag Bit Map */ -#define IF_IADBG_INIT_ADAPTER 0x00000001 // init adapter info -#define IF_IADBG_TX 0x00000002 // debug TX -#define IF_IADBG_RX 0x00000004 // debug RX -#define IF_IADBG_QUERY_INFO 0x00000008 // debug Request call -#define IF_IADBG_SHUTDOWN 0x00000010 // debug shutdown event -#define IF_IADBG_INTR 0x00000020 // debug interrupt DPC -#define IF_IADBG_TXPKT 0x00000040 // debug TX PKT -#define IF_IADBG_RXPKT 0x00000080 // debug RX PKT -#define IF_IADBG_ERR 0x00000100 // debug system error -#define IF_IADBG_EVENT 0x00000200 // debug event -#define IF_IADBG_DIS_INTR 0x00001000 // debug disable interrupt -#define IF_IADBG_EN_INTR 0x00002000 // debug enable interrupt -#define IF_IADBG_LOUD 0x00004000 // debugging info -#define IF_IADBG_VERY_LOUD 0x00008000 // excessive debugging info -#define IF_IADBG_CBR 0x00100000 // -#define IF_IADBG_UBR 0x00200000 // -#define IF_IADBG_ABR 0x00400000 // -#define IF_IADBG_DESC 0x01000000 // -#define IF_IADBG_SUNI_STAT 0x02000000 // suni statistics -#define IF_IADBG_RESET 0x04000000 - -#define IF_IADBG(f) if (IADebugFlag & (f)) - -#ifdef CONFIG_ATM_IA_DEBUG /* Debug build */ - -#define IF_LOUD(A) IF_IADBG(IF_IADBG_LOUD) { A } -#define IF_ERR(A) IF_IADBG(IF_IADBG_ERR) { A } -#define IF_VERY_LOUD(A) IF_IADBG( IF_IADBG_VERY_LOUD ) { A } - -#define IF_INIT_ADAPTER(A) IF_IADBG( IF_IADBG_INIT_ADAPTER ) { A } -#define IF_INIT(A) IF_IADBG( IF_IADBG_INIT_ADAPTER ) { A } -#define IF_SUNI_STAT(A) IF_IADBG( IF_IADBG_SUNI_STAT ) { A } -#define IF_QUERY_INFO(A) IF_IADBG( IF_IADBG_QUERY_INFO ) { A } -#define IF_COPY_OVER(A) IF_IADBG( IF_IADBG_COPY_OVER ) { A } - -#define IF_INTR(A) IF_IADBG( IF_IADBG_INTR ) { A } -#define IF_DIS_INTR(A) IF_IADBG( IF_IADBG_DIS_INTR ) { A } -#define IF_EN_INTR(A) IF_IADBG( IF_IADBG_EN_INTR ) { A } - -#define IF_TX(A) IF_IADBG( IF_IADBG_TX ) { A } -#define IF_RX(A) IF_IADBG( IF_IADBG_RX ) { A } -#define IF_TXPKT(A) IF_IADBG( IF_IADBG_TXPKT ) { A } -#define IF_RXPKT(A) IF_IADBG( IF_IADBG_RXPKT ) { A } - -#define IF_SHUTDOWN(A) IF_IADBG(IF_IADBG_SHUTDOWN) { A } -#define IF_CBR(A) IF_IADBG( IF_IADBG_CBR ) { A } -#define IF_UBR(A) IF_IADBG( IF_IADBG_UBR ) { A } -#define IF_ABR(A) IF_IADBG( IF_IADBG_ABR ) { A } -#define IF_EVENT(A) IF_IADBG( IF_IADBG_EVENT) { A } - -#else /* free build */ -#define IF_LOUD(A) -#define IF_VERY_LOUD(A) -#define IF_INIT_ADAPTER(A) -#define IF_INIT(A) -#define IF_SUNI_STAT(A) -#define IF_PVC_CHKPKT(A) -#define IF_QUERY_INFO(A) -#define IF_COPY_OVER(A) -#define IF_HANG(A) -#define IF_INTR(A) -#define IF_DIS_INTR(A) -#define IF_EN_INTR(A) -#define IF_TX(A) -#define IF_RX(A) -#define IF_TXDEBUG(A) -#define IF_VC(A) -#define IF_ERR(A) -#define IF_CBR(A) -#define IF_UBR(A) -#define IF_ABR(A) -#define IF_SHUTDOWN(A) -#define DbgPrint(A) -#define IF_EVENT(A) -#define IF_TXPKT(A) -#define IF_RXPKT(A) -#endif /* CONFIG_ATM_IA_DEBUG */ - -#define ATM_DESC(skb) (skb->protocol) -#define IA_SKB_STATE(skb) (skb->protocol) -#define IA_DLED 1 -#define IA_TX_DONE 2 - -/* iadbg defines */ -#define IA_CMD 0x7749 -typedef struct { - int cmd; - int sub_cmd; - int len; - u32 maddr; - int status; - void __user *buf; -} IA_CMDBUF, *PIA_CMDBUF; - -/* cmds */ -#define MEMDUMP 0x01 - -/* sub_cmds */ -#define MEMDUMP_SEGREG 0x2 -#define MEMDUMP_DEV 0x1 -#define MEMDUMP_REASSREG 0x3 -#define MEMDUMP_FFL 0x4 -#define READ_REG 0x5 -#define WAKE_DBG_WAIT 0x6 - -/************************ IADBG DEFINE END ***************************/ - -#define Boolean(x) ((x) ? 1 : 0) -#define NR_VCI 1024 /* number of VCIs */ -#define NR_VCI_LD 10 /* log2(NR_VCI) */ -#define NR_VCI_4K 4096 /* number of VCIs */ -#define NR_VCI_4K_LD 12 /* log2(NR_VCI) */ -#define MEM_VALID 0xfffffff0 /* mask base address with this */ - -#ifndef PCI_VENDOR_ID_IPHASE -#define PCI_VENDOR_ID_IPHASE 0x107e -#endif -#ifndef PCI_DEVICE_ID_IPHASE_5575 -#define PCI_DEVICE_ID_IPHASE_5575 0x0008 -#endif -#define DEV_LABEL "ia" -#define PCR 207692 -#define ICR 100000 -#define MCR 0 -#define TBE 1000 -#define FRTT 1 -#define RIF 2 -#define RDF 4 -#define NRMCODE 5 /* 0 - 7 */ -#define TRMCODE 3 /* 0 - 7 */ -#define CDFCODE 6 -#define ATDFCODE 2 /* 0 - 15 */ - -/*---------------------- Packet/Cell Memory ------------------------*/ -#define TX_PACKET_RAM 0x00000 /* start of Trasnmit Packet memory - 0 */ -#define DFL_TX_BUF_SZ 10240 /* 10 K buffers */ -#define DFL_TX_BUFFERS 50 /* number of packet buffers for Tx - - descriptor 0 unused */ -#define REASS_RAM_SIZE 0x10000 /* for 64K 1K VC board */ -#define RX_PACKET_RAM 0x80000 /* start of Receive Packet memory - 512K */ -#define DFL_RX_BUF_SZ 10240 /* 10k buffers */ -#define DFL_RX_BUFFERS 50 /* number of packet buffers for Rx - - descriptor 0 unused */ - -struct cpcs_trailer -{ - u_short control; - u_short length; - u_int crc32; -}; - -struct cpcs_trailer_desc -{ - struct cpcs_trailer *cpcs; - dma_addr_t dma_addr; -}; - -struct ia_vcc -{ - int rxing; - int txing; - int NumCbrEntry; - u32 pcr; - u32 saved_tx_quota; - int flow_inc; - struct sk_buff_head txing_skb; - int ltimeout; - u8 vc_desc_cnt; - -}; - -struct abr_vc_table -{ - u_char status; - u_char rdf; - u_short air; - u_int res[3]; - u_int req_rm_cell_data1; - u_int req_rm_cell_data2; - u_int add_rm_cell_data1; - u_int add_rm_cell_data2; -}; - -/* 32 byte entries */ -struct main_vc -{ - u_short type; -#define ABR 0x8000 -#define UBR 0xc000 -#define CBR 0x0000 - /* ABR fields */ - u_short nrm; - u_short trm; - u_short rm_timestamp_hi; - u_short rm_timestamp_lo:8, - crm:8; - u_short remainder; /* ABR and UBR fields - last 10 bits*/ - u_short next_vc_sched; - u_short present_desc; /* all classes */ - u_short last_cell_slot; /* ABR and UBR */ - u_short pcr; - u_short fraction; - u_short icr; - u_short atdf; - u_short mcr; - u_short acr; - u_short unack:8, - status:8; /* all classes */ -#define UIOLI 0x80 -#define CRC_APPEND 0x40 /* for status field - CRC-32 append */ -#define ABR_STATE 0x02 - -}; - - -/* 8 byte entries */ -struct ext_vc -{ - u_short atm_hdr1; - u_short atm_hdr2; - u_short last_desc; - u_short out_of_rate_link; /* reserved for UBR and CBR */ -}; - - -#define DLE_ENTRIES 256 -#define DMA_INT_ENABLE 0x0002 /* use for both Tx and Rx */ -#define TX_DLE_PSI 0x0001 -#define DLE_TOTAL_SIZE (sizeof(struct dle)*DLE_ENTRIES) - -/* Descriptor List Entries (DLE) */ -struct dle -{ - u32 sys_pkt_addr; - u32 local_pkt_addr; - u32 bytes; - u16 prq_wr_ptr_data; - u16 mode; -}; - -struct dle_q -{ - struct dle *start; - struct dle *end; - struct dle *read; - struct dle *write; -}; - -struct free_desc_q -{ - int desc; /* Descriptor number */ - struct free_desc_q *next; -}; - -struct tx_buf_desc { - unsigned short desc_mode; - unsigned short vc_index; - unsigned short res1; /* reserved field */ - unsigned short bytes; - unsigned short buf_start_hi; - unsigned short buf_start_lo; - unsigned short res2[10]; /* reserved field */ -}; - - -struct rx_buf_desc { - unsigned short desc_mode; - unsigned short vc_index; - unsigned short vpi; - unsigned short bytes; - unsigned short buf_start_hi; - unsigned short buf_start_lo; - unsigned short dma_start_hi; - unsigned short dma_start_lo; - unsigned short crc_upper; - unsigned short crc_lower; - unsigned short res:8, timeout:8; - unsigned short res2[5]; /* reserved field */ -}; - -/*--------SAR stuff ---------------------*/ - -#define EPROM_SIZE 0x40000 /* says 64K in the docs ??? */ -#define MAC1_LEN 4 -#define MAC2_LEN 2 - -/*------------ PCI Memory Space Map, 128K SAR memory ----------------*/ -#define IPHASE5575_PCI_CONFIG_REG_BASE 0x0000 -#define IPHASE5575_BUS_CONTROL_REG_BASE 0x1000 /* offsets 0x00 - 0x3c */ -#define IPHASE5575_FRAG_CONTROL_REG_BASE 0x2000 -#define IPHASE5575_REASS_CONTROL_REG_BASE 0x3000 -#define IPHASE5575_DMA_CONTROL_REG_BASE 0x4000 -#define IPHASE5575_FRONT_END_REG_BASE IPHASE5575_DMA_CONTROL_REG_BASE -#define IPHASE5575_FRAG_CONTROL_RAM_BASE 0x10000 -#define IPHASE5575_REASS_CONTROL_RAM_BASE 0x20000 - -/*------------ Bus interface control registers -----------------*/ -#define IPHASE5575_BUS_CONTROL_REG 0x00 -#define IPHASE5575_BUS_STATUS_REG 0x01 /* actual offset 0x04 */ -#define IPHASE5575_MAC1 0x02 -#define IPHASE5575_REV 0x03 -#define IPHASE5575_MAC2 0x03 /*actual offset 0x0e-reg 0x0c*/ -#define IPHASE5575_EXT_RESET 0x04 -#define IPHASE5575_INT_RESET 0x05 /* addr 1c ?? reg 0x06 */ -#define IPHASE5575_PCI_ADDR_PAGE 0x07 /* reg 0x08, 0x09 ?? */ -#define IPHASE5575_EEPROM_ACCESS 0x0a /* actual offset 0x28 */ -#define IPHASE5575_CELL_FIFO_QUEUE_SZ 0x0b -#define IPHASE5575_CELL_FIFO_MARK_STATE 0x0c -#define IPHASE5575_CELL_FIFO_READ_PTR 0x0d -#define IPHASE5575_CELL_FIFO_WRITE_PTR 0x0e -#define IPHASE5575_CELL_FIFO_CELLS_AVL 0x0f /* actual offset 0x3c */ - -/* Bus Interface Control Register bits */ -#define CTRL_FE_RST 0x80000000 -#define CTRL_LED 0x40000000 -#define CTRL_25MBPHY 0x10000000 -#define CTRL_ENCMBMEM 0x08000000 -#define CTRL_ENOFFSEG 0x01000000 -#define CTRL_ERRMASK 0x00400000 -#define CTRL_DLETMASK 0x00100000 -#define CTRL_DLERMASK 0x00080000 -#define CTRL_FEMASK 0x00040000 -#define CTRL_SEGMASK 0x00020000 -#define CTRL_REASSMASK 0x00010000 -#define CTRL_CSPREEMPT 0x00002000 -#define CTRL_B128 0x00000200 -#define CTRL_B64 0x00000100 -#define CTRL_B48 0x00000080 -#define CTRL_B32 0x00000040 -#define CTRL_B16 0x00000020 -#define CTRL_B8 0x00000010 - -/* Bus Interface Status Register bits */ -#define STAT_CMEMSIZ 0xc0000000 -#define STAT_ADPARCK 0x20000000 -#define STAT_RESVD 0x1fffff80 -#define STAT_ERRINT 0x00000040 -#define STAT_MARKINT 0x00000020 -#define STAT_DLETINT 0x00000010 -#define STAT_DLERINT 0x00000008 -#define STAT_FEINT 0x00000004 -#define STAT_SEGINT 0x00000002 -#define STAT_REASSINT 0x00000001 - - -/*--------------- Segmentation control registers -----------------*/ -/* The segmentation registers are 16 bits access and the addresses - are defined as such so the addresses are the actual "offsets" */ -#define IDLEHEADHI 0x00 -#define IDLEHEADLO 0x01 -#define MAXRATE 0x02 -/* Values for MAXRATE register for 155Mbps and 25.6 Mbps operation */ -#define RATE155 0x64b1 // 16 bits float format -#define MAX_ATM_155 352768 // Cells/second p.118 -#define RATE25 0x5f9d - -#define STPARMS 0x03 -#define STPARMS_1K 0x008c -#define STPARMS_2K 0x0049 -#define STPARMS_4K 0x0026 -#define COMP_EN 0x4000 -#define CBR_EN 0x2000 -#define ABR_EN 0x0800 -#define UBR_EN 0x0400 - -#define ABRUBR_ARB 0x04 -#define RM_TYPE 0x05 -/*Value for RM_TYPE register for ATM Forum Traffic Mangement4.0 support*/ -#define RM_TYPE_4_0 0x0100 - -#define SEG_COMMAND_REG 0x17 -/* Values for the command register */ -#define RESET_SEG 0x0055 -#define RESET_SEG_STATE 0x00aa -#define RESET_TX_CELL_CTR 0x00cc - -#define CBR_PTR_BASE 0x20 -#define ABR_SBPTR_BASE 0x22 -#define UBR_SBPTR_BASE 0x23 -#define ABRWQ_BASE 0x26 -#define UBRWQ_BASE 0x27 -#define VCT_BASE 0x28 -#define VCTE_BASE 0x29 -#define CBR_TAB_BEG 0x2c -#define CBR_TAB_END 0x2d -#define PRQ_ST_ADR 0x30 -#define PRQ_ED_ADR 0x31 -#define PRQ_RD_PTR 0x32 -#define PRQ_WR_PTR 0x33 -#define TCQ_ST_ADR 0x34 -#define TCQ_ED_ADR 0x35 -#define TCQ_RD_PTR 0x36 -#define TCQ_WR_PTR 0x37 -#define SEG_QUEUE_BASE 0x40 -#define SEG_DESC_BASE 0x41 -#define MODE_REG_0 0x45 -#define T_ONLINE 0x0002 /* (i)chipSAR is online */ - -#define MODE_REG_1 0x46 -#define MODE_REG_1_VAL 0x0400 /*for propoer device operation*/ - -#define SEG_INTR_STATUS_REG 0x47 -#define SEG_MASK_REG 0x48 -#define TRANSMIT_DONE 0x0200 -#define TCQ_NOT_EMPTY 0x1000 /* this can be used for both the interrupt - status registers as well as the mask register */ - -#define CELL_CTR_HIGH_AUTO 0x49 -#define CELL_CTR_HIGH_NOAUTO 0xc9 -#define CELL_CTR_LO_AUTO 0x4a -#define CELL_CTR_LO_NOAUTO 0xca - -/* Diagnostic registers */ -#define NEXTDESC 0x59 -#define NEXTVC 0x5a -#define PSLOTCNT 0x5d -#define NEWDN 0x6a -#define NEWVC 0x6b -#define SBPTR 0x6c -#define ABRWQ_WRPTR 0x6f -#define ABRWQ_RDPTR 0x70 -#define UBRWQ_WRPTR 0x71 -#define UBRWQ_RDPTR 0x72 -#define CBR_VC 0x73 -#define ABR_SBVC 0x75 -#define UBR_SBVC 0x76 -#define ABRNEXTLINK 0x78 -#define UBRNEXTLINK 0x79 - - -/*----------------- Reassembly control registers ---------------------*/ -/* The reassembly registers are 16 bits access and the addresses - are defined as such so the addresses are the actual "offsets" */ -#define MODE_REG 0x00 -#define R_ONLINE 0x0002 /* (i)chip is online */ -#define IGN_RAW_FL 0x0004 - -#define PROTOCOL_ID 0x01 -#define REASS_MASK_REG 0x02 -#define REASS_INTR_STATUS_REG 0x03 -/* Interrupt Status register bits */ -#define RX_PKT_CTR_OF 0x8000 -#define RX_ERR_CTR_OF 0x4000 -#define RX_CELL_CTR_OF 0x1000 -#define RX_FREEQ_EMPT 0x0200 -#define RX_EXCPQ_FL 0x0080 -#define RX_RAWQ_FL 0x0010 -#define RX_EXCP_RCVD 0x0008 -#define RX_PKT_RCVD 0x0004 -#define RX_RAW_RCVD 0x0001 - -#define DRP_PKT_CNTR 0x04 -#define ERR_CNTR 0x05 -#define RAW_BASE_ADR 0x08 -#define CELL_CTR0 0x0c -#define CELL_CTR1 0x0d -#define REASS_COMMAND_REG 0x0f -/* Values for command register */ -#define RESET_REASS 0x0055 -#define RESET_REASS_STATE 0x00aa -#define RESET_DRP_PKT_CNTR 0x00f1 -#define RESET_ERR_CNTR 0x00f2 -#define RESET_CELL_CNTR 0x00f8 -#define RESET_REASS_ALL_REGS 0x00ff - -#define REASS_DESC_BASE 0x10 -#define VC_LKUP_BASE 0x11 -#define REASS_TABLE_BASE 0x12 -#define REASS_QUEUE_BASE 0x13 -#define PKT_TM_CNT 0x16 -#define TMOUT_RANGE 0x17 -#define INTRVL_CNTR 0x18 -#define TMOUT_INDX 0x19 -#define VP_LKUP_BASE 0x1c -#define VP_FILTER 0x1d -#define ABR_LKUP_BASE 0x1e -#define FREEQ_ST_ADR 0x24 -#define FREEQ_ED_ADR 0x25 -#define FREEQ_RD_PTR 0x26 -#define FREEQ_WR_PTR 0x27 -#define PCQ_ST_ADR 0x28 -#define PCQ_ED_ADR 0x29 -#define PCQ_RD_PTR 0x2a -#define PCQ_WR_PTR 0x2b -#define EXCP_Q_ST_ADR 0x2c -#define EXCP_Q_ED_ADR 0x2d -#define EXCP_Q_RD_PTR 0x2e -#define EXCP_Q_WR_PTR 0x2f -#define CC_FIFO_ST_ADR 0x34 -#define CC_FIFO_ED_ADR 0x35 -#define CC_FIFO_RD_PTR 0x36 -#define CC_FIFO_WR_PTR 0x37 -#define STATE_REG 0x38 -#define BUF_SIZE 0x42 -#define XTRA_RM_OFFSET 0x44 -#define DRP_PKT_CNTR_NC 0x84 -#define ERR_CNTR_NC 0x85 -#define CELL_CNTR0_NC 0x8c -#define CELL_CNTR1_NC 0x8d - -/* State Register bits */ -#define EXCPQ_EMPTY 0x0040 -#define PCQ_EMPTY 0x0010 -#define FREEQ_EMPTY 0x0004 - - -/*----------------- Front End registers/ DMA control --------------*/ -/* There is a lot of documentation error regarding these offsets ??? - eg:- 2 offsets given 800, a00 for rx counter - similarly many others - Remember again that the offsets are to be 4*register number, so - correct the #defines here -*/ -#define IPHASE5575_TX_COUNTER 0x200 /* offset - 0x800 */ -#define IPHASE5575_RX_COUNTER 0x280 /* offset - 0xa00 */ -#define IPHASE5575_TX_LIST_ADDR 0x300 /* offset - 0xc00 */ -#define IPHASE5575_RX_LIST_ADDR 0x380 /* offset - 0xe00 */ - -/*--------------------------- RAM ---------------------------*/ -/* These memory maps are actually offsets from the segmentation and reassembly RAM base addresses */ - -/* Segmentation Control Memory map */ -#define TX_DESC_BASE 0x0000 /* Buffer Decriptor Table */ -#define TX_COMP_Q 0x1000 /* Transmit Complete Queue */ -#define PKT_RDY_Q 0x1400 /* Packet Ready Queue */ -#define CBR_SCHED_TABLE 0x1800 /* CBR Table */ -#define UBR_SCHED_TABLE 0x3000 /* UBR Table */ -#define UBR_WAIT_Q 0x4000 /* UBR Wait Queue */ -#define ABR_SCHED_TABLE 0x5000 /* ABR Table */ -#define ABR_WAIT_Q 0x5800 /* ABR Wait Queue */ -#define EXT_VC_TABLE 0x6000 /* Extended VC Table */ -#define MAIN_VC_TABLE 0x8000 /* Main VC Table */ -#define SCHEDSZ 1024 /* ABR and UBR Scheduling Table size */ -#define TX_DESC_TABLE_SZ 128 /* Number of entries in the Transmit - Buffer Descriptor Table */ - -/* These are used as table offsets in Descriptor Table address generation */ -#define DESC_MODE 0x0 -#define VC_INDEX 0x1 -#define BYTE_CNT 0x3 -#define PKT_START_HI 0x4 -#define PKT_START_LO 0x5 - -/* Descriptor Mode Word Bits */ -#define EOM_EN 0x0800 -#define AAL5 0x0100 -#define APP_CRC32 0x0400 -#define CMPL_INT 0x1000 - -#define TABLE_ADDRESS(db, dn, to) \ - (((unsigned long)(db & 0x04)) << 16) | (dn << 5) | (to << 1) - -/* Reassembly Control Memory Map */ -#define RX_DESC_BASE 0x0000 /* Buffer Descriptor Table */ -#define VP_TABLE 0x5c00 /* VP Table */ -#define EXCEPTION_Q 0x5e00 /* Exception Queue */ -#define FREE_BUF_DESC_Q 0x6000 /* Free Buffer Descriptor Queue */ -#define PKT_COMP_Q 0x6800 /* Packet Complete Queue */ -#define REASS_TABLE 0x7000 /* Reassembly Table */ -#define RX_VC_TABLE 0x7800 /* VC Table */ -#define ABR_VC_TABLE 0x8000 /* ABR VC Table */ -#define RX_DESC_TABLE_SZ 736 /* Number of entries in the Receive - Buffer Descriptor Table */ -#define VP_TABLE_SZ 256 /* Number of entries in VPTable */ -#define RX_VC_TABLE_SZ 1024 /* Number of entries in VC Table */ -#define REASS_TABLE_SZ 1024 /* Number of entries in Reassembly Table */ - /* Buffer Descriptor Table */ -#define RX_ACT 0x8000 -#define RX_VPVC 0x4000 -#define RX_CNG 0x0040 -#define RX_CER 0x0008 -#define RX_PTE 0x0004 -#define RX_OFL 0x0002 -#define NUM_RX_EXCP 32 - -/* Reassembly Table */ -#define NO_AAL5_PKT 0x0000 -#define AAL5_PKT_REASSEMBLED 0x4000 -#define AAL5_PKT_TERMINATED 0x8000 -#define RAW_PKT 0xc000 -#define REASS_ABR 0x2000 - -/*-------------------- Base Registers --------------------*/ -#define REG_BASE IPHASE5575_BUS_CONTROL_REG_BASE -#define RAM_BASE IPHASE5575_FRAG_CONTROL_RAM_BASE -#define PHY_BASE IPHASE5575_FRONT_END_REG_BASE -#define SEG_BASE IPHASE5575_FRAG_CONTROL_REG_BASE -#define REASS_BASE IPHASE5575_REASS_CONTROL_REG_BASE - -typedef volatile u_int ffreg_t; -typedef u_int rreg_t; - -typedef struct _ffredn_t { - ffreg_t idlehead_high; /* Idle cell header (high) */ - ffreg_t idlehead_low; /* Idle cell header (low) */ - ffreg_t maxrate; /* Maximum rate */ - ffreg_t stparms; /* Traffic Management Parameters */ - ffreg_t abrubr_abr; /* ABRUBR Priority Byte 1, TCR Byte 0 */ - ffreg_t rm_type; /* */ - u_int filler5[0x17 - 0x06]; - ffreg_t cmd_reg; /* Command register */ - u_int filler18[0x20 - 0x18]; - ffreg_t cbr_base; /* CBR Pointer Base */ - ffreg_t vbr_base; /* VBR Pointer Base */ - ffreg_t abr_base; /* ABR Pointer Base */ - ffreg_t ubr_base; /* UBR Pointer Base */ - u_int filler24; - ffreg_t vbrwq_base; /* VBR Wait Queue Base */ - ffreg_t abrwq_base; /* ABR Wait Queue Base */ - ffreg_t ubrwq_base; /* UBR Wait Queue Base */ - ffreg_t vct_base; /* Main VC Table Base */ - ffreg_t vcte_base; /* Extended Main VC Table Base */ - u_int filler2a[0x2C - 0x2A]; - ffreg_t cbr_tab_beg; /* CBR Table Begin */ - ffreg_t cbr_tab_end; /* CBR Table End */ - ffreg_t cbr_pointer; /* CBR Pointer */ - u_int filler2f[0x30 - 0x2F]; - ffreg_t prq_st_adr; /* Packet Ready Queue Start Address */ - ffreg_t prq_ed_adr; /* Packet Ready Queue End Address */ - ffreg_t prq_rd_ptr; /* Packet Ready Queue read pointer */ - ffreg_t prq_wr_ptr; /* Packet Ready Queue write pointer */ - ffreg_t tcq_st_adr; /* Transmit Complete Queue Start Address*/ - ffreg_t tcq_ed_adr; /* Transmit Complete Queue End Address */ - ffreg_t tcq_rd_ptr; /* Transmit Complete Queue read pointer */ - ffreg_t tcq_wr_ptr; /* Transmit Complete Queue write pointer*/ - u_int filler38[0x40 - 0x38]; - ffreg_t queue_base; /* Base address for PRQ and TCQ */ - ffreg_t desc_base; /* Base address of descriptor table */ - u_int filler42[0x45 - 0x42]; - ffreg_t mode_reg_0; /* Mode register 0 */ - ffreg_t mode_reg_1; /* Mode register 1 */ - ffreg_t intr_status_reg;/* Interrupt Status register */ - ffreg_t mask_reg; /* Mask Register */ - ffreg_t cell_ctr_high1; /* Total cell transfer count (high) */ - ffreg_t cell_ctr_lo1; /* Total cell transfer count (low) */ - ffreg_t state_reg; /* Status register */ - u_int filler4c[0x58 - 0x4c]; - ffreg_t curr_desc_num; /* Contains the current descriptor num */ - ffreg_t next_desc; /* Next descriptor */ - ffreg_t next_vc; /* Next VC */ - u_int filler5b[0x5d - 0x5b]; - ffreg_t present_slot_cnt;/* Present slot count */ - u_int filler5e[0x6a - 0x5e]; - ffreg_t new_desc_num; /* New descriptor number */ - ffreg_t new_vc; /* New VC */ - ffreg_t sched_tbl_ptr; /* Schedule table pointer */ - ffreg_t vbrwq_wptr; /* VBR wait queue write pointer */ - ffreg_t vbrwq_rptr; /* VBR wait queue read pointer */ - ffreg_t abrwq_wptr; /* ABR wait queue write pointer */ - ffreg_t abrwq_rptr; /* ABR wait queue read pointer */ - ffreg_t ubrwq_wptr; /* UBR wait queue write pointer */ - ffreg_t ubrwq_rptr; /* UBR wait queue read pointer */ - ffreg_t cbr_vc; /* CBR VC */ - ffreg_t vbr_sb_vc; /* VBR SB VC */ - ffreg_t abr_sb_vc; /* ABR SB VC */ - ffreg_t ubr_sb_vc; /* UBR SB VC */ - ffreg_t vbr_next_link; /* VBR next link */ - ffreg_t abr_next_link; /* ABR next link */ - ffreg_t ubr_next_link; /* UBR next link */ - u_int filler7a[0x7c-0x7a]; - ffreg_t out_rate_head; /* Out of rate head */ - u_int filler7d[0xca-0x7d]; /* pad out to full address space */ - ffreg_t cell_ctr_high1_nc;/* Total cell transfer count (high) */ - ffreg_t cell_ctr_lo1_nc;/* Total cell transfer count (low) */ - u_int fillercc[0x100-0xcc]; /* pad out to full address space */ -} ffredn_t; - -typedef struct _rfredn_t { - rreg_t mode_reg_0; /* Mode register 0 */ - rreg_t protocol_id; /* Protocol ID */ - rreg_t mask_reg; /* Mask Register */ - rreg_t intr_status_reg;/* Interrupt status register */ - rreg_t drp_pkt_cntr; /* Dropped packet cntr (clear on read) */ - rreg_t err_cntr; /* Error Counter (cleared on read) */ - u_int filler6[0x08 - 0x06]; - rreg_t raw_base_adr; /* Base addr for raw cell Q */ - u_int filler2[0x0c - 0x09]; - rreg_t cell_ctr0; /* Cell Counter 0 (cleared when read) */ - rreg_t cell_ctr1; /* Cell Counter 1 (cleared when read) */ - u_int filler3[0x0f - 0x0e]; - rreg_t cmd_reg; /* Command register */ - rreg_t desc_base; /* Base address for description table */ - rreg_t vc_lkup_base; /* Base address for VC lookup table */ - rreg_t reass_base; /* Base address for reassembler table */ - rreg_t queue_base; /* Base address for Communication queue */ - u_int filler14[0x16 - 0x14]; - rreg_t pkt_tm_cnt; /* Packet Timeout and count register */ - rreg_t tmout_range; /* Range of reassembley IDs for timeout */ - rreg_t intrvl_cntr; /* Packet aging interval counter */ - rreg_t tmout_indx; /* index of pkt being tested for aging */ - u_int filler1a[0x1c - 0x1a]; - rreg_t vp_lkup_base; /* Base address for VP lookup table */ - rreg_t vp_filter; /* VP filter register */ - rreg_t abr_lkup_base; /* Base address of ABR VC Table */ - u_int filler1f[0x24 - 0x1f]; - rreg_t fdq_st_adr; /* Free desc queue start address */ - rreg_t fdq_ed_adr; /* Free desc queue end address */ - rreg_t fdq_rd_ptr; /* Free desc queue read pointer */ - rreg_t fdq_wr_ptr; /* Free desc queue write pointer */ - rreg_t pcq_st_adr; /* Packet Complete queue start address */ - rreg_t pcq_ed_adr; /* Packet Complete queue end address */ - rreg_t pcq_rd_ptr; /* Packet Complete queue read pointer */ - rreg_t pcq_wr_ptr; /* Packet Complete queue write pointer */ - rreg_t excp_st_adr; /* Exception queue start address */ - rreg_t excp_ed_adr; /* Exception queue end address */ - rreg_t excp_rd_ptr; /* Exception queue read pointer */ - rreg_t excp_wr_ptr; /* Exception queue write pointer */ - u_int filler30[0x34 - 0x30]; - rreg_t raw_st_adr; /* Raw Cell start address */ - rreg_t raw_ed_adr; /* Raw Cell end address */ - rreg_t raw_rd_ptr; /* Raw Cell read pointer */ - rreg_t raw_wr_ptr; /* Raw Cell write pointer */ - rreg_t state_reg; /* State Register */ - u_int filler39[0x42 - 0x39]; - rreg_t buf_size; /* Buffer size */ - u_int filler43; - rreg_t xtra_rm_offset; /* Offset of the additional turnaround RM */ - u_int filler45[0x84 - 0x45]; - rreg_t drp_pkt_cntr_nc;/* Dropped Packet cntr, Not clear on rd */ - rreg_t err_cntr_nc; /* Error Counter, Not clear on read */ - u_int filler86[0x8c - 0x86]; - rreg_t cell_ctr0_nc; /* Cell Counter 0, Not clear on read */ - rreg_t cell_ctr1_nc; /* Cell Counter 1, Not clear on read */ - u_int filler8e[0x100-0x8e]; /* pad out to full address space */ -} rfredn_t; - -typedef struct { - /* Atlantic */ - ffredn_t ffredn; /* F FRED */ - rfredn_t rfredn; /* R FRED */ -} ia_regs_t; - -typedef struct { - u_short f_vc_type; /* VC type */ - u_short f_nrm; /* Nrm */ - u_short f_nrmexp; /* Nrm Exp */ - u_short reserved6; /* */ - u_short f_crm; /* Crm */ - u_short reserved10; /* Reserved */ - u_short reserved12; /* Reserved */ - u_short reserved14; /* Reserved */ - u_short last_cell_slot; /* last_cell_slot_count */ - u_short f_pcr; /* Peak Cell Rate */ - u_short fraction; /* fraction */ - u_short f_icr; /* Initial Cell Rate */ - u_short f_cdf; /* */ - u_short f_mcr; /* Minimum Cell Rate */ - u_short f_acr; /* Allowed Cell Rate */ - u_short f_status; /* */ -} f_vc_abr_entry; - -typedef struct { - u_short r_status_rdf; /* status + RDF */ - u_short r_air; /* AIR */ - u_short reserved4[14]; /* Reserved */ -} r_vc_abr_entry; - -#define MRM 3 - -typedef struct srv_cls_param { - u32 class_type; /* CBR/VBR/ABR/UBR; use the enum above */ - u32 pcr; /* Peak Cell Rate (24-bit) */ - /* VBR parameters */ - u32 scr; /* sustainable cell rate */ - u32 max_burst_size; /* ?? cell rate or data rate */ - - /* ABR only UNI 4.0 Parameters */ - u32 mcr; /* Min Cell Rate (24-bit) */ - u32 icr; /* Initial Cell Rate (24-bit) */ - u32 tbe; /* Transient Buffer Exposure (24-bit) */ - u32 frtt; /* Fixed Round Trip Time (24-bit) */ - -#if 0 /* Additional Parameters of TM 4.0 */ -bits 31 30 29 28 27-25 24-22 21-19 18-9 ------------------------------------------------------------------------------ -| NRM present | TRM prsnt | CDF prsnt | ADTF prsnt | NRM | TRM | CDF | ADTF | ------------------------------------------------------------------------------ -#endif /* 0 */ - - u8 nrm; /* Max # of Cells for each forward RM - cell (3-bit) */ - u8 trm; /* Time between forward RM cells (3-bit) */ - u16 adtf; /* ACR Decrease Time Factor (10-bit) */ - u8 cdf; /* Cutoff Decrease Factor (3-bit) */ - u8 rif; /* Rate Increment Factor (4-bit) */ - u8 rdf; /* Rate Decrease Factor (4-bit) */ - u8 reserved; /* 8 bits to keep structure word aligned */ -} srv_cls_param_t; - -struct testTable_t { - u16 lastTime; - u16 fract; - u8 vc_status; -}; - -typedef struct { - u16 vci; - u16 error; -} RX_ERROR_Q; - -typedef struct { - u8 active: 1; - u8 abr: 1; - u8 ubr: 1; - u8 cnt: 5; -#define VC_ACTIVE 0x01 -#define VC_ABR 0x02 -#define VC_UBR 0x04 -} vcstatus_t; - -struct ia_rfL_t { - u32 fdq_st; /* Free desc queue start address */ - u32 fdq_ed; /* Free desc queue end address */ - u32 fdq_rd; /* Free desc queue read pointer */ - u32 fdq_wr; /* Free desc queue write pointer */ - u32 pcq_st; /* Packet Complete queue start address */ - u32 pcq_ed; /* Packet Complete queue end address */ - u32 pcq_rd; /* Packet Complete queue read pointer */ - u32 pcq_wr; /* Packet Complete queue write pointer */ -}; - -struct ia_ffL_t { - u32 prq_st; /* Packet Ready Queue Start Address */ - u32 prq_ed; /* Packet Ready Queue End Address */ - u32 prq_wr; /* Packet Ready Queue write pointer */ - u32 tcq_st; /* Transmit Complete Queue Start Address*/ - u32 tcq_ed; /* Transmit Complete Queue End Address */ - u32 tcq_rd; /* Transmit Complete Queue read pointer */ -}; - -struct desc_tbl_t { - u32 timestamp; - struct ia_vcc *iavcc; - struct sk_buff *txskb; -}; - -typedef struct ia_rtn_q { - struct desc_tbl_t data; - struct ia_rtn_q *next, *tail; -} IARTN_Q; - -#define SUNI_LOSV 0x04 -enum ia_suni { - SUNI_MASTER_RESET = 0x000, /* SUNI Master Reset and Identity */ - SUNI_MASTER_CONFIG = 0x004, /* SUNI Master Configuration */ - SUNI_MASTER_INTR_STAT = 0x008, /* SUNI Master Interrupt Status */ - SUNI_RESERVED1 = 0x00c, /* Reserved */ - SUNI_MASTER_CLK_MONITOR = 0x010, /* SUNI Master Clock Monitor */ - SUNI_MASTER_CONTROL = 0x014, /* SUNI Master Clock Monitor */ - /* Reserved (10) */ - SUNI_RSOP_CONTROL = 0x040, /* RSOP Control/Interrupt Enable */ - SUNI_RSOP_STATUS = 0x044, /* RSOP Status/Interrupt States */ - SUNI_RSOP_SECTION_BIP8L = 0x048, /* RSOP Section BIP-8 LSB */ - SUNI_RSOP_SECTION_BIP8M = 0x04c, /* RSOP Section BIP-8 MSB */ - - SUNI_TSOP_CONTROL = 0x050, /* TSOP Control */ - SUNI_TSOP_DIAG = 0x054, /* TSOP Disgnostics */ - /* Reserved (2) */ - SUNI_RLOP_CS = 0x060, /* RLOP Control/Status */ - SUNI_RLOP_INTR = 0x064, /* RLOP Interrupt Enable/Status */ - SUNI_RLOP_LINE_BIP24L = 0x068, /* RLOP Line BIP-24 LSB */ - SUNI_RLOP_LINE_BIP24 = 0x06c, /* RLOP Line BIP-24 */ - SUNI_RLOP_LINE_BIP24M = 0x070, /* RLOP Line BIP-24 MSB */ - SUNI_RLOP_LINE_FEBEL = 0x074, /* RLOP Line FEBE LSB */ - SUNI_RLOP_LINE_FEBE = 0x078, /* RLOP Line FEBE */ - SUNI_RLOP_LINE_FEBEM = 0x07c, /* RLOP Line FEBE MSB */ - - SUNI_TLOP_CONTROL = 0x080, /* TLOP Control */ - SUNI_TLOP_DISG = 0x084, /* TLOP Disgnostics */ - /* Reserved (14) */ - SUNI_RPOP_CS = 0x0c0, /* RPOP Status/Control */ - SUNI_RPOP_INTR = 0x0c4, /* RPOP Interrupt/Status */ - SUNI_RPOP_RESERVED = 0x0c8, /* RPOP Reserved */ - SUNI_RPOP_INTR_ENA = 0x0cc, /* RPOP Interrupt Enable */ - /* Reserved (3) */ - SUNI_RPOP_PATH_SIG = 0x0dc, /* RPOP Path Signal Label */ - SUNI_RPOP_BIP8L = 0x0e0, /* RPOP Path BIP-8 LSB */ - SUNI_RPOP_BIP8M = 0x0e4, /* RPOP Path BIP-8 MSB */ - SUNI_RPOP_FEBEL = 0x0e8, /* RPOP Path FEBE LSB */ - SUNI_RPOP_FEBEM = 0x0ec, /* RPOP Path FEBE MSB */ - /* Reserved (4) */ - SUNI_TPOP_CNTRL_DAIG = 0x100, /* TPOP Control/Disgnostics */ - SUNI_TPOP_POINTER_CTRL = 0x104, /* TPOP Pointer Control */ - SUNI_TPOP_SOURCER_CTRL = 0x108, /* TPOP Source Control */ - /* Reserved (2) */ - SUNI_TPOP_ARB_PRTL = 0x114, /* TPOP Arbitrary Pointer LSB */ - SUNI_TPOP_ARB_PRTM = 0x118, /* TPOP Arbitrary Pointer MSB */ - SUNI_TPOP_RESERVED2 = 0x11c, /* TPOP Reserved */ - SUNI_TPOP_PATH_SIG = 0x120, /* TPOP Path Signal Lable */ - SUNI_TPOP_PATH_STATUS = 0x124, /* TPOP Path Status */ - /* Reserved (6) */ - SUNI_RACP_CS = 0x140, /* RACP Control/Status */ - SUNI_RACP_INTR = 0x144, /* RACP Interrupt Enable/Status */ - SUNI_RACP_HDR_PATTERN = 0x148, /* RACP Match Header Pattern */ - SUNI_RACP_HDR_MASK = 0x14c, /* RACP Match Header Mask */ - SUNI_RACP_CORR_HCS = 0x150, /* RACP Correctable HCS Error Count */ - SUNI_RACP_UNCORR_HCS = 0x154, /* RACP Uncorrectable HCS Err Count */ - /* Reserved (10) */ - SUNI_TACP_CONTROL = 0x180, /* TACP Control */ - SUNI_TACP_IDLE_HDR_PAT = 0x184, /* TACP Idle Cell Header Pattern */ - SUNI_TACP_IDLE_PAY_PAY = 0x188, /* TACP Idle Cell Payld Octet Patrn */ - /* Reserved (5) */ - /* Reserved (24) */ - /* FIXME: unused but name conflicts. - * SUNI_MASTER_TEST = 0x200, SUNI Master Test */ - SUNI_RESERVED_TEST = 0x204 /* SUNI Reserved for Test */ -}; - -typedef struct _SUNI_STATS_ -{ - u32 valid; // 1 = oc3 PHY card - u32 carrier_detect; // GPIN input - // RSOP: receive section overhead processor - u16 rsop_oof_state; // 1 = out of frame - u16 rsop_lof_state; // 1 = loss of frame - u16 rsop_los_state; // 1 = loss of signal - u32 rsop_los_count; // loss of signal count - u32 rsop_bse_count; // section BIP-8 error count - // RLOP: receive line overhead processor - u16 rlop_ferf_state; // 1 = far end receive failure - u16 rlop_lais_state; // 1 = line AIS - u32 rlop_lbe_count; // BIP-24 count - u32 rlop_febe_count; // FEBE count; - // RPOP: receive path overhead processor - u16 rpop_lop_state; // 1 = LOP - u16 rpop_pais_state; // 1 = path AIS - u16 rpop_pyel_state; // 1 = path yellow alert - u32 rpop_bip_count; // path BIP-8 error count - u32 rpop_febe_count; // path FEBE error count - u16 rpop_psig; // path signal label value - // RACP: receive ATM cell processor - u16 racp_hp_state; // hunt/presync state - u32 racp_fu_count; // FIFO underrun count - u32 racp_fo_count; // FIFO overrun count - u32 racp_chcs_count; // correctable HCS error count - u32 racp_uchcs_count; // uncorrectable HCS error count -} IA_SUNI_STATS; - -typedef struct iadev_priv { - /*-----base pointers into (i)chipSAR+ address space */ - u32 __iomem *phy; /* Base pointer into phy (SUNI). */ - u32 __iomem *dma; /* Base pointer into DMA control registers. */ - u32 __iomem *reg; /* Base pointer to SAR registers. */ - u32 __iomem *seg_reg; /* base pointer to segmentation engine - internal registers */ - u32 __iomem *reass_reg; /* base pointer to reassemble engine - internal registers */ - u32 __iomem *ram; /* base pointer to SAR RAM */ - void __iomem *seg_ram; - void __iomem *reass_ram; - struct dle_q tx_dle_q; - struct free_desc_q *tx_free_desc_qhead; - struct sk_buff_head tx_dma_q, tx_backlog; - spinlock_t tx_lock; - IARTN_Q tx_return_q; - u32 close_pending; - wait_queue_head_t close_wait; - wait_queue_head_t timeout_wait; - struct cpcs_trailer_desc *tx_buf; - u16 num_tx_desc, tx_buf_sz, rate_limit; - u32 tx_cell_cnt, tx_pkt_cnt; - void __iomem *MAIN_VC_TABLE_ADDR, *EXT_VC_TABLE_ADDR, *ABR_SCHED_TABLE_ADDR; - struct dle_q rx_dle_q; - struct free_desc_q *rx_free_desc_qhead; - struct sk_buff_head rx_dma_q; - spinlock_t rx_lock; - struct atm_vcc **rx_open; /* list of all open VCs */ - u16 num_rx_desc, rx_buf_sz, rxing; - u32 rx_pkt_ram, rx_tmp_cnt; - unsigned long rx_tmp_jif; - void __iomem *RX_DESC_BASE_ADDR; - u32 drop_rxpkt, drop_rxcell, rx_cell_cnt, rx_pkt_cnt; - struct atm_dev *next_board; /* other iphase devices */ - struct pci_dev *pci; - int mem; - unsigned int real_base; /* real and virtual base address */ - void __iomem *base; - unsigned int pci_map_size; /*pci map size of board */ - unsigned char irq; - unsigned char bus; - unsigned char dev_fn; - u_short phy_type; - u_short num_vc, memSize, memType; - struct ia_ffL_t ffL; - struct ia_rfL_t rfL; - /* Suni stat */ - // IA_SUNI_STATS suni_stats; - unsigned char carrier_detect; - /* CBR related */ - // transmit DMA & Receive - unsigned int tx_dma_cnt; // number of elements on dma queue - unsigned int rx_dma_cnt; // number of elements on rx dma queue - unsigned int NumEnabledCBR; // number of CBR VCI's enabled. CBR - // receive MARK for Cell FIFO - unsigned int rx_mark_cnt; // number of elements on mark queue - unsigned int CbrTotEntries; // Total CBR Entries in Scheduling Table. - unsigned int CbrRemEntries; // Remaining CBR Entries in Scheduling Table. - unsigned int CbrEntryPt; // CBR Sched Table Entry Point. - unsigned int Granularity; // CBR Granularity given Table Size. - /* ABR related */ - unsigned int sum_mcr, sum_cbr, LineRate; - unsigned int n_abr; - struct desc_tbl_t *desc_tbl; - u_short host_tcq_wr; - struct testTable_t **testTable; - dma_addr_t tx_dle_dma; - dma_addr_t rx_dle_dma; -} IADEV; - - -#define INPH_IA_DEV(d) ((IADEV *) (d)->dev_data) -#define INPH_IA_VCC(v) ((struct ia_vcc *) (v)->dev_data) - -/******************* IDT77105 25MB/s PHY DEFINE *****************************/ -enum ia_mb25 { - MB25_MASTER_CTRL = 0x00, /* Master control */ - MB25_INTR_STATUS = 0x04, /* Interrupt status */ - MB25_DIAG_CONTROL = 0x08, /* Diagnostic control */ - MB25_LED_HEC = 0x0c, /* LED driver and HEC status/control */ - MB25_LOW_BYTE_COUNTER = 0x10, - MB25_HIGH_BYTE_COUNTER = 0x14 -}; - -/* - * Master Control - */ -#define MB25_MC_UPLO 0x80 /* UPLO */ -#define MB25_MC_DREC 0x40 /* Discard receive cell errors */ -#define MB25_MC_ECEIO 0x20 /* Enable Cell Error Interrupts Only */ -#define MB25_MC_TDPC 0x10 /* Transmit data parity check */ -#define MB25_MC_DRIC 0x08 /* Discard receive idle cells */ -#define MB25_MC_HALTTX 0x04 /* Halt Tx */ -#define MB25_MC_UMS 0x02 /* UTOPIA mode select */ -#define MB25_MC_ENABLED 0x01 /* Enable interrupt */ - -/* - * Interrupt Status - */ -#define MB25_IS_GSB 0x40 /* GOOD Symbol Bit */ -#define MB25_IS_HECECR 0x20 /* HEC error cell received */ -#define MB25_IS_SCR 0x10 /* "Short Cell" Received */ -#define MB25_IS_TPE 0x08 /* Trnamsit Parity Error */ -#define MB25_IS_RSCC 0x04 /* Receive Signal Condition change */ -#define MB25_IS_RCSE 0x02 /* Received Cell Symbol Error */ -#define MB25_IS_RFIFOO 0x01 /* Received FIFO Overrun */ - -/* - * Diagnostic Control - */ -#define MB25_DC_FTXCD 0x80 /* Force TxClav deassert */ -#define MB25_DC_RXCOS 0x40 /* RxClav operation select */ -#define MB25_DC_ECEIO 0x20 /* Single/Multi-PHY config select */ -#define MB25_DC_RLFLUSH 0x10 /* Clear receive FIFO */ -#define MB25_DC_IXPE 0x08 /* Insert xmit payload error */ -#define MB25_DC_IXHECE 0x04 /* Insert Xmit HEC Error */ -#define MB25_DC_LB_MASK 0x03 /* Loopback control mask */ - -#define MB25_DC_LL 0x03 /* Line Loopback */ -#define MB25_DC_PL 0x02 /* PHY Loopback */ -#define MB25_DC_NM 0x00 - -#define FE_MASK 0x00F0 -#define FE_MULTI_MODE 0x0000 -#define FE_SINGLE_MODE 0x0010 -#define FE_UTP_OPTION 0x0020 -#define FE_25MBIT_PHY 0x0040 -#define FE_DS3_PHY 0x0080 /* DS3 */ -#define FE_E3_PHY 0x0090 /* E3 */ - -/*********************** SUNI_PM7345 PHY DEFINE HERE *********************/ -enum suni_pm7345 { - SUNI_CONFIG = 0x000, /* SUNI Configuration */ - SUNI_INTR_ENBL = 0x004, /* SUNI Interrupt Enable */ - SUNI_INTR_STAT = 0x008, /* SUNI Interrupt Status */ - SUNI_CONTROL = 0x00c, /* SUNI Control */ - SUNI_ID_RESET = 0x010, /* SUNI Reset and Identity */ - SUNI_DATA_LINK_CTRL = 0x014, - SUNI_RBOC_CONF_INTR_ENBL = 0x018, - SUNI_RBOC_STAT = 0x01c, - SUNI_DS3_FRM_CFG = 0x020, - SUNI_DS3_FRM_INTR_ENBL = 0x024, - SUNI_DS3_FRM_INTR_STAT = 0x028, - SUNI_DS3_FRM_STAT = 0x02c, - SUNI_RFDL_CFG = 0x030, - SUNI_RFDL_ENBL_STAT = 0x034, - SUNI_RFDL_STAT = 0x038, - SUNI_RFDL_DATA = 0x03c, - SUNI_PMON_CHNG = 0x040, - SUNI_PMON_INTR_ENBL_STAT = 0x044, - /* SUNI_RESERVED1 (0x13 - 0x11) */ - SUNI_PMON_LCV_EVT_CNT_LSB = 0x050, - SUNI_PMON_LCV_EVT_CNT_MSB = 0x054, - SUNI_PMON_FBE_EVT_CNT_LSB = 0x058, - SUNI_PMON_FBE_EVT_CNT_MSB = 0x05c, - SUNI_PMON_SEZ_DET_CNT_LSB = 0x060, - SUNI_PMON_SEZ_DET_CNT_MSB = 0x064, - SUNI_PMON_PE_EVT_CNT_LSB = 0x068, - SUNI_PMON_PE_EVT_CNT_MSB = 0x06c, - SUNI_PMON_PPE_EVT_CNT_LSB = 0x070, - SUNI_PMON_PPE_EVT_CNT_MSB = 0x074, - SUNI_PMON_FEBE_EVT_CNT_LSB = 0x078, - SUNI_PMON_FEBE_EVT_CNT_MSB = 0x07c, - SUNI_DS3_TRAN_CFG = 0x080, - SUNI_DS3_TRAN_DIAG = 0x084, - /* SUNI_RESERVED2 (0x23 - 0x21) */ - SUNI_XFDL_CFG = 0x090, - SUNI_XFDL_INTR_ST = 0x094, - SUNI_XFDL_XMIT_DATA = 0x098, - SUNI_XBOC_CODE = 0x09c, - SUNI_SPLR_CFG = 0x0a0, - SUNI_SPLR_INTR_EN = 0x0a4, - SUNI_SPLR_INTR_ST = 0x0a8, - SUNI_SPLR_STATUS = 0x0ac, - SUNI_SPLT_CFG = 0x0b0, - SUNI_SPLT_CNTL = 0x0b4, - SUNI_SPLT_DIAG_G1 = 0x0b8, - SUNI_SPLT_F1 = 0x0bc, - SUNI_CPPM_LOC_METERS = 0x0c0, - SUNI_CPPM_CHG_OF_CPPM_PERF_METR = 0x0c4, - SUNI_CPPM_B1_ERR_CNT_LSB = 0x0c8, - SUNI_CPPM_B1_ERR_CNT_MSB = 0x0cc, - SUNI_CPPM_FRAMING_ERR_CNT_LSB = 0x0d0, - SUNI_CPPM_FRAMING_ERR_CNT_MSB = 0x0d4, - SUNI_CPPM_FEBE_CNT_LSB = 0x0d8, - SUNI_CPPM_FEBE_CNT_MSB = 0x0dc, - SUNI_CPPM_HCS_ERR_CNT_LSB = 0x0e0, - SUNI_CPPM_HCS_ERR_CNT_MSB = 0x0e4, - SUNI_CPPM_IDLE_UN_CELL_CNT_LSB = 0x0e8, - SUNI_CPPM_IDLE_UN_CELL_CNT_MSB = 0x0ec, - SUNI_CPPM_RCV_CELL_CNT_LSB = 0x0f0, - SUNI_CPPM_RCV_CELL_CNT_MSB = 0x0f4, - SUNI_CPPM_XMIT_CELL_CNT_LSB = 0x0f8, - SUNI_CPPM_XMIT_CELL_CNT_MSB = 0x0fc, - SUNI_RXCP_CTRL = 0x100, - SUNI_RXCP_FCTRL = 0x104, - SUNI_RXCP_INTR_EN_STS = 0x108, - SUNI_RXCP_IDLE_PAT_H1 = 0x10c, - SUNI_RXCP_IDLE_PAT_H2 = 0x110, - SUNI_RXCP_IDLE_PAT_H3 = 0x114, - SUNI_RXCP_IDLE_PAT_H4 = 0x118, - SUNI_RXCP_IDLE_MASK_H1 = 0x11c, - SUNI_RXCP_IDLE_MASK_H2 = 0x120, - SUNI_RXCP_IDLE_MASK_H3 = 0x124, - SUNI_RXCP_IDLE_MASK_H4 = 0x128, - SUNI_RXCP_CELL_PAT_H1 = 0x12c, - SUNI_RXCP_CELL_PAT_H2 = 0x130, - SUNI_RXCP_CELL_PAT_H3 = 0x134, - SUNI_RXCP_CELL_PAT_H4 = 0x138, - SUNI_RXCP_CELL_MASK_H1 = 0x13c, - SUNI_RXCP_CELL_MASK_H2 = 0x140, - SUNI_RXCP_CELL_MASK_H3 = 0x144, - SUNI_RXCP_CELL_MASK_H4 = 0x148, - SUNI_RXCP_HCS_CS = 0x14c, - SUNI_RXCP_LCD_CNT_THRESHOLD = 0x150, - /* SUNI_RESERVED3 (0x57 - 0x54) */ - SUNI_TXCP_CTRL = 0x160, - SUNI_TXCP_INTR_EN_STS = 0x164, - SUNI_TXCP_IDLE_PAT_H1 = 0x168, - SUNI_TXCP_IDLE_PAT_H2 = 0x16c, - SUNI_TXCP_IDLE_PAT_H3 = 0x170, - SUNI_TXCP_IDLE_PAT_H4 = 0x174, - SUNI_TXCP_IDLE_PAT_H5 = 0x178, - SUNI_TXCP_IDLE_PAYLOAD = 0x17c, - SUNI_E3_FRM_FRAM_OPTIONS = 0x180, - SUNI_E3_FRM_MAINT_OPTIONS = 0x184, - SUNI_E3_FRM_FRAM_INTR_ENBL = 0x188, - SUNI_E3_FRM_FRAM_INTR_IND_STAT = 0x18c, - SUNI_E3_FRM_MAINT_INTR_ENBL = 0x190, - SUNI_E3_FRM_MAINT_INTR_IND = 0x194, - SUNI_E3_FRM_MAINT_STAT = 0x198, - SUNI_RESERVED4 = 0x19c, - SUNI_E3_TRAN_FRAM_OPTIONS = 0x1a0, - SUNI_E3_TRAN_STAT_DIAG_OPTIONS = 0x1a4, - SUNI_E3_TRAN_BIP_8_ERR_MASK = 0x1a8, - SUNI_E3_TRAN_MAINT_ADAPT_OPTS = 0x1ac, - SUNI_TTB_CTRL = 0x1b0, - SUNI_TTB_TRAIL_TRACE_ID_STAT = 0x1b4, - SUNI_TTB_IND_ADDR = 0x1b8, - SUNI_TTB_IND_DATA = 0x1bc, - SUNI_TTB_EXP_PAYLOAD_TYPE = 0x1c0, - SUNI_TTB_PAYLOAD_TYPE_CTRL_STAT = 0x1c4, - /* SUNI_PAD5 (0x7f - 0x71) */ - SUNI_MASTER_TEST = 0x200, - /* SUNI_PAD6 (0xff - 0x80) */ -}; - -#define SUNI_PM7345_T suni_pm7345_t -#define SUNI_PM7345 0x20 /* Suni chip type */ -#define SUNI_PM5346 0x30 /* Suni chip type */ -/* - * SUNI_PM7345 Configuration - */ -#define SUNI_PM7345_CLB 0x01 /* Cell loopback */ -#define SUNI_PM7345_PLB 0x02 /* Payload loopback */ -#define SUNI_PM7345_DLB 0x04 /* Diagnostic loopback */ -#define SUNI_PM7345_LLB 0x80 /* Line loopback */ -#define SUNI_PM7345_E3ENBL 0x40 /* E3 enable bit */ -#define SUNI_PM7345_LOOPT 0x10 /* LOOPT enable bit */ -#define SUNI_PM7345_FIFOBP 0x20 /* FIFO bypass */ -#define SUNI_PM7345_FRMRBP 0x08 /* Framer bypass */ -/* - * DS3 FRMR Interrupt Enable - */ -#define SUNI_DS3_COFAE 0x80 /* Enable change of frame align */ -#define SUNI_DS3_REDE 0x40 /* Enable DS3 RED state intr */ -#define SUNI_DS3_CBITE 0x20 /* Enable Appl ID channel intr */ -#define SUNI_DS3_FERFE 0x10 /* Enable Far End Receive Failure intr*/ -#define SUNI_DS3_IDLE 0x08 /* Enable Idle signal intr */ -#define SUNI_DS3_AISE 0x04 /* Enable Alarm Indication signal intr*/ -#define SUNI_DS3_OOFE 0x02 /* Enable Out of frame intr */ -#define SUNI_DS3_LOSE 0x01 /* Enable Loss of signal intr */ - -/* - * DS3 FRMR Status - */ -#define SUNI_DS3_ACE 0x80 /* Additional Configuration Reg */ -#define SUNI_DS3_REDV 0x40 /* DS3 RED state */ -#define SUNI_DS3_CBITV 0x20 /* Application ID channel state */ -#define SUNI_DS3_FERFV 0x10 /* Far End Receive Failure state*/ -#define SUNI_DS3_IDLV 0x08 /* Idle signal state */ -#define SUNI_DS3_AISV 0x04 /* Alarm Indication signal state*/ -#define SUNI_DS3_OOFV 0x02 /* Out of frame state */ -#define SUNI_DS3_LOSV 0x01 /* Loss of signal state */ - -/* - * E3 FRMR Interrupt/Status - */ -#define SUNI_E3_CZDI 0x40 /* Consecutive Zeros indicator */ -#define SUNI_E3_LOSI 0x20 /* Loss of signal intr status */ -#define SUNI_E3_LCVI 0x10 /* Line code violation intr */ -#define SUNI_E3_COFAI 0x08 /* Change of frame align intr */ -#define SUNI_E3_OOFI 0x04 /* Out of frame intr status */ -#define SUNI_E3_LOS 0x02 /* Loss of signal state */ -#define SUNI_E3_OOF 0x01 /* Out of frame state */ - -/* - * E3 FRMR Maintenance Status - */ -#define SUNI_E3_AISD 0x80 /* Alarm Indication signal state*/ -#define SUNI_E3_FERF_RAI 0x40 /* FERF/RAI indicator */ -#define SUNI_E3_FEBE 0x20 /* Far End Block Error indicator*/ - -/* - * RXCP Control/Status - */ -#define SUNI_DS3_HCSPASS 0x80 /* Pass cell with HEC errors */ -#define SUNI_DS3_HCSDQDB 0x40 /* Control octets in HCS calc */ -#define SUNI_DS3_HCSADD 0x20 /* Add coset poly */ -#define SUNI_DS3_HCK 0x10 /* Control FIFO data path integ chk*/ -#define SUNI_DS3_BLOCK 0x08 /* Enable cell filtering */ -#define SUNI_DS3_DSCR 0x04 /* Disable payload descrambling */ -#define SUNI_DS3_OOCDV 0x02 /* Cell delineation state */ -#define SUNI_DS3_FIFORST 0x01 /* Cell FIFO reset */ - -/* - * RXCP Interrupt Enable/Status - */ -#define SUNI_DS3_OOCDE 0x80 /* Intr enable, change in CDS */ -#define SUNI_DS3_HCSE 0x40 /* Intr enable, corr HCS errors */ -#define SUNI_DS3_FIFOE 0x20 /* Intr enable, unco HCS errors */ -#define SUNI_DS3_OOCDI 0x10 /* SYNC state */ -#define SUNI_DS3_UHCSI 0x08 /* Uncorr. HCS errors detected */ -#define SUNI_DS3_COCAI 0x04 /* Corr. HCS errors detected */ -#define SUNI_DS3_FOVRI 0x02 /* FIFO overrun */ -#define SUNI_DS3_FUDRI 0x01 /* FIFO underrun */ - -///////////////////SUNI_PM7345 PHY DEFINE END ///////////////////////////// - -/* ia_eeprom define*/ -#define MEM_SIZE_MASK 0x000F /* mask of 4 bits defining memory size*/ -#define MEM_SIZE_128K 0x0000 /* board has 128k buffer */ -#define MEM_SIZE_512K 0x0001 /* board has 512K of buffer */ -#define MEM_SIZE_1M 0x0002 /* board has 1M of buffer */ - /* 0x3 to 0xF are reserved for future */ - -#define FE_MASK 0x00F0 /* mask of 4 bits defining FE type */ -#define FE_MULTI_MODE 0x0000 /* 155 MBit multimode fiber */ -#define FE_SINGLE_MODE 0x0010 /* 155 MBit single mode laser */ -#define FE_UTP_OPTION 0x0020 /* 155 MBit UTP front end */ - -#define NOVRAM_SIZE 64 -#define CMD_LEN 10 - -/*********** - * - * Switches and defines for header files. - * - * The following defines are used to turn on and off - * various options in the header files. Primarily useful - * for debugging. - * - ***********/ - -/* - * a list of the commands that can be sent to the NOVRAM - */ - -#define EXTEND 0x100 -#define IAWRITE 0x140 -#define IAREAD 0x180 -#define ERASE 0x1c0 - -#define EWDS 0x00 -#define WRAL 0x10 -#define ERAL 0x20 -#define EWEN 0x30 - -/* - * these bits duplicate the hw_flip.h register settings - * note: how the data in / out bits are defined in the flipper specification - */ - -#define NVCE 0x02 -#define NVSK 0x01 -#define NVDO 0x08 -#define NVDI 0x04 -/*********************** - * - * This define ands the value and the current config register and puts - * the result in the config register - * - ***********************/ - -#define CFG_AND(val) { \ - u32 t; \ - t = readl(iadev->reg+IPHASE5575_EEPROM_ACCESS); \ - t &= (val); \ - writel(t, iadev->reg+IPHASE5575_EEPROM_ACCESS); \ - } - -/*********************** - * - * This define ors the value and the current config register and puts - * the result in the config register - * - ***********************/ - -#define CFG_OR(val) { \ - u32 t; \ - t = readl(iadev->reg+IPHASE5575_EEPROM_ACCESS); \ - t |= (val); \ - writel(t, iadev->reg+IPHASE5575_EEPROM_ACCESS); \ - } - -/*********************** - * - * Send a command to the NOVRAM, the command is in cmd. - * - * clear CE and SK. Then assert CE. - * Clock each of the command bits out in the correct order with SK - * exit with CE still asserted - * - ***********************/ - -#define NVRAM_CMD(cmd) { \ - int i; \ - u_short c = cmd; \ - CFG_AND(~(NVCE|NVSK)); \ - CFG_OR(NVCE); \ - for (i=0; ireg+IPHASE5575_EEPROM_ACCESS); \ - value = (_t & NVDO) ? 1 : 0; \ - } - - -#endif /* IPHASE_H */ diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c deleted file mode 100644 index d6af999a9ebb..000000000000 --- a/drivers/atm/lanai.c +++ /dev/null @@ -1,2603 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* lanai.c -- Copyright 1999-2003 by Mitchell Blank Jr - * - * This driver supports ATM cards based on the Efficient "Lanai" - * chipset such as the Speedstream 3010 and the ENI-25p. The - * Speedstream 3060 is currently not supported since we don't - * have the code to drive the on-board Alcatel DSL chipset (yet). - * - * Thanks to Efficient for supporting this project with hardware, - * documentation, and by answering my questions. - * - * Things not working yet: - * - * o We don't support the Speedstream 3060 yet - this card has - * an on-board DSL modem chip by Alcatel and the driver will - * need some extra code added to handle it - * - * o Note that due to limitations of the Lanai only one VCC can be - * in CBR at once - * - * o We don't currently parse the EEPROM at all. The code is all - * there as per the spec, but it doesn't actually work. I think - * there may be some issues with the docs. Anyway, do NOT - * enable it yet - bugs in that code may actually damage your - * hardware! Because of this you should hardware an ESI before - * trying to use this in a LANE or MPOA environment. - * - * o AAL0 is stubbed in but the actual rx/tx path isn't written yet: - * vcc_tx_aal0() needs to send or queue a SKB - * vcc_tx_unqueue_aal0() needs to attempt to send queued SKBs - * vcc_rx_aal0() needs to handle AAL0 interrupts - * This isn't too much work - I just wanted to get other things - * done first. - * - * o lanai_change_qos() isn't written yet - * - * o There aren't any ioctl's yet -- I'd like to eventually support - * setting loopback and LED modes that way. - * - * o If the segmentation engine or DMA gets shut down we should restart - * card as per section 17.0i. (see lanai_reset) - * - * o setsockopt(SO_CIRANGE) isn't done (although despite what the - * API says it isn't exactly commonly implemented) - */ - -/* Version history: - * v.1.00 -- 26-JUL-2003 -- PCI/DMA updates - * v.0.02 -- 11-JAN-2000 -- Endian fixes - * v.0.01 -- 30-NOV-1999 -- Initial release - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* -------------------- TUNABLE PARAMATERS: */ - -/* - * Maximum number of VCIs per card. Setting it lower could theoretically - * save some memory, but since we allocate our vcc list with get_free_pages, - * it's not really likely for most architectures - */ -#define NUM_VCI (1024) - -/* - * Enable extra debugging - */ -#define DEBUG -/* - * Debug _all_ register operations with card, except the memory test. - * Also disables the timed poll to prevent extra chattiness. This - * isn't for normal use - */ -#undef DEBUG_RW - -/* - * The programming guide specifies a full test of the on-board SRAM - * at initialization time. Undefine to remove this - */ -#define FULL_MEMORY_TEST - -/* - * This is the number of (4 byte) service entries that we will - * try to allocate at startup. Note that we will end up with - * one PAGE_SIZE's worth regardless of what this is set to - */ -#define SERVICE_ENTRIES (1024) -/* TODO: make above a module load-time option */ - -/* - * We normally read the onboard EEPROM in order to discover our MAC - * address. Undefine to _not_ do this - */ -/* #define READ_EEPROM */ /* ***DONT ENABLE YET*** */ -/* TODO: make above a module load-time option (also) */ - -/* - * Depth of TX fifo (in 128 byte units; range 2-31) - * Smaller numbers are better for network latency - * Larger numbers are better for PCI latency - * I'm really sure where the best tradeoff is, but the BSD driver uses - * 7 and it seems to work ok. - */ -#define TX_FIFO_DEPTH (7) -/* TODO: make above a module load-time option */ - -/* - * How often (in jiffies) we will try to unstick stuck connections - - * shouldn't need to happen much - */ -#define LANAI_POLL_PERIOD (10*HZ) -/* TODO: make above a module load-time option */ - -/* - * When allocating an AAL5 receiving buffer, try to make it at least - * large enough to hold this many max_sdu sized PDUs - */ -#define AAL5_RX_MULTIPLIER (3) -/* TODO: make above a module load-time option */ - -/* - * Same for transmitting buffer - */ -#define AAL5_TX_MULTIPLIER (3) -/* TODO: make above a module load-time option */ - -/* - * When allocating an AAL0 transmiting buffer, how many cells should fit. - * Remember we'll end up with a PAGE_SIZE of them anyway, so this isn't - * really critical - */ -#define AAL0_TX_MULTIPLIER (40) -/* TODO: make above a module load-time option */ - -/* - * How large should we make the AAL0 receiving buffer. Remember that this - * is shared between all AAL0 VC's - */ -#define AAL0_RX_BUFFER_SIZE (PAGE_SIZE) -/* TODO: make above a module load-time option */ - -/* - * Should we use Lanai's "powerdown" feature when no vcc's are bound? - */ -/* #define USE_POWERDOWN */ -/* TODO: make above a module load-time option (also) */ - -/* -------------------- DEBUGGING AIDS: */ - -#define DEV_LABEL "lanai" - -#ifdef DEBUG - -#define DPRINTK(format, args...) \ - printk(KERN_DEBUG DEV_LABEL ": " format, ##args) -#define APRINTK(truth, format, args...) \ - do { \ - if (unlikely(!(truth))) \ - printk(KERN_ERR DEV_LABEL ": " format, ##args); \ - } while (0) - -#else /* !DEBUG */ - -#define DPRINTK(format, args...) -#define APRINTK(truth, format, args...) - -#endif /* DEBUG */ - -#ifdef DEBUG_RW -#define RWDEBUG(format, args...) \ - printk(KERN_DEBUG DEV_LABEL ": " format, ##args) -#else /* !DEBUG_RW */ -#define RWDEBUG(format, args...) -#endif - -/* -------------------- DATA DEFINITIONS: */ - -#define LANAI_MAPPING_SIZE (0x40000) -#define LANAI_EEPROM_SIZE (128) - -typedef int vci_t; -typedef void __iomem *bus_addr_t; - -/* DMA buffer in host memory for TX, RX, or service list. */ -struct lanai_buffer { - u32 *start; /* From get_free_pages */ - u32 *end; /* One past last byte */ - u32 *ptr; /* Pointer to current host location */ - dma_addr_t dmaaddr; -}; - -struct lanai_vcc_stats { - unsigned rx_nomem; - union { - struct { - unsigned rx_badlen; - unsigned service_trash; - unsigned service_stream; - unsigned service_rxcrc; - } aal5; - struct { - } aal0; - } x; -}; - -struct lanai_dev; /* Forward declaration */ - -/* - * This is the card-specific per-vcc data. Note that unlike some other - * drivers there is NOT a 1-to-1 correspondance between these and - * atm_vcc's - each one of these represents an actual 2-way vcc, but - * an atm_vcc can be 1-way and share with a 1-way vcc in the other - * direction. To make it weirder, there can even be 0-way vccs - * bound to us, waiting to do a change_qos - */ -struct lanai_vcc { - bus_addr_t vbase; /* Base of VCC's registers */ - struct lanai_vcc_stats stats; - int nref; /* # of atm_vcc's who reference us */ - vci_t vci; - struct { - struct lanai_buffer buf; - struct atm_vcc *atmvcc; /* atm_vcc who is receiver */ - } rx; - struct { - struct lanai_buffer buf; - struct atm_vcc *atmvcc; /* atm_vcc who is transmitter */ - int endptr; /* last endptr from service entry */ - struct sk_buff_head backlog; - void (*unqueue)(struct lanai_dev *, struct lanai_vcc *, int); - } tx; -}; - -enum lanai_type { - lanai2 = PCI_DEVICE_ID_EF_ATM_LANAI2, - lanaihb = PCI_DEVICE_ID_EF_ATM_LANAIHB -}; - -struct lanai_dev_stats { - unsigned ovfl_trash; /* # of cells dropped - buffer overflow */ - unsigned vci_trash; /* # of cells dropped - closed vci */ - unsigned hec_err; /* # of cells dropped - bad HEC */ - unsigned atm_ovfl; /* # of cells dropped - rx fifo overflow */ - unsigned pcierr_parity_detect; - unsigned pcierr_serr_set; - unsigned pcierr_master_abort; - unsigned pcierr_m_target_abort; - unsigned pcierr_s_target_abort; - unsigned pcierr_master_parity; - unsigned service_notx; - unsigned service_norx; - unsigned service_rxnotaal5; - unsigned dma_reenable; - unsigned card_reset; -}; - -struct lanai_dev { - bus_addr_t base; - struct lanai_dev_stats stats; - struct lanai_buffer service; - struct lanai_vcc **vccs; -#ifdef USE_POWERDOWN - int nbound; /* number of bound vccs */ -#endif - enum lanai_type type; - vci_t num_vci; /* Currently just NUM_VCI */ - u8 eeprom[LANAI_EEPROM_SIZE]; - u32 serialno, magicno; - struct pci_dev *pci; - DECLARE_BITMAP(backlog_vccs, NUM_VCI); /* VCCs with tx backlog */ - DECLARE_BITMAP(transmit_ready, NUM_VCI); /* VCCs with transmit space */ - struct timer_list timer; - int naal0; - struct lanai_buffer aal0buf; /* AAL0 RX buffers */ - u32 conf1, conf2; /* CONFIG[12] registers */ - u32 status; /* STATUS register */ - spinlock_t endtxlock; - spinlock_t servicelock; - struct atm_vcc *cbrvcc; - int number; - int board_rev; -/* TODO - look at race conditions with maintence of conf1/conf2 */ -/* TODO - transmit locking: should we use _irq not _irqsave? */ -/* TODO - organize above in some rational fashion (see ) */ -}; - -/* - * Each device has two bitmaps for each VCC (baclog_vccs and transmit_ready) - * This function iterates one of these, calling a given function for each - * vci with their bit set - */ -static void vci_bitfield_iterate(struct lanai_dev *lanai, - const unsigned long *lp, - void (*func)(struct lanai_dev *,vci_t vci)) -{ - vci_t vci; - - for_each_set_bit(vci, lp, NUM_VCI) - func(lanai, vci); -} - -/* -------------------- BUFFER UTILITIES: */ - -/* - * Lanai needs DMA buffers aligned to 256 bytes of at least 1024 bytes - - * usually any page allocation will do. Just to be safe in case - * PAGE_SIZE is insanely tiny, though... - */ -#define LANAI_PAGE_SIZE ((PAGE_SIZE >= 1024) ? PAGE_SIZE : 1024) - -/* - * Allocate a buffer in host RAM for service list, RX, or TX - * Returns buf->start==NULL if no memory - * Note that the size will be rounded up 2^n bytes, and - * if we can't allocate that we'll settle for something smaller - * until minbytes - */ -static void lanai_buf_allocate(struct lanai_buffer *buf, - size_t bytes, size_t minbytes, struct pci_dev *pci) -{ - int size; - - if (bytes > (128 * 1024)) /* max lanai buffer size */ - bytes = 128 * 1024; - for (size = LANAI_PAGE_SIZE; size < bytes; size *= 2) - ; - if (minbytes < LANAI_PAGE_SIZE) - minbytes = LANAI_PAGE_SIZE; - do { - /* - * Technically we could use non-consistent mappings for - * everything, but the way the lanai uses DMA memory would - * make that a terrific pain. This is much simpler. - */ - buf->start = dma_alloc_coherent(&pci->dev, - size, &buf->dmaaddr, GFP_KERNEL); - if (buf->start != NULL) { /* Success */ - /* Lanai requires 256-byte alignment of DMA bufs */ - APRINTK((buf->dmaaddr & ~0xFFFFFF00) == 0, - "bad dmaaddr: 0x%lx\n", - (unsigned long) buf->dmaaddr); - buf->ptr = buf->start; - buf->end = (u32 *) - (&((unsigned char *) buf->start)[size]); - memset(buf->start, 0, size); - break; - } - size /= 2; - } while (size >= minbytes); -} - -/* size of buffer in bytes */ -static inline size_t lanai_buf_size(const struct lanai_buffer *buf) -{ - return ((unsigned long) buf->end) - ((unsigned long) buf->start); -} - -static void lanai_buf_deallocate(struct lanai_buffer *buf, - struct pci_dev *pci) -{ - if (buf->start != NULL) { - dma_free_coherent(&pci->dev, lanai_buf_size(buf), - buf->start, buf->dmaaddr); - buf->start = buf->end = buf->ptr = NULL; - } -} - -/* size of buffer as "card order" (0=1k .. 7=128k) */ -static int lanai_buf_size_cardorder(const struct lanai_buffer *buf) -{ - int order = get_order(lanai_buf_size(buf)) + (PAGE_SHIFT - 10); - - /* This can only happen if PAGE_SIZE is gigantic, but just in case */ - if (order > 7) - order = 7; - return order; -} - -/* -------------------- PORT I/O UTILITIES: */ - -/* Registers (and their bit-fields) */ -enum lanai_register { - Reset_Reg = 0x00, /* Reset; read for chip type; bits: */ -#define RESET_GET_BOARD_REV(x) (((x)>> 0)&0x03) /* Board revision */ -#define RESET_GET_BOARD_ID(x) (((x)>> 2)&0x03) /* Board ID */ -#define BOARD_ID_LANAI256 (0) /* 25.6M adapter card */ - Endian_Reg = 0x04, /* Endian setting */ - IntStatus_Reg = 0x08, /* Interrupt status */ - IntStatusMasked_Reg = 0x0C, /* Interrupt status (masked) */ - IntAck_Reg = 0x10, /* Interrupt acknowledge */ - IntAckMasked_Reg = 0x14, /* Interrupt acknowledge (masked) */ - IntStatusSet_Reg = 0x18, /* Get status + enable/disable */ - IntStatusSetMasked_Reg = 0x1C, /* Get status + en/di (masked) */ - IntControlEna_Reg = 0x20, /* Interrupt control enable */ - IntControlDis_Reg = 0x24, /* Interrupt control disable */ - Status_Reg = 0x28, /* Status */ -#define STATUS_PROMDATA (0x00000001) /* PROM_DATA pin */ -#define STATUS_WAITING (0x00000002) /* Interrupt being delayed */ -#define STATUS_SOOL (0x00000004) /* SOOL alarm */ -#define STATUS_LOCD (0x00000008) /* LOCD alarm */ -#define STATUS_LED (0x00000010) /* LED (HAPPI) output */ -#define STATUS_GPIN (0x00000020) /* GPIN pin */ -#define STATUS_BUTTBUSY (0x00000040) /* Butt register is pending */ - Config1_Reg = 0x2C, /* Config word 1; bits: */ -#define CONFIG1_PROMDATA (0x00000001) /* PROM_DATA pin */ -#define CONFIG1_PROMCLK (0x00000002) /* PROM_CLK pin */ -#define CONFIG1_SET_READMODE(x) ((x)*0x004) /* PCI BM reads; values: */ -#define READMODE_PLAIN (0) /* Plain memory read */ -#define READMODE_LINE (2) /* Memory read line */ -#define READMODE_MULTIPLE (3) /* Memory read multiple */ -#define CONFIG1_DMA_ENABLE (0x00000010) /* Turn on DMA */ -#define CONFIG1_POWERDOWN (0x00000020) /* Turn off clocks */ -#define CONFIG1_SET_LOOPMODE(x) ((x)*0x080) /* Clock&loop mode; values: */ -#define LOOPMODE_NORMAL (0) /* Normal - no loop */ -#define LOOPMODE_TIME (1) -#define LOOPMODE_DIAG (2) -#define LOOPMODE_LINE (3) -#define CONFIG1_MASK_LOOPMODE (0x00000180) -#define CONFIG1_SET_LEDMODE(x) ((x)*0x0200) /* Mode of LED; values: */ -#define LEDMODE_NOT_SOOL (0) /* !SOOL */ -#define LEDMODE_OFF (1) /* 0 */ -#define LEDMODE_ON (2) /* 1 */ -#define LEDMODE_NOT_LOCD (3) /* !LOCD */ -#define LEDMORE_GPIN (4) /* GPIN */ -#define LEDMODE_NOT_GPIN (7) /* !GPIN */ -#define CONFIG1_MASK_LEDMODE (0x00000E00) -#define CONFIG1_GPOUT1 (0x00001000) /* Toggle for reset */ -#define CONFIG1_GPOUT2 (0x00002000) /* Loopback PHY */ -#define CONFIG1_GPOUT3 (0x00004000) /* Loopback lanai */ - Config2_Reg = 0x30, /* Config word 2; bits: */ -#define CONFIG2_HOWMANY (0x00000001) /* >512 VCIs? */ -#define CONFIG2_PTI7_MODE (0x00000002) /* Make PTI=7 RM, not OAM */ -#define CONFIG2_VPI_CHK_DIS (0x00000004) /* Ignore RX VPI value */ -#define CONFIG2_HEC_DROP (0x00000008) /* Drop cells w/ HEC errors */ -#define CONFIG2_VCI0_NORMAL (0x00000010) /* Treat VCI=0 normally */ -#define CONFIG2_CBR_ENABLE (0x00000020) /* Deal with CBR traffic */ -#define CONFIG2_TRASH_ALL (0x00000040) /* Trashing incoming cells */ -#define CONFIG2_TX_DISABLE (0x00000080) /* Trashing outgoing cells */ -#define CONFIG2_SET_TRASH (0x00000100) /* Turn trashing on */ - Statistics_Reg = 0x34, /* Statistics; bits: */ -#define STATS_GET_FIFO_OVFL(x) (((x)>> 0)&0xFF) /* FIFO overflowed */ -#define STATS_GET_HEC_ERR(x) (((x)>> 8)&0xFF) /* HEC was bad */ -#define STATS_GET_BAD_VCI(x) (((x)>>16)&0xFF) /* VCI not open */ -#define STATS_GET_BUF_OVFL(x) (((x)>>24)&0xFF) /* VCC buffer full */ - ServiceStuff_Reg = 0x38, /* Service stuff; bits: */ -#define SSTUFF_SET_SIZE(x) ((x)*0x20000000) /* size of service buffer */ -#define SSTUFF_SET_ADDR(x) ((x)>>8) /* set address of buffer */ - ServWrite_Reg = 0x3C, /* ServWrite Pointer */ - ServRead_Reg = 0x40, /* ServRead Pointer */ - TxDepth_Reg = 0x44, /* FIFO Transmit Depth */ - Butt_Reg = 0x48, /* Butt register */ - CBR_ICG_Reg = 0x50, - CBR_PTR_Reg = 0x54, - PingCount_Reg = 0x58, /* Ping count */ - DMA_Addr_Reg = 0x5C /* DMA address */ -}; - -static inline bus_addr_t reg_addr(const struct lanai_dev *lanai, - enum lanai_register reg) -{ - return lanai->base + reg; -} - -static inline u32 reg_read(const struct lanai_dev *lanai, - enum lanai_register reg) -{ - u32 t; - t = readl(reg_addr(lanai, reg)); - RWDEBUG("R [0x%08X] 0x%02X = 0x%08X\n", (unsigned int) lanai->base, - (int) reg, t); - return t; -} - -static inline void reg_write(const struct lanai_dev *lanai, u32 val, - enum lanai_register reg) -{ - RWDEBUG("W [0x%08X] 0x%02X < 0x%08X\n", (unsigned int) lanai->base, - (int) reg, val); - writel(val, reg_addr(lanai, reg)); -} - -static inline void conf1_write(const struct lanai_dev *lanai) -{ - reg_write(lanai, lanai->conf1, Config1_Reg); -} - -static inline void conf2_write(const struct lanai_dev *lanai) -{ - reg_write(lanai, lanai->conf2, Config2_Reg); -} - -/* Same as conf2_write(), but defers I/O if we're powered down */ -static inline void conf2_write_if_powerup(const struct lanai_dev *lanai) -{ -#ifdef USE_POWERDOWN - if (unlikely((lanai->conf1 & CONFIG1_POWERDOWN) != 0)) - return; -#endif /* USE_POWERDOWN */ - conf2_write(lanai); -} - -static inline void reset_board(const struct lanai_dev *lanai) -{ - DPRINTK("about to reset board\n"); - reg_write(lanai, 0, Reset_Reg); - /* - * If we don't delay a little while here then we can end up - * leaving the card in a VERY weird state and lock up the - * PCI bus. This isn't documented anywhere but I've convinced - * myself after a lot of painful experimentation - */ - udelay(5); -} - -/* -------------------- CARD SRAM UTILITIES: */ - -/* The SRAM is mapped into normal PCI memory space - the only catch is - * that it is only 16-bits wide but must be accessed as 32-bit. The - * 16 high bits will be zero. We don't hide this, since they get - * programmed mostly like discrete registers anyway - */ -#define SRAM_START (0x20000) -#define SRAM_BYTES (0x20000) /* Again, half don't really exist */ - -static inline bus_addr_t sram_addr(const struct lanai_dev *lanai, int offset) -{ - return lanai->base + SRAM_START + offset; -} - -static inline u32 sram_read(const struct lanai_dev *lanai, int offset) -{ - return readl(sram_addr(lanai, offset)); -} - -static inline void sram_write(const struct lanai_dev *lanai, - u32 val, int offset) -{ - writel(val, sram_addr(lanai, offset)); -} - -static int sram_test_word(const struct lanai_dev *lanai, int offset, - u32 pattern) -{ - u32 readback; - sram_write(lanai, pattern, offset); - readback = sram_read(lanai, offset); - if (likely(readback == pattern)) - return 0; - printk(KERN_ERR DEV_LABEL - "(itf %d): SRAM word at %d bad: wrote 0x%X, read 0x%X\n", - lanai->number, offset, - (unsigned int) pattern, (unsigned int) readback); - return -EIO; -} - -static int sram_test_pass(const struct lanai_dev *lanai, u32 pattern) -{ - int offset, result = 0; - for (offset = 0; offset < SRAM_BYTES && result == 0; offset += 4) - result = sram_test_word(lanai, offset, pattern); - return result; -} - -static int sram_test_and_clear(const struct lanai_dev *lanai) -{ -#ifdef FULL_MEMORY_TEST - int result; - DPRINTK("testing SRAM\n"); - if ((result = sram_test_pass(lanai, 0x5555)) != 0) - return result; - if ((result = sram_test_pass(lanai, 0xAAAA)) != 0) - return result; -#endif - DPRINTK("clearing SRAM\n"); - return sram_test_pass(lanai, 0x0000); -} - -/* -------------------- CARD-BASED VCC TABLE UTILITIES: */ - -/* vcc table */ -enum lanai_vcc_offset { - vcc_rxaddr1 = 0x00, /* Location1, plus bits: */ -#define RXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of RX buffer */ -#define RXADDR1_SET_RMMODE(x) ((x)*0x00800) /* RM cell action; values: */ -#define RMMODE_TRASH (0) /* discard */ -#define RMMODE_PRESERVE (1) /* input as AAL0 */ -#define RMMODE_PIPE (2) /* pipe to coscheduler */ -#define RMMODE_PIPEALL (3) /* pipe non-RM too */ -#define RXADDR1_OAM_PRESERVE (0x00002000) /* Input OAM cells as AAL0 */ -#define RXADDR1_SET_MODE(x) ((x)*0x0004000) /* Reassembly mode */ -#define RXMODE_TRASH (0) /* discard */ -#define RXMODE_AAL0 (1) /* non-AAL5 mode */ -#define RXMODE_AAL5 (2) /* AAL5, intr. each PDU */ -#define RXMODE_AAL5_STREAM (3) /* AAL5 w/o per-PDU intr */ - vcc_rxaddr2 = 0x04, /* Location2 */ - vcc_rxcrc1 = 0x08, /* RX CRC claculation space */ - vcc_rxcrc2 = 0x0C, - vcc_rxwriteptr = 0x10, /* RX writeptr, plus bits: */ -#define RXWRITEPTR_LASTEFCI (0x00002000) /* Last PDU had EFCI bit */ -#define RXWRITEPTR_DROPPING (0x00004000) /* Had error, dropping */ -#define RXWRITEPTR_TRASHING (0x00008000) /* Trashing */ - vcc_rxbufstart = 0x14, /* RX bufstart, plus bits: */ -#define RXBUFSTART_CLP (0x00004000) -#define RXBUFSTART_CI (0x00008000) - vcc_rxreadptr = 0x18, /* RX readptr */ - vcc_txicg = 0x1C, /* TX ICG */ - vcc_txaddr1 = 0x20, /* Location1, plus bits: */ -#define TXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of TX buffer */ -#define TXADDR1_ABR (0x00008000) /* use ABR (doesn't work) */ - vcc_txaddr2 = 0x24, /* Location2 */ - vcc_txcrc1 = 0x28, /* TX CRC claculation space */ - vcc_txcrc2 = 0x2C, - vcc_txreadptr = 0x30, /* TX Readptr, plus bits: */ -#define TXREADPTR_GET_PTR(x) ((x)&0x01FFF) -#define TXREADPTR_MASK_DELTA (0x0000E000) /* ? */ - vcc_txendptr = 0x34, /* TX Endptr, plus bits: */ -#define TXENDPTR_CLP (0x00002000) -#define TXENDPTR_MASK_PDUMODE (0x0000C000) /* PDU mode; values: */ -#define PDUMODE_AAL0 (0*0x04000) -#define PDUMODE_AAL5 (2*0x04000) -#define PDUMODE_AAL5STREAM (3*0x04000) - vcc_txwriteptr = 0x38, /* TX Writeptr */ -#define TXWRITEPTR_GET_PTR(x) ((x)&0x1FFF) - vcc_txcbr_next = 0x3C /* # of next CBR VCI in ring */ -#define TXCBR_NEXT_BOZO (0x00008000) /* "bozo bit" */ -}; - -#define CARDVCC_SIZE (0x40) - -static inline bus_addr_t cardvcc_addr(const struct lanai_dev *lanai, - vci_t vci) -{ - return sram_addr(lanai, vci * CARDVCC_SIZE); -} - -static inline u32 cardvcc_read(const struct lanai_vcc *lvcc, - enum lanai_vcc_offset offset) -{ - u32 val; - APRINTK(lvcc->vbase != NULL, "cardvcc_read: unbound vcc!\n"); - val= readl(lvcc->vbase + offset); - RWDEBUG("VR vci=%04d 0x%02X = 0x%08X\n", - lvcc->vci, (int) offset, val); - return val; -} - -static inline void cardvcc_write(const struct lanai_vcc *lvcc, - u32 val, enum lanai_vcc_offset offset) -{ - APRINTK(lvcc->vbase != NULL, "cardvcc_write: unbound vcc!\n"); - APRINTK((val & ~0xFFFF) == 0, - "cardvcc_write: bad val 0x%X (vci=%d, addr=0x%02X)\n", - (unsigned int) val, lvcc->vci, (unsigned int) offset); - RWDEBUG("VW vci=%04d 0x%02X > 0x%08X\n", - lvcc->vci, (unsigned int) offset, (unsigned int) val); - writel(val, lvcc->vbase + offset); -} - -/* -------------------- COMPUTE SIZE OF AN AAL5 PDU: */ - -/* How many bytes will an AAL5 PDU take to transmit - remember that: - * o we need to add 8 bytes for length, CPI, UU, and CRC - * o we need to round up to 48 bytes for cells - */ -static inline int aal5_size(int size) -{ - int cells = (size + 8 + 47) / 48; - return cells * 48; -} - -/* -------------------- FREE AN ATM SKB: */ - -static inline void lanai_free_skb(struct atm_vcc *atmvcc, struct sk_buff *skb) -{ - if (atmvcc->pop != NULL) - atmvcc->pop(atmvcc, skb); - else - dev_kfree_skb_any(skb); -} - -/* -------------------- TURN VCCS ON AND OFF: */ - -static void host_vcc_start_rx(const struct lanai_vcc *lvcc) -{ - u32 addr1; - if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) { - dma_addr_t dmaaddr = lvcc->rx.buf.dmaaddr; - cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc1); - cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc2); - cardvcc_write(lvcc, 0, vcc_rxwriteptr); - cardvcc_write(lvcc, 0, vcc_rxbufstart); - cardvcc_write(lvcc, 0, vcc_rxreadptr); - cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_rxaddr2); - addr1 = ((dmaaddr >> 8) & 0xFF) | - RXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->rx.buf))| - RXADDR1_SET_RMMODE(RMMODE_TRASH) | /* ??? */ - /* RXADDR1_OAM_PRESERVE | --- no OAM support yet */ - RXADDR1_SET_MODE(RXMODE_AAL5); - } else - addr1 = RXADDR1_SET_RMMODE(RMMODE_PRESERVE) | /* ??? */ - RXADDR1_OAM_PRESERVE | /* ??? */ - RXADDR1_SET_MODE(RXMODE_AAL0); - /* This one must be last! */ - cardvcc_write(lvcc, addr1, vcc_rxaddr1); -} - -static void host_vcc_start_tx(const struct lanai_vcc *lvcc) -{ - dma_addr_t dmaaddr = lvcc->tx.buf.dmaaddr; - cardvcc_write(lvcc, 0, vcc_txicg); - cardvcc_write(lvcc, 0xFFFF, vcc_txcrc1); - cardvcc_write(lvcc, 0xFFFF, vcc_txcrc2); - cardvcc_write(lvcc, 0, vcc_txreadptr); - cardvcc_write(lvcc, 0, vcc_txendptr); - cardvcc_write(lvcc, 0, vcc_txwriteptr); - cardvcc_write(lvcc, - (lvcc->tx.atmvcc->qos.txtp.traffic_class == ATM_CBR) ? - TXCBR_NEXT_BOZO | lvcc->vci : 0, vcc_txcbr_next); - cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_txaddr2); - cardvcc_write(lvcc, - ((dmaaddr >> 8) & 0xFF) | - TXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->tx.buf)), - vcc_txaddr1); -} - -/* Shutdown receiving on card */ -static void lanai_shutdown_rx_vci(const struct lanai_vcc *lvcc) -{ - if (lvcc->vbase == NULL) /* We were never bound to a VCI */ - return; - /* 15.1.1 - set to trashing, wait one cell time (15us) */ - cardvcc_write(lvcc, - RXADDR1_SET_RMMODE(RMMODE_TRASH) | - RXADDR1_SET_MODE(RXMODE_TRASH), vcc_rxaddr1); - udelay(15); - /* 15.1.2 - clear rest of entries */ - cardvcc_write(lvcc, 0, vcc_rxaddr2); - cardvcc_write(lvcc, 0, vcc_rxcrc1); - cardvcc_write(lvcc, 0, vcc_rxcrc2); - cardvcc_write(lvcc, 0, vcc_rxwriteptr); - cardvcc_write(lvcc, 0, vcc_rxbufstart); - cardvcc_write(lvcc, 0, vcc_rxreadptr); -} - -/* Shutdown transmitting on card. - * Unfortunately the lanai needs us to wait until all the data - * drains out of the buffer before we can dealloc it, so this - * can take a while -- up to 370ms for a full 128KB buffer - * assuming everone else is quiet. In theory the time is - * boundless if there's a CBR VCC holding things up. - */ -static void lanai_shutdown_tx_vci(struct lanai_dev *lanai, - struct lanai_vcc *lvcc) -{ - struct sk_buff *skb; - unsigned long flags, timeout; - int read, write, lastread = -1; - - if (lvcc->vbase == NULL) /* We were never bound to a VCI */ - return; - /* 15.2.1 - wait for queue to drain */ - while ((skb = skb_dequeue(&lvcc->tx.backlog)) != NULL) - lanai_free_skb(lvcc->tx.atmvcc, skb); - read_lock_irqsave(&vcc_sklist_lock, flags); - __clear_bit(lvcc->vci, lanai->backlog_vccs); - read_unlock_irqrestore(&vcc_sklist_lock, flags); - /* - * We need to wait for the VCC to drain but don't wait forever. We - * give each 1K of buffer size 1/128th of a second to clear out. - * TODO: maybe disable CBR if we're about to timeout? - */ - timeout = jiffies + - (((lanai_buf_size(&lvcc->tx.buf) / 1024) * HZ) >> 7); - write = TXWRITEPTR_GET_PTR(cardvcc_read(lvcc, vcc_txwriteptr)); - for (;;) { - read = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)); - if (read == write && /* Is TX buffer empty? */ - (lvcc->tx.atmvcc->qos.txtp.traffic_class != ATM_CBR || - (cardvcc_read(lvcc, vcc_txcbr_next) & - TXCBR_NEXT_BOZO) == 0)) - break; - if (read != lastread) { /* Has there been any progress? */ - lastread = read; - timeout += HZ / 10; - } - if (unlikely(time_after(jiffies, timeout))) { - printk(KERN_ERR DEV_LABEL "(itf %d): Timed out on " - "backlog closing vci %d\n", - lvcc->tx.atmvcc->dev->number, lvcc->vci); - DPRINTK("read, write = %d, %d\n", read, write); - break; - } - msleep(40); - } - /* 15.2.2 - clear out all tx registers */ - cardvcc_write(lvcc, 0, vcc_txreadptr); - cardvcc_write(lvcc, 0, vcc_txwriteptr); - cardvcc_write(lvcc, 0, vcc_txendptr); - cardvcc_write(lvcc, 0, vcc_txcrc1); - cardvcc_write(lvcc, 0, vcc_txcrc2); - cardvcc_write(lvcc, 0, vcc_txaddr2); - cardvcc_write(lvcc, 0, vcc_txaddr1); -} - -/* -------------------- MANAGING AAL0 RX BUFFER: */ - -static inline int aal0_buffer_allocate(struct lanai_dev *lanai) -{ - DPRINTK("aal0_buffer_allocate: allocating AAL0 RX buffer\n"); - lanai_buf_allocate(&lanai->aal0buf, AAL0_RX_BUFFER_SIZE, 80, - lanai->pci); - return (lanai->aal0buf.start == NULL) ? -ENOMEM : 0; -} - -static inline void aal0_buffer_free(struct lanai_dev *lanai) -{ - DPRINTK("aal0_buffer_allocate: freeing AAL0 RX buffer\n"); - lanai_buf_deallocate(&lanai->aal0buf, lanai->pci); -} - -/* -------------------- EEPROM UTILITIES: */ - -/* Offsets of data in the EEPROM */ -#define EEPROM_COPYRIGHT (0) -#define EEPROM_COPYRIGHT_LEN (44) -#define EEPROM_CHECKSUM (62) -#define EEPROM_CHECKSUM_REV (63) -#define EEPROM_MAC (64) -#define EEPROM_MAC_REV (70) -#define EEPROM_SERIAL (112) -#define EEPROM_SERIAL_REV (116) -#define EEPROM_MAGIC (120) -#define EEPROM_MAGIC_REV (124) - -#define EEPROM_MAGIC_VALUE (0x5AB478D2) - -#ifndef READ_EEPROM - -/* Stub functions to use if EEPROM reading is disabled */ -static int eeprom_read(struct lanai_dev *lanai) -{ - printk(KERN_INFO DEV_LABEL "(itf %d): *NOT* reading EEPROM\n", - lanai->number); - memset(&lanai->eeprom[EEPROM_MAC], 0, 6); - return 0; -} - -static int eeprom_validate(struct lanai_dev *lanai) -{ - lanai->serialno = 0; - lanai->magicno = EEPROM_MAGIC_VALUE; - return 0; -} - -#else /* READ_EEPROM */ - -static int eeprom_read(struct lanai_dev *lanai) -{ - int i, address; - u8 data; - u32 tmp; -#define set_config1(x) do { lanai->conf1 = x; conf1_write(lanai); \ - } while (0) -#define clock_h() set_config1(lanai->conf1 | CONFIG1_PROMCLK) -#define clock_l() set_config1(lanai->conf1 &~ CONFIG1_PROMCLK) -#define data_h() set_config1(lanai->conf1 | CONFIG1_PROMDATA) -#define data_l() set_config1(lanai->conf1 &~ CONFIG1_PROMDATA) -#define pre_read() do { data_h(); clock_h(); udelay(5); } while (0) -#define read_pin() (reg_read(lanai, Status_Reg) & STATUS_PROMDATA) -#define send_stop() do { data_l(); udelay(5); clock_h(); udelay(5); \ - data_h(); udelay(5); } while (0) - /* start with both clock and data high */ - data_h(); clock_h(); udelay(5); - for (address = 0; address < LANAI_EEPROM_SIZE; address++) { - data = (address << 1) | 1; /* Command=read + address */ - /* send start bit */ - data_l(); udelay(5); - clock_l(); udelay(5); - for (i = 128; i != 0; i >>= 1) { /* write command out */ - tmp = (lanai->conf1 & ~CONFIG1_PROMDATA) | - ((data & i) ? CONFIG1_PROMDATA : 0); - if (lanai->conf1 != tmp) { - set_config1(tmp); - udelay(5); /* Let new data settle */ - } - clock_h(); udelay(5); clock_l(); udelay(5); - } - /* look for ack */ - data_h(); clock_h(); udelay(5); - if (read_pin() != 0) - goto error; /* No ack seen */ - clock_l(); udelay(5); - /* read back result */ - for (data = 0, i = 7; i >= 0; i--) { - data_h(); clock_h(); udelay(5); - data = (data << 1) | !!read_pin(); - clock_l(); udelay(5); - } - /* look again for ack */ - data_h(); clock_h(); udelay(5); - if (read_pin() == 0) - goto error; /* Spurious ack */ - clock_l(); udelay(5); - send_stop(); - lanai->eeprom[address] = data; - DPRINTK("EEPROM 0x%04X %02X\n", - (unsigned int) address, (unsigned int) data); - } - return 0; - error: - clock_l(); udelay(5); /* finish read */ - send_stop(); - printk(KERN_ERR DEV_LABEL "(itf %d): error reading EEPROM byte %d\n", - lanai->number, address); - return -EIO; -#undef set_config1 -#undef clock_h -#undef clock_l -#undef data_h -#undef data_l -#undef pre_read -#undef read_pin -#undef send_stop -} - -/* read a big-endian 4-byte value out of eeprom */ -static inline u32 eeprom_be4(const struct lanai_dev *lanai, int address) -{ - return be32_to_cpup((const u32 *) &lanai->eeprom[address]); -} - -/* Checksum/validate EEPROM contents */ -static int eeprom_validate(struct lanai_dev *lanai) -{ - int i, s; - u32 v; - const u8 *e = lanai->eeprom; -#ifdef DEBUG - /* First, see if we can get an ASCIIZ string out of the copyright */ - for (i = EEPROM_COPYRIGHT; - i < (EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN); i++) - if (e[i] < 0x20 || e[i] > 0x7E) - break; - if ( i != EEPROM_COPYRIGHT && - i != EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN && e[i] == '\0') - DPRINTK("eeprom: copyright = \"%s\"\n", - (char *) &e[EEPROM_COPYRIGHT]); - else - DPRINTK("eeprom: copyright not found\n"); -#endif - /* Validate checksum */ - for (i = s = 0; i < EEPROM_CHECKSUM; i++) - s += e[i]; - s &= 0xFF; - if (s != e[EEPROM_CHECKSUM]) { - printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM checksum bad " - "(wanted 0x%02X, got 0x%02X)\n", lanai->number, - (unsigned int) s, (unsigned int) e[EEPROM_CHECKSUM]); - return -EIO; - } - s ^= 0xFF; - if (s != e[EEPROM_CHECKSUM_REV]) { - printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM inverse checksum " - "bad (wanted 0x%02X, got 0x%02X)\n", lanai->number, - (unsigned int) s, (unsigned int) e[EEPROM_CHECKSUM_REV]); - return -EIO; - } - /* Verify MAC address */ - for (i = 0; i < 6; i++) - if ((e[EEPROM_MAC + i] ^ e[EEPROM_MAC_REV + i]) != 0xFF) { - printk(KERN_ERR DEV_LABEL - "(itf %d) : EEPROM MAC addresses don't match " - "(0x%02X, inverse 0x%02X)\n", lanai->number, - (unsigned int) e[EEPROM_MAC + i], - (unsigned int) e[EEPROM_MAC_REV + i]); - return -EIO; - } - DPRINTK("eeprom: MAC address = %pM\n", &e[EEPROM_MAC]); - /* Verify serial number */ - lanai->serialno = eeprom_be4(lanai, EEPROM_SERIAL); - v = eeprom_be4(lanai, EEPROM_SERIAL_REV); - if ((lanai->serialno ^ v) != 0xFFFFFFFF) { - printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM serial numbers " - "don't match (0x%08X, inverse 0x%08X)\n", lanai->number, - (unsigned int) lanai->serialno, (unsigned int) v); - return -EIO; - } - DPRINTK("eeprom: Serial number = %d\n", (unsigned int) lanai->serialno); - /* Verify magic number */ - lanai->magicno = eeprom_be4(lanai, EEPROM_MAGIC); - v = eeprom_be4(lanai, EEPROM_MAGIC_REV); - if ((lanai->magicno ^ v) != 0xFFFFFFFF) { - printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM magic numbers " - "don't match (0x%08X, inverse 0x%08X)\n", lanai->number, - lanai->magicno, v); - return -EIO; - } - DPRINTK("eeprom: Magic number = 0x%08X\n", lanai->magicno); - if (lanai->magicno != EEPROM_MAGIC_VALUE) - printk(KERN_WARNING DEV_LABEL "(itf %d): warning - EEPROM " - "magic not what expected (got 0x%08X, not 0x%08X)\n", - lanai->number, (unsigned int) lanai->magicno, - (unsigned int) EEPROM_MAGIC_VALUE); - return 0; -} - -#endif /* READ_EEPROM */ - -static inline const u8 *eeprom_mac(const struct lanai_dev *lanai) -{ - return &lanai->eeprom[EEPROM_MAC]; -} - -/* -------------------- INTERRUPT HANDLING UTILITIES: */ - -/* Interrupt types */ -#define INT_STATS (0x00000002) /* Statistics counter overflow */ -#define INT_SOOL (0x00000004) /* SOOL changed state */ -#define INT_LOCD (0x00000008) /* LOCD changed state */ -#define INT_LED (0x00000010) /* LED (HAPPI) changed state */ -#define INT_GPIN (0x00000020) /* GPIN changed state */ -#define INT_PING (0x00000040) /* PING_COUNT fulfilled */ -#define INT_WAKE (0x00000080) /* Lanai wants bus */ -#define INT_CBR0 (0x00000100) /* CBR sched hit VCI 0 */ -#define INT_LOCK (0x00000200) /* Service list overflow */ -#define INT_MISMATCH (0x00000400) /* TX magic list mismatch */ -#define INT_AAL0_STR (0x00000800) /* Non-AAL5 buffer half filled */ -#define INT_AAL0 (0x00001000) /* Non-AAL5 data available */ -#define INT_SERVICE (0x00002000) /* Service list entries available */ -#define INT_TABORTSENT (0x00004000) /* Target abort sent by lanai */ -#define INT_TABORTBM (0x00008000) /* Abort rcv'd as bus master */ -#define INT_TIMEOUTBM (0x00010000) /* No response to bus master */ -#define INT_PCIPARITY (0x00020000) /* Parity error on PCI */ - -/* Sets of the above */ -#define INT_ALL (0x0003FFFE) /* All interrupts */ -#define INT_STATUS (0x0000003C) /* Some status pin changed */ -#define INT_DMASHUT (0x00038000) /* DMA engine got shut down */ -#define INT_SEGSHUT (0x00000700) /* Segmentation got shut down */ - -static inline u32 intr_pending(const struct lanai_dev *lanai) -{ - return reg_read(lanai, IntStatusMasked_Reg); -} - -static inline void intr_enable(const struct lanai_dev *lanai, u32 i) -{ - reg_write(lanai, i, IntControlEna_Reg); -} - -static inline void intr_disable(const struct lanai_dev *lanai, u32 i) -{ - reg_write(lanai, i, IntControlDis_Reg); -} - -/* -------------------- CARD/PCI STATUS: */ - -static void status_message(int itf, const char *name, int status) -{ - static const char *onoff[2] = { "off to on", "on to off" }; - printk(KERN_INFO DEV_LABEL "(itf %d): %s changed from %s\n", - itf, name, onoff[!status]); -} - -static void lanai_check_status(struct lanai_dev *lanai) -{ - u32 new = reg_read(lanai, Status_Reg); - u32 changes = new ^ lanai->status; - lanai->status = new; -#define e(flag, name) \ - if (changes & flag) \ - status_message(lanai->number, name, new & flag) - e(STATUS_SOOL, "SOOL"); - e(STATUS_LOCD, "LOCD"); - e(STATUS_LED, "LED"); - e(STATUS_GPIN, "GPIN"); -#undef e -} - -static void pcistatus_got(int itf, const char *name) -{ - printk(KERN_INFO DEV_LABEL "(itf %d): PCI got %s error\n", itf, name); -} - -static void pcistatus_check(struct lanai_dev *lanai, int clearonly) -{ - u16 s; - int result; - result = pci_read_config_word(lanai->pci, PCI_STATUS, &s); - if (result != PCIBIOS_SUCCESSFUL) { - printk(KERN_ERR DEV_LABEL "(itf %d): can't read PCI_STATUS: " - "%d\n", lanai->number, result); - return; - } - s &= PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR | - PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT | - PCI_STATUS_SIG_TARGET_ABORT | PCI_STATUS_PARITY; - if (s == 0) - return; - result = pci_write_config_word(lanai->pci, PCI_STATUS, s); - if (result != PCIBIOS_SUCCESSFUL) - printk(KERN_ERR DEV_LABEL "(itf %d): can't write PCI_STATUS: " - "%d\n", lanai->number, result); - if (clearonly) - return; -#define e(flag, name, stat) \ - if (s & flag) { \ - pcistatus_got(lanai->number, name); \ - ++lanai->stats.pcierr_##stat; \ - } - e(PCI_STATUS_DETECTED_PARITY, "parity", parity_detect); - e(PCI_STATUS_SIG_SYSTEM_ERROR, "signalled system", serr_set); - e(PCI_STATUS_REC_MASTER_ABORT, "master", master_abort); - e(PCI_STATUS_REC_TARGET_ABORT, "master target", m_target_abort); - e(PCI_STATUS_SIG_TARGET_ABORT, "slave", s_target_abort); - e(PCI_STATUS_PARITY, "master parity", master_parity); -#undef e -} - -/* -------------------- VCC TX BUFFER UTILITIES: */ - -/* space left in tx buffer in bytes */ -static inline int vcc_tx_space(const struct lanai_vcc *lvcc, int endptr) -{ - int r; - r = endptr * 16; - r -= ((unsigned long) lvcc->tx.buf.ptr) - - ((unsigned long) lvcc->tx.buf.start); - r -= 16; /* Leave "bubble" - if start==end it looks empty */ - if (r < 0) - r += lanai_buf_size(&lvcc->tx.buf); - return r; -} - -/* test if VCC is currently backlogged */ -static inline int vcc_is_backlogged(const struct lanai_vcc *lvcc) -{ - return !skb_queue_empty(&lvcc->tx.backlog); -} - -/* Bit fields in the segmentation buffer descriptor */ -#define DESCRIPTOR_MAGIC (0xD0000000) -#define DESCRIPTOR_AAL5 (0x00008000) -#define DESCRIPTOR_AAL5_STREAM (0x00004000) -#define DESCRIPTOR_CLP (0x00002000) - -/* Add 32-bit descriptor with its padding */ -static inline void vcc_tx_add_aal5_descriptor(struct lanai_vcc *lvcc, - u32 flags, int len) -{ - int pos; - APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 0, - "vcc_tx_add_aal5_descriptor: bad ptr=%p\n", lvcc->tx.buf.ptr); - lvcc->tx.buf.ptr += 4; /* Hope the values REALLY don't matter */ - pos = ((unsigned char *) lvcc->tx.buf.ptr) - - (unsigned char *) lvcc->tx.buf.start; - APRINTK((pos & ~0x0001FFF0) == 0, - "vcc_tx_add_aal5_descriptor: bad pos (%d) before, vci=%d, " - "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci, - lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); - pos = (pos + len) & (lanai_buf_size(&lvcc->tx.buf) - 1); - APRINTK((pos & ~0x0001FFF0) == 0, - "vcc_tx_add_aal5_descriptor: bad pos (%d) after, vci=%d, " - "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci, - lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); - lvcc->tx.buf.ptr[-1] = - cpu_to_le32(DESCRIPTOR_MAGIC | DESCRIPTOR_AAL5 | - ((lvcc->tx.atmvcc->atm_options & ATM_ATMOPT_CLP) ? - DESCRIPTOR_CLP : 0) | flags | pos >> 4); - if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end) - lvcc->tx.buf.ptr = lvcc->tx.buf.start; -} - -/* Add 32-bit AAL5 trailer and leave room for its CRC */ -static inline void vcc_tx_add_aal5_trailer(struct lanai_vcc *lvcc, - int len, int cpi, int uu) -{ - APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 8, - "vcc_tx_add_aal5_trailer: bad ptr=%p\n", lvcc->tx.buf.ptr); - lvcc->tx.buf.ptr += 2; - lvcc->tx.buf.ptr[-2] = cpu_to_be32((uu << 24) | (cpi << 16) | len); - if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end) - lvcc->tx.buf.ptr = lvcc->tx.buf.start; -} - -static inline void vcc_tx_memcpy(struct lanai_vcc *lvcc, - const unsigned char *src, int n) -{ - unsigned char *e; - int m; - e = ((unsigned char *) lvcc->tx.buf.ptr) + n; - m = e - (unsigned char *) lvcc->tx.buf.end; - if (m < 0) - m = 0; - memcpy(lvcc->tx.buf.ptr, src, n - m); - if (m != 0) { - memcpy(lvcc->tx.buf.start, src + n - m, m); - e = ((unsigned char *) lvcc->tx.buf.start) + m; - } - lvcc->tx.buf.ptr = (u32 *) e; -} - -static inline void vcc_tx_memzero(struct lanai_vcc *lvcc, int n) -{ - unsigned char *e; - int m; - if (n == 0) - return; - e = ((unsigned char *) lvcc->tx.buf.ptr) + n; - m = e - (unsigned char *) lvcc->tx.buf.end; - if (m < 0) - m = 0; - memset(lvcc->tx.buf.ptr, 0, n - m); - if (m != 0) { - memset(lvcc->tx.buf.start, 0, m); - e = ((unsigned char *) lvcc->tx.buf.start) + m; - } - lvcc->tx.buf.ptr = (u32 *) e; -} - -/* Update "butt" register to specify new WritePtr */ -static inline void lanai_endtx(struct lanai_dev *lanai, - const struct lanai_vcc *lvcc) -{ - int i, ptr = ((unsigned char *) lvcc->tx.buf.ptr) - - (unsigned char *) lvcc->tx.buf.start; - APRINTK((ptr & ~0x0001FFF0) == 0, - "lanai_endtx: bad ptr (%d), vci=%d, start,ptr,end=%p,%p,%p\n", - ptr, lvcc->vci, lvcc->tx.buf.start, lvcc->tx.buf.ptr, - lvcc->tx.buf.end); - - /* - * Since the "butt register" is a shared resounce on the card we - * serialize all accesses to it through this spinlock. This is - * mostly just paranoia since the register is rarely "busy" anyway - * but is needed for correctness. - */ - spin_lock(&lanai->endtxlock); - /* - * We need to check if the "butt busy" bit is set before - * updating the butt register. In theory this should - * never happen because the ATM card is plenty fast at - * updating the register. Still, we should make sure - */ - for (i = 0; reg_read(lanai, Status_Reg) & STATUS_BUTTBUSY; i++) { - if (unlikely(i > 50)) { - printk(KERN_ERR DEV_LABEL "(itf %d): butt register " - "always busy!\n", lanai->number); - break; - } - udelay(5); - } - /* - * Before we tall the card to start work we need to be sure 100% of - * the info in the service buffer has been written before we tell - * the card about it - */ - wmb(); - reg_write(lanai, (ptr << 12) | lvcc->vci, Butt_Reg); - spin_unlock(&lanai->endtxlock); -} - -/* - * Add one AAL5 PDU to lvcc's transmit buffer. Caller garauntees there's - * space available. "pdusize" is the number of bytes the PDU will take - */ -static void lanai_send_one_aal5(struct lanai_dev *lanai, - struct lanai_vcc *lvcc, struct sk_buff *skb, int pdusize) -{ - int pad; - APRINTK(pdusize == aal5_size(skb->len), - "lanai_send_one_aal5: wrong size packet (%d != %d)\n", - pdusize, aal5_size(skb->len)); - vcc_tx_add_aal5_descriptor(lvcc, 0, pdusize); - pad = pdusize - skb->len - 8; - APRINTK(pad >= 0, "pad is negative (%d)\n", pad); - APRINTK(pad < 48, "pad is too big (%d)\n", pad); - vcc_tx_memcpy(lvcc, skb->data, skb->len); - vcc_tx_memzero(lvcc, pad); - vcc_tx_add_aal5_trailer(lvcc, skb->len, 0, 0); - lanai_endtx(lanai, lvcc); - lanai_free_skb(lvcc->tx.atmvcc, skb); - atomic_inc(&lvcc->tx.atmvcc->stats->tx); -} - -/* Try to fill the buffer - don't call unless there is backlog */ -static void vcc_tx_unqueue_aal5(struct lanai_dev *lanai, - struct lanai_vcc *lvcc, int endptr) -{ - int n; - struct sk_buff *skb; - int space = vcc_tx_space(lvcc, endptr); - APRINTK(vcc_is_backlogged(lvcc), - "vcc_tx_unqueue() called with empty backlog (vci=%d)\n", - lvcc->vci); - while (space >= 64) { - skb = skb_dequeue(&lvcc->tx.backlog); - if (skb == NULL) - goto no_backlog; - n = aal5_size(skb->len); - if (n + 16 > space) { - /* No room for this packet - put it back on queue */ - skb_queue_head(&lvcc->tx.backlog, skb); - return; - } - lanai_send_one_aal5(lanai, lvcc, skb, n); - space -= n + 16; - } - if (!vcc_is_backlogged(lvcc)) { - no_backlog: - __clear_bit(lvcc->vci, lanai->backlog_vccs); - } -} - -/* Given an skb that we want to transmit either send it now or queue */ -static void vcc_tx_aal5(struct lanai_dev *lanai, struct lanai_vcc *lvcc, - struct sk_buff *skb) -{ - int space, n; - if (vcc_is_backlogged(lvcc)) /* Already backlogged */ - goto queue_it; - space = vcc_tx_space(lvcc, - TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr))); - n = aal5_size(skb->len); - APRINTK(n + 16 >= 64, "vcc_tx_aal5: n too small (%d)\n", n); - if (space < n + 16) { /* No space for this PDU */ - __set_bit(lvcc->vci, lanai->backlog_vccs); - queue_it: - skb_queue_tail(&lvcc->tx.backlog, skb); - return; - } - lanai_send_one_aal5(lanai, lvcc, skb, n); -} - -static void vcc_tx_unqueue_aal0(struct lanai_dev *lanai, - struct lanai_vcc *lvcc, int endptr) -{ - printk(KERN_INFO DEV_LABEL - ": vcc_tx_unqueue_aal0: not implemented\n"); -} - -static void vcc_tx_aal0(struct lanai_dev *lanai, struct lanai_vcc *lvcc, - struct sk_buff *skb) -{ - printk(KERN_INFO DEV_LABEL ": vcc_tx_aal0: not implemented\n"); - /* Remember to increment lvcc->tx.atmvcc->stats->tx */ - lanai_free_skb(lvcc->tx.atmvcc, skb); -} - -/* -------------------- VCC RX BUFFER UTILITIES: */ - -/* unlike the _tx_ cousins, this doesn't update ptr */ -static inline void vcc_rx_memcpy(unsigned char *dest, - const struct lanai_vcc *lvcc, int n) -{ - int m = ((const unsigned char *) lvcc->rx.buf.ptr) + n - - ((const unsigned char *) (lvcc->rx.buf.end)); - if (m < 0) - m = 0; - memcpy(dest, lvcc->rx.buf.ptr, n - m); - memcpy(dest + n - m, lvcc->rx.buf.start, m); - /* Make sure that these copies don't get reordered */ - barrier(); -} - -/* Receive AAL5 data on a VCC with a particular endptr */ -static void vcc_rx_aal5(struct lanai_vcc *lvcc, int endptr) -{ - int size; - struct sk_buff *skb; - const u32 *x; - u32 *end = &lvcc->rx.buf.start[endptr * 4]; - int n = ((unsigned long) end) - ((unsigned long) lvcc->rx.buf.ptr); - if (n < 0) - n += lanai_buf_size(&lvcc->rx.buf); - APRINTK(n >= 0 && n < lanai_buf_size(&lvcc->rx.buf) && !(n & 15), - "vcc_rx_aal5: n out of range (%d/%zu)\n", - n, lanai_buf_size(&lvcc->rx.buf)); - /* Recover the second-to-last word to get true pdu length */ - if ((x = &end[-2]) < lvcc->rx.buf.start) - x = &lvcc->rx.buf.end[-2]; - /* - * Before we actually read from the buffer, make sure the memory - * changes have arrived - */ - rmb(); - size = be32_to_cpup(x) & 0xffff; - if (unlikely(n != aal5_size(size))) { - /* Make sure size matches padding */ - printk(KERN_INFO DEV_LABEL "(itf %d): Got bad AAL5 length " - "on vci=%d - size=%d n=%d\n", - lvcc->rx.atmvcc->dev->number, lvcc->vci, size, n); - lvcc->stats.x.aal5.rx_badlen++; - goto out; - } - skb = atm_alloc_charge(lvcc->rx.atmvcc, size, GFP_ATOMIC); - if (unlikely(skb == NULL)) { - lvcc->stats.rx_nomem++; - goto out; - } - skb_put(skb, size); - vcc_rx_memcpy(skb->data, lvcc, size); - ATM_SKB(skb)->vcc = lvcc->rx.atmvcc; - __net_timestamp(skb); - lvcc->rx.atmvcc->push(lvcc->rx.atmvcc, skb); - atomic_inc(&lvcc->rx.atmvcc->stats->rx); - out: - lvcc->rx.buf.ptr = end; - cardvcc_write(lvcc, endptr, vcc_rxreadptr); -} - -static void vcc_rx_aal0(struct lanai_dev *lanai) -{ - printk(KERN_INFO DEV_LABEL ": vcc_rx_aal0: not implemented\n"); - /* Remember to get read_lock(&vcc_sklist_lock) while looking up VC */ - /* Remember to increment lvcc->rx.atmvcc->stats->rx */ -} - -/* -------------------- MANAGING HOST-BASED VCC TABLE: */ - -/* Decide whether to use vmalloc or get_zeroed_page for VCC table */ -#if (NUM_VCI * BITS_PER_LONG) <= PAGE_SIZE -#define VCCTABLE_GETFREEPAGE -#else -#include -#endif - -static int vcc_table_allocate(struct lanai_dev *lanai) -{ -#ifdef VCCTABLE_GETFREEPAGE - APRINTK((lanai->num_vci) * sizeof(struct lanai_vcc *) <= PAGE_SIZE, - "vcc table > PAGE_SIZE!"); - lanai->vccs = (struct lanai_vcc **) get_zeroed_page(GFP_KERNEL); - return (lanai->vccs == NULL) ? -ENOMEM : 0; -#else - int bytes = (lanai->num_vci) * sizeof(struct lanai_vcc *); - lanai->vccs = vzalloc(bytes); - if (unlikely(lanai->vccs == NULL)) - return -ENOMEM; - return 0; -#endif -} - -static inline void vcc_table_deallocate(const struct lanai_dev *lanai) -{ -#ifdef VCCTABLE_GETFREEPAGE - free_page((unsigned long) lanai->vccs); -#else - vfree(lanai->vccs); -#endif -} - -/* Allocate a fresh lanai_vcc, with the appropriate things cleared */ -static inline struct lanai_vcc *new_lanai_vcc(void) -{ - struct lanai_vcc *lvcc; - lvcc = kzalloc_obj(*lvcc); - if (likely(lvcc != NULL)) { - skb_queue_head_init(&lvcc->tx.backlog); -#ifdef DEBUG - lvcc->vci = -1; -#endif - } - return lvcc; -} - -static int lanai_get_sized_buffer(struct lanai_dev *lanai, - struct lanai_buffer *buf, int max_sdu, int multiplier, - const char *name) -{ - int size; - if (unlikely(max_sdu < 1)) - max_sdu = 1; - max_sdu = aal5_size(max_sdu); - size = (max_sdu + 16) * multiplier + 16; - lanai_buf_allocate(buf, size, max_sdu + 32, lanai->pci); - if (unlikely(buf->start == NULL)) - return -ENOMEM; - if (unlikely(lanai_buf_size(buf) < size)) - printk(KERN_WARNING DEV_LABEL "(itf %d): wanted %d bytes " - "for %s buffer, got only %zu\n", lanai->number, size, - name, lanai_buf_size(buf)); - DPRINTK("Allocated %zu byte %s buffer\n", lanai_buf_size(buf), name); - return 0; -} - -/* Setup a RX buffer for a currently unbound AAL5 vci */ -static inline int lanai_setup_rx_vci_aal5(struct lanai_dev *lanai, - struct lanai_vcc *lvcc, const struct atm_qos *qos) -{ - return lanai_get_sized_buffer(lanai, &lvcc->rx.buf, - qos->rxtp.max_sdu, AAL5_RX_MULTIPLIER, "RX"); -} - -/* Setup a TX buffer for a currently unbound AAL5 vci */ -static int lanai_setup_tx_vci(struct lanai_dev *lanai, struct lanai_vcc *lvcc, - const struct atm_qos *qos) -{ - int max_sdu, multiplier; - if (qos->aal == ATM_AAL0) { - lvcc->tx.unqueue = vcc_tx_unqueue_aal0; - max_sdu = ATM_CELL_SIZE - 1; - multiplier = AAL0_TX_MULTIPLIER; - } else { - lvcc->tx.unqueue = vcc_tx_unqueue_aal5; - max_sdu = qos->txtp.max_sdu; - multiplier = AAL5_TX_MULTIPLIER; - } - return lanai_get_sized_buffer(lanai, &lvcc->tx.buf, max_sdu, - multiplier, "TX"); -} - -static inline void host_vcc_bind(struct lanai_dev *lanai, - struct lanai_vcc *lvcc, vci_t vci) -{ - if (lvcc->vbase != NULL) - return; /* We already were bound in the other direction */ - DPRINTK("Binding vci %d\n", vci); -#ifdef USE_POWERDOWN - if (lanai->nbound++ == 0) { - DPRINTK("Coming out of powerdown\n"); - lanai->conf1 &= ~CONFIG1_POWERDOWN; - conf1_write(lanai); - conf2_write(lanai); - } -#endif - lvcc->vbase = cardvcc_addr(lanai, vci); - lanai->vccs[lvcc->vci = vci] = lvcc; -} - -static inline void host_vcc_unbind(struct lanai_dev *lanai, - struct lanai_vcc *lvcc) -{ - if (lvcc->vbase == NULL) - return; /* This vcc was never bound */ - DPRINTK("Unbinding vci %d\n", lvcc->vci); - lvcc->vbase = NULL; - lanai->vccs[lvcc->vci] = NULL; -#ifdef USE_POWERDOWN - if (--lanai->nbound == 0) { - DPRINTK("Going into powerdown\n"); - lanai->conf1 |= CONFIG1_POWERDOWN; - conf1_write(lanai); - } -#endif -} - -/* -------------------- RESET CARD: */ - -static void lanai_reset(struct lanai_dev *lanai) -{ - printk(KERN_CRIT DEV_LABEL "(itf %d): *NOT* resetting - not " - "implemented\n", lanai->number); - /* TODO */ - /* The following is just a hack until we write the real - * resetter - at least ack whatever interrupt sent us - * here - */ - reg_write(lanai, INT_ALL, IntAck_Reg); - lanai->stats.card_reset++; -} - -/* -------------------- SERVICE LIST UTILITIES: */ - -/* - * Allocate service buffer and tell card about it - */ -static int service_buffer_allocate(struct lanai_dev *lanai) -{ - lanai_buf_allocate(&lanai->service, SERVICE_ENTRIES * 4, 8, - lanai->pci); - if (unlikely(lanai->service.start == NULL)) - return -ENOMEM; - DPRINTK("allocated service buffer at %p, size %zu(%d)\n", - lanai->service.start, - lanai_buf_size(&lanai->service), - lanai_buf_size_cardorder(&lanai->service)); - /* Clear ServWrite register to be safe */ - reg_write(lanai, 0, ServWrite_Reg); - /* ServiceStuff register contains size and address of buffer */ - reg_write(lanai, - SSTUFF_SET_SIZE(lanai_buf_size_cardorder(&lanai->service)) | - SSTUFF_SET_ADDR(lanai->service.dmaaddr), - ServiceStuff_Reg); - return 0; -} - -static inline void service_buffer_deallocate(struct lanai_dev *lanai) -{ - lanai_buf_deallocate(&lanai->service, lanai->pci); -} - -/* Bitfields in service list */ -#define SERVICE_TX (0x80000000) /* Was from transmission */ -#define SERVICE_TRASH (0x40000000) /* RXed PDU was trashed */ -#define SERVICE_CRCERR (0x20000000) /* RXed PDU had CRC error */ -#define SERVICE_CI (0x10000000) /* RXed PDU had CI set */ -#define SERVICE_CLP (0x08000000) /* RXed PDU had CLP set */ -#define SERVICE_STREAM (0x04000000) /* RX Stream mode */ -#define SERVICE_GET_VCI(x) (((x)>>16)&0x3FF) -#define SERVICE_GET_END(x) ((x)&0x1FFF) - -/* Handle one thing from the service list - returns true if it marked a - * VCC ready for xmit - */ -static int handle_service(struct lanai_dev *lanai, u32 s) -{ - vci_t vci = SERVICE_GET_VCI(s); - struct lanai_vcc *lvcc; - read_lock(&vcc_sklist_lock); - lvcc = lanai->vccs[vci]; - if (unlikely(lvcc == NULL)) { - read_unlock(&vcc_sklist_lock); - DPRINTK("(itf %d) got service entry 0x%X for nonexistent " - "vcc %d\n", lanai->number, (unsigned int) s, vci); - if (s & SERVICE_TX) - lanai->stats.service_notx++; - else - lanai->stats.service_norx++; - return 0; - } - if (s & SERVICE_TX) { /* segmentation interrupt */ - if (unlikely(lvcc->tx.atmvcc == NULL)) { - read_unlock(&vcc_sklist_lock); - DPRINTK("(itf %d) got service entry 0x%X for non-TX " - "vcc %d\n", lanai->number, (unsigned int) s, vci); - lanai->stats.service_notx++; - return 0; - } - __set_bit(vci, lanai->transmit_ready); - lvcc->tx.endptr = SERVICE_GET_END(s); - read_unlock(&vcc_sklist_lock); - return 1; - } - if (unlikely(lvcc->rx.atmvcc == NULL)) { - read_unlock(&vcc_sklist_lock); - DPRINTK("(itf %d) got service entry 0x%X for non-RX " - "vcc %d\n", lanai->number, (unsigned int) s, vci); - lanai->stats.service_norx++; - return 0; - } - if (unlikely(lvcc->rx.atmvcc->qos.aal != ATM_AAL5)) { - read_unlock(&vcc_sklist_lock); - DPRINTK("(itf %d) got RX service entry 0x%X for non-AAL5 " - "vcc %d\n", lanai->number, (unsigned int) s, vci); - lanai->stats.service_rxnotaal5++; - atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); - return 0; - } - if (likely(!(s & (SERVICE_TRASH | SERVICE_STREAM | SERVICE_CRCERR)))) { - vcc_rx_aal5(lvcc, SERVICE_GET_END(s)); - read_unlock(&vcc_sklist_lock); - return 0; - } - if (s & SERVICE_TRASH) { - int bytes; - read_unlock(&vcc_sklist_lock); - DPRINTK("got trashed rx pdu on vci %d\n", vci); - atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); - lvcc->stats.x.aal5.service_trash++; - bytes = (SERVICE_GET_END(s) * 16) - - (((unsigned long) lvcc->rx.buf.ptr) - - ((unsigned long) lvcc->rx.buf.start)) + 47; - if (bytes < 0) - bytes += lanai_buf_size(&lvcc->rx.buf); - lanai->stats.ovfl_trash += (bytes / 48); - return 0; - } - if (s & SERVICE_STREAM) { - read_unlock(&vcc_sklist_lock); - atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); - lvcc->stats.x.aal5.service_stream++; - printk(KERN_ERR DEV_LABEL "(itf %d): Got AAL5 stream " - "PDU on VCI %d!\n", lanai->number, vci); - lanai_reset(lanai); - return 0; - } - DPRINTK("got rx crc error on vci %d\n", vci); - atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); - lvcc->stats.x.aal5.service_rxcrc++; - lvcc->rx.buf.ptr = &lvcc->rx.buf.start[SERVICE_GET_END(s) * 4]; - cardvcc_write(lvcc, SERVICE_GET_END(s), vcc_rxreadptr); - read_unlock(&vcc_sklist_lock); - return 0; -} - -/* Try transmitting on all VCIs that we marked ready to serve */ -static void iter_transmit(struct lanai_dev *lanai, vci_t vci) -{ - struct lanai_vcc *lvcc = lanai->vccs[vci]; - if (vcc_is_backlogged(lvcc)) - lvcc->tx.unqueue(lanai, lvcc, lvcc->tx.endptr); -} - -/* Run service queue -- called from interrupt context or with - * interrupts otherwise disabled and with the lanai->servicelock - * lock held - */ -static void run_service(struct lanai_dev *lanai) -{ - int ntx = 0; - u32 wreg = reg_read(lanai, ServWrite_Reg); - const u32 *end = lanai->service.start + wreg; - while (lanai->service.ptr != end) { - ntx += handle_service(lanai, - le32_to_cpup(lanai->service.ptr++)); - if (lanai->service.ptr >= lanai->service.end) - lanai->service.ptr = lanai->service.start; - } - reg_write(lanai, wreg, ServRead_Reg); - if (ntx != 0) { - read_lock(&vcc_sklist_lock); - vci_bitfield_iterate(lanai, lanai->transmit_ready, - iter_transmit); - bitmap_zero(lanai->transmit_ready, NUM_VCI); - read_unlock(&vcc_sklist_lock); - } -} - -/* -------------------- GATHER STATISTICS: */ - -static void get_statistics(struct lanai_dev *lanai) -{ - u32 statreg = reg_read(lanai, Statistics_Reg); - lanai->stats.atm_ovfl += STATS_GET_FIFO_OVFL(statreg); - lanai->stats.hec_err += STATS_GET_HEC_ERR(statreg); - lanai->stats.vci_trash += STATS_GET_BAD_VCI(statreg); - lanai->stats.ovfl_trash += STATS_GET_BUF_OVFL(statreg); -} - -/* -------------------- POLLING TIMER: */ - -#ifndef DEBUG_RW -/* Try to undequeue 1 backlogged vcc */ -static void iter_dequeue(struct lanai_dev *lanai, vci_t vci) -{ - struct lanai_vcc *lvcc = lanai->vccs[vci]; - int endptr; - if (lvcc == NULL || lvcc->tx.atmvcc == NULL || - !vcc_is_backlogged(lvcc)) { - __clear_bit(vci, lanai->backlog_vccs); - return; - } - endptr = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)); - lvcc->tx.unqueue(lanai, lvcc, endptr); -} -#endif /* !DEBUG_RW */ - -static void lanai_timed_poll(struct timer_list *t) -{ - struct lanai_dev *lanai = timer_container_of(lanai, t, timer); -#ifndef DEBUG_RW - unsigned long flags; -#ifdef USE_POWERDOWN - if (lanai->conf1 & CONFIG1_POWERDOWN) - return; -#endif /* USE_POWERDOWN */ - local_irq_save(flags); - /* If we can grab the spinlock, check if any services need to be run */ - if (spin_trylock(&lanai->servicelock)) { - run_service(lanai); - spin_unlock(&lanai->servicelock); - } - /* ...and see if any backlogged VCs can make progress */ - /* unfortunately linux has no read_trylock() currently */ - read_lock(&vcc_sklist_lock); - vci_bitfield_iterate(lanai, lanai->backlog_vccs, iter_dequeue); - read_unlock(&vcc_sklist_lock); - local_irq_restore(flags); - - get_statistics(lanai); -#endif /* !DEBUG_RW */ - mod_timer(&lanai->timer, jiffies + LANAI_POLL_PERIOD); -} - -static inline void lanai_timed_poll_start(struct lanai_dev *lanai) -{ - timer_setup(&lanai->timer, lanai_timed_poll, 0); - lanai->timer.expires = jiffies + LANAI_POLL_PERIOD; - add_timer(&lanai->timer); -} - -static inline void lanai_timed_poll_stop(struct lanai_dev *lanai) -{ - timer_delete_sync(&lanai->timer); -} - -/* -------------------- INTERRUPT SERVICE: */ - -static inline void lanai_int_1(struct lanai_dev *lanai, u32 reason) -{ - u32 ack = 0; - if (reason & INT_SERVICE) { - ack = INT_SERVICE; - spin_lock(&lanai->servicelock); - run_service(lanai); - spin_unlock(&lanai->servicelock); - } - if (reason & (INT_AAL0_STR | INT_AAL0)) { - ack |= reason & (INT_AAL0_STR | INT_AAL0); - vcc_rx_aal0(lanai); - } - /* The rest of the interrupts are pretty rare */ - if (ack == reason) - goto done; - if (reason & INT_STATS) { - reason &= ~INT_STATS; /* No need to ack */ - get_statistics(lanai); - } - if (reason & INT_STATUS) { - ack |= reason & INT_STATUS; - lanai_check_status(lanai); - } - if (unlikely(reason & INT_DMASHUT)) { - printk(KERN_ERR DEV_LABEL "(itf %d): driver error - DMA " - "shutdown, reason=0x%08X, address=0x%08X\n", - lanai->number, (unsigned int) (reason & INT_DMASHUT), - (unsigned int) reg_read(lanai, DMA_Addr_Reg)); - if (reason & INT_TABORTBM) { - lanai_reset(lanai); - return; - } - ack |= (reason & INT_DMASHUT); - printk(KERN_ERR DEV_LABEL "(itf %d): re-enabling DMA\n", - lanai->number); - conf1_write(lanai); - lanai->stats.dma_reenable++; - pcistatus_check(lanai, 0); - } - if (unlikely(reason & INT_TABORTSENT)) { - ack |= (reason & INT_TABORTSENT); - printk(KERN_ERR DEV_LABEL "(itf %d): sent PCI target abort\n", - lanai->number); - pcistatus_check(lanai, 0); - } - if (unlikely(reason & INT_SEGSHUT)) { - printk(KERN_ERR DEV_LABEL "(itf %d): driver error - " - "segmentation shutdown, reason=0x%08X\n", lanai->number, - (unsigned int) (reason & INT_SEGSHUT)); - lanai_reset(lanai); - return; - } - if (unlikely(reason & (INT_PING | INT_WAKE))) { - printk(KERN_ERR DEV_LABEL "(itf %d): driver error - " - "unexpected interrupt 0x%08X, resetting\n", - lanai->number, - (unsigned int) (reason & (INT_PING | INT_WAKE))); - lanai_reset(lanai); - return; - } -#ifdef DEBUG - if (unlikely(ack != reason)) { - DPRINTK("unacked ints: 0x%08X\n", - (unsigned int) (reason & ~ack)); - ack = reason; - } -#endif - done: - if (ack != 0) - reg_write(lanai, ack, IntAck_Reg); -} - -static irqreturn_t lanai_int(int irq, void *devid) -{ - struct lanai_dev *lanai = devid; - u32 reason; - -#ifdef USE_POWERDOWN - /* - * If we're powered down we shouldn't be generating any interrupts - - * so assume that this is a shared interrupt line and it's for someone - * else - */ - if (unlikely(lanai->conf1 & CONFIG1_POWERDOWN)) - return IRQ_NONE; -#endif - - reason = intr_pending(lanai); - if (reason == 0) - return IRQ_NONE; /* Must be for someone else */ - - do { - if (unlikely(reason == 0xFFFFFFFF)) - break; /* Maybe we've been unplugged? */ - lanai_int_1(lanai, reason); - reason = intr_pending(lanai); - } while (reason != 0); - - return IRQ_HANDLED; -} - -/* TODO - it would be nice if we could use the "delayed interrupt" system - * to some advantage - */ - -/* -------------------- CHECK BOARD ID/REV: */ - -/* - * The board id and revision are stored both in the reset register and - * in the PCI configuration space - the documentation says to check - * each of them. If revp!=NULL we store the revision there - */ -static int check_board_id_and_rev(const char *name, u32 val, int *revp) -{ - DPRINTK("%s says board_id=%d, board_rev=%d\n", name, - (int) RESET_GET_BOARD_ID(val), - (int) RESET_GET_BOARD_REV(val)); - if (RESET_GET_BOARD_ID(val) != BOARD_ID_LANAI256) { - printk(KERN_ERR DEV_LABEL ": Found %s board-id %d -- not a " - "Lanai 25.6\n", name, (int) RESET_GET_BOARD_ID(val)); - return -ENODEV; - } - if (revp != NULL) - *revp = RESET_GET_BOARD_REV(val); - return 0; -} - -/* -------------------- PCI INITIALIZATION/SHUTDOWN: */ - -static int lanai_pci_start(struct lanai_dev *lanai) -{ - struct pci_dev *pci = lanai->pci; - int result; - - if (pci_enable_device(pci) != 0) { - printk(KERN_ERR DEV_LABEL "(itf %d): can't enable " - "PCI device", lanai->number); - return -ENXIO; - } - pci_set_master(pci); - if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32)) != 0) { - printk(KERN_WARNING DEV_LABEL - "(itf %d): No suitable DMA available.\n", lanai->number); - return -EBUSY; - } - result = check_board_id_and_rev("PCI", pci->subsystem_device, NULL); - if (result != 0) - return result; - /* Set latency timer to zero as per lanai docs */ - result = pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0); - if (result != PCIBIOS_SUCCESSFUL) { - printk(KERN_ERR DEV_LABEL "(itf %d): can't write " - "PCI_LATENCY_TIMER: %d\n", lanai->number, result); - return -EINVAL; - } - pcistatus_check(lanai, 1); - pcistatus_check(lanai, 0); - return 0; -} - -/* -------------------- VPI/VCI ALLOCATION: */ - -/* - * We _can_ use VCI==0 for normal traffic, but only for UBR (or we'll - * get a CBRZERO interrupt), and we can use it only if no one is receiving - * AAL0 traffic (since they will use the same queue) - according to the - * docs we shouldn't even use it for AAL0 traffic - */ -static inline int vci0_is_ok(struct lanai_dev *lanai, - const struct atm_qos *qos) -{ - if (qos->txtp.traffic_class == ATM_CBR || qos->aal == ATM_AAL0) - return 0; - if (qos->rxtp.traffic_class != ATM_NONE) { - if (lanai->naal0 != 0) - return 0; - lanai->conf2 |= CONFIG2_VCI0_NORMAL; - conf2_write_if_powerup(lanai); - } - return 1; -} - -/* return true if vci is currently unused, or if requested qos is - * compatible - */ -static int vci_is_ok(struct lanai_dev *lanai, vci_t vci, - const struct atm_vcc *atmvcc) -{ - const struct atm_qos *qos = &atmvcc->qos; - const struct lanai_vcc *lvcc = lanai->vccs[vci]; - if (vci == 0 && !vci0_is_ok(lanai, qos)) - return 0; - if (unlikely(lvcc != NULL)) { - if (qos->rxtp.traffic_class != ATM_NONE && - lvcc->rx.atmvcc != NULL && lvcc->rx.atmvcc != atmvcc) - return 0; - if (qos->txtp.traffic_class != ATM_NONE && - lvcc->tx.atmvcc != NULL && lvcc->tx.atmvcc != atmvcc) - return 0; - if (qos->txtp.traffic_class == ATM_CBR && - lanai->cbrvcc != NULL && lanai->cbrvcc != atmvcc) - return 0; - } - if (qos->aal == ATM_AAL0 && lanai->naal0 == 0 && - qos->rxtp.traffic_class != ATM_NONE) { - const struct lanai_vcc *vci0 = lanai->vccs[0]; - if (vci0 != NULL && vci0->rx.atmvcc != NULL) - return 0; - lanai->conf2 &= ~CONFIG2_VCI0_NORMAL; - conf2_write_if_powerup(lanai); - } - return 1; -} - -static int lanai_normalize_ci(struct lanai_dev *lanai, - const struct atm_vcc *atmvcc, short *vpip, vci_t *vcip) -{ - switch (*vpip) { - case ATM_VPI_ANY: - *vpip = 0; - fallthrough; - case 0: - break; - default: - return -EADDRINUSE; - } - switch (*vcip) { - case ATM_VCI_ANY: - for (*vcip = ATM_NOT_RSV_VCI; *vcip < lanai->num_vci; - (*vcip)++) - if (vci_is_ok(lanai, *vcip, atmvcc)) - return 0; - return -EADDRINUSE; - default: - if (*vcip >= lanai->num_vci || *vcip < 0 || - !vci_is_ok(lanai, *vcip, atmvcc)) - return -EADDRINUSE; - } - return 0; -} - -/* -------------------- MANAGE CBR: */ - -/* - * CBR ICG is stored as a fixed-point number with 4 fractional bits. - * Note that storing a number greater than 2046.0 will result in - * incorrect shaping - */ -#define CBRICG_FRAC_BITS (4) -#define CBRICG_MAX (2046 << CBRICG_FRAC_BITS) - -/* - * ICG is related to PCR with the formula PCR = MAXPCR / (ICG + 1) - * where MAXPCR is (according to the docs) 25600000/(54*8), - * which is equal to (3125<<9)/27. - * - * Solving for ICG, we get: - * ICG = MAXPCR/PCR - 1 - * ICG = (3125<<9)/(27*PCR) - 1 - * ICG = ((3125<<9) - (27*PCR)) / (27*PCR) - * - * The end result is supposed to be a fixed-point number with FRAC_BITS - * bits of a fractional part, so we keep everything in the numerator - * shifted by that much as we compute - * - */ -static int pcr_to_cbricg(const struct atm_qos *qos) -{ - int rounddown = 0; /* 1 = Round PCR down, i.e. round ICG _up_ */ - int x, icg, pcr = atm_pcr_goal(&qos->txtp); - if (pcr == 0) /* Use maximum bandwidth */ - return 0; - if (pcr < 0) { - rounddown = 1; - pcr = -pcr; - } - x = pcr * 27; - icg = (3125 << (9 + CBRICG_FRAC_BITS)) - (x << CBRICG_FRAC_BITS); - if (rounddown) - icg += x - 1; - icg /= x; - if (icg > CBRICG_MAX) - icg = CBRICG_MAX; - DPRINTK("pcr_to_cbricg: pcr=%d rounddown=%c icg=%d\n", - pcr, rounddown ? 'Y' : 'N', icg); - return icg; -} - -static inline void lanai_cbr_setup(struct lanai_dev *lanai) -{ - reg_write(lanai, pcr_to_cbricg(&lanai->cbrvcc->qos), CBR_ICG_Reg); - reg_write(lanai, lanai->cbrvcc->vci, CBR_PTR_Reg); - lanai->conf2 |= CONFIG2_CBR_ENABLE; - conf2_write(lanai); -} - -static inline void lanai_cbr_shutdown(struct lanai_dev *lanai) -{ - lanai->conf2 &= ~CONFIG2_CBR_ENABLE; - conf2_write(lanai); -} - -/* -------------------- OPERATIONS: */ - -/* setup a newly detected device */ -static int lanai_dev_open(struct atm_dev *atmdev) -{ - struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; - unsigned long raw_base; - int result; - - DPRINTK("In lanai_dev_open()\n"); - /* Basic device fields */ - lanai->number = atmdev->number; - lanai->num_vci = NUM_VCI; - bitmap_zero(lanai->backlog_vccs, NUM_VCI); - bitmap_zero(lanai->transmit_ready, NUM_VCI); - lanai->naal0 = 0; -#ifdef USE_POWERDOWN - lanai->nbound = 0; -#endif - lanai->cbrvcc = NULL; - memset(&lanai->stats, 0, sizeof lanai->stats); - spin_lock_init(&lanai->endtxlock); - spin_lock_init(&lanai->servicelock); - atmdev->ci_range.vpi_bits = 0; - atmdev->ci_range.vci_bits = 0; - while (1 << atmdev->ci_range.vci_bits < lanai->num_vci) - atmdev->ci_range.vci_bits++; - atmdev->link_rate = ATM_25_PCR; - - /* 3.2: PCI initialization */ - if ((result = lanai_pci_start(lanai)) != 0) - goto error; - raw_base = lanai->pci->resource[0].start; - lanai->base = (bus_addr_t) ioremap(raw_base, LANAI_MAPPING_SIZE); - if (lanai->base == NULL) { - printk(KERN_ERR DEV_LABEL ": couldn't remap I/O space\n"); - result = -ENOMEM; - goto error_pci; - } - /* 3.3: Reset lanai and PHY */ - reset_board(lanai); - lanai->conf1 = reg_read(lanai, Config1_Reg); - lanai->conf1 &= ~(CONFIG1_GPOUT1 | CONFIG1_POWERDOWN | - CONFIG1_MASK_LEDMODE); - lanai->conf1 |= CONFIG1_SET_LEDMODE(LEDMODE_NOT_SOOL); - reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg); - udelay(1000); - conf1_write(lanai); - - /* - * 3.4: Turn on endian mode for big-endian hardware - * We don't actually want to do this - the actual bit fields - * in the endian register are not documented anywhere. - * Instead we do the bit-flipping ourselves on big-endian - * hardware. - * - * 3.5: get the board ID/rev by reading the reset register - */ - result = check_board_id_and_rev("register", - reg_read(lanai, Reset_Reg), &lanai->board_rev); - if (result != 0) - goto error_unmap; - - /* 3.6: read EEPROM */ - if ((result = eeprom_read(lanai)) != 0) - goto error_unmap; - if ((result = eeprom_validate(lanai)) != 0) - goto error_unmap; - - /* 3.7: re-reset PHY, do loopback tests, setup PHY */ - reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg); - udelay(1000); - conf1_write(lanai); - /* TODO - loopback tests */ - lanai->conf1 |= (CONFIG1_GPOUT2 | CONFIG1_GPOUT3 | CONFIG1_DMA_ENABLE); - conf1_write(lanai); - - /* 3.8/3.9: test and initialize card SRAM */ - if ((result = sram_test_and_clear(lanai)) != 0) - goto error_unmap; - - /* 3.10: initialize lanai registers */ - lanai->conf1 |= CONFIG1_DMA_ENABLE; - conf1_write(lanai); - if ((result = service_buffer_allocate(lanai)) != 0) - goto error_unmap; - if ((result = vcc_table_allocate(lanai)) != 0) - goto error_service; - lanai->conf2 = (lanai->num_vci >= 512 ? CONFIG2_HOWMANY : 0) | - CONFIG2_HEC_DROP | /* ??? */ CONFIG2_PTI7_MODE; - conf2_write(lanai); - reg_write(lanai, TX_FIFO_DEPTH, TxDepth_Reg); - reg_write(lanai, 0, CBR_ICG_Reg); /* CBR defaults to no limit */ - if ((result = request_irq(lanai->pci->irq, lanai_int, IRQF_SHARED, - DEV_LABEL, lanai)) != 0) { - printk(KERN_ERR DEV_LABEL ": can't allocate interrupt\n"); - goto error_vcctable; - } - mb(); /* Make sure that all that made it */ - intr_enable(lanai, INT_ALL & ~(INT_PING | INT_WAKE)); - /* 3.11: initialize loop mode (i.e. turn looping off) */ - lanai->conf1 = (lanai->conf1 & ~CONFIG1_MASK_LOOPMODE) | - CONFIG1_SET_LOOPMODE(LOOPMODE_NORMAL) | - CONFIG1_GPOUT2 | CONFIG1_GPOUT3; - conf1_write(lanai); - lanai->status = reg_read(lanai, Status_Reg); - /* We're now done initializing this card */ -#ifdef USE_POWERDOWN - lanai->conf1 |= CONFIG1_POWERDOWN; - conf1_write(lanai); -#endif - memcpy(atmdev->esi, eeprom_mac(lanai), ESI_LEN); - lanai_timed_poll_start(lanai); - printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d, base=%p, irq=%u " - "(%pMF)\n", lanai->number, (int) lanai->pci->revision, - lanai->base, lanai->pci->irq, atmdev->esi); - printk(KERN_NOTICE DEV_LABEL "(itf %d): LANAI%s, serialno=%u(0x%X), " - "board_rev=%d\n", lanai->number, - lanai->type==lanai2 ? "2" : "HB", (unsigned int) lanai->serialno, - (unsigned int) lanai->serialno, lanai->board_rev); - return 0; - - error_vcctable: - vcc_table_deallocate(lanai); - error_service: - service_buffer_deallocate(lanai); - error_unmap: - reset_board(lanai); -#ifdef USE_POWERDOWN - lanai->conf1 = reg_read(lanai, Config1_Reg) | CONFIG1_POWERDOWN; - conf1_write(lanai); -#endif - iounmap(lanai->base); - lanai->base = NULL; - error_pci: - pci_disable_device(lanai->pci); - error: - return result; -} - -/* called when device is being shutdown, and all vcc's are gone - higher - * levels will deallocate the atm device for us - */ -static void lanai_dev_close(struct atm_dev *atmdev) -{ - struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; - if (lanai->base==NULL) - return; - printk(KERN_INFO DEV_LABEL "(itf %d): shutting down interface\n", - lanai->number); - lanai_timed_poll_stop(lanai); -#ifdef USE_POWERDOWN - lanai->conf1 = reg_read(lanai, Config1_Reg) & ~CONFIG1_POWERDOWN; - conf1_write(lanai); -#endif - intr_disable(lanai, INT_ALL); - free_irq(lanai->pci->irq, lanai); - reset_board(lanai); -#ifdef USE_POWERDOWN - lanai->conf1 |= CONFIG1_POWERDOWN; - conf1_write(lanai); -#endif - pci_disable_device(lanai->pci); - vcc_table_deallocate(lanai); - service_buffer_deallocate(lanai); - iounmap(lanai->base); - kfree(lanai); -} - -/* close a vcc */ -static void lanai_close(struct atm_vcc *atmvcc) -{ - struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data; - struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data; - if (lvcc == NULL) - return; - clear_bit(ATM_VF_READY, &atmvcc->flags); - clear_bit(ATM_VF_PARTIAL, &atmvcc->flags); - if (lvcc->rx.atmvcc == atmvcc) { - lanai_shutdown_rx_vci(lvcc); - if (atmvcc->qos.aal == ATM_AAL0) { - if (--lanai->naal0 <= 0) - aal0_buffer_free(lanai); - } else - lanai_buf_deallocate(&lvcc->rx.buf, lanai->pci); - lvcc->rx.atmvcc = NULL; - } - if (lvcc->tx.atmvcc == atmvcc) { - if (atmvcc == lanai->cbrvcc) { - if (lvcc->vbase != NULL) - lanai_cbr_shutdown(lanai); - lanai->cbrvcc = NULL; - } - lanai_shutdown_tx_vci(lanai, lvcc); - lanai_buf_deallocate(&lvcc->tx.buf, lanai->pci); - lvcc->tx.atmvcc = NULL; - } - if (--lvcc->nref == 0) { - host_vcc_unbind(lanai, lvcc); - kfree(lvcc); - } - atmvcc->dev_data = NULL; - clear_bit(ATM_VF_ADDR, &atmvcc->flags); -} - -/* open a vcc on the card to vpi/vci */ -static int lanai_open(struct atm_vcc *atmvcc) -{ - struct lanai_dev *lanai; - struct lanai_vcc *lvcc; - int result = 0; - int vci = atmvcc->vci; - short vpi = atmvcc->vpi; - /* we don't support partial open - it's not really useful anyway */ - if ((test_bit(ATM_VF_PARTIAL, &atmvcc->flags)) || - (vpi == ATM_VPI_UNSPEC) || (vci == ATM_VCI_UNSPEC)) - return -EINVAL; - lanai = (struct lanai_dev *) atmvcc->dev->dev_data; - result = lanai_normalize_ci(lanai, atmvcc, &vpi, &vci); - if (unlikely(result != 0)) - goto out; - set_bit(ATM_VF_ADDR, &atmvcc->flags); - if (atmvcc->qos.aal != ATM_AAL0 && atmvcc->qos.aal != ATM_AAL5) - return -EINVAL; - DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n", lanai->number, - (int) vpi, vci); - lvcc = lanai->vccs[vci]; - if (lvcc == NULL) { - lvcc = new_lanai_vcc(); - if (unlikely(lvcc == NULL)) - return -ENOMEM; - atmvcc->dev_data = lvcc; - } - lvcc->nref++; - if (atmvcc->qos.rxtp.traffic_class != ATM_NONE) { - APRINTK(lvcc->rx.atmvcc == NULL, "rx.atmvcc!=NULL, vci=%d\n", - vci); - if (atmvcc->qos.aal == ATM_AAL0) { - if (lanai->naal0 == 0) - result = aal0_buffer_allocate(lanai); - } else - result = lanai_setup_rx_vci_aal5( - lanai, lvcc, &atmvcc->qos); - if (unlikely(result != 0)) - goto out_free; - lvcc->rx.atmvcc = atmvcc; - lvcc->stats.rx_nomem = 0; - lvcc->stats.x.aal5.rx_badlen = 0; - lvcc->stats.x.aal5.service_trash = 0; - lvcc->stats.x.aal5.service_stream = 0; - lvcc->stats.x.aal5.service_rxcrc = 0; - if (atmvcc->qos.aal == ATM_AAL0) - lanai->naal0++; - } - if (atmvcc->qos.txtp.traffic_class != ATM_NONE) { - APRINTK(lvcc->tx.atmvcc == NULL, "tx.atmvcc!=NULL, vci=%d\n", - vci); - result = lanai_setup_tx_vci(lanai, lvcc, &atmvcc->qos); - if (unlikely(result != 0)) - goto out_free; - lvcc->tx.atmvcc = atmvcc; - if (atmvcc->qos.txtp.traffic_class == ATM_CBR) { - APRINTK(lanai->cbrvcc == NULL, - "cbrvcc!=NULL, vci=%d\n", vci); - lanai->cbrvcc = atmvcc; - } - } - host_vcc_bind(lanai, lvcc, vci); - /* - * Make sure everything made it to RAM before we tell the card about - * the VCC - */ - wmb(); - if (atmvcc == lvcc->rx.atmvcc) - host_vcc_start_rx(lvcc); - if (atmvcc == lvcc->tx.atmvcc) { - host_vcc_start_tx(lvcc); - if (lanai->cbrvcc == atmvcc) - lanai_cbr_setup(lanai); - } - set_bit(ATM_VF_READY, &atmvcc->flags); - return 0; - out_free: - lanai_close(atmvcc); - out: - return result; -} - -static int lanai_send(struct atm_vcc *atmvcc, struct sk_buff *skb) -{ - struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data; - struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data; - unsigned long flags; - if (unlikely(lvcc == NULL || lvcc->vbase == NULL || - lvcc->tx.atmvcc != atmvcc)) - goto einval; -#ifdef DEBUG - if (unlikely(skb == NULL)) { - DPRINTK("lanai_send: skb==NULL for vci=%d\n", atmvcc->vci); - goto einval; - } - if (unlikely(lanai == NULL)) { - DPRINTK("lanai_send: lanai==NULL for vci=%d\n", atmvcc->vci); - goto einval; - } -#endif - ATM_SKB(skb)->vcc = atmvcc; - switch (atmvcc->qos.aal) { - case ATM_AAL5: - read_lock_irqsave(&vcc_sklist_lock, flags); - vcc_tx_aal5(lanai, lvcc, skb); - read_unlock_irqrestore(&vcc_sklist_lock, flags); - return 0; - case ATM_AAL0: - if (unlikely(skb->len != ATM_CELL_SIZE-1)) - goto einval; - /* NOTE - this next line is technically invalid - we haven't unshared skb */ - cpu_to_be32s((u32 *) skb->data); - read_lock_irqsave(&vcc_sklist_lock, flags); - vcc_tx_aal0(lanai, lvcc, skb); - read_unlock_irqrestore(&vcc_sklist_lock, flags); - return 0; - } - DPRINTK("lanai_send: bad aal=%d on vci=%d\n", (int) atmvcc->qos.aal, - atmvcc->vci); - einval: - lanai_free_skb(atmvcc, skb); - return -EINVAL; -} - -static int lanai_change_qos(struct atm_vcc *atmvcc, - /*const*/ struct atm_qos *qos, int flags) -{ - return -EBUSY; /* TODO: need to write this */ -} - -#ifndef CONFIG_PROC_FS -#define lanai_proc_read NULL -#else -static int lanai_proc_read(struct atm_dev *atmdev, loff_t *pos, char *page) -{ - struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; - loff_t left = *pos; - struct lanai_vcc *lvcc; - if (left-- == 0) - return sprintf(page, DEV_LABEL "(itf %d): chip=LANAI%s, " - "serial=%u, magic=0x%08X, num_vci=%d\n", - atmdev->number, lanai->type==lanai2 ? "2" : "HB", - (unsigned int) lanai->serialno, - (unsigned int) lanai->magicno, lanai->num_vci); - if (left-- == 0) - return sprintf(page, "revision: board=%d, pci_if=%d\n", - lanai->board_rev, (int) lanai->pci->revision); - if (left-- == 0) - return sprintf(page, "EEPROM ESI: %pM\n", - &lanai->eeprom[EEPROM_MAC]); - if (left-- == 0) - return sprintf(page, "status: SOOL=%d, LOCD=%d, LED=%d, " - "GPIN=%d\n", (lanai->status & STATUS_SOOL) ? 1 : 0, - (lanai->status & STATUS_LOCD) ? 1 : 0, - (lanai->status & STATUS_LED) ? 1 : 0, - (lanai->status & STATUS_GPIN) ? 1 : 0); - if (left-- == 0) - return sprintf(page, "global buffer sizes: service=%zu, " - "aal0_rx=%zu\n", lanai_buf_size(&lanai->service), - lanai->naal0 ? lanai_buf_size(&lanai->aal0buf) : 0); - if (left-- == 0) { - get_statistics(lanai); - return sprintf(page, "cells in error: overflow=%u, " - "closed_vci=%u, bad_HEC=%u, rx_fifo=%u\n", - lanai->stats.ovfl_trash, lanai->stats.vci_trash, - lanai->stats.hec_err, lanai->stats.atm_ovfl); - } - if (left-- == 0) - return sprintf(page, "PCI errors: parity_detect=%u, " - "master_abort=%u, master_target_abort=%u,\n", - lanai->stats.pcierr_parity_detect, - lanai->stats.pcierr_serr_set, - lanai->stats.pcierr_m_target_abort); - if (left-- == 0) - return sprintf(page, " slave_target_abort=%u, " - "master_parity=%u\n", lanai->stats.pcierr_s_target_abort, - lanai->stats.pcierr_master_parity); - if (left-- == 0) - return sprintf(page, " no_tx=%u, " - "no_rx=%u, bad_rx_aal=%u\n", lanai->stats.service_norx, - lanai->stats.service_notx, - lanai->stats.service_rxnotaal5); - if (left-- == 0) - return sprintf(page, "resets: dma=%u, card=%u\n", - lanai->stats.dma_reenable, lanai->stats.card_reset); - /* At this point, "left" should be the VCI we're looking for */ - read_lock(&vcc_sklist_lock); - for (; ; left++) { - if (left >= NUM_VCI) { - left = 0; - goto out; - } - if ((lvcc = lanai->vccs[left]) != NULL) - break; - (*pos)++; - } - /* Note that we re-use "left" here since we're done with it */ - left = sprintf(page, "VCI %4d: nref=%d, rx_nomem=%u", (vci_t) left, - lvcc->nref, lvcc->stats.rx_nomem); - if (lvcc->rx.atmvcc != NULL) { - left += sprintf(&page[left], ",\n rx_AAL=%d", - lvcc->rx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0); - if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) - left += sprintf(&page[left], ", rx_buf_size=%zu, " - "rx_bad_len=%u,\n rx_service_trash=%u, " - "rx_service_stream=%u, rx_bad_crc=%u", - lanai_buf_size(&lvcc->rx.buf), - lvcc->stats.x.aal5.rx_badlen, - lvcc->stats.x.aal5.service_trash, - lvcc->stats.x.aal5.service_stream, - lvcc->stats.x.aal5.service_rxcrc); - } - if (lvcc->tx.atmvcc != NULL) - left += sprintf(&page[left], ",\n tx_AAL=%d, " - "tx_buf_size=%zu, tx_qos=%cBR, tx_backlogged=%c", - lvcc->tx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0, - lanai_buf_size(&lvcc->tx.buf), - lvcc->tx.atmvcc == lanai->cbrvcc ? 'C' : 'U', - vcc_is_backlogged(lvcc) ? 'Y' : 'N'); - page[left++] = '\n'; - page[left] = '\0'; - out: - read_unlock(&vcc_sklist_lock); - return left; -} -#endif /* CONFIG_PROC_FS */ - -/* -------------------- HOOKS: */ - -static const struct atmdev_ops ops = { - .dev_close = lanai_dev_close, - .open = lanai_open, - .close = lanai_close, - .send = lanai_send, - .phy_put = NULL, - .phy_get = NULL, - .change_qos = lanai_change_qos, - .proc_read = lanai_proc_read, - .owner = THIS_MODULE -}; - -/* initialize one probed card */ -static int lanai_init_one(struct pci_dev *pci, - const struct pci_device_id *ident) -{ - struct lanai_dev *lanai; - struct atm_dev *atmdev; - int result; - - lanai = kzalloc_obj(*lanai); - if (lanai == NULL) { - printk(KERN_ERR DEV_LABEL - ": couldn't allocate dev_data structure!\n"); - return -ENOMEM; - } - - atmdev = atm_dev_register(DEV_LABEL, &pci->dev, &ops, -1, NULL); - if (atmdev == NULL) { - printk(KERN_ERR DEV_LABEL - ": couldn't register atm device!\n"); - kfree(lanai); - return -EBUSY; - } - - atmdev->dev_data = lanai; - lanai->pci = pci; - lanai->type = (enum lanai_type) ident->device; - - result = lanai_dev_open(atmdev); - if (result != 0) { - DPRINTK("lanai_start() failed, err=%d\n", -result); - atm_dev_deregister(atmdev); - kfree(lanai); - } - return result; -} - -static const struct pci_device_id lanai_pci_tbl[] = { - { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_LANAI2) }, - { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_LANAIHB) }, - { 0, } /* terminal entry */ -}; -MODULE_DEVICE_TABLE(pci, lanai_pci_tbl); - -static struct pci_driver lanai_driver = { - .name = DEV_LABEL, - .id_table = lanai_pci_tbl, - .probe = lanai_init_one, -}; - -module_pci_driver(lanai_driver); - -MODULE_AUTHOR("Mitchell Blank Jr "); -MODULE_DESCRIPTION("Efficient Networks Speedstream 3010 driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/atm/midway.h b/drivers/atm/midway.h deleted file mode 100644 index d47307adc0c9..000000000000 --- a/drivers/atm/midway.h +++ /dev/null @@ -1,266 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* drivers/atm/midway.h - Efficient Networks Midway (SAR) description */ - -/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ - - -#ifndef DRIVERS_ATM_MIDWAY_H -#define DRIVERS_ATM_MIDWAY_H - - -#define NR_VCI 1024 /* number of VCIs */ -#define NR_VCI_LD 10 /* log2(NR_VCI) */ -#define NR_DMA_RX 512 /* RX DMA queue entries */ -#define NR_DMA_TX 512 /* TX DMA queue entries */ -#define NR_SERVICE NR_VCI /* service list size */ -#define NR_CHAN 8 /* number of TX channels */ -#define TS_CLOCK 25000000 /* traffic shaper clock (cell/sec) */ - -#define MAP_MAX_SIZE 0x00400000 /* memory window for max config */ -#define EPROM_SIZE 0x00010000 -#define MEM_VALID 0xffc00000 /* mask base address with this */ -#define PHY_BASE 0x00020000 /* offset of PHY register are */ -#define REG_BASE 0x00040000 /* offset of Midway register area */ -#define RAM_BASE 0x00200000 /* offset of RAM area */ -#define RAM_INCREMENT 0x00020000 /* probe for RAM every 128kB */ - -#define MID_VCI_BASE RAM_BASE -#define MID_DMA_RX_BASE (MID_VCI_BASE+NR_VCI*16) -#define MID_DMA_TX_BASE (MID_DMA_RX_BASE+NR_DMA_RX*8) -#define MID_SERVICE_BASE (MID_DMA_TX_BASE+NR_DMA_TX*8) -#define MID_FREE_BASE (MID_SERVICE_BASE+NR_SERVICE*4) - -#define MAC_LEN 6 /* atm.h */ - -#define MID_MIN_BUF_SIZE (1024) /* 1 kB is minimum */ -#define MID_MAX_BUF_SIZE (128*1024) /* 128 kB is maximum */ - -#define RX_DESCR_SIZE 1 /* RX PDU descr is 1 longword */ -#define TX_DESCR_SIZE 2 /* TX PDU descr is 2 longwords */ -#define AAL5_TRAILER (ATM_AAL5_TRAILER/4) /* AAL5 trailer is 2 longwords */ - -#define TX_GAP 8 /* TX buffer gap (words) */ - -/* - * Midway Reset/ID - * - * All values read-only. Writing to this register resets Midway chip. - */ - -#define MID_RES_ID_MCON 0x00 /* Midway Reset/ID */ - -#define MID_ID 0xf0000000 /* Midway version */ -#define MID_SHIFT 24 -#define MID_MOTHER_ID 0x00000700 /* mother board id */ -#define MID_MOTHER_SHIFT 8 -#define MID_CON_TI 0x00000080 /* 0: normal ctrl; 1: SABRE */ -#define MID_CON_SUNI 0x00000040 /* 0: UTOPIA; 1: SUNI */ -#define MID_CON_V6 0x00000020 /* 0: non-pipel UTOPIA (required iff - !CON_SUNI; 1: UTOPIA */ -#define DAUGHTER_ID 0x0000001f /* daughter board id */ - -/* - * Interrupt Status Acknowledge, Interrupt Status & Interrupt Enable - */ - -#define MID_ISA 0x01 /* Interrupt Status Acknowledge */ -#define MID_IS 0x02 /* Interrupt Status */ -#define MID_IE 0x03 /* Interrupt Enable */ - -#define MID_TX_COMPLETE_7 0x00010000 /* channel N completed a PDU */ -#define MID_TX_COMPLETE_6 0x00008000 /* transmission */ -#define MID_TX_COMPLETE_5 0x00004000 -#define MID_TX_COMPLETE_4 0x00002000 -#define MID_TX_COMPLETE_3 0x00001000 -#define MID_TX_COMPLETE_2 0x00000800 -#define MID_TX_COMPLETE_1 0x00000400 -#define MID_TX_COMPLETE_0 0x00000200 -#define MID_TX_COMPLETE 0x0001fe00 /* any TX */ -#define MID_TX_DMA_OVFL 0x00000100 /* DMA to adapter overflow */ -#define MID_TX_IDENT_MISM 0x00000080 /* TX: ident mismatch => halted */ -#define MID_DMA_LERR_ACK 0x00000040 /* LERR - SBus ? */ -#define MID_DMA_ERR_ACK 0x00000020 /* DMA error */ -#define MID_RX_DMA_COMPLETE 0x00000010 /* DMA to host done */ -#define MID_TX_DMA_COMPLETE 0x00000008 /* DMA from host done */ -#define MID_SERVICE 0x00000004 /* something in service list */ -#define MID_SUNI_INT 0x00000002 /* interrupt from SUNI */ -#define MID_STAT_OVFL 0x00000001 /* statistics overflow */ - -/* - * Master Control/Status - */ - -#define MID_MC_S 0x04 - -#define MID_INT_SELECT 0x000001C0 /* Interrupt level (000: off) */ -#define MID_INT_SEL_SHIFT 6 -#define MID_TX_LOCK_MODE 0x00000020 /* 0: streaming; 1: TX ovfl->lock */ -#define MID_DMA_ENABLE 0x00000010 /* R: 0: disable; 1: enable - W: 0: no change; 1: enable */ -#define MID_TX_ENABLE 0x00000008 /* R: 0: TX disabled; 1: enabled - W: 0: no change; 1: enable */ -#define MID_RX_ENABLE 0x00000004 /* like TX */ -#define MID_WAIT_1MS 0x00000002 /* R: 0: timer not running; 1: running - W: 0: no change; 1: no interrupts - for 1 ms */ -#define MID_WAIT_500US 0x00000001 /* like WAIT_1MS, but 0.5 ms */ - -/* - * Statistics - * - * Cleared when reading. - */ - -#define MID_STAT 0x05 - -#define MID_VCI_TRASH 0xFFFF0000 /* trashed cells because of VCI mode */ -#define MID_VCI_TRASH_SHIFT 16 -#define MID_OVFL_TRASH 0x0000FFFF /* trashed cells because of overflow */ - -/* - * Address registers - */ - -#define MID_SERV_WRITE 0x06 /* free pos in service area (R, 10 bits) */ -#define MID_DMA_ADDR 0x07 /* virtual DMA address (R, 32 bits) */ -#define MID_DMA_WR_RX 0x08 /* (RW, 9 bits) */ -#define MID_DMA_RD_RX 0x09 -#define MID_DMA_WR_TX 0x0A -#define MID_DMA_RD_TX 0x0B - -/* - * Transmit Place Registers (0x10+4*channel) - */ - -#define MID_TX_PLACE(c) (0x10+4*(c)) - -#define MID_SIZE 0x00003800 /* size, N*256 x 32 bit */ -#define MID_SIZE_SHIFT 11 -#define MID_LOCATION 0x000007FF /* location in adapter memory (word) */ - -#define MID_LOC_SKIP 8 /* 8 bits of location are always zero - (applies to all uses of location) */ - -/* - * Transmit ReadPtr Registers (0x11+4*channel) - */ - -#define MID_TX_RDPTR(c) (0x11+4*(c)) - -#define MID_READ_PTR 0x00007FFF /* next word for PHY */ - -/* - * Transmit DescrStart Registers (0x12+4*channel) - */ - -#define MID_TX_DESCRSTART(c) (0x12+4*(c)) - -#define MID_DESCR_START 0x00007FFF /* seg buffer being DMAed */ - -#define ENI155_MAGIC 0xa54b872d - -struct midway_eprom { - unsigned char mac[MAC_LEN],inv_mac[MAC_LEN]; - unsigned char pad[36]; - u32 serial,inv_serial; - u32 magic,inv_magic; -}; - - -/* - * VCI table entry - */ - -#define MID_VCI_IN_SERVICE 0x00000001 /* set if VCI is currently in - service list */ -#define MID_VCI_SIZE 0x00038000 /* reassembly buffer size, - 2* kB */ -#define MID_VCI_SIZE_SHIFT 15 -#define MID_VCI_LOCATION 0x1ffc0000 /* buffer location */ -#define MID_VCI_LOCATION_SHIFT 18 -#define MID_VCI_PTI_MODE 0x20000000 /* 0: trash, 1: preserve */ -#define MID_VCI_MODE 0xc0000000 -#define MID_VCI_MODE_SHIFT 30 -#define MID_VCI_READ 0x00007fff -#define MID_VCI_READ_SHIFT 0 -#define MID_VCI_DESCR 0x7fff0000 -#define MID_VCI_DESCR_SHIFT 16 -#define MID_VCI_COUNT 0x000007ff -#define MID_VCI_COUNT_SHIFT 0 -#define MID_VCI_STATE 0x0000c000 -#define MID_VCI_STATE_SHIFT 14 -#define MID_VCI_WRITE 0x7fff0000 -#define MID_VCI_WRITE_SHIFT 16 - -#define MID_MODE_TRASH 0 -#define MID_MODE_RAW 1 -#define MID_MODE_AAL5 2 - -/* - * Reassembly buffer descriptor - */ - -#define MID_RED_COUNT 0x000007ff -#define MID_RED_CRC_ERR 0x00000800 -#define MID_RED_T 0x00001000 -#define MID_RED_CE 0x00010000 -#define MID_RED_CLP 0x01000000 -#define MID_RED_IDEN 0xfe000000 -#define MID_RED_SHIFT 25 - -#define MID_RED_RX_ID 0x1b /* constant identifier */ - -/* - * Segmentation buffer descriptor - */ - -#define MID_SEG_COUNT MID_RED_COUNT -#define MID_SEG_RATE 0x01f80000 -#define MID_SEG_RATE_SHIFT 19 -#define MID_SEG_PR 0x06000000 -#define MID_SEG_PR_SHIFT 25 -#define MID_SEG_AAL5 0x08000000 -#define MID_SEG_ID 0xf0000000 -#define MID_SEG_ID_SHIFT 28 -#define MID_SEG_MAX_RATE 63 - -#define MID_SEG_CLP 0x00000001 -#define MID_SEG_PTI 0x0000000e -#define MID_SEG_PTI_SHIFT 1 -#define MID_SEG_VCI 0x00003ff0 -#define MID_SEG_VCI_SHIFT 4 - -#define MID_SEG_TX_ID 0xb /* constant identifier */ - -/* - * DMA entry - */ - -#define MID_DMA_COUNT 0xffff0000 -#define MID_DMA_COUNT_SHIFT 16 -#define MID_DMA_END 0x00000020 -#define MID_DMA_TYPE 0x0000000f - -#define MID_DT_JK 0x3 -#define MID_DT_WORD 0x0 -#define MID_DT_2W 0x7 -#define MID_DT_4W 0x4 -#define MID_DT_8W 0x5 -#define MID_DT_16W 0x6 -#define MID_DT_2WM 0xf -#define MID_DT_4WM 0xc -#define MID_DT_8WM 0xd -#define MID_DT_16WM 0xe - -/* only for RX*/ -#define MID_DMA_VCI 0x0000ffc0 -#define MID_DMA_VCI_SHIFT 6 - -/* only for TX */ -#define MID_DMA_CHAN 0x000001c0 -#define MID_DMA_CHAN_SHIFT 6 - -#define MID_DT_BYTE 0x1 -#define MID_DT_HWORD 0x2 - -#endif diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c deleted file mode 100644 index 24e51343df15..000000000000 --- a/drivers/atm/nicstar.c +++ /dev/null @@ -1,2759 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * nicstar.c - * - * Device driver supporting CBR for IDT 77201/77211 "NICStAR" based cards. - * - * IMPORTANT: The included file nicstarmac.c was NOT WRITTEN BY ME. - * It was taken from the frle-0.22 device driver. - * As the file doesn't have a copyright notice, in the file - * nicstarmac.copyright I put the copyright notice from the - * frle-0.22 device driver. - * Some code is based on the nicstar driver by M. Welsh. - * - * Author: Rui Prior (rprior@inescn.pt) - * PowerPC support by Jay Talbott (jay_talbott@mcg.mot.com) April 1999 - * - * - * (C) INESC 1999 - */ - -/* - * IMPORTANT INFORMATION - * - * There are currently three types of spinlocks: - * - * 1 - Per card interrupt spinlock (to protect structures and such) - * 2 - Per SCQ scq spinlock - * 3 - Per card resource spinlock (to access registers, etc.) - * - * These must NEVER be grabbed in reverse order. - * - */ - -/* Header files */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "nicstar.h" -#ifdef CONFIG_ATM_NICSTAR_USE_SUNI -#include "suni.h" -#endif /* CONFIG_ATM_NICSTAR_USE_SUNI */ -#ifdef CONFIG_ATM_NICSTAR_USE_IDT77105 -#include "idt77105.h" -#endif /* CONFIG_ATM_NICSTAR_USE_IDT77105 */ - -/* Additional code */ - -#include "nicstarmac.c" - -/* Configurable parameters */ - -#undef PHY_LOOPBACK -#undef TX_DEBUG -#undef RX_DEBUG -#undef GENERAL_DEBUG -#undef EXTRA_DEBUG - -/* Do not touch these */ - -#ifdef TX_DEBUG -#define TXPRINTK(args...) printk(args) -#else -#define TXPRINTK(args...) -#endif /* TX_DEBUG */ - -#ifdef RX_DEBUG -#define RXPRINTK(args...) printk(args) -#else -#define RXPRINTK(args...) -#endif /* RX_DEBUG */ - -#ifdef GENERAL_DEBUG -#define PRINTK(args...) printk(args) -#else -#define PRINTK(args...) do {} while (0) -#endif /* GENERAL_DEBUG */ - -#ifdef EXTRA_DEBUG -#define XPRINTK(args...) printk(args) -#else -#define XPRINTK(args...) -#endif /* EXTRA_DEBUG */ - -/* Macros */ - -#define CMD_BUSY(card) (readl((card)->membase + STAT) & NS_STAT_CMDBZ) - -#define NS_DELAY mdelay(1) - -#define PTR_DIFF(a, b) ((u32)((unsigned long)(a) - (unsigned long)(b))) - -#ifndef ATM_SKB -#define ATM_SKB(s) (&(s)->atm) -#endif - -#define scq_virt_to_bus(scq, p) \ - (scq->dma + ((unsigned long)(p) - (unsigned long)(scq)->org)) - -/* Function declarations */ - -static u32 ns_read_sram(ns_dev * card, u32 sram_address); -static void ns_write_sram(ns_dev * card, u32 sram_address, u32 * value, - int count); -static int ns_init_card(int i, struct pci_dev *pcidev); -static void ns_init_card_error(ns_dev * card, int error); -static scq_info *get_scq(ns_dev *card, int size, u32 scd); -static void free_scq(ns_dev *card, scq_info * scq, struct atm_vcc *vcc); -static void push_rxbufs(ns_dev *, struct sk_buff *); -static irqreturn_t ns_irq_handler(int irq, void *dev_id); -static int ns_open(struct atm_vcc *vcc); -static void ns_close(struct atm_vcc *vcc); -static void fill_tst(ns_dev * card, int n, vc_map * vc); -static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb); -static int ns_send_bh(struct atm_vcc *vcc, struct sk_buff *skb); -static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd, - struct sk_buff *skb, bool may_sleep); -static void process_tsq(ns_dev * card); -static void drain_scq(ns_dev * card, scq_info * scq, int pos); -static void process_rsq(ns_dev * card); -static void dequeue_rx(ns_dev * card, ns_rsqe * rsqe); -static void recycle_rx_buf(ns_dev * card, struct sk_buff *skb); -static void recycle_iovec_rx_bufs(ns_dev * card, struct iovec *iov, int count); -static void recycle_iov_buf(ns_dev * card, struct sk_buff *iovb); -static void dequeue_sm_buf(ns_dev * card, struct sk_buff *sb); -static void dequeue_lg_buf(ns_dev * card, struct sk_buff *lb); -static int ns_proc_read(struct atm_dev *dev, loff_t * pos, char *page); -static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg); -#ifdef EXTRA_DEBUG -static void which_list(ns_dev * card, struct sk_buff *skb); -#endif -static void ns_poll(struct timer_list *unused); -static void ns_phy_put(struct atm_dev *dev, unsigned char value, - unsigned long addr); -static unsigned char ns_phy_get(struct atm_dev *dev, unsigned long addr); - -/* Global variables */ - -static struct ns_dev *cards[NS_MAX_CARDS]; -static unsigned num_cards; -static const struct atmdev_ops atm_ops = { - .open = ns_open, - .close = ns_close, - .ioctl = ns_ioctl, - .send = ns_send, - .send_bh = ns_send_bh, - .phy_put = ns_phy_put, - .phy_get = ns_phy_get, - .proc_read = ns_proc_read, - .owner = THIS_MODULE, -}; - -static struct timer_list ns_timer; -static char *mac[NS_MAX_CARDS]; -module_param_array(mac, charp, NULL, 0); -MODULE_DESCRIPTION("ATM NIC driver for IDT 77201/77211 \"NICStAR\" and Fore ForeRunnerLE."); -MODULE_LICENSE("GPL"); - -/* Functions */ - -static int nicstar_init_one(struct pci_dev *pcidev, - const struct pci_device_id *ent) -{ - static int index = -1; - unsigned int error; - - index++; - cards[index] = NULL; - - error = ns_init_card(index, pcidev); - if (error) { - cards[index--] = NULL; /* don't increment index */ - goto err_out; - } - - return 0; -err_out: - return -ENODEV; -} - -static void nicstar_remove_one(struct pci_dev *pcidev) -{ - int i, j; - ns_dev *card = pci_get_drvdata(pcidev); - struct sk_buff *hb; - struct sk_buff *iovb; - struct sk_buff *lb; - struct sk_buff *sb; - - i = card->index; - - if (cards[i] == NULL) - return; - - if (card->atmdev->phy && card->atmdev->phy->stop) - card->atmdev->phy->stop(card->atmdev); - - /* Stop everything */ - writel(0x00000000, card->membase + CFG); - - /* De-register device */ - atm_dev_deregister(card->atmdev); - - /* Disable PCI device */ - pci_disable_device(pcidev); - - /* Free up resources */ - j = 0; - PRINTK("nicstar%d: freeing %d huge buffers.\n", i, card->hbpool.count); - while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL) { - dev_kfree_skb_any(hb); - j++; - } - PRINTK("nicstar%d: %d huge buffers freed.\n", i, j); - j = 0; - PRINTK("nicstar%d: freeing %d iovec buffers.\n", i, - card->iovpool.count); - while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL) { - dev_kfree_skb_any(iovb); - j++; - } - PRINTK("nicstar%d: %d iovec buffers freed.\n", i, j); - while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL) - dev_kfree_skb_any(lb); - while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL) - dev_kfree_skb_any(sb); - free_scq(card, card->scq0, NULL); - for (j = 0; j < NS_FRSCD_NUM; j++) { - if (card->scd2vc[j] != NULL) - free_scq(card, card->scd2vc[j]->scq, card->scd2vc[j]->tx_vcc); - } - idr_destroy(&card->idr); - dma_free_coherent(&card->pcidev->dev, NS_RSQSIZE + NS_RSQ_ALIGNMENT, - card->rsq.org, card->rsq.dma); - dma_free_coherent(&card->pcidev->dev, NS_TSQSIZE + NS_TSQ_ALIGNMENT, - card->tsq.org, card->tsq.dma); - free_irq(card->pcidev->irq, card); - iounmap(card->membase); - kfree(card); -} - -static const struct pci_device_id nicstar_pci_tbl[] = { - { PCI_VDEVICE(IDT, PCI_DEVICE_ID_IDT_IDT77201), 0 }, - {0,} /* terminate list */ -}; - -MODULE_DEVICE_TABLE(pci, nicstar_pci_tbl); - -static struct pci_driver nicstar_driver = { - .name = "nicstar", - .id_table = nicstar_pci_tbl, - .probe = nicstar_init_one, - .remove = nicstar_remove_one, -}; - -static int __init nicstar_init(void) -{ - unsigned error = 0; /* Initialized to remove compile warning */ - - XPRINTK("nicstar: nicstar_init() called.\n"); - - error = pci_register_driver(&nicstar_driver); - - TXPRINTK("nicstar: TX debug enabled.\n"); - RXPRINTK("nicstar: RX debug enabled.\n"); - PRINTK("nicstar: General debug enabled.\n"); -#ifdef PHY_LOOPBACK - printk("nicstar: using PHY loopback.\n"); -#endif /* PHY_LOOPBACK */ - XPRINTK("nicstar: nicstar_init() returned.\n"); - - if (!error) { - timer_setup(&ns_timer, ns_poll, 0); - ns_timer.expires = jiffies + NS_POLL_PERIOD; - add_timer(&ns_timer); - } - - return error; -} - -static void __exit nicstar_cleanup(void) -{ - XPRINTK("nicstar: nicstar_cleanup() called.\n"); - - timer_delete_sync(&ns_timer); - - pci_unregister_driver(&nicstar_driver); - - XPRINTK("nicstar: nicstar_cleanup() returned.\n"); -} - -static u32 ns_read_sram(ns_dev * card, u32 sram_address) -{ - unsigned long flags; - u32 data; - sram_address <<= 2; - sram_address &= 0x0007FFFC; /* address must be dword aligned */ - sram_address |= 0x50000000; /* SRAM read command */ - spin_lock_irqsave(&card->res_lock, flags); - while (CMD_BUSY(card)) ; - writel(sram_address, card->membase + CMD); - while (CMD_BUSY(card)) ; - data = readl(card->membase + DR0); - spin_unlock_irqrestore(&card->res_lock, flags); - return data; -} - -static void ns_write_sram(ns_dev * card, u32 sram_address, u32 * value, - int count) -{ - unsigned long flags; - int i, c; - count--; /* count range now is 0..3 instead of 1..4 */ - c = count; - c <<= 2; /* to use increments of 4 */ - spin_lock_irqsave(&card->res_lock, flags); - while (CMD_BUSY(card)) ; - for (i = 0; i <= c; i += 4) - writel(*(value++), card->membase + i); - /* Note: DR# registers are the first 4 dwords in nicstar's memspace, - so card->membase + DR0 == card->membase */ - sram_address <<= 2; - sram_address &= 0x0007FFFC; - sram_address |= (0x40000000 | count); - writel(sram_address, card->membase + CMD); - spin_unlock_irqrestore(&card->res_lock, flags); -} - -static int ns_init_card(int i, struct pci_dev *pcidev) -{ - int j; - struct ns_dev *card = NULL; - unsigned char pci_latency; - unsigned error; - u32 data; - u32 u32d[4]; - u32 ns_cfg_rctsize; - int bcount; - unsigned long membase; - - error = 0; - - if (pci_enable_device(pcidev)) { - printk("nicstar%d: can't enable PCI device\n", i); - error = 2; - ns_init_card_error(card, error); - return error; - } - if (dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32)) != 0) { - printk(KERN_WARNING - "nicstar%d: No suitable DMA available.\n", i); - error = 2; - ns_init_card_error(card, error); - return error; - } - - card = kmalloc_obj(*card); - if (!card) { - printk - ("nicstar%d: can't allocate memory for device structure.\n", - i); - error = 2; - ns_init_card_error(card, error); - return error; - } - cards[i] = card; - spin_lock_init(&card->int_lock); - spin_lock_init(&card->res_lock); - - pci_set_drvdata(pcidev, card); - - card->index = i; - card->atmdev = NULL; - card->pcidev = pcidev; - membase = pci_resource_start(pcidev, 1); - card->membase = ioremap(membase, NS_IOREMAP_SIZE); - if (!card->membase) { - printk("nicstar%d: can't ioremap() membase.\n", i); - error = 3; - ns_init_card_error(card, error); - return error; - } - PRINTK("nicstar%d: membase at 0x%p.\n", i, card->membase); - - pci_set_master(pcidev); - - if (pci_read_config_byte(pcidev, PCI_LATENCY_TIMER, &pci_latency) != 0) { - printk("nicstar%d: can't read PCI latency timer.\n", i); - error = 6; - ns_init_card_error(card, error); - return error; - } -#ifdef NS_PCI_LATENCY - if (pci_latency < NS_PCI_LATENCY) { - PRINTK("nicstar%d: setting PCI latency timer to %d.\n", i, - NS_PCI_LATENCY); - for (j = 1; j < 4; j++) { - if (pci_write_config_byte - (pcidev, PCI_LATENCY_TIMER, NS_PCI_LATENCY) != 0) - break; - } - if (j == 4) { - printk - ("nicstar%d: can't set PCI latency timer to %d.\n", - i, NS_PCI_LATENCY); - error = 7; - ns_init_card_error(card, error); - return error; - } - } -#endif /* NS_PCI_LATENCY */ - - /* Clear timer overflow */ - data = readl(card->membase + STAT); - if (data & NS_STAT_TMROF) - writel(NS_STAT_TMROF, card->membase + STAT); - - /* Software reset */ - writel(NS_CFG_SWRST, card->membase + CFG); - NS_DELAY; - writel(0x00000000, card->membase + CFG); - - /* PHY reset */ - writel(0x00000008, card->membase + GP); - NS_DELAY; - writel(0x00000001, card->membase + GP); - NS_DELAY; - while (CMD_BUSY(card)) ; - writel(NS_CMD_WRITE_UTILITY | 0x00000100, card->membase + CMD); /* Sync UTOPIA with SAR clock */ - NS_DELAY; - - /* Detect PHY type */ - while (CMD_BUSY(card)) ; - writel(NS_CMD_READ_UTILITY | 0x00000200, card->membase + CMD); - while (CMD_BUSY(card)) ; - data = readl(card->membase + DR0); - switch (data) { - case 0x00000009: - printk("nicstar%d: PHY seems to be 25 Mbps.\n", i); - card->max_pcr = ATM_25_PCR; - while (CMD_BUSY(card)) ; - writel(0x00000008, card->membase + DR0); - writel(NS_CMD_WRITE_UTILITY | 0x00000200, card->membase + CMD); - /* Clear an eventual pending interrupt */ - writel(NS_STAT_SFBQF, card->membase + STAT); -#ifdef PHY_LOOPBACK - while (CMD_BUSY(card)) ; - writel(0x00000022, card->membase + DR0); - writel(NS_CMD_WRITE_UTILITY | 0x00000202, card->membase + CMD); -#endif /* PHY_LOOPBACK */ - break; - case 0x00000030: - case 0x00000031: - printk("nicstar%d: PHY seems to be 155 Mbps.\n", i); - card->max_pcr = ATM_OC3_PCR; -#ifdef PHY_LOOPBACK - while (CMD_BUSY(card)) ; - writel(0x00000002, card->membase + DR0); - writel(NS_CMD_WRITE_UTILITY | 0x00000205, card->membase + CMD); -#endif /* PHY_LOOPBACK */ - break; - default: - printk("nicstar%d: unknown PHY type (0x%08X).\n", i, data); - error = 8; - ns_init_card_error(card, error); - return error; - } - writel(0x00000000, card->membase + GP); - - /* Determine SRAM size */ - data = 0x76543210; - ns_write_sram(card, 0x1C003, &data, 1); - data = 0x89ABCDEF; - ns_write_sram(card, 0x14003, &data, 1); - if (ns_read_sram(card, 0x14003) == 0x89ABCDEF && - ns_read_sram(card, 0x1C003) == 0x76543210) - card->sram_size = 128; - else - card->sram_size = 32; - PRINTK("nicstar%d: %dK x 32bit SRAM size.\n", i, card->sram_size); - - card->rct_size = NS_MAX_RCTSIZE; - -#if (NS_MAX_RCTSIZE == 4096) - if (card->sram_size == 128) - printk - ("nicstar%d: limiting maximum VCI. See NS_MAX_RCTSIZE in nicstar.h\n", - i); -#elif (NS_MAX_RCTSIZE == 16384) - if (card->sram_size == 32) { - printk - ("nicstar%d: wasting memory. See NS_MAX_RCTSIZE in nicstar.h\n", - i); - card->rct_size = 4096; - } -#else -#error NS_MAX_RCTSIZE must be either 4096 or 16384 in nicstar.c -#endif - - card->vpibits = NS_VPIBITS; - if (card->rct_size == 4096) - card->vcibits = 12 - NS_VPIBITS; - else /* card->rct_size == 16384 */ - card->vcibits = 14 - NS_VPIBITS; - - /* Initialize the nicstar eeprom/eprom stuff, for the MAC addr */ - if (mac[i] == NULL) - nicstar_init_eprom(card->membase); - - /* Set the VPI/VCI MSb mask to zero so we can receive OAM cells */ - writel(0x00000000, card->membase + VPM); - - card->intcnt = 0; - if (request_irq - (pcidev->irq, &ns_irq_handler, IRQF_SHARED, "nicstar", card) != 0) { - pr_err("nicstar%d: can't allocate IRQ %d.\n", i, pcidev->irq); - error = 9; - ns_init_card_error(card, error); - return error; - } - - /* Initialize TSQ */ - card->tsq.org = dma_alloc_coherent(&card->pcidev->dev, - NS_TSQSIZE + NS_TSQ_ALIGNMENT, - &card->tsq.dma, GFP_KERNEL); - if (card->tsq.org == NULL) { - printk("nicstar%d: can't allocate TSQ.\n", i); - error = 10; - ns_init_card_error(card, error); - return error; - } - card->tsq.base = PTR_ALIGN(card->tsq.org, NS_TSQ_ALIGNMENT); - card->tsq.next = card->tsq.base; - card->tsq.last = card->tsq.base + (NS_TSQ_NUM_ENTRIES - 1); - for (j = 0; j < NS_TSQ_NUM_ENTRIES; j++) - ns_tsi_init(card->tsq.base + j); - writel(0x00000000, card->membase + TSQH); - writel(ALIGN(card->tsq.dma, NS_TSQ_ALIGNMENT), card->membase + TSQB); - PRINTK("nicstar%d: TSQ base at 0x%p.\n", i, card->tsq.base); - - /* Initialize RSQ */ - card->rsq.org = dma_alloc_coherent(&card->pcidev->dev, - NS_RSQSIZE + NS_RSQ_ALIGNMENT, - &card->rsq.dma, GFP_KERNEL); - if (card->rsq.org == NULL) { - printk("nicstar%d: can't allocate RSQ.\n", i); - error = 11; - ns_init_card_error(card, error); - return error; - } - card->rsq.base = PTR_ALIGN(card->rsq.org, NS_RSQ_ALIGNMENT); - card->rsq.next = card->rsq.base; - card->rsq.last = card->rsq.base + (NS_RSQ_NUM_ENTRIES - 1); - for (j = 0; j < NS_RSQ_NUM_ENTRIES; j++) - ns_rsqe_init(card->rsq.base + j); - writel(0x00000000, card->membase + RSQH); - writel(ALIGN(card->rsq.dma, NS_RSQ_ALIGNMENT), card->membase + RSQB); - PRINTK("nicstar%d: RSQ base at 0x%p.\n", i, card->rsq.base); - - /* Initialize SCQ0, the only VBR SCQ used */ - card->scq1 = NULL; - card->scq2 = NULL; - card->scq0 = get_scq(card, VBR_SCQSIZE, NS_VRSCD0); - if (card->scq0 == NULL) { - printk("nicstar%d: can't get SCQ0.\n", i); - error = 12; - ns_init_card_error(card, error); - return error; - } - u32d[0] = scq_virt_to_bus(card->scq0, card->scq0->base); - u32d[1] = (u32) 0x00000000; - u32d[2] = (u32) 0xffffffff; - u32d[3] = (u32) 0x00000000; - ns_write_sram(card, NS_VRSCD0, u32d, 4); - ns_write_sram(card, NS_VRSCD1, u32d, 4); /* These last two won't be used */ - ns_write_sram(card, NS_VRSCD2, u32d, 4); /* but are initialized, just in case... */ - card->scq0->scd = NS_VRSCD0; - PRINTK("nicstar%d: VBR-SCQ0 base at 0x%p.\n", i, card->scq0->base); - - /* Initialize TSTs */ - card->tst_addr = NS_TST0; - card->tst_free_entries = NS_TST_NUM_ENTRIES; - data = NS_TST_OPCODE_VARIABLE; - for (j = 0; j < NS_TST_NUM_ENTRIES; j++) - ns_write_sram(card, NS_TST0 + j, &data, 1); - data = ns_tste_make(NS_TST_OPCODE_END, NS_TST0); - ns_write_sram(card, NS_TST0 + NS_TST_NUM_ENTRIES, &data, 1); - for (j = 0; j < NS_TST_NUM_ENTRIES; j++) - ns_write_sram(card, NS_TST1 + j, &data, 1); - data = ns_tste_make(NS_TST_OPCODE_END, NS_TST1); - ns_write_sram(card, NS_TST1 + NS_TST_NUM_ENTRIES, &data, 1); - for (j = 0; j < NS_TST_NUM_ENTRIES; j++) - card->tste2vc[j] = NULL; - writel(NS_TST0 << 2, card->membase + TSTB); - - /* Initialize RCT. AAL type is set on opening the VC. */ -#ifdef RCQ_SUPPORT - u32d[0] = NS_RCTE_RAWCELLINTEN; -#else - u32d[0] = 0x00000000; -#endif /* RCQ_SUPPORT */ - u32d[1] = 0x00000000; - u32d[2] = 0x00000000; - u32d[3] = 0xFFFFFFFF; - for (j = 0; j < card->rct_size; j++) - ns_write_sram(card, j * 4, u32d, 4); - - memset(card->vcmap, 0, sizeof(card->vcmap)); - - for (j = 0; j < NS_FRSCD_NUM; j++) - card->scd2vc[j] = NULL; - - /* Initialize buffer levels */ - card->sbnr.min = MIN_SB; - card->sbnr.init = NUM_SB; - card->sbnr.max = MAX_SB; - card->lbnr.min = MIN_LB; - card->lbnr.init = NUM_LB; - card->lbnr.max = MAX_LB; - card->iovnr.min = MIN_IOVB; - card->iovnr.init = NUM_IOVB; - card->iovnr.max = MAX_IOVB; - card->hbnr.min = MIN_HB; - card->hbnr.init = NUM_HB; - card->hbnr.max = MAX_HB; - - card->sm_handle = NULL; - card->sm_addr = 0x00000000; - card->lg_handle = NULL; - card->lg_addr = 0x00000000; - - card->efbie = 1; /* To prevent push_rxbufs from enabling the interrupt */ - - idr_init(&card->idr); - - /* Pre-allocate some huge buffers */ - skb_queue_head_init(&card->hbpool.queue); - card->hbpool.count = 0; - for (j = 0; j < NUM_HB; j++) { - struct sk_buff *hb; - hb = __dev_alloc_skb(NS_HBUFSIZE, GFP_KERNEL); - if (hb == NULL) { - printk - ("nicstar%d: can't allocate %dth of %d huge buffers.\n", - i, j, NUM_HB); - error = 13; - ns_init_card_error(card, error); - return error; - } - NS_PRV_BUFTYPE(hb) = BUF_NONE; - skb_queue_tail(&card->hbpool.queue, hb); - card->hbpool.count++; - } - - /* Allocate large buffers */ - skb_queue_head_init(&card->lbpool.queue); - card->lbpool.count = 0; /* Not used */ - for (j = 0; j < NUM_LB; j++) { - struct sk_buff *lb; - lb = __dev_alloc_skb(NS_LGSKBSIZE, GFP_KERNEL); - if (lb == NULL) { - printk - ("nicstar%d: can't allocate %dth of %d large buffers.\n", - i, j, NUM_LB); - error = 14; - ns_init_card_error(card, error); - return error; - } - NS_PRV_BUFTYPE(lb) = BUF_LG; - skb_queue_tail(&card->lbpool.queue, lb); - skb_reserve(lb, NS_SMBUFSIZE); - push_rxbufs(card, lb); - /* Due to the implementation of push_rxbufs() this is 1, not 0 */ - if (j == 1) { - card->rcbuf = lb; - card->rawcell = (struct ns_rcqe *) lb->data; - card->rawch = NS_PRV_DMA(lb); - } - } - /* Test for strange behaviour which leads to crashes */ - if ((bcount = - ns_stat_lfbqc_get(readl(card->membase + STAT))) < card->lbnr.min) { - printk - ("nicstar%d: Strange... Just allocated %d large buffers and lfbqc = %d.\n", - i, j, bcount); - error = 14; - ns_init_card_error(card, error); - return error; - } - - /* Allocate small buffers */ - skb_queue_head_init(&card->sbpool.queue); - card->sbpool.count = 0; /* Not used */ - for (j = 0; j < NUM_SB; j++) { - struct sk_buff *sb; - sb = __dev_alloc_skb(NS_SMSKBSIZE, GFP_KERNEL); - if (sb == NULL) { - printk - ("nicstar%d: can't allocate %dth of %d small buffers.\n", - i, j, NUM_SB); - error = 15; - ns_init_card_error(card, error); - return error; - } - NS_PRV_BUFTYPE(sb) = BUF_SM; - skb_queue_tail(&card->sbpool.queue, sb); - skb_reserve(sb, NS_AAL0_HEADER); - push_rxbufs(card, sb); - } - /* Test for strange behaviour which leads to crashes */ - if ((bcount = - ns_stat_sfbqc_get(readl(card->membase + STAT))) < card->sbnr.min) { - printk - ("nicstar%d: Strange... Just allocated %d small buffers and sfbqc = %d.\n", - i, j, bcount); - error = 15; - ns_init_card_error(card, error); - return error; - } - - /* Allocate iovec buffers */ - skb_queue_head_init(&card->iovpool.queue); - card->iovpool.count = 0; - for (j = 0; j < NUM_IOVB; j++) { - struct sk_buff *iovb; - iovb = alloc_skb(NS_IOVBUFSIZE, GFP_KERNEL); - if (iovb == NULL) { - printk - ("nicstar%d: can't allocate %dth of %d iovec buffers.\n", - i, j, NUM_IOVB); - error = 16; - ns_init_card_error(card, error); - return error; - } - NS_PRV_BUFTYPE(iovb) = BUF_NONE; - skb_queue_tail(&card->iovpool.queue, iovb); - card->iovpool.count++; - } - - /* Configure NICStAR */ - if (card->rct_size == 4096) - ns_cfg_rctsize = NS_CFG_RCTSIZE_4096_ENTRIES; - else /* (card->rct_size == 16384) */ - ns_cfg_rctsize = NS_CFG_RCTSIZE_16384_ENTRIES; - - card->efbie = 1; - - /* Register device */ - card->atmdev = atm_dev_register("nicstar", &card->pcidev->dev, &atm_ops, - -1, NULL); - if (card->atmdev == NULL) { - printk("nicstar%d: can't register device.\n", i); - error = 17; - ns_init_card_error(card, error); - return error; - } - - if (mac[i] == NULL || !mac_pton(mac[i], card->atmdev->esi)) { - nicstar_read_eprom(card->membase, NICSTAR_EPROM_MAC_ADDR_OFFSET, - card->atmdev->esi, 6); - if (ether_addr_equal(card->atmdev->esi, "\x00\x00\x00\x00\x00\x00")) { - nicstar_read_eprom(card->membase, - NICSTAR_EPROM_MAC_ADDR_OFFSET_ALT, - card->atmdev->esi, 6); - } - } - - printk("nicstar%d: MAC address %pM\n", i, card->atmdev->esi); - - card->atmdev->dev_data = card; - card->atmdev->ci_range.vpi_bits = card->vpibits; - card->atmdev->ci_range.vci_bits = card->vcibits; - card->atmdev->link_rate = card->max_pcr; - card->atmdev->phy = NULL; - -#ifdef CONFIG_ATM_NICSTAR_USE_SUNI - if (card->max_pcr == ATM_OC3_PCR) - suni_init(card->atmdev); -#endif /* CONFIG_ATM_NICSTAR_USE_SUNI */ - -#ifdef CONFIG_ATM_NICSTAR_USE_IDT77105 - if (card->max_pcr == ATM_25_PCR) - idt77105_init(card->atmdev); -#endif /* CONFIG_ATM_NICSTAR_USE_IDT77105 */ - - if (card->atmdev->phy && card->atmdev->phy->start) - card->atmdev->phy->start(card->atmdev); - - writel(NS_CFG_RXPATH | NS_CFG_SMBUFSIZE | NS_CFG_LGBUFSIZE | NS_CFG_EFBIE | NS_CFG_RSQSIZE | NS_CFG_VPIBITS | ns_cfg_rctsize | NS_CFG_RXINT_NODELAY | NS_CFG_RAWIE | /* Only enabled if RCQ_SUPPORT */ - NS_CFG_RSQAFIE | NS_CFG_TXEN | NS_CFG_TXIE | NS_CFG_TSQFIE_OPT | /* Only enabled if ENABLE_TSQFIE */ - NS_CFG_PHYIE, card->membase + CFG); - - num_cards++; - - return error; -} - -static void ns_init_card_error(ns_dev *card, int error) -{ - if (error >= 17) { - writel(0x00000000, card->membase + CFG); - } - if (error >= 16) { - struct sk_buff *iovb; - while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL) - dev_kfree_skb_any(iovb); - } - if (error >= 15) { - struct sk_buff *sb; - while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL) - dev_kfree_skb_any(sb); - free_scq(card, card->scq0, NULL); - } - if (error >= 14) { - struct sk_buff *lb; - while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL) - dev_kfree_skb_any(lb); - } - if (error >= 13) { - struct sk_buff *hb; - while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL) - dev_kfree_skb_any(hb); - } - if (error >= 12) { - dma_free_coherent(&card->pcidev->dev, NS_RSQSIZE + NS_RSQ_ALIGNMENT, - card->rsq.org, card->rsq.dma); - } - if (error >= 11) { - dma_free_coherent(&card->pcidev->dev, NS_TSQSIZE + NS_TSQ_ALIGNMENT, - card->tsq.org, card->tsq.dma); - } - if (error >= 10) { - free_irq(card->pcidev->irq, card); - } - if (error >= 4) { - iounmap(card->membase); - } - if (error >= 3) { - pci_disable_device(card->pcidev); - kfree(card); - } -} - -static scq_info *get_scq(ns_dev *card, int size, u32 scd) -{ - scq_info *scq; - - if (size != VBR_SCQSIZE && size != CBR_SCQSIZE) - return NULL; - - scq = kmalloc_obj(*scq); - if (!scq) - return NULL; - scq->org = dma_alloc_coherent(&card->pcidev->dev, - 2 * size, &scq->dma, GFP_KERNEL); - if (!scq->org) { - kfree(scq); - return NULL; - } - scq->skb = kzalloc_objs(*scq->skb, size / NS_SCQE_SIZE); - if (!scq->skb) { - dma_free_coherent(&card->pcidev->dev, - 2 * size, scq->org, scq->dma); - kfree(scq); - return NULL; - } - scq->num_entries = size / NS_SCQE_SIZE; - scq->base = PTR_ALIGN(scq->org, size); - scq->next = scq->base; - scq->last = scq->base + (scq->num_entries - 1); - scq->tail = scq->last; - scq->scd = scd; - scq->tbd_count = 0; - init_waitqueue_head(&scq->scqfull_waitq); - scq->full = 0; - spin_lock_init(&scq->lock); - - return scq; -} - -/* For variable rate SCQ vcc must be NULL */ -static void free_scq(ns_dev *card, scq_info *scq, struct atm_vcc *vcc) -{ - int i; - - if (scq->num_entries == VBR_SCQ_NUM_ENTRIES) - for (i = 0; i < scq->num_entries; i++) { - if (scq->skb[i] != NULL) { - vcc = ATM_SKB(scq->skb[i])->vcc; - if (vcc->pop != NULL) - vcc->pop(vcc, scq->skb[i]); - else - dev_kfree_skb_any(scq->skb[i]); - } - } else { /* vcc must be != NULL */ - - if (vcc == NULL) { - printk - ("nicstar: free_scq() called with vcc == NULL for fixed rate scq."); - for (i = 0; i < scq->num_entries; i++) - dev_kfree_skb_any(scq->skb[i]); - } else - for (i = 0; i < scq->num_entries; i++) { - if (scq->skb[i] != NULL) { - if (vcc->pop != NULL) - vcc->pop(vcc, scq->skb[i]); - else - dev_kfree_skb_any(scq->skb[i]); - } - } - } - kfree(scq->skb); - dma_free_coherent(&card->pcidev->dev, - 2 * (scq->num_entries == VBR_SCQ_NUM_ENTRIES ? - VBR_SCQSIZE : CBR_SCQSIZE), - scq->org, scq->dma); - kfree(scq); -} - -/* The handles passed must be pointers to the sk_buff containing the small - or large buffer(s) cast to u32. */ -static void push_rxbufs(ns_dev * card, struct sk_buff *skb) -{ - struct sk_buff *handle1, *handle2; - int id1, id2; - u32 addr1, addr2; - u32 stat; - unsigned long flags; - - /* *BARF* */ - handle2 = NULL; - addr2 = 0; - handle1 = skb; - addr1 = dma_map_single(&card->pcidev->dev, - skb->data, - (NS_PRV_BUFTYPE(skb) == BUF_SM - ? NS_SMSKBSIZE : NS_LGSKBSIZE), - DMA_TO_DEVICE); - NS_PRV_DMA(skb) = addr1; /* save so we can unmap later */ - -#ifdef GENERAL_DEBUG - if (!addr1) - printk("nicstar%d: push_rxbufs called with addr1 = 0.\n", - card->index); -#endif /* GENERAL_DEBUG */ - - stat = readl(card->membase + STAT); - card->sbfqc = ns_stat_sfbqc_get(stat); - card->lbfqc = ns_stat_lfbqc_get(stat); - if (NS_PRV_BUFTYPE(skb) == BUF_SM) { - if (!addr2) { - if (card->sm_addr) { - addr2 = card->sm_addr; - handle2 = card->sm_handle; - card->sm_addr = 0x00000000; - card->sm_handle = NULL; - } else { /* (!sm_addr) */ - - card->sm_addr = addr1; - card->sm_handle = handle1; - } - } - } else { /* buf_type == BUF_LG */ - - if (!addr2) { - if (card->lg_addr) { - addr2 = card->lg_addr; - handle2 = card->lg_handle; - card->lg_addr = 0x00000000; - card->lg_handle = NULL; - } else { /* (!lg_addr) */ - - card->lg_addr = addr1; - card->lg_handle = handle1; - } - } - } - - if (addr2) { - if (NS_PRV_BUFTYPE(skb) == BUF_SM) { - if (card->sbfqc >= card->sbnr.max) { - skb_unlink(handle1, &card->sbpool.queue); - dev_kfree_skb_any(handle1); - skb_unlink(handle2, &card->sbpool.queue); - dev_kfree_skb_any(handle2); - return; - } else - card->sbfqc += 2; - } else { /* (buf_type == BUF_LG) */ - - if (card->lbfqc >= card->lbnr.max) { - skb_unlink(handle1, &card->lbpool.queue); - dev_kfree_skb_any(handle1); - skb_unlink(handle2, &card->lbpool.queue); - dev_kfree_skb_any(handle2); - return; - } else - card->lbfqc += 2; - } - - id1 = idr_alloc(&card->idr, handle1, 0, 0, GFP_ATOMIC); - if (id1 < 0) - goto out; - - id2 = idr_alloc(&card->idr, handle2, 0, 0, GFP_ATOMIC); - if (id2 < 0) - goto out; - - spin_lock_irqsave(&card->res_lock, flags); - while (CMD_BUSY(card)) ; - writel(addr2, card->membase + DR3); - writel(id2, card->membase + DR2); - writel(addr1, card->membase + DR1); - writel(id1, card->membase + DR0); - writel(NS_CMD_WRITE_FREEBUFQ | NS_PRV_BUFTYPE(skb), - card->membase + CMD); - spin_unlock_irqrestore(&card->res_lock, flags); - - XPRINTK("nicstar%d: Pushing %s buffers at 0x%x and 0x%x.\n", - card->index, - (NS_PRV_BUFTYPE(skb) == BUF_SM ? "small" : "large"), - addr1, addr2); - } - - if (!card->efbie && card->sbfqc >= card->sbnr.min && - card->lbfqc >= card->lbnr.min) { - card->efbie = 1; - writel((readl(card->membase + CFG) | NS_CFG_EFBIE), - card->membase + CFG); - } - -out: - return; -} - -static irqreturn_t ns_irq_handler(int irq, void *dev_id) -{ - u32 stat_r; - ns_dev *card; - struct atm_dev *dev; - unsigned long flags; - - card = (ns_dev *) dev_id; - dev = card->atmdev; - card->intcnt++; - - PRINTK("nicstar%d: NICStAR generated an interrupt\n", card->index); - - spin_lock_irqsave(&card->int_lock, flags); - - stat_r = readl(card->membase + STAT); - - /* Transmit Status Indicator has been written to T. S. Queue */ - if (stat_r & NS_STAT_TSIF) { - TXPRINTK("nicstar%d: TSI interrupt\n", card->index); - process_tsq(card); - writel(NS_STAT_TSIF, card->membase + STAT); - } - - /* Incomplete CS-PDU has been transmitted */ - if (stat_r & NS_STAT_TXICP) { - writel(NS_STAT_TXICP, card->membase + STAT); - TXPRINTK("nicstar%d: Incomplete CS-PDU transmitted.\n", - card->index); - } - - /* Transmit Status Queue 7/8 full */ - if (stat_r & NS_STAT_TSQF) { - writel(NS_STAT_TSQF, card->membase + STAT); - PRINTK("nicstar%d: TSQ full.\n", card->index); - process_tsq(card); - } - - /* Timer overflow */ - if (stat_r & NS_STAT_TMROF) { - writel(NS_STAT_TMROF, card->membase + STAT); - PRINTK("nicstar%d: Timer overflow.\n", card->index); - } - - /* PHY device interrupt signal active */ - if (stat_r & NS_STAT_PHYI) { - writel(NS_STAT_PHYI, card->membase + STAT); - PRINTK("nicstar%d: PHY interrupt.\n", card->index); - if (dev->phy && dev->phy->interrupt) { - dev->phy->interrupt(dev); - } - } - - /* Small Buffer Queue is full */ - if (stat_r & NS_STAT_SFBQF) { - writel(NS_STAT_SFBQF, card->membase + STAT); - printk("nicstar%d: Small free buffer queue is full.\n", - card->index); - } - - /* Large Buffer Queue is full */ - if (stat_r & NS_STAT_LFBQF) { - writel(NS_STAT_LFBQF, card->membase + STAT); - printk("nicstar%d: Large free buffer queue is full.\n", - card->index); - } - - /* Receive Status Queue is full */ - if (stat_r & NS_STAT_RSQF) { - writel(NS_STAT_RSQF, card->membase + STAT); - printk("nicstar%d: RSQ full.\n", card->index); - process_rsq(card); - } - - /* Complete CS-PDU received */ - if (stat_r & NS_STAT_EOPDU) { - RXPRINTK("nicstar%d: End of CS-PDU received.\n", card->index); - process_rsq(card); - writel(NS_STAT_EOPDU, card->membase + STAT); - } - - /* Raw cell received */ - if (stat_r & NS_STAT_RAWCF) { - writel(NS_STAT_RAWCF, card->membase + STAT); -#ifndef RCQ_SUPPORT - printk("nicstar%d: Raw cell received and no support yet...\n", - card->index); -#endif /* RCQ_SUPPORT */ - /* NOTE: the following procedure may keep a raw cell pending until the - next interrupt. As this preliminary support is only meant to - avoid buffer leakage, this is not an issue. */ - while (readl(card->membase + RAWCT) != card->rawch) { - - if (ns_rcqe_islast(card->rawcell)) { - struct sk_buff *oldbuf; - - oldbuf = card->rcbuf; - card->rcbuf = idr_find(&card->idr, - ns_rcqe_nextbufhandle(card->rawcell)); - card->rawch = NS_PRV_DMA(card->rcbuf); - card->rawcell = (struct ns_rcqe *) - card->rcbuf->data; - recycle_rx_buf(card, oldbuf); - } else { - card->rawch += NS_RCQE_SIZE; - card->rawcell++; - } - } - } - - /* Small buffer queue is empty */ - if (stat_r & NS_STAT_SFBQE) { - int i; - struct sk_buff *sb; - - writel(NS_STAT_SFBQE, card->membase + STAT); - printk("nicstar%d: Small free buffer queue empty.\n", - card->index); - for (i = 0; i < card->sbnr.min; i++) { - sb = dev_alloc_skb(NS_SMSKBSIZE); - if (sb == NULL) { - writel(readl(card->membase + CFG) & - ~NS_CFG_EFBIE, card->membase + CFG); - card->efbie = 0; - break; - } - NS_PRV_BUFTYPE(sb) = BUF_SM; - skb_queue_tail(&card->sbpool.queue, sb); - skb_reserve(sb, NS_AAL0_HEADER); - push_rxbufs(card, sb); - } - card->sbfqc = i; - process_rsq(card); - } - - /* Large buffer queue empty */ - if (stat_r & NS_STAT_LFBQE) { - int i; - struct sk_buff *lb; - - writel(NS_STAT_LFBQE, card->membase + STAT); - printk("nicstar%d: Large free buffer queue empty.\n", - card->index); - for (i = 0; i < card->lbnr.min; i++) { - lb = dev_alloc_skb(NS_LGSKBSIZE); - if (lb == NULL) { - writel(readl(card->membase + CFG) & - ~NS_CFG_EFBIE, card->membase + CFG); - card->efbie = 0; - break; - } - NS_PRV_BUFTYPE(lb) = BUF_LG; - skb_queue_tail(&card->lbpool.queue, lb); - skb_reserve(lb, NS_SMBUFSIZE); - push_rxbufs(card, lb); - } - card->lbfqc = i; - process_rsq(card); - } - - /* Receive Status Queue is 7/8 full */ - if (stat_r & NS_STAT_RSQAF) { - writel(NS_STAT_RSQAF, card->membase + STAT); - RXPRINTK("nicstar%d: RSQ almost full.\n", card->index); - process_rsq(card); - } - - spin_unlock_irqrestore(&card->int_lock, flags); - PRINTK("nicstar%d: end of interrupt service\n", card->index); - return IRQ_HANDLED; -} - -static int ns_open(struct atm_vcc *vcc) -{ - ns_dev *card; - vc_map *vc; - unsigned long tmpl, modl; - int tcr, tcra; /* target cell rate, and absolute value */ - int n = 0; /* Number of entries in the TST. Initialized to remove - the compiler warning. */ - u32 u32d[4]; - int frscdi = 0; /* Index of the SCD. Initialized to remove the compiler - warning. How I wish compilers were clever enough to - tell which variables can truly be used - uninitialized... */ - int inuse; /* tx or rx vc already in use by another vcc */ - short vpi = vcc->vpi; - int vci = vcc->vci; - - card = (ns_dev *) vcc->dev->dev_data; - PRINTK("nicstar%d: opening vpi.vci %d.%d \n", card->index, (int)vpi, - vci); - if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0) { - PRINTK("nicstar%d: unsupported AAL.\n", card->index); - return -EINVAL; - } - - vc = &(card->vcmap[vpi << card->vcibits | vci]); - vcc->dev_data = vc; - - inuse = 0; - if (vcc->qos.txtp.traffic_class != ATM_NONE && vc->tx) - inuse = 1; - if (vcc->qos.rxtp.traffic_class != ATM_NONE && vc->rx) - inuse += 2; - if (inuse) { - printk("nicstar%d: %s vci already in use.\n", card->index, - inuse == 1 ? "tx" : inuse == 2 ? "rx" : "tx and rx"); - return -EINVAL; - } - - set_bit(ATM_VF_ADDR, &vcc->flags); - - /* NOTE: You are not allowed to modify an open connection's QOS. To change - that, remove the ATM_VF_PARTIAL flag checking. There may be other changes - needed to do that. */ - if (!test_bit(ATM_VF_PARTIAL, &vcc->flags)) { - scq_info *scq; - - set_bit(ATM_VF_PARTIAL, &vcc->flags); - if (vcc->qos.txtp.traffic_class == ATM_CBR) { - /* Check requested cell rate and availability of SCD */ - if (vcc->qos.txtp.max_pcr == 0 && vcc->qos.txtp.pcr == 0 - && vcc->qos.txtp.min_pcr == 0) { - PRINTK - ("nicstar%d: trying to open a CBR vc with cell rate = 0 \n", - card->index); - clear_bit(ATM_VF_PARTIAL, &vcc->flags); - clear_bit(ATM_VF_ADDR, &vcc->flags); - return -EINVAL; - } - - tcr = atm_pcr_goal(&(vcc->qos.txtp)); - tcra = tcr >= 0 ? tcr : -tcr; - - PRINTK("nicstar%d: target cell rate = %d.\n", - card->index, vcc->qos.txtp.max_pcr); - - tmpl = - (unsigned long)tcra *(unsigned long) - NS_TST_NUM_ENTRIES; - modl = tmpl % card->max_pcr; - - n = (int)(tmpl / card->max_pcr); - if (tcr > 0) { - if (modl > 0) - n++; - } else if (tcr == 0) { - if ((n = - (card->tst_free_entries - - NS_TST_RESERVED)) <= 0) { - PRINTK - ("nicstar%d: no CBR bandwidth free.\n", - card->index); - clear_bit(ATM_VF_PARTIAL, &vcc->flags); - clear_bit(ATM_VF_ADDR, &vcc->flags); - return -EINVAL; - } - } - - if (n == 0) { - printk - ("nicstar%d: selected bandwidth < granularity.\n", - card->index); - clear_bit(ATM_VF_PARTIAL, &vcc->flags); - clear_bit(ATM_VF_ADDR, &vcc->flags); - return -EINVAL; - } - - if (n > (card->tst_free_entries - NS_TST_RESERVED)) { - PRINTK - ("nicstar%d: not enough free CBR bandwidth.\n", - card->index); - clear_bit(ATM_VF_PARTIAL, &vcc->flags); - clear_bit(ATM_VF_ADDR, &vcc->flags); - return -EINVAL; - } else - card->tst_free_entries -= n; - - XPRINTK("nicstar%d: writing %d tst entries.\n", - card->index, n); - for (frscdi = 0; frscdi < NS_FRSCD_NUM; frscdi++) { - if (card->scd2vc[frscdi] == NULL) { - card->scd2vc[frscdi] = vc; - break; - } - } - if (frscdi == NS_FRSCD_NUM) { - PRINTK - ("nicstar%d: no SCD available for CBR channel.\n", - card->index); - card->tst_free_entries += n; - clear_bit(ATM_VF_PARTIAL, &vcc->flags); - clear_bit(ATM_VF_ADDR, &vcc->flags); - return -EBUSY; - } - - vc->cbr_scd = NS_FRSCD + frscdi * NS_FRSCD_SIZE; - - scq = get_scq(card, CBR_SCQSIZE, vc->cbr_scd); - if (scq == NULL) { - PRINTK("nicstar%d: can't get fixed rate SCQ.\n", - card->index); - card->scd2vc[frscdi] = NULL; - card->tst_free_entries += n; - clear_bit(ATM_VF_PARTIAL, &vcc->flags); - clear_bit(ATM_VF_ADDR, &vcc->flags); - return -ENOMEM; - } - vc->scq = scq; - u32d[0] = scq_virt_to_bus(scq, scq->base); - u32d[1] = (u32) 0x00000000; - u32d[2] = (u32) 0xffffffff; - u32d[3] = (u32) 0x00000000; - ns_write_sram(card, vc->cbr_scd, u32d, 4); - - fill_tst(card, n, vc); - } else if (vcc->qos.txtp.traffic_class == ATM_UBR) { - vc->cbr_scd = 0x00000000; - vc->scq = card->scq0; - } - - if (vcc->qos.txtp.traffic_class != ATM_NONE) { - vc->tx = 1; - vc->tx_vcc = vcc; - vc->tbd_count = 0; - } - if (vcc->qos.rxtp.traffic_class != ATM_NONE) { - u32 status; - - vc->rx = 1; - vc->rx_vcc = vcc; - vc->rx_iov = NULL; - - /* Open the connection in hardware */ - if (vcc->qos.aal == ATM_AAL5) - status = NS_RCTE_AAL5 | NS_RCTE_CONNECTOPEN; - else /* vcc->qos.aal == ATM_AAL0 */ - status = NS_RCTE_AAL0 | NS_RCTE_CONNECTOPEN; -#ifdef RCQ_SUPPORT - status |= NS_RCTE_RAWCELLINTEN; -#endif /* RCQ_SUPPORT */ - ns_write_sram(card, - NS_RCT + - (vpi << card->vcibits | vci) * - NS_RCT_ENTRY_SIZE, &status, 1); - } - - } - - set_bit(ATM_VF_READY, &vcc->flags); - return 0; -} - -static void ns_close(struct atm_vcc *vcc) -{ - vc_map *vc; - ns_dev *card; - u32 data; - int i; - - vc = vcc->dev_data; - card = vcc->dev->dev_data; - PRINTK("nicstar%d: closing vpi.vci %d.%d \n", card->index, - (int)vcc->vpi, vcc->vci); - - clear_bit(ATM_VF_READY, &vcc->flags); - - if (vcc->qos.rxtp.traffic_class != ATM_NONE) { - u32 addr; - unsigned long flags; - - addr = - NS_RCT + - (vcc->vpi << card->vcibits | vcc->vci) * NS_RCT_ENTRY_SIZE; - spin_lock_irqsave(&card->res_lock, flags); - while (CMD_BUSY(card)) ; - writel(NS_CMD_CLOSE_CONNECTION | addr << 2, - card->membase + CMD); - spin_unlock_irqrestore(&card->res_lock, flags); - - vc->rx = 0; - if (vc->rx_iov != NULL) { - struct sk_buff *iovb; - u32 stat; - - stat = readl(card->membase + STAT); - card->sbfqc = ns_stat_sfbqc_get(stat); - card->lbfqc = ns_stat_lfbqc_get(stat); - - PRINTK - ("nicstar%d: closing a VC with pending rx buffers.\n", - card->index); - iovb = vc->rx_iov; - recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data, - NS_PRV_IOVCNT(iovb)); - NS_PRV_IOVCNT(iovb) = 0; - spin_lock_irqsave(&card->int_lock, flags); - recycle_iov_buf(card, iovb); - spin_unlock_irqrestore(&card->int_lock, flags); - vc->rx_iov = NULL; - } - } - - if (vcc->qos.txtp.traffic_class != ATM_NONE) { - vc->tx = 0; - } - - if (vcc->qos.txtp.traffic_class == ATM_CBR) { - unsigned long flags; - ns_scqe *scqep; - scq_info *scq; - - scq = vc->scq; - - for (;;) { - spin_lock_irqsave(&scq->lock, flags); - scqep = scq->next; - if (scqep == scq->base) - scqep = scq->last; - else - scqep--; - if (scqep == scq->tail) { - spin_unlock_irqrestore(&scq->lock, flags); - break; - } - /* If the last entry is not a TSR, place one in the SCQ in order to - be able to completely drain it and then close. */ - if (!ns_scqe_is_tsr(scqep) && scq->tail != scq->next) { - ns_scqe tsr; - u32 scdi, scqi; - u32 data; - int index; - - tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE); - scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE; - scqi = scq->next - scq->base; - tsr.word_2 = ns_tsr_mkword_2(scdi, scqi); - tsr.word_3 = 0x00000000; - tsr.word_4 = 0x00000000; - *scq->next = tsr; - index = (int)scqi; - scq->skb[index] = NULL; - if (scq->next == scq->last) - scq->next = scq->base; - else - scq->next++; - data = scq_virt_to_bus(scq, scq->next); - ns_write_sram(card, scq->scd, &data, 1); - } - spin_unlock_irqrestore(&scq->lock, flags); - schedule(); - } - - /* Free all TST entries */ - data = NS_TST_OPCODE_VARIABLE; - for (i = 0; i < NS_TST_NUM_ENTRIES; i++) { - if (card->tste2vc[i] == vc) { - ns_write_sram(card, card->tst_addr + i, &data, - 1); - card->tste2vc[i] = NULL; - card->tst_free_entries++; - } - } - - card->scd2vc[(vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE] = NULL; - free_scq(card, vc->scq, vcc); - } - - /* remove all references to vcc before deleting it */ - if (vcc->qos.txtp.traffic_class != ATM_NONE) { - unsigned long flags; - scq_info *scq = card->scq0; - - spin_lock_irqsave(&scq->lock, flags); - - for (i = 0; i < scq->num_entries; i++) { - if (scq->skb[i] && ATM_SKB(scq->skb[i])->vcc == vcc) { - ATM_SKB(scq->skb[i])->vcc = NULL; - atm_return(vcc, scq->skb[i]->truesize); - PRINTK - ("nicstar: deleted pending vcc mapping\n"); - } - } - - spin_unlock_irqrestore(&scq->lock, flags); - } - - vcc->dev_data = NULL; - clear_bit(ATM_VF_PARTIAL, &vcc->flags); - clear_bit(ATM_VF_ADDR, &vcc->flags); - -#ifdef RX_DEBUG - { - u32 stat, cfg; - stat = readl(card->membase + STAT); - cfg = readl(card->membase + CFG); - printk("STAT = 0x%08X CFG = 0x%08X \n", stat, cfg); - printk - ("TSQ: base = 0x%p next = 0x%p last = 0x%p TSQT = 0x%08X \n", - card->tsq.base, card->tsq.next, - card->tsq.last, readl(card->membase + TSQT)); - printk - ("RSQ: base = 0x%p next = 0x%p last = 0x%p RSQT = 0x%08X \n", - card->rsq.base, card->rsq.next, - card->rsq.last, readl(card->membase + RSQT)); - printk("Empty free buffer queue interrupt %s \n", - card->efbie ? "enabled" : "disabled"); - printk("SBCNT = %d count = %d LBCNT = %d count = %d \n", - ns_stat_sfbqc_get(stat), card->sbpool.count, - ns_stat_lfbqc_get(stat), card->lbpool.count); - printk("hbpool.count = %d iovpool.count = %d \n", - card->hbpool.count, card->iovpool.count); - } -#endif /* RX_DEBUG */ -} - -static void fill_tst(ns_dev * card, int n, vc_map * vc) -{ - u32 new_tst; - unsigned long cl; - int e, r; - u32 data; - - /* It would be very complicated to keep the two TSTs synchronized while - assuring that writes are only made to the inactive TST. So, for now I - will use only one TST. If problems occur, I will change this again */ - - new_tst = card->tst_addr; - - /* Fill procedure */ - - for (e = 0; e < NS_TST_NUM_ENTRIES; e++) { - if (card->tste2vc[e] == NULL) - break; - } - if (e == NS_TST_NUM_ENTRIES) { - printk("nicstar%d: No free TST entries found. \n", card->index); - return; - } - - r = n; - cl = NS_TST_NUM_ENTRIES; - data = ns_tste_make(NS_TST_OPCODE_FIXED, vc->cbr_scd); - - while (r > 0) { - if (cl >= NS_TST_NUM_ENTRIES && card->tste2vc[e] == NULL) { - card->tste2vc[e] = vc; - ns_write_sram(card, new_tst + e, &data, 1); - cl -= NS_TST_NUM_ENTRIES; - r--; - } - - if (++e == NS_TST_NUM_ENTRIES) { - e = 0; - } - cl += n; - } - - /* End of fill procedure */ - - data = ns_tste_make(NS_TST_OPCODE_END, new_tst); - ns_write_sram(card, new_tst + NS_TST_NUM_ENTRIES, &data, 1); - ns_write_sram(card, card->tst_addr + NS_TST_NUM_ENTRIES, &data, 1); - card->tst_addr = new_tst; -} - -static int _ns_send(struct atm_vcc *vcc, struct sk_buff *skb, bool may_sleep) -{ - ns_dev *card; - vc_map *vc; - scq_info *scq; - unsigned long buflen; - ns_scqe scqe; - u32 flags; /* TBD flags, not CPU flags */ - - card = vcc->dev->dev_data; - TXPRINTK("nicstar%d: ns_send() called.\n", card->index); - if ((vc = (vc_map *) vcc->dev_data) == NULL) { - printk("nicstar%d: vcc->dev_data == NULL on ns_send().\n", - card->index); - atomic_inc(&vcc->stats->tx_err); - dev_kfree_skb_any(skb); - return -EINVAL; - } - - if (!vc->tx) { - printk("nicstar%d: Trying to transmit on a non-tx VC.\n", - card->index); - atomic_inc(&vcc->stats->tx_err); - dev_kfree_skb_any(skb); - return -EINVAL; - } - - if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0) { - printk("nicstar%d: Only AAL0 and AAL5 are supported.\n", - card->index); - atomic_inc(&vcc->stats->tx_err); - dev_kfree_skb_any(skb); - return -EINVAL; - } - - if (skb_shinfo(skb)->nr_frags != 0) { - printk("nicstar%d: No scatter-gather yet.\n", card->index); - atomic_inc(&vcc->stats->tx_err); - dev_kfree_skb_any(skb); - return -EINVAL; - } - - ATM_SKB(skb)->vcc = vcc; - - NS_PRV_DMA(skb) = dma_map_single(&card->pcidev->dev, skb->data, - skb->len, DMA_TO_DEVICE); - - if (vcc->qos.aal == ATM_AAL5) { - buflen = (skb->len + 47 + 8) / 48 * 48; /* Multiple of 48 */ - flags = NS_TBD_AAL5; - scqe.word_2 = cpu_to_le32(NS_PRV_DMA(skb)); - scqe.word_3 = cpu_to_le32(skb->len); - scqe.word_4 = - ns_tbd_mkword_4(0, (u32) vcc->vpi, (u32) vcc->vci, 0, - ATM_SKB(skb)-> - atm_options & ATM_ATMOPT_CLP ? 1 : 0); - flags |= NS_TBD_EOPDU; - } else { /* (vcc->qos.aal == ATM_AAL0) */ - - buflen = ATM_CELL_PAYLOAD; /* i.e., 48 bytes */ - flags = NS_TBD_AAL0; - scqe.word_2 = cpu_to_le32(NS_PRV_DMA(skb) + NS_AAL0_HEADER); - scqe.word_3 = cpu_to_le32(0x00000000); - if (*skb->data & 0x02) /* Payload type 1 - end of pdu */ - flags |= NS_TBD_EOPDU; - scqe.word_4 = - cpu_to_le32(*((u32 *) skb->data) & ~NS_TBD_VC_MASK); - /* Force the VPI/VCI to be the same as in VCC struct */ - scqe.word_4 |= - cpu_to_le32((((u32) vcc-> - vpi) << NS_TBD_VPI_SHIFT | ((u32) vcc-> - vci) << - NS_TBD_VCI_SHIFT) & NS_TBD_VC_MASK); - } - - if (vcc->qos.txtp.traffic_class == ATM_CBR) { - scqe.word_1 = ns_tbd_mkword_1_novbr(flags, (u32) buflen); - scq = ((vc_map *) vcc->dev_data)->scq; - } else { - scqe.word_1 = - ns_tbd_mkword_1(flags, (u32) 1, (u32) 1, (u32) buflen); - scq = card->scq0; - } - - if (push_scqe(card, vc, scq, &scqe, skb, may_sleep) != 0) { - atomic_inc(&vcc->stats->tx_err); - dma_unmap_single(&card->pcidev->dev, NS_PRV_DMA(skb), skb->len, - DMA_TO_DEVICE); - dev_kfree_skb_any(skb); - return -EIO; - } - atomic_inc(&vcc->stats->tx); - - return 0; -} - -static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - return _ns_send(vcc, skb, true); -} - -static int ns_send_bh(struct atm_vcc *vcc, struct sk_buff *skb) -{ - return _ns_send(vcc, skb, false); -} - -static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd, - struct sk_buff *skb, bool may_sleep) -{ - unsigned long flags; - ns_scqe tsr; - u32 scdi, scqi; - int scq_is_vbr; - u32 data; - int index; - - spin_lock_irqsave(&scq->lock, flags); - while (scq->tail == scq->next) { - if (!may_sleep) { - spin_unlock_irqrestore(&scq->lock, flags); - printk("nicstar%d: Error pushing TBD.\n", card->index); - return 1; - } - - scq->full = 1; - wait_event_interruptible_lock_irq_timeout(scq->scqfull_waitq, - scq->tail != scq->next, - scq->lock, - SCQFULL_TIMEOUT); - - if (scq->full) { - spin_unlock_irqrestore(&scq->lock, flags); - printk("nicstar%d: Timeout pushing TBD.\n", - card->index); - return 1; - } - } - *scq->next = *tbd; - index = (int)(scq->next - scq->base); - scq->skb[index] = skb; - XPRINTK("nicstar%d: sending skb at 0x%p (pos %d).\n", - card->index, skb, index); - XPRINTK("nicstar%d: TBD written:\n0x%x\n0x%x\n0x%x\n0x%x\n at 0x%p.\n", - card->index, le32_to_cpu(tbd->word_1), le32_to_cpu(tbd->word_2), - le32_to_cpu(tbd->word_3), le32_to_cpu(tbd->word_4), - scq->next); - if (scq->next == scq->last) - scq->next = scq->base; - else - scq->next++; - - vc->tbd_count++; - if (scq->num_entries == VBR_SCQ_NUM_ENTRIES) { - scq->tbd_count++; - scq_is_vbr = 1; - } else - scq_is_vbr = 0; - - if (vc->tbd_count >= MAX_TBD_PER_VC - || scq->tbd_count >= MAX_TBD_PER_SCQ) { - int has_run = 0; - - while (scq->tail == scq->next) { - if (!may_sleep) { - data = scq_virt_to_bus(scq, scq->next); - ns_write_sram(card, scq->scd, &data, 1); - spin_unlock_irqrestore(&scq->lock, flags); - printk("nicstar%d: Error pushing TSR.\n", - card->index); - return 0; - } - - scq->full = 1; - if (has_run++) - break; - wait_event_interruptible_lock_irq_timeout(scq->scqfull_waitq, - scq->tail != scq->next, - scq->lock, - SCQFULL_TIMEOUT); - } - - if (!scq->full) { - tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE); - if (scq_is_vbr) - scdi = NS_TSR_SCDISVBR; - else - scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE; - scqi = scq->next - scq->base; - tsr.word_2 = ns_tsr_mkword_2(scdi, scqi); - tsr.word_3 = 0x00000000; - tsr.word_4 = 0x00000000; - - *scq->next = tsr; - index = (int)scqi; - scq->skb[index] = NULL; - XPRINTK - ("nicstar%d: TSR written:\n0x%x\n0x%x\n0x%x\n0x%x\n at 0x%p.\n", - card->index, le32_to_cpu(tsr.word_1), - le32_to_cpu(tsr.word_2), le32_to_cpu(tsr.word_3), - le32_to_cpu(tsr.word_4), scq->next); - if (scq->next == scq->last) - scq->next = scq->base; - else - scq->next++; - vc->tbd_count = 0; - scq->tbd_count = 0; - } else - PRINTK("nicstar%d: Timeout pushing TSR.\n", - card->index); - } - data = scq_virt_to_bus(scq, scq->next); - ns_write_sram(card, scq->scd, &data, 1); - - spin_unlock_irqrestore(&scq->lock, flags); - - return 0; -} - -static void process_tsq(ns_dev * card) -{ - u32 scdi; - scq_info *scq; - ns_tsi *previous = NULL, *one_ahead, *two_ahead; - int serviced_entries; /* flag indicating at least on entry was serviced */ - - serviced_entries = 0; - - if (card->tsq.next == card->tsq.last) - one_ahead = card->tsq.base; - else - one_ahead = card->tsq.next + 1; - - if (one_ahead == card->tsq.last) - two_ahead = card->tsq.base; - else - two_ahead = one_ahead + 1; - - while (!ns_tsi_isempty(card->tsq.next) || !ns_tsi_isempty(one_ahead) || - !ns_tsi_isempty(two_ahead)) - /* At most two empty, as stated in the 77201 errata */ - { - serviced_entries = 1; - - /* Skip the one or two possible empty entries */ - while (ns_tsi_isempty(card->tsq.next)) { - if (card->tsq.next == card->tsq.last) - card->tsq.next = card->tsq.base; - else - card->tsq.next++; - } - - if (!ns_tsi_tmrof(card->tsq.next)) { - scdi = ns_tsi_getscdindex(card->tsq.next); - if (scdi == NS_TSI_SCDISVBR) - scq = card->scq0; - else { - if (card->scd2vc[scdi] == NULL) { - printk - ("nicstar%d: could not find VC from SCD index.\n", - card->index); - ns_tsi_init(card->tsq.next); - return; - } - scq = card->scd2vc[scdi]->scq; - } - drain_scq(card, scq, ns_tsi_getscqpos(card->tsq.next)); - scq->full = 0; - wake_up_interruptible(&(scq->scqfull_waitq)); - } - - ns_tsi_init(card->tsq.next); - previous = card->tsq.next; - if (card->tsq.next == card->tsq.last) - card->tsq.next = card->tsq.base; - else - card->tsq.next++; - - if (card->tsq.next == card->tsq.last) - one_ahead = card->tsq.base; - else - one_ahead = card->tsq.next + 1; - - if (one_ahead == card->tsq.last) - two_ahead = card->tsq.base; - else - two_ahead = one_ahead + 1; - } - - if (serviced_entries) - writel(PTR_DIFF(previous, card->tsq.base), - card->membase + TSQH); -} - -static void drain_scq(ns_dev * card, scq_info * scq, int pos) -{ - struct atm_vcc *vcc; - struct sk_buff *skb; - int i; - unsigned long flags; - - XPRINTK("nicstar%d: drain_scq() called, scq at 0x%p, pos %d.\n", - card->index, scq, pos); - if (pos >= scq->num_entries) { - printk("nicstar%d: Bad index on drain_scq().\n", card->index); - return; - } - - spin_lock_irqsave(&scq->lock, flags); - i = (int)(scq->tail - scq->base); - if (++i == scq->num_entries) - i = 0; - while (i != pos) { - skb = scq->skb[i]; - XPRINTK("nicstar%d: freeing skb at 0x%p (index %d).\n", - card->index, skb, i); - if (skb != NULL) { - dma_unmap_single(&card->pcidev->dev, - NS_PRV_DMA(skb), - skb->len, - DMA_TO_DEVICE); - vcc = ATM_SKB(skb)->vcc; - if (vcc && vcc->pop != NULL) { - vcc->pop(vcc, skb); - } else { - dev_kfree_skb_irq(skb); - } - scq->skb[i] = NULL; - } - if (++i == scq->num_entries) - i = 0; - } - scq->tail = scq->base + pos; - spin_unlock_irqrestore(&scq->lock, flags); -} - -static void process_rsq(ns_dev * card) -{ - ns_rsqe *previous; - - if (!ns_rsqe_valid(card->rsq.next)) - return; - do { - dequeue_rx(card, card->rsq.next); - ns_rsqe_init(card->rsq.next); - previous = card->rsq.next; - if (card->rsq.next == card->rsq.last) - card->rsq.next = card->rsq.base; - else - card->rsq.next++; - } while (ns_rsqe_valid(card->rsq.next)); - writel(PTR_DIFF(previous, card->rsq.base), card->membase + RSQH); -} - -static void dequeue_rx(ns_dev * card, ns_rsqe * rsqe) -{ - u32 vpi, vci; - vc_map *vc; - struct sk_buff *iovb; - struct iovec *iov; - struct atm_vcc *vcc; - struct sk_buff *skb; - unsigned short aal5_len; - int len; - u32 stat; - u32 id; - - stat = readl(card->membase + STAT); - card->sbfqc = ns_stat_sfbqc_get(stat); - card->lbfqc = ns_stat_lfbqc_get(stat); - - id = le32_to_cpu(rsqe->buffer_handle); - skb = idr_remove(&card->idr, id); - if (!skb) { - RXPRINTK(KERN_ERR - "nicstar%d: skb not found!\n", card->index); - return; - } - dma_sync_single_for_cpu(&card->pcidev->dev, - NS_PRV_DMA(skb), - (NS_PRV_BUFTYPE(skb) == BUF_SM - ? NS_SMSKBSIZE : NS_LGSKBSIZE), - DMA_FROM_DEVICE); - dma_unmap_single(&card->pcidev->dev, - NS_PRV_DMA(skb), - (NS_PRV_BUFTYPE(skb) == BUF_SM - ? NS_SMSKBSIZE : NS_LGSKBSIZE), - DMA_FROM_DEVICE); - vpi = ns_rsqe_vpi(rsqe); - vci = ns_rsqe_vci(rsqe); - if (vpi >= 1UL << card->vpibits || vci >= 1UL << card->vcibits) { - printk("nicstar%d: SDU received for out-of-range vc %d.%d.\n", - card->index, vpi, vci); - recycle_rx_buf(card, skb); - return; - } - - vc = &(card->vcmap[vpi << card->vcibits | vci]); - if (!vc->rx) { - RXPRINTK("nicstar%d: SDU received on non-rx vc %d.%d.\n", - card->index, vpi, vci); - recycle_rx_buf(card, skb); - return; - } - - vcc = vc->rx_vcc; - - if (vcc->qos.aal == ATM_AAL0) { - struct sk_buff *sb; - unsigned char *cell; - int i; - - cell = skb->data; - for (i = ns_rsqe_cellcount(rsqe); i; i--) { - sb = dev_alloc_skb(NS_SMSKBSIZE); - if (!sb) { - printk - ("nicstar%d: Can't allocate buffers for aal0.\n", - card->index); - atomic_add(i, &vcc->stats->rx_drop); - break; - } - if (!atm_charge(vcc, sb->truesize)) { - RXPRINTK - ("nicstar%d: atm_charge() dropped aal0 packets.\n", - card->index); - atomic_add(i - 1, &vcc->stats->rx_drop); /* already increased by 1 */ - dev_kfree_skb_any(sb); - break; - } - /* Rebuild the header */ - *((u32 *) sb->data) = le32_to_cpu(rsqe->word_1) << 4 | - (ns_rsqe_clp(rsqe) ? 0x00000001 : 0x00000000); - if (i == 1 && ns_rsqe_eopdu(rsqe)) - *((u32 *) sb->data) |= 0x00000002; - skb_put(sb, NS_AAL0_HEADER); - memcpy(skb_tail_pointer(sb), cell, ATM_CELL_PAYLOAD); - skb_put(sb, ATM_CELL_PAYLOAD); - ATM_SKB(sb)->vcc = vcc; - __net_timestamp(sb); - vcc->push(vcc, sb); - atomic_inc(&vcc->stats->rx); - cell += ATM_CELL_PAYLOAD; - } - - recycle_rx_buf(card, skb); - return; - } - - /* To reach this point, the AAL layer can only be AAL5 */ - - if ((iovb = vc->rx_iov) == NULL) { - iovb = skb_dequeue(&(card->iovpool.queue)); - if (iovb == NULL) { /* No buffers in the queue */ - iovb = alloc_skb(NS_IOVBUFSIZE, GFP_ATOMIC); - if (iovb == NULL) { - printk("nicstar%d: Out of iovec buffers.\n", - card->index); - atomic_inc(&vcc->stats->rx_drop); - recycle_rx_buf(card, skb); - return; - } - NS_PRV_BUFTYPE(iovb) = BUF_NONE; - } else if (--card->iovpool.count < card->iovnr.min) { - struct sk_buff *new_iovb; - if ((new_iovb = - alloc_skb(NS_IOVBUFSIZE, GFP_ATOMIC)) != NULL) { - NS_PRV_BUFTYPE(iovb) = BUF_NONE; - skb_queue_tail(&card->iovpool.queue, new_iovb); - card->iovpool.count++; - } - } - vc->rx_iov = iovb; - NS_PRV_IOVCNT(iovb) = 0; - iovb->len = 0; - iovb->data = iovb->head; - skb_reset_tail_pointer(iovb); - /* IMPORTANT: a pointer to the sk_buff containing the small or large - buffer is stored as iovec base, NOT a pointer to the - small or large buffer itself. */ - } else if (NS_PRV_IOVCNT(iovb) >= NS_MAX_IOVECS) { - printk("nicstar%d: received too big AAL5 SDU.\n", card->index); - atomic_inc(&vcc->stats->rx_err); - recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data, - NS_MAX_IOVECS); - NS_PRV_IOVCNT(iovb) = 0; - iovb->len = 0; - iovb->data = iovb->head; - skb_reset_tail_pointer(iovb); - } - iov = &((struct iovec *)iovb->data)[NS_PRV_IOVCNT(iovb)++]; - iov->iov_base = (void *)skb; - iov->iov_len = ns_rsqe_cellcount(rsqe) * 48; - iovb->len += iov->iov_len; - -#ifdef EXTRA_DEBUG - if (NS_PRV_IOVCNT(iovb) == 1) { - if (NS_PRV_BUFTYPE(skb) != BUF_SM) { - printk - ("nicstar%d: Expected a small buffer, and this is not one.\n", - card->index); - which_list(card, skb); - atomic_inc(&vcc->stats->rx_err); - recycle_rx_buf(card, skb); - vc->rx_iov = NULL; - recycle_iov_buf(card, iovb); - return; - } - } else { /* NS_PRV_IOVCNT(iovb) >= 2 */ - - if (NS_PRV_BUFTYPE(skb) != BUF_LG) { - printk - ("nicstar%d: Expected a large buffer, and this is not one.\n", - card->index); - which_list(card, skb); - atomic_inc(&vcc->stats->rx_err); - recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data, - NS_PRV_IOVCNT(iovb)); - vc->rx_iov = NULL; - recycle_iov_buf(card, iovb); - return; - } - } -#endif /* EXTRA_DEBUG */ - - if (ns_rsqe_eopdu(rsqe)) { - /* This works correctly regardless of the endianness of the host */ - unsigned char *L1L2 = (unsigned char *) - (skb->data + iov->iov_len - 6); - aal5_len = L1L2[0] << 8 | L1L2[1]; - len = (aal5_len == 0x0000) ? 0x10000 : aal5_len; - if (ns_rsqe_crcerr(rsqe) || - len + 8 > iovb->len || len + (47 + 8) < iovb->len) { - printk("nicstar%d: AAL5 CRC error", card->index); - if (len + 8 > iovb->len || len + (47 + 8) < iovb->len) - printk(" - PDU size mismatch.\n"); - else - printk(".\n"); - atomic_inc(&vcc->stats->rx_err); - recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data, - NS_PRV_IOVCNT(iovb)); - vc->rx_iov = NULL; - recycle_iov_buf(card, iovb); - return; - } - - /* By this point we (hopefully) have a complete SDU without errors. */ - - if (NS_PRV_IOVCNT(iovb) == 1) { /* Just a small buffer */ - /* skb points to a small buffer */ - if (!atm_charge(vcc, skb->truesize)) { - push_rxbufs(card, skb); - atomic_inc(&vcc->stats->rx_drop); - } else { - skb_put(skb, len); - dequeue_sm_buf(card, skb); - ATM_SKB(skb)->vcc = vcc; - __net_timestamp(skb); - vcc->push(vcc, skb); - atomic_inc(&vcc->stats->rx); - } - } else if (NS_PRV_IOVCNT(iovb) == 2) { /* One small plus one large buffer */ - struct sk_buff *sb; - - sb = (struct sk_buff *)(iov - 1)->iov_base; - /* skb points to a large buffer */ - - if (len <= NS_SMBUFSIZE) { - if (!atm_charge(vcc, sb->truesize)) { - push_rxbufs(card, sb); - atomic_inc(&vcc->stats->rx_drop); - } else { - skb_put(sb, len); - dequeue_sm_buf(card, sb); - ATM_SKB(sb)->vcc = vcc; - __net_timestamp(sb); - vcc->push(vcc, sb); - atomic_inc(&vcc->stats->rx); - } - - push_rxbufs(card, skb); - - } else { /* len > NS_SMBUFSIZE, the usual case */ - - if (!atm_charge(vcc, skb->truesize)) { - push_rxbufs(card, skb); - atomic_inc(&vcc->stats->rx_drop); - } else { - dequeue_lg_buf(card, skb); - skb_push(skb, NS_SMBUFSIZE); - skb_copy_from_linear_data(sb, skb->data, - NS_SMBUFSIZE); - skb_put(skb, len - NS_SMBUFSIZE); - ATM_SKB(skb)->vcc = vcc; - __net_timestamp(skb); - vcc->push(vcc, skb); - atomic_inc(&vcc->stats->rx); - } - - push_rxbufs(card, sb); - - } - - } else { /* Must push a huge buffer */ - - struct sk_buff *hb, *sb, *lb; - int remaining, tocopy; - int j; - - hb = skb_dequeue(&(card->hbpool.queue)); - if (hb == NULL) { /* No buffers in the queue */ - - hb = dev_alloc_skb(NS_HBUFSIZE); - if (hb == NULL) { - printk - ("nicstar%d: Out of huge buffers.\n", - card->index); - atomic_inc(&vcc->stats->rx_drop); - recycle_iovec_rx_bufs(card, - (struct iovec *) - iovb->data, - NS_PRV_IOVCNT(iovb)); - vc->rx_iov = NULL; - recycle_iov_buf(card, iovb); - return; - } else if (card->hbpool.count < card->hbnr.min) { - struct sk_buff *new_hb; - if ((new_hb = - dev_alloc_skb(NS_HBUFSIZE)) != - NULL) { - skb_queue_tail(&card->hbpool. - queue, new_hb); - card->hbpool.count++; - } - } - NS_PRV_BUFTYPE(hb) = BUF_NONE; - } else if (--card->hbpool.count < card->hbnr.min) { - struct sk_buff *new_hb; - if ((new_hb = - dev_alloc_skb(NS_HBUFSIZE)) != NULL) { - NS_PRV_BUFTYPE(new_hb) = BUF_NONE; - skb_queue_tail(&card->hbpool.queue, - new_hb); - card->hbpool.count++; - } - if (card->hbpool.count < card->hbnr.min) { - if ((new_hb = - dev_alloc_skb(NS_HBUFSIZE)) != - NULL) { - NS_PRV_BUFTYPE(new_hb) = - BUF_NONE; - skb_queue_tail(&card->hbpool. - queue, new_hb); - card->hbpool.count++; - } - } - } - - iov = (struct iovec *)iovb->data; - - if (!atm_charge(vcc, hb->truesize)) { - recycle_iovec_rx_bufs(card, iov, - NS_PRV_IOVCNT(iovb)); - if (card->hbpool.count < card->hbnr.max) { - skb_queue_tail(&card->hbpool.queue, hb); - card->hbpool.count++; - } else - dev_kfree_skb_any(hb); - atomic_inc(&vcc->stats->rx_drop); - } else { - /* Copy the small buffer to the huge buffer */ - sb = (struct sk_buff *)iov->iov_base; - skb_copy_from_linear_data(sb, hb->data, - iov->iov_len); - skb_put(hb, iov->iov_len); - remaining = len - iov->iov_len; - iov++; - /* Free the small buffer */ - push_rxbufs(card, sb); - - /* Copy all large buffers to the huge buffer and free them */ - for (j = 1; j < NS_PRV_IOVCNT(iovb); j++) { - lb = (struct sk_buff *)iov->iov_base; - tocopy = - min_t(int, remaining, iov->iov_len); - skb_copy_from_linear_data(lb, - skb_tail_pointer - (hb), tocopy); - skb_put(hb, tocopy); - iov++; - remaining -= tocopy; - push_rxbufs(card, lb); - } -#ifdef EXTRA_DEBUG - if (remaining != 0 || hb->len != len) - printk - ("nicstar%d: Huge buffer len mismatch.\n", - card->index); -#endif /* EXTRA_DEBUG */ - ATM_SKB(hb)->vcc = vcc; - __net_timestamp(hb); - vcc->push(vcc, hb); - atomic_inc(&vcc->stats->rx); - } - } - - vc->rx_iov = NULL; - recycle_iov_buf(card, iovb); - } - -} - -static void recycle_rx_buf(ns_dev * card, struct sk_buff *skb) -{ - if (unlikely(NS_PRV_BUFTYPE(skb) == BUF_NONE)) { - printk("nicstar%d: What kind of rx buffer is this?\n", - card->index); - dev_kfree_skb_any(skb); - } else - push_rxbufs(card, skb); -} - -static void recycle_iovec_rx_bufs(ns_dev * card, struct iovec *iov, int count) -{ - while (count-- > 0) - recycle_rx_buf(card, (struct sk_buff *)(iov++)->iov_base); -} - -static void recycle_iov_buf(ns_dev * card, struct sk_buff *iovb) -{ - if (card->iovpool.count < card->iovnr.max) { - skb_queue_tail(&card->iovpool.queue, iovb); - card->iovpool.count++; - } else - dev_kfree_skb_any(iovb); -} - -static void dequeue_sm_buf(ns_dev * card, struct sk_buff *sb) -{ - skb_unlink(sb, &card->sbpool.queue); - if (card->sbfqc < card->sbnr.init) { - struct sk_buff *new_sb; - if ((new_sb = dev_alloc_skb(NS_SMSKBSIZE)) != NULL) { - NS_PRV_BUFTYPE(new_sb) = BUF_SM; - skb_queue_tail(&card->sbpool.queue, new_sb); - skb_reserve(new_sb, NS_AAL0_HEADER); - push_rxbufs(card, new_sb); - } - } - if (card->sbfqc < card->sbnr.init) - { - struct sk_buff *new_sb; - if ((new_sb = dev_alloc_skb(NS_SMSKBSIZE)) != NULL) { - NS_PRV_BUFTYPE(new_sb) = BUF_SM; - skb_queue_tail(&card->sbpool.queue, new_sb); - skb_reserve(new_sb, NS_AAL0_HEADER); - push_rxbufs(card, new_sb); - } - } -} - -static void dequeue_lg_buf(ns_dev * card, struct sk_buff *lb) -{ - skb_unlink(lb, &card->lbpool.queue); - if (card->lbfqc < card->lbnr.init) { - struct sk_buff *new_lb; - if ((new_lb = dev_alloc_skb(NS_LGSKBSIZE)) != NULL) { - NS_PRV_BUFTYPE(new_lb) = BUF_LG; - skb_queue_tail(&card->lbpool.queue, new_lb); - skb_reserve(new_lb, NS_SMBUFSIZE); - push_rxbufs(card, new_lb); - } - } - if (card->lbfqc < card->lbnr.init) - { - struct sk_buff *new_lb; - if ((new_lb = dev_alloc_skb(NS_LGSKBSIZE)) != NULL) { - NS_PRV_BUFTYPE(new_lb) = BUF_LG; - skb_queue_tail(&card->lbpool.queue, new_lb); - skb_reserve(new_lb, NS_SMBUFSIZE); - push_rxbufs(card, new_lb); - } - } -} - -static int ns_proc_read(struct atm_dev *dev, loff_t * pos, char *page) -{ - u32 stat; - ns_dev *card; - int left; - - left = (int)*pos; - card = (ns_dev *) dev->dev_data; - stat = readl(card->membase + STAT); - if (!left--) - return sprintf(page, "Pool count min init max \n"); - if (!left--) - return sprintf(page, "Small %5d %5d %5d %5d \n", - ns_stat_sfbqc_get(stat), card->sbnr.min, - card->sbnr.init, card->sbnr.max); - if (!left--) - return sprintf(page, "Large %5d %5d %5d %5d \n", - ns_stat_lfbqc_get(stat), card->lbnr.min, - card->lbnr.init, card->lbnr.max); - if (!left--) - return sprintf(page, "Huge %5d %5d %5d %5d \n", - card->hbpool.count, card->hbnr.min, - card->hbnr.init, card->hbnr.max); - if (!left--) - return sprintf(page, "Iovec %5d %5d %5d %5d \n", - card->iovpool.count, card->iovnr.min, - card->iovnr.init, card->iovnr.max); - if (!left--) { - int retval; - retval = - sprintf(page, "Interrupt counter: %u \n", card->intcnt); - card->intcnt = 0; - return retval; - } -#if 0 - /* Dump 25.6 Mbps PHY registers */ - /* Now there's a 25.6 Mbps PHY driver this code isn't needed. I left it - here just in case it's needed for debugging. */ - if (card->max_pcr == ATM_25_PCR && !left--) { - u32 phy_regs[4]; - u32 i; - - for (i = 0; i < 4; i++) { - while (CMD_BUSY(card)) ; - writel(NS_CMD_READ_UTILITY | 0x00000200 | i, - card->membase + CMD); - while (CMD_BUSY(card)) ; - phy_regs[i] = readl(card->membase + DR0) & 0x000000FF; - } - - return sprintf(page, "PHY regs: 0x%02X 0x%02X 0x%02X 0x%02X \n", - phy_regs[0], phy_regs[1], phy_regs[2], - phy_regs[3]); - } -#endif /* 0 - Dump 25.6 Mbps PHY registers */ -#if 0 - /* Dump TST */ - if (left-- < NS_TST_NUM_ENTRIES) { - if (card->tste2vc[left + 1] == NULL) - return sprintf(page, "%5d - VBR/UBR \n", left + 1); - else - return sprintf(page, "%5d - %d %d \n", left + 1, - card->tste2vc[left + 1]->tx_vcc->vpi, - card->tste2vc[left + 1]->tx_vcc->vci); - } -#endif /* 0 */ - return 0; -} - -static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg) -{ - ns_dev *card; - pool_levels pl; - long btype; - unsigned long flags; - - card = dev->dev_data; - switch (cmd) { - case NS_GETPSTAT: - if (get_user - (pl.buftype, &((pool_levels __user *) arg)->buftype)) - return -EFAULT; - switch (pl.buftype) { - case NS_BUFTYPE_SMALL: - pl.count = - ns_stat_sfbqc_get(readl(card->membase + STAT)); - pl.level.min = card->sbnr.min; - pl.level.init = card->sbnr.init; - pl.level.max = card->sbnr.max; - break; - - case NS_BUFTYPE_LARGE: - pl.count = - ns_stat_lfbqc_get(readl(card->membase + STAT)); - pl.level.min = card->lbnr.min; - pl.level.init = card->lbnr.init; - pl.level.max = card->lbnr.max; - break; - - case NS_BUFTYPE_HUGE: - pl.count = card->hbpool.count; - pl.level.min = card->hbnr.min; - pl.level.init = card->hbnr.init; - pl.level.max = card->hbnr.max; - break; - - case NS_BUFTYPE_IOVEC: - pl.count = card->iovpool.count; - pl.level.min = card->iovnr.min; - pl.level.init = card->iovnr.init; - pl.level.max = card->iovnr.max; - break; - - default: - return -ENOIOCTLCMD; - - } - if (!copy_to_user((pool_levels __user *) arg, &pl, sizeof(pl))) - return (sizeof(pl)); - else - return -EFAULT; - - case NS_SETBUFLEV: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (copy_from_user(&pl, (pool_levels __user *) arg, sizeof(pl))) - return -EFAULT; - if (pl.level.min >= pl.level.init - || pl.level.init >= pl.level.max) - return -EINVAL; - if (pl.level.min == 0) - return -EINVAL; - switch (pl.buftype) { - case NS_BUFTYPE_SMALL: - if (pl.level.max > TOP_SB) - return -EINVAL; - card->sbnr.min = pl.level.min; - card->sbnr.init = pl.level.init; - card->sbnr.max = pl.level.max; - break; - - case NS_BUFTYPE_LARGE: - if (pl.level.max > TOP_LB) - return -EINVAL; - card->lbnr.min = pl.level.min; - card->lbnr.init = pl.level.init; - card->lbnr.max = pl.level.max; - break; - - case NS_BUFTYPE_HUGE: - if (pl.level.max > TOP_HB) - return -EINVAL; - card->hbnr.min = pl.level.min; - card->hbnr.init = pl.level.init; - card->hbnr.max = pl.level.max; - break; - - case NS_BUFTYPE_IOVEC: - if (pl.level.max > TOP_IOVB) - return -EINVAL; - card->iovnr.min = pl.level.min; - card->iovnr.init = pl.level.init; - card->iovnr.max = pl.level.max; - break; - - default: - return -EINVAL; - - } - return 0; - - case NS_ADJBUFLEV: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - btype = (long)arg; /* a long is the same size as a pointer or bigger */ - switch (btype) { - case NS_BUFTYPE_SMALL: - while (card->sbfqc < card->sbnr.init) { - struct sk_buff *sb; - - sb = __dev_alloc_skb(NS_SMSKBSIZE, GFP_KERNEL); - if (sb == NULL) - return -ENOMEM; - NS_PRV_BUFTYPE(sb) = BUF_SM; - skb_queue_tail(&card->sbpool.queue, sb); - skb_reserve(sb, NS_AAL0_HEADER); - push_rxbufs(card, sb); - } - break; - - case NS_BUFTYPE_LARGE: - while (card->lbfqc < card->lbnr.init) { - struct sk_buff *lb; - - lb = __dev_alloc_skb(NS_LGSKBSIZE, GFP_KERNEL); - if (lb == NULL) - return -ENOMEM; - NS_PRV_BUFTYPE(lb) = BUF_LG; - skb_queue_tail(&card->lbpool.queue, lb); - skb_reserve(lb, NS_SMBUFSIZE); - push_rxbufs(card, lb); - } - break; - - case NS_BUFTYPE_HUGE: - while (card->hbpool.count > card->hbnr.init) { - struct sk_buff *hb; - - spin_lock_irqsave(&card->int_lock, flags); - hb = skb_dequeue(&card->hbpool.queue); - card->hbpool.count--; - spin_unlock_irqrestore(&card->int_lock, flags); - if (hb == NULL) - printk - ("nicstar%d: huge buffer count inconsistent.\n", - card->index); - else - dev_kfree_skb_any(hb); - - } - while (card->hbpool.count < card->hbnr.init) { - struct sk_buff *hb; - - hb = __dev_alloc_skb(NS_HBUFSIZE, GFP_KERNEL); - if (hb == NULL) - return -ENOMEM; - NS_PRV_BUFTYPE(hb) = BUF_NONE; - spin_lock_irqsave(&card->int_lock, flags); - skb_queue_tail(&card->hbpool.queue, hb); - card->hbpool.count++; - spin_unlock_irqrestore(&card->int_lock, flags); - } - break; - - case NS_BUFTYPE_IOVEC: - while (card->iovpool.count > card->iovnr.init) { - struct sk_buff *iovb; - - spin_lock_irqsave(&card->int_lock, flags); - iovb = skb_dequeue(&card->iovpool.queue); - card->iovpool.count--; - spin_unlock_irqrestore(&card->int_lock, flags); - if (iovb == NULL) - printk - ("nicstar%d: iovec buffer count inconsistent.\n", - card->index); - else - dev_kfree_skb_any(iovb); - - } - while (card->iovpool.count < card->iovnr.init) { - struct sk_buff *iovb; - - iovb = alloc_skb(NS_IOVBUFSIZE, GFP_KERNEL); - if (iovb == NULL) - return -ENOMEM; - NS_PRV_BUFTYPE(iovb) = BUF_NONE; - spin_lock_irqsave(&card->int_lock, flags); - skb_queue_tail(&card->iovpool.queue, iovb); - card->iovpool.count++; - spin_unlock_irqrestore(&card->int_lock, flags); - } - break; - - default: - return -EINVAL; - - } - return 0; - - default: - if (dev->phy && dev->phy->ioctl) { - return dev->phy->ioctl(dev, cmd, arg); - } else { - printk("nicstar%d: %s == NULL \n", card->index, - dev->phy ? "dev->phy->ioctl" : "dev->phy"); - return -ENOIOCTLCMD; - } - } -} - -#ifdef EXTRA_DEBUG -static void which_list(ns_dev * card, struct sk_buff *skb) -{ - printk("skb buf_type: 0x%08x\n", NS_PRV_BUFTYPE(skb)); -} -#endif /* EXTRA_DEBUG */ - -static void ns_poll(struct timer_list *unused) -{ - int i; - ns_dev *card; - unsigned long flags; - u32 stat_r, stat_w; - - PRINTK("nicstar: Entering ns_poll().\n"); - for (i = 0; i < num_cards; i++) { - card = cards[i]; - if (!spin_trylock_irqsave(&card->int_lock, flags)) { - /* Probably it isn't worth spinning */ - continue; - } - - stat_w = 0; - stat_r = readl(card->membase + STAT); - if (stat_r & NS_STAT_TSIF) - stat_w |= NS_STAT_TSIF; - if (stat_r & NS_STAT_EOPDU) - stat_w |= NS_STAT_EOPDU; - - process_tsq(card); - process_rsq(card); - - writel(stat_w, card->membase + STAT); - spin_unlock_irqrestore(&card->int_lock, flags); - } - mod_timer(&ns_timer, jiffies + NS_POLL_PERIOD); - PRINTK("nicstar: Leaving ns_poll().\n"); -} - -static void ns_phy_put(struct atm_dev *dev, unsigned char value, - unsigned long addr) -{ - ns_dev *card; - unsigned long flags; - - card = dev->dev_data; - spin_lock_irqsave(&card->res_lock, flags); - while (CMD_BUSY(card)) ; - writel((u32) value, card->membase + DR0); - writel(NS_CMD_WRITE_UTILITY | 0x00000200 | (addr & 0x000000FF), - card->membase + CMD); - spin_unlock_irqrestore(&card->res_lock, flags); -} - -static unsigned char ns_phy_get(struct atm_dev *dev, unsigned long addr) -{ - ns_dev *card; - unsigned long flags; - u32 data; - - card = dev->dev_data; - spin_lock_irqsave(&card->res_lock, flags); - while (CMD_BUSY(card)) ; - writel(NS_CMD_READ_UTILITY | 0x00000200 | (addr & 0x000000FF), - card->membase + CMD); - while (CMD_BUSY(card)) ; - data = readl(card->membase + DR0) & 0x000000FF; - spin_unlock_irqrestore(&card->res_lock, flags); - return (unsigned char)data; -} - -module_init(nicstar_init); -module_exit(nicstar_cleanup); diff --git a/drivers/atm/nicstar.h b/drivers/atm/nicstar.h deleted file mode 100644 index 1b7f1dfc1735..000000000000 --- a/drivers/atm/nicstar.h +++ /dev/null @@ -1,759 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * nicstar.h - * - * Header file for the nicstar device driver. - * - * Author: Rui Prior (rprior@inescn.pt) - * PowerPC support by Jay Talbott (jay_talbott@mcg.mot.com) April 1999 - * - * (C) INESC 1998 - */ - -#ifndef _LINUX_NICSTAR_H_ -#define _LINUX_NICSTAR_H_ - -/* Includes */ - -#include -#include -#include -#include -#include -#include -#include - -/* Options */ - -#define NS_MAX_CARDS 4 /* Maximum number of NICStAR based cards - controlled by the device driver. Must - be <= 5 */ - -#undef RCQ_SUPPORT /* Do not define this for now */ - -#define NS_TST_NUM_ENTRIES 2340 /* + 1 for return */ -#define NS_TST_RESERVED 340 /* N. entries reserved for UBR/ABR/VBR */ - -#define NS_SMBUFSIZE 48 /* 48, 96, 240 or 2048 */ -#define NS_LGBUFSIZE 16384 /* 2048, 4096, 8192 or 16384 */ -#define NS_RSQSIZE 8192 /* 2048, 4096 or 8192 */ -#define NS_VPIBITS 2 /* 0, 1, 2, or 8 */ - -#define NS_MAX_RCTSIZE 4096 /* Number of entries. 4096 or 16384. - Define 4096 only if (all) your card(s) - have 32K x 32bit SRAM, in which case - setting this to 16384 will just waste a - lot of memory. - Setting this to 4096 for a card with - 128K x 32bit SRAM will limit the maximum - VCI. */ - - /*#define NS_PCI_LATENCY 64*//* Must be a multiple of 32 */ - - /* Number of buffers initially allocated */ -#define NUM_SB 32 /* Must be even */ -#define NUM_LB 24 /* Must be even */ -#define NUM_HB 8 /* Pre-allocated huge buffers */ -#define NUM_IOVB 48 /* Iovec buffers */ - - /* Lower level for count of buffers */ -#define MIN_SB 8 /* Must be even */ -#define MIN_LB 8 /* Must be even */ -#define MIN_HB 6 -#define MIN_IOVB 8 - - /* Upper level for count of buffers */ -#define MAX_SB 64 /* Must be even, <= 508 */ -#define MAX_LB 48 /* Must be even, <= 508 */ -#define MAX_HB 10 -#define MAX_IOVB 80 - - /* These are the absolute maximum allowed for the ioctl() */ -#define TOP_SB 256 /* Must be even, <= 508 */ -#define TOP_LB 128 /* Must be even, <= 508 */ -#define TOP_HB 64 -#define TOP_IOVB 256 - -#define MAX_TBD_PER_VC 1 /* Number of TBDs before a TSR */ -#define MAX_TBD_PER_SCQ 10 /* Only meaningful for variable rate SCQs */ - -#undef ENABLE_TSQFIE - -#define SCQFULL_TIMEOUT (5 * HZ) - -#define NS_POLL_PERIOD (HZ) - -#define PCR_TOLERANCE (1.0001) - -/* ESI stuff */ - -#define NICSTAR_EPROM_MAC_ADDR_OFFSET 0x6C -#define NICSTAR_EPROM_MAC_ADDR_OFFSET_ALT 0xF6 - -/* #defines */ - -#define NS_IOREMAP_SIZE 4096 - -/* - * BUF_XX distinguish the Rx buffers depending on their (small/large) size. - * BUG_SM and BUG_LG are both used by the driver and the device. - * BUF_NONE is only used by the driver. - */ -#define BUF_SM 0x00000000 /* These two are used for push_rxbufs() */ -#define BUF_LG 0x00000001 /* CMD, Write_FreeBufQ, LBUF bit */ -#define BUF_NONE 0xffffffff /* Software only: */ - -#define NS_HBUFSIZE 65568 /* Size of max. AAL5 PDU */ -#define NS_MAX_IOVECS (2 + (65568 - NS_SMBUFSIZE) / \ - (NS_LGBUFSIZE - (NS_LGBUFSIZE % 48))) -#define NS_IOVBUFSIZE (NS_MAX_IOVECS * (sizeof(struct iovec))) - -#define NS_SMBUFSIZE_USABLE (NS_SMBUFSIZE - NS_SMBUFSIZE % 48) -#define NS_LGBUFSIZE_USABLE (NS_LGBUFSIZE - NS_LGBUFSIZE % 48) - -#define NS_AAL0_HEADER (ATM_AAL0_SDU - ATM_CELL_PAYLOAD) /* 4 bytes */ - -#define NS_SMSKBSIZE (NS_SMBUFSIZE + NS_AAL0_HEADER) -#define NS_LGSKBSIZE (NS_SMBUFSIZE + NS_LGBUFSIZE) - -/* NICStAR structures located in host memory */ - -/* - * RSQ - Receive Status Queue - * - * Written by the NICStAR, read by the device driver. - */ - -typedef struct ns_rsqe { - u32 word_1; - u32 buffer_handle; - u32 final_aal5_crc32; - u32 word_4; -} ns_rsqe; - -#define ns_rsqe_vpi(ns_rsqep) \ - ((le32_to_cpu((ns_rsqep)->word_1) & 0x00FF0000) >> 16) -#define ns_rsqe_vci(ns_rsqep) \ - (le32_to_cpu((ns_rsqep)->word_1) & 0x0000FFFF) - -#define NS_RSQE_VALID 0x80000000 -#define NS_RSQE_NZGFC 0x00004000 -#define NS_RSQE_EOPDU 0x00002000 -#define NS_RSQE_BUFSIZE 0x00001000 -#define NS_RSQE_CONGESTION 0x00000800 -#define NS_RSQE_CLP 0x00000400 -#define NS_RSQE_CRCERR 0x00000200 - -#define NS_RSQE_BUFSIZE_SM 0x00000000 -#define NS_RSQE_BUFSIZE_LG 0x00001000 - -#define ns_rsqe_valid(ns_rsqep) \ - (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_VALID) -#define ns_rsqe_nzgfc(ns_rsqep) \ - (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_NZGFC) -#define ns_rsqe_eopdu(ns_rsqep) \ - (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_EOPDU) -#define ns_rsqe_bufsize(ns_rsqep) \ - (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_BUFSIZE) -#define ns_rsqe_congestion(ns_rsqep) \ - (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_CONGESTION) -#define ns_rsqe_clp(ns_rsqep) \ - (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_CLP) -#define ns_rsqe_crcerr(ns_rsqep) \ - (le32_to_cpu((ns_rsqep)->word_4) & NS_RSQE_CRCERR) - -#define ns_rsqe_cellcount(ns_rsqep) \ - (le32_to_cpu((ns_rsqep)->word_4) & 0x000001FF) -#define ns_rsqe_init(ns_rsqep) \ - ((ns_rsqep)->word_4 = cpu_to_le32(0x00000000)) - -#define NS_RSQ_NUM_ENTRIES (NS_RSQSIZE / 16) -#define NS_RSQ_ALIGNMENT NS_RSQSIZE - -/* - * RCQ - Raw Cell Queue - * - * Written by the NICStAR, read by the device driver. - */ - -typedef struct cell_payload { - u32 word[12]; -} cell_payload; - -typedef struct ns_rcqe { - u32 word_1; - u32 word_2; - u32 word_3; - u32 word_4; - cell_payload payload; -} ns_rcqe; - -#define NS_RCQE_SIZE 64 /* bytes */ - -#define ns_rcqe_islast(ns_rcqep) \ - (le32_to_cpu((ns_rcqep)->word_2) != 0x00000000) -#define ns_rcqe_cellheader(ns_rcqep) \ - (le32_to_cpu((ns_rcqep)->word_1)) -#define ns_rcqe_nextbufhandle(ns_rcqep) \ - (le32_to_cpu((ns_rcqep)->word_2)) - -/* - * SCQ - Segmentation Channel Queue - * - * Written by the device driver, read by the NICStAR. - */ - -typedef struct ns_scqe { - u32 word_1; - u32 word_2; - u32 word_3; - u32 word_4; -} ns_scqe; - - /* NOTE: SCQ entries can be either a TBD (Transmit Buffer Descriptors) - or TSR (Transmit Status Requests) */ - -#define NS_SCQE_TYPE_TBD 0x00000000 -#define NS_SCQE_TYPE_TSR 0x80000000 - -#define NS_TBD_EOPDU 0x40000000 -#define NS_TBD_AAL0 0x00000000 -#define NS_TBD_AAL34 0x04000000 -#define NS_TBD_AAL5 0x08000000 - -#define NS_TBD_VPI_MASK 0x0FF00000 -#define NS_TBD_VCI_MASK 0x000FFFF0 -#define NS_TBD_VC_MASK (NS_TBD_VPI_MASK | NS_TBD_VCI_MASK) - -#define NS_TBD_VPI_SHIFT 20 -#define NS_TBD_VCI_SHIFT 4 - -#define ns_tbd_mkword_1(flags, m, n, buflen) \ - (cpu_to_le32((flags) | (m) << 23 | (n) << 16 | (buflen))) -#define ns_tbd_mkword_1_novbr(flags, buflen) \ - (cpu_to_le32((flags) | (buflen) | 0x00810000)) -#define ns_tbd_mkword_3(control, pdulen) \ - (cpu_to_le32((control) << 16 | (pdulen))) -#define ns_tbd_mkword_4(gfc, vpi, vci, pt, clp) \ - (cpu_to_le32((gfc) << 28 | (vpi) << 20 | (vci) << 4 | (pt) << 1 | (clp))) - -#define NS_TSR_INTENABLE 0x20000000 - -#define NS_TSR_SCDISVBR 0xFFFF /* Use as scdi for VBR SCD */ - -#define ns_tsr_mkword_1(flags) \ - (cpu_to_le32(NS_SCQE_TYPE_TSR | (flags))) -#define ns_tsr_mkword_2(scdi, scqi) \ - (cpu_to_le32((scdi) << 16 | 0x00008000 | (scqi))) - -#define ns_scqe_is_tsr(ns_scqep) \ - (le32_to_cpu((ns_scqep)->word_1) & NS_SCQE_TYPE_TSR) - -#define VBR_SCQ_NUM_ENTRIES 512 -#define VBR_SCQSIZE 8192 -#define CBR_SCQ_NUM_ENTRIES 64 -#define CBR_SCQSIZE 1024 - -#define NS_SCQE_SIZE 16 - -/* - * TSQ - Transmit Status Queue - * - * Written by the NICStAR, read by the device driver. - */ - -typedef struct ns_tsi { - u32 word_1; - u32 word_2; -} ns_tsi; - - /* NOTE: The first word can be a status word copied from the TSR which - originated the TSI, or a timer overflow indicator. In this last - case, the value of the first word is all zeroes. */ - -#define NS_TSI_EMPTY 0x80000000 -#define NS_TSI_TIMESTAMP_MASK 0x00FFFFFF - -#define ns_tsi_isempty(ns_tsip) \ - (le32_to_cpu((ns_tsip)->word_2) & NS_TSI_EMPTY) -#define ns_tsi_gettimestamp(ns_tsip) \ - (le32_to_cpu((ns_tsip)->word_2) & NS_TSI_TIMESTAMP_MASK) - -#define ns_tsi_init(ns_tsip) \ - ((ns_tsip)->word_2 = cpu_to_le32(NS_TSI_EMPTY)) - -#define NS_TSQSIZE 8192 -#define NS_TSQ_NUM_ENTRIES 1024 -#define NS_TSQ_ALIGNMENT 8192 - -#define NS_TSI_SCDISVBR NS_TSR_SCDISVBR - -#define ns_tsi_tmrof(ns_tsip) \ - (le32_to_cpu((ns_tsip)->word_1) == 0x00000000) -#define ns_tsi_getscdindex(ns_tsip) \ - ((le32_to_cpu((ns_tsip)->word_1) & 0xFFFF0000) >> 16) -#define ns_tsi_getscqpos(ns_tsip) \ - (le32_to_cpu((ns_tsip)->word_1) & 0x00007FFF) - -/* NICStAR structures located in local SRAM */ - -/* - * RCT - Receive Connection Table - * - * Written by both the NICStAR and the device driver. - */ - -typedef struct ns_rcte { - u32 word_1; - u32 buffer_handle; - u32 dma_address; - u32 aal5_crc32; -} ns_rcte; - -#define NS_RCTE_BSFB 0x00200000 /* Rev. D only */ -#define NS_RCTE_NZGFC 0x00100000 -#define NS_RCTE_CONNECTOPEN 0x00080000 -#define NS_RCTE_AALMASK 0x00070000 -#define NS_RCTE_AAL0 0x00000000 -#define NS_RCTE_AAL34 0x00010000 -#define NS_RCTE_AAL5 0x00020000 -#define NS_RCTE_RCQ 0x00030000 -#define NS_RCTE_RAWCELLINTEN 0x00008000 -#define NS_RCTE_RXCONSTCELLADDR 0x00004000 -#define NS_RCTE_BUFFVALID 0x00002000 -#define NS_RCTE_FBDSIZE 0x00001000 -#define NS_RCTE_EFCI 0x00000800 -#define NS_RCTE_CLP 0x00000400 -#define NS_RCTE_CRCERROR 0x00000200 -#define NS_RCTE_CELLCOUNT_MASK 0x000001FF - -#define NS_RCTE_FBDSIZE_SM 0x00000000 -#define NS_RCTE_FBDSIZE_LG 0x00001000 - -#define NS_RCT_ENTRY_SIZE 4 /* Number of dwords */ - - /* NOTE: We could make macros to contruct the first word of the RCTE, - but that doesn't seem to make much sense... */ - -/* - * FBD - Free Buffer Descriptor - * - * Written by the device driver using via the command register. - */ - -typedef struct ns_fbd { - u32 buffer_handle; - u32 dma_address; -} ns_fbd; - -/* - * TST - Transmit Schedule Table - * - * Written by the device driver. - */ - -typedef u32 ns_tste; - -#define NS_TST_OPCODE_MASK 0x60000000 - -#define NS_TST_OPCODE_NULL 0x00000000 /* Insert null cell */ -#define NS_TST_OPCODE_FIXED 0x20000000 /* Cell from a fixed rate channel */ -#define NS_TST_OPCODE_VARIABLE 0x40000000 -#define NS_TST_OPCODE_END 0x60000000 /* Jump */ - -#define ns_tste_make(opcode, sramad) (opcode | sramad) - - /* NOTE: - - - When the opcode is FIXED, sramad specifies the SRAM address of the - SCD for that fixed rate channel. - - When the opcode is END, sramad specifies the SRAM address of the - location of the next TST entry to read. - */ - -/* - * SCD - Segmentation Channel Descriptor - * - * Written by both the device driver and the NICStAR - */ - -typedef struct ns_scd { - u32 word_1; - u32 word_2; - u32 partial_aal5_crc; - u32 reserved; - ns_scqe cache_a; - ns_scqe cache_b; -} ns_scd; - -#define NS_SCD_BASE_MASK_VAR 0xFFFFE000 /* Variable rate */ -#define NS_SCD_BASE_MASK_FIX 0xFFFFFC00 /* Fixed rate */ -#define NS_SCD_TAIL_MASK_VAR 0x00001FF0 -#define NS_SCD_TAIL_MASK_FIX 0x000003F0 -#define NS_SCD_HEAD_MASK_VAR 0x00001FF0 -#define NS_SCD_HEAD_MASK_FIX 0x000003F0 -#define NS_SCD_XMITFOREVER 0x02000000 - - /* NOTE: There are other fields in word 2 of the SCD, but as they should - not be needed in the device driver they are not defined here. */ - -/* NICStAR local SRAM memory map */ - -#define NS_RCT 0x00000 -#define NS_RCT_32_END 0x03FFF -#define NS_RCT_128_END 0x0FFFF -#define NS_UNUSED_32 0x04000 -#define NS_UNUSED_128 0x10000 -#define NS_UNUSED_END 0x1BFFF -#define NS_TST_FRSCD 0x1C000 -#define NS_TST_FRSCD_END 0x1E7DB -#define NS_VRSCD2 0x1E7DC -#define NS_VRSCD2_END 0x1E7E7 -#define NS_VRSCD1 0x1E7E8 -#define NS_VRSCD1_END 0x1E7F3 -#define NS_VRSCD0 0x1E7F4 -#define NS_VRSCD0_END 0x1E7FF -#define NS_RXFIFO 0x1E800 -#define NS_RXFIFO_END 0x1F7FF -#define NS_SMFBQ 0x1F800 -#define NS_SMFBQ_END 0x1FBFF -#define NS_LGFBQ 0x1FC00 -#define NS_LGFBQ_END 0x1FFFF - -/* NISCtAR operation registers */ - -/* See Section 3.4 of `IDT77211 NICStAR User Manual' from www.idt.com */ - -enum ns_regs { - DR0 = 0x00, /* Data Register 0 R/W */ - DR1 = 0x04, /* Data Register 1 W */ - DR2 = 0x08, /* Data Register 2 W */ - DR3 = 0x0C, /* Data Register 3 W */ - CMD = 0x10, /* Command W */ - CFG = 0x14, /* Configuration R/W */ - STAT = 0x18, /* Status R/W */ - RSQB = 0x1C, /* Receive Status Queue Base W */ - RSQT = 0x20, /* Receive Status Queue Tail R */ - RSQH = 0x24, /* Receive Status Queue Head W */ - CDC = 0x28, /* Cell Drop Counter R/clear */ - VPEC = 0x2C, /* VPI/VCI Lookup Error Count R/clear */ - ICC = 0x30, /* Invalid Cell Count R/clear */ - RAWCT = 0x34, /* Raw Cell Tail R */ - TMR = 0x38, /* Timer R */ - TSTB = 0x3C, /* Transmit Schedule Table Base R/W */ - TSQB = 0x40, /* Transmit Status Queue Base W */ - TSQT = 0x44, /* Transmit Status Queue Tail R */ - TSQH = 0x48, /* Transmit Status Queue Head W */ - GP = 0x4C, /* General Purpose R/W */ - VPM = 0x50 /* VPI/VCI Mask W */ -}; - -/* NICStAR commands issued to the CMD register */ - -/* Top 4 bits are command opcode, lower 28 are parameters. */ - -#define NS_CMD_NO_OPERATION 0x00000000 - /* params always 0 */ - -#define NS_CMD_OPENCLOSE_CONNECTION 0x20000000 - /* b19{1=open,0=close} b18-2{SRAM addr} */ - -#define NS_CMD_WRITE_SRAM 0x40000000 - /* b18-2{SRAM addr} b1-0{burst size} */ - -#define NS_CMD_READ_SRAM 0x50000000 - /* b18-2{SRAM addr} */ - -#define NS_CMD_WRITE_FREEBUFQ 0x60000000 - /* b0{large buf indicator} */ - -#define NS_CMD_READ_UTILITY 0x80000000 - /* b8{1=select UTL_CS1} b9{1=select UTL_CS0} b7-0{bus addr} */ - -#define NS_CMD_WRITE_UTILITY 0x90000000 - /* b8{1=select UTL_CS1} b9{1=select UTL_CS0} b7-0{bus addr} */ - -#define NS_CMD_OPEN_CONNECTION (NS_CMD_OPENCLOSE_CONNECTION | 0x00080000) -#define NS_CMD_CLOSE_CONNECTION NS_CMD_OPENCLOSE_CONNECTION - -/* NICStAR configuration bits */ - -#define NS_CFG_SWRST 0x80000000 /* Software Reset */ -#define NS_CFG_RXPATH 0x20000000 /* Receive Path Enable */ -#define NS_CFG_SMBUFSIZE_MASK 0x18000000 /* Small Receive Buffer Size */ -#define NS_CFG_LGBUFSIZE_MASK 0x06000000 /* Large Receive Buffer Size */ -#define NS_CFG_EFBIE 0x01000000 /* Empty Free Buffer Queue - Interrupt Enable */ -#define NS_CFG_RSQSIZE_MASK 0x00C00000 /* Receive Status Queue Size */ -#define NS_CFG_ICACCEPT 0x00200000 /* Invalid Cell Accept */ -#define NS_CFG_IGNOREGFC 0x00100000 /* Ignore General Flow Control */ -#define NS_CFG_VPIBITS_MASK 0x000C0000 /* VPI/VCI Bits Size Select */ -#define NS_CFG_RCTSIZE_MASK 0x00030000 /* Receive Connection Table Size */ -#define NS_CFG_VCERRACCEPT 0x00008000 /* VPI/VCI Error Cell Accept */ -#define NS_CFG_RXINT_MASK 0x00007000 /* End of Receive PDU Interrupt - Handling */ -#define NS_CFG_RAWIE 0x00000800 /* Raw Cell Qu' Interrupt Enable */ -#define NS_CFG_RSQAFIE 0x00000400 /* Receive Queue Almost Full - Interrupt Enable */ -#define NS_CFG_RXRM 0x00000200 /* Receive RM Cells */ -#define NS_CFG_TMRROIE 0x00000080 /* Timer Roll Over Interrupt - Enable */ -#define NS_CFG_TXEN 0x00000020 /* Transmit Operation Enable */ -#define NS_CFG_TXIE 0x00000010 /* Transmit Status Interrupt - Enable */ -#define NS_CFG_TXURIE 0x00000008 /* Transmit Under-run Interrupt - Enable */ -#define NS_CFG_UMODE 0x00000004 /* Utopia Mode (cell/byte) Select */ -#define NS_CFG_TSQFIE 0x00000002 /* Transmit Status Queue Full - Interrupt Enable */ -#define NS_CFG_PHYIE 0x00000001 /* PHY Interrupt Enable */ - -#define NS_CFG_SMBUFSIZE_48 0x00000000 -#define NS_CFG_SMBUFSIZE_96 0x08000000 -#define NS_CFG_SMBUFSIZE_240 0x10000000 -#define NS_CFG_SMBUFSIZE_2048 0x18000000 - -#define NS_CFG_LGBUFSIZE_2048 0x00000000 -#define NS_CFG_LGBUFSIZE_4096 0x02000000 -#define NS_CFG_LGBUFSIZE_8192 0x04000000 -#define NS_CFG_LGBUFSIZE_16384 0x06000000 - -#define NS_CFG_RSQSIZE_2048 0x00000000 -#define NS_CFG_RSQSIZE_4096 0x00400000 -#define NS_CFG_RSQSIZE_8192 0x00800000 - -#define NS_CFG_VPIBITS_0 0x00000000 -#define NS_CFG_VPIBITS_1 0x00040000 -#define NS_CFG_VPIBITS_2 0x00080000 -#define NS_CFG_VPIBITS_8 0x000C0000 - -#define NS_CFG_RCTSIZE_4096_ENTRIES 0x00000000 -#define NS_CFG_RCTSIZE_8192_ENTRIES 0x00010000 -#define NS_CFG_RCTSIZE_16384_ENTRIES 0x00020000 - -#define NS_CFG_RXINT_NOINT 0x00000000 -#define NS_CFG_RXINT_NODELAY 0x00001000 -#define NS_CFG_RXINT_314US 0x00002000 -#define NS_CFG_RXINT_624US 0x00003000 -#define NS_CFG_RXINT_899US 0x00004000 - -/* NICStAR STATus bits */ - -#define NS_STAT_SFBQC_MASK 0xFF000000 /* hi 8 bits Small Buffer Queue Count */ -#define NS_STAT_LFBQC_MASK 0x00FF0000 /* hi 8 bits Large Buffer Queue Count */ -#define NS_STAT_TSIF 0x00008000 /* Transmit Status Queue Indicator */ -#define NS_STAT_TXICP 0x00004000 /* Transmit Incomplete PDU */ -#define NS_STAT_TSQF 0x00001000 /* Transmit Status Queue Full */ -#define NS_STAT_TMROF 0x00000800 /* Timer Overflow */ -#define NS_STAT_PHYI 0x00000400 /* PHY Device Interrupt */ -#define NS_STAT_CMDBZ 0x00000200 /* Command Busy */ -#define NS_STAT_SFBQF 0x00000100 /* Small Buffer Queue Full */ -#define NS_STAT_LFBQF 0x00000080 /* Large Buffer Queue Full */ -#define NS_STAT_RSQF 0x00000040 /* Receive Status Queue Full */ -#define NS_STAT_EOPDU 0x00000020 /* End of PDU */ -#define NS_STAT_RAWCF 0x00000010 /* Raw Cell Flag */ -#define NS_STAT_SFBQE 0x00000008 /* Small Buffer Queue Empty */ -#define NS_STAT_LFBQE 0x00000004 /* Large Buffer Queue Empty */ -#define NS_STAT_RSQAF 0x00000002 /* Receive Status Queue Almost Full */ - -#define ns_stat_sfbqc_get(stat) (((stat) & NS_STAT_SFBQC_MASK) >> 23) -#define ns_stat_lfbqc_get(stat) (((stat) & NS_STAT_LFBQC_MASK) >> 15) - -/* #defines which depend on other #defines */ - -#define NS_TST0 NS_TST_FRSCD -#define NS_TST1 (NS_TST_FRSCD + NS_TST_NUM_ENTRIES + 1) - -#define NS_FRSCD (NS_TST1 + NS_TST_NUM_ENTRIES + 1) -#define NS_FRSCD_SIZE 12 /* 12 dwords */ -#define NS_FRSCD_NUM ((NS_TST_FRSCD_END + 1 - NS_FRSCD) / NS_FRSCD_SIZE) - -#if (NS_SMBUFSIZE == 48) -#define NS_CFG_SMBUFSIZE NS_CFG_SMBUFSIZE_48 -#elif (NS_SMBUFSIZE == 96) -#define NS_CFG_SMBUFSIZE NS_CFG_SMBUFSIZE_96 -#elif (NS_SMBUFSIZE == 240) -#define NS_CFG_SMBUFSIZE NS_CFG_SMBUFSIZE_240 -#elif (NS_SMBUFSIZE == 2048) -#define NS_CFG_SMBUFSIZE NS_CFG_SMBUFSIZE_2048 -#else -#error NS_SMBUFSIZE is incorrect in nicstar.h -#endif /* NS_SMBUFSIZE */ - -#if (NS_LGBUFSIZE == 2048) -#define NS_CFG_LGBUFSIZE NS_CFG_LGBUFSIZE_2048 -#elif (NS_LGBUFSIZE == 4096) -#define NS_CFG_LGBUFSIZE NS_CFG_LGBUFSIZE_4096 -#elif (NS_LGBUFSIZE == 8192) -#define NS_CFG_LGBUFSIZE NS_CFG_LGBUFSIZE_8192 -#elif (NS_LGBUFSIZE == 16384) -#define NS_CFG_LGBUFSIZE NS_CFG_LGBUFSIZE_16384 -#else -#error NS_LGBUFSIZE is incorrect in nicstar.h -#endif /* NS_LGBUFSIZE */ - -#if (NS_RSQSIZE == 2048) -#define NS_CFG_RSQSIZE NS_CFG_RSQSIZE_2048 -#elif (NS_RSQSIZE == 4096) -#define NS_CFG_RSQSIZE NS_CFG_RSQSIZE_4096 -#elif (NS_RSQSIZE == 8192) -#define NS_CFG_RSQSIZE NS_CFG_RSQSIZE_8192 -#else -#error NS_RSQSIZE is incorrect in nicstar.h -#endif /* NS_RSQSIZE */ - -#if (NS_VPIBITS == 0) -#define NS_CFG_VPIBITS NS_CFG_VPIBITS_0 -#elif (NS_VPIBITS == 1) -#define NS_CFG_VPIBITS NS_CFG_VPIBITS_1 -#elif (NS_VPIBITS == 2) -#define NS_CFG_VPIBITS NS_CFG_VPIBITS_2 -#elif (NS_VPIBITS == 8) -#define NS_CFG_VPIBITS NS_CFG_VPIBITS_8 -#else -#error NS_VPIBITS is incorrect in nicstar.h -#endif /* NS_VPIBITS */ - -#ifdef RCQ_SUPPORT -#define NS_CFG_RAWIE_OPT NS_CFG_RAWIE -#else -#define NS_CFG_RAWIE_OPT 0x00000000 -#endif /* RCQ_SUPPORT */ - -#ifdef ENABLE_TSQFIE -#define NS_CFG_TSQFIE_OPT NS_CFG_TSQFIE -#else -#define NS_CFG_TSQFIE_OPT 0x00000000 -#endif /* ENABLE_TSQFIE */ - -/* PCI stuff */ - -#ifndef PCI_VENDOR_ID_IDT -#define PCI_VENDOR_ID_IDT 0x111D -#endif /* PCI_VENDOR_ID_IDT */ - -#ifndef PCI_DEVICE_ID_IDT_IDT77201 -#define PCI_DEVICE_ID_IDT_IDT77201 0x0001 -#endif /* PCI_DEVICE_ID_IDT_IDT77201 */ - -/* Device driver structures */ - -struct ns_skb_prv { - u32 buf_type; /* BUF_SM/BUF_LG/BUF_NONE */ - u32 dma; - int iovcnt; -}; - -#define NS_PRV_BUFTYPE(skb) \ - (((struct ns_skb_prv *)(ATM_SKB(skb)+1))->buf_type) -#define NS_PRV_DMA(skb) \ - (((struct ns_skb_prv *)(ATM_SKB(skb)+1))->dma) -#define NS_PRV_IOVCNT(skb) \ - (((struct ns_skb_prv *)(ATM_SKB(skb)+1))->iovcnt) - -typedef struct tsq_info { - void *org; - dma_addr_t dma; - ns_tsi *base; - ns_tsi *next; - ns_tsi *last; -} tsq_info; - -typedef struct scq_info { - void *org; - dma_addr_t dma; - ns_scqe *base; - ns_scqe *last; - ns_scqe *next; - volatile ns_scqe *tail; /* Not related to the nicstar register */ - unsigned num_entries; - struct sk_buff **skb; /* Pointer to an array of pointers - to the sk_buffs used for tx */ - u32 scd; /* SRAM address of the corresponding - SCD */ - int tbd_count; /* Only meaningful on variable rate */ - wait_queue_head_t scqfull_waitq; - volatile char full; /* SCQ full indicator */ - spinlock_t lock; /* SCQ spinlock */ -} scq_info; - -typedef struct rsq_info { - void *org; - dma_addr_t dma; - ns_rsqe *base; - ns_rsqe *next; - ns_rsqe *last; -} rsq_info; - -typedef struct skb_pool { - volatile int count; /* number of buffers in the queue */ - struct sk_buff_head queue; -} skb_pool; - -/* NOTE: for small and large buffer pools, the count is not used, as the - actual value used for buffer management is the one read from the - card. */ - -typedef struct vc_map { - volatile unsigned int tx:1; /* TX vc? */ - volatile unsigned int rx:1; /* RX vc? */ - struct atm_vcc *tx_vcc, *rx_vcc; - struct sk_buff *rx_iov; /* RX iovector skb */ - scq_info *scq; /* To keep track of the SCQ */ - u32 cbr_scd; /* SRAM address of the corresponding - SCD. 0x00000000 for UBR/VBR/ABR */ - int tbd_count; -} vc_map; - -typedef struct ns_dev { - int index; /* Card ID to the device driver */ - int sram_size; /* In k x 32bit words. 32 or 128 */ - void __iomem *membase; /* Card's memory base address */ - unsigned long max_pcr; - int rct_size; /* Number of entries */ - int vpibits; - int vcibits; - struct pci_dev *pcidev; - struct idr idr; - struct atm_dev *atmdev; - tsq_info tsq; - rsq_info rsq; - scq_info *scq0, *scq1, *scq2; /* VBR SCQs */ - skb_pool sbpool; /* Small buffers */ - skb_pool lbpool; /* Large buffers */ - skb_pool hbpool; /* Pre-allocated huge buffers */ - skb_pool iovpool; /* iovector buffers */ - volatile int efbie; /* Empty free buf. queue int. enabled */ - volatile u32 tst_addr; /* SRAM address of the TST in use */ - volatile int tst_free_entries; - vc_map vcmap[NS_MAX_RCTSIZE]; - vc_map *tste2vc[NS_TST_NUM_ENTRIES]; - vc_map *scd2vc[NS_FRSCD_NUM]; - buf_nr sbnr; - buf_nr lbnr; - buf_nr hbnr; - buf_nr iovnr; - int sbfqc; - int lbfqc; - struct sk_buff *sm_handle; - u32 sm_addr; - struct sk_buff *lg_handle; - u32 lg_addr; - struct sk_buff *rcbuf; /* Current raw cell buffer */ - struct ns_rcqe *rawcell; - u32 rawch; /* Raw cell queue head */ - unsigned intcnt; /* Interrupt counter */ - spinlock_t int_lock; /* Interrupt lock */ - spinlock_t res_lock; /* Card resource lock */ -} ns_dev; - - /* NOTE: Each tste2vc entry relates a given TST entry to the corresponding - CBR vc. If the entry is not allocated, it must be NULL. - - There are two TSTs so the driver can modify them on the fly - without stopping the transmission. - - scd2vc allows us to find out unused fixed rate SCDs, because - they must have a NULL pointer here. */ - -#endif /* _LINUX_NICSTAR_H_ */ diff --git a/drivers/atm/nicstarmac.c b/drivers/atm/nicstarmac.c deleted file mode 100644 index 791f69a07ddf..000000000000 --- a/drivers/atm/nicstarmac.c +++ /dev/null @@ -1,244 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * this file included by nicstar.c - */ - -/* - * nicstarmac.c - * Read this ForeRunner's MAC address from eprom/eeprom - */ - -#include - -typedef void __iomem *virt_addr_t; - -#define CYCLE_DELAY 5 - -#define osp_MicroDelay(microsec) {unsigned long useconds = (microsec); \ - udelay((useconds));} -/* - * The following tables represent the timing diagrams found in - * the Data Sheet for the Xicor X25020 EEProm. The #defines below - * represent the bits in the NICStAR's General Purpose register - * that must be toggled for the corresponding actions on the EEProm - * to occur. - */ - -/* Write Data To EEProm from SI line on rising edge of CLK */ -/* Read Data From EEProm on falling edge of CLK */ - -#define CS_HIGH 0x0002 /* Chip select high */ -#define CS_LOW 0x0000 /* Chip select low (active low) */ -#define CLK_HIGH 0x0004 /* Clock high */ -#define CLK_LOW 0x0000 /* Clock low */ -#define SI_HIGH 0x0001 /* Serial input data high */ -#define SI_LOW 0x0000 /* Serial input data low */ - -/* Read Status Register = 0000 0101b */ -#if 0 -static u_int32_t rdsrtab[] = { - CS_HIGH | CLK_HIGH, - CS_LOW | CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW | SI_HIGH, - CLK_HIGH | SI_HIGH, /* 1 */ - CLK_LOW | SI_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW | SI_HIGH, - CLK_HIGH | SI_HIGH /* 1 */ -}; -#endif /* 0 */ - -/* Read from EEPROM = 0000 0011b */ -static u_int32_t readtab[] = { - /* - CS_HIGH | CLK_HIGH, - */ - CS_LOW | CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW, - CLK_HIGH, /* 0 */ - CLK_LOW | SI_HIGH, - CLK_HIGH | SI_HIGH, /* 1 */ - CLK_LOW | SI_HIGH, - CLK_HIGH | SI_HIGH /* 1 */ -}; - -/* Clock to read from/write to the eeprom */ -static u_int32_t clocktab[] = { - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW, - CLK_HIGH, - CLK_LOW -}; - -#define NICSTAR_REG_WRITE(bs, reg, val) \ - while ( readl(bs + STAT) & 0x0200 ) ; \ - writel((val),(base)+(reg)) -#define NICSTAR_REG_READ(bs, reg) \ - readl((base)+(reg)) -#define NICSTAR_REG_GENERAL_PURPOSE GP - -/* - * This routine will clock the Read_Status_reg function into the X2520 - * eeprom, then pull the result from bit 16 of the NicSTaR's General Purpose - * register. - */ -#if 0 -u_int32_t nicstar_read_eprom_status(virt_addr_t base) -{ - u_int32_t val; - u_int32_t rbyte; - int32_t i, j; - - /* Send read instruction */ - val = NICSTAR_REG_READ(base, NICSTAR_REG_GENERAL_PURPOSE) & 0xFFFFFFF0; - - for (i = 0; i < ARRAY_SIZE(rdsrtab); i++) { - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, - (val | rdsrtab[i])); - osp_MicroDelay(CYCLE_DELAY); - } - - /* Done sending instruction - now pull data off of bit 16, MSB first */ - /* Data clocked out of eeprom on falling edge of clock */ - - rbyte = 0; - for (i = 7, j = 0; i >= 0; i--) { - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, - (val | clocktab[j++])); - rbyte |= (((NICSTAR_REG_READ(base, NICSTAR_REG_GENERAL_PURPOSE) - & 0x00010000) >> 16) << i); - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, - (val | clocktab[j++])); - osp_MicroDelay(CYCLE_DELAY); - } - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, 2); - osp_MicroDelay(CYCLE_DELAY); - return rbyte; -} -#endif /* 0 */ - -/* - * This routine will clock the Read_data function into the X2520 - * eeprom, followed by the address to read from, through the NicSTaR's General - * Purpose register. - */ - -static u_int8_t read_eprom_byte(virt_addr_t base, u_int8_t offset) -{ - u_int32_t val = 0; - int i, j = 0; - u_int8_t tempread = 0; - - val = NICSTAR_REG_READ(base, NICSTAR_REG_GENERAL_PURPOSE) & 0xFFFFFFF0; - - /* Send READ instruction */ - for (i = 0; i < ARRAY_SIZE(readtab); i++) { - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, - (val | readtab[i])); - osp_MicroDelay(CYCLE_DELAY); - } - - /* Next, we need to send the byte address to read from */ - for (i = 7; i >= 0; i--) { - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, - (val | clocktab[j++] | ((offset >> i) & 1))); - osp_MicroDelay(CYCLE_DELAY); - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, - (val | clocktab[j++] | ((offset >> i) & 1))); - osp_MicroDelay(CYCLE_DELAY); - } - - j = 0; - - /* Now, we can read data from the eeprom by clocking it in */ - for (i = 7; i >= 0; i--) { - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, - (val | clocktab[j++])); - osp_MicroDelay(CYCLE_DELAY); - tempread |= - (((NICSTAR_REG_READ(base, NICSTAR_REG_GENERAL_PURPOSE) - & 0x00010000) >> 16) << i); - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, - (val | clocktab[j++])); - osp_MicroDelay(CYCLE_DELAY); - } - - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, 2); - osp_MicroDelay(CYCLE_DELAY); - return tempread; -} - -static void nicstar_init_eprom(virt_addr_t base) -{ - u_int32_t val; - - /* - * turn chip select off - */ - val = NICSTAR_REG_READ(base, NICSTAR_REG_GENERAL_PURPOSE) & 0xFFFFFFF0; - - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, - (val | CS_HIGH | CLK_HIGH)); - osp_MicroDelay(CYCLE_DELAY); - - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, - (val | CS_HIGH | CLK_LOW)); - osp_MicroDelay(CYCLE_DELAY); - - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, - (val | CS_HIGH | CLK_HIGH)); - osp_MicroDelay(CYCLE_DELAY); - - NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, - (val | CS_HIGH | CLK_LOW)); - osp_MicroDelay(CYCLE_DELAY); -} - -/* - * This routine will be the interface to the ReadPromByte function - * above. - */ - -static void -nicstar_read_eprom(virt_addr_t base, - u_int8_t prom_offset, u_int8_t * buffer, u_int32_t nbytes) -{ - u_int i; - - for (i = 0; i < nbytes; i++) { - buffer[i] = read_eprom_byte(base, prom_offset); - ++prom_offset; - osp_MicroDelay(CYCLE_DELAY); - } -} diff --git a/drivers/atm/nicstarmac.copyright b/drivers/atm/nicstarmac.copyright deleted file mode 100644 index 180531a83c62..000000000000 --- a/drivers/atm/nicstarmac.copyright +++ /dev/null @@ -1,61 +0,0 @@ -/* nicstar.c v0.22 Jawaid Bazyar (bazyar@hypermall.com) - * nicstar.c, M. Welsh (matt.welsh@cl.cam.ac.uk) - * - * Hacked October, 1997 by Jawaid Bazyar, Interlink Advertising Services Inc. - * http://www.hypermall.com/ - * 10/1/97 - commented out CFG_PHYIE bit - we don't care when the PHY - * interrupts us (except possibly for removal/insertion of the cable?) - * 10/4/97 - began heavy inline documentation of the code. Corrected typos - * and spelling mistakes. - * 10/5/97 - added code to handle PHY interrupts, disable PHY on - * loss of link, and correctly re-enable PHY when link is - * re-established. (put back CFG_PHYIE) - * - * Modified to work with the IDT7721 nicstar -- AAL5 (tested) only. - * - * R. D. Rechenmacher , Aug. 6, 1997 - * - * Linux driver for the IDT77201 NICStAR PCI ATM controller. - * PHY component is expected to be 155 Mbps S/UNI-Lite or IDT 77155; - * see init_nicstar() for PHY initialization to change this. This driver - * expects the Linux ATM stack to support scatter-gather lists - * (skb->atm.iovcnt != 0) for Rx skb's passed to vcc->push. - * - * Implementing minimal-copy of received data: - * IDT always receives data into a small buffer, then large buffers - * as needed. This means that data must always be copied to create - * the linear buffer needed by most non-ATM protocol stacks (e.g. IP) - * Fix is simple: make large buffers large enough to hold entire - * SDU, and leave bytes empty at the start. Then - * copy small buffer contents to head of large buffer. - * Trick is to avoid fragmenting Linux, due to need for a lot of large - * buffers. This is done by 2 things: - * 1) skb->destructor / skb->atm.recycle_buffer - * combined, allow nicstar_free_rx_skb to be called to - * recycle large data buffers - * 2) skb_clone of received buffers - * See nicstar_free_rx_skb and linearize_buffer for implementation - * details. - * - * - * - * Copyright (c) 1996 University of Cambridge Computer Laboratory - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * M. Welsh, 6 July 1996 - * - * - */ diff --git a/drivers/atm/suni.c b/drivers/atm/suni.c deleted file mode 100644 index bb588c98216d..000000000000 --- a/drivers/atm/suni.c +++ /dev/null @@ -1,391 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * drivers/atm/suni.c - S/UNI PHY driver - * - * Supports the following: - * PMC PM5346 S/UNI LITE - * PMC PM5350 S/UNI 155 ULTRA - * PMC PM5355 S/UNI 622 - */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "suni.h" - - -#if 0 -#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) -#else -#define DPRINTK(format,args...) -#endif - -#define PRIV(dev) ((struct suni_priv *) dev->phy_data) - -#define PUT(val,reg) dev->ops->phy_put(dev,val,SUNI_##reg) -#define GET(reg) dev->ops->phy_get(dev,SUNI_##reg) -#define REG_CHANGE(mask,shift,value,reg) \ - PUT((GET(reg) & ~(mask)) | ((value) << (shift)),reg) - - -static struct timer_list poll_timer; -static struct suni_priv *sunis = NULL; -static DEFINE_SPINLOCK(sunis_lock); - - -#define ADD_LIMITED(s,v) \ - atomic_add((v),&stats->s); \ - if (atomic_read(&stats->s) < 0) atomic_set(&stats->s,INT_MAX); - - -static void suni_hz(struct timer_list *timer) -{ - struct suni_priv *walk; - struct atm_dev *dev; - struct k_sonet_stats *stats; - - for (walk = sunis; walk; walk = walk->next) { - dev = walk->dev; - stats = &walk->sonet_stats; - PUT(0,MRI); /* latch counters */ - udelay(1); - ADD_LIMITED(section_bip,(GET(RSOP_SBL) & 0xff) | - ((GET(RSOP_SBM) & 0xff) << 8)); - ADD_LIMITED(line_bip,(GET(RLOP_LBL) & 0xff) | - ((GET(RLOP_LB) & 0xff) << 8) | - ((GET(RLOP_LBM) & 0xf) << 16)); - ADD_LIMITED(path_bip,(GET(RPOP_PBL) & 0xff) | - ((GET(RPOP_PBM) & 0xff) << 8)); - ADD_LIMITED(line_febe,(GET(RLOP_LFL) & 0xff) | - ((GET(RLOP_LF) & 0xff) << 8) | - ((GET(RLOP_LFM) & 0xf) << 16)); - ADD_LIMITED(path_febe,(GET(RPOP_PFL) & 0xff) | - ((GET(RPOP_PFM) & 0xff) << 8)); - ADD_LIMITED(corr_hcs,GET(RACP_CHEC) & 0xff); - ADD_LIMITED(uncorr_hcs,GET(RACP_UHEC) & 0xff); - ADD_LIMITED(rx_cells,(GET(RACP_RCCL) & 0xff) | - ((GET(RACP_RCC) & 0xff) << 8) | - ((GET(RACP_RCCM) & 7) << 16)); - ADD_LIMITED(tx_cells,(GET(TACP_TCCL) & 0xff) | - ((GET(TACP_TCC) & 0xff) << 8) | - ((GET(TACP_TCCM) & 7) << 16)); - } - if (timer) mod_timer(&poll_timer,jiffies+HZ); -} - - -#undef ADD_LIMITED - - -static int fetch_stats(struct atm_dev *dev,struct sonet_stats __user *arg,int zero) -{ - struct sonet_stats tmp; - int error = 0; - - sonet_copy_stats(&PRIV(dev)->sonet_stats,&tmp); - if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp)); - if (zero && !error) sonet_subtract_stats(&PRIV(dev)->sonet_stats,&tmp); - return error ? -EFAULT : 0; -} - - -#define HANDLE_FLAG(flag,reg,bit) \ - if (todo & flag) { \ - if (set) PUT(GET(reg) | bit,reg); \ - else PUT(GET(reg) & ~bit,reg); \ - todo &= ~flag; \ - } - - -static int change_diag(struct atm_dev *dev,void __user *arg,int set) -{ - int todo; - - if (get_user(todo,(int __user *)arg)) return -EFAULT; - HANDLE_FLAG(SONET_INS_SBIP,TSOP_DIAG,SUNI_TSOP_DIAG_DBIP8); - HANDLE_FLAG(SONET_INS_LBIP,TLOP_DIAG,SUNI_TLOP_DIAG_DBIP); - HANDLE_FLAG(SONET_INS_PBIP,TPOP_CD,SUNI_TPOP_DIAG_DB3); - HANDLE_FLAG(SONET_INS_FRAME,RSOP_CIE,SUNI_RSOP_CIE_FOOF); - HANDLE_FLAG(SONET_INS_LAIS,TSOP_CTRL,SUNI_TSOP_CTRL_LAIS); - HANDLE_FLAG(SONET_INS_PAIS,TPOP_CD,SUNI_TPOP_DIAG_PAIS); - HANDLE_FLAG(SONET_INS_LOS,TSOP_DIAG,SUNI_TSOP_DIAG_DLOS); - HANDLE_FLAG(SONET_INS_HCS,TACP_CS,SUNI_TACP_CS_DHCS); - return put_user(todo,(int __user *)arg) ? -EFAULT : 0; -} - - -#undef HANDLE_FLAG - - -static int get_diag(struct atm_dev *dev,void __user *arg) -{ - int set; - - set = 0; - if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DBIP8) set |= SONET_INS_SBIP; - if (GET(TLOP_DIAG) & SUNI_TLOP_DIAG_DBIP) set |= SONET_INS_LBIP; - if (GET(TPOP_CD) & SUNI_TPOP_DIAG_DB3) set |= SONET_INS_PBIP; - /* SONET_INS_FRAME is one-shot only */ - if (GET(TSOP_CTRL) & SUNI_TSOP_CTRL_LAIS) set |= SONET_INS_LAIS; - if (GET(TPOP_CD) & SUNI_TPOP_DIAG_PAIS) set |= SONET_INS_PAIS; - if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DLOS) set |= SONET_INS_LOS; - if (GET(TACP_CS) & SUNI_TACP_CS_DHCS) set |= SONET_INS_HCS; - return put_user(set,(int __user *)arg) ? -EFAULT : 0; -} - - -static int set_loopback(struct atm_dev *dev,int mode) -{ - unsigned char control; - int reg, dle, lle; - - if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { - reg = SUNI_MCM; - dle = SUNI_MCM_DLE; - lle = SUNI_MCM_LLE; - } else { - reg = SUNI_MCT; - dle = SUNI_MCT_DLE; - lle = SUNI_MCT_LLE; - } - - control = dev->ops->phy_get(dev, reg) & ~(dle | lle); - switch (mode) { - case ATM_LM_NONE: - break; - case ATM_LM_LOC_PHY: - control |= dle; - break; - case ATM_LM_RMT_PHY: - control |= lle; - break; - default: - return -EINVAL; - } - dev->ops->phy_put(dev, control, reg); - PRIV(dev)->loop_mode = mode; - return 0; -} - -/* - * SONET vs. SDH Configuration - * - * Z0INS (register 0x06): 0 for SONET, 1 for SDH - * ENSS (register 0x3D): 0 for SONET, 1 for SDH - * LEN16 (register 0x28): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD) - * LEN16 (register 0x50): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD) - * S[1:0] (register 0x46): 00 for SONET, 10 for SDH - */ - -static int set_sonet(struct atm_dev *dev) -{ - if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { - PUT(GET(RPOP_RC) & ~SUNI_RPOP_RC_ENSS, RPOP_RC); - PUT(GET(SSTB_CTRL) & ~SUNI_SSTB_CTRL_LEN16, SSTB_CTRL); - PUT(GET(SPTB_CTRL) & ~SUNI_SPTB_CTRL_LEN16, SPTB_CTRL); - } - - REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT, - SUNI_TPOP_S_SONET, TPOP_APM); - - return 0; -} - -static int set_sdh(struct atm_dev *dev) -{ - if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { - PUT(GET(RPOP_RC) | SUNI_RPOP_RC_ENSS, RPOP_RC); - PUT(GET(SSTB_CTRL) | SUNI_SSTB_CTRL_LEN16, SSTB_CTRL); - PUT(GET(SPTB_CTRL) | SUNI_SPTB_CTRL_LEN16, SPTB_CTRL); - } - - REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT, - SUNI_TPOP_S_SDH, TPOP_APM); - - return 0; -} - - -static int get_framing(struct atm_dev *dev, void __user *arg) -{ - int framing; - unsigned char s; - - - s = (GET(TPOP_APM) & SUNI_TPOP_APM_S) >> SUNI_TPOP_APM_S_SHIFT; - if (s == SUNI_TPOP_S_SONET) - framing = SONET_FRAME_SONET; - else - framing = SONET_FRAME_SDH; - - return put_user(framing, (int __user *) arg) ? -EFAULT : 0; -} - -static int set_framing(struct atm_dev *dev, void __user *arg) -{ - int mode; - - if (get_user(mode, (int __user *) arg)) - return -EFAULT; - - if (mode == SONET_FRAME_SONET) - return set_sonet(dev); - else if (mode == SONET_FRAME_SDH) - return set_sdh(dev); - - return -EINVAL; -} - - -static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) -{ - switch (cmd) { - case SONET_GETSTATZ: - case SONET_GETSTAT: - return fetch_stats(dev, arg, cmd == SONET_GETSTATZ); - case SONET_SETDIAG: - return change_diag(dev,arg,1); - case SONET_CLRDIAG: - return change_diag(dev,arg,0); - case SONET_GETDIAG: - return get_diag(dev,arg); - case SONET_SETFRAMING: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - return set_framing(dev, arg); - case SONET_GETFRAMING: - return get_framing(dev, arg); - case SONET_GETFRSENSE: - return -EINVAL; - case ATM_SETLOOP: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - return set_loopback(dev,(int)(unsigned long)arg); - case ATM_GETLOOP: - return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ? - -EFAULT : 0; - case ATM_QUERYLOOP: - return put_user(ATM_LM_LOC_PHY | ATM_LM_RMT_PHY, - (int __user *) arg) ? -EFAULT : 0; - default: - return -ENOIOCTLCMD; - } -} - - -static void poll_los(struct atm_dev *dev) -{ - atm_dev_signal_change(dev, - GET(RSOP_SIS) & SUNI_RSOP_SIS_LOSV ? - ATM_PHY_SIG_LOST : ATM_PHY_SIG_FOUND); -} - - -static void suni_int(struct atm_dev *dev) -{ - poll_los(dev); - printk(KERN_NOTICE "%s(itf %d): signal %s\n",dev->type,dev->number, - dev->signal == ATM_PHY_SIG_LOST ? "lost" : "detected again"); -} - - -static int suni_start(struct atm_dev *dev) -{ - unsigned long flags; - int first; - - spin_lock_irqsave(&sunis_lock,flags); - first = !sunis; - PRIV(dev)->next = sunis; - sunis = PRIV(dev); - spin_unlock_irqrestore(&sunis_lock,flags); - memset(&PRIV(dev)->sonet_stats,0,sizeof(struct k_sonet_stats)); - PUT(GET(RSOP_CIE) | SUNI_RSOP_CIE_LOSE,RSOP_CIE); - /* interrupt on loss of signal */ - poll_los(dev); /* ... and clear SUNI interrupts */ - if (dev->signal == ATM_PHY_SIG_LOST) - printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type, - dev->number); - PRIV(dev)->loop_mode = ATM_LM_NONE; - suni_hz(NULL); /* clear SUNI counters */ - (void) fetch_stats(dev,NULL,1); /* clear kernel counters */ - if (first) { - timer_setup(&poll_timer, suni_hz, 0); - poll_timer.expires = jiffies+HZ; -#if 0 -printk(KERN_DEBUG "[u] p=0x%lx,n=0x%lx\n",(unsigned long) poll_timer.list.prev, - (unsigned long) poll_timer.list.next); -#endif - add_timer(&poll_timer); - } - return 0; -} - - -static int suni_stop(struct atm_dev *dev) -{ - struct suni_priv **walk; - unsigned long flags; - - /* let SAR driver worry about stopping interrupts */ - spin_lock_irqsave(&sunis_lock,flags); - for (walk = &sunis; *walk != PRIV(dev); - walk = &PRIV((*walk)->dev)->next); - *walk = PRIV((*walk)->dev)->next; - if (!sunis) timer_delete_sync(&poll_timer); - spin_unlock_irqrestore(&sunis_lock,flags); - kfree(PRIV(dev)); - - return 0; -} - - -static const struct atmphy_ops suni_ops = { - .start = suni_start, - .ioctl = suni_ioctl, - .interrupt = suni_int, - .stop = suni_stop, -}; - - -int suni_init(struct atm_dev *dev) -{ - unsigned char mri; - - if (!(dev->phy_data = kmalloc_obj(struct suni_priv))) - return -ENOMEM; - PRIV(dev)->dev = dev; - - mri = GET(MRI); /* reset SUNI */ - PRIV(dev)->type = (mri & SUNI_MRI_TYPE) >> SUNI_MRI_TYPE_SHIFT; - PUT(mri | SUNI_MRI_RESET,MRI); - PUT(mri,MRI); - PUT((GET(MT) & SUNI_MT_DS27_53),MT); /* disable all tests */ - set_sonet(dev); - REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP, - TACP_IUCHP); /* idle cells */ - PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP); - dev->phy = &suni_ops; - - return 0; -} - -EXPORT_SYMBOL(suni_init); - -MODULE_DESCRIPTION("S/UNI PHY driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/atm/suni.h b/drivers/atm/suni.h deleted file mode 100644 index d28a50d47d8b..000000000000 --- a/drivers/atm/suni.h +++ /dev/null @@ -1,242 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * drivers/atm/suni.h - S/UNI PHY driver - */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - -#ifndef DRIVER_ATM_SUNI_H -#define DRIVER_ATM_SUNI_H - -#include -#include -#include - -/* SUNI registers */ - -#define SUNI_MRI 0x00 /* Master Reset and Identity / Load - Meter */ -#define SUNI_MC 0x01 /* Master Configuration */ -#define SUNI_MIS 0x02 /* Master Interrupt Status */ - /* no 0x03 */ -#define SUNI_MCM 0x04 /* Master Clock Monitor */ -#define SUNI_MCT 0x05 /* Master Control */ -#define SUNI_CSCS 0x06 /* Clock Synthesis Control and Status */ -#define SUNI_CRCS 0x07 /* Clock Recovery Control and Status */ - /* 0x08-0x0F reserved */ -#define SUNI_RSOP_CIE 0x10 /* RSOP Control/Interrupt Enable */ -#define SUNI_RSOP_SIS 0x11 /* RSOP Status/Interrupt Status */ -#define SUNI_RSOP_SBL 0x12 /* RSOP Section BIP-8 LSB */ -#define SUNI_RSOP_SBM 0x13 /* RSOP Section BIP-8 MSB */ -#define SUNI_TSOP_CTRL 0x14 /* TSOP Control */ -#define SUNI_TSOP_DIAG 0x15 /* TSOP Diagnostic */ - /* 0x16-0x17 reserved */ -#define SUNI_RLOP_CS 0x18 /* RLOP Control/Status */ -#define SUNI_RLOP_IES 0x19 /* RLOP Interrupt Enable/Status */ -#define SUNI_RLOP_LBL 0x1A /* RLOP Line BIP-8/24 LSB */ -#define SUNI_RLOP_LB 0x1B /* RLOP Line BIP-8/24 */ -#define SUNI_RLOP_LBM 0x1C /* RLOP Line BIP-8/24 MSB */ -#define SUNI_RLOP_LFL 0x1D /* RLOP Line FEBE LSB */ -#define SUNI_RLOP_LF 0x1E /* RLOP Line FEBE */ -#define SUNI_RLOP_LFM 0x1F /* RLOP Line FEBE MSB */ -#define SUNI_TLOP_CTRL 0x20 /* TLOP Control */ -#define SUNI_TLOP_DIAG 0x21 /* TLOP Diagnostic */ - /* 0x22-0x27 reserved */ -#define SUNI_SSTB_CTRL 0x28 -#define SUNI_RPOP_SC 0x30 /* RPOP Status/Control */ -#define SUNI_RPOP_IS 0x31 /* RPOP Interrupt Status */ - /* 0x32 reserved */ -#define SUNI_RPOP_IE 0x33 /* RPOP Interrupt Enable */ - /* 0x34-0x36 reserved */ -#define SUNI_RPOP_PSL 0x37 /* RPOP Path Signal Label */ -#define SUNI_RPOP_PBL 0x38 /* RPOP Path BIP-8 LSB */ -#define SUNI_RPOP_PBM 0x39 /* RPOP Path BIP-8 MSB */ -#define SUNI_RPOP_PFL 0x3A /* RPOP Path FEBE LSB */ -#define SUNI_RPOP_PFM 0x3B /* RPOP Path FEBE MSB */ - /* 0x3C reserved */ -#define SUNI_RPOP_PBC 0x3D /* RPOP Path BIP-8 Configuration */ -#define SUNI_RPOP_RC 0x3D /* RPOP Ring Control (PM5355) */ - /* 0x3E-0x3F reserved */ -#define SUNI_TPOP_CD 0x40 /* TPOP Control/Diagnostic */ -#define SUNI_TPOP_PC 0x41 /* TPOP Pointer Control */ - /* 0x42-0x44 reserved */ -#define SUNI_TPOP_APL 0x45 /* TPOP Arbitrary Pointer LSB */ -#define SUNI_TPOP_APM 0x46 /* TPOP Arbitrary Pointer MSB */ - /* 0x47 reserved */ -#define SUNI_TPOP_PSL 0x48 /* TPOP Path Signal Label */ -#define SUNI_TPOP_PS 0x49 /* TPOP Path Status */ - /* 0x4A-0x4F reserved */ -#define SUNI_RACP_CS 0x50 /* RACP Control/Status */ -#define SUNI_RACP_IES 0x51 /* RACP Interrupt Enable/Status */ -#define SUNI_RACP_MHP 0x52 /* RACP Match Header Pattern */ -#define SUNI_RACP_MHM 0x53 /* RACP Match Header Mask */ -#define SUNI_RACP_CHEC 0x54 /* RACP Correctable HCS Error Count */ -#define SUNI_RACP_UHEC 0x55 /* RACP Uncorrectable HCS Err Count */ -#define SUNI_RACP_RCCL 0x56 /* RACP Receive Cell Counter LSB */ -#define SUNI_RACP_RCC 0x57 /* RACP Receive Cell Counter */ -#define SUNI_RACP_RCCM 0x58 /* RACP Receive Cell Counter MSB */ -#define SUNI_RACP_CFG 0x59 /* RACP Configuration */ - /* 0x5A-0x5F reserved */ -#define SUNI_TACP_CS 0x60 /* TACP Control/Status */ -#define SUNI_TACP_IUCHP 0x61 /* TACP Idle/Unassigned Cell Hdr Pat */ -#define SUNI_TACP_IUCPOP 0x62 /* TACP Idle/Unassigned Cell Payload - Octet Pattern */ -#define SUNI_TACP_FIFO 0x63 /* TACP FIFO Configuration */ -#define SUNI_TACP_TCCL 0x64 /* TACP Transmit Cell Counter LSB */ -#define SUNI_TACP_TCC 0x65 /* TACP Transmit Cell Counter */ -#define SUNI_TACP_TCCM 0x66 /* TACP Transmit Cell Counter MSB */ -#define SUNI_TACP_CFG 0x67 /* TACP Configuration */ -#define SUNI_SPTB_CTRL 0x68 /* SPTB Control */ - /* 0x69-0x7F reserved */ -#define SUNI_MT 0x80 /* Master Test */ - /* 0x81-0xFF reserved */ - -/* SUNI register values */ - - -/* MRI is reg 0 */ -#define SUNI_MRI_ID 0x0f /* R, SUNI revision number */ -#define SUNI_MRI_ID_SHIFT 0 -#define SUNI_MRI_TYPE 0x70 /* R, SUNI type (lite is 011) */ -#define SUNI_MRI_TYPE_SHIFT 4 -#define SUNI_MRI_TYPE_PM5346 0x3 /* S/UNI 155 LITE */ -#define SUNI_MRI_TYPE_PM5347 0x4 /* S/UNI 155 PLUS */ -#define SUNI_MRI_TYPE_PM5350 0x7 /* S/UNI 155 ULTRA */ -#define SUNI_MRI_TYPE_PM5355 0x1 /* S/UNI 622 */ -#define SUNI_MRI_RESET 0x80 /* RW, reset & power down chip - 0: normal operation - 1: reset & low power */ - -/* MCM is reg 0x4 */ -#define SUNI_MCM_LLE 0x20 /* line loopback (PM5355) */ -#define SUNI_MCM_DLE 0x10 /* diagnostic loopback (PM5355) */ - -/* MCT is reg 5 */ -#define SUNI_MCT_LOOPT 0x01 /* RW, timing source, 0: from - TRCLK+/- */ -#define SUNI_MCT_DLE 0x02 /* RW, diagnostic loopback */ -#define SUNI_MCT_LLE 0x04 /* RW, line loopback */ -#define SUNI_MCT_FIXPTR 0x20 /* RW, disable transmit payload pointer - adjustments - 0: payload ptr controlled by TPOP - ptr control reg - 1: payload pointer fixed at 522 */ -#define SUNI_MCT_LCDV 0x40 /* R, loss of cell delineation */ -#define SUNI_MCT_LCDE 0x80 /* RW, loss of cell delineation - interrupt (1: on) */ -/* RSOP_CIE is reg 0x10 */ -#define SUNI_RSOP_CIE_OOFE 0x01 /* RW, enable interrupt on frame alarm - state change */ -#define SUNI_RSOP_CIE_LOFE 0x02 /* RW, enable interrupt on loss of - frame state change */ -#define SUNI_RSOP_CIE_LOSE 0x04 /* RW, enable interrupt on loss of - signal state change */ -#define SUNI_RSOP_CIE_BIPEE 0x08 /* RW, enable interrupt on section - BIP-8 error (B1) */ -#define SUNI_RSOP_CIE_FOOF 0x20 /* W, force RSOP out of frame at next - boundary */ -#define SUNI_RSOP_CIE_DDS 0x40 /* RW, disable scrambling */ - -/* RSOP_SIS is reg 0x11 */ -#define SUNI_RSOP_SIS_OOFV 0x01 /* R, out of frame */ -#define SUNI_RSOP_SIS_LOFV 0x02 /* R, loss of frame */ -#define SUNI_RSOP_SIS_LOSV 0x04 /* R, loss of signal */ -#define SUNI_RSOP_SIS_OOFI 0x08 /* R, out of frame interrupt */ -#define SUNI_RSOP_SIS_LOFI 0x10 /* R, loss of frame interrupt */ -#define SUNI_RSOP_SIS_LOSI 0x20 /* R, loss of signal interrupt */ -#define SUNI_RSOP_SIS_BIPEI 0x40 /* R, section BIP-8 interrupt */ - -/* TSOP_CTRL is reg 0x14 */ -#define SUNI_TSOP_CTRL_LAIS 0x01 /* insert alarm indication signal */ -#define SUNI_TSOP_CTRL_DS 0x40 /* disable scrambling */ - -/* TSOP_DIAG is reg 0x15 */ -#define SUNI_TSOP_DIAG_DFP 0x01 /* insert single bit error cont. */ -#define SUNI_TSOP_DIAG_DBIP8 0x02 /* insert section BIP err (cont) */ -#define SUNI_TSOP_DIAG_DLOS 0x04 /* set line to zero (loss of signal) */ - -/* TLOP_DIAG is reg 0x21 */ -#define SUNI_TLOP_DIAG_DBIP 0x01 /* insert line BIP err (continuously) */ - -/* SSTB_CTRL is reg 0x28 */ -#define SUNI_SSTB_CTRL_LEN16 0x01 /* path trace message length bit */ - -/* RPOP_RC is reg 0x3D (PM5355) */ -#define SUNI_RPOP_RC_ENSS 0x40 /* enable size bit */ - -/* TPOP_DIAG is reg 0x40 */ -#define SUNI_TPOP_DIAG_PAIS 0x01 /* insert STS path alarm ind (cont) */ -#define SUNI_TPOP_DIAG_DB3 0x02 /* insert path BIP err (continuously) */ - -/* TPOP_APM is reg 0x46 */ -#define SUNI_TPOP_APM_APTR 0x03 /* RW, arbitrary pointer, upper 2 - bits */ -#define SUNI_TPOP_APM_APTR_SHIFT 0 -#define SUNI_TPOP_APM_S 0x0c /* RW, "unused" bits of payload - pointer */ -#define SUNI_TPOP_APM_S_SHIFT 2 -#define SUNI_TPOP_APM_NDF 0xf0 /* RW, NDF bits */ -#define SUNI_TPOP_APM_NDF_SHIFT 4 - -#define SUNI_TPOP_S_SONET 0 /* set S bits to 00 */ -#define SUNI_TPOP_S_SDH 2 /* set S bits to 10 */ - -/* RACP_IES is reg 0x51 */ -#define SUNI_RACP_IES_FOVRI 0x02 /* R, FIFO overrun */ -#define SUNI_RACP_IES_UHCSI 0x04 /* R, uncorrectable HCS error */ -#define SUNI_RACP_IES_CHCSI 0x08 /* R, correctable HCS error */ -#define SUNI_RACP_IES_OOCDI 0x10 /* R, change of cell delineation - state */ -#define SUNI_RACP_IES_FIFOE 0x20 /* RW, enable FIFO overrun interrupt */ -#define SUNI_RACP_IES_HCSE 0x40 /* RW, enable HCS error interrupt */ -#define SUNI_RACP_IES_OOCDE 0x80 /* RW, enable cell delineation state - change interrupt */ - -/* TACP_CS is reg 0x60 */ -#define SUNI_TACP_CS_FIFORST 0x01 /* RW, reset transmit FIFO (sticky) */ -#define SUNI_TACP_CS_DSCR 0x02 /* RW, disable payload scrambling */ -#define SUNI_TACP_CS_HCAADD 0x04 /* RW, add coset polynomial to HCS */ -#define SUNI_TACP_CS_DHCS 0x10 /* RW, insert HCS errors */ -#define SUNI_TACP_CS_FOVRI 0x20 /* R, FIFO overrun */ -#define SUNI_TACP_CS_TSOCI 0x40 /* R, TSOC input high */ -#define SUNI_TACP_CS_FIFOE 0x80 /* RW, enable FIFO overrun interrupt */ - -/* TACP_IUCHP is reg 0x61 */ -#define SUNI_TACP_IUCHP_CLP 0x01 /* RW, 8th bit of 4th octet of i/u - pattern */ -#define SUNI_TACP_IUCHP_PTI 0x0e /* RW, 5th-7th bits of 4th octet of i/u - pattern */ -#define SUNI_TACP_IUCHP_PTI_SHIFT 1 -#define SUNI_TACP_IUCHP_GFC 0xf0 /* RW, 1st-4th bits of 1st octet of i/u - pattern */ -#define SUNI_TACP_IUCHP_GFC_SHIFT 4 - -/* SPTB_CTRL is reg 0x68 */ -#define SUNI_SPTB_CTRL_LEN16 0x01 /* path trace message length */ - -/* MT is reg 0x80 */ -#define SUNI_MT_HIZIO 0x01 /* RW, all but data bus & MP interface - tri-state */ -#define SUNI_MT_HIZDATA 0x02 /* W, also tri-state data bus */ -#define SUNI_MT_IOTST 0x04 /* RW, enable test mode */ -#define SUNI_MT_DBCTRL 0x08 /* W, control data bus by CSB pin */ -#define SUNI_MT_PMCTST 0x10 /* W, PMC test mode */ -#define SUNI_MT_DS27_53 0x80 /* RW, select between 8- or 16- bit */ - - -#define SUNI_IDLE_PATTERN 0x6a /* idle pattern */ - - -#ifdef __KERNEL__ -struct suni_priv { - struct k_sonet_stats sonet_stats; /* link diagnostics */ - int loop_mode; /* loopback mode */ - int type; /* phy type */ - struct atm_dev *dev; /* device back-pointer */ - struct suni_priv *next; /* next SUNI */ -}; - -int suni_init(struct atm_dev *dev); -#endif - -#endif diff --git a/drivers/atm/tonga.h b/drivers/atm/tonga.h deleted file mode 100644 index 771b3f95246c..000000000000 --- a/drivers/atm/tonga.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* drivers/atm/tonga.h - Efficient Networks Tonga (PCI bridge) declarations */ - -/* Written 1995 by Werner Almesberger, EPFL LRC */ - - -#ifndef DRIVER_ATM_TONGA_H -#define DRIVER_ATM_TONGA_H - -#define PCI_TONGA_CTRL 0x60 /* control register */ - -#define END_SWAP_DMA 0x80 /* endian swap on DMA */ -#define END_SWAP_BYTE 0x40 /* endian swap on slave byte accesses */ -#define END_SWAP_WORD 0x20 /* endian swap on slave word accesses */ -#define SEPROM_MAGIC 0x0c /* obscure required pattern (ASIC only) */ -#define SEPROM_DATA 0x02 /* serial EEPROM data (ASIC only) */ -#define SEPROM_CLK 0x01 /* serial EEPROM clock (ASIC only) */ - -#define SEPROM_ESI_BASE 64 /* start of ESI in serial EEPROM */ - -#endif diff --git a/drivers/atm/zeprom.h b/drivers/atm/zeprom.h deleted file mode 100644 index 8e8819a3840d..000000000000 --- a/drivers/atm/zeprom.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* drivers/atm/zeprom.h - ZeitNet ZN122x EEPROM (NM93C46) declarations */ - -/* Written 1995,1996 by Werner Almesberger, EPFL LRC */ - - -#ifndef DRIVER_ATM_ZEPROM_H -#define DRIVER_ATM_ZEPROM_H - -/* Different versions use different control registers */ - -#define ZEPROM_V1_REG PCI_VENDOR_ID /* PCI register */ -#define ZEPROM_V2_REG 0x40 - -/* Bits in control register */ - -#define ZEPROM_SK 0x80000000 /* strobe (probably on raising edge) */ -#define ZEPROM_CS 0x40000000 /* Chip Select */ -#define ZEPROM_DI 0x20000000 /* Data Input */ -#define ZEPROM_DO 0x10000000 /* Data Output */ - -#define ZEPROM_SIZE 32 /* 32 bytes */ -#define ZEPROM_V1_ESI_OFF 24 /* ESI offset in EEPROM (V1) */ -#define ZEPROM_V2_ESI_OFF 4 /* ESI offset in EEPROM (V2) */ - -#define ZEPROM_CMD_LEN 3 /* commands are three bits */ -#define ZEPROM_ADDR_LEN 6 /* addresses are six bits */ - -/* Commands (3 bits) */ - -#define ZEPROM_CMD_READ 6 - -/* No other commands are needed. */ - -#endif diff --git a/drivers/base/core.c b/drivers/base/core.c index 673d709fa7e6..bd2ddf2aab50 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3688,6 +3688,21 @@ int device_add(struct device *dev) fw_devlink_link_device(dev); } + /* + * The moment the device was linked into the bus's "klist_devices" in + * bus_add_device() then it's possible that probe could have been + * attempted in a different thread via userspace loading a driver + * matching the device. "ready_to_probe" being unset would have + * blocked those attempts. Now that all of the above initialization has + * happened, unblock probe. If probe happens through another thread + * after this point but before bus_probe_device() runs then it's fine. + * bus_probe_device() -> device_initial_probe() -> __device_attach() + * will notice (under device_lock) that the device is already bound. + */ + device_lock(dev); + dev_set_ready_to_probe(dev); + device_unlock(dev); + bus_probe_device(dev); /* diff --git a/drivers/base/dd.c b/drivers/base/dd.c index cb5046f0634d..1dc1e3528043 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -836,6 +836,26 @@ static int __driver_probe_device(const struct device_driver *drv, struct device if (dev->driver) return -EBUSY; + /* + * In device_add(), the "struct device" gets linked into the subsystem's + * list of devices and broadcast to userspace (via uevent) before we're + * quite ready to probe. Those open pathways to driver probe before + * we've finished enough of device_add() to reliably support probe. + * Detect this and tell other pathways to try again later. device_add() + * itself will also try to probe immediately after setting + * "ready_to_probe". + */ + if (!dev_ready_to_probe(dev)) + return dev_err_probe(dev, -EPROBE_DEFER, "Device not ready to probe\n"); + + /* + * Set can_match = true after calling dev_ready_to_probe(), so + * driver_deferred_probe_add() won't actually add the device to the + * deferred probe list when dev_ready_to_probe() returns false. + * + * When dev_ready_to_probe() returns false, it means that device_add() + * will do another probe() attempt for us. + */ dev->can_match = true; dev_dbg(dev, "bus: '%s': %s: matched device with driver %s\n", drv->bus->name, __func__, drv->name); diff --git a/drivers/base/property.c b/drivers/base/property.c index d16e9c5f1921..8e0148a37fff 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -602,6 +602,8 @@ EXPORT_SYMBOL_GPL(fwnode_property_match_property_string); * %-ENOENT when the index is out of bounds, the index has an empty * reference or the property was not found * %-EINVAL on parse error + * %-ENOTCONN when the remote firmware node exists but has not been + * registered yet */ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode, const char *prop, const char *nargs_prop, diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index c9b39a02278e..31e30dfced19 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -303,6 +303,50 @@ static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = { .max_raw_write = I2C_SMBUS_BLOCK_MAX - 2, }; +/* + * SMBus byte/word reg16 support for adapters that have SMBUS_BYTE_DATA + * and SMBUS_WORD_DATA but lack I2C_FUNC_I2C and I2C_FUNC_SMBUS_I2C_BLOCK, + * such as the AMD PIIX4. + * + * READ: set 16-bit EEPROM address via write_byte_data(addr_lo, addr_hi), + * then sequentially read bytes via read_byte() (EEPROM auto- + * increments the address pointer). Same as the I2C-block reg16 + * read path above. + * + * WRITE: encode the low address byte and data into a word transaction: + * write_word_data(addr_hi, (data_byte << 8) | addr_lo). + * Only single-byte writes are supported (one value per transaction). + */ +static int regmap_smbus_word_write_reg16(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + u8 addr_hi, addr_lo, val; + + /* + * data layout: [addr_hi, addr_lo, val0, val1, ...]. + * Only single-byte value writes are supported; multi-byte would + * require raw I2C (or repeated word writes with incrementing address). + */ + if (count != 3) + return -EINVAL; + + addr_hi = ((u8 *)data)[0]; + addr_lo = ((u8 *)data)[1]; + val = ((u8 *)data)[2]; + + return i2c_smbus_write_word_data(i2c, addr_hi, + cpu_to_le16(((u16)val << 8) | addr_lo)); +} + +static const struct regmap_bus regmap_smbus_byte_word_reg16 = { + .write = regmap_smbus_word_write_reg16, + .read = regmap_i2c_smbus_i2c_read_reg16, + .max_raw_read = I2C_SMBUS_BLOCK_MAX - 2, + .max_raw_write = 1, +}; + static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, const struct regmap_config *config) { @@ -321,6 +365,11 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) bus = ®map_i2c_smbus_i2c_block_reg16; + else if (config->val_bits == 8 && config->reg_bits == 16 && + i2c_check_functionality(i2c->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + bus = ®map_smbus_byte_word_reg16; else if (config->val_bits == 16 && config->reg_bits == 8 && i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_WORD_DATA)) diff --git a/drivers/base/regmap/regmap-ram.c b/drivers/base/regmap/regmap-ram.c index 0272d53fead1..c7356b0d8c83 100644 --- a/drivers/base/regmap/regmap-ram.c +++ b/drivers/base/regmap/regmap-ram.c @@ -71,11 +71,17 @@ struct regmap *__regmap_init_ram(struct device *dev, return ERR_PTR(-ENOMEM); data->written = kzalloc_objs(bool, config->max_register + 1); - if (!data->written) + if (!data->written) { + kfree(data->read); return ERR_PTR(-ENOMEM); + } map = __regmap_init(dev, ®map_ram, data, config, lock_key, lock_name); + if (IS_ERR(map)) { + kfree(data->read); + kfree(data->written); + } return map; } diff --git a/drivers/base/regmap/regmap-sdw-mbq.c b/drivers/base/regmap/regmap-sdw-mbq.c index 6a61629f5f89..2585933d4946 100644 --- a/drivers/base/regmap/regmap-sdw-mbq.c +++ b/drivers/base/regmap/regmap-sdw-mbq.c @@ -74,7 +74,7 @@ static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg, static int regmap_sdw_mbq_write_impl(struct sdw_slave *slave, unsigned int reg, unsigned int val, - int mbq_size, bool deferrable) + int mbq_size) { int shift = mbq_size * BITS_PER_BYTE; int ret; @@ -88,17 +88,14 @@ static int regmap_sdw_mbq_write_impl(struct sdw_slave *slave, return ret; } - ret = sdw_write_no_pm(slave, reg, val & 0xff); - if (deferrable && ret == -ENODATA) - return -EAGAIN; - - return ret; + return sdw_write_no_pm(slave, reg, val & 0xff); } static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val) { struct regmap_mbq_context *ctx = context; struct sdw_slave *slave = ctx->sdw; + struct device *dev = ctx->dev; bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg); int mbq_size = regmap_sdw_mbq_size(ctx, reg); int ret; @@ -113,13 +110,16 @@ static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int va * process a single wait/timeout on function busy and a single retry * of the transaction. */ - ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size, deferrable); - if (ret == -EAGAIN) { + ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size); + if (ret == -ENODATA) { + if (!deferrable) + dev_warn(dev, "Defer on undeferrable control: %x\n", reg); + ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx); if (ret) return ret; - ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size, false); + ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size); } return ret; @@ -127,18 +127,14 @@ static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int va static int regmap_sdw_mbq_read_impl(struct sdw_slave *slave, unsigned int reg, unsigned int *val, - int mbq_size, bool deferrable) + int mbq_size) { int shift = BITS_PER_BYTE; int read; read = sdw_read_no_pm(slave, reg); - if (read < 0) { - if (deferrable && read == -ENODATA) - return -EAGAIN; - + if (read < 0) return read; - } *val = read; @@ -158,6 +154,7 @@ static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *va { struct regmap_mbq_context *ctx = context; struct sdw_slave *slave = ctx->sdw; + struct device *dev = ctx->dev; bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg); int mbq_size = regmap_sdw_mbq_size(ctx, reg); int ret; @@ -172,13 +169,16 @@ static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *va * process a single wait/timeout on function busy and a single retry * of the transaction. */ - ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size, deferrable); - if (ret == -EAGAIN) { + ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size); + if (ret == -ENODATA) { + if (!deferrable) + dev_warn(dev, "Defer on undeferrable control: %x\n", reg); + ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx); if (ret) return ret; - ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size, false); + ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size); } return ret; diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index a80575bf598b..a19f8f722bc8 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -554,7 +554,7 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, return -EINVAL; if (!refnode) - return -ENOENT; + return -ENOTCONN; if (nargs_prop) { error = fwnode_property_read_u32(refnode, nargs_prop, &nargs_prop_val); diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index e7da06200c1e..4065336ebd1f 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -7165,7 +7165,7 @@ static ssize_t do_rbd_add(const char *buf, size_t count) rc = device_add_disk(&rbd_dev->dev, rbd_dev->disk, NULL); if (rc) - goto err_out_cleanup_disk; + goto err_out_device; spin_lock(&rbd_dev_list_lock); list_add_tail(&rbd_dev->node, &rbd_dev_list); @@ -7179,8 +7179,8 @@ out: module_put(THIS_MODULE); return rc; -err_out_cleanup_disk: - rbd_free_disk(rbd_dev); +err_out_device: + device_del(&rbd_dev->dev); err_out_image_lock: rbd_dev_image_unlock(rbd_dev); rbd_dev_device_release(rbd_dev); diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index 603a98a30989..6c041eaebdb9 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -900,12 +900,29 @@ static int ublk_validate_params(const struct ublk_device *ub) if (p->logical_bs_shift > PAGE_SHIFT || p->logical_bs_shift < 9) return -EINVAL; + /* + * 256M is a reasonable upper bound for physical block size, + * io_min and io_opt; it aligns with the maximum physical + * block size possible in NVMe. + */ + if (p->physical_bs_shift > ilog2(SZ_256M)) + return -EINVAL; + + if (p->io_min_shift > ilog2(SZ_256M)) + return -EINVAL; + + if (p->io_opt_shift > ilog2(SZ_256M)) + return -EINVAL; + if (p->logical_bs_shift > p->physical_bs_shift) return -EINVAL; if (p->max_sectors > (ub->dev_info.max_io_buf_bytes >> 9)) return -EINVAL; + if (p->max_sectors < PAGE_SECTORS) + return -EINVAL; + if (ublk_dev_is_zoned(ub) && !p->chunk_sectors) return -EINVAL; } else @@ -1319,10 +1336,18 @@ static bool ublk_copy_user_bvec(const struct bio_vec *bv, unsigned *offset, len = bv->bv_len - *offset; bv_buf = kmap_local_page(bv->bv_page) + bv->bv_offset + *offset; + /* + * Bio pages may originate from slab caches without a usercopy region + * (e.g. jbd2 frozen metadata buffers). This is the same data that + * the loop driver writes to its backing file — no exposure risk. + * The bvec length is always trusted, so the size check in + * check_copy_size() is not needed either. Use the unchecked + * helpers to avoid false positives on slab pages. + */ if (dir == ITER_DEST) - copied = copy_to_iter(bv_buf, len, uiter); + copied = _copy_to_iter(bv_buf, len, uiter); else - copied = copy_from_iter(bv_buf, len, uiter); + copied = _copy_from_iter(bv_buf, len, uiter); kunmap_local(bv_buf); @@ -2389,8 +2414,14 @@ static void ublk_reset_ch_dev(struct ublk_device *ub) { int i; - for (i = 0; i < ub->dev_info.nr_hw_queues; i++) - ublk_queue_reinit(ub, ublk_get_queue(ub, i)); + for (i = 0; i < ub->dev_info.nr_hw_queues; i++) { + struct ublk_queue *ubq = ublk_get_queue(ub, i); + + /* Sync with ublk_cancel_cmd() */ + spin_lock(&ubq->cancel_lock); + ublk_queue_reinit(ub, ubq); + spin_unlock(&ubq->cancel_lock); + } /* set to NULL, otherwise new tasks cannot mmap io_cmd_buf */ ub->mm = NULL; @@ -2731,6 +2762,7 @@ static void ublk_cancel_cmd(struct ublk_queue *ubq, unsigned tag, { struct ublk_io *io = &ubq->ios[tag]; struct ublk_device *ub = ubq->dev; + struct io_uring_cmd *cmd = NULL; struct request *req; bool done; @@ -2753,12 +2785,15 @@ static void ublk_cancel_cmd(struct ublk_queue *ubq, unsigned tag, spin_lock(&ubq->cancel_lock); done = !!(io->flags & UBLK_IO_FLAG_CANCELED); - if (!done) + if (!done) { io->flags |= UBLK_IO_FLAG_CANCELED; + cmd = io->cmd; + io->cmd = NULL; + } spin_unlock(&ubq->cancel_lock); - if (!done) - io_uring_cmd_done(io->cmd, UBLK_IO_RES_ABORT, issue_flags); + if (!done && cmd) + io_uring_cmd_done(cmd, UBLK_IO_RES_ABORT, issue_flags); } /* @@ -3488,8 +3523,10 @@ static void ublk_ch_uring_cmd_cb(struct io_tw_req tw_req, io_tw_token_t tw) { unsigned int issue_flags = IO_URING_CMD_TASK_WORK_ISSUE_FLAGS; struct io_uring_cmd *cmd = io_uring_cmd_from_tw(tw_req); - int ret = ublk_ch_uring_cmd_local(cmd, issue_flags); + int ret = -ECANCELED; + if (!tw.cancel) + ret = ublk_ch_uring_cmd_local(cmd, issue_flags); if (ret != -EIOCBQUEUED) io_uring_cmd_done(cmd, ret, issue_flags); } @@ -4982,13 +5019,15 @@ static int ublk_ctrl_set_params(struct ublk_device *ub, */ ret = -EACCES; } else if (copy_from_user(&ub->params, argp, ph.len)) { + /* zero out partial copy so no stale params survive */ + memset(&ub->params, 0, sizeof(ub->params)); ret = -EFAULT; } else { /* clear all we don't support yet */ ub->params.types &= UBLK_PARAM_TYPE_ALL; ret = ublk_validate_params(ub); if (ret) - ub->params.types = 0; + memset(&ub->params, 0, sizeof(ub->params)); } mutex_unlock(&ub->mutex); @@ -5413,39 +5452,88 @@ err_free_pages: return ret; } -static int __ublk_ctrl_unreg_buf(struct ublk_device *ub, int buf_index) +static void ublk_unpin_range_pages(unsigned long base_pfn, + unsigned long nr_pages) +{ +#define UBLK_UNPIN_BATCH 32 + struct page *pages[UBLK_UNPIN_BATCH]; + unsigned long off; + + for (off = 0; off < nr_pages; ) { + unsigned int batch = min_t(unsigned long, + nr_pages - off, UBLK_UNPIN_BATCH); + unsigned int j; + + for (j = 0; j < batch; j++) + pages[j] = pfn_to_page(base_pfn + off + j); + unpin_user_pages(pages, batch); + off += batch; + } +} + +/* + * Inner loop: erase up to UBLK_REMOVE_BATCH matching ranges under + * mas_lock, collecting them into an xarray. Then drop the lock and + * unpin pages + free ranges outside spinlock context. + * + * Returns true if the tree walk completed, false if more ranges remain. + * Xarray key is the base PFN, value encodes nr_pages via xa_mk_value(). + */ +#define UBLK_REMOVE_BATCH 64 + +static bool __ublk_shmem_remove_ranges(struct ublk_device *ub, + int buf_index, int *ret) { MA_STATE(mas, &ub->buf_tree, 0, ULONG_MAX); struct ublk_buf_range *range; - struct page *pages[32]; - int ret = -ENOENT; + struct xarray to_unpin; + unsigned long idx; + unsigned int count = 0; + bool done = false; + void *entry; + + xa_init(&to_unpin); mas_lock(&mas); mas_for_each(&mas, range, ULONG_MAX) { - unsigned long base, nr, off; + unsigned long nr; - if (range->buf_index != buf_index) + if (buf_index >= 0 && range->buf_index != buf_index) continue; - ret = 0; - base = mas.index; - nr = mas.last - base + 1; + *ret = 0; + nr = mas.last - mas.index + 1; + if (xa_err(xa_store(&to_unpin, mas.index, + xa_mk_value(nr), GFP_ATOMIC))) + goto unlock; mas_erase(&mas); - - for (off = 0; off < nr; ) { - unsigned int batch = min_t(unsigned long, - nr - off, 32); - unsigned int j; - - for (j = 0; j < batch; j++) - pages[j] = pfn_to_page(base + off + j); - unpin_user_pages(pages, batch); - off += batch; - } kfree(range); + if (++count >= UBLK_REMOVE_BATCH) + goto unlock; } + done = true; +unlock: mas_unlock(&mas); + xa_for_each(&to_unpin, idx, entry) + ublk_unpin_range_pages(idx, xa_to_value(entry)); + xa_destroy(&to_unpin); + + return done; +} + +/* + * Remove ranges from the maple tree matching buf_index, unpin pages + * and free range structs. If buf_index < 0, remove all ranges. + * Processes ranges in batches to avoid holding the maple tree spinlock + * across potentially expensive page unpinning. + */ +static int ublk_shmem_remove_ranges(struct ublk_device *ub, int buf_index) +{ + int ret = -ENOENT; + + while (!__ublk_shmem_remove_ranges(ub, buf_index, &ret)) + cond_resched(); return ret; } @@ -5464,7 +5552,7 @@ static int ublk_ctrl_unreg_buf(struct ublk_device *ub, memflags = ublk_lock_buf_tree(ub); - ret = __ublk_ctrl_unreg_buf(ub, index); + ret = ublk_shmem_remove_ranges(ub, index); if (!ret) ida_free(&ub->buf_ida, index); @@ -5474,27 +5562,7 @@ static int ublk_ctrl_unreg_buf(struct ublk_device *ub, static void ublk_buf_cleanup(struct ublk_device *ub) { - MA_STATE(mas, &ub->buf_tree, 0, ULONG_MAX); - struct ublk_buf_range *range; - struct page *pages[32]; - - mas_for_each(&mas, range, ULONG_MAX) { - unsigned long base = mas.index; - unsigned long nr = mas.last - base + 1; - unsigned long off; - - for (off = 0; off < nr; ) { - unsigned int batch = min_t(unsigned long, - nr - off, 32); - unsigned int j; - - for (j = 0; j < batch; j++) - pages[j] = pfn_to_page(base + off + j); - unpin_user_pages(pages, batch); - off += batch; - } - kfree(range); - } + ublk_shmem_remove_ranges(ub, -1); mtree_destroy(&ub->buf_tree); ida_destroy(&ub->buf_ida); } diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c index 8baf642037fd..55eeb6aac0ea 100644 --- a/drivers/block/zloop.c +++ b/drivers/block/zloop.c @@ -288,12 +288,29 @@ static bool zloop_do_open_zone(struct zloop_device *zlo, } } +static void zloop_mark_full(struct zloop_device *zlo, struct zloop_zone *zone) +{ + lockdep_assert_held(&zone->wp_lock); + + zloop_lru_remove_open_zone(zlo, zone); + zone->cond = BLK_ZONE_COND_FULL; + zone->wp = ULLONG_MAX; +} + +static void zloop_mark_empty(struct zloop_device *zlo, struct zloop_zone *zone) +{ + lockdep_assert_held(&zone->wp_lock); + + zloop_lru_remove_open_zone(zlo, zone); + zone->cond = BLK_ZONE_COND_EMPTY; + zone->wp = zone->start; +} + static int zloop_update_seq_zone(struct zloop_device *zlo, unsigned int zone_no) { struct zloop_zone *zone = &zlo->zones[zone_no]; struct kstat stat; sector_t file_sectors; - unsigned long flags; int ret; lockdep_assert_held(&zone->lock); @@ -313,28 +330,24 @@ static int zloop_update_seq_zone(struct zloop_device *zlo, unsigned int zone_no) return -EINVAL; } - if (file_sectors & ((zlo->block_size >> SECTOR_SHIFT) - 1)) { - pr_err("Zone %u file size not aligned to block size %u\n", - zone_no, zlo->block_size); + if (!IS_ALIGNED(stat.size, zlo->block_size)) { + pr_err("Zone %u file size (%llu) not aligned to block size %u\n", + zone_no, stat.size, zlo->block_size); return -EINVAL; } - spin_lock_irqsave(&zone->wp_lock, flags); + spin_lock(&zone->wp_lock); if (!file_sectors) { - zloop_lru_remove_open_zone(zlo, zone); - zone->cond = BLK_ZONE_COND_EMPTY; - zone->wp = zone->start; + zloop_mark_empty(zlo, zone); } else if (file_sectors == zlo->zone_capacity) { - zloop_lru_remove_open_zone(zlo, zone); - zone->cond = BLK_ZONE_COND_FULL; - zone->wp = ULLONG_MAX; + zloop_mark_full(zlo, zone); } else { if (zone->cond != BLK_ZONE_COND_IMP_OPEN && zone->cond != BLK_ZONE_COND_EXP_OPEN) zone->cond = BLK_ZONE_COND_CLOSED; zone->wp = zone->start + file_sectors; } - spin_unlock_irqrestore(&zone->wp_lock, flags); + spin_unlock(&zone->wp_lock); return 0; } @@ -367,7 +380,6 @@ unlock: static int zloop_close_zone(struct zloop_device *zlo, unsigned int zone_no) { struct zloop_zone *zone = &zlo->zones[zone_no]; - unsigned long flags; int ret = 0; if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) @@ -386,13 +398,13 @@ static int zloop_close_zone(struct zloop_device *zlo, unsigned int zone_no) break; case BLK_ZONE_COND_IMP_OPEN: case BLK_ZONE_COND_EXP_OPEN: - spin_lock_irqsave(&zone->wp_lock, flags); + spin_lock(&zone->wp_lock); zloop_lru_remove_open_zone(zlo, zone); if (zone->wp == zone->start) zone->cond = BLK_ZONE_COND_EMPTY; else zone->cond = BLK_ZONE_COND_CLOSED; - spin_unlock_irqrestore(&zone->wp_lock, flags); + spin_unlock(&zone->wp_lock); break; case BLK_ZONE_COND_EMPTY: case BLK_ZONE_COND_FULL: @@ -410,7 +422,6 @@ unlock: static int zloop_reset_zone(struct zloop_device *zlo, unsigned int zone_no) { struct zloop_zone *zone = &zlo->zones[zone_no]; - unsigned long flags; int ret = 0; if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) @@ -428,12 +439,10 @@ static int zloop_reset_zone(struct zloop_device *zlo, unsigned int zone_no) goto unlock; } - spin_lock_irqsave(&zone->wp_lock, flags); - zloop_lru_remove_open_zone(zlo, zone); - zone->cond = BLK_ZONE_COND_EMPTY; - zone->wp = zone->start; + spin_lock(&zone->wp_lock); + zloop_mark_empty(zlo, zone); clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags); - spin_unlock_irqrestore(&zone->wp_lock, flags); + spin_unlock(&zone->wp_lock); unlock: mutex_unlock(&zone->lock); @@ -458,7 +467,6 @@ static int zloop_reset_all_zones(struct zloop_device *zlo) static int zloop_finish_zone(struct zloop_device *zlo, unsigned int zone_no) { struct zloop_zone *zone = &zlo->zones[zone_no]; - unsigned long flags; int ret = 0; if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) @@ -476,12 +484,10 @@ static int zloop_finish_zone(struct zloop_device *zlo, unsigned int zone_no) goto unlock; } - spin_lock_irqsave(&zone->wp_lock, flags); - zloop_lru_remove_open_zone(zlo, zone); - zone->cond = BLK_ZONE_COND_FULL; - zone->wp = ULLONG_MAX; + spin_lock(&zone->wp_lock); + zloop_mark_full(zlo, zone); clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags); - spin_unlock_irqrestore(&zone->wp_lock, flags); + spin_unlock(&zone->wp_lock); unlock: mutex_unlock(&zone->lock); @@ -571,10 +577,9 @@ static int zloop_seq_write_prep(struct zloop_cmd *cmd) bool is_append = req_op(rq) == REQ_OP_ZONE_APPEND; struct zloop_zone *zone = &zlo->zones[zone_no]; sector_t zone_end = zone->start + zlo->zone_capacity; - unsigned long flags; int ret = 0; - spin_lock_irqsave(&zone->wp_lock, flags); + spin_lock(&zone->wp_lock); /* * Zone append operations always go at the current write pointer, but @@ -616,14 +621,11 @@ static int zloop_seq_write_prep(struct zloop_cmd *cmd) */ if (!is_append || !zlo->ordered_zone_append) { zone->wp += nr_sectors; - if (zone->wp == zone_end) { - zloop_lru_remove_open_zone(zlo, zone); - zone->cond = BLK_ZONE_COND_FULL; - zone->wp = ULLONG_MAX; - } + if (zone->wp == zone_end) + zloop_mark_full(zlo, zone); } out_unlock: - spin_unlock_irqrestore(&zone->wp_lock, flags); + spin_unlock(&zone->wp_lock); return ret; } @@ -861,25 +863,21 @@ static bool zloop_set_zone_append_sector(struct request *rq) struct zloop_zone *zone = &zlo->zones[zone_no]; sector_t zone_end = zone->start + zlo->zone_capacity; sector_t nr_sectors = blk_rq_sectors(rq); - unsigned long flags; - spin_lock_irqsave(&zone->wp_lock, flags); + spin_lock(&zone->wp_lock); if (zone->cond == BLK_ZONE_COND_FULL || zone->wp + nr_sectors > zone_end) { - spin_unlock_irqrestore(&zone->wp_lock, flags); + spin_unlock(&zone->wp_lock); return false; } rq->__sector = zone->wp; zone->wp += blk_rq_sectors(rq); - if (zone->wp >= zone_end) { - zloop_lru_remove_open_zone(zlo, zone); - zone->cond = BLK_ZONE_COND_FULL; - zone->wp = ULLONG_MAX; - } + if (zone->wp >= zone_end) + zloop_mark_full(zlo, zone); - spin_unlock_irqrestore(&zone->wp_lock, flags); + spin_unlock(&zone->wp_lock); return true; } @@ -891,8 +889,10 @@ static blk_status_t zloop_queue_rq(struct blk_mq_hw_ctx *hctx, struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq); struct zloop_device *zlo = rq->q->queuedata; - if (data_race(READ_ONCE(zlo->state)) == Zlo_deleting) + if (data_race(READ_ONCE(zlo->state)) == Zlo_deleting) { + rq->rq_flags |= RQF_QUIET; return BLK_STS_IOERR; + } /* * If we need to strongly order zone append operations, set the request @@ -938,7 +938,6 @@ static int zloop_report_zones(struct gendisk *disk, sector_t sector, struct zloop_device *zlo = disk->private_data; struct blk_zone blkz = {}; unsigned int first, i; - unsigned long flags; int ret; first = disk_zone_no(disk, sector); @@ -962,9 +961,9 @@ static int zloop_report_zones(struct gendisk *disk, sector_t sector, blkz.start = zone->start; blkz.len = zlo->zone_size; - spin_lock_irqsave(&zone->wp_lock, flags); + spin_lock(&zone->wp_lock); blkz.wp = zone->wp; - spin_unlock_irqrestore(&zone->wp_lock, flags); + spin_unlock(&zone->wp_lock); blkz.cond = zone->cond; if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) { blkz.type = BLK_ZONE_TYPE_CONVENTIONAL; @@ -1363,20 +1362,6 @@ out: return ret; } -static void zloop_truncate(struct file *file, loff_t pos) -{ - struct mnt_idmap *idmap = file_mnt_idmap(file); - struct dentry *dentry = file_dentry(file); - struct iattr newattrs; - - newattrs.ia_size = pos; - newattrs.ia_valid = ATTR_SIZE; - - inode_lock(dentry->d_inode); - notify_change(idmap, dentry, &newattrs, NULL); - inode_unlock(dentry->d_inode); -} - static void zloop_forget_cache(struct zloop_device *zlo) { unsigned int i; @@ -1401,8 +1386,18 @@ static void zloop_forget_cache(struct zloop_device *zlo) zlo->disk->part0, ret); continue; } - if (old_wp < zone->wp) - zloop_truncate(file, old_wp); + + if (old_wp > zone->wp) + continue; + /* + * This should not happen, if we recored a full zone, it can't + * be active. + */ + if (WARN_ON_ONCE(old_wp == ULLONG_MAX)) + continue; + + vfs_truncate(&file->f_path, + (old_wp - zone->start) << SECTOR_SHIFT); } } diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index c2afd1c34f4a..aebc710f0d6a 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -2546,6 +2546,8 @@ static ssize_t recompress_store(struct device *dev, mode = RECOMPRESS_HUGE; if (!strcmp(val, "huge_idle")) mode = RECOMPRESS_IDLE | RECOMPRESS_HUGE; + if (!mode) + return -EINVAL; continue; } @@ -2678,7 +2680,7 @@ static void zram_bio_discard(struct zram *zram, struct bio *bio) */ if (offset) { if (n <= (PAGE_SIZE - offset)) - return; + goto end_bio; n -= (PAGE_SIZE - offset); index++; @@ -2693,6 +2695,7 @@ static void zram_bio_discard(struct zram *zram, struct bio *bio) n -= PAGE_SIZE; } +end_bio: bio_endio(bio); } diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 2f59c0d6f9ec..a3643e67b33f 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -289,6 +289,9 @@ static inline void btintel_pcie_dump_debug_registers(struct hci_dev *hdev) skb_put_data(skb, buf, strlen(buf)); data->boot_stage_cache = reg; + if (reg & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_WARNING) + bt_dev_warn(hdev, "Controller device warning (boot_stage: 0x%8.8x)", reg); + reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_IPC_STATUS_REG); snprintf(buf, sizeof(buf), "ipc status: 0x%8.8x", reg); skb_put_data(skb, buf, strlen(buf)); @@ -880,8 +883,11 @@ static inline bool btintel_pcie_in_lockdown(struct btintel_pcie_data *data) static inline bool btintel_pcie_in_error(struct btintel_pcie_data *data) { - return (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_ERR) || - (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER); + if (data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_WARNING) + bt_dev_warn(data->hdev, "Controller device warning (boot_stage: 0x%8.8x)", + data->boot_stage_cache); + + return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER; } static void btintel_pcie_msix_gp1_handler(struct btintel_pcie_data *data) @@ -914,7 +920,8 @@ static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data) data->img_resp_cache = reg; if (btintel_pcie_in_error(data)) { - bt_dev_err(data->hdev, "Controller in error state"); + bt_dev_err(data->hdev, "Controller in error state (boot_stage: 0x%8.8x)", + data->boot_stage_cache); btintel_pcie_dump_debug_registers(data->hdev); return; } diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h index 3c7bb708362d..f922abd1e7d8 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -48,7 +48,7 @@ #define BTINTEL_PCIE_CSR_BOOT_STAGE_OPFW (BIT(2)) #define BTINTEL_PCIE_CSR_BOOT_STAGE_ROM_LOCKDOWN (BIT(10)) #define BTINTEL_PCIE_CSR_BOOT_STAGE_IML_LOCKDOWN (BIT(11)) -#define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_ERR (BIT(12)) +#define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_WARNING (BIT(12)) #define BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER (BIT(13)) #define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED (BIT(14)) #define BTINTEL_PCIE_CSR_BOOT_STAGE_MAC_ACCESS_ON (BIT(16)) diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index 6fb6ca274808..f70c1b0f8990 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -695,8 +695,13 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev, if (data->evt_skb == NULL) goto err_free_wc; - /* Parse and handle the return WMT event */ - wmt_evt = (struct btmtk_hci_wmt_evt *)data->evt_skb->data; + wmt_evt = skb_pull_data(data->evt_skb, sizeof(*wmt_evt)); + if (!wmt_evt) { + bt_dev_err(hdev, "WMT event too short (%u bytes)", + data->evt_skb->len); + err = -EINVAL; + goto err_free_skb; + } if (wmt_evt->whdr.op != hdr->op) { bt_dev_err(hdev, "Wrong op received %d expected %d", wmt_evt->whdr.op, hdr->op); @@ -712,6 +717,12 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev, status = BTMTK_WMT_PATCH_DONE; break; case BTMTK_WMT_FUNC_CTRL: + if (!skb_pull_data(data->evt_skb, + sizeof(wmt_evt_funcc->status))) { + err = -EINVAL; + goto err_free_skb; + } + wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt; if (be16_to_cpu(wmt_evt_funcc->status) == 0x404) status = BTMTK_WMT_ON_DONE; diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c index fa679ad0acdf..8201fa7f61e8 100644 --- a/drivers/bluetooth/hci_ath.c +++ b/drivers/bluetooth/hci_ath.c @@ -191,6 +191,9 @@ static int ath_recv(struct hci_uart *hu, const void *data, int count) { struct ath_struct *ath = hu->priv; + if (!ath) + return -ENODEV; + ath->rx_skb = h4_recv_buf(hu, ath->rx_skb, data, count, ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts)); if (IS_ERR(ath->rx_skb)) { diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index b386f91d8b46..db56eead27ce 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -585,6 +585,9 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count) if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) return -EUNATCH; + if (!bcsp) + return -ENODEV; + BT_DBG("hu %p count %d rx_state %d rx_count %ld", hu, count, bcsp->rx_state, bcsp->rx_count); diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index a889a66a326f..767372707498 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -109,6 +109,9 @@ static int h4_recv(struct hci_uart *hu, const void *data, int count) { struct h4_struct *h4 = hu->priv; + if (!h4) + return -ENODEV; + h4->rx_skb = h4_recv_buf(hu, h4->rx_skb, data, count, h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts)); if (IS_ERR(h4->rx_skb)) { diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index cfdf75dc2847..d35383718212 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -587,6 +587,9 @@ static int h5_recv(struct hci_uart *hu, const void *data, int count) struct h5 *h5 = hu->priv; const unsigned char *ptr = data; + if (!h5) + return -ENODEV; + BT_DBG("%s pending %zu count %d", hu->hdev->name, h5->rx_pending, count); diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c index 76d61af8a275..140ab55c9fc5 100644 --- a/drivers/bluetooth/virtio_bt.c +++ b/drivers/bluetooth/virtio_bt.c @@ -12,6 +12,7 @@ #include #define VERSION "0.1" +#define VIRTBT_RX_BUF_SIZE 1000 enum { VIRTBT_VQ_TX, @@ -33,11 +34,11 @@ static int virtbt_add_inbuf(struct virtio_bluetooth *vbt) struct sk_buff *skb; int err; - skb = alloc_skb(1000, GFP_KERNEL); + skb = alloc_skb(VIRTBT_RX_BUF_SIZE, GFP_KERNEL); if (!skb) return -ENOMEM; - sg_init_one(sg, skb->data, 1000); + sg_init_one(sg, skb->data, VIRTBT_RX_BUF_SIZE); err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL); if (err < 0) { @@ -197,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev) static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb) { + size_t min_hdr; __u8 pkt_type; pkt_type = *((__u8 *) skb->data); @@ -204,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb) switch (pkt_type) { case HCI_EVENT_PKT: + min_hdr = sizeof(struct hci_event_hdr); + break; case HCI_ACLDATA_PKT: + min_hdr = sizeof(struct hci_acl_hdr); + break; case HCI_SCODATA_PKT: + min_hdr = sizeof(struct hci_sco_hdr); + break; case HCI_ISODATA_PKT: - hci_skb_pkt_type(skb) = pkt_type; - hci_recv_frame(vbt->hdev, skb); + min_hdr = sizeof(struct hci_iso_hdr); break; default: kfree_skb(skb); - break; + return; } + + if (skb->len < min_hdr) { + bt_dev_err_ratelimited(vbt->hdev, + "rx pkt_type 0x%02x payload %u < hdr %zu\n", + pkt_type, skb->len, min_hdr); + kfree_skb(skb); + return; + } + + hci_skb_pkt_type(skb) = pkt_type; + hci_recv_frame(vbt->hdev, skb); } static void virtbt_rx_work(struct work_struct *work) @@ -227,8 +245,15 @@ static void virtbt_rx_work(struct work_struct *work) if (!skb) return; - skb_put(skb, len); - virtbt_rx_handle(vbt, skb); + if (!len || len > VIRTBT_RX_BUF_SIZE) { + bt_dev_err_ratelimited(vbt->hdev, + "rx reply len %u outside [1, %u]\n", + len, VIRTBT_RX_BUF_SIZE); + kfree_skb(skb); + } else { + skb_put(skb, len); + virtbt_rx_handle(vbt, skb); + } if (virtbt_add_inbuf(vbt) < 0) return; diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 2a1b46f07080..3181d8aa32a3 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -38,36 +38,6 @@ config BRCMSTB_GISB_ARB arbiter. This driver provides timeout and target abort error handling and internal bus master decoding. -config BT1_APB - bool "Baikal-T1 APB-bus driver" - depends on MIPS_BAIKAL_T1 || COMPILE_TEST - select REGMAP_MMIO - help - Baikal-T1 AXI-APB bridge is used to access the SoC subsystem CSRs. - IO requests are routed to this bus by means of the DW AMBA 3 AXI - Interconnect. In case of any APB protocol collisions, slave device - not responding on timeout an IRQ is raised with an erroneous address - reported to the APB terminator (APB Errors Handler Block). This - driver provides the interrupt handler to detect the erroneous - address, prints an error message about the address fault, updates an - errors counter. The counter and the APB-bus operations timeout can be - accessed via corresponding sysfs nodes. - -config BT1_AXI - bool "Baikal-T1 AXI-bus driver" - depends on MIPS_BAIKAL_T1 || COMPILE_TEST - select MFD_SYSCON - help - AXI3-bus is the main communication bus connecting all high-speed - peripheral IP-cores with RAM controller and with MIPS P5600 cores on - Baikal-T1 SoC. Traffic arbitration is done by means of DW AMBA 3 AXI - Interconnect (so called AXI Main Interconnect) routing IO requests - from one SoC block to another. This driver provides a way to detect - any bus protocol errors and device not responding situations by - means of an embedded on top of the interconnect errors handler - block (EHB). AXI Interconnect QoS arbitration tuning is currently - unsupported. - config MOXTET tristate "CZ.NIC Turris Mox module configuration bus" depends on SPI_MASTER && OF @@ -169,6 +139,16 @@ config QCOM_SSC_BLOCK_BUS i2c/spi/uart controllers, a hexagon core, and a clock controller which provides clocks for the above. +config STM32_DBG_BUS + tristate "OP-TEE based debug access bus" + depends on OPTEE && STM32_FIREWALL + depends on ARCH_STM32 || COMPILE_TEST + help + Select this to get the support for the OP-TEE based STM32 debug bus + driver that is used to handle debug-related peripherals on STM32 + platforms when the debug configuration is not accessible by the + normal world. + config STM32_FIREWALL bool "STM32 Firewall framework" depends on (ARCH_STM32 || COMPILE_TEST) && OF diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 8e693fe8a03a..a01f97fef3e8 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -13,8 +13,6 @@ obj-$(CONFIG_MOXTET) += moxtet.o # DPAA2 fsl-mc bus obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/ -obj-$(CONFIG_BT1_APB) += bt1-apb.o -obj-$(CONFIG_BT1_AXI) += bt1-axi.o obj-$(CONFIG_IMX_AIPSTZ) += imx-aipstz.o obj-$(CONFIG_IMX_WEIM) += imx-weim.o obj-$(CONFIG_INTEL_IXP4XX_EB) += intel-ixp4xx-eb.o @@ -27,6 +25,7 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o obj-$(CONFIG_QCOM_EBI2) += qcom-ebi2.o obj-$(CONFIG_QCOM_SSC_BLOCK_BUS) += qcom-ssc-block-bus.o +obj-$(CONFIG_STM32_DBG_BUS) += stm32_dbg_bus.o obj-$(CONFIG_STM32_FIREWALL) += stm32_firewall.o stm32_rifsc.o stm32_etzpc.o obj-$(CONFIG_SUN50I_DE2_BUS) += sun50i-de2.o obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o diff --git a/drivers/bus/bt1-apb.c b/drivers/bus/bt1-apb.c deleted file mode 100644 index 7463124b6dd9..000000000000 --- a/drivers/bus/bt1-apb.c +++ /dev/null @@ -1,396 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC - * - * Authors: - * Serge Semin - * - * Baikal-T1 APB-bus driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define APB_EHB_ISR 0x00 -#define APB_EHB_ISR_PENDING BIT(0) -#define APB_EHB_ISR_MASK BIT(1) -#define APB_EHB_ADDR 0x04 -#define APB_EHB_TIMEOUT 0x08 - -#define APB_EHB_TIMEOUT_MIN 0x000003FFU -#define APB_EHB_TIMEOUT_MAX 0xFFFFFFFFU - -/* - * struct bt1_apb - Baikal-T1 APB EHB private data - * @dev: Pointer to the device structure. - * @regs: APB EHB registers map. - * @res: No-device error injection memory region. - * @irq: Errors IRQ number. - * @rate: APB-bus reference clock rate. - * @pclk: APB-reference clock. - * @prst: APB domain reset line. - * @count: Number of errors detected. - */ -struct bt1_apb { - struct device *dev; - - struct regmap *regs; - void __iomem *res; - int irq; - - unsigned long rate; - struct clk *pclk; - - struct reset_control *prst; - - atomic_t count; -}; - -static const struct regmap_config bt1_apb_regmap_cfg = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = APB_EHB_TIMEOUT, - .fast_io = true -}; - -static inline unsigned long bt1_apb_n_to_timeout_us(struct bt1_apb *apb, u32 n) -{ - u64 timeout = (u64)n * USEC_PER_SEC; - - do_div(timeout, apb->rate); - - return timeout; - -} - -static inline unsigned long bt1_apb_timeout_to_n_us(struct bt1_apb *apb, - unsigned long timeout) -{ - u64 n = (u64)timeout * apb->rate; - - do_div(n, USEC_PER_SEC); - - return n; - -} - -static irqreturn_t bt1_apb_isr(int irq, void *data) -{ - struct bt1_apb *apb = data; - u32 addr = 0; - - regmap_read(apb->regs, APB_EHB_ADDR, &addr); - - dev_crit_ratelimited(apb->dev, - "APB-bus fault %d: Slave access timeout at 0x%08x\n", - atomic_inc_return(&apb->count), - addr); - - /* - * Print backtrace on each CPU. This might be pointless if the fault - * has happened on the same CPU as the IRQ handler is executed or - * the other core proceeded further execution despite the error. - * But if it's not, by looking at the trace we would get straight to - * the cause of the problem. - */ - trigger_all_cpu_backtrace(); - - regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_PENDING, 0); - - return IRQ_HANDLED; -} - -static void bt1_apb_clear_data(void *data) -{ - struct bt1_apb *apb = data; - struct platform_device *pdev = to_platform_device(apb->dev); - - platform_set_drvdata(pdev, NULL); -} - -static struct bt1_apb *bt1_apb_create_data(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct bt1_apb *apb; - int ret; - - apb = devm_kzalloc(dev, sizeof(*apb), GFP_KERNEL); - if (!apb) - return ERR_PTR(-ENOMEM); - - ret = devm_add_action(dev, bt1_apb_clear_data, apb); - if (ret) { - dev_err(dev, "Can't add APB EHB data clear action\n"); - return ERR_PTR(ret); - } - - apb->dev = dev; - atomic_set(&apb->count, 0); - platform_set_drvdata(pdev, apb); - - return apb; -} - -static int bt1_apb_request_regs(struct bt1_apb *apb) -{ - struct platform_device *pdev = to_platform_device(apb->dev); - void __iomem *regs; - - regs = devm_platform_ioremap_resource_byname(pdev, "ehb"); - if (IS_ERR(regs)) { - dev_err(apb->dev, "Couldn't map APB EHB registers\n"); - return PTR_ERR(regs); - } - - apb->regs = devm_regmap_init_mmio(apb->dev, regs, &bt1_apb_regmap_cfg); - if (IS_ERR(apb->regs)) { - dev_err(apb->dev, "Couldn't create APB EHB regmap\n"); - return PTR_ERR(apb->regs); - } - - apb->res = devm_platform_ioremap_resource_byname(pdev, "nodev"); - if (IS_ERR(apb->res)) - dev_err(apb->dev, "Couldn't map reserved region\n"); - - return PTR_ERR_OR_ZERO(apb->res); -} - -static int bt1_apb_request_rst(struct bt1_apb *apb) -{ - int ret; - - apb->prst = devm_reset_control_get_optional_exclusive(apb->dev, "prst"); - if (IS_ERR(apb->prst)) - return dev_err_probe(apb->dev, PTR_ERR(apb->prst), - "Couldn't get reset control line\n"); - - ret = reset_control_deassert(apb->prst); - if (ret) - dev_err(apb->dev, "Failed to deassert the reset line\n"); - - return ret; -} - -static int bt1_apb_request_clk(struct bt1_apb *apb) -{ - apb->pclk = devm_clk_get_enabled(apb->dev, "pclk"); - if (IS_ERR(apb->pclk)) - return dev_err_probe(apb->dev, PTR_ERR(apb->pclk), - "Couldn't get APB clock descriptor\n"); - - apb->rate = clk_get_rate(apb->pclk); - if (!apb->rate) { - dev_err(apb->dev, "Invalid clock rate\n"); - return -EINVAL; - } - - return 0; -} - -static void bt1_apb_clear_irq(void *data) -{ - struct bt1_apb *apb = data; - - regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_MASK, 0); -} - -static int bt1_apb_request_irq(struct bt1_apb *apb) -{ - struct platform_device *pdev = to_platform_device(apb->dev); - int ret; - - apb->irq = platform_get_irq(pdev, 0); - if (apb->irq < 0) - return apb->irq; - - ret = devm_request_irq(apb->dev, apb->irq, bt1_apb_isr, IRQF_SHARED, - "bt1-apb", apb); - if (ret) { - dev_err(apb->dev, "Couldn't request APB EHB IRQ\n"); - return ret; - } - - ret = devm_add_action(apb->dev, bt1_apb_clear_irq, apb); - if (ret) { - dev_err(apb->dev, "Can't add APB EHB IRQs clear action\n"); - return ret; - } - - /* Unmask IRQ and clear it' pending flag. */ - regmap_update_bits(apb->regs, APB_EHB_ISR, - APB_EHB_ISR_PENDING | APB_EHB_ISR_MASK, - APB_EHB_ISR_MASK); - - return 0; -} - -static ssize_t count_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct bt1_apb *apb = dev_get_drvdata(dev); - - return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&apb->count)); -} -static DEVICE_ATTR_RO(count); - -static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct bt1_apb *apb = dev_get_drvdata(dev); - unsigned long timeout; - int ret; - u32 n; - - ret = regmap_read(apb->regs, APB_EHB_TIMEOUT, &n); - if (ret) - return ret; - - timeout = bt1_apb_n_to_timeout_us(apb, n); - - return scnprintf(buf, PAGE_SIZE, "%lu\n", timeout); -} - -static ssize_t timeout_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct bt1_apb *apb = dev_get_drvdata(dev); - unsigned long timeout; - int ret; - u32 n; - - if (kstrtoul(buf, 0, &timeout) < 0) - return -EINVAL; - - n = bt1_apb_timeout_to_n_us(apb, timeout); - n = clamp(n, APB_EHB_TIMEOUT_MIN, APB_EHB_TIMEOUT_MAX); - - ret = regmap_write(apb->regs, APB_EHB_TIMEOUT, n); - - return ret ?: count; -} -static DEVICE_ATTR_RW(timeout); - -static ssize_t inject_error_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return scnprintf(buf, PAGE_SIZE, "Error injection: nodev irq\n"); -} - -static ssize_t inject_error_store(struct device *dev, - struct device_attribute *attr, - const char *data, size_t count) -{ - struct bt1_apb *apb = dev_get_drvdata(dev); - - /* - * Either dummy read from the unmapped address in the APB IO area - * or manually set the IRQ status. - */ - if (sysfs_streq(data, "nodev")) - readl(apb->res); - else if (sysfs_streq(data, "irq")) - regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_PENDING, - APB_EHB_ISR_PENDING); - else - return -EINVAL; - - return count; -} -static DEVICE_ATTR_RW(inject_error); - -static struct attribute *bt1_apb_sysfs_attrs[] = { - &dev_attr_count.attr, - &dev_attr_timeout.attr, - &dev_attr_inject_error.attr, - NULL -}; -ATTRIBUTE_GROUPS(bt1_apb_sysfs); - -static void bt1_apb_remove_sysfs(void *data) -{ - struct bt1_apb *apb = data; - - device_remove_groups(apb->dev, bt1_apb_sysfs_groups); -} - -static int bt1_apb_init_sysfs(struct bt1_apb *apb) -{ - int ret; - - ret = device_add_groups(apb->dev, bt1_apb_sysfs_groups); - if (ret) { - dev_err(apb->dev, "Failed to create EHB APB sysfs nodes\n"); - return ret; - } - - ret = devm_add_action_or_reset(apb->dev, bt1_apb_remove_sysfs, apb); - if (ret) - dev_err(apb->dev, "Can't add APB EHB sysfs remove action\n"); - - return ret; -} - -static int bt1_apb_probe(struct platform_device *pdev) -{ - struct bt1_apb *apb; - int ret; - - apb = bt1_apb_create_data(pdev); - if (IS_ERR(apb)) - return PTR_ERR(apb); - - ret = bt1_apb_request_regs(apb); - if (ret) - return ret; - - ret = bt1_apb_request_rst(apb); - if (ret) - return ret; - - ret = bt1_apb_request_clk(apb); - if (ret) - return ret; - - ret = bt1_apb_request_irq(apb); - if (ret) - return ret; - - ret = bt1_apb_init_sysfs(apb); - if (ret) - return ret; - - return 0; -} - -static const struct of_device_id bt1_apb_of_match[] = { - { .compatible = "baikal,bt1-apb" }, - { } -}; -MODULE_DEVICE_TABLE(of, bt1_apb_of_match); - -static struct platform_driver bt1_apb_driver = { - .probe = bt1_apb_probe, - .driver = { - .name = "bt1-apb", - .of_match_table = bt1_apb_of_match - } -}; -module_platform_driver(bt1_apb_driver); - -MODULE_AUTHOR("Serge Semin "); -MODULE_DESCRIPTION("Baikal-T1 APB-bus driver"); diff --git a/drivers/bus/bt1-axi.c b/drivers/bus/bt1-axi.c deleted file mode 100644 index a5254c73bf43..000000000000 --- a/drivers/bus/bt1-axi.c +++ /dev/null @@ -1,292 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC - * - * Authors: - * Serge Semin - * - * Baikal-T1 AXI-bus driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT1_AXI_WERRL 0x110 -#define BT1_AXI_WERRH 0x114 -#define BT1_AXI_WERRH_TYPE BIT(23) -#define BT1_AXI_WERRH_ADDR_FLD 24 -#define BT1_AXI_WERRH_ADDR_MASK GENMASK(31, BT1_AXI_WERRH_ADDR_FLD) - -/* - * struct bt1_axi - Baikal-T1 AXI-bus private data - * @dev: Pointer to the device structure. - * @qos_regs: AXI Interconnect QoS tuning registers. - * @sys_regs: Baikal-T1 System Controller registers map. - * @irq: Errors IRQ number. - * @aclk: AXI reference clock. - * @arst: AXI Interconnect reset line. - * @count: Number of errors detected. - */ -struct bt1_axi { - struct device *dev; - - void __iomem *qos_regs; - struct regmap *sys_regs; - int irq; - - struct clk *aclk; - - struct reset_control *arst; - - atomic_t count; -}; - -static irqreturn_t bt1_axi_isr(int irq, void *data) -{ - struct bt1_axi *axi = data; - u32 low = 0, high = 0; - - regmap_read(axi->sys_regs, BT1_AXI_WERRL, &low); - regmap_read(axi->sys_regs, BT1_AXI_WERRH, &high); - - dev_crit_ratelimited(axi->dev, - "AXI-bus fault %d: %s at 0x%x%08x\n", - atomic_inc_return(&axi->count), - high & BT1_AXI_WERRH_TYPE ? "no slave" : "slave protocol error", - high, low); - - /* - * Print backtrace on each CPU. This might be pointless if the fault - * has happened on the same CPU as the IRQ handler is executed or - * the other core proceeded further execution despite the error. - * But if it's not, by looking at the trace we would get straight to - * the cause of the problem. - */ - trigger_all_cpu_backtrace(); - - return IRQ_HANDLED; -} - -static void bt1_axi_clear_data(void *data) -{ - struct bt1_axi *axi = data; - struct platform_device *pdev = to_platform_device(axi->dev); - - platform_set_drvdata(pdev, NULL); -} - -static struct bt1_axi *bt1_axi_create_data(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct bt1_axi *axi; - int ret; - - axi = devm_kzalloc(dev, sizeof(*axi), GFP_KERNEL); - if (!axi) - return ERR_PTR(-ENOMEM); - - ret = devm_add_action(dev, bt1_axi_clear_data, axi); - if (ret) { - dev_err(dev, "Can't add AXI EHB data clear action\n"); - return ERR_PTR(ret); - } - - axi->dev = dev; - atomic_set(&axi->count, 0); - platform_set_drvdata(pdev, axi); - - return axi; -} - -static int bt1_axi_request_regs(struct bt1_axi *axi) -{ - struct platform_device *pdev = to_platform_device(axi->dev); - struct device *dev = axi->dev; - - axi->sys_regs = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); - if (IS_ERR(axi->sys_regs)) { - dev_err(dev, "Couldn't find syscon registers\n"); - return PTR_ERR(axi->sys_regs); - } - - axi->qos_regs = devm_platform_ioremap_resource_byname(pdev, "qos"); - if (IS_ERR(axi->qos_regs)) - dev_err(dev, "Couldn't map AXI-bus QoS registers\n"); - - return PTR_ERR_OR_ZERO(axi->qos_regs); -} - -static int bt1_axi_request_rst(struct bt1_axi *axi) -{ - int ret; - - axi->arst = devm_reset_control_get_optional_exclusive(axi->dev, "arst"); - if (IS_ERR(axi->arst)) - return dev_err_probe(axi->dev, PTR_ERR(axi->arst), - "Couldn't get reset control line\n"); - - ret = reset_control_deassert(axi->arst); - if (ret) - dev_err(axi->dev, "Failed to deassert the reset line\n"); - - return ret; -} - -static int bt1_axi_request_clk(struct bt1_axi *axi) -{ - axi->aclk = devm_clk_get_enabled(axi->dev, "aclk"); - if (IS_ERR(axi->aclk)) - return dev_err_probe(axi->dev, PTR_ERR(axi->aclk), - "Couldn't get AXI Interconnect clock\n"); - - return 0; -} - -static int bt1_axi_request_irq(struct bt1_axi *axi) -{ - struct platform_device *pdev = to_platform_device(axi->dev); - int ret; - - axi->irq = platform_get_irq(pdev, 0); - if (axi->irq < 0) - return axi->irq; - - ret = devm_request_irq(axi->dev, axi->irq, bt1_axi_isr, IRQF_SHARED, - "bt1-axi", axi); - if (ret) - dev_err(axi->dev, "Couldn't request AXI EHB IRQ\n"); - - return ret; -} - -static ssize_t count_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct bt1_axi *axi = dev_get_drvdata(dev); - - return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&axi->count)); -} -static DEVICE_ATTR_RO(count); - -static ssize_t inject_error_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return scnprintf(buf, PAGE_SIZE, "Error injection: bus unaligned\n"); -} - -static ssize_t inject_error_store(struct device *dev, - struct device_attribute *attr, - const char *data, size_t count) -{ - struct bt1_axi *axi = dev_get_drvdata(dev); - - /* - * Performing unaligned read from the memory will cause the CM2 bus - * error while unaligned writing - the AXI bus write error handled - * by this driver. - */ - if (sysfs_streq(data, "bus")) - readb(axi->qos_regs); - else if (sysfs_streq(data, "unaligned")) - writeb(0, axi->qos_regs); - else - return -EINVAL; - - return count; -} -static DEVICE_ATTR_RW(inject_error); - -static struct attribute *bt1_axi_sysfs_attrs[] = { - &dev_attr_count.attr, - &dev_attr_inject_error.attr, - NULL -}; -ATTRIBUTE_GROUPS(bt1_axi_sysfs); - -static void bt1_axi_remove_sysfs(void *data) -{ - struct bt1_axi *axi = data; - - device_remove_groups(axi->dev, bt1_axi_sysfs_groups); -} - -static int bt1_axi_init_sysfs(struct bt1_axi *axi) -{ - int ret; - - ret = device_add_groups(axi->dev, bt1_axi_sysfs_groups); - if (ret) { - dev_err(axi->dev, "Failed to add sysfs files group\n"); - return ret; - } - - ret = devm_add_action_or_reset(axi->dev, bt1_axi_remove_sysfs, axi); - if (ret) - dev_err(axi->dev, "Can't add AXI EHB sysfs remove action\n"); - - return ret; -} - -static int bt1_axi_probe(struct platform_device *pdev) -{ - struct bt1_axi *axi; - int ret; - - axi = bt1_axi_create_data(pdev); - if (IS_ERR(axi)) - return PTR_ERR(axi); - - ret = bt1_axi_request_regs(axi); - if (ret) - return ret; - - ret = bt1_axi_request_rst(axi); - if (ret) - return ret; - - ret = bt1_axi_request_clk(axi); - if (ret) - return ret; - - ret = bt1_axi_request_irq(axi); - if (ret) - return ret; - - ret = bt1_axi_init_sysfs(axi); - if (ret) - return ret; - - return 0; -} - -static const struct of_device_id bt1_axi_of_match[] = { - { .compatible = "baikal,bt1-axi" }, - { } -}; -MODULE_DEVICE_TABLE(of, bt1_axi_of_match); - -static struct platform_driver bt1_axi_driver = { - .probe = bt1_axi_probe, - .driver = { - .name = "bt1-axi", - .of_match_table = bt1_axi_of_match - } -}; -module_platform_driver(bt1_axi_driver); - -MODULE_AUTHOR("Serge Semin "); -MODULE_DESCRIPTION("Baikal-T1 AXI-bus driver"); diff --git a/drivers/bus/imx-aipstz.c b/drivers/bus/imx-aipstz.c index 5fdf377f5d06..83371e5b35a2 100644 --- a/drivers/bus/imx-aipstz.c +++ b/drivers/bus/imx-aipstz.c @@ -11,9 +11,19 @@ #include #define IMX_AIPSTZ_MPR0 0x0 +#define IMX_AIPSTZ_OPACR0 0x40 +#define IMX_AIPSTZ_OPACR1 0x44 +#define IMX_AIPSTZ_OPACR2 0x48 +#define IMX_AIPSTZ_OPACR3 0x4c +#define IMX_AIPSTZ_OPACR4 0x50 struct imx_aipstz_config { u32 mpr0; + u32 opacr0; + u32 opacr1; + u32 opacr2; + u32 opacr3; + u32 opacr4; }; struct imx_aipstz_data { @@ -24,6 +34,11 @@ struct imx_aipstz_data { static void imx_aipstz_apply_default(struct imx_aipstz_data *data) { writel(data->default_cfg->mpr0, data->base + IMX_AIPSTZ_MPR0); + writel(data->default_cfg->opacr0, data->base + IMX_AIPSTZ_OPACR0); + writel(data->default_cfg->opacr1, data->base + IMX_AIPSTZ_OPACR1); + writel(data->default_cfg->opacr2, data->base + IMX_AIPSTZ_OPACR2); + writel(data->default_cfg->opacr3, data->base + IMX_AIPSTZ_OPACR3); + writel(data->default_cfg->opacr4, data->base + IMX_AIPSTZ_OPACR4); } static const struct of_device_id imx_aipstz_match_table[] = { diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index e3d0a3cbaf94..0277e1ab1198 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -367,7 +367,7 @@ static void mhi_ep_read_completion(struct mhi_ep_buf_info *buf_info) ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, MHI_TRE_DATA_GET_LEN(el), MHI_EV_CC_EOB); - if (ret < 0) { + if (ret) { dev_err(&mhi_chan->mhi_dev->dev, "Error sending transfer compl. event\n"); goto err_free_tre_buf; @@ -383,7 +383,7 @@ static void mhi_ep_read_completion(struct mhi_ep_buf_info *buf_info) ret = mhi_ep_send_completion_event(mhi_cntrl, ring, el, MHI_TRE_DATA_GET_LEN(el), MHI_EV_CC_EOT); - if (ret < 0) { + if (ret) { dev_err(&mhi_chan->mhi_dev->dev, "Error sending transfer compl. event\n"); goto err_free_tre_buf; @@ -449,7 +449,7 @@ static int mhi_ep_read_channel(struct mhi_ep_cntrl *mhi_cntrl, dev_dbg(dev, "Reading %zd bytes from channel (%u)\n", tr_len, ring->ch_id); ret = mhi_cntrl->read_async(mhi_cntrl, &buf_info); - if (ret < 0) { + if (ret) { dev_err(&mhi_chan->mhi_dev->dev, "Error reading from channel\n"); goto err_free_buf_addr; } @@ -494,7 +494,7 @@ static int mhi_ep_process_ch_ring(struct mhi_ep_ring *ring) } else { /* UL channel */ ret = mhi_ep_read_channel(mhi_cntrl, ring); - if (ret < 0) { + if (ret) { dev_err(&mhi_chan->mhi_dev->dev, "Failed to read channel\n"); return ret; } @@ -591,7 +591,7 @@ int mhi_ep_queue_skb(struct mhi_ep_device *mhi_dev, struct sk_buff *skb) dev_dbg(dev, "Writing %zd bytes to channel (%u)\n", tr_len, ring->ch_id); ret = mhi_cntrl->write_async(mhi_cntrl, &buf_info); - if (ret < 0) { + if (ret) { dev_err(dev, "Error writing to the channel\n"); goto err_exit; } diff --git a/drivers/bus/mhi/ep/ring.c b/drivers/bus/mhi/ep/ring.c index 9375b16ff2a5..405ce16c02a8 100644 --- a/drivers/bus/mhi/ep/ring.c +++ b/drivers/bus/mhi/ep/ring.c @@ -49,7 +49,7 @@ static int __mhi_ep_cache_ring(struct mhi_ep_ring *ring, size_t end) buf_info.dev_addr = &ring->ring_cache[start]; ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info); - if (ret < 0) + if (ret) return ret; } else { buf_info.size = (ring->ring_size - start) * sizeof(struct mhi_ring_element); @@ -57,7 +57,7 @@ static int __mhi_ep_cache_ring(struct mhi_ep_ring *ring, size_t end) buf_info.dev_addr = &ring->ring_cache[start]; ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info); - if (ret < 0) + if (ret) return ret; if (end) { @@ -66,7 +66,7 @@ static int __mhi_ep_cache_ring(struct mhi_ep_ring *ring, size_t end) buf_info.size = end * sizeof(struct mhi_ring_element); ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info); - if (ret < 0) + if (ret) return ret; } } diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c index f16a1e67a667..19c84913cfb9 100644 --- a/drivers/bus/mhi/host/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -308,7 +308,6 @@ static void mhi_free_bhi_buffer(struct mhi_controller *mhi_cntrl, struct mhi_buf *mhi_buf = image_info->mhi_buf; dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len, mhi_buf->buf, mhi_buf->dma_addr); - kfree(image_info->mhi_buf); kfree(image_info); } @@ -322,7 +321,6 @@ void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl, dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len, mhi_buf->buf, mhi_buf->dma_addr); - kfree(image_info->mhi_buf); kfree(image_info); } @@ -333,15 +331,10 @@ static int mhi_alloc_bhi_buffer(struct mhi_controller *mhi_cntrl, struct image_info *img_info; struct mhi_buf *mhi_buf; - img_info = kzalloc_obj(*img_info); + img_info = kzalloc_flex(*img_info, mhi_buf, 1); if (!img_info) return -ENOMEM; - /* Allocate memory for entry */ - img_info->mhi_buf = kzalloc_obj(*img_info->mhi_buf); - if (!img_info->mhi_buf) - goto error_alloc_mhi_buf; - /* Allocate and populate vector table */ mhi_buf = img_info->mhi_buf; @@ -358,8 +351,6 @@ static int mhi_alloc_bhi_buffer(struct mhi_controller *mhi_cntrl, return 0; error_alloc_segment: - kfree(mhi_buf); -error_alloc_mhi_buf: kfree(img_info); return -ENOMEM; @@ -375,14 +366,11 @@ int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl, struct image_info *img_info; struct mhi_buf *mhi_buf; - img_info = kzalloc_obj(*img_info); + img_info = kzalloc_flex(*img_info, mhi_buf, segments); if (!img_info) return -ENOMEM; - /* Allocate memory for entries */ - img_info->mhi_buf = kzalloc_objs(*img_info->mhi_buf, segments); - if (!img_info->mhi_buf) - goto error_alloc_mhi_buf; + img_info->entries = segments; /* Allocate and populate vector table */ mhi_buf = img_info->mhi_buf; @@ -402,7 +390,6 @@ int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl, } img_info->bhi_vec = img_info->mhi_buf[segments - 1].buf; - img_info->entries = segments; *image_info = img_info; return 0; @@ -411,9 +398,6 @@ error_alloc_segment: for (--i, --mhi_buf; i >= 0; i--, mhi_buf--) dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len, mhi_buf->buf, mhi_buf->dma_addr); - kfree(img_info->mhi_buf); - -error_alloc_mhi_buf: kfree(img_info); return -ENOMEM; diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 0884a384b77f..750da3dbb4c6 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -253,6 +253,13 @@ static const struct mhi_channel_config mhi_qcom_qdu100_channels[] = { MHI_CHANNEL_CONFIG_DL(41, "MHI_PHC", 32, 4), MHI_CHANNEL_CONFIG_UL(46, "IP_SW0", 256, 5), MHI_CHANNEL_CONFIG_DL(47, "IP_SW0", 256, 5), + MHI_CHANNEL_CONFIG_UL(48, "IP_SW1", 256, 6), + MHI_CHANNEL_CONFIG_DL(49, "IP_SW1", 256, 6), + MHI_CHANNEL_CONFIG_UL(50, "IP_ETH0", 256, 7), + MHI_CHANNEL_CONFIG_DL(51, "IP_ETH0", 256, 7), + MHI_CHANNEL_CONFIG_UL(52, "IP_ETH1", 256, 8), + MHI_CHANNEL_CONFIG_DL(53, "IP_ETH1", 256, 8), + }; static struct mhi_event_config mhi_qcom_qdu100_events[] = { @@ -268,6 +275,7 @@ static struct mhi_event_config mhi_qcom_qdu100_events[] = { MHI_EVENT_CONFIG_SW_DATA(5, 512), MHI_EVENT_CONFIG_SW_DATA(6, 512), MHI_EVENT_CONFIG_SW_DATA(7, 512), + MHI_EVENT_CONFIG_SW_DATA(8, 512), }; static const struct mhi_controller_config mhi_qcom_qdu100_config = { @@ -407,6 +415,16 @@ static const struct mhi_pci_dev_info mhi_qcom_sdx55_info = { .sideband_wake = false, }; +static const struct mhi_pci_dev_info mhi_qcom_sdx35_info = { + .name = "qcom-sdx35m", + .config = &modem_qcom_v2_mhiv_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .mru_default = 32768, + .sideband_wake = false, + .edl_trigger = true, +}; + static const struct mhi_pci_dev_info mhi_qcom_sdx24_info = { .name = "qcom-sdx24", .edl = "qcom/prog_firehose_sdx24.mbn", @@ -788,6 +806,8 @@ static const struct mhi_channel_config mhi_telit_fn990_channels[] = { MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0), MHI_CHANNEL_CONFIG_UL(92, "DUN2", 32, 1), MHI_CHANNEL_CONFIG_DL(93, "DUN2", 32, 1), + MHI_CHANNEL_CONFIG_UL(94, "NMEA", 32, 1), + MHI_CHANNEL_CONFIG_DL(95, "NMEA", 32, 1), MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0_MBIM", 128, 2), MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3), }; @@ -839,6 +859,8 @@ static const struct mhi_channel_config mhi_telit_fn920c04_channels[] = { MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0), MHI_CHANNEL_CONFIG_UL(92, "DUN2", 32, 1), MHI_CHANNEL_CONFIG_DL(93, "DUN2", 32, 1), + MHI_CHANNEL_CONFIG_UL(94, "NMEA", 32, 1), + MHI_CHANNEL_CONFIG_DL(95, "NMEA", 32, 1), MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 2), MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 3), }; @@ -882,6 +904,16 @@ static const struct mhi_pci_dev_info mhi_telit_fe990b40_info = { .edl_trigger = true, }; +static const struct mhi_pci_dev_info mhi_telit_fe912c04_info = { + .name = "telit-fe912c04", + .config = &modem_telit_fn920c04_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .sideband_wake = false, + .mru_default = 32768, + .edl_trigger = true, +}; + static const struct mhi_pci_dev_info mhi_netprisma_lcur57_info = { .name = "netprisma-lcur57", .edl = "qcom/prog_firehose_sdx24.mbn", @@ -909,6 +941,11 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* Telit FN920C04 (sdx35) */ {PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x011a, 0x1c5d, 0x2020), .driver_data = (kernel_ulong_t) &mhi_telit_fn920c04_info }, + /* Telit FE912C04 (sdx35) */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x011a, 0x1c5d, 0x2045), + .driver_data = (kernel_ulong_t) &mhi_telit_fe912c04_info }, + { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x011a), + .driver_data = (kernel_ulong_t) &mhi_qcom_sdx35_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0304), .driver_data = (kernel_ulong_t) &mhi_qcom_sdx24_info }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, PCI_VENDOR_ID_QCOM, 0x010c), @@ -1393,7 +1430,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_unregister; } - err = mhi_sync_power_up(mhi_cntrl); + err = mhi_async_power_up(mhi_cntrl); if (err) { dev_err(&pdev->dev, "failed to power up MHI controller\n"); goto err_unprepare; @@ -1428,6 +1465,7 @@ static void mhi_pci_remove(struct pci_dev *pdev) struct mhi_pci_device *mhi_pdev = pci_get_drvdata(pdev); struct mhi_controller *mhi_cntrl = &mhi_pdev->mhi_cntrl; + pm_runtime_forbid(&pdev->dev); pci_disable_sriov(pdev); if (pdev->is_physfn) diff --git a/drivers/bus/stm32_dbg_bus.c b/drivers/bus/stm32_dbg_bus.c new file mode 100644 index 000000000000..e30ef3465609 --- /dev/null +++ b/drivers/bus/stm32_dbg_bus.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026, STMicroelectronics - All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum stm32_dbg_profile { + PERIPHERAL_DBG_PROFILE = 0, + HDP_DBG_PROFILE = 1, +}; + +enum stm32_dbg_pta_command { + /* + * PTA_CMD_GRANT_DBG_ACCESS - Verify the debug configuration against the given debug profile + * and grant access or not + * + * [in] value[0].a Debug profile to grant access to. + */ + PTA_CMD_GRANT_DBG_ACCESS, +}; + +/** + * struct stm32_dbg_bus - OP-TEE based STM32 debug bus private data + * @dev: STM32 debug bus device. + * @ctx: OP-TEE context handler. + */ +struct stm32_dbg_bus { + struct device *dev; + struct tee_context *ctx; +}; + +/* Expect at most 1 instance of this driver */ +static struct stm32_dbg_bus *stm32_dbg_bus_priv; + +static int stm32_dbg_pta_open_session(u32 *id) +{ + struct tee_client_device *dbg_bus_dev = to_tee_client_device(stm32_dbg_bus_priv->dev); + struct tee_ioctl_open_session_arg sess_arg; + int ret; + + memset(&sess_arg, 0, sizeof(sess_arg)); + export_uuid(sess_arg.uuid, &dbg_bus_dev->id.uuid); + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; + + ret = tee_client_open_session(stm32_dbg_bus_priv->ctx, &sess_arg, NULL); + if (ret < 0 || sess_arg.ret) { + dev_err(stm32_dbg_bus_priv->dev, "Failed opening tee session, err: %#x\n", + sess_arg.ret); + return -EOPNOTSUPP; + } + + *id = sess_arg.session; + + return 0; +} + +static void stm32_dbg_pta_close_session(u32 id) +{ + tee_client_close_session(stm32_dbg_bus_priv->ctx, id); +} + +static int stm32_dbg_bus_grant_access(struct stm32_firewall_controller *ctrl, u32 dbg_profile) +{ + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[1] = {0}; + u32 session_id; + int ret; + + if (dbg_profile != PERIPHERAL_DBG_PROFILE && dbg_profile != HDP_DBG_PROFILE) + return -EOPNOTSUPP; + + ret = stm32_dbg_pta_open_session(&session_id); + if (ret) + return ret; + + inv_arg.func = PTA_CMD_GRANT_DBG_ACCESS; + inv_arg.session = session_id; + inv_arg.num_params = 1; + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = dbg_profile; + + ret = tee_client_invoke_func(stm32_dbg_bus_priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_dbg(stm32_dbg_bus_priv->dev, + "When invoking function, err %x, TEE returns: %x\n", ret, inv_arg.ret); + if (!ret) + ret = -EACCES; + } + + stm32_dbg_pta_close_session(session_id); + + return ret; +} + +/* Implement mandatory release_access ops even if it does nothing*/ +static void stm32_dbg_bus_release_access(struct stm32_firewall_controller *ctrl, u32 dbg_profile) +{ +} + +static int stm32_dbg_bus_plat_probe(struct platform_device *pdev) +{ + struct stm32_firewall_controller *dbg_controller; + int ret; + + /* Defer if OP-TEE service is not yet available */ + if (!stm32_dbg_bus_priv) + return -EPROBE_DEFER; + + dbg_controller = devm_kzalloc(&pdev->dev, sizeof(*dbg_controller), GFP_KERNEL); + if (!dbg_controller) + return dev_err_probe(&pdev->dev, -ENOMEM, "Couldn't allocate debug controller\n"); + + dbg_controller->dev = &pdev->dev; + dbg_controller->mmio = NULL; + dbg_controller->name = dev_driver_string(dbg_controller->dev); + dbg_controller->type = STM32_PERIPHERAL_FIREWALL; + dbg_controller->grant_access = stm32_dbg_bus_grant_access; + dbg_controller->release_access = stm32_dbg_bus_release_access; + + ret = stm32_firewall_controller_register(dbg_controller); + if (ret) { + dev_err(dbg_controller->dev, "Couldn't register as a firewall controller: %d", ret); + return ret; + } + + ret = stm32_firewall_populate_bus(dbg_controller); + if (ret) { + dev_err(dbg_controller->dev, "Couldn't populate debug bus: %d", ret); + stm32_firewall_controller_unregister(dbg_controller); + return ret; + } + + pm_runtime_enable(&pdev->dev); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) { + dev_err(dbg_controller->dev, "Couldn't populate the node: %d", ret); + stm32_firewall_controller_unregister(dbg_controller); + return ret; + } + + return 0; +} + +static const struct of_device_id stm32_dbg_bus_of_match[] = { + { .compatible = "st,stm32mp131-dbg-bus", }, + { .compatible = "st,stm32mp151-dbg-bus", }, + { }, +}; +MODULE_DEVICE_TABLE(of, stm32_dbg_bus_of_match); + +static struct platform_driver stm32_dbg_bus_driver = { + .probe = stm32_dbg_bus_plat_probe, + .driver = { + .name = "stm32-dbg-bus", + .of_match_table = stm32_dbg_bus_of_match, + }, +}; + +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{ + return (ver->impl_id == TEE_IMPL_ID_OPTEE); +} + +static void stm32_dbg_bus_remove(struct tee_client_device *tee_dev) +{ + tee_client_close_context(stm32_dbg_bus_priv->ctx); + stm32_dbg_bus_priv = NULL; + + of_platform_depopulate(&tee_dev->dev); +} + +static int stm32_dbg_bus_probe(struct tee_client_device *tee_dev) +{ + struct device *dev = &tee_dev->dev; + struct stm32_dbg_bus *priv; + int ret = 0; + + if (stm32_dbg_bus_priv) + return dev_err_probe(dev, -EBUSY, + "A STM32 debug bus device is already initialized\n"); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Open context with TEE driver */ + priv->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL); + if (IS_ERR_OR_NULL(priv->ctx)) + return dev_err_probe(dev, PTR_ERR_OR_ZERO(priv->ctx), "Cannot open TEE context\n"); + + stm32_dbg_bus_priv = priv; + stm32_dbg_bus_priv->dev = dev; + + return ret; +} + +static const struct tee_client_device_id optee_dbg_bus_id_table[] = { + {UUID_INIT(0xdd05bc8b, 0x9f3b, 0x49f0, + 0xb6, 0x49, 0x01, 0xaa, 0x10, 0xc1, 0xc2, 0x10)}, + {} +}; + +static struct tee_client_driver stm32_optee_dbg_bus_driver = { + .id_table = optee_dbg_bus_id_table, + .probe = stm32_dbg_bus_probe, + .remove = stm32_dbg_bus_remove, + .driver = { + .name = "optee_dbg_bus", + }, +}; + +static void __exit stm32_optee_dbg_bus_driver_exit(void) +{ + platform_driver_unregister(&stm32_dbg_bus_driver); + tee_client_driver_unregister(&stm32_optee_dbg_bus_driver); +} +module_exit(stm32_optee_dbg_bus_driver_exit); + +static int __init stm32_optee_dbg_bus_driver_init(void) +{ + int err; + + err = tee_client_driver_register(&stm32_optee_dbg_bus_driver); + if (err) + return err; + + err = platform_driver_register(&stm32_dbg_bus_driver); + if (err) + tee_client_driver_unregister(&stm32_optee_dbg_bus_driver); + + return err; +} +module_init(stm32_optee_dbg_bus_driver_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gatien Chevallier "); +MODULE_DESCRIPTION("OP-TEE based STM32 debug access bus driver"); diff --git a/drivers/bus/stm32_etzpc.c b/drivers/bus/stm32_etzpc.c index 7fc0f16960be..4918a14e507e 100644 --- a/drivers/bus/stm32_etzpc.c +++ b/drivers/bus/stm32_etzpc.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -16,8 +17,6 @@ #include #include -#include "stm32_firewall.h" - /* * ETZPC registers */ diff --git a/drivers/bus/stm32_firewall.c b/drivers/bus/stm32_firewall.c index 7e7afe8007db..e3619dba8b06 100644 --- a/drivers/bus/stm32_firewall.c +++ b/drivers/bus/stm32_firewall.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -18,8 +19,6 @@ #include #include -#include "stm32_firewall.h" - /* Corresponds to STM32_FIREWALL_MAX_EXTRA_ARGS + firewall ID */ #define STM32_FIREWALL_MAX_ARGS (STM32_FIREWALL_MAX_EXTRA_ARGS + 1) @@ -185,6 +184,48 @@ void stm32_firewall_release_access_by_id(struct stm32_firewall *firewall, u32 su } EXPORT_SYMBOL_GPL(stm32_firewall_release_access_by_id); +int stm32_firewall_get_grant_all_access(struct device *dev, struct stm32_firewall **firewall, + int *nb_firewall) +{ + struct stm32_firewall *loc_firewall; + int err; + int i; + + *nb_firewall = of_count_phandle_with_args(dev->of_node, "access-controllers", + "#access-controller-cells"); + if (*nb_firewall < 0) + return *nb_firewall; + + if (!*nb_firewall) { + *firewall = NULL; + return 0; + } + + loc_firewall = devm_kcalloc(dev, *nb_firewall, sizeof(*loc_firewall), GFP_KERNEL); + if (!loc_firewall) + return -ENOMEM; + + /* Get stm32 firewall information */ + err = stm32_firewall_get_firewall(dev->of_node, loc_firewall, *nb_firewall); + if (err) + return err; + + for (i = 0; i < *nb_firewall; i++) { + err = stm32_firewall_grant_access(&loc_firewall[i]); + if (err) { + while (i--) + stm32_firewall_release_access(&loc_firewall[i]); + + return err; + } + } + + *firewall = loc_firewall; + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_firewall_get_grant_all_access); + /* Firewall controller API */ int stm32_firewall_controller_register(struct stm32_firewall_controller *firewall_controller) @@ -241,7 +282,6 @@ EXPORT_SYMBOL_GPL(stm32_firewall_controller_unregister); int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_controller) { struct stm32_firewall *firewalls; - struct device_node *child; struct device *parent; unsigned int i; int len; @@ -251,30 +291,25 @@ int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_contr dev_dbg(parent, "Populating %s system bus\n", dev_name(firewall_controller->dev)); - for_each_available_child_of_node(dev_of_node(parent), child) { + for_each_available_child_of_node_scoped(dev_of_node(parent), child) { /* The access-controllers property is mandatory for firewall bus devices */ len = of_count_phandle_with_args(child, "access-controllers", "#access-controller-cells"); - if (len <= 0) { - of_node_put(child); + if (len <= 0) return -EINVAL; - } firewalls = kzalloc_objs(*firewalls, len); - if (!firewalls) { - of_node_put(child); + if (!firewalls) return -ENOMEM; - } err = stm32_firewall_get_firewall(child, firewalls, (unsigned int)len); if (err) { kfree(firewalls); - of_node_put(child); return err; } for (i = 0; i < len; i++) { - if (firewall_controller->grant_access(firewall_controller, + if (firewall_controller->grant_access(firewalls[i].firewall_ctrl, firewalls[i].firewall_id)) { /* * Peripheral access not allowed or not defined. diff --git a/drivers/bus/stm32_rifsc.c b/drivers/bus/stm32_rifsc.c index debeaf8ea1bd..19d10379dcef 100644 --- a/drivers/bus/stm32_rifsc.c +++ b/drivers/bus/stm32_rifsc.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -15,10 +16,9 @@ #include #include #include +#include #include -#include "stm32_firewall.h" - /* * RIFSC offset register */ @@ -450,7 +450,7 @@ static void stm32_rifsc_fill_rimu_dbg_entry(struct rifsc_dbg_private *rifsc, const struct stm32_rifsc_resources_names *dbg_names = rifsc->res_names; u32 rimc_attr = readl_relaxed(rifsc->mmio + RIFSC_RIMC_ATTR0 + 0x4 * i); - snprintf(dbg_entry->m_name, sizeof(dbg_entry->m_name), "%s", dbg_names->initiator_names[i]); + strscpy(dbg_entry->m_name, dbg_names->initiator_names[i]); dbg_entry->m_cid = FIELD_GET(RIFSC_RIMC_MCID_MASK, rimc_attr); dbg_entry->cidsel = rimc_attr & RIFSC_RIMC_CIDSEL; dbg_entry->m_sec = rimc_attr & RIFSC_RIMC_MSEC; @@ -469,8 +469,7 @@ static void stm32_rifsc_fill_dev_dbg_entry(struct rifsc_dbg_private *rifsc, sec_cfgr = readl_relaxed(rifsc->mmio + RIFSC_RISC_SECCFGR0 + 0x4 * reg_id); priv_cfgr = readl_relaxed(rifsc->mmio + RIFSC_RISC_PRIVCFGR0 + 0x4 * reg_id); - snprintf(dbg_entry->dev_name, sizeof(dbg_entry->dev_name), "%s", - dbg_names->device_names[i]); + strscpy(dbg_entry->dev_name, dbg_names->device_names[i]); dbg_entry->dev_id = i; dbg_entry->dev_cid_filt_en = cid_cfgr & CIDCFGR_CFEN; dbg_entry->dev_sem_en = cid_cfgr & CIDCFGR_SEMEN; @@ -688,34 +687,6 @@ static int stm32_rifsc_grant_access(struct stm32_firewall_controller *ctrl, u32 sec_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_SECCFGR0 + 0x4 * reg_id); cid_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_PER0_CIDCFGR + 0x8 * firewall_id); - /* First check conditions for semaphore mode, which doesn't take into account static CID. */ - if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) { - if (cid_reg_value & BIT(RIF_CID1 + SEMWL_SHIFT)) { - /* Static CID is irrelevant if semaphore mode */ - goto skip_cid_check; - } else { - dev_dbg(rifsc_controller->dev, - "Invalid bus semaphore configuration: index %d\n", firewall_id); - return -EACCES; - } - } - - /* - * Skip CID check if CID filtering isn't enabled or filtering is enabled on CID0, which - * corresponds to whatever CID. - */ - if (!(cid_reg_value & CIDCFGR_CFEN) || - FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) == RIF_CID0) - goto skip_cid_check; - - /* Coherency check with the CID configuration */ - if (FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) != RIF_CID1) { - dev_dbg(rifsc_controller->dev, "Invalid CID configuration for peripheral: %d\n", - firewall_id); - return -EACCES; - } - -skip_cid_check: /* Check security configuration */ if (sec_reg_value & BIT(reg_offset)) { dev_dbg(rifsc_controller->dev, @@ -723,19 +694,31 @@ skip_cid_check: return -EACCES; } - /* - * If the peripheral is in semaphore mode, take the semaphore so that - * the CID1 has the ownership. - */ - if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) { + /* Skip CID check if CID filtering isn't enabled */ + if (!(cid_reg_value & CIDCFGR_CFEN)) + goto skip_cid_check; + + /* First check conditions for semaphore mode, which doesn't take into account static CID. */ + if (cid_reg_value & CIDCFGR_SEMEN) { + if (!(cid_reg_value & BIT(RIF_CID1 + SEMWL_SHIFT))) { + dev_dbg(rifsc_controller->dev, + "Invalid bus semaphore configuration: index %d\n", firewall_id); + return -EACCES; + } + rc = stm32_rif_acquire_semaphore(rifsc_controller, firewall_id); if (rc) { - dev_err(rifsc_controller->dev, + dev_dbg(rifsc_controller->dev, "Couldn't acquire semaphore for peripheral: %d\n", firewall_id); return rc; } + } else if (FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) != RIF_CID1) { + dev_dbg(rifsc_controller->dev, "Invalid CID configuration for peripheral: %d\n", + firewall_id); + return -EACCES; } +skip_cid_check: return 0; } diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index fc049612d6dc..62934cf4b10d 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -631,6 +631,16 @@ int register_cdrom(struct gendisk *disk, struct cdrom_device_info *cdi) WARN_ON(!cdo->generic_packet); + /* + * Propagate the drive's write support to the block layer so BLKROGET + * reflects actual write capability. Drivers that use GET CONFIGURATION + * features (CDC_MRW_W, CDC_RAM) must have called + * cdrom_probe_write_features() before register_cdrom() so the mask is + * complete here. + */ + set_disk_ro(disk, !CDROM_CAN(CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | + CDC_CD_RW)); + cd_dbg(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); mutex_lock(&cdrom_mutex); list_add(&cdi->list, &cdrom_list); @@ -742,6 +752,44 @@ static int cdrom_is_random_writable(struct cdrom_device_info *cdi, int *write) return 0; } +/* + * Probe write-related MMC features via GET CONFIGURATION and update + * cdi->mask accordingly. Drivers that populate cdi->mask from the MODE SENSE + * capabilities page (e.g. sr) should call this after those MODE SENSE bits + * have been set but before register_cdrom(), so that the full set of + * write-capability bits is known by the time register_cdrom() decides on the + * initial read-only state of the disk. + */ +void cdrom_probe_write_features(struct cdrom_device_info *cdi) +{ + int mrw, mrw_write, ram_write; + + mrw = 0; + if (!cdrom_is_mrw(cdi, &mrw_write)) + mrw = 1; + + if (CDROM_CAN(CDC_MO_DRIVE)) + ram_write = 1; + else + (void) cdrom_is_random_writable(cdi, &ram_write); + + if (mrw) + cdi->mask &= ~CDC_MRW; + else + cdi->mask |= CDC_MRW; + + if (mrw_write) + cdi->mask &= ~CDC_MRW_W; + else + cdi->mask |= CDC_MRW_W; + + if (ram_write) + cdi->mask &= ~CDC_RAM; + else + cdi->mask |= CDC_RAM; +} +EXPORT_SYMBOL(cdrom_probe_write_features); + static int cdrom_media_erasable(struct cdrom_device_info *cdi) { disc_information di; @@ -894,33 +942,8 @@ static int cdrom_is_dvd_rw(struct cdrom_device_info *cdi) */ static int cdrom_open_write(struct cdrom_device_info *cdi) { - int mrw, mrw_write, ram_write; int ret = 1; - mrw = 0; - if (!cdrom_is_mrw(cdi, &mrw_write)) - mrw = 1; - - if (CDROM_CAN(CDC_MO_DRIVE)) - ram_write = 1; - else - (void) cdrom_is_random_writable(cdi, &ram_write); - - if (mrw) - cdi->mask &= ~CDC_MRW; - else - cdi->mask |= CDC_MRW; - - if (mrw_write) - cdi->mask &= ~CDC_MRW_W; - else - cdi->mask |= CDC_MRW_W; - - if (ram_write) - cdi->mask &= ~CDC_RAM; - else - cdi->mask |= CDC_RAM; - if (CDROM_CAN(CDC_MRW_W)) ret = cdrom_mrw_open_write(cdi); else if (CDROM_CAN(CDC_DVD_RAM)) diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 4d920ca534a4..8983addca695 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -323,18 +323,6 @@ int agp_try_unsupported_boot; EXPORT_SYMBOL(agp_off); EXPORT_SYMBOL(agp_try_unsupported_boot); -static int __init agp_init(void) -{ - if (!agp_off) - printk(KERN_INFO "Linux agpgart interface v%d.%d\n", - AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR); - return 0; -} - -static void __exit agp_exit(void) -{ -} - #ifndef MODULE static __init int agp_setup(char *s) { @@ -351,7 +339,3 @@ MODULE_AUTHOR("Dave Jones, Jeff Hartmann"); MODULE_DESCRIPTION("AGP GART driver"); MODULE_LICENSE("GPL and additional rights"); MODULE_ALIAS_MISCDEV(AGPGART_MINOR); - -module_init(agp_init); -module_exit(agp_exit); - diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 8f128cc40147..46c84e5df00f 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -973,8 +974,9 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) return AE_OK; } -static int hpet_acpi_add(struct acpi_device *device) +static int hpet_acpi_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_status result; struct hpet_data data; @@ -1002,12 +1004,12 @@ static const struct acpi_device_id hpet_device_ids[] = { {"", 0}, }; -static struct acpi_driver hpet_acpi_driver = { - .name = "hpet", - .ids = hpet_device_ids, - .ops = { - .add = hpet_acpi_add, - }, +static struct platform_driver hpet_acpi_driver = { + .probe = hpet_acpi_probe, + .driver = { + .name = "hpet_acpi", + .acpi_match_table = hpet_device_ids, + }, }; static struct miscdevice hpet_misc = { HPET_MINOR, "hpet", &hpet_fops }; @@ -1022,7 +1024,7 @@ static int __init hpet_init(void) sysctl_header = register_sysctl("dev/hpet", hpet_table); - result = acpi_bus_register_driver(&hpet_acpi_driver); + result = platform_driver_register(&hpet_acpi_driver); if (result < 0) { unregister_sysctl_table(sysctl_header); misc_deregister(&hpet_misc); diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 92bed266d07c..669f76000197 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -187,6 +187,16 @@ config SSIF_IPMI_BMC The driver implements the BMC side of the SMBus system interface (SSIF). +config SSIF_IPMI_BMC_KUNIT_TEST + bool "KUnit tests for SSIF IPMI BMC driver" if !KUNIT_ALL_TESTS + depends on KUNIT=y + depends on SSIF_IPMI_BMC + default KUNIT_ALL_TESTS + help + This option builds unit tests that exercise the SSIF BMC state + machine, including request handling, response transmission, + and error paths such as aborted or truncated transfers. + config IPMB_DEVICE_INTERFACE tristate 'IPMB Interface handler' depends on I2C diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index c41f51c82edd..869ac87a4b6a 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -987,7 +987,7 @@ static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) mutex_lock(&intf->user_msgs_mutex); list_add_tail(&msg->link, &intf->user_msgs); mutex_unlock(&intf->user_msgs_mutex); - queue_work(system_wq, &intf->smi_work); + queue_work(system_percpu_wq, &intf->smi_work); } return rv; @@ -4977,7 +4977,7 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf, if (run_to_completion) smi_work(&intf->smi_work); else - queue_work(system_wq, &intf->smi_work); + queue_work(system_percpu_wq, &intf->smi_work); } EXPORT_SYMBOL(ipmi_smi_msg_received); @@ -4987,7 +4987,7 @@ void ipmi_smi_watchdog_pretimeout(struct ipmi_smi *intf) return; atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1); - queue_work(system_wq, &intf->smi_work); + queue_work(system_percpu_wq, &intf->smi_work); } EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); @@ -5162,7 +5162,7 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf, flags); } - queue_work(system_wq, &intf->smi_work); + queue_work(system_percpu_wq, &intf->smi_work); return need_timer; } @@ -5218,7 +5218,7 @@ static void ipmi_timeout(struct timer_list *unused) if (atomic_read(&stop_operation)) return; - queue_work(system_wq, &ipmi_timer_work); + queue_work(system_percpu_wq, &ipmi_timer_work); } static void need_waiter(struct ipmi_smi *intf) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 4a9e9de4d684..9a9d12be9bf7 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -168,6 +168,10 @@ struct smi_info { OEM2_DATA_AVAIL) unsigned char msg_flags; + /* When requesting events and messages, don't do it forever. */ + unsigned int num_requests_in_a_row; + bool last_was_flag_fetch; + /* Does the BMC have an event buffer? */ bool has_event_buffer; @@ -410,7 +414,10 @@ static void start_getting_msg_queue(struct smi_info *smi_info) start_new_msg(smi_info, smi_info->curr_msg->data, smi_info->curr_msg->data_size); - smi_info->si_state = SI_GETTING_MESSAGES; + if (smi_info->si_state != SI_GETTING_MESSAGES) { + smi_info->num_requests_in_a_row = 0; + smi_info->si_state = SI_GETTING_MESSAGES; + } } static void start_getting_events(struct smi_info *smi_info) @@ -421,7 +428,10 @@ static void start_getting_events(struct smi_info *smi_info) start_new_msg(smi_info, smi_info->curr_msg->data, smi_info->curr_msg->data_size); - smi_info->si_state = SI_GETTING_EVENTS; + if (smi_info->si_state != SI_GETTING_EVENTS) { + smi_info->num_requests_in_a_row = 0; + smi_info->si_state = SI_GETTING_EVENTS; + } } /* @@ -487,15 +497,19 @@ retry: } else if (smi_info->msg_flags & RECEIVE_MSG_AVAIL) { /* Messages available. */ smi_info->curr_msg = alloc_msg_handle_irq(smi_info); - if (!smi_info->curr_msg) + if (!smi_info->curr_msg) { + smi_info->si_state = SI_NORMAL; return; + } start_getting_msg_queue(smi_info); } else if (smi_info->msg_flags & EVENT_MSG_BUFFER_FULL) { /* Events available. */ smi_info->curr_msg = alloc_msg_handle_irq(smi_info); - if (!smi_info->curr_msg) + if (!smi_info->curr_msg) { + smi_info->si_state = SI_NORMAL; return; + } start_getting_events(smi_info); } else if (smi_info->msg_flags & OEM_DATA_AVAIL && @@ -595,6 +609,7 @@ static void handle_transaction_done(struct smi_info *smi_info) smi_info->si_state = SI_NORMAL; } else { smi_info->msg_flags = msg[3]; + smi_info->last_was_flag_fetch = true; handle_flags(smi_info); } break; @@ -630,7 +645,13 @@ static void handle_transaction_done(struct smi_info *smi_info) */ msg = smi_info->curr_msg; smi_info->curr_msg = NULL; - if (msg->rsp[2] != 0) { + /* + * It appears some BMCs, with no event data, return no + * data in the message and not a 0x80 error as the + * spec says they should. Shut down processing if + * the data is not the right length. + */ + if (msg->rsp[2] != 0 || msg->rsp_size != 19) { /* Error getting event, probably done. */ msg->done(msg); @@ -640,6 +661,11 @@ static void handle_transaction_done(struct smi_info *smi_info) } else { smi_inc_stat(smi_info, events); + smi_info->num_requests_in_a_row++; + if (smi_info->num_requests_in_a_row > 10) + /* Stop if we do this too many times. */ + smi_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; + /* * Do this before we deliver the message * because delivering the message releases the @@ -678,6 +704,11 @@ static void handle_transaction_done(struct smi_info *smi_info) } else { smi_inc_stat(smi_info, incoming_messages); + smi_info->num_requests_in_a_row++; + if (smi_info->num_requests_in_a_row > 10) + /* Stop if we do this too many times. */ + smi_info->msg_flags &= ~RECEIVE_MSG_AVAIL; + /* * Do this before we deliver the message * because delivering the message releases the @@ -819,6 +850,26 @@ restart: goto out; } + /* + * If we are currently idle, or if the last thing that was + * done was a flag fetch and there is a message pending, try + * to start the next message. + * + * We do the waiting message check to avoid a stuck flag + * completely wedging the driver. Let a message through + * in between flag operations if that happens. + */ + if (si_sm_result == SI_SM_IDLE || + (si_sm_result == SI_SM_ATTN && smi_info->waiting_msg && + smi_info->last_was_flag_fetch)) { + smi_info->last_was_flag_fetch = false; + smi_inc_stat(smi_info, idles); + + si_sm_result = start_next_msg(smi_info); + if (si_sm_result != SI_SM_IDLE) + goto restart; + } + /* * We prefer handling attn over new messages. But don't do * this if there is not yet an upper layer to handle anything. @@ -846,15 +897,6 @@ restart: } } - /* If we are currently idle, try to start the next message. */ - if (si_sm_result == SI_SM_IDLE) { - smi_inc_stat(smi_info, idles); - - si_sm_result = start_next_msg(smi_info); - if (si_sm_result != SI_SM_IDLE) - goto restart; - } - if ((si_sm_result == SI_SM_IDLE) && (atomic_read(&smi_info->req_events))) { /* diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 37a5cb5c53f1..f419b46bf002 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -225,6 +225,9 @@ struct ssif_info { bool has_event_buffer; bool supports_alert; + /* When requesting events and messages, don't do it forever. */ + unsigned int num_requests_in_a_row; + /* * Used to tell what we should do with alerts. If we are * waiting on a response, read the data immediately. @@ -413,7 +416,10 @@ static void start_event_fetch(struct ssif_info *ssif_info, unsigned long *flags) } ssif_info->curr_msg = msg; - ssif_info->ssif_state = SSIF_GETTING_EVENTS; + if (ssif_info->ssif_state != SSIF_GETTING_EVENTS) { + ssif_info->num_requests_in_a_row = 0; + ssif_info->ssif_state = SSIF_GETTING_EVENTS; + } ipmi_ssif_unlock_cond(ssif_info, flags); msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); @@ -436,7 +442,10 @@ static void start_recv_msg_fetch(struct ssif_info *ssif_info, } ssif_info->curr_msg = msg; - ssif_info->ssif_state = SSIF_GETTING_MESSAGES; + if (ssif_info->ssif_state != SSIF_GETTING_MESSAGES) { + ssif_info->num_requests_in_a_row = 0; + ssif_info->ssif_state = SSIF_GETTING_MESSAGES; + } ipmi_ssif_unlock_cond(ssif_info, flags); msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); @@ -843,6 +852,11 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; handle_flags(ssif_info, flags); } else { + ssif_info->num_requests_in_a_row++; + if (ssif_info->num_requests_in_a_row > 10) + /* Stop if we do this too many times. */ + ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; + handle_flags(ssif_info, flags); ssif_inc_stat(ssif_info, events); deliver_recv_msg(ssif_info, msg); @@ -876,6 +890,11 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ssif_info->msg_flags &= ~RECEIVE_MSG_AVAIL; handle_flags(ssif_info, flags); } else { + ssif_info->num_requests_in_a_row++; + if (ssif_info->num_requests_in_a_row > 10) + /* Stop if we do this too many times. */ + ssif_info->msg_flags &= ~RECEIVE_MSG_AVAIL; + ssif_inc_stat(ssif_info, incoming_messages); handle_flags(ssif_info, flags); deliver_recv_msg(ssif_info, msg); @@ -1268,8 +1287,10 @@ static void shutdown_ssif(void *send_info) ssif_info->stopping = true; timer_delete_sync(&ssif_info->watch_timer); timer_delete_sync(&ssif_info->retry_timer); - if (ssif_info->thread) + if (ssif_info->thread) { kthread_stop(ssif_info->thread); + ssif_info->thread = NULL; + } } static void ssif_remove(struct i2c_client *client) @@ -1658,6 +1679,7 @@ static int ssif_probe(struct i2c_client *client) int len = 0; int i; u8 slave_addr = 0; + unsigned int thread_num; struct ssif_addr_info *addr_info = NULL; mutex_lock(&ssif_infos_mutex); @@ -1876,22 +1898,18 @@ static int ssif_probe(struct i2c_client *client) ssif_info->handlers.request_events = request_events; ssif_info->handlers.set_need_watch = ssif_set_need_watch; - { - unsigned int thread_num; - - thread_num = ((i2c_adapter_id(ssif_info->client->adapter) - << 8) | - ssif_info->client->addr); - init_completion(&ssif_info->wake_thread); - ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info, - "kssif%4.4x", thread_num); - if (IS_ERR(ssif_info->thread)) { - rv = PTR_ERR(ssif_info->thread); - dev_notice(&ssif_info->client->dev, - "Could not start kernel thread: error %d\n", - rv); - goto out; - } + thread_num = ((i2c_adapter_id(ssif_info->client->adapter) << 8) | + ssif_info->client->addr); + init_completion(&ssif_info->wake_thread); + ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info, + "kssif%4.4x", thread_num); + if (IS_ERR(ssif_info->thread)) { + rv = PTR_ERR(ssif_info->thread); + ssif_info->thread = NULL; + dev_notice(&ssif_info->client->dev, + "Could not start kernel thread: error %d\n", + rv); + goto out; } dev_set_drvdata(&ssif_info->client->dev, ssif_info); @@ -1916,6 +1934,15 @@ static int ssif_probe(struct i2c_client *client) out: if (rv) { + /* + * If ipmi_register_smi() starts the interface, it will + * call shutdown and that will free the thread and set + * it to NULL. Otherwise it must be freed here. + */ + if (ssif_info->thread) { + kthread_stop(ssif_info->thread); + ssif_info->thread = NULL; + } if (addr_info) addr_info->client = NULL; diff --git a/drivers/char/ipmi/ssif_bmc.c b/drivers/char/ipmi/ssif_bmc.c index 7a52e3ea49ed..1df0e9284ad9 100644 --- a/drivers/char/ipmi/ssif_bmc.c +++ b/drivers/char/ipmi/ssif_bmc.c @@ -18,6 +18,9 @@ #include #include #include +#if IS_ENABLED(CONFIG_SSIF_IPMI_BMC_KUNIT_TEST) +#include +#endif #define DEVICE_NAME "ipmi-ssif-host" @@ -163,6 +166,8 @@ static ssize_t ssif_bmc_read(struct file *file, char __user *buf, size_t count, spin_unlock_irqrestore(&ssif_bmc->lock, flags); ret = copy_to_user(buf, &msg, count); + if (ret > 0) + ret = -EFAULT; } return (ret < 0) ? ret : count; @@ -456,6 +461,15 @@ static bool supported_write_cmd(u8 cmd) return false; } +static bool supported_write_start_cmd(u8 cmd) +{ + if (cmd == SSIF_IPMI_SINGLEPART_WRITE || + cmd == SSIF_IPMI_MULTIPART_WRITE_START) + return true; + + return false; +} + /* Process the IPMI response that will be read by master */ static void handle_read_processed(struct ssif_bmc_ctx *ssif_bmc, u8 *val) { @@ -558,7 +572,7 @@ static void process_request_part(struct ssif_bmc_ctx *ssif_bmc) len = ssif_bmc->request.len + part->length; /* Do the bound check here, not allow the request len exceed 254 bytes */ if (len > IPMI_SSIF_PAYLOAD_MAX) { - dev_warn(&ssif_bmc->client->dev, + dev_dbg(&ssif_bmc->client->dev, "Warn: Request exceeded 254 bytes, aborting"); /* Request too long, aborting */ ssif_bmc->aborting = true; @@ -604,7 +618,7 @@ static void on_read_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) ssif_bmc->state == SSIF_START || ssif_bmc->state == SSIF_REQ_RECVING || ssif_bmc->state == SSIF_RES_SENDING) { - dev_warn(&ssif_bmc->client->dev, + dev_dbg(&ssif_bmc->client->dev, "Warn: %s unexpected READ REQUESTED in state=%s\n", __func__, state_to_string(ssif_bmc->state)); ssif_bmc->state = SSIF_ABORTING; @@ -613,7 +627,7 @@ static void on_read_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) } else if (ssif_bmc->state == SSIF_SMBUS_CMD) { if (!supported_read_cmd(ssif_bmc->part_buf.smbus_cmd)) { - dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus read command=0x%x", + dev_dbg(&ssif_bmc->client->dev, "Warn: Unknown SMBus read command=0x%x", ssif_bmc->part_buf.smbus_cmd); ssif_bmc->aborting = true; } @@ -648,7 +662,7 @@ static void on_read_processed_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) ssif_bmc->state == SSIF_START || ssif_bmc->state == SSIF_REQ_RECVING || ssif_bmc->state == SSIF_SMBUS_CMD) { - dev_warn(&ssif_bmc->client->dev, + dev_dbg(&ssif_bmc->client->dev, "Warn: %s unexpected READ PROCESSED in state=%s\n", __func__, state_to_string(ssif_bmc->state)); ssif_bmc->state = SSIF_ABORTING; @@ -673,7 +687,7 @@ static void on_write_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) } else if (ssif_bmc->state == SSIF_START || ssif_bmc->state == SSIF_REQ_RECVING || ssif_bmc->state == SSIF_RES_SENDING) { - dev_warn(&ssif_bmc->client->dev, + dev_dbg(&ssif_bmc->client->dev, "Warn: %s unexpected WRITE REQUEST in state=%s\n", __func__, state_to_string(ssif_bmc->state)); ssif_bmc->state = SSIF_ABORTING; @@ -688,7 +702,7 @@ static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) { if (ssif_bmc->state == SSIF_READY || ssif_bmc->state == SSIF_RES_SENDING) { - dev_warn(&ssif_bmc->client->dev, + dev_dbg(&ssif_bmc->client->dev, "Warn: %s unexpected WRITE RECEIVED in state=%s\n", __func__, state_to_string(ssif_bmc->state)); ssif_bmc->state = SSIF_ABORTING; @@ -698,7 +712,7 @@ static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) } else if (ssif_bmc->state == SSIF_SMBUS_CMD) { if (!supported_write_cmd(ssif_bmc->part_buf.smbus_cmd)) { - dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus write command=0x%x", + dev_dbg(&ssif_bmc->client->dev, "Warn: Unknown SMBus write command=0x%x", ssif_bmc->part_buf.smbus_cmd); ssif_bmc->aborting = true; } @@ -707,6 +721,11 @@ static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) ssif_bmc->state = SSIF_ABORTING; else ssif_bmc->state = SSIF_REQ_RECVING; + } else if (ssif_bmc->state == SSIF_ABORTING) { + if (supported_write_start_cmd(*val)) { + ssif_bmc->state = SSIF_SMBUS_CMD; + ssif_bmc->aborting = false; + } } /* This is response sending state */ @@ -722,7 +741,7 @@ static void on_stop_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) ssif_bmc->state == SSIF_START || ssif_bmc->state == SSIF_SMBUS_CMD || ssif_bmc->state == SSIF_ABORTING) { - dev_warn(&ssif_bmc->client->dev, + dev_dbg(&ssif_bmc->client->dev, "Warn: %s unexpected SLAVE STOP in state=%s\n", __func__, state_to_string(ssif_bmc->state)); ssif_bmc->state = SSIF_READY; @@ -789,7 +808,7 @@ static int ssif_bmc_cb(struct i2c_client *client, enum i2c_slave_event event, u8 break; default: - dev_warn(&ssif_bmc->client->dev, "Warn: Unknown i2c slave event\n"); + dev_dbg(&ssif_bmc->client->dev, "Warn: Unknown i2c slave event\n"); break; } @@ -843,6 +862,7 @@ static void ssif_bmc_remove(struct i2c_client *client) { struct ssif_bmc_ctx *ssif_bmc = i2c_get_clientdata(client); + timer_delete_sync(&ssif_bmc->response_timer); i2c_slave_unregister(client); misc_deregister(&ssif_bmc->miscdev); } @@ -869,6 +889,373 @@ static struct i2c_driver ssif_bmc_driver = { .id_table = ssif_bmc_id, }; +#if IS_ENABLED(CONFIG_SSIF_IPMI_BMC_KUNIT_TEST) +struct ssif_bmc_test_ctx { + struct ssif_bmc_ctx ssif_bmc; + struct i2c_client client; + struct i2c_adapter adapter; + struct i2c_algorithm algo; +}; + +static int ssif_bmc_test_init(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx; + + test_ctx = kunit_kzalloc(test, sizeof(*test_ctx), GFP_KERNEL); + if (!test_ctx) + return -ENOMEM; + + test_ctx->adapter.algo = &test_ctx->algo; + test_ctx->client.addr = 0x20; + test_ctx->client.adapter = &test_ctx->adapter; + + spin_lock_init(&test_ctx->ssif_bmc.lock); + init_waitqueue_head(&test_ctx->ssif_bmc.wait_queue); + test_ctx->ssif_bmc.client = &test_ctx->client; + i2c_set_clientdata(&test_ctx->client, &test_ctx->ssif_bmc); + + test->priv = test_ctx; + + return 0; +} + +static void ssif_bmc_test_exit(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + + if (test_ctx->ssif_bmc.response_timer_inited) + timer_delete_sync(&test_ctx->ssif_bmc.response_timer); +} + +static int ssif_bmc_test_run_event_val(struct ssif_bmc_test_ctx *test_ctx, + enum i2c_slave_event event, + u8 *value) +{ + return ssif_bmc_cb(&test_ctx->client, event, value); +} + +static int ssif_bmc_test_run_event(struct ssif_bmc_test_ctx *test_ctx, + enum i2c_slave_event event, u8 value) +{ + return ssif_bmc_test_run_event_val(test_ctx, event, &value); +} + +static void ssif_bmc_test_singlepart_req(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)); + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE); + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 2); + + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xaa); + + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x55); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY); + + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY); + KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available); + KUNIT_EXPECT_TRUE(test, ssif_bmc->busy); + KUNIT_EXPECT_FALSE(test, ssif_bmc->aborting); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 2); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0xaa); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[1], 0x55); + KUNIT_EXPECT_TRUE(test, ssif_bmc->response_timer_inited); +} + +static void ssif_bmc_test_restart_write_without_stop(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 2), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xde), 0); + + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_REQ_RECVING); + + /* Write transaction, without stop, and new request coming */ + ssif_bmc_test_singlepart_req(test); +} + + +static void ssif_bmc_test_restart_after_invalid_command(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xff), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0); + + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_ABORTING); + KUNIT_EXPECT_TRUE(test, ssif_bmc->aborting); + + /* After An Invalid Command, expect could handle new request */ + ssif_bmc_test_singlepart_req(test); +} + +static void ssif_bmc_test_singlepart_read_response_completion(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + u8 value; + + ssif_bmc->state = SSIF_SMBUS_CMD; + ssif_bmc->part_buf.smbus_cmd = SSIF_IPMI_SINGLEPART_READ; + ssif_bmc->response.len = 2; + ssif_bmc->response.payload[0] = 0x11; + ssif_bmc->response.payload[1] = 0x22; + ssif_bmc->response_in_progress = true; + ssif_bmc->is_singlepart_read = true; + ssif_bmc->pec_support = true; + + value = 0; + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED, + &value), 0); + KUNIT_EXPECT_EQ(test, value, 2); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_RES_SENDING); + + value = 0; + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED, + &value), 0); + KUNIT_EXPECT_EQ(test, value, 0x11); + + value = 0; + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED, + &value), 0); + KUNIT_EXPECT_EQ(test, value, 0x22); + + value = 0; + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED, + &value), 0); + KUNIT_EXPECT_EQ(test, value, ssif_bmc->part_buf.pec); + + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY); + KUNIT_EXPECT_FALSE(test, ssif_bmc->response_in_progress); + KUNIT_EXPECT_EQ(test, ssif_bmc->response.len, 0); +} + +static void ssif_bmc_test_stop_during_start_discards_partial_request(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_START); + + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY); + KUNIT_EXPECT_FALSE(test, ssif_bmc->request_available); + KUNIT_EXPECT_EQ(test, ssif_bmc->msg_idx, 0); + + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x77), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY); + + KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 1); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0x77); +} + +static void ssif_bmc_test_read_interrupts_partial_write(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + u8 value = 0xff; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 2), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xab), 0); + + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_REQ_RECVING); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED, + &value), 0); + KUNIT_EXPECT_EQ(test, value, 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_ABORTING); + + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY); + KUNIT_EXPECT_FALSE(test, ssif_bmc->request_available); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 0); + + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xcd), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY); + + KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 1); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0xcd); +} + +static void ssif_bmc_test_write_interrupts_response_send(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + u8 value = 0; + + ssif_bmc->state = SSIF_SMBUS_CMD; + ssif_bmc->part_buf.smbus_cmd = SSIF_IPMI_SINGLEPART_READ; + ssif_bmc->response.len = 1; + ssif_bmc->response.payload[0] = 0x66; + ssif_bmc->response_in_progress = true; + ssif_bmc->is_singlepart_read = true; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED, + &value), 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_RES_SENDING); + + /* READ_REQUESTED transaction */ + ssif_bmc_test_singlepart_req(test); +} + +static void ssif_bmc_test_write_interrupts_response_sending(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + u8 value = 0; + + ssif_bmc->state = SSIF_SMBUS_CMD; + ssif_bmc->part_buf.smbus_cmd = SSIF_IPMI_SINGLEPART_READ; + ssif_bmc->response.len = 1; + ssif_bmc->response.payload[0] = 0x66; + ssif_bmc->response_in_progress = true; + ssif_bmc->is_singlepart_read = true; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED, + &value), 0); + KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_RES_SENDING); + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED, + &value), 0); + KUNIT_EXPECT_EQ(test, value, 0x66); + + /* READ_REQUESTED transaction */ + ssif_bmc_test_singlepart_req(test); +} + +static void ssif_bmc_test_timeout_interrupt_allows_retry(struct kunit *test) +{ + struct ssif_bmc_test_ctx *test_ctx = test->priv; + struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc; + + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x21), 0); + KUNIT_ASSERT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY); + + KUNIT_ASSERT_TRUE(test, timer_pending(&ssif_bmc->response_timer)); + timer_delete_sync(&ssif_bmc->response_timer); + response_timeout(&ssif_bmc->response_timer); + + KUNIT_EXPECT_FALSE(test, ssif_bmc->busy); + KUNIT_EXPECT_TRUE(test, ssif_bmc->aborting); + KUNIT_EXPECT_FALSE(test, ssif_bmc->response_timer_inited); + + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED, + GET_8BIT_ADDR(test_ctx->client.addr)), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, + SSIF_IPMI_SINGLEPART_WRITE), 0); + KUNIT_EXPECT_FALSE(test, ssif_bmc->aborting); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x22), 0); + KUNIT_EXPECT_EQ(test, + ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY); + + KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 1); + KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0x22); +} + +static struct kunit_case ssif_bmc_test_cases[] = { + KUNIT_CASE(ssif_bmc_test_singlepart_req), + KUNIT_CASE(ssif_bmc_test_restart_write_without_stop), + KUNIT_CASE(ssif_bmc_test_restart_after_invalid_command), + KUNIT_CASE(ssif_bmc_test_singlepart_read_response_completion), + KUNIT_CASE(ssif_bmc_test_stop_during_start_discards_partial_request), + KUNIT_CASE(ssif_bmc_test_read_interrupts_partial_write), + KUNIT_CASE(ssif_bmc_test_write_interrupts_response_send), + KUNIT_CASE(ssif_bmc_test_write_interrupts_response_sending), + KUNIT_CASE(ssif_bmc_test_timeout_interrupt_allows_retry), + {} +}; + +static struct kunit_suite ssif_bmc_test_suite = { + .name = "ssif_bmc_test", + .init = ssif_bmc_test_init, + .exit = ssif_bmc_test_exit, + .test_cases = ssif_bmc_test_cases, +}; + +kunit_test_suite(ssif_bmc_test_suite); +#endif + module_i2c_driver(ssif_bmc_driver); MODULE_AUTHOR("Quan Nguyen "); diff --git a/drivers/char/nsc_gpio.c b/drivers/char/nsc_gpio.c index da930c72bc74..2c9b12b85435 100644 --- a/drivers/char/nsc_gpio.c +++ b/drivers/char/nsc_gpio.c @@ -121,20 +121,6 @@ EXPORT_SYMBOL(nsc_gpio_write); EXPORT_SYMBOL(nsc_gpio_read); EXPORT_SYMBOL(nsc_gpio_dump); -static int __init nsc_gpio_init(void) -{ - printk(KERN_DEBUG NAME " initializing\n"); - return 0; -} - -static void __exit nsc_gpio_cleanup(void) -{ - printk(KERN_DEBUG NAME " cleanup\n"); -} - -module_init(nsc_gpio_init); -module_exit(nsc_gpio_cleanup); - MODULE_AUTHOR("Jim Cromie "); MODULE_DESCRIPTION("NatSemi GPIO Common Methods"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 677bb5ac950a..ccda997a9098 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1115,15 +1115,17 @@ static int sonypi_disable(void) } #ifdef CONFIG_ACPI -static int sonypi_acpi_add(struct acpi_device *device) +static int sonypi_acpi_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + sonypi_acpi_device = device; strcpy(acpi_device_name(device), "Sony laptop hotkeys"); strcpy(acpi_device_class(device), "sony/hotkey"); return 0; } -static void sonypi_acpi_remove(struct acpi_device *device) +static void sonypi_acpi_remove(struct platform_device *pdev) { sonypi_acpi_device = NULL; } @@ -1133,13 +1135,12 @@ static const struct acpi_device_id sonypi_device_ids[] = { {"", 0}, }; -static struct acpi_driver sonypi_acpi_driver = { - .name = "sonypi", - .class = "hkey", - .ids = sonypi_device_ids, - .ops = { - .add = sonypi_acpi_add, - .remove = sonypi_acpi_remove, +static struct platform_driver sonypi_acpi_driver = { + .probe = sonypi_acpi_probe, + .remove = sonypi_acpi_remove, + .driver = { + .name = "sonypi_acpi", + .acpi_match_table = sonypi_device_ids, }, }; #endif @@ -1518,8 +1519,8 @@ static int __init sonypi_init(void) goto err_free_device; #ifdef CONFIG_ACPI - if (acpi_bus_register_driver(&sonypi_acpi_driver) >= 0) - acpi_driver_registered = 1; + error = platform_driver_register(&sonypi_acpi_driver); + acpi_driver_registered = !error; #endif return 0; @@ -1535,7 +1536,7 @@ static void __exit sonypi_exit(void) { #ifdef CONFIG_ACPI if (acpi_driver_registered) - acpi_bus_unregister_driver(&sonypi_acpi_driver); + platform_driver_unregister(&sonypi_acpi_driver); #endif platform_device_unregister(sonypi_platform_device); platform_driver_unregister(&sonypi_driver); diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 0719577e584d..12b7394b34bd 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -247,7 +247,7 @@ static void tpm_dev_release(struct device *dev) kfree(chip->work_space.context_buf); kfree(chip->work_space.session_buf); #ifdef CONFIG_TCG_TPM2_HMAC - kfree(chip->auth); + kfree_sensitive(chip->auth); #endif kfree(chip); } diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 3a77be7ebf4a..b11e6fa8b740 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -21,7 +21,7 @@ static bool disable_pcr_integrity; module_param(disable_pcr_integrity, bool, 0444); MODULE_PARM_DESC(disable_pcr_integrity, "Disable integrity protection of TPM2_PCR_Extend"); -struct tpm2_hash tpm2_hash_map[] = { +static const struct tpm2_hash tpm2_hash_map[] = { {HASH_ALGO_SHA1, TPM_ALG_SHA1}, {HASH_ALGO_SHA256, TPM_ALG_SHA256}, {HASH_ALGO_SHA384, TPM_ALG_SHA384}, @@ -295,10 +295,8 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) } tpm_buf_append_u16(&buf, num_bytes); err = tpm_buf_fill_hmac_session(chip, &buf); - if (err) { - tpm_buf_destroy(&buf); - return err; - } + if (err) + goto out; err = tpm_transmit_cmd(chip, &buf, offsetof(struct tpm2_get_random_out, diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index 3b1cf1ca0420..c4da6fde748f 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -203,8 +203,10 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name) rc = tpm_buf_read_u16(&buf, &offset); name_size_alg = name_size(&buf.data[offset]); - if (name_size_alg < 0) + if (name_size_alg < 0) { + tpm_buf_destroy(&buf); return name_size_alg; + } if (rc != name_size_alg) { tpm_buf_destroy(&buf); @@ -217,6 +219,7 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name) } memcpy(name, &buf.data[offset], rc); + tpm_buf_destroy(&buf); return name_size_alg; } #endif /* CONFIG_TCG_TPM2_HMAC */ diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 6c25305c256e..7d1377e8e616 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #ifdef CONFIG_ARM64 #include @@ -602,13 +603,13 @@ static u64 crb_fixup_cmd_size(struct device *dev, struct resource *io_res, return io_res->end - start + 1; } -static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, +static int crb_map_io(struct device *dev, struct crb_priv *priv, struct acpi_table_tpm2 *buf) { + struct acpi_device *device = ACPI_COMPANION(dev); struct list_head acpi_resource_list; struct resource iores_array[TPM_CRB_MAX_RESOURCES + 1] = { {0} }; void __iomem *iobase_array[TPM_CRB_MAX_RESOURCES] = {NULL}; - struct device *dev = &device->dev; struct resource *iores; void __iomem **iobase_ptr; int i; @@ -782,12 +783,13 @@ static int crb_map_pluton(struct device *dev, struct crb_priv *priv, return 0; } -static int crb_acpi_add(struct acpi_device *device) +static int crb_acpi_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct acpi_device *device = ACPI_COMPANION(dev); struct acpi_table_tpm2 *buf; struct crb_priv *priv; struct tpm_chip *chip; - struct device *dev = &device->dev; struct tpm2_crb_smc *crb_smc; struct tpm2_crb_ffa *crb_ffa; struct tpm2_crb_pluton *crb_pluton; @@ -867,7 +869,7 @@ static int crb_acpi_add(struct acpi_device *device) priv->sm = sm; priv->hid = acpi_device_hid(device); - rc = crb_map_io(device, priv, buf); + rc = crb_map_io(dev, priv, buf); if (rc) goto out; @@ -901,12 +903,9 @@ out: return rc; } -static void crb_acpi_remove(struct acpi_device *device) +static void crb_acpi_remove(struct platform_device *pdev) { - struct device *dev = &device->dev; - struct tpm_chip *chip = dev_get_drvdata(dev); - - tpm_chip_unregister(chip); + tpm_chip_unregister(platform_get_drvdata(pdev)); } static const struct dev_pm_ops crb_pm = { @@ -919,19 +918,17 @@ static const struct acpi_device_id crb_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, crb_device_ids); -static struct acpi_driver crb_acpi_driver = { - .name = "tpm_crb", - .ids = crb_device_ids, - .ops = { - .add = crb_acpi_add, - .remove = crb_acpi_remove, - }, - .drv = { +static struct platform_driver crb_acpi_driver = { + .probe = crb_acpi_probe, + .remove = crb_acpi_remove, + .driver = { + .name = "tpm_crb_acpi", + .acpi_match_table = crb_device_ids, .pm = &crb_pm, }, }; -module_acpi_driver(crb_acpi_driver); +module_platform_driver(crb_acpi_driver); MODULE_AUTHOR("Jarkko Sakkinen "); MODULE_DESCRIPTION("TPM2 Driver"); MODULE_VERSION("0.1"); diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index 4f229656a8e2..9fd73049821f 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -31,9 +31,11 @@ struct priv_data { size_t len; - /* This is the amount we read on the first try. 25 was chosen to fit a + /* + * This is the amount we read on the first try. 25 was chosen to fit a * fair number of read responses in the buffer so a 2nd retry can be - * avoided in small message cases. */ + * avoided in small message cases. + */ u8 buffer[sizeof(struct tpm_header) + 25]; }; @@ -58,7 +60,9 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, if (status < 0) return status; - /* The upper layer does not support incomplete sends. */ + /* + * The upper layer does not support incomplete sends. + */ if (status != len) return -E2BIG; @@ -76,9 +80,11 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) if (priv->len == 0) return -EIO; - /* Get the message size from the message header, if we didn't get the + /* + * Get the message size from the message header, if we didn't get the * whole message in read_status then we need to re-read the - * message. */ + * message. + */ expected_len = be32_to_cpu(hdr->length); if (expected_len > count) return -ENOMEM; @@ -111,15 +117,19 @@ static u8 i2c_atmel_read_status(struct tpm_chip *chip) struct i2c_client *client = to_i2c_client(chip->dev.parent); int rc; - /* The TPM fails the I2C read until it is ready, so we do the entire + /* + * The TPM fails the I2C read until it is ready, so we do the entire * transfer here and buffer it locally. This way the common code can - * properly handle the timeouts. */ + * properly handle the timeouts. + */ priv->len = 0; memset(priv->buffer, 0, sizeof(priv->buffer)); - /* Once the TPM has completed the command the command remains readable - * until another command is issued. */ + /* + * Once the TPM has completed the command the command remains readable + * until another command is issued. + */ rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer)); dev_dbg(&chip->dev, "%s: sts=%d", __func__, rc); @@ -172,9 +182,11 @@ static int i2c_atmel_probe(struct i2c_client *client) dev_set_drvdata(&chip->dev, priv); - /* There is no known way to probe for this device, and all version + /* + * There is no known way to probe for this device, and all version * information seems to be read via TPM commands. Thus we rely on the - * TPM startup process in the common code to detect the device. */ + * TPM startup process in the common code to detect the device. + */ return tpm_chip_register(chip); } diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index e2a1769081b1..21d79ad3b164 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -471,6 +471,8 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len) status = tpm_tis_status(chip); if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) { rc = -EIO; + dev_err(&chip->dev, "TPM_STS_DATA_EXPECT should be set. sts = 0x%08x\n", + status); goto out_err; } } @@ -491,6 +493,8 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len) status = tpm_tis_status(chip); if (!itpm && (status & TPM_STS_DATA_EXPECT) != 0) { rc = -EIO; + dev_err(&chip->dev, "TPM_STS_DATA_EXPECT should be unset. sts = 0x%08x\n", + status); goto out_err; } @@ -552,11 +556,16 @@ static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len) break; else if (rc != -EAGAIN && rc != -EIO) /* Data transfer failed, not recoverable */ - return rc; + goto out_err; usleep_range(priv->timeout_min, priv->timeout_max); } + if (rc == -EAGAIN || rc == -EIO) { + dev_err(&chip->dev, "Exhausted %d tpm_tis_send_data retries\n", TPM_RETRY); + goto out_err; + } + /* go and do it */ rc = tpm_tis_write8(priv, TPM_STS(priv->locality), TPM_STS_GO); if (rc < 0) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 3d803b4cf5c1..b2efbe9f6acb 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -218,13 +218,13 @@ config COMMON_CLK_CS2000_CP If you say yes here you get support for the CS2000 clock multiplier. config COMMON_CLK_EN7523 - bool "Clock driver for Airoha EN7523 SoC system clocks" + bool "Clock driver for Airoha/EcoNet SoC system clocks" depends on OF - depends on ARCH_AIROHA || COMPILE_TEST + depends on ARCH_AIROHA || ECONET || COMPILE_TEST default ARCH_AIROHA help This driver provides the fixed clocks and gates present on Airoha - ARM silicon. + and EcoNet silicon. config COMMON_CLK_EP93XX tristate "Clock driver for Cirrus Logic ep93xx SoC" @@ -236,9 +236,9 @@ config COMMON_CLK_EP93XX config COMMON_CLK_EYEQ bool "Clock driver for the Mobileye EyeQ platform" - depends on MACH_EYEQ5 || MACH_EYEQ6H || COMPILE_TEST + depends on EYEQ || COMPILE_TEST select AUXILIARY_BUS - default MACH_EYEQ5 || MACH_EYEQ6H + default EYEQ help This driver provides clocks found on Mobileye EyeQ5, EyeQ6L and Eye6H SoCs. Controllers live in shared register regions called OLB. Driver @@ -255,7 +255,7 @@ config COMMON_CLK_FSL_FLEXSPI config COMMON_CLK_FSL_SAI bool "Clock driver for BCLK of Freescale SAI cores" - depends on ARCH_LAYERSCAPE || COMPILE_TEST + depends on ARCH_LAYERSCAPE || ARCH_MXC || COMPILE_TEST help This driver supports the Freescale SAI (Synchronous Audio Interface) to be used as a generic clock output. Some SoCs have restrictions @@ -502,8 +502,8 @@ config COMMON_CLK_RPMI source "drivers/clk/actions/Kconfig" source "drivers/clk/analogbits/Kconfig" source "drivers/clk/aspeed/Kconfig" -source "drivers/clk/baikal-t1/Kconfig" source "drivers/clk/bcm/Kconfig" +source "drivers/clk/eswin/Kconfig" source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/imgtec/Kconfig" source "drivers/clk/imx/Kconfig" @@ -531,6 +531,7 @@ source "drivers/clk/starfive/Kconfig" source "drivers/clk/sunxi/Kconfig" source "drivers/clk/sunxi-ng/Kconfig" source "drivers/clk/tegra/Kconfig" +source "drivers/clk/tenstorrent/Kconfig" source "drivers/clk/thead/Kconfig" source "drivers/clk/stm32/Kconfig" source "drivers/clk/ti/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index f7bce3951a30..a3e2862ebd7e 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -116,10 +116,10 @@ obj-y += aspeed/ obj-$(CONFIG_COMMON_CLK_AT91) += at91/ obj-$(CONFIG_ARCH_ARTPEC) += axis/ obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/ -obj-$(CONFIG_CLK_BAIKAL_T1) += baikal-t1/ obj-y += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ obj-$(CONFIG_ARCH_DAVINCI) += davinci/ +obj-$(CONFIG_COMMON_CLK_ESWIN) += eswin/ obj-$(CONFIG_ARCH_HISI) += hisilicon/ obj-y += imgtec/ obj-y += imx/ @@ -155,6 +155,7 @@ obj-y += starfive/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-y += sunxi-ng/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ +obj-y += tenstorrent/ obj-$(CONFIG_ARCH_THEAD) += thead/ obj-y += ti/ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ diff --git a/drivers/clk/baikal-t1/Kconfig b/drivers/clk/baikal-t1/Kconfig deleted file mode 100644 index f0b186830324..000000000000 --- a/drivers/clk/baikal-t1/Kconfig +++ /dev/null @@ -1,52 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config CLK_BAIKAL_T1 - bool "Baikal-T1 Clocks Control Unit interface" - depends on (MIPS_BAIKAL_T1 && OF) || COMPILE_TEST - default MIPS_BAIKAL_T1 - help - Clocks Control Unit is the core of Baikal-T1 SoC System Controller - responsible for the chip subsystems clocking and resetting. It - consists of multiple global clock domains, which can be reset by - means of the CCU control registers. These domains and devices placed - in them are fed with clocks generated by a hierarchy of PLLs, - configurable and fixed clock dividers. Enable this option to be able - to select Baikal-T1 CCU PLLs and Dividers drivers. - -if CLK_BAIKAL_T1 - -config CLK_BT1_CCU_PLL - bool "Baikal-T1 CCU PLLs support" - select MFD_SYSCON - default MIPS_BAIKAL_T1 - help - Enable this to support the PLLs embedded into the Baikal-T1 SoC - System Controller. These are five PLLs placed at the root of the - clocks hierarchy, right after an external reference oscillator - (normally of 25MHz). They are used to generate high frequency - signals, which are either directly wired to the consumers (like - CPUs, DDR, etc.) or passed over the clock dividers to be only - then used as an individual reference clock of a target device. - -config CLK_BT1_CCU_DIV - bool "Baikal-T1 CCU Dividers support" - select MFD_SYSCON - default MIPS_BAIKAL_T1 - help - Enable this to support the CCU dividers used to distribute clocks - between AXI-bus and system devices coming from CCU PLLs of Baikal-T1 - SoC. CCU dividers can be either configurable or with fixed divider, - either gateable or ungateable. Some of the CCU dividers can be as well - used to reset the domains they're supplying clock to. - -config CLK_BT1_CCU_RST - bool "Baikal-T1 CCU Resets support" - select RESET_CONTROLLER - select MFD_SYSCON - default MIPS_BAIKAL_T1 - help - Enable this to support the CCU reset blocks responsible for the - AXI-bus and some subsystems reset. These are mainly the - self-deasserted reset controls but there are several lines which - can be directly asserted/de-asserted (PCIe and DDR sub-domains). - -endif diff --git a/drivers/clk/baikal-t1/Makefile b/drivers/clk/baikal-t1/Makefile deleted file mode 100644 index 9c3637de9407..000000000000 --- a/drivers/clk/baikal-t1/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_CLK_BT1_CCU_PLL) += ccu-pll.o clk-ccu-pll.o -obj-$(CONFIG_CLK_BT1_CCU_DIV) += ccu-div.o clk-ccu-div.o -obj-$(CONFIG_CLK_BT1_CCU_RST) += ccu-rst.o diff --git a/drivers/clk/baikal-t1/ccu-div.c b/drivers/clk/baikal-t1/ccu-div.c deleted file mode 100644 index cc48e580e159..000000000000 --- a/drivers/clk/baikal-t1/ccu-div.c +++ /dev/null @@ -1,653 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC - * - * Authors: - * Serge Semin - * Dmitry Dunaev - * - * Baikal-T1 CCU Dividers interface driver - */ - -#define pr_fmt(fmt) "bt1-ccu-div: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ccu-div.h" - -#define CCU_DIV_CTL 0x00 -#define CCU_DIV_CTL_EN BIT(0) -#define CCU_DIV_CTL_RST BIT(1) -#define CCU_DIV_CTL_SET_CLKDIV BIT(2) -#define CCU_DIV_CTL_CLKDIV_FLD 4 -#define CCU_DIV_CTL_CLKDIV_MASK(_width) \ - GENMASK((_width) + CCU_DIV_CTL_CLKDIV_FLD - 1, CCU_DIV_CTL_CLKDIV_FLD) -#define CCU_DIV_CTL_LOCK_SHIFTED BIT(27) -#define CCU_DIV_CTL_GATE_REF_BUF BIT(28) -#define CCU_DIV_CTL_LOCK_NORMAL BIT(31) - -#define CCU_DIV_LOCK_CHECK_RETRIES 50 - -#define CCU_DIV_CLKDIV_MIN 0 -#define CCU_DIV_CLKDIV_MAX(_mask) \ - ((_mask) >> CCU_DIV_CTL_CLKDIV_FLD) - -/* - * Use the next two methods until there are generic field setter and - * getter available with non-constant mask support. - */ -static inline u32 ccu_div_get(u32 mask, u32 val) -{ - return (val & mask) >> CCU_DIV_CTL_CLKDIV_FLD; -} - -static inline u32 ccu_div_prep(u32 mask, u32 val) -{ - return (val << CCU_DIV_CTL_CLKDIV_FLD) & mask; -} - -static inline unsigned long ccu_div_lock_delay_ns(unsigned long ref_clk, - unsigned long div) -{ - u64 ns = 4ULL * (div ?: 1) * NSEC_PER_SEC; - - do_div(ns, ref_clk); - - return ns; -} - -static inline unsigned long ccu_div_calc_freq(unsigned long ref_clk, - unsigned long div) -{ - return ref_clk / (div ?: 1); -} - -static int ccu_div_var_update_clkdiv(struct ccu_div *div, - unsigned long parent_rate, - unsigned long divider) -{ - unsigned long nd; - u32 val = 0; - u32 lock; - int count; - - nd = ccu_div_lock_delay_ns(parent_rate, divider); - - if (div->features & CCU_DIV_LOCK_SHIFTED) - lock = CCU_DIV_CTL_LOCK_SHIFTED; - else - lock = CCU_DIV_CTL_LOCK_NORMAL; - - regmap_update_bits(div->sys_regs, div->reg_ctl, - CCU_DIV_CTL_SET_CLKDIV, CCU_DIV_CTL_SET_CLKDIV); - - /* - * Until there is nsec-version of readl_poll_timeout() is available - * we have to implement the next polling loop. - */ - count = CCU_DIV_LOCK_CHECK_RETRIES; - do { - ndelay(nd); - regmap_read(div->sys_regs, div->reg_ctl, &val); - if (val & lock) - return 0; - } while (--count); - - return -ETIMEDOUT; -} - -static int ccu_div_var_enable(struct clk_hw *hw) -{ - struct clk_hw *parent_hw = clk_hw_get_parent(hw); - struct ccu_div *div = to_ccu_div(hw); - unsigned long flags; - u32 val = 0; - int ret; - - if (!parent_hw) { - pr_err("Can't enable '%s' with no parent", clk_hw_get_name(hw)); - return -EINVAL; - } - - regmap_read(div->sys_regs, div->reg_ctl, &val); - if (val & CCU_DIV_CTL_EN) - return 0; - - spin_lock_irqsave(&div->lock, flags); - ret = ccu_div_var_update_clkdiv(div, clk_hw_get_rate(parent_hw), - ccu_div_get(div->mask, val)); - if (!ret) - regmap_update_bits(div->sys_regs, div->reg_ctl, - CCU_DIV_CTL_EN, CCU_DIV_CTL_EN); - spin_unlock_irqrestore(&div->lock, flags); - if (ret) - pr_err("Divider '%s' lock timed out\n", clk_hw_get_name(hw)); - - return ret; -} - -static int ccu_div_gate_enable(struct clk_hw *hw) -{ - struct ccu_div *div = to_ccu_div(hw); - unsigned long flags; - - spin_lock_irqsave(&div->lock, flags); - regmap_update_bits(div->sys_regs, div->reg_ctl, - CCU_DIV_CTL_EN, CCU_DIV_CTL_EN); - spin_unlock_irqrestore(&div->lock, flags); - - return 0; -} - -static void ccu_div_gate_disable(struct clk_hw *hw) -{ - struct ccu_div *div = to_ccu_div(hw); - unsigned long flags; - - spin_lock_irqsave(&div->lock, flags); - regmap_update_bits(div->sys_regs, div->reg_ctl, CCU_DIV_CTL_EN, 0); - spin_unlock_irqrestore(&div->lock, flags); -} - -static int ccu_div_gate_is_enabled(struct clk_hw *hw) -{ - struct ccu_div *div = to_ccu_div(hw); - u32 val = 0; - - regmap_read(div->sys_regs, div->reg_ctl, &val); - - return !!(val & CCU_DIV_CTL_EN); -} - -static int ccu_div_buf_enable(struct clk_hw *hw) -{ - struct ccu_div *div = to_ccu_div(hw); - unsigned long flags; - - spin_lock_irqsave(&div->lock, flags); - regmap_update_bits(div->sys_regs, div->reg_ctl, - CCU_DIV_CTL_GATE_REF_BUF, 0); - spin_unlock_irqrestore(&div->lock, flags); - - return 0; -} - -static void ccu_div_buf_disable(struct clk_hw *hw) -{ - struct ccu_div *div = to_ccu_div(hw); - unsigned long flags; - - spin_lock_irqsave(&div->lock, flags); - regmap_update_bits(div->sys_regs, div->reg_ctl, - CCU_DIV_CTL_GATE_REF_BUF, CCU_DIV_CTL_GATE_REF_BUF); - spin_unlock_irqrestore(&div->lock, flags); -} - -static int ccu_div_buf_is_enabled(struct clk_hw *hw) -{ - struct ccu_div *div = to_ccu_div(hw); - u32 val = 0; - - regmap_read(div->sys_regs, div->reg_ctl, &val); - - return !(val & CCU_DIV_CTL_GATE_REF_BUF); -} - -static unsigned long ccu_div_var_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct ccu_div *div = to_ccu_div(hw); - unsigned long divider; - u32 val = 0; - - regmap_read(div->sys_regs, div->reg_ctl, &val); - divider = ccu_div_get(div->mask, val); - - return ccu_div_calc_freq(parent_rate, divider); -} - -static inline unsigned long ccu_div_var_calc_divider(unsigned long rate, - unsigned long parent_rate, - unsigned int mask) -{ - unsigned long divider; - - divider = parent_rate / rate; - return clamp_t(unsigned long, divider, CCU_DIV_CLKDIV_MIN, - CCU_DIV_CLKDIV_MAX(mask)); -} - -static int ccu_div_var_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - struct ccu_div *div = to_ccu_div(hw); - unsigned long divider; - - divider = ccu_div_var_calc_divider(req->rate, req->best_parent_rate, - div->mask); - - req->rate = ccu_div_calc_freq(req->best_parent_rate, divider); - - return 0; -} - -/* - * This method is used for the clock divider blocks, which support the - * on-the-fly rate change. So due to lacking the EN bit functionality - * they can't be gated before the rate adjustment. - */ -static int ccu_div_var_set_rate_slow(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct ccu_div *div = to_ccu_div(hw); - unsigned long flags, divider; - u32 val; - int ret; - - divider = ccu_div_var_calc_divider(rate, parent_rate, div->mask); - if (divider == 1 && div->features & CCU_DIV_SKIP_ONE) { - divider = 0; - } else if (div->features & CCU_DIV_SKIP_ONE_TO_THREE) { - if (divider == 1 || divider == 2) - divider = 0; - else if (divider == 3) - divider = 4; - } - - val = ccu_div_prep(div->mask, divider); - - spin_lock_irqsave(&div->lock, flags); - regmap_update_bits(div->sys_regs, div->reg_ctl, div->mask, val); - ret = ccu_div_var_update_clkdiv(div, parent_rate, divider); - spin_unlock_irqrestore(&div->lock, flags); - if (ret) - pr_err("Divider '%s' lock timed out\n", clk_hw_get_name(hw)); - - return ret; -} - -/* - * This method is used for the clock divider blocks, which don't support - * the on-the-fly rate change. - */ -static int ccu_div_var_set_rate_fast(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct ccu_div *div = to_ccu_div(hw); - unsigned long flags, divider; - u32 val; - - divider = ccu_div_var_calc_divider(rate, parent_rate, div->mask); - val = ccu_div_prep(div->mask, divider); - - /* - * Also disable the clock divider block if it was enabled by default - * or by the bootloader. - */ - spin_lock_irqsave(&div->lock, flags); - regmap_update_bits(div->sys_regs, div->reg_ctl, - div->mask | CCU_DIV_CTL_EN, val); - spin_unlock_irqrestore(&div->lock, flags); - - return 0; -} - -static unsigned long ccu_div_fixed_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct ccu_div *div = to_ccu_div(hw); - - return ccu_div_calc_freq(parent_rate, div->divider); -} - -static int ccu_div_fixed_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - struct ccu_div *div = to_ccu_div(hw); - - req->rate = ccu_div_calc_freq(req->best_parent_rate, div->divider); - - return 0; -} - -static int ccu_div_fixed_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - return 0; -} - -#ifdef CONFIG_DEBUG_FS - -struct ccu_div_dbgfs_bit { - struct ccu_div *div; - const char *name; - u32 mask; -}; - -#define CCU_DIV_DBGFS_BIT_ATTR(_name, _mask) { \ - .name = _name, \ - .mask = _mask \ - } - -static const struct ccu_div_dbgfs_bit ccu_div_bits[] = { - CCU_DIV_DBGFS_BIT_ATTR("div_en", CCU_DIV_CTL_EN), - CCU_DIV_DBGFS_BIT_ATTR("div_rst", CCU_DIV_CTL_RST), - CCU_DIV_DBGFS_BIT_ATTR("div_bypass", CCU_DIV_CTL_SET_CLKDIV), - CCU_DIV_DBGFS_BIT_ATTR("div_buf", CCU_DIV_CTL_GATE_REF_BUF), - CCU_DIV_DBGFS_BIT_ATTR("div_lock", CCU_DIV_CTL_LOCK_NORMAL) -}; - -#define CCU_DIV_DBGFS_BIT_NUM ARRAY_SIZE(ccu_div_bits) - -/* - * It can be dangerous to change the Divider settings behind clock framework - * back, therefore we don't provide any kernel config based compile time option - * for this feature to enable. - */ -#undef CCU_DIV_ALLOW_WRITE_DEBUGFS -#ifdef CCU_DIV_ALLOW_WRITE_DEBUGFS - -static int ccu_div_dbgfs_bit_set(void *priv, u64 val) -{ - const struct ccu_div_dbgfs_bit *bit = priv; - struct ccu_div *div = bit->div; - unsigned long flags; - - spin_lock_irqsave(&div->lock, flags); - regmap_update_bits(div->sys_regs, div->reg_ctl, - bit->mask, val ? bit->mask : 0); - spin_unlock_irqrestore(&div->lock, flags); - - return 0; -} - -static int ccu_div_dbgfs_var_clkdiv_set(void *priv, u64 val) -{ - struct ccu_div *div = priv; - unsigned long flags; - u32 data; - - val = clamp_t(u64, val, CCU_DIV_CLKDIV_MIN, - CCU_DIV_CLKDIV_MAX(div->mask)); - data = ccu_div_prep(div->mask, val); - - spin_lock_irqsave(&div->lock, flags); - regmap_update_bits(div->sys_regs, div->reg_ctl, div->mask, data); - spin_unlock_irqrestore(&div->lock, flags); - - return 0; -} - -#define ccu_div_dbgfs_mode 0644 - -#else /* !CCU_DIV_ALLOW_WRITE_DEBUGFS */ - -#define ccu_div_dbgfs_bit_set NULL -#define ccu_div_dbgfs_var_clkdiv_set NULL -#define ccu_div_dbgfs_mode 0444 - -#endif /* !CCU_DIV_ALLOW_WRITE_DEBUGFS */ - -static int ccu_div_dbgfs_bit_get(void *priv, u64 *val) -{ - const struct ccu_div_dbgfs_bit *bit = priv; - struct ccu_div *div = bit->div; - u32 data = 0; - - regmap_read(div->sys_regs, div->reg_ctl, &data); - *val = !!(data & bit->mask); - - return 0; -} -DEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_bit_fops, - ccu_div_dbgfs_bit_get, ccu_div_dbgfs_bit_set, "%llu\n"); - -static int ccu_div_dbgfs_var_clkdiv_get(void *priv, u64 *val) -{ - struct ccu_div *div = priv; - u32 data = 0; - - regmap_read(div->sys_regs, div->reg_ctl, &data); - *val = ccu_div_get(div->mask, data); - - return 0; -} -DEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_var_clkdiv_fops, - ccu_div_dbgfs_var_clkdiv_get, ccu_div_dbgfs_var_clkdiv_set, "%llu\n"); - -static int ccu_div_dbgfs_fixed_clkdiv_get(void *priv, u64 *val) -{ - struct ccu_div *div = priv; - - *val = div->divider; - - return 0; -} -DEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_fixed_clkdiv_fops, - ccu_div_dbgfs_fixed_clkdiv_get, NULL, "%llu\n"); - -static void ccu_div_var_debug_init(struct clk_hw *hw, struct dentry *dentry) -{ - struct ccu_div *div = to_ccu_div(hw); - struct ccu_div_dbgfs_bit *bits; - int didx, bidx, num = 2; - const char *name; - - num += !!(div->flags & CLK_SET_RATE_GATE) + - !!(div->features & CCU_DIV_RESET_DOMAIN); - - bits = kzalloc_objs(*bits, num); - if (!bits) - return; - - for (didx = 0, bidx = 0; bidx < CCU_DIV_DBGFS_BIT_NUM; ++bidx) { - name = ccu_div_bits[bidx].name; - if (!(div->flags & CLK_SET_RATE_GATE) && - !strcmp("div_en", name)) { - continue; - } - - if (!(div->features & CCU_DIV_RESET_DOMAIN) && - !strcmp("div_rst", name)) { - continue; - } - - if (!strcmp("div_buf", name)) - continue; - - bits[didx] = ccu_div_bits[bidx]; - bits[didx].div = div; - - if (div->features & CCU_DIV_LOCK_SHIFTED && - !strcmp("div_lock", name)) { - bits[didx].mask = CCU_DIV_CTL_LOCK_SHIFTED; - } - - debugfs_create_file_unsafe(bits[didx].name, ccu_div_dbgfs_mode, - dentry, &bits[didx], - &ccu_div_dbgfs_bit_fops); - ++didx; - } - - debugfs_create_file_unsafe("div_clkdiv", ccu_div_dbgfs_mode, dentry, - div, &ccu_div_dbgfs_var_clkdiv_fops); -} - -static void ccu_div_gate_debug_init(struct clk_hw *hw, struct dentry *dentry) -{ - struct ccu_div *div = to_ccu_div(hw); - struct ccu_div_dbgfs_bit *bit; - - bit = kmalloc_obj(*bit); - if (!bit) - return; - - *bit = ccu_div_bits[0]; - bit->div = div; - debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit, - &ccu_div_dbgfs_bit_fops); - - debugfs_create_file_unsafe("div_clkdiv", 0400, dentry, div, - &ccu_div_dbgfs_fixed_clkdiv_fops); -} - -static void ccu_div_buf_debug_init(struct clk_hw *hw, struct dentry *dentry) -{ - struct ccu_div *div = to_ccu_div(hw); - struct ccu_div_dbgfs_bit *bit; - - bit = kmalloc_obj(*bit); - if (!bit) - return; - - *bit = ccu_div_bits[3]; - bit->div = div; - debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit, - &ccu_div_dbgfs_bit_fops); -} - -static void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry) -{ - struct ccu_div *div = to_ccu_div(hw); - - debugfs_create_file_unsafe("div_clkdiv", 0400, dentry, div, - &ccu_div_dbgfs_fixed_clkdiv_fops); -} - -#else /* !CONFIG_DEBUG_FS */ - -#define ccu_div_var_debug_init NULL -#define ccu_div_gate_debug_init NULL -#define ccu_div_buf_debug_init NULL -#define ccu_div_fixed_debug_init NULL - -#endif /* !CONFIG_DEBUG_FS */ - -static const struct clk_ops ccu_div_var_gate_to_set_ops = { - .enable = ccu_div_var_enable, - .disable = ccu_div_gate_disable, - .is_enabled = ccu_div_gate_is_enabled, - .recalc_rate = ccu_div_var_recalc_rate, - .determine_rate = ccu_div_var_determine_rate, - .set_rate = ccu_div_var_set_rate_fast, - .debug_init = ccu_div_var_debug_init -}; - -static const struct clk_ops ccu_div_var_nogate_ops = { - .recalc_rate = ccu_div_var_recalc_rate, - .determine_rate = ccu_div_var_determine_rate, - .set_rate = ccu_div_var_set_rate_slow, - .debug_init = ccu_div_var_debug_init -}; - -static const struct clk_ops ccu_div_gate_ops = { - .enable = ccu_div_gate_enable, - .disable = ccu_div_gate_disable, - .is_enabled = ccu_div_gate_is_enabled, - .recalc_rate = ccu_div_fixed_recalc_rate, - .determine_rate = ccu_div_fixed_determine_rate, - .set_rate = ccu_div_fixed_set_rate, - .debug_init = ccu_div_gate_debug_init -}; - -static const struct clk_ops ccu_div_buf_ops = { - .enable = ccu_div_buf_enable, - .disable = ccu_div_buf_disable, - .is_enabled = ccu_div_buf_is_enabled, - .debug_init = ccu_div_buf_debug_init -}; - -static const struct clk_ops ccu_div_fixed_ops = { - .recalc_rate = ccu_div_fixed_recalc_rate, - .determine_rate = ccu_div_fixed_determine_rate, - .set_rate = ccu_div_fixed_set_rate, - .debug_init = ccu_div_fixed_debug_init -}; - -struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *div_init) -{ - struct clk_parent_data parent_data = { }; - struct clk_init_data hw_init = { }; - struct ccu_div *div; - int ret; - - if (!div_init) - return ERR_PTR(-EINVAL); - - div = kzalloc_obj(*div); - if (!div) - return ERR_PTR(-ENOMEM); - - /* - * Note since Baikal-T1 System Controller registers are MMIO-backed - * we won't check the regmap IO operations return status, because it - * must be zero anyway. - */ - div->hw.init = &hw_init; - div->id = div_init->id; - div->reg_ctl = div_init->base + CCU_DIV_CTL; - div->sys_regs = div_init->sys_regs; - div->flags = div_init->flags; - div->features = div_init->features; - spin_lock_init(&div->lock); - - hw_init.name = div_init->name; - hw_init.flags = div_init->flags; - - if (div_init->type == CCU_DIV_VAR) { - if (hw_init.flags & CLK_SET_RATE_GATE) - hw_init.ops = &ccu_div_var_gate_to_set_ops; - else - hw_init.ops = &ccu_div_var_nogate_ops; - div->mask = CCU_DIV_CTL_CLKDIV_MASK(div_init->width); - } else if (div_init->type == CCU_DIV_GATE) { - hw_init.ops = &ccu_div_gate_ops; - div->divider = div_init->divider; - } else if (div_init->type == CCU_DIV_BUF) { - hw_init.ops = &ccu_div_buf_ops; - } else if (div_init->type == CCU_DIV_FIXED) { - hw_init.ops = &ccu_div_fixed_ops; - div->divider = div_init->divider; - } else { - ret = -EINVAL; - goto err_free_div; - } - - if (!div_init->parent_name) { - ret = -EINVAL; - goto err_free_div; - } - parent_data.fw_name = div_init->parent_name; - parent_data.name = div_init->parent_name; - hw_init.parent_data = &parent_data; - hw_init.num_parents = 1; - - ret = of_clk_hw_register(div_init->np, &div->hw); - if (ret) - goto err_free_div; - - return div; - -err_free_div: - kfree(div); - - return ERR_PTR(ret); -} - -void ccu_div_hw_unregister(struct ccu_div *div) -{ - clk_hw_unregister(&div->hw); - - kfree(div); -} diff --git a/drivers/clk/baikal-t1/ccu-div.h b/drivers/clk/baikal-t1/ccu-div.h deleted file mode 100644 index 76d8ee44d415..000000000000 --- a/drivers/clk/baikal-t1/ccu-div.h +++ /dev/null @@ -1,121 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC - * - * Baikal-T1 CCU Dividers interface driver - */ -#ifndef __CLK_BT1_CCU_DIV_H__ -#define __CLK_BT1_CCU_DIV_H__ - -#include -#include -#include -#include -#include - -/* - * CCU Divider private clock IDs - * @CCU_SYS_SATA_CLK: CCU SATA internal clock - * @CCU_SYS_XGMAC_CLK: CCU XGMAC internal clock - */ -#define CCU_SYS_SATA_CLK -1 -#define CCU_SYS_XGMAC_CLK -2 - -/* - * CCU Divider private flags - * @CCU_DIV_BASIC: Basic divider clock required by the kernel as early as - * possible. - * @CCU_DIV_SKIP_ONE: Due to some reason divider can't be set to 1. - * It can be 0 though, which is functionally the same. - * @CCU_DIV_SKIP_ONE_TO_THREE: For some reason divider can't be within [1,3]. - * It can be either 0 or greater than 3. - * @CCU_DIV_LOCK_SHIFTED: Find lock-bit at non-standard position. - * @CCU_DIV_RESET_DOMAIN: There is a clock domain reset handle. - */ -#define CCU_DIV_BASIC BIT(0) -#define CCU_DIV_SKIP_ONE BIT(1) -#define CCU_DIV_SKIP_ONE_TO_THREE BIT(2) -#define CCU_DIV_LOCK_SHIFTED BIT(3) -#define CCU_DIV_RESET_DOMAIN BIT(4) - -/* - * enum ccu_div_type - CCU Divider types - * @CCU_DIV_VAR: Clocks gate with variable divider. - * @CCU_DIV_GATE: Clocks gate with fixed divider. - * @CCU_DIV_BUF: Clock gate with no divider. - * @CCU_DIV_FIXED: Ungateable clock with fixed divider. - */ -enum ccu_div_type { - CCU_DIV_VAR, - CCU_DIV_GATE, - CCU_DIV_BUF, - CCU_DIV_FIXED -}; - -/* - * struct ccu_div_init_data - CCU Divider initialization data - * @id: Clocks private identifier. - * @name: Clocks name. - * @parent_name: Parent clocks name in a fw node. - * @base: Divider register base address with respect to the sys_regs base. - * @sys_regs: Baikal-T1 System Controller registers map. - * @np: Pointer to the node describing the CCU Dividers. - * @type: CCU divider type (variable, fixed with and without gate). - * @width: Divider width if it's variable. - * @divider: Divider fixed value. - * @flags: CCU Divider clock flags. - * @features: CCU Divider private features. - */ -struct ccu_div_init_data { - unsigned int id; - const char *name; - const char *parent_name; - unsigned int base; - struct regmap *sys_regs; - struct device_node *np; - enum ccu_div_type type; - union { - unsigned int width; - unsigned int divider; - }; - unsigned long flags; - unsigned long features; -}; - -/* - * struct ccu_div - CCU Divider descriptor - * @hw: clk_hw of the divider. - * @id: Clock private identifier. - * @reg_ctl: Divider control register base address. - * @sys_regs: Baikal-T1 System Controller registers map. - * @lock: Divider state change spin-lock. - * @mask: Divider field mask. - * @divider: Divider fixed value. - * @flags: Divider clock flags. - * @features: CCU Divider private features. - */ -struct ccu_div { - struct clk_hw hw; - unsigned int id; - unsigned int reg_ctl; - struct regmap *sys_regs; - spinlock_t lock; - union { - u32 mask; - unsigned int divider; - }; - unsigned long flags; - unsigned long features; -}; -#define to_ccu_div(_hw) container_of(_hw, struct ccu_div, hw) - -static inline struct clk_hw *ccu_div_get_clk_hw(struct ccu_div *div) -{ - return div ? &div->hw : NULL; -} - -struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *init); - -void ccu_div_hw_unregister(struct ccu_div *div); - -#endif /* __CLK_BT1_CCU_DIV_H__ */ diff --git a/drivers/clk/baikal-t1/ccu-pll.c b/drivers/clk/baikal-t1/ccu-pll.c deleted file mode 100644 index da7fbebb39ab..000000000000 --- a/drivers/clk/baikal-t1/ccu-pll.c +++ /dev/null @@ -1,560 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC - * - * Authors: - * Serge Semin - * Dmitry Dunaev - * - * Baikal-T1 CCU PLL interface driver - */ - -#define pr_fmt(fmt) "bt1-ccu-pll: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ccu-pll.h" - -#define CCU_PLL_CTL 0x000 -#define CCU_PLL_CTL_EN BIT(0) -#define CCU_PLL_CTL_RST BIT(1) -#define CCU_PLL_CTL_CLKR_FLD 2 -#define CCU_PLL_CTL_CLKR_MASK GENMASK(7, CCU_PLL_CTL_CLKR_FLD) -#define CCU_PLL_CTL_CLKF_FLD 8 -#define CCU_PLL_CTL_CLKF_MASK GENMASK(20, CCU_PLL_CTL_CLKF_FLD) -#define CCU_PLL_CTL_CLKOD_FLD 21 -#define CCU_PLL_CTL_CLKOD_MASK GENMASK(24, CCU_PLL_CTL_CLKOD_FLD) -#define CCU_PLL_CTL_BYPASS BIT(30) -#define CCU_PLL_CTL_LOCK BIT(31) -#define CCU_PLL_CTL1 0x004 -#define CCU_PLL_CTL1_BWADJ_FLD 3 -#define CCU_PLL_CTL1_BWADJ_MASK GENMASK(14, CCU_PLL_CTL1_BWADJ_FLD) - -#define CCU_PLL_LOCK_CHECK_RETRIES 50 - -#define CCU_PLL_NR_MAX \ - ((CCU_PLL_CTL_CLKR_MASK >> CCU_PLL_CTL_CLKR_FLD) + 1) -#define CCU_PLL_NF_MAX \ - ((CCU_PLL_CTL_CLKF_MASK >> (CCU_PLL_CTL_CLKF_FLD + 1)) + 1) -#define CCU_PLL_OD_MAX \ - ((CCU_PLL_CTL_CLKOD_MASK >> CCU_PLL_CTL_CLKOD_FLD) + 1) -#define CCU_PLL_NB_MAX \ - ((CCU_PLL_CTL1_BWADJ_MASK >> CCU_PLL_CTL1_BWADJ_FLD) + 1) -#define CCU_PLL_FDIV_MIN 427000UL -#define CCU_PLL_FDIV_MAX 3500000000UL -#define CCU_PLL_FOUT_MIN 200000000UL -#define CCU_PLL_FOUT_MAX 2500000000UL -#define CCU_PLL_FVCO_MIN 700000000UL -#define CCU_PLL_FVCO_MAX 3500000000UL -#define CCU_PLL_CLKOD_FACTOR 2 - -static inline unsigned long ccu_pll_lock_delay_us(unsigned long ref_clk, - unsigned long nr) -{ - u64 us = 500ULL * nr * USEC_PER_SEC; - - do_div(us, ref_clk); - - return us; -} - -static inline unsigned long ccu_pll_calc_freq(unsigned long ref_clk, - unsigned long nr, - unsigned long nf, - unsigned long od) -{ - u64 tmp = ref_clk; - - do_div(tmp, nr); - tmp *= nf; - do_div(tmp, od); - - return tmp; -} - -static int ccu_pll_reset(struct ccu_pll *pll, unsigned long ref_clk, - unsigned long nr) -{ - unsigned long ud, ut; - u32 val; - - ud = ccu_pll_lock_delay_us(ref_clk, nr); - ut = ud * CCU_PLL_LOCK_CHECK_RETRIES; - - regmap_update_bits(pll->sys_regs, pll->reg_ctl, - CCU_PLL_CTL_RST, CCU_PLL_CTL_RST); - - return regmap_read_poll_timeout_atomic(pll->sys_regs, pll->reg_ctl, val, - val & CCU_PLL_CTL_LOCK, ud, ut); -} - -static int ccu_pll_enable(struct clk_hw *hw) -{ - struct clk_hw *parent_hw = clk_hw_get_parent(hw); - struct ccu_pll *pll = to_ccu_pll(hw); - unsigned long flags; - u32 val = 0; - int ret; - - if (!parent_hw) { - pr_err("Can't enable '%s' with no parent", clk_hw_get_name(hw)); - return -EINVAL; - } - - regmap_read(pll->sys_regs, pll->reg_ctl, &val); - if (val & CCU_PLL_CTL_EN) - return 0; - - spin_lock_irqsave(&pll->lock, flags); - regmap_write(pll->sys_regs, pll->reg_ctl, val | CCU_PLL_CTL_EN); - ret = ccu_pll_reset(pll, clk_hw_get_rate(parent_hw), - FIELD_GET(CCU_PLL_CTL_CLKR_MASK, val) + 1); - spin_unlock_irqrestore(&pll->lock, flags); - if (ret) - pr_err("PLL '%s' reset timed out\n", clk_hw_get_name(hw)); - - return ret; -} - -static void ccu_pll_disable(struct clk_hw *hw) -{ - struct ccu_pll *pll = to_ccu_pll(hw); - unsigned long flags; - - spin_lock_irqsave(&pll->lock, flags); - regmap_update_bits(pll->sys_regs, pll->reg_ctl, CCU_PLL_CTL_EN, 0); - spin_unlock_irqrestore(&pll->lock, flags); -} - -static int ccu_pll_is_enabled(struct clk_hw *hw) -{ - struct ccu_pll *pll = to_ccu_pll(hw); - u32 val = 0; - - regmap_read(pll->sys_regs, pll->reg_ctl, &val); - - return !!(val & CCU_PLL_CTL_EN); -} - -static unsigned long ccu_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct ccu_pll *pll = to_ccu_pll(hw); - unsigned long nr, nf, od; - u32 val = 0; - - regmap_read(pll->sys_regs, pll->reg_ctl, &val); - nr = FIELD_GET(CCU_PLL_CTL_CLKR_MASK, val) + 1; - nf = FIELD_GET(CCU_PLL_CTL_CLKF_MASK, val) + 1; - od = FIELD_GET(CCU_PLL_CTL_CLKOD_MASK, val) + 1; - - return ccu_pll_calc_freq(parent_rate, nr, nf, od); -} - -static void ccu_pll_calc_factors(unsigned long rate, unsigned long parent_rate, - unsigned long *nr, unsigned long *nf, - unsigned long *od) -{ - unsigned long err, freq, min_err = ULONG_MAX; - unsigned long num, denom, n1, d1, nri; - unsigned long nr_max, nf_max, od_max; - - /* - * Make sure PLL is working with valid input signal (Fdiv). If - * you want to speed the function up just reduce CCU_PLL_NR_MAX. - * This will cause a worse approximation though. - */ - nri = (parent_rate / CCU_PLL_FDIV_MAX) + 1; - nr_max = min(parent_rate / CCU_PLL_FDIV_MIN, CCU_PLL_NR_MAX); - - /* - * Find a closest [nr;nf;od] vector taking into account the - * limitations like: 1) 700MHz <= Fvco <= 3.5GHz, 2) PLL Od is - * either 1 or even number within the acceptable range (alas 1s - * is also excluded by the next loop). - */ - for (; nri <= nr_max; ++nri) { - /* Use Od factor to fulfill the limitation 2). */ - num = CCU_PLL_CLKOD_FACTOR * rate; - denom = parent_rate / nri; - - /* - * Make sure Fvco is within the acceptable range to fulfill - * the condition 1). Note due to the CCU_PLL_CLKOD_FACTOR value - * the actual upper limit is also divided by that factor. - * It's not big problem for us since practically there is no - * need in clocks with that high frequency. - */ - nf_max = min(CCU_PLL_FVCO_MAX / denom, CCU_PLL_NF_MAX); - od_max = CCU_PLL_OD_MAX / CCU_PLL_CLKOD_FACTOR; - - /* - * Bypass the out-of-bound values, which can't be properly - * handled by the rational fraction approximation algorithm. - */ - if (num / denom >= nf_max) { - n1 = nf_max; - d1 = 1; - } else if (denom / num >= od_max) { - n1 = 1; - d1 = od_max; - } else { - rational_best_approximation(num, denom, nf_max, od_max, - &n1, &d1); - } - - /* Select the best approximation of the target rate. */ - freq = ccu_pll_calc_freq(parent_rate, nri, n1, d1); - err = abs((int64_t)freq - num); - if (err < min_err) { - min_err = err; - *nr = nri; - *nf = n1; - *od = CCU_PLL_CLKOD_FACTOR * d1; - } - } -} - -static int ccu_pll_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - unsigned long nr = 1, nf = 1, od = 1; - - ccu_pll_calc_factors(req->rate, req->best_parent_rate, &nr, &nf, &od); - - req->rate = ccu_pll_calc_freq(req->best_parent_rate, nr, nf, od); - - return 0; -} - -/* - * This method is used for PLLs, which support the on-the-fly dividers - * adjustment. So there is no need in gating such clocks. - */ -static int ccu_pll_set_rate_reset(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct ccu_pll *pll = to_ccu_pll(hw); - unsigned long nr, nf, od; - unsigned long flags; - u32 mask, val; - int ret; - - ccu_pll_calc_factors(rate, parent_rate, &nr, &nf, &od); - - mask = CCU_PLL_CTL_CLKR_MASK | CCU_PLL_CTL_CLKF_MASK | - CCU_PLL_CTL_CLKOD_MASK; - val = FIELD_PREP(CCU_PLL_CTL_CLKR_MASK, nr - 1) | - FIELD_PREP(CCU_PLL_CTL_CLKF_MASK, nf - 1) | - FIELD_PREP(CCU_PLL_CTL_CLKOD_MASK, od - 1); - - spin_lock_irqsave(&pll->lock, flags); - regmap_update_bits(pll->sys_regs, pll->reg_ctl, mask, val); - ret = ccu_pll_reset(pll, parent_rate, nr); - spin_unlock_irqrestore(&pll->lock, flags); - if (ret) - pr_err("PLL '%s' reset timed out\n", clk_hw_get_name(hw)); - - return ret; -} - -/* - * This method is used for PLLs, which don't support the on-the-fly dividers - * adjustment. So the corresponding clocks are supposed to be gated first. - */ -static int ccu_pll_set_rate_norst(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct ccu_pll *pll = to_ccu_pll(hw); - unsigned long nr, nf, od; - unsigned long flags; - u32 mask, val; - - ccu_pll_calc_factors(rate, parent_rate, &nr, &nf, &od); - - /* - * Disable PLL if it was enabled by default or left enabled by the - * system bootloader. - */ - mask = CCU_PLL_CTL_CLKR_MASK | CCU_PLL_CTL_CLKF_MASK | - CCU_PLL_CTL_CLKOD_MASK | CCU_PLL_CTL_EN; - val = FIELD_PREP(CCU_PLL_CTL_CLKR_MASK, nr - 1) | - FIELD_PREP(CCU_PLL_CTL_CLKF_MASK, nf - 1) | - FIELD_PREP(CCU_PLL_CTL_CLKOD_MASK, od - 1); - - spin_lock_irqsave(&pll->lock, flags); - regmap_update_bits(pll->sys_regs, pll->reg_ctl, mask, val); - spin_unlock_irqrestore(&pll->lock, flags); - - return 0; -} - -#ifdef CONFIG_DEBUG_FS - -struct ccu_pll_dbgfs_bit { - struct ccu_pll *pll; - const char *name; - unsigned int reg; - u32 mask; -}; - -struct ccu_pll_dbgfs_fld { - struct ccu_pll *pll; - const char *name; - unsigned int reg; - unsigned int lsb; - u32 mask; - u32 min; - u32 max; -}; - -#define CCU_PLL_DBGFS_BIT_ATTR(_name, _reg, _mask) \ - { \ - .name = _name, \ - .reg = _reg, \ - .mask = _mask \ - } - -#define CCU_PLL_DBGFS_FLD_ATTR(_name, _reg, _lsb, _mask, _min, _max) \ - { \ - .name = _name, \ - .reg = _reg, \ - .lsb = _lsb, \ - .mask = _mask, \ - .min = _min, \ - .max = _max \ - } - -static const struct ccu_pll_dbgfs_bit ccu_pll_bits[] = { - CCU_PLL_DBGFS_BIT_ATTR("pll_en", CCU_PLL_CTL, CCU_PLL_CTL_EN), - CCU_PLL_DBGFS_BIT_ATTR("pll_rst", CCU_PLL_CTL, CCU_PLL_CTL_RST), - CCU_PLL_DBGFS_BIT_ATTR("pll_bypass", CCU_PLL_CTL, CCU_PLL_CTL_BYPASS), - CCU_PLL_DBGFS_BIT_ATTR("pll_lock", CCU_PLL_CTL, CCU_PLL_CTL_LOCK) -}; - -#define CCU_PLL_DBGFS_BIT_NUM ARRAY_SIZE(ccu_pll_bits) - -static const struct ccu_pll_dbgfs_fld ccu_pll_flds[] = { - CCU_PLL_DBGFS_FLD_ATTR("pll_nr", CCU_PLL_CTL, CCU_PLL_CTL_CLKR_FLD, - CCU_PLL_CTL_CLKR_MASK, 1, CCU_PLL_NR_MAX), - CCU_PLL_DBGFS_FLD_ATTR("pll_nf", CCU_PLL_CTL, CCU_PLL_CTL_CLKF_FLD, - CCU_PLL_CTL_CLKF_MASK, 1, CCU_PLL_NF_MAX), - CCU_PLL_DBGFS_FLD_ATTR("pll_od", CCU_PLL_CTL, CCU_PLL_CTL_CLKOD_FLD, - CCU_PLL_CTL_CLKOD_MASK, 1, CCU_PLL_OD_MAX), - CCU_PLL_DBGFS_FLD_ATTR("pll_nb", CCU_PLL_CTL1, CCU_PLL_CTL1_BWADJ_FLD, - CCU_PLL_CTL1_BWADJ_MASK, 1, CCU_PLL_NB_MAX) -}; - -#define CCU_PLL_DBGFS_FLD_NUM ARRAY_SIZE(ccu_pll_flds) - -/* - * It can be dangerous to change the PLL settings behind clock framework back, - * therefore we don't provide any kernel config based compile time option for - * this feature to enable. - */ -#undef CCU_PLL_ALLOW_WRITE_DEBUGFS -#ifdef CCU_PLL_ALLOW_WRITE_DEBUGFS - -static int ccu_pll_dbgfs_bit_set(void *priv, u64 val) -{ - const struct ccu_pll_dbgfs_bit *bit = priv; - struct ccu_pll *pll = bit->pll; - unsigned long flags; - - spin_lock_irqsave(&pll->lock, flags); - regmap_update_bits(pll->sys_regs, pll->reg_ctl + bit->reg, - bit->mask, val ? bit->mask : 0); - spin_unlock_irqrestore(&pll->lock, flags); - - return 0; -} - -static int ccu_pll_dbgfs_fld_set(void *priv, u64 val) -{ - struct ccu_pll_dbgfs_fld *fld = priv; - struct ccu_pll *pll = fld->pll; - unsigned long flags; - u32 data; - - val = clamp_t(u64, val, fld->min, fld->max); - data = ((val - 1) << fld->lsb) & fld->mask; - - spin_lock_irqsave(&pll->lock, flags); - regmap_update_bits(pll->sys_regs, pll->reg_ctl + fld->reg, fld->mask, - data); - spin_unlock_irqrestore(&pll->lock, flags); - - return 0; -} - -#define ccu_pll_dbgfs_mode 0644 - -#else /* !CCU_PLL_ALLOW_WRITE_DEBUGFS */ - -#define ccu_pll_dbgfs_bit_set NULL -#define ccu_pll_dbgfs_fld_set NULL -#define ccu_pll_dbgfs_mode 0444 - -#endif /* !CCU_PLL_ALLOW_WRITE_DEBUGFS */ - -static int ccu_pll_dbgfs_bit_get(void *priv, u64 *val) -{ - struct ccu_pll_dbgfs_bit *bit = priv; - struct ccu_pll *pll = bit->pll; - u32 data = 0; - - regmap_read(pll->sys_regs, pll->reg_ctl + bit->reg, &data); - *val = !!(data & bit->mask); - - return 0; -} -DEFINE_DEBUGFS_ATTRIBUTE(ccu_pll_dbgfs_bit_fops, - ccu_pll_dbgfs_bit_get, ccu_pll_dbgfs_bit_set, "%llu\n"); - -static int ccu_pll_dbgfs_fld_get(void *priv, u64 *val) -{ - struct ccu_pll_dbgfs_fld *fld = priv; - struct ccu_pll *pll = fld->pll; - u32 data = 0; - - regmap_read(pll->sys_regs, pll->reg_ctl + fld->reg, &data); - *val = ((data & fld->mask) >> fld->lsb) + 1; - - return 0; -} -DEFINE_DEBUGFS_ATTRIBUTE(ccu_pll_dbgfs_fld_fops, - ccu_pll_dbgfs_fld_get, ccu_pll_dbgfs_fld_set, "%llu\n"); - -static void ccu_pll_debug_init(struct clk_hw *hw, struct dentry *dentry) -{ - struct ccu_pll *pll = to_ccu_pll(hw); - struct ccu_pll_dbgfs_bit *bits; - struct ccu_pll_dbgfs_fld *flds; - int idx; - - bits = kzalloc_objs(*bits, CCU_PLL_DBGFS_BIT_NUM); - if (!bits) - return; - - for (idx = 0; idx < CCU_PLL_DBGFS_BIT_NUM; ++idx) { - bits[idx] = ccu_pll_bits[idx]; - bits[idx].pll = pll; - - debugfs_create_file_unsafe(bits[idx].name, ccu_pll_dbgfs_mode, - dentry, &bits[idx], - &ccu_pll_dbgfs_bit_fops); - } - - flds = kzalloc_objs(*flds, CCU_PLL_DBGFS_FLD_NUM); - if (!flds) - return; - - for (idx = 0; idx < CCU_PLL_DBGFS_FLD_NUM; ++idx) { - flds[idx] = ccu_pll_flds[idx]; - flds[idx].pll = pll; - - debugfs_create_file_unsafe(flds[idx].name, ccu_pll_dbgfs_mode, - dentry, &flds[idx], - &ccu_pll_dbgfs_fld_fops); - } -} - -#else /* !CONFIG_DEBUG_FS */ - -#define ccu_pll_debug_init NULL - -#endif /* !CONFIG_DEBUG_FS */ - -static const struct clk_ops ccu_pll_gate_to_set_ops = { - .enable = ccu_pll_enable, - .disable = ccu_pll_disable, - .is_enabled = ccu_pll_is_enabled, - .recalc_rate = ccu_pll_recalc_rate, - .determine_rate = ccu_pll_determine_rate, - .set_rate = ccu_pll_set_rate_norst, - .debug_init = ccu_pll_debug_init -}; - -static const struct clk_ops ccu_pll_straight_set_ops = { - .enable = ccu_pll_enable, - .disable = ccu_pll_disable, - .is_enabled = ccu_pll_is_enabled, - .recalc_rate = ccu_pll_recalc_rate, - .determine_rate = ccu_pll_determine_rate, - .set_rate = ccu_pll_set_rate_reset, - .debug_init = ccu_pll_debug_init -}; - -struct ccu_pll *ccu_pll_hw_register(const struct ccu_pll_init_data *pll_init) -{ - struct clk_parent_data parent_data = { }; - struct clk_init_data hw_init = { }; - struct ccu_pll *pll; - int ret; - - if (!pll_init) - return ERR_PTR(-EINVAL); - - pll = kzalloc_obj(*pll); - if (!pll) - return ERR_PTR(-ENOMEM); - - /* - * Note since Baikal-T1 System Controller registers are MMIO-backed - * we won't check the regmap IO operations return status, because it - * must be zero anyway. - */ - pll->hw.init = &hw_init; - pll->reg_ctl = pll_init->base + CCU_PLL_CTL; - pll->reg_ctl1 = pll_init->base + CCU_PLL_CTL1; - pll->sys_regs = pll_init->sys_regs; - pll->id = pll_init->id; - spin_lock_init(&pll->lock); - - hw_init.name = pll_init->name; - hw_init.flags = pll_init->flags; - - if (hw_init.flags & CLK_SET_RATE_GATE) - hw_init.ops = &ccu_pll_gate_to_set_ops; - else - hw_init.ops = &ccu_pll_straight_set_ops; - - if (!pll_init->parent_name) { - ret = -EINVAL; - goto err_free_pll; - } - parent_data.fw_name = pll_init->parent_name; - hw_init.parent_data = &parent_data; - hw_init.num_parents = 1; - - ret = of_clk_hw_register(pll_init->np, &pll->hw); - if (ret) - goto err_free_pll; - - return pll; - -err_free_pll: - kfree(pll); - - return ERR_PTR(ret); -} - -void ccu_pll_hw_unregister(struct ccu_pll *pll) -{ - clk_hw_unregister(&pll->hw); - - kfree(pll); -} diff --git a/drivers/clk/baikal-t1/ccu-pll.h b/drivers/clk/baikal-t1/ccu-pll.h deleted file mode 100644 index a71bfd7b90ec..000000000000 --- a/drivers/clk/baikal-t1/ccu-pll.h +++ /dev/null @@ -1,72 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC - * - * Baikal-T1 CCU PLL interface driver - */ -#ifndef __CLK_BT1_CCU_PLL_H__ -#define __CLK_BT1_CCU_PLL_H__ - -#include -#include -#include -#include -#include - -/* - * CCU PLL private flags - * @CCU_PLL_BASIC: Basic PLL required by the kernel as early as possible. - */ -#define CCU_PLL_BASIC BIT(0) - -/* - * struct ccu_pll_init_data - CCU PLL initialization data - * @id: Clock private identifier. - * @name: Clocks name. - * @parent_name: Clocks parent name in a fw node. - * @base: PLL registers base address with respect to the sys_regs base. - * @sys_regs: Baikal-T1 System Controller registers map. - * @np: Pointer to the node describing the CCU PLLs. - * @flags: PLL clock flags. - * @features: PLL private features. - */ -struct ccu_pll_init_data { - unsigned int id; - const char *name; - const char *parent_name; - unsigned int base; - struct regmap *sys_regs; - struct device_node *np; - unsigned long flags; - unsigned long features; -}; - -/* - * struct ccu_pll - CCU PLL descriptor - * @hw: clk_hw of the PLL. - * @id: Clock private identifier. - * @reg_ctl: PLL control register base. - * @reg_ctl1: PLL control1 register base. - * @sys_regs: Baikal-T1 System Controller registers map. - * @lock: PLL state change spin-lock. - */ -struct ccu_pll { - struct clk_hw hw; - unsigned int id; - unsigned int reg_ctl; - unsigned int reg_ctl1; - struct regmap *sys_regs; - spinlock_t lock; -}; -#define to_ccu_pll(_hw) container_of(_hw, struct ccu_pll, hw) - -static inline struct clk_hw *ccu_pll_get_clk_hw(struct ccu_pll *pll) -{ - return pll ? &pll->hw : NULL; -} - -struct ccu_pll *ccu_pll_hw_register(const struct ccu_pll_init_data *init); - -void ccu_pll_hw_unregister(struct ccu_pll *pll); - -#endif /* __CLK_BT1_CCU_PLL_H__ */ diff --git a/drivers/clk/baikal-t1/ccu-rst.c b/drivers/clk/baikal-t1/ccu-rst.c deleted file mode 100644 index 969e5de381a8..000000000000 --- a/drivers/clk/baikal-t1/ccu-rst.c +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC - * - * Authors: - * Serge Semin - * - * Baikal-T1 CCU Resets interface driver - */ - -#define pr_fmt(fmt) "bt1-ccu-rst: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "ccu-rst.h" - -#define CCU_AXI_MAIN_BASE 0x030 -#define CCU_AXI_DDR_BASE 0x034 -#define CCU_AXI_SATA_BASE 0x038 -#define CCU_AXI_GMAC0_BASE 0x03C -#define CCU_AXI_GMAC1_BASE 0x040 -#define CCU_AXI_XGMAC_BASE 0x044 -#define CCU_AXI_PCIE_M_BASE 0x048 -#define CCU_AXI_PCIE_S_BASE 0x04C -#define CCU_AXI_USB_BASE 0x050 -#define CCU_AXI_HWA_BASE 0x054 -#define CCU_AXI_SRAM_BASE 0x058 - -#define CCU_SYS_DDR_BASE 0x02c -#define CCU_SYS_SATA_REF_BASE 0x060 -#define CCU_SYS_APB_BASE 0x064 -#define CCU_SYS_PCIE_BASE 0x144 - -#define CCU_RST_DELAY_US 1 - -#define CCU_RST_TRIG(_base, _ofs) \ - { \ - .type = CCU_RST_TRIG, \ - .base = _base, \ - .mask = BIT(_ofs), \ - } - -#define CCU_RST_DIR(_base, _ofs) \ - { \ - .type = CCU_RST_DIR, \ - .base = _base, \ - .mask = BIT(_ofs), \ - } - -struct ccu_rst_info { - enum ccu_rst_type type; - unsigned int base; - unsigned int mask; -}; - -/* - * Each AXI-bus clock divider is equipped with the corresponding clock-consumer - * domain reset (it's self-deasserted reset control). - */ -static const struct ccu_rst_info axi_rst_info[] = { - [CCU_AXI_MAIN_RST] = CCU_RST_TRIG(CCU_AXI_MAIN_BASE, 1), - [CCU_AXI_DDR_RST] = CCU_RST_TRIG(CCU_AXI_DDR_BASE, 1), - [CCU_AXI_SATA_RST] = CCU_RST_TRIG(CCU_AXI_SATA_BASE, 1), - [CCU_AXI_GMAC0_RST] = CCU_RST_TRIG(CCU_AXI_GMAC0_BASE, 1), - [CCU_AXI_GMAC1_RST] = CCU_RST_TRIG(CCU_AXI_GMAC1_BASE, 1), - [CCU_AXI_XGMAC_RST] = CCU_RST_TRIG(CCU_AXI_XGMAC_BASE, 1), - [CCU_AXI_PCIE_M_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_M_BASE, 1), - [CCU_AXI_PCIE_S_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_S_BASE, 1), - [CCU_AXI_USB_RST] = CCU_RST_TRIG(CCU_AXI_USB_BASE, 1), - [CCU_AXI_HWA_RST] = CCU_RST_TRIG(CCU_AXI_HWA_BASE, 1), - [CCU_AXI_SRAM_RST] = CCU_RST_TRIG(CCU_AXI_SRAM_BASE, 1), -}; - -/* - * SATA reference clock domain and APB-bus domain are connected with the - * sefl-deasserted reset control, which can be activated via the corresponding - * clock divider register. DDR and PCIe sub-domains can be reset with directly - * controlled reset signals. Resetting the DDR controller though won't end up - * well while the Linux kernel is working. - */ -static const struct ccu_rst_info sys_rst_info[] = { - [CCU_SYS_SATA_REF_RST] = CCU_RST_TRIG(CCU_SYS_SATA_REF_BASE, 1), - [CCU_SYS_APB_RST] = CCU_RST_TRIG(CCU_SYS_APB_BASE, 1), - [CCU_SYS_DDR_FULL_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 1), - [CCU_SYS_DDR_INIT_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 2), - [CCU_SYS_PCIE_PCS_PHY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 0), - [CCU_SYS_PCIE_PIPE0_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 4), - [CCU_SYS_PCIE_CORE_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 8), - [CCU_SYS_PCIE_PWR_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 9), - [CCU_SYS_PCIE_STICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 10), - [CCU_SYS_PCIE_NSTICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 11), - [CCU_SYS_PCIE_HOT_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 12), -}; - -static int ccu_rst_reset(struct reset_controller_dev *rcdev, unsigned long idx) -{ - struct ccu_rst *rst = to_ccu_rst(rcdev); - const struct ccu_rst_info *info = &rst->rsts_info[idx]; - - if (info->type != CCU_RST_TRIG) - return -EOPNOTSUPP; - - regmap_update_bits(rst->sys_regs, info->base, info->mask, info->mask); - - /* The next delay must be enough to cover all the resets. */ - udelay(CCU_RST_DELAY_US); - - return 0; -} - -static int ccu_rst_set(struct reset_controller_dev *rcdev, - unsigned long idx, bool high) -{ - struct ccu_rst *rst = to_ccu_rst(rcdev); - const struct ccu_rst_info *info = &rst->rsts_info[idx]; - - if (info->type != CCU_RST_DIR) - return high ? -EOPNOTSUPP : 0; - - return regmap_update_bits(rst->sys_regs, info->base, - info->mask, high ? info->mask : 0); -} - -static int ccu_rst_assert(struct reset_controller_dev *rcdev, - unsigned long idx) -{ - return ccu_rst_set(rcdev, idx, true); -} - -static int ccu_rst_deassert(struct reset_controller_dev *rcdev, - unsigned long idx) -{ - return ccu_rst_set(rcdev, idx, false); -} - -static int ccu_rst_status(struct reset_controller_dev *rcdev, - unsigned long idx) -{ - struct ccu_rst *rst = to_ccu_rst(rcdev); - const struct ccu_rst_info *info = &rst->rsts_info[idx]; - u32 val; - - if (info->type != CCU_RST_DIR) - return -EOPNOTSUPP; - - regmap_read(rst->sys_regs, info->base, &val); - - return !!(val & info->mask); -} - -static const struct reset_control_ops ccu_rst_ops = { - .reset = ccu_rst_reset, - .assert = ccu_rst_assert, - .deassert = ccu_rst_deassert, - .status = ccu_rst_status, -}; - -struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *rst_init) -{ - struct ccu_rst *rst; - int ret; - - if (!rst_init) - return ERR_PTR(-EINVAL); - - rst = kzalloc_obj(*rst); - if (!rst) - return ERR_PTR(-ENOMEM); - - rst->sys_regs = rst_init->sys_regs; - if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-axi")) { - rst->rcdev.nr_resets = ARRAY_SIZE(axi_rst_info); - rst->rsts_info = axi_rst_info; - } else if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-sys")) { - rst->rcdev.nr_resets = ARRAY_SIZE(sys_rst_info); - rst->rsts_info = sys_rst_info; - } else { - pr_err("Incompatible DT node '%s' specified\n", - of_node_full_name(rst_init->np)); - ret = -EINVAL; - goto err_kfree_rst; - } - - rst->rcdev.owner = THIS_MODULE; - rst->rcdev.ops = &ccu_rst_ops; - rst->rcdev.of_node = rst_init->np; - - ret = reset_controller_register(&rst->rcdev); - if (ret) { - pr_err("Couldn't register '%s' reset controller\n", - of_node_full_name(rst_init->np)); - goto err_kfree_rst; - } - - return rst; - -err_kfree_rst: - kfree(rst); - - return ERR_PTR(ret); -} - -void ccu_rst_hw_unregister(struct ccu_rst *rst) -{ - reset_controller_unregister(&rst->rcdev); - - kfree(rst); -} diff --git a/drivers/clk/baikal-t1/ccu-rst.h b/drivers/clk/baikal-t1/ccu-rst.h deleted file mode 100644 index d6e8b2f671f4..000000000000 --- a/drivers/clk/baikal-t1/ccu-rst.h +++ /dev/null @@ -1,67 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC - * - * Baikal-T1 CCU Resets interface driver - */ -#ifndef __CLK_BT1_CCU_RST_H__ -#define __CLK_BT1_CCU_RST_H__ - -#include -#include -#include - -struct ccu_rst_info; - -/* - * enum ccu_rst_type - CCU Reset types - * @CCU_RST_TRIG: Self-deasserted reset signal. - * @CCU_RST_DIR: Directly controlled reset signal. - */ -enum ccu_rst_type { - CCU_RST_TRIG, - CCU_RST_DIR, -}; - -/* - * struct ccu_rst_init_data - CCU Resets initialization data - * @sys_regs: Baikal-T1 System Controller registers map. - * @np: Pointer to the node with the System CCU block. - */ -struct ccu_rst_init_data { - struct regmap *sys_regs; - struct device_node *np; -}; - -/* - * struct ccu_rst - CCU Reset descriptor - * @rcdev: Reset controller descriptor. - * @sys_regs: Baikal-T1 System Controller registers map. - * @rsts_info: Reset flag info (base address and mask). - */ -struct ccu_rst { - struct reset_controller_dev rcdev; - struct regmap *sys_regs; - const struct ccu_rst_info *rsts_info; -}; -#define to_ccu_rst(_rcdev) container_of(_rcdev, struct ccu_rst, rcdev) - -#ifdef CONFIG_CLK_BT1_CCU_RST - -struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *init); - -void ccu_rst_hw_unregister(struct ccu_rst *rst); - -#else - -static inline -struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *init) -{ - return NULL; -} - -static inline void ccu_rst_hw_unregister(struct ccu_rst *rst) {} - -#endif - -#endif /* __CLK_BT1_CCU_RST_H__ */ diff --git a/drivers/clk/baikal-t1/clk-ccu-div.c b/drivers/clk/baikal-t1/clk-ccu-div.c deleted file mode 100644 index d32072e4dd49..000000000000 --- a/drivers/clk/baikal-t1/clk-ccu-div.c +++ /dev/null @@ -1,520 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC - * - * Authors: - * Serge Semin - * Dmitry Dunaev - * - * Baikal-T1 CCU Dividers clock driver - */ - -#define pr_fmt(fmt) "bt1-ccu-div: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "ccu-div.h" -#include "ccu-rst.h" - -#define CCU_AXI_MAIN_BASE 0x030 -#define CCU_AXI_DDR_BASE 0x034 -#define CCU_AXI_SATA_BASE 0x038 -#define CCU_AXI_GMAC0_BASE 0x03C -#define CCU_AXI_GMAC1_BASE 0x040 -#define CCU_AXI_XGMAC_BASE 0x044 -#define CCU_AXI_PCIE_M_BASE 0x048 -#define CCU_AXI_PCIE_S_BASE 0x04C -#define CCU_AXI_USB_BASE 0x050 -#define CCU_AXI_HWA_BASE 0x054 -#define CCU_AXI_SRAM_BASE 0x058 - -#define CCU_SYS_SATA_REF_BASE 0x060 -#define CCU_SYS_APB_BASE 0x064 -#define CCU_SYS_GMAC0_BASE 0x068 -#define CCU_SYS_GMAC1_BASE 0x06C -#define CCU_SYS_XGMAC_BASE 0x070 -#define CCU_SYS_USB_BASE 0x074 -#define CCU_SYS_PVT_BASE 0x078 -#define CCU_SYS_HWA_BASE 0x07C -#define CCU_SYS_UART_BASE 0x084 -#define CCU_SYS_TIMER0_BASE 0x088 -#define CCU_SYS_TIMER1_BASE 0x08C -#define CCU_SYS_TIMER2_BASE 0x090 -#define CCU_SYS_WDT_BASE 0x150 - -#define CCU_DIV_VAR_INFO(_id, _name, _pname, _base, _width, _flags, _features) \ - { \ - .id = _id, \ - .name = _name, \ - .parent_name = _pname, \ - .base = _base, \ - .type = CCU_DIV_VAR, \ - .width = _width, \ - .flags = _flags, \ - .features = _features \ - } - -#define CCU_DIV_GATE_INFO(_id, _name, _pname, _base, _divider) \ - { \ - .id = _id, \ - .name = _name, \ - .parent_name = _pname, \ - .base = _base, \ - .type = CCU_DIV_GATE, \ - .divider = _divider \ - } - -#define CCU_DIV_BUF_INFO(_id, _name, _pname, _base, _flags) \ - { \ - .id = _id, \ - .name = _name, \ - .parent_name = _pname, \ - .base = _base, \ - .type = CCU_DIV_BUF, \ - .flags = _flags \ - } - -#define CCU_DIV_FIXED_INFO(_id, _name, _pname, _divider) \ - { \ - .id = _id, \ - .name = _name, \ - .parent_name = _pname, \ - .type = CCU_DIV_FIXED, \ - .divider = _divider \ - } - -struct ccu_div_info { - unsigned int id; - const char *name; - const char *parent_name; - unsigned int base; - enum ccu_div_type type; - union { - unsigned int width; - unsigned int divider; - }; - unsigned long flags; - unsigned long features; -}; - -struct ccu_div_data { - struct device_node *np; - struct regmap *sys_regs; - - unsigned int divs_num; - const struct ccu_div_info *divs_info; - struct ccu_div **divs; - - struct ccu_rst *rsts; -}; - -/* - * AXI Main Interconnect (axi_main_clk) and DDR AXI-bus (axi_ddr_clk) clocks - * must be left enabled in any case, since former one is responsible for - * clocking a bus between CPU cores and the rest of the SoC components, while - * the later is clocking the AXI-bus between DDR controller and the Main - * Interconnect. So should any of these clocks get to be disabled, the system - * will literally stop working. That's why we marked them as critical. - */ -static const struct ccu_div_info axi_info[] = { - CCU_DIV_VAR_INFO(CCU_AXI_MAIN_CLK, "axi_main_clk", "pcie_clk", - CCU_AXI_MAIN_BASE, 4, - CLK_IS_CRITICAL, CCU_DIV_RESET_DOMAIN), - CCU_DIV_VAR_INFO(CCU_AXI_DDR_CLK, "axi_ddr_clk", "sata_clk", - CCU_AXI_DDR_BASE, 4, - CLK_IS_CRITICAL | CLK_SET_RATE_GATE, - CCU_DIV_RESET_DOMAIN), - CCU_DIV_VAR_INFO(CCU_AXI_SATA_CLK, "axi_sata_clk", "sata_clk", - CCU_AXI_SATA_BASE, 4, - CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), - CCU_DIV_VAR_INFO(CCU_AXI_GMAC0_CLK, "axi_gmac0_clk", "eth_clk", - CCU_AXI_GMAC0_BASE, 4, - CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), - CCU_DIV_VAR_INFO(CCU_AXI_GMAC1_CLK, "axi_gmac1_clk", "eth_clk", - CCU_AXI_GMAC1_BASE, 4, - CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), - CCU_DIV_VAR_INFO(CCU_AXI_XGMAC_CLK, "axi_xgmac_clk", "eth_clk", - CCU_AXI_XGMAC_BASE, 4, - CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), - CCU_DIV_VAR_INFO(CCU_AXI_PCIE_M_CLK, "axi_pcie_m_clk", "pcie_clk", - CCU_AXI_PCIE_M_BASE, 4, - CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), - CCU_DIV_VAR_INFO(CCU_AXI_PCIE_S_CLK, "axi_pcie_s_clk", "pcie_clk", - CCU_AXI_PCIE_S_BASE, 4, - CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), - CCU_DIV_VAR_INFO(CCU_AXI_USB_CLK, "axi_usb_clk", "sata_clk", - CCU_AXI_USB_BASE, 4, - CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), - CCU_DIV_VAR_INFO(CCU_AXI_HWA_CLK, "axi_hwa_clk", "sata_clk", - CCU_AXI_HWA_BASE, 4, - CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), - CCU_DIV_VAR_INFO(CCU_AXI_SRAM_CLK, "axi_sram_clk", "eth_clk", - CCU_AXI_SRAM_BASE, 4, - CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN) -}; - -/* - * APB-bus clock is marked as critical since it's a main communication bus - * for the SoC devices registers IO-operations. - */ -static const struct ccu_div_info sys_info[] = { - CCU_DIV_VAR_INFO(CCU_SYS_SATA_CLK, "sys_sata_clk", - "sata_clk", CCU_SYS_SATA_REF_BASE, 4, - CLK_SET_RATE_GATE, - CCU_DIV_SKIP_ONE | CCU_DIV_LOCK_SHIFTED | - CCU_DIV_RESET_DOMAIN), - CCU_DIV_BUF_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk", - "sys_sata_clk", CCU_SYS_SATA_REF_BASE, - CLK_SET_RATE_PARENT), - CCU_DIV_VAR_INFO(CCU_SYS_APB_CLK, "sys_apb_clk", - "pcie_clk", CCU_SYS_APB_BASE, 5, - CLK_IS_CRITICAL, CCU_DIV_BASIC | CCU_DIV_RESET_DOMAIN), - CCU_DIV_GATE_INFO(CCU_SYS_GMAC0_TX_CLK, "sys_gmac0_tx_clk", - "eth_clk", CCU_SYS_GMAC0_BASE, 5), - CCU_DIV_FIXED_INFO(CCU_SYS_GMAC0_PTP_CLK, "sys_gmac0_ptp_clk", - "eth_clk", 10), - CCU_DIV_GATE_INFO(CCU_SYS_GMAC1_TX_CLK, "sys_gmac1_tx_clk", - "eth_clk", CCU_SYS_GMAC1_BASE, 5), - CCU_DIV_FIXED_INFO(CCU_SYS_GMAC1_PTP_CLK, "sys_gmac1_ptp_clk", - "eth_clk", 10), - CCU_DIV_GATE_INFO(CCU_SYS_XGMAC_CLK, "sys_xgmac_clk", - "eth_clk", CCU_SYS_XGMAC_BASE, 1), - CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_REF_CLK, "sys_xgmac_ref_clk", - "sys_xgmac_clk", 8), - CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_PTP_CLK, "sys_xgmac_ptp_clk", - "sys_xgmac_clk", 8), - CCU_DIV_GATE_INFO(CCU_SYS_USB_CLK, "sys_usb_clk", - "eth_clk", CCU_SYS_USB_BASE, 10), - CCU_DIV_VAR_INFO(CCU_SYS_PVT_CLK, "sys_pvt_clk", - "ref_clk", CCU_SYS_PVT_BASE, 5, - CLK_SET_RATE_GATE, 0), - CCU_DIV_VAR_INFO(CCU_SYS_HWA_CLK, "sys_hwa_clk", - "sata_clk", CCU_SYS_HWA_BASE, 4, - CLK_SET_RATE_GATE, 0), - CCU_DIV_VAR_INFO(CCU_SYS_UART_CLK, "sys_uart_clk", - "eth_clk", CCU_SYS_UART_BASE, 17, - CLK_SET_RATE_GATE, 0), - CCU_DIV_FIXED_INFO(CCU_SYS_I2C1_CLK, "sys_i2c1_clk", - "eth_clk", 10), - CCU_DIV_FIXED_INFO(CCU_SYS_I2C2_CLK, "sys_i2c2_clk", - "eth_clk", 10), - CCU_DIV_FIXED_INFO(CCU_SYS_GPIO_CLK, "sys_gpio_clk", - "ref_clk", 25), - CCU_DIV_VAR_INFO(CCU_SYS_TIMER0_CLK, "sys_timer0_clk", - "ref_clk", CCU_SYS_TIMER0_BASE, 17, - CLK_SET_RATE_GATE, CCU_DIV_BASIC), - CCU_DIV_VAR_INFO(CCU_SYS_TIMER1_CLK, "sys_timer1_clk", - "ref_clk", CCU_SYS_TIMER1_BASE, 17, - CLK_SET_RATE_GATE, CCU_DIV_BASIC), - CCU_DIV_VAR_INFO(CCU_SYS_TIMER2_CLK, "sys_timer2_clk", - "ref_clk", CCU_SYS_TIMER2_BASE, 17, - CLK_SET_RATE_GATE, CCU_DIV_BASIC), - CCU_DIV_VAR_INFO(CCU_SYS_WDT_CLK, "sys_wdt_clk", - "eth_clk", CCU_SYS_WDT_BASE, 17, - CLK_SET_RATE_GATE, CCU_DIV_SKIP_ONE_TO_THREE) -}; - -static struct ccu_div_data *axi_data; -static struct ccu_div_data *sys_data; - -static void ccu_div_set_data(struct ccu_div_data *data) -{ - struct device_node *np = data->np; - - if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) - axi_data = data; - else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) - sys_data = data; - else - pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np)); -} - -static struct ccu_div_data *ccu_div_get_data(struct device_node *np) -{ - if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) - return axi_data; - else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) - return sys_data; - - pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np)); - - return NULL; -} - -static struct ccu_div *ccu_div_find_desc(struct ccu_div_data *data, - unsigned int clk_id) -{ - int idx; - - for (idx = 0; idx < data->divs_num; ++idx) { - if (data->divs_info[idx].id == clk_id) - return data->divs[idx]; - } - - return ERR_PTR(-EINVAL); -} - -static struct ccu_div_data *ccu_div_create_data(struct device_node *np) -{ - struct ccu_div_data *data; - int ret; - - data = kzalloc_obj(*data); - if (!data) - return ERR_PTR(-ENOMEM); - - data->np = np; - if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) { - data->divs_num = ARRAY_SIZE(axi_info); - data->divs_info = axi_info; - } else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) { - data->divs_num = ARRAY_SIZE(sys_info); - data->divs_info = sys_info; - } else { - pr_err("Incompatible DT node '%s' specified\n", - of_node_full_name(np)); - ret = -EINVAL; - goto err_kfree_data; - } - - data->divs = kzalloc_objs(*data->divs, data->divs_num); - if (!data->divs) { - ret = -ENOMEM; - goto err_kfree_data; - } - - return data; - -err_kfree_data: - kfree(data); - - return ERR_PTR(ret); -} - -static void ccu_div_free_data(struct ccu_div_data *data) -{ - kfree(data->divs); - - kfree(data); -} - -static int ccu_div_find_sys_regs(struct ccu_div_data *data) -{ - data->sys_regs = syscon_node_to_regmap(data->np->parent); - if (IS_ERR(data->sys_regs)) { - pr_err("Failed to find syscon regs for '%s'\n", - of_node_full_name(data->np)); - return PTR_ERR(data->sys_regs); - } - - return 0; -} - -static struct clk_hw *ccu_div_of_clk_hw_get(struct of_phandle_args *clkspec, - void *priv) -{ - struct ccu_div_data *data = priv; - struct ccu_div *div; - unsigned int clk_id; - - clk_id = clkspec->args[0]; - div = ccu_div_find_desc(data, clk_id); - if (IS_ERR(div)) { - if (div != ERR_PTR(-EPROBE_DEFER)) - pr_info("Invalid clock ID %d specified\n", clk_id); - - return ERR_CAST(div); - } - - return ccu_div_get_clk_hw(div); -} - -static int ccu_div_clk_register(struct ccu_div_data *data, bool defer) -{ - int idx, ret; - - for (idx = 0; idx < data->divs_num; ++idx) { - const struct ccu_div_info *info = &data->divs_info[idx]; - struct ccu_div_init_data init = {0}; - - if (!!(info->features & CCU_DIV_BASIC) ^ defer) { - if (!data->divs[idx]) - data->divs[idx] = ERR_PTR(-EPROBE_DEFER); - - continue; - } - - init.id = info->id; - init.name = info->name; - init.parent_name = info->parent_name; - init.np = data->np; - init.type = info->type; - init.flags = info->flags; - init.features = info->features; - - if (init.type == CCU_DIV_VAR) { - init.base = info->base; - init.sys_regs = data->sys_regs; - init.width = info->width; - } else if (init.type == CCU_DIV_GATE) { - init.base = info->base; - init.sys_regs = data->sys_regs; - init.divider = info->divider; - } else if (init.type == CCU_DIV_BUF) { - init.base = info->base; - init.sys_regs = data->sys_regs; - } else { - init.divider = info->divider; - } - - data->divs[idx] = ccu_div_hw_register(&init); - if (IS_ERR(data->divs[idx])) { - ret = PTR_ERR(data->divs[idx]); - pr_err("Couldn't register divider '%s' hw\n", - init.name); - goto err_hw_unregister; - } - } - - return 0; - -err_hw_unregister: - for (--idx; idx >= 0; --idx) { - if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer) - continue; - - ccu_div_hw_unregister(data->divs[idx]); - } - - return ret; -} - -static void ccu_div_clk_unregister(struct ccu_div_data *data, bool defer) -{ - int idx; - - /* Uninstall only the clocks registered on the specified stage */ - for (idx = 0; idx < data->divs_num; ++idx) { - if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer) - continue; - - ccu_div_hw_unregister(data->divs[idx]); - } -} - -static int ccu_div_of_register(struct ccu_div_data *data) -{ - int ret; - - ret = of_clk_add_hw_provider(data->np, ccu_div_of_clk_hw_get, data); - if (ret) { - pr_err("Couldn't register dividers '%s' clock provider\n", - of_node_full_name(data->np)); - } - - return ret; -} - -static int ccu_div_rst_register(struct ccu_div_data *data) -{ - struct ccu_rst_init_data init = {0}; - - init.sys_regs = data->sys_regs; - init.np = data->np; - - data->rsts = ccu_rst_hw_register(&init); - if (IS_ERR(data->rsts)) { - pr_err("Couldn't register divider '%s' reset controller\n", - of_node_full_name(data->np)); - return PTR_ERR(data->rsts); - } - - return 0; -} - -static int ccu_div_probe(struct platform_device *pdev) -{ - struct ccu_div_data *data; - int ret; - - data = ccu_div_get_data(dev_of_node(&pdev->dev)); - if (!data) - return -EINVAL; - - ret = ccu_div_clk_register(data, false); - if (ret) - return ret; - - ret = ccu_div_rst_register(data); - if (ret) - goto err_clk_unregister; - - return 0; - -err_clk_unregister: - ccu_div_clk_unregister(data, false); - - return ret; -} - -static const struct of_device_id ccu_div_of_match[] = { - { .compatible = "baikal,bt1-ccu-axi" }, - { .compatible = "baikal,bt1-ccu-sys" }, - { } -}; - -static struct platform_driver ccu_div_driver = { - .probe = ccu_div_probe, - .driver = { - .name = "clk-ccu-div", - .of_match_table = ccu_div_of_match, - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver(ccu_div_driver); - -static __init void ccu_div_init(struct device_node *np) -{ - struct ccu_div_data *data; - int ret; - - data = ccu_div_create_data(np); - if (IS_ERR(data)) - return; - - ret = ccu_div_find_sys_regs(data); - if (ret) - goto err_free_data; - - ret = ccu_div_clk_register(data, true); - if (ret) - goto err_free_data; - - ret = ccu_div_of_register(data); - if (ret) - goto err_clk_unregister; - - ccu_div_set_data(data); - - return; - -err_clk_unregister: - ccu_div_clk_unregister(data, true); - -err_free_data: - ccu_div_free_data(data); -} -CLK_OF_DECLARE_DRIVER(ccu_axi, "baikal,bt1-ccu-axi", ccu_div_init); -CLK_OF_DECLARE_DRIVER(ccu_sys, "baikal,bt1-ccu-sys", ccu_div_init); diff --git a/drivers/clk/baikal-t1/clk-ccu-pll.c b/drivers/clk/baikal-t1/clk-ccu-pll.c deleted file mode 100644 index e5e4a6ea6f78..000000000000 --- a/drivers/clk/baikal-t1/clk-ccu-pll.c +++ /dev/null @@ -1,277 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC - * - * Authors: - * Serge Semin - * Dmitry Dunaev - * - * Baikal-T1 CCU PLL clocks driver - */ - -#define pr_fmt(fmt) "bt1-ccu-pll: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "ccu-pll.h" - -#define CCU_CPU_PLL_BASE 0x000 -#define CCU_SATA_PLL_BASE 0x008 -#define CCU_DDR_PLL_BASE 0x010 -#define CCU_PCIE_PLL_BASE 0x018 -#define CCU_ETH_PLL_BASE 0x020 - -#define CCU_PLL_INFO(_id, _name, _pname, _base, _flags, _features) \ - { \ - .id = _id, \ - .name = _name, \ - .parent_name = _pname, \ - .base = _base, \ - .flags = _flags, \ - .features = _features, \ - } - -#define CCU_PLL_NUM ARRAY_SIZE(pll_info) - -struct ccu_pll_info { - unsigned int id; - const char *name; - const char *parent_name; - unsigned int base; - unsigned long flags; - unsigned long features; -}; - -/* - * Alas we have to mark all PLLs as critical. CPU and DDR PLLs are sources of - * CPU cores and DDR controller reference clocks, due to which they obviously - * shouldn't be ever gated. SATA and PCIe PLLs are the parents of APB-bus and - * DDR controller AXI-bus clocks. If they are gated the system will be - * unusable. Moreover disabling SATA and Ethernet PLLs causes automatic reset - * of the corresponding subsystems. So until we aren't ready to re-initialize - * all the devices consuming those PLLs, they will be marked as critical too. - */ -static const struct ccu_pll_info pll_info[] = { - CCU_PLL_INFO(CCU_CPU_PLL, "cpu_pll", "ref_clk", CCU_CPU_PLL_BASE, - CLK_IS_CRITICAL, CCU_PLL_BASIC), - CCU_PLL_INFO(CCU_SATA_PLL, "sata_pll", "ref_clk", CCU_SATA_PLL_BASE, - CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0), - CCU_PLL_INFO(CCU_DDR_PLL, "ddr_pll", "ref_clk", CCU_DDR_PLL_BASE, - CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0), - CCU_PLL_INFO(CCU_PCIE_PLL, "pcie_pll", "ref_clk", CCU_PCIE_PLL_BASE, - CLK_IS_CRITICAL, CCU_PLL_BASIC), - CCU_PLL_INFO(CCU_ETH_PLL, "eth_pll", "ref_clk", CCU_ETH_PLL_BASE, - CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0) -}; - -struct ccu_pll_data { - struct device_node *np; - struct regmap *sys_regs; - struct ccu_pll *plls[CCU_PLL_NUM]; -}; - -static struct ccu_pll_data *pll_data; - -static struct ccu_pll *ccu_pll_find_desc(struct ccu_pll_data *data, - unsigned int clk_id) -{ - int idx; - - for (idx = 0; idx < CCU_PLL_NUM; ++idx) { - if (pll_info[idx].id == clk_id) - return data->plls[idx]; - } - - return ERR_PTR(-EINVAL); -} - -static struct ccu_pll_data *ccu_pll_create_data(struct device_node *np) -{ - struct ccu_pll_data *data; - - data = kzalloc_obj(*data); - if (!data) - return ERR_PTR(-ENOMEM); - - data->np = np; - - return data; -} - -static void ccu_pll_free_data(struct ccu_pll_data *data) -{ - kfree(data); -} - -static int ccu_pll_find_sys_regs(struct ccu_pll_data *data) -{ - data->sys_regs = syscon_node_to_regmap(data->np->parent); - if (IS_ERR(data->sys_regs)) { - pr_err("Failed to find syscon regs for '%s'\n", - of_node_full_name(data->np)); - return PTR_ERR(data->sys_regs); - } - - return 0; -} - -static struct clk_hw *ccu_pll_of_clk_hw_get(struct of_phandle_args *clkspec, - void *priv) -{ - struct ccu_pll_data *data = priv; - struct ccu_pll *pll; - unsigned int clk_id; - - clk_id = clkspec->args[0]; - pll = ccu_pll_find_desc(data, clk_id); - if (IS_ERR(pll)) { - if (pll != ERR_PTR(-EPROBE_DEFER)) - pr_info("Invalid PLL clock ID %d specified\n", clk_id); - - return ERR_CAST(pll); - } - - return ccu_pll_get_clk_hw(pll); -} - -static int ccu_pll_clk_register(struct ccu_pll_data *data, bool defer) -{ - int idx, ret; - - for (idx = 0; idx < CCU_PLL_NUM; ++idx) { - const struct ccu_pll_info *info = &pll_info[idx]; - struct ccu_pll_init_data init = {0}; - - /* Defer non-basic PLLs allocation for the probe stage */ - if (!!(info->features & CCU_PLL_BASIC) ^ defer) { - if (!data->plls[idx]) - data->plls[idx] = ERR_PTR(-EPROBE_DEFER); - - continue; - } - - init.id = info->id; - init.name = info->name; - init.parent_name = info->parent_name; - init.base = info->base; - init.sys_regs = data->sys_regs; - init.np = data->np; - init.flags = info->flags; - init.features = info->features; - - data->plls[idx] = ccu_pll_hw_register(&init); - if (IS_ERR(data->plls[idx])) { - ret = PTR_ERR(data->plls[idx]); - pr_err("Couldn't register PLL hw '%s'\n", - init.name); - goto err_hw_unregister; - } - } - - return 0; - -err_hw_unregister: - for (--idx; idx >= 0; --idx) { - if (!!(pll_info[idx].features & CCU_PLL_BASIC) ^ defer) - continue; - - ccu_pll_hw_unregister(data->plls[idx]); - } - - return ret; -} - -static void ccu_pll_clk_unregister(struct ccu_pll_data *data, bool defer) -{ - int idx; - - /* Uninstall only the clocks registered on the specified stage */ - for (idx = 0; idx < CCU_PLL_NUM; ++idx) { - if (!!(pll_info[idx].features & CCU_PLL_BASIC) ^ defer) - continue; - - ccu_pll_hw_unregister(data->plls[idx]); - } -} - -static int ccu_pll_of_register(struct ccu_pll_data *data) -{ - int ret; - - ret = of_clk_add_hw_provider(data->np, ccu_pll_of_clk_hw_get, data); - if (ret) { - pr_err("Couldn't register PLL provider of '%s'\n", - of_node_full_name(data->np)); - } - - return ret; -} - -static int ccu_pll_probe(struct platform_device *pdev) -{ - struct ccu_pll_data *data = pll_data; - - if (!data) - return -EINVAL; - - return ccu_pll_clk_register(data, false); -} - -static const struct of_device_id ccu_pll_of_match[] = { - { .compatible = "baikal,bt1-ccu-pll" }, - { } -}; - -static struct platform_driver ccu_pll_driver = { - .probe = ccu_pll_probe, - .driver = { - .name = "clk-ccu-pll", - .of_match_table = ccu_pll_of_match, - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver(ccu_pll_driver); - -static __init void ccu_pll_init(struct device_node *np) -{ - struct ccu_pll_data *data; - int ret; - - data = ccu_pll_create_data(np); - if (IS_ERR(data)) - return; - - ret = ccu_pll_find_sys_regs(data); - if (ret) - goto err_free_data; - - ret = ccu_pll_clk_register(data, true); - if (ret) - goto err_free_data; - - ret = ccu_pll_of_register(data); - if (ret) - goto err_clk_unregister; - - pll_data = data; - - return; - -err_clk_unregister: - ccu_pll_clk_unregister(data, true); - -err_free_data: - ccu_pll_free_data(data); -} -CLK_OF_DECLARE_DRIVER(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init); diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 1a9162f0ae31..f1a99de6de4f 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -160,6 +160,13 @@ raspberrypi_clk_variants[RPI_FIRMWARE_NUM_CLK_ID] = { [RPI_FIRMWARE_VEC_CLK_ID] = { .export = true, .minimize = true, + + /* + * If this clock is disabled during boot, it causes a bus + * lockup in RPi 3B. Therefore, make sure it's left enabled + * during boot. + */ + .flags = CLK_IGNORE_UNUSED, }, [RPI_FIRMWARE_DISP_CLK_ID] = { .export = true, @@ -289,16 +296,31 @@ static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw, static int raspberrypi_fw_prepare(struct clk_hw *hw) { const struct raspberrypi_clk_data *data = clk_hw_to_data(hw); + struct raspberrypi_clk_variant *variant = data->variant; struct raspberrypi_clk *rpi = data->rpi; u32 state = RPI_FIRMWARE_STATE_ENABLE_BIT; int ret; ret = raspberrypi_clock_property(rpi->firmware, data, RPI_FIRMWARE_SET_CLOCK_STATE, &state); - if (ret) + if (ret) { dev_err_ratelimited(rpi->dev, "Failed to set clock %s state to on: %d\n", clk_hw_get_name(hw), ret); + return ret; + } + + /* + * For clocks marked with 'maximize', restore the rate to the + * maximum after enabling. This compensates for the rate being + * set to minimum during unprepare (see raspberrypi_fw_unprepare). + */ + if (variant->maximize) { + unsigned long min_rate, max_rate; + + clk_hw_get_rate_range(hw, &min_rate, &max_rate); + ret = raspberrypi_fw_set_rate(hw, max_rate, 0); + } return ret; } @@ -307,9 +329,27 @@ static void raspberrypi_fw_unprepare(struct clk_hw *hw) { const struct raspberrypi_clk_data *data = clk_hw_to_data(hw); struct raspberrypi_clk *rpi = data->rpi; + unsigned long min_rate, max_rate; u32 state = 0; int ret; + clk_hw_get_rate_range(hw, &min_rate, &max_rate); + + /* + * Setting the rate in unprepare is a deviation from the usual CCF + * behavior, where unprepare only gates the clock. However, this is + * needed, as RPI_FIRMWARE_SET_CLOCK_STATE doesn't actually power off + * the clock on current firmware versions. Setting the rate to minimum + * before disabling the clock is the only way to achieve meaningful + * power savings. + * + * This is safe because no consumer should rely on the rate of an + * unprepared clock. Any consumer must call clk_prepare() before use, + * at which point the rate is either restored to maximum (for clocks + * with the 'maximize' flag) or re-established by the consumer. + */ + raspberrypi_fw_set_rate(hw, min_rate, 0); + ret = raspberrypi_clock_property(rpi->firmware, data, RPI_FIRMWARE_SET_CLOCK_STATE, &state); if (ret) @@ -387,9 +427,6 @@ static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi, } } - if (variant->maximize) - variant->min_rate = max_rate; - if (variant->min_rate) { unsigned long rate; diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 44d010bccfb1..835b1e4e5869 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -47,22 +47,10 @@ static int clk_composite_determine_rate_for_parent(struct clk_hw *rate_hw, struct clk_hw *parent_hw, const struct clk_ops *rate_ops) { - long rate; - req->best_parent_hw = parent_hw; req->best_parent_rate = clk_hw_get_rate(parent_hw); - if (rate_ops->determine_rate) - return rate_ops->determine_rate(rate_hw, req); - - rate = rate_ops->round_rate(rate_hw, req->rate, - &req->best_parent_rate); - if (rate < 0) - return rate; - - req->rate = rate; - - return 0; + return rate_ops->determine_rate(rate_hw, req); } static int clk_composite_determine_rate(struct clk_hw *hw, @@ -79,8 +67,7 @@ static int clk_composite_determine_rate(struct clk_hw *hw, unsigned long best_rate = 0; int i, ret; - if (rate_hw && rate_ops && - (rate_ops->determine_rate || rate_ops->round_rate) && + if (rate_hw && rate_ops && rate_ops->determine_rate && mux_hw && mux_ops && mux_ops->set_parent) { req->best_parent_hw = NULL; @@ -150,18 +137,6 @@ static int clk_composite_determine_rate(struct clk_hw *hw, } } -static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) -{ - struct clk_composite *composite = to_clk_composite(hw); - const struct clk_ops *rate_ops = composite->rate_ops; - struct clk_hw *rate_hw = composite->rate_hw; - - __clk_hw_set_clk(rate_hw, hw); - - return rate_ops->round_rate(rate_hw, rate, prate); -} - static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -288,17 +263,14 @@ static struct clk_hw *__clk_hw_register_composite(struct device *dev, if (rate_ops->determine_rate) clk_composite_ops->determine_rate = clk_composite_determine_rate; - else if (rate_ops->round_rate) - clk_composite_ops->round_rate = - clk_composite_round_rate; - /* .set_rate requires either .round_rate or .determine_rate */ + /* .set_rate requires .determine_rate */ if (rate_ops->set_rate) { - if (rate_ops->determine_rate || rate_ops->round_rate) + if (rate_ops->determine_rate) clk_composite_ops->set_rate = clk_composite_set_rate; else - WARN(1, "%s: missing round_rate op is required\n", + WARN(1, "%s: missing determine_rate op is required\n", __func__); } diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 45e7ebde4a8b..b3b485d23ea8 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -387,50 +387,6 @@ int divider_ro_determine_rate(struct clk_hw *hw, struct clk_rate_request *req, } EXPORT_SYMBOL_GPL(divider_ro_determine_rate); -long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent, - unsigned long rate, unsigned long *prate, - const struct clk_div_table *table, - u8 width, unsigned long flags) -{ - struct clk_rate_request req; - int ret; - - clk_hw_init_rate_request(hw, &req, rate); - req.best_parent_rate = *prate; - req.best_parent_hw = parent; - - ret = divider_determine_rate(hw, &req, table, width, flags); - if (ret) - return ret; - - *prate = req.best_parent_rate; - - return req.rate; -} -EXPORT_SYMBOL_GPL(divider_round_rate_parent); - -long divider_ro_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent, - unsigned long rate, unsigned long *prate, - const struct clk_div_table *table, u8 width, - unsigned long flags, unsigned int val) -{ - struct clk_rate_request req; - int ret; - - clk_hw_init_rate_request(hw, &req, rate); - req.best_parent_rate = *prate; - req.best_parent_hw = parent; - - ret = divider_ro_determine_rate(hw, &req, table, width, flags, val); - if (ret) - return ret; - - *prate = req.best_parent_rate; - - return req.rate; -} -EXPORT_SYMBOL_GPL(divider_ro_round_rate_parent); - static int clk_divider_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c index 08cc8e5acf43..1ab0e2eca5d3 100644 --- a/drivers/clk/clk-en7523.c +++ b/drivers/clk/clk-en7523.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include #include @@ -11,6 +12,8 @@ #include #include #include +#include +#include #define RST_NR_PER_BANK 32 @@ -33,15 +36,50 @@ #define REG_RESET_CONTROL_PCIEHB BIT(29) #define REG_RESET_CONTROL_PCIE1 BIT(27) #define REG_RESET_CONTROL_PCIE2 BIT(26) +#define REG_HIR 0x064 +#define REG_HIR_MASK GENMASK(31, 16) /* EN7581 */ #define REG_NP_SCU_PCIC 0x88 #define REG_NP_SCU_SSTR 0x9c #define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13) #define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11) #define REG_CRYPTO_CLKSRC2 0x20c +/* EN751221 */ +#define EN751221_REG_SPI_DIV 0x0cc +#define EN751221_REG_SPI_DIV_MASK GENMASK(31, 8) +#define EN751221_SPI_BASE 500000000 +#define EN751221_SPI_BASE_EN7526C 400000000 +#define EN751221_SPI_DIV_DEFAULT 40 +#define EN751221_REG_BUS 0x284 +#define EN751221_REG_BUS_MASK GENMASK(21, 12) +#define EN751221_REG_SSR3 0x094 +#define EN751221_REG_SSR3_GSW_MASK GENMASK(9, 8) #define REG_RST_CTRL2 0x830 #define REG_RST_CTRL1 0x834 +#define EN751221_REG_RST_DMT 0x84 +#define EN751221_REG_RST_USB 0xec + +#define EN751221_MAX_CLKS 5 + +enum en_hir { + HIR_UNKNOWN = -1, + HIR_TC3169 = 0, + HIR_TC3182 = 1, + HIR_RT65168 = 2, + HIR_RT63165 = 3, + HIR_RT63365 = 4, + HIR_MT751020 = 5, + HIR_MT7505 = 6, + HIR_EN751221 = 7, + HIR_EN7526C = 8, + HIR_EN751627 = 9, + HIR_EN7580 = 10, + HIR_EN7528 = 11, + HIR_EN7523 = 12, + HIR_EN7581 = 13, + HIR_MAX = 14, +}; struct en_clk_desc { int id; @@ -93,6 +131,8 @@ static const u32 bus7581_base[] = { 600000000, 540000000 }; static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 }; static const u32 crypto_base[] = { 540000000, 480000000 }; static const u32 emmc7581_base[] = { 200000000, 150000000 }; +/* EN751221 */ +static const u32 gsw751221_base[] = { 500000000, 250000000, 400000000, 200000000 }; static const struct en_clk_desc en7523_base_clks[] = { { @@ -300,6 +340,13 @@ static const u16 en7581_rst_ofs[] = { REG_RST_CTRL1, }; +static const u16 en751221_rst_ofs[] = { + REG_RST_CTRL2, + REG_RST_CTRL1, + EN751221_REG_RST_DMT, + EN751221_REG_RST_USB, +}; + static const u16 en7523_rst_map[] = { /* RST_CTRL2 */ [EN7523_XPON_PHY_RST] = 0, @@ -405,8 +452,61 @@ static const u16 en7581_rst_map[] = { [EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31, }; +static const u16 en751221_rst_map[] = { + /* RST_CTRL2 */ + [EN751221_XPON_PHY_RST] = 0, + [EN751221_GFAST_RST] = 1, + [EN751221_CPU_TIMER2_RST] = 2, + [EN751221_UART3_RST] = 3, + [EN751221_UART4_RST] = 4, + [EN751221_UART5_RST] = 5, + [EN751221_I2C2_RST] = 6, + [EN751221_XSI_MAC_RST] = 7, + [EN751221_XSI_PHY_RST] = 8, + + /* RST_CTRL1 */ + [EN751221_PCM1_ZSI_ISI_RST] = RST_NR_PER_BANK + 0, + [EN751221_FE_QDMA1_RST] = RST_NR_PER_BANK + 1, + [EN751221_FE_QDMA2_RST] = RST_NR_PER_BANK + 2, + [EN751221_FE_UNZIP_RST] = RST_NR_PER_BANK + 3, + [EN751221_PCM2_RST] = RST_NR_PER_BANK + 4, + [EN751221_PTM_MAC_RST] = RST_NR_PER_BANK + 5, + [EN751221_CRYPTO_RST] = RST_NR_PER_BANK + 6, + [EN751221_SAR_RST] = RST_NR_PER_BANK + 7, + [EN751221_TIMER_RST] = RST_NR_PER_BANK + 8, + [EN751221_INTC_RST] = RST_NR_PER_BANK + 9, + [EN751221_BONDING_RST] = RST_NR_PER_BANK + 10, + [EN751221_PCM1_RST] = RST_NR_PER_BANK + 11, + [EN751221_UART_RST] = RST_NR_PER_BANK + 12, + [EN751221_GPIO_RST] = RST_NR_PER_BANK + 13, + [EN751221_GDMA_RST] = RST_NR_PER_BANK + 14, + [EN751221_I2C_MASTER_RST] = RST_NR_PER_BANK + 16, + [EN751221_PCM2_ZSI_ISI_RST] = RST_NR_PER_BANK + 17, + [EN751221_SFC_RST] = RST_NR_PER_BANK + 18, + [EN751221_UART2_RST] = RST_NR_PER_BANK + 19, + [EN751221_GDMP_RST] = RST_NR_PER_BANK + 20, + [EN751221_FE_RST] = RST_NR_PER_BANK + 21, + [EN751221_USB_HOST_P0_RST] = RST_NR_PER_BANK + 22, + [EN751221_GSW_RST] = RST_NR_PER_BANK + 23, + [EN751221_SFC2_PCM_RST] = RST_NR_PER_BANK + 25, + [EN751221_PCIE0_RST] = RST_NR_PER_BANK + 26, + [EN751221_PCIE1_RST] = RST_NR_PER_BANK + 27, + [EN751221_CPU_TIMER_RST] = RST_NR_PER_BANK + 28, + [EN751221_PCIE_HB_RST] = RST_NR_PER_BANK + 29, + [EN751221_SIMIF_RST] = RST_NR_PER_BANK + 30, + [EN751221_XPON_MAC_RST] = RST_NR_PER_BANK + 31, + + /* RST_DMT */ + [EN751221_DMT_RST] = 2 * RST_NR_PER_BANK + 0, + + /* RST_USB */ + [EN751221_USB_PHY_P0_RST] = 3 * RST_NR_PER_BANK + 6, + [EN751221_USB_PHY_P1_RST] = 3 * RST_NR_PER_BANK + 7, +}; + static int en7581_reset_register(struct device *dev, void __iomem *base, - const u16 *rst_map, int nr_resets); + const u16 *rst_map, int nr_resets, + const u16 *rst_reg_ofs); static u32 en7523_get_base_rate(const struct en_clk_desc *desc, u32 val) { @@ -604,7 +704,8 @@ static int en7523_clk_hw_init(struct platform_device *pdev, en7523_register_clocks(&pdev->dev, clk_data, base, np_base); return en7581_reset_register(&pdev->dev, np_base, en7523_rst_map, - ARRAY_SIZE(en7523_rst_map)); + ARRAY_SIZE(en7523_rst_map), + en7581_rst_ofs); } static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, @@ -705,7 +806,8 @@ static const struct reset_control_ops en7581_reset_ops = { }; static int en7581_reset_register(struct device *dev, void __iomem *base, - const u16 *rst_map, int nr_resets) + const u16 *rst_map, int nr_resets, + const u16 *rst_reg_ofs) { struct en_rst_data *rst_data; @@ -713,7 +815,7 @@ static int en7581_reset_register(struct device *dev, void __iomem *base, if (!rst_data) return -ENOMEM; - rst_data->bank_ofs = en7581_rst_ofs; + rst_data->bank_ofs = rst_reg_ofs; rst_data->idx_map = rst_map; rst_data->base = base; @@ -752,7 +854,107 @@ static int en7581_clk_hw_init(struct platform_device *pdev, writel(val | 3, base + REG_NP_SCU_PCIC); return en7581_reset_register(&pdev->dev, base, en7581_rst_map, - ARRAY_SIZE(en7581_rst_map)); + ARRAY_SIZE(en7581_rst_map), + en7581_rst_ofs); +} + +static enum en_hir get_hw_id(void __iomem *np_base) +{ + u32 val = FIELD_GET(REG_HIR_MASK, readl(np_base + REG_HIR)); + + if (val < HIR_MAX) + return (enum en_hir)val; + + pr_warn("Unable to determine EcoNet SoC\n"); + + return HIR_UNKNOWN; +} + +static void en751221_try_register_clk(struct device *dev, int key, + struct clk_hw_onecell_data *clk_data, + const char *name, u32 rate) +{ + struct clk_hw *hw; + + if (WARN_ON_ONCE(key >= EN751221_MAX_CLKS)) + return; + + hw = clk_hw_register_fixed_rate(dev, name, NULL, 0, rate); + if (IS_ERR(hw)) + pr_err("Failed to register clk %s: %pe\n", name, hw); + else + clk_data->hws[key] = hw; +} + +static void en751221_register_clocks(struct device *dev, + struct clk_hw_onecell_data *clk_data, + struct regmap *map, void __iomem *np_base) +{ + enum en_hir hid = get_hw_id(np_base); + struct clk_hw *hw; + u32 rate; + u32 div; + int err; + + /* PCI */ + hw = en7523_register_pcie_clk(dev, np_base); + clk_data->hws[EN751221_CLK_PCIE] = hw; + + /* SPI */ + rate = EN751221_SPI_BASE; + if (hid == HIR_EN7526C) + rate = EN751221_SPI_BASE_EN7526C; + + err = regmap_read(map, EN751221_REG_SPI_DIV, &div); + if (err) { + pr_err("Failed reading fixed clk div %s: %d\n", + "spi", err); + } else { + div = FIELD_GET(EN751221_REG_SPI_DIV_MASK, div) * 2; + if (!div) + div = EN751221_SPI_DIV_DEFAULT; + + en751221_try_register_clk(dev, EN751221_CLK_SPI, clk_data, + "spi", rate / div); + } + + /* BUS */ + rate = FIELD_GET(EN751221_REG_BUS_MASK, + readl(np_base + EN751221_REG_BUS)); + rate *= 1000000; + en751221_try_register_clk(dev, EN751221_CLK_BUS, clk_data, "bus", + rate); + + /* CPU */ + en751221_try_register_clk(dev, EN751221_CLK_CPU, clk_data, "cpu", + rate * 4); + + /* GSW */ + rate = FIELD_GET(EN751221_REG_SSR3_GSW_MASK, + readl(np_base + EN751221_REG_SSR3)); + en751221_try_register_clk(dev, EN751221_CLK_GSW, clk_data, "gsw", + gsw751221_base[rate]); +} + +static int en751221_clk_hw_init(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data) +{ + struct regmap *map; + void __iomem *base; + + map = syscon_regmap_lookup_by_compatible("econet,en751221-chip-scu"); + if (IS_ERR(map)) + return PTR_ERR(map); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + en751221_register_clocks(&pdev->dev, clk_data, map, base); + + return en7581_reset_register(&pdev->dev, base, en751221_rst_map, + ARRAY_SIZE(en751221_rst_map), + en751221_rst_ofs); } static int en7523_clk_probe(struct platform_device *pdev) @@ -799,9 +1001,20 @@ static const struct en_clk_soc_data en7581_data = { .hw_init = en7581_clk_hw_init, }; +static const struct en_clk_soc_data en751221_data = { + .num_clocks = EN751221_MAX_CLKS, + .pcie_ops = { + .is_enabled = en7523_pci_is_enabled, + .prepare = en7523_pci_prepare, + .unprepare = en7523_pci_unprepare, + }, + .hw_init = en751221_clk_hw_init, +}; + static const struct of_device_id of_match_clk_en7523[] = { { .compatible = "airoha,en7523-scu", .data = &en7523_data }, { .compatible = "airoha,en7581-scu", .data = &en7581_data }, + { .compatible = "econet,en751221-scu", .data = &en751221_data }, { /* sentinel */ } }; diff --git a/drivers/clk/clk-eyeq.c b/drivers/clk/clk-eyeq.c index 81137269352d..d9303c2c7aa5 100644 --- a/drivers/clk/clk-eyeq.c +++ b/drivers/clk/clk-eyeq.c @@ -45,6 +45,7 @@ #include #include +#include /* In frac mode, it enables fractional noise canceling DAC. Else, no function. */ #define PCSR0_DAC_EN BIT(0) @@ -109,6 +110,7 @@ struct eqc_match_data { const char *reset_auxdev_name; const char *pinctrl_auxdev_name; + const char *eth_phy_auxdev_name; unsigned int early_clk_count; }; @@ -163,7 +165,7 @@ static void eqc_pll_downshift_factors(unsigned long *mult, unsigned long *div) static int eqc_pll_parse_registers(u32 r0, u32 r1, unsigned long *mult, unsigned long *div, unsigned long *acc) { - u32 spread; + unsigned long spread; if (r0 & PCSR0_BYPASS) { *mult = 1; @@ -177,8 +179,6 @@ static int eqc_pll_parse_registers(u32 r0, u32 r1, unsigned long *mult, *mult = FIELD_GET(PCSR0_INTIN, r0); *div = FIELD_GET(PCSR0_REF_DIV, r0); - if (r0 & PCSR0_FOUTPOSTDIV_EN) - *div *= FIELD_GET(PCSR0_POST_DIV1, r0) * FIELD_GET(PCSR0_POST_DIV2, r0); /* Fractional mode, in 2^20 (0x100000) parts. */ if (r0 & PCSR0_DSM_EN) { @@ -197,23 +197,23 @@ static int eqc_pll_parse_registers(u32 r0, u32 r1, unsigned long *mult, /* * Spread spectrum. * - * Spread is 1/1000 parts of frequency, accuracy is half of - * that. To get accuracy, convert to ppb (parts per billion). + * Spread is in 1/1024 parts of frequency. Clock accuracy + * is half the spread value expressed in parts per billion. * - * acc = spread * 1e6 / 2 - * with acc in parts per billion and, - * spread in parts per thousand. + * accuracy = (spread * 1e9) / (1024 * 2) + * + * Care is taken to avoid overflowing or losing precision. */ spread = FIELD_GET(PCSR1_SPREAD, r1); - *acc = spread * 500000; + *acc = DIV_ROUND_CLOSEST(spread * 1000000000, 1024 * 2); if (r1 & PCSR1_DOWN_SPREAD) { /* * Downspreading: the central frequency is half a * spread lower. */ - *mult *= 2000 - spread; - *div *= 2000; + *mult *= 2048 - spread; + *div *= 2048; /* * Previous operation might overflow 32 bits. If it @@ -322,38 +322,18 @@ static void eqc_probe_init_fixed_factors(struct device *dev, } } -static void eqc_auxdev_release(struct device *dev) -{ - struct auxiliary_device *adev = to_auxiliary_dev(dev); - - kfree(adev); -} - -static int eqc_auxdev_create(struct device *dev, void __iomem *base, - const char *name, u32 id) +static void eqc_auxdev_create_optional(struct device *dev, void __iomem *base, + const char *name) { struct auxiliary_device *adev; - int ret; - adev = kzalloc_obj(*adev); - if (!adev) - return -ENOMEM; - - adev->name = name; - adev->dev.parent = dev; - adev->dev.platform_data = (void __force *)base; - adev->dev.release = eqc_auxdev_release; - adev->id = id; - - ret = auxiliary_device_init(adev); - if (ret) - return ret; - - ret = auxiliary_device_add(adev); - if (ret) - auxiliary_device_uninit(adev); - - return ret; + if (name) { + adev = devm_auxiliary_device_create(dev, name, + (void __force *)base); + if (!adev) + dev_warn(dev, "failed creating auxiliary device %s.%s\n", + KBUILD_MODNAME, name); + } } static int eqc_probe(struct platform_device *pdev) @@ -365,7 +345,6 @@ static int eqc_probe(struct platform_device *pdev) unsigned int i, clk_count; struct resource *res; void __iomem *base; - int ret; data = device_get_match_data(dev); if (!data) @@ -379,21 +358,10 @@ static int eqc_probe(struct platform_device *pdev) if (!base) return -ENOMEM; - /* Init optional reset auxiliary device. */ - if (data->reset_auxdev_name) { - ret = eqc_auxdev_create(dev, base, data->reset_auxdev_name, 0); - if (ret) - dev_warn(dev, "failed creating auxiliary device %s.%s: %d\n", - KBUILD_MODNAME, data->reset_auxdev_name, ret); - } - - /* Init optional pinctrl auxiliary device. */ - if (data->pinctrl_auxdev_name) { - ret = eqc_auxdev_create(dev, base, data->pinctrl_auxdev_name, 0); - if (ret) - dev_warn(dev, "failed creating auxiliary device %s.%s: %d\n", - KBUILD_MODNAME, data->pinctrl_auxdev_name, ret); - } + /* Init optional auxiliary devices. */ + eqc_auxdev_create_optional(dev, base, data->reset_auxdev_name); + eqc_auxdev_create_optional(dev, base, data->pinctrl_auxdev_name); + eqc_auxdev_create_optional(dev, base, data->eth_phy_auxdev_name); if (data->pll_count + data->div_count + data->fixed_factor_count == 0) return 0; /* Zero clocks, we are done. */ @@ -554,6 +522,7 @@ static const struct eqc_match_data eqc_eyeq5_match_data = { .reset_auxdev_name = "reset", .pinctrl_auxdev_name = "pinctrl", + .eth_phy_auxdev_name = "phy", .early_clk_count = ARRAY_SIZE(eqc_eyeq5_early_plls) + ARRAY_SIZE(eqc_eyeq5_early_fixed_factors), @@ -573,6 +542,68 @@ static const struct eqc_match_data eqc_eyeq6l_match_data = { .reset_auxdev_name = "reset", }; +static const struct eqc_pll eqc_eyeq6lplus_early_plls[] = { + { .index = EQ6LPC_PLL_CPU, .name = "pll-cpu", .reg64 = 0x058 }, +}; + +static const struct eqc_pll eqc_eyeq6lplus_plls[] = { + { .index = EQ6LPC_PLL_DDR, .name = "pll-ddr", .reg64 = 0x02C }, + { .index = EQ6LPC_PLL_ACC, .name = "pll-acc", .reg64 = 0x034 }, + { .index = EQ6LPC_PLL_PER, .name = "pll-per", .reg64 = 0x03C }, + { .index = EQ6LPC_PLL_VDI, .name = "pll-vdi", .reg64 = 0x044 }, +}; + +static const struct eqc_fixed_factor eqc_eyeq6lplus_early_fixed_factors[] = { + { EQ6LPC_CPU_OCC, "occ-cpu", 1, 1, EQ6LPC_PLL_CPU }, +}; + +static const struct eqc_fixed_factor eqc_eyeq6lplus_fixed_factors[] = { + { EQ6LPC_DDR_OCC, "occ-ddr", 1, 1, EQ6LPC_PLL_DDR }, + + { EQ6LPC_ACC_VDI, "vdi-div", 1, 10, EQ6LPC_PLL_ACC }, + { EQ6LPC_ACC_OCC, "occ-acc", 1, 1, EQ6LPC_PLL_ACC }, + { EQ6LPC_ACC_FCMU, "fcmu-a-clk", 1, 10, EQ6LPC_ACC_OCC }, + + { EQ6LPC_PER_OCC, "occ-per", 1, 1, EQ6LPC_PLL_PER }, + { EQ6LPC_PER_I2C_SER, "i2c-ser-clk", 1, 10, EQ6LPC_PER_OCC }, + { EQ6LPC_PER_PCLK, "pclk", 1, 4, EQ6LPC_PER_OCC }, + { EQ6LPC_PER_TSU, "tsu-clk", 1, 8, EQ6LPC_PER_OCC }, + { EQ6LPC_PER_OSPI, "ospi-ref-clk", 1, 10, EQ6LPC_PER_OCC }, + { EQ6LPC_PER_GPIO, "gpio-clk", 1, 4, EQ6LPC_PER_OCC }, + { EQ6LPC_PER_TIMER, "timer-clk", 1, 4, EQ6LPC_PER_OCC }, + { EQ6LPC_PER_I2C, "i2c-clk", 1, 4, EQ6LPC_PER_OCC }, + { EQ6LPC_PER_UART, "uart-clk", 1, 4, EQ6LPC_PER_OCC }, + { EQ6LPC_PER_SPI, "spi-clk", 1, 4, EQ6LPC_PER_OCC }, + { EQ6LPC_PER_PERIPH, "periph-clk", 1, 1, EQ6LPC_PER_OCC }, + + { EQ6LPC_VDI_OCC, "occ-vdi", 1, 1, EQ6LPC_PLL_VDI }, +}; + +static const struct eqc_early_match_data eqc_eyeq6lplus_early_match_data __initconst = { + .early_pll_count = ARRAY_SIZE(eqc_eyeq6lplus_early_plls), + .early_plls = eqc_eyeq6lplus_early_plls, + + .early_fixed_factor_count = ARRAY_SIZE(eqc_eyeq6lplus_early_fixed_factors), + .early_fixed_factors = eqc_eyeq6lplus_early_fixed_factors, + + .late_clk_count = ARRAY_SIZE(eqc_eyeq6lplus_plls) + + ARRAY_SIZE(eqc_eyeq6lplus_fixed_factors), +}; + +static const struct eqc_match_data eqc_eyeq6lplus_match_data = { + .pll_count = ARRAY_SIZE(eqc_eyeq6lplus_plls), + .plls = eqc_eyeq6lplus_plls, + + .fixed_factor_count = ARRAY_SIZE(eqc_eyeq6lplus_fixed_factors), + .fixed_factors = eqc_eyeq6lplus_fixed_factors, + + .reset_auxdev_name = "reset", + .pinctrl_auxdev_name = "pinctrl", + + .early_clk_count = ARRAY_SIZE(eqc_eyeq6lplus_early_plls) + + ARRAY_SIZE(eqc_eyeq6lplus_early_fixed_factors), +}; + static const struct eqc_match_data eqc_eyeq6h_west_match_data = { .reset_auxdev_name = "reset_west", }; @@ -674,6 +705,7 @@ static const struct eqc_match_data eqc_eyeq6h_acc_match_data = { static const struct of_device_id eqc_match_table[] = { { .compatible = "mobileye,eyeq5-olb", .data = &eqc_eyeq5_match_data }, { .compatible = "mobileye,eyeq6l-olb", .data = &eqc_eyeq6l_match_data }, + { .compatible = "mobileye,eyeq6lplus-olb", .data = &eqc_eyeq6lplus_match_data }, { .compatible = "mobileye,eyeq6h-west-olb", .data = &eqc_eyeq6h_west_match_data }, { .compatible = "mobileye,eyeq6h-east-olb", .data = &eqc_eyeq6h_east_match_data }, { .compatible = "mobileye,eyeq6h-south-olb", .data = &eqc_eyeq6h_south_match_data }, @@ -857,3 +889,9 @@ static void __init eqc_eyeq6h_west_early_init(struct device_node *np) } CLK_OF_DECLARE_DRIVER(eqc_eyeq6h_west, "mobileye,eyeq6h-west-olb", eqc_eyeq6h_west_early_init); + +static void __init eqc_eyeq6lplus_early_init(struct device_node *np) +{ + eqc_early_init(np, &eqc_eyeq6lplus_early_match_data); +} +CLK_OF_DECLARE_DRIVER(eqc_eyeq6lplus, "mobileye,eyeq6lplus-olb", eqc_eyeq6lplus_early_init); diff --git a/drivers/clk/clk-fsl-sai.c b/drivers/clk/clk-fsl-sai.c index cba45e07562d..eded6a5fac21 100644 --- a/drivers/clk/clk-fsl-sai.c +++ b/drivers/clk/clk-fsl-sai.c @@ -5,34 +5,110 @@ * Copyright 2020 Michael Walle */ -#include -#include #include +#include #include +#include #include #include +#include #include #define I2S_CSR 0x00 #define I2S_CR2 0x08 +#define I2S_MCR 0x100 #define CSR_BCE_BIT 28 +#define CSR_TE_BIT 31 #define CR2_BCD BIT(24) #define CR2_DIV_SHIFT 0 #define CR2_DIV_WIDTH 8 +#define MCR_MOE BIT(30) + +struct fsl_sai_data { + unsigned int offset; /* Register offset */ + bool have_mclk; /* Have MCLK control */ +}; struct fsl_sai_clk { - struct clk_divider div; - struct clk_gate gate; + const struct fsl_sai_data *data; + struct clk_divider bclk_div; + struct clk_divider mclk_div; + struct clk_gate bclk_gate; + struct clk_gate mclk_gate; + struct clk_hw *bclk_hw; + struct clk_hw *mclk_hw; spinlock_t lock; }; +static struct clk_hw * +fsl_sai_of_clk_get(struct of_phandle_args *clkspec, void *data) +{ + struct fsl_sai_clk *sai_clk = data; + + if (clkspec->args_count == 0) + return sai_clk->bclk_hw; + + if (clkspec->args_count == 1) { + if (clkspec->args[0] == 0) + return sai_clk->bclk_hw; + if (sai_clk->data->have_mclk && clkspec->args[0] == 1) + return sai_clk->mclk_hw; + } + + return ERR_PTR(-EINVAL); +} + +static int fsl_sai_clk_register(struct device *dev, void __iomem *base, + spinlock_t *lock, struct clk_divider *div, + struct clk_gate *gate, struct clk_hw **hw, + const int gate_bit, const int dir_bit, + const int div_reg, char *name) +{ + const struct fsl_sai_data *data = device_get_match_data(dev); + struct clk_parent_data pdata = { .index = 0 }; + struct clk_hw *chw; + char *cname; + + gate->reg = base + data->offset + I2S_CSR; + gate->bit_idx = gate_bit; + gate->lock = lock; + + div->reg = base + div_reg; + div->shift = CR2_DIV_SHIFT; + div->width = CR2_DIV_WIDTH; + div->lock = lock; + + cname = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", + of_node_full_name(dev->of_node), name); + if (!cname) + return -ENOMEM; + + /* Set clock direction */ + writel(dir_bit, base + div_reg); + + chw = devm_clk_hw_register_composite_pdata(dev, cname, + &pdata, 1, NULL, NULL, + &div->hw, + &clk_divider_ops, + &gate->hw, + &clk_gate_ops, + CLK_SET_RATE_GATE); + if (IS_ERR(chw)) + return PTR_ERR(chw); + + *hw = chw; + + return 0; +} + static int fsl_sai_clk_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + const struct fsl_sai_data *data = device_get_match_data(dev); struct fsl_sai_clk *sai_clk; - struct clk_parent_data pdata = { .index = 0 }; + struct clk *clk_bus; void __iomem *base; - struct clk_hw *hw; + int ret; sai_clk = devm_kzalloc(dev, sizeof(*sai_clk), GFP_KERNEL); if (!sai_clk) @@ -42,35 +118,47 @@ static int fsl_sai_clk_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); + clk_bus = devm_clk_get_optional_enabled(dev, "bus"); + if (IS_ERR(clk_bus)) + return PTR_ERR(clk_bus); + + sai_clk->data = data; spin_lock_init(&sai_clk->lock); - sai_clk->gate.reg = base + I2S_CSR; - sai_clk->gate.bit_idx = CSR_BCE_BIT; - sai_clk->gate.lock = &sai_clk->lock; + ret = fsl_sai_clk_register(dev, base, &sai_clk->lock, + &sai_clk->bclk_div, &sai_clk->bclk_gate, + &sai_clk->bclk_hw, CSR_BCE_BIT, CR2_BCD, + data->offset + I2S_CR2, "BCLK"); + if (ret) + return ret; - sai_clk->div.reg = base + I2S_CR2; - sai_clk->div.shift = CR2_DIV_SHIFT; - sai_clk->div.width = CR2_DIV_WIDTH; - sai_clk->div.lock = &sai_clk->lock; + if (data->have_mclk) { + ret = fsl_sai_clk_register(dev, base, &sai_clk->lock, + &sai_clk->mclk_div, + &sai_clk->mclk_gate, + &sai_clk->mclk_hw, + CSR_TE_BIT, MCR_MOE, I2S_MCR, + "MCLK"); + if (ret) + return ret; + } - /* set clock direction, we are the BCLK master */ - writel(CR2_BCD, base + I2S_CR2); - - hw = devm_clk_hw_register_composite_pdata(dev, dev->of_node->name, - &pdata, 1, NULL, NULL, - &sai_clk->div.hw, - &clk_divider_ops, - &sai_clk->gate.hw, - &clk_gate_ops, - CLK_SET_RATE_GATE); - if (IS_ERR(hw)) - return PTR_ERR(hw); - - return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); + return devm_of_clk_add_hw_provider(dev, fsl_sai_of_clk_get, sai_clk); } +static const struct fsl_sai_data fsl_sai_vf610_data = { + .offset = 0, + .have_mclk = false, +}; + +static const struct fsl_sai_data fsl_sai_imx8mq_data = { + .offset = 8, + .have_mclk = true, +}; + static const struct of_device_id of_fsl_sai_clk_ids[] = { - { .compatible = "fsl,vf610-sai-clock" }, + { .compatible = "fsl,vf610-sai-clock", .data = &fsl_sai_vf610_data }, + { .compatible = "fsl,imx8mq-sai-clock", .data = &fsl_sai_imx8mq_data }, { } }; MODULE_DEVICE_TABLE(of, of_fsl_sai_clk_ids); diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index f05631e55310..2524c5c0eb46 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -907,13 +907,11 @@ static const struct clockgen_pll_div *get_pll_div(struct clockgen *cg, return &cg->pll[pll].div[div]; } -static struct clk * __init create_mux_common(struct clockgen *cg, - struct mux_hwclock *hwc, - const struct clk_ops *ops, - unsigned long min_rate, - unsigned long max_rate, - unsigned long pct80_rate, - const char *fmt, int idx) +static struct clk * __init __printf(7, 8) +create_mux_common(struct clockgen *cg, struct mux_hwclock *hwc, + const struct clk_ops *ops, unsigned long min_rate, + unsigned long max_rate, unsigned long pct80_rate, + const char *fmt, ...) { struct clk_init_data init = {}; struct clk *clk; @@ -921,8 +919,11 @@ static struct clk * __init create_mux_common(struct clockgen *cg, const char *parent_names[NUM_MUX_PARENTS]; char name[32]; int i, j; + va_list args; - snprintf(name, sizeof(name), fmt, idx); + va_start(args, fmt); + vsnprintf(name, sizeof(name), fmt, args); + va_end(args); for (i = 0, j = 0; i < NUM_MUX_PARENTS; i++) { unsigned long rate; diff --git a/drivers/clk/clk-rk808.c b/drivers/clk/clk-rk808.c index f7412b137e5e..5a75b5c91555 100644 --- a/drivers/clk/clk-rk808.c +++ b/drivers/clk/clk-rk808.c @@ -153,7 +153,7 @@ static int rk808_clkout_probe(struct platform_device *pdev) struct rk808_clkout *rk808_clkout; int ret; - dev->of_node = pdev->dev.parent->of_node; + device_set_of_node_from_dev(dev, dev->parent); rk808_clkout = devm_kzalloc(dev, sizeof(*rk808_clkout), GFP_KERNEL); diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index ba3b1057e4f0..abb6c8fcdc91 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -188,6 +188,8 @@ static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_ty of_clk_add_provider(np, of_clk_src_simple_get, clk); clk_register_clkdev(clk, clk_name, NULL); pr_debug("Add %s clock PLL\n", clk_name); + } else { + iounmap(reg); } } diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 47093cda9df3..048adfa86a5d 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1560,8 +1560,6 @@ late_initcall_sync(clk_disable_unused); static int clk_core_determine_round_nolock(struct clk_core *core, struct clk_rate_request *req) { - long rate; - lockdep_assert_held(&prepare_lock); if (!core) @@ -1591,13 +1589,6 @@ static int clk_core_determine_round_nolock(struct clk_core *core, req->rate = core->rate; } else if (core->ops->determine_rate) { return core->ops->determine_rate(core->hw, req); - } else if (core->ops->round_rate) { - rate = core->ops->round_rate(core->hw, req->rate, - &req->best_parent_rate); - if (rate < 0) - return rate; - - req->rate = rate; } else { return -EINVAL; } @@ -1682,7 +1673,7 @@ EXPORT_SYMBOL_GPL(clk_hw_forward_rate_request); static bool clk_core_can_round(struct clk_core * const core) { - return core->ops->determine_rate || core->ops->round_rate; + return core->ops->determine_rate; } static int clk_core_round_rate_nolock(struct clk_core *core, @@ -1750,11 +1741,11 @@ EXPORT_SYMBOL_GPL(__clk_determine_rate); * use. * * Context: prepare_lock must be held. - * For clk providers to call from within clk_ops such as .round_rate, + * For clk providers to call from within clk_ops such as * .determine_rate. * - * Return: returns rounded rate of hw clk if clk supports round_rate operation - * else returns the parent rate. + * Return: returns rounded rate of hw clk if clk supports determine_rate + * operation; else returns the parent rate. */ unsigned long clk_hw_round_rate(struct clk_hw *hw, unsigned long rate) { @@ -2569,12 +2560,13 @@ err: * * Setting the CLK_SET_RATE_PARENT flag allows the rate change operation to * propagate up to clk's parent; whether or not this happens depends on the - * outcome of clk's .round_rate implementation. If *parent_rate is unchanged - * after calling .round_rate then upstream parent propagation is ignored. If - * *parent_rate comes back with a new rate for clk's parent then we propagate - * up to clk's parent and set its rate. Upward propagation will continue - * until either a clk does not support the CLK_SET_RATE_PARENT flag or - * .round_rate stops requesting changes to clk's parent_rate. + * outcome of clk's .determine_rate implementation. If req->best_parent_rate + * is unchanged after calling .determine_rate then upstream parent propagation + * is ignored. If req->best_parent_rate comes back with a new rate for clk's + * parent then we propagate up to clk's parent and set its rate. Upward + * propagation will continue until either a clk does not support the + * CLK_SET_RATE_PARENT flag or .determine_rate stops requesting changes to + * clk's parent_rate. * * Rate changes are accomplished via tree traversal that also recalculates the * rates for the clocks and fires off POST_RATE_CHANGE notifiers. @@ -2703,8 +2695,6 @@ static int clk_set_rate_range_nolock(struct clk *clk, * FIXME: * There is a catch. It may fail for the usual reason (clock * broken, clock protected, etc) but also because: - * - round_rate() was not favorable and fell on the wrong - * side of the boundary * - the determine_rate() callback does not really check for * this corner case when determining the rate */ @@ -3259,11 +3249,10 @@ bool clk_is_match(const struct clk *p, const struct clk *q) return true; /* true if clk->core pointers match. Avoid dereferencing garbage */ - if (!IS_ERR_OR_NULL(p) && !IS_ERR_OR_NULL(q)) - if (p->core == q->core) - return true; + if (IS_ERR_OR_NULL(p) || IS_ERR_OR_NULL(q)) + return false; - return false; + return p->core == q->core; } EXPORT_SYMBOL_GPL(clk_is_match); @@ -3915,10 +3904,9 @@ static int __clk_core_init(struct clk_core *core) } /* check that clk_ops are sane. See Documentation/driver-api/clk.rst */ - if (core->ops->set_rate && - !((core->ops->round_rate || core->ops->determine_rate) && - core->ops->recalc_rate)) { - pr_err("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n", + if (core->ops->set_rate && !core->ops->determine_rate && + core->ops->recalc_rate) { + pr_err("%s: %s must implement .determine_rate in addition to .recalc_rate\n", __func__, core->name); ret = -EINVAL; goto out; diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c index a268d7b5d4cb..b1961daac5e2 100644 --- a/drivers/clk/clk_test.c +++ b/drivers/clk/clk_test.c @@ -241,8 +241,8 @@ static void clk_test_get_rate(struct kunit *test) * Test that, after a call to clk_set_rate(), the rate returned by * clk_get_rate() matches. * - * This assumes that clk_ops.determine_rate or clk_ops.round_rate won't - * modify the requested rate, which is our case in clk_dummy_rate_ops. + * This assumes that clk_ops.determine_rate won't modify the requested rate, + * which is our case in clk_dummy_rate_ops. */ static void clk_test_set_get_rate(struct kunit *test) { @@ -266,8 +266,8 @@ static void clk_test_set_get_rate(struct kunit *test) * Test that, after several calls to clk_set_rate(), the rate returned * by clk_get_rate() matches the last one. * - * This assumes that clk_ops.determine_rate or clk_ops.round_rate won't - * modify the requested rate, which is our case in clk_dummy_rate_ops. + * This assumes that clk_ops.determine_rate won't modify the requested rate, + * which is our case in clk_dummy_rate_ops. */ static void clk_test_set_set_get_rate(struct kunit *test) { @@ -1675,8 +1675,8 @@ static void clk_range_test_set_range_set_round_rate_consistent_higher(struct kun * call to clk_set_rate_range(), the rate will be raised to match the * new minimum. * - * This assumes that clk_ops.determine_rate or clk_ops.round_rate won't - * modify the requested rate, which is our case in clk_dummy_rate_ops. + * This assumes that clk_ops.determine_rate won't modify the requested rate, + * which is our case in clk_dummy_rate_ops. */ static void clk_range_test_set_range_get_rate_raised(struct kunit *test) { @@ -1707,8 +1707,8 @@ static void clk_range_test_set_range_get_rate_raised(struct kunit *test) * call to clk_set_rate_range(), the rate will be lowered to match the * new maximum. * - * This assumes that clk_ops.determine_rate or clk_ops.round_rate won't - * modify the requested rate, which is our case in clk_dummy_rate_ops. + * This assumes that clk_ops.determine_rate won't modify the requested rate, + * which is our case in clk_dummy_rate_ops. */ static void clk_range_test_set_range_get_rate_lowered(struct kunit *test) { diff --git a/drivers/clk/eswin/Kconfig b/drivers/clk/eswin/Kconfig new file mode 100644 index 000000000000..0406ec499ec9 --- /dev/null +++ b/drivers/clk/eswin/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 + +config COMMON_CLK_ESWIN + bool + +config COMMON_CLK_EIC7700 + tristate "EIC7700 Clock Driver" + depends on ARCH_ESWIN || COMPILE_TEST + select COMMON_CLK_ESWIN + default ARCH_ESWIN + help + This driver provides support for clock controller on ESWIN EIC7700 + SoC. The clock controller generates and supplies clocks to various + peripherals within the SoC. + Say yes here to support the clock controller on the EIC7700 SoC. diff --git a/drivers/clk/eswin/Makefile b/drivers/clk/eswin/Makefile new file mode 100644 index 000000000000..4a7c2af82164 --- /dev/null +++ b/drivers/clk/eswin/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Eswin Clock specific Makefile +# + +obj-$(CONFIG_COMMON_CLK_ESWIN) += clk.o + +obj-$(CONFIG_COMMON_CLK_EIC7700) += clk-eic7700.o diff --git a/drivers/clk/eswin/clk-eic7700.c b/drivers/clk/eswin/clk-eic7700.c new file mode 100644 index 000000000000..be81d74192da --- /dev/null +++ b/drivers/clk/eswin/clk-eic7700.c @@ -0,0 +1,1376 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * ESWIN EIC7700 Clk Provider Driver + * + * Authors: + * Yifeng Huang + * Xuyang Dong + */ + +#include +#include +#include + +#include + +#include "common.h" + +/* REG OFFSET OF SYS-CRG */ +#define EIC7700_REG_OFFSET_SPLL0_CFG_0 0x0 +#define EIC7700_REG_OFFSET_SPLL0_CFG_1 0x4 +#define EIC7700_REG_OFFSET_SPLL0_CFG_2 0x8 +#define EIC7700_REG_OFFSET_SPLL0_DSKEWCAL 0xC +#define EIC7700_REG_OFFSET_SPLL0_SSC 0x10 +#define EIC7700_REG_OFFSET_SPLL1_CFG_0 0x14 +#define EIC7700_REG_OFFSET_SPLL1_CFG_1 0x18 +#define EIC7700_REG_OFFSET_SPLL1_CFG_2 0x1C +#define EIC7700_REG_OFFSET_SPLL1_DSKEWCAL 0x20 +#define EIC7700_REG_OFFSET_SPLL1_SSC 0x24 +#define EIC7700_REG_OFFSET_SPLL2_CFG_0 0x28 +#define EIC7700_REG_OFFSET_SPLL2_CFG_1 0x2C +#define EIC7700_REG_OFFSET_SPLL2_CFG_2 0x30 +#define EIC7700_REG_OFFSET_SPLL2_DSKEWCAL 0x34 +#define EIC7700_REG_OFFSET_SPLL2_SSC 0x38 +#define EIC7700_REG_OFFSET_VPLL_CFG_0 0x3C +#define EIC7700_REG_OFFSET_VPLL_CFG_1 0x40 +#define EIC7700_REG_OFFSET_VPLL_CFG_2 0x44 +#define EIC7700_REG_OFFSET_VPLL_DSKEWCAL 0x48 +#define EIC7700_REG_OFFSET_VPLL_SSC 0x4C +#define EIC7700_REG_OFFSET_APLL_CFG_0 0x50 +#define EIC7700_REG_OFFSET_APLL_CFG_1 0x54 +#define EIC7700_REG_OFFSET_APLL_CFG_2 0x58 +#define EIC7700_REG_OFFSET_APLL_DSKEWCAL 0x5C +#define EIC7700_REG_OFFSET_APLL_SSC 0x60 +#define EIC7700_REG_OFFSET_MCPUT_PLL_CFG_0 0x64 +#define EIC7700_REG_OFFSET_MCPUT_PLL_CFG_1 0x68 +#define EIC7700_REG_OFFSET_MCPUT_PLL_CFG_2 0x6C +#define EIC7700_REG_OFFSET_MCPUT_PLL_DSKEWCAL 0x70 +#define EIC7700_REG_OFFSET_MCPUT_PLL_SSC 0x74 +#define EIC7700_REG_OFFSET_DDRT_PLL_CFG_0 0x78 +#define EIC7700_REG_OFFSET_DDRT_PLL_CFG_1 0x7C +#define EIC7700_REG_OFFSET_DDRT_PLL_CFG_2 0x80 +#define EIC7700_REG_OFFSET_DDRT_PLL_DSKEWCAL 0x84 +#define EIC7700_REG_OFFSET_DDRT_PLL_SSC 0x88 +#define EIC7700_REG_OFFSET_PLL_STATUS 0xA4 +#define EIC7700_REG_OFFSET_NOC 0x100 +#define EIC7700_REG_OFFSET_BOOTSPI 0x104 +#define EIC7700_REG_OFFSET_BOOTSPI_CFGCLK 0x108 +#define EIC7700_REG_OFFSET_SCPU_CORE 0x10C +#define EIC7700_REG_OFFSET_SCPU_BUSCLK 0x110 +#define EIC7700_REG_OFFSET_LPCPU_CORE 0x114 +#define EIC7700_REG_OFFSET_LPCPU_BUSCLK 0x118 +#define EIC7700_REG_OFFSET_TCU_ACLK 0x11C +#define EIC7700_REG_OFFSET_TCU_CFG 0x120 +#define EIC7700_REG_OFFSET_DDR 0x124 +#define EIC7700_REG_OFFSET_DDR1 0x128 +#define EIC7700_REG_OFFSET_GPU_ACLK 0x12C +#define EIC7700_REG_OFFSET_GPU_CFG 0x130 +#define EIC7700_REG_OFFSET_GPU_GRAY 0x134 +#define EIC7700_REG_OFFSET_DSP_ACLK 0x138 +#define EIC7700_REG_OFFSET_DSP_CFG 0x13C +#define EIC7700_REG_OFFSET_D2D_ACLK 0x140 +#define EIC7700_REG_OFFSET_D2D_CFG 0x144 +#define EIC7700_REG_OFFSET_HSP_ACLK 0x148 +#define EIC7700_REG_OFFSET_HSP_CFG 0x14C +#define EIC7700_REG_OFFSET_SATA_RBC 0x150 +#define EIC7700_REG_OFFSET_SATA_OOB 0x154 +#define EIC7700_REG_OFFSET_ETH0 0x158 +#define EIC7700_REG_OFFSET_ETH1 0x15C +#define EIC7700_REG_OFFSET_MSHC0_CORE 0x160 +#define EIC7700_REG_OFFSET_MSHC1_CORE 0x164 +#define EIC7700_REG_OFFSET_MSHC2_CORE 0x168 +#define EIC7700_REG_OFFSET_MSHC_USB_SLWCLK 0x16C +#define EIC7700_REG_OFFSET_PCIE_ACLK 0x170 +#define EIC7700_REG_OFFSET_PCIE_CFG 0x174 +#define EIC7700_REG_OFFSET_NPU_ACLK 0x178 +#define EIC7700_REG_OFFSET_NPU_LLC 0x17C +#define EIC7700_REG_OFFSET_NPU_CORE 0x180 +#define EIC7700_REG_OFFSET_VI_DWCLK 0x184 +#define EIC7700_REG_OFFSET_VI_ACLK 0x188 +#define EIC7700_REG_OFFSET_VI_DIG_ISP 0x18C +#define EIC7700_REG_OFFSET_VI_DVP 0x190 +#define EIC7700_REG_OFFSET_VI_SHUTTER0 0x194 +#define EIC7700_REG_OFFSET_VI_SHUTTER1 0x198 +#define EIC7700_REG_OFFSET_VI_SHUTTER2 0x19C +#define EIC7700_REG_OFFSET_VI_SHUTTER3 0x1A0 +#define EIC7700_REG_OFFSET_VI_SHUTTER4 0x1A4 +#define EIC7700_REG_OFFSET_VI_SHUTTER5 0x1A8 +#define EIC7700_REG_OFFSET_VI_PHY 0x1AC +#define EIC7700_REG_OFFSET_VO_ACLK 0x1B0 +#define EIC7700_REG_OFFSET_VO_IESMCLK 0x1B4 +#define EIC7700_REG_OFFSET_VO_PIXEL 0x1B8 +#define EIC7700_REG_OFFSET_VO_MCLK 0x1BC +#define EIC7700_REG_OFFSET_VO_PHY_CLK 0x1C0 +#define EIC7700_REG_OFFSET_VC_ACLK 0x1C4 +#define EIC7700_REG_OFFSET_VCDEC_ROOT 0x1C8 +#define EIC7700_REG_OFFSET_G2D 0x1CC +#define EIC7700_REG_OFFSET_VC_CLKEN 0x1D0 +#define EIC7700_REG_OFFSET_JE 0x1D4 +#define EIC7700_REG_OFFSET_JD 0x1D8 +#define EIC7700_REG_OFFSET_VD 0x1DC +#define EIC7700_REG_OFFSET_VE 0x1E0 +#define EIC7700_REG_OFFSET_AON_DMA 0x1E4 +#define EIC7700_REG_OFFSET_TIMER 0x1E8 +#define EIC7700_REG_OFFSET_RTC 0x1EC +#define EIC7700_REG_OFFSET_PKA 0x1F0 +#define EIC7700_REG_OFFSET_SPACC 0x1F4 +#define EIC7700_REG_OFFSET_TRNG 0x1F8 +#define EIC7700_REG_OFFSET_OTP 0x1FC +#define EIC7700_REG_OFFSET_LSP_EN0 0x200 +#define EIC7700_REG_OFFSET_LSP_EN1 0x204 +#define EIC7700_REG_OFFSET_U84 0x208 +#define EIC7700_REG_OFFSET_SYSCFG 0x20C +#define EIC7700_REG_OFFSET_I2C0 0x210 +#define EIC7700_REG_OFFSET_I2C1 0x214 + +#define EIC7700_NR_CLKS (EIC7700_CLK_GATE_NOC_WDREF + 1) + +/* + * The 24 MHz oscillator, the root of most of the clock tree. + */ +static const struct clk_parent_data xtal24M[] = { + { .index = 0, } +}; + +/* fixed rate clocks */ +static struct eswin_fixed_rate_clock eic7700_fixed_rate_clks[] = { + ESWIN_FIXED(EIC7700_CLK_XTAL_32K, "fixed_rate_clk_xtal_32k", 0, 32768), + ESWIN_FIXED(EIC7700_CLK_SPLL0_FOUT1, "fixed_rate_clk_spll0_fout1", 0, + 1600000000), + ESWIN_FIXED(EIC7700_CLK_SPLL0_FOUT2, "fixed_rate_clk_spll0_fout2", 0, + 800000000), + ESWIN_FIXED(EIC7700_CLK_SPLL0_FOUT3, "fixed_rate_clk_spll0_fout3", 0, + 400000000), + ESWIN_FIXED(EIC7700_CLK_SPLL1_FOUT1, "fixed_rate_clk_spll1_fout1", 0, + 1500000000), + ESWIN_FIXED(EIC7700_CLK_SPLL1_FOUT2, "fixed_rate_clk_spll1_fout2", 0, + 300000000), + ESWIN_FIXED(EIC7700_CLK_SPLL1_FOUT3, "fixed_rate_clk_spll1_fout3", 0, + 250000000), + ESWIN_FIXED(EIC7700_CLK_SPLL2_FOUT1, "fixed_rate_clk_spll2_fout1", 0, + 2080000000), + ESWIN_FIXED(EIC7700_CLK_SPLL2_FOUT2, "fixed_rate_clk_spll2_fout2", 0, + 1040000000), + ESWIN_FIXED(EIC7700_CLK_SPLL2_FOUT3, "fixed_rate_clk_spll2_fout3", 0, + 416000000), + ESWIN_FIXED(EIC7700_CLK_VPLL_FOUT1, "fixed_rate_clk_vpll_fout1", 0, + 1188000000), + ESWIN_FIXED(EIC7700_CLK_VPLL_FOUT2, "fixed_rate_clk_vpll_fout2", 0, + 594000000), + ESWIN_FIXED(EIC7700_CLK_VPLL_FOUT3, "fixed_rate_clk_vpll_fout3", 0, + 49500000), + ESWIN_FIXED(EIC7700_CLK_APLL_FOUT2, "fixed_rate_clk_apll_fout2", 0, 0), + ESWIN_FIXED(EIC7700_CLK_APLL_FOUT3, "fixed_rate_clk_apll_fout3", 0, 0), + ESWIN_FIXED(EIC7700_CLK_EXT_MCLK, "fixed_rate_ext_mclk", 0, 0), + ESWIN_FIXED(EIC7700_CLK_LPDDR_REF_BAK, "fixed_rate_lpddr_ref_bak", 0, + 50000000), +}; + +/* pll clocks */ +static struct eswin_pll_clock eic7700_pll_clks[] = { + ESWIN_PLL(EIC7700_CLK_APLL_FOUT1, "clk_apll_fout1", xtal24M, + EIC7700_REG_OFFSET_APLL_CFG_0, 20, + EIC7700_REG_OFFSET_APLL_CFG_1, 4, + EIC7700_REG_OFFSET_APLL_CFG_2, EIC7700_REG_OFFSET_PLL_STATUS, + 4, 1, APLL_HIGH_FREQ, APLL_LOW_FREQ), + ESWIN_PLL(EIC7700_CLK_PLL_CPU, "clk_pll_cpu", xtal24M, + EIC7700_REG_OFFSET_MCPUT_PLL_CFG_0, 20, + EIC7700_REG_OFFSET_MCPUT_PLL_CFG_1, 4, + EIC7700_REG_OFFSET_MCPUT_PLL_CFG_2, + EIC7700_REG_OFFSET_PLL_STATUS, 5, 1, PLL_HIGH_FREQ, + PLL_LOW_FREQ), +}; + +/* fixed factor clocks */ +static struct eswin_fixed_factor_clock eic7700_factor_clks[] = { + ESWIN_FACTOR(EIC7700_CLK_FIXED_FACTOR_CLK_1M_DIV24, + "fixed_factor_clk_1m_div24", xtal24M, 1, 24, 0), + ESWIN_FACTOR(EIC7700_CLK_FIXED_FACTOR_PVT_DIV20, + "fixed_factor_pvt_div20", xtal24M, 1, 20, 0), +}; + +/* divider clocks */ +static struct eswin_divider_clock eic7700_div_clks[] = { + ESWIN_DIV(EIC7700_CLK_DIV_U84_RTC_TOGGLE_DYNM, + "divider_u84_rtc_toggle_dynm", xtal24M, 0, + EIC7700_REG_OFFSET_RTC, 16, 5, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, 0), + ESWIN_DIV(EIC7700_CLK_DIV_NOC_WDREF_DYNM, "divider_noc_wdref_dynm", + xtal24M, 0, EIC7700_REG_OFFSET_NOC, 4, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, 0), +}; + +/* gate clocks */ +static struct eswin_gate_clock eic7700_gate_clks[] = { + ESWIN_GATE(EIC7700_CLK_GATE_GPU_GRAY_CLK, "gate_gpu_gray_clk", xtal24M, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_GPU_GRAY, 31, 0), + ESWIN_GATE(EIC7700_CLK_GATE_VI_PHY_CFG, "gate_vi_phy_cfg", xtal24M, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VI_PHY, 1, 0), + ESWIN_GATE(EIC7700_CLK_GATE_TIMER_CLK_0, "gate_time_clk_0", xtal24M, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_TIMER, 0, 0), + ESWIN_GATE(EIC7700_CLK_GATE_TIMER_CLK_1, "gate_time_clk_1", xtal24M, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_TIMER, 1, 0), + ESWIN_GATE(EIC7700_CLK_GATE_TIMER_CLK_2, "gate_time_clk_2", xtal24M, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_TIMER, 2, 0), + ESWIN_GATE(EIC7700_CLK_GATE_TIMER_CLK_3, "gate_time_clk_3", xtal24M, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_TIMER, 3, 0), +}; + +/* Define the early clocks as the parent clocks of the mux clocks. */ +static struct eswin_clk_info eic7700_early_clks[] = { + ESWIN_FACTOR_TYPE(EIC7700_CLK_FIXED_FACTOR_HSP_RMII_REF_DIV6, + "fixed_factor_hsp_rmii_ref_div6", + EIC7700_CLK_SPLL1_FOUT2, 1, 6, 0), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_NPU_LLC_SRC0_DYNM, + "divider_npu_llc_src0_div_dynm", + EIC7700_CLK_SPLL0_FOUT1, 0, EIC7700_REG_OFFSET_NPU_LLC, + 4, 4, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_NPU_LLC_SRC1_DYNM, + "divider_npu_llc_src1_div_dynm", + EIC7700_CLK_SPLL2_FOUT1, 0, EIC7700_REG_OFFSET_NPU_LLC, + 8, 4, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_SPLL0_FOUT2, "gate_clk_spll0_fout2", + EIC7700_CLK_SPLL0_FOUT2, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_SPLL0_CFG_2, 31, 0), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_BOOTSPI_DYNM, "divider_bootspi_div_dynm", + EIC7700_CLK_GATE_SPLL0_FOUT2, 0, + EIC7700_REG_OFFSET_BOOTSPI, 4, 6, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_SCPU_CORE_DYNM, + "divider_scpu_core_div_dynm", EIC7700_CLK_SPLL0_FOUT1, 0, + EIC7700_REG_OFFSET_SCPU_CORE, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_LPCPU_CORE_DYNM, + "divider_lpcpu_core_div_dynm", EIC7700_CLK_SPLL0_FOUT1, + 0, EIC7700_REG_OFFSET_LPCPU_CORE, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VO_MCLK_DYNM, "divider_vo_mclk_div_dynm", + EIC7700_CLK_APLL_FOUT1, 0, EIC7700_REG_OFFSET_VO_MCLK, 4, + 8, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_AONDMA_AXI_DYNM, + "divider_aondma_axi_div_dynm", EIC7700_CLK_SPLL0_FOUT1, + 0, EIC7700_REG_OFFSET_AON_DMA, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_SATA_PHY_REF_DYNM, + "divider_sata_phy_ref_div_dynm", + EIC7700_CLK_SPLL1_FOUT2, 0, EIC7700_REG_OFFSET_SATA_OOB, + 0, 4, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_SYS_CFG_DYNM, "divider_sys_cfg_div_dynm", + EIC7700_CLK_SPLL0_FOUT3, 0, EIC7700_REG_OFFSET_SYSCFG, 4, + 3, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_FACTOR_TYPE(EIC7700_CLK_FIXED_FACTOR_U84_CORE_LP_DIV2, + "fixed_factor_u84_core_lp_div2", + EIC7700_CLK_GATE_SPLL0_FOUT2, 1, 2, 0), +}; + +static const struct clk_parent_data dsp_aclk_root_2mux1_gfree_mux_p[] = { + { .hw = &eic7700_fixed_rate_clks[7].hw }, + { .hw = &eic7700_fixed_rate_clks[1].hw }, +}; + +static const struct clk_parent_data d2d_aclk_root_2mux1_gfree_mux_p[] = { + { .hw = &eic7700_fixed_rate_clks[7].hw }, + { .hw = &eic7700_fixed_rate_clks[1].hw }, +}; + +static const struct clk_parent_data ddr_aclk_root_2mux1_gfree_mux_p[] = { + { .hw = &eic7700_fixed_rate_clks[7].hw }, + { .hw = &eic7700_fixed_rate_clks[1].hw }, +}; + +static const struct clk_parent_data mshcore_root_3mux1_0_mux_p[] = { + { .hw = &eic7700_fixed_rate_clks[3].hw }, + { .hw = &eic7700_fixed_rate_clks[9].hw }, +}; + +static const struct clk_parent_data mshcore_root_3mux1_1_mux_p[] = { + { .hw = &eic7700_fixed_rate_clks[3].hw }, + { .hw = &eic7700_fixed_rate_clks[9].hw }, +}; + +static const struct clk_parent_data mshcore_root_3mux1_2_mux_p[] = { + { .hw = &eic7700_fixed_rate_clks[3].hw }, + { .hw = &eic7700_fixed_rate_clks[9].hw }, +}; + +static const struct clk_parent_data npu_core_3mux1_gfree_mux_p[] = { + { .hw = &eic7700_fixed_rate_clks[4].hw }, + { .hw = &eic7700_fixed_rate_clks[10].hw }, + { .hw = &eic7700_fixed_rate_clks[8].hw }, +}; + +static const struct clk_parent_data npu_e31_3mux1_gfree_mux_p[] = { + { .hw = &eic7700_fixed_rate_clks[4].hw }, + { .hw = &eic7700_fixed_rate_clks[10].hw }, + { .hw = &eic7700_fixed_rate_clks[8].hw }, +}; + +static const struct clk_parent_data vi_aclk_root_2mux1_gfree_mux_p[] = { + { .hw = &eic7700_fixed_rate_clks[1].hw }, + { .hw = &eic7700_fixed_rate_clks[7].hw }, +}; + +static const struct clk_parent_data mux_vi_dw_root_2mux1_p[] = { + { .hw = &eic7700_fixed_rate_clks[10].hw }, + { .hw = &eic7700_fixed_rate_clks[1].hw }, +}; + +static const struct clk_parent_data mux_vi_dvp_root_2mux1_gfree_p[] = { + { .hw = &eic7700_fixed_rate_clks[10].hw }, + { .hw = &eic7700_fixed_rate_clks[1].hw }, +}; + +static const struct clk_parent_data mux_vi_dig_isp_root_2mux1_gfree_p[] = { + { .hw = &eic7700_fixed_rate_clks[10].hw }, + { .hw = &eic7700_fixed_rate_clks[1].hw }, +}; + +static const struct clk_parent_data mux_vo_aclk_root_2mux1_gfree_p[] = { + { .hw = &eic7700_fixed_rate_clks[1].hw }, + { .hw = &eic7700_fixed_rate_clks[7].hw }, +}; + +static const struct clk_parent_data mux_vo_pixel_root_2mux1_p[] = { + { .hw = &eic7700_fixed_rate_clks[10].hw }, + { .hw = &eic7700_fixed_rate_clks[8].hw }, +}; + +static const struct clk_parent_data mux_vcdec_root_2mux1_gfree_p[] = { + { .hw = &eic7700_fixed_rate_clks[1].hw }, + { .hw = &eic7700_fixed_rate_clks[7].hw }, +}; + +static const struct clk_parent_data mux_vcaclk_root_2mux1_gfree_p[] = { + { .hw = &eic7700_fixed_rate_clks[1].hw }, + { .hw = &eic7700_fixed_rate_clks[7].hw }, +}; + +static const struct clk_parent_data npu_llclk_3mux1_gfree_mux_p[] = { + { .hw = &eic7700_early_clks[1].hw }, + { .hw = &eic7700_early_clks[2].hw }, + { .hw = &eic7700_fixed_rate_clks[10].hw }, +}; + +static const struct clk_parent_data mux_bootspi_clk_2mux1_gfree_p[] = { + { .hw = &eic7700_early_clks[4].hw }, + { .index = 0 }, +}; + +static const struct clk_parent_data mux_scpu_core_clk_2mux1_gfree_p[] = { + { .hw = &eic7700_early_clks[5].hw }, + { .index = 0 }, +}; + +static const struct clk_parent_data mux_lpcpu_core_clk_2mux1_gfree_p[] = { + { .hw = &eic7700_early_clks[6].hw }, + { .index = 0 }, +}; + +static const struct clk_parent_data mux_vo_mclk_2mux_ext_mclk_p[] = { + { .hw = &eic7700_early_clks[7].hw }, + { .hw = &eic7700_fixed_rate_clks[15].hw }, +}; + +static const struct clk_parent_data mux_aondma_axi2mux1_gfree_p[] = { + { .hw = &eic7700_early_clks[8].hw }, + { .index = 0 }, +}; + +static const struct clk_parent_data mux_rmii_ref_2mux1_p[] = { + { .hw = &eic7700_early_clks[0].hw }, + { .hw = &eic7700_fixed_rate_clks[16].hw }, +}; + +static const struct clk_parent_data mux_eth_core_2mux1_p[] = { + { .hw = &eic7700_fixed_rate_clks[6].hw }, + { .hw = &eic7700_fixed_rate_clks[16].hw }, +}; + +static const struct clk_parent_data mux_sata_phy_2mux1_p[] = { + { .hw = &eic7700_early_clks[9].hw }, + { .hw = &eic7700_fixed_rate_clks[16].hw }, +}; + +static const struct clk_parent_data mux_syscfg_clk_root_2mux1_gfree_p[] = { + { .hw = &eic7700_early_clks[10].hw }, + { .index = 0 }, +}; + +static const struct clk_parent_data mux_cpu_root_3mux1_gfree_p[] = { + { .hw = &eic7700_pll_clks[1].hw }, + { .hw = &eic7700_early_clks[11].hw }, + { .index = 0 }, +}; + +static struct eswin_mux_clock eic7700_mux_clks[] = { + ESWIN_MUX(EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE, + "mux_cpu_root_3mux1_gfree", mux_cpu_root_3mux1_gfree_p, + ARRAY_SIZE(mux_cpu_root_3mux1_gfree_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_U84, 0, 2, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_RMII_REF_2MUX, "mux_rmii_ref_2mux1", + mux_rmii_ref_2mux1_p, ARRAY_SIZE(mux_rmii_ref_2mux1_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_ETH0, 2, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_DSP_ACLK_ROOT_2MUX1_GFREE, + "mux_dsp_aclk_root_2mux1_gfree", + dsp_aclk_root_2mux1_gfree_mux_p, + ARRAY_SIZE(dsp_aclk_root_2mux1_gfree_mux_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_DSP_ACLK, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_D2D_ACLK_ROOT_2MUX1_GFREE, + "mux_d2d_aclk_root_2mux1_gfree", + d2d_aclk_root_2mux1_gfree_mux_p, + ARRAY_SIZE(d2d_aclk_root_2mux1_gfree_mux_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_D2D_ACLK, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_DDR_ACLK_ROOT_2MUX1_GFREE, + "mux_ddr_aclk_root_2mux1_gfree", + ddr_aclk_root_2mux1_gfree_mux_p, + ARRAY_SIZE(ddr_aclk_root_2mux1_gfree_mux_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_DDR, 16, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_MSHCORE_ROOT_3MUX1_0, + "mux_mshcore_root_3mux1_0", mshcore_root_3mux1_0_mux_p, + ARRAY_SIZE(mshcore_root_3mux1_0_mux_p), CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_MSHC0_CORE, 0, 1, CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_MSHCORE_ROOT_3MUX1_1, + "mux_mshcore_root_3mux1_1", mshcore_root_3mux1_1_mux_p, + ARRAY_SIZE(mshcore_root_3mux1_1_mux_p), CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_MSHC1_CORE, 0, 1, CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_MSHCORE_ROOT_3MUX1_2, + "mux_mshcore_root_3mux1_2", mshcore_root_3mux1_2_mux_p, + ARRAY_SIZE(mshcore_root_3mux1_2_mux_p), CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_MSHC2_CORE, 0, 1, CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_NPU_LLCLK_3MUX1_GFREE, + "mux_npu_llclk_3mux1_gfree", npu_llclk_3mux1_gfree_mux_p, + ARRAY_SIZE(npu_llclk_3mux1_gfree_mux_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_NPU_LLC, 0, 2, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_NPU_CORE_3MUX1_GFREE, + "mux_npu_core_3mux1_gfree", npu_core_3mux1_gfree_mux_p, + ARRAY_SIZE(npu_core_3mux1_gfree_mux_p), CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_NPU_CORE, 0, 2, CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_NPU_E31_3MUX1_GFREE, + "mux_npu_e31_3mux1_gfree", npu_e31_3mux1_gfree_mux_p, + ARRAY_SIZE(npu_e31_3mux1_gfree_mux_p), CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_NPU_CORE, 8, 2, CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_VI_ACLK_ROOT_2MUX1_GFREE, + "mux_vi_aclk_root_2mux1_gfree", + vi_aclk_root_2mux1_gfree_mux_p, + ARRAY_SIZE(vi_aclk_root_2mux1_gfree_mux_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VI_ACLK, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_VI_DW_ROOT_2MUX1, "mux_vi_dw_root_2mux1", + mux_vi_dw_root_2mux1_p, ARRAY_SIZE(mux_vi_dw_root_2mux1_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VI_DWCLK, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_VI_DVP_ROOT_2MUX1_GFREE, + "mux_vi_dvp_root_2mux1_gfree", + mux_vi_dvp_root_2mux1_gfree_p, + ARRAY_SIZE(mux_vi_dvp_root_2mux1_gfree_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VI_DVP, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_VI_DIG_ISP_ROOT_2MUX1_GFREE, + "mux_vi_dig_isp_root_2mux1_gfree", + mux_vi_dig_isp_root_2mux1_gfree_p, + ARRAY_SIZE(mux_vi_dig_isp_root_2mux1_gfree_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VI_DIG_ISP, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_VO_ACLK_ROOT_2MUX1_GFREE, + "mux_vo_aclk_root_2mux1_gfree", + mux_vo_aclk_root_2mux1_gfree_p, + ARRAY_SIZE(mux_vo_aclk_root_2mux1_gfree_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VO_ACLK, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_VO_PIXEL_ROOT_2MUX1, + "mux_vo_pixel_root_2mux1", mux_vo_pixel_root_2mux1_p, + ARRAY_SIZE(mux_vo_pixel_root_2mux1_p), CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VO_PIXEL, 0, 1, CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_VCDEC_ROOT_2MUX1_GFREE, + "mux_vcdec_root_2mux1_gfree", mux_vcdec_root_2mux1_gfree_p, + ARRAY_SIZE(mux_vcdec_root_2mux1_gfree_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VCDEC_ROOT, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_VCACLK_ROOT_2MUX1_GFREE, + "mux_vcaclk_root_2mux1_gfree", + mux_vcaclk_root_2mux1_gfree_p, + ARRAY_SIZE(mux_vcaclk_root_2mux1_gfree_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VC_ACLK, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + "mux_syscfg_clk_root_2mux1_gfree", + mux_syscfg_clk_root_2mux1_gfree_p, + ARRAY_SIZE(mux_syscfg_clk_root_2mux1_gfree_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_SYSCFG, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_BOOTSPI_CLK_2MUX1_GFREE, + "mux_bootspi_clk_2mux1_gfree", + mux_bootspi_clk_2mux1_gfree_p, + ARRAY_SIZE(mux_bootspi_clk_2mux1_gfree_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_BOOTSPI, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_SCPU_CORE_CLK_2MUX1_GFREE, + "mux_scpu_core_clk_2mux1_gfree", + mux_scpu_core_clk_2mux1_gfree_p, + ARRAY_SIZE(mux_scpu_core_clk_2mux1_gfree_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_SCPU_CORE, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_LPCPU_CORE_CLK_2MUX1_GFREE, + "mux_lpcpu_core_clk_2mux1_gfree", + mux_lpcpu_core_clk_2mux1_gfree_p, + ARRAY_SIZE(mux_lpcpu_core_clk_2mux1_gfree_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LPCPU_CORE, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_VO_MCLK_2MUX_EXT_MCLK, + "mux_vo_mclk_2mux_ext_mclk", mux_vo_mclk_2mux_ext_mclk_p, + ARRAY_SIZE(mux_vo_mclk_2mux_ext_mclk_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VO_MCLK, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_AONDMA_AXI2MUX1_GFREE, + "mux_aondma_axi2mux1_gfree", mux_aondma_axi2mux1_gfree_p, + ARRAY_SIZE(mux_aondma_axi2mux1_gfree_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_AON_DMA, 0, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_ETH_CORE_2MUX1, "mux_eth_core_2mux1", + mux_eth_core_2mux1_p, ARRAY_SIZE(mux_eth_core_2mux1_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_ETH0, 1, 1, + CLK_MUX_ROUND_CLOSEST), + ESWIN_MUX(EIC7700_CLK_MUX_SATA_PHY_2MUX1, "mux_sata_phy_2mux1", + mux_sata_phy_2mux1_p, ARRAY_SIZE(mux_sata_phy_2mux1_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_SATA_OOB, 9, 1, + CLK_MUX_ROUND_CLOSEST), +}; + +static const struct clk_parent_data mux_cpu_aclk_2mux1_gfree_p[] = { + { .hw = &eic7700_mux_clks[1].hw }, + { .hw = &eic7700_mux_clks[0].hw }, +}; + +static struct eswin_clk_info eic7700_clks[] = { + ESWIN_MUX_TYPE(EIC7700_CLK_MUX_CPU_ACLK_2MUX1_GFREE, + "mux_cpu_aclk_2mux1_gfree", mux_cpu_aclk_2mux1_gfree_p, + ARRAY_SIZE(mux_cpu_aclk_2mux1_gfree_p), + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_U84, 20, 1, + CLK_MUX_ROUND_CLOSEST, NULL), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_CPU_TRACE_COM_CLK, + "gate_clk_cpu_trace_com_clk", + EIC7700_CLK_MUX_CPU_ACLK_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_U84, 23, 0), + ESWIN_FACTOR_TYPE(EIC7700_CLK_FIXED_FACTOR_CPU_DIV2, + "fixed_factor_cpu_div2", + EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE, 1, 2, 0), + ESWIN_FACTOR_TYPE(EIC7700_CLK_FIXED_FACTOR_MIPI_TXESC_DIV10, + "fixed_factor_mipi_txesc_div10", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, 1, 10, + 0), + ESWIN_FACTOR_TYPE(EIC7700_CLK_FIXED_FACTOR_SCPU_BUS_DIV2, + "fixed_factor_scpu_bus_div2", + EIC7700_CLK_MUX_SCPU_CORE_CLK_2MUX1_GFREE, 1, 2, 0), + ESWIN_FACTOR_TYPE(EIC7700_CLK_FIXED_FACTOR_LPCPU_BUS_DIV2, + "fixed_factor_lpcpu_bus_div2", + EIC7700_CLK_MUX_LPCPU_CORE_CLK_2MUX1_GFREE, 1, 2, 0), + ESWIN_FACTOR_TYPE(EIC7700_CLK_FIXED_FACTOR_PCIE_CR_DIV2, + "fixed_factor_pcie_cr_div2", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, 1, 2, 0), + ESWIN_FACTOR_TYPE(EIC7700_CLK_FIXED_FACTOR_PCIE_AUX_DIV4, + "fixed_factor_pcie_aux_div4", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, 1, 4, 0), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_D2D_ACLK_DYNM, + "divider_d2d_aclk_div_dynm", + EIC7700_CLK_MUX_D2D_ACLK_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_D2D_ACLK, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_DSP_ACLK_DYNM, + "divider_dsp_aclk_div_dynm", + EIC7700_CLK_MUX_DSP_ACLK_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_DSP_ACLK, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_DDR_ACLK_DYNM, + "divider_ddr_aclk_div_dynm", + EIC7700_CLK_MUX_DDR_ACLK_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_DDR, 20, 4, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_ETH_TXCLK_DYNM_0, + "divider_eth_txclk_div_dynm_0", + EIC7700_CLK_MUX_ETH_CORE_2MUX1, 0, + EIC7700_REG_OFFSET_ETH0, 4, 7, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_ETH_TXCLK_DYNM_1, + "divider_eth_txclk_div_dynm_1", + EIC7700_CLK_MUX_ETH_CORE_2MUX1, 0, + EIC7700_REG_OFFSET_ETH1, 4, 7, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_MSHC_CORE_DYNM_0, + "divider_mshc_core_div_dynm_0", + EIC7700_CLK_MUX_MSHCORE_ROOT_3MUX1_0, + 0, EIC7700_REG_OFFSET_MSHC0_CORE, 4, 12, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, 0), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_MSHC_CORE_DYNM_1, + "divider_mshc_core_div_dynm_1", + EIC7700_CLK_MUX_MSHCORE_ROOT_3MUX1_1, + 0, EIC7700_REG_OFFSET_MSHC1_CORE, 4, 12, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, 0), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_MSHC_CORE_DYNM_2, + "divider_mshc_core_div_dynm_2", + EIC7700_CLK_MUX_MSHCORE_ROOT_3MUX1_2, + 0, EIC7700_REG_OFFSET_MSHC2_CORE, 4, 12, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, 0), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_NPU_CORECLK_DYNM, + "divider_npu_coreclk_div_dynm", + EIC7700_CLK_MUX_NPU_CORE_3MUX1_GFREE, + 0, EIC7700_REG_OFFSET_NPU_CORE, 4, 4, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, 0), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_NPU_E31_DYNM, "divider_npu_e31_div_dynm", + EIC7700_CLK_MUX_NPU_E31_3MUX1_GFREE, 0, + EIC7700_REG_OFFSET_NPU_CORE, 12, 4, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, 0), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VI_ACLK_DYNM, "divider_vi_aclk_div_dynm", + EIC7700_CLK_MUX_VI_ACLK_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_VI_ACLK, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VI_DW_DYNM, "divider_vi_dw_div_dynm", + EIC7700_CLK_MUX_VI_DW_ROOT_2MUX1, 0, + EIC7700_REG_OFFSET_VI_DWCLK, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VI_DVP_DYNM, "divider_vi_dvp_div_dynm", + EIC7700_CLK_MUX_VI_DVP_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_VI_DVP, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VI_DIG_ISP_DYNM, + "divider_vi_dig_isp_div_dynm", + EIC7700_CLK_MUX_VI_DIG_ISP_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_VI_DIG_ISP, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VO_ACLK_DYNM, "divider_vo_aclk_div_dynm", + EIC7700_CLK_MUX_VO_ACLK_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_VO_ACLK, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VO_PIXEL_DYNM, + "divider_vo_pixel_div_dynm", + EIC7700_CLK_MUX_VO_PIXEL_ROOT_2MUX1, 0, + EIC7700_REG_OFFSET_VO_PIXEL, 4, 6, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VC_ACLK_DYNM, "divider_vc_aclk_div_dynm", + EIC7700_CLK_MUX_VCACLK_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_VC_ACLK, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_JD_DYNM, "divider_jd_div_dynm", + EIC7700_CLK_MUX_VCDEC_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_JD, 4, 4, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_JE_DYNM, "divider_je_div_dynm", + EIC7700_CLK_MUX_VCDEC_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_JE, 4, 4, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VE_DYNM, "divider_ve_div_dynm", + EIC7700_CLK_MUX_VCDEC_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_VE, 4, 4, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VD_DYNM, "divider_vd_div_dynm", + EIC7700_CLK_MUX_VCDEC_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_VD, 4, 4, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_G2D_DYNM, "divider_g2d_div_dynm", + EIC7700_CLK_MUX_DSP_ACLK_ROOT_2MUX1_GFREE, 0, + EIC7700_REG_OFFSET_G2D, 4, 4, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_NOC_NSP_DYNM, "divider_noc_nsp_div_dynm", + EIC7700_CLK_SPLL2_FOUT1, 0, EIC7700_REG_OFFSET_NOC, 0, 3, + 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_GPU_ACLK_DYNM, + "divider_gpu_aclk_div_dynm", EIC7700_CLK_SPLL0_FOUT1, 0, + EIC7700_REG_OFFSET_GPU_ACLK, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_HSP_ACLK_DYNM, + "divider_hsp_aclk_div_dynm", EIC7700_CLK_SPLL0_FOUT1, 0, + EIC7700_REG_OFFSET_HSP_ACLK, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_PCIE_ACLK_DYNM, + "divider_pcie_aclk_div_dynm", EIC7700_CLK_SPLL2_FOUT2, 0, + EIC7700_REG_OFFSET_PCIE_ACLK, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_NPU_ACLK_DYNM, + "divider_npu_aclk_div_dynm", EIC7700_CLK_SPLL0_FOUT1, 0, + EIC7700_REG_OFFSET_NPU_ACLK, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VI_SHUTTER_DYNM_0, + "divider_vi_shutter_div_dynm_0", + EIC7700_CLK_VPLL_FOUT2, 0, + EIC7700_REG_OFFSET_VI_SHUTTER0, 4, 7, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VI_SHUTTER_DYNM_1, + "divider_vi_shutter_div_dynm_1", + EIC7700_CLK_VPLL_FOUT2, 0, + EIC7700_REG_OFFSET_VI_SHUTTER1, 4, 7, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VI_SHUTTER_DYNM_2, + "divider_vi_shutter_div_dynm_2", + EIC7700_CLK_VPLL_FOUT2, 0, + EIC7700_REG_OFFSET_VI_SHUTTER2, 4, 7, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VI_SHUTTER_DYNM_3, + "divider_vi_shutter_div_dynm_3", + EIC7700_CLK_VPLL_FOUT2, 0, + EIC7700_REG_OFFSET_VI_SHUTTER3, 4, 7, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VI_SHUTTER_DYNM_4, + "divider_vi_shutter_div_dynm_4", + EIC7700_CLK_VPLL_FOUT2, 0, + EIC7700_REG_OFFSET_VI_SHUTTER4, 4, 7, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VI_SHUTTER_DYNM_5, + "divider_vi_shutter_div_dynm_5", + EIC7700_CLK_VPLL_FOUT2, 0, + EIC7700_REG_OFFSET_VI_SHUTTER5, 4, 7, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_IESMCLK_DYNM, "divider_iesmclk_div_dynm", + EIC7700_CLK_SPLL0_FOUT3, 0, + EIC7700_REG_OFFSET_VO_IESMCLK, 4, 4, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_VO_CEC_DYNM, "divider_vo_cec_div_dynm", + EIC7700_CLK_VPLL_FOUT2, 0, + EIC7700_REG_OFFSET_VO_PHY_CLK, 16, 16, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_CRYPTO_DYNM, "divider_crypto_div_dynm", + EIC7700_CLK_SPLL0_FOUT1, 0, + EIC7700_REG_OFFSET_SPACC, 4, 4, 0, ESWIN_PRIV_DIV_MIN_2), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_AON_RTC_DYNM, "divider_aon_rtc_div_dynm", + EIC7700_CLK_FIXED_FACTOR_CLK_1M_DIV24, 0, + EIC7700_REG_OFFSET_RTC, 21, 11, 0, + ESWIN_PRIV_DIV_MIN_2), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DSPT_ACLK, "gate_dspt_aclk", + EIC7700_CLK_DIV_DSP_ACLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_DSP_ACLK, 31, 0), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_DSP_0_ACLK_DYNM, + "divider_dsp_0_aclk_div_dynm", + EIC7700_CLK_GATE_DSPT_ACLK, 0, + EIC7700_REG_OFFSET_DSP_CFG, 19, 1, 0, 0), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_DSP_1_ACLK_DYNM, + "divider_dsp_1_aclk_div_dynm", + EIC7700_CLK_GATE_DSPT_ACLK, 0, + EIC7700_REG_OFFSET_DSP_CFG, 20, 1, 0, 0), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_DSP_2_ACLK_DYNM, + "divider_dsp_2_aclk_div_dynm", + EIC7700_CLK_GATE_DSPT_ACLK, 0, + EIC7700_REG_OFFSET_DSP_CFG, 21, 1, 0, 0), + ESWIN_DIV_TYPE(EIC7700_CLK_DIV_DSP_3_ACLK_DYNM, + "divider_dsp_3_aclk_div_dynm", + EIC7700_CLK_GATE_DSPT_ACLK, 0, + EIC7700_REG_OFFSET_DSP_CFG, 22, 1, 0, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_CPU_EXT_SRC_CORE_CLK_0, + "gate_clk_cpu_ext_src_core_clk_0", + EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_U84, 28, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_CPU_EXT_SRC_CORE_CLK_1, + "gate_clk_cpu_ext_src_core_clk_1", + EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_U84, 29, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_CPU_EXT_SRC_CORE_CLK_2, + "gate_clk_cpu_ext_src_core_clk_2", + EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_U84, 30, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_CPU_EXT_SRC_CORE_CLK_3, + "gate_clk_cpu_ext_src_core_clk_3", + EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_U84, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_CPU_TRACE_CLK_0, + "gate_clk_cpu_trace_clk_0", + EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_U84, 24, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_CPU_TRACE_CLK_1, + "gate_clk_cpu_trace_clk_1", + EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_U84, 25, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_CPU_TRACE_CLK_2, + "gate_clk_cpu_trace_clk_2", + EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_U84, 26, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_CPU_TRACE_CLK_3, + "gate_clk_cpu_trace_clk_3", + EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_U84, 27, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_NOC_NSP_CLK, "gate_noc_nsp_clk", + EIC7700_CLK_DIV_NOC_NSP_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_NOC, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_BOOTSPI, "gate_clk_bootspi", + EIC7700_CLK_MUX_BOOTSPI_CLK_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_BOOTSPI, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_BOOTSPI_CFG, "gate_clk_bootspi_cfg", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_BOOTSPI_CFGCLK, + 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_SCPU_CORE, "gate_clk_scpu_core", + EIC7700_CLK_MUX_SCPU_CORE_CLK_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_SCPU_CORE, 31, + 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_SCPU_BUS, "gate_clk_scpu_bus", + EIC7700_CLK_FIXED_FACTOR_SCPU_BUS_DIV2, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_SCPU_BUSCLK, 31, + 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LPCPU_CORE, "gate_clk_lpcpu_core", + EIC7700_CLK_MUX_LPCPU_CORE_CLK_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LPCPU_CORE, 31, + 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LPCPU_BUS, "gate_clk_lpcpu_bus", + EIC7700_CLK_FIXED_FACTOR_LPCPU_BUS_DIV2, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LPCPU_BUSCLK, + 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_GPU_ACLK, "gate_gpu_aclk", + EIC7700_CLK_DIV_GPU_ACLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_GPU_ACLK, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_GPU_CFG_CLK, "gate_gpu_cfg_clk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_GPU_CFG, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DSPT_CFG_CLK, "gate_dspt_cfg_clk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_DSP_CFG, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_D2D_ACLK, "gate_d2d_aclk", + EIC7700_CLK_DIV_D2D_ACLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_D2D_ACLK, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_D2D_CFG_CLK, "gate_d2d_cfg_clk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_D2D_CFG, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_TCU_ACLK, "gate_tcu_aclk", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_TCU_ACLK, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_TCU_CFG_CLK, "gate_tcu_cfg_clk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_TCU_CFG, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDRT_CFG_CLK, "gate_ddrt_cfg_clk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_DDR, 9, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDRT0_P0_ACLK, "gate_ddrt0_p0_aclk", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_DDR, 4, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDRT0_P1_ACLK, "gate_ddrt0_p1_aclk", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_DDR, 5, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDRT0_P2_ACLK, "gate_ddrt0_p2_aclk", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_DDR, 6, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDRT0_P3_ACLK, "gate_ddrt0_p3_aclk", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_DDR, 7, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDRT0_P4_ACLK, "gate_ddrt0_p4_aclk", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_DDR, 8, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDRT1_P0_ACLK, "gate_ddrt1_p0_aclk", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_DDR1, 4, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDRT1_P1_ACLK, "gate_ddrt1_p1_aclk", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_DDR1, 5, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDRT1_P2_ACLK, "gate_ddrt1_p2_aclk", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_DDR1, 6, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDRT1_P3_ACLK, "gate_ddrt1_p3_aclk", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_DDR1, 7, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDRT1_P4_ACLK, "gate_ddrt1_p4_aclk", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_DDR1, 8, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_ACLK, "gate_clk_hsp_aclk", + EIC7700_CLK_DIV_HSP_ACLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_HSP_ACLK, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_CFG_CLK, "gate_clk_hsp_cfg_clk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_HSP_CFG, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_PCIET_ACLK, "gate_pciet_aclk", + EIC7700_CLK_DIV_PCIE_ACLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_PCIE_ACLK, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_PCIET_CFG_CLK, "gate_pciet_cfg_clk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_PCIE_CFG, 31, + 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_PCIET_CR_CLK, "gate_pciet_cr_clk", + EIC7700_CLK_FIXED_FACTOR_PCIE_CR_DIV2, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_PCIE_CFG, 0, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_PCIET_AUX_CLK, "gate_pciet_aux_clk", + EIC7700_CLK_FIXED_FACTOR_PCIE_AUX_DIV4, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_PCIE_CFG, 1, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_NPU_ACLK, "gate_npu_aclk", + EIC7700_CLK_DIV_NPU_ACLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_NPU_ACLK, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_NPU_CFG_CLK, "gate_npu_cfg_clk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_NPU_ACLK, 30, + 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_NPU_LLC_ACLK, "gate_npu_llc_aclk", + EIC7700_CLK_MUX_NPU_LLCLK_3MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_NPU_LLC, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_NPU_CLK, "gate_npu_clk", + EIC7700_CLK_DIV_NPU_CORECLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_NPU_CORE, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_NPU_E31_CLK, "gate_npu_e31_clk", + EIC7700_CLK_DIV_NPU_E31_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_NPU_CORE, 30, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VI_ACLK, "gate_vi_aclk", + EIC7700_CLK_DIV_VI_ACLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VI_ACLK, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VI_CFG_CLK, "gate_vi_cfg_clk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VI_ACLK, 30, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VI_DIG_DW_CLK, "gate_vi_dig_dw_clk", + EIC7700_CLK_DIV_VI_DW_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VI_DWCLK, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VI_DVP_CLK, "gate_vi_dvp_clk", + EIC7700_CLK_DIV_VI_DVP_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VI_DVP, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VI_DIG_ISP_CLK, "gate_vi_dig_isp_clk", + EIC7700_CLK_DIV_VI_DIG_ISP_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VI_DIG_ISP, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VI_SHUTTER_0, "gate_vi_shutter_0", + EIC7700_CLK_DIV_VI_SHUTTER_DYNM_0, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VI_SHUTTER0, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VI_SHUTTER_1, "gate_vi_shutter_1", + EIC7700_CLK_DIV_VI_SHUTTER_DYNM_1, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VI_SHUTTER1, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VI_SHUTTER_2, "gate_vi_shutter_2", + EIC7700_CLK_DIV_VI_SHUTTER_DYNM_2, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VI_SHUTTER2, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VI_SHUTTER_3, "gate_vi_shutter_3", + EIC7700_CLK_DIV_VI_SHUTTER_DYNM_3, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VI_SHUTTER3, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VI_SHUTTER_4, "gate_vi_shutter_4", + EIC7700_CLK_DIV_VI_SHUTTER_DYNM_4, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VI_SHUTTER4, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VI_SHUTTER_5, "gate_vi_shutter_5", + EIC7700_CLK_DIV_VI_SHUTTER_DYNM_5, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VI_SHUTTER5, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VI_PHY_TXCLKESC, + "gate_vi_phy_txclkesc", + EIC7700_CLK_FIXED_FACTOR_MIPI_TXESC_DIV10, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VI_PHY, 0, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VO_ACLK, "gate_vo_aclk", + EIC7700_CLK_DIV_VO_ACLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VO_ACLK, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VO_CFG_CLK, "gate_vo_cfg_clk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VO_ACLK, 30, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VO_HDMI_IESMCLK, + "gate_vo_hdmi_iesmclk", EIC7700_CLK_DIV_IESMCLK_DYNM, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VO_IESMCLK, 31, + 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VO_PIXEL_CLK, "gate_vo_pixel_clk", + EIC7700_CLK_DIV_VO_PIXEL_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VO_PIXEL, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VO_I2S_MCLK, "gate_vo_i2s_mclk", + EIC7700_CLK_MUX_VO_MCLK_2MUX_EXT_MCLK, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VO_MCLK, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VO_CR_CLK, "gate_vo_cr_clk", + EIC7700_CLK_FIXED_FACTOR_MIPI_TXESC_DIV10, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VO_PHY_CLK, 1, + 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VC_ACLK, "gate_vc_aclk", + EIC7700_CLK_DIV_VC_ACLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VC_ACLK, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VC_CFG_CLK, "gate_vc_cfg_clk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VC_CLKEN, 0, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VC_JE_CLK, "gate_vc_je_clk", + EIC7700_CLK_DIV_JE_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_JE, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VC_JD_CLK, "gate_vc_jd_clk", + EIC7700_CLK_DIV_JD_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_JD, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VC_VE_CLK, "gate_vc_ve_clk", + EIC7700_CLK_DIV_VE_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VE, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VC_VD_CLK, "gate_vc_vd_clk", + EIC7700_CLK_DIV_VD_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_VD, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_G2D_CFG_CLK, "gate_g2d_cfg_clk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_G2D, 28, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_G2D_CLK, "gate_g2d_clk", + EIC7700_CLK_DIV_G2D_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_G2D, 30, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_G2D_ACLK, "gate_g2d_aclk", + EIC7700_CLK_DIV_G2D_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_G2D, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_AONDMA_CFG, "gate_clk_aondma_cfg", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_AON_DMA, 30, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_AONDMA_ACLK, "gate_aondma_aclk", + EIC7700_CLK_MUX_AONDMA_AXI2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_AON_DMA, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_AON_ACLK, "gate_aon_aclk", + EIC7700_CLK_MUX_AONDMA_AXI2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_AON_DMA, 29, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_TIMER_PCLK_0, "gate_timer_pclk_0", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_TIMER, 4, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_TIMER_PCLK_1, "gate_timer_pclk_1", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_TIMER, 5, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_TIMER_PCLK_2, "gate_timer_pclk_2", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_TIMER, 6, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_TIMER_PCLK_3, "gate_timer_pclk_3", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_TIMER, 7, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_TIMER3_CLK8, "gate_timer3_clk8", + EIC7700_CLK_VPLL_FOUT3, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_TIMER, 8, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_RTC_CFG, "gate_clk_rtc_cfg", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_RTC, 2, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_RTC, "gate_clk_rtc", + EIC7700_CLK_DIV_AON_RTC_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_RTC, 1, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_RMII_REF_0, "gate_hsp_rmii_ref_0", + EIC7700_CLK_MUX_RMII_REF_2MUX, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_ETH0, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_RMII_REF_1, "gate_hsp_rmii_ref_1", + EIC7700_CLK_MUX_RMII_REF_2MUX, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_ETH1, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_PKA_CFG, "gate_clk_pka_cfg", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_PKA, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_SPACC_CFG, "gate_clk_spacc_cfg", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_SPACC, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_CRYPTO, "gate_clk_crypto", + EIC7700_CLK_DIV_CRYPTO_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_SPACC, 30, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_TRNG_CFG, "gate_clk_trng_cfg", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_TRNG, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_OTP_CFG, "gate_clk_otp_cfg", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_OTP, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_0, "gate_clk_mailbox_0", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 0, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_1, "gate_clk_mailbox_1", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 1, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_2, "gate_clk_mailbox_2", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 2, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_3, "gate_clk_mailbox_3", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 3, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_4, "gate_clk_mailbox_4", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 4, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_5, "gate_clk_mailbox_5", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 5, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_6, "gate_clk_mailbox_6", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 6, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_7, "gate_clk_mailbox_7", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 7, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_8, "gate_clk_mailbox_8", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 8, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_9, "gate_clk_mailbox_9", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 9, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_10, "gate_clk_mailbox_10", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 10, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_11, "gate_clk_mailbox_11", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 11, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_12, "gate_clk_mailbox_12", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 12, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_13, "gate_clk_mailbox_13", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 13, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_14, "gate_clk_mailbox_14", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 14, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_MAILBOX_15, "gate_clk_mailbox_15", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN1, 15, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_I2C0_PCLK, "gate_i2c0_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 7, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_I2C1_PCLK, "gate_i2c1_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 8, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_I2C2_PCLK, "gate_i2c2_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 9, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_I2C3_PCLK, "gate_i2c3_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 10, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_I2C4_PCLK, "gate_i2c4_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 11, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_I2C5_PCLK, "gate_i2c5_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 12, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_I2C6_PCLK, "gate_i2c6_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 13, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_I2C7_PCLK, "gate_i2c7_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 14, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_I2C8_PCLK, "gate_i2c8_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 15, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_I2C9_PCLK, "gate_i2c9_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 16, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_WDT0_PCLK, "gate_lsp_wdt0_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 28, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_WDT1_PCLK, "gate_lsp_wdt1_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 29, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_WDT2_PCLK, "gate_lsp_wdt2_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 30, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_WDT3_PCLK, "gate_lsp_wdt3_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_SSI0_PCLK, "gate_lsp_ssi0_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 26, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_SSI1_PCLK, "gate_lsp_ssi1_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 27, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_UART0_PCLK, "gate_lsp_uart0_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_LSP_EN0, 17, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_UART1_PCLK, "gate_lsp_uart1_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 18, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_UART2_PCLK, "gate_lsp_uart2_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_LSP_EN0, 19, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_UART3_PCLK, "gate_lsp_uart3_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 20, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_UART4_PCLK, "gate_lsp_uart4_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 21, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_TIMER_PCLK, "gate_lsp_timer_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_REG_OFFSET_LSP_EN0, 25, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_FAN_PCLK, "gate_lsp_fan_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 0, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_PVT_PCLK, "gate_lsp_pvt_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_LSP_EN0, 1, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_PVT0_CLK, "gate_pvt0_clk", + EIC7700_CLK_FIXED_FACTOR_PVT_DIV20, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_LSP_EN1, 16, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_LSP_PVT1_CLK, "gate_pvt1_clk", + EIC7700_CLK_FIXED_FACTOR_PVT_DIV20, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_LSP_EN1, 17, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VC_JE_PCLK, "gate_vc_je_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VC_CLKEN, 2, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VC_JD_PCLK, "gate_vc_jd_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VC_CLKEN, 1, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VC_VE_PCLK, "gate_vc_ve_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VC_CLKEN, 5, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VC_VD_PCLK, "gate_vc_vd_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VC_CLKEN, 4, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_VC_MON_PCLK, "gate_vc_mon_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_VC_CLKEN, 3, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_MSHC0_CORE_CLK, + "gate_hsp_mshc0_core_clk", + EIC7700_CLK_DIV_MSHC_CORE_DYNM_0, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_MSHC0_CORE, 16, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_MSHC1_CORE_CLK, + "gate_hsp_mshc1_core_clk", + EIC7700_CLK_DIV_MSHC_CORE_DYNM_1, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_MSHC1_CORE, 16, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_MSHC2_CORE_CLK, + "gate_hsp_mshc2_core_clk", + EIC7700_CLK_DIV_MSHC_CORE_DYNM_2, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_MSHC2_CORE, 16, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_SATA_RBC_CLK, + "gate_hsp_sata_rbc_clk", EIC7700_CLK_SPLL1_FOUT2, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_SATA_RBC, + 0, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_SATA_OOB_CLK, + "gate_hsp_sata_oob_clk", EIC7700_CLK_MUX_SATA_PHY_2MUX1, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_SATA_OOB, 31, + 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_DMA0_CLK_TEST, + "gate_hsp_dma0_clk_test", EIC7700_CLK_GATE_HSP_ACLK, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_HSP_ACLK, 1, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_DMA0_CLK, "gate_hsp_dma0_clk", + EIC7700_CLK_GATE_HSP_ACLK, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_HSP_ACLK, 0, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_ETH0_CORE_CLK, + "gate_hsp_eth0_core_clk", + EIC7700_CLK_DIV_ETH_TXCLK_DYNM_0, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_ETH0, 0, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_HSP_ETH1_CORE_CLK, + "gate_hsp_eth1_core_clk", + EIC7700_CLK_DIV_ETH_TXCLK_DYNM_1, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_ETH1, 0, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_AON_I2C0_PCLK, "gate_aon_i2c0_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_I2C0, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_AON_I2C1_PCLK, "gate_aon_i2c1_pclk", + EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE, + CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_I2C1, 31, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDR0_TRACE, "gate_ddr0_trace", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_DDR, 0, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_DDR1_TRACE, "gate_ddr1_trace", + EIC7700_CLK_DIV_DDR_ACLK_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_DDR1, 0, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_RNOC_NSP, "gate_rnoc_nsp", + EIC7700_CLK_DIV_NOC_NSP_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_NOC, 29, 0), + ESWIN_GATE_TYPE(EIC7700_CLK_GATE_NOC_WDREF, "gate_noc_wdref", + EIC7700_CLK_DIV_NOC_WDREF_DYNM, CLK_SET_RATE_PARENT, + EIC7700_REG_OFFSET_NOC, 30, 0), +}; + +/* + * This clock notifier is called when the rate of clk_pll_cpu clock is to be + * changed. The mux_cpu_root_3mux1_gfree clock should save the current parent + * clock and switch its parent clock to fixed_factor_u84_core_lp_div2 before + * clk_pll_cpu rate will be changed. Then switch its parent clock back after + * the clk_pll_cpu rate is completed. + */ +static int eic7700_clk_pll_cpu_notifier_cb(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct eswin_clock_data *pdata; + struct clk_hw *mux_clk; + struct clk_hw *lp_clk; + int ret = 0; + + pdata = container_of(nb, struct eswin_clock_data, pll_nb); + + mux_clk = &eic7700_mux_clks[0].hw; + lp_clk = &eic7700_early_clks[11].hw; + + if (action == PRE_RATE_CHANGE) { + pdata->original_clk = clk_hw_get_parent(mux_clk); + ret = clk_hw_set_parent(mux_clk, lp_clk); + } else if (action == POST_RATE_CHANGE) { + ret = clk_hw_set_parent(mux_clk, pdata->original_clk); + } + + return notifier_from_errno(ret); +} + +static int eic7700_clk_probe(struct platform_device *pdev) +{ + struct eswin_clock_data *clk_data; + struct device *dev = &pdev->dev; + struct clk *pll_clk; + int ret; + + clk_data = eswin_clk_init(pdev, EIC7700_NR_CLKS); + if (IS_ERR(clk_data)) + return dev_err_probe(dev, PTR_ERR(clk_data), + "failed to get clk data!\n"); + + ret = eswin_clk_register_fixed_rate(dev, eic7700_fixed_rate_clks, + ARRAY_SIZE(eic7700_fixed_rate_clks), + clk_data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register fixed rate clock\n"); + + ret = eswin_clk_register_pll(dev, eic7700_pll_clks, + ARRAY_SIZE(eic7700_pll_clks), + clk_data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register pll clock\n"); + + pll_clk = devm_clk_hw_get_clk(dev, &eic7700_pll_clks[1].hw, + "clk_pll_cpu"); + if (IS_ERR(pll_clk)) + return dev_err_probe(dev, PTR_ERR(pll_clk), + "failed to get clk_pll_cpu\n"); + + clk_data->pll_nb.notifier_call = eic7700_clk_pll_cpu_notifier_cb; + ret = devm_clk_notifier_register(dev, pll_clk, &clk_data->pll_nb); + if (ret) + return ret; + + ret = eswin_clk_register_fixed_factor(dev, eic7700_factor_clks, + ARRAY_SIZE(eic7700_factor_clks), + clk_data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register fixed factor clock\n"); + + ret = eswin_clk_register_divider(dev, eic7700_div_clks, + ARRAY_SIZE(eic7700_div_clks), + clk_data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register divider clock\n"); + + ret = eswin_clk_register_gate(dev, eic7700_gate_clks, + ARRAY_SIZE(eic7700_gate_clks), clk_data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register gate clock\n"); + + ret = eswin_clk_register_clks(dev, eic7700_early_clks, + ARRAY_SIZE(eic7700_early_clks), clk_data); + if (ret) + return dev_err_probe(dev, ret, "failed to register clock\n"); + + ret = eswin_clk_register_mux(dev, eic7700_mux_clks, + ARRAY_SIZE(eic7700_mux_clks), clk_data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register mux clock\n"); + + ret = eswin_clk_register_clks(dev, eic7700_clks, + ARRAY_SIZE(eic7700_clks), clk_data); + if (ret) + return dev_err_probe(dev, ret, "failed to register clock\n"); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &clk_data->clk_data); +} + +static const struct of_device_id eic7700_clock_dt_ids[] = { + { .compatible = "eswin,eic7700-clock", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, eic7700_clock_dt_ids); + +static struct platform_driver eic7700_clock_driver = { + .probe = eic7700_clk_probe, + .driver = { + .name = "eic7700-clock", + .of_match_table = eic7700_clock_dt_ids, + }, +}; +module_platform_driver(eic7700_clock_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yifeng Huang "); +MODULE_AUTHOR("Xuyang Dong "); +MODULE_DESCRIPTION("ESWIN EIC7700 clock controller driver"); diff --git a/drivers/clk/eswin/clk.c b/drivers/clk/eswin/clk.c new file mode 100644 index 000000000000..e09a52cc3587 --- /dev/null +++ b/drivers/clk/eswin/clk.c @@ -0,0 +1,586 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * Authors: + * Yifeng Huang + * Xuyang Dong + */ + +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#define PLL_EN_MASK GENMASK(1, 0) +#define PLL_REFDIV_MASK GENMASK(17, 12) +#define PLL_FBDIV_MASK GENMASK(31, 20) +#define PLL_FRAC_MASK GENMASK(27, 4) +#define PLL_POSTDIV1_MASK GENMASK(10, 8) +#define PLL_POSTDIV2_MASK GENMASK(18, 16) + +struct eswin_clock_data *eswin_clk_init(struct platform_device *pdev, + size_t nr_clks) +{ + struct eswin_clock_data *eclk_data; + + eclk_data = devm_kzalloc(&pdev->dev, + struct_size(eclk_data, clk_data.hws, nr_clks), + GFP_KERNEL); + if (!eclk_data) + return ERR_PTR(-ENOMEM); + + eclk_data->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(eclk_data->base)) + return ERR_PTR(-EINVAL); + + eclk_data->clk_data.num = nr_clks; + spin_lock_init(&eclk_data->lock); + + return eclk_data; +} +EXPORT_SYMBOL_GPL(eswin_clk_init); + +/** + * eswin_calc_pll - calculate PLL values + * @frac_val: fractional divider + * @fbdiv_val: feedback divider + * @rate: reference rate + * @parent_rate: parent rate + * + * Calculate PLL values for frac and fbdiv: + * fbdiv = rate * 4 / parent_rate + * frac = (rate * 4 % parent_rate * (2 ^ 24)) / parent_rate + */ +static void eswin_calc_pll(u32 *frac_val, u32 *fbdiv_val, unsigned long rate, + unsigned long parent_rate) +{ + u32 rem; + u64 tmp; + + /* step 1: rate * 4 */ + tmp = rate * 4; + /* step 2: use do_div() to get the quotient(tmp) and remainder(rem) */ + rem = do_div(tmp, (u32)parent_rate); + /* fbdiv = rate * 4 / parent_rate */ + *fbdiv_val = (u32)tmp; + /* + * step 3: rem << 24 + * 24: 24-bit fractional accuracy + */ + tmp = (u64)rem << 24; + /* step 4: use do_div() to get the quotient(tmp) */ + do_div(tmp, (u32)parent_rate); + /* frac = (rate * 4 % parent_rate * (2 ^ 24)) / parent_rate */ + *frac_val = (u32)tmp; +} + +static inline struct eswin_clk_pll *to_pll_clk(struct clk_hw *hw) +{ + return container_of(hw, struct eswin_clk_pll, hw); +} + +static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct eswin_clk_pll *clk = to_pll_clk(hw); + u32 frac_val, fbdiv_val, val, mask; + int ret; + + eswin_calc_pll(&frac_val, &fbdiv_val, rate, parent_rate); + + /* First, disable pll */ + val = readl_relaxed(clk->ctrl_reg0); + val &= ~PLL_EN_MASK; + val |= FIELD_PREP(PLL_EN_MASK, 0); + writel_relaxed(val, clk->ctrl_reg0); + + val = readl_relaxed(clk->ctrl_reg0); + val &= ~(PLL_REFDIV_MASK | PLL_FBDIV_MASK); + val |= FIELD_PREP(PLL_FBDIV_MASK, fbdiv_val); + val |= FIELD_PREP(PLL_REFDIV_MASK, 1); + writel_relaxed(val, clk->ctrl_reg0); + + val = readl_relaxed(clk->ctrl_reg1); + val &= ~PLL_FRAC_MASK; + val |= FIELD_PREP(PLL_FRAC_MASK, frac_val); + writel_relaxed(val, clk->ctrl_reg1); + + val = readl_relaxed(clk->ctrl_reg2); + val &= ~(PLL_POSTDIV1_MASK | PLL_POSTDIV2_MASK); + val |= FIELD_PREP(PLL_POSTDIV1_MASK, 1); + val |= FIELD_PREP(PLL_POSTDIV2_MASK, 1); + writel_relaxed(val, clk->ctrl_reg2); + + /* Last, enable pll */ + val = readl_relaxed(clk->ctrl_reg0); + val &= ~PLL_EN_MASK; + val |= FIELD_PREP(PLL_EN_MASK, 1); + writel_relaxed(val, clk->ctrl_reg0); + + /* Usually the pll will lock in 50us */ + mask = GENMASK(clk->lock_shift + clk->lock_width - 1, clk->lock_shift); + ret = readl_poll_timeout(clk->status_reg, val, val & mask, 1, 50 * 2); + if (ret) + pr_err("failed to lock the pll!\n"); + + return ret; +} + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct eswin_clk_pll *clk = to_pll_clk(hw); + u64 fbdiv_val, frac_val, tmp; + u32 rem, val; + + val = readl_relaxed(clk->ctrl_reg0); + val &= PLL_FBDIV_MASK; + fbdiv_val = (val >> clk->fbdiv_shift); + + val = readl_relaxed(clk->ctrl_reg1); + val &= PLL_FRAC_MASK; + frac_val = (val >> clk->frac_shift); + + /* rate = 24000000 * (fbdiv + frac / (2 ^ 24)) / 4 */ + tmp = parent_rate * frac_val; + rem = do_div(tmp, BIT(24)); + if (rem) + tmp = parent_rate * fbdiv_val + tmp + 1; + else + tmp = parent_rate * fbdiv_val + tmp; + + do_div(tmp, 4); + + return tmp; +} + +static int clk_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct eswin_clk_pll *clk = to_pll_clk(hw); + + req->rate = clamp(req->rate, clk->min_rate, clk->max_rate); + req->min_rate = clk->min_rate; + req->max_rate = clk->max_rate; + + return 0; +} + +int eswin_clk_register_fixed_rate(struct device *dev, + struct eswin_fixed_rate_clock *clks, + int nums, struct eswin_clock_data *data) +{ + struct clk_hw *clk_hw; + int i; + + for (i = 0; i < nums; i++) { + clk_hw = devm_clk_hw_register_fixed_rate(dev, clks[i].name, + NULL, clks[i].flags, + clks[i].rate); + if (IS_ERR(clk_hw)) + return PTR_ERR(clk_hw); + + clks[i].hw = *clk_hw; + data->clk_data.hws[clks[i].id] = clk_hw; + } + + return 0; +} +EXPORT_SYMBOL_GPL(eswin_clk_register_fixed_rate); + +static const struct clk_ops eswin_clk_pll_ops = { + .set_rate = clk_pll_set_rate, + .recalc_rate = clk_pll_recalc_rate, + .determine_rate = clk_pll_determine_rate, +}; + +int eswin_clk_register_pll(struct device *dev, struct eswin_pll_clock *clks, + int nums, struct eswin_clock_data *data) +{ + struct eswin_clk_pll *p_clk = NULL; + struct clk_init_data init; + struct clk_hw *clk_hw; + int i, ret; + + p_clk = devm_kzalloc(dev, sizeof(*p_clk) * nums, GFP_KERNEL); + if (!p_clk) + return -ENOMEM; + + for (i = 0; i < nums; i++) { + p_clk->id = clks[i].id; + p_clk->ctrl_reg0 = data->base + clks[i].ctrl_reg0; + p_clk->fbdiv_shift = clks[i].fbdiv_shift; + + p_clk->ctrl_reg1 = data->base + clks[i].ctrl_reg1; + p_clk->frac_shift = clks[i].frac_shift; + + p_clk->ctrl_reg2 = data->base + clks[i].ctrl_reg2; + + p_clk->status_reg = data->base + clks[i].status_reg; + p_clk->lock_shift = clks[i].lock_shift; + p_clk->lock_width = clks[i].lock_width; + + p_clk->max_rate = clks[i].max_rate; + p_clk->min_rate = clks[i].min_rate; + + init.name = clks[i].name; + init.flags = 0; + init.parent_data = clks[i].parent_data; + init.num_parents = 1; + init.ops = &eswin_clk_pll_ops; + p_clk->hw.init = &init; + + clk_hw = &p_clk->hw; + + ret = devm_clk_hw_register(dev, clk_hw); + if (ret) + return ret; + + clks[i].hw = *clk_hw; + data->clk_data.hws[clks[i].id] = clk_hw; + p_clk++; + } + + return 0; +} +EXPORT_SYMBOL_GPL(eswin_clk_register_pll); + +int eswin_clk_register_fixed_factor(struct device *dev, + struct eswin_fixed_factor_clock *clks, + int nums, struct eswin_clock_data *data) +{ + struct clk_hw *clk_hw; + int i; + + for (i = 0; i < nums; i++) { + clk_hw = devm_clk_hw_register_fixed_factor_index(dev, clks[i].name, + clks[i].parent_data->index, + clks[i].flags, clks[i].mult, + clks[i].div); + + if (IS_ERR(clk_hw)) + return PTR_ERR(clk_hw); + + clks[i].hw = *clk_hw; + data->clk_data.hws[clks[i].id] = clk_hw; + } + + return 0; +} +EXPORT_SYMBOL_GPL(eswin_clk_register_fixed_factor); + +int eswin_clk_register_mux(struct device *dev, struct eswin_mux_clock *clks, + int nums, struct eswin_clock_data *data) +{ + struct clk_hw *clk_hw; + int i; + + for (i = 0; i < nums; i++) { + clk_hw = devm_clk_hw_register_mux_parent_data_table(dev, clks[i].name, + clks[i].parent_data, + clks[i].num_parents, + clks[i].flags, + data->base + clks[i].reg, + clks[i].shift, clks[i].width, + clks[i].mux_flags, + clks[i].table, &data->lock); + + if (IS_ERR(clk_hw)) + return PTR_ERR(clk_hw); + + clks[i].hw = *clk_hw; + data->clk_data.hws[clks[i].id] = clk_hw; + } + + return 0; +} +EXPORT_SYMBOL_GPL(eswin_clk_register_mux); + +static unsigned int _eswin_get_val(unsigned int div, unsigned long flags, + u8 width) +{ + unsigned int maxdiv; + + maxdiv = clk_div_mask(width); + div = div > maxdiv ? maxdiv : div; + + if (flags & ESWIN_PRIV_DIV_MIN_2) + return (div < 2) ? 2 : div; + + return div; +} + +static unsigned int eswin_div_get_val(unsigned long rate, + unsigned long parent_rate, u8 width, + unsigned long flags) +{ + unsigned int div; + + div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); + + return _eswin_get_val(div, flags, width); +} + +static inline struct eswin_divider_clock *to_div_clk(struct clk_hw *hw) +{ + return container_of(hw, struct eswin_divider_clock, hw); +} + +static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct eswin_divider_clock *dclk = to_div_clk(hw); + unsigned long flags; + unsigned int value; + u32 val; + + value = eswin_div_get_val(rate, parent_rate, dclk->width, + dclk->priv_flag); + + spin_lock_irqsave(dclk->lock, flags); + + val = readl_relaxed(dclk->ctrl_reg); + val &= ~(clk_div_mask(dclk->width) << dclk->shift); + val |= (u32)value << dclk->shift; + writel_relaxed(val, dclk->ctrl_reg); + + spin_unlock_irqrestore(dclk->lock, flags); + + return 0; +} + +static unsigned long clk_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct eswin_divider_clock *dclk = to_div_clk(hw); + unsigned int div, val; + + val = readl_relaxed(dclk->ctrl_reg) >> dclk->shift; + val &= clk_div_mask(dclk->width); + div = _eswin_get_val(val, dclk->priv_flag, dclk->width); + + return DIV_ROUND_UP_ULL((u64)parent_rate, div); +} + +static int eswin_clk_bestdiv(unsigned long rate, + unsigned long best_parent_rate, u8 width, + unsigned long flags) +{ + unsigned long bestdiv, up_rate, down_rate; + int up, down; + + if (!rate) + rate = 1; + + /* closest round */ + up = DIV_ROUND_UP_ULL((u64)best_parent_rate, rate); + down = best_parent_rate / rate; + + up_rate = DIV_ROUND_UP_ULL((u64)best_parent_rate, up); + down_rate = DIV_ROUND_UP_ULL((u64)best_parent_rate, down); + + bestdiv = (rate - up_rate) <= (down_rate - rate) ? up : down; + + return bestdiv; +} + +static int clk_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct eswin_divider_clock *dclk = to_div_clk(hw); + int div; + + div = eswin_clk_bestdiv(req->rate, req->best_parent_rate, dclk->width, + dclk->priv_flag); + div = _eswin_get_val(div, dclk->priv_flag, dclk->width); + req->rate = DIV_ROUND_UP_ULL((u64)req->best_parent_rate, div); + + return 0; +} + +static const struct clk_ops eswin_clk_div_ops = { + .set_rate = clk_div_set_rate, + .recalc_rate = clk_div_recalc_rate, + .determine_rate = clk_div_determine_rate, +}; + +struct clk_hw *eswin_register_clkdiv(struct device *dev, unsigned int id, + const char *name, + const struct clk_hw *parent_hw, + unsigned long flags, void __iomem *reg, + u8 shift, u8 width, + unsigned long clk_divider_flags, + unsigned long priv_flag, spinlock_t *lock) +{ + struct eswin_divider_clock *dclk; + struct clk_init_data init; + struct clk_hw *clk_hw; + int ret; + + dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL); + if (!dclk) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &eswin_clk_div_ops; + init.flags = flags; + init.parent_hws = &parent_hw; + init.num_parents = 1; + + /* struct clk_divider assignments */ + dclk->id = id; + dclk->ctrl_reg = reg; + dclk->shift = shift; + dclk->width = width; + dclk->div_flags = clk_divider_flags; + dclk->priv_flag = priv_flag; + dclk->lock = lock; + dclk->hw.init = &init; + + /* register the clock */ + clk_hw = &dclk->hw; + ret = devm_clk_hw_register(dev, clk_hw); + if (ret) { + dev_err(dev, "failed to register divider clock!\n"); + return ERR_PTR(ret); + } + + return clk_hw; +} +EXPORT_SYMBOL_GPL(eswin_register_clkdiv); + +int eswin_clk_register_divider(struct device *dev, + struct eswin_divider_clock *clks, + int nums, struct eswin_clock_data *data) +{ + struct clk_hw *clk_hw; + int i; + + for (i = 0; i < nums; i++) { + clk_hw = devm_clk_hw_register_divider_parent_data(dev, clks[i].name, + clks[i].parent_data, + clks[i].flags, + data->base + clks[i].reg, + clks[i].shift, clks[i].width, + clks[i].div_flags, &data->lock); + + if (IS_ERR(clk_hw)) + return PTR_ERR(clk_hw); + + clks[i].hw = *clk_hw; + data->clk_data.hws[clks[i].id] = clk_hw; + } + + return 0; +} +EXPORT_SYMBOL_GPL(eswin_clk_register_divider); + +int eswin_clk_register_gate(struct device *dev, struct eswin_gate_clock *clks, + int nums, struct eswin_clock_data *data) +{ + struct clk_hw *clk_hw; + int i; + + for (i = 0; i < nums; i++) { + clk_hw = devm_clk_hw_register_gate_parent_data(dev, clks[i].name, + clks[i].parent_data, + clks[i].flags, + data->base + clks[i].reg, + clks[i].bit_idx, clks[i].gate_flags, + &data->lock); + + if (IS_ERR(clk_hw)) + return PTR_ERR(clk_hw); + + clks[i].hw = *clk_hw; + data->clk_data.hws[clks[i].id] = clk_hw; + } + + return 0; +} +EXPORT_SYMBOL_GPL(eswin_clk_register_gate); + +int eswin_clk_register_clks(struct device *dev, struct eswin_clk_info *clks, + int nums, struct eswin_clock_data *data) +{ + struct eswin_clk_info *info; + const struct clk_hw *phw = NULL; + struct clk_hw *hw; + int i; + + for (i = 0; i < nums; i++) { + info = &clks[i]; + switch (info->type) { + case CLK_FIXED_FACTOR: { + const struct eswin_fixed_factor_clock *factor; + + factor = &info->data.factor; + phw = data->clk_data.hws[info->pid]; + hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, factor->name, phw, + factor->flags, + factor->mult, + factor->div); + break; + } + case CLK_MUX: { + const struct eswin_mux_clock *mux = &info->data.mux; + + hw = devm_clk_hw_register_mux_parent_data_table(dev, mux->name, + mux->parent_data, + mux->num_parents, + mux->flags, + data->base + mux->reg, + mux->shift, mux->width, + mux->mux_flags, + mux->table, &data->lock); + break; + } + case CLK_DIVIDER: { + const struct eswin_divider_clock *div = &info->data.div; + + phw = data->clk_data.hws[info->pid]; + if (div->priv_flag) + hw = eswin_register_clkdiv(dev, div->id, div->name, phw, + div->flags, data->base + div->reg, + div->shift, div->width, div->div_flags, + div->priv_flag, &data->lock); + else + hw = devm_clk_hw_register_divider_parent_hw(dev, div->name, phw, + div->flags, + data->base + div->reg, + div->shift, div->width, + div->div_flags, + &data->lock); + break; + } + case CLK_GATE: { + const struct eswin_gate_clock *gate = &info->data.gate; + + phw = data->clk_data.hws[info->pid]; + hw = devm_clk_hw_register_gate_parent_hw(dev, gate->name, phw, + gate->flags, + data->base + gate->reg, + gate->bit_idx, gate->gate_flags, + &data->lock); + break; + } + default: + dev_err(dev, "Unidentifiable clock type!\n"); + return -EINVAL; + } + if (IS_ERR(hw)) + return PTR_ERR(hw); + + info->hw = *hw; + data->clk_data.hws[info->id] = hw; + } + + return 0; +} +EXPORT_SYMBOL_GPL(eswin_clk_register_clks); diff --git a/drivers/clk/eswin/common.h b/drivers/clk/eswin/common.h new file mode 100644 index 000000000000..d8e5e6545894 --- /dev/null +++ b/drivers/clk/eswin/common.h @@ -0,0 +1,340 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * Authors: + * Yifeng Huang + * Xuyang Dong + */ + +#ifndef __ESWIN_COMMON_H__ +#define __ESWIN_COMMON_H__ + +#define APLL_HIGH_FREQ 983040000 +#define APLL_LOW_FREQ 225792000 +#define PLL_HIGH_FREQ 1800000000 +#define PLL_LOW_FREQ 24000000 + +/* + * ESWIN_PRIV_DIV_MIN_2: If ESWIN_PRIV_DIV_MIN_2 is set, the minimum value of + * the register is 2, i.e. the minimum division ratio is 2. + */ +#define ESWIN_PRIV_DIV_MIN_2 BIT(0) + +enum eswin_clk_type { + CLK_FIXED_FACTOR, + CLK_MUX, + CLK_DIVIDER, + CLK_GATE, +}; + +struct eswin_clock_data { + void __iomem *base; + struct clk_hw *original_clk; + struct notifier_block pll_nb; + spinlock_t lock; /* protect register read-modify-write cycle */ + struct clk_hw_onecell_data clk_data; +}; + +struct eswin_divider_clock { + struct clk_hw hw; + unsigned int id; + const char *name; + const struct clk_parent_data *parent_data; + void __iomem *ctrl_reg; /* register address of the divider clock */ + unsigned long flags; + unsigned long reg; /* register offset */ + u8 shift; + u8 width; + unsigned long div_flags; + unsigned long priv_flag; + spinlock_t *lock; /* protect register read-modify-write cycle */ +}; + +struct eswin_fixed_rate_clock { + struct clk_hw hw; + unsigned int id; + const char *name; + unsigned long flags; + unsigned long rate; +}; + +struct eswin_fixed_factor_clock { + struct clk_hw hw; + unsigned int id; + const char *name; + const struct clk_parent_data *parent_data; + unsigned long mult; + unsigned long div; + unsigned long flags; +}; + +struct eswin_gate_clock { + struct clk_hw hw; + unsigned int id; + const char *name; + const struct clk_parent_data *parent_data; + unsigned long flags; + unsigned long reg; + u8 bit_idx; + u8 gate_flags; +}; + +struct eswin_mux_clock { + struct clk_hw hw; + unsigned int id; + const char *name; + const struct clk_parent_data *parent_data; + u8 num_parents; + unsigned long flags; + unsigned long reg; + u8 shift; + u8 width; + u8 mux_flags; + u32 *table; +}; + +struct eswin_pll_clock { + struct clk_hw hw; + u32 id; + const char *name; + const struct clk_parent_data *parent_data; + const u32 ctrl_reg0; + const u8 fbdiv_shift; + + const u32 ctrl_reg1; + const u8 frac_shift; + + const u32 ctrl_reg2; + + const u32 status_reg; + const u8 lock_shift; + const u8 lock_width; + + const u64 max_rate; + const u64 min_rate; +}; + +struct eswin_clk_pll { + struct clk_hw hw; + u32 id; + void __iomem *ctrl_reg0; + u8 fbdiv_shift; + + void __iomem *ctrl_reg1; + u8 frac_shift; + + void __iomem *ctrl_reg2; + + void __iomem *status_reg; + u8 lock_shift; + u8 lock_width; + + u64 max_rate; + u64 min_rate; +}; + +struct eswin_clk_info { + unsigned int type; + unsigned int pid; + unsigned int id; + struct clk_hw hw; + union { + struct eswin_divider_clock div; + struct eswin_fixed_factor_clock factor; + struct eswin_gate_clock gate; + struct eswin_mux_clock mux; + } data; +}; + +struct eswin_clock_data *eswin_clk_init(struct platform_device *pdev, + size_t nr_clks); +int eswin_clk_register_fixed_rate(struct device *dev, + struct eswin_fixed_rate_clock *clks, + int nums, struct eswin_clock_data *data); +int eswin_clk_register_pll(struct device *dev, struct eswin_pll_clock *clks, + int nums, struct eswin_clock_data *data); +int eswin_clk_register_fixed_factor(struct device *dev, + struct eswin_fixed_factor_clock *clks, + int nums, struct eswin_clock_data *data); +int eswin_clk_register_mux(struct device *dev, struct eswin_mux_clock *clks, + int nums, struct eswin_clock_data *data); +int eswin_clk_register_divider(struct device *dev, + struct eswin_divider_clock *clks, + int nums, struct eswin_clock_data *data); +int eswin_clk_register_gate(struct device *dev, struct eswin_gate_clock *clks, + int nums, struct eswin_clock_data *data); +int eswin_clk_register_clks(struct device *dev, struct eswin_clk_info *clks, + int nums, struct eswin_clock_data *data); +struct clk_hw *eswin_register_clkdiv(struct device *dev, unsigned int id, + const char *name, + const struct clk_hw *parent_hw, + unsigned long flags, void __iomem *reg, + u8 shift, u8 width, + unsigned long clk_divider_flags, + unsigned long priv_flag, spinlock_t *lock); + +#define ESWIN_DIV(_id, _name, _pdata, _flags, _reg, _shift, _width, \ + _dflags, _pflag) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_data = _pdata, \ + .flags = _flags, \ + .reg = _reg, \ + .shift = _shift, \ + .width = _width, \ + .div_flags = _dflags, \ + .priv_flag = _pflag, \ + } + +#define ESWIN_DIV_TYPE(_id, _name, _pid, _flags, _reg, _shift, _width, \ + _dflags, _pflag) \ + { \ + .type = CLK_DIVIDER, \ + .pid = _pid, \ + .id = _id, \ + .data = { \ + .div = { \ + .name = _name, \ + .flags = _flags, \ + .reg = _reg, \ + .shift = _shift, \ + .width = _width, \ + .div_flags = _dflags, \ + .priv_flag = _pflag, \ + }, \ + }, \ + } + +#define ESWIN_FACTOR(_id, _name, _pdata, _mult, _div, _flags) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_data = _pdata, \ + .mult = _mult, \ + .div = _div, \ + .flags = _flags, \ + } + +#define ESWIN_FACTOR_TYPE(_id, _name, _pid, _mult, _div, _flags) \ + { \ + .type = CLK_FIXED_FACTOR, \ + .pid = _pid, \ + .id = _id, \ + .data = { \ + .factor = { \ + .name = _name, \ + .mult = _mult, \ + .div = _div, \ + .flags = _flags, \ + }, \ + }, \ + } + +#define ESWIN_FIXED(_id, _name, _flags, _rate) \ + { \ + .id = _id, \ + .name = _name, \ + .flags = _flags, \ + .rate = _rate, \ + } + +#define ESWIN_GATE(_id, _name, _pdata, _flags, _reg, _idx, _gflags) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_data = _pdata, \ + .flags = _flags, \ + .reg = _reg, \ + .bit_idx = _idx, \ + .gate_flags = _gflags, \ + } + +#define ESWIN_GATE_TYPE(_id, _name, _pid, _flags, _reg, _idx, _gflags) \ + { \ + .type = CLK_GATE, \ + .pid = _pid, \ + .id = _id, \ + .data = { \ + .gate = { \ + .name = _name, \ + .flags = _flags, \ + .reg = _reg, \ + .bit_idx = _idx, \ + .gate_flags = _gflags, \ + }, \ + }, \ + } + +#define ESWIN_MUX(_id, _name, _pdata, _num_parents, _flags, _reg, \ + _shift, _width, _mflags) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_data = _pdata, \ + .num_parents = _num_parents, \ + .flags = _flags, \ + .reg = _reg, \ + .shift = _shift, \ + .width = _width, \ + .mux_flags = _mflags, \ + .table = NULL, \ + } + +#define ESWIN_MUX_TBL(_id, _name, _pdata, _num_parents, _flags, _reg, \ + _shift, _width, _mflags, _table) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_data = _pdata, \ + .num_parents = _num_parents, \ + .flags = _flags, \ + .reg = _reg, \ + .shift = _shift, \ + .width = _width, \ + .mux_flags = _mflags, \ + .table = _table, \ + } + +#define ESWIN_MUX_TYPE(_id, _name, _pdata, _num_parents, _flags, _reg, \ + _shift, _width, _mflags, _table) \ + { \ + .type = CLK_MUX, \ + .id = _id, \ + .data = { \ + .mux = { \ + .name = _name, \ + .parent_data = _pdata, \ + .num_parents = _num_parents, \ + .flags = _flags, \ + .reg = _reg, \ + .shift = _shift, \ + .width = _width, \ + .mux_flags = _mflags, \ + .table = _table, \ + }, \ + }, \ + } + +#define ESWIN_PLL(_id, _name, _pdata, _reg0, _fb_shift, _reg1, \ + _frac_shift, _reg2, _reg, _lock_shift, _lock_width, \ + _max_rate, _min_rate) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_data = _pdata, \ + .ctrl_reg0 = _reg0, \ + .fbdiv_shift = _fb_shift, \ + .ctrl_reg1 = _reg1, \ + .frac_shift = _frac_shift, \ + .ctrl_reg2 = _reg2, \ + .status_reg = _reg, \ + .lock_shift = _lock_shift, \ + .lock_width = _lock_width, \ + .max_rate = _max_rate, \ + .min_rate = _min_rate, \ + } + +#endif /* __ESWIN_COMMON_H__ */ diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c index 89ed7749bf47..4048c16c0578 100644 --- a/drivers/clk/imx/clk-fracn-gppll.c +++ b/drivers/clk/imx/clk-fracn-gppll.c @@ -85,9 +85,11 @@ static const struct imx_fracn_gppll_rate_table fracn_tbl[] = { PLL_FRACN_GP(519750000U, 173, 25, 100, 1, 8), PLL_FRACN_GP(498000000U, 166, 0, 1, 0, 8), PLL_FRACN_GP(484000000U, 121, 0, 1, 0, 6), + PLL_FRACN_GP(477400000U, 119, 35, 100, 0, 6), PLL_FRACN_GP(445333333U, 167, 0, 1, 0, 9), PLL_FRACN_GP(400000000U, 200, 0, 1, 0, 12), PLL_FRACN_GP(393216000U, 163, 84, 100, 0, 10), + PLL_FRACN_GP(333333333U, 125, 0, 1, 1, 9), PLL_FRACN_GP(332600000U, 138, 584, 1000, 0, 10), PLL_FRACN_GP(300000000U, 150, 0, 1, 0, 12), PLL_FRACN_GP(241900000U, 201, 584, 1000, 0, 20), diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index f726c00aba72..35e6b59c01db 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -188,9 +188,11 @@ static void of_assigned_ldb_sels(struct device_node *node, } if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) { pr_err("ccm: parent clock %d not in ccm\n", index); + of_node_put(clkspec.np); return; } parent = clkspec.args[0]; + of_node_put(clkspec.np); rc = of_parse_phandle_with_args(node, "assigned-clocks", "#clock-cells", index, &clkspec); @@ -198,9 +200,11 @@ static void of_assigned_ldb_sels(struct device_node *node, return; if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) { pr_err("ccm: child clock %d not in ccm\n", index); + of_node_put(clkspec.np); return; } child = clkspec.args[0]; + of_node_put(clkspec.np); if (child != IMX6QDL_CLK_LDB_DI0_SEL && child != IMX6QDL_CLK_LDB_DI1_SEL) @@ -238,8 +242,11 @@ static bool pll6_bypassed(struct device_node *node) return false; if (clkspec.np == node && - clkspec.args[0] == IMX6QDL_PLL6_BYPASS) + clkspec.args[0] == IMX6QDL_PLL6_BYPASS) { + of_node_put(clkspec.np); break; + } + of_node_put(clkspec.np); } /* PLL6 bypass is not part of the assigned clock list */ @@ -249,6 +256,9 @@ static bool pll6_bypassed(struct device_node *node) ret = of_parse_phandle_with_args(node, "assigned-clock-parents", "#clock-cells", index, &clkspec); + if (!ret) + of_node_put(clkspec.np); + if (clkspec.args[0] != IMX6QDL_CLK_PLL6) return true; diff --git a/drivers/clk/imx/clk-imx8-acm.c b/drivers/clk/imx/clk-imx8-acm.c index 790f7e44b11e..07dca6f31cf8 100644 --- a/drivers/clk/imx/clk-imx8-acm.c +++ b/drivers/clk/imx/clk-imx8-acm.c @@ -371,7 +371,8 @@ static int imx8_acm_clk_probe(struct platform_device *pdev) for (i = 0; i < priv->soc_data->num_sels; i++) { hws[sels[i].clkid] = devm_clk_hw_register_mux_parent_data_table(dev, sels[i].name, sels[i].parents, - sels[i].num_parents, 0, + sels[i].num_parents, + CLK_SET_RATE_NO_REPARENT, base + sels[i].reg, sels[i].shift, sels[i].width, 0, NULL, NULL); diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c index f70ed231b92d..cedc8a02aa1f 100644 --- a/drivers/clk/imx/clk-imx8mq.c +++ b/drivers/clk/imx/clk-imx8mq.c @@ -237,7 +237,7 @@ static const char * const imx8mq_dsi_esc_sels[] = {"osc_25m", "sys2_pll_100m", " static const char * const imx8mq_csi1_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll_out", "audio_pll2_out", "video_pll1_out", }; -static const char * const imx8mq_csi1_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m", +static const char * const imx8mq_csi1_phy_sels[] = {"osc_25m", "sys2_pll_333m", "sys2_pll_100m", "sys1_pll_800m", "sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; static const char * const imx8mq_csi1_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", @@ -246,7 +246,7 @@ static const char * const imx8mq_csi1_esc_sels[] = {"osc_25m", "sys2_pll_100m", static const char * const imx8mq_csi2_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll_out", "audio_pll2_out", "video_pll1_out", }; -static const char * const imx8mq_csi2_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m", +static const char * const imx8mq_csi2_phy_sels[] = {"osc_25m", "sys2_pll_333m", "sys2_pll_100m", "sys1_pll_800m", "sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; static const char * const imx8mq_csi2_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c index 7552aaafc339..39600ee22be3 100644 --- a/drivers/clk/imx/clk-pll14xx.c +++ b/drivers/clk/imx/clk-pll14xx.c @@ -151,7 +151,7 @@ static void imx_pll14xx_calc_settings(struct clk_pll14xx *pll, unsigned long rat /* First try if we can get the desired rate from one of the static entries */ tt = imx_get_pll_settings(pll, rate); if (tt) { - pr_debug("%s: in=%ld, want=%ld, Using PLL setting from table\n", + pr_debug("%s: in=%lu, want=%lu, Using PLL setting from table\n", clk_hw_get_name(&pll->hw), prate, rate); t->rate = tt->rate; t->mdiv = tt->mdiv; @@ -173,7 +173,7 @@ static void imx_pll14xx_calc_settings(struct clk_pll14xx *pll, unsigned long rat if (rate >= rate_min && rate <= rate_max) { kdiv = pll1443x_calc_kdiv(mdiv, pdiv, sdiv, rate, prate); - pr_debug("%s: in=%ld, want=%ld Only adjust kdiv %ld -> %d\n", + pr_debug("%s: in=%lu, want=%lu Only adjust kdiv %ld -> %d\n", clk_hw_get_name(&pll->hw), prate, rate, FIELD_GET(KDIV_MASK, pll_div_ctl1), kdiv); fout = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, kdiv, prate); @@ -211,7 +211,7 @@ static void imx_pll14xx_calc_settings(struct clk_pll14xx *pll, unsigned long rat } } found: - pr_debug("%s: in=%ld, want=%ld got=%d (pdiv=%d sdiv=%d mdiv=%d kdiv=%d)\n", + pr_debug("%s: in=%lu, want=%lu got=%u (pdiv=%d sdiv=%d mdiv=%d kdiv=%d)\n", clk_hw_get_name(&pll->hw), prate, rate, t->rate, t->pdiv, t->sdiv, t->mdiv, t->kdiv); } diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c index 41eb38552a9c..1fbd8011fde2 100644 --- a/drivers/clk/imx/clk-vf610.c +++ b/drivers/clk/imx/clk-vf610.c @@ -11,6 +11,13 @@ #include "clk.h" +/* + * The VF610_CLK_END corresponds to ones defined in + * include/dt-bindings/clock/vf610-clock.h + * It shall be the value of the last defined clock +1 + */ +#define VF610_CLK_END 196 + #define CCM_CCR (ccm_base + 0x00) #define CCM_CSR (ccm_base + 0x04) #define CCM_CCSR (ccm_base + 0x08) @@ -313,6 +320,11 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) clk[VF610_CLK_ENET_TS] = imx_clk_gate("enet_ts", "enet_ts_sel", CCM_CSCDR1, 23); clk[VF610_CLK_ENET0] = imx_clk_gate2("enet0", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(0)); clk[VF610_CLK_ENET1] = imx_clk_gate2("enet1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(1)); + clk[VF610_CLK_ESW] = imx_clk_gate2("esw", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_ESW_MAC_TAB0] = imx_clk_gate2("esw_tab0", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_ESW_MAC_TAB1] = imx_clk_gate2("esw_tab1", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(13)); + clk[VF610_CLK_ESW_MAC_TAB2] = imx_clk_gate2("esw_tab2", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(14)); + clk[VF610_CLK_ESW_MAC_TAB3] = imx_clk_gate2("esw_tab3", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(15)); clk[VF610_CLK_PIT] = imx_clk_gate2("pit", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(7)); diff --git a/drivers/clk/microchip/clk-mpfs-ccc.c b/drivers/clk/microchip/clk-mpfs-ccc.c index 3a3ea2d142f8..0a76a1aaa50f 100644 --- a/drivers/clk/microchip/clk-mpfs-ccc.c +++ b/drivers/clk/microchip/clk-mpfs-ccc.c @@ -178,7 +178,7 @@ static int mpfs_ccc_register_outputs(struct device *dev, struct mpfs_ccc_out_hw_ return dev_err_probe(dev, ret, "failed to register clock id: %d\n", out_hw->id); - data->hw_data.hws[out_hw->id] = &out_hw->divider.hw; + data->hw_data.hws[out_hw->id - 2] = &out_hw->divider.hw; } return 0; @@ -234,6 +234,10 @@ static int mpfs_ccc_probe(struct platform_device *pdev) unsigned int num_clks; int ret; + /* + * If DLLs get added here, mpfs_ccc_register_outputs() currently packs + * sparse clock IDs in the hws array + */ num_clks = ARRAY_SIZE(mpfs_ccc_pll_clks) + ARRAY_SIZE(mpfs_ccc_pll0out_clks) + ARRAY_SIZE(mpfs_ccc_pll1out_clks); diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c index bd0bc8e7b1e7..3123771b9c99 100644 --- a/drivers/clk/mvebu/armada-37xx-periph.c +++ b/drivers/clk/mvebu/armada-37xx-periph.c @@ -126,9 +126,11 @@ static const struct clk_div_table clk_table2[] = { static const struct clk_ops clk_double_div_ops; static const struct clk_ops clk_pm_cpu_ops; +#define __reg(__x) ((void __iomem __force *)(__x)) + #define PERIPH_GATE(_name, _bit) \ struct clk_gate gate_##_name = { \ - .reg = (void *)CLK_DIS, \ + .reg = __reg(CLK_DIS), \ .bit_idx = _bit, \ .hw.init = &(struct clk_init_data){ \ .ops = &clk_gate_ops, \ @@ -137,7 +139,7 @@ struct clk_gate gate_##_name = { \ #define PERIPH_MUX(_name, _shift) \ struct clk_mux mux_##_name = { \ - .reg = (void *)TBG_SEL, \ + .reg = __reg(TBG_SEL), \ .shift = _shift, \ .mask = 3, \ .hw.init = &(struct clk_init_data){ \ @@ -147,8 +149,8 @@ struct clk_mux mux_##_name = { \ #define PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2) \ struct clk_double_div rate_##_name = { \ - .reg1 = (void *)_reg1, \ - .reg2 = (void *)_reg2, \ + .reg1 = __reg(_reg1), \ + .reg2 = __reg(_reg2), \ .shift1 = _shift1, \ .shift2 = _shift2, \ .hw.init = &(struct clk_init_data){ \ @@ -158,7 +160,7 @@ struct clk_double_div rate_##_name = { \ #define PERIPH_DIV(_name, _reg, _shift, _table) \ struct clk_divider rate_##_name = { \ - .reg = (void *)_reg, \ + .reg = __reg(_reg), \ .table = _table, \ .shift = _shift, \ .hw.init = &(struct clk_init_data){ \ @@ -168,10 +170,10 @@ struct clk_divider rate_##_name = { \ #define PERIPH_PM_CPU(_name, _shift1, _reg, _shift2) \ struct clk_pm_cpu muxrate_##_name = { \ - .reg_mux = (void *)TBG_SEL, \ + .reg_mux = __reg(TBG_SEL), \ .mask_mux = 3, \ .shift_mux = _shift1, \ - .reg_div = (void *)_reg, \ + .reg_div = __reg(_reg), \ .shift_div = _shift2, \ .hw.init = &(struct clk_init_data){ \ .ops = &clk_pm_cpu_ops, \ diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index a277c434d641..df21ef5ffd68 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -19,33 +19,77 @@ menuconfig COMMON_CLK_QCOM if COMMON_CLK_QCOM +config CLK_ELIZA_DISPCC + tristate "Eliza Display Clock Controller" + depends on ARM64 || COMPILE_TEST + select CLK_ELIZA_GCC + help + Support for the display clock controllers on Eliza SoCs. + Say Y if you want to support display devices and functionality such as + splash screen. + +config CLK_ELIZA_GCC + tristate "Eliza Global Clock Controller" + depends on ARM64 || COMPILE_TEST + select QCOM_GDSC + help + Support for the global clock controller on Eliza devices. + Say Y if you want to use peripheral devices such as UART, SPI, + I2C, USB, UFS, SDCC, etc. + +config CLK_ELIZA_TCSRCC + tristate "Eliza TCSR Clock Controller" + depends on ARM64 || COMPILE_TEST + select QCOM_GDSC + help + Support for the TCSR clock controller on Eliza devices. + Say Y if you want to use peripheral devices such as USB/PCIe/UFS. + config CLK_GLYMUR_DISPCC - tristate "GLYMUR Display Clock Controller" + tristate "Glymur Display Clock Controller" depends on ARM64 || COMPILE_TEST select CLK_GLYMUR_GCC help Support for the display clock controllers on Qualcomm - Technologies, Inc. GLYMUR devices. + Technologies, Inc. Glymur devices. Say Y if you want to support display devices and functionality such as splash screen. config CLK_GLYMUR_GCC - tristate "GLYMUR Global Clock Controller" + tristate "Glymur Global Clock Controller" depends on ARM64 || COMPILE_TEST select QCOM_GDSC help - Support for the global clock controller on GLYMUR devices. + Support for the global clock controller on Glymur devices. Say Y if you want to use peripheral devices such as UART, SPI, I2C, USB, UFS, SDCC, etc. +config CLK_GLYMUR_GPUCC + tristate "GLYMUR Graphics Clock Controller" + depends on ARM64 || COMPILE_TEST + select CLK_GLYMUR_GCC + help + Support for the graphics clock controller on GLYMUR devices. + Say Y if you want to support graphics controller devices and + functionality such as 3D graphics. + config CLK_GLYMUR_TCSRCC - tristate "GLYMUR TCSR Clock Controller" + tristate "Glymur TCSR Clock Controller" depends on ARM64 || COMPILE_TEST select QCOM_GDSC help - Support for the TCSR clock controller on GLYMUR devices. + Support for the TCSR clock controller on Glymur devices. Say Y if you want to use peripheral devices such as USB/PCIe/EDP. +config CLK_GLYMUR_VIDEOCC + tristate "Glymur Video Clock Controller" + depends on ARM64 || COMPILE_TEST + select CLK_GLYMUR_GCC + help + Support for the video clock controller on Glymur devices. + Say Y if you want to support video devices and functionality such as + video encode and decode. + config CLK_KAANAPALI_CAMCC tristate "Kaanapali Camera Clock Controller" depends on ARM64 || COMPILE_TEST @@ -101,6 +145,16 @@ config CLK_KAANAPALI_VIDEOCC Say Y if you want to support video devices and functionality such as video encode/decode. +config CLK_NORD_GCC + tristate "Nord Global Clock Controller" + depends on ARM64 || COMPILE_TEST + select QCOM_GDSC + help + Support for the global clock controller on Nord devices. + Say Y if you want to use peripheral devices such as UART, + SPI, I2C, USB, SD/UFS, PCIe etc. The clock controller is a combination + of GCC, SE_GCC, NE_GCC and NW_GCC. + config CLK_X1E80100_CAMCC tristate "X1E80100 Camera Clock Controller" depends on ARM64 || COMPILE_TEST @@ -314,6 +368,14 @@ config IPQ_GCC_5018 Say Y if you want to use peripheral devices such as UART, SPI, i2c, USB, SD/eMMC, etc. +config IPQ_GCC_5210 + tristate "IPQ5210 Global Clock Controller" + depends on ARM64 || COMPILE_TEST + help + Support for the global clock controller on ipq5210 devices. + Say Y if you want to use peripheral devices such as UART, SPI, + i2c, USB, SD/eMMC, etc. + config IPQ_GCC_5332 tristate "IPQ5332 Global Clock Controller" depends on ARM64 || COMPILE_TEST @@ -622,6 +684,13 @@ config QCS_GCC_404 Say Y if you want to use multimedia devices or peripheral devices such as UART, SPI, I2C, USB, SD/eMMC, PCIe etc. +config CLK_NORD_TCSRCC + tristate "Nord TCSR Clock Controller" + depends on ARM64 || COMPILE_TEST + help + Support for the TCSR clock controller on Nord devices. + Say Y if you want to use peripheral devices such as PCIe, USB, UFS etc. + config SA_CAMCC_8775P tristate "SA8775P Camera Clock Controller" depends on ARM64 || COMPILE_TEST @@ -1481,6 +1550,15 @@ config SM_GPUCC_8650 Say Y if you want to support graphics controller devices and functionality such as 3D graphics. +config SM_GPUCC_8750 + tristate "SM8750 Graphics Clock Controller" + depends on ARM64 || COMPILE_TEST + select SM_GCC_8750 + help + Support for the graphics clock controller on SM8750 devices. + Say Y if you want to support graphics controller devices and + functionality such as 3D graphics. + config SM_LPASSCC_6115 tristate "SM6115 Low Power Audio Subsystem (LPASS) Clock Controller" depends on ARM64 || COMPILE_TEST @@ -1579,10 +1657,10 @@ config SM_VIDEOCC_8250 config SM_VIDEOCC_8350 tristate "SM8350 Video Clock Controller" depends on ARM64 || COMPILE_TEST - select SM_GCC_8350 + depends on SM_GCC_8350 || SC_GCC_8280XP select QCOM_GDSC help - Support for the video clock controller on SM8350 devices. + Support for the video clock controller on SM8350 or SC8280XP devices. Say Y if you want to support video devices and functionality such as video encode and decode. diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 6b0ad8832b55..89d07c35e4d9 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -20,16 +20,23 @@ clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o # Keep alphabetically sorted by config obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o +obj-$(CONFIG_CLK_ELIZA_DISPCC) += dispcc-eliza.o +obj-$(CONFIG_CLK_ELIZA_GCC) += gcc-eliza.o +obj-$(CONFIG_CLK_ELIZA_TCSRCC) += tcsrcc-eliza.o obj-$(CONFIG_CLK_GFM_LPASS_SM8250) += lpass-gfm-sm8250.o obj-$(CONFIG_CLK_GLYMUR_DISPCC) += dispcc-glymur.o obj-$(CONFIG_CLK_GLYMUR_GCC) += gcc-glymur.o +obj-$(CONFIG_CLK_GLYMUR_GPUCC) += gpucc-glymur.o gxclkctl-kaanapali.o obj-$(CONFIG_CLK_GLYMUR_TCSRCC) += tcsrcc-glymur.o +obj-$(CONFIG_CLK_GLYMUR_VIDEOCC) += videocc-glymur.o obj-$(CONFIG_CLK_KAANAPALI_CAMCC) += cambistmclkcc-kaanapali.o camcc-kaanapali.o obj-$(CONFIG_CLK_KAANAPALI_DISPCC) += dispcc-kaanapali.o obj-$(CONFIG_CLK_KAANAPALI_GCC) += gcc-kaanapali.o obj-$(CONFIG_CLK_KAANAPALI_GPUCC) += gpucc-kaanapali.o gxclkctl-kaanapali.o obj-$(CONFIG_CLK_KAANAPALI_TCSRCC) += tcsrcc-kaanapali.o obj-$(CONFIG_CLK_KAANAPALI_VIDEOCC) += videocc-kaanapali.o +obj-$(CONFIG_CLK_NORD_GCC) += gcc-nord.o negcc-nord.o nwgcc-nord.o segcc-nord.o +obj-$(CONFIG_CLK_NORD_TCSRCC) += tcsrcc-nord.o obj-$(CONFIG_CLK_X1E80100_CAMCC) += camcc-x1e80100.o obj-$(CONFIG_CLK_X1E80100_DISPCC) += dispcc-x1e80100.o obj-$(CONFIG_CLK_X1E80100_GCC) += gcc-x1e80100.o @@ -43,6 +50,7 @@ obj-$(CONFIG_IPQ_APSS_6018) += apss-ipq6018.o obj-$(CONFIG_IPQ_CMN_PLL) += ipq-cmn-pll.o obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o obj-$(CONFIG_IPQ_GCC_5018) += gcc-ipq5018.o +obj-$(CONFIG_IPQ_GCC_5210) += gcc-ipq5210.o obj-$(CONFIG_IPQ_GCC_5332) += gcc-ipq5332.o obj-$(CONFIG_IPQ_GCC_5424) += gcc-ipq5424.o obj-$(CONFIG_IPQ_GCC_6018) += gcc-ipq6018.o @@ -180,6 +188,7 @@ obj-$(CONFIG_SM_GPUCC_8350) += gpucc-sm8350.o obj-$(CONFIG_SM_GPUCC_8450) += gpucc-sm8450.o obj-$(CONFIG_SM_GPUCC_8550) += gpucc-sm8550.o obj-$(CONFIG_SM_GPUCC_8650) += gpucc-sm8650.o +obj-$(CONFIG_SM_GPUCC_8750) += gpucc-sm8750.o gxclkctl-kaanapali.o obj-$(CONFIG_SM_GPUCC_MILOS) += gpucc-milos.o obj-$(CONFIG_SM_LPASSCC_6115) += lpasscc-sm6115.o obj-$(CONFIG_SM_TCSRCC_8550) += tcsrcc-sm8550.o diff --git a/drivers/clk/qcom/apss-ipq5424.c b/drivers/clk/qcom/apss-ipq5424.c index 2d622c1fe5d0..1662c83058bc 100644 --- a/drivers/clk/qcom/apss-ipq5424.c +++ b/drivers/clk/qcom/apss-ipq5424.c @@ -211,7 +211,7 @@ static struct clk_alpha_pll *ipa5424_apss_plls[] = { &ipq5424_apss_pll, }; -static struct qcom_cc_driver_data ipa5424_apss_driver_data = { +static const struct qcom_cc_driver_data ipa5424_apss_driver_data = { .alpha_plls = ipa5424_apss_plls, .num_alpha_plls = ARRAY_SIZE(ipa5424_apss_plls), }; diff --git a/drivers/clk/qcom/cambistmclkcc-kaanapali.c b/drivers/clk/qcom/cambistmclkcc-kaanapali.c index 066c1087b0b6..6028d8f6959c 100644 --- a/drivers/clk/qcom/cambistmclkcc-kaanapali.c +++ b/drivers/clk/qcom/cambistmclkcc-kaanapali.c @@ -6,9 +6,7 @@ #include #include #include -#include #include -#include #include #include @@ -385,7 +383,7 @@ static struct clk_alpha_pll *cam_bist_mclk_cc_kaanapali_plls[] = { &cam_bist_mclk_cc_pll0, }; -static u32 cam_bist_mclk_cc_kaanapali_critical_cbcrs[] = { +static const u32 cam_bist_mclk_cc_kaanapali_critical_cbcrs[] = { 0x40e0, /* CAM_BIST_MCLK_CC_SLEEP_CLK */ }; @@ -397,7 +395,7 @@ static const struct regmap_config cam_bist_mclk_cc_kaanapali_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data cam_bist_mclk_cc_kaanapali_driver_data = { +static const struct qcom_cc_driver_data cam_bist_mclk_cc_kaanapali_driver_data = { .alpha_plls = cam_bist_mclk_cc_kaanapali_plls, .num_alpha_plls = ARRAY_SIZE(cam_bist_mclk_cc_kaanapali_plls), .clk_cbcrs = cam_bist_mclk_cc_kaanapali_critical_cbcrs, diff --git a/drivers/clk/qcom/cambistmclkcc-sm8750.c b/drivers/clk/qcom/cambistmclkcc-sm8750.c index d889a8f6561d..5df12aced4a5 100644 --- a/drivers/clk/qcom/cambistmclkcc-sm8750.c +++ b/drivers/clk/qcom/cambistmclkcc-sm8750.c @@ -402,7 +402,7 @@ static struct clk_alpha_pll *cam_bist_mclk_cc_sm8750_plls[] = { &cam_bist_mclk_cc_pll0, }; -static u32 cam_bist_mclk_cc_sm8750_critical_cbcrs[] = { +static const u32 cam_bist_mclk_cc_sm8750_critical_cbcrs[] = { 0x40f8, /* CAM_BIST_MCLK_CC_SLEEP_CLK */ }; @@ -414,7 +414,7 @@ static const struct regmap_config cam_bist_mclk_cc_sm8750_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data cam_bist_mclk_cc_sm8750_driver_data = { +static const struct qcom_cc_driver_data cam_bist_mclk_cc_sm8750_driver_data = { .alpha_plls = cam_bist_mclk_cc_sm8750_plls, .num_alpha_plls = ARRAY_SIZE(cam_bist_mclk_cc_sm8750_plls), .clk_cbcrs = cam_bist_mclk_cc_sm8750_critical_cbcrs, diff --git a/drivers/clk/qcom/camcc-kaanapali.c b/drivers/clk/qcom/camcc-kaanapali.c index 82967993fcff..af5486418492 100644 --- a/drivers/clk/qcom/camcc-kaanapali.c +++ b/drivers/clk/qcom/camcc-kaanapali.c @@ -6,9 +6,7 @@ #include #include #include -#include #include -#include #include #include @@ -2602,7 +2600,7 @@ static struct clk_alpha_pll *cam_cc_kaanapali_plls[] = { &cam_cc_pll7, }; -static u32 cam_cc_kaanapali_critical_cbcrs[] = { +static const u32 cam_cc_kaanapali_critical_cbcrs[] = { 0x21398, /* CAM_CC_DRV_AHB_CLK */ 0x21390, /* CAM_CC_DRV_XO_CLK */ 0x21364, /* CAM_CC_GDSC_CLK */ @@ -2617,7 +2615,7 @@ static const struct regmap_config cam_cc_kaanapali_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data cam_cc_kaanapali_driver_data = { +static const struct qcom_cc_driver_data cam_cc_kaanapali_driver_data = { .alpha_plls = cam_cc_kaanapali_plls, .num_alpha_plls = ARRAY_SIZE(cam_cc_kaanapali_plls), .clk_cbcrs = cam_cc_kaanapali_critical_cbcrs, diff --git a/drivers/clk/qcom/camcc-milos.c b/drivers/clk/qcom/camcc-milos.c index 0077c9c9249f..409d47098c10 100644 --- a/drivers/clk/qcom/camcc-milos.c +++ b/drivers/clk/qcom/camcc-milos.c @@ -2104,7 +2104,7 @@ static struct clk_alpha_pll *cam_cc_milos_plls[] = { &cam_cc_pll6, }; -static u32 cam_cc_milos_critical_cbcrs[] = { +static const u32 cam_cc_milos_critical_cbcrs[] = { 0x25038, /* CAM_CC_GDSC_CLK */ 0x2505c, /* CAM_CC_SLEEP_CLK */ }; @@ -2117,7 +2117,7 @@ static const struct regmap_config cam_cc_milos_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data cam_cc_milos_driver_data = { +static const struct qcom_cc_driver_data cam_cc_milos_driver_data = { .alpha_plls = cam_cc_milos_plls, .num_alpha_plls = ARRAY_SIZE(cam_cc_milos_plls), .clk_cbcrs = cam_cc_milos_critical_cbcrs, diff --git a/drivers/clk/qcom/camcc-qcs615.c b/drivers/clk/qcom/camcc-qcs615.c index c063a3bfacd0..8377126c2cfe 100644 --- a/drivers/clk/qcom/camcc-qcs615.c +++ b/drivers/clk/qcom/camcc-qcs615.c @@ -1556,7 +1556,7 @@ static const struct regmap_config cam_cc_qcs615_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data cam_cc_qcs615_driver_data = { +static const struct qcom_cc_driver_data cam_cc_qcs615_driver_data = { .alpha_plls = cam_cc_qcs615_plls, .num_alpha_plls = ARRAY_SIZE(cam_cc_qcs615_plls), }; diff --git a/drivers/clk/qcom/camcc-sc8180x.c b/drivers/clk/qcom/camcc-sc8180x.c index 388fedf1dc81..016f37d08468 100644 --- a/drivers/clk/qcom/camcc-sc8180x.c +++ b/drivers/clk/qcom/camcc-sc8180x.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -63,6 +62,7 @@ static const struct alpha_pll_config cam_cc_pll0_config = { static struct clk_alpha_pll cam_cc_pll0 = { .offset = 0x0, + .config = &cam_cc_pll0_config, .vco_table = trion_vco, .num_vco = ARRAY_SIZE(trion_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], @@ -138,6 +138,7 @@ static const struct alpha_pll_config cam_cc_pll1_config = { static struct clk_alpha_pll cam_cc_pll1 = { .offset = 0x1000, + .config = &cam_cc_pll1_config, .vco_table = trion_vco, .num_vco = ARRAY_SIZE(trion_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], @@ -167,6 +168,7 @@ static const struct alpha_pll_config cam_cc_pll2_config = { static struct clk_alpha_pll cam_cc_pll2 = { .offset = 0x2000, + .config = &cam_cc_pll2_config, .vco_table = regera_vco, .num_vco = ARRAY_SIZE(regera_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_REGERA], @@ -219,6 +221,7 @@ static const struct alpha_pll_config cam_cc_pll3_config = { static struct clk_alpha_pll cam_cc_pll3 = { .offset = 0x3000, + .config = &cam_cc_pll3_config, .vco_table = trion_vco, .num_vco = ARRAY_SIZE(trion_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], @@ -248,6 +251,7 @@ static const struct alpha_pll_config cam_cc_pll4_config = { static struct clk_alpha_pll cam_cc_pll4 = { .offset = 0x4000, + .config = &cam_cc_pll4_config, .vco_table = trion_vco, .num_vco = ARRAY_SIZE(trion_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], @@ -277,6 +281,7 @@ static const struct alpha_pll_config cam_cc_pll5_config = { static struct clk_alpha_pll cam_cc_pll5 = { .offset = 0x4078, + .config = &cam_cc_pll5_config, .vco_table = trion_vco, .num_vco = ARRAY_SIZE(trion_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], @@ -306,6 +311,7 @@ static const struct alpha_pll_config cam_cc_pll6_config = { static struct clk_alpha_pll cam_cc_pll6 = { .offset = 0x40f0, + .config = &cam_cc_pll6_config, .vco_table = trion_vco, .num_vco = ARRAY_SIZE(trion_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], @@ -2813,6 +2819,21 @@ static const struct qcom_reset_map cam_cc_sc8180x_resets[] = { [CAM_CC_MCLK7_BCR] = { 0x50e0 }, }; +static struct clk_alpha_pll *cam_cc_sc8180x_plls[] = { + &cam_cc_pll0, + &cam_cc_pll1, + &cam_cc_pll2, + &cam_cc_pll3, + &cam_cc_pll4, + &cam_cc_pll5, + &cam_cc_pll6, +}; + +static const u32 cam_cc_sc8180x_critical_cbcrs[] = { + 0xc1e4, /* CAM_CC_GDSC_CLK */ + 0xc200, /* CAM_CC_SLEEP_CLK */ +}; + static const struct regmap_config cam_cc_sc8180x_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -2821,6 +2842,13 @@ static const struct regmap_config cam_cc_sc8180x_regmap_config = { .fast_io = true, }; +static const struct qcom_cc_driver_data cam_cc_sc8180x_driver_data = { + .alpha_plls = cam_cc_sc8180x_plls, + .num_alpha_plls = ARRAY_SIZE(cam_cc_sc8180x_plls), + .clk_cbcrs = cam_cc_sc8180x_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(cam_cc_sc8180x_critical_cbcrs), +}; + static const struct qcom_cc_desc cam_cc_sc8180x_desc = { .config = &cam_cc_sc8180x_regmap_config, .clks = cam_cc_sc8180x_clocks, @@ -2829,6 +2857,8 @@ static const struct qcom_cc_desc cam_cc_sc8180x_desc = { .num_resets = ARRAY_SIZE(cam_cc_sc8180x_resets), .gdscs = cam_cc_sc8180x_gdscs, .num_gdscs = ARRAY_SIZE(cam_cc_sc8180x_gdscs), + .use_rpm = true, + .driver_data = &cam_cc_sc8180x_driver_data, }; static const struct of_device_id cam_cc_sc8180x_match_table[] = { @@ -2839,40 +2869,7 @@ MODULE_DEVICE_TABLE(of, cam_cc_sc8180x_match_table); static int cam_cc_sc8180x_probe(struct platform_device *pdev) { - struct regmap *regmap; - int ret; - - ret = devm_pm_runtime_enable(&pdev->dev); - if (ret) - return ret; - - ret = pm_runtime_resume_and_get(&pdev->dev); - if (ret) - return ret; - - regmap = qcom_cc_map(pdev, &cam_cc_sc8180x_desc); - if (IS_ERR(regmap)) { - pm_runtime_put(&pdev->dev); - return PTR_ERR(regmap); - } - - clk_trion_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config); - clk_trion_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config); - clk_regera_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config); - clk_trion_pll_configure(&cam_cc_pll3, regmap, &cam_cc_pll3_config); - clk_trion_pll_configure(&cam_cc_pll4, regmap, &cam_cc_pll4_config); - clk_trion_pll_configure(&cam_cc_pll5, regmap, &cam_cc_pll5_config); - clk_trion_pll_configure(&cam_cc_pll6, regmap, &cam_cc_pll6_config); - - /* Keep some clocks always enabled */ - qcom_branch_set_clk_en(regmap, 0xc1e4); /* CAM_CC_GDSC_CLK */ - qcom_branch_set_clk_en(regmap, 0xc200); /* CAM_CC_SLEEP_CLK */ - - ret = qcom_cc_really_probe(&pdev->dev, &cam_cc_sc8180x_desc, regmap); - - pm_runtime_put(&pdev->dev); - - return ret; + return qcom_cc_probe(pdev, &cam_cc_sc8180x_desc); } static struct platform_driver cam_cc_sc8180x_driver = { diff --git a/drivers/clk/qcom/camcc-sm8450.c b/drivers/clk/qcom/camcc-sm8450.c index ef8cf54d0eed..1891262a559b 100644 --- a/drivers/clk/qcom/camcc-sm8450.c +++ b/drivers/clk/qcom/camcc-sm8450.c @@ -2915,7 +2915,7 @@ static struct clk_alpha_pll *cam_cc_sm8450_plls[] = { &cam_cc_pll8, }; -static u32 cam_cc_sm8450_critical_cbcrs[] = { +static const u32 cam_cc_sm8450_critical_cbcrs[] = { 0x1320c, /* CAM_CC_GDSC_CLK */ }; @@ -3030,7 +3030,7 @@ static struct gdsc *cam_cc_sm8450_gdscs[] = { [TITAN_TOP_GDSC] = &titan_top_gdsc, }; -static struct qcom_cc_driver_data cam_cc_sm8450_driver_data = { +static const struct qcom_cc_driver_data cam_cc_sm8450_driver_data = { .alpha_plls = cam_cc_sm8450_plls, .num_alpha_plls = ARRAY_SIZE(cam_cc_sm8450_plls), .clk_cbcrs = cam_cc_sm8450_critical_cbcrs, diff --git a/drivers/clk/qcom/camcc-sm8550.c b/drivers/clk/qcom/camcc-sm8550.c index b8ece8a57a8a..34d53e2ffad7 100644 --- a/drivers/clk/qcom/camcc-sm8550.c +++ b/drivers/clk/qcom/camcc-sm8550.c @@ -3517,7 +3517,7 @@ static struct clk_alpha_pll *cam_cc_sm8550_plls[] = { &cam_cc_pll12, }; -static u32 cam_cc_sm8550_critical_cbcrs[] = { +static const u32 cam_cc_sm8550_critical_cbcrs[] = { 0x1419c, /* CAM_CC_GDSC_CLK */ 0x142cc, /* CAM_CC_SLEEP_CLK */ }; @@ -3530,7 +3530,7 @@ static const struct regmap_config cam_cc_sm8550_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data cam_cc_sm8550_driver_data = { +static const struct qcom_cc_driver_data cam_cc_sm8550_driver_data = { .alpha_plls = cam_cc_sm8550_plls, .num_alpha_plls = ARRAY_SIZE(cam_cc_sm8550_plls), .clk_cbcrs = cam_cc_sm8550_critical_cbcrs, diff --git a/drivers/clk/qcom/camcc-sm8650.c b/drivers/clk/qcom/camcc-sm8650.c index 8b388904f56f..9dea43e74cb6 100644 --- a/drivers/clk/qcom/camcc-sm8650.c +++ b/drivers/clk/qcom/camcc-sm8650.c @@ -3533,7 +3533,7 @@ static struct clk_alpha_pll *cam_cc_sm8650_plls[] = { &cam_cc_pll10, }; -static u32 cam_cc_sm8650_critical_cbcrs[] = { +static const u32 cam_cc_sm8650_critical_cbcrs[] = { 0x132ec, /* CAM_CC_GDSC_CLK */ 0x13308, /* CAM_CC_SLEEP_CLK */ 0x13314, /* CAM_CC_DRV_XO_CLK */ @@ -3548,7 +3548,7 @@ static const struct regmap_config cam_cc_sm8650_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data cam_cc_sm8650_driver_data = { +static const struct qcom_cc_driver_data cam_cc_sm8650_driver_data = { .alpha_plls = cam_cc_sm8650_plls, .num_alpha_plls = ARRAY_SIZE(cam_cc_sm8650_plls), .clk_cbcrs = cam_cc_sm8650_critical_cbcrs, diff --git a/drivers/clk/qcom/camcc-sm8750.c b/drivers/clk/qcom/camcc-sm8750.c index a797b783d4a9..6618b074c90e 100644 --- a/drivers/clk/qcom/camcc-sm8750.c +++ b/drivers/clk/qcom/camcc-sm8750.c @@ -2651,7 +2651,7 @@ static struct clk_alpha_pll *cam_cc_sm8750_plls[] = { &cam_cc_pll6, }; -static u32 cam_cc_sm8750_critical_cbcrs[] = { +static const u32 cam_cc_sm8750_critical_cbcrs[] = { 0x113c4, /* CAM_CC_DRV_AHB_CLK */ 0x113c0, /* CAM_CC_DRV_XO_CLK */ 0x1137c, /* CAM_CC_GDSC_CLK */ @@ -2666,7 +2666,7 @@ static const struct regmap_config cam_cc_sm8750_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data cam_cc_sm8750_driver_data = { +static const struct qcom_cc_driver_data cam_cc_sm8750_driver_data = { .alpha_plls = cam_cc_sm8750_plls, .num_alpha_plls = ARRAY_SIZE(cam_cc_sm8750_plls), .clk_cbcrs = cam_cc_sm8750_critical_cbcrs, diff --git a/drivers/clk/qcom/camcc-x1e80100.c b/drivers/clk/qcom/camcc-x1e80100.c index cbcc1c9fcb34..81f579ff6993 100644 --- a/drivers/clk/qcom/camcc-x1e80100.c +++ b/drivers/clk/qcom/camcc-x1e80100.c @@ -2434,7 +2434,7 @@ static struct clk_alpha_pll *cam_cc_x1e80100_plls[] = { &cam_cc_pll8, }; -static u32 cam_cc_x1e80100_critical_cbcrs[] = { +static const u32 cam_cc_x1e80100_critical_cbcrs[] = { 0x13a9c, /* CAM_CC_GDSC_CLK */ 0x13ab8, /* CAM_CC_SLEEP_CLK */ }; @@ -2447,7 +2447,7 @@ static const struct regmap_config cam_cc_x1e80100_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data cam_cc_x1e80100_driver_data = { +static const struct qcom_cc_driver_data cam_cc_x1e80100_driver_data = { .alpha_plls = cam_cc_x1e80100_plls, .num_alpha_plls = ARRAY_SIZE(cam_cc_x1e80100_plls), .clk_cbcrs = cam_cc_x1e80100_critical_cbcrs, diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index fc696b66ccda..6064a0e17d51 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -1117,6 +1117,8 @@ static const struct frac_entry frac_table_pixel[] = { { 4, 9 }, { 1, 1 }, { 2, 3 }, + { 16, 35}, + { 4, 15}, { } }; diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c index 547729b1a8ee..339a6bbcdc4c 100644 --- a/drivers/clk/qcom/clk-rpmh.c +++ b/drivers/clk/qcom/clk-rpmh.c @@ -349,6 +349,10 @@ DEFINE_CLK_RPMH_ARC(bi_tcxo, "xo.lvl", 0x3, 2); DEFINE_CLK_RPMH_ARC(bi_tcxo, "xo.lvl", 0x3, 4); DEFINE_CLK_RPMH_ARC(qlink, "qphy.lvl", 0x1, 4); +DEFINE_CLK_RPMH_VRM(ln_bb_clk1, _a1, "lnbclka1", 1); +DEFINE_CLK_RPMH_VRM(ln_bb_clk2, _a1, "lnbclka2", 1); +DEFINE_CLK_RPMH_VRM(ln_bb_clk3, _a1, "lnbclka3", 1); + DEFINE_CLK_RPMH_VRM(ln_bb_clk1, _a2, "lnbclka1", 2); DEFINE_CLK_RPMH_VRM(ln_bb_clk2, _a2, "lnbclka2", 2); DEFINE_CLK_RPMH_VRM(ln_bb_clk3, _a2, "lnbclka3", 2); @@ -372,6 +376,8 @@ DEFINE_CLK_RPMH_VRM(rf_clk3, _d, "rfclkd3", 1); DEFINE_CLK_RPMH_VRM(rf_clk4, _d, "rfclkd4", 1); DEFINE_CLK_RPMH_VRM(rf_clk3, _a2, "rfclka3", 2); +DEFINE_CLK_RPMH_VRM(rf_clk4, _a2, "rfclka4", 2); +DEFINE_CLK_RPMH_VRM(rf_clk5, _a2, "rfclka5", 2); DEFINE_CLK_RPMH_VRM(clk1, _a1, "clka1", 1); DEFINE_CLK_RPMH_VRM(clk2, _a1, "clka2", 1); @@ -940,6 +946,44 @@ static const struct clk_rpmh_desc clk_rpmh_kaanapali = { .num_clks = ARRAY_SIZE(kaanapali_rpmh_clocks), }; +static struct clk_hw *eliza_rpmh_clocks[] = { + [RPMH_CXO_CLK] = &clk_rpmh_bi_tcxo_div2.hw, + [RPMH_CXO_CLK_A] = &clk_rpmh_bi_tcxo_div2_ao.hw, + [RPMH_LN_BB_CLK1] = &clk_rpmh_clk6_a2.hw, + [RPMH_LN_BB_CLK1_A] = &clk_rpmh_clk6_a2_ao.hw, + [RPMH_LN_BB_CLK3] = &clk_rpmh_clk8_a2.hw, + [RPMH_LN_BB_CLK3_A] = &clk_rpmh_clk8_a2_ao.hw, + [RPMH_RF_CLK1] = &clk_rpmh_rf_clk1_a.hw, + [RPMH_RF_CLK1_A] = &clk_rpmh_rf_clk1_a_ao.hw, + [RPMH_RF_CLK2] = &clk_rpmh_rf_clk2_a.hw, + [RPMH_RF_CLK2_A] = &clk_rpmh_rf_clk2_a_ao.hw, + [RPMH_RF_CLK4] = &clk_rpmh_rf_clk4_a2.hw, + [RPMH_RF_CLK4_A] = &clk_rpmh_rf_clk4_a2_ao.hw, + [RPMH_RF_CLK5] = &clk_rpmh_rf_clk5_a2.hw, + [RPMH_RF_CLK5_A] = &clk_rpmh_rf_clk5_a2_ao.hw, + [RPMH_IPA_CLK] = &clk_rpmh_ipa.hw, +}; + +static const struct clk_rpmh_desc clk_rpmh_eliza = { + .clks = eliza_rpmh_clocks, + .num_clks = ARRAY_SIZE(eliza_rpmh_clocks), +}; + +static struct clk_hw *nord_rpmh_clocks[] = { + [RPMH_CXO_CLK] = &clk_rpmh_bi_tcxo_div1.hw, + [RPMH_CXO_CLK_A] = &clk_rpmh_bi_tcxo_div1_ao.hw, + [RPMH_LN_BB_CLK2] = &clk_rpmh_ln_bb_clk2_a1.hw, + [RPMH_LN_BB_CLK2_A] = &clk_rpmh_ln_bb_clk2_a1_ao.hw, + [RPMH_LN_BB_CLK3] = &clk_rpmh_ln_bb_clk3_a1.hw, + [RPMH_LN_BB_CLK3_A] = &clk_rpmh_ln_bb_clk3_a1_ao.hw, + [RPMH_IPA_CLK] = &clk_rpmh_ipa.hw, +}; + +static const struct clk_rpmh_desc clk_rpmh_nord = { + .clks = nord_rpmh_clocks, + .num_clks = ARRAY_SIZE(nord_rpmh_clocks), +}; + static struct clk_hw *of_clk_rpmh_hw_get(struct of_phandle_args *clkspec, void *data) { @@ -1029,9 +1073,11 @@ static int clk_rpmh_probe(struct platform_device *pdev) } static const struct of_device_id clk_rpmh_match_table[] = { + { .compatible = "qcom,eliza-rpmh-clk", .data = &clk_rpmh_eliza}, { .compatible = "qcom,glymur-rpmh-clk", .data = &clk_rpmh_glymur}, { .compatible = "qcom,kaanapali-rpmh-clk", .data = &clk_rpmh_kaanapali}, { .compatible = "qcom,milos-rpmh-clk", .data = &clk_rpmh_milos}, + { .compatible = "qcom,nord-rpmh-clk", .data = &clk_rpmh_nord}, { .compatible = "qcom,qcs615-rpmh-clk", .data = &clk_rpmh_qcs615}, { .compatible = "qcom,qdu1000-rpmh-clk", .data = &clk_rpmh_qdu1000}, { .compatible = "qcom,sa8775p-rpmh-clk", .data = &clk_rpmh_sa8775p}, diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 953c91f7b145..6f2406f8839e 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -28,7 +28,7 @@ struct qcom_icc_hws_data { struct qcom_cc_driver_data { struct clk_alpha_pll **alpha_plls; size_t num_alpha_plls; - u32 *clk_cbcrs; + const u32 *clk_cbcrs; size_t num_clk_cbcrs; const struct clk_rcg_dfs_data *dfs_rcgs; size_t num_dfs_rcgs; @@ -49,7 +49,7 @@ struct qcom_cc_desc { size_t num_icc_hws; unsigned int icc_first_node_id; bool use_rpm; - struct qcom_cc_driver_data *driver_data; + const struct qcom_cc_driver_data *driver_data; }; /** diff --git a/drivers/clk/qcom/dispcc-eliza.c b/drivers/clk/qcom/dispcc-eliza.c new file mode 100644 index 000000000000..479f26e0dde2 --- /dev/null +++ b/drivers/clk/qcom/dispcc-eliza.c @@ -0,0 +1,2121 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023-2024, Linaro Ltd. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "gdsc.h" +#include "reset.h" + +/* Need to match the order of clocks in DT binding */ +enum { + DT_BI_TCXO, + DT_BI_TCXO_AO, + DT_AHB_CLK, + DT_SLEEP_CLK, + + DT_DSI0_PHY_PLL_OUT_BYTECLK, + DT_DSI0_PHY_PLL_OUT_DSICLK, + DT_DSI1_PHY_PLL_OUT_BYTECLK, + DT_DSI1_PHY_PLL_OUT_DSICLK, + + DT_DP0_PHY_PLL_LINK_CLK, + DT_DP0_PHY_PLL_VCO_DIV_CLK, + DT_DP1_PHY_PLL_LINK_CLK, + DT_DP1_PHY_PLL_VCO_DIV_CLK, + DT_DP2_PHY_PLL_LINK_CLK, + DT_DP2_PHY_PLL_VCO_DIV_CLK, + DT_DP3_PHY_PLL_LINK_CLK, + DT_DP3_PHY_PLL_VCO_DIV_CLK, + DT_HDMI_PHY_PLL_CLK, +}; + +#define DISP_CC_MISC_CMD 0xF000 + +enum { + P_BI_TCXO, + P_DISP_CC_PLL0_OUT_MAIN, + P_DISP_CC_PLL1_OUT_EVEN, + P_DISP_CC_PLL1_OUT_MAIN, + P_DISP_CC_PLL2_OUT_MAIN, + P_DP0_PHY_PLL_LINK_CLK, + P_DP0_PHY_PLL_VCO_DIV_CLK, + P_DP1_PHY_PLL_LINK_CLK, + P_DP1_PHY_PLL_VCO_DIV_CLK, + P_DP2_PHY_PLL_LINK_CLK, + P_DP2_PHY_PLL_VCO_DIV_CLK, + P_DP3_PHY_PLL_LINK_CLK, + P_DP3_PHY_PLL_VCO_DIV_CLK, + P_DSI0_PHY_PLL_OUT_BYTECLK, + P_DSI0_PHY_PLL_OUT_DSICLK, + P_DSI1_PHY_PLL_OUT_BYTECLK, + P_DSI1_PHY_PLL_OUT_DSICLK, + P_HDMI_PHY_PLL_CLK, + P_SLEEP_CLK, +}; + +static const struct pll_vco lucid_ole_vco[] = { + { 249600000, 2300000000, 0 }, +}; + +static const struct pll_vco pongo_ole_vco[] = { + { 38400000, 38400000, 0 }, +}; + +static struct alpha_pll_config disp_cc_pll0_config = { + .l = 0xd, + .cal_l = 0x44, + .alpha = 0x6492, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll disp_cc_pll0 = { + .offset = 0x0, + .config = &disp_cc_pll0_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static struct alpha_pll_config disp_cc_pll1_config = { + .l = 0x1f, + .cal_l = 0x44, + .alpha = 0x4000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll disp_cc_pll1 = { + .offset = 0x1000, + .config = &disp_cc_pll1_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_pll1", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct alpha_pll_config disp_cc_pll2_config = { + .l = 0x493, + .cal_l = 0x493, + .alpha = 0x0, + .config_ctl_val = 0x60000f6a, + .config_ctl_hi_val = 0x0001c808, + .config_ctl_hi1_val = 0x00000000, + .config_ctl_hi2_val = 0x04008174, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x0080c496, + .test_ctl_hi1_val = 0x40100180, + .test_ctl_hi2_val = 0x441001bc, + .test_ctl_hi3_val = 0x000003d8, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00e50302, +}; + +static struct clk_alpha_pll disp_cc_pll2 = { + .offset = 0x2000, + .config = &disp_cc_pll2_config, + .vco_table = pongo_ole_vco, + .num_vco = ARRAY_SIZE(pongo_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_PONGO_ELU], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_pll2", + .parent_data = &(const struct clk_parent_data) { + .index = DT_SLEEP_CLK, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_pongo_elu_ops, + }, + }, +}; + +static const struct parent_map disp_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct clk_parent_data disp_cc_parent_data_0_ao[] = { + { .index = DT_BI_TCXO_AO }, +}; + +static const struct parent_map disp_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_DSI0_PHY_PLL_OUT_DSICLK, 1 }, + { P_DSI0_PHY_PLL_OUT_BYTECLK, 2 }, + { P_DSI1_PHY_PLL_OUT_DSICLK, 3 }, + { P_DSI1_PHY_PLL_OUT_BYTECLK, 4 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DSI0_PHY_PLL_OUT_DSICLK }, + { .index = DT_DSI0_PHY_PLL_OUT_BYTECLK }, + { .index = DT_DSI1_PHY_PLL_OUT_DSICLK }, + { .index = DT_DSI1_PHY_PLL_OUT_BYTECLK }, +}; + +static const struct parent_map disp_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_DP3_PHY_PLL_VCO_DIV_CLK, 3 }, + { P_DP1_PHY_PLL_VCO_DIV_CLK, 4 }, + { P_DP2_PHY_PLL_VCO_DIV_CLK, 6 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DP3_PHY_PLL_VCO_DIV_CLK }, + { .index = DT_DP1_PHY_PLL_VCO_DIV_CLK }, + { .index = DT_DP2_PHY_PLL_VCO_DIV_CLK }, +}; + +static const struct parent_map disp_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_DP1_PHY_PLL_LINK_CLK, 2 }, + { P_DP2_PHY_PLL_LINK_CLK, 3 }, + { P_DP3_PHY_PLL_LINK_CLK, 4 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DP1_PHY_PLL_LINK_CLK }, + { .index = DT_DP2_PHY_PLL_LINK_CLK }, + { .index = DT_DP3_PHY_PLL_LINK_CLK }, +}; + +static const struct parent_map disp_cc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_DSI0_PHY_PLL_OUT_DSICLK, 1 }, + { P_DISP_CC_PLL2_OUT_MAIN, 2 }, + { P_DSI1_PHY_PLL_OUT_DSICLK, 3 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DSI0_PHY_PLL_OUT_DSICLK }, + { .hw = &disp_cc_pll2.clkr.hw }, + { .index = DT_DSI1_PHY_PLL_OUT_DSICLK }, +}; + +static const struct parent_map disp_cc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_DP0_PHY_PLL_LINK_CLK, 1 }, + { P_DP0_PHY_PLL_VCO_DIV_CLK, 2 }, + { P_DP3_PHY_PLL_VCO_DIV_CLK, 3 }, + { P_DP1_PHY_PLL_VCO_DIV_CLK, 4 }, + { P_DP2_PHY_PLL_VCO_DIV_CLK, 6 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_5[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DP0_PHY_PLL_LINK_CLK }, + { .index = DT_DP0_PHY_PLL_VCO_DIV_CLK }, + { .index = DT_DP3_PHY_PLL_VCO_DIV_CLK }, + { .index = DT_DP1_PHY_PLL_VCO_DIV_CLK }, + { .index = DT_DP2_PHY_PLL_VCO_DIV_CLK }, +}; + +static const struct parent_map disp_cc_parent_map_6[] = { + { P_BI_TCXO, 0 }, + { P_DSI0_PHY_PLL_OUT_BYTECLK, 2 }, + { P_DSI1_PHY_PLL_OUT_BYTECLK, 4 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_6[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DSI0_PHY_PLL_OUT_BYTECLK }, + { .index = DT_DSI1_PHY_PLL_OUT_BYTECLK }, +}; + +static const struct parent_map disp_cc_parent_map_7[] = { + { P_BI_TCXO, 0 }, + { P_DISP_CC_PLL1_OUT_MAIN, 4 }, + { P_DISP_CC_PLL1_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_7[] = { + { .index = DT_BI_TCXO }, + { .hw = &disp_cc_pll1.clkr.hw }, + { .hw = &disp_cc_pll1.clkr.hw }, +}; + +static const struct parent_map disp_cc_parent_map_8[] = { + { P_BI_TCXO, 0 }, + { P_DP0_PHY_PLL_LINK_CLK, 1 }, + { P_DP1_PHY_PLL_LINK_CLK, 2 }, + { P_DP2_PHY_PLL_LINK_CLK, 3 }, + { P_DP3_PHY_PLL_LINK_CLK, 4 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_8[] = { + { .index = DT_BI_TCXO }, + { .index = DT_DP0_PHY_PLL_LINK_CLK }, + { .index = DT_DP1_PHY_PLL_LINK_CLK }, + { .index = DT_DP2_PHY_PLL_LINK_CLK }, + { .index = DT_DP3_PHY_PLL_LINK_CLK }, +}; + +static const struct parent_map disp_cc_parent_map_9[] = { + { P_BI_TCXO, 0 }, + { P_DISP_CC_PLL0_OUT_MAIN, 1 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_9[] = { + { .index = DT_BI_TCXO }, + { .hw = &disp_cc_pll0.clkr.hw }, +}; + +static const struct parent_map disp_cc_parent_map_10[] = { + { P_BI_TCXO, 0 }, + { P_HDMI_PHY_PLL_CLK, 1 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_10[] = { + { .index = DT_BI_TCXO }, + { .index = DT_HDMI_PHY_PLL_CLK }, +}; + +static const struct parent_map disp_cc_parent_map_11[] = { + { P_BI_TCXO, 0 }, + { P_DISP_CC_PLL0_OUT_MAIN, 1 }, + { P_DISP_CC_PLL1_OUT_MAIN, 4 }, + { P_DISP_CC_PLL1_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_11[] = { + { .index = DT_BI_TCXO }, + { .hw = &disp_cc_pll0.clkr.hw }, + { .hw = &disp_cc_pll1.clkr.hw }, + { .hw = &disp_cc_pll1.clkr.hw }, +}; + +static const struct parent_map disp_cc_parent_map_12[] = { + { P_BI_TCXO, 0 }, + { P_DISP_CC_PLL2_OUT_MAIN, 2 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_12[] = { + { .index = DT_BI_TCXO }, + { .hw = &disp_cc_pll2.clkr.hw }, +}; + +static const struct parent_map disp_cc_parent_map_13[] = { + { P_SLEEP_CLK, 0 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_13_ao[] = { + { .index = DT_SLEEP_CLK }, +}; + +static const struct freq_tbl ftbl_disp_cc_esync0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_esync0_clk_src = { + .cmd_rcgr = 0x80c0, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_4, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_esync0_clk_src", + .parent_data = disp_cc_parent_data_4, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_byte2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_esync1_clk_src = { + .cmd_rcgr = 0x80d8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_4, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_esync1_clk_src", + .parent_data = disp_cc_parent_data_4, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_byte2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(37500000, P_DISP_CC_PLL1_OUT_MAIN, 16, 0, 0), + F(75000000, P_DISP_CC_PLL1_OUT_MAIN, 8, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_ahb_clk_src = { + .cmd_rcgr = 0x8360, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_7, + .freq_tbl = ftbl_disp_cc_mdss_ahb_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_ahb_clk_src", + .parent_data = disp_cc_parent_data_7, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_7), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_byte0_clk_src = { + .cmd_rcgr = 0x8180, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_byte0_clk_src", + .parent_data = disp_cc_parent_data_1, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + .ops = &clk_byte2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_byte1_clk_src = { + .cmd_rcgr = 0x819c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_byte1_clk_src", + .parent_data = disp_cc_parent_data_1, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + .ops = &clk_byte2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx0_aux_clk_src = { + .cmd_rcgr = 0x8234, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_aux_clk_src", + .parent_data = disp_cc_parent_data_0, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx0_link_clk_src = { + .cmd_rcgr = 0x81e8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_8, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_link_clk_src", + .parent_data = disp_cc_parent_data_8, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_8), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_byte2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx0_pixel0_clk_src = { + .cmd_rcgr = 0x8204, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_5, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_pixel0_clk_src", + .parent_data = disp_cc_parent_data_5, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx0_pixel1_clk_src = { + .cmd_rcgr = 0x821c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_5, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_pixel1_clk_src", + .parent_data = disp_cc_parent_data_5, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx1_aux_clk_src = { + .cmd_rcgr = 0x8298, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx1_aux_clk_src", + .parent_data = disp_cc_parent_data_0, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx1_link_clk_src = { + .cmd_rcgr = 0x827c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_3, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx1_link_clk_src", + .parent_data = disp_cc_parent_data_3, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_byte2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx1_pixel0_clk_src = { + .cmd_rcgr = 0x824c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx1_pixel0_clk_src", + .parent_data = disp_cc_parent_data_2, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx1_pixel1_clk_src = { + .cmd_rcgr = 0x8264, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx1_pixel1_clk_src", + .parent_data = disp_cc_parent_data_2, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx2_aux_clk_src = { + .cmd_rcgr = 0x82fc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx2_aux_clk_src", + .parent_data = disp_cc_parent_data_0, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx2_link_clk_src = { + .cmd_rcgr = 0x82b0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_3, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx2_link_clk_src", + .parent_data = disp_cc_parent_data_3, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_byte2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx2_pixel0_clk_src = { + .cmd_rcgr = 0x82cc, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx2_pixel0_clk_src", + .parent_data = disp_cc_parent_data_2, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx2_pixel1_clk_src = { + .cmd_rcgr = 0x82e4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx2_pixel1_clk_src", + .parent_data = disp_cc_parent_data_2, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx3_aux_clk_src = { + .cmd_rcgr = 0x8348, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx3_aux_clk_src", + .parent_data = disp_cc_parent_data_0, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx3_link_clk_src = { + .cmd_rcgr = 0x832c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_3, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx3_link_clk_src", + .parent_data = disp_cc_parent_data_3, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_byte2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dptx3_pixel0_clk_src = { + .cmd_rcgr = 0x8314, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx3_pixel0_clk_src", + .parent_data = disp_cc_parent_data_2, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_esc0_clk_src = { + .cmd_rcgr = 0x81b8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_6, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_esc0_clk_src", + .parent_data = disp_cc_parent_data_6, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_6), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_esc1_clk_src = { + .cmd_rcgr = 0x81d0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_6, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_esc1_clk_src", + .parent_data = disp_cc_parent_data_6, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_6), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_hdmi_app_clk_src = { + .cmd_rcgr = 0x83a8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_9, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_hdmi_app_clk_src", + .parent_data = disp_cc_parent_data_9, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_9), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_hdmi_pclk_clk_src = { + .cmd_rcgr = 0x8390, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_10, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_hdmi_pclk_clk_src", + .parent_data = disp_cc_parent_data_10, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_10), + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + .ops = &clk_pixel_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = { + F(85714286, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(100000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(150000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(207000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(342000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(417000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(535000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(600000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(660000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = { + .cmd_rcgr = 0x8150, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_11, + .freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_mdp_clk_src", + .parent_data = disp_cc_parent_data_11, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_11), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = { + .cmd_rcgr = 0x8108, + .mnd_width = 8, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_pclk0_clk_src", + .parent_data = disp_cc_parent_data_1, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + .ops = &clk_pixel_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_pclk1_clk_src = { + .cmd_rcgr = 0x8120, + .mnd_width = 8, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_pclk1_clk_src", + .parent_data = disp_cc_parent_data_1, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + .ops = &clk_pixel_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_pclk2_clk_src = { + .cmd_rcgr = 0x8138, + .mnd_width = 8, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_pclk2_clk_src", + .parent_data = disp_cc_parent_data_1, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + .ops = &clk_pixel_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_vsync_clk_src = { + .cmd_rcgr = 0x8168, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_vsync_clk_src", + .parent_data = disp_cc_parent_data_0, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_osc_clk_src[] = { + F(38400000, P_DISP_CC_PLL2_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_osc_clk_src = { + .cmd_rcgr = 0x80f0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_12, + .freq_tbl = ftbl_disp_cc_osc_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_osc_clk_src", + .parent_data = disp_cc_parent_data_12, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_12), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_sleep_clk_src[] = { + F(32000, P_SLEEP_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_sleep_clk_src = { + .cmd_rcgr = 0xe064, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_13, + .freq_tbl = ftbl_disp_cc_sleep_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_sleep_clk_src", + .parent_data = disp_cc_parent_data_13_ao, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_13_ao), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_xo_clk_src = { + .cmd_rcgr = 0xe044, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_esync0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_xo_clk_src", + .parent_data = disp_cc_parent_data_0_ao, + .num_parents = ARRAY_SIZE(disp_cc_parent_data_0_ao), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { + .reg = 0x8198, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_byte0_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_byte0_clk_src.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_byte1_div_clk_src = { + .reg = 0x81b4, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_byte1_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_byte1_clk_src.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_dptx0_link_div_clk_src = { + .reg = 0x8200, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_link_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx0_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_dptx1_link_div_clk_src = { + .reg = 0x8294, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx1_link_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx1_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_dptx2_link_div_clk_src = { + .reg = 0x82c8, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx2_link_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx2_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_dptx3_link_div_clk_src = { + .reg = 0x8344, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx3_link_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx3_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_hdmi_pclk_div_clk_src = { + .reg = 0x838c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_hdmi_pclk_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_hdmi_pclk_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch disp_cc_esync0_clk = { + .halt_reg = 0x80b8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x80b8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_esync0_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_esync0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_esync1_clk = { + .halt_reg = 0x80bc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x80bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_esync1_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_esync1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_accu_shift_clk = { + .halt_reg = 0xe060, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xe060, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_accu_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_ahb1_clk = { + .halt_reg = 0xa028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_ahb1_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_ahb_clk = { + .halt_reg = 0x80b0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x80b0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_byte0_clk = { + .halt_reg = 0x8034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_byte0_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_byte0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_byte0_intf_clk = { + .halt_reg = 0x8038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_byte0_intf_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_byte0_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_byte1_clk = { + .halt_reg = 0x803c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x803c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_byte1_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_byte1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_byte1_intf_clk = { + .halt_reg = 0x8040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_byte1_intf_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_byte1_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx0_aux_clk = { + .halt_reg = 0x8064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx0_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx0_crypto_clk = { + .halt_reg = 0x8058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_crypto_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx0_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx0_link_clk = { + .halt_reg = 0x804c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x804c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_link_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx0_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx0_link_intf_clk = { + .halt_reg = 0x8054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_link_intf_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx0_link_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx0_pixel0_clk = { + .halt_reg = 0x805c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x805c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_pixel0_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx0_pixel0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx0_pixel1_clk = { + .halt_reg = 0x8060, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8060, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_pixel1_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx0_pixel1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx0_usb_router_link_intf_clk = { + .halt_reg = 0x8050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8050, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx0_usb_router_link_intf_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx0_link_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx1_aux_clk = { + .halt_reg = 0x8080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8080, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx1_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx1_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx1_crypto_clk = { + .halt_reg = 0x807c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x807c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx1_crypto_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx1_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx1_link_clk = { + .halt_reg = 0x8070, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx1_link_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx1_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx1_link_intf_clk = { + .halt_reg = 0x8078, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8078, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx1_link_intf_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx1_link_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx1_pixel0_clk = { + .halt_reg = 0x8068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx1_pixel0_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx1_pixel0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx1_pixel1_clk = { + .halt_reg = 0x806c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x806c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx1_pixel1_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx1_pixel1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx1_usb_router_link_intf_clk = { + .halt_reg = 0x8074, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8074, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx1_usb_router_link_intf_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx1_link_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx2_aux_clk = { + .halt_reg = 0x8098, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8098, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx2_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx2_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx2_crypto_clk = { + .halt_reg = 0x8094, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8094, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx2_crypto_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx2_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx2_link_clk = { + .halt_reg = 0x808c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x808c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx2_link_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx2_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx2_link_intf_clk = { + .halt_reg = 0x8090, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8090, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx2_link_intf_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx2_link_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx2_pixel0_clk = { + .halt_reg = 0x8084, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8084, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx2_pixel0_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx2_pixel0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx2_pixel1_clk = { + .halt_reg = 0x8088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8088, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx2_pixel1_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx2_pixel1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx3_aux_clk = { + .halt_reg = 0x80a8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x80a8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx3_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx3_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx3_crypto_clk = { + .halt_reg = 0x80ac, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x80ac, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx3_crypto_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx3_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx3_link_clk = { + .halt_reg = 0x80a0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x80a0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx3_link_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx3_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx3_link_intf_clk = { + .halt_reg = 0x80a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x80a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx3_link_intf_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx3_link_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dptx3_pixel0_clk = { + .halt_reg = 0x809c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x809c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_dptx3_pixel0_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_dptx3_pixel0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_esc0_clk = { + .halt_reg = 0x8044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_esc0_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_esc0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_esc1_clk = { + .halt_reg = 0x8048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_esc1_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_esc1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_hdmi_ahbm_clk = { + .halt_reg = 0x8378, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8378, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_hdmi_ahbm_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_hdmi_app_clk = { + .halt_reg = 0x8388, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8388, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_hdmi_app_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_hdmi_app_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_hdmi_crypto_clk = { + .halt_reg = 0x8384, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8384, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_hdmi_crypto_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_hdmi_pclk_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_hdmi_intf_clk = { + .halt_reg = 0x8380, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8380, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_hdmi_intf_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_hdmi_pclk_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_hdmi_pclk_clk = { + .halt_reg = 0x837c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x837c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_hdmi_pclk_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_hdmi_pclk_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_mdp1_clk = { + .halt_reg = 0xa004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_mdp1_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_mdp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_mdp_clk = { + .halt_reg = 0x8010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_mdp_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_mdp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_mdp_lut1_clk = { + .halt_reg = 0xa014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_mdp_lut1_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_mdp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_mdp_lut_clk = { + .halt_reg = 0x8020, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x8020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_mdp_lut_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_mdp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_non_gdsc_ahb_clk = { + .halt_reg = 0xc004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xc004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_non_gdsc_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_pclk0_clk = { + .halt_reg = 0x8004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_pclk0_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_pclk0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_pclk1_clk = { + .halt_reg = 0x8008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_pclk1_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_pclk1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_pclk2_clk = { + .halt_reg = 0x800c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x800c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_pclk2_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_pclk2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_vsync1_clk = { + .halt_reg = 0xa024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa024, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_vsync1_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_vsync_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_vsync_clk = { + .halt_reg = 0x8030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_mdss_vsync_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_mdss_vsync_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_osc_clk = { + .halt_reg = 0x80b4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x80b4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "disp_cc_osc_clk", + .parent_hws = (const struct clk_hw*[]) { + &disp_cc_osc_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc mdss_gdsc = { + .gdscr = 0x9000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "mdss_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | HW_CTRL | RETAIN_FF_ENABLE, +}; + +static struct gdsc mdss_int2_gdsc = { + .gdscr = 0xb000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "mdss_int2_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | HW_CTRL | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *disp_cc_eliza_clocks[] = { + [DISP_CC_ESYNC0_CLK] = &disp_cc_esync0_clk.clkr, + [DISP_CC_ESYNC0_CLK_SRC] = &disp_cc_esync0_clk_src.clkr, + [DISP_CC_ESYNC1_CLK] = &disp_cc_esync1_clk.clkr, + [DISP_CC_ESYNC1_CLK_SRC] = &disp_cc_esync1_clk_src.clkr, + [DISP_CC_MDSS_ACCU_SHIFT_CLK] = &disp_cc_mdss_accu_shift_clk.clkr, + [DISP_CC_MDSS_AHB1_CLK] = &disp_cc_mdss_ahb1_clk.clkr, + [DISP_CC_MDSS_AHB_CLK] = &disp_cc_mdss_ahb_clk.clkr, + [DISP_CC_MDSS_AHB_CLK_SRC] = &disp_cc_mdss_ahb_clk_src.clkr, + [DISP_CC_MDSS_BYTE0_CLK] = &disp_cc_mdss_byte0_clk.clkr, + [DISP_CC_MDSS_BYTE0_CLK_SRC] = &disp_cc_mdss_byte0_clk_src.clkr, + [DISP_CC_MDSS_BYTE0_DIV_CLK_SRC] = &disp_cc_mdss_byte0_div_clk_src.clkr, + [DISP_CC_MDSS_BYTE0_INTF_CLK] = &disp_cc_mdss_byte0_intf_clk.clkr, + [DISP_CC_MDSS_BYTE1_CLK] = &disp_cc_mdss_byte1_clk.clkr, + [DISP_CC_MDSS_BYTE1_CLK_SRC] = &disp_cc_mdss_byte1_clk_src.clkr, + [DISP_CC_MDSS_BYTE1_DIV_CLK_SRC] = &disp_cc_mdss_byte1_div_clk_src.clkr, + [DISP_CC_MDSS_BYTE1_INTF_CLK] = &disp_cc_mdss_byte1_intf_clk.clkr, + [DISP_CC_MDSS_DPTX0_AUX_CLK] = &disp_cc_mdss_dptx0_aux_clk.clkr, + [DISP_CC_MDSS_DPTX0_AUX_CLK_SRC] = &disp_cc_mdss_dptx0_aux_clk_src.clkr, + [DISP_CC_MDSS_DPTX0_CRYPTO_CLK] = &disp_cc_mdss_dptx0_crypto_clk.clkr, + [DISP_CC_MDSS_DPTX0_LINK_CLK] = &disp_cc_mdss_dptx0_link_clk.clkr, + [DISP_CC_MDSS_DPTX0_LINK_CLK_SRC] = &disp_cc_mdss_dptx0_link_clk_src.clkr, + [DISP_CC_MDSS_DPTX0_LINK_DIV_CLK_SRC] = &disp_cc_mdss_dptx0_link_div_clk_src.clkr, + [DISP_CC_MDSS_DPTX0_LINK_INTF_CLK] = &disp_cc_mdss_dptx0_link_intf_clk.clkr, + [DISP_CC_MDSS_DPTX0_PIXEL0_CLK] = &disp_cc_mdss_dptx0_pixel0_clk.clkr, + [DISP_CC_MDSS_DPTX0_PIXEL0_CLK_SRC] = &disp_cc_mdss_dptx0_pixel0_clk_src.clkr, + [DISP_CC_MDSS_DPTX0_PIXEL1_CLK] = &disp_cc_mdss_dptx0_pixel1_clk.clkr, + [DISP_CC_MDSS_DPTX0_PIXEL1_CLK_SRC] = &disp_cc_mdss_dptx0_pixel1_clk_src.clkr, + [DISP_CC_MDSS_DPTX0_USB_ROUTER_LINK_INTF_CLK] = + &disp_cc_mdss_dptx0_usb_router_link_intf_clk.clkr, + [DISP_CC_MDSS_DPTX1_AUX_CLK] = &disp_cc_mdss_dptx1_aux_clk.clkr, + [DISP_CC_MDSS_DPTX1_AUX_CLK_SRC] = &disp_cc_mdss_dptx1_aux_clk_src.clkr, + [DISP_CC_MDSS_DPTX1_CRYPTO_CLK] = &disp_cc_mdss_dptx1_crypto_clk.clkr, + [DISP_CC_MDSS_DPTX1_LINK_CLK] = &disp_cc_mdss_dptx1_link_clk.clkr, + [DISP_CC_MDSS_DPTX1_LINK_CLK_SRC] = &disp_cc_mdss_dptx1_link_clk_src.clkr, + [DISP_CC_MDSS_DPTX1_LINK_DIV_CLK_SRC] = &disp_cc_mdss_dptx1_link_div_clk_src.clkr, + [DISP_CC_MDSS_DPTX1_LINK_INTF_CLK] = &disp_cc_mdss_dptx1_link_intf_clk.clkr, + [DISP_CC_MDSS_DPTX1_PIXEL0_CLK] = &disp_cc_mdss_dptx1_pixel0_clk.clkr, + [DISP_CC_MDSS_DPTX1_PIXEL0_CLK_SRC] = &disp_cc_mdss_dptx1_pixel0_clk_src.clkr, + [DISP_CC_MDSS_DPTX1_PIXEL1_CLK] = &disp_cc_mdss_dptx1_pixel1_clk.clkr, + [DISP_CC_MDSS_DPTX1_PIXEL1_CLK_SRC] = &disp_cc_mdss_dptx1_pixel1_clk_src.clkr, + [DISP_CC_MDSS_DPTX1_USB_ROUTER_LINK_INTF_CLK] = + &disp_cc_mdss_dptx1_usb_router_link_intf_clk.clkr, + [DISP_CC_MDSS_DPTX2_AUX_CLK] = &disp_cc_mdss_dptx2_aux_clk.clkr, + [DISP_CC_MDSS_DPTX2_AUX_CLK_SRC] = &disp_cc_mdss_dptx2_aux_clk_src.clkr, + [DISP_CC_MDSS_DPTX2_CRYPTO_CLK] = &disp_cc_mdss_dptx2_crypto_clk.clkr, + [DISP_CC_MDSS_DPTX2_LINK_CLK] = &disp_cc_mdss_dptx2_link_clk.clkr, + [DISP_CC_MDSS_DPTX2_LINK_CLK_SRC] = &disp_cc_mdss_dptx2_link_clk_src.clkr, + [DISP_CC_MDSS_DPTX2_LINK_DIV_CLK_SRC] = &disp_cc_mdss_dptx2_link_div_clk_src.clkr, + [DISP_CC_MDSS_DPTX2_LINK_INTF_CLK] = &disp_cc_mdss_dptx2_link_intf_clk.clkr, + [DISP_CC_MDSS_DPTX2_PIXEL0_CLK] = &disp_cc_mdss_dptx2_pixel0_clk.clkr, + [DISP_CC_MDSS_DPTX2_PIXEL0_CLK_SRC] = &disp_cc_mdss_dptx2_pixel0_clk_src.clkr, + [DISP_CC_MDSS_DPTX2_PIXEL1_CLK] = &disp_cc_mdss_dptx2_pixel1_clk.clkr, + [DISP_CC_MDSS_DPTX2_PIXEL1_CLK_SRC] = &disp_cc_mdss_dptx2_pixel1_clk_src.clkr, + [DISP_CC_MDSS_DPTX3_AUX_CLK] = &disp_cc_mdss_dptx3_aux_clk.clkr, + [DISP_CC_MDSS_DPTX3_AUX_CLK_SRC] = &disp_cc_mdss_dptx3_aux_clk_src.clkr, + [DISP_CC_MDSS_DPTX3_CRYPTO_CLK] = &disp_cc_mdss_dptx3_crypto_clk.clkr, + [DISP_CC_MDSS_DPTX3_LINK_CLK] = &disp_cc_mdss_dptx3_link_clk.clkr, + [DISP_CC_MDSS_DPTX3_LINK_CLK_SRC] = &disp_cc_mdss_dptx3_link_clk_src.clkr, + [DISP_CC_MDSS_DPTX3_LINK_DIV_CLK_SRC] = &disp_cc_mdss_dptx3_link_div_clk_src.clkr, + [DISP_CC_MDSS_DPTX3_LINK_INTF_CLK] = &disp_cc_mdss_dptx3_link_intf_clk.clkr, + [DISP_CC_MDSS_DPTX3_PIXEL0_CLK] = &disp_cc_mdss_dptx3_pixel0_clk.clkr, + [DISP_CC_MDSS_DPTX3_PIXEL0_CLK_SRC] = &disp_cc_mdss_dptx3_pixel0_clk_src.clkr, + [DISP_CC_MDSS_ESC0_CLK] = &disp_cc_mdss_esc0_clk.clkr, + [DISP_CC_MDSS_ESC0_CLK_SRC] = &disp_cc_mdss_esc0_clk_src.clkr, + [DISP_CC_MDSS_ESC1_CLK] = &disp_cc_mdss_esc1_clk.clkr, + [DISP_CC_MDSS_ESC1_CLK_SRC] = &disp_cc_mdss_esc1_clk_src.clkr, + [DISP_CC_MDSS_HDMI_AHBM_CLK] = &disp_cc_mdss_hdmi_ahbm_clk.clkr, + [DISP_CC_MDSS_HDMI_APP_CLK] = &disp_cc_mdss_hdmi_app_clk.clkr, + [DISP_CC_MDSS_HDMI_APP_CLK_SRC] = &disp_cc_mdss_hdmi_app_clk_src.clkr, + [DISP_CC_MDSS_HDMI_CRYPTO_CLK] = &disp_cc_mdss_hdmi_crypto_clk.clkr, + [DISP_CC_MDSS_HDMI_INTF_CLK] = &disp_cc_mdss_hdmi_intf_clk.clkr, + [DISP_CC_MDSS_HDMI_PCLK_CLK] = &disp_cc_mdss_hdmi_pclk_clk.clkr, + [DISP_CC_MDSS_HDMI_PCLK_CLK_SRC] = &disp_cc_mdss_hdmi_pclk_clk_src.clkr, + [DISP_CC_MDSS_HDMI_PCLK_DIV_CLK_SRC] = &disp_cc_mdss_hdmi_pclk_div_clk_src.clkr, + [DISP_CC_MDSS_MDP1_CLK] = &disp_cc_mdss_mdp1_clk.clkr, + [DISP_CC_MDSS_MDP_CLK] = &disp_cc_mdss_mdp_clk.clkr, + [DISP_CC_MDSS_MDP_CLK_SRC] = &disp_cc_mdss_mdp_clk_src.clkr, + [DISP_CC_MDSS_MDP_LUT1_CLK] = &disp_cc_mdss_mdp_lut1_clk.clkr, + [DISP_CC_MDSS_MDP_LUT_CLK] = &disp_cc_mdss_mdp_lut_clk.clkr, + [DISP_CC_MDSS_NON_GDSC_AHB_CLK] = &disp_cc_mdss_non_gdsc_ahb_clk.clkr, + [DISP_CC_MDSS_PCLK0_CLK] = &disp_cc_mdss_pclk0_clk.clkr, + [DISP_CC_MDSS_PCLK0_CLK_SRC] = &disp_cc_mdss_pclk0_clk_src.clkr, + [DISP_CC_MDSS_PCLK1_CLK] = &disp_cc_mdss_pclk1_clk.clkr, + [DISP_CC_MDSS_PCLK1_CLK_SRC] = &disp_cc_mdss_pclk1_clk_src.clkr, + [DISP_CC_MDSS_PCLK2_CLK] = &disp_cc_mdss_pclk2_clk.clkr, + [DISP_CC_MDSS_PCLK2_CLK_SRC] = &disp_cc_mdss_pclk2_clk_src.clkr, + [DISP_CC_MDSS_VSYNC1_CLK] = &disp_cc_mdss_vsync1_clk.clkr, + [DISP_CC_MDSS_VSYNC_CLK] = &disp_cc_mdss_vsync_clk.clkr, + [DISP_CC_MDSS_VSYNC_CLK_SRC] = &disp_cc_mdss_vsync_clk_src.clkr, + [DISP_CC_OSC_CLK] = &disp_cc_osc_clk.clkr, + [DISP_CC_OSC_CLK_SRC] = &disp_cc_osc_clk_src.clkr, + [DISP_CC_PLL0] = &disp_cc_pll0.clkr, + [DISP_CC_PLL1] = &disp_cc_pll1.clkr, + [DISP_CC_PLL2] = &disp_cc_pll2.clkr, + [DISP_CC_SLEEP_CLK_SRC] = &disp_cc_sleep_clk_src.clkr, + [DISP_CC_XO_CLK_SRC] = &disp_cc_xo_clk_src.clkr, +}; + +static const struct qcom_reset_map disp_cc_eliza_resets[] = { + [DISP_CC_MDSS_CORE_BCR] = { 0x8000 }, + [DISP_CC_MDSS_CORE_INT2_BCR] = { 0xa000 }, + [DISP_CC_MDSS_RSCC_BCR] = { 0xc000 }, +}; + +static struct gdsc *disp_cc_eliza_gdscs[] = { + [MDSS_GDSC] = &mdss_gdsc, + [MDSS_INT2_GDSC] = &mdss_int2_gdsc, +}; + +static const struct regmap_config disp_cc_eliza_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf004, /* 0x10000, 0x10004 and maybe others are for TZ */ + .fast_io = true, +}; + +static struct clk_alpha_pll *disp_cc_eliza_plls[] = { + &disp_cc_pll0, + &disp_cc_pll1, + &disp_cc_pll2, +}; + +static const u32 disp_cc_eliza_critical_cbcrs[] = { + 0xe07c, /* DISP_CC_SLEEP_CLK */ + 0xe05c, /* DISP_CC_XO_CLK */ + 0xc00c, /* DISP_CC_MDSS_RSCC_AHB_CLK */ + 0xc008, /* DISP_CC_MDSS_RSCC_VSYNC_CLK */ +}; + +static void clk_eliza_regs_configure(struct device *dev, struct regmap *regmap) +{ + /* Enable clock gating for MDP clocks */ + regmap_set_bits(regmap, DISP_CC_MISC_CMD, BIT(4)); +} + +static const struct qcom_cc_driver_data disp_cc_eliza_driver_data = { + .alpha_plls = disp_cc_eliza_plls, + .num_alpha_plls = ARRAY_SIZE(disp_cc_eliza_plls), + .clk_cbcrs = disp_cc_eliza_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(disp_cc_eliza_critical_cbcrs), + .clk_regs_configure = clk_eliza_regs_configure, +}; + +static const struct qcom_cc_desc disp_cc_eliza_desc = { + .config = &disp_cc_eliza_regmap_config, + .clks = disp_cc_eliza_clocks, + .num_clks = ARRAY_SIZE(disp_cc_eliza_clocks), + .resets = disp_cc_eliza_resets, + .num_resets = ARRAY_SIZE(disp_cc_eliza_resets), + .gdscs = disp_cc_eliza_gdscs, + .num_gdscs = ARRAY_SIZE(disp_cc_eliza_gdscs), + .use_rpm = true, + .driver_data = &disp_cc_eliza_driver_data, +}; + +static const struct of_device_id disp_cc_eliza_match_table[] = { + { .compatible = "qcom,eliza-dispcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, disp_cc_eliza_match_table); + +static int disp_cc_eliza_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &disp_cc_eliza_desc); +} + +static struct platform_driver disp_cc_eliza_driver = { + .probe = disp_cc_eliza_probe, + .driver = { + .name = "dispcc-eliza", + .of_match_table = disp_cc_eliza_match_table, + }, +}; + +module_platform_driver(disp_cc_eliza_driver); + +MODULE_DESCRIPTION("QTI DISPCC Eliza Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/dispcc-glymur.c b/drivers/clk/qcom/dispcc-glymur.c index 5203fa6383f6..c4bb328d432f 100644 --- a/drivers/clk/qcom/dispcc-glymur.c +++ b/drivers/clk/qcom/dispcc-glymur.c @@ -6,9 +6,7 @@ #include #include #include -#include #include -#include #include #include @@ -417,7 +415,7 @@ static struct clk_rcg2 disp_cc_mdss_dptx1_aux_clk_src = { .parent_data = disp_cc_parent_data_1, .num_parents = ARRAY_SIZE(disp_cc_parent_data_1), .flags = CLK_SET_RATE_PARENT, - .ops = &clk_dp_ops, + .ops = &clk_rcg2_shared_ops, }, }; @@ -747,7 +745,6 @@ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { &disp_cc_mdss_byte0_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; @@ -762,7 +759,6 @@ static struct clk_regmap_div disp_cc_mdss_byte1_div_clk_src = { &disp_cc_mdss_byte1_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; @@ -1925,7 +1921,7 @@ static struct clk_alpha_pll *disp_cc_glymur_plls[] = { &disp_cc_pll1, }; -static u32 disp_cc_glymur_critical_cbcrs[] = { +static const u32 disp_cc_glymur_critical_cbcrs[] = { 0xe07c, /* DISP_CC_SLEEP_CLK */ 0xe05c, /* DISP_CC_XO_CLK */ }; @@ -1938,7 +1934,7 @@ static const struct regmap_config disp_cc_glymur_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data disp_cc_glymur_driver_data = { +static const struct qcom_cc_driver_data disp_cc_glymur_driver_data = { .alpha_plls = disp_cc_glymur_plls, .num_alpha_plls = ARRAY_SIZE(disp_cc_glymur_plls), .clk_cbcrs = disp_cc_glymur_critical_cbcrs, @@ -1978,5 +1974,5 @@ static struct platform_driver disp_cc_glymur_driver = { module_platform_driver(disp_cc_glymur_driver); -MODULE_DESCRIPTION("QTI DISPCC GLYMUR Driver"); +MODULE_DESCRIPTION("QTI DISPCC Glymur Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/dispcc-kaanapali.c b/drivers/clk/qcom/dispcc-kaanapali.c index baae2ec1f72a..42912c617c31 100644 --- a/drivers/clk/qcom/dispcc-kaanapali.c +++ b/drivers/clk/qcom/dispcc-kaanapali.c @@ -6,9 +6,7 @@ #include #include #include -#include #include -#include #include #include @@ -800,7 +798,6 @@ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { &disp_cc_mdss_byte0_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; @@ -815,7 +812,6 @@ static struct clk_regmap_div disp_cc_mdss_byte1_div_clk_src = { &disp_cc_mdss_byte1_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; @@ -1890,7 +1886,7 @@ static struct clk_alpha_pll *disp_cc_kaanapali_plls[] = { &disp_cc_pll2, }; -static u32 disp_cc_kaanapali_critical_cbcrs[] = { +static const u32 disp_cc_kaanapali_critical_cbcrs[] = { 0xe064, /* DISP_CC_SLEEP_CLK */ 0xe05c, /* DISP_CC_XO_CLK */ 0xc00c, /* DISP_CC_MDSS_RSCC_AHB_CLK */ @@ -1911,7 +1907,7 @@ static void clk_kaanapali_regs_configure(struct device *dev, struct regmap *regm regmap_update_bits(regmap, DISP_CC_MISC_CMD, BIT(4), BIT(4)); } -static struct qcom_cc_driver_data disp_cc_kaanapali_driver_data = { +static const struct qcom_cc_driver_data disp_cc_kaanapali_driver_data = { .alpha_plls = disp_cc_kaanapali_plls, .num_alpha_plls = ARRAY_SIZE(disp_cc_kaanapali_plls), .clk_cbcrs = disp_cc_kaanapali_critical_cbcrs, diff --git a/drivers/clk/qcom/dispcc-milos.c b/drivers/clk/qcom/dispcc-milos.c index 95b6dd89d9ae..dfffb6d14b0e 100644 --- a/drivers/clk/qcom/dispcc-milos.c +++ b/drivers/clk/qcom/dispcc-milos.c @@ -4,12 +4,10 @@ * Copyright (c) 2025, Luca Weiss */ -#include #include -#include #include +#include #include -#include #include #include @@ -394,7 +392,6 @@ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { &disp_cc_mdss_byte0_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; @@ -909,7 +906,7 @@ static struct clk_alpha_pll *disp_cc_milos_plls[] = { &disp_cc_pll0, }; -static u32 disp_cc_milos_critical_cbcrs[] = { +static const u32 disp_cc_milos_critical_cbcrs[] = { 0xe06c, /* DISP_CC_SLEEP_CLK */ 0xe04c, /* DISP_CC_XO_CLK */ }; @@ -929,7 +926,7 @@ static void disp_cc_milos_clk_regs_configure(struct device *dev, struct regmap * } -static struct qcom_cc_driver_data disp_cc_milos_driver_data = { +static const struct qcom_cc_driver_data disp_cc_milos_driver_data = { .alpha_plls = disp_cc_milos_plls, .num_alpha_plls = ARRAY_SIZE(disp_cc_milos_plls), .clk_cbcrs = disp_cc_milos_critical_cbcrs, diff --git a/drivers/clk/qcom/dispcc-qcs615.c b/drivers/clk/qcom/dispcc-qcs615.c index 4a6d78466098..637698e6dc2b 100644 --- a/drivers/clk/qcom/dispcc-qcs615.c +++ b/drivers/clk/qcom/dispcc-qcs615.c @@ -739,7 +739,7 @@ static struct clk_alpha_pll *disp_cc_qcs615_plls[] = { &disp_cc_pll0, }; -static u32 disp_cc_qcs615_critical_cbcrs[] = { +static const u32 disp_cc_qcs615_critical_cbcrs[] = { 0x6054, /* DISP_CC_XO_CLK */ }; @@ -751,7 +751,7 @@ static const struct regmap_config disp_cc_qcs615_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data disp_cc_qcs615_driver_data = { +static const struct qcom_cc_driver_data disp_cc_qcs615_driver_data = { .alpha_plls = disp_cc_qcs615_plls, .num_alpha_plls = ARRAY_SIZE(disp_cc_qcs615_plls), .clk_cbcrs = disp_cc_qcs615_critical_cbcrs, diff --git a/drivers/clk/qcom/dispcc-sc7180.c b/drivers/clk/qcom/dispcc-sc7180.c index ab1a8d419863..d7e37fbbe87e 100644 --- a/drivers/clk/qcom/dispcc-sc7180.c +++ b/drivers/clk/qcom/dispcc-sc7180.c @@ -17,6 +17,7 @@ #include "clk-regmap-divider.h" #include "common.h" #include "gdsc.h" +#include "reset.h" enum { P_BI_TCXO, @@ -636,6 +637,11 @@ static struct gdsc mdss_gdsc = { .flags = HW_CTRL, }; +static const struct qcom_reset_map disp_cc_sc7180_resets[] = { + [DISP_CC_MDSS_CORE_BCR] = { 0x2000 }, + [DISP_CC_MDSS_RSCC_BCR] = { 0x4000 }, +}; + static struct gdsc *disp_cc_sc7180_gdscs[] = { [MDSS_GDSC] = &mdss_gdsc, }; @@ -687,6 +693,8 @@ static const struct qcom_cc_desc disp_cc_sc7180_desc = { .config = &disp_cc_sc7180_regmap_config, .clks = disp_cc_sc7180_clocks, .num_clks = ARRAY_SIZE(disp_cc_sc7180_clocks), + .resets = disp_cc_sc7180_resets, + .num_resets = ARRAY_SIZE(disp_cc_sc7180_resets), .gdscs = disp_cc_sc7180_gdscs, .num_gdscs = ARRAY_SIZE(disp_cc_sc7180_gdscs), }; diff --git a/drivers/clk/qcom/dispcc-sc8280xp.c b/drivers/clk/qcom/dispcc-sc8280xp.c index 5903a759d4af..e91dfed0f37e 100644 --- a/drivers/clk/qcom/dispcc-sc8280xp.c +++ b/drivers/clk/qcom/dispcc-sc8280xp.c @@ -1160,7 +1160,6 @@ static struct clk_regmap_div disp0_cc_mdss_byte0_div_clk_src = { &disp0_cc_mdss_byte0_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; @@ -1175,7 +1174,6 @@ static struct clk_regmap_div disp1_cc_mdss_byte0_div_clk_src = { &disp1_cc_mdss_byte0_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; @@ -1190,7 +1188,6 @@ static struct clk_regmap_div disp0_cc_mdss_byte1_div_clk_src = { &disp0_cc_mdss_byte1_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; @@ -1205,7 +1202,6 @@ static struct clk_regmap_div disp1_cc_mdss_byte1_div_clk_src = { &disp1_cc_mdss_byte1_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; diff --git a/drivers/clk/qcom/dispcc-sm4450.c b/drivers/clk/qcom/dispcc-sm4450.c index e8752d01c8e6..2fdacc26df69 100644 --- a/drivers/clk/qcom/dispcc-sm4450.c +++ b/drivers/clk/qcom/dispcc-sm4450.c @@ -335,7 +335,6 @@ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { &disp_cc_mdss_byte0_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; diff --git a/drivers/clk/qcom/dispcc-sm6115.c b/drivers/clk/qcom/dispcc-sm6115.c index 8ae25d51db94..75bd57213079 100644 --- a/drivers/clk/qcom/dispcc-sm6115.c +++ b/drivers/clk/qcom/dispcc-sm6115.c @@ -22,6 +22,7 @@ #include "clk-regmap-divider.h" #include "common.h" #include "gdsc.h" +#include "reset.h" enum { DT_BI_TCXO, @@ -511,6 +512,10 @@ static struct clk_branch disp_cc_sleep_clk = { }, }; +static const struct qcom_reset_map disp_cc_sm6115_resets[] = { + [DISP_CC_MDSS_CORE_BCR] = { 0x2000 }, +}; + static struct gdsc mdss_gdsc = { .gdscr = 0x3000, .pd = { @@ -561,6 +566,8 @@ static const struct qcom_cc_desc disp_cc_sm6115_desc = { .config = &disp_cc_sm6115_regmap_config, .clks = disp_cc_sm6115_clocks, .num_clks = ARRAY_SIZE(disp_cc_sm6115_clocks), + .resets = disp_cc_sm6115_resets, + .num_resets = ARRAY_SIZE(disp_cc_sm6115_resets), .gdscs = disp_cc_sm6115_gdscs, .num_gdscs = ARRAY_SIZE(disp_cc_sm6115_gdscs), }; diff --git a/drivers/clk/qcom/dispcc-sm6125.c b/drivers/clk/qcom/dispcc-sm6125.c index 851d38a487d3..2c67abcfef12 100644 --- a/drivers/clk/qcom/dispcc-sm6125.c +++ b/drivers/clk/qcom/dispcc-sm6125.c @@ -17,6 +17,7 @@ #include "clk-regmap.h" #include "common.h" #include "gdsc.h" +#include "reset.h" enum { P_BI_TCXO, @@ -607,6 +608,10 @@ static struct clk_branch disp_cc_xo_clk = { }, }; +static const struct qcom_reset_map disp_cc_sm6125_resets[] = { + [DISP_CC_MDSS_CORE_BCR] = { 0x2000 }, +}; + static struct gdsc mdss_gdsc = { .gdscr = 0x3000, .pd = { @@ -663,6 +668,8 @@ static const struct qcom_cc_desc disp_cc_sm6125_desc = { .config = &disp_cc_sm6125_regmap_config, .clks = disp_cc_sm6125_clocks, .num_clks = ARRAY_SIZE(disp_cc_sm6125_clocks), + .resets = disp_cc_sm6125_resets, + .num_resets = ARRAY_SIZE(disp_cc_sm6125_resets), .gdscs = disp_cc_sm6125_gdscs, .num_gdscs = ARRAY_SIZE(disp_cc_sm6125_gdscs), }; diff --git a/drivers/clk/qcom/dispcc-sm8250.c b/drivers/clk/qcom/dispcc-sm8250.c index 8f433e1e7028..e59cdadd5647 100644 --- a/drivers/clk/qcom/dispcc-sm8250.c +++ b/drivers/clk/qcom/dispcc-sm8250.c @@ -578,7 +578,7 @@ static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = { .name = "disp_cc_mdss_pclk0_clk_src", .parent_data = disp_cc_parent_data_6, .num_parents = ARRAY_SIZE(disp_cc_parent_data_6), - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, .ops = &clk_pixel_ops, }, }; @@ -592,7 +592,7 @@ static struct clk_rcg2 disp_cc_mdss_pclk1_clk_src = { .name = "disp_cc_mdss_pclk1_clk_src", .parent_data = disp_cc_parent_data_6, .num_parents = ARRAY_SIZE(disp_cc_parent_data_6), - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, .ops = &clk_pixel_ops, }, }; @@ -632,7 +632,7 @@ static struct clk_rcg2 disp_cc_mdss_vsync_clk_src = { .parent_data = disp_cc_parent_data_1, .num_parents = ARRAY_SIZE(disp_cc_parent_data_1), .flags = CLK_SET_RATE_PARENT, - .ops = &clk_rcg2_ops, + .ops = &clk_rcg2_shared_ops, }, }; diff --git a/drivers/clk/qcom/dispcc-sm8450.c b/drivers/clk/qcom/dispcc-sm8450.c index 9ce9fd28e55b..2e91332dd92a 100644 --- a/drivers/clk/qcom/dispcc-sm8450.c +++ b/drivers/clk/qcom/dispcc-sm8450.c @@ -409,7 +409,7 @@ static struct clk_rcg2 disp_cc_mdss_dptx1_aux_clk_src = { .parent_data = disp_cc_parent_data_1, .num_parents = ARRAY_SIZE(disp_cc_parent_data_1), .flags = CLK_SET_RATE_PARENT, - .ops = &clk_dp_ops, + .ops = &clk_rcg2_ops, }, }; diff --git a/drivers/clk/qcom/dispcc0-sa8775p.c b/drivers/clk/qcom/dispcc0-sa8775p.c index aeda9cf4bfee..b248fa970587 100644 --- a/drivers/clk/qcom/dispcc0-sa8775p.c +++ b/drivers/clk/qcom/dispcc0-sa8775p.c @@ -591,7 +591,6 @@ static struct clk_regmap_div mdss_0_disp_cc_mdss_byte0_div_clk_src = { &mdss_0_disp_cc_mdss_byte0_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; @@ -606,7 +605,6 @@ static struct clk_regmap_div mdss_0_disp_cc_mdss_byte1_div_clk_src = { &mdss_0_disp_cc_mdss_byte1_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; diff --git a/drivers/clk/qcom/dispcc1-sa8775p.c b/drivers/clk/qcom/dispcc1-sa8775p.c index cd55d1c11902..9882edbb79f9 100644 --- a/drivers/clk/qcom/dispcc1-sa8775p.c +++ b/drivers/clk/qcom/dispcc1-sa8775p.c @@ -591,7 +591,6 @@ static struct clk_regmap_div mdss_1_disp_cc_mdss_byte0_div_clk_src = { &mdss_1_disp_cc_mdss_byte0_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; @@ -606,7 +605,6 @@ static struct clk_regmap_div mdss_1_disp_cc_mdss_byte1_div_clk_src = { &mdss_1_disp_cc_mdss_byte1_clk_src.clkr.hw, }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_regmap_div_ops, }, }; diff --git a/drivers/clk/qcom/gcc-eliza.c b/drivers/clk/qcom/gcc-eliza.c new file mode 100644 index 000000000000..24c3aae0810f --- /dev/null +++ b/drivers/clk/qcom/gcc-eliza.c @@ -0,0 +1,3105 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "clk-regmap-phy-mux.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_BI_TCXO, + DT_SLEEP_CLK, + DT_PCIE_0_PIPE_CLK, + DT_PCIE_1_PIPE_CLK, + DT_UFS_PHY_RX_SYMBOL_0_CLK, + DT_UFS_PHY_RX_SYMBOL_1_CLK, + DT_UFS_PHY_TX_SYMBOL_0_CLK, + DT_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, +}; + +enum { + P_BI_TCXO, + P_GCC_GPLL0_OUT_EVEN, + P_GCC_GPLL0_OUT_MAIN, + P_GCC_GPLL4_OUT_MAIN, + P_GCC_GPLL7_OUT_MAIN, + P_GCC_GPLL8_OUT_MAIN, + P_GCC_GPLL9_OUT_MAIN, + P_PCIE_0_PIPE_CLK, + P_PCIE_1_PIPE_CLK, + P_SLEEP_CLK, + P_UFS_PHY_RX_SYMBOL_0_CLK, + P_UFS_PHY_RX_SYMBOL_1_CLK, + P_UFS_PHY_TX_SYMBOL_0_CLK, + P_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, +}; + +static struct clk_alpha_pll gcc_gpll0 = { + .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gcc_gpll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gcc_gpll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_gcc_gpll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_gcc_gpll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +static struct clk_alpha_pll gcc_gpll4 = { + .offset = 0x4000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll4", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static struct clk_alpha_pll gcc_gpll7 = { + .offset = 0x7000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll7", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static struct clk_alpha_pll gcc_gpll8 = { + .offset = 0x8000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll8", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static struct clk_alpha_pll gcc_gpll9 = { + .offset = 0x9000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(9), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll9", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static const struct parent_map gcc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_SLEEP_CLK, 5 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL4_OUT_MAIN, 5 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll4.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_SLEEP_CLK, 5 }, +}; + +static const struct clk_parent_data gcc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL7_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll7.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL8_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_5[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll8.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_6[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data gcc_parent_data_6[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map gcc_parent_map_7[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL9_OUT_MAIN, 2 }, + { P_GCC_GPLL4_OUT_MAIN, 5 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_7[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll9.clkr.hw }, + { .hw = &gcc_gpll4.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_8[] = { + { P_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, 0 }, + { P_BI_TCXO, 2 }, + { P_GCC_GPLL0_OUT_EVEN, 3 }, +}; + +static const struct clk_parent_data gcc_parent_data_8[] = { + { .index = DT_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK }, + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static struct clk_regmap_phy_mux gcc_pcie_0_pipe_clk_src = { + .reg = 0x6b080, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_0_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie_1_pipe_clk_src = { + .reg = 0xac07c, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_1_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_ufs_phy_rx_symbol_0_clk_src = { + .reg = 0x77068, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_0_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_UFS_PHY_RX_SYMBOL_0_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_ufs_phy_rx_symbol_1_clk_src = { + .reg = 0x770ec, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_1_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_UFS_PHY_RX_SYMBOL_1_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_ufs_phy_tx_symbol_0_clk_src = { + .reg = 0x77058, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_tx_symbol_0_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_UFS_PHY_TX_SYMBOL_0_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_mux gcc_usb3_prim_phy_pipe_clk_src = { + .reg = 0x39070, + .shift = 0, + .width = 2, + .parent_map = gcc_parent_map_8, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_pipe_clk_src", + .parent_data = gcc_parent_data_8, + .num_parents = ARRAY_SIZE(gcc_parent_data_8), + .ops = &clk_regmap_mux_closest_ops, + }, + }, +}; + +static const struct freq_tbl ftbl_gcc_gp1_clk_src[] = { + F(50000000, P_GCC_GPLL0_OUT_EVEN, 6, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(200000000, P_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_gp1_clk_src = { + .cmd_rcgr = 0x64004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp1_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_gp2_clk_src = { + .cmd_rcgr = 0x65004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp2_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_gp3_clk_src = { + .cmd_rcgr = 0x66004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp3_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_0_aux_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie_0_aux_clk_src = { + .cmd_rcgr = 0x6b084, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_aux_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_0_phy_rchng_clk_src[] = { + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie_0_phy_rchng_clk_src = { + .cmd_rcgr = 0x6b068, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_phy_rchng_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_phy_rchng_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_1_aux_clk_src = { + .cmd_rcgr = 0xac080, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_aux_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_1_phy_rchng_clk_src = { + .cmd_rcgr = 0xac064, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_phy_rchng_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_phy_rchng_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pdm2_clk_src[] = { + F(60000000, P_GCC_GPLL0_OUT_MAIN, 10, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pdm2_clk_src = { + .cmd_rcgr = 0x33010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pdm2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap1_qspi_ref_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 375), + F(112000000, P_GCC_GPLL0_OUT_EVEN, 1, 28, 75), + F(117964800, P_GCC_GPLL0_OUT_EVEN, 1, 6144, 15625), + F(120000000, P_GCC_GPLL0_OUT_MAIN, 5, 0, 0), + F(150000000, P_GCC_GPLL0_OUT_EVEN, 2, 0, 0), + F(250000000, P_GCC_GPLL7_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap1_qspi_ref_clk_src_init = { + .name = "gcc_qupv3_wrap1_qspi_ref_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_qspi_ref_clk_src = { + .cmd_rcgr = 0x188c0, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_qupv3_wrap1_qspi_ref_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_qspi_ref_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap1_s0_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(61440000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 625), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 375), + F(112000000, P_GCC_GPLL0_OUT_EVEN, 1, 28, 75), + F(117964800, P_GCC_GPLL0_OUT_EVEN, 1, 6144, 15625), + F(120000000, P_GCC_GPLL0_OUT_MAIN, 5, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap1_s0_clk_src_init = { + .name = "gcc_qupv3_wrap1_s0_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s0_clk_src = { + .cmd_rcgr = 0x18014, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s1_clk_src_init = { + .name = "gcc_qupv3_wrap1_s1_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s1_clk_src = { + .cmd_rcgr = 0x18150, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s1_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap1_s3_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(61440000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 625), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap1_s3_clk_src_init = { + .name = "gcc_qupv3_wrap1_s3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s3_clk_src = { + .cmd_rcgr = 0x182a0, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s3_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s3_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s4_clk_src_init = { + .name = "gcc_qupv3_wrap1_s4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s4_clk_src = { + .cmd_rcgr = 0x183dc, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s4_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap1_s5_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(61440000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 625), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 375), + F(112000000, P_GCC_GPLL0_OUT_EVEN, 1, 28, 75), + F(117964800, P_GCC_GPLL0_OUT_EVEN, 1, 6144, 15625), + F(128000000, P_GCC_GPLL0_OUT_MAIN, 1, 16, 75), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap1_s5_clk_src_init = { + .name = "gcc_qupv3_wrap1_s5_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s5_clk_src = { + .cmd_rcgr = 0x18518, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s5_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s5_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s6_clk_src_init = { + .name = "gcc_qupv3_wrap1_s6_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s6_clk_src = { + .cmd_rcgr = 0x18654, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s3_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s6_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s7_clk_src_init = { + .name = "gcc_qupv3_wrap1_s7_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s7_clk_src = { + .cmd_rcgr = 0x18790, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s3_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s7_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s0_clk_src_init = { + .name = "gcc_qupv3_wrap2_s0_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s0_clk_src = { + .cmd_rcgr = 0x1e014, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s1_clk_src_init = { + .name = "gcc_qupv3_wrap2_s1_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s1_clk_src = { + .cmd_rcgr = 0x1e150, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s1_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap2_s2_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(61440000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 625), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 375), + F(112000000, P_GCC_GPLL0_OUT_EVEN, 1, 28, 75), + F(117964800, P_GCC_GPLL0_OUT_EVEN, 1, 6144, 15625), + F(125000000, P_GCC_GPLL7_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap2_s2_clk_src_init = { + .name = "gcc_qupv3_wrap2_s2_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s2_clk_src = { + .cmd_rcgr = 0x1e28c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_qupv3_wrap2_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s2_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s3_clk_src_init = { + .name = "gcc_qupv3_wrap2_s3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s3_clk_src = { + .cmd_rcgr = 0x1e3c8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s3_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s3_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s4_clk_src_init = { + .name = "gcc_qupv3_wrap2_s4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s4_clk_src = { + .cmd_rcgr = 0x1e504, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s4_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s5_clk_src_init = { + .name = "gcc_qupv3_wrap2_s5_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s5_clk_src = { + .cmd_rcgr = 0x1e640, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s5_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s5_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s6_clk_src_init = { + .name = "gcc_qupv3_wrap2_s6_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s6_clk_src = { + .cmd_rcgr = 0x1e77c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s3_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s6_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s7_clk_src_init = { + .name = "gcc_qupv3_wrap2_s7_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s7_clk_src = { + .cmd_rcgr = 0x1e8b8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s3_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s7_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_src[] = { + F(144000, P_BI_TCXO, 16, 3, 25), + F(400000, P_BI_TCXO, 12, 1, 4), + F(20000000, P_GCC_GPLL0_OUT_EVEN, 5, 1, 3), + F(25000000, P_GCC_GPLL0_OUT_EVEN, 12, 0, 0), + F(50000000, P_GCC_GPLL0_OUT_EVEN, 6, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(192000000, P_GCC_GPLL8_OUT_MAIN, 2, 0, 0), + F(384000000, P_GCC_GPLL8_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_apps_clk_src = { + .cmd_rcgr = 0xa9018, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_sdcc1_apps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_apps_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_ice_core_clk_src[] = { + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(150000000, P_GCC_GPLL0_OUT_EVEN, 2, 0, 0), + F(300000000, P_GCC_GPLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_ice_core_clk_src = { + .cmd_rcgr = 0xa9040, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_sdcc1_ice_core_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ice_core_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = { + F(400000, P_BI_TCXO, 12, 1, 4), + F(25000000, P_GCC_GPLL0_OUT_EVEN, 12, 0, 0), + F(37500000, P_GCC_GPLL0_OUT_EVEN, 8, 0, 0), + F(50000000, P_GCC_GPLL0_OUT_EVEN, 6, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(202000000, P_GCC_GPLL9_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc2_apps_clk_src = { + .cmd_rcgr = 0x1401c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_7, + .freq_tbl = ftbl_gcc_sdcc2_apps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_apps_clk_src", + .parent_data = gcc_parent_data_7, + .num_parents = ARRAY_SIZE(gcc_parent_data_7), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_axi_clk_src[] = { + F(25000000, P_GCC_GPLL0_OUT_EVEN, 12, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(201500000, P_GCC_GPLL4_OUT_MAIN, 4, 0, 0), + F(403000000, P_GCC_GPLL4_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_axi_clk_src = { + .cmd_rcgr = 0x77034, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_ufs_phy_axi_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_axi_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_ice_core_clk_src[] = { + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(201500000, P_GCC_GPLL4_OUT_MAIN, 4, 0, 0), + F(403000000, P_GCC_GPLL4_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_ice_core_clk_src = { + .cmd_rcgr = 0x7708c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_ufs_phy_ice_core_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_ice_core_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_phy_aux_clk_src[] = { + F(9600000, P_BI_TCXO, 2, 0, 0), + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_phy_aux_clk_src = { + .cmd_rcgr = 0x770c0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_6, + .freq_tbl = ftbl_gcc_ufs_phy_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_phy_aux_clk_src", + .parent_data = gcc_parent_data_6, + .num_parents = ARRAY_SIZE(gcc_parent_data_6), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_ufs_phy_unipro_core_clk_src = { + .cmd_rcgr = 0x770a4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_ufs_phy_ice_core_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_unipro_core_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb30_prim_master_clk_src[] = { + F(66666667, P_GCC_GPLL0_OUT_EVEN, 4.5, 0, 0), + F(133333333, P_GCC_GPLL0_OUT_MAIN, 4.5, 0, 0), + F(200000000, P_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + F(240000000, P_GCC_GPLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_usb30_prim_master_clk_src = { + .cmd_rcgr = 0x39030, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_usb30_prim_master_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_master_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_usb30_prim_mock_utmi_clk_src = { + .cmd_rcgr = 0x39048, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_mock_utmi_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_usb3_prim_phy_aux_clk_src = { + .cmd_rcgr = 0x39074, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_aux_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_regmap_div gcc_pcie_0_pipe_div2_clk_src = { + .reg = 0x6b0a4, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_div2_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_pcie_1_pipe_div2_clk_src = { + .reg = 0xac0a0, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_div2_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_qupv3_wrap1_s2_clk_src = { + .reg = 0x1828c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s2_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_usb30_prim_mock_utmi_postdiv_clk_src = { + .reg = 0x39060, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_mock_utmi_postdiv_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch gcc_aggre_noc_pcie_axi_clk = { + .halt_reg = 0x10068, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x10068, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(30), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_aggre_noc_pcie_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_aggre_ufs_phy_axi_clk = { + .halt_reg = 0x770f4, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x770f4, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x770f4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_aggre_ufs_phy_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_aggre_usb3_prim_axi_clk = { + .halt_reg = 0x39094, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x39094, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_aggre_usb3_prim_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_boot_rom_ahb_clk = { + .halt_reg = 0x38004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x38004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_boot_rom_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camera_hf_axi_clk = { + .halt_reg = 0x26014, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x26014, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x26014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camera_hf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camera_sf_axi_clk = { + .halt_reg = 0x26024, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x26024, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x26024, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camera_sf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cfg_noc_pcie_anoc_ahb_clk = { + .halt_reg = 0x10050, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x10050, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(20), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cfg_noc_pcie_anoc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cfg_noc_usb3_prim_axi_clk = { + .halt_reg = 0x39090, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x39090, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cfg_noc_usb3_prim_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cnoc_pcie_sf_axi_clk = { + .halt_reg = 0x10058, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x10058, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cnoc_pcie_sf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ddrss_gpu_axi_clk = { + .halt_reg = 0x71158, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x71158, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x71158, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ddrss_gpu_axi_clk", + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch gcc_ddrss_pcie_sf_qtb_clk = { + .halt_reg = 0x1007c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x1007c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(19), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ddrss_pcie_sf_qtb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_disp_hf_axi_clk = { + .halt_reg = 0x27008, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x27008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_disp_hf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp1_clk = { + .halt_reg = 0x64000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x64000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp2_clk = { + .halt_reg = 0x65000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x65000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp3_clk = { + .halt_reg = 0x66000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x66000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gemnoc_gfx_clk = { + .halt_reg = 0x71010, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x71010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x71010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gemnoc_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gpll0_cph_clk_src = { + .halt_reg = 0x71150, + .halt_check = BRANCH_HALT_ENABLE_VOTED, + .clkr = { + .enable_reg = 0x71150, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gpll0_cph_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gpll0_div_cph_clk_src = { + .halt_reg = 0x71154, + .halt_check = BRANCH_HALT_ENABLE_VOTED, + .clkr = { + .enable_reg = 0x71154, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gpll0_div_cph_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gpll0_out_even.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_smmu_vote_clk = { + .halt_reg = 0x7d000, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_smmu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mmu_tcu_vote_clk = { + .halt_reg = 0x7d02c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d02c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_mmu_tcu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_aux_clk = { + .halt_reg = 0x6b044, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(3), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_cfg_ahb_clk = { + .halt_reg = 0x6b040, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b040, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_mstr_axi_clk = { + .halt_reg = 0x6b030, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x6b030, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_phy_rchng_clk = { + .halt_reg = 0x6b064, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(22), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_phy_rchng_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_phy_rchng_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_pipe_clk = { + .halt_reg = 0x6b054, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_pipe_div2_clk = { + .halt_reg = 0x6b0a8, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(13), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_div2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_pipe_div2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_slv_axi_clk = { + .halt_reg = 0x6b020, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b020, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_slv_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_slv_q2a_axi_clk = { + .halt_reg = 0x6b01c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_slv_q2a_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_aux_clk = { + .halt_reg = 0xac040, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(29), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_cfg_ahb_clk = { + .halt_reg = 0xac03c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xac03c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(28), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_mstr_axi_clk = { + .halt_reg = 0xac02c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0xac02c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(27), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_phy_rchng_clk = { + .halt_reg = 0xac060, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_phy_rchng_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_phy_rchng_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_pipe_clk = { + .halt_reg = 0xac050, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(23), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_pipe_div2_clk = { + .halt_reg = 0xac0a4, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_div2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_pipe_div2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_slv_axi_clk = { + .halt_reg = 0xac01c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xac01c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_slv_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_slv_q2a_axi_clk = { + .halt_reg = 0xac018, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_slv_q2a_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm2_clk = { + .halt_reg = 0x3300c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3300c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pdm2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_ahb_clk = { + .halt_reg = 0x33004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x33004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x33004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_xo4_clk = { + .halt_reg = 0x33008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x33008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm_xo4_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_camera_cmd_ahb_clk = { + .halt_reg = 0x26010, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x26010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x26010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_camera_cmd_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_camera_nrt_ahb_clk = { + .halt_reg = 0x26008, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x26008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x26008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_camera_nrt_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_camera_rt_ahb_clk = { + .halt_reg = 0x2600c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2600c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2600c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_camera_rt_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_gpu_ahb_clk = { + .halt_reg = 0x71008, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x71008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x71008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_gpu_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_pcie_ahb_clk = { + .halt_reg = 0x6b018, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(11), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_pcie_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_video_v_cpu_ahb_clk = { + .halt_reg = 0x32010, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x32010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x32010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_video_v_cpu_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qmip_video_vcodec_ahb_clk = { + .halt_reg = 0x3200c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x3200c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x3200c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qmip_video_vcodec_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_core_2x_clk = { + .halt_reg = 0x2301c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(18), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_core_clk = { + .halt_reg = 0x23008, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(19), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_qspi_ref_clk = { + .halt_reg = 0x188bc, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(29), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_qspi_ref_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s0_clk = { + .halt_reg = 0x18004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(22), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s1_clk = { + .halt_reg = 0x18140, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(23), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s2_clk = { + .halt_reg = 0x1827c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s3_clk = { + .halt_reg = 0x18290, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s4_clk = { + .halt_reg = 0x183cc, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s5_clk = { + .halt_reg = 0x18508, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(27), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s5_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s6_clk = { + .halt_reg = 0x18644, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(28), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s6_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s7_clk = { + .halt_reg = 0x18780, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s7_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s7_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_core_2x_clk = { + .halt_reg = 0x23174, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(3), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_core_clk = { + .halt_reg = 0x23160, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s0_clk = { + .halt_reg = 0x1e004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s1_clk = { + .halt_reg = 0x1e140, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s2_clk = { + .halt_reg = 0x1e27c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s3_clk = { + .halt_reg = 0x1e3b8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s4_clk = { + .halt_reg = 0x1e4f4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s5_clk = { + .halt_reg = 0x1e630, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(9), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s5_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s6_clk = { + .halt_reg = 0x1e76c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s6_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s7_clk = { + .halt_reg = 0x1e8a8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(17), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s7_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s7_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_1_m_ahb_clk = { + .halt_reg = 0x23000, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(20), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_1_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_1_s_ahb_clk = { + .halt_reg = 0x23004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(21), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_1_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_2_m_ahb_clk = { + .halt_reg = 0x23158, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23158, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_2_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_2_s_ahb_clk = { + .halt_reg = 0x2315c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2315c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_2_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ahb_clk = { + .halt_reg = 0xa9004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa9004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_apps_clk = { + .halt_reg = 0xa9008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xa9008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_apps_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc1_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ice_core_clk = { + .halt_reg = 0xa9030, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0xa9030, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xa9030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ice_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc1_ice_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_ahb_clk = { + .halt_reg = 0x14014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x14014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_apps_clk = { + .halt_reg = 0x14004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x14004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_apps_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc2_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_ahb_clk = { + .halt_reg = 0x77028, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x77028, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_axi_clk = { + .halt_reg = 0x77018, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x77018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_ice_core_clk = { + .halt_reg = 0x7707c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x7707c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7707c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_ice_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_ice_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_phy_aux_clk = { + .halt_reg = 0x770bc, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x770bc, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x770bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_rx_symbol_0_clk = { + .halt_reg = 0x77030, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x77030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_rx_symbol_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_rx_symbol_1_clk = { + .halt_reg = 0x770d8, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x770d8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_rx_symbol_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_tx_symbol_0_clk = { + .halt_reg = 0x7702c, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x7702c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_tx_symbol_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_tx_symbol_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_unipro_core_clk = { + .halt_reg = 0x7706c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x7706c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7706c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_unipro_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_unipro_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_atb_clk = { + .halt_reg = 0x3908c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x3908c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_atb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_master_clk = { + .halt_reg = 0x39018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_mock_utmi_clk = { + .halt_reg = 0x3902c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3902c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_mock_utmi_postdiv_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_sleep_clk = { + .halt_reg = 0x39028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_aux_clk = { + .halt_reg = 0x39064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb3_prim_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_com_aux_clk = { + .halt_reg = 0x39068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_com_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb3_prim_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_pipe_clk = { + .halt_reg = 0x3906c, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0x3906c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x3906c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb3_prim_phy_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_axi0_clk = { + .halt_reg = 0x32018, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x32018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x32018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_video_axi0_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_axi1_clk = { + .halt_reg = 0x32028, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x32028, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x32028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_video_axi1_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc gcc_pcie_0_gdsc = { + .gdscr = 0x6b004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .collapse_ctrl = 0x5214c, + .collapse_mask = BIT(0), + .pd = { + .name = "gcc_pcie_0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_0_phy_gdsc = { + .gdscr = 0x6c000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .collapse_ctrl = 0x5214c, + .collapse_mask = BIT(2), + .pd = { + .name = "gcc_pcie_0_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_1_gdsc = { + .gdscr = 0xac004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .collapse_ctrl = 0x5214c, + .collapse_mask = BIT(3), + .pd = { + .name = "gcc_pcie_1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_1_phy_gdsc = { + .gdscr = 0xad000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .collapse_ctrl = 0x5214c, + .collapse_mask = BIT(4), + .pd = { + .name = "gcc_pcie_1_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_ufs_mem_phy_gdsc = { + .gdscr = 0x9e000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "gcc_ufs_mem_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_ufs_phy_gdsc = { + .gdscr = 0x77004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "gcc_ufs_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_usb30_prim_gdsc = { + .gdscr = 0x39004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "gcc_usb30_prim_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_usb3_phy_gdsc = { + .gdscr = 0x50018, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "gcc_usb3_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *gcc_eliza_clocks[] = { + [GCC_AGGRE_NOC_PCIE_AXI_CLK] = &gcc_aggre_noc_pcie_axi_clk.clkr, + [GCC_AGGRE_UFS_PHY_AXI_CLK] = &gcc_aggre_ufs_phy_axi_clk.clkr, + [GCC_AGGRE_USB3_PRIM_AXI_CLK] = &gcc_aggre_usb3_prim_axi_clk.clkr, + [GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr, + [GCC_CAMERA_HF_AXI_CLK] = &gcc_camera_hf_axi_clk.clkr, + [GCC_CAMERA_SF_AXI_CLK] = &gcc_camera_sf_axi_clk.clkr, + [GCC_CFG_NOC_PCIE_ANOC_AHB_CLK] = &gcc_cfg_noc_pcie_anoc_ahb_clk.clkr, + [GCC_CFG_NOC_USB3_PRIM_AXI_CLK] = &gcc_cfg_noc_usb3_prim_axi_clk.clkr, + [GCC_CNOC_PCIE_SF_AXI_CLK] = &gcc_cnoc_pcie_sf_axi_clk.clkr, + [GCC_DDRSS_GPU_AXI_CLK] = &gcc_ddrss_gpu_axi_clk.clkr, + [GCC_DDRSS_PCIE_SF_QTB_CLK] = &gcc_ddrss_pcie_sf_qtb_clk.clkr, + [GCC_DISP_HF_AXI_CLK] = &gcc_disp_hf_axi_clk.clkr, + [GCC_GP1_CLK] = &gcc_gp1_clk.clkr, + [GCC_GP1_CLK_SRC] = &gcc_gp1_clk_src.clkr, + [GCC_GP2_CLK] = &gcc_gp2_clk.clkr, + [GCC_GP2_CLK_SRC] = &gcc_gp2_clk_src.clkr, + [GCC_GP3_CLK] = &gcc_gp3_clk.clkr, + [GCC_GP3_CLK_SRC] = &gcc_gp3_clk_src.clkr, + [GCC_GPLL0] = &gcc_gpll0.clkr, + [GCC_GPLL0_OUT_EVEN] = &gcc_gpll0_out_even.clkr, + [GCC_GPLL4] = &gcc_gpll4.clkr, + [GCC_GPLL7] = &gcc_gpll7.clkr, + [GCC_GPLL8] = &gcc_gpll8.clkr, + [GCC_GPLL9] = &gcc_gpll9.clkr, + [GCC_GPU_GEMNOC_GFX_CLK] = &gcc_gpu_gemnoc_gfx_clk.clkr, + [GCC_GPU_GPLL0_CPH_CLK_SRC] = &gcc_gpu_gpll0_cph_clk_src.clkr, + [GCC_GPU_GPLL0_DIV_CPH_CLK_SRC] = &gcc_gpu_gpll0_div_cph_clk_src.clkr, + [GCC_GPU_SMMU_VOTE_CLK] = &gcc_gpu_smmu_vote_clk.clkr, + [GCC_MMU_TCU_VOTE_CLK] = &gcc_mmu_tcu_vote_clk.clkr, + [GCC_PCIE_0_AUX_CLK] = &gcc_pcie_0_aux_clk.clkr, + [GCC_PCIE_0_AUX_CLK_SRC] = &gcc_pcie_0_aux_clk_src.clkr, + [GCC_PCIE_0_CFG_AHB_CLK] = &gcc_pcie_0_cfg_ahb_clk.clkr, + [GCC_PCIE_0_MSTR_AXI_CLK] = &gcc_pcie_0_mstr_axi_clk.clkr, + [GCC_PCIE_0_PHY_RCHNG_CLK] = &gcc_pcie_0_phy_rchng_clk.clkr, + [GCC_PCIE_0_PHY_RCHNG_CLK_SRC] = &gcc_pcie_0_phy_rchng_clk_src.clkr, + [GCC_PCIE_0_PIPE_CLK] = &gcc_pcie_0_pipe_clk.clkr, + [GCC_PCIE_0_PIPE_CLK_SRC] = &gcc_pcie_0_pipe_clk_src.clkr, + [GCC_PCIE_0_PIPE_DIV2_CLK] = &gcc_pcie_0_pipe_div2_clk.clkr, + [GCC_PCIE_0_PIPE_DIV2_CLK_SRC] = &gcc_pcie_0_pipe_div2_clk_src.clkr, + [GCC_PCIE_0_SLV_AXI_CLK] = &gcc_pcie_0_slv_axi_clk.clkr, + [GCC_PCIE_0_SLV_Q2A_AXI_CLK] = &gcc_pcie_0_slv_q2a_axi_clk.clkr, + [GCC_PCIE_1_AUX_CLK] = &gcc_pcie_1_aux_clk.clkr, + [GCC_PCIE_1_AUX_CLK_SRC] = &gcc_pcie_1_aux_clk_src.clkr, + [GCC_PCIE_1_CFG_AHB_CLK] = &gcc_pcie_1_cfg_ahb_clk.clkr, + [GCC_PCIE_1_MSTR_AXI_CLK] = &gcc_pcie_1_mstr_axi_clk.clkr, + [GCC_PCIE_1_PHY_RCHNG_CLK] = &gcc_pcie_1_phy_rchng_clk.clkr, + [GCC_PCIE_1_PHY_RCHNG_CLK_SRC] = &gcc_pcie_1_phy_rchng_clk_src.clkr, + [GCC_PCIE_1_PIPE_CLK] = &gcc_pcie_1_pipe_clk.clkr, + [GCC_PCIE_1_PIPE_CLK_SRC] = &gcc_pcie_1_pipe_clk_src.clkr, + [GCC_PCIE_1_PIPE_DIV2_CLK] = &gcc_pcie_1_pipe_div2_clk.clkr, + [GCC_PCIE_1_PIPE_DIV2_CLK_SRC] = &gcc_pcie_1_pipe_div2_clk_src.clkr, + [GCC_PCIE_1_SLV_AXI_CLK] = &gcc_pcie_1_slv_axi_clk.clkr, + [GCC_PCIE_1_SLV_Q2A_AXI_CLK] = &gcc_pcie_1_slv_q2a_axi_clk.clkr, + [GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr, + [GCC_PDM2_CLK_SRC] = &gcc_pdm2_clk_src.clkr, + [GCC_PDM_AHB_CLK] = &gcc_pdm_ahb_clk.clkr, + [GCC_PDM_XO4_CLK] = &gcc_pdm_xo4_clk.clkr, + [GCC_QMIP_CAMERA_CMD_AHB_CLK] = &gcc_qmip_camera_cmd_ahb_clk.clkr, + [GCC_QMIP_CAMERA_NRT_AHB_CLK] = &gcc_qmip_camera_nrt_ahb_clk.clkr, + [GCC_QMIP_CAMERA_RT_AHB_CLK] = &gcc_qmip_camera_rt_ahb_clk.clkr, + [GCC_QMIP_GPU_AHB_CLK] = &gcc_qmip_gpu_ahb_clk.clkr, + [GCC_QMIP_PCIE_AHB_CLK] = &gcc_qmip_pcie_ahb_clk.clkr, + [GCC_QMIP_VIDEO_V_CPU_AHB_CLK] = &gcc_qmip_video_v_cpu_ahb_clk.clkr, + [GCC_QMIP_VIDEO_VCODEC_AHB_CLK] = &gcc_qmip_video_vcodec_ahb_clk.clkr, + [GCC_QUPV3_WRAP1_CORE_2X_CLK] = &gcc_qupv3_wrap1_core_2x_clk.clkr, + [GCC_QUPV3_WRAP1_CORE_CLK] = &gcc_qupv3_wrap1_core_clk.clkr, + [GCC_QUPV3_WRAP1_QSPI_REF_CLK] = &gcc_qupv3_wrap1_qspi_ref_clk.clkr, + [GCC_QUPV3_WRAP1_QSPI_REF_CLK_SRC] = &gcc_qupv3_wrap1_qspi_ref_clk_src.clkr, + [GCC_QUPV3_WRAP1_S0_CLK] = &gcc_qupv3_wrap1_s0_clk.clkr, + [GCC_QUPV3_WRAP1_S0_CLK_SRC] = &gcc_qupv3_wrap1_s0_clk_src.clkr, + [GCC_QUPV3_WRAP1_S1_CLK] = &gcc_qupv3_wrap1_s1_clk.clkr, + [GCC_QUPV3_WRAP1_S1_CLK_SRC] = &gcc_qupv3_wrap1_s1_clk_src.clkr, + [GCC_QUPV3_WRAP1_S2_CLK] = &gcc_qupv3_wrap1_s2_clk.clkr, + [GCC_QUPV3_WRAP1_S2_CLK_SRC] = &gcc_qupv3_wrap1_s2_clk_src.clkr, + [GCC_QUPV3_WRAP1_S3_CLK] = &gcc_qupv3_wrap1_s3_clk.clkr, + [GCC_QUPV3_WRAP1_S3_CLK_SRC] = &gcc_qupv3_wrap1_s3_clk_src.clkr, + [GCC_QUPV3_WRAP1_S4_CLK] = &gcc_qupv3_wrap1_s4_clk.clkr, + [GCC_QUPV3_WRAP1_S4_CLK_SRC] = &gcc_qupv3_wrap1_s4_clk_src.clkr, + [GCC_QUPV3_WRAP1_S5_CLK] = &gcc_qupv3_wrap1_s5_clk.clkr, + [GCC_QUPV3_WRAP1_S5_CLK_SRC] = &gcc_qupv3_wrap1_s5_clk_src.clkr, + [GCC_QUPV3_WRAP1_S6_CLK] = &gcc_qupv3_wrap1_s6_clk.clkr, + [GCC_QUPV3_WRAP1_S6_CLK_SRC] = &gcc_qupv3_wrap1_s6_clk_src.clkr, + [GCC_QUPV3_WRAP1_S7_CLK] = &gcc_qupv3_wrap1_s7_clk.clkr, + [GCC_QUPV3_WRAP1_S7_CLK_SRC] = &gcc_qupv3_wrap1_s7_clk_src.clkr, + [GCC_QUPV3_WRAP2_CORE_2X_CLK] = &gcc_qupv3_wrap2_core_2x_clk.clkr, + [GCC_QUPV3_WRAP2_CORE_CLK] = &gcc_qupv3_wrap2_core_clk.clkr, + [GCC_QUPV3_WRAP2_S0_CLK] = &gcc_qupv3_wrap2_s0_clk.clkr, + [GCC_QUPV3_WRAP2_S0_CLK_SRC] = &gcc_qupv3_wrap2_s0_clk_src.clkr, + [GCC_QUPV3_WRAP2_S1_CLK] = &gcc_qupv3_wrap2_s1_clk.clkr, + [GCC_QUPV3_WRAP2_S1_CLK_SRC] = &gcc_qupv3_wrap2_s1_clk_src.clkr, + [GCC_QUPV3_WRAP2_S2_CLK] = &gcc_qupv3_wrap2_s2_clk.clkr, + [GCC_QUPV3_WRAP2_S2_CLK_SRC] = &gcc_qupv3_wrap2_s2_clk_src.clkr, + [GCC_QUPV3_WRAP2_S3_CLK] = &gcc_qupv3_wrap2_s3_clk.clkr, + [GCC_QUPV3_WRAP2_S3_CLK_SRC] = &gcc_qupv3_wrap2_s3_clk_src.clkr, + [GCC_QUPV3_WRAP2_S4_CLK] = &gcc_qupv3_wrap2_s4_clk.clkr, + [GCC_QUPV3_WRAP2_S4_CLK_SRC] = &gcc_qupv3_wrap2_s4_clk_src.clkr, + [GCC_QUPV3_WRAP2_S5_CLK] = &gcc_qupv3_wrap2_s5_clk.clkr, + [GCC_QUPV3_WRAP2_S5_CLK_SRC] = &gcc_qupv3_wrap2_s5_clk_src.clkr, + [GCC_QUPV3_WRAP2_S6_CLK] = &gcc_qupv3_wrap2_s6_clk.clkr, + [GCC_QUPV3_WRAP2_S6_CLK_SRC] = &gcc_qupv3_wrap2_s6_clk_src.clkr, + [GCC_QUPV3_WRAP2_S7_CLK] = &gcc_qupv3_wrap2_s7_clk.clkr, + [GCC_QUPV3_WRAP2_S7_CLK_SRC] = &gcc_qupv3_wrap2_s7_clk_src.clkr, + [GCC_QUPV3_WRAP_1_M_AHB_CLK] = &gcc_qupv3_wrap_1_m_ahb_clk.clkr, + [GCC_QUPV3_WRAP_1_S_AHB_CLK] = &gcc_qupv3_wrap_1_s_ahb_clk.clkr, + [GCC_QUPV3_WRAP_2_M_AHB_CLK] = &gcc_qupv3_wrap_2_m_ahb_clk.clkr, + [GCC_QUPV3_WRAP_2_S_AHB_CLK] = &gcc_qupv3_wrap_2_s_ahb_clk.clkr, + [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr, + [GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr, + [GCC_SDCC1_APPS_CLK_SRC] = &gcc_sdcc1_apps_clk_src.clkr, + [GCC_SDCC1_ICE_CORE_CLK] = &gcc_sdcc1_ice_core_clk.clkr, + [GCC_SDCC1_ICE_CORE_CLK_SRC] = &gcc_sdcc1_ice_core_clk_src.clkr, + [GCC_SDCC2_AHB_CLK] = &gcc_sdcc2_ahb_clk.clkr, + [GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr, + [GCC_SDCC2_APPS_CLK_SRC] = &gcc_sdcc2_apps_clk_src.clkr, + [GCC_UFS_PHY_AHB_CLK] = &gcc_ufs_phy_ahb_clk.clkr, + [GCC_UFS_PHY_AXI_CLK] = &gcc_ufs_phy_axi_clk.clkr, + [GCC_UFS_PHY_AXI_CLK_SRC] = &gcc_ufs_phy_axi_clk_src.clkr, + [GCC_UFS_PHY_ICE_CORE_CLK] = &gcc_ufs_phy_ice_core_clk.clkr, + [GCC_UFS_PHY_ICE_CORE_CLK_SRC] = &gcc_ufs_phy_ice_core_clk_src.clkr, + [GCC_UFS_PHY_PHY_AUX_CLK] = &gcc_ufs_phy_phy_aux_clk.clkr, + [GCC_UFS_PHY_PHY_AUX_CLK_SRC] = &gcc_ufs_phy_phy_aux_clk_src.clkr, + [GCC_UFS_PHY_RX_SYMBOL_0_CLK] = &gcc_ufs_phy_rx_symbol_0_clk.clkr, + [GCC_UFS_PHY_RX_SYMBOL_0_CLK_SRC] = &gcc_ufs_phy_rx_symbol_0_clk_src.clkr, + [GCC_UFS_PHY_RX_SYMBOL_1_CLK] = &gcc_ufs_phy_rx_symbol_1_clk.clkr, + [GCC_UFS_PHY_RX_SYMBOL_1_CLK_SRC] = &gcc_ufs_phy_rx_symbol_1_clk_src.clkr, + [GCC_UFS_PHY_TX_SYMBOL_0_CLK] = &gcc_ufs_phy_tx_symbol_0_clk.clkr, + [GCC_UFS_PHY_TX_SYMBOL_0_CLK_SRC] = &gcc_ufs_phy_tx_symbol_0_clk_src.clkr, + [GCC_UFS_PHY_UNIPRO_CORE_CLK] = &gcc_ufs_phy_unipro_core_clk.clkr, + [GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC] = &gcc_ufs_phy_unipro_core_clk_src.clkr, + [GCC_USB30_PRIM_ATB_CLK] = &gcc_usb30_prim_atb_clk.clkr, + [GCC_USB30_PRIM_MASTER_CLK] = &gcc_usb30_prim_master_clk.clkr, + [GCC_USB30_PRIM_MASTER_CLK_SRC] = &gcc_usb30_prim_master_clk_src.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_CLK] = &gcc_usb30_prim_mock_utmi_clk.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC] = &gcc_usb30_prim_mock_utmi_clk_src.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_POSTDIV_CLK_SRC] = &gcc_usb30_prim_mock_utmi_postdiv_clk_src.clkr, + [GCC_USB30_PRIM_SLEEP_CLK] = &gcc_usb30_prim_sleep_clk.clkr, + [GCC_USB3_PRIM_PHY_AUX_CLK] = &gcc_usb3_prim_phy_aux_clk.clkr, + [GCC_USB3_PRIM_PHY_AUX_CLK_SRC] = &gcc_usb3_prim_phy_aux_clk_src.clkr, + [GCC_USB3_PRIM_PHY_COM_AUX_CLK] = &gcc_usb3_prim_phy_com_aux_clk.clkr, + [GCC_USB3_PRIM_PHY_PIPE_CLK] = &gcc_usb3_prim_phy_pipe_clk.clkr, + [GCC_USB3_PRIM_PHY_PIPE_CLK_SRC] = &gcc_usb3_prim_phy_pipe_clk_src.clkr, + [GCC_VIDEO_AXI0_CLK] = &gcc_video_axi0_clk.clkr, + [GCC_VIDEO_AXI1_CLK] = &gcc_video_axi1_clk.clkr, +}; + +static struct gdsc *gcc_eliza_gdscs[] = { + [GCC_PCIE_0_GDSC] = &gcc_pcie_0_gdsc, + [GCC_PCIE_0_PHY_GDSC] = &gcc_pcie_0_phy_gdsc, + [GCC_PCIE_1_GDSC] = &gcc_pcie_1_gdsc, + [GCC_PCIE_1_PHY_GDSC] = &gcc_pcie_1_phy_gdsc, + [GCC_UFS_MEM_PHY_GDSC] = &gcc_ufs_mem_phy_gdsc, + [GCC_UFS_PHY_GDSC] = &gcc_ufs_phy_gdsc, + [GCC_USB30_PRIM_GDSC] = &gcc_usb30_prim_gdsc, + [GCC_USB3_PHY_GDSC] = &gcc_usb3_phy_gdsc, +}; + +static const struct qcom_reset_map gcc_eliza_resets[] = { + [GCC_CAMERA_BCR] = { 0x26000 }, + [GCC_DISPLAY_BCR] = { 0x27000 }, + [GCC_GPU_BCR] = { 0x71000 }, + [GCC_PCIE_0_BCR] = { 0x6b000 }, + [GCC_PCIE_0_LINK_DOWN_BCR] = { 0x6c014 }, + [GCC_PCIE_0_NOCSR_COM_PHY_BCR] = { 0x6c020 }, + [GCC_PCIE_0_PHY_BCR] = { 0x6c01c }, + [GCC_PCIE_0_PHY_NOCSR_COM_PHY_BCR] = { 0x6c028 }, + [GCC_PCIE_1_BCR] = { 0xac000 }, + [GCC_PCIE_1_LINK_DOWN_BCR] = { 0x8e014 }, + [GCC_PCIE_1_NOCSR_COM_PHY_BCR] = { 0x8e020 }, + [GCC_PCIE_1_PHY_BCR] = { 0x8e01c }, + [GCC_PCIE_1_PHY_NOCSR_COM_PHY_BCR] = { 0x8e024 }, + [GCC_PCIE_PHY_BCR] = { 0x6f000 }, + [GCC_PCIE_PHY_CFG_AHB_BCR] = { 0x6f00c }, + [GCC_PCIE_PHY_COM_BCR] = { 0x6f010 }, + [GCC_PCIE_RSCC_BCR] = { 0x11000 }, + [GCC_PDM_BCR] = { 0x33000 }, + [GCC_QUPV3_WRAPPER_1_BCR] = { 0x18000 }, + [GCC_QUPV3_WRAPPER_2_BCR] = { 0x1e000 }, + [GCC_QUSB2PHY_PRIM_BCR] = { 0x12000 }, + [GCC_QUSB2PHY_SEC_BCR] = { 0x12004 }, + [GCC_SDCC1_BCR] = { 0xa9000 }, + [GCC_SDCC2_BCR] = { 0x14000 }, + [GCC_UFS_PHY_BCR] = { 0x77000 }, + [GCC_USB30_PRIM_BCR] = { 0x39000 }, + [GCC_USB3_DP_PHY_PRIM_BCR] = { 0x50008 }, + [GCC_USB3_DP_PHY_SEC_BCR] = { 0x50014 }, + [GCC_USB3_PHY_PRIM_BCR] = { 0x50000 }, + [GCC_USB3_PHY_SEC_BCR] = { 0x5000c }, + [GCC_USB3PHY_PHY_PRIM_BCR] = { 0x50004 }, + [GCC_USB3PHY_PHY_SEC_BCR] = { 0x50010 }, + [GCC_VIDEO_AXI0_CLK_ARES] = { 0x32018, 2 }, + [GCC_VIDEO_AXI1_CLK_ARES] = { 0x32028, 2 }, + [GCC_VIDEO_BCR] = { 0x32000 }, +}; + +static const u32 gcc_eliza_critical_cbcrs[] = { + 0xa0004, /* GCC_CAM_BIST_MCLK_AHB_CLK */ + 0x26004, /* GCC_CAMERA_AHB_CLK */ + 0x26034, /* GCC_CAMERA_XO_CLK */ + 0x27004, /* GCC_DISP_AHB_CLK */ + 0x71004, /* GCC_GPU_CFG_AHB_CLK */ + 0x52010, /* GCC_PCIE_RSCC_CFG_AHB_CLK */ + 0x52010, /* GCC_PCIE_RSCC_XO_CLK */ + 0x32004, /* GCC_VIDEO_AHB_CLK */ + 0x32038, /* GCC_VIDEO_XO_CLK */ +}; + +static const struct clk_rcg_dfs_data gcc_eliza_dfs_clocks[] = { + DEFINE_RCG_DFS(gcc_qupv3_wrap1_qspi_ref_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s6_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s7_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s6_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s7_clk_src), +}; + +static const struct regmap_config gcc_eliza_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1f41f0, + .fast_io = true, +}; + +static void clk_eliza_regs_configure(struct device *dev, struct regmap *regmap) +{ + /* FORCE_MEM_CORE_ON for ufs phy ice core and gcc ufs phy axi clocks */ + qcom_branch_set_force_mem_core(regmap, gcc_ufs_phy_ice_core_clk, true); + qcom_branch_set_force_mem_core(regmap, gcc_ufs_phy_axi_clk, true); +} + +static const struct qcom_cc_driver_data gcc_eliza_driver_data = { + .clk_cbcrs = gcc_eliza_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(gcc_eliza_critical_cbcrs), + .dfs_rcgs = gcc_eliza_dfs_clocks, + .num_dfs_rcgs = ARRAY_SIZE(gcc_eliza_dfs_clocks), + .clk_regs_configure = clk_eliza_regs_configure, +}; + +static const struct qcom_cc_desc gcc_eliza_desc = { + .config = &gcc_eliza_regmap_config, + .clks = gcc_eliza_clocks, + .num_clks = ARRAY_SIZE(gcc_eliza_clocks), + .resets = gcc_eliza_resets, + .num_resets = ARRAY_SIZE(gcc_eliza_resets), + .gdscs = gcc_eliza_gdscs, + .num_gdscs = ARRAY_SIZE(gcc_eliza_gdscs), + .driver_data = &gcc_eliza_driver_data, +}; + +static const struct of_device_id gcc_eliza_match_table[] = { + { .compatible = "qcom,eliza-gcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_eliza_match_table); + +static int gcc_eliza_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gcc_eliza_desc); +} + +static struct platform_driver gcc_eliza_driver = { + .probe = gcc_eliza_probe, + .driver = { + .name = "gcc-eliza", + .of_match_table = gcc_eliza_match_table, + }, +}; + +static int __init gcc_eliza_init(void) +{ + return platform_driver_register(&gcc_eliza_driver); +} +subsys_initcall(gcc_eliza_init); + +static void __exit gcc_eliza_exit(void) +{ + platform_driver_unregister(&gcc_eliza_driver); +} +module_exit(gcc_eliza_exit); + +MODULE_DESCRIPTION("QTI GCC Eliza Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/gcc-glymur.c b/drivers/clk/qcom/gcc-glymur.c index 238e205735ed..2736465efdea 100644 --- a/drivers/clk/qcom/gcc-glymur.c +++ b/drivers/clk/qcom/gcc-glymur.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -8507,6 +8506,7 @@ static const struct qcom_reset_map gcc_glymur_resets[] = { [GCC_VIDEO_AXI0_CLK_ARES] = { 0x3201c, 2 }, [GCC_VIDEO_AXI1_CLK_ARES] = { 0x32044, 2 }, [GCC_VIDEO_BCR] = { 0x32000 }, + [GCC_VIDEO_AXI0C_CLK_ARES] = { 0x32030, 2 }, }; static const struct clk_rcg_dfs_data gcc_dfs_clocks[] = { @@ -8538,7 +8538,7 @@ static const struct clk_rcg_dfs_data gcc_dfs_clocks[] = { DEFINE_RCG_DFS(gcc_qupv3_wrap2_s7_clk_src), }; -static u32 gcc_glymur_critical_cbcrs[] = { +static const u32 gcc_glymur_critical_cbcrs[] = { 0x26004, /* GCC_CAMERA_AHB_CLK */ 0x26040, /* GCC_CAMERA_XO_CLK */ 0x27004, /* GCC_DISP_AHB_CLK */ @@ -8561,7 +8561,7 @@ static void clk_glymur_regs_configure(struct device *dev, struct regmap *regmap) qcom_branch_set_force_mem_core(regmap, gcc_ufs_phy_ice_core_clk, true); } -static struct qcom_cc_driver_data gcc_glymur_driver_data = { +static const struct qcom_cc_driver_data gcc_glymur_driver_data = { .clk_cbcrs = gcc_glymur_critical_cbcrs, .num_clk_cbcrs = ARRAY_SIZE(gcc_glymur_critical_cbcrs), .dfs_rcgs = gcc_dfs_clocks, @@ -8611,5 +8611,5 @@ static void __exit gcc_glymur_exit(void) } module_exit(gcc_glymur_exit); -MODULE_DESCRIPTION("QTI GCC GLYMUR Driver"); +MODULE_DESCRIPTION("QTI GCC Glymur Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/gcc-ipq5210.c b/drivers/clk/qcom/gcc-ipq5210.c new file mode 100644 index 000000000000..3a786a21bdff --- /dev/null +++ b/drivers/clk/qcom/gcc-ipq5210.c @@ -0,0 +1,2661 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "clk-regmap-phy-mux.h" +#include "reset.h" + +enum { + DT_XO, + DT_SLEEP_CLK, + DT_PCIE30_PHY0_PIPE_CLK, + DT_PCIE30_PHY1_PIPE_CLK, + DT_USB3_PHY0_CC_PIPE_CLK, + DT_NSS_CMN_CLK, +}; + +enum { + P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, + P_GPLL0_OUT_AUX, + P_GPLL0_OUT_MAIN, + P_GPLL2_OUT_AUX, + P_GPLL2_OUT_MAIN, + P_GPLL4_OUT_AUX, + P_GPLL4_OUT_MAIN, + P_NSS_CMN_CLK, + P_SLEEP_CLK, + P_USB3PHY_0_PIPE, + P_XO, +}; + +static const struct clk_parent_data gcc_parent_data_xo = { .index = DT_XO }; + +static struct clk_alpha_pll gpll0_main = { + .offset = 0x20000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr = { + .enable_reg = 0xb000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpll0_main", + .parent_data = &gcc_parent_data_xo, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +static struct clk_fixed_factor gpll0_div2 = { + .mult = 1, + .div = 2, + .hw.init = &(const struct clk_init_data) { + .name = "gpll0_div2", + .parent_hws = (const struct clk_hw *[]) { + &gpll0_main.clkr.hw + }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + }, +}; + +static struct clk_alpha_pll_postdiv gpll0 = { + .offset = 0x20000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpll0", + .parent_hws = (const struct clk_hw *[]) { + &gpll0_main.clkr.hw + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + }, +}; + +static struct clk_alpha_pll gpll2_main = { + .offset = 0x21000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr = { + .enable_reg = 0xb000, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gpll2_main", + .parent_data = &gcc_parent_data_xo, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gpll2[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gpll2 = { + .offset = 0x21000, + .post_div_table = post_div_table_gpll2, + .num_post_div = ARRAY_SIZE(post_div_table_gpll2), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpll2", + .parent_hws = (const struct clk_hw*[]) { + &gpll2_main.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + }, +}; + +static struct clk_alpha_pll gpll4_main = { + .offset = 0x22000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT_EVO], + .clkr = { + .enable_reg = 0xb000, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gpll4_main", + .parent_data = &gcc_parent_data_xo, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, + /* + * There are no consumers for this GPLL in kernel yet, + * (will be added soon), so the clock framework + * disables this source. But some of the clocks + * initialized by boot loaders uses this source. So we + * need to keep this clock ON. Add the + * CLK_IGNORE_UNUSED flag so the clock will not be + * disabled. Once the consumer in kernel is added, we + * can get rid of this flag. + */ + .flags = CLK_IS_CRITICAL, + }, + }, +}; +static const struct parent_map gcc_parent_map_xo[] = { + { P_XO, 0 }, +}; + +static const struct parent_map gcc_parent_map_0[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_0[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_1[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, +}; + +static const struct clk_parent_data gcc_parent_data_1[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_2[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL4_OUT_MAIN, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_2[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll4_main.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_3[] = { + { P_XO, 0 }, +}; + +static const struct clk_parent_data gcc_parent_data_3[] = { + { .index = DT_XO }, +}; + +static const struct parent_map gcc_parent_map_4[] = { + { P_XO, 0 }, + { P_NSS_CMN_CLK, 1 }, + { P_GPLL0_OUT_AUX, 2 }, + { P_GPLL2_OUT_AUX, 3 }, +}; + +static const struct clk_parent_data gcc_parent_data_4[] = { + { .index = DT_XO }, + { .index = DT_NSS_CMN_CLK }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll2_main.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_5[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL0_OUT_AUX, 2 }, + { P_SLEEP_CLK, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_5[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_6[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL2_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_6[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll2.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_7[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL4_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_7[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll4_main.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_8[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_AUX, 2 }, + { P_SLEEP_CLK, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_8[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_9[] = { + { P_XO, 0 }, + { P_GPLL4_OUT_AUX, 1 }, + { P_GPLL0_OUT_MAIN, 3 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_9[] = { + { .index = DT_XO }, + { .hw = &gpll4_main.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_10[] = { + { P_XO, 0 }, + { P_GPLL4_OUT_MAIN, 1 }, + { P_GPLL0_OUT_AUX, 2 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_10[] = { + { .index = DT_XO }, + { .hw = &gpll4_main.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_11[] = { + { P_XO, 0 }, + { P_GPLL4_OUT_MAIN, 1 }, + { P_GPLL0_OUT_AUX, 2 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_11[] = { + { .index = DT_XO }, + { .hw = &gpll4_main.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_12[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL2_OUT_AUX, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_12[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll2_main.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_13[] = { + { P_SLEEP_CLK, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_13[] = { + { .index = DT_SLEEP_CLK }, +}; + +static const struct freq_tbl ftbl_gcc_adss_pwm_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_adss_pwm_clk_src = { + .cmd_rcgr = 0x1c004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_adss_pwm_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_nss_ts_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_nss_ts_clk_src = { + .cmd_rcgr = 0x17088, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_nss_ts_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_nss_ts_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_system_noc_bfdcd_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(133333333, P_GPLL0_OUT_MAIN, 6, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(266666667, P_GPLL4_OUT_MAIN, 4.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_system_noc_bfdcd_clk_src = { + .cmd_rcgr = 0x2e004, + .freq_tbl = ftbl_gcc_system_noc_bfdcd_clk_src, + .hid_width = 5, + .parent_map = gcc_parent_map_7, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_system_noc_bfdcd_clk_src", + .parent_data = gcc_parent_data_7, + .num_parents = ARRAY_SIZE(gcc_parent_data_7), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_nssnoc_memnoc_bfdcd_clk_src[] = { + F(429000000, P_NSS_CMN_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_nssnoc_memnoc_bfdcd_clk_src = { + .cmd_rcgr = 0x17004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_nssnoc_memnoc_bfdcd_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_memnoc_bfdcd_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie0_axi_m_clk_src[] = { + F(200000000, P_GPLL4_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie0_axi_m_clk_src = { + .cmd_rcgr = 0x28018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_m_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie0_axi_s_clk_src = { + .cmd_rcgr = 0x28020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_s_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie0_rchng_clk_src = { + .cmd_rcgr = 0x28028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_rchng_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie1_axi_m_clk_src[] = { + F(266666667, P_GPLL4_OUT_MAIN, 4.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie1_axi_m_clk_src = { + .cmd_rcgr = 0x29018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_pcie1_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_m_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie1_axi_s_clk_src = { + .cmd_rcgr = 0x29020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_s_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie1_rchng_clk_src = { + .cmd_rcgr = 0x29028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_rchng_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_aux_clk_src[] = { + F(20000000, P_GPLL0_OUT_MAIN, 10, 1, 4), + { } +}; + +static struct clk_rcg2 gcc_pcie_aux_clk_src = { + .cmd_rcgr = 0x28004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_pcie_aux_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_aux_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap_se0_clk_src[] = { + F(960000, P_XO, 10, 2, 5), + F(3686636, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 2, 217), + F(4800000, P_XO, 5, 0, 0), + F(7373272, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 4, 217), + F(9600000, P_XO, 2.5, 0, 0), + F(14746544, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 8, 217), + F(16000000, P_GPLL0_OUT_MAIN, 10, 1, 5), + F(24000000, P_XO, 1, 0, 0), + F(25000000, P_GPLL0_OUT_MAIN, 16, 1, 2), + F(32000000, P_GPLL0_OUT_MAIN, 1, 1, 25), + F(40000000, P_GPLL0_OUT_MAIN, 1, 1, 20), + F(46400000, P_GPLL0_OUT_MAIN, 2, 29, 250), + F(48000000, P_GPLL0_OUT_MAIN, 1, 3, 50), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + F(51200000, P_GPLL0_OUT_MAIN, 1, 8, 125), + F(56000000, P_GPLL0_OUT_MAIN, 1, 7, 100), + F(58986175, P_GPLL0_OUT_MAIN, 1, 16, 217), + F(60000000, P_GPLL0_OUT_MAIN, 1, 3, 40), + F(64000000, P_GPLL0_OUT_MAIN, 12.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se0_clk_src = { + .cmd_rcgr = 0x4004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se0_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se1_clk_src = { + .cmd_rcgr = 0x5004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se1_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se2_clk_src = { + .cmd_rcgr = 0x2018, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se3_clk_src = { + .cmd_rcgr = 0x2034, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se4_clk_src = { + .cmd_rcgr = 0x3018, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se5_clk_src = { + .cmd_rcgr = 0x3034, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se5_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_src[] = { + F(144000, P_XO, 16, 12, 125), + F(400000, P_XO, 12, 1, 5), + F(24000000, P_GPLL2_OUT_MAIN, 12, 1, 2), + F(48000000, P_GPLL2_OUT_MAIN, 12, 0, 0), + F(96000000, P_GPLL2_OUT_MAIN, 6, 0, 0), + F(177777778, P_GPLL0_OUT_MAIN, 4.5, 0, 0), + F(192000000, P_GPLL2_OUT_MAIN, 3, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_apps_clk_src = { + .cmd_rcgr = 0x33004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_6, + .freq_tbl = ftbl_gcc_sdcc1_apps_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_apps_clk_src", + .parent_data = gcc_parent_data_6, + .num_parents = ARRAY_SIZE(gcc_parent_data_6), + .ops = &clk_rcg2_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_ice_core_clk_src[] = { + F(300000000, P_GPLL4_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_ice_core_clk_src = { + .cmd_rcgr = 0x33018, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_7, + .freq_tbl = ftbl_gcc_sdcc1_ice_core_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ice_core_clk_src", + .parent_data = gcc_parent_data_7, + .num_parents = ARRAY_SIZE(gcc_parent_data_7), + .ops = &clk_rcg2_floor_ops, + }, +}; + +static struct clk_rcg2 gcc_uniphy_sys_clk_src = { + .cmd_rcgr = 0x17090, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_nss_ts_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy_sys_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_usb0_aux_clk_src = { + .cmd_rcgr = 0x2c018, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_8, + .freq_tbl = ftbl_gcc_nss_ts_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_aux_clk_src", + .parent_data = gcc_parent_data_8, + .num_parents = ARRAY_SIZE(gcc_parent_data_8), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb0_master_clk_src[] = { + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_usb0_master_clk_src = { + .cmd_rcgr = 0x2c004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_usb0_master_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_master_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb0_mock_utmi_clk_src[] = { + F(60000000, P_GPLL4_OUT_AUX, 10, 1, 2), + { } +}; + +static struct clk_rcg2 gcc_usb0_mock_utmi_clk_src = { + .cmd_rcgr = 0x2c02c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_9, + .freq_tbl = ftbl_gcc_usb0_mock_utmi_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_mock_utmi_clk_src", + .parent_data = gcc_parent_data_9, + .num_parents = ARRAY_SIZE(gcc_parent_data_9), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qdss_at_clk_src[] = { + F(240000000, P_GPLL4_OUT_MAIN, 5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qdss_at_clk_src = { + .cmd_rcgr = 0x2d004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_10, + .freq_tbl = ftbl_gcc_qdss_at_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_at_clk_src", + .parent_data = gcc_parent_data_10, + .num_parents = ARRAY_SIZE(gcc_parent_data_10), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qdss_tsctr_clk_src[] = { + F(600000000, P_GPLL4_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qdss_tsctr_clk_src = { + .cmd_rcgr = 0x2d01c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_10, + .freq_tbl = ftbl_gcc_qdss_tsctr_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_tsctr_clk_src", + .parent_data = gcc_parent_data_10, + .num_parents = ARRAY_SIZE(gcc_parent_data_10), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcnoc_bfdcd_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcnoc_bfdcd_clk_src = { + .cmd_rcgr = 0x31004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcnoc_bfdcd_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcnoc_bfdcd_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + /* + * There are no consumers for this source in kernel yet, + * (will be added soon), so the clock framework + * disables this source. But some of the clocks + * initialized by boot loaders uses this source. So we + * need to keep this clock ON. Add the + * CLK_IGNORE_UNUSED flag so the clock will not be + * disabled. Once the consumer in kernel is added, we + * can get rid of this flag. + */ + .flags = CLK_IS_CRITICAL, + }, +}; + +static const struct freq_tbl ftbl_gcc_qpic_io_macro_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(320000000, P_GPLL0_OUT_MAIN, 2.5, 0, 0), + F(400000000, P_GPLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qpic_io_macro_clk_src = { + .cmd_rcgr = 0x32004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_12, + .freq_tbl = ftbl_gcc_qpic_io_macro_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_io_macro_clk_src", + .parent_data = gcc_parent_data_12, + .num_parents = ARRAY_SIZE(gcc_parent_data_12), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qpic_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qpic_clk_src = { + .cmd_rcgr = 0x32020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_12, + .freq_tbl = ftbl_gcc_qpic_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_clk_src", + .parent_data = gcc_parent_data_12, + .num_parents = ARRAY_SIZE(gcc_parent_data_12), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pon_tm2x_clk_src[] = { + F(342860000, P_GPLL4_OUT_MAIN, 3.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pon_tm2x_clk_src = { + .cmd_rcgr = 0x3c004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_11, + .freq_tbl = ftbl_gcc_pon_tm2x_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pon_tm2x_clk_src", + .parent_data = gcc_parent_data_11, + .num_parents = ARRAY_SIZE(gcc_parent_data_11), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sleep_clk_src[] = { + F(32000, P_SLEEP_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sleep_clk_src = { + .cmd_rcgr = 0x3400c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_13, + .freq_tbl = ftbl_gcc_sleep_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sleep_clk_src", + .parent_data = gcc_parent_data_13, + .num_parents = ARRAY_SIZE(gcc_parent_data_13), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_lpass_sway_clk_src[] = { + F(133333333, P_GPLL0_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_lpass_sway_clk_src = { + .cmd_rcgr = 0x27004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_lpass_sway_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_lpass_sway_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_lpass_axim_clk_src = { + .cmd_rcgr = 0x2700c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_lpass_sway_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_lpass_axim_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_regmap_div gcc_nssnoc_memnoc_div_clk_src = { + .reg = 0x1700c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_memnoc_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_nssnoc_memnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_usb0_mock_utmi_div_clk_src = { + .reg = 0x2c040, + .shift = 0, + .width = 2, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_mock_utmi_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_fixed_factor gcc_pon_tm_div_clk_src = { + .mult = 1, + .div = 2, + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pon_tm_div_clk_src", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pon_tm2x_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_fixed_factor_ops, + }, +}; + +static struct clk_branch gcc_adss_pwm_clk = { + .halt_reg = 0x1c00c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1c00c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_adss_pwm_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_adss_pwm_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cnoc_pcie0_1lane_s_clk = { + .halt_reg = 0x31088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x31088, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cnoc_pcie0_1lane_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cnoc_pcie1_2lane_s_clk = { + .halt_reg = 0x3108c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3108c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cnoc_pcie1_2lane_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cnoc_usb_clk = { + .halt_reg = 0x310a8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x310a8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cnoc_usb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mdio_ahb_clk = { + .halt_reg = 0x17040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_mdio_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mdio_gephy_ahb_clk = { + .halt_reg = 0x17098, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17098, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_mdio_gephy_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nss_ts_clk = { + .halt_reg = 0x17018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nss_ts_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_nss_ts_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nsscc_clk = { + .halt_reg = 0x17034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nsscc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nsscfg_clk = { + .halt_reg = 0x1702c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1702c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nsscfg_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_atb_clk = { + .halt_reg = 0x17014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_atb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qdss_at_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_memnoc_1_clk = { + .halt_reg = 0x17084, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17084, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_memnoc_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_nssnoc_memnoc_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_memnoc_clk = { + .halt_reg = 0x17024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17024, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_memnoc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_nssnoc_memnoc_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_nsscc_clk = { + .halt_reg = 0x17030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_nsscc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_rcg2 gcc_xo_clk_src = { + .cmd_rcgr = 0x34004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_xo, + .freq_tbl = ftbl_gcc_nss_ts_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_xo_clk_src", + .parent_data = &gcc_parent_data_xo, + .num_parents = 1, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_fixed_factor gcc_xo_div4_clk_src = { + .mult = 1, + .div = 4, + .hw.init = &(const struct clk_init_data) { + .name = "gcc_xo_div4_clk_src", + .parent_hws = (const struct clk_hw *[]) { + &gcc_xo_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_fixed_factor_ops, + }, +}; + +static struct clk_branch gcc_gephy_sys_clk = { + .halt_reg = 0x2a004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gephy_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_pcnoc_1_clk = { + .halt_reg = 0x17080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17080, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_pcnoc_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_qosgen_ref_clk = { + .halt_reg = 0x1701c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1701c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_qosgen_ref_clk", + .parent_hws = (const struct clk_hw *[]){ + &gcc_xo_div4_clk_src.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_snoc_1_clk = { + .halt_reg = 0x1707c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1707c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_snoc_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_system_noc_bfdcd_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_snoc_clk = { + .halt_reg = 0x17028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_snoc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_system_noc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_timeout_ref_clk = { + .halt_reg = 0x17020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_timeout_ref_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_xo_div4_clk_src.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_xo_dcd_clk = { + .halt_reg = 0x17074, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17074, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_xo_dcd_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_ahb_clk = { + .halt_reg = 0x28030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_aux_clk = { + .halt_reg = 0x28070, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_axi_m_clk = { + .halt_reg = 0x28038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_axi_s_bridge_clk = { + .halt_reg = 0x28048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_axi_s_clk = { + .halt_reg = 0x28040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie0_pipe_clk_src = { + .reg = 0x28064, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "pcie0_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE30_PHY0_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_pipe_clk = { + .halt_reg = 0x28068, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x28068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie0_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_ahb_clk = { + .halt_reg = 0x29030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_aux_clk = { + .halt_reg = 0x29074, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29074, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_axi_m_clk = { + .halt_reg = 0x29038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_axi_s_bridge_clk = { + .halt_reg = 0x29048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_axi_s_clk = { + .halt_reg = 0x29040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie1_pipe_clk_src = { + .reg = 0x29064, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "pcie1_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE30_PHY1_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_pipe_clk = { + .halt_reg = 0x29068, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x29068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie1_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qrng_ahb_clk = { + .halt_reg = 0x13024, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb004, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qrng_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_ahb_mst_clk = { + .halt_reg = 0x1014, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb004, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_ahb_mst_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_ahb_slv_clk = { + .halt_reg = 0x102c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb004, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_ahb_slv_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se0_clk = { + .halt_reg = 0x4020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se1_clk = { + .halt_reg = 0x5020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se2_clk = { + .halt_reg = 0x202c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x202c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se3_clk = { + .halt_reg = 0x2048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se4_clk = { + .halt_reg = 0x302c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x302c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se5_clk = { + .halt_reg = 0x3048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se5_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ahb_clk = { + .halt_reg = 0x3303c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3303c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_apps_clk = { + .halt_reg = 0x3302c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3302c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_apps_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc1_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ice_core_clk = { + .halt_reg = 0x33034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x33034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ice_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc1_ice_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_snoc_pcie0_axi_m_clk = { + .halt_reg = 0x2e04c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e04c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_snoc_pcie0_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_snoc_pcie1_axi_m_clk = { + .halt_reg = 0x2e050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e050, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_snoc_pcie1_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy0_ahb_clk = { + .halt_reg = 0x1704c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1704c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy0_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy0_sys_clk = { + .halt_reg = 0x17048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy0_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_uniphy_sys_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy1_ahb_clk = { + .halt_reg = 0x1705c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1705c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy1_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy1_sys_clk = { + .halt_reg = 0x17058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy1_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_uniphy_sys_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy2_ahb_clk = { + .halt_reg = 0x1706c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1706c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy2_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy2_sys_clk = { + .halt_reg = 0x17068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy2_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_uniphy_sys_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_aux_clk = { + .halt_reg = 0x2c04c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2c04c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_master_clk = { + .halt_reg = 0x2c044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2c044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_mock_utmi_clk = { + .halt_reg = 0x2c050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2c050, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_mock_utmi_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_phy_cfg_ahb_clk = { + .halt_reg = 0x2c05c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2c05c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_phy_cfg_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_usb0_pipe_clk_src = { + .reg = 0x2c074, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_USB3_PHY0_CC_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_pipe_clk = { + .halt_reg = 0x2c054, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x2c054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_usb0_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_sleep_clk = { + .halt_reg = 0x2c058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2c058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_sleep_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sleep_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_rchng_clk = { + .halt_reg = 0x28028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28028, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_rchng_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie0_rchng_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_rchng_clk = { + .halt_reg = 0x29028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29028, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_rchng_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie1_rchng_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qpic_ahb_clk = { + .halt_reg = 0x32010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x32010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qpic_clk = { + .halt_reg = 0x32028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x32028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qpic_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qpic_io_macro_clk = { + .halt_reg = 0x3200c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3200c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_io_macro_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qpic_io_macro_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cmn_12gpll_ahb_clk = { + .halt_reg = 0x3a004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3a004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cmn_12gpll_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cmn_12gpll_sys_clk = { + .halt_reg = 0x3a008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3a008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cmn_12gpll_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_uniphy_sys_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qdss_at_clk = { + .halt_reg = 0x2d034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2d034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_at_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qdss_at_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qdss_dap_clk = { + .halt_reg = 0x2d058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb004, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_dap_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qdss_tsctr_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pon_apb_clk = { + .halt_reg = 0x3c01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3c01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pon_apb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pon_tm_clk = { + .halt_reg = 0x3c014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3c014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pon_tm_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pon_tm_div_clk_src.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pon_tm2x_clk = { + .halt_reg = 0x3c00c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3c00c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pon_tm2x_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pon_tm2x_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_snoc_lpass_clk = { + .halt_reg = 0x2e028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_snoc_lpass_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_lpass_axim_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_lpass_sway_clk = { + .halt_reg = 0x27014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x27014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_lpass_sway_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_lpass_sway_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cnoc_lpass_cfg_clk = { + .halt_reg = 0x31020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x31020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cnoc_lpass_cfg_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_lpass_sway_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_lpass_core_axim_clk = { + .halt_reg = 0x27018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x27018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_lpass_core_axim_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_lpass_axim_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static __maybe_unused struct clk_regmap *gcc_ipq5210_clocks[] = { + [GCC_ADSS_PWM_CLK] = &gcc_adss_pwm_clk.clkr, + [GCC_ADSS_PWM_CLK_SRC] = &gcc_adss_pwm_clk_src.clkr, + [GCC_CMN_12GPLL_AHB_CLK] = &gcc_cmn_12gpll_ahb_clk.clkr, + [GCC_CMN_12GPLL_SYS_CLK] = &gcc_cmn_12gpll_sys_clk.clkr, + [GCC_CNOC_LPASS_CFG_CLK] = &gcc_cnoc_lpass_cfg_clk.clkr, + [GCC_CNOC_PCIE0_1LANE_S_CLK] = &gcc_cnoc_pcie0_1lane_s_clk.clkr, + [GCC_CNOC_PCIE1_2LANE_S_CLK] = &gcc_cnoc_pcie1_2lane_s_clk.clkr, + [GCC_CNOC_USB_CLK] = &gcc_cnoc_usb_clk.clkr, + [GCC_GEPHY_SYS_CLK] = &gcc_gephy_sys_clk.clkr, + [GCC_LPASS_AXIM_CLK_SRC] = &gcc_lpass_axim_clk_src.clkr, + [GCC_LPASS_CORE_AXIM_CLK] = &gcc_lpass_core_axim_clk.clkr, + [GCC_LPASS_SWAY_CLK] = &gcc_lpass_sway_clk.clkr, + [GCC_LPASS_SWAY_CLK_SRC] = &gcc_lpass_sway_clk_src.clkr, + [GCC_MDIO_AHB_CLK] = &gcc_mdio_ahb_clk.clkr, + [GCC_MDIO_GEPHY_AHB_CLK] = &gcc_mdio_gephy_ahb_clk.clkr, + [GCC_NSS_TS_CLK] = &gcc_nss_ts_clk.clkr, + [GCC_NSS_TS_CLK_SRC] = &gcc_nss_ts_clk_src.clkr, + [GCC_NSSCC_CLK] = &gcc_nsscc_clk.clkr, + [GCC_NSSCFG_CLK] = &gcc_nsscfg_clk.clkr, + [GCC_NSSNOC_ATB_CLK] = &gcc_nssnoc_atb_clk.clkr, + [GCC_NSSNOC_MEMNOC_1_CLK] = &gcc_nssnoc_memnoc_1_clk.clkr, + [GCC_NSSNOC_MEMNOC_BFDCD_CLK_SRC] = &gcc_nssnoc_memnoc_bfdcd_clk_src.clkr, + [GCC_NSSNOC_MEMNOC_CLK] = &gcc_nssnoc_memnoc_clk.clkr, + [GCC_NSSNOC_MEMNOC_DIV_CLK_SRC] = &gcc_nssnoc_memnoc_div_clk_src.clkr, + [GCC_NSSNOC_NSSCC_CLK] = &gcc_nssnoc_nsscc_clk.clkr, + [GCC_NSSNOC_PCNOC_1_CLK] = &gcc_nssnoc_pcnoc_1_clk.clkr, + [GCC_NSSNOC_QOSGEN_REF_CLK] = &gcc_nssnoc_qosgen_ref_clk.clkr, + [GCC_NSSNOC_SNOC_1_CLK] = &gcc_nssnoc_snoc_1_clk.clkr, + [GCC_NSSNOC_SNOC_CLK] = &gcc_nssnoc_snoc_clk.clkr, + [GCC_NSSNOC_TIMEOUT_REF_CLK] = &gcc_nssnoc_timeout_ref_clk.clkr, + [GCC_NSSNOC_XO_DCD_CLK] = &gcc_nssnoc_xo_dcd_clk.clkr, + [GCC_PCIE0_AHB_CLK] = &gcc_pcie0_ahb_clk.clkr, + [GCC_PCIE0_AUX_CLK] = &gcc_pcie0_aux_clk.clkr, + [GCC_PCIE0_AXI_M_CLK] = &gcc_pcie0_axi_m_clk.clkr, + [GCC_PCIE0_AXI_M_CLK_SRC] = &gcc_pcie0_axi_m_clk_src.clkr, + [GCC_PCIE0_AXI_S_BRIDGE_CLK] = &gcc_pcie0_axi_s_bridge_clk.clkr, + [GCC_PCIE0_AXI_S_CLK] = &gcc_pcie0_axi_s_clk.clkr, + [GCC_PCIE0_AXI_S_CLK_SRC] = &gcc_pcie0_axi_s_clk_src.clkr, + [GCC_PCIE0_PIPE_CLK] = &gcc_pcie0_pipe_clk.clkr, + [GCC_PCIE0_PIPE_CLK_SRC] = &gcc_pcie0_pipe_clk_src.clkr, + [GCC_PCIE0_RCHNG_CLK] = &gcc_pcie0_rchng_clk.clkr, + [GCC_PCIE0_RCHNG_CLK_SRC] = &gcc_pcie0_rchng_clk_src.clkr, + [GCC_PCIE1_AHB_CLK] = &gcc_pcie1_ahb_clk.clkr, + [GCC_PCIE1_AUX_CLK] = &gcc_pcie1_aux_clk.clkr, + [GCC_PCIE1_AXI_M_CLK] = &gcc_pcie1_axi_m_clk.clkr, + [GCC_PCIE1_AXI_M_CLK_SRC] = &gcc_pcie1_axi_m_clk_src.clkr, + [GCC_PCIE1_AXI_S_BRIDGE_CLK] = &gcc_pcie1_axi_s_bridge_clk.clkr, + [GCC_PCIE1_AXI_S_CLK] = &gcc_pcie1_axi_s_clk.clkr, + [GCC_PCIE1_AXI_S_CLK_SRC] = &gcc_pcie1_axi_s_clk_src.clkr, + [GCC_PCIE1_PIPE_CLK] = &gcc_pcie1_pipe_clk.clkr, + [GCC_PCIE1_PIPE_CLK_SRC] = &gcc_pcie1_pipe_clk_src.clkr, + [GCC_PCIE1_RCHNG_CLK] = &gcc_pcie1_rchng_clk.clkr, + [GCC_PCIE1_RCHNG_CLK_SRC] = &gcc_pcie1_rchng_clk_src.clkr, + [GCC_PCIE_AUX_CLK_SRC] = &gcc_pcie_aux_clk_src.clkr, + [GCC_PCNOC_BFDCD_CLK_SRC] = &gcc_pcnoc_bfdcd_clk_src.clkr, + [GCC_PON_APB_CLK] = &gcc_pon_apb_clk.clkr, + [GCC_PON_TM_CLK] = &gcc_pon_tm_clk.clkr, + [GCC_PON_TM2X_CLK] = &gcc_pon_tm2x_clk.clkr, + [GCC_PON_TM2X_CLK_SRC] = &gcc_pon_tm2x_clk_src.clkr, + [GCC_QDSS_AT_CLK] = &gcc_qdss_at_clk.clkr, + [GCC_QDSS_AT_CLK_SRC] = &gcc_qdss_at_clk_src.clkr, + [GCC_QDSS_DAP_CLK] = &gcc_qdss_dap_clk.clkr, + [GCC_QDSS_TSCTR_CLK_SRC] = &gcc_qdss_tsctr_clk_src.clkr, + [GCC_QPIC_AHB_CLK] = &gcc_qpic_ahb_clk.clkr, + [GCC_QPIC_CLK] = &gcc_qpic_clk.clkr, + [GCC_QPIC_CLK_SRC] = &gcc_qpic_clk_src.clkr, + [GCC_QPIC_IO_MACRO_CLK] = &gcc_qpic_io_macro_clk.clkr, + [GCC_QPIC_IO_MACRO_CLK_SRC] = &gcc_qpic_io_macro_clk_src.clkr, + [GCC_QRNG_AHB_CLK] = &gcc_qrng_ahb_clk.clkr, + [GCC_QUPV3_AHB_MST_CLK] = &gcc_qupv3_ahb_mst_clk.clkr, + [GCC_QUPV3_AHB_SLV_CLK] = &gcc_qupv3_ahb_slv_clk.clkr, + [GCC_QUPV3_WRAP_SE0_CLK] = &gcc_qupv3_wrap_se0_clk.clkr, + [GCC_QUPV3_WRAP_SE0_CLK_SRC] = &gcc_qupv3_wrap_se0_clk_src.clkr, + [GCC_QUPV3_WRAP_SE1_CLK] = &gcc_qupv3_wrap_se1_clk.clkr, + [GCC_QUPV3_WRAP_SE1_CLK_SRC] = &gcc_qupv3_wrap_se1_clk_src.clkr, + [GCC_QUPV3_WRAP_SE2_CLK] = &gcc_qupv3_wrap_se2_clk.clkr, + [GCC_QUPV3_WRAP_SE2_CLK_SRC] = &gcc_qupv3_wrap_se2_clk_src.clkr, + [GCC_QUPV3_WRAP_SE3_CLK] = &gcc_qupv3_wrap_se3_clk.clkr, + [GCC_QUPV3_WRAP_SE3_CLK_SRC] = &gcc_qupv3_wrap_se3_clk_src.clkr, + [GCC_QUPV3_WRAP_SE4_CLK] = &gcc_qupv3_wrap_se4_clk.clkr, + [GCC_QUPV3_WRAP_SE4_CLK_SRC] = &gcc_qupv3_wrap_se4_clk_src.clkr, + [GCC_QUPV3_WRAP_SE5_CLK] = &gcc_qupv3_wrap_se5_clk.clkr, + [GCC_QUPV3_WRAP_SE5_CLK_SRC] = &gcc_qupv3_wrap_se5_clk_src.clkr, + [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr, + [GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr, + [GCC_SDCC1_APPS_CLK_SRC] = &gcc_sdcc1_apps_clk_src.clkr, + [GCC_SDCC1_ICE_CORE_CLK] = &gcc_sdcc1_ice_core_clk.clkr, + [GCC_SDCC1_ICE_CORE_CLK_SRC] = &gcc_sdcc1_ice_core_clk_src.clkr, + [GCC_SLEEP_CLK_SRC] = &gcc_sleep_clk_src.clkr, + [GCC_SNOC_LPASS_CLK] = &gcc_snoc_lpass_clk.clkr, + [GCC_SNOC_PCIE0_AXI_M_CLK] = &gcc_snoc_pcie0_axi_m_clk.clkr, + [GCC_SNOC_PCIE1_AXI_M_CLK] = &gcc_snoc_pcie1_axi_m_clk.clkr, + [GCC_SYSTEM_NOC_BFDCD_CLK_SRC] = &gcc_system_noc_bfdcd_clk_src.clkr, + [GCC_UNIPHY0_AHB_CLK] = &gcc_uniphy0_ahb_clk.clkr, + [GCC_UNIPHY0_SYS_CLK] = &gcc_uniphy0_sys_clk.clkr, + [GCC_UNIPHY1_AHB_CLK] = &gcc_uniphy1_ahb_clk.clkr, + [GCC_UNIPHY1_SYS_CLK] = &gcc_uniphy1_sys_clk.clkr, + [GCC_UNIPHY2_AHB_CLK] = &gcc_uniphy2_ahb_clk.clkr, + [GCC_UNIPHY2_SYS_CLK] = &gcc_uniphy2_sys_clk.clkr, + [GCC_UNIPHY_SYS_CLK_SRC] = &gcc_uniphy_sys_clk_src.clkr, + [GCC_USB0_AUX_CLK] = &gcc_usb0_aux_clk.clkr, + [GCC_USB0_AUX_CLK_SRC] = &gcc_usb0_aux_clk_src.clkr, + [GCC_USB0_MASTER_CLK] = &gcc_usb0_master_clk.clkr, + [GCC_USB0_MASTER_CLK_SRC] = &gcc_usb0_master_clk_src.clkr, + [GCC_USB0_MOCK_UTMI_CLK] = &gcc_usb0_mock_utmi_clk.clkr, + [GCC_USB0_MOCK_UTMI_CLK_SRC] = &gcc_usb0_mock_utmi_clk_src.clkr, + [GCC_USB0_MOCK_UTMI_DIV_CLK_SRC] = &gcc_usb0_mock_utmi_div_clk_src.clkr, + [GCC_USB0_PHY_CFG_AHB_CLK] = &gcc_usb0_phy_cfg_ahb_clk.clkr, + [GCC_USB0_PIPE_CLK] = &gcc_usb0_pipe_clk.clkr, + [GCC_USB0_PIPE_CLK_SRC] = &gcc_usb0_pipe_clk_src.clkr, + [GCC_USB0_SLEEP_CLK] = &gcc_usb0_sleep_clk.clkr, + [GCC_XO_CLK_SRC] = &gcc_xo_clk_src.clkr, + [GPLL0_MAIN] = &gpll0_main.clkr, + [GPLL0] = &gpll0.clkr, + [GPLL2_MAIN] = &gpll2_main.clkr, + [GPLL2] = &gpll2.clkr, + [GPLL4_MAIN] = &gpll4_main.clkr, +}; + +static const struct qcom_reset_map gcc_ipq5210_resets[] = { + [GCC_ADSS_BCR] = { 0x1c000 }, + [GCC_ADSS_PWM_ARES] = { 0x1c00c, 2 }, + [GCC_APC0_VOLTAGE_DROOP_DETECTOR_BCR] = { 0x38000 }, + [GCC_APC0_VOLTAGE_DROOP_DETECTOR_GPLL0_ARES] = { 0x3800c, 2 }, + [GCC_APSS_AHB_ARES] = { 0x24014, 2 }, + [GCC_APSS_ATB_ARES] = { 0x24034, 2 }, + [GCC_APSS_AXI_ARES] = { 0x24018, 2 }, + [GCC_APSS_TS_ARES] = { 0x24030, 2 }, + [GCC_BOOT_ROM_AHB_ARES] = { 0x1302c, 2 }, + [GCC_BOOT_ROM_BCR] = { 0x13028 }, + [GCC_GEPHY_BCR] = { 0x2a000 }, + [GCC_GEPHY_SYS_ARES] = { 0x2a004, 2 }, + [GCC_GP1_ARES] = { 0x8018, 2 }, + [GCC_GP2_ARES] = { 0x9018, 2 }, + [GCC_GP3_ARES] = { 0xa018, 2 }, + [GCC_MDIO_AHB_ARES] = { 0x17040, 2 }, + [GCC_MDIO_BCR] = { 0x1703c }, + [GCC_MDIO_GEPHY_AHB_ARES] = { 0x17098, 2 }, + [GCC_NSS_BCR] = { 0x17000 }, + [GCC_NSS_TS_ARES] = { 0x17018, 2 }, + [GCC_NSSCC_ARES] = { 0x17034, 2 }, + [GCC_NSSCFG_ARES] = { 0x1702c, 2 }, + [GCC_NSSNOC_ATB_ARES] = { 0x17014, 2 }, + [GCC_NSSNOC_MEMNOC_1_ARES] = { 0x17084, 2 }, + [GCC_NSSNOC_MEMNOC_ARES] = { 0x17024, 2 }, + [GCC_NSSNOC_NSSCC_ARES] = { 0x17030, 2 }, + [GCC_NSSNOC_PCNOC_1_ARES] = { 0x17080, 2 }, + [GCC_NSSNOC_QOSGEN_REF_ARES] = { 0x1701c, 2 }, + [GCC_NSSNOC_SNOC_1_ARES] = { 0x1707c, 2 }, + [GCC_NSSNOC_SNOC_ARES] = { 0x17028, 2 }, + [GCC_NSSNOC_TIMEOUT_REF_ARES] = { 0x17020, 2 }, + [GCC_NSSNOC_XO_DCD_ARES] = { 0x17074, 2 }, + [GCC_PCIE0_AHB_ARES] = { 0x28030, 2 }, + [GCC_PCIE0_AUX_ARES] = { 0x28070, 2 }, + [GCC_PCIE0_AXI_M_ARES] = { 0x28038, 2 }, + [GCC_PCIE0_AXI_S_BRIDGE_ARES] = { 0x28048, 2 }, + [GCC_PCIE0_AXI_S_ARES] = { 0x28040, 2 }, + [GCC_PCIE0_BCR] = { 0x28000 }, + [GCC_PCIE0_LINK_DOWN_BCR] = { 0x28054 }, + [GCC_PCIE0_PIPE_RESET] = { 0x28058, 0 }, + [GCC_PCIE0_CORE_STICKY_RESET] = { 0x28058, 1 }, + [GCC_PCIE0_AXI_S_STICKY_RESET] = { 0x28058, 2 }, + [GCC_PCIE0_AXI_S_RESET] = { 0x28058, 3 }, + [GCC_PCIE0_AXI_M_STICKY_RESET] = { 0x28058, 4 }, + [GCC_PCIE0_AXI_M_RESET] = { 0x28058, 5 }, + [GCC_PCIE0_AUX_RESET] = { 0x28058, 6 }, + [GCC_PCIE0_AHB_RESET] = { 0x28058, 7 }, + [GCC_PCIE0_PHY_BCR] = { 0x28060 }, + [GCC_PCIE0_PIPE_ARES] = { 0x28068, 2 }, + [GCC_PCIE0PHY_PHY_BCR] = { 0x2805c }, + [GCC_PCIE1_AHB_ARES] = { 0x29030, 2 }, + [GCC_PCIE1_AUX_ARES] = { 0x29074, 2 }, + [GCC_PCIE1_AXI_M_ARES] = { 0x29038, 2 }, + [GCC_PCIE1_AXI_S_BRIDGE_ARES] = { 0x29048, 2 }, + [GCC_PCIE1_AXI_S_ARES] = { 0x29040, 2 }, + [GCC_PCIE1_BCR] = { 0x29000 }, + [GCC_PCIE1_LINK_DOWN_BCR] = { 0x29054 }, + [GCC_PCIE1_PIPE_RESET] = { 0x29058, 0 }, + [GCC_PCIE1_CORE_STICKY_RESET] = { 0x29058, 1 }, + [GCC_PCIE1_AXI_S_STICKY_RESET] = { 0x29058, 2 }, + [GCC_PCIE1_AXI_S_RESET] = { 0x29058, 3 }, + [GCC_PCIE1_AXI_M_STICKY_RESET] = { 0x29058, 4 }, + [GCC_PCIE1_AXI_M_RESET] = { 0x29058, 5 }, + [GCC_PCIE1_AUX_RESET] = { 0x29058, 6 }, + [GCC_PCIE1_AHB_RESET] = { 0x29058, 7 }, + [GCC_PCIE1_PHY_BCR] = { 0x29060 }, + [GCC_PCIE1_PIPE_ARES] = { 0x29068, 2 }, + [GCC_PCIE1PHY_PHY_BCR] = { 0x2905c }, + [GCC_QRNG_AHB_ARES] = { 0x13024, 2 }, + [GCC_QRNG_BCR] = { 0x13020 }, + [GCC_QUPV3_2X_CORE_ARES] = { 0x1020, 2 }, + [GCC_QUPV3_AHB_MST_ARES] = { 0x1014, 2 }, + [GCC_QUPV3_AHB_SLV_ARES] = { 0x102c, 2 }, + [GCC_QUPV3_BCR] = { 0x1000 }, + [GCC_QUPV3_CORE_ARES] = { 0x1018, 2 }, + [GCC_QUPV3_WRAP_SE0_ARES] = { 0x4020, 2 }, + [GCC_QUPV3_WRAP_SE0_BCR] = { 0x4000 }, + [GCC_QUPV3_WRAP_SE1_ARES] = { 0x5020, 2 }, + [GCC_QUPV3_WRAP_SE1_BCR] = { 0x5000 }, + [GCC_QUPV3_WRAP_SE2_ARES] = { 0x202c, 2 }, + [GCC_QUPV3_WRAP_SE2_BCR] = { 0x2000 }, + [GCC_QUPV3_WRAP_SE3_ARES] = { 0x2048, 2 }, + [GCC_QUPV3_WRAP_SE3_BCR] = { 0x2030 }, + [GCC_QUPV3_WRAP_SE4_ARES] = { 0x302c, 2 }, + [GCC_QUPV3_WRAP_SE4_BCR] = { 0x3000 }, + [GCC_QUPV3_WRAP_SE5_ARES] = { 0x3048, 2 }, + [GCC_QUPV3_WRAP_SE5_BCR] = { 0x3030 }, + [GCC_QUSB2_0_PHY_BCR] = { 0x2c068 }, + [GCC_SDCC1_AHB_ARES] = { 0x3303c, 2 }, + [GCC_SDCC1_APPS_ARES] = { 0x3302c, 2 }, + [GCC_SDCC1_ICE_CORE_ARES] = { 0x33034, 2 }, + [GCC_SDCC_BCR] = { 0x33000 }, + [GCC_TLMM_AHB_ARES] = { 0x3e004, 2 }, + [GCC_TLMM_ARES] = { 0x3e008, 2 }, + [GCC_TLMM_BCR] = { 0x3e000 }, + [GCC_UNIPHY0_AHB_ARES] = { 0x1704c, 2 }, + [GCC_UNIPHY0_BCR] = { 0x17044 }, + [GCC_UNIPHY0_SYS_ARES] = { 0x17048, 2 }, + [GCC_UNIPHY1_AHB_ARES] = { 0x1705c, 2 }, + [GCC_UNIPHY1_BCR] = { 0x17054 }, + [GCC_UNIPHY1_SYS_ARES] = { 0x17058, 2 }, + [GCC_UNIPHY2_AHB_ARES] = { 0x1706c, 2 }, + [GCC_UNIPHY2_BCR] = { 0x17064 }, + [GCC_UNIPHY2_SYS_ARES] = { 0x17068, 2 }, + [GCC_USB0_AUX_ARES] = { 0x2c04c, 2 }, + [GCC_USB0_MASTER_ARES] = { 0x2c044, 2 }, + [GCC_USB0_MOCK_UTMI_ARES] = { 0x2c050, 2 }, + [GCC_USB0_PHY_BCR] = { 0x2c06c }, + [GCC_USB0_PHY_CFG_AHB_ARES] = { 0x2c05c, 2 }, + [GCC_USB0_PIPE_ARES] = { 0x2c054, 2 }, + [GCC_USB0_SLEEP_ARES] = { 0x2c058, 2 }, + [GCC_USB3PHY_0_PHY_BCR] = { 0x2c070 }, + [GCC_USB_BCR] = { 0x2c000 }, + [GCC_QDSS_BCR] = { 0x2d000 }, +}; + +static const struct of_device_id gcc_ipq5210_match_table[] = { + { .compatible = "qcom,ipq5210-gcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_ipq5210_match_table); + +static const struct regmap_config gcc_ipq5210_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3f024, + .fast_io = true, +}; + +static struct clk_hw *gcc_ipq5210_hws[] = { + &gpll0_div2.hw, + &gcc_xo_div4_clk_src.hw, + &gcc_pon_tm_div_clk_src.hw, +}; + +static const struct qcom_cc_desc gcc_ipq5210_desc = { + .config = &gcc_ipq5210_regmap_config, + .clks = gcc_ipq5210_clocks, + .num_clks = ARRAY_SIZE(gcc_ipq5210_clocks), + .resets = gcc_ipq5210_resets, + .num_resets = ARRAY_SIZE(gcc_ipq5210_resets), + .clk_hws = gcc_ipq5210_hws, + .num_clk_hws = ARRAY_SIZE(gcc_ipq5210_hws), +}; + +static int gcc_ipq5210_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gcc_ipq5210_desc); +} + +static struct platform_driver gcc_ipq5210_driver = { + .probe = gcc_ipq5210_probe, + .driver = { + .name = "qcom,gcc-ipq5210", + .of_match_table = gcc_ipq5210_match_table, + }, +}; + +static int __init gcc_ipq5210_init(void) +{ + return platform_driver_register(&gcc_ipq5210_driver); +} +core_initcall(gcc_ipq5210_init); + +static void __exit gcc_ipq5210_exit(void) +{ + platform_driver_unregister(&gcc_ipq5210_driver); +} +module_exit(gcc_ipq5210_exit); + +MODULE_DESCRIPTION("QTI GCC IPQ5210 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/gcc-ipq6018.c b/drivers/clk/qcom/gcc-ipq6018.c index d4fc491a18b2..6943dc511534 100644 --- a/drivers/clk/qcom/gcc-ipq6018.c +++ b/drivers/clk/qcom/gcc-ipq6018.c @@ -400,7 +400,7 @@ static struct clk_branch gcc_xo_clk_src = { .fw_name = "xo", }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, .ops = &clk_branch2_ops, }, }, diff --git a/drivers/clk/qcom/gcc-kaanapali.c b/drivers/clk/qcom/gcc-kaanapali.c index b9743284927d..6e628b51f38c 100644 --- a/drivers/clk/qcom/gcc-kaanapali.c +++ b/drivers/clk/qcom/gcc-kaanapali.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -3458,7 +3457,7 @@ static const struct clk_rcg_dfs_data gcc_dfs_clocks[] = { DEFINE_RCG_DFS(gcc_qupv3_wrap4_s4_clk_src), }; -static u32 gcc_kaanapali_critical_cbcrs[] = { +static const u32 gcc_kaanapali_critical_cbcrs[] = { 0xa0004, /* GCC_CAM_BIST_MCLK_AHB_CLK */ 0x26004, /* GCC_CAMERA_AHB_CLK */ 0x2603c, /* GCC_CAMERA_XO_CLK */ @@ -3486,7 +3485,7 @@ static void clk_kaanapali_regs_configure(struct device *dev, struct regmap *regm qcom_branch_set_force_mem_core(regmap, gcc_ufs_phy_ice_core_clk, true); } -static struct qcom_cc_driver_data gcc_kaanapali_driver_data = { +static const struct qcom_cc_driver_data gcc_kaanapali_driver_data = { .clk_cbcrs = gcc_kaanapali_critical_cbcrs, .num_clk_cbcrs = ARRAY_SIZE(gcc_kaanapali_critical_cbcrs), .dfs_rcgs = gcc_dfs_clocks, diff --git a/drivers/clk/qcom/gcc-milos.c b/drivers/clk/qcom/gcc-milos.c index 81fa09ec55d7..67d0eee8ef35 100644 --- a/drivers/clk/qcom/gcc-milos.c +++ b/drivers/clk/qcom/gcc-milos.c @@ -3152,7 +3152,7 @@ static struct gdsc *gcc_milos_gdscs[] = { [USB3_PHY_GDSC] = &usb3_phy_gdsc, }; -static u32 gcc_milos_critical_cbcrs[] = { +static const u32 gcc_milos_critical_cbcrs[] = { 0x26004, /* GCC_CAMERA_AHB_CLK */ 0x26018, /* GCC_CAMERA_HF_XO_CLK */ 0x2601c, /* GCC_CAMERA_SF_XO_CLK */ @@ -3171,7 +3171,7 @@ static const struct regmap_config gcc_milos_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data gcc_milos_driver_data = { +static const struct qcom_cc_driver_data gcc_milos_driver_data = { .clk_cbcrs = gcc_milos_critical_cbcrs, .num_clk_cbcrs = ARRAY_SIZE(gcc_milos_critical_cbcrs), .dfs_rcgs = gcc_milos_dfs_clocks, diff --git a/drivers/clk/qcom/gcc-nord.c b/drivers/clk/qcom/gcc-nord.c new file mode 100644 index 000000000000..3098d8fac0fb --- /dev/null +++ b/drivers/clk/qcom/gcc-nord.c @@ -0,0 +1,1902 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "clk-regmap-phy-mux.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_BI_TCXO, + DT_SLEEP_CLK, + DT_PCIE_A_PIPE_CLK, + DT_PCIE_B_PIPE_CLK, + DT_PCIE_C_PIPE_CLK, + DT_PCIE_D_PIPE_CLK, +}; + +enum { + P_BI_TCXO, + P_GCC_GPLL0_OUT_EVEN, + P_GCC_GPLL0_OUT_MAIN, + P_PCIE_A_PIPE_CLK, + P_PCIE_B_PIPE_CLK, + P_PCIE_C_PIPE_CLK, + P_PCIE_D_PIPE_CLK, + P_SLEEP_CLK, +}; + +static struct clk_alpha_pll gcc_gpll0 = { + .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x9d020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gcc_gpll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gcc_gpll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_gcc_gpll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_gcc_gpll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +static const struct parent_map gcc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_SLEEP_CLK, 5 }, +}; + +static const struct clk_parent_data gcc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_SLEEP_CLK, 5 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, +}; + +static const struct clk_parent_data gcc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, +}; + +static struct clk_regmap_phy_mux gcc_pcie_a_pipe_clk_src = { + .reg = 0x49094, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_pipe_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_A_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie_b_pipe_clk_src = { + .reg = 0x4a094, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_pipe_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_B_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie_c_pipe_clk_src = { + .reg = 0x4b094, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_pipe_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_C_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie_d_pipe_clk_src = { + .reg = 0x4c094, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_pipe_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_D_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static const struct freq_tbl ftbl_gcc_gp1_clk_src[] = { + F(66666667, P_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(200000000, P_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_gp1_clk_src = { + .cmd_rcgr = 0x30004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp1_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_gp2_clk_src = { + .cmd_rcgr = 0x31004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp2_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_a_aux_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie_a_aux_clk_src = { + .cmd_rcgr = 0x49098, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie_a_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_aux_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_a_phy_aux_clk_src = { + .cmd_rcgr = 0x4d020, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie_a_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_phy_aux_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_a_phy_rchng_clk_src[] = { + F(66666667, P_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie_a_phy_rchng_clk_src = { + .cmd_rcgr = 0x4907c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_a_phy_rchng_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_phy_rchng_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_b_aux_clk_src = { + .cmd_rcgr = 0x4a098, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie_a_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_aux_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_b_phy_aux_clk_src = { + .cmd_rcgr = 0x4e020, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie_a_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_phy_aux_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_b_phy_rchng_clk_src = { + .cmd_rcgr = 0x4a07c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_a_phy_rchng_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_phy_rchng_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_c_aux_clk_src = { + .cmd_rcgr = 0x4b098, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie_a_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_aux_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_c_phy_aux_clk_src = { + .cmd_rcgr = 0x4f020, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie_a_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_phy_aux_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_c_phy_rchng_clk_src = { + .cmd_rcgr = 0x4b07c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_pcie_a_phy_rchng_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_phy_rchng_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_d_aux_clk_src = { + .cmd_rcgr = 0x4c098, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie_a_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_aux_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_d_phy_aux_clk_src = { + .cmd_rcgr = 0x50020, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie_a_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_phy_aux_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_d_phy_rchng_clk_src = { + .cmd_rcgr = 0x4c07c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_pcie_a_phy_rchng_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_phy_rchng_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_noc_refgen_clk_src = { + .cmd_rcgr = 0x52094, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_a_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_refgen_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_noc_safety_clk_src = { + .cmd_rcgr = 0x520ac, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_a_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_safety_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pdm2_clk_src[] = { + F(40000000, P_GCC_GPLL0_OUT_MAIN, 15, 0, 0), + F(60000000, P_GCC_GPLL0_OUT_MAIN, 10, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pdm2_clk_src = { + .cmd_rcgr = 0x1a010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pdm2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap3_qspi_ref_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 375), + F(112000000, P_GCC_GPLL0_OUT_EVEN, 1, 28, 75), + F(117964800, P_GCC_GPLL0_OUT_EVEN, 1, 6144, 15625), + F(120000000, P_GCC_GPLL0_OUT_MAIN, 5, 0, 0), + F(150000000, P_GCC_GPLL0_OUT_EVEN, 2, 0, 0), + F(240000000, P_GCC_GPLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap3_qspi_ref_clk_src_init = { + .name = "gcc_qupv3_wrap3_qspi_ref_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_qspi_ref_clk_src = { + .cmd_rcgr = 0x23174, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap3_qspi_ref_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_qspi_ref_clk_src_init, +}; + +static struct clk_regmap_div gcc_qupv3_wrap3_s0_clk_src = { + .reg = 0x2316c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s0_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch gcc_boot_rom_ahb_clk = { + .halt_reg = 0x1f004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1f004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_boot_rom_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp1_clk = { + .halt_reg = 0x30000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x30000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp2_clk = { + .halt_reg = 0x31000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x31000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mmu_0_tcu_vote_clk = { + .halt_reg = 0x7d094, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d094, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_mmu_0_tcu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_a_aux_clk = { + .halt_reg = 0x49058, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x49058, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_a_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_a_cfg_ahb_clk = { + .halt_reg = 0x49054, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x49054, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(13), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_a_dti_qtc_clk = { + .halt_reg = 0x49018, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x49018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_dti_qtc_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_a_mstr_axi_clk = { + .halt_reg = 0x49040, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x49040, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(12), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_a_phy_aux_clk = { + .halt_reg = 0x4d01c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(12), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_a_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_a_phy_rchng_clk = { + .halt_reg = 0x49078, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x49078, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_phy_rchng_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_a_phy_rchng_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_a_pipe_clk = { + .halt_reg = 0x49068, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x49068, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_a_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_a_slv_axi_clk = { + .halt_reg = 0x4902c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x4902c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(11), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_slv_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_a_slv_q2a_axi_clk = { + .halt_reg = 0x49024, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x49024, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_a_slv_q2a_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_b_aux_clk = { + .halt_reg = 0x4a058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(23), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_b_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_b_cfg_ahb_clk = { + .halt_reg = 0x4a054, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x4a054, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(22), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_b_dti_qtc_clk = { + .halt_reg = 0x4a018, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x4a018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(17), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_dti_qtc_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_b_mstr_axi_clk = { + .halt_reg = 0x4a040, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x4a040, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(21), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_b_phy_aux_clk = { + .halt_reg = 0x4e01c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(13), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_b_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_b_phy_rchng_clk = { + .halt_reg = 0x4a078, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_phy_rchng_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_b_phy_rchng_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_b_pipe_clk = { + .halt_reg = 0x4a068, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_b_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_b_slv_axi_clk = { + .halt_reg = 0x4a02c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x4a02c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(20), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_slv_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_b_slv_q2a_axi_clk = { + .halt_reg = 0x4a024, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(19), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_b_slv_q2a_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_c_aux_clk = { + .halt_reg = 0x4b058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_c_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_c_cfg_ahb_clk = { + .halt_reg = 0x4b054, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x4b054, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(31), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_c_dti_qtc_clk = { + .halt_reg = 0x4b018, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x4b018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_dti_qtc_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_c_mstr_axi_clk = { + .halt_reg = 0x4b040, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x4b040, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(30), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_c_phy_aux_clk = { + .halt_reg = 0x4f01c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_c_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_c_phy_rchng_clk = { + .halt_reg = 0x4b078, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_phy_rchng_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_c_phy_rchng_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_c_pipe_clk = { + .halt_reg = 0x4b068, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_c_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_c_slv_axi_clk = { + .halt_reg = 0x4b02c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x4b02c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(29), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_slv_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_c_slv_q2a_axi_clk = { + .halt_reg = 0x4b024, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d008, + .enable_mask = BIT(28), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_c_slv_q2a_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_d_aux_clk = { + .halt_reg = 0x4c058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(9), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_d_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_d_cfg_ahb_clk = { + .halt_reg = 0x4c054, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x4c054, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_d_dti_qtc_clk = { + .halt_reg = 0x4c018, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x4c018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(3), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_dti_qtc_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_d_mstr_axi_clk = { + .halt_reg = 0x4c040, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x4c040, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_d_phy_aux_clk = { + .halt_reg = 0x5001c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_d_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_d_phy_rchng_clk = { + .halt_reg = 0x4c078, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(11), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_phy_rchng_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_d_phy_rchng_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_d_pipe_clk = { + .halt_reg = 0x4c068, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_d_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_d_slv_axi_clk = { + .halt_reg = 0x4c02c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x4c02c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_slv_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_d_slv_q2a_axi_clk = { + .halt_reg = 0x4c024, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_d_slv_q2a_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_link_ahb_clk = { + .halt_reg = 0x52464, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x52464, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_link_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_link_xo_clk = { + .halt_reg = 0x52468, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x52468, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52468, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_link_xo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_async_bridge_clk = { + .halt_reg = 0x52048, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x52048, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d018, + .enable_mask = BIT(18), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_async_bridge_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_cnoc_sf_qx_clk = { + .halt_reg = 0x52040, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x52040, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_cnoc_sf_qx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_m_cfg_clk = { + .halt_reg = 0x52060, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x52060, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d018, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_m_cfg_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_m_pdb_clk = { + .halt_reg = 0x52084, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x52084, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d018, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_m_pdb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_mstr_axi_clk = { + .halt_reg = 0x52050, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x52050, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_pwrctl_clk = { + .halt_reg = 0x52080, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d018, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_pwrctl_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_qosgen_extref_clk = { + .halt_reg = 0x52074, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(19), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_qosgen_extref_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_refgen_clk = { + .halt_reg = 0x52078, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x52078, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_refgen_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_noc_refgen_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_s_cfg_clk = { + .halt_reg = 0x52064, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d018, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_s_cfg_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_s_pdb_clk = { + .halt_reg = 0x5208c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x5208c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d018, + .enable_mask = BIT(9), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_s_pdb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_safety_clk = { + .halt_reg = 0x5207c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5207c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_safety_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_noc_safety_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_slave_axi_clk = { + .halt_reg = 0x52058, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x52058, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_slave_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_tsctr_clk = { + .halt_reg = 0x52070, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(18), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_tsctr_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_noc_xo_clk = { + .halt_reg = 0x52068, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d018, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_noc_xo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm2_clk = { + .halt_reg = 0x1a00c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1a00c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pdm2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_ahb_clk = { + .halt_reg = 0x1a004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x1a004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1a004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_xo4_clk = { + .halt_reg = 0x1a008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1a008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm_xo4_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_core_2x_clk = { + .halt_reg = 0x23020, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d000, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_core_clk = { + .halt_reg = 0x2300c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d000, + .enable_mask = BIT(23), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_m_clk = { + .halt_reg = 0x23004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d000, + .enable_mask = BIT(22), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_m_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_qspi_ref_clk = { + .halt_reg = 0x23170, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d000, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_qspi_ref_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s0_clk = { + .halt_reg = 0x2315c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9d000, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s_ahb_clk = { + .halt_reg = 0x23008, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9d010, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_smmu_pcie_qtc_vote_clk = { + .halt_reg = 0x7d0b8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d0b8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_smmu_pcie_qtc_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc gcc_pcie_a_gdsc = { + .gdscr = 0x49004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .collapse_ctrl = 0x8d02c, + .collapse_mask = BIT(1), + .pd = { + .name = "gcc_pcie_a_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_a_phy_gdsc = { + .gdscr = 0x4d004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .collapse_ctrl = 0x8d02c, + .collapse_mask = BIT(5), + .pd = { + .name = "gcc_pcie_a_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_b_gdsc = { + .gdscr = 0x4a004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .collapse_ctrl = 0x8d02c, + .collapse_mask = BIT(2), + .pd = { + .name = "gcc_pcie_b_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_b_phy_gdsc = { + .gdscr = 0x4e004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .collapse_ctrl = 0x8d02c, + .collapse_mask = BIT(6), + .pd = { + .name = "gcc_pcie_b_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_c_gdsc = { + .gdscr = 0x4b004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .collapse_ctrl = 0x8d02c, + .collapse_mask = BIT(3), + .pd = { + .name = "gcc_pcie_c_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_c_phy_gdsc = { + .gdscr = 0x4f004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .collapse_ctrl = 0x8d02c, + .collapse_mask = BIT(7), + .pd = { + .name = "gcc_pcie_c_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_d_gdsc = { + .gdscr = 0x4c004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .collapse_ctrl = 0x8d02c, + .collapse_mask = BIT(4), + .pd = { + .name = "gcc_pcie_d_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_d_phy_gdsc = { + .gdscr = 0x50004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .collapse_ctrl = 0x8d02c, + .collapse_mask = BIT(8), + .pd = { + .name = "gcc_pcie_d_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_noc_gdsc = { + .gdscr = 0x52004, + .gds_hw_ctrl = 0x52018, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .collapse_ctrl = 0x8d02c, + .collapse_mask = BIT(0), + .pd = { + .name = "gcc_pcie_noc_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct clk_regmap *gcc_nord_clocks[] = { + [GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr, + [GCC_GP1_CLK] = &gcc_gp1_clk.clkr, + [GCC_GP1_CLK_SRC] = &gcc_gp1_clk_src.clkr, + [GCC_GP2_CLK] = &gcc_gp2_clk.clkr, + [GCC_GP2_CLK_SRC] = &gcc_gp2_clk_src.clkr, + [GCC_GPLL0] = &gcc_gpll0.clkr, + [GCC_GPLL0_OUT_EVEN] = &gcc_gpll0_out_even.clkr, + [GCC_MMU_0_TCU_VOTE_CLK] = &gcc_mmu_0_tcu_vote_clk.clkr, + [GCC_PCIE_A_AUX_CLK] = &gcc_pcie_a_aux_clk.clkr, + [GCC_PCIE_A_AUX_CLK_SRC] = &gcc_pcie_a_aux_clk_src.clkr, + [GCC_PCIE_A_CFG_AHB_CLK] = &gcc_pcie_a_cfg_ahb_clk.clkr, + [GCC_PCIE_A_DTI_QTC_CLK] = &gcc_pcie_a_dti_qtc_clk.clkr, + [GCC_PCIE_A_MSTR_AXI_CLK] = &gcc_pcie_a_mstr_axi_clk.clkr, + [GCC_PCIE_A_PHY_AUX_CLK] = &gcc_pcie_a_phy_aux_clk.clkr, + [GCC_PCIE_A_PHY_AUX_CLK_SRC] = &gcc_pcie_a_phy_aux_clk_src.clkr, + [GCC_PCIE_A_PHY_RCHNG_CLK] = &gcc_pcie_a_phy_rchng_clk.clkr, + [GCC_PCIE_A_PHY_RCHNG_CLK_SRC] = &gcc_pcie_a_phy_rchng_clk_src.clkr, + [GCC_PCIE_A_PIPE_CLK] = &gcc_pcie_a_pipe_clk.clkr, + [GCC_PCIE_A_PIPE_CLK_SRC] = &gcc_pcie_a_pipe_clk_src.clkr, + [GCC_PCIE_A_SLV_AXI_CLK] = &gcc_pcie_a_slv_axi_clk.clkr, + [GCC_PCIE_A_SLV_Q2A_AXI_CLK] = &gcc_pcie_a_slv_q2a_axi_clk.clkr, + [GCC_PCIE_B_AUX_CLK] = &gcc_pcie_b_aux_clk.clkr, + [GCC_PCIE_B_AUX_CLK_SRC] = &gcc_pcie_b_aux_clk_src.clkr, + [GCC_PCIE_B_CFG_AHB_CLK] = &gcc_pcie_b_cfg_ahb_clk.clkr, + [GCC_PCIE_B_DTI_QTC_CLK] = &gcc_pcie_b_dti_qtc_clk.clkr, + [GCC_PCIE_B_MSTR_AXI_CLK] = &gcc_pcie_b_mstr_axi_clk.clkr, + [GCC_PCIE_B_PHY_AUX_CLK] = &gcc_pcie_b_phy_aux_clk.clkr, + [GCC_PCIE_B_PHY_AUX_CLK_SRC] = &gcc_pcie_b_phy_aux_clk_src.clkr, + [GCC_PCIE_B_PHY_RCHNG_CLK] = &gcc_pcie_b_phy_rchng_clk.clkr, + [GCC_PCIE_B_PHY_RCHNG_CLK_SRC] = &gcc_pcie_b_phy_rchng_clk_src.clkr, + [GCC_PCIE_B_PIPE_CLK] = &gcc_pcie_b_pipe_clk.clkr, + [GCC_PCIE_B_PIPE_CLK_SRC] = &gcc_pcie_b_pipe_clk_src.clkr, + [GCC_PCIE_B_SLV_AXI_CLK] = &gcc_pcie_b_slv_axi_clk.clkr, + [GCC_PCIE_B_SLV_Q2A_AXI_CLK] = &gcc_pcie_b_slv_q2a_axi_clk.clkr, + [GCC_PCIE_C_AUX_CLK] = &gcc_pcie_c_aux_clk.clkr, + [GCC_PCIE_C_AUX_CLK_SRC] = &gcc_pcie_c_aux_clk_src.clkr, + [GCC_PCIE_C_CFG_AHB_CLK] = &gcc_pcie_c_cfg_ahb_clk.clkr, + [GCC_PCIE_C_DTI_QTC_CLK] = &gcc_pcie_c_dti_qtc_clk.clkr, + [GCC_PCIE_C_MSTR_AXI_CLK] = &gcc_pcie_c_mstr_axi_clk.clkr, + [GCC_PCIE_C_PHY_AUX_CLK] = &gcc_pcie_c_phy_aux_clk.clkr, + [GCC_PCIE_C_PHY_AUX_CLK_SRC] = &gcc_pcie_c_phy_aux_clk_src.clkr, + [GCC_PCIE_C_PHY_RCHNG_CLK] = &gcc_pcie_c_phy_rchng_clk.clkr, + [GCC_PCIE_C_PHY_RCHNG_CLK_SRC] = &gcc_pcie_c_phy_rchng_clk_src.clkr, + [GCC_PCIE_C_PIPE_CLK] = &gcc_pcie_c_pipe_clk.clkr, + [GCC_PCIE_C_PIPE_CLK_SRC] = &gcc_pcie_c_pipe_clk_src.clkr, + [GCC_PCIE_C_SLV_AXI_CLK] = &gcc_pcie_c_slv_axi_clk.clkr, + [GCC_PCIE_C_SLV_Q2A_AXI_CLK] = &gcc_pcie_c_slv_q2a_axi_clk.clkr, + [GCC_PCIE_D_AUX_CLK] = &gcc_pcie_d_aux_clk.clkr, + [GCC_PCIE_D_AUX_CLK_SRC] = &gcc_pcie_d_aux_clk_src.clkr, + [GCC_PCIE_D_CFG_AHB_CLK] = &gcc_pcie_d_cfg_ahb_clk.clkr, + [GCC_PCIE_D_DTI_QTC_CLK] = &gcc_pcie_d_dti_qtc_clk.clkr, + [GCC_PCIE_D_MSTR_AXI_CLK] = &gcc_pcie_d_mstr_axi_clk.clkr, + [GCC_PCIE_D_PHY_AUX_CLK] = &gcc_pcie_d_phy_aux_clk.clkr, + [GCC_PCIE_D_PHY_AUX_CLK_SRC] = &gcc_pcie_d_phy_aux_clk_src.clkr, + [GCC_PCIE_D_PHY_RCHNG_CLK] = &gcc_pcie_d_phy_rchng_clk.clkr, + [GCC_PCIE_D_PHY_RCHNG_CLK_SRC] = &gcc_pcie_d_phy_rchng_clk_src.clkr, + [GCC_PCIE_D_PIPE_CLK] = &gcc_pcie_d_pipe_clk.clkr, + [GCC_PCIE_D_PIPE_CLK_SRC] = &gcc_pcie_d_pipe_clk_src.clkr, + [GCC_PCIE_D_SLV_AXI_CLK] = &gcc_pcie_d_slv_axi_clk.clkr, + [GCC_PCIE_D_SLV_Q2A_AXI_CLK] = &gcc_pcie_d_slv_q2a_axi_clk.clkr, + [GCC_PCIE_LINK_AHB_CLK] = &gcc_pcie_link_ahb_clk.clkr, + [GCC_PCIE_LINK_XO_CLK] = &gcc_pcie_link_xo_clk.clkr, + [GCC_PCIE_NOC_ASYNC_BRIDGE_CLK] = &gcc_pcie_noc_async_bridge_clk.clkr, + [GCC_PCIE_NOC_CNOC_SF_QX_CLK] = &gcc_pcie_noc_cnoc_sf_qx_clk.clkr, + [GCC_PCIE_NOC_M_CFG_CLK] = &gcc_pcie_noc_m_cfg_clk.clkr, + [GCC_PCIE_NOC_M_PDB_CLK] = &gcc_pcie_noc_m_pdb_clk.clkr, + [GCC_PCIE_NOC_MSTR_AXI_CLK] = &gcc_pcie_noc_mstr_axi_clk.clkr, + [GCC_PCIE_NOC_PWRCTL_CLK] = &gcc_pcie_noc_pwrctl_clk.clkr, + [GCC_PCIE_NOC_QOSGEN_EXTREF_CLK] = &gcc_pcie_noc_qosgen_extref_clk.clkr, + [GCC_PCIE_NOC_REFGEN_CLK] = &gcc_pcie_noc_refgen_clk.clkr, + [GCC_PCIE_NOC_REFGEN_CLK_SRC] = &gcc_pcie_noc_refgen_clk_src.clkr, + [GCC_PCIE_NOC_S_CFG_CLK] = &gcc_pcie_noc_s_cfg_clk.clkr, + [GCC_PCIE_NOC_S_PDB_CLK] = &gcc_pcie_noc_s_pdb_clk.clkr, + [GCC_PCIE_NOC_SAFETY_CLK] = &gcc_pcie_noc_safety_clk.clkr, + [GCC_PCIE_NOC_SAFETY_CLK_SRC] = &gcc_pcie_noc_safety_clk_src.clkr, + [GCC_PCIE_NOC_SLAVE_AXI_CLK] = &gcc_pcie_noc_slave_axi_clk.clkr, + [GCC_PCIE_NOC_TSCTR_CLK] = &gcc_pcie_noc_tsctr_clk.clkr, + [GCC_PCIE_NOC_XO_CLK] = &gcc_pcie_noc_xo_clk.clkr, + [GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr, + [GCC_PDM2_CLK_SRC] = &gcc_pdm2_clk_src.clkr, + [GCC_PDM_AHB_CLK] = &gcc_pdm_ahb_clk.clkr, + [GCC_PDM_XO4_CLK] = &gcc_pdm_xo4_clk.clkr, + [GCC_QUPV3_WRAP3_CORE_2X_CLK] = &gcc_qupv3_wrap3_core_2x_clk.clkr, + [GCC_QUPV3_WRAP3_CORE_CLK] = &gcc_qupv3_wrap3_core_clk.clkr, + [GCC_QUPV3_WRAP3_M_CLK] = &gcc_qupv3_wrap3_m_clk.clkr, + [GCC_QUPV3_WRAP3_QSPI_REF_CLK] = &gcc_qupv3_wrap3_qspi_ref_clk.clkr, + [GCC_QUPV3_WRAP3_QSPI_REF_CLK_SRC] = &gcc_qupv3_wrap3_qspi_ref_clk_src.clkr, + [GCC_QUPV3_WRAP3_S0_CLK] = &gcc_qupv3_wrap3_s0_clk.clkr, + [GCC_QUPV3_WRAP3_S0_CLK_SRC] = &gcc_qupv3_wrap3_s0_clk_src.clkr, + [GCC_QUPV3_WRAP3_S_AHB_CLK] = &gcc_qupv3_wrap3_s_ahb_clk.clkr, + [GCC_SMMU_PCIE_QTC_VOTE_CLK] = &gcc_smmu_pcie_qtc_vote_clk.clkr, +}; + +static struct gdsc *gcc_nord_gdscs[] = { + [GCC_PCIE_A_GDSC] = &gcc_pcie_a_gdsc, + [GCC_PCIE_A_PHY_GDSC] = &gcc_pcie_a_phy_gdsc, + [GCC_PCIE_B_GDSC] = &gcc_pcie_b_gdsc, + [GCC_PCIE_B_PHY_GDSC] = &gcc_pcie_b_phy_gdsc, + [GCC_PCIE_C_GDSC] = &gcc_pcie_c_gdsc, + [GCC_PCIE_C_PHY_GDSC] = &gcc_pcie_c_phy_gdsc, + [GCC_PCIE_D_GDSC] = &gcc_pcie_d_gdsc, + [GCC_PCIE_D_PHY_GDSC] = &gcc_pcie_d_phy_gdsc, + [GCC_PCIE_NOC_GDSC] = &gcc_pcie_noc_gdsc, +}; + +static const struct qcom_reset_map gcc_nord_resets[] = { + [GCC_PCIE_A_BCR] = { 0x49000 }, + [GCC_PCIE_A_LINK_DOWN_BCR] = { 0xb9000 }, + [GCC_PCIE_A_NOCSR_COM_PHY_BCR] = { 0xb900c }, + [GCC_PCIE_A_PHY_BCR] = { 0x4d000 }, + [GCC_PCIE_A_PHY_CFG_AHB_BCR] = { 0xb9014 }, + [GCC_PCIE_A_PHY_COM_BCR] = { 0xb9018 }, + [GCC_PCIE_A_PHY_NOCSR_COM_PHY_BCR] = { 0xb9010 }, + [GCC_PCIE_B_BCR] = { 0x4a000 }, + [GCC_PCIE_B_LINK_DOWN_BCR] = { 0xba000 }, + [GCC_PCIE_B_NOCSR_COM_PHY_BCR] = { 0xba008 }, + [GCC_PCIE_B_PHY_BCR] = { 0x4e000 }, + [GCC_PCIE_B_PHY_CFG_AHB_BCR] = { 0xba010 }, + [GCC_PCIE_B_PHY_COM_BCR] = { 0xba014 }, + [GCC_PCIE_B_PHY_NOCSR_COM_PHY_BCR] = { 0xba00c }, + [GCC_PCIE_C_BCR] = { 0x4b000 }, + [GCC_PCIE_C_LINK_DOWN_BCR] = { 0xbb07c }, + [GCC_PCIE_C_NOCSR_COM_PHY_BCR] = { 0xbb084 }, + [GCC_PCIE_C_PHY_BCR] = { 0x4f000 }, + [GCC_PCIE_C_PHY_CFG_AHB_BCR] = { 0xbb08c }, + [GCC_PCIE_C_PHY_COM_BCR] = { 0xbb090 }, + [GCC_PCIE_C_PHY_NOCSR_COM_PHY_BCR] = { 0xbb088 }, + [GCC_PCIE_D_BCR] = { 0x4c000 }, + [GCC_PCIE_D_LINK_DOWN_BCR] = { 0xbc000 }, + [GCC_PCIE_D_NOCSR_COM_PHY_BCR] = { 0xbc008 }, + [GCC_PCIE_D_PHY_BCR] = { 0x50000 }, + [GCC_PCIE_D_PHY_CFG_AHB_BCR] = { 0xbc010 }, + [GCC_PCIE_D_PHY_COM_BCR] = { 0xbc014 }, + [GCC_PCIE_D_PHY_NOCSR_COM_PHY_BCR] = { 0xbc00c }, + [GCC_PCIE_NOC_BCR] = { 0x52000 }, + [GCC_PDM_BCR] = { 0x1a000 }, + [GCC_QUPV3_WRAPPER_3_BCR] = { 0x23000 }, + [GCC_TCSR_PCIE_BCR] = { 0xb901c }, +}; + +static const struct clk_rcg_dfs_data gcc_nord_dfs_clocks[] = { + DEFINE_RCG_DFS(gcc_qupv3_wrap3_qspi_ref_clk_src), +}; + +static const struct regmap_config gcc_nord_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1f41f0, + .fast_io = true, +}; + +static struct qcom_cc_driver_data gcc_nord_driver_data = { + .dfs_rcgs = gcc_nord_dfs_clocks, + .num_dfs_rcgs = ARRAY_SIZE(gcc_nord_dfs_clocks), +}; + +static const struct qcom_cc_desc gcc_nord_desc = { + .config = &gcc_nord_regmap_config, + .clks = gcc_nord_clocks, + .num_clks = ARRAY_SIZE(gcc_nord_clocks), + .resets = gcc_nord_resets, + .num_resets = ARRAY_SIZE(gcc_nord_resets), + .gdscs = gcc_nord_gdscs, + .num_gdscs = ARRAY_SIZE(gcc_nord_gdscs), + .use_rpm = true, + .driver_data = &gcc_nord_driver_data, +}; + +static const struct of_device_id gcc_nord_match_table[] = { + { .compatible = "qcom,nord-gcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_nord_match_table); + +static int gcc_nord_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gcc_nord_desc); +} + +static struct platform_driver gcc_nord_driver = { + .probe = gcc_nord_probe, + .driver = { + .name = "gcc-nord", + .of_match_table = gcc_nord_match_table, + }, +}; + +static int __init gcc_nord_init(void) +{ + return platform_driver_register(&gcc_nord_driver); +} +subsys_initcall(gcc_nord_init); + +static void __exit gcc_nord_exit(void) +{ + platform_driver_unregister(&gcc_nord_driver); +} +module_exit(gcc_nord_exit); + +MODULE_DESCRIPTION("QTI GCC NORD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/gcc-sc8180x.c b/drivers/clk/qcom/gcc-sc8180x.c index 31e788e22ab4..e6b7f1a5dcef 100644 --- a/drivers/clk/qcom/gcc-sc8180x.c +++ b/drivers/clk/qcom/gcc-sc8180x.c @@ -4172,7 +4172,7 @@ static struct gdsc usb30_sec_gdsc = { .pd = { .name = "usb30_sec_gdsc", }, - .pwrsts = PWRSTS_OFF_ON, + .pwrsts = PWRSTS_RET_ON, .flags = POLL_CFG_GDSCR, }; @@ -4190,7 +4190,7 @@ static struct gdsc usb30_prim_gdsc = { .pd = { .name = "usb30_prim_gdsc", }, - .pwrsts = PWRSTS_OFF_ON, + .pwrsts = PWRSTS_RET_ON, .flags = POLL_CFG_GDSCR, }; @@ -4199,7 +4199,7 @@ static struct gdsc pcie_0_gdsc = { .pd = { .name = "pcie_0_gdsc", }, - .pwrsts = PWRSTS_OFF_ON, + .pwrsts = PWRSTS_RET_ON, .flags = POLL_CFG_GDSCR, }; @@ -4226,7 +4226,7 @@ static struct gdsc pcie_1_gdsc = { .pd = { .name = "pcie_1_gdsc", }, - .pwrsts = PWRSTS_OFF_ON, + .pwrsts = PWRSTS_RET_ON, .flags = POLL_CFG_GDSCR, }; @@ -4235,7 +4235,7 @@ static struct gdsc pcie_2_gdsc = { .pd = { .name = "pcie_2_gdsc", }, - .pwrsts = PWRSTS_OFF_ON, + .pwrsts = PWRSTS_RET_ON, .flags = POLL_CFG_GDSCR, }; @@ -4253,7 +4253,7 @@ static struct gdsc pcie_3_gdsc = { .pd = { .name = "pcie_3_gdsc", }, - .pwrsts = PWRSTS_OFF_ON, + .pwrsts = PWRSTS_RET_ON, .flags = POLL_CFG_GDSCR, }; @@ -4262,10 +4262,55 @@ static struct gdsc usb30_mp_gdsc = { .pd = { .name = "usb30_mp_gdsc", }, - .pwrsts = PWRSTS_OFF_ON, + .pwrsts = PWRSTS_RET_ON, .flags = POLL_CFG_GDSCR, }; +static struct gdsc hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc = { + .gdscr = 0x7d050, + .pd = { + .name = "hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, +}; + +static struct gdsc hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc = { + .gdscr = 0x7d058, + .pd = { + .name = "hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, +}; + +static struct gdsc hlos1_vote_mmnoc_mmu_tbu_sf_gdsc = { + .gdscr = 0x7d054, + .pd = { + .name = "hlos1_vote_mmnoc_mmu_tbu_sf_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, +}; + +static struct gdsc hlos1_vote_turing_mmu_tbu0_gdsc = { + .gdscr = 0x7d05c, + .pd = { + .name = "hlos1_vote_turing_mmu_tbu0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, +}; + +static struct gdsc hlos1_vote_turing_mmu_tbu1_gdsc = { + .gdscr = 0x7d060, + .pd = { + .name = "hlos1_vote_turing_mmu_tbu1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, +}; + static struct clk_regmap *gcc_sc8180x_clocks[] = { [GCC_AGGRE_NOC_PCIE_TBU_CLK] = &gcc_aggre_noc_pcie_tbu_clk.clkr, [GCC_AGGRE_UFS_CARD_AXI_CLK] = &gcc_aggre_ufs_card_axi_clk.clkr, @@ -4560,7 +4605,7 @@ static const struct qcom_reset_map gcc_sc8180x_resets[] = { [GCC_VIDEO_AXI1_CLK_BCR] = { .reg = 0xb028, .bit = 2, .udelay = 150 }, }; -static const struct clk_rcg_dfs_data gcc_dfs_clocks[] = { +static const struct clk_rcg_dfs_data gcc_sc8180x_dfs_clocks[] = { DEFINE_RCG_DFS(gcc_qupv3_wrap0_s0_clk_src), DEFINE_RCG_DFS(gcc_qupv3_wrap0_s1_clk_src), DEFINE_RCG_DFS(gcc_qupv3_wrap0_s2_clk_src), @@ -4595,6 +4640,24 @@ static struct gdsc *gcc_sc8180x_gdscs[] = { [USB30_MP_GDSC] = &usb30_mp_gdsc, [USB30_PRIM_GDSC] = &usb30_prim_gdsc, [USB30_SEC_GDSC] = &usb30_sec_gdsc, + [HLOS1_VOTE_MMNOC_MMU_TBU_HF0_GDSC] = &hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc, + [HLOS1_VOTE_MMNOC_MMU_TBU_HF1_GDSC] = &hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc, + [HLOS1_VOTE_MMNOC_MMU_TBU_SF_GDSC] = &hlos1_vote_mmnoc_mmu_tbu_sf_gdsc, + [HLOS1_VOTE_TURING_MMU_TBU0_GDSC] = &hlos1_vote_turing_mmu_tbu0_gdsc, + [HLOS1_VOTE_TURING_MMU_TBU1_GDSC] = &hlos1_vote_turing_mmu_tbu1_gdsc, +}; + +static const u32 gcc_sc8180x_critical_cbcrs[] = { + 0xb004, /* GCC_VIDEO_AHB_CLK */ + 0xb008, /* GCC_CAMERA_AHB_CLK */ + 0xb00c, /* GCC_DISP_AHB_CLK */ + 0xb040, /* GCC_VIDEO_XO_CLK */ + 0xb044, /* GCC_CAMERA_XO_CLK */ + 0xb048, /* GCC_DISP_XO_CLK */ + 0x48004, /* GCC_CPUSS_GNOC_CLK */ + 0x48190, /* GCC_CPUSS_DVM_BUS_CLK */ + 0x4d004, /* GCC_NPU_CFG_AHB_CLK */ + 0x71004, /* GCC_GPU_CFG_AHB_CLK */ }; static const struct regmap_config gcc_sc8180x_regmap_config = { @@ -4605,6 +4668,21 @@ static const struct regmap_config gcc_sc8180x_regmap_config = { .fast_io = true, }; +static void clk_sc8180x_regs_configure(struct device *dev, struct regmap *regmap) +{ + /* Disable the GPLL0 active input to NPU and GPU via MISC registers */ + regmap_update_bits(regmap, 0x4d110, 0x3, 0x3); + regmap_update_bits(regmap, 0x71028, 0x3, 0x3); +} + +static const struct qcom_cc_driver_data gcc_sc8180x_driver_data = { + .clk_cbcrs = gcc_sc8180x_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(gcc_sc8180x_critical_cbcrs), + .dfs_rcgs = gcc_sc8180x_dfs_clocks, + .num_dfs_rcgs = ARRAY_SIZE(gcc_sc8180x_dfs_clocks), + .clk_regs_configure = clk_sc8180x_regs_configure, +}; + static const struct qcom_cc_desc gcc_sc8180x_desc = { .config = &gcc_sc8180x_regmap_config, .clks = gcc_sc8180x_clocks, @@ -4613,6 +4691,8 @@ static const struct qcom_cc_desc gcc_sc8180x_desc = { .num_resets = ARRAY_SIZE(gcc_sc8180x_resets), .gdscs = gcc_sc8180x_gdscs, .num_gdscs = ARRAY_SIZE(gcc_sc8180x_gdscs), + .use_rpm = true, + .driver_data = &gcc_sc8180x_driver_data, }; static const struct of_device_id gcc_sc8180x_match_table[] = { @@ -4623,35 +4703,7 @@ MODULE_DEVICE_TABLE(of, gcc_sc8180x_match_table); static int gcc_sc8180x_probe(struct platform_device *pdev) { - struct regmap *regmap; - int ret; - - regmap = qcom_cc_map(pdev, &gcc_sc8180x_desc); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - /* Keep some clocks always-on */ - qcom_branch_set_clk_en(regmap, 0xb004); /* GCC_VIDEO_AHB_CLK */ - qcom_branch_set_clk_en(regmap, 0xb008); /* GCC_CAMERA_AHB_CLK */ - qcom_branch_set_clk_en(regmap, 0xb00c); /* GCC_DISP_AHB_CLK */ - qcom_branch_set_clk_en(regmap, 0xb040); /* GCC_VIDEO_XO_CLK */ - qcom_branch_set_clk_en(regmap, 0xb044); /* GCC_CAMERA_XO_CLK */ - qcom_branch_set_clk_en(regmap, 0xb048); /* GCC_DISP_XO_CLK */ - qcom_branch_set_clk_en(regmap, 0x48004); /* GCC_CPUSS_GNOC_CLK */ - qcom_branch_set_clk_en(regmap, 0x48190); /* GCC_CPUSS_DVM_BUS_CLK */ - qcom_branch_set_clk_en(regmap, 0x4d004); /* GCC_NPU_CFG_AHB_CLK */ - qcom_branch_set_clk_en(regmap, 0x71004); /* GCC_GPU_CFG_AHB_CLK */ - - /* Disable the GPLL0 active input to NPU and GPU via MISC registers */ - regmap_update_bits(regmap, 0x4d110, 0x3, 0x3); - regmap_update_bits(regmap, 0x71028, 0x3, 0x3); - - ret = qcom_cc_register_rcg_dfs(regmap, gcc_dfs_clocks, - ARRAY_SIZE(gcc_dfs_clocks)); - if (ret) - return ret; - - return qcom_cc_really_probe(&pdev->dev, &gcc_sc8180x_desc, regmap); + return qcom_cc_probe(pdev, &gcc_sc8180x_desc); } static struct platform_driver gcc_sc8180x_driver = { diff --git a/drivers/clk/qcom/gcc-x1e80100.c b/drivers/clk/qcom/gcc-x1e80100.c index 74afd12c158c..73a2a5112623 100644 --- a/drivers/clk/qcom/gcc-x1e80100.c +++ b/drivers/clk/qcom/gcc-x1e80100.c @@ -7480,6 +7480,7 @@ static int gcc_x1e80100_probe(struct platform_device *pdev) qcom_branch_set_clk_en(regmap, 0x32004); /* GCC_VIDEO_AHB_CLK */ qcom_branch_set_clk_en(regmap, 0x32030); /* GCC_VIDEO_XO_CLK */ qcom_branch_set_clk_en(regmap, 0x71004); /* GCC_GPU_CFG_AHB_CLK */ + qcom_branch_set_clk_en(regmap, 0x7d01c); /* GCC_HLOS1_VOTE_AGGRE_NOC_MMU_USB_QTB_CLK */ /* Clear GDSC_SLEEP_ENA_VOTE to stop votes being auto-removed in sleep. */ regmap_write(regmap, 0x52224, 0x0); diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index 7deabf8400cf..95aa07120245 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -518,10 +518,20 @@ static int gdsc_add_subdomain_list(struct dev_pm_domain_list *pd_list, ret = pm_genpd_add_subdomain(genpd, subdomain); if (ret) - return ret; + goto remove_added_subdomains; } return 0; + +remove_added_subdomains: + for (i--; i >= 0; i--) { + struct device *dev = pd_list->pd_devs[i]; + struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain); + + pm_genpd_remove_subdomain(genpd, subdomain); + } + + return ret; } static void gdsc_remove_subdomain_list(struct dev_pm_domain_list *pd_list, diff --git a/drivers/clk/qcom/gpucc-glymur.c b/drivers/clk/qcom/gpucc-glymur.c new file mode 100644 index 000000000000..54cc3127718a --- /dev/null +++ b/drivers/clk/qcom/gpucc-glymur.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_BI_TCXO, + DT_GPLL0_OUT_MAIN, + DT_GPLL0_OUT_MAIN_DIV, +}; + +enum { + P_BI_TCXO, + P_GPLL0_OUT_MAIN, + P_GPLL0_OUT_MAIN_DIV, + P_GPU_CC_PLL0_OUT_EVEN, + P_GPU_CC_PLL0_OUT_MAIN, + P_GPU_CC_PLL0_OUT_ODD, +}; + +static const struct pll_vco taycan_eko_t_vco[] = { + { 249600000, 2500000000, 0 }, +}; + +/* 1150.0 MHz Configuration */ +static const struct alpha_pll_config gpu_cc_pll0_config = { + .l = 0x3b, + .alpha = 0xe555, + .config_ctl_val = 0x25c400e7, + .config_ctl_hi_val = 0x0a8060e0, + .config_ctl_hi1_val = 0xf51dea20, + .user_ctl_val = 0x00000408, + .user_ctl_hi_val = 0x00000002, +}; + +static struct clk_alpha_pll gpu_cc_pll0 = { + .offset = 0x0, + .config = &gpu_cc_pll0_config, + .vco_table = taycan_eko_t_vco, + .num_vco = ARRAY_SIZE(taycan_eko_t_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EKO_T], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_taycan_eko_t_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gpu_cc_pll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gpu_cc_pll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_gpu_cc_pll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_gpu_cc_pll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EKO_T], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_pll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_taycan_eko_t_ops, + }, +}; + +static const struct parent_map gpu_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_GPLL0_OUT_MAIN_DIV, 6 }, +}; + +static const struct clk_parent_data gpu_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .index = DT_GPLL0_OUT_MAIN }, + { .index = DT_GPLL0_OUT_MAIN_DIV }, +}; + +static const struct parent_map gpu_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_GPU_CC_PLL0_OUT_MAIN, 1 }, + { P_GPU_CC_PLL0_OUT_EVEN, 2 }, + { P_GPU_CC_PLL0_OUT_ODD, 3 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_GPLL0_OUT_MAIN_DIV, 6 }, +}; + +static const struct clk_parent_data gpu_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpu_cc_pll0.clkr.hw }, + { .hw = &gpu_cc_pll0_out_even.clkr.hw }, + { .hw = &gpu_cc_pll0.clkr.hw }, + { .index = DT_GPLL0_OUT_MAIN }, + { .index = DT_GPLL0_OUT_MAIN_DIV }, +}; + +static const struct freq_tbl ftbl_gpu_cc_ff_clk_src[] = { + F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_ff_clk_src = { + .cmd_rcgr = 0x9474, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_0, + .freq_tbl = ftbl_gpu_cc_ff_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_ff_clk_src", + .parent_data = gpu_cc_parent_data_0, + .num_parents = ARRAY_SIZE(gpu_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(575000000, P_GPU_CC_PLL0_OUT_EVEN, 1, 0, 0), + F(700000000, P_GPU_CC_PLL0_OUT_EVEN, 1, 0, 0), + F(725000000, P_GPU_CC_PLL0_OUT_EVEN, 1, 0, 0), + F(750000000, P_GPU_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_gmu_clk_src = { + .cmd_rcgr = 0x9318, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_1, + .freq_tbl = ftbl_gpu_cc_gmu_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_gmu_clk_src", + .parent_data = gpu_cc_parent_data_1, + .num_parents = ARRAY_SIZE(gpu_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gpu_cc_hub_clk_src[] = { + F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), + F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0), + F(400000000, P_GPLL0_OUT_MAIN, 1.5, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_hub_clk_src = { + .cmd_rcgr = 0x93f0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_1, + .freq_tbl = ftbl_gpu_cc_hub_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_hub_clk_src", + .parent_data = gpu_cc_parent_data_1, + .num_parents = ARRAY_SIZE(gpu_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_regmap_div gpu_cc_hub_div_clk_src = { + .reg = 0x9430, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_hub_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_hub_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch gpu_cc_ahb_clk = { + .halt_reg = 0x90bc, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x90bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_hub_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_accu_shift_clk = { + .halt_reg = 0x9108, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9108, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_cx_accu_shift_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_ff_clk = { + .halt_reg = 0x90ec, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x90ec, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_cx_ff_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_ff_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gmu_clk = { + .halt_reg = 0x90d4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x90d4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_cx_gmu_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_gmu_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_clk = { + .halt_reg = 0x90e4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x90e4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_cxo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_demet_clk = { + .halt_reg = 0x9010, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_demet_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_dpm_clk = { + .halt_reg = 0x910c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x910c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_dpm_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_freq_measure_clk = { + .halt_reg = 0x900c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x900c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_freq_measure_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gpu_smmu_vote_clk = { + .halt_reg = 0x7000, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_gpu_smmu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_accu_shift_clk = { + .halt_reg = 0x9070, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_gx_accu_shift_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_acd_ahb_ff_clk = { + .halt_reg = 0x9068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_gx_acd_ahb_ff_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_ff_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_ahb_ff_clk = { + .halt_reg = 0x9064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_gx_ahb_ff_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_ff_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_gmu_clk = { + .halt_reg = 0x9060, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9060, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_gx_gmu_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_gmu_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_rcg_ahb_ff_clk = { + .halt_reg = 0x906c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x906c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_gx_rcg_ahb_ff_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_ff_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_hub_aon_clk = { + .halt_reg = 0x93ec, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x93ec, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_hub_aon_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_hub_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_hub_cx_int_clk = { + .halt_reg = 0x90e8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x90e8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_hub_cx_int_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_hub_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_memnoc_gfx_clk = { + .halt_reg = 0x90f0, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x90f0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_memnoc_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_rscc_hub_aon_clk = { + .halt_reg = 0x93e8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x93e8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_rscc_hub_aon_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_hub_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_sleep_clk = { + .halt_reg = 0x90cc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x90cc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc gpu_cc_cx_gdsc = { + .gdscr = 0x9080, + .gds_hw_ctrl = 0x9094, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "gpu_cc_cx_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *gpu_cc_glymur_clocks[] = { + [GPU_CC_AHB_CLK] = &gpu_cc_ahb_clk.clkr, + [GPU_CC_CX_ACCU_SHIFT_CLK] = &gpu_cc_cx_accu_shift_clk.clkr, + [GPU_CC_CX_FF_CLK] = &gpu_cc_cx_ff_clk.clkr, + [GPU_CC_CX_GMU_CLK] = &gpu_cc_cx_gmu_clk.clkr, + [GPU_CC_CXO_CLK] = &gpu_cc_cxo_clk.clkr, + [GPU_CC_DEMET_CLK] = &gpu_cc_demet_clk.clkr, + [GPU_CC_DPM_CLK] = &gpu_cc_dpm_clk.clkr, + [GPU_CC_FF_CLK_SRC] = &gpu_cc_ff_clk_src.clkr, + [GPU_CC_FREQ_MEASURE_CLK] = &gpu_cc_freq_measure_clk.clkr, + [GPU_CC_GMU_CLK_SRC] = &gpu_cc_gmu_clk_src.clkr, + [GPU_CC_GPU_SMMU_VOTE_CLK] = &gpu_cc_gpu_smmu_vote_clk.clkr, + [GPU_CC_GX_ACCU_SHIFT_CLK] = &gpu_cc_gx_accu_shift_clk.clkr, + [GPU_CC_GX_ACD_AHB_FF_CLK] = &gpu_cc_gx_acd_ahb_ff_clk.clkr, + [GPU_CC_GX_AHB_FF_CLK] = &gpu_cc_gx_ahb_ff_clk.clkr, + [GPU_CC_GX_GMU_CLK] = &gpu_cc_gx_gmu_clk.clkr, + [GPU_CC_GX_RCG_AHB_FF_CLK] = &gpu_cc_gx_rcg_ahb_ff_clk.clkr, + [GPU_CC_HUB_AON_CLK] = &gpu_cc_hub_aon_clk.clkr, + [GPU_CC_HUB_CLK_SRC] = &gpu_cc_hub_clk_src.clkr, + [GPU_CC_HUB_CX_INT_CLK] = &gpu_cc_hub_cx_int_clk.clkr, + [GPU_CC_HUB_DIV_CLK_SRC] = &gpu_cc_hub_div_clk_src.clkr, + [GPU_CC_MEMNOC_GFX_CLK] = &gpu_cc_memnoc_gfx_clk.clkr, + [GPU_CC_PLL0] = &gpu_cc_pll0.clkr, + [GPU_CC_PLL0_OUT_EVEN] = &gpu_cc_pll0_out_even.clkr, + [GPU_CC_RSCC_HUB_AON_CLK] = &gpu_cc_rscc_hub_aon_clk.clkr, + [GPU_CC_SLEEP_CLK] = &gpu_cc_sleep_clk.clkr, +}; + +static struct gdsc *gpu_cc_glymur_gdscs[] = { + [GPU_CC_CX_GDSC] = &gpu_cc_cx_gdsc, +}; + +static const struct qcom_reset_map gpu_cc_glymur_resets[] = { + [GPU_CC_CB_BCR] = { 0x93a0 }, + [GPU_CC_CX_BCR] = { 0x907c }, + [GPU_CC_FAST_HUB_BCR] = { 0x93e4 }, + [GPU_CC_FF_BCR] = { 0x9470 }, + [GPU_CC_GMU_BCR] = { 0x9314 }, + [GPU_CC_GX_BCR] = { 0x905c }, + [GPU_CC_XO_BCR] = { 0x9000 }, +}; + +static struct clk_alpha_pll *gpu_cc_glymur_plls[] = { + &gpu_cc_pll0, +}; + +static const u32 gpu_cc_glymur_critical_cbcrs[] = { + 0x93a4, /* GPU_CC_CB_CLK */ + 0x9008, /* GPU_CC_CXO_AON_CLK */ + 0x9004, /* GPU_CC_RSCC_XO_AON_CLK */ +}; + +static const struct regmap_config gpu_cc_glymur_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x95e8, + .fast_io = true, +}; + +static const struct qcom_cc_driver_data gpu_cc_glymur_driver_data = { + .alpha_plls = gpu_cc_glymur_plls, + .num_alpha_plls = ARRAY_SIZE(gpu_cc_glymur_plls), + .clk_cbcrs = gpu_cc_glymur_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(gpu_cc_glymur_critical_cbcrs), +}; + +static const struct qcom_cc_desc gpu_cc_glymur_desc = { + .config = &gpu_cc_glymur_regmap_config, + .clks = gpu_cc_glymur_clocks, + .num_clks = ARRAY_SIZE(gpu_cc_glymur_clocks), + .resets = gpu_cc_glymur_resets, + .num_resets = ARRAY_SIZE(gpu_cc_glymur_resets), + .gdscs = gpu_cc_glymur_gdscs, + .num_gdscs = ARRAY_SIZE(gpu_cc_glymur_gdscs), + .use_rpm = true, + .driver_data = &gpu_cc_glymur_driver_data, +}; + +static const struct of_device_id gpu_cc_glymur_match_table[] = { + { .compatible = "qcom,glymur-gpucc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpu_cc_glymur_match_table); + +static int gpu_cc_glymur_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gpu_cc_glymur_desc); +} + +static struct platform_driver gpu_cc_glymur_driver = { + .probe = gpu_cc_glymur_probe, + .driver = { + .name = "gpucc-glymur", + .of_match_table = gpu_cc_glymur_match_table, + }, +}; + +module_platform_driver(gpu_cc_glymur_driver); + +MODULE_DESCRIPTION("QTI GPUCC Glymur Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/gpucc-kaanapali.c b/drivers/clk/qcom/gpucc-kaanapali.c index 52be48c15c67..7f6013b348ad 100644 --- a/drivers/clk/qcom/gpucc-kaanapali.c +++ b/drivers/clk/qcom/gpucc-kaanapali.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -424,7 +423,7 @@ static struct clk_alpha_pll *gpu_cc_kaanapali_plls[] = { &gpu_cc_pll0, }; -static u32 gpu_cc_kaanapali_critical_cbcrs[] = { +static const u32 gpu_cc_kaanapali_critical_cbcrs[] = { 0x9008, /* GPU_CC_CXO_AON_CLK */ 0x93e8, /* GPU_CC_RSCC_HUB_AON_CLK */ 0x9004, /* GPU_CC_RSCC_XO_AON_CLK */ @@ -438,7 +437,7 @@ static const struct regmap_config gpu_cc_kaanapali_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data gpu_cc_kaanapali_driver_data = { +static const struct qcom_cc_driver_data gpu_cc_kaanapali_driver_data = { .alpha_plls = gpu_cc_kaanapali_plls, .num_alpha_plls = ARRAY_SIZE(gpu_cc_kaanapali_plls), .clk_cbcrs = gpu_cc_kaanapali_critical_cbcrs, diff --git a/drivers/clk/qcom/gpucc-milos.c b/drivers/clk/qcom/gpucc-milos.c index 4ee09879156e..1448d95cb1dc 100644 --- a/drivers/clk/qcom/gpucc-milos.c +++ b/drivers/clk/qcom/gpucc-milos.c @@ -500,7 +500,7 @@ static struct clk_alpha_pll *gpu_cc_milos_plls[] = { &gpu_cc_pll0, }; -static u32 gpu_cc_milos_critical_cbcrs[] = { +static const u32 gpu_cc_milos_critical_cbcrs[] = { 0x93a4, /* GPU_CC_CB_CLK */ 0x9008, /* GPU_CC_CXO_AON_CLK */ 0x9010, /* GPU_CC_DEMET_CLK */ @@ -518,7 +518,7 @@ static const struct regmap_config gpu_cc_milos_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data gpu_cc_milos_driver_data = { +static const struct qcom_cc_driver_data gpu_cc_milos_driver_data = { .alpha_plls = gpu_cc_milos_plls, .num_alpha_plls = ARRAY_SIZE(gpu_cc_milos_plls), .clk_cbcrs = gpu_cc_milos_critical_cbcrs, diff --git a/drivers/clk/qcom/gpucc-qcs615.c b/drivers/clk/qcom/gpucc-qcs615.c index ec6739c08425..91919cdb75ae 100644 --- a/drivers/clk/qcom/gpucc-qcs615.c +++ b/drivers/clk/qcom/gpucc-qcs615.c @@ -459,7 +459,7 @@ static struct clk_alpha_pll *gpu_cc_qcs615_plls[] = { &gpu_cc_pll1, }; -static u32 gpu_cc_qcs615_critical_cbcrs[] = { +static const u32 gpu_cc_qcs615_critical_cbcrs[] = { 0x1078, /* GPU_CC_AHB_CLK */ }; @@ -485,7 +485,7 @@ static void clk_qcs615_regs_crc_configure(struct device *dev, struct regmap *reg regmap_update_bits(regmap, 0x1024, 0x00800000, 0x00800000); } -static struct qcom_cc_driver_data gpu_cc_qcs615_driver_data = { +static const struct qcom_cc_driver_data gpu_cc_qcs615_driver_data = { .alpha_plls = gpu_cc_qcs615_plls, .num_alpha_plls = ARRAY_SIZE(gpu_cc_qcs615_plls), .clk_cbcrs = gpu_cc_qcs615_critical_cbcrs, diff --git a/drivers/clk/qcom/gpucc-sm8750.c b/drivers/clk/qcom/gpucc-sm8750.c new file mode 100644 index 000000000000..5d52c6d8b5e5 --- /dev/null +++ b/drivers/clk/qcom/gpucc-sm8750.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_BI_TCXO, + DT_GPLL0_OUT_MAIN, + DT_GPLL0_OUT_MAIN_DIV, +}; + +enum { + P_BI_TCXO, + P_GPLL0_OUT_MAIN, + P_GPLL0_OUT_MAIN_DIV, + P_GPU_CC_PLL0_OUT_EVEN, + P_GPU_CC_PLL0_OUT_MAIN, + P_GPU_CC_PLL0_OUT_ODD, +}; + +static const struct pll_vco taycan_elu_vco[] = { + { 249600000, 2500000000, 0 }, +}; + +static const struct alpha_pll_config gpu_cc_pll0_config = { + .l = 0x34, + .alpha = 0x1555, + .config_ctl_val = 0x19660387, + .config_ctl_hi_val = 0x098060a0, + .config_ctl_hi1_val = 0xb416cb20, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000002, +}; + +static struct clk_alpha_pll gpu_cc_pll0 = { + .offset = 0x0, + .config = &gpu_cc_pll0_config, + .vco_table = taycan_elu_vco, + .num_vco = ARRAY_SIZE(taycan_elu_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_ELU], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_taycan_elu_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gpu_cc_pll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gpu_cc_pll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_gpu_cc_pll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_gpu_cc_pll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_ELU], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_pll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_taycan_elu_ops, + }, +}; + +static const struct parent_map gpu_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_GPU_CC_PLL0_OUT_MAIN, 1 }, + { P_GPU_CC_PLL0_OUT_EVEN, 2 }, + { P_GPU_CC_PLL0_OUT_ODD, 3 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_GPLL0_OUT_MAIN_DIV, 6 }, +}; + +static const struct clk_parent_data gpu_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &gpu_cc_pll0.clkr.hw }, + { .hw = &gpu_cc_pll0_out_even.clkr.hw }, + { .hw = &gpu_cc_pll0.clkr.hw }, + { .index = DT_GPLL0_OUT_MAIN }, + { .index = DT_GPLL0_OUT_MAIN_DIV }, +}; + +static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(500000000, P_GPU_CC_PLL0_OUT_EVEN, 1, 0, 0), + F(650000000, P_GPU_CC_PLL0_OUT_EVEN, 1, 0, 0), + F(687500000, P_GPU_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_gmu_clk_src = { + .cmd_rcgr = 0x9318, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_1, + .freq_tbl = ftbl_gpu_cc_gmu_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_gmu_clk_src", + .parent_data = gpu_cc_parent_data_1, + .num_parents = ARRAY_SIZE(gpu_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gpu_cc_hub_clk_src[] = { + F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), + F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0), + F(400000000, P_GPLL0_OUT_MAIN, 1.5, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_hub_clk_src = { + .cmd_rcgr = 0x93ec, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_1, + .freq_tbl = ftbl_gpu_cc_hub_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_hub_clk_src", + .parent_data = gpu_cc_parent_data_1, + .num_parents = ARRAY_SIZE(gpu_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_regmap_div gpu_cc_hub_div_clk_src = { + .reg = 0x942c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_hub_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_hub_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch gpu_cc_ahb_clk = { + .halt_reg = 0x90bc, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x90bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_hub_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_accu_shift_clk = { + .halt_reg = 0x910c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x910c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_cx_accu_shift_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gmu_clk = { + .halt_reg = 0x90d4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x90d4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_cx_gmu_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_gmu_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_clk = { + .halt_reg = 0x90e4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x90e4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_cxo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_demet_clk = { + .halt_reg = 0x9010, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_demet_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_dpm_clk = { + .halt_reg = 0x9110, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9110, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_dpm_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_freq_measure_clk = { + .halt_reg = 0x900c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x900c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_freq_measure_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_accu_shift_clk = { + .halt_reg = 0x9070, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x9070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_gx_accu_shift_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_gmu_clk = { + .halt_reg = 0x9060, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x9060, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_gx_gmu_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_gmu_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_hlos1_vote_gpu_smmu_clk = { + .halt_reg = 0x7000, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_hlos1_vote_gpu_smmu_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_hub_aon_clk = { + .halt_reg = 0x93e8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x93e8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_hub_aon_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_hub_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_hub_cx_int_clk = { + .halt_reg = 0x90e8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x90e8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_hub_cx_int_clk", + .parent_hws = (const struct clk_hw*[]) { + &gpu_cc_hub_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_memnoc_gfx_clk = { + .halt_reg = 0x90f4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x90f4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpu_cc_memnoc_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc gpu_cc_cx_gdsc = { + .gdscr = 0x9080, + .gds_hw_ctrl = 0x9094, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x8, + .pd = { + .name = "gpu_cc_cx_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct clk_regmap *gpu_cc_sm8750_clocks[] = { + [GPU_CC_AHB_CLK] = &gpu_cc_ahb_clk.clkr, + [GPU_CC_CX_ACCU_SHIFT_CLK] = &gpu_cc_cx_accu_shift_clk.clkr, + [GPU_CC_CX_GMU_CLK] = &gpu_cc_cx_gmu_clk.clkr, + [GPU_CC_CXO_CLK] = &gpu_cc_cxo_clk.clkr, + [GPU_CC_DEMET_CLK] = &gpu_cc_demet_clk.clkr, + [GPU_CC_DPM_CLK] = &gpu_cc_dpm_clk.clkr, + [GPU_CC_FREQ_MEASURE_CLK] = &gpu_cc_freq_measure_clk.clkr, + [GPU_CC_GMU_CLK_SRC] = &gpu_cc_gmu_clk_src.clkr, + [GPU_CC_GX_ACCU_SHIFT_CLK] = &gpu_cc_gx_accu_shift_clk.clkr, + [GPU_CC_GX_GMU_CLK] = &gpu_cc_gx_gmu_clk.clkr, + [GPU_CC_HLOS1_VOTE_GPU_SMMU_CLK] = &gpu_cc_hlos1_vote_gpu_smmu_clk.clkr, + [GPU_CC_HUB_AON_CLK] = &gpu_cc_hub_aon_clk.clkr, + [GPU_CC_HUB_CLK_SRC] = &gpu_cc_hub_clk_src.clkr, + [GPU_CC_HUB_CX_INT_CLK] = &gpu_cc_hub_cx_int_clk.clkr, + [GPU_CC_HUB_DIV_CLK_SRC] = &gpu_cc_hub_div_clk_src.clkr, + [GPU_CC_MEMNOC_GFX_CLK] = &gpu_cc_memnoc_gfx_clk.clkr, + [GPU_CC_PLL0] = &gpu_cc_pll0.clkr, + [GPU_CC_PLL0_OUT_EVEN] = &gpu_cc_pll0_out_even.clkr, +}; + +static struct gdsc *gpu_cc_sm8750_gdscs[] = { + [GPU_CC_CX_GDSC] = &gpu_cc_cx_gdsc, +}; + +static const struct qcom_reset_map gpu_cc_sm8750_resets[] = { + [GPU_CC_GPU_CC_XO_BCR] = { 0x9000 }, + [GPU_CC_GPU_CC_GX_BCR] = { 0x905c }, + [GPU_CC_GPU_CC_CX_BCR] = { 0x907c }, + [GPU_CC_GPU_CC_GMU_BCR] = { 0x9314 }, + [GPU_CC_GPU_CC_CB_BCR] = { 0x93a0 }, + [GPU_CC_GPU_CC_FAST_HUB_BCR] = { 0x93e4 }, +}; + +static const struct regmap_config gpu_cc_sm8750_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9800, + .fast_io = true, +}; + +static struct clk_alpha_pll *gpu_cc_alpha_plls[] = { + &gpu_cc_pll0, +}; + +static u32 gpu_cc_sm8750_critical_cbcrs[] = { + 0x9004, /* GPU_CC_RSCC_XO_AON_CLK */ + 0x9008, /* GPU_CC_CXO_AON_CLK */ + 0x9064, /* GPU_CC_GX_AHB_FF_CLK */ + 0x90cc, /* GPU_CC_SLEEP_CLK */ + 0x93a4, /* GPU_CC_CB_CLK */ + 0x93a8, /* GPU_CC_RSCC_HUB_AON_CLK */ +}; + +static struct qcom_cc_driver_data gpu_cc_sm8750_driver_data = { + .alpha_plls = gpu_cc_alpha_plls, + .num_alpha_plls = ARRAY_SIZE(gpu_cc_alpha_plls), + .clk_cbcrs = gpu_cc_sm8750_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(gpu_cc_sm8750_critical_cbcrs), +}; + +static const struct qcom_cc_desc gpu_cc_sm8750_desc = { + .config = &gpu_cc_sm8750_regmap_config, + .clks = gpu_cc_sm8750_clocks, + .num_clks = ARRAY_SIZE(gpu_cc_sm8750_clocks), + .resets = gpu_cc_sm8750_resets, + .num_resets = ARRAY_SIZE(gpu_cc_sm8750_resets), + .gdscs = gpu_cc_sm8750_gdscs, + .num_gdscs = ARRAY_SIZE(gpu_cc_sm8750_gdscs), + .use_rpm = true, + .driver_data = &gpu_cc_sm8750_driver_data, +}; + +static const struct of_device_id gpu_cc_sm8750_match_table[] = { + { .compatible = "qcom,sm8750-gpucc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpu_cc_sm8750_match_table); + +static int gpu_cc_sm8750_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gpu_cc_sm8750_desc); +} + +static struct platform_driver gpu_cc_sm8750_driver = { + .probe = gpu_cc_sm8750_probe, + .driver = { + .name = "sm8750-gpucc", + .of_match_table = gpu_cc_sm8750_match_table, + }, +}; +module_platform_driver(gpu_cc_sm8750_driver); + +MODULE_DESCRIPTION("QTI GPU_CC SM8750 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/gxclkctl-kaanapali.c b/drivers/clk/qcom/gxclkctl-kaanapali.c index c209ce5fe4f0..40d856378a74 100644 --- a/drivers/clk/qcom/gxclkctl-kaanapali.c +++ b/drivers/clk/qcom/gxclkctl-kaanapali.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -52,7 +51,9 @@ static const struct qcom_cc_desc gx_clkctl_kaanapali_desc = { }; static const struct of_device_id gx_clkctl_kaanapali_match_table[] = { + { .compatible = "qcom,glymur-gxclkctl" }, { .compatible = "qcom,kaanapali-gxclkctl" }, + { .compatible = "qcom,sm8750-gxclkctl" }, { } }; MODULE_DEVICE_TABLE(of, gx_clkctl_kaanapali_match_table); diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c index dafbf5732048..5763e4df59a1 100644 --- a/drivers/clk/qcom/ipq-cmn-pll.c +++ b/drivers/clk/qcom/ipq-cmn-pll.c @@ -52,6 +52,8 @@ #include #include #include +#include +#include #define CMN_PLL_REFCLK_SRC_SELECTION 0x28 #define CMN_PLL_REFCLK_SRC_DIV GENMASK(9, 8) @@ -117,6 +119,18 @@ static const struct cmn_pll_fixed_output_clk ipq5018_output_clks[] = { { /* Sentinel */ } }; +static const struct cmn_pll_fixed_output_clk ipq6018_output_clks[] = { + CLK_PLL_OUTPUT(IPQ6018_BIAS_PLL_CC_CLK, "bias_pll_cc_clk", 300000000UL), + CLK_PLL_OUTPUT(IPQ6018_BIAS_PLL_NSS_NOC_CLK, "bias_pll_nss_noc_clk", 416500000UL), + { /* Sentinel */ } +}; + +static const struct cmn_pll_fixed_output_clk ipq8074_output_clks[] = { + CLK_PLL_OUTPUT(IPQ8074_BIAS_PLL_CC_CLK, "bias_pll_cc_clk", 300000000UL), + CLK_PLL_OUTPUT(IPQ8074_BIAS_PLL_NSS_NOC_CLK, "bias_pll_nss_noc_clk", 416500000UL), + { /* Sentinel */ } +}; + static const struct cmn_pll_fixed_output_clk ipq5424_output_clks[] = { CLK_PLL_OUTPUT(IPQ5424_XO_24MHZ_CLK, "xo-24mhz", 24000000UL), CLK_PLL_OUTPUT(IPQ5424_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), @@ -448,6 +462,8 @@ static const struct dev_pm_ops ipq_cmn_pll_pm_ops = { static const struct of_device_id ipq_cmn_pll_clk_ids[] = { { .compatible = "qcom,ipq5018-cmn-pll", .data = &ipq5018_output_clks }, { .compatible = "qcom,ipq5424-cmn-pll", .data = &ipq5424_output_clks }, + { .compatible = "qcom,ipq6018-cmn-pll", .data = &ipq6018_output_clks }, + { .compatible = "qcom,ipq8074-cmn-pll", .data = &ipq8074_output_clks }, { .compatible = "qcom,ipq9574-cmn-pll", .data = &ipq9574_output_clks }, { } }; diff --git a/drivers/clk/qcom/negcc-nord.c b/drivers/clk/qcom/negcc-nord.c new file mode 100644 index 000000000000..1aa24e2784e5 --- /dev/null +++ b/drivers/clk/qcom/negcc-nord.c @@ -0,0 +1,1987 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "clk-regmap-phy-mux.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_BI_TCXO, + DT_SLEEP_CLK, + DT_UFS_PHY_RX_SYMBOL_0_CLK, + DT_UFS_PHY_RX_SYMBOL_1_CLK, + DT_UFS_PHY_TX_SYMBOL_0_CLK, + DT_USB3_PHY_SEC_WRAPPER_NE_GCC_USB31_PIPE_CLK, + DT_USB3_PHY_WRAPPER_NE_GCC_USB31_PIPE_CLK, +}; + +enum { + P_BI_TCXO, + P_NE_GCC_GPLL0_OUT_EVEN, + P_NE_GCC_GPLL0_OUT_MAIN, + P_NE_GCC_GPLL2_OUT_MAIN, + P_SLEEP_CLK, + P_UFS_PHY_RX_SYMBOL_0_CLK, + P_UFS_PHY_RX_SYMBOL_1_CLK, + P_UFS_PHY_TX_SYMBOL_0_CLK, + P_USB3_PHY_SEC_WRAPPER_NE_GCC_USB31_PIPE_CLK, + P_USB3_PHY_WRAPPER_NE_GCC_USB31_PIPE_CLK, +}; + +static struct clk_alpha_pll ne_gcc_gpll0 = { + .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_gpll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_ne_gcc_gpll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv ne_gcc_gpll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_ne_gcc_gpll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_ne_gcc_gpll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_gpll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +static struct clk_alpha_pll ne_gcc_gpll2 = { + .offset = 0x2000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x0, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_gpll2", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static const struct parent_map ne_gcc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_NE_GCC_GPLL0_OUT_MAIN, 1 }, +}; + +static const struct clk_parent_data ne_gcc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &ne_gcc_gpll0.clkr.hw }, +}; + +static const struct parent_map ne_gcc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_NE_GCC_GPLL0_OUT_MAIN, 1 }, + { P_NE_GCC_GPLL0_OUT_EVEN, 5 }, +}; + +static const struct clk_parent_data ne_gcc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &ne_gcc_gpll0.clkr.hw }, + { .hw = &ne_gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map ne_gcc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_NE_GCC_GPLL0_OUT_MAIN, 1 }, + { P_NE_GCC_GPLL2_OUT_MAIN, 3 }, +}; + +static const struct clk_parent_data ne_gcc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &ne_gcc_gpll0.clkr.hw }, + { .hw = &ne_gcc_gpll2.clkr.hw }, +}; + +static const struct parent_map ne_gcc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_SLEEP_CLK, 5 }, +}; + +static const struct clk_parent_data ne_gcc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map ne_gcc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_NE_GCC_GPLL0_OUT_MAIN, 1 }, + { P_SLEEP_CLK, 5 }, +}; + +static const struct clk_parent_data ne_gcc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .hw = &ne_gcc_gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map ne_gcc_parent_map_5[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data ne_gcc_parent_data_5[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map ne_gcc_parent_map_6[] = { + { P_USB3_PHY_WRAPPER_NE_GCC_USB31_PIPE_CLK, 0 }, + { P_BI_TCXO, 2 }, +}; + +static const struct clk_parent_data ne_gcc_parent_data_6[] = { + { .index = DT_USB3_PHY_WRAPPER_NE_GCC_USB31_PIPE_CLK }, + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map ne_gcc_parent_map_7[] = { + { P_USB3_PHY_SEC_WRAPPER_NE_GCC_USB31_PIPE_CLK, 0 }, + { P_BI_TCXO, 2 }, +}; + +static const struct clk_parent_data ne_gcc_parent_data_7[] = { + { .index = DT_USB3_PHY_SEC_WRAPPER_NE_GCC_USB31_PIPE_CLK }, + { .index = DT_BI_TCXO }, +}; + +static struct clk_regmap_phy_mux ne_gcc_ufs_phy_rx_symbol_0_clk_src = { + .reg = 0x33068, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_rx_symbol_0_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_UFS_PHY_RX_SYMBOL_0_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux ne_gcc_ufs_phy_rx_symbol_1_clk_src = { + .reg = 0x330f0, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_rx_symbol_1_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_UFS_PHY_RX_SYMBOL_1_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux ne_gcc_ufs_phy_tx_symbol_0_clk_src = { + .reg = 0x33058, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_tx_symbol_0_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_UFS_PHY_TX_SYMBOL_0_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_mux ne_gcc_usb3_prim_phy_pipe_clk_src = { + .reg = 0x2a078, + .shift = 0, + .width = 2, + .parent_map = ne_gcc_parent_map_6, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb3_prim_phy_pipe_clk_src", + .parent_data = ne_gcc_parent_data_6, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_6), + .ops = &clk_regmap_mux_closest_ops, + }, + }, +}; + +static struct clk_regmap_mux ne_gcc_usb3_sec_phy_pipe_clk_src = { + .reg = 0x2c078, + .shift = 0, + .width = 2, + .parent_map = ne_gcc_parent_map_7, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb3_sec_phy_pipe_clk_src", + .parent_data = ne_gcc_parent_data_7, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_7), + .ops = &clk_regmap_mux_closest_ops, + }, + }, +}; + +static const struct freq_tbl ftbl_ne_gcc_gp1_clk_src[] = { + F(66666667, P_NE_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(100000000, P_NE_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(200000000, P_NE_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 ne_gcc_gp1_clk_src = { + .cmd_rcgr = 0x21004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_4, + .freq_tbl = ftbl_ne_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_gp1_clk_src", + .parent_data = ne_gcc_parent_data_4, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 ne_gcc_gp2_clk_src = { + .cmd_rcgr = 0x22004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_4, + .freq_tbl = ftbl_ne_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_gp2_clk_src", + .parent_data = ne_gcc_parent_data_4, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_ne_gcc_qupv3_wrap2_s0_clk_src[] = { + F(7372800, P_NE_GCC_GPLL0_OUT_MAIN, 1, 192, 15625), + F(14745600, P_NE_GCC_GPLL0_OUT_MAIN, 1, 384, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_NE_GCC_GPLL0_OUT_MAIN, 1, 768, 15625), + F(32000000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 4, 75), + F(48000000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 2, 25), + F(51200000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 32, 375), + F(64000000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 8, 75), + F(66666667, P_NE_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(75000000, P_NE_GCC_GPLL0_OUT_MAIN, 8, 0, 0), + F(80000000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 2, 15), + F(96000000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 4, 25), + F(100000000, P_NE_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 64, 375), + F(112000000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 14, 75), + F(117964800, P_NE_GCC_GPLL0_OUT_MAIN, 1, 3072, 15625), + F(120000000, P_NE_GCC_GPLL0_OUT_MAIN, 5, 0, 0), + { } +}; + +static struct clk_init_data ne_gcc_qupv3_wrap2_s0_clk_src_init = { + .name = "ne_gcc_qupv3_wrap2_s0_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 ne_gcc_qupv3_wrap2_s0_clk_src = { + .cmd_rcgr = 0x3816c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_qupv3_wrap2_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &ne_gcc_qupv3_wrap2_s0_clk_src_init, +}; + +static struct clk_init_data ne_gcc_qupv3_wrap2_s1_clk_src_init = { + .name = "ne_gcc_qupv3_wrap2_s1_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 ne_gcc_qupv3_wrap2_s1_clk_src = { + .cmd_rcgr = 0x382a8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_qupv3_wrap2_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &ne_gcc_qupv3_wrap2_s1_clk_src_init, +}; + +static const struct freq_tbl ftbl_ne_gcc_qupv3_wrap2_s2_clk_src[] = { + F(7372800, P_NE_GCC_GPLL0_OUT_MAIN, 1, 192, 15625), + F(14745600, P_NE_GCC_GPLL0_OUT_MAIN, 1, 384, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_NE_GCC_GPLL0_OUT_MAIN, 1, 768, 15625), + F(32000000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 4, 75), + F(48000000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 2, 25), + F(51200000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 32, 375), + F(64000000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 8, 75), + F(66666667, P_NE_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(75000000, P_NE_GCC_GPLL0_OUT_MAIN, 8, 0, 0), + F(80000000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 2, 15), + F(96000000, P_NE_GCC_GPLL0_OUT_MAIN, 1, 4, 25), + F(100000000, P_NE_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_init_data ne_gcc_qupv3_wrap2_s2_clk_src_init = { + .name = "ne_gcc_qupv3_wrap2_s2_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 ne_gcc_qupv3_wrap2_s2_clk_src = { + .cmd_rcgr = 0x383e4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_qupv3_wrap2_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &ne_gcc_qupv3_wrap2_s2_clk_src_init, +}; + +static struct clk_init_data ne_gcc_qupv3_wrap2_s3_clk_src_init = { + .name = "ne_gcc_qupv3_wrap2_s3_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 ne_gcc_qupv3_wrap2_s3_clk_src = { + .cmd_rcgr = 0x38520, + .mnd_width = 16, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_qupv3_wrap2_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &ne_gcc_qupv3_wrap2_s3_clk_src_init, +}; + +static struct clk_init_data ne_gcc_qupv3_wrap2_s4_clk_src_init = { + .name = "ne_gcc_qupv3_wrap2_s4_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 ne_gcc_qupv3_wrap2_s4_clk_src = { + .cmd_rcgr = 0x3865c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_qupv3_wrap2_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &ne_gcc_qupv3_wrap2_s4_clk_src_init, +}; + +static struct clk_init_data ne_gcc_qupv3_wrap2_s5_clk_src_init = { + .name = "ne_gcc_qupv3_wrap2_s5_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 ne_gcc_qupv3_wrap2_s5_clk_src = { + .cmd_rcgr = 0x38798, + .mnd_width = 16, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_qupv3_wrap2_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &ne_gcc_qupv3_wrap2_s5_clk_src_init, +}; + +static struct clk_init_data ne_gcc_qupv3_wrap2_s6_clk_src_init = { + .name = "ne_gcc_qupv3_wrap2_s6_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 ne_gcc_qupv3_wrap2_s6_clk_src = { + .cmd_rcgr = 0x388d4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_qupv3_wrap2_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &ne_gcc_qupv3_wrap2_s6_clk_src_init, +}; + +static const struct freq_tbl ftbl_ne_gcc_sdcc4_apps_clk_src[] = { + F(37500000, P_NE_GCC_GPLL0_OUT_MAIN, 16, 0, 0), + F(50000000, P_NE_GCC_GPLL0_OUT_MAIN, 12, 0, 0), + F(100000000, P_NE_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_rcg2 ne_gcc_sdcc4_apps_clk_src = { + .cmd_rcgr = 0x1801c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_sdcc4_apps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_sdcc4_apps_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_ne_gcc_ufs_phy_axi_clk_src[] = { + F(120000000, P_NE_GCC_GPLL0_OUT_MAIN, 5, 0, 0), + F(201500000, P_NE_GCC_GPLL2_OUT_MAIN, 4, 0, 0), + F(300000000, P_NE_GCC_GPLL0_OUT_MAIN, 2, 0, 0), + F(403000000, P_NE_GCC_GPLL2_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 ne_gcc_ufs_phy_axi_clk_src = { + .cmd_rcgr = 0x33034, + .mnd_width = 8, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_2, + .freq_tbl = ftbl_ne_gcc_ufs_phy_axi_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_axi_clk_src", + .parent_data = ne_gcc_parent_data_2, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 ne_gcc_ufs_phy_ice_core_clk_src = { + .cmd_rcgr = 0x3308c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_2, + .freq_tbl = ftbl_ne_gcc_ufs_phy_axi_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_ice_core_clk_src", + .parent_data = ne_gcc_parent_data_2, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_ne_gcc_ufs_phy_phy_aux_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 ne_gcc_ufs_phy_phy_aux_clk_src = { + .cmd_rcgr = 0x330c0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_5, + .freq_tbl = ftbl_ne_gcc_ufs_phy_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_phy_aux_clk_src", + .parent_data = ne_gcc_parent_data_5, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 ne_gcc_ufs_phy_unipro_core_clk_src = { + .cmd_rcgr = 0x330a4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_2, + .freq_tbl = ftbl_ne_gcc_ufs_phy_axi_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_unipro_core_clk_src", + .parent_data = ne_gcc_parent_data_2, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_ne_gcc_usb20_master_clk_src[] = { + F(75000000, P_NE_GCC_GPLL0_OUT_MAIN, 8, 0, 0), + F(120000000, P_NE_GCC_GPLL0_OUT_MAIN, 5, 0, 0), + { } +}; + +static struct clk_rcg2 ne_gcc_usb20_master_clk_src = { + .cmd_rcgr = 0x31030, + .mnd_width = 8, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_usb20_master_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb20_master_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 ne_gcc_usb20_mock_utmi_clk_src = { + .cmd_rcgr = 0x31048, + .mnd_width = 0, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_ufs_phy_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb20_mock_utmi_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_ne_gcc_usb31_prim_master_clk_src[] = { + F(85714286, P_NE_GCC_GPLL0_OUT_MAIN, 7, 0, 0), + F(133333333, P_NE_GCC_GPLL0_OUT_MAIN, 4.5, 0, 0), + F(200000000, P_NE_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + F(240000000, P_NE_GCC_GPLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 ne_gcc_usb31_prim_master_clk_src = { + .cmd_rcgr = 0x2a038, + .mnd_width = 8, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_1, + .freq_tbl = ftbl_ne_gcc_usb31_prim_master_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_prim_master_clk_src", + .parent_data = ne_gcc_parent_data_1, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 ne_gcc_usb31_prim_mock_utmi_clk_src = { + .cmd_rcgr = 0x2a050, + .mnd_width = 0, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_ufs_phy_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_prim_mock_utmi_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 ne_gcc_usb31_sec_master_clk_src = { + .cmd_rcgr = 0x2c038, + .mnd_width = 8, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_usb31_prim_master_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_sec_master_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 ne_gcc_usb31_sec_mock_utmi_clk_src = { + .cmd_rcgr = 0x2c050, + .mnd_width = 0, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_0, + .freq_tbl = ftbl_ne_gcc_ufs_phy_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_sec_mock_utmi_clk_src", + .parent_data = ne_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 ne_gcc_usb3_prim_phy_aux_clk_src = { + .cmd_rcgr = 0x2a07c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_3, + .freq_tbl = ftbl_ne_gcc_ufs_phy_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb3_prim_phy_aux_clk_src", + .parent_data = ne_gcc_parent_data_3, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 ne_gcc_usb3_sec_phy_aux_clk_src = { + .cmd_rcgr = 0x2c07c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = ne_gcc_parent_map_3, + .freq_tbl = ftbl_ne_gcc_ufs_phy_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb3_sec_phy_aux_clk_src", + .parent_data = ne_gcc_parent_data_3, + .num_parents = ARRAY_SIZE(ne_gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_regmap_div ne_gcc_usb20_mock_utmi_postdiv_clk_src = { + .reg = 0x31060, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb20_mock_utmi_postdiv_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb20_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div ne_gcc_usb31_prim_mock_utmi_postdiv_clk_src = { + .reg = 0x2a068, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_prim_mock_utmi_postdiv_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb31_prim_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div ne_gcc_usb31_sec_mock_utmi_postdiv_clk_src = { + .reg = 0x2c068, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_sec_mock_utmi_postdiv_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb31_sec_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch ne_gcc_aggre_noc_ufs_phy_axi_clk = { + .halt_reg = 0x330f4, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x330f4, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x330f4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_aggre_noc_ufs_phy_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_ufs_phy_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_aggre_noc_usb2_axi_clk = { + .halt_reg = 0x31068, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x31068, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x31068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_aggre_noc_usb2_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb20_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_aggre_noc_usb3_prim_axi_clk = { + .halt_reg = 0x2a098, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2a098, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2a098, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_aggre_noc_usb3_prim_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb31_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_aggre_noc_usb3_sec_axi_clk = { + .halt_reg = 0x2c098, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2c098, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2c098, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_aggre_noc_usb3_sec_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb31_sec_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_ahb2phy_clk = { + .halt_reg = 0x30004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x30004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x30004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ahb2phy_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_cnoc_usb2_axi_clk = { + .halt_reg = 0x31064, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x31064, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x31064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_cnoc_usb2_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb20_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_cnoc_usb3_prim_axi_clk = { + .halt_reg = 0x2a094, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2a094, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2a094, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_cnoc_usb3_prim_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb31_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_cnoc_usb3_sec_axi_clk = { + .halt_reg = 0x2c094, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2c094, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2c094, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_cnoc_usb3_sec_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb31_sec_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_frq_measure_ref_clk = { + .halt_reg = 0x20008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x20008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_frq_measure_ref_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_gp1_clk = { + .halt_reg = 0x21000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x21000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_gp1_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_gp1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_gp2_clk = { + .halt_reg = 0x22000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x22000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_gp2_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_gp2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_gpu_2_cfg_clk = { + .halt_reg = 0x34004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x34004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x34004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_gpu_2_cfg_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_gpu_2_gpll0_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(19), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_gpu_2_gpll0_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_gpu_2_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(20), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_gpu_2_gpll0_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_gpll0_out_even.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_gpu_2_hscnoc_gfx_clk = { + .halt_reg = 0x34014, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x34014, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x34014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_gpu_2_hscnoc_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_gpu_2_smmu_vote_clk = { + .halt_reg = 0x57028, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_gpu_2_smmu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_qupv3_wrap2_core_2x_clk = { + .halt_reg = 0x38020, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57008, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_qupv3_wrap2_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_qupv3_wrap2_core_clk = { + .halt_reg = 0x3800c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_qupv3_wrap2_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_qupv3_wrap2_m_ahb_clk = { + .halt_reg = 0x38004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x38004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(30), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_qupv3_wrap2_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_qupv3_wrap2_s0_clk = { + .halt_reg = 0x3815c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57008, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_qupv3_wrap2_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_qupv3_wrap2_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_qupv3_wrap2_s1_clk = { + .halt_reg = 0x38298, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57008, + .enable_mask = BIT(3), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_qupv3_wrap2_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_qupv3_wrap2_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_qupv3_wrap2_s2_clk = { + .halt_reg = 0x383d4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57008, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_qupv3_wrap2_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_qupv3_wrap2_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_qupv3_wrap2_s3_clk = { + .halt_reg = 0x38510, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57008, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_qupv3_wrap2_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_qupv3_wrap2_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_qupv3_wrap2_s4_clk = { + .halt_reg = 0x3864c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57008, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_qupv3_wrap2_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_qupv3_wrap2_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_qupv3_wrap2_s5_clk = { + .halt_reg = 0x38788, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57008, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_qupv3_wrap2_s5_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_qupv3_wrap2_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_qupv3_wrap2_s6_clk = { + .halt_reg = 0x388c4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57008, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_qupv3_wrap2_s6_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_qupv3_wrap2_s6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_qupv3_wrap2_s_ahb_clk = { + .halt_reg = 0x38008, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x38008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(31), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_qupv3_wrap2_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_sdcc4_apps_clk = { + .halt_reg = 0x18004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x18004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_sdcc4_apps_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_sdcc4_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_sdcc4_axi_clk = { + .halt_reg = 0x18014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x18014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_sdcc4_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_ufs_phy_ahb_clk = { + .halt_reg = 0x33028, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x33028, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x33028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_ufs_phy_axi_clk = { + .halt_reg = 0x33018, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x33018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x33018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_ufs_phy_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_ufs_phy_ice_core_clk = { + .halt_reg = 0x3307c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x3307c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x3307c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_ice_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_ufs_phy_ice_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_ufs_phy_phy_aux_clk = { + .halt_reg = 0x330bc, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x330bc, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x330bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_ufs_phy_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_ufs_phy_rx_symbol_0_clk = { + .halt_reg = 0x33030, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x33030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_rx_symbol_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_ufs_phy_rx_symbol_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_ufs_phy_rx_symbol_1_clk = { + .halt_reg = 0x330d8, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x330d8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_rx_symbol_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_ufs_phy_rx_symbol_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_ufs_phy_tx_symbol_0_clk = { + .halt_reg = 0x3302c, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x3302c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_tx_symbol_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_ufs_phy_tx_symbol_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_ufs_phy_unipro_core_clk = { + .halt_reg = 0x3306c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x3306c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x3306c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_ufs_phy_unipro_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_ufs_phy_unipro_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb20_master_clk = { + .halt_reg = 0x31018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x31018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb20_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb20_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb20_mock_utmi_clk = { + .halt_reg = 0x3102c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3102c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb20_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb20_mock_utmi_postdiv_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb20_sleep_clk = { + .halt_reg = 0x31028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x31028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb20_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb31_prim_atb_clk = { + .halt_reg = 0x2a018, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2a018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_prim_atb_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb31_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb31_prim_eud_ahb_clk = { + .halt_reg = 0x2a02c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2a02c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2a02c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_prim_eud_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb31_prim_master_clk = { + .halt_reg = 0x2a01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_prim_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb31_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb31_prim_mock_utmi_clk = { + .halt_reg = 0x2a034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_prim_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb31_prim_mock_utmi_postdiv_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb31_prim_sleep_clk = { + .halt_reg = 0x2a030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_prim_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb31_sec_atb_clk = { + .halt_reg = 0x2c018, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2c018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_sec_atb_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb31_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb31_sec_eud_ahb_clk = { + .halt_reg = 0x2c02c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2c02c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2c02c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_sec_eud_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb31_sec_master_clk = { + .halt_reg = 0x2c01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2c01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_sec_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb31_sec_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb31_sec_mock_utmi_clk = { + .halt_reg = 0x2c034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2c034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_sec_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb31_sec_mock_utmi_postdiv_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb31_sec_sleep_clk = { + .halt_reg = 0x2c030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2c030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb31_sec_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb3_prim_phy_aux_clk = { + .halt_reg = 0x2a06c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a06c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb3_prim_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb3_prim_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb3_prim_phy_com_aux_clk = { + .halt_reg = 0x2a070, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb3_prim_phy_com_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb3_prim_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb3_prim_phy_pipe_clk = { + .halt_reg = 0x2a074, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2a074, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2a074, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb3_prim_phy_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb3_prim_phy_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb3_sec_phy_aux_clk = { + .halt_reg = 0x2c06c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2c06c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb3_sec_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb3_sec_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb3_sec_phy_com_aux_clk = { + .halt_reg = 0x2c070, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2c070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb3_sec_phy_com_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb3_sec_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ne_gcc_usb3_sec_phy_pipe_clk = { + .halt_reg = 0x2c074, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2c074, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2c074, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "ne_gcc_usb3_sec_phy_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &ne_gcc_usb3_sec_phy_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc ne_gcc_ufs_mem_phy_gdsc = { + .gdscr = 0x32000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "ne_gcc_ufs_mem_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc ne_gcc_ufs_phy_gdsc = { + .gdscr = 0x33004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ne_gcc_ufs_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc ne_gcc_usb20_prim_gdsc = { + .gdscr = 0x31004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ne_gcc_usb20_prim_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc ne_gcc_usb31_prim_gdsc = { + .gdscr = 0x2a004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ne_gcc_usb31_prim_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc ne_gcc_usb31_sec_gdsc = { + .gdscr = 0x2c004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "ne_gcc_usb31_sec_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc ne_gcc_usb3_phy_gdsc = { + .gdscr = 0x2b00c, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "ne_gcc_usb3_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc ne_gcc_usb3_sec_phy_gdsc = { + .gdscr = 0x2d00c, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "ne_gcc_usb3_sec_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *ne_gcc_nord_clocks[] = { + [NE_GCC_AGGRE_NOC_UFS_PHY_AXI_CLK] = &ne_gcc_aggre_noc_ufs_phy_axi_clk.clkr, + [NE_GCC_AGGRE_NOC_USB2_AXI_CLK] = &ne_gcc_aggre_noc_usb2_axi_clk.clkr, + [NE_GCC_AGGRE_NOC_USB3_PRIM_AXI_CLK] = &ne_gcc_aggre_noc_usb3_prim_axi_clk.clkr, + [NE_GCC_AGGRE_NOC_USB3_SEC_AXI_CLK] = &ne_gcc_aggre_noc_usb3_sec_axi_clk.clkr, + [NE_GCC_AHB2PHY_CLK] = &ne_gcc_ahb2phy_clk.clkr, + [NE_GCC_CNOC_USB2_AXI_CLK] = &ne_gcc_cnoc_usb2_axi_clk.clkr, + [NE_GCC_CNOC_USB3_PRIM_AXI_CLK] = &ne_gcc_cnoc_usb3_prim_axi_clk.clkr, + [NE_GCC_CNOC_USB3_SEC_AXI_CLK] = &ne_gcc_cnoc_usb3_sec_axi_clk.clkr, + [NE_GCC_FRQ_MEASURE_REF_CLK] = &ne_gcc_frq_measure_ref_clk.clkr, + [NE_GCC_GP1_CLK] = &ne_gcc_gp1_clk.clkr, + [NE_GCC_GP1_CLK_SRC] = &ne_gcc_gp1_clk_src.clkr, + [NE_GCC_GP2_CLK] = &ne_gcc_gp2_clk.clkr, + [NE_GCC_GP2_CLK_SRC] = &ne_gcc_gp2_clk_src.clkr, + [NE_GCC_GPLL0] = &ne_gcc_gpll0.clkr, + [NE_GCC_GPLL0_OUT_EVEN] = &ne_gcc_gpll0_out_even.clkr, + [NE_GCC_GPLL2] = &ne_gcc_gpll2.clkr, + [NE_GCC_GPU_2_CFG_CLK] = &ne_gcc_gpu_2_cfg_clk.clkr, + [NE_GCC_GPU_2_GPLL0_CLK_SRC] = &ne_gcc_gpu_2_gpll0_clk_src.clkr, + [NE_GCC_GPU_2_GPLL0_DIV_CLK_SRC] = &ne_gcc_gpu_2_gpll0_div_clk_src.clkr, + [NE_GCC_GPU_2_HSCNOC_GFX_CLK] = &ne_gcc_gpu_2_hscnoc_gfx_clk.clkr, + [NE_GCC_GPU_2_SMMU_VOTE_CLK] = &ne_gcc_gpu_2_smmu_vote_clk.clkr, + [NE_GCC_QUPV3_WRAP2_CORE_2X_CLK] = &ne_gcc_qupv3_wrap2_core_2x_clk.clkr, + [NE_GCC_QUPV3_WRAP2_CORE_CLK] = &ne_gcc_qupv3_wrap2_core_clk.clkr, + [NE_GCC_QUPV3_WRAP2_M_AHB_CLK] = &ne_gcc_qupv3_wrap2_m_ahb_clk.clkr, + [NE_GCC_QUPV3_WRAP2_S0_CLK] = &ne_gcc_qupv3_wrap2_s0_clk.clkr, + [NE_GCC_QUPV3_WRAP2_S0_CLK_SRC] = &ne_gcc_qupv3_wrap2_s0_clk_src.clkr, + [NE_GCC_QUPV3_WRAP2_S1_CLK] = &ne_gcc_qupv3_wrap2_s1_clk.clkr, + [NE_GCC_QUPV3_WRAP2_S1_CLK_SRC] = &ne_gcc_qupv3_wrap2_s1_clk_src.clkr, + [NE_GCC_QUPV3_WRAP2_S2_CLK] = &ne_gcc_qupv3_wrap2_s2_clk.clkr, + [NE_GCC_QUPV3_WRAP2_S2_CLK_SRC] = &ne_gcc_qupv3_wrap2_s2_clk_src.clkr, + [NE_GCC_QUPV3_WRAP2_S3_CLK] = &ne_gcc_qupv3_wrap2_s3_clk.clkr, + [NE_GCC_QUPV3_WRAP2_S3_CLK_SRC] = &ne_gcc_qupv3_wrap2_s3_clk_src.clkr, + [NE_GCC_QUPV3_WRAP2_S4_CLK] = &ne_gcc_qupv3_wrap2_s4_clk.clkr, + [NE_GCC_QUPV3_WRAP2_S4_CLK_SRC] = &ne_gcc_qupv3_wrap2_s4_clk_src.clkr, + [NE_GCC_QUPV3_WRAP2_S5_CLK] = &ne_gcc_qupv3_wrap2_s5_clk.clkr, + [NE_GCC_QUPV3_WRAP2_S5_CLK_SRC] = &ne_gcc_qupv3_wrap2_s5_clk_src.clkr, + [NE_GCC_QUPV3_WRAP2_S6_CLK] = &ne_gcc_qupv3_wrap2_s6_clk.clkr, + [NE_GCC_QUPV3_WRAP2_S6_CLK_SRC] = &ne_gcc_qupv3_wrap2_s6_clk_src.clkr, + [NE_GCC_QUPV3_WRAP2_S_AHB_CLK] = &ne_gcc_qupv3_wrap2_s_ahb_clk.clkr, + [NE_GCC_SDCC4_APPS_CLK] = &ne_gcc_sdcc4_apps_clk.clkr, + [NE_GCC_SDCC4_APPS_CLK_SRC] = &ne_gcc_sdcc4_apps_clk_src.clkr, + [NE_GCC_SDCC4_AXI_CLK] = &ne_gcc_sdcc4_axi_clk.clkr, + [NE_GCC_UFS_PHY_AHB_CLK] = &ne_gcc_ufs_phy_ahb_clk.clkr, + [NE_GCC_UFS_PHY_AXI_CLK] = &ne_gcc_ufs_phy_axi_clk.clkr, + [NE_GCC_UFS_PHY_AXI_CLK_SRC] = &ne_gcc_ufs_phy_axi_clk_src.clkr, + [NE_GCC_UFS_PHY_ICE_CORE_CLK] = &ne_gcc_ufs_phy_ice_core_clk.clkr, + [NE_GCC_UFS_PHY_ICE_CORE_CLK_SRC] = &ne_gcc_ufs_phy_ice_core_clk_src.clkr, + [NE_GCC_UFS_PHY_PHY_AUX_CLK] = &ne_gcc_ufs_phy_phy_aux_clk.clkr, + [NE_GCC_UFS_PHY_PHY_AUX_CLK_SRC] = &ne_gcc_ufs_phy_phy_aux_clk_src.clkr, + [NE_GCC_UFS_PHY_RX_SYMBOL_0_CLK] = &ne_gcc_ufs_phy_rx_symbol_0_clk.clkr, + [NE_GCC_UFS_PHY_RX_SYMBOL_0_CLK_SRC] = &ne_gcc_ufs_phy_rx_symbol_0_clk_src.clkr, + [NE_GCC_UFS_PHY_RX_SYMBOL_1_CLK] = &ne_gcc_ufs_phy_rx_symbol_1_clk.clkr, + [NE_GCC_UFS_PHY_RX_SYMBOL_1_CLK_SRC] = &ne_gcc_ufs_phy_rx_symbol_1_clk_src.clkr, + [NE_GCC_UFS_PHY_TX_SYMBOL_0_CLK] = &ne_gcc_ufs_phy_tx_symbol_0_clk.clkr, + [NE_GCC_UFS_PHY_TX_SYMBOL_0_CLK_SRC] = &ne_gcc_ufs_phy_tx_symbol_0_clk_src.clkr, + [NE_GCC_UFS_PHY_UNIPRO_CORE_CLK] = &ne_gcc_ufs_phy_unipro_core_clk.clkr, + [NE_GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC] = &ne_gcc_ufs_phy_unipro_core_clk_src.clkr, + [NE_GCC_USB20_MASTER_CLK] = &ne_gcc_usb20_master_clk.clkr, + [NE_GCC_USB20_MASTER_CLK_SRC] = &ne_gcc_usb20_master_clk_src.clkr, + [NE_GCC_USB20_MOCK_UTMI_CLK] = &ne_gcc_usb20_mock_utmi_clk.clkr, + [NE_GCC_USB20_MOCK_UTMI_CLK_SRC] = &ne_gcc_usb20_mock_utmi_clk_src.clkr, + [NE_GCC_USB20_MOCK_UTMI_POSTDIV_CLK_SRC] = &ne_gcc_usb20_mock_utmi_postdiv_clk_src.clkr, + [NE_GCC_USB20_SLEEP_CLK] = &ne_gcc_usb20_sleep_clk.clkr, + [NE_GCC_USB31_PRIM_ATB_CLK] = &ne_gcc_usb31_prim_atb_clk.clkr, + [NE_GCC_USB31_PRIM_EUD_AHB_CLK] = &ne_gcc_usb31_prim_eud_ahb_clk.clkr, + [NE_GCC_USB31_PRIM_MASTER_CLK] = &ne_gcc_usb31_prim_master_clk.clkr, + [NE_GCC_USB31_PRIM_MASTER_CLK_SRC] = &ne_gcc_usb31_prim_master_clk_src.clkr, + [NE_GCC_USB31_PRIM_MOCK_UTMI_CLK] = &ne_gcc_usb31_prim_mock_utmi_clk.clkr, + [NE_GCC_USB31_PRIM_MOCK_UTMI_CLK_SRC] = &ne_gcc_usb31_prim_mock_utmi_clk_src.clkr, + [NE_GCC_USB31_PRIM_MOCK_UTMI_POSTDIV_CLK_SRC] = + &ne_gcc_usb31_prim_mock_utmi_postdiv_clk_src.clkr, + [NE_GCC_USB31_PRIM_SLEEP_CLK] = &ne_gcc_usb31_prim_sleep_clk.clkr, + [NE_GCC_USB31_SEC_ATB_CLK] = &ne_gcc_usb31_sec_atb_clk.clkr, + [NE_GCC_USB31_SEC_EUD_AHB_CLK] = &ne_gcc_usb31_sec_eud_ahb_clk.clkr, + [NE_GCC_USB31_SEC_MASTER_CLK] = &ne_gcc_usb31_sec_master_clk.clkr, + [NE_GCC_USB31_SEC_MASTER_CLK_SRC] = &ne_gcc_usb31_sec_master_clk_src.clkr, + [NE_GCC_USB31_SEC_MOCK_UTMI_CLK] = &ne_gcc_usb31_sec_mock_utmi_clk.clkr, + [NE_GCC_USB31_SEC_MOCK_UTMI_CLK_SRC] = &ne_gcc_usb31_sec_mock_utmi_clk_src.clkr, + [NE_GCC_USB31_SEC_MOCK_UTMI_POSTDIV_CLK_SRC] = + &ne_gcc_usb31_sec_mock_utmi_postdiv_clk_src.clkr, + [NE_GCC_USB31_SEC_SLEEP_CLK] = &ne_gcc_usb31_sec_sleep_clk.clkr, + [NE_GCC_USB3_PRIM_PHY_AUX_CLK] = &ne_gcc_usb3_prim_phy_aux_clk.clkr, + [NE_GCC_USB3_PRIM_PHY_AUX_CLK_SRC] = &ne_gcc_usb3_prim_phy_aux_clk_src.clkr, + [NE_GCC_USB3_PRIM_PHY_COM_AUX_CLK] = &ne_gcc_usb3_prim_phy_com_aux_clk.clkr, + [NE_GCC_USB3_PRIM_PHY_PIPE_CLK] = &ne_gcc_usb3_prim_phy_pipe_clk.clkr, + [NE_GCC_USB3_PRIM_PHY_PIPE_CLK_SRC] = &ne_gcc_usb3_prim_phy_pipe_clk_src.clkr, + [NE_GCC_USB3_SEC_PHY_AUX_CLK] = &ne_gcc_usb3_sec_phy_aux_clk.clkr, + [NE_GCC_USB3_SEC_PHY_AUX_CLK_SRC] = &ne_gcc_usb3_sec_phy_aux_clk_src.clkr, + [NE_GCC_USB3_SEC_PHY_COM_AUX_CLK] = &ne_gcc_usb3_sec_phy_com_aux_clk.clkr, + [NE_GCC_USB3_SEC_PHY_PIPE_CLK] = &ne_gcc_usb3_sec_phy_pipe_clk.clkr, + [NE_GCC_USB3_SEC_PHY_PIPE_CLK_SRC] = &ne_gcc_usb3_sec_phy_pipe_clk_src.clkr, +}; + +static struct gdsc *ne_gcc_nord_gdscs[] = { + [NE_GCC_UFS_MEM_PHY_GDSC] = &ne_gcc_ufs_mem_phy_gdsc, + [NE_GCC_UFS_PHY_GDSC] = &ne_gcc_ufs_phy_gdsc, + [NE_GCC_USB20_PRIM_GDSC] = &ne_gcc_usb20_prim_gdsc, + [NE_GCC_USB31_PRIM_GDSC] = &ne_gcc_usb31_prim_gdsc, + [NE_GCC_USB31_SEC_GDSC] = &ne_gcc_usb31_sec_gdsc, + [NE_GCC_USB3_PHY_GDSC] = &ne_gcc_usb3_phy_gdsc, + [NE_GCC_USB3_SEC_PHY_GDSC] = &ne_gcc_usb3_sec_phy_gdsc, +}; + +static const struct qcom_reset_map ne_gcc_nord_resets[] = { + [NE_GCC_GPU_2_BCR] = { 0x34000 }, + [NE_GCC_QUPV3_WRAPPER_2_BCR] = { 0x38000 }, + [NE_GCC_SDCC4_BCR] = { 0x18000 }, + [NE_GCC_UFS_PHY_BCR] = { 0x33000 }, + [NE_GCC_USB20_PRIM_BCR] = { 0x31000 }, + [NE_GCC_USB31_PRIM_BCR] = { 0x2a000 }, + [NE_GCC_USB31_SEC_BCR] = { 0x2c000 }, + [NE_GCC_USB3_DP_PHY_PRIM_BCR] = { 0x2b008 }, + [NE_GCC_USB3_DP_PHY_SEC_BCR] = { 0x2d008 }, + [NE_GCC_USB3_PHY_PRIM_BCR] = { 0x2b000 }, + [NE_GCC_USB3_PHY_SEC_BCR] = { 0x2d000 }, + [NE_GCC_USB3PHY_PHY_PRIM_BCR] = { 0x2b004 }, + [NE_GCC_USB3PHY_PHY_SEC_BCR] = { 0x2d004 }, +}; + +static const struct clk_rcg_dfs_data ne_gcc_nord_dfs_clocks[] = { + DEFINE_RCG_DFS(ne_gcc_qupv3_wrap2_s0_clk_src), + DEFINE_RCG_DFS(ne_gcc_qupv3_wrap2_s1_clk_src), + DEFINE_RCG_DFS(ne_gcc_qupv3_wrap2_s2_clk_src), + DEFINE_RCG_DFS(ne_gcc_qupv3_wrap2_s3_clk_src), + DEFINE_RCG_DFS(ne_gcc_qupv3_wrap2_s4_clk_src), + DEFINE_RCG_DFS(ne_gcc_qupv3_wrap2_s5_clk_src), + DEFINE_RCG_DFS(ne_gcc_qupv3_wrap2_s6_clk_src), +}; + +static const struct regmap_config ne_gcc_nord_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf41f0, + .fast_io = true, +}; + +static void clk_nord_regs_configure(struct device *dev, struct regmap *regmap) +{ + /* FORCE_MEM_CORE_ON for ne_gcc_ufs_phy_ice_core_clk and ne_gcc_ufs_phy_axi_clk */ + qcom_branch_set_force_mem_core(regmap, ne_gcc_ufs_phy_ice_core_clk, true); + qcom_branch_set_force_mem_core(regmap, ne_gcc_ufs_phy_axi_clk, true); +} + +static struct qcom_cc_driver_data ne_gcc_nord_driver_data = { + .dfs_rcgs = ne_gcc_nord_dfs_clocks, + .num_dfs_rcgs = ARRAY_SIZE(ne_gcc_nord_dfs_clocks), + .clk_regs_configure = clk_nord_regs_configure, +}; + +static const struct qcom_cc_desc ne_gcc_nord_desc = { + .config = &ne_gcc_nord_regmap_config, + .clks = ne_gcc_nord_clocks, + .num_clks = ARRAY_SIZE(ne_gcc_nord_clocks), + .resets = ne_gcc_nord_resets, + .num_resets = ARRAY_SIZE(ne_gcc_nord_resets), + .gdscs = ne_gcc_nord_gdscs, + .num_gdscs = ARRAY_SIZE(ne_gcc_nord_gdscs), + .driver_data = &ne_gcc_nord_driver_data, +}; + +static const struct of_device_id ne_gcc_nord_match_table[] = { + { .compatible = "qcom,nord-negcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, ne_gcc_nord_match_table); + +static int ne_gcc_nord_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &ne_gcc_nord_desc); +} + +static struct platform_driver ne_gcc_nord_driver = { + .probe = ne_gcc_nord_probe, + .driver = { + .name = "negcc-nord", + .of_match_table = ne_gcc_nord_match_table, + }, +}; + +module_platform_driver(ne_gcc_nord_driver); + +MODULE_DESCRIPTION("QTI NEGCC NORD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/nwgcc-nord.c b/drivers/clk/qcom/nwgcc-nord.c new file mode 100644 index 000000000000..163ab63c872b --- /dev/null +++ b/drivers/clk/qcom/nwgcc-nord.c @@ -0,0 +1,688 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "common.h" +#include "reset.h" + +enum { + DT_BI_TCXO, + DT_SLEEP_CLK, +}; + +enum { + P_BI_TCXO, + P_NW_GCC_GPLL0_OUT_EVEN, + P_NW_GCC_GPLL0_OUT_MAIN, + P_SLEEP_CLK, +}; + +static struct clk_alpha_pll nw_gcc_gpll0 = { + .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gpll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_nw_gcc_gpll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv nw_gcc_gpll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_nw_gcc_gpll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_nw_gcc_gpll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gpll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &nw_gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +static const struct parent_map nw_gcc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_NW_GCC_GPLL0_OUT_MAIN, 1 }, + { P_SLEEP_CLK, 5 }, + { P_NW_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data nw_gcc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &nw_gcc_gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, + { .hw = &nw_gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct freq_tbl ftbl_nw_gcc_gp1_clk_src[] = { + F(60000000, P_NW_GCC_GPLL0_OUT_MAIN, 10, 0, 0), + F(100000000, P_NW_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(200000000, P_NW_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 nw_gcc_gp1_clk_src = { + .cmd_rcgr = 0x20004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = nw_gcc_parent_map_0, + .freq_tbl = ftbl_nw_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gp1_clk_src", + .parent_data = nw_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(nw_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 nw_gcc_gp2_clk_src = { + .cmd_rcgr = 0x21004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = nw_gcc_parent_map_0, + .freq_tbl = ftbl_nw_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gp2_clk_src", + .parent_data = nw_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(nw_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_branch nw_gcc_acmu_mux_clk = { + .halt_reg = 0x1f01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1f01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_acmu_mux_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_camera_hf_axi_clk = { + .halt_reg = 0x16008, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x16008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x16008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_camera_hf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_camera_sf_axi_clk = { + .halt_reg = 0x1601c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x1601c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1601c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_camera_sf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_camera_trig_clk = { + .halt_reg = 0x16034, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x16034, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x16034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_camera_trig_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_disp_0_hf_axi_clk = { + .halt_reg = 0x18008, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x18008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x18008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_disp_0_hf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_disp_0_trig_clk = { + .halt_reg = 0x1801c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x1801c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1801c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_disp_0_trig_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_disp_1_hf_axi_clk = { + .halt_reg = 0x19008, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x19008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x19008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_disp_1_hf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_disp_1_trig_clk = { + .halt_reg = 0x1901c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x1901c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1901c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_disp_1_trig_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_dprx0_axi_hf_clk = { + .halt_reg = 0x29004, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x29004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x29004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_dprx0_axi_hf_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_dprx1_axi_hf_clk = { + .halt_reg = 0x2a004, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x2a004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2a004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_dprx1_axi_hf_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_eva_axi0_clk = { + .halt_reg = 0x1b008, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x1b008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1b008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_eva_axi0_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_eva_axi0c_clk = { + .halt_reg = 0x1b01c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x1b01c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1b01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_eva_axi0c_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_eva_trig_clk = { + .halt_reg = 0x1b028, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x1b028, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1b028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_eva_trig_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_frq_measure_ref_clk = { + .halt_reg = 0x1f008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1f008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_frq_measure_ref_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_gp1_clk = { + .halt_reg = 0x20000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x20000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gp1_clk", + .parent_hws = (const struct clk_hw*[]) { + &nw_gcc_gp1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_gp2_clk = { + .halt_reg = 0x21000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x21000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gp2_clk", + .parent_hws = (const struct clk_hw*[]) { + &nw_gcc_gp2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_gpu_2_gpll0_clk_src = { + .halt_reg = 0x24150, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x24150, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x76000, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gpu_2_gpll0_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &nw_gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_gpu_2_gpll0_div_clk_src = { + .halt_reg = 0x24158, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x24158, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x76000, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gpu_2_gpll0_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &nw_gcc_gpll0_out_even.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_gpu_2_hscnoc_gfx_clk = { + .halt_reg = 0x2400c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2400c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2400c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gpu_2_hscnoc_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_gpu_gpll0_clk_src = { + .halt_reg = 0x23150, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23150, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x76000, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gpu_gpll0_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &nw_gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_gpu_gpll0_div_clk_src = { + .halt_reg = 0x23158, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23158, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x76000, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gpu_gpll0_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &nw_gcc_gpll0_out_even.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_gpu_hscnoc_gfx_clk = { + .halt_reg = 0x2300c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x2300c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2300c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gpu_hscnoc_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_gpu_smmu_vote_clk = { + .halt_reg = 0x86038, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x86038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_gpu_smmu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_hscnoc_gpu_2_axi_clk = { + .halt_reg = 0x24160, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x24160, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x24160, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_hscnoc_gpu_2_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_hscnoc_gpu_axi_clk = { + .halt_reg = 0x23160, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x23160, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x23160, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_hscnoc_gpu_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_mmu_1_tcu_vote_clk = { + .halt_reg = 0x86040, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x86040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_mmu_1_tcu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_video_axi0_clk = { + .halt_reg = 0x1a008, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x1a008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1a008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_video_axi0_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_video_axi0c_clk = { + .halt_reg = 0x1a01c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x1a01c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1a01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_video_axi0c_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch nw_gcc_video_axi1_clk = { + .halt_reg = 0x1a030, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x1a030, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x1a030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "nw_gcc_video_axi1_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *nw_gcc_nord_clocks[] = { + [NW_GCC_ACMU_MUX_CLK] = &nw_gcc_acmu_mux_clk.clkr, + [NW_GCC_CAMERA_HF_AXI_CLK] = &nw_gcc_camera_hf_axi_clk.clkr, + [NW_GCC_CAMERA_SF_AXI_CLK] = &nw_gcc_camera_sf_axi_clk.clkr, + [NW_GCC_CAMERA_TRIG_CLK] = &nw_gcc_camera_trig_clk.clkr, + [NW_GCC_DISP_0_HF_AXI_CLK] = &nw_gcc_disp_0_hf_axi_clk.clkr, + [NW_GCC_DISP_0_TRIG_CLK] = &nw_gcc_disp_0_trig_clk.clkr, + [NW_GCC_DISP_1_HF_AXI_CLK] = &nw_gcc_disp_1_hf_axi_clk.clkr, + [NW_GCC_DISP_1_TRIG_CLK] = &nw_gcc_disp_1_trig_clk.clkr, + [NW_GCC_DPRX0_AXI_HF_CLK] = &nw_gcc_dprx0_axi_hf_clk.clkr, + [NW_GCC_DPRX1_AXI_HF_CLK] = &nw_gcc_dprx1_axi_hf_clk.clkr, + [NW_GCC_EVA_AXI0_CLK] = &nw_gcc_eva_axi0_clk.clkr, + [NW_GCC_EVA_AXI0C_CLK] = &nw_gcc_eva_axi0c_clk.clkr, + [NW_GCC_EVA_TRIG_CLK] = &nw_gcc_eva_trig_clk.clkr, + [NW_GCC_FRQ_MEASURE_REF_CLK] = &nw_gcc_frq_measure_ref_clk.clkr, + [NW_GCC_GP1_CLK] = &nw_gcc_gp1_clk.clkr, + [NW_GCC_GP1_CLK_SRC] = &nw_gcc_gp1_clk_src.clkr, + [NW_GCC_GP2_CLK] = &nw_gcc_gp2_clk.clkr, + [NW_GCC_GP2_CLK_SRC] = &nw_gcc_gp2_clk_src.clkr, + [NW_GCC_GPLL0] = &nw_gcc_gpll0.clkr, + [NW_GCC_GPLL0_OUT_EVEN] = &nw_gcc_gpll0_out_even.clkr, + [NW_GCC_GPU_2_GPLL0_CLK_SRC] = &nw_gcc_gpu_2_gpll0_clk_src.clkr, + [NW_GCC_GPU_2_GPLL0_DIV_CLK_SRC] = &nw_gcc_gpu_2_gpll0_div_clk_src.clkr, + [NW_GCC_GPU_2_HSCNOC_GFX_CLK] = &nw_gcc_gpu_2_hscnoc_gfx_clk.clkr, + [NW_GCC_GPU_GPLL0_CLK_SRC] = &nw_gcc_gpu_gpll0_clk_src.clkr, + [NW_GCC_GPU_GPLL0_DIV_CLK_SRC] = &nw_gcc_gpu_gpll0_div_clk_src.clkr, + [NW_GCC_GPU_HSCNOC_GFX_CLK] = &nw_gcc_gpu_hscnoc_gfx_clk.clkr, + [NW_GCC_GPU_SMMU_VOTE_CLK] = &nw_gcc_gpu_smmu_vote_clk.clkr, + [NW_GCC_HSCNOC_GPU_2_AXI_CLK] = &nw_gcc_hscnoc_gpu_2_axi_clk.clkr, + [NW_GCC_HSCNOC_GPU_AXI_CLK] = &nw_gcc_hscnoc_gpu_axi_clk.clkr, + [NW_GCC_MMU_1_TCU_VOTE_CLK] = &nw_gcc_mmu_1_tcu_vote_clk.clkr, + [NW_GCC_VIDEO_AXI0_CLK] = &nw_gcc_video_axi0_clk.clkr, + [NW_GCC_VIDEO_AXI0C_CLK] = &nw_gcc_video_axi0c_clk.clkr, + [NW_GCC_VIDEO_AXI1_CLK] = &nw_gcc_video_axi1_clk.clkr, +}; + +static const struct qcom_reset_map nw_gcc_nord_resets[] = { + [NW_GCC_CAMERA_BCR] = { 0x16000 }, + [NW_GCC_DISPLAY_0_BCR] = { 0x18000 }, + [NW_GCC_DISPLAY_1_BCR] = { 0x19000 }, + [NW_GCC_DPRX0_BCR] = { 0x29000 }, + [NW_GCC_DPRX1_BCR] = { 0x2a000 }, + [NW_GCC_EVA_BCR] = { 0x1b000 }, + [NW_GCC_GPU_2_BCR] = { 0x24000 }, + [NW_GCC_GPU_BCR] = { 0x23000 }, + [NW_GCC_VIDEO_BCR] = { 0x1a000 }, +}; + +static u32 nw_gcc_nord_critical_cbcrs[] = { + 0x16004, /* NW_GCC_CAMERA_AHB_CLK */ + 0x16030, /* NW_GCC_CAMERA_XO_CLK */ + 0x18004, /* NW_GCC_DISP_0_AHB_CLK */ + 0x19004, /* NW_GCC_DISP_1_AHB_CLK */ + 0x29018, /* NW_GCC_DPRX0_CFG_AHB_CLK */ + 0x2a018, /* NW_GCC_DPRX1_CFG_AHB_CLK */ + 0x1b004, /* NW_GCC_EVA_AHB_CLK */ + 0x1b024, /* NW_GCC_EVA_XO_CLK */ + 0x23004, /* NW_GCC_GPU_CFG_AHB_CLK */ + 0x24004, /* NW_GCC_GPU_2_CFG_AHB_CLK */ + 0x1a004, /* NW_GCC_VIDEO_AHB_CLK */ + 0x1a044, /* NW_GCC_VIDEO_XO_CLK */ +}; + +static struct qcom_cc_driver_data nw_gcc_nord_driver_data = { + .clk_cbcrs = nw_gcc_nord_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(nw_gcc_nord_critical_cbcrs), +}; + +static const struct regmap_config nw_gcc_nord_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf41f0, + .fast_io = true, +}; + +static const struct qcom_cc_desc nw_gcc_nord_desc = { + .config = &nw_gcc_nord_regmap_config, + .clks = nw_gcc_nord_clocks, + .num_clks = ARRAY_SIZE(nw_gcc_nord_clocks), + .resets = nw_gcc_nord_resets, + .num_resets = ARRAY_SIZE(nw_gcc_nord_resets), + .driver_data = &nw_gcc_nord_driver_data, +}; + +static const struct of_device_id nw_gcc_nord_match_table[] = { + { .compatible = "qcom,nord-nwgcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, nw_gcc_nord_match_table); + +static int nw_gcc_nord_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &nw_gcc_nord_desc); +} + +static struct platform_driver nw_gcc_nord_driver = { + .probe = nw_gcc_nord_probe, + .driver = { + .name = "nwgcc-nord", + .of_match_table = nw_gcc_nord_match_table, + }, +}; + +module_platform_driver(nw_gcc_nord_driver); + +MODULE_DESCRIPTION("QTI NWGCC NORD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/segcc-nord.c b/drivers/clk/qcom/segcc-nord.c new file mode 100644 index 000000000000..1aab0999de4d --- /dev/null +++ b/drivers/clk/qcom/segcc-nord.c @@ -0,0 +1,1609 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_BI_TCXO, + DT_SLEEP_CLK, +}; + +enum { + P_BI_TCXO, + P_SE_GCC_GPLL0_OUT_EVEN, + P_SE_GCC_GPLL0_OUT_MAIN, + P_SE_GCC_GPLL2_OUT_MAIN, + P_SE_GCC_GPLL4_OUT_MAIN, + P_SE_GCC_GPLL5_OUT_MAIN, + P_SLEEP_CLK, +}; + +static struct clk_alpha_pll se_gcc_gpll0 = { + .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_gpll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_se_gcc_gpll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv se_gcc_gpll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_se_gcc_gpll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_se_gcc_gpll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "se_gcc_gpll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +static struct clk_alpha_pll se_gcc_gpll2 = { + .offset = 0x2000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x0, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_gpll2", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static struct clk_alpha_pll se_gcc_gpll4 = { + .offset = 0x4000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x0, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_gpll4", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static struct clk_alpha_pll se_gcc_gpll5 = { + .offset = 0x5000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .enable_reg = 0x0, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_gpll5", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ole_ops, + }, + }, +}; + +static const struct parent_map se_gcc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_SE_GCC_GPLL0_OUT_MAIN, 1 }, + { P_SE_GCC_GPLL0_OUT_EVEN, 2 }, +}; + +static const struct clk_parent_data se_gcc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &se_gcc_gpll0.clkr.hw }, + { .hw = &se_gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map se_gcc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_SE_GCC_GPLL0_OUT_MAIN, 1 }, +}; + +static const struct clk_parent_data se_gcc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &se_gcc_gpll0.clkr.hw }, +}; + +static const struct parent_map se_gcc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_SE_GCC_GPLL0_OUT_MAIN, 1 }, + { P_SLEEP_CLK, 5 }, +}; + +static const struct clk_parent_data se_gcc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &se_gcc_gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map se_gcc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_SE_GCC_GPLL0_OUT_MAIN, 1 }, + { P_SE_GCC_GPLL5_OUT_MAIN, 3 }, + { P_SE_GCC_GPLL4_OUT_MAIN, 5 }, + { P_SE_GCC_GPLL2_OUT_MAIN, 6 }, +}; + +static const struct clk_parent_data se_gcc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .hw = &se_gcc_gpll0.clkr.hw }, + { .hw = &se_gcc_gpll5.clkr.hw }, + { .hw = &se_gcc_gpll4.clkr.hw }, + { .hw = &se_gcc_gpll2.clkr.hw }, +}; + +static const struct parent_map se_gcc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_SE_GCC_GPLL0_OUT_MAIN, 1 }, + { P_SE_GCC_GPLL0_OUT_EVEN, 2 }, + { P_SLEEP_CLK, 5 }, +}; + +static const struct clk_parent_data se_gcc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .hw = &se_gcc_gpll0.clkr.hw }, + { .hw = &se_gcc_gpll0_out_even.clkr.hw }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct freq_tbl ftbl_se_gcc_eee_emac0_clk_src[] = { + F(66666667, P_SE_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(100000000, P_SE_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_rcg2 se_gcc_eee_emac0_clk_src = { + .cmd_rcgr = 0x240b8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_2, + .freq_tbl = ftbl_se_gcc_eee_emac0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "se_gcc_eee_emac0_clk_src", + .parent_data = se_gcc_parent_data_2, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 se_gcc_eee_emac1_clk_src = { + .cmd_rcgr = 0x250b8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_2, + .freq_tbl = ftbl_se_gcc_eee_emac0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "se_gcc_eee_emac1_clk_src", + .parent_data = se_gcc_parent_data_2, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_se_gcc_emac0_phy_aux_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 se_gcc_emac0_phy_aux_clk_src = { + .cmd_rcgr = 0x24030, + .mnd_width = 0, + .hid_width = 5, + .parent_map = se_gcc_parent_map_2, + .freq_tbl = ftbl_se_gcc_emac0_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_phy_aux_clk_src", + .parent_data = se_gcc_parent_data_2, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_se_gcc_emac0_ptp_clk_src[] = { + F(150000000, P_SE_GCC_GPLL0_OUT_MAIN, 4, 0, 0), + F(250000000, P_SE_GCC_GPLL5_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 se_gcc_emac0_ptp_clk_src = { + .cmd_rcgr = 0x24084, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_3, + .freq_tbl = ftbl_se_gcc_emac0_ptp_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_ptp_clk_src", + .parent_data = se_gcc_parent_data_3, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_se_gcc_emac0_rgmii_clk_src[] = { + F(75000000, P_SE_GCC_GPLL0_OUT_MAIN, 8, 0, 0), + F(120000000, P_SE_GCC_GPLL0_OUT_MAIN, 5, 0, 0), + F(250000000, P_SE_GCC_GPLL5_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 se_gcc_emac0_rgmii_clk_src = { + .cmd_rcgr = 0x2406c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_3, + .freq_tbl = ftbl_se_gcc_emac0_rgmii_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_rgmii_clk_src", + .parent_data = se_gcc_parent_data_3, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 se_gcc_emac1_phy_aux_clk_src = { + .cmd_rcgr = 0x25030, + .mnd_width = 0, + .hid_width = 5, + .parent_map = se_gcc_parent_map_2, + .freq_tbl = ftbl_se_gcc_emac0_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_phy_aux_clk_src", + .parent_data = se_gcc_parent_data_2, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 se_gcc_emac1_ptp_clk_src = { + .cmd_rcgr = 0x25084, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_3, + .freq_tbl = ftbl_se_gcc_emac0_ptp_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_ptp_clk_src", + .parent_data = se_gcc_parent_data_3, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 se_gcc_emac1_rgmii_clk_src = { + .cmd_rcgr = 0x2506c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_3, + .freq_tbl = ftbl_se_gcc_emac0_rgmii_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_rgmii_clk_src", + .parent_data = se_gcc_parent_data_3, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_se_gcc_gp1_clk_src[] = { + F(66666667, P_SE_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(100000000, P_SE_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(200000000, P_SE_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 se_gcc_gp1_clk_src = { + .cmd_rcgr = 0x19004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_4, + .freq_tbl = ftbl_se_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "se_gcc_gp1_clk_src", + .parent_data = se_gcc_parent_data_4, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 se_gcc_gp2_clk_src = { + .cmd_rcgr = 0x1a004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_4, + .freq_tbl = ftbl_se_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "se_gcc_gp2_clk_src", + .parent_data = se_gcc_parent_data_4, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_se_gcc_qupv3_wrap0_s0_clk_src[] = { + F(7372800, P_SE_GCC_GPLL0_OUT_MAIN, 1, 192, 15625), + F(14745600, P_SE_GCC_GPLL0_OUT_MAIN, 1, 384, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_SE_GCC_GPLL0_OUT_MAIN, 1, 768, 15625), + F(32000000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 4, 75), + F(48000000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 2, 25), + F(51200000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 32, 375), + F(64000000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 8, 75), + F(66666667, P_SE_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(75000000, P_SE_GCC_GPLL0_OUT_MAIN, 8, 0, 0), + F(80000000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 2, 15), + F(96000000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 4, 25), + F(100000000, P_SE_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 64, 375), + F(112000000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 14, 75), + F(117964800, P_SE_GCC_GPLL0_OUT_MAIN, 1, 3072, 15625), + F(120000000, P_SE_GCC_GPLL0_OUT_MAIN, 5, 0, 0), + { } +}; + +static struct clk_init_data se_gcc_qupv3_wrap0_s0_clk_src_init = { + .name = "se_gcc_qupv3_wrap0_s0_clk_src", + .parent_data = se_gcc_parent_data_1, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap0_s0_clk_src = { + .cmd_rcgr = 0x2616c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_1, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap0_s0_clk_src_init, +}; + +static struct clk_init_data se_gcc_qupv3_wrap0_s1_clk_src_init = { + .name = "se_gcc_qupv3_wrap0_s1_clk_src", + .parent_data = se_gcc_parent_data_1, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap0_s1_clk_src = { + .cmd_rcgr = 0x262a8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_1, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap0_s1_clk_src_init, +}; + +static const struct freq_tbl ftbl_se_gcc_qupv3_wrap0_s2_clk_src[] = { + F(7372800, P_SE_GCC_GPLL0_OUT_MAIN, 1, 192, 15625), + F(14745600, P_SE_GCC_GPLL0_OUT_MAIN, 1, 384, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_SE_GCC_GPLL0_OUT_MAIN, 1, 768, 15625), + F(32000000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 4, 75), + F(48000000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 2, 25), + F(51200000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 32, 375), + F(64000000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 8, 75), + F(66666667, P_SE_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(75000000, P_SE_GCC_GPLL0_OUT_MAIN, 8, 0, 0), + F(80000000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 2, 15), + F(96000000, P_SE_GCC_GPLL0_OUT_MAIN, 1, 4, 25), + F(100000000, P_SE_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_init_data se_gcc_qupv3_wrap0_s2_clk_src_init = { + .name = "se_gcc_qupv3_wrap0_s2_clk_src", + .parent_data = se_gcc_parent_data_1, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap0_s2_clk_src = { + .cmd_rcgr = 0x263e4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_1, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap0_s2_clk_src_init, +}; + +static struct clk_init_data se_gcc_qupv3_wrap0_s3_clk_src_init = { + .name = "se_gcc_qupv3_wrap0_s3_clk_src", + .parent_data = se_gcc_parent_data_1, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap0_s3_clk_src = { + .cmd_rcgr = 0x26520, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_1, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap0_s3_clk_src_init, +}; + +static struct clk_init_data se_gcc_qupv3_wrap0_s4_clk_src_init = { + .name = "se_gcc_qupv3_wrap0_s4_clk_src", + .parent_data = se_gcc_parent_data_1, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap0_s4_clk_src = { + .cmd_rcgr = 0x2665c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_1, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap0_s4_clk_src_init, +}; + +static struct clk_init_data se_gcc_qupv3_wrap0_s5_clk_src_init = { + .name = "se_gcc_qupv3_wrap0_s5_clk_src", + .parent_data = se_gcc_parent_data_1, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap0_s5_clk_src = { + .cmd_rcgr = 0x26798, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_1, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap0_s5_clk_src_init, +}; + +static struct clk_init_data se_gcc_qupv3_wrap0_s6_clk_src_init = { + .name = "se_gcc_qupv3_wrap0_s6_clk_src", + .parent_data = se_gcc_parent_data_1, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap0_s6_clk_src = { + .cmd_rcgr = 0x268d4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_1, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap0_s6_clk_src_init, +}; + +static struct clk_init_data se_gcc_qupv3_wrap1_s0_clk_src_init = { + .name = "se_gcc_qupv3_wrap1_s0_clk_src", + .parent_data = se_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap1_s0_clk_src = { + .cmd_rcgr = 0x2716c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_0, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap1_s0_clk_src_init, +}; + +static struct clk_init_data se_gcc_qupv3_wrap1_s1_clk_src_init = { + .name = "se_gcc_qupv3_wrap1_s1_clk_src", + .parent_data = se_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap1_s1_clk_src = { + .cmd_rcgr = 0x272a8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_0, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap1_s1_clk_src_init, +}; + +static struct clk_init_data se_gcc_qupv3_wrap1_s2_clk_src_init = { + .name = "se_gcc_qupv3_wrap1_s2_clk_src", + .parent_data = se_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap1_s2_clk_src = { + .cmd_rcgr = 0x273e4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_0, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap1_s2_clk_src_init, +}; + +static struct clk_init_data se_gcc_qupv3_wrap1_s3_clk_src_init = { + .name = "se_gcc_qupv3_wrap1_s3_clk_src", + .parent_data = se_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap1_s3_clk_src = { + .cmd_rcgr = 0x27520, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_0, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap1_s3_clk_src_init, +}; + +static struct clk_init_data se_gcc_qupv3_wrap1_s4_clk_src_init = { + .name = "se_gcc_qupv3_wrap1_s4_clk_src", + .parent_data = se_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap1_s4_clk_src = { + .cmd_rcgr = 0x2765c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_0, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap1_s4_clk_src_init, +}; + +static struct clk_init_data se_gcc_qupv3_wrap1_s5_clk_src_init = { + .name = "se_gcc_qupv3_wrap1_s5_clk_src", + .parent_data = se_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap1_s5_clk_src = { + .cmd_rcgr = 0x27798, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_0, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap1_s5_clk_src_init, +}; + +static struct clk_init_data se_gcc_qupv3_wrap1_s6_clk_src_init = { + .name = "se_gcc_qupv3_wrap1_s6_clk_src", + .parent_data = se_gcc_parent_data_0, + .num_parents = ARRAY_SIZE(se_gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 se_gcc_qupv3_wrap1_s6_clk_src = { + .cmd_rcgr = 0x278d4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = se_gcc_parent_map_0, + .freq_tbl = ftbl_se_gcc_qupv3_wrap0_s2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &se_gcc_qupv3_wrap1_s6_clk_src_init, +}; + +static struct clk_branch se_gcc_eee_emac0_clk = { + .halt_reg = 0x240b4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x240b4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_eee_emac0_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_eee_emac0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_eee_emac1_clk = { + .halt_reg = 0x250b4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x250b4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_eee_emac1_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_eee_emac1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac0_axi_clk = { + .halt_reg = 0x2401c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2401c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2401c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac0_cc_sgmiiphy_rx_clk = { + .halt_reg = 0x24064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x24064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_cc_sgmiiphy_rx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac0_cc_sgmiiphy_tx_clk = { + .halt_reg = 0x2405c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2405c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_cc_sgmiiphy_tx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac0_phy_aux_clk = { + .halt_reg = 0x2402c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2402c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_emac0_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac0_ptp_clk = { + .halt_reg = 0x24048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x24048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_ptp_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_emac0_ptp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac0_rgmii_clk = { + .halt_reg = 0x24058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x24058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_rgmii_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_emac0_rgmii_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac0_rpcs_rx_clk = { + .halt_reg = 0x240a8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x240a8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_rpcs_rx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac0_rpcs_tx_clk = { + .halt_reg = 0x240a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x240a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_rpcs_tx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac0_xgxs_rx_clk = { + .halt_reg = 0x240b0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x240b0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_xgxs_rx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac0_xgxs_tx_clk = { + .halt_reg = 0x240ac, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x240ac, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac0_xgxs_tx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac1_axi_clk = { + .halt_reg = 0x2501c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2501c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2501c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac1_cc_sgmiiphy_rx_clk = { + .halt_reg = 0x25064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x25064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_cc_sgmiiphy_rx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac1_cc_sgmiiphy_tx_clk = { + .halt_reg = 0x2505c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2505c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_cc_sgmiiphy_tx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac1_phy_aux_clk = { + .halt_reg = 0x2502c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2502c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_emac1_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac1_ptp_clk = { + .halt_reg = 0x25048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x25048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_ptp_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_emac1_ptp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac1_rgmii_clk = { + .halt_reg = 0x25058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x25058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_rgmii_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_emac1_rgmii_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac1_rpcs_rx_clk = { + .halt_reg = 0x250a8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x250a8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_rpcs_rx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac1_rpcs_tx_clk = { + .halt_reg = 0x250a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x250a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_rpcs_tx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac1_xgxs_rx_clk = { + .halt_reg = 0x250b0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x250b0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_xgxs_rx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_emac1_xgxs_tx_clk = { + .halt_reg = 0x250ac, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x250ac, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_emac1_xgxs_tx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_frq_measure_ref_clk = { + .halt_reg = 0x18008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x18008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_frq_measure_ref_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_gp1_clk = { + .halt_reg = 0x19000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x19000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_gp1_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_gp1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_gp2_clk = { + .halt_reg = 0x1a000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1a000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_gp2_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_gp2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_mmu_2_tcu_vote_clk = { + .halt_reg = 0x57040, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_mmu_2_tcu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap0_core_2x_clk = { + .halt_reg = 0x26020, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap0_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap0_core_clk = { + .halt_reg = 0x2600c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap0_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap0_m_ahb_clk = { + .halt_reg = 0x26004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x26004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(12), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap0_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap0_s0_clk = { + .halt_reg = 0x2615c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap0_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap0_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap0_s1_clk = { + .halt_reg = 0x26298, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(17), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap0_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap0_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap0_s2_clk = { + .halt_reg = 0x263d4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(18), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap0_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap0_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap0_s3_clk = { + .halt_reg = 0x26510, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(19), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap0_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap0_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap0_s4_clk = { + .halt_reg = 0x2664c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(20), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap0_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap0_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap0_s5_clk = { + .halt_reg = 0x26788, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(21), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap0_s5_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap0_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap0_s6_clk = { + .halt_reg = 0x268c4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(22), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap0_s6_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap0_s6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap0_s_ahb_clk = { + .halt_reg = 0x26008, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x26008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(13), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap0_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap1_core_2x_clk = { + .halt_reg = 0x27020, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap1_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap1_core_clk = { + .halt_reg = 0x2700c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap1_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap1_m_ahb_clk = { + .halt_reg = 0x27004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x27004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(23), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap1_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap1_s0_clk = { + .halt_reg = 0x2715c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(27), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap1_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap1_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap1_s1_clk = { + .halt_reg = 0x27298, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(28), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap1_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap1_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap1_s2_clk = { + .halt_reg = 0x273d4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(29), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap1_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap1_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap1_s3_clk = { + .halt_reg = 0x27510, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(30), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap1_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap1_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap1_s4_clk = { + .halt_reg = 0x2764c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(31), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap1_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap1_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap1_s5_clk = { + .halt_reg = 0x27788, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap1_s5_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap1_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap1_s6_clk = { + .halt_reg = 0x278c4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x57008, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap1_s6_clk", + .parent_hws = (const struct clk_hw*[]) { + &se_gcc_qupv3_wrap1_s6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch se_gcc_qupv3_wrap1_s_ahb_clk = { + .halt_reg = 0x27008, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x27008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x57000, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "se_gcc_qupv3_wrap1_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc se_gcc_emac0_gdsc = { + .gdscr = 0x24004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "se_gcc_emac0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc se_gcc_emac1_gdsc = { + .gdscr = 0x25004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "se_gcc_emac1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *se_gcc_nord_clocks[] = { + [SE_GCC_EEE_EMAC0_CLK] = &se_gcc_eee_emac0_clk.clkr, + [SE_GCC_EEE_EMAC0_CLK_SRC] = &se_gcc_eee_emac0_clk_src.clkr, + [SE_GCC_EEE_EMAC1_CLK] = &se_gcc_eee_emac1_clk.clkr, + [SE_GCC_EEE_EMAC1_CLK_SRC] = &se_gcc_eee_emac1_clk_src.clkr, + [SE_GCC_EMAC0_AXI_CLK] = &se_gcc_emac0_axi_clk.clkr, + [SE_GCC_EMAC0_CC_SGMIIPHY_RX_CLK] = &se_gcc_emac0_cc_sgmiiphy_rx_clk.clkr, + [SE_GCC_EMAC0_CC_SGMIIPHY_TX_CLK] = &se_gcc_emac0_cc_sgmiiphy_tx_clk.clkr, + [SE_GCC_EMAC0_PHY_AUX_CLK] = &se_gcc_emac0_phy_aux_clk.clkr, + [SE_GCC_EMAC0_PHY_AUX_CLK_SRC] = &se_gcc_emac0_phy_aux_clk_src.clkr, + [SE_GCC_EMAC0_PTP_CLK] = &se_gcc_emac0_ptp_clk.clkr, + [SE_GCC_EMAC0_PTP_CLK_SRC] = &se_gcc_emac0_ptp_clk_src.clkr, + [SE_GCC_EMAC0_RGMII_CLK] = &se_gcc_emac0_rgmii_clk.clkr, + [SE_GCC_EMAC0_RGMII_CLK_SRC] = &se_gcc_emac0_rgmii_clk_src.clkr, + [SE_GCC_EMAC0_RPCS_RX_CLK] = &se_gcc_emac0_rpcs_rx_clk.clkr, + [SE_GCC_EMAC0_RPCS_TX_CLK] = &se_gcc_emac0_rpcs_tx_clk.clkr, + [SE_GCC_EMAC0_XGXS_RX_CLK] = &se_gcc_emac0_xgxs_rx_clk.clkr, + [SE_GCC_EMAC0_XGXS_TX_CLK] = &se_gcc_emac0_xgxs_tx_clk.clkr, + [SE_GCC_EMAC1_AXI_CLK] = &se_gcc_emac1_axi_clk.clkr, + [SE_GCC_EMAC1_CC_SGMIIPHY_RX_CLK] = &se_gcc_emac1_cc_sgmiiphy_rx_clk.clkr, + [SE_GCC_EMAC1_CC_SGMIIPHY_TX_CLK] = &se_gcc_emac1_cc_sgmiiphy_tx_clk.clkr, + [SE_GCC_EMAC1_PHY_AUX_CLK] = &se_gcc_emac1_phy_aux_clk.clkr, + [SE_GCC_EMAC1_PHY_AUX_CLK_SRC] = &se_gcc_emac1_phy_aux_clk_src.clkr, + [SE_GCC_EMAC1_PTP_CLK] = &se_gcc_emac1_ptp_clk.clkr, + [SE_GCC_EMAC1_PTP_CLK_SRC] = &se_gcc_emac1_ptp_clk_src.clkr, + [SE_GCC_EMAC1_RGMII_CLK] = &se_gcc_emac1_rgmii_clk.clkr, + [SE_GCC_EMAC1_RGMII_CLK_SRC] = &se_gcc_emac1_rgmii_clk_src.clkr, + [SE_GCC_EMAC1_RPCS_RX_CLK] = &se_gcc_emac1_rpcs_rx_clk.clkr, + [SE_GCC_EMAC1_RPCS_TX_CLK] = &se_gcc_emac1_rpcs_tx_clk.clkr, + [SE_GCC_EMAC1_XGXS_RX_CLK] = &se_gcc_emac1_xgxs_rx_clk.clkr, + [SE_GCC_EMAC1_XGXS_TX_CLK] = &se_gcc_emac1_xgxs_tx_clk.clkr, + [SE_GCC_FRQ_MEASURE_REF_CLK] = &se_gcc_frq_measure_ref_clk.clkr, + [SE_GCC_GP1_CLK] = &se_gcc_gp1_clk.clkr, + [SE_GCC_GP1_CLK_SRC] = &se_gcc_gp1_clk_src.clkr, + [SE_GCC_GP2_CLK] = &se_gcc_gp2_clk.clkr, + [SE_GCC_GP2_CLK_SRC] = &se_gcc_gp2_clk_src.clkr, + [SE_GCC_GPLL0] = &se_gcc_gpll0.clkr, + [SE_GCC_GPLL0_OUT_EVEN] = &se_gcc_gpll0_out_even.clkr, + [SE_GCC_GPLL2] = &se_gcc_gpll2.clkr, + [SE_GCC_GPLL4] = &se_gcc_gpll4.clkr, + [SE_GCC_GPLL5] = &se_gcc_gpll5.clkr, + [SE_GCC_MMU_2_TCU_VOTE_CLK] = &se_gcc_mmu_2_tcu_vote_clk.clkr, + [SE_GCC_QUPV3_WRAP0_CORE_2X_CLK] = &se_gcc_qupv3_wrap0_core_2x_clk.clkr, + [SE_GCC_QUPV3_WRAP0_CORE_CLK] = &se_gcc_qupv3_wrap0_core_clk.clkr, + [SE_GCC_QUPV3_WRAP0_M_AHB_CLK] = &se_gcc_qupv3_wrap0_m_ahb_clk.clkr, + [SE_GCC_QUPV3_WRAP0_S0_CLK] = &se_gcc_qupv3_wrap0_s0_clk.clkr, + [SE_GCC_QUPV3_WRAP0_S0_CLK_SRC] = &se_gcc_qupv3_wrap0_s0_clk_src.clkr, + [SE_GCC_QUPV3_WRAP0_S1_CLK] = &se_gcc_qupv3_wrap0_s1_clk.clkr, + [SE_GCC_QUPV3_WRAP0_S1_CLK_SRC] = &se_gcc_qupv3_wrap0_s1_clk_src.clkr, + [SE_GCC_QUPV3_WRAP0_S2_CLK] = &se_gcc_qupv3_wrap0_s2_clk.clkr, + [SE_GCC_QUPV3_WRAP0_S2_CLK_SRC] = &se_gcc_qupv3_wrap0_s2_clk_src.clkr, + [SE_GCC_QUPV3_WRAP0_S3_CLK] = &se_gcc_qupv3_wrap0_s3_clk.clkr, + [SE_GCC_QUPV3_WRAP0_S3_CLK_SRC] = &se_gcc_qupv3_wrap0_s3_clk_src.clkr, + [SE_GCC_QUPV3_WRAP0_S4_CLK] = &se_gcc_qupv3_wrap0_s4_clk.clkr, + [SE_GCC_QUPV3_WRAP0_S4_CLK_SRC] = &se_gcc_qupv3_wrap0_s4_clk_src.clkr, + [SE_GCC_QUPV3_WRAP0_S5_CLK] = &se_gcc_qupv3_wrap0_s5_clk.clkr, + [SE_GCC_QUPV3_WRAP0_S5_CLK_SRC] = &se_gcc_qupv3_wrap0_s5_clk_src.clkr, + [SE_GCC_QUPV3_WRAP0_S6_CLK] = &se_gcc_qupv3_wrap0_s6_clk.clkr, + [SE_GCC_QUPV3_WRAP0_S6_CLK_SRC] = &se_gcc_qupv3_wrap0_s6_clk_src.clkr, + [SE_GCC_QUPV3_WRAP0_S_AHB_CLK] = &se_gcc_qupv3_wrap0_s_ahb_clk.clkr, + [SE_GCC_QUPV3_WRAP1_CORE_2X_CLK] = &se_gcc_qupv3_wrap1_core_2x_clk.clkr, + [SE_GCC_QUPV3_WRAP1_CORE_CLK] = &se_gcc_qupv3_wrap1_core_clk.clkr, + [SE_GCC_QUPV3_WRAP1_M_AHB_CLK] = &se_gcc_qupv3_wrap1_m_ahb_clk.clkr, + [SE_GCC_QUPV3_WRAP1_S0_CLK] = &se_gcc_qupv3_wrap1_s0_clk.clkr, + [SE_GCC_QUPV3_WRAP1_S0_CLK_SRC] = &se_gcc_qupv3_wrap1_s0_clk_src.clkr, + [SE_GCC_QUPV3_WRAP1_S1_CLK] = &se_gcc_qupv3_wrap1_s1_clk.clkr, + [SE_GCC_QUPV3_WRAP1_S1_CLK_SRC] = &se_gcc_qupv3_wrap1_s1_clk_src.clkr, + [SE_GCC_QUPV3_WRAP1_S2_CLK] = &se_gcc_qupv3_wrap1_s2_clk.clkr, + [SE_GCC_QUPV3_WRAP1_S2_CLK_SRC] = &se_gcc_qupv3_wrap1_s2_clk_src.clkr, + [SE_GCC_QUPV3_WRAP1_S3_CLK] = &se_gcc_qupv3_wrap1_s3_clk.clkr, + [SE_GCC_QUPV3_WRAP1_S3_CLK_SRC] = &se_gcc_qupv3_wrap1_s3_clk_src.clkr, + [SE_GCC_QUPV3_WRAP1_S4_CLK] = &se_gcc_qupv3_wrap1_s4_clk.clkr, + [SE_GCC_QUPV3_WRAP1_S4_CLK_SRC] = &se_gcc_qupv3_wrap1_s4_clk_src.clkr, + [SE_GCC_QUPV3_WRAP1_S5_CLK] = &se_gcc_qupv3_wrap1_s5_clk.clkr, + [SE_GCC_QUPV3_WRAP1_S5_CLK_SRC] = &se_gcc_qupv3_wrap1_s5_clk_src.clkr, + [SE_GCC_QUPV3_WRAP1_S6_CLK] = &se_gcc_qupv3_wrap1_s6_clk.clkr, + [SE_GCC_QUPV3_WRAP1_S6_CLK_SRC] = &se_gcc_qupv3_wrap1_s6_clk_src.clkr, + [SE_GCC_QUPV3_WRAP1_S_AHB_CLK] = &se_gcc_qupv3_wrap1_s_ahb_clk.clkr, +}; + +static struct gdsc *se_gcc_nord_gdscs[] = { + [SE_GCC_EMAC0_GDSC] = &se_gcc_emac0_gdsc, + [SE_GCC_EMAC1_GDSC] = &se_gcc_emac1_gdsc, +}; + +static const struct qcom_reset_map se_gcc_nord_resets[] = { + [SE_GCC_EMAC0_BCR] = { 0x24000 }, + [SE_GCC_EMAC1_BCR] = { 0x25000 }, + [SE_GCC_QUPV3_WRAPPER_0_BCR] = { 0x26000 }, + [SE_GCC_QUPV3_WRAPPER_1_BCR] = { 0x27000 }, +}; + +static const struct clk_rcg_dfs_data se_gcc_nord_dfs_clocks[] = { + DEFINE_RCG_DFS(se_gcc_qupv3_wrap0_s0_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap0_s1_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap0_s2_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap0_s3_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap0_s4_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap0_s5_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap0_s6_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap1_s0_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap1_s1_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap1_s2_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap1_s3_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap1_s4_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap1_s5_clk_src), + DEFINE_RCG_DFS(se_gcc_qupv3_wrap1_s6_clk_src), +}; + +static const struct regmap_config se_gcc_nord_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf41f0, + .fast_io = true, +}; + +static struct qcom_cc_driver_data se_gcc_nord_driver_data = { + .dfs_rcgs = se_gcc_nord_dfs_clocks, + .num_dfs_rcgs = ARRAY_SIZE(se_gcc_nord_dfs_clocks), +}; + +static const struct qcom_cc_desc se_gcc_nord_desc = { + .config = &se_gcc_nord_regmap_config, + .clks = se_gcc_nord_clocks, + .num_clks = ARRAY_SIZE(se_gcc_nord_clocks), + .resets = se_gcc_nord_resets, + .num_resets = ARRAY_SIZE(se_gcc_nord_resets), + .gdscs = se_gcc_nord_gdscs, + .num_gdscs = ARRAY_SIZE(se_gcc_nord_gdscs), + .driver_data = &se_gcc_nord_driver_data, +}; + +static const struct of_device_id se_gcc_nord_match_table[] = { + { .compatible = "qcom,nord-segcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, se_gcc_nord_match_table); + +static int se_gcc_nord_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &se_gcc_nord_desc); +} + +static struct platform_driver se_gcc_nord_driver = { + .probe = se_gcc_nord_probe, + .driver = { + .name = "segcc-nord", + .of_match_table = se_gcc_nord_match_table, + }, +}; + +module_platform_driver(se_gcc_nord_driver); + +MODULE_DESCRIPTION("QTI SEGCC NORD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/tcsrcc-eliza.c b/drivers/clk/qcom/tcsrcc-eliza.c new file mode 100644 index 000000000000..5a47a4c77cb5 --- /dev/null +++ b/drivers/clk/qcom/tcsrcc-eliza.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-branch.h" +#include "clk-regmap.h" +#include "common.h" + +enum { + DT_BI_TCXO_PAD, +}; + +static struct clk_branch tcsr_hdmi_clkref_en = { + .halt_reg = 0x14, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x14, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_hdmi_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_pcie_0_clkref_en = { + .halt_reg = 0x0, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_pcie_0_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_pcie_1_clkref_en = { + .halt_reg = 0x1c, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x1c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_pcie_1_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_ufs_clkref_en = { + .halt_reg = 0x8, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_ufs_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_usb2_clkref_en = { + .halt_reg = 0x4, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_usb2_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_usb3_clkref_en = { + .halt_reg = 0x10, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x10, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_usb3_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *tcsr_cc_eliza_clocks[] = { + [TCSR_HDMI_CLKREF_EN] = &tcsr_hdmi_clkref_en.clkr, + [TCSR_PCIE_0_CLKREF_EN] = &tcsr_pcie_0_clkref_en.clkr, + [TCSR_PCIE_1_CLKREF_EN] = &tcsr_pcie_1_clkref_en.clkr, + [TCSR_UFS_CLKREF_EN] = &tcsr_ufs_clkref_en.clkr, + [TCSR_USB2_CLKREF_EN] = &tcsr_usb2_clkref_en.clkr, + [TCSR_USB3_CLKREF_EN] = &tcsr_usb3_clkref_en.clkr, +}; + +static const struct regmap_config tcsr_cc_eliza_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1c, + .fast_io = true, +}; + +static const struct qcom_cc_desc tcsr_cc_eliza_desc = { + .config = &tcsr_cc_eliza_regmap_config, + .clks = tcsr_cc_eliza_clocks, + .num_clks = ARRAY_SIZE(tcsr_cc_eliza_clocks), +}; + +static const struct of_device_id tcsr_cc_eliza_match_table[] = { + { .compatible = "qcom,eliza-tcsr" }, + { } +}; +MODULE_DEVICE_TABLE(of, tcsr_cc_eliza_match_table); + +static int tcsr_cc_eliza_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &tcsr_cc_eliza_desc); +} + +static struct platform_driver tcsr_cc_eliza_driver = { + .probe = tcsr_cc_eliza_probe, + .driver = { + .name = "tcsr_cc-eliza", + .of_match_table = tcsr_cc_eliza_match_table, + }, +}; + +static int __init tcsr_cc_eliza_init(void) +{ + return platform_driver_register(&tcsr_cc_eliza_driver); +} +subsys_initcall(tcsr_cc_eliza_init); + +static void __exit tcsr_cc_eliza_exit(void) +{ + platform_driver_unregister(&tcsr_cc_eliza_driver); +} +module_exit(tcsr_cc_eliza_exit); + +MODULE_DESCRIPTION("QTI TCSR_CC Eliza Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/tcsrcc-glymur.c b/drivers/clk/qcom/tcsrcc-glymur.c index 215bc2ac548d..9c0edebcdbb1 100644 --- a/drivers/clk/qcom/tcsrcc-glymur.c +++ b/drivers/clk/qcom/tcsrcc-glymur.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -309,5 +308,5 @@ static void __exit tcsr_cc_glymur_exit(void) } module_exit(tcsr_cc_glymur_exit); -MODULE_DESCRIPTION("QTI TCSRCC GLYMUR Driver"); +MODULE_DESCRIPTION("QTI TCSRCC Glymur Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/tcsrcc-kaanapali.c b/drivers/clk/qcom/tcsrcc-kaanapali.c index 4da77367c9e0..db46d639edb8 100644 --- a/drivers/clk/qcom/tcsrcc-kaanapali.c +++ b/drivers/clk/qcom/tcsrcc-kaanapali.c @@ -5,7 +5,6 @@ #include #include -#include #include #include diff --git a/drivers/clk/qcom/tcsrcc-nord.c b/drivers/clk/qcom/tcsrcc-nord.c new file mode 100644 index 000000000000..ed0f4909158f --- /dev/null +++ b/drivers/clk/qcom/tcsrcc-nord.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "common.h" +#include "reset.h" + +enum { + DT_BI_TCXO_PAD, +}; + +static struct clk_branch tcsr_dp_rx_0_clkref_en = { + .halt_reg = 0xa008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0xa008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_dp_rx_0_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_dp_rx_1_clkref_en = { + .halt_reg = 0xb008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0xb008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_dp_rx_1_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_dp_tx_0_clkref_en = { + .halt_reg = 0xc008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0xc008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_dp_tx_0_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_dp_tx_1_clkref_en = { + .halt_reg = 0xd008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0xd008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_dp_tx_1_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_dp_tx_2_clkref_en = { + .halt_reg = 0xe008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0xe008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_dp_tx_2_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_dp_tx_3_clkref_en = { + .halt_reg = 0xf008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0xf008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_dp_tx_3_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_pcie_clkref_en = { + .halt_reg = 0x8, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_pcie_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_ufs_clkref_en = { + .halt_reg = 0x3008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x3008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_ufs_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_usb2_0_clkref_en = { + .halt_reg = 0x4008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x4008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_usb2_0_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_usb2_1_clkref_en = { + .halt_reg = 0x5008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x5008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_usb2_1_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_usb2_2_clkref_en = { + .halt_reg = 0x6008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x6008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_usb2_2_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_usb3_0_clkref_en = { + .halt_reg = 0x8008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x8008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_usb3_0_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_usb3_1_clkref_en = { + .halt_reg = 0x7008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x7008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_usb3_1_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_ux_sgmii_0_clkref_en = { + .halt_reg = 0x1008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x1008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_ux_sgmii_0_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_ux_sgmii_1_clkref_en = { + .halt_reg = 0x2008, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x2008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_ux_sgmii_1_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *tcsr_cc_nord_clocks[] = { + [TCSR_DP_RX_0_CLKREF_EN] = &tcsr_dp_rx_0_clkref_en.clkr, + [TCSR_DP_RX_1_CLKREF_EN] = &tcsr_dp_rx_1_clkref_en.clkr, + [TCSR_DP_TX_0_CLKREF_EN] = &tcsr_dp_tx_0_clkref_en.clkr, + [TCSR_DP_TX_1_CLKREF_EN] = &tcsr_dp_tx_1_clkref_en.clkr, + [TCSR_DP_TX_2_CLKREF_EN] = &tcsr_dp_tx_2_clkref_en.clkr, + [TCSR_DP_TX_3_CLKREF_EN] = &tcsr_dp_tx_3_clkref_en.clkr, + [TCSR_PCIE_CLKREF_EN] = &tcsr_pcie_clkref_en.clkr, + [TCSR_UFS_CLKREF_EN] = &tcsr_ufs_clkref_en.clkr, + [TCSR_USB2_0_CLKREF_EN] = &tcsr_usb2_0_clkref_en.clkr, + [TCSR_USB2_1_CLKREF_EN] = &tcsr_usb2_1_clkref_en.clkr, + [TCSR_USB2_2_CLKREF_EN] = &tcsr_usb2_2_clkref_en.clkr, + [TCSR_USB3_0_CLKREF_EN] = &tcsr_usb3_0_clkref_en.clkr, + [TCSR_USB3_1_CLKREF_EN] = &tcsr_usb3_1_clkref_en.clkr, + [TCSR_UX_SGMII_0_CLKREF_EN] = &tcsr_ux_sgmii_0_clkref_en.clkr, + [TCSR_UX_SGMII_1_CLKREF_EN] = &tcsr_ux_sgmii_1_clkref_en.clkr, +}; + +static const struct regmap_config tcsr_cc_nord_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf008, + .fast_io = true, +}; + +static const struct qcom_cc_desc tcsr_cc_nord_desc = { + .config = &tcsr_cc_nord_regmap_config, + .clks = tcsr_cc_nord_clocks, + .num_clks = ARRAY_SIZE(tcsr_cc_nord_clocks), +}; + +static const struct of_device_id tcsr_cc_nord_match_table[] = { + { .compatible = "qcom,nord-tcsrcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, tcsr_cc_nord_match_table); + +static int tcsr_cc_nord_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &tcsr_cc_nord_desc); +} + +static struct platform_driver tcsr_cc_nord_driver = { + .probe = tcsr_cc_nord_probe, + .driver = { + .name = "tcsrcc-nord", + .of_match_table = tcsr_cc_nord_match_table, + }, +}; + +module_platform_driver(tcsr_cc_nord_driver); + +MODULE_DESCRIPTION("QTI TCSRCC NORD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/tcsrcc-sm8750.c b/drivers/clk/qcom/tcsrcc-sm8750.c index 242e320986ef..46af98760197 100644 --- a/drivers/clk/qcom/tcsrcc-sm8750.c +++ b/drivers/clk/qcom/tcsrcc-sm8750.c @@ -4,8 +4,8 @@ */ #include +#include #include -#include #include #include diff --git a/drivers/clk/qcom/videocc-glymur.c b/drivers/clk/qcom/videocc-glymur.c new file mode 100644 index 000000000000..bbf13f4ba82d --- /dev/null +++ b/drivers/clk/qcom/videocc-glymur.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_BI_TCXO, + DT_BI_TCXO_AO, + DT_SLEEP_CLK, +}; + +enum { + P_BI_TCXO, + P_SLEEP_CLK, + P_VIDEO_CC_PLL0_OUT_MAIN, +}; + +static const struct pll_vco taycan_eko_t_vco[] = { + { 249600000, 2500000000, 0 }, +}; + +/* 720.0 MHz Configuration */ +static const struct alpha_pll_config video_cc_pll0_config = { + .l = 0x25, + .alpha = 0x8000, + .config_ctl_val = 0x25c400e7, + .config_ctl_hi_val = 0x0a8060e0, + .config_ctl_hi1_val = 0xf51dea20, + .user_ctl_val = 0x00000008, + .user_ctl_hi_val = 0x00000002, +}; + +static struct clk_alpha_pll video_cc_pll0 = { + .offset = 0x0, + .config = &video_cc_pll0_config, + .vco_table = taycan_eko_t_vco, + .num_vco = ARRAY_SIZE(taycan_eko_t_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EKO_T], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_taycan_eko_t_ops, + }, + }, +}; + +static const struct parent_map video_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data video_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map video_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_VIDEO_CC_PLL0_OUT_MAIN, 1 }, +}; + +static const struct clk_parent_data video_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &video_cc_pll0.clkr.hw }, +}; + +static const struct parent_map video_cc_parent_map_2[] = { + { P_SLEEP_CLK, 0 }, +}; + +static const struct clk_parent_data video_cc_parent_data_2[] = { + { .index = DT_SLEEP_CLK }, +}; + +static const struct freq_tbl ftbl_video_cc_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_ahb_clk_src = { + .cmd_rcgr = 0x8018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_0, + .freq_tbl = ftbl_video_cc_ahb_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_ahb_clk_src", + .parent_data = video_cc_parent_data_0, + .num_parents = ARRAY_SIZE(video_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_video_cc_mvs0_clk_src[] = { + F(720000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(1014000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(1098000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(1332000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(1600000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(1965000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_mvs0_clk_src = { + .cmd_rcgr = 0x8000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_1, + .freq_tbl = ftbl_video_cc_mvs0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_clk_src", + .parent_data = video_cc_parent_data_1, + .num_parents = ARRAY_SIZE(video_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_video_cc_sleep_clk_src[] = { + F(32000, P_SLEEP_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_sleep_clk_src = { + .cmd_rcgr = 0x8120, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_2, + .freq_tbl = ftbl_video_cc_sleep_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_sleep_clk_src", + .parent_data = video_cc_parent_data_2, + .num_parents = ARRAY_SIZE(video_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 video_cc_xo_clk_src = { + .cmd_rcgr = 0x80f8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_0, + .freq_tbl = ftbl_video_cc_ahb_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_xo_clk_src", + .parent_data = video_cc_parent_data_0, + .num_parents = ARRAY_SIZE(video_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_regmap_div video_cc_mvs0_div_clk_src = { + .reg = 0x809c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div video_cc_mvs0c_div2_div_clk_src = { + .reg = 0x8060, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0c_div2_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div video_cc_mvs1_div_clk_src = { + .reg = 0x80d8, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch video_cc_mvs0_clk = { + .halt_reg = 0x807c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x807c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x807c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0_freerun_clk = { + .halt_reg = 0x808c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x808c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_freerun_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0_shift_clk = { + .halt_reg = 0x8114, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x8114, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x8114, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0c_clk = { + .halt_reg = 0x804c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x804c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0c_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0c_div2_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0c_freerun_clk = { + .halt_reg = 0x805c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x805c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0c_freerun_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0c_div2_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0c_shift_clk = { + .halt_reg = 0x811c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x811c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x811c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0c_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs1_clk = { + .halt_reg = 0x80b8, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x80b8, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x80b8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs1_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs1_freerun_clk = { + .halt_reg = 0x80c8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x80c8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1_freerun_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs1_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs1_shift_clk = { + .halt_reg = 0x8118, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x8118, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x8118, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc video_cc_mvs0c_gdsc = { + .gdscr = 0x8034, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x6, + .pd = { + .name = "video_cc_mvs0c_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc video_cc_mvs0_gdsc = { + .gdscr = 0x8068, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x6, + .pd = { + .name = "video_cc_mvs0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, + .parent = &video_cc_mvs0c_gdsc.pd, +}; + +static struct gdsc video_cc_mvs1_gdsc = { + .gdscr = 0x80a4, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x6, + .pd = { + .name = "video_cc_mvs1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *video_cc_glymur_clocks[] = { + [VIDEO_CC_AHB_CLK_SRC] = &video_cc_ahb_clk_src.clkr, + [VIDEO_CC_MVS0_CLK] = &video_cc_mvs0_clk.clkr, + [VIDEO_CC_MVS0_CLK_SRC] = &video_cc_mvs0_clk_src.clkr, + [VIDEO_CC_MVS0_DIV_CLK_SRC] = &video_cc_mvs0_div_clk_src.clkr, + [VIDEO_CC_MVS0_FREERUN_CLK] = &video_cc_mvs0_freerun_clk.clkr, + [VIDEO_CC_MVS0_SHIFT_CLK] = &video_cc_mvs0_shift_clk.clkr, + [VIDEO_CC_MVS0C_CLK] = &video_cc_mvs0c_clk.clkr, + [VIDEO_CC_MVS0C_DIV2_DIV_CLK_SRC] = &video_cc_mvs0c_div2_div_clk_src.clkr, + [VIDEO_CC_MVS0C_FREERUN_CLK] = &video_cc_mvs0c_freerun_clk.clkr, + [VIDEO_CC_MVS0C_SHIFT_CLK] = &video_cc_mvs0c_shift_clk.clkr, + [VIDEO_CC_MVS1_CLK] = &video_cc_mvs1_clk.clkr, + [VIDEO_CC_MVS1_DIV_CLK_SRC] = &video_cc_mvs1_div_clk_src.clkr, + [VIDEO_CC_MVS1_FREERUN_CLK] = &video_cc_mvs1_freerun_clk.clkr, + [VIDEO_CC_MVS1_SHIFT_CLK] = &video_cc_mvs1_shift_clk.clkr, + [VIDEO_CC_PLL0] = &video_cc_pll0.clkr, + [VIDEO_CC_SLEEP_CLK_SRC] = &video_cc_sleep_clk_src.clkr, + [VIDEO_CC_XO_CLK_SRC] = &video_cc_xo_clk_src.clkr, +}; + +static struct gdsc *video_cc_glymur_gdscs[] = { + [VIDEO_CC_MVS0_GDSC] = &video_cc_mvs0_gdsc, + [VIDEO_CC_MVS0C_GDSC] = &video_cc_mvs0c_gdsc, + [VIDEO_CC_MVS1_GDSC] = &video_cc_mvs1_gdsc, +}; + +static const struct qcom_reset_map video_cc_glymur_resets[] = { + [VIDEO_CC_INTERFACE_BCR] = { 0x80dc }, + [VIDEO_CC_MVS0_BCR] = { 0x8064 }, + [VIDEO_CC_MVS0C_FREERUN_CLK_ARES] = { 0x805c, 2 }, + [VIDEO_CC_MVS0C_BCR] = { 0x8030 }, + [VIDEO_CC_MVS0_FREERUN_CLK_ARES] = { 0x808c, 2 }, + [VIDEO_CC_MVS1_FREERUN_CLK_ARES] = { 0x80c8, 2 }, + [VIDEO_CC_MVS1_BCR] = { 0x80a0 }, +}; + +static struct clk_alpha_pll *video_cc_glymur_plls[] = { + &video_cc_pll0, +}; + +static const u32 video_cc_glymur_critical_cbcrs[] = { + 0x80e0, /* VIDEO_CC_AHB_CLK */ + 0x8138, /* VIDEO_CC_SLEEP_CLK */ + 0x8110, /* VIDEO_CC_XO_CLK */ +}; + +static const struct regmap_config video_cc_glymur_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9f54, + .fast_io = true, +}; + +static void clk_glymur_regs_configure(struct device *dev, struct regmap *regmap) +{ + /* Update CTRL_IN register */ + regmap_update_bits(regmap, 0x9f24, BIT(0), BIT(0)); +} + +static const struct qcom_cc_driver_data video_cc_glymur_driver_data = { + .alpha_plls = video_cc_glymur_plls, + .num_alpha_plls = ARRAY_SIZE(video_cc_glymur_plls), + .clk_cbcrs = video_cc_glymur_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(video_cc_glymur_critical_cbcrs), + .clk_regs_configure = clk_glymur_regs_configure, +}; + +static const struct qcom_cc_desc video_cc_glymur_desc = { + .config = &video_cc_glymur_regmap_config, + .clks = video_cc_glymur_clocks, + .num_clks = ARRAY_SIZE(video_cc_glymur_clocks), + .resets = video_cc_glymur_resets, + .num_resets = ARRAY_SIZE(video_cc_glymur_resets), + .gdscs = video_cc_glymur_gdscs, + .num_gdscs = ARRAY_SIZE(video_cc_glymur_gdscs), + .use_rpm = true, + .driver_data = &video_cc_glymur_driver_data, +}; + +static const struct of_device_id video_cc_glymur_match_table[] = { + { .compatible = "qcom,glymur-videocc" }, + { } +}; +MODULE_DEVICE_TABLE(of, video_cc_glymur_match_table); + +static int video_cc_glymur_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &video_cc_glymur_desc); +} + +static struct platform_driver video_cc_glymur_driver = { + .probe = video_cc_glymur_probe, + .driver = { + .name = "videocc-glymur", + .of_match_table = video_cc_glymur_match_table, + }, +}; + +module_platform_driver(video_cc_glymur_driver); + +MODULE_DESCRIPTION("QTI VIDEOCC Glymur Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/videocc-kaanapali.c b/drivers/clk/qcom/videocc-kaanapali.c index 835a59536ba7..b29e3da465e5 100644 --- a/drivers/clk/qcom/videocc-kaanapali.c +++ b/drivers/clk/qcom/videocc-kaanapali.c @@ -741,7 +741,7 @@ static struct clk_alpha_pll *video_cc_kaanapali_plls[] = { &video_cc_pll3, }; -static u32 video_cc_kaanapali_critical_cbcrs[] = { +static const u32 video_cc_kaanapali_critical_cbcrs[] = { 0x817c, /* VIDEO_CC_AHB_CLK */ 0x81bc, /* VIDEO_CC_SLEEP_CLK */ 0x81b0, /* VIDEO_CC_TS_XO_CLK */ @@ -776,7 +776,7 @@ static void clk_kaanapali_regs_configure(struct device *dev, struct regmap *regm regmap_set_bits(regmap, 0x8158, ACCU_CFG_MASK); } -static struct qcom_cc_driver_data video_cc_kaanapali_driver_data = { +static const struct qcom_cc_driver_data video_cc_kaanapali_driver_data = { .alpha_plls = video_cc_kaanapali_plls, .num_alpha_plls = ARRAY_SIZE(video_cc_kaanapali_plls), .clk_cbcrs = video_cc_kaanapali_critical_cbcrs, diff --git a/drivers/clk/qcom/videocc-milos.c b/drivers/clk/qcom/videocc-milos.c index acc9df295d4f..3cce34e8c71a 100644 --- a/drivers/clk/qcom/videocc-milos.c +++ b/drivers/clk/qcom/videocc-milos.c @@ -345,7 +345,7 @@ static struct clk_alpha_pll *video_cc_milos_plls[] = { &video_cc_pll0, }; -static u32 video_cc_milos_critical_cbcrs[] = { +static const u32 video_cc_milos_critical_cbcrs[] = { 0x80f4, /* VIDEO_CC_AHB_CLK */ 0x8140, /* VIDEO_CC_SLEEP_CLK */ 0x8124, /* VIDEO_CC_XO_CLK */ @@ -359,7 +359,7 @@ static const struct regmap_config video_cc_milos_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data video_cc_milos_driver_data = { +static const struct qcom_cc_driver_data video_cc_milos_driver_data = { .alpha_plls = video_cc_milos_plls, .num_alpha_plls = ARRAY_SIZE(video_cc_milos_plls), .clk_cbcrs = video_cc_milos_critical_cbcrs, diff --git a/drivers/clk/qcom/videocc-qcs615.c b/drivers/clk/qcom/videocc-qcs615.c index 1b41fa44c17e..3203cb938ad1 100644 --- a/drivers/clk/qcom/videocc-qcs615.c +++ b/drivers/clk/qcom/videocc-qcs615.c @@ -283,7 +283,7 @@ static struct clk_alpha_pll *video_cc_qcs615_plls[] = { &video_pll0, }; -static u32 video_cc_qcs615_critical_cbcrs[] = { +static const u32 video_cc_qcs615_critical_cbcrs[] = { 0xab8, /* VIDEO_CC_XO_CLK */ }; @@ -295,7 +295,7 @@ static const struct regmap_config video_cc_qcs615_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data video_cc_qcs615_driver_data = { +static const struct qcom_cc_driver_data video_cc_qcs615_driver_data = { .alpha_plls = video_cc_qcs615_plls, .num_alpha_plls = ARRAY_SIZE(video_cc_qcs615_plls), .clk_cbcrs = video_cc_qcs615_critical_cbcrs, diff --git a/drivers/clk/qcom/videocc-sm8450.c b/drivers/clk/qcom/videocc-sm8450.c index dc168ce199cc..18b191f598b5 100644 --- a/drivers/clk/qcom/videocc-sm8450.c +++ b/drivers/clk/qcom/videocc-sm8450.c @@ -413,7 +413,7 @@ static struct clk_alpha_pll *video_cc_sm8450_plls[] = { &video_cc_pll1, }; -static u32 video_cc_sm8450_critical_cbcrs[] = { +static const u32 video_cc_sm8450_critical_cbcrs[] = { 0x80e4, /* VIDEO_CC_AHB_CLK */ 0x8114, /* VIDEO_CC_XO_CLK */ 0x8130, /* VIDEO_CC_SLEEP_CLK */ @@ -427,7 +427,7 @@ static const struct regmap_config video_cc_sm8450_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data video_cc_sm8450_driver_data = { +static const struct qcom_cc_driver_data video_cc_sm8450_driver_data = { .alpha_plls = video_cc_sm8450_plls, .num_alpha_plls = ARRAY_SIZE(video_cc_sm8450_plls), .clk_cbcrs = video_cc_sm8450_critical_cbcrs, diff --git a/drivers/clk/qcom/videocc-sm8550.c b/drivers/clk/qcom/videocc-sm8550.c index 32a6505abe26..4e35964f0803 100644 --- a/drivers/clk/qcom/videocc-sm8550.c +++ b/drivers/clk/qcom/videocc-sm8550.c @@ -536,13 +536,13 @@ static struct clk_alpha_pll *video_cc_sm8550_plls[] = { &video_cc_pll1, }; -static u32 video_cc_sm8550_critical_cbcrs[] = { +static const u32 video_cc_sm8550_critical_cbcrs[] = { 0x80f4, /* VIDEO_CC_AHB_CLK */ 0x8124, /* VIDEO_CC_XO_CLK */ 0x8140, /* VIDEO_CC_SLEEP_CLK */ }; -static u32 video_cc_sm8650_critical_cbcrs[] = { +static const u32 video_cc_sm8650_critical_cbcrs[] = { 0x80f4, /* VIDEO_CC_AHB_CLK */ 0x8124, /* VIDEO_CC_XO_CLK */ 0x8150, /* VIDEO_CC_SLEEP_CLK */ diff --git a/drivers/clk/qcom/videocc-sm8750.c b/drivers/clk/qcom/videocc-sm8750.c index 823aca2bdd34..e9414390a3cc 100644 --- a/drivers/clk/qcom/videocc-sm8750.c +++ b/drivers/clk/qcom/videocc-sm8750.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -393,7 +392,7 @@ static struct clk_alpha_pll *video_cc_sm8750_plls[] = { &video_cc_pll0, }; -static u32 video_cc_sm8750_critical_cbcrs[] = { +static const u32 video_cc_sm8750_critical_cbcrs[] = { 0x80a4, /* VIDEO_CC_AHB_CLK */ 0x80f8, /* VIDEO_CC_SLEEP_CLK */ 0x80d4, /* VIDEO_CC_XO_CLK */ @@ -408,7 +407,7 @@ static void clk_sm8750_regs_configure(struct device *dev, struct regmap *regmap) regmap_update_bits(regmap, 0x9f24, BIT(0), BIT(0)); } -static struct qcom_cc_driver_data video_cc_sm8750_driver_data = { +static const struct qcom_cc_driver_data video_cc_sm8750_driver_data = { .alpha_plls = video_cc_sm8750_plls, .num_alpha_plls = ARRAY_SIZE(video_cc_sm8750_plls), .clk_cbcrs = video_cc_sm8750_critical_cbcrs, diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index 6a5a04664990..0203ecbb3882 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -39,6 +39,7 @@ config CLK_RENESAS select CLK_R9A07G044 if ARCH_R9A07G044 select CLK_R9A07G054 if ARCH_R9A07G054 select CLK_R9A08G045 if ARCH_R9A08G045 + select CLK_R9A08G046 if ARCH_R9A08G046 select CLK_R9A09G011 if ARCH_R9A09G011 select CLK_R9A09G047 if ARCH_R9A09G047 select CLK_R9A09G056 if ARCH_R9A09G056 @@ -194,6 +195,10 @@ config CLK_R9A08G045 bool "RZ/G3S clock support" if COMPILE_TEST select CLK_RZG2L +config CLK_R9A08G046 + bool "RZ/G3L clock support" if COMPILE_TEST + select CLK_RZG2L + config CLK_R9A09G011 bool "RZ/V2M clock support" if COMPILE_TEST select CLK_RZG2L @@ -250,7 +255,7 @@ config CLK_RCAR_USB2_CLOCK_SEL This is a driver for R-Car USB2 clock selector config CLK_RZG2L - bool "RZ/{G2L,G2UL,G3S,V2L} family clock support" if COMPILE_TEST + bool "RZ/{G2{L,UL},G3{S,L},V2L} family clock support" if COMPILE_TEST select RESET_CONTROLLER config CLK_RZV2H diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index d28eb276a153..bd2bed91ab29 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_CLK_R9A07G043) += r9a07g043-cpg.o obj-$(CONFIG_CLK_R9A07G044) += r9a07g044-cpg.o obj-$(CONFIG_CLK_R9A07G054) += r9a07g044-cpg.o obj-$(CONFIG_CLK_R9A08G045) += r9a08g045-cpg.o +obj-$(CONFIG_CLK_R9A08G046) += r9a08g046-cpg.o obj-$(CONFIG_CLK_R9A09G011) += r9a09g011-cpg.o obj-$(CONFIG_CLK_R9A09G047) += r9a09g047-cpg.o obj-$(CONFIG_CLK_R9A09G056) += r9a09g056-cpg.o diff --git a/drivers/clk/renesas/r9a06g032-clocks.c b/drivers/clk/renesas/r9a06g032-clocks.c index 7407a4183a6c..076f587dfd39 100644 --- a/drivers/clk/renesas/r9a06g032-clocks.c +++ b/drivers/clk/renesas/r9a06g032-clocks.c @@ -1342,8 +1342,9 @@ static int __init r9a06g032_clocks_probe(struct platform_device *pdev) /* Clear potentially pending resets */ writel(R9A06G032_SYSCTRL_WDA7RST_0 | R9A06G032_SYSCTRL_WDA7RST_1, clocks->reg + R9A06G032_SYSCTRL_RSTCTRL); - /* Allow software reset */ - writel(R9A06G032_SYSCTRL_SWRST | R9A06G032_SYSCTRL_RSTEN_MRESET_EN, + /* Allow watchdog and software resets */ + writel(R9A06G032_SYSCTRL_WDA7RST_0 | R9A06G032_SYSCTRL_WDA7RST_1 | + R9A06G032_SYSCTRL_SWRST | R9A06G032_SYSCTRL_RSTEN_MRESET_EN, clocks->reg + R9A06G032_SYSCTRL_RSTEN); error = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART, SYS_OFF_PRIO_HIGH, diff --git a/drivers/clk/renesas/r9a07g043-cpg.c b/drivers/clk/renesas/r9a07g043-cpg.c index 33e9a1223c72..70944ef8c5b8 100644 --- a/drivers/clk/renesas/r9a07g043-cpg.c +++ b/drivers/clk/renesas/r9a07g043-cpg.c @@ -379,6 +379,11 @@ static const unsigned int r9a07g043_crit_mod_clks[] __initconst = { MOD_CLK_BASE + R9A07G043_DMAC_ACLK, }; +static const unsigned int r9a07g043_crit_resets[] = { + R9A07G043_DMAC_ARESETN, + R9A07G043_DMAC_RST_ASYNC, +}; + #ifdef CONFIG_ARM64 static const unsigned int r9a07g043_no_pm_mod_clks[] = { MOD_CLK_BASE + R9A07G043_CRU_SYSCLK, @@ -420,5 +425,9 @@ const struct rzg2l_cpg_info r9a07g043_cpg_info = { .num_resets = R9A07G043_IAX45_RESETN + 1, /* Last reset ID + 1 */ #endif + /* Critical Resets */ + .crit_resets = r9a07g043_crit_resets, + .num_crit_resets = ARRAY_SIZE(r9a07g043_crit_resets), + .has_clk_mon_regs = true, }; diff --git a/drivers/clk/renesas/r9a07g044-cpg.c b/drivers/clk/renesas/r9a07g044-cpg.c index 0dd264877b9a..2d3487203bf5 100644 --- a/drivers/clk/renesas/r9a07g044-cpg.c +++ b/drivers/clk/renesas/r9a07g044-cpg.c @@ -489,6 +489,11 @@ static const unsigned int r9a07g044_crit_mod_clks[] __initconst = { MOD_CLK_BASE + R9A07G044_DMAC_ACLK, }; +static const unsigned int r9a07g044_crit_resets[] = { + R9A07G044_DMAC_ARESETN, + R9A07G044_DMAC_RST_ASYNC, +}; + static const unsigned int r9a07g044_no_pm_mod_clks[] = { MOD_CLK_BASE + R9A07G044_CRU_SYSCLK, MOD_CLK_BASE + R9A07G044_CRU_VCLK, @@ -519,6 +524,10 @@ const struct rzg2l_cpg_info r9a07g044_cpg_info = { .resets = r9a07g044_resets, .num_resets = R9A07G044_TSU_PRESETN + 1, /* Last reset ID + 1 */ + /* Critical Resets */ + .crit_resets = r9a07g044_crit_resets, + .num_crit_resets = ARRAY_SIZE(r9a07g044_crit_resets), + .has_clk_mon_regs = true, }; #endif @@ -548,6 +557,10 @@ const struct rzg2l_cpg_info r9a07g054_cpg_info = { .resets = r9a07g044_resets, .num_resets = R9A07G054_STPAI_ARESETN + 1, /* Last reset ID + 1 */ + /* Critical Resets */ + .crit_resets = r9a07g044_crit_resets, + .num_crit_resets = ARRAY_SIZE(r9a07g044_crit_resets), + .has_clk_mon_regs = true, }; #endif diff --git a/drivers/clk/renesas/r9a08g045-cpg.c b/drivers/clk/renesas/r9a08g045-cpg.c index 79e7b19c7882..1232fec913eb 100644 --- a/drivers/clk/renesas/r9a08g045-cpg.c +++ b/drivers/clk/renesas/r9a08g045-cpg.c @@ -361,6 +361,11 @@ static const unsigned int r9a08g045_crit_mod_clks[] __initconst = { MOD_CLK_BASE + R9A08G045_VBAT_BCLK, }; +static const unsigned int r9a08g045_crit_resets[] = { + R9A08G045_DMAC_ARESETN, + R9A08G045_DMAC_RST_ASYNC, +}; + static const unsigned int r9a08g045_no_pm_mod_clks[] = { MOD_CLK_BASE + R9A08G045_PCI_CLKL1PM, }; @@ -389,5 +394,9 @@ const struct rzg2l_cpg_info r9a08g045_cpg_info = { .resets = r9a08g045_resets, .num_resets = R9A08G045_VBAT_BRESETN + 1, /* Last reset ID + 1 */ + /* Critical Resets */ + .crit_resets = r9a08g045_crit_resets, + .num_crit_resets = ARRAY_SIZE(r9a08g045_crit_resets), + .has_clk_mon_regs = true, }; diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c new file mode 100644 index 000000000000..6759957980f2 --- /dev/null +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G3L CPG driver + * + * Copyright (C) 2026 Renesas Electronics Corp. + */ + +#include +#include +#include +#include + +#include + +#include "rzg2l-cpg.h" + +/* RZ/G3L Specific registers. */ +#define G3L_CPG_PL2_DDIV (0x204) +#define G3L_CPG_PL3_DDIV (0x208) +#define G3L_CLKDIVSTATUS (0x280) + +/* RZ/G3L Specific division configuration. */ +#define G3L_DIVPL2A DDIV_PACK(G3L_CPG_PL2_DDIV, 0, 2) +#define G3L_DIVPL2B DDIV_PACK(G3L_CPG_PL2_DDIV, 4, 2) +#define G3L_DIVPL3A DDIV_PACK(G3L_CPG_PL3_DDIV, 0, 2) + +/* RZ/G3L Clock status configuration. */ +#define G3L_DIVPL2A_STS DDIV_PACK(G3L_CLKDIVSTATUS, 4, 1) +#define G3L_DIVPL2B_STS DDIV_PACK(G3L_CLKDIVSTATUS, 5, 1) +#define G3L_DIVPL3A_STS DDIV_PACK(G3L_CLKDIVSTATUS, 8, 1) + +enum clk_ids { + /* Core Clock Outputs exported to DT */ + LAST_DT_CORE_CLK = R9A08G046_USB_SCLK, + + /* External Input Clocks */ + CLK_EXTAL, + CLK_ETH0_TXC_TX_CLK_IN, + CLK_ETH0_RXC_RX_CLK_IN, + CLK_ETH1_TXC_TX_CLK_IN, + CLK_ETH1_RXC_RX_CLK_IN, + + /* Internal Core Clocks */ + CLK_PLL2, + CLK_PLL2_DIV2, + CLK_PLL3, + CLK_PLL3_DIV2, + + /* Module Clocks */ + MOD_CLK_BASE, +}; + +/* Divider tables */ +static const struct clk_div_table dtable_4_128[] = { + { 0, 4 }, + { 1, 8 }, + { 2, 16 }, + { 3, 128 }, + { 0, 0 }, +}; + +static const struct clk_div_table dtable_8_256[] = { + { 0, 8 }, + { 1, 16 }, + { 2, 32 }, + { 3, 256 }, + { 0, 0 }, +}; + +static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { + /* External Clock Inputs */ + DEF_INPUT("extal", CLK_EXTAL), + DEF_INPUT("eth0_txc_tx_clk", CLK_ETH0_TXC_TX_CLK_IN), + DEF_INPUT("eth0_rxc_rx_clk", CLK_ETH0_RXC_RX_CLK_IN), + DEF_INPUT("eth1_txc_tx_clk", CLK_ETH1_TXC_TX_CLK_IN), + DEF_INPUT("eth1_rxc_rx_clk", CLK_ETH1_RXC_RX_CLK_IN), + + /* Internal Core Clocks */ + DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3), + DEF_FIXED(".pll3", CLK_PLL3, CLK_EXTAL, 200, 3), + DEF_FIXED(".pll2_div2", CLK_PLL2_DIV2, CLK_PLL2, 1, 2), + DEF_FIXED(".pll3_div2", CLK_PLL3_DIV2, CLK_PLL3, 1, 2), + + /* Core output clk */ + DEF_G3S_DIV("P0", R9A08G046_CLK_P0, CLK_PLL2_DIV2, G3L_DIVPL2B, G3L_DIVPL2B_STS, + dtable_8_256, 0, 0, 0, NULL), + DEF_G3S_DIV("P1", R9A08G046_CLK_P1, CLK_PLL3_DIV2, G3L_DIVPL3A, G3L_DIVPL3A_STS, + dtable_4_128, 0, 0, 0, NULL), + DEF_G3S_DIV("P3", R9A08G046_CLK_P3, CLK_PLL2_DIV2, G3L_DIVPL2A, G3L_DIVPL2A_STS, + dtable_4_128, 0, 0, 0, NULL), +}; + +static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { + DEF_MOD("gic_gicclk", R9A08G046_GIC600_GICCLK, R9A08G046_CLK_P1, 0x514, 0, + MSTOP(BUS_PERI_COM, BIT(12))), + DEF_MOD("ia55_pclk", R9A08G046_IA55_PCLK, R9A08G046_CLK_P0, 0x518, 0, + MSTOP(BUS_PERI_CPU, BIT(13))), + DEF_MOD("ia55_clk", R9A08G046_IA55_CLK, R9A08G046_CLK_P1, 0x518, 1, + MSTOP(BUS_PERI_CPU, BIT(13))), + DEF_MOD("dmac_aclk", R9A08G046_DMAC_ACLK, R9A08G046_CLK_P3, 0x52c, 0, + MSTOP(BUS_REG1, BIT(2))), + DEF_MOD("dmac_pclk", R9A08G046_DMAC_PCLK, R9A08G046_CLK_P3, 0x52c, 1, + MSTOP(BUS_REG1, BIT(3))), + DEF_MOD("scif0_clk_pck", R9A08G046_SCIF0_CLK_PCK, R9A08G046_CLK_P0, 0x584, 0, + MSTOP(BUS_MCPU2, BIT(1))), +}; + +static const struct rzg2l_reset r9a08g046_resets[] = { + DEF_RST(R9A08G046_GIC600_GICRESET_N, 0x814, 0), + DEF_RST(R9A08G046_GIC600_DBG_GICRESET_N, 0x814, 1), + DEF_RST(R9A08G046_IA55_RESETN, 0x818, 0), + DEF_RST(R9A08G046_DMAC_ARESETN, 0x82c, 0), + DEF_RST(R9A08G046_DMAC_RST_ASYNC, 0x82c, 1), + DEF_RST(R9A08G046_SCIF0_RST_SYSTEM_N, 0x884, 0), +}; + +static const unsigned int r9a08g046_crit_mod_clks[] __initconst = { + MOD_CLK_BASE + R9A08G046_GIC600_GICCLK, + MOD_CLK_BASE + R9A08G046_IA55_CLK, + MOD_CLK_BASE + R9A08G046_DMAC_ACLK, +}; + +static const unsigned int r9a08g046_crit_resets[] = { + R9A08G046_DMAC_ARESETN, + R9A08G046_DMAC_RST_ASYNC, +}; + +const struct rzg2l_cpg_info r9a08g046_cpg_info = { + /* Core Clocks */ + .core_clks = r9a08g046_core_clks, + .num_core_clks = ARRAY_SIZE(r9a08g046_core_clks), + .last_dt_core_clk = LAST_DT_CORE_CLK, + .num_total_core_clks = MOD_CLK_BASE, + + /* Critical Module Clocks */ + .crit_mod_clks = r9a08g046_crit_mod_clks, + .num_crit_mod_clks = ARRAY_SIZE(r9a08g046_crit_mod_clks), + + /* Module Clocks */ + .mod_clks = r9a08g046_mod_clks, + .num_mod_clks = ARRAY_SIZE(r9a08g046_mod_clks), + .num_hw_mod_clks = R9A08G046_BSC_X_BCK_BSC + 1, + + /* Resets */ + .resets = r9a08g046_resets, + .num_resets = R9A08G046_BSC_X_PRESET_BSC + 1, /* Last reset ID + 1 */ + + /* Critical Resets */ + .crit_resets = r9a08g046_crit_resets, + .num_crit_resets = ARRAY_SIZE(r9a08g046_crit_resets), + + .has_clk_mon_regs = true, +}; diff --git a/drivers/clk/renesas/r9a09g047-cpg.c b/drivers/clk/renesas/r9a09g047-cpg.c index 1e9896742a06..e59ac4a05a7f 100644 --- a/drivers/clk/renesas/r9a09g047-cpg.c +++ b/drivers/clk/renesas/r9a09g047-cpg.c @@ -224,6 +224,24 @@ static const struct rzv2h_mod_clk r9a09g047_mod_clks[] __initconst = { BUS_MSTOP(5, BIT(13))), DEF_MOD("wdt_3_clk_loco", CLK_QEXTAL, 5, 2, 2, 18, BUS_MSTOP(5, BIT(13))), + DEF_MOD("rspi_0_pclk", CLK_PLLCLN_DIV8, 5, 4, 2, 20, + BUS_MSTOP(11, BIT(0))), + DEF_MOD("rspi_0_pclk_sfr", CLK_PLLCLN_DIV8, 5, 5, 2, 21, + BUS_MSTOP(11, BIT(0))), + DEF_MOD("rspi_0_tclk", CLK_PLLCLN_DIV8, 5, 6, 2, 22, + BUS_MSTOP(11, BIT(0))), + DEF_MOD("rspi_1_pclk", CLK_PLLCLN_DIV8, 5, 7, 2, 23, + BUS_MSTOP(11, BIT(1))), + DEF_MOD("rspi_1_pclk_sfr", CLK_PLLCLN_DIV8, 5, 8, 2, 24, + BUS_MSTOP(11, BIT(1))), + DEF_MOD("rspi_1_tclk", CLK_PLLCLN_DIV8, 5, 9, 2, 25, + BUS_MSTOP(11, BIT(1))), + DEF_MOD("rspi_2_pclk", CLK_PLLCLN_DIV8, 5, 10, 2, 26, + BUS_MSTOP(11, BIT(2))), + DEF_MOD("rspi_2_pclk_sfr", CLK_PLLCLN_DIV8, 5, 11, 2, 27, + BUS_MSTOP(11, BIT(2))), + DEF_MOD("rspi_2_tclk", CLK_PLLCLN_DIV8, 5, 12, 2, 28, + BUS_MSTOP(11, BIT(2))), DEF_MOD("rsci0_pclk", CLK_PLLCLN_DIV16, 5, 13, 2, 29, BUS_MSTOP(11, BIT(3))), DEF_MOD("rsci0_tclk", CLK_PLLCLN_DIV16, 5, 14, 2, 30, @@ -424,6 +442,10 @@ static const struct rzv2h_mod_clk r9a09g047_mod_clks[] __initconst = { BUS_MSTOP(8, BIT(6))), DEF_MOD("gbeth_1_aclk_i", CLK_PLLDTY_DIV8, 12, 3, 6, 3, BUS_MSTOP(8, BIT(6))), + DEF_MOD("pcie_0_aclk", CLK_PLLDTY_ACPU_DIV2, 12, 4, 6, 4, + BUS_MSTOP(1, BIT(15))), + DEF_MOD("pcie_0_clk_pmu", CLK_PLLDTY_ACPU_DIV2, 12, 5, 6, 5, + BUS_MSTOP(1, BIT(15))), DEF_MOD("cru_0_aclk", CLK_PLLDTY_ACPU_DIV2, 13, 2, 6, 18, BUS_MSTOP(9, BIT(4))), DEF_MOD_NO_PM("cru_0_vclk", CLK_PLLVDO_CRU0, 13, 3, 6, 19, @@ -457,6 +479,12 @@ static const struct rzv2h_reset r9a09g047_resets[] __initconst = { DEF_RST(7, 6, 3, 7), /* WDT_1_RESET */ DEF_RST(7, 7, 3, 8), /* WDT_2_RESET */ DEF_RST(7, 8, 3, 9), /* WDT_3_RESET */ + DEF_RST(7, 11, 3, 12), /* RSPI_0_PRESETN */ + DEF_RST(7, 12, 3, 13), /* RSPI_0_TRESETN */ + DEF_RST(7, 13, 3, 14), /* RSPI_1_PRESETN */ + DEF_RST(7, 14, 3, 15), /* RSPI_1_TRESETN */ + DEF_RST(7, 15, 3, 16), /* RSPI_2_PRESETN */ + DEF_RST(8, 0, 3, 17), /* RSPI_2_TRESETN */ DEF_RST(8, 1, 3, 18), /* RSCI0_PRESETN */ DEF_RST(8, 2, 3, 19), /* RSCI0_TRESETN */ DEF_RST(8, 3, 3, 20), /* RSCI1_PRESETN */ @@ -503,6 +531,7 @@ static const struct rzv2h_reset r9a09g047_resets[] __initconst = { DEF_RST(10, 15, 5, 0), /* USB2_0_PRESETN */ DEF_RST(11, 0, 5, 1), /* GBETH_0_ARESETN_I */ DEF_RST(11, 1, 5, 2), /* GBETH_1_ARESETN_I */ + DEF_RST(11, 2, 5, 3), /* PCIE_0_ARESETN */ DEF_RST(12, 5, 5, 22), /* CRU_0_PRESETN */ DEF_RST(12, 6, 5, 23), /* CRU_0_ARESETN */ DEF_RST(12, 7, 5, 24), /* CRU_0_S_RESETN */ diff --git a/drivers/clk/renesas/r9a09g056-cpg.c b/drivers/clk/renesas/r9a09g056-cpg.c index fead173cae8b..51c1e322826a 100644 --- a/drivers/clk/renesas/r9a09g056-cpg.c +++ b/drivers/clk/renesas/r9a09g056-cpg.c @@ -273,22 +273,30 @@ static const struct rzv2h_mod_clk r9a09g056_mod_clks[] __initconst = { BUS_MSTOP(11, BIT(15))), DEF_MOD("gtm_7_pclk", CLK_PLLCLN_DIV16, 4, 10, 2, 10, BUS_MSTOP(12, BIT(0))), - DEF_MOD("wdt_0_clkp", CLK_PLLCM33_DIV16, 4, 11, 2, 11, - BUS_MSTOP(3, BIT(10))), - DEF_MOD("wdt_0_clk_loco", CLK_QEXTAL, 4, 12, 2, 12, - BUS_MSTOP(3, BIT(10))), DEF_MOD("wdt_1_clkp", CLK_PLLCLN_DIV16, 4, 13, 2, 13, BUS_MSTOP(1, BIT(0))), DEF_MOD("wdt_1_clk_loco", CLK_QEXTAL, 4, 14, 2, 14, BUS_MSTOP(1, BIT(0))), - DEF_MOD("wdt_2_clkp", CLK_PLLCLN_DIV16, 4, 15, 2, 15, - BUS_MSTOP(5, BIT(12))), - DEF_MOD("wdt_2_clk_loco", CLK_QEXTAL, 5, 0, 2, 16, - BUS_MSTOP(5, BIT(12))), - DEF_MOD("wdt_3_clkp", CLK_PLLCLN_DIV16, 5, 1, 2, 17, - BUS_MSTOP(5, BIT(13))), - DEF_MOD("wdt_3_clk_loco", CLK_QEXTAL, 5, 2, 2, 18, - BUS_MSTOP(5, BIT(13))), + DEF_MOD("rtc_0_clk_rtc", CLK_PLLCM33_DIV16, 5, 3, 2, 19, + BUS_MSTOP(3, BIT(11) | BIT(12))), + DEF_MOD("rspi_0_pclk", CLK_PLLCLN_DIV8, 5, 4, 2, 20, + BUS_MSTOP(11, BIT(0))), + DEF_MOD("rspi_0_pclk_sfr", CLK_PLLCLN_DIV8, 5, 5, 2, 21, + BUS_MSTOP(11, BIT(0))), + DEF_MOD("rspi_0_tclk", CLK_PLLCLN_DIV8, 5, 6, 2, 22, + BUS_MSTOP(11, BIT(0))), + DEF_MOD("rspi_1_pclk", CLK_PLLCLN_DIV8, 5, 7, 2, 23, + BUS_MSTOP(11, BIT(1))), + DEF_MOD("rspi_1_pclk_sfr", CLK_PLLCLN_DIV8, 5, 8, 2, 24, + BUS_MSTOP(11, BIT(1))), + DEF_MOD("rspi_1_tclk", CLK_PLLCLN_DIV8, 5, 9, 2, 25, + BUS_MSTOP(11, BIT(1))), + DEF_MOD("rspi_2_pclk", CLK_PLLCLN_DIV8, 5, 10, 2, 26, + BUS_MSTOP(11, BIT(2))), + DEF_MOD("rspi_2_pclk_sfr", CLK_PLLCLN_DIV8, 5, 11, 2, 27, + BUS_MSTOP(11, BIT(2))), + DEF_MOD("rspi_2_tclk", CLK_PLLCLN_DIV8, 5, 12, 2, 28, + BUS_MSTOP(11, BIT(2))), DEF_MOD("rsci0_pclk", CLK_PLLCLN_DIV16, 5, 13, 2, 29, BUS_MSTOP(11, BIT(3))), DEF_MOD("rsci0_tclk", CLK_PLLCLN_DIV16, 5, 14, 2, 30, @@ -389,24 +397,6 @@ static const struct rzv2h_mod_clk r9a09g056_mod_clks[] __initconst = { BUS_MSTOP(11, BIT(12))), DEF_MOD("rsci9_ps_ps1_n", CLK_PLLCLN_DIV64, 8, 14, 4, 14, BUS_MSTOP(11, BIT(12))), - DEF_MOD("rspi_0_pclk", CLK_PLLCLN_DIV8, 5, 4, 2, 20, - BUS_MSTOP(11, BIT(0))), - DEF_MOD("rspi_0_pclk_sfr", CLK_PLLCLN_DIV8, 5, 5, 2, 21, - BUS_MSTOP(11, BIT(0))), - DEF_MOD("rspi_0_tclk", CLK_PLLCLN_DIV8, 5, 6, 2, 22, - BUS_MSTOP(11, BIT(0))), - DEF_MOD("rspi_1_pclk", CLK_PLLCLN_DIV8, 5, 7, 2, 23, - BUS_MSTOP(11, BIT(1))), - DEF_MOD("rspi_1_pclk_sfr", CLK_PLLCLN_DIV8, 5, 8, 2, 24, - BUS_MSTOP(11, BIT(1))), - DEF_MOD("rspi_1_tclk", CLK_PLLCLN_DIV8, 5, 9, 2, 25, - BUS_MSTOP(11, BIT(1))), - DEF_MOD("rspi_2_pclk", CLK_PLLCLN_DIV8, 5, 10, 2, 26, - BUS_MSTOP(11, BIT(2))), - DEF_MOD("rspi_2_pclk_sfr", CLK_PLLCLN_DIV8, 5, 11, 2, 27, - BUS_MSTOP(11, BIT(2))), - DEF_MOD("rspi_2_tclk", CLK_PLLCLN_DIV8, 5, 12, 2, 28, - BUS_MSTOP(11, BIT(2))), DEF_MOD("scif_0_clk_pck", CLK_PLLCM33_DIV16, 8, 15, 4, 15, BUS_MSTOP(3, BIT(14))), DEF_MOD("i3c_0_pclkrw", CLK_PLLCLN_DIV16, 9, 0, 4, 16, @@ -503,6 +493,10 @@ static const struct rzv2h_mod_clk r9a09g056_mod_clks[] __initconst = { BUS_MSTOP(8, BIT(6))), DEF_MOD("gbeth_1_aclk_i", CLK_PLLDTY_DIV8, 12, 3, 6, 3, BUS_MSTOP(8, BIT(6))), + DEF_MOD("pcie_0_aclk", CLK_PLLDTY_ACPU_DIV2, 12, 4, 6, 4, + BUS_MSTOP(1, BIT(15))), + DEF_MOD("pcie_0_clk_pmu", CLK_PLLDTY_ACPU_DIV2, 12, 5, 6, 5, + BUS_MSTOP(1, BIT(15))), DEF_MOD("cru_0_aclk", CLK_PLLDTY_ACPU_DIV2, 13, 2, 6, 18, BUS_MSTOP(9, BIT(4))), DEF_MOD_NO_PM("cru_0_vclk", CLK_PLLVDO_CRU0, 13, 3, 6, 19, @@ -569,10 +563,7 @@ static const struct rzv2h_reset r9a09g056_resets[] __initconst = { DEF_RST(7, 2, 3, 3), /* GTM_5_PRESETZ */ DEF_RST(7, 3, 3, 4), /* GTM_6_PRESETZ */ DEF_RST(7, 4, 3, 5), /* GTM_7_PRESETZ */ - DEF_RST(7, 5, 3, 6), /* WDT_0_RESET */ DEF_RST(7, 6, 3, 7), /* WDT_1_RESET */ - DEF_RST(7, 7, 3, 8), /* WDT_2_RESET */ - DEF_RST(7, 8, 3, 9), /* WDT_3_RESET */ DEF_RST(8, 1, 3, 18), /* RSCI0_PRESETN */ DEF_RST(8, 2, 3, 19), /* RSCI0_TRESETN */ DEF_RST(8, 3, 3, 20), /* RSCI1_PRESETN */ @@ -593,6 +584,8 @@ static const struct rzv2h_reset r9a09g056_resets[] __initconst = { DEF_RST(9, 2, 4, 3), /* RSCI8_TRESETN */ DEF_RST(9, 3, 4, 4), /* RSCI9_PRESETN */ DEF_RST(9, 4, 4, 5), /* RSCI9_TRESETN */ + DEF_RST(7, 9, 3, 10), /* RTC_0_RST_RTC */ + DEF_RST(7, 10, 3, 11), /* RTC_0_RST_RTC_V */ DEF_RST(7, 11, 3, 12), /* RSPI_0_PRESETN */ DEF_RST(7, 12, 3, 13), /* RSPI_0_TRESETN */ DEF_RST(7, 13, 3, 14), /* RSPI_1_PRESETN */ @@ -624,6 +617,7 @@ static const struct rzv2h_reset r9a09g056_resets[] __initconst = { DEF_RST(10, 15, 5, 0), /* USB2_0_PRESETN */ DEF_RST(11, 0, 5, 1), /* GBETH_0_ARESETN_I */ DEF_RST(11, 1, 5, 2), /* GBETH_1_ARESETN_I */ + DEF_RST(11, 2, 5, 3), /* PCIE_0_ARESETN */ DEF_RST(12, 5, 5, 22), /* CRU_0_PRESETN */ DEF_RST(12, 6, 5, 23), /* CRU_0_ARESETN */ DEF_RST(12, 7, 5, 24), /* CRU_0_S_RESETN */ diff --git a/drivers/clk/renesas/r9a09g057-cpg.c b/drivers/clk/renesas/r9a09g057-cpg.c index 6943cad318b5..2fa5a620bbd9 100644 --- a/drivers/clk/renesas/r9a09g057-cpg.c +++ b/drivers/clk/renesas/r9a09g057-cpg.c @@ -280,22 +280,30 @@ static const struct rzv2h_mod_clk r9a09g057_mod_clks[] __initconst = { BUS_MSTOP(11, BIT(15))), DEF_MOD("gtm_7_pclk", CLK_PLLCLN_DIV16, 4, 10, 2, 10, BUS_MSTOP(12, BIT(0))), - DEF_MOD("wdt_0_clkp", CLK_PLLCM33_DIV16, 4, 11, 2, 11, - BUS_MSTOP(3, BIT(10))), - DEF_MOD("wdt_0_clk_loco", CLK_QEXTAL, 4, 12, 2, 12, - BUS_MSTOP(3, BIT(10))), DEF_MOD("wdt_1_clkp", CLK_PLLCLN_DIV16, 4, 13, 2, 13, BUS_MSTOP(1, BIT(0))), DEF_MOD("wdt_1_clk_loco", CLK_QEXTAL, 4, 14, 2, 14, BUS_MSTOP(1, BIT(0))), - DEF_MOD("wdt_2_clkp", CLK_PLLCLN_DIV16, 4, 15, 2, 15, - BUS_MSTOP(5, BIT(12))), - DEF_MOD("wdt_2_clk_loco", CLK_QEXTAL, 5, 0, 2, 16, - BUS_MSTOP(5, BIT(12))), - DEF_MOD("wdt_3_clkp", CLK_PLLCLN_DIV16, 5, 1, 2, 17, - BUS_MSTOP(5, BIT(13))), - DEF_MOD("wdt_3_clk_loco", CLK_QEXTAL, 5, 2, 2, 18, - BUS_MSTOP(5, BIT(13))), + DEF_MOD("rtc_0_clk_rtc", CLK_PLLCM33_DIV16, 5, 3, 2, 19, + BUS_MSTOP(3, BIT(11) | BIT(12))), + DEF_MOD("rspi_0_pclk", CLK_PLLCLN_DIV8, 5, 4, 2, 20, + BUS_MSTOP(11, BIT(0))), + DEF_MOD("rspi_0_pclk_sfr", CLK_PLLCLN_DIV8, 5, 5, 2, 21, + BUS_MSTOP(11, BIT(0))), + DEF_MOD("rspi_0_tclk", CLK_PLLCLN_DIV8, 5, 6, 2, 22, + BUS_MSTOP(11, BIT(0))), + DEF_MOD("rspi_1_pclk", CLK_PLLCLN_DIV8, 5, 7, 2, 23, + BUS_MSTOP(11, BIT(1))), + DEF_MOD("rspi_1_pclk_sfr", CLK_PLLCLN_DIV8, 5, 8, 2, 24, + BUS_MSTOP(11, BIT(1))), + DEF_MOD("rspi_1_tclk", CLK_PLLCLN_DIV8, 5, 9, 2, 25, + BUS_MSTOP(11, BIT(1))), + DEF_MOD("rspi_2_pclk", CLK_PLLCLN_DIV8, 5, 10, 2, 26, + BUS_MSTOP(11, BIT(2))), + DEF_MOD("rspi_2_pclk_sfr", CLK_PLLCLN_DIV8, 5, 11, 2, 27, + BUS_MSTOP(11, BIT(2))), + DEF_MOD("rspi_2_tclk", CLK_PLLCLN_DIV8, 5, 12, 2, 28, + BUS_MSTOP(11, BIT(2))), DEF_MOD("rsci0_pclk", CLK_PLLCLN_DIV16, 5, 13, 2, 29, BUS_MSTOP(11, BIT(3))), DEF_MOD("rsci0_tclk", CLK_PLLCLN_DIV16, 5, 14, 2, 30, @@ -396,26 +404,6 @@ static const struct rzv2h_mod_clk r9a09g057_mod_clks[] __initconst = { BUS_MSTOP(11, BIT(12))), DEF_MOD("rsci9_ps_ps1_n", CLK_PLLCLN_DIV64, 8, 14, 4, 14, BUS_MSTOP(11, BIT(12))), - DEF_MOD("rtc_0_clk_rtc", CLK_PLLCM33_DIV16, 5, 3, 2, 19, - BUS_MSTOP(3, BIT(11) | BIT(12))), - DEF_MOD("rspi_0_pclk", CLK_PLLCLN_DIV8, 5, 4, 2, 20, - BUS_MSTOP(11, BIT(0))), - DEF_MOD("rspi_0_pclk_sfr", CLK_PLLCLN_DIV8, 5, 5, 2, 21, - BUS_MSTOP(11, BIT(0))), - DEF_MOD("rspi_0_tclk", CLK_PLLCLN_DIV8, 5, 6, 2, 22, - BUS_MSTOP(11, BIT(0))), - DEF_MOD("rspi_1_pclk", CLK_PLLCLN_DIV8, 5, 7, 2, 23, - BUS_MSTOP(11, BIT(1))), - DEF_MOD("rspi_1_pclk_sfr", CLK_PLLCLN_DIV8, 5, 8, 2, 24, - BUS_MSTOP(11, BIT(1))), - DEF_MOD("rspi_1_tclk", CLK_PLLCLN_DIV8, 5, 9, 2, 25, - BUS_MSTOP(11, BIT(1))), - DEF_MOD("rspi_2_pclk", CLK_PLLCLN_DIV8, 5, 10, 2, 26, - BUS_MSTOP(11, BIT(2))), - DEF_MOD("rspi_2_pclk_sfr", CLK_PLLCLN_DIV8, 5, 11, 2, 27, - BUS_MSTOP(11, BIT(2))), - DEF_MOD("rspi_2_tclk", CLK_PLLCLN_DIV8, 5, 12, 2, 28, - BUS_MSTOP(11, BIT(2))), DEF_MOD("scif_0_clk_pck", CLK_PLLCM33_DIV16, 8, 15, 4, 15, BUS_MSTOP(3, BIT(14))), DEF_MOD("i3c_0_pclkrw", CLK_PLLCLN_DIV16, 9, 0, 4, 16, @@ -520,6 +508,10 @@ static const struct rzv2h_mod_clk r9a09g057_mod_clks[] __initconst = { BUS_MSTOP(8, BIT(6))), DEF_MOD("gbeth_1_aclk_i", CLK_PLLDTY_DIV8, 12, 3, 6, 3, BUS_MSTOP(8, BIT(6))), + DEF_MOD("pcie_0_aclk", CLK_PLLDTY_ACPU_DIV2, 12, 4, 6, 4, + BUS_MSTOP(1, BIT(13) | BIT(15))), + DEF_MOD("pcie_0_clk_pmu", CLK_PLLDTY_ACPU_DIV2, 12, 5, 6, 5, + BUS_MSTOP(1, BIT(13) | BIT(15))), DEF_MOD("cru_0_aclk", CLK_PLLDTY_ACPU_DIV2, 13, 2, 6, 18, BUS_MSTOP(9, BIT(4))), DEF_MOD_NO_PM("cru_0_vclk", CLK_PLLVDO_CRU0, 13, 3, 6, 19, @@ -598,10 +590,7 @@ static const struct rzv2h_reset r9a09g057_resets[] __initconst = { DEF_RST(7, 2, 3, 3), /* GTM_5_PRESETZ */ DEF_RST(7, 3, 3, 4), /* GTM_6_PRESETZ */ DEF_RST(7, 4, 3, 5), /* GTM_7_PRESETZ */ - DEF_RST(7, 5, 3, 6), /* WDT_0_RESET */ DEF_RST(7, 6, 3, 7), /* WDT_1_RESET */ - DEF_RST(7, 7, 3, 8), /* WDT_2_RESET */ - DEF_RST(7, 8, 3, 9), /* WDT_3_RESET */ DEF_RST(8, 1, 3, 18), /* RSCI0_PRESETN */ DEF_RST(8, 2, 3, 19), /* RSCI0_TRESETN */ DEF_RST(8, 3, 3, 20), /* RSCI1_PRESETN */ @@ -657,6 +646,7 @@ static const struct rzv2h_reset r9a09g057_resets[] __initconst = { DEF_RST(10, 15, 5, 0), /* USB2_0_PRESETN */ DEF_RST(11, 0, 5, 1), /* GBETH_0_ARESETN_I */ DEF_RST(11, 1, 5, 2), /* GBETH_1_ARESETN_I */ + DEF_RST(11, 2, 5, 3), /* PCIE_0_ARESETN */ DEF_RST(12, 5, 5, 22), /* CRU_0_PRESETN */ DEF_RST(12, 6, 5, 23), /* CRU_0_ARESETN */ DEF_RST(12, 7, 5, 24), /* CRU_0_S_RESETN */ diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 64a432fd0e8a..26ea85cfaa02 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -569,7 +569,7 @@ fail: struct cpg_mssr_clk_domain { struct generic_pm_domain genpd; unsigned int num_core_pm_clks; - unsigned int core_pm_clks[]; + unsigned int core_pm_clks[] __counted_by(num_core_pm_clks); }; static struct cpg_mssr_clk_domain *cpg_mssr_clk_domain; @@ -667,7 +667,7 @@ static int __init cpg_mssr_add_clk_domain(struct device *dev, size_t pm_size = num_core_pm_clks * sizeof(core_pm_clks[0]); int ret; - pd = devm_kzalloc(dev, sizeof(*pd) + pm_size, GFP_KERNEL); + pd = devm_kzalloc(dev, struct_size(pd, core_pm_clks, num_core_pm_clks), GFP_KERNEL); if (!pd) return -ENOMEM; diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index c0584bab58a3..abfd8634d2be 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -1439,7 +1439,8 @@ static int rzg2l_mod_clock_mstop_show(struct seq_file *s, void *what) } DEFINE_SHOW_ATTRIBUTE(rzg2l_mod_clock_mstop); -static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable) +static int rzg2l_mod_clock_endisable_helper(struct clk_hw *hw, bool enable, + bool set_mstop_state) { struct mod_clock *clock = to_mod_clock(hw); struct rzg2l_cpg_priv *priv = clock->priv; @@ -1464,9 +1465,11 @@ static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable) scoped_guard(spinlock_irqsave, &priv->rmw_lock) { if (enable) { writel(value, priv->base + CLK_ON_R(reg)); - rzg2l_mod_clock_module_set_state(clock, false); + if (set_mstop_state) + rzg2l_mod_clock_module_set_state(clock, false); } else { - rzg2l_mod_clock_module_set_state(clock, true); + if (set_mstop_state) + rzg2l_mod_clock_module_set_state(clock, true); writel(value, priv->base + CLK_ON_R(reg)); } } @@ -1486,6 +1489,11 @@ static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable) return error; } +static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable) +{ + return rzg2l_mod_clock_endisable_helper(hw, enable, true); +} + static int rzg2l_mod_clock_enable(struct clk_hw *hw) { struct mod_clock *clock = to_mod_clock(hw); @@ -1586,6 +1594,35 @@ static struct mstop *rzg2l_mod_clock_get_mstop(struct rzg2l_cpg_priv *priv, u32 return NULL; } +static void rzg2l_mod_clock_init_mstop_helper(struct rzg2l_cpg_priv *priv, + struct mod_clock *clk) +{ + /* + * Out of reset all modules are enabled. Set module state in case + * associated clocks are disabled at probe/resume. Otherwise module + * is in invalid HW state. + */ + scoped_guard(spinlock_irqsave, &priv->rmw_lock) { + if (!rzg2l_mod_clock_is_enabled(&clk->hw)) + rzg2l_mod_clock_module_set_state(clk, true); + } +} + +static void rzg2l_mod_enable_crit_clock_init_mstop(struct rzg2l_cpg_priv *priv) +{ + struct mod_clock *clk; + struct clk_hw *hw; + + for_each_mod_clock(clk, hw, priv) { + if ((clk_hw_get_flags(&clk->hw) & CLK_IS_CRITICAL) && + (!rzg2l_mod_clock_is_enabled(&clk->hw))) + rzg2l_mod_clock_endisable_helper(&clk->hw, true, false); + + if (clk->mstop) + rzg2l_mod_clock_init_mstop_helper(priv, clk); + } +} + static void rzg2l_mod_clock_init_mstop(struct rzg2l_cpg_priv *priv) { struct mod_clock *clk; @@ -1595,15 +1632,7 @@ static void rzg2l_mod_clock_init_mstop(struct rzg2l_cpg_priv *priv) if (!clk->mstop) continue; - /* - * Out of reset all modules are enabled. Set module state - * in case associated clocks are disabled at probe. Otherwise - * module is in invalid HW state. - */ - scoped_guard(spinlock_irqsave, &priv->rmw_lock) { - if (!rzg2l_mod_clock_is_enabled(&clk->hw)) - rzg2l_mod_clock_module_set_state(clk, true); - } + rzg2l_mod_clock_init_mstop_helper(priv, clk); } } @@ -1765,6 +1794,13 @@ static int __rzg2l_cpg_assert(struct reset_controller_dev *rcdev, dev_dbg(rcdev->dev, "%s id:%ld offset:0x%x\n", assert ? "assert" : "deassert", id, CLK_RST_R(reg)); + if (assert) { + for (unsigned int i = 0; i < priv->info->num_crit_resets; i++) { + if (id == priv->info->crit_resets[i]) + return 0; + } + } + if (!assert) value |= mask; writel(value, priv->base + CLK_RST_R(reg)); @@ -1802,6 +1838,20 @@ static int rzg2l_cpg_deassert(struct reset_controller_dev *rcdev, return __rzg2l_cpg_assert(rcdev, id, false); } +static int rzg2l_cpg_deassert_crit_resets(struct reset_controller_dev *rcdev, + const struct rzg2l_cpg_info *info) +{ + int ret; + + for (unsigned int i = 0; i < info->num_crit_resets; i++) { + ret = rzg2l_cpg_deassert(rcdev, info->crit_resets[i]); + if (ret) + return ret; + } + + return 0; +} + static int rzg2l_cpg_reset(struct reset_controller_dev *rcdev, unsigned long id) { @@ -2051,6 +2101,10 @@ static int __init rzg2l_cpg_probe(struct platform_device *pdev) if (error) return error; + error = rzg2l_cpg_deassert_crit_resets(&priv->rcdev, info); + if (error) + return error; + debugfs_create_file("mstop", 0444, NULL, priv, &rzg2l_mod_clock_mstop_fops); return 0; } @@ -2058,8 +2112,13 @@ static int __init rzg2l_cpg_probe(struct platform_device *pdev) static int rzg2l_cpg_resume(struct device *dev) { struct rzg2l_cpg_priv *priv = dev_get_drvdata(dev); + int ret; - rzg2l_mod_clock_init_mstop(priv); + ret = rzg2l_cpg_deassert_crit_resets(&priv->rcdev, priv->info); + if (ret) + return ret; + + rzg2l_mod_enable_crit_clock_init_mstop(priv); return 0; } @@ -2093,6 +2152,12 @@ static const struct of_device_id rzg2l_cpg_match[] = { .data = &r9a08g045_cpg_info, }, #endif +#ifdef CONFIG_CLK_R9A08G046 + { + .compatible = "renesas,r9a08g046-cpg", + .data = &r9a08g046_cpg_info, + }, +#endif #ifdef CONFIG_CLK_R9A09G011 { .compatible = "renesas,r9a09g011-cpg", diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h index 55e815be16c8..10baf9e71a6e 100644 --- a/drivers/clk/renesas/rzg2l-cpg.h +++ b/drivers/clk/renesas/rzg2l-cpg.h @@ -276,6 +276,9 @@ struct rzg2l_reset { * @crit_mod_clks: Array with Module Clock IDs of critical clocks that * should not be disabled without a knowledgeable driver * @num_crit_mod_clks: Number of entries in crit_mod_clks[] + * @crit_resets: Array with Reset IDs of critical resets that should not be + * asserted without a knowledgeable driver + * @num_crit_resets: Number of entries in crit_resets[] * @has_clk_mon_regs: Flag indicating whether the SoC has CLK_MON registers */ struct rzg2l_cpg_info { @@ -302,6 +305,10 @@ struct rzg2l_cpg_info { const unsigned int *crit_mod_clks; unsigned int num_crit_mod_clks; + /* Critical Resets that should not be asserted */ + const unsigned int *crit_resets; + unsigned int num_crit_resets; + bool has_clk_mon_regs; }; @@ -309,6 +316,7 @@ extern const struct rzg2l_cpg_info r9a07g043_cpg_info; extern const struct rzg2l_cpg_info r9a07g044_cpg_info; extern const struct rzg2l_cpg_info r9a07g054_cpg_info; extern const struct rzg2l_cpg_info r9a08g045_cpg_info; +extern const struct rzg2l_cpg_info r9a08g046_cpg_info; extern const struct rzg2l_cpg_info r9a09g011_cpg_info; int rzg2l_cpg_sd_clk_mux_notifier(struct notifier_block *nb, unsigned long event, void *data); diff --git a/drivers/clk/rockchip/Kconfig b/drivers/clk/rockchip/Kconfig index 5cf1e0fd6fb3..7e1433502061 100644 --- a/drivers/clk/rockchip/Kconfig +++ b/drivers/clk/rockchip/Kconfig @@ -16,6 +16,13 @@ config CLK_PX30 help Build the driver for PX30 Clock Driver. +config CLK_RV1103B + bool "Rockchip RV1103B clock controller support" + depends on ARM || COMPILE_TEST + default y + help + Build the driver for RV1103B Clock Driver. + config CLK_RV110X bool "Rockchip RV110x clock controller support" depends on ARM || COMPILE_TEST diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index 4d8cbb2044c7..7c984ee006c6 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -18,6 +18,7 @@ clk-rockchip-y += gate-link.o clk-rockchip-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-$(CONFIG_CLK_PX30) += clk-px30.o +obj-$(CONFIG_CLK_RV1103B) += clk-rv1103b.o obj-$(CONFIG_CLK_RV110X) += clk-rv1108.o obj-$(CONFIG_CLK_RV1126) += clk-rv1126.o obj-$(CONFIG_CLK_RV1126B) += clk-rv1126b.o rst-rv1126b.o diff --git a/drivers/clk/rockchip/clk-rk3568.c b/drivers/clk/rockchip/clk-rk3568.c index 74eabf9b2ae2..d571c4b0c35f 100644 --- a/drivers/clk/rockchip/clk-rk3568.c +++ b/drivers/clk/rockchip/clk-rk3568.c @@ -827,6 +827,8 @@ static struct rockchip_clk_branch rk3568_clk_branches[] __initdata = { RK3568_CLKGATE_CON(12), 3, GFLAGS), GATE(CLK_PCIE20_AUX_NDFT, "clk_pcie20_aux_ndft", "xin24m", 0, RK3568_CLKGATE_CON(12), 4, GFLAGS), + GATE(CLK_PCIE20_PIPE_DFT, "clk_pcie20_pipe_dft", "aclk_pipe", CLK_IGNORE_UNUSED, + RK3568_CLKGATE_CON(12), 5, GFLAGS), GATE(ACLK_PCIE30X1_MST, "aclk_pcie30x1_mst", "aclk_pipe", 0, RK3568_CLKGATE_CON(12), 8, GFLAGS), GATE(ACLK_PCIE30X1_SLV, "aclk_pcie30x1_slv", "aclk_pipe", 0, @@ -837,6 +839,8 @@ static struct rockchip_clk_branch rk3568_clk_branches[] __initdata = { RK3568_CLKGATE_CON(12), 11, GFLAGS), GATE(CLK_PCIE30X1_AUX_NDFT, "clk_pcie30x1_aux_ndft", "xin24m", 0, RK3568_CLKGATE_CON(12), 12, GFLAGS), + GATE(CLK_PCIE30X1_PIPE_DFT, "clk_pcie30x1_pipe_dft", "aclk_pipe", CLK_IGNORE_UNUSED, + RK3568_CLKGATE_CON(12), 13, GFLAGS), GATE(ACLK_PCIE30X2_MST, "aclk_pcie30x2_mst", "aclk_pipe", 0, RK3568_CLKGATE_CON(13), 0, GFLAGS), GATE(ACLK_PCIE30X2_SLV, "aclk_pcie30x2_slv", "aclk_pipe", 0, @@ -847,6 +851,8 @@ static struct rockchip_clk_branch rk3568_clk_branches[] __initdata = { RK3568_CLKGATE_CON(13), 3, GFLAGS), GATE(CLK_PCIE30X2_AUX_NDFT, "clk_pcie30x2_aux_ndft", "xin24m", 0, RK3568_CLKGATE_CON(13), 4, GFLAGS), + GATE(CLK_PCIE30X2_PIPE_DFT, "clk_pcie30x2_pipe_dft", "aclk_pipe", CLK_IGNORE_UNUSED, + RK3568_CLKGATE_CON(13), 5, GFLAGS), GATE(ACLK_SATA0, "aclk_sata0", "aclk_pipe", 0, RK3568_CLKGATE_CON(11), 0, GFLAGS), GATE(CLK_SATA0_PMALIVE, "clk_sata0_pmalive", "gpll_20m", 0, diff --git a/drivers/clk/rockchip/clk-rv1103b.c b/drivers/clk/rockchip/clk-rv1103b.c new file mode 100644 index 000000000000..7da1fda5e1b9 --- /dev/null +++ b/drivers/clk/rockchip/clk-rv1103b.c @@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 Rockchip Electronics Co. Ltd. + * Author: Elaine Zhang + */ + +#include +#include +#include +#include +#include "clk.h" + +#define RV1103B_GRF_SOC_STATUS0 0x10 +#define RV1103B_FRAC_MAX_PRATE 1200000000 +#define PVTPLL_SRC_SEL_PVTPLL (BIT(0) | BIT(16)) + +enum rv1103b_plls { + dpll, + gpll, +}; + +static struct rockchip_pll_rate_table rv1103b_pll_rates[] = { + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ + RK3036_PLL_RATE(1200000000, 1, 100, 2, 1, 1, 0), + RK3036_PLL_RATE(1188000000, 1, 99, 2, 1, 1, 0), + RK3036_PLL_RATE(1000000000, 3, 250, 2, 1, 1, 0), + { /* sentinel */ }, +}; + +#define RV1103B_DIV_ACLK_CORE_MASK 0x1f +#define RV1103B_DIV_ACLK_CORE_SHIFT 0 +#define RV1103B_DIV_PCLK_DBG_MASK 0x1f +#define RV1103B_DIV_PCLK_DBG_SHIFT 8 + +#define RV1103B_CLKSEL0(_aclk_core) \ +{ \ + .reg = RV1103B_CORECLKSEL_CON(2), \ + .val = HIWORD_UPDATE(_aclk_core - 1, RV1103B_DIV_ACLK_CORE_MASK, \ + RV1103B_DIV_ACLK_CORE_SHIFT), \ +} + +#define RV1103B_CLKSEL1(_pclk_dbg) \ +{ \ + .reg = RV1103B_CORECLKSEL_CON(2), \ + .val = HIWORD_UPDATE(_pclk_dbg - 1, RV1103B_DIV_PCLK_DBG_MASK, \ + RV1103B_DIV_PCLK_DBG_SHIFT), \ +} + +#define RV1103B_CPUCLK_RATE(_prate, _aclk_core, _pclk_dbg) \ +{ \ + .prate = _prate, \ + .divs = { \ + RV1103B_CLKSEL0(_aclk_core), \ + RV1103B_CLKSEL1(_pclk_dbg), \ + }, \ +} + +static struct rockchip_cpuclk_rate_table rv1103b_cpuclk_rates[] __initdata = { + RV1103B_CPUCLK_RATE(1608000000, 4, 10), + RV1103B_CPUCLK_RATE(1512000000, 4, 10), + RV1103B_CPUCLK_RATE(1416000000, 4, 10), + RV1103B_CPUCLK_RATE(1296000000, 3, 10), + RV1103B_CPUCLK_RATE(1200000000, 3, 10), + RV1103B_CPUCLK_RATE(1188000000, 3, 8), + RV1103B_CPUCLK_RATE(1104000000, 2, 8), + RV1103B_CPUCLK_RATE(1008000000, 2, 8), + RV1103B_CPUCLK_RATE(816000000, 2, 6), + RV1103B_CPUCLK_RATE(600000000, 2, 4), + RV1103B_CPUCLK_RATE(594000000, 2, 4), + RV1103B_CPUCLK_RATE(408000000, 1, 3), + RV1103B_CPUCLK_RATE(396000000, 1, 3), +}; + +PNAME(mux_pll_p) = { "xin24m" }; +PNAME(mux_200m_100m_p) = { "clk_gpll_div6", "clk_gpll_div12" }; +PNAME(mux_gpll_24m_p) = { "gpll", "xin24m" }; +PNAME(mux_480m_400m_300m_200m_p) = { "clk_gpll_div2p5", "clk_gpll_div3", "clk_gpll_div4", "clk_gpll_div6" }; +PNAME(mux_480m_400m_300m_p) = { "clk_gpll_div2p5", "clk_gpll_div3", "clk_gpll_div4" }; +PNAME(mux_300m_200m_p) = { "clk_gpll_div4", "clk_gpll_div6" }; +PNAME(mux_600m_480m_400m_p) = { "clk_gpll_div2", "clk_gpll_div2p5", "clk_gpll_div3" }; +PNAME(mux_400m_300m_p) = { "clk_gpll_div3", "clk_gpll_div4" }; +PNAME(mux_100m_24m_p) = { "clk_gpll_div12", "xin24m" }; +PNAME(mux_200m_24m_p) = { "clk_gpll_div6", "xin24m" }; +PNAME(mux_200m_100m_50m_24m_p) = { "clk_gpll_div6", "clk_gpll_div12", "clk_gpll_div24", "xin24m" }; +PNAME(mux_300m_200m_100m_p) = { "clk_gpll_div4", "clk_gpll_div6", "clk_gpll_div12" }; +PNAME(sclk_uart0_src_p) = { "clk_uart0_src", "clk_uart0_frac", "xin24m" }; +PNAME(sclk_uart1_src_p) = { "clk_uart1_src", "clk_uart1_frac", "xin24m" }; +PNAME(sclk_uart2_src_p) = { "clk_uart2_src", "clk_uart2_frac", "xin24m" }; +PNAME(mclk_sai_src_p) = { "clk_sai_src", "clk_sai_frac", "mclk_sai_from_io", "xin_osc0_half" }; +PNAME(clk_freq_pwm0_src_p) = { "sclk_sai_from_io", "mclk_sai_from_io", "clk_testout_out" }; +PNAME(clk_counter_pwm0_src_p) = { "sclk_sai_from_io", "mclk_sai_from_io", "clk_testout_out" }; +PNAME(clk_mipi0_out2io_p) = { "clk_ref_mipi0", "xin24m" }; +PNAME(clk_mipi1_out2io_p) = { "clk_ref_mipi1", "xin24m" }; +PNAME(mclk_sai_out2io_p) = { "mclk_sai_src", "xin_osc0_half" }; +PNAME(aclk_npu_root_p) = { "clk_npu_src", "clk_npu_pvtpll" }; +PNAME(clk_core_vepu_p) = { "clk_vepu_src", "clk_vepu_pvtpll" }; +PNAME(lsclk_vi_root_p) = { "clk_gpll_div6", "lsclk_vi_100m" }; +PNAME(clk_core_isp_p) = { "clk_isp_src", "clk_isp_pvtpll_src" }; +PNAME(lsclk_pmu_root_p) = { "xin24m", "clk_rc_osc_io" }; +PNAME(xin_rc_div_p) = { "xin24m", "clk_rc_osc_io" }; +PNAME(clk_32k_p) = { "xin_rc_div", "clk_32k_rtc", "clk_32k_io" }; +PNAME(dbclk_pmu_gpio0_p) = { "xin24m", "clk_32k" }; +PNAME(sclk_sfc_2x_pmu1_p) = { "clk_gpll_div12", "clk_rc_osc_io" }; +PNAME(mux_armclk_p) = { "armclk_gpll", "clk_core_pvtpll" }; + +static struct rockchip_pll_clock rv1103b_pll_clks[] __initdata = { + [dpll] = PLL(pll_rk3328, PLL_DPLL, "dpll", mux_pll_p, + CLK_IS_CRITICAL, RV1103B_PLL_CON(16), + RV1103B_MODE_CON, 0, 10, 0, rv1103b_pll_rates), + [gpll] = PLL(pll_rk3328, PLL_GPLL, "gpll", mux_pll_p, + CLK_IS_CRITICAL, RV1103B_PLL_CON(24), + RV1103B_MODE_CON, 0, 10, 0, rv1103b_pll_rates), +}; + +#define MFLAGS CLK_MUX_HIWORD_MASK +#define DFLAGS CLK_DIVIDER_HIWORD_MASK +#define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE) + +static struct rockchip_clk_branch rv1103b_clk_uart0_fracmux __initdata = + MUX(SCLK_UART0_SRC, "sclk_uart0_src", sclk_uart0_src_p, CLK_SET_RATE_PARENT, + RV1103B_CLKSEL_CON(32), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rv1103b_clk_uart1_fracmux __initdata = + MUX(SCLK_UART1_SRC, "sclk_uart1_src", sclk_uart1_src_p, CLK_SET_RATE_PARENT, + RV1103B_CLKSEL_CON(32), 10, 2, MFLAGS); + +static struct rockchip_clk_branch rv1103b_clk_uart2_fracmux __initdata = + MUX(SCLK_UART2_SRC, "sclk_uart2_src", sclk_uart2_src_p, CLK_SET_RATE_PARENT, + RV1103B_CLKSEL_CON(32), 12, 2, MFLAGS); + +static struct rockchip_clk_branch rv1103b_rcdiv_pmu_fracmux __initdata = + MUX(CLK_32K, "clk_32k", clk_32k_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + RK3568_PMU_CLKSEL_CON(0), 0, 2, MFLAGS); + +static struct rockchip_clk_branch rv1103b_clk_branches[] __initdata = { + + /* Clock Definition */ + FACTOR(XIN_OSC0_HALF, "xin_osc0_half", "xin24m", 0, 1, 2), + + COMPOSITE_NOGATE(0, "armclk_gpll", mux_gpll_24m_p, CLK_IS_CRITICAL, + RV1103B_CLKSEL_CON(37), 12, 1, MFLAGS, 13, 3, DFLAGS), + + /* pd_top */ + COMPOSITE_NOMUX(CLK_GPLL_DIV24, "clk_gpll_div24", "gpll", 0, + RV1103B_CLKSEL_CON(0), 0, 5, DFLAGS, + RV1103B_CLKGATE_CON(0), 0, GFLAGS), + COMPOSITE_NOMUX(CLK_GPLL_DIV12, "clk_gpll_div12", "gpll", 0, + RV1103B_CLKSEL_CON(0), 5, 5, DFLAGS, + RV1103B_CLKGATE_CON(0), 1, GFLAGS), + COMPOSITE_NOMUX(CLK_GPLL_DIV6, "clk_gpll_div6", "gpll", 0, + RV1103B_CLKSEL_CON(1), 0, 5, DFLAGS, + RV1103B_CLKGATE_CON(0), 3, GFLAGS), + COMPOSITE_NOMUX(CLK_GPLL_DIV4, "clk_gpll_div4", "gpll", 0, + RV1103B_CLKSEL_CON(1), 10, 5, DFLAGS, + RV1103B_CLKGATE_CON(0), 5, GFLAGS), + COMPOSITE_NOMUX(CLK_GPLL_DIV3, "clk_gpll_div3", "gpll", 0, + RV1103B_CLKSEL_CON(2), 0, 5, DFLAGS, + RV1103B_CLKGATE_CON(0), 7, GFLAGS), + COMPOSITE_NOMUX_HALFDIV(CLK_GPLL_DIV2P5, "clk_gpll_div2p5", "gpll", 0, + RV1103B_CLKSEL_CON(2), 5, 5, DFLAGS, + RV1103B_CLKGATE_CON(0), 8, GFLAGS), + COMPOSITE_NOMUX(CLK_GPLL_DIV2, "clk_gpll_div2", "gpll", 0, + RV1103B_CLKSEL_CON(2), 10, 5, DFLAGS, + RV1103B_CLKGATE_CON(0), 9, GFLAGS), + COMPOSITE_NOMUX(CLK_UART0_SRC, "clk_uart0_src", "gpll", 0, + RV1103B_CLKSEL_CON(5), 0, 5, DFLAGS, + RV1103B_CLKGATE_CON(1), 0, GFLAGS), + COMPOSITE_NOMUX(CLK_UART1_SRC, "clk_uart1_src", "gpll", 0, + RV1103B_CLKSEL_CON(5), 5, 5, DFLAGS, + RV1103B_CLKGATE_CON(1), 1, GFLAGS), + COMPOSITE_NOMUX(CLK_UART2_SRC, "clk_uart2_src", "gpll", 0, + RV1103B_CLKSEL_CON(5), 10, 5, DFLAGS, + RV1103B_CLKGATE_CON(1), 2, GFLAGS), + COMPOSITE_FRACMUX(CLK_UART0_FRAC, "clk_uart0_frac", "clk_uart0_src", 0, + RV1103B_CLKSEL_CON(10), 0, + RV1103B_CLKGATE_CON(1), 6, GFLAGS, + &rv1103b_clk_uart0_fracmux), + COMPOSITE_FRACMUX(CLK_UART1_FRAC, "clk_uart1_frac", "clk_uart1_src", 0, + RV1103B_CLKSEL_CON(11), 0, + RV1103B_CLKGATE_CON(1), 7, GFLAGS, + &rv1103b_clk_uart1_fracmux), + COMPOSITE_FRACMUX(CLK_UART2_FRAC, "clk_uart2_frac", "clk_uart2_src", 0, + RV1103B_CLKSEL_CON(12), 0, + RV1103B_CLKGATE_CON(1), 8, GFLAGS, + &rv1103b_clk_uart2_fracmux), + GATE(SCLK_UART0, "sclk_uart0", "sclk_uart0_src", 0, + RV1103B_CLKGATE_CON(3), 3, GFLAGS), + GATE(SCLK_UART1, "sclk_uart1", "sclk_uart1_src", 0, + RV1103B_CLKGATE_CON(3), 4, GFLAGS), + GATE(SCLK_UART2, "sclk_uart2", "sclk_uart2_src", 0, + RV1103B_CLKGATE_CON(3), 8, GFLAGS), + + COMPOSITE_NOMUX(CLK_SAI_SRC, "clk_sai_src", "gpll", 0, + RV1103B_CLKSEL_CON(20), 0, 5, DFLAGS, + RV1103B_CLKGATE_CON(1), 12, GFLAGS), + MUX(MCLK_SAI_SRC, "mclk_sai_src", mclk_sai_src_p, CLK_SET_RATE_PARENT, + RV1103B_CLKSEL_CON(35), 10, 2, MFLAGS), + GATE(MCLK_SAI, "mclk_sai", "mclk_sai_src", 0, + RV1103B_CLKGATE_CON(5), 5, GFLAGS), + + COMPOSITE_NODIV(LSCLK_NPU_SRC, "lsclk_npu_src", mux_200m_100m_p, CLK_IS_CRITICAL, + RV1103B_CLKSEL_CON(30), 0, 1, MFLAGS, + RV1103B_CLKGATE_CON(2), 0, GFLAGS), + COMPOSITE(CLK_NPU_SRC, "clk_npu_src", mux_gpll_24m_p, 0, + RV1103B_CLKSEL_CON(37), 0, 1, MFLAGS, 1, 2, DFLAGS, + RV1103B_CLKGATE_CON(5), 12, GFLAGS), + COMPOSITE_NODIV(ACLK_VEPU_SRC, "aclk_vepu_src", mux_480m_400m_300m_200m_p, 0, + RV1103B_CLKSEL_CON(30), 8, 2, MFLAGS, + RV1103B_CLKGATE_CON(2), 4, GFLAGS), + COMPOSITE(CLK_VEPU_SRC, "clk_vepu_src", mux_gpll_24m_p, 0, + RV1103B_CLKSEL_CON(37), 4, 1, MFLAGS, 5, 2, DFLAGS, + RV1103B_CLKGATE_CON(5), 13, GFLAGS), + COMPOSITE_NODIV(ACLK_VI_SRC, "aclk_vi_src", mux_480m_400m_300m_p, CLK_IS_CRITICAL, + RV1103B_CLKSEL_CON(30), 12, 2, MFLAGS, + RV1103B_CLKGATE_CON(2), 8, GFLAGS), + COMPOSITE(CLK_ISP_SRC, "clk_isp_src", mux_gpll_24m_p, 0, + RV1103B_CLKSEL_CON(37), 8, 1, MFLAGS, 9, 2, DFLAGS, + RV1103B_CLKGATE_CON(5), 14, GFLAGS), + COMPOSITE_NODIV(DCLK_VICAP, "dclk_vicap", mux_300m_200m_p, 0, + RV1103B_CLKSEL_CON(30), 14, 1, MFLAGS, + RV1103B_CLKGATE_CON(2), 9, GFLAGS), + COMPOSITE(CCLK_EMMC, "cclk_emmc", mux_gpll_24m_p, 0, + RV1103B_CLKSEL_CON(31), 15, 1, MFLAGS, 0, 8, DFLAGS, + RV1103B_CLKGATE_CON(2), 10, GFLAGS), + COMPOSITE(CCLK_SDMMC0, "cclk_sdmmc0", mux_gpll_24m_p, 0, + RV1103B_CLKSEL_CON(32), 15, 1, MFLAGS, 0, 8, DFLAGS, + RV1103B_CLKGATE_CON(2), 11, GFLAGS), + COMPOSITE(SCLK_SFC_2X, "sclk_sfc_2x", mux_gpll_24m_p, 0, + RV1103B_CLKSEL_CON(33), 15, 1, MFLAGS, 0, 8, DFLAGS, + RV1103B_CLKGATE_CON(2), 12, GFLAGS), + COMPOSITE_NODIV(LSCLK_PERI_SRC, "lsclk_peri_src", mux_300m_200m_p, CLK_IS_CRITICAL, + RV1103B_CLKSEL_CON(31), 9, 1, MFLAGS, + RV1103B_CLKGATE_CON(3), 0, GFLAGS), + COMPOSITE_NODIV(ACLK_PERI_SRC, "aclk_peri_src", mux_600m_480m_400m_p, CLK_IS_CRITICAL, + RV1103B_CLKSEL_CON(31), 10, 2, MFLAGS, + RV1103B_CLKGATE_CON(3), 1, GFLAGS), + COMPOSITE_NODIV(HCLK_HPMCU, "hclk_hpmcu", mux_400m_300m_p, 0, + RV1103B_CLKSEL_CON(31), 12, 1, MFLAGS, + RV1103B_CLKGATE_CON(3), 2, GFLAGS), + COMPOSITE_NODIV(CLK_I2C_PMU, "clk_i2c_pmu", mux_100m_24m_p, 0, + RV1103B_CLKSEL_CON(34), 0, 1, MFLAGS, + RV1103B_CLKGATE_CON(4), 0, GFLAGS), + COMPOSITE_NODIV(CLK_I2C_PERI, "clk_i2c_peri", mux_200m_24m_p, 0, + RV1103B_CLKSEL_CON(34), 1, 1, MFLAGS, + RV1103B_CLKGATE_CON(4), 4, GFLAGS), + COMPOSITE_NODIV(CLK_SPI0, "clk_spi0", mux_200m_100m_50m_24m_p, 0, + RV1103B_CLKSEL_CON(34), 2, 2, MFLAGS, + RV1103B_CLKGATE_CON(4), 5, GFLAGS), + COMPOSITE_NODIV(CLK_PWM0_SRC, "clk_pwm0_src", mux_100m_24m_p, 0, + RV1103B_CLKSEL_CON(34), 12, 1, MFLAGS, + RV1103B_CLKGATE_CON(4), 10, GFLAGS), + COMPOSITE_NODIV(CLK_PWM1, "clk_pwm1", mux_100m_24m_p, 0, + RV1103B_CLKSEL_CON(34), 13, 1, MFLAGS, + RV1103B_CLKGATE_CON(4), 11, GFLAGS), + COMPOSITE_NODIV(CLK_PWM2, "clk_pwm2", mux_100m_24m_p, 0, + RV1103B_CLKSEL_CON(34), 14, 1, MFLAGS, + RV1103B_CLKGATE_CON(4), 12, GFLAGS), + COMPOSITE_NODIV(DCLK_DECOM_SRC, "dclk_decom_src", mux_480m_400m_300m_p, 0, + RV1103B_CLKSEL_CON(35), 0, 2, MFLAGS, + RV1103B_CLKGATE_CON(5), 0, GFLAGS), + COMPOSITE(CCLK_SDMMC1, "cclk_sdmmc1", mux_gpll_24m_p, 0, + RV1103B_CLKSEL_CON(36), 15, 1, MFLAGS, 0, 8, DFLAGS, + RV1103B_CLKGATE_CON(5), 1, GFLAGS), + COMPOSITE_NODIV(CLK_CORE_CRYPTO, "clk_core_crypto", mux_300m_200m_100m_p, 0, + RV1103B_CLKSEL_CON(35), 2, 2, MFLAGS, + RV1103B_CLKGATE_CON(5), 2, GFLAGS), + COMPOSITE_NODIV(CLK_PKA_CRYPTO, "clk_pka_crypto", mux_300m_200m_100m_p, 0, + RV1103B_CLKSEL_CON(35), 4, 2, MFLAGS, + RV1103B_CLKGATE_CON(5), 3, GFLAGS), + COMPOSITE_NODIV(CLK_CORE_RGA, "clk_core_rga", mux_400m_300m_p, 0, + RV1103B_CLKSEL_CON(35), 8, 1, MFLAGS, + RV1103B_CLKGATE_CON(5), 4, GFLAGS), + + GATE(PCLK_TOP_ROOT, "pclk_top_root", "clk_gpll_div12", CLK_IS_CRITICAL, + RV1103B_CLKGATE_CON(6), 0, GFLAGS), + COMPOSITE_NOMUX(CLK_REF_MIPI0, "clk_ref_mipi0", "clk_gpll_div2", 0, + RV1103B_CLKSEL_CON(40), 0, 5, DFLAGS, + RV1103B_CLKGATE_CON(6), 3, GFLAGS), + COMPOSITE_NODIV(CLK_MIPI0_OUT2IO, "clk_mipi0_out2io", clk_mipi0_out2io_p, CLK_SET_RATE_PARENT, + RV1103B_CLKSEL_CON(40), 6, 1, MFLAGS, + RV1103B_CLKGATE_CON(6), 4, GFLAGS), + COMPOSITE_NOMUX(CLK_REF_MIPI1, "clk_ref_mipi1", "clk_gpll_div2", 0, + RV1103B_CLKSEL_CON(40), 8, 5, DFLAGS, + RV1103B_CLKGATE_CON(6), 5, GFLAGS), + COMPOSITE_NODIV(CLK_MIPI1_OUT2IO, "clk_mipi1_out2io", clk_mipi1_out2io_p, CLK_SET_RATE_PARENT, + RV1103B_CLKSEL_CON(40), 14, 1, MFLAGS, + RV1103B_CLKGATE_CON(6), 6, GFLAGS), + COMPOSITE(MCLK_SAI_OUT2IO, "mclk_sai_out2io", mclk_sai_out2io_p, 0, + RV1103B_CLKSEL_CON(41), 7, 1, MFLAGS, 13, 3, DFLAGS, + RV1103B_CLKGATE_CON(6), 9, GFLAGS), + + /* pd_vpu */ + COMPOSITE_NODIV(ACLK_NPU_ROOT, "aclk_npu_root", aclk_npu_root_p, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + RV1103B_NPUCLKSEL_CON(0), 1, 1, MFLAGS, + RV1103B_NPUCLKGATE_CON(0), 1, GFLAGS), + GATE(HCLK_RKNN, "hclk_rknn", "lsclk_npu_src", 0, + RV1103B_NPUCLKGATE_CON(0), 4, GFLAGS), + GATE(ACLK_RKNN, "aclk_rknn", "aclk_npu_root", 0, + RV1103B_NPUCLKGATE_CON(0), 5, GFLAGS), + + /* pd_vepu */ + COMPOSITE_NOMUX(LSCLK_VEPU_ROOT, "lsclk_vepu_root", "aclk_vepu_src", CLK_IS_CRITICAL, + RV1103B_VEPUCLKSEL_CON(0), 2, 2, DFLAGS, + RV1103B_VEPUCLKGATE_CON(0), 0, GFLAGS), + GATE(HCLK_VEPU, "hclk_vepu", "lsclk_vepu_root", 0, + RV1103B_VEPUCLKGATE_CON(0), 4, GFLAGS), + GATE(ACLK_VEPU, "aclk_vepu", "aclk_vepu_src", 0, + RV1103B_VEPUCLKGATE_CON(0), 5, GFLAGS), + COMPOSITE_NODIV(CLK_CORE_VEPU, "clk_core_vepu", clk_core_vepu_p, 0, + RV1103B_VEPUCLKSEL_CON(0), 1, 1, MFLAGS, + RV1103B_VEPUCLKGATE_CON(0), 6, GFLAGS), + GATE(PCLK_ACODEC, "pclk_acodec", "lsclk_vepu_root", 0, + RV1103B_VEPUCLKGATE_CON(0), 13, GFLAGS), + GATE(PCLK_USBPHY, "pclk_usbphy", "lsclk_vepu_root", 0, + RV1103B_VEPUCLKGATE_CON(0), 14, GFLAGS), + + /* pd_vi */ + FACTOR(LSCLK_VI_100M, "lsclk_vi_100m", "clk_gpll_div6", 0, 1, 2), + COMPOSITE_NODIV(LSCLK_VI_ROOT, "lsclk_vi_root", lsclk_vi_root_p, CLK_IS_CRITICAL, + RV1103B_VICLKSEL_CON(0), 3, 1, MFLAGS, + RV1103B_VICLKGATE_CON(0), 0, GFLAGS), + GATE(HCLK_ISP, "hclk_isp", "lsclk_vi_root", 0, + RV1103B_VICLKGATE_CON(0), 4, GFLAGS), + GATE(ACLK_ISP, "aclk_isp", "aclk_vi_src", 0, + RV1103B_VICLKGATE_CON(0), 5, GFLAGS), + COMPOSITE_NODIV(CLK_CORE_ISP, "clk_core_isp", clk_core_isp_p, 0, + RV1103B_VICLKSEL_CON(0), 1, 1, MFLAGS, + RV1103B_VICLKGATE_CON(0), 6, GFLAGS), + GATE(ACLK_VICAP, "aclk_vicap", "aclk_vi_src", 0, + RV1103B_VICLKGATE_CON(1), 2, GFLAGS), + GATE(HCLK_VICAP, "hclk_vicap", "lsclk_vi_root", 0, + RV1103B_VICLKGATE_CON(1), 3, GFLAGS), + GATE(ISP0CLK_VICAP, "isp0clk_vicap", "clk_core_isp", 0, + RV1103B_VICLKGATE_CON(1), 8, GFLAGS), + GATE(PCLK_CSI2HOST0, "pclk_csi2host0", "lsclk_vi_root", 0, + RV1103B_VICLKGATE_CON(1), 9, GFLAGS), + GATE(PCLK_CSI2HOST1, "pclk_csi2host1", "lsclk_vi_root", 0, + RV1103B_VICLKGATE_CON(1), 11, GFLAGS), + GATE(HCLK_EMMC, "hclk_emmc", "lsclk_vi_root", 0, + RV1103B_VICLKGATE_CON(1), 13, GFLAGS), + GATE(HCLK_SFC, "hclk_sfc", "lsclk_vi_root", 0, + RV1103B_VICLKGATE_CON(1), 14, GFLAGS), + GATE(HCLK_SFC_XIP, "hclk_sfc_xip", "lsclk_vi_root", 0, + RV1103B_VICLKGATE_CON(1), 15, GFLAGS), + GATE(HCLK_SDMMC0, "hclk_sdmmc0", "lsclk_vi_root", 0, + RV1103B_VICLKGATE_CON(2), 0, GFLAGS), + GATE(PCLK_CSIPHY, "pclk_csiphy", "lsclk_vi_root", 0, + RV1103B_VICLKGATE_CON(2), 2, GFLAGS), + GATE(PCLK_GPIO1, "pclk_gpio1", "lsclk_vi_root", 0, + RV1103B_VICLKGATE_CON(2), 3, GFLAGS), + GATE(DBCLK_GPIO1, "dbclk_gpio1", "xin24m", 0, + RV1103B_VICLKGATE_CON(2), 4, GFLAGS), + + /* pd_ddr */ + GATE(LSCLK_DDR_ROOT, "lsclk_ddr_root", "clk_gpll_div12", CLK_IS_CRITICAL, + RV1103B_DDRCLKGATE_CON(0), 0, GFLAGS), + GATE(CLK_TIMER_DDRMON, "clk_timer_ddrmon", "xin24m", 0, + RV1103B_DDRCLKGATE_CON(0), 4, GFLAGS), + FACTOR(0, "sclk_ddr", "dpll", 0, 1, 2), + + /* pd_pmu */ + COMPOSITE(LSCLK_PMU_ROOT, "lsclk_pmu_root", lsclk_pmu_root_p, CLK_IS_CRITICAL, + RV1103B_PMUCLKSEL_CON(2), 4, 1, MFLAGS, 0, 2, DFLAGS, + RV1103B_PMUCLKGATE_CON(0), 0, GFLAGS), + GATE(PCLK_PMU, "pclk_pmu", "lsclk_pmu_root", CLK_IS_CRITICAL, + RV1103B_PMUCLKGATE_CON(0), 2, GFLAGS), + MUX(XIN_RC_SRC, "xin_rc_src", xin_rc_div_p, 0, + RV1103B_PMUCLKSEL_CON(0), 2, 1, MFLAGS), + COMPOSITE_FRACMUX(XIN_RC_DIV, "xin_rc_div", "xin_rc_src", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + RV1103B_PMUCLKSEL_CON(1), 0, + RV1103B_PMUCLKGATE_CON(0), 3, GFLAGS, + &rv1103b_rcdiv_pmu_fracmux), + GATE(PCLK_PMU_GPIO0, "pclk_pmu_gpio0", "lsclk_pmu_root", 0, + RV1103B_PMUCLKGATE_CON(0), 4, GFLAGS), + COMPOSITE_NODIV(DBCLK_PMU_GPIO0, "dbclk_pmu_gpio0", dbclk_pmu_gpio0_p, 0, + RK3568_PMU_CLKSEL_CON(0), 3, 1, MFLAGS, + RV1103B_PMUCLKGATE_CON(0), 5, GFLAGS), + GATE(PCLK_PWM0, "pclk_pwm0", "lsclk_pmu_root", 0, + RV1103B_PMUCLKGATE_CON(2), 0, GFLAGS), + GATE(CLK_PWM0, "clk_pwm0", "clk_pwm0_src", 0, + RV1103B_PMUCLKGATE_CON(2), 1, GFLAGS), + GATE(CLK_OSC_PWM0, "clk_osc_pwm0", "xin24m", 0, + RV1103B_PMUCLKGATE_CON(2), 2, GFLAGS), + GATE(CLK_RC_PWM0, "clk_rc_pwm0", "clk_32k", 0, + RV1103B_PMUCLKGATE_CON(2), 3, GFLAGS), + GATE(PCLK_I2C0, "pclk_i2c0", "lsclk_pmu_root", 0, + RV1103B_PMUCLKGATE_CON(0), 12, GFLAGS), + GATE(CLK_I2C0, "clk_i2c0", "clk_i2c_pmu", 0, + RV1103B_PMUCLKGATE_CON(0), 13, GFLAGS), + GATE(PCLK_UART0, "pclk_uart0", "lsclk_pmu_root", 0, + RV1103B_PMUCLKGATE_CON(0), 14, GFLAGS), + GATE(CLK_REFOUT, "clk_refout", "xin24m", 0, + RV1103B_PMUCLKGATE_CON(1), 4, GFLAGS), + GATE(CLK_PREROLL, "clk_preroll", "lsclk_pmu_root", 0, + RV1103B_PMUCLKGATE_CON(1), 6, GFLAGS), + GATE(CLK_PREROLL_32K, "clk_preroll_32k", "clk_32k", 0, + RV1103B_PMUCLKGATE_CON(1), 7, GFLAGS), + GATE(CLK_LPMCU_PMU, "clk_lpmcu_pmu", "lsclk_pmu_root", 0, + RV1103B_PMUCLKGATE_CON(2), 12, GFLAGS), + + /* pd_pmu1 */ + GATE(PCLK_SPI2AHB, "pclk_spi2ahb", "lsclk_pmu_root", 0, + RV1103B_PMU1CLKGATE_CON(0), 0, GFLAGS), + GATE(HCLK_SPI2AHB, "hclk_spi2ahb", "lsclk_pmu_root", 0, + RV1103B_PMU1CLKGATE_CON(0), 1, GFLAGS), + GATE(PCLK_WDT_LPMCU, "pclk_wdt_lpmcu", "lsclk_pmu_root", 0, + RV1103B_PMU1CLKGATE_CON(0), 9, GFLAGS), + GATE(TCLK_WDT_LPMCU, "tclk_wdt_lpmcu", "xin24m", 0, + RV1103B_PMU1CLKGATE_CON(0), 10, GFLAGS), + GATE(HCLK_SFC_PMU1, "hclk_sfc_pmu1", "lsclk_pmu_root", 0, + RV1103B_PMU1CLKGATE_CON(0), 12, GFLAGS), + GATE(HCLK_SFC_XIP_PMU1, "hclk_sfc_xip_pmu1", "lsclk_pmu_root", 0, + RV1103B_PMU1CLKGATE_CON(0), 13, GFLAGS), + COMPOSITE_NODIV(SCLK_SFC_2X_PMU1, "sclk_sfc_2x_pmu1", sclk_sfc_2x_pmu1_p, 0, + RV1103B_PMU1CLKSEL_CON(0), 8, 1, MFLAGS, + RV1103B_PMU1CLKGATE_CON(0), 14, GFLAGS), + GATE(CLK_LPMCU, "clk_lpmcu", "lsclk_pmu_root", 0, + RV1103B_PMU1CLKGATE_CON(1), 0, GFLAGS), + GATE(CLK_LPMCU_RTC, "clk_lpmcu_rtc", "xin24m", 0, + RV1103B_PMU1CLKGATE_CON(1), 4, GFLAGS), + GATE(PCLK_LPMCU_MAILBOX, "pclk_lpmcu_mailbox", "lsclk_pmu_root", 0, + RV1103B_PMU1CLKGATE_CON(1), 8, GFLAGS), + + /* pd_peri */ + COMPOSITE_NOMUX(PCLK_PERI_ROOT, "pclk_peri_root", "lsclk_peri_src", CLK_IS_CRITICAL, + RV1103B_PERICLKSEL_CON(0), 0, 2, DFLAGS, + RV1103B_PERICLKGATE_CON(0), 0, GFLAGS), + COMPOSITE_NOMUX(PCLK_RTC_ROOT, "pclk_rtc_root", "lsclk_peri_src", CLK_IS_CRITICAL, + RV1103B_PERICLKSEL_CON(2), 12, 4, DFLAGS, + RV1103B_PERICLKGATE_CON(0), 8, GFLAGS), + GATE(CLK_TIMER_ROOT, "clk_timer_root", "xin24m", 0, + RV1103B_PERICLKGATE_CON(0), 1, GFLAGS), + GATE(PCLK_TIMER, "pclk_timer", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(1), 0, GFLAGS), + GATE(CLK_TIMER0, "clk_timer0", "clk_timer_root", 0, + RV1103B_PERICLKGATE_CON(1), 1, GFLAGS), + GATE(CLK_TIMER1, "clk_timer1", "clk_timer_root", 0, + RV1103B_PERICLKGATE_CON(1), 2, GFLAGS), + GATE(CLK_TIMER2, "clk_timer2", "clk_timer_root", 0, + RV1103B_PERICLKGATE_CON(1), 3, GFLAGS), + GATE(CLK_TIMER3, "clk_timer3", "clk_timer_root", 0, + RV1103B_PERICLKGATE_CON(1), 4, GFLAGS), + GATE(CLK_TIMER4, "clk_timer4", "clk_timer_root", 0, + RV1103B_PERICLKGATE_CON(1), 5, GFLAGS), + GATE(CLK_TIMER5, "clk_timer5", "clk_timer_root", 0, + RV1103B_PERICLKGATE_CON(1), 6, GFLAGS), + GATE(PCLK_STIMER, "pclk_stimer", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(1), 7, GFLAGS), + GATE(CLK_STIMER0, "clk_stimer0", "clk_timer_root", 0, + RV1103B_PERICLKGATE_CON(1), 8, GFLAGS), + GATE(CLK_STIMER1, "clk_stimer1", "clk_timer_root", 0, + RV1103B_PERICLKGATE_CON(1), 9, GFLAGS), + GATE(PCLK_WDT_NS, "pclk_wdt_ns", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(2), 0, GFLAGS), + GATE(TCLK_WDT_NS, "tclk_wdt_ns", "xin24m", 0, + RV1103B_PERICLKGATE_CON(2), 1, GFLAGS), + GATE(PCLK_WDT_S, "pclk_wdt_s", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(2), 2, GFLAGS), + GATE(TCLK_WDT_S, "tclk_wdt_s", "xin24m", 0, + RV1103B_PERICLKGATE_CON(2), 3, GFLAGS), + GATE(PCLK_WDT_HPMCU, "pclk_wdt_hpmcu", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(2), 4, GFLAGS), + GATE(TCLK_WDT_HPMCU, "tclk_wdt_hpmcu", "xin24m", 0, + RV1103B_PERICLKGATE_CON(2), 5, GFLAGS), + GATE(PCLK_I2C1, "pclk_i2c1", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(2), 6, GFLAGS), + GATE(CLK_I2C1, "clk_i2c1", "clk_i2c_peri", 0, + RV1103B_PERICLKGATE_CON(2), 7, GFLAGS), + GATE(PCLK_I2C2, "pclk_i2c2", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(2), 8, GFLAGS), + GATE(CLK_I2C2, "clk_i2c2", "clk_i2c_peri", 0, + RV1103B_PERICLKGATE_CON(2), 9, GFLAGS), + GATE(PCLK_I2C3, "pclk_i2c3", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(2), 10, GFLAGS), + GATE(CLK_I2C3, "clk_i2c3", "clk_i2c_peri", 0, + RV1103B_PERICLKGATE_CON(2), 11, GFLAGS), + GATE(PCLK_I2C4, "pclk_i2c4", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(2), 12, GFLAGS), + GATE(CLK_I2C4, "clk_i2c4", "clk_i2c_peri", 0, + RV1103B_PERICLKGATE_CON(2), 13, GFLAGS), + GATE(PCLK_SPI0, "pclk_spi0", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(3), 10, GFLAGS), + GATE(PCLK_PWM1, "pclk_pwm1", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(4), 6, GFLAGS), + GATE(CLK_OSC_PWM1, "clk_osc_pwm1", "xin24m", 0, + RV1103B_PERICLKGATE_CON(4), 8, GFLAGS), + GATE(PCLK_PWM2, "pclk_pwm2", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(4), 12, GFLAGS), + GATE(CLK_OSC_PWM2, "clk_osc_pwm2", "xin24m", 0, + RV1103B_PERICLKGATE_CON(4), 13, GFLAGS), + GATE(PCLK_UART2, "pclk_uart2", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(3), 0, GFLAGS), + GATE(PCLK_UART1, "pclk_uart1", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(3), 2, GFLAGS), + GATE(ACLK_RKDMA, "aclk_rkdma", "lsclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(5), 8, GFLAGS), + GATE(PCLK_TSADC, "pclk_tsadc", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(5), 9, GFLAGS), + COMPOSITE_NOMUX(CLK_TSADC, "clk_tsadc", "xin24m", 0, + RV1103B_PERICLKSEL_CON(0), 4, 5, DFLAGS, + RV1103B_PERICLKGATE_CON(5), 10, GFLAGS), + COMPOSITE_NOMUX(CLK_TSADC_TSEN, "clk_tsadc_tsen", "xin24m", 0, + RV1103B_PERICLKSEL_CON(0), 10, 5, DFLAGS, + RV1103B_PERICLKGATE_CON(5), 11, GFLAGS), + GATE(PCLK_SARADC, "pclk_saradc", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(5), 12, GFLAGS), + COMPOSITE_NOMUX(CLK_SARADC, "clk_saradc", "xin24m", 0, + RV1103B_PERICLKSEL_CON(1), 0, 3, DFLAGS, + RV1103B_PERICLKGATE_CON(5), 13, GFLAGS), + GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(6), 3, GFLAGS), + GATE(DBCLK_GPIO2, "dbclk_gpio2", "xin24m", 0, + RV1103B_PERICLKGATE_CON(6), 4, GFLAGS), + GATE(ACLK_USBOTG, "aclk_usbotg", "lsclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(6), 9, GFLAGS), + GATE(CLK_REF_USBOTG, "clk_ref_usbotg", "xin24m", 0, + RV1103B_PERICLKGATE_CON(6), 10, GFLAGS), + GATE(HCLK_SDMMC1, "hclk_sdmmc1", "lsclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(7), 0, GFLAGS), + GATE(HCLK_SAI, "hclk_sai", "lsclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(7), 1, GFLAGS), + GATE(ACLK_CRYPTO, "aclk_crypto", "lsclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(8), 2, GFLAGS), + GATE(HCLK_CRYPTO, "hclk_crypto", "lsclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(8), 3, GFLAGS), + GATE(HCLK_RK_RNG_S, "hclk_rk_rng_s", "lsclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(8), 5, GFLAGS), + GATE(HCLK_RK_RNG_NS, "hclk_rk_rng_ns", "hclk_rk_rng_s", 0, + RV1103B_PERICLKGATE_CON(8), 4, GFLAGS), + GATE(PCLK_OTPC_NS, "pclk_otpc_ns", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(8), 6, GFLAGS), + GATE(CLK_OTPC_ROOT_NS, "clk_otpc_root_ns", "xin24m", 0, + RV1103B_PERICLKGATE_CON(8), 7, GFLAGS), + GATE(CLK_SBPI_OTPC_NS, "clk_sbpi_otpc_ns", "clk_otpc_root_ns", 0, + RV1103B_PERICLKGATE_CON(8), 8, GFLAGS), + COMPOSITE_NOMUX(CLK_USER_OTPC_NS, "clk_user_otpc_ns", "clk_otpc_root_ns", 0, + RV1103B_PERICLKSEL_CON(1), 4, 3, DFLAGS, + RV1103B_PERICLKGATE_CON(8), 9, GFLAGS), + GATE(PCLK_OTPC_S, "pclk_otpc_s", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(8), 10, GFLAGS), + GATE(CLK_OTPC_ROOT_S, "clk_otpc_root_s", "xin24m", 0, + RV1103B_PERICLKGATE_CON(8), 11, GFLAGS), + GATE(CLK_SBPI_OTPC_S, "clk_sbpi_otpc_s", "clk_otpc_root_s", 0, + RV1103B_PERICLKGATE_CON(8), 12, GFLAGS), + COMPOSITE_NOMUX(CLK_USER_OTPC_S, "clk_user_otpc_s", "clk_otpc_root_s", 0, + RV1103B_PERICLKSEL_CON(1), 8, 3, DFLAGS, + RV1103B_PERICLKGATE_CON(8), 13, GFLAGS), + GATE(PCLK_OTP_MASK, "pclk_otp_mask", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(8), 15, GFLAGS), + GATE(HCLK_RGA, "hclk_rga", "lsclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(9), 0, GFLAGS), + GATE(ACLK_RGA, "aclk_rga", "aclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(9), 1, GFLAGS), + GATE(ACLK_MAC, "aclk_mac", "lsclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(9), 3, GFLAGS), + GATE(PCLK_MAC, "pclk_mac", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(9), 4, GFLAGS), + GATE(CLK_MACPHY, "clk_macphy", "xin24m", 0, + RV1103B_PERICLKGATE_CON(9), 11, GFLAGS), + GATE(ACLK_SPINLOCK, "aclk_spinlock", "lsclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(10), 0, GFLAGS), + GATE(HCLK_CACHE, "hclk_cache", "hclk_hpmcu", 0, + RV1103B_PERICLKGATE_CON(10), 1, GFLAGS), + GATE(PCLK_HPMCU_MAILBOX, "pclk_hpmcu_mailbox", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(10), 2, GFLAGS), + GATE(PCLK_HPMCU_INTMUX, "pclk_hpmcu_intmux", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(10), 3, GFLAGS), + GATE(CLK_HPMCU, "clk_hpmcu", "hclk_hpmcu", 0, + RV1103B_PERICLKGATE_CON(10), 4, GFLAGS), + GATE(CLK_HPMCU_RTC, "clk_hpmcu_rtc", "xin24m", 0, + RV1103B_PERICLKGATE_CON(10), 8, GFLAGS), + GATE(DCLK_DECOM, "dclk_decom", "dclk_decom_src", 0, + RV1103B_PERICLKGATE_CON(11), 0, GFLAGS), + GATE(ACLK_DECOM, "aclk_decom", "aclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(11), 1, GFLAGS), + GATE(PCLK_DECOM, "pclk_decom", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(11), 2, GFLAGS), + GATE(ACLK_SYS_SRAM, "aclk_sys_sram", "lsclk_peri_src", CLK_IS_CRITICAL, + RV1103B_PERICLKGATE_CON(11), 3, GFLAGS), + GATE(PCLK_DMA2DDR, "pclk_dma2ddr", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(11), 4, GFLAGS), + GATE(ACLK_DMA2DDR, "aclk_dma2ddr", "aclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(11), 5, GFLAGS), + GATE(PCLK_DCF, "pclk_dcf", "pclk_peri_root", 0, + RV1103B_PERICLKGATE_CON(11), 6, GFLAGS), + GATE(ACLK_DCF, "aclk_dcf", "lsclk_peri_src", 0, + RV1103B_PERICLKGATE_CON(11), 7, GFLAGS), + COMPOSITE_NOMUX(MCLK_ACODEC_TX, "mclk_acodec_tx", "mclk_sai_src", 0, + RV1103B_PERICLKSEL_CON(2), 0, 3, DFLAGS, + RV1103B_PERICLKGATE_CON(11), 9, GFLAGS), + GATE(CLK_REF_USBPHY, "clk_ref_usbphy", "xin24m", 0, + RV1103B_PERICLKGATE_CON(11), 12, GFLAGS), + + /* io */ + COMPOSITE_NODIV(CLK_FREQ_PWM0_SRC, "clk_freq_pwm0_src", clk_freq_pwm0_src_p, 0, + RV1103B_CLKSEL_CON(35), 12, 2, MFLAGS, + RV1103B_CLKGATE_CON(5), 6, GFLAGS), + GATE(CLK_FREQ_PWM0, "clk_freq_pwm0", "clk_freq_pwm0_src", 0, + RV1103B_PMUCLKGATE_CON(2), 4, GFLAGS), + COMPOSITE_NODIV(CLK_COUNTER_PWM0_SRC, "clk_counter_pwm0_src", clk_counter_pwm0_src_p, 0, + RV1103B_CLKSEL_CON(35), 14, 2, MFLAGS, + RV1103B_CLKGATE_CON(5), 7, GFLAGS), + GATE(CLK_COUNTER_PWM0, "clk_counter_pwm0", "clk_counter_pwm0_src", 0, + RV1103B_PMUCLKGATE_CON(2), 5, GFLAGS), + GATE(SCLK_SPI2AHB, "sclk_spi2ahb", "sclk_spi2ahb_io", 0, + RV1103B_PMU1CLKGATE_CON(0), 2, GFLAGS), + GATE(CLK_UTMI_USBOTG, "clk_utmi_usbotg", "clk_utmi_usbotg_io", 0, + RV1103B_PERICRU_IP_CON, 14, GFLAGS), +}; + +static struct rockchip_clk_branch rv1103b_armclk __initdata = + MUX(ARMCLK, "armclk", mux_armclk_p, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, + RV1103B_CORECLKSEL_CON(0), 1, 1, MFLAGS); + +static void __init rv1103b_clk_init(struct device_node *np) +{ + struct rockchip_clk_provider *ctx; + unsigned long clk_nr; + void __iomem *reg_base; + + clk_nr = rockchip_clk_find_max_clk_id(rv1103b_clk_branches, + ARRAY_SIZE(rv1103b_clk_branches)) + 1; + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: could not map cru region\n", __func__); + return; + } + + ctx = rockchip_clk_init(np, reg_base, clk_nr); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(reg_base); + return; + } + + rockchip_clk_register_plls(ctx, rv1103b_pll_clks, + ARRAY_SIZE(rv1103b_pll_clks), + RV1103B_GRF_SOC_STATUS0); + + rockchip_clk_register_branches(ctx, rv1103b_clk_branches, + ARRAY_SIZE(rv1103b_clk_branches)); + + rockchip_clk_register_armclk_multi_pll(ctx, &rv1103b_armclk, + rv1103b_cpuclk_rates, + ARRAY_SIZE(rv1103b_cpuclk_rates)); + + rockchip_register_restart_notifier(ctx, RV1103B_GLB_SRST_FST, NULL); + + rockchip_clk_of_add_provider(np, ctx); + + /* pvtpll src init */ + writel_relaxed(PVTPLL_SRC_SEL_PVTPLL, reg_base + RV1103B_CORECLKSEL_CON(0)); + writel_relaxed(PVTPLL_SRC_SEL_PVTPLL, reg_base + RV1103B_NPUCLKSEL_CON(0)); + writel_relaxed(PVTPLL_SRC_SEL_PVTPLL, reg_base + RV1103B_VICLKSEL_CON(0)); + writel_relaxed(PVTPLL_SRC_SEL_PVTPLL, reg_base + RV1103B_VEPUCLKSEL_CON(0)); +} + +CLK_OF_DECLARE(rv1103b_cru, "rockchip,rv1103b-cru", rv1103b_clk_init); diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index b2fff1d13a4a..cf0f5f11c34b 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -66,6 +66,55 @@ struct clk; #define PX30_PMU_CLKGATE_CON(x) ((x) * 0x4 + 0x80) #define PX30_PMU_MODE 0x0020 +#define RV1103B_TOPCRU_BASE 0x60000 +#define RV1103B_PERICRU_BASE 0x0 +#define RV1103B_VICRU_BASE 0x30000 +#define RV1103B_NPUCRU_BASE 0x20000 +#define RV1103B_CORECRU_BASE 0x40000 +#define RV1103B_VEPUCRU_BASE 0x10000 +#define RV1103B_DDRCRU_BASE 0x50000 +#define RV1103B_SUBDDRCRU_BASE 0x58000 +#define RV1103B_PMUCRU_BASE 0x70000 +#define RV1103B_PMU1CRU_BASE 0x80000 + +#define RV1103B_PMUCLKSEL_CON(x) ((x) * 0x4 + 0x300 + RV1103B_PMUCRU_BASE) +#define RV1103B_PMUCLKGATE_CON(x) ((x) * 0x4 + 0x800 + RV1103B_PMUCRU_BASE) +#define RV1103B_PMUSOFTRST_CON(x) ((x) * 0x4 + 0xa00 + RV1103B_PMUCRU_BASE) +#define RV1103B_PMU1CLKSEL_CON(x) ((x) * 0x4 + 0x300 + RV1103B_PMU1CRU_BASE) +#define RV1103B_PMU1CLKGATE_CON(x) ((x) * 0x4 + 0x800 + RV1103B_PMU1CRU_BASE) +#define RV1103B_PMU1SOFTRST_CON(x) ((x) * 0x4 + 0xa00 + RV1103B_PMU1CRU_BASE) +#define RV1103B_PLL_CON(x) ((x) * 0x4 + RV1103B_TOPCRU_BASE) +#define RV1103B_MODE_CON (0x280 + RV1103B_TOPCRU_BASE) +#define RV1103B_CLKSEL_CON(x) ((x) * 0x4 + 0x300 + RV1103B_TOPCRU_BASE) +#define RV1103B_CLKGATE_CON(x) ((x) * 0x4 + 0x800 + RV1103B_TOPCRU_BASE) +#define RV1103B_SOFTRST_CON(x) ((x) * 0x4 + 0xa00 + RV1103B_TOPCRU_BASE) +#define RV1103B_GLB_SRST_FST (0xc08 + RV1103B_TOPCRU_BASE) +#define RV1103B_GLB_SRST_SND (0xc0c + RV1103B_TOPCRU_BASE) +#define RV1103B_CLK_SAI_FRAC_DIV_HIGH (0xcc0 + RV1103B_TOPCRU_BASE) +#define RV1103B_PERICLKSEL_CON(x) ((x) * 0x4 + 0x300 + RV1103B_PERICRU_BASE) +#define RV1103B_PERICLKGATE_CON(x) ((x) * 0x4 + 0x800 + RV1103B_PERICRU_BASE) +#define RV1103B_PERISOFTRST_CON(x) ((x) * 0x4 + 0xa00 + RV1103B_PERICRU_BASE) +#define RV1103B_PERICRU_IP_CON (0xc08 + RV1103B_PERICRU_BASE) +#define RV1103B_VICLKSEL_CON(x) ((x) * 0x4 + 0x300 + RV1103B_VICRU_BASE) +#define RV1103B_VICLKGATE_CON(x) ((x) * 0x4 + 0x800 + RV1103B_VICRU_BASE) +#define RV1103B_VISOFTRST_CON(x) ((x) * 0x4 + 0xa00 + RV1103B_VICRU_BASE) +#define RV1103B_NPUCLKSEL_CON(x) ((x) * 0x4 + 0x300 + RV1103B_NPUCRU_BASE) +#define RV1103B_NPUCLKGATE_CON(x) ((x) * 0x4 + 0x800 + RV1103B_NPUCRU_BASE) +#define RV1103B_NPUSOFTRST_CON(x) ((x) * 0x4 + 0xa00 + RV1103B_NPUCRU_BASE) +#define RV1103B_CORECLKSEL_CON(x) ((x) * 0x4 + 0x300 + RV1103B_CORECRU_BASE) +#define RV1103B_CORECLKGATE_CON(x) ((x) * 0x4 + 0x800 + RV1103B_CORECRU_BASE) +#define RV1103B_CORESOFTRST_CON(x) ((x) * 0x4 + 0xa00 + RV1103B_CORECRU_BASE) +#define RV1103B_VEPUCLKSEL_CON(x) ((x) * 0x4 + 0x300 + RV1103B_VEPUCRU_BASE) +#define RV1103B_VEPUCLKGATE_CON(x) ((x) * 0x4 + 0x800 + RV1103B_VEPUCRU_BASE) +#define RV1103B_VEPUSOFTRST_CON(x) ((x) * 0x4 + 0xa00 + RV1103B_VEPUCRU_BASE) +#define RV1103B_DDRCLKSEL_CON(x) ((x) * 0x4 + 0x300 + RV1103B_DDRCRU_BASE) +#define RV1103B_DDRCLKGATE_CON(x) ((x) * 0x4 + 0x800 + RV1103B_DDRCRU_BASE) +#define RV1103B_DDRSOFTRST_CON(x) ((x) * 0x4 + 0xa00 + RV1103B_DDRCRU_BASE) +#define RV1103B_SUBDDRCLKSEL_CON(x) ((x) * 0x4 + 0x300 + RV1103B_SUBDDRCRU_BASE) +#define RV1103B_SUBDDRCLKGATE_CON(x) ((x) * 0x4 + 0x800 + RV1103B_SUBDDRCRU_BASE) +#define RV1103B_SUBDDRSOFTRST_CON(x) ((x) * 0x4 + 0xa00 + RV1103B_SUBDDRCRU_BASE) +#define RV1103B_SUBDDRMODE_CON (0x280 + RV1103B_SUBDDRCRU_BASE) + #define RV1108_PLL_CON(x) ((x) * 0x4) #define RV1108_CLKSEL_CON(x) ((x) * 0x4 + 0x60) #define RV1108_CLKGATE_CON(x) ((x) * 0x4 + 0x120) diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index f3657f2e1b98..b3c4ef4e0dbf 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_EXYNOS_5410_COMMON_CLK) += clk-exynos5410.o obj-$(CONFIG_EXYNOS_5420_COMMON_CLK) += clk-exynos5420.o obj-$(CONFIG_EXYNOS_5420_COMMON_CLK) += clk-exynos5-subcmu.o obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-artpec8.o +obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-artpec9.o obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynos5433.o obj-$(CONFIG_EXYNOS_AUDSS_CLK_CON) += clk-exynos-audss.o obj-$(CONFIG_EXYNOS_CLKOUT) += clk-exynos-clkout.o diff --git a/drivers/clk/samsung/clk-acpm.c b/drivers/clk/samsung/clk-acpm.c index b90809ce3f88..d8944160793a 100644 --- a/drivers/clk/samsung/clk-acpm.c +++ b/drivers/clk/samsung/clk-acpm.c @@ -20,7 +20,7 @@ struct acpm_clk { u32 id; struct clk_hw hw; unsigned int mbox_chan_id; - const struct acpm_handle *handle; + struct acpm_handle *handle; }; struct acpm_clk_variant { @@ -113,7 +113,7 @@ static int acpm_clk_register(struct device *dev, struct acpm_clk *aclk, static int acpm_clk_probe(struct platform_device *pdev) { - const struct acpm_handle *acpm_handle; + struct acpm_handle *acpm_handle; struct clk_hw_onecell_data *clk_data; struct clk_hw **hws; struct device *dev = &pdev->dev; diff --git a/drivers/clk/samsung/clk-artpec9.c b/drivers/clk/samsung/clk-artpec9.c new file mode 100644 index 000000000000..2eaf8117638c --- /dev/null +++ b/drivers/clk/samsung/clk-artpec9.c @@ -0,0 +1,1224 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * https://www.samsung.com + * Copyright (c) 2025 Axis Communications AB. + * https://www.axis.com + * + * Common Clock Framework support for ARTPEC-9 SoC. + */ + +#include +#include +#include + +#include "clk.h" +#include "clk-exynos-arm64.h" + +/* NOTE: Must be equal to the last clock ID increased by one */ +#define CMU_CMU_NR_CLK (CLK_DOUT_CMU_VIO_AUDIO + 1) +#define CMU_BUS_NR_CLK (CLK_MOUT_BUS_ACLK_USER + 1) +#define CMU_CORE_NR_CLK (CLK_MOUT_CORE_ACLK_USER + 1) +#define CMU_CPUCL_NR_CLK (CLK_GOUT_CPUCL_CSSYS_IPCLKPORT_PCLKDBG + 1) +#define CMU_FSYS0_NR_CLK (CLK_GOUT_FSYS0_PWM_IPCLKPORT_I_PCLK_S0 + 1) +#define CMU_FSYS1_NR_CLK (CLK_GOUT_FSYS1_XHB_USB_IPCLKPORT_CLK + 1) +#define CMU_IMEM_NR_CLK (CLK_GOUT_IMEM_PCLK_TMU0_APBIF + 1) +#define CMU_PERI_NR_CLK (CLK_GOUT_PERI_UART2_SCLK_UART + 1) + +/* Register Offset definitions for CMU_CMU (0x12c00000) */ +#define PLL_LOCKTIME_PLL_AUDIO 0x0000 +#define PLL_LOCKTIME_PLL_SHARED0 0x0004 +#define PLL_LOCKTIME_PLL_SHARED1 0x0008 +#define PLL_CON0_PLL_AUDIO 0x0100 +#define PLL_CON0_PLL_SHARED0 0x0120 +#define PLL_CON0_PLL_SHARED1 0x0140 +#define CLK_CON_MUX_CLKCMU_BUS 0x1000 +#define CLK_CON_MUX_CLKCMU_CDC_CORE 0x1004 +#define CLK_CON_MUX_CLKCMU_CORE_MAIN 0x1008 +#define CLK_CON_MUX_CLKCMU_CPUCL_SWITCH 0x100c +#define CLK_CON_MUX_CLKCMU_VIO_AUDIO 0x1010 +#define CLK_CON_MUX_CLKCMU_DLP_CORE 0x1014 +#define CLK_CON_MUX_CLKCMU_FSYS0_BUS 0x1018 +#define CLK_CON_MUX_CLKCMU_FSYS0_IP 0x101c +#define CLK_CON_MUX_CLKCMU_FSYS1_BUS 0x1020 +#define CLK_CON_MUX_CLKCMU_FSYS1_SCAN0 0x1024 +#define CLK_CON_MUX_CLKCMU_FSYS1_SCAN1 0x1028 +#define CLK_CON_MUX_CLKCMU_GPU_2D 0x102c +#define CLK_CON_MUX_CLKCMU_GPU_3D 0x1030 +#define CLK_CON_MUX_CLKCMU_IMEM_ACLK 0x1034 +#define CLK_CON_MUX_CLKCMU_IMEM_CA5 0x1038 +#define CLK_CON_MUX_CLKCMU_IMEM_JPEG 0x103c +#define CLK_CON_MUX_CLKCMU_IMEM_SSS 0x1040 +#define CLK_CON_MUX_CLKCMU_IPA_CORE 0x1044 +#define CLK_CON_MUX_CLKCMU_MIF_BUSP 0x1048 +#define CLK_CON_MUX_CLKCMU_MIF_SWITCH 0x104c +#define CLK_CON_MUX_CLKCMU_PERI_DISP 0x1050 +#define CLK_CON_MUX_CLKCMU_PERI_IP 0x1054 +#define CLK_CON_MUX_CLKCMU_RSP_CORE 0x1058 +#define CLK_CON_MUX_CLKCMU_TRFM 0x105c +#define CLK_CON_MUX_CLKCMU_VIO_CORE 0x1060 +#define CLK_CON_MUX_CLKCMU_VIO_CORE_L 0x1064 +#define CLK_CON_MUX_CLKCMU_VIP0 0x1068 +#define CLK_CON_MUX_CLKCMU_VIP1 0x106c +#define CLK_CON_MUX_CLKCMU_VPP_CORE 0x1070 +#define CLK_CON_DIV_CLKCMU_ADD 0x1800 +#define CLK_CON_DIV_CLKCMU_BUS 0x1804 +#define CLK_CON_DIV_CLKCMU_CDC_CORE 0x1808 +#define CLK_CON_DIV_CLKCMU_CORE_MAIN 0x180c +#define CLK_CON_DIV_CLKCMU_CPUCL_SWITCH 0x1810 +#define CLK_CON_DIV_CLKCMU_DLP_CORE 0x1814 +#define CLK_CON_DIV_CLKCMU_FSYS0_BUS 0x1818 +#define CLK_CON_DIV_CLKCMU_FSYS0_IP 0x181c +#define CLK_CON_DIV_CLKCMU_FSYS1_BUS 0x1820 +#define CLK_CON_DIV_CLKCMU_FSYS1_SCAN0 0x1824 +#define CLK_CON_DIV_CLKCMU_FSYS1_SCAN1 0x1828 +#define CLK_CON_DIV_CLKCMU_GPU_2D 0x182c +#define CLK_CON_DIV_CLKCMU_GPU_3D 0x1830 +#define CLK_CON_DIV_CLKCMU_IMEM_ACLK 0x1834 +#define CLK_CON_DIV_CLKCMU_IMEM_CA5 0x1838 +#define CLK_CON_DIV_CLKCMU_IMEM_JPEG 0x183c +#define CLK_CON_DIV_CLKCMU_IMEM_SSS 0x1840 +#define CLK_CON_DIV_CLKCMU_IPA_CORE 0x1844 +#define CLK_CON_DIV_CLKCMU_LCPU 0x1848 +#define CLK_CON_DIV_CLKCMU_MIF_BUSP 0x184c +#define CLK_CON_DIV_CLKCMU_MIF_SWITCH 0x1850 +#define CLK_CON_DIV_CLKCMU_PERI_DISP 0x1854 +#define CLK_CON_DIV_CLKCMU_PERI_IP 0x1858 +#define CLK_CON_DIV_CLKCMU_RSP_CORE 0x185c +#define CLK_CON_DIV_CLKCMU_TRFM 0x1860 +#define CLK_CON_DIV_CLKCMU_VIO_AUDIO 0x1864 +#define CLK_CON_DIV_CLKCMU_VIO_CORE 0x1868 +#define CLK_CON_DIV_CLKCMU_VIO_CORE_L 0x186c +#define CLK_CON_DIV_CLKCMU_VIP0 0x1870 +#define CLK_CON_DIV_CLKCMU_VIP1 0x1874 +#define CLK_CON_DIV_CLKCMU_VPP_CORE 0x1878 +#define CLK_CON_DIV_PLL_SHARED0_DIV2 0x187c +#define CLK_CON_DIV_PLL_SHARED0_DIV3 0x1880 +#define CLK_CON_DIV_PLL_SHARED0_DIV4 0x1884 +#define CLK_CON_DIV_PLL_SHARED1_DIV2 0x1888 +#define CLK_CON_DIV_PLL_SHARED1_DIV3 0x188c +#define CLK_CON_DIV_PLL_SHARED1_DIV4 0x1890 + +static const unsigned long cmu_cmu_clk_regs[] __initconst = { + PLL_LOCKTIME_PLL_AUDIO, + PLL_LOCKTIME_PLL_SHARED0, + PLL_LOCKTIME_PLL_SHARED1, + PLL_CON0_PLL_AUDIO, + PLL_CON0_PLL_SHARED0, + PLL_CON0_PLL_SHARED1, + CLK_CON_MUX_CLKCMU_BUS, + CLK_CON_MUX_CLKCMU_CDC_CORE, + CLK_CON_MUX_CLKCMU_CORE_MAIN, + CLK_CON_MUX_CLKCMU_CPUCL_SWITCH, + CLK_CON_MUX_CLKCMU_DLP_CORE, + CLK_CON_MUX_CLKCMU_FSYS0_BUS, + CLK_CON_MUX_CLKCMU_FSYS0_IP, + CLK_CON_MUX_CLKCMU_FSYS1_BUS, + CLK_CON_MUX_CLKCMU_FSYS1_SCAN0, + CLK_CON_MUX_CLKCMU_FSYS1_SCAN1, + CLK_CON_MUX_CLKCMU_GPU_2D, + CLK_CON_MUX_CLKCMU_GPU_3D, + CLK_CON_MUX_CLKCMU_IMEM_ACLK, + CLK_CON_MUX_CLKCMU_IMEM_CA5, + CLK_CON_MUX_CLKCMU_IMEM_JPEG, + CLK_CON_MUX_CLKCMU_IMEM_SSS, + CLK_CON_MUX_CLKCMU_IPA_CORE, + CLK_CON_MUX_CLKCMU_MIF_BUSP, + CLK_CON_MUX_CLKCMU_MIF_SWITCH, + CLK_CON_MUX_CLKCMU_PERI_DISP, + CLK_CON_MUX_CLKCMU_PERI_IP, + CLK_CON_MUX_CLKCMU_RSP_CORE, + CLK_CON_MUX_CLKCMU_TRFM, + CLK_CON_MUX_CLKCMU_VIO_CORE, + CLK_CON_MUX_CLKCMU_VIO_CORE_L, + CLK_CON_MUX_CLKCMU_VIP0, + CLK_CON_MUX_CLKCMU_VIP1, + CLK_CON_MUX_CLKCMU_VPP_CORE, + CLK_CON_DIV_CLKCMU_ADD, + CLK_CON_DIV_CLKCMU_BUS, + CLK_CON_DIV_CLKCMU_CDC_CORE, + CLK_CON_DIV_CLKCMU_CORE_MAIN, + CLK_CON_DIV_CLKCMU_CPUCL_SWITCH, + CLK_CON_DIV_CLKCMU_VIO_AUDIO, + CLK_CON_DIV_CLKCMU_DLP_CORE, + CLK_CON_DIV_CLKCMU_FSYS0_BUS, + CLK_CON_DIV_CLKCMU_FSYS0_IP, + CLK_CON_DIV_CLKCMU_FSYS1_BUS, + CLK_CON_DIV_CLKCMU_FSYS1_SCAN0, + CLK_CON_DIV_CLKCMU_FSYS1_SCAN1, + CLK_CON_DIV_CLKCMU_GPU_2D, + CLK_CON_DIV_CLKCMU_GPU_3D, + CLK_CON_DIV_CLKCMU_IMEM_ACLK, + CLK_CON_DIV_CLKCMU_IMEM_CA5, + CLK_CON_DIV_CLKCMU_IMEM_JPEG, + CLK_CON_DIV_CLKCMU_IMEM_SSS, + CLK_CON_DIV_CLKCMU_IPA_CORE, + CLK_CON_DIV_CLKCMU_LCPU, + CLK_CON_DIV_CLKCMU_MIF_BUSP, + CLK_CON_DIV_CLKCMU_MIF_SWITCH, + CLK_CON_DIV_CLKCMU_PERI_DISP, + CLK_CON_DIV_CLKCMU_PERI_IP, + CLK_CON_DIV_CLKCMU_RSP_CORE, + CLK_CON_DIV_CLKCMU_TRFM, + CLK_CON_DIV_CLKCMU_VIO_AUDIO, + CLK_CON_DIV_CLKCMU_VIO_CORE, + CLK_CON_DIV_CLKCMU_VIO_CORE_L, + CLK_CON_DIV_CLKCMU_VIP0, + CLK_CON_DIV_CLKCMU_VIP1, + CLK_CON_DIV_CLKCMU_VPP_CORE, + CLK_CON_DIV_PLL_SHARED0_DIV2, + CLK_CON_DIV_PLL_SHARED0_DIV3, + CLK_CON_DIV_PLL_SHARED0_DIV4, + CLK_CON_DIV_PLL_SHARED1_DIV2, + CLK_CON_DIV_PLL_SHARED1_DIV3, + CLK_CON_DIV_PLL_SHARED1_DIV4, +}; + +static const struct samsung_pll_rate_table artpec9_pll_audio_rates[] __initconst = { + PLL_A9FRACO_RATE(25 * MHZ, 589824000U, 94, 1, 3, 6238440), +}; + +static const struct samsung_pll_clock cmu_cmu_pll_clks[] __initconst = { + PLL(pll_a9fracm, CLK_FOUT_SHARED0_PLL, "fout_pll_shared0", "fin_pll", + PLL_LOCKTIME_PLL_SHARED0, PLL_CON0_PLL_SHARED0, NULL), + PLL(pll_a9fracm, CLK_FOUT_SHARED1_PLL, "fout_pll_shared1", "fin_pll", + PLL_LOCKTIME_PLL_SHARED1, PLL_CON0_PLL_SHARED1, NULL), + PLL(pll_a9fraco, CLK_FOUT_AUDIO_PLL, "fout_pll_audio", "fin_pll", + PLL_LOCKTIME_PLL_AUDIO, PLL_CON0_PLL_AUDIO, artpec9_pll_audio_rates), +}; + +PNAME(mout_clkcmu_bus_bus_p) = { "dout_pll_shared0_div2", "dout_pll_shared1_div2", + "dout_pll_shared1_div3", "dout_pll_shared1_div4" }; +PNAME(mout_clkcmu_cdc_core_p) = { "dout_pll_shared0_div2", "dout_pll_shared1_div3", + "dout_pll_shared1_div2", "mout_clk_pll_fsys1" }; +PNAME(mout_clkcmu_core_main_p) = { "dout_pll_shared0_div2", "dout_pll_shared1_div2", + "dout_pll_shared1_div3", "dout_pll_shared1_div4" }; +PNAME(mout_clkcmu_cpucl_switch_p) = { "dout_pll_shared0_div2", "dout_pll_shared1_div2", + "dout_pll_shared0_div3", "dout_pll_shared1_div3" }; +PNAME(mout_clkcmu_dlp_core_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div2", "mout_clk_pll_fsys1" }; +PNAME(mout_clkcmu_fsys0_bus_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div4", "dout_pll_shared1_div2" }; +PNAME(mout_clkcmu_fsys0_ip_p) = { "dout_pll_shared0_div2", "dout_pll_shared1_div3", + "dout_pll_shared1_div2", "dout_pll_shared0_div3" }; +PNAME(mout_clkcmu_fsys1_bus_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div2", "dout_pll_shared0_div4" }; +PNAME(mout_clkcmu_fsys1_scan0_p) = { "dout_pll_shared0_div2", "dout_pll_shared1_div4" }; +PNAME(mout_clkcmu_fsys1_scan1_p) = { "dout_pll_shared1_div3", "dout_pll_shared1_div4" }; +PNAME(mout_clkcmu_gpu_3d_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div2", "mout_clk_pll_fsys1" }; +PNAME(mout_clkcmu_gpu_2d_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div2", "mout_clk_pll_fsys1" }; +PNAME(mout_clkcmu_imem_aclk_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div4", "dout_pll_shared1_div2" }; +PNAME(mout_clkcmu_imem_ca5_p) = { "dout_pll_shared0_div2", "dout_pll_shared1_div2", + "dout_pll_shared1_div3", "mout_clk_pll_shared1" }; +PNAME(mout_clkcmu_imem_jpeg_p) = { "dout_pll_shared0_div2", "dout_pll_shared0_div3", + "dout_pll_shared1_div2", "dout_pll_shared1_div3" }; +PNAME(mout_clkcmu_imem_sss_p) = { "dout_pll_shared0_div2", "dout_pll_shared1_div2" }; +PNAME(mout_clkcmu_ipa_core_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div2", "mout_clk_pll_fsys1" }; +PNAME(mout_clkcmu_mif_switch_p) = { "fout_pll_shared1", "mout_clkcmu_pll_shared0", + "dout_pll_shared0_div2", "dout_pll_shared0_div3" }; +PNAME(mout_clkcmu_mif_busp_p) = { "dout_pll_shared1_div3", "dout_pll_shared1_div4", + "dout_pll_shared0_div4", "dout_pll_shared0_div2" }; +PNAME(mout_clkcmu_peri_disp_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div4", "dout_pll_shared1_div2" }; +PNAME(mout_clkcmu_peri_ip_p) = { "fout_pll_fsys1", "dout_pll_shared1_2", + "dout_pll_shared1_div4", "dout_pll_shared0_div2" }; +PNAME(mout_clkcmu_rsp_core_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div2", "mout_clk_pll_fsys1" }; +PNAME(mout_clkcmu_trfm_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div2", "mout_clk_pll_fsys1" }; +PNAME(mout_clkcmu_vio_core_l_p) = { "dout_pll_shared0_div2", "dout_pll_shared1_div3", + "dout_pll_shared1_div2", "mout_clk_pll_fsys1" }; +PNAME(mout_clkcmu_vio_core_p) = { "fout_pll_fsys1", "dout_pll_shared0_div2", + "dout_pll_shared1_div3", "dout_pll_shared1_div2" }; +PNAME(mout_clkcmu_vio_audio_p) = { "fout_pll_audio", "mout_clkcmu_pll_audio" }; +PNAME(mout_clkcmu_vip0_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div2", "mout_clk_pll_fsys1" }; +PNAME(mout_clkcmu_vip1_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div2", "mout_clk_pll_fsys1" }; +PNAME(mout_clkcmu_vpp_core_p) = { "dout_pll_shared1_div3", "dout_pll_shared0_div2", + "dout_pll_shared1_div2", "mout_clk_pll_fsys1" }; +PNAME(mout_clkcmu_pll_shared0_p) = { "fin_pll", "fout_pll_shared0" }; +PNAME(mout_clkcmu_pll_shared1_p) = { "fin_pll", "fout_pll_shared1" }; +PNAME(mout_clkcmu_pll_audio_p) = { "fin_pll", "fout_pll_audio" }; + +static const struct samsung_mux_clock cmu_cmu_mux_clks[] __initconst = { + MUX(0, "mout_clkcmu_pll_shared0", mout_clkcmu_pll_shared0_p, PLL_CON0_PLL_SHARED0, 4, 1), + MUX(0, "mout_clkcmu_pll_shared1", mout_clkcmu_pll_shared1_p, PLL_CON0_PLL_SHARED1, 4, 1), + MUX(0, "mout_clkcmu_pll_audio", mout_clkcmu_pll_audio_p, PLL_CON0_PLL_AUDIO, 4, 1), + MUX(0, "mout_clkcmu_bus_bus", mout_clkcmu_bus_bus_p, CLK_CON_MUX_CLKCMU_BUS, 0, 2), + nMUX(0, "mout_clkcmu_cdc_core", mout_clkcmu_cdc_core_p, CLK_CON_MUX_CLKCMU_CDC_CORE, 0, 2), + MUX(0, "mout_clkcmu_core_main", mout_clkcmu_core_main_p, + CLK_CON_MUX_CLKCMU_CORE_MAIN, 0, 2), + MUX(0, "mout_clkcmu_cpucl_switch", mout_clkcmu_cpucl_switch_p, + CLK_CON_MUX_CLKCMU_CPUCL_SWITCH, 0, 2), + nMUX(0, "mout_clkcmu_dlp_core", mout_clkcmu_dlp_core_p, CLK_CON_MUX_CLKCMU_DLP_CORE, 0, 2), + MUX(0, "mout_clkcmu_fsys0_bus", mout_clkcmu_fsys0_bus_p, + CLK_CON_MUX_CLKCMU_FSYS0_BUS, 0, 2), + MUX(0, "mout_clkcmu_fsys0_ip", mout_clkcmu_fsys0_ip_p, CLK_CON_MUX_CLKCMU_FSYS0_IP, 0, 2), + MUX(0, "mout_clkcmu_fsys1_bus", mout_clkcmu_fsys1_bus_p, + CLK_CON_MUX_CLKCMU_FSYS1_BUS, 0, 2), + MUX(0, "mout_clkcmu_fsys1_scan0", mout_clkcmu_fsys1_scan0_p, + CLK_CON_MUX_CLKCMU_FSYS1_SCAN0, 0, 1), + MUX(0, "mout_clkcmu_fsys1_scan1", mout_clkcmu_fsys1_scan1_p, + CLK_CON_MUX_CLKCMU_FSYS1_SCAN1, 0, 1), + MUX(0, "mout_clkcmu_gpu_2d", mout_clkcmu_gpu_2d_p, CLK_CON_MUX_CLKCMU_GPU_2D, 0, 2), + MUX(0, "mout_clkcmu_gpu_3d", mout_clkcmu_gpu_3d_p, CLK_CON_MUX_CLKCMU_GPU_3D, 0, 2), + MUX(0, "mout_clkcmu_imem_aclk", mout_clkcmu_imem_aclk_p, + CLK_CON_MUX_CLKCMU_IMEM_ACLK, 0, 2), + MUX(0, "mout_clkcmu_imem_ca5", mout_clkcmu_imem_ca5_p, CLK_CON_MUX_CLKCMU_IMEM_CA5, 0, 2), + MUX(0, "mout_clkcmu_imem_jpeg", mout_clkcmu_imem_jpeg_p, + CLK_CON_MUX_CLKCMU_IMEM_JPEG, 0, 2), + MUX(0, "mout_clkcmu_imem_sss", mout_clkcmu_imem_sss_p, CLK_CON_MUX_CLKCMU_IMEM_SSS, 0, 1), + MUX(0, "mout_clkcmu_ipa_core", mout_clkcmu_ipa_core_p, CLK_CON_MUX_CLKCMU_IPA_CORE, 0, 2), + MUX(0, "mout_clkcmu_mif_busp", mout_clkcmu_mif_busp_p, CLK_CON_MUX_CLKCMU_MIF_BUSP, 0, 2), + MUX(0, "mout_clkcmu_mif_switch", mout_clkcmu_mif_switch_p, + CLK_CON_MUX_CLKCMU_MIF_SWITCH, 0, 2), + MUX(0, "mout_clkcmu_peri_disp", mout_clkcmu_peri_disp_p, + CLK_CON_MUX_CLKCMU_PERI_DISP, 0, 2), + MUX(0, "mout_clkcmu_peri_ip", mout_clkcmu_peri_ip_p, CLK_CON_MUX_CLKCMU_PERI_IP, 0, 2), + MUX(0, "mout_clkcmu_rsp_core", mout_clkcmu_rsp_core_p, CLK_CON_MUX_CLKCMU_RSP_CORE, 0, 2), + MUX(0, "mout_clkcmu_trfm", mout_clkcmu_trfm_p, CLK_CON_MUX_CLKCMU_TRFM, 0, 2), + MUX(0, "mout_clkcmu_vio_core", mout_clkcmu_vio_core_p, CLK_CON_MUX_CLKCMU_VIO_CORE, 0, 2), + MUX(0, "mout_clkcmu_vio_core_l", mout_clkcmu_vio_core_l_p, + CLK_CON_MUX_CLKCMU_VIO_CORE_L, 0, 2), + MUX(0, "mout_clkcmu_vio_audio", mout_clkcmu_vio_audio_p, + CLK_CON_MUX_CLKCMU_VIO_AUDIO, 0, 1), + MUX(0, "mout_clkcmu_vip0", mout_clkcmu_vip0_p, CLK_CON_MUX_CLKCMU_VIP0, 0, 2), + MUX(0, "mout_clkcmu_vip1", mout_clkcmu_vip1_p, CLK_CON_MUX_CLKCMU_VIP1, 0, 2), + MUX(0, "mout_clkcmu_vpp_core", mout_clkcmu_vpp_core_p, CLK_CON_MUX_CLKCMU_VPP_CORE, 0, 2), +}; + +static const struct samsung_div_clock cmu_cmu_div_clks[] __initconst = { + DIV(CLK_DOUT_CMU_ADD, "dout_clkcmu_add", "gate_clkcmu_add", CLK_CON_DIV_CLKCMU_ADD, 0, 8), + DIV(CLK_DOUT_CMU_BUS, "dout_clkcmu_bus", + "gate_clkcmu_bus_bus", CLK_CON_DIV_CLKCMU_BUS, 0, 4), + DIV_F(CLK_DOUT_CMU_CDC_CORE, "dout_clkcmu_cdc_core", + "gate_clkcmu_cdc_core", CLK_CON_DIV_CLKCMU_CDC_CORE, 0, 4, CLK_SET_RATE_PARENT, 0), + DIV(CLK_DOUT_CMU_CORE_MAIN, "dout_clkcmu_core_main", + "gate_clkcmu_core_main", CLK_CON_DIV_CLKCMU_CORE_MAIN, 0, 4), + DIV(CLK_DOUT_CMU_CPUCL_SWITCH, "dout_clkcmu_cpucl_switch", + "gate_clkcmu_cpucl_switch", CLK_CON_DIV_CLKCMU_CPUCL_SWITCH, 0, 3), + DIV_F(CLK_DOUT_CMU_DLP_CORE, "dout_clkcmu_dlp_core", + "gate_clkcmu_dlp_core", CLK_CON_DIV_CLKCMU_DLP_CORE, 0, 4, CLK_SET_RATE_PARENT, 0), + DIV(CLK_DOUT_CMU_FSYS0_BUS, "dout_clkcmu_fsys0_bus", + "gate_clkcmu_fsys0_bus", CLK_CON_DIV_CLKCMU_FSYS0_BUS, 0, 4), + DIV(CLK_DOUT_CMU_FSYS0_IP, "dout_clkcmu_fsys0_ip", + "gate_clkcmu_fsys0_ip", CLK_CON_DIV_CLKCMU_FSYS0_IP, 0, 9), + DIV(CLK_DOUT_CMU_FSYS1_BUS, "dout_clkcmu_fsys1_bus", + "gate_clkcmu_fsys1_bus", CLK_CON_DIV_CLKCMU_FSYS1_BUS, 0, 4), + DIV(CLK_DOUT_CMU_FSYS1_SCAN0, "dout_clkcmu_fsys1_scan0", + "gate_clkcmu_fsys1_scan0", CLK_CON_DIV_CLKCMU_FSYS1_SCAN0, 0, 4), + DIV(CLK_DOUT_CMU_FSYS1_SCAN1, "dout_clkcmu_fsys1_scan1", + "gate_clkcmu_fsys1_scan1", CLK_CON_DIV_CLKCMU_FSYS1_SCAN1, 0, 4), + DIV(CLK_DOUT_CMU_GPU_2D, "dout_clkcmu_gpu_2d", + "gate_clkcmu_gpu_2d", CLK_CON_DIV_CLKCMU_GPU_2D, 0, 4), + DIV(CLK_DOUT_CMU_GPU_3D, "dout_clkcmu_gpu_3d", + "gate_clkcmu_gpu_3d", CLK_CON_DIV_CLKCMU_GPU_3D, 0, 4), + DIV(CLK_DOUT_CMU_IMEM_ACLK, "dout_clkcmu_imem_aclk", + "gate_clkcmu_imem_aclk", CLK_CON_DIV_CLKCMU_IMEM_ACLK, 0, 4), + DIV(CLK_DOUT_CMU_IMEM_CA5, "dout_clkcmu_imem_ca5", + "gate_clkcmu_imem_ca5", CLK_CON_DIV_CLKCMU_IMEM_CA5, 0, 4), + DIV(CLK_DOUT_CMU_IMEM_JPEG, "dout_clkcmu_imem_jpeg", + "gate_clkcmu_imem_jpeg", CLK_CON_DIV_CLKCMU_IMEM_JPEG, 0, 4), + DIV(CLK_DOUT_CMU_IMEM_SSS, "dout_clkcmu_imem_sss", + "gate_clkcmu_imem_sss", CLK_CON_DIV_CLKCMU_IMEM_SSS, 0, 4), + DIV(CLK_DOUT_CMU_IPA_CORE, "dout_clkcmu_ipa_core", + "gate_clkcmu_ipa_core", CLK_CON_DIV_CLKCMU_IPA_CORE, 0, 4), + DIV(CLK_DOUT_CMU_LCPU, "dout_clkcmu_lcpu", + "gate_clkcmu_lcpu", CLK_CON_DIV_CLKCMU_LCPU, 0, 4), + DIV(CLK_DOUT_CMU_MIF_BUSP, "dout_clkcmu_mif_busp", + "gate_clkcmu_mif_busp", CLK_CON_DIV_CLKCMU_MIF_BUSP, 0, 3), + DIV(CLK_DOUT_CMU_MIF_SWITCH, "dout_clkcmu_mif_switch", + "gate_clkcmu_mif_switch", CLK_CON_DIV_CLKCMU_MIF_SWITCH, 0, 4), + DIV(CLK_DOUT_CMU_PERI_DISP, "dout_clkcmu_peri_disp", + "gate_clkcmu_peri_disp", CLK_CON_DIV_CLKCMU_PERI_DISP, 0, 4), + DIV(CLK_DOUT_CMU_PERI_IP, "dout_clkcmu_peri_ip", + "gate_clkcmu_peri_ip", CLK_CON_DIV_CLKCMU_PERI_IP, 0, 4), + DIV(CLK_DOUT_CMU_RSP_CORE, "dout_clkcmu_rsp_core", + "gate_clkcmu_rsp_core", CLK_CON_DIV_CLKCMU_RSP_CORE, 0, 4), + DIV(CLK_DOUT_CMU_TRFM, "dout_clkcmu_trfm", + "gate_clkcmu_trfm", CLK_CON_DIV_CLKCMU_TRFM, 0, 4), + DIV(CLK_DOUT_CMU_VIO_CORE, "dout_clkcmu_vio_core", + "gate_clkcmu_vio_core", CLK_CON_DIV_CLKCMU_VIO_CORE, 0, 4), + DIV(CLK_DOUT_CMU_VIO_CORE_L, "dout_clkcmu_vio_core_l", + "gate_clkcmu_vio_core_l", CLK_CON_DIV_CLKCMU_VIO_CORE_L, 0, 4), + DIV(CLK_DOUT_CMU_VIO_AUDIO, "dout_clkcmu_vio_audio", + "gate_clkcmu_vio_audio", CLK_CON_DIV_CLKCMU_VIO_AUDIO, 0, 4), + DIV(CLK_DOUT_CMU_VIP0, "dout_clkcmu_vip0", + "gate_clkcmu_vip0", CLK_CON_DIV_CLKCMU_VIP0, 0, 4), + DIV(CLK_DOUT_CMU_VIP1, "dout_clkcmu_vip1", + "gate_clkcmu_vip1", CLK_CON_DIV_CLKCMU_VIP1, 0, 4), + DIV(CLK_DOUT_CMU_VPP_CORE, "dout_clkcmu_vpp_core", + "gate_clkcmu_vpp_core", CLK_CON_DIV_CLKCMU_VPP_CORE, 0, 4), + DIV(CLK_DOUT_SHARED0_DIV2, "dout_pll_shared0_div2", + "mout_clkcmu_pll_shared0", CLK_CON_DIV_PLL_SHARED0_DIV2, 0, 1), + DIV(CLK_DOUT_SHARED0_DIV3, "dout_pll_shared0_div3", + "mout_clkcmu_pll_shared0", CLK_CON_DIV_PLL_SHARED0_DIV3, 0, 2), + DIV(CLK_DOUT_SHARED0_DIV4, "dout_pll_shared0_div4", + "dout_pll_shared0_div2", CLK_CON_DIV_PLL_SHARED0_DIV4, 0, 1), + DIV(CLK_DOUT_SHARED1_DIV2, "dout_pll_shared1_div2", + "mout_clkcmu_pll_shared1", CLK_CON_DIV_PLL_SHARED1_DIV2, 0, 1), + DIV(CLK_DOUT_SHARED1_DIV3, "dout_pll_shared1_div3", + "mout_clkcmu_pll_shared1", CLK_CON_DIV_PLL_SHARED1_DIV3, 0, 2), + DIV(CLK_DOUT_SHARED1_DIV4, "dout_pll_shared1_div4", + "dout_pll_shared1_div2", CLK_CON_DIV_PLL_SHARED1_DIV4, 0, 1), +}; + +static const struct samsung_cmu_info cmu_cmu_info __initconst = { + .pll_clks = cmu_cmu_pll_clks, + .nr_pll_clks = ARRAY_SIZE(cmu_cmu_pll_clks), + .mux_clks = cmu_cmu_mux_clks, + .nr_mux_clks = ARRAY_SIZE(cmu_cmu_mux_clks), + .div_clks = cmu_cmu_div_clks, + .nr_div_clks = ARRAY_SIZE(cmu_cmu_div_clks), + .nr_clk_ids = CMU_CMU_NR_CLK, + .clk_regs = cmu_cmu_clk_regs, + .nr_clk_regs = ARRAY_SIZE(cmu_cmu_clk_regs), +}; + +/* Register Offset definitions for CMU_BUS (0x13410000) */ +#define PLL_CON0_MUX_CLK_BUS_ACLK_USER 0x0100 + +static const unsigned long cmu_bus_clk_regs[] __initconst = { + PLL_CON0_MUX_CLK_BUS_ACLK_USER, +}; + +PNAME(mout_clk_bus_aclk_user_p) = {"fin_pll", "dout_clkcmu_bus_bus",}; + +static const struct samsung_mux_clock cmu_bus_mux_clks[] __initconst = { + MUX(CLK_MOUT_BUS_ACLK_USER, "mout_clk_bus_aclk_user", mout_clk_bus_aclk_user_p, + PLL_CON0_MUX_CLK_BUS_ACLK_USER, 4, 1), +}; + +static const struct samsung_cmu_info cmu_bus_info __initconst = { + .mux_clks = cmu_bus_mux_clks, + .nr_mux_clks = ARRAY_SIZE(cmu_bus_mux_clks), + .nr_clk_ids = CMU_BUS_NR_CLK, + .clk_regs = cmu_bus_clk_regs, + .nr_clk_regs = ARRAY_SIZE(cmu_bus_clk_regs), +}; + +/* Register Offset definitions for CMU_CORE (0x12c10000) */ +#define PLL_CON0_MUX_CLK_CORE_ACLK_USER 0x0100 + +static const unsigned long cmu_core_clk_regs[] __initconst = { + PLL_CON0_MUX_CLK_CORE_ACLK_USER, +}; + +PNAME(mout_clk_core_aclk_user_p) = {"fin_pll", "dout_clkcmu_core_main",}; + +static const struct samsung_mux_clock cmu_core_mux_clks[] __initconst = { + MUX(CLK_MOUT_CORE_ACLK_USER, "mout_clk_core_aclk_user", mout_clk_core_aclk_user_p, + PLL_CON0_MUX_CLK_CORE_ACLK_USER, 4, 1), +}; + +static const struct samsung_cmu_info cmu_core_info __initconst = { + .mux_clks = cmu_core_mux_clks, + .nr_mux_clks = ARRAY_SIZE(cmu_core_mux_clks), + .nr_clk_ids = CMU_CORE_NR_CLK, + .clk_regs = cmu_core_clk_regs, + .nr_clk_regs = ARRAY_SIZE(cmu_core_clk_regs), +}; + +/* Register Offset definitions for CMU_CPUCL (0x12810000) */ +#define PLL_LOCKTIME_PLL0_CPUCL 0x0000 +#define PLL_LOCKTIME_PLL1_CPUCL 0x0008 +#define PLL_CON0_MUX_CLKCMU_CPUCL_SWITCH_SCU_USER 0x0100 +#define PLL_CON0_MUX_CLKCMU_CPUCL_SWITCH_USER 0x0120 +#define PLL_CON0_PLL0_CPUCL 0x0140 +#define PLL_CON0_PLL1_CPUCL 0x0160 +#define CLK_CON_MUX_CLK_CPUCL_PLL 0x1000 +#define CLK_CON_MUX_CLK_CPUCL_PLL_SCU 0x1004 +#define CLK_CON_DIV_CLK_CPUCL_CLUSTER_PERIPHCLK 0x1800 +#define CLK_CON_DIV_CLK_CPUCL_CLUSTER_GICCLK 0x1804 +#define CLK_CON_DIV_CLK_CPUCL_CLUSTER_PCLK 0x1808 +#define CLK_CON_DIV_CLK_CPUCL_CMUREF 0x180c +#define CLK_CON_DIV_CLK_CPUCL_CPU 0x1810 +#define CLK_CON_DIV_CLK_CPUCL_CLUSTER_ATCLK 0x1818 +#define CLK_CON_DIV_CLK_CPUCL_CLUSTER_SCU 0x181c +#define CLK_CON_DIV_CLK_CPUCL_DBG 0x1820 +#define CLK_CON_GAT_CLK_CLUSTER_CPU 0x2008 +#define CLK_CON_GAT_CLK_CPUCL_SHORTSTOP 0x200c +#define CSSYS_IPCLKPORT_ATCLK 0x2070 +#define CSSYS_IPCLKPORT_PCLKDBG 0x2074 +#define DMYQCH_CON_CSSYS_QCH 0x3000 +#define DMYQCH_CON_CLUSTER_QCH_CORECLK0 0x3104 +#define DMYQCH_CON_CLUSTER_QCH_CORECLK1 0x3108 +#define DMYQCH_CON_CLUSTER_QCH_CORECLK2 0x310c +#define DMYQCH_CON_CLUSTER_QCH_CORECLK3 0x3110 +#define DMYQCH_CON_CLUSTER_QCH_CORECLK4 0x3114 +#define DMYQCH_CON_CLUSTER_QCH_CORECLK5 0x3118 +#define DMYQCH_CON_CLUSTER_QCH_PERIPHCLK 0x311c + +static const unsigned long cmu_cpucl_clk_regs[] __initconst = { + PLL_LOCKTIME_PLL0_CPUCL, + PLL_LOCKTIME_PLL1_CPUCL, + PLL_CON0_MUX_CLKCMU_CPUCL_SWITCH_SCU_USER, + PLL_CON0_MUX_CLKCMU_CPUCL_SWITCH_USER, + PLL_CON0_PLL0_CPUCL, + PLL_CON0_PLL1_CPUCL, + CLK_CON_MUX_CLK_CPUCL_PLL, + CLK_CON_MUX_CLK_CPUCL_PLL_SCU, + CLK_CON_DIV_CLK_CPUCL_CLUSTER_PERIPHCLK, + CLK_CON_DIV_CLK_CPUCL_CLUSTER_GICCLK, + CLK_CON_DIV_CLK_CPUCL_CLUSTER_PCLK, + CLK_CON_DIV_CLK_CPUCL_CMUREF, + CLK_CON_DIV_CLK_CPUCL_CPU, + CLK_CON_DIV_CLK_CPUCL_CLUSTER_ATCLK, + CLK_CON_DIV_CLK_CPUCL_CLUSTER_SCU, + CLK_CON_DIV_CLK_CPUCL_DBG, + CLK_CON_GAT_CLK_CLUSTER_CPU, + CLK_CON_GAT_CLK_CPUCL_SHORTSTOP, + CSSYS_IPCLKPORT_ATCLK, + CSSYS_IPCLKPORT_PCLKDBG, + DMYQCH_CON_CSSYS_QCH, + DMYQCH_CON_CLUSTER_QCH_CORECLK0, + DMYQCH_CON_CLUSTER_QCH_CORECLK1, + DMYQCH_CON_CLUSTER_QCH_CORECLK2, + DMYQCH_CON_CLUSTER_QCH_CORECLK3, + DMYQCH_CON_CLUSTER_QCH_CORECLK4, + DMYQCH_CON_CLUSTER_QCH_CORECLK5, + DMYQCH_CON_CLUSTER_QCH_PERIPHCLK, +}; + +/* rate_table must be in descending order */ +static const struct samsung_pll_rate_table artpec9_pll_cpucl_rates[] __initconst = { + PLL_35XX_RATE(25 * MHZ, 1400000000U, 56, 1, 0), + PLL_35XX_RATE(25 * MHZ, 1100000000U, 44, 1, 0), + PLL_35XX_RATE(25 * MHZ, 850000000U, 34, 1, 0), +}; + +static const struct samsung_pll_clock cmu_cpucl_pll_clks[] __initconst = { + PLL(pll_a9fracm, CLK_FOUT_CPUCL_PLL0, "fout_pll0_cpucl", "fin_pll", + PLL_LOCKTIME_PLL0_CPUCL, PLL_CON0_PLL0_CPUCL, artpec9_pll_cpucl_rates), + PLL(pll_a9fracm, CLK_FOUT_CPUCL_PLL1, "fout_pll1_cpucl", "fin_pll", + PLL_LOCKTIME_PLL1_CPUCL, PLL_CON0_PLL1_CPUCL, artpec9_pll_cpucl_rates), +}; + +PNAME(mout_clkcmu_cpucl_switch_scu_user_p) = { "fin_pll", "dout_clkcmu_cpucl_switch" }; +PNAME(mout_clkcmu_cpucl_switch_user_p) = { "fin_pll", "dout_clkcmu_cpucl_switch" }; +PNAME(mout_pll0_cpucl_p) = { "fin_pll", "fout_pll0_cpucl" }; +PNAME(mout_clk_cpucl_pll0_p) = { "mout_pll0_cpucl", "mout_clkcmu_cpucl_switch_user" }; +PNAME(mout_pll1_cpucl_p) = { "fin_pll", "fout_pll1_cpucl" }; +PNAME(mout_clk_cpucl_pll_scu_p) = { "mout_pll1_cpucl", "mout_clkcmu_cpucl_switch_scu_user" }; + +static const struct samsung_mux_clock cmu_cpucl_mux_clks[] __initconst = { + MUX_F(0, "mout_pll0_cpucl", mout_pll0_cpucl_p, + PLL_CON0_PLL0_CPUCL, 4, 1, CLK_SET_RATE_PARENT | CLK_RECALC_NEW_RATES, 0), + MUX_F(0, "mout_pll1_cpucl", mout_pll1_cpucl_p, + PLL_CON0_PLL1_CPUCL, 4, 1, CLK_SET_RATE_PARENT | CLK_RECALC_NEW_RATES, 0), + MUX(CLK_MOUT_CPUCL_SWITCH_SCU_USER, "mout_clkcmu_cpucl_switch_scu_user", + mout_clkcmu_cpucl_switch_scu_user_p, PLL_CON0_MUX_CLKCMU_CPUCL_SWITCH_SCU_USER, 4, 1), + MUX(CLK_MOUT_CPUCL_SWITCH_USER, "mout_clkcmu_cpucl_switch_user", + mout_clkcmu_cpucl_switch_user_p, PLL_CON0_MUX_CLKCMU_CPUCL_SWITCH_USER, 4, 1), + MUX_F(CLK_MOUT_CPUCL_PLL0, "mout_clk_cpucl_pll0", + mout_clk_cpucl_pll0_p, CLK_CON_MUX_CLK_CPUCL_PLL, 0, 1, CLK_SET_RATE_PARENT, 0), + MUX_F(CLK_MOUT_CPUCL_PLL_SCU, "mout_clk_cpucl_pll_scu", mout_clk_cpucl_pll_scu_p, + CLK_CON_MUX_CLK_CPUCL_PLL_SCU, 0, 1, CLK_SET_RATE_PARENT, 0), +}; + +static const struct samsung_fixed_factor_clock cpucl_ffactor_clks[] __initconst = { + FFACTOR(CLK_DOUT_CPUCL_CPU, "dout_clk_cpucl_cpu", + "mout_clk_cpucl_pll0", 1, 1, CLK_SET_RATE_PARENT), +}; + +static const struct samsung_div_clock cmu_cpucl_div_clks[] __initconst = { + DIV(CLK_DOUT_CPUCL_CLUSTER_PERIPHCLK, "dout_clk_cluster_periphclk", + "clk_con_gat_clk_cluster_cpu", CLK_CON_DIV_CLK_CPUCL_CLUSTER_PERIPHCLK, 0, 4), + DIV(CLK_DOUT_CPUCL_CLUSTER_GICCLK, "dout_clk_cluster_gicclk", + "clk_con_gat_clk_cluster_cpu", CLK_CON_DIV_CLK_CPUCL_CLUSTER_GICCLK, 0, 4), + DIV(CLK_DOUT_CPUCL_CLUSTER_PCLK, "dout_clk_cluster_pclk", + "clk_con_gat_clk_cluster_cpu", CLK_CON_DIV_CLK_CPUCL_CLUSTER_PCLK, 0, 4), + DIV(CLK_DOUT_CPUCL_CMUREF, "dout_clk_cpucl_cmuref", + "dout_clk_cpucl_cpu", CLK_CON_DIV_CLK_CPUCL_CMUREF, 0, 3), + DIV(CLK_DOUT_CPUCL_CLUSTER_ATCLK, "dout_clk_cluster_atclk", + "clk_con_gat_clk_cluster_cpu", CLK_CON_DIV_CLK_CPUCL_CLUSTER_ATCLK, 0, 4), + DIV_F(CLK_DOUT_CPUCL_CLUSTER_SCU, "dout_clk_cluster_scu", "mout_clk_cpucl_pll_scu", + CLK_CON_DIV_CLK_CPUCL_CLUSTER_SCU, 0, 4, CLK_SET_RATE_PARENT, 0), + DIV(CLK_DOUT_CPUCL_DBG, "dout_clk_cpucl_dbg", + "dout_clk_cpucl_cpu", CLK_CON_DIV_CLK_CPUCL_DBG, 0, 4), +}; + +static const struct samsung_gate_clock cmu_cpucl_gate_clks[] __initconst = { + GATE(CLK_GOUT_CPUCL_CLUSTER_CPU, "clk_con_gat_clk_cluster_cpu", + "clk_con_gat_clk_cpucl_shortstop", CLK_CON_GAT_CLK_CLUSTER_CPU, 21, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_CPUCL_SHORTSTOP, "clk_con_gat_clk_cpucl_shortstop", "dout_clk_cpucl_cpu", + CLK_CON_GAT_CLK_CPUCL_SHORTSTOP, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_CPUCL_CSSYS_IPCLKPORT_ATCLK, "cssys_ipclkport_atclk", "dout_clk_cpucl_dbg", + CSSYS_IPCLKPORT_ATCLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_CPUCL_CSSYS_IPCLKPORT_PCLKDBG, "cssys_ipclkport_pclkdbg", + "dout_clk_cpucl_dbg", CSSYS_IPCLKPORT_PCLKDBG, 21, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), +}; + +static const struct samsung_cmu_info cmu_cpucl_info __initconst = { + .pll_clks = cmu_cpucl_pll_clks, + .nr_pll_clks = ARRAY_SIZE(cmu_cpucl_pll_clks), + .fixed_factor_clks = cpucl_ffactor_clks, + .nr_fixed_factor_clks = ARRAY_SIZE(cpucl_ffactor_clks), + .mux_clks = cmu_cpucl_mux_clks, + .nr_mux_clks = ARRAY_SIZE(cmu_cpucl_mux_clks), + .div_clks = cmu_cpucl_div_clks, + .nr_div_clks = ARRAY_SIZE(cmu_cpucl_div_clks), + .gate_clks = cmu_cpucl_gate_clks, + .nr_gate_clks = ARRAY_SIZE(cmu_cpucl_gate_clks), + .nr_clk_ids = CMU_CPUCL_NR_CLK, + .clk_regs = cmu_cpucl_clk_regs, + .nr_clk_regs = ARRAY_SIZE(cmu_cpucl_clk_regs), +}; + +/* Register Offset definitions for CMU_FSYS0 (0x14410000) */ +#define PLL_CON0_MUX_CLK_FSYS0_BUS_USER 0x0100 +#define PLL_CON0_MUX_CLK_FSYS0_IP_USER 0x0120 +#define PLL_CON0_MUX_CLK_FSYS0_MAIN_USER 0x0140 +#define CLK_CON_DIV_CLK_FSYS0_125 0x1800 +#define CLK_CON_DIV_CLK_FSYS0_ADC 0x1804 +#define CLK_CON_DIV_CLK_FSYS0_BUS_300 0x1808 +#define CLK_CON_DIV_CLK_FSYS0_EQOS0 0x1814 +#define CLK_CON_DIV_CLK_FSYS0_EQOS1 0x1818 +#define CLK_CON_DIV_CLK_FSYS0_EQOS_250 0x181C +#define CLK_CON_DIV_CLK_FSYS0_MMC_CARD0 0x1820 +#define CLK_CON_DIV_CLK_FSYS0_MMC_CARD1 0x1824 +#define CLK_CON_DIV_CLK_FSYS0_MMC_CARD2 0x1828 +#define CLK_CON_DIV_CLK_FSYS0_QSPI 0x182c +#define CLK_CON_DIV_CLK_FSYS0_SFMC_NAND 0x1830 +#define CLK_CON_FSYS0_I2C0_IPCLKPORT_I_PCLK 0x2040 +#define CLK_CON_FSYS0_I2C1_IPCLKPORT_I_PCLK 0x2044 +#define CLK_CON_MMC0_IPCLKPORT_I_ACLK 0x2078 +#define CLK_CON_MMC1_IPCLKPORT_I_ACLK 0x2080 +#define CLK_CON_MMC2_IPCLKPORT_I_ACLK 0x2088 +#define CLK_CON_PWM_IPCLKPORT_I_PCLK_S0 0x2090 +#define CLK_CON_DMYQCH_CON_ADC_WRAP_QCH 0x3000 +#define CLK_CON_DMYQCH_CON_EQOS_TOP0_QCH 0x3004 +#define CLK_CON_DMYQCH_CON_EQOS_TOP1_QCH 0x3008 +#define CLK_CON_DMYQCH_CON_FSYS0_I3C0_QCH 0x3010 +#define CLK_CON_DMYQCH_CON_FSYS0_I3C1_QCH 0x3014 +#define CLK_CON_DMYQCH_CON_MMC0_QCH 0x3018 +#define CLK_CON_DMYQCH_CON_MMC1_QCH 0x301c +#define CLK_CON_DMYQCH_CON_MMC2_QCH 0x3020 +#define CLK_CON_DMYQCH_CON_QSPI_QCH 0x3024 +#define CLK_CON_DMYQCH_CON_SFMC_QCH 0x3028 + +static const unsigned long cmu_fsys0_clk_regs[] __initconst = { + PLL_CON0_MUX_CLK_FSYS0_BUS_USER, + PLL_CON0_MUX_CLK_FSYS0_IP_USER, + PLL_CON0_MUX_CLK_FSYS0_MAIN_USER, + CLK_CON_DIV_CLK_FSYS0_125, + CLK_CON_DIV_CLK_FSYS0_ADC, + CLK_CON_DIV_CLK_FSYS0_BUS_300, + CLK_CON_DIV_CLK_FSYS0_EQOS0, + CLK_CON_DIV_CLK_FSYS0_EQOS1, + CLK_CON_DIV_CLK_FSYS0_EQOS_250, + CLK_CON_DIV_CLK_FSYS0_MMC_CARD0, + CLK_CON_DIV_CLK_FSYS0_MMC_CARD1, + CLK_CON_DIV_CLK_FSYS0_MMC_CARD2, + CLK_CON_DIV_CLK_FSYS0_QSPI, + CLK_CON_DIV_CLK_FSYS0_SFMC_NAND, + CLK_CON_FSYS0_I2C0_IPCLKPORT_I_PCLK, + CLK_CON_FSYS0_I2C1_IPCLKPORT_I_PCLK, + CLK_CON_MMC0_IPCLKPORT_I_ACLK, + CLK_CON_MMC1_IPCLKPORT_I_ACLK, + CLK_CON_MMC2_IPCLKPORT_I_ACLK, + CLK_CON_PWM_IPCLKPORT_I_PCLK_S0, + CLK_CON_DMYQCH_CON_ADC_WRAP_QCH, + CLK_CON_DMYQCH_CON_EQOS_TOP0_QCH, + CLK_CON_DMYQCH_CON_EQOS_TOP1_QCH, + CLK_CON_DMYQCH_CON_FSYS0_I3C0_QCH, + CLK_CON_DMYQCH_CON_FSYS0_I3C1_QCH, + CLK_CON_DMYQCH_CON_MMC0_QCH, + CLK_CON_DMYQCH_CON_MMC1_QCH, + CLK_CON_DMYQCH_CON_MMC2_QCH, + CLK_CON_DMYQCH_CON_QSPI_QCH, + CLK_CON_DMYQCH_CON_SFMC_QCH, +}; + +PNAME(mout_fsys0_bus_user_p) = { "fin_pll", "dout_clkcmu_fsys0_bus" }; +PNAME(mout_fsys0_ip_user_p) = { "fin_pll", "dout_clkcmu_fsys0_ip" }; +PNAME(mout_fsys0_main_user_p) = { "fin_pll", "fout_pll_fsys1" }; + +static const struct samsung_mux_clock cmu_fsys0_mux_clks[] __initconst = { + MUX(CLK_MOUT_FSYS0_BUS_USER, "mout_fsys0_bus_user", + mout_fsys0_bus_user_p, PLL_CON0_MUX_CLK_FSYS0_BUS_USER, 4, 1), + MUX(CLK_MOUT_FSYS0_IP_USER, "mout_fsys0_ip_user", + mout_fsys0_ip_user_p, PLL_CON0_MUX_CLK_FSYS0_IP_USER, 4, 1), + MUX(CLK_MOUT_FSYS0_MAIN_USER, "mout_fsys0_main_user", + mout_fsys0_main_user_p, PLL_CON0_MUX_CLK_FSYS0_MAIN_USER, 4, 1), +}; + +static const struct samsung_div_clock cmu_fsys0_div_clks[] __initconst = { + DIV(CLK_DOUT_FSYS0_125, "dout_fsys0_125", "mout_fsys0_main_user", + CLK_CON_DIV_CLK_FSYS0_125, 0, 5), + DIV(CLK_DOUT_FSYS0_ADC, "dout_fsys0_adc", "mout_fsys0_main_user", + CLK_CON_DIV_CLK_FSYS0_ADC, 0, 7), + DIV(CLK_DOUT_FSYS0_BUS_300, "dout_fsys0_bus_300", "mout_fsys0_bus_user", + CLK_CON_DIV_CLK_FSYS0_BUS_300, 0, 4), + DIV(CLK_DOUT_FSYS0_EQOS0, "dout_fsys0_eqos0", "dout_fsys0_eqos_250", + CLK_CON_DIV_CLK_FSYS0_EQOS0, 0, 7), + DIV(CLK_DOUT_FSYS0_EQOS1, "dout_fsys0_eqos1", "dout_fsys0_eqos_250", + CLK_CON_DIV_CLK_FSYS0_EQOS1, 0, 7), + DIV(0, "dout_fsys0_eqos_250", "mout_fsys0_main_user", + CLK_CON_DIV_CLK_FSYS0_EQOS_250, 0, 4), + DIV(CLK_DOUT_FSYS0_MMC_CARD0, "dout_fsys0_mmc_card0", "mout_fsys0_ip_user", + CLK_CON_DIV_CLK_FSYS0_MMC_CARD0, 0, 10), + DIV(CLK_DOUT_FSYS0_MMC_CARD1, "dout_fsys0_mmc_card1", "mout_fsys0_ip_user", + CLK_CON_DIV_CLK_FSYS0_MMC_CARD1, 0, 10), + DIV(CLK_DOUT_FSYS0_MMC_CARD2, "dout_fsys0_mmc_card2", "mout_fsys0_ip_user", + CLK_CON_DIV_CLK_FSYS0_MMC_CARD2, 0, 10), + DIV(CLK_DOUT_FSYS0_QSPI, "dout_fsys0_qspi", "mout_fsys0_ip_user", + CLK_CON_DIV_CLK_FSYS0_QSPI, 0, 4), + DIV(CLK_DOUT_FSYS0_SFMC_NAND, "dout_fsys0_sfmc_nand", "mout_fsys0_ip_user", + CLK_CON_DIV_CLK_FSYS0_SFMC_NAND, 0, 4), +}; + +static const struct samsung_gate_clock cmu_fsys0_gate_clks[] __initconst = { + GATE(0, "adc_wrap_ipclkport_clk", "dout_fsys0_adc", + CLK_CON_DMYQCH_CON_ADC_WRAP_QCH, 1, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_GOUT_FSYS0_EQOS_TOP0_IPCLKPORT_ACLK_I, "eqos_top0_ipclkport_aclk_i", + "dout_fsys0_bus_300", CLK_CON_DMYQCH_CON_EQOS_TOP0_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_EQOS_TOP0_IPCLKPORT_CLK_CSR_I, "eqos_top0_ipclkport_clk_csr_i", + "dout_fsys0_bus_300", CLK_CON_DMYQCH_CON_EQOS_TOP0_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_EQOS_TOP0_IPCLKPORT_I_RGMII_PHASE_CLK_250, + "eqos_top0_ipclkport_i_rgmii_phase_clk_250", + "dout_fsys0_eqos_250", CLK_CON_DMYQCH_CON_EQOS_TOP0_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_EQOS_TOP0_IPCLKPORT_I_RGMII_TXCLK, "eqos_top0_ipclkport_i_rgmii_txclk", + "dout_fsys0_eqos0", CLK_CON_DMYQCH_CON_EQOS_TOP0_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_EQOS_TOP1_IPCLKPORT_I_RGMII_PHASE_CLK_250, + "eqos_top1_ipclkport_i_rgmii_phase_clk_250", + "dout_fsys0_eqos_250", CLK_CON_DMYQCH_CON_EQOS_TOP1_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_EQOS_TOP1_IPCLKPORT_I_RGMII_TXCLK, "eqos_top1_ipclkport_i_rgmii_txclk", + "dout_fsys0_eqos1", CLK_CON_DMYQCH_CON_EQOS_TOP1_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_EQOS_TOP1_IPCLKPORT_ACLK_I, "eqos_top1_ipclkport_aclk_i", + "dout_fsys0_bus_300", CLK_CON_DMYQCH_CON_EQOS_TOP1_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_EQOS_TOP1_IPCLKPORT_CLK_CSR_I, "eqos_top1_ipclkport_clk_csr_i", + "dout_fsys0_bus_300", CLK_CON_DMYQCH_CON_EQOS_TOP1_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_I3C0_IPCLKPORT_I_APB_S_PCLK, "i3c0_ipclkport_i_apb_s_pclk", + "dout_fsys0_bus_300", CLK_CON_DMYQCH_CON_FSYS0_I3C0_QCH, 1, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS0_I3C0_IPCLKPORT_I_CORE_CLK, "i3c0_ipclkport_i_core_clk", + "dout_fsys0_125", CLK_CON_DMYQCH_CON_FSYS0_I3C0_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_I3C0_IPCLKPORT_I_DMA_CLK, "i3c0_ipclkport_i_dma_clk", + "dout_fsys0_bus_300", CLK_CON_DMYQCH_CON_FSYS0_I3C0_QCH, + 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS0_I3C0_IPCLKPORT_I_HDR_TX_CLK, "i3c0_ipclkport_i_hdr_tx_clk", + "dout_fsys0_bus_300", CLK_CON_DMYQCH_CON_FSYS0_I3C0_QCH, + 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS0_I3C1_IPCLKPORT_I_APB_S_PCLK, "i3c1_ipclkport_i_apb_s_pclk", + "dout_fsys0_bus_300", CLK_CON_DMYQCH_CON_FSYS0_I3C1_QCH, + 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS0_I3C1_IPCLKPORT_I_CORE_CLK, "i3c1_ipclkport_i_core_clk", + "dout_fsys0_125", CLK_CON_DMYQCH_CON_FSYS0_I3C1_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_I3C1_IPCLKPORT_I_DMA_CLK, "i3c1_ipclkport_i_dma_clk", + "dout_fsys0_bus_300", CLK_CON_DMYQCH_CON_FSYS0_I3C1_QCH, + 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS0_I3C1_IPCLKPORT_I_HDR_TX_CLK, "i3c1_ipclkport_i_hdr_tx_clk", + "dout_fsys0_bus_300", CLK_CON_DMYQCH_CON_FSYS0_I3C1_QCH, + 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS0_MMC0_IPCLKPORT_SDCLKIN, "mmc0_ipclkport_sdclkin", + "dout_fsys0_mmc_card0", CLK_CON_DMYQCH_CON_MMC0_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_MMC1_IPCLKPORT_SDCLKIN, "mmc1_ipclkport_sdclkin", + "dout_fsys0_mmc_card1", CLK_CON_DMYQCH_CON_MMC1_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_MMC2_IPCLKPORT_SDCLKIN, "mmc2_ipclkport_sdclkin", + "dout_fsys0_mmc_card2", CLK_CON_DMYQCH_CON_MMC2_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_QSPI_IPCLKPORT_HCLK, "qspi_ipclkport_hclk", + "dout_fsys0_bus_300", CLK_CON_DMYQCH_CON_QSPI_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_QSPI_IPCLKPORT_SSI_CLK, "qspi_ipclkport_ssi_clk", "dout_fsys0_qspi", + CLK_CON_DMYQCH_CON_QSPI_QCH, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS0_SFMC_IPCLKPORT_I_ACLK_NAND, "sfmc_ipclkport_i_aclk_nand", + "dout_fsys0_sfmc_nand", CLK_CON_DMYQCH_CON_SFMC_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_FSYS0_I2C0_IPCLKPORT_I_PCLK, "i2c0_ipclkport_i_pclk", "dout_fsys0_bus_300", + CLK_CON_FSYS0_I2C0_IPCLKPORT_I_PCLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS0_I2C1_IPCLKPORT_I_PCLK, "i2c1_ipclkport_i_pclk", "dout_fsys0_bus_300", + CLK_CON_FSYS0_I2C1_IPCLKPORT_I_PCLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS0_MMC0_IPCLKPORT_I_ACLK, "mmc0_ipclkport_i_aclk", "dout_fsys0_bus_300", + CLK_CON_MMC0_IPCLKPORT_I_ACLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS0_MMC1_IPCLKPORT_I_ACLK, "mmc1_ipclkport_i_aclk", "dout_fsys0_bus_300", + CLK_CON_MMC1_IPCLKPORT_I_ACLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS0_MMC2_IPCLKPORT_I_ACLK, "mmc2_ipclkport_i_aclk", "dout_fsys0_bus_300", + CLK_CON_MMC2_IPCLKPORT_I_ACLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS0_PWM_IPCLKPORT_I_PCLK_S0, "pwm_ipclkport_i_pclk", "dout_fsys0_bus_300", + CLK_CON_PWM_IPCLKPORT_I_PCLK_S0, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), +}; + +static const struct samsung_cmu_info cmu_fsys0_info __initconst = { + .mux_clks = cmu_fsys0_mux_clks, + .nr_mux_clks = ARRAY_SIZE(cmu_fsys0_mux_clks), + .div_clks = cmu_fsys0_div_clks, + .nr_div_clks = ARRAY_SIZE(cmu_fsys0_div_clks), + .gate_clks = cmu_fsys0_gate_clks, + .nr_gate_clks = ARRAY_SIZE(cmu_fsys0_gate_clks), + .nr_clk_ids = CMU_FSYS0_NR_CLK, + .clk_regs = cmu_fsys0_clk_regs, + .nr_clk_regs = ARRAY_SIZE(cmu_fsys0_clk_regs), +}; + +/* Register Offset definitions for CMU_FSYS1 (0x14c10000) */ +#define PLL_LOCKTIME_PLL_FSYS1 0x0000 +#define PLL_CON0_MUX_CLK_FSYS1_BUS_USER 0x0100 +#define PLL_CON0_MUX_CLK_FSYS1_SCAN0_USER 0x0120 +#define PLL_CON0_MUX_CLK_FSYS1_SCAN1_USER 0x0140 +#define PLL_CON0_PLL_FSYS1 0x0160 +#define CLK_CON_DIV_CLK_FSYS1_200 0x1808 +#define CLK_CON_DIV_CLK_FSYS1_BUS_300 0x1810 +#define CLK_CON_DIV_CLK_FSYS1_OTP_MEM 0x1814 +#define CLK_CON_DIV_CLK_FSYS1_PCIE_PHY_REFCLK_SYSPLL 0x1818 +#define CLK_CON_FSYS1_UART0_IPCLKPORT_I_PCLK 0x202c +#define CLK_CON_FSYS1_UART0_IPCLKPORT_I_SCLK_UART 0x2030 +#define CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_PHY_APB2CR_PCLK_300 0x205c +#define CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X1_DBI_ACLK_SOC 0x2068 +#define CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X1_MSTR_ACLK_SOC 0x206c +#define CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X1_SLV_ACLK_SOC 0x2070 +#define CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X2_DBI_ACLK_SOC 0x2078 +#define CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X2_MSTR_ACLK_SOC 0x2080 +#define CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X2_SLV_ACLK_SOC 0x2084 +#define CLK_CON_USB20DRD_IPCLKPORT_ACLK_PHYCTRL_20 0x209c +#define CLK_CON_USB20DRD_IPCLKPORT_BUS_CLK_EARLY 0x20a0 +#define CLK_CON_XHB_AHBBR_FSYS1_IPCLKPORT_CLK 0x20a8 +#define CLK_CON_XHB_USB_IPCLKPORT_CLK 0x20ac +#define CLK_CON_DMYQCH_CON_TZ400_QCH 0x3004 +#define CLK_CON_DMYQCH_CON_PCIE_TOP_QCH_PHY_100 0x309c +#define CLK_CON_QCH_CON_MMU_FSYS1_QCH_U_TBU_0_0 0x3050 +#define CLK_CON_QCH_CON_MMU_FSYS1_QCH_U_TBU_1_0 0x3058 + +static const unsigned long cmu_fsys1_clk_regs[] __initconst = { + PLL_LOCKTIME_PLL_FSYS1, + PLL_CON0_MUX_CLK_FSYS1_BUS_USER, + PLL_CON0_MUX_CLK_FSYS1_SCAN0_USER, + PLL_CON0_MUX_CLK_FSYS1_SCAN1_USER, + PLL_CON0_PLL_FSYS1, + CLK_CON_DIV_CLK_FSYS1_200, + CLK_CON_DIV_CLK_FSYS1_BUS_300, + CLK_CON_DIV_CLK_FSYS1_OTP_MEM, + CLK_CON_DIV_CLK_FSYS1_PCIE_PHY_REFCLK_SYSPLL, + CLK_CON_FSYS1_UART0_IPCLKPORT_I_PCLK, + CLK_CON_FSYS1_UART0_IPCLKPORT_I_SCLK_UART, + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_PHY_APB2CR_PCLK_300, + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X1_DBI_ACLK_SOC, + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X1_MSTR_ACLK_SOC, + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X1_SLV_ACLK_SOC, + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X2_DBI_ACLK_SOC, + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X2_MSTR_ACLK_SOC, + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X2_SLV_ACLK_SOC, + CLK_CON_USB20DRD_IPCLKPORT_ACLK_PHYCTRL_20, + CLK_CON_USB20DRD_IPCLKPORT_BUS_CLK_EARLY, + CLK_CON_XHB_AHBBR_FSYS1_IPCLKPORT_CLK, + CLK_CON_XHB_USB_IPCLKPORT_CLK, + CLK_CON_DMYQCH_CON_TZ400_QCH, + CLK_CON_DMYQCH_CON_PCIE_TOP_QCH_PHY_100, + CLK_CON_QCH_CON_MMU_FSYS1_QCH_U_TBU_0_0, + CLK_CON_QCH_CON_MMU_FSYS1_QCH_U_TBU_1_0 +}; + +static const struct samsung_pll_rate_table artpec9_pll_fsys1_rates[] __initconst = { + PLL_35XX_RATE(25 * MHZ, 2000000000U, 80, 1, 0), +}; + +static const struct samsung_pll_clock cmu_fsys1_pll_clks[] __initconst = { + PLL(pll_a9fracm, CLK_FOUT_FSYS1_PLL, "fout_pll_fsys1", "fin_pll", + PLL_LOCKTIME_PLL_FSYS1, PLL_CON0_PLL_FSYS1, artpec9_pll_fsys1_rates), +}; + +PNAME(mout_fsys1_scan0_user_p) = { "fin_pll", "dout_clkcmu_fsys1_scan0" }; +PNAME(mout_fsys1_scan1_user_p) = { "fin_pll", "dout_clkcmu_fsys1_scan1" }; +PNAME(mout_fsys1_bus_user_p) = { "fin_pll", "dout_clkcmu_fsys1_bus" }; +PNAME(mout_fsys_pll_fsys_p) = { "fin_pll", "fout_pll_fsys1" }; + +static const struct samsung_mux_clock cmu_fsys1_mux_clks[] __initconst = { + MUX(0, "mout_clk_pll_fsys1", mout_fsys_pll_fsys_p, PLL_CON0_PLL_FSYS1, 4, 1), + MUX(CLK_MOUT_FSYS1_SCAN0_USER, "mout_fsys1_scan0_user", + mout_fsys1_scan0_user_p, PLL_CON0_MUX_CLK_FSYS1_SCAN0_USER, 4, 1), + MUX(CLK_MOUT_FSYS1_SCAN1_USER, "mout_fsys1_scan1_user", + mout_fsys1_scan1_user_p, PLL_CON0_MUX_CLK_FSYS1_SCAN1_USER, 4, 1), + MUX(CLK_MOUT_FSYS1_BUS_USER, "mout_fsys1_bus_user", + mout_fsys1_bus_user_p, PLL_CON0_MUX_CLK_FSYS1_BUS_USER, 4, 1), +}; + +static const struct samsung_div_clock cmu_fsys1_div_clks[] __initconst = { + DIV(CLK_DOUT_FSYS1_200, "dout_fsys1_200", "mout_clk_pll_fsys1", + CLK_CON_DIV_CLK_FSYS1_200, 0, 4), + DIV(CLK_DOUT_FSYS1_BUS_300, "dout_fsys1_bus_300", "mout_fsys1_bus_user", + CLK_CON_DIV_CLK_FSYS1_BUS_300, 0, 4), + DIV(CLK_DOUT_FSYS1_OTP_MEM, "dout_fsys1_otp_mem", "fin_pll", + CLK_CON_DIV_CLK_FSYS1_OTP_MEM, 0, 4), + DIV(CLK_DOUT_FSYS1_PCIE_PHY_REFCLK_SYSPLL, "dout_fsys1_pcie_phy_refclk_syspll", + "mout_clk_pll_fsys1", CLK_CON_DIV_CLK_FSYS1_PCIE_PHY_REFCLK_SYSPLL, 0, 5), +}; + +static const struct samsung_gate_clock cmu_fsys1_gate_clks[] __initconst = { + GATE(CLK_GOUT_FSYS1_IPCLKPORT_PCIE_PHY_APB2CR_PCLK_100, + "pcie_top_ipclkport_pcie_phy_apb2cr_pclk_100", "dout_fsys1_pcie_phy_refclk_syspll", + CLK_CON_DMYQCH_CON_PCIE_TOP_QCH_PHY_100, 1, CLK_SET_RATE_PARENT, 0), + GATE(0, "tzc400_ipclkport_aclk0", "mout_fsys1_bus_user", + CLK_CON_DMYQCH_CON_TZ400_QCH, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(0, "tzc400_ipclkport_aclk1", "mout_fsys1_bus_user", + CLK_CON_DMYQCH_CON_TZ400_QCH, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(0, "tzc400_ipclkport_pclk", "dout_fsys1_bus_300", + CLK_CON_DMYQCH_CON_TZ400_QCH, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_UART0_PCLK, "uart", "dout_fsys1_bus_300", + CLK_CON_FSYS1_UART0_IPCLKPORT_I_PCLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_UART0_SCLK_UART, "clk_uart_baud0", "dout_fsys1_200", + CLK_CON_FSYS1_UART0_IPCLKPORT_I_SCLK_UART, 21, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_IPCLKPORT_PCIE_PHY_APB2CR_PCLK_300, + "pcie_top_ipclkport_pcie_phy_apb2cr_pclk_300", "dout_fsys1_bus_300", + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_PHY_APB2CR_PCLK_300, 21, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_IPCLKPORT_PCIE_SUB_CON_X1_DBI_ACLK_SOC, + "pcie_top_ipclkport_pcie_sub_con_x1_dbi_aclk_soc", "dout_fsys1_bus_300", + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X1_DBI_ACLK_SOC, + 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_IPCLKPORT_PCIE_SUB_CON_X1_MSTR_ACLK_SOC, + "pcie_top_ipclkport_pcie_sub_con_x1_mstr_aclk_soc", "mout_fsys1_bus_user", + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X1_MSTR_ACLK_SOC, + 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_IPCLKPORT_PCIE_SUB_CON_X1_SLV_ACLK_SOC, + "pcie_top_ipclkport_pcie_sub_con_x1_slv_aclk_soc", "mout_fsys1_bus_user", + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X1_SLV_ACLK_SOC, + 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_IPCLKPORT_PCIE_SUB_CON_X2_DBI_ACLK_SOC, + "pcie_top_ipclkport_pcie_sub_con_x2_dbi_aclk_soc", "dout_fsys1_bus_300", + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X2_DBI_ACLK_SOC, + 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_IPCLKPORT_PCIE_SUB_CON_X2_MSTR_ACLK_SOC, + "pcie_top_ipclkport_pcie_sub_con_x2_mstr_aclk_soc", "mout_fsys1_bus_user", + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X2_MSTR_ACLK_SOC, + 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_IPCLKPORT_PCIE_SUB_CON_X2_SLV_ACLK_SOC, + "pcie_top_ipclkport_pcie_sub_con_x2_slv_aclk_soc", "mout_fsys1_bus_user", + CLK_CON_PCIE_TOP_IPCLKPORT_PCIE_SUB_CON_X2_SLV_ACLK_SOC, + 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_USB20DRD_IPCLKPORT_ACLK_PHYCTRL_20, + "usb20drd_ipclkport_aclk_phyctrl_20", "dout_fsys1_bus_300", + CLK_CON_USB20DRD_IPCLKPORT_ACLK_PHYCTRL_20, + 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_USB20DRD_IPCLKPORT_BUS_CLK_EARLY, "usb20drd_ipclkport_bus_clk_early", + "dout_fsys1_bus_300", CLK_CON_USB20DRD_IPCLKPORT_BUS_CLK_EARLY, + 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_XHB_AHBBR_FSYS1_IPCLKPORT_CLK, "xhb_ahbbr_fsys1_ipclkport_clk", + "dout_fsys1_bus_300", CLK_CON_XHB_AHBBR_FSYS1_IPCLKPORT_CLK, + 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_FSYS1_XHB_USB_IPCLKPORT_CLK, "xhb_usb_ipclkport_clk", "dout_fsys1_bus_300", + CLK_CON_XHB_USB_IPCLKPORT_CLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(0, "qch_con_mmu_fsys1_qch_u_tbu_0_0", "mout_fsys1_bus_user", + CLK_CON_QCH_CON_MMU_FSYS1_QCH_U_TBU_0_0, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(0, "qch_con_mmu_fsys1_qch_u_tbu_1_0", "mout_fsys1_bus_user", + CLK_CON_QCH_CON_MMU_FSYS1_QCH_U_TBU_1_0, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), +}; + +static const struct samsung_cmu_info cmu_fsys1_info __initconst = { + .pll_clks = cmu_fsys1_pll_clks, + .nr_pll_clks = ARRAY_SIZE(cmu_fsys1_pll_clks), + .mux_clks = cmu_fsys1_mux_clks, + .nr_mux_clks = ARRAY_SIZE(cmu_fsys1_mux_clks), + .div_clks = cmu_fsys1_div_clks, + .nr_div_clks = ARRAY_SIZE(cmu_fsys1_div_clks), + .gate_clks = cmu_fsys1_gate_clks, + .nr_gate_clks = ARRAY_SIZE(cmu_fsys1_gate_clks), + .nr_clk_ids = CMU_FSYS1_NR_CLK, + .clk_regs = cmu_fsys1_clk_regs, + .nr_clk_regs = ARRAY_SIZE(cmu_fsys1_clk_regs), +}; + +/* Register Offset definitions for CMU_IMEM (0x10010000) */ +#define PLL_CON0_MUX_CLK_IMEM_ACLK_USER 0x0100 +#define PLL_CON0_MUX_CLK_IMEM_CA5_USER 0x0120 +#define PLL_CON0_MUX_CLK_IMEM_JPEG_USER 0x0140 +#define PLL_CON0_MUX_CLK_IMEM_SSS_USER 0x0160 +#define CLK_CON_MCT0_IPCLKPORT_PCLK 0x20b4 +#define CLK_CON_MCT1_IPCLKPORT_PCLK 0x20b8 +#define CLK_CON_MCT2_IPCLKPORT_PCLK 0x20bc +#define CLK_CON_MCT3_IPCLKPORT_PCLK 0x20c0 +#define CLK_CON_TMU_APB_IPCLKPORT_PCLK 0x20d4 +#define CLK_CON_DMYQCH_CON_CA5_0_QCH 0x3008 +#define CLK_CON_DMYQCH_CON_CA5_1_QCH 0x3018 +#define CLK_CON_DMYQCH_CON_INTMEM_QCH 0x3020 +#define CLK_CON_QCH_CON_GIC_CA55_QCHANNEL_SLAVE_0 0x306c +#define CLK_CON_QCH_CON_GIC_CA5_0_QCH 0x3078 +#define CLK_CON_QCH_CON_GIC_CA5_1_QCH 0x307c +#define CLK_CON_QCH_CON_MMU_IMEM_QCH_U_TBU_0_0 0x30ac +#define CLK_CON_QCH_CON_MMU_IMEM_QCH_U_TBU_1_0 0x30b4 + +static const unsigned long cmu_imem_clk_regs[] __initconst = { + PLL_CON0_MUX_CLK_IMEM_ACLK_USER, + PLL_CON0_MUX_CLK_IMEM_CA5_USER, + PLL_CON0_MUX_CLK_IMEM_JPEG_USER, + PLL_CON0_MUX_CLK_IMEM_SSS_USER, + CLK_CON_MCT0_IPCLKPORT_PCLK, + CLK_CON_MCT1_IPCLKPORT_PCLK, + CLK_CON_MCT2_IPCLKPORT_PCLK, + CLK_CON_MCT3_IPCLKPORT_PCLK, + CLK_CON_TMU_APB_IPCLKPORT_PCLK, + CLK_CON_DMYQCH_CON_CA5_0_QCH, + CLK_CON_DMYQCH_CON_CA5_1_QCH, + CLK_CON_DMYQCH_CON_INTMEM_QCH, + CLK_CON_QCH_CON_GIC_CA55_QCHANNEL_SLAVE_0, + CLK_CON_QCH_CON_GIC_CA5_0_QCH, + CLK_CON_QCH_CON_GIC_CA5_1_QCH, + CLK_CON_QCH_CON_MMU_IMEM_QCH_U_TBU_0_0, + CLK_CON_QCH_CON_MMU_IMEM_QCH_U_TBU_1_0 +}; + +PNAME(mout_imem_aclk_user_p) = { "fin_pll", "dout_clkcmu_imem_aclk" }; +PNAME(mout_imem_ca5_user_p) = { "fin_pll", "dout_clkcmu_imem_ca5" }; +PNAME(mout_imem_jpeg_user_p) = { "fin_pll", "dout_clkcmu_imem_jpeg" }; +PNAME(mout_imem_sss_user_p) = { "fin_pll", "dout_clkcmu_imem_sss" }; + +static const struct samsung_mux_clock cmu_imem_mux_clks[] __initconst = { + MUX(CLK_MOUT_IMEM_ACLK_USER, "mout_clk_imem_aclk_user", + mout_imem_aclk_user_p, PLL_CON0_MUX_CLK_IMEM_ACLK_USER, 4, 1), + MUX(CLK_MOUT_IMEM_CA5_USER, "mout_clk_imem_ca5_user", + mout_imem_ca5_user_p, PLL_CON0_MUX_CLK_IMEM_CA5_USER, 4, 1), + MUX(CLK_MOUT_IMEM_SSS_USER, "mout_clk_imem_sss_user", + mout_imem_sss_user_p, PLL_CON0_MUX_CLK_IMEM_SSS_USER, 4, 1), + MUX(CLK_MOUT_IMEM_JPEG_USER, "mout_clk_imem_jpeg_user", + mout_imem_jpeg_user_p, PLL_CON0_MUX_CLK_IMEM_JPEG_USER, 4, 1), +}; + +static const struct samsung_fixed_factor_clock imem_ffactor_clks[] __initconst = { + FFACTOR(CLK_DOUT_IMEM_PCLK, "dout_clk_imem_pclk", "mout_clk_imem_aclk_user", 1, 2, 0), +}; + +static const struct samsung_gate_clock cmu_imem_gate_clks[] __initconst = { + GATE(CLK_GOUT_IMEM_CA5_0_IPCLKPORT_ATCLK, "ca5_0_ipclkport_atclk", + "mout_clk_imem_ca5_user", CLK_CON_DMYQCH_CON_CA5_0_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_IMEM_CA5_0_IPCLKPORT_CLKIN, "ca5_0_ipclkport_clkin", + "mout_clk_imem_ca5_user", CLK_CON_DMYQCH_CON_CA5_0_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_IMEM_CA5_0_IPCLKPORT_PCLK_DBG, "ca5_0_ipclkport_pclk_dbg", + "mout_clk_imem_ca5_user", CLK_CON_DMYQCH_CON_CA5_0_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_IMEM_CA5_1_IPCLKPORT_ATCLK, "ca5_1_ipclkport_atclk", + "mout_clk_imem_ca5_user", CLK_CON_DMYQCH_CON_CA5_1_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_IMEM_CA5_1_IPCLKPORT_CLKIN, "ca5_1_ipclkport_clkin", + "mout_clk_imem_ca5_user", CLK_CON_DMYQCH_CON_CA5_1_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_IMEM_CA5_1_IPCLKPORT_PCLK_DBG, "ca5_1_ipclkport_pclk_dbg", + "mout_clk_imem_ca5_user", CLK_CON_DMYQCH_CON_CA5_1_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(0, "intmem_ipclkport_aclk", "mout_clk_imem_aclk_user", + CLK_CON_DMYQCH_CON_INTMEM_QCH, 1, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, 0), + GATE(CLK_GOUT_IMEM_MCT0_PCLK, "mct0", "dout_clk_imem_pclk", + CLK_CON_MCT0_IPCLKPORT_PCLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_IMEM_MCT1_PCLK, "mct1", "dout_clk_imem_pclk", + CLK_CON_MCT1_IPCLKPORT_PCLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_IMEM_MCT2_PCLK, "mct2", "dout_clk_imem_pclk", + CLK_CON_MCT2_IPCLKPORT_PCLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_IMEM_MCT3_PCLK, "mct3", "dout_clk_imem_pclk", + CLK_CON_MCT3_IPCLKPORT_PCLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_IMEM_PCLK_TMU0_APBIF, "tmu_apb_ipclkport_pclk", "dout_clk_imem_pclk", + CLK_CON_TMU_APB_IPCLKPORT_PCLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(0, "qch_con_gic_ca55_qchannel_slave_0", "dout_clk_imem_pclk", + CLK_CON_QCH_CON_GIC_CA55_QCHANNEL_SLAVE_0, 1, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(0, "qch_con_gic_ca5_0_qch", "dout_clk_imem_pclk", + CLK_CON_QCH_CON_GIC_CA5_0_QCH, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(0, "qch_con_gic_ca5_1_qch", "dout_clk_imem_pclk", + CLK_CON_QCH_CON_GIC_CA5_1_QCH, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(0, "qch_con_mmu_imem_qch_u_tbu_0_0", "mout_clk_imem_ca5_user", + CLK_CON_QCH_CON_MMU_IMEM_QCH_U_TBU_0_0, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(0, "qch_con_mmu_imem_qch_u_tbu_1_0", "mout_clk_imem_ca5_user", + CLK_CON_QCH_CON_MMU_IMEM_QCH_U_TBU_1_0, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), +}; + +static const struct samsung_cmu_info cmu_imem_info __initconst = { + .fixed_factor_clks = imem_ffactor_clks, + .nr_fixed_factor_clks = ARRAY_SIZE(imem_ffactor_clks), + .mux_clks = cmu_imem_mux_clks, + .nr_mux_clks = ARRAY_SIZE(cmu_imem_mux_clks), + .gate_clks = cmu_imem_gate_clks, + .nr_gate_clks = ARRAY_SIZE(cmu_imem_gate_clks), + .nr_clk_ids = CMU_IMEM_NR_CLK, + .clk_regs = cmu_imem_clk_regs, + .nr_clk_regs = ARRAY_SIZE(cmu_imem_clk_regs), +}; + +static void __init artpec9_cmu_imem_init(struct device_node *np) +{ + exynos_arm64_register_cmu(NULL, np, &cmu_imem_info); +} + +CLK_OF_DECLARE(artpec9_cmu_imem, "axis,artpec9-cmu-imem", artpec9_cmu_imem_init); + +/* Register Offset definitions for CMU_PERI (0x14010000) */ +#define PLL_CON0_MUX_CLK_PERI_DISP_USER 0x0100 +#define PLL_CON0_MUX_CLK_PERI_IP_USER 0x0120 +#define CLK_CON_DIV_CLK_PERI_125 0x1800 +#define CLK_CON_DIV_CLK_PERI_PCLK 0x180c +#define CLK_CON_DIV_CLK_PERI_SPI 0x1810 +#define CLK_CON_DIV_CLK_PERI_UART1 0x1814 +#define CLK_CON_DIV_CLK_PERI_UART2 0x1818 +#define CLK_CON_APB_ASYNC_DSIM_IPCLKPORT_PCLKS 0x2000 +#define CLK_CON_PERI_I2C2_IPCLKPORT_I_PCLK 0x202c +#define CLK_CON_PERI_I2C3_IPCLKPORT_I_PCLK 0x2030 +#define CLK_CON_PERI_SPI0_IPCLKPORT_I_PCLK 0x2054 +#define CLK_CON_PERI_SPI0_IPCLKPORT_I_SCLK_SPI 0x2058 +#define CLK_CON_PERI_UART1_IPCLKPORT_I_PCLK 0x205c +#define CLK_CON_PERI_UART1_IPCLKPORT_I_SCLK_UART 0x2060 +#define CLK_CON_PERI_UART2_IPCLKPORT_I_PCLK 0x2064 +#define CLK_CON_PERI_UART2_IPCLKPORT_I_SCLK_UART 0x2068 +#define CLK_CON_DMYQCH_CON_DMA4DSIM_QCH 0x3000 +#define CLK_CON_DMYQCH_CON_PERI_I3C2_QCH 0x3004 +#define CLK_CON_DMYQCH_CON_PERI_I3C3_QCH 0x3008 + +static const unsigned long cmu_peri_clk_regs[] __initconst = { + PLL_CON0_MUX_CLK_PERI_DISP_USER, + PLL_CON0_MUX_CLK_PERI_IP_USER, + CLK_CON_DIV_CLK_PERI_125, + CLK_CON_DIV_CLK_PERI_PCLK, + CLK_CON_DIV_CLK_PERI_SPI, + CLK_CON_DIV_CLK_PERI_UART1, + CLK_CON_DIV_CLK_PERI_UART2, + CLK_CON_APB_ASYNC_DSIM_IPCLKPORT_PCLKS, + CLK_CON_PERI_I2C2_IPCLKPORT_I_PCLK, + CLK_CON_PERI_I2C3_IPCLKPORT_I_PCLK, + CLK_CON_PERI_SPI0_IPCLKPORT_I_PCLK, + CLK_CON_PERI_SPI0_IPCLKPORT_I_SCLK_SPI, + CLK_CON_PERI_UART1_IPCLKPORT_I_PCLK, + CLK_CON_PERI_UART1_IPCLKPORT_I_SCLK_UART, + CLK_CON_PERI_UART2_IPCLKPORT_I_PCLK, + CLK_CON_PERI_UART2_IPCLKPORT_I_SCLK_UART, + CLK_CON_DMYQCH_CON_DMA4DSIM_QCH, + CLK_CON_DMYQCH_CON_PERI_I3C2_QCH, + CLK_CON_DMYQCH_CON_PERI_I3C3_QCH, +}; + +PNAME(mout_peri_ip_user_p) = { "fin_pll", "dout_clkcmu_peri_ip" }; +PNAME(mout_peri_disp_user_p) = { "fin_pll", "dout_clkcmu_peri_disp" }; + +static const struct samsung_mux_clock cmu_peri_mux_clks[] __initconst = { + MUX(CLK_MOUT_PERI_IP_USER, "mout_peri_ip_user", mout_peri_ip_user_p, + PLL_CON0_MUX_CLK_PERI_IP_USER, 4, 1), + MUX(CLK_MOUT_PERI_DISP_USER, "mout_peri_disp_user", mout_peri_disp_user_p, + PLL_CON0_MUX_CLK_PERI_DISP_USER, 4, 1), +}; + +static const struct samsung_div_clock cmu_peri_div_clks[] __initconst = { + DIV(CLK_DOUT_PERI_125, "dout_peri_125", "mout_peri_ip_user", + CLK_CON_DIV_CLK_PERI_125, 0, 4), + DIV(CLK_DOUT_PERI_PCLK, "dout_peri_pclk", "mout_peri_ip_user", + CLK_CON_DIV_CLK_PERI_PCLK, 0, 4), + DIV(CLK_DOUT_PERI_SPI, "dout_peri_spi", "mout_peri_ip_user", + CLK_CON_DIV_CLK_PERI_SPI, 0, 13), + DIV(CLK_DOUT_PERI_UART1, "dout_peri_uart1", "mout_peri_ip_user", + CLK_CON_DIV_CLK_PERI_UART1, 0, 10), + DIV(CLK_DOUT_PERI_UART2, "dout_peri_uart2", "mout_peri_ip_user", + CLK_CON_DIV_CLK_PERI_UART2, 0, 10), +}; + +static const struct samsung_gate_clock cmu_peri_gate_clks[] __initconst = { + GATE(CLK_GOUT_PERI_DMA4DSIM_IPCLKPORT_CLK_APB_CLK, "dma4dsim_ipclkport_clk_apb_clk", + "dout_peri_pclk", CLK_CON_DMYQCH_CON_DMA4DSIM_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_PERI_DMA4DSIM_IPCLKPORT_CLK_AXI_CLK, "dma4dsim_ipclkport_clk_axi_clk", + "mout_peri_disp_user", CLK_CON_DMYQCH_CON_DMA4DSIM_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_PERI_I3C2_IPCLKPORT_I_APB_S_PCLK, "peri_i3c2_ipclkport_i_apb_s_pclk", + "dout_peri_pclk", CLK_CON_DMYQCH_CON_PERI_I3C2_QCH, 1, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_I3C2_IPCLKPORT_I_CORE_CLK, "peri_i3c2_ipclkport_i_core_clk", + "dout_peri_125", CLK_CON_DMYQCH_CON_PERI_I3C2_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_PERI_I3C2_IPCLKPORT_I_DMA_CLK, "peri_i3c2_ipclkport_i_dma_clk", + "dout_peri_pclk", CLK_CON_DMYQCH_CON_PERI_I3C2_QCH, 1, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_I3C2_IPCLKPORT_I_HDR_TX_CLK, "peri_i3c2_ipclkport_i_hdr_tx_clk", + "dout_peri_pclk", CLK_CON_DMYQCH_CON_PERI_I3C2_QCH, 1, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_I3C3_IPCLKPORT_I_APB_S_PCLK, "peri_i3c3_ipclkport_i_apb_s_pclk", + "dout_peri_pclk", CLK_CON_DMYQCH_CON_PERI_I3C3_QCH, 1, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_I3C3_IPCLKPORT_I_CORE_CLK, "peri_i3c3_ipclkport_i_core_clk", + "dout_peri_125", CLK_CON_DMYQCH_CON_PERI_I3C3_QCH, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_GOUT_PERI_I3C3_IPCLKPORT_I_DMA_CLK, "peri_i3c3_ipclkport_i_dma_clk", + "dout_peri_pclk", CLK_CON_DMYQCH_CON_PERI_I3C3_QCH, 1, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_I3C3_IPCLKPORT_I_HDR_TX_CLK, "peri_i3c3_ipclkport_i_hdr_tx_clk", + "dout_peri_pclk", CLK_CON_DMYQCH_CON_PERI_I3C3_QCH, 1, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_APB_ASYNC_DSIM_IPCLKPORT_PCLKS, "apb_async_dsim_ipclkport_pclks", + "dout_peri_pclk", CLK_CON_APB_ASYNC_DSIM_IPCLKPORT_PCLKS, 21, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_I2C2_IPCLKPORT_I_PCLK, "peri_i2c2_ipclkport_i_pclk", + "dout_peri_pclk", CLK_CON_PERI_I2C2_IPCLKPORT_I_PCLK, 21, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_I2C3_IPCLKPORT_I_PCLK, "peri_i2c3_ipclkport_i_pclk", + "dout_peri_pclk", CLK_CON_PERI_I2C3_IPCLKPORT_I_PCLK, 21, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_SPI0_PCLK, "peri_spi0_ipclkport_i_pclk", + "dout_peri_pclk", CLK_CON_PERI_SPI0_IPCLKPORT_I_PCLK, 21, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_SPI0_SCLK_SPI, "peri_spi0_ipclkport_i_sclk_spi", + "dout_peri_spi", CLK_CON_PERI_SPI0_IPCLKPORT_I_SCLK_SPI, 21, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_UART1_PCLK, "uart1", "dout_peri_pclk", + CLK_CON_PERI_UART1_IPCLKPORT_I_PCLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_UART1_SCLK_UART, "clk_uart_baud1", "dout_peri_uart1", + CLK_CON_PERI_UART1_IPCLKPORT_I_SCLK_UART, 21, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_UART2_PCLK, "uart2", "dout_peri_pclk", + CLK_CON_PERI_UART2_IPCLKPORT_I_PCLK, 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), + GATE(CLK_GOUT_PERI_UART2_SCLK_UART, "clk_uart_baud2", "dout_peri_uart2", + CLK_CON_PERI_UART2_IPCLKPORT_I_SCLK_UART, + 21, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0), +}; + +static const struct samsung_cmu_info cmu_peri_info __initconst = { + .mux_clks = cmu_peri_mux_clks, + .nr_mux_clks = ARRAY_SIZE(cmu_peri_mux_clks), + .div_clks = cmu_peri_div_clks, + .nr_div_clks = ARRAY_SIZE(cmu_peri_div_clks), + .gate_clks = cmu_peri_gate_clks, + .nr_gate_clks = ARRAY_SIZE(cmu_peri_gate_clks), + .nr_clk_ids = CMU_PERI_NR_CLK, + .clk_regs = cmu_peri_clk_regs, + .nr_clk_regs = ARRAY_SIZE(cmu_peri_clk_regs), +}; + +static int __init artpec9_cmu_probe(struct platform_device *pdev) +{ + const struct samsung_cmu_info *info; + struct device *dev = &pdev->dev; + + info = of_device_get_match_data(dev); + exynos_arm64_register_cmu(dev, dev->of_node, info); + + return 0; +} + +static const struct of_device_id artpec9_cmu_of_match[] = { + { + .compatible = "axis,artpec9-cmu-cmu", + .data = &cmu_cmu_info, + }, { + .compatible = "axis,artpec9-cmu-bus", + .data = &cmu_bus_info, + }, { + .compatible = "axis,artpec9-cmu-core", + .data = &cmu_core_info, + }, { + .compatible = "axis,artpec9-cmu-cpucl", + .data = &cmu_cpucl_info, + }, { + .compatible = "axis,artpec9-cmu-fsys0", + .data = &cmu_fsys0_info, + }, { + .compatible = "axis,artpec9-cmu-fsys1", + .data = &cmu_fsys1_info, + }, { + .compatible = "axis,artpec9-cmu-peri", + .data = &cmu_peri_info, + }, { + }, +}; + +static struct platform_driver artpec9_cmu_driver __refdata = { + .driver = { + .name = "artpec9-cmu", + .of_match_table = artpec9_cmu_of_match, + .suppress_bind_attrs = true, + }, + .probe = artpec9_cmu_probe, +}; + +static int __init artpec9_cmu_init(void) +{ + return platform_driver_register(&artpec9_cmu_driver); +} +core_initcall(artpec9_cmu_init); diff --git a/drivers/clk/samsung/clk-exynos850.c b/drivers/clk/samsung/clk-exynos850.c index 56f27697c76b..eb9c80b60225 100644 --- a/drivers/clk/samsung/clk-exynos850.c +++ b/drivers/clk/samsung/clk-exynos850.c @@ -19,7 +19,7 @@ /* NOTE: Must be equal to the last clock ID increased by one */ #define CLKS_NR_TOP (CLK_DOUT_CPUCL1_SWITCH + 1) -#define CLKS_NR_APM (CLK_GOUT_SYSREG_APM_PCLK + 1) +#define CLKS_NR_APM (CLK_GOUT_MAILBOX_APM_AP_PCLK + 1) #define CLKS_NR_AUD (CLK_GOUT_AUD_CMU_AUD_PCLK + 1) #define CLKS_NR_CMGP (CLK_GOUT_SYSREG_CMGP_PCLK + 1) #define CLKS_NR_CPUCL0 (CLK_CLUSTER0_SCLK + 1) @@ -604,6 +604,7 @@ CLK_OF_DECLARE(exynos850_cmu_top, "samsung,exynos850-cmu-top", #define CLK_CON_GAT_GOUT_APM_APBIF_TOP_RTC_PCLK 0x2028 #define CLK_CON_GAT_GOUT_APM_I3C_APM_PMIC_I_PCLK 0x2034 #define CLK_CON_GAT_GOUT_APM_I3C_APM_PMIC_I_SCLK 0x2038 +#define CLK_CON_GAT_GOUT_APM_MAILBOX_APM_AP_PCLK 0x2060 #define CLK_CON_GAT_GOUT_APM_SPEEDY_APM_PCLK 0x20bc #define CLK_CON_GAT_GOUT_APM_SYSREG_APM_PCLK 0x20c0 @@ -628,6 +629,7 @@ static const unsigned long apm_clk_regs[] __initconst = { CLK_CON_GAT_GOUT_APM_I3C_APM_PMIC_I_SCLK, CLK_CON_GAT_GOUT_APM_SPEEDY_APM_PCLK, CLK_CON_GAT_GOUT_APM_SYSREG_APM_PCLK, + CLK_CON_GAT_GOUT_APM_MAILBOX_APM_AP_PCLK, }; /* List of parent clocks for Muxes in CMU_APM */ @@ -698,6 +700,9 @@ static const struct samsung_gate_clock apm_gate_clks[] __initconst = { CLK_CON_GAT_GOUT_APM_APBIF_PMU_ALIVE_PCLK, 21, CLK_IS_CRITICAL, 0), GATE(CLK_GOUT_SYSREG_APM_PCLK, "gout_sysreg_apm_pclk", "dout_apm_bus", CLK_CON_GAT_GOUT_APM_SYSREG_APM_PCLK, 21, 0, 0), + GATE(CLK_GOUT_MAILBOX_APM_AP_PCLK, "gout_mailbox_apm_ap_pclk", + "dout_apm_func", + CLK_CON_GAT_GOUT_APM_MAILBOX_APM_AP_PCLK, 21, 0, 0), }; static const struct samsung_cmu_info apm_cmu_info __initconst = { diff --git a/drivers/clk/samsung/clk-exynosautov920.c b/drivers/clk/samsung/clk-exynosautov920.c index d0617c7fff3a..04cd40c71d13 100644 --- a/drivers/clk/samsung/clk-exynosautov920.c +++ b/drivers/clk/samsung/clk-exynosautov920.c @@ -30,6 +30,7 @@ #define CLKS_NR_M2M (CLK_DOUT_M2M_NOCP + 1) #define CLKS_NR_MFC (CLK_DOUT_MFC_NOCP + 1) #define CLKS_NR_MFD (CLK_DOUT_MFD_NOCP + 1) +#define CLKS_NR_G3D (CLK_MOUT_G3D_NOCP_USER + 1) /* ---- CMU_TOP ------------------------------------------------------------ */ @@ -1942,6 +1943,54 @@ static const struct samsung_cmu_info mfd_cmu_info __initconst = { .clk_name = "noc", }; +/* ---- CMU_G3D --------------------------------------------------------- */ + +/* Register Offset definitions for CMU_G3D (0x1a000000) */ +#define PLL_LOCKTIME_PLL_G3D 0x0 +#define PLL_CON3_PLL_G3D 0x10c +#define CLK_CON_MUX_MUX_CLK_G3D_NOC 0x1000 +#define PLL_CON0_MUX_CLKCMU_G3D_NOCP_USER 0x600 +#define PLL_CON0_MUX_CLKCMU_G3D_SWITCH_USER 0x610 + +static const unsigned long g3d_clk_regs[] __initconst = { + PLL_LOCKTIME_PLL_G3D, + PLL_CON3_PLL_G3D, + CLK_CON_MUX_MUX_CLK_G3D_NOC, + PLL_CON0_MUX_CLKCMU_G3D_NOCP_USER, + PLL_CON0_MUX_CLKCMU_G3D_SWITCH_USER, +}; + +static const struct samsung_pll_clock g3d_pll_clks[] __initconst = { + /* CMU_G3D_PLL */ + PLL(pll_531x, FOUT_PLL_G3D, "fout_pll_g3d", "oscclk", + PLL_LOCKTIME_PLL_G3D, PLL_CON3_PLL_G3D, NULL), +}; + +/* List of parent clocks for Muxes in CMU_G3D */ +PNAME(mout_clk_g3d_noc_p) = { "oscclk", "fout_pll_g3d", "mout_clkcmu_g3d_switch_user"}; +PNAME(mout_clkcmu_g3d_switch_user_p) = { "oscclk", "dout_clkcmu_g3d_switch" }; +PNAME(mout_clkcmu_g3d_nocp_user_p) = { "oscclk", "dout_clkcmu_g3d_nocp" }; + +static const struct samsung_mux_clock g3d_mux_clks[] __initconst = { + MUX(CLK_MOUT_G3D_NOC, "mout_clk_g3d_noc", + mout_clk_g3d_noc_p, CLK_CON_MUX_MUX_CLK_G3D_NOC, 0, 2), + MUX(CLK_MOUT_G3D_SWITCH_USER, "mout_clkcmu_g3d_switch_user", + mout_clkcmu_g3d_switch_user_p, PLL_CON0_MUX_CLKCMU_G3D_SWITCH_USER, 4, 1), + MUX(CLK_MOUT_G3D_NOCP_USER, "mout_clkcmu_g3d_nocp_user", + mout_clkcmu_g3d_nocp_user_p, PLL_CON0_MUX_CLKCMU_G3D_NOCP_USER, 4, 1), +}; + +static const struct samsung_cmu_info g3d_cmu_info __initconst = { + .pll_clks = g3d_pll_clks, + .nr_pll_clks = ARRAY_SIZE(g3d_pll_clks), + .mux_clks = g3d_mux_clks, + .nr_mux_clks = ARRAY_SIZE(g3d_mux_clks), + .nr_clk_ids = CLKS_NR_G3D, + .clk_regs = g3d_clk_regs, + .nr_clk_regs = ARRAY_SIZE(g3d_clk_regs), + .clk_name = "noc", +}; + static int __init exynosautov920_cmu_probe(struct platform_device *pdev) { const struct samsung_cmu_info *info; @@ -1981,6 +2030,9 @@ static const struct of_device_id exynosautov920_cmu_of_match[] = { }, { .compatible = "samsung,exynosautov920-cmu-mfd", .data = &mfd_cmu_info, + }, { + .compatible = "samsung,exynosautov920-cmu-g3d", + .data = &g3d_cmu_info, }, { } }; diff --git a/drivers/clk/samsung/clk-gs101.c b/drivers/clk/samsung/clk-gs101.c index 44a8ecd332fd..d2bcd3a9daf8 100644 --- a/drivers/clk/samsung/clk-gs101.c +++ b/drivers/clk/samsung/clk-gs101.c @@ -339,7 +339,7 @@ #define GENERALIO_ACD_CHANNEL_3 0x3f0c #define GENERALIO_ACD_MASK 0x3f14 -static const unsigned long cmu_top_clk_regs[] __initconst = { +static const unsigned long top_clk_regs[] __initconst = { PLL_LOCKTIME_PLL_SHARED0, PLL_LOCKTIME_PLL_SHARED1, PLL_LOCKTIME_PLL_SHARED2, @@ -638,7 +638,7 @@ static const unsigned long cmu_top_clk_regs[] __initconst = { GENERALIO_ACD_MASK, }; -static const struct samsung_pll_clock cmu_top_pll_clks[] __initconst = { +static const struct samsung_pll_clock top_pll_clks[] __initconst = { /* CMU_TOP_PURECLKCOMP */ PLL(pll_0517x, CLK_FOUT_SHARED0_PLL, "fout_shared0_pll", "oscclk", PLL_LOCKTIME_PLL_SHARED0, PLL_CON3_PLL_SHARED0, @@ -952,7 +952,7 @@ PNAME(mout_cmu_cmuref_p) = { "mout_cmu_top_boost_option1", * For gates remove _UID _BLK _IPCLKPORT and _RSTNSYNC */ -static const struct samsung_mux_clock cmu_top_mux_clks[] __initconst = { +static const struct samsung_mux_clock top_mux_clks[] __initconst = { MUX(CLK_MOUT_PLL_SHARED0, "mout_pll_shared0", mout_pll_shared0_p, PLL_CON0_PLL_SHARED0, 4, 1), MUX(CLK_MOUT_PLL_SHARED1, "mout_pll_shared1", mout_pll_shared1_p, @@ -1108,7 +1108,7 @@ static const struct samsung_mux_clock cmu_top_mux_clks[] __initconst = { CLK_CON_MUX_MUX_CMU_CMUREF, 0, 1), }; -static const struct samsung_div_clock cmu_top_div_clks[] __initconst = { +static const struct samsung_div_clock top_div_clks[] __initconst = { DIV(CLK_DOUT_CMU_BO_BUS, "dout_cmu_bo_bus", "gout_cmu_bo_bus", CLK_CON_DIV_CLKCMU_BO_BUS, 0, 4), DIV(CLK_DOUT_CMU_BUS0_BUS, "dout_cmu_bus0_bus", "gout_cmu_bus0_bus", @@ -1253,13 +1253,13 @@ static const struct samsung_div_clock cmu_top_div_clks[] __initconst = { "mout_pll_shared3", CLK_CON_DIV_PLL_SHARED3_DIV2, 0, 1), }; -static const struct samsung_fixed_factor_clock cmu_top_ffactor[] __initconst = { +static const struct samsung_fixed_factor_clock top_ffactor_clks[] __initconst = { FFACTOR(CLK_DOUT_CMU_HSI0_USBDPDBG, "dout_cmu_hsi0_usbdpdbg", "gout_cmu_hsi0_usbdpdbg", 1, 4, 0), FFACTOR(CLK_DOUT_CMU_OTP, "dout_cmu_otp", "oscclk", 1, 8, 0), }; -static const struct samsung_gate_clock cmu_top_gate_clks[] __initconst = { +static const struct samsung_gate_clock top_gate_clks[] __initconst = { GATE(CLK_GOUT_CMU_BUS0_BOOST, "gout_cmu_bus0_boost", "mout_cmu_boost_option1", CLK_CON_GAT_CLKCMU_BUS0_BOOST, 21, 0, 0), GATE(CLK_GOUT_CMU_BUS1_BOOST, "gout_cmu_bus1_boost", @@ -1425,19 +1425,19 @@ static const struct samsung_gate_clock cmu_top_gate_clks[] __initconst = { }; static const struct samsung_cmu_info top_cmu_info __initconst = { - .pll_clks = cmu_top_pll_clks, - .nr_pll_clks = ARRAY_SIZE(cmu_top_pll_clks), - .mux_clks = cmu_top_mux_clks, - .nr_mux_clks = ARRAY_SIZE(cmu_top_mux_clks), - .div_clks = cmu_top_div_clks, - .nr_div_clks = ARRAY_SIZE(cmu_top_div_clks), - .fixed_factor_clks = cmu_top_ffactor, - .nr_fixed_factor_clks = ARRAY_SIZE(cmu_top_ffactor), - .gate_clks = cmu_top_gate_clks, - .nr_gate_clks = ARRAY_SIZE(cmu_top_gate_clks), + .pll_clks = top_pll_clks, + .nr_pll_clks = ARRAY_SIZE(top_pll_clks), + .mux_clks = top_mux_clks, + .nr_mux_clks = ARRAY_SIZE(top_mux_clks), + .div_clks = top_div_clks, + .nr_div_clks = ARRAY_SIZE(top_div_clks), + .fixed_factor_clks = top_ffactor_clks, + .nr_fixed_factor_clks = ARRAY_SIZE(top_ffactor_clks), + .gate_clks = top_gate_clks, + .nr_gate_clks = ARRAY_SIZE(top_gate_clks), .nr_clk_ids = CLKS_NR_TOP, - .clk_regs = cmu_top_clk_regs, - .nr_clk_regs = ARRAY_SIZE(cmu_top_clk_regs), + .clk_regs = top_clk_regs, + .nr_clk_regs = ARRAY_SIZE(top_clk_regs), .auto_clock_gate = true, .gate_dbg_offset = GS101_GATE_DBG_OFFSET, .option_offset = CMU_CMU_TOP_CONTROLLER_OPTION, @@ -2434,15 +2434,15 @@ PNAME(mout_hsi0_usb31drd_p) = { "fout_usb_pll", "dout_hsi0_usb31drd", "fout_usb_pll" }; -static const struct samsung_pll_rate_table cmu_hsi0_usb_pll_rates[] __initconst = { +static const struct samsung_pll_rate_table hsi0_usb_pll_rates[] __initconst = { PLL_35XX_RATE(24576000, 19200000, 150, 6, 5), { /* sentinel */ } }; -static const struct samsung_pll_clock cmu_hsi0_pll_clks[] __initconst = { +static const struct samsung_pll_clock hsi0_pll_clks[] __initconst = { PLL(pll_0518x, CLK_FOUT_USB_PLL, "fout_usb_pll", "oscclk", PLL_LOCKTIME_PLL_USB, PLL_CON3_PLL_USB, - cmu_hsi0_usb_pll_rates), + hsi0_usb_pll_rates), }; static const struct samsung_mux_clock hsi0_mux_clks[] __initconst = { @@ -2660,8 +2660,8 @@ static const struct samsung_fixed_rate_clock hsi0_fixed_clks[] __initconst = { }; static const struct samsung_cmu_info hsi0_cmu_info __initconst = { - .pll_clks = cmu_hsi0_pll_clks, - .nr_pll_clks = ARRAY_SIZE(cmu_hsi0_pll_clks), + .pll_clks = hsi0_pll_clks, + .nr_pll_clks = ARRAY_SIZE(hsi0_pll_clks), .mux_clks = hsi0_mux_clks, .nr_mux_clks = ARRAY_SIZE(hsi0_mux_clks), .div_clks = hsi0_div_clks, @@ -2791,7 +2791,7 @@ static const struct samsung_cmu_info hsi0_cmu_info __initconst = { #define QCH_CON_UFS_EMBD_QCH_FMP 0x3094 #define QUEUE_CTRL_REG_BLK_HSI2_CMU_HSI2 0x3c00 -static const unsigned long cmu_hsi2_clk_regs[] __initconst = { +static const unsigned long hsi2_clk_regs[] __initconst = { PLL_CON0_MUX_CLKCMU_HSI2_BUS_USER, PLL_CON1_MUX_CLKCMU_HSI2_BUS_USER, PLL_CON0_MUX_CLKCMU_HSI2_MMC_CARD_USER, @@ -3166,8 +3166,8 @@ static const struct samsung_cmu_info hsi2_cmu_info __initconst = { .gate_clks = hsi2_gate_clks, .nr_gate_clks = ARRAY_SIZE(hsi2_gate_clks), .nr_clk_ids = CLKS_NR_HSI2, - .clk_regs = cmu_hsi2_clk_regs, - .nr_clk_regs = ARRAY_SIZE(cmu_hsi2_clk_regs), + .clk_regs = hsi2_clk_regs, + .nr_clk_regs = ARRAY_SIZE(hsi2_clk_regs), .sysreg_clk_regs = dcrg_memclk_sysreg, .nr_sysreg_clk_regs = ARRAY_SIZE(dcrg_memclk_sysreg), .clk_name = "bus", diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 026ac556fa2e..fdb84bcec912 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -201,6 +201,9 @@ static const struct clk_ops samsung_pll3000_clk_ops = { #define PLL35XX_LOCK_STAT_SHIFT (29) #define PLL35XX_ENABLE_SHIFT (31) +/* A9FRACM is similar to PLL35xx, except that MDIV is bit different */ +#define PLLA9FRACM_MDIV_SHIFT (14) + static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -209,7 +212,12 @@ static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, u64 fvco = parent_rate; pll_con = readl_relaxed(pll->con_reg); - mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK; + + if (pll->type == pll_a9fracm) + mdiv = (pll_con >> PLLA9FRACM_MDIV_SHIFT) & PLL35XX_MDIV_MASK; + else + mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK; + pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK; sdiv = (pll_con >> PLL35XX_SDIV_SHIFT) & PLL35XX_SDIV_MASK; @@ -219,12 +227,15 @@ static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, return (unsigned long)fvco; } -static inline bool samsung_pll35xx_mp_change( - const struct samsung_pll_rate_table *rate, u32 pll_con) +static inline bool samsung_pll35xx_mp_change(u32 pll_type, + const struct samsung_pll_rate_table *rate, u32 pll_con) { u32 old_mdiv, old_pdiv; - old_mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK; + if (pll_type == pll_a9fracm) + old_mdiv = (pll_con >> PLLA9FRACM_MDIV_SHIFT) & PLL35XX_MDIV_MASK; + else + old_mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK; old_pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK; return (rate->mdiv != old_mdiv || rate->pdiv != old_pdiv); @@ -236,6 +247,12 @@ static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate, struct samsung_clk_pll *pll = to_clk_pll(hw); const struct samsung_pll_rate_table *rate; u32 tmp; + u32 mdiv_shift; + + if (pll->type == pll_a9fracm) + mdiv_shift = PLLA9FRACM_MDIV_SHIFT; + else + mdiv_shift = PLL35XX_MDIV_SHIFT; /* Get required rate settings from table */ rate = samsung_get_pll_settings(pll, drate); @@ -247,7 +264,7 @@ static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate, tmp = readl_relaxed(pll->con_reg); - if (!(samsung_pll35xx_mp_change(rate, tmp))) { + if (!(samsung_pll35xx_mp_change(pll->type, rate, tmp))) { /* If only s change, change just s value only*/ tmp &= ~(PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT); tmp |= rate->sdiv << PLL35XX_SDIV_SHIFT; @@ -257,7 +274,7 @@ static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate, } /* Set PLL lock time. */ - if (pll->type == pll_142xx || pll->type == pll_1017x) + if (pll->type == pll_142xx || pll->type == pll_1017x || pll->type == pll_a9fracm) writel_relaxed(rate->pdiv * PLL142XX_LOCK_FACTOR, pll->lock_reg); else @@ -265,10 +282,10 @@ static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate, pll->lock_reg); /* Change PLL PMS values */ - tmp &= ~((PLL35XX_MDIV_MASK << PLL35XX_MDIV_SHIFT) | + tmp &= ~((PLL35XX_MDIV_MASK << mdiv_shift) | (PLL35XX_PDIV_MASK << PLL35XX_PDIV_SHIFT) | (PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT)); - tmp |= (rate->mdiv << PLL35XX_MDIV_SHIFT) | + tmp |= (rate->mdiv << mdiv_shift) | (rate->pdiv << PLL35XX_PDIV_SHIFT) | (rate->sdiv << PLL35XX_SDIV_SHIFT); writel_relaxed(tmp, pll->con_reg); @@ -1428,6 +1445,149 @@ static const struct clk_ops samsung_pll1031x_clk_min_ops = { .recalc_rate = samsung_pll1031x_recalc_rate, }; +/* + * PLLA9FRACO Clock Type + */ +#define PLLA9FRACO_LOCK_FACTOR (500) + +#define PLLA9FRACO_MDIV_MASK (0x3ff) +#define PLLA9FRACO_PDIV_MASK (0x3f) +#define PLLA9FRACO_SDIV_MASK (0x7) +#define PLLA9FRACO_MDIV_SHIFT (14) +#define PLLA9FRACO_PDIV_SHIFT (8) +#define PLLA9FRACO_SDIV_SHIFT (0) + +#define PLLA9FRACO_PLL_CON5_DIV_FRAC (0x14) +#define PLLA9FRACO_KDIV_MASK (0xffffff) +#define PLLA9FRACO_KDIV_SHIFT (0) +#define PLLA9FRACO_DAC_MODE BIT(30) +#define PLLA9FRACO_DSM_EN BIT(31) +#define PLLA9FRACO_FOUTPOSTDIVEN BIT(3) +#define PLLA9FRACO_MUX_SEL BIT(4) +#define PLLA9FRACO_ENABLE_SHIFT (31) +#define PLLA9FRACO_LOCK_STAT_SHIFT (29) + +static unsigned long samsung_a9fraco_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + u32 pll_con0, pll_con5; + u64 mdiv, pdiv, sdiv, kdiv; + u64 fvco = parent_rate; + + pll_con0 = readl_relaxed(pll->con_reg); + pll_con5 = readl_relaxed(pll->con_reg + PLLA9FRACO_PLL_CON5_DIV_FRAC); + mdiv = (pll_con0 >> PLLA9FRACO_MDIV_SHIFT) & PLLA9FRACO_MDIV_MASK; + pdiv = (pll_con0 >> PLLA9FRACO_PDIV_SHIFT) & PLLA9FRACO_PDIV_MASK; + sdiv = (pll_con0 >> PLLA9FRACO_SDIV_SHIFT) & PLLA9FRACO_SDIV_MASK; + kdiv = (pll_con5 & PLLA9FRACO_KDIV_MASK); + + /* fvco = fref * (M + K/2^24) / p * (S+1) */ + fvco *= mdiv; + fvco = (fvco << 24) + kdiv; + fvco = div64_u64(fvco, ((pdiv * (sdiv + 1)) << 24)); + + return (unsigned long)fvco; +} + +static bool samsung_a9fraco_mpk_change(u32 pll_con0, u32 pll_con5, + const struct samsung_pll_rate_table *rate) +{ + u32 old_mdiv, old_pdiv, old_kdiv; + + old_mdiv = (pll_con0 >> PLLA9FRACO_MDIV_SHIFT) & PLLA9FRACO_MDIV_MASK; + old_pdiv = (pll_con0 >> PLLA9FRACO_PDIV_SHIFT) & PLLA9FRACO_PDIV_MASK; + old_kdiv = (pll_con5 >> PLLA9FRACO_KDIV_SHIFT) & PLLA9FRACO_KDIV_MASK; + + return (old_mdiv != rate->mdiv || old_pdiv != rate->pdiv || old_kdiv != rate->kdiv); +} + +static int samsung_a9fraco_set_rate(struct clk_hw *hw, unsigned long drate, unsigned long prate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + const struct samsung_pll_rate_table *rate; + u32 con0, con5; + int ret; + + /* Get required rate settings from table */ + rate = samsung_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, clk_hw_get_name(hw)); + return -EINVAL; + } + + con0 = readl_relaxed(pll->con_reg); + con5 = readl_relaxed(pll->con_reg + PLLA9FRACO_PLL_CON5_DIV_FRAC); + + if (!(samsung_a9fraco_mpk_change(con0, con5, rate))) { + /* If only s change, change just s value only */ + con0 &= ~(PLLA9FRACO_SDIV_MASK << PLLA9FRACO_SDIV_SHIFT); + con0 |= rate->sdiv << PLLA9FRACO_SDIV_SHIFT; + writel_relaxed(con0, pll->con_reg); + + return 0; + } + + /* Select OSCCLK (0) */ + con0 = readl_relaxed(pll->con_reg); + con0 &= ~(PLLA9FRACO_MUX_SEL); + writel_relaxed(con0, pll->con_reg); + + /* Disable PLL */ + con0 &= ~BIT(PLLA9FRACO_ENABLE_SHIFT); + writel_relaxed(con0, pll->con_reg); + + /* Set PLL lock time. */ + writel_relaxed(rate->pdiv * PLLA9FRACO_LOCK_FACTOR, pll->lock_reg); + + /* Set PLL M, P, and S values. */ + con0 &= ~((PLLA9FRACO_MDIV_MASK << PLLA9FRACO_MDIV_SHIFT) | + (PLLA9FRACO_PDIV_MASK << PLLA9FRACO_PDIV_SHIFT) | + (PLLA9FRACO_SDIV_MASK << PLLA9FRACO_SDIV_SHIFT)); + + /* The field FOUTPOSTDIVEN should always be 1, else FOUT might be 0 Hz. */ + con0 |= (rate->mdiv << PLLA9FRACO_MDIV_SHIFT) | + (rate->pdiv << PLLA9FRACO_PDIV_SHIFT) | + (rate->sdiv << PLLA9FRACO_SDIV_SHIFT) | (PLLA9FRACO_FOUTPOSTDIVEN); + + /* Set PLL K, DSM_EN and DAC_MODE values. */ + con5 = readl_relaxed(pll->con_reg + PLLA9FRACO_PLL_CON5_DIV_FRAC); + con5 &= ~((PLLA9FRACO_KDIV_MASK << PLLA9FRACO_KDIV_SHIFT) | + PLLA9FRACO_DSM_EN | PLLA9FRACO_DAC_MODE); + con5 |= (rate->kdiv << PLLA9FRACO_KDIV_SHIFT) | PLLA9FRACO_DSM_EN | PLLA9FRACO_DAC_MODE; + + /* Write configuration to PLL */ + writel_relaxed(con0, pll->con_reg); + writel_relaxed(con5, pll->con_reg + PLLA9FRACO_PLL_CON5_DIV_FRAC); + + /* Enable PLL */ + con0 = readl_relaxed(pll->con_reg); + con0 |= BIT(PLLA9FRACO_ENABLE_SHIFT); + writel_relaxed(con0, pll->con_reg); + + /* Wait for PLL lock if the PLL is enabled */ + ret = samsung_pll_lock_wait(pll, BIT(pll->lock_offs)); + if (ret < 0) + return ret; + + /* Select FOUT (1) */ + con0 |= (PLLA9FRACO_MUX_SEL); + writel_relaxed(con0, pll->con_reg); + + return 0; +} + +static const struct clk_ops samsung_a9fraco_clk_ops = { + .recalc_rate = samsung_a9fraco_recalc_rate, + .determine_rate = samsung_pll_determine_rate, + .set_rate = samsung_a9fraco_set_rate, +}; + +static const struct clk_ops samsung_a9fraco_clk_min_ops = { + .recalc_rate = samsung_a9fraco_recalc_rate, +}; + static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, const struct samsung_pll_clock *pll_clk) { @@ -1477,6 +1637,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, case pll_1452x: case pll_142xx: case pll_1017x: + case pll_a9fracm: pll->enable_offs = PLL35XX_ENABLE_SHIFT; pll->lock_offs = PLL35XX_LOCK_STAT_SHIFT; if (!pll->rate_table) @@ -1578,6 +1739,14 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, else init.ops = &samsung_pll1031x_clk_ops; break; + case pll_a9fraco: + pll->enable_offs = PLLA9FRACO_ENABLE_SHIFT; + pll->lock_offs = PLLA9FRACO_LOCK_STAT_SHIFT; + if (!pll->rate_table) + init.ops = &samsung_a9fraco_clk_min_ops; + else + init.ops = &samsung_a9fraco_clk_ops; + break; default: pr_warn("%s: Unknown pll type for pll clk %s\n", __func__, pll_clk->name); diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h index 6c8bb7f26da5..d6eb3246611b 100644 --- a/drivers/clk/samsung/clk-pll.h +++ b/drivers/clk/samsung/clk-pll.h @@ -51,6 +51,8 @@ enum samsung_pll_type { pll_4311, pll_1017x, pll_1031x, + pll_a9fracm, + pll_a9fraco, }; #define PLL_RATE(_fin, _m, _p, _s, _k, _ks) \ @@ -58,6 +60,11 @@ enum samsung_pll_type { #define PLL_VALID_RATE(_fin, _fout, _m, _p, _s, _k, _ks) ((_fout) + \ BUILD_BUG_ON_ZERO(PLL_RATE(_fin, _m, _p, _s, _k, _ks) != (_fout))) +#define PLL_FRACO_RATE(_fin, _m, _p, _s, _k, _ks) \ + ((u64)(_fin) * (BIT(_ks) * (_m) + (_k)) / BIT(_ks) / ((_p) * ((_s) + 1))) +#define PLL_FRACO_VALID_RATE(_fin, _fout, _m, _p, _s, _k, _ks) ((_fout) + \ + BUILD_BUG_ON_ZERO(PLL_FRACO_RATE(_fin, _m, _p, _s, _k, _ks) != (_fout))) + #define PLL_35XX_RATE(_fin, _rate, _m, _p, _s) \ { \ .rate = PLL_VALID_RATE(_fin, _rate, \ @@ -111,6 +118,16 @@ enum samsung_pll_type { .vsel = (_vsel), \ } +#define PLL_A9FRACO_RATE(_fin, _rate, _m, _p, _s, _k) \ + { \ + .rate = PLL_FRACO_VALID_RATE(_fin, _rate, \ + _m, _p, _s, _k, 24), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + .kdiv = (_k), \ + } + /* NOTE: Rate table should be kept sorted in descending order. */ struct samsung_pll_rate_table { diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index 94b2bccc7d02..91e5cdbc79d7 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -359,8 +359,8 @@ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, ctx->reg_base + list->offset, list->bit_idx, list->gate_flags, &ctx->lock); if (IS_ERR(clk_hw)) { - pr_err("%s: failed to register clock %s: %ld\n", __func__, - list->name, PTR_ERR(clk_hw)); + pr_err("%s: failed to register clock %s: %pe\n", __func__, + list->name, clk_hw); continue; } diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index e98afd59f05c..bb8b75bdbdb3 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -846,7 +846,7 @@ static const struct clk_parent_data top_parents[] = { CCU_PARENT_HW(pll6_d3), }; CCU_MUX_DIV_GATE_FC_DEFINE(top_dclk, top_parents, APMU_TOP_DCLK_CTRL, 5, 3, - BIT(8), 2, 3, BIT(1), 0); + BIT(8), 2, 3, BIT(1), CLK_IS_CRITICAL); static const struct clk_parent_data ucie_parents[] = { CCU_PARENT_HW(pll1_d8_307p2), diff --git a/drivers/clk/spacemit/ccu_mix.c b/drivers/clk/spacemit/ccu_mix.c index 9578366e9746..a8b407049bf4 100644 --- a/drivers/clk/spacemit/ccu_mix.c +++ b/drivers/clk/spacemit/ccu_mix.c @@ -73,7 +73,7 @@ static int ccu_mix_trigger_fc(struct clk_hw *hw) struct ccu_common *common = hw_to_ccu_common(hw); unsigned int val; - if (common->reg_fc) + if (!common->reg_fc) return 0; ccu_update(common, fc, common->mask_fc, common->mask_fc); diff --git a/drivers/clk/spear/clk-vco-pll.c b/drivers/clk/spear/clk-vco-pll.c index 601e123f5c4b..faba727e2f84 100644 --- a/drivers/clk/spear/clk-vco-pll.c +++ b/drivers/clk/spear/clk-vco-pll.c @@ -343,13 +343,15 @@ struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, tpll_clk = clk_register(NULL, &pll->hw); if (IS_ERR_OR_NULL(tpll_clk)) - goto free_pll; + goto unregister_clk; if (pll_clk) *pll_clk = tpll_clk; return vco_clk; +unregister_clk: + clk_unregister(vco_clk); free_pll: kfree(pll); free_vco: diff --git a/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c b/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c index 0339c4af0fe5..db0e36d8838e 100644 --- a/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c +++ b/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c @@ -83,9 +83,22 @@ static SUNXI_CCU_MUX_DATA_WITH_GATE(r_pwmctrl_clk, "r-pwmctrl", static SUNXI_CCU_GATE_HW(bus_r_pwmctrl_clk, "bus-r-pwmctrl", &r_apb0_clk.common.hw, 0x13c, BIT(0), 0); -/* SPI clock is /M/N (same as new MMC?) */ +static const struct clk_parent_data r_spi_parents[] = { + { .fw_name = "hosc" }, + { .fw_name = "pll-periph" }, + { .name = "pll-periph0-300M" }, + { .name = "pll-periph1-300M" }, + { .name = "pll-audio" }, +}; +static SUNXI_CCU_DUALDIV_MUX_GATE(r_spi_clk, "r-spi", r_spi_parents, 0x150, + 0, 5, /* M */ + 8, 5, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); static SUNXI_CCU_GATE_HW(bus_r_spi_clk, "bus-r-spi", &r_ahb_clk.common.hw, 0x15c, BIT(0), 0); + static SUNXI_CCU_GATE_HW(bus_r_spinlock_clk, "bus-r-spinlock", &r_ahb_clk.common.hw, 0x16c, BIT(0), 0); static SUNXI_CCU_GATE_HW(bus_r_msgbox_clk, "bus-r-msgbox", @@ -138,6 +151,7 @@ static struct ccu_common *sun55i_a523_r_ccu_clks[] = { &bus_r_twd_clk.common, &r_pwmctrl_clk.common, &bus_r_pwmctrl_clk.common, + &r_spi_clk.common, &bus_r_spi_clk.common, &bus_r_spinlock_clk.common, &bus_r_msgbox_clk.common, @@ -169,6 +183,7 @@ static struct clk_hw_onecell_data sun55i_a523_r_hw_clks = { [CLK_BUS_R_TWD] = &bus_r_twd_clk.common.hw, [CLK_R_PWMCTRL] = &r_pwmctrl_clk.common.hw, [CLK_BUS_R_PWMCTRL] = &bus_r_pwmctrl_clk.common.hw, + [CLK_R_SPI] = &r_spi_clk.common.hw, [CLK_BUS_R_SPI] = &bus_r_spi_clk.common.hw, [CLK_BUS_R_SPINLOCK] = &bus_r_spinlock_clk.common.hw, [CLK_BUS_R_MSGBOX] = &bus_r_msgbox_clk.common.hw, diff --git a/drivers/clk/tenstorrent/Kconfig b/drivers/clk/tenstorrent/Kconfig new file mode 100644 index 000000000000..9d4391eeeae0 --- /dev/null +++ b/drivers/clk/tenstorrent/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config TENSTORRENT_ATLANTIS_PRCM + tristate "Support for Tenstorrent Atlantis PRCM Clock Controller" + depends on ARCH_TENSTORRENT || COMPILE_TEST + default ARCH_TENSTORRENT + select REGMAP_MMIO + select AUXILIARY_BUS + select MFD_SYSCON + help + Say yes here to support the different clock + controllers found in the Tenstorrent Atlantis SoC. + This includes the clocks from the RCPU, HSIO, MMIO + and PCIE domain. diff --git a/drivers/clk/tenstorrent/Makefile b/drivers/clk/tenstorrent/Makefile new file mode 100644 index 000000000000..95d87bac7bf5 --- /dev/null +++ b/drivers/clk/tenstorrent/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_TENSTORRENT_ATLANTIS_PRCM) += atlantis-prcm.o diff --git a/drivers/clk/tenstorrent/atlantis-prcm.c b/drivers/clk/tenstorrent/atlantis-prcm.c new file mode 100644 index 000000000000..6d4386eeb7da --- /dev/null +++ b/drivers/clk/tenstorrent/atlantis-prcm.c @@ -0,0 +1,870 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Tenstorrent Atlantis PRCM Clock Driver + * + * Copyright (c) 2026 Tenstorrent + */ + +#include +#include +#include +#include +#include +#include +#include + +/* RCPU Clock Register Offsets */ +#define PLL_RCPU_CFG_REG 0x0000 +#define PLL_NOCC_CFG_REG 0x0004 +#define NOCC_CLK_CFG_REG 0x0008 +#define RCPU_DIV_CFG_REG 0x000C +#define RCPU_BLK_CG_REG 0x0014 +#define LSIO_BLK_CG_REG 0x0018 +#define PLL_RCPU_EN_REG 0x011C +#define PLL_NOCC_EN_REG 0x0120 +#define BUS_CG_REG 0x01FC + +/* PLL Bit Definitions */ +#define PLL_CFG_EN_BIT BIT(0) +#define PLL_CFG_BYPASS_BIT BIT(1) +#define PLL_CFG_REFDIV_MASK GENMASK(7, 2) +#define PLL_CFG_REFDIV_SHIFT 2 +#define PLL_CFG_POSTDIV1_MASK GENMASK(10, 8) +#define PLL_CFG_POSTDIV1_SHIFT 8 +#define PLL_CFG_POSTDIV2_MASK GENMASK(13, 11) +#define PLL_CFG_POSTDIV2_SHIFT 11 +#define PLL_CFG_FBDIV_MASK GENMASK(25, 14) +#define PLL_CFG_FBDIV_SHIFT 14 +#define PLL_CFG_LKDT_BIT BIT(30) +#define PLL_CFG_LOCK_BIT BIT(31) +#define PLL_LOCK_TIMEOUT_US 1000 +#define PLL_BYPASS_WAIT_US 500 + +struct atlantis_clk_common { + int clkid; + struct regmap *regmap; + struct clk_hw hw; +}; + +static inline struct atlantis_clk_common * +hw_to_atlantis_clk_common(struct clk_hw *hw) +{ + return container_of(hw, struct atlantis_clk_common, hw); +} + +struct atlantis_clk_mux_config { + u8 shift; + u8 width; + u32 reg_offset; +}; + +struct atlantis_clk_mux { + struct atlantis_clk_common common; + struct atlantis_clk_mux_config config; +}; + +struct atlantis_clk_gate_config { + u32 reg_offset; + u32 enable; +}; + +struct atlantis_clk_gate { + struct atlantis_clk_common common; + struct atlantis_clk_gate_config config; +}; + +struct atlantis_clk_divider_config { + u8 shift; + u8 width; + u32 flags; + u32 reg_offset; +}; + +struct atlantis_clk_divider { + struct atlantis_clk_common common; + struct atlantis_clk_divider_config config; +}; + +struct atlantis_clk_pll_config { + u32 tbl_num; + u32 reg_offset; + u32 en_reg_offset; + u32 cg_reg_offset; + u32 cg_reg_enable; +}; + +/* Models a PLL with Bypass Functionality and Enable Bit + an optional Gate Clock at it's output */ +struct atlantis_clk_pll { + struct atlantis_clk_common common; + struct atlantis_clk_pll_config config; +}; + +struct atlantis_clk_gate_shared_config { + u32 reg_offset; + u32 enable; + unsigned int *share_count; + spinlock_t *refcount_lock; +}; + +struct atlantis_clk_gate_shared { + struct atlantis_clk_common common; + struct atlantis_clk_gate_shared_config config; +}; + +struct atlantis_clk_fixed_factor_config { + unsigned int mult; + unsigned int div; +}; + +struct atlantis_clk_fixed_factor { + struct atlantis_clk_fixed_factor_config config; + struct atlantis_clk_common common; +}; + +static inline struct atlantis_clk_mux *hw_to_atlantis_clk_mux(struct clk_hw *hw) +{ + struct atlantis_clk_common *common = hw_to_atlantis_clk_common(hw); + + return container_of(common, struct atlantis_clk_mux, common); +} + +static inline struct atlantis_clk_gate * +hw_to_atlantis_clk_gate(struct clk_hw *hw) +{ + struct atlantis_clk_common *common = hw_to_atlantis_clk_common(hw); + + return container_of(common, struct atlantis_clk_gate, common); +} + +static inline struct atlantis_clk_divider * +hw_to_atlantis_clk_divider(struct clk_hw *hw) +{ + struct atlantis_clk_common *common = hw_to_atlantis_clk_common(hw); + + return container_of(common, struct atlantis_clk_divider, common); +} + +static inline struct atlantis_clk_pll *hw_to_atlantis_pll(struct clk_hw *hw) +{ + struct atlantis_clk_common *common = hw_to_atlantis_clk_common(hw); + + return container_of(common, struct atlantis_clk_pll, common); +} + +static inline struct atlantis_clk_gate_shared * +hw_to_atlantis_clk_gate_shared(struct clk_hw *hw) +{ + struct atlantis_clk_common *common = hw_to_atlantis_clk_common(hw); + + return container_of(common, struct atlantis_clk_gate_shared, common); +} + +static inline struct atlantis_clk_fixed_factor * +hw_to_atlantis_clk_fixed_factor(struct clk_hw *hw) +{ + struct atlantis_clk_common *common = hw_to_atlantis_clk_common(hw); + + return container_of(common, struct atlantis_clk_fixed_factor, common); +} + +static u8 atlantis_clk_mux_get_parent(struct clk_hw *hw) +{ + struct atlantis_clk_mux *mux = hw_to_atlantis_clk_mux(hw); + u32 val; + + regmap_read(mux->common.regmap, mux->config.reg_offset, &val); + val >>= mux->config.shift; + val &= (BIT(mux->config.width) - 1); + + return val; +} + +static int atlantis_clk_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct atlantis_clk_mux *mux = hw_to_atlantis_clk_mux(hw); + u32 val = index; + + return regmap_update_bits(mux->common.regmap, mux->config.reg_offset, + (BIT(mux->config.width) - 1) << mux->config.shift, + val << mux->config.shift); +} + +static int atlantis_clk_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return clk_mux_determine_rate_flags(hw, req, hw->init->flags); +} + +static const struct clk_ops atlantis_clk_mux_ops = { + .get_parent = atlantis_clk_mux_get_parent, + .set_parent = atlantis_clk_mux_set_parent, + .determine_rate = atlantis_clk_mux_determine_rate, +}; + +static int atlantis_clk_gate_endisable(struct clk_hw *hw, int enable) +{ + struct atlantis_clk_gate *gate = hw_to_atlantis_clk_gate(hw); + + if (enable) + return regmap_set_bits(gate->common.regmap, + gate->config.reg_offset, + gate->config.enable); + else + return regmap_clear_bits(gate->common.regmap, + gate->config.reg_offset, + gate->config.enable); +} + +static int atlantis_clk_gate_enable(struct clk_hw *hw) +{ + return atlantis_clk_gate_endisable(hw, 1); +} + +static void atlantis_clk_gate_disable(struct clk_hw *hw) +{ + atlantis_clk_gate_endisable(hw, 0); +} + +static int atlantis_clk_gate_is_enabled(struct clk_hw *hw) +{ + struct atlantis_clk_gate *gate = hw_to_atlantis_clk_gate(hw); + + return regmap_test_bits(gate->common.regmap, gate->config.reg_offset, gate->config.enable); +} + +static const struct clk_ops atlantis_clk_gate_ops = { + .enable = atlantis_clk_gate_enable, + .disable = atlantis_clk_gate_disable, + .is_enabled = atlantis_clk_gate_is_enabled, +}; + +static unsigned long atlantis_clk_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct atlantis_clk_divider *divider = hw_to_atlantis_clk_divider(hw); + u32 val; + + regmap_read(divider->common.regmap, divider->config.reg_offset, &val); + + val >>= divider->config.shift; + val &= ((1 << (divider->config.width)) - 1); + + return DIV_ROUND_UP_ULL((u64)parent_rate, val + 1); +} + +static const struct clk_ops atlantis_clk_divider_ops = { + .recalc_rate = atlantis_clk_divider_recalc_rate, +}; + +static unsigned long +atlantis_clk_fixed_factor_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct atlantis_clk_fixed_factor *factor = + hw_to_atlantis_clk_fixed_factor(hw); + unsigned long long rate; + + rate = (unsigned long long)parent_rate * factor->config.mult; + do_div(rate, factor->config.div); + + return (unsigned long)rate; +} + +static const struct clk_ops atlantis_clk_fixed_factor_ops = { + .recalc_rate = atlantis_clk_fixed_factor_recalc_rate, +}; + +static int atlantis_clk_pll_is_enabled(struct clk_hw *hw) +{ + struct atlantis_clk_pll *pll = hw_to_atlantis_pll(hw); + u32 val, en_val, cg_val; + + regmap_read(pll->common.regmap, pll->config.reg_offset, &val); + regmap_read(pll->common.regmap, pll->config.en_reg_offset, &en_val); + regmap_read(pll->common.regmap, pll->config.cg_reg_offset, &cg_val); + + /* Check if PLL is powered on, locked, not bypassed and Gate clk is enabled */ + return !!(en_val & PLL_CFG_EN_BIT) && !!(val & PLL_CFG_LOCK_BIT) && + (!pll->config.cg_reg_enable || (cg_val & pll->config.cg_reg_enable)) && + !(val & PLL_CFG_BYPASS_BIT); +} + +static int atlantis_clk_pll_enable(struct clk_hw *hw) +{ + struct atlantis_clk_pll *pll = hw_to_atlantis_pll(hw); + u32 val, en_val, cg_val; + int ret; + + regmap_read(pll->common.regmap, pll->config.reg_offset, &val); + regmap_read(pll->common.regmap, pll->config.en_reg_offset, &en_val); + regmap_read(pll->common.regmap, pll->config.cg_reg_offset, &cg_val); + + /* Check if PLL is already enabled, locked, not bypassed and Gate clk is enabled */ + if ((en_val & PLL_CFG_EN_BIT) && (val & PLL_CFG_LOCK_BIT) && + (!pll->config.cg_reg_enable || (cg_val & pll->config.cg_reg_enable)) && + !(val & PLL_CFG_BYPASS_BIT)) { + return 0; + } + + /* Step 1: Set bypass mode first */ + regmap_update_bits(pll->common.regmap, pll->config.reg_offset, + PLL_CFG_BYPASS_BIT, PLL_CFG_BYPASS_BIT); + + /* Step 2: Enable PLL (clear then set power bit) */ + regmap_update_bits(pll->common.regmap, pll->config.en_reg_offset, + PLL_CFG_EN_BIT, 0); + + regmap_update_bits(pll->common.regmap, pll->config.en_reg_offset, + PLL_CFG_EN_BIT, PLL_CFG_EN_BIT); + + /* Step 3: Wait for PLL lock */ + ret = regmap_read_poll_timeout(pll->common.regmap, + pll->config.reg_offset, val, + val & PLL_CFG_LOCK_BIT, + PLL_BYPASS_WAIT_US, PLL_LOCK_TIMEOUT_US); + if (ret) { + pr_err("PLL failed to lock within timeout\n"); + return ret; + } + + /* Step 4: Switch from bypass to PLL output */ + regmap_update_bits(pll->common.regmap, pll->config.reg_offset, + PLL_CFG_BYPASS_BIT, 0); + + /* Enable Gate clk at PLL Output */ + return regmap_update_bits(pll->common.regmap, pll->config.cg_reg_offset, + pll->config.cg_reg_enable, + pll->config.cg_reg_enable); +} + +static void atlantis_clk_pll_disable(struct clk_hw *hw) +{ + struct atlantis_clk_pll *pll = hw_to_atlantis_pll(hw); + + /* Step 1: Switch to bypass mode before disabling */ + regmap_update_bits(pll->common.regmap, pll->config.reg_offset, + PLL_CFG_BYPASS_BIT, PLL_CFG_BYPASS_BIT); + /* Step 2: Power down PLL */ + regmap_update_bits(pll->common.regmap, pll->config.en_reg_offset, + PLL_CFG_EN_BIT, 0); +} + +static unsigned long atlantis_clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct atlantis_clk_pll *pll = hw_to_atlantis_pll(hw); + + u32 val, refdiv, fbdiv, postdiv1, postdiv2; + u64 fout; + + regmap_read(pll->common.regmap, pll->config.reg_offset, &val); + + if (val & PLL_CFG_BYPASS_BIT) + return parent_rate; + + refdiv = FIELD_GET(PLL_CFG_REFDIV_MASK, val); + fbdiv = FIELD_GET(PLL_CFG_FBDIV_MASK, val); + postdiv1 = FIELD_GET(PLL_CFG_POSTDIV1_MASK, val); + postdiv2 = FIELD_GET(PLL_CFG_POSTDIV2_MASK, val); + + if (!refdiv) + refdiv = 1; + if (!postdiv1) + postdiv1 = 1; + if (!postdiv2) + postdiv2 = 1; + if (!fbdiv) + return 0; + + fout = div64_u64((u64)parent_rate * fbdiv, + refdiv * postdiv1 * postdiv2); + + return fout; +} + +static const struct clk_ops atlantis_clk_pll_ops = { + .enable = atlantis_clk_pll_enable, + .disable = atlantis_clk_pll_disable, + .recalc_rate = atlantis_clk_pll_recalc_rate, + .is_enabled = atlantis_clk_pll_is_enabled, +}; + +static int atlantis_clk_gate_shared_enable(struct clk_hw *hw) +{ + struct atlantis_clk_gate_shared *gate = + hw_to_atlantis_clk_gate_shared(hw); + bool need_enable; + + scoped_guard(spinlock_irqsave, gate->config.refcount_lock) + { + need_enable = (*gate->config.share_count)++ == 0; + if (need_enable) { + regmap_set_bits(gate->common.regmap, + gate->config.reg_offset, + gate->config.enable); + } + } + + if (need_enable) { + if (!regmap_test_bits(gate->common.regmap, + gate->config.reg_offset, + gate->config.enable)) { + pr_warn("%s: gate enable %d failed to enable\n", + clk_hw_get_name(hw), gate->config.enable); + return -EIO; + } + } + + return 0; +} + +static void atlantis_clk_gate_shared_disable(struct clk_hw *hw) +{ + struct atlantis_clk_gate_shared *gate = + hw_to_atlantis_clk_gate_shared(hw); + + scoped_guard(spinlock_irqsave, gate->config.refcount_lock) + { + if (WARN_ON(*gate->config.share_count == 0)) + return; + if (--(*gate->config.share_count) > 0) + return; + + regmap_clear_bits(gate->common.regmap, + gate->config.reg_offset, + gate->config.enable); + } +} + +static int atlantis_clk_gate_shared_is_enabled(struct clk_hw *hw) +{ + struct atlantis_clk_gate_shared *gate = + hw_to_atlantis_clk_gate_shared(hw); + + return regmap_test_bits(gate->common.regmap, gate->config.reg_offset, gate->config.enable); +} + +static void atlantis_clk_gate_shared_disable_unused(struct clk_hw *hw) +{ + struct atlantis_clk_gate_shared *gate = + hw_to_atlantis_clk_gate_shared(hw); + + scoped_guard(spinlock_irqsave, gate->config.refcount_lock) + { + if (*gate->config.share_count == 0) + regmap_clear_bits(gate->common.regmap, + gate->config.reg_offset, + gate->config.enable); + } +} + +static const struct clk_ops atlantis_clk_gate_shared_ops = { + .enable = atlantis_clk_gate_shared_enable, + .disable = atlantis_clk_gate_shared_disable, + .disable_unused = atlantis_clk_gate_shared_disable_unused, + .is_enabled = atlantis_clk_gate_shared_is_enabled, +}; + +#define ATLANTIS_PLL_CONFIG(_reg_offset, _en_reg_offset, _cg_reg_offset, \ + _cg_reg_enable) \ + { \ + .reg_offset = (_reg_offset), \ + .en_reg_offset = (_en_reg_offset), \ + .cg_reg_offset = (_cg_reg_offset), \ + .cg_reg_enable = (_cg_reg_enable), \ + } + +#define ATLANTIS_PLL_DEFINE(_clkid, _name, _parent, _reg_offset, \ + _en_reg_offset, _cg_reg_offset, _cg_reg_enable, \ + _flags) \ + static struct atlantis_clk_pll _name = { \ + .config = ATLANTIS_PLL_CONFIG(_reg_offset, _en_reg_offset, \ + _cg_reg_offset, _cg_reg_enable), \ + .common = { .clkid = _clkid, \ + .hw.init = CLK_HW_INIT_PARENTS_DATA( \ + #_name, _parent, &atlantis_clk_pll_ops, \ + _flags) }, \ + } +#define ATLANTIS_MUX_CONFIG(_shift, _width, _reg_offset) \ + { \ + .shift = _shift, .width = _width, .reg_offset = _reg_offset \ + } + +#define ATLANTIS_MUX_DEFINE(_clkid, _name, _parents, _reg_offset, _shift, \ + _width, _flags) \ + static struct atlantis_clk_mux _name = { \ + .config = ATLANTIS_MUX_CONFIG(_shift, _width, _reg_offset), \ + .common = { .clkid = _clkid, \ + .hw.init = CLK_HW_INIT_PARENTS_DATA( \ + #_name, _parents, &atlantis_clk_mux_ops, \ + _flags) } \ + } + +#define ATLANTIS_DIVIDER_CONFIG(_shift, _width, _flags, _reg_offset) \ + { \ + .shift = _shift, .width = _width, .flags = _flags, \ + .reg_offset = _reg_offset \ + } + +#define ATLANTIS_DIVIDER_DEFINE(_clkid, _name, _parent, _reg_offset, _shift, \ + _width, _divflags, _flags) \ + static struct atlantis_clk_divider _name = { \ + .config = ATLANTIS_DIVIDER_CONFIG(_shift, _width, _divflags, \ + _reg_offset), \ + .common = { .clkid = _clkid, \ + .hw.init = CLK_HW_INIT_HW( \ + #_name, &_parent.common.hw, \ + &atlantis_clk_divider_ops, _flags) } \ + } +#define ATLANTIS_GATE_CONFIG(_enable, _reg_offset) \ + { \ + .enable = _enable, .reg_offset = _reg_offset \ + } + +#define ATLANTIS_GATE_DEFINE(_clkid, _name, _parent, _reg_offset, _enable, \ + _flags) \ + static struct atlantis_clk_gate _name = { \ + .config = ATLANTIS_GATE_CONFIG(_enable, _reg_offset), \ + .common = { .clkid = _clkid, \ + .hw.init = CLK_HW_INIT_HW( \ + #_name, &_parent.common.hw, \ + &atlantis_clk_gate_ops, _flags) } \ + } +#define ATLANTIS_GATE_SHARED_CONFIG(_reg_offset, _enable, _share_count) \ + { \ + .reg_offset = _reg_offset, .enable = _enable, \ + .share_count = _share_count, .refcount_lock = &refcount_lock \ + } +#define ATLANTIS_GATE_SHARED_DEFINE(_clkid, _name, _parent, _reg_offset, \ + _enable, _share_count, _flags) \ + static struct atlantis_clk_gate_shared _name = { \ + .config = ATLANTIS_GATE_SHARED_CONFIG(_reg_offset, _enable, \ + _share_count), \ + .common = { .clkid = _clkid, \ + .hw.init = CLK_HW_INIT_HW( \ + #_name, &_parent.common.hw, \ + &atlantis_clk_gate_shared_ops, _flags) } \ + } +#define ATLANTIS_FIXED_FACTOR_DEFINE(_clkid, _name, _parent, _mult, _div, \ + _flags) \ + static struct atlantis_clk_fixed_factor _name = { \ + .config = { .mult = _mult, .div = _div }, \ + .common = { .clkid = _clkid, \ + .hw.init = CLK_HW_INIT_HW( \ + #_name, &_parent.common.hw, \ + &atlantis_clk_fixed_factor_ops, _flags) } \ + } + +static DEFINE_SPINLOCK(refcount_lock); /* Lock for refcount value accesses */ + +static const struct regmap_config atlantis_prcm_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xFFFC, + .cache_type = REGCACHE_NONE, +}; + +struct atlantis_prcm_data { + struct clk_hw **hws; + size_t num; + const char *reset_name; +}; + +static const struct clk_parent_data osc_24m_clk[] = { + { .index = 0 }, +}; + +ATLANTIS_PLL_DEFINE(CLK_RCPU_PLL, rcpu_pll_clk, osc_24m_clk, PLL_RCPU_CFG_REG, + PLL_RCPU_EN_REG, BUS_CG_REG, 0, /* No Gate Clk at Output */ + CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL); + +static const struct clk_parent_data rcpu_root_parents[] = { + { .index = 0 }, + { .hw = &rcpu_pll_clk.common.hw }, +}; + +ATLANTIS_MUX_DEFINE(CLK_RCPU_ROOT, rcpu_root_clk, rcpu_root_parents, + RCPU_DIV_CFG_REG, 0, 1, CLK_SET_RATE_NO_REPARENT); + +ATLANTIS_DIVIDER_DEFINE(CLK_RCPU_DIV2, rcpu_div2_clk, rcpu_root_clk, + RCPU_DIV_CFG_REG, 2, 4, 0, 0); +ATLANTIS_DIVIDER_DEFINE(CLK_RCPU_DIV4, rcpu_div4_clk, rcpu_root_clk, + RCPU_DIV_CFG_REG, 7, 4, 0, 0); +ATLANTIS_DIVIDER_DEFINE(CLK_RCPU_RTC, rcpu_rtc_clk, rcpu_div4_clk, + RCPU_DIV_CFG_REG, 12, 6, 0, 0); + +ATLANTIS_GATE_DEFINE(CLK_SMNDMA0_ACLK, rcpu_dma0_clk, rcpu_div2_clk, + RCPU_BLK_CG_REG, BIT(0), 0); +ATLANTIS_GATE_DEFINE(CLK_SMNDMA1_ACLK, rcpu_dma1_clk, rcpu_div2_clk, + RCPU_BLK_CG_REG, BIT(1), 0); +ATLANTIS_GATE_DEFINE(CLK_WDT0_PCLK, sl_wdt0_pclk, rcpu_div4_clk, + RCPU_BLK_CG_REG, BIT(2), 0); +ATLANTIS_GATE_DEFINE(CLK_WDT1_PCLK, sl_wdt1_pclk, rcpu_div4_clk, + RCPU_BLK_CG_REG, BIT(3), 0); +ATLANTIS_GATE_DEFINE(CLK_TIMER_PCLK, sl_timer_pclk, rcpu_div4_clk, + RCPU_BLK_CG_REG, BIT(4), 0); +ATLANTIS_GATE_DEFINE(CLK_PVTC_PCLK, sl_pvtc_pclk, rcpu_div4_clk, + RCPU_BLK_CG_REG, BIT(12), 0); +ATLANTIS_GATE_DEFINE(CLK_PMU_PCLK, sl_pmu_pclk, rcpu_div4_clk, RCPU_BLK_CG_REG, + BIT(13), 0); +ATLANTIS_GATE_DEFINE(CLK_MAILBOX_HCLK, rcpu_ipc_clk, rcpu_div2_clk, + RCPU_BLK_CG_REG, BIT(14), 0); +ATLANTIS_GATE_DEFINE(CLK_SEC_SPACC_HCLK, sec_spacc_hclk, rcpu_div2_clk, + RCPU_BLK_CG_REG, BIT(26), 0); +ATLANTIS_GATE_DEFINE(CLK_SEC_OTP_HCLK, sec_otp_hclk, rcpu_div2_clk, + RCPU_BLK_CG_REG, BIT(28), 0); +ATLANTIS_GATE_DEFINE(CLK_TRNG_PCLK, sec_trng_pclk, rcpu_div4_clk, + RCPU_BLK_CG_REG, BIT(29), 0); +ATLANTIS_GATE_DEFINE(CLK_SEC_CRC_HCLK, sec_crc_hclk, rcpu_div2_clk, + RCPU_BLK_CG_REG, BIT(30), 0); + +ATLANTIS_FIXED_FACTOR_DEFINE(CLK_SMN_HCLK, rcpu_smn_hclk, rcpu_div2_clk, 1, 1, + 0); +ATLANTIS_FIXED_FACTOR_DEFINE(CLK_AHB0_HCLK, rcpu_ahb0_hclk, rcpu_div2_clk, 1, 1, + 0); + +ATLANTIS_FIXED_FACTOR_DEFINE(CLK_SMN_PCLK, rcpu_smn_pclk, rcpu_div4_clk, 1, 1, + 0); + +ATLANTIS_FIXED_FACTOR_DEFINE(CLK_SMN_CLK, rcpu_smn_clk, rcpu_root_clk, 1, 1, 0); +ATLANTIS_FIXED_FACTOR_DEFINE(CLK_SCRATCHPAD_CLK, rcpu_scratchpad_aclk, + rcpu_root_clk, 1, 1, 0); +ATLANTIS_FIXED_FACTOR_DEFINE(CLK_RCPU_CORE_CLK, rcpu_core_clk, rcpu_root_clk, 1, + 1, 0); +ATLANTIS_FIXED_FACTOR_DEFINE(CLK_RCPU_ROM_CLK, rcpu_rom_aclk, rcpu_root_clk, 1, + 1, 0); + +static struct atlantis_clk_fixed_factor + otp_load_clk = { .config = { .mult = 1, .div = 1 }, + .common = { + .clkid = CLK_OTP_LOAD_CLK, + .hw.init = CLK_HW_INIT_PARENTS_DATA( + "otp_load_clk", osc_24m_clk, + &atlantis_clk_fixed_factor_ops, + CLK_SET_RATE_NO_REPARENT), + } }; + +ATLANTIS_PLL_DEFINE(CLK_NOC_PLL, nocc_pll_clk, osc_24m_clk, PLL_NOCC_CFG_REG, + PLL_NOCC_EN_REG, BUS_CG_REG, BIT(0), + CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL); + +static const struct clk_parent_data nocc_mux_parents[] = { + { .index = 0 }, + { .hw = &nocc_pll_clk.common.hw }, +}; + +ATLANTIS_MUX_DEFINE(CLK_NOCC_CLK, nocc_clk, nocc_mux_parents, NOCC_CLK_CFG_REG, + 0, 1, CLK_SET_RATE_NO_REPARENT); + +ATLANTIS_DIVIDER_DEFINE(CLK_NOCC_DIV2, nocc_div2_clk, nocc_clk, + NOCC_CLK_CFG_REG, 1, 4, 0, 0); +ATLANTIS_DIVIDER_DEFINE(CLK_NOCC_DIV4, nocc_div4_clk, nocc_clk, + NOCC_CLK_CFG_REG, 5, 4, 0, 0); +ATLANTIS_DIVIDER_DEFINE(CLK_NOCC_RTC, nocc_rtc_clk, nocc_div4_clk, + NOCC_CLK_CFG_REG, 9, 6, 0, 0); +ATLANTIS_DIVIDER_DEFINE(CLK_NOCC_CAN, nocc_can_clk, nocc_clk, NOCC_CLK_CFG_REG, + 15, 4, 0, 0); + +static unsigned int refcnt_qspi; +ATLANTIS_GATE_SHARED_DEFINE(CLK_QSPI_SCLK, lsio_qspi_sclk, nocc_clk, + LSIO_BLK_CG_REG, BIT(0), &refcnt_qspi, 0); +ATLANTIS_GATE_SHARED_DEFINE(CLK_QSPI_HCLK, lsio_qspi_hclk, nocc_div2_clk, + LSIO_BLK_CG_REG, BIT(0), &refcnt_qspi, 0); +ATLANTIS_GATE_DEFINE(CLK_I2C0_PCLK, lsio_i2c0_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(1), 0); +ATLANTIS_GATE_DEFINE(CLK_I2C1_PCLK, lsio_i2c1_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(2), 0); +ATLANTIS_GATE_DEFINE(CLK_I2C2_PCLK, lsio_i2c2_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(3), 0); +ATLANTIS_GATE_DEFINE(CLK_I2C3_PCLK, lsio_i2c3_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(4), 0); +ATLANTIS_GATE_DEFINE(CLK_I2C4_PCLK, lsio_i2c4_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(5), 0); + +ATLANTIS_GATE_DEFINE(CLK_UART0_PCLK, lsio_uart0_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(6), 0); +ATLANTIS_GATE_DEFINE(CLK_UART1_PCLK, lsio_uart1_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(7), 0); +ATLANTIS_GATE_DEFINE(CLK_UART2_PCLK, lsio_uart2_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(8), 0); +ATLANTIS_GATE_DEFINE(CLK_UART3_PCLK, lsio_uart3_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(9), 0); +ATLANTIS_GATE_DEFINE(CLK_UART4_PCLK, lsio_uart4_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(10), 0); +ATLANTIS_GATE_DEFINE(CLK_SPI0_PCLK, lsio_spi0_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(11), 0); +ATLANTIS_GATE_DEFINE(CLK_SPI1_PCLK, lsio_spi1_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(12), 0); +ATLANTIS_GATE_DEFINE(CLK_SPI2_PCLK, lsio_spi2_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(13), 0); +ATLANTIS_GATE_DEFINE(CLK_SPI3_PCLK, lsio_spi3_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(14), 0); +ATLANTIS_GATE_DEFINE(CLK_GPIO_PCLK, lsio_gpio_pclk, nocc_div4_clk, + LSIO_BLK_CG_REG, BIT(15), 0); + +static unsigned int refcnt_can0; +ATLANTIS_GATE_SHARED_DEFINE(CLK_CAN0_HCLK, lsio_can0_hclk, nocc_div2_clk, + LSIO_BLK_CG_REG, BIT(17), &refcnt_can0, 0); +ATLANTIS_GATE_SHARED_DEFINE(CLK_CAN0_CLK, lsio_can0_clk, nocc_can_clk, + LSIO_BLK_CG_REG, BIT(17), &refcnt_can0, 0); + +static unsigned int refcnt_can1; +ATLANTIS_GATE_SHARED_DEFINE(CLK_CAN1_HCLK, lsio_can1_hclk, nocc_div2_clk, + LSIO_BLK_CG_REG, BIT(18), &refcnt_can1, 0); +ATLANTIS_GATE_SHARED_DEFINE(CLK_CAN1_CLK, lsio_can1_clk, nocc_can_clk, + LSIO_BLK_CG_REG, BIT(18), &refcnt_can1, 0); + +ATLANTIS_FIXED_FACTOR_DEFINE(CLK_CAN0_TIMER_CLK, lsio_can0_timer_clk, + nocc_rtc_clk, 1, 1, 0); +ATLANTIS_FIXED_FACTOR_DEFINE(CLK_CAN1_TIMER_CLK, lsio_can1_timer_clk, + nocc_rtc_clk, 1, 1, 0); + +static struct clk_hw *atlantis_rcpu_clks[] = { + [CLK_RCPU_PLL] = &rcpu_pll_clk.common.hw, + [CLK_RCPU_ROOT] = &rcpu_root_clk.common.hw, + [CLK_RCPU_DIV2] = &rcpu_div2_clk.common.hw, + [CLK_RCPU_DIV4] = &rcpu_div4_clk.common.hw, + [CLK_RCPU_RTC] = &rcpu_rtc_clk.common.hw, + [CLK_SMNDMA0_ACLK] = &rcpu_dma0_clk.common.hw, + [CLK_SMNDMA1_ACLK] = &rcpu_dma1_clk.common.hw, + [CLK_WDT0_PCLK] = &sl_wdt0_pclk.common.hw, + [CLK_WDT1_PCLK] = &sl_wdt1_pclk.common.hw, + [CLK_TIMER_PCLK] = &sl_timer_pclk.common.hw, + [CLK_PVTC_PCLK] = &sl_pvtc_pclk.common.hw, + [CLK_PMU_PCLK] = &sl_pmu_pclk.common.hw, + [CLK_MAILBOX_HCLK] = &rcpu_ipc_clk.common.hw, + [CLK_SEC_SPACC_HCLK] = &sec_spacc_hclk.common.hw, + [CLK_SEC_OTP_HCLK] = &sec_otp_hclk.common.hw, + [CLK_TRNG_PCLK] = &sec_trng_pclk.common.hw, + [CLK_SEC_CRC_HCLK] = &sec_crc_hclk.common.hw, + [CLK_SMN_HCLK] = &rcpu_smn_hclk.common.hw, + [CLK_AHB0_HCLK] = &rcpu_ahb0_hclk.common.hw, + [CLK_SMN_PCLK] = &rcpu_smn_pclk.common.hw, + [CLK_SMN_CLK] = &rcpu_smn_clk.common.hw, + [CLK_SCRATCHPAD_CLK] = &rcpu_scratchpad_aclk.common.hw, + [CLK_RCPU_CORE_CLK] = &rcpu_core_clk.common.hw, + [CLK_RCPU_ROM_CLK] = &rcpu_rom_aclk.common.hw, + [CLK_OTP_LOAD_CLK] = &otp_load_clk.common.hw, + [CLK_NOC_PLL] = &nocc_pll_clk.common.hw, + [CLK_NOCC_CLK] = &nocc_clk.common.hw, + [CLK_NOCC_DIV2] = &nocc_div2_clk.common.hw, + [CLK_NOCC_DIV4] = &nocc_div4_clk.common.hw, + [CLK_NOCC_RTC] = &nocc_rtc_clk.common.hw, + [CLK_NOCC_CAN] = &nocc_can_clk.common.hw, + [CLK_QSPI_SCLK] = &lsio_qspi_sclk.common.hw, + [CLK_QSPI_HCLK] = &lsio_qspi_hclk.common.hw, + [CLK_I2C0_PCLK] = &lsio_i2c0_pclk.common.hw, + [CLK_I2C1_PCLK] = &lsio_i2c1_pclk.common.hw, + [CLK_I2C2_PCLK] = &lsio_i2c2_pclk.common.hw, + [CLK_I2C3_PCLK] = &lsio_i2c3_pclk.common.hw, + [CLK_I2C4_PCLK] = &lsio_i2c4_pclk.common.hw, + [CLK_UART0_PCLK] = &lsio_uart0_pclk.common.hw, + [CLK_UART1_PCLK] = &lsio_uart1_pclk.common.hw, + [CLK_UART2_PCLK] = &lsio_uart2_pclk.common.hw, + [CLK_UART3_PCLK] = &lsio_uart3_pclk.common.hw, + [CLK_UART4_PCLK] = &lsio_uart4_pclk.common.hw, + [CLK_SPI0_PCLK] = &lsio_spi0_pclk.common.hw, + [CLK_SPI1_PCLK] = &lsio_spi1_pclk.common.hw, + [CLK_SPI2_PCLK] = &lsio_spi2_pclk.common.hw, + [CLK_SPI3_PCLK] = &lsio_spi3_pclk.common.hw, + [CLK_GPIO_PCLK] = &lsio_gpio_pclk.common.hw, + [CLK_CAN0_HCLK] = &lsio_can0_hclk.common.hw, + [CLK_CAN0_CLK] = &lsio_can0_clk.common.hw, + [CLK_CAN1_HCLK] = &lsio_can1_hclk.common.hw, + [CLK_CAN1_CLK] = &lsio_can1_clk.common.hw, + [CLK_CAN0_TIMER_CLK] = &lsio_can0_timer_clk.common.hw, + [CLK_CAN1_TIMER_CLK] = &lsio_can1_timer_clk.common.hw, +}; + +static const struct atlantis_prcm_data atlantis_prcm_rcpu_data = { + .hws = atlantis_rcpu_clks, + .num = ARRAY_SIZE(atlantis_rcpu_clks), + .reset_name = "rcpu-reset" +}; + +static int atlantis_prcm_clocks_register(struct device *dev, + struct regmap *regmap, + const struct atlantis_prcm_data *data) +{ + struct clk_hw_onecell_data *clk_data; + int i, ret; + size_t num_clks = data->num; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, data->num), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + for (i = 0; i < data->num; i++) { + struct clk_hw *hw = data->hws[i]; + struct atlantis_clk_common *common = + hw_to_atlantis_clk_common(hw); + common->regmap = regmap; + + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; + + clk_data->hws[common->clkid] = hw; + } + + clk_data->num = num_clks; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); +} + +static int atlantis_prcm_probe(struct platform_device *pdev) +{ + const struct atlantis_prcm_data *data; + struct auxiliary_device *reset_adev; + struct regmap *regmap; + void __iomem *base; + struct device *dev = &pdev->dev; + int ret; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), + "Failed to map registers\n"); + + regmap = devm_regmap_init_mmio(dev, base, &atlantis_prcm_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to init regmap\n"); + + data = of_device_get_match_data(dev); + + ret = atlantis_prcm_clocks_register(dev, regmap, data); + if (ret) + return dev_err_probe(dev, ret, "failed to register clocks\n"); + + reset_adev = devm_auxiliary_device_create(dev, data->reset_name, NULL); + if (!reset_adev) + return dev_err_probe(dev, -ENODEV, "failed to register resets\n"); + + return 0; +} + +static const struct of_device_id atlantis_prcm_of_match[] = { + { + .compatible = "tenstorrent,atlantis-prcm-rcpu", + .data = &atlantis_prcm_rcpu_data, + }, + {} + +}; +MODULE_DEVICE_TABLE(of, atlantis_prcm_of_match); + +static struct platform_driver atlantis_prcm_driver = { + .probe = atlantis_prcm_probe, + .driver = { + .name = "atlantis-prcm", + .of_match_table = atlantis_prcm_of_match, + }, +}; +module_platform_driver(atlantis_prcm_driver); + +MODULE_DESCRIPTION("Tenstorrent Atlantis PRCM Clock Controller Driver"); +MODULE_AUTHOR("Anirudh Srinivasan "); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/visconti/pll.c b/drivers/clk/visconti/pll.c index 6fd02c4b641e..805b95481281 100644 --- a/drivers/clk/visconti/pll.c +++ b/drivers/clk/visconti/pll.c @@ -249,7 +249,7 @@ static struct clk_hw *visconti_register_pll(struct visconti_pll_provider *ctx, const struct visconti_pll_rate_table *rate_table, spinlock_t *lock) { - struct clk_init_data init; + struct clk_init_data init = {}; struct visconti_pll *pll; struct clk_hw *pll_hw_clk; size_t len; diff --git a/drivers/comedi/Kconfig b/drivers/comedi/Kconfig index 6dcc2567de6d..c34a8d68547b 100644 --- a/drivers/comedi/Kconfig +++ b/drivers/comedi/Kconfig @@ -405,20 +405,20 @@ config COMEDI_FL512 called fl512. config COMEDI_AIO_AIO12_8 - tristate "I/O Products PC/104 AIO12-8 Analog I/O Board support" + tristate "ACCES I/O Products PC/104 AIO12-8 Analog I/O Board support" select COMEDI_8254 select COMEDI_8255 help - Enable support for I/O Products PC/104 AIO12-8 Analog I/O Board + Enable support for ACCES I/O Products PC/104 AIO12-8 Analog I/O Board To compile this driver as a module, choose M here: the module will be called aio_aio12_8. config COMEDI_AIO_IIRO_16 - tristate "I/O Products PC/104 IIRO16 Board support" + tristate "ACCES I/O Products PC/104 IIRO16 Board support" help - Enable support for I/O Products PC/104 IIRO16 Relay And Isolated - Input Board + Enable support for ACCES I/O Products PC/104 IIRO16 Relay And + Isolated Input Board To compile this driver as a module, choose M here: the module will be called aio_iiro_16. diff --git a/drivers/comedi/comedi_pci.c b/drivers/comedi/comedi_pci.c index cc2581902195..da618f5e3a4d 100644 --- a/drivers/comedi/comedi_pci.c +++ b/drivers/comedi/comedi_pci.c @@ -211,17 +211,6 @@ void comedi_pci_driver_unregister(struct comedi_driver *comedi_driver, } EXPORT_SYMBOL_GPL(comedi_pci_driver_unregister); -static int __init comedi_pci_init(void) -{ - return 0; -} -module_init(comedi_pci_init); - -static void __exit comedi_pci_exit(void) -{ -} -module_exit(comedi_pci_exit); - MODULE_AUTHOR("https://www.comedi.org"); MODULE_DESCRIPTION("Comedi PCI interface module"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/comedi_pcmcia.c b/drivers/comedi/comedi_pcmcia.c index c53aad0fc2ce..17962bf66892 100644 --- a/drivers/comedi/comedi_pcmcia.c +++ b/drivers/comedi/comedi_pcmcia.c @@ -192,17 +192,6 @@ void comedi_pcmcia_driver_unregister(struct comedi_driver *comedi_driver, } EXPORT_SYMBOL_GPL(comedi_pcmcia_driver_unregister); -static int __init comedi_pcmcia_init(void) -{ - return 0; -} -module_init(comedi_pcmcia_init); - -static void __exit comedi_pcmcia_exit(void) -{ -} -module_exit(comedi_pcmcia_exit); - MODULE_AUTHOR("https://www.comedi.org"); MODULE_DESCRIPTION("Comedi PCMCIA interface module"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/comedi_usb.c b/drivers/comedi/comedi_usb.c index d11ea148ebf8..75d65171c76d 100644 --- a/drivers/comedi/comedi_usb.c +++ b/drivers/comedi/comedi_usb.c @@ -134,17 +134,6 @@ void comedi_usb_driver_unregister(struct comedi_driver *comedi_driver, } EXPORT_SYMBOL_GPL(comedi_usb_driver_unregister); -static int __init comedi_usb_init(void) -{ - return 0; -} -module_init(comedi_usb_init); - -static void __exit comedi_usb_exit(void) -{ -} -module_exit(comedi_usb_exit); - MODULE_AUTHOR("https://www.comedi.org"); MODULE_DESCRIPTION("Comedi USB interface module"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers.c b/drivers/comedi/drivers.c index 5ab96b5eefd1..e78ad565f8f3 100644 --- a/drivers/comedi/drivers.c +++ b/drivers/comedi/drivers.c @@ -933,19 +933,24 @@ int comedi_load_firmware(struct comedi_device *dev, EXPORT_SYMBOL_GPL(comedi_load_firmware); /** - * __comedi_request_region() - Request an I/O region for a legacy driver + * __comedi_check_request_region() - Request an I/O region for a legacy driver * @dev: COMEDI device. * @start: Base address of the I/O region. * @len: Length of the I/O region. + * @minstart: Minimum allowed start address of region. + * @maxend: Maximum allowed region end address of region. + * @minalign: Required alignment for base address. * * Requests the specified I/O port region which must start at a non-zero - * address. + * address, must fall within specified bounds, and must be correctly aligned. * * Returns 0 on success, -EINVAL if @start is 0, or -EIO if the request * fails. */ -int __comedi_request_region(struct comedi_device *dev, - unsigned long start, unsigned long len) +int __comedi_check_request_region(struct comedi_device *dev, + unsigned long start, unsigned long len, + unsigned long minstart, unsigned long maxend, + unsigned long minalign) { if (!start) { dev_warn(dev->class_dev, @@ -954,6 +959,19 @@ int __comedi_request_region(struct comedi_device *dev, return -EINVAL; } + if (start < minstart || start > maxend || maxend - start < len - 1) { + dev_warn(dev->class_dev, + "%s: I/O base address or length out of range\n", + dev->board_name); + return -EINVAL; + } + if (!IS_ALIGNED(start, minalign)) { + dev_warn(dev->class_dev, + "%s: I/O base address not correctly aligned\n", + dev->board_name); + return -EINVAL; + } + if (!request_region(start, len, dev->board_name)) { dev_warn(dev->class_dev, "%s: I/O port conflict (%#lx,%lu)\n", dev->board_name, start, len); @@ -962,16 +980,19 @@ int __comedi_request_region(struct comedi_device *dev, return 0; } -EXPORT_SYMBOL_GPL(__comedi_request_region); +EXPORT_SYMBOL_GPL(__comedi_check_request_region); /** - * comedi_request_region() - Request an I/O region for a legacy driver + * comedi_check_request_region() - Request an I/O region for a legacy driver * @dev: COMEDI device. * @start: Base address of the I/O region. * @len: Length of the I/O region. + * @minstart: Minimum allowed start address of region. + * @maxend: Maximum allowed region end address of region. + * @minalign: Required alignment for base address. * * Requests the specified I/O port region which must start at a non-zero - * address. + * address, must fall within specified bounds, and must be correctly aligned. * * On success, @dev->iobase is set to the base address of the region and * @dev->iolen is set to its length. @@ -979,12 +1000,15 @@ EXPORT_SYMBOL_GPL(__comedi_request_region); * Returns 0 on success, -EINVAL if @start is 0, or -EIO if the request * fails. */ -int comedi_request_region(struct comedi_device *dev, - unsigned long start, unsigned long len) +int comedi_check_request_region(struct comedi_device *dev, + unsigned long start, unsigned long len, + unsigned long minstart, unsigned long maxend, + unsigned long minalign) { int ret; - ret = __comedi_request_region(dev, start, len); + ret = __comedi_check_request_region(dev, start, len, minstart, maxend, + minalign); if (ret == 0) { dev->iobase = start; dev->iolen = len; @@ -992,7 +1016,7 @@ int comedi_request_region(struct comedi_device *dev, return ret; } -EXPORT_SYMBOL_GPL(comedi_request_region); +EXPORT_SYMBOL_GPL(comedi_check_request_region); /** * comedi_legacy_detach() - A generic (*detach) function for legacy drivers diff --git a/drivers/comedi/drivers/8255.c b/drivers/comedi/drivers/8255.c index 5f70938b4477..ff45248ebb29 100644 --- a/drivers/comedi/drivers/8255.c +++ b/drivers/comedi/drivers/8255.c @@ -47,7 +47,7 @@ static int dev_8255_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct comedi_subdevice *s; - unsigned long iobase; + unsigned int iobase; int ret; int i; @@ -70,13 +70,15 @@ static int dev_8255_attach(struct comedi_device *dev, iobase = it->options[i]; /* - * __comedi_request_region() does not set dev->iobase. + * __comedi_check_request_region() does not set dev->iobase. * * For 8255 devices that are manually attached using * comedi_config, the 'iobase' is the actual I/O port - * base address of the chip. + * base address of the chip. It should be aligned on + * a 4-byte boundary. */ - ret = __comedi_request_region(dev, iobase, I8255_SIZE); + ret = __comedi_check_request_region(dev, iobase, I8255_SIZE, + 0, UINT_MAX, 4); if (ret) return ret; ret = subdev_8255_io_init(dev, s, iobase); diff --git a/drivers/comedi/drivers/addi_watchdog.c b/drivers/comedi/drivers/addi_watchdog.c index ed87ab432020..b778c88d4c11 100644 --- a/drivers/comedi/drivers/addi_watchdog.c +++ b/drivers/comedi/drivers/addi_watchdog.c @@ -124,17 +124,6 @@ int addi_watchdog_init(struct comedi_subdevice *s, unsigned long iobase) } EXPORT_SYMBOL_GPL(addi_watchdog_init); -static int __init addi_watchdog_module_init(void) -{ - return 0; -} -module_init(addi_watchdog_module_init); - -static void __exit addi_watchdog_module_exit(void) -{ -} -module_exit(addi_watchdog_module_exit); - MODULE_DESCRIPTION("ADDI-DATA Watchdog subdevice"); MODULE_AUTHOR("H Hartley Sweeten "); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/adq12b.c b/drivers/comedi/drivers/adq12b.c index 19d765182006..f8fa5c6ecdff 100644 --- a/drivers/comedi/drivers/adq12b.c +++ b/drivers/comedi/drivers/adq12b.c @@ -179,7 +179,8 @@ static int adq12b_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0x300, 0x3af, 0x20); if (ret) return ret; diff --git a/drivers/comedi/drivers/aio_aio12_8.c b/drivers/comedi/drivers/aio_aio12_8.c index 227a86a3a760..e0cbc2ec7c4d 100644 --- a/drivers/comedi/drivers/aio_aio12_8.c +++ b/drivers/comedi/drivers/aio_aio12_8.c @@ -1,17 +1,17 @@ // SPDX-License-Identifier: GPL-2.0+ /* * aio_aio12_8.c - * Driver for Access I/O Products PC-104 AIO12-8 Analog I/O Board + * Driver for ACCES I/O Products PC-104 AIO12-8 Analog I/O Board * Copyright (C) 2006 C&C Technologies, Inc. */ /* * Driver: aio_aio12_8 - * Description: Access I/O Products PC-104 AIO12-8 Analog I/O Board + * Description: ACCES I/O Products PC-104 AIO12-8 Analog I/O Board * Author: Pablo Mejia - * Devices: [Access I/O] PC-104 AIO12-8 (aio_aio12_8), - * [Access I/O] PC-104 AI12-8 (aio_ai12_8), - * [Access I/O] PC-104 AO12-4 (aio_ao12_4) + * Devices: [ACCES I/O] PC-104 AIO12-8 (aio_aio12_8), + * [ACCES I/O] PC-104 AI12-8 (aio_ai12_8), + * [ACCES I/O] PC-104 AO12-4 (aio_ao12_4) * Status: experimental * * Configuration Options: @@ -202,7 +202,8 @@ static int aio_aio12_8_attach(struct comedi_device *dev, struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 32); + ret = comedi_check_request_region(dev, it->options[0], 32, + 0x100, 0x3ff, 32); if (ret) return ret; @@ -272,5 +273,5 @@ static struct comedi_driver aio_aio12_8_driver = { module_comedi_driver(aio_aio12_8_driver); MODULE_AUTHOR("Comedi https://www.comedi.org"); -MODULE_DESCRIPTION("Comedi driver for Access I/O AIO12-8 Analog I/O Board"); +MODULE_DESCRIPTION("Comedi driver for ACCES I/O AIO12-8 Analog I/O Board"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/aio_iiro_16.c b/drivers/comedi/drivers/aio_iiro_16.c index 739cc4db52ac..d5d18fa2c638 100644 --- a/drivers/comedi/drivers/aio_iiro_16.c +++ b/drivers/comedi/drivers/aio_iiro_16.c @@ -1,15 +1,15 @@ // SPDX-License-Identifier: GPL-2.0+ /* * aio_iiro_16.c - * Comedi driver for Access I/O Products 104-IIRO-16 board + * Comedi driver for ACCES I/O Products 104-IIRO-16 board * Copyright (C) 2006 C&C Technologies, Inc. */ /* * Driver: aio_iiro_16 - * Description: Access I/O Products PC/104 Isolated Input/Relay Output Board + * Description: ACCES I/O Products PC/104 Isolated Input/Relay Output Board * Author: Zachary Ware - * Devices: [Access I/O] 104-IIRO-16 (aio_iiro_16) + * Devices: [ACCES I/O] 104-IIRO-16 (aio_iiro_16) * Status: experimental * * Configuration Options: @@ -167,7 +167,8 @@ static int aio_iiro_16_attach(struct comedi_device *dev, struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x8); + ret = comedi_check_request_region(dev, it->options[0], 0x8, + 0x100, 0x3ff, 0x8); if (ret) return ret; @@ -231,5 +232,5 @@ static struct comedi_driver aio_iiro_16_driver = { module_comedi_driver(aio_iiro_16_driver); MODULE_AUTHOR("Comedi https://www.comedi.org"); -MODULE_DESCRIPTION("Comedi driver for Access I/O Products 104-IIRO-16 board"); +MODULE_DESCRIPTION("Comedi driver for ACCES I/O Products 104-IIRO-16 board"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/amplc_dio200.c b/drivers/comedi/drivers/amplc_dio200.c index 4544bcdd8a70..8b1cbc3d24e5 100644 --- a/drivers/comedi/drivers/amplc_dio200.c +++ b/drivers/comedi/drivers/amplc_dio200.c @@ -242,7 +242,8 @@ static int dio200_attach(struct comedi_device *dev, struct comedi_devconfig *it) { int ret; - ret = comedi_request_region(dev, it->options[0], 0x20); + ret = comedi_check_request_region(dev, it->options[0], 0x20, + 0, 0xfff, 0x20); if (ret) return ret; diff --git a/drivers/comedi/drivers/amplc_dio200_common.c b/drivers/comedi/drivers/amplc_dio200_common.c index b1a9b4c4a185..d61f51900976 100644 --- a/drivers/comedi/drivers/amplc_dio200_common.c +++ b/drivers/comedi/drivers/amplc_dio200_common.c @@ -901,17 +901,6 @@ int amplc_dio200_common_attach(struct comedi_device *dev, unsigned int irq, } EXPORT_SYMBOL_GPL(amplc_dio200_common_attach); -static int __init amplc_dio200_common_init(void) -{ - return 0; -} -module_init(amplc_dio200_common_init); - -static void __exit amplc_dio200_common_exit(void) -{ -} -module_exit(amplc_dio200_common_exit); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi helper for amplc_dio200 and amplc_dio200_pci"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/amplc_pc236.c b/drivers/comedi/drivers/amplc_pc236.c index b21e0c906aab..eadee10983bf 100644 --- a/drivers/comedi/drivers/amplc_pc236.c +++ b/drivers/comedi/drivers/amplc_pc236.c @@ -45,7 +45,8 @@ static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x4); + ret = comedi_check_request_region(dev, it->options[0], 0x4, + 0, 0xfff, 4); if (ret) return ret; diff --git a/drivers/comedi/drivers/amplc_pc236_common.c b/drivers/comedi/drivers/amplc_pc236_common.c index 326ca72c24ec..c3692f50bdb9 100644 --- a/drivers/comedi/drivers/amplc_pc236_common.c +++ b/drivers/comedi/drivers/amplc_pc236_common.c @@ -176,17 +176,6 @@ int amplc_pc236_common_attach(struct comedi_device *dev, unsigned long iobase, } EXPORT_SYMBOL_GPL(amplc_pc236_common_attach); -static int __init amplc_pc236_common_init(void) -{ - return 0; -} -module_init(amplc_pc236_common_init); - -static void __exit amplc_pc236_common_exit(void) -{ -} -module_exit(amplc_pc236_common_exit); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi helper for amplc_pc236 and amplc_pci236"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/amplc_pc263.c b/drivers/comedi/drivers/amplc_pc263.c index d7f088a8a5e3..e2196dc9d426 100644 --- a/drivers/comedi/drivers/amplc_pc263.c +++ b/drivers/comedi/drivers/amplc_pc263.c @@ -61,7 +61,8 @@ static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x2); + ret = comedi_check_request_region(dev, it->options[0], 0x2, + 0, 0x7ff, 2); if (ret) return ret; diff --git a/drivers/comedi/drivers/c6xdigio.c b/drivers/comedi/drivers/c6xdigio.c index 8a38d97d463b..b6563a48ada6 100644 --- a/drivers/comedi/drivers/c6xdigio.c +++ b/drivers/comedi/drivers/c6xdigio.c @@ -239,9 +239,11 @@ static int c6xdigio_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct comedi_subdevice *s; + unsigned int iobase = it->options[0]; int ret; - ret = comedi_request_region(dev, it->options[0], 0x03); + ret = comedi_check_request_region(dev, iobase, 0x03, + 0, UINT_MAX, 4); if (ret) return ret; diff --git a/drivers/comedi/drivers/comedi_8254.c b/drivers/comedi/drivers/comedi_8254.c index a297dd650dab..158e8b4108a8 100644 --- a/drivers/comedi/drivers/comedi_8254.c +++ b/drivers/comedi/drivers/comedi_8254.c @@ -724,17 +724,6 @@ struct comedi_8254 *comedi_8254_mm_alloc(void __iomem *mmio, } EXPORT_SYMBOL_GPL(comedi_8254_mm_alloc); -static int __init comedi_8254_module_init(void) -{ - return 0; -} -module_init(comedi_8254_module_init); - -static void __exit comedi_8254_module_exit(void) -{ -} -module_exit(comedi_8254_module_exit); - MODULE_AUTHOR("H Hartley Sweeten "); MODULE_DESCRIPTION("Comedi: Generic 8254 timer/counter support"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/comedi_8255.c b/drivers/comedi/drivers/comedi_8255.c index a933ef53845a..fd6b1cf621a7 100644 --- a/drivers/comedi/drivers/comedi_8255.c +++ b/drivers/comedi/drivers/comedi_8255.c @@ -259,17 +259,6 @@ unsigned long subdev_8255_regbase(struct comedi_subdevice *s) } EXPORT_SYMBOL_GPL(subdev_8255_regbase); -static int __init comedi_8255_module_init(void) -{ - return 0; -} -module_init(comedi_8255_module_init); - -static void __exit comedi_8255_module_exit(void) -{ -} -module_exit(comedi_8255_module_exit); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi: Generic 8255 digital I/O support"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/comedi_isadma.c b/drivers/comedi/drivers/comedi_isadma.c index 04ce3eb9ae4f..b346079b9b6b 100644 --- a/drivers/comedi/drivers/comedi_isadma.c +++ b/drivers/comedi/drivers/comedi_isadma.c @@ -161,14 +161,10 @@ struct comedi_isadma *comedi_isadma_alloc(struct comedi_device *dev, if (n_desc < 1 || n_desc > 2) goto no_dma; - dma = kzalloc_obj(*dma); + dma = kzalloc_flex(*dma, desc, n_desc); if (!dma) goto no_dma; - desc = kzalloc_objs(*desc, n_desc); - if (!desc) - goto no_dma; - dma->desc = desc; dma->n_desc = n_desc; if (dev->hw_dev) { dma->dev = dev->hw_dev; @@ -231,15 +227,12 @@ void comedi_isadma_free(struct comedi_isadma *dma) if (!dma) return; - if (dma->desc) { - for (i = 0; i < dma->n_desc; i++) { - desc = &dma->desc[i]; - if (desc->virt_addr) - dma_free_coherent(dma->dev, desc->maxsize, - desc->virt_addr, - desc->hw_addr); - } - kfree(dma->desc); + for (i = 0; i < dma->n_desc; i++) { + desc = &dma->desc[i]; + if (desc->virt_addr) + dma_free_coherent(dma->dev, desc->maxsize, + desc->virt_addr, + desc->hw_addr); } if (dma->chan2 && dma->chan2 != dma->chan) free_dma(dma->chan2); @@ -249,17 +242,6 @@ void comedi_isadma_free(struct comedi_isadma *dma) } EXPORT_SYMBOL_GPL(comedi_isadma_free); -static int __init comedi_isadma_init(void) -{ - return 0; -} -module_init(comedi_isadma_init); - -static void __exit comedi_isadma_exit(void) -{ -} -module_exit(comedi_isadma_exit); - MODULE_AUTHOR("H Hartley Sweeten "); MODULE_DESCRIPTION("Comedi ISA DMA support"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/comedi_parport.c b/drivers/comedi/drivers/comedi_parport.c index 098738a688fe..2604680d86c4 100644 --- a/drivers/comedi/drivers/comedi_parport.c +++ b/drivers/comedi/drivers/comedi_parport.c @@ -225,9 +225,11 @@ static int parport_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct comedi_subdevice *s; + unsigned int iobase = it->options[0]; int ret; - ret = comedi_request_region(dev, it->options[0], 0x03); + ret = comedi_check_request_region(dev, iobase, 0x03, + 0, UINT_MAX, 4); if (ret) return ret; diff --git a/drivers/comedi/drivers/dac02.c b/drivers/comedi/drivers/dac02.c index 4b011d66d7b0..2c04472c5078 100644 --- a/drivers/comedi/drivers/dac02.c +++ b/drivers/comedi/drivers/dac02.c @@ -103,7 +103,8 @@ static int dac02_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x08); + ret = comedi_check_request_region(dev, it->options[0], 0x08, + 0x200, 0x3ff, 8); if (ret) return ret; diff --git a/drivers/comedi/drivers/das08.c b/drivers/comedi/drivers/das08.c index 49944ce1f813..a3298a3238e7 100644 --- a/drivers/comedi/drivers/das08.c +++ b/drivers/comedi/drivers/das08.c @@ -453,17 +453,6 @@ int das08_common_attach(struct comedi_device *dev, unsigned long iobase) } EXPORT_SYMBOL_GPL(das08_common_attach); -static int __init das08_init(void) -{ - return 0; -} -module_init(das08_init); - -static void __exit das08_exit(void) -{ -} -module_exit(das08_exit); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi common DAS08 support module"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/das08_isa.c b/drivers/comedi/drivers/das08_isa.c index 3d43b77cc9f4..1b022dc10b78 100644 --- a/drivers/comedi/drivers/das08_isa.c +++ b/drivers/comedi/drivers/das08_isa.c @@ -167,7 +167,8 @@ static int das08_isa_attach(struct comedi_device *dev, if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], board->iosize); + ret = comedi_check_request_region(dev, it->options[0], board->iosize, + 0, 0x3ff, board->iosize); if (ret) return ret; diff --git a/drivers/comedi/drivers/das16.c b/drivers/comedi/drivers/das16.c index 1f85572c21b4..6eebfda2cb53 100644 --- a/drivers/comedi/drivers/das16.c +++ b/drivers/comedi/drivers/das16.c @@ -1018,6 +1018,7 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it) const struct das16_board *board = dev->board_ptr; struct das16_private_struct *devpriv; struct comedi_subdevice *s; + unsigned int iobase = it->options[0]; unsigned int osc_base; unsigned int status; int ret; @@ -1037,11 +1038,25 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it) devpriv->dev = dev; if (board->size < 0x400) { - ret = comedi_request_region(dev, it->options[0], board->size); + unsigned int size = board->size; + + if (size > 0x10 && (iobase & 0x10) != 0) { + /* + * The board has more than 0x10 registers and is + * being placed on an odd 16-byte boundary. The + * board has some jumpers to configure this mode, + * disabling the 8255 at offset 0x10, so only 0x10 + * registers will need to be mapped in this mode. + */ + size = 0x10; + } + ret = comedi_check_request_region(dev, iobase, size, + 0, 0x3ff, 16); if (ret) return ret; } else { - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, iobase, 0x10, + 0, 0x3ff, 16); if (ret) return ret; /* Request an additional region for the 8255 */ @@ -1146,9 +1161,15 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it) /* 8255 Digital I/O subdevice */ if (board->has_8255) { s = &dev->subdevices[4]; - ret = subdev_8255_io_init(dev, s, board->i8255_offset); - if (ret) - return ret; + if (board->i8255_offset == 0x10 && (dev->iobase & 0x10) != 0) { + dev_info(dev->class_dev, + "Disabling 8255 subdevice on unsupported base address\n"); + s->type = COMEDI_SUBD_UNUSED; + } else { + ret = subdev_8255_io_init(dev, s, board->i8255_offset); + if (ret) + return ret; + } } das16_reset(dev); diff --git a/drivers/comedi/drivers/das16m1.c b/drivers/comedi/drivers/das16m1.c index 1b638f5b5a4f..9f531ba593b4 100644 --- a/drivers/comedi/drivers/das16m1.c +++ b/drivers/comedi/drivers/das16m1.c @@ -511,7 +511,8 @@ static int das16m1_attach(struct comedi_device *dev, if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; /* Request an additional region for the 8255 and 3rd 8254 */ diff --git a/drivers/comedi/drivers/das1800.c b/drivers/comedi/drivers/das1800.c index 7117c67aee7e..7d7ea99a81aa 100644 --- a/drivers/comedi/drivers/das1800.c +++ b/drivers/comedi/drivers/das1800.c @@ -1172,7 +1172,8 @@ static int das1800_attach(struct comedi_device *dev, if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], DAS1800_SIZE); + ret = comedi_check_request_region(dev, it->options[0], DAS1800_SIZE, + 0, 0x3ff, 16); if (ret) return ret; diff --git a/drivers/comedi/drivers/das6402.c b/drivers/comedi/drivers/das6402.c index 7660487e563c..516a5d5a2840 100644 --- a/drivers/comedi/drivers/das6402.c +++ b/drivers/comedi/drivers/das6402.c @@ -560,7 +560,8 @@ static int das6402_attach(struct comedi_device *dev, if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; diff --git a/drivers/comedi/drivers/das800.c b/drivers/comedi/drivers/das800.c index 300775523031..7563f40a4023 100644 --- a/drivers/comedi/drivers/das800.c +++ b/drivers/comedi/drivers/das800.c @@ -655,7 +655,8 @@ static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x8); + ret = comedi_check_request_region(dev, it->options[0], 0x8, + 0, 0x3ff, 8); if (ret) return ret; diff --git a/drivers/comedi/drivers/dmm32at.c b/drivers/comedi/drivers/dmm32at.c index 910cd24b1bed..d1d9f75e168f 100644 --- a/drivers/comedi/drivers/dmm32at.c +++ b/drivers/comedi/drivers/dmm32at.c @@ -572,11 +572,27 @@ static int dmm32at_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct comedi_subdevice *s; + unsigned int iobase = it->options[0]; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); - if (ret) - return ret; + switch (iobase) { + case 0x100: + case 0x140: + case 0x180: + case 0x200: + case 0x280: + case 0x300: + case 0x340: + case 0x380: + ret = comedi_request_region(dev, iobase, 0x10); + if (ret) + return ret; + break; + default: + dev_err(dev->class_dev, "unsupported base address %#x\n", + iobase); + return -EINVAL; + } ret = dmm32at_reset(dev); if (ret) { diff --git a/drivers/comedi/drivers/dt2801.c b/drivers/comedi/drivers/dt2801.c index 230d25010f58..d790e69f98a0 100644 --- a/drivers/comedi/drivers/dt2801.c +++ b/drivers/comedi/drivers/dt2801.c @@ -540,7 +540,8 @@ static int dt2801_attach(struct comedi_device *dev, struct comedi_devconfig *it) int ret = 0; int n_ai_chans; - ret = comedi_request_region(dev, it->options[0], 0x2); + ret = comedi_check_request_region(dev, it->options[0], 0x2, + 0x200, 0x3ff, 2); if (ret) return ret; diff --git a/drivers/comedi/drivers/dt2811.c b/drivers/comedi/drivers/dt2811.c index dbb9f38da289..bcc4b5ef48e8 100644 --- a/drivers/comedi/drivers/dt2811.c +++ b/drivers/comedi/drivers/dt2811.c @@ -556,7 +556,8 @@ static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x8); + ret = comedi_check_request_region(dev, it->options[0], 0x8, + 0x200, 0x3ff, 8); if (ret) return ret; diff --git a/drivers/comedi/drivers/dt2814.c b/drivers/comedi/drivers/dt2814.c index c98a5a4a7aec..caec16482afb 100644 --- a/drivers/comedi/drivers/dt2814.c +++ b/drivers/comedi/drivers/dt2814.c @@ -305,7 +305,8 @@ static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x2); + ret = comedi_check_request_region(dev, it->options[0], 0x2, + 0x200, 0x3ff, 2); if (ret) return ret; diff --git a/drivers/comedi/drivers/dt2815.c b/drivers/comedi/drivers/dt2815.c index d066dc303520..8832ecef2ab6 100644 --- a/drivers/comedi/drivers/dt2815.c +++ b/drivers/comedi/drivers/dt2815.c @@ -144,7 +144,8 @@ static int dt2815_attach(struct comedi_device *dev, struct comedi_devconfig *it) const struct comedi_lrange *current_range_type, *voltage_range_type; int ret; - ret = comedi_request_region(dev, it->options[0], 0x2); + ret = comedi_check_request_region(dev, it->options[0], 0x2, + 0x200, 0x3ff, 2); if (ret) return ret; diff --git a/drivers/comedi/drivers/dt2817.c b/drivers/comedi/drivers/dt2817.c index 6738045c7531..9fd5b47c7fa7 100644 --- a/drivers/comedi/drivers/dt2817.c +++ b/drivers/comedi/drivers/dt2817.c @@ -103,7 +103,8 @@ static int dt2817_attach(struct comedi_device *dev, struct comedi_devconfig *it) int ret; struct comedi_subdevice *s; - ret = comedi_request_region(dev, it->options[0], 0x5); + ret = comedi_check_request_region(dev, it->options[0], 0x5, + 0x200, 0x3ff, 8); if (ret) return ret; diff --git a/drivers/comedi/drivers/dt282x.c b/drivers/comedi/drivers/dt282x.c index 4ae80e6c7266..29832e1f062d 100644 --- a/drivers/comedi/drivers/dt282x.c +++ b/drivers/comedi/drivers/dt282x.c @@ -1064,7 +1064,12 @@ static int dt282x_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + /* + * Although it has only 16 bytes (8 16-bit registers), it needs to + * start on a 32-byte boundary in range 0x200 to 0x3E0. + */ + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0x200, 0x3ff, 32); if (ret) return ret; diff --git a/drivers/comedi/drivers/fl512.c b/drivers/comedi/drivers/fl512.c index 139e801fc358..d9e6007556ac 100644 --- a/drivers/comedi/drivers/fl512.c +++ b/drivers/comedi/drivers/fl512.c @@ -98,9 +98,15 @@ static int fl512_ao_insn_write(struct comedi_device *dev, static int fl512_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct comedi_subdevice *s; + unsigned int iobase = it->options[0]; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + /* + * FIXME: Don't know the allowed range, but assume it needs to be + * on a 16-byte boundary - Ian Abbott + */ + ret = comedi_check_request_region(dev, iobase, 0x10, + 0, UINT_MAX, 16); if (ret) return ret; diff --git a/drivers/comedi/drivers/mite.c b/drivers/comedi/drivers/mite.c index e93150d6dc57..9098616900f7 100644 --- a/drivers/comedi/drivers/mite.c +++ b/drivers/comedi/drivers/mite.c @@ -921,17 +921,6 @@ void mite_detach(struct mite *mite) } EXPORT_SYMBOL_GPL(mite_detach); -static int __init mite_module_init(void) -{ - return 0; -} -module_init(mite_module_init); - -static void __exit mite_module_exit(void) -{ -} -module_exit(mite_module_exit); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi helper for NI Mite PCI interface chip"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/mpc624.c b/drivers/comedi/drivers/mpc624.c index 9e51ff528ed1..e6343f4267c1 100644 --- a/drivers/comedi/drivers/mpc624.c +++ b/drivers/comedi/drivers/mpc624.c @@ -237,7 +237,8 @@ static int mpc624_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; diff --git a/drivers/comedi/drivers/multiq3.c b/drivers/comedi/drivers/multiq3.c index ac369e9a262d..3dde3a1876d7 100644 --- a/drivers/comedi/drivers/multiq3.c +++ b/drivers/comedi/drivers/multiq3.c @@ -259,7 +259,8 @@ static int multiq3_attach(struct comedi_device *dev, int ret; int i; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; diff --git a/drivers/comedi/drivers/ni_at_a2150.c b/drivers/comedi/drivers/ni_at_a2150.c index e4e5a0ebd195..44221c928e32 100644 --- a/drivers/comedi/drivers/ni_at_a2150.c +++ b/drivers/comedi/drivers/ni_at_a2150.c @@ -694,7 +694,8 @@ static int a2150_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x1c); + ret = comedi_check_request_region(dev, it->options[0], 0x1c, + 0, 0x3ff, 32); if (ret) return ret; diff --git a/drivers/comedi/drivers/ni_at_ao.c b/drivers/comedi/drivers/ni_at_ao.c index 9cf6b4ff6b65..31fd64bf8206 100644 --- a/drivers/comedi/drivers/ni_at_ao.c +++ b/drivers/comedi/drivers/ni_at_ao.c @@ -295,7 +295,8 @@ static int atao_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x20); + ret = comedi_check_request_region(dev, it->options[0], 0x20, + 0, 0x3ff, 32); if (ret) return ret; diff --git a/drivers/comedi/drivers/ni_atmio.c b/drivers/comedi/drivers/ni_atmio.c index b4e759e5703f..7bc336333ace 100644 --- a/drivers/comedi/drivers/ni_atmio.c +++ b/drivers/comedi/drivers/ni_atmio.c @@ -311,7 +311,8 @@ static int ni_atmio_attach(struct comedi_device *dev, comedi_set_hw_dev(dev, &isapnp_dev->dev); } - ret = comedi_request_region(dev, iobase, 0x20); + ret = comedi_check_request_region(dev, iobase, 0x20, + 0x20, 0xffff, 32); if (ret) return ret; diff --git a/drivers/comedi/drivers/ni_atmio16d.c b/drivers/comedi/drivers/ni_atmio16d.c index b057b3b3582e..b2772d909896 100644 --- a/drivers/comedi/drivers/ni_atmio16d.c +++ b/drivers/comedi/drivers/ni_atmio16d.c @@ -574,7 +574,8 @@ static int atmio16d_attach(struct comedi_device *dev, struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x20); + ret = comedi_check_request_region(dev, it->options[0], 0x20, + 0, 0x3ff, 32); if (ret) return ret; diff --git a/drivers/comedi/drivers/ni_labpc.c b/drivers/comedi/drivers/ni_labpc.c index b25a8e117072..93b5333a99b5 100644 --- a/drivers/comedi/drivers/ni_labpc.c +++ b/drivers/comedi/drivers/ni_labpc.c @@ -78,7 +78,8 @@ static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it) unsigned int dma_chan = it->options[2]; int ret; - ret = comedi_request_region(dev, it->options[0], 0x20); + ret = comedi_check_request_region(dev, it->options[0], 0x20, + 0, 0x3ff, 32); if (ret) return ret; diff --git a/drivers/comedi/drivers/ni_labpc_common.c b/drivers/comedi/drivers/ni_labpc_common.c index 7e0ce0ce0adf..21b838a9d9ec 100644 --- a/drivers/comedi/drivers/ni_labpc_common.c +++ b/drivers/comedi/drivers/ni_labpc_common.c @@ -1357,17 +1357,6 @@ void labpc_common_detach(struct comedi_device *dev) } EXPORT_SYMBOL_GPL(labpc_common_detach); -static int __init labpc_common_init(void) -{ - return 0; -} -module_init(labpc_common_init); - -static void __exit labpc_common_exit(void) -{ -} -module_exit(labpc_common_exit); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi helper for ni_labpc, ni_labpc_pci, ni_labpc_cs"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/ni_labpc_isadma.c b/drivers/comedi/drivers/ni_labpc_isadma.c index 0652ca8345b6..125797f655f2 100644 --- a/drivers/comedi/drivers/ni_labpc_isadma.c +++ b/drivers/comedi/drivers/ni_labpc_isadma.c @@ -164,17 +164,6 @@ void labpc_free_dma_chan(struct comedi_device *dev) } EXPORT_SYMBOL_GPL(labpc_free_dma_chan); -static int __init ni_labpc_isadma_init_module(void) -{ - return 0; -} -module_init(ni_labpc_isadma_init_module); - -static void __exit ni_labpc_isadma_cleanup_module(void) -{ -} -module_exit(ni_labpc_isadma_cleanup_module); - MODULE_AUTHOR("Comedi https://www.comedi.org"); MODULE_DESCRIPTION("Comedi NI Lab-PC ISA DMA support"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/ni_tio.c b/drivers/comedi/drivers/ni_tio.c index 519359a870a7..c3a909abcfa7 100644 --- a/drivers/comedi/drivers/ni_tio.c +++ b/drivers/comedi/drivers/ni_tio.c @@ -1825,17 +1825,6 @@ void ni_gpct_device_destroy(struct ni_gpct_device *counter_dev) } EXPORT_SYMBOL_GPL(ni_gpct_device_destroy); -static int __init ni_tio_init_module(void) -{ - return 0; -} -module_init(ni_tio_init_module); - -static void __exit ni_tio_cleanup_module(void) -{ -} -module_exit(ni_tio_cleanup_module); - MODULE_AUTHOR("Comedi "); MODULE_DESCRIPTION("Comedi support for NI general-purpose counters"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/ni_tiocmd.c b/drivers/comedi/drivers/ni_tiocmd.c index ab6d9e8269f3..9c166703626b 100644 --- a/drivers/comedi/drivers/ni_tiocmd.c +++ b/drivers/comedi/drivers/ni_tiocmd.c @@ -494,17 +494,6 @@ void ni_tio_set_mite_channel(struct ni_gpct *counter, } EXPORT_SYMBOL_GPL(ni_tio_set_mite_channel); -static int __init ni_tiocmd_init_module(void) -{ - return 0; -} -module_init(ni_tiocmd_init_module); - -static void __exit ni_tiocmd_cleanup_module(void) -{ -} -module_exit(ni_tiocmd_cleanup_module); - MODULE_AUTHOR("Comedi "); MODULE_DESCRIPTION("Comedi command support for NI general-purpose counters"); MODULE_LICENSE("GPL"); diff --git a/drivers/comedi/drivers/ni_usb6501.c b/drivers/comedi/drivers/ni_usb6501.c index 0dd9edf7bced..7afc5661a5ec 100644 --- a/drivers/comedi/drivers/ni_usb6501.c +++ b/drivers/comedi/drivers/ni_usb6501.c @@ -477,31 +477,16 @@ static int ni6501_find_endpoints(struct comedi_device *dev) struct usb_interface *intf = comedi_to_usb_interface(dev); struct ni6501_private *devpriv = dev->private; struct usb_host_interface *iface_desc = intf->cur_altsetting; - struct usb_endpoint_descriptor *ep_desc; - int i; + int ret; if (iface_desc->desc.bNumEndpoints != 2) { dev_err(dev->class_dev, "Wrong number of endpoints\n"); return -ENODEV; } - for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { - ep_desc = &iface_desc->endpoint[i].desc; - - if (usb_endpoint_is_bulk_in(ep_desc)) { - if (!devpriv->ep_rx) - devpriv->ep_rx = ep_desc; - continue; - } - - if (usb_endpoint_is_bulk_out(ep_desc)) { - if (!devpriv->ep_tx) - devpriv->ep_tx = ep_desc; - continue; - } - } - - if (!devpriv->ep_rx || !devpriv->ep_tx) + ret = usb_find_common_endpoints(iface_desc, &devpriv->ep_rx, + &devpriv->ep_tx, NULL, NULL); + if (ret) return -ENODEV; if (usb_endpoint_maxp(devpriv->ep_rx) < RX_MAX_SIZE) diff --git a/drivers/comedi/drivers/pcl711.c b/drivers/comedi/drivers/pcl711.c index 0cf3917defe7..5d2c4b2aa3bb 100644 --- a/drivers/comedi/drivers/pcl711.c +++ b/drivers/comedi/drivers/pcl711.c @@ -112,6 +112,7 @@ struct pcl711_board { int n_aichan; int n_aochan; int maxirq; + unsigned int min_io_start; const struct comedi_lrange *ai_range_type; }; @@ -132,12 +133,14 @@ static const struct pcl711_board boardtypes[] = { .n_aichan = 16, .n_aochan = 2, .maxirq = 15, + .min_io_start = 0x200, .ai_range_type = &range_acl8112hg_ai, }, { .name = "acl8112dg", .n_aichan = 16, .n_aochan = 2, .maxirq = 15, + .min_io_start = 0x200, .ai_range_type = &range_acl8112dg_ai, }, }; @@ -418,7 +421,8 @@ static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + board->min_io_start, 0x3ff, 16); if (ret) return ret; diff --git a/drivers/comedi/drivers/pcl724.c b/drivers/comedi/drivers/pcl724.c index 00474710b81f..9707d0c89304 100644 --- a/drivers/comedi/drivers/pcl724.c +++ b/drivers/comedi/drivers/pcl724.c @@ -31,6 +31,8 @@ struct pcl724_board { const char *name; unsigned int io_range; + unsigned int min_io_start; + unsigned int max_io_end; unsigned int can_have96:1; unsigned int is_pet48:1; int numofports; @@ -40,37 +42,53 @@ static const struct pcl724_board boardtypes[] = { { .name = "pcl724", .io_range = 0x04, + .min_io_start = 0x200, + .max_io_end = 0x3ff, .numofports = 1, /* 24 DIO channels */ }, { .name = "pcl722", .io_range = 0x20, + .min_io_start = 0x200, + .max_io_end = 0x3ff, .can_have96 = 1, .numofports = 6, /* 144 (or 96) DIO channels */ }, { .name = "pcl731", .io_range = 0x08, + .min_io_start = 0, + .max_io_end = 0x3ff, .numofports = 2, /* 48 DIO channels */ }, { .name = "acl7122", .io_range = 0x20, + .min_io_start = 0x200, + .max_io_end = 0x3ff, .can_have96 = 1, .numofports = 6, /* 144 (or 96) DIO channels */ }, { .name = "acl7124", .io_range = 0x04, + .min_io_start = 0x200, + .max_io_end = 0x3ff, .numofports = 1, /* 24 DIO channels */ }, { .name = "pet48dio", .io_range = 0x02, + .min_io_start = 0, + .max_io_end = 0x3ff, .is_pet48 = 1, .numofports = 2, /* 48 DIO channels */ }, { .name = "pcmio48", .io_range = 0x08, + .min_io_start = 0x100, + .max_io_end = 0x17f, .numofports = 2, /* 48 DIO channels */ }, { .name = "onyx-mm-dio", .io_range = 0x10, + .min_io_start = 0, + .max_io_end = 0x3ff, .numofports = 2, /* 48 DIO channels */ }, }; @@ -112,7 +130,9 @@ static int pcl724_attach(struct comedi_device *dev, n_subdevices = 4; } - ret = comedi_request_region(dev, it->options[0], iorange); + ret = comedi_check_request_region(dev, it->options[0], iorange, + board->min_io_start, + board->max_io_end, iorange); if (ret) return ret; diff --git a/drivers/comedi/drivers/pcl726.c b/drivers/comedi/drivers/pcl726.c index b542896fa0e4..d3b1f4388643 100644 --- a/drivers/comedi/drivers/pcl726.c +++ b/drivers/comedi/drivers/pcl726.c @@ -91,7 +91,8 @@ static const struct comedi_lrange *const rangelist_728[] = { struct pcl726_board { const char *name; - unsigned long io_len; + unsigned int io_len; + unsigned int min_io_start; unsigned int irq_mask; const struct comedi_lrange *const *ao_ranges; int ao_num_ranges; @@ -104,6 +105,7 @@ static const struct pcl726_board pcl726_boards[] = { { .name = "pcl726", .io_len = 0x10, + .min_io_start = 0x200, .ao_ranges = &rangelist_726[0], .ao_num_ranges = ARRAY_SIZE(rangelist_726), .ao_nchan = 6, @@ -111,6 +113,7 @@ static const struct pcl726_board pcl726_boards[] = { }, { .name = "pcl727", .io_len = 0x20, + .min_io_start = 0x200, .ao_ranges = &rangelist_727[0], .ao_num_ranges = ARRAY_SIZE(rangelist_727), .ao_nchan = 12, @@ -119,12 +122,14 @@ static const struct pcl726_board pcl726_boards[] = { }, { .name = "pcl728", .io_len = 0x08, + .min_io_start = 0, .ao_num_ranges = ARRAY_SIZE(rangelist_728), .ao_ranges = &rangelist_728[0], .ao_nchan = 2, }, { .name = "acl6126", .io_len = 0x10, + .min_io_start = 0x200, .irq_mask = 0x96e8, .ao_num_ranges = ARRAY_SIZE(rangelist_726), .ao_ranges = &rangelist_726[0], @@ -133,6 +138,7 @@ static const struct pcl726_board pcl726_boards[] = { }, { .name = "acl6128", .io_len = 0x08, + .min_io_start = 0, .ao_num_ranges = ARRAY_SIZE(rangelist_728), .ao_ranges = &rangelist_728[0], .ao_nchan = 2, @@ -316,7 +322,9 @@ static int pcl726_attach(struct comedi_device *dev, int ret; int i; - ret = comedi_request_region(dev, it->options[0], board->io_len); + ret = comedi_check_request_region(dev, it->options[0], board->io_len, + board->min_io_start, 0x3ff, + board->io_len); if (ret) return ret; diff --git a/drivers/comedi/drivers/pcl730.c b/drivers/comedi/drivers/pcl730.c index d2733cd5383d..cf0f97b6e2c0 100644 --- a/drivers/comedi/drivers/pcl730.c +++ b/drivers/comedi/drivers/pcl730.c @@ -101,7 +101,9 @@ struct pcl730_board { const char *name; - unsigned int io_range; + unsigned short io_range; + unsigned short min_io_start; + unsigned short align_io_start; unsigned is_pcl725:1; unsigned is_acl7225b:1; unsigned is_ir104:1; @@ -117,6 +119,8 @@ static const struct pcl730_board pcl730_boards[] = { { .name = "pcl730", .io_range = 0x04, + .min_io_start = 0, + .align_io_start = 0x04, .has_ttl_io = 1, .n_subdevs = 4, .n_iso_out_chan = 16, @@ -125,6 +129,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "iso730", .io_range = 0x04, + .min_io_start = 0, + .align_io_start = 0x04, .n_subdevs = 4, .n_iso_out_chan = 16, .n_iso_in_chan = 16, @@ -132,6 +138,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "acl7130", .io_range = 0x08, + .min_io_start = 0x200, + .align_io_start = 0x08, .has_ttl_io = 1, .n_subdevs = 4, .n_iso_out_chan = 16, @@ -140,6 +148,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "pcm3730", .io_range = 0x04, + .min_io_start = 0, + .align_io_start = 0x04, .has_ttl_io = 1, .n_subdevs = 4, .n_iso_out_chan = 8, @@ -148,6 +158,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "pcl725", .io_range = 0x02, + .min_io_start = 0x200, + .align_io_start = 0x02, .is_pcl725 = 1, .n_subdevs = 2, .n_iso_out_chan = 8, @@ -155,6 +167,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "p8r8dio", .io_range = 0x02, + .min_io_start = 0, + .align_io_start = 0x10, .is_pcl725 = 1, .has_readback = 1, .n_subdevs = 2, @@ -163,6 +177,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "acl7225b", .io_range = 0x08, /* only 4 are used */ + .min_io_start = 0x200, + .align_io_start = 0x08, .is_acl7225b = 1, .has_readback = 1, .n_subdevs = 2, @@ -171,6 +187,8 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "p16r16dio", .io_range = 0x04, + .min_io_start = 0, + .align_io_start = 0x08, .is_acl7225b = 1, .has_readback = 1, .n_subdevs = 2, @@ -179,16 +197,22 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "pcl733", .io_range = 0x04, + .min_io_start = 0, + .align_io_start = 0x04, .n_subdevs = 1, .n_iso_in_chan = 32, }, { .name = "pcl734", .io_range = 0x04, + .min_io_start = 0, + .align_io_start = 0x04, .n_subdevs = 1, .n_iso_out_chan = 32, }, { .name = "opmm-1616-xt", .io_range = 0x10, + .min_io_start = 0x100, + .align_io_start = 0x10, .is_acl7225b = 1, .has_readback = 1, .n_subdevs = 2, @@ -197,11 +221,15 @@ static const struct pcl730_board pcl730_boards[] = { }, { .name = "pearl-mm-p", .io_range = 0x02, + .min_io_start = 0x240, + .align_io_start = 0x40, .n_subdevs = 1, .n_iso_out_chan = 16, }, { .name = "ir104-pbf", .io_range = 0x08, + .min_io_start = 0x240, + .align_io_start = 0x20, .is_ir104 = 1, .has_readback = 1, .n_iso_out_chan = 20, @@ -266,10 +294,27 @@ static int pcl730_attach(struct comedi_device *dev, { const struct pcl730_board *board = dev->board_ptr; struct comedi_subdevice *s; + unsigned int iobase = it->options[0]; int subdev; int ret; - ret = comedi_request_region(dev, it->options[0], board->io_range); + if (board->is_ir104) { + switch (iobase) { + case 0x240: + case 0x260: + case 0x280: + case 0x300: + break; + default: + dev_warn(dev->class_dev, + "%s: unsupported I/O base address %#x\n", + dev->board_name, iobase); + return -EINVAL; + } + } + ret = comedi_check_request_region(dev, iobase, board->io_range, + board->min_io_start, 0x3ff, + board->align_io_start); if (ret) return ret; diff --git a/drivers/comedi/drivers/pcl812.c b/drivers/comedi/drivers/pcl812.c index abca61a72cf7..98b75792c09b 100644 --- a/drivers/comedi/drivers/pcl812.c +++ b/drivers/comedi/drivers/pcl812.c @@ -331,6 +331,7 @@ enum pcl812_boardtype { struct pcl812_board { const char *name; enum pcl812_boardtype board_type; + unsigned short min_io_start; int n_aichan; int n_aochan; unsigned int ai_ns_min; @@ -346,6 +347,7 @@ static const struct pcl812_board boardtypes[] = { { .name = "pcl812", .board_type = BOARD_PCL812, + .min_io_start = 0, .n_aichan = 16, .n_aochan = 2, .ai_ns_min = 33000, @@ -355,6 +357,7 @@ static const struct pcl812_board boardtypes[] = { .has_dio = 1, }, { .name = "pcl812pg", + .min_io_start = 0, .board_type = BOARD_PCL812PG, .n_aichan = 16, .n_aochan = 2, @@ -366,6 +369,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "acl8112pg", .board_type = BOARD_PCL812PG, + .min_io_start = 0x200, .n_aichan = 16, .n_aochan = 2, .ai_ns_min = 10000, @@ -376,6 +380,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "acl8112dg", .board_type = BOARD_ACL8112, + .min_io_start = 0x200, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 10000, @@ -387,6 +392,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "acl8112hg", .board_type = BOARD_ACL8112, + .min_io_start = 0x200, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 10000, @@ -398,6 +404,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a821pgl", .board_type = BOARD_A821, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 1, .ai_ns_min = 10000, @@ -407,6 +414,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a821pglnda", .board_type = BOARD_A821, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .ai_ns_min = 10000, .rangelist_ai = &range_pcl813b_ai, @@ -414,6 +422,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a821pgh", .board_type = BOARD_A821, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 1, .ai_ns_min = 10000, @@ -423,6 +432,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a822pgl", .board_type = BOARD_ACL8112, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 10000, @@ -433,6 +443,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a822pgh", .board_type = BOARD_ACL8112, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 10000, @@ -443,6 +454,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a823pgl", .board_type = BOARD_ACL8112, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 8000, @@ -453,6 +465,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a823pgh", .board_type = BOARD_ACL8112, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 8000, @@ -463,26 +476,31 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "pcl813", .board_type = BOARD_PCL813, + .min_io_start = 0, .n_aichan = 32, .rangelist_ai = &range_pcl813b_ai, }, { .name = "pcl813b", .board_type = BOARD_PCL813B, + .min_io_start = 0, .n_aichan = 32, .rangelist_ai = &range_pcl813b_ai, }, { .name = "acl8113", .board_type = BOARD_ACL8113, + .min_io_start = 0x200, .n_aichan = 32, .rangelist_ai = &range_acl8113_1_ai, }, { .name = "iso813", .board_type = BOARD_ISO813, + .min_io_start = 0, .n_aichan = 32, .rangelist_ai = &range_iso813_1_ai, }, { .name = "acl8216", .board_type = BOARD_ACL8216, + .min_io_start = 0x200, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 10000, @@ -495,6 +513,7 @@ static const struct pcl812_board boardtypes[] = { }, { .name = "a826pg", .board_type = BOARD_ACL8216, + .min_io_start = 0, .n_aichan = 16, /* 8 differential */ .n_aochan = 2, .ai_ns_min = 10000, @@ -1138,7 +1157,8 @@ static int pcl812_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + board->min_io_start, 0x3ff, 16); if (ret) return ret; diff --git a/drivers/comedi/drivers/pcl816.c b/drivers/comedi/drivers/pcl816.c index 28d1a88c50f6..1fcb2f798c7a 100644 --- a/drivers/comedi/drivers/pcl816.c +++ b/drivers/comedi/drivers/pcl816.c @@ -608,7 +608,8 @@ static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; diff --git a/drivers/comedi/drivers/pcl818.c b/drivers/comedi/drivers/pcl818.c index 06fe06396f23..aa775a024fc7 100644 --- a/drivers/comedi/drivers/pcl818.c +++ b/drivers/comedi/drivers/pcl818.c @@ -981,6 +981,10 @@ static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it) const struct pcl818_board *board = dev->board_ptr; struct pcl818_private *devpriv; struct comedi_subdevice *s; + unsigned int io_base = it->options[0]; + bool fifo_is_supported = board->has_fifo && !(io_base & 0x10); + bool fifo_is_wanted = board->has_fifo && it->options[2] == -1; + unsigned int io_len = fifo_is_supported ? 0x20 : 0x10; unsigned int osc_base; int ret; @@ -988,11 +992,28 @@ static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (!devpriv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], - board->has_fifo ? 0x20 : 0x10); + ret = comedi_check_request_region(dev, io_base, io_len, + 0, 0x3ff, io_len); if (ret) return ret; + if (board->has_fifo) { + /* let user know about any required JP6 setting */ + if (fifo_is_supported) { + if (fifo_is_wanted) { + dev_info(dev->class_dev, + "Assuming JP6 is in \"Enabled\" (default) position to use the FIFO.\n"); + } + } else { + dev_info(dev->class_dev, + "JP6 needs to be in \"Disabled\" position for correct operation at this base address\n"); + if (fifo_is_wanted) { + dev_warn(dev->class_dev, + "FIFO cannot be used at this base address\n"); + } + } + } + /* we can use IRQ 2-7 for async command support */ if (it->options[1] >= 2 && it->options[1] <= 7) { ret = request_irq(it->options[1], pcl818_interrupt, 0, @@ -1002,7 +1023,7 @@ static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it) } /* should we use the FIFO? */ - if (dev->irq && board->has_fifo && it->options[2] == -1) + if (dev->irq && fifo_is_supported && fifo_is_wanted) devpriv->usefifo = 1; /* we need an IRQ to do DMA on channel 3 or 1 */ diff --git a/drivers/comedi/drivers/pcm3724.c b/drivers/comedi/drivers/pcm3724.c index fb41de3baef8..867bb5b3860a 100644 --- a/drivers/comedi/drivers/pcm3724.c +++ b/drivers/comedi/drivers/pcm3724.c @@ -194,7 +194,8 @@ static int pcm3724_attach(struct comedi_device *dev, if (!priv) return -ENOMEM; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; diff --git a/drivers/comedi/drivers/pcmad.c b/drivers/comedi/drivers/pcmad.c index 976eda43881b..fd26b1eed1db 100644 --- a/drivers/comedi/drivers/pcmad.c +++ b/drivers/comedi/drivers/pcmad.c @@ -106,7 +106,8 @@ static int pcmad_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x04); + ret = comedi_check_request_region(dev, it->options[0], 0x04, + 0, 0x3ff, 4); if (ret) return ret; diff --git a/drivers/comedi/drivers/pcmda12.c b/drivers/comedi/drivers/pcmda12.c index 611f13bedca0..6efd1ae6271a 100644 --- a/drivers/comedi/drivers/pcmda12.c +++ b/drivers/comedi/drivers/pcmda12.c @@ -120,7 +120,14 @@ static int pcmda12_attach(struct comedi_device *dev, struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + /* + * The datasheet says it requires 16 contiguous addresses and is + * "configurable on any even sixteen port boundary". So require + * a 32-byte boundary and assume it uses 10-bit addresses like + * similar boards. + */ + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 32); if (ret) return ret; diff --git a/drivers/comedi/drivers/pcmmio.c b/drivers/comedi/drivers/pcmmio.c index c2402239d551..d38202c8a12b 100644 --- a/drivers/comedi/drivers/pcmmio.c +++ b/drivers/comedi/drivers/pcmmio.c @@ -667,7 +667,8 @@ static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 32); + ret = comedi_check_request_region(dev, it->options[0], 32, + 0, 0xffff, 32); if (ret) return ret; diff --git a/drivers/comedi/drivers/pcmuio.c b/drivers/comedi/drivers/pcmuio.c index 33b24dbbb919..0995911a3ea3 100644 --- a/drivers/comedi/drivers/pcmuio.c +++ b/drivers/comedi/drivers/pcmuio.c @@ -521,11 +521,12 @@ static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it) const struct pcmuio_board *board = dev->board_ptr; struct comedi_subdevice *s; struct pcmuio_private *devpriv; + unsigned int io_len = board->num_asics * PCMUIO_ASIC_IOSIZE; int ret; int i; - ret = comedi_request_region(dev, it->options[0], - board->num_asics * PCMUIO_ASIC_IOSIZE); + ret = comedi_check_request_region(dev, it->options[0], io_len, + 0, 0xffff, io_len); if (ret) return ret; diff --git a/drivers/comedi/drivers/rti800.c b/drivers/comedi/drivers/rti800.c index 1b02e47bdb4c..fa3965cf92f2 100644 --- a/drivers/comedi/drivers/rti800.c +++ b/drivers/comedi/drivers/rti800.c @@ -257,7 +257,8 @@ static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x10); + ret = comedi_check_request_region(dev, it->options[0], 0x10, + 0, 0x3ff, 16); if (ret) return ret; diff --git a/drivers/comedi/drivers/rti802.c b/drivers/comedi/drivers/rti802.c index d66762a22258..af990881c27a 100644 --- a/drivers/comedi/drivers/rti802.c +++ b/drivers/comedi/drivers/rti802.c @@ -72,7 +72,8 @@ static int rti802_attach(struct comedi_device *dev, struct comedi_devconfig *it) int i; int ret; - ret = comedi_request_region(dev, it->options[0], 0x04); + ret = comedi_check_request_region(dev, it->options[0], 0x04, + 0, 0x3ff, 4); if (ret) return ret; diff --git a/drivers/comedi/drivers/s526.c b/drivers/comedi/drivers/s526.c index 9245c679a3c4..dd27c754dea4 100644 --- a/drivers/comedi/drivers/s526.c +++ b/drivers/comedi/drivers/s526.c @@ -553,7 +553,8 @@ static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it) struct comedi_subdevice *s; int ret; - ret = comedi_request_region(dev, it->options[0], 0x40); + ret = comedi_check_request_region(dev, it->options[0], 0x40, + 0, 0xffc0, 64); if (ret) return ret; diff --git a/drivers/comedi/kcomedilib/kcomedilib_main.c b/drivers/comedi/kcomedilib/kcomedilib_main.c index baa9eaaf97d4..517e60ffd81b 100644 --- a/drivers/comedi/kcomedilib/kcomedilib_main.c +++ b/drivers/comedi/kcomedilib/kcomedilib_main.c @@ -351,15 +351,3 @@ int comedi_get_n_channels(struct comedi_device *dev, unsigned int subdevice) return n; } EXPORT_SYMBOL_GPL(comedi_get_n_channels); - -static int __init kcomedilib_module_init(void) -{ - return 0; -} - -static void __exit kcomedilib_module_exit(void) -{ -} - -module_init(kcomedilib_module_init); -module_exit(kcomedilib_module_exit); diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index 42c523343d32..ed85da907982 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -1101,8 +1101,6 @@ static int counter_sysfs_attr_add(struct counter_device *const counter, /* Add device extensions */ return counter_sysfs_exts_add(dev, cattr_group, counter->ext, counter->num_ext, scope, NULL); - - return 0; } /** diff --git a/drivers/cpufreq/airoha-cpufreq.c b/drivers/cpufreq/airoha-cpufreq.c index b6b1cdc4d11d..3e7770860d13 100644 --- a/drivers/cpufreq/airoha-cpufreq.c +++ b/drivers/cpufreq/airoha-cpufreq.c @@ -115,15 +115,10 @@ MODULE_DEVICE_TABLE(of, airoha_cpufreq_match_list); static int __init airoha_cpufreq_init(void) { - struct device_node *np = of_find_node_by_path("/"); const struct of_device_id *match; int ret; - if (!np) - return -ENODEV; - - match = of_match_node(airoha_cpufreq_match_list, np); - of_node_put(np); + match = of_machine_get_match(airoha_cpufreq_match_list); if (!match) return -ENODEV; diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index e2ae608e7043..453084c67327 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -256,7 +256,7 @@ static int msr_update_perf(struct cpufreq_policy *policy, u8 min_perf, if (trace_amd_pstate_epp_perf_enabled()) { union perf_cached perf = READ_ONCE(cpudata->perf); - trace_amd_pstate_epp_perf(cpudata->cpu, + trace_call__amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf, epp, min_perf, @@ -306,7 +306,7 @@ static int msr_set_epp(struct cpufreq_policy *policy, u8 epp) if (trace_amd_pstate_epp_perf_enabled()) { union perf_cached perf = cpudata->perf; - trace_amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf, + trace_call__amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf, epp, FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cpudata->cppc_req_cached), @@ -420,7 +420,7 @@ static int shmem_set_epp(struct cpufreq_policy *policy, u8 epp) if (trace_amd_pstate_epp_perf_enabled()) { union perf_cached perf = cpudata->perf; - trace_amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf, + trace_call__amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf, epp, FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cpudata->cppc_req_cached), @@ -585,7 +585,7 @@ static int shmem_update_perf(struct cpufreq_policy *policy, u8 min_perf, if (trace_amd_pstate_epp_perf_enabled()) { union perf_cached perf = READ_ONCE(cpudata->perf); - trace_amd_pstate_epp_perf(cpudata->cpu, + trace_call__amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf, epp, min_perf, @@ -663,7 +663,7 @@ static void amd_pstate_update(struct cpufreq_policy *policy, u8 min_perf, } if (trace_amd_pstate_perf_enabled() && amd_pstate_sample(cpudata)) { - trace_amd_pstate_perf(min_perf, des_perf, max_perf, cpudata->freq, + trace_call__amd_pstate_perf(min_perf, des_perf, max_perf, cpudata->freq, cpudata->cur.mperf, cpudata->cur.aperf, cpudata->cur.tsc, cpudata->cpu, fast_switch); } diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 3c5b7fe52cd7..44eb1b7e7fc1 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2212,7 +2212,7 @@ unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy, if (trace_cpu_frequency_enabled()) { for_each_cpu(cpu, policy->cpus) - trace_cpu_frequency(freq, cpu); + trace_call__cpu_frequency(freq, cpu); } return freq; diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 1552b2d32a34..1292da53e5fc 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -3132,7 +3132,7 @@ static void intel_cpufreq_trace(struct cpudata *cpu, unsigned int trace_type, in return; sample = &cpu->sample; - trace_pstate_sample(trace_type, + trace_call__pstate_sample(trace_type, 0, old_pstate, cpu->pstate.current_pstate, diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index b8081acba928..e6d28d162442 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -291,17 +291,9 @@ static int qcom_cpufreq_ipq8064_name_version(struct device *cpu_dev, ret = qcom_smem_get_soc_id(&msm_id); if (ret == -ENODEV) { const struct of_device_id *match; - struct device_node *root; - - root = of_find_node_by_path("/"); - if (!root) { - ret = -ENODEV; - goto exit; - } /* Fallback to compatible match with no SMEM initialized */ - match = of_match_node(qcom_cpufreq_ipq806x_match_list, root); - of_node_put(root); + match = of_machine_get_match(qcom_cpufreq_ipq806x_match_list); if (!match) { ret = -ENODEV; goto exit; @@ -647,14 +639,10 @@ MODULE_DEVICE_TABLE(of, qcom_cpufreq_match_list); */ static int __init qcom_cpufreq_init(void) { - struct device_node *np __free(device_node) = of_find_node_by_path("/"); const struct of_device_id *match; int ret; - if (!np) - return -ENODEV; - - match = of_match_node(qcom_cpufreq_match_list, np); + match = of_machine_get_match(qcom_cpufreq_match_list); if (!match) return -ENODEV; diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c index 3d1129aeed02..a01abc1622eb 100644 --- a/drivers/cpufreq/ti-cpufreq.c +++ b/drivers/cpufreq/ti-cpufreq.c @@ -502,16 +502,6 @@ static const struct of_device_id ti_cpufreq_of_match[] __maybe_unused = { {}, }; -static const struct of_device_id *ti_cpufreq_match_node(void) -{ - struct device_node *np __free(device_node) = of_find_node_by_path("/"); - const struct of_device_id *match; - - match = of_match_node(ti_cpufreq_of_match, np); - - return match; -} - static int ti_cpufreq_probe(struct platform_device *pdev) { u32 version[VERSION_COUNT]; @@ -596,7 +586,7 @@ static int __init ti_cpufreq_init(void) const struct of_device_id *match; /* Check to ensure we are on a compatible platform */ - match = ti_cpufreq_match_node(); + match = of_machine_get_match(ti_cpufreq_of_match); if (match) platform_device_register_data(NULL, "ti-cpufreq", -1, match, sizeof(*match)); diff --git a/drivers/crypto/ccp/ccp-crypto-aes.c b/drivers/crypto/ccp/ccp-crypto-aes.c index 94bccc5d6c78..f475819b6fc3 100644 --- a/drivers/crypto/ccp/ccp-crypto-aes.c +++ b/drivers/crypto/ccp/ccp-crypto-aes.c @@ -30,8 +30,11 @@ static int ccp_aes_complete(struct crypto_async_request *async_req, int ret) if (ret) return ret; - if (ctx->u.aes.mode != CCP_AES_MODE_ECB) - memcpy(req->iv, rctx->iv, AES_BLOCK_SIZE); + if (ctx->u.aes.mode != CCP_AES_MODE_ECB) { + size_t ivsize = crypto_skcipher_ivsize(crypto_skcipher_reqtfm(req)); + + memcpy(req->iv, rctx->iv, ivsize); + } return 0; } diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 450d491379d4..d1e9e0ac63b6 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -1860,7 +1860,10 @@ cmd: ret = __sev_do_cmd_locked(SEV_CMD_PEK_CSR, &data, &argp->error); - /* If we query the CSR length, FW responded with expected data. */ + /* + * Firmware will returns the length of the CSR blob (either the minimum + * required length or the actual length written), return it to the user. + */ input.length = data.len; if (copy_to_user((void __user *)argp->data, &input, sizeof(input))) { @@ -1868,6 +1871,9 @@ cmd: goto e_free_blob; } + if (ret || WARN_ON_ONCE(argp->error)) + goto e_free_blob; + if (blob) { if (copy_to_user(input_address, blob, input.length)) ret = -EFAULT; @@ -2217,6 +2223,9 @@ static int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp) goto e_free; } + if (ret || WARN_ON_ONCE(argp->error)) + goto e_free; + if (id_blob) { if (copy_to_user(input_address, id_blob, data.len)) { ret = -EFAULT; @@ -2333,7 +2342,10 @@ cmd: ret = __sev_do_cmd_locked(SEV_CMD_PDH_CERT_EXPORT, &data, &argp->error); - /* If we query the length, FW responded with expected data. */ + /* + * Firmware will return the length of the blobs (either the minimum + * required length or the actual length written), return 'em to the user. + */ input.cert_chain_len = data.cert_chain_len; input.pdh_cert_len = data.pdh_cert_len; @@ -2342,6 +2354,9 @@ cmd: goto e_free_cert; } + if (ret || WARN_ON_ONCE(argp->error)) + goto e_free_cert; + if (pdh_blob) { if (copy_to_user(input_pdh_cert_address, pdh_blob, input.pdh_cert_len)) { diff --git a/drivers/crypto/inside-secure/eip93/eip93-common.c b/drivers/crypto/inside-secure/eip93/eip93-common.c index 6f147014f996..4c163d7281b3 100644 --- a/drivers/crypto/inside-secure/eip93/eip93-common.c +++ b/drivers/crypto/inside-secure/eip93/eip93-common.c @@ -731,7 +731,7 @@ int eip93_hmac_setkey(u32 ctx_flags, const u8 *key, unsigned int keylen, return -EINVAL; } - ahash_tfm = crypto_alloc_ahash(alg_name, 0, CRYPTO_ALG_ASYNC); + ahash_tfm = crypto_alloc_ahash(alg_name, 0, 0); if (IS_ERR(ahash_tfm)) return PTR_ERR(ahash_tfm); diff --git a/drivers/crypto/sa2ul.c b/drivers/crypto/sa2ul.c index df3defa1ef4b..965a03d5b27a 100644 --- a/drivers/crypto/sa2ul.c +++ b/drivers/crypto/sa2ul.c @@ -1744,13 +1744,13 @@ static int sa_cra_init_aead(struct crypto_aead *tfm, const char *hash, static int sa_cra_init_aead_sha1(struct crypto_aead *tfm) { return sa_cra_init_aead(tfm, "sha1", - "authenc(hmac(sha1-ce),cbc(aes-ce))"); + "authenc(hmac(sha1),cbc(aes))"); } static int sa_cra_init_aead_sha256(struct crypto_aead *tfm) { return sa_cra_init_aead(tfm, "sha256", - "authenc(hmac(sha256-ce),cbc(aes-ce))"); + "authenc(hmac(sha256),cbc(aes))"); } static void sa_exit_tfm_aead(struct crypto_aead *tfm) diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile index a639a9499972..ce7213818d3c 100644 --- a/drivers/cxl/core/Makefile +++ b/drivers/cxl/core/Makefile @@ -15,7 +15,7 @@ cxl_core-y += hdm.o cxl_core-y += pmu.o cxl_core-y += cdat.o cxl_core-$(CONFIG_TRACING) += trace.o -cxl_core-$(CONFIG_CXL_REGION) += region.o +cxl_core-$(CONFIG_CXL_REGION) += region.o region_pmem.o region_dax.o cxl_core-$(CONFIG_CXL_MCE) += mce.o cxl_core-$(CONFIG_CXL_FEATURES) += features.o cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 5b0570df0fd9..82ca3a476708 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -50,6 +50,8 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port); struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa); u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, u64 dpa); +int devm_cxl_add_dax_region(struct cxl_region *cxlr); +int devm_cxl_add_pmem_region(struct cxl_region *cxlr); #else static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, @@ -224,4 +226,6 @@ int cxl_set_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid, u16 *return_code); #endif +resource_size_t cxl_rcd_component_reg_phys(struct device *dev, + struct cxl_dport *dport); #endif /* __CXL_CORE_H__ */ diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c index cb5d5a047a9d..0c80b76a5f9b 100644 --- a/drivers/cxl/core/hdm.c +++ b/drivers/cxl/core/hdm.c @@ -170,7 +170,7 @@ static struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port, } parse_hdm_decoder_caps(cxlhdm); - if (cxlhdm->decoder_count == 0) { + if (cxlhdm->decoder_count < 0) { dev_err(dev, "Spec violation. Caps invalid\n"); return ERR_PTR(-ENXIO); } diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index 12386d912705..7c6c5b7450a5 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -893,7 +893,7 @@ out: } EXPORT_SYMBOL_NS_GPL(cxl_enumerate_cmds, "CXL"); -void cxl_event_trace_record(const struct cxl_memdev *cxlmd, +void cxl_event_trace_record(struct cxl_memdev *cxlmd, enum cxl_event_log_type type, enum cxl_event_type event_type, const uuid_t *uuid, union cxl_event *evt) @@ -920,6 +920,7 @@ void cxl_event_trace_record(const struct cxl_memdev *cxlmd, * translations. Take topology mutation locks and lookup * { HPA, REGION } from { DPA, MEMDEV } in the event record. */ + guard(device)(&cxlmd->dev); guard(rwsem_read)(&cxl_rwsem.region); guard(rwsem_read)(&cxl_rwsem.dpa); @@ -968,7 +969,7 @@ void cxl_event_trace_record(const struct cxl_memdev *cxlmd, } EXPORT_SYMBOL_NS_GPL(cxl_event_trace_record, "CXL"); -static void __cxl_event_trace_record(const struct cxl_memdev *cxlmd, +static void __cxl_event_trace_record(struct cxl_memdev *cxlmd, enum cxl_event_log_type type, struct cxl_event_record_raw *record) { @@ -1521,23 +1522,21 @@ int cxl_mailbox_init(struct cxl_mailbox *cxl_mbox, struct device *host) } EXPORT_SYMBOL_NS_GPL(cxl_mailbox_init, "CXL"); -struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev) +struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial, + u16 dvsec) { struct cxl_memdev_state *mds; int rc; - mds = devm_kzalloc(dev, sizeof(*mds), GFP_KERNEL); + mds = devm_cxl_dev_state_create(dev, CXL_DEVTYPE_CLASSMEM, serial, + dvsec, struct cxl_memdev_state, cxlds, + true); if (!mds) { dev_err(dev, "No memory available\n"); return ERR_PTR(-ENOMEM); } mutex_init(&mds->event.log_lock); - mds->cxlds.dev = dev; - mds->cxlds.reg_map.host = dev; - mds->cxlds.cxl_mbox.host = dev; - mds->cxlds.reg_map.resource = CXL_RESOURCE_NONE; - mds->cxlds.type = CXL_DEVTYPE_CLASSMEM; rc = devm_cxl_register_mce_notifier(dev, &mds->mce_notifier); if (rc == -EOPNOTSUPP) diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index 273c22118d3d..80e65690eb77 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -204,6 +204,9 @@ bool cxl_memdev_has_poison_cmd(struct cxl_memdev *cxlmd, { struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); + if (!mds) + return 0; + return test_bit(cmd, mds->poison.enabled_cmds); } @@ -656,6 +659,30 @@ static void detach_memdev(struct work_struct *work) static struct lock_class_key cxl_memdev_key; +struct cxl_dev_state *_devm_cxl_dev_state_create(struct device *dev, + enum cxl_devtype type, + u64 serial, u16 dvsec, + size_t size, bool has_mbox) +{ + struct cxl_dev_state *cxlds = devm_kzalloc(dev, size, GFP_KERNEL); + + if (!cxlds) + return NULL; + + cxlds->dev = dev; + cxlds->type = type; + cxlds->serial = serial; + cxlds->cxl_dvsec = dvsec; + cxlds->reg_map.host = dev; + cxlds->reg_map.resource = CXL_RESOURCE_NONE; + + if (has_mbox) + cxlds->cxl_mbox.host = dev; + + return cxlds; +} +EXPORT_SYMBOL_NS_GPL(_devm_cxl_dev_state_create, "CXL"); + static struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds, const struct file_operations *fops, const struct cxl_memdev_attach *attach) diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index f96ce884a213..d1f487b3d809 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -696,6 +696,63 @@ bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port) } EXPORT_SYMBOL_NS_GPL(cxl_endpoint_decoder_reset_detected, "CXL"); +static int cxl_rcrb_get_comp_regs(struct pci_dev *pdev, + struct cxl_register_map *map, + struct cxl_dport *dport) +{ + resource_size_t component_reg_phys; + + *map = (struct cxl_register_map) { + .host = &pdev->dev, + .resource = CXL_RESOURCE_NONE, + }; + + component_reg_phys = cxl_rcd_component_reg_phys(&pdev->dev, dport); + if (component_reg_phys == CXL_RESOURCE_NONE) + return -ENXIO; + + map->resource = component_reg_phys; + map->reg_type = CXL_REGLOC_RBI_COMPONENT; + map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE; + + return 0; +} + +int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, + struct cxl_register_map *map) +{ + int rc; + + rc = cxl_find_regblock(pdev, type, map); + + /* + * If the Register Locator DVSEC does not exist, check if it + * is an RCH and try to extract the Component Registers from + * an RCRB. + */ + if (rc && type == CXL_REGLOC_RBI_COMPONENT && is_cxl_restricted(pdev)) { + struct cxl_dport *dport; + struct cxl_port *port __free(put_cxl_port) = + cxl_pci_find_port(pdev, &dport); + if (!port) + return -EPROBE_DEFER; + + rc = cxl_rcrb_get_comp_regs(pdev, map, dport); + if (rc) + return rc; + + rc = cxl_dport_map_rcd_linkcap(pdev, dport); + if (rc) + return rc; + + } else if (rc) { + return rc; + } + + return cxl_setup_regs(map); +} +EXPORT_SYMBOL_NS_GPL(cxl_pci_setup_regs, "CXL"); + int cxl_pci_get_bandwidth(struct pci_dev *pdev, struct access_coordinate *c) { int speed, bw; diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index c37ae0b28bbb..e50dc716d4e8 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -485,22 +485,14 @@ static ssize_t interleave_ways_show(struct device *dev, static const struct attribute_group *get_cxl_region_target_group(void); -static ssize_t interleave_ways_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) +static int set_interleave_ways(struct cxl_region *cxlr, int val) { - struct cxl_region *cxlr = to_cxl_region(dev); struct cxl_root_decoder *cxlrd = cxlr->cxlrd; struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; struct cxl_region_params *p = &cxlr->params; - unsigned int val, save; - int rc; + int save, rc; u8 iw; - rc = kstrtouint(buf, 0, &val); - if (rc) - return rc; - rc = ways_to_eiw(val, &iw); if (rc) return rc; @@ -515,9 +507,7 @@ static ssize_t interleave_ways_store(struct device *dev, return -EINVAL; } - ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); - if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) - return rc; + lockdep_assert_held_write(&cxl_rwsem.region); if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) return -EBUSY; @@ -525,10 +515,31 @@ static ssize_t interleave_ways_store(struct device *dev, save = p->interleave_ways; p->interleave_ways = val; rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group()); - if (rc) { + if (rc) p->interleave_ways = save; + + return rc; +} + +static ssize_t interleave_ways_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + int val; + int rc; + + rc = kstrtoint(buf, 0, &val); + if (rc) + return rc; + + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) + return rc; + + rc = set_interleave_ways(cxlr, val); + if (rc) return rc; - } return len; } @@ -548,21 +559,14 @@ static ssize_t interleave_granularity_show(struct device *dev, return sysfs_emit(buf, "%d\n", p->interleave_granularity); } -static ssize_t interleave_granularity_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) +static int set_interleave_granularity(struct cxl_region *cxlr, int val) { - struct cxl_region *cxlr = to_cxl_region(dev); struct cxl_root_decoder *cxlrd = cxlr->cxlrd; struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; struct cxl_region_params *p = &cxlr->params; - int rc, val; + int rc; u16 ig; - rc = kstrtoint(buf, 0, &val); - if (rc) - return rc; - rc = granularity_to_eig(val, &ig); if (rc) return rc; @@ -578,14 +582,33 @@ static ssize_t interleave_granularity_store(struct device *dev, if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity) return -EINVAL; - ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); - if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) - return rc; + lockdep_assert_held_write(&cxl_rwsem.region); if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) return -EBUSY; p->interleave_granularity = val; + return 0; +} + +static ssize_t interleave_granularity_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + int rc, val; + + rc = kstrtoint(buf, 0, &val); + if (rc) + return rc; + + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) + return rc; + + rc = set_interleave_granularity(cxlr, val); + if (rc) + return rc; return len; } @@ -767,6 +790,22 @@ static ssize_t extended_linear_cache_size_show(struct device *dev, } static DEVICE_ATTR_RO(extended_linear_cache_size); +static ssize_t locked_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cxl_region *cxlr = to_cxl_region(dev); + int rc; + + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region); + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) + return rc; + + rc = test_bit(CXL_REGION_F_LOCK, &cxlr->flags); + return sysfs_emit(buf, "%d\n", rc); +} +static DEVICE_ATTR_RO(locked); + static struct attribute *cxl_region_attrs[] = { &dev_attr_uuid.attr, &dev_attr_commit.attr, @@ -776,6 +815,7 @@ static struct attribute *cxl_region_attrs[] = { &dev_attr_size.attr, &dev_attr_mode.attr, &dev_attr_extended_linear_cache_size.attr, + &dev_attr_locked.attr, NULL, }; @@ -1063,6 +1103,14 @@ static int cxl_rr_ep_add(struct cxl_region_ref *cxl_rr, if (!cxld->region) { cxld->region = cxlr; + + /* + * Now that cxld->region is set the intermediate staging state + * can be cleared. + */ + if (cxld == &cxled->cxld && + cxled->state == CXL_DECODER_STATE_AUTO_STAGED) + cxled->state = CXL_DECODER_STATE_AUTO; get_device(&cxlr->dev); } @@ -1804,6 +1852,7 @@ static int cxl_region_attach_auto(struct cxl_region *cxlr, pos = p->nr_targets; p->targets[pos] = cxled; cxled->pos = pos; + cxled->state = CXL_DECODER_STATE_AUTO_STAGED; p->nr_targets++; return 0; @@ -2153,6 +2202,47 @@ static int cxl_region_attach(struct cxl_region *cxlr, return 0; } +static int cxl_region_by_target(struct device *dev, const void *data) +{ + const struct cxl_endpoint_decoder *cxled = data; + struct cxl_region_params *p; + struct cxl_region *cxlr; + + if (!is_cxl_region(dev)) + return 0; + + cxlr = to_cxl_region(dev); + p = &cxlr->params; + return p->targets[cxled->pos] == cxled; +} + +/* + * When an auto-region fails to assemble the decoder may be listed as a target, + * but not fully attached. + */ +static void cxl_cancel_auto_attach(struct cxl_endpoint_decoder *cxled) +{ + struct cxl_region_params *p; + struct cxl_region *cxlr; + int pos = cxled->pos; + + if (cxled->state != CXL_DECODER_STATE_AUTO_STAGED) + return; + + struct device *dev __free(put_device) = + bus_find_device(&cxl_bus_type, NULL, cxled, cxl_region_by_target); + if (!dev) + return; + + cxlr = to_cxl_region(dev); + p = &cxlr->params; + + p->nr_targets--; + cxled->state = CXL_DECODER_STATE_AUTO; + cxled->pos = -1; + p->targets[pos] = NULL; +} + static struct cxl_region * __cxl_decoder_detach(struct cxl_region *cxlr, struct cxl_endpoint_decoder *cxled, int pos, @@ -2176,8 +2266,10 @@ __cxl_decoder_detach(struct cxl_region *cxlr, cxled = p->targets[pos]; } else { cxlr = cxled->cxld.region; - if (!cxlr) + if (!cxlr) { + cxl_cancel_auto_attach(cxled); return NULL; + } p = &cxlr->params; } @@ -2650,7 +2742,8 @@ static ssize_t create_ram_region_show(struct device *dev, } static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd, - enum cxl_partition_mode mode, int id) + enum cxl_partition_mode mode, int id, + enum cxl_decoder_type target_type) { int rc; @@ -2672,7 +2765,7 @@ static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd, return ERR_PTR(-EBUSY); } - return devm_cxl_add_region(cxlrd, id, mode, CXL_DECODER_HOSTONLYMEM); + return devm_cxl_add_region(cxlrd, id, mode, target_type); } static ssize_t create_region_store(struct device *dev, const char *buf, @@ -2686,7 +2779,7 @@ static ssize_t create_region_store(struct device *dev, const char *buf, if (rc != 1) return -EINVAL; - cxlr = __create_region(cxlrd, mode, id); + cxlr = __create_region(cxlrd, mode, id, CXL_DECODER_HOSTONLYMEM); if (IS_ERR(cxlr)) return PTR_ERR(cxlr); @@ -2757,46 +2850,6 @@ static ssize_t delete_region_store(struct device *dev, } DEVICE_ATTR_WO(delete_region); -static void cxl_pmem_region_release(struct device *dev) -{ - struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev); - int i; - - for (i = 0; i < cxlr_pmem->nr_mappings; i++) { - struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd; - - put_device(&cxlmd->dev); - } - - kfree(cxlr_pmem); -} - -static const struct attribute_group *cxl_pmem_region_attribute_groups[] = { - &cxl_base_attribute_group, - NULL, -}; - -const struct device_type cxl_pmem_region_type = { - .name = "cxl_pmem_region", - .release = cxl_pmem_region_release, - .groups = cxl_pmem_region_attribute_groups, -}; - -bool is_cxl_pmem_region(struct device *dev) -{ - return dev->type == &cxl_pmem_region_type; -} -EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL"); - -struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev) -{ - if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev), - "not a cxl_pmem_region device\n")) - return NULL; - return container_of(dev, struct cxl_pmem_region, dev); -} -EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL"); - struct cxl_poison_context { struct cxl_port *port; int part; @@ -2950,13 +3003,15 @@ static int __cxl_dpa_to_region(struct device *dev, void *arg) struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa) { struct cxl_dpa_to_region_context ctx; - struct cxl_port *port; + struct cxl_port *port = cxlmd->endpoint; + + if (!cxlmd->dev.driver) + return NULL; ctx = (struct cxl_dpa_to_region_context) { .dpa = dpa, }; - port = cxlmd->endpoint; - if (port && is_cxl_endpoint(port) && cxl_num_decoders_committed(port)) + if (cxl_num_decoders_committed(port)) device_for_each_child(&port->dev, &ctx, __cxl_dpa_to_region); return ctx.cxlr; @@ -3450,249 +3505,6 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset, return -ENXIO; } -static struct lock_class_key cxl_pmem_region_key; - -static int cxl_pmem_region_alloc(struct cxl_region *cxlr) -{ - struct cxl_region_params *p = &cxlr->params; - struct cxl_nvdimm_bridge *cxl_nvb; - struct device *dev; - int i; - - guard(rwsem_read)(&cxl_rwsem.region); - if (p->state != CXL_CONFIG_COMMIT) - return -ENXIO; - - struct cxl_pmem_region *cxlr_pmem __free(kfree) = - kzalloc_flex(*cxlr_pmem, mapping, p->nr_targets); - if (!cxlr_pmem) - return -ENOMEM; - - cxlr_pmem->hpa_range.start = p->res->start; - cxlr_pmem->hpa_range.end = p->res->end; - - /* Snapshot the region configuration underneath the cxl_rwsem.region */ - cxlr_pmem->nr_mappings = p->nr_targets; - for (i = 0; i < p->nr_targets; i++) { - struct cxl_endpoint_decoder *cxled = p->targets[i]; - struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); - struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i]; - - /* - * Regions never span CXL root devices, so by definition the - * bridge for one device is the same for all. - */ - if (i == 0) { - cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint); - if (!cxl_nvb) - return -ENODEV; - cxlr->cxl_nvb = cxl_nvb; - } - m->cxlmd = cxlmd; - get_device(&cxlmd->dev); - m->start = cxled->dpa_res->start; - m->size = resource_size(cxled->dpa_res); - m->position = i; - } - - dev = &cxlr_pmem->dev; - device_initialize(dev); - lockdep_set_class(&dev->mutex, &cxl_pmem_region_key); - device_set_pm_not_required(dev); - dev->parent = &cxlr->dev; - dev->bus = &cxl_bus_type; - dev->type = &cxl_pmem_region_type; - cxlr_pmem->cxlr = cxlr; - cxlr->cxlr_pmem = no_free_ptr(cxlr_pmem); - - return 0; -} - -static void cxl_dax_region_release(struct device *dev) -{ - struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev); - - kfree(cxlr_dax); -} - -static const struct attribute_group *cxl_dax_region_attribute_groups[] = { - &cxl_base_attribute_group, - NULL, -}; - -const struct device_type cxl_dax_region_type = { - .name = "cxl_dax_region", - .release = cxl_dax_region_release, - .groups = cxl_dax_region_attribute_groups, -}; - -static bool is_cxl_dax_region(struct device *dev) -{ - return dev->type == &cxl_dax_region_type; -} - -struct cxl_dax_region *to_cxl_dax_region(struct device *dev) -{ - if (dev_WARN_ONCE(dev, !is_cxl_dax_region(dev), - "not a cxl_dax_region device\n")) - return NULL; - return container_of(dev, struct cxl_dax_region, dev); -} -EXPORT_SYMBOL_NS_GPL(to_cxl_dax_region, "CXL"); - -static struct lock_class_key cxl_dax_region_key; - -static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr) -{ - struct cxl_region_params *p = &cxlr->params; - struct cxl_dax_region *cxlr_dax; - struct device *dev; - - guard(rwsem_read)(&cxl_rwsem.region); - if (p->state != CXL_CONFIG_COMMIT) - return ERR_PTR(-ENXIO); - - cxlr_dax = kzalloc_obj(*cxlr_dax); - if (!cxlr_dax) - return ERR_PTR(-ENOMEM); - - cxlr_dax->hpa_range.start = p->res->start; - cxlr_dax->hpa_range.end = p->res->end; - - dev = &cxlr_dax->dev; - cxlr_dax->cxlr = cxlr; - device_initialize(dev); - lockdep_set_class(&dev->mutex, &cxl_dax_region_key); - device_set_pm_not_required(dev); - dev->parent = &cxlr->dev; - dev->bus = &cxl_bus_type; - dev->type = &cxl_dax_region_type; - - return cxlr_dax; -} - -static void cxlr_pmem_unregister(void *_cxlr_pmem) -{ - struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem; - struct cxl_region *cxlr = cxlr_pmem->cxlr; - struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb; - - /* - * Either the bridge is in ->remove() context under the device_lock(), - * or cxlr_release_nvdimm() is cancelling the bridge's release action - * for @cxlr_pmem and doing it itself (while manually holding the bridge - * lock). - */ - device_lock_assert(&cxl_nvb->dev); - cxlr->cxlr_pmem = NULL; - cxlr_pmem->cxlr = NULL; - device_unregister(&cxlr_pmem->dev); -} - -static void cxlr_release_nvdimm(void *_cxlr) -{ - struct cxl_region *cxlr = _cxlr; - struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb; - - scoped_guard(device, &cxl_nvb->dev) { - if (cxlr->cxlr_pmem) - devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister, - cxlr->cxlr_pmem); - } - cxlr->cxl_nvb = NULL; - put_device(&cxl_nvb->dev); -} - -/** - * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge - * @cxlr: parent CXL region for this pmem region bridge device - * - * Return: 0 on success negative error code on failure. - */ -static int devm_cxl_add_pmem_region(struct cxl_region *cxlr) -{ - struct cxl_pmem_region *cxlr_pmem; - struct cxl_nvdimm_bridge *cxl_nvb; - struct device *dev; - int rc; - - rc = cxl_pmem_region_alloc(cxlr); - if (rc) - return rc; - cxlr_pmem = cxlr->cxlr_pmem; - cxl_nvb = cxlr->cxl_nvb; - - dev = &cxlr_pmem->dev; - rc = dev_set_name(dev, "pmem_region%d", cxlr->id); - if (rc) - goto err; - - rc = device_add(dev); - if (rc) - goto err; - - dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), - dev_name(dev)); - - scoped_guard(device, &cxl_nvb->dev) { - if (cxl_nvb->dev.driver) - rc = devm_add_action_or_reset(&cxl_nvb->dev, - cxlr_pmem_unregister, - cxlr_pmem); - else - rc = -ENXIO; - } - - if (rc) - goto err_bridge; - - /* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */ - return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr); - -err: - put_device(dev); -err_bridge: - put_device(&cxl_nvb->dev); - cxlr->cxl_nvb = NULL; - return rc; -} - -static void cxlr_dax_unregister(void *_cxlr_dax) -{ - struct cxl_dax_region *cxlr_dax = _cxlr_dax; - - device_unregister(&cxlr_dax->dev); -} - -static int devm_cxl_add_dax_region(struct cxl_region *cxlr) -{ - struct cxl_dax_region *cxlr_dax; - struct device *dev; - int rc; - - cxlr_dax = cxl_dax_region_alloc(cxlr); - if (IS_ERR(cxlr_dax)) - return PTR_ERR(cxlr_dax); - - dev = &cxlr_dax->dev; - rc = dev_set_name(dev, "dax_region%d", cxlr->id); - if (rc) - goto err; - - rc = device_add(dev); - if (rc) - goto err; - - dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), - dev_name(dev)); - - return devm_add_action_or_reset(&cxlr->dev, cxlr_dax_unregister, - cxlr_dax); -err: - put_device(dev); - return rc; -} - static int match_root_decoder(struct device *dev, const void *data) { const struct range *r1, *r2 = data; @@ -3904,7 +3716,8 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd, do { cxlr = __create_region(cxlrd, cxlds->part[part].mode, - atomic_read(&cxlrd->region_id)); + atomic_read(&cxlrd->region_id), + cxled->cxld.target_type); } while (IS_ERR(cxlr) && PTR_ERR(cxlr) == -EBUSY); if (IS_ERR(cxlr)) { @@ -4175,6 +3988,36 @@ static int cxl_region_setup_poison(struct cxl_region *cxlr) return devm_add_action_or_reset(dev, remove_debugfs, dentry); } +static int region_contains_resource(struct device *dev, const void *data) +{ + const struct resource *res = data; + struct cxl_region *cxlr; + struct cxl_region_params *p; + + if (!is_cxl_region(dev)) + return 0; + + cxlr = to_cxl_region(dev); + p = &cxlr->params; + + if (p->state != CXL_CONFIG_COMMIT) + return 0; + + if (!p->res) + return 0; + + return resource_contains(p->res, res) ? 1 : 0; +} + +bool cxl_region_contains_resource(const struct resource *res) +{ + guard(rwsem_read)(&cxl_rwsem.region); + struct device *dev __free(put_device) = bus_find_device( + &cxl_bus_type, NULL, res, region_contains_resource); + return !!dev; +} +EXPORT_SYMBOL_FOR_MODULES(cxl_region_contains_resource, "dax_hmem"); + static int cxl_region_can_probe(struct cxl_region *cxlr) { struct cxl_region_params *p = &cxlr->params; diff --git a/drivers/cxl/core/region_dax.c b/drivers/cxl/core/region_dax.c new file mode 100644 index 000000000000..de04f78f6ad8 --- /dev/null +++ b/drivers/cxl/core/region_dax.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright(c) 2022 Intel Corporation. All rights reserved. + * Copyright(c) 2026 Meta Technologies Inc. All rights reserved. + */ +#include +#include +#include +#include +#include "core.h" + +static void cxl_dax_region_release(struct device *dev) +{ + struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev); + + kfree(cxlr_dax); +} + +static const struct attribute_group *cxl_dax_region_attribute_groups[] = { + &cxl_base_attribute_group, + NULL +}; + +const struct device_type cxl_dax_region_type = { + .name = "cxl_dax_region", + .release = cxl_dax_region_release, + .groups = cxl_dax_region_attribute_groups, +}; + +static bool is_cxl_dax_region(struct device *dev) +{ + return dev->type == &cxl_dax_region_type; +} + +struct cxl_dax_region *to_cxl_dax_region(struct device *dev) +{ + if (dev_WARN_ONCE(dev, !is_cxl_dax_region(dev), + "not a cxl_dax_region device\n")) + return NULL; + return container_of(dev, struct cxl_dax_region, dev); +} +EXPORT_SYMBOL_NS_GPL(to_cxl_dax_region, "CXL"); + +static struct lock_class_key cxl_dax_region_key; + +static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + struct cxl_dax_region *cxlr_dax; + struct device *dev; + + guard(rwsem_read)(&cxl_rwsem.region); + if (p->state != CXL_CONFIG_COMMIT) + return ERR_PTR(-ENXIO); + + cxlr_dax = kzalloc_obj(*cxlr_dax); + if (!cxlr_dax) + return ERR_PTR(-ENOMEM); + + cxlr_dax->hpa_range.start = p->res->start; + cxlr_dax->hpa_range.end = p->res->end; + + dev = &cxlr_dax->dev; + cxlr_dax->cxlr = cxlr; + device_initialize(dev); + lockdep_set_class(&dev->mutex, &cxl_dax_region_key); + device_set_pm_not_required(dev); + dev->parent = &cxlr->dev; + dev->bus = &cxl_bus_type; + dev->type = &cxl_dax_region_type; + + return cxlr_dax; +} + +static void cxlr_dax_unregister(void *_cxlr_dax) +{ + struct cxl_dax_region *cxlr_dax = _cxlr_dax; + + device_unregister(&cxlr_dax->dev); +} + +int devm_cxl_add_dax_region(struct cxl_region *cxlr) +{ + struct device *dev; + int rc; + + struct cxl_dax_region *cxlr_dax __free(put_cxl_dax_region) = + cxl_dax_region_alloc(cxlr); + if (IS_ERR(cxlr_dax)) + return PTR_ERR(cxlr_dax); + + dev = &cxlr_dax->dev; + rc = dev_set_name(dev, "dax_region%d", cxlr->id); + if (rc) + return rc; + + rc = device_add(dev); + if (rc) + return rc; + + dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), + dev_name(dev)); + + return devm_add_action_or_reset(&cxlr->dev, cxlr_dax_unregister, + no_free_ptr(cxlr_dax)); +} diff --git a/drivers/cxl/core/region_pmem.c b/drivers/cxl/core/region_pmem.c new file mode 100644 index 000000000000..23d97e3d78b6 --- /dev/null +++ b/drivers/cxl/core/region_pmem.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 Intel Corporation. All rights reserved. */ +#include +#include +#include +#include +#include "core.h" + +static void cxl_pmem_region_release(struct device *dev) +{ + struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev); + int i; + + for (i = 0; i < cxlr_pmem->nr_mappings; i++) { + struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd; + + put_device(&cxlmd->dev); + } + + kfree(cxlr_pmem); +} + +static const struct attribute_group *cxl_pmem_region_attribute_groups[] = { + &cxl_base_attribute_group, + NULL +}; + +const struct device_type cxl_pmem_region_type = { + .name = "cxl_pmem_region", + .release = cxl_pmem_region_release, + .groups = cxl_pmem_region_attribute_groups, +}; + +bool is_cxl_pmem_region(struct device *dev) +{ + return dev->type == &cxl_pmem_region_type; +} +EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL"); + +struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev) +{ + if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev), + "not a cxl_pmem_region device\n")) + return NULL; + return container_of(dev, struct cxl_pmem_region, dev); +} +EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL"); + +static struct lock_class_key cxl_pmem_region_key; + +static int cxl_pmem_region_alloc(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + struct cxl_nvdimm_bridge *cxl_nvb; + struct device *dev; + int i; + + guard(rwsem_read)(&cxl_rwsem.region); + if (p->state != CXL_CONFIG_COMMIT) + return -ENXIO; + + struct cxl_pmem_region *cxlr_pmem __free(kfree) = + kzalloc_flex(*cxlr_pmem, mapping, p->nr_targets); + if (!cxlr_pmem) + return -ENOMEM; + + cxlr_pmem->hpa_range.start = p->res->start; + cxlr_pmem->hpa_range.end = p->res->end; + + /* Snapshot the region configuration underneath the cxl_rwsem.region */ + cxlr_pmem->nr_mappings = p->nr_targets; + for (i = 0; i < p->nr_targets; i++) { + struct cxl_endpoint_decoder *cxled = p->targets[i]; + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i]; + + /* + * Regions never span CXL root devices, so by definition the + * bridge for one device is the same for all. + */ + if (i == 0) { + cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint); + if (!cxl_nvb) + return -ENODEV; + cxlr->cxl_nvb = cxl_nvb; + } + m->cxlmd = cxlmd; + get_device(&cxlmd->dev); + m->start = cxled->dpa_res->start; + m->size = resource_size(cxled->dpa_res); + m->position = i; + } + + dev = &cxlr_pmem->dev; + device_initialize(dev); + lockdep_set_class(&dev->mutex, &cxl_pmem_region_key); + device_set_pm_not_required(dev); + dev->parent = &cxlr->dev; + dev->bus = &cxl_bus_type; + dev->type = &cxl_pmem_region_type; + cxlr_pmem->cxlr = cxlr; + cxlr->cxlr_pmem = no_free_ptr(cxlr_pmem); + + return 0; +} + +static void cxlr_pmem_unregister(void *_cxlr_pmem) +{ + struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem; + struct cxl_region *cxlr = cxlr_pmem->cxlr; + struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb; + + /* + * Either the bridge is in ->remove() context under the device_lock(), + * or cxlr_release_nvdimm() is cancelling the bridge's release action + * for @cxlr_pmem and doing it itself (while manually holding the bridge + * lock). + */ + device_lock_assert(&cxl_nvb->dev); + cxlr->cxlr_pmem = NULL; + cxlr_pmem->cxlr = NULL; + device_unregister(&cxlr_pmem->dev); +} + +static void cxlr_release_nvdimm(void *_cxlr) +{ + struct cxl_region *cxlr = _cxlr; + struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb; + + scoped_guard(device, &cxl_nvb->dev) { + if (cxlr->cxlr_pmem) + devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister, + cxlr->cxlr_pmem); + } + cxlr->cxl_nvb = NULL; + put_device(&cxl_nvb->dev); +} + +/** + * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge + * @cxlr: parent CXL region for this pmem region bridge device + * + * Return: 0 on success negative error code on failure. + */ +int devm_cxl_add_pmem_region(struct cxl_region *cxlr) +{ + struct cxl_pmem_region *cxlr_pmem; + struct cxl_nvdimm_bridge *cxl_nvb; + struct device *dev; + int rc; + + rc = cxl_pmem_region_alloc(cxlr); + if (rc) + return rc; + cxlr_pmem = cxlr->cxlr_pmem; + cxl_nvb = cxlr->cxl_nvb; + + dev = &cxlr_pmem->dev; + rc = dev_set_name(dev, "pmem_region%d", cxlr->id); + if (rc) + goto err; + + rc = device_add(dev); + if (rc) + goto err; + + dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), + dev_name(dev)); + + scoped_guard(device, &cxl_nvb->dev) { + if (cxl_nvb->dev.driver) + rc = devm_add_action_or_reset(&cxl_nvb->dev, + cxlr_pmem_unregister, + cxlr_pmem); + else + rc = -ENXIO; + } + + if (rc) + goto err_bridge; + + /* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */ + return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr); + +err: + put_device(dev); +err_bridge: + put_device(&cxl_nvb->dev); + cxlr->cxl_nvb = NULL; + return rc; +} diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c index a010b3214342..93710cf4f0a6 100644 --- a/drivers/cxl/core/regs.c +++ b/drivers/cxl/core/regs.c @@ -641,4 +641,3 @@ resource_size_t cxl_rcd_component_reg_phys(struct device *dev, return CXL_RESOURCE_NONE; return __rcrb_to_component(dev, &dport->rcrb, CXL_RCRB_UPSTREAM); } -EXPORT_SYMBOL_NS_GPL(cxl_rcd_component_reg_phys, "CXL"); diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 9b947286eb9b..1297594beaec 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -12,6 +12,7 @@ #include #include #include +#include extern const struct nvdimm_security_ops *cxl_security_ops; @@ -77,7 +78,16 @@ static inline int cxl_hdm_decoder_count(u32 cap_hdr) { int val = FIELD_GET(CXL_HDM_DECODER_COUNT_MASK, cap_hdr); - return val ? val * 2 : 1; + switch (val) { + case 0: + return 1; + case 1 ... 8: + return val * 2; + case 9 ... 12: + return (val - 4) * 4; + default: + return -ENXIO; + } } /* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */ @@ -201,97 +211,6 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw) #define CXLDEV_MBOX_BG_CMD_COMMAND_VENDOR_MASK GENMASK_ULL(63, 48) #define CXLDEV_MBOX_PAYLOAD_OFFSET 0x20 -/* - * Using struct_group() allows for per register-block-type helper routines, - * without requiring block-type agnostic code to include the prefix. - */ -struct cxl_regs { - /* - * Common set of CXL Component register block base pointers - * @hdm_decoder: CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure - * @ras: CXL 2.0 8.2.5.9 CXL RAS Capability Structure - */ - struct_group_tagged(cxl_component_regs, component, - void __iomem *hdm_decoder; - void __iomem *ras; - ); - /* - * Common set of CXL Device register block base pointers - * @status: CXL 2.0 8.2.8.3 Device Status Registers - * @mbox: CXL 2.0 8.2.8.4 Mailbox Registers - * @memdev: CXL 2.0 8.2.8.5 Memory Device Registers - */ - struct_group_tagged(cxl_device_regs, device_regs, - void __iomem *status, *mbox, *memdev; - ); - - struct_group_tagged(cxl_pmu_regs, pmu_regs, - void __iomem *pmu; - ); - - /* - * RCH downstream port specific RAS register - * @aer: CXL 3.0 8.2.1.1 RCH Downstream Port RCRB - */ - struct_group_tagged(cxl_rch_regs, rch_regs, - void __iomem *dport_aer; - ); - - /* - * RCD upstream port specific PCIe cap register - * @pcie_cap: CXL 3.0 8.2.1.2 RCD Upstream Port RCRB - */ - struct_group_tagged(cxl_rcd_regs, rcd_regs, - void __iomem *rcd_pcie_cap; - ); -}; - -struct cxl_reg_map { - bool valid; - int id; - unsigned long offset; - unsigned long size; -}; - -struct cxl_component_reg_map { - struct cxl_reg_map hdm_decoder; - struct cxl_reg_map ras; -}; - -struct cxl_device_reg_map { - struct cxl_reg_map status; - struct cxl_reg_map mbox; - struct cxl_reg_map memdev; -}; - -struct cxl_pmu_reg_map { - struct cxl_reg_map pmu; -}; - -/** - * struct cxl_register_map - DVSEC harvested register block mapping parameters - * @host: device for devm operations and logging - * @base: virtual base of the register-block-BAR + @block_offset - * @resource: physical resource base of the register block - * @max_size: maximum mapping size to perform register search - * @reg_type: see enum cxl_regloc_type - * @component_map: cxl_reg_map for component registers - * @device_map: cxl_reg_maps for device registers - * @pmu_map: cxl_reg_maps for CXL Performance Monitoring Units - */ -struct cxl_register_map { - struct device *host; - void __iomem *base; - resource_size_t resource; - resource_size_t max_size; - u8 reg_type; - union { - struct cxl_component_reg_map component_map; - struct cxl_device_reg_map device_map; - struct cxl_pmu_reg_map pmu_map; - }; -}; - void cxl_probe_component_regs(struct device *dev, void __iomem *base, struct cxl_component_reg_map *map); void cxl_probe_device_regs(struct device *dev, void __iomem *base, @@ -312,8 +231,6 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type, struct cxl_register_map *map); int cxl_setup_regs(struct cxl_register_map *map); struct cxl_dport; -resource_size_t cxl_rcd_component_reg_phys(struct device *dev, - struct cxl_dport *dport); int cxl_dport_map_rcd_linkcap(struct pci_dev *pdev, struct cxl_dport *dport); #define CXL_RESOURCE_NONE ((resource_size_t) -1) @@ -333,6 +250,7 @@ int cxl_dport_map_rcd_linkcap(struct pci_dev *pdev, struct cxl_dport *dport); #define CXL_DECODER_F_LOCK BIT(4) #define CXL_DECODER_F_ENABLE BIT(5) #define CXL_DECODER_F_NORMALIZED_ADDRESSING BIT(6) +#define CXL_DECODER_F_RESET_MASK (CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK) enum cxl_decoder_type { CXL_DECODER_DEVMEM = 2, @@ -378,12 +296,14 @@ struct cxl_decoder { }; /* - * Track whether this decoder is reserved for region autodiscovery, or - * free for userspace provisioning. + * Track whether this decoder is free for userspace provisioning, reserved for + * region autodiscovery, whether it is started connecting (awaiting other + * peers), or has completed auto assembly. */ enum cxl_decoder_state { CXL_DECODER_STATE_MANUAL, CXL_DECODER_STATE_AUTO, + CXL_DECODER_STATE_AUTO_STAGED, }; /** @@ -497,11 +417,6 @@ struct cxl_region_params { resource_size_t cache_size; }; -enum cxl_partition_mode { - CXL_PARTMODE_RAM, - CXL_PARTMODE_PMEM, -}; - /* * Indicate whether this region has been assembled by autodetection or * userspace assembly. Prevent endpoint decoders outside of automatic @@ -808,6 +723,7 @@ DEFINE_FREE(put_cxl_root, struct cxl_root *, if (_T) put_device(&_T->port.dev)) DEFINE_FREE(put_cxl_port, struct cxl_port *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev)) DEFINE_FREE(put_cxl_root_decoder, struct cxl_root_decoder *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->cxlsd.cxld.dev)) DEFINE_FREE(put_cxl_region, struct cxl_region *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev)) +DEFINE_FREE(put_cxl_dax_region, struct cxl_dax_region *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev)) int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd); void cxl_bus_rescan(void); @@ -939,6 +855,7 @@ struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev); int cxl_add_to_region(struct cxl_endpoint_decoder *cxled); struct cxl_dax_region *to_cxl_dax_region(struct device *dev); u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa); +bool cxl_region_contains_resource(const struct resource *res); #else static inline bool is_cxl_pmem_region(struct device *dev) { @@ -961,6 +878,10 @@ static inline u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, { return 0; } +static inline bool cxl_region_contains_resource(const struct resource *res) +{ + return false; +} #endif void cxl_endpoint_parse_cdat(struct cxl_port *port); diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index e21d744d639b..776c50d1db51 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -113,8 +113,6 @@ int devm_cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled, resource_size_t base, resource_size_t len, resource_size_t skipped); -#define CXL_NR_PARTITIONS_MAX 2 - struct cxl_dpa_info { u64 size; struct cxl_dpa_part_info { @@ -373,87 +371,6 @@ struct cxl_security_state { struct kernfs_node *sanitize_node; }; -/* - * enum cxl_devtype - delineate type-2 from a generic type-3 device - * @CXL_DEVTYPE_DEVMEM - Vendor specific CXL Type-2 device implementing HDM-D or - * HDM-DB, no requirement that this device implements a - * mailbox, or other memory-device-standard manageability - * flows. - * @CXL_DEVTYPE_CLASSMEM - Common class definition of a CXL Type-3 device with - * HDM-H and class-mandatory memory device registers - */ -enum cxl_devtype { - CXL_DEVTYPE_DEVMEM, - CXL_DEVTYPE_CLASSMEM, -}; - -/** - * struct cxl_dpa_perf - DPA performance property entry - * @dpa_range: range for DPA address - * @coord: QoS performance data (i.e. latency, bandwidth) - * @cdat_coord: raw QoS performance data from CDAT - * @qos_class: QoS Class cookies - */ -struct cxl_dpa_perf { - struct range dpa_range; - struct access_coordinate coord[ACCESS_COORDINATE_MAX]; - struct access_coordinate cdat_coord[ACCESS_COORDINATE_MAX]; - int qos_class; -}; - -/** - * struct cxl_dpa_partition - DPA partition descriptor - * @res: shortcut to the partition in the DPA resource tree (cxlds->dpa_res) - * @perf: performance attributes of the partition from CDAT - * @mode: operation mode for the DPA capacity, e.g. ram, pmem, dynamic... - */ -struct cxl_dpa_partition { - struct resource res; - struct cxl_dpa_perf perf; - enum cxl_partition_mode mode; -}; - -/** - * struct cxl_dev_state - The driver device state - * - * cxl_dev_state represents the CXL driver/device state. It provides an - * interface to mailbox commands as well as some cached data about the device. - * Currently only memory devices are represented. - * - * @dev: The device associated with this CXL state - * @cxlmd: The device representing the CXL.mem capabilities of @dev - * @reg_map: component and ras register mapping parameters - * @regs: Class device "Device" registers - * @cxl_dvsec: Offset to the PCIe device DVSEC - * @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH) - * @media_ready: Indicate whether the device media is usable - * @dpa_res: Overall DPA resource tree for the device - * @part: DPA partition array - * @nr_partitions: Number of DPA partitions - * @serial: PCIe Device Serial Number - * @type: Generic Memory Class device or Vendor Specific Memory device - * @cxl_mbox: CXL mailbox context - * @cxlfs: CXL features context - */ -struct cxl_dev_state { - struct device *dev; - struct cxl_memdev *cxlmd; - struct cxl_register_map reg_map; - struct cxl_device_regs regs; - int cxl_dvsec; - bool rcd; - bool media_ready; - struct resource dpa_res; - struct cxl_dpa_partition part[CXL_NR_PARTITIONS_MAX]; - unsigned int nr_partitions; - u64 serial; - enum cxl_devtype type; - struct cxl_mailbox cxl_mbox; -#ifdef CONFIG_CXL_FEATURES - struct cxl_features_state *cxlfs; -#endif -}; - static inline resource_size_t cxl_pmem_size(struct cxl_dev_state *cxlds) { /* @@ -858,13 +775,14 @@ int cxl_dev_state_identify(struct cxl_memdev_state *mds); int cxl_await_media_ready(struct cxl_dev_state *cxlds); int cxl_enumerate_cmds(struct cxl_memdev_state *mds); int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info); -struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev); +struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial, + u16 dvsec); void set_exclusive_cxl_commands(struct cxl_memdev_state *mds, unsigned long *cmds); void clear_exclusive_cxl_commands(struct cxl_memdev_state *mds, unsigned long *cmds); void cxl_mem_get_event_records(struct cxl_memdev_state *mds, u32 status); -void cxl_event_trace_record(const struct cxl_memdev *cxlmd, +void cxl_event_trace_record(struct cxl_memdev *cxlmd, enum cxl_event_log_type type, enum cxl_event_type event_type, const uuid_t *uuid, union cxl_event *evt); @@ -923,7 +841,7 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd); */ struct cxl_hdm { struct cxl_component_regs regs; - unsigned int decoder_count; + int decoder_count; unsigned int target_count; unsigned int interleave_mask; unsigned long iw_cap_mask; diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h index 0cf64218aa16..b826eb53cf7b 100644 --- a/drivers/cxl/cxlpci.h +++ b/drivers/cxl/cxlpci.h @@ -74,6 +74,17 @@ static inline bool cxl_pci_flit_256(struct pci_dev *pdev) return lnksta2 & PCI_EXP_LNKSTA2_FLIT; } +/* + * Assume that the caller has already validated that @pdev has CXL + * capabilities, any RCiEP with CXL capabilities is treated as a + * Restricted CXL Device (RCD) and finds upstream port and endpoint + * registers in a Root Complex Register Block (RCRB). + */ +static inline bool is_cxl_restricted(struct pci_dev *pdev) +{ + return pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END; +} + struct cxl_dev_state; void read_cdat_data(struct cxl_port *port); @@ -101,4 +112,6 @@ static inline void devm_cxl_port_ras_setup(struct cxl_port *port) } #endif +int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, + struct cxl_register_map *map); #endif /* __CXL_PCI_H__ */ diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index fbb300a01830..bace662dc988 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -465,76 +465,6 @@ static int cxl_pci_setup_mailbox(struct cxl_memdev_state *mds, bool irq_avail) return 0; } -/* - * Assume that any RCIEP that emits the CXL memory expander class code - * is an RCD - */ -static bool is_cxl_restricted(struct pci_dev *pdev) -{ - return pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END; -} - -static int cxl_rcrb_get_comp_regs(struct pci_dev *pdev, - struct cxl_register_map *map, - struct cxl_dport *dport) -{ - resource_size_t component_reg_phys; - - *map = (struct cxl_register_map) { - .host = &pdev->dev, - .resource = CXL_RESOURCE_NONE, - }; - - struct cxl_port *port __free(put_cxl_port) = - cxl_pci_find_port(pdev, &dport); - if (!port) - return -EPROBE_DEFER; - - component_reg_phys = cxl_rcd_component_reg_phys(&pdev->dev, dport); - if (component_reg_phys == CXL_RESOURCE_NONE) - return -ENXIO; - - map->resource = component_reg_phys; - map->reg_type = CXL_REGLOC_RBI_COMPONENT; - map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE; - - return 0; -} - -static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, - struct cxl_register_map *map) -{ - int rc; - - rc = cxl_find_regblock(pdev, type, map); - - /* - * If the Register Locator DVSEC does not exist, check if it - * is an RCH and try to extract the Component Registers from - * an RCRB. - */ - if (rc && type == CXL_REGLOC_RBI_COMPONENT && is_cxl_restricted(pdev)) { - struct cxl_dport *dport; - struct cxl_port *port __free(put_cxl_port) = - cxl_pci_find_port(pdev, &dport); - if (!port) - return -EPROBE_DEFER; - - rc = cxl_rcrb_get_comp_regs(pdev, map, dport); - if (rc) - return rc; - - rc = cxl_dport_map_rcd_linkcap(pdev, dport); - if (rc) - return rc; - - } else if (rc) { - return rc; - } - - return cxl_setup_regs(map); -} - static void free_event_buf(void *buf) { kvfree(buf); @@ -865,25 +795,25 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) int rc, pmu_count; unsigned int i; bool irq_avail; + u16 dvsec; rc = pcim_enable_device(pdev); if (rc) return rc; pci_set_master(pdev); - mds = cxl_memdev_state_create(&pdev->dev); + dvsec = pci_find_dvsec_capability(pdev, PCI_VENDOR_ID_CXL, + PCI_DVSEC_CXL_DEVICE); + if (!dvsec) + pci_warn(pdev, "Device DVSEC not present, skip CXL.mem init\n"); + + mds = cxl_memdev_state_create(&pdev->dev, pci_get_dsn(pdev), dvsec); if (IS_ERR(mds)) return PTR_ERR(mds); cxlds = &mds->cxlds; pci_set_drvdata(pdev, cxlds); cxlds->rcd = is_cxl_restricted(pdev); - cxlds->serial = pci_get_dsn(pdev); - cxlds->cxl_dvsec = pci_find_dvsec_capability( - pdev, PCI_VENDOR_ID_CXL, PCI_DVSEC_CXL_DEVICE); - if (!cxlds->cxl_dvsec) - dev_warn(&pdev->dev, - "Device DVSEC not present, skip CXL.mem init\n"); rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map); if (rc) @@ -1030,6 +960,19 @@ static void cxl_error_resume(struct pci_dev *pdev) dev->driver ? "successful" : "failed"); } +static int cxl_endpoint_decoder_clear_reset_flags(struct device *dev, void *data) +{ + struct cxl_endpoint_decoder *cxled; + + if (!is_endpoint_decoder(dev)) + return 0; + + cxled = to_cxl_endpoint_decoder(dev); + cxled->cxld.flags &= ~CXL_DECODER_F_RESET_MASK; + + return 0; +} + static void cxl_reset_done(struct pci_dev *pdev) { struct cxl_dev_state *cxlds = pci_get_drvdata(pdev); @@ -1043,8 +986,14 @@ static void cxl_reset_done(struct pci_dev *pdev) * that no longer exists. */ guard(device)(&cxlmd->dev); + if (!cxlmd->dev.driver) + return; + if (cxlmd->endpoint && cxl_endpoint_decoder_reset_detected(cxlmd->endpoint)) { + device_for_each_child(&cxlmd->endpoint->dev, NULL, + cxl_endpoint_decoder_clear_reset_flags); + dev_crit(dev, "SBR happened without memory regions removal.\n"); dev_crit(dev, "System may be unstable if regions hosted system memory.\n"); add_taint(TAINT_USER, LOCKDEP_STILL_OK); diff --git a/drivers/dax/Kconfig b/drivers/dax/Kconfig index d656e4c0eb84..602f9a0839a9 100644 --- a/drivers/dax/Kconfig +++ b/drivers/dax/Kconfig @@ -32,6 +32,9 @@ config DEV_DAX_HMEM depends on EFI_SOFT_RESERVE select NUMA_KEEP_MEMINFO if NUMA_MEMBLKS default DEV_DAX + depends on CXL_ACPI || !CXL_ACPI + depends on CXL_PCI || !CXL_PCI + depends on CXL_BUS || !CXL_BUS help EFI 2.8 platforms, and others, may advertise 'specific purpose' memory. For example, a high bandwidth memory pool. The @@ -48,6 +51,7 @@ config DEV_DAX_CXL tristate "CXL DAX: direct access to CXL RAM regions" depends on CXL_BUS && CXL_REGION && DEV_DAX default CXL_REGION && DEV_DAX + depends on DEV_DAX_HMEM || !DEV_DAX_HMEM help CXL RAM regions are either mapped by platform-firmware and published in the initial system-memory map as "System RAM", mapped @@ -61,6 +65,11 @@ config DEV_DAX_HMEM_DEVICES depends on DEV_DAX_HMEM && DAX def_bool y +config DEV_DAX_FSDEV + tristate + depends on DEV_DAX && FS_DAX + default DEV_DAX + config DEV_DAX_KMEM tristate "KMEM DAX: map dax-devices as System-RAM" default DEV_DAX diff --git a/drivers/dax/Makefile b/drivers/dax/Makefile index 5ed5c39857c8..c0bc6eb1ea20 100644 --- a/drivers/dax/Makefile +++ b/drivers/dax/Makefile @@ -1,14 +1,15 @@ # SPDX-License-Identifier: GPL-2.0 +obj-y += hmem/ obj-$(CONFIG_DAX) += dax.o obj-$(CONFIG_DEV_DAX) += device_dax.o obj-$(CONFIG_DEV_DAX_KMEM) += kmem.o obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o obj-$(CONFIG_DEV_DAX_CXL) += dax_cxl.o +obj-$(CONFIG_DEV_DAX_FSDEV) += fsdev_dax.o dax-y := super.o dax-y += bus.o device_dax-y := device.o dax_pmem-y := pmem.o dax_cxl-y := cxl.o - -obj-y += hmem/ +fsdev_dax-y := fsdev.o diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index c94c09622516..ccfe65004888 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -10,6 +10,7 @@ #include "dax-private.h" #include "bus.h" +static struct resource dax_regions = DEFINE_RES_MEM_NAMED(0, -1, "DAX Regions"); static DEFINE_MUTEX(dax_bus_lock); /* @@ -39,8 +40,6 @@ static int dax_bus_uevent(const struct device *dev, struct kobj_uevent_env *env) return add_uevent_var(env, "MODALIAS=" DAX_DEVICE_MODALIAS_FMT, 0); } -#define to_dax_drv(__drv) container_of_const(__drv, struct dax_device_driver, drv) - static struct dax_id *__dax_match_id(const struct dax_device_driver *dax_drv, const char *dev_name) { @@ -627,6 +626,7 @@ static void dax_region_unregister(void *region) sysfs_remove_groups(&dax_region->dev->kobj, dax_region_attribute_groups); + release_resource(&dax_region->res); dax_region_put(dax_region); } @@ -635,6 +635,7 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id, unsigned long flags) { struct dax_region *dax_region; + int rc; /* * The DAX core assumes that it can store its private data in @@ -667,14 +668,25 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id, .flags = IORESOURCE_MEM | flags, }; - if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) { - kfree(dax_region); - return NULL; + rc = request_resource(&dax_regions, &dax_region->res); + if (rc) { + dev_dbg(parent, "dax_region resource conflict for %pR\n", + &dax_region->res); + goto err_res; } + if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) + goto err_sysfs; + if (devm_add_action_or_reset(parent, dax_region_unregister, dax_region)) return NULL; return dax_region; + +err_sysfs: + release_resource(&dax_region->res); +err_res: + dax_region_put(dax_region); + return NULL; } EXPORT_SYMBOL_GPL(alloc_dax_region); @@ -1417,6 +1429,26 @@ static const struct device_type dev_dax_type = { .groups = dax_attribute_groups, }; +/* see "strong" declaration in tools/testing/nvdimm/dax-dev.c */ +__weak phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff, + unsigned long size) +{ + for (int i = 0; i < dev_dax->nr_range; i++) { + struct dev_dax_range *dax_range = &dev_dax->ranges[i]; + struct range *range = &dax_range->range; + phys_addr_t phys; + + if (!in_range(pgoff, dax_range->pgoff, PHYS_PFN(range_len(range)))) + continue; + phys = PFN_PHYS(pgoff - dax_range->pgoff) + range->start; + if (phys + size - 1 <= range->end) + return phys; + break; + } + return -1; +} +EXPORT_SYMBOL_GPL(dax_pgoff_to_phys); + static struct dev_dax *__devm_create_dev_dax(struct dev_dax_data *data) { struct dax_region *dax_region = data->dax_region; diff --git a/drivers/dax/bus.h b/drivers/dax/bus.h index cbbf64443098..5909171a4428 100644 --- a/drivers/dax/bus.h +++ b/drivers/dax/bus.h @@ -3,7 +3,9 @@ #ifndef __DAX_BUS_H__ #define __DAX_BUS_H__ #include +#include #include +#include struct dev_dax; struct resource; @@ -31,6 +33,7 @@ struct dev_dax *devm_create_dev_dax(struct dev_dax_data *data); enum dax_driver_type { DAXDRV_KMEM_TYPE, DAXDRV_DEVICE_TYPE, + DAXDRV_FSDEV_TYPE, }; struct dax_device_driver { @@ -41,6 +44,8 @@ struct dax_device_driver { void (*remove)(struct dev_dax *dev); }; +#define to_dax_drv(__drv) container_of_const(__drv, struct dax_device_driver, drv) + int __dax_driver_register(struct dax_device_driver *dax_drv, struct module *module, const char *mod_name); #define dax_driver_register(driver) \ @@ -49,6 +54,24 @@ void dax_driver_unregister(struct dax_device_driver *dax_drv); void kill_dev_dax(struct dev_dax *dev_dax); bool static_dev_dax(struct dev_dax *dev_dax); +struct hmem_platform_device { + struct platform_device pdev; + struct work_struct work; + bool did_probe; +}; + +static inline struct hmem_platform_device * +to_hmem_platform_device(struct platform_device *pdev) +{ + return container_of(pdev, struct hmem_platform_device, pdev); +} + +#if IS_ENABLED(CONFIG_DEV_DAX_HMEM) +void dax_hmem_flush_work(void); +#else +static inline void dax_hmem_flush_work(void) { } +#endif + #define MODULE_ALIAS_DAX_DEVICE(type) \ MODULE_ALIAS("dax:t" __stringify(type) "*") #define DAX_DEVICE_MODALIAS_FMT "dax:t%d" diff --git a/drivers/dax/cxl.c b/drivers/dax/cxl.c index 13cd94d32ff7..3ab39b77843d 100644 --- a/drivers/dax/cxl.c +++ b/drivers/dax/cxl.c @@ -38,10 +38,36 @@ static struct cxl_driver cxl_dax_region_driver = { .id = CXL_DEVICE_DAX_REGION, .drv = { .suppress_bind_attrs = true, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; -module_cxl_driver(cxl_dax_region_driver); +static void cxl_dax_region_driver_register(struct work_struct *work) +{ + dax_hmem_flush_work(); + cxl_driver_register(&cxl_dax_region_driver); +} + +static DECLARE_WORK(cxl_dax_region_driver_work, cxl_dax_region_driver_register); + +static int __init cxl_dax_region_init(void) +{ + /* + * Need to resolve a race with dax_hmem wanting to drive regions + * instead of CXL + */ + queue_work(system_long_wq, &cxl_dax_region_driver_work); + return 0; +} +module_init(cxl_dax_region_init); + +static void __exit cxl_dax_region_exit(void) +{ + flush_work(&cxl_dax_region_driver_work); + cxl_driver_unregister(&cxl_dax_region_driver); +} +module_exit(cxl_dax_region_exit); + MODULE_ALIAS_CXL(CXL_DEVICE_DAX_REGION); MODULE_DESCRIPTION("CXL DAX: direct access to CXL regions"); MODULE_LICENSE("GPL"); diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index c6ae27c982f4..81e4af49e39c 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -69,6 +69,8 @@ struct dev_dax_range { * data while the device is activated in the driver. * @region: parent region * @dax_dev: core dax functionality + * @virt_addr: kva from memremap; used by fsdev_dax + * @cached_size: size of daxdev cached by fsdev_dax * @align: alignment of this instance * @target_node: effective numa node if dev_dax memory range is onlined * @dyn_id: is this a dynamic or statically created instance @@ -83,6 +85,8 @@ struct dev_dax_range { struct dev_dax { struct dax_region *region; struct dax_device *dax_dev; + void *virt_addr; + u64 cached_size; unsigned int align; int target_node; bool dyn_id; diff --git a/drivers/dax/device.c b/drivers/dax/device.c index 381021c2e031..d0c9b4e03b47 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -57,29 +57,6 @@ static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma, vma->vm_file, func); } -/* see "strong" declaration in tools/testing/nvdimm/dax-dev.c */ -__weak phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff, - unsigned long size) -{ - int i; - - for (i = 0; i < dev_dax->nr_range; i++) { - struct dev_dax_range *dax_range = &dev_dax->ranges[i]; - struct range *range = &dax_range->range; - unsigned long long pgoff_end; - phys_addr_t phys; - - pgoff_end = dax_range->pgoff + PHYS_PFN(range_len(range)) - 1; - if (pgoff < dax_range->pgoff || pgoff > pgoff_end) - continue; - phys = PFN_PHYS(pgoff - dax_range->pgoff) + range->start; - if (phys + size - 1 <= range->end) - return phys; - break; - } - return -1; -} - static void dax_set_mapping(struct vm_fault *vmf, unsigned long pfn, unsigned long fault_size) { diff --git a/drivers/dax/fsdev.c b/drivers/dax/fsdev.c new file mode 100644 index 000000000000..188b2526bee4 --- /dev/null +++ b/drivers/dax/fsdev.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2026 Micron Technology, Inc. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dax-private.h" +#include "bus.h" + +/* + * FS-DAX compatible devdax driver + * + * Unlike drivers/dax/device.c which pre-initializes compound folios based + * on device alignment (via vmemmap_shift), this driver leaves folios + * uninitialized similar to pmem. This allows fs-dax filesystems like famfs + * to work without needing special handling for pre-initialized folios. + * + * Key differences from device.c: + * - pgmap type is MEMORY_DEVICE_FS_DAX (not MEMORY_DEVICE_GENERIC) + * - vmemmap_shift is NOT set (folios remain order-0) + * - fs-dax can dynamically create compound folios as needed + * - No mmap support - all access is through fs-dax/iomap + */ + +static void fsdev_write_dax(void *addr, struct page *page, + unsigned int off, unsigned int len) +{ + while (len) { + void *mem = kmap_local_page(page); + unsigned int chunk = min_t(unsigned int, len, PAGE_SIZE - off); + + memcpy_flushcache(addr, mem + off, chunk); + kunmap_local(mem); + len -= chunk; + off = 0; + page++; + addr += chunk; + } +} + +static long __fsdev_dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff, + long nr_pages, enum dax_access_mode mode, void **kaddr, + unsigned long *pfn) +{ + struct dev_dax *dev_dax = dax_get_private(dax_dev); + size_t size = nr_pages << PAGE_SHIFT; + size_t offset = pgoff << PAGE_SHIFT; + void *virt_addr = dev_dax->virt_addr + offset; + phys_addr_t phys; + unsigned long local_pfn; + + phys = dax_pgoff_to_phys(dev_dax, pgoff, size); + if (phys == -1) { + dev_dbg(&dev_dax->dev, + "pgoff (%#lx) out of range\n", pgoff); + return -EFAULT; + } + + if (kaddr) + *kaddr = virt_addr; + + local_pfn = PHYS_PFN(phys); + if (pfn) + *pfn = local_pfn; + + /* + * Use cached_size which was computed at probe time. The size cannot + * change while the driver is bound (resize returns -EBUSY). + */ + return PHYS_PFN(min(size, dev_dax->cached_size - offset)); +} + +static int fsdev_dax_zero_page_range(struct dax_device *dax_dev, + pgoff_t pgoff, size_t nr_pages) +{ + void *kaddr; + long rc; + + WARN_ONCE(nr_pages > 1, "%s: nr_pages > 1\n", __func__); + rc = __fsdev_dax_direct_access(dax_dev, pgoff, 1, DAX_ACCESS, &kaddr, NULL); + if (rc < 0) + return rc; + fsdev_write_dax(kaddr, ZERO_PAGE(0), 0, PAGE_SIZE); + return 0; +} + +static long fsdev_dax_direct_access(struct dax_device *dax_dev, + pgoff_t pgoff, long nr_pages, enum dax_access_mode mode, + void **kaddr, unsigned long *pfn) +{ + return __fsdev_dax_direct_access(dax_dev, pgoff, nr_pages, mode, + kaddr, pfn); +} + +static size_t fsdev_dax_recovery_write(struct dax_device *dax_dev, pgoff_t pgoff, + void *addr, size_t bytes, struct iov_iter *i) +{ + return _copy_from_iter_flushcache(addr, bytes, i); +} + +static const struct dax_operations dev_dax_ops = { + .direct_access = fsdev_dax_direct_access, + .zero_page_range = fsdev_dax_zero_page_range, + .recovery_write = fsdev_dax_recovery_write, +}; + +static void fsdev_cdev_del(void *cdev) +{ + cdev_del(cdev); +} + +static void fsdev_kill(void *dev_dax) +{ + kill_dev_dax(dev_dax); +} + +static void fsdev_clear_ops(void *data) +{ + struct dev_dax *dev_dax = data; + + dax_set_ops(dev_dax->dax_dev, NULL); +} + +/* + * Page map operations for FS-DAX mode + * Similar to fsdax_pagemap_ops in drivers/nvdimm/pmem.c + * + * Note: folio_free callback is not needed for MEMORY_DEVICE_FS_DAX. + * The core mm code in free_zone_device_folio() handles the wake_up_var() + * directly for this memory type. + */ +static int fsdev_pagemap_memory_failure(struct dev_pagemap *pgmap, + unsigned long pfn, unsigned long nr_pages, int mf_flags) +{ + struct dev_dax *dev_dax = pgmap->owner; + u64 offset = PFN_PHYS(pfn) - dev_dax->ranges[0].range.start; + u64 len = nr_pages << PAGE_SHIFT; + + return dax_holder_notify_failure(dev_dax->dax_dev, offset, + len, mf_flags); +} + +static const struct dev_pagemap_ops fsdev_pagemap_ops = { + .memory_failure = fsdev_pagemap_memory_failure, +}; + +/* + * Clear any stale folio state from pages in the given range. + * This is necessary because device_dax pre-initializes compound folios + * based on vmemmap_shift, and that state may persist after driver unbind. + * Since fsdev_dax uses MEMORY_DEVICE_FS_DAX without vmemmap_shift, fs-dax + * expects to find clean order-0 folios that it can build into compound + * folios on demand. + * + * At probe time, no filesystem should be mounted yet, so all mappings + * are stale and must be cleared along with compound state. + */ +static void fsdev_clear_folio_state(struct dev_dax *dev_dax) +{ + for (int i = 0; i < dev_dax->nr_range; i++) { + struct range *range = &dev_dax->ranges[i].range; + unsigned long pfn = PHYS_PFN(range->start); + unsigned long end_pfn = PHYS_PFN(range->end) + 1; + + while (pfn < end_pfn) { + struct folio *folio = pfn_folio(pfn); + int order = dax_folio_reset_order(folio); + + pfn += 1UL << order; + } + } +} + +static void fsdev_clear_folio_state_action(void *data) +{ + fsdev_clear_folio_state(data); +} + +static int fsdev_open(struct inode *inode, struct file *filp) +{ + struct dax_device *dax_dev = inode_dax(inode); + struct dev_dax *dev_dax = dax_get_private(dax_dev); + + filp->private_data = dev_dax; + + return 0; +} + +static int fsdev_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static const struct file_operations fsdev_fops = { + .llseek = noop_llseek, + .owner = THIS_MODULE, + .open = fsdev_open, + .release = fsdev_release, +}; + +static int fsdev_dax_probe(struct dev_dax *dev_dax) +{ + struct dax_device *dax_dev = dev_dax->dax_dev; + struct device *dev = &dev_dax->dev; + struct dev_pagemap *pgmap; + struct inode *inode; + u64 data_offset = 0; + struct cdev *cdev; + void *addr; + int rc, i; + + if (static_dev_dax(dev_dax)) { + if (dev_dax->nr_range > 1) { + dev_warn(dev, "static pgmap / multi-range device conflict\n"); + return -EINVAL; + } + + pgmap = dev_dax->pgmap; + } else { + size_t pgmap_size; + + if (dev_dax->pgmap) { + dev_warn(dev, "dynamic-dax with pre-populated page map\n"); + return -EINVAL; + } + + pgmap_size = struct_size(pgmap, ranges, dev_dax->nr_range - 1); + pgmap = devm_kzalloc(dev, pgmap_size, GFP_KERNEL); + if (!pgmap) + return -ENOMEM; + + pgmap->nr_range = dev_dax->nr_range; + dev_dax->pgmap = pgmap; + + for (i = 0; i < dev_dax->nr_range; i++) { + struct range *range = &dev_dax->ranges[i].range; + + pgmap->ranges[i] = *range; + } + } + + for (i = 0; i < dev_dax->nr_range; i++) { + struct range *range = &dev_dax->ranges[i].range; + + if (!devm_request_mem_region(dev, range->start, + range_len(range), dev_name(dev))) { + dev_warn(dev, "mapping%d: %#llx-%#llx could not reserve range\n", + i, range->start, range->end); + return -EBUSY; + } + } + + /* Cache size now; it cannot change while driver is bound */ + dev_dax->cached_size = 0; + for (i = 0; i < dev_dax->nr_range; i++) + dev_dax->cached_size += range_len(&dev_dax->ranges[i].range); + + /* + * Use MEMORY_DEVICE_FS_DAX without setting vmemmap_shift, leaving + * folios at order-0. Unlike device.c (MEMORY_DEVICE_GENERIC), this + * lets fs-dax dynamically build compound folios as needed, similar + * to pmem behavior. + */ + pgmap->type = MEMORY_DEVICE_FS_DAX; + pgmap->ops = &fsdev_pagemap_ops; + pgmap->owner = dev_dax; + + addr = devm_memremap_pages(dev, pgmap); + if (IS_ERR(addr)) + return PTR_ERR(addr); + + /* + * Clear any stale compound folio state left over from a previous + * driver (e.g., device_dax with vmemmap_shift). Also register this + * as a devm action so folio state is cleared on unbind, ensuring + * clean pages for subsequent drivers (e.g., kmem for system-ram). + */ + fsdev_clear_folio_state(dev_dax); + rc = devm_add_action_or_reset(dev, fsdev_clear_folio_state_action, + dev_dax); + if (rc) + return rc; + + /* Detect whether the data is at a non-zero offset into the memory */ + if (pgmap->range.start != dev_dax->ranges[0].range.start) { + u64 phys = dev_dax->ranges[0].range.start; + u64 pgmap_phys = dev_dax->pgmap[0].range.start; + + if (!WARN_ON(pgmap_phys > phys)) + data_offset = phys - pgmap_phys; + + pr_debug("%s: offset detected phys=%llx pgmap_phys=%llx offset=%llx\n", + __func__, phys, pgmap_phys, data_offset); + } + dev_dax->virt_addr = addr + data_offset; + + inode = dax_inode(dax_dev); + cdev = inode->i_cdev; + cdev_init(cdev, &fsdev_fops); + cdev->owner = dev->driver->owner; + cdev_set_parent(cdev, &dev->kobj); + rc = cdev_add(cdev, dev->devt, 1); + if (rc) + return rc; + + rc = devm_add_action_or_reset(dev, fsdev_cdev_del, cdev); + if (rc) + return rc; + + /* Set the dax operations for fs-dax access path */ + rc = dax_set_ops(dax_dev, &dev_dax_ops); + if (rc) + return rc; + + rc = devm_add_action_or_reset(dev, fsdev_clear_ops, dev_dax); + if (rc) + return rc; + + run_dax(dax_dev); + return devm_add_action_or_reset(dev, fsdev_kill, dev_dax); +} + +static struct dax_device_driver fsdev_dax_driver = { + .probe = fsdev_dax_probe, + .type = DAXDRV_FSDEV_TYPE, +}; + +static int __init dax_init(void) +{ + return dax_driver_register(&fsdev_dax_driver); +} + +static void __exit dax_exit(void) +{ + dax_driver_unregister(&fsdev_dax_driver); +} + +MODULE_AUTHOR("John Groves"); +MODULE_DESCRIPTION("FS-DAX Device: fs-dax compatible devdax driver"); +MODULE_LICENSE("GPL"); +module_init(dax_init); +module_exit(dax_exit); +MODULE_ALIAS_DAX_DEVICE(0); diff --git a/drivers/dax/hmem/device.c b/drivers/dax/hmem/device.c index 56e3cbd181b5..d70359b4307b 100644 --- a/drivers/dax/hmem/device.c +++ b/drivers/dax/hmem/device.c @@ -4,6 +4,7 @@ #include #include #include +#include "../bus.h" static bool nohmem; module_param_named(disable, nohmem, bool, 0444); @@ -33,9 +34,21 @@ int walk_hmem_resources(struct device *host, walk_hmem_fn fn) } EXPORT_SYMBOL_GPL(walk_hmem_resources); +static void hmem_work(struct work_struct *work) +{ + /* place holder until dax_hmem driver attaches */ +} + +static struct hmem_platform_device hmem_platform = { + .pdev = { + .name = "hmem_platform", + .id = 0, + }, + .work = __WORK_INITIALIZER(hmem_platform.work, hmem_work), +}; + static void __hmem_register_resource(int target_nid, struct resource *res) { - struct platform_device *pdev; struct resource *new; int rc; @@ -51,17 +64,13 @@ static void __hmem_register_resource(int target_nid, struct resource *res) if (platform_initialized) return; - pdev = platform_device_alloc("hmem_platform", 0); - if (!pdev) { + rc = platform_device_register(&hmem_platform.pdev); + if (rc) { pr_err_once("failed to register device-dax hmem_platform device\n"); return; } - rc = platform_device_add(pdev); - if (rc) - platform_device_put(pdev); - else - platform_initialized = true; + platform_initialized = true; } void hmem_register_resource(int target_nid, struct resource *res) diff --git a/drivers/dax/hmem/hmem.c b/drivers/dax/hmem/hmem.c index 1cf7c2a0ee1c..af21f66bf872 100644 --- a/drivers/dax/hmem/hmem.c +++ b/drivers/dax/hmem/hmem.c @@ -3,6 +3,7 @@ #include #include #include +#include "../../cxl/cxl.h" #include "../bus.h" static bool region_idle; @@ -58,21 +59,22 @@ static void release_hmem(void *pdev) platform_device_unregister(pdev); } -static int hmem_register_device(struct device *host, int target_nid, - const struct resource *res) +static struct workqueue_struct *dax_hmem_wq; + +void dax_hmem_flush_work(void) +{ + flush_workqueue(dax_hmem_wq); +} +EXPORT_SYMBOL_FOR_MODULES(dax_hmem_flush_work, "dax_cxl"); + +static int __hmem_register_device(struct device *host, int target_nid, + const struct resource *res) { struct platform_device *pdev; struct memregion_info info; long id; int rc; - if (IS_ENABLED(CONFIG_CXL_REGION) && - region_intersects(res->start, resource_size(res), IORESOURCE_MEM, - IORES_DESC_CXL) != REGION_DISJOINT) { - dev_dbg(host, "deferring range to CXL: %pr\n", res); - return 0; - } - rc = region_intersects_soft_reserve(res->start, resource_size(res)); if (rc != REGION_INTERSECTS) return 0; @@ -94,6 +96,7 @@ static int hmem_register_device(struct device *host, int target_nid, return -ENOMEM; } + pdev->dev.parent = host; pdev->dev.numa_node = numa_map_to_online_node(target_nid); info = (struct memregion_info) { .target_node = target_nid, @@ -123,8 +126,74 @@ out_put: return rc; } +static int hmem_register_cxl_device(struct device *host, int target_nid, + const struct resource *res) +{ + if (region_intersects(res->start, resource_size(res), IORESOURCE_MEM, + IORES_DESC_CXL) == REGION_DISJOINT) + return 0; + + if (cxl_region_contains_resource(res)) { + dev_dbg(host, "CXL claims resource, dropping: %pr\n", res); + return 0; + } + + dev_dbg(host, "CXL did not claim resource, registering: %pr\n", res); + return __hmem_register_device(host, target_nid, res); +} + +static void process_defer_work(struct work_struct *w) +{ + struct hmem_platform_device *hpdev = container_of(w, typeof(*hpdev), work); + struct device *dev = &hpdev->pdev.dev; + + /* Relies on cxl_acpi and cxl_pci having had a chance to load */ + wait_for_device_probe(); + + guard(device)(dev); + if (!dev->driver) + goto out; + + if (!hpdev->did_probe) { + hpdev->did_probe = true; + walk_hmem_resources(dev, hmem_register_cxl_device); + } +out: + put_device(dev); +} + +static int hmem_register_device(struct device *host, int target_nid, + const struct resource *res) +{ + struct platform_device *pdev = to_platform_device(host); + struct hmem_platform_device *hpdev = to_hmem_platform_device(pdev); + + if (IS_ENABLED(CONFIG_DEV_DAX_CXL) && + region_intersects(res->start, resource_size(res), IORESOURCE_MEM, + IORES_DESC_CXL) != REGION_DISJOINT) { + if (!hpdev->did_probe) { + dev_dbg(host, "await CXL initial probe: %pr\n", res); + hpdev->work.func = process_defer_work; + get_device(host); + if (!queue_work(dax_hmem_wq, &hpdev->work)) + put_device(host); + return 0; + } + dev_dbg(host, "deferring range to CXL: %pr\n", res); + return 0; + } + + return __hmem_register_device(host, target_nid, res); +} + static int dax_hmem_platform_probe(struct platform_device *pdev) { + struct hmem_platform_device *hpdev = to_hmem_platform_device(pdev); + + /* queue is only flushed on module unload, fail rebind with pending work */ + if (work_pending(&hpdev->work)) + return -EBUSY; + return walk_hmem_resources(&pdev->dev, hmem_register_device); } @@ -139,13 +208,34 @@ static __init int dax_hmem_init(void) { int rc; + /* + * Ensure that cxl_acpi and cxl_pci have a chance to kick off + * CXL topology discovery at least once before scanning the + * iomem resource tree for IORES_DESC_CXL resources. + */ + if (IS_ENABLED(CONFIG_DEV_DAX_CXL)) { + request_module("cxl_acpi"); + request_module("cxl_pci"); + } + + dax_hmem_wq = alloc_ordered_workqueue("dax_hmem_wq", 0); + if (!dax_hmem_wq) + return -ENOMEM; + rc = platform_driver_register(&dax_hmem_platform_driver); if (rc) - return rc; + goto err_platform_driver; rc = platform_driver_register(&dax_hmem_driver); if (rc) - platform_driver_unregister(&dax_hmem_platform_driver); + goto err_driver; + + return 0; + +err_driver: + platform_driver_unregister(&dax_hmem_platform_driver); +err_platform_driver: + destroy_workqueue(dax_hmem_wq); return rc; } @@ -154,18 +244,12 @@ static __exit void dax_hmem_exit(void) { platform_driver_unregister(&dax_hmem_driver); platform_driver_unregister(&dax_hmem_platform_driver); + destroy_workqueue(dax_hmem_wq); } module_init(dax_hmem_init); module_exit(dax_hmem_exit); -/* Allow for CXL to define its own dax regions */ -#if IS_ENABLED(CONFIG_CXL_REGION) -#if IS_MODULE(CONFIG_CXL_ACPI) -MODULE_SOFTDEP("pre: cxl_acpi"); -#endif -#endif - MODULE_ALIAS("platform:hmem*"); MODULE_ALIAS("platform:hmem_platform*"); MODULE_DESCRIPTION("HMEM DAX: direct access to 'specific purpose' memory"); diff --git a/drivers/dax/super.c b/drivers/dax/super.c index c00b9dff4a06..25cf99dd9360 100644 --- a/drivers/dax/super.c +++ b/drivers/dax/super.c @@ -14,6 +14,7 @@ #include #include #include "dax-private.h" +#include "bus.h" /** * struct dax_device - anchor object for dax services @@ -111,6 +112,10 @@ struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev, u64 *start_off, } EXPORT_SYMBOL_GPL(fs_dax_get_by_bdev); +#endif /* CONFIG_BLOCK && CONFIG_FS_DAX */ + +#if IS_ENABLED(CONFIG_FS_DAX) + void fs_put_dax(struct dax_device *dax_dev, void *holder) { if (dax_dev && holder && @@ -119,7 +124,66 @@ void fs_put_dax(struct dax_device *dax_dev, void *holder) put_dax(dax_dev); } EXPORT_SYMBOL_GPL(fs_put_dax); -#endif /* CONFIG_BLOCK && CONFIG_FS_DAX */ + +/** + * fs_dax_get() - get ownership of a devdax via holder/holder_ops + * + * fs-dax file systems call this function to prepare to use a devdax device for + * fsdax. This is like fs_dax_get_by_bdev(), but the caller already has struct + * dev_dax (and there is no bdev). The holder makes this exclusive. + * + * @dax_dev: dev to be prepared for fs-dax usage + * @holder: filesystem or mapped device inside the dax_device + * @hops: operations for the inner holder + * + * Returns: 0 on success, <0 on failure + */ +int fs_dax_get(struct dax_device *dax_dev, void *holder, + const struct dax_holder_operations *hops) +{ + struct dev_dax *dev_dax; + struct dax_device_driver *dax_drv; + int id; + + id = dax_read_lock(); + if (!dax_dev || !dax_alive(dax_dev) || !igrab(&dax_dev->inode)) { + dax_read_unlock(id); + return -ENODEV; + } + dax_read_unlock(id); + + /* Verify the device is bound to fsdev_dax driver */ + dev_dax = dax_get_private(dax_dev); + if (!dev_dax) { + iput(&dax_dev->inode); + return -ENODEV; + } + + device_lock(&dev_dax->dev); + if (!dev_dax->dev.driver) { + device_unlock(&dev_dax->dev); + iput(&dax_dev->inode); + return -ENODEV; + } + dax_drv = to_dax_drv(dev_dax->dev.driver); + if (dax_drv->type != DAXDRV_FSDEV_TYPE) { + device_unlock(&dev_dax->dev); + iput(&dax_dev->inode); + return -EOPNOTSUPP; + } + device_unlock(&dev_dax->dev); + + if (cmpxchg(&dax_dev->holder_data, NULL, holder)) { + iput(&dax_dev->inode); + return -EBUSY; + } + + dax_dev->holder_ops = hops; + + return 0; +} +EXPORT_SYMBOL_GPL(fs_dax_get); +#endif /* CONFIG_FS_DAX */ enum dax_device_flags { /* !alive + rcu grace period == no new operations / mappings */ @@ -157,6 +221,9 @@ long dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff, long nr_pages, if (!dax_alive(dax_dev)) return -ENXIO; + if (!dax_dev->ops) + return -EOPNOTSUPP; + if (nr_pages < 0) return -EINVAL; @@ -207,6 +274,10 @@ int dax_zero_page_range(struct dax_device *dax_dev, pgoff_t pgoff, if (!dax_alive(dax_dev)) return -ENXIO; + + if (!dax_dev->ops) + return -EOPNOTSUPP; + /* * There are no callers that want to zero more than one page as of now. * Once users are there, this check can be removed after the @@ -223,7 +294,7 @@ EXPORT_SYMBOL_GPL(dax_zero_page_range); size_t dax_recovery_write(struct dax_device *dax_dev, pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *iter) { - if (!dax_dev->ops->recovery_write) + if (!dax_dev->ops || !dax_dev->ops->recovery_write) return 0; return dax_dev->ops->recovery_write(dax_dev, pgoff, addr, bytes, iter); } @@ -307,6 +378,35 @@ void set_dax_nomc(struct dax_device *dax_dev) } EXPORT_SYMBOL_GPL(set_dax_nomc); +/** + * dax_set_ops - set the dax_operations for a dax_device + * @dax_dev: the dax_device to configure + * @ops: the operations to set (may be NULL to clear) + * + * This allows drivers to set the dax_operations after the dax_device + * has been allocated. This is needed when the device is created before + * the driver that needs specific ops is bound (e.g., fsdev_dax binding + * to a dev_dax created by hmem). + * + * When setting non-NULL ops, fails if ops are already set (returns -EBUSY). + * When clearing ops (NULL), always succeeds. + * + * Return: 0 on success, -EBUSY if ops already set + */ +int dax_set_ops(struct dax_device *dax_dev, const struct dax_operations *ops) +{ + if (ops) { + /* Setting ops: fail if already set */ + if (cmpxchg(&dax_dev->ops, NULL, ops) != NULL) + return -EBUSY; + } else { + /* Clearing ops: always allowed */ + dax_dev->ops = NULL; + } + return 0; +} +EXPORT_SYMBOL_GPL(dax_set_ops); + bool dax_alive(struct dax_device *dax_dev) { lockdep_assert_held(&dax_srcu); @@ -421,7 +521,7 @@ static int dax_set(struct inode *inode, void *data) return 0; } -static struct dax_device *dax_dev_get(dev_t devt) +struct dax_device *dax_dev_get(dev_t devt) { struct dax_device *dax_dev; struct inode *inode; @@ -444,6 +544,7 @@ static struct dax_device *dax_dev_get(dev_t devt) return dax_dev; } +EXPORT_SYMBOL_GPL(dax_dev_get); struct dax_device *alloc_dax(void *private, const struct dax_operations *ops) { diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index b0bc204bae14..71f37544a5c6 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -845,9 +845,8 @@ void dma_buf_put(struct dma_buf *dmabuf) if (WARN_ON(!dmabuf || !dmabuf->file)) return; - fput(dmabuf->file); - DMA_BUF_TRACE(trace_dma_buf_put, dmabuf); + fput(dmabuf->file); } EXPORT_SYMBOL_NS_GPL(dma_buf_put, "DMA_BUF"); @@ -1353,6 +1352,7 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_attach_revocable, "DMA_BUF"); * Upon return importers may continue to access the DMA-buf memory. The caller * must do two additional waits to ensure that the memory is no longer being * accessed: + * * 1) Until dma_resv_wait_timeout() retires fences the importer is allowed to * fully access the memory. * 2) Until the importer calls unmap it is allowed to speculatively diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 1826ba73094c..a2aa82f4eedd 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -1037,8 +1037,8 @@ EXPORT_SYMBOL(dma_fence_set_deadline); */ void dma_fence_describe(struct dma_fence *fence, struct seq_file *seq) { - const char __rcu *timeline = ""; - const char __rcu *driver = ""; + const char __rcu *timeline = (const char __rcu *)""; + const char __rcu *driver = (const char __rcu *)""; const char *signaled = ""; rcu_read_lock(); diff --git a/drivers/dma-buf/heaps/cma_heap.c b/drivers/dma-buf/heaps/cma_heap.c index 92865786cfc9..a359aac45579 100644 --- a/drivers/dma-buf/heaps/cma_heap.c +++ b/drivers/dma-buf/heaps/cma_heap.c @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -30,19 +29,6 @@ #define DEFAULT_CMA_NAME "default_cma_region" -static struct cma *dma_areas[MAX_CMA_AREAS] __initdata; -static unsigned int dma_areas_num __initdata; - -int __init dma_heap_cma_register_heap(struct cma *cma) -{ - if (dma_areas_num >= ARRAY_SIZE(dma_areas)) - return -EINVAL; - - dma_areas[dma_areas_num++] = cma; - - return 0; -} - struct cma_heap { struct dma_heap *heap; struct cma *cma; @@ -411,6 +397,7 @@ static int __init __add_cma_heap(struct cma *cma, const char *name) static int __init add_cma_heaps(void) { struct cma *default_cma = dev_get_cma_area(NULL); + struct cma *cma; unsigned int i; int ret; @@ -420,9 +407,7 @@ static int __init add_cma_heaps(void) return ret; } - for (i = 0; i < dma_areas_num; i++) { - struct cma *cma = dma_areas[i]; - + for (i = 0; (cma = dma_contiguous_get_area_by_idx(i)) != NULL; i++) { ret = __add_cma_heap(cma, cma_get_name(cma)); if (ret) { pr_warn("Failed to add CMA heap %s", cma_get_name(cma)); diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c index b3650d8fd651..03c2b87cb111 100644 --- a/drivers/dma-buf/heaps/system_heap.c +++ b/drivers/dma-buf/heaps/system_heap.c @@ -10,17 +10,25 @@ * Andrew F. Davis */ +#include #include #include #include #include #include +#include #include +#include #include +#include #include #include #include +struct system_heap_priv { + bool cc_shared; +}; + struct system_heap_buffer { struct dma_heap *heap; struct list_head attachments; @@ -29,6 +37,7 @@ struct system_heap_buffer { struct sg_table sg_table; int vmap_cnt; void *vaddr; + bool cc_shared; }; struct dma_heap_attachment { @@ -36,6 +45,7 @@ struct dma_heap_attachment { struct sg_table table; struct list_head list; bool mapped; + bool cc_shared; }; #define LOW_ORDER_GFP (GFP_HIGHUSER | __GFP_ZERO) @@ -52,6 +62,34 @@ static gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP}; static const unsigned int orders[] = {8, 4, 0}; #define NUM_ORDERS ARRAY_SIZE(orders) +static int system_heap_set_page_decrypted(struct page *page) +{ + unsigned long addr = (unsigned long)page_address(page); + unsigned int nr_pages = 1 << compound_order(page); + int ret; + + ret = set_memory_decrypted(addr, nr_pages); + if (ret) + pr_warn_ratelimited("dma-buf system heap: failed to decrypt page at %p\n", + page_address(page)); + + return ret; +} + +static int system_heap_set_page_encrypted(struct page *page) +{ + unsigned long addr = (unsigned long)page_address(page); + unsigned int nr_pages = 1 << compound_order(page); + int ret; + + ret = set_memory_encrypted(addr, nr_pages); + if (ret) + pr_warn_ratelimited("dma-buf system heap: failed to re-encrypt page at %p, leaking memory\n", + page_address(page)); + + return ret; +} + static int dup_sg_table(struct sg_table *from, struct sg_table *to) { struct scatterlist *sg, *new_sg; @@ -90,6 +128,7 @@ static int system_heap_attach(struct dma_buf *dmabuf, a->dev = attachment->dev; INIT_LIST_HEAD(&a->list); a->mapped = false; + a->cc_shared = buffer->cc_shared; attachment->priv = a; @@ -119,9 +158,11 @@ static struct sg_table *system_heap_map_dma_buf(struct dma_buf_attachment *attac { struct dma_heap_attachment *a = attachment->priv; struct sg_table *table = &a->table; + unsigned long attrs; int ret; - ret = dma_map_sgtable(attachment->dev, table, direction, 0); + attrs = a->cc_shared ? DMA_ATTR_CC_SHARED : 0; + ret = dma_map_sgtable(attachment->dev, table, direction, attrs); if (ret) return ERR_PTR(ret); @@ -188,8 +229,13 @@ static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) unsigned long addr = vma->vm_start; unsigned long pgoff = vma->vm_pgoff; struct scatterlist *sg; + pgprot_t prot; int i, ret; + prot = vma->vm_page_prot; + if (buffer->cc_shared) + prot = pgprot_decrypted(prot); + for_each_sgtable_sg(table, sg, i) { unsigned long n = sg->length >> PAGE_SHIFT; @@ -206,8 +252,7 @@ static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) if (addr + size > vma->vm_end) size = vma->vm_end - addr; - ret = remap_pfn_range(vma, addr, page_to_pfn(page), - size, vma->vm_page_prot); + ret = remap_pfn_range(vma, addr, page_to_pfn(page), size, prot); if (ret) return ret; @@ -225,6 +270,7 @@ static void *system_heap_do_vmap(struct system_heap_buffer *buffer) struct page **pages = vmalloc(sizeof(struct page *) * npages); struct page **tmp = pages; struct sg_page_iter piter; + pgprot_t prot; void *vaddr; if (!pages) @@ -235,7 +281,10 @@ static void *system_heap_do_vmap(struct system_heap_buffer *buffer) *tmp++ = sg_page_iter_page(&piter); } - vaddr = vmap(pages, npages, VM_MAP, PAGE_KERNEL); + prot = PAGE_KERNEL; + if (buffer->cc_shared) + prot = pgprot_decrypted(prot); + vaddr = vmap(pages, npages, VM_MAP, prot); vfree(pages); if (!vaddr) @@ -296,6 +345,14 @@ static void system_heap_dma_buf_release(struct dma_buf *dmabuf) for_each_sgtable_sg(table, sg, i) { struct page *page = sg_page(sg); + /* + * Intentionally leak pages that cannot be re-encrypted + * to prevent shared memory from being reused. + */ + if (buffer->cc_shared && + system_heap_set_page_encrypted(page)) + continue; + __free_pages(page, compound_order(page)); } sg_free_table(table); @@ -347,6 +404,8 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap, DEFINE_DMA_BUF_EXPORT_INFO(exp_info); unsigned long size_remaining = len; unsigned int max_order = orders[0]; + struct system_heap_priv *priv = dma_heap_get_drvdata(heap); + bool cc_shared = priv->cc_shared; struct dma_buf *dmabuf; struct sg_table *table; struct scatterlist *sg; @@ -362,6 +421,7 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap, mutex_init(&buffer->lock); buffer->heap = heap; buffer->len = len; + buffer->cc_shared = cc_shared; INIT_LIST_HEAD(&pages); i = 0; @@ -396,6 +456,14 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap, list_del(&page->lru); } + if (cc_shared) { + for_each_sgtable_sg(table, sg, i) { + ret = system_heap_set_page_decrypted(sg_page(sg)); + if (ret) + goto free_pages; + } + } + /* create the dmabuf */ exp_info.exp_name = dma_heap_get_name(heap); exp_info.ops = &system_heap_buf_ops; @@ -413,6 +481,13 @@ free_pages: for_each_sgtable_sg(table, sg, i) { struct page *p = sg_page(sg); + /* + * Intentionally leak pages that cannot be re-encrypted + * to prevent shared memory from being reused. + */ + if (buffer->cc_shared && + system_heap_set_page_encrypted(p)) + continue; __free_pages(p, compound_order(p)); } sg_free_table(table); @@ -428,6 +503,14 @@ static const struct dma_heap_ops system_heap_ops = { .allocate = system_heap_allocate, }; +static struct system_heap_priv system_heap_priv = { + .cc_shared = false, +}; + +static struct system_heap_priv system_heap_cc_shared_priv = { + .cc_shared = true, +}; + static int __init system_heap_create(void) { struct dma_heap_export_info exp_info; @@ -435,8 +518,18 @@ static int __init system_heap_create(void) exp_info.name = "system"; exp_info.ops = &system_heap_ops; - exp_info.priv = NULL; + exp_info.priv = &system_heap_priv; + sys_heap = dma_heap_add(&exp_info); + if (IS_ERR(sys_heap)) + return PTR_ERR(sys_heap); + + if (IS_ENABLED(CONFIG_HIGHMEM) || + !cc_platform_has(CC_ATTR_MEM_ENCRYPT)) + return 0; + + exp_info.name = "system_cc_shared"; + exp_info.priv = &system_heap_cc_shared_priv; sys_heap = dma_heap_add(&exp_info); if (IS_ERR(sys_heap)) return PTR_ERR(sys_heap); diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 66cda7cc9f7a..ae6a682c9f76 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -376,29 +376,6 @@ config K3_DMA Support the DMA engine for Hisilicon K3 platform devices. -config LOONGSON1_APB_DMA - tristate "Loongson1 APB DMA support" - depends on MACH_LOONGSON32 || COMPILE_TEST - select DMA_ENGINE - select DMA_VIRTUAL_CHANNELS - help - This selects support for the APB DMA controller in Loongson1 SoCs, - which is required by Loongson1 NAND and audio support. - -config LOONGSON2_APB_DMA - tristate "Loongson2 APB DMA support" - depends on LOONGARCH || COMPILE_TEST - select DMA_ENGINE - select DMA_VIRTUAL_CHANNELS - help - Support for the Loongson2 APB DMA controller driver. The - DMA controller is having single DMA channel which can be - configured for different peripherals like audio, nand, sdio - etc which is in APB bus. - - This DMA controller transfers data from memory to peripheral fifo. - It does not support memory to memory data transfer. - config LPC18XX_DMAMUX bool "NXP LPC18xx/43xx DMA MUX for PL080" depends on ARCH_LPC18XX || COMPILE_TEST @@ -505,7 +482,7 @@ config MV_XOR_V2 platforms. config MXS_DMA - bool "MXS DMA support" + tristate "MXS DMA support" depends on ARCH_MXS || ARCH_MXC || COMPILE_TEST select STMP_DEVICE select DMA_ENGINE @@ -610,6 +587,15 @@ config SPRD_DMA help Enable support for the on-chip DMA controller on Spreadtrum platform. +config SWITCHTEC_DMA + tristate "Switchtec PSX/PFX Switch DMA Engine Support" + depends on PCI + select DMA_ENGINE + help + Some Switchtec PSX/PFX PCIe Switches support additional DMA engines. + These are exposed via an extra function on the switch's upstream + port. + config TXX9_DMAC tristate "Toshiba TXx9 SoC DMA support" depends on MACH_TX49XX @@ -774,6 +760,8 @@ source "drivers/dma/fsl-dpaa2-qdma/Kconfig" source "drivers/dma/lgm/Kconfig" +source "drivers/dma/loongson/Kconfig" + source "drivers/dma/stm32/Kconfig" # clients diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index a54d7688392b..14aa086629d5 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -49,8 +49,6 @@ obj-$(CONFIG_INTEL_IDMA64) += idma64.o obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-y += idxd/ obj-$(CONFIG_K3_DMA) += k3dma.o -obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o -obj-$(CONFIG_LOONGSON2_APB_DMA) += loongson2-apb-dma.o obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o obj-$(CONFIG_LPC32XX_DMAMUX) += lpc32xx-dmamux.o obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o @@ -74,6 +72,7 @@ obj-$(CONFIG_SF_PDMA) += sf-pdma/ obj-$(CONFIG_SOPHGO_CV1800B_DMAMUX) += cv1800b-dmamux.o obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o obj-$(CONFIG_SPRD_DMA) += sprd-dma.o +obj-$(CONFIG_SWITCHTEC_DMA) += switchtec_dma.o obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o obj-$(CONFIG_TEGRA186_GPC_DMA) += tegra186-gpc-dma.o obj-$(CONFIG_TEGRA20_APB_DMA) += tegra20-apb-dma.o @@ -87,6 +86,7 @@ obj-$(CONFIG_FSL_DPAA2_QDMA) += fsl-dpaa2-qdma/ obj-$(CONFIG_INTEL_LDMA) += lgm/ obj-y += amd/ +obj-y += loongson/ obj-y += mediatek/ obj-y += qcom/ obj-y += stm32/ diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index eb65872c5d5c..45c2c8e4bc45 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -134,6 +134,7 @@ struct axi_dmac_desc { struct axi_dmac_chan *chan; bool cyclic; + bool cyclic_eot; bool have_partial_xfer; unsigned int num_submitted; @@ -162,6 +163,7 @@ struct axi_dmac_chan { bool hw_cyclic; bool hw_2d; bool hw_sg; + bool hw_cyclic_hotfix; }; struct axi_dmac { @@ -227,29 +229,94 @@ static bool axi_dmac_check_addr(struct axi_dmac_chan *chan, dma_addr_t addr) return true; } +static struct axi_dmac_desc *axi_dmac_active_desc(struct axi_dmac_chan *chan) +{ + return list_first_entry_or_null(&chan->active_descs, + struct axi_dmac_desc, vdesc.node); +} + +static struct axi_dmac_desc *axi_dmac_get_next_desc(struct axi_dmac *dmac, + struct axi_dmac_chan *chan) +{ + struct axi_dmac_desc *active = axi_dmac_active_desc(chan); + struct virt_dma_desc *vdesc; + struct axi_dmac_desc *desc; + unsigned int val; + + /* + * Just play safe and ignore any SOF if we have an active cyclic transfer + * flagged to end. We'll start it as soon as the current cyclic one ends. + */ + if (active && active->cyclic_eot) + return NULL; + + /* + * It means a SW cyclic transfer is in place so we should just return + * the same descriptor. SW cyclic transfer termination is handled + * in axi_dmac_transfer_done(). + */ + if (chan->next_desc) + return chan->next_desc; + + vdesc = vchan_next_desc(&chan->vchan); + if (!vdesc) + return NULL; + + if (active && active->cyclic && !(vdesc->tx.flags & DMA_PREP_LOAD_EOT)) { + struct device *dev = chan_to_axi_dmac(chan)->dma_dev.dev; + + dev_warn(dev, "Discarding non EOT transfer after cyclic\n"); + list_del(&vdesc->node); + return NULL; + } + + list_move_tail(&vdesc->node, &chan->active_descs); + desc = to_axi_dmac_desc(vdesc); + chan->next_desc = desc; + + if (!active || !active->cyclic) + return desc; + + active->cyclic_eot = true; + + if (chan->hw_sg) { + unsigned long flags = AXI_DMAC_HW_FLAG_IRQ | AXI_DMAC_HW_FLAG_LAST; + /* + * Let's then stop the current cyclic transfer by making sure we + * get an EOT interrupt and to open the cyclic loop by marking + * the last segment. + */ + active->sg[active->num_sgs - 1].hw->flags = flags; + return NULL; + } + + /* + * Clear the cyclic bit if there's no Scatter-Gather HW so that we get + * at the end of the transfer. + */ + val = axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS); + val &= ~AXI_DMAC_FLAG_CYCLIC; + axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, val); + + return NULL; +} + static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) { struct axi_dmac *dmac = chan_to_axi_dmac(chan); - struct virt_dma_desc *vdesc; struct axi_dmac_desc *desc; struct axi_dmac_sg *sg; unsigned int flags = 0; unsigned int val; + desc = axi_dmac_get_next_desc(dmac, chan); + if (!desc) + return; + val = axi_dmac_read(dmac, AXI_DMAC_REG_START_TRANSFER); if (val) /* Queue is full, wait for the next SOT IRQ */ return; - desc = chan->next_desc; - - if (!desc) { - vdesc = vchan_next_desc(&chan->vchan); - if (!vdesc) - return; - list_move_tail(&vdesc->node, &chan->active_descs); - desc = to_axi_dmac_desc(vdesc); - chan->next_desc = desc; - } sg = &desc->sg[desc->num_submitted]; /* Already queued in cyclic mode. Wait for it to finish */ @@ -291,10 +358,12 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) * call, enable hw cyclic mode to avoid unnecessary interrupts. */ if (chan->hw_cyclic && desc->cyclic && !desc->vdesc.tx.callback) { - if (chan->hw_sg) + if (chan->hw_sg) { desc->sg[desc->num_sgs - 1].hw->flags &= ~AXI_DMAC_HW_FLAG_IRQ; - else if (desc->num_sgs == 1) + } else if (desc->num_sgs == 1) { + chan->next_desc = NULL; flags |= AXI_DMAC_FLAG_CYCLIC; + } } if (chan->hw_partial_xfer) @@ -312,12 +381,6 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) axi_dmac_write(dmac, AXI_DMAC_REG_START_TRANSFER, 1); } -static struct axi_dmac_desc *axi_dmac_active_desc(struct axi_dmac_chan *chan) -{ - return list_first_entry_or_null(&chan->active_descs, - struct axi_dmac_desc, vdesc.node); -} - static inline unsigned int axi_dmac_total_sg_bytes(struct axi_dmac_chan *chan, struct axi_dmac_sg *sg) { @@ -398,6 +461,61 @@ static void axi_dmac_compute_residue(struct axi_dmac_chan *chan, } } +static bool axi_dmac_handle_cyclic_eot(struct axi_dmac_chan *chan, + struct axi_dmac_desc *active) +{ + struct device *dev = chan_to_axi_dmac(chan)->dma_dev.dev; + struct virt_dma_desc *vdesc; + + /* wrap around */ + active->num_completed = 0; + + if (active->cyclic_eot) { + /* + * It means an HW cyclic transfer was marked to stop. And we + * know we have something to schedule, so start the next + * transfer now the cyclic one is done. + */ + list_del(&active->vdesc.node); + vchan_cookie_complete(&active->vdesc); + + if (chan->hw_cyclic_hotfix) { + struct axi_dmac *dmac = chan_to_axi_dmac(chan); + /* + * In older IP cores, ending a cyclic transfer by clearing + * the CYCLIC flag does not guarantee a graceful end. + * It can happen that some data (of the next frame) is + * already prefetched and will be wrongly visible in the + * next transfer. To workaround this, we need to reenable + * the core so everything is flushed. Newer cores handles + * this correctly and do not require this "hotfix". The + * SG IP also does not require this. + */ + dev_dbg(dev, "HW cyclic hotfix\n"); + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0); + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, AXI_DMAC_CTRL_ENABLE); + } + + return true; + } + + vdesc = vchan_next_desc(&chan->vchan); + if (!vdesc) + return false; + if (!(vdesc->tx.flags & DMA_PREP_LOAD_EOT)) { + dev_warn(dev, "Discarding non EOT transfer after cyclic\n"); + list_del(&vdesc->node); + return false; + } + + /* then let's end the cyclic transfer */ + chan->next_desc = NULL; + list_del(&active->vdesc.node); + vchan_cookie_complete(&active->vdesc); + + return true; +} + static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan, unsigned int completed_transfers) { @@ -416,6 +534,7 @@ static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan, if (chan->hw_sg) { if (active->cyclic) { vchan_cyclic_callback(&active->vdesc); + start_next = axi_dmac_handle_cyclic_eot(chan, active); } else { list_del(&active->vdesc.node); vchan_cookie_complete(&active->vdesc); @@ -445,7 +564,8 @@ static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan, if (active->num_completed == active->num_sgs || sg->partial_len) { if (active->cyclic) { - active->num_completed = 0; /* wrap around */ + /* keep start_next as is, if already true... */ + start_next |= axi_dmac_handle_cyclic_eot(chan, active); } else { list_del(&active->vdesc.node); vchan_cookie_complete(&active->vdesc); @@ -657,7 +777,12 @@ axi_dmac_prep_peripheral_dma_vec(struct dma_chan *c, const struct dma_vec *vecs, vecs[i].len, dsg); } - desc->cyclic = false; + desc->cyclic = flags & DMA_PREP_REPEAT; + if (desc->cyclic) { + /* Chain the last descriptor to the first, and remove its "last" flag */ + desc->sg[num_sgs - 1].hw->flags &= ~AXI_DMAC_HW_FLAG_LAST; + desc->sg[num_sgs - 1].hw->next_sg_addr = desc->sg[0].hw_phys; + } return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); } @@ -1053,6 +1178,9 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version) chan->length_align_mask = chan->address_align_mask; } + if (version < ADI_AXI_PCORE_VER(4, 6, 0) && !chan->hw_sg) + chan->hw_cyclic_hotfix = true; + return 0; } diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 27a8980b03dd..405bd2fbb4a3 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -31,29 +31,29 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "dmaengine.h" @@ -765,7 +765,7 @@ struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, mutex_lock(&dma_list_mutex); list_for_each_entry_safe(device, _d, &dma_device_list, global_node) { /* Finds a DMA controller with matching device node */ - if (np && device->dev->of_node && np != device->dev->of_node) + if (np && !device_match_of_node(device->dev, np)) continue; chan = find_candidate(device, mask, fn, fn_param); @@ -943,12 +943,14 @@ static void dmaenginem_release_channel(void *chan) struct dma_chan *devm_dma_request_chan(struct device *dev, const char *name) { - struct dma_chan *chan = dma_request_chan(dev, name); - int ret = 0; + struct dma_chan *chan; + int ret; - if (!IS_ERR(chan)) - ret = devm_add_action_or_reset(dev, dmaenginem_release_channel, chan); + chan = dma_request_chan(dev, name); + if (IS_ERR(chan)) + return chan; + ret = devm_add_action_or_reset(dev, dmaenginem_release_channel, chan); if (ret) return ERR_PTR(ret); diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index 5d74bc29cf89..4d53f077e9d2 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -50,6 +50,7 @@ #define AXI_DMA_FLAG_HAS_APB_REGS BIT(0) #define AXI_DMA_FLAG_HAS_RESETS BIT(1) #define AXI_DMA_FLAG_USE_CFG2 BIT(2) +#define AXI_DMA_FLAG_ARG0_AS_CHAN BIT(3) static inline void axi_dma_iowrite32(struct axi_dma_chip *chip, u32 reg, u32 val) @@ -342,8 +343,8 @@ static void axi_desc_put(struct axi_dma_desc *desc) kfree(desc); atomic_sub(descs_put, &chan->descs_allocated); dev_vdbg(chan2dev(chan), "%s: %d descs put, %d still allocated\n", - axi_chan_name(chan), descs_put, - atomic_read(&chan->descs_allocated)); + axi_chan_name(chan), descs_put, + atomic_read(&chan->descs_allocated)); } static void vchan_desc_put(struct virt_dma_desc *vdesc) @@ -353,7 +354,7 @@ static void vchan_desc_put(struct virt_dma_desc *vdesc) static enum dma_status dma_chan_tx_status(struct dma_chan *dchan, dma_cookie_t cookie, - struct dma_tx_state *txstate) + struct dma_tx_state *txstate) { struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); struct virt_dma_desc *vdesc; @@ -419,6 +420,7 @@ static void dw_axi_dma_set_byte_halfword(struct axi_dma_chan *chan, bool set) iowrite32(val, chan->chip->apb_regs + offset); } + /* Called in chan locked context */ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan, struct axi_dma_desc *first) @@ -491,7 +493,7 @@ static void axi_chan_start_first_queued(struct axi_dma_chan *chan) desc = vd_to_axi_desc(vd); dev_vdbg(chan2dev(chan), "%s: started %u\n", axi_chan_name(chan), - vd->tx.cookie); + vd->tx.cookie); axi_chan_block_xfer_start(chan, desc); } @@ -592,8 +594,6 @@ static void dw_axi_dma_set_hw_channel(struct axi_dma_chan *chan, bool set) (chan->id * DMA_APB_HS_SEL_BIT_SIZE)); reg_value |= (val << (chan->id * DMA_APB_HS_SEL_BIT_SIZE)); lo_hi_writeq(reg_value, chip->apb_regs + DMAC_APB_HW_HS_SEL_0); - - return; } /* @@ -1162,7 +1162,7 @@ static irqreturn_t dw_axi_dma_interrupt(int irq, void *dev_id) axi_chan_irq_clear(chan, status); dev_vdbg(chip->dev, "%s %u IRQ status: 0x%08x\n", - axi_chan_name(chan), i, status); + axi_chan_name(chan), i, status); if (status & DWAXIDMAC_IRQ_ALL_ERR) axi_chan_handle_err(chan, status); @@ -1358,16 +1358,27 @@ static int __maybe_unused axi_dma_runtime_resume(struct device *dev) static struct dma_chan *dw_axi_dma_of_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { + unsigned int handshake = dma_spec->args[0]; struct dw_axi_dma *dw = ofdma->of_dma_data; - struct axi_dma_chan *chan; + struct axi_dma_chan *chan = NULL; struct dma_chan *dchan; - dchan = dma_get_any_slave_channel(&dw->dma); + if (dw->hdata->use_handshake_as_channel_number) { + if (handshake >= dw->hdata->nr_channels) + return NULL; + + chan = &dw->chan[handshake]; + dchan = dma_get_slave_channel(&chan->vc.chan); + } else { + dchan = dma_get_any_slave_channel(&dw->dma); + } + if (!dchan) return NULL; - chan = dchan_to_axi_dma_chan(dchan); - chan->hw_handshake_num = dma_spec->args[0]; + if (!chan) + chan = dchan_to_axi_dma_chan(dchan); + chan->hw_handshake_num = handshake; return dchan; } @@ -1451,7 +1462,7 @@ static int axi_req_irqs(struct platform_device *pdev, struct axi_dma_chip *chip) if (chip->irq[i] < 0) return chip->irq[i]; ret = devm_request_irq(chip->dev, chip->irq[i], dw_axi_dma_interrupt, - IRQF_SHARED, KBUILD_MODNAME, chip); + IRQF_SHARED, KBUILD_MODNAME, chip); if (ret < 0) return ret; } @@ -1506,6 +1517,8 @@ static int dw_probe(struct platform_device *pdev) return ret; } + chip->dw->hdata->use_handshake_as_channel_number = !!(flags & AXI_DMA_FLAG_ARG0_AS_CHAN); + chip->dw->hdata->use_cfg2 = !!(flags & AXI_DMA_FLAG_USE_CFG2); chip->core_clk = devm_clk_get(chip->dev, "core-clk"); @@ -1645,7 +1658,7 @@ static void dw_remove(struct platform_device *pdev) of_dma_controller_free(chip->dev->of_node); list_for_each_entry_safe(chan, _chan, &dw->dma.channels, - vc.chan.device_node) { + vc.chan.device_node) { list_del(&chan->vc.chan.device_node); tasklet_kill(&chan->vc.task); } @@ -1661,6 +1674,9 @@ static const struct of_device_id dw_dma_of_id_table[] = { }, { .compatible = "intel,kmb-axi-dma", .data = (void *)AXI_DMA_FLAG_HAS_APB_REGS, + }, { + .compatible = "sophgo,cv1800b-axi-dma", + .data = (void *)AXI_DMA_FLAG_ARG0_AS_CHAN, }, { .compatible = "starfive,jh7110-axi-dma", .data = (void *)(AXI_DMA_FLAG_HAS_RESETS | AXI_DMA_FLAG_USE_CFG2), diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h index b842e6a8d90d..67cc199e24d1 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h @@ -34,6 +34,7 @@ struct dw_axi_dma_hcfg { bool reg_map_8_channels; bool restrict_axi_burst_len; bool use_cfg2; + bool use_handshake_as_channel_number; }; struct axi_dma_chan { diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index 5397dbda4f72..c2feb3adc79f 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -223,6 +223,43 @@ static int dw_edma_device_config(struct dma_chan *dchan, struct dma_slave_config *config) { struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); + bool cfg_non_ll; + int non_ll = 0; + + chan->non_ll = false; + if (chan->dw->chip->mf == EDMA_MF_HDMA_NATIVE) { + if (config->peripheral_config && + config->peripheral_size != sizeof(int)) { + dev_err(dchan->device->dev, + "config param peripheral size mismatch\n"); + return -EINVAL; + } + + /* + * When there is no valid LLP base address available then the + * default DMA ops will use the non-LL mode. + * + * Cases where LL mode is enabled and client wants to use the + * non-LL mode then also client can do so via providing the + * peripheral_config param. + */ + cfg_non_ll = chan->dw->chip->cfg_non_ll; + if (config->peripheral_config) { + non_ll = *(int *)config->peripheral_config; + + if (cfg_non_ll && !non_ll) { + dev_err(dchan->device->dev, "invalid configuration\n"); + return -EINVAL; + } + } + + if (cfg_non_ll || non_ll) + chan->non_ll = true; + } else if (config->peripheral_config) { + dev_err(dchan->device->dev, + "peripheral config param applicable only for HDMA\n"); + return -EINVAL; + } memcpy(&chan->config, config, sizeof(*config)); chan->configured = true; @@ -358,6 +395,7 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer) struct dw_edma_desc *desc; u64 src_addr, dst_addr; size_t fsz = 0; + u32 bursts_max; u32 cnt = 0; int i; @@ -415,6 +453,13 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer) return NULL; } + /* + * For non-LL mode, only a single burst can be handled + * in a single chunk unlike LL mode where multiple bursts + * can be configured in a single chunk. + */ + bursts_max = chan->non_ll ? 1 : chan->ll_max; + desc = dw_edma_alloc_desc(chan); if (unlikely(!desc)) goto err_alloc; @@ -450,7 +495,7 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer) if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg) break; - if (chunk->bursts_alloc == chan->ll_max) { + if (chunk->bursts_alloc == bursts_max) { chunk = dw_edma_alloc_chunk(desc); if (unlikely(!chunk)) goto err_alloc; @@ -663,7 +708,96 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) chan->status = EDMA_ST_IDLE; } -static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data) +static void dw_edma_emul_irq_ack(struct irq_data *d) +{ + struct dw_edma *dw = irq_data_get_irq_chip_data(d); + + dw_edma_core_ack_emulated_irq(dw); +} + +/* + * irq_chip implementation for interrupt-emulation doorbells. + * + * The emulated source has no mask/unmask mechanism. With handle_level_irq(), + * the flow is therefore: + * 1) .irq_ack() deasserts the source + * 2) registered handlers (if any) are dispatched + * Since deassertion is already done in .irq_ack(), handlers do not need to take + * care of it, hence IRQCHIP_ONESHOT_SAFE. + */ +static struct irq_chip dw_edma_emul_irqchip = { + .name = "dw-edma-emul", + .irq_ack = dw_edma_emul_irq_ack, + .flags = IRQCHIP_ONESHOT_SAFE | IRQCHIP_SKIP_SET_WAKE, +}; + +static int dw_edma_emul_irq_alloc(struct dw_edma *dw) +{ + struct dw_edma_chip *chip = dw->chip; + int virq; + + chip->db_irq = 0; + chip->db_offset = ~0; + + /* + * Only meaningful when the core provides the deassert sequence + * for interrupt emulation. + */ + if (!dw->core->ack_emulated_irq) + return 0; + + /* + * Allocate a single, requestable Linux virtual IRQ number. + * Use >= 1 so that 0 can remain a "not available" sentinel. + */ + virq = irq_alloc_desc(NUMA_NO_NODE); + if (virq < 0) + return virq; + + irq_set_chip_and_handler(virq, &dw_edma_emul_irqchip, handle_level_irq); + irq_set_chip_data(virq, dw); + irq_set_noprobe(virq); + + chip->db_irq = virq; + chip->db_offset = dw_edma_core_db_offset(dw); + + return 0; +} + +static void dw_edma_emul_irq_free(struct dw_edma *dw) +{ + struct dw_edma_chip *chip = dw->chip; + + if (!chip) + return; + if (chip->db_irq <= 0) + return; + + irq_free_descs(chip->db_irq, 1); + chip->db_irq = 0; + chip->db_offset = ~0; +} + +static inline irqreturn_t dw_edma_interrupt_emulated(void *data) +{ + struct dw_edma_irq *dw_irq = data; + struct dw_edma *dw = dw_irq->dw; + int db_irq = dw->chip->db_irq; + + if (db_irq > 0) { + /* + * Interrupt emulation may assert the IRQ line without updating the + * normal DONE/ABORT status bits. With a shared IRQ handler we + * cannot reliably detect such events by status registers alone, so + * always perform the core-specific deassert sequence. + */ + generic_handle_irq(db_irq); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static inline irqreturn_t dw_edma_interrupt_write_inner(int irq, void *data) { struct dw_edma_irq *dw_irq = data; @@ -672,7 +806,7 @@ static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data) dw_edma_abort_interrupt); } -static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data) +static inline irqreturn_t dw_edma_interrupt_read_inner(int irq, void *data) { struct dw_edma_irq *dw_irq = data; @@ -681,12 +815,33 @@ static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data) dw_edma_abort_interrupt); } -static irqreturn_t dw_edma_interrupt_common(int irq, void *data) +static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data) { irqreturn_t ret = IRQ_NONE; - ret |= dw_edma_interrupt_write(irq, data); - ret |= dw_edma_interrupt_read(irq, data); + ret |= dw_edma_interrupt_write_inner(irq, data); + ret |= dw_edma_interrupt_emulated(data); + + return ret; +} + +static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data) +{ + irqreturn_t ret = IRQ_NONE; + + ret |= dw_edma_interrupt_read_inner(irq, data); + ret |= dw_edma_interrupt_emulated(data); + + return ret; +} + +static inline irqreturn_t dw_edma_interrupt_common(int irq, void *data) +{ + irqreturn_t ret = IRQ_NONE; + + ret |= dw_edma_interrupt_write_inner(irq, data); + ret |= dw_edma_interrupt_read_inner(irq, data); + ret |= dw_edma_interrupt_emulated(data); return ret; } @@ -977,6 +1132,11 @@ int dw_edma_probe(struct dw_edma_chip *chip) if (err) return err; + /* Allocate a dedicated virtual IRQ for interrupt-emulation doorbells */ + err = dw_edma_emul_irq_alloc(dw); + if (err) + dev_warn(dev, "Failed to allocate emulation IRQ: %d\n", err); + /* Setup write/read channels */ err = dw_edma_channel_setup(dw, wr_alloc, rd_alloc); if (err) @@ -992,6 +1152,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) err_irq_free: for (i = (dw->nr_irqs - 1); i >= 0; i--) free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]); + dw_edma_emul_irq_free(dw); return err; } @@ -1014,6 +1175,7 @@ int dw_edma_remove(struct dw_edma_chip *chip) /* Free irqs */ for (i = (dw->nr_irqs - 1); i >= 0; i--) free_irq(chip->ops->irq_vector(dev, i), &dw->irq[i]); + dw_edma_emul_irq_free(dw); /* Deregister eDMA device */ dma_async_device_unregister(&dw->dma); diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h index 71894b9e0b15..902574b1ba86 100644 --- a/drivers/dma/dw-edma/dw-edma-core.h +++ b/drivers/dma/dw-edma/dw-edma-core.h @@ -86,6 +86,7 @@ struct dw_edma_chan { u8 configured; struct dma_slave_config config; + bool non_ll; }; struct dw_edma_irq { @@ -126,6 +127,8 @@ struct dw_edma_core_ops { void (*start)(struct dw_edma_chunk *chunk, bool first); void (*ch_config)(struct dw_edma_chan *chan); void (*debugfs_on)(struct dw_edma *dw); + void (*ack_emulated_irq)(struct dw_edma *dw); + resource_size_t (*db_offset)(struct dw_edma *dw); }; struct dw_edma_sg { @@ -206,4 +209,19 @@ void dw_edma_core_debugfs_on(struct dw_edma *dw) dw->core->debugfs_on(dw); } +static inline int dw_edma_core_ack_emulated_irq(struct dw_edma *dw) +{ + if (!dw->core->ack_emulated_irq) + return -EOPNOTSUPP; + + dw->core->ack_emulated_irq(dw); + return 0; +} + +static inline resource_size_t +dw_edma_core_db_offset(struct dw_edma *dw) +{ + return dw->core->db_offset(dw); +} + #endif /* _DW_EDMA_CORE_H */ diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c index 83230acaa597..0b30ce138503 100644 --- a/drivers/dma/dw-edma/dw-edma-pcie.c +++ b/drivers/dma/dw-edma/dw-edma-pcie.c @@ -14,14 +14,35 @@ #include #include #include +#include #include "dw-edma-core.h" -#define DW_PCIE_VSEC_DMA_ID 0x6 -#define DW_PCIE_VSEC_DMA_BAR GENMASK(10, 8) -#define DW_PCIE_VSEC_DMA_MAP GENMASK(2, 0) -#define DW_PCIE_VSEC_DMA_WR_CH GENMASK(9, 0) -#define DW_PCIE_VSEC_DMA_RD_CH GENMASK(25, 16) +/* Synopsys */ +#define DW_PCIE_SYNOPSYS_VSEC_DMA_ID 0x6 +#define DW_PCIE_SYNOPSYS_VSEC_DMA_BAR GENMASK(10, 8) +#define DW_PCIE_SYNOPSYS_VSEC_DMA_MAP GENMASK(2, 0) +#define DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH GENMASK(9, 0) +#define DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH GENMASK(25, 16) + +/* AMD MDB (Xilinx) specific defines */ +#define PCI_DEVICE_ID_XILINX_B054 0xb054 + +#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID 0x6 +#define DW_PCIE_XILINX_MDB_VSEC_ID 0x20 +#define DW_PCIE_XILINX_MDB_VSEC_DMA_BAR GENMASK(10, 8) +#define DW_PCIE_XILINX_MDB_VSEC_DMA_MAP GENMASK(2, 0) +#define DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH GENMASK(9, 0) +#define DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH GENMASK(25, 16) + +#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH 0xc +#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW 0x8 +#define DW_PCIE_XILINX_MDB_INVALID_ADDR (~0ULL) + +#define DW_PCIE_XILINX_MDB_LL_OFF_GAP 0x200000 +#define DW_PCIE_XILINX_MDB_LL_SIZE 0x800 +#define DW_PCIE_XILINX_MDB_DT_OFF_GAP 0x100000 +#define DW_PCIE_XILINX_MDB_DT_SIZE 0x800 #define DW_BLOCK(a, b, c) \ { \ @@ -50,6 +71,7 @@ struct dw_edma_pcie_data { u8 irqs; u16 wr_ch_cnt; u16 rd_ch_cnt; + u64 devmem_phys_off; }; static const struct dw_edma_pcie_data snps_edda_data = { @@ -90,6 +112,64 @@ static const struct dw_edma_pcie_data snps_edda_data = { .rd_ch_cnt = 2, }; +static const struct dw_edma_pcie_data xilinx_mdb_data = { + /* MDB registers location */ + .rg.bar = BAR_0, + .rg.off = SZ_4K, /* 4 Kbytes */ + .rg.sz = SZ_8K, /* 8 Kbytes */ + + /* Other */ + .mf = EDMA_MF_HDMA_NATIVE, + .irqs = 1, + .wr_ch_cnt = 8, + .rd_ch_cnt = 8, +}; + +static void dw_edma_set_chan_region_offset(struct dw_edma_pcie_data *pdata, + enum pci_barno bar, off_t start_off, + off_t ll_off_gap, size_t ll_size, + off_t dt_off_gap, size_t dt_size) +{ + u16 wr_ch = pdata->wr_ch_cnt; + u16 rd_ch = pdata->rd_ch_cnt; + off_t off; + u16 i; + + off = start_off; + + /* Write channel LL region */ + for (i = 0; i < wr_ch; i++) { + pdata->ll_wr[i].bar = bar; + pdata->ll_wr[i].off = off; + pdata->ll_wr[i].sz = ll_size; + off += ll_off_gap; + } + + /* Read channel LL region */ + for (i = 0; i < rd_ch; i++) { + pdata->ll_rd[i].bar = bar; + pdata->ll_rd[i].off = off; + pdata->ll_rd[i].sz = ll_size; + off += ll_off_gap; + } + + /* Write channel data region */ + for (i = 0; i < wr_ch; i++) { + pdata->dt_wr[i].bar = bar; + pdata->dt_wr[i].off = off; + pdata->dt_wr[i].sz = dt_size; + off += dt_off_gap; + } + + /* Read channel data region */ + for (i = 0; i < rd_ch; i++) { + pdata->dt_rd[i].bar = bar; + pdata->dt_rd[i].off = off; + pdata->dt_rd[i].sz = dt_size; + off += dt_off_gap; + } +} + static int dw_edma_pcie_irq_vector(struct device *dev, unsigned int nr) { return pci_irq_vector(to_pci_dev(dev), nr); @@ -114,15 +194,15 @@ static const struct dw_edma_plat_ops dw_edma_pcie_plat_ops = { .pci_address = dw_edma_pcie_address, }; -static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev, - struct dw_edma_pcie_data *pdata) +static void dw_edma_pcie_get_synopsys_dma_data(struct pci_dev *pdev, + struct dw_edma_pcie_data *pdata) { u32 val, map; u16 vsec; u64 off; vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS, - DW_PCIE_VSEC_DMA_ID); + DW_PCIE_SYNOPSYS_VSEC_DMA_ID); if (!vsec) return; @@ -131,9 +211,9 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev, PCI_VNDR_HEADER_LEN(val) != 0x18) return; - pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability DMA\n"); + pci_dbg(pdev, "Detected Synopsys PCIe Vendor-Specific Extended Capability DMA\n"); pci_read_config_dword(pdev, vsec + 0x8, &val); - map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val); + map = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_MAP, val); if (map != EDMA_MF_EDMA_LEGACY && map != EDMA_MF_EDMA_UNROLL && map != EDMA_MF_HDMA_COMPAT && @@ -141,13 +221,13 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev, return; pdata->mf = map; - pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val); + pdata->rg.bar = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_BAR, val); pci_read_config_dword(pdev, vsec + 0xc, &val); pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt, - FIELD_GET(DW_PCIE_VSEC_DMA_WR_CH, val)); + FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH, val)); pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt, - FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val)); + FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH, val)); pci_read_config_dword(pdev, vsec + 0x14, &val); off = val; @@ -157,6 +237,73 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev, pdata->rg.off = off; } +static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev, + struct dw_edma_pcie_data *pdata) +{ + u32 val, map; + u16 vsec; + u64 off; + + pdata->devmem_phys_off = DW_PCIE_XILINX_MDB_INVALID_ADDR; + + vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX, + DW_PCIE_XILINX_MDB_VSEC_DMA_ID); + if (!vsec) + return; + + pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val); + if (PCI_VNDR_HEADER_REV(val) != 0x00 || + PCI_VNDR_HEADER_LEN(val) != 0x18) + return; + + pci_dbg(pdev, "Detected Xilinx PCIe Vendor-Specific Extended Capability DMA\n"); + pci_read_config_dword(pdev, vsec + 0x8, &val); + map = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_MAP, val); + if (map != EDMA_MF_HDMA_NATIVE) + return; + + pdata->mf = map; + pdata->rg.bar = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_BAR, val); + + pci_read_config_dword(pdev, vsec + 0xc, &val); + pdata->wr_ch_cnt = min(pdata->wr_ch_cnt, + FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH, val)); + pdata->rd_ch_cnt = min(pdata->rd_ch_cnt, + FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH, val)); + + pci_read_config_dword(pdev, vsec + 0x14, &val); + off = val; + pci_read_config_dword(pdev, vsec + 0x10, &val); + off <<= 32; + off |= val; + pdata->rg.off = off; + + vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX, + DW_PCIE_XILINX_MDB_VSEC_ID); + if (!vsec) + return; + + pci_read_config_dword(pdev, + vsec + DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH, + &val); + off = val; + pci_read_config_dword(pdev, + vsec + DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW, + &val); + off <<= 32; + off |= val; + pdata->devmem_phys_off = off; +} + +static u64 dw_edma_get_phys_addr(struct pci_dev *pdev, + struct dw_edma_pcie_data *pdata, + enum pci_barno bar) +{ + if (pdev->vendor == PCI_VENDOR_ID_XILINX) + return pdata->devmem_phys_off; + return pci_bus_address(pdev, bar); +} + static int dw_edma_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *pid) { @@ -165,6 +312,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, struct dw_edma_chip *chip; int err, nr_irqs; int i, mask; + bool non_ll = false; struct dw_edma_pcie_data *vsec_data __free(kfree) = kmalloc_obj(*vsec_data); @@ -184,7 +332,32 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, * Tries to find if exists a PCIe Vendor-Specific Extended Capability * for the DMA, if one exists, then reconfigures it. */ - dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data); + dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data); + + if (pdev->vendor == PCI_VENDOR_ID_XILINX) { + dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data); + + /* + * There is no valid address found for the LL memory + * space on the device side. In the absence of LL base + * address use the non-LL mode or simple mode supported by + * the HDMA IP. + */ + if (vsec_data->devmem_phys_off == DW_PCIE_XILINX_MDB_INVALID_ADDR) + non_ll = true; + + /* + * Configure the channel LL and data blocks if number of + * channels enabled in VSEC capability are more than the + * channels configured in xilinx_mdb_data. + */ + if (!non_ll) + dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0, + DW_PCIE_XILINX_MDB_LL_OFF_GAP, + DW_PCIE_XILINX_MDB_LL_SIZE, + DW_PCIE_XILINX_MDB_DT_OFF_GAP, + DW_PCIE_XILINX_MDB_DT_SIZE); + } /* Mapping PCI BAR regions */ mask = BIT(vsec_data->rg.bar); @@ -231,6 +404,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, chip->mf = vsec_data->mf; chip->nr_irqs = nr_irqs; chip->ops = &dw_edma_pcie_plat_ops; + chip->cfg_non_ll = non_ll; chip->ll_wr_cnt = vsec_data->wr_ch_cnt; chip->ll_rd_cnt = vsec_data->rd_ch_cnt; @@ -239,7 +413,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, if (!chip->reg_base) return -ENOMEM; - for (i = 0; i < chip->ll_wr_cnt; i++) { + for (i = 0; i < chip->ll_wr_cnt && !non_ll; i++) { struct dw_edma_region *ll_region = &chip->ll_region_wr[i]; struct dw_edma_region *dt_region = &chip->dt_region_wr[i]; struct dw_edma_block *ll_block = &vsec_data->ll_wr[i]; @@ -250,7 +424,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return -ENOMEM; ll_region->vaddr.io += ll_block->off; - ll_region->paddr = pci_bus_address(pdev, ll_block->bar); + ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data, + ll_block->bar); ll_region->paddr += ll_block->off; ll_region->sz = ll_block->sz; @@ -259,12 +434,13 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return -ENOMEM; dt_region->vaddr.io += dt_block->off; - dt_region->paddr = pci_bus_address(pdev, dt_block->bar); + dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data, + dt_block->bar); dt_region->paddr += dt_block->off; dt_region->sz = dt_block->sz; } - for (i = 0; i < chip->ll_rd_cnt; i++) { + for (i = 0; i < chip->ll_rd_cnt && !non_ll; i++) { struct dw_edma_region *ll_region = &chip->ll_region_rd[i]; struct dw_edma_region *dt_region = &chip->dt_region_rd[i]; struct dw_edma_block *ll_block = &vsec_data->ll_rd[i]; @@ -275,7 +451,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return -ENOMEM; ll_region->vaddr.io += ll_block->off; - ll_region->paddr = pci_bus_address(pdev, ll_block->bar); + ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data, + ll_block->bar); ll_region->paddr += ll_block->off; ll_region->sz = ll_block->sz; @@ -284,7 +461,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, return -ENOMEM; dt_region->vaddr.io += dt_block->off; - dt_region->paddr = pci_bus_address(pdev, dt_block->bar); + dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data, + dt_block->bar); dt_region->paddr += dt_block->off; dt_region->sz = dt_block->sz; } @@ -367,6 +545,8 @@ static void dw_edma_pcie_remove(struct pci_dev *pdev) static const struct pci_device_id dw_edma_pcie_id_table[] = { { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) }, + { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054), + (kernel_ulong_t)&xilinx_mdb_data }, { } }; MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table); diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c index b75fdaffad9a..69e8279adec8 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.c +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c @@ -509,6 +509,25 @@ static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) dw_edma_v0_debugfs_on(dw); } +static void dw_edma_v0_core_ack_emulated_irq(struct dw_edma *dw) +{ + /* + * Interrupt emulation may assert the IRQ without setting + * DONE/ABORT status bits. A zero write to INT_CLEAR deasserts the + * emulated IRQ, while being a no-op for real interrupts. + */ + SET_BOTH_32(dw, int_clear, 0); +} + +static resource_size_t dw_edma_v0_core_db_offset(struct dw_edma *dw) +{ + /* + * rd_int_status is chosen arbitrarily, but wr_int_status would be + * equally suitable. + */ + return offsetof(struct dw_edma_v0_regs, rd_int_status); +} + static const struct dw_edma_core_ops dw_edma_v0_core = { .off = dw_edma_v0_core_off, .ch_count = dw_edma_v0_core_ch_count, @@ -517,6 +536,8 @@ static const struct dw_edma_core_ops dw_edma_v0_core = { .start = dw_edma_v0_core_start, .ch_config = dw_edma_v0_core_ch_config, .debugfs_on = dw_edma_v0_core_debugfs_on, + .ack_emulated_irq = dw_edma_v0_core_ack_emulated_irq, + .db_offset = dw_edma_v0_core_db_offset, }; void dw_edma_v0_core_register(struct dw_edma *dw) diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c index ce8f7254bab2..632abb8b481c 100644 --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c @@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct dw_edma_chunk *chunk) readl(chunk->ll_region.vaddr.io); } -static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first) +static void dw_hdma_v0_core_ll_start(struct dw_edma_chunk *chunk, bool first) { struct dw_edma_chan *chan = chunk->chan; struct dw_edma *dw = chan->dw; @@ -263,6 +263,68 @@ static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first) SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START); } +static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk *chunk) +{ + struct dw_edma_chan *chan = chunk->chan; + struct dw_edma *dw = chan->dw; + struct dw_edma_burst *child; + u32 val; + + child = list_first_entry_or_null(&chunk->burst->list, + struct dw_edma_burst, list); + if (!child) + return; + + SET_CH_32(dw, chan->dir, chan->id, ch_en, HDMA_V0_CH_EN); + + /* Source address */ + SET_CH_32(dw, chan->dir, chan->id, sar.lsb, + lower_32_bits(child->sar)); + SET_CH_32(dw, chan->dir, chan->id, sar.msb, + upper_32_bits(child->sar)); + + /* Destination address */ + SET_CH_32(dw, chan->dir, chan->id, dar.lsb, + lower_32_bits(child->dar)); + SET_CH_32(dw, chan->dir, chan->id, dar.msb, + upper_32_bits(child->dar)); + + /* Transfer size */ + SET_CH_32(dw, chan->dir, chan->id, transfer_size, child->sz); + + /* Interrupt setup */ + val = GET_CH_32(dw, chan->dir, chan->id, int_setup) | + HDMA_V0_STOP_INT_MASK | + HDMA_V0_ABORT_INT_MASK | + HDMA_V0_LOCAL_STOP_INT_EN | + HDMA_V0_LOCAL_ABORT_INT_EN; + + if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) { + val |= HDMA_V0_REMOTE_STOP_INT_EN | + HDMA_V0_REMOTE_ABORT_INT_EN; + } + + SET_CH_32(dw, chan->dir, chan->id, int_setup, val); + + /* Channel control setup */ + val = GET_CH_32(dw, chan->dir, chan->id, control1); + val &= ~HDMA_V0_LINKLIST_EN; + SET_CH_32(dw, chan->dir, chan->id, control1, val); + + SET_CH_32(dw, chan->dir, chan->id, doorbell, + HDMA_V0_DOORBELL_START); +} + +static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first) +{ + struct dw_edma_chan *chan = chunk->chan; + + if (chan->non_ll) + dw_hdma_v0_core_non_ll_start(chunk); + else + dw_hdma_v0_core_ll_start(chunk, first); +} + static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan) { struct dw_edma *dw = chan->dw; @@ -283,6 +345,12 @@ static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw) dw_hdma_v0_debugfs_on(dw); } +static resource_size_t dw_hdma_v0_core_db_offset(struct dw_edma *dw) +{ + /* Implement once the correct offset is known. */ + return ~0; +} + static const struct dw_edma_core_ops dw_hdma_v0_core = { .off = dw_hdma_v0_core_off, .ch_count = dw_hdma_v0_core_ch_count, @@ -291,6 +359,7 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = { .start = dw_hdma_v0_core_start, .ch_config = dw_hdma_v0_core_ch_config, .debugfs_on = dw_hdma_v0_core_debugfs_on, + .db_offset = dw_hdma_v0_core_db_offset, }; void dw_hdma_v0_core_register(struct dw_edma *dw) diff --git a/drivers/dma/dw-edma/dw-hdma-v0-regs.h b/drivers/dma/dw-edma/dw-hdma-v0-regs.h index eab5fd7177e5..7759ba9b4850 100644 --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h @@ -12,6 +12,7 @@ #include #define HDMA_V0_MAX_NR_CH 8 +#define HDMA_V0_CH_EN BIT(0) #define HDMA_V0_LOCAL_ABORT_INT_EN BIT(6) #define HDMA_V0_REMOTE_ABORT_INT_EN BIT(5) #define HDMA_V0_LOCAL_STOP_INT_EN BIT(4) diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c index 6a38738e56e2..bb7531c456df 100644 --- a/drivers/dma/fsl-edma-common.c +++ b/drivers/dma/fsl-edma-common.c @@ -905,8 +905,7 @@ void fsl_edma_free_chan_resources(struct dma_chan *chan) fsl_chan->is_sw = false; fsl_chan->srcid = 0; fsl_chan->is_remote = false; - if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_CHCLK) - clk_disable_unprepare(fsl_chan->clk); + clk_disable_unprepare(fsl_chan->clk); } void fsl_edma_cleanup_vchan(struct dma_device *dmadev) diff --git a/drivers/dma/fsl-edma-main.c b/drivers/dma/fsl-edma-main.c index b596baa0a182..36155ab1602a 100644 --- a/drivers/dma/fsl-edma-main.c +++ b/drivers/dma/fsl-edma-main.c @@ -705,16 +705,14 @@ static int fsl_edma_probe(struct platform_device *pdev) int ret, i; drvdata = device_get_match_data(&pdev->dev); - if (!drvdata) { - dev_err(&pdev->dev, "unable to find driver data\n"); - return -EINVAL; - } + if (!drvdata) + return dev_err_probe(&pdev->dev, -EINVAL, + "unable to find driver data\n"); ret = of_property_read_u32(np, "dma-channels", &chans); - if (ret) { - dev_err(&pdev->dev, "Can't get dma-channels.\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Can't get dma-channels.\n"); fsl_edma = devm_kzalloc(&pdev->dev, struct_size(fsl_edma, chans, chans), GFP_KERNEL); @@ -738,10 +736,10 @@ static int fsl_edma_probe(struct platform_device *pdev) if (drvdata->flags & FSL_EDMA_DRV_HAS_DMACLK) { fsl_edma->dmaclk = devm_clk_get_enabled(&pdev->dev, "dma"); - if (IS_ERR(fsl_edma->dmaclk)) { - dev_err(&pdev->dev, "Missing DMA block clock.\n"); - return PTR_ERR(fsl_edma->dmaclk); - } + if (IS_ERR(fsl_edma->dmaclk)) + return dev_err_probe(&pdev->dev, + PTR_ERR(fsl_edma->dmaclk), + "Missing DMA block clock.\n"); } ret = of_property_read_variable_u32_array(np, "dma-channel-mask", chan_mask, 1, 2); @@ -765,11 +763,10 @@ static int fsl_edma_probe(struct platform_device *pdev) sprintf(clkname, "dmamux%d", i); fsl_edma->muxclk[i] = devm_clk_get_enabled(&pdev->dev, clkname); - if (IS_ERR(fsl_edma->muxclk[i])) { - dev_err(&pdev->dev, "Missing DMAMUX block clock.\n"); - /* on error: disable all previously enabled clks */ - return PTR_ERR(fsl_edma->muxclk[i]); - } + if (IS_ERR(fsl_edma->muxclk[i])) + return dev_err_probe(&pdev->dev, + PTR_ERR(fsl_edma->muxclk[i]), + "Missing DMAMUX block clock.\n"); } fsl_edma->big_endian = of_property_read_bool(np, "big-endian"); @@ -878,22 +875,17 @@ static int fsl_edma_probe(struct platform_device *pdev) platform_set_drvdata(pdev, fsl_edma); - ret = dma_async_device_register(&fsl_edma->dma_dev); - if (ret) { - dev_err(&pdev->dev, - "Can't register Freescale eDMA engine. (%d)\n", ret); - return ret; - } + ret = dmaenginem_async_device_register(&fsl_edma->dma_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Can't register Freescale eDMA engine.\n"); - ret = of_dma_controller_register(np, + ret = devm_of_dma_controller_register(&pdev->dev, np, drvdata->dmamuxs ? fsl_edma_xlate : fsl_edma3_xlate, fsl_edma); - if (ret) { - dev_err(&pdev->dev, - "Can't register Freescale eDMA of_dma. (%d)\n", ret); - dma_async_device_unregister(&fsl_edma->dma_dev); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Can't register Freescale eDMA of_dma.\n"); /* enable round robin arbitration */ if (!(drvdata->flags & FSL_EDMA_DRV_SPLIT_REG)) @@ -904,12 +896,9 @@ static int fsl_edma_probe(struct platform_device *pdev) static void fsl_edma_remove(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; struct fsl_edma_engine *fsl_edma = platform_get_drvdata(pdev); fsl_edma_irq_exit(pdev, fsl_edma); - of_dma_controller_free(np); - dma_async_device_unregister(&fsl_edma->dma_dev); fsl_edma_cleanup_vchan(&fsl_edma->dma_dev); } diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c index 0bbff9df362f..df843fad0ece 100644 --- a/drivers/dma/fsl-qdma.c +++ b/drivers/dma/fsl-qdma.c @@ -1127,22 +1127,19 @@ static int fsl_qdma_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; ret = of_property_read_u32(np, "dma-channels", &chans); - if (ret) { - dev_err(&pdev->dev, "Can't get dma-channels.\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Can't get dma-channels.\n"); ret = of_property_read_u32(np, "block-offset", &blk_off); - if (ret) { - dev_err(&pdev->dev, "Can't get block-offset.\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Can't get block-offset.\n"); ret = of_property_read_u32(np, "block-number", &blk_num); - if (ret) { - dev_err(&pdev->dev, "Can't get block-number.\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Can't get block-number.\n"); blk_num = min_t(int, blk_num, num_online_cpus()); @@ -1167,10 +1164,8 @@ static int fsl_qdma_probe(struct platform_device *pdev) return -ENOMEM; ret = of_property_read_u32(np, "fsl,dma-queues", &queues); - if (ret) { - dev_err(&pdev->dev, "Can't get queues.\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Can't get queues.\n"); fsl_qdma->desc_allocated = 0; fsl_qdma->n_chans = chans; @@ -1231,28 +1226,24 @@ static int fsl_qdma_probe(struct platform_device *pdev) fsl_qdma->dma_dev.device_terminate_all = fsl_qdma_terminate_all; ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)); - if (ret) { - dev_err(&pdev->dev, "dma_set_mask failure.\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "dma_set_mask failure.\n"); platform_set_drvdata(pdev, fsl_qdma); ret = fsl_qdma_reg_init(fsl_qdma); - if (ret) { - dev_err(&pdev->dev, "Can't Initialize the qDMA engine.\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Can't Initialize the qDMA engine.\n"); ret = fsl_qdma_irq_init(pdev, fsl_qdma); if (ret) return ret; ret = dma_async_device_register(&fsl_qdma->dma_dev); - if (ret) { - dev_err(&pdev->dev, "Can't register NXP Layerscape qDMA engine.\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Can't register NXP Layerscape qDMA engine.\n"); return 0; } diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 4c8196d78001..3d527883776b 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -2265,34 +2265,24 @@ static int sdma_probe(struct platform_device *pdev) if (IS_ERR(sdma->regs)) return PTR_ERR(sdma->regs); - sdma->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + sdma->clk_ipg = devm_clk_get_prepared(&pdev->dev, "ipg"); if (IS_ERR(sdma->clk_ipg)) return PTR_ERR(sdma->clk_ipg); - sdma->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); + sdma->clk_ahb = devm_clk_get_prepared(&pdev->dev, "ahb"); if (IS_ERR(sdma->clk_ahb)) return PTR_ERR(sdma->clk_ahb); - ret = clk_prepare(sdma->clk_ipg); - if (ret) - return ret; - - ret = clk_prepare(sdma->clk_ahb); - if (ret) - goto err_clk; - ret = devm_request_irq(&pdev->dev, irq, sdma_int_handler, 0, dev_name(&pdev->dev), sdma); if (ret) - goto err_irq; + return ret; sdma->irq = irq; sdma->script_addrs = kzalloc_obj(*sdma->script_addrs); - if (!sdma->script_addrs) { - ret = -ENOMEM; - goto err_irq; - } + if (!sdma->script_addrs) + return -ENOMEM; /* initially no scripts available */ saddr_arr = (s32 *)sdma->script_addrs; @@ -2333,11 +2323,11 @@ static int sdma_probe(struct platform_device *pdev) ret = sdma_init(sdma); if (ret) - goto err_init; + return ret; ret = sdma_event_remap(sdma); if (ret) - goto err_init; + return ret; if (sdma->drvdata->script_addrs) sdma_add_scripts(sdma, sdma->drvdata->script_addrs); @@ -2363,18 +2353,16 @@ static int sdma_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sdma); - ret = dma_async_device_register(&sdma->dma_device); - if (ret) { - dev_err(&pdev->dev, "unable to register\n"); - goto err_init; - } + ret = dmaenginem_async_device_register(&sdma->dma_device); + if (ret) + return dev_err_probe(&pdev->dev, ret, "unable to register\n"); if (np) { - ret = of_dma_controller_register(np, sdma_xlate, sdma); - if (ret) { - dev_err(&pdev->dev, "failed to register controller\n"); - goto err_register; - } + ret = devm_of_dma_controller_register(&pdev->dev, np, + sdma_xlate, sdma); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to register controller\n"); spba_bus = of_find_compatible_node(NULL, NULL, "fsl,spba-bus"); ret = of_address_to_resource(spba_bus, 0, &spba_res); @@ -2401,16 +2389,6 @@ static int sdma_probe(struct platform_device *pdev) } return 0; - -err_register: - dma_async_device_unregister(&sdma->dma_device); -err_init: - kfree(sdma->script_addrs); -err_irq: - clk_unprepare(sdma->clk_ahb); -err_clk: - clk_unprepare(sdma->clk_ipg); - return ret; } static void sdma_remove(struct platform_device *pdev) @@ -2419,10 +2397,6 @@ static void sdma_remove(struct platform_device *pdev) int i; devm_free_irq(&pdev->dev, sdma->irq, sdma); - dma_async_device_unregister(&sdma->dma_device); - kfree(sdma->script_addrs); - clk_unprepare(sdma->clk_ahb); - clk_unprepare(sdma->clk_ipg); /* Kill the tasklet */ for (i = 0; i < MAX_DMA_CHANNELS; i++) { struct sdma_channel *sdmac = &sdma->channel[i]; diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index 12a4a4860a74..e8a880f338c6 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -140,12 +140,6 @@ struct ioatdma_chan { int prev_intr_coalesce; }; -struct ioat_sysfs_entry { - struct attribute attr; - ssize_t (*show)(struct dma_chan *, char *); - ssize_t (*store)(struct dma_chan *, const char *, size_t); -}; - /** * struct ioat_sed_ent - wrapper around super extended hardware descriptor * @hw: hardware SED @@ -195,11 +189,8 @@ struct ioat_ring_ent { struct ioat_sed_ent *sed; }; -extern const struct sysfs_ops ioat_sysfs_ops; -extern struct ioat_sysfs_entry ioat_version_attr; -extern struct ioat_sysfs_entry ioat_cap_attr; extern int ioat_pending_level; -extern struct kobj_type ioat_ktype; +extern const struct kobj_type ioat_ktype; extern struct kmem_cache *ioat_cache; extern struct kmem_cache *ioat_sed_cache; @@ -402,7 +393,7 @@ void ioat_issue_pending(struct dma_chan *chan); /* IOAT Init functions */ bool is_bwd_ioat(struct pci_dev *pdev); struct dca_provider *ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase); -void ioat_kobject_add(struct ioatdma_device *ioat_dma, struct kobj_type *type); +void ioat_kobject_add(struct ioatdma_device *ioat_dma, const struct kobj_type *type); void ioat_kobject_del(struct ioatdma_device *ioat_dma); int ioat_dma_setup_interrupts(struct ioatdma_device *ioat_dma); void ioat_stop(struct ioatdma_chan *ioat_chan); diff --git a/drivers/dma/ioat/sysfs.c b/drivers/dma/ioat/sysfs.c index 168adf28c5b1..e796ddb5383f 100644 --- a/drivers/dma/ioat/sysfs.c +++ b/drivers/dma/ioat/sysfs.c @@ -14,6 +14,12 @@ #include "../dmaengine.h" +struct ioat_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct dma_chan *, char *); + ssize_t (*store)(struct dma_chan *, const char *, size_t); +}; + static ssize_t cap_show(struct dma_chan *c, char *page) { struct dma_device *dma = c->device; @@ -26,7 +32,7 @@ static ssize_t cap_show(struct dma_chan *c, char *page) dma_has_cap(DMA_INTERRUPT, dma->cap_mask) ? " intr" : ""); } -struct ioat_sysfs_entry ioat_cap_attr = __ATTR_RO(cap); +static const struct ioat_sysfs_entry ioat_cap_attr = __ATTR_RO(cap); static ssize_t version_show(struct dma_chan *c, char *page) { @@ -36,15 +42,15 @@ static ssize_t version_show(struct dma_chan *c, char *page) return sprintf(page, "%d.%d\n", ioat_dma->version >> 4, ioat_dma->version & 0xf); } -struct ioat_sysfs_entry ioat_version_attr = __ATTR_RO(version); +static const struct ioat_sysfs_entry ioat_version_attr = __ATTR_RO(version); static ssize_t ioat_attr_show(struct kobject *kobj, struct attribute *attr, char *page) { - struct ioat_sysfs_entry *entry; + const struct ioat_sysfs_entry *entry; struct ioatdma_chan *ioat_chan; - entry = container_of(attr, struct ioat_sysfs_entry, attr); + entry = container_of_const(attr, struct ioat_sysfs_entry, attr); ioat_chan = container_of(kobj, struct ioatdma_chan, kobj); if (!entry->show) @@ -56,10 +62,10 @@ static ssize_t ioat_attr_store(struct kobject *kobj, struct attribute *attr, const char *page, size_t count) { - struct ioat_sysfs_entry *entry; + const struct ioat_sysfs_entry *entry; struct ioatdma_chan *ioat_chan; - entry = container_of(attr, struct ioat_sysfs_entry, attr); + entry = container_of_const(attr, struct ioat_sysfs_entry, attr); ioat_chan = container_of(kobj, struct ioatdma_chan, kobj); if (!entry->store) @@ -67,12 +73,12 @@ const char *page, size_t count) return entry->store(&ioat_chan->dma_chan, page, count); } -const struct sysfs_ops ioat_sysfs_ops = { +static const struct sysfs_ops ioat_sysfs_ops = { .show = ioat_attr_show, .store = ioat_attr_store, }; -void ioat_kobject_add(struct ioatdma_device *ioat_dma, struct kobj_type *type) +void ioat_kobject_add(struct ioatdma_device *ioat_dma, const struct kobj_type *type) { struct dma_device *dma = &ioat_dma->dma_dev; struct dma_chan *c; @@ -114,7 +120,7 @@ static ssize_t ring_size_show(struct dma_chan *c, char *page) return sprintf(page, "%d\n", (1 << ioat_chan->alloc_order) & ~1); } -static struct ioat_sysfs_entry ring_size_attr = __ATTR_RO(ring_size); +static const struct ioat_sysfs_entry ring_size_attr = __ATTR_RO(ring_size); static ssize_t ring_active_show(struct dma_chan *c, char *page) { @@ -123,7 +129,7 @@ static ssize_t ring_active_show(struct dma_chan *c, char *page) /* ...taken outside the lock, no need to be precise */ return sprintf(page, "%d\n", ioat_ring_active(ioat_chan)); } -static struct ioat_sysfs_entry ring_active_attr = __ATTR_RO(ring_active); +static const struct ioat_sysfs_entry ring_active_attr = __ATTR_RO(ring_active); static ssize_t intr_coalesce_show(struct dma_chan *c, char *page) { @@ -148,9 +154,9 @@ size_t count) return count; } -static struct ioat_sysfs_entry intr_coalesce_attr = __ATTR_RW(intr_coalesce); +static const struct ioat_sysfs_entry intr_coalesce_attr = __ATTR_RW(intr_coalesce); -static struct attribute *ioat_attrs[] = { +static const struct attribute *const ioat_attrs[] = { &ring_size_attr.attr, &ring_active_attr.attr, &ioat_cap_attr.attr, @@ -160,7 +166,7 @@ static struct attribute *ioat_attrs[] = { }; ATTRIBUTE_GROUPS(ioat); -struct kobj_type ioat_ktype = { +const struct kobj_type ioat_ktype = { .sysfs_ops = &ioat_sysfs_ops, .default_groups = ioat_groups, }; diff --git a/drivers/dma/loongson/Kconfig b/drivers/dma/loongson/Kconfig new file mode 100644 index 000000000000..c4e62dce5d4f --- /dev/null +++ b/drivers/dma/loongson/Kconfig @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Loongson DMA controllers drivers +# +if MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST + +config LOONGSON1_APB_DMA + tristate "Loongson1 APB DMA support" + depends on MACH_LOONGSON32 || COMPILE_TEST + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + This selects support for the APB DMA controller in Loongson1 SoCs, + which is required by Loongson1 NAND and audio support. + +config LOONGSON2_APB_DMA + tristate "Loongson2 APB DMA support" + depends on MACH_LOONGSON64 || COMPILE_TEST + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Support for the Loongson2 APB DMA controller driver. The + DMA controller is having single DMA channel which can be + configured for different peripherals like audio, nand, sdio + etc which is in APB bus. + + This DMA controller transfers data from memory to peripheral fifo. + It does not support memory to memory data transfer. + +config LOONGSON2_APB_CMC_DMA + tristate "Loongson2 Chain Multi-Channel DMA support" + depends on MACH_LOONGSON64 || COMPILE_TEST + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Support for the Loongson Chain Multi-Channel DMA controller driver. + It is discovered on the Loongson-2K chip (Loongson-2K0300/Loongson-2K3000), + which has 4/8 channels internally, enabling bidirectional data transfer + between devices and memory. + +endif diff --git a/drivers/dma/loongson/Makefile b/drivers/dma/loongson/Makefile new file mode 100644 index 000000000000..48c19781e729 --- /dev/null +++ b/drivers/dma/loongson/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o +obj-$(CONFIG_LOONGSON2_APB_DMA) += loongson2-apb-dma.o +obj-$(CONFIG_LOONGSON2_APB_CMC_DMA) += loongson2-apb-cmc-dma.o diff --git a/drivers/dma/loongson1-apb-dma.c b/drivers/dma/loongson/loongson1-apb-dma.c similarity index 99% rename from drivers/dma/loongson1-apb-dma.c rename to drivers/dma/loongson/loongson1-apb-dma.c index 2e347aba9af8..89786cbd20ab 100644 --- a/drivers/dma/loongson1-apb-dma.c +++ b/drivers/dma/loongson/loongson1-apb-dma.c @@ -16,8 +16,8 @@ #include #include -#include "dmaengine.h" -#include "virt-dma.h" +#include "../dmaengine.h" +#include "../virt-dma.h" /* Loongson-1 DMA Control Register */ #define LS1X_DMA_CTRL 0x0 diff --git a/drivers/dma/loongson/loongson2-apb-cmc-dma.c b/drivers/dma/loongson/loongson2-apb-cmc-dma.c new file mode 100644 index 000000000000..1c9a542edc85 --- /dev/null +++ b/drivers/dma/loongson/loongson2-apb-cmc-dma.c @@ -0,0 +1,730 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Loongson-2 Chain Multi-Channel DMA Controller driver + * + * Copyright (C) 2024-2026 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../dmaengine.h" +#include "../virt-dma.h" + +#define LOONGSON2_CMCDMA_ISR 0x0 /* DMA Interrupt Status Register */ +#define LOONGSON2_CMCDMA_IFCR 0x4 /* DMA Interrupt Flag Clear Register */ +#define LOONGSON2_CMCDMA_CCR 0x8 /* DMA Channel Configuration Register */ +#define LOONGSON2_CMCDMA_CNDTR 0xc /* DMA Channel Transmit Count Register */ +#define LOONGSON2_CMCDMA_CPAR 0x10 /* DMA Channel Peripheral Address Register */ +#define LOONGSON2_CMCDMA_CMAR 0x14 /* DMA Channel Memory Address Register */ + +/* Bitfields of DMA interrupt status register */ +#define LOONGSON2_CMCDMA_TCI BIT(1) /* Transfer Complete Interrupt */ +#define LOONGSON2_CMCDMA_HTI BIT(2) /* Half Transfer Interrupt */ +#define LOONGSON2_CMCDMA_TEI BIT(3) /* Transfer Error Interrupt */ + +#define LOONGSON2_CMCDMA_MASKI \ + (LOONGSON2_CMCDMA_TCI | LOONGSON2_CMCDMA_HTI | LOONGSON2_CMCDMA_TEI) + +/* Bitfields of DMA channel x Configuration Register */ +#define LOONGSON2_CMCDMA_CCR_EN BIT(0) /* Stream Enable */ +#define LOONGSON2_CMCDMA_CCR_TCIE BIT(1) /* Transfer Complete Interrupt Enable */ +#define LOONGSON2_CMCDMA_CCR_HTIE BIT(2) /* Half Transfer Complete Interrupt Enable */ +#define LOONGSON2_CMCDMA_CCR_TEIE BIT(3) /* Transfer Error Interrupt Enable */ +#define LOONGSON2_CMCDMA_CCR_DIR BIT(4) /* Data Transfer Direction */ +#define LOONGSON2_CMCDMA_CCR_CIRC BIT(5) /* Circular mode */ +#define LOONGSON2_CMCDMA_CCR_PINC BIT(6) /* Peripheral increment mode */ +#define LOONGSON2_CMCDMA_CCR_MINC BIT(7) /* Memory increment mode */ +#define LOONGSON2_CMCDMA_CCR_PSIZE_MASK GENMASK(9, 8) +#define LOONGSON2_CMCDMA_CCR_MSIZE_MASK GENMASK(11, 10) +#define LOONGSON2_CMCDMA_CCR_PL_MASK GENMASK(13, 12) +#define LOONGSON2_CMCDMA_CCR_M2M BIT(14) + +#define LOONGSON2_CMCDMA_CCR_CFG_MASK \ + (LOONGSON2_CMCDMA_CCR_PINC | LOONGSON2_CMCDMA_CCR_MINC | LOONGSON2_CMCDMA_CCR_PL_MASK) + +#define LOONGSON2_CMCDMA_CCR_IRQ_MASK \ + (LOONGSON2_CMCDMA_CCR_TCIE | LOONGSON2_CMCDMA_CCR_HTIE | LOONGSON2_CMCDMA_CCR_TEIE) + +#define LOONGSON2_CMCDMA_STREAM_MASK \ + (LOONGSON2_CMCDMA_CCR_CFG_MASK | LOONGSON2_CMCDMA_CCR_IRQ_MASK) + +#define LOONGSON2_CMCDMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) + +#define LOONSON2_CMCDMA_MAX_DATA_ITEMS SZ_64K + +struct loongson2_cmc_dma_chan_reg { + u32 ccr; + u32 cndtr; + u32 cpar; + u32 cmar; +}; + +struct loongson2_cmc_dma_sg_req { + u32 len; + struct loongson2_cmc_dma_chan_reg chan_reg; +}; + +struct loongson2_cmc_dma_desc { + struct virt_dma_desc vdesc; + bool cyclic; + u32 num_sgs; + struct loongson2_cmc_dma_sg_req sg_req[] __counted_by(num_sgs); +}; + +struct loongson2_cmc_dma_chan { + struct virt_dma_chan vchan; + struct dma_slave_config dma_sconfig; + struct loongson2_cmc_dma_desc *desc; + u32 id; + u32 irq; + u32 next_sg; + struct loongson2_cmc_dma_chan_reg chan_reg; +}; + +struct loongson2_cmc_dma_dev { + struct dma_device ddev; + struct clk *dma_clk; + void __iomem *base; + u32 nr_channels; + u32 chan_reg_offset; + struct loongson2_cmc_dma_chan chan[] __counted_by(nr_channels); +}; + +struct loongson2_cmc_dma_config { + u32 max_channels; + u32 chan_reg_offset; +}; + +static const struct loongson2_cmc_dma_config ls2k0300_cmc_dma_config = { + .max_channels = 8, + .chan_reg_offset = 0x14, +}; + +static const struct loongson2_cmc_dma_config ls2k3000_cmc_dma_config = { + .max_channels = 4, + .chan_reg_offset = 0x18, +}; + +static struct loongson2_cmc_dma_dev *lmdma_get_dev(struct loongson2_cmc_dma_chan *lchan) +{ + return container_of(lchan->vchan.chan.device, struct loongson2_cmc_dma_dev, ddev); +} + +static struct loongson2_cmc_dma_chan *to_lmdma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct loongson2_cmc_dma_chan, vchan.chan); +} + +static struct loongson2_cmc_dma_desc *to_lmdma_desc(struct virt_dma_desc *vdesc) +{ + return container_of(vdesc, struct loongson2_cmc_dma_desc, vdesc); +} + +static struct device *chan2dev(struct loongson2_cmc_dma_chan *lchan) +{ + return &lchan->vchan.chan.dev->device; +} + +static u32 loongson2_cmc_dma_read(struct loongson2_cmc_dma_dev *lddev, u32 reg, u32 id) +{ + return readl(lddev->base + (reg + lddev->chan_reg_offset * id)); +} + +static void loongson2_cmc_dma_write(struct loongson2_cmc_dma_dev *lddev, u32 reg, u32 id, u32 val) +{ + writel(val, lddev->base + (reg + lddev->chan_reg_offset * id)); +} + +static int loongson2_cmc_dma_get_width(enum dma_slave_buswidth width) +{ + switch (width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + case DMA_SLAVE_BUSWIDTH_2_BYTES: + case DMA_SLAVE_BUSWIDTH_4_BYTES: + return ffs(width) - 1; + default: + return -EINVAL; + } +} + +static int loongson2_cmc_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *config) +{ + struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan); + + memcpy(&lchan->dma_sconfig, config, sizeof(*config)); + + return 0; +} + +static void loongson2_cmc_dma_irq_clear(struct loongson2_cmc_dma_chan *lchan, u32 flags) +{ + struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan); + u32 ifcr; + + ifcr = flags << (4 * lchan->id); + loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_IFCR, 0, ifcr); +} + +static void loongson2_cmc_dma_stop(struct loongson2_cmc_dma_chan *lchan) +{ + struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan); + u32 ccr; + + ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, lchan->id); + ccr &= ~(LOONGSON2_CMCDMA_CCR_IRQ_MASK | LOONGSON2_CMCDMA_CCR_EN); + loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, lchan->id, ccr); + + loongson2_cmc_dma_irq_clear(lchan, LOONGSON2_CMCDMA_MASKI); +} + +static int loongson2_cmc_dma_terminate_all(struct dma_chan *chan) +{ + struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan); + + LIST_HEAD(head); + + scoped_guard(spinlock_irqsave, &lchan->vchan.lock) { + if (lchan->desc) { + vchan_terminate_vdesc(&lchan->desc->vdesc); + loongson2_cmc_dma_stop(lchan); + lchan->desc = NULL; + } + vchan_get_all_descriptors(&lchan->vchan, &head); + } + + vchan_dma_desc_free_list(&lchan->vchan, &head); + + return 0; +} + +static void loongson2_cmc_dma_synchronize(struct dma_chan *chan) +{ + struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan); + + vchan_synchronize(&lchan->vchan); +} + +static void loongson2_cmc_dma_start_transfer(struct loongson2_cmc_dma_chan *lchan) +{ + struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan); + struct loongson2_cmc_dma_sg_req *sg_req; + struct loongson2_cmc_dma_chan_reg *reg; + struct virt_dma_desc *vdesc; + + loongson2_cmc_dma_stop(lchan); + + if (!lchan->desc) { + vdesc = vchan_next_desc(&lchan->vchan); + if (!vdesc) + return; + + list_del(&vdesc->node); + lchan->desc = to_lmdma_desc(vdesc); + lchan->next_sg = 0; + } + + if (lchan->next_sg == lchan->desc->num_sgs) + lchan->next_sg = 0; + + sg_req = &lchan->desc->sg_req[lchan->next_sg]; + reg = &sg_req->chan_reg; + + loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, lchan->id, reg->ccr); + loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CNDTR, lchan->id, reg->cndtr); + loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CPAR, lchan->id, reg->cpar); + loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CMAR, lchan->id, reg->cmar); + + lchan->next_sg++; + + /* Start DMA */ + reg->ccr |= LOONGSON2_CMCDMA_CCR_EN; + loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, lchan->id, reg->ccr); +} + +static void loongson2_cmc_dma_configure_next_sg(struct loongson2_cmc_dma_chan *lchan) +{ + struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan); + struct loongson2_cmc_dma_sg_req *sg_req; + u32 ccr, id = lchan->id; + + if (lchan->next_sg == lchan->desc->num_sgs) + lchan->next_sg = 0; + + /* Stop to update mem addr */ + ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, id); + ccr &= ~LOONGSON2_CMCDMA_CCR_EN; + loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, id, ccr); + + sg_req = &lchan->desc->sg_req[lchan->next_sg]; + loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CMAR, id, sg_req->chan_reg.cmar); + + /* Start transition */ + ccr |= LOONGSON2_CMCDMA_CCR_EN; + loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, id, ccr); +} + +static void loongson2_cmc_dma_handle_chan_done(struct loongson2_cmc_dma_chan *lchan) +{ + if (!lchan->desc) + return; + + if (lchan->desc->cyclic) { + vchan_cyclic_callback(&lchan->desc->vdesc); + /* LOONGSON2_CMCDMA_CCR_CIRC mode don't need update register */ + if (lchan->desc->num_sgs == 1) + return; + loongson2_cmc_dma_configure_next_sg(lchan); + lchan->next_sg++; + } else { + if (lchan->next_sg == lchan->desc->num_sgs) { + vchan_cookie_complete(&lchan->desc->vdesc); + lchan->desc = NULL; + } + loongson2_cmc_dma_start_transfer(lchan); + } +} + +static irqreturn_t loongson2_cmc_dma_chan_irq(int irq, void *devid) +{ + struct loongson2_cmc_dma_chan *lchan = devid; + struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan); + struct device *dev = chan2dev(lchan); + u32 ists, status, ccr; + + scoped_guard(spinlock, &lchan->vchan.lock) { + ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, lchan->id); + ists = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_ISR, 0); + status = (ists >> (4 * lchan->id)) & LOONGSON2_CMCDMA_MASKI; + + loongson2_cmc_dma_irq_clear(lchan, status); + + if (status & LOONGSON2_CMCDMA_TCI) { + loongson2_cmc_dma_handle_chan_done(lchan); + status &= ~LOONGSON2_CMCDMA_TCI; + } + + if (status & LOONGSON2_CMCDMA_HTI) + status &= ~LOONGSON2_CMCDMA_HTI; + + if (status & LOONGSON2_CMCDMA_TEI) { + dev_err(dev, "DMA Transform Error.\n"); + if (!(ccr & LOONGSON2_CMCDMA_CCR_EN)) + dev_err(dev, "Channel disabled by HW.\n"); + } + } + + return IRQ_HANDLED; +} + +static void loongson2_cmc_dma_issue_pending(struct dma_chan *chan) +{ + struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan); + + guard(spinlock_irqsave)(&lchan->vchan.lock); + + if (vchan_issue_pending(&lchan->vchan) && !lchan->desc) { + dev_dbg(chan2dev(lchan), "vchan %pK: issued\n", &lchan->vchan); + loongson2_cmc_dma_start_transfer(lchan); + } +} + +static int loongson2_cmc_dma_set_xfer_param(struct loongson2_cmc_dma_chan *lchan, + enum dma_transfer_direction direction, + enum dma_slave_buswidth *buswidth, u32 buf_len) +{ + struct dma_slave_config sconfig = lchan->dma_sconfig; + struct device *dev = chan2dev(lchan); + int dev_width; + u32 ccr; + + switch (direction) { + case DMA_MEM_TO_DEV: + dev_width = loongson2_cmc_dma_get_width(sconfig.dst_addr_width); + if (dev_width < 0) { + dev_err(dev, "DMA_MEM_TO_DEV bus width not supported\n"); + return dev_width; + } + lchan->chan_reg.cpar = sconfig.dst_addr; + ccr = LOONGSON2_CMCDMA_CCR_DIR; + *buswidth = sconfig.dst_addr_width; + break; + case DMA_DEV_TO_MEM: + dev_width = loongson2_cmc_dma_get_width(sconfig.src_addr_width); + if (dev_width < 0) { + dev_err(dev, "DMA_DEV_TO_MEM bus width not supported\n"); + return dev_width; + } + lchan->chan_reg.cpar = sconfig.src_addr; + ccr = LOONGSON2_CMCDMA_CCR_MINC; + *buswidth = sconfig.src_addr_width; + break; + default: + return -EINVAL; + } + + ccr |= FIELD_PREP(LOONGSON2_CMCDMA_CCR_PSIZE_MASK, dev_width) | + FIELD_PREP(LOONGSON2_CMCDMA_CCR_MSIZE_MASK, dev_width); + + /* Set DMA control register */ + lchan->chan_reg.ccr &= ~(LOONGSON2_CMCDMA_CCR_PSIZE_MASK | LOONGSON2_CMCDMA_CCR_MSIZE_MASK); + lchan->chan_reg.ccr |= ccr; + + return 0; +} + +static struct dma_async_tx_descriptor * +loongson2_cmc_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, u32 sg_len, + enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan); + struct loongson2_cmc_dma_desc *desc; + enum dma_slave_buswidth buswidth; + struct scatterlist *sg; + u32 num_items, i; + int ret; + + desc = kzalloc_flex(*desc, sg_req, sg_len, GFP_NOWAIT); + if (!desc) + return ERR_PTR(-ENOMEM); + + for_each_sg(sgl, sg, sg_len, i) { + ret = loongson2_cmc_dma_set_xfer_param(lchan, direction, &buswidth, sg_dma_len(sg)); + if (ret) + return ERR_PTR(ret); + + num_items = DIV_ROUND_UP(sg_dma_len(sg), buswidth); + if (num_items >= LOONSON2_CMCDMA_MAX_DATA_ITEMS) { + dev_err(chan2dev(lchan), "Number of items not supported\n"); + kfree(desc); + return ERR_PTR(-EINVAL); + } + + desc->sg_req[i].len = sg_dma_len(sg); + desc->sg_req[i].chan_reg.ccr = lchan->chan_reg.ccr; + desc->sg_req[i].chan_reg.cpar = lchan->chan_reg.cpar; + desc->sg_req[i].chan_reg.cmar = sg_dma_address(sg); + desc->sg_req[i].chan_reg.cndtr = num_items; + } + + desc->num_sgs = sg_len; + desc->cyclic = false; + + return vchan_tx_prep(&lchan->vchan, &desc->vdesc, flags); +} + +static struct dma_async_tx_descriptor * +loongson2_cmc_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags) +{ + struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan); + struct loongson2_cmc_dma_desc *desc; + enum dma_slave_buswidth buswidth; + u32 num_periods, num_items, i; + int ret; + + if (unlikely(buf_len % period_len)) + return ERR_PTR(-EINVAL); + + ret = loongson2_cmc_dma_set_xfer_param(lchan, direction, &buswidth, period_len); + if (ret) + return ERR_PTR(ret); + + num_items = DIV_ROUND_UP(period_len, buswidth); + if (num_items >= LOONSON2_CMCDMA_MAX_DATA_ITEMS) { + dev_err(chan2dev(lchan), "Number of items not supported\n"); + return ERR_PTR(-EINVAL); + } + + /* Enable Circular mode */ + if (buf_len == period_len) + lchan->chan_reg.ccr |= LOONGSON2_CMCDMA_CCR_CIRC; + + num_periods = DIV_ROUND_UP(buf_len, period_len); + desc = kzalloc_flex(*desc, sg_req, num_periods, GFP_NOWAIT); + if (!desc) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < num_periods; i++) { + desc->sg_req[i].len = period_len; + desc->sg_req[i].chan_reg.ccr = lchan->chan_reg.ccr; + desc->sg_req[i].chan_reg.cpar = lchan->chan_reg.cpar; + desc->sg_req[i].chan_reg.cmar = buf_addr; + desc->sg_req[i].chan_reg.cndtr = num_items; + buf_addr += period_len; + } + + desc->num_sgs = num_periods; + desc->cyclic = true; + + return vchan_tx_prep(&lchan->vchan, &desc->vdesc, flags); +} + +static size_t loongson2_cmc_dma_desc_residue(struct loongson2_cmc_dma_chan *lchan, + struct loongson2_cmc_dma_desc *desc, u32 next_sg) +{ + struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan); + u32 residue, width, ndtr, ccr, i; + + ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, lchan->id); + width = FIELD_GET(LOONGSON2_CMCDMA_CCR_PSIZE_MASK, ccr); + + ndtr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CNDTR, lchan->id); + residue = ndtr << width; + + if (lchan->desc->cyclic && next_sg == 0) + return residue; + + for (i = next_sg; i < desc->num_sgs; i++) + residue += desc->sg_req[i].len; + + return residue; +} + +static enum dma_status loongson2_cmc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + struct dma_tx_state *state) +{ + struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan); + struct virt_dma_desc *vdesc; + enum dma_status status; + + status = dma_cookie_status(chan, cookie, state); + if (status == DMA_COMPLETE || !state) + return status; + + scoped_guard(spinlock_irqsave, &lchan->vchan.lock) { + vdesc = vchan_find_desc(&lchan->vchan, cookie); + if (lchan->desc && cookie == lchan->desc->vdesc.tx.cookie) + state->residue = loongson2_cmc_dma_desc_residue(lchan, lchan->desc, + lchan->next_sg); + else if (vdesc) + state->residue = loongson2_cmc_dma_desc_residue(lchan, + to_lmdma_desc(vdesc), 0); + } + + return status; +} + +static void loongson2_cmc_dma_free_chan_resources(struct dma_chan *chan) +{ + vchan_free_chan_resources(to_virt_chan(chan)); +} + +static void loongson2_cmc_dma_desc_free(struct virt_dma_desc *vdesc) +{ + kfree(to_lmdma_desc(vdesc)); +} + +static bool loongson2_cmc_dma_acpi_filter(struct dma_chan *chan, void *param) +{ + struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan); + struct acpi_dma_spec *dma_spec = param; + + memset(&lchan->chan_reg, 0, sizeof(struct loongson2_cmc_dma_chan_reg)); + lchan->chan_reg.ccr = dma_spec->chan_id & LOONGSON2_CMCDMA_STREAM_MASK; + + return true; +} + +static int loongson2_cmc_dma_acpi_controller_register(struct loongson2_cmc_dma_dev *lddev) +{ + struct device *dev = lddev->ddev.dev; + struct acpi_dma_filter_info *info; + + if (!is_acpi_node(dev_fwnode(dev))) + return 0; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + dma_cap_zero(info->dma_cap); + info->dma_cap = lddev->ddev.cap_mask; + info->filter_fn = loongson2_cmc_dma_acpi_filter; + + return devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate, info); +} + +static struct dma_chan *loongson2_cmc_dma_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct loongson2_cmc_dma_dev *lddev = ofdma->of_dma_data; + struct device *dev = lddev->ddev.dev; + struct loongson2_cmc_dma_chan *lchan; + struct dma_chan *chan; + + if (dma_spec->args_count < 2) + return ERR_PTR(-EINVAL); + + if (dma_spec->args[0] >= lddev->nr_channels) { + dev_err(dev, "Invalid channel id.\n"); + return ERR_PTR(-EINVAL); + } + + lchan = &lddev->chan[dma_spec->args[0]]; + chan = dma_get_slave_channel(&lchan->vchan.chan); + if (!chan) { + dev_err(dev, "No more channels available.\n"); + return ERR_PTR(-EINVAL); + } + + memset(&lchan->chan_reg, 0, sizeof(struct loongson2_cmc_dma_chan_reg)); + lchan->chan_reg.ccr = dma_spec->args[1] & LOONGSON2_CMCDMA_STREAM_MASK; + + return chan; +} + +static int loongson2_cmc_dma_of_controller_register(struct loongson2_cmc_dma_dev *lddev) +{ + struct device *dev = lddev->ddev.dev; + + if (!is_of_node(dev_fwnode(dev))) + return 0; + + return of_dma_controller_register(dev->of_node, loongson2_cmc_dma_of_xlate, lddev); +} + +static int loongson2_cmc_dma_probe(struct platform_device *pdev) +{ + const struct loongson2_cmc_dma_config *config; + struct loongson2_cmc_dma_chan *lchan; + struct loongson2_cmc_dma_dev *lddev; + struct device *dev = &pdev->dev; + struct dma_device *ddev; + u32 nr_chans, i; + int ret; + + config = (const struct loongson2_cmc_dma_config *)device_get_match_data(dev); + if (!config) + return -EINVAL; + + ret = device_property_read_u32(dev, "dma-channels", &nr_chans); + if (ret || nr_chans > config->max_channels) { + dev_err(dev, "missing or invalid dma-channels property\n"); + nr_chans = config->max_channels; + } + + lddev = devm_kzalloc(dev, struct_size(lddev, chan, nr_chans), GFP_KERNEL); + if (!lddev) + return -ENOMEM; + + lddev->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(lddev->base)) + return PTR_ERR(lddev->base); + + platform_set_drvdata(pdev, lddev); + lddev->nr_channels = nr_chans; + lddev->chan_reg_offset = config->chan_reg_offset; + + lddev->dma_clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(lddev->dma_clk)) + return dev_err_probe(dev, PTR_ERR(lddev->dma_clk), "Failed to get dma clock\n"); + + ddev = &lddev->ddev; + ddev->dev = dev; + + dma_cap_zero(ddev->cap_mask); + dma_cap_set(DMA_SLAVE, ddev->cap_mask); + dma_cap_set(DMA_PRIVATE, ddev->cap_mask); + dma_cap_set(DMA_CYCLIC, ddev->cap_mask); + + ddev->device_free_chan_resources = loongson2_cmc_dma_free_chan_resources; + ddev->device_config = loongson2_cmc_dma_slave_config; + ddev->device_prep_slave_sg = loongson2_cmc_dma_prep_slave_sg; + ddev->device_prep_dma_cyclic = loongson2_cmc_dma_prep_dma_cyclic; + ddev->device_issue_pending = loongson2_cmc_dma_issue_pending; + ddev->device_synchronize = loongson2_cmc_dma_synchronize; + ddev->device_tx_status = loongson2_cmc_dma_tx_status; + ddev->device_terminate_all = loongson2_cmc_dma_terminate_all; + + ddev->max_sg_burst = LOONSON2_CMCDMA_MAX_DATA_ITEMS; + ddev->src_addr_widths = LOONGSON2_CMCDMA_BUSWIDTHS; + ddev->dst_addr_widths = LOONGSON2_CMCDMA_BUSWIDTHS; + ddev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + INIT_LIST_HEAD(&ddev->channels); + + for (i = 0; i < nr_chans; i++) { + lchan = &lddev->chan[i]; + + lchan->id = i; + lchan->vchan.desc_free = loongson2_cmc_dma_desc_free; + vchan_init(&lchan->vchan, ddev); + } + + ret = dmaenginem_async_device_register(ddev); + if (ret) + return dev_err_probe(dev, ret, "Failed to register DMA engine device.\n"); + + for (i = 0; i < nr_chans; i++) { + lchan = &lddev->chan[i]; + + lchan->irq = platform_get_irq(pdev, i); + if (lchan->irq < 0) + return lchan->irq; + + ret = devm_request_irq(dev, lchan->irq, loongson2_cmc_dma_chan_irq, IRQF_SHARED, + dev_name(chan2dev(lchan)), lchan); + if (ret) + return ret; + } + + ret = loongson2_cmc_dma_acpi_controller_register(lddev); + if (ret) + return dev_err_probe(dev, ret, "Failed to register dma controller with ACPI.\n"); + + ret = loongson2_cmc_dma_of_controller_register(lddev); + if (ret) + return dev_err_probe(dev, ret, "Failed to register dma controller with FDT.\n"); + + dev_info(dev, "Loongson-2 Multi-Channel DMA Controller registered successfully.\n"); + + return 0; +} + +static void loongson2_cmc_dma_remove(struct platform_device *pdev) +{ + of_dma_controller_free(pdev->dev.of_node); +} + +static const struct of_device_id loongson2_cmc_dma_of_match[] = { + { .compatible = "loongson,ls2k0300-dma", .data = &ls2k0300_cmc_dma_config }, + { .compatible = "loongson,ls2k3000-dma", .data = &ls2k3000_cmc_dma_config }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, loongson2_cmc_dma_of_match); + +static const struct acpi_device_id loongson2_cmc_dma_acpi_match[] = { + { "LOON0014", .driver_data = (kernel_ulong_t)&ls2k3000_cmc_dma_config }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, loongson2_cmc_dma_acpi_match); + +static struct platform_driver loongson2_cmc_dma_driver = { + .driver = { + .name = "loongson2-apb-cmc-dma", + .of_match_table = loongson2_cmc_dma_of_match, + .acpi_match_table = loongson2_cmc_dma_acpi_match, + }, + .probe = loongson2_cmc_dma_probe, + .remove = loongson2_cmc_dma_remove, +}; +module_platform_driver(loongson2_cmc_dma_driver); + +MODULE_DESCRIPTION("Loongson-2 Chain Multi-Channel DMA Controller driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma/loongson2-apb-dma.c b/drivers/dma/loongson/loongson2-apb-dma.c similarity index 91% rename from drivers/dma/loongson2-apb-dma.c rename to drivers/dma/loongson/loongson2-apb-dma.c index b981475e6779..aceb069e71fc 100644 --- a/drivers/dma/loongson2-apb-dma.c +++ b/drivers/dma/loongson/loongson2-apb-dma.c @@ -17,8 +17,8 @@ #include #include -#include "dmaengine.h" -#include "virt-dma.h" +#include "../dmaengine.h" +#include "../virt-dma.h" /* Global Configuration Register */ #define LDMA_ORDER_ERG 0x0 @@ -461,12 +461,11 @@ static int ls2x_dma_slave_config(struct dma_chan *chan, static void ls2x_dma_issue_pending(struct dma_chan *chan) { struct ls2x_dma_chan *lchan = to_ldma_chan(chan); - unsigned long flags; - spin_lock_irqsave(&lchan->vchan.lock, flags); + guard(spinlock_irqsave)(&lchan->vchan.lock); + if (vchan_issue_pending(&lchan->vchan) && !lchan->desc) ls2x_dma_start_transfer(lchan); - spin_unlock_irqrestore(&lchan->vchan.lock, flags); } /* @@ -478,19 +477,18 @@ static void ls2x_dma_issue_pending(struct dma_chan *chan) static int ls2x_dma_terminate_all(struct dma_chan *chan) { struct ls2x_dma_chan *lchan = to_ldma_chan(chan); - unsigned long flags; LIST_HEAD(head); - spin_lock_irqsave(&lchan->vchan.lock, flags); - /* Setting stop cmd */ - ls2x_dma_write_cmd(lchan, LDMA_STOP); - if (lchan->desc) { - vchan_terminate_vdesc(&lchan->desc->vdesc); - lchan->desc = NULL; - } + scoped_guard(spinlock_irqsave, &lchan->vchan.lock) { + /* Setting stop cmd */ + ls2x_dma_write_cmd(lchan, LDMA_STOP); + if (lchan->desc) { + vchan_terminate_vdesc(&lchan->desc->vdesc); + lchan->desc = NULL; + } - vchan_get_all_descriptors(&lchan->vchan, &head); - spin_unlock_irqrestore(&lchan->vchan.lock, flags); + vchan_get_all_descriptors(&lchan->vchan, &head); + } vchan_dma_desc_free_list(&lchan->vchan, &head); return 0; @@ -511,14 +509,13 @@ static void ls2x_dma_synchronize(struct dma_chan *chan) static int ls2x_dma_pause(struct dma_chan *chan) { struct ls2x_dma_chan *lchan = to_ldma_chan(chan); - unsigned long flags; - spin_lock_irqsave(&lchan->vchan.lock, flags); + guard(spinlock_irqsave)(&lchan->vchan.lock); + if (lchan->desc && lchan->desc->status == DMA_IN_PROGRESS) { ls2x_dma_write_cmd(lchan, LDMA_STOP); lchan->desc->status = DMA_PAUSED; } - spin_unlock_irqrestore(&lchan->vchan.lock, flags); return 0; } @@ -526,14 +523,13 @@ static int ls2x_dma_pause(struct dma_chan *chan) static int ls2x_dma_resume(struct dma_chan *chan) { struct ls2x_dma_chan *lchan = to_ldma_chan(chan); - unsigned long flags; - spin_lock_irqsave(&lchan->vchan.lock, flags); + guard(spinlock_irqsave)(&lchan->vchan.lock); + if (lchan->desc && lchan->desc->status == DMA_PAUSED) { lchan->desc->status = DMA_IN_PROGRESS; ls2x_dma_write_cmd(lchan, LDMA_START); } - spin_unlock_irqrestore(&lchan->vchan.lock, flags); return 0; } @@ -550,22 +546,22 @@ static irqreturn_t ls2x_dma_isr(int irq, void *dev_id) struct ls2x_dma_chan *lchan = dev_id; struct ls2x_dma_desc *desc; - spin_lock(&lchan->vchan.lock); - desc = lchan->desc; - if (desc) { - if (desc->cyclic) { - vchan_cyclic_callback(&desc->vdesc); - } else { - desc->status = DMA_COMPLETE; - vchan_cookie_complete(&desc->vdesc); - ls2x_dma_start_transfer(lchan); - } + scoped_guard(spinlock, &lchan->vchan.lock) { + desc = lchan->desc; + if (desc) { + if (desc->cyclic) { + vchan_cyclic_callback(&desc->vdesc); + } else { + desc->status = DMA_COMPLETE; + vchan_cookie_complete(&desc->vdesc); + ls2x_dma_start_transfer(lchan); + } - /* ls2x_dma_start_transfer() updates lchan->desc */ - if (!lchan->desc) - ls2x_dma_write_cmd(lchan, LDMA_STOP); + /* ls2x_dma_start_transfer() updates lchan->desc */ + if (!lchan->desc) + ls2x_dma_write_cmd(lchan, LDMA_STOP); + } } - spin_unlock(&lchan->vchan.lock); return IRQ_HANDLED; } @@ -616,17 +612,13 @@ static int ls2x_dma_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(priv->regs), "devm_platform_ioremap_resource failed.\n"); - priv->dma_clk = devm_clk_get(&pdev->dev, NULL); + priv->dma_clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(priv->dma_clk)) - return dev_err_probe(dev, PTR_ERR(priv->dma_clk), "devm_clk_get failed.\n"); - - ret = clk_prepare_enable(priv->dma_clk); - if (ret) - return dev_err_probe(dev, ret, "clk_prepare_enable failed.\n"); + return dev_err_probe(dev, PTR_ERR(priv->dma_clk), "Couldn't start the clock.\n"); ret = ls2x_dma_chan_init(pdev, priv); if (ret) - goto disable_clk; + return ret; ddev = &priv->ddev; ddev->dev = dev; @@ -650,25 +642,18 @@ static int ls2x_dma_probe(struct platform_device *pdev) ddev->dst_addr_widths = LDMA_SLAVE_BUSWIDTHS; ddev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); - ret = dma_async_device_register(&priv->ddev); + ret = dmaenginem_async_device_register(&priv->ddev); if (ret < 0) - goto disable_clk; + return dev_err_probe(dev, ret, "Failed to register DMA engine device.\n"); ret = of_dma_controller_register(dev->of_node, of_dma_xlate_by_chan_id, priv); if (ret < 0) - goto unregister_dmac; + return dev_err_probe(dev, ret, "Failed to register dma controller.\n"); platform_set_drvdata(pdev, priv); dev_info(dev, "Loongson LS2X APB DMA driver registered successfully.\n"); return 0; - -unregister_dmac: - dma_async_device_unregister(&priv->ddev); -disable_clk: - clk_disable_unprepare(priv->dma_clk); - - return ret; } /* @@ -677,11 +662,7 @@ disable_clk: */ static void ls2x_dma_remove(struct platform_device *pdev) { - struct ls2x_dma_priv *priv = platform_get_drvdata(pdev); - of_dma_controller_free(pdev->dev.of_node); - dma_async_device_unregister(&priv->ddev); - clk_disable_unprepare(priv->dma_clk); } static const struct of_device_id ls2x_dma_of_match_table[] = { diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index cfb9962417ef..7acb3d29dad3 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -744,20 +744,19 @@ static int mxs_dma_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; const struct mxs_dma_type *dma_type; + struct device *dev = &pdev->dev; struct mxs_dma_engine *mxs_dma; int ret, i; - mxs_dma = devm_kzalloc(&pdev->dev, sizeof(*mxs_dma), GFP_KERNEL); + mxs_dma = devm_kzalloc(dev, sizeof(*mxs_dma), GFP_KERNEL); if (!mxs_dma) return -ENOMEM; ret = of_property_read_u32(np, "dma-channels", &mxs_dma->nr_channels); - if (ret) { - dev_err(&pdev->dev, "failed to read dma-channels\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to read dma-channels\n"); - dma_type = (struct mxs_dma_type *)of_device_get_match_data(&pdev->dev); + dma_type = (struct mxs_dma_type *)of_device_get_match_data(dev); mxs_dma->type = dma_type->type; mxs_dma->dev_id = dma_type->id; @@ -765,7 +764,7 @@ static int mxs_dma_probe(struct platform_device *pdev) if (IS_ERR(mxs_dma->base)) return PTR_ERR(mxs_dma->base); - mxs_dma->clk = devm_clk_get(&pdev->dev, NULL); + mxs_dma->clk = devm_clk_get(dev, NULL); if (IS_ERR(mxs_dma->clk)) return PTR_ERR(mxs_dma->clk); @@ -795,10 +794,10 @@ static int mxs_dma_probe(struct platform_device *pdev) return ret; mxs_dma->pdev = pdev; - mxs_dma->dma_device.dev = &pdev->dev; + mxs_dma->dma_device.dev = dev; /* mxs_dma gets 65535 bytes maximum sg size */ - dma_set_max_seg_size(mxs_dma->dma_device.dev, MAX_XFER_BYTES); + dma_set_max_seg_size(dev, MAX_XFER_BYTES); mxs_dma->dma_device.device_alloc_chan_resources = mxs_dma_alloc_chan_resources; mxs_dma->dma_device.device_free_chan_resources = mxs_dma_free_chan_resources; @@ -815,18 +814,15 @@ static int mxs_dma_probe(struct platform_device *pdev) mxs_dma->dma_device.device_issue_pending = mxs_dma_enable_chan; ret = dmaenginem_async_device_register(&mxs_dma->dma_device); - if (ret) { - dev_err(mxs_dma->dma_device.dev, "unable to register\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "unable to register\n"); - ret = of_dma_controller_register(np, mxs_dma_xlate, mxs_dma); - if (ret) { - dev_err(mxs_dma->dma_device.dev, - "failed to register controller\n"); - } + ret = devm_of_dma_controller_register(dev, np, mxs_dma_xlate, mxs_dma); + if (ret) + return dev_err_probe(dev, ret, + "failed to register controller\n"); - dev_info(mxs_dma->dma_device.dev, "initialized\n"); + dev_info(dev, "initialized\n"); return 0; } @@ -840,3 +836,6 @@ static struct platform_driver mxs_dma_driver = { }; builtin_platform_driver(mxs_dma_driver); + +MODULE_DESCRIPTION("MXS DMA driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index f30bdf69c740..625ff29024de 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -95,9 +96,16 @@ struct rz_dmac_icu { u8 dmac_index; }; +struct rz_dmac_info { + void (*icu_register_dma_req)(struct platform_device *icu_dev, + u8 dmac_index, u8 dmac_channel, u16 req_no); + u16 default_dma_req_no; +}; + struct rz_dmac { struct dma_device engine; struct rz_dmac_icu icu; + const struct rz_dmac_info *info; struct device *dev; struct reset_control *rstc; void __iomem *base; @@ -106,8 +114,6 @@ struct rz_dmac { unsigned int n_channels; struct rz_dmac_chan *channels; - bool has_icu; - DECLARE_BITMAP(modules, 1024); }; @@ -118,10 +124,12 @@ struct rz_dmac { * Registers */ +#define CRTB 0x0020 #define CHSTAT 0x0024 #define CHCTRL 0x0028 #define CHCFG 0x002c #define NXLA 0x0038 +#define CRLA 0x003c #define DCTRL 0x0000 @@ -132,10 +140,12 @@ struct rz_dmac { #define CHANNEL_8_15_COMMON_BASE 0x0700 #define CHSTAT_ER BIT(4) +#define CHSTAT_SUS BIT(3) #define CHSTAT_EN BIT(0) #define CHCTRL_CLRINTMSK BIT(17) #define CHCTRL_CLRSUS BIT(9) +#define CHCTRL_SETSUS BIT(8) #define CHCTRL_CLRTC BIT(6) #define CHCTRL_CLREND BIT(5) #define CHCTRL_CLRRQ BIT(4) @@ -266,15 +276,12 @@ static void rz_dmac_enable_hw(struct rz_dmac_chan *channel) { struct dma_chan *chan = &channel->vc.chan; struct rz_dmac *dmac = to_rz_dmac(chan->device); - unsigned long flags; u32 nxla; u32 chctrl; u32 chstat; dev_dbg(dmac->dev, "%s channel %d\n", __func__, channel->index); - local_irq_save(flags); - rz_dmac_lmdesc_recycle(channel); nxla = channel->lmdesc.base_dma + @@ -289,8 +296,6 @@ static void rz_dmac_enable_hw(struct rz_dmac_chan *channel) rz_dmac_ch_writel(channel, CHCTRL_SWRST, CHCTRL, 1); rz_dmac_ch_writel(channel, chctrl, CHCTRL, 1); } - - local_irq_restore(flags); } static void rz_dmac_disable_hw(struct rz_dmac_chan *channel) @@ -316,6 +321,16 @@ static void rz_dmac_set_dmars_register(struct rz_dmac *dmac, int nr, u32 dmars) rz_dmac_ext_writel(dmac, dmars32, dmars_offset); } +static void rz_dmac_set_dma_req_no(struct rz_dmac *dmac, unsigned int index, + int req_no) +{ + if (dmac->info->icu_register_dma_req) + dmac->info->icu_register_dma_req(dmac->icu.pdev, dmac->icu.dmac_index, + index, req_no); + else + rz_dmac_set_dmars_register(dmac, index, req_no); +} + static void rz_dmac_prepare_desc_for_memcpy(struct rz_dmac_chan *channel) { struct dma_chan *chan = &channel->vc.chan; @@ -333,13 +348,7 @@ static void rz_dmac_prepare_desc_for_memcpy(struct rz_dmac_chan *channel) lmdesc->chext = 0; lmdesc->header = HEADER_LV; - if (dmac->has_icu) { - rzv2h_icu_register_dma_req(dmac->icu.pdev, dmac->icu.dmac_index, - channel->index, - RZV2H_ICU_DMAC_REQ_NO_DEFAULT); - } else { - rz_dmac_set_dmars_register(dmac, channel->index, 0); - } + rz_dmac_set_dma_req_no(dmac, channel->index, dmac->info->default_dma_req_no); channel->chcfg = chcfg; channel->chctrl = CHCTRL_STG | CHCTRL_SETEN; @@ -390,12 +399,7 @@ static void rz_dmac_prepare_descs_for_slave_sg(struct rz_dmac_chan *channel) channel->lmdesc.tail = lmdesc; - if (dmac->has_icu) { - rzv2h_icu_register_dma_req(dmac->icu.pdev, dmac->icu.dmac_index, - channel->index, channel->mid_rid); - } else { - rz_dmac_set_dmars_register(dmac, channel->index, channel->mid_rid); - } + rz_dmac_set_dma_req_no(dmac, channel->index, channel->mid_rid); channel->chctrl = CHCTRL_SETEN; } @@ -460,15 +464,12 @@ static void rz_dmac_free_chan_resources(struct dma_chan *chan) { struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); struct rz_dmac *dmac = to_rz_dmac(chan->device); - struct rz_lmdesc *lmdesc = channel->lmdesc.base; struct rz_dmac_desc *desc, *_desc; unsigned long flags; - unsigned int i; spin_lock_irqsave(&channel->vc.lock, flags); - for (i = 0; i < DMAC_NR_LMDESC; i++) - lmdesc[i].header = 0; + rz_lmdesc_setup(channel, channel->lmdesc.base); rz_dmac_disable_hw(channel); list_splice_tail_init(&channel->ld_active, &channel->ld_free); @@ -560,15 +561,12 @@ rz_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, static int rz_dmac_terminate_all(struct dma_chan *chan) { struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); - struct rz_lmdesc *lmdesc = channel->lmdesc.base; unsigned long flags; - unsigned int i; LIST_HEAD(head); spin_lock_irqsave(&channel->vc.lock, flags); rz_dmac_disable_hw(channel); - for (i = 0; i < DMAC_NR_LMDESC; i++) - lmdesc[i].header = 0; + rz_lmdesc_setup(channel, channel->lmdesc.base); list_splice_tail_init(&channel->ld_active, &channel->ld_free); list_splice_tail_init(&channel->ld_queue, &channel->ld_free); @@ -679,13 +677,185 @@ static void rz_dmac_device_synchronize(struct dma_chan *chan) if (ret < 0) dev_warn(dmac->dev, "DMA Timeout"); - if (dmac->has_icu) { - rzv2h_icu_register_dma_req(dmac->icu.pdev, dmac->icu.dmac_index, - channel->index, - RZV2H_ICU_DMAC_REQ_NO_DEFAULT); - } else { - rz_dmac_set_dmars_register(dmac, channel->index, 0); + rz_dmac_set_dma_req_no(dmac, channel->index, dmac->info->default_dma_req_no); +} + +static struct rz_lmdesc * +rz_dmac_get_next_lmdesc(struct rz_lmdesc *base, struct rz_lmdesc *lmdesc) +{ + struct rz_lmdesc *next = ++lmdesc; + + if (next >= base + DMAC_NR_LMDESC) + next = base; + + return next; +} + +static u32 rz_dmac_calculate_residue_bytes_in_vd(struct rz_dmac_chan *channel, u32 crla) +{ + struct rz_lmdesc *lmdesc = channel->lmdesc.head; + struct dma_chan *chan = &channel->vc.chan; + struct rz_dmac *dmac = to_rz_dmac(chan->device); + u32 residue = 0, i = 0; + + while (lmdesc->nxla != crla) { + lmdesc = rz_dmac_get_next_lmdesc(channel->lmdesc.base, lmdesc); + if (++i >= DMAC_NR_LMDESC) + return 0; } + + /* Calculate residue from next lmdesc to end of virtual desc */ + while (lmdesc->chcfg & CHCFG_DEM) { + residue += lmdesc->tb; + lmdesc = rz_dmac_get_next_lmdesc(channel->lmdesc.base, lmdesc); + } + + dev_dbg(dmac->dev, "%s: VD residue is %u\n", __func__, residue); + + return residue; +} + +static u32 rz_dmac_chan_get_residue(struct rz_dmac_chan *channel, + dma_cookie_t cookie) +{ + struct rz_dmac_desc *current_desc, *desc; + enum dma_status status; + u32 crla, crtb, i; + + /* Get current processing virtual descriptor */ + current_desc = list_first_entry(&channel->ld_active, + struct rz_dmac_desc, node); + if (!current_desc) + return 0; + + /* + * If the cookie corresponds to a descriptor that has been completed + * there is no residue. The same check has already been performed by the + * caller but without holding the channel lock, so the descriptor could + * now be complete. + */ + status = dma_cookie_status(&channel->vc.chan, cookie, NULL); + if (status == DMA_COMPLETE) + return 0; + + /* + * If the cookie doesn't correspond to the currently processing virtual + * descriptor then the descriptor hasn't been processed yet, and the + * residue is equal to the full descriptor size. Also, a client driver + * is possible to call this function before rz_dmac_irq_handler_thread() + * runs. In this case, the running descriptor will be the next + * descriptor, and will appear in the done list. So, if the argument + * cookie matches the done list's cookie, we can assume the residue is + * zero. + */ + if (cookie != current_desc->vd.tx.cookie) { + list_for_each_entry(desc, &channel->ld_free, node) { + if (cookie == desc->vd.tx.cookie) + return 0; + } + + list_for_each_entry(desc, &channel->ld_queue, node) { + if (cookie == desc->vd.tx.cookie) + return desc->len; + } + + list_for_each_entry(desc, &channel->ld_active, node) { + if (cookie == desc->vd.tx.cookie) + return desc->len; + } + + /* + * No descriptor found for the cookie, there's thus no residue. + * This shouldn't happen if the calling driver passes a correct + * cookie value. + */ + WARN(1, "No descriptor for cookie!"); + return 0; + } + + /* + * We need to read two registers. Make sure the hardware does not move + * to next lmdesc while reading the current lmdesc. Trying it 3 times + * should be enough: initial read, retry, retry for the paranoid. + */ + for (i = 0; i < 3; i++) { + crla = rz_dmac_ch_readl(channel, CRLA, 1); + crtb = rz_dmac_ch_readl(channel, CRTB, 1); + /* Still the same? */ + if (crla == rz_dmac_ch_readl(channel, CRLA, 1)) + break; + } + + WARN_ONCE(i >= 3, "residue might not be continuous!"); + + /* + * Calculate number of bytes transferred in processing virtual descriptor. + * One virtual descriptor can have many lmdesc. + */ + return crtb + rz_dmac_calculate_residue_bytes_in_vd(channel, crla); +} + +static enum dma_status rz_dmac_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + enum dma_status status; + u32 residue; + + status = dma_cookie_status(chan, cookie, txstate); + if (status == DMA_COMPLETE || !txstate) + return status; + + scoped_guard(spinlock_irqsave, &channel->vc.lock) { + u32 val; + + residue = rz_dmac_chan_get_residue(channel, cookie); + + val = rz_dmac_ch_readl(channel, CHSTAT, 1); + if (val & CHSTAT_SUS) + status = DMA_PAUSED; + } + + /* if there's no residue and no paused, the cookie is complete */ + if (!residue && status != DMA_PAUSED) + return DMA_COMPLETE; + + dma_set_residue(txstate, residue); + + return status; +} + +static int rz_dmac_device_pause(struct dma_chan *chan) +{ + struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + u32 val; + + guard(spinlock_irqsave)(&channel->vc.lock); + + val = rz_dmac_ch_readl(channel, CHSTAT, 1); + if (!(val & CHSTAT_EN)) + return 0; + + rz_dmac_ch_writel(channel, CHCTRL_SETSUS, CHCTRL, 1); + return read_poll_timeout_atomic(rz_dmac_ch_readl, val, + (val & CHSTAT_SUS), 1, 1024, + false, channel, CHSTAT, 1); +} + +static int rz_dmac_device_resume(struct dma_chan *chan) +{ + struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + u32 val; + + guard(spinlock_irqsave)(&channel->vc.lock); + + /* Do not check CHSTAT_SUS but rely on HW capabilities. */ + + rz_dmac_ch_writel(channel, CHCTRL_CLRSUS, CHCTRL, 1); + return read_poll_timeout_atomic(rz_dmac_ch_readl, val, + !(val & CHSTAT_SUS), 1, 1024, + false, channel, CHSTAT, 1); } /* @@ -697,7 +867,7 @@ static void rz_dmac_irq_handle_channel(struct rz_dmac_chan *channel) { struct dma_chan *chan = &channel->vc.chan; struct rz_dmac *dmac = to_rz_dmac(chan->device); - u32 chstat, chctrl; + u32 chstat; chstat = rz_dmac_ch_readl(channel, CHSTAT, 1); if (chstat & CHSTAT_ER) { @@ -706,13 +876,14 @@ static void rz_dmac_irq_handle_channel(struct rz_dmac_chan *channel) scoped_guard(spinlock_irqsave, &channel->vc.lock) rz_dmac_ch_writel(channel, CHCTRL_DEFAULT, CHCTRL, 1); - goto done; + return; } - chctrl = rz_dmac_ch_readl(channel, CHCTRL, 1); - rz_dmac_ch_writel(channel, chctrl | CHCTRL_CLREND, CHCTRL, 1); -done: - return; + /* + * No need to lock. This just clears the END interrupt. Writing + * zeros to CHCTRL is just ignored by HW. + */ + rz_dmac_ch_writel(channel, CHCTRL_CLREND, CHCTRL, 1); } static irqreturn_t rz_dmac_irq_handler(int irq, void *dev_id) @@ -876,14 +1047,13 @@ static int rz_dmac_parse_of_icu(struct device *dev, struct rz_dmac *dmac) uint32_t dmac_index; int ret; - ret = of_parse_phandle_with_fixed_args(np, "renesas,icu", 1, 0, &args); - if (ret == -ENOENT) + if (!dmac->info->icu_register_dma_req) return 0; + + ret = of_parse_phandle_with_fixed_args(np, "renesas,icu", 1, 0, &args); if (ret) return ret; - dmac->has_icu = true; - dmac->icu.pdev = of_find_device_by_node(args.np); of_node_put(args.np); if (!dmac->icu.pdev) { @@ -938,6 +1108,7 @@ static int rz_dmac_probe(struct platform_device *pdev) if (!dmac) return -ENOMEM; + dmac->info = device_get_match_data(&pdev->dev); dmac->dev = &pdev->dev; platform_set_drvdata(pdev, dmac); @@ -955,23 +1126,22 @@ static int rz_dmac_probe(struct platform_device *pdev) if (IS_ERR(dmac->base)) return PTR_ERR(dmac->base); - if (!dmac->has_icu) { + if (!dmac->info->icu_register_dma_req) { dmac->ext_base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(dmac->ext_base)) return PTR_ERR(dmac->ext_base); } /* Register interrupt handler for error */ - irq = platform_get_irq_byname(pdev, irqname); - if (irq < 0) - return irq; - - ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0, - irqname, NULL); - if (ret) { - dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n", - irq, ret); - return ret; + irq = platform_get_irq_byname_optional(pdev, irqname); + if (irq > 0) { + ret = devm_request_irq(&pdev->dev, irq, rz_dmac_irq_handler, 0, + irqname, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ %u (%d)\n", + irq, ret); + return ret; + } } /* Initialize the channels. */ @@ -1009,6 +1179,7 @@ static int rz_dmac_probe(struct platform_device *pdev) engine = &dmac->engine; dma_cap_set(DMA_SLAVE, engine->cap_mask); dma_cap_set(DMA_MEMCPY, engine->cap_mask); + engine->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; rz_dmac_writel(dmac, DCTRL_DEFAULT, CHANNEL_0_7_COMMON_BASE + DCTRL); rz_dmac_writel(dmac, DCTRL_DEFAULT, CHANNEL_8_15_COMMON_BASE + DCTRL); @@ -1016,13 +1187,15 @@ static int rz_dmac_probe(struct platform_device *pdev) engine->device_alloc_chan_resources = rz_dmac_alloc_chan_resources; engine->device_free_chan_resources = rz_dmac_free_chan_resources; - engine->device_tx_status = dma_cookie_status; + engine->device_tx_status = rz_dmac_tx_status; engine->device_prep_slave_sg = rz_dmac_prep_slave_sg; engine->device_prep_dma_memcpy = rz_dmac_prep_dma_memcpy; engine->device_config = rz_dmac_config; engine->device_terminate_all = rz_dmac_terminate_all; engine->device_issue_pending = rz_dmac_issue_pending; engine->device_synchronize = rz_dmac_device_synchronize; + engine->device_pause = rz_dmac_device_pause; + engine->device_resume = rz_dmac_device_resume; engine->copy_align = DMAENGINE_ALIGN_1_BYTE; dma_set_max_seg_size(engine->dev, U32_MAX); @@ -1076,9 +1249,24 @@ static void rz_dmac_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } +static const struct rz_dmac_info rz_dmac_v2h_info = { + .icu_register_dma_req = rzv2h_icu_register_dma_req, + .default_dma_req_no = RZV2H_ICU_DMAC_REQ_NO_DEFAULT, +}; + +static const struct rz_dmac_info rz_dmac_t2h_info = { + .icu_register_dma_req = rzt2h_icu_register_dma_req, + .default_dma_req_no = RZT2H_ICU_DMAC_REQ_NO_DEFAULT, +}; + +static const struct rz_dmac_info rz_dmac_generic_info = { + .default_dma_req_no = 0, +}; + static const struct of_device_id of_rz_dmac_match[] = { - { .compatible = "renesas,r9a09g057-dmac", }, - { .compatible = "renesas,rz-dmac", }, + { .compatible = "renesas,r9a09g057-dmac", .data = &rz_dmac_v2h_info }, + { .compatible = "renesas,r9a09g077-dmac", .data = &rz_dmac_t2h_info }, + { .compatible = "renesas,rz-dmac", .data = &rz_dmac_generic_info }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, of_rz_dmac_match); diff --git a/drivers/dma/switchtec_dma.c b/drivers/dma/switchtec_dma.c new file mode 100644 index 000000000000..3ef928640615 --- /dev/null +++ b/drivers/dma/switchtec_dma.c @@ -0,0 +1,1437 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip Switchtec(tm) DMA Controller Driver + * Copyright (c) 2025, Kelvin Cao + * Copyright (c) 2025, Microchip Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dmaengine.h" + +MODULE_DESCRIPTION("Switchtec PCIe Switch DMA Engine"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kelvin Cao"); + +#define SWITCHTEC_DMAC_CHAN_CTRL_OFFSET 0x1000 +#define SWITCHTEC_DMAC_CHAN_CFG_STS_OFFSET 0x160000 + +#define SWITCHTEC_DMA_CHAN_HW_REGS_SIZE 0x1000 +#define SWITCHTEC_DMA_CHAN_FW_REGS_SIZE 0x80 + +#define SWITCHTEC_REG_CAP 0x80 +#define SWITCHTEC_REG_CHAN_CNT 0x84 +#define SWITCHTEC_REG_TAG_LIMIT 0x90 +#define SWITCHTEC_REG_CHAN_STS_VEC 0x94 +#define SWITCHTEC_REG_SE_BUF_CNT 0x98 +#define SWITCHTEC_REG_SE_BUF_BASE 0x9a + +#define SWITCHTEC_DESC_MAX_SIZE 0x100000 + +#define SWITCHTEC_CHAN_CTRL_PAUSE BIT(0) +#define SWITCHTEC_CHAN_CTRL_HALT BIT(1) +#define SWITCHTEC_CHAN_CTRL_RESET BIT(2) +#define SWITCHTEC_CHAN_CTRL_ERR_PAUSE BIT(3) + +#define SWITCHTEC_CHAN_STS_PAUSED BIT(9) +#define SWITCHTEC_CHAN_STS_HALTED BIT(10) +#define SWITCHTEC_CHAN_STS_PAUSED_MASK GENMASK(29, 13) + +#define SWITCHTEC_INVALID_HFID 0xffff + +#define SWITCHTEC_DMA_SQ_SIZE SZ_32K +#define SWITCHTEC_DMA_CQ_SIZE SZ_32K + +#define SWITCHTEC_DMA_RING_SIZE SZ_32K + +static const char * const channel_status_str[] = { + [13] = "received a VDM with length error status", + [14] = "received a VDM or Cpl with Unsupported Request error status", + [15] = "received a VDM or Cpl with Completion Abort error status", + [16] = "received a VDM with ECRC error status", + [17] = "received a VDM with EP error status", + [18] = "received a VDM with Reserved Cpl error status", + [19] = "received only part of split SE CplD", + [20] = "the ISP_DMAC detected a Completion Time Out", + [21] = "received a Cpl with Unsupported Request status", + [22] = "received a Cpl with Completion Abort status", + [23] = "received a Cpl with a reserved status", + [24] = "received a TLP with ECRC error status in its metadata", + [25] = "received a TLP with the EP bit set in the header", + [26] = "the ISP_DMAC tried to process a SE with an invalid Connection ID", + [27] = "the ISP_DMAC tried to process a SE with an invalid Remote Host interrupt", + [28] = "a reserved opcode was detected in an SE", + [29] = "received a SE Cpl with error status", +}; + +struct chan_hw_regs { + u16 cq_head; + u16 rsvd1; + u16 sq_tail; + u16 rsvd2; + u8 ctrl; + u8 rsvd3[3]; + u16 status; + u16 rsvd4; +}; + +#define PERF_BURST_SCALE_MASK GENMASK_U32(3, 2) +#define PERF_MRRS_MASK GENMASK_U32(6, 4) +#define PERF_INTERVAL_MASK GENMASK_U32(10, 8) +#define PERF_BURST_SIZE_MASK GENMASK_U32(14, 12) +#define PERF_ARB_WEIGHT_MASK GENMASK_U32(31, 24) + +#define SE_BUF_BASE_MASK GENMASK_U32(10, 2) +#define SE_BUF_LEN_MASK GENMASK_U32(20, 12) +#define SE_THRESH_MASK GENMASK_U32(31, 23) + +#define SWITCHTEC_CHAN_ENABLE BIT(1) + +struct chan_fw_regs { + u32 valid_en_se; + u32 cq_base_lo; + u32 cq_base_hi; + u16 cq_size; + u16 rsvd1; + u32 sq_base_lo; + u32 sq_base_hi; + u16 sq_size; + u16 rsvd2; + u32 int_vec; + u32 perf_cfg; + u32 rsvd3; + u32 perf_latency_selector; + u32 perf_fetched_se_cnt_lo; + u32 perf_fetched_se_cnt_hi; + u32 perf_byte_cnt_lo; + u32 perf_byte_cnt_hi; + u32 rsvd4; + u16 perf_se_pending; + u16 perf_se_buf_empty; + u32 perf_chan_idle; + u32 perf_lat_max; + u32 perf_lat_min; + u32 perf_lat_last; + u16 sq_current; + u16 sq_phase; + u16 cq_current; + u16 cq_phase; +}; + +struct switchtec_dma_chan { + struct switchtec_dma_dev *swdma_dev; + struct dma_chan dma_chan; + struct chan_hw_regs __iomem *mmio_chan_hw; + struct chan_fw_regs __iomem *mmio_chan_fw; + + /* Serialize hardware control register access */ + spinlock_t hw_ctrl_lock; + + struct tasklet_struct desc_task; + + /* Serialize descriptor preparation */ + spinlock_t submit_lock; + bool ring_active; + int cid; + + /* Serialize completion processing */ + spinlock_t complete_lock; + bool comp_ring_active; + + /* channel index and irq */ + int index; + int irq; + + /* + * In driver context, head is advanced by producer while + * tail is advanced by consumer. + */ + + /* the head and tail for both desc_ring and hw_sq */ + int head; + int tail; + int phase_tag; + struct switchtec_dma_hw_se_desc *hw_sq; + dma_addr_t dma_addr_sq; + + /* the tail for hw_cq */ + int cq_tail; + struct switchtec_dma_hw_ce *hw_cq; + dma_addr_t dma_addr_cq; + + struct list_head list; + + struct switchtec_dma_desc *desc_ring[SWITCHTEC_DMA_RING_SIZE]; +}; + +struct switchtec_dma_dev { + struct dma_device dma_dev; + struct pci_dev __rcu *pdev; + void __iomem *bar; + + struct switchtec_dma_chan **swdma_chans; + int chan_cnt; + int chan_status_irq; +}; + +enum chan_op { + ENABLE_CHAN, + DISABLE_CHAN, +}; + +enum switchtec_dma_opcode { + SWITCHTEC_DMA_OPC_MEMCPY = 0, + SWITCHTEC_DMA_OPC_RDIMM = 0x1, + SWITCHTEC_DMA_OPC_WRIMM = 0x2, + SWITCHTEC_DMA_OPC_RHI = 0x6, + SWITCHTEC_DMA_OPC_NOP = 0x7, +}; + +struct switchtec_dma_hw_se_desc { + u8 opc; + u8 ctrl; + __le16 tlp_setting; + __le16 rsvd1; + __le16 cid; + __le32 byte_cnt; + __le32 addr_lo; /* SADDR_LO/WIADDR_LO */ + __le32 addr_hi; /* SADDR_HI/WIADDR_HI */ + __le32 daddr_lo; + __le32 daddr_hi; + __le16 dfid; + __le16 sfid; +}; + +#define SWITCHTEC_SE_DFM BIT(5) +#define SWITCHTEC_SE_LIOF BIT(6) +#define SWITCHTEC_SE_BRR BIT(7) +#define SWITCHTEC_SE_CID_MASK GENMASK(15, 0) + +#define SWITCHTEC_CE_SC_LEN_ERR BIT(0) +#define SWITCHTEC_CE_SC_UR BIT(1) +#define SWITCHTEC_CE_SC_CA BIT(2) +#define SWITCHTEC_CE_SC_RSVD_CPL BIT(3) +#define SWITCHTEC_CE_SC_ECRC_ERR BIT(4) +#define SWITCHTEC_CE_SC_EP_SET BIT(5) +#define SWITCHTEC_CE_SC_D_RD_CTO BIT(8) +#define SWITCHTEC_CE_SC_D_RIMM_UR BIT(9) +#define SWITCHTEC_CE_SC_D_RIMM_CA BIT(10) +#define SWITCHTEC_CE_SC_D_RIMM_RSVD_CPL BIT(11) +#define SWITCHTEC_CE_SC_D_ECRC BIT(12) +#define SWITCHTEC_CE_SC_D_EP_SET BIT(13) +#define SWITCHTEC_CE_SC_D_BAD_CONNID BIT(14) +#define SWITCHTEC_CE_SC_D_BAD_RHI_ADDR BIT(15) +#define SWITCHTEC_CE_SC_D_INVD_CMD BIT(16) +#define SWITCHTEC_CE_SC_MASK GENMASK(16, 0) + +struct switchtec_dma_hw_ce { + __le32 rdimm_cpl_dw0; + __le32 rdimm_cpl_dw1; + __le32 rsvd1; + __le32 cpl_byte_cnt; + __le16 sq_head; + __le16 rsvd2; + __le32 rsvd3; + __le32 sts_code; + __le16 cid; + __le16 phase_tag; +}; + +struct switchtec_dma_desc { + struct dma_async_tx_descriptor txd; + struct switchtec_dma_hw_se_desc *hw; + u32 orig_size; + bool completed; +}; + +static int wait_for_chan_status(struct chan_hw_regs __iomem *chan_hw, u32 mask, + bool set) +{ + u32 status; + + return readl_poll_timeout_atomic(&chan_hw->status, status, + (set && (status & mask)) || + (!set && !(status & mask)), + 10, 100 * USEC_PER_MSEC); +} + +static int halt_channel(struct switchtec_dma_chan *swdma_chan) +{ + struct chan_hw_regs __iomem *chan_hw = swdma_chan->mmio_chan_hw; + struct pci_dev *pdev; + int ret; + + rcu_read_lock(); + pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); + if (!pdev) { + ret = -ENODEV; + goto unlock_and_exit; + } + + spin_lock(&swdma_chan->hw_ctrl_lock); + writeb(SWITCHTEC_CHAN_CTRL_HALT, &chan_hw->ctrl); + ret = wait_for_chan_status(chan_hw, SWITCHTEC_CHAN_STS_HALTED, true); + spin_unlock(&swdma_chan->hw_ctrl_lock); + +unlock_and_exit: + rcu_read_unlock(); + return ret; +} + +static int unhalt_channel(struct switchtec_dma_chan *swdma_chan) +{ + struct chan_hw_regs __iomem *chan_hw = swdma_chan->mmio_chan_hw; + struct pci_dev *pdev; + u8 ctrl; + int ret; + + rcu_read_lock(); + pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); + if (!pdev) { + ret = -ENODEV; + goto unlock_and_exit; + } + + spin_lock(&swdma_chan->hw_ctrl_lock); + ctrl = readb(&chan_hw->ctrl); + ctrl &= ~SWITCHTEC_CHAN_CTRL_HALT; + writeb(ctrl, &chan_hw->ctrl); + ret = wait_for_chan_status(chan_hw, SWITCHTEC_CHAN_STS_HALTED, false); + spin_unlock(&swdma_chan->hw_ctrl_lock); + +unlock_and_exit: + rcu_read_unlock(); + return ret; +} + +static void flush_pci_write(struct chan_hw_regs __iomem *chan_hw) +{ + readl(&chan_hw->cq_head); +} + +static int reset_channel(struct switchtec_dma_chan *swdma_chan) +{ + struct chan_hw_regs __iomem *chan_hw = swdma_chan->mmio_chan_hw; + struct pci_dev *pdev; + + rcu_read_lock(); + pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); + if (!pdev) { + rcu_read_unlock(); + return -ENODEV; + } + + spin_lock(&swdma_chan->hw_ctrl_lock); + writel(SWITCHTEC_CHAN_CTRL_RESET | SWITCHTEC_CHAN_CTRL_ERR_PAUSE, + &chan_hw->ctrl); + flush_pci_write(chan_hw); + + udelay(1000); + + writel(SWITCHTEC_CHAN_CTRL_ERR_PAUSE, &chan_hw->ctrl); + spin_unlock(&swdma_chan->hw_ctrl_lock); + flush_pci_write(chan_hw); + + rcu_read_unlock(); + return 0; +} + +static int pause_reset_channel(struct switchtec_dma_chan *swdma_chan) +{ + struct chan_hw_regs __iomem *chan_hw = swdma_chan->mmio_chan_hw; + struct pci_dev *pdev; + + rcu_read_lock(); + pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); + if (!pdev) { + rcu_read_unlock(); + return -ENODEV; + } + + spin_lock(&swdma_chan->hw_ctrl_lock); + writeb(SWITCHTEC_CHAN_CTRL_PAUSE, &chan_hw->ctrl); + spin_unlock(&swdma_chan->hw_ctrl_lock); + + flush_pci_write(chan_hw); + + rcu_read_unlock(); + + /* wait 60ms to ensure no pending CEs */ + mdelay(60); + + return reset_channel(swdma_chan); +} + +static int channel_op(struct switchtec_dma_chan *swdma_chan, int op) +{ + struct chan_fw_regs __iomem *chan_fw = swdma_chan->mmio_chan_fw; + struct pci_dev *pdev; + u32 valid_en_se; + + rcu_read_lock(); + pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); + if (!pdev) { + rcu_read_unlock(); + return -ENODEV; + } + + valid_en_se = readl(&chan_fw->valid_en_se); + if (op == ENABLE_CHAN) + valid_en_se |= SWITCHTEC_CHAN_ENABLE; + else + valid_en_se &= ~SWITCHTEC_CHAN_ENABLE; + + writel(valid_en_se, &chan_fw->valid_en_se); + + rcu_read_unlock(); + return 0; +} + +static int enable_channel(struct switchtec_dma_chan *swdma_chan) +{ + return channel_op(swdma_chan, ENABLE_CHAN); +} + +static int disable_channel(struct switchtec_dma_chan *swdma_chan) +{ + return channel_op(swdma_chan, DISABLE_CHAN); +} + +static void +switchtec_dma_cleanup_completed(struct switchtec_dma_chan *swdma_chan) +{ + struct device *chan_dev = &swdma_chan->dma_chan.dev->device; + struct switchtec_dma_desc *desc; + struct switchtec_dma_hw_ce *ce; + struct dmaengine_result res; + int tail, cid, se_idx, i; + __le16 phase_tag; + u32 sts_code; + __le32 *p; + + do { + spin_lock_bh(&swdma_chan->complete_lock); + if (!swdma_chan->comp_ring_active) { + spin_unlock_bh(&swdma_chan->complete_lock); + break; + } + + ce = &swdma_chan->hw_cq[swdma_chan->cq_tail]; + /* + * phase_tag is updated by hardware, ensure the value is + * not from the cache + */ + phase_tag = smp_load_acquire(&ce->phase_tag); + if (le16_to_cpu(phase_tag) == swdma_chan->phase_tag) { + spin_unlock_bh(&swdma_chan->complete_lock); + break; + } + + cid = le16_to_cpu(ce->cid); + se_idx = cid & (SWITCHTEC_DMA_SQ_SIZE - 1); + desc = swdma_chan->desc_ring[se_idx]; + + tail = swdma_chan->tail; + + res.residue = desc->orig_size - le32_to_cpu(ce->cpl_byte_cnt); + + sts_code = le32_to_cpu(ce->sts_code); + + if (!(sts_code & SWITCHTEC_CE_SC_MASK)) { + res.result = DMA_TRANS_NOERROR; + } else { + if (sts_code & SWITCHTEC_CE_SC_D_RD_CTO) + res.result = DMA_TRANS_READ_FAILED; + else + res.result = DMA_TRANS_WRITE_FAILED; + + dev_err(chan_dev, "CID 0x%04x failed, SC 0x%08x\n", cid, + (u32)(sts_code & SWITCHTEC_CE_SC_MASK)); + + p = (__le32 *)ce; + for (i = 0; i < sizeof(*ce) / 4; i++) { + dev_err(chan_dev, "CE DW%d: 0x%08x\n", i, + le32_to_cpu(*p)); + p++; + } + } + + desc->completed = true; + + swdma_chan->cq_tail++; + swdma_chan->cq_tail &= SWITCHTEC_DMA_CQ_SIZE - 1; + + rcu_read_lock(); + if (!rcu_dereference(swdma_chan->swdma_dev->pdev)) { + rcu_read_unlock(); + spin_unlock_bh(&swdma_chan->complete_lock); + return; + } + writew(swdma_chan->cq_tail, &swdma_chan->mmio_chan_hw->cq_head); + rcu_read_unlock(); + + if (swdma_chan->cq_tail == 0) + swdma_chan->phase_tag = !swdma_chan->phase_tag; + + /* Out of order CE */ + if (se_idx != tail) { + spin_unlock_bh(&swdma_chan->complete_lock); + continue; + } + + do { + dma_cookie_complete(&desc->txd); + dma_descriptor_unmap(&desc->txd); + dmaengine_desc_get_callback_invoke(&desc->txd, &res); + desc->txd.callback = NULL; + desc->txd.callback_result = NULL; + desc->completed = false; + + tail++; + tail &= SWITCHTEC_DMA_SQ_SIZE - 1; + + /* + * Ensure the desc updates are visible before updating + * the tail index + */ + smp_store_release(&swdma_chan->tail, tail); + desc = swdma_chan->desc_ring[swdma_chan->tail]; + if (!desc->completed) + break; + } while (CIRC_CNT(READ_ONCE(swdma_chan->head), swdma_chan->tail, + SWITCHTEC_DMA_SQ_SIZE)); + + spin_unlock_bh(&swdma_chan->complete_lock); + } while (1); +} + +static void +switchtec_dma_abort_desc(struct switchtec_dma_chan *swdma_chan, int force) +{ + struct switchtec_dma_desc *desc; + struct dmaengine_result res; + + if (!force) + switchtec_dma_cleanup_completed(swdma_chan); + + spin_lock_bh(&swdma_chan->complete_lock); + + while (CIRC_CNT(swdma_chan->head, swdma_chan->tail, + SWITCHTEC_DMA_SQ_SIZE) >= 1) { + desc = swdma_chan->desc_ring[swdma_chan->tail]; + + res.residue = desc->orig_size; + res.result = DMA_TRANS_ABORTED; + + dma_cookie_complete(&desc->txd); + dma_descriptor_unmap(&desc->txd); + if (!force) + dmaengine_desc_get_callback_invoke(&desc->txd, &res); + desc->txd.callback = NULL; + desc->txd.callback_result = NULL; + + swdma_chan->tail++; + swdma_chan->tail &= SWITCHTEC_DMA_SQ_SIZE - 1; + } + + spin_unlock_bh(&swdma_chan->complete_lock); +} + +static void switchtec_dma_chan_stop(struct switchtec_dma_chan *swdma_chan) +{ + int rc; + + rc = halt_channel(swdma_chan); + if (rc) + return; + + rcu_read_lock(); + if (!rcu_dereference(swdma_chan->swdma_dev->pdev)) { + rcu_read_unlock(); + return; + } + + writel(0, &swdma_chan->mmio_chan_fw->sq_base_lo); + writel(0, &swdma_chan->mmio_chan_fw->sq_base_hi); + writel(0, &swdma_chan->mmio_chan_fw->cq_base_lo); + writel(0, &swdma_chan->mmio_chan_fw->cq_base_hi); + + rcu_read_unlock(); +} + +static int switchtec_dma_terminate_all(struct dma_chan *chan) +{ + struct switchtec_dma_chan *swdma_chan = + container_of(chan, struct switchtec_dma_chan, dma_chan); + + spin_lock_bh(&swdma_chan->complete_lock); + swdma_chan->comp_ring_active = false; + spin_unlock_bh(&swdma_chan->complete_lock); + + return pause_reset_channel(swdma_chan); +} + +static void switchtec_dma_synchronize(struct dma_chan *chan) +{ + struct switchtec_dma_chan *swdma_chan = + container_of(chan, struct switchtec_dma_chan, dma_chan); + + int rc; + + switchtec_dma_abort_desc(swdma_chan, 1); + + rc = enable_channel(swdma_chan); + if (rc) + return; + + rc = reset_channel(swdma_chan); + if (rc) + return; + + rc = unhalt_channel(swdma_chan); + if (rc) + return; + + spin_lock_bh(&swdma_chan->submit_lock); + swdma_chan->head = 0; + spin_unlock_bh(&swdma_chan->submit_lock); + + spin_lock_bh(&swdma_chan->complete_lock); + swdma_chan->comp_ring_active = true; + swdma_chan->phase_tag = 0; + swdma_chan->tail = 0; + swdma_chan->cq_tail = 0; + swdma_chan->cid = 0; + dma_cookie_init(chan); + spin_unlock_bh(&swdma_chan->complete_lock); +} + +static struct dma_async_tx_descriptor * +switchtec_dma_prep_desc(struct dma_chan *c, u16 dst_fid, dma_addr_t dma_dst, + u16 src_fid, dma_addr_t dma_src, u64 data, + size_t len, unsigned long flags) + __acquires(swdma_chan->submit_lock) +{ + struct switchtec_dma_chan *swdma_chan = + container_of(c, struct switchtec_dma_chan, dma_chan); + struct switchtec_dma_desc *desc; + int head, tail; + + spin_lock_bh(&swdma_chan->submit_lock); + + if (!swdma_chan->ring_active) + goto err_unlock; + + tail = READ_ONCE(swdma_chan->tail); + head = swdma_chan->head; + + if (!CIRC_SPACE(head, tail, SWITCHTEC_DMA_RING_SIZE)) + goto err_unlock; + + desc = swdma_chan->desc_ring[head]; + + if (src_fid != SWITCHTEC_INVALID_HFID && + dst_fid != SWITCHTEC_INVALID_HFID) + desc->hw->ctrl |= SWITCHTEC_SE_DFM; + + if (flags & DMA_PREP_INTERRUPT) + desc->hw->ctrl |= SWITCHTEC_SE_LIOF; + + if (flags & DMA_PREP_FENCE) + desc->hw->ctrl |= SWITCHTEC_SE_BRR; + + desc->txd.flags = flags; + + desc->completed = false; + desc->hw->opc = SWITCHTEC_DMA_OPC_MEMCPY; + desc->hw->addr_lo = cpu_to_le32(lower_32_bits(dma_src)); + desc->hw->addr_hi = cpu_to_le32(upper_32_bits(dma_src)); + desc->hw->daddr_lo = cpu_to_le32(lower_32_bits(dma_dst)); + desc->hw->daddr_hi = cpu_to_le32(upper_32_bits(dma_dst)); + desc->hw->byte_cnt = cpu_to_le32(len); + desc->hw->tlp_setting = 0; + desc->hw->dfid = cpu_to_le16(dst_fid); + desc->hw->sfid = cpu_to_le16(src_fid); + swdma_chan->cid &= SWITCHTEC_SE_CID_MASK; + desc->hw->cid = cpu_to_le16(swdma_chan->cid++); + desc->orig_size = len; + + /* return with the lock held, it will be released in tx_submit */ + + return &desc->txd; + +err_unlock: + /* + * Keep sparse happy by restoring an even lock count on + * this lock. + */ + __acquire(swdma_chan->submit_lock); + + spin_unlock_bh(&swdma_chan->submit_lock); + return NULL; +} + +static struct dma_async_tx_descriptor * +switchtec_dma_prep_memcpy(struct dma_chan *c, dma_addr_t dma_dst, + dma_addr_t dma_src, size_t len, unsigned long flags) + __acquires(swdma_chan->submit_lock) +{ + if (len > SWITCHTEC_DESC_MAX_SIZE) { + /* + * Keep sparse happy by restoring an even lock count on + * this lock. + */ + __acquire(swdma_chan->submit_lock); + return NULL; + } + + return switchtec_dma_prep_desc(c, SWITCHTEC_INVALID_HFID, dma_dst, + SWITCHTEC_INVALID_HFID, dma_src, 0, len, + flags); +} + +static dma_cookie_t +switchtec_dma_tx_submit(struct dma_async_tx_descriptor *desc) + __releases(swdma_chan->submit_lock) +{ + struct switchtec_dma_chan *swdma_chan = + container_of(desc->chan, struct switchtec_dma_chan, dma_chan); + dma_cookie_t cookie; + int head; + + head = swdma_chan->head + 1; + head &= SWITCHTEC_DMA_RING_SIZE - 1; + + /* + * Ensure the desc updates are visible before updating the head index + */ + smp_store_release(&swdma_chan->head, head); + + cookie = dma_cookie_assign(desc); + + spin_unlock_bh(&swdma_chan->submit_lock); + + return cookie; +} + +static enum dma_status switchtec_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + struct switchtec_dma_chan *swdma_chan = + container_of(chan, struct switchtec_dma_chan, dma_chan); + enum dma_status ret; + + ret = dma_cookie_status(chan, cookie, txstate); + if (ret == DMA_COMPLETE) + return ret; + + /* + * For jobs where the interrupts are disabled, this is the only place + * to process the completions returned by the hardware. Callers that + * disable interrupts must call tx_status() to determine when a job + * is done, so it is safe to process completions here. If a job has + * interrupts enabled, then the completions will normally be processed + * in the tasklet that is triggered by the interrupt and tx_status() + * does not need to be called. + */ + switchtec_dma_cleanup_completed(swdma_chan); + + return dma_cookie_status(chan, cookie, txstate); +} + +static void switchtec_dma_issue_pending(struct dma_chan *chan) +{ + struct switchtec_dma_chan *swdma_chan = + container_of(chan, struct switchtec_dma_chan, dma_chan); + struct switchtec_dma_dev *swdma_dev = swdma_chan->swdma_dev; + + /* + * The sq_tail register is actually for the head of the + * submisssion queue. Chip has the opposite define of head/tail + * to the Linux kernel. + */ + + rcu_read_lock(); + if (!rcu_dereference(swdma_dev->pdev)) { + rcu_read_unlock(); + return; + } + + spin_lock_bh(&swdma_chan->submit_lock); + writew(swdma_chan->head, &swdma_chan->mmio_chan_hw->sq_tail); + spin_unlock_bh(&swdma_chan->submit_lock); + + rcu_read_unlock(); +} + +static int switchtec_dma_pause(struct dma_chan *chan) +{ + struct switchtec_dma_chan *swdma_chan = + container_of(chan, struct switchtec_dma_chan, dma_chan); + struct chan_hw_regs __iomem *chan_hw = swdma_chan->mmio_chan_hw; + struct pci_dev *pdev; + int ret; + + rcu_read_lock(); + pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); + if (!pdev) { + ret = -ENODEV; + goto unlock_and_exit; + } + + spin_lock(&swdma_chan->hw_ctrl_lock); + writeb(SWITCHTEC_CHAN_CTRL_PAUSE, &chan_hw->ctrl); + ret = wait_for_chan_status(chan_hw, SWITCHTEC_CHAN_STS_PAUSED, true); + spin_unlock(&swdma_chan->hw_ctrl_lock); + +unlock_and_exit: + rcu_read_unlock(); + return ret; +} + +static int switchtec_dma_resume(struct dma_chan *chan) +{ + struct switchtec_dma_chan *swdma_chan = + container_of(chan, struct switchtec_dma_chan, dma_chan); + struct chan_hw_regs __iomem *chan_hw = swdma_chan->mmio_chan_hw; + struct pci_dev *pdev; + int ret; + + rcu_read_lock(); + pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); + if (!pdev) { + ret = -ENODEV; + goto unlock_and_exit; + } + + spin_lock(&swdma_chan->hw_ctrl_lock); + writeb(0, &chan_hw->ctrl); + ret = wait_for_chan_status(chan_hw, SWITCHTEC_CHAN_STS_PAUSED, false); + spin_unlock(&swdma_chan->hw_ctrl_lock); + +unlock_and_exit: + rcu_read_unlock(); + return ret; +} + +static void switchtec_dma_desc_task(unsigned long data) +{ + struct switchtec_dma_chan *swdma_chan = (void *)data; + + switchtec_dma_cleanup_completed(swdma_chan); +} + +static irqreturn_t switchtec_dma_isr(int irq, void *chan) +{ + struct switchtec_dma_chan *swdma_chan = chan; + + if (swdma_chan->comp_ring_active) + tasklet_schedule(&swdma_chan->desc_task); + + return IRQ_HANDLED; +} + +static irqreturn_t switchtec_dma_chan_status_isr(int irq, void *dma) +{ + struct switchtec_dma_dev *swdma_dev = dma; + struct dma_device *dma_dev = &swdma_dev->dma_dev; + struct switchtec_dma_chan *swdma_chan; + struct chan_hw_regs __iomem *chan_hw; + struct device *chan_dev; + struct dma_chan *chan; + u32 chan_status; + int bit; + + list_for_each_entry(chan, &dma_dev->channels, device_node) { + swdma_chan = container_of(chan, struct switchtec_dma_chan, + dma_chan); + chan_dev = &swdma_chan->dma_chan.dev->device; + chan_hw = swdma_chan->mmio_chan_hw; + + rcu_read_lock(); + if (!rcu_dereference(swdma_dev->pdev)) { + rcu_read_unlock(); + goto out; + } + + chan_status = readl(&chan_hw->status); + chan_status &= SWITCHTEC_CHAN_STS_PAUSED_MASK; + rcu_read_unlock(); + + bit = ffs(chan_status); + if (!bit) + dev_dbg(chan_dev, "No pause bit set.\n"); + else + dev_err(chan_dev, "Paused, %s\n", + channel_status_str[bit - 1]); + } + +out: + return IRQ_HANDLED; +} + +static void switchtec_dma_free_desc(struct switchtec_dma_chan *swdma_chan) +{ + struct switchtec_dma_dev *swdma_dev = swdma_chan->swdma_dev; + size_t size; + int i; + + size = SWITCHTEC_DMA_SQ_SIZE * sizeof(*swdma_chan->hw_sq); + if (swdma_chan->hw_sq) + dma_free_coherent(swdma_dev->dma_dev.dev, size, + swdma_chan->hw_sq, swdma_chan->dma_addr_sq); + + size = SWITCHTEC_DMA_CQ_SIZE * sizeof(*swdma_chan->hw_cq); + if (swdma_chan->hw_cq) + dma_free_coherent(swdma_dev->dma_dev.dev, size, + swdma_chan->hw_cq, swdma_chan->dma_addr_cq); + + for (i = 0; i < SWITCHTEC_DMA_RING_SIZE; i++) + kfree(swdma_chan->desc_ring[i]); +} + +static int switchtec_dma_alloc_desc(struct switchtec_dma_chan *swdma_chan) +{ + struct switchtec_dma_dev *swdma_dev = swdma_chan->swdma_dev; + struct chan_fw_regs __iomem *chan_fw = swdma_chan->mmio_chan_fw; + struct switchtec_dma_desc *desc; + struct pci_dev *pdev; + size_t size; + int rc, i; + + swdma_chan->head = 0; + swdma_chan->tail = 0; + swdma_chan->cq_tail = 0; + + size = SWITCHTEC_DMA_SQ_SIZE * sizeof(*swdma_chan->hw_sq); + swdma_chan->hw_sq = dma_alloc_coherent(swdma_dev->dma_dev.dev, size, + &swdma_chan->dma_addr_sq, + GFP_NOWAIT); + if (!swdma_chan->hw_sq) { + rc = -ENOMEM; + goto free_and_exit; + } + + size = SWITCHTEC_DMA_CQ_SIZE * sizeof(*swdma_chan->hw_cq); + swdma_chan->hw_cq = dma_alloc_coherent(swdma_dev->dma_dev.dev, size, + &swdma_chan->dma_addr_cq, + GFP_NOWAIT); + if (!swdma_chan->hw_cq) { + rc = -ENOMEM; + goto free_and_exit; + } + + /* reset host phase tag */ + swdma_chan->phase_tag = 0; + + for (i = 0; i < SWITCHTEC_DMA_RING_SIZE; i++) { + desc = kzalloc_obj(*desc, GFP_NOWAIT); + if (!desc) { + rc = -ENOMEM; + goto free_and_exit; + } + + dma_async_tx_descriptor_init(&desc->txd, &swdma_chan->dma_chan); + desc->txd.tx_submit = switchtec_dma_tx_submit; + desc->hw = &swdma_chan->hw_sq[i]; + desc->completed = true; + + swdma_chan->desc_ring[i] = desc; + } + + rcu_read_lock(); + pdev = rcu_dereference(swdma_dev->pdev); + if (!pdev) { + rcu_read_unlock(); + rc = -ENODEV; + goto free_and_exit; + } + + /* set sq/cq */ + writel(lower_32_bits(swdma_chan->dma_addr_sq), &chan_fw->sq_base_lo); + writel(upper_32_bits(swdma_chan->dma_addr_sq), &chan_fw->sq_base_hi); + writel(lower_32_bits(swdma_chan->dma_addr_cq), &chan_fw->cq_base_lo); + writel(upper_32_bits(swdma_chan->dma_addr_cq), &chan_fw->cq_base_hi); + + writew(SWITCHTEC_DMA_SQ_SIZE, &swdma_chan->mmio_chan_fw->sq_size); + writew(SWITCHTEC_DMA_CQ_SIZE, &swdma_chan->mmio_chan_fw->cq_size); + + rcu_read_unlock(); + return 0; + +free_and_exit: + switchtec_dma_free_desc(swdma_chan); + return rc; +} + +static int switchtec_dma_alloc_chan_resources(struct dma_chan *chan) +{ + struct switchtec_dma_chan *swdma_chan = + container_of(chan, struct switchtec_dma_chan, dma_chan); + struct switchtec_dma_dev *swdma_dev = swdma_chan->swdma_dev; + u32 perf_cfg; + int rc; + + rc = switchtec_dma_alloc_desc(swdma_chan); + if (rc) + return rc; + + rc = enable_channel(swdma_chan); + if (rc) + return rc; + + rc = reset_channel(swdma_chan); + if (rc) + return rc; + + rc = unhalt_channel(swdma_chan); + if (rc) + return rc; + + swdma_chan->ring_active = true; + swdma_chan->comp_ring_active = true; + swdma_chan->cid = 0; + + dma_cookie_init(chan); + + rcu_read_lock(); + if (!rcu_dereference(swdma_dev->pdev)) { + rcu_read_unlock(); + return -ENODEV; + } + + perf_cfg = readl(&swdma_chan->mmio_chan_fw->perf_cfg); + rcu_read_unlock(); + + dev_dbg(&chan->dev->device, "Burst Size: 0x%x\n", + FIELD_GET(PERF_BURST_SIZE_MASK, perf_cfg)); + + dev_dbg(&chan->dev->device, "Burst Scale: 0x%x\n", + FIELD_GET(PERF_BURST_SCALE_MASK, perf_cfg)); + + dev_dbg(&chan->dev->device, "Interval: 0x%x\n", + FIELD_GET(PERF_INTERVAL_MASK, perf_cfg)); + + dev_dbg(&chan->dev->device, "Arb Weight: 0x%x\n", + FIELD_GET(PERF_ARB_WEIGHT_MASK, perf_cfg)); + + dev_dbg(&chan->dev->device, "MRRS: 0x%x\n", + FIELD_GET(PERF_MRRS_MASK, perf_cfg)); + + return SWITCHTEC_DMA_SQ_SIZE; +} + +static void switchtec_dma_free_chan_resources(struct dma_chan *chan) +{ + struct switchtec_dma_chan *swdma_chan = + container_of(chan, struct switchtec_dma_chan, dma_chan); + + spin_lock_bh(&swdma_chan->submit_lock); + swdma_chan->ring_active = false; + spin_unlock_bh(&swdma_chan->submit_lock); + + spin_lock_bh(&swdma_chan->complete_lock); + swdma_chan->comp_ring_active = false; + spin_unlock_bh(&swdma_chan->complete_lock); + + switchtec_dma_chan_stop(swdma_chan); + switchtec_dma_abort_desc(swdma_chan, 0); + switchtec_dma_free_desc(swdma_chan); + + disable_channel(swdma_chan); +} + +static int switchtec_dma_chan_init(struct switchtec_dma_dev *swdma_dev, + struct pci_dev *pdev, int i) +{ + struct dma_device *dma = &swdma_dev->dma_dev; + struct switchtec_dma_chan *swdma_chan; + u32 valid_en_se, thresh; + int se_buf_len, irq, rc; + struct dma_chan *chan; + + swdma_chan = kzalloc_obj(*swdma_chan, GFP_KERNEL); + if (!swdma_chan) + return -ENOMEM; + + swdma_chan->phase_tag = 0; + swdma_chan->index = i; + swdma_chan->swdma_dev = swdma_dev; + + spin_lock_init(&swdma_chan->hw_ctrl_lock); + spin_lock_init(&swdma_chan->submit_lock); + spin_lock_init(&swdma_chan->complete_lock); + tasklet_init(&swdma_chan->desc_task, switchtec_dma_desc_task, + (unsigned long)swdma_chan); + + swdma_chan->mmio_chan_fw = + swdma_dev->bar + SWITCHTEC_DMAC_CHAN_CFG_STS_OFFSET + + i * SWITCHTEC_DMA_CHAN_FW_REGS_SIZE; + swdma_chan->mmio_chan_hw = + swdma_dev->bar + SWITCHTEC_DMAC_CHAN_CTRL_OFFSET + + i * SWITCHTEC_DMA_CHAN_HW_REGS_SIZE; + + swdma_dev->swdma_chans[i] = swdma_chan; + + rc = pause_reset_channel(swdma_chan); + if (rc) + goto free_and_exit; + + /* init perf tuner */ + writel(FIELD_PREP(PERF_BURST_SCALE_MASK, 1) | + FIELD_PREP(PERF_MRRS_MASK, 3) | + FIELD_PREP(PERF_BURST_SIZE_MASK, 6) | + FIELD_PREP(PERF_ARB_WEIGHT_MASK, 1), + &swdma_chan->mmio_chan_fw->perf_cfg); + + valid_en_se = readl(&swdma_chan->mmio_chan_fw->valid_en_se); + + dev_dbg(&pdev->dev, "Channel %d: SE buffer base %d\n", i, + FIELD_GET(SE_BUF_BASE_MASK, valid_en_se)); + + se_buf_len = FIELD_GET(SE_BUF_LEN_MASK, valid_en_se); + dev_dbg(&pdev->dev, "Channel %d: SE buffer count %d\n", i, se_buf_len); + + thresh = se_buf_len / 2; + valid_en_se |= FIELD_GET(SE_THRESH_MASK, thresh); + writel(valid_en_se, &swdma_chan->mmio_chan_fw->valid_en_se); + + /* request irqs */ + irq = readl(&swdma_chan->mmio_chan_fw->int_vec); + dev_dbg(&pdev->dev, "Channel %d: CE irq vector %d\n", i, irq); + + rc = pci_request_irq(pdev, irq, switchtec_dma_isr, NULL, swdma_chan, + KBUILD_MODNAME); + if (rc) + goto free_and_exit; + + swdma_chan->irq = irq; + + chan = &swdma_chan->dma_chan; + chan->device = dma; + dma_cookie_init(chan); + + list_add_tail(&chan->device_node, &dma->channels); + + return 0; + +free_and_exit: + kfree(swdma_chan); + return rc; +} + +static int switchtec_dma_chan_free(struct pci_dev *pdev, + struct switchtec_dma_chan *swdma_chan) +{ + spin_lock_bh(&swdma_chan->submit_lock); + swdma_chan->ring_active = false; + spin_unlock_bh(&swdma_chan->submit_lock); + + spin_lock_bh(&swdma_chan->complete_lock); + swdma_chan->comp_ring_active = false; + spin_unlock_bh(&swdma_chan->complete_lock); + + pci_free_irq(pdev, swdma_chan->irq, swdma_chan); + tasklet_kill(&swdma_chan->desc_task); + + switchtec_dma_chan_stop(swdma_chan); + + return 0; +} + +static int switchtec_dma_chans_release(struct pci_dev *pdev, + struct switchtec_dma_dev *swdma_dev) +{ + int i; + + for (i = 0; i < swdma_dev->chan_cnt; i++) + switchtec_dma_chan_free(pdev, swdma_dev->swdma_chans[i]); + + return 0; +} + +static int switchtec_dma_chans_enumerate(struct switchtec_dma_dev *swdma_dev, + struct pci_dev *pdev, int chan_cnt) +{ + struct dma_device *dma = &swdma_dev->dma_dev; + int base, cnt, rc, i; + + swdma_dev->swdma_chans = kcalloc(chan_cnt, sizeof(*swdma_dev->swdma_chans), + GFP_KERNEL); + + if (!swdma_dev->swdma_chans) + return -ENOMEM; + + base = readw(swdma_dev->bar + SWITCHTEC_REG_SE_BUF_BASE); + cnt = readw(swdma_dev->bar + SWITCHTEC_REG_SE_BUF_CNT); + + dev_dbg(&pdev->dev, "EP SE buffer base %d\n", base); + dev_dbg(&pdev->dev, "EP SE buffer count %d\n", cnt); + + INIT_LIST_HEAD(&dma->channels); + + for (i = 0; i < chan_cnt; i++) { + rc = switchtec_dma_chan_init(swdma_dev, pdev, i); + if (rc) { + dev_err(&pdev->dev, "Channel %d: init channel failed\n", + i); + chan_cnt = i; + goto err_exit; + } + } + + return chan_cnt; + +err_exit: + for (i = 0; i < chan_cnt; i++) + switchtec_dma_chan_free(pdev, swdma_dev->swdma_chans[i]); + + kfree(swdma_dev->swdma_chans); + + return rc; +} + +static void switchtec_dma_release(struct dma_device *dma_dev) +{ + struct switchtec_dma_dev *swdma_dev = + container_of(dma_dev, struct switchtec_dma_dev, dma_dev); + int i; + + for (i = 0; i < swdma_dev->chan_cnt; i++) + kfree(swdma_dev->swdma_chans[i]); + + kfree(swdma_dev->swdma_chans); + + put_device(dma_dev->dev); + kfree(swdma_dev); +} + +static int switchtec_dma_create(struct pci_dev *pdev) +{ + struct switchtec_dma_dev *swdma_dev; + int chan_cnt, nr_vecs, irq, rc; + struct dma_device *dma; + struct dma_chan *chan; + + /* + * Create the switchtec dma device + */ + swdma_dev = kzalloc_obj(*swdma_dev, GFP_KERNEL); + if (!swdma_dev) + return -ENOMEM; + + swdma_dev->bar = ioremap(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + + RCU_INIT_POINTER(swdma_dev->pdev, pdev); + + nr_vecs = pci_msix_vec_count(pdev); + rc = pci_alloc_irq_vectors(pdev, nr_vecs, nr_vecs, PCI_IRQ_MSIX); + if (rc < 0) + goto err_exit; + + irq = readw(swdma_dev->bar + SWITCHTEC_REG_CHAN_STS_VEC); + pci_dbg(pdev, "Channel pause irq vector %d\n", irq); + + rc = pci_request_irq(pdev, irq, NULL, switchtec_dma_chan_status_isr, + swdma_dev, KBUILD_MODNAME); + if (rc) + goto err_exit; + + swdma_dev->chan_status_irq = irq; + + chan_cnt = readl(swdma_dev->bar + SWITCHTEC_REG_CHAN_CNT); + if (!chan_cnt) { + pci_err(pdev, "No channel configured.\n"); + rc = -ENXIO; + goto err_exit; + } + + chan_cnt = switchtec_dma_chans_enumerate(swdma_dev, pdev, chan_cnt); + if (chan_cnt < 0) { + pci_err(pdev, "Failed to enumerate dma channels: %d\n", + chan_cnt); + rc = -ENXIO; + goto err_exit; + } + + swdma_dev->chan_cnt = chan_cnt; + + dma = &swdma_dev->dma_dev; + dma->copy_align = DMAENGINE_ALIGN_8_BYTES; + dma_cap_set(DMA_MEMCPY, dma->cap_mask); + dma_cap_set(DMA_PRIVATE, dma->cap_mask); + dma->dev = get_device(&pdev->dev); + + dma->device_alloc_chan_resources = switchtec_dma_alloc_chan_resources; + dma->device_free_chan_resources = switchtec_dma_free_chan_resources; + dma->device_prep_dma_memcpy = switchtec_dma_prep_memcpy; + dma->device_tx_status = switchtec_dma_tx_status; + dma->device_issue_pending = switchtec_dma_issue_pending; + dma->device_pause = switchtec_dma_pause; + dma->device_resume = switchtec_dma_resume; + dma->device_terminate_all = switchtec_dma_terminate_all; + dma->device_synchronize = switchtec_dma_synchronize; + dma->device_release = switchtec_dma_release; + + rc = dma_async_device_register(dma); + if (rc) { + pci_err(pdev, "Failed to register dma device: %d\n", rc); + goto err_chans_release_exit; + } + + pci_dbg(pdev, "Channel count: %d\n", chan_cnt); + + list_for_each_entry(chan, &dma->channels, device_node) + pci_dbg(pdev, "%s\n", dma_chan_name(chan)); + + pci_set_drvdata(pdev, swdma_dev); + + return 0; + +err_chans_release_exit: + switchtec_dma_chans_release(pdev, swdma_dev); + +err_exit: + if (swdma_dev->chan_status_irq) + free_irq(swdma_dev->chan_status_irq, swdma_dev); + + iounmap(swdma_dev->bar); + kfree(swdma_dev); + return rc; +} + +static int switchtec_dma_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int rc; + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + + rc = pci_request_mem_regions(pdev, KBUILD_MODNAME); + if (rc) + goto err_disable; + + pci_set_master(pdev); + + rc = switchtec_dma_create(pdev); + if (rc) + goto err_free; + + return 0; + +err_free: + pci_free_irq_vectors(pdev); + pci_release_mem_regions(pdev); + +err_disable: + pci_disable_device(pdev); + + return rc; +} + +static void switchtec_dma_remove(struct pci_dev *pdev) +{ + struct switchtec_dma_dev *swdma_dev = pci_get_drvdata(pdev); + + switchtec_dma_chans_release(pdev, swdma_dev); + + rcu_assign_pointer(swdma_dev->pdev, NULL); + synchronize_rcu(); + + pci_free_irq(pdev, swdma_dev->chan_status_irq, swdma_dev); + + pci_free_irq_vectors(pdev); + + dma_async_device_unregister(&swdma_dev->dma_dev); + + iounmap(swdma_dev->bar); + pci_release_mem_regions(pdev); + pci_disable_device(pdev); +} + +/* + * Also use the class code to identify the devices, as some of the + * device IDs are also used for other devices with other classes by + * Microsemi. + */ +#define SW_ID(vendor_id, device_id) \ + { \ + .vendor = vendor_id, \ + .device = device_id, \ + .subvendor = PCI_ANY_ID, \ + .subdevice = PCI_ANY_ID, \ + .class = PCI_CLASS_SYSTEM_OTHER << 8, \ + .class_mask = 0xffffffff, \ + } + +static const struct pci_device_id switchtec_dma_pci_tbl[] = { + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4000), /* PFX 100XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4084), /* PFX 84XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4068), /* PFX 68XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4052), /* PFX 52XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4036), /* PFX 36XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4028), /* PFX 28XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4100), /* PSX 100XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4184), /* PSX 84XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4168), /* PSX 68XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4152), /* PSX 52XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4136), /* PSX 36XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4128), /* PSX 28XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4352), /* PFXA 52XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4336), /* PFXA 36XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4328), /* PFXA 28XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4452), /* PSXA 52XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4436), /* PSXA 36XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4428), /* PSXA 28XG4 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5000), /* PFX 100XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5084), /* PFX 84XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5068), /* PFX 68XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5052), /* PFX 52XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5036), /* PFX 36XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5028), /* PFX 28XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5100), /* PSX 100XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5184), /* PSX 84XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5168), /* PSX 68XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5152), /* PSX 52XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5136), /* PSX 36XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5128), /* PSX 28XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5300), /* PFXA 100XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5384), /* PFXA 84XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5368), /* PFXA 68XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5352), /* PFXA 52XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5336), /* PFXA 36XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5328), /* PFXA 28XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5400), /* PSXA 100XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5484), /* PSXA 84XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5468), /* PSXA 68XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5452), /* PSXA 52XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5436), /* PSXA 36XG5 */ + SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5428), /* PSXA 28XG5 */ + SW_ID(PCI_VENDOR_ID_EFAR, 0x1001), /* PCI1001 16XG4 */ + SW_ID(PCI_VENDOR_ID_EFAR, 0x1002), /* PCI1002 16XG4 */ + SW_ID(PCI_VENDOR_ID_EFAR, 0x1003), /* PCI1003 16XG4 */ + SW_ID(PCI_VENDOR_ID_EFAR, 0x1004), /* PCI1004 16XG4 */ + SW_ID(PCI_VENDOR_ID_EFAR, 0x1005), /* PCI1005 16XG4 */ + SW_ID(PCI_VENDOR_ID_EFAR, 0x1006), /* PCI1006 16XG4 */ + {0} +}; +MODULE_DEVICE_TABLE(pci, switchtec_dma_pci_tbl); + +static struct pci_driver switchtec_dma_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = switchtec_dma_pci_tbl, + .probe = switchtec_dma_probe, + .remove = switchtec_dma_remove, +}; +module_pci_driver(switchtec_dma_pci_driver); diff --git a/drivers/dma/xilinx/xdma.c b/drivers/dma/xilinx/xdma.c index 782a55edc55b..90a22a730cc9 100644 --- a/drivers/dma/xilinx/xdma.c +++ b/drivers/dma/xilinx/xdma.c @@ -61,6 +61,8 @@ struct xdma_desc_block { * @dir: Transferring direction of the channel * @cfg: Transferring config of the channel * @irq: IRQ assigned to the channel + * @last_interrupt: task for comppleting last interrupt + * @stop_requested: stop request flag */ struct xdma_chan { struct virt_dma_chan vchan; diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index e3a18ee42aa2..404235c17353 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -3194,7 +3194,7 @@ static int xilinx_dma_probe(struct platform_device *pdev) = axivdma_clk_init; struct device_node *node = pdev->dev.of_node; struct xilinx_dma_device *xdev; - struct device_node *child, *np = pdev->dev.of_node; + struct device_node *np = pdev->dev.of_node; u32 num_frames, addr_width = XILINX_DMA_DFAULT_ADDRWIDTH, len_width; int i, err; @@ -3334,12 +3334,10 @@ static int xilinx_dma_probe(struct platform_device *pdev) platform_set_drvdata(pdev, xdev); /* Initialize the channels */ - for_each_child_of_node(node, child) { + for_each_child_of_node_scoped(node, child) { err = xilinx_dma_child_probe(xdev, child); - if (err < 0) { - of_node_put(child); + if (err < 0) goto error; - } } if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) { diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index af7ce62ec55c..0ff1658c2dc1 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -900,11 +900,21 @@ int dpll_pin_delete_ntf(struct dpll_pin *pin) return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin); } +/** + * __dpll_pin_change_ntf - notify that the pin has been changed + * @pin: registered pin pointer + * + * Context: caller must hold dpll_lock. Suitable for use inside pin + * callbacks which are already invoked under dpll_lock. + * Return: 0 if succeeds, error code otherwise. + */ int __dpll_pin_change_ntf(struct dpll_pin *pin) { + lockdep_assert_held(&dpll_lock); dpll_pin_notify(pin, DPLL_PIN_CHANGED); return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin); } +EXPORT_SYMBOL_GPL(__dpll_pin_change_ntf); /** * dpll_pin_change_ntf - notify that the pin has been changed diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h index dd28b56d27c5..a9cfd55f57fc 100644 --- a/drivers/dpll/dpll_netlink.h +++ b/drivers/dpll/dpll_netlink.h @@ -11,5 +11,3 @@ int dpll_device_delete_ntf(struct dpll_device *dpll); int dpll_pin_create_ntf(struct dpll_pin *pin); int dpll_pin_delete_ntf(struct dpll_pin *pin); - -int __dpll_pin_change_ntf(struct dpll_pin *pin); diff --git a/drivers/edac/versalnet_edac.c b/drivers/edac/versalnet_edac.c index ec1315582414..97ec05d68bbb 100644 --- a/drivers/edac/versalnet_edac.c +++ b/drivers/edac/versalnet_edac.c @@ -777,9 +777,9 @@ static int init_one_mc(struct mc_priv *priv, struct platform_device *pdev, int i u32 num_chans, rank, dwidth, config; struct edac_mc_layer layers[2]; struct mem_ctl_info *mci; + char name[MC_NAME_LEN]; struct device *dev; enum dev_type dt; - char *name; int rc; config = priv->adec[CONF + i * ADEC_NUM]; @@ -813,13 +813,9 @@ static int init_one_mc(struct mc_priv *priv, struct platform_device *pdev, int i layers[1].is_virt_csrow = false; rc = -ENOMEM; - name = kzalloc(MC_NAME_LEN, GFP_KERNEL); - if (!name) - return rc; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) - goto err_name_free; + return rc; mci = edac_mc_alloc(i, ARRAY_SIZE(layers), layers, sizeof(struct mc_priv)); if (!mci) { @@ -858,8 +854,6 @@ err_mc_free: edac_mc_free(mci); err_dev_free: kfree(dev); -err_name_free: - kfree(name); return rc; } diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index aec46bf03302..68d9df7d2dae 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -158,6 +158,7 @@ config EXTCON_PTN5150 tristate "NXP PTN5150 CC LOGIC USB EXTCON support" depends on I2C && (GPIOLIB || COMPILE_TEST) depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH + depends on TYPEC || !TYPEC select REGMAP_I2C help Say Y here to enable support for USB peripheral and USB host diff --git a/drivers/extcon/extcon-intel-int3496.c b/drivers/extcon/extcon-intel-int3496.c index ded1a85a5549..7d16d5b7d58f 100644 --- a/drivers/extcon/extcon-intel-int3496.c +++ b/drivers/extcon/extcon-intel-int3496.c @@ -106,7 +106,7 @@ static irqreturn_t int3496_thread_isr(int irq, void *priv) struct int3496_data *data = priv; /* Let the pin settle before processing it */ - mod_delayed_work(system_wq, &data->work, DEBOUNCE_TIME); + mod_delayed_work(system_percpu_wq, &data->work, DEBOUNCE_TIME); return IRQ_HANDLED; } @@ -181,7 +181,7 @@ static int int3496_probe(struct platform_device *pdev) } /* process id-pin so that we start with the right status */ - queue_delayed_work(system_wq, &data->work, 0); + queue_delayed_work(system_percpu_wq, &data->work, 0); flush_delayed_work(&data->work); platform_set_drvdata(pdev, data); diff --git a/drivers/extcon/extcon-ptn5150.c b/drivers/extcon/extcon-ptn5150.c index 78ad86c4a3be..eca1b140aeb0 100644 --- a/drivers/extcon/extcon-ptn5150.c +++ b/drivers/extcon/extcon-ptn5150.c @@ -18,6 +18,7 @@ #include #include #include +#include /* PTN5150 registers */ #define PTN5150_REG_DEVICE_ID 0x01 @@ -38,7 +39,11 @@ #define PTN5150_REG_DEVICE_ID_VERSION GENMASK(7, 3) #define PTN5150_REG_DEVICE_ID_VENDOR GENMASK(2, 0) +#define PTN5150_POLARITY_CC1 0x1 +#define PTN5150_POLARITY_CC2 0x2 + #define PTN5150_REG_CC_PORT_ATTACHMENT GENMASK(4, 2) +#define PTN5150_REG_CC_POLARITY GENMASK(1, 0) #define PTN5150_REG_CC_VBUS_DETECTION BIT(7) #define PTN5150_REG_INT_CABLE_ATTACH_MASK BIT(0) #define PTN5150_REG_INT_CABLE_DETACH_MASK BIT(1) @@ -53,6 +58,7 @@ struct ptn5150_info { int irq; struct work_struct irq_work; struct mutex mutex; + struct typec_switch *orient_sw; struct usb_role_switch *role_sw; }; @@ -71,6 +77,7 @@ static const struct regmap_config ptn5150_regmap_config = { static void ptn5150_check_state(struct ptn5150_info *info) { + enum typec_orientation orient = TYPEC_ORIENTATION_NONE; unsigned int port_status, reg_data, vbus; enum usb_role usb_role = USB_ROLE_NONE; int ret; @@ -81,6 +88,23 @@ static void ptn5150_check_state(struct ptn5150_info *info) return; } + orient = FIELD_GET(PTN5150_REG_CC_POLARITY, reg_data); + switch (orient) { + case PTN5150_POLARITY_CC1: + orient = TYPEC_ORIENTATION_NORMAL; + break; + case PTN5150_POLARITY_CC2: + orient = TYPEC_ORIENTATION_REVERSE; + break; + default: + orient = TYPEC_ORIENTATION_NONE; + break; + } + + ret = typec_switch_set(info->orient_sw, orient); + if (ret) + dev_err(info->dev, "failed to set orientation: %d\n", ret); + port_status = FIELD_GET(PTN5150_REG_CC_PORT_ATTACHMENT, reg_data); switch (port_status) { @@ -152,6 +176,12 @@ static void ptn5150_irq_work(struct work_struct *work) dev_err(info->dev, "failed to set none role: %d\n", ret); + + ret = typec_switch_set(info->orient_sw, + TYPEC_ORIENTATION_NONE); + if (ret) + dev_err(info->dev, + "failed to set orientation: %d\n", ret); } } @@ -219,12 +249,14 @@ static void ptn5150_work_sync_and_put(void *data) cancel_work_sync(&info->irq_work); usb_role_switch_put(info->role_sw); + typec_switch_put(info->orient_sw); } static int ptn5150_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct device_node *np = i2c->dev.of_node; + struct fwnode_handle *connector; struct ptn5150_info *info; int ret; @@ -311,7 +343,17 @@ static int ptn5150_i2c_probe(struct i2c_client *i2c) if (ret) return -EINVAL; + connector = device_get_named_child_node(dev, "connector"); + if (connector) { + info->orient_sw = fwnode_typec_switch_get(connector); + if (IS_ERR(info->orient_sw)) + return dev_err_probe(info->dev, PTR_ERR(info->orient_sw), + "failed to get orientation switch\n"); + } + info->role_sw = usb_role_switch_get(info->dev); + if (!info->role_sw && connector) + info->role_sw = fwnode_usb_role_switch_get(connector); if (IS_ERR(info->role_sw)) return dev_err_probe(info->dev, PTR_ERR(info->role_sw), "failed to get role switch\n"); @@ -331,6 +373,19 @@ static int ptn5150_i2c_probe(struct i2c_client *i2c) return 0; } +static int ptn5150_resume(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct ptn5150_info *info = i2c_get_clientdata(i2c); + + /* Need to check possible pending interrupt events */ + schedule_work(&info->irq_work); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(ptn5150_pm_ops, NULL, ptn5150_resume); + static const struct of_device_id ptn5150_dt_match[] = { { .compatible = "nxp,ptn5150" }, { }, @@ -346,6 +401,7 @@ MODULE_DEVICE_TABLE(i2c, ptn5150_i2c_id); static struct i2c_driver ptn5150_i2c_driver = { .driver = { .name = "ptn5150", + .pm = pm_sleep_ptr(&ptn5150_pm_ops), .of_match_table = ptn5150_dt_match, }, .probe = ptn5150_i2c_probe, diff --git a/drivers/extcon/extcon-usbc-tusb320.c b/drivers/extcon/extcon-usbc-tusb320.c index 2eab341de6b7..920b03421850 100644 --- a/drivers/extcon/extcon-usbc-tusb320.c +++ b/drivers/extcon/extcon-usbc-tusb320.c @@ -454,20 +454,18 @@ static int tusb320_typec_probe(struct i2c_client *client, priv->port_type = priv->cap.type; /* This goes into register 0x8 field CURRENT_MODE_ADVERTISE */ - ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str); - if (ret) - goto err_put; + if (!fwnode_property_read_string(connector, "typec-power-opmode", + &cap_str)) { + ret = typec_find_pwr_opmode(cap_str); + if (ret < 0) + goto err_put; + priv->pwr_opmode = ret; - ret = typec_find_pwr_opmode(cap_str); - if (ret < 0) - goto err_put; - - priv->pwr_opmode = ret; - - /* Initialize the hardware with the devicetree settings. */ - ret = tusb320_set_adv_pwr_mode(priv); - if (ret) - goto err_put; + /* Initialize the hardware with the devicetree settings. */ + ret = tusb320_set_adv_pwr_mode(priv); + if (ret) + goto err_put; + } priv->cap.revision = USB_TYPEC_REV_1_1; priv->cap.accessory[0] = TYPEC_ACCESSORY_AUDIO; diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index d9e9815a5f96..98d85cc114a7 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -1366,10 +1366,10 @@ void extcon_dev_unregister(struct extcon_dev *edev) return; } - ida_free(&extcon_dev_ids, edev->id); - device_unregister(&edev->dev); + ida_free(&extcon_dev_ids, edev->id); + if (edev->mutually_exclusive && edev->max_supported) { for (index = 0; edev->mutually_exclusive[index]; index++) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index f2f94d4d533e..eb2782848283 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -2078,7 +2078,7 @@ static int __init ffa_init(void) ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer), virt_to_phys(drv_info->rx_buffer), - rxtx_bufsz / FFA_PAGE_SIZE); + PAGE_ALIGN(rxtx_bufsz) / FFA_PAGE_SIZE); if (ret) { pr_err("failed to register FFA RxTx buffers\n"); goto free_pages; diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index 22267bbd0f4d..cd1331c2fc40 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -7,6 +7,7 @@ #define pr_fmt(fmt) "SCMI Notifications BASE - " fmt +#include #include #include @@ -219,8 +220,7 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph, } real_list_sz = t->rx.len - sizeof(u32); - calc_list_sz = (1 + (loop_num_ret - 1) / sizeof(u32)) * - sizeof(u32); + calc_list_sz = round_up(loop_num_ret, sizeof(u32)); if (calc_list_sz != real_list_sz) { dev_warn(dev, "Malformed reply - real_sz:%zd calc_sz:%u (loop_num_ret:%d)\n", diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 7c35c95fddba..7c9617d080a0 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -235,6 +235,9 @@ struct scmi_transport_ops { * to have an execution latency lesser-equal to the threshold * should be considered for atomic mode operation: such * decision is finally left up to the SCMI drivers. + * @no_completion_irq: Flag to indicate that this transport has no completion + * interrupt and has to be polled. This is similar to the + * force_polling below, except this is set via DT property. * @force_polling: Flag to force this whole transport to use SCMI core polling * mechanism instead of completion interrupts even if available. * @sync_cmds_completed_on_ret: Flag to indicate that the transport assures @@ -254,6 +257,7 @@ struct scmi_desc { int max_msg; int max_msg_size; unsigned int atomic_threshold; + bool no_completion_irq; const bool force_polling; const bool sync_cmds_completed_on_ret; const bool atomic_enabled; diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 3e76a3204ba4..f167194f7cf6 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2735,6 +2735,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node, cinfo->is_p2a = !tx; cinfo->rx_timeout_ms = info->desc->max_rx_timeout_ms; cinfo->max_msg_size = info->desc->max_msg_size; + cinfo->no_completion_irq = info->desc->no_completion_irq; /* Create a unique name for this transport device */ snprintf(name, 32, "__scmi_transport_device_%s_%02X", @@ -3150,6 +3151,9 @@ static const struct scmi_desc *scmi_transport_setup(struct device *dev) if (ret && ret != -EINVAL) dev_err(dev, "Malformed arm,max-msg DT property.\n"); + trans->desc.no_completion_irq = of_property_read_bool(dev->of_node, + "arm,no-completion-irq"); + dev_info(dev, "SCMI max-rx-timeout: %dms / max-msg-size: %dbytes / max-msg: %d\n", trans->desc.max_rx_timeout_ms, trans->desc.max_msg_size, diff --git a/drivers/firmware/arm_scmi/quirks.h b/drivers/firmware/arm_scmi/quirks.h index a71fde85a527..d8ba60b95652 100644 --- a/drivers/firmware/arm_scmi/quirks.h +++ b/drivers/firmware/arm_scmi/quirks.h @@ -20,10 +20,10 @@ * named as _qn. */ #define SCMI_QUIRK(_qn, _blk) \ - do { \ + ({ \ if (static_branch_unlikely(&(scmi_quirk_ ## _qn))) \ (_blk); \ - } while (0) + }) void scmi_quirks_initialize(void); void scmi_quirks_enable(struct device *dev, const char *vend, @@ -34,10 +34,10 @@ void scmi_quirks_enable(struct device *dev, const char *vend, #define DECLARE_SCMI_QUIRK(_qn) /* Force quirks compilation even when SCMI Quirks are disabled */ #define SCMI_QUIRK(_qn, _blk) \ - do { \ + ({ \ if (0) \ (_blk); \ - } while (0) + }) static inline void scmi_quirks_initialize(void) { } static inline void scmi_quirks_enable(struct device *dev, const char *vend, diff --git a/drivers/firmware/efi/apple-properties.c b/drivers/firmware/efi/apple-properties.c index 13ac28754c03..2e525e17fba7 100644 --- a/drivers/firmware/efi/apple-properties.c +++ b/drivers/firmware/efi/apple-properties.c @@ -226,7 +226,7 @@ static int __init map_properties(void) */ data->len = 0; memunmap(data); - memblock_free_late(pa_data + sizeof(*data), data_len); + memblock_phys_free(pa_data + sizeof(*data), data_len); return ret; } diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index a253b6144945..a5db3534f0a6 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -60,8 +60,10 @@ static int efi_pstore_open(struct pstore_info *psi) return err; psi->data = kzalloc(record_size, GFP_KERNEL); - if (!psi->data) + if (!psi->data) { + efivar_unlock(); return -ENOMEM; + } return 0; } diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index fbb8d4e33256..cfedb3025c26 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -66,7 +66,7 @@ KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__ lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ file.o mem.o random.o randomalloc.o pci.o \ skip_spaces.o lib-cmdline.o lib-ctype.o \ - alignedmem.o relocate.o printk.o vsprintf.o + alignedmem.o printk.o vsprintf.o # include the stub's libfdt dependencies from lib/ when needed libfdt-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c \ @@ -96,6 +96,7 @@ zboot-obj-$(CONFIG_KERNEL_ZSTD) := zboot-decompress-zstd.o lib-xxhash.o CFLAGS_zboot-decompress-zstd.o += -I$(srctree)/lib/zstd zboot-obj-$(CONFIG_RISCV) += lib-clz_ctz.o lib-ashldi3.o +zboot-obj-$(CONFIG_LOONGARCH) += lib-clz_ctz.o lib-ashldi3.o lib-$(CONFIG_EFI_ZBOOT) += zboot.o $(zboot-obj-y) lib-$(CONFIG_UNACCEPTED_MEMORY) += unaccepted_memory.o bitmap.o find.o diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 979a21818cc1..fd91fc15ec81 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -1104,13 +1104,6 @@ efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, unsigned long *addr, unsigned long min); -efi_status_t efi_relocate_kernel(unsigned long *image_addr, - unsigned long image_size, - unsigned long alloc_size, - unsigned long preferred_addr, - unsigned long alignment, - unsigned long min_addr); - efi_status_t efi_parse_options(char const *cmdline); void efi_parse_option_graphics(char *option); diff --git a/drivers/firmware/efi/libstub/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c index 736b6aae323d..c87ac7025107 100644 --- a/drivers/firmware/efi/libstub/loongarch-stub.c +++ b/drivers/firmware/efi/libstub/loongarch-stub.c @@ -14,6 +14,86 @@ extern int kernel_asize; extern int kernel_fsize; extern int kernel_entry; +/** + * efi_relocate_kernel() - copy memory area + * @image_addr: pointer to address of memory area to copy + * @image_size: size of memory area to copy + * @alloc_size: minimum size of memory to allocate, must be greater or + * equal to image_size + * @preferred_addr: preferred target address + * @alignment: minimum alignment of the allocated memory area. It + * should be a power of two. + * @min_addr: minimum target address + * + * Copy a memory area to a newly allocated memory area aligned according + * to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address + * is not available, the allocated address will not be below @min_addr. + * On exit, @image_addr is updated to the target copy address that was used. + * + * This function is used to copy the Linux kernel verbatim. It does not apply + * any relocation changes. + * + * Return: status code + */ +static +efi_status_t efi_relocate_kernel(unsigned long *image_addr, + unsigned long image_size, + unsigned long alloc_size, + unsigned long preferred_addr, + unsigned long alignment, + unsigned long min_addr) +{ + unsigned long cur_image_addr; + unsigned long new_addr = 0; + efi_status_t status; + unsigned long nr_pages; + efi_physical_addr_t efi_addr = preferred_addr; + + if (!image_addr || !image_size || !alloc_size) + return EFI_INVALID_PARAMETER; + if (alloc_size < image_size) + return EFI_INVALID_PARAMETER; + + cur_image_addr = *image_addr; + + /* + * The EFI firmware loader could have placed the kernel image + * anywhere in memory, but the kernel has restrictions on the + * max physical address it can run at. Some architectures + * also have a preferred address, so first try to relocate + * to the preferred address. If that fails, allocate as low + * as possible while respecting the required alignment. + */ + nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, + EFI_LOADER_DATA, nr_pages, &efi_addr); + new_addr = efi_addr; + /* + * If preferred address allocation failed allocate as low as + * possible. + */ + if (status != EFI_SUCCESS) { + status = efi_low_alloc_above(alloc_size, alignment, &new_addr, + min_addr); + } + if (status != EFI_SUCCESS) { + efi_err("Failed to allocate usable memory for kernel.\n"); + return status; + } + + /* + * We know source/dest won't overlap since both memory ranges + * have been allocated by UEFI, so we can safely use memcpy. + */ + memcpy((void *)new_addr, (void *)cur_image_addr, image_size); + efi_cache_sync_image(new_addr, image_size); + + /* Return the new address of the relocated image. */ + *image_addr = new_addr; + + return status; +} + efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, unsigned long *reserve_addr, diff --git a/drivers/firmware/efi/libstub/loongarch.c b/drivers/firmware/efi/libstub/loongarch.c index 9825f5218137..f7938d5c196a 100644 --- a/drivers/firmware/efi/libstub/loongarch.c +++ b/drivers/firmware/efi/libstub/loongarch.c @@ -18,6 +18,11 @@ efi_status_t check_platform_features(void) return EFI_SUCCESS; } +void efi_cache_sync_image(unsigned long image_base, unsigned long alloc_size) +{ + asm volatile ("ibar 0" ::: "memory"); +} + struct exit_boot_struct { efi_memory_desc_t *runtime_map; int runtime_entry_count; diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 9c82259eea81..59f3f83de50c 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -124,3 +124,85 @@ void efi_free(unsigned long size, unsigned long addr) nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; efi_bs_call(free_pages, addr, nr_pages); } + +/** + * efi_low_alloc_above() - allocate pages at or above given address + * @size: size of the memory area to allocate + * @align: minimum alignment of the allocated memory area. It should + * a power of two. + * @addr: on exit the address of the allocated memory + * @min: minimum address to used for the memory allocation + * + * Allocate at the lowest possible address that is not below @min as + * EFI_LOADER_DATA. The allocated pages are aligned according to @align but at + * least EFI_ALLOC_ALIGN. The first allocated page will not below the address + * given by @min. + * + * Return: status code + */ +efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long min) +{ + struct efi_boot_memmap *map __free(efi_pool) = NULL; + efi_status_t status; + unsigned long nr_pages; + int i; + + status = efi_get_memory_map(&map, false); + if (status != EFI_SUCCESS) + return status; + + /* + * Enforce minimum alignment that EFI or Linux requires when + * requesting a specific address. We are doing page-based (or + * larger) allocations, and both the address and size must meet + * alignment constraints. + */ + if (align < EFI_ALLOC_ALIGN) + align = EFI_ALLOC_ALIGN; + + size = round_up(size, EFI_ALLOC_ALIGN); + nr_pages = size / EFI_PAGE_SIZE; + for (i = 0; i < map->map_size / map->desc_size; i++) { + efi_memory_desc_t *desc; + unsigned long m = (unsigned long)map->map; + u64 start, end; + + desc = efi_memdesc_ptr(m, map->desc_size, i); + + if (desc->type != EFI_CONVENTIONAL_MEMORY) + continue; + + if (desc->attribute & EFI_MEMORY_HOT_PLUGGABLE) + continue; + + if (efi_soft_reserve_enabled() && + (desc->attribute & EFI_MEMORY_SP)) + continue; + + if (desc->num_pages < nr_pages) + continue; + + start = desc->phys_addr; + end = start + desc->num_pages * EFI_PAGE_SIZE; + + if (start < min) + start = min; + + start = round_up(start, align); + if ((start + size) > end) + continue; + + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, + EFI_LOADER_DATA, nr_pages, &start); + if (status == EFI_SUCCESS) { + *addr = start; + break; + } + } + + if (i == map->map_size / map->desc_size) + return EFI_NOT_FOUND; + + return EFI_SUCCESS; +} diff --git a/drivers/firmware/efi/libstub/relocate.c b/drivers/firmware/efi/libstub/relocate.c deleted file mode 100644 index d4264bfb6dc1..000000000000 --- a/drivers/firmware/efi/libstub/relocate.c +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include - -#include "efistub.h" - -/** - * efi_low_alloc_above() - allocate pages at or above given address - * @size: size of the memory area to allocate - * @align: minimum alignment of the allocated memory area. It should - * a power of two. - * @addr: on exit the address of the allocated memory - * @min: minimum address to used for the memory allocation - * - * Allocate at the lowest possible address that is not below @min as - * EFI_LOADER_DATA. The allocated pages are aligned according to @align but at - * least EFI_ALLOC_ALIGN. The first allocated page will not below the address - * given by @min. - * - * Return: status code - */ -efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long min) -{ - struct efi_boot_memmap *map __free(efi_pool) = NULL; - efi_status_t status; - unsigned long nr_pages; - int i; - - status = efi_get_memory_map(&map, false); - if (status != EFI_SUCCESS) - return status; - - /* - * Enforce minimum alignment that EFI or Linux requires when - * requesting a specific address. We are doing page-based (or - * larger) allocations, and both the address and size must meet - * alignment constraints. - */ - if (align < EFI_ALLOC_ALIGN) - align = EFI_ALLOC_ALIGN; - - size = round_up(size, EFI_ALLOC_ALIGN); - nr_pages = size / EFI_PAGE_SIZE; - for (i = 0; i < map->map_size / map->desc_size; i++) { - efi_memory_desc_t *desc; - unsigned long m = (unsigned long)map->map; - u64 start, end; - - desc = efi_memdesc_ptr(m, map->desc_size, i); - - if (desc->type != EFI_CONVENTIONAL_MEMORY) - continue; - - if (desc->attribute & EFI_MEMORY_HOT_PLUGGABLE) - continue; - - if (efi_soft_reserve_enabled() && - (desc->attribute & EFI_MEMORY_SP)) - continue; - - if (desc->num_pages < nr_pages) - continue; - - start = desc->phys_addr; - end = start + desc->num_pages * EFI_PAGE_SIZE; - - if (start < min) - start = min; - - start = round_up(start, align); - if ((start + size) > end) - continue; - - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, nr_pages, &start); - if (status == EFI_SUCCESS) { - *addr = start; - break; - } - } - - if (i == map->map_size / map->desc_size) - return EFI_NOT_FOUND; - - return EFI_SUCCESS; -} - -/** - * efi_relocate_kernel() - copy memory area - * @image_addr: pointer to address of memory area to copy - * @image_size: size of memory area to copy - * @alloc_size: minimum size of memory to allocate, must be greater or - * equal to image_size - * @preferred_addr: preferred target address - * @alignment: minimum alignment of the allocated memory area. It - * should be a power of two. - * @min_addr: minimum target address - * - * Copy a memory area to a newly allocated memory area aligned according - * to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address - * is not available, the allocated address will not be below @min_addr. - * On exit, @image_addr is updated to the target copy address that was used. - * - * This function is used to copy the Linux kernel verbatim. It does not apply - * any relocation changes. - * - * Return: status code - */ -efi_status_t efi_relocate_kernel(unsigned long *image_addr, - unsigned long image_size, - unsigned long alloc_size, - unsigned long preferred_addr, - unsigned long alignment, - unsigned long min_addr) -{ - unsigned long cur_image_addr; - unsigned long new_addr = 0; - efi_status_t status; - unsigned long nr_pages; - efi_physical_addr_t efi_addr = preferred_addr; - - if (!image_addr || !image_size || !alloc_size) - return EFI_INVALID_PARAMETER; - if (alloc_size < image_size) - return EFI_INVALID_PARAMETER; - - cur_image_addr = *image_addr; - - /* - * The EFI firmware loader could have placed the kernel image - * anywhere in memory, but the kernel has restrictions on the - * max physical address it can run at. Some architectures - * also have a preferred address, so first try to relocate - * to the preferred address. If that fails, allocate as low - * as possible while respecting the required alignment. - */ - nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, nr_pages, &efi_addr); - new_addr = efi_addr; - /* - * If preferred address allocation failed allocate as low as - * possible. - */ - if (status != EFI_SUCCESS) { - status = efi_low_alloc_above(alloc_size, alignment, &new_addr, - min_addr); - } - if (status != EFI_SUCCESS) { - efi_err("Failed to allocate usable memory for kernel.\n"); - return status; - } - - /* - * We know source/dest won't overlap since both memory ranges - * have been allocated by UEFI, so we can safely use memcpy. - */ - memcpy((void *)new_addr, (void *)cur_image_addr, image_size); - - /* Return the new address of the relocated image. */ - *image_addr = new_addr; - - return status; -} diff --git a/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c b/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c index 98a463e9774b..befa68d1dcaf 100644 --- a/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c +++ b/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c @@ -699,20 +699,18 @@ static DEFINE_MUTEX(__qcuefi_lock); static int qcuefi_set_reference(struct qcuefi_client *qcuefi) { - mutex_lock(&__qcuefi_lock); + guard(mutex)(&__qcuefi_lock); - if (qcuefi && __qcuefi) { - mutex_unlock(&__qcuefi_lock); + if (qcuefi && __qcuefi) return -EEXIST; - } __qcuefi = qcuefi; - mutex_unlock(&__qcuefi_lock); return 0; } static struct qcuefi_client *qcuefi_acquire(void) + __acquires(__qcuefi_lock) { mutex_lock(&__qcuefi_lock); if (!__qcuefi) { @@ -723,6 +721,7 @@ static struct qcuefi_client *qcuefi_acquire(void) } static void qcuefi_release(void) + __releases(__qcuefi_lock) { mutex_unlock(&__qcuefi_lock); } diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 8fbc96693a55..9b06a69d3a6d 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -199,19 +199,18 @@ static int qcom_scm_bw_enable(void) if (!__scm->path) return 0; - mutex_lock(&__scm->scm_bw_lock); + guard(mutex)(&__scm->scm_bw_lock); + if (!__scm->scm_vote_count) { ret = icc_set_bw(__scm->path, 0, UINT_MAX); if (ret < 0) { dev_err(__scm->dev, "failed to set bandwidth request\n"); - goto err_bw; + return ret; } } __scm->scm_vote_count++; -err_bw: - mutex_unlock(&__scm->scm_bw_lock); - return ret; + return 0; } static void qcom_scm_bw_disable(void) @@ -923,14 +922,13 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c goto free_input_rt; } - tbl_ptr = kzalloc(size, GFP_KERNEL); + tbl_ptr = kmemdup(output_rt_tzm, size, GFP_KERNEL); if (!tbl_ptr) { qcom_tzmem_free(output_rt_tzm); ret = -ENOMEM; goto free_input_rt; } - memcpy(tbl_ptr, output_rt_tzm, size); *output_rt_size = size; qcom_tzmem_free(output_rt_tzm); @@ -2290,15 +2288,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_send); */ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = { { .compatible = "asus,vivobook-s15" }, + { .compatible = "asus,vivobook-s15-x1p4" }, { .compatible = "asus,zenbook-a14-ux3407qa" }, { .compatible = "asus,zenbook-a14-ux3407ra" }, { .compatible = "dell,inspiron-14-plus-7441" }, { .compatible = "dell,latitude-7455" }, { .compatible = "dell,xps13-9345" }, + { .compatible = "ecs,liva-qc710" }, { .compatible = "hp,elitebook-ultra-g1q" }, { .compatible = "hp,omnibook-x14" }, { .compatible = "huawei,gaokun3" }, { .compatible = "lenovo,flex-5g" }, + { .compatible = "lenovo,ideacentre-mini-01q8x10" }, { .compatible = "lenovo,thinkbook-16" }, { .compatible = "lenovo,thinkpad-t14s" }, { .compatible = "lenovo,thinkpad-x13s", }, @@ -2309,7 +2310,10 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = { { .compatible = "microsoft,denali", }, { .compatible = "microsoft,romulus13", }, { .compatible = "microsoft,romulus15", }, + { .compatible = "qcom,glymur-crd" }, { .compatible = "qcom,hamoa-iot-evk" }, + { .compatible = "qcom,mahua-crd" }, + { .compatible = "qcom,purwa-iot-evk" }, { .compatible = "qcom,sc8180x-primus" }, { .compatible = "qcom,x1e001de-devkit" }, { .compatible = "qcom,x1e80100-crd" }, @@ -2467,6 +2471,56 @@ int qcom_scm_qtee_callback_response(phys_addr_t buf, size_t buf_size, } EXPORT_SYMBOL(qcom_scm_qtee_callback_response); +static void qcom_scm_gunyah_wdt_free(void *data) +{ + struct platform_device *gunyah_wdt_dev = data; + + platform_device_unregister(gunyah_wdt_dev); +} + +static void qcom_scm_gunyah_wdt_init(struct qcom_scm *scm) +{ + struct platform_device *gunyah_wdt_dev; + struct device_node *np; + bool of_wdt_available; + int i; + static const uuid_t gunyah_uuid = UUID_INIT(0xc1d58fcd, 0xa453, 0x5fdb, + 0x92, 0x65, 0xce, 0x36, + 0x67, 0x3d, 0x5f, 0x14); + static const char * const of_wdt_compatible[] = { + "qcom,kpss-wdt", + "arm,sbsa-gwdt", + }; + + /* Bail out if we are not running under Gunyah */ + if (!IS_ENABLED(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) || + !arm_smccc_hypervisor_has_uuid(&gunyah_uuid)) + return; + + /* + * Gunyah emulates either of Qualcomm watchdog or ARM SBSA watchdog on + * newer platforms. Bail out if we find them in the devicetree. + */ + for (i = 0; i < ARRAY_SIZE(of_wdt_compatible); i++) { + np = of_find_compatible_node(NULL, NULL, of_wdt_compatible[i]); + of_wdt_available = of_device_is_available(np); + of_node_put(np); + if (of_wdt_available) + return; + } + + gunyah_wdt_dev = platform_device_register_simple("gunyah-wdt", -1, + NULL, 0); + if (IS_ERR(gunyah_wdt_dev)) { + dev_err(scm->dev, "Failed to register Gunyah watchdog device: %ld\n", + PTR_ERR(gunyah_wdt_dev)); + return; + } + + devm_add_action_or_reset(scm->dev, qcom_scm_gunyah_wdt_free, + gunyah_wdt_dev); +} + static void qcom_scm_qtee_free(void *data) { struct platform_device *qtee_dev = data; @@ -2811,6 +2865,9 @@ static int qcom_scm_probe(struct platform_device *pdev) /* Initialize the QTEE object interface. */ qcom_scm_qtee_init(scm); + /* Initialize the Gunyah watchdog platform device. */ + qcom_scm_gunyah_wdt_init(scm); + return 0; } diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.c b/drivers/firmware/samsung/exynos-acpm-dvfs.c index 1c5b2b143bcc..06bdf62dea1f 100644 --- a/drivers/firmware/samsung/exynos-acpm-dvfs.c +++ b/drivers/firmware/samsung/exynos-acpm-dvfs.c @@ -5,6 +5,7 @@ * Copyright 2025 Linaro Ltd. */ +#include #include #include #include @@ -24,12 +25,12 @@ static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen, unsigned int acpm_chan_id, bool response) { xfer->acpm_chan_id = acpm_chan_id; + xfer->txcnt = cmdlen; xfer->txd = cmd; - xfer->txlen = cmdlen; if (response) { + xfer->rxcnt = cmdlen; xfer->rxd = cmd; - xfer->rxlen = cmdlen; } } @@ -42,7 +43,7 @@ static void acpm_dvfs_init_set_rate_cmd(u32 cmd[4], unsigned int clk_id, cmd[3] = ktime_to_ms(ktime_get()); } -int acpm_dvfs_set_rate(const struct acpm_handle *handle, +int acpm_dvfs_set_rate(struct acpm_handle *handle, unsigned int acpm_chan_id, unsigned int clk_id, unsigned long rate) { @@ -50,7 +51,7 @@ int acpm_dvfs_set_rate(const struct acpm_handle *handle, u32 cmd[4]; acpm_dvfs_init_set_rate_cmd(cmd, clk_id, rate); - acpm_dvfs_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id, false); + acpm_dvfs_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, false); return acpm_do_xfer(handle, &xfer); } @@ -62,7 +63,7 @@ static void acpm_dvfs_init_get_rate_cmd(u32 cmd[4], unsigned int clk_id) cmd[3] = ktime_to_ms(ktime_get()); } -unsigned long acpm_dvfs_get_rate(const struct acpm_handle *handle, +unsigned long acpm_dvfs_get_rate(struct acpm_handle *handle, unsigned int acpm_chan_id, unsigned int clk_id) { struct acpm_xfer xfer; @@ -70,7 +71,7 @@ unsigned long acpm_dvfs_get_rate(const struct acpm_handle *handle, int ret; acpm_dvfs_init_get_rate_cmd(cmd, clk_id); - acpm_dvfs_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id, true); + acpm_dvfs_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, true); ret = acpm_do_xfer(handle, &xfer); if (ret) diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.h b/drivers/firmware/samsung/exynos-acpm-dvfs.h index 9f2778e649c9..b37b15426102 100644 --- a/drivers/firmware/samsung/exynos-acpm-dvfs.h +++ b/drivers/firmware/samsung/exynos-acpm-dvfs.h @@ -11,10 +11,10 @@ struct acpm_handle; -int acpm_dvfs_set_rate(const struct acpm_handle *handle, +int acpm_dvfs_set_rate(struct acpm_handle *handle, unsigned int acpm_chan_id, unsigned int id, unsigned long rate); -unsigned long acpm_dvfs_get_rate(const struct acpm_handle *handle, +unsigned long acpm_dvfs_get_rate(struct acpm_handle *handle, unsigned int acpm_chan_id, unsigned int clk_id); diff --git a/drivers/firmware/samsung/exynos-acpm-pmic.c b/drivers/firmware/samsung/exynos-acpm-pmic.c index 961d7599e422..0c50993cc9a8 100644 --- a/drivers/firmware/samsung/exynos-acpm-pmic.c +++ b/drivers/firmware/samsung/exynos-acpm-pmic.c @@ -41,7 +41,7 @@ static const int acpm_pmic_linux_errmap[] = { [2] = -EACCES, /* Write register can't be accessed or issues to access it. */ }; -static int acpm_pmic_to_linux_err(int err) +static int acpm_pmic_to_linux_err(unsigned int err) { if (err >= 0 && err < ARRAY_SIZE(acpm_pmic_linux_errmap)) return acpm_pmic_linux_errmap[err]; @@ -63,8 +63,8 @@ static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen, { xfer->txd = cmd; xfer->rxd = cmd; - xfer->txlen = cmdlen; - xfer->rxlen = cmdlen; + xfer->txcnt = cmdlen; + xfer->rxcnt = cmdlen; xfer->acpm_chan_id = acpm_chan_id; } @@ -77,7 +77,7 @@ static void acpm_pmic_init_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan) cmd[3] = ktime_to_ms(ktime_get()); } -int acpm_pmic_read_reg(const struct acpm_handle *handle, +int acpm_pmic_read_reg(struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 *buf) { @@ -86,7 +86,7 @@ int acpm_pmic_read_reg(const struct acpm_handle *handle, int ret; acpm_pmic_init_read_cmd(cmd, type, reg, chan); - acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); + acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) @@ -107,7 +107,7 @@ static void acpm_pmic_init_bulk_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, FIELD_PREP(ACPM_PMIC_VALUE, count); } -int acpm_pmic_bulk_read(const struct acpm_handle *handle, +int acpm_pmic_bulk_read(struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 count, u8 *buf) { @@ -119,7 +119,7 @@ int acpm_pmic_bulk_read(const struct acpm_handle *handle, return -EINVAL; acpm_pmic_init_bulk_read_cmd(cmd, type, reg, chan, count); - acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); + acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) @@ -150,7 +150,7 @@ static void acpm_pmic_init_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, cmd[3] = ktime_to_ms(ktime_get()); } -int acpm_pmic_write_reg(const struct acpm_handle *handle, +int acpm_pmic_write_reg(struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 value) { @@ -159,7 +159,7 @@ int acpm_pmic_write_reg(const struct acpm_handle *handle, int ret; acpm_pmic_init_write_cmd(cmd, type, reg, chan, value); - acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); + acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) @@ -187,7 +187,7 @@ static void acpm_pmic_init_bulk_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, } } -int acpm_pmic_bulk_write(const struct acpm_handle *handle, +int acpm_pmic_bulk_write(struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 count, const u8 *buf) { @@ -199,7 +199,7 @@ int acpm_pmic_bulk_write(const struct acpm_handle *handle, return -EINVAL; acpm_pmic_init_bulk_write_cmd(cmd, type, reg, chan, count, buf); - acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); + acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) @@ -220,7 +220,7 @@ static void acpm_pmic_init_update_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, cmd[3] = ktime_to_ms(ktime_get()); } -int acpm_pmic_update_reg(const struct acpm_handle *handle, +int acpm_pmic_update_reg(struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 value, u8 mask) { @@ -229,7 +229,7 @@ int acpm_pmic_update_reg(const struct acpm_handle *handle, int ret; acpm_pmic_init_update_cmd(cmd, type, reg, chan, value, mask); - acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); + acpm_pmic_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) diff --git a/drivers/firmware/samsung/exynos-acpm-pmic.h b/drivers/firmware/samsung/exynos-acpm-pmic.h index 078421888a14..88ae9aada2ae 100644 --- a/drivers/firmware/samsung/exynos-acpm-pmic.h +++ b/drivers/firmware/samsung/exynos-acpm-pmic.h @@ -11,19 +11,19 @@ struct acpm_handle; -int acpm_pmic_read_reg(const struct acpm_handle *handle, +int acpm_pmic_read_reg(struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 *buf); -int acpm_pmic_bulk_read(const struct acpm_handle *handle, +int acpm_pmic_bulk_read(struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 count, u8 *buf); -int acpm_pmic_write_reg(const struct acpm_handle *handle, +int acpm_pmic_write_reg(struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 value); -int acpm_pmic_bulk_write(const struct acpm_handle *handle, +int acpm_pmic_bulk_write(struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 count, const u8 *buf); -int acpm_pmic_update_reg(const struct acpm_handle *handle, +int acpm_pmic_update_reg(struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 value, u8 mask); #endif /* __EXYNOS_ACPM_PMIC_H__ */ diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 0cb269c70460..16c46ed60837 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -205,7 +205,7 @@ static void acpm_get_saved_rx(struct acpm_chan *achan, rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, rx_data->cmd[0]); if (rx_seqnum == tx_seqnum) { - memcpy(xfer->rxd, rx_data->cmd, xfer->rxlen); + memcpy(xfer->rxd, rx_data->cmd, xfer->rxcnt * sizeof(*xfer->rxd)); clear_bit(rx_seqnum - 1, achan->bitmap_seqnum); } } @@ -258,8 +258,7 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) if (rx_data->response) { if (rx_seqnum == tx_seqnum) { - __ioread32_copy(xfer->rxd, addr, - xfer->rxlen / 4); + __ioread32_copy(xfer->rxd, addr, xfer->rxcnt); rx_set = true; clear_bit(seqnum, achan->bitmap_seqnum); } else { @@ -269,8 +268,7 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) * clear yet the bitmap. It will be cleared * after the response is copied to the request. */ - __ioread32_copy(rx_data->cmd, addr, - xfer->rxlen / 4); + __ioread32_copy(rx_data->cmd, addr, xfer->rxcnt); } } else { clear_bit(seqnum, achan->bitmap_seqnum); @@ -412,7 +410,7 @@ static int acpm_wait_for_message_response(struct acpm_chan *achan, * * Return: 0 on success, -errno otherwise. */ -int acpm_do_xfer(const struct acpm_handle *handle, const struct acpm_xfer *xfer) +int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer) { struct acpm_info *acpm = handle_to_acpm_info(handle); struct exynos_mbox_msg msg; @@ -425,7 +423,9 @@ int acpm_do_xfer(const struct acpm_handle *handle, const struct acpm_xfer *xfer) achan = &acpm->chans[xfer->acpm_chan_id]; - if (!xfer->txd || xfer->txlen > achan->mlen || xfer->rxlen > achan->mlen) + if (!xfer->txd || + (xfer->txcnt * sizeof(*xfer->txd) > achan->mlen) || + (xfer->rxcnt * sizeof(*xfer->rxd) > achan->mlen)) return -EINVAL; if (!achan->poll_completion) { @@ -448,7 +448,7 @@ int acpm_do_xfer(const struct acpm_handle *handle, const struct acpm_xfer *xfer) /* Write TX command. */ __iowrite32_copy(achan->tx.base + achan->mlen * tx_front, - xfer->txd, xfer->txlen / 4); + xfer->txd, xfer->txcnt); /* Advance TX front. */ writel(idx, achan->tx.front); @@ -674,7 +674,7 @@ static int acpm_probe(struct platform_device *pdev) * acpm_handle_put() - release the handle acquired by acpm_get_by_phandle. * @handle: Handle acquired by acpm_get_by_phandle. */ -static void acpm_handle_put(const struct acpm_handle *handle) +static void acpm_handle_put(struct acpm_handle *handle) { struct acpm_info *acpm = handle_to_acpm_info(handle); struct device *dev = acpm->dev; @@ -700,9 +700,11 @@ static void devm_acpm_release(struct device *dev, void *res) * @np: ACPM device tree node. * * Return: pointer to handle on success, ERR_PTR(-errno) otherwise. + * + * Note: handle CANNOT be pointer to const */ -static const struct acpm_handle *acpm_get_by_node(struct device *dev, - struct device_node *np) +static struct acpm_handle *acpm_get_by_node(struct device *dev, + struct device_node *np) { struct platform_device *pdev; struct device_link *link; @@ -743,10 +745,10 @@ static const struct acpm_handle *acpm_get_by_node(struct device *dev, * * Return: pointer to handle on success, ERR_PTR(-errno) otherwise. */ -const struct acpm_handle *devm_acpm_get_by_node(struct device *dev, - struct device_node *np) +struct acpm_handle *devm_acpm_get_by_node(struct device *dev, + struct device_node *np) { - const struct acpm_handle **ptr, *handle; + struct acpm_handle **ptr, *handle; ptr = devres_alloc(devm_acpm_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) diff --git a/drivers/firmware/samsung/exynos-acpm.h b/drivers/firmware/samsung/exynos-acpm.h index 2d14cb58f98c..5df8354dc96c 100644 --- a/drivers/firmware/samsung/exynos-acpm.h +++ b/drivers/firmware/samsung/exynos-acpm.h @@ -8,16 +8,16 @@ #define __EXYNOS_ACPM_H__ struct acpm_xfer { - const u32 *txd; - u32 *rxd; - size_t txlen; - size_t rxlen; + const u32 *txd __counted_by_ptr(txcnt); + u32 *rxd __counted_by_ptr(rxcnt); + size_t txcnt; + size_t rxcnt; unsigned int acpm_chan_id; }; struct acpm_handle; -int acpm_do_xfer(const struct acpm_handle *handle, +int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer); #endif /* __EXYNOS_ACPM_H__ */ diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index e74bba7ccc44..753472b53bd8 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -32,6 +32,40 @@ channel_to_ops(struct tegra_bpmp_channel *channel) return bpmp->soc->ops; } +struct tegra_bpmp *tegra_bpmp_get_with_id(struct device *dev, unsigned int *id) +{ + struct platform_device *pdev; + struct of_phandle_args args; + struct tegra_bpmp *bpmp; + int err; + + err = __of_parse_phandle_with_args(dev->of_node, "nvidia,bpmp", NULL, + 1, 0, &args); + if (err < 0) + return ERR_PTR(err); + + pdev = of_find_device_by_node(args.np); + if (!pdev) { + bpmp = ERR_PTR(-ENODEV); + goto put; + } + + bpmp = platform_get_drvdata(pdev); + if (!bpmp) { + bpmp = ERR_PTR(-EPROBE_DEFER); + put_device(&pdev->dev); + goto put; + } + + if (id) + *id = args.args[0]; + +put: + of_node_put(args.np); + return bpmp; +} +EXPORT_SYMBOL_GPL(tegra_bpmp_get_with_id); + struct tegra_bpmp *tegra_bpmp_get(struct device *dev) { struct platform_device *pdev; diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index ca68c38aa4a1..8c275bd48a0d 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -290,7 +290,7 @@ static ssize_t name_show(struct device *dev, { struct fpga_bridge *bridge = to_fpga_bridge(dev); - return sprintf(buf, "%s\n", bridge->name); + return sysfs_emit(buf, "%s\n", bridge->name); } static ssize_t state_show(struct device *dev, diff --git a/drivers/fpga/intel-m10-bmc-sec-update.c b/drivers/fpga/intel-m10-bmc-sec-update.c index 10f678b9ed36..b15dab6a39a3 100644 --- a/drivers/fpga/intel-m10-bmc-sec-update.c +++ b/drivers/fpga/intel-m10-bmc-sec-update.c @@ -183,7 +183,7 @@ show_canceled_csk(struct device *dev, u32 addr, char *buf) bitmap_from_arr32(csk_map, csk32, CSK_BIT_LEN); bitmap_complement(csk_map, csk_map, CSK_BIT_LEN); - return bitmap_print_to_pagebuf(1, buf, csk_map, CSK_BIT_LEN); + return sysfs_emit(buf, "%*pbl\n", CSK_BIT_LEN, csk_map); } #define DEVICE_ATTR_SEC_CSK_RO(_name) \ diff --git a/drivers/fwctl/Kconfig b/drivers/fwctl/Kconfig index b5583b12a011..d1b1925bdaec 100644 --- a/drivers/fwctl/Kconfig +++ b/drivers/fwctl/Kconfig @@ -9,6 +9,17 @@ menuconfig FWCTL fit neatly into an existing subsystem. if FWCTL +config FWCTL_BNXT + tristate "bnxt control fwctl driver" + depends on BNXT + help + BNXT provides interface for the user process to access the debug and + configuration registers of the Broadcom NIC hardware family. + This will allow configuration and debug tools to work out of the box on + mainstream kernel. + + If you don't know what to do here, say N. + config FWCTL_MLX5 tristate "mlx5 ConnectX control fwctl driver" depends on MLX5_CORE diff --git a/drivers/fwctl/Makefile b/drivers/fwctl/Makefile index c093b5f661d6..692e4b8d7beb 100644 --- a/drivers/fwctl/Makefile +++ b/drivers/fwctl/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_FWCTL) += fwctl.o +obj-$(CONFIG_FWCTL_BNXT) += bnxt/ obj-$(CONFIG_FWCTL_MLX5) += mlx5/ obj-$(CONFIG_FWCTL_PDS) += pds/ diff --git a/drivers/fwctl/bnxt/Makefile b/drivers/fwctl/bnxt/Makefile new file mode 100644 index 000000000000..b47172761f1e --- /dev/null +++ b/drivers/fwctl/bnxt/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_FWCTL_BNXT) += bnxt_fwctl.o + +bnxt_fwctl-y += main.o diff --git a/drivers/fwctl/bnxt/main.c b/drivers/fwctl/bnxt/main.c new file mode 100644 index 000000000000..951c8ac2e0a1 --- /dev/null +++ b/drivers/fwctl/bnxt/main.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026, Broadcom Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct bnxtctl_uctx { + struct fwctl_uctx uctx; + u32 uctx_caps; +}; + +struct bnxtctl_dev { + struct fwctl_device fwctl; + struct bnxt_aux_priv *aux_priv; +}; + +DEFINE_FREE(bnxtctl, struct bnxtctl_dev *, if (_T) fwctl_put(&_T->fwctl)) + +static int bnxtctl_open_uctx(struct fwctl_uctx *uctx) +{ + struct bnxtctl_uctx *bnxtctl_uctx = + container_of(uctx, struct bnxtctl_uctx, uctx); + + bnxtctl_uctx->uctx_caps = BIT(FWCTL_BNXT_INLINE_COMMANDS) | + BIT(FWCTL_BNXT_QUERY_COMMANDS) | + BIT(FWCTL_BNXT_SEND_COMMANDS); + return 0; +} + +static void bnxtctl_close_uctx(struct fwctl_uctx *uctx) +{ +} + +static void *bnxtctl_info(struct fwctl_uctx *uctx, size_t *length) +{ + struct bnxtctl_uctx *bnxtctl_uctx = + container_of(uctx, struct bnxtctl_uctx, uctx); + struct fwctl_info_bnxt *info; + + info = kzalloc_obj(*info); + if (!info) + return ERR_PTR(-ENOMEM); + + info->uctx_caps = bnxtctl_uctx->uctx_caps; + + *length = sizeof(*info); + return info; +} + +/* Caller must hold edev->en_dev_lock */ +static bool bnxtctl_validate_rpc(struct bnxt_en_dev *edev, + struct bnxt_fw_msg *hwrm_in, + enum fwctl_rpc_scope scope) +{ + struct input *req = (struct input *)hwrm_in->msg; + + lockdep_assert_held(&edev->en_dev_lock); + if (edev->flags & BNXT_EN_FLAG_ULP_STOPPED) + return false; + + switch (le16_to_cpu(req->req_type)) { + case HWRM_FUNC_RESET: + case HWRM_PORT_CLR_STATS: + case HWRM_FW_RESET: + case HWRM_FW_SYNC: + case HWRM_FW_SET_TIME: + case HWRM_DBG_LOG_BUFFER_FLUSH: + case HWRM_DBG_ERASE_NVM: + case HWRM_DBG_CFG: + case HWRM_NVM_DEFRAG: + case HWRM_NVM_FACTORY_DEFAULTS: + case HWRM_NVM_FLUSH: + case HWRM_NVM_VERIFY_UPDATE: + case HWRM_NVM_ERASE_DIR_ENTRY: + case HWRM_NVM_MOD_DIR_ENTRY: + case HWRM_NVM_FIND_DIR_ENTRY: + return scope >= FWCTL_RPC_CONFIGURATION; + + case HWRM_VER_GET: + case HWRM_ERROR_RECOVERY_QCFG: + case HWRM_FUNC_QCAPS: + case HWRM_FUNC_QCFG: + case HWRM_FUNC_QSTATS: + case HWRM_PORT_PHY_QCFG: + case HWRM_PORT_MAC_QCFG: + case HWRM_PORT_PHY_QCAPS: + case HWRM_PORT_PHY_I2C_READ: + case HWRM_PORT_PHY_MDIO_READ: + case HWRM_QUEUE_PRI2COS_QCFG: + case HWRM_QUEUE_COS2BW_QCFG: + case HWRM_VNIC_RSS_QCFG: + case HWRM_QUEUE_GLOBAL_QCFG: + case HWRM_QUEUE_ADPTV_QOS_RX_FEATURE_QCFG: + case HWRM_QUEUE_ADPTV_QOS_TX_FEATURE_QCFG: + case HWRM_QUEUE_QCAPS: + case HWRM_QUEUE_ADPTV_QOS_RX_TUNING_QCFG: + case HWRM_QUEUE_ADPTV_QOS_TX_TUNING_QCFG: + case HWRM_TUNNEL_DST_PORT_QUERY: + case HWRM_PORT_TX_FIR_QCFG: + case HWRM_FW_LIVEPATCH_QUERY: + case HWRM_FW_QSTATUS: + case HWRM_FW_HEALTH_CHECK: + case HWRM_FW_GET_TIME: + case HWRM_PORT_EP_TX_QCFG: + case HWRM_PORT_QCFG: + case HWRM_PORT_MAC_QCAPS: + case HWRM_TEMP_MONITOR_QUERY: + case HWRM_REG_POWER_QUERY: + case HWRM_CORE_FREQUENCY_QUERY: + case HWRM_CFA_REDIRECT_QUERY_TUNNEL_TYPE: + case HWRM_CFA_ADV_FLOW_MGNT_QCAPS: + case HWRM_FUNC_RESOURCE_QCAPS: + case HWRM_FUNC_BACKING_STORE_QCAPS: + case HWRM_FUNC_BACKING_STORE_QCFG: + case HWRM_FUNC_QSTATS_EXT: + case HWRM_FUNC_PTP_PIN_QCFG: + case HWRM_FUNC_PTP_EXT_QCFG: + case HWRM_FUNC_BACKING_STORE_QCFG_V2: + case HWRM_FUNC_BACKING_STORE_QCAPS_V2: + case HWRM_FUNC_SYNCE_QCFG: + case HWRM_FUNC_TTX_PACING_RATE_PROF_QUERY: + case HWRM_PORT_PHY_FDRSTAT: + case HWRM_DBG_RING_INFO_GET: + case HWRM_DBG_QCAPS: + case HWRM_DBG_QCFG: + case HWRM_DBG_USEQ_FLUSH: + case HWRM_DBG_USEQ_QCAPS: + case HWRM_DBG_SIM_CABLE_STATE: + case HWRM_DBG_TOKEN_QUERY_AUTH_IDS: + case HWRM_NVM_GET_DEV_INFO: + case HWRM_NVM_GET_DIR_INFO: + case HWRM_SELFTEST_QLIST: + return scope >= FWCTL_RPC_DEBUG_READ_ONLY; + + case HWRM_PORT_PHY_I2C_WRITE: + case HWRM_PORT_PHY_MDIO_WRITE: + return scope >= FWCTL_RPC_DEBUG_WRITE; + + default: + return false; + } +} + +#define BNXTCTL_HWRM_CMD_TIMEOUT_DFLT 500 /* ms */ +#define BNXTCTL_HWRM_CMD_TIMEOUT_MEDM 2000 /* ms */ +#define BNXTCTL_HWRM_CMD_TIMEOUT_LONG 60000 /* ms */ + +static unsigned int bnxtctl_get_timeout(struct input *req) +{ + switch (le16_to_cpu(req->req_type)) { + case HWRM_NVM_DEFRAG: + case HWRM_NVM_FACTORY_DEFAULTS: + case HWRM_NVM_FLUSH: + case HWRM_NVM_VERIFY_UPDATE: + case HWRM_NVM_ERASE_DIR_ENTRY: + case HWRM_NVM_MOD_DIR_ENTRY: + return BNXTCTL_HWRM_CMD_TIMEOUT_LONG; + case HWRM_FUNC_RESET: + return BNXTCTL_HWRM_CMD_TIMEOUT_MEDM; + default: + return BNXTCTL_HWRM_CMD_TIMEOUT_DFLT; + } +} + +static void *bnxtctl_fw_rpc(struct fwctl_uctx *uctx, + enum fwctl_rpc_scope scope, + void *in, size_t in_len, size_t *out_len) +{ + struct bnxtctl_dev *bnxtctl = + container_of(uctx->fwctl, struct bnxtctl_dev, fwctl); + struct bnxt_en_dev *edev = bnxtctl->aux_priv->edev; + struct bnxt_fw_msg rpc_in = {0}; + int rc; + + if (in_len < sizeof(struct input) || in_len > HWRM_MAX_REQ_LEN) + return ERR_PTR(-EINVAL); + + if (*out_len < sizeof(struct output)) + return ERR_PTR(-EINVAL); + + rpc_in.msg = in; + rpc_in.msg_len = in_len; + rpc_in.resp = kzalloc(*out_len, GFP_KERNEL); + if (!rpc_in.resp) + return ERR_PTR(-ENOMEM); + + rpc_in.resp_max_len = *out_len; + rpc_in.timeout = bnxtctl_get_timeout(in); + + guard(mutex)(&edev->en_dev_lock); + + if (!bnxtctl_validate_rpc(edev, &rpc_in, scope)) { + kfree(rpc_in.resp); + return ERR_PTR(-EPERM); + } + + rc = bnxt_send_msg(edev, &rpc_in); + if (rc) { + struct output *resp = rpc_in.resp; + + /* Copy the response to user always, as it contains + * detailed status of the command failure + */ + if (!resp->error_code) + /* bnxt_send_msg() returned much before FW + * received the command. + */ + resp->error_code = cpu_to_le16(rc); + } + + return rpc_in.resp; +} + +static const struct fwctl_ops bnxtctl_ops = { + .device_type = FWCTL_DEVICE_TYPE_BNXT, + .uctx_size = sizeof(struct bnxtctl_uctx), + .open_uctx = bnxtctl_open_uctx, + .close_uctx = bnxtctl_close_uctx, + .info = bnxtctl_info, + .fw_rpc = bnxtctl_fw_rpc, +}; + +static int bnxtctl_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct bnxt_aux_priv *aux_priv = + container_of(adev, struct bnxt_aux_priv, aux_dev); + struct bnxtctl_dev *bnxtctl __free(bnxtctl) = + fwctl_alloc_device(&aux_priv->edev->pdev->dev, &bnxtctl_ops, + struct bnxtctl_dev, fwctl); + int rc; + + if (!bnxtctl) + return -ENOMEM; + + bnxtctl->aux_priv = aux_priv; + + rc = fwctl_register(&bnxtctl->fwctl); + if (rc) + return rc; + + auxiliary_set_drvdata(adev, no_free_ptr(bnxtctl)); + return 0; +} + +static void bnxtctl_remove(struct auxiliary_device *adev) +{ + struct bnxtctl_dev *ctldev = auxiliary_get_drvdata(adev); + + fwctl_unregister(&ctldev->fwctl); + fwctl_put(&ctldev->fwctl); +} + +static const struct auxiliary_device_id bnxtctl_id_table[] = { + { .name = "bnxt_en.fwctl", }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, bnxtctl_id_table); + +static struct auxiliary_driver bnxtctl_driver = { + .name = "bnxt_fwctl", + .probe = bnxtctl_probe, + .remove = bnxtctl_remove, + .id_table = bnxtctl_id_table, +}; + +module_auxiliary_driver(bnxtctl_driver); + +MODULE_IMPORT_NS("FWCTL"); +MODULE_DESCRIPTION("BNXT fwctl driver"); +MODULE_AUTHOR("Pavan Chebbi "); +MODULE_AUTHOR("Andy Gospodarek "); +MODULE_LICENSE("GPL"); diff --git a/drivers/fwctl/main.c b/drivers/fwctl/main.c index bc6378506296..098c3824ad75 100644 --- a/drivers/fwctl/main.c +++ b/drivers/fwctl/main.c @@ -415,7 +415,7 @@ static void __exit fwctl_exit(void) unregister_chrdev_region(fwctl_dev, FWCTL_MAX_DEVICES); } -module_init(fwctl_init); +subsys_initcall(fwctl_init); module_exit(fwctl_exit); MODULE_DESCRIPTION("fwctl device firmware access framework"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpib/agilent_82357a/agilent_82357a.c b/drivers/gpib/agilent_82357a/agilent_82357a.c index e1349afbf933..770ba6eb40d1 100644 --- a/drivers/gpib/agilent_82357a/agilent_82357a.c +++ b/drivers/gpib/agilent_82357a/agilent_82357a.c @@ -1479,7 +1479,7 @@ static int agilent_82357a_driver_probe(struct usb_interface *interface, if (mutex_lock_interruptible(&agilent_82357a_hotplug_lock)) return -ERESTARTSYS; - usb_dev = usb_get_dev(interface_to_usbdev(interface)); + usb_dev = interface_to_usbdev(interface); for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) { if (!agilent_82357a_driver_interfaces[i]) { agilent_82357a_driver_interfaces[i] = interface; @@ -1490,14 +1490,12 @@ static int agilent_82357a_driver_probe(struct usb_interface *interface, } } if (i == MAX_NUM_82357A_INTERFACES) { - usb_put_dev(usb_dev); mutex_unlock(&agilent_82357a_hotplug_lock); dev_err(&usb_dev->dev, "out of space in agilent_82357a_driver_interfaces[]\n"); return -1; } path = kmalloc(path_length, GFP_KERNEL); if (!path) { - usb_put_dev(usb_dev); mutex_unlock(&agilent_82357a_hotplug_lock); return -ENOMEM; } @@ -1539,7 +1537,6 @@ static void agilent_82357a_driver_disconnect(struct usb_interface *interface) } if (i == MAX_NUM_82357A_INTERFACES) dev_err(&usb_dev->dev, "unable to find interface - bug?\n"); - usb_put_dev(usb_dev); mutex_unlock(&agilent_82357a_hotplug_lock); } diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c index 97c98f0a7a43..5909274ddc12 100644 --- a/drivers/gpib/common/gpib_os.c +++ b/drivers/gpib/common/gpib_os.c @@ -2219,10 +2219,13 @@ void init_gpib_status_queue(struct gpib_status_queue *device) device->dropped_byte = 0; } -static struct class *gpib_class; +static const struct class gpib_class = { + .name = "gpib_common", +}; static int __init gpib_common_init_module(void) { + int err; int i; pr_info("GPIB core driver\n"); @@ -2231,14 +2234,14 @@ static int __init gpib_common_init_module(void) pr_err("gpib: can't get major %d\n", GPIB_CODE); return -EIO; } - gpib_class = class_create("gpib_common"); - if (IS_ERR(gpib_class)) { + err = class_register(&gpib_class); + if (err) { pr_err("gpib: failed to create gpib class\n"); unregister_chrdev(GPIB_CODE, "gpib"); - return PTR_ERR(gpib_class); + return err; } for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i) - board_array[i].gpib_dev = device_create(gpib_class, NULL, + board_array[i].gpib_dev = device_create(&gpib_class, NULL, MKDEV(GPIB_CODE, i), NULL, "gpib%i", i); return 0; @@ -2249,9 +2252,9 @@ static void __exit gpib_common_exit_module(void) int i; for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i) - device_destroy(gpib_class, MKDEV(GPIB_CODE, i)); + device_destroy(&gpib_class, MKDEV(GPIB_CODE, i)); - class_destroy(gpib_class); + class_unregister(&gpib_class); unregister_chrdev(GPIB_CODE, "gpib"); } diff --git a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c index 0f9d385bc50b..e6ea9422d6f2 100644 --- a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c +++ b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c @@ -51,10 +51,10 @@ MODULE_DESCRIPTION("GPIB driver for LPVO usb devices"); * */ -static const struct usb_device_id skel_table[] = { +static const struct usb_device_id lpvo_table[] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE(usb, skel_table); +MODULE_DEVICE_TABLE(usb, lpvo_table); /* * *** Diagnostics and Debug *** @@ -182,15 +182,11 @@ static int usb_minors[MAX_DEV]; /* usb minors */ static int assigned_usb_minors; /* mask of filled slots */ static struct mutex minors_lock; /* operations on usb_minors are to be protected */ -/* - * usb-skeleton prototypes - */ - -struct usb_skel; -static ssize_t skel_do_write(struct usb_skel *, const char *, size_t); -static ssize_t skel_do_read(struct usb_skel *, char *, size_t); -static int skel_do_open(struct gpib_board *, int); -static int skel_do_release(struct gpib_board *); +struct lpvo; +static ssize_t lpvo_do_write(struct lpvo *, const char *, size_t); +static ssize_t lpvo_do_read(struct lpvo *, char *, size_t); +static int lpvo_do_open(struct gpib_board *, int); +static int lpvo_do_release(struct gpib_board *); /* * usec_diff : take difference in MICROsec between two 'timespec' @@ -218,7 +214,7 @@ static inline int usec_diff(struct timespec64 *a, struct timespec64 *b) static int write_loop(void *dev, char *msg, int leng) { - return skel_do_write(dev, msg, leng); + return lpvo_do_write(dev, msg, leng); } /** @@ -246,7 +242,7 @@ static int send_command(struct gpib_board *board, char *msg, int leng) if (retval < 0) return retval; - nchar = skel_do_read(GPIB_DEV, buffer, 64); + nchar = lpvo_do_read(GPIB_DEV, buffer, 64); if (nchar < 0) { dev_err(board->gpib_dev, " return from read: %d\n", nchar); @@ -310,7 +306,7 @@ static int one_char(struct gpib_board *board, struct char_buf *b) return b->inbuf[b->last - b->nchar--]; } ktime_get_real_ts64 (&before); - b->nchar = skel_do_read(GPIB_DEV, b->inbuf, INBUF_SIZE); + b->nchar = lpvo_do_read(GPIB_DEV, b->inbuf, INBUF_SIZE); b->last = b->nchar; ktime_get_real_ts64 (&after); @@ -445,12 +441,12 @@ static int usb_gpib_attach(struct gpib_board *board, const struct gpib_board_con if (!board->private_data) return -ENOMEM; - retval = skel_do_open(board, usb_minors[j]); + retval = lpvo_do_open(board, usb_minors[j]); - DIA_LOG(1, "Skel open: %d\n", retval); + DIA_LOG(1, "lpvo open: %d\n", retval); if (retval) { - dev_err(board->gpib_dev, "skel open failed.\n"); + dev_err(board->gpib_dev, "lpvo open failed.\n"); kfree(board->private_data); board->private_data = NULL; return -ENODEV; @@ -517,8 +513,8 @@ static void usb_gpib_detach(struct gpib_board *board) write_loop(GPIB_DEV, USB_GPIB_OFF, strlen(USB_GPIB_OFF)); msleep(100); DIA_LOG(1, "%s", "GPIB off\n"); - retval = skel_do_release(board); - DIA_LOG(1, "skel release -> %d\n", retval); + retval = lpvo_do_release(board); + DIA_LOG(1, "lpvo release -> %d\n", retval); } kfree(board->private_data); board->private_data = NULL; @@ -772,8 +768,8 @@ static int usb_gpib_read(struct gpib_board *board, if (retval < 0) return retval; - retval = skel_do_read(GPIB_DEV, inbuf, 1); - retval += skel_do_read(GPIB_DEV, inbuf + 1, 1); + retval = lpvo_do_read(GPIB_DEV, inbuf, 1); + retval += lpvo_do_read(GPIB_DEV, inbuf + 1, 1); ktime_get_real_ts64 (&after); @@ -1197,12 +1193,12 @@ static int write_latency_timer(struct usb_device *udev) * written by Greg Kroah-Hartman and available in the kernel tree. * * * * Functions skel_open() and skel_release() have been rewritten and named * - * skel_do_open() and skel_do_release() to process the attach and detach * + * lpvo_do_open() and lpvo_do_release() to process the attach and detach * * requests coming from gpib_config. * * * - * Functions skel_read() and skel_write() have been split into a * - * skel_do_read() and skel_do_write(), that cover the kernel stuff of read * - * and write operations, and the original skel_read() and skel_write(), * + * Functions lpvo_read() and lpvo_write() have been split into a * + * lpvo_do_read() and lpvo_do_write(), that cover the kernel stuff of read * + * and write operations, and the original lpvo_read() and lpvo_write(), * * that handle communication with user space and call their _do_ companion. * * * * Only the _do_ versions are used by the lpvo_usb_gpib driver; other ones * @@ -1230,7 +1226,7 @@ static int write_latency_timer(struct usb_device *udev) #include /* Get a minor range for your devices from the usb maintainer */ -#define USB_SKEL_MINOR_BASE 192 +#define USB_LPVO_MINOR_BASE 192 /* private defines */ @@ -1245,7 +1241,7 @@ static int write_latency_timer(struct usb_device *udev) #define USER_DEVICE 1 /* compile for device(s) in user space */ /* Structure to hold all of our device specific stuff */ -struct usb_skel { +struct lpvo { struct usb_device *udev; /* the usb device for this device */ struct usb_interface *interface; /* the interface for this device */ struct semaphore limit_sem; /* limiting the number of writes in progress */ @@ -1265,14 +1261,14 @@ struct usb_skel { wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */ }; -#define to_skel_dev(d) container_of(d, struct usb_skel, kref) +#define to_lpvo_dev(d) container_of(d, struct lpvo, kref) -static struct usb_driver skel_driver; -static void skel_draw_down(struct usb_skel *dev); +static struct usb_driver lpvo_driver; +static void lpvo_draw_down(struct lpvo *dev); -static void skel_delete(struct kref *kref) +static void lpvo_delete(struct kref *kref) { - struct usb_skel *dev = to_skel_dev(kref); + struct lpvo *dev = to_lpvo_dev(kref); usb_free_urb(dev->bulk_in_urb); usb_put_dev(dev->udev); @@ -1281,16 +1277,16 @@ static void skel_delete(struct kref *kref) } /* - * skel_do_open() - to be called by usb_gpib_attach + * lpvo_do_open() - to be called by usb_gpib_attach */ -static int skel_do_open(struct gpib_board *board, int subminor) +static int lpvo_do_open(struct gpib_board *board, int subminor) { - struct usb_skel *dev; + struct lpvo *dev; struct usb_interface *interface; int retval = 0; - interface = usb_find_interface(&skel_driver, subminor); + interface = usb_find_interface(&lpvo_driver, subminor); if (!interface) { dev_err(board->gpib_dev, "can't find device for minor %d\n", subminor); retval = -ENODEV; @@ -1318,12 +1314,12 @@ exit: } /* - * skel_do_release() - to be called by usb_gpib_detach + * lpvo_do_release() - to be called by usb_gpib_detach */ -static int skel_do_release(struct gpib_board *board) +static int lpvo_do_release(struct gpib_board *board) { - struct usb_skel *dev; + struct lpvo *dev; dev = GPIB_DEV; if (!dev) @@ -1336,7 +1332,7 @@ static int skel_do_release(struct gpib_board *board) mutex_unlock(&dev->io_mutex); /* decrement the count on our device */ - kref_put(&dev->kref, skel_delete); + kref_put(&dev->kref, lpvo_delete); return 0; } @@ -1344,9 +1340,9 @@ static int skel_do_release(struct gpib_board *board) * read functions */ -static void skel_read_bulk_callback(struct urb *urb) +static void lpvo_read_bulk_callback(struct urb *urb) { - struct usb_skel *dev; + struct lpvo *dev; unsigned long flags; dev = urb->context; @@ -1370,7 +1366,7 @@ static void skel_read_bulk_callback(struct urb *urb) wake_up_interruptible(&dev->bulk_in_wait); } -static int skel_do_read_io(struct usb_skel *dev, size_t count) +static int lpvo_do_read_io(struct lpvo *dev, size_t count) { int rv; @@ -1381,7 +1377,7 @@ static int skel_do_read_io(struct usb_skel *dev, size_t count) dev->bulk_in_endpoint_addr), dev->bulk_in_buffer, min(dev->bulk_in_size, count), - skel_read_bulk_callback, + lpvo_read_bulk_callback, dev); /* tell everybody to leave the URB alone */ spin_lock_irq(&dev->err_lock); @@ -1406,10 +1402,10 @@ static int skel_do_read_io(struct usb_skel *dev, size_t count) } /* - * skel_do_read() - read operations from lpvo_usb_gpib + * lpvo_do_read() - read operations from lpvo_usb_gpib */ -static ssize_t skel_do_read(struct usb_skel *dev, char *buffer, size_t count) +static ssize_t lpvo_do_read(struct lpvo *dev, char *buffer, size_t count) { int rv; bool ongoing_io; @@ -1487,7 +1483,7 @@ retry: * it seems that requests for less than dev->bulk_in_size * are not accepted */ - rv = skel_do_read_io(dev, dev->bulk_in_size); + rv = lpvo_do_read_io(dev, dev->bulk_in_size); if (rv < 0) goto exit; else @@ -1534,10 +1530,10 @@ retry: * asked for by the lpvo_usb_gpib layer. */ // if (available < count) -// skel_do_read_io(dev, dev->bulk_in_size); +// lpvo_do_read_io(dev, dev->bulk_in_size); } else { /* no data in the buffer */ - rv = skel_do_read_io(dev, dev->bulk_in_size); + rv = lpvo_do_read_io(dev, dev->bulk_in_size); if (rv < 0) goto exit; else @@ -1557,9 +1553,9 @@ exit: * write functions */ -static void skel_write_bulk_callback(struct urb *urb) +static void lpvo_write_bulk_callback(struct urb *urb) { - struct usb_skel *dev; + struct lpvo *dev; unsigned long flags; dev = urb->context; @@ -1584,10 +1580,10 @@ static void skel_write_bulk_callback(struct urb *urb) } /* - * skel_do_write() - write operations from lpvo_usb_gpib + * lpvo_do_write() - write operations from lpvo_usb_gpib */ -static ssize_t skel_do_write(struct usb_skel *dev, const char *buffer, size_t count) +static ssize_t lpvo_do_write(struct lpvo *dev, const char *buffer, size_t count) { int retval = 0; struct urb *urb = NULL; @@ -1655,7 +1651,7 @@ static ssize_t skel_do_write(struct usb_skel *dev, const char *buffer, size_t co /* initialize the urb properly */ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpoint_addr), - buf, writesize, skel_write_bulk_callback, dev); + buf, writesize, lpvo_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_anchor_urb(urb, &dev->submitted); @@ -1694,9 +1690,9 @@ exit: #if USER_DEVICE /* conditional compilation of user space device */ -static int skel_flush(struct file *file, fl_owner_t id) +static int lpvo_flush(struct file *file, fl_owner_t id) { - struct usb_skel *dev; + struct lpvo *dev; int res; dev = file->private_data; @@ -1705,7 +1701,7 @@ static int skel_flush(struct file *file, fl_owner_t id) /* wait for io to stop */ mutex_lock(&dev->io_mutex); - skel_draw_down(dev); + lpvo_draw_down(dev); /* read out errors, leave subsequent opens a clean slate */ spin_lock_irq(&dev->err_lock); @@ -1718,16 +1714,16 @@ static int skel_flush(struct file *file, fl_owner_t id) return res; } -static int skel_open(struct inode *inode, struct file *file) +static int lpvo_open(struct inode *inode, struct file *file) { - struct usb_skel *dev; + struct lpvo *dev; struct usb_interface *interface; int subminor; int retval = 0; subminor = iminor(inode); - interface = usb_find_interface(&skel_driver, subminor); + interface = usb_find_interface(&lpvo_driver, subminor); if (!interface) { pr_err("can't find device for minor %d\n", subminor); retval = -ENODEV; @@ -1754,9 +1750,9 @@ exit: return retval; } -static int skel_release(struct inode *inode, struct file *file) +static int lpvo_release(struct inode *inode, struct file *file) { - struct usb_skel *dev; + struct lpvo *dev; dev = file->private_data; if (!dev) @@ -1769,7 +1765,7 @@ static int skel_release(struct inode *inode, struct file *file) mutex_unlock(&dev->io_mutex); /* decrement the count on our device */ - kref_put(&dev->kref, skel_delete); + kref_put(&dev->kref, lpvo_delete); return 0; } @@ -1777,10 +1773,10 @@ static int skel_release(struct inode *inode, struct file *file) * user space access to read function */ -static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, +static ssize_t lpvo_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - struct usb_skel *dev; + struct lpvo *dev; char *buf; ssize_t rv; @@ -1790,7 +1786,7 @@ static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, if (!buf) return -ENOMEM; - rv = skel_do_read(dev, buf, count); + rv = lpvo_do_read(dev, buf, count); if (rv > 0) { if (copy_to_user(buffer, buf, rv)) { @@ -1806,10 +1802,10 @@ static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, * user space access to write function */ -static ssize_t skel_write(struct file *file, const char __user *user_buffer, +static ssize_t lpvo_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *ppos) { - struct usb_skel *dev; + struct lpvo *dev; char *buf; ssize_t rv; @@ -1824,20 +1820,20 @@ static ssize_t skel_write(struct file *file, const char __user *user_buffer, return -EFAULT; } - rv = skel_do_write(dev, buf, count); + rv = lpvo_do_write(dev, buf, count); kfree(buf); return rv; } #endif -static const struct file_operations skel_fops = { +static const struct file_operations lpvo_fops = { .owner = THIS_MODULE, #if USER_DEVICE - .read = skel_read, - .write = skel_write, - .open = skel_open, - .release = skel_release, - .flush = skel_flush, + .read = lpvo_read, + .write = lpvo_write, + .open = lpvo_open, + .release = lpvo_release, + .flush = lpvo_flush, .llseek = noop_llseek, #endif }; @@ -1847,17 +1843,17 @@ static const struct file_operations skel_fops = { * and to have the device registered with the driver core */ #if USER_DEVICE -static struct usb_class_driver skel_class = { +static struct usb_class_driver lpvo_class = { .name = "lpvo_raw%d", - .fops = &skel_fops, - .minor_base = USB_SKEL_MINOR_BASE, + .fops = &lpvo_fops, + .minor_base = USB_LPVO_MINOR_BASE, }; #endif -static int skel_probe(struct usb_interface *interface, +static int lpvo_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct usb_skel *dev; + struct lpvo *dev; struct usb_endpoint_descriptor *bulk_in, *bulk_out; int retval; char *device_path; @@ -1916,7 +1912,7 @@ static int skel_probe(struct usb_interface *interface, #if USER_DEVICE /* we can register the device now, as it is ready */ - retval = usb_register_dev(interface, &skel_class); + retval = usb_register_dev(interface, &lpvo_class); if (retval) { /* something prevented us from registering this driver */ dev_err(&interface->dev, @@ -1934,14 +1930,14 @@ static int skel_probe(struct usb_interface *interface, error: /* this frees allocated memory */ - kref_put(&dev->kref, skel_delete); + kref_put(&dev->kref, lpvo_delete); return retval; } -static void skel_disconnect(struct usb_interface *interface) +static void lpvo_disconnect(struct usb_interface *interface) { - struct usb_skel *dev; + struct lpvo *dev; int minor = interface->minor; usb_gpib_exit_module(minor); /* first, disactivate the lpvo */ @@ -1951,7 +1947,7 @@ static void skel_disconnect(struct usb_interface *interface) #if USER_DEVICE /* give back our minor */ - usb_deregister_dev(interface, &skel_class); + usb_deregister_dev(interface, &lpvo_class); #endif /* prevent more I/O from starting */ @@ -1962,10 +1958,10 @@ static void skel_disconnect(struct usb_interface *interface) usb_kill_anchored_urbs(&dev->submitted); /* decrement our usage count */ - kref_put(&dev->kref, skel_delete); + kref_put(&dev->kref, lpvo_delete); } -static void skel_draw_down(struct usb_skel *dev) +static void lpvo_draw_down(struct lpvo *dev) { int time; @@ -1975,34 +1971,34 @@ static void skel_draw_down(struct usb_skel *dev) usb_kill_urb(dev->bulk_in_urb); } -static int skel_suspend(struct usb_interface *intf, pm_message_t message) +static int lpvo_suspend(struct usb_interface *intf, pm_message_t message) { - struct usb_skel *dev = usb_get_intfdata(intf); + struct lpvo *dev = usb_get_intfdata(intf); if (!dev) return 0; - skel_draw_down(dev); + lpvo_draw_down(dev); return 0; } -static int skel_resume(struct usb_interface *intf) +static int lpvo_resume(struct usb_interface *intf) { return 0; } -static int skel_pre_reset(struct usb_interface *intf) +static int lpvo_pre_reset(struct usb_interface *intf) { - struct usb_skel *dev = usb_get_intfdata(intf); + struct lpvo *dev = usb_get_intfdata(intf); mutex_lock(&dev->io_mutex); - skel_draw_down(dev); + lpvo_draw_down(dev); return 0; } -static int skel_post_reset(struct usb_interface *intf) +static int lpvo_post_reset(struct usb_interface *intf) { - struct usb_skel *dev = usb_get_intfdata(intf); + struct lpvo *dev = usb_get_intfdata(intf); /* we are sure no URBs are active - no locking needed */ dev->errors = -EPIPE; @@ -2011,16 +2007,16 @@ static int skel_post_reset(struct usb_interface *intf) return 0; } -static struct usb_driver skel_driver = { +static struct usb_driver lpvo_driver = { .name = NAME, - .probe = skel_probe, - .disconnect = skel_disconnect, - .suspend = skel_suspend, - .resume = skel_resume, - .pre_reset = skel_pre_reset, - .post_reset = skel_post_reset, - .id_table = skel_table, + .probe = lpvo_probe, + .disconnect = lpvo_disconnect, + .suspend = lpvo_suspend, + .resume = lpvo_resume, + .pre_reset = lpvo_pre_reset, + .post_reset = lpvo_post_reset, + .id_table = lpvo_table, .supports_autosuspend = 1, }; -module_usb_driver(skel_driver); +module_usb_driver(lpvo_driver); diff --git a/drivers/gpib/nec7210/nec7210.c b/drivers/gpib/nec7210/nec7210.c index bbf39367f5e4..f15d38dfa4cc 100644 --- a/drivers/gpib/nec7210/nec7210.c +++ b/drivers/gpib/nec7210/nec7210.c @@ -1107,15 +1107,3 @@ void nec7210_locking_iomem_write_byte(struct nec7210_priv *priv, u8 data, spin_unlock_irqrestore(&priv->register_page_lock, flags); } EXPORT_SYMBOL(nec7210_locking_iomem_write_byte); - -static int __init nec7210_init_module(void) -{ - return 0; -} - -static void __exit nec7210_exit_module(void) -{ -} - -module_init(nec7210_init_module); -module_exit(nec7210_exit_module); diff --git a/drivers/gpib/ni_usb/ni_usb_gpib.c b/drivers/gpib/ni_usb/ni_usb_gpib.c index a24cd6521362..0bbc13ecebf9 100644 --- a/drivers/gpib/ni_usb/ni_usb_gpib.c +++ b/drivers/gpib/ni_usb/ni_usb_gpib.c @@ -2431,7 +2431,6 @@ static int ni_usb_driver_probe(struct usb_interface *interface, const struct usb static const int path_length = 1024; mutex_lock(&ni_usb_hotplug_lock); - usb_get_dev(usb_dev); for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) { if (!ni_usb_driver_interfaces[i]) { ni_usb_driver_interfaces[i] = interface; @@ -2440,14 +2439,12 @@ static int ni_usb_driver_probe(struct usb_interface *interface, const struct usb } } if (i == MAX_NUM_NI_USB_INTERFACES) { - usb_put_dev(usb_dev); mutex_unlock(&ni_usb_hotplug_lock); dev_err(&usb_dev->dev, "ni_usb_driver_interfaces[] full\n"); return -1; } path = kmalloc(path_length, GFP_KERNEL); if (!path) { - usb_put_dev(usb_dev); mutex_unlock(&ni_usb_hotplug_lock); return -ENOMEM; } @@ -2488,7 +2485,6 @@ static void ni_usb_driver_disconnect(struct usb_interface *interface) } if (i == MAX_NUM_NI_USB_INTERFACES) dev_err(&usb_dev->dev, "unable to find interface bug?\n"); - usb_put_dev(usb_dev); mutex_unlock(&ni_usb_hotplug_lock); } diff --git a/drivers/gpib/tms9914/tms9914.c b/drivers/gpib/tms9914/tms9914.c index 72a11596a35e..1411297e6217 100644 --- a/drivers/gpib/tms9914/tms9914.c +++ b/drivers/gpib/tms9914/tms9914.c @@ -899,16 +899,3 @@ void tms9914_iomem_write_byte(struct tms9914_priv *priv, u8 data, unsigned int r udelay(1); } EXPORT_SYMBOL_GPL(tms9914_iomem_write_byte); - -static int __init tms9914_init_module(void) -{ - return 0; -} - -static void __exit tms9914_exit_module(void) -{ -} - -module_init(tms9914_init_module); -module_exit(tms9914_exit_module); - diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c5ede0e4a32a..020e51e30317 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -580,6 +580,7 @@ config GPIO_PL061 config GPIO_POLARFIRE_SOC bool "Microchip FPGA GPIO support" select REGMAP_MMIO + select GPIOLIB_IRQCHIP help Say yes here to support the GPIO controllers on Microchip FPGAs. diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index e6af7f3fba5e..dc53b2decb66 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -42,8 +42,8 @@ #define GPIO_G7_CTRL_IRQ_TYPE1 BIT(4) #define GPIO_G7_CTRL_IRQ_TYPE2 BIT(5) #define GPIO_G7_CTRL_RST_TOLERANCE BIT(6) -#define GPIO_G7_CTRL_DEBOUNCE_SEL1 BIT(7) -#define GPIO_G7_CTRL_DEBOUNCE_SEL2 BIT(8) +#define GPIO_G7_CTRL_DEBOUNCE_SEL2 BIT(7) +#define GPIO_G7_CTRL_DEBOUNCE_SEL1 BIT(8) #define GPIO_G7_CTRL_INPUT_MASK BIT(9) #define GPIO_G7_CTRL_IRQ_STS BIT(12) #define GPIO_G7_CTRL_IN_DATA BIT(13) diff --git a/drivers/gpio/gpio-mpfs.c b/drivers/gpio/gpio-mpfs.c index 9468795b9634..1a4cf213c723 100644 --- a/drivers/gpio/gpio-mpfs.c +++ b/drivers/gpio/gpio-mpfs.c @@ -9,8 +9,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -18,7 +19,7 @@ #define MPFS_GPIO_CTRL(i) (0x4 * (i)) #define MPFS_MAX_NUM_GPIO 32 -#define MPFS_GPIO_EN_INT 3 +#define MPFS_GPIO_EN_INT BIT(3) #define MPFS_GPIO_EN_OUT_BUF BIT(2) #define MPFS_GPIO_EN_IN BIT(1) #define MPFS_GPIO_EN_OUT BIT(0) @@ -52,6 +53,7 @@ static const struct regmap_config mpfs_gpio_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, + .use_raw_spinlock = true, }; static int mpfs_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio_index) @@ -114,13 +116,98 @@ static int mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int valu return ret; } +static int mpfs_gpio_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + int gpio_index = irqd_to_hwirq(data) % 32; + u32 interrupt_type; + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_BOTH; + break; + case IRQ_TYPE_EDGE_FALLING: + interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_NEG; + break; + case IRQ_TYPE_EDGE_RISING: + interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_POS; + break; + case IRQ_TYPE_LEVEL_HIGH: + interrupt_type = MPFS_GPIO_TYPE_INT_LEVEL_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + interrupt_type = MPFS_GPIO_TYPE_INT_LEVEL_LOW; + break; + } + + regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), + MPFS_GPIO_TYPE_INT_MASK, interrupt_type); + + return 0; +} + +static void mpfs_gpio_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + int gpio_index = irqd_to_hwirq(data) % 32; + + gpiochip_enable_irq(gc, gpio_index); + mpfs_gpio_direction_input(gc, gpio_index); + regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), + MPFS_GPIO_EN_INT, MPFS_GPIO_EN_INT); +} + +static void mpfs_gpio_irq_mask(struct irq_data *data) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + int gpio_index = irqd_to_hwirq(data) % 32; + + regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), + MPFS_GPIO_EN_INT, 0); + gpiochip_disable_irq(gc, gpio_index); +} + +static const struct irq_chip mpfs_gpio_irqchip = { + .name = "MPFS GPIO", + .irq_set_type = mpfs_gpio_irq_set_type, + .irq_mask = mpfs_gpio_irq_mask, + .irq_unmask = mpfs_gpio_irq_unmask, + .flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void mpfs_gpio_irq_handler(struct irq_desc *desc) +{ + struct irq_chip *irqchip = irq_desc_get_chip(desc); + struct mpfs_gpio_chip *mpfs_gpio = irq_desc_get_handler_data(desc); + unsigned long status; + u32 val; + int i; + + chained_irq_enter(irqchip, desc); + + regmap_read(mpfs_gpio->regs, MPFS_IRQ_REG, &val); + status = val; + for_each_set_bit(i, &status, MPFS_MAX_NUM_GPIO) { + regmap_write(mpfs_gpio->regs, MPFS_IRQ_REG, BIT(i)); + generic_handle_domain_irq(mpfs_gpio->gc.irq.domain, i); + } + + chained_irq_exit(irqchip, desc); +} + static int mpfs_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; struct mpfs_gpio_chip *mpfs_gpio; + struct gpio_irq_chip *girq; struct clk *clk; void __iomem *base; - int ngpios; + int ngpios, nirqs, ret; mpfs_gpio = devm_kzalloc(dev, sizeof(*mpfs_gpio), GFP_KERNEL); if (!mpfs_gpio) @@ -157,6 +244,35 @@ static int mpfs_gpio_probe(struct platform_device *pdev) mpfs_gpio->gc.parent = dev; mpfs_gpio->gc.owner = THIS_MODULE; + nirqs = of_irq_count(node); + if (nirqs > MPFS_MAX_NUM_GPIO) + return -ENXIO; + + if (nirqs) { + girq = &mpfs_gpio->gc.irq; + + gpio_irq_chip_set_chip(girq, &mpfs_gpio_irqchip); + + girq->num_parents = nirqs; + girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, + sizeof(*girq->parents), GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + + for (int i = 0; i < nirqs; i++) { + ret = platform_get_irq(pdev, i); + if (ret < 0) + return ret; + + girq->parents[i] = ret; + girq->parent_handler_data = mpfs_gpio; + girq->parent_handler = mpfs_gpio_irq_handler; + } + + girq->handler = handle_level_irq; + girq->default_type = IRQ_TYPE_NONE; + } + return devm_gpiochip_add_data(dev, &mpfs_gpio->gc, mpfs_gpio); } diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index 50f3733a455d..44d7ebd12724 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -296,7 +296,7 @@ static int rockchip_gpio_set_config(struct gpio_chip *gc, unsigned int offset, */ return -ENOTSUPP; default: - return -ENOTSUPP; + return gpiochip_generic_config(gc, offset, config); } } @@ -617,7 +617,7 @@ static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank) return -ENODEV; ret = gpiochip_add_pin_range(gc, dev_name(pctldev->dev), 0, - gc->base, gc->ngpio); + bank->pin_base, bank->nr_pins); if (ret) { dev_err(bank->dev, "Failed to add pin range\n"); goto fail; diff --git a/drivers/gpio/gpio-spacemit-k1.c b/drivers/gpio/gpio-spacemit-k1.c index dbd2e81094b9..5fe813b7f9bb 100644 --- a/drivers/gpio/gpio-spacemit-k1.c +++ b/drivers/gpio/gpio-spacemit-k1.c @@ -228,6 +228,7 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg, gc->label = dev_name(dev); gc->request = gpiochip_generic_request; gc->free = gpiochip_generic_free; + gc->set_config = gpiochip_generic_config; gc->ngpio = SPACEMIT_NR_GPIOS_PER_BANK; gc->base = -1; gc->of_gpio_n_cells = 3; diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig b/drivers/gpu/drm/amd/amdgpu/Kconfig index 7f515be5185d..7fb0b93bc1ca 100644 --- a/drivers/gpu/drm/amd/amdgpu/Kconfig +++ b/drivers/gpu/drm/amd/amdgpu/Kconfig @@ -103,6 +103,23 @@ config DRM_AMDGPU_WERROR Add -Werror to the build flags for amdgpu.ko. Only enable this if you are warning code for amdgpu.ko. + +config GCOV_PROFILE_AMDGPU + bool "Enable GCOV profiling on amdgpu" + depends on DRM_AMDGPU + depends on GCOV_KERNEL + default n + help + Enable GCOV profiling on the amdgpu driver for checking which + functions/lines are executed during testing. This adds compiler + instrumentation flags to all amdgpu source files, producing + .gcda/.gcno coverage data accessible via debugfs. + + This increases the amdgpu module size by ~50% and adds ~2-5% + runtime overhead on GPU submission paths. + + If unsure, say N. + source "drivers/gpu/drm/amd/acp/Kconfig" source "drivers/gpu/drm/amd/display/Kconfig" source "drivers/gpu/drm/amd/amdkfd/Kconfig" diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile index 6a7e9bfec59e..db66c6372199 100644 --- a/drivers/gpu/drm/amd/amdgpu/Makefile +++ b/drivers/gpu/drm/amd/amdgpu/Makefile @@ -27,6 +27,10 @@ FULL_AMD_PATH=$(src)/.. DISPLAY_FOLDER_NAME=display FULL_AMD_DISPLAY_PATH = $(FULL_AMD_PATH)/$(DISPLAY_FOLDER_NAME) +ifdef CONFIG_GCOV_PROFILE_AMDGPU +GCOV_PROFILE := y +endif + ccflags-y := -I$(FULL_AMD_PATH)/include/asic_reg \ -I$(FULL_AMD_PATH)/include \ -I$(FULL_AMD_PATH)/amdgpu \ diff --git a/drivers/gpu/drm/amd/amdgpu/aldebaran.c b/drivers/gpu/drm/amd/amdgpu/aldebaran.c index 938fb0b2368d..8686c6dc2c08 100644 --- a/drivers/gpu/drm/amd/amdgpu/aldebaran.c +++ b/drivers/gpu/drm/amd/amdgpu/aldebaran.c @@ -179,7 +179,7 @@ aldebaran_mode2_perform_reset(struct amdgpu_reset_control *reset_ctl, list_for_each_entry(tmp_adev, reset_device_list, reset_list) { /* For XGMI run all resets in parallel to speed up the process */ if (tmp_adev->gmc.xgmi.num_physical_nodes > 1) { - if (!queue_work(system_unbound_wq, + if (!queue_work(system_dfl_wq, &tmp_adev->reset_cntl->reset_work)) r = -EALREADY; } else diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 49e7881750fa..8bc591deb546 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1045,11 +1045,6 @@ struct amdgpu_device { struct amdgpu_mqd mqds[AMDGPU_HW_IP_NUM]; const struct amdgpu_userq_funcs *userq_funcs[AMDGPU_HW_IP_NUM]; - /* xarray used to retrieve the user queue fence driver reference - * in the EOP interrupt handler to signal the particular user - * queue fence. - */ - struct xarray userq_xa; /** * @userq_doorbell_xa: Global user queue map (doorbell index → queue) * Key: doorbell_index (unique global identifier for the queue) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 4f27c75abedb..d9e283f3b57d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -805,7 +805,10 @@ u64 amdgpu_amdkfd_xcp_memory_size(struct amdgpu_device *adev, int xcp_id) } else { tmp = adev->gmc.mem_partitions[mem_id].size; } - do_div(tmp, adev->xcp_mgr->num_xcp_per_mem_partition); + + if (adev->xcp_mgr->mem_alloc_mode == AMDGPU_PARTITION_MEM_CAPPING_EVEN) + do_div(tmp, adev->xcp_mgr->num_xcp_per_mem_partition); + return ALIGN_DOWN(tmp, PAGE_SIZE); } else if (adev->apu_prefer_gtt) { return (ttm_tt_pages_limit() << PAGE_SHIFT); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 29b400cdd6d5..72a5a29e63f6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -1735,7 +1735,8 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu( alloc_domain = AMDGPU_GEM_DOMAIN_GTT; alloc_flags = 0; } else { - alloc_flags = AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE; + alloc_flags = AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE | + AMDGPU_GEM_CREATE_VRAM_CLEARED; alloc_flags |= (flags & KFD_IOC_ALLOC_MEM_FLAGS_PUBLIC) ? AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED : 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c index 9f38b7dd1011..3698dd0330ff 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c @@ -1685,9 +1685,9 @@ static int amdgpu_atombios_allocate_fb_scratch(struct amdgpu_device *adev) (uint32_t)(ATOM_VRAM_BLOCK_SRIOV_MSG_SHARE_RESERVATION << ATOM_VRAM_OPERATION_FLAGS_SHIFT)) { /* Firmware request VRAM reservation for SR-IOV */ - adev->mman.fw_vram_usage_start_offset = (start_addr & - (~ATOM_VRAM_OPERATION_FLAGS_MASK)) << 10; - adev->mman.fw_vram_usage_size = size << 10; + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_FW_VRAM_USAGE, + (start_addr & (~ATOM_VRAM_OPERATION_FLAGS_MASK)) << 10, + size << 10, true); /* Use the default scratch size */ usage_bytes = 0; } else { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c index cd9aa5b45e94..6860a3a4d466 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c @@ -120,9 +120,9 @@ static int amdgpu_atomfirmware_allocate_fb_v2_1(struct amdgpu_device *adev, (u32)(ATOM_VRAM_BLOCK_SRIOV_MSG_SHARE_RESERVATION << ATOM_VRAM_OPERATION_FLAGS_SHIFT)) { /* Firmware request VRAM reservation for SR-IOV */ - adev->mman.fw_vram_usage_start_offset = (start_addr & - (~ATOM_VRAM_OPERATION_FLAGS_MASK)) << 10; - adev->mman.fw_vram_usage_size = fw_size << 10; + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_FW_VRAM_USAGE, + (start_addr & (~ATOM_VRAM_OPERATION_FLAGS_MASK)) << 10, + fw_size << 10, true); /* Use the default scratch size */ *usage_bytes = 0; } else { @@ -152,18 +152,18 @@ static int amdgpu_atomfirmware_allocate_fb_v2_2(struct amdgpu_device *adev, ((fw_start_addr & (ATOM_VRAM_BLOCK_NEEDS_NO_RESERVATION << ATOM_VRAM_OPERATION_FLAGS_SHIFT)) == 0)) { /* Firmware request VRAM reservation for SR-IOV */ - adev->mman.fw_vram_usage_start_offset = (fw_start_addr & - (~ATOM_VRAM_OPERATION_FLAGS_MASK)) << 10; - adev->mman.fw_vram_usage_size = fw_size << 10; + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_FW_VRAM_USAGE, + (fw_start_addr & (~ATOM_VRAM_OPERATION_FLAGS_MASK)) << 10, + fw_size << 10, true); } if (amdgpu_sriov_vf(adev) && ((drv_start_addr & (ATOM_VRAM_BLOCK_NEEDS_NO_RESERVATION << ATOM_VRAM_OPERATION_FLAGS_SHIFT)) == 0)) { /* driver request VRAM reservation for SR-IOV */ - adev->mman.drv_vram_usage_start_offset = (drv_start_addr & - (~ATOM_VRAM_OPERATION_FLAGS_MASK)) << 10; - adev->mman.drv_vram_usage_size = drv_size << 10; + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_DRV_VRAM_USAGE, + (drv_start_addr & (~ATOM_VRAM_OPERATION_FLAGS_MASK)) << 10, + drv_size << 10, true); } *usage_bytes = 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c index b04fa9fd90b7..92c98e999efe 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c @@ -866,6 +866,7 @@ amdgpu_connector_vga_detect(struct drm_connector *connector, bool force) if (dret) { amdgpu_connector->detected_by_load = false; drm_edid_free(amdgpu_connector->edid); + amdgpu_connector->edid = NULL; amdgpu_connector_get_edid(connector); if (!amdgpu_connector->edid) { @@ -882,6 +883,7 @@ amdgpu_connector_vga_detect(struct drm_connector *connector, bool force) */ if (amdgpu_connector->use_digital && amdgpu_connector->shared_ddc) { drm_edid_free(amdgpu_connector->edid); + amdgpu_connector->edid = NULL; ret = connector_status_disconnected; } else { ret = connector_status_connected; @@ -977,6 +979,7 @@ static void amdgpu_connector_shared_ddc(enum drm_connector_status *status, if (!amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) { drm_edid_free(amdgpu_connector->edid); + amdgpu_connector->edid = NULL; *status = connector_status_disconnected; } } @@ -1046,6 +1049,7 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force) if (dret) { amdgpu_connector->detected_by_load = false; drm_edid_free(amdgpu_connector->edid); + amdgpu_connector->edid = NULL; amdgpu_connector_get_edid(connector); if (!amdgpu_connector->edid) { @@ -1062,6 +1066,7 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force) */ if ((!amdgpu_connector->use_digital) && amdgpu_connector->shared_ddc) { drm_edid_free(amdgpu_connector->edid); + amdgpu_connector->edid = NULL; ret = connector_status_disconnected; } else { ret = connector_status_connected; @@ -1412,6 +1417,7 @@ amdgpu_connector_dp_detect(struct drm_connector *connector, bool force) } drm_edid_free(amdgpu_connector->edid); + amdgpu_connector->edid = NULL; if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c index c72c345334d0..4e6e390854e6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c @@ -32,6 +32,8 @@ static const guid_t BOOT = BOOT_TYPE; static const guid_t CRASHDUMP = AMD_CRASHDUMP; static const guid_t RUNTIME = AMD_GPU_NONSTANDARD_ERROR; +#define CPER_SIGNATURE_SZ (sizeof(((struct cper_hdr *)0)->signature)) + static void __inc_entry_length(struct cper_hdr *hdr, uint32_t size) { hdr->record_length += size; @@ -425,23 +427,40 @@ int amdgpu_cper_generate_ce_records(struct amdgpu_device *adev, static bool amdgpu_cper_is_hdr(struct amdgpu_ring *ring, u64 pos) { - struct cper_hdr *chdr; + char signature[CPER_SIGNATURE_SZ]; - chdr = (struct cper_hdr *)&(ring->ring[pos]); - return strcmp(chdr->signature, "CPER") ? false : true; + if ((pos << 2) >= ring->ring_size) + return false; + + if ((pos << 2) + CPER_SIGNATURE_SZ <= ring->ring_size) { + memcpy(signature, &ring->ring[pos], CPER_SIGNATURE_SZ); + } else { + u32 chunk = ring->ring_size - (pos << 2); + + memcpy(signature, &ring->ring[pos], chunk); + memcpy(signature + chunk, ring->ring, CPER_SIGNATURE_SZ - chunk); + } + + return !memcmp(signature, "CPER", CPER_SIGNATURE_SZ); } static u32 amdgpu_cper_ring_get_ent_sz(struct amdgpu_ring *ring, u64 pos) { - struct cper_hdr *chdr; + struct cper_hdr chdr; u64 p; u32 chunk, rec_len = 0; - chdr = (struct cper_hdr *)&(ring->ring[pos]); chunk = ring->ring_size - (pos << 2); - if (!strcmp(chdr->signature, "CPER")) { - rec_len = chdr->record_length; + if (amdgpu_cper_is_hdr(ring, pos)) { + if (chunk >= sizeof(chdr)) { + memcpy(&chdr, &ring->ring[pos], sizeof(chdr)); + } else { + memcpy(&chdr, &ring->ring[pos], chunk); + memcpy((u8 *)&chdr + chunk, ring->ring, sizeof(chdr) - chunk); + } + + rec_len = chdr.record_length; goto calc; } @@ -450,8 +469,7 @@ static u32 amdgpu_cper_ring_get_ent_sz(struct amdgpu_ring *ring, u64 pos) goto calc; for (p = pos + 1; p <= ring->buf_mask; p++) { - chdr = (struct cper_hdr *)&(ring->ring[p]); - if (!strcmp(chdr->signature, "CPER")) { + if (amdgpu_cper_is_hdr(ring, p)) { rec_len = (p - pos) << 2; goto calc; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index fddf4e1252bd..d386bc775d03 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -210,12 +210,24 @@ static void amdgpu_devcoredump_fw_info(struct amdgpu_device *adev, static ssize_t amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_info *coredump) { + struct amdgpu_device *adev = coredump->adev; struct drm_printer p; struct drm_print_iterator iter; struct amdgpu_vm_fault_info *fault_info; + struct amdgpu_bo_va_mapping *mapping; struct amdgpu_ip_block *ip_block; - int ver; + struct amdgpu_res_cursor cursor; + struct amdgpu_bo *abo, *root; + uint64_t va_start, offset; + struct amdgpu_ring *ring; + struct amdgpu_vm *vm; + u32 *ib_content; + uint8_t *kptr; + int ver, i, j, r; + u32 ring_idx, off; + bool sizing_pass; + sizing_pass = buffer == NULL; iter.data = buffer; iter.offset = 0; iter.remain = count; @@ -303,23 +315,25 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf /* Add ring buffer information */ drm_printf(&p, "Ring buffer information\n"); - for (int i = 0; i < coredump->adev->num_rings; i++) { - int j = 0; - struct amdgpu_ring *ring = coredump->adev->rings[i]; + if (coredump->num_rings) { + for (i = 0; i < coredump->num_rings; i++) { + ring_idx = coredump->rings[i].ring_index; + ring = coredump->adev->rings[ring_idx]; + off = coredump->rings[i].offset; - drm_printf(&p, "ring name: %s\n", ring->name); - drm_printf(&p, "Rptr: 0x%llx Wptr: 0x%llx RB mask: %x\n", - amdgpu_ring_get_rptr(ring), - amdgpu_ring_get_wptr(ring), - ring->buf_mask); - drm_printf(&p, "Ring size in dwords: %d\n", - ring->ring_size / 4); - drm_printf(&p, "Ring contents\n"); - drm_printf(&p, "Offset \t Value\n"); + drm_printf(&p, "ring name: %s\n", ring->name); + drm_printf(&p, "Rptr: 0x%llx Wptr: 0x%llx RB mask: %x\n", + coredump->rings[i].rptr, + coredump->rings[i].wptr, + ring->buf_mask); + drm_printf(&p, "Ring size in dwords: %d\n", + ring->ring_size / 4); + drm_printf(&p, "Ring contents\n"); + drm_printf(&p, "Offset \t Value\n"); - while (j < ring->ring_size) { - drm_printf(&p, "0x%x \t 0x%x\n", j, ring->ring[j / 4]); - j += 4; + for (j = 0; j < ring->ring_size; j += 4) + drm_printf(&p, "0x%x \t 0x%x\n", j, + coredump->rings_dw[off + j / 4]); } } @@ -328,6 +342,87 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf else if (coredump->reset_vram_lost) drm_printf(&p, "VRAM is lost due to GPU reset!\n"); + if (coredump->num_ibs) { + /* Don't try to lookup the VM or map the BOs when calculating the + * size required to store the devcoredump. + */ + if (sizing_pass) + vm = NULL; + else + vm = amdgpu_vm_lock_by_pasid(adev, &root, coredump->pasid); + + for (int i = 0; i < coredump->num_ibs && (sizing_pass || vm); i++) { + ib_content = kvmalloc_array(coredump->ibs[i].ib_size_dw, 4, + GFP_KERNEL); + if (!ib_content) + continue; + + /* vm=NULL can only happen when 'sizing_pass' is true. Skip to the + * drm_printf() calls (ib_content doesn't need to be initialized + * as its content won't be written anywhere). + */ + if (!vm) + goto output_ib_content; + + va_start = coredump->ibs[i].gpu_addr & AMDGPU_GMC_HOLE_MASK; + mapping = amdgpu_vm_bo_lookup_mapping(vm, va_start / AMDGPU_GPU_PAGE_SIZE); + if (!mapping) + goto free_ib_content; + + offset = va_start - (mapping->start * AMDGPU_GPU_PAGE_SIZE); + abo = amdgpu_bo_ref(mapping->bo_va->base.bo); + r = amdgpu_bo_reserve(abo, false); + if (r) + goto free_ib_content; + + if (abo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) { + off = 0; + + if (abo->tbo.resource->mem_type != TTM_PL_VRAM) + goto unreserve_abo; + + amdgpu_res_first(abo->tbo.resource, offset, + coredump->ibs[i].ib_size_dw * 4, + &cursor); + while (cursor.remaining) { + amdgpu_device_mm_access(adev, cursor.start / 4, + &ib_content[off], cursor.size / 4, + false); + off += cursor.size; + amdgpu_res_next(&cursor, cursor.size); + } + } else { + r = ttm_bo_kmap(&abo->tbo, 0, + PFN_UP(abo->tbo.base.size), + &abo->kmap); + if (r) + goto unreserve_abo; + + kptr = amdgpu_bo_kptr(abo); + kptr += offset; + memcpy(ib_content, kptr, + coredump->ibs[i].ib_size_dw * 4); + + amdgpu_bo_kunmap(abo); + } + +output_ib_content: + drm_printf(&p, "\nIB #%d 0x%llx %d dw\n", + i, coredump->ibs[i].gpu_addr, coredump->ibs[i].ib_size_dw); + for (int j = 0; j < coredump->ibs[i].ib_size_dw; j++) + drm_printf(&p, "0x%08x\n", ib_content[j]); +unreserve_abo: + if (vm) + amdgpu_bo_unreserve(abo); +free_ib_content: + kvfree(ib_content); + } + if (vm) { + amdgpu_bo_unreserve(root); + amdgpu_bo_unref(&root); + } + } + return count - iter.remain; } @@ -359,6 +454,8 @@ static void amdgpu_devcoredump_free(void *data) struct amdgpu_coredump_info *coredump = data; kvfree(coredump->formatted); + kvfree(coredump->rings); + kvfree(coredump->rings_dw); kvfree(data); } @@ -367,6 +464,9 @@ static void amdgpu_devcoredump_deferred_work(struct work_struct *work) struct amdgpu_device *adev = container_of(work, typeof(*adev), coredump_work); struct amdgpu_coredump_info *coredump = adev->coredump; + if (!coredump) + goto end; + /* Do a one-time preparation of the coredump output because * repeatingly calling drm_coredump_printer is very slow. */ @@ -395,13 +495,20 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, { struct drm_device *dev = adev_to_drm(adev); struct amdgpu_coredump_info *coredump; + size_t size = sizeof(*coredump); struct drm_sched_job *s_job; + u64 total_ring_size, ring_count; + struct amdgpu_ring *ring; + int i, off, idx; /* No need to generate a new coredump if there's one in progress already. */ - if (work_pending(&adev->coredump_work)) + if (work_busy(&adev->coredump_work)) return; - coredump = kzalloc_obj(*coredump, GFP_NOWAIT); + if (job && job->pasid) + size += sizeof(struct amdgpu_coredump_ib_info) * job->num_ibs; + + coredump = kzalloc(size, GFP_NOWAIT); if (!coredump) return; @@ -416,6 +523,12 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, coredump->reset_task_info = *ti; amdgpu_vm_put_task_info(ti); } + coredump->pasid = job->pasid; + coredump->num_ibs = job->num_ibs; + for (i = 0; i < job->num_ibs; ++i) { + coredump->ibs[i].gpu_addr = job->ibs[i].gpu_addr; + coredump->ibs[i].ib_size_dw = job->ibs[i].length_dw; + } } if (job) { @@ -423,6 +536,47 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, coredump->ring = to_amdgpu_ring(s_job->sched); } + /* Dump ring content if memory allocation succeeds. */ + ring_count = 0; + total_ring_size = 0; + for (i = 0; i < adev->num_rings; i++) { + ring = adev->rings[i]; + + /* Only dump rings with unsignalled fences. */ + if (atomic_read(&ring->fence_drv.last_seq) == ring->fence_drv.sync_seq && + coredump->ring != ring) + continue; + + total_ring_size += ring->ring_size; + ring_count++; + } + coredump->rings_dw = kzalloc(total_ring_size, GFP_NOWAIT); + coredump->rings = kcalloc(ring_count, sizeof(struct amdgpu_coredump_ring), GFP_NOWAIT); + if (coredump->rings && coredump->rings_dw) { + for (i = 0, off = 0, idx = 0; i < adev->num_rings; i++) { + ring = adev->rings[i]; + + if (atomic_read(&ring->fence_drv.last_seq) == ring->fence_drv.sync_seq && + coredump->ring != ring) + continue; + + coredump->rings[idx].ring_index = ring->idx; + coredump->rings[idx].rptr = amdgpu_ring_get_rptr(ring); + coredump->rings[idx].wptr = amdgpu_ring_get_wptr(ring); + coredump->rings[idx].offset = off; + + memcpy(&coredump->rings_dw[off], ring->ring, ring->ring_size); + off += ring->ring_size / 4; + idx++; + } + coredump->num_rings = idx; + } else { + kvfree(coredump->rings_dw); + kvfree(coredump->rings); + coredump->rings_dw = NULL; + coredump->rings = NULL; + } + coredump->adev = adev; ktime_get_ts64(&coredump->reset_time); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h index f8f2f4df129b..2371e20fc68b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h @@ -31,6 +31,18 @@ #define AMDGPU_COREDUMP_VERSION "1" +struct amdgpu_coredump_ring { + u64 rptr; + u64 wptr; + u32 ring_index; + u32 offset; +}; + +struct amdgpu_coredump_ib_info { + uint64_t gpu_addr; + u32 ib_size_dw; +}; + struct amdgpu_coredump_info { struct amdgpu_device *adev; struct amdgpu_task_info reset_task_info; @@ -39,11 +51,20 @@ struct amdgpu_coredump_info { bool skip_vram_check; bool reset_vram_lost; struct amdgpu_ring *ring; + + struct amdgpu_coredump_ring *rings; + u32 *rings_dw; + u32 num_rings; + /* Readable form of coredevdump, generate once to speed up * reading it (see drm_coredump_printer's documentation). */ ssize_t formatted_size; char *formatted; + + unsigned int pasid; + int num_ibs; + struct amdgpu_coredump_ib_info ibs[] __counted_by(num_ibs); }; #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 9c936519bb2b..66ca043658ff 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1334,18 +1334,15 @@ static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev) #if IS_ENABLED(CONFIG_X86) struct cpuinfo_x86 *c = &cpu_data(0); - if (!(amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(12, 0, 0) || - amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(12, 0, 1))) - return false; - - if (c->x86 == 6 && - adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN5) { + if (c->x86_vendor == X86_VENDOR_INTEL) { switch (c->x86_model) { case VFM_MODEL(INTEL_ALDERLAKE): case VFM_MODEL(INTEL_ALDERLAKE_L): case VFM_MODEL(INTEL_RAPTORLAKE): case VFM_MODEL(INTEL_RAPTORLAKE_P): case VFM_MODEL(INTEL_RAPTORLAKE_S): + case VFM_MODEL(INTEL_TIGERLAKE): + case VFM_MODEL(INTEL_TIGERLAKE_L): return true; default: return false; @@ -2842,8 +2839,12 @@ static int amdgpu_device_ip_fini_early(struct amdgpu_device *adev) * that checks whether the PSP is running. A solution for those issues * in the APU is to trigger a GPU reset, but this should be done during * the unload phase to avoid adding boot latency and screen flicker. + * GFX V11 has GC block as default off IP. Every time AMDGPU driver sends + * a request to PMFW to unload MP1, PMFW will put GC in reset and power down + * the voltage. Hence, skipping reset for APUs with GFX V11 or later. */ - if ((adev->flags & AMD_IS_APU) && !adev->gmc.is_app_apu) { + if ((adev->flags & AMD_IS_APU) && !adev->gmc.is_app_apu && + amdgpu_ip_version(adev, GC_HWIP, 0) < IP_VERSION(11, 0, 0)) { r = amdgpu_asic_reset(adev); if (r) dev_err(adev->dev, "asic reset on %s failed\n", __func__); @@ -3757,15 +3758,13 @@ int amdgpu_device_init(struct amdgpu_device *adev, spin_lock_init(&adev->virt.rlcg_reg_lock); spin_lock_init(&adev->wb.lock); - xa_init_flags(&adev->userq_xa, XA_FLAGS_LOCK_IRQ); - INIT_LIST_HEAD(&adev->reset_list); INIT_LIST_HEAD(&adev->ras_list); INIT_LIST_HEAD(&adev->pm.od_kobj_list); - xa_init(&adev->userq_doorbell_xa); + xa_init_flags(&adev->userq_doorbell_xa, XA_FLAGS_LOCK_IRQ); INIT_DELAYED_WORK(&adev->delayed_init_work, amdgpu_device_delayed_init_work_handler); @@ -4065,7 +4064,7 @@ fence_driver_init: } /* must succeed. */ amdgpu_ras_resume(adev); - queue_delayed_work(system_wq, &adev->delayed_init_work, + queue_delayed_work(system_dfl_wq, &adev->delayed_init_work, msecs_to_jiffies(AMDGPU_RESUME_MS)); } @@ -4630,7 +4629,7 @@ int amdgpu_device_resume(struct drm_device *dev, bool notify_clients) if (r) goto exit; - queue_delayed_work(system_wq, &adev->delayed_init_work, + queue_delayed_work(system_dfl_wq, &adev->delayed_init_work, msecs_to_jiffies(AMDGPU_RESUME_MS)); exit: if (amdgpu_sriov_vf(adev)) { @@ -5339,7 +5338,7 @@ int amdgpu_do_asic_reset(struct list_head *device_list_handle, list_for_each_entry(tmp_adev, device_list_handle, reset_list) { /* For XGMI run all resets in parallel to speed up the process */ if (tmp_adev->gmc.xgmi.num_physical_nodes > 1) { - if (!queue_work(system_unbound_wq, + if (!queue_work(system_dfl_wq, &tmp_adev->xgmi_reset_work)) r = -EALREADY; } else @@ -5520,8 +5519,6 @@ static void amdgpu_device_recovery_prepare(struct amdgpu_device *adev, list_add_tail(&tmp_adev->reset_list, device_list); if (adev->shutdown) tmp_adev->shutdown = true; - if (amdgpu_reset_in_dpc(adev)) - tmp_adev->pcie_reset_ctx.in_link_reset = true; } if (!list_is_first(&adev->reset_list, device_list)) list_rotate_to_front(&adev->reset_list, device_list); @@ -6293,6 +6290,9 @@ pci_ers_result_t amdgpu_pci_error_detected(struct pci_dev *pdev, pci_channel_sta amdgpu_reset_set_dpc_status(adev, true); mutex_lock(&hive->hive_lock); + } else { + if (amdgpu_device_bus_status_check(adev)) + amdgpu_reset_set_dpc_status(adev, true); } memset(&reset_context, 0, sizeof(reset_context)); INIT_LIST_HEAD(&device_list); @@ -6413,6 +6413,7 @@ pci_ers_result_t amdgpu_pci_slot_reset(struct pci_dev *pdev) list_for_each_entry(tmp_adev, &hive->device_list, gmc.xgmi.head) tmp_adev->pcie_reset_ctx.in_link_reset = true; } else { + adev->pcie_reset_ctx.in_link_reset = true; set_bit(AMDGPU_SKIP_HW_RESET, &reset_context.flags); } @@ -6469,9 +6470,10 @@ void amdgpu_pci_resume(struct pci_dev *pdev) tmp_adev->pcie_reset_ctx.in_link_reset = false; list_add_tail(&tmp_adev->reset_list, &device_list); } - } else + } else { + adev->pcie_reset_ctx.in_link_reset = false; list_add_tail(&adev->reset_list, &device_list); - + } amdgpu_device_sched_resume(&device_list, NULL, NULL); amdgpu_device_gpu_resume(adev, &device_list, false); amdgpu_device_recovery_put_reset_lock(adev, &device_list); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 8ec5465c3349..8d99bfaa498f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -535,10 +535,11 @@ static int amdgpu_discovery_get_table_info(struct amdgpu_device *adev, *info = &bhdrv2->table_list[table_id]; break; case 1: + case 0: *info = &bhdr->table_list[table_id]; break; default: - dev_err(adev->dev, "Invalid ip discovery table version\n"); + dev_err(adev->dev, "Invalid ip discovery table version %d\n",bhdr->version_major); return -EINVAL; } @@ -3089,10 +3090,8 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 1): case IP_VERSION(11, 5, 2): case IP_VERSION(11, 5, 3): - adev->family = AMDGPU_FAMILY_GC_11_5_0; - break; case IP_VERSION(11, 5, 4): - adev->family = AMDGPU_FAMILY_GC_11_5_4; + adev->family = AMDGPU_FAMILY_GC_11_5_0; break; case IP_VERSION(12, 0, 0): case IP_VERSION(12, 0, 1): diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 8ed637f92322..60debd543e44 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -641,9 +641,7 @@ module_param_named(si_support, amdgpu_si_support, int, 0444); * CIK (Sea Islands) are second generation GCN GPUs, supported by both * drivers: radeon (old) and amdgpu (new). This parameter controls whether * amdgpu should support CIK. - * By default: - * - CIK dedicated GPUs are supported by amdgpu. - * - CIK APUs are supported by radeon (except when radeon is not built). + * By default, CIK dedicated GPUs and APUs are supported by amdgpu. * Only relevant when CONFIG_DRM_AMDGPU_CIK is enabled to build CIK support in amdgpu. * See also radeon.cik_support which should be disabled when amdgpu.cik_support is * enabled, and vice versa. @@ -2323,8 +2321,6 @@ static bool amdgpu_support_enabled(struct device *dev, case CHIP_BONAIRE: case CHIP_HAWAII: - support_by_default = true; - fallthrough; case CHIP_KAVERI: case CHIP_KABINI: case CHIP_MULLINS: @@ -2332,6 +2328,7 @@ static bool amdgpu_support_enabled(struct device *dev, param = "cik_support"; module_param = amdgpu_cik_support; amdgpu_support_built = IS_ENABLED(CONFIG_DRM_AMDGPU_CIK); + support_by_default = true; break; default: @@ -3152,17 +3149,15 @@ static int __init amdgpu_init(void) r = amdgpu_sync_init(); if (r) - goto error_sync; - - r = amdgpu_userq_fence_slab_init(); - if (r) - goto error_fence; + return r; amdgpu_register_atpx_handler(); amdgpu_acpi_detect(); - /* Ignore KFD init failures. Normal when CONFIG_HSA_AMD is not set. */ - amdgpu_amdkfd_init(); + /* Ignore KFD init failures when CONFIG_HSA_AMD is not set. */ + r = amdgpu_amdkfd_init(); + if (r && r != -ENOENT) + goto error_fini_sync; if (amdgpu_pp_feature_mask & PP_OVERDRIVE_MASK) { add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK); @@ -3173,10 +3168,8 @@ static int __init amdgpu_init(void) /* let modprobe override vga console setting */ return pci_register_driver(&amdgpu_kms_pci_driver); -error_fence: +error_fini_sync: amdgpu_sync_fini(); - -error_sync: return r; } @@ -3187,7 +3180,6 @@ static void __exit amdgpu_exit(void) amdgpu_unregister_atpx_handler(); amdgpu_acpi_release(); amdgpu_sync_fini(); - amdgpu_userq_fence_slab_fini(); mmu_notifier_synchronize(); amdgpu_xcp_drv_release(); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c index bc772ca3dab7..b6f849d51c2e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c @@ -262,12 +262,19 @@ void amdgpu_gart_table_ram_free(struct amdgpu_device *adev) */ int amdgpu_gart_table_vram_alloc(struct amdgpu_device *adev) { + int r; + if (adev->gart.bo != NULL) return 0; - return amdgpu_bo_create_kernel(adev, adev->gart.table_size, PAGE_SIZE, - AMDGPU_GEM_DOMAIN_VRAM, &adev->gart.bo, - NULL, (void *)&adev->gart.ptr); + r = amdgpu_bo_create_kernel(adev, adev->gart.table_size, PAGE_SIZE, + AMDGPU_GEM_DOMAIN_VRAM, &adev->gart.bo, + NULL, (void *)&adev->gart.ptr); + if (r) + return r; + + memset_io(adev->gart.ptr, adev->gart.gart_pte_flags, adev->gart.table_size); + return 0; } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index cab3196a87fb..b8ca876694ff 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -1580,6 +1580,36 @@ static ssize_t amdgpu_gfx_set_compute_partition(struct device *dev, return count; } +static ssize_t compute_partition_mem_alloc_mode_show(struct device *dev, + struct device_attribute *addr, + char *buf) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + int mode = adev->xcp_mgr->mem_alloc_mode; + + return sysfs_emit(buf, "%s\n", + amdgpu_gfx_compute_mem_alloc_mode_desc(mode)); +} + + +static ssize_t compute_partition_mem_alloc_mode_store(struct device *dev, + struct device_attribute *addr, + const char *buf, size_t count) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + + if (!strncasecmp("CAPPING", buf, strlen("CAPPING"))) + adev->xcp_mgr->mem_alloc_mode = AMDGPU_PARTITION_MEM_CAPPING_EVEN; + else if (!strncasecmp("ALL", buf, strlen("ALL"))) + adev->xcp_mgr->mem_alloc_mode = AMDGPU_PARTITION_MEM_ALLOC_ALL; + else + return -EINVAL; + + return count; +} + static const char *xcp_desc[] = { [AMDGPU_SPX_PARTITION_MODE] = "SPX", [AMDGPU_DPX_PARTITION_MODE] = "DPX", @@ -1935,6 +1965,10 @@ static DEVICE_ATTR(gfx_reset_mask, 0444, static DEVICE_ATTR(compute_reset_mask, 0444, amdgpu_gfx_get_compute_reset_mask, NULL); +static DEVICE_ATTR(compute_partition_mem_alloc_mode, 0644, + compute_partition_mem_alloc_mode_show, + compute_partition_mem_alloc_mode_store); + static int amdgpu_gfx_sysfs_xcp_init(struct amdgpu_device *adev) { struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr; @@ -1955,6 +1989,11 @@ static int amdgpu_gfx_sysfs_xcp_init(struct amdgpu_device *adev) if (r) return r; + r = device_create_file(adev->dev, + &dev_attr_compute_partition_mem_alloc_mode); + if (r) + return r; + if (xcp_switch_supported) r = device_create_file(adev->dev, &dev_attr_available_compute_partition); @@ -1974,6 +2013,8 @@ static void amdgpu_gfx_sysfs_xcp_fini(struct amdgpu_device *adev) (xcp_mgr->funcs && xcp_mgr->funcs->switch_partition_mode); device_remove_file(adev->dev, &dev_attr_current_compute_partition); + device_remove_file(adev->dev, &dev_attr_compute_partition_mem_alloc_mode); + if (xcp_switch_supported) device_remove_file(adev->dev, &dev_attr_available_compute_partition); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h index 2785eda6fea5..a0cf0a3b41da 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h @@ -71,6 +71,11 @@ enum amdgpu_gfx_partition { AMDGPU_AUTO_COMPUTE_PARTITION_MODE = -2, }; +enum amdgpu_gfx_partition_mem_alloc_mode { + AMDGPU_PARTITION_MEM_CAPPING_EVEN = 0, + AMDGPU_PARTITION_MEM_ALLOC_ALL = 1, +}; + #define NUM_XCC(x) hweight16(x) enum amdgpu_gfx_ras_mem_id_type { @@ -677,4 +682,16 @@ static inline const char *amdgpu_gfx_compute_mode_desc(int mode) } } +static inline const char *amdgpu_gfx_compute_mem_alloc_mode_desc(int mode) +{ + switch (mode) { + case AMDGPU_PARTITION_MEM_CAPPING_EVEN: + return "CAPPING"; + case AMDGPU_PARTITION_MEM_ALLOC_ALL: + return "ALL"; + default: + return "UNKNOWN"; + } +} + #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index ec74f3971732..3d9497d121ca 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -314,7 +314,10 @@ void amdgpu_gmc_gart_location(struct amdgpu_device *adev, struct amdgpu_gmc *mc, mc->gart_start = max_mc_address - mc->gart_size + 1; break; case AMDGPU_GART_PLACEMENT_LOW: - mc->gart_start = 0; + if (size_bf >= mc->gart_size) + mc->gart_start = 0; + else + mc->gart_start = ALIGN(mc->fb_end, four_gb); break; case AMDGPU_GART_PLACEMENT_BEST_FIT: default: @@ -1033,17 +1036,17 @@ void amdgpu_gmc_set_vm_fault_masks(struct amdgpu_device *adev, int hub_type, } } -void amdgpu_gmc_get_vbios_allocations(struct amdgpu_device *adev) +void amdgpu_gmc_init_vga_resv_regions(struct amdgpu_device *adev) { unsigned size; + if (adev->gmc.is_app_apu) + return; + /* * Some ASICs need to reserve a region of video memory to avoid access * from driver */ - adev->mman.stolen_reserved_offset = 0; - adev->mman.stolen_reserved_size = 0; - /* * TODO: * Currently there is a bug where some memory client outside @@ -1060,8 +1063,8 @@ void amdgpu_gmc_get_vbios_allocations(struct amdgpu_device *adev) */ #ifdef CONFIG_X86 if (amdgpu_sriov_vf(adev) && hypervisor_is_type(X86_HYPER_MS_HYPERV)) { - adev->mman.stolen_reserved_offset = 0x500000; - adev->mman.stolen_reserved_size = 0x200000; + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_STOLEN_RESERVED, + 0x500000, 0x200000, false); } #endif break; @@ -1099,11 +1102,14 @@ void amdgpu_gmc_get_vbios_allocations(struct amdgpu_device *adev) size = 0; if (size > AMDGPU_VBIOS_VGA_ALLOCATION) { - adev->mman.stolen_vga_size = AMDGPU_VBIOS_VGA_ALLOCATION; - adev->mman.stolen_extended_size = size - adev->mman.stolen_vga_size; + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_STOLEN_VGA, + 0, AMDGPU_VBIOS_VGA_ALLOCATION, false); + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_STOLEN_EXTENDED, + AMDGPU_VBIOS_VGA_ALLOCATION, + size - AMDGPU_VBIOS_VGA_ALLOCATION, false); } else { - adev->mman.stolen_vga_size = size; - adev->mman.stolen_extended_size = 0; + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_STOLEN_VGA, + 0, size, false); } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h index 32e73e8ba778..6ab4c1e297fc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h @@ -456,7 +456,7 @@ extern void amdgpu_gmc_set_vm_fault_masks(struct amdgpu_device *adev, int hub_type, bool enable); -void amdgpu_gmc_get_vbios_allocations(struct amdgpu_device *adev); +void amdgpu_gmc_init_vga_resv_regions(struct amdgpu_device *adev); void amdgpu_gmc_init_pdb0(struct amdgpu_device *adev); uint64_t amdgpu_gmc_vram_mc2pa(struct amdgpu_device *adev, uint64_t mc_addr); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c index 569c5a89ff10..124fb38eb465 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c @@ -22,7 +22,7 @@ */ #include "amdgpu_ids.h" -#include +#include #include @@ -40,8 +40,8 @@ * VMs are looked up from the PASID per amdgpu_device. */ -static DEFINE_IDR(amdgpu_pasid_idr); -static DEFINE_SPINLOCK(amdgpu_pasid_idr_lock); +static DEFINE_XARRAY_FLAGS(amdgpu_pasid_xa, XA_FLAGS_LOCK_IRQ | XA_FLAGS_ALLOC1); +static u32 amdgpu_pasid_xa_next; /* Helper to free pasid from a fence callback */ struct amdgpu_pasid_cb { @@ -62,36 +62,37 @@ struct amdgpu_pasid_cb { */ int amdgpu_pasid_alloc(unsigned int bits) { - int pasid; + u32 pasid; + int r; if (bits == 0) return -EINVAL; - spin_lock(&amdgpu_pasid_idr_lock); - /* TODO: Need to replace the idr with an xarry, and then - * handle the internal locking with ATOMIC safe paths. - */ - pasid = idr_alloc_cyclic(&amdgpu_pasid_idr, NULL, 1, - 1U << bits, GFP_ATOMIC); - spin_unlock(&amdgpu_pasid_idr_lock); - - if (pasid >= 0) - trace_amdgpu_pasid_allocated(pasid); + r = xa_alloc_cyclic_irq(&amdgpu_pasid_xa, &pasid, xa_mk_value(0), + XA_LIMIT(1, (1U << bits) - 1), + &amdgpu_pasid_xa_next, GFP_KERNEL); + if (r < 0) + return r; + trace_amdgpu_pasid_allocated(pasid); return pasid; } /** * amdgpu_pasid_free - Free a PASID * @pasid: PASID to free + * + * Called in IRQ context. */ void amdgpu_pasid_free(u32 pasid) { + unsigned long flags; + trace_amdgpu_pasid_freed(pasid); - spin_lock(&amdgpu_pasid_idr_lock); - idr_remove(&amdgpu_pasid_idr, pasid); - spin_unlock(&amdgpu_pasid_idr_lock); + xa_lock_irqsave(&amdgpu_pasid_xa, flags); + __xa_erase(&amdgpu_pasid_xa, pasid); + xa_unlock_irqrestore(&amdgpu_pasid_xa, flags); } static void amdgpu_pasid_free_cb(struct dma_fence *fence, @@ -634,7 +635,5 @@ void amdgpu_vmid_mgr_fini(struct amdgpu_device *adev) */ void amdgpu_pasid_mgr_cleanup(void) { - spin_lock(&amdgpu_pasid_idr_lock); - idr_destroy(&amdgpu_pasid_idr); - spin_unlock(&amdgpu_pasid_idr_lock); + xa_destroy(&amdgpu_pasid_xa); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 06efce38f323..71272f40feef 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -873,68 +873,59 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) ? -EFAULT : 0; } case AMDGPU_INFO_READ_MMR_REG: { - int ret = 0; - unsigned int n, alloc_size; - uint32_t *regs; unsigned int se_num = (info->read_mmr_reg.instance >> AMDGPU_INFO_MMR_SE_INDEX_SHIFT) & AMDGPU_INFO_MMR_SE_INDEX_MASK; unsigned int sh_num = (info->read_mmr_reg.instance >> AMDGPU_INFO_MMR_SH_INDEX_SHIFT) & AMDGPU_INFO_MMR_SH_INDEX_MASK; - - if (!down_read_trylock(&adev->reset_domain->sem)) - return -ENOENT; + unsigned int alloc_size; + uint32_t *regs; + int ret; /* set full masks if the userspace set all bits * in the bitfields */ - if (se_num == AMDGPU_INFO_MMR_SE_INDEX_MASK) { + if (se_num == AMDGPU_INFO_MMR_SE_INDEX_MASK) se_num = 0xffffffff; - } else if (se_num >= AMDGPU_GFX_MAX_SE) { - ret = -EINVAL; - goto out; - } + else if (se_num >= AMDGPU_GFX_MAX_SE) + return -EINVAL; - if (sh_num == AMDGPU_INFO_MMR_SH_INDEX_MASK) { + if (sh_num == AMDGPU_INFO_MMR_SH_INDEX_MASK) sh_num = 0xffffffff; - } else if (sh_num >= AMDGPU_GFX_MAX_SH_PER_SE) { - ret = -EINVAL; - goto out; - } + else if (sh_num >= AMDGPU_GFX_MAX_SH_PER_SE) + return -EINVAL; - if (info->read_mmr_reg.count > 128) { - ret = -EINVAL; - goto out; - } + if (info->read_mmr_reg.count > 128) + return -EINVAL; - regs = kmalloc_array(info->read_mmr_reg.count, sizeof(*regs), GFP_KERNEL); - if (!regs) { - ret = -ENOMEM; - goto out; - } + regs = kmalloc_array(info->read_mmr_reg.count, sizeof(*regs), + GFP_KERNEL); + if (!regs) + return -ENOMEM; + down_read(&adev->reset_domain->sem); alloc_size = info->read_mmr_reg.count * sizeof(*regs); - amdgpu_gfx_off_ctrl(adev, false); + ret = 0; for (i = 0; i < info->read_mmr_reg.count; i++) { if (amdgpu_asic_read_register(adev, se_num, sh_num, info->read_mmr_reg.dword_offset + i, ®s[i])) { DRM_DEBUG_KMS("unallowed offset %#x\n", info->read_mmr_reg.dword_offset + i); - kfree(regs); - amdgpu_gfx_off_ctrl(adev, true); ret = -EFAULT; - goto out; + break; } } amdgpu_gfx_off_ctrl(adev, true); - n = copy_to_user(out, regs, min(size, alloc_size)); - kfree(regs); - ret = (n ? -EFAULT : 0); -out: up_read(&adev->reset_domain->sem); + + if (!ret) { + ret = copy_to_user(out, regs, min(size, alloc_size)) + ? -EFAULT : 0; + } + kfree(regs); return ret; } case AMDGPU_INFO_DEV_INFO: { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index d39b695cd925..f0e4d020f4c7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -1076,24 +1076,25 @@ int psp_update_fw_reservation(struct psp_context *psp) return 0; } - amdgpu_bo_free_kernel(&adev->mman.fw_reserved_memory, NULL, NULL); + amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_FW); reserv_size = roundup(reserv_size, SZ_1M); - ret = amdgpu_bo_create_kernel_at(adev, reserv_addr, reserv_size, &adev->mman.fw_reserved_memory, NULL); + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_FW, + reserv_addr, reserv_size, false); + ret = amdgpu_ttm_mark_vram_reserved(adev, AMDGPU_RESV_FW); if (ret) { dev_err(adev->dev, "reserve fw region failed(%d)!\n", ret); - amdgpu_bo_free_kernel(&adev->mman.fw_reserved_memory, NULL, NULL); return ret; } reserv_size_ext = roundup(reserv_size_ext, SZ_1M); - ret = amdgpu_bo_create_kernel_at(adev, reserv_addr_ext, reserv_size_ext, - &adev->mman.fw_reserved_memory_extend, NULL); + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_FW_EXTEND, + reserv_addr_ext, reserv_size_ext, false); + ret = amdgpu_ttm_mark_vram_reserved(adev, AMDGPU_RESV_FW_EXTEND); if (ret) { dev_err(adev->dev, "reserve extend fw region failed(%d)!\n", ret); - amdgpu_bo_free_kernel(&adev->mman.fw_reserved_memory_extend, NULL, NULL); return ret; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h index 79a49cba8d40..7e94ec11c57e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h @@ -277,7 +277,6 @@ struct psp_memory_training_context { /*vram offset of the c2p training data*/ u64 c2p_train_data_offset; - struct amdgpu_bo *c2p_bo; enum psp_memory_training_init_flag init; u32 training_cnt; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index 6edcb7713299..6c644cfe6695 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -5726,7 +5726,7 @@ out: static void amdgpu_ras_critical_region_init(struct amdgpu_device *adev) { - amdgpu_ras_add_critical_region(adev, adev->mman.fw_reserved_memory); + amdgpu_ras_add_critical_region(adev, adev->mman.resv_region[AMDGPU_RESV_FW].bo); } static void amdgpu_ras_critical_region_fini(struct amdgpu_device *adev) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c index cdf4909592d2..0c57fe259894 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c @@ -1950,7 +1950,7 @@ void amdgpu_ras_check_bad_page_status(struct amdgpu_device *adev) if (!control || amdgpu_bad_page_threshold == 0) return; - if (control->ras_num_bad_pages >= ras->bad_page_cnt_threshold) { + if (control->ras_num_bad_pages > ras->bad_page_cnt_threshold) { if (amdgpu_dpm_send_rma_reason(adev)) dev_warn(adev->dev, "Unable to send out-of-band RMA CPER"); else diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c index 7a2fcb7ded1d..1b982b803e6f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c @@ -116,7 +116,7 @@ static int amdgpu_reset_xgmi_reset_on_init_perform_reset( /* Mode1 reset needs to be triggered on all devices together */ list_for_each_entry(tmp_adev, reset_device_list, reset_list) { /* For XGMI run all resets in parallel to speed up the process */ - if (!queue_work(system_unbound_wq, &tmp_adev->xgmi_reset_work)) + if (!queue_work(system_dfl_wq, &tmp_adev->xgmi_reset_work)) r = -EALREADY; if (r) { dev_err(tmp_adev->dev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c index 66e8a2f7afcf..d6bee5c30073 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c @@ -552,8 +552,9 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf, size_t size, loff_t *pos) { struct amdgpu_ring *ring = file_inode(f)->i_private; - uint32_t value, result, early[3]; + u32 value, result, early[3] = { 0 }; uint64_t p; + u32 avail_dw, start_dw, read_dw; loff_t i; int r; @@ -565,10 +566,10 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf, result = 0; - if (*pos < 12) { - if (ring->funcs->type == AMDGPU_RING_TYPE_CPER) - mutex_lock(&ring->adev->cper.ring_lock); + if (ring->funcs->type == AMDGPU_RING_TYPE_CPER) + mutex_lock(&ring->adev->cper.ring_lock); + if (*pos < 12) { early[0] = amdgpu_ring_get_rptr(ring) & ring->buf_mask; early[1] = amdgpu_ring_get_wptr(ring) & ring->buf_mask; early[2] = ring->wptr & ring->buf_mask; @@ -600,13 +601,24 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf, *pos += 4; } } else { + early[0] = amdgpu_ring_get_rptr(ring) & ring->buf_mask; + early[1] = amdgpu_ring_get_wptr(ring) & ring->buf_mask; + p = early[0]; if (early[0] <= early[1]) - size = (early[1] - early[0]); + avail_dw = early[1] - early[0]; else - size = ring->ring_size - (early[0] - early[1]); + avail_dw = ring->buf_mask + 1 - (early[0] - early[1]); - while (size) { + start_dw = (*pos > 12) ? ((*pos - 12) >> 2) : 0; + if (start_dw >= avail_dw) + goto out; + + p = (p + start_dw) & ring->ptr_mask; + avail_dw -= start_dw; + read_dw = min_t(u32, avail_dw, size >> 2); + + while (read_dw) { if (p == early[1]) goto out; @@ -619,9 +631,10 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf, buf += 4; result += 4; - size--; + read_dw--; p++; p &= ring->ptr_mask; + *pos += 4; } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h index ce5af137ee40..715c9e43e13a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h @@ -559,15 +559,18 @@ void amdgpu_debugfs_ring_init(struct amdgpu_device *adev, int amdgpu_ring_init_mqd(struct amdgpu_ring *ring); -static inline u32 amdgpu_ib_get_value(struct amdgpu_ib *ib, int idx) +static inline u32 amdgpu_ib_get_value(struct amdgpu_ib *ib, uint32_t idx) { - return ib->ptr[idx]; + if (idx < ib->length_dw) + return ib->ptr[idx]; + return 0; } -static inline void amdgpu_ib_set_value(struct amdgpu_ib *ib, int idx, +static inline void amdgpu_ib_set_value(struct amdgpu_ib *ib, uint32_t idx, uint32_t value) { - ib->ptr[idx] = value; + if (idx < ib->length_dw) + ib->ptr[idx] = value; } int amdgpu_ib_get(struct amdgpu_device *adev, struct amdgpu_vm *vm, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index afaaab6496de..3d2e00efc741 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -75,6 +75,9 @@ static int amdgpu_ttm_init_on_chip(struct amdgpu_device *adev, unsigned int type, uint64_t size_in_page) { + if (!size_in_page) + return 0; + return ttm_range_man_init(&adev->mman.bdev, type, false, size_in_page); } @@ -1671,87 +1674,160 @@ static struct ttm_device_funcs amdgpu_bo_driver = { .access_memory = &amdgpu_ttm_access_memory, }; -/* - * Firmware Reservation functions - */ -/** - * amdgpu_ttm_fw_reserve_vram_fini - free fw reserved vram - * - * @adev: amdgpu_device pointer - * - * free fw reserved vram if it has been reserved. - */ -static void amdgpu_ttm_fw_reserve_vram_fini(struct amdgpu_device *adev) +void amdgpu_ttm_init_vram_resv(struct amdgpu_device *adev, + enum amdgpu_resv_region_id id, + uint64_t offset, uint64_t size, + bool needs_cpu_map) { - amdgpu_bo_free_kernel(&adev->mman.fw_vram_usage_reserved_bo, - NULL, &adev->mman.fw_vram_usage_va); + struct amdgpu_vram_resv *resv; + + if (id >= AMDGPU_RESV_MAX) + return; + + resv = &adev->mman.resv_region[id]; + resv->offset = offset; + resv->size = size; + resv->needs_cpu_map = needs_cpu_map; } -/* - * Driver Reservation functions - */ -/** - * amdgpu_ttm_drv_reserve_vram_fini - free drv reserved vram - * - * @adev: amdgpu_device pointer - * - * free drv reserved vram if it has been reserved. - */ -static void amdgpu_ttm_drv_reserve_vram_fini(struct amdgpu_device *adev) +static void amdgpu_ttm_init_fw_resv_region(struct amdgpu_device *adev) { - amdgpu_bo_free_kernel(&adev->mman.drv_vram_usage_reserved_bo, - NULL, - &adev->mman.drv_vram_usage_va); + uint32_t reserve_size = 0; + + if (!adev->discovery.reserve_tmr) + return; + + /* + * Query reserved tmr size through atom firmwareinfo for Sienna_Cichlid and onwards for all + * the use cases (IP discovery/G6 memory training/profiling/diagnostic data.etc) + * + * Otherwise, fallback to legacy approach to check and reserve tmr block for ip + * discovery data and G6 memory training data respectively + */ + if (adev->bios) + reserve_size = + amdgpu_atomfirmware_get_fw_reserved_fb_size(adev); + + if (!adev->bios && + (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 3) || + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 4) || + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 5, 0))) + reserve_size = max(reserve_size, (uint32_t)280 << 20); + else if (!adev->bios && + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(12, 1, 0)) { + if (hweight32(adev->aid_mask) == 1) + reserve_size = max(reserve_size, (uint32_t)128 << 20); + else + reserve_size = max(reserve_size, (uint32_t)144 << 20); + } else if (!reserve_size) + reserve_size = DISCOVERY_TMR_OFFSET; + + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_FW, + adev->gmc.real_vram_size - reserve_size, + reserve_size, false); } -/** - * amdgpu_ttm_fw_reserve_vram_init - create bo vram reservation from fw - * - * @adev: amdgpu_device pointer - * - * create bo vram reservation from fw. - */ -static int amdgpu_ttm_fw_reserve_vram_init(struct amdgpu_device *adev) +static void amdgpu_ttm_init_mem_train_resv_region(struct amdgpu_device *adev) +{ + uint64_t reserve_size; + uint64_t offset; + + if (!adev->discovery.reserve_tmr) + return; + + if (!adev->bios || amdgpu_sriov_vf(adev)) + return; + + if (!amdgpu_atomfirmware_mem_training_supported(adev)) + return; + + reserve_size = adev->mman.resv_region[AMDGPU_RESV_FW].size; + offset = ALIGN((adev->gmc.mc_vram_size - reserve_size - SZ_1M), SZ_1M); + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_MEM_TRAIN, + offset, + GDDR6_MEM_TRAINING_DATA_SIZE_IN_BYTES, + false); +} + +static void amdgpu_ttm_init_vram_resv_regions(struct amdgpu_device *adev) { uint64_t vram_size = adev->gmc.visible_vram_size; - adev->mman.fw_vram_usage_va = NULL; - adev->mman.fw_vram_usage_reserved_bo = NULL; + /* Initialize memory reservations as required for VGA. + * This is used for VGA emulation and pre-OS scanout buffers to + * avoid display artifacts while transitioning between pre-OS + * and driver. + */ + amdgpu_gmc_init_vga_resv_regions(adev); + amdgpu_ttm_init_fw_resv_region(adev); + amdgpu_ttm_init_mem_train_resv_region(adev); - if (adev->mman.fw_vram_usage_size == 0 || - adev->mman.fw_vram_usage_size > vram_size) - return 0; + if (adev->mman.resv_region[AMDGPU_RESV_FW_VRAM_USAGE].size > vram_size) + adev->mman.resv_region[AMDGPU_RESV_FW_VRAM_USAGE].size = 0; - return amdgpu_bo_create_kernel_at(adev, - adev->mman.fw_vram_usage_start_offset, - adev->mman.fw_vram_usage_size, - &adev->mman.fw_vram_usage_reserved_bo, - &adev->mman.fw_vram_usage_va); + if (adev->mman.resv_region[AMDGPU_RESV_DRV_VRAM_USAGE].size > vram_size) + adev->mman.resv_region[AMDGPU_RESV_DRV_VRAM_USAGE].size = 0; } -/** - * amdgpu_ttm_drv_reserve_vram_init - create bo vram reservation from driver - * - * @adev: amdgpu_device pointer - * - * create bo vram reservation from drv. - */ -static int amdgpu_ttm_drv_reserve_vram_init(struct amdgpu_device *adev) +int amdgpu_ttm_mark_vram_reserved(struct amdgpu_device *adev, + enum amdgpu_resv_region_id id) { - u64 vram_size = adev->gmc.visible_vram_size; + struct amdgpu_vram_resv *resv; + int ret; - adev->mman.drv_vram_usage_va = NULL; - adev->mman.drv_vram_usage_reserved_bo = NULL; + if (id >= AMDGPU_RESV_MAX) + return -EINVAL; - if (adev->mman.drv_vram_usage_size == 0 || - adev->mman.drv_vram_usage_size > vram_size) + resv = &adev->mman.resv_region[id]; + if (!resv->size) return 0; - return amdgpu_bo_create_kernel_at(adev, - adev->mman.drv_vram_usage_start_offset, - adev->mman.drv_vram_usage_size, - &adev->mman.drv_vram_usage_reserved_bo, - &adev->mman.drv_vram_usage_va); + ret = amdgpu_bo_create_kernel_at(adev, resv->offset, resv->size, + &resv->bo, + resv->needs_cpu_map ? &resv->cpu_ptr : NULL); + if (ret) { + dev_err(adev->dev, + "reserve vram failed: id=%d offset=0x%llx size=0x%llx ret=%d\n", + id, resv->offset, resv->size, ret); + memset(resv, 0, sizeof(*resv)); + } + + return ret; +} + +void amdgpu_ttm_unmark_vram_reserved(struct amdgpu_device *adev, + enum amdgpu_resv_region_id id) +{ + struct amdgpu_vram_resv *resv; + + if (id >= AMDGPU_RESV_MAX) + return; + + resv = &adev->mman.resv_region[id]; + if (!resv->bo) + return; + + amdgpu_bo_free_kernel(&resv->bo, NULL, + resv->needs_cpu_map ? &resv->cpu_ptr : NULL); + memset(resv, 0, sizeof(*resv)); +} + +/* + * Reserve all regions with non-zero size. Regions whose info is not + * yet available (e.g., fw extended region) may still be reserved + * during runtime. + */ +static int amdgpu_ttm_alloc_vram_resv_regions(struct amdgpu_device *adev) +{ + int i, r; + + for (i = 0; i < AMDGPU_RESV_MAX; i++) { + r = amdgpu_ttm_mark_vram_reserved(adev, i); + if (r) + return r; + } + + return 0; } /* @@ -1770,25 +1846,23 @@ static int amdgpu_ttm_training_reserve_vram_fini(struct amdgpu_device *adev) struct psp_memory_training_context *ctx = &adev->psp.mem_train_ctx; ctx->init = PSP_MEM_TRAIN_NOT_SUPPORT; - amdgpu_bo_free_kernel(&ctx->c2p_bo, NULL, NULL); - ctx->c2p_bo = NULL; + amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_MEM_TRAIN); return 0; } -static void amdgpu_ttm_training_data_block_init(struct amdgpu_device *adev, - uint32_t reserve_size) +static void amdgpu_ttm_training_data_block_init(struct amdgpu_device *adev) { struct psp_memory_training_context *ctx = &adev->psp.mem_train_ctx; + struct amdgpu_vram_resv *resv = + &adev->mman.resv_region[AMDGPU_RESV_MEM_TRAIN]; memset(ctx, 0, sizeof(*ctx)); - ctx->c2p_train_data_offset = - ALIGN((adev->gmc.mc_vram_size - reserve_size - SZ_1M), SZ_1M); + ctx->c2p_train_data_offset = resv->offset; ctx->p2c_train_data_offset = (adev->gmc.mc_vram_size - GDDR6_MEM_TRAINING_OFFSET); - ctx->train_data_size = - GDDR6_MEM_TRAINING_DATA_SIZE_IN_BYTES; + ctx->train_data_size = resv->size; DRM_DEBUG("train_data_size:%llx,p2c_train_data_offset:%llx,c2p_train_data_offset:%llx.\n", ctx->train_data_size, @@ -1796,78 +1870,6 @@ static void amdgpu_ttm_training_data_block_init(struct amdgpu_device *adev, ctx->c2p_train_data_offset); } -/* - * reserve TMR memory at the top of VRAM which holds - * IP Discovery data and is protected by PSP. - */ -static int amdgpu_ttm_reserve_tmr(struct amdgpu_device *adev) -{ - struct psp_memory_training_context *ctx = &adev->psp.mem_train_ctx; - bool mem_train_support = false; - uint32_t reserve_size = 0; - int ret; - - if (adev->bios && !amdgpu_sriov_vf(adev)) { - if (amdgpu_atomfirmware_mem_training_supported(adev)) - mem_train_support = true; - else - DRM_DEBUG("memory training does not support!\n"); - } - - /* - * Query reserved tmr size through atom firmwareinfo for Sienna_Cichlid and onwards for all - * the use cases (IP discovery/G6 memory training/profiling/diagnostic data.etc) - * - * Otherwise, fallback to legacy approach to check and reserve tmr block for ip - * discovery data and G6 memory training data respectively - */ - if (adev->bios) - reserve_size = - amdgpu_atomfirmware_get_fw_reserved_fb_size(adev); - - if (!adev->bios && - (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 3) || - amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 4) || - amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 5, 0))) - reserve_size = max(reserve_size, (uint32_t)280 << 20); - else if (!adev->bios && - amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(12, 1, 0)) { - if (hweight32(adev->aid_mask) == 1) - reserve_size = max(reserve_size, (uint32_t)128 << 20); - else - reserve_size = max(reserve_size, (uint32_t)144 << 20); - } else if (!reserve_size) - reserve_size = DISCOVERY_TMR_OFFSET; - - if (mem_train_support) { - /* reserve vram for mem train according to TMR location */ - amdgpu_ttm_training_data_block_init(adev, reserve_size); - ret = amdgpu_bo_create_kernel_at(adev, - ctx->c2p_train_data_offset, - ctx->train_data_size, - &ctx->c2p_bo, - NULL); - if (ret) { - dev_err(adev->dev, "alloc c2p_bo failed(%d)!\n", ret); - amdgpu_ttm_training_reserve_vram_fini(adev); - return ret; - } - ctx->init = PSP_MEM_TRAIN_RESERVE_SUCCESS; - } - - ret = amdgpu_bo_create_kernel_at( - adev, adev->gmc.real_vram_size - reserve_size, reserve_size, - &adev->mman.fw_reserved_memory, NULL); - if (ret) { - dev_err(adev->dev, "alloc tmr failed(%d)!\n", ret); - amdgpu_bo_free_kernel(&adev->mman.fw_reserved_memory, NULL, - NULL); - return ret; - } - - return 0; -} - static int amdgpu_ttm_pools_init(struct amdgpu_device *adev) { int i; @@ -2115,63 +2117,18 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) adev->gmc.visible_vram_size); #endif - /* - *The reserved vram for firmware must be pinned to the specified - *place on the VRAM, so reserve it early. - */ - r = amdgpu_ttm_fw_reserve_vram_init(adev); + amdgpu_ttm_init_vram_resv_regions(adev); + + r = amdgpu_ttm_alloc_vram_resv_regions(adev); if (r) return r; - /* - * The reserved VRAM for the driver must be pinned to a specific - * location in VRAM, so reserve it early. - */ - r = amdgpu_ttm_drv_reserve_vram_init(adev); - if (r) - return r; + if (adev->mman.resv_region[AMDGPU_RESV_MEM_TRAIN].size) { + struct psp_memory_training_context *ctx = + &adev->psp.mem_train_ctx; - /* - * only NAVI10 and later ASICs support IP discovery. - * If IP discovery is enabled, a block of memory should be - * reserved for it. - */ - if (adev->discovery.reserve_tmr) { - r = amdgpu_ttm_reserve_tmr(adev); - if (r) - return r; - } - - /* allocate memory as required for VGA - * This is used for VGA emulation and pre-OS scanout buffers to - * avoid display artifacts while transitioning between pre-OS - * and driver. - */ - if (!adev->gmc.is_app_apu) { - r = amdgpu_bo_create_kernel_at(adev, 0, - adev->mman.stolen_vga_size, - &adev->mman.stolen_vga_memory, - NULL); - if (r) - return r; - - r = amdgpu_bo_create_kernel_at(adev, adev->mman.stolen_vga_size, - adev->mman.stolen_extended_size, - &adev->mman.stolen_extended_memory, - NULL); - - if (r) - return r; - - r = amdgpu_bo_create_kernel_at(adev, - adev->mman.stolen_reserved_offset, - adev->mman.stolen_reserved_size, - &adev->mman.stolen_reserved_memory, - NULL); - if (r) - return r; - } else { - DRM_DEBUG_DRIVER("Skipped stolen memory reservation\n"); + amdgpu_ttm_training_data_block_init(adev); + ctx->init = PSP_MEM_TRAIN_RESERVE_SUCCESS; } dev_info(adev->dev, " %uM of VRAM memory ready\n", @@ -2284,23 +2241,19 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev) amdgpu_ttm_training_reserve_vram_fini(adev); /* return the stolen vga memory back to VRAM */ if (!adev->gmc.is_app_apu) { - amdgpu_bo_free_kernel(&adev->mman.stolen_vga_memory, NULL, NULL); - amdgpu_bo_free_kernel(&adev->mman.stolen_extended_memory, NULL, NULL); + amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_STOLEN_VGA); + amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_STOLEN_EXTENDED); /* return the FW reserved memory back to VRAM */ - amdgpu_bo_free_kernel(&adev->mman.fw_reserved_memory, NULL, - NULL); - amdgpu_bo_free_kernel(&adev->mman.fw_reserved_memory_extend, NULL, - NULL); - if (adev->mman.stolen_reserved_size) - amdgpu_bo_free_kernel(&adev->mman.stolen_reserved_memory, - NULL, NULL); + amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_FW); + amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_FW_EXTEND); + amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_STOLEN_RESERVED); } amdgpu_bo_free_kernel(&adev->mman.sdma_access_bo, NULL, &adev->mman.sdma_access_ptr); amdgpu_ttm_free_mmio_remap_bo(adev); - amdgpu_ttm_fw_reserve_vram_fini(adev); - amdgpu_ttm_drv_reserve_vram_fini(adev); + amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_FW_VRAM_USAGE); + amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_DRV_VRAM_USAGE); if (drm_dev_enter(adev_to_drm(adev), &idx)) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h index 3b1973611446..f2f23a42b3cc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h @@ -59,6 +59,26 @@ struct amdgpu_ttm_buffer_entity { u64 gart_window_offs[2]; }; +enum amdgpu_resv_region_id { + AMDGPU_RESV_STOLEN_VGA, + AMDGPU_RESV_STOLEN_EXTENDED, + AMDGPU_RESV_STOLEN_RESERVED, + AMDGPU_RESV_FW, + AMDGPU_RESV_FW_EXTEND, + AMDGPU_RESV_FW_VRAM_USAGE, + AMDGPU_RESV_DRV_VRAM_USAGE, + AMDGPU_RESV_MEM_TRAIN, + AMDGPU_RESV_MAX +}; + +struct amdgpu_vram_resv { + uint64_t offset; + uint64_t size; + struct amdgpu_bo *bo; + void *cpu_ptr; + bool needs_cpu_map; +}; + struct amdgpu_mman { struct ttm_device bdev; struct ttm_pool *ttm_pools; @@ -83,31 +103,9 @@ struct amdgpu_mman { struct amdgpu_gtt_mgr gtt_mgr; struct ttm_resource_manager preempt_mgr; - uint64_t stolen_vga_size; - struct amdgpu_bo *stolen_vga_memory; - uint64_t stolen_extended_size; - struct amdgpu_bo *stolen_extended_memory; bool keep_stolen_vga_memory; - struct amdgpu_bo *stolen_reserved_memory; - uint64_t stolen_reserved_offset; - uint64_t stolen_reserved_size; - - /* fw reserved memory */ - struct amdgpu_bo *fw_reserved_memory; - struct amdgpu_bo *fw_reserved_memory_extend; - - /* firmware VRAM reservation */ - u64 fw_vram_usage_start_offset; - u64 fw_vram_usage_size; - struct amdgpu_bo *fw_vram_usage_reserved_bo; - void *fw_vram_usage_va; - - /* driver VRAM reservation */ - u64 drv_vram_usage_start_offset; - u64 drv_vram_usage_size; - struct amdgpu_bo *drv_vram_usage_reserved_bo; - void *drv_vram_usage_va; + struct amdgpu_vram_resv resv_region[AMDGPU_RESV_MAX]; /* PAGE_SIZE'd BO for process memory r/w over SDMA. */ struct amdgpu_bo *sdma_access_bo; @@ -175,6 +173,15 @@ void amdgpu_vram_mgr_clear_reset_blocks(struct amdgpu_device *adev); bool amdgpu_res_cpu_visible(struct amdgpu_device *adev, struct ttm_resource *res); +void amdgpu_ttm_init_vram_resv(struct amdgpu_device *adev, + enum amdgpu_resv_region_id id, + uint64_t offset, uint64_t size, + bool needs_cpu_map); +int amdgpu_ttm_mark_vram_reserved(struct amdgpu_device *adev, + enum amdgpu_resv_region_id id); +void amdgpu_ttm_unmark_vram_reserved(struct amdgpu_device *adev, + enum amdgpu_resv_region_id id); + int amdgpu_ttm_init(struct amdgpu_device *adev); void amdgpu_ttm_fini(struct amdgpu_device *adev); void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 366728ed03e3..70d74f04d2dd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -106,9 +106,6 @@ amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr) int r = 0; int i; - /* Warning if current process mutex is not held */ - WARN_ON(!mutex_is_locked(&uq_mgr->userq_mutex)); - if (unlikely(adev->debug_disable_gpu_ring_reset)) { dev_err(adev->dev, "userq reset disabled by debug mask\n"); return 0; @@ -127,9 +124,11 @@ amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr) */ for (i = 0; i < num_queue_types; i++) { int ring_type = queue_types[i]; - const struct amdgpu_userq_funcs *funcs = adev->userq_funcs[ring_type]; + const struct amdgpu_userq_funcs *funcs = + adev->userq_funcs[ring_type]; - if (!amdgpu_userq_is_reset_type_supported(adev, ring_type, AMDGPU_RESET_TYPE_PER_QUEUE)) + if (!amdgpu_userq_is_reset_type_supported(adev, ring_type, + AMDGPU_RESET_TYPE_PER_QUEUE)) continue; if (atomic_read(&uq_mgr->userq_count[ring_type]) > 0 && @@ -150,38 +149,22 @@ amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr) static void amdgpu_userq_hang_detect_work(struct work_struct *work) { - struct amdgpu_usermode_queue *queue = container_of(work, - struct amdgpu_usermode_queue, - hang_detect_work.work); - struct dma_fence *fence; - struct amdgpu_userq_mgr *uq_mgr; + struct amdgpu_usermode_queue *queue = + container_of(work, struct amdgpu_usermode_queue, + hang_detect_work.work); - if (!queue->userq_mgr) - return; - - uq_mgr = queue->userq_mgr; - fence = READ_ONCE(queue->hang_detect_fence); - /* Fence already signaled – no action needed */ - if (!fence || dma_fence_is_signaled(fence)) - return; - - mutex_lock(&uq_mgr->userq_mutex); - amdgpu_userq_detect_and_reset_queues(uq_mgr); - mutex_unlock(&uq_mgr->userq_mutex); + amdgpu_userq_detect_and_reset_queues(queue->userq_mgr); } /* * Start hang detection for a user queue fence. A delayed work will be scheduled - * to check if the fence is still pending after the timeout period. -*/ + * to reset the queues when the fence doesn't signal in time. + */ void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue) { struct amdgpu_device *adev; unsigned long timeout_ms; - if (!queue || !queue->userq_mgr || !queue->userq_mgr->adev) - return; - adev = queue->userq_mgr->adev; /* Determine timeout based on queue type */ switch (queue->queue_type) { @@ -199,16 +182,33 @@ void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue) break; } - /* Store the fence to monitor and schedule hang detection */ - WRITE_ONCE(queue->hang_detect_fence, queue->last_fence); schedule_delayed_work(&queue->hang_detect_work, msecs_to_jiffies(timeout_ms)); } -static void amdgpu_userq_init_hang_detect_work(struct amdgpu_usermode_queue *queue) +void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell) { - INIT_DELAYED_WORK(&queue->hang_detect_work, amdgpu_userq_hang_detect_work); - queue->hang_detect_fence = NULL; + struct xarray *xa = &adev->userq_doorbell_xa; + struct amdgpu_usermode_queue *queue; + unsigned long flags; + int r; + + xa_lock_irqsave(xa, flags); + queue = xa_load(xa, doorbell); + if (queue) { + r = amdgpu_userq_fence_driver_process(queue->fence_drv); + /* + * We are in interrupt context here, this *can't* wait for + * reset work to finish. + */ + if (r >= 0) + cancel_delayed_work(&queue->hang_detect_work); + + /* Restart the timer when there are still fences pending */ + if (r == 1) + amdgpu_userq_start_hang_detect_work(queue); + } + xa_unlock_irqrestore(xa, flags); } static int amdgpu_userq_buffer_va_list_add(struct amdgpu_usermode_queue *queue, @@ -239,13 +239,12 @@ int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, u64 size; int r = 0; + /* Caller must hold vm->root.bo reservation */ + dma_resv_assert_held(queue->vm->root.bo->tbo.base.resv); + user_addr = (addr & AMDGPU_GMC_HOLE_MASK) >> AMDGPU_GPU_PAGE_SHIFT; size = expected_size >> AMDGPU_GPU_PAGE_SHIFT; - r = amdgpu_bo_reserve(vm->root.bo, false); - if (r) - return r; - va_map = amdgpu_vm_bo_lookup_mapping(vm, user_addr); if (!va_map) { r = -EINVAL; @@ -255,13 +254,11 @@ int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, if (user_addr >= va_map->start && va_map->last - user_addr + 1 >= size) { amdgpu_userq_buffer_va_list_add(queue, va_map, user_addr); - amdgpu_bo_unreserve(vm->root.bo); return 0; } r = -EINVAL; out_err: - amdgpu_bo_unreserve(vm->root.bo); return r; } @@ -270,15 +267,13 @@ static bool amdgpu_userq_buffer_va_mapped(struct amdgpu_vm *vm, u64 addr) struct amdgpu_bo_va_mapping *mapping; bool r; - if (amdgpu_bo_reserve(vm->root.bo, false)) - return false; + dma_resv_assert_held(vm->root.bo->tbo.base.resv); mapping = amdgpu_vm_bo_lookup_mapping(vm, addr); if (!IS_ERR_OR_NULL(mapping) && atomic_read(&mapping->bo_va->userq_va_mapped)) r = true; else r = false; - amdgpu_bo_unreserve(vm->root.bo); return r; } @@ -314,25 +309,21 @@ static int amdgpu_userq_buffer_vas_list_cleanup(struct amdgpu_device *adev, { struct amdgpu_userq_va_cursor *va_cursor, *tmp; struct amdgpu_bo_va_mapping *mapping; - int r; - r = amdgpu_bo_reserve(queue->vm->root.bo, false); - if (r) - return r; + /* Caller must hold vm->root.bo reservation */ + dma_resv_assert_held(queue->vm->root.bo->tbo.base.resv); list_for_each_entry_safe(va_cursor, tmp, &queue->userq_va_list, list) { mapping = amdgpu_vm_bo_lookup_mapping(queue->vm, va_cursor->gpu_addr); if (!mapping) { - r = -EINVAL; - goto err; + return -EINVAL; } dev_dbg(adev->dev, "delete the userq:%p va:%llx\n", queue, va_cursor->gpu_addr); amdgpu_userq_buffer_va_list_del(mapping, va_cursor); } -err: - amdgpu_bo_unreserve(queue->vm->root.bo); - return r; + + return 0; } static int amdgpu_userq_preempt_helper(struct amdgpu_usermode_queue *queue) @@ -341,23 +332,18 @@ static int amdgpu_userq_preempt_helper(struct amdgpu_usermode_queue *queue) struct amdgpu_device *adev = uq_mgr->adev; const struct amdgpu_userq_funcs *userq_funcs = adev->userq_funcs[queue->queue_type]; - bool found_hung_queue = false; - int r = 0; + int r; if (queue->state == AMDGPU_USERQ_STATE_MAPPED) { r = userq_funcs->preempt(queue); if (r) { queue->state = AMDGPU_USERQ_STATE_HUNG; - found_hung_queue = true; + return r; } else { queue->state = AMDGPU_USERQ_STATE_PREEMPTED; } } - - if (found_hung_queue) - amdgpu_userq_detect_and_reset_queues(uq_mgr); - - return r; + return 0; } static int amdgpu_userq_restore_helper(struct amdgpu_usermode_queue *queue) @@ -386,24 +372,21 @@ static int amdgpu_userq_unmap_helper(struct amdgpu_usermode_queue *queue) struct amdgpu_device *adev = uq_mgr->adev; const struct amdgpu_userq_funcs *userq_funcs = adev->userq_funcs[queue->queue_type]; - bool found_hung_queue = false; - int r = 0; + int r; if ((queue->state == AMDGPU_USERQ_STATE_MAPPED) || - (queue->state == AMDGPU_USERQ_STATE_PREEMPTED)) { + (queue->state == AMDGPU_USERQ_STATE_PREEMPTED)) { + r = userq_funcs->unmap(queue); if (r) { queue->state = AMDGPU_USERQ_STATE_HUNG; - found_hung_queue = true; + return r; } else { queue->state = AMDGPU_USERQ_STATE_UNMAPPED; } } - if (found_hung_queue) - amdgpu_userq_detect_and_reset_queues(uq_mgr); - - return r; + return 0; } static int amdgpu_userq_map_helper(struct amdgpu_usermode_queue *queue) @@ -412,38 +395,29 @@ static int amdgpu_userq_map_helper(struct amdgpu_usermode_queue *queue) struct amdgpu_device *adev = uq_mgr->adev; const struct amdgpu_userq_funcs *userq_funcs = adev->userq_funcs[queue->queue_type]; - int r = 0; + int r; if (queue->state == AMDGPU_USERQ_STATE_UNMAPPED) { r = userq_funcs->map(queue); if (r) { queue->state = AMDGPU_USERQ_STATE_HUNG; - amdgpu_userq_detect_and_reset_queues(uq_mgr); + return r; } else { queue->state = AMDGPU_USERQ_STATE_MAPPED; } } - return r; + return 0; } -static int amdgpu_userq_wait_for_last_fence(struct amdgpu_usermode_queue *queue) +static void amdgpu_userq_wait_for_last_fence(struct amdgpu_usermode_queue *queue) { - struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr; struct dma_fence *f = queue->last_fence; - int ret = 0; - if (f && !dma_fence_is_signaled(f)) { - ret = dma_fence_wait_timeout(f, true, MAX_SCHEDULE_TIMEOUT); - if (ret <= 0) { - drm_file_err(uq_mgr->file, "Timed out waiting for fence=%llu:%llu\n", - f->context, f->seqno); - queue->state = AMDGPU_USERQ_STATE_HUNG; - return -ETIME; - } - } + if (!f) + return; - return ret; + dma_fence_wait(f, false); } static void amdgpu_userq_cleanup(struct amdgpu_usermode_queue *queue) @@ -455,19 +429,26 @@ static void amdgpu_userq_cleanup(struct amdgpu_usermode_queue *queue) /* Wait for mode-1 reset to complete */ down_read(&adev->reset_domain->sem); - /* Drop the userq reference. */ - amdgpu_userq_buffer_vas_list_cleanup(adev, queue); uq_funcs->mqd_destroy(queue); - amdgpu_userq_fence_driver_free(queue); /* Use interrupt-safe locking since IRQ handlers may access these XArrays */ xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index); + amdgpu_userq_fence_driver_free(queue); + queue->fence_drv = NULL; queue->userq_mgr = NULL; list_del(&queue->userq_va_list); - kfree(queue); up_read(&adev->reset_domain->sem); } +/** + * amdgpu_userq_ensure_ev_fence - ensure a valid, unsignaled eviction fence exists + * @uq_mgr: the usermode queue manager for this process + * @evf_mgr: the eviction fence manager to check and rearm + * + * Ensures that a valid and not yet signaled eviction fence is attached to the + * usermode queue before any queue operations proceed. If it is signalled, then + * rearm a new eviction fence. + */ void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_eviction_fence_mgr *evf_mgr) @@ -627,6 +608,9 @@ static int amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_queue *queue) { struct amdgpu_device *adev = uq_mgr->adev; + struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr); + struct amdgpu_vm *vm = &fpriv->vm; + int r = 0; cancel_delayed_work_sync(&uq_mgr->resume_work); @@ -634,38 +618,36 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que /* Cancel any pending hang detection work and cleanup */ cancel_delayed_work_sync(&queue->hang_detect_work); + r = amdgpu_bo_reserve(vm->root.bo, false); + if (r) { + drm_file_err(uq_mgr->file, "Failed to reserve root bo during userqueue destroy\n"); + return r; + } + amdgpu_userq_buffer_vas_list_cleanup(adev, queue); + amdgpu_bo_unreserve(vm->root.bo); + mutex_lock(&uq_mgr->userq_mutex); - queue->hang_detect_fence = NULL; amdgpu_userq_wait_for_last_fence(queue); - r = amdgpu_bo_reserve(queue->db_obj.obj, true); - if (!r) { - amdgpu_bo_unpin(queue->db_obj.obj); - amdgpu_bo_unreserve(queue->db_obj.obj); - } - amdgpu_bo_unref(&queue->db_obj.obj); - - r = amdgpu_bo_reserve(queue->wptr_obj.obj, true); - if (!r) { - amdgpu_bo_unpin(queue->wptr_obj.obj); - amdgpu_bo_unreserve(queue->wptr_obj.obj); - } - amdgpu_bo_unref(&queue->wptr_obj.obj); - - atomic_dec(&uq_mgr->userq_count[queue->queue_type]); #if defined(CONFIG_DEBUG_FS) debugfs_remove_recursive(queue->debugfs_queue); #endif - amdgpu_userq_detect_and_reset_queues(uq_mgr); r = amdgpu_userq_unmap_helper(queue); - /*TODO: It requires a reset for userq hw unmap error*/ - if (unlikely(r != AMDGPU_USERQ_STATE_UNMAPPED)) { - drm_warn(adev_to_drm(uq_mgr->adev), "trying to destroy a HW mapping userq\n"); - queue->state = AMDGPU_USERQ_STATE_HUNG; - } + atomic_dec(&uq_mgr->userq_count[queue->queue_type]); amdgpu_userq_cleanup(queue); mutex_unlock(&uq_mgr->userq_mutex); + amdgpu_bo_reserve(queue->db_obj.obj, true); + amdgpu_bo_unpin(queue->db_obj.obj); + amdgpu_bo_unreserve(queue->db_obj.obj); + amdgpu_bo_unref(&queue->db_obj.obj); + + amdgpu_bo_reserve(queue->wptr_obj.obj, true); + amdgpu_bo_unpin(queue->wptr_obj.obj); + amdgpu_bo_unreserve(queue->wptr_obj.obj); + amdgpu_bo_unref(&queue->wptr_obj.obj); + kfree(queue); + pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r; @@ -738,35 +720,25 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) if (r) return r; - r = pm_runtime_get_sync(adev_to_drm(adev)->dev); + r = pm_runtime_resume_and_get(adev_to_drm(adev)->dev); if (r < 0) { - drm_file_err(uq_mgr->file, "pm_runtime_get_sync() failed for userqueue create\n"); - pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); + drm_file_err(uq_mgr->file, "pm_runtime_resume_and_get() failed for userqueue create\n"); return r; } - /* - * There could be a situation that we are creating a new queue while - * the other queues under this UQ_mgr are suspended. So if there is any - * resume work pending, wait for it to get done. - * - * This will also make sure we have a valid eviction fence ready to be used. - */ - amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr); - uq_funcs = adev->userq_funcs[args->in.ip_type]; if (!uq_funcs) { drm_file_err(uq_mgr->file, "Usermode queue is not supported for this IP (%u)\n", args->in.ip_type); r = -EINVAL; - goto unlock; + goto err_pm_runtime; } queue = kzalloc_obj(struct amdgpu_usermode_queue); if (!queue) { drm_file_err(uq_mgr->file, "Failed to allocate memory for queue\n"); r = -ENOMEM; - goto unlock; + goto err_pm_runtime; } INIT_LIST_HEAD(&queue->userq_va_list); @@ -781,28 +753,36 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) db_info.doorbell_offset = args->in.doorbell_offset; queue->userq_mgr = uq_mgr; + /* Validate the userq virtual address.*/ + r = amdgpu_bo_reserve(fpriv->vm.root.bo, false); + if (r) + goto free_queue; + if (amdgpu_userq_input_va_validate(adev, queue, args->in.queue_va, args->in.queue_size) || amdgpu_userq_input_va_validate(adev, queue, args->in.rptr_va, AMDGPU_GPU_PAGE_SIZE) || amdgpu_userq_input_va_validate(adev, queue, args->in.wptr_va, AMDGPU_GPU_PAGE_SIZE)) { r = -EINVAL; - goto free_queue; + amdgpu_bo_unreserve(fpriv->vm.root.bo); + goto clean_mapping; } + amdgpu_bo_unreserve(fpriv->vm.root.bo); /* Convert relative doorbell offset into absolute doorbell index */ index = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp); if (index == (uint64_t)-EINVAL) { drm_file_err(uq_mgr->file, "Failed to get doorbell for queue\n"); r = -EINVAL; - goto free_queue; + goto clean_mapping; } queue->doorbell_index = index; + mutex_init(&queue->fence_drv_lock); xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC); - r = amdgpu_userq_fence_driver_alloc(adev, queue); + r = amdgpu_userq_fence_driver_alloc(adev, &queue->fence_drv); if (r) { drm_file_err(uq_mgr->file, "Failed to alloc fence driver\n"); - goto free_queue; + goto clean_mapping; } r = uq_funcs->mqd_create(queue, &args->in); @@ -811,6 +791,8 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) goto clean_fence_driver; } + amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr); + /* don't map the queue if scheduling is halted */ if (adev->userq_halt_for_enforce_isolation && ((queue->queue_type == AMDGPU_HW_IP_GFX) || @@ -822,7 +804,6 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) r = amdgpu_userq_map_helper(queue); if (r) { drm_file_err(uq_mgr->file, "Failed to map Queue\n"); - down_read(&adev->reset_domain->sem); goto clean_mqd; } } @@ -838,9 +819,8 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) if (r) { if (!skip_map_queue) amdgpu_userq_unmap_helper(queue); - r = -ENOMEM; - goto clean_mqd; + goto clean_reset_domain; } r = xa_err(xa_store_irq(&adev->userq_doorbell_xa, index, queue, GFP_KERNEL)); @@ -848,29 +828,35 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) xa_erase(&uq_mgr->userq_xa, qid); if (!skip_map_queue) amdgpu_userq_unmap_helper(queue); - - goto clean_mqd; + goto clean_reset_domain; } up_read(&adev->reset_domain->sem); amdgpu_debugfs_userq_init(filp, queue, qid); - amdgpu_userq_init_hang_detect_work(queue); + INIT_DELAYED_WORK(&queue->hang_detect_work, + amdgpu_userq_hang_detect_work); args->out.queue_id = qid; atomic_inc(&uq_mgr->userq_count[queue->queue_type]); mutex_unlock(&uq_mgr->userq_mutex); return 0; -clean_mqd: - uq_funcs->mqd_destroy(queue); +clean_reset_domain: up_read(&adev->reset_domain->sem); +clean_mqd: + mutex_unlock(&uq_mgr->userq_mutex); + uq_funcs->mqd_destroy(queue); clean_fence_driver: amdgpu_userq_fence_driver_free(queue); +clean_mapping: + amdgpu_bo_reserve(fpriv->vm.root.bo, true); + amdgpu_userq_buffer_vas_list_cleanup(adev, queue); + amdgpu_bo_unreserve(fpriv->vm.root.bo); + mutex_destroy(&queue->fence_drv_lock); free_queue: kfree(queue); -unlock: - mutex_unlock(&uq_mgr->userq_mutex); - +err_pm_runtime: + pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r; } @@ -1000,10 +986,16 @@ int amdgpu_userq_ioctl(struct drm_device *dev, void *data, static int amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr) { + struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr); + struct amdgpu_vm *vm = &fpriv->vm; struct amdgpu_usermode_queue *queue; unsigned long queue_id; int ret = 0, r; + + if (amdgpu_bo_reserve(vm->root.bo, false)) + return false; + mutex_lock(&uq_mgr->userq_mutex); /* Resume all the queues for this process */ xa_for_each(&uq_mgr->userq_xa, queue_id, queue) { @@ -1021,9 +1013,11 @@ amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr) } mutex_unlock(&uq_mgr->userq_mutex); + amdgpu_bo_unreserve(vm->root.bo); if (ret) - drm_file_err(uq_mgr->file, "Failed to map all the queues\n"); + drm_file_err(uq_mgr->file, + "Failed to map all the queues, restore failed ret=%d\n", ret); return ret; } @@ -1180,7 +1174,7 @@ retry_lock: bo = range->bo; ret = amdgpu_ttm_tt_get_user_pages(bo, range); if (ret) - goto unlock_all; + goto free_ranges; } invalidated = true; @@ -1207,6 +1201,7 @@ retry_lock: unlock_all: drm_exec_fini(&exec); +free_ranges: xa_for_each(&xa, tmp_key, range) { if (!range) continue; @@ -1230,13 +1225,11 @@ static void amdgpu_userq_restore_worker(struct work_struct *work) ret = amdgpu_userq_vm_validate(uq_mgr); if (ret) { - drm_file_err(uq_mgr->file, "Failed to validate BOs to restore\n"); + drm_file_err(uq_mgr->file, "Failed to validate BOs to restore ret=%d\n", ret); goto put_fence; } - ret = amdgpu_userq_restore_all(uq_mgr); - if (ret) - drm_file_err(uq_mgr->file, "Failed to restore all queues\n"); + amdgpu_userq_restore_all(uq_mgr); put_fence: dma_fence_put(ev_fence); @@ -1249,7 +1242,6 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr) unsigned long queue_id; int ret = 0, r; - amdgpu_userq_detect_and_reset_queues(uq_mgr); /* Try to unmap all the queues in this process ctx */ xa_for_each(&uq_mgr->userq_xa, queue_id, queue) { r = amdgpu_userq_preempt_helper(queue); @@ -1257,8 +1249,11 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr) ret = r; } - if (ret) - drm_file_err(uq_mgr->file, "Couldn't unmap all the queues\n"); + if (ret) { + drm_file_err(uq_mgr->file, + "Couldn't unmap all the queues, eviction failed ret=%d\n", ret); + amdgpu_userq_detect_and_reset_queues(uq_mgr); + } return ret; } @@ -1279,46 +1274,28 @@ void amdgpu_userq_reset_work(struct work_struct *work) amdgpu_device_gpu_recover(adev, NULL, &reset_context); } -static int +static void amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr) { struct amdgpu_usermode_queue *queue; unsigned long queue_id; - int ret; xa_for_each(&uq_mgr->userq_xa, queue_id, queue) { struct dma_fence *f = queue->last_fence; - if (!f || dma_fence_is_signaled(f)) + if (!f) continue; - ret = dma_fence_wait_timeout(f, true, msecs_to_jiffies(100)); - if (ret <= 0) { - drm_file_err(uq_mgr->file, "Timed out waiting for fence=%llu:%llu\n", - f->context, f->seqno); - - return -ETIMEDOUT; - } + dma_fence_wait(f, false); } - - return 0; } void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr) { - struct amdgpu_device *adev = uq_mgr->adev; - int ret; - /* Wait for any pending userqueue fence work to finish */ - ret = amdgpu_userq_wait_for_signal(uq_mgr); - if (ret) - dev_err(adev->dev, "Not evicting userqueue, timeout waiting for work\n"); - - ret = amdgpu_userq_evict_all(uq_mgr); - if (ret) - dev_err(adev->dev, "Failed to evict userqueue\n"); - + amdgpu_userq_wait_for_signal(uq_mgr); + amdgpu_userq_evict_all(uq_mgr); } int amdgpu_userq_mgr_init(struct amdgpu_userq_mgr *userq_mgr, struct drm_file *file_priv, @@ -1376,7 +1353,6 @@ int amdgpu_userq_suspend(struct amdgpu_device *adev) uqm = queue->userq_mgr; cancel_delayed_work_sync(&uqm->resume_work); guard(mutex)(&uqm->userq_mutex); - amdgpu_userq_detect_and_reset_queues(uqm); if (adev->in_s0ix) r = amdgpu_userq_preempt_helper(queue); else @@ -1435,7 +1411,6 @@ int amdgpu_userq_stop_sched_for_enforce_isolation(struct amdgpu_device *adev, if (((queue->queue_type == AMDGPU_HW_IP_GFX) || (queue->queue_type == AMDGPU_HW_IP_COMPUTE)) && (queue->xcp_id == idx)) { - amdgpu_userq_detect_and_reset_queues(uqm); r = amdgpu_userq_preempt_helper(queue); if (r) ret = r; @@ -1480,17 +1455,16 @@ int amdgpu_userq_start_sched_for_enforce_isolation(struct amdgpu_device *adev, return ret; } -int amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, - uint64_t saddr) +void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, + struct amdgpu_bo_va_mapping *mapping, + uint64_t saddr) { u32 ip_mask = amdgpu_userq_get_supported_ip_mask(adev); struct amdgpu_bo_va *bo_va = mapping->bo_va; struct dma_resv *resv = bo_va->base.bo->tbo.base.resv; - int ret = 0; if (!ip_mask) - return 0; + return; dev_warn_once(adev->dev, "now unmapping a vital queue va:%llx\n", saddr); /** @@ -1501,37 +1475,29 @@ int amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, * unmap is only for one kind of userq VAs, so at this point suppose * the eviction fence is always unsignaled. */ - if (!dma_resv_test_signaled(resv, DMA_RESV_USAGE_BOOKKEEP)) { - ret = dma_resv_wait_timeout(resv, DMA_RESV_USAGE_BOOKKEEP, true, - MAX_SCHEDULE_TIMEOUT); - if (ret <= 0) - return -EBUSY; - } - - return 0; + dma_resv_wait_timeout(resv, DMA_RESV_USAGE_BOOKKEEP, + false, MAX_SCHEDULE_TIMEOUT); } void amdgpu_userq_pre_reset(struct amdgpu_device *adev) { const struct amdgpu_userq_funcs *userq_funcs; struct amdgpu_usermode_queue *queue; - struct amdgpu_userq_mgr *uqm; unsigned long queue_id; + /* TODO: We probably need a new lock for the queue state */ xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) { - uqm = queue->userq_mgr; - cancel_delayed_work_sync(&uqm->resume_work); - if (queue->state == AMDGPU_USERQ_STATE_MAPPED) { - amdgpu_userq_wait_for_last_fence(queue); - userq_funcs = adev->userq_funcs[queue->queue_type]; - userq_funcs->unmap(queue); - /* just mark all queues as hung at this point. - * if unmap succeeds, we could map again - * in amdgpu_userq_post_reset() if vram is not lost - */ - queue->state = AMDGPU_USERQ_STATE_HUNG; - amdgpu_userq_fence_driver_force_completion(queue); - } + if (queue->state != AMDGPU_USERQ_STATE_MAPPED) + continue; + + userq_funcs = adev->userq_funcs[queue->queue_type]; + userq_funcs->unmap(queue); + /* just mark all queues as hung at this point. + * if unmap succeeds, we could map again + * in amdgpu_userq_post_reset() if vram is not lost + */ + queue->state = AMDGPU_USERQ_STATE_HUNG; + amdgpu_userq_fence_driver_force_completion(queue); } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index a4d44abf24fa..85f460e7c31b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -66,6 +66,18 @@ struct amdgpu_usermode_queue { struct amdgpu_userq_obj db_obj; struct amdgpu_userq_obj fw_obj; struct amdgpu_userq_obj wptr_obj; + + /** + * @fence_drv_lock: Protecting @fence_drv_xa. + */ + struct mutex fence_drv_lock; + + /** + * @fence_drv_xa: + * + * References to the external fence drivers returned by wait_ioctl. + * Dropped on the next signaled dma_fence or queue destruction. + */ struct xarray fence_drv_xa; struct amdgpu_userq_fence_driver *fence_drv; struct dma_fence *last_fence; @@ -73,7 +85,6 @@ struct amdgpu_usermode_queue { int priority; struct dentry *debugfs_queue; struct delayed_work hang_detect_work; - struct dma_fence *hang_detect_fence; struct kref refcount; struct list_head userq_va_list; @@ -156,11 +167,12 @@ void amdgpu_userq_reset_work(struct work_struct *work); void amdgpu_userq_pre_reset(struct amdgpu_device *adev); int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost); void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue); +void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell); int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, struct amdgpu_usermode_queue *queue, u64 addr, u64 expected_size); -int amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, - struct amdgpu_bo_va_mapping *mapping, - uint64_t saddr); +void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, + struct amdgpu_bo_va_mapping *mapping, + uint64_t saddr); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c index fe6d83e859a0..53a8944bab05 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c @@ -32,29 +32,9 @@ #include "amdgpu.h" #include "amdgpu_userq_fence.h" -static const struct dma_fence_ops amdgpu_userq_fence_ops; -static struct kmem_cache *amdgpu_userq_fence_slab; - #define AMDGPU_USERQ_MAX_HANDLES (1U << 16) -int amdgpu_userq_fence_slab_init(void) -{ - amdgpu_userq_fence_slab = kmem_cache_create("amdgpu_userq_fence", - sizeof(struct amdgpu_userq_fence), - 0, - SLAB_HWCACHE_ALIGN, - NULL); - if (!amdgpu_userq_fence_slab) - return -ENOMEM; - - return 0; -} - -void amdgpu_userq_fence_slab_fini(void) -{ - rcu_barrier(); - kmem_cache_destroy(amdgpu_userq_fence_slab); -} +static const struct dma_fence_ops amdgpu_userq_fence_ops; static inline struct amdgpu_userq_fence *to_amdgpu_userq_fence(struct dma_fence *f) { @@ -78,12 +58,15 @@ amdgpu_userq_fence_write(struct amdgpu_userq_fence_driver *fence_drv, } int amdgpu_userq_fence_driver_alloc(struct amdgpu_device *adev, - struct amdgpu_usermode_queue *userq) + struct amdgpu_userq_fence_driver **fence_drv_req) { struct amdgpu_userq_fence_driver *fence_drv; - unsigned long flags; int r; + if (!fence_drv_req) + return -EINVAL; + *fence_drv_req = NULL; + fence_drv = kzalloc_obj(*fence_drv); if (!fence_drv) return -ENOMEM; @@ -104,19 +87,10 @@ int amdgpu_userq_fence_driver_alloc(struct amdgpu_device *adev, fence_drv->context = dma_fence_context_alloc(1); get_task_comm(fence_drv->timeline_name, current); - xa_lock_irqsave(&adev->userq_xa, flags); - r = xa_err(__xa_store(&adev->userq_xa, userq->doorbell_index, - fence_drv, GFP_KERNEL)); - xa_unlock_irqrestore(&adev->userq_xa, flags); - if (r) - goto free_seq64; - - userq->fence_drv = fence_drv; + *fence_drv_req = fence_drv; return 0; -free_seq64: - amdgpu_seq64_free(adev, fence_drv->va); free_fence_drv: kfree(fence_drv); @@ -144,42 +118,66 @@ void amdgpu_userq_fence_driver_free(struct amdgpu_usermode_queue *userq) { dma_fence_put(userq->last_fence); - + userq->last_fence = NULL; amdgpu_userq_walk_and_drop_fence_drv(&userq->fence_drv_xa); xa_destroy(&userq->fence_drv_xa); - /* Drop the fence_drv reference held by user queue */ + mutex_destroy(&userq->fence_drv_lock); + /* Drop the queue's ownership reference to fence_drv explicitly */ amdgpu_userq_fence_driver_put(userq->fence_drv); } -void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_drv) +static void +amdgpu_userq_fence_put_fence_drv_array(struct amdgpu_userq_fence *userq_fence) +{ + unsigned long i; + for (i = 0; i < userq_fence->fence_drv_array_count; i++) + amdgpu_userq_fence_driver_put(userq_fence->fence_drv_array[i]); + userq_fence->fence_drv_array_count = 0; +} + +/* + * Returns: + * -ENOENT when no fences were processes + * 1 when more fences are pending + * 0 when no fences are pending any more + */ +int +amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_drv) { struct amdgpu_userq_fence *userq_fence, *tmp; + LIST_HEAD(to_be_signaled); struct dma_fence *fence; unsigned long flags; u64 rptr; - int i; - - if (!fence_drv) - return; spin_lock_irqsave(&fence_drv->fence_list_lock, flags); rptr = amdgpu_userq_fence_read(fence_drv); - list_for_each_entry_safe(userq_fence, tmp, &fence_drv->fences, link) { - fence = &userq_fence->base; - - if (rptr < fence->seqno) + list_for_each_entry(userq_fence, &fence_drv->fences, link) { + if (rptr < userq_fence->base.seqno) break; + } + list_cut_before(&to_be_signaled, &fence_drv->fences, + &userq_fence->link); + spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags); + + if (list_empty(&to_be_signaled)) + return -ENOENT; + + list_for_each_entry_safe(userq_fence, tmp, &to_be_signaled, link) { + fence = &userq_fence->base; + list_del_init(&userq_fence->link); dma_fence_signal(fence); - - for (i = 0; i < userq_fence->fence_drv_array_count; i++) - amdgpu_userq_fence_driver_put(userq_fence->fence_drv_array[i]); - - list_del(&userq_fence->link); + /* Drop fence_drv_array outside fence_list_lock + * to avoid the recursion lock. + */ + amdgpu_userq_fence_put_fence_drv_array(userq_fence); dma_fence_put(fence); } - spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags); + + /* That doesn't need to be accurate so no locking */ + return list_empty(&fence_drv->fences) ? 0 : 1; } void amdgpu_userq_fence_driver_destroy(struct kref *ref) @@ -187,11 +185,9 @@ void amdgpu_userq_fence_driver_destroy(struct kref *ref) struct amdgpu_userq_fence_driver *fence_drv = container_of(ref, struct amdgpu_userq_fence_driver, refcount); - struct amdgpu_userq_fence_driver *xa_fence_drv; struct amdgpu_device *adev = fence_drv->adev; struct amdgpu_userq_fence *fence, *tmp; - struct xarray *xa = &adev->userq_xa; - unsigned long index, flags; + unsigned long flags; struct dma_fence *f; spin_lock_irqsave(&fence_drv->fence_list_lock, flags); @@ -208,12 +204,6 @@ void amdgpu_userq_fence_driver_destroy(struct kref *ref) } spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags); - xa_lock_irqsave(xa, flags); - xa_for_each(xa, index, xa_fence_drv) - if (xa_fence_drv == fence_drv) - __xa_erase(xa, index); - xa_unlock_irqrestore(xa, flags); - /* Free seq64 memory */ amdgpu_seq64_free(adev, fence_drv->va); kfree(fence_drv); @@ -229,75 +219,84 @@ void amdgpu_userq_fence_driver_put(struct amdgpu_userq_fence_driver *fence_drv) kref_put(&fence_drv->refcount, amdgpu_userq_fence_driver_destroy); } -static int amdgpu_userq_fence_alloc(struct amdgpu_userq_fence **userq_fence) +static int amdgpu_userq_fence_alloc(struct amdgpu_usermode_queue *userq, + struct amdgpu_userq_fence **pfence) { - *userq_fence = kmem_cache_alloc(amdgpu_userq_fence_slab, GFP_ATOMIC); - return *userq_fence ? 0 : -ENOMEM; -} + struct amdgpu_userq_fence_driver *fence_drv = userq->fence_drv; + struct amdgpu_userq_fence *userq_fence; + void *entry; -static int amdgpu_userq_fence_create(struct amdgpu_usermode_queue *userq, - struct amdgpu_userq_fence *userq_fence, - u64 seq, struct dma_fence **f) -{ - struct amdgpu_userq_fence_driver *fence_drv; - struct dma_fence *fence; - unsigned long flags; + userq_fence = kmalloc(sizeof(*userq_fence), GFP_KERNEL); + if (!userq_fence) + return -ENOMEM; - fence_drv = userq->fence_drv; - if (!fence_drv) - return -EINVAL; + /* + * Get the next unused entry, since we fill from the start this can be + * used as size to allocate the array. + */ + mutex_lock(&userq->fence_drv_lock); + XA_STATE(xas, &userq->fence_drv_xa, 0); - spin_lock_init(&userq_fence->lock); - INIT_LIST_HEAD(&userq_fence->link); - fence = &userq_fence->base; - userq_fence->fence_drv = fence_drv; + rcu_read_lock(); + do { + entry = xas_find_marked(&xas, ULONG_MAX, XA_FREE_MARK); + } while (xas_retry(&xas, entry)); + rcu_read_unlock(); - dma_fence_init64(fence, &amdgpu_userq_fence_ops, &userq_fence->lock, - fence_drv->context, seq); - - amdgpu_userq_fence_driver_get(fence_drv); - dma_fence_get(fence); - - if (!xa_empty(&userq->fence_drv_xa)) { - struct amdgpu_userq_fence_driver *stored_fence_drv; - unsigned long index, count = 0; - int i = 0; - - xa_lock(&userq->fence_drv_xa); - xa_for_each(&userq->fence_drv_xa, index, stored_fence_drv) - count++; - - userq_fence->fence_drv_array = - kvmalloc_objs(struct amdgpu_userq_fence_driver *, count, - GFP_ATOMIC); - - if (userq_fence->fence_drv_array) { - xa_for_each(&userq->fence_drv_xa, index, stored_fence_drv) { - userq_fence->fence_drv_array[i] = stored_fence_drv; - __xa_erase(&userq->fence_drv_xa, index); - i++; - } - } - - userq_fence->fence_drv_array_count = i; - xa_unlock(&userq->fence_drv_xa); - } else { - userq_fence->fence_drv_array = NULL; - userq_fence->fence_drv_array_count = 0; + userq_fence->fence_drv_array = kvmalloc_array(xas.xa_index, + sizeof(fence_drv), + GFP_KERNEL); + if (!userq_fence->fence_drv_array) { + mutex_unlock(&userq->fence_drv_lock); + kfree(userq_fence); + return -ENOMEM; } - /* Check if hardware has already processed the job */ - spin_lock_irqsave(&fence_drv->fence_list_lock, flags); - if (!dma_fence_is_signaled(fence)) - list_add_tail(&userq_fence->link, &fence_drv->fences); - else - dma_fence_put(fence); + userq_fence->fence_drv_array_count = xas.xa_index; + xa_extract(&userq->fence_drv_xa, (void **)userq_fence->fence_drv_array, + 0, ULONG_MAX, xas.xa_index, XA_PRESENT); + xa_destroy(&userq->fence_drv_xa); + mutex_unlock(&userq->fence_drv_lock); + + amdgpu_userq_fence_driver_get(fence_drv); + userq_fence->fence_drv = fence_drv; + + *pfence = userq_fence; + return 0; +} + +static void amdgpu_userq_fence_init(struct amdgpu_usermode_queue *userq, + struct amdgpu_userq_fence *fence, + u64 seq) +{ + struct amdgpu_userq_fence_driver *fence_drv = userq->fence_drv; + unsigned long flags; + bool signaled = false; + + spin_lock_init(&fence->lock); + dma_fence_init64(&fence->base, &amdgpu_userq_fence_ops, &fence->lock, + fence_drv->context, seq); + + /* Make sure the fence is visible to the hang detect worker */ + dma_fence_put(userq->last_fence); + userq->last_fence = dma_fence_get(&fence->base); + + /* Check if hardware has already processed the fence */ + spin_lock_irqsave(&fence_drv->fence_list_lock, flags); + if (!dma_fence_is_signaled(&fence->base)) { + dma_fence_get(&fence->base); + list_add_tail(&fence->link, &fence_drv->fences); + } else { + INIT_LIST_HEAD(&fence->link); + signaled = true; + } spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags); - *f = fence; - - return 0; + if (signaled) + amdgpu_userq_fence_put_fence_drv_array(fence); + else + amdgpu_userq_start_hang_detect_work(userq); } static const char *amdgpu_userq_fence_get_driver_name(struct dma_fence *f) @@ -337,7 +336,7 @@ static void amdgpu_userq_fence_free(struct rcu_head *rcu) amdgpu_userq_fence_driver_put(fence_drv); kvfree(userq_fence->fence_drv_array); - kmem_cache_free(amdgpu_userq_fence_slab, userq_fence); + kfree(userq_fence); } static void amdgpu_userq_fence_release(struct dma_fence *f) @@ -418,11 +417,6 @@ map_error: return r; } -static void amdgpu_userq_fence_cleanup(struct dma_fence *fence) -{ - dma_fence_put(fence); -} - static void amdgpu_userq_fence_driver_set_error(struct amdgpu_userq_fence *fence, int error) @@ -466,13 +460,14 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data, const unsigned int num_read_bo_handles = args->num_bo_read_handles; struct amdgpu_fpriv *fpriv = filp->driver_priv; struct amdgpu_userq_mgr *userq_mgr = &fpriv->userq_mgr; + struct drm_gem_object **gobj_write, **gobj_read; u32 *syncobj_handles, num_syncobj_handles; - struct amdgpu_userq_fence *userq_fence; - struct amdgpu_usermode_queue *queue = NULL; - struct drm_syncobj **syncobj = NULL; - struct dma_fence *fence; + struct amdgpu_usermode_queue *queue; + struct amdgpu_userq_fence *fence; + struct drm_syncobj **syncobj; struct drm_exec exec; + void __user *ptr; int r, i, entry; u64 wptr; @@ -484,13 +479,14 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data, return -EINVAL; num_syncobj_handles = args->num_syncobj_handles; - syncobj_handles = memdup_array_user(u64_to_user_ptr(args->syncobj_handles), - num_syncobj_handles, sizeof(u32)); + ptr = u64_to_user_ptr(args->syncobj_handles); + syncobj_handles = memdup_array_user(ptr, num_syncobj_handles, + sizeof(u32)); if (IS_ERR(syncobj_handles)) return PTR_ERR(syncobj_handles); - /* Array of pointers to the looked up syncobjs */ - syncobj = kmalloc_array(num_syncobj_handles, sizeof(*syncobj), GFP_KERNEL); + syncobj = kmalloc_array(num_syncobj_handles, sizeof(*syncobj), + GFP_KERNEL); if (!syncobj) { r = -ENOMEM; goto free_syncobj_handles; @@ -504,21 +500,17 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data, } } - r = drm_gem_objects_lookup(filp, - u64_to_user_ptr(args->bo_read_handles), - num_read_bo_handles, - &gobj_read); + ptr = u64_to_user_ptr(args->bo_read_handles); + r = drm_gem_objects_lookup(filp, ptr, num_read_bo_handles, &gobj_read); if (r) goto free_syncobj; - r = drm_gem_objects_lookup(filp, - u64_to_user_ptr(args->bo_write_handles), - num_write_bo_handles, + ptr = u64_to_user_ptr(args->bo_write_handles); + r = drm_gem_objects_lookup(filp, ptr, num_write_bo_handles, &gobj_write); if (r) goto put_gobj_read; - /* Retrieve the user queue */ queue = amdgpu_userq_get(userq_mgr, args->queue_id); if (!queue) { r = -ENOENT; @@ -527,73 +519,61 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data, r = amdgpu_userq_fence_read_wptr(adev, queue, &wptr); if (r) - goto put_gobj_write; + goto put_queue; - r = amdgpu_userq_fence_alloc(&userq_fence); + r = amdgpu_userq_fence_alloc(queue, &fence); if (r) - goto put_gobj_write; + goto put_queue; /* We are here means UQ is active, make sure the eviction fence is valid */ amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr); - /* Create a new fence */ - r = amdgpu_userq_fence_create(queue, userq_fence, wptr, &fence); - if (r) { - mutex_unlock(&userq_mgr->userq_mutex); - kmem_cache_free(amdgpu_userq_fence_slab, userq_fence); - goto put_gobj_write; - } + /* Create the new fence */ + amdgpu_userq_fence_init(queue, fence, wptr); - dma_fence_put(queue->last_fence); - queue->last_fence = dma_fence_get(fence); - amdgpu_userq_start_hang_detect_work(queue); mutex_unlock(&userq_mgr->userq_mutex); + /* + * This needs to come after the fence is created since + * amdgpu_userq_ensure_ev_fence() can't be called while holding the resv + * locks. + */ drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, (num_read_bo_handles + num_write_bo_handles)); - /* Lock all BOs with retry handling */ drm_exec_until_all_locked(&exec) { - r = drm_exec_prepare_array(&exec, gobj_read, num_read_bo_handles, 1); + r = drm_exec_prepare_array(&exec, gobj_read, + num_read_bo_handles, 1); drm_exec_retry_on_contention(&exec); - if (r) { - amdgpu_userq_fence_cleanup(fence); + if (r) goto exec_fini; - } - r = drm_exec_prepare_array(&exec, gobj_write, num_write_bo_handles, 1); + r = drm_exec_prepare_array(&exec, gobj_write, + num_write_bo_handles, 1); drm_exec_retry_on_contention(&exec); - if (r) { - amdgpu_userq_fence_cleanup(fence); + if (r) goto exec_fini; - } } - for (i = 0; i < num_read_bo_handles; i++) { - if (!gobj_read || !gobj_read[i]->resv) - continue; - - dma_resv_add_fence(gobj_read[i]->resv, fence, + /* And publish the new fence in the BOs and syncobj */ + for (i = 0; i < num_read_bo_handles; i++) + dma_resv_add_fence(gobj_read[i]->resv, &fence->base, DMA_RESV_USAGE_READ); - } - for (i = 0; i < num_write_bo_handles; i++) { - if (!gobj_write || !gobj_write[i]->resv) - continue; - - dma_resv_add_fence(gobj_write[i]->resv, fence, + for (i = 0; i < num_write_bo_handles; i++) + dma_resv_add_fence(gobj_write[i]->resv, &fence->base, DMA_RESV_USAGE_WRITE); - } - /* Add the created fence to syncobj/BO's */ for (i = 0; i < num_syncobj_handles; i++) - drm_syncobj_replace_fence(syncobj[i], fence); - - /* drop the reference acquired in fence creation function */ - dma_fence_put(fence); + drm_syncobj_replace_fence(syncobj[i], &fence->base); exec_fini: + /* drop the reference acquired in fence creation function */ + dma_fence_put(&fence->base); + drm_exec_fini(&exec); +put_queue: + amdgpu_userq_put(queue); put_gobj_write: for (i = 0; i < num_write_bo_handles; i++) drm_gem_object_put(gobj_write[i]); @@ -604,15 +584,11 @@ put_gobj_read: kvfree(gobj_read); free_syncobj: while (entry-- > 0) - if (syncobj[entry]) - drm_syncobj_put(syncobj[entry]); + drm_syncobj_put(syncobj[entry]); kfree(syncobj); free_syncobj_handles: kfree(syncobj_handles); - if (queue) - amdgpu_userq_put(queue); - return r; } @@ -887,8 +863,10 @@ amdgpu_userq_wait_return_fence_info(struct drm_file *filp, * Otherwise, we would gather those references until we don't * have any more space left and crash. */ + mutex_lock(&waitq->fence_drv_lock); r = xa_alloc(&waitq->fence_drv_xa, &index, fence_drv, xa_limit_32b, GFP_KERNEL); + mutex_unlock(&waitq->fence_drv_lock); if (r) goto put_waitq; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.h index d76add2afc77..0bd51616cef1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.h @@ -58,15 +58,12 @@ struct amdgpu_userq_fence_driver { char timeline_name[TASK_COMM_LEN]; }; -int amdgpu_userq_fence_slab_init(void); -void amdgpu_userq_fence_slab_fini(void); - void amdgpu_userq_fence_driver_get(struct amdgpu_userq_fence_driver *fence_drv); void amdgpu_userq_fence_driver_put(struct amdgpu_userq_fence_driver *fence_drv); int amdgpu_userq_fence_driver_alloc(struct amdgpu_device *adev, - struct amdgpu_usermode_queue *userq); + struct amdgpu_userq_fence_driver **fence_drv_req); void amdgpu_userq_fence_driver_free(struct amdgpu_usermode_queue *userq); -void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_drv); +int amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_drv); void amdgpu_userq_fence_driver_force_completion(struct amdgpu_usermode_queue *userq); void amdgpu_userq_fence_driver_destroy(struct kref *ref); int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c index eb4a15db2ef2..efdebd9c0a1f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c @@ -680,6 +680,9 @@ static int amdgpu_vce_cs_reloc(struct amdgpu_cs_parser *p, struct amdgpu_ib *ib, uint64_t addr; int r; + if (lo >= ib->length_dw || hi >= ib->length_dw) + return -EINVAL; + if (index == 0xffffffff) index = 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c index 03d95dca93d7..debb82a2e031 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c @@ -34,6 +34,7 @@ #include "amdgpu.h" #include "amdgpu_pm.h" #include "amdgpu_vcn.h" +#include "amdgpu_reset.h" #include "soc15d.h" /* Firmware Names */ @@ -361,7 +362,7 @@ int amdgpu_vcn_suspend(struct amdgpu_device *adev, int i) /* err_event_athub and dpc recovery will corrupt VCPU buffer, so we need to * restore fw data and clear buffer in amdgpu_vcn_resume() */ - if (in_ras_intr || adev->pcie_reset_ctx.in_link_reset) + if (in_ras_intr || amdgpu_reset_in_dpc(adev)) return 0; return amdgpu_vcn_save_vcpu_bo_inst(adev, i); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c index dba7ea16a10d..e8d180a412d1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c @@ -437,12 +437,9 @@ static void amdgpu_virt_add_bad_page(struct amdgpu_device *adev, struct eeprom_table_record bp; uint64_t retired_page; uint32_t bp_idx, bp_cnt; - void *vram_usage_va = NULL; - - if (adev->mman.fw_vram_usage_va) - vram_usage_va = adev->mman.fw_vram_usage_va; - else - vram_usage_va = adev->mman.drv_vram_usage_va; + void *fw_va = adev->mman.resv_region[AMDGPU_RESV_FW_VRAM_USAGE].cpu_ptr; + void *drv_va = adev->mman.resv_region[AMDGPU_RESV_DRV_VRAM_USAGE].cpu_ptr; + void *vram_usage_va = fw_va ? fw_va : drv_va; memset(&bp, 0, sizeof(bp)); @@ -710,15 +707,17 @@ void amdgpu_virt_fini_data_exchange(struct amdgpu_device *adev) void amdgpu_virt_init_data_exchange(struct amdgpu_device *adev) { uint32_t *pfvf_data = NULL; + void *fw_va = adev->mman.resv_region[AMDGPU_RESV_FW_VRAM_USAGE].cpu_ptr; + void *drv_va = adev->mman.resv_region[AMDGPU_RESV_DRV_VRAM_USAGE].cpu_ptr; adev->virt.fw_reserve.p_pf2vf = NULL; adev->virt.fw_reserve.p_vf2pf = NULL; adev->virt.vf2pf_update_interval_ms = 0; adev->virt.vf2pf_update_retry_cnt = 0; - if (adev->mman.fw_vram_usage_va && adev->mman.drv_vram_usage_va) { + if (fw_va && drv_va) { dev_warn(adev->dev, "Currently fw_vram and drv_vram should not have values at the same time!"); - } else if (adev->mman.fw_vram_usage_va || adev->mman.drv_vram_usage_va) { + } else if (fw_va || drv_va) { /* go through this logic in ip_init and reset to init workqueue*/ amdgpu_virt_exchange_data(adev); @@ -763,41 +762,43 @@ void amdgpu_virt_exchange_data(struct amdgpu_device *adev) uint64_t bp_block_offset = 0; uint32_t bp_block_size = 0; struct amd_sriov_msg_pf2vf_info *pf2vf_v2 = NULL; + void *fw_va = adev->mman.resv_region[AMDGPU_RESV_FW_VRAM_USAGE].cpu_ptr; + void *drv_va = adev->mman.resv_region[AMDGPU_RESV_DRV_VRAM_USAGE].cpu_ptr; - if (adev->mman.fw_vram_usage_va || adev->mman.drv_vram_usage_va) { - if (adev->mman.fw_vram_usage_va) { + if (fw_va || drv_va) { + if (fw_va) { if (adev->virt.req_init_data_ver == GPU_CRIT_REGION_V2) { adev->virt.fw_reserve.p_pf2vf = (struct amd_sriov_msg_pf2vf_info_header *) - (adev->mman.fw_vram_usage_va + + (fw_va + adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_DATAEXCHANGE_TABLE_ID].offset); adev->virt.fw_reserve.p_vf2pf = (struct amd_sriov_msg_vf2pf_info_header *) - (adev->mman.fw_vram_usage_va + + (fw_va + adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_DATAEXCHANGE_TABLE_ID].offset + (AMD_SRIOV_MSG_SIZE_KB << 10)); adev->virt.fw_reserve.ras_telemetry = - (adev->mman.fw_vram_usage_va + + (fw_va + adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_RAS_TELEMETRY_TABLE_ID].offset); } else { adev->virt.fw_reserve.p_pf2vf = (struct amd_sriov_msg_pf2vf_info_header *) - (adev->mman.fw_vram_usage_va + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB_V1 << 10)); + (fw_va + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB_V1 << 10)); adev->virt.fw_reserve.p_vf2pf = (struct amd_sriov_msg_vf2pf_info_header *) - (adev->mman.fw_vram_usage_va + (AMD_SRIOV_MSG_VF2PF_OFFSET_KB_V1 << 10)); + (fw_va + (AMD_SRIOV_MSG_VF2PF_OFFSET_KB_V1 << 10)); adev->virt.fw_reserve.ras_telemetry = - (adev->mman.fw_vram_usage_va + (AMD_SRIOV_MSG_RAS_TELEMETRY_OFFSET_KB_V1 << 10)); + (fw_va + (AMD_SRIOV_MSG_RAS_TELEMETRY_OFFSET_KB_V1 << 10)); } - } else if (adev->mman.drv_vram_usage_va) { + } else if (drv_va) { adev->virt.fw_reserve.p_pf2vf = (struct amd_sriov_msg_pf2vf_info_header *) - (adev->mman.drv_vram_usage_va + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB_V1 << 10)); + (drv_va + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB_V1 << 10)); adev->virt.fw_reserve.p_vf2pf = (struct amd_sriov_msg_vf2pf_info_header *) - (adev->mman.drv_vram_usage_va + (AMD_SRIOV_MSG_VF2PF_OFFSET_KB_V1 << 10)); + (drv_va + (AMD_SRIOV_MSG_VF2PF_OFFSET_KB_V1 << 10)); adev->virt.fw_reserve.ras_telemetry = - (adev->mman.drv_vram_usage_va + (AMD_SRIOV_MSG_RAS_TELEMETRY_OFFSET_KB_V1 << 10)); + (drv_va + (AMD_SRIOV_MSG_RAS_TELEMETRY_OFFSET_KB_V1 << 10)); } amdgpu_virt_read_pf2vf_data(adev); @@ -1081,13 +1082,14 @@ int amdgpu_virt_init_critical_region(struct amdgpu_device *adev) } /* reserved memory starts from crit region base offset with the size of 5MB */ - adev->mman.fw_vram_usage_start_offset = adev->virt.crit_regn.offset; - adev->mman.fw_vram_usage_size = adev->virt.crit_regn.size_kb << 10; + amdgpu_ttm_init_vram_resv(adev, AMDGPU_RESV_FW_VRAM_USAGE, + adev->virt.crit_regn.offset, + adev->virt.crit_regn.size_kb << 10, true); dev_info(adev->dev, "critical region v%d requested to reserve memory start at %08llx with %llu KB.\n", init_data_hdr->version, - adev->mman.fw_vram_usage_start_offset, - adev->mman.fw_vram_usage_size >> 10); + adev->mman.resv_region[AMDGPU_RESV_FW_VRAM_USAGE].offset, + adev->mman.resv_region[AMDGPU_RESV_FW_VRAM_USAGE].size >> 10); adev->virt.is_dynamic_crit_regn_enabled = true; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 73abac6be5b3..9ba9de16a27a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1978,7 +1978,6 @@ int amdgpu_vm_bo_unmap(struct amdgpu_device *adev, struct amdgpu_bo_va_mapping *mapping; struct amdgpu_vm *vm = bo_va->base.vm; bool valid = true; - int r; saddr /= AMDGPU_GPU_PAGE_SIZE; @@ -2003,12 +2002,8 @@ int amdgpu_vm_bo_unmap(struct amdgpu_device *adev, * during user requests GEM unmap IOCTL except for forcing the unmap * from user space. */ - if (unlikely(atomic_read(&bo_va->userq_va_mapped) > 0)) { - r = amdgpu_userq_gem_va_unmap_validate(adev, mapping, saddr); - if (unlikely(r == -EBUSY)) - dev_warn_once(adev->dev, - "Attempt to unmap an active userq buffer\n"); - } + if (unlikely(atomic_read(&bo_va->userq_va_mapped) > 0)) + amdgpu_userq_gem_va_unmap_validate(adev, mapping, saddr); list_del(&mapping->list); amdgpu_vm_it_remove(mapping, &vm->va); @@ -2954,6 +2949,50 @@ int amdgpu_vm_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return 0; } +/** + * amdgpu_vm_lock_by_pasid - return an amdgpu_vm and its root bo from a pasid, if possible. + * @adev: amdgpu device pointer + * @root: root BO of the VM + * @pasid: PASID of the VM + * The caller needs to unreserve and unref the root bo on success. + */ +struct amdgpu_vm *amdgpu_vm_lock_by_pasid(struct amdgpu_device *adev, + struct amdgpu_bo **root, u32 pasid) +{ + unsigned long irqflags; + struct amdgpu_vm *vm; + int r; + + xa_lock_irqsave(&adev->vm_manager.pasids, irqflags); + vm = xa_load(&adev->vm_manager.pasids, pasid); + *root = vm ? amdgpu_bo_ref(vm->root.bo) : NULL; + xa_unlock_irqrestore(&adev->vm_manager.pasids, irqflags); + + if (!*root) + return NULL; + + r = amdgpu_bo_reserve(*root, true); + if (r) + goto error_unref; + + /* Double check that the VM still exists */ + xa_lock_irqsave(&adev->vm_manager.pasids, irqflags); + vm = xa_load(&adev->vm_manager.pasids, pasid); + if (vm && vm->root.bo != *root) + vm = NULL; + xa_unlock_irqrestore(&adev->vm_manager.pasids, irqflags); + if (!vm) + goto error_unlock; + + return vm; +error_unlock: + amdgpu_bo_unreserve(*root); + +error_unref: + amdgpu_bo_unref(root); + return NULL; +} + /** * amdgpu_vm_handle_fault - graceful handling of VM faults. * @adev: amdgpu device pointer @@ -2969,50 +3008,40 @@ int amdgpu_vm_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) * shouldn't be reported any more. */ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid, - u32 vmid, u32 node_id, uint64_t addr, uint64_t ts, - bool write_fault) + u32 vmid, u32 node_id, uint64_t addr, + uint64_t ts, bool write_fault) { bool is_compute_context = false; struct amdgpu_bo *root; - unsigned long irqflags; uint64_t value, flags; struct amdgpu_vm *vm; int r; - xa_lock_irqsave(&adev->vm_manager.pasids, irqflags); - vm = xa_load(&adev->vm_manager.pasids, pasid); - if (vm) { - root = amdgpu_bo_ref(vm->root.bo); - is_compute_context = vm->is_compute_context; - } else { - root = NULL; - } - xa_unlock_irqrestore(&adev->vm_manager.pasids, irqflags); - - if (!root) + vm = amdgpu_vm_lock_by_pasid(adev, &root, pasid); + if (!vm) return false; - if (is_compute_context && !svm_range_restore_pages(adev, pasid, vmid, - node_id, addr >> PAGE_SHIFT, ts, write_fault)) { + is_compute_context = vm->is_compute_context; + + if (is_compute_context) { + /* Unreserve root since svm_range_restore_pages might try to reserve it. */ + /* TODO: rework svm_range_restore_pages so that this isn't necessary. */ + amdgpu_bo_unreserve(root); + + if (!svm_range_restore_pages(adev, pasid, vmid, + node_id, addr >> PAGE_SHIFT, ts, write_fault)) { + amdgpu_bo_unref(&root); + return true; + } amdgpu_bo_unref(&root); - return true; + + /* Re-acquire the VM lock, could be that the VM was freed in between. */ + vm = amdgpu_vm_lock_by_pasid(adev, &root, pasid); + if (!vm) + return false; } addr /= AMDGPU_GPU_PAGE_SIZE; - - r = amdgpu_bo_reserve(root, true); - if (r) - goto error_unref; - - /* Double check that the VM still exists */ - xa_lock_irqsave(&adev->vm_manager.pasids, irqflags); - vm = xa_load(&adev->vm_manager.pasids, pasid); - if (vm && vm->root.bo != root) - vm = NULL; - xa_unlock_irqrestore(&adev->vm_manager.pasids, irqflags); - if (!vm) - goto error_unlock; - flags = AMDGPU_PTE_VALID | AMDGPU_PTE_SNOOPED | AMDGPU_PTE_SYSTEM; @@ -3051,7 +3080,6 @@ error_unlock: if (r < 0) dev_err(adev->dev, "Can't handle page fault (%d)\n", r); -error_unref: amdgpu_bo_unref(&root); return false; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index 3b32f41c3655..d083d7aab75c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -592,6 +592,9 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid, u32 vmid, u32 node_id, uint64_t addr, uint64_t ts, bool write_fault); +struct amdgpu_vm *amdgpu_vm_lock_by_pasid(struct amdgpu_device *adev, + struct amdgpu_bo **root, u32 pasid); + void amdgpu_vm_set_task_info(struct amdgpu_vm *vm); void amdgpu_vm_move_to_lru_tail(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c index 22e2e5b47341..f078db3fef79 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c @@ -21,6 +21,8 @@ */ #include "amdgpu_vm.h" +#include "amdgpu.h" +#include "amdgpu_reset.h" #include "amdgpu_object.h" #include "amdgpu_trace.h" @@ -108,11 +110,19 @@ static int amdgpu_vm_cpu_update(struct amdgpu_vm_update_params *p, static int amdgpu_vm_cpu_commit(struct amdgpu_vm_update_params *p, struct dma_fence **fence) { + struct amdgpu_device *adev = p->adev; + if (p->needs_flush) atomic64_inc(&p->vm->tlb_seq); mb(); - amdgpu_device_flush_hdp(p->adev, NULL); + /* A reset flushed the HDP anyway, so that here can be skipped when a reset is ongoing */ + if (!down_read_trylock(&adev->reset_domain->sem)) + return 0; + + amdgpu_device_flush_hdp(adev, NULL); + up_read(&adev->reset_domain->sem); + return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c index 31a437ce9570..a930f1522f96 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c @@ -693,8 +693,11 @@ static void amdgpu_vm_pte_update_flags(struct amdgpu_vm_update_params *params, !(flags & AMDGPU_PTE_VALID) && !(flags & AMDGPU_PTE_PRT_FLAG(params->adev))) { - /* Workaround for fault priority problem on GMC9 */ - flags |= AMDGPU_PTE_EXECUTABLE; + /* Workaround for fault priority problem on GMC9 and GFX12, + * EXECUTABLE for GMC9 fault priority and init_pte_flags + * (e.g. AMDGPU_PTE_IS_PTE on GFX12) + */ + flags |= AMDGPU_PTE_EXECUTABLE | adev->gmc.init_pte_flags; } /* diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c index cc5f4e01e38f..42be8ee155dd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c @@ -181,6 +181,7 @@ int amdgpu_xcp_init(struct amdgpu_xcp_mgr *xcp_mgr, int num_xcps, int mode) } xcp_mgr->num_xcps = num_xcps; + xcp_mgr->mem_alloc_mode = AMDGPU_PARTITION_MEM_CAPPING_EVEN; amdgpu_xcp_update_partition_sched_list(adev); return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h index 8058e8f35d41..878c1c422893 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.h @@ -132,6 +132,8 @@ struct amdgpu_xcp_mgr { struct amdgpu_xcp_cfg *xcp_cfg; uint32_t supp_xcp_modes; uint32_t avail_xcp_modes; + /* used to determin KFD memory alloc mode for each partition */ + uint32_t mem_alloc_mode; }; struct amdgpu_xcp_mgr_funcs { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index ae39b9e1f7d6..d40ab1e95480 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -64,6 +64,11 @@ #define regPC_CONFIG_CNTL_1 0x194d #define regPC_CONFIG_CNTL_1_BASE_IDX 1 +#define regGOLDEN_TSC_COUNT_UPPER_smu_15_0_0 0x0030 +#define regGOLDEN_TSC_COUNT_UPPER_smu_15_0_0_BASE_IDX 1 +#define regGOLDEN_TSC_COUNT_LOWER_smu_15_0_0 0x0031 +#define regGOLDEN_TSC_COUNT_LOWER_smu_15_0_0_BASE_IDX 1 + #define regCP_GFX_MQD_CONTROL_DEFAULT 0x00000100 #define regCP_GFX_HQD_VMID_DEFAULT 0x00000000 #define regCP_GFX_HQD_QUEUE_PRIORITY_DEFAULT 0x00000000 @@ -5234,11 +5239,27 @@ static uint64_t gfx_v11_0_get_gpu_clock_counter(struct amdgpu_device *adev) amdgpu_gfx_off_ctrl(adev, true); } else { preempt_disable(); - clock_counter_hi_pre = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_UPPER); - clock_counter_lo = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_LOWER); - clock_counter_hi_after = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_UPPER); - if (clock_counter_hi_pre != clock_counter_hi_after) - clock_counter_lo = (uint64_t)RREG32_SOC15(SMUIO, 0, regGOLDEN_TSC_COUNT_LOWER); + if (amdgpu_ip_version(adev, SMUIO_HWIP, 0) < IP_VERSION(15, 0, 0)) { + clock_counter_hi_pre = (uint64_t)RREG32_SOC15(SMUIO, 0, + regGOLDEN_TSC_COUNT_UPPER); + clock_counter_lo = (uint64_t)RREG32_SOC15(SMUIO, 0, + regGOLDEN_TSC_COUNT_LOWER); + clock_counter_hi_after = (uint64_t)RREG32_SOC15(SMUIO, 0, + regGOLDEN_TSC_COUNT_UPPER); + if (clock_counter_hi_pre != clock_counter_hi_after) + clock_counter_lo = (uint64_t)RREG32_SOC15(SMUIO, 0, + regGOLDEN_TSC_COUNT_LOWER); + } else { + clock_counter_hi_pre = (uint64_t)RREG32_SOC15(SMUIO, 0, + regGOLDEN_TSC_COUNT_UPPER_smu_15_0_0); + clock_counter_lo = (uint64_t)RREG32_SOC15(SMUIO, 0, + regGOLDEN_TSC_COUNT_LOWER_smu_15_0_0); + clock_counter_hi_after = (uint64_t)RREG32_SOC15(SMUIO, 0, + regGOLDEN_TSC_COUNT_UPPER_smu_15_0_0); + if (clock_counter_hi_pre != clock_counter_hi_after) + clock_counter_lo = (uint64_t)RREG32_SOC15(SMUIO, 0, + regGOLDEN_TSC_COUNT_LOWER_smu_15_0_0); + } preempt_enable(); } clock = clock_counter_lo | (clock_counter_hi_after << 32ULL); @@ -6502,15 +6523,7 @@ static int gfx_v11_0_eop_irq(struct amdgpu_device *adev, DRM_DEBUG("IH: CP EOP\n"); if (adev->enable_mes && doorbell_offset) { - struct amdgpu_userq_fence_driver *fence_drv = NULL; - struct xarray *xa = &adev->userq_xa; - unsigned long flags; - - xa_lock_irqsave(xa, flags); - fence_drv = xa_load(xa, doorbell_offset); - if (fence_drv) - amdgpu_userq_fence_driver_process(fence_drv); - xa_unlock_irqrestore(xa, flags); + amdgpu_userq_process_fence_irq(adev, doorbell_offset); } else { me_id = (entry->ring_id & 0x0c) >> 2; pipe_id = (entry->ring_id & 0x03) >> 0; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index a418ae609c36..c35372e21261 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -602,6 +602,13 @@ static int gfx_v12_0_init_microcode(struct amdgpu_device *adev) "amdgpu/%s_pfp.bin", ucode_prefix); if (err) goto out; + + adev->gfx.rs64_enable = amdgpu_ucode_hdr_version( + (union amdgpu_firmware_header *) + adev->gfx.pfp_fw->data, 2, 0); + if (adev->gfx.rs64_enable) + dev_dbg(adev->dev, "CP RS64 enable\n"); + amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_RS64_PFP); amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_RS64_PFP_P0_STACK); @@ -4854,15 +4861,7 @@ static int gfx_v12_0_eop_irq(struct amdgpu_device *adev, DRM_DEBUG("IH: CP EOP\n"); if (adev->enable_mes && doorbell_offset) { - struct amdgpu_userq_fence_driver *fence_drv = NULL; - struct xarray *xa = &adev->userq_xa; - unsigned long flags; - - xa_lock_irqsave(xa, flags); - fence_drv = xa_load(xa, doorbell_offset); - if (fence_drv) - amdgpu_userq_fence_driver_process(fence_drv); - xa_unlock_irqrestore(xa, flags); + amdgpu_userq_process_fence_irq(adev, doorbell_offset); } else { me_id = (entry->ring_id & 0x0c) >> 2; pipe_id = (entry->ring_id & 0x03) >> 0; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c index db49582a211f..68db1bc73bc7 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c @@ -3643,15 +3643,7 @@ static int gfx_v12_1_eop_irq(struct amdgpu_device *adev, DRM_DEBUG("IH: CP EOP\n"); if (adev->enable_mes && doorbell_offset) { - struct amdgpu_userq_fence_driver *fence_drv = NULL; - struct xarray *xa = &adev->userq_xa; - unsigned long flags; - - xa_lock_irqsave(xa, flags); - fence_drv = xa_load(xa, doorbell_offset); - if (fence_drv) - amdgpu_userq_fence_driver_process(fence_drv); - xa_unlock_irqrestore(xa, flags); + amdgpu_userq_process_fence_irq(adev, doorbell_offset); } else { me_id = (entry->ring_id & 0x0c) >> 2; pipe_id = (entry->ring_id & 0x03) >> 0; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c index 73223d97a87f..ac90d8e9d86a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c @@ -1571,6 +1571,71 @@ static void gfx_v6_0_setup_spi(struct amdgpu_device *adev) mutex_unlock(&adev->grbm_idx_mutex); } +/** + * gfx_v6_0_setup_tcc() - setup which TCCs are used + * + * @adev: amdgpu_device pointer + * + * Verify whether the current GPU has any TCCs disabled, + * which can happen when the GPU is harvested and some + * memory channels are disabled, reducing the memory bus width. + * For example, on the Radeon HD 7870 XT (Tahiti LE). + * + * If some TCCs are disabled, we need to make sure that + * the disabled TCCs are not used, and the remaining TCCs + * are used optimally. + * + * TCP_CHAN_STEER_LO/HI control which TCC is used by TCP channels. + * TCP_ADDR_CONFIG.NUM_TCC_BANKS controls how many channels are used. + * + * For optimal performance: + * - Rely on the CHAN_STEER from the golden registers table, + * only skip disabled TCCs but keep the mapping order. + * - Limit NUM_TCC_BANKS to number of active TCCs to avoid thrashing, + * which performs better than using the same TCC twice. + */ +static void gfx_v6_0_setup_tcc(struct amdgpu_device *adev) +{ + u32 i, tcc, tcp_addr_config, num_active_tcc = 0; + u64 chan_steer, patched_chan_steer = 0; + const u32 num_max_tcc = adev->gfx.config.max_texture_channel_caches; + const u32 dis_tcc_mask = + amdgpu_gfx_create_bitmask(num_max_tcc) & + (REG_GET_FIELD(RREG32(mmCGTS_TCC_DISABLE), + CGTS_TCC_DISABLE, TCC_DISABLE) | + REG_GET_FIELD(RREG32(mmCGTS_USER_TCC_DISABLE), + CGTS_USER_TCC_DISABLE, TCC_DISABLE)); + + /* When no TCC is disabled, the golden registers table already has optimal TCC setup */ + if (!dis_tcc_mask) + return; + + /* Each 4-bit nibble contains the index of a TCC used by all TCPs */ + chan_steer = RREG32(mmTCP_CHAN_STEER_LO) | ((u64)RREG32(mmTCP_CHAN_STEER_HI) << 32ull); + + /* Patch the TCP to TCC mapping to skip disabled TCCs */ + for (i = 0; i < num_max_tcc; ++i) { + tcc = (chan_steer >> (u64)(4 * i)) & 0xf; + + if (!((1 << tcc) & dis_tcc_mask)) { + /* Copy enabled TCC indices to the patched register value. */ + patched_chan_steer |= (u64)tcc << (u64)(4 * num_active_tcc); + ++num_active_tcc; + } + } + + WARN_ON(num_active_tcc != num_max_tcc - hweight32(dis_tcc_mask)); + + /* Patch number of TCCs used by TCPs */ + tcp_addr_config = REG_SET_FIELD(RREG32(mmTCP_ADDR_CONFIG), + TCP_ADDR_CONFIG, NUM_TCC_BANKS, + num_active_tcc - 1); + + WREG32(mmTCP_ADDR_CONFIG, tcp_addr_config); + WREG32(mmTCP_CHAN_STEER_HI, upper_32_bits(patched_chan_steer)); + WREG32(mmTCP_CHAN_STEER_LO, lower_32_bits(patched_chan_steer)); +} + static void gfx_v6_0_config_init(struct amdgpu_device *adev) { adev->gfx.config.double_offchip_lds_buf = 0; @@ -1729,6 +1794,7 @@ static void gfx_v6_0_constants_init(struct amdgpu_device *adev) gfx_v6_0_tiling_mode_table_init(adev); gfx_v6_0_setup_rb(adev); + gfx_v6_0_setup_tcc(adev); gfx_v6_0_setup_spi(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 95be105671ec..86c7c2a429b7 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -5660,9 +5660,6 @@ static void gfx_v9_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c index fd691b2a6e21..e1ace7d44ffd 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c @@ -860,8 +860,6 @@ static int gmc_v10_0_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; - amdgpu_gmc_get_vbios_allocations(adev); - /* Memory manager */ r = amdgpu_bo_init(adev); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c index e6db87b94eb1..94d6631ce0bc 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c @@ -834,8 +834,6 @@ static int gmc_v11_0_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; - amdgpu_gmc_get_vbios_allocations(adev); - /* Memory manager */ r = amdgpu_bo_init(adev); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c index 6e184ea069ef..e10ac9788d13 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c @@ -924,8 +924,6 @@ static int gmc_v12_0_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; - amdgpu_gmc_get_vbios_allocations(adev); - #ifdef HAVE_ACPI_DEV_GET_FIRST_MATCH_DEV if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(12, 1, 0)) { r = amdgpu_gmc_init_mem_ranges(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c index 886bf77309a5..cc272a96fcef 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c @@ -854,8 +854,6 @@ static int gmc_v6_0_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; - amdgpu_gmc_get_vbios_allocations(adev); - r = amdgpu_bo_init(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index d25fdedb0d9f..bb16ba2ef6fd 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -1034,8 +1034,6 @@ static int gmc_v7_0_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; - amdgpu_gmc_get_vbios_allocations(adev); - /* Memory manager */ r = amdgpu_bo_init(adev); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index 4910e5557a67..a59174f6bcc1 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -1149,8 +1149,6 @@ static int gmc_v8_0_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; - amdgpu_gmc_get_vbios_allocations(adev); - /* Memory manager */ r = amdgpu_bo_init(adev); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index d865059e884a..e7b78027002b 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -2010,8 +2010,6 @@ static int gmc_v9_0_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; - amdgpu_gmc_get_vbios_allocations(adev); - if (amdgpu_is_multi_aid(adev)) { r = amdgpu_gmc_init_mem_ranges(adev); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c index 9fe8d10ab270..cffb1e6bab35 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_0.c @@ -802,6 +802,7 @@ static const struct amd_ip_funcs jpeg_v2_0_ip_funcs = { static const struct amdgpu_ring_funcs jpeg_v2_0_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_JPEG, .align_mask = 0xf, + .no_user_fence = true, .get_rptr = jpeg_v2_0_dec_ring_get_rptr, .get_wptr = jpeg_v2_0_dec_ring_get_wptr, .set_wptr = jpeg_v2_0_dec_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c index 20983f126b49..13a6e24c624a 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v2_5.c @@ -693,6 +693,7 @@ static const struct amd_ip_funcs jpeg_v2_6_ip_funcs = { static const struct amdgpu_ring_funcs jpeg_v2_5_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_JPEG, .align_mask = 0xf, + .no_user_fence = true, .get_rptr = jpeg_v2_5_dec_ring_get_rptr, .get_wptr = jpeg_v2_5_dec_ring_get_wptr, .set_wptr = jpeg_v2_5_dec_ring_set_wptr, @@ -724,6 +725,7 @@ static const struct amdgpu_ring_funcs jpeg_v2_5_dec_ring_vm_funcs = { static const struct amdgpu_ring_funcs jpeg_v2_6_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_JPEG, .align_mask = 0xf, + .no_user_fence = true, .get_rptr = jpeg_v2_5_dec_ring_get_rptr, .get_wptr = jpeg_v2_5_dec_ring_get_wptr, .set_wptr = jpeg_v2_5_dec_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c index 98f5e0622bc5..d0445df39d2c 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v3_0.c @@ -594,6 +594,7 @@ static const struct amd_ip_funcs jpeg_v3_0_ip_funcs = { static const struct amdgpu_ring_funcs jpeg_v3_0_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_JPEG, .align_mask = 0xf, + .no_user_fence = true, .get_rptr = jpeg_v3_0_dec_ring_get_rptr, .get_wptr = jpeg_v3_0_dec_ring_get_wptr, .set_wptr = jpeg_v3_0_dec_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c index 0bd83820dd20..6fd4238a8471 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0.c @@ -759,6 +759,7 @@ static const struct amd_ip_funcs jpeg_v4_0_ip_funcs = { static const struct amdgpu_ring_funcs jpeg_v4_0_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_JPEG, .align_mask = 0xf, + .no_user_fence = true, .get_rptr = jpeg_v4_0_dec_ring_get_rptr, .get_wptr = jpeg_v4_0_dec_ring_get_wptr, .set_wptr = jpeg_v4_0_dec_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c index 4b4aa9553624..0c746580de11 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c @@ -736,15 +736,35 @@ static void jpeg_v4_0_3_dec_ring_set_wptr(struct amdgpu_ring *ring) */ void jpeg_v4_0_3_dec_ring_insert_start(struct amdgpu_ring *ring) { - if (!amdgpu_sriov_vf(ring->adev)) { + struct amdgpu_device *adev = ring->adev; + + if (!amdgpu_sriov_vf(adev)) { + int jpeg_inst = GET_INST(JPEG, ring->me); + uint32_t value = 0x80004000; /* default DS14 */ + amdgpu_ring_write(ring, PACKETJ(regUVD_JRBC_EXTERNAL_REG_INTERNAL_OFFSET, 0, 0, PACKETJ_TYPE0)); - amdgpu_ring_write(ring, 0x62a04); /* PCTL0_MMHUB_DEEPSLEEP_IB */ + + /* PCTL0__MMHUB_DEEPSLEEP_IB could be different on different mmhub version */ + switch (amdgpu_ip_version(adev, MMHUB_HWIP, 0)) { + case IP_VERSION(4, 1, 0): + amdgpu_ring_write(ring, 0x69004); + value = 0x80010000; + break; + case IP_VERSION(4, 2, 0): + amdgpu_ring_write(ring, 0x60804); + if (jpeg_inst & 1) + value = 0x80010000; + break; + default: + amdgpu_ring_write(ring, 0x62a04); + break; + } amdgpu_ring_write(ring, PACKETJ(JRBC_DEC_EXTERNAL_REG_WRITE_ADDR, 0, 0, PACKETJ_TYPE0)); - amdgpu_ring_write(ring, 0x80004000); + amdgpu_ring_write(ring, value); } } @@ -757,15 +777,35 @@ void jpeg_v4_0_3_dec_ring_insert_start(struct amdgpu_ring *ring) */ void jpeg_v4_0_3_dec_ring_insert_end(struct amdgpu_ring *ring) { - if (!amdgpu_sriov_vf(ring->adev)) { + struct amdgpu_device *adev = ring->adev; + + if (!amdgpu_sriov_vf(adev)) { + int jpeg_inst = GET_INST(JPEG, ring->me); + uint32_t value = 0x00004000; /* default DS14 */ + amdgpu_ring_write(ring, PACKETJ(regUVD_JRBC_EXTERNAL_REG_INTERNAL_OFFSET, 0, 0, PACKETJ_TYPE0)); - amdgpu_ring_write(ring, 0x62a04); + + /* PCTL0__MMHUB_DEEPSLEEP_IB could be different on different mmhub version */ + switch (amdgpu_ip_version(adev, MMHUB_HWIP, 0)) { + case IP_VERSION(4, 1, 0): + amdgpu_ring_write(ring, 0x69004); + value = 0x00010000; + break; + case IP_VERSION(4, 2, 0): + amdgpu_ring_write(ring, 0x60804); + if (jpeg_inst & 1) + value = 0x00010000; + break; + default: + amdgpu_ring_write(ring, 0x62a04); + break; + } amdgpu_ring_write(ring, PACKETJ(JRBC_DEC_EXTERNAL_REG_WRITE_ADDR, 0, 0, PACKETJ_TYPE0)); - amdgpu_ring_write(ring, 0x00004000); + amdgpu_ring_write(ring, value); } } @@ -1179,6 +1219,7 @@ static const struct amd_ip_funcs jpeg_v4_0_3_ip_funcs = { static const struct amdgpu_ring_funcs jpeg_v4_0_3_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_JPEG, .align_mask = 0xf, + .no_user_fence = true, .get_rptr = jpeg_v4_0_3_dec_ring_get_rptr, .get_wptr = jpeg_v4_0_3_dec_ring_get_wptr, .set_wptr = jpeg_v4_0_3_dec_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c index 54fd9c800c40..a43582b9c876 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c @@ -804,6 +804,7 @@ static const struct amd_ip_funcs jpeg_v4_0_5_ip_funcs = { static const struct amdgpu_ring_funcs jpeg_v4_0_5_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_JPEG, .align_mask = 0xf, + .no_user_fence = true, .get_rptr = jpeg_v4_0_5_dec_ring_get_rptr, .get_wptr = jpeg_v4_0_5_dec_ring_get_wptr, .set_wptr = jpeg_v4_0_5_dec_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c index 46bf15dce2bd..72a4b2d0676f 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c @@ -680,6 +680,7 @@ static const struct amd_ip_funcs jpeg_v5_0_0_ip_funcs = { static const struct amdgpu_ring_funcs jpeg_v5_0_0_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_JPEG, .align_mask = 0xf, + .no_user_fence = true, .get_rptr = jpeg_v5_0_0_dec_ring_get_rptr, .get_wptr = jpeg_v5_0_0_dec_ring_get_wptr, .set_wptr = jpeg_v5_0_0_dec_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c index edecbfe66c79..250316704dfa 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c @@ -884,6 +884,7 @@ static const struct amd_ip_funcs jpeg_v5_0_1_ip_funcs = { static const struct amdgpu_ring_funcs jpeg_v5_0_1_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_JPEG, .align_mask = 0xf, + .no_user_fence = true, .get_rptr = jpeg_v5_0_1_dec_ring_get_rptr, .get_wptr = jpeg_v5_0_1_dec_ring_get_wptr, .set_wptr = jpeg_v5_0_1_dec_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c index 285c459379c4..7a4ecea6b39a 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c @@ -703,6 +703,7 @@ static const struct amd_ip_funcs jpeg_v5_0_2_ip_funcs = { static const struct amdgpu_ring_funcs jpeg_v5_0_2_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_JPEG, .align_mask = 0xf, + .no_user_fence = true, .get_rptr = jpeg_v5_0_2_dec_ring_get_rptr, .get_wptr = jpeg_v5_0_2_dec_ring_get_wptr, .set_wptr = jpeg_v5_0_2_dec_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c index 1821dced936f..e7546816baba 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c @@ -661,6 +661,7 @@ static const struct amd_ip_funcs jpeg_v5_3_0_ip_funcs = { static const struct amdgpu_ring_funcs jpeg_v5_3_0_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_JPEG, .align_mask = 0xf, + .no_user_fence = true, .get_rptr = jpeg_v5_3_0_dec_ring_get_rptr, .get_wptr = jpeg_v5_3_0_dec_ring_get_wptr, .set_wptr = jpeg_v5_3_0_dec_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index faac21ee5739..5b4121ddc78c 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -30,34 +30,6 @@ #define AMDGPU_USERQ_PROC_CTX_SZ PAGE_SIZE #define AMDGPU_USERQ_GANG_CTX_SZ PAGE_SIZE -static int -mes_userq_map_gtt_bo_to_gart(struct amdgpu_bo *bo) -{ - int ret; - - ret = amdgpu_bo_reserve(bo, true); - if (ret) { - DRM_ERROR("Failed to reserve bo. ret %d\n", ret); - goto err_reserve_bo_failed; - } - - ret = amdgpu_ttm_alloc_gart(&bo->tbo); - if (ret) { - DRM_ERROR("Failed to bind bo to GART. ret %d\n", ret); - goto err_map_bo_gart_failed; - } - - amdgpu_bo_unreserve(bo); - bo = amdgpu_bo_ref(bo); - - return 0; - -err_map_bo_gart_failed: - amdgpu_bo_unreserve(bo); -err_reserve_bo_failed: - return ret; -} - static int mes_userq_create_wptr_mapping(struct amdgpu_device *adev, struct amdgpu_userq_mgr *uq_mgr, @@ -65,55 +37,62 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev, uint64_t wptr) { struct amdgpu_bo_va_mapping *wptr_mapping; - struct amdgpu_vm *wptr_vm; struct amdgpu_userq_obj *wptr_obj = &queue->wptr_obj; + struct amdgpu_bo *obj; + struct amdgpu_vm *vm = queue->vm; + struct drm_exec exec; int ret; - wptr_vm = queue->vm; - ret = amdgpu_bo_reserve(wptr_vm->root.bo, false); - if (ret) - return ret; - wptr &= AMDGPU_GMC_HOLE_MASK; - wptr_mapping = amdgpu_vm_bo_lookup_mapping(wptr_vm, wptr >> PAGE_SHIFT); - amdgpu_bo_unreserve(wptr_vm->root.bo); - if (!wptr_mapping) { - DRM_ERROR("Failed to lookup wptr bo\n"); - return -EINVAL; + + drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES, 2); + drm_exec_until_all_locked(&exec) { + ret = amdgpu_vm_lock_pd(vm, &exec, 1); + drm_exec_retry_on_contention(&exec); + if (unlikely(ret)) + goto fail_lock; + + wptr_mapping = amdgpu_vm_bo_lookup_mapping(vm, wptr >> PAGE_SHIFT); + if (!wptr_mapping) { + ret = -EINVAL; + goto fail_lock; + } + + obj = wptr_mapping->bo_va->base.bo; + ret = drm_exec_lock_obj(&exec, &obj->tbo.base); + drm_exec_retry_on_contention(&exec); + if (unlikely(ret)) + goto fail_lock; } - wptr_obj->obj = wptr_mapping->bo_va->base.bo; + wptr_obj->obj = amdgpu_bo_ref(wptr_mapping->bo_va->base.bo); if (wptr_obj->obj->tbo.base.size > PAGE_SIZE) { - DRM_ERROR("Requested GART mapping for wptr bo larger than one page\n"); - return -EINVAL; - } - - ret = mes_userq_map_gtt_bo_to_gart(wptr_obj->obj); - if (ret) { - DRM_ERROR("Failed to map wptr bo to GART\n"); - return ret; - } - - ret = amdgpu_bo_reserve(wptr_obj->obj, true); - if (ret) { - DRM_ERROR("Failed to reserve wptr bo\n"); - return ret; + ret = -EINVAL; + goto fail_map; } /* TODO use eviction fence instead of pinning. */ ret = amdgpu_bo_pin(wptr_obj->obj, AMDGPU_GEM_DOMAIN_GTT); if (ret) { - drm_file_err(uq_mgr->file, "[Usermode queues] Failed to pin wptr bo\n"); - goto unresv_bo; + DRM_ERROR("Failed to pin wptr bo. ret %d\n", ret); + goto fail_map; + } + + ret = amdgpu_ttm_alloc_gart(&wptr_obj->obj->tbo); + if (ret) { + DRM_ERROR("Failed to bind bo to GART. ret %d\n", ret); + goto fail_map; } queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(wptr_obj->obj); - amdgpu_bo_unreserve(wptr_obj->obj); + drm_exec_fini(&exec); return 0; -unresv_bo: - amdgpu_bo_unreserve(wptr_obj->obj); +fail_map: + amdgpu_bo_unref(&wptr_obj->obj); +fail_lock: + drm_exec_fini(&exec); return ret; } @@ -322,8 +301,14 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, goto free_mqd; } + r = amdgpu_bo_reserve(queue->vm->root.bo, false); + if (r) { + kfree(compute_mqd); + goto free_mqd; + } r = amdgpu_userq_input_va_validate(adev, queue, compute_mqd->eop_va, 2048); + amdgpu_bo_unreserve(queue->vm->root.bo); if (r) { kfree(compute_mqd); goto free_mqd; @@ -365,14 +350,22 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, userq_props->tmz_queue = mqd_user->flags & AMDGPU_USERQ_CREATE_FLAGS_QUEUE_SECURE; - r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->shadow_va, - shadow_info.shadow_size); + r = amdgpu_bo_reserve(queue->vm->root.bo, false); if (r) { kfree(mqd_gfx_v11); goto free_mqd; } + r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->shadow_va, + shadow_info.shadow_size); + if (r) { + amdgpu_bo_unreserve(queue->vm->root.bo); + kfree(mqd_gfx_v11); + goto free_mqd; + } + r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->csa_va, shadow_info.csa_size); + amdgpu_bo_unreserve(queue->vm->root.bo); if (r) { kfree(mqd_gfx_v11); goto free_mqd; @@ -394,8 +387,15 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, r = -ENOMEM; goto free_mqd; } + + r = amdgpu_bo_reserve(queue->vm->root.bo, false); + if (r) { + kfree(mqd_sdma_v11); + goto free_mqd; + } r = amdgpu_userq_input_va_validate(adev, queue, mqd_sdma_v11->csa_va, 32); + amdgpu_bo_unreserve(queue->vm->root.bo); if (r) { kfree(mqd_sdma_v11); goto free_mqd; diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c index 0e9089544769..cec801278126 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c @@ -2028,7 +2028,7 @@ static int mes_v12_1_test_ring(struct amdgpu_device *adev, int xcc_id, int num_xcc = NUM_XCC(adev->gfx.xcc_mask); int sdma_ring_align = 0x10, compute_ring_align = 0x100; uint32_t tmp, xcc_offset; - int r = 0, i, wptr = 0; + int r = 0, i, j, wptr = 0; if (queue_type == AMDGPU_RING_TYPE_COMPUTE) { if (!adev->mes.enable_coop_mode) { @@ -2077,11 +2077,11 @@ static int mes_v12_1_test_ring(struct amdgpu_device *adev, int xcc_id, tmp = RREG32_SOC15(GC, GET_INST(GC, xcc_id), regSCRATCH_REG0); } else { - for (i = 0; i < num_xcc; i++) { - if (xcc_id != adev->mes.master_xcc_ids[i]) + for (j = 0; j < num_xcc; j++) { + if (xcc_id != adev->mes.master_xcc_ids[j]) continue; - tmp = RREG32_SOC15(GC, GET_INST(GC, i), + tmp = RREG32_SOC15(GC, GET_INST(GC, j), regSCRATCH_REG0); if (tmp != 0xDEADBEEF) break; diff --git a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c index db14a1a326d2..b6f832c53860 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c +++ b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c @@ -54,6 +54,8 @@ #define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10_BASE_IDX 3 #define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10 0x4f0af6 #define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10_BASE_IDX 3 +#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10 0x0021 +#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10_BASE_IDX 2 static void nbif_v6_3_1_remap_hdp_registers(struct amdgpu_device *adev) { @@ -65,7 +67,12 @@ static void nbif_v6_3_1_remap_hdp_registers(struct amdgpu_device *adev) static u32 nbif_v6_3_1_get_rev_id(struct amdgpu_device *adev) { - u32 tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0); + u32 tmp; + + if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 4)) + tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10); + else + tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0); tmp &= RCC_STRAP0_RCC_DEV0_EPF0_STRAP0__STRAP_ATI_REV_ID_DEV0_F0_MASK; tmp >>= RCC_STRAP0_RCC_DEV0_EPF0_STRAP0__STRAP_ATI_REV_ID_DEV0_F0__SHIFT; diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c index 73a709773e85..2a8582e87f2b 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c @@ -32,6 +32,7 @@ #include "mp/mp_15_0_0_sh_mask.h" MODULE_FIRMWARE("amdgpu/psp_15_0_0_toc.bin"); +MODULE_FIRMWARE("amdgpu/psp_15_0_0_ta.bin"); static int psp_v15_0_0_init_microcode(struct psp_context *psp) { diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c index 44f0f23e1148..e64f2f6df9a9 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c @@ -889,7 +889,7 @@ static void sdma_v4_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se /* write the fence */ amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -899,7 +899,7 @@ static void sdma_v4_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se addr += 4; amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c index b005672f2f96..8ca46e1e474e 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c @@ -1662,17 +1662,8 @@ static int sdma_v6_0_process_fence_irq(struct amdgpu_device *adev, u32 doorbell_offset = entry->src_data[0]; if (adev->enable_mes && doorbell_offset) { - struct amdgpu_userq_fence_driver *fence_drv = NULL; - struct xarray *xa = &adev->userq_xa; - unsigned long flags; - doorbell_offset >>= SDMA0_QUEUE0_DOORBELL_OFFSET__OFFSET__SHIFT; - - xa_lock_irqsave(xa, flags); - fence_drv = xa_load(xa, doorbell_offset); - if (fence_drv) - amdgpu_userq_fence_driver_process(fence_drv); - xa_unlock_irqrestore(xa, flags); + amdgpu_userq_process_fence_irq(adev, doorbell_offset); } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c index 5679a94d0815..37191e2918d4 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c @@ -1594,17 +1594,8 @@ static int sdma_v7_0_process_fence_irq(struct amdgpu_device *adev, u32 doorbell_offset = entry->src_data[0]; if (adev->enable_mes && doorbell_offset) { - struct amdgpu_userq_fence_driver *fence_drv = NULL; - struct xarray *xa = &adev->userq_xa; - unsigned long flags; - doorbell_offset >>= SDMA0_QUEUE0_DOORBELL_OFFSET__OFFSET__SHIFT; - - xa_lock_irqsave(xa, flags); - fence_drv = xa_load(xa, doorbell_offset); - if (fence_drv) - amdgpu_userq_fence_driver_process(fence_drv); - xa_unlock_irqrestore(xa, flags); + amdgpu_userq_process_fence_irq(adev, doorbell_offset); } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c index f20e0fc3fc74..061934a2e93a 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c @@ -1268,6 +1268,18 @@ static int sdma_v7_1_early_init(struct amdgpu_ip_block *ip_block) struct amdgpu_device *adev = ip_block->adev; int r; + switch (amdgpu_user_queue) { + case -1: + default: + adev->sdma.no_user_submission = true; + adev->sdma.disable_uq = true; + break; + case 0: + adev->sdma.no_user_submission = false; + adev->sdma.disable_uq = true; + break; + } + r = amdgpu_sdma_init_microcode(adev, 0, true); if (r) { DRM_ERROR("Failed to init sdma firmware!\n"); diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c b/drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c index fea576a7f397..efb3fde919ee 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v3_1.c @@ -242,6 +242,10 @@ static void uvd_v3_1_mc_resume(struct amdgpu_device *adev) uint64_t addr; uint32_t size; + /* When the keyselect is already set, don't perturb it. */ + if (RREG32(mmUVD_FW_START)) + return; + /* program the VCPU memory controller bits 0-27 */ addr = (adev->uvd.inst->gpu_addr + AMDGPU_UVD_FIRMWARE_OFFSET) >> 3; size = AMDGPU_UVD_FIRMWARE_SIZE(adev) >> 3; @@ -284,6 +288,12 @@ static int uvd_v3_1_fw_validate(struct amdgpu_device *adev) int i; uint32_t keysel = adev->uvd.keyselect; + if (RREG32(mmUVD_FW_START) & UVD_FW_STATUS__PASS_MASK) { + dev_dbg(adev->dev, "UVD keyselect already set: 0x%x (on CPU: 0x%x)\n", + RREG32(mmUVD_FW_START), adev->uvd.keyselect); + return 0; + } + WREG32(mmUVD_FW_START, keysel); for (i = 0; i < 10; ++i) { diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c index e35fae9cdaf6..0442bfcfd384 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c @@ -2113,6 +2113,7 @@ static const struct amd_ip_funcs vcn_v2_0_ip_funcs = { static const struct amdgpu_ring_funcs vcn_v2_0_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_DEC, .align_mask = 0xf, + .no_user_fence = true, .secure_submission_supported = true, .get_rptr = vcn_v2_0_dec_ring_get_rptr, .get_wptr = vcn_v2_0_dec_ring_get_wptr, @@ -2145,6 +2146,7 @@ static const struct amdgpu_ring_funcs vcn_v2_0_enc_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_ENC, .align_mask = 0x3f, .nop = VCN_ENC_CMD_NO_OP, + .no_user_fence = true, .get_rptr = vcn_v2_0_enc_ring_get_rptr, .get_wptr = vcn_v2_0_enc_ring_get_wptr, .set_wptr = vcn_v2_0_enc_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c b/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c index 006a15451197..8b8184fe6764 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c @@ -1778,6 +1778,7 @@ static void vcn_v2_5_dec_ring_set_wptr(struct amdgpu_ring *ring) static const struct amdgpu_ring_funcs vcn_v2_5_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_DEC, .align_mask = 0xf, + .no_user_fence = true, .secure_submission_supported = true, .get_rptr = vcn_v2_5_dec_ring_get_rptr, .get_wptr = vcn_v2_5_dec_ring_get_wptr, @@ -1879,6 +1880,7 @@ static const struct amdgpu_ring_funcs vcn_v2_5_enc_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_ENC, .align_mask = 0x3f, .nop = VCN_ENC_CMD_NO_OP, + .no_user_fence = true, .get_rptr = vcn_v2_5_enc_ring_get_rptr, .get_wptr = vcn_v2_5_enc_ring_get_wptr, .set_wptr = vcn_v2_5_enc_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c index 02d5c5af65f2..81bba3ec2a93 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c @@ -1856,6 +1856,7 @@ static const struct amdgpu_ring_funcs vcn_v3_0_dec_sw_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_DEC, .align_mask = 0x3f, .nop = VCN_DEC_SW_CMD_NO_OP, + .no_user_fence = true, .secure_submission_supported = true, .get_rptr = vcn_v3_0_dec_ring_get_rptr, .get_wptr = vcn_v3_0_dec_ring_get_wptr, @@ -1909,7 +1910,7 @@ static int vcn_v3_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, struct ttm_operation_ctx ctx = { false, false }; struct amdgpu_device *adev = p->adev; struct amdgpu_bo_va_mapping *map; - uint32_t *msg, num_buffers; + uint32_t *msg, num_buffers, len_dw; struct amdgpu_bo *bo; uint64_t start, end; unsigned int i; @@ -1930,6 +1931,11 @@ static int vcn_v3_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, return -EINVAL; } + if (end - addr < 16) { + DRM_ERROR("VCN messages must be at least 4 DWORDs!\n"); + return -EINVAL; + } + bo->flags |= AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED; amdgpu_bo_placement_from_domain(bo, bo->allowed_domains); r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); @@ -1946,8 +1952,8 @@ static int vcn_v3_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, msg = ptr + addr - start; - /* Check length */ if (msg[1] > end - addr) { + DRM_ERROR("VCN message header does not fit in BO!\n"); r = -EINVAL; goto out; } @@ -1955,9 +1961,19 @@ static int vcn_v3_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, if (msg[3] != RDECODE_MSG_CREATE) goto out; + len_dw = msg[1] / 4; num_buffers = msg[2]; + + /* Verify that all indices fit within the claimed length. Each index is 4 DWORDs */ + if (num_buffers > len_dw || 6 + num_buffers * 4 > len_dw) { + DRM_ERROR("VCN message has too many buffers!\n"); + r = -EINVAL; + goto out; + } + for (i = 0, msg = &msg[6]; i < num_buffers; ++i, msg += 4) { uint32_t offset, size, *create; + uint64_t buf_end; if (msg[0] != RDECODE_MESSAGE_CREATE) continue; @@ -1965,14 +1981,16 @@ static int vcn_v3_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, offset = msg[1]; size = msg[2]; - if (offset + size > end) { + if (size < 4 || check_add_overflow(offset, size, &buf_end) || + buf_end > end - addr) { + DRM_ERROR("VCN message buffer exceeds BO bounds!\n"); r = -EINVAL; goto out; } create = ptr + addr + offset - start; - /* H246, HEVC and VP9 can run on any instance */ + /* H264, HEVC and VP9 can run on any instance */ if (create[0] == 0x7 || create[0] == 0x10 || create[0] == 0x11) continue; @@ -2021,6 +2039,7 @@ static int vcn_v3_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p, static const struct amdgpu_ring_funcs vcn_v3_0_dec_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_DEC, .align_mask = 0xf, + .no_user_fence = true, .secure_submission_supported = true, .get_rptr = vcn_v3_0_dec_ring_get_rptr, .get_wptr = vcn_v3_0_dec_ring_get_wptr, @@ -2123,6 +2142,7 @@ static const struct amdgpu_ring_funcs vcn_v3_0_enc_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_ENC, .align_mask = 0x3f, .nop = VCN_ENC_CMD_NO_OP, + .no_user_fence = true, .get_rptr = vcn_v3_0_enc_ring_get_rptr, .get_wptr = vcn_v3_0_enc_ring_get_wptr, .set_wptr = vcn_v3_0_enc_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c index d17219be50f3..ff7269bafae8 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c @@ -1826,7 +1826,7 @@ static int vcn_v4_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, struct ttm_operation_ctx ctx = { false, false }; struct amdgpu_device *adev = p->adev; struct amdgpu_bo_va_mapping *map; - uint32_t *msg, num_buffers; + uint32_t *msg, num_buffers, len_dw; struct amdgpu_bo *bo; uint64_t start, end; unsigned int i; @@ -1847,6 +1847,11 @@ static int vcn_v4_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, return -EINVAL; } + if (end - addr < 16) { + DRM_ERROR("VCN messages must be at least 4 DWORDs!\n"); + return -EINVAL; + } + bo->flags |= AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED; amdgpu_bo_placement_from_domain(bo, bo->allowed_domains); r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); @@ -1863,8 +1868,8 @@ static int vcn_v4_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, msg = ptr + addr - start; - /* Check length */ if (msg[1] > end - addr) { + DRM_ERROR("VCN message header does not fit in BO!\n"); r = -EINVAL; goto out; } @@ -1872,9 +1877,19 @@ static int vcn_v4_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, if (msg[3] != RDECODE_MSG_CREATE) goto out; + len_dw = msg[1] / 4; num_buffers = msg[2]; + + /* Verify that all indices fit within the claimed length. Each index is 4 DWORDs */ + if (num_buffers > len_dw || 6 + num_buffers * 4 > len_dw) { + DRM_ERROR("VCN message has too many buffers!\n"); + r = -EINVAL; + goto out; + } + for (i = 0, msg = &msg[6]; i < num_buffers; ++i, msg += 4) { uint32_t offset, size, *create; + uint64_t buf_end; if (msg[0] != RDECODE_MESSAGE_CREATE) continue; @@ -1882,7 +1897,9 @@ static int vcn_v4_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, offset = msg[1]; size = msg[2]; - if (offset + size > end) { + if (size < 4 || check_add_overflow(offset, size, &buf_end) || + buf_end > end - addr) { + DRM_ERROR("VCN message buffer exceeds BO bounds!\n"); r = -EINVAL; goto out; } @@ -1913,9 +1930,10 @@ out: static int vcn_v4_0_enc_find_ib_param(struct amdgpu_ib *ib, uint32_t id, int start) { int i; + uint32_t len; - for (i = start; i < ib->length_dw && ib->ptr[i] >= 8; i += ib->ptr[i] / 4) { - if (ib->ptr[i + 1] == id) + for (i = start; (len = amdgpu_ib_get_value(ib, i)) >= 8; i += len / 4) { + if (amdgpu_ib_get_value(ib, i + 1) == id) return i; } return -1; @@ -1926,8 +1944,6 @@ static int vcn_v4_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p, struct amdgpu_ib *ib) { struct amdgpu_ring *ring = amdgpu_job_ring(job); - struct amdgpu_vcn_decode_buffer *decode_buffer; - uint64_t addr; uint32_t val; int idx = 0, sidx; @@ -1938,20 +1954,22 @@ static int vcn_v4_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p, while ((idx = vcn_v4_0_enc_find_ib_param(ib, RADEON_VCN_ENGINE_INFO, idx)) >= 0) { val = amdgpu_ib_get_value(ib, idx + 2); /* RADEON_VCN_ENGINE_TYPE */ if (val == RADEON_VCN_ENGINE_TYPE_DECODE) { - decode_buffer = (struct amdgpu_vcn_decode_buffer *)&ib->ptr[idx + 6]; + uint32_t valid_buf_flag = amdgpu_ib_get_value(ib, idx + 6); + uint64_t msg_buffer_addr; - if (!(decode_buffer->valid_buf_flag & 0x1)) + if (!(valid_buf_flag & 0x1)) return 0; - addr = ((u64)decode_buffer->msg_buffer_address_hi) << 32 | - decode_buffer->msg_buffer_address_lo; - return vcn_v4_0_dec_msg(p, job, addr); + msg_buffer_addr = ((u64)amdgpu_ib_get_value(ib, idx + 7)) << 32 | + amdgpu_ib_get_value(ib, idx + 8); + return vcn_v4_0_dec_msg(p, job, msg_buffer_addr); } else if (val == RADEON_VCN_ENGINE_TYPE_ENCODE) { sidx = vcn_v4_0_enc_find_ib_param(ib, RENCODE_IB_PARAM_SESSION_INIT, idx); - if (sidx >= 0 && ib->ptr[sidx + 2] == RENCODE_ENCODE_STANDARD_AV1) + if (sidx >= 0 && + amdgpu_ib_get_value(ib, sidx + 2) == RENCODE_ENCODE_STANDARD_AV1) return vcn_v4_0_limit_sched(p, job); } - idx += ib->ptr[idx] / 4; + idx += amdgpu_ib_get_value(ib, idx) / 4; } return 0; } @@ -1978,6 +1996,7 @@ static struct amdgpu_ring_funcs vcn_v4_0_unified_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_ENC, .align_mask = 0x3f, .nop = VCN_ENC_CMD_NO_OP, + .no_user_fence = true, .extra_bytes = sizeof(struct amdgpu_vcn_rb_metadata), .get_rptr = vcn_v4_0_unified_ring_get_rptr, .get_wptr = vcn_v4_0_unified_ring_get_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c index ff3013b97abd..10e8fc2821f3 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c @@ -1775,6 +1775,7 @@ static const struct amdgpu_ring_funcs vcn_v4_0_3_unified_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_ENC, .align_mask = 0x3f, .nop = VCN_ENC_CMD_NO_OP, + .no_user_fence = true, .get_rptr = vcn_v4_0_3_unified_ring_get_rptr, .get_wptr = vcn_v4_0_3_unified_ring_get_wptr, .set_wptr = vcn_v4_0_3_unified_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c index 1f6a22983c0d..1571cc5a148c 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c @@ -1483,6 +1483,7 @@ static struct amdgpu_ring_funcs vcn_v4_0_5_unified_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_ENC, .align_mask = 0x3f, .nop = VCN_ENC_CMD_NO_OP, + .no_user_fence = true, .get_rptr = vcn_v4_0_5_unified_ring_get_rptr, .get_wptr = vcn_v4_0_5_unified_ring_get_wptr, .set_wptr = vcn_v4_0_5_unified_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c index 6109124f852e..d5f49fa33bee 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c @@ -1207,6 +1207,7 @@ static const struct amdgpu_ring_funcs vcn_v5_0_0_unified_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_ENC, .align_mask = 0x3f, .nop = VCN_ENC_CMD_NO_OP, + .no_user_fence = true, .get_rptr = vcn_v5_0_0_unified_ring_get_rptr, .get_wptr = vcn_v5_0_0_unified_ring_get_wptr, .set_wptr = vcn_v5_0_0_unified_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c index c28c6aff17aa..54fbf8d73ca6 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c @@ -1419,6 +1419,7 @@ static const struct amdgpu_ring_funcs vcn_v5_0_1_unified_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_ENC, .align_mask = 0x3f, .nop = VCN_ENC_CMD_NO_OP, + .no_user_fence = true, .get_rptr = vcn_v5_0_1_unified_ring_get_rptr, .get_wptr = vcn_v5_0_1_unified_ring_get_wptr, .set_wptr = vcn_v5_0_1_unified_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c index c3d3cc023058..bbc172db91a1 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c @@ -994,6 +994,7 @@ static const struct amdgpu_ring_funcs vcn_v5_0_2_unified_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_ENC, .align_mask = 0x3f, .nop = VCN_ENC_CMD_NO_OP, + .no_user_fence = true, .get_rptr = vcn_v5_0_2_unified_ring_get_rptr, .get_wptr = vcn_v5_0_2_unified_ring_get_wptr, .set_wptr = vcn_v5_0_2_unified_ring_set_wptr, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 462a32abf720..f95bf6d95534 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -776,6 +777,9 @@ static int kfd_ioctl_get_process_apertures_new(struct file *filp, goto out_unlock; } + if (args->num_of_nodes > kfd_topology_get_num_devices()) + return -EINVAL; + /* Fill in process-aperture information for all available * nodes, but not more than args->num_of_nodes as that is * the amount of memory allocated by user @@ -1356,7 +1360,7 @@ static int kfd_ioctl_map_memory_to_gpu(struct file *filep, peer_pdd = kfd_process_device_data_by_id(p, devices_arr[i]); if (WARN_ON_ONCE(!peer_pdd)) continue; - kfd_flush_tlb(peer_pdd, TLB_FLUSH_LEGACY); + kfd_flush_tlb(peer_pdd); } kfree(devices_arr); @@ -1451,7 +1455,7 @@ static int kfd_ioctl_unmap_memory_from_gpu(struct file *filep, if (WARN_ON_ONCE(!peer_pdd)) continue; if (flush_tlb) - kfd_flush_tlb(peer_pdd, TLB_FLUSH_HEAVYWEIGHT); + kfd_flush_tlb(peer_pdd); /* Remove dma mapping after tlb flush to avoid IO_PAGE_FAULT */ err = amdgpu_amdkfd_gpuvm_dmaunmap_mem(mem, peer_pdd->drm_priv); @@ -1692,6 +1696,16 @@ static int kfd_ioctl_smi_events(struct file *filep, return kfd_smi_event_open(pdd->dev, &args->anon_fd); } +static int kfd_ioctl_svm_validate(void *kdata, unsigned int usize) +{ + struct kfd_ioctl_svm_args *args = kdata; + size_t expected = struct_size(args, attrs, args->nattr); + + if (expected == SIZE_MAX || usize < expected) + return -EINVAL; + return 0; +} + #if IS_ENABLED(CONFIG_HSA_AMD_SVM) static int kfd_ioctl_set_xnack_mode(struct file *filep, @@ -3206,7 +3220,11 @@ static int kfd_ioctl_create_process(struct file *filep, struct kfd_process *p, v #define AMDKFD_IOCTL_DEF(ioctl, _func, _flags) \ [_IOC_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, \ - .cmd_drv = 0, .name = #ioctl} + .validate = NULL, .cmd_drv = 0, .name = #ioctl} + +#define AMDKFD_IOCTL_DEF_V(ioctl, _func, _validate, _flags) \ + [_IOC_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, \ + .validate = _validate, .cmd_drv = 0, .name = #ioctl} /** Ioctl table */ static const struct amdkfd_ioctl_desc amdkfd_ioctls[] = { @@ -3303,7 +3321,8 @@ static const struct amdkfd_ioctl_desc amdkfd_ioctls[] = { AMDKFD_IOCTL_DEF(AMDKFD_IOC_SMI_EVENTS, kfd_ioctl_smi_events, 0), - AMDKFD_IOCTL_DEF(AMDKFD_IOC_SVM, kfd_ioctl_svm, 0), + AMDKFD_IOCTL_DEF_V(AMDKFD_IOC_SVM, kfd_ioctl_svm, + kfd_ioctl_svm_validate, 0), AMDKFD_IOCTL_DEF(AMDKFD_IOC_SET_XNACK_MODE, kfd_ioctl_set_xnack_mode, 0), @@ -3428,6 +3447,12 @@ static long kfd_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) memset(kdata, 0, usize); } + if (ioctl->validate) { + retcode = ioctl->validate(kdata, usize); + if (retcode) + goto err_i1; + } + retcode = func(filep, process, kdata); if (cmd & IOC_OUT) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 8ff97bf7d95a..b7f8f7ff8198 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -1737,37 +1737,6 @@ bool kgd2kfd_vmfault_fast_path(struct amdgpu_device *adev, struct amdgpu_iv_entr return false; } -/* check if there is kfd process still uses adev */ -static bool kgd2kfd_check_device_idle(struct amdgpu_device *adev) -{ - struct kfd_process *p; - struct hlist_node *p_temp; - unsigned int temp; - struct kfd_node *dev; - - mutex_lock(&kfd_processes_mutex); - - if (hash_empty(kfd_processes_table)) { - mutex_unlock(&kfd_processes_mutex); - return true; - } - - /* check if there is device still use adev */ - hash_for_each_safe(kfd_processes_table, temp, p_temp, p, kfd_processes) { - for (int i = 0; i < p->n_pdds; i++) { - dev = p->pdds[i]->dev; - if (dev->adev == adev) { - mutex_unlock(&kfd_processes_mutex); - return false; - } - } - } - - mutex_unlock(&kfd_processes_mutex); - - return true; -} - /** kgd2kfd_teardown_processes - gracefully tear down existing * kfd processes that use adev * @@ -1800,7 +1769,7 @@ void kgd2kfd_teardown_processes(struct amdgpu_device *adev) mutex_unlock(&kfd_processes_mutex); /* wait all kfd processes use adev terminate */ - while (!kgd2kfd_check_device_idle(adev)) + while (!!atomic_read(&adev->kfd.dev->kfd_processes_count)) cond_resched(); } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index ab3b2e7be9bd..9185ebe4c079 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -572,7 +572,7 @@ static int allocate_vmid(struct device_queue_manager *dqm, qpd->vmid, qpd->page_table_base); /* invalidate the VM context after pasid and vmid mapping is set up */ - kfd_flush_tlb(qpd_to_pdd(qpd), TLB_FLUSH_LEGACY); + kfd_flush_tlb(qpd_to_pdd(qpd)); if (dqm->dev->kfd2kgd->set_scratch_backing_va) dqm->dev->kfd2kgd->set_scratch_backing_va(dqm->dev->adev, @@ -610,7 +610,7 @@ static void deallocate_vmid(struct device_queue_manager *dqm, if (flush_texture_cache_nocpsch(q->device, qpd)) dev_err(dev, "Failed to flush TC\n"); - kfd_flush_tlb(qpd_to_pdd(qpd), TLB_FLUSH_LEGACY); + kfd_flush_tlb(qpd_to_pdd(qpd)); /* Release the vmid mapping */ set_pasid_vmid_mapping(dqm, 0, qpd->vmid); @@ -1284,7 +1284,7 @@ static int restore_process_queues_nocpsch(struct device_queue_manager *dqm, dqm->dev->adev, qpd->vmid, qpd->page_table_base); - kfd_flush_tlb(pdd, TLB_FLUSH_LEGACY); + kfd_flush_tlb(pdd); } /* Take a safe reference to the mm_struct, which may otherwise diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index fa025bea9b4f..7b5b12206919 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -1047,10 +1047,13 @@ extern struct srcu_struct kfd_processes_srcu; typedef int amdkfd_ioctl_t(struct file *filep, struct kfd_process *p, void *data); +typedef int amdkfd_ioctl_validate_t(void *kdata, unsigned int usize); + struct amdkfd_ioctl_desc { unsigned int cmd; int flags; amdkfd_ioctl_t *func; + amdkfd_ioctl_validate_t *validate; unsigned int cmd_drv; const char *name; }; @@ -1191,6 +1194,7 @@ static inline struct kfd_node *kfd_node_by_irq_ids(struct amdgpu_device *adev, return NULL; } int kfd_topology_enum_kfd_devices(uint8_t idx, struct kfd_node **kdev); +uint32_t kfd_topology_get_num_devices(void); int kfd_numa_node_to_apic_id(int numa_node_id); uint32_t kfd_gpu_node_num(void); @@ -1550,13 +1554,13 @@ void kfd_signal_reset_event(struct kfd_node *dev); void kfd_signal_poison_consumed_event(struct kfd_node *dev, u32 pasid); void kfd_signal_process_terminate_event(struct kfd_process *p); -static inline void kfd_flush_tlb(struct kfd_process_device *pdd, - enum TLB_FLUSH_TYPE type) +static inline void kfd_flush_tlb(struct kfd_process_device *pdd) { struct amdgpu_device *adev = pdd->dev->adev; struct amdgpu_vm *vm = drm_priv_to_vm(pdd->drm_priv); - amdgpu_vm_flush_compute_tlb(adev, vm, type, pdd->dev->xcc_mask); + amdgpu_vm_flush_compute_tlb(adev, vm, TLB_FLUSH_HEAVYWEIGHT, + pdd->dev->xcc_mask); } static inline bool kfd_flush_tlb_after_unmap(struct kfd_dev *dev) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index bcd21204aa50..d28ca581cad0 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -689,7 +689,8 @@ void kfd_procfs_del_queue(struct queue *q) int kfd_process_create_wq(void) { if (!kfd_process_wq) - kfd_process_wq = alloc_workqueue("kfd_process_wq", 0, 0); + kfd_process_wq = alloc_workqueue("kfd_process_wq", WQ_UNBOUND, + 0); if (!kfd_restore_wq) kfd_restore_wq = alloc_ordered_workqueue("kfd_restore_wq", WQ_FREEZABLE); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index b120fdb0ef77..35ec67d9739b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -1366,6 +1366,12 @@ svm_range_unmap_from_gpu(struct amdgpu_device *adev, struct amdgpu_vm *vm, pr_debug("CPU[0x%llx 0x%llx] -> GPU[0x%llx 0x%llx]\n", start, last, gpu_start, gpu_end); + + if (!amdgpu_vm_ready(vm)) { + pr_debug("VM not ready, canceling unmap\n"); + return -EINVAL; + } + return amdgpu_vm_update_range(adev, vm, false, true, true, false, NULL, gpu_start, gpu_end, init_pte_value, 0, 0, NULL, NULL, fence); @@ -1418,7 +1424,7 @@ svm_range_unmap_from_gpus(struct svm_range *prange, unsigned long start, if (r) break; } - kfd_flush_tlb(pdd, TLB_FLUSH_HEAVYWEIGHT); + kfd_flush_tlb(pdd); } return r; @@ -1443,6 +1449,11 @@ svm_range_map_to_gpu(struct kfd_process_device *pdd, struct svm_range *prange, pr_debug("svms 0x%p [0x%lx 0x%lx] readonly %d\n", prange->svms, last_start, last_start + npages - 1, readonly); + if (!amdgpu_vm_ready(vm)) { + pr_debug("VM not ready, canceling map\n"); + return -EINVAL; + } + for (i = offset; i < offset + npages; i++) { uint64_t gpu_start; uint64_t gpu_end; @@ -1560,7 +1571,7 @@ svm_range_map_to_gpus(struct svm_range *prange, unsigned long offset, } } - kfd_flush_tlb(pdd, TLB_FLUSH_LEGACY); + kfd_flush_tlb(pdd); } return r; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index 995f2c2528a9..29dee26261ab 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -2297,6 +2297,17 @@ int kfd_topology_remove_device(struct kfd_node *gpu) return res; } +uint32_t kfd_topology_get_num_devices(void) +{ + uint32_t num_devices; + + down_read(&topology_lock); + num_devices = sys_props.num_devices; + up_read(&topology_lock); + + return num_devices; +} + /* kfd_topology_enum_kfd_devices - Enumerate through all devices in KFD * topology. If GPU device is found @idx, then valid kfd_dev pointer is * returned through @kdev diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 21635e80349a..5fc5d5608506 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -95,6 +95,7 @@ #include #include #include +#include #include #include @@ -572,7 +573,7 @@ static void schedule_dc_vmin_vmax(struct amdgpu_device *adev, offload_work->stream = stream; offload_work->adjust = adjust_copy; - queue_work(system_wq, &offload_work->work); + queue_work(system_percpu_wq, &offload_work->work); } static void dm_vupdate_high_irq(void *interrupt_params) @@ -1902,7 +1903,11 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) goto error; } - init_data.asic_id.chip_family = adev->family; + /* special handling for early revisions of GC 11.5.4 */ + if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 4)) + init_data.asic_id.chip_family = AMDGPU_FAMILY_GC_11_5_4; + else + init_data.asic_id.chip_family = adev->family; init_data.asic_id.pci_revision_id = adev->pdev->revision; init_data.asic_id.hw_internal_rev = adev->external_rev_id; @@ -2255,6 +2260,10 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev) adev->dm.idle_workqueue = NULL; } + /* Disable ISM before dc_destroy() invalidates dm->dc */ + scoped_guard(mutex, &adev->dm.dc_lock) + amdgpu_dm_ism_disable(&adev->dm); + amdgpu_dm_destroy_drm_device(&adev->dm); #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) @@ -3833,6 +3842,66 @@ static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = { .atomic_commit_setup = amdgpu_dm_atomic_setup_commit, }; +#define DDC_MANUFACTURERNAME_SAMSUNG 0x2D4C + +static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector) +{ + struct drm_connector *connector = &aconnector->base; + struct drm_display_info *display_info = &connector->display_info; + struct dc_link *link = aconnector->dc_link; + struct amdgpu_device *adev; + + adev = drm_to_adev(connector->dev); + + link->panel_type = PANEL_TYPE_NONE; + + switch (display_info->amd_vsdb.panel_type) { + case AMD_VSDB_PANEL_TYPE_OLED: + link->panel_type = PANEL_TYPE_OLED; + break; + case AMD_VSDB_PANEL_TYPE_MINILED: + link->panel_type = PANEL_TYPE_MINILED; + break; + } + + /* If VSDB didn't determine panel type, check DPCD ext caps */ + if (link->panel_type == PANEL_TYPE_NONE) { + if (link->dpcd_sink_ext_caps.bits.miniled == 1) + link->panel_type = PANEL_TYPE_MINILED; + if (link->dpcd_sink_ext_caps.bits.oled == 1) + link->panel_type = PANEL_TYPE_OLED; + } + + /* + * TODO: get panel type from DID2 that has device technology field + * to specify if it's OLED or not. But we need to wait for DID2 + * support in DC and EDID parser to be able to use it here. + */ + + if (link->panel_type == PANEL_TYPE_NONE) { + struct drm_amd_vsdb_info *vsdb = &display_info->amd_vsdb; + u32 lum1_max = vsdb->luminance_range1.max_luminance; + u32 lum2_max = vsdb->luminance_range2.max_luminance; + + if (vsdb->version && link->local_sink && + link->local_sink->edid_caps.manufacturer_id == + DDC_MANUFACTURERNAME_SAMSUNG && + lum1_max >= ((lum2_max * 3) / 2)) + link->panel_type = PANEL_TYPE_MINILED; + } + + if (link->panel_type == PANEL_TYPE_OLED) + drm_object_property_set_value(&connector->base, + adev_to_drm(adev)->mode_config.panel_type_property, + DRM_MODE_PANEL_TYPE_OLED); + else + drm_object_property_set_value(&connector->base, + adev_to_drm(adev)->mode_config.panel_type_property, + DRM_MODE_PANEL_TYPE_UNKNOWN); + + drm_dbg_kms(aconnector->base.dev, "Panel type: %d\n", link->panel_type); +} + static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) { const struct drm_panel_backlight_quirk *panel_backlight_quirk; @@ -3854,10 +3923,6 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps; caps->aux_support = false; - drm_object_property_set_value(&conn_base->base, - adev_to_drm(adev)->mode_config.panel_type_property, - caps->ext_caps->bits.oled ? DRM_MODE_PANEL_TYPE_OLED : DRM_MODE_PANEL_TYPE_UNKNOWN); - if (caps->ext_caps->bits.oled == 1 /* * || @@ -3938,7 +4003,7 @@ void amdgpu_dm_update_connector_after_detect( if (sink) { if (aconnector->dc_sink) { - amdgpu_dm_update_freesync_caps(connector, NULL); + amdgpu_dm_update_freesync_caps(connector, NULL, true); /* * retain and release below are used to * bump up refcount for sink because the link doesn't point @@ -3950,9 +4015,9 @@ void amdgpu_dm_update_connector_after_detect( aconnector->dc_sink = sink; dc_sink_retain(aconnector->dc_sink); amdgpu_dm_update_freesync_caps(connector, - aconnector->drm_edid); + aconnector->drm_edid, true); } else { - amdgpu_dm_update_freesync_caps(connector, NULL); + amdgpu_dm_update_freesync_caps(connector, NULL, true); if (!aconnector->dc_sink) { aconnector->dc_sink = aconnector->dc_em_sink; dc_sink_retain(aconnector->dc_sink); @@ -3996,7 +4061,7 @@ void amdgpu_dm_update_connector_after_detect( * If yes, put it here. */ if (aconnector->dc_sink) { - amdgpu_dm_update_freesync_caps(connector, NULL); + amdgpu_dm_update_freesync_caps(connector, NULL, true); dc_sink_release(aconnector->dc_sink); } @@ -4029,12 +4094,13 @@ void amdgpu_dm_update_connector_after_detect( "failed to create aconnector->requested_timing\n"); } - amdgpu_dm_update_freesync_caps(connector, aconnector->drm_edid); + amdgpu_dm_update_freesync_caps(connector, aconnector->drm_edid, true); update_connector_ext_caps(aconnector); + dm_set_panel_type(aconnector); } else { hdmi_cec_unset_edid(aconnector); drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux); - amdgpu_dm_update_freesync_caps(connector, NULL); + amdgpu_dm_update_freesync_caps(connector, NULL, true); aconnector->num_modes = 0; dc_sink_release(aconnector->dc_sink); aconnector->dc_sink = NULL; @@ -4211,7 +4277,7 @@ static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector) dc_sink_retain(aconnector->hdmi_prev_sink); /* Schedule delayed detection. */ - if (mod_delayed_work(system_wq, + if (mod_delayed_work(system_percpu_wq, &aconnector->hdmi_hpd_debounce_work, msecs_to_jiffies(aconnector->hdmi_hpd_debounce_delay_ms))) drm_dbg_kms(dev, "HDMI HPD: Re-scheduled debounce work\n"); @@ -8798,7 +8864,7 @@ static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, * drm_edid_connector_add_modes() and need to be * restored here. */ - amdgpu_dm_update_freesync_caps(connector, drm_edid); + amdgpu_dm_update_freesync_caps(connector, drm_edid, false); } else { amdgpu_dm_connector->num_modes = 0; } @@ -9342,9 +9408,21 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, if (acrtc_state) { timing = &acrtc_state->stream->timing; - if (amdgpu_ip_version(adev, DCE_HWIP, 0) < - IP_VERSION(3, 5, 0) || - !(adev->flags & AMD_IS_APU)) { + if (amdgpu_ip_version(adev, DCE_HWIP, 0) >= + IP_VERSION(3, 2, 0) && + !(adev->flags & AMD_IS_APU)) { + /* + * DGPUs NV3x and newer that support idle optimizations + * experience intermittent flip-done timeouts on cursor + * updates. Restore 5s offdelay behavior for now. + * + * Discussion on the issue: + * https://lore.kernel.org/amd-gfx/20260217191632.1243826-1-sysdadmin@m1k.cloud/ + */ + config.offdelay_ms = 5000; + config.disable_immediate = false; + } else if (amdgpu_ip_version(adev, DCE_HWIP, 0) < + IP_VERSION(3, 5, 0)) { /* * Older HW and DGPU have issues with instant off; * use a 2 frame offdelay. @@ -11142,8 +11220,8 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) if (!adev->in_suspend) { /* return the stolen vga memory back to VRAM */ if (!adev->mman.keep_stolen_vga_memory) - amdgpu_bo_free_kernel(&adev->mman.stolen_vga_memory, NULL, NULL); - amdgpu_bo_free_kernel(&adev->mman.stolen_extended_memory, NULL, NULL); + amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_STOLEN_VGA); + amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_STOLEN_EXTENDED); } /* @@ -12272,6 +12350,38 @@ static int add_affected_mst_dsc_crtcs(struct drm_atomic_state *state, struct drm * available. */ +/** + * dm_plane_color_pipeline_active() - Check if a plane's color pipeline active. + * @state: DRM atomic state + * @plane: DRM plane to check + * @use_old: if true, inspect the old colorop states; otherwise the new ones + * + * A color pipeline may be selected (color_pipeline != NULL) but still is + * inactive if every colorop in the chain is bypassed. Only return + * true when at least one colorop has bypass == false, meaning the cursor + * would be subjected to the transformation in native mode. + * + * Return: true if the pipeline modifies pixels, false otherwise. + */ +static bool dm_plane_color_pipeline_active(struct drm_atomic_state *state, + struct drm_plane *plane, + bool use_old) +{ + struct drm_colorop *colorop; + struct drm_colorop_state *old_colorop_state, *new_colorop_state; + int i; + + for_each_oldnew_colorop_in_state(state, colorop, old_colorop_state, new_colorop_state, i) { + struct drm_colorop_state *cstate = use_old ? old_colorop_state : new_colorop_state; + + if (cstate->colorop->plane != plane) + continue; + if (!cstate->bypass) + return true; + } + return false; +} + /** * dm_crtc_get_cursor_mode() - Determine the required cursor mode on crtc * @adev: amdgpu device @@ -12283,8 +12393,8 @@ static int add_affected_mst_dsc_crtcs(struct drm_atomic_state *state, struct drm * the dm_crtc_state. * * The cursor should be enabled in overlay mode if there exists an underlying - * plane - on which the cursor may be blended - that is either YUV formatted, or - * scaled differently from the cursor. + * plane - on which the cursor may be blended - that is either YUV formatted, + * scaled differently from the cursor, or has a color pipeline active. * * Since zpos info is required, drm_atomic_normalize_zpos must be called before * calling this function. @@ -12322,7 +12432,7 @@ static int dm_crtc_get_cursor_mode(struct amdgpu_device *adev, /* * Cursor mode can change if a plane's format changes, scale changes, is - * enabled/disabled, or z-order changes. + * enabled/disabled, z-order changes, or color management properties change. */ for_each_oldnew_plane_in_state(state, plane, old_plane_state, plane_state, i) { int new_scale_w, new_scale_h, old_scale_w, old_scale_h; @@ -12347,6 +12457,12 @@ static int dm_crtc_get_cursor_mode(struct amdgpu_device *adev, consider_mode_change = true; break; } + + if (dm_plane_color_pipeline_active(state, plane, true) != + dm_plane_color_pipeline_active(state, plane, false)) { + consider_mode_change = true; + break; + } } if (!consider_mode_change && !crtc_state->zpos_changed) @@ -12387,6 +12503,12 @@ static int dm_crtc_get_cursor_mode(struct amdgpu_device *adev, return 0; } + /* Underlying plane has an active color pipeline - cursor would be transformed */ + if (dm_plane_color_pipeline_active(state, plane, false)) { + *cursor_mode = DM_CURSOR_OVERLAY_MODE; + return 0; + } + dm_get_plane_scale(plane_state, &underlying_scale_w, &underlying_scale_h); dm_get_plane_scale(cursor_state, @@ -12766,7 +12888,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, goto fail; } else if (required_cursor_mode == DM_CURSOR_OVERLAY_MODE) { drm_dbg_driver(crtc->dev, - "[CRTC:%d:%s] Cannot enable native cursor due to scaling or YUV restrictions\n", + "[CRTC:%d:%s] Cannot enable native cursor due to scaling, YUV, or color pipeline restrictions\n", crtc->base.id, crtc->name); ret = -EINVAL; goto fail; @@ -13031,6 +13153,7 @@ static bool dm_edid_parser_send_cea(struct amdgpu_display_manager *dm, vsdb->amd_vsdb_version = output->amd_vsdb.amd_vsdb_version; vsdb->min_refresh_rate_hz = output->amd_vsdb.min_frame_rate; vsdb->max_refresh_rate_hz = output->amd_vsdb.max_frame_rate; + vsdb->freesync_mccs_vcp_code = output->amd_vsdb.freesync_mccs_vcp_code; } else { drm_warn(adev_to_drm(dm->adev), "Unknown EDID CEA parser results\n"); return false; @@ -13065,6 +13188,8 @@ static bool parse_edid_cea_dmcu(struct amdgpu_display_manager *dm, vsdb_info->amd_vsdb_version = version; vsdb_info->min_refresh_rate_hz = min_rate; vsdb_info->max_refresh_rate_hz = max_rate; + /* Not enabled on DMCU*/ + vsdb_info->freesync_mccs_vcp_code = 0; return true; } /* not amd vsdb */ @@ -13155,56 +13280,15 @@ static void parse_edid_displayid_vrr(struct drm_connector *connector, } } -static int parse_amd_vsdb(struct amdgpu_dm_connector *aconnector, - const struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info) +static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector, + struct amdgpu_hdmi_vsdb_info *vsdb_info) { - u8 *edid_ext = NULL; - int i; - int j = 0; - int total_ext_block_len; + struct drm_connector *connector = &aconnector->base; - if (edid == NULL || edid->extensions == 0) - return -ENODEV; + vsdb_info->replay_mode = connector->display_info.amd_vsdb.replay_mode; + vsdb_info->amd_vsdb_version = connector->display_info.amd_vsdb.version; - /* Find DisplayID extension */ - for (i = 0; i < edid->extensions; i++) { - edid_ext = (void *)(edid + (i + 1)); - if (edid_ext[0] == DISPLAYID_EXT) - break; - } - - total_ext_block_len = EDID_LENGTH * edid->extensions; - while (j < total_ext_block_len - sizeof(struct amd_vsdb_block)) { - struct amd_vsdb_block *amd_vsdb = (struct amd_vsdb_block *)&edid_ext[j]; - unsigned int ieeeId = (amd_vsdb->ieee_id[2] << 16) | (amd_vsdb->ieee_id[1] << 8) | (amd_vsdb->ieee_id[0]); - - if (ieeeId == HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_IEEE_REGISTRATION_ID && - amd_vsdb->version == HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3) { - u8 panel_type; - vsdb_info->replay_mode = (amd_vsdb->feature_caps & AMD_VSDB_VERSION_3_FEATURECAP_REPLAYMODE) ? true : false; - vsdb_info->amd_vsdb_version = HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3; - drm_dbg_kms(aconnector->base.dev, "Panel supports Replay Mode: %d\n", vsdb_info->replay_mode); - panel_type = (amd_vsdb->color_space_eotf_support & AMD_VDSB_VERSION_3_PANEL_TYPE_MASK) >> AMD_VDSB_VERSION_3_PANEL_TYPE_SHIFT; - switch (panel_type) { - case AMD_VSDB_PANEL_TYPE_OLED: - aconnector->dc_link->panel_type = PANEL_TYPE_OLED; - break; - case AMD_VSDB_PANEL_TYPE_MINILED: - aconnector->dc_link->panel_type = PANEL_TYPE_MINILED; - break; - default: - aconnector->dc_link->panel_type = PANEL_TYPE_NONE; - break; - } - drm_dbg_kms(aconnector->base.dev, "Panel type: %d\n", - aconnector->dc_link->panel_type); - - return true; - } - j++; - } - - return false; + return connector->display_info.amd_vsdb.version != 0; } static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, @@ -13244,6 +13328,10 @@ static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, * * @connector: Connector to query. * @drm_edid: DRM EDID from monitor + * @do_mccs: Controls whether MCCS (Monitor Control Command Set) over + * DDC (Display Data Channel) transactions are performed. When true, + * the driver queries the monitor to get or update additional FreeSync + * capability information. When false, these transactions are skipped. * * Amdgpu supports Freesync in DP and HDMI displays, and it is required to keep * track of some of the display information in the internal data struct used by @@ -13251,7 +13339,7 @@ static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, * FreeSync parameters. */ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, - const struct drm_edid *drm_edid) + const struct drm_edid *drm_edid, bool do_mccs) { int i = 0; struct amdgpu_dm_connector *amdgpu_dm_connector = @@ -13307,7 +13395,7 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, freesync_capable = true; } - parse_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); + get_amd_vsdb(amdgpu_dm_connector, &vsdb_info); if (vsdb_info.replay_mode) { amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode; @@ -13317,14 +13405,19 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) { i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); - if (i >= 0 && vsdb_info.freesync_supported) { - amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; - amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; - if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) - freesync_capable = true; + if (i >= 0) { + amdgpu_dm_connector->vsdb_info = vsdb_info; + sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code; - connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; - connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; + if (vsdb_info.freesync_supported) { + amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; + amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; + if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) + freesync_capable = true; + + connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; + connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; + } } } @@ -13333,22 +13426,38 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) { i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); - if (i >= 0 && vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) { - - amdgpu_dm_connector->pack_sdp_v1_3 = true; - amdgpu_dm_connector->as_type = as_type; + if (i >= 0) { amdgpu_dm_connector->vsdb_info = vsdb_info; + sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code; - amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; - amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; - if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) - freesync_capable = true; + if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) { + amdgpu_dm_connector->pack_sdp_v1_3 = true; + amdgpu_dm_connector->as_type = as_type; - connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; - connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; + amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; + amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; + if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) + freesync_capable = true; + + connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; + connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; + } } } + /* Handle MCCS */ + if (do_mccs) + dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); + + if ((sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A || + as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) && + (!sink->edid_caps.freesync_vcp_code || + (sink->edid_caps.freesync_vcp_code && !sink->mccs_caps.freesync_supported))) + freesync_capable = false; + + if (do_mccs && sink->mccs_caps.freesync_supported && freesync_capable) + dm_helpers_mccs_vcp_set(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); + update: if (dm_con_state) dm_con_state->freesync_capable = freesync_capable; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index d1a14e0c12bd..74a8fe1a1999 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -53,12 +53,6 @@ #define AMDGPU_DMUB_NOTIFICATION_MAX 8 -#define HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_IEEE_REGISTRATION_ID 0x00001A -#define AMD_VSDB_VERSION_3_FEATURECAP_REPLAYMODE 0x40 -#define AMD_VDSB_VERSION_3_PANEL_TYPE_MASK 0xC0 -#define AMD_VDSB_VERSION_3_PANEL_TYPE_SHIFT 6 -#define HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3 0x3 - enum amd_vsdb_panel_type { AMD_VSDB_PANEL_TYPE_DEFAULT = 0, AMD_VSDB_PANEL_TYPE_MINILED, @@ -97,14 +91,6 @@ struct dc_plane_state; struct dmub_notification; struct dmub_cmd_fused_request; -struct amd_vsdb_block { - unsigned char ieee_id[3]; - unsigned char version; - unsigned char feature_caps; - unsigned char reserved[3]; - unsigned char color_space_eotf_support; -}; - struct common_irq_params { struct amdgpu_device *adev; enum dc_irq_source irq_src; @@ -772,6 +758,11 @@ struct amdgpu_hdmi_vsdb_info { */ unsigned int max_refresh_rate_hz; + /** + * @freesync_mccs_vcp_code: MCCS VCP code for freesync state + */ + unsigned int freesync_mccs_vcp_code; + /** * @replay_mode: Replay supported */ @@ -1080,7 +1071,7 @@ void dm_restore_drm_connector_state(struct drm_device *dev, struct drm_connector *connector); void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, - const struct drm_edid *drm_edid); + const struct drm_edid *drm_edid, bool do_mccs); void amdgpu_dm_trigger_timing_sync(struct drm_device *dev); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index c3c588294665..d69f5a75b685 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -101,23 +101,22 @@ bool amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state *dm_state) /** * amdgpu_dm_crtc_set_panel_sr_feature() - Manage panel self-refresh features. - * - * @vblank_work: is a pointer to a struct vblank_control_work object. - * @vblank_enabled: indicates whether the DRM vblank counter is currently - * enabled (true) or disabled (false). - * @allow_sr_entry: represents whether entry into the self-refresh mode is - * allowed (true) or not allowed (false). + * @dm: amdgpu display manager instance. + * @acrtc: CRTC whose panel self-refresh state is being updated. + * @stream: DC stream associated with @acrtc. + * @vblank_enabled: Whether the DRM vblank counter is currently enabled. + * @allow_sr_entry: Whether entry into self-refresh mode is allowed. * * The DRM vblank counter enable/disable action is used as the trigger to enable * or disable various panel self-refresh features: * * Panel Replay and PSR SU * - Enable when: - * - VRR is disabled - * - vblank counter is disabled - * - entry is allowed: usermode demonstrates an adequate number of fast - * commits) - * - CRC capture window isn't active + * - VRR is disabled + * - vblank counter is disabled + * - entry is allowed: usermode demonstrates an adequate number of fast + * commits + * - CRC capture window isn't active * - Keep enabled even when vblank counter gets enabled * * PSR1 @@ -458,9 +457,12 @@ static struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *cr static void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc) { - struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + /* + * amdgpu_dm_ism_fini() is intentionally called in amdgpu_dm_fini(). + * It must be called before dc_destroy() in amdgpu_dm_fini() + * to avoid ISM accessing an invalid dc handle once dc is released. + */ - amdgpu_dm_ism_fini(&acrtc->ism); drm_crtc_cleanup(crtc); kfree(crtc); } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index f3fa8eb4bcce..a3cb05490dc9 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -49,6 +49,45 @@ #include "ddc_service_types.h" #include "clk_mgr.h" +#define MCCS_DEST_ADDR (0x6E >> 1) +#define MCCS_SRC_ADDR 0x51 +#define MCCS_LENGTH_OFFSET 0x80 +#define MCCS_MAX_DATA_SIZE 0x20 + +enum mccs_op_code { + MCCS_OP_CODE_VCP_REQUEST = 0x01, + MCCS_OP_CODE_VCP_REPLY = 0x02, + MCCS_OP_CODE_VCP_SET = 0x03, + MCCS_OP_CODE_VCP_RESET = 0x09, + MCCS_OP_CODE_CAP_REQUEST = 0xF3, + MCCS_OP_CODE_CAP_REPLY = 0xE3 +}; + +enum mccs_op_buff_size { + MCCS_OP_BUFF_SIZE__WR_VCP_REQUEST = 5, + MCCS_OP_BUFF_SIZE_RD_VCP_REQUEST = 11, + MCCS_OP_BUFF_SIZE_WR_VCP_SET = 7, +}; + +enum vcp_reply_mask { + FREESYNC_SUPPORTED = 0x1 +}; + +union vcp_reply { + struct { + unsigned char src_addr; + unsigned char length; /* Length is offset by MccsLengthOffs = 0x80 */ + unsigned char reply_op_code; /* Should return MCCS_OP_CODE_VCP_REPLY = 0x02 */ + unsigned char result_code; /* 00h No Error, 01h Unsupported VCP Code */ + unsigned char request_code; /* Should return mccs vcp code sent in the vcp request */ + unsigned char type_code; /* VCP type code: 00h Set parameter, 01h Momentary */ + unsigned char max_value[2]; /* 2 bytes returning max value current value */ + unsigned char present_value[2]; /* NOTE: Byte0 is MSB, Byte1 is LSB */ + unsigned char check_sum; + } bytes; + unsigned char raw[11]; +}; + static u32 edid_extract_panel_id(struct edid *edid) { return (u32)edid->mfg_id[0] << 24 | @@ -993,6 +1032,45 @@ dm_helpers_read_acpi_edid(struct amdgpu_dm_connector *aconnector) return drm_edid_read_custom(connector, dm_helpers_probe_acpi_edid, connector); } +static const struct drm_edid * +dm_helpers_read_vbios_hardcoded_edid(struct dc_link *link, struct amdgpu_dm_connector *aconnector) +{ + struct dc_bios *bios = link->ctx->dc_bios; + struct embedded_panel_info info; + const struct drm_edid *edid; + enum bp_result r; + + if (!dc_is_embedded_signal(link->connector_signal) || + !bios->funcs->get_embedded_panel_info) + return NULL; + + memset(&info, 0, sizeof(info)); + r = bios->funcs->get_embedded_panel_info(bios, &info); + + if (r != BP_RESULT_OK) { + dm_error("Error when reading embedded panel info: %u\n", r); + return NULL; + } + + if (!info.fake_edid || !info.fake_edid_size) { + dm_error("Embedded panel info doesn't contain an EDID\n"); + return NULL; + } + + edid = drm_edid_alloc(info.fake_edid, info.fake_edid_size); + + if (!drm_edid_valid(edid)) { + dm_error("EDID from embedded panel info is invalid\n"); + drm_edid_free(edid); + return NULL; + } + + aconnector->base.display_info.width_mm = info.panel_width_mm; + aconnector->base.display_info.height_mm = info.panel_height_mm; + + return edid; +} + void populate_hdmi_info_from_connector(struct drm_hdmi_info *hdmi, struct dc_edid_caps *edid_caps) { edid_caps->scdc_present = hdmi->scdc.supported; @@ -1013,6 +1091,9 @@ enum dc_edid_status dm_helpers_read_local_edid( if (link->aux_mode) ddc = &aconnector->dm_dp_aux.aux.ddc; + else if (link->ddc_hw_inst == GPIO_DDC_LINE_UNKNOWN && + dc_is_embedded_signal(link->connector_signal)) + ddc = NULL; else ddc = &aconnector->i2c->base; @@ -1026,6 +1107,8 @@ enum dc_edid_status dm_helpers_read_local_edid( drm_edid = dm_helpers_read_acpi_edid(aconnector); if (drm_edid) drm_info(connector->dev, "Using ACPI provided EDID for %s\n", connector->name); + else if (!ddc) + drm_edid = dm_helpers_read_vbios_hardcoded_edid(link, aconnector); else drm_edid = drm_edid_read_ddc(connector, ddc); drm_edid_connector_update(connector, drm_edid); @@ -1400,6 +1483,8 @@ static bool dm_is_freesync_pcon_whitelist(const uint32_t branch_dev_id) case DP_BRANCH_DEVICE_ID_0060AD: case DP_BRANCH_DEVICE_ID_00E04C: case DP_BRANCH_DEVICE_ID_90CC24: + case DP_BRANCH_DEVICE_ID_001CF8: + case DP_BRANCH_DEVICE_ID_001FF2: ret_val = true; break; default: @@ -1439,3 +1524,203 @@ bool dm_helpers_is_hdr_on(struct dc_context *ctx, struct dc_stream_state *stream // TODO return false; } + +static int mccs_operation_vcp_request(unsigned int vcp_code, struct dc_link *link, + union vcp_reply *reply) +{ + const unsigned char retry_interval_ms = 40; + unsigned char retry = 5; + struct amdgpu_dm_connector *aconnector = link->priv; + struct i2c_adapter *ddc; + struct i2c_msg msg = {0}; + int ret = 0; + int idx; + + unsigned char wr_data[MCCS_OP_BUFF_SIZE__WR_VCP_REQUEST] = { + MCCS_SRC_ADDR, /* Byte0 - Src Addr */ + MCCS_LENGTH_OFFSET + 2, /* Byte1 - Length */ + MCCS_OP_CODE_VCP_REQUEST, /* Byte2 - MCCS Command */ + (unsigned char) vcp_code, /* Byte3 - VCP Code */ + MCCS_DEST_ADDR << 1 /* Byte4 - CheckSum */ + }; + + /* calculate checksum */ + for (idx = 0; idx < (MCCS_OP_BUFF_SIZE__WR_VCP_REQUEST - 1); idx++) + wr_data[(MCCS_OP_BUFF_SIZE__WR_VCP_REQUEST-1)] ^= wr_data[idx]; + + if (link->aux_mode) + ddc = &aconnector->dm_dp_aux.aux.ddc; + else + ddc = &aconnector->i2c->base; + + do { + msg.addr = MCCS_DEST_ADDR; + msg.flags = 0; + msg.len = MCCS_OP_BUFF_SIZE__WR_VCP_REQUEST; + msg.buf = wr_data; + + ret = i2c_transfer(ddc, &msg, 1); + if (ret != 1) + goto mccs_retry; + + msleep(retry_interval_ms); + + msg.addr = MCCS_DEST_ADDR; + msg.flags = I2C_M_RD; + msg.len = MCCS_OP_BUFF_SIZE_RD_VCP_REQUEST; + msg.buf = reply->raw; + + ret = i2c_transfer(ddc, &msg, 1); + + /* sink might reply with null msg if it can't reply in time */ + if (ret == 1 && reply->bytes.length > MCCS_LENGTH_OFFSET) + break; +mccs_retry: + retry--; + msleep(retry_interval_ms); + } while (retry); + + if (!retry) { + drm_dbg_driver(aconnector->base.dev, + "%s: MCCS VCP request failed after retries", __func__); + return -EIO; + } + + return 0; +} + +void dm_helpers_read_mccs_caps(struct dc_context *ctx, struct dc_link *link, + struct dc_sink *sink) +{ + bool mccs_op = false; + struct dpcd_caps *dpcd_caps; + struct drm_device *dev; + uint16_t freesync_vcp_value = 0; + union vcp_reply vcp_reply_value = {0}; + + if (!ctx) + return; + dev = adev_to_drm(ctx->driver_context); + + if (!link || !sink) { + drm_dbg_driver(dev, "%s: link or sink is NULL", __func__); + return; + } + + sink->mccs_caps.freesync_supported = false; + dpcd_caps = &link->dpcd_caps; + + if (sink->edid_caps.freesync_vcp_code != 0) { + if (dc_is_dp_signal(link->connector_signal)) { + if ((dpcd_caps->dpcd_rev.raw >= DPCD_REV_14) && + (dpcd_caps->dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) && + dm_is_freesync_pcon_whitelist(dpcd_caps->branch_dev_id) && + (dpcd_caps->adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT == true)) + mccs_op = true; + + if ((dpcd_caps->dongle_type != DISPLAY_DONGLE_NONE && + dpcd_caps->dongle_type != DISPLAY_DONGLE_DP_HDMI_CONVERTER)) { + if (mccs_op == false) + drm_dbg_driver(dev, "%s: Legacy Pcon support", __func__); + mccs_op = true; + } + + if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + // Todo: Freesync over MST + mccs_op = false; + } + } + + if (dc_is_hdmi_signal(link->connector_signal)) { + drm_dbg_driver(dev, "%s: Local HDMI sink", __func__); + mccs_op = true; + } + + if (mccs_op == true) { + // MCCS VCP request to get VCP value + if (!mccs_operation_vcp_request(sink->edid_caps.freesync_vcp_code, link, + &vcp_reply_value)) { + freesync_vcp_value = vcp_reply_value.bytes.present_value[1]; + freesync_vcp_value |= (uint16_t) vcp_reply_value.bytes.present_value[0] << 8; + } + // If VCP Value bit 0 is 1, freesyncSupport = true + sink->mccs_caps.freesync_supported = + (freesync_vcp_value & FREESYNC_SUPPORTED) ? true : false; + } + } +} + +static int mccs_operation_vcp_set(unsigned int vcp_code, struct dc_link *link, uint16_t value) +{ + const unsigned char retry_interval_ms = 40; + unsigned char retry = 5; + struct amdgpu_dm_connector *aconnector = link->priv; + struct i2c_adapter *ddc; + struct i2c_msg msg = {0}; + int ret = 0; + int idx; + + unsigned char wr_data[MCCS_OP_BUFF_SIZE_WR_VCP_SET] = { + MCCS_SRC_ADDR, /* Byte0 - Src Addr */ + MCCS_LENGTH_OFFSET + 4, /* Byte1 - Length */ + MCCS_OP_CODE_VCP_SET, /* Byte2 - MCCS Command */ + (unsigned char)vcp_code, /* Byte3 - VCP Code */ + (unsigned char)(value >> 8), /* Byte4 - Value High Byte */ + (unsigned char)(value & 0xFF), /* Byte5 - Value Low Byte */ + MCCS_DEST_ADDR << 1 /* Byte6 - CheckSum */ + }; + + /* calculate checksum */ + for (idx = 0; idx < (MCCS_OP_BUFF_SIZE_WR_VCP_SET - 1); idx++) + wr_data[MCCS_OP_BUFF_SIZE_WR_VCP_SET - 1] ^= wr_data[idx]; + + if (link->aux_mode) + ddc = &aconnector->dm_dp_aux.aux.ddc; + else + ddc = &aconnector->i2c->base; + + do { + msg.addr = MCCS_DEST_ADDR; + msg.flags = 0; + msg.len = MCCS_OP_BUFF_SIZE_WR_VCP_SET; + msg.buf = wr_data; + + ret = i2c_transfer(ddc, &msg, 1); + if (ret == 1) + break; + + retry--; + msleep(retry_interval_ms); + } while (retry); + + if (!retry) + return -EIO; + + return 0; +} + +void dm_helpers_mccs_vcp_set(struct dc_context *ctx, struct dc_link *link, + struct dc_sink *sink) +{ + struct drm_device *dev; + const uint16_t enable = 0x0101; + + if (!ctx) + return; + dev = adev_to_drm(ctx->driver_context); + + if (!link || !sink) { + drm_dbg_driver(dev, "%s: link or sink is NULL", __func__); + return; + } + + if (!sink->mccs_caps.freesync_supported) { + drm_dbg_driver(dev, "%s: MCCS freesync not supported on this sink", __func__); + return; + } + + if (mccs_operation_vcp_set(sink->edid_caps.freesync_vcp_code, link, enable)) + drm_dbg_driver(dev, "%s: Failed to set VCP code %d", __func__, + sink->edid_caps.freesync_vcp_code); +} + diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c index 65a5cfe1e106..a64e95860e99 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c @@ -35,6 +35,9 @@ /** * dm_ism_next_state - Get next state based on current state and event + * @current_state: current ISM state + * @event: event being processed + * @next_state: place to store the next state * * This function defines the idle state management FSM. Invalid transitions * are ignored and will not progress the FSM. @@ -148,6 +151,11 @@ static uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism, /** * dm_ism_get_idle_allow_delay - Calculate hysteresis-based idle allow delay + * @ism: ISM instance containing configuration, history, and current state + * @stream: display stream used to derive frame timing values for delay + * + * Calculates the delay before allowing idle optimizations based on recent + * idle history and the current stream timing. */ static uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism, const struct dc_stream_state *stream) @@ -212,6 +220,7 @@ static uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism, /** * dm_ism_insert_record - Insert a record into the circular history buffer + * @ism: ISM instance */ static void dm_ism_insert_record(struct amdgpu_dm_ism *ism) { @@ -261,7 +270,6 @@ static void dm_ism_commit_idle_optimization_state(struct amdgpu_dm_ism *ism, struct amdgpu_crtc *acrtc = ism_to_amdgpu_crtc(ism); struct amdgpu_device *adev = drm_to_adev(acrtc->base.dev); struct amdgpu_display_manager *dm = &adev->dm; - int r; trace_amdgpu_dm_ism_commit(dm->active_vblank_irq_count, vblank_enabled, @@ -314,16 +322,7 @@ static void dm_ism_commit_idle_optimization_state(struct amdgpu_dm_ism *ism, */ if (!vblank_enabled && dm->active_vblank_irq_count == 0) { dc_post_update_surfaces_to_stream(dm->dc); - - r = amdgpu_dpm_pause_power_profile(adev, true); - if (r) - dev_warn(adev->dev, "failed to set default power profile mode\n"); - dc_allow_idle_optimizations(dm->dc, true); - - r = amdgpu_dpm_pause_power_profile(adev, false); - if (r) - dev_warn(adev->dev, "failed to restore the power profile mode\n"); } } @@ -463,6 +462,9 @@ void amdgpu_dm_ism_commit_event(struct amdgpu_dm_ism *ism, /* ISM transitions must be called with mutex acquired */ ASSERT(mutex_is_locked(&dm->dc_lock)); + /* ISM should not run after dc is destroyed */ + ASSERT(dm->dc); + if (!acrtc_state) { trace_amdgpu_dm_ism_event(acrtc->crtc_id, "NO_STATE", "NO_STATE", "N/A"); @@ -536,6 +538,8 @@ void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm) struct amdgpu_crtc *acrtc; struct amdgpu_dm_ism *ism; + ASSERT(mutex_is_locked(&dm->dc_lock)); + drm_for_each_crtc(crtc, dm->ddev) { acrtc = to_amdgpu_crtc(crtc); ism = &acrtc->ism; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index 5d8c4c7020b1..be038d9014bb 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -474,7 +474,7 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector) if (aconnector->dc_sink) { amdgpu_dm_update_freesync_caps( - connector, aconnector->drm_edid); + connector, aconnector->drm_edid, true); #if defined(CONFIG_DRM_AMD_DC_FP) if (!validate_dsc_caps_on_connector(aconnector)) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c index 8ba9b4f56f87..172999cc84e5 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c @@ -59,7 +59,7 @@ inline void dc_assert_fp_enabled(void) } /** - * dc_assert_fp_enabled - Check if FPU protection is enabled + * dc_is_fp_enabled - Check if FPU protection is enabled * * This function tells if the code is already under FPU protection or not. A * function that works as an API for a set of FPU operations can use this diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c index dd362071a6c9..c307f42fe0b9 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c @@ -794,11 +794,13 @@ static enum bp_result bios_parser_external_encoder_control( static enum bp_result bios_parser_dac_load_detection( struct dc_bios *dcb, - enum engine_id engine_id) + enum engine_id engine_id, + struct graphics_object_id ext_enc_id) { struct bios_parser *bp = BP_FROM_DCB(dcb); struct dc_context *ctx = dcb->ctx; struct bp_load_detection_parameters bp_params = {0}; + struct bp_external_encoder_control ext_cntl = {0}; enum bp_result bp_result = BP_RESULT_UNSUPPORTED; uint32_t bios_0_scratch; uint32_t device_id_mask = 0; @@ -824,6 +826,13 @@ static enum bp_result bios_parser_dac_load_detection( bp_params.engine_id = engine_id; bp_result = bp->cmd_tbl.dac_load_detection(bp, &bp_params); + } else if (ext_enc_id.id) { + if (!bp->cmd_tbl.external_encoder_control) + return BP_RESULT_UNSUPPORTED; + + ext_cntl.action = EXTERNAL_ENCODER_CONTROL_DAC_LOAD_DETECT; + ext_cntl.encoder_id = ext_enc_id; + bp_result = bp->cmd_tbl.external_encoder_control(bp, &ext_cntl); } if (bp_result != BP_RESULT_OK) @@ -1304,6 +1313,60 @@ static enum bp_result bios_parser_get_embedded_panel_info( return BP_RESULT_FAILURE; } +static enum bp_result get_embedded_panel_extra_info( + struct bios_parser *bp, + struct embedded_panel_info *info, + const uint32_t table_offset) +{ + uint8_t *record = bios_get_image(&bp->base, table_offset, 1); + ATOM_PANEL_RESOLUTION_PATCH_RECORD *panel_res_record; + ATOM_FAKE_EDID_PATCH_RECORD *fake_edid_record; + + while (*record != ATOM_RECORD_END_TYPE) { + switch (*record) { + case LCD_MODE_PATCH_RECORD_MODE_TYPE: + record += sizeof(ATOM_PATCH_RECORD_MODE); + break; + case LCD_RTS_RECORD_TYPE: + record += sizeof(ATOM_LCD_RTS_RECORD); + break; + case LCD_CAP_RECORD_TYPE: + record += sizeof(ATOM_LCD_MODE_CONTROL_CAP); + break; + case LCD_FAKE_EDID_PATCH_RECORD_TYPE: + fake_edid_record = (ATOM_FAKE_EDID_PATCH_RECORD *)record; + if (fake_edid_record->ucFakeEDIDLength) { + if (fake_edid_record->ucFakeEDIDLength == 128) + info->fake_edid_size = + fake_edid_record->ucFakeEDIDLength; + else + info->fake_edid_size = + fake_edid_record->ucFakeEDIDLength * 128; + + info->fake_edid = fake_edid_record->ucFakeEDIDString; + + record += struct_size(fake_edid_record, + ucFakeEDIDString, + info->fake_edid_size); + } else { + /* empty fake edid record must be 3 bytes long */ + record += sizeof(ATOM_FAKE_EDID_PATCH_RECORD) + 1; + } + break; + case LCD_PANEL_RESOLUTION_RECORD_TYPE: + panel_res_record = (ATOM_PANEL_RESOLUTION_PATCH_RECORD *)record; + info->panel_width_mm = panel_res_record->usHSize; + info->panel_height_mm = panel_res_record->usVSize; + record += sizeof(ATOM_PANEL_RESOLUTION_PATCH_RECORD); + break; + default: + return BP_RESULT_BADBIOSTABLE; + } + } + + return BP_RESULT_OK; +} + static enum bp_result get_embedded_panel_info_v1_2( struct bios_parser *bp, struct embedded_panel_info *info) @@ -1420,6 +1483,10 @@ static enum bp_result get_embedded_panel_info_v1_2( if (ATOM_PANEL_MISC_API_ENABLED & lvds->ucLVDS_Misc) info->lcd_timing.misc_info.API_ENABLED = true; + if (lvds->usExtInfoTableOffset) + return get_embedded_panel_extra_info(bp, info, + le16_to_cpu(lvds->usExtInfoTableOffset) + DATA_TABLES(LCD_Info)); + return BP_RESULT_OK; } @@ -1545,6 +1612,10 @@ static enum bp_result get_embedded_panel_info_v1_3( (uint32_t) (ATOM_PANEL_MISC_V13_GREY_LEVEL & lvds->ucLCD_Misc) >> ATOM_PANEL_MISC_V13_GREY_LEVEL_SHIFT; + if (lvds->usExtInfoTableOffset) + return get_embedded_panel_extra_info(bp, info, + le16_to_cpu(lvds->usExtInfoTableOffset) + DATA_TABLES(LCD_Info)); + return BP_RESULT_OK; } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c index ec888aed207d..6a97ce69a562 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c @@ -611,80 +611,6 @@ static struct clk_bw_params dcn42_bw_params = { }; -static struct wm_table ddr5_wm_table = { - .entries = { - { - .wm_inst = WM_A, - .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 11.72, - .sr_exit_time_us = 28.0, - .sr_enter_plus_exit_time_us = 30.0, - .valid = true, - }, - { - .wm_inst = WM_B, - .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 11.72, - .sr_exit_time_us = 28.0, - .sr_enter_plus_exit_time_us = 30.0, - .valid = true, - }, - { - .wm_inst = WM_C, - .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 11.72, - .sr_exit_time_us = 28.0, - .sr_enter_plus_exit_time_us = 30.0, - .valid = true, - }, - { - .wm_inst = WM_D, - .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 11.72, - .sr_exit_time_us = 28.0, - .sr_enter_plus_exit_time_us = 30.0, - .valid = true, - }, - } -}; - -static struct wm_table lpddr5_wm_table = { - .entries = { - { - .wm_inst = WM_A, - .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 11.65333, - .sr_exit_time_us = 28.0, - .sr_enter_plus_exit_time_us = 30.0, - .valid = true, - }, - { - .wm_inst = WM_B, - .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 11.65333, - .sr_exit_time_us = 28.0, - .sr_enter_plus_exit_time_us = 30.0, - .valid = true, - }, - { - .wm_inst = WM_C, - .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 11.65333, - .sr_exit_time_us = 28.0, - .sr_enter_plus_exit_time_us = 30.0, - .valid = true, - }, - { - .wm_inst = WM_D, - .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 11.65333, - .sr_exit_time_us = 28.0, - .sr_enter_plus_exit_time_us = 30.0, - .valid = true, - }, - } -}; - struct dcn42_ss_info_table dcn42_ss_info_table = { .ss_divider = 1000, .ss_percentage = {0, 0, 375, 375, 375} @@ -1141,10 +1067,6 @@ void dcn42_clk_mgr_construct( if (ctx->dc_bios->integrated_info) { clk_mgr->base.base.dentist_vco_freq_khz = ctx->dc_bios->integrated_info->dentist_vco_freq; - if (ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) - dcn42_bw_params.wm_table = lpddr5_wm_table; - else - dcn42_bw_params.wm_table = ddr5_wm_table; dcn42_bw_params.vram_type = ctx->dc_bios->integrated_info->memory_type; dcn42_bw_params.dram_channel_width_bytes = ctx->dc_bios->integrated_info->memory_type == 0x22 ? 8 : 4; dcn42_bw_params.num_channels = ctx->dc_bios->integrated_info->ma_channel_number ? ctx->dc_bios->integrated_info->ma_channel_number : 1; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index db86e346307c..7333f5905330 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -40,7 +40,6 @@ #include "dcn10/dcn10_hubbub.h" #include "dce/dmub_hw_lock_mgr.h" -#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0])) #define MAX_NUM_MCACHE 8 /* used as index in array of black_color_format */ @@ -230,7 +229,7 @@ const uint16_t *find_color_matrix(enum dc_color_space color_space, int i; enum dc_color_space_type type; const uint16_t *val = NULL; - int arr_size = NUM_ELEMENTS(output_csc_matrix); + int arr_size = ARRAY_SIZE(output_csc_matrix); type = get_color_space_type(color_space); for (i = 0; i < arr_size; i++) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index 66597a1f5b78..05991a10f8bf 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -5062,13 +5062,19 @@ void resource_build_bit_depth_reduction_params(struct dc_stream_state *stream, option = DITHER_OPTION_SPATIAL8; break; case COLOR_DEPTH_101010: - option = DITHER_OPTION_TRUN10; + option = DITHER_OPTION_SPATIAL10; break; default: option = DITHER_OPTION_DISABLE; } } + if (stream->ctx->dce_version < DCE_VERSION_8_0 && + stream->timing.display_color_depth >= COLOR_DEPTH_101010) { + /* DCE 6.x doesn't support 10-bit truncation or dither options. */ + option = DITHER_OPTION_DISABLE; + } + if (option == DITHER_OPTION_DISABLE) return; diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 55ec281db3b7..37714d4371fb 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -63,7 +63,7 @@ struct dcn_dsc_reg_state; struct dcn_optc_reg_state; struct dcn_dccg_reg_state; -#define DC_VER "3.2.376" +#define DC_VER "3.2.378" /** * MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC @@ -562,6 +562,7 @@ struct dc_config { bool frame_update_cmd_version2; struct spl_sharpness_range dcn_sharpness_range; struct spl_sharpness_range dcn_override_sharpness_range; + bool no_native422_support; }; enum visual_confirm { @@ -986,7 +987,6 @@ struct link_service; * causing an issue or not. */ struct dc_debug_options { - bool native422_support; bool disable_dsc; enum visual_confirm visual_confirm; int visual_confirm_rect_height; @@ -1061,9 +1061,11 @@ struct dc_debug_options { bool hdmi20_disable; bool skip_detection_link_training; uint32_t edid_read_retry_times; - unsigned int force_odm_combine; //bit vector based on otg inst - unsigned int seamless_boot_odm_combine; - unsigned int force_odm_combine_4to1; //bit vector based on otg inst + + uint8_t force_odm_combine; //bit vector based on otg inst + uint8_t seamless_boot_odm_combine; + uint8_t force_odm_combine_4to1; //bit vector based on otg inst + int minimum_z8_residency_time; int minimum_z10_residency_time; bool disable_z9_mpc; @@ -1680,7 +1682,7 @@ struct dc_scratch_space { struct dc_link_training_overrides preferred_training_settings; struct dp_audio_test_data audio_test_data; - uint8_t ddc_hw_inst; + enum gpio_ddc_line ddc_hw_inst; uint8_t hpd_src; @@ -2725,6 +2727,7 @@ struct dc_sink { struct stereo_3d_features features_3d[TIMING_3D_FORMAT_MAX]; bool converter_disable_audio; + struct mccs_caps mccs_caps; struct scdc_caps scdc_caps; struct dc_sink_dsc_caps dsc_caps; struct dc_sink_fec_caps fec_caps; diff --git a/drivers/gpu/drm/amd/display/dc/dc_bios_types.h b/drivers/gpu/drm/amd/display/dc/dc_bios_types.h index 6f96c5cf39fe..526f71616f94 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_bios_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_bios_types.h @@ -102,7 +102,8 @@ struct dc_vbios_funcs { struct bp_external_encoder_control *cntl); enum bp_result (*dac_load_detection)( struct dc_bios *bios, - enum engine_id engine_id); + enum engine_id engine_id, + struct graphics_object_id ext_enc_id); enum bp_result (*transmitter_control)( struct dc_bios *bios, struct bp_transmitter_control *cntl); diff --git a/drivers/gpu/drm/amd/display/dc/dc_dsc.h b/drivers/gpu/drm/amd/display/dc/dc_dsc.h index 9d18f1c08079..101bce6b8de6 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dsc.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dsc.h @@ -52,6 +52,7 @@ struct dc_dsc_policy { uint32_t max_target_bpp; uint32_t min_target_bpp; bool enable_dsc_when_not_needed; + bool ycbcr422_simple; }; struct dc_dsc_config_options { diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h index 86394203cee7..7c38fa6f8cb1 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_stream.h +++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h @@ -162,13 +162,13 @@ struct test_pattern { #define SUBVP_DRR_MARGIN_US 100 // 100us for DRR margin (SubVP + DRR) struct dc_stream_debug_options { - char force_odm_combine_segments; + uint8_t force_odm_combine_segments; /* * When force_odm_combine_segments is non zero, allow dc to * temporarily transition to ODM bypass when minimal transition state * is required to prevent visual glitches showing on the screen */ - char allow_transition_for_forced_odm; + uint8_t allow_transition_for_forced_odm; }; #define LUMINANCE_DATA_TABLE_SIZE 10 diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index fd8ec1660312..c08d5c005df6 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -205,6 +205,8 @@ struct dc_edid_caps { uint32_t audio_latency; uint32_t video_latency; + unsigned char freesync_vcp_code; + uint8_t qs_bit; uint8_t qy_bit; @@ -1313,6 +1315,10 @@ struct dc_panel_config { } rio; }; +struct mccs_caps { + bool freesync_supported; +}; + #define MAX_SINKS_PER_LINK 4 /* diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index 34e54fdb9d13..25c13822fede 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -57,8 +57,6 @@ #define CALC_PLL_CLK_SRC_ERR_TOLERANCE 1 #define MAX_PLL_CALC_ERROR 0xFFFFFFFF -#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0])) - static const struct spread_spectrum_data *get_ss_data_entry( struct dce110_clk_src *clk_src, enum signal_type signal, @@ -1271,7 +1269,7 @@ const struct pixel_rate_range_table_entry *look_up_in_video_optimized_rate_tlb( { int i; - for (i = 0; i < NUM_ELEMENTS(video_optimized_pixel_rates); i++) { + for (i = 0; i < ARRAY_SIZE(video_optimized_pixel_rates); i++) { const struct pixel_rate_range_table_entry *e = &video_optimized_pixel_rates[i]; if (e->range_min_khz <= pixel_rate_khz && pixel_rate_khz <= e->range_max_khz) { diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c index 5f40ae9e3120..e15fd1454d3b 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c @@ -1102,7 +1102,9 @@ void dce110_link_encoder_hw_init( ASSERT(result == BP_RESULT_OK); } - aux_initialize(enc110); + + if (enc110->aux_regs) + aux_initialize(enc110); /* reinitialize HPD. * hpd_initialize() will pass DIG_FE id to HW context. diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_mmhubbub.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_mmhubbub.c index 6f2a0d5d963b..62fe5c3b18dc 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_mmhubbub.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_mmhubbub.c @@ -40,8 +40,8 @@ #define FN(reg_name, field_name) \ mcif_wb30->mcif_wb_shift->field_name, mcif_wb30->mcif_wb_mask->field_name -#define MCIF_ADDR(addr) (((unsigned long long)addr & 0xffffffffff) + 0xFE) >> 8 -#define MCIF_ADDR_HIGH(addr) (unsigned long long)addr >> 40 +#define MCIF_ADDR(addr) ((uint32_t)((((unsigned long long)(addr) & 0xffffffffffULL) + 0xFEULL) >> 8)) +#define MCIF_ADDR_HIGH(addr) ((uint32_t)(((unsigned long long)(addr)) >> 40)) /* wbif programming guide: * 1. set up wbif parameter: diff --git a/drivers/gpu/drm/amd/display/dc/dm_helpers.h b/drivers/gpu/drm/amd/display/dc/dm_helpers.h index 2818df555e62..107aec6a1265 100644 --- a/drivers/gpu/drm/amd/display/dc/dm_helpers.h +++ b/drivers/gpu/drm/amd/display/dc/dm_helpers.h @@ -181,6 +181,16 @@ enum dc_edid_status dm_helpers_read_local_edid( struct dc_link *link, struct dc_sink *sink); +void dm_helpers_read_mccs_caps( + struct dc_context *ctx, + struct dc_link *link, + struct dc_sink *sink); + +void dm_helpers_mccs_vcp_set( + struct dc_context *ctx, + struct dc_link *link, + struct dc_sink *sink); + bool dm_helpers_dp_handle_test_pattern_request( struct dc_context *ctx, const struct dc_link *link, diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c index 887744d56d6a..e82f2d531211 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c @@ -2399,7 +2399,7 @@ static struct _vcs_dpi_voltage_scaling_st construct_low_pstate_lvl(struct clk_li return low_pstate_lvl; } -void dcn21_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +void dcn21_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params) { struct _vcs_dpi_voltage_scaling_st *s = dc->scratch.update_bw_bounding_box.clock_limits; struct dcn21_resource_pool *pool = TO_DCN21_RES_POOL(dc->res_pool); diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h index aed00039ca62..8b2226c5bbbf 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h @@ -78,7 +78,7 @@ int dcn21_populate_dml_pipes_from_context(struct dc *dc, enum dc_validate_mode validate_mode); bool dcn21_validate_bandwidth_fp(struct dc *dc, struct dc_state *context, enum dc_validate_mode, display_e2e_pipe_params_st *pipes); -void dcn21_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params); +void dcn21_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params); void dcn21_clk_mgr_set_bw_params_wm_table(struct clk_bw_params *bw_params); diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn31/dcn31_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn31/dcn31_fpu.c index 1a28061bb9ff..ad23215da9f8 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn31/dcn31_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn31/dcn31_fpu.c @@ -587,7 +587,7 @@ void dcn31_calculate_wm_and_dlg_fp( context->bw_ctx.bw.dcn.compbuf_size_kb = context->bw_ctx.dml.ip.config_return_buffer_size_in_kbytes - total_det; } -void dcn31_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +void dcn31_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params) { struct _vcs_dpi_voltage_scaling_st *s = dc->scratch.update_bw_bounding_box.clock_limits; struct clk_limit_table *clk_table = &bw_params->clk_table; @@ -665,7 +665,7 @@ void dcn31_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params dml_init_instance(&dc->dml, &dcn3_1_soc, &dcn3_1_ip, DML_PROJECT_DCN31); } -void dcn315_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +void dcn315_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params) { struct clk_limit_table *clk_table = &bw_params->clk_table; int i, max_dispclk_mhz = 0, max_dppclk_mhz = 0; @@ -726,7 +726,7 @@ void dcn315_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_param dml_init_instance(&dc->dml, &dcn3_15_soc, &dcn3_15_ip, DML_PROJECT_DCN315); } -void dcn316_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +void dcn316_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params) { struct _vcs_dpi_voltage_scaling_st *s = dc->scratch.update_bw_bounding_box.clock_limits; struct clk_limit_table *clk_table = &bw_params->clk_table; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn31/dcn31_fpu.h b/drivers/gpu/drm/amd/display/dc/dml/dcn31/dcn31_fpu.h index dfcc5d50071e..0b7fcbbfd17b 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn31/dcn31_fpu.h +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn31/dcn31_fpu.h @@ -44,9 +44,9 @@ void dcn31_calculate_wm_and_dlg_fp( int pipe_cnt, int vlevel); -void dcn31_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params); -void dcn315_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params); -void dcn316_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params); +void dcn31_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params); +void dcn315_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params); +void dcn316_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params); int dcn_get_max_non_odm_pix_rate_100hz(struct _vcs_dpi_soc_bounding_box_st *soc); int dcn_get_approx_det_segs_required_for_pstate( struct _vcs_dpi_soc_bounding_box_st *soc, diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c index e29497204df7..eb199215d298 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c @@ -1610,38 +1610,6 @@ static bool is_dtbclk_required(struct dc *dc, struct dc_state *context) return false; } -static void dcn20_adjust_freesync_v_startup(const struct dc_crtc_timing *dc_crtc_timing, int *vstartup_start) -{ - struct dc_crtc_timing patched_crtc_timing; - uint32_t asic_blank_end = 0; - uint32_t asic_blank_start = 0; - uint32_t newVstartup = 0; - - patched_crtc_timing = *dc_crtc_timing; - - if (patched_crtc_timing.flags.INTERLACE == 1) { - if (patched_crtc_timing.v_front_porch < 2) - patched_crtc_timing.v_front_porch = 2; - } else { - if (patched_crtc_timing.v_front_porch < 1) - patched_crtc_timing.v_front_porch = 1; - } - - /* blank_start = frame end - front porch */ - asic_blank_start = patched_crtc_timing.v_total - - patched_crtc_timing.v_front_porch; - - /* blank_end = blank_start - active */ - asic_blank_end = asic_blank_start - - patched_crtc_timing.v_border_bottom - - patched_crtc_timing.v_addressable - - patched_crtc_timing.v_border_top; - - newVstartup = asic_blank_end + (patched_crtc_timing.v_total - asic_blank_start); - - *vstartup_start = ((newVstartup > *vstartup_start) ? newVstartup : *vstartup_start); -} - static void dcn32_calculate_dlg_params(struct dc *dc, struct dc_state *context, display_e2e_pipe_params_st *pipes, int pipe_cnt, int vlevel) @@ -1756,11 +1724,6 @@ static void dcn32_calculate_dlg_params(struct dc *dc, struct dc_state *context, } } - if (context->res_ctx.pipe_ctx[i].stream->adaptive_sync_infopacket.valid) - dcn20_adjust_freesync_v_startup( - &context->res_ctx.pipe_ctx[i].stream->timing, - &context->res_ctx.pipe_ctx[i].pipe_dlg_param.vstartup_start); - pipe_idx++; } /* If DCN isn't making memory requests we can allow pstate change and lower clocks */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile index 2625943d7f7e..8a451c36fdb3 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile @@ -100,6 +100,7 @@ DML21 += src/dml2_mcg/dml2_mcg_factory.o DML21 += src/dml2_pmo/dml2_pmo_dcn3.o DML21 += src/dml2_pmo/dml2_pmo_factory.o DML21 += src/dml2_pmo/dml2_pmo_dcn4_fams2.o +DML21 += src/dml2_pmo/dml2_pmo_dcn42.o DML21 += src/dml2_standalone_libraries/lib_float_math.o DML21 += dml21_translation_helper.o DML21 += dml21_wrapper.o diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c index 8e8935995fca..698d62fb9cf7 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c @@ -1812,6 +1812,8 @@ static dml_float_t CalculateWriteBackDISPCLK( dml_uint_t WritebackLineBufferSize, dml_float_t DISPCLKDPPCLKVCOSpeed) { + (void)WritebackPixelFormat; + (void)WritebackVRatio; dml_float_t DISPCLK_H, DISPCLK_V, DISPCLK_HB; DISPCLK_H = PixelClock * dml_ceil(WritebackHTaps / 8.0, 1) / WritebackHRatio; @@ -1830,6 +1832,8 @@ static dml_float_t CalculateWriteBackDelay( dml_uint_t WritebackSourceHeight, dml_uint_t HTotal) { + (void)WritebackPixelFormat; + (void)WritebackHRatio; dml_float_t CalculateWriteBackDelay; dml_float_t Line_length; dml_float_t Output_lines_last_notclamped; @@ -1977,6 +1981,7 @@ static void CalculateFlipSchedule( dml_float_t *final_flip_bw, dml_bool_t *ImmediateFlipSupportedForPipe) { + (void)HostVMMinPageSize; dml_float_t min_row_time = 0.0; dml_uint_t HostVMDynamicLevelsTrips = 0; dml_float_t TimeForFetchingMetaPTEImmediateFlip = 0; @@ -2118,6 +2123,11 @@ static void CalculateDCCConfiguration( dml_uint_t *IndependentBlockLuma, dml_uint_t *IndependentBlockChroma) { + (void)SurfaceWidthChroma; + (void)SurfaceHeightChroma; + (void)TilingFormat; + (void)BytePerPixelDETY; + (void)BytePerPixelDETC; dml_uint_t DETBufferSizeForDCC = nomDETInKByte * 1024; dml_uint_t yuv420; @@ -2489,6 +2499,7 @@ static dml_uint_t CalculateVMAndRowBytes( dml_uint_t *DPDE0BytesFrame, dml_uint_t *MetaPTEBytesFrame) { + (void)SourcePixelFormat; dml_uint_t MPDEBytesFrame; dml_uint_t DCCMetaSurfaceBytes; dml_uint_t ExtraDPDEBytesFrame; @@ -3662,6 +3673,8 @@ static void CalculateVMGroupAndRequestTimes( dml_float_t TimePerVMRequestVBlank[], dml_float_t TimePerVMRequestFlip[]) { + (void)dpte_row_width_luma_ub; + (void)dpte_row_width_chroma_ub; dml_uint_t num_group_per_lower_vm_stage; dml_uint_t num_req_per_lower_vm_stage; @@ -3762,6 +3775,7 @@ static void CalculateVMGroupAndRequestTimes( static void CalculateStutterEfficiency(struct display_mode_lib_scratch_st *scratch, struct CalculateStutterEfficiency_params_st *p) { + (void)scratch; dml_float_t DETBufferingTimeY = 0; dml_float_t SwathWidthYCriticalSurface = 0; dml_float_t SwathHeightYCriticalSurface = 0; @@ -4085,6 +4099,7 @@ static void CalculateStutterEfficiency(struct display_mode_lib_scratch_st *scrat static void CalculateSwathAndDETConfiguration(struct display_mode_lib_scratch_st *scratch, struct CalculateSwathAndDETConfiguration_params_st *p) { + (void)scratch; dml_uint_t MaximumSwathHeightY[__DML_NUM_PLANES__]; dml_uint_t MaximumSwathHeightC[__DML_NUM_PLANES__]; dml_uint_t RoundedUpMaxSwathSizeBytesY[__DML_NUM_PLANES__]; @@ -4331,6 +4346,7 @@ static void CalculateSwathWidth( dml_uint_t swath_width_luma_ub[], // per-pipe dml_uint_t swath_width_chroma_ub[]) // per-pipe { + (void)BytePerPixY; enum dml_odm_mode MainSurfaceODMMode; dml_uint_t surface_width_ub_l; dml_uint_t surface_height_ub_l; @@ -5029,6 +5045,7 @@ static void CalculateMaxDETAndMinCompressedBufferSize( dml_uint_t *nomDETInKByte, dml_uint_t *MinCompressedBufferSizeInKByte) { + (void)ROBBufferSizeInKByte; *MaxTotalDETInKByte = ConfigReturnBufferSizeInKByte - ConfigReturnBufferSegmentSizeInKByte; *nomDETInKByte = (dml_uint_t)(dml_floor((dml_float_t) *MaxTotalDETInKByte / (dml_float_t) MaxNumDPP, ConfigReturnBufferSegmentSizeInKByte)); *MinCompressedBufferSizeInKByte = ConfigReturnBufferSizeInKByte - *MaxTotalDETInKByte; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c index 4022f91193ed..b2fada6c44c3 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c @@ -178,6 +178,7 @@ dml_float_t dml_log2(dml_float_t x) dml_float_t dml_round(dml_float_t val, dml_bool_t bankers_rounding) { + (void)bankers_rounding; // if (bankers_rounding) // return (dml_float_t) lrint(val); // else { @@ -217,6 +218,7 @@ dml_uint_t dml_round_to_multiple(dml_uint_t num, dml_uint_t multiple, dml_bool_t void dml_print_data_rq_regs_st(const dml_display_plane_rq_regs_st *rq_regs) { + (void)rq_regs; dml_print("DML: ===================================== \n"); dml_print("DML: DISPLAY_PLANE_RQ_REGS_ST\n"); dml_print("DML: chunk_size = 0x%x\n", rq_regs->chunk_size); @@ -248,6 +250,7 @@ void dml_print_rq_regs_st(const dml_display_rq_regs_st *rq_regs) void dml_print_dlg_regs_st(const dml_display_dlg_regs_st *dlg_regs) { + (void)dlg_regs; dml_print("DML: ===================================== \n"); dml_print("DML: DISPLAY_DLG_REGS_ST \n"); dml_print("DML: refcyc_h_blank_end = 0x%x\n", dlg_regs->refcyc_h_blank_end); @@ -299,6 +302,7 @@ void dml_print_dlg_regs_st(const dml_display_dlg_regs_st *dlg_regs) void dml_print_ttu_regs_st(const dml_display_ttu_regs_st *ttu_regs) { + (void)ttu_regs; dml_print("DML: ===================================== \n"); dml_print("DML: DISPLAY_TTU_REGS_ST \n"); dml_print("DML: qos_level_low_wm = 0x%x\n", ttu_regs->qos_level_low_wm); @@ -326,6 +330,7 @@ void dml_print_ttu_regs_st(const dml_display_ttu_regs_st *ttu_regs) void dml_print_dml_policy(const struct dml_mode_eval_policy_st *policy) { + (void)policy; dml_print("DML: ===================================== \n"); dml_print("DML: DML_MODE_EVAL_POLICY_ST\n"); dml_print("DML: Policy: UseUnboundedRequesting = 0x%x\n", policy->UseUnboundedRequesting); @@ -353,6 +358,8 @@ void dml_print_dml_policy(const struct dml_mode_eval_policy_st *policy) void dml_print_mode_support(struct display_mode_lib_st *mode_lib, dml_uint_t j) { + (void)j; + (void)mode_lib; dml_print("DML: MODE SUPPORT: ===============================================\n"); dml_print("DML: MODE SUPPORT: Voltage State %d\n", j); dml_print("DML: MODE SUPPORT: Mode Supported : %s\n", mode_lib->ms.support.ModeSupport[j] == true ? "Supported" : "NOT Supported"); @@ -526,6 +533,7 @@ void dml_print_dml_mode_support_info(const struct dml_mode_support_info_st *supp void dml_print_dml_display_cfg_timing(const struct dml_timing_cfg_st *timing, dml_uint_t num_plane) { + (void)timing; for (dml_uint_t i = 0; i < num_plane; i++) { dml_print("DML: timing_cfg: plane=%d, HTotal = %d\n", i, timing->HTotal[i]); dml_print("DML: timing_cfg: plane=%d, VTotal = %d\n", i, timing->VTotal[i]); @@ -542,6 +550,7 @@ void dml_print_dml_display_cfg_timing(const struct dml_timing_cfg_st *timing, dm void dml_print_dml_display_cfg_plane(const struct dml_plane_cfg_st *plane, dml_uint_t num_plane) { + (void)plane; dml_print("DML: plane_cfg: num_plane = %d\n", num_plane); dml_print("DML: plane_cfg: GPUVMEnable = %d\n", plane->GPUVMEnable); dml_print("DML: plane_cfg: HostVMEnable = %d\n", plane->HostVMEnable); @@ -590,6 +599,7 @@ void dml_print_dml_display_cfg_plane(const struct dml_plane_cfg_st *plane, dml_u void dml_print_dml_display_cfg_surface(const struct dml_surface_cfg_st *surface, dml_uint_t num_plane) { + (void)surface; for (dml_uint_t i = 0; i < num_plane; i++) { dml_print("DML: surface_cfg: plane=%d, PitchY = %d\n", i, surface->PitchY[i]); dml_print("DML: surface_cfg: plane=%d, SurfaceWidthY = %d\n", i, surface->SurfaceWidthY[i]); @@ -609,6 +619,7 @@ void dml_print_dml_display_cfg_surface(const struct dml_surface_cfg_st *surface, void dml_print_dml_display_cfg_hw_resource(const struct dml_hw_resource_st *hw, dml_uint_t num_plane) { + (void)hw; for (dml_uint_t i = 0; i < num_plane; i++) { dml_print("DML: hw_resource: plane=%d, ODMMode = %d\n", i, hw->ODMMode[i]); dml_print("DML: hw_resource: plane=%d, DPPPerSurface = %d\n", i, hw->DPPPerSurface[i]); @@ -620,6 +631,7 @@ void dml_print_dml_display_cfg_hw_resource(const struct dml_hw_resource_st *hw, __DML_DLL_EXPORT__ void dml_print_soc_state_bounding_box(const struct soc_state_bounding_box_st *state) { + (void)state; dml_print("DML: state_bbox: socclk_mhz = %f\n", state->socclk_mhz); dml_print("DML: state_bbox: dscclk_mhz = %f\n", state->dscclk_mhz); dml_print("DML: state_bbox: phyclk_mhz = %f\n", state->phyclk_mhz); @@ -649,6 +661,7 @@ __DML_DLL_EXPORT__ void dml_print_soc_state_bounding_box(const struct soc_state_ __DML_DLL_EXPORT__ void dml_print_soc_bounding_box(const struct soc_bounding_box_st *soc) { + (void)soc; dml_print("DML: soc_bbox: dprefclk_mhz = %f\n", soc->dprefclk_mhz); dml_print("DML: soc_bbox: xtalclk_mhz = %f\n", soc->xtalclk_mhz); dml_print("DML: soc_bbox: pcierefclk_mhz = %f\n", soc->pcierefclk_mhz); @@ -686,6 +699,7 @@ __DML_DLL_EXPORT__ void dml_print_soc_bounding_box(const struct soc_bounding_box __DML_DLL_EXPORT__ void dml_print_clk_cfg(const struct dml_clk_cfg_st *clk_cfg) { + (void)clk_cfg; dml_print("DML: clk_cfg: 0-use_required, 1-use pipe.clks_cfg, 2-use state bbox\n"); dml_print("DML: clk_cfg: dcfclk_option = %d\n", clk_cfg->dcfclk_option); dml_print("DML: clk_cfg: dispclk_option = %d\n", clk_cfg->dispclk_option); diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c index 2f0e0048bea8..476030193f14 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c @@ -389,7 +389,9 @@ static void populate_dml21_dummy_surface_cfg(struct dml2_surface_cfg *surface, c surface->tiling = dml2_sw_64kb_2d; } -static void populate_dml21_dummy_plane_cfg(struct dml2_plane_parameters *plane, const struct dc_stream_state *stream) +static void populate_dml21_dummy_plane_cfg(struct dml2_plane_parameters *plane, + const struct dc_stream_state *stream, + const struct dml2_soc_bb *soc_bb) { unsigned int width, height; @@ -433,7 +435,8 @@ static void populate_dml21_dummy_plane_cfg(struct dml2_plane_parameters *plane, plane->pixel_format = dml2_444_32; plane->dynamic_meta_data.enable = false; - plane->overrides.gpuvm_min_page_size_kbytes = 256; + plane->overrides.gpuvm_min_page_size_kbytes = soc_bb->gpuvm_min_page_size_kbytes; + plane->overrides.hostvm_min_page_size_kbytes = soc_bb->hostvm_min_page_size_kbytes; } static void populate_dml21_surface_config_from_plane_state( @@ -441,6 +444,7 @@ static void populate_dml21_surface_config_from_plane_state( struct dml2_surface_cfg *surface, const struct dc_plane_state *plane_state) { + (void)in_dc; surface->plane0.pitch = plane_state->plane_size.surface_pitch; surface->plane1.pitch = plane_state->plane_size.chroma_pitch; surface->plane0.height = plane_state->plane_size.surface_size.height; @@ -503,7 +507,7 @@ static const struct scaler_data *get_scaler_data_for_plane( static void populate_dml21_plane_config_from_plane_state(struct dml2_context *dml_ctx, struct dml2_plane_parameters *plane, const struct dc_plane_state *plane_state, - const struct dc_state *context, unsigned int stream_index) + const struct dc_state *context, unsigned int stream_index, const struct dml2_soc_bb *soc_bb) { const struct scaler_data *scaler_data = get_scaler_data_for_plane(dml_ctx, plane_state, context); struct dc_stream_state *stream = context->streams[stream_index]; @@ -647,7 +651,8 @@ static void populate_dml21_plane_config_from_plane_state(struct dml2_context *dm plane->composition.rotation_angle = (enum dml2_rotation_angle) plane_state->rotation; plane->stream_index = stream_index; - plane->overrides.gpuvm_min_page_size_kbytes = 256; + plane->overrides.gpuvm_min_page_size_kbytes = soc_bb->gpuvm_min_page_size_kbytes; + plane->overrides.hostvm_min_page_size_kbytes = soc_bb->hostvm_min_page_size_kbytes; plane->immediate_flip = plane_state->flip_immediate; @@ -785,7 +790,9 @@ bool dml21_map_dc_state_into_dml_display_cfg(const struct dc *in_dc, struct dc_s if (context->stream_status[stream_index].plane_count == 0) { disp_cfg_plane_location = dml_dispcfg->num_planes++; populate_dml21_dummy_surface_cfg(&dml_dispcfg->plane_descriptors[disp_cfg_plane_location].surface, context->streams[stream_index]); - populate_dml21_dummy_plane_cfg(&dml_dispcfg->plane_descriptors[disp_cfg_plane_location], context->streams[stream_index]); + populate_dml21_dummy_plane_cfg( + &dml_dispcfg->plane_descriptors[disp_cfg_plane_location], + context->streams[stream_index], &dml_ctx->v21.dml_init.soc_bb); dml_dispcfg->plane_descriptors[disp_cfg_plane_location].stream_index = disp_cfg_stream_location; } else { for (plane_index = 0; plane_index < context->stream_status[stream_index].plane_count; plane_index++) { @@ -797,7 +804,10 @@ bool dml21_map_dc_state_into_dml_display_cfg(const struct dc *in_dc, struct dc_s ASSERT(disp_cfg_plane_location >= 0 && disp_cfg_plane_location < __DML2_WRAPPER_MAX_STREAMS_PLANES__); populate_dml21_surface_config_from_plane_state(in_dc, &dml_dispcfg->plane_descriptors[disp_cfg_plane_location].surface, context->stream_status[stream_index].plane_states[plane_index]); - populate_dml21_plane_config_from_plane_state(dml_ctx, &dml_dispcfg->plane_descriptors[disp_cfg_plane_location], context->stream_status[stream_index].plane_states[plane_index], context, stream_index); + populate_dml21_plane_config_from_plane_state( + dml_ctx, &dml_dispcfg->plane_descriptors[disp_cfg_plane_location], + context->stream_status[stream_index].plane_states[plane_index], + context, stream_index, &dml_ctx->v21.dml_init.soc_bb); dml_dispcfg->plane_descriptors[disp_cfg_plane_location].stream_index = disp_cfg_stream_location; if (dml21_wrapper_get_plane_id(context, context->streams[stream_index]->stream_id, context->stream_status[stream_index].plane_states[plane_index], &dml_ctx->v21.dml_to_dc_pipe_mapping.disp_cfg_to_plane_id[disp_cfg_plane_location])) @@ -873,6 +883,7 @@ static struct dml2_dchub_watermark_regs *wm_set_index_to_dc_wm_set(union dcn_wat void dml21_extract_watermark_sets(const struct dc *in_dc, union dcn_watermark_set *watermarks, struct dml2_context *in_ctx) { + (void)in_dc; const struct dml2_display_cfg_programming *programming = in_ctx->v21.mode_programming.programming; unsigned int wm_index; @@ -907,6 +918,7 @@ void dml21_get_pipe_mcache_config( struct dml2_per_plane_programming *pln_prog, struct dml2_pipe_configuration_descriptor *mcache_pipe_config) { + (void)context; mcache_pipe_config->plane0.viewport_x_start = pipe_ctx->plane_res.scl_data.viewport.x; mcache_pipe_config->plane0.viewport_width = pipe_ctx->plane_res.scl_data.viewport.width; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c index 4724b08c77e1..732de97335fa 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c @@ -88,6 +88,7 @@ int dml21_find_dc_pipes_for_plane(const struct dc *in_dc, struct pipe_ctx *dc_phantom_pipes[__DML2_WRAPPER_MAX_STREAMS_PLANES__], int dml_plane_idx) { + (void)in_dc; unsigned int dml_stream_index; unsigned int main_stream_id; unsigned int dc_plane_index; @@ -282,6 +283,7 @@ static struct dc_plane_state *dml21_add_phantom_plane(struct dml2_context *dml_c struct dc_plane_state *main_plane, struct dml2_per_plane_programming *plane_programming) { + (void)plane_programming; struct dc_plane_state *phantom_plane; phantom_plane = dml_ctx->config.svp_pstate.callbacks.create_phantom_plane(dc, context, main_plane); diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c index 7398f8b69adb..8bed59e976d1 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c @@ -58,8 +58,8 @@ bool dml21_create(const struct dc *in_dc, struct dml2_context **dml_ctx, const s void dml21_destroy(struct dml2_context *dml2) { - vfree(dml2->v21.dml_init.dml2_instance); - vfree(dml2->v21.mode_programming.programming); + DC_RUN_WITH_PREEMPTION_ENABLED(vfree(dml2->v21.dml_init.dml2_instance)); + DC_RUN_WITH_PREEMPTION_ENABLED(vfree(dml2->v21.mode_programming.programming)); } void dml21_copy(struct dml2_context *dst_dml_ctx, diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c index cc992af6ac9c..de40d7bae252 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c @@ -51,6 +51,8 @@ void dml21_reinit(const struct dc *in_dc, struct dml2_context *dml_ctx, const st static void dml21_calculate_rq_and_dlg_params(const struct dc *dc, struct dc_state *context, struct resource_context *out_new_hw_state, struct dml2_context *in_ctx, unsigned int pipe_cnt) { + (void)out_new_hw_state; + (void)pipe_cnt; unsigned int dml_prog_idx = 0, dc_pipe_index = 0, num_dpps_required = 0; struct dml2_per_plane_programming *pln_prog = NULL; struct dml2_per_stream_programming *stream_prog = NULL; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42_soc_bb.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42_soc_bb.h index c75778ea7a2c..040d89f6de35 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42_soc_bb.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42_soc_bb.h @@ -68,6 +68,7 @@ static const struct dml2_soc_qos_parameters dml_dcn42_variant_a_soc_qos_params = .qos_type = dml2_qos_param_type_dcn3, }; +/* Default SOC bounding box for DCN42 based on LPDDR5/LPCAMM2 latencies*/ static const struct dml2_soc_bb dml2_socbb_dcn42 = { .clk_table = { .wck_ratio = { @@ -185,12 +186,13 @@ static const struct dml2_soc_bb dml2_socbb_dcn42 = { .qos_type = dml2_qos_param_type_dcn3, }, + /* DCN42 params for LPDDR5/LPCAMM2 */ .power_management_parameters = { - .dram_clk_change_blackout_us = 29, + .dram_clk_change_blackout_us = 36, .fclk_change_blackout_us = 0, .g7_ppt_blackout_us = 0, - .stutter_enter_plus_exit_latency_us = 11, - .stutter_exit_latency_us = 9, + .stutter_enter_plus_exit_latency_us = 14, + .stutter_exit_latency_us = 12, .z8_stutter_enter_plus_exit_latency_us = 300, .z8_stutter_exit_latency_us = 200, }, @@ -203,12 +205,12 @@ static const struct dml2_soc_bb dml2_socbb_dcn42 = { .xtalclk_mhz = 24, .pcie_refclk_mhz = 100, .dchub_refclk_mhz = 50, - .mall_allocated_for_dcn_mbytes = 64, + .mall_allocated_for_dcn_mbytes = 0, .max_outstanding_reqs = 256, .fabric_datapath_to_dcn_data_return_bytes = 32, .return_bus_width_bytes = 64, .hostvm_min_page_size_kbytes = 4, - .gpuvm_min_page_size_kbytes = 256, + .gpuvm_min_page_size_kbytes = 4, .gpuvm_max_page_table_levels = 1, .hostvm_max_non_cached_page_table_levels = 2, .phy_downspread_percent = 0.38, @@ -222,6 +224,17 @@ static const struct dml2_soc_bb dml2_socbb_dcn42 = { .max_fclk_for_uclk_dpm_khz = 2200 * 1000, }; +/* DCN42 params for DDR5 */ +struct dml2_soc_power_management_parameters dcn42_ddr5_power_management_parameters = { + .dram_clk_change_blackout_us = 36, + .fclk_change_blackout_us = 0, + .g7_ppt_blackout_us = 0, + .stutter_enter_plus_exit_latency_us = 23.5, + .stutter_exit_latency_us = 21.5, + .z8_stutter_enter_plus_exit_latency_us = 300, + .z8_stutter_exit_latency_us = 200, +}; + static const struct dml2_ip_capabilities dml2_dcn42_max_ip_caps = { .pipe_count = 4, .otg_count = 4, @@ -234,7 +247,7 @@ static const struct dml2_ip_capabilities dml2_dcn42_max_ip_caps = { .config_return_buffer_segment_size_in_kbytes = 64, .meta_fifo_size_in_kentries = 32, .compressed_buffer_segment_size_in_kbytes = 64, - .cursor_buffer_size = 24, + .cursor_buffer_size = 42, .max_flip_time_us = 110, .max_flip_time_lines = 50, .hostvm_mode = 0, diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h index 4e9abe1a568d..79dfba54344c 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h @@ -26,20 +26,6 @@ enum dml2_swizzle_mode { dml2_gfx11_sw_64kb_r_x, dml2_gfx11_sw_256kb_d_x, dml2_gfx11_sw_256kb_r_x, - - dml2_sw_linear_256b, // GFX10 SW_LINEAR only accepts 256 byte aligned pitch - dml2_gfx10_sw_64kb_r_x, - dml2_gfx102_sw_64kb_s, - dml2_gfx102_sw_64kb_s_t, - dml2_gfx102_sw_64kb_s_x, - dml2_gfx102_sw_64kb_r_x, - - dml2_linear_64elements, // GFX7 LINEAR_ALIGNED accepts pitch alignment of the maximum of 64 elements or 256 bytes - dml2_gfx7_1d_thin, - dml2_gfx7_2d_thin_gen_zero, - dml2_gfx7_2d_thin_gen_one, - dml2_gfx7_2d_thin_arlene, - dml2_gfx7_2d_thin_anubis }; enum dml2_source_format_class { diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c index 99fc2f0666e2..858e7bbc511f 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c @@ -135,7 +135,7 @@ struct dml2_core_ip_params core_dcn42_ip_caps_base = { .cursor_64bpp_support = true, .dynamic_metadata_vm_enabled = false, - .max_num_hdmi_frl_outputs = 0, + .max_num_hdmi_frl_outputs = 1, .max_num_dp2p0_outputs = 2, .max_num_dp2p0_streams = 4, .imall_supported = 1, @@ -155,7 +155,7 @@ struct dml2_core_ip_params core_dcn42_ip_caps_base = { .min_meta_chunk_size_bytes = 256, .dchub_arb_to_ret_delay = 102, - .hostvm_mode = 1, + .hostvm_mode = 0, }; static void patch_ip_caps_with_explicit_ip_params(struct dml2_ip_capabilities *ip_caps, const struct dml2_core_ip_params *ip_params) @@ -281,6 +281,7 @@ static void create_phantom_stream_from_main_stream(struct dml2_stream_parameters static void create_phantom_plane_from_main_plane(struct dml2_plane_parameters *phantom, const struct dml2_plane_parameters *main, const struct dml2_stream_parameters *phantom_stream, int phantom_stream_index, const struct dml2_stream_parameters *main_stream) { + (void)main_stream; memcpy(phantom, main, sizeof(struct dml2_plane_parameters)); phantom->stream_index = phantom_stream_index; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c index f6402e199354..827bd9143c87 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c @@ -840,6 +840,7 @@ static void CalculateSwathWidth( unsigned int swath_width_luma_ub[], // per-pipe unsigned int swath_width_chroma_ub[]) // per-pipe { + (void)BytePerPixY; enum dml2_odm_mode MainSurfaceODMMode; double odm_hactive_factor = 1.0; unsigned int req_width_horz_y; @@ -1283,6 +1284,8 @@ static double TruncToValidBPP( // Output unsigned int *RequiredSlots) { + (void)DSCInputBitPerComponent; + (void)RequiredSlots; double MaxLinkBPP; unsigned int MinDSCBPP; double MaxDSCBPP; @@ -1922,6 +1925,7 @@ static void CalculateRowBandwidth( double *dpte_row_bw, double *meta_row_bw) { + (void)use_one_row_for_frame; if (!DCCEnable || !mrq_present) { *meta_row_bw = 0; } else if (dml_is_420(SourcePixelFormat) || SourcePixelFormat == dml2_rgbe_alpha) { @@ -2020,6 +2024,11 @@ static void CalculateDCCConfiguration( unsigned int *IndependentBlockLuma, unsigned int *IndependentBlockChroma) { + (void)SurfaceWidthChroma; + (void)SurfaceHeightChroma; + (void)TilingFormat; + (void)BytePerPixelDETY; + (void)BytePerPixelDETC; unsigned int DETBufferSizeForDCC = nomDETInKByte * 1024; unsigned int segment_order_horz_contiguous_luma; @@ -2270,6 +2279,7 @@ static void calculate_mcache_row_bytes( struct dml2_core_internal_scratch *scratch, struct dml2_core_calcs_calculate_mcache_row_bytes_params *p) { + (void)scratch; unsigned int vmpg_bytes = 0; unsigned int blk_bytes = 0; float meta_per_mvmpg_per_channel = 0; @@ -3642,6 +3652,8 @@ static double CalculateWriteBackDelay( unsigned int WritebackSourceHeight, unsigned int HTotal) { + (void)WritebackPixelFormat; + (void)WritebackHRatio; double CalculateWriteBackDelay; double Line_length; double Output_lines_last_notclamped; @@ -3959,6 +3971,7 @@ static enum dml2_odm_mode DecideODMMode(unsigned int HActive, double SurfaceRequiredDISPCLKWithODMCombineThreeToOne, double SurfaceRequiredDISPCLKWithODMCombineFourToOne) { + (void)SurfaceRequiredDISPCLKWithODMCombineFourToOne; enum dml2_odm_mode MinimumRequiredODMModeForMaxDispClock; enum dml2_odm_mode MinimumRequiredODMModeForMaxDSCHActive; enum dml2_odm_mode MinimumRequiredODMModeForMax420HActive; @@ -4460,6 +4473,8 @@ static double CalculateWriteBackDISPCLK( unsigned int HTotal, unsigned int WritebackLineBufferSize) { + (void)WritebackPixelFormat; + (void)WritebackVRatio; double DISPCLK_H, DISPCLK_V, DISPCLK_HB; DISPCLK_H = PixelClock * math_ceil2((double)WritebackHTaps / 8.0, 1) / WritebackHRatio; @@ -4561,6 +4576,10 @@ static void CalculateSurfaceSizeInMall( unsigned int SurfaceSizeInMALL[], bool *ExceededMALLSize) { + (void)Read256BytesBlockWidthY; + (void)Read256BytesBlockWidthC; + (void)Read256BytesBlockHeightY; + (void)Read256BytesBlockHeightC; unsigned int TotalSurfaceSizeInMALLForSS = 0; unsigned int TotalSurfaceSizeInMALLForSubVP = 0; unsigned int MALLAllocatedForDCNInBytes = MALLAllocatedForDCN * 1024 * 1024; @@ -4620,6 +4639,7 @@ static void calculate_tdlut_setting( struct dml2_core_internal_scratch *scratch, struct dml2_core_calcs_calculate_tdlut_setting_params *p) { + (void)scratch; // locals unsigned int tdlut_bpe = 8; unsigned int tdlut_width; @@ -6503,6 +6523,7 @@ static void CalculateFlipSchedule( double *final_flip_bw, bool *ImmediateFlipSupportedForPipe) { + (void)use_one_row_for_frame_flip; struct dml2_core_shared_CalculateFlipSchedule_locals *l = &s->CalculateFlipSchedule_locals; l->dual_plane = dml_is_420(SourcePixelFormat) || SourcePixelFormat == dml2_rgbe_alpha; @@ -7381,7 +7402,7 @@ static noinline_for_stack void dml_core_ms_prefetch_check(struct dml2_core_inter s->tdlut_bytes_per_group, s->HostVMInefficiencyFactor, s->HostVMInefficiencyFactorPrefetch, - mode_lib->soc.hostvm_min_page_size_kbytes, + mode_lib->soc.hostvm_min_page_size_kbytes * 1024, mode_lib->soc.qos_parameters.qos_type, !(display_cfg->overrides.max_outstanding_when_urgent_expected_disable), mode_lib->soc.max_outstanding_reqs, @@ -7477,12 +7498,11 @@ static noinline_for_stack void dml_core_ms_prefetch_check(struct dml2_core_inter CalculatePrefetchSchedule_params->OutputFormat = display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].output.output_format; CalculatePrefetchSchedule_params->MaxInterDCNTileRepeaters = mode_lib->ip.max_inter_dcn_tile_repeaters; CalculatePrefetchSchedule_params->VStartup = s->MaximumVStartup[k]; - CalculatePrefetchSchedule_params->HostVMMinPageSize = mode_lib->soc.hostvm_min_page_size_kbytes; + CalculatePrefetchSchedule_params->HostVMMinPageSize = mode_lib->soc.hostvm_min_page_size_kbytes * 1024; CalculatePrefetchSchedule_params->DynamicMetadataEnable = display_cfg->plane_descriptors[k].dynamic_meta_data.enable; CalculatePrefetchSchedule_params->DynamicMetadataVMEnabled = mode_lib->ip.dynamic_metadata_vm_enabled; CalculatePrefetchSchedule_params->DynamicMetadataLinesBeforeActiveRequired = display_cfg->plane_descriptors[k].dynamic_meta_data.lines_before_active_required; CalculatePrefetchSchedule_params->DynamicMetadataTransmittedBytes = display_cfg->plane_descriptors[k].dynamic_meta_data.transmitted_bytes; - CalculatePrefetchSchedule_params->UrgentLatency = mode_lib->ms.UrgLatency; CalculatePrefetchSchedule_params->ExtraLatencyPrefetch = mode_lib->ms.ExtraLatencyPrefetch; CalculatePrefetchSchedule_params->TCalc = mode_lib->ms.TimeCalc; CalculatePrefetchSchedule_params->vm_bytes = mode_lib->ms.vm_bytes[k]; @@ -8965,7 +8985,7 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out CalculateVMRowAndSwath_params->MALLAllocatedForDCN = mode_lib->soc.mall_allocated_for_dcn_mbytes; CalculateVMRowAndSwath_params->SwathWidthY = mode_lib->ms.SwathWidthY; CalculateVMRowAndSwath_params->SwathWidthC = mode_lib->ms.SwathWidthC; - CalculateVMRowAndSwath_params->HostVMMinPageSize = mode_lib->soc.hostvm_min_page_size_kbytes; + CalculateVMRowAndSwath_params->HostVMMinPageSize = mode_lib->soc.hostvm_min_page_size_kbytes * 1024; CalculateVMRowAndSwath_params->DCCMetaBufferSizeBytes = mode_lib->ip.dcc_meta_buffer_size_bytes; CalculateVMRowAndSwath_params->mrq_present = mode_lib->ip.dcn_mrq_present; @@ -9968,6 +9988,8 @@ static void CalculateVMGroupAndRequestTimes( double TimePerVMRequestVBlank[], double TimePerVMRequestFlip[]) { + (void)dpte_row_width_luma_ub; + (void)dpte_row_width_chroma_ub; unsigned int num_group_per_lower_vm_stage = 0; unsigned int num_req_per_lower_vm_stage = 0; unsigned int num_group_per_lower_vm_stage_flip; @@ -10755,7 +10777,7 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex CalculateVMRowAndSwath_params->MALLAllocatedForDCN = mode_lib->soc.mall_allocated_for_dcn_mbytes; CalculateVMRowAndSwath_params->SwathWidthY = mode_lib->mp.SwathWidthY; CalculateVMRowAndSwath_params->SwathWidthC = mode_lib->mp.SwathWidthC; - CalculateVMRowAndSwath_params->HostVMMinPageSize = mode_lib->soc.hostvm_min_page_size_kbytes; + CalculateVMRowAndSwath_params->HostVMMinPageSize = mode_lib->soc.hostvm_min_page_size_kbytes * 1024; CalculateVMRowAndSwath_params->DCCMetaBufferSizeBytes = mode_lib->ip.dcc_meta_buffer_size_bytes; CalculateVMRowAndSwath_params->mrq_present = mode_lib->ip.dcn_mrq_present; @@ -10971,7 +10993,7 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex s->tdlut_bytes_per_group, s->HostVMInefficiencyFactor, s->HostVMInefficiencyFactorPrefetch, - mode_lib->soc.hostvm_min_page_size_kbytes, + mode_lib->soc.hostvm_min_page_size_kbytes * 1024, mode_lib->soc.qos_parameters.qos_type, !(display_cfg->overrides.max_outstanding_when_urgent_expected_disable), mode_lib->soc.max_outstanding_reqs, @@ -11264,12 +11286,11 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex CalculatePrefetchSchedule_params->OutputFormat = display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].output.output_format; CalculatePrefetchSchedule_params->MaxInterDCNTileRepeaters = mode_lib->ip.max_inter_dcn_tile_repeaters; CalculatePrefetchSchedule_params->VStartup = s->MaxVStartupLines[k]; - CalculatePrefetchSchedule_params->HostVMMinPageSize = mode_lib->soc.hostvm_min_page_size_kbytes; + CalculatePrefetchSchedule_params->HostVMMinPageSize = mode_lib->soc.hostvm_min_page_size_kbytes * 1024; CalculatePrefetchSchedule_params->DynamicMetadataEnable = display_cfg->plane_descriptors[k].dynamic_meta_data.enable; CalculatePrefetchSchedule_params->DynamicMetadataVMEnabled = mode_lib->ip.dynamic_metadata_vm_enabled; CalculatePrefetchSchedule_params->DynamicMetadataLinesBeforeActiveRequired = display_cfg->plane_descriptors[k].dynamic_meta_data.lines_before_active_required; CalculatePrefetchSchedule_params->DynamicMetadataTransmittedBytes = display_cfg->plane_descriptors[k].dynamic_meta_data.transmitted_bytes; - CalculatePrefetchSchedule_params->UrgentLatency = mode_lib->mp.UrgentLatency; CalculatePrefetchSchedule_params->ExtraLatencyPrefetch = mode_lib->mp.ExtraLatencyPrefetch; CalculatePrefetchSchedule_params->TCalc = mode_lib->mp.TCalc; CalculatePrefetchSchedule_params->vm_bytes = mode_lib->mp.vm_bytes[k]; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h index 953f40fde1e1..080bc3c3d244 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h @@ -269,6 +269,9 @@ struct dml2_core_internal_mode_support_info { bool global_dram_clock_change_supported; bool global_fclk_change_supported; bool global_temp_read_or_ppt_supported; + bool fclk_pstate_schedule_admissible; + bool temp_read_pstate_schedule_admissible; + bool ppt_pstate_schedule_admissible; bool USRRetrainingSupport; bool AvgBandwidthSupport; bool UrgVactiveBandwidthSupport; @@ -1063,6 +1066,8 @@ struct dml2_core_calcs_mode_support_locals { bool dummy_boolean_array[2][DML2_MAX_PLANES]; double dummy_single[3]; double dummy_single_array[DML2_MAX_PLANES]; + double dummy_double_array[3][DML2_MAX_PLANES]; + enum dml2_pstate_method dummy_pstate_method_array[DML2_MAX_PLANES]; struct dml2_core_internal_watermarks dummy_watermark; double dummy_bw[dml2_core_internal_soc_state_max][dml2_core_internal_bw_max]; double surface_dummy_bw[dml2_core_internal_soc_state_max][dml2_core_internal_bw_max][DML2_MAX_PLANES]; @@ -1721,30 +1726,30 @@ struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_param double ReturnBW; bool SynchronizeTimings; bool SynchronizeDRRDisplaysForUCLKPStateChange; - unsigned int *dpte_group_bytes; + const unsigned int *dpte_group_bytes; struct dml2_core_internal_SOCParametersList mmSOCParameters; unsigned int WritebackChunkSize; double SOCCLK; double DCFClkDeepSleep; - unsigned int *DETBufferSizeY; - unsigned int *DETBufferSizeC; - unsigned int *SwathHeightY; - unsigned int *SwathHeightC; - unsigned int *SwathWidthY; - unsigned int *SwathWidthC; - unsigned int *DPPPerSurface; - double *BytePerPixelDETY; - double *BytePerPixelDETC; - unsigned int *DSTXAfterScaler; - unsigned int *DSTYAfterScaler; + const unsigned int *DETBufferSizeY; + const unsigned int *DETBufferSizeC; + const unsigned int *SwathHeightY; + const unsigned int *SwathHeightC; + const unsigned int *SwathWidthY; + const unsigned int *SwathWidthC; + const unsigned int *DPPPerSurface; + const double *BytePerPixelDETY; + const double *BytePerPixelDETC; + const unsigned int *DSTXAfterScaler; + const unsigned int *DSTYAfterScaler; bool UnboundedRequestEnabled; unsigned int CompressedBufferSizeInkByte; bool max_outstanding_when_urgent_expected; - unsigned int max_outstanding_requests; - unsigned int max_request_size_bytes; - unsigned int *meta_row_height_l; - unsigned int *meta_row_height_c; - enum dml2_pstate_method *uclk_pstate_switch_modes; + const unsigned int max_outstanding_requests; + const unsigned int max_request_size_bytes; + const unsigned int *meta_row_height_l; + const unsigned int *meta_row_height_c; + const enum dml2_pstate_method *uclk_pstate_switch_modes; // Output struct dml2_core_internal_watermarks *Watermark; @@ -1931,7 +1936,6 @@ struct dml2_core_calcs_CalculatePrefetchSchedule_params { bool DynamicMetadataVMEnabled; unsigned int DynamicMetadataLinesBeforeActiveRequired; unsigned int DynamicMetadataTransmittedBytes; - double UrgentLatency; double ExtraLatencyPrefetch; double TCalc; unsigned int vm_bytes; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c index 6930ba7ce5b7..4f5533dc0430 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c @@ -428,10 +428,6 @@ bool dml2_core_utils_is_phantom_pipe(const struct dml2_plane_parameters *plane_c unsigned int dml2_core_utils_get_tile_block_size_bytes(enum dml2_swizzle_mode sw_mode, unsigned int byte_per_pixel) { - if (dml2_core_utils_get_gfx_version(sw_mode) == 10 || dml2_core_utils_get_gfx_version(sw_mode) == 7) { - return dml2_core_utils_get_tile_block_size_bytes_backcompat(sw_mode, byte_per_pixel); - } - if (sw_mode == dml2_sw_linear) return 256; else if (sw_mode == dml2_sw_256b_2d) @@ -462,56 +458,14 @@ unsigned int dml2_core_utils_get_tile_block_size_bytes(enum dml2_swizzle_mode sw }; } -unsigned int dml2_core_utils_get_tile_block_size_bytes_backcompat(enum dml2_swizzle_mode sw_mode, unsigned int byte_per_pixel) -{ - if (sw_mode == dml2_sw_linear_256b) - return 256; - else if (sw_mode == dml2_gfx10_sw_64kb_r_x) - return 65536; - else if (sw_mode == dml2_gfx102_sw_64kb_s) - return 65536; - else if (sw_mode == dml2_gfx102_sw_64kb_s_t) - return 65536; - else if (sw_mode == dml2_gfx102_sw_64kb_s_x) - return 65536; - else if (sw_mode == dml2_gfx102_sw_64kb_r_x) - return 65536; - else if (sw_mode == dml2_linear_64elements) - return 256; - else if (sw_mode == dml2_gfx7_1d_thin) - return 256; - else if (sw_mode == dml2_gfx7_2d_thin_gen_zero) - return (128 * 64 * byte_per_pixel); - else if (sw_mode == dml2_gfx7_2d_thin_gen_one) - return (128 * 128 * byte_per_pixel); - else if (sw_mode == dml2_gfx7_2d_thin_arlene) - return (64 * 32 * byte_per_pixel); - else if (sw_mode == dml2_gfx7_2d_thin_anubis) - return (128 * 128 * byte_per_pixel); - else { - DML_ASSERT(0); - return 256; - }; -} - bool dml2_core_utils_get_segment_horizontal_contiguous(enum dml2_swizzle_mode sw_mode, unsigned int byte_per_pixel) { - if (dml2_core_utils_get_gfx_version(sw_mode) == 10 || dml2_core_utils_get_gfx_version(sw_mode) == 7) { - return dml2_core_utils_get_segment_horizontal_contiguous_backcompat(sw_mode, byte_per_pixel); - } else { - return (byte_per_pixel != 2); - } -} - -bool dml2_core_utils_get_segment_horizontal_contiguous_backcompat(enum dml2_swizzle_mode sw_mode, unsigned int byte_per_pixel) -{ - return !((byte_per_pixel == 4) && - ((sw_mode == dml2_gfx10_sw_64kb_r_x) || (sw_mode == dml2_gfx102_sw_64kb_s) || (sw_mode == dml2_gfx102_sw_64kb_s_t) || (sw_mode == dml2_gfx102_sw_64kb_s_x))); + return (byte_per_pixel != 2); } bool dml2_core_utils_is_linear(enum dml2_swizzle_mode sw_mode) { - return (sw_mode == dml2_sw_linear || sw_mode == dml2_sw_linear_256b || sw_mode == dml2_linear_64elements); + return sw_mode == dml2_sw_linear; }; @@ -544,20 +498,6 @@ int unsigned dml2_core_utils_get_gfx_version(enum dml2_swizzle_mode sw_mode) sw_mode == dml2_gfx11_sw_256kb_d_x || sw_mode == dml2_gfx11_sw_256kb_r_x) version = 11; - else if (sw_mode == dml2_sw_linear_256b || - sw_mode == dml2_gfx10_sw_64kb_r_x || - sw_mode == dml2_gfx102_sw_64kb_s || - sw_mode == dml2_gfx102_sw_64kb_s_t || - sw_mode == dml2_gfx102_sw_64kb_s_x || - sw_mode == dml2_gfx102_sw_64kb_r_x) - version = 10; - else if (sw_mode == dml2_linear_64elements || - sw_mode == dml2_gfx7_1d_thin || - sw_mode == dml2_gfx7_2d_thin_gen_zero || - sw_mode == dml2_gfx7_2d_thin_gen_one || - sw_mode == dml2_gfx7_2d_thin_arlene || - sw_mode == dml2_gfx7_2d_thin_anubis) - version = 7; else { DML_LOG_VERBOSE("ERROR: Invalid sw_mode setting! val=%u\n", sw_mode); DML_ASSERT(0); @@ -648,6 +588,7 @@ static void create_phantom_stream_from_main_stream(struct dml2_stream_parameters static void create_phantom_plane_from_main_plane(struct dml2_plane_parameters *phantom, const struct dml2_plane_parameters *main, const struct dml2_stream_parameters *phantom_stream, int phantom_stream_index, const struct dml2_stream_parameters *main_stream) { + (void)main_stream; memcpy(phantom, main, sizeof(struct dml2_plane_parameters)); phantom->stream_index = phantom_stream_index; @@ -845,3 +786,11 @@ bool dml2_core_utils_is_odm_split(enum dml2_odm_mode odm_mode) return false; } } + +double dml2_core_utils_get_frame_time_us(const struct dml2_stream_parameters *stream) +{ + double otg_vline_time_us = (double)stream->timing.h_total / (double)stream->timing.pixel_clock_khz * 1000.0; + double non_vtotal = stream->timing.vblank_nom + stream->timing.v_active; + double frame_time_us = non_vtotal * otg_vline_time_us; + return frame_time_us; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.h index 471e73ed671c..60fa2abfef85 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.h @@ -22,8 +22,6 @@ void dml2_core_utils_pipe_plane_mapping(const struct core_display_cfg_support_in bool dml2_core_utils_is_phantom_pipe(const struct dml2_plane_parameters *plane_cfg); unsigned int dml2_core_utils_get_tile_block_size_bytes(enum dml2_swizzle_mode sw_mode, unsigned int byte_per_pixel); bool dml2_core_utils_get_segment_horizontal_contiguous(enum dml2_swizzle_mode sw_mode, unsigned int byte_per_pixel); -unsigned int dml2_core_utils_get_tile_block_size_bytes_backcompat(enum dml2_swizzle_mode sw_mode, unsigned int byte_per_pixel); -bool dml2_core_utils_get_segment_horizontal_contiguous_backcompat(enum dml2_swizzle_mode sw_mode, unsigned int byte_per_pixel); bool dml2_core_utils_is_vertical_rotation(enum dml2_rotation_angle Scan); bool dml2_core_utils_is_linear(enum dml2_swizzle_mode sw_mode); int unsigned dml2_core_utils_get_gfx_version(enum dml2_swizzle_mode sw_mode); @@ -41,5 +39,6 @@ bool dml2_core_utils_is_hpo_dp_encoder(const struct dml2_stream_parameters *stre bool dml2_core_utils_is_dp_8b_10b_link_rate(enum dml2_output_link_dp_rate rate); bool dml2_core_utils_is_dp_128b_132b_link_rate(enum dml2_output_link_dp_rate rate); bool dml2_core_utils_is_odm_split(enum dml2_odm_mode odm_mode); +double dml2_core_utils_get_frame_time_us(const struct dml2_stream_parameters *stream); #endif /* __DML2_CORE_UTILS_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c index ab0b4a4b5d65..5ffe211a6643 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c @@ -552,6 +552,7 @@ static int get_displays_without_vactive_margin_mask(struct dml2_dpmm_map_mode_to static int get_displays_with_fams_mask(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out, int latency_hiding_requirement_us) { + (void)latency_hiding_requirement_us; unsigned int i; int displays_with_fams_mask = 0x0; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.c index 1f2d9e97f5fd..39965ff2e111 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.c @@ -8,11 +8,13 @@ static bool dummy_map_mode_to_soc_dpm(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out) { + (void)in_out; return true; } static bool dummy_map_watermarks(struct dml2_dpmm_map_watermarks_params_in_out *in_out) { + (void)in_out; return true; } diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.c index 3dcd2c250633..fb0b0ac547c7 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.c @@ -9,6 +9,7 @@ static bool dummy_build_min_clock_table(struct dml2_mcg_build_min_clock_table_params_in_out *in_out) { + (void)in_out; return true; } diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.c new file mode 100644 index 000000000000..30fd5efe4b87 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. + +#include "dml2_pmo_dcn42.h" +#include "lib_float_math.h" +#include "dml2_debug.h" +#include "dml2_pmo_dcn4_fams2.h" + +/* + * DCN42 PMO Policy Implementation + * This implementation provides VBlank-only strategies for 1, 2, 3, and 4 display + * configurations, ensuring p-state watermark support in the blank period only. + */ + +static const struct dml2_pmo_pstate_strategy dcn42_strategy_list_1_display[] = { + // VBlank only + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_na, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, +}; + +static const int dcn42_strategy_list_1_display_size = sizeof(dcn42_strategy_list_1_display) / sizeof(struct dml2_pmo_pstate_strategy); + +static const struct dml2_pmo_pstate_strategy dcn42_strategy_list_2_display[] = { + // VBlank only for both displays + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_na, dml2_pstate_method_na }, + .allow_state_increase = true, + }, +}; + +static const int dcn42_strategy_list_2_display_size = sizeof(dcn42_strategy_list_2_display) / sizeof(struct dml2_pmo_pstate_strategy); + +static const struct dml2_pmo_pstate_strategy dcn42_strategy_list_3_display[] = { + // VBlank only for all three displays + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_na }, + .allow_state_increase = true, + }, +}; + +static const int dcn42_strategy_list_3_display_size = sizeof(dcn42_strategy_list_3_display) / sizeof(struct dml2_pmo_pstate_strategy); + +static const struct dml2_pmo_pstate_strategy dcn42_strategy_list_4_display[] = { + // VBlank only for all four displays + { + .per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank }, + .allow_state_increase = true, + }, +}; + +static const int dcn42_strategy_list_4_display_size = sizeof(dcn42_strategy_list_4_display) / sizeof(struct dml2_pmo_pstate_strategy); + +bool pmo_dcn42_test_for_pstate_support(struct dml2_pmo_test_for_pstate_support_in_out *in_out) +{ + const struct dml2_pmo_scratch *s = &in_out->instance->scratch; + const int REQUIRED_RESERVED_TIME = + (int)in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us; + bool p_state_supported = true; + unsigned int stream_index; + + if (in_out->base_display_config->display_config.overrides.all_streams_blanked) + return true; + + if (s->pmo_dcn4.cur_pstate_candidate < 0) + return false; + + for (stream_index = 0; stream_index < in_out->base_display_config->display_config.num_streams; stream_index++) { + if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_vblank) { + if (dcn4_get_minimum_reserved_time_us_for_planes(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < REQUIRED_RESERVED_TIME || + dcn4_get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) > 0) { + p_state_supported = false; + break; + } + } else { + p_state_supported = false; + break; + } + } + + return p_state_supported; +} + +bool pmo_dcn42_initialize(struct dml2_pmo_initialize_in_out *in_out) +{ + int i = 0; + struct dml2_pmo_instance *pmo = in_out->instance; + + unsigned int base_list_size = 0; + const struct dml2_pmo_pstate_strategy *base_list = NULL; + unsigned int *expanded_list_size = NULL; + struct dml2_pmo_pstate_strategy *expanded_list = NULL; + + DML_LOG_COMP_IF_ENTER(); + + pmo->soc_bb = in_out->soc_bb; + pmo->ip_caps = in_out->ip_caps; + pmo->mpc_combine_limit = 2; + pmo->odm_combine_limit = 4; + pmo->mcg_clock_table_size = in_out->mcg_clock_table_size; + + /* + * DCN42 does not support FAMS features like SubVP and DRR. + * These parameters are initialized to safe values but won't be used + * since our strategies only use VBlank. + */ + pmo->fams_params.v2.subvp.refresh_rate_limit_max = 0; + pmo->fams_params.v2.subvp.refresh_rate_limit_min = 0; + pmo->fams_params.v2.drr.refresh_rate_limit_max = 0; + pmo->fams_params.v2.drr.refresh_rate_limit_min = 0; + + pmo->options = in_out->options; + + /* Generate permutations of p-state configs from base strategy list */ + for (i = 0; i < PMO_DCN4_MAX_DISPLAYS; i++) { + switch (i+1) { + case 1: + if (pmo->options->override_strategy_lists[i] && pmo->options->num_override_strategies_per_list[i]) { + base_list = pmo->options->override_strategy_lists[i]; + base_list_size = pmo->options->num_override_strategies_per_list[i]; + } else { + base_list = dcn42_strategy_list_1_display; + base_list_size = dcn42_strategy_list_1_display_size; + } + + expanded_list_size = &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i]; + expanded_list = pmo->init_data.pmo_dcn4.expanded_strategy_list_1_display; + + break; + case 2: + if (pmo->options->override_strategy_lists[i] && pmo->options->num_override_strategies_per_list[i]) { + base_list = pmo->options->override_strategy_lists[i]; + base_list_size = pmo->options->num_override_strategies_per_list[i]; + } else { + base_list = dcn42_strategy_list_2_display; + base_list_size = dcn42_strategy_list_2_display_size; + } + + expanded_list_size = &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i]; + expanded_list = pmo->init_data.pmo_dcn4.expanded_strategy_list_2_display; + + break; + case 3: + if (pmo->options->override_strategy_lists[i] && pmo->options->num_override_strategies_per_list[i]) { + base_list = pmo->options->override_strategy_lists[i]; + base_list_size = pmo->options->num_override_strategies_per_list[i]; + } else { + base_list = dcn42_strategy_list_3_display; + base_list_size = dcn42_strategy_list_3_display_size; + } + + expanded_list_size = &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i]; + expanded_list = pmo->init_data.pmo_dcn4.expanded_strategy_list_3_display; + + break; + case 4: + if (pmo->options->override_strategy_lists[i] && pmo->options->num_override_strategies_per_list[i]) { + base_list = pmo->options->override_strategy_lists[i]; + base_list_size = pmo->options->num_override_strategies_per_list[i]; + } else { + base_list = dcn42_strategy_list_4_display; + base_list_size = dcn42_strategy_list_4_display_size; + } + + expanded_list_size = &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i]; + expanded_list = pmo->init_data.pmo_dcn4.expanded_strategy_list_4_display; + + break; + } + + DML_ASSERT(base_list_size <= PMO_DCN4_MAX_BASE_STRATEGIES); + + /* + * Populate list using DCN4 FAMS2 expansion function. + * Since our strategies only contain VBlank methods, the expansion + * will not introduce any FAMS-specific logic. + */ + pmo_dcn4_fams2_expand_base_pstate_strategies( + base_list, + base_list_size, + i + 1, + expanded_list, + expanded_list_size); + } + + DML_LOG_DEBUG("%s exit with true\n", __func__); + DML_LOG_COMP_IF_EXIT(); + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.h new file mode 100644 index 000000000000..31ba8575351d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#ifndef __DML2_PMO_DCN42_H__ +#define __DML2_PMO_DCN42_H__ + +#include "dml2_internal_shared_types.h" + +struct dml2_pmo_initialize_in_out; +struct dml2_pmo_test_for_pstate_support_in_out; + +bool pmo_dcn42_initialize(struct dml2_pmo_initialize_in_out *in_out); +bool pmo_dcn42_test_for_pstate_support(struct dml2_pmo_test_for_pstate_support_in_out *in_out); + +#endif /* __DML2_PMO_DCN42_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c index e8691983c0eb..b348c65a0f75 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c @@ -428,6 +428,7 @@ static void insert_strategy_into_expanded_list( struct dml2_pmo_pstate_strategy *expanded_strategy_list, unsigned int *num_expanded_strategies) { + (void)stream_count; if (expanded_strategy_list && num_expanded_strategies) { memcpy(&expanded_strategy_list[*num_expanded_strategies], per_stream_pstate_strategy, sizeof(struct dml2_pmo_pstate_strategy)); @@ -520,6 +521,7 @@ static bool is_variant_method_valid(const struct dml2_pmo_pstate_strategy *base_ const unsigned int num_streams_per_variant_method[PMO_DCN4_MAX_DISPLAYS], const unsigned int stream_count) { + (void)variant_strategy; bool valid = true; unsigned int i; @@ -1180,6 +1182,7 @@ static bool all_timings_support_svp(const struct dml2_pmo_instance *pmo, static void insert_into_candidate_list(const struct dml2_pmo_pstate_strategy *pstate_strategy, int stream_count, struct dml2_pmo_scratch *scratch) { + (void)stream_count; scratch->pmo_dcn4.pstate_strategy_candidates[scratch->pmo_dcn4.num_pstate_candidates] = *pstate_strategy; scratch->pmo_dcn4.num_pstate_candidates++; } @@ -1659,7 +1662,7 @@ static bool validate_pstate_support_strategy_cofunctionality(struct dml2_pmo_ins return is_config_schedulable(pmo, display_cfg, pstate_strategy); } -static int get_vactive_pstate_margin(const struct display_configuation_with_meta *display_cfg, int plane_mask) +int dcn4_get_vactive_pstate_margin(const struct display_configuation_with_meta *display_cfg, int plane_mask) { unsigned int i; int min_vactive_margin_us = 0xFFFFFFF; @@ -1847,6 +1850,7 @@ static void build_subvp_meta_per_stream(struct dml2_pmo_instance *pmo, struct display_configuation_with_meta *display_config, int stream_index) { + (void)display_config; struct dml2_implicit_svp_meta *stream_svp_meta = &pmo->scratch.pmo_dcn4.stream_svp_meta[stream_index]; struct dml2_pstate_meta *stream_pstate_meta = &pmo->scratch.pmo_dcn4.stream_pstate_meta[stream_index]; @@ -1903,7 +1907,7 @@ bool pmo_dcn4_fams2_init_for_pstate_support(struct dml2_pmo_init_for_pstate_supp // Figure out which streams can do vactive, and also build up implicit SVP and FAMS2 meta for (stream_index = 0; stream_index < display_config->display_config.num_streams; stream_index++) { - if (get_vactive_pstate_margin(display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) >= (int)(MIN_VACTIVE_MARGIN_PCT * pmo->soc_bb->power_management_parameters.dram_clk_change_blackout_us)) + if (dcn4_get_vactive_pstate_margin(display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) >= (int)(MIN_VACTIVE_MARGIN_PCT * pmo->soc_bb->power_management_parameters.dram_clk_change_blackout_us)) set_bit_in_bitfield(&s->pmo_dcn4.stream_vactive_capability_mask, stream_index); /* FAMS2 meta */ @@ -1990,6 +1994,7 @@ static void setup_planes_for_drr_by_mask(struct display_configuation_with_meta * struct dml2_pmo_instance *pmo, int plane_mask) { + (void)pmo; unsigned int plane_index; struct dml2_plane_parameters *plane; @@ -2177,7 +2182,9 @@ static bool setup_display_config(struct display_configuation_with_meta *display_ return success; } -static int get_minimum_reserved_time_us_for_planes(struct display_configuation_with_meta *display_config, int plane_mask) +int dcn4_get_minimum_reserved_time_us_for_planes( + const struct display_configuation_with_meta *display_config, + int plane_mask) { int min_time_us = 0xFFFFFF; unsigned int plane_index = 0; @@ -2217,16 +2224,16 @@ bool pmo_dcn4_fams2_test_for_pstate_support(struct dml2_pmo_test_for_pstate_supp if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_vactive || s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vactive_drr) { - if (get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < (MIN_VACTIVE_MARGIN_PCT * in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us) || + if (dcn4_get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < (MIN_VACTIVE_MARGIN_PCT * in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us) || get_vactive_det_fill_latency_delay_us(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) > stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us) { p_state_supported = false; break; } } else if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_vblank || s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vblank_drr) { - if (get_minimum_reserved_time_us_for_planes(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < + if (dcn4_get_minimum_reserved_time_us_for_planes(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < REQUIRED_RESERVED_TIME || - get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < MIN_VACTIVE_MARGIN_VBLANK) { + dcn4_get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < MIN_VACTIVE_MARGIN_VBLANK) { p_state_supported = false; break; } @@ -2238,7 +2245,7 @@ bool pmo_dcn4_fams2_test_for_pstate_support(struct dml2_pmo_test_for_pstate_supp } } else if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_drr) { if (!all_planes_match_method(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index], dml2_pstate_method_fw_drr) || - get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < MIN_VACTIVE_MARGIN_DRR) { + dcn4_get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < MIN_VACTIVE_MARGIN_DRR) { p_state_supported = false; break; } diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h index 6baab7ad6ecc..f0afa8002a2f 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h @@ -7,6 +7,16 @@ #include "dml2_internal_shared_types.h" +struct display_configuation_with_meta; + +int dcn4_get_vactive_pstate_margin( + const struct display_configuation_with_meta *display_cfg, + int plane_mask); + +int dcn4_get_minimum_reserved_time_us_for_planes( + const struct display_configuation_with_meta *display_config, + int plane_mask); + bool pmo_dcn4_fams2_initialize(struct dml2_pmo_initialize_in_out *in_out); bool pmo_dcn4_fams2_optimize_dcc_mcache(struct dml2_pmo_optimize_dcc_mcache_in_out *in_out); diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.c index 4d687fa86caa..83802aac11cd 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.c @@ -9,16 +9,19 @@ static bool dummy_init_for_stutter(struct dml2_pmo_init_for_stutter_in_out *in_out) { + (void)in_out; return false; } static bool dummy_test_for_stutter(struct dml2_pmo_test_for_stutter_in_out *in_out) { + (void)in_out; return true; } static bool dummy_optimize_for_stutter(struct dml2_pmo_optimize_for_stutter_in_out *in_out) { + (void)in_out; return false; } diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.c index e17b5ceba447..dc5bc649f3ac 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.c @@ -23,7 +23,7 @@ double math_mod(const double arg1, const double arg2) return arg2; if (isNaN(arg2)) return arg1; - return arg1 - arg1 * ((int)(arg1 / arg2)); + return arg1 - arg2 * ((int)(arg1 / arg2)); } double math_min2(const double arg1, const double arg2) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.c index 4a7c4c62111e..fa20a91c6e16 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.c @@ -17,6 +17,7 @@ static void setup_unoptimized_display_config_with_meta(const struct dml2_instanc static void setup_speculative_display_config_with_meta(const struct dml2_instance *dml, struct display_configuation_with_meta *out, const struct dml2_display_cfg *display_config) { + (void)dml; memcpy(&out->display_config, display_config, sizeof(struct dml2_display_cfg)); out->stage1.min_clk_index_for_latency = 0; } @@ -472,6 +473,7 @@ static unsigned int count_elements_in_span(int *array, unsigned int array_size, static bool calculate_h_split_for_scaling_transform(int full_vp_width, int h_active, int num_pipes, enum dml2_scaling_transform scaling_transform, int *pipe_vp_x_start, int *pipe_vp_x_end) { + (void)h_active; int i, slice_width; const char MAX_SCL_VP_OVERLAP = 3; bool success = false; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c index 6ef93c6fc1cd..6b78334c2554 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c @@ -178,6 +178,10 @@ static unsigned int find_pipes_assigned_to_plane(struct dml2_context *ctx, static bool validate_pipe_assignment(const struct dml2_context *ctx, const struct dc_state *state, const struct dml_display_cfg_st *disp_cfg, const struct dml2_dml_to_dc_pipe_mapping *mapping) { + (void)ctx; + (void)disp_cfg; + (void)mapping; + (void)state; // int i, j, k; // // unsigned int plane_id; @@ -292,6 +296,7 @@ static unsigned int find_last_resort_pipe_candidates(const struct dc_state *exis const unsigned int stream_id, unsigned int *last_resort_pipe_candidates) { + (void)stream_id; unsigned int num_last_resort_candidates = 0; int i; @@ -541,6 +546,7 @@ static void add_odm_slice_to_odm_tree(struct dml2_context *ctx, struct dc_pipe_mapping_scratch *scratch, unsigned int odm_slice_index) { + (void)ctx; struct pipe_ctx *pipe = NULL; int i; @@ -567,6 +573,8 @@ static struct pipe_ctx *add_plane_to_blend_tree(struct dml2_context *ctx, unsigned int odm_slice, struct pipe_ctx *top_pipe) { + (void)ctx; + (void)plane; int i; for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) { @@ -722,6 +730,7 @@ static void free_unused_pipes_for_plane(struct dml2_context *ctx, struct dc_stat static void remove_pipes_from_blend_trees(struct dml2_context *ctx, struct dc_state *state, struct dc_plane_pipe_pool *pipe_pool, unsigned int odm_slice) { + (void)ctx; struct pipe_ctx *pipe; int i; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c index cf3a69aba638..8e0997441ee0 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c @@ -33,6 +33,7 @@ void dml2_init_ip_params(struct dml2_context *dml2, const struct dc *in_dc, struct ip_params_st *out) { + (void)in_dc; switch (dml2->v20.dml_core_ctx.project) { case dml_project_dcn32: case dml_project_dcn321: @@ -244,6 +245,7 @@ void dml2_init_ip_params(struct dml2_context *dml2, const struct dc *in_dc, stru void dml2_init_socbb_params(struct dml2_context *dml2, const struct dc *in_dc, struct soc_bounding_box_st *out) { + (void)in_dc; out->dprefclk_mhz = dml2->config.bbox_overrides.dprefclk_mhz; out->xtalclk_mhz = dml2->config.bbox_overrides.xtalclk_mhz; out->pcierefclk_mhz = 100; @@ -328,6 +330,7 @@ void dml2_init_socbb_params(struct dml2_context *dml2, const struct dc *in_dc, s void dml2_init_soc_states(struct dml2_context *dml2, const struct dc *in_dc, const struct soc_bounding_box_st *in_bbox, struct soc_states_st *out) { + (void)in_dc; struct dml2_policy_build_synthetic_soc_states_scratch *s = &dml2->v20.scratch.create_scratch.build_synthetic_socbb_scratch; struct dml2_policy_build_synthetic_soc_states_params *p = &dml2->v20.scratch.build_synthetic_socbb_params; int dcfclk_stas_mhz[NUM_DCFCLK_STAS] = {0}; @@ -782,6 +785,7 @@ static void populate_dml_timing_cfg_from_stream_state(struct dml_timing_cfg_st * static void populate_dml_output_cfg_from_stream_state(struct dml_output_cfg_st *out, unsigned int location, const struct dc_stream_state *in, const struct pipe_ctx *pipe, struct dml2_context *dml2) { + (void)pipe; unsigned int output_bpc; out->DSCEnable[location] = (enum dml_dsc_enable)in->timing.flags.DSC; @@ -1133,6 +1137,7 @@ static void populate_dml_plane_cfg_from_plane_state(struct dml_plane_cfg_st *out static unsigned int map_stream_to_dml_display_cfg(const struct dml2_context *dml2, const struct dc_stream_state *stream, const struct dml_display_cfg_st *dml_dispcfg) { + (void)dml_dispcfg; int i = 0; int location = -1; @@ -1173,6 +1178,7 @@ static bool get_plane_id(struct dml2_context *dml2, const struct dc_state *conte static unsigned int map_plane_to_dml_display_cfg(const struct dml2_context *dml2, const struct dc_plane_state *plane, const struct dc_state *context, const struct dml_display_cfg_st *dml_dispcfg, unsigned int stream_id, int plane_index) { + (void)dml_dispcfg; unsigned int plane_id; unsigned int i = 0; unsigned int location = UINT_MAX; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c index 6c7cdf102906..86567e232415 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c @@ -465,6 +465,7 @@ void dml2_initialize_det_scratch(struct dml2_context *in_ctx) static unsigned int find_planes_per_stream_and_stream_count(struct dml2_context *in_ctx, struct dml_display_cfg_st *dml_dispcfg, int *num_of_planes_per_stream) { + (void)in_ctx; unsigned int plane_index, stream_index = 0, num_of_streams; for (plane_index = 0; plane_index < dml_dispcfg->num_surfaces; plane_index++) { diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c index 93b7613fc4f2..1772e74349c7 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c @@ -108,6 +108,17 @@ bool dml2_create(const struct dc *in_dc, const struct dml2_configuration_options return true; } +void dml2_destroy(struct dml2_context *dml2) +{ + if (!dml2) + return; + + if (dml2->architecture == dml2_architecture_21) + dml21_destroy(dml2); + + DC_RUN_WITH_PREEMPTION_ENABLED(vfree(dml2)); +} + void dml2_reinit(const struct dc *in_dc, const struct dml2_configuration_options *config, struct dml2_context **dml2) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper_fpu.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper_fpu.c index 66624cfc27b1..a14e3004a7b7 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper_fpu.c @@ -548,16 +548,6 @@ void dml2_apply_debug_options(const struct dc *dc, struct dml2_context *dml2) } } -void dml2_destroy(struct dml2_context *dml2) -{ - if (!dml2) - return; - - if (dml2->architecture == dml2_architecture_21) - dml21_destroy(dml2); - vfree(dml2); -} - void dml2_extract_dram_and_fclk_change_support(struct dml2_context *dml2, unsigned int *fclk_change_support, unsigned int *dram_clk_change_support) { diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml_display_rq_dlg_calc.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_display_rq_dlg_calc.c index 00d22e542469..18962bbf455b 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml_display_rq_dlg_calc.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_display_rq_dlg_calc.c @@ -563,6 +563,7 @@ void dml_rq_dlg_get_dlg_reg(dml_display_dlg_regs_st *disp_dlg_regs, void dml_rq_dlg_get_arb_params(struct display_mode_lib_st *mode_lib, dml_display_arb_params_st *arb_param) { + (void)mode_lib; memset(arb_param, 0, sizeof(*arb_param)); arb_param->max_req_outstanding = 256; arb_param->min_req_outstanding = 256; // turn off the sat level feature if this set to max diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c index f8f6019d8304..2bdd063cc1e1 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c @@ -49,9 +49,6 @@ #define FN(reg_name, field_name) \ dpp->tf_shift->field_name, dpp->tf_mask->field_name -#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0])) - - enum dcn10_coef_filter_type_sel { SCL_COEF_LUMA_VERT_FILTER = 0, SCL_COEF_LUMA_HORZ_FILTER = 1, diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c index 821d5173b59f..4f3b48ed8679 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c @@ -49,9 +49,6 @@ #define FN(reg_name, field_name) \ dpp->tf_shift->field_name, dpp->tf_mask->field_name -#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0])) - - enum dcn401_coef_filter_type_sel { SCL_COEF_LUMA_VERT_FILTER = 0, SCL_COEF_LUMA_HORZ_FILTER = 1, diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c index 5b3584ad5b6b..8dfb6dd14eb2 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c @@ -680,9 +680,6 @@ static void get_dsc_enc_caps( } else { build_dsc_enc_caps(dsc, dsc_enc_caps); } - - if (dsc->ctx->dc->debug.native422_support) - dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 1; } /* Returns 'false' if no intersection was found for at least one capability. @@ -1100,13 +1097,14 @@ static bool setup_dsc_config( branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_0_mps; break; case PIXEL_ENCODING_YCBCR422: - is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_NATIVE_422; - sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_1_mps; - branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_1_mps; - if (!is_dsc_possible) { + if (policy.ycbcr422_simple) { is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_SIMPLE_422; dsc_cfg->ycbcr422_simple = is_dsc_possible; sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_0_mps; + } else { + is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_NATIVE_422; + sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_1_mps; + branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_1_mps; } break; case PIXEL_ENCODING_YCBCR420: @@ -1406,6 +1404,7 @@ void dc_dsc_get_policy_for_timing(const struct dc_crtc_timing *timing, policy->min_target_bpp = 8; /* DP specs limits to 3 x bpc */ policy->max_target_bpp = 3 * bpc; + policy->ycbcr422_simple = true; break; case PIXEL_ENCODING_YCBCR420: /* DP specs limits to 6 */ diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c index 242f1e6f0d8f..6e1e759462bf 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c @@ -100,7 +100,7 @@ void dsc2_get_enc_caps(struct dsc_enc_caps *dsc_enc_caps, int pixel_clock_100Hz) dsc_enc_caps->color_formats.bits.RGB = 1; dsc_enc_caps->color_formats.bits.YCBCR_444 = 1; dsc_enc_caps->color_formats.bits.YCBCR_SIMPLE_422 = 1; - dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 0; + dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 1; dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_420 = 1; dsc_enc_caps->color_depth.bits.COLOR_DEPTH_8_BPC = 1; diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c index e712985f7abd..17acb64a9d80 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c @@ -128,7 +128,7 @@ void dsc35_get_single_enc_caps(struct dsc_enc_caps *dsc_enc_caps, unsigned int m dsc_enc_caps->color_formats.bits.RGB = 1; dsc_enc_caps->color_formats.bits.YCBCR_444 = 1; dsc_enc_caps->color_formats.bits.YCBCR_SIMPLE_422 = 1; - dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 0; + dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 1; dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_420 = 1; dsc_enc_caps->color_depth.bits.COLOR_DEPTH_8_BPC = 1; diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c index 3bf737195bac..41c3b814b6bd 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c @@ -78,7 +78,7 @@ static void dsc401_get_single_enc_caps(struct dsc_enc_caps *dsc_enc_caps, unsign dsc_enc_caps->color_formats.bits.RGB = 1; dsc_enc_caps->color_formats.bits.YCBCR_444 = 1; dsc_enc_caps->color_formats.bits.YCBCR_SIMPLE_422 = 1; - dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 0; + dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 1; dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_420 = 1; dsc_enc_caps->color_depth.bits.COLOR_DEPTH_8_BPC = 1; diff --git a/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c b/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c index a2c46350e44e..95f8b7c7d657 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/gpio_service.c @@ -646,6 +646,9 @@ failure: enum gpio_ddc_line dal_ddc_get_line( const struct ddc *ddc) { + if (!ddc) + return GPIO_DDC_LINE_UNKNOWN; + return (enum gpio_ddc_line)dal_gpio_get_enum(ddc->pin_data); } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c index 5273ca09fe12..f0abbb7c2cb2 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c @@ -665,16 +665,45 @@ void dce110_update_info_frame(struct pipe_ctx *pipe_ctx) } static void -dce110_dac_encoder_control(struct pipe_ctx *pipe_ctx, bool enable) +dce110_external_encoder_control(enum bp_external_encoder_control_action action, + struct dc_link *link, + struct dc_crtc_timing *timing) { - struct dc_link *link = pipe_ctx->stream->link; + struct dc *dc = link->ctx->dc; struct dc_bios *bios = link->ctx->dc_bios; - struct bp_encoder_control encoder_control = {0}; + const struct dc_link_settings *link_settings = &link->cur_link_settings; + enum bp_result bp_result = BP_RESULT_OK; + struct bp_external_encoder_control ext_cntl = { + .action = action, + .connector_obj_id = link->link_enc->connector, + .encoder_id = link->ext_enc_id, + .lanes_number = link_settings->lane_count, + .link_rate = link_settings->link_rate, - encoder_control.action = enable ? ENCODER_CONTROL_ENABLE : ENCODER_CONTROL_DISABLE; - encoder_control.engine_id = link->link_enc->analog_engine; - encoder_control.pixel_clock = pipe_ctx->stream->timing.pix_clk_100hz / 10; - bios->funcs->encoder_control(bios, &encoder_control); + /* Use signal type of the real link encoder, ie. DP */ + .signal = link->connector_signal, + + /* We don't know the timing yet when executing the SETUP action, + * so use a reasonably high default value. It seems that ENABLE + * can change the actual pixel clock but doesn't work with higher + * pixel clocks than what SETUP was called with. + */ + .pixel_clock = timing ? timing->pix_clk_100hz / 10 : 300000, + .color_depth = timing ? timing->display_color_depth : COLOR_DEPTH_888, + }; + DC_LOGGER_INIT(dc->ctx); + + bp_result = bios->funcs->external_encoder_control(bios, &ext_cntl); + + if (bp_result != BP_RESULT_OK) + DC_LOG_ERROR("Failed to execute external encoder action: 0x%x\n", action); +} + +static void +dce110_prepare_ddc(struct dc_link *link) +{ + if (link->ext_enc_id.id) + dce110_external_encoder_control(EXTERNAL_ENCODER_CONTROL_DDC_SETUP, link, NULL); } static bool @@ -684,7 +713,8 @@ dce110_dac_load_detect(struct dc_link *link) struct link_encoder *link_enc = link->link_enc; enum bp_result bp_result; - bp_result = bios->funcs->dac_load_detection(bios, link_enc->analog_engine); + bp_result = bios->funcs->dac_load_detection( + bios, link_enc->analog_engine, link->ext_enc_id); return bp_result == BP_RESULT_OK; } @@ -700,7 +730,6 @@ void dce110_enable_stream(struct pipe_ctx *pipe_ctx) uint32_t early_control = 0; struct timing_generator *tg = pipe_ctx->stream_res.tg; - link_hwss->setup_stream_attribute(pipe_ctx); link_hwss->setup_stream_encoder(pipe_ctx); dc->hwss.update_info_frame(pipe_ctx); @@ -719,8 +748,8 @@ void dce110_enable_stream(struct pipe_ctx *pipe_ctx) tg->funcs->set_early_control(tg, early_control); - if (dc_is_rgb_signal(pipe_ctx->stream->signal)) - dce110_dac_encoder_control(pipe_ctx, true); + if (link->ext_enc_id.id) + dce110_external_encoder_control(EXTERNAL_ENCODER_CONTROL_ENABLE, link, timing); } static enum bp_result link_transmitter_control( @@ -1219,8 +1248,8 @@ void dce110_disable_stream(struct pipe_ctx *pipe_ctx) link_enc->transmitter - TRANSMITTER_UNIPHY_A); } - if (dc_is_rgb_signal(pipe_ctx->stream->signal)) - dce110_dac_encoder_control(pipe_ctx, false); + if (link->ext_enc_id.id) + dce110_external_encoder_control(EXTERNAL_ENCODER_CONTROL_DISABLE, link, NULL); } void dce110_unblank_stream(struct pipe_ctx *pipe_ctx, @@ -1603,22 +1632,6 @@ static enum dc_status dce110_enable_stream_timing( return DC_OK; } -static void -dce110_select_crtc_source(struct pipe_ctx *pipe_ctx) -{ - struct dc_link *link = pipe_ctx->stream->link; - struct dc_bios *bios = link->ctx->dc_bios; - struct bp_crtc_source_select crtc_source_select = {0}; - enum engine_id engine_id = link->link_enc->preferred_engine; - - if (dc_is_rgb_signal(pipe_ctx->stream->signal)) - engine_id = link->link_enc->analog_engine; - crtc_source_select.controller_id = CONTROLLER_ID_D0 + pipe_ctx->stream_res.tg->inst; - crtc_source_select.color_depth = pipe_ctx->stream->timing.display_color_depth; - crtc_source_select.engine_id = engine_id; - crtc_source_select.sink_signal = pipe_ctx->stream->signal; - bios->funcs->select_crtc_source(bios, &crtc_source_select); -} enum dc_status dce110_apply_single_controller_ctx_to_hw( struct pipe_ctx *pipe_ctx, @@ -1639,10 +1652,6 @@ enum dc_status dce110_apply_single_controller_ctx_to_hw( hws->funcs.disable_stream_gating(dc, pipe_ctx); } - if (pipe_ctx->stream->signal == SIGNAL_TYPE_RGB) { - dce110_select_crtc_source(pipe_ctx); - } - if (pipe_ctx->stream_res.audio != NULL) { struct audio_output audio_output = {0}; @@ -1722,8 +1731,7 @@ enum dc_status dce110_apply_single_controller_ctx_to_hw( pipe_ctx->stream_res.tg->funcs->set_static_screen_control( pipe_ctx->stream_res.tg, event_triggers, 2); - if (!dc_is_virtual_signal(pipe_ctx->stream->signal) && - !dc_is_rgb_signal(pipe_ctx->stream->signal)) + if (!dc_is_virtual_signal(pipe_ctx->stream->signal)) pipe_ctx->stream_res.stream_enc->funcs->dig_connect_to_otg( pipe_ctx->stream_res.stream_enc, pipe_ctx->stream_res.tg->inst); @@ -3376,6 +3384,15 @@ void dce110_enable_tmds_link_output(struct dc_link *link, link->phy_state.symclk_state = SYMCLK_ON_TX_ON; } +static void dce110_enable_analog_link_output( + struct dc_link *link, + uint32_t pix_clk_100hz) +{ + link->link_enc->funcs->enable_analog_output( + link->link_enc, + pix_clk_100hz); +} + void dce110_enable_dp_link_output( struct dc_link *link, const struct link_resource *link_res, @@ -3423,6 +3440,11 @@ void dce110_enable_dp_link_output( } } + if (link->ext_enc_id.id) { + dce110_external_encoder_control(EXTERNAL_ENCODER_CONTROL_INIT, link, NULL); + dce110_external_encoder_control(EXTERNAL_ENCODER_CONTROL_SETUP, link, NULL); + } + if (dc->link_srv->dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) { if (dc->clk_mgr->funcs->notify_link_rate_change) dc->clk_mgr->funcs->notify_link_rate_change(dc->clk_mgr, link); @@ -3513,8 +3535,10 @@ static const struct hw_sequencer_funcs dce110_funcs = { .enable_lvds_link_output = dce110_enable_lvds_link_output, .enable_tmds_link_output = dce110_enable_tmds_link_output, .enable_dp_link_output = dce110_enable_dp_link_output, + .enable_analog_link_output = dce110_enable_analog_link_output, .disable_link_output = dce110_disable_link_output, .dac_load_detect = dce110_dac_load_detect, + .prepare_ddc = dce110_prepare_ddc, }; static const struct hwseq_private_funcs dce110_private_funcs = { diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c index 59851924bfcd..794dd6a95918 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c @@ -781,10 +781,8 @@ static void restore_phy_clocks_for_destructive_link_verification(const struct dc } static void verify_link_capability_destructive(struct dc_link *link, - struct dc_sink *sink, enum dc_detect_reason reason) { - (void)sink; bool should_prepare_phy_clocks = should_prepare_phy_clocks_for_link_verification(link->dc, reason); @@ -857,11 +855,11 @@ static bool should_verify_link_capability_destructively(struct dc_link *link, return destrictive; } -static void verify_link_capability(struct dc_link *link, struct dc_sink *sink, +static void verify_link_capability(struct dc_link *link, enum dc_detect_reason reason) { if (should_verify_link_capability_destructively(link, reason)) - verify_link_capability_destructive(link, sink, reason); + verify_link_capability_destructive(link, reason); else verify_link_capability_non_destructive(link); } @@ -1236,6 +1234,20 @@ static bool detect_link_and_local_sink(struct dc_link *link, if (dc_is_hdmi_signal(link->connector_signal)) read_scdc_caps(link->ddc, link->local_sink); + /* When FreeSync is toggled through OSD, + * we see same EDID no matter what. Check MCCS caps + * to see if we should update FreeSync caps now. + */ + dm_helpers_read_mccs_caps( + link->ctx, + link, + sink); + + if (prev_sink != NULL) { + if (memcmp(&sink->mccs_caps, &prev_sink->mccs_caps, sizeof(struct mccs_caps))) + same_edid = false; + } + if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && sink_caps.transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { @@ -1455,8 +1467,9 @@ bool link_detect(struct dc_link *link, enum dc_detect_reason reason) is_local_sink_detect_success = detect_link_and_local_sink(link, reason); - if (is_local_sink_detect_success && link->local_sink) - verify_link_capability(link, link->local_sink, reason); + if (is_local_sink_detect_success && link->local_sink) { + verify_link_capability(link, reason); + } DC_LOG_DC("%s: link_index=%d is_local_sink_detect_success=%d pre_link_type=%d link_type=%d\n", __func__, link->link_index, is_local_sink_detect_success, pre_link_type, link->type); diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c index b4f46408a000..e12c25896364 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c @@ -181,7 +181,8 @@ void link_set_all_streams_dpms_off_for_link(struct dc_link *link) /* link can be also enabled by vbios. In this case it is not recorded * in pipe_ctx. Disable link phy here to make sure it is completely off */ - dp_disable_link_phy(link, &link_res, link->connector_signal); + if (dc_is_dp_signal(link->connector_signal)) + dp_disable_link_phy(link, &link_res, link->connector_signal); } void link_resume(struct dc_link *link) diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c index 7e7682d7dfc8..ae4c4ad05baa 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c @@ -568,7 +568,9 @@ static bool construct_phy(struct dc_link *link, goto ddc_create_fail; } - if (!link->ddc->ddc_pin) { + /* Embedded display connectors such as LVDS may not have DDC. */ + if (!link->ddc->ddc_pin && + !dc_is_embedded_signal(link->connector_signal)) { DC_ERROR("Failed to get I2C info for connector!\n"); goto ddc_create_fail; } diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c index e12bf3dd3e46..782a45caa13d 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c @@ -743,8 +743,6 @@ static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_setting { struct dc_link_settings initial_link_setting = { LANE_COUNT_ONE, LINK_RATE_LOW, LINK_SPREAD_DISABLED, false, 0}; - if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) - initial_link_setting.link_rate = link->preferred_link_setting.link_rate; struct dc_link_settings current_link_setting = initial_link_setting; uint32_t link_bw; @@ -752,6 +750,9 @@ static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_setting if (req_bw > dp_link_bandwidth_kbps(link, &link->verified_link_cap)) return false; + if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) + initial_link_setting.link_rate = link->preferred_link_setting.link_rate; + /* search for the minimum link setting that: * 1. is supported according to the link training result * 2. could support the b/w requested by the timing diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c index ea73473b970a..fa600593f4c1 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c @@ -43,8 +43,6 @@ #define FN(reg_name, field_name) \ mpc20->mpc_shift->field_name, mpc20->mpc_mask->field_name -#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0])) - void mpc2_update_blending( struct mpc *mpc, struct mpcc_blnd_cfg *blnd_cfg, diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c index 4c7bb0522a8c..4e91e9f6f11a 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c @@ -40,10 +40,6 @@ #define FN(reg_name, field_name) \ mpc30->mpc_shift->field_name, mpc30->mpc_mask->field_name - -#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0])) - - void mpc3_mpc_init(struct mpc *mpc) { struct dcn30_mpc *mpc30 = TO_DCN30_MPC(mpc); diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c index 6a25dcfcdf17..d2d56a1c4b8b 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c @@ -753,7 +753,8 @@ static struct link_encoder *dce60_link_encoder_create( enc_init_data, &link_enc_feature, &link_enc_regs[link_regs_id], - &link_enc_aux_regs[enc_init_data->channel - 1], + enc_init_data->channel == CHANNEL_ID_UNKNOWN ? + NULL : &link_enc_aux_regs[enc_init_data->channel - 1], enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs) ? NULL : &link_enc_hpd_regs[enc_init_data->hpd_source]); return &enc110->base; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c index 33be49b3c1b1..6c00497e9a01 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c @@ -760,7 +760,8 @@ static struct link_encoder *dce80_link_encoder_create( enc_init_data, &link_enc_feature, &link_enc_regs[link_regs_id], - &link_enc_aux_regs[enc_init_data->channel - 1], + enc_init_data->channel == CHANNEL_ID_UNKNOWN ? + NULL : &link_enc_aux_regs[enc_init_data->channel - 1], enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs) ? NULL : &link_enc_hpd_regs[enc_init_data->hpd_source]); return &enc110->base; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c index 54ebf8cf607f..84f6d9dc443f 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c @@ -1394,6 +1394,13 @@ static enum dc_status dcn21_patch_unknown_plane_state(struct dc_plane_state *pla return dcn20_patch_unknown_plane_state(plane_state); } +static void dcn21_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +{ + DC_FP_START(); + dcn21_update_bw_bounding_box_fpu(dc, bw_params); + DC_FP_END(); +} + static const struct resource_funcs dcn21_res_pool_funcs = { .destroy = dcn21_destroy_resource_pool, .link_enc_create = dcn21_link_encoder_create, diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c index ee4bc2c2e73a..39944d90ea98 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c @@ -1854,6 +1854,13 @@ static struct dc_cap_funcs cap_funcs = { .get_dcc_compression_cap = dcn20_get_dcc_compression_cap }; +static void dcn31_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +{ + DC_FP_START(); + dcn31_update_bw_bounding_box_fpu(dc, bw_params); + DC_FP_END(); +} + static struct resource_funcs dcn31_res_pool_funcs = { .destroy = dcn31_destroy_resource_pool, .link_enc_create = dcn31_link_encoder_create, @@ -1996,6 +2003,8 @@ static bool dcn31_resource_construct( dc->config.use_pipe_ctx_sync_logic = true; dc->config.disable_hbr_audio_dp2 = true; + dc->config.no_native422_support = true; + /* read VBIOS LTTPR caps */ { if (ctx->dc_bios->funcs->get_lttpr_caps) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c index 2ca673114841..975e14f3f5fa 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c @@ -1849,6 +1849,13 @@ static struct dc_cap_funcs cap_funcs = { .get_dcc_compression_cap = dcn20_get_dcc_compression_cap }; +static void dcn315_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +{ + DC_FP_START(); + dcn315_update_bw_bounding_box_fpu(dc, bw_params); + DC_FP_END(); +} + static struct resource_funcs dcn315_res_pool_funcs = { .destroy = dcn315_destroy_resource_pool, .link_enc_create = dcn31_link_encoder_create, @@ -1959,6 +1966,8 @@ static bool dcn315_resource_construct( dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; + dc->config.no_native422_support = true; + /* read VBIOS LTTPR caps */ { if (ctx->dc_bios->funcs->get_lttpr_caps) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c index 2242df112a3f..914d91df174c 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c @@ -1725,6 +1725,13 @@ static struct dc_cap_funcs cap_funcs = { .get_dcc_compression_cap = dcn20_get_dcc_compression_cap }; +static void dcn316_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params) +{ + DC_FP_START(); + dcn316_update_bw_bounding_box_fpu(dc, bw_params); + DC_FP_END(); +} + static struct resource_funcs dcn316_res_pool_funcs = { .destroy = dcn316_destroy_resource_pool, .link_enc_create = dcn31_link_encoder_create, diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c index 82f81b586986..3751f7a94a05 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c @@ -92,9 +92,14 @@ #include "dml/dcn32/dcn32_fpu.h" #include "dc_state_priv.h" +#include "dc_fpu.h" #include "dml2_0/dml2_wrapper.h" +#if !defined(DC_RUN_WITH_PREEMPTION_ENABLED) +#define DC_RUN_WITH_PREEMPTION_ENABLED(code) code +#endif + #define DC_LOGGER_INIT(logger) enum dcn32_clk_src_array_id { @@ -1684,7 +1689,8 @@ static void dcn32_enable_phantom_plane(struct dc *dc, if (curr_pipe->top_pipe && curr_pipe->top_pipe->plane_state == curr_pipe->plane_state) phantom_plane = prev_phantom_plane; else - phantom_plane = dc_state_create_phantom_plane(dc, context, curr_pipe->plane_state); + DC_RUN_WITH_PREEMPTION_ENABLED(phantom_plane = + dc_state_create_phantom_plane(dc, context, curr_pipe->plane_state)); if (!phantom_plane) continue; diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c index 146a6e47934b..e723b4d0aff3 100644 --- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c @@ -155,6 +155,10 @@ static void dcn42_update_soc_bb_with_values_from_clk_mgr(struct dml2_soc_bb *soc dcn42_convert_dc_clock_table_to_soc_bb_clock_table(&soc_bb->clk_table, &soc_bb->vmin_limit, dc->clk_mgr->bw_params); } + + if (dc->clk_mgr->bw_params->vram_type == Ddr5MemType) { + soc_bb->power_management_parameters = dcn42_ddr5_power_management_parameters; + } } static void apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config) diff --git a/drivers/gpu/drm/amd/display/include/ddc_service_types.h b/drivers/gpu/drm/amd/display/include/ddc_service_types.h index 1c603b12957f..53210e3aa0e0 100644 --- a/drivers/gpu/drm/amd/display/include/ddc_service_types.h +++ b/drivers/gpu/drm/amd/display/include/ddc_service_types.h @@ -36,6 +36,7 @@ #define DP_BRANCH_DEVICE_ID_006037 0x006037 #define DP_BRANCH_DEVICE_ID_001CF8 0x001CF8 #define DP_BRANCH_DEVICE_ID_0060AD 0x0060AD +#define DP_BRANCH_DEVICE_ID_001FF2 0x001FF2 #define DP_BRANCH_HW_REV_10 0x10 #define DP_BRANCH_HW_REV_20 0x20 diff --git a/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h b/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h index 38a77fa9b4af..a0f03fb67605 100644 --- a/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h +++ b/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h @@ -153,6 +153,10 @@ struct embedded_panel_info { uint32_t drr_enabled; uint32_t min_drr_refresh_rate; bool realtek_eDPToLVDS; + uint16_t panel_width_mm; + uint16_t panel_height_mm; + uint16_t fake_edid_size; + const uint8_t *fake_edid; }; struct dc_firmware_info { diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index c9150019aab0..f3b41087678b 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -153,7 +153,7 @@ unsigned int mod_freesync_calc_v_total_from_refresh( * round down the vtotal value to avoid stretching vblank over * panel's vtotal boundary. */ - v_total = div64_u64(div64_u64(((unsigned long long)( + v_total = (unsigned int)div64_u64(div64_u64(((unsigned long long)( frame_duration_in_ns) * (stream->timing.pix_clk_100hz / 10)), stream->timing.h_total), 1000000); } else if (refresh_in_uhz >= stream->timing.max_refresh_in_uhz) { @@ -161,11 +161,11 @@ unsigned int mod_freesync_calc_v_total_from_refresh( * round up the vtotal value to prevent off-by-one error causing * v_total_min to be below the panel's lower bound */ - v_total = div64_u64(div64_u64(((unsigned long long)( + v_total = (unsigned int)div64_u64(div64_u64(((unsigned long long)( frame_duration_in_ns) * (stream->timing.pix_clk_100hz / 10)), stream->timing.h_total) + (1000000 - 1), 1000000); } else { - v_total = div64_u64(div64_u64(((unsigned long long)( + v_total = (unsigned int)div64_u64(div64_u64(((unsigned long long)( frame_duration_in_ns) * (stream->timing.pix_clk_100hz / 10)), stream->timing.h_total) + 500000, 1000000); } @@ -196,11 +196,11 @@ static unsigned int calc_v_total_from_duration( uint32_t h_total_up_scaled; h_total_up_scaled = stream->timing.h_total * 10000; - v_total = div_u64((unsigned long long)duration_in_us + v_total = (unsigned int)div_u64((unsigned long long)duration_in_us * stream->timing.pix_clk_100hz + (h_total_up_scaled - 1), h_total_up_scaled); //ceiling for MMax and MMin for MVRR } else { - v_total = div64_u64(div64_u64(((unsigned long long)( + v_total = (unsigned int)div64_u64(div64_u64(((unsigned long long)( duration_in_us) * (stream->timing.pix_clk_100hz / 10)), stream->timing.h_total), 1000); } @@ -232,22 +232,28 @@ static void update_v_total_for_static_ramp( target_duration_in_us; /* Calculate ratio between new and current frame duration with 3 digit */ - unsigned int frame_duration_ratio = div64_u64(1000000, + uint64_t frame_duration_ratio_u64 = div64_u64(1000000, (1000 + div64_u64(((unsigned long long)( STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME) * current_duration_in_us), 1000000))); + ASSERT(frame_duration_ratio_u64 <= 0xFFFFFFFF); + unsigned int frame_duration_ratio = (unsigned int)frame_duration_ratio_u64; /* Calculate delta between new and current frame duration in us */ - unsigned int frame_duration_delta = div64_u64(((unsigned long long)( + uint64_t frame_duration_delta_u64 = div64_u64(((unsigned long long)( current_duration_in_us) * (1000 - frame_duration_ratio)), 1000); + ASSERT(frame_duration_delta_u64 <= 0xFFFFFFFF); + unsigned int frame_duration_delta = (unsigned int)frame_duration_delta_u64; /* Adjust frame duration delta based on ratio between current and * standard frame duration (frame duration at 60 Hz refresh rate). */ - unsigned int ramp_rate_interpolated = div64_u64(((unsigned long long)( + uint64_t ramp_rate_interpolated_u64 = div64_u64(((unsigned long long)( frame_duration_delta) * current_duration_in_us), 16666); + ASSERT(ramp_rate_interpolated_u64 <= 0xFFFFFFFF); + unsigned int ramp_rate_interpolated = (unsigned int)ramp_rate_interpolated_u64; /* Going to a higher refresh rate (lower frame duration) */ if (ramp_direction_is_up) { @@ -277,7 +283,7 @@ static void update_v_total_for_static_ramp( } } - v_total = div64_u64(div64_u64(((unsigned long long)( + v_total = (unsigned int)div64_u64(div64_u64(((unsigned long long)( current_duration_in_us) * (stream->timing.pix_clk_100hz / 10)), stream->timing.h_total), 1000); @@ -1058,8 +1064,12 @@ void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync, else in_out_vrr->fixed_refresh_in_uhz = 0; - refresh_range = div_u64(in_out_vrr->max_refresh_in_uhz + 500000, 1000000) - - div_u64(in_out_vrr->min_refresh_in_uhz + 500000, 1000000); + { + uint64_t rr_tmp = div_u64(in_out_vrr->max_refresh_in_uhz + 500000, 1000000) - + div_u64(in_out_vrr->min_refresh_in_uhz + 500000, 1000000); + ASSERT(rr_tmp <= 0xFFFFFFFF); + refresh_range = (unsigned int)rr_tmp; + } in_out_vrr->supported = true; } diff --git a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c b/drivers/gpu/drm/amd/display/modules/power/power_helpers.c index df3b8383b06d..5d444e9eb38f 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c +++ b/drivers/gpu/drm/amd/display/modules/power/power_helpers.c @@ -250,10 +250,12 @@ static void fill_backlight_transform_table(struct dmcu_iram_parameters params, unsigned int lut_index; table->backlight_thresholds[0] = 0; - table->backlight_offsets[0] = params.backlight_lut_array[0]; + ASSERT(params.backlight_lut_array[0] <= 0xFFFF); + table->backlight_offsets[0] = (uint16_t)params.backlight_lut_array[0]; table->backlight_thresholds[num_entries-1] = 0xFFFF; + ASSERT(params.backlight_lut_array[params.backlight_lut_array_size - 1] <= 0xFFFF); table->backlight_offsets[num_entries-1] = - params.backlight_lut_array[params.backlight_lut_array_size - 1]; + (uint16_t)params.backlight_lut_array[params.backlight_lut_array_size - 1]; /* Setup all brightness levels between 0% and 100% exclusive * Fills brightness-to-backlight transform table. Backlight custom curve @@ -265,12 +267,17 @@ static void fill_backlight_transform_table(struct dmcu_iram_parameters params, */ for (i = 1; i+1 < num_entries; i++) { lut_index = (params.backlight_lut_array_size - 1) * i / (num_entries - 1); + ASSERT(lut_index < params.backlight_lut_array_size); - table->backlight_thresholds[i] = - cpu_to_be16(DIV_ROUNDUP((i * 65536), num_entries)); - table->backlight_offsets[i] = - cpu_to_be16(params.backlight_lut_array[lut_index]); + unsigned int threshold_val = DIV_ROUNDUP((i * 65536), num_entries); + unsigned int offset_val = params.backlight_lut_array[lut_index]; + + ASSERT(threshold_val <= 0xFFFF); + ASSERT(offset_val <= 0xFFFF); + + table->backlight_thresholds[i] = cpu_to_be16((uint16_t)threshold_val); + table->backlight_offsets[i] = cpu_to_be16((uint16_t)offset_val); } } @@ -282,10 +289,12 @@ static void fill_backlight_transform_table_v_2_2(struct dmcu_iram_parameters par unsigned int lut_index; table->backlight_thresholds[0] = 0; - table->backlight_offsets[0] = params.backlight_lut_array[0]; + ASSERT(params.backlight_lut_array[0] <= 0xFFFF); + table->backlight_offsets[0] = (uint16_t)params.backlight_lut_array[0]; table->backlight_thresholds[num_entries-1] = 0xFFFF; + ASSERT(params.backlight_lut_array[params.backlight_lut_array_size - 1] <= 0xFFFF); table->backlight_offsets[num_entries-1] = - params.backlight_lut_array[params.backlight_lut_array_size - 1]; + (uint16_t)params.backlight_lut_array[params.backlight_lut_array_size - 1]; /* Setup all brightness levels between 0% and 100% exclusive * Fills brightness-to-backlight transform table. Backlight custom curve @@ -299,12 +308,16 @@ static void fill_backlight_transform_table_v_2_2(struct dmcu_iram_parameters par lut_index = DIV_ROUNDUP((i * params.backlight_lut_array_size), num_entries); ASSERT(lut_index < params.backlight_lut_array_size); + unsigned int threshold_val = DIV_ROUNDUP((i * 65536), num_entries); + unsigned int offset_val = params.backlight_lut_array[lut_index]; + + ASSERT(threshold_val <= 0xFFFF); + ASSERT(offset_val <= 0xFFFF); + table->backlight_thresholds[i] = (big_endian) ? - cpu_to_be16(DIV_ROUNDUP((i * 65536), num_entries)) : - cpu_to_le16(DIV_ROUNDUP((i * 65536), num_entries)); + cpu_to_be16((uint16_t)threshold_val) : cpu_to_le16((uint16_t)threshold_val); table->backlight_offsets[i] = (big_endian) ? - cpu_to_be16(params.backlight_lut_array[lut_index]) : - cpu_to_le16(params.backlight_lut_array[lut_index]); + cpu_to_be16((uint16_t)offset_val) : cpu_to_le16((uint16_t)offset_val); } } @@ -740,9 +753,12 @@ bool dmub_init_abm_config(struct resource_pool *res_pool, } if (params.backlight_ramping_override) { + + ASSERT(params.backlight_ramping_reduction <= 0xFFFF); + ASSERT(params.backlight_ramping_start <= 0xFFFF); for (i = 0; i < NUM_AGGR_LEVEL; i++) { - config.blRampReduction[i] = params.backlight_ramping_reduction; - config.blRampStart[i] = params.backlight_ramping_start; + config.blRampReduction[i] = (uint16_t)params.backlight_ramping_reduction; + config.blRampStart[i] = (uint16_t)params.backlight_ramping_start; } } else { for (i = 0; i < NUM_AGGR_LEVEL; i++) { @@ -1060,6 +1076,7 @@ void calculate_replay_link_off_frame_count(struct dc_link *link, bool fill_custom_backlight_caps(unsigned int config_no, struct dm_acpi_atif_backlight_caps *caps) { unsigned int data_points_size; + uint64_t caps_size; if (config_no >= ARRAY_SIZE(custom_backlight_profiles)) return false; @@ -1067,7 +1084,9 @@ bool fill_custom_backlight_caps(unsigned int config_no, struct dm_acpi_atif_back data_points_size = custom_backlight_profiles[config_no].num_data_points * sizeof(custom_backlight_profiles[config_no].data_points[0]); - caps->size = sizeof(struct dm_acpi_atif_backlight_caps) - sizeof(caps->data_points) + data_points_size; + caps_size = sizeof(struct dm_acpi_atif_backlight_caps) - sizeof(caps->data_points) + data_points_size; + ASSERT(caps_size <= 0xFFFF); + caps->size = (uint16_t)caps_size; caps->flags = 0; caps->error_code = 0; caps->ac_level_percentage = custom_backlight_profiles[config_no].ac_level_percentage; diff --git a/drivers/gpu/drm/amd/display/modules/vmid/vmid.c b/drivers/gpu/drm/amd/display/modules/vmid/vmid.c index 9f408cb11ac9..179b505f7777 100644 --- a/drivers/gpu/drm/amd/display/modules/vmid/vmid.c +++ b/drivers/gpu/drm/amd/display/modules/vmid/vmid.c @@ -57,7 +57,10 @@ static void clear_entry_from_vmid_table(struct core_vmid *core_vmid, unsigned in static void evict_vmids(struct core_vmid *core_vmid) { int i; - uint16_t ord = dc_get_vmid_use_vector(core_vmid->dc); + int ord_int = dc_get_vmid_use_vector(core_vmid->dc); + + ASSERT(ord_int >= 0 && ord_int <= 0xFFFF); + uint16_t ord = (uint16_t)ord_int; // At this point any positions with value 0 are unused vmids, evict them for (i = 1; i < core_vmid->num_vmid; i++) { @@ -120,7 +123,8 @@ uint8_t mod_vmid_get_for_ptb(struct mod_vmid *mod_vmid, uint64_t ptb) ASSERT(0); } - return vmid; + ASSERT(vmid >= 0 && vmid <= 0xFF); + return (uint8_t)vmid; } void mod_vmid_reset(struct mod_vmid *mod_vmid) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 62b0b1ef0d10..736304e73ca4 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -995,12 +995,15 @@ static ssize_t amdgpu_get_pp_dpm_clock(struct device *dev, return ret; ret = amdgpu_dpm_emit_clock_levels(adev, type, buf, &size); - if (ret) - return ret; + if (ret) { + size = ret; + goto out_pm_put; + } if (size == 0) size = sysfs_emit(buf, "\n"); +out_pm_put: amdgpu_pm_put_access(adev); return size; @@ -3902,11 +3905,14 @@ static int amdgpu_retrieve_od_settings(struct amdgpu_device *adev, return ret; ret = amdgpu_dpm_emit_clock_levels(adev, od_type, buf, &size); - if (ret) - return ret; + if (ret) { + size = ret; + goto out_pm_put; + } if (size == 0) size = sysfs_emit(buf, "\n"); +out_pm_put: amdgpu_pm_put_access(adev); return size; diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index 8c37aa452569..55e2375e1dad 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -3062,9 +3062,6 @@ static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr) smu7_set_private_data_based_on_pptable_v0(hwmgr); } - if (result) - goto fail; - data->is_tlu_enabled = false; hwmgr->platform_descriptor.hardwareActivityPerformanceLevels = diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c index 731355bdb9bc..3650e7beeb67 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/ci_smumgr.c @@ -1333,12 +1333,13 @@ static int ci_populate_all_memory_levels(struct pp_hwmgr *hwmgr) dev_id = adev->pdev->device; - if ((dpm_table->mclk_table.count >= 2) - && ((dev_id == 0x67B0) || (dev_id == 0x67B1))) { - smu_data->smc_state_table.MemoryLevel[1].MinVddci = - smu_data->smc_state_table.MemoryLevel[0].MinVddci; - smu_data->smc_state_table.MemoryLevel[1].MinMvdd = - smu_data->smc_state_table.MemoryLevel[0].MinMvdd; + if ((dpm_table->mclk_table.count >= 2) && + ((dev_id == 0x67B0) || (dev_id == 0x67B1)) && + (adev->pdev->revision == 0)) { + smu_data->smc_state_table.MemoryLevel[1].MinVddc = + smu_data->smc_state_table.MemoryLevel[0].MinVddc; + smu_data->smc_state_table.MemoryLevel[1].MinVddcPhases = + smu_data->smc_state_table.MemoryLevel[0].MinVddcPhases; } smu_data->smc_state_table.MemoryLevel[0].ActivityLevel = 0x1F; CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.MemoryLevel[0].ActivityLevel); diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h index 609f5ab07d8a..d76e0b005308 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h @@ -584,6 +584,7 @@ struct cmn2asic_mapping { /* Message flags for smu_msg_args */ #define SMU_MSG_FLAG_ASYNC BIT(0) /* Async send - skip post-poll */ #define SMU_MSG_FLAG_LOCK_HELD BIT(1) /* Caller holds ctl->lock */ +#define SMU_MSG_FLAG_FORCE_READ_ARG BIT(2) /* force read smu arg from pmfw */ /* smu_msg_ctl flags */ #define SMU_MSG_CTL_DEBUG_MAILBOX BIT(0) /* Debug mailbox supported */ @@ -2164,4 +2165,21 @@ static inline void smu_feature_init(struct smu_context *smu, int feature_num) smu_feature_list_clear_all(smu, SMU_FEATURE_LIST_ALLOWED); } +/* + * smu_safe_u16_nn - Make u16 safe by filtering negative overflow errors + * @val: Input u16 value, may contain invalid negative overflows + * + * Convert u16 to non-negative value. Cast to s16 to detect negative values + * caused by calculation errors. Return 0 for negative errors, return + * original value if valid. + * + * Return: Valid u16 value or 0 + */ +static inline u16 smu_safe_u16_nn(u16 val) +{ + s16 tmp = (s16)val; + + return tmp < 0 ? 0 : val; +} + #endif diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c index dc056f1e4b64..9d8b1227388f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c @@ -425,6 +425,7 @@ static int aldebaran_set_default_dpm_table(struct smu_context *smu) dpm_table->dpm_levels[0].enabled = true; dpm_table->dpm_levels[1].value = pptable->GfxclkFmax; dpm_table->dpm_levels[1].enabled = true; + dpm_table->flags |= SMU_DPM_TABLE_FINE_GRAINED; } else { dpm_table->count = 1; dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100; @@ -1846,6 +1847,7 @@ static int aldebaran_mode2_reset(struct smu_context *smu) amdgpu_device_load_pci_state(adev->pdev); dev_dbg(adev->dev, "wait for reset ack\n"); + ret = -ETIME; while (ret == -ETIME && timeout) { ret = smu_msg_wait_response(ctl, 0); /* Wait a bit more time for getting ACK */ @@ -1855,7 +1857,7 @@ static int aldebaran_mode2_reset(struct smu_context *smu) continue; } - if (ret != 1) { + if (ret != 0) { dev_err(adev->dev, "failed to send mode2 message \tparam: 0x%08x response %#x\n", SMU_RESET_MODE_2, ret); goto out; @@ -1865,10 +1867,9 @@ static int aldebaran_mode2_reset(struct smu_context *smu) } else { dev_err(adev->dev, "smu fw 0x%x does not support MSG_GfxDeviceDriverReset MSG\n", smu->smc_fw_version); + ret = -EOPNOTSUPP; } - if (ret == 1) - ret = 0; out: mutex_unlock(&ctl->lock); diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index b414a74d29fd..0a7f5fa3c1d3 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -773,13 +773,13 @@ static int smu_v13_0_0_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageGfxclkFrequencyPreDs; break; case METRICS_AVERAGE_FCLK: - if (metrics->AverageUclkActivity <= SMU_13_0_0_BUSY_THRESHOLD) + if (smu_safe_u16_nn(metrics->AverageUclkActivity) <= SMU_13_0_0_BUSY_THRESHOLD) *value = metrics->AverageFclkFrequencyPostDs; else *value = metrics->AverageFclkFrequencyPreDs; break; case METRICS_AVERAGE_UCLK: - if (metrics->AverageUclkActivity <= SMU_13_0_0_BUSY_THRESHOLD) + if (smu_safe_u16_nn(metrics->AverageUclkActivity) <= SMU_13_0_0_BUSY_THRESHOLD) *value = metrics->AverageMemclkFrequencyPostDs; else *value = metrics->AverageMemclkFrequencyPreDs; @@ -800,7 +800,7 @@ static int smu_v13_0_0_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageGfxActivity; break; case METRICS_AVERAGE_MEMACTIVITY: - *value = metrics->AverageUclkActivity; + *value = smu_safe_u16_nn(metrics->AverageUclkActivity); break; case METRICS_AVERAGE_VCNACTIVITY: *value = max(metrics->Vcn0ActivityPercentage, @@ -2085,7 +2085,7 @@ static ssize_t smu_v13_0_0_get_gpu_metrics(struct smu_context *smu, metrics->AvgTemperature[TEMP_VR_MEM1]); gpu_metrics->average_gfx_activity = metrics->AverageGfxActivity; - gpu_metrics->average_umc_activity = metrics->AverageUclkActivity; + gpu_metrics->average_umc_activity = smu_safe_u16_nn(metrics->AverageUclkActivity); gpu_metrics->average_mm_activity = max(metrics->Vcn0ActivityPercentage, metrics->Vcn1ActivityPercentage); @@ -2102,7 +2102,7 @@ static ssize_t smu_v13_0_0_get_gpu_metrics(struct smu_context *smu, else gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPreDs; - if (metrics->AverageUclkActivity <= SMU_13_0_0_BUSY_THRESHOLD) + if (smu_safe_u16_nn(metrics->AverageUclkActivity) <= SMU_13_0_0_BUSY_THRESHOLD) gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPostDs; else gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPreDs; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c index 54a86eb77cd5..fe929bd89058 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c @@ -479,9 +479,14 @@ static int smu_v13_0_12_get_system_metrics_table(struct smu_context *smu) } amdgpu_hdp_invalidate(smu->adev, NULL); + + ret = smu_cmn_vram_cpy(smu, sys_table->cache.buffer, + table->cpu_addr, + smu_v13_0_12_get_system_metrics_size()); + if (ret) + return ret; + smu_table_cache_update_time(sys_table, jiffies); - memcpy(sys_table->cache.buffer, table->cpu_addr, - smu_v13_0_12_get_system_metrics_size()); return 0; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index 475541189782..0df8c05a7fce 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -778,7 +778,10 @@ int smu_v13_0_6_get_metrics_table(struct smu_context *smu, void *metrics_table, } amdgpu_hdp_invalidate(smu->adev, NULL); - memcpy(smu_table->metrics_table, table->cpu_addr, table_size); + ret = smu_cmn_vram_cpy(smu, smu_table->metrics_table, + table->cpu_addr, table_size); + if (ret) + return ret; smu_table->metrics_time = jiffies; } @@ -857,9 +860,9 @@ int smu_v13_0_6_get_static_metrics_table(struct smu_context *smu) } amdgpu_hdp_invalidate(smu->adev, NULL); - memcpy(smu_table->metrics_table, table->cpu_addr, table_size); - return 0; + return smu_cmn_vram_cpy(smu, smu_table->metrics_table, + table->cpu_addr, table_size); } static void smu_v13_0_6_update_caps(struct smu_context *smu) @@ -1126,6 +1129,7 @@ static int smu_v13_0_6_set_default_dpm_table(struct smu_context *smu) /* gfxclk dpm table setup */ dpm_table = &dpm_context->dpm_tables.gfx_table; dpm_table->clk_type = SMU_GFXCLK; + dpm_table->flags = SMU_DPM_TABLE_FINE_GRAINED; if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT)) { /* In the case of gfxclk, only fine-grained dpm is honored. * Get min/max values from FW. @@ -2404,13 +2408,15 @@ static int smu_v13_0_6_request_i2c_xfer(struct smu_context *smu, table_size = smu_table->tables[SMU_TABLE_I2C_COMMANDS].size; - memcpy(table->cpu_addr, table_data, table_size); + ret = smu_cmn_vram_cpy(smu, table->cpu_addr, table_data, table_size); + if (ret) + return ret; + /* Flush hdp cache */ amdgpu_hdp_flush(adev, NULL); - ret = smu_cmn_send_smc_msg(smu, SMU_MSG_RequestI2cTransaction, - NULL); - return ret; + return smu_cmn_send_smc_msg(smu, SMU_MSG_RequestI2cTransaction, + NULL); } static int smu_v13_0_6_i2c_xfer(struct i2c_adapter *i2c_adap, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index fd0b6215364f..5abf2b0703c6 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -783,13 +783,13 @@ static int smu_v13_0_7_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageGfxclkFrequencyPreDs; break; case METRICS_AVERAGE_FCLK: - if (metrics->AverageUclkActivity <= SMU_13_0_7_BUSY_THRESHOLD) + if (smu_safe_u16_nn(metrics->AverageUclkActivity) <= SMU_13_0_7_BUSY_THRESHOLD) *value = metrics->AverageFclkFrequencyPostDs; else *value = metrics->AverageFclkFrequencyPreDs; break; case METRICS_AVERAGE_UCLK: - if (metrics->AverageUclkActivity <= SMU_13_0_7_BUSY_THRESHOLD) + if (smu_safe_u16_nn(metrics->AverageUclkActivity) <= SMU_13_0_7_BUSY_THRESHOLD) *value = metrics->AverageMemclkFrequencyPostDs; else *value = metrics->AverageMemclkFrequencyPreDs; @@ -814,7 +814,7 @@ static int smu_v13_0_7_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageGfxActivity; break; case METRICS_AVERAGE_MEMACTIVITY: - *value = metrics->AverageUclkActivity; + *value = smu_safe_u16_nn(metrics->AverageUclkActivity); break; case METRICS_AVERAGE_SOCKETPOWER: *value = metrics->AverageSocketPower << 8; @@ -2091,7 +2091,7 @@ static ssize_t smu_v13_0_7_get_gpu_metrics(struct smu_context *smu, metrics->AvgTemperature[TEMP_VR_MEM1]); gpu_metrics->average_gfx_activity = metrics->AverageGfxActivity; - gpu_metrics->average_umc_activity = metrics->AverageUclkActivity; + gpu_metrics->average_umc_activity = smu_safe_u16_nn(metrics->AverageUclkActivity); gpu_metrics->average_mm_activity = max(metrics->Vcn0ActivityPercentage, metrics->Vcn1ActivityPercentage); @@ -2104,7 +2104,7 @@ static ssize_t smu_v13_0_7_get_gpu_metrics(struct smu_context *smu, else gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPreDs; - if (metrics->AverageUclkActivity <= SMU_13_0_7_BUSY_THRESHOLD) + if (smu_safe_u16_nn(metrics->AverageUclkActivity) <= SMU_13_0_7_BUSY_THRESHOLD) gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPostDs; else gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPreDs; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c index 31f9566f7979..5ce4e982ca33 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c @@ -661,13 +661,13 @@ static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageGfxclkFrequencyPreDs; break; case METRICS_AVERAGE_FCLK: - if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD) + if (smu_safe_u16_nn(metrics->AverageUclkActivity) <= SMU_14_0_2_BUSY_THRESHOLD) *value = metrics->AverageFclkFrequencyPostDs; else *value = metrics->AverageFclkFrequencyPreDs; break; case METRICS_AVERAGE_UCLK: - if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD) + if (smu_safe_u16_nn(metrics->AverageUclkActivity) <= SMU_14_0_2_BUSY_THRESHOLD) *value = metrics->AverageMemclkFrequencyPostDs; else *value = metrics->AverageMemclkFrequencyPreDs; @@ -688,7 +688,7 @@ static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageGfxActivity; break; case METRICS_AVERAGE_MEMACTIVITY: - *value = metrics->AverageUclkActivity; + *value = smu_safe_u16_nn(metrics->AverageUclkActivity); break; case METRICS_AVERAGE_VCNACTIVITY: *value = max(metrics->AverageVcn0ActivityPercentage, @@ -2147,7 +2147,7 @@ static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu, metrics->AvgTemperature[TEMP_VR_MEM1]); gpu_metrics->average_gfx_activity = metrics->AverageGfxActivity; - gpu_metrics->average_umc_activity = metrics->AverageUclkActivity; + gpu_metrics->average_umc_activity = smu_safe_u16_nn(metrics->AverageUclkActivity); gpu_metrics->average_mm_activity = max(metrics->AverageVcn0ActivityPercentage, metrics->Vcn1ActivityPercentage); @@ -2159,7 +2159,7 @@ static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu, else gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPreDs; - if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD) + if (smu_safe_u16_nn(metrics->AverageUclkActivity) <= SMU_14_0_2_BUSY_THRESHOLD) gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPostDs; else gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPreDs; @@ -2214,17 +2214,61 @@ static void smu_v14_0_2_dump_od_table(struct smu_context *smu, od_table->OverDriveTable.UclkFmax); } +#define OD_ERROR_MSG_MAP(msg) \ + [msg] = #msg + +static const char *od_error_message[] = { + OD_ERROR_MSG_MAP(OD_REQUEST_ADVANCED_NOT_SUPPORTED), + OD_ERROR_MSG_MAP(OD_UNSUPPORTED_FEATURE), + OD_ERROR_MSG_MAP(OD_INVALID_FEATURE_COMBO_ERROR), + OD_ERROR_MSG_MAP(OD_GFXCLK_VF_CURVE_OFFSET_ERROR), + OD_ERROR_MSG_MAP(OD_VDD_GFX_VMAX_ERROR), + OD_ERROR_MSG_MAP(OD_VDD_SOC_VMAX_ERROR), + OD_ERROR_MSG_MAP(OD_PPT_ERROR), + OD_ERROR_MSG_MAP(OD_FAN_MIN_PWM_ERROR), + OD_ERROR_MSG_MAP(OD_FAN_ACOUSTIC_TARGET_ERROR), + OD_ERROR_MSG_MAP(OD_FAN_ACOUSTIC_LIMIT_ERROR), + OD_ERROR_MSG_MAP(OD_FAN_TARGET_TEMP_ERROR), + OD_ERROR_MSG_MAP(OD_FAN_ZERO_RPM_STOP_TEMP_ERROR), + OD_ERROR_MSG_MAP(OD_FAN_CURVE_PWM_ERROR), + OD_ERROR_MSG_MAP(OD_FAN_CURVE_TEMP_ERROR), + OD_ERROR_MSG_MAP(OD_FULL_CTRL_GFXCLK_ERROR), + OD_ERROR_MSG_MAP(OD_FULL_CTRL_UCLK_ERROR), + OD_ERROR_MSG_MAP(OD_FULL_CTRL_FCLK_ERROR), + OD_ERROR_MSG_MAP(OD_FULL_CTRL_VDD_GFX_ERROR), + OD_ERROR_MSG_MAP(OD_FULL_CTRL_VDD_SOC_ERROR), + OD_ERROR_MSG_MAP(OD_TDC_ERROR), + OD_ERROR_MSG_MAP(OD_GFXCLK_ERROR), + OD_ERROR_MSG_MAP(OD_UCLK_ERROR), + OD_ERROR_MSG_MAP(OD_FCLK_ERROR), + OD_ERROR_MSG_MAP(OD_OP_TEMP_ERROR), + OD_ERROR_MSG_MAP(OD_OP_GFX_EDC_ERROR), + OD_ERROR_MSG_MAP(OD_OP_GFX_PCC_ERROR), + OD_ERROR_MSG_MAP(OD_POWER_FEATURE_CTRL_ERROR), +}; + static int smu_v14_0_2_upload_overdrive_table(struct smu_context *smu, OverDriveTableExternal_t *od_table) { - int ret; - ret = smu_cmn_update_table(smu, - SMU_TABLE_OVERDRIVE, - 0, - (void *)od_table, - true); - if (ret) - dev_err(smu->adev->dev, "Failed to upload overdrive table!\n"); + uint32_t read_arg = 0; + int ret, od_error_type; + + ret = smu_cmn_update_table_read_arg(smu, + SMU_TABLE_OVERDRIVE, + 0, + (void *)od_table, + &read_arg, + true); + if (ret) { + dev_err(smu->adev->dev, "Failed to upload overdrive table, ret:%d\n", ret); + if ((read_arg & 0xff) == TABLE_TRANSFER_FAILED) { + od_error_type = read_arg >> 16; + dev_err(smu->adev->dev, "Invalid overdrive table content: %s (%d)\n", + od_error_type < ARRAY_SIZE(od_error_message) ? + od_error_message[od_error_type] : "unknown", + od_error_type); + } + } return ret; } @@ -2374,6 +2418,7 @@ static int smu_v14_0_2_od_restore_table_single(struct smu_context *smu, long inp } od_table->OverDriveTable.FanMode = FAN_MODE_AUTO; od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT); + od_table->OverDriveTable.FeatureCtrlMask &= ~BIT(PP_OD_FEATURE_FAN_LEGACY_BIT); break; case PP_OD_EDIT_FAN_ZERO_RPM_ENABLE: od_table->OverDriveTable.FanZeroRpmEnable = @@ -2402,7 +2447,8 @@ static int smu_v14_0_2_od_restore_table_single(struct smu_context *smu, long inp od_table->OverDriveTable.FanMinimumPwm = boot_overdrive_table->OverDriveTable.FanMinimumPwm; od_table->OverDriveTable.FanMode = FAN_MODE_AUTO; - od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT); + od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_LEGACY_BIT); + od_table->OverDriveTable.FeatureCtrlMask &= ~BIT(PP_OD_FEATURE_FAN_CURVE_BIT); break; default: dev_info(adev->dev, "Invalid table index: %ld\n", input); @@ -2572,6 +2618,7 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu, od_table->OverDriveTable.FanLinearPwmPoints[input[0]] = input[2]; od_table->OverDriveTable.FanMode = FAN_MODE_MANUAL_LINEAR; od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT); + od_table->OverDriveTable.FeatureCtrlMask &= ~BIT(PP_OD_FEATURE_FAN_LEGACY_BIT); break; case PP_OD_EDIT_ACOUSTIC_LIMIT: @@ -2641,7 +2688,7 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu, break; case PP_OD_EDIT_FAN_MINIMUM_PWM: - if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) { + if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_LEGACY_BIT)) { dev_warn(adev->dev, "Fan curve setting not supported!\n"); return -ENOTSUPP; } @@ -2659,7 +2706,8 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu, od_table->OverDriveTable.FanMinimumPwm = input[0]; od_table->OverDriveTable.FanMode = FAN_MODE_AUTO; - od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT); + od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_LEGACY_BIT); + od_table->OverDriveTable.FeatureCtrlMask &= ~BIT(PP_OD_FEATURE_FAN_CURVE_BIT); break; case PP_OD_EDIT_FAN_ZERO_RPM_ENABLE: diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c index cc2babc6a341..db85186f2d66 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c @@ -344,7 +344,12 @@ static int smu_v15_0_8_get_metrics_table_internal(struct smu_context *smu, uint3 } amdgpu_device_invalidate_hdp(smu->adev, NULL); - memcpy(smu_table->metrics_table, table->cpu_addr, table_size); + ret = smu_cmn_vram_cpy(smu, smu_table->metrics_table, + table->cpu_addr, table_size); + if (ret) { + mutex_unlock(&smu_table->metrics_lock); + return ret; + } smu_table->metrics_time = jiffies; } @@ -551,9 +556,14 @@ static int smu_v15_0_8_get_system_metrics_table(struct smu_context *smu) } amdgpu_hdp_invalidate(smu->adev, NULL); + + ret = smu_cmn_vram_cpy(smu, sys_table->cache.buffer, + table->cpu_addr, + sizeof(SystemMetricsTable_t)); + if (ret) + return ret; + smu_table_cache_update_time(sys_table, jiffies); - memcpy(sys_table->cache.buffer, table->cpu_addr, - sizeof(SystemMetricsTable_t)); return 0; } @@ -988,9 +998,9 @@ static int smu_v15_0_8_get_static_metrics_table(struct smu_context *smu) } amdgpu_hdp_invalidate(smu->adev, NULL); - memcpy(smu_table->metrics_table, table->cpu_addr, table_size); - return 0; + return smu_cmn_vram_cpy(smu, smu_table->metrics_table, + table->cpu_addr, table_size); } static int smu_v15_0_8_fru_get_product_info(struct smu_context *smu, @@ -1601,8 +1611,6 @@ static ssize_t smu_v15_0_8_get_gpu_metrics(struct smu_context *smu, void **table uint32_t mid_mask = adev->aid_mask; MetricsTable_t *metrics; - metrics = kzalloc(sizeof(MetricsTable_t), GFP_KERNEL); - ret = smu_v15_0_8_get_metrics_table_internal(smu, 1, NULL); if (ret) return ret; @@ -1775,7 +1783,7 @@ static int smu_v15_0_8_get_power_limit(struct smu_context *smu, *current_power_limit = power_limit; if (default_power_limit) - *max_power_limit = pptable->MaxSocketPowerLimit; + *default_power_limit = pptable->MaxSocketPowerLimit; if (max_power_limit) *max_power_limit = pptable->MaxSocketPowerLimit; @@ -1901,42 +1909,36 @@ static int smu_v15_0_8_set_soft_freq_limited_range(struct smu_context *smu, if (smu_dpm->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) return -EINVAL; - if (smu_dpm->dpm_level == AMD_DPM_FORCED_LEVEL_MANUAL) { - if (min >= max) { - dev_err(smu->adev->dev, - "Minimum clk should be less than the maximum allowed clock\n"); - return -EINVAL; - } - - if (clk_type == SMU_GFXCLK || clk_type == SMU_SCLK) { - if ((min == pstate_table->gfxclk_pstate.curr.min) && - (max == pstate_table->gfxclk_pstate.curr.max)) - return 0; - - ret = smu_v15_0_8_set_gfx_soft_freq_limited_range(smu, - min, max); - if (!ret) { - pstate_table->gfxclk_pstate.curr.min = min; - pstate_table->gfxclk_pstate.curr.max = max; - } - } - - if (clk_type == SMU_UCLK) { - if (max == pstate_table->uclk_pstate.curr.max) - return 0; - - ret = smu_v15_0_set_soft_freq_limited_range(smu, - SMU_UCLK, - 0, max, - false); - if (!ret) - pstate_table->uclk_pstate.curr.max = max; - } - - return ret; + if (min >= max) { + dev_err(smu->adev->dev, + "Minimum clk should be less than the maximum allowed clock\n"); + return -EINVAL; } - return 0; + if (clk_type == SMU_GFXCLK || clk_type == SMU_SCLK) { + if ((min == pstate_table->gfxclk_pstate.curr.min) && + (max == pstate_table->gfxclk_pstate.curr.max)) + return 0; + + ret = smu_v15_0_8_set_gfx_soft_freq_limited_range(smu, min, + max); + if (!ret) { + pstate_table->gfxclk_pstate.curr.min = min; + pstate_table->gfxclk_pstate.curr.max = max; + } + } + + if (clk_type == SMU_UCLK) { + if (max == pstate_table->uclk_pstate.curr.max) + return 0; + + ret = smu_v15_0_set_soft_freq_limited_range(smu, SMU_UCLK, 0, + max, false); + if (!ret) + pstate_table->uclk_pstate.curr.max = max; + } + + return ret; } static int smu_v15_0_8_od_edit_dpm_table(struct smu_context *smu, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c index 7bd8c435466a..90c7127beabf 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c @@ -496,7 +496,8 @@ static int smu_msg_v1_send_msg(struct smu_msg_ctl *ctl, } /* Read output args */ - if (ret == 0 && args->num_out_args > 0) { + if ((ret == 0 || (args->flags & SMU_MSG_FLAG_FORCE_READ_ARG)) && + args->num_out_args > 0) { __smu_msg_v1_read_out_args(ctl, args); dev_dbg(adev->dev, "smu send message: %s(%d) resp : 0x%08x", smu_get_message_name(smu, args->msg), index, reg); @@ -1060,20 +1061,24 @@ int smu_cmn_check_fw_version(struct smu_context *smu) return 0; } -int smu_cmn_update_table(struct smu_context *smu, - enum smu_table_id table_index, - int argument, - void *table_data, - bool drv2smu) +int smu_cmn_update_table_read_arg(struct smu_context *smu, + enum smu_table_id table_index, + int argument, + void *table_data, + uint32_t *read_arg, + bool drv2smu) { - struct smu_table_context *smu_table = &smu->smu_table; struct amdgpu_device *adev = smu->adev; + struct smu_table_context *smu_table = &smu->smu_table; struct smu_table *table = &smu_table->driver_table; + struct smu_msg_ctl *ctl = &smu->msg_ctl; + struct smu_msg_args args; int table_id = smu_cmn_to_asic_specific_index(smu, CMN2ASIC_MAPPING_TABLE, table_index); uint32_t table_size; int ret = 0; + if (!table_data || table_index >= SMU_TABLE_COUNT || table_id < 0) return -EINVAL; @@ -1088,11 +1093,19 @@ int smu_cmn_update_table(struct smu_context *smu, amdgpu_hdp_flush(adev, NULL); } - ret = smu_cmn_send_smc_msg_with_param(smu, drv2smu ? - SMU_MSG_TransferTableDram2Smu : - SMU_MSG_TransferTableSmu2Dram, - table_id | ((argument & 0xFFFF) << 16), - NULL); + args.msg = drv2smu ? SMU_MSG_TransferTableDram2Smu : SMU_MSG_TransferTableSmu2Dram; + args.args[0] = ((argument & 0xFFFF) << 16) | (table_id & 0xffff); + args.num_args = 1; + args.out_args[0] = 0; + args.num_out_args = read_arg ? 1 : 0; + args.flags = read_arg ? SMU_MSG_FLAG_FORCE_READ_ARG : 0; + args.timeout = 0; + + ret = ctl->ops->send_msg(ctl, &args); + + if (read_arg) + *read_arg = args.out_args[0]; + if (ret) return ret; @@ -1104,6 +1117,18 @@ int smu_cmn_update_table(struct smu_context *smu, return 0; } +int smu_cmn_vram_cpy(struct smu_context *smu, void *dst, const void *src, + size_t len) +{ + memcpy(dst, src, len); + + /* Don't trust the copy operation if RAS fatal error happened. */ + if (amdgpu_ras_get_fed_status(smu->adev)) + return -EHWPOISON; + + return 0; +} + int smu_cmn_write_watermarks_table(struct smu_context *smu) { void *watermarks_table = smu->smu_table.watermarks_table; @@ -1345,7 +1370,7 @@ int smu_cmn_print_dpm_clk_levels(struct smu_context *smu, level_index = 1; } - if (!is_fine_grained) { + if (!is_fine_grained || count == 1) { for (i = 0; i < count; i++) { freq_match = !is_deep_sleep && smu_cmn_freqs_match( diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h index b76e86df5da7..c6ac0e876aea 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h @@ -102,6 +102,9 @@ int smu_msg_send_async_locked(struct smu_msg_ctl *ctl, #define SMU_DPM_PCIE_GEN_IDX(gen) smu_cmn_dpm_pcie_gen_idx((gen)) #define SMU_DPM_PCIE_WIDTH_IDX(width) smu_cmn_dpm_pcie_width_idx((width)) +#define smu_cmn_update_table(smu, table_index, argument, table_data, drv2smu) \ + smu_cmn_update_table_read_arg((smu), (table_index), (argument), (table_data), NULL, (drv2smu)) + extern const int link_speed[]; /* Helper to Convert from PCIE Gen 1/2/3/4/5/6 to 0.1 GT/s speed units */ @@ -168,11 +171,15 @@ int smu_cmn_get_smc_version(struct smu_context *smu, uint32_t *if_version, uint32_t *smu_version); -int smu_cmn_update_table(struct smu_context *smu, - enum smu_table_id table_index, - int argument, - void *table_data, - bool drv2smu); +int smu_cmn_update_table_read_arg(struct smu_context *smu, + enum smu_table_id table_index, + int argument, + void *table_data, + uint32_t *read_arg, + bool drv2smu); + +int smu_cmn_vram_cpy(struct smu_context *smu, void *dst, + const void *src, size_t len); int smu_cmn_write_watermarks_table(struct smu_context *smu); diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c index d213eea71cff..ad8862d43263 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c @@ -290,13 +290,10 @@ static int amdgpu_ras_mgr_sw_init(struct amdgpu_ip_block *ip_block) /* Disabled by default */ con->uniras_enabled = false; - /* Enabled only in debug mode */ - if (adev->debug_enable_ras_aca) { + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 14) || + adev->debug_enable_ras_aca) con->uniras_enabled = true; - RAS_DEV_INFO(adev, "Debug amdgpu uniras!"); - } - - if (!con->uniras_enabled) + else return 0; ras_mgr = kzalloc_obj(*ras_mgr); diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c index c83357307c55..fb4d375e87b2 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c @@ -36,22 +36,22 @@ static int amdgpu_virt_ras_get_cmd_shared_mem(struct ras_core_context *ras_core, struct amdgpu_device *adev = ras_core->dev; struct amdsriov_ras_telemetry *ras_telemetry_cpu; struct amdsriov_ras_telemetry *ras_telemetry_gpu; + void *fw_va = adev->mman.resv_region[AMDGPU_RESV_FW_VRAM_USAGE].cpu_ptr; + void *drv_va = adev->mman.resv_region[AMDGPU_RESV_DRV_VRAM_USAGE].cpu_ptr; uint64_t fw_vram_usage_start_offset = 0; uint64_t ras_telemetry_offset = 0; if (!adev->virt.fw_reserve.ras_telemetry) return -EINVAL; - if (adev->mman.fw_vram_usage_va && - adev->mman.fw_vram_usage_va <= adev->virt.fw_reserve.ras_telemetry) { - fw_vram_usage_start_offset = adev->mman.fw_vram_usage_start_offset; + if (fw_va && fw_va <= adev->virt.fw_reserve.ras_telemetry) { + fw_vram_usage_start_offset = adev->mman.resv_region[AMDGPU_RESV_FW_VRAM_USAGE].offset; ras_telemetry_offset = (uintptr_t)adev->virt.fw_reserve.ras_telemetry - - (uintptr_t)adev->mman.fw_vram_usage_va; - } else if (adev->mman.drv_vram_usage_va && - adev->mman.drv_vram_usage_va <= adev->virt.fw_reserve.ras_telemetry) { - fw_vram_usage_start_offset = adev->mman.drv_vram_usage_start_offset; + (uintptr_t)fw_va; + } else if (drv_va && drv_va <= adev->virt.fw_reserve.ras_telemetry) { + fw_vram_usage_start_offset = adev->mman.resv_region[AMDGPU_RESV_DRV_VRAM_USAGE].offset; ras_telemetry_offset = (uintptr_t)adev->virt.fw_reserve.ras_telemetry - - (uintptr_t)adev->mman.drv_vram_usage_va; + (uintptr_t)drv_va; } else { return -EINVAL; } @@ -517,14 +517,9 @@ int amdgpu_virt_ras_hw_fini(struct amdgpu_device *adev) (struct amdgpu_virt_ras_cmd *)ras_mgr->virt_ras_cmd; struct vram_blocks_ecc *blks_ecc = &virt_ras->blocks_ecc; - if (blks_ecc->shared_mem.cpu_addr) { - __set_cmd_auto_update(adev, - RAS_CMD__GET_ALL_BLOCK_ECC_STATUS, - blks_ecc->shared_mem.gpa, - blks_ecc->shared_mem.size, false); - + if (blks_ecc->shared_mem.cpu_addr) memset(blks_ecc->shared_mem.cpu_addr, 0, blks_ecc->shared_mem.size); - } + memset(blks_ecc, 0, sizeof(*blks_ecc)); return 0; diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c index 441fd32dc91c..d64e328bf542 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c @@ -222,52 +222,58 @@ static const struct drm_bridge_funcs imx8qxp_pxl2dpi_bridge_funcs = { imx8qxp_pxl2dpi_bridge_atomic_get_output_bus_fmts, }; -static struct device_node * +static int imx8qxp_pxl2dpi_get_available_ep_from_port(struct imx8qxp_pxl2dpi *p2d, - u32 port_id) + u32 port_id, + struct device_node **ep) { - struct device_node *port, *ep; + struct device_node *port; + int ret = 0; int ep_cnt; + *ep = NULL; + port = of_graph_get_port_by_id(p2d->dev->of_node, port_id); if (!port) { DRM_DEV_ERROR(p2d->dev, "failed to get port@%u\n", port_id); - return ERR_PTR(-ENODEV); + return -ENODEV; } ep_cnt = of_get_available_child_count(port); if (ep_cnt == 0) { DRM_DEV_ERROR(p2d->dev, "no available endpoints of port@%u\n", port_id); - ep = ERR_PTR(-ENODEV); + ret = -ENODEV; goto out; } else if (ep_cnt > 1) { DRM_DEV_ERROR(p2d->dev, "invalid available endpoints of port@%u\n", port_id); - ep = ERR_PTR(-EINVAL); + ret = -EINVAL; goto out; } - ep = of_get_next_available_child(port, NULL); - if (!ep) { + *ep = of_get_next_available_child(port, NULL); + if (!*ep) { DRM_DEV_ERROR(p2d->dev, "failed to get available endpoint of port@%u\n", port_id); - ep = ERR_PTR(-ENODEV); + ret = -ENODEV; goto out; } out: of_node_put(port); - return ep; + return ret; } static int imx8qxp_pxl2dpi_find_next_bridge(struct imx8qxp_pxl2dpi *p2d) { - struct device_node *ep __free(device_node) = - imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1); - if (IS_ERR(ep)) - return PTR_ERR(ep); + struct device_node *ep __free(device_node) = NULL; + int ret; + + ret = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1, &ep); + if (ret) + return ret; struct device_node *remote __free(device_node) = of_graph_get_remote_port_parent(ep); if (!remote || !of_device_is_available(remote)) { @@ -291,9 +297,9 @@ static int imx8qxp_pxl2dpi_set_pixel_link_sel(struct imx8qxp_pxl2dpi *p2d) struct of_endpoint endpoint; int ret; - ep = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 0); - if (IS_ERR(ep)) - return PTR_ERR(ep); + ret = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 0, &ep); + if (ret) + return ret; ret = of_graph_parse_endpoint(ep, &endpoint); if (ret) { diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index ca4dea226f4b..ef7be20a59cd 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -345,10 +345,14 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host, if (pdata->host_ops && pdata->host_ops->attach) { ret = pdata->host_ops->attach(pdata->priv_data, device); if (ret < 0) - return ret; + goto err_remove_bridge; } return 0; + +err_remove_bridge: + drm_bridge_remove(&dsi->bridge); + return ret; } static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host, diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c index e6eaf9fd0251..a4bfd3ad166d 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c @@ -540,10 +540,14 @@ static int dw_mipi_dsi2_host_attach(struct mipi_dsi_host *host, if (pdata->host_ops && pdata->host_ops->attach) { ret = pdata->host_ops->attach(pdata->priv_data, device); if (ret < 0) - return ret; + goto err_remove_bridge; } return 0; + +err_remove_bridge: + drm_bridge_remove(&dsi2->bridge); + return ret; } static int dw_mipi_dsi2_host_detach(struct mipi_dsi_host *host, diff --git a/drivers/gpu/drm/bridge/tda998x_drv.c b/drivers/gpu/drm/bridge/tda998x_drv.c index d9b388165de1..6c427bc75896 100644 --- a/drivers/gpu/drm/bridge/tda998x_drv.c +++ b/drivers/gpu/drm/bridge/tda998x_drv.c @@ -1293,7 +1293,7 @@ static const struct drm_edid *tda998x_edid_read(struct tda998x_priv *priv, * can't handle signals gracefully. */ if (tda998x_edid_delay_wait(priv)) - return 0; + return NULL; if (priv->rev == TDA19988) reg_clear(priv, REG_TX4, TX4_PD_RAM); @@ -1762,7 +1762,7 @@ static const struct drm_bridge_funcs tda998x_bridge_funcs = { static int tda998x_get_audio_ports(struct tda998x_priv *priv, struct device_node *np) { - const u32 *port_data; + const __be32 *port_data; u32 size; int i; diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 26953ed6b53e..a768398a1884 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1916,7 +1916,7 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, ret = wait_event_timeout(*queue, state->crtcs[i].last_vblank_count != drm_crtc_vblank_count(crtc), - msecs_to_jiffies(100)); + msecs_to_jiffies(1000)); WARN(!ret, "[CRTC:%d:%s] vblank wait timed out\n", crtc->base.id, crtc->name); @@ -3751,6 +3751,13 @@ drm_atomic_helper_duplicate_state(struct drm_device *dev, err = PTR_ERR(plane_state); goto free; } + + if (plane_state->color_pipeline) { + err = drm_atomic_add_affected_colorops(state, plane); + if (err) + goto free; + } + } drm_connector_list_iter_begin(dev, &conn_iter); @@ -3856,6 +3863,8 @@ int drm_atomic_helper_commit_duplicated_state(struct drm_atomic_state *state, int i, ret; struct drm_plane *plane; struct drm_plane_state *new_plane_state; + struct drm_colorop *colorop; + struct drm_colorop_state *new_colorop_state; struct drm_connector *connector; struct drm_connector_state *new_conn_state; struct drm_crtc *crtc; @@ -3863,6 +3872,9 @@ int drm_atomic_helper_commit_duplicated_state(struct drm_atomic_state *state, state->acquire_ctx = ctx; + for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) + state->colorops[i].old_state = colorop->state; + for_each_new_plane_in_state(state, plane, new_plane_state, i) state->planes[i].old_state = plane->state; diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index c598b99673fc..e7db4e4ea700 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -831,7 +831,7 @@ static void fill_palette_332(struct drm_crtc *crtc, u16 r, u16 g, u16 b, } /** - * drm_crtc_fill_palette_332 - Programs a default palette for R332-like formats + * drm_crtc_fill_palette_332 - Programs a default palette for RGB332-like formats * @crtc: The displaying CRTC * @set_palette: Callback for programming the hardware gamma LUT * diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 5f9fcd7d9ce4..404208bf23a6 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -99,6 +99,29 @@ enum drm_edid_internal_quirk { }; #define MICROSOFT_IEEE_OUI 0xca125c +#define AMD_IEEE_OUI 0x00001A + +#define AMD_VSDB_V3_PAYLOAD_MIN_LEN 15 +#define AMD_VSDB_V3_PAYLOAD_MAX_LEN 20 + +struct amd_vsdb_v3_payload { + u8 oui[3]; + u8 version; + u8 feature_caps; + u8 rsvd0[3]; + u8 cs_eotf_support; + u8 lum1_max; + u8 lum1_min; + u8 lum2_max; + u8 lum2_min; + u8 rsvd1[2]; + /* + * Bytes beyond AMD_VSDB_V3_PAYLOAD_MIN_LEN are optional; a + * monitor may provide a payload as short as 15 bytes. Always + * check cea_db_payload_len() before accessing extra[]. + */ + u8 extra[AMD_VSDB_V3_PAYLOAD_MAX_LEN - AMD_VSDB_V3_PAYLOAD_MIN_LEN]; +} __packed; struct detailed_mode_closure { struct drm_connector *connector; @@ -5205,6 +5228,13 @@ static bool cea_db_is_microsoft_vsdb(const struct cea_db *db) cea_db_payload_len(db) == 21; } +static bool cea_db_is_amd_vsdb(const struct cea_db *db) +{ + return cea_db_is_vendor(db, AMD_IEEE_OUI) && + cea_db_payload_len(db) >= AMD_VSDB_V3_PAYLOAD_MIN_LEN && + cea_db_payload_len(db) <= AMD_VSDB_V3_PAYLOAD_MAX_LEN; +} + static bool cea_db_is_vcdb(const struct cea_db *db) { return cea_db_is_extended_tag(db, CTA_EXT_DB_VIDEO_CAP) && @@ -6401,6 +6431,45 @@ static void drm_parse_microsoft_vsdb(struct drm_connector *connector, connector->base.id, connector->name, version, db[5]); } +static void drm_parse_amd_vsdb(struct drm_connector *connector, + const struct cea_db *db) +{ + struct drm_display_info *info = &connector->display_info; + const u8 *data = cea_db_data(db); + const struct amd_vsdb_v3_payload *p; + + p = (const struct amd_vsdb_v3_payload *)data; + + if (p->version != 0x03) { + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Unsupported AMD VSDB version %u\n", + connector->base.id, connector->name, p->version); + return; + } + + info->amd_vsdb.version = p->version; + info->amd_vsdb.replay_mode = p->feature_caps & 0x40; + info->amd_vsdb.panel_type = (p->cs_eotf_support & 0xC0) >> 6; + info->amd_vsdb.luminance_range1.max_luminance = p->lum1_max; + info->amd_vsdb.luminance_range1.min_luminance = p->lum1_min; + info->amd_vsdb.luminance_range2.max_luminance = p->lum2_max; + info->amd_vsdb.luminance_range2.min_luminance = p->lum2_min; + + /* + * The AMD VSDB v3 payload length is variable (15..20 bytes). + * All fields through p->rsvd1 (byte 14) are always present, + * but p->extra[] (bytes 15+) may not be. Any future access to + * extra[] must be guarded with a runtime length check to avoid + * out-of-bounds reads on shorter (but spec-valid) payloads. + * For example: + * + * int len = cea_db_payload_len(db); + * + * if (len > AMD_VSDB_V3_PAYLOAD_MIN_LEN) + * info->amd_vsdb.foo = p->extra[0]; + */ +} + static void drm_parse_cea_ext(struct drm_connector *connector, const struct drm_edid *drm_edid) { @@ -6449,6 +6518,8 @@ static void drm_parse_cea_ext(struct drm_connector *connector, drm_parse_hdmi_forum_scds(connector, data); else if (cea_db_is_microsoft_vsdb(db)) drm_parse_microsoft_vsdb(connector, data); + else if (cea_db_is_amd_vsdb(db)) + drm_parse_amd_vsdb(connector, db); else if (cea_db_is_y420cmdb(db)) parse_cta_y420cmdb(connector, db, &y420cmdb_map); else if (cea_db_is_y420vdb(db)) @@ -6641,6 +6712,7 @@ static void drm_reset_display_info(struct drm_connector *connector) info->quirks = 0; info->source_physical_address = CEC_PHYS_ADDR_INVALID; + memset(&info->amd_vsdb, 0, sizeof(info->amd_vsdb)); } static void update_displayid_info(struct drm_connector *connector, diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index a80a335f4148..1541fc8a9ac2 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -490,7 +490,7 @@ static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off, * the number of horizontal pixels that need an update. */ off_t bit_off = (off % line_length) * 8; - off_t bit_end = (end % line_length) * 8; + off_t bit_end = bit_off + len * 8; x1 = bit_off / info->var.bits_per_pixel; x2 = DIV_ROUND_UP(bit_end, info->var.bits_per_pixel); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index d6424267260b..8afab57fc055 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1019,7 +1019,7 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_gem_change_handle *args = data; - struct drm_gem_object *obj; + struct drm_gem_object *obj, *idrobj; int handle, ret; if (!drm_core_check_feature(dev, DRIVER_GEM)) @@ -1042,12 +1042,30 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, mutex_lock(&file_priv->prime.lock); spin_lock(&file_priv->table_lock); + + /* When create_tail allocs an obj idr, it needs to first alloc as NULL, + * then later replace with the correct object. This is not necessary + * here, because the only operations that could race are drm_prime + * bookkeeping, and we hold the prime lock. + */ ret = idr_alloc(&file_priv->object_idr, obj, handle, handle + 1, GFP_NOWAIT); - spin_unlock(&file_priv->table_lock); - if (ret < 0) - goto out_unlock; + if (ret < 0) { + spin_unlock(&file_priv->table_lock); + goto out_unlock; + } + + idrobj = idr_replace(&file_priv->object_idr, NULL, handle); + if (idrobj != obj) { + idr_replace(&file_priv->object_idr, idrobj, handle); + idr_remove(&file_priv->object_idr, args->new_handle); + spin_unlock(&file_priv->table_lock); + ret = -ENOENT; + goto out_unlock; + } + + spin_unlock(&file_priv->table_lock); if (obj->dma_buf) { ret = drm_prime_add_buf_handle(&file_priv->prime, obj->dma_buf, @@ -1066,7 +1084,9 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, spin_lock(&file_priv->table_lock); idr_remove(&file_priv->object_idr, args->handle); + idrobj = idr_replace(&file_priv->object_idr, obj, handle); spin_unlock(&file_priv->table_lock); + WARN_ON(idrobj != NULL); out_unlock: mutex_unlock(&file_priv->prime.lock); diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 9166c353f131..88808e972cc1 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -172,8 +172,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, } for (i = 0; i < info->num_planes; i++) { - unsigned int width = mode_cmd->width / (i ? info->hsub : 1); - unsigned int height = mode_cmd->height / (i ? info->vsub : 1); + unsigned int width = drm_format_info_plane_width(info, mode_cmd->width, i); + unsigned int height = drm_format_info_plane_height(info, mode_cmd->height, i); unsigned int min_size; objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_sched.c b/drivers/gpu/drm/etnaviv/etnaviv_sched.c index df4232d7e135..3cc50d697c89 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_sched.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_sched.c @@ -116,16 +116,18 @@ int etnaviv_sched_push_job(struct etnaviv_gem_submit *submit) */ mutex_lock(&gpu->sched_lock); + ret = xa_alloc_cyclic(&gpu->user_fences, &submit->out_fence_id, + NULL, xa_limit_32b, &gpu->next_user_fence, + GFP_KERNEL); + if (ret < 0) + goto out_unlock; + drm_sched_job_arm(&submit->sched_job); submit->out_fence = dma_fence_get(&submit->sched_job.s_fence->finished); - ret = xa_alloc_cyclic(&gpu->user_fences, &submit->out_fence_id, - submit->out_fence, xa_limit_32b, - &gpu->next_user_fence, GFP_KERNEL); - if (ret < 0) { - drm_sched_job_cleanup(&submit->sched_job); - goto out_unlock; - } + + xa_store(&gpu->user_fences, submit->out_fence_id, + submit->out_fence, GFP_KERNEL); /* the scheduler holds on to the job now */ kref_get(&submit->refcount); diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c index 29a8366513fa..e68c954ec3e6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_mic.c +++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c @@ -423,7 +423,9 @@ static int exynos_mic_probe(struct platform_device *pdev) mic->bridge.of_node = dev->of_node; - drm_bridge_add(&mic->bridge); + ret = devm_drm_bridge_add(dev, &mic->bridge); + if (ret) + goto err; pm_runtime_enable(dev); @@ -443,12 +445,8 @@ err: static void exynos_mic_remove(struct platform_device *pdev) { - struct exynos_mic *mic = platform_get_drvdata(pdev); - component_del(&pdev->dev, &exynos_mic_component_ops); pm_runtime_disable(&pdev->dev); - - drm_bridge_remove(&mic->bridge); } static const struct of_device_id exynos_mic_of_match[] = { diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c index 58d7e191fd56..403d21cbb3a2 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c @@ -580,6 +580,7 @@ static int oaktrail_hdmi_get_modes(struct drm_connector *connector) } else { edid = (struct edid *)raw_edid; /* FIXME ? edid = drm_get_edid(connector, i2c_adap); */ + i2c_put_adapter(i2c_adap); } if (edid) { diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c index 884d324f0044..e194d0cce067 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c @@ -293,7 +293,7 @@ void oaktrail_lvds_init(struct drm_device *dev, { struct gma_encoder *gma_encoder; struct gma_connector *gma_connector; - struct gma_i2c_chan *ddc_bus; + struct gma_i2c_chan *ddc_bus = NULL; struct drm_connector *connector; struct drm_encoder *encoder; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); @@ -367,6 +367,8 @@ void oaktrail_lvds_init(struct drm_device *dev, if (edid == NULL && dev_priv->lpc_gpio_base) { ddc_bus = oaktrail_lvds_i2c_init(dev); if (!IS_ERR(ddc_bus)) { + if (i2c_adap) + i2c_put_adapter(i2c_adap); i2c_adap = &ddc_bus->base; edid = drm_get_edid(connector, i2c_adap); } @@ -421,7 +423,10 @@ out: err_unlock: mutex_unlock(&dev->mode_config.mutex); - gma_i2c_destroy(to_gma_i2c_chan(connector->ddc)); + if (!IS_ERR_OR_NULL(ddc_bus)) + gma_i2c_destroy(ddc_bus); + else if (i2c_adap) + i2c_put_adapter(i2c_adap); drm_encoder_cleanup(encoder); err_connector_cleanup: drm_connector_cleanup(connector); diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c index b8189cd5d864..03de219f7a64 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc.c +++ b/drivers/gpu/drm/i915/display/intel_crtc.c @@ -209,6 +209,8 @@ static struct intel_crtc *intel_crtc_alloc(void) crtc->base.state = &crtc_state->uapi; crtc->config = crtc_state; + INIT_LIST_HEAD(&crtc->pipe_head); + return crtc; } @@ -222,6 +224,8 @@ static void intel_crtc_destroy(struct drm_crtc *_crtc) { struct intel_crtc *crtc = to_intel_crtc(_crtc); + list_del(&crtc->pipe_head); + cpu_latency_qos_remove_request(&crtc->vblank_pm_qos); drm_crtc_cleanup(&crtc->base); @@ -308,6 +312,20 @@ static const struct drm_crtc_funcs i8xx_crtc_funcs = { .get_vblank_timestamp = intel_crtc_get_vblank_timestamp, }; +static void add_crtc_to_pipe_list(struct intel_display *display, struct intel_crtc *crtc) +{ + struct intel_crtc *iter; + + list_for_each_entry(iter, &display->pipe_list, pipe_head) { + if (crtc->pipe < iter->pipe) { + list_add_tail(&crtc->pipe_head, &iter->pipe_head); + return; + } + } + + list_add_tail(&crtc->pipe_head, &display->pipe_list); +} + static int __intel_crtc_init(struct intel_display *display, enum pipe pipe) { struct intel_plane *primary, *cursor; @@ -393,11 +411,11 @@ static int __intel_crtc_init(struct intel_display *display, enum pipe pipe) cpu_latency_qos_add_request(&crtc->vblank_pm_qos, PM_QOS_DEFAULT_VALUE); - drm_WARN_ON(display->drm, drm_crtc_index(&crtc->base) != crtc->pipe); - if (HAS_CASF(display) && crtc->num_scalers >= 2) drm_crtc_create_sharpness_strength_property(&crtc->base); + add_crtc_to_pipe_list(display, crtc); + return 0; fail: @@ -406,6 +424,31 @@ fail: return ret; } +#define HAS_PIPE(display, pipe) (DISPLAY_RUNTIME_INFO(display)->pipe_mask & BIT(pipe)) + +/* + * Expose the pipes in order A, C, B, D on discrete platforms to trick user + * space into using pipes that are more likely to be available for both a) user + * space if pipe B has been reserved for the joiner, and b) the joiner if pipe A + * doesn't need the joiner. + * + * Swap pipes B and C only if both are available i.e. not fused off. + */ +static enum pipe reorder_pipe(struct intel_display *display, enum pipe pipe) +{ + if (!display->platform.dgfx || !HAS_PIPE(display, PIPE_B) || !HAS_PIPE(display, PIPE_C)) + return pipe; + + switch (pipe) { + case PIPE_B: + return PIPE_C; + case PIPE_C: + return PIPE_B; + default: + return pipe; + } +} + int intel_crtc_init(struct intel_display *display) { enum pipe pipe; @@ -415,7 +458,7 @@ int intel_crtc_init(struct intel_display *display) INTEL_NUM_PIPES(display), str_plural(INTEL_NUM_PIPES(display))); for_each_pipe(display, pipe) { - ret = __intel_crtc_init(display, pipe); + ret = __intel_crtc_init(display, reorder_pipe(display, pipe)); if (ret) return ret; } diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 10b6c6fcb03f..ad2fe10b6b1f 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -5939,17 +5939,6 @@ static int intel_atomic_check_joiner(struct intel_atomic_state *state, return -EINVAL; } - /* - * The state copy logic assumes the primary crtc gets processed - * before the secondary crtc during the main compute_config loop. - * This works because the crtcs are created in pipe order, - * and the hardware requires primary pipe < secondary pipe as well. - * Should that change we need to rethink the logic. - */ - if (WARN_ON(drm_crtc_index(&primary_crtc->base) > - drm_crtc_index(&secondary_crtc->base))) - return -EINVAL; - drm_dbg_kms(display->drm, "[CRTC:%d:%s] Used as secondary for joiner primary [CRTC:%d:%s]\n", secondary_crtc->base.base.id, secondary_crtc->base.name, @@ -6327,9 +6316,7 @@ static int intel_atomic_check_config(struct intel_atomic_state *state, for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { if (!intel_crtc_needs_modeset(new_crtc_state)) { - if (intel_crtc_is_joiner_secondary(new_crtc_state)) - copy_joiner_crtc_state_nomodeset(state, crtc); - else + if (!intel_crtc_is_joiner_secondary(new_crtc_state)) intel_crtc_copy_uapi_to_hw_state_nomodeset(state, crtc); continue; } @@ -6460,8 +6447,11 @@ int intel_atomic_check(struct drm_device *dev, goto fail; for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { - if (!intel_crtc_needs_modeset(new_crtc_state)) + if (!intel_crtc_needs_modeset(new_crtc_state)) { + if (intel_crtc_is_joiner_secondary(new_crtc_state)) + copy_joiner_crtc_state_nomodeset(state, crtc); continue; + } if (intel_crtc_is_joiner_secondary(new_crtc_state)) { drm_WARN_ON(display->drm, new_crtc_state->uapi.enable); diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index 552a59d19e0f..1e76a455d7c4 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -212,22 +212,23 @@ enum phy_fia { base.head) \ for_each_if((intel_plane)->pipe == (intel_crtc)->pipe) -#define for_each_intel_crtc(dev, intel_crtc) \ - list_for_each_entry(intel_crtc, \ - &(dev)->mode_config.crtc_list, \ - base.head) +#define for_each_intel_crtc(dev, crtc) \ + list_for_each_entry((crtc), \ + &to_intel_display(dev)->pipe_list, \ + pipe_head) -#define for_each_intel_crtc_in_pipe_mask(dev, intel_crtc, pipe_mask) \ - list_for_each_entry(intel_crtc, \ - &(dev)->mode_config.crtc_list, \ - base.head) \ - for_each_if((pipe_mask) & BIT(intel_crtc->pipe)) +#define for_each_intel_crtc_reverse(dev, crtc) \ + list_for_each_entry_reverse((crtc), \ + &to_intel_display(dev)->pipe_list, \ + pipe_head) -#define for_each_intel_crtc_in_pipe_mask_reverse(dev, intel_crtc, pipe_mask) \ - list_for_each_entry_reverse((intel_crtc), \ - &(dev)->mode_config.crtc_list, \ - base.head) \ - for_each_if((pipe_mask) & BIT((intel_crtc)->pipe)) +#define for_each_intel_crtc_in_pipe_mask(dev, crtc, pipe_mask) \ + for_each_intel_crtc((dev), (crtc)) \ + for_each_if((pipe_mask) & BIT((crtc)->pipe)) + +#define for_each_intel_crtc_in_pipe_mask_reverse(dev, crtc, pipe_mask) \ + for_each_intel_crtc_reverse((dev), (crtc)) \ + for_each_if((pipe_mask) & BIT((crtc)->pipe)) #define for_each_intel_encoder(dev, intel_encoder) \ list_for_each_entry(intel_encoder, \ @@ -269,14 +270,6 @@ enum phy_fia { (__i)++) \ for_each_if(plane) -#define for_each_old_intel_crtc_in_state(__state, crtc, old_crtc_state, __i) \ - for ((__i) = 0; \ - (__i) < (__state)->base.dev->mode_config.num_crtc && \ - ((crtc) = to_intel_crtc((__state)->base.crtcs[__i].ptr), \ - (old_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].old_state), 1); \ - (__i)++) \ - for_each_if(crtc) - #define for_each_new_intel_plane_in_state(__state, plane, new_plane_state, __i) \ for ((__i) = 0; \ (__i) < (__state)->base.dev->mode_config.num_total_plane && \ @@ -285,22 +278,6 @@ enum phy_fia { (__i)++) \ for_each_if(plane) -#define for_each_new_intel_crtc_in_state(__state, crtc, new_crtc_state, __i) \ - for ((__i) = 0; \ - (__i) < (__state)->base.dev->mode_config.num_crtc && \ - ((crtc) = to_intel_crtc((__state)->base.crtcs[__i].ptr), \ - (new_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].new_state), 1); \ - (__i)++) \ - for_each_if(crtc) - -#define for_each_new_intel_crtc_in_state_reverse(__state, crtc, new_crtc_state, __i) \ - for ((__i) = (__state)->base.dev->mode_config.num_crtc - 1; \ - (__i) >= 0 && \ - ((crtc) = to_intel_crtc((__state)->base.crtcs[__i].ptr), \ - (new_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].new_state), 1); \ - (__i)--) \ - for_each_if(crtc) - #define for_each_oldnew_intel_plane_in_state(__state, plane, old_plane_state, new_plane_state, __i) \ for ((__i) = 0; \ (__i) < (__state)->base.dev->mode_config.num_total_plane && \ @@ -310,23 +287,32 @@ enum phy_fia { (__i)++) \ for_each_if(plane) +#define for_each_old_intel_crtc_in_state(__state, crtc, old_crtc_state, __i) \ + for_each_intel_crtc((__state)->base.dev, (crtc)) \ + for_each_if(((__i) = drm_crtc_index(&(crtc)->base), (void)(__i), \ + (old_crtc_state) = intel_atomic_get_old_crtc_state((__state), (crtc)))) + +#define for_each_new_intel_crtc_in_state(__state, crtc, new_crtc_state, __i) \ + for_each_intel_crtc((__state)->base.dev, (crtc)) \ + for_each_if(((__i) = drm_crtc_index(&(crtc)->base), (void)(__i), \ + (new_crtc_state) = intel_atomic_get_new_crtc_state((__state), (crtc)))) + +#define for_each_new_intel_crtc_in_state_reverse(__state, crtc, new_crtc_state, __i) \ + for_each_intel_crtc_reverse((__state)->base.dev, (crtc)) \ + for_each_if(((__i) = drm_crtc_index(&(crtc)->base), (void)(__i), \ + (new_crtc_state) = intel_atomic_get_new_crtc_state((__state), (crtc)))) + #define for_each_oldnew_intel_crtc_in_state(__state, crtc, old_crtc_state, new_crtc_state, __i) \ - for ((__i) = 0; \ - (__i) < (__state)->base.dev->mode_config.num_crtc && \ - ((crtc) = to_intel_crtc((__state)->base.crtcs[__i].ptr), \ - (old_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].old_state), \ - (new_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].new_state), 1); \ - (__i)++) \ - for_each_if(crtc) + for_each_intel_crtc((__state)->base.dev, (crtc)) \ + for_each_if(((__i) = drm_crtc_index(&(crtc)->base), (void)(__i), \ + (old_crtc_state) = intel_atomic_get_old_crtc_state((__state), (crtc)), \ + (new_crtc_state) = intel_atomic_get_new_crtc_state((__state), (crtc)))) #define for_each_oldnew_intel_crtc_in_state_reverse(__state, crtc, old_crtc_state, new_crtc_state, __i) \ - for ((__i) = (__state)->base.dev->mode_config.num_crtc - 1; \ - (__i) >= 0 && \ - ((crtc) = to_intel_crtc((__state)->base.crtcs[__i].ptr), \ - (old_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].old_state), \ - (new_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].new_state), 1); \ - (__i)--) \ - for_each_if(crtc) + for_each_intel_crtc_reverse((__state)->base.dev, (crtc)) \ + for_each_if(((__i) = drm_crtc_index(&(crtc)->base), (void)(__i), \ + (old_crtc_state) = intel_atomic_get_old_crtc_state((__state), (crtc)), \ + (new_crtc_state) = intel_atomic_get_new_crtc_state((__state), (crtc)))) #define intel_atomic_crtc_state_for_each_plane_state( \ plane, plane_state, \ diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index d708d322aa85..d9baca2d5aaf 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -294,6 +294,9 @@ struct intel_display { /* Parent, or core, driver functions exposed to display */ const struct intel_display_parent_interface *parent; + /* list of all intel_crtcs sorted by pipe */ + struct list_head pipe_list; + /* Display functions */ struct { /* Top level crtc-ish functions */ diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index 23bfecc983e8..9c2f7ad6c7b7 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -117,6 +117,7 @@ static void intel_mode_config_init(struct intel_display *display) drm_mode_config_init(display->drm); INIT_LIST_HEAD(&display->global.obj_list); + INIT_LIST_HEAD(&display->pipe_list); mode_config->min_width = 0; mode_config->min_height = 0; diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index e2496db1642a..f6cd0a062090 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1484,6 +1484,7 @@ struct intel_flipq { struct intel_crtc { struct drm_crtc base; + struct list_head pipe_head; enum pipe pipe; /* * Whether the crtc and the connected output pipeline is active. Implies diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 4955bd8b11d7..50d34b4fdf82 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3119,8 +3119,13 @@ static void intel_dp_compute_vsc_colorimetry(const struct intel_crtc_state *crtc drm_WARN_ON(display->drm, vsc->bpc == 6 && vsc->pixelformat != DP_PIXELFORMAT_RGB); - /* all YCbCr are always limited range */ - vsc->dynamic_range = DP_DYNAMIC_RANGE_CTA; + /* All YCbCr formats are always limited range. */ + if (vsc->pixelformat == DP_PIXELFORMAT_RGB) + vsc->dynamic_range = crtc_state->limited_color_range ? + DP_DYNAMIC_RANGE_CTA : DP_DYNAMIC_RANGE_VESA; + else + vsc->dynamic_range = DP_DYNAMIC_RANGE_CTA; + vsc->content_type = DP_CONTENT_TYPE_NOT_DEFINED; } diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index d0c76632a946..a8d56ebf06a2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -615,8 +615,13 @@ check_if_vesa_backlight_possible(struct intel_dp *intel_dp) int ret; u8 bit_min, bit_max; - if (!(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) - return true; + /* + * Since we only support Fully AUX Based VESA Backlight interface make sure + * backlight enable is possible via AUX along with backlight adjustment + */ + if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP && + intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) + return false; ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &bit_min); if (ret < 0) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 998c3faf5f2e..53c10ae76ab5 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -2981,7 +2981,7 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, return ret; do { - bool cursor_in_su_area; + bool cursor_in_su_area = false; /* * Adjust su area to cover cursor fully as necessary diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index d45b3bcc6ef0..e0ac4e2ce4dc 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -4028,8 +4028,8 @@ void intel_wm_state_verify(struct intel_atomic_state *state, } /* DDB */ - hw_ddb_entry = &hw->ddb[PLANE_CURSOR]; - sw_ddb_entry = &new_crtc_state->wm.skl.plane_ddb[PLANE_CURSOR]; + hw_ddb_entry = &hw->ddb[plane->id]; + sw_ddb_entry = &new_crtc_state->wm.skl.plane_ddb[plane->id]; if (!skl_ddb_entry_equal(hw_ddb_entry, sw_ddb_entry)) { drm_err(display->drm, diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c index 984d0056c01c..adff482a6c9c 100644 --- a/drivers/gpu/drm/i915/gt/intel_reset.c +++ b/drivers/gpu/drm/i915/gt/intel_reset.c @@ -132,7 +132,8 @@ void __i915_request_reset(struct i915_request *rq, bool guilty) rcu_read_lock(); /* protect the GEM context */ if (guilty) { i915_request_set_error_once(rq, -EIO); - __i915_request_skip(rq); + if (!i915_request_signaled(rq)) + __i915_request_skip(rq); banned = mark_guilty(rq); } else { i915_request_set_error_once(rq, -EAGAIN); diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 385a634c3ed0..d9be7a5a239c 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -750,9 +750,8 @@ static bool has_auxccs(struct drm_device *drm) { struct drm_i915_private *i915 = to_i915(drm); - return IS_GRAPHICS_VER(i915, 9, 12) || - IS_ALDERLAKE_P(i915) || - IS_METEORLAKE(i915); + return IS_GRAPHICS_VER(i915, 9, 12) && + !HAS_FLAT_CCS(i915); } static bool has_fenced_regions(struct drm_device *drm) diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c index e154cb35f604..6193811ef7be 100644 --- a/drivers/gpu/drm/imagination/pvr_fw_trace.c +++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c @@ -558,6 +558,6 @@ pvr_fw_trace_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir) &pvr_fw_trace_fops); } - debugfs_create_file("trace_mask", 0600, dir, fw_trace, + debugfs_create_file("trace_mask", 0600, dir, pvr_dev, &pvr_fw_trace_mask_fops); } diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c index 1ece1ea42f78..34405073c4d4 100644 --- a/drivers/gpu/drm/loongson/lsdc_drv.c +++ b/drivers/gpu/drm/loongson/lsdc_drv.c @@ -293,7 +293,7 @@ static int lsdc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) vga_client_register(pdev, lsdc_vga_set_decode); - drm_kms_helper_poll_init(ddev); + drmm_kms_helper_poll_init(ddev); if (loongson_vblank) { ret = drm_vblank_init(ddev, descp->num_of_crtc); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 5d8475e4895e..517ff2c31dce 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -875,7 +875,7 @@ static int nouveau_drm_probe(struct pci_dev *pdev, /* Remove conflicting drivers (vesafb, efifb etc). */ ret = aperture_remove_conflicting_pci_devices(pdev, driver_pci.name); if (ret) - return ret; + goto fail_nvkm; pci_set_master(pdev); diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 82621ede42e1..20dba02d6175 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -686,7 +686,7 @@ nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli, } nvbo = (void *)(unsigned long)bo[r->reloc_bo_index].user_priv; - if (unlikely(r->reloc_bo_offset + 4 > + if (unlikely((u64)r->reloc_bo_offset + 4 > nvbo->bo.base.size)) { NV_PRINTK(err, cli, "reloc outside of bo\n"); ret = -EINVAL; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index 72848ed80df7..b101e14f841e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -2513,6 +2513,7 @@ static const struct nvkm_device_chip nv170_chipset = { .name = "GA100", .bar = { 0x00000001, tu102_bar_new }, + .bios = { 0x00000001, nvkm_bios_new }, .devinit = { 0x00000001, ga100_devinit_new }, .fault = { 0x00000001, tu102_fault_new }, .fb = { 0x00000001, ga100_fb_new }, @@ -2529,7 +2530,6 @@ nv170_chipset = { .vfn = { 0x00000001, ga100_vfn_new }, .ce = { 0x000003ff, ga100_ce_new }, .fifo = { 0x00000001, ga100_fifo_new }, - .sec2 = { 0x00000001, tu102_sec2_new }, }; static const struct nvkm_device_chip @@ -3341,7 +3341,6 @@ nvkm_device_ctor(const struct nvkm_device_func *func, case 0x166: device->chip = &nv166_chipset; break; case 0x167: device->chip = &nv167_chipset; break; case 0x168: device->chip = &nv168_chipset; break; - case 0x170: device->chip = &nv170_chipset; break; case 0x172: device->chip = &nv172_chipset; break; case 0x173: device->chip = &nv173_chipset; break; case 0x174: device->chip = &nv174_chipset; break; @@ -3361,6 +3360,14 @@ nvkm_device_ctor(const struct nvkm_device_func *func, case 0x1b6: device->chip = &nv1b6_chipset; break; case 0x1b7: device->chip = &nv1b7_chipset; break; default: + if (nvkm_boolopt(device->cfgopt, "NvEnableUnsupportedChipsets", false)) { + switch (device->chipset) { + case 0x170: device->chip = &nv170_chipset; break; + default: + break; + } + } + if (!device->chip) { nvdev_error(device, "unknown chipset (%08x)\n", boot0); ret = -ENODEV; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c index fdd820eeef81..27a13aeccd3c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c @@ -41,11 +41,15 @@ ga100_gsp_flcn = { static const struct nvkm_gsp_func ga100_gsp = { .flcn = &ga100_gsp_flcn, + .fwsec = &tu102_gsp_fwsec, .sig_section = ".fwsignature_ga100", .booter.ctor = tu102_gsp_booter_ctor, + .fwsec_sb.ctor = tu102_gsp_fwsec_sb_ctor, + .fwsec_sb.dtor = tu102_gsp_fwsec_sb_dtor, + .dtor = r535_gsp_dtor, .oneinit = tu102_gsp_oneinit, .init = tu102_gsp_init, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c index dd82c76b8b9a..19cb269e7a26 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c @@ -318,13 +318,8 @@ tu102_gsp_oneinit(struct nvkm_gsp *gsp) if (ret) return ret; - /* - * Calculate FB layout. FRTS is a memory region created by the FWSEC-FRTS firmware. - * FWSEC comes from VBIOS. So on systems with no VBIOS (e.g. GA100), the FRTS does - * not exist. Therefore, use the existence of VBIOS to determine whether to reserve - * an FRTS region. - */ - gsp->fb.wpr2.frts.size = device->bios ? 0x100000 : 0; + /* Calculate FB layout. */ + gsp->fb.wpr2.frts.size = 0x100000; gsp->fb.wpr2.frts.addr = ALIGN_DOWN(gsp->fb.bios.addr, 0x20000) - gsp->fb.wpr2.frts.size; gsp->fb.wpr2.boot.size = gsp->boot.fw.size; @@ -348,12 +343,9 @@ tu102_gsp_oneinit(struct nvkm_gsp *gsp) if (ret) return ret; - /* Only boot FWSEC-FRTS if it actually exists */ - if (gsp->fb.wpr2.frts.size) { - ret = nvkm_gsp_fwsec_frts(gsp); - if (WARN_ON(ret)) - return ret; - } + ret = nvkm_gsp_fwsec_frts(gsp); + if (WARN_ON(ret)) + return ret; /* Reset GSP into RISC-V mode. */ ret = gsp->func->reset(gsp); diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index d6863b28ddc5..d592f4f4b939 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -208,6 +208,7 @@ config DRM_PANEL_HIMAX_HX83121A depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_DSC_HELPER select DRM_KMS_HELPER help Say Y here if you want to enable support for Himax HX83121A-based diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c index d5fe105bdbdd..658ce64c71eb 100644 --- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c +++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c @@ -1324,6 +1324,8 @@ static int boe_panel_disable(struct drm_panel *panel) mipi_dsi_dcs_set_display_off_multi(&ctx); mipi_dsi_dcs_enter_sleep_mode_multi(&ctx); + boe->dsi->mode_flags |= MIPI_DSI_MODE_LPM; + mipi_dsi_msleep(&ctx, 150); return ctx.accum_err; diff --git a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c index 4f8d6d8c07e4..dbdb7e3cb7b6 100644 --- a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c +++ b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c @@ -98,9 +98,7 @@ static int feiyang_enable(struct drm_panel *panel) /* T12 (video & logic signal rise + backlight rise) T12 >= 200ms */ msleep(200); - mipi_dsi_dcs_set_display_on(ctx->dsi); - - return 0; + return mipi_dsi_dcs_set_display_on(ctx->dsi); } static int feiyang_disable(struct drm_panel *panel) diff --git a/drivers/gpu/drm/panel/panel-himax-hx83102.c b/drivers/gpu/drm/panel/panel-himax-hx83102.c index 8b2a68ee851e..a5e5c9ea7a73 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx83102.c +++ b/drivers/gpu/drm/panel/panel-himax-hx83102.c @@ -937,6 +937,8 @@ static int hx83102_disable(struct drm_panel *panel) mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + mipi_dsi_msleep(&dsi_ctx, 150); return dsi_ctx.accum_err; diff --git a/drivers/gpu/drm/panel/panel-himax-hx83121a.c b/drivers/gpu/drm/panel/panel-himax-hx83121a.c index ebe643ba4184..bed79aa06f46 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx83121a.c +++ b/drivers/gpu/drm/panel/panel-himax-hx83121a.c @@ -596,8 +596,8 @@ static int himax_probe(struct mipi_dsi_device *dsi) ctx = devm_drm_panel_alloc(dev, struct himax, panel, &himax_panel_funcs, DRM_MODE_CONNECTOR_DSI); - if (!ctx) - return -ENOMEM; + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ret = devm_regulator_bulk_get_const(&dsi->dev, ARRAY_SIZE(himax_supplies), diff --git a/drivers/gpu/drm/panel/panel-visionox-rm69299.c b/drivers/gpu/drm/panel/panel-visionox-rm69299.c index e5e688cf98fd..f1430370ff94 100644 --- a/drivers/gpu/drm/panel/panel-visionox-rm69299.c +++ b/drivers/gpu/drm/panel/panel-visionox-rm69299.c @@ -376,6 +376,8 @@ static int visionox_rm69299_probe(struct mipi_dsi_device *dsi) return PTR_ERR(ctx->reset_gpio); } + ctx->panel.prepare_prev_first = true; + ctx->panel.backlight = visionox_rm69299_create_backlight(ctx); if (IS_ERR(ctx->panel.backlight)) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 711f5101aa04..074c0995ddc2 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -390,6 +390,8 @@ panfrost_ioctl_wait_bo(struct drm_device *dev, void *data, true, timeout); if (!ret) ret = timeout ? -ETIMEDOUT : -EBUSY; + else if (ret > 0) + ret = 0; drm_gem_object_put(gem_obj); diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c index 6d14b0269574..cd49859da89b 100644 --- a/drivers/gpu/drm/panthor/panthor_gem.c +++ b/drivers/gpu/drm/panthor/panthor_gem.c @@ -157,7 +157,7 @@ void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo) /** * panthor_kernel_bo_create() - Create and map a GEM object to a VM * @ptdev: Device. - * @vm: VM to map the GEM to. If NULL, the kernel object is not GPU mapped. + * @vm: VM to map the GEM to. * @size: Size of the buffer object. * @bo_flags: Combination of drm_panthor_bo_flags flags. * @vm_map_flags: Combination of drm_panthor_vm_bind_op_flags (only those diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index f8c41e36afa4..75d98dad7b1d 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -1648,6 +1648,25 @@ static int panthor_vm_lock_region(struct panthor_vm *vm, u64 start, u64 size) start + size <= vm->locked_region.start + vm->locked_region.size) return 0; + /* sm_step_remap() may need a locked region that isn't a strict superset + * of the original one because of having to extend unmap boundaries beyond + * it to deal with partial unmaps of transparent huge pages. What we want + * in those cases is to lock the union of both regions. The new region must + * always overlap with the original one, because the upper and lower unmap + * boundaries in a remap operation can only shift up or down respectively, + * but never otherwise. + */ + if (vm->locked_region.size) { + u64 end = max(vm->locked_region.start + vm->locked_region.size, + start + size); + + drm_WARN_ON_ONCE(&vm->ptdev->base, (start + size <= vm->locked_region.start) || + (start >= vm->locked_region.start + vm->locked_region.size)); + + start = min(start, vm->locked_region.start); + size = end - start; + } + mutex_lock(&ptdev->mmu->as.slots_lock); if (vm->as.id >= 0 && size) { /* Lock the region that needs to be updated */ diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index 2bbb1168a3ff..1e6a2392d7c6 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -118,12 +118,13 @@ qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Complete initialization. */ ret = drm_dev_register(&qdev->ddev, ent->driver_data); if (ret) - goto modeset_cleanup; + goto poll_fini; drm_client_setup(&qdev->ddev, NULL); return 0; -modeset_cleanup: +poll_fini: + drm_kms_helper_poll_fini(&qdev->ddev); qxl_modeset_fini(qdev); unload: qxl_device_fini(qdev); @@ -154,6 +155,7 @@ qxl_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); + drm_kms_helper_poll_fini(dev); drm_dev_unregister(dev); drm_atomic_helper_shutdown(dev); if (pci_is_vga(pdev) && pdev->revision < 5) diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index 22321eb95b7d..703848fac189 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -2461,7 +2461,8 @@ static void ci_register_patching_mc_arb(struct radeon_device *rdev, if (patch && ((rdev->pdev->device == 0x67B0) || - (rdev->pdev->device == 0x67B1))) { + (rdev->pdev->device == 0x67B1)) && + (rdev->pdev->revision == 0)) { if ((memory_clock > 100000) && (memory_clock <= 125000)) { tmp2 = (((0x31 * engine_clock) / 125000) - 1) & 0xff; *dram_timimg2 &= ~0x00ff0000; @@ -3304,7 +3305,8 @@ static int ci_populate_all_memory_levels(struct radeon_device *rdev) pi->smc_state_table.MemoryLevel[0].EnabledForActivity = 1; if ((dpm_table->mclk_table.count >= 2) && - ((rdev->pdev->device == 0x67B0) || (rdev->pdev->device == 0x67B1))) { + ((rdev->pdev->device == 0x67B0) || (rdev->pdev->device == 0x67B1)) && + (rdev->pdev->revision == 0)) { pi->smc_state_table.MemoryLevel[1].MinVddc = pi->smc_state_table.MemoryLevel[0].MinVddc; pi->smc_state_table.MemoryLevel[1].MinVddcPhases = @@ -4493,7 +4495,8 @@ static int ci_register_patching_mc_seq(struct radeon_device *rdev, if (patch && ((rdev->pdev->device == 0x67B0) || - (rdev->pdev->device == 0x67B1))) { + (rdev->pdev->device == 0x67B1)) && + (rdev->pdev->revision == 0)) { for (i = 0; i < table->last; i++) { if (table->last >= SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE) return -EINVAL; diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 5c72aad3dae7..aac6733ddd82 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -686,7 +686,8 @@ static void radeon_crtc_init(struct drm_device *dev, int index) if (radeon_crtc == NULL) return; - radeon_crtc->flip_queue = alloc_workqueue("radeon-crtc", WQ_HIGHPRI, 0); + radeon_crtc->flip_queue = alloc_workqueue("radeon-crtc", + WQ_HIGHPRI | WQ_PERCPU, 0); if (!radeon_crtc->flip_queue) { kfree(radeon_crtc); return; diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 87fd6255c114..53d06053dec8 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -278,14 +278,13 @@ static bool radeon_support_enabled(struct device *dev, case CHIP_BONAIRE: case CHIP_HAWAII: - support_by_default = false; - fallthrough; case CHIP_KAVERI: case CHIP_KABINI: case CHIP_MULLINS: gen = "CIK"; module_param = radeon_cik_support; amdgpu_support_built &= IS_ENABLED(CONFIG_DRM_AMDGPU_CIK); + support_by_default = false; break; default: diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c index 7c36c30a75b6..1a246ebbfc61 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c @@ -513,7 +513,7 @@ static void rcar_du_cmm_setup(struct drm_crtc *crtc) struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_cmm_config cmm_config = {}; - if (!rcrtc->cmm->dev) + if (!rcrtc->cmm) return; if (drm_lut) @@ -667,7 +667,7 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) rcar_du_vsp_disable(rcrtc); - if (rcrtc->cmm->dev) + if (rcrtc->cmm) rcar_cmm_disable(rcrtc->cmm->dev); /* @@ -726,7 +726,7 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc, struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc->state); struct rcar_du_device *rcdu = rcrtc->dev; - if (rcrtc->cmm->dev) + if (rcrtc->cmm) rcar_cmm_enable(rcrtc->cmm->dev); rcar_du_crtc_get(rcrtc); diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index b7397827889c..360a88ca8f0c 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -741,6 +741,7 @@ static int sti_hda_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct sti_hda *hda; struct resource *res; + int ret; DRM_INFO("%s\n", __func__); @@ -779,7 +780,9 @@ static int sti_hda_probe(struct platform_device *pdev) return PTR_ERR(hda->clk_hddac); } - drm_bridge_add(&hda->bridge); + ret = devm_drm_bridge_add(dev, &hda->bridge); + if (ret) + return ret; platform_set_drvdata(pdev, hda); @@ -788,10 +791,7 @@ static int sti_hda_probe(struct platform_device *pdev) static void sti_hda_remove(struct platform_device *pdev) { - struct sti_hda *hda = platform_get_drvdata(pdev); - component_del(&pdev->dev, &sti_hda_ops); - drm_bridge_remove(&hda->bridge); } static const struct of_device_id hda_of_match[] = { diff --git a/drivers/gpu/drm/stm/lvds.c b/drivers/gpu/drm/stm/lvds.c index fe38c0984b2b..25e2ba98f36a 100644 --- a/drivers/gpu/drm/stm/lvds.c +++ b/drivers/gpu/drm/stm/lvds.c @@ -897,14 +897,14 @@ static int lvds_connector_atomic_check(struct drm_connector *connector, if (!conn_state) return -EINVAL; + if (!conn_state->crtc) + return 0; + if (list_empty(&connector->modes)) { drm_dbg(connector->dev, "connector: empty modes list\n"); return -EINVAL; } - if (!conn_state->crtc) - return -EINVAL; - panel_mode = list_first_entry(&connector->modes, struct drm_display_mode, head); diff --git a/drivers/gpu/drm/sysfb/ofdrm.c b/drivers/gpu/drm/sysfb/ofdrm.c index d38ba70f4e0d..247cf13c80a0 100644 --- a/drivers/gpu/drm/sysfb/ofdrm.c +++ b/drivers/gpu/drm/sysfb/ofdrm.c @@ -350,6 +350,7 @@ static void ofdrm_pci_release(void *data) struct pci_dev *pcidev = data; pci_disable_device(pcidev); + pci_dev_put(pcidev); } static int ofdrm_device_init_pci(struct ofdrm_device *odev) @@ -375,6 +376,7 @@ static int ofdrm_device_init_pci(struct ofdrm_device *odev) if (ret) { drm_err(dev, "pci_enable_device(%s) failed: %d\n", dev_name(&pcidev->dev), ret); + pci_dev_put(pcidev); return ret; } ret = devm_add_action_or_reset(&pdev->dev, ofdrm_pci_release, pcidev); diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c index 3bae91d7eefe..278bb23fe4c8 100644 --- a/drivers/gpu/drm/tiny/appletbdrm.c +++ b/drivers/gpu/drm/tiny/appletbdrm.c @@ -353,7 +353,7 @@ static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, frames_size + sizeof(struct appletbdrm_fb_request_footer), 16); - appletbdrm_state->request = kzalloc(request_size, GFP_KERNEL); + appletbdrm_state->request = kvzalloc(request_size, GFP_KERNEL); if (!appletbdrm_state->request) return -ENOMEM; @@ -543,7 +543,7 @@ static void appletbdrm_primary_plane_destroy_state(struct drm_plane *plane, { struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(state); - kfree(appletbdrm_state->request); + kvfree(appletbdrm_state->request); kfree(appletbdrm_state->response); __drm_gem_destroy_shadow_plane_state(&appletbdrm_state->base); diff --git a/drivers/gpu/drm/tiny/arcpgu.c b/drivers/gpu/drm/tiny/arcpgu.c index 505888497482..c93d61ac0bb7 100644 --- a/drivers/gpu/drm/tiny/arcpgu.c +++ b/drivers/gpu/drm/tiny/arcpgu.c @@ -250,7 +250,8 @@ DEFINE_DRM_GEM_DMA_FOPS(arcpgu_drm_ops); static int arcpgu_load(struct arcpgu_drm_private *arcpgu) { struct platform_device *pdev = to_platform_device(arcpgu->drm.dev); - struct device_node *encoder_node = NULL, *endpoint_node = NULL; + struct device_node *encoder_node __free(device_node) = NULL; + struct device_node *endpoint_node = NULL; struct drm_connector *connector = NULL; struct drm_device *drm = &arcpgu->drm; int ret; diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c index 222e4ae1abbd..5d8dc5efec77 100644 --- a/drivers/gpu/drm/tiny/bochs.c +++ b/drivers/gpu/drm/tiny/bochs.c @@ -761,25 +761,21 @@ static int bochs_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent ret = pcim_enable_device(pdev); if (ret) - goto err_free_dev; + return ret; pci_set_drvdata(pdev, dev); ret = bochs_load(bochs); if (ret) - goto err_free_dev; + return ret; ret = drm_dev_register(dev, 0); if (ret) - goto err_free_dev; + return ret; drm_client_setup(dev, NULL); return ret; - -err_free_dev: - drm_dev_put(dev); - return ret; } static void bochs_pci_remove(struct pci_dev *pdev) diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index d85f0a37ac35..bcd76f6bb7f0 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -739,7 +739,7 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo, may_evict = (force_space && place->mem_type != TTM_PL_SYSTEM); ret = ttm_resource_alloc(bo, place, res, force_space ? &limit_pool : NULL); if (ret) { - if (ret != -ENOSPC && ret != -EAGAIN) { + if (ret != -ENOSPC) { dmem_cgroup_pool_state_put(limit_pool); return ret; } @@ -1177,17 +1177,13 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo) bdev->funcs->swap_notify(bo); if (ttm_tt_is_populated(tt)) { - spin_lock(&bdev->lru_lock); - ttm_resource_del_bulk_move(bo->resource, bo); - spin_unlock(&bdev->lru_lock); - ret = ttm_tt_swapout(bdev, tt, swapout_walk->gfp_flags); - - spin_lock(&bdev->lru_lock); - if (ret) - ttm_resource_add_bulk_move(bo->resource, bo); - ttm_resource_move_to_lru_tail(bo->resource); - spin_unlock(&bdev->lru_lock); + if (!ret) { + spin_lock(&bdev->lru_lock); + ttm_resource_del_bulk_move_unevictable(bo->resource, bo); + ttm_resource_move_to_lru_tail(bo->resource); + spin_unlock(&bdev->lru_lock); + } } out: diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index f83b7d5ec6c6..3e3c201a0222 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -1112,19 +1112,14 @@ long ttm_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo, if (lret < 0) return lret; - if (bo->bulk_move) { - spin_lock(&bdev->lru_lock); - ttm_resource_del_bulk_move(bo->resource, bo); - spin_unlock(&bdev->lru_lock); - } - lret = ttm_tt_backup(bdev, bo->ttm, (struct ttm_backup_flags) {.purge = flags.purge, .writeback = flags.writeback}); - if (lret <= 0 && bo->bulk_move) { + if (lret > 0) { spin_lock(&bdev->lru_lock); - ttm_resource_add_bulk_move(bo->resource, bo); + ttm_resource_del_bulk_move_unevictable(bo->resource, bo); + ttm_resource_move_to_lru_tail(bo->resource); spin_unlock(&bdev->lru_lock); } diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index 26a3689e5fd9..278bbe7a11ad 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -206,6 +206,14 @@ error_free: return NULL; } +static void __free_pages_gpu_account(struct page *p, unsigned int order, + bool reclaim) +{ + mod_lruvec_page_state(p, reclaim ? NR_GPU_RECLAIM : NR_GPU_ACTIVE, + -(1 << order)); + __free_pages(p, order); +} + /* Reset the caching and pages of size 1 << order */ static void ttm_pool_free_page(struct ttm_pool *pool, enum ttm_caching caching, unsigned int order, struct page *p, bool reclaim) @@ -223,9 +231,7 @@ static void ttm_pool_free_page(struct ttm_pool *pool, enum ttm_caching caching, #endif if (!pool || !ttm_pool_uses_dma_alloc(pool)) { - mod_lruvec_page_state(p, reclaim ? NR_GPU_RECLAIM : NR_GPU_ACTIVE, - -(1 << order)); - __free_pages(p, order); + __free_pages_gpu_account(p, order, reclaim); return; } @@ -606,7 +612,7 @@ static int ttm_pool_restore_commit(struct ttm_pool_tt_restore *restore, */ ttm_pool_split_for_swap(restore->pool, p); copy_highpage(restore->alloced_page + i, p); - __free_pages(p, 0); + __free_pages_gpu_account(p, 0, false); } restore->restored_pages++; @@ -1068,7 +1074,7 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, if (flags->purge) { shrunken += num_pages; page->private = 0; - __free_pages(page, order); + __free_pages_gpu_account(page, order, false); memset(tt->pages + i, 0, num_pages * sizeof(*tt->pages)); } @@ -1109,7 +1115,7 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, } handle = shandle; tt->pages[i] = ttm_backup_handle_to_page_ptr(handle); - put_page(page); + __free_pages_gpu_account(page, 0, false); shrunken++; } diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index 9f36631d48b6..154d6739256f 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -292,6 +292,19 @@ void ttm_resource_del_bulk_move(struct ttm_resource *res, ttm_lru_bulk_move_del(bo->bulk_move, res); } +/* + * Remove a resource from its bulk_move, bypassing the unevictable check. + * Use only when the resource is known to still be tracked in the range despite + * the BO having just become unevictable; asserts that this is the case. + */ +void ttm_resource_del_bulk_move_unevictable(struct ttm_resource *res, + struct ttm_buffer_object *bo) +{ + WARN_ON_ONCE(!ttm_resource_unevictable(res, bo)); + if (bo->bulk_move) + ttm_lru_bulk_move_del(bo->bulk_move, res); +} + /* Move a resource to the LRU or bulk tail */ void ttm_resource_move_to_lru_tail(struct ttm_resource *res) { @@ -385,8 +398,11 @@ int ttm_resource_alloc(struct ttm_buffer_object *bo, if (man->cg) { ret = dmem_cgroup_try_charge(man->cg, bo->base.size, &pool, ret_limit_pool); - if (ret) + if (ret) { + if (ret == -EAGAIN) + ret = -ENOSPC; return ret; + } } ret = man->func->alloc(man, bo, place, res_ptr); diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 08a0e9480d70..17950fe3a0ec 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -285,13 +285,12 @@ static struct urb *udl_get_urb_locked(struct udl_device *udl, long timeout) return unode->urb; } -#define GET_URB_TIMEOUT HZ struct urb *udl_get_urb(struct udl_device *udl) { struct urb *urb; spin_lock_irq(&udl->urbs.lock); - urb = udl_get_urb_locked(udl, GET_URB_TIMEOUT); + urb = udl_get_urb_locked(udl, HZ * 2); spin_unlock_irq(&udl->urbs.lock); return urb; } diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index 231e829bd709..1ca073a4ecb2 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -342,8 +343,10 @@ static void udl_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atom return; urb = udl_get_urb(udl); - if (!urb) + if (!urb) { + drm_err_ratelimited(dev, "get urb failed when enabling crtc\n"); goto out; + } buf = (char *)urb->transfer_buffer; buf = udl_vidreg_lock(buf); diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index 8f061b6a05c6..ee4512db294b 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -399,6 +399,11 @@ v3d_get_multisync_submit_deps(struct drm_file *file_priv, if (multisync.pad) return -EINVAL; + if (!multisync.in_sync_count && !multisync.out_sync_count) { + drm_dbg(&v3d->drm, "Empty multisync extension\n"); + return -EINVAL; + } + ret = v3d_get_multisync_post_deps(file_priv, se, multisync.out_sync_count, multisync.out_syncs); if (ret) diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 49de1c22a469..03242e8b3d87 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -88,6 +88,7 @@ xe-y += xe_bb.o \ xe_irq.o \ xe_late_bind_fw.o \ xe_lrc.o \ + xe_mem_pool.o \ xe_migrate.o \ xe_mmio.o \ xe_mmio_gem.o \ diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index a0a4ddf3bb46..00dfa68af29a 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -21,6 +21,7 @@ #include "intel_audio.h" #include "intel_bw.h" #include "intel_display.h" +#include "intel_display_core.h" #include "intel_display_device.h" #include "intel_display_driver.h" #include "intel_display_irq.h" diff --git a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c index 29c72aa4b0d2..33494b86205d 100644 --- a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c +++ b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c @@ -37,9 +37,17 @@ static bool intel_hdcp_gsc_check_status(struct drm_device *drm) struct xe_device *xe = to_xe_device(drm); struct xe_tile *tile = xe_device_get_root_tile(xe); struct xe_gt *gt = tile->media_gt; - struct xe_gsc *gsc = >->uc.gsc; + struct xe_gsc *gsc; - if (!gsc || !xe_uc_fw_is_available(&gsc->fw)) { + if (!gt) { + drm_dbg_kms(&xe->drm, + "not checking GSC status for HDCP2.x: media GT not present or disabled\n"); + return false; + } + + gsc = >->uc.gsc; + + if (!xe_uc_fw_is_available(&gsc->fw)) { drm_dbg_kms(&xe->drm, "GSC Components not ready for HDCP2.x\n"); return false; diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h index 4ebaa0888a43..9c88ca3ce768 100644 --- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h +++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h @@ -583,7 +583,7 @@ #define DISABLE_128B_EVICTION_COMMAND_UDW REG_BIT(36 - 32) #define LSCFE_SAME_ADDRESS_ATOMICS_COALESCING_DISABLE REG_BIT(35 - 32) -#define ROW_CHICKEN5 XE_REG_MCR(0xe7f0) +#define ROW_CHICKEN5 XE_REG_MCR(0xe7f0, XE_REG_OPTION_MASKED) #define CPSS_AWARE_DIS REG_BIT(3) #define SARB_CHICKEN1 XE_REG_MCR(0xe90c) diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index a7c2dc7f224c..6b518858538f 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -897,10 +897,10 @@ void xe_bo_set_purgeable_state(struct xe_bo *bo, new_state == XE_MADV_PURGEABLE_PURGED); /* Once purged, always purged - cannot transition out */ - xe_assert(xe, !(bo->madv_purgeable == XE_MADV_PURGEABLE_PURGED && + xe_assert(xe, !(bo->purgeable.state == XE_MADV_PURGEABLE_PURGED && new_state != XE_MADV_PURGEABLE_PURGED)); - bo->madv_purgeable = new_state; + bo->purgeable.state = new_state; xe_bo_set_purgeable_shrinker(bo, new_state); } @@ -2322,8 +2322,10 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo, } /* XE_BO_FLAG_GGTTx requires XE_BO_FLAG_GGTT also be set */ - if ((flags & XE_BO_FLAG_GGTT_ALL) && !(flags & XE_BO_FLAG_GGTT)) + if ((flags & XE_BO_FLAG_GGTT_ALL) && !(flags & XE_BO_FLAG_GGTT)) { + xe_bo_free(bo); return ERR_PTR(-EINVAL); + } if (flags & (XE_BO_FLAG_VRAM_MASK | XE_BO_FLAG_STOLEN) && !(flags & XE_BO_FLAG_IGNORE_MIN_PAGE_SIZE) && @@ -2342,8 +2344,10 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo, alignment = SZ_4K >> PAGE_SHIFT; } - if (type == ttm_bo_type_device && aligned_size != size) + if (type == ttm_bo_type_device && aligned_size != size) { + xe_bo_free(bo); return ERR_PTR(-EINVAL); + } if (!bo) { bo = xe_bo_alloc(); @@ -2364,7 +2368,7 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo, INIT_LIST_HEAD(&bo->vram_userfault_link); /* Initialize purge advisory state */ - bo->madv_purgeable = XE_MADV_PURGEABLE_WILLNEED; + bo->purgeable.state = XE_MADV_PURGEABLE_WILLNEED; drm_gem_private_object_init(&xe->drm, &bo->ttm.base, size); diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h index 68dea7d25a6b..6340317f7d2e 100644 --- a/drivers/gpu/drm/xe/xe_bo.h +++ b/drivers/gpu/drm/xe/xe_bo.h @@ -251,7 +251,7 @@ static inline bool xe_bo_is_protected(const struct xe_bo *bo) static inline bool xe_bo_is_purged(struct xe_bo *bo) { xe_bo_assert_held(bo); - return bo->madv_purgeable == XE_MADV_PURGEABLE_PURGED; + return bo->purgeable.state == XE_MADV_PURGEABLE_PURGED; } /** @@ -268,11 +268,95 @@ static inline bool xe_bo_is_purged(struct xe_bo *bo) static inline bool xe_bo_madv_is_dontneed(struct xe_bo *bo) { xe_bo_assert_held(bo); - return bo->madv_purgeable == XE_MADV_PURGEABLE_DONTNEED; + return bo->purgeable.state == XE_MADV_PURGEABLE_DONTNEED; } void xe_bo_set_purgeable_state(struct xe_bo *bo, enum xe_madv_purgeable_state new_state); +/** + * xe_bo_willneed_get_locked() - Acquire a WILLNEED holder on a BO + * @bo: Buffer object + * + * Increments willneed_count and, on a 0->1 transition, promotes the BO + * from DONTNEED to WILLNEED. PURGED is terminal and is never modified. + * + * Caller must hold the BO's dma-resv lock. + */ +static inline void xe_bo_willneed_get_locked(struct xe_bo *bo) +{ + xe_bo_assert_held(bo); + + /* Imported BOs are owned externally; do not track purgeability. */ + if (drm_gem_is_imported(&bo->ttm.base)) + return; + + if (bo->purgeable.willneed_count++ == 0 && xe_bo_madv_is_dontneed(bo)) + xe_bo_set_purgeable_state(bo, XE_MADV_PURGEABLE_WILLNEED); +} + +/** + * xe_bo_willneed_put_locked() - Release a WILLNEED holder on a BO + * @bo: Buffer object + * + * Decrements willneed_count and, on a 1->0 transition, marks the BO + * DONTNEED only if it still has VMAs (implying all active VMAs are + * DONTNEED). If the last VMA is being removed, preserve the current BO + * state to match the previous VMA-walk semantics. + * + * PURGED is terminal and the BO state is never modified. + * + * Caller must hold the BO's dma-resv lock. + */ +static inline void xe_bo_willneed_put_locked(struct xe_bo *bo) +{ + xe_bo_assert_held(bo); + + if (drm_gem_is_imported(&bo->ttm.base)) + return; + + xe_assert(xe_bo_device(bo), bo->purgeable.willneed_count > 0); + if (--bo->purgeable.willneed_count == 0 && bo->purgeable.vma_count > 0 && + !xe_bo_is_purged(bo)) + xe_bo_set_purgeable_state(bo, XE_MADV_PURGEABLE_DONTNEED); +} + +/** + * xe_bo_vma_count_inc_locked() - Account a new VMA on a BO + * @bo: Buffer object + * + * Increments vma_count. + * + * Caller must hold the BO's dma-resv lock. + */ +static inline void xe_bo_vma_count_inc_locked(struct xe_bo *bo) +{ + xe_bo_assert_held(bo); + + if (drm_gem_is_imported(&bo->ttm.base)) + return; + + bo->purgeable.vma_count++; +} + +/** + * xe_bo_vma_count_dec_locked() - Account a VMA removal on a BO + * @bo: Buffer object + * + * Decrements vma_count. + * + * Caller must hold the BO's dma-resv lock. + */ +static inline void xe_bo_vma_count_dec_locked(struct xe_bo *bo) +{ + xe_bo_assert_held(bo); + + if (drm_gem_is_imported(&bo->ttm.base)) + return; + + xe_assert(xe_bo_device(bo), bo->purgeable.vma_count > 0); + bo->purgeable.vma_count--; +} + static inline void xe_bo_unpin_map_no_vm(struct xe_bo *bo) { if (likely(bo)) { diff --git a/drivers/gpu/drm/xe/xe_bo_types.h b/drivers/gpu/drm/xe/xe_bo_types.h index ff8317bfc1ae..077e35b4cdce 100644 --- a/drivers/gpu/drm/xe/xe_bo_types.h +++ b/drivers/gpu/drm/xe/xe_bo_types.h @@ -18,6 +18,7 @@ #include "xe_ggtt_types.h" struct xe_device; +struct xe_mem_pool_node; struct xe_vm; #define XE_BO_MAX_PLACEMENTS 3 @@ -88,7 +89,7 @@ struct xe_bo { bool ccs_cleared; /** @bb_ccs: BB instructions of CCS read/write. Valid only for VF */ - struct xe_bb *bb_ccs[XE_SRIOV_VF_CCS_CTX_COUNT]; + struct xe_mem_pool_node *bb_ccs[XE_SRIOV_VF_CCS_CTX_COUNT]; /** * @cpu_caching: CPU caching mode. Currently only used for userspace @@ -110,10 +111,32 @@ struct xe_bo { u64 min_align; /** - * @madv_purgeable: user space advise on BO purgeability, protected - * by BO's dma-resv lock. + * @purgeable: Purgeability state and accounting. + * + * All fields are protected by the BO's dma-resv lock. */ - u32 madv_purgeable; + struct { + /** + * @purgeable.state: BO purgeability state + * (WILLNEED/DONTNEED/PURGED). + */ + u32 state; + + /** + * @purgeable.vma_count: Number of VMAs currently mapping this BO. + */ + u32 vma_count; + + /** + * @purgeable.willneed_count: Number of active WILLNEED holders. + * + * Counts WILLNEED VMAs plus active dma-buf exports for + * non-imported BOs. The BO flips to DONTNEED on a 1->0 + * transition only when VMAs still exist; if the last VMA is + * removed, the previous BO state is preserved. + */ + u32 willneed_count; + } purgeable; }; #endif diff --git a/drivers/gpu/drm/xe/xe_debugfs.c b/drivers/gpu/drm/xe/xe_debugfs.c index 844cfafe1ec7..ad2d8f179eb6 100644 --- a/drivers/gpu/drm/xe/xe_debugfs.c +++ b/drivers/gpu/drm/xe/xe_debugfs.c @@ -45,7 +45,7 @@ static void read_residency_counter(struct xe_device *xe, struct xe_mmio *mmio, u64 residency = 0; int ret; - ret = xe_pmt_telem_read(to_pci_dev(xe->drm.dev), + ret = xe_pmt_telem_read(xe->drm.dev, xe_mmio_read32(mmio, PUNIT_TELEMETRY_GUID), &residency, offset, sizeof(residency)); if (ret != sizeof(residency)) { diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c index 7f9602b3363d..8a920e58245c 100644 --- a/drivers/gpu/drm/xe/xe_dma_buf.c +++ b/drivers/gpu/drm/xe/xe_dma_buf.c @@ -193,6 +193,18 @@ static int xe_dma_buf_begin_cpu_access(struct dma_buf *dma_buf, return 0; } +static void xe_dma_buf_release(struct dma_buf *dmabuf) +{ + struct drm_gem_object *obj = dmabuf->priv; + struct xe_bo *bo = gem_to_xe_bo(obj); + + xe_bo_lock(bo, false); + xe_bo_willneed_put_locked(bo); + xe_bo_unlock(bo); + + drm_gem_dmabuf_release(dmabuf); +} + static const struct dma_buf_ops xe_dmabuf_ops = { .attach = xe_dma_buf_attach, .detach = xe_dma_buf_detach, @@ -200,7 +212,7 @@ static const struct dma_buf_ops xe_dmabuf_ops = { .unpin = xe_dma_buf_unpin, .map_dma_buf = xe_dma_buf_map, .unmap_dma_buf = xe_dma_buf_unmap, - .release = drm_gem_dmabuf_release, + .release = xe_dma_buf_release, .begin_cpu_access = xe_dma_buf_begin_cpu_access, .mmap = drm_gem_dmabuf_mmap, .vmap = drm_gem_dmabuf_vmap, @@ -241,26 +253,33 @@ struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags) ret = -EINVAL; goto out_unlock; } + + xe_bo_willneed_get_locked(bo); xe_bo_unlock(bo); ret = ttm_bo_setup_export(&bo->ttm, &ctx); if (ret) - return ERR_PTR(ret); + goto out_put; buf = drm_gem_prime_export(obj, flags); - if (!IS_ERR(buf)) - buf->ops = &xe_dmabuf_ops; + if (IS_ERR(buf)) { + ret = PTR_ERR(buf); + goto out_put; + } + buf->ops = &xe_dmabuf_ops; return buf; +out_put: + xe_bo_lock(bo, false); + xe_bo_willneed_put_locked(bo); out_unlock: xe_bo_unlock(bo); return ERR_PTR(ret); } static struct drm_gem_object * -xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, - struct dma_buf *dma_buf) +xe_dma_buf_create_obj(struct drm_device *dev, struct dma_buf *dma_buf) { struct dma_resv *resv = dma_buf->resv; struct xe_device *xe = to_xe_device(dev); @@ -281,7 +300,7 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, if (ret) break; - bo = xe_bo_init_locked(xe, storage, NULL, resv, NULL, dma_buf->size, + bo = xe_bo_init_locked(xe, NULL, NULL, resv, NULL, dma_buf->size, 0, /* Will require 1way or 2way for vm_bind */ ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec); drm_exec_retry_on_contention(&exec); @@ -332,7 +351,6 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev, const struct dma_buf_attach_ops *attach_ops; struct dma_buf_attachment *attach; struct drm_gem_object *obj; - struct xe_bo *bo; if (dma_buf->ops == &xe_dmabuf_ops) { obj = dma_buf->priv; @@ -348,13 +366,15 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev, } /* - * Don't publish the bo until we have a valid attachment, and a - * valid attachment needs the bo address. So pre-create a bo before - * creating the attachment and publish. + * This needs to happen before the attach, since it will create a new + * attachment for this, and add it to the list of attachments, at which + * point it is globally visible, and at any point the export side can + * call into on invalidate_mappings callback, which require a working + * object. */ - bo = xe_bo_alloc(); - if (IS_ERR(bo)) - return ERR_CAST(bo); + obj = xe_dma_buf_create_obj(dev, dma_buf); + if (IS_ERR(obj)) + return obj; attach_ops = &xe_dma_buf_attach_ops; #if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST) @@ -362,26 +382,15 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev, attach_ops = test->attach_ops; #endif - attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, &bo->ttm.base); + attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, obj); if (IS_ERR(attach)) { - obj = ERR_CAST(attach); - goto out_err; + xe_bo_put(gem_to_xe_bo(obj)); + return ERR_CAST(attach); } - /* Errors here will take care of freeing the bo. */ - obj = xe_dma_buf_init_obj(dev, bo, dma_buf); - if (IS_ERR(obj)) - return obj; - - get_dma_buf(dma_buf); obj->import_attach = attach; return obj; - -out_err: - xe_bo_free(bo); - - return obj; } #if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST) diff --git a/drivers/gpu/drm/xe/xe_eu_stall.c b/drivers/gpu/drm/xe/xe_eu_stall.c index c34408cfd292..dddcdd0bb7a3 100644 --- a/drivers/gpu/drm/xe/xe_eu_stall.c +++ b/drivers/gpu/drm/xe/xe_eu_stall.c @@ -869,14 +869,14 @@ static int xe_eu_stall_stream_close(struct inode *inode, struct file *file) struct xe_eu_stall_data_stream *stream = file->private_data; struct xe_gt *gt = stream->gt; - drm_dev_put(>->tile->xe->drm); - mutex_lock(>->eu_stall->stream_lock); xe_eu_stall_disable_locked(stream); xe_eu_stall_data_buf_destroy(stream); xe_eu_stall_stream_free(stream); mutex_unlock(>->eu_stall->stream_lock); + drm_dev_put(>->tile->xe->drm); + return 0; } diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c index b287d0e0e60a..071b8c41df43 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.c +++ b/drivers/gpu/drm/xe/xe_exec_queue.c @@ -1405,7 +1405,7 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, if (q->vm && q->hwe->hw_engine_group) { err = xe_hw_engine_group_add_exec_queue(q->hwe->hw_engine_group, q); if (err) - goto put_exec_queue; + goto kill_exec_queue; } } @@ -1416,12 +1416,15 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data, /* user id alloc must always be last in ioctl to prevent UAF */ err = xa_alloc(&xef->exec_queue.xa, &id, q, xa_limit_32b, GFP_KERNEL); if (err) - goto kill_exec_queue; + goto del_hw_engine_group; args->exec_queue_id = id; return 0; +del_hw_engine_group: + if (q->vm && q->hwe && q->hwe->hw_engine_group) + xe_hw_engine_group_del_exec_queue(q->hwe->hw_engine_group, q); kill_exec_queue: xe_exec_queue_kill(q); delete_queue_group: @@ -1760,7 +1763,7 @@ void xe_exec_queue_tlb_inval_last_fence_put(struct xe_exec_queue *q, void xe_exec_queue_tlb_inval_last_fence_put_unlocked(struct xe_exec_queue *q, unsigned int type) { - xe_assert(q->vm->xe, type == XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT || + xe_assert(gt_to_xe(q->gt), type == XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT || type == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT); dma_fence_put(q->tlb_inval[type].last_fence); diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c index e5c234f3d795..0d13e357fb43 100644 --- a/drivers/gpu/drm/xe/xe_gsc.c +++ b/drivers/gpu/drm/xe/xe_gsc.c @@ -166,7 +166,7 @@ static int query_compatibility_version(struct xe_gsc *gsc) &rd_offset); if (err) { xe_gt_err(gt, "HuC: invalid GSC reply for version query (err=%d)\n", err); - return err; + goto out_bo; } compat->major = version_query_rd(xe, &bo->vmap, rd_offset, proj_major); diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c index 87a164efcc33..01fe03b9efe8 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c @@ -385,10 +385,10 @@ static int pf_migration_mmio_save(struct xe_gt *gt, unsigned int vfid, void *buf if (xe_gt_is_media_type(gt)) for (n = 0; n < MED_VF_SW_FLAG_COUNT; n++) - regs[n] = xe_mmio_read32(>->mmio, MED_VF_SW_FLAG(n)); + regs[n] = xe_mmio_read32(&mmio, MED_VF_SW_FLAG(n)); else for (n = 0; n < VF_SW_FLAG_COUNT; n++) - regs[n] = xe_mmio_read32(>->mmio, VF_SW_FLAG(n)); + regs[n] = xe_mmio_read32(&mmio, VF_SW_FLAG(n)); return 0; } @@ -407,10 +407,10 @@ static int pf_migration_mmio_restore(struct xe_gt *gt, unsigned int vfid, if (xe_gt_is_media_type(gt)) for (n = 0; n < MED_VF_SW_FLAG_COUNT; n++) - xe_mmio_write32(>->mmio, MED_VF_SW_FLAG(n), regs[n]); + xe_mmio_write32(&mmio, MED_VF_SW_FLAG(n), regs[n]); else for (n = 0; n < VF_SW_FLAG_COUNT; n++) - xe_mmio_write32(>->mmio, VF_SW_FLAG(n), regs[n]); + xe_mmio_write32(&mmio, VF_SW_FLAG(n), regs[n]); return 0; } diff --git a/drivers/gpu/drm/xe/xe_gt_types.h b/drivers/gpu/drm/xe/xe_gt_types.h index 8b55cf25a75f..fffb5d631b69 100644 --- a/drivers/gpu/drm/xe/xe_gt_types.h +++ b/drivers/gpu/drm/xe/xe_gt_types.h @@ -144,6 +144,13 @@ struct xe_gt { u8 id; /** @info.has_indirect_ring_state: GT has indirect ring state support */ u8 has_indirect_ring_state:1; + /** + * @info.has_xe2_blt_instructions: GT supports Xe2-style MEM_SET + * and MEM_COPY blitter functionality. Note that despite the + * name, some Xe1 platforms may also support this "Xe2-style" + * feature. + */ + u8 has_xe2_blt_instructions:1; /** * @info.num_geometry_xecore_fuse_regs: Number of 32b-bit fuse * registers the geometry XeCore mask spans. diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index 81b5f01b1f65..2b835d48b565 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -512,12 +512,9 @@ static void guc_golden_lrc_init(struct xe_guc_ads *ads) * that starts after the execlists LRC registers. This is * required to allow the GuC to restore just the engine state * when a watchdog reset occurs. - * We calculate the engine state size by removing the size of - * what comes before it in the context image (which is identical - * on all engines). */ ads_blob_write(ads, ads.eng_state_size[guc_class], - real_size - xe_lrc_skip_size(xe)); + xe_lrc_engine_state_size(gt, class)); ads_blob_write(ads, ads.golden_context_lrca[guc_class], addr_ggtt); diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index a145234f662b..10556156eaad 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -259,24 +259,12 @@ static void guc_submit_sw_fini(struct drm_device *drm, void *arg) } static void guc_submit_fini(void *arg) -{ - struct xe_guc *guc = arg; - - /* Forcefully kill any remaining exec queues */ - xe_guc_ct_stop(&guc->ct); - guc_submit_reset_prepare(guc); - xe_guc_softreset(guc); - xe_guc_submit_stop(guc); - xe_uc_fw_sanitize(&guc->fw); - xe_guc_submit_pause_abort(guc); -} - -static void guc_submit_wedged_fini(void *arg) { struct xe_guc *guc = arg; struct xe_exec_queue *q; unsigned long index; + /* Drop any wedged queue refs */ mutex_lock(&guc->submission_state.lock); xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) { if (exec_queue_wedged(q)) { @@ -286,6 +274,14 @@ static void guc_submit_wedged_fini(void *arg) } } mutex_unlock(&guc->submission_state.lock); + + /* Forcefully kill any remaining exec queues */ + xe_guc_ct_stop(&guc->ct); + guc_submit_reset_prepare(guc); + xe_guc_softreset(guc); + xe_guc_submit_stop(guc); + xe_uc_fw_sanitize(&guc->fw); + xe_guc_submit_pause_abort(guc); } static const struct xe_exec_queue_ops guc_exec_queue_ops; @@ -1320,10 +1316,8 @@ static void disable_scheduling_deregister(struct xe_guc *guc, void xe_guc_submit_wedge(struct xe_guc *guc) { struct xe_device *xe = guc_to_xe(guc); - struct xe_gt *gt = guc_to_gt(guc); struct xe_exec_queue *q; unsigned long index; - int err; xe_gt_assert(guc_to_gt(guc), guc_to_xe(guc)->wedged.mode); @@ -1335,15 +1329,6 @@ void xe_guc_submit_wedge(struct xe_guc *guc) return; if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET) { - err = devm_add_action_or_reset(guc_to_xe(guc)->drm.dev, - guc_submit_wedged_fini, guc); - if (err) { - xe_gt_err(gt, "Failed to register clean-up on wedged.mode=%s; " - "Although device is wedged.\n", - xe_wedged_mode_to_string(XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)); - return; - } - mutex_lock(&guc->submission_state.lock); xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) if (xe_exec_queue_get_unless_zero(q)) diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c index 0fd4d4f1014a..92e423a339f1 100644 --- a/drivers/gpu/drm/xe/xe_hwmon.c +++ b/drivers/gpu/drm/xe/xe_hwmon.c @@ -506,7 +506,7 @@ xe_hwmon_energy_get(struct xe_hwmon *hwmon, int channel, long *energy) if (hwmon->xe->info.platform == XE_BATTLEMAGE) { u64 pmt_val; - ret = xe_pmt_telem_read(to_pci_dev(hwmon->xe->drm.dev), + ret = xe_pmt_telem_read(hwmon->xe->drm.dev, xe_mmio_read32(mmio, PUNIT_TELEMETRY_GUID), &pmt_val, BMG_ENERGY_STATUS_PMT_OFFSET, sizeof(pmt_val)); if (ret != sizeof(pmt_val)) { diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index 9d12a0d2f0b5..4af9f0d7c6f3 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -746,9 +746,16 @@ size_t xe_lrc_reg_size(struct xe_device *xe) return 80 * sizeof(u32); } -size_t xe_lrc_skip_size(struct xe_device *xe) +/** + * xe_lrc_engine_state_size() - Get size of the engine state within LRC + * @gt: the &xe_gt struct instance + * @class: Hardware engine class + * + * Returns: Size of the engine state + */ +size_t xe_lrc_engine_state_size(struct xe_gt *gt, enum xe_engine_class class) { - return LRC_PPHWSP_SIZE + xe_lrc_reg_size(xe); + return xe_gt_lrc_hang_replay_size(gt, class) - xe_lrc_reg_size(gt_to_xe(gt)); } static inline u32 __xe_lrc_seqno_offset(struct xe_lrc *lrc) @@ -1214,7 +1221,7 @@ static ssize_t setup_invalidate_state_cache_wa(struct xe_lrc *lrc, if (xe_gt_WARN_ON(lrc->gt, max_len < 3)) return -ENOSPC; - *cmd++ = MI_LOAD_REGISTER_IMM | MI_LRI_NUM_REGS(1); + *cmd++ = MI_LOAD_REGISTER_IMM | MI_LRI_LRM_CS_MMIO | MI_LRI_NUM_REGS(1); *cmd++ = CS_DEBUG_MODE2(0).addr; *cmd++ = REG_MASKED_FIELD_ENABLE(INSTRUCTION_STATE_CACHE_INVALIDATE); diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h index e7c975f9e2d9..5440663183f6 100644 --- a/drivers/gpu/drm/xe/xe_lrc.h +++ b/drivers/gpu/drm/xe/xe_lrc.h @@ -130,7 +130,7 @@ u32 xe_lrc_parallel_ggtt_addr(struct xe_lrc *lrc); struct iosys_map xe_lrc_parallel_map(struct xe_lrc *lrc); size_t xe_lrc_reg_size(struct xe_device *xe); -size_t xe_lrc_skip_size(struct xe_device *xe); +size_t xe_lrc_engine_state_size(struct xe_gt *gt, enum xe_engine_class class); void xe_lrc_dump_default(struct drm_printer *p, struct xe_gt *gt, diff --git a/drivers/gpu/drm/xe/xe_mem_pool.c b/drivers/gpu/drm/xe/xe_mem_pool.c new file mode 100644 index 000000000000..d5e24d6aa88d --- /dev/null +++ b/drivers/gpu/drm/xe/xe_mem_pool.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include + +#include + +#include "instructions/xe_mi_commands.h" +#include "xe_bo.h" +#include "xe_device_types.h" +#include "xe_map.h" +#include "xe_mem_pool.h" +#include "xe_mem_pool_types.h" +#include "xe_tile_printk.h" + +/** + * struct xe_mem_pool - DRM MM pool for sub-allocating memory from a BO on an + * XE tile. + * + * The XE memory pool is a DRM MM manager that provides sub-allocation of memory + * from a backing buffer object (BO) on a specific XE tile. It is designed to + * manage memory for GPU workloads, allowing for efficient allocation and + * deallocation of memory regions within the BO. + * + * The memory pool maintains a primary BO that is pinned in the GGTT and mapped + * into the CPU address space for direct access. Optionally, it can also maintain + * a shadow BO that can be used for atomic updates to the primary BO's contents. + * + * The API provided by the memory pool allows clients to allocate and free memory + * regions, retrieve GPU and CPU addresses, and synchronize data between the + * primary and shadow BOs as needed. + */ +struct xe_mem_pool { + /** @base: Range allocator over [0, @size) in bytes */ + struct drm_mm base; + /** @bo: Active pool BO (GGTT-pinned, CPU-mapped). */ + struct xe_bo *bo; + /** @shadow: Shadow BO for atomic command updates. */ + struct xe_bo *shadow; + /** @swap_guard: Timeline guard updating @bo and @shadow */ + struct mutex swap_guard; + /** @cpu_addr: CPU virtual address of the active BO. */ + void *cpu_addr; + /** @is_iomem: Indicates if the BO mapping is I/O memory. */ + bool is_iomem; +}; + +static struct xe_mem_pool *node_to_pool(struct xe_mem_pool_node *node) +{ + return container_of(node->sa_node.mm, struct xe_mem_pool, base); +} + +static struct xe_tile *pool_to_tile(struct xe_mem_pool *pool) +{ + return pool->bo->tile; +} + +static void fini_pool_action(struct drm_device *drm, void *arg) +{ + struct xe_mem_pool *pool = arg; + + if (pool->is_iomem) + kvfree(pool->cpu_addr); + + drm_mm_takedown(&pool->base); +} + +static int pool_shadow_init(struct xe_mem_pool *pool) +{ + struct xe_tile *tile = pool->bo->tile; + struct xe_device *xe = tile_to_xe(tile); + struct xe_bo *shadow; + int ret; + + xe_assert(xe, !pool->shadow); + + ret = drmm_mutex_init(&xe->drm, &pool->swap_guard); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_PROVE_LOCKING)) { + fs_reclaim_acquire(GFP_KERNEL); + might_lock(&pool->swap_guard); + fs_reclaim_release(GFP_KERNEL); + } + shadow = xe_managed_bo_create_pin_map(xe, tile, + xe_bo_size(pool->bo), + XE_BO_FLAG_VRAM_IF_DGFX(tile) | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_GGTT_INVALIDATE | + XE_BO_FLAG_PINNED_NORESTORE); + if (IS_ERR(shadow)) + return PTR_ERR(shadow); + + pool->shadow = shadow; + + return 0; +} + +/** + * xe_mem_pool_init() - Initialize memory pool. + * @tile: the &xe_tile where allocate. + * @size: number of bytes to allocate. + * @guard: the size of the guard region at the end of the BO that is not + * sub-allocated, in bytes. + * @flags: flags to use to create shadow pool. + * + * Initializes a memory pool for sub-allocating memory from a backing BO on the + * specified XE tile. The backing BO is pinned in the GGTT and mapped into + * the CPU address space for direct access. Optionally, a shadow BO can also be + * initialized for atomic updates to the primary BO's contents. + * + * Returns: a pointer to the &xe_mem_pool, or an error pointer on failure. + */ +struct xe_mem_pool *xe_mem_pool_init(struct xe_tile *tile, u32 size, + u32 guard, int flags) +{ + struct xe_device *xe = tile_to_xe(tile); + struct xe_mem_pool *pool; + struct xe_bo *bo; + u32 managed_size; + int ret; + + xe_tile_assert(tile, size > guard); + managed_size = size - guard; + + pool = drmm_kzalloc(&xe->drm, sizeof(*pool), GFP_KERNEL); + if (!pool) + return ERR_PTR(-ENOMEM); + + bo = xe_managed_bo_create_pin_map(xe, tile, size, + XE_BO_FLAG_VRAM_IF_DGFX(tile) | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_GGTT_INVALIDATE | + XE_BO_FLAG_PINNED_NORESTORE); + if (IS_ERR(bo)) { + xe_tile_err(tile, "Failed to prepare %uKiB BO for mem pool (%pe)\n", + size / SZ_1K, bo); + return ERR_CAST(bo); + } + pool->bo = bo; + pool->is_iomem = bo->vmap.is_iomem; + + if (pool->is_iomem) { + pool->cpu_addr = kvzalloc(size, GFP_KERNEL); + if (!pool->cpu_addr) + return ERR_PTR(-ENOMEM); + } else { + pool->cpu_addr = bo->vmap.vaddr; + } + + if (flags & XE_MEM_POOL_BO_FLAG_INIT_SHADOW_COPY) { + ret = pool_shadow_init(pool); + + if (ret) + goto out_err; + } + + drm_mm_init(&pool->base, 0, managed_size); + ret = drmm_add_action_or_reset(&xe->drm, fini_pool_action, pool); + if (ret) + return ERR_PTR(ret); + + return pool; + +out_err: + if (flags & XE_MEM_POOL_BO_FLAG_INIT_SHADOW_COPY) + xe_tile_err(tile, + "Failed to initialize shadow BO for mem pool (%d)\n", ret); + if (bo->vmap.is_iomem) + kvfree(pool->cpu_addr); + return ERR_PTR(ret); +} + +/** + * xe_mem_pool_sync() - Copy the entire contents of the main pool to shadow pool. + * @pool: the memory pool containing the primary and shadow BOs. + * + * Copies the entire contents of the primary pool to the shadow pool. This must + * be done after xe_mem_pool_init() with the XE_MEM_POOL_BO_FLAG_INIT_SHADOW_COPY + * flag to ensure that the shadow pool has the same initial contents as the primary + * pool. After this initial synchronization, clients can choose to synchronize the + * shadow pool with the primary pool on a node basis using + * xe_mem_pool_sync_shadow_locked() as needed. + * + * Return: None. + */ +void xe_mem_pool_sync(struct xe_mem_pool *pool) +{ + struct xe_tile *tile = pool_to_tile(pool); + struct xe_device *xe = tile_to_xe(tile); + + xe_tile_assert(tile, pool->shadow); + + xe_map_memcpy_to(xe, &pool->shadow->vmap, 0, + pool->cpu_addr, xe_bo_size(pool->bo)); +} + +/** + * xe_mem_pool_swap_shadow_locked() - Swap the primary BO with the shadow BO. + * @pool: the memory pool containing the primary and shadow BOs. + * + * Swaps the primary buffer object with the shadow buffer object in the mem + * pool. This allows for atomic updates to the contents of the primary BO + * by first writing to the shadow BO and then swapping it with the primary BO. + * Swap_guard must be held to ensure synchronization with any concurrent swap + * operations. + * + * Return: None. + */ +void xe_mem_pool_swap_shadow_locked(struct xe_mem_pool *pool) +{ + struct xe_tile *tile = pool_to_tile(pool); + + xe_tile_assert(tile, pool->shadow); + lockdep_assert_held(&pool->swap_guard); + + swap(pool->bo, pool->shadow); + if (!pool->bo->vmap.is_iomem) + pool->cpu_addr = pool->bo->vmap.vaddr; +} + +/** + * xe_mem_pool_sync_shadow_locked() - Copy node from primary pool to shadow pool. + * @node: the node allocated in the memory pool. + * + * Copies the specified batch buffer from the primary pool to the shadow pool. + * Swap_guard must be held to ensure synchronization with any concurrent swap + * operations. + * + * Return: None. + */ +void xe_mem_pool_sync_shadow_locked(struct xe_mem_pool_node *node) +{ + struct xe_mem_pool *pool = node_to_pool(node); + struct xe_tile *tile = pool_to_tile(pool); + struct xe_device *xe = tile_to_xe(tile); + struct drm_mm_node *sa_node = &node->sa_node; + + xe_tile_assert(tile, pool->shadow); + lockdep_assert_held(&pool->swap_guard); + + xe_map_memcpy_to(xe, &pool->shadow->vmap, + sa_node->start, + pool->cpu_addr + sa_node->start, + sa_node->size); +} + +/** + * xe_mem_pool_gpu_addr() - Retrieve GPU address of memory pool. + * @pool: the memory pool + * + * Returns: GGTT address of the memory pool. + */ +u64 xe_mem_pool_gpu_addr(struct xe_mem_pool *pool) +{ + return xe_bo_ggtt_addr(pool->bo); +} + +/** + * xe_mem_pool_cpu_addr() - Retrieve CPU address of manager pool. + * @pool: the memory pool + * + * Returns: CPU virtual address of memory pool. + */ +void *xe_mem_pool_cpu_addr(struct xe_mem_pool *pool) +{ + return pool->cpu_addr; +} + +/** + * xe_mem_pool_bo_swap_guard() - Retrieve the mutex used to guard swap + * operations on a memory pool. + * @pool: the memory pool + * + * Returns: Swap guard mutex or NULL if shadow pool is not created. + */ +struct mutex *xe_mem_pool_bo_swap_guard(struct xe_mem_pool *pool) +{ + if (!pool->shadow) + return NULL; + + return &pool->swap_guard; +} + +/** + * xe_mem_pool_bo_flush_write() - Copy the data from the sub-allocation + * to the GPU memory. + * @node: the node allocated in the memory pool to flush. + */ +void xe_mem_pool_bo_flush_write(struct xe_mem_pool_node *node) +{ + struct xe_mem_pool *pool = node_to_pool(node); + struct xe_tile *tile = pool_to_tile(pool); + struct xe_device *xe = tile_to_xe(tile); + struct drm_mm_node *sa_node = &node->sa_node; + + if (!pool->bo->vmap.is_iomem) + return; + + xe_map_memcpy_to(xe, &pool->bo->vmap, sa_node->start, + pool->cpu_addr + sa_node->start, + sa_node->size); +} + +/** + * xe_mem_pool_bo_sync_read() - Copy the data from GPU memory to the + * sub-allocation. + * @node: the node allocated in the memory pool to read back. + */ +void xe_mem_pool_bo_sync_read(struct xe_mem_pool_node *node) +{ + struct xe_mem_pool *pool = node_to_pool(node); + struct xe_tile *tile = pool_to_tile(pool); + struct xe_device *xe = tile_to_xe(tile); + struct drm_mm_node *sa_node = &node->sa_node; + + if (!pool->bo->vmap.is_iomem) + return; + + xe_map_memcpy_from(xe, pool->cpu_addr + sa_node->start, + &pool->bo->vmap, sa_node->start, sa_node->size); +} + +/** + * xe_mem_pool_alloc_node() - Allocate a new node for use with xe_mem_pool. + * + * Returns: node structure or an ERR_PTR(-ENOMEM). + */ +struct xe_mem_pool_node *xe_mem_pool_alloc_node(void) +{ + struct xe_mem_pool_node *node = kzalloc_obj(*node); + + if (!node) + return ERR_PTR(-ENOMEM); + + return node; +} + +/** + * xe_mem_pool_insert_node() - Insert a node into the memory pool. + * @pool: the memory pool to insert into + * @node: the node to insert + * @size: the size of the node to be allocated in bytes. + * + * Inserts a node into the specified memory pool using drm_mm for + * allocation. + * + * Returns: 0 on success or a negative error code on failure. + */ +int xe_mem_pool_insert_node(struct xe_mem_pool *pool, + struct xe_mem_pool_node *node, u32 size) +{ + if (!pool) + return -EINVAL; + + return drm_mm_insert_node(&pool->base, &node->sa_node, size); +} + +/** + * xe_mem_pool_free_node() - Free a node allocated from the memory pool. + * @node: the node to free + * + * Returns: None. + */ +void xe_mem_pool_free_node(struct xe_mem_pool_node *node) +{ + if (!node) + return; + + drm_mm_remove_node(&node->sa_node); + kfree(node); +} + +/** + * xe_mem_pool_node_cpu_addr() - Retrieve CPU address of the node. + * @node: the node allocated in the memory pool + * + * Returns: CPU virtual address of the node. + */ +void *xe_mem_pool_node_cpu_addr(struct xe_mem_pool_node *node) +{ + struct xe_mem_pool *pool = node_to_pool(node); + + return xe_mem_pool_cpu_addr(pool) + node->sa_node.start; +} + +/** + * xe_mem_pool_dump() - Dump the state of the DRM MM manager for debugging. + * @pool: the memory pool info be dumped. + * @p: The DRM printer to use for output. + * + * Only the drm managed region is dumped, not the state of the BOs or any other + * pool information. + * + * Returns: None. + */ +void xe_mem_pool_dump(struct xe_mem_pool *pool, struct drm_printer *p) +{ + drm_mm_print(&pool->base, p); +} diff --git a/drivers/gpu/drm/xe/xe_mem_pool.h b/drivers/gpu/drm/xe/xe_mem_pool.h new file mode 100644 index 000000000000..89cd2555fe91 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_mem_pool.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2026 Intel Corporation + */ +#ifndef _XE_MEM_POOL_H_ +#define _XE_MEM_POOL_H_ + +#include +#include + +#include +#include "xe_mem_pool_types.h" + +struct drm_printer; +struct xe_mem_pool; +struct xe_tile; + +struct xe_mem_pool *xe_mem_pool_init(struct xe_tile *tile, u32 size, + u32 guard, int flags); +void xe_mem_pool_sync(struct xe_mem_pool *pool); +void xe_mem_pool_swap_shadow_locked(struct xe_mem_pool *pool); +void xe_mem_pool_sync_shadow_locked(struct xe_mem_pool_node *node); +u64 xe_mem_pool_gpu_addr(struct xe_mem_pool *pool); +void *xe_mem_pool_cpu_addr(struct xe_mem_pool *pool); +struct mutex *xe_mem_pool_bo_swap_guard(struct xe_mem_pool *pool); +void xe_mem_pool_bo_flush_write(struct xe_mem_pool_node *node); +void xe_mem_pool_bo_sync_read(struct xe_mem_pool_node *node); +struct xe_mem_pool_node *xe_mem_pool_alloc_node(void); +int xe_mem_pool_insert_node(struct xe_mem_pool *pool, + struct xe_mem_pool_node *node, u32 size); +void xe_mem_pool_free_node(struct xe_mem_pool_node *node); +void *xe_mem_pool_node_cpu_addr(struct xe_mem_pool_node *node); +void xe_mem_pool_dump(struct xe_mem_pool *pool, struct drm_printer *p); + +#endif diff --git a/drivers/gpu/drm/xe/xe_mem_pool_types.h b/drivers/gpu/drm/xe/xe_mem_pool_types.h new file mode 100644 index 000000000000..d5e926c93351 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_mem_pool_types.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2026 Intel Corporation + */ + +#ifndef _XE_MEM_POOL_TYPES_H_ +#define _XE_MEM_POOL_TYPES_H_ + +#include + +#define XE_MEM_POOL_BO_FLAG_INIT_SHADOW_COPY BIT(0) + +/** + * struct xe_mem_pool_node - Sub-range allocations from mem pool. + */ +struct xe_mem_pool_node { + /** @sa_node: drm_mm_node for this allocation. */ + struct drm_mm_node sa_node; +}; + +#endif diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index fc918b4fba54..a22413f892a0 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -29,6 +29,7 @@ #include "xe_hw_engine.h" #include "xe_lrc.h" #include "xe_map.h" +#include "xe_mem_pool.h" #include "xe_mocs.h" #include "xe_printk.h" #include "xe_pt.h" @@ -1166,11 +1167,12 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, u32 batch_size, batch_size_allocated; struct xe_device *xe = gt_to_xe(gt); struct xe_res_cursor src_it, ccs_it; + struct xe_mem_pool *bb_pool; struct xe_sriov_vf_ccs_ctx *ctx; - struct xe_sa_manager *bb_pool; u64 size = xe_bo_size(src_bo); - struct xe_bb *bb = NULL; + struct xe_mem_pool_node *bb; u64 src_L0, src_L0_ofs; + struct xe_bb xe_bb_tmp; u32 src_L0_pt; int err; @@ -1208,18 +1210,18 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, size -= src_L0; } - bb = xe_bb_alloc(gt); + bb = xe_mem_pool_alloc_node(); if (IS_ERR(bb)) return PTR_ERR(bb); bb_pool = ctx->mem.ccs_bb_pool; - scoped_guard(mutex, xe_sa_bo_swap_guard(bb_pool)) { - xe_sa_bo_swap_shadow(bb_pool); + scoped_guard(mutex, xe_mem_pool_bo_swap_guard(bb_pool)) { + xe_mem_pool_swap_shadow_locked(bb_pool); - err = xe_bb_init(bb, bb_pool, batch_size); + err = xe_mem_pool_insert_node(bb_pool, bb, batch_size * sizeof(u32)); if (err) { xe_gt_err(gt, "BB allocation failed.\n"); - xe_bb_free(bb, NULL); + kfree(bb); return err; } @@ -1227,6 +1229,7 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, size = xe_bo_size(src_bo); batch_size = 0; + xe_bb_tmp = (struct xe_bb){ .cs = xe_mem_pool_node_cpu_addr(bb), .len = 0 }; /* * Emit PTE and copy commands here. * The CCS copy command can only support limited size. If the size to be @@ -1255,24 +1258,27 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, xe_assert(xe, IS_ALIGNED(ccs_it.start, PAGE_SIZE)); batch_size += EMIT_COPY_CCS_DW; - emit_pte(m, bb, src_L0_pt, false, true, &src_it, src_L0, src); + emit_pte(m, &xe_bb_tmp, src_L0_pt, false, true, &src_it, src_L0, src); - emit_pte(m, bb, ccs_pt, false, false, &ccs_it, ccs_size, src); + emit_pte(m, &xe_bb_tmp, ccs_pt, false, false, &ccs_it, ccs_size, src); - bb->len = emit_flush_invalidate(bb->cs, bb->len, flush_flags); - flush_flags = xe_migrate_ccs_copy(m, bb, src_L0_ofs, src_is_pltt, + xe_bb_tmp.len = emit_flush_invalidate(xe_bb_tmp.cs, xe_bb_tmp.len, + flush_flags); + flush_flags = xe_migrate_ccs_copy(m, &xe_bb_tmp, src_L0_ofs, src_is_pltt, src_L0_ofs, dst_is_pltt, src_L0, ccs_ofs, true); - bb->len = emit_flush_invalidate(bb->cs, bb->len, flush_flags); + xe_bb_tmp.len = emit_flush_invalidate(xe_bb_tmp.cs, xe_bb_tmp.len, + flush_flags); size -= src_L0; } - xe_assert(xe, (batch_size_allocated == bb->len)); + xe_assert(xe, (batch_size_allocated == xe_bb_tmp.len)); + xe_assert(xe, bb->sa_node.size == xe_bb_tmp.len * sizeof(u32)); src_bo->bb_ccs[read_write] = bb; xe_sriov_vf_ccs_rw_update_bb_addr(ctx); - xe_sa_bo_sync_shadow(bb->bo); + xe_mem_pool_sync_shadow_locked(bb); } return 0; @@ -1297,10 +1303,10 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, enum xe_sriov_vf_ccs_rw_ctxs read_write) { - struct xe_bb *bb = src_bo->bb_ccs[read_write]; + struct xe_mem_pool_node *bb = src_bo->bb_ccs[read_write]; struct xe_device *xe = xe_bo_device(src_bo); + struct xe_mem_pool *bb_pool; struct xe_sriov_vf_ccs_ctx *ctx; - struct xe_sa_manager *bb_pool; u32 *cs; xe_assert(xe, IS_SRIOV_VF(xe)); @@ -1308,17 +1314,17 @@ void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, ctx = &xe->sriov.vf.ccs.contexts[read_write]; bb_pool = ctx->mem.ccs_bb_pool; - guard(mutex) (xe_sa_bo_swap_guard(bb_pool)); - xe_sa_bo_swap_shadow(bb_pool); + scoped_guard(mutex, xe_mem_pool_bo_swap_guard(bb_pool)) { + xe_mem_pool_swap_shadow_locked(bb_pool); - cs = xe_sa_bo_cpu_addr(bb->bo); - memset(cs, MI_NOOP, bb->len * sizeof(u32)); - xe_sriov_vf_ccs_rw_update_bb_addr(ctx); + cs = xe_mem_pool_node_cpu_addr(bb); + memset(cs, MI_NOOP, bb->sa_node.size); + xe_sriov_vf_ccs_rw_update_bb_addr(ctx); - xe_sa_bo_sync_shadow(bb->bo); - - xe_bb_free(bb, NULL); - src_bo->bb_ccs[read_write] = NULL; + xe_mem_pool_sync_shadow_locked(bb); + xe_mem_pool_free_node(bb); + src_bo->bb_ccs[read_write] = NULL; + } } /** @@ -1518,23 +1524,9 @@ static void emit_clear_main_copy(struct xe_gt *gt, struct xe_bb *bb, bb->len += len; } -static bool has_service_copy_support(struct xe_gt *gt) -{ - /* - * What we care about is whether the architecture was designed with - * service copy functionality (specifically the new MEM_SET / MEM_COPY - * instructions) so check the architectural engine list rather than the - * actual list since these instructions are usable on BCS0 even if - * all of the actual service copy engines (BCS1-BCS8) have been fused - * off. - */ - return gt->info.engine_mask & GENMASK(XE_HW_ENGINE_BCS8, - XE_HW_ENGINE_BCS1); -} - static u32 emit_clear_cmd_len(struct xe_gt *gt) { - if (has_service_copy_support(gt)) + if (gt->info.has_xe2_blt_instructions) return PVC_MEM_SET_CMD_LEN_DW; else return XY_FAST_COLOR_BLT_DW; @@ -1543,7 +1535,7 @@ static u32 emit_clear_cmd_len(struct xe_gt *gt) static void emit_clear(struct xe_gt *gt, struct xe_bb *bb, u64 src_ofs, u32 size, u32 pitch, bool is_vram) { - if (has_service_copy_support(gt)) + if (gt->info.has_xe2_blt_instructions) emit_clear_link_copy(gt, bb, src_ofs, size, pitch); else emit_clear_main_copy(gt, bb, src_ofs, size, pitch, diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c index 01673d2b2464..c2ecd27ec770 100644 --- a/drivers/gpu/drm/xe/xe_pci.c +++ b/drivers/gpu/drm/xe/xe_pci.c @@ -118,6 +118,7 @@ static const struct xe_graphics_desc graphics_xe2 = { static const struct xe_graphics_desc graphics_xe3p_lpg = { XE2_GFX_FEATURES, + .has_indirect_ring_state = 1, .multi_queue_engine_class_mask = BIT(XE_ENGINE_CLASS_COPY) | BIT(XE_ENGINE_CLASS_COMPUTE), .num_geometry_xecore_fuse_regs = 3, .num_compute_xecore_fuse_regs = 3, @@ -850,6 +851,15 @@ static struct xe_gt *alloc_primary_gt(struct xe_tile *tile, gt->info.num_geometry_xecore_fuse_regs = graphics_desc->num_geometry_xecore_fuse_regs; gt->info.num_compute_xecore_fuse_regs = graphics_desc->num_compute_xecore_fuse_regs; + /* + * Even if the service copy engines wind up being fused off, their + * presence in the IP descriptor indicates that the platform supports + * Xe2-style MEM_SET and MEM_COPY functionality. + */ + if (graphics_desc->hw_engine_mask & GENMASK(XE_HW_ENGINE_BCS8, + XE_HW_ENGINE_BCS1)) + gt->info.has_xe2_blt_instructions = true; + /* * Before media version 13, the media IP was part of the primary GT * so we need to add the media engines to the primary GT's engine list. diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index 80577e4b7437..8cc313182968 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -226,7 +226,7 @@ void xe_reg_whitelist_print_entry(struct drm_printer *p, unsigned int indent, } range_start = reg & REG_GENMASK(25, range_bit); - range_end = range_start | REG_GENMASK(range_bit, 0); + range_end = range_start | REG_GENMASK(range_bit - 1, 0); switch (val & RING_FORCE_TO_NONPRIV_ACCESS_MASK) { case RING_FORCE_TO_NONPRIV_ACCESS_RW: diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_migration.c b/drivers/gpu/drm/xe/xe_sriov_pf_migration.c index 6c4b16409cc9..150a241110fb 100644 --- a/drivers/gpu/drm/xe/xe_sriov_pf_migration.c +++ b/drivers/gpu/drm/xe/xe_sriov_pf_migration.c @@ -149,10 +149,11 @@ pf_migration_consume(struct xe_device *xe, unsigned int vfid) for_each_gt(gt, xe, gt_id) { data = xe_gt_sriov_pf_migration_save_consume(gt, vfid); - if (data && PTR_ERR(data) != EAGAIN) + if (!data) + continue; + if (!IS_ERR(data) || PTR_ERR(data) != -EAGAIN) return data; - if (PTR_ERR(data) == -EAGAIN) - more_data = true; + more_data = true; } if (!more_data) diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c index db023fb66a27..09b99fb2608b 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c @@ -14,9 +14,9 @@ #include "xe_guc.h" #include "xe_guc_submit.h" #include "xe_lrc.h" +#include "xe_mem_pool.h" #include "xe_migrate.h" #include "xe_pm.h" -#include "xe_sa.h" #include "xe_sriov_printk.h" #include "xe_sriov_vf.h" #include "xe_sriov_vf_ccs.h" @@ -141,43 +141,47 @@ static u64 get_ccs_bb_pool_size(struct xe_device *xe) static int alloc_bb_pool(struct xe_tile *tile, struct xe_sriov_vf_ccs_ctx *ctx) { + struct xe_mem_pool *pool; struct xe_device *xe = tile_to_xe(tile); - struct xe_sa_manager *sa_manager; + u32 *pool_cpu_addr, *last_dw_addr; u64 bb_pool_size; - int offset, err; + int err; bb_pool_size = get_ccs_bb_pool_size(xe); xe_sriov_info(xe, "Allocating %s CCS BB pool size = %lldMB\n", ctx->ctx_id ? "Restore" : "Save", bb_pool_size / SZ_1M); - sa_manager = __xe_sa_bo_manager_init(tile, bb_pool_size, SZ_4K, SZ_16, - XE_SA_BO_MANAGER_FLAG_SHADOW); - - if (IS_ERR(sa_manager)) { - xe_sriov_err(xe, "Suballocator init failed with error: %pe\n", - sa_manager); - err = PTR_ERR(sa_manager); + pool = xe_mem_pool_init(tile, bb_pool_size, sizeof(u32), + XE_MEM_POOL_BO_FLAG_INIT_SHADOW_COPY); + if (IS_ERR(pool)) { + xe_sriov_err(xe, "xe_mem_pool_init failed with error: %pe\n", + pool); + err = PTR_ERR(pool); return err; } - offset = 0; - xe_map_memset(xe, &sa_manager->bo->vmap, offset, MI_NOOP, - bb_pool_size); - xe_map_memset(xe, &sa_manager->shadow->vmap, offset, MI_NOOP, - bb_pool_size); + pool_cpu_addr = xe_mem_pool_cpu_addr(pool); + memset(pool_cpu_addr, 0, bb_pool_size); - offset = bb_pool_size - sizeof(u32); - xe_map_wr(xe, &sa_manager->bo->vmap, offset, u32, MI_BATCH_BUFFER_END); - xe_map_wr(xe, &sa_manager->shadow->vmap, offset, u32, MI_BATCH_BUFFER_END); + last_dw_addr = pool_cpu_addr + (bb_pool_size / sizeof(u32)) - 1; + *last_dw_addr = MI_BATCH_BUFFER_END; - ctx->mem.ccs_bb_pool = sa_manager; + /** + * Sync the main copy and shadow copy so that the shadow copy is + * replica of main copy. We sync only BBs after init part. So, we + * need to make sure the main pool and shadow copy are in sync after + * this point. This is needed as GuC may read the BB commands from + * shadow copy. + */ + xe_mem_pool_sync(pool); + ctx->mem.ccs_bb_pool = pool; return 0; } static void ccs_rw_update_ring(struct xe_sriov_vf_ccs_ctx *ctx) { - u64 addr = xe_sa_manager_gpu_addr(ctx->mem.ccs_bb_pool); + u64 addr = xe_mem_pool_gpu_addr(ctx->mem.ccs_bb_pool); struct xe_lrc *lrc = xe_exec_queue_lrc(ctx->mig_q); u32 dw[10], i = 0; @@ -388,7 +392,7 @@ err_ret: #define XE_SRIOV_VF_CCS_RW_BB_ADDR_OFFSET (2 * sizeof(u32)) void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx) { - u64 addr = xe_sa_manager_gpu_addr(ctx->mem.ccs_bb_pool); + u64 addr = xe_mem_pool_gpu_addr(ctx->mem.ccs_bb_pool); struct xe_lrc *lrc = xe_exec_queue_lrc(ctx->mig_q); struct xe_device *xe = gt_to_xe(ctx->mig_q->gt); @@ -412,8 +416,8 @@ int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo) struct xe_device *xe = xe_bo_device(bo); enum xe_sriov_vf_ccs_rw_ctxs ctx_id; struct xe_sriov_vf_ccs_ctx *ctx; + struct xe_mem_pool_node *bb; struct xe_tile *tile; - struct xe_bb *bb; int err = 0; xe_assert(xe, IS_VF_CCS_READY(xe)); @@ -445,7 +449,7 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo) { struct xe_device *xe = xe_bo_device(bo); enum xe_sriov_vf_ccs_rw_ctxs ctx_id; - struct xe_bb *bb; + struct xe_mem_pool_node *bb; xe_assert(xe, IS_VF_CCS_READY(xe)); @@ -471,8 +475,8 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo) */ void xe_sriov_vf_ccs_print(struct xe_device *xe, struct drm_printer *p) { - struct xe_sa_manager *bb_pool; enum xe_sriov_vf_ccs_rw_ctxs ctx_id; + struct xe_mem_pool *bb_pool; if (!IS_VF_CCS_READY(xe)) return; @@ -485,7 +489,7 @@ void xe_sriov_vf_ccs_print(struct xe_device *xe, struct drm_printer *p) drm_printf(p, "ccs %s bb suballoc info\n", ctx_id ? "write" : "read"); drm_printf(p, "-------------------------\n"); - drm_suballoc_dump_debug_info(&bb_pool->base, p, xe_sa_manager_gpu_addr(bb_pool)); + xe_mem_pool_dump(bb_pool, p); drm_puts(p, "\n"); } } diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs_types.h b/drivers/gpu/drm/xe/xe_sriov_vf_ccs_types.h index 22c499943d2a..6fc8f97ef3f4 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs_types.h +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs_types.h @@ -17,9 +17,6 @@ enum xe_sriov_vf_ccs_rw_ctxs { XE_SRIOV_VF_CCS_CTX_COUNT }; -struct xe_migrate; -struct xe_sa_manager; - /** * struct xe_sriov_vf_ccs_ctx - VF CCS migration context data. */ @@ -33,7 +30,7 @@ struct xe_sriov_vf_ccs_ctx { /** @mem: memory data */ struct { /** @mem.ccs_bb_pool: Pool from which batch buffers are allocated. */ - struct xe_sa_manager *ccs_bb_pool; + struct xe_mem_pool *ccs_bb_pool; } mem; }; diff --git a/drivers/gpu/drm/xe/xe_tile_types.h b/drivers/gpu/drm/xe/xe_tile_types.h index 33932fd547d7..0048100ccb72 100644 --- a/drivers/gpu/drm/xe/xe_tile_types.h +++ b/drivers/gpu/drm/xe/xe_tile_types.h @@ -106,8 +106,6 @@ struct xe_tile { struct xe_lmtt lmtt; } pf; struct { - /** @sriov.vf.ggtt_balloon: GGTT regions excluded from use. */ - struct xe_ggtt_node *ggtt_balloon[2]; /** @sriov.vf.self_config: VF configuration data */ struct xe_tile_sriov_vf_selfconfig self_config; } vf; diff --git a/drivers/gpu/drm/xe/xe_tuning.c b/drivers/gpu/drm/xe/xe_tuning.c index f8de6a4bf189..0b78ec2bc6a4 100644 --- a/drivers/gpu/drm/xe/xe_tuning.c +++ b/drivers/gpu/drm/xe/xe_tuning.c @@ -97,7 +97,7 @@ static const struct xe_rtp_entry_sr gt_tunings[] = { { XE_RTP_NAME("Tuning: Set STLB Bank Hash Mode to 4KB"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3510, XE_RTP_END_VERSION_UNDEFINED), IS_INTEGRATED), - XE_RTP_ACTIONS(FIELD_SET(XEHP_GAMSTLB_CTRL, BANK_HASH_MODE, + XE_RTP_ACTIONS(FIELD_SET(GAMSTLB_CTRL, BANK_HASH_MODE, BANK_HASH_4KB_MODE)) }, }; diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 56e2db50bb36..ab6cc1f0a789 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -1120,6 +1120,25 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm, xe_bo_assert_held(bo); + /* + * Reject only WILLNEED mappings on DONTNEED/PURGED BOs. This + * gates new vm_bind ioctls (user supplies WILLNEED) while + * still allowing partial-unbind / remap splits whose new VMAs + * inherit the parent's DONTNEED attr. It must also run before + * xe_bo_willneed_get_locked() below so a 0->1 holder bump + * cannot silently promote DONTNEED back to WILLNEED. + */ + if (vma->attr.purgeable_state == XE_MADV_PURGEABLE_WILLNEED) { + if (xe_bo_madv_is_dontneed(bo)) { + xe_vma_free(vma); + return ERR_PTR(-EBUSY); + } + if (xe_bo_is_purged(bo)) { + xe_vma_free(vma); + return ERR_PTR(-EINVAL); + } + } + vm_bo = drm_gpuvm_bo_obtain_locked(vma->gpuva.vm, &bo->ttm.base); if (IS_ERR(vm_bo)) { xe_vma_free(vma); @@ -1131,6 +1150,10 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm, vma->gpuva.gem.offset = bo_offset_or_userptr; drm_gpuva_link(&vma->gpuva, vm_bo); drm_gpuvm_bo_put(vm_bo); + + xe_bo_vma_count_inc_locked(bo); + if (vma->attr.purgeable_state == XE_MADV_PURGEABLE_WILLNEED) + xe_bo_willneed_get_locked(bo); } else /* userptr or null */ { if (!is_null && !is_cpu_addr_mirror) { struct xe_userptr_vma *uvma = to_userptr_vma(vma); @@ -1208,7 +1231,10 @@ static void xe_vma_destroy(struct xe_vma *vma, struct dma_fence *fence) xe_bo_assert_held(bo); drm_gpuva_unlink(&vma->gpuva); - xe_bo_recompute_purgeable_state(bo); + + xe_bo_vma_count_dec_locked(bo); + if (vma->attr.purgeable_state == XE_MADV_PURGEABLE_WILLNEED) + xe_bo_willneed_put_locked(bo); } xe_vm_assert_held(vm); @@ -3016,7 +3042,7 @@ static void vm_bind_ioctl_ops_unwind(struct xe_vm *vm, * @res_evict: Allow evicting resources during validation * @validate: Perform BO validation * @request_decompress: Request BO decompression - * @check_purged: Reject operation if BO is purged + * @check_purged: Reject operation if BO is DONTNEED or PURGED */ struct xe_vma_lock_and_validate_flags { u32 res_evict : 1; @@ -3030,6 +3056,7 @@ static int vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma, { struct xe_bo *bo = xe_vma_bo(vma); struct xe_vm *vm = xe_vma_vm(vma); + bool validate_bo = flags.validate; int err = 0; if (bo) { @@ -3044,7 +3071,11 @@ static int vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma, err = -EINVAL; /* BO already purged */ } - if (!err && flags.validate) + /* Don't validate the BO for DONTNEED/PURGED remap remnants. */ + if (vma->attr.purgeable_state != XE_MADV_PURGEABLE_WILLNEED) + validate_bo = false; + + if (!err && validate_bo) err = xe_bo_validate(bo, vm, xe_vm_allow_vm_eviction(vm) && flags.res_evict, exec); @@ -3152,7 +3183,7 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm, op->map.immediate, .request_decompress = op->map.request_decompress, - .check_purged = true, + .check_purged = false, }); break; case DRM_GPUVA_OP_REMAP: @@ -3174,7 +3205,7 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm, .res_evict = res_evict, .validate = true, .request_decompress = false, - .check_purged = true, + .check_purged = false, }); if (!err && op->remap.next) err = vma_lock_and_validate(exec, op->remap.next, @@ -3182,7 +3213,7 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm, .res_evict = res_evict, .validate = true, .request_decompress = false, - .check_purged = true, + .check_purged = false, }); break; case DRM_GPUVA_OP_UNMAP: @@ -3211,9 +3242,11 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm, } /* - * Prefetch attempts to migrate BO's backing store without - * repopulating it first. Purged BOs have no backing store - * to migrate, so reject the operation. + * PREFETCH is the only op that still gates on BO purge state. + * MAP/REMAP handle this inside xe_vma_create() so partial + * unbind on a DONTNEED BO still works. PREFETCH skips + * xe_vma_create() and would migrate a BO with no backing + * store, so reject DONTNEED/PURGED here. */ err = vma_lock_and_validate(exec, gpuva_to_vma(op->base.prefetch.va), @@ -3658,6 +3691,8 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm, op == DRM_XE_VM_BIND_OP_MAP_USERPTR) || XE_IOCTL_DBG(xe, coh_mode == XE_COH_NONE && op == DRM_XE_VM_BIND_OP_MAP_USERPTR) || + XE_IOCTL_DBG(xe, !IS_DGFX(xe) && coh_mode == XE_COH_NONE && + is_cpu_addr_mirror) || XE_IOCTL_DBG(xe, xe_device_is_l2_flush_optimized(xe) && (op == DRM_XE_VM_BIND_OP_MAP_USERPTR || is_cpu_addr_mirror) && @@ -4156,7 +4191,8 @@ int xe_vm_get_property_ioctl(struct drm_device *drm, void *data, int ret = 0; if (XE_IOCTL_DBG(xe, (args->reserved[0] || args->reserved[1] || - args->reserved[2]))) + args->reserved[2] || args->extensions || + args->pad))) return -EINVAL; vm = xe_vm_lookup(xef, args->vm_id); diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c index 66f00d3f5c07..c4fb29004195 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.c +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c @@ -185,147 +185,6 @@ static void madvise_pat_index(struct xe_device *xe, struct xe_vm *vm, } } -/** - * xe_bo_is_dmabuf_shared() - Check if BO is shared via dma-buf - * @bo: Buffer object - * - * Prevent marking imported or exported dma-bufs as purgeable. - * For imported BOs, Xe doesn't own the backing store and cannot - * safely reclaim pages (exporter or other devices may still be - * using them). For exported BOs, external devices may have active - * mappings we cannot track. - * - * Return: true if BO is imported or exported, false otherwise - */ -static bool xe_bo_is_dmabuf_shared(struct xe_bo *bo) -{ - struct drm_gem_object *obj = &bo->ttm.base; - - /* Imported: exporter owns backing store */ - if (drm_gem_is_imported(obj)) - return true; - - /* Exported: external devices may be accessing */ - if (obj->dma_buf) - return true; - - return false; -} - -/** - * enum xe_bo_vmas_purge_state - VMA purgeable state aggregation - * - * Distinguishes whether a BO's VMAs are all DONTNEED, have at least - * one WILLNEED, or have no VMAs at all. - * - * Enum values align with XE_MADV_PURGEABLE_* states for consistency. - */ -enum xe_bo_vmas_purge_state { - /** @XE_BO_VMAS_STATE_WILLNEED: At least one VMA is WILLNEED */ - XE_BO_VMAS_STATE_WILLNEED = 0, - /** @XE_BO_VMAS_STATE_DONTNEED: All VMAs are DONTNEED */ - XE_BO_VMAS_STATE_DONTNEED = 1, - /** @XE_BO_VMAS_STATE_NO_VMAS: BO has no VMAs */ - XE_BO_VMAS_STATE_NO_VMAS = 2, -}; - -/* - * xe_bo_recompute_purgeable_state() casts between xe_bo_vmas_purge_state and - * xe_madv_purgeable_state. Enforce that WILLNEED=0 and DONTNEED=1 match across - * both enums so the single-line cast is always valid. - */ -static_assert(XE_BO_VMAS_STATE_WILLNEED == (int)XE_MADV_PURGEABLE_WILLNEED, - "VMA purge state WILLNEED must equal madv purgeable WILLNEED"); -static_assert(XE_BO_VMAS_STATE_DONTNEED == (int)XE_MADV_PURGEABLE_DONTNEED, - "VMA purge state DONTNEED must equal madv purgeable DONTNEED"); - -/** - * xe_bo_all_vmas_dontneed() - Determine BO VMA purgeable state - * @bo: Buffer object - * - * Check all VMAs across all VMs to determine aggregate purgeable state. - * Shared BOs require unanimous DONTNEED state from all mappings. - * - * Caller must hold BO dma-resv lock. - * - * Return: XE_BO_VMAS_STATE_DONTNEED if all VMAs are DONTNEED, - * XE_BO_VMAS_STATE_WILLNEED if at least one VMA is not DONTNEED, - * XE_BO_VMAS_STATE_NO_VMAS if BO has no VMAs - */ -static enum xe_bo_vmas_purge_state xe_bo_all_vmas_dontneed(struct xe_bo *bo) -{ - struct drm_gpuvm_bo *vm_bo; - struct drm_gpuva *gpuva; - struct drm_gem_object *obj = &bo->ttm.base; - bool has_vmas = false; - - xe_bo_assert_held(bo); - - /* Shared dma-bufs cannot be purgeable */ - if (xe_bo_is_dmabuf_shared(bo)) - return XE_BO_VMAS_STATE_WILLNEED; - - drm_gem_for_each_gpuvm_bo(vm_bo, obj) { - drm_gpuvm_bo_for_each_va(gpuva, vm_bo) { - struct xe_vma *vma = gpuva_to_vma(gpuva); - - has_vmas = true; - - /* Any non-DONTNEED VMA prevents purging */ - if (vma->attr.purgeable_state != XE_MADV_PURGEABLE_DONTNEED) - return XE_BO_VMAS_STATE_WILLNEED; - } - } - - /* - * No VMAs => preserve existing BO purgeable state. - * Avoids incorrectly flipping DONTNEED -> WILLNEED when last VMA unmapped. - */ - if (!has_vmas) - return XE_BO_VMAS_STATE_NO_VMAS; - - return XE_BO_VMAS_STATE_DONTNEED; -} - -/** - * xe_bo_recompute_purgeable_state() - Recompute BO purgeable state from VMAs - * @bo: Buffer object - * - * Walk all VMAs to determine if BO should be purgeable or not. - * Shared BOs require unanimous DONTNEED state from all mappings. - * If the BO has no VMAs the existing state is preserved. - * - * Locking: Caller must hold BO dma-resv lock. When iterating GPUVM lists, - * VM lock must also be held (write) to prevent concurrent VMA modifications. - * This is satisfied at both call sites: - * - xe_vma_destroy(): holds vm->lock write - * - madvise_purgeable(): holds vm->lock write (from madvise ioctl path) - * - * Return: nothing - */ -void xe_bo_recompute_purgeable_state(struct xe_bo *bo) -{ - enum xe_bo_vmas_purge_state vma_state; - - if (!bo) - return; - - xe_bo_assert_held(bo); - - /* - * Once purged, always purged. Cannot transition back to WILLNEED. - * This matches i915 semantics where purged BOs are permanently invalid. - */ - if (bo->madv_purgeable == XE_MADV_PURGEABLE_PURGED) - return; - - vma_state = xe_bo_all_vmas_dontneed(bo); - - if (vma_state != (enum xe_bo_vmas_purge_state)bo->madv_purgeable && - vma_state != XE_BO_VMAS_STATE_NO_VMAS) - xe_bo_set_purgeable_state(bo, (enum xe_madv_purgeable_state)vma_state); -} - /** * madvise_purgeable - Handle purgeable buffer object advice * @xe: XE device @@ -359,12 +218,6 @@ static void madvise_purgeable(struct xe_device *xe, struct xe_vm *vm, /* BO must be locked before modifying madv state */ xe_bo_assert_held(bo); - /* Skip shared dma-bufs - no PTEs to zap */ - if (xe_bo_is_dmabuf_shared(bo)) { - vmas[i]->skip_invalidation = true; - continue; - } - /* * Once purged, always purged. Cannot transition back to WILLNEED. * This matches i915 semantics where purged BOs are permanently invalid. @@ -377,13 +230,14 @@ static void madvise_purgeable(struct xe_device *xe, struct xe_vm *vm, switch (op->purge_state_val.val) { case DRM_XE_VMA_PURGEABLE_STATE_WILLNEED: - vmas[i]->attr.purgeable_state = XE_MADV_PURGEABLE_WILLNEED; vmas[i]->skip_invalidation = true; - - xe_bo_recompute_purgeable_state(bo); + /* Only act on a real DONTNEED -> WILLNEED transition. */ + if (vmas[i]->attr.purgeable_state == XE_MADV_PURGEABLE_DONTNEED) { + vmas[i]->attr.purgeable_state = XE_MADV_PURGEABLE_WILLNEED; + xe_bo_willneed_get_locked(bo); + } break; case DRM_XE_VMA_PURGEABLE_STATE_DONTNEED: - vmas[i]->attr.purgeable_state = XE_MADV_PURGEABLE_DONTNEED; /* * Don't zap PTEs at DONTNEED time -- pages are still * alive. The zap happens in xe_bo_move_notify() right @@ -391,7 +245,11 @@ static void madvise_purgeable(struct xe_device *xe, struct xe_vm *vm, */ vmas[i]->skip_invalidation = true; - xe_bo_recompute_purgeable_state(bo); + /* Only act on a real WILLNEED -> DONTNEED transition. */ + if (vmas[i]->attr.purgeable_state == XE_MADV_PURGEABLE_WILLNEED) { + vmas[i]->attr.purgeable_state = XE_MADV_PURGEABLE_DONTNEED; + xe_bo_willneed_put_locked(bo); + } break; default: /* Should never hit - values validated in madvise_args_are_sane() */ @@ -621,6 +479,45 @@ static int xe_madvise_purgeable_retained_to_user(const struct xe_madvise_details return 0; } +static bool check_pat_args_are_sane(struct xe_device *xe, + struct xe_vmas_in_madvise_range *madvise_range, + u16 pat_index) +{ + u16 coh_mode = xe_pat_index_get_coh_mode(xe, pat_index); + int i; + + /* + * Using coh_none with CPU cached buffers is not allowed on iGPU. + * On iGPU the GPU shares the LLC with the CPU, so with coh_none + * the GPU bypasses CPU caches and reads directly from DRAM, + * potentially seeing stale sensitive data from previously freed + * pages. On dGPU this restriction does not apply, because the + * platform does not provide a non-coherent system memory access + * path that would violate the DMA coherency contract. + */ + if (coh_mode != XE_COH_NONE || IS_DGFX(xe)) + return true; + + for (i = 0; i < madvise_range->num_vmas; i++) { + struct xe_vma *vma = madvise_range->vmas[i]; + struct xe_bo *bo = xe_vma_bo(vma); + + if (bo) { + /* BO with WB caching + COH_NONE is not allowed */ + if (XE_IOCTL_DBG(xe, bo->cpu_caching == DRM_XE_GEM_CPU_CACHING_WB)) + return false; + /* Imported dma-buf without caching info, assume cached */ + if (XE_IOCTL_DBG(xe, !bo->cpu_caching)) + return false; + } else if (XE_IOCTL_DBG(xe, xe_vma_is_cpu_addr_mirror(vma) || + xe_vma_is_userptr(vma))) + /* System memory (userptr/SVM) is always CPU cached */ + return false; + } + + return true; +} + static bool check_bo_args_are_sane(struct xe_vm *vm, struct xe_vma **vmas, int num_vmas, u32 atomic_val) { @@ -750,6 +647,14 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil } } + if (args->type == DRM_XE_MEM_RANGE_ATTR_PAT) { + if (!check_pat_args_are_sane(xe, &madvise_range, + args->pat_index.val)) { + err = -EINVAL; + goto free_vmas; + } + } + if (madvise_range.has_bo_vmas) { if (args->type == DRM_XE_MEM_RANGE_ATTR_ATOMIC) { if (!check_bo_args_are_sane(vm, madvise_range.vmas, diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.h b/drivers/gpu/drm/xe/xe_vm_madvise.h index 39acd2689ca0..a3078f634c7e 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.h +++ b/drivers/gpu/drm/xe/xe_vm_madvise.h @@ -13,6 +13,4 @@ struct xe_bo; int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *file); -void xe_bo_recompute_purgeable_state(struct xe_bo *bo); - #endif diff --git a/drivers/gpu/drm/xe/xe_vsec.c b/drivers/gpu/drm/xe/xe_vsec.c index 4ebb4dbe1c9b..a9baf0bfe572 100644 --- a/drivers/gpu/drm/xe/xe_vsec.c +++ b/drivers/gpu/drm/xe/xe_vsec.c @@ -140,10 +140,10 @@ static int xe_guid_decode(u32 guid, int *index, u32 *offset) return 0; } -int xe_pmt_telem_read(struct pci_dev *pdev, u32 guid, u64 *data, loff_t user_offset, +int xe_pmt_telem_read(struct device *dev, u32 guid, u64 *data, loff_t user_offset, u32 count) { - struct xe_device *xe = pdev_to_xe_device(pdev); + struct xe_device *xe = kdev_to_xe_device(dev); void __iomem *telem_addr = xe->mmio.regs + BMG_TELEMETRY_OFFSET; u32 mem_region; u32 offset; @@ -198,7 +198,6 @@ void xe_vsec_init(struct xe_device *xe) { struct intel_vsec_platform_info *info; struct device *dev = xe->drm.dev; - struct pci_dev *pdev = to_pci_dev(dev); enum xe_vsec platform; platform = get_platform_info(xe); @@ -221,6 +220,6 @@ void xe_vsec_init(struct xe_device *xe) * Register a VSEC. Cleanup is handled using device managed * resources. */ - intel_vsec_register(pdev, info); + intel_vsec_register(dev, info); } MODULE_IMPORT_NS("INTEL_VSEC"); diff --git a/drivers/gpu/drm/xe/xe_vsec.h b/drivers/gpu/drm/xe/xe_vsec.h index dabfb4e02d70..a25b4e6e681b 100644 --- a/drivers/gpu/drm/xe/xe_vsec.h +++ b/drivers/gpu/drm/xe/xe_vsec.h @@ -6,10 +6,10 @@ #include -struct pci_dev; +struct device; struct xe_device; void xe_vsec_init(struct xe_device *xe); -int xe_pmt_telem_read(struct pci_dev *pdev, u32 guid, u64 *data, loff_t user_offset, u32 count); +int xe_pmt_telem_read(struct device *dev, u32 guid, u64 *data, loff_t user_offset, u32 count); #endif diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c index 546296f0220b..4b1cbced06be 100644 --- a/drivers/gpu/drm/xe/xe_wa.c +++ b/drivers/gpu/drm/xe/xe_wa.c @@ -743,14 +743,6 @@ static const struct xe_rtp_entry_sr lrc_was[] = { XE_RTP_RULES(GRAPHICS_VERSION(2001), ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(SET(WM_CHICKEN3, HIZ_PLANE_COMPRESSION_DIS)) }, - { XE_RTP_NAME("14019988906"), - XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002), ENGINE_CLASS(RENDER)), - XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FLSH_IGNORES_PSD)) - }, - { XE_RTP_NAME("14019877138"), - XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002), ENGINE_CLASS(RENDER)), - XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FD_END_COLLECT)) - }, { XE_RTP_NAME("14021490052"), XE_RTP_RULES(GRAPHICS_VERSION(2001), ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(SET(FF_MODE, diff --git a/drivers/greybus/es2.c b/drivers/greybus/es2.c index 6ae0ac828afa..75b889fa21b4 100644 --- a/drivers/greybus/es2.c +++ b/drivers/greybus/es2.c @@ -772,7 +772,6 @@ static int check_urb_status(struct urb *urb) static void es2_destroy(struct es2_ap_dev *es2) { - struct usb_device *udev; struct urb *urb; int i; @@ -804,10 +803,7 @@ static void es2_destroy(struct es2_ap_dev *es2) gb_hd_cport_release_reserved(es2->hd, ES2_CPORT_CDSI1); gb_hd_cport_release_reserved(es2->hd, ES2_CPORT_CDSI0); - udev = es2->usb_dev; gb_hd_put(es2->hd); - - usb_put_dev(udev); } static void cport_in_callback(struct urb *urb) @@ -1257,11 +1253,10 @@ static int ap_probe(struct usb_interface *interface, bool bulk_in_found = false; bool arpc_in_found = false; - udev = usb_get_dev(interface_to_usbdev(interface)); + udev = interface_to_usbdev(interface); num_cports = apb_get_cport_count(udev); if (num_cports < 0) { - usb_put_dev(udev); dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n", num_cports); return num_cports; @@ -1269,10 +1264,8 @@ static int ap_probe(struct usb_interface *interface, hd = gb_hd_create(&es2_driver, &udev->dev, ES2_GBUF_MSG_SIZE_MAX, num_cports); - if (IS_ERR(hd)) { - usb_put_dev(udev); + if (IS_ERR(hd)) return PTR_ERR(hd); - } es2 = hd_to_es2(hd); es2->hd = hd; diff --git a/drivers/greybus/gb-beagleplay.c b/drivers/greybus/gb-beagleplay.c index 87186f891a6a..244966d56c9b 100644 --- a/drivers/greybus/gb-beagleplay.c +++ b/drivers/greybus/gb-beagleplay.c @@ -242,30 +242,26 @@ static void hdlc_write(struct gb_beagleplay *bg) } /** - * hdlc_append() - Queue HDLC data for sending. + * hdlc_append() - Queue a single HDLC byte for sending. * @bg: beagleplay greybus driver * @value: hdlc byte to transmit * - * Assumes that producer lock as been acquired. + * Caller must hold tx_producer_lock and must have ensured sufficient + * space in the circular buffer before calling (see hdlc_tx_frames()). */ static void hdlc_append(struct gb_beagleplay *bg, u8 value) { - int tail, head = bg->tx_circ_buf.head; + int head = bg->tx_circ_buf.head; + int tail = READ_ONCE(bg->tx_circ_buf.tail); - while (true) { - tail = READ_ONCE(bg->tx_circ_buf.tail); + lockdep_assert_held(&bg->tx_producer_lock); + if (WARN_ON_ONCE(CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) < 1)) + return; - if (CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) >= 1) { - bg->tx_circ_buf.buf[head] = value; - - /* Finish producing HDLC byte */ - smp_store_release(&bg->tx_circ_buf.head, - (head + 1) & (TX_CIRC_BUF_SIZE - 1)); - return; - } - dev_warn(&bg->sd->dev, "Tx circ buf full"); - usleep_range(3000, 5000); - } + bg->tx_circ_buf.buf[head] = value; + /* Ensure buffer write is visible before advancing head. */ + smp_store_release(&bg->tx_circ_buf.head, + (head + 1) & (TX_CIRC_BUF_SIZE - 1)); } static void hdlc_append_escaped(struct gb_beagleplay *bg, u8 value) @@ -313,13 +309,94 @@ static void hdlc_transmit(struct work_struct *work) spin_unlock_bh(&bg->tx_consumer_lock); } -static void hdlc_tx_frames(struct gb_beagleplay *bg, u8 address, u8 control, +/** + * hdlc_encoded_length() - Calculate worst-case encoded length of an HDLC frame. + * @payloads: array of payload buffers + * @count: number of payloads + * + * Every data byte may need HDLC escaping (doubling its size). + * Frame layout: flag(1) + address(1-2) + control(1-2) + payload + CRC(2-4) + flag(1). + * + * Returns the maximum number of bytes needed in the circular buffer. + */ +static size_t hdlc_encoded_length(const struct hdlc_payload payloads[], + size_t count) +{ + size_t i, payload_len = 0; + + for (i = 0; i < count; i++) + payload_len += payloads[i].len; + + /* + * Worst case: every data byte needs escaping (doubles in size). + * data bytes = address(1) + control(1) + payload + crc(2) + * framing = opening flag(1) + closing flag(1) + */ + return 2 + (1 + 1 + payload_len + 2) * 2; +} + +#define HDLC_TX_BUF_WAIT_RETRIES 500 +#define HDLC_TX_BUF_WAIT_US_MIN 3000 +#define HDLC_TX_BUF_WAIT_US_MAX 5000 + +/** + * hdlc_tx_frames() - Encode and queue an HDLC frame for transmission. + * @bg: beagleplay greybus driver + * @address: HDLC address field + * @control: HDLC control field + * @payloads: array of payload buffers + * @count: number of payloads + * + * Sleeps outside the spinlock until enough circular-buffer space is + * available, then verifies space under the lock and writes the entire + * frame atomically. Either a complete frame is enqueued or nothing is + * written, avoiding both sleeping in atomic context and partial frames. + * + * Returns 0 on success, -EAGAIN if the buffer remains full after retries. + */ +static int hdlc_tx_frames(struct gb_beagleplay *bg, u8 address, u8 control, const struct hdlc_payload payloads[], size_t count) { + size_t needed = hdlc_encoded_length(payloads, count); + int retries = HDLC_TX_BUF_WAIT_RETRIES; size_t i; + int head, tail; + + /* Wait outside the lock for sufficient buffer space. */ + while (retries--) { + /* Pairs with smp_store_release() in hdlc_append(). */ + head = smp_load_acquire(&bg->tx_circ_buf.head); + tail = READ_ONCE(bg->tx_circ_buf.tail); + + if (CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) >= needed) + break; + + /* Kick the consumer and sleep — no lock held. */ + schedule_work(&bg->tx_work); + usleep_range(HDLC_TX_BUF_WAIT_US_MIN, HDLC_TX_BUF_WAIT_US_MAX); + } + + if (retries < 0) { + dev_warn_ratelimited(&bg->sd->dev, "Tx circ buf full, dropping frame\n"); + return -EAGAIN; + } spin_lock(&bg->tx_producer_lock); + /* + * Re-check space under the lock to close the TOCTOU window. + * This should be rare since tx_producer_lock serialises all + * producers and the consumer only frees space. If it fires, + * the caller is expected to handle -EAGAIN (retry or report). + */ + head = bg->tx_circ_buf.head; + tail = READ_ONCE(bg->tx_circ_buf.tail); + if (unlikely(CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) < needed)) { + spin_unlock(&bg->tx_producer_lock); + dev_warn_ratelimited(&bg->sd->dev, "Tx circ buf space lost, dropping frame\n"); + return -EAGAIN; + } + hdlc_append_tx_frame(bg); hdlc_append_tx_u8(bg, address); hdlc_append_tx_u8(bg, control); @@ -333,11 +410,16 @@ static void hdlc_tx_frames(struct gb_beagleplay *bg, u8 address, u8 control, spin_unlock(&bg->tx_producer_lock); schedule_work(&bg->tx_work); + return 0; } static void hdlc_tx_s_frame_ack(struct gb_beagleplay *bg) { - hdlc_tx_frames(bg, bg->rx_buffer[0], (bg->rx_buffer[1] >> 1) & 0x7, NULL, 0); + int ret; + + ret = hdlc_tx_frames(bg, bg->rx_buffer[0], (bg->rx_buffer[1] >> 1) & 0x7, NULL, 0); + if (ret) + dev_warn_ratelimited(&bg->sd->dev, "Failed to send HDLC ACK: %d\n", ret); } static void hdlc_rx_frame(struct gb_beagleplay *bg) @@ -535,6 +617,19 @@ static size_t cc1352_bootloader_rx(struct gb_beagleplay *bg, const u8 *data, int ret; size_t off = 0; + if (count > sizeof(bg->rx_buffer) - bg->rx_buffer_len) { + dev_err_ratelimited(&bg->sd->dev, "Bootloader RX buffer overflow"); + bg->rx_buffer_len = 0; + return count; + } + + if (count > sizeof(bg->rx_buffer) - bg->rx_buffer_len) { + dev_warn(&bg->sd->dev, + "dropping oversized bootloader receive chunk"); + bg->rx_buffer_len = 0; + return count; + } + memcpy(bg->rx_buffer + bg->rx_buffer_len, data, count); bg->rx_buffer_len += count; @@ -595,6 +690,7 @@ static int gb_message_send(struct gb_host_device *hd, u16 cport, struct gb_messa struct gb_beagleplay *bg = dev_get_drvdata(&hd->dev); struct hdlc_payload payloads[3]; __le16 cport_id = cpu_to_le16(cport); + int ret; dev_dbg(&hd->dev, "Sending greybus message with Operation %u, Type: %X on Cport %u", msg->header->operation_id, msg->header->type, cport); @@ -609,7 +705,10 @@ static int gb_message_send(struct gb_host_device *hd, u16 cport, struct gb_messa payloads[2].buf = msg->payload; payloads[2].len = msg->payload_size; - hdlc_tx_frames(bg, ADDRESS_GREYBUS, 0x03, payloads, 3); + ret = hdlc_tx_frames(bg, ADDRESS_GREYBUS, 0x03, payloads, 3); + if (ret) + return ret; + greybus_message_sent(bg->gb_hd, msg, 0); return 0; @@ -622,20 +721,20 @@ static void gb_message_cancel(struct gb_message *message) static struct gb_hd_driver gb_hdlc_driver = { .message_send = gb_message_send, .message_cancel = gb_message_cancel }; -static void gb_beagleplay_start_svc(struct gb_beagleplay *bg) +static int gb_beagleplay_start_svc(struct gb_beagleplay *bg) { const u8 command = CONTROL_SVC_START; const struct hdlc_payload payload = { .len = 1, .buf = (void *)&command }; - hdlc_tx_frames(bg, ADDRESS_CONTROL, 0x03, &payload, 1); + return hdlc_tx_frames(bg, ADDRESS_CONTROL, 0x03, &payload, 1); } -static void gb_beagleplay_stop_svc(struct gb_beagleplay *bg) +static int gb_beagleplay_stop_svc(struct gb_beagleplay *bg) { const u8 command = CONTROL_SVC_STOP; const struct hdlc_payload payload = { .len = 1, .buf = (void *)&command }; - hdlc_tx_frames(bg, ADDRESS_CONTROL, 0x03, &payload, 1); + return hdlc_tx_frames(bg, ADDRESS_CONTROL, 0x03, &payload, 1); } static int cc1352_bootloader_wait_for_ack(struct gb_beagleplay *bg) @@ -873,7 +972,9 @@ static enum fw_upload_err cc1352_prepare(struct fw_upload *fw_upload, gb_greybus_deinit(bg); msleep(5 * MSEC_PER_SEC); - gb_beagleplay_stop_svc(bg); + /* Best effort — device is entering bootloader mode regardless. */ + if (gb_beagleplay_stop_svc(bg)) + dev_warn(&bg->sd->dev, "Failed to send SVC stop before flashing\n"); msleep(200); flush_work(&bg->tx_work); @@ -915,7 +1016,9 @@ static enum fw_upload_err cc1352_prepare(struct fw_upload *fw_upload, if (gb_greybus_init(bg) < 0) return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_RW_ERROR, "Failed to initialize greybus"); - gb_beagleplay_start_svc(bg); + if (gb_beagleplay_start_svc(bg)) + return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_RW_ERROR, + "Failed to restart SVC after skip"); return FW_UPLOAD_ERR_FW_INVALID; } @@ -996,7 +1099,9 @@ static enum fw_upload_err cc1352_poll_complete(struct fw_upload *fw_upload) return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_RW_ERROR, "Failed to initialize greybus"); - gb_beagleplay_start_svc(bg); + if (gb_beagleplay_start_svc(bg) < 0) + return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_RW_ERROR, + "Failed to start SVC"); return FW_UPLOAD_ERR_NONE; } @@ -1107,10 +1212,14 @@ static int gb_beagleplay_probe(struct serdev_device *serdev) if (ret) goto free_fw; - gb_beagleplay_start_svc(bg); + ret = gb_beagleplay_start_svc(bg); + if (ret) + goto free_greybus; return 0; +free_greybus: + gb_greybus_deinit(bg); free_fw: gb_fw_deinit(bg); free_hdlc: @@ -1126,6 +1235,7 @@ static void gb_beagleplay_remove(struct serdev_device *serdev) gb_fw_deinit(bg); gb_greybus_deinit(bg); + /* Best effort — device is being removed. */ gb_beagleplay_stop_svc(bg); hdlc_deinit(bg); gb_serdev_deinit(bg); diff --git a/drivers/greybus/svc.c b/drivers/greybus/svc.c index 1b854f53f21e..490577731a19 100644 --- a/drivers/greybus/svc.c +++ b/drivers/greybus/svc.c @@ -775,10 +775,9 @@ static void gb_svc_pwrmon_debugfs_init(struct gb_svc *svc) if (!rail_count || rail_count > GB_SVC_PWRMON_MAX_RAIL_COUNT) goto err_pwrmon_debugfs; - bufsize = sizeof(*rail_names) + - GB_SVC_PWRMON_RAIL_NAME_BUFSIZE * rail_count; + bufsize = struct_size(rail_names, name, rail_count); - rail_names = kzalloc(bufsize, GFP_KERNEL); + rail_names = kzalloc_flex(*rail_names, name, rail_count); if (!rail_names) goto err_pwrmon_debugfs; diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index c1d9f7c6a5f2..ff2f580b660b 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -107,7 +107,6 @@ menu "Special HID drivers" config HID_A4TECH tristate "A4TECH mice" - default !EXPERT help Support for some A4TECH mice with two scroll wheels. @@ -140,7 +139,6 @@ config HID_APPLE tristate "Apple {i,Power,Mac}Books" depends on LEDS_CLASS depends on NEW_LEDS - default !EXPERT help Support for some Apple devices which less or more break HID specification. @@ -209,7 +207,6 @@ config HID_AUREAL config HID_BELKIN tristate "Belkin Flip KVM and Wireless keyboard" - default !EXPERT help Support for Belkin Flip KVM and Wireless keyboard. @@ -237,14 +234,12 @@ config HID_BIGBEN_FF config HID_CHERRY tristate "Cherry Cymotion keyboard" - default !EXPERT help Support for Cherry Cymotion keyboard. config HID_CHICONY tristate "Chicony devices" depends on USB_HID - default !EXPERT help Support for Chicony Tactical pad and special keys on Chicony keyboards. @@ -322,7 +317,6 @@ config HID_CREATIVE_SB0540 config HID_CYPRESS tristate "Cypress mouse and barcode readers" - default !EXPERT help Support for cypress mouse and barcode readers. @@ -388,7 +382,6 @@ config HID_EVISION config HID_EZKEY tristate "Ezkey BTC 8193 keyboard" - default !EXPERT help Support for Ezkey BTC 8193 keyboard. @@ -564,7 +557,6 @@ config HID_ICADE config HID_ITE tristate "ITE devices" - default !EXPERT help Support for ITE devices not fully compliant with HID standard. @@ -585,7 +577,6 @@ config HID_TWINHAN config HID_KENSINGTON tristate "Kensington Slimblade Trackball" - default !EXPERT help Support for Kensington Slimblade Trackball. @@ -610,8 +601,7 @@ config HID_LED config HID_LENOVO tristate "Lenovo / Thinkpad devices" - select NEW_LEDS - select LEDS_CLASS + depends on LEDS_CLASS help Support for IBM/Lenovo devices that are not fully compliant with HID standard. @@ -623,6 +613,28 @@ config HID_LENOVO - ThinkPad Compact Bluetooth Keyboard with TrackPoint (supports Fn keys) - ThinkPad Compact USB Keyboard with TrackPoint (supports Fn keys) +config HID_LENOVO_GO + tristate "HID Driver for Lenovo Legion Go Series Controllers" + depends on USB_HID + depends on LEDS_CLASS_MULTICOLOR + help + Support for Lenovo Legion Go devices with detachable controllers. + + Say Y here to include configuration interface support for the Lenovo Legion Go + and Legion Go 2 Handheld Console Controllers. Say M here to compile this + driver as a module. The module will be called hid-lenovo-go. + +config HID_LENOVO_GO_S + tristate "HID Driver for Lenovo Legion Go S Controller" + depends on USB_HID + depends on LEDS_CLASS_MULTICOLOR + help + Support for Lenovo Legion Go S Handheld Console Controller. + + Say Y here to include configuration interface support for the Lenovo Legion Go + S. Say M here to compile this driver as a module. The module will be called + hid-lenovo-go-s. + config HID_LETSKETCH tristate "Letsketch WP9620N tablets" depends on USB_HID @@ -642,7 +654,6 @@ config HID_LOGITECH depends on USB_HID depends on LEDS_CLASS depends on LEDS_CLASS_MULTICOLOR - default !EXPERT help Support for Logitech devices that are not fully compliant with HID standard. @@ -757,20 +768,17 @@ config HID_MEGAWORLD_FF config HID_REDRAGON tristate "Redragon keyboards" - default !EXPERT help Support for Redragon keyboards that need fix-ups to work properly. config HID_MICROSOFT tristate "Microsoft non-fully HID-compliant devices" - default !EXPERT select INPUT_FF_MEMLESS help Support for Microsoft devices that are not fully compliant with HID standard. config HID_MONTEREY tristate "Monterey Genius KB29E keyboard" - default !EXPERT help Support for Monterey Genius KB29E. @@ -1100,13 +1108,15 @@ config HID_SONY help Support for - * Sony PS3 6-axis controllers + * Sixaxis controllers for PS3 * Buzz controllers - * Sony PS3 Blue-ray Disk Remote Control (Bluetooth) - * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth) - * Guitar Hero Live PS3, Wii U and PS4 guitar dongles - * Guitar Hero PS3 and PC guitar dongles + * Blu-ray Disc Remote Control for PS3 + * Logitech Harmony adapter for PS3 + * Guitar Hero Live PS3, Wii U and PS4 guitars + * Guitar Hero PS3 and PC guitars + * Rock Band 1, 2 and 3 PS3 and Wii instruments * Rock Band 4 PS4 and PS5 guitars + * DJ Hero Turntable for PS3 config SONY_FF bool "Sony PS2/3/4 accessories force feedback support" @@ -1435,6 +1445,13 @@ config HID_KUNIT_TEST If in doubt, say "N". +config HID_HUAWEI + tristate "Huawei HID devices support" + depends on USB_HID + help + Support for huawei cd30 keyboard or other hid devices + that need fix-ups to work properly. + endmenu source "drivers/hid/bpf/Kconfig" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index e01838239ae6..0597fd6a4ffd 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -76,6 +76,8 @@ obj-$(CONFIG_HID_KYE) += hid-kye.o obj-$(CONFIG_HID_KYSONA) += hid-kysona.o obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o +obj-$(CONFIG_HID_LENOVO_GO) += hid-lenovo-go.o +obj-$(CONFIG_HID_LENOVO_GO_S) += hid-lenovo-go-s.o obj-$(CONFIG_HID_LETSKETCH) += hid-letsketch.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_LOGITECH) += hid-lg-g15.o @@ -152,6 +154,7 @@ obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o obj-$(CONFIG_HID_VIEWSONIC) += hid-viewsonic.o obj-$(CONFIG_HID_VRC2) += hid-vrc2.o +obj-$(CONFIG_HID_HUAWEI) += hid-huawei.o wacom-objs := wacom_wac.o wacom_sys.o obj-$(CONFIG_HID_WACOM) += wacom.o diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 50c7b45c59e3..d0130658091b 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -24,7 +24,8 @@ EXPORT_SYMBOL(hid_ops); u8 * dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data, - u32 *size, int interrupt, u64 source, bool from_bpf) + size_t *buf_size, u32 *size, int interrupt, u64 source, + bool from_bpf) { struct hid_bpf_ctx_kern ctx_kern = { .ctx = { @@ -74,6 +75,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type *size = ret; } + *buf_size = ctx_kern.ctx.allocated_size; return ctx_kern.data; } EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event); @@ -505,7 +507,7 @@ __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *b if (ret) return ret; - return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (u64)(long)ctx, true, + return hid_ops->hid_input_report(ctx->hid, type, buf, size, size, 0, (u64)(long)ctx, true, lock_already_taken); } diff --git a/drivers/hid/bpf/progs/Generic__touchpad.bpf.c b/drivers/hid/bpf/progs/Generic__touchpad.bpf.c new file mode 100644 index 000000000000..b9f2cac91724 --- /dev/null +++ b/drivers/hid/bpf/progs/Generic__touchpad.bpf.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2025 Benjamin Tissoires + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include "hid_report_helpers.h" +#include "hid_usages.h" +#include + +HID_BPF_CONFIG( + HID_DEVICE(BUS_ANY, HID_GROUP_MULTITOUCH_WIN_8, HID_VID_ANY, HID_PID_ANY), +); + +EXPORT_UDEV_PROP(HID_DIGITIZER_PAD_TYPE, 32); + +__u8 hw_req_buf[1024]; + +/* to be filled by udev-hid-bpf */ +struct hid_rdesc_descriptor HID_REPORT_DESCRIPTOR; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + struct hid_rdesc_report *pad_type_feature = NULL; + struct hid_rdesc_field *pad_type = NULL; + struct hid_rdesc_report *feature; + struct hid_bpf_ctx *hid_ctx; + char *pad_type_str = ""; + int ret; + + hid_bpf_for_each_feature_report(&HID_REPORT_DESCRIPTOR, feature) { + struct hid_rdesc_field *field; + + hid_bpf_for_each_field(feature, field) { + if (field->usage_page == HidUsagePage_Digitizers && + field->usage_id == HidUsage_Dig_PadType) { + pad_type = field; + pad_type_feature = feature; + break; + } + } + if (pad_type) + break; + } + + if (!pad_type || !pad_type_feature) { + ctx->retval = -EINVAL; + return 0; + } + + hid_ctx = hid_bpf_allocate_context(ctx->hid); + + if (!hid_ctx) + return -1; /* EPERM check */ + + hw_req_buf[0] = pad_type_feature->report_id; + + ret = hid_bpf_hw_request(hid_ctx, hw_req_buf, sizeof(hw_req_buf), + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + hid_bpf_release_context(hid_ctx); + + if (ret < 0) { + ctx->retval = ret; + return 0; + } + + ctx->retval = 0; + + switch (EXTRACT_BITS(hw_req_buf, pad_type)) { + case 0: + pad_type_str = "Clickpad"; + break; + case 1: + pad_type_str = "Pressurepad"; + break; + case 2: + pad_type_str = "Discrete"; + break; + default: + pad_type_str = "Unknown"; + } + + UDEV_PROP_SPRINTF(HID_DIGITIZER_PAD_TYPE, "%s", pad_type_str); + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/Huion__KeydialK20-Bluetooth.bpf.c b/drivers/hid/bpf/progs/Huion__KeydialK20-Bluetooth.bpf.c new file mode 100644 index 000000000000..d0769e990039 --- /dev/null +++ b/drivers/hid/bpf/progs/Huion__KeydialK20-Bluetooth.bpf.c @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2024 Red Hat, Inc + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include "hid_report_helpers.h" +#include + +#define VID_HUION 0x256C +#define PID_KEYDIAL_K20_BLUETOOTH 0x8251 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC, VID_HUION, PID_KEYDIAL_K20_BLUETOOTH), +); + +/* This is the same device as in 0010-Huion__KeydialK20 but connected via Bluetooth. + * It does not need (to support?) switching to a vendor mode so we just modify the + * existing mode. + * + * By default it exports two hidraw nodes, only the second one sends events. + * + * This is the first hidraw node which we disable: + * + * # Keydial mini-050 + * # Report descriptor length: 114 bytes + * # Bytes // Field Name Offset + * # ---------------------------------------------------------------------------------- + * # 🮥 0x05, 0x01, // Usage Page (Generic Desktop) 0 + * # 🭬 0x09, 0x0e, // Usage (System Multi-Axis Controller) 2 + * # 0xa1, 0x01, // Collection (Application) 4 + * # ┅ 0x85, 0x03, // Report ID (3) 6 + * # 🮥 0x05, 0x0d, // Usage Page (Digitizers) 8 + * # 0x75, 0x08, // Report Size (8) 10 + * # 0x95, 0x01, // Report Count (1) 12 + * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 14 + * # 🭬 0x09, 0x21, // Usage (Puck) 16 + * # 0xa1, 0x02, // Collection (Logical) 18 + * # 0x15, 0x00, // Logical Minimum (0) 20 + * # 0x25, 0x01, // Logical Maximum (1) 22 + * # 0x75, 0x01, // Report Size (1) 24 + * # 0x95, 0x01, // Report Count (1) 26 + * # 0xa1, 0x00, // Collection (Physical) 28 + * # 🮥 0x05, 0x09, // Usage Page (Button) 30 + * # 🭬 0x09, 0x01, // Usage (Button 1) 32 + * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 34 + * # 🮥 0x05, 0x0d, // Usage Page (Digitizers) 36 + * # 🭬 0x09, 0x33, // Usage (Touch) 38 + * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 40 + * # 0x95, 0x06, // Report Count (6) 42 + * # ┇ 0x81, 0x03, // Input (Cnst,Var,Abs) 44 + * # 0xa1, 0x02, // Collection (Logical) 46 + * # 🮥 0x05, 0x01, // Usage Page (Generic Desktop) 48 + * # 🭬 0x09, 0x37, // Usage (Dial) 50 + * # 0x16, 0x00, 0x80, // Logical Minimum (32768) 52 + * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 55 + * # 0x75, 0x10, // Report Size (16) 58 + * # 0x95, 0x01, // Report Count (1) 60 + * # ┇ 0x81, 0x06, // Input (Data,Var,Rel) 62 + * # 0x35, 0x00, // Physical Minimum (0) 64 + * # 0x46, 0x10, 0x0e, // Physical Maximum (3600) 66 + * # 0x15, 0x00, // Logical Minimum (0) 69 + * # 0x26, 0x10, 0x0e, // Logical Maximum (3600) 71 + * # 🭬 0x09, 0x48, // Usage (Resolution Multiplier) 74 + * # ║ 0xb1, 0x02, // Feature (Data,Var,Abs) 76 + * # 0x45, 0x00, // Physical Maximum (0) 78 + * # 0xc0, // End Collection 80 + * # 0x75, 0x08, // Report Size (8) 81 + * # 0x95, 0x01, // Report Count (1) 83 + * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 85 + * # 0x75, 0x08, // Report Size (8) 87 + * # 0x95, 0x01, // Report Count (1) 89 + * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 91 + * # 0x75, 0x08, // Report Size (8) 93 + * # 0x95, 0x01, // Report Count (1) 95 + * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 97 + * # 0x75, 0x08, // Report Size (8) 99 + * # 0x95, 0x01, // Report Count (1) 101 + * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 103 + * # 0x75, 0x08, // Report Size (8) 105 + * # 0x95, 0x01, // Report Count (1) 107 + * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 109 + * # 0xc0, // End Collection 111 + * # 0xc0, // End Collection 112 + * # 0xc0, // End Collection 113 + * R: 114 05 01 09 0e a1 01 85 03 05 0d 75 08 95 01 81 01 09 21 a1 02 15 00 25 01 75 01 95 01 a1 00 05 09 09 01 81 02 05 0d 09 33 81 02 95 06 81 03 a1 02 05 01 09 37 16 00 80 26 ff 7f 75 10 95 01 81 06 35 00 46 10 0e 15 00 26 10 0e 09 48 b1 02 45 00 c0 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 c0 c0 c0 + * N: Keydial mini-050 + * I: 5 256c 8251 + * + * The second hidraw node is what sends events: + * + * # Keydial mini-050 + * # Report descriptor length: 160 bytes + * # Bytes // Field Name Offset + * # ---------------------------------------------------------------------------------- + * # 🮥 0x05, 0x01, // Usage Page (Generic Desktop) 0 + * # 🭬 0x09, 0x06, // Usage (Keyboard) 2 + * # 0xa1, 0x01, // Collection (Application) 4 + * # ┅ 0x85, 0x01, // Report ID (1) 6 + * # 🮥 0x05, 0x07, // Usage Page (Keyboard/Keypad) 8 + * # 🭬 0x19, 0xe0, // Usage Minimum (224) 10 + * # 🭬 0x29, 0xe7, // Usage Maximum (231) 12 + * # 0x15, 0x00, // Logical Minimum (0) 14 + * # 0x25, 0x01, // Logical Maximum (1) 16 + * # 0x75, 0x01, // Report Size (1) 18 + * # 0x95, 0x08, // Report Count (8) 20 + * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 22 + * # 0x95, 0x01, // Report Count (1) 24 + * # 0x75, 0x08, // Report Size (8) 26 + * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 28 + * # 0x95, 0x05, // Report Count (5) 30 + * # 0x75, 0x01, // Report Size (1) 32 + * # 🮥 0x05, 0x08, // Usage Page (LED) 34 + * # 🭬 0x19, 0x01, // Usage Minimum (1) 36 + * # 🭬 0x29, 0x05, // Usage Maximum (5) 38 + * # ┊ 0x91, 0x02, // Output (Data,Var,Abs) 40 + * # 0x95, 0x01, // Report Count (1) 42 + * # 0x75, 0x03, // Report Size (3) 44 + * # ┊ 0x91, 0x01, // Output (Cnst,Arr,Abs) 46 + * # 0x95, 0x06, // Report Count (6) 48 + * # 0x75, 0x08, // Report Size (8) 50 + * # 0x15, 0x00, // Logical Minimum (0) 52 + * # 0x25, 0xf1, // Logical Maximum (241) 54 + * # 🮥 0x05, 0x07, // Usage Page (Keyboard/Keypad) 56 + * # 🭬 0x19, 0x00, // Usage Minimum (0) 58 + * # 🭬 0x29, 0xf1, // Usage Maximum (241) 60 + * # ┇ 0x81, 0x00, // Input (Data,Arr,Abs) 62 + * # 0xc0, // End Collection 64 + * # 🮥 0x05, 0x0c, // Usage Page (Consumer) 65 + * # 🭬 0x09, 0x01, // Usage (Consumer Control) 67 + * # 0xa1, 0x01, // Collection (Application) 69 + * # ┅ 0x85, 0x02, // Report ID (2) 71 + * # 🮥 0x05, 0x0c, // Usage Page (Consumer) 73 + * # 🭬 0x19, 0x00, // Usage Minimum (0) 75 + * # 🭬 0x2a, 0x80, 0x03, // Usage Maximum (896) 77 + * # 0x15, 0x00, // Logical Minimum (0) 80 + * # 0x26, 0x80, 0x03, // Logical Maximum (896) 82 + * # 0x75, 0x10, // Report Size (16) 85 + * # 0x95, 0x01, // Report Count (1) 87 + * # ┇ 0x81, 0x00, // Input (Data,Arr,Abs) 89 + * # 0xc0, // End Collection 91 + * # 🮥 0x05, 0x01, // Usage Page (Generic Desktop) 92 + * # 🭬 0x09, 0x02, // Usage (Mouse) 94 + * # 0xa1, 0x01, // Collection (Application) 96 + * # 🭬 0x09, 0x01, // Usage (Pointer) 98 + * # ┅ 0x85, 0x05, // Report ID (5) 100 + * # 0xa1, 0x00, // Collection (Physical) 102 + * # 🮥 0x05, 0x09, // Usage Page (Button) 104 + * # 🭬 0x19, 0x01, // Usage Minimum (1) 106 + * # 🭬 0x29, 0x05, // Usage Maximum (5) 108 + * # 0x15, 0x00, // Logical Minimum (0) 110 + * # 0x25, 0x01, // Logical Maximum (1) 112 + * # 0x95, 0x05, // Report Count (5) 114 + * # 0x75, 0x01, // Report Size (1) 116 + * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 118 + * # 0x95, 0x01, // Report Count (1) 120 + * # 0x75, 0x03, // Report Size (3) 122 + * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 124 + * # 🮥 0x05, 0x01, // Usage Page (Generic Desktop) 126 + * # 🭬 0x09, 0x30, // Usage (X) 128 + * # 🭬 0x09, 0x31, // Usage (Y) 130 + * # 0x16, 0x01, 0x80, // Logical Minimum (32769) 132 + * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 135 + * # 0x75, 0x10, // Report Size (16) 138 + * # 0x95, 0x02, // Report Count (2) 140 + * # ┇ 0x81, 0x06, // Input (Data,Var,Rel) 142 + * # 🮥 0x05, 0x01, // Usage Page (Generic Desktop) 144 + * # 🭬 0x09, 0x38, // Usage (Wheel) 146 + * # 0x15, 0x81, // Logical Minimum (129) 148 + * # 0x25, 0x7f, // Logical Maximum (127) 150 + * # 0x95, 0x01, // Report Count (1) 152 + * # 0x75, 0x08, // Report Size (8) 154 + * # ┇ 0x81, 0x06, // Input (Data,Var,Rel) 156 + * # 0xc0, // End Collection 158 + * # 0xc0, // End Collection 159 + * R: 160 05 01 09 06 a1 01 85 01 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 95 01 75 08 81 01 95 05 75 01 05 08 19 01 29 05 91 02 95 01 75 03 91 01 95 06 75 08 15 00 25 f1 05 07 19 00 29 f1 81 00 c0 05 0c 09 01 a1 01 85 02 05 0c 19 00 2a 80 03 15 00 26 80 03 75 10 95 01 81 00 c0 05 01 09 02 a1 01 09 01 85 05 a1 00 05 09 19 01 29 05 15 00 25 01 95 05 75 01 81 02 95 01 75 03 81 01 05 01 09 30 09 31 16 01 80 26 ff 7f 75 10 95 02 81 06 05 01 09 38 15 81 25 7f 95 01 75 08 81 06 c0 c0 + * N: Keydial mini-050 + * I: 5 256c 8251 + * # Report descriptor: + * # ------- Input Report ------- + * # ░ Report ID: 1 + * # ░ | Report size: 72 bits + * # ░ Bit: 8 Usage: 0007/00e0: Keyboard/Keypad / Keyboard LeftControl Logical Range: 0..=1 + * # ░ Bit: 9 Usage: 0007/00e1: Keyboard/Keypad / Keyboard LeftShift Logical Range: 0..=1 + * # ░ Bit: 10 Usage: 0007/00e2: Keyboard/Keypad / Keyboard LeftAlt Logical Range: 0..=1 + * # ░ Bit: 11 Usage: 0007/00e3: Keyboard/Keypad / Keyboard Left GUI Logical Range: 0..=1 + * # ░ Bit: 12 Usage: 0007/00e4: Keyboard/Keypad / Keyboard RightControl Logical Range: 0..=1 + * # ░ Bit: 13 Usage: 0007/00e5: Keyboard/Keypad / Keyboard RightShift Logical Range: 0..=1 + * # ░ Bit: 14 Usage: 0007/00e6: Keyboard/Keypad / Keyboard RightAlt Logical Range: 0..=1 + * # ░ Bit: 15 Usage: 0007/00e7: Keyboard/Keypad / Keyboard Right GUI Logical Range: 0..=1 + * # ░ Bits: 16..=23 ######### Padding + * # ░ Bits: 24..=71 Usages: Logical Range: 0..=241 + * # ░ 0007/0000: + * # ░ 0007/0001: Keyboard/Keypad / ErrorRollOver + * # ░ 0007/0002: Keyboard/Keypad / POSTFail + * # ░ 0007/0003: Keyboard/Keypad / ErrorUndefined + * # ░ 0007/0004: Keyboard/Keypad / Keyboard A + * # ░ ... use --full to see all usages + * # ------- Input Report ------- + * # ▒ Report ID: 2 + * # ▒ | Report size: 24 bits + * # ▒ Bits: 8..=23 Usages: Logical Range: 0..=896 + * # ▒ 000c/0000: + * # ▒ 000c/0001: Consumer / Consumer Control + * # ▒ 000c/0002: Consumer / Numeric Key Pad + * # ▒ 000c/0003: Consumer / Programmable Buttons + * # ▒ 000c/0004: Consumer / Microphone + * # ▒ ... use --full to see all usages + * # ------- Input Report ------- + * # ▞ Report ID: 5 + * # ▞ | Report size: 56 bits + * # ▞ Bit: 8 Usage: 0009/0001: Button / Button 1 Logical Range: 0..=1 + * # ▞ Bit: 9 Usage: 0009/0002: Button / Button 2 Logical Range: 0..=1 + * # ▞ Bit: 10 Usage: 0009/0003: Button / Button 3 Logical Range: 0..=1 + * # ▞ Bit: 11 Usage: 0009/0004: Button / Button 4 Logical Range: 0..=1 + * # ▞ Bit: 12 Usage: 0009/0005: Button / Button 5 Logical Range: 0..=1 + * # ▞ Bits: 13..=15 ######### Padding + * # ▞ Bits: 16..=31 Usage: 0001/0030: Generic Desktop / X Logical Range: 32769..=32767 + * # ▞ Bits: 32..=47 Usage: 0001/0031: Generic Desktop / Y Logical Range: 32769..=32767 + * # ▞ Bits: 48..=55 Usage: 0001/0038: Generic Desktop / Wheel Logical Range: 129..=127 + * # ------- Output Report ------- + * # ░ Report ID: 1 + * # ░ | Report size: 16 bits + * # ░ Bit: 8 Usage: 0008/0001: LED / Num Lock Logical Range: 0..=1 + * # ░ Bit: 9 Usage: 0008/0002: LED / Caps Lock Logical Range: 0..=1 + * # ░ Bit: 10 Usage: 0008/0003: LED / Scroll Lock Logical Range: 0..=1 + * # ░ Bit: 11 Usage: 0008/0004: LED / Compose Logical Range: 0..=1 + * # ░ Bit: 12 Usage: 0008/0005: LED / Kana Logical Range: 0..=1 + * # ░ Bits: 13..=15 ######### Padding + * ############################################################################## + * # Event nodes: + * # - /dev/input/event12: "Keydial mini-050 Keyboard" + * # - /dev/input/event14: "Keydial mini-050 Mouse" + * ############################################################################## + * # Recorded events below in format: + * # E: . [bytes ...] + * # + * + * - Report ID 1 sends keyboard shortcuts when pressing the buttons, e.g. + * + * # ░ Report ID: 1 / + * # ░ Keyboard LeftControl: 0 |Keyboard LeftShift: 0 |Keyboard LeftAlt: 0 |Keyboard Left GUI: 0 |Keyboard RightControl: 0 |Keyboard RightShift: 0 |Keyboard RightAlt: 0 |Keyboard Right GUI: 0 |<8 bits padding> |0007/0000: 0| Keyboard K: 14| 0007/0000: 0| 0007/0000: 0| 0007/0000: 0| 0007/0000: 0 + * E: 000000.000292 9 01 00 00 00 0e 00 00 00 00 + * + * - Report ID 2 sends the button inside the wheel/dial thing + * # ▒ Report ID: 2 / + * # ▒ Play/Pause: 205 + * E: 000134.347845 3 02 cd 00 + * # ▒ Report ID: 2 / + * # ▒ 000c/0000: 0 + * E: 000134.444965 3 02 00 00 + * + * - Report ID 5 sends the wheel relative events (always a double-event with the second as zero) + * # ▞ Report ID: 5 / + * # ▞ Button 1: 0 |Button 2: 0 |Button 3: 0 |Button 4: 0 |Button 5: 0 |<3 bits padding> |X: 0 |Y: 0 |Wheel: 255 + * E: 000064.859915 7 05 00 00 00 00 00 ff + * # ▞ Report ID: 5 / + * # ▞ Button 1: 0 |Button 2: 0 |Button 3: 0 |Button 4: 0 |Button 5: 0 |<3 bits padding> |X: 0 |Y: 0 |Wheel: 0 + * E: 000064.882009 7 05 00 00 00 00 00 00 + */ + +#define BT_PAD_REPORT_DESCRIPTOR_LENGTH 160 +#define BT_PUCK_REPORT_DESCRIPTOR_LENGTH 114 // This one doesn't send events +#define BT_PAD_KBD_REPORT_ID 1 +#define BT_PAD_CC_REPORT_ID 2 +#define BT_PAD_MOUSE_REPORT_ID 5 +#define BT_PAD_KBD_REPORT_LENGTH 9 +#define BT_PAD_CC_REPORT_LENGTH 3 +#define BT_PAD_MOUSE_REPORT_LENGTH 7 +#define OUR_REPORT_ID 11 /* "randomly" picked report ID for our reports */ + +__u32 last_button_state = 0; + +static const __u8 disabled_rdesc_puck[] = { + FixedSizeVendorReport(BT_PUCK_REPORT_DESCRIPTOR_LENGTH) +}; + +static const __u8 fixed_rdesc_pad[] = { + UsagePage_GenericDesktop + Usage_GD_Keypad + CollectionApplication( + // Byte 0 + ReportId(OUR_REPORT_ID) + UsagePage_Digitizers + Usage_Dig_TabletFunctionKeys + CollectionPhysical( + // Byte 1 is a button so we look like a tablet + Usage_Dig_BarrelSwitch // BTN_STYLUS, needed so we get to be a tablet pad + ReportCount(1) + ReportSize(1) + Input(Var|Abs) + ReportCount(7) // Padding + Input(Const) + // Bytes 2/3 - x/y just exist so we get to be a tablet pad + UsagePage_GenericDesktop + Usage_GD_X + Usage_GD_Y + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) + ReportCount(2) + ReportSize(8) + Input(Var|Abs) + // Bytes 4-7 are the button state for 19 buttons + pad out to u32 + // We send the first 10 buttons as buttons 1-10 which is BTN_0 -> BTN_9 + UsagePage_Button + UsageMinimum_i8(1) + UsageMaximum_i8(10) + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) + ReportCount(10) + ReportSize(1) + Input(Var|Abs) + // We send the other 9 buttons as buttons 0x31 and above -> BTN_A - BTN_TL2 + UsageMinimum_i8(0x31) + UsageMaximum_i8(0x3a) + ReportCount(9) + ReportSize(1) + Input(Var|Abs) + ReportCount(13) + ReportSize(1) + Input(Const) // padding + // Byte 8 is the wheel + UsagePage_GenericDesktop + Usage_GD_Wheel + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) + ReportCount(1) + ReportSize(8) + Input(Var|Rel) + ) + // Make sure we match our original report length + FixedSizeVendorReport(BT_PAD_KBD_REPORT_LENGTH) + ) +}; + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(k20_bt_fix_rdesc, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */); + __s32 rdesc_size = hctx->size; + + if (!data) + return 0; /* EPERM check */ + + if (rdesc_size == BT_PAD_REPORT_DESCRIPTOR_LENGTH) { + __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad)); + return sizeof(fixed_rdesc_pad); + } + if (rdesc_size == BT_PUCK_REPORT_DESCRIPTOR_LENGTH) { + // This hidraw node doesn't send anything and can be ignored + __builtin_memcpy(data, disabled_rdesc_puck, sizeof(disabled_rdesc_puck)); + return sizeof(disabled_rdesc_puck); + } + + return 0; +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(k20_bt_fix_events, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 12 /* size */); + struct pad_report { + __u8 report_id; + __u8 btn_stylus:1; + __u8 pad:7; + __u8 x; + __u8 y; + __u32 buttons; + __u8 wheel; + } __packed * pad_report = (struct pad_report *)data; + + if (!data) + return 0; /* EPERM check */ + + /* Report ID 1 - Keyboard events (button presses) */ + if (data[0] == BT_PAD_KBD_REPORT_ID) { + const __u8 button_mapping[] = { + 0x0e, /* Button 1: K */ + 0x0a, /* Button 2: G */ + 0x0f, /* Button 3: L */ + 0x4c, /* Button 4: Delete */ + 0x0c, /* Button 5: I */ + 0x07, /* Button 6: D */ + 0x05, /* Button 7: B */ + 0x08, /* Button 8: E */ + 0x16, /* Button 9: S */ + 0x1d, /* Button 10: Z */ + 0x06, /* Button 11: C */ + 0x19, /* Button 12: V */ + 0xff, /* Button 13: LeftControl */ + 0xff, /* Button 14: LeftAlt */ + 0xff, /* Button 15: LeftShift */ + 0x28, /* Button 16: Return Enter */ + 0x2c, /* Button 17: Spacebar */ + 0x11, /* Button 18: N */ + }; + + __u8 modifiers = data[1]; + __u32 buttons = 0; + + if (modifiers & 0x01) { /* Control */ + buttons |= BIT(12); + } + if (modifiers & 0x02) { /* Shift */ + buttons |= BIT(14); + } + if (modifiers & 0x04) { /* Alt */ + buttons |= BIT(13); + } + + for (int i = 4; i < BT_PAD_KBD_REPORT_LENGTH; i++) { + if (!data[i]) + break; + + for (size_t b = 0; b < ARRAY_SIZE(button_mapping); b++) { + if (data[i] != 0xff && data[i] == button_mapping[b]) { + buttons |= BIT(b); + break; + } + } + } + + last_button_state = buttons; + + pad_report->report_id = OUR_REPORT_ID; + pad_report->btn_stylus = 0; + pad_report->x = 0; + pad_report->y = 0; + pad_report->buttons = buttons; + pad_report->wheel = 0; + + return sizeof(struct pad_report); + } + + /* Report ID 2 - Consumer control events (the button inside the wheel) */ + if (data[0] == BT_PAD_CC_REPORT_ID) { + const __u8 PlayPause = 0xcd; + + if (data[1] == PlayPause) + last_button_state |= BIT(18); + else + last_button_state &= ~BIT(18); + + pad_report->report_id = OUR_REPORT_ID; + pad_report->btn_stylus = 0; + pad_report->x = 0; + pad_report->y = 0; + pad_report->buttons = last_button_state; + pad_report->wheel = 0; + + return sizeof(struct pad_report); + } + + /* Report ID 5 - Mouse events (wheel rotation) */ + if (data[0] == BT_PAD_MOUSE_REPORT_ID) { + __u8 wheel_delta = data[6]; + + pad_report->report_id = OUR_REPORT_ID; + pad_report->btn_stylus = 0; + pad_report->x = 0; + pad_report->y = 0; + pad_report->buttons = last_button_state; + pad_report->wheel = wheel_delta; + + return sizeof(struct pad_report); + } + + return 0; +} + +HID_BPF_OPS(keydial_k20_bluetooth) = { + .hid_device_event = (void *)k20_bt_fix_events, + .hid_rdesc_fixup = (void *)k20_bt_fix_rdesc, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + switch (ctx->rdesc_size) { + case BT_PAD_REPORT_DESCRIPTOR_LENGTH: + case BT_PUCK_REPORT_DESCRIPTOR_LENGTH: + ctx->retval = 0; + break; + default: + ctx->retval = -EINVAL; + } + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/Huion__KeydialK20.bpf.c b/drivers/hid/bpf/progs/Huion__KeydialK20.bpf.c index ec360d71130f..c562c2d684fe 100644 --- a/drivers/hid/bpf/progs/Huion__KeydialK20.bpf.c +++ b/drivers/hid/bpf/progs/Huion__KeydialK20.bpf.c @@ -462,7 +462,8 @@ int BPF_PROG(k20_fix_events, struct hid_bpf_ctx *hctx) __u32 buttons; __u8 wheel; } __attribute__((packed)) *pad_report; - int i, b; + int i; + size_t b; __u8 modifiers = data[1]; __u32 buttons = 0; diff --git a/drivers/hid/bpf/progs/IOGEAR__Kaliber-MMOmentum.bpf.c b/drivers/hid/bpf/progs/IOGEAR__Kaliber-MMOmentum.bpf.c index 82f1950445dd..253b96458c58 100644 --- a/drivers/hid/bpf/progs/IOGEAR__Kaliber-MMOmentum.bpf.c +++ b/drivers/hid/bpf/progs/IOGEAR__Kaliber-MMOmentum.bpf.c @@ -34,7 +34,7 @@ int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx) if (data[3] != 0x06) return 0; - for (int idx = 0; idx < ARRAY_SIZE(offsets); idx++) { + for (size_t idx = 0; idx < ARRAY_SIZE(offsets); idx++) { u8 offset = offsets[idx]; /* if Input (Cnst,Var,Abs) , make it Input (Data,Var,Abs) */ diff --git a/drivers/hid/bpf/progs/Trust__Philips-SPK6327.bpf.c b/drivers/hid/bpf/progs/Trust__Philips-SPK6327.bpf.c new file mode 100644 index 000000000000..bc7ff27eac9f --- /dev/null +++ b/drivers/hid/bpf/progs/Trust__Philips-SPK6327.bpf.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Fix for Trust Philips SPK6327 (145f:024b) + * Modifier keys report as Array (0x00) instead of Variable (0x02) + * causing LCtrl, LAlt, Super etc. to all act as LShift + */ +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include + +#define VID_TRUST 0x145F +#define PID_SPK6327 0x024B + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_TRUST, PID_SPK6327) +); + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0, 4096); + + if (!data) + return 0; + + /* Fix modifier keys: Input Array (0x00) -> Input Variable (0x02) */ + if (data[101] == 0x00) + data[101] = 0x02; + + return 0; +} + +HID_BPF_OPS(trust_spk6327) = { + .hid_rdesc_fixup = (void *)hid_fix_rdesc, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + /* Only apply to interface 1 (169 bytes) not interface 0 (62 bytes) */ + if (ctx->rdesc_size == 169) + ctx->retval = 0; + else + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/Wacom__ArtPen.bpf.c b/drivers/hid/bpf/progs/Wacom__ArtPen.bpf.c index 2da680bc4e11..ed60a660cc1a 100644 --- a/drivers/hid/bpf/progs/Wacom__ArtPen.bpf.c +++ b/drivers/hid/bpf/progs/Wacom__ArtPen.bpf.c @@ -148,7 +148,7 @@ int probe(struct hid_bpf_probe_args *ctx) { struct hid_bpf_ctx *hid_ctx; __u16 pid; - int i; + size_t i; /* get a struct hid_device to access the actual pid of the device */ hid_ctx = hid_bpf_allocate_context(ctx->hid); diff --git a/drivers/hid/bpf/progs/XPPen__DecoMini4.bpf.c b/drivers/hid/bpf/progs/XPPen__DecoMini4.bpf.c index 46d5c459d0c9..ac07216f5b67 100644 --- a/drivers/hid/bpf/progs/XPPen__DecoMini4.bpf.c +++ b/drivers/hid/bpf/progs/XPPen__DecoMini4.bpf.c @@ -173,7 +173,7 @@ int BPF_PROG(hid_device_event_xppen_deco_mini_4, struct hid_bpf_ctx *hctx) { __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 8 /* size */); __u8 button_mask = 0; - int d, b; + size_t d, b; if (!data) return 0; /* EPERM check */ diff --git a/drivers/hid/bpf/progs/hid_bpf_async.h b/drivers/hid/bpf/progs/hid_bpf_async.h index 9ab585434239..877bb7e81f03 100644 --- a/drivers/hid/bpf/progs/hid_bpf_async.h +++ b/drivers/hid/bpf/progs/hid_bpf_async.h @@ -116,15 +116,14 @@ static int hid_bpf_async_find_empty_key(void) if (!elem) return -ENOMEM; /* should never happen */ - bpf_spin_lock(&elem->lock); + { + guard(bpf_spin)(&elem->lock); - if (elem->state == HID_BPF_ASYNC_STATE_UNSET) { - elem->state = HID_BPF_ASYNC_STATE_INITIALIZING; - bpf_spin_unlock(&elem->lock); - return i; + if (elem->state == HID_BPF_ASYNC_STATE_UNSET) { + elem->state = HID_BPF_ASYNC_STATE_INITIALIZING; + return i; + } } - - bpf_spin_unlock(&elem->lock); } return -EINVAL; @@ -175,18 +174,19 @@ static int hid_bpf_async_delayed_call(struct hid_bpf_ctx *hctx, u64 milliseconds if (!elem) return -EINVAL; - bpf_spin_lock(&elem->lock); - /* The wq must be: - * - HID_BPF_ASYNC_STATE_INITIALIZED -> it's been initialized and ready to be called - * - HID_BPF_ASYNC_STATE_RUNNING -> possible re-entry from the wq itself - */ - if (elem->state != HID_BPF_ASYNC_STATE_INITIALIZED && - elem->state != HID_BPF_ASYNC_STATE_RUNNING) { - bpf_spin_unlock(&elem->lock); - return -EINVAL; + { + guard(bpf_spin)(&elem->lock); + + /* The wq must be: + * - HID_BPF_ASYNC_STATE_INITIALIZED -> it's been initialized and ready to be called + * - HID_BPF_ASYNC_STATE_RUNNING -> possible re-entry from the wq itself + */ + if (elem->state != HID_BPF_ASYNC_STATE_INITIALIZED && + elem->state != HID_BPF_ASYNC_STATE_RUNNING) + return -EINVAL; + + elem->state = HID_BPF_ASYNC_STATE_STARTING; } - elem->state = HID_BPF_ASYNC_STATE_STARTING; - bpf_spin_unlock(&elem->lock); elem->hid = hctx->hid->id; diff --git a/drivers/hid/bpf/progs/hid_bpf_helpers.h b/drivers/hid/bpf/progs/hid_bpf_helpers.h index 228f8d787567..0fd8e7d90742 100644 --- a/drivers/hid/bpf/progs/hid_bpf_helpers.h +++ b/drivers/hid/bpf/progs/hid_bpf_helpers.h @@ -7,7 +7,18 @@ #include "vmlinux.h" #include +#include #include +#include "hid_report_descriptor_helpers.h" + +/* Compiler attributes */ +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +#ifndef __maybe_unused +#define __maybe_unused __attribute__((__unused__)) +#endif extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, @@ -40,6 +51,86 @@ extern int bpf_wq_set_callback(struct bpf_wq *wq, #define HID_MAX_DESCRIPTOR_SIZE 4096 #define HID_IGNORE_EVENT -1 +/** + * Use: _cleanup_(somefunction) struct foo *bar; + */ +#define _cleanup_(_x) __attribute__((cleanup(_x))) + +/** + * Use: _release_(foo) *bar; + * + * This requires foo_releasep() to be present, use DEFINE_RELEASE_CLEANUP_FUNC. + */ +#define _release_(_type) struct _type __attribute__((cleanup(_type##_releasep))) + +/** + * Define a cleanup function for the struct type foo with a matching + * foo_release(). Use: + * DEFINE_RELEASE_CLEANUP_FUNC(foo) + * _unref_(foo) struct foo *bar; + */ +#define DEFINE_RELEASE_CLEANUP_FUNC(_type) \ + static inline void _type##_releasep(struct _type **_p) { \ + if (*_p) \ + _type##_release(*_p); \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +/* for being able to have a cleanup function */ +#define hid_bpf_ctx_release hid_bpf_release_context +DEFINE_RELEASE_CLEANUP_FUNC(hid_bpf_ctx); + +/* + * Kernel-style guard macros adapted for BPF + * Based on include/linux/cleanup.h from the Linux kernel + * + * These provide automatic lock/unlock using __attribute__((cleanup)) + * similar to how _release_() works for contexts. + */ + +/** + * DEFINE_GUARD(name, type, lock, unlock): + * Define a guard for automatic lock/unlock using the same pattern as _release_() + * @name: identifier for the guard (e.g., bpf_spin) + * @type: lock variable type (e.g., struct bpf_spin_lock) + * @lock: lock function name (e.g., bpf_spin_lock) + * @unlock: unlock function name (e.g., bpf_spin_unlock) + * + * guard(name): + * Declare and lock in one statement - lock held until end of scope + * + * Example: + * DEFINE_GUARD(bpf_spin, struct bpf_spin_lock, bpf_spin_lock, bpf_spin_unlock) + * + * void foo(struct bpf_spin_lock *lock) { + * guard(bpf_spin)(lock); + * // lock held until end of scope + * } + */ + +/* Guard helper struct - stores lock pointer for cleanup */ +#define DEFINE_GUARD(_name, _type, _lock, _unlock) \ +struct _name##_guard { \ + _type *lock; \ +}; \ +static inline void _name##_guard_cleanup(struct _name##_guard *g) { \ + if (g && g->lock) \ + _unlock(g->lock); \ +} \ +static inline struct _name##_guard _name##_guard_init(_type *l) { \ + if (l) \ + _lock(l); \ + return (struct _name##_guard){.lock = l}; \ +} \ +struct __useless_struct_to_allow_trailing_semicolon__ + +#define guard(_name) \ + struct _name##_guard COMBINE(guard, __LINE__) __attribute__((cleanup(_name##_guard_cleanup))) = \ + _name##_guard_init + +/* Define BPF spinlock guard */ +DEFINE_GUARD(bpf_spin, struct bpf_spin_lock, bpf_spin_lock, bpf_spin_unlock); + /* extracted from */ #define BUS_ANY 0x00 #define BUS_PCI 0x01 @@ -183,4 +274,234 @@ extern int bpf_wq_set_callback(struct bpf_wq *wq, _EXPAND(_ARG, __VA_ARGS__) \ } _device_ids SEC(".hid_bpf_config") + +/* Equivalency macros for bpf_htons and friends which are + * Big Endian only - HID needs little endian so these are the + * corresponding macros for that. See bpf/bpf_endian.h + */ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define __hid_bpf_le16_to_cpu(x) (x) +# define __hid_bpf_le32_to_cpu(x) (x) +# define __hid_bpf_le64_to_cpu(x) (x) +# define __hid_bpf_cpu_to_le16(x) (x) +# define __hid_bpf_cpu_to_le32(x) (x) +# define __hid_bpf_cpu_to_le64(x) (x) +# define __hid_bpf_constant_le16_to_cpu(x) (x) +# define __hid_bpf_constant_le32_to_cpu(x) (x) +# define __hid_bpf_constant_le64_to_cpu(x) (x) +# define __hid_bpf_constant_cpu_to_le16(x) (x) +# define __hid_bpf_constant_cpu_to_le32(x) (x) +# define __hid_bpf_constant_cpu_to_le64(x) (x) +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define __hid_bpf_le16_to_cpu(x) __builtin_bswap16(x) +# define __hid_bpf_le32_to_cpu(x) __builtin_bswap32(x) +# define __hid_bpf_le64_to_cpu(x) __builtin_bswap64(x) +# define __hid_bpf_cpu_to_le16(x) __builtin_bswap16(x) +# define __hid_bpf_cpu_to_le32(x) __builtin_bswap32(x) +# define __hid_bpf_cpu_to_le64(x) __builtin_bswap64(x) +# define __hid_bpf_constant_le16_to_cpu(x) __bpf_swab16(x) +# define __hid_bpf_constant_le32_to_cpu(x) __bpf_swab32(x) +# define __hid_bpf_constant_le64_to_cpu(x) __bpf_swab64(x) +# define __hid_bpf_constant_cpu_to_le16(x) __bpf_swab16(x) +# define __hid_bpf_constant_cpu_to_le32(x) __bpf_swab32(x) +# define __hid_bpf_constant_cpu_to_le64(x) __bpf_swab64(x) +#else +# error "Invalid __BYTE_ORDER__" +#endif + +#define hid_bpf_le16_to_cpu(x) \ + (__builtin_constant_p(x) ? \ + __hid_bpf_constant_le16_to_cpu(x) : __hid_bpf_le16_to_cpu(x)) + +#define hid_bpf_le32_to_cpu(x) \ + (__builtin_constant_p(x) ? \ + __hid_bpf_constant_le32_to_cpu(x) : __hid_bpf_le32_to_cpu(x)) + +#define hid_bpf_le64_to_cpu(x) \ + (__builtin_constant_p(x) ? \ + __hid_bpf_constant_le64_to_cpu(x) : __hid_bpf_le64_to_cpu(x)) + +#define hid_bpf_cpu_to_le16(x) \ + (__builtin_constant_p(x) ? \ + __hid_bpf_constant_cpu_to_le16(x) : __hid_bpf_cpu_to_le16(x)) + +#define hid_bpf_cpu_to_le32(x) \ + (__builtin_constant_p(x) ? \ + __hid_bpf_constant_cpu_to_le32(x) : __hid_bpf_cpu_to_le32(x)) + +#define hid_bpf_cpu_to_le64(x) \ + (__builtin_constant_p(x) ? \ + __hid_bpf_constant_cpu_to_le64(x) : __hid_bpf_cpu_to_le64(x)) + +#define hid_bpf_be16_to_cpu(x) bpf_ntohs(x) +#define hid_bpf_be32_to_cpu(x) bpf_ntohl(x) +#define hid_bpf_be64_to_cpu(x) bpf_be64_to_cpu(x) +#define hid_bpf_cpu_to_be16(x) bpf_htons(x) +#define hid_bpf_cpu_to_be32(x) bpf_htonl(x) +#define hid_bpf_cpu_to_be64(x) bpf_cpu_to_be64(x) + +/* + * The following macros are helpers for exporting udev properties: + * + * EXPORT_UDEV_PROP(name, len) generates: + * - a map with a single element UDEV_PROP_##name, of size len + * - a const global declaration of that len: SIZEOF_##name + * + * udev_prop_ptr(name) retrieves the data pointer behind the map. + * + * UDEV_PROP_SPRINTF(name, fmt, ...) writes data into the udev property. + * + * Can be used as such: + * EXPORT_UDEV_PROP(HID_FOO, 32); + * + * SEC("syscall") + * int probe(struct hid_bpf_probe_args *ctx) + * { + * const char *foo = "foo"; + * UDEV_PROP_SPRINTF(HID_FOO, "%s", foo); + * + * return 0; + * } + */ +#define EXPORT_UDEV_PROP(name, len) \ + const __u32 SIZEOF_##name = len; \ + struct COMBINE(udev_prop, __LINE__) { \ + __uint(type, BPF_MAP_TYPE_ARRAY); \ + __uint(max_entries, 1); \ + __type(key, __u32); \ + __type(value, __u8[len]); \ + } UDEV_PROP_##name SEC(".maps"); + +#define udev_prop_ptr(name) \ + bpf_map_lookup_elem(&UDEV_PROP_##name, &(__u32){0}) + +#define UDEV_PROP_SPRINTF(name, fmt, ...) \ + BPF_SNPRINTF(udev_prop_ptr(name), SIZEOF_##name, fmt, ##__VA_ARGS__) + +static inline __maybe_unused __u16 field_start_byte(struct hid_rdesc_field *field) +{ + return field->bits_start / 8; +} + +static inline __maybe_unused __u16 field_end_byte(struct hid_rdesc_field *field) +{ + if (!field->bits_end) + return 0; + + return (__u16)(field->bits_end - 1) / 8; +} + +static __maybe_unused __u32 extract_bits(__u8 *buffer, const size_t size, struct hid_rdesc_field *field) +{ + __s32 nbits = field->bits_end - field->bits_start; + __u32 start = field_start_byte(field); + __u32 end = field_end_byte(field); + __u8 base_shift = field->bits_start % 8; + + if (nbits <= 0 || nbits > 32 || start >= size || end >= size) + return 0; + + /* Fast path for byte-aligned standard-sized reads */ + if (base_shift == 0) { + /* 8-bit aligned read */ + if (nbits == 8 && start < size) + return buffer[start]; + + /* 16-bit aligned read - use separate variables for verifier */ + if (nbits == 16) { + __u32 off0 = start; + __u32 off1 = start + 1; + + if (off0 < size && off1 < size) { + return buffer[off0] | + ((__u32)buffer[off1] << 8); + } + } + + /* 32-bit aligned read - use separate variables for verifier */ + if (nbits == 32) { + __u32 off0 = start; + __u32 off1 = start + 1; + __u32 off2 = start + 2; + __u32 off3 = start + 3; + + if (off0 < size && off1 < size && + off2 < size && off3 < size) { + return buffer[off0] | + ((__u32)buffer[off1] << 8) | + ((__u32)buffer[off2] << 16) | + ((__u32)buffer[off3] << 24); + } + } + } + + /* General case: bit manipulation for unaligned or non-standard sizes */ + int mask = 0xffffffff >> (32 - nbits); + __u64 value = 0; + __u32 i; + + bpf_for (i, start, end + 1) { + value |= (__u64)buffer[i] << ((i - start) * 8); + } + + return (value >> base_shift) & mask; +} + +#define EXTRACT_BITS(buffer, field) extract_bits(buffer, sizeof(buffer), field) + +/* Base macro for iterating over HID arrays with bounds checking. + * Follows the bpf_for pattern from libbpf. + */ +#define __hid_bpf_for_each_array(array, num_elements, max_elements, var) \ + for ( \ + /* initialize and define destructor */ \ + struct bpf_iter_num ___it __attribute__((aligned(8), \ + cleanup(bpf_iter_num_destroy))), \ + /* ___p pointer is necessary to call bpf_iter_num_new() *once* */ \ + *___p __attribute__((unused)) = ( \ + /* always initialize iterator; if bounds fail, iterate 0 times */ \ + bpf_iter_num_new(&___it, 0, \ + (num_elements) > (max_elements) ? \ + 0 : (num_elements)), \ + /* workaround for Clang bug */ \ + (void)bpf_iter_num_destroy, (void *)0); \ + ({ \ + /* iteration step */ \ + int *___t = bpf_iter_num_next(&___it); \ + int ___i; \ + /* termination and bounds check, assign var */ \ + (___t && (___i = *___t, ___i >= 0 && ___i < (num_elements)) && \ + ((num_elements) <= (max_elements)) && \ + (var = &(array)[___i], 1)); \ + }); \ + ) + +/* Iterate over input reports in a descriptor */ +#define hid_bpf_for_each_input_report(descriptor, report_var) \ + __hid_bpf_for_each_array((descriptor)->input_reports, \ + (descriptor)->num_input_reports, \ + HID_MAX_REPORTS, report_var) + +/* Iterate over feature reports in a descriptor */ +#define hid_bpf_for_each_feature_report(descriptor, report_var) \ + __hid_bpf_for_each_array((descriptor)->feature_reports, \ + (descriptor)->num_feature_reports, \ + HID_MAX_REPORTS, report_var) + +/* Iterate over output reports in a descriptor */ +#define hid_bpf_for_each_output_report(descriptor, report_var) \ + __hid_bpf_for_each_array((descriptor)->output_reports, \ + (descriptor)->num_output_reports, \ + HID_MAX_REPORTS, report_var) + +/* Iterate over fields in a report */ +#define hid_bpf_for_each_field(report, field_var) \ + __hid_bpf_for_each_array((report)->fields, (report)->num_fields, \ + HID_MAX_FIELDS, field_var) + +/* Iterate over collections in a field */ +#define hid_bpf_for_each_collection(field, collection_var) \ + __hid_bpf_for_each_array((field)->collections, (field)->num_collections, \ + HID_MAX_COLLECTIONS, collection_var) + #endif /* __HID_BPF_HELPERS_H */ diff --git a/drivers/hid/bpf/progs/hid_report_descriptor_helpers.h b/drivers/hid/bpf/progs/hid_report_descriptor_helpers.h new file mode 100644 index 000000000000..2aed5c0a6ad4 --- /dev/null +++ b/drivers/hid/bpf/progs/hid_report_descriptor_helpers.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2022 Benjamin Tissoires + */ + +#ifndef __HID_REPORT_DESCRIPTOR_HELPERS_H +#define __HID_REPORT_DESCRIPTOR_HELPERS_H + +#include "vmlinux.h" + +/* Compiler attributes */ +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +#ifndef __maybe_unused +#define __maybe_unused __attribute__((__unused__)) +#endif + +/* Report Descriptor Structures */ +#define HID_MAX_COLLECTIONS 32 +#define HID_MAX_FIELDS 64 +#define HID_MAX_REPORTS 16 + +enum hid_rdesc_field_type { + HID_FIELD_VARIABLE = 0, + HID_FIELD_ARRAY = 1, + HID_FIELD_CONSTANT = 2, +}; + +struct hid_rdesc_collection { + __u16 usage_page; + __u16 usage_id; + __u8 collection_type; +} __packed; + +struct hid_rdesc_field { + __u8 field_type; /* enum hid_rdesc_field_type */ + __u8 num_collections; + __u16 bits_start; + __u16 bits_end; + __u16 usage_page; + union { + __u16 usage_id; /* For Variable fields */ + struct __packed { /* For Array fields */ + __u16 usage_minimum; + __u16 usage_maximum; + }; + }; + __s32 logical_minimum; + __s32 logical_maximum; + struct { + __u8 is_relative:1; /* Data is relative to previous value */ + __u8 wraps:1; /* Value wraps around (e.g., rotary encoder) */ + __u8 is_nonlinear:1; /* Non-linear relationship between logical/physical */ + __u8 has_no_preferred_state:1; /* No rest position (e.g., free-floating joystick) */ + __u8 has_null_state:1; /* Can report null/no-data values */ + __u8 is_volatile:1; /* Volatile (Output/Feature) - NOT POPULATED, always 0 */ + __u8 is_buffered_bytes:1; /* Fixed-size byte stream vs bitfield */ + __u8 reserved:1; /* Reserved for future use */ + } flags; + struct hid_rdesc_collection collections[HID_MAX_COLLECTIONS]; +} __packed; + +struct hid_rdesc_report { + __u8 report_id; /* 0 means no report ID */ + __u16 size_in_bits; + __u8 num_fields; + struct hid_rdesc_field fields[HID_MAX_FIELDS]; +} __packed; + +struct hid_rdesc_descriptor { + __u8 num_input_reports; + __u8 num_output_reports; + __u8 num_feature_reports; + struct hid_rdesc_report input_reports[HID_MAX_REPORTS]; + struct hid_rdesc_report output_reports[HID_MAX_REPORTS]; + struct hid_rdesc_report feature_reports[HID_MAX_REPORTS]; +} __packed; + +#endif /* __HID_REPORT_DESCRIPTOR_HELPERS_H */ diff --git a/drivers/hid/bpf/progs/hid_usages.h b/drivers/hid/bpf/progs/hid_usages.h new file mode 100644 index 000000000000..5469b25c9f79 --- /dev/null +++ b/drivers/hid/bpf/progs/hid_usages.h @@ -0,0 +1,2810 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2025 Red Hat, Inc + */ + +// THIS FILE IS GENERATED, DO NOT EDIT + +#pragma once + +/* ----- Generated Usage Pages and Usages ------ */ +#define HidUsagePage_GenericDesktop 0x01 +#define HidUsagePage_SimulationControls 0x02 +#define HidUsagePage_VRControls 0x03 +#define HidUsagePage_SportControls 0x04 +#define HidUsagePage_GameControls 0x05 +#define HidUsagePage_GenericDeviceControls 0x06 +#define HidUsagePage_KeyboardKeypad 0x07 +#define HidUsagePage_LED 0x08 +#define HidUsagePage_Button 0x09 +#define HidUsagePage_Ordinal 0x0a +#define HidUsagePage_TelephonyDevice 0x0b +#define HidUsagePage_Consumer 0x0c +#define HidUsagePage_Digitizers 0x0d +#define HidUsagePage_Haptics 0x0e +#define HidUsagePage_PhysicalInputDevice 0x0f +#define HidUsagePage_Unicode 0x10 +#define HidUsagePage_SoC 0x11 +#define HidUsagePage_EyeandHeadTrackers 0x12 +#define HidUsagePage_AuxiliaryDisplay 0x14 +#define HidUsagePage_Sensors 0x20 +#define HidUsagePage_MedicalInstrument 0x40 +#define HidUsagePage_BrailleDisplay 0x41 +#define HidUsagePage_LightingAndIllumination 0x59 +#define HidUsagePage_Monitor 0x80 +#define HidUsagePage_MonitorEnumerated 0x81 +#define HidUsagePage_VESAVirtualControls 0x82 +#define HidUsagePage_Power 0x84 +#define HidUsagePage_BatterySystem 0x85 +#define HidUsagePage_BarcodeScanner 0x8c +#define HidUsagePage_Scales 0x8d +#define HidUsagePage_MagneticStripeReader 0x8e +#define HidUsagePage_CameraControl 0x90 +#define HidUsagePage_Arcade 0x91 +#define HidUsagePage_FIDOAlliance 0xf1d0 + +#define HidUsage_GD_Pointer 0x01 +#define HidUsage_GD_Mouse 0x02 +#define HidUsage_GD_Joystick 0x04 +#define HidUsage_GD_Gamepad 0x05 +#define HidUsage_GD_Keyboard 0x06 +#define HidUsage_GD_Keypad 0x07 +#define HidUsage_GD_MultiaxisController 0x08 +#define HidUsage_GD_TabletPCSystemControls 0x09 +#define HidUsage_GD_WaterCoolingDevice 0x0a +#define HidUsage_GD_ComputerChassisDevice 0x0b +#define HidUsage_GD_WirelessRadioControls 0x0c +#define HidUsage_GD_PortableDeviceControl 0x0d +#define HidUsage_GD_SystemMultiAxisController 0x0e +#define HidUsage_GD_SpatialController 0x0f +#define HidUsage_GD_AssistiveControl 0x10 +#define HidUsage_GD_DeviceDock 0x11 +#define HidUsage_GD_DockableDevice 0x12 +#define HidUsage_GD_CallStateManagementControl 0x13 +#define HidUsage_GD_X 0x30 +#define HidUsage_GD_Y 0x31 +#define HidUsage_GD_Z 0x32 +#define HidUsage_GD_Rx 0x33 +#define HidUsage_GD_Ry 0x34 +#define HidUsage_GD_Rz 0x35 +#define HidUsage_GD_Slider 0x36 +#define HidUsage_GD_Dial 0x37 +#define HidUsage_GD_Wheel 0x38 +#define HidUsage_GD_HatSwitch 0x39 +#define HidUsage_GD_CountedBuffer 0x3a +#define HidUsage_GD_ByteCount 0x3b +#define HidUsage_GD_MotionWakeup 0x3c +#define HidUsage_GD_Start 0x3d +#define HidUsage_GD_Select 0x3e +#define HidUsage_GD_Vx 0x40 +#define HidUsage_GD_Vy 0x41 +#define HidUsage_GD_Vz 0x42 +#define HidUsage_GD_Vbrx 0x43 +#define HidUsage_GD_Vbry 0x44 +#define HidUsage_GD_Vbrz 0x45 +#define HidUsage_GD_Vno 0x46 +#define HidUsage_GD_FeatureNotification 0x47 +#define HidUsage_GD_ResolutionMultiplier 0x48 +#define HidUsage_GD_Qx 0x49 +#define HidUsage_GD_Qy 0x4a +#define HidUsage_GD_Qz 0x4b +#define HidUsage_GD_Qw 0x4c +#define HidUsage_GD_SystemControl 0x80 +#define HidUsage_GD_SystemPowerDown 0x81 +#define HidUsage_GD_SystemSleep 0x82 +#define HidUsage_GD_SystemWakeUp 0x83 +#define HidUsage_GD_SystemContextMenu 0x84 +#define HidUsage_GD_SystemMainMenu 0x85 +#define HidUsage_GD_SystemAppMenu 0x86 +#define HidUsage_GD_SystemMenuHelp 0x87 +#define HidUsage_GD_SystemMenuExit 0x88 +#define HidUsage_GD_SystemMenuSelect 0x89 +#define HidUsage_GD_SystemMenuRight 0x8a +#define HidUsage_GD_SystemMenuLeft 0x8b +#define HidUsage_GD_SystemMenuUp 0x8c +#define HidUsage_GD_SystemMenuDown 0x8d +#define HidUsage_GD_SystemColdRestart 0x8e +#define HidUsage_GD_SystemWarmRestart 0x8f +#define HidUsage_GD_DpadUp 0x90 +#define HidUsage_GD_DpadDown 0x91 +#define HidUsage_GD_DpadRight 0x92 +#define HidUsage_GD_DpadLeft 0x93 +#define HidUsage_GD_IndexTrigger 0x94 +#define HidUsage_GD_PalmTrigger 0x95 +#define HidUsage_GD_Thumbstick 0x96 +#define HidUsage_GD_SystemFunctionShift 0x97 +#define HidUsage_GD_SystemFunctionShiftLock 0x98 +#define HidUsage_GD_SystemFunctionShiftLockIndicator 0x99 +#define HidUsage_GD_SystemDismissNotification 0x9a +#define HidUsage_GD_SystemDoNotDisturb 0x9b +#define HidUsage_GD_SystemDock 0xa0 +#define HidUsage_GD_SystemUndock 0xa1 +#define HidUsage_GD_SystemSetup 0xa2 +#define HidUsage_GD_SystemBreak 0xa3 +#define HidUsage_GD_SystemDebuggerBreak 0xa4 +#define HidUsage_GD_ApplicationBreak 0xa5 +#define HidUsage_GD_ApplicationDebuggerBreak 0xa6 +#define HidUsage_GD_SystemSpeakerMute 0xa7 +#define HidUsage_GD_SystemHibernate 0xa8 +#define HidUsage_GD_SystemMicrophoneMute 0xa9 +#define HidUsage_GD_SystemAccessibilityBinding 0xaa +#define HidUsage_GD_SystemDisplayInvert 0xb0 +#define HidUsage_GD_SystemDisplayInternal 0xb1 +#define HidUsage_GD_SystemDisplayExternal 0xb2 +#define HidUsage_GD_SystemDisplayBoth 0xb3 +#define HidUsage_GD_SystemDisplayDual 0xb4 +#define HidUsage_GD_SystemDisplayToggleIntExtMode 0xb5 +#define HidUsage_GD_SystemDisplaySwapPrimarySecondary 0xb6 +#define HidUsage_GD_SystemDisplayToggleLCDAutoscale 0xb7 +#define HidUsage_GD_SensorZone 0xc0 +#define HidUsage_GD_RPM 0xc1 +#define HidUsage_GD_CoolantLevel 0xc2 +#define HidUsage_GD_CoolantCriticalLevel 0xc3 +#define HidUsage_GD_CoolantPump 0xc4 +#define HidUsage_GD_ChassisEnclosure 0xc5 +#define HidUsage_GD_WirelessRadioButton 0xc6 +#define HidUsage_GD_WirelessRadioLED 0xc7 +#define HidUsage_GD_WirelessRadioSliderSwitch 0xc8 +#define HidUsage_GD_SystemDisplayRotationLockButton 0xc9 +#define HidUsage_GD_SystemDisplayRotationLockSliderSwitch 0xca +#define HidUsage_GD_ControlEnable 0xcb +#define HidUsage_GD_DockableDeviceUniqueID 0xd0 +#define HidUsage_GD_DockableDeviceVendorID 0xd1 +#define HidUsage_GD_DockableDevicePrimaryUsagePage 0xd2 +#define HidUsage_GD_DockableDevicePrimaryUsageID 0xd3 +#define HidUsage_GD_DockableDeviceDockingState 0xd4 +#define HidUsage_GD_DockableDeviceDisplayOcclusion 0xd5 +#define HidUsage_GD_DockableDeviceObjectType 0xd6 +#define HidUsage_GD_CallActiveLED 0xe0 +#define HidUsage_GD_CallMuteToggle 0xe1 +#define HidUsage_GD_CallMuteLED 0xe2 +#define HidUsage_SC_FlightSimulationDevice 0x01 +#define HidUsage_SC_AutomobileSimulationDevice 0x02 +#define HidUsage_SC_TankSimulationDevice 0x03 +#define HidUsage_SC_SpaceshipSimulationDevice 0x04 +#define HidUsage_SC_SubmarineSimulationDevice 0x05 +#define HidUsage_SC_SailingSimulationDevice 0x06 +#define HidUsage_SC_MotorcycleSimulationDevice 0x07 +#define HidUsage_SC_SportsSimulationDevice 0x08 +#define HidUsage_SC_AirplaneSimulationDevice 0x09 +#define HidUsage_SC_HelicopterSimulationDevice 0x0a +#define HidUsage_SC_MagicCarpetSimulationDevice 0x0b +#define HidUsage_SC_BicycleSimulationDevice 0x0c +#define HidUsage_SC_FlightControlStick 0x20 +#define HidUsage_SC_FlightStick 0x21 +#define HidUsage_SC_CyclicControl 0x22 +#define HidUsage_SC_CyclicTrim 0x23 +#define HidUsage_SC_FlightYoke 0x24 +#define HidUsage_SC_TrackControl 0x25 +#define HidUsage_SC_Aileron 0xb0 +#define HidUsage_SC_AileronTrim 0xb1 +#define HidUsage_SC_AntiTorqueControl 0xb2 +#define HidUsage_SC_AutopilotEnable 0xb3 +#define HidUsage_SC_ChaffRelease 0xb4 +#define HidUsage_SC_CollectiveControl 0xb5 +#define HidUsage_SC_DiveBrake 0xb6 +#define HidUsage_SC_ElectronicCountermeasures 0xb7 +#define HidUsage_SC_Elevator 0xb8 +#define HidUsage_SC_ElevatorTrim 0xb9 +#define HidUsage_SC_Rudder 0xba +#define HidUsage_SC_Throttle 0xbb +#define HidUsage_SC_FlightCommunications 0xbc +#define HidUsage_SC_FlareRelease 0xbd +#define HidUsage_SC_LandingGear 0xbe +#define HidUsage_SC_ToeBrake 0xbf +#define HidUsage_SC_Trigger 0xc0 +#define HidUsage_SC_WeaponsArm 0xc1 +#define HidUsage_SC_WeaponsSelect 0xc2 +#define HidUsage_SC_WingFlaps 0xc3 +#define HidUsage_SC_Accelerator 0xc4 +#define HidUsage_SC_Brake 0xc5 +#define HidUsage_SC_Clutch 0xc6 +#define HidUsage_SC_Shifter 0xc7 +#define HidUsage_SC_Steering 0xc8 +#define HidUsage_SC_TurretDirection 0xc9 +#define HidUsage_SC_BarrelElevation 0xca +#define HidUsage_SC_DivePlane 0xcb +#define HidUsage_SC_Ballast 0xcc +#define HidUsage_SC_BicycleCrank 0xcd +#define HidUsage_SC_HandleBars 0xce +#define HidUsage_SC_FrontBrake 0xcf +#define HidUsage_SC_RearBrake 0xd0 +#define HidUsage_VRC_Belt 0x01 +#define HidUsage_VRC_BodySuit 0x02 +#define HidUsage_VRC_Flexor 0x03 +#define HidUsage_VRC_Glove 0x04 +#define HidUsage_VRC_HeadTracker 0x05 +#define HidUsage_VRC_HeadMountedDisplay 0x06 +#define HidUsage_VRC_HandTracker 0x07 +#define HidUsage_VRC_Oculometer 0x08 +#define HidUsage_VRC_Vest 0x09 +#define HidUsage_VRC_AnimatronicDevice 0x0a +#define HidUsage_VRC_StereoEnable 0x20 +#define HidUsage_VRC_DisplayEnable 0x21 +#define HidUsage_SC_BaseballBat 0x01 +#define HidUsage_SC_GolfClub 0x02 +#define HidUsage_SC_RowingMachine 0x03 +#define HidUsage_SC_Treadmill 0x04 +#define HidUsage_SC_Oar 0x30 +#define HidUsage_SC_Slope 0x31 +#define HidUsage_SC_Rate 0x32 +#define HidUsage_SC_StickSpeed 0x33 +#define HidUsage_SC_StickFaceAngle 0x34 +#define HidUsage_SC_StickHeelToe 0x35 +#define HidUsage_SC_StickFollowThrough 0x36 +#define HidUsage_SC_StickTempo 0x37 +#define HidUsage_SC_StickType 0x38 +#define HidUsage_SC_StickHeight 0x39 +#define HidUsage_SC_Putter 0x50 +#define HidUsage_SC_OneIron 0x51 +#define HidUsage_SC_TwoIron 0x52 +#define HidUsage_SC_ThreeIron 0x53 +#define HidUsage_SC_FourIron 0x54 +#define HidUsage_SC_FiveIron 0x55 +#define HidUsage_SC_SixIron 0x56 +#define HidUsage_SC_SevenIron 0x57 +#define HidUsage_SC_EightIron 0x58 +#define HidUsage_SC_NineIron 0x59 +#define HidUsage_SC_One0Iron 0x5a +#define HidUsage_SC_One1Iron 0x5b +#define HidUsage_SC_SandWedge 0x5c +#define HidUsage_SC_LoftWedge 0x5d +#define HidUsage_SC_PowerWedge 0x5e +#define HidUsage_SC_OneWood 0x5f +#define HidUsage_SC_ThreeWood 0x60 +#define HidUsage_SC_FiveWood 0x61 +#define HidUsage_SC_SevenWood 0x62 +#define HidUsage_SC_NineWood 0x63 +#define HidUsage_GC_ThreeDGameController 0x01 +#define HidUsage_GC_PinballDevice 0x02 +#define HidUsage_GC_GunDevice 0x03 +#define HidUsage_GC_PointofView 0x20 +#define HidUsage_GC_TurnRightLeft 0x21 +#define HidUsage_GC_PitchForwardBackward 0x22 +#define HidUsage_GC_RollRightLeft 0x23 +#define HidUsage_GC_MoveRightLeft 0x24 +#define HidUsage_GC_MoveForwardBackward 0x25 +#define HidUsage_GC_MoveUpDown 0x26 +#define HidUsage_GC_LeanRightLeft 0x27 +#define HidUsage_GC_LeanForwardBackward 0x28 +#define HidUsage_GC_HeightofPOV 0x29 +#define HidUsage_GC_Flipper 0x2a +#define HidUsage_GC_SecondaryFlipper 0x2b +#define HidUsage_GC_Bump 0x2c +#define HidUsage_GC_NewGame 0x2d +#define HidUsage_GC_ShootBall 0x2e +#define HidUsage_GC_Player 0x2f +#define HidUsage_GC_GunBolt 0x30 +#define HidUsage_GC_GunClip 0x31 +#define HidUsage_GC_GunSelector 0x32 +#define HidUsage_GC_GunSingleShot 0x33 +#define HidUsage_GC_GunBurst 0x34 +#define HidUsage_GC_GunAutomatic 0x35 +#define HidUsage_GC_GunSafety 0x36 +#define HidUsage_GC_GamepadFireJump 0x37 +#define HidUsage_GC_GamepadTrigger 0x39 +#define HidUsage_GC_FormfittingGamepad 0x3a +#define HidUsage_GDC_BackgroundNonuserControls 0x01 +#define HidUsage_GDC_BatteryStrength 0x20 +#define HidUsage_GDC_WirelessChannel 0x21 +#define HidUsage_GDC_WirelessID 0x22 +#define HidUsage_GDC_DiscoverWirelessControl 0x23 +#define HidUsage_GDC_SecurityCodeCharacterEntered 0x24 +#define HidUsage_GDC_SecurityCodeCharacterErased 0x25 +#define HidUsage_GDC_SecurityCodeCleared 0x26 +#define HidUsage_GDC_SequenceID 0x27 +#define HidUsage_GDC_SequenceIDReset 0x28 +#define HidUsage_GDC_RFSignalStrength 0x29 +#define HidUsage_GDC_SoftwareVersion 0x2a +#define HidUsage_GDC_ProtocolVersion 0x2b +#define HidUsage_GDC_HardwareVersion 0x2c +#define HidUsage_GDC_Major 0x2d +#define HidUsage_GDC_Minor 0x2e +#define HidUsage_GDC_Revision 0x2f +#define HidUsage_GDC_Handedness 0x30 +#define HidUsage_GDC_EitherHand 0x31 +#define HidUsage_GDC_LeftHand 0x32 +#define HidUsage_GDC_RightHand 0x33 +#define HidUsage_GDC_BothHands 0x34 +#define HidUsage_GDC_GripPoseOffset 0x40 +#define HidUsage_GDC_PointerPoseOffset 0x41 +#define HidUsage_KK_ErrorRollOver 0x01 +#define HidUsage_KK_POSTFail 0x02 +#define HidUsage_KK_ErrorUndefined 0x03 +#define HidUsage_KK_KeyboardA 0x04 +#define HidUsage_KK_KeyboardB 0x05 +#define HidUsage_KK_KeyboardC 0x06 +#define HidUsage_KK_KeyboardD 0x07 +#define HidUsage_KK_KeyboardE 0x08 +#define HidUsage_KK_KeyboardF 0x09 +#define HidUsage_KK_KeyboardG 0x0a +#define HidUsage_KK_KeyboardH 0x0b +#define HidUsage_KK_KeyboardI 0x0c +#define HidUsage_KK_KeyboardJ 0x0d +#define HidUsage_KK_KeyboardK 0x0e +#define HidUsage_KK_KeyboardL 0x0f +#define HidUsage_KK_KeyboardM 0x10 +#define HidUsage_KK_KeyboardN 0x11 +#define HidUsage_KK_KeyboardO 0x12 +#define HidUsage_KK_KeyboardP 0x13 +#define HidUsage_KK_KeyboardQ 0x14 +#define HidUsage_KK_KeyboardR 0x15 +#define HidUsage_KK_KeyboardS 0x16 +#define HidUsage_KK_KeyboardT 0x17 +#define HidUsage_KK_KeyboardU 0x18 +#define HidUsage_KK_KeyboardV 0x19 +#define HidUsage_KK_KeyboardW 0x1a +#define HidUsage_KK_KeyboardX 0x1b +#define HidUsage_KK_KeyboardY 0x1c +#define HidUsage_KK_KeyboardZ 0x1d +#define HidUsage_KK_Keyboard1andBang 0x1e +#define HidUsage_KK_Keyboard2andAt 0x1f +#define HidUsage_KK_Keyboard3andHash 0x20 +#define HidUsage_KK_Keyboard4andDollar 0x21 +#define HidUsage_KK_Keyboard5andPercent 0x22 +#define HidUsage_KK_Keyboard6andCaret 0x23 +#define HidUsage_KK_Keyboard7andAmpersand 0x24 +#define HidUsage_KK_Keyboard8andStar 0x25 +#define HidUsage_KK_Keyboard9andLeftBracket 0x26 +#define HidUsage_KK_Keyboard0andRightBracket 0x27 +#define HidUsage_KK_KeyboardReturnEnter 0x28 +#define HidUsage_KK_KeyboardEscape 0x29 +#define HidUsage_KK_KeyboardDelete 0x2a +#define HidUsage_KK_KeyboardTab 0x2b +#define HidUsage_KK_KeyboardSpacebar 0x2c +#define HidUsage_KK_KeyboardDashandUnderscore 0x2d +#define HidUsage_KK_KeyboardEqualsandPlus 0x2e +#define HidUsage_KK_KeyboardLeftBrace 0x2f +#define HidUsage_KK_KeyboardRightBrace 0x30 +#define HidUsage_KK_KeyboardBackslashandPipe 0x31 +#define HidUsage_KK_KeyboardNonUSHashandTilde 0x32 +#define HidUsage_KK_KeyboardSemiColonandColon 0x33 +#define HidUsage_KK_KeyboardLeftAposandDouble 0x34 +#define HidUsage_KK_KeyboardGraveAccentandTilde 0x35 +#define HidUsage_KK_KeyboardCommaandLessThan 0x36 +#define HidUsage_KK_KeyboardPeriodandGreaterThan 0x37 +#define HidUsage_KK_KeyboardForwardSlashandQuestionMark 0x38 +#define HidUsage_KK_KeyboardCapsLock 0x39 +#define HidUsage_KK_KeyboardF1 0x3a +#define HidUsage_KK_KeyboardF2 0x3b +#define HidUsage_KK_KeyboardF3 0x3c +#define HidUsage_KK_KeyboardF4 0x3d +#define HidUsage_KK_KeyboardF5 0x3e +#define HidUsage_KK_KeyboardF6 0x3f +#define HidUsage_KK_KeyboardF7 0x40 +#define HidUsage_KK_KeyboardF8 0x41 +#define HidUsage_KK_KeyboardF9 0x42 +#define HidUsage_KK_KeyboardF10 0x43 +#define HidUsage_KK_KeyboardF11 0x44 +#define HidUsage_KK_KeyboardF12 0x45 +#define HidUsage_KK_KeyboardPrintScreen 0x46 +#define HidUsage_KK_KeyboardScrollLock 0x47 +#define HidUsage_KK_KeyboardPause 0x48 +#define HidUsage_KK_KeyboardInsert 0x49 +#define HidUsage_KK_KeyboardHome 0x4a +#define HidUsage_KK_KeyboardPageUp 0x4b +#define HidUsage_KK_KeyboardDeleteForward 0x4c +#define HidUsage_KK_KeyboardEnd 0x4d +#define HidUsage_KK_KeyboardPageDown 0x4e +#define HidUsage_KK_KeyboardRightArrow 0x4f +#define HidUsage_KK_KeyboardLeftArrow 0x50 +#define HidUsage_KK_KeyboardDownArrow 0x51 +#define HidUsage_KK_KeyboardUpArrow 0x52 +#define HidUsage_KK_KeypadNumLockandClear 0x53 +#define HidUsage_KK_KeypadForwardSlash 0x54 +#define HidUsage_KK_KeypadStar 0x55 +#define HidUsage_KK_KeypadDash 0x56 +#define HidUsage_KK_KeypadPlus 0x57 +#define HidUsage_KK_KeypadENTER 0x58 +#define HidUsage_KK_Keypad1andEnd 0x59 +#define HidUsage_KK_Keypad2andDownArrow 0x5a +#define HidUsage_KK_Keypad3andPageDn 0x5b +#define HidUsage_KK_Keypad4andLeftArrow 0x5c +#define HidUsage_KK_Keypad5 0x5d +#define HidUsage_KK_Keypad6andRightArrow 0x5e +#define HidUsage_KK_Keypad7andHome 0x5f +#define HidUsage_KK_Keypad8andUpArrow 0x60 +#define HidUsage_KK_Keypad9andPageUp 0x61 +#define HidUsage_KK_Keypad0andInsert 0x62 +#define HidUsage_KK_KeypadPeriodandDelete 0x63 +#define HidUsage_KK_KeyboardNonUSBackslashandPipe 0x64 +#define HidUsage_KK_KeyboardApplication 0x65 +#define HidUsage_KK_KeyboardPower 0x66 +#define HidUsage_KK_KeypadEquals 0x67 +#define HidUsage_KK_KeyboardF13 0x68 +#define HidUsage_KK_KeyboardF14 0x69 +#define HidUsage_KK_KeyboardF15 0x6a +#define HidUsage_KK_KeyboardF16 0x6b +#define HidUsage_KK_KeyboardF17 0x6c +#define HidUsage_KK_KeyboardF18 0x6d +#define HidUsage_KK_KeyboardF19 0x6e +#define HidUsage_KK_KeyboardF20 0x6f +#define HidUsage_KK_KeyboardF21 0x70 +#define HidUsage_KK_KeyboardF22 0x71 +#define HidUsage_KK_KeyboardF23 0x72 +#define HidUsage_KK_KeyboardF24 0x73 +#define HidUsage_KK_KeyboardExecute 0x74 +#define HidUsage_KK_KeyboardHelp 0x75 +#define HidUsage_KK_KeyboardMenu 0x76 +#define HidUsage_KK_KeyboardSelect 0x77 +#define HidUsage_KK_KeyboardStop 0x78 +#define HidUsage_KK_KeyboardAgain 0x79 +#define HidUsage_KK_KeyboardUndo 0x7a +#define HidUsage_KK_KeyboardCut 0x7b +#define HidUsage_KK_KeyboardCopy 0x7c +#define HidUsage_KK_KeyboardPaste 0x7d +#define HidUsage_KK_KeyboardFind 0x7e +#define HidUsage_KK_KeyboardMute 0x7f +#define HidUsage_KK_KeyboardVolumeUp 0x80 +#define HidUsage_KK_KeyboardVolumeDown 0x81 +#define HidUsage_KK_KeyboardLockingCapsLock 0x82 +#define HidUsage_KK_KeyboardLockingNumLock 0x83 +#define HidUsage_KK_KeyboardLockingScrollLock 0x84 +#define HidUsage_KK_KeypadComma 0x85 +#define HidUsage_KK_KeypadEqualSign 0x86 +#define HidUsage_KK_KeyboardInternational1 0x87 +#define HidUsage_KK_KeyboardInternational2 0x88 +#define HidUsage_KK_KeyboardInternational3 0x89 +#define HidUsage_KK_KeyboardInternational4 0x8a +#define HidUsage_KK_KeyboardInternational5 0x8b +#define HidUsage_KK_KeyboardInternational6 0x8c +#define HidUsage_KK_KeyboardInternational7 0x8d +#define HidUsage_KK_KeyboardInternational8 0x8e +#define HidUsage_KK_KeyboardInternational9 0x8f +#define HidUsage_KK_KeyboardLANG1 0x90 +#define HidUsage_KK_KeyboardLANG2 0x91 +#define HidUsage_KK_KeyboardLANG3 0x92 +#define HidUsage_KK_KeyboardLANG4 0x93 +#define HidUsage_KK_KeyboardLANG5 0x94 +#define HidUsage_KK_KeyboardLANG6 0x95 +#define HidUsage_KK_KeyboardLANG7 0x96 +#define HidUsage_KK_KeyboardLANG8 0x97 +#define HidUsage_KK_KeyboardLANG9 0x98 +#define HidUsage_KK_KeyboardAlternateErase 0x99 +#define HidUsage_KK_KeyboardSysReqAttention 0x9a +#define HidUsage_KK_KeyboardCancel 0x9b +#define HidUsage_KK_KeyboardClear 0x9c +#define HidUsage_KK_KeyboardPrior 0x9d +#define HidUsage_KK_KeyboardReturn 0x9e +#define HidUsage_KK_KeyboardSeparator 0x9f +#define HidUsage_KK_KeyboardOut 0xa0 +#define HidUsage_KK_KeyboardOper 0xa1 +#define HidUsage_KK_KeyboardClearAgain 0xa2 +#define HidUsage_KK_KeyboardCrSelProps 0xa3 +#define HidUsage_KK_KeyboardExSel 0xa4 +#define HidUsage_KK_KeypadDouble0 0xb0 +#define HidUsage_KK_KeypadTriple0 0xb1 +#define HidUsage_KK_ThousandsSeparator 0xb2 +#define HidUsage_KK_DecimalSeparator 0xb3 +#define HidUsage_KK_CurrencyUnit 0xb4 +#define HidUsage_KK_CurrencySubunit 0xb5 +#define HidUsage_KK_KeypadLeftBracket 0xb6 +#define HidUsage_KK_KeypadRightBracket 0xb7 +#define HidUsage_KK_KeypadLeftBrace 0xb8 +#define HidUsage_KK_KeypadRightBrace 0xb9 +#define HidUsage_KK_KeypadTab 0xba +#define HidUsage_KK_KeypadBackspace 0xbb +#define HidUsage_KK_KeypadA 0xbc +#define HidUsage_KK_KeypadB 0xbd +#define HidUsage_KK_KeypadC 0xbe +#define HidUsage_KK_KeypadD 0xbf +#define HidUsage_KK_KeypadE 0xc0 +#define HidUsage_KK_KeypadF 0xc1 +#define HidUsage_KK_KeypadXOR 0xc2 +#define HidUsage_KK_KeypadCaret 0xc3 +#define HidUsage_KK_KeypadPercentage 0xc4 +#define HidUsage_KK_KeypadLess 0xc5 +#define HidUsage_KK_KeypadGreater 0xc6 +#define HidUsage_KK_KeypadAmpersand 0xc7 +#define HidUsage_KK_KeypadDoubleAmpersand 0xc8 +#define HidUsage_KK_KeypadBar 0xc9 +#define HidUsage_KK_KeypadDoubleBar 0xca +#define HidUsage_KK_KeypadColon 0xcb +#define HidUsage_KK_KeypadHash 0xcc +#define HidUsage_KK_KeypadSpace 0xcd +#define HidUsage_KK_KeypadAt 0xce +#define HidUsage_KK_KeypadBang 0xcf +#define HidUsage_KK_KeypadMemoryStore 0xd0 +#define HidUsage_KK_KeypadMemoryRecall 0xd1 +#define HidUsage_KK_KeypadMemoryClear 0xd2 +#define HidUsage_KK_KeypadMemoryAdd 0xd3 +#define HidUsage_KK_KeypadMemorySubtract 0xd4 +#define HidUsage_KK_KeypadMemoryMultiply 0xd5 +#define HidUsage_KK_KeypadMemoryDivide 0xd6 +#define HidUsage_KK_KeypadPlusMinus 0xd7 +#define HidUsage_KK_KeypadClear 0xd8 +#define HidUsage_KK_KeypadClearEntry 0xd9 +#define HidUsage_KK_KeypadBinary 0xda +#define HidUsage_KK_KeypadOctal 0xdb +#define HidUsage_KK_KeypadDecimal 0xdc +#define HidUsage_KK_KeypadHexadecimal 0xdd +#define HidUsage_KK_KeyboardLeftControl 0xe0 +#define HidUsage_KK_KeyboardLeftShift 0xe1 +#define HidUsage_KK_KeyboardLeftAlt 0xe2 +#define HidUsage_KK_KeyboardLeftGUI 0xe3 +#define HidUsage_KK_KeyboardRightControl 0xe4 +#define HidUsage_KK_KeyboardRightShift 0xe5 +#define HidUsage_KK_KeyboardRightAlt 0xe6 +#define HidUsage_KK_KeyboardRightGUI 0xe7 +#define HidUsage_LED_NumLock 0x01 +#define HidUsage_LED_CapsLock 0x02 +#define HidUsage_LED_ScrollLock 0x03 +#define HidUsage_LED_Compose 0x04 +#define HidUsage_LED_Kana 0x05 +#define HidUsage_LED_Power 0x06 +#define HidUsage_LED_Shift 0x07 +#define HidUsage_LED_DoNotDisturb 0x08 +#define HidUsage_LED_Mute 0x09 +#define HidUsage_LED_ToneEnable 0x0a +#define HidUsage_LED_HighCutFilter 0x0b +#define HidUsage_LED_LowCutFilter 0x0c +#define HidUsage_LED_EqualizerEnable 0x0d +#define HidUsage_LED_SoundFieldOn 0x0e +#define HidUsage_LED_SurroundOn 0x0f +#define HidUsage_LED_Repeat 0x10 +#define HidUsage_LED_Stereo 0x11 +#define HidUsage_LED_SamplingRateDetect 0x12 +#define HidUsage_LED_Spinning 0x13 +#define HidUsage_LED_CAV 0x14 +#define HidUsage_LED_CLV 0x15 +#define HidUsage_LED_RecordingFormatDetect 0x16 +#define HidUsage_LED_OffHook 0x17 +#define HidUsage_LED_Ring 0x18 +#define HidUsage_LED_MessageWaiting 0x19 +#define HidUsage_LED_DataMode 0x1a +#define HidUsage_LED_BatteryOperation 0x1b +#define HidUsage_LED_BatteryOK 0x1c +#define HidUsage_LED_BatteryLow 0x1d +#define HidUsage_LED_Speaker 0x1e +#define HidUsage_LED_Headset 0x1f +#define HidUsage_LED_Hold 0x20 +#define HidUsage_LED_Microphone 0x21 +#define HidUsage_LED_Coverage 0x22 +#define HidUsage_LED_NightMode 0x23 +#define HidUsage_LED_SendCalls 0x24 +#define HidUsage_LED_CallPickup 0x25 +#define HidUsage_LED_Conference 0x26 +#define HidUsage_LED_Standby 0x27 +#define HidUsage_LED_CameraOn 0x28 +#define HidUsage_LED_CameraOff 0x29 +#define HidUsage_LED_OnLine 0x2a +#define HidUsage_LED_OffLine 0x2b +#define HidUsage_LED_Busy 0x2c +#define HidUsage_LED_Ready 0x2d +#define HidUsage_LED_PaperOut 0x2e +#define HidUsage_LED_PaperJam 0x2f +#define HidUsage_LED_Remote 0x30 +#define HidUsage_LED_Forward 0x31 +#define HidUsage_LED_Reverse 0x32 +#define HidUsage_LED_Stop 0x33 +#define HidUsage_LED_Rewind 0x34 +#define HidUsage_LED_FastForward 0x35 +#define HidUsage_LED_Play 0x36 +#define HidUsage_LED_Pause 0x37 +#define HidUsage_LED_Record 0x38 +#define HidUsage_LED_Error 0x39 +#define HidUsage_LED_UsageSelectedIndicator 0x3a +#define HidUsage_LED_UsageInUseIndicator 0x3b +#define HidUsage_LED_UsageMultiModeIndicator 0x3c +#define HidUsage_LED_IndicatorOn 0x3d +#define HidUsage_LED_IndicatorFlash 0x3e +#define HidUsage_LED_IndicatorSlowBlink 0x3f +#define HidUsage_LED_IndicatorFastBlink 0x40 +#define HidUsage_LED_IndicatorOff 0x41 +#define HidUsage_LED_FlashOnTime 0x42 +#define HidUsage_LED_SlowBlinkOnTime 0x43 +#define HidUsage_LED_SlowBlinkOffTime 0x44 +#define HidUsage_LED_FastBlinkOnTime 0x45 +#define HidUsage_LED_FastBlinkOffTime 0x46 +#define HidUsage_LED_UsageIndicatorColor 0x47 +#define HidUsage_LED_IndicatorRed 0x48 +#define HidUsage_LED_IndicatorGreen 0x49 +#define HidUsage_LED_IndicatorAmber 0x4a +#define HidUsage_LED_GenericIndicator 0x4b +#define HidUsage_LED_SystemSuspend 0x4c +#define HidUsage_LED_ExternalPowerConnected 0x4d +#define HidUsage_LED_IndicatorBlue 0x4e +#define HidUsage_LED_IndicatorOrange 0x4f +#define HidUsage_LED_GoodStatus 0x50 +#define HidUsage_LED_WarningStatus 0x51 +#define HidUsage_LED_RGBLED 0x52 +#define HidUsage_LED_RedLEDChannel 0x53 +#define HidUsage_LED_BlueLEDChannel 0x54 +#define HidUsage_LED_GreenLEDChannel 0x55 +#define HidUsage_LED_LEDIntensity 0x56 +#define HidUsage_LED_SystemMicrophoneMute 0x57 +#define HidUsage_LED_PlayerIndicator 0x60 +#define HidUsage_LED_Player1 0x61 +#define HidUsage_LED_Player2 0x62 +#define HidUsage_LED_Player3 0x63 +#define HidUsage_LED_Player4 0x64 +#define HidUsage_LED_Player5 0x65 +#define HidUsage_LED_Player6 0x66 +#define HidUsage_LED_Player7 0x67 +#define HidUsage_LED_Player8 0x68 +#define HidUsage_TD_Phone 0x01 +#define HidUsage_TD_AnsweringMachine 0x02 +#define HidUsage_TD_MessageControls 0x03 +#define HidUsage_TD_Handset 0x04 +#define HidUsage_TD_Headset 0x05 +#define HidUsage_TD_TelephonyKeyPad 0x06 +#define HidUsage_TD_ProgrammableButton 0x07 +#define HidUsage_TD_HookSwitch 0x20 +#define HidUsage_TD_Flash 0x21 +#define HidUsage_TD_Feature 0x22 +#define HidUsage_TD_Hold 0x23 +#define HidUsage_TD_Redial 0x24 +#define HidUsage_TD_Transfer 0x25 +#define HidUsage_TD_Drop 0x26 +#define HidUsage_TD_Park 0x27 +#define HidUsage_TD_ForwardCalls 0x28 +#define HidUsage_TD_AlternateFunction 0x29 +#define HidUsage_TD_Line 0x2a +#define HidUsage_TD_SpeakerPhone 0x2b +#define HidUsage_TD_Conference 0x2c +#define HidUsage_TD_RingEnable 0x2d +#define HidUsage_TD_RingSelect 0x2e +#define HidUsage_TD_PhoneMute 0x2f +#define HidUsage_TD_CallerID 0x30 +#define HidUsage_TD_Send 0x31 +#define HidUsage_TD_SpeedDial 0x50 +#define HidUsage_TD_StoreNumber 0x51 +#define HidUsage_TD_RecallNumber 0x52 +#define HidUsage_TD_PhoneDirectory 0x53 +#define HidUsage_TD_VoiceMail 0x70 +#define HidUsage_TD_ScreenCalls 0x71 +#define HidUsage_TD_DoNotDisturb 0x72 +#define HidUsage_TD_Message 0x73 +#define HidUsage_TD_AnswerOnOff 0x74 +#define HidUsage_TD_InsideDialTone 0x90 +#define HidUsage_TD_OutsideDialTone 0x91 +#define HidUsage_TD_InsideRingTone 0x92 +#define HidUsage_TD_OutsideRingTone 0x93 +#define HidUsage_TD_PriorityRingTone 0x94 +#define HidUsage_TD_InsideRingback 0x95 +#define HidUsage_TD_PriorityRingback 0x96 +#define HidUsage_TD_LineBusyTone 0x97 +#define HidUsage_TD_ReorderTone 0x98 +#define HidUsage_TD_CallWaitingTone 0x99 +#define HidUsage_TD_ConfirmationTone1 0x9a +#define HidUsage_TD_ConfirmationTone2 0x9b +#define HidUsage_TD_TonesOff 0x9c +#define HidUsage_TD_OutsideRingback 0x9d +#define HidUsage_TD_Ringer 0x9e +#define HidUsage_TD_PhoneKey0 0xb0 +#define HidUsage_TD_PhoneKey1 0xb1 +#define HidUsage_TD_PhoneKey2 0xb2 +#define HidUsage_TD_PhoneKey3 0xb3 +#define HidUsage_TD_PhoneKey4 0xb4 +#define HidUsage_TD_PhoneKey5 0xb5 +#define HidUsage_TD_PhoneKey6 0xb6 +#define HidUsage_TD_PhoneKey7 0xb7 +#define HidUsage_TD_PhoneKey8 0xb8 +#define HidUsage_TD_PhoneKey9 0xb9 +#define HidUsage_TD_PhoneKeyStar 0xba +#define HidUsage_TD_PhoneKeyPound 0xbb +#define HidUsage_TD_PhoneKeyA 0xbc +#define HidUsage_TD_PhoneKeyB 0xbd +#define HidUsage_TD_PhoneKeyC 0xbe +#define HidUsage_TD_PhoneKeyD 0xbf +#define HidUsage_TD_PhoneCallHistoryKey 0xc0 +#define HidUsage_TD_PhoneCallerIDKey 0xc1 +#define HidUsage_TD_PhoneSettingsKey 0xc2 +#define HidUsage_TD_HostControl 0xf0 +#define HidUsage_TD_HostAvailable 0xf1 +#define HidUsage_TD_HostCallActive 0xf2 +#define HidUsage_TD_ActivateHandsetAudio 0xf3 +#define HidUsage_TD_RingType 0xf4 +#define HidUsage_TD_RedialablePhoneNumber 0xf5 +#define HidUsage_TD_StopRingTone 0xf8 +#define HidUsage_TD_PSTNRingTone 0xf9 +#define HidUsage_TD_HostRingTone 0xfa +#define HidUsage_TD_AlertSoundError 0xfb +#define HidUsage_TD_AlertSoundConfirm 0xfc +#define HidUsage_TD_AlertSoundNotification 0xfd +#define HidUsage_TD_SilentRing 0xfe +#define HidUsage_TD_EmailMessageWaiting 0x108 +#define HidUsage_TD_VoicemailMessageWaiting 0x109 +#define HidUsage_TD_HostHold 0x10a +#define HidUsage_TD_IncomingCallHistoryCount 0x110 +#define HidUsage_TD_OutgoingCallHistoryCount 0x111 +#define HidUsage_TD_IncomingCallHistory 0x112 +#define HidUsage_TD_OutgoingCallHistory 0x113 +#define HidUsage_TD_PhoneLocale 0x114 +#define HidUsage_TD_PhoneTimeSecond 0x140 +#define HidUsage_TD_PhoneTimeMinute 0x141 +#define HidUsage_TD_PhoneTimeHour 0x142 +#define HidUsage_TD_PhoneDateDay 0x143 +#define HidUsage_TD_PhoneDateMonth 0x144 +#define HidUsage_TD_PhoneDateYear 0x145 +#define HidUsage_TD_HandsetNickname 0x146 +#define HidUsage_TD_AddressBookID 0x147 +#define HidUsage_TD_CallDuration 0x14a +#define HidUsage_TD_DualModePhone 0x14b +#define HidUsage_Con_ConsumerControl 0x01 +#define HidUsage_Con_NumericKeyPad 0x02 +#define HidUsage_Con_ProgrammableButtons 0x03 +#define HidUsage_Con_Microphone 0x04 +#define HidUsage_Con_Headphone 0x05 +#define HidUsage_Con_GraphicEqualizer 0x06 +#define HidUsage_Con_Plus10 0x20 +#define HidUsage_Con_Plus100 0x21 +#define HidUsage_Con_AMPM 0x22 +#define HidUsage_Con_Power 0x30 +#define HidUsage_Con_Reset 0x31 +#define HidUsage_Con_Sleep 0x32 +#define HidUsage_Con_SleepAfter 0x33 +#define HidUsage_Con_SleepMode 0x34 +#define HidUsage_Con_Illumination 0x35 +#define HidUsage_Con_FunctionButtons 0x36 +#define HidUsage_Con_Menu 0x40 +#define HidUsage_Con_MenuPick 0x41 +#define HidUsage_Con_MenuUp 0x42 +#define HidUsage_Con_MenuDown 0x43 +#define HidUsage_Con_MenuLeft 0x44 +#define HidUsage_Con_MenuRight 0x45 +#define HidUsage_Con_MenuEscape 0x46 +#define HidUsage_Con_MenuValueIncrease 0x47 +#define HidUsage_Con_MenuValueDecrease 0x48 +#define HidUsage_Con_DataOnScreen 0x60 +#define HidUsage_Con_ClosedCaption 0x61 +#define HidUsage_Con_ClosedCaptionSelect 0x62 +#define HidUsage_Con_VCRTV 0x63 +#define HidUsage_Con_BroadcastMode 0x64 +#define HidUsage_Con_Snapshot 0x65 +#define HidUsage_Con_Still 0x66 +#define HidUsage_Con_PictureinPictureToggle 0x67 +#define HidUsage_Con_PictureinPictureSwap 0x68 +#define HidUsage_Con_RedMenuButton 0x69 +#define HidUsage_Con_GreenMenuButton 0x6a +#define HidUsage_Con_BlueMenuButton 0x6b +#define HidUsage_Con_YellowMenuButton 0x6c +#define HidUsage_Con_Aspect 0x6d +#define HidUsage_Con_ThreeDModeSelect 0x6e +#define HidUsage_Con_DisplayBrightnessIncrement 0x6f +#define HidUsage_Con_DisplayBrightnessDecrement 0x70 +#define HidUsage_Con_DisplayBrightness 0x71 +#define HidUsage_Con_DisplayBacklightToggle 0x72 +#define HidUsage_Con_DisplaySetBrightnesstoMinimum 0x73 +#define HidUsage_Con_DisplaySetBrightnesstoMaximum 0x74 +#define HidUsage_Con_DisplaySetAutoBrightness 0x75 +#define HidUsage_Con_CameraAccessEnabled 0x76 +#define HidUsage_Con_CameraAccessDisabled 0x77 +#define HidUsage_Con_CameraAccessToggle 0x78 +#define HidUsage_Con_KeyboardBrightnessIncrement 0x79 +#define HidUsage_Con_KeyboardBrightnessDecrement 0x7a +#define HidUsage_Con_KeyboardBacklightSetLevel 0x7b +#define HidUsage_Con_KeyboardBacklightOOC 0x7c +#define HidUsage_Con_KeyboardBacklightSetMinimum 0x7d +#define HidUsage_Con_KeyboardBacklightSetMaximum 0x7e +#define HidUsage_Con_KeyboardBacklightAuto 0x7f +#define HidUsage_Con_Selection 0x80 +#define HidUsage_Con_AssignSelection 0x81 +#define HidUsage_Con_ModeStep 0x82 +#define HidUsage_Con_RecallLast 0x83 +#define HidUsage_Con_EnterChannel 0x84 +#define HidUsage_Con_OrderMovie 0x85 +#define HidUsage_Con_Channel 0x86 +#define HidUsage_Con_MediaSelection 0x87 +#define HidUsage_Con_MediaSelectComputer 0x88 +#define HidUsage_Con_MediaSelectTV 0x89 +#define HidUsage_Con_MediaSelectWWW 0x8a +#define HidUsage_Con_MediaSelectDVD 0x8b +#define HidUsage_Con_MediaSelectTelephone 0x8c +#define HidUsage_Con_MediaSelectProgramGuide 0x8d +#define HidUsage_Con_MediaSelectVideoPhone 0x8e +#define HidUsage_Con_MediaSelectGames 0x8f +#define HidUsage_Con_MediaSelectMessages 0x90 +#define HidUsage_Con_MediaSelectCD 0x91 +#define HidUsage_Con_MediaSelectVCR 0x92 +#define HidUsage_Con_MediaSelectTuner 0x93 +#define HidUsage_Con_Quit 0x94 +#define HidUsage_Con_Help 0x95 +#define HidUsage_Con_MediaSelectTape 0x96 +#define HidUsage_Con_MediaSelectCable 0x97 +#define HidUsage_Con_MediaSelectSatellite 0x98 +#define HidUsage_Con_MediaSelectSecurity 0x99 +#define HidUsage_Con_MediaSelectHome 0x9a +#define HidUsage_Con_MediaSelectCall 0x9b +#define HidUsage_Con_ChannelIncrement 0x9c +#define HidUsage_Con_ChannelDecrement 0x9d +#define HidUsage_Con_MediaSelectSAP 0x9e +#define HidUsage_Con_VCRPlus 0xa0 +#define HidUsage_Con_Once 0xa1 +#define HidUsage_Con_Daily 0xa2 +#define HidUsage_Con_Weekly 0xa3 +#define HidUsage_Con_Monthly 0xa4 +#define HidUsage_Con_Play 0xb0 +#define HidUsage_Con_Pause 0xb1 +#define HidUsage_Con_Record 0xb2 +#define HidUsage_Con_FastForward 0xb3 +#define HidUsage_Con_Rewind 0xb4 +#define HidUsage_Con_ScanNextTrack 0xb5 +#define HidUsage_Con_ScanPreviousTrack 0xb6 +#define HidUsage_Con_Stop 0xb7 +#define HidUsage_Con_Eject 0xb8 +#define HidUsage_Con_RandomPlay 0xb9 +#define HidUsage_Con_SelectDisc 0xba +#define HidUsage_Con_EnterDisc 0xbb +#define HidUsage_Con_Repeat 0xbc +#define HidUsage_Con_Tracking 0xbd +#define HidUsage_Con_TrackNormal 0xbe +#define HidUsage_Con_SlowTracking 0xbf +#define HidUsage_Con_FrameForward 0xc0 +#define HidUsage_Con_FrameBack 0xc1 +#define HidUsage_Con_Mark 0xc2 +#define HidUsage_Con_ClearMark 0xc3 +#define HidUsage_Con_RepeatFromMark 0xc4 +#define HidUsage_Con_ReturnToMark 0xc5 +#define HidUsage_Con_SearchMarkForward 0xc6 +#define HidUsage_Con_SearchMarkBackwards 0xc7 +#define HidUsage_Con_CounterReset 0xc8 +#define HidUsage_Con_ShowCounter 0xc9 +#define HidUsage_Con_TrackingIncrement 0xca +#define HidUsage_Con_TrackingDecrement 0xcb +#define HidUsage_Con_StopEject 0xcc +#define HidUsage_Con_PlayPause 0xcd +#define HidUsage_Con_PlaySkip 0xce +#define HidUsage_Con_VoiceCommand 0xcf +#define HidUsage_Con_InvokeCaptureInterface 0xd0 +#define HidUsage_Con_StartorStopGameRecording 0xd1 +#define HidUsage_Con_HistoricalGameCapture 0xd2 +#define HidUsage_Con_CaptureGameScreenshot 0xd3 +#define HidUsage_Con_ShoworHideRecordingIndicator 0xd4 +#define HidUsage_Con_StartorStopMicrophoneCapture 0xd5 +#define HidUsage_Con_StartorStopCameraCapture 0xd6 +#define HidUsage_Con_StartorStopGameBroadcast 0xd7 +#define HidUsage_Con_StartorStopVoiceDictationSession 0xd8 +#define HidUsage_Con_InvokeDismissEmojiPicker 0xd9 +#define HidUsage_Con_Volume 0xe0 +#define HidUsage_Con_Balance 0xe1 +#define HidUsage_Con_Mute 0xe2 +#define HidUsage_Con_Bass 0xe3 +#define HidUsage_Con_Treble 0xe4 +#define HidUsage_Con_BassBoost 0xe5 +#define HidUsage_Con_SurroundMode 0xe6 +#define HidUsage_Con_Loudness 0xe7 +#define HidUsage_Con_MPX 0xe8 +#define HidUsage_Con_VolumeIncrement 0xe9 +#define HidUsage_Con_VolumeDecrement 0xea +#define HidUsage_Con_SpeedSelect 0xf0 +#define HidUsage_Con_PlaybackSpeed 0xf1 +#define HidUsage_Con_StandardPlay 0xf2 +#define HidUsage_Con_LongPlay 0xf3 +#define HidUsage_Con_ExtendedPlay 0xf4 +#define HidUsage_Con_Slow 0xf5 +#define HidUsage_Con_FanEnable 0x100 +#define HidUsage_Con_FanSpeed 0x101 +#define HidUsage_Con_LightEnable 0x102 +#define HidUsage_Con_LightIlluminationLevel 0x103 +#define HidUsage_Con_ClimateControlEnable 0x104 +#define HidUsage_Con_RoomTemperature 0x105 +#define HidUsage_Con_SecurityEnable 0x106 +#define HidUsage_Con_FireAlarm 0x107 +#define HidUsage_Con_PoliceAlarm 0x108 +#define HidUsage_Con_Proximity 0x109 +#define HidUsage_Con_Motion 0x10a +#define HidUsage_Con_DuressAlarm 0x10b +#define HidUsage_Con_HoldupAlarm 0x10c +#define HidUsage_Con_MedicalAlarm 0x10d +#define HidUsage_Con_BalanceRight 0x150 +#define HidUsage_Con_BalanceLeft 0x151 +#define HidUsage_Con_BassIncrement 0x152 +#define HidUsage_Con_BassDecrement 0x153 +#define HidUsage_Con_TrebleIncrement 0x154 +#define HidUsage_Con_TrebleDecrement 0x155 +#define HidUsage_Con_SpeakerSystem 0x160 +#define HidUsage_Con_ChannelLeft 0x161 +#define HidUsage_Con_ChannelRight 0x162 +#define HidUsage_Con_ChannelCenter 0x163 +#define HidUsage_Con_ChannelFront 0x164 +#define HidUsage_Con_ChannelCenterFront 0x165 +#define HidUsage_Con_ChannelSide 0x166 +#define HidUsage_Con_ChannelSurround 0x167 +#define HidUsage_Con_ChannelLowFrequencyEnhancement 0x168 +#define HidUsage_Con_ChannelTop 0x169 +#define HidUsage_Con_ChannelUnknown 0x16a +#define HidUsage_Con_Subchannel 0x170 +#define HidUsage_Con_SubchannelIncrement 0x171 +#define HidUsage_Con_SubchannelDecrement 0x172 +#define HidUsage_Con_AlternateAudioIncrement 0x173 +#define HidUsage_Con_AlternateAudioDecrement 0x174 +#define HidUsage_Con_ApplicationLaunchButtons 0x180 +#define HidUsage_Con_ALLaunchButtonConfigurationTool 0x181 +#define HidUsage_Con_ALProgrammableButtonConfiguration 0x182 +#define HidUsage_Con_ALConsumerControlConfiguration 0x183 +#define HidUsage_Con_ALWordProcessor 0x184 +#define HidUsage_Con_ALTextEditor 0x185 +#define HidUsage_Con_ALSpreadsheet 0x186 +#define HidUsage_Con_ALGraphicsEditor 0x187 +#define HidUsage_Con_ALPresentationApp 0x188 +#define HidUsage_Con_ALDatabaseApp 0x189 +#define HidUsage_Con_ALEmailReader 0x18a +#define HidUsage_Con_ALNewsreader 0x18b +#define HidUsage_Con_ALVoicemail 0x18c +#define HidUsage_Con_ALContactsAddressBook 0x18d +#define HidUsage_Con_ALCalendarSchedule 0x18e +#define HidUsage_Con_ALTaskProjectManager 0x18f +#define HidUsage_Con_ALLogJournalTimecard 0x190 +#define HidUsage_Con_ALCheckbookFinance 0x191 +#define HidUsage_Con_ALCalculator 0x192 +#define HidUsage_Con_ALAVCapturePlayback 0x193 +#define HidUsage_Con_ALLocalMachineBrowser 0x194 +#define HidUsage_Con_ALLANWANBrowser 0x195 +#define HidUsage_Con_ALInternetBrowser 0x196 +#define HidUsage_Con_ALRemoteNetworkingISPConnect 0x197 +#define HidUsage_Con_ALNetworkConference 0x198 +#define HidUsage_Con_ALNetworkChat 0x199 +#define HidUsage_Con_ALTelephonyDialer 0x19a +#define HidUsage_Con_ALLogon 0x19b +#define HidUsage_Con_ALLogoff 0x19c +#define HidUsage_Con_ALLogonLogoff 0x19d +#define HidUsage_Con_ALTerminalLockScreensaver 0x19e +#define HidUsage_Con_ALControlPanel 0x19f +#define HidUsage_Con_ALCommandLineProcessorRun 0x1a0 +#define HidUsage_Con_ALProcessTaskManager 0x1a1 +#define HidUsage_Con_ALSelectTaskApplication 0x1a2 +#define HidUsage_Con_ALNextTaskApplication 0x1a3 +#define HidUsage_Con_ALPreviousTaskApplication 0x1a4 +#define HidUsage_Con_ALPreemptiveHaltTaskApplication 0x1a5 +#define HidUsage_Con_ALIntegratedHelpCenter 0x1a6 +#define HidUsage_Con_ALDocuments 0x1a7 +#define HidUsage_Con_ALThesaurus 0x1a8 +#define HidUsage_Con_ALDictionary 0x1a9 +#define HidUsage_Con_ALDesktop 0x1aa +#define HidUsage_Con_ALSpellCheck 0x1ab +#define HidUsage_Con_ALGrammarCheck 0x1ac +#define HidUsage_Con_ALWirelessStatus 0x1ad +#define HidUsage_Con_ALKeyboardLayout 0x1ae +#define HidUsage_Con_ALVirusProtection 0x1af +#define HidUsage_Con_ALEncryption 0x1b0 +#define HidUsage_Con_ALScreenSaver 0x1b1 +#define HidUsage_Con_ALAlarms 0x1b2 +#define HidUsage_Con_ALClock 0x1b3 +#define HidUsage_Con_ALFileBrowser 0x1b4 +#define HidUsage_Con_ALPowerStatus 0x1b5 +#define HidUsage_Con_ALImageBrowser 0x1b6 +#define HidUsage_Con_ALAudioBrowser 0x1b7 +#define HidUsage_Con_ALMovieBrowser 0x1b8 +#define HidUsage_Con_ALDigitalRightsManager 0x1b9 +#define HidUsage_Con_ALDigitalWallet 0x1ba +#define HidUsage_Con_ALInstantMessaging 0x1bc +#define HidUsage_Con_ALOEMFeaturesTipsTutorialBrowser 0x1bd +#define HidUsage_Con_ALOEMHelp 0x1be +#define HidUsage_Con_ALOnlineCommunity 0x1bf +#define HidUsage_Con_ALEntertainmentContentBrowser 0x1c0 +#define HidUsage_Con_ALOnlineShoppingBrowser 0x1c1 +#define HidUsage_Con_ALSmartCardInformationHelp 0x1c2 +#define HidUsage_Con_ALMarketMonitorFinanceBrowser 0x1c3 +#define HidUsage_Con_ALCustomizedCorporateNewsBrowser 0x1c4 +#define HidUsage_Con_ALOnlineActivityBrowser 0x1c5 +#define HidUsage_Con_ALResearchSearchBrowser 0x1c6 +#define HidUsage_Con_ALAudioPlayer 0x1c7 +#define HidUsage_Con_ALMessageStatus 0x1c8 +#define HidUsage_Con_ALContactSync 0x1c9 +#define HidUsage_Con_ALNavigation 0x1ca +#define HidUsage_Con_ALContextawareDesktopAssistant 0x1cb +#define HidUsage_Con_GenericGUIApplicationControls 0x200 +#define HidUsage_Con_ACNew 0x201 +#define HidUsage_Con_ACOpen 0x202 +#define HidUsage_Con_ACClose 0x203 +#define HidUsage_Con_ACExit 0x204 +#define HidUsage_Con_ACMaximize 0x205 +#define HidUsage_Con_ACMinimize 0x206 +#define HidUsage_Con_ACSave 0x207 +#define HidUsage_Con_ACPrint 0x208 +#define HidUsage_Con_ACProperties 0x209 +#define HidUsage_Con_ACUndo 0x21a +#define HidUsage_Con_ACCopy 0x21b +#define HidUsage_Con_ACCut 0x21c +#define HidUsage_Con_ACPaste 0x21d +#define HidUsage_Con_ACSelectAll 0x21e +#define HidUsage_Con_ACFind 0x21f +#define HidUsage_Con_ACFindandReplace 0x220 +#define HidUsage_Con_ACSearch 0x221 +#define HidUsage_Con_ACGoTo 0x222 +#define HidUsage_Con_ACHome 0x223 +#define HidUsage_Con_ACBack 0x224 +#define HidUsage_Con_ACForward 0x225 +#define HidUsage_Con_ACStop 0x226 +#define HidUsage_Con_ACRefresh 0x227 +#define HidUsage_Con_ACPreviousLink 0x228 +#define HidUsage_Con_ACNextLink 0x229 +#define HidUsage_Con_ACBookmarks 0x22a +#define HidUsage_Con_ACHistory 0x22b +#define HidUsage_Con_ACSubscriptions 0x22c +#define HidUsage_Con_ACZoomIn 0x22d +#define HidUsage_Con_ACZoomOut 0x22e +#define HidUsage_Con_ACZoom 0x22f +#define HidUsage_Con_ACFullScreenView 0x230 +#define HidUsage_Con_ACNormalView 0x231 +#define HidUsage_Con_ACViewToggle 0x232 +#define HidUsage_Con_ACScrollUp 0x233 +#define HidUsage_Con_ACScrollDown 0x234 +#define HidUsage_Con_ACScroll 0x235 +#define HidUsage_Con_ACPanLeft 0x236 +#define HidUsage_Con_ACPanRight 0x237 +#define HidUsage_Con_ACPan 0x238 +#define HidUsage_Con_ACNewWindow 0x239 +#define HidUsage_Con_ACTileHorizontally 0x23a +#define HidUsage_Con_ACTileVertically 0x23b +#define HidUsage_Con_ACFormat 0x23c +#define HidUsage_Con_ACEdit 0x23d +#define HidUsage_Con_ACBold 0x23e +#define HidUsage_Con_ACItalics 0x23f +#define HidUsage_Con_ACUnderline 0x240 +#define HidUsage_Con_ACStrikethrough 0x241 +#define HidUsage_Con_ACSubscript 0x242 +#define HidUsage_Con_ACSuperscript 0x243 +#define HidUsage_Con_ACAllCaps 0x244 +#define HidUsage_Con_ACRotate 0x245 +#define HidUsage_Con_ACResize 0x246 +#define HidUsage_Con_ACFlipHorizontal 0x247 +#define HidUsage_Con_ACFlipVertical 0x248 +#define HidUsage_Con_ACMirrorHorizontal 0x249 +#define HidUsage_Con_ACMirrorVertical 0x24a +#define HidUsage_Con_ACFontSelect 0x24b +#define HidUsage_Con_ACFontColor 0x24c +#define HidUsage_Con_ACFontSize 0x24d +#define HidUsage_Con_ACJustifyLeft 0x24e +#define HidUsage_Con_ACJustifyCenterH 0x24f +#define HidUsage_Con_ACJustifyRight 0x250 +#define HidUsage_Con_ACJustifyBlockH 0x251 +#define HidUsage_Con_ACJustifyTop 0x252 +#define HidUsage_Con_ACJustifyCenterV 0x253 +#define HidUsage_Con_ACJustifyBottom 0x254 +#define HidUsage_Con_ACJustifyBlockV 0x255 +#define HidUsage_Con_ACIndentDecrease 0x256 +#define HidUsage_Con_ACIndentIncrease 0x257 +#define HidUsage_Con_ACNumberedList 0x258 +#define HidUsage_Con_ACRestartNumbering 0x259 +#define HidUsage_Con_ACBulletedList 0x25a +#define HidUsage_Con_ACPromote 0x25b +#define HidUsage_Con_ACDemote 0x25c +#define HidUsage_Con_ACYes 0x25d +#define HidUsage_Con_ACNo 0x25e +#define HidUsage_Con_ACCancel 0x25f +#define HidUsage_Con_ACCatalog 0x260 +#define HidUsage_Con_ACBuyCheckout 0x261 +#define HidUsage_Con_ACAddtoCart 0x262 +#define HidUsage_Con_ACExpand 0x263 +#define HidUsage_Con_ACExpandAll 0x264 +#define HidUsage_Con_ACCollapse 0x265 +#define HidUsage_Con_ACCollapseAll 0x266 +#define HidUsage_Con_ACPrintPreview 0x267 +#define HidUsage_Con_ACPasteSpecial 0x268 +#define HidUsage_Con_ACInsertMode 0x269 +#define HidUsage_Con_ACDelete 0x26a +#define HidUsage_Con_ACLock 0x26b +#define HidUsage_Con_ACUnlock 0x26c +#define HidUsage_Con_ACProtect 0x26d +#define HidUsage_Con_ACUnprotect 0x26e +#define HidUsage_Con_ACAttachComment 0x26f +#define HidUsage_Con_ACDeleteComment 0x270 +#define HidUsage_Con_ACViewComment 0x271 +#define HidUsage_Con_ACSelectWord 0x272 +#define HidUsage_Con_ACSelectSentence 0x273 +#define HidUsage_Con_ACSelectParagraph 0x274 +#define HidUsage_Con_ACSelectColumn 0x275 +#define HidUsage_Con_ACSelectRow 0x276 +#define HidUsage_Con_ACSelectTable 0x277 +#define HidUsage_Con_ACSelectObject 0x278 +#define HidUsage_Con_ACRedoRepeat 0x279 +#define HidUsage_Con_ACSort 0x27a +#define HidUsage_Con_ACSortAscending 0x27b +#define HidUsage_Con_ACSortDescending 0x27c +#define HidUsage_Con_ACFilter 0x27d +#define HidUsage_Con_ACSetClock 0x27e +#define HidUsage_Con_ACViewClock 0x27f +#define HidUsage_Con_ACSelectTimeZone 0x280 +#define HidUsage_Con_ACEditTimeZones 0x281 +#define HidUsage_Con_ACSetAlarm 0x282 +#define HidUsage_Con_ACClearAlarm 0x283 +#define HidUsage_Con_ACSnoozeAlarm 0x284 +#define HidUsage_Con_ACResetAlarm 0x285 +#define HidUsage_Con_ACSynchronize 0x286 +#define HidUsage_Con_ACSendReceive 0x287 +#define HidUsage_Con_ACSendTo 0x288 +#define HidUsage_Con_ACReply 0x289 +#define HidUsage_Con_ACReplyAll 0x28a +#define HidUsage_Con_ACForwardMsg 0x28b +#define HidUsage_Con_ACSend 0x28c +#define HidUsage_Con_ACAttachFile 0x28d +#define HidUsage_Con_ACUpload 0x28e +#define HidUsage_Con_ACDownloadSaveTargetAs 0x28f +#define HidUsage_Con_ACSetBorders 0x290 +#define HidUsage_Con_ACInsertRow 0x291 +#define HidUsage_Con_ACInsertColumn 0x292 +#define HidUsage_Con_ACInsertFile 0x293 +#define HidUsage_Con_ACInsertPicture 0x294 +#define HidUsage_Con_ACInsertObject 0x295 +#define HidUsage_Con_ACInsertSymbol 0x296 +#define HidUsage_Con_ACSaveandClose 0x297 +#define HidUsage_Con_ACRename 0x298 +#define HidUsage_Con_ACMerge 0x299 +#define HidUsage_Con_ACSplit 0x29a +#define HidUsage_Con_ACDisributeHorizontally 0x29b +#define HidUsage_Con_ACDistributeVertically 0x29c +#define HidUsage_Con_ACNextKeyboardLayoutSelect 0x29d +#define HidUsage_Con_ACNavigationGuidance 0x29e +#define HidUsage_Con_ACDesktopShowAllWindows 0x29f +#define HidUsage_Con_ACSoftKeyLeft 0x2a0 +#define HidUsage_Con_ACSoftKeyRight 0x2a1 +#define HidUsage_Con_ACDesktopShowAllApplications 0x2a2 +#define HidUsage_Con_ACIdleKeepAlive 0x2b0 +#define HidUsage_Con_ExtendedKeyboardAttributesCollection 0x2c0 +#define HidUsage_Con_KeyboardFormFactor 0x2c1 +#define HidUsage_Con_KeyboardKeyType 0x2c2 +#define HidUsage_Con_KeyboardPhysicalLayout 0x2c3 +#define HidUsage_Con_VendorSpecificKeyboardPhysicalLayout 0x2c4 +#define HidUsage_Con_KeyboardIETFLanguageTagIndex 0x2c5 +#define HidUsage_Con_ImplementedKeyboardInputAssistControls 0x2c6 +#define HidUsage_Con_KeyboardInputAssistPrevious 0x2c7 +#define HidUsage_Con_KeyboardInputAssistNext 0x2c8 +#define HidUsage_Con_KeyboardInputAssistPreviousGroup 0x2c9 +#define HidUsage_Con_KeyboardInputAssistNextGroup 0x2ca +#define HidUsage_Con_KeyboardInputAssistAccept 0x2cb +#define HidUsage_Con_KeyboardInputAssistCancel 0x2cc +#define HidUsage_Con_PrivacyScreenToggle 0x2d0 +#define HidUsage_Con_PrivacyScreenLevelDecrement 0x2d1 +#define HidUsage_Con_PrivacyScreenLevelIncrement 0x2d2 +#define HidUsage_Con_PrivacyScreenLevelMinimum 0x2d3 +#define HidUsage_Con_PrivacyScreenLevelMaximum 0x2d4 +#define HidUsage_Con_ContactEdited 0x500 +#define HidUsage_Con_ContactAdded 0x501 +#define HidUsage_Con_ContactRecordActive 0x502 +#define HidUsage_Con_ContactIndex 0x503 +#define HidUsage_Con_ContactNickname 0x504 +#define HidUsage_Con_ContactFirstName 0x505 +#define HidUsage_Con_ContactLastName 0x506 +#define HidUsage_Con_ContactFullName 0x507 +#define HidUsage_Con_ContactPhoneNumberPersonal 0x508 +#define HidUsage_Con_ContactPhoneNumberBusiness 0x509 +#define HidUsage_Con_ContactPhoneNumberMobile 0x50a +#define HidUsage_Con_ContactPhoneNumberPager 0x50b +#define HidUsage_Con_ContactPhoneNumberFax 0x50c +#define HidUsage_Con_ContactPhoneNumberOther 0x50d +#define HidUsage_Con_ContactEmailPersonal 0x50e +#define HidUsage_Con_ContactEmailBusiness 0x50f +#define HidUsage_Con_ContactEmailOther 0x510 +#define HidUsage_Con_ContactEmailMain 0x511 +#define HidUsage_Con_ContactSpeedDialNumber 0x512 +#define HidUsage_Con_ContactStatusFlag 0x513 +#define HidUsage_Con_ContactMisc 0x514 +#define HidUsage_Dig_Digitizer 0x01 +#define HidUsage_Dig_Pen 0x02 +#define HidUsage_Dig_LightPen 0x03 +#define HidUsage_Dig_TouchScreen 0x04 +#define HidUsage_Dig_TouchPad 0x05 +#define HidUsage_Dig_Whiteboard 0x06 +#define HidUsage_Dig_CoordinateMeasuringMachine 0x07 +#define HidUsage_Dig_ThreeDDigitizer 0x08 +#define HidUsage_Dig_StereoPlotter 0x09 +#define HidUsage_Dig_ArticulatedArm 0x0a +#define HidUsage_Dig_Armature 0x0b +#define HidUsage_Dig_MultiplePointDigitizer 0x0c +#define HidUsage_Dig_FreeSpaceWand 0x0d +#define HidUsage_Dig_DeviceConfiguration 0x0e +#define HidUsage_Dig_CapacitiveHeatMapDigitizer 0x0f +#define HidUsage_Dig_Stylus 0x20 +#define HidUsage_Dig_Puck 0x21 +#define HidUsage_Dig_Finger 0x22 +#define HidUsage_Dig_Devicesettings 0x23 +#define HidUsage_Dig_CharacterGesture 0x24 +#define HidUsage_Dig_TipPressure 0x30 +#define HidUsage_Dig_BarrelPressure 0x31 +#define HidUsage_Dig_InRange 0x32 +#define HidUsage_Dig_Touch 0x33 +#define HidUsage_Dig_Untouch 0x34 +#define HidUsage_Dig_Tap 0x35 +#define HidUsage_Dig_Quality 0x36 +#define HidUsage_Dig_DataValid 0x37 +#define HidUsage_Dig_TransducerIndex 0x38 +#define HidUsage_Dig_TabletFunctionKeys 0x39 +#define HidUsage_Dig_ProgramChangeKeys 0x3a +#define HidUsage_Dig_BatteryStrength 0x3b +#define HidUsage_Dig_Invert 0x3c +#define HidUsage_Dig_XTilt 0x3d +#define HidUsage_Dig_YTilt 0x3e +#define HidUsage_Dig_Azimuth 0x3f +#define HidUsage_Dig_Altitude 0x40 +#define HidUsage_Dig_Twist 0x41 +#define HidUsage_Dig_TipSwitch 0x42 +#define HidUsage_Dig_SecondaryTipSwitch 0x43 +#define HidUsage_Dig_BarrelSwitch 0x44 +#define HidUsage_Dig_Eraser 0x45 +#define HidUsage_Dig_TabletPick 0x46 +#define HidUsage_Dig_TouchValid 0x47 +#define HidUsage_Dig_Width 0x48 +#define HidUsage_Dig_Height 0x49 +#define HidUsage_Dig_ContactIdentifier 0x51 +#define HidUsage_Dig_DeviceMode 0x52 +#define HidUsage_Dig_DeviceIdentifier 0x53 +#define HidUsage_Dig_ContactCount 0x54 +#define HidUsage_Dig_ContactCountMaximum 0x55 +#define HidUsage_Dig_ScanTime 0x56 +#define HidUsage_Dig_SurfaceSwitch 0x57 +#define HidUsage_Dig_ButtonSwitch 0x58 +#define HidUsage_Dig_PadType 0x59 +#define HidUsage_Dig_SecondaryBarrelSwitch 0x5a +#define HidUsage_Dig_TransducerSerialNumber 0x5b +#define HidUsage_Dig_PreferredColor 0x5c +#define HidUsage_Dig_PreferredColorisLocked 0x5d +#define HidUsage_Dig_PreferredLineWidth 0x5e +#define HidUsage_Dig_PreferredLineWidthisLocked 0x5f +#define HidUsage_Dig_LatencyMode 0x60 +#define HidUsage_Dig_GestureCharacterQuality 0x61 +#define HidUsage_Dig_CharacterGestureDataLength 0x62 +#define HidUsage_Dig_CharacterGestureData 0x63 +#define HidUsage_Dig_GestureCharacterEncoding 0x64 +#define HidUsage_Dig_UTF8CharacterGestureEncoding 0x65 +#define HidUsage_Dig_UTF16LittleEndianCharacterGestureEncoding 0x66 +#define HidUsage_Dig_UTF16BigEndianCharacterGestureEncoding 0x67 +#define HidUsage_Dig_UTF32LittleEndianCharacterGestureEncoding 0x68 +#define HidUsage_Dig_UTF32BigEndianCharacterGestureEncoding 0x69 +#define HidUsage_Dig_CapacitiveHeatMapProtocolVendorID 0x6a +#define HidUsage_Dig_CapacitiveHeatMapProtocolVersion 0x6b +#define HidUsage_Dig_CapacitiveHeatMapFrameData 0x6c +#define HidUsage_Dig_GestureCharacterEnable 0x6d +#define HidUsage_Dig_TransducerSerialNumberPart2 0x6e +#define HidUsage_Dig_NoPreferredColor 0x6f +#define HidUsage_Dig_PreferredLineStyle 0x70 +#define HidUsage_Dig_PreferredLineStyleisLocked 0x71 +#define HidUsage_Dig_Ink 0x72 +#define HidUsage_Dig_Pencil 0x73 +#define HidUsage_Dig_Highlighter 0x74 +#define HidUsage_Dig_ChiselMarker 0x75 +#define HidUsage_Dig_Brush 0x76 +#define HidUsage_Dig_NoPreference 0x77 +#define HidUsage_Dig_DigitizerDiagnostic 0x80 +#define HidUsage_Dig_DigitizerError 0x81 +#define HidUsage_Dig_ErrNormalStatus 0x82 +#define HidUsage_Dig_ErrTransducersExceeded 0x83 +#define HidUsage_Dig_ErrFullTransFeaturesUnavailable 0x84 +#define HidUsage_Dig_ErrChargeLow 0x85 +#define HidUsage_Dig_TransducerSoftwareInfo 0x90 +#define HidUsage_Dig_TransducerVendorId 0x91 +#define HidUsage_Dig_TransducerProductId 0x92 +#define HidUsage_Dig_DeviceSupportedProtocols 0x93 +#define HidUsage_Dig_TransducerSupportedProtocols 0x94 +#define HidUsage_Dig_NoProtocol 0x95 +#define HidUsage_Dig_WacomAESProtocol 0x96 +#define HidUsage_Dig_USIProtocol 0x97 +#define HidUsage_Dig_MicrosoftPenProtocol 0x98 +#define HidUsage_Dig_SupportedReportRates 0xa0 +#define HidUsage_Dig_ReportRate 0xa1 +#define HidUsage_Dig_TransducerConnected 0xa2 +#define HidUsage_Dig_SwitchDisabled 0xa3 +#define HidUsage_Dig_SwitchUnimplemented 0xa4 +#define HidUsage_Dig_TransducerSwitches 0xa5 +#define HidUsage_Dig_TransducerIndexSelector 0xa6 +#define HidUsage_Dig_ButtonPressThreshold 0xb0 +#define HidUsage_Hap_SimpleHapticController 0x01 +#define HidUsage_Hap_WaveformList 0x10 +#define HidUsage_Hap_DurationList 0x11 +#define HidUsage_Hap_AutoTrigger 0x20 +#define HidUsage_Hap_ManualTrigger 0x21 +#define HidUsage_Hap_AutoTriggerAssociatedControl 0x22 +#define HidUsage_Hap_Intensity 0x23 +#define HidUsage_Hap_RepeatCount 0x24 +#define HidUsage_Hap_RetriggerPeriod 0x25 +#define HidUsage_Hap_WaveformVendorPage 0x26 +#define HidUsage_Hap_WaveformVendorID 0x27 +#define HidUsage_Hap_WaveformCutoffTime 0x28 +#define HidUsage_Hap_WaveformNone 0x1001 +#define HidUsage_Hap_WaveformStop 0x1002 +#define HidUsage_Hap_WaveformClick 0x1003 +#define HidUsage_Hap_WaveformBuzzContinuous 0x1004 +#define HidUsage_Hap_WaveformRumbleContinuous 0x1005 +#define HidUsage_Hap_WaveformPress 0x1006 +#define HidUsage_Hap_WaveformRelease 0x1007 +#define HidUsage_Hap_WaveformHover 0x1008 +#define HidUsage_Hap_WaveformSuccess 0x1009 +#define HidUsage_Hap_WaveformError 0x100a +#define HidUsage_Hap_WaveformInkContinuous 0x100b +#define HidUsage_Hap_WaveformPencilContinuous 0x100c +#define HidUsage_Hap_WaveformMarkerContinuous 0x100d +#define HidUsage_Hap_WaveformChiselMarkerContinuous 0x100e +#define HidUsage_Hap_WaveformBrushContinuous 0x100f +#define HidUsage_Hap_WaveformEraserContinuous 0x1010 +#define HidUsage_Hap_WaveformSparkleContinuous 0x1011 +#define HidUsage_PID_PhysicalInputDevice 0x01 +#define HidUsage_PID_Normal 0x20 +#define HidUsage_PID_SetEffectReport 0x21 +#define HidUsage_PID_EffectParameterBlockIndex 0x22 +#define HidUsage_PID_ParameterBlockOffset 0x23 +#define HidUsage_PID_ROMFlag 0x24 +#define HidUsage_PID_EffectType 0x25 +#define HidUsage_PID_ETConstantForce 0x26 +#define HidUsage_PID_ETRamp 0x27 +#define HidUsage_PID_ETCustomForce 0x28 +#define HidUsage_PID_ETSquare 0x30 +#define HidUsage_PID_ETSine 0x31 +#define HidUsage_PID_ETTriangle 0x32 +#define HidUsage_PID_ETSawtoothUp 0x33 +#define HidUsage_PID_ETSawtoothDown 0x34 +#define HidUsage_PID_ETSpring 0x40 +#define HidUsage_PID_ETDamper 0x41 +#define HidUsage_PID_ETInertia 0x42 +#define HidUsage_PID_ETFriction 0x43 +#define HidUsage_PID_Duration 0x50 +#define HidUsage_PID_SamplePeriod 0x51 +#define HidUsage_PID_Gain 0x52 +#define HidUsage_PID_TriggerButton 0x53 +#define HidUsage_PID_TriggerRepeatInterval 0x54 +#define HidUsage_PID_AxesEnable 0x55 +#define HidUsage_PID_DirectionEnable 0x56 +#define HidUsage_PID_Direction 0x57 +#define HidUsage_PID_TypeSpecificBlockOffset 0x58 +#define HidUsage_PID_BlockType 0x59 +#define HidUsage_PID_SetEnvelopeReport 0x5a +#define HidUsage_PID_AttackLevel 0x5b +#define HidUsage_PID_AttackTime 0x5c +#define HidUsage_PID_FadeLevel 0x5d +#define HidUsage_PID_FadeTime 0x5e +#define HidUsage_PID_SetConditionReport 0x5f +#define HidUsage_PID_CenterPointOffset 0x60 +#define HidUsage_PID_PositiveCoefficient 0x61 +#define HidUsage_PID_NegativeCoefficient 0x62 +#define HidUsage_PID_PositiveSaturation 0x63 +#define HidUsage_PID_NegativeSaturation 0x64 +#define HidUsage_PID_DeadBand 0x65 +#define HidUsage_PID_DownloadForceSample 0x66 +#define HidUsage_PID_IsochCustomForceEnable 0x67 +#define HidUsage_PID_CustomForceDataReport 0x68 +#define HidUsage_PID_CustomForceData 0x69 +#define HidUsage_PID_CustomForceVendorDefinedData 0x6a +#define HidUsage_PID_SetCustomForceReport 0x6b +#define HidUsage_PID_CustomForceDataOffset 0x6c +#define HidUsage_PID_SampleCount 0x6d +#define HidUsage_PID_SetPeriodicReport 0x6e +#define HidUsage_PID_Offset 0x6f +#define HidUsage_PID_Magnitude 0x70 +#define HidUsage_PID_Phase 0x71 +#define HidUsage_PID_Period 0x72 +#define HidUsage_PID_SetConstantForceReport 0x73 +#define HidUsage_PID_SetRampForceReport 0x74 +#define HidUsage_PID_RampStart 0x75 +#define HidUsage_PID_RampEnd 0x76 +#define HidUsage_PID_EffectOperationReport 0x77 +#define HidUsage_PID_EffectOperation 0x78 +#define HidUsage_PID_OpEffectStart 0x79 +#define HidUsage_PID_OpEffectStartSolo 0x7a +#define HidUsage_PID_OpEffectStop 0x7b +#define HidUsage_PID_LoopCount 0x7c +#define HidUsage_PID_DeviceGainReport 0x7d +#define HidUsage_PID_DeviceGain 0x7e +#define HidUsage_PID_ParameterBlockPoolsReport 0x7f +#define HidUsage_PID_RAMPoolSize 0x80 +#define HidUsage_PID_ROMPoolSize 0x81 +#define HidUsage_PID_ROMEffectBlockCount 0x82 +#define HidUsage_PID_SimultaneousEffectsMax 0x83 +#define HidUsage_PID_PoolAlignment 0x84 +#define HidUsage_PID_ParameterBlockMoveReport 0x85 +#define HidUsage_PID_MoveSource 0x86 +#define HidUsage_PID_MoveDestination 0x87 +#define HidUsage_PID_MoveLength 0x88 +#define HidUsage_PID_EffectParameterBlockLoadReport 0x89 +#define HidUsage_PID_EffectParameterBlockLoadStatus 0x8b +#define HidUsage_PID_BlockLoadSuccess 0x8c +#define HidUsage_PID_BlockLoadFull 0x8d +#define HidUsage_PID_BlockLoadError 0x8e +#define HidUsage_PID_BlockHandle 0x8f +#define HidUsage_PID_EffectParameterBlockFreeReport 0x90 +#define HidUsage_PID_TypeSpecificBlockHandle 0x91 +#define HidUsage_PID_PIDStateReport 0x92 +#define HidUsage_PID_EffectPlaying 0x94 +#define HidUsage_PID_PIDDeviceControlReport 0x95 +#define HidUsage_PID_PIDDeviceControl 0x96 +#define HidUsage_PID_DCEnableActuators 0x97 +#define HidUsage_PID_DCDisableActuators 0x98 +#define HidUsage_PID_DCStopAllEffects 0x99 +#define HidUsage_PID_DCReset 0x9a +#define HidUsage_PID_DCPause 0x9b +#define HidUsage_PID_DCContinue 0x9c +#define HidUsage_PID_DevicePaused 0x9f +#define HidUsage_PID_ActuatorsEnabled 0xa0 +#define HidUsage_PID_SafetySwitch 0xa4 +#define HidUsage_PID_ActuatorOverrideSwitch 0xa5 +#define HidUsage_PID_ActuatorPower 0xa6 +#define HidUsage_PID_StartDelay 0xa7 +#define HidUsage_PID_ParameterBlockSize 0xa8 +#define HidUsage_PID_DeviceManagedPool 0xa9 +#define HidUsage_PID_SharedParameterBlocks 0xaa +#define HidUsage_PID_CreateNewEffectParameterBlockReport 0xab +#define HidUsage_PID_RAMPoolAvailable 0xac +#define HidUsage_SC_SocControl 0x01 +#define HidUsage_SC_FirmwareTransfer 0x02 +#define HidUsage_SC_FirmwareFileId 0x03 +#define HidUsage_SC_FileOffsetInBytes 0x04 +#define HidUsage_SC_FileTransferSizeMaxInBytes 0x05 +#define HidUsage_SC_FilePayload 0x06 +#define HidUsage_SC_FilePayloadSizeInBytes 0x07 +#define HidUsage_SC_FilePayloadContainsLastBytes 0x08 +#define HidUsage_SC_FileTransferStop 0x09 +#define HidUsage_SC_FileTransferTillEnd 0x0a +#define HidUsage_EHT_EyeTracker 0x01 +#define HidUsage_EHT_HeadTracker 0x02 +#define HidUsage_EHT_TrackingData 0x10 +#define HidUsage_EHT_Capabilities 0x11 +#define HidUsage_EHT_Configuration 0x12 +#define HidUsage_EHT_Status 0x13 +#define HidUsage_EHT_Control 0x14 +#define HidUsage_EHT_SensorTimestamp 0x20 +#define HidUsage_EHT_PositionX 0x21 +#define HidUsage_EHT_PositionY 0x22 +#define HidUsage_EHT_PositionZ 0x23 +#define HidUsage_EHT_GazePoint 0x24 +#define HidUsage_EHT_LeftEyePosition 0x25 +#define HidUsage_EHT_RightEyePosition 0x26 +#define HidUsage_EHT_HeadPosition 0x27 +#define HidUsage_EHT_HeadDirectionPoint 0x28 +#define HidUsage_EHT_RotationaboutXaxis 0x29 +#define HidUsage_EHT_RotationaboutYaxis 0x2a +#define HidUsage_EHT_RotationaboutZaxis 0x2b +#define HidUsage_EHT_TrackerQuality 0x100 +#define HidUsage_EHT_MinimumTrackingDistance 0x101 +#define HidUsage_EHT_OptimumTrackingDistance 0x102 +#define HidUsage_EHT_MaximumTrackingDistance 0x103 +#define HidUsage_EHT_MaximumScreenPlaneWidth 0x104 +#define HidUsage_EHT_MaximumScreenPlaneHeight 0x105 +#define HidUsage_EHT_DisplayManufacturerID 0x200 +#define HidUsage_EHT_DisplayProductID 0x201 +#define HidUsage_EHT_DisplaySerialNumber 0x202 +#define HidUsage_EHT_DisplayManufacturerDate 0x203 +#define HidUsage_EHT_CalibratedScreenWidth 0x204 +#define HidUsage_EHT_CalibratedScreenHeight 0x205 +#define HidUsage_EHT_SamplingFrequency 0x300 +#define HidUsage_EHT_ConfigurationStatus 0x301 +#define HidUsage_EHT_DeviceModeRequest 0x400 +#define HidUsage_AD_AlphanumericDisplay 0x01 +#define HidUsage_AD_AuxiliaryDisplay 0x02 +#define HidUsage_AD_DisplayAttributesReport 0x20 +#define HidUsage_AD_ASCIICharacterSet 0x21 +#define HidUsage_AD_DataReadBack 0x22 +#define HidUsage_AD_FontReadBack 0x23 +#define HidUsage_AD_DisplayControlReport 0x24 +#define HidUsage_AD_ClearDisplay 0x25 +#define HidUsage_AD_DisplayEnable 0x26 +#define HidUsage_AD_ScreenSaverDelay 0x27 +#define HidUsage_AD_ScreenSaverEnable 0x28 +#define HidUsage_AD_VerticalScroll 0x29 +#define HidUsage_AD_HorizontalScroll 0x2a +#define HidUsage_AD_CharacterReport 0x2b +#define HidUsage_AD_DisplayData 0x2c +#define HidUsage_AD_DisplayStatus 0x2d +#define HidUsage_AD_StatNotReady 0x2e +#define HidUsage_AD_StatReady 0x2f +#define HidUsage_AD_ErrNotaloadablecharacter 0x30 +#define HidUsage_AD_ErrFontdatacannotberead 0x31 +#define HidUsage_AD_CursorPositionReport 0x32 +#define HidUsage_AD_Row 0x33 +#define HidUsage_AD_Column 0x34 +#define HidUsage_AD_Rows 0x35 +#define HidUsage_AD_Columns 0x36 +#define HidUsage_AD_CursorPixelPositioning 0x37 +#define HidUsage_AD_CursorMode 0x38 +#define HidUsage_AD_CursorEnable 0x39 +#define HidUsage_AD_CursorBlink 0x3a +#define HidUsage_AD_FontReport 0x3b +#define HidUsage_AD_FontData 0x3c +#define HidUsage_AD_CharacterWidth 0x3d +#define HidUsage_AD_CharacterHeight 0x3e +#define HidUsage_AD_CharacterSpacingHorizontal 0x3f +#define HidUsage_AD_CharacterSpacingVertical 0x40 +#define HidUsage_AD_UnicodeCharacterSet 0x41 +#define HidUsage_AD_Font7Segment 0x42 +#define HidUsage_AD_SevenSegmentDirectMap 0x43 +#define HidUsage_AD_Font14Segment 0x44 +#define HidUsage_AD_One4SegmentDirectMap 0x45 +#define HidUsage_AD_DisplayBrightness 0x46 +#define HidUsage_AD_DisplayContrast 0x47 +#define HidUsage_AD_CharacterAttribute 0x48 +#define HidUsage_AD_AttributeReadback 0x49 +#define HidUsage_AD_AttributeData 0x4a +#define HidUsage_AD_CharAttrEnhance 0x4b +#define HidUsage_AD_CharAttrUnderline 0x4c +#define HidUsage_AD_CharAttrBlink 0x4d +#define HidUsage_AD_BitmapSizeX 0x80 +#define HidUsage_AD_BitmapSizeY 0x81 +#define HidUsage_AD_MaxBlitSize 0x82 +#define HidUsage_AD_BitDepthFormat 0x83 +#define HidUsage_AD_DisplayOrientation 0x84 +#define HidUsage_AD_PaletteReport 0x85 +#define HidUsage_AD_PaletteDataSize 0x86 +#define HidUsage_AD_PaletteDataOffset 0x87 +#define HidUsage_AD_PaletteData 0x88 +#define HidUsage_AD_BlitReport 0x8a +#define HidUsage_AD_BlitRectangleX1 0x8b +#define HidUsage_AD_BlitRectangleY1 0x8c +#define HidUsage_AD_BlitRectangleX2 0x8d +#define HidUsage_AD_BlitRectangleY2 0x8e +#define HidUsage_AD_BlitData 0x8f +#define HidUsage_AD_SoftButton 0x90 +#define HidUsage_AD_SoftButtonID 0x91 +#define HidUsage_AD_SoftButtonSide 0x92 +#define HidUsage_AD_SoftButtonOffset1 0x93 +#define HidUsage_AD_SoftButtonOffset2 0x94 +#define HidUsage_AD_SoftButtonReport 0x95 +#define HidUsage_AD_SoftKeys 0xc2 +#define HidUsage_AD_DisplayDataExtensions 0xcc +#define HidUsage_AD_CharacterMapping 0xcf +#define HidUsage_AD_UnicodeEquivalent 0xdd +#define HidUsage_AD_CharacterPageMapping 0xdf +#define HidUsage_AD_RequestReport 0xff +#define HidUsage_Sen_Sensor 0x01 +#define HidUsage_Sen_Biometric 0x10 +#define HidUsage_Sen_BiometricHumanPresence 0x11 +#define HidUsage_Sen_BiometricHumanProximity 0x12 +#define HidUsage_Sen_BiometricHumanTouch 0x13 +#define HidUsage_Sen_BiometricBloodPressure 0x14 +#define HidUsage_Sen_BiometricBodyTemperature 0x15 +#define HidUsage_Sen_BiometricHeartRate 0x16 +#define HidUsage_Sen_BiometricHeartRateVariability 0x17 +#define HidUsage_Sen_BiometricPeripheralOxygenSaturation 0x18 +#define HidUsage_Sen_BiometricRespiratoryRate 0x19 +#define HidUsage_Sen_Electrical 0x20 +#define HidUsage_Sen_ElectricalCapacitance 0x21 +#define HidUsage_Sen_ElectricalCurrent 0x22 +#define HidUsage_Sen_ElectricalPower 0x23 +#define HidUsage_Sen_ElectricalInductance 0x24 +#define HidUsage_Sen_ElectricalResistance 0x25 +#define HidUsage_Sen_ElectricalVoltage 0x26 +#define HidUsage_Sen_ElectricalPotentiometer 0x27 +#define HidUsage_Sen_ElectricalFrequency 0x28 +#define HidUsage_Sen_ElectricalPeriod 0x29 +#define HidUsage_Sen_Environmental 0x30 +#define HidUsage_Sen_EnvironmentalAtmosphericPressure 0x31 +#define HidUsage_Sen_EnvironmentalHumidity 0x32 +#define HidUsage_Sen_EnvironmentalTemperature 0x33 +#define HidUsage_Sen_EnvironmentalWindDirection 0x34 +#define HidUsage_Sen_EnvironmentalWindSpeed 0x35 +#define HidUsage_Sen_EnvironmentalAirQuality 0x36 +#define HidUsage_Sen_EnvironmentalHeatIndex 0x37 +#define HidUsage_Sen_EnvironmentalSurfaceTemperature 0x38 +#define HidUsage_Sen_EnvironmentalVolatileOrganicCompounds 0x39 +#define HidUsage_Sen_EnvironmentalObjectPresence 0x3a +#define HidUsage_Sen_EnvironmentalObjectProximity 0x3b +#define HidUsage_Sen_Light 0x40 +#define HidUsage_Sen_LightAmbientLight 0x41 +#define HidUsage_Sen_LightConsumerInfrared 0x42 +#define HidUsage_Sen_LightInfraredLight 0x43 +#define HidUsage_Sen_LightVisibleLight 0x44 +#define HidUsage_Sen_LightUltravioletLight 0x45 +#define HidUsage_Sen_Location 0x50 +#define HidUsage_Sen_LocationBroadcast 0x51 +#define HidUsage_Sen_LocationDeadReckoning 0x52 +#define HidUsage_Sen_LocationGPSGlobalPositioningSystem 0x53 +#define HidUsage_Sen_LocationLookup 0x54 +#define HidUsage_Sen_LocationOther 0x55 +#define HidUsage_Sen_LocationStatic 0x56 +#define HidUsage_Sen_LocationTriangulation 0x57 +#define HidUsage_Sen_Mechanical 0x60 +#define HidUsage_Sen_MechanicalBooleanSwitch 0x61 +#define HidUsage_Sen_MechanicalBooleanSwitchArray 0x62 +#define HidUsage_Sen_MechanicalMultivalueSwitch 0x63 +#define HidUsage_Sen_MechanicalForce 0x64 +#define HidUsage_Sen_MechanicalPressure 0x65 +#define HidUsage_Sen_MechanicalStrain 0x66 +#define HidUsage_Sen_MechanicalWeight 0x67 +#define HidUsage_Sen_MechanicalHapticVibrator 0x68 +#define HidUsage_Sen_MechanicalHallEffectSwitch 0x69 +#define HidUsage_Sen_Motion 0x70 +#define HidUsage_Sen_MotionAccelerometer1D 0x71 +#define HidUsage_Sen_MotionAccelerometer2D 0x72 +#define HidUsage_Sen_MotionAccelerometer3D 0x73 +#define HidUsage_Sen_MotionGyrometer1D 0x74 +#define HidUsage_Sen_MotionGyrometer2D 0x75 +#define HidUsage_Sen_MotionGyrometer3D 0x76 +#define HidUsage_Sen_MotionMotionDetector 0x77 +#define HidUsage_Sen_MotionSpeedometer 0x78 +#define HidUsage_Sen_MotionAccelerometer 0x79 +#define HidUsage_Sen_MotionGyrometer 0x7a +#define HidUsage_Sen_MotionGravityVector 0x7b +#define HidUsage_Sen_MotionLinearAccelerometer 0x7c +#define HidUsage_Sen_Orientation 0x80 +#define HidUsage_Sen_OrientationCompass1D 0x81 +#define HidUsage_Sen_OrientationCompass2D 0x82 +#define HidUsage_Sen_OrientationCompass3D 0x83 +#define HidUsage_Sen_OrientationInclinometer1D 0x84 +#define HidUsage_Sen_OrientationInclinometer2D 0x85 +#define HidUsage_Sen_OrientationInclinometer3D 0x86 +#define HidUsage_Sen_OrientationDistance1D 0x87 +#define HidUsage_Sen_OrientationDistance2D 0x88 +#define HidUsage_Sen_OrientationDistance3D 0x89 +#define HidUsage_Sen_OrientationDeviceOrientation 0x8a +#define HidUsage_Sen_OrientationCompass 0x8b +#define HidUsage_Sen_OrientationInclinometer 0x8c +#define HidUsage_Sen_OrientationDistance 0x8d +#define HidUsage_Sen_OrientationRelativeOrientation 0x8e +#define HidUsage_Sen_OrientationSimpleOrientation 0x8f +#define HidUsage_Sen_Scanner 0x90 +#define HidUsage_Sen_ScannerBarcode 0x91 +#define HidUsage_Sen_ScannerRFID 0x92 +#define HidUsage_Sen_ScannerNFC 0x93 +#define HidUsage_Sen_Time 0xa0 +#define HidUsage_Sen_TimeAlarmTimer 0xa1 +#define HidUsage_Sen_TimeRealTimeClock 0xa2 +#define HidUsage_Sen_PersonalActivity 0xb0 +#define HidUsage_Sen_PersonalActivityActivityDetection 0xb1 +#define HidUsage_Sen_PersonalActivityDevicePosition 0xb2 +#define HidUsage_Sen_PersonalActivityFloorTracker 0xb3 +#define HidUsage_Sen_PersonalActivityPedometer 0xb4 +#define HidUsage_Sen_PersonalActivityStepDetection 0xb5 +#define HidUsage_Sen_OrientationExtended 0xc0 +#define HidUsage_Sen_OrientationExtendedGeomagneticOrientation 0xc1 +#define HidUsage_Sen_OrientationExtendedMagnetometer 0xc2 +#define HidUsage_Sen_Gesture 0xd0 +#define HidUsage_Sen_GestureChassisFlipGesture 0xd1 +#define HidUsage_Sen_GestureHingeFoldGesture 0xd2 +#define HidUsage_Sen_Other 0xe0 +#define HidUsage_Sen_OtherCustom 0xe1 +#define HidUsage_Sen_OtherGeneric 0xe2 +#define HidUsage_Sen_OtherGenericEnumerator 0xe3 +#define HidUsage_Sen_OtherHingeAngle 0xe4 +#define HidUsage_Sen_VendorReserved1 0xf0 +#define HidUsage_Sen_VendorReserved2 0xf1 +#define HidUsage_Sen_VendorReserved3 0xf2 +#define HidUsage_Sen_VendorReserved4 0xf3 +#define HidUsage_Sen_VendorReserved5 0xf4 +#define HidUsage_Sen_VendorReserved6 0xf5 +#define HidUsage_Sen_VendorReserved7 0xf6 +#define HidUsage_Sen_VendorReserved8 0xf7 +#define HidUsage_Sen_VendorReserved9 0xf8 +#define HidUsage_Sen_VendorReserved10 0xf9 +#define HidUsage_Sen_VendorReserved11 0xfa +#define HidUsage_Sen_VendorReserved12 0xfb +#define HidUsage_Sen_VendorReserved13 0xfc +#define HidUsage_Sen_VendorReserved14 0xfd +#define HidUsage_Sen_VendorReserved15 0xfe +#define HidUsage_Sen_VendorReserved16 0xff +#define HidUsage_Sen_Event 0x200 +#define HidUsage_Sen_EventSensorState 0x201 +#define HidUsage_Sen_EventSensorEvent 0x202 +#define HidUsage_Sen_Property 0x300 +#define HidUsage_Sen_PropertyFriendlyName 0x301 +#define HidUsage_Sen_PropertyPersistentUniqueID 0x302 +#define HidUsage_Sen_PropertySensorStatus 0x303 +#define HidUsage_Sen_PropertyMinimumReportInterval 0x304 +#define HidUsage_Sen_PropertySensorManufacturer 0x305 +#define HidUsage_Sen_PropertySensorModel 0x306 +#define HidUsage_Sen_PropertySensorSerialNumber 0x307 +#define HidUsage_Sen_PropertySensorDescription 0x308 +#define HidUsage_Sen_PropertySensorConnectionType 0x309 +#define HidUsage_Sen_PropertySensorDevicePath 0x30a +#define HidUsage_Sen_PropertyHardwareRevision 0x30b +#define HidUsage_Sen_PropertyFirmwareVersion 0x30c +#define HidUsage_Sen_PropertyReleaseDate 0x30d +#define HidUsage_Sen_PropertyReportInterval 0x30e +#define HidUsage_Sen_PropertyChangeSensitivityAbsolute 0x30f +#define HidUsage_Sen_PropertyChangeSensitivityPercentofRange 0x310 +#define HidUsage_Sen_PropertyChangeSensitivityPercentRelative 0x311 +#define HidUsage_Sen_PropertyAccuracy 0x312 +#define HidUsage_Sen_PropertyResolution 0x313 +#define HidUsage_Sen_PropertyMaximum 0x314 +#define HidUsage_Sen_PropertyMinimum 0x315 +#define HidUsage_Sen_PropertyReportingState 0x316 +#define HidUsage_Sen_PropertySamplingRate 0x317 +#define HidUsage_Sen_PropertyResponseCurve 0x318 +#define HidUsage_Sen_PropertyPowerState 0x319 +#define HidUsage_Sen_PropertyMaximumFIFOEvents 0x31a +#define HidUsage_Sen_PropertyReportLatency 0x31b +#define HidUsage_Sen_PropertyFlushFIFOEvents 0x31c +#define HidUsage_Sen_PropertyMaximumPowerConsumption 0x31d +#define HidUsage_Sen_PropertyIsPrimary 0x31e +#define HidUsage_Sen_PropertyHumanPresenceDetectionType 0x31f +#define HidUsage_Sen_DataFieldLocation 0x400 +#define HidUsage_Sen_DataFieldAltitudeAntennaSeaLevel 0x402 +#define HidUsage_Sen_DataFieldDifferentialReferenceStationID 0x403 +#define HidUsage_Sen_DataFieldAltitudeEllipsoidError 0x404 +#define HidUsage_Sen_DataFieldAltitudeEllipsoid 0x405 +#define HidUsage_Sen_DataFieldAltitudeSeaLevelError 0x406 +#define HidUsage_Sen_DataFieldAltitudeSeaLevel 0x407 +#define HidUsage_Sen_DataFieldDifferentialGPSDataAge 0x408 +#define HidUsage_Sen_DataFieldErrorRadius 0x409 +#define HidUsage_Sen_DataFieldFixQuality 0x40a +#define HidUsage_Sen_DataFieldFixType 0x40b +#define HidUsage_Sen_DataFieldGeoidalSeparation 0x40c +#define HidUsage_Sen_DataFieldGPSOperationMode 0x40d +#define HidUsage_Sen_DataFieldGPSSelectionMode 0x40e +#define HidUsage_Sen_DataFieldGPSStatus 0x40f +#define HidUsage_Sen_DataFieldPositionDilutionofPrecision 0x410 +#define HidUsage_Sen_DataFieldHorizontalDilutionofPrecision 0x411 +#define HidUsage_Sen_DataFieldVerticalDilutionofPrecision 0x412 +#define HidUsage_Sen_DataFieldLatitude 0x413 +#define HidUsage_Sen_DataFieldLongitude 0x414 +#define HidUsage_Sen_DataFieldTrueHeading 0x415 +#define HidUsage_Sen_DataFieldMagneticHeading 0x416 +#define HidUsage_Sen_DataFieldMagneticVariation 0x417 +#define HidUsage_Sen_DataFieldSpeed 0x418 +#define HidUsage_Sen_DataFieldSatellitesinView 0x419 +#define HidUsage_Sen_DataFieldSatellitesinViewAzimuth 0x41a +#define HidUsage_Sen_DataFieldSatellitesinViewElevation 0x41b +#define HidUsage_Sen_DataFieldSatellitesinViewIDs 0x41c +#define HidUsage_Sen_DataFieldSatellitesinViewPRNs 0x41d +#define HidUsage_Sen_DataFieldSatellitesinViewSNRatios 0x41e +#define HidUsage_Sen_DataFieldSatellitesUsedCount 0x41f +#define HidUsage_Sen_DataFieldSatellitesUsedPRNs 0x420 +#define HidUsage_Sen_DataFieldNMEASentence 0x421 +#define HidUsage_Sen_DataFieldAddressLine1 0x422 +#define HidUsage_Sen_DataFieldAddressLine2 0x423 +#define HidUsage_Sen_DataFieldCity 0x424 +#define HidUsage_Sen_DataFieldStateorProvince 0x425 +#define HidUsage_Sen_DataFieldCountryorRegion 0x426 +#define HidUsage_Sen_DataFieldPostalCode 0x427 +#define HidUsage_Sen_PropertyLocation 0x42a +#define HidUsage_Sen_PropertyLocationDesiredAccuracy 0x42b +#define HidUsage_Sen_DataFieldEnvironmental 0x430 +#define HidUsage_Sen_DataFieldAtmosphericPressure 0x431 +#define HidUsage_Sen_DataFieldRelativeHumidity 0x433 +#define HidUsage_Sen_DataFieldTemperature 0x434 +#define HidUsage_Sen_DataFieldWindDirection 0x435 +#define HidUsage_Sen_DataFieldWindSpeed 0x436 +#define HidUsage_Sen_DataFieldAirQualityIndex 0x437 +#define HidUsage_Sen_DataFieldEquivalentCO2 0x438 +#define HidUsage_Sen_DataFieldVolatileOrganicCompoundConcentration 0x439 +#define HidUsage_Sen_DataFieldObjectPresence 0x43a +#define HidUsage_Sen_DataFieldObjectProximityRange 0x43b +#define HidUsage_Sen_DataFieldObjectProximityOutofRange 0x43c +#define HidUsage_Sen_PropertyEnvironmental 0x440 +#define HidUsage_Sen_PropertyReferencePressure 0x441 +#define HidUsage_Sen_DataFieldMotion 0x450 +#define HidUsage_Sen_DataFieldMotionState 0x451 +#define HidUsage_Sen_DataFieldAcceleration 0x452 +#define HidUsage_Sen_DataFieldAccelerationAxisX 0x453 +#define HidUsage_Sen_DataFieldAccelerationAxisY 0x454 +#define HidUsage_Sen_DataFieldAccelerationAxisZ 0x455 +#define HidUsage_Sen_DataFieldAngularVelocity 0x456 +#define HidUsage_Sen_DataFieldAngularVelocityaboutXAxis 0x457 +#define HidUsage_Sen_DataFieldAngularVelocityaboutYAxis 0x458 +#define HidUsage_Sen_DataFieldAngularVelocityaboutZAxis 0x459 +#define HidUsage_Sen_DataFieldAngularPosition 0x45a +#define HidUsage_Sen_DataFieldAngularPositionaboutXAxis 0x45b +#define HidUsage_Sen_DataFieldAngularPositionaboutYAxis 0x45c +#define HidUsage_Sen_DataFieldAngularPositionaboutZAxis 0x45d +#define HidUsage_Sen_DataFieldMotionSpeed 0x45e +#define HidUsage_Sen_DataFieldMotionIntensity 0x45f +#define HidUsage_Sen_DataFieldOrientation 0x470 +#define HidUsage_Sen_DataFieldHeading 0x471 +#define HidUsage_Sen_DataFieldHeadingXAxis 0x472 +#define HidUsage_Sen_DataFieldHeadingYAxis 0x473 +#define HidUsage_Sen_DataFieldHeadingZAxis 0x474 +#define HidUsage_Sen_DataFieldHeadingCompensatedMagneticNorth 0x475 +#define HidUsage_Sen_DataFieldHeadingCompensatedTrueNorth 0x476 +#define HidUsage_Sen_DataFieldHeadingMagneticNorth 0x477 +#define HidUsage_Sen_DataFieldHeadingTrueNorth 0x478 +#define HidUsage_Sen_DataFieldDistance 0x479 +#define HidUsage_Sen_DataFieldDistanceXAxis 0x47a +#define HidUsage_Sen_DataFieldDistanceYAxis 0x47b +#define HidUsage_Sen_DataFieldDistanceZAxis 0x47c +#define HidUsage_Sen_DataFieldDistanceOutofRange 0x47d +#define HidUsage_Sen_DataFieldTilt 0x47e +#define HidUsage_Sen_DataFieldTiltXAxis 0x47f +#define HidUsage_Sen_DataFieldTiltYAxis 0x480 +#define HidUsage_Sen_DataFieldTiltZAxis 0x481 +#define HidUsage_Sen_DataFieldRotationMatrix 0x482 +#define HidUsage_Sen_DataFieldQuaternion 0x483 +#define HidUsage_Sen_DataFieldMagneticFlux 0x484 +#define HidUsage_Sen_DataFieldMagneticFluxXAxis 0x485 +#define HidUsage_Sen_DataFieldMagneticFluxYAxis 0x486 +#define HidUsage_Sen_DataFieldMagneticFluxZAxis 0x487 +#define HidUsage_Sen_DataFieldMagnetometerAccuracy 0x488 +#define HidUsage_Sen_DataFieldSimpleOrientationDirection 0x489 +#define HidUsage_Sen_DataFieldMechanical 0x490 +#define HidUsage_Sen_DataFieldBooleanSwitchState 0x491 +#define HidUsage_Sen_DataFieldBooleanSwitchArrayStates 0x492 +#define HidUsage_Sen_DataFieldMultivalueSwitchValue 0x493 +#define HidUsage_Sen_DataFieldForce 0x494 +#define HidUsage_Sen_DataFieldAbsolutePressure 0x495 +#define HidUsage_Sen_DataFieldGaugePressure 0x496 +#define HidUsage_Sen_DataFieldStrain 0x497 +#define HidUsage_Sen_DataFieldWeight 0x498 +#define HidUsage_Sen_PropertyMechanical 0x4a0 +#define HidUsage_Sen_PropertyVibrationState 0x4a1 +#define HidUsage_Sen_PropertyForwardVibrationSpeed 0x4a2 +#define HidUsage_Sen_PropertyBackwardVibrationSpeed 0x4a3 +#define HidUsage_Sen_DataFieldBiometric 0x4b0 +#define HidUsage_Sen_DataFieldHumanPresence 0x4b1 +#define HidUsage_Sen_DataFieldHumanProximityRange 0x4b2 +#define HidUsage_Sen_DataFieldHumanProximityOutofRange 0x4b3 +#define HidUsage_Sen_DataFieldHumanTouchState 0x4b4 +#define HidUsage_Sen_DataFieldBloodPressure 0x4b5 +#define HidUsage_Sen_DataFieldBloodPressureDiastolic 0x4b6 +#define HidUsage_Sen_DataFieldBloodPressureSystolic 0x4b7 +#define HidUsage_Sen_DataFieldHeartRate 0x4b8 +#define HidUsage_Sen_DataFieldRestingHeartRate 0x4b9 +#define HidUsage_Sen_DataFieldHeartbeatInterval 0x4ba +#define HidUsage_Sen_DataFieldRespiratoryRate 0x4bb +#define HidUsage_Sen_DataFieldSpO2 0x4bc +#define HidUsage_Sen_DataFieldHumanAttentionDetected 0x4bd +#define HidUsage_Sen_DataFieldHumanHeadAzimuth 0x4be +#define HidUsage_Sen_DataFieldHumanHeadAltitude 0x4bf +#define HidUsage_Sen_DataFieldHumanHeadRoll 0x4c0 +#define HidUsage_Sen_DataFieldHumanHeadPitch 0x4c1 +#define HidUsage_Sen_DataFieldHumanHeadYaw 0x4c2 +#define HidUsage_Sen_DataFieldHumanCorrelationId 0x4c3 +#define HidUsage_Sen_DataFieldLight 0x4d0 +#define HidUsage_Sen_DataFieldIlluminance 0x4d1 +#define HidUsage_Sen_DataFieldColorTemperature 0x4d2 +#define HidUsage_Sen_DataFieldChromaticity 0x4d3 +#define HidUsage_Sen_DataFieldChromaticityX 0x4d4 +#define HidUsage_Sen_DataFieldChromaticityY 0x4d5 +#define HidUsage_Sen_DataFieldConsumerIRSentenceReceive 0x4d6 +#define HidUsage_Sen_DataFieldInfraredLight 0x4d7 +#define HidUsage_Sen_DataFieldRedLight 0x4d8 +#define HidUsage_Sen_DataFieldGreenLight 0x4d9 +#define HidUsage_Sen_DataFieldBlueLight 0x4da +#define HidUsage_Sen_DataFieldUltravioletALight 0x4db +#define HidUsage_Sen_DataFieldUltravioletBLight 0x4dc +#define HidUsage_Sen_DataFieldUltravioletIndex 0x4dd +#define HidUsage_Sen_DataFieldNearInfraredLight 0x4de +#define HidUsage_Sen_PropertyLight 0x4df +#define HidUsage_Sen_PropertyConsumerIRSentenceSend 0x4e0 +#define HidUsage_Sen_PropertyAutoBrightnessPreferred 0x4e2 +#define HidUsage_Sen_PropertyAutoColorPreferred 0x4e3 +#define HidUsage_Sen_DataFieldScanner 0x4f0 +#define HidUsage_Sen_DataFieldRFIDTag40Bit 0x4f1 +#define HidUsage_Sen_DataFieldNFCSentenceReceive 0x4f2 +#define HidUsage_Sen_PropertyScanner 0x4f8 +#define HidUsage_Sen_PropertyNFCSentenceSend 0x4f9 +#define HidUsage_Sen_DataFieldElectrical 0x500 +#define HidUsage_Sen_DataFieldCapacitance 0x501 +#define HidUsage_Sen_DataFieldCurrent 0x502 +#define HidUsage_Sen_DataFieldElectricalPower 0x503 +#define HidUsage_Sen_DataFieldInductance 0x504 +#define HidUsage_Sen_DataFieldResistance 0x505 +#define HidUsage_Sen_DataFieldVoltage 0x506 +#define HidUsage_Sen_DataFieldFrequency 0x507 +#define HidUsage_Sen_DataFieldPeriod 0x508 +#define HidUsage_Sen_DataFieldPercentofRange 0x509 +#define HidUsage_Sen_DataFieldTime 0x520 +#define HidUsage_Sen_DataFieldYear 0x521 +#define HidUsage_Sen_DataFieldMonth 0x522 +#define HidUsage_Sen_DataFieldDay 0x523 +#define HidUsage_Sen_DataFieldDayofWeek 0x524 +#define HidUsage_Sen_DataFieldHour 0x525 +#define HidUsage_Sen_DataFieldMinute 0x526 +#define HidUsage_Sen_DataFieldSecond 0x527 +#define HidUsage_Sen_DataFieldMillisecond 0x528 +#define HidUsage_Sen_DataFieldTimestamp 0x529 +#define HidUsage_Sen_DataFieldJulianDayofYear 0x52a +#define HidUsage_Sen_DataFieldTimeSinceSystemBoot 0x52b +#define HidUsage_Sen_PropertyTime 0x530 +#define HidUsage_Sen_PropertyTimeZoneOffsetfromUTC 0x531 +#define HidUsage_Sen_PropertyTimeZoneName 0x532 +#define HidUsage_Sen_PropertyDaylightSavingsTimeObserved 0x533 +#define HidUsage_Sen_PropertyTimeTrimAdjustment 0x534 +#define HidUsage_Sen_PropertyArmAlarm 0x535 +#define HidUsage_Sen_DataFieldCustom 0x540 +#define HidUsage_Sen_DataFieldCustomUsage 0x541 +#define HidUsage_Sen_DataFieldCustomBooleanArray 0x542 +#define HidUsage_Sen_DataFieldCustomValue 0x543 +#define HidUsage_Sen_DataFieldCustomValue1 0x544 +#define HidUsage_Sen_DataFieldCustomValue2 0x545 +#define HidUsage_Sen_DataFieldCustomValue3 0x546 +#define HidUsage_Sen_DataFieldCustomValue4 0x547 +#define HidUsage_Sen_DataFieldCustomValue5 0x548 +#define HidUsage_Sen_DataFieldCustomValue6 0x549 +#define HidUsage_Sen_DataFieldCustomValue7 0x54a +#define HidUsage_Sen_DataFieldCustomValue8 0x54b +#define HidUsage_Sen_DataFieldCustomValue9 0x54c +#define HidUsage_Sen_DataFieldCustomValue10 0x54d +#define HidUsage_Sen_DataFieldCustomValue11 0x54e +#define HidUsage_Sen_DataFieldCustomValue12 0x54f +#define HidUsage_Sen_DataFieldCustomValue13 0x550 +#define HidUsage_Sen_DataFieldCustomValue14 0x551 +#define HidUsage_Sen_DataFieldCustomValue15 0x552 +#define HidUsage_Sen_DataFieldCustomValue16 0x553 +#define HidUsage_Sen_DataFieldCustomValue17 0x554 +#define HidUsage_Sen_DataFieldCustomValue18 0x555 +#define HidUsage_Sen_DataFieldCustomValue19 0x556 +#define HidUsage_Sen_DataFieldCustomValue20 0x557 +#define HidUsage_Sen_DataFieldCustomValue21 0x558 +#define HidUsage_Sen_DataFieldCustomValue22 0x559 +#define HidUsage_Sen_DataFieldCustomValue23 0x55a +#define HidUsage_Sen_DataFieldCustomValue24 0x55b +#define HidUsage_Sen_DataFieldCustomValue25 0x55c +#define HidUsage_Sen_DataFieldCustomValue26 0x55d +#define HidUsage_Sen_DataFieldCustomValue27 0x55e +#define HidUsage_Sen_DataFieldCustomValue28 0x55f +#define HidUsage_Sen_DataFieldGeneric 0x560 +#define HidUsage_Sen_DataFieldGenericGUIDorPROPERTYKEY 0x561 +#define HidUsage_Sen_DataFieldGenericCategoryGUID 0x562 +#define HidUsage_Sen_DataFieldGenericTypeGUID 0x563 +#define HidUsage_Sen_DataFieldGenericEventPROPERTYKEY 0x564 +#define HidUsage_Sen_DataFieldGenericPropertyPROPERTYKEY 0x565 +#define HidUsage_Sen_DataFieldGenericDataFieldPROPERTYKEY 0x566 +#define HidUsage_Sen_DataFieldGenericEvent 0x567 +#define HidUsage_Sen_DataFieldGenericProperty 0x568 +#define HidUsage_Sen_DataFieldGenericDataField 0x569 +#define HidUsage_Sen_DataFieldEnumeratorTableRowIndex 0x56a +#define HidUsage_Sen_DataFieldEnumeratorTableRowCount 0x56b +#define HidUsage_Sen_DataFieldGenericGUIDorPROPERTYKEYkind 0x56c +#define HidUsage_Sen_DataFieldGenericGUID 0x56d +#define HidUsage_Sen_DataFieldGenericPROPERTYKEY 0x56e +#define HidUsage_Sen_DataFieldGenericTopLevelCollectionID 0x56f +#define HidUsage_Sen_DataFieldGenericReportID 0x570 +#define HidUsage_Sen_DataFieldGenericReportItemPositionIndex 0x571 +#define HidUsage_Sen_DataFieldGenericFirmwareVARTYPE 0x572 +#define HidUsage_Sen_DataFieldGenericUnitofMeasure 0x573 +#define HidUsage_Sen_DataFieldGenericUnitExponent 0x574 +#define HidUsage_Sen_DataFieldGenericReportSize 0x575 +#define HidUsage_Sen_DataFieldGenericReportCount 0x576 +#define HidUsage_Sen_PropertyGeneric 0x580 +#define HidUsage_Sen_PropertyEnumeratorTableRowIndex 0x581 +#define HidUsage_Sen_PropertyEnumeratorTableRowCount 0x582 +#define HidUsage_Sen_DataFieldPersonalActivity 0x590 +#define HidUsage_Sen_DataFieldActivityType 0x591 +#define HidUsage_Sen_DataFieldActivityState 0x592 +#define HidUsage_Sen_DataFieldDevicePosition 0x593 +#define HidUsage_Sen_DataFieldStepCount 0x594 +#define HidUsage_Sen_DataFieldStepCountReset 0x595 +#define HidUsage_Sen_DataFieldStepDuration 0x596 +#define HidUsage_Sen_DataFieldStepType 0x597 +#define HidUsage_Sen_PropertyMinimumActivityDetectionInterval 0x5a0 +#define HidUsage_Sen_PropertySupportedActivityTypes 0x5a1 +#define HidUsage_Sen_PropertySubscribedActivityTypes 0x5a2 +#define HidUsage_Sen_PropertySupportedStepTypes 0x5a3 +#define HidUsage_Sen_PropertySubscribedStepTypes 0x5a4 +#define HidUsage_Sen_PropertyFloorHeight 0x5a5 +#define HidUsage_Sen_DataFieldCustomTypeID 0x5b0 +#define HidUsage_Sen_PropertyCustom 0x5c0 +#define HidUsage_Sen_PropertyCustomValue1 0x5c1 +#define HidUsage_Sen_PropertyCustomValue2 0x5c2 +#define HidUsage_Sen_PropertyCustomValue3 0x5c3 +#define HidUsage_Sen_PropertyCustomValue4 0x5c4 +#define HidUsage_Sen_PropertyCustomValue5 0x5c5 +#define HidUsage_Sen_PropertyCustomValue6 0x5c6 +#define HidUsage_Sen_PropertyCustomValue7 0x5c7 +#define HidUsage_Sen_PropertyCustomValue8 0x5c8 +#define HidUsage_Sen_PropertyCustomValue9 0x5c9 +#define HidUsage_Sen_PropertyCustomValue10 0x5ca +#define HidUsage_Sen_PropertyCustomValue11 0x5cb +#define HidUsage_Sen_PropertyCustomValue12 0x5cc +#define HidUsage_Sen_PropertyCustomValue13 0x5cd +#define HidUsage_Sen_PropertyCustomValue14 0x5ce +#define HidUsage_Sen_PropertyCustomValue15 0x5cf +#define HidUsage_Sen_PropertyCustomValue16 0x5d0 +#define HidUsage_Sen_DataFieldHinge 0x5e0 +#define HidUsage_Sen_DataFieldHingeAngle 0x5e1 +#define HidUsage_Sen_DataFieldGestureSensor 0x5f0 +#define HidUsage_Sen_DataFieldGestureState 0x5f1 +#define HidUsage_Sen_DataFieldHingeFoldInitialAngle 0x5f2 +#define HidUsage_Sen_DataFieldHingeFoldFinalAngle 0x5f3 +#define HidUsage_Sen_DataFieldHingeFoldContributingPanel 0x5f4 +#define HidUsage_Sen_DataFieldHingeFoldType 0x5f5 +#define HidUsage_Sen_SensorStateUndefined 0x800 +#define HidUsage_Sen_SensorStateReady 0x801 +#define HidUsage_Sen_SensorStateNotAvailable 0x802 +#define HidUsage_Sen_SensorStateNoData 0x803 +#define HidUsage_Sen_SensorStateInitializing 0x804 +#define HidUsage_Sen_SensorStateAccessDenied 0x805 +#define HidUsage_Sen_SensorStateError 0x806 +#define HidUsage_Sen_SensorEventUnknown 0x810 +#define HidUsage_Sen_SensorEventStateChanged 0x811 +#define HidUsage_Sen_SensorEventPropertyChanged 0x812 +#define HidUsage_Sen_SensorEventDataUpdated 0x813 +#define HidUsage_Sen_SensorEventPollResponse 0x814 +#define HidUsage_Sen_SensorEventChangeSensitivity 0x815 +#define HidUsage_Sen_SensorEventRangeMaximumReached 0x816 +#define HidUsage_Sen_SensorEventRangeMinimumReached 0x817 +#define HidUsage_Sen_SensorEventHighThresholdCrossUpward 0x818 +#define HidUsage_Sen_SensorEventHighThresholdCrossDownward 0x819 +#define HidUsage_Sen_SensorEventLowThresholdCrossUpward 0x81a +#define HidUsage_Sen_SensorEventLowThresholdCrossDownward 0x81b +#define HidUsage_Sen_SensorEventZeroThresholdCrossUpward 0x81c +#define HidUsage_Sen_SensorEventZeroThresholdCrossDownward 0x81d +#define HidUsage_Sen_SensorEventPeriodExceeded 0x81e +#define HidUsage_Sen_SensorEventFrequencyExceeded 0x81f +#define HidUsage_Sen_SensorEventComplexTrigger 0x820 +#define HidUsage_Sen_ConnectionTypePCIntegrated 0x830 +#define HidUsage_Sen_ConnectionTypePCAttached 0x831 +#define HidUsage_Sen_ConnectionTypePCExternal 0x832 +#define HidUsage_Sen_ReportingStateReportNoEvents 0x840 +#define HidUsage_Sen_ReportingStateReportAllEvents 0x841 +#define HidUsage_Sen_ReportingStateReportThresholdEvents 0x842 +#define HidUsage_Sen_ReportingStateWakeOnNoEvents 0x843 +#define HidUsage_Sen_ReportingStateWakeOnAllEvents 0x844 +#define HidUsage_Sen_ReportingStateWakeOnThresholdEvents 0x845 +#define HidUsage_Sen_ReportingStateAnytime 0x846 +#define HidUsage_Sen_PowerStateUndefined 0x850 +#define HidUsage_Sen_PowerStateD0FullPower 0x851 +#define HidUsage_Sen_PowerStateD1LowPower 0x852 +#define HidUsage_Sen_PowerStateD2StandbyPowerwithWakeup 0x853 +#define HidUsage_Sen_PowerStateD3SleepwithWakeup 0x854 +#define HidUsage_Sen_PowerStateD4PowerOff 0x855 +#define HidUsage_Sen_AccuracyDefault 0x860 +#define HidUsage_Sen_AccuracyHigh 0x861 +#define HidUsage_Sen_AccuracyMedium 0x862 +#define HidUsage_Sen_AccuracyLow 0x863 +#define HidUsage_Sen_FixQualityNoFix 0x870 +#define HidUsage_Sen_FixQualityGPS 0x871 +#define HidUsage_Sen_FixQualityDGPS 0x872 +#define HidUsage_Sen_FixTypeNoFix 0x880 +#define HidUsage_Sen_FixTypeGPSSPSModeFixValid 0x881 +#define HidUsage_Sen_FixTypeDGPSSPSModeFixValid 0x882 +#define HidUsage_Sen_FixTypeGPSPPSModeFixValid 0x883 +#define HidUsage_Sen_FixTypeRealTimeKinematic 0x884 +#define HidUsage_Sen_FixTypeFloatRTK 0x885 +#define HidUsage_Sen_FixTypeEstimateddeadreckoned 0x886 +#define HidUsage_Sen_FixTypeManualInputMode 0x887 +#define HidUsage_Sen_FixTypeSimulatorMode 0x888 +#define HidUsage_Sen_GPSOperationModeManual 0x890 +#define HidUsage_Sen_GPSOperationModeAutomatic 0x891 +#define HidUsage_Sen_GPSSelectionModeAutonomous 0x8a0 +#define HidUsage_Sen_GPSSelectionModeDGPS 0x8a1 +#define HidUsage_Sen_GPSSelectionModeEstimateddeadreckoned 0x8a2 +#define HidUsage_Sen_GPSSelectionModeManualInput 0x8a3 +#define HidUsage_Sen_GPSSelectionModeSimulator 0x8a4 +#define HidUsage_Sen_GPSSelectionModeDataNotValid 0x8a5 +#define HidUsage_Sen_GPSStatusDataValid 0x8b0 +#define HidUsage_Sen_GPSStatusDataNotValid 0x8b1 +#define HidUsage_Sen_DayofWeekSunday 0x8c0 +#define HidUsage_Sen_DayofWeekMonday 0x8c1 +#define HidUsage_Sen_DayofWeekTuesday 0x8c2 +#define HidUsage_Sen_DayofWeekWednesday 0x8c3 +#define HidUsage_Sen_DayofWeekThursday 0x8c4 +#define HidUsage_Sen_DayofWeekFriday 0x8c5 +#define HidUsage_Sen_DayofWeekSaturday 0x8c6 +#define HidUsage_Sen_KindCategory 0x8d0 +#define HidUsage_Sen_KindType 0x8d1 +#define HidUsage_Sen_KindEvent 0x8d2 +#define HidUsage_Sen_KindProperty 0x8d3 +#define HidUsage_Sen_KindDataField 0x8d4 +#define HidUsage_Sen_MagnetometerAccuracyLow 0x8e0 +#define HidUsage_Sen_MagnetometerAccuracyMedium 0x8e1 +#define HidUsage_Sen_MagnetometerAccuracyHigh 0x8e2 +#define HidUsage_Sen_SimpleOrientationDirectionNotRotated 0x8f0 +#define HidUsage_Sen_SimpleOrientationDirectionRotated90DegreesCCW 0x8f1 +#define HidUsage_Sen_SimpleOrientationDirectionRotated180DegreesCCW 0x8f2 +#define HidUsage_Sen_SimpleOrientationDirectionRotated270DegreesCCW 0x8f3 +#define HidUsage_Sen_SimpleOrientationDirectionFaceUp 0x8f4 +#define HidUsage_Sen_SimpleOrientationDirectionFaceDown 0x8f5 +#define HidUsage_Sen_VT_NULL 0x900 +#define HidUsage_Sen_VT_BOOL 0x901 +#define HidUsage_Sen_VT_UI1 0x902 +#define HidUsage_Sen_VT_I1 0x903 +#define HidUsage_Sen_VT_UI2 0x904 +#define HidUsage_Sen_VT_I2 0x905 +#define HidUsage_Sen_VT_UI4 0x906 +#define HidUsage_Sen_VT_I4 0x907 +#define HidUsage_Sen_VT_UI8 0x908 +#define HidUsage_Sen_VT_I8 0x909 +#define HidUsage_Sen_VT_R4 0x90a +#define HidUsage_Sen_VT_R8 0x90b +#define HidUsage_Sen_VT_WSTR 0x90c +#define HidUsage_Sen_VT_STR 0x90d +#define HidUsage_Sen_VT_CLSID 0x90e +#define HidUsage_Sen_VT_VECTORVT_UI1 0x90f +#define HidUsage_Sen_VT_F16E0 0x910 +#define HidUsage_Sen_VT_F16E1 0x911 +#define HidUsage_Sen_VT_F16E2 0x912 +#define HidUsage_Sen_VT_F16E3 0x913 +#define HidUsage_Sen_VT_F16E4 0x914 +#define HidUsage_Sen_VT_F16E5 0x915 +#define HidUsage_Sen_VT_F16E6 0x916 +#define HidUsage_Sen_VT_F16E7 0x917 +#define HidUsage_Sen_VT_F16E8 0x918 +#define HidUsage_Sen_VT_F16E9 0x919 +#define HidUsage_Sen_VT_F16EA 0x91a +#define HidUsage_Sen_VT_F16EB 0x91b +#define HidUsage_Sen_VT_F16EC 0x91c +#define HidUsage_Sen_VT_F16ED 0x91d +#define HidUsage_Sen_VT_F16EE 0x91e +#define HidUsage_Sen_VT_F16EF 0x91f +#define HidUsage_Sen_VT_F32E0 0x920 +#define HidUsage_Sen_VT_F32E1 0x921 +#define HidUsage_Sen_VT_F32E2 0x922 +#define HidUsage_Sen_VT_F32E3 0x923 +#define HidUsage_Sen_VT_F32E4 0x924 +#define HidUsage_Sen_VT_F32E5 0x925 +#define HidUsage_Sen_VT_F32E6 0x926 +#define HidUsage_Sen_VT_F32E7 0x927 +#define HidUsage_Sen_VT_F32E8 0x928 +#define HidUsage_Sen_VT_F32E9 0x929 +#define HidUsage_Sen_VT_F32EA 0x92a +#define HidUsage_Sen_VT_F32EB 0x92b +#define HidUsage_Sen_VT_F32EC 0x92c +#define HidUsage_Sen_VT_F32ED 0x92d +#define HidUsage_Sen_VT_F32EE 0x92e +#define HidUsage_Sen_VT_F32EF 0x92f +#define HidUsage_Sen_ActivityTypeUnknown 0x930 +#define HidUsage_Sen_ActivityTypeStationary 0x931 +#define HidUsage_Sen_ActivityTypeFidgeting 0x932 +#define HidUsage_Sen_ActivityTypeWalking 0x933 +#define HidUsage_Sen_ActivityTypeRunning 0x934 +#define HidUsage_Sen_ActivityTypeInVehicle 0x935 +#define HidUsage_Sen_ActivityTypeBiking 0x936 +#define HidUsage_Sen_ActivityTypeIdle 0x937 +#define HidUsage_Sen_UnitNotSpecified 0x940 +#define HidUsage_Sen_UnitLux 0x941 +#define HidUsage_Sen_UnitDegreesKelvin 0x942 +#define HidUsage_Sen_UnitDegreesCelsius 0x943 +#define HidUsage_Sen_UnitPascal 0x944 +#define HidUsage_Sen_UnitNewton 0x945 +#define HidUsage_Sen_UnitMetersSecond 0x946 +#define HidUsage_Sen_UnitKilogram 0x947 +#define HidUsage_Sen_UnitMeter 0x948 +#define HidUsage_Sen_UnitMetersSecondSecond 0x949 +#define HidUsage_Sen_UnitFarad 0x94a +#define HidUsage_Sen_UnitAmpere 0x94b +#define HidUsage_Sen_UnitWatt 0x94c +#define HidUsage_Sen_UnitHenry 0x94d +#define HidUsage_Sen_UnitOhm 0x94e +#define HidUsage_Sen_UnitVolt 0x94f +#define HidUsage_Sen_UnitHertz 0x950 +#define HidUsage_Sen_UnitBar 0x951 +#define HidUsage_Sen_UnitDegreesAnticlockwise 0x952 +#define HidUsage_Sen_UnitDegreesClockwise 0x953 +#define HidUsage_Sen_UnitDegrees 0x954 +#define HidUsage_Sen_UnitDegreesSecond 0x955 +#define HidUsage_Sen_UnitDegreesSecondSecond 0x956 +#define HidUsage_Sen_UnitKnot 0x957 +#define HidUsage_Sen_UnitPercent 0x958 +#define HidUsage_Sen_UnitSecond 0x959 +#define HidUsage_Sen_UnitMillisecond 0x95a +#define HidUsage_Sen_UnitG 0x95b +#define HidUsage_Sen_UnitBytes 0x95c +#define HidUsage_Sen_UnitMilligauss 0x95d +#define HidUsage_Sen_UnitBits 0x95e +#define HidUsage_Sen_ActivityStateNoStateChange 0x960 +#define HidUsage_Sen_ActivityStateStartActivity 0x961 +#define HidUsage_Sen_ActivityStateEndActivity 0x962 +#define HidUsage_Sen_Exponent0 0x970 +#define HidUsage_Sen_Exponent1 0x971 +#define HidUsage_Sen_Exponent2 0x972 +#define HidUsage_Sen_Exponent3 0x973 +#define HidUsage_Sen_Exponent4 0x974 +#define HidUsage_Sen_Exponent5 0x975 +#define HidUsage_Sen_Exponent6 0x976 +#define HidUsage_Sen_Exponent7 0x977 +#define HidUsage_Sen_Exponent8 0x978 +#define HidUsage_Sen_Exponent9 0x979 +#define HidUsage_Sen_ExponentA 0x97a +#define HidUsage_Sen_ExponentB 0x97b +#define HidUsage_Sen_ExponentC 0x97c +#define HidUsage_Sen_ExponentD 0x97d +#define HidUsage_Sen_ExponentE 0x97e +#define HidUsage_Sen_ExponentF 0x97f +#define HidUsage_Sen_DevicePositionUnknown 0x980 +#define HidUsage_Sen_DevicePositionUnchanged 0x981 +#define HidUsage_Sen_DevicePositionOnDesk 0x982 +#define HidUsage_Sen_DevicePositionInHand 0x983 +#define HidUsage_Sen_DevicePositionMovinginBag 0x984 +#define HidUsage_Sen_DevicePositionStationaryinBag 0x985 +#define HidUsage_Sen_StepTypeUnknown 0x990 +#define HidUsage_Sen_StepTypeWalking 0x991 +#define HidUsage_Sen_StepTypeRunning 0x992 +#define HidUsage_Sen_GestureStateUnknown 0x9a0 +#define HidUsage_Sen_GestureStateStarted 0x9a1 +#define HidUsage_Sen_GestureStateCompleted 0x9a2 +#define HidUsage_Sen_GestureStateCancelled 0x9a3 +#define HidUsage_Sen_HingeFoldContributingPanelUnknown 0x9b0 +#define HidUsage_Sen_HingeFoldContributingPanelPanel1 0x9b1 +#define HidUsage_Sen_HingeFoldContributingPanelPanel2 0x9b2 +#define HidUsage_Sen_HingeFoldContributingPanelBoth 0x9b3 +#define HidUsage_Sen_HingeFoldTypeUnknown 0x9b4 +#define HidUsage_Sen_HingeFoldTypeIncreasing 0x9b5 +#define HidUsage_Sen_HingeFoldTypeDecreasing 0x9b6 +#define HidUsage_Sen_HumanPresenceDetectionTypeVendorDefinedNonBiometric 0x9c0 +#define HidUsage_Sen_HumanPresenceDetectionTypeVendorDefinedBiometric 0x9c1 +#define HidUsage_Sen_HumanPresenceDetectionTypeFacialBiometric 0x9c2 +#define HidUsage_Sen_HumanPresenceDetectionTypeAudioBiometric 0x9c3 +#define HidUsage_Sen_ModifierChangeSensitivityAbsolute 0x1000 +#define HidUsage_Sen_ModifierMaximum 0x2000 +#define HidUsage_Sen_ModifierMinimum 0x3000 +#define HidUsage_Sen_ModifierAccuracy 0x4000 +#define HidUsage_Sen_ModifierResolution 0x5000 +#define HidUsage_Sen_ModifierThresholdHigh 0x6000 +#define HidUsage_Sen_ModifierThresholdLow 0x7000 +#define HidUsage_Sen_ModifierCalibrationOffset 0x8000 +#define HidUsage_Sen_ModifierCalibrationMultiplier 0x9000 +#define HidUsage_Sen_ModifierReportInterval 0xa000 +#define HidUsage_Sen_ModifierFrequencyMax 0xb000 +#define HidUsage_Sen_ModifierPeriodMax 0xc000 +#define HidUsage_Sen_ModifierChangeSensitivityPercentofRange 0xd000 +#define HidUsage_Sen_ModifierChangeSensitivityPercentRelative 0xe000 +#define HidUsage_Sen_ModifierVendorReserved 0xf000 +#define HidUsage_MI_MedicalUltrasound 0x01 +#define HidUsage_MI_VCRAcquisition 0x20 +#define HidUsage_MI_FreezeThaw 0x21 +#define HidUsage_MI_ClipStore 0x22 +#define HidUsage_MI_Update 0x23 +#define HidUsage_MI_Next 0x24 +#define HidUsage_MI_Save 0x25 +#define HidUsage_MI_Print 0x26 +#define HidUsage_MI_MicrophoneEnable 0x27 +#define HidUsage_MI_Cine 0x40 +#define HidUsage_MI_TransmitPower 0x41 +#define HidUsage_MI_Volume 0x42 +#define HidUsage_MI_Focus 0x43 +#define HidUsage_MI_Depth 0x44 +#define HidUsage_MI_SoftStepPrimary 0x60 +#define HidUsage_MI_SoftStepSecondary 0x61 +#define HidUsage_MI_DepthGainCompensation 0x70 +#define HidUsage_MI_ZoomSelect 0x80 +#define HidUsage_MI_ZoomAdjust 0x81 +#define HidUsage_MI_SpectralDopplerModeSelect 0x82 +#define HidUsage_MI_SpectralDopplerAdjust 0x83 +#define HidUsage_MI_ColorDopplerModeSelect 0x84 +#define HidUsage_MI_ColorDopplerAdjust 0x85 +#define HidUsage_MI_MotionModeSelect 0x86 +#define HidUsage_MI_MotionModeAdjust 0x87 +#define HidUsage_MI_TwoDModeSelect 0x88 +#define HidUsage_MI_TwoDModeAdjust 0x89 +#define HidUsage_MI_SoftControlSelect 0xa0 +#define HidUsage_MI_SoftControlAdjust 0xa1 +#define HidUsage_BD_BrailleDisplay 0x01 +#define HidUsage_BD_BrailleRow 0x02 +#define HidUsage_BD_EightDotBrailleCell 0x03 +#define HidUsage_BD_SixDotBrailleCell 0x04 +#define HidUsage_BD_NumberofBrailleCells 0x05 +#define HidUsage_BD_ScreenReaderControl 0x06 +#define HidUsage_BD_ScreenReaderIdentifier 0x07 +#define HidUsage_BD_RouterSet1 0xfa +#define HidUsage_BD_RouterSet2 0xfb +#define HidUsage_BD_RouterSet3 0xfc +#define HidUsage_BD_RouterKey 0x100 +#define HidUsage_BD_RowRouterKey 0x101 +#define HidUsage_BD_BrailleButtons 0x200 +#define HidUsage_BD_BrailleKeyboardDot1 0x201 +#define HidUsage_BD_BrailleKeyboardDot2 0x202 +#define HidUsage_BD_BrailleKeyboardDot3 0x203 +#define HidUsage_BD_BrailleKeyboardDot4 0x204 +#define HidUsage_BD_BrailleKeyboardDot5 0x205 +#define HidUsage_BD_BrailleKeyboardDot6 0x206 +#define HidUsage_BD_BrailleKeyboardDot7 0x207 +#define HidUsage_BD_BrailleKeyboardDot8 0x208 +#define HidUsage_BD_BrailleKeyboardSpace 0x209 +#define HidUsage_BD_BrailleKeyboardLeftSpace 0x20a +#define HidUsage_BD_BrailleKeyboardRightSpace 0x20b +#define HidUsage_BD_BrailleFaceControls 0x20c +#define HidUsage_BD_BrailleLeftControls 0x20d +#define HidUsage_BD_BrailleRightControls 0x20e +#define HidUsage_BD_BrailleTopControls 0x20f +#define HidUsage_BD_BrailleJoystickCenter 0x210 +#define HidUsage_BD_BrailleJoystickUp 0x211 +#define HidUsage_BD_BrailleJoystickDown 0x212 +#define HidUsage_BD_BrailleJoystickLeft 0x213 +#define HidUsage_BD_BrailleJoystickRight 0x214 +#define HidUsage_BD_BrailleDPadCenter 0x215 +#define HidUsage_BD_BrailleDPadUp 0x216 +#define HidUsage_BD_BrailleDPadDown 0x217 +#define HidUsage_BD_BrailleDPadLeft 0x218 +#define HidUsage_BD_BrailleDPadRight 0x219 +#define HidUsage_BD_BraillePanLeft 0x21a +#define HidUsage_BD_BraillePanRight 0x21b +#define HidUsage_BD_BrailleRockerUp 0x21c +#define HidUsage_BD_BrailleRockerDown 0x21d +#define HidUsage_BD_BrailleRockerPress 0x21e +#define HidUsage_LAI_LampArray 0x01 +#define HidUsage_LAI_LampArrayAttributesReport 0x02 +#define HidUsage_LAI_LampCount 0x03 +#define HidUsage_LAI_BoundingBoxWidthInMicrometers 0x04 +#define HidUsage_LAI_BoundingBoxHeightInMicrometers 0x05 +#define HidUsage_LAI_BoundingBoxDepthInMicrometers 0x06 +#define HidUsage_LAI_LampArrayKind 0x07 +#define HidUsage_LAI_MinUpdateIntervalInMicroseconds 0x08 +#define HidUsage_LAI_LampAttributesRequestReport 0x20 +#define HidUsage_LAI_LampId 0x21 +#define HidUsage_LAI_LampAttributesResponseReport 0x22 +#define HidUsage_LAI_PositionXInMicrometers 0x23 +#define HidUsage_LAI_PositionYInMicrometers 0x24 +#define HidUsage_LAI_PositionZInMicrometers 0x25 +#define HidUsage_LAI_LampPurposes 0x26 +#define HidUsage_LAI_UpdateLatencyInMicroseconds 0x27 +#define HidUsage_LAI_RedLevelCount 0x28 +#define HidUsage_LAI_GreenLevelCount 0x29 +#define HidUsage_LAI_BlueLevelCount 0x2a +#define HidUsage_LAI_IntensityLevelCount 0x2b +#define HidUsage_LAI_IsProgrammable 0x2c +#define HidUsage_LAI_InputBinding 0x2d +#define HidUsage_LAI_LampMultiUpdateReport 0x50 +#define HidUsage_LAI_RedUpdateChannel 0x51 +#define HidUsage_LAI_GreenUpdateChannel 0x52 +#define HidUsage_LAI_BlueUpdateChannel 0x53 +#define HidUsage_LAI_IntensityUpdateChannel 0x54 +#define HidUsage_LAI_LampUpdateFlags 0x55 +#define HidUsage_LAI_LampRangeUpdateReport 0x60 +#define HidUsage_LAI_LampIdStart 0x61 +#define HidUsage_LAI_LampIdEnd 0x62 +#define HidUsage_LAI_LampArrayControlReport 0x70 +#define HidUsage_LAI_AutonomousMode 0x71 +#define HidUsage_Mon_MonitorControl 0x01 +#define HidUsage_Mon_EDIDInformation 0x02 +#define HidUsage_Mon_VDIFInformation 0x03 +#define HidUsage_Mon_VESAVersion 0x04 +#define HidUsage_VESAVC_Degauss 0x01 +#define HidUsage_VESAVC_Brightness 0x10 +#define HidUsage_VESAVC_Contrast 0x12 +#define HidUsage_VESAVC_RedVideoGain 0x16 +#define HidUsage_VESAVC_GreenVideoGain 0x18 +#define HidUsage_VESAVC_BlueVideoGain 0x1a +#define HidUsage_VESAVC_Focus 0x1c +#define HidUsage_VESAVC_HorizontalPosition 0x20 +#define HidUsage_VESAVC_HorizontalSize 0x22 +#define HidUsage_VESAVC_HorizontalPincushion 0x24 +#define HidUsage_VESAVC_HorizontalPincushionBalance 0x26 +#define HidUsage_VESAVC_HorizontalMisconvergence 0x28 +#define HidUsage_VESAVC_HorizontalLinearity 0x2a +#define HidUsage_VESAVC_HorizontalLinearityBalance 0x2c +#define HidUsage_VESAVC_VerticalPosition 0x30 +#define HidUsage_VESAVC_VerticalSize 0x32 +#define HidUsage_VESAVC_VerticalPincushion 0x34 +#define HidUsage_VESAVC_VerticalPincushionBalance 0x36 +#define HidUsage_VESAVC_VerticalMisconvergence 0x38 +#define HidUsage_VESAVC_VerticalLinearity 0x3a +#define HidUsage_VESAVC_VerticalLinearityBalance 0x3c +#define HidUsage_VESAVC_ParallelogramDistortionKeyBalance 0x40 +#define HidUsage_VESAVC_TrapezoidalDistortionKey 0x42 +#define HidUsage_VESAVC_TiltRotation 0x44 +#define HidUsage_VESAVC_TopCornerDistortionControl 0x46 +#define HidUsage_VESAVC_TopCornerDistortionBalance 0x48 +#define HidUsage_VESAVC_BottomCornerDistortionControl 0x4a +#define HidUsage_VESAVC_BottomCornerDistortionBalance 0x4c +#define HidUsage_VESAVC_HorizontalMoiré 0x56 +#define HidUsage_VESAVC_VerticalMoiré 0x58 +#define HidUsage_VESAVC_InputLevelSelect 0x5e +#define HidUsage_VESAVC_InputSourceSelect 0x60 +#define HidUsage_VESAVC_RedVideoBlackLevel 0x6c +#define HidUsage_VESAVC_GreenVideoBlackLevel 0x6e +#define HidUsage_VESAVC_BlueVideoBlackLevel 0x70 +#define HidUsage_VESAVC_AutoSizeCenter 0xa2 +#define HidUsage_VESAVC_PolarityHorizontalSynchronization 0xa4 +#define HidUsage_VESAVC_PolarityVerticalSynchronization 0xa6 +#define HidUsage_VESAVC_SynchronizationType 0xa8 +#define HidUsage_VESAVC_ScreenOrientation 0xaa +#define HidUsage_VESAVC_HorizontalFrequency 0xac +#define HidUsage_VESAVC_VerticalFrequency 0xae +#define HidUsage_VESAVC_Settings 0xb0 +#define HidUsage_VESAVC_OnScreenDisplay 0xca +#define HidUsage_VESAVC_StereoMode 0xd4 +#define HidUsage_Pow_iName 0x01 +#define HidUsage_Pow_PresentStatus 0x02 +#define HidUsage_Pow_ChangedStatus 0x03 +#define HidUsage_Pow_UPS 0x04 +#define HidUsage_Pow_PowerSupply 0x05 +#define HidUsage_Pow_BatterySystem 0x10 +#define HidUsage_Pow_BatterySystemId 0x11 +#define HidUsage_Pow_Battery 0x12 +#define HidUsage_Pow_BatteryId 0x13 +#define HidUsage_Pow_Charger 0x14 +#define HidUsage_Pow_ChargerId 0x15 +#define HidUsage_Pow_PowerConverter 0x16 +#define HidUsage_Pow_PowerConverterId 0x17 +#define HidUsage_Pow_OutletSystem 0x18 +#define HidUsage_Pow_OutletSystemId 0x19 +#define HidUsage_Pow_Input 0x1a +#define HidUsage_Pow_InputId 0x1b +#define HidUsage_Pow_Output 0x1c +#define HidUsage_Pow_OutputId 0x1d +#define HidUsage_Pow_Flow 0x1e +#define HidUsage_Pow_FlowId 0x1f +#define HidUsage_Pow_Outlet 0x20 +#define HidUsage_Pow_OutletId 0x21 +#define HidUsage_Pow_Gang 0x22 +#define HidUsage_Pow_GangId 0x23 +#define HidUsage_Pow_PowerSummary 0x24 +#define HidUsage_Pow_PowerSummaryId 0x25 +#define HidUsage_Pow_Voltage 0x30 +#define HidUsage_Pow_Current 0x31 +#define HidUsage_Pow_Frequency 0x32 +#define HidUsage_Pow_ApparentPower 0x33 +#define HidUsage_Pow_ActivePower 0x34 +#define HidUsage_Pow_PercentLoad 0x35 +#define HidUsage_Pow_Temperature 0x36 +#define HidUsage_Pow_Humidity 0x37 +#define HidUsage_Pow_BadCount 0x38 +#define HidUsage_Pow_ConfigVoltage 0x40 +#define HidUsage_Pow_ConfigCurrent 0x41 +#define HidUsage_Pow_ConfigFrequency 0x42 +#define HidUsage_Pow_ConfigApparentPower 0x43 +#define HidUsage_Pow_ConfigActivePower 0x44 +#define HidUsage_Pow_ConfigPercentLoad 0x45 +#define HidUsage_Pow_ConfigTemperature 0x46 +#define HidUsage_Pow_ConfigHumidity 0x47 +#define HidUsage_Pow_SwitchOnControl 0x50 +#define HidUsage_Pow_SwitchOffControl 0x51 +#define HidUsage_Pow_ToggleControl 0x52 +#define HidUsage_Pow_LowVoltageTransfer 0x53 +#define HidUsage_Pow_HighVoltageTransfer 0x54 +#define HidUsage_Pow_DelayBeforeReboot 0x55 +#define HidUsage_Pow_DelayBeforeStartup 0x56 +#define HidUsage_Pow_DelayBeforeShutdown 0x57 +#define HidUsage_Pow_Test 0x58 +#define HidUsage_Pow_ModuleReset 0x59 +#define HidUsage_Pow_AudibleAlarmControl 0x5a +#define HidUsage_Pow_Present 0x60 +#define HidUsage_Pow_Good 0x61 +#define HidUsage_Pow_InternalFailure 0x62 +#define HidUsage_Pow_VoltagOutOfRange 0x63 +#define HidUsage_Pow_FrequencyOutOfRange 0x64 +#define HidUsage_Pow_Overload 0x65 +#define HidUsage_Pow_OverCharged 0x66 +#define HidUsage_Pow_OverTemperature 0x67 +#define HidUsage_Pow_ShutdownRequested 0x68 +#define HidUsage_Pow_ShutdownImminent 0x69 +#define HidUsage_Pow_SwitchOnOff 0x6b +#define HidUsage_Pow_Switchable 0x6c +#define HidUsage_Pow_Used 0x6d +#define HidUsage_Pow_Boost 0x6e +#define HidUsage_Pow_Buck 0x6f +#define HidUsage_Pow_Initialized 0x70 +#define HidUsage_Pow_Tested 0x71 +#define HidUsage_Pow_AwaitingPower 0x72 +#define HidUsage_Pow_CommunicationLost 0x73 +#define HidUsage_Pow_iManufacturer 0xfd +#define HidUsage_Pow_iProduct 0xfe +#define HidUsage_Pow_iSerialNumber 0xff +#define HidUsage_BS_SmartBatteryBatteryMode 0x01 +#define HidUsage_BS_SmartBatteryBatteryStatus 0x02 +#define HidUsage_BS_SmartBatteryAlarmWarning 0x03 +#define HidUsage_BS_SmartBatteryChargerMode 0x04 +#define HidUsage_BS_SmartBatteryChargerStatus 0x05 +#define HidUsage_BS_SmartBatteryChargerSpecInfo 0x06 +#define HidUsage_BS_SmartBatterySelectorState 0x07 +#define HidUsage_BS_SmartBatterySelectorPresets 0x08 +#define HidUsage_BS_SmartBatterySelectorInfo 0x09 +#define HidUsage_BS_OptionalMfgFunction1 0x10 +#define HidUsage_BS_OptionalMfgFunction2 0x11 +#define HidUsage_BS_OptionalMfgFunction3 0x12 +#define HidUsage_BS_OptionalMfgFunction4 0x13 +#define HidUsage_BS_OptionalMfgFunction5 0x14 +#define HidUsage_BS_ConnectionToSMBus 0x15 +#define HidUsage_BS_OutputConnection 0x16 +#define HidUsage_BS_ChargerConnection 0x17 +#define HidUsage_BS_BatteryInsertion 0x18 +#define HidUsage_BS_UseNext 0x19 +#define HidUsage_BS_OKToUse 0x1a +#define HidUsage_BS_BatterySupported 0x1b +#define HidUsage_BS_SelectorRevision 0x1c +#define HidUsage_BS_ChargingIndicator 0x1d +#define HidUsage_BS_ManufacturerAccess 0x28 +#define HidUsage_BS_RemainingCapacityLimit 0x29 +#define HidUsage_BS_RemainingTimeLimit 0x2a +#define HidUsage_BS_AtRate 0x2b +#define HidUsage_BS_CapacityMode 0x2c +#define HidUsage_BS_BroadcastToCharger 0x2d +#define HidUsage_BS_PrimaryBattery 0x2e +#define HidUsage_BS_ChargeController 0x2f +#define HidUsage_BS_TerminateCharge 0x40 +#define HidUsage_BS_TerminateDischarge 0x41 +#define HidUsage_BS_BelowRemainingCapacityLimit 0x42 +#define HidUsage_BS_RemainingTimeLimitExpired 0x43 +#define HidUsage_BS_Charging 0x44 +#define HidUsage_BS_Discharging 0x45 +#define HidUsage_BS_FullyCharged 0x46 +#define HidUsage_BS_FullyDischarged 0x47 +#define HidUsage_BS_ConditioningFlag 0x48 +#define HidUsage_BS_AtRateOK 0x49 +#define HidUsage_BS_SmartBatteryErrorCode 0x4a +#define HidUsage_BS_NeedReplacement 0x4b +#define HidUsage_BS_AtRateTimeToFull 0x60 +#define HidUsage_BS_AtRateTimeToEmpty 0x61 +#define HidUsage_BS_AverageCurrent 0x62 +#define HidUsage_BS_MaxError 0x63 +#define HidUsage_BS_RelativeStateOfCharge 0x64 +#define HidUsage_BS_AbsoluteStateOfCharge 0x65 +#define HidUsage_BS_RemainingCapacity 0x66 +#define HidUsage_BS_FullChargeCapacity 0x67 +#define HidUsage_BS_RunTimeToEmpty 0x68 +#define HidUsage_BS_AverageTimeToEmpty 0x69 +#define HidUsage_BS_AverageTimeToFull 0x6a +#define HidUsage_BS_CycleCount 0x6b +#define HidUsage_BS_BatteryPackModelLevel 0x80 +#define HidUsage_BS_InternalChargeController 0x81 +#define HidUsage_BS_PrimaryBatterySupport 0x82 +#define HidUsage_BS_DesignCapacity 0x83 +#define HidUsage_BS_SpecificationInfo 0x84 +#define HidUsage_BS_ManufactureDate 0x85 +#define HidUsage_BS_SerialNumber 0x86 +#define HidUsage_BS_iManufacturerName 0x87 +#define HidUsage_BS_iDeviceName 0x88 +#define HidUsage_BS_iDeviceChemistry 0x89 +#define HidUsage_BS_ManufacturerData 0x8a +#define HidUsage_BS_Rechargeable 0x8b +#define HidUsage_BS_WarningCapacityLimit 0x8c +#define HidUsage_BS_CapacityGranularity1 0x8d +#define HidUsage_BS_CapacityGranularity2 0x8e +#define HidUsage_BS_iOEMInformation 0x8f +#define HidUsage_BS_InhibitCharge 0xc0 +#define HidUsage_BS_EnablePolling 0xc1 +#define HidUsage_BS_ResetToZero 0xc2 +#define HidUsage_BS_ACPresent 0xd0 +#define HidUsage_BS_BatteryPresent 0xd1 +#define HidUsage_BS_PowerFail 0xd2 +#define HidUsage_BS_AlarmInhibited 0xd3 +#define HidUsage_BS_ThermistorUnderRange 0xd4 +#define HidUsage_BS_ThermistorHot 0xd5 +#define HidUsage_BS_ThermistorCold 0xd6 +#define HidUsage_BS_ThermistorOverRange 0xd7 +#define HidUsage_BS_VoltageOutOfRange 0xd8 +#define HidUsage_BS_CurrentOutOfRange 0xd9 +#define HidUsage_BS_CurrentNotRegulated 0xda +#define HidUsage_BS_VoltageNotRegulated 0xdb +#define HidUsage_BS_MasterMode 0xdc +#define HidUsage_BS_ChargerSelectorSupport 0xf0 +#define HidUsage_BS_ChargerSpec 0xf1 +#define HidUsage_BS_Level2 0xf2 +#define HidUsage_BS_Level3 0xf3 +#define HidUsage_BS_BarcodeBadgeReader 0x01 +#define HidUsage_BS_BarcodeScanner 0x02 +#define HidUsage_BS_DumbBarCodeScanner 0x03 +#define HidUsage_BS_CordlessScannerBase 0x04 +#define HidUsage_BS_BarCodeScannerCradle 0x05 +#define HidUsage_BS_AttributeReport 0x10 +#define HidUsage_BS_SettingsReport 0x11 +#define HidUsage_BS_ScannedDataReport 0x12 +#define HidUsage_BS_RawScannedDataReport 0x13 +#define HidUsage_BS_TriggerReport 0x14 +#define HidUsage_BS_StatusReport 0x15 +#define HidUsage_BS_UPCEANControlReport 0x16 +#define HidUsage_BS_EAN23LabelControlReport 0x17 +#define HidUsage_BS_Code39ControlReport 0x18 +#define HidUsage_BS_Interleaved2of5ControlReport 0x19 +#define HidUsage_BS_Standard2of5ControlReport 0x1a +#define HidUsage_BS_MSIPlesseyControlReport 0x1b +#define HidUsage_BS_CodabarControlReport 0x1c +#define HidUsage_BS_Code128ControlReport 0x1d +#define HidUsage_BS_Misc1DControlReport 0x1e +#define HidUsage_BS_TwoDControlReport 0x1f +#define HidUsage_BS_AimingPointerMode 0x30 +#define HidUsage_BS_BarCodePresentSensor 0x31 +#define HidUsage_BS_Class1ALaser 0x32 +#define HidUsage_BS_Class2Laser 0x33 +#define HidUsage_BS_HeaterPresent 0x34 +#define HidUsage_BS_ContactScanner 0x35 +#define HidUsage_BS_ElectronicArticleSurveillanceNotification 0x36 +#define HidUsage_BS_ConstantElectronicArticleSurveillance 0x37 +#define HidUsage_BS_ErrorIndication 0x38 +#define HidUsage_BS_FixedBeeper 0x39 +#define HidUsage_BS_GoodDecodeIndication 0x3a +#define HidUsage_BS_HandsFreeScanning 0x3b +#define HidUsage_BS_IntrinsicallySafe 0x3c +#define HidUsage_BS_KlasseEinsLaser 0x3d +#define HidUsage_BS_LongRangeScanner 0x3e +#define HidUsage_BS_MirrorSpeedControl 0x3f +#define HidUsage_BS_NotOnFileIndication 0x40 +#define HidUsage_BS_ProgrammableBeeper 0x41 +#define HidUsage_BS_Triggerless 0x42 +#define HidUsage_BS_Wand 0x43 +#define HidUsage_BS_WaterResistant 0x44 +#define HidUsage_BS_MultiRangeScanner 0x45 +#define HidUsage_BS_ProximitySensor 0x46 +#define HidUsage_BS_FragmentDecoding 0x4d +#define HidUsage_BS_ScannerReadConfidence 0x4e +#define HidUsage_BS_DataPrefix 0x4f +#define HidUsage_BS_PrefixAIMI 0x50 +#define HidUsage_BS_PrefixNone 0x51 +#define HidUsage_BS_PrefixProprietary 0x52 +#define HidUsage_BS_ActiveTime 0x55 +#define HidUsage_BS_AimingLaserPattern 0x56 +#define HidUsage_BS_BarCodePresent 0x57 +#define HidUsage_BS_BeeperState 0x58 +#define HidUsage_BS_LaserOnTime 0x59 +#define HidUsage_BS_LaserState 0x5a +#define HidUsage_BS_LockoutTime 0x5b +#define HidUsage_BS_MotorState 0x5c +#define HidUsage_BS_MotorTimeout 0x5d +#define HidUsage_BS_PowerOnResetScanner 0x5e +#define HidUsage_BS_PreventReadofBarcodes 0x5f +#define HidUsage_BS_InitiateBarcodeRead 0x60 +#define HidUsage_BS_TriggerState 0x61 +#define HidUsage_BS_TriggerMode 0x62 +#define HidUsage_BS_TriggerModeBlinkingLaserOn 0x63 +#define HidUsage_BS_TriggerModeContinuousLaserOn 0x64 +#define HidUsage_BS_TriggerModeLaseronwhilePulled 0x65 +#define HidUsage_BS_TriggerModeLaserstaysonafterrelease 0x66 +#define HidUsage_BS_CommitParameterstoNVM 0x6d +#define HidUsage_BS_ParameterScanning 0x6e +#define HidUsage_BS_ParametersChanged 0x6f +#define HidUsage_BS_Setparameterdefaultvalues 0x70 +#define HidUsage_BS_ScannerInCradle 0x75 +#define HidUsage_BS_ScannerInRange 0x76 +#define HidUsage_BS_AimDuration 0x7a +#define HidUsage_BS_GoodReadLampDuration 0x7b +#define HidUsage_BS_GoodReadLampIntensity 0x7c +#define HidUsage_BS_GoodReadLED 0x7d +#define HidUsage_BS_GoodReadToneFrequency 0x7e +#define HidUsage_BS_GoodReadToneLength 0x7f +#define HidUsage_BS_GoodReadToneVolume 0x80 +#define HidUsage_BS_NoReadMessage 0x82 +#define HidUsage_BS_NotonFileVolume 0x83 +#define HidUsage_BS_PowerupBeep 0x84 +#define HidUsage_BS_SoundErrorBeep 0x85 +#define HidUsage_BS_SoundGoodReadBeep 0x86 +#define HidUsage_BS_SoundNotOnFileBeep 0x87 +#define HidUsage_BS_GoodReadWhentoWrite 0x88 +#define HidUsage_BS_GRWTIAfterDecode 0x89 +#define HidUsage_BS_GRWTIBeepLampaftertransmit 0x8a +#define HidUsage_BS_GRWTINoBeepLampuseatall 0x8b +#define HidUsage_BS_BooklandEAN 0x91 +#define HidUsage_BS_ConvertEAN8to13Type 0x92 +#define HidUsage_BS_ConvertUPCAtoEAN13 0x93 +#define HidUsage_BS_ConvertUPCEtoA 0x94 +#define HidUsage_BS_EAN13 0x95 +#define HidUsage_BS_EAN8 0x96 +#define HidUsage_BS_EAN99128Mandatory 0x97 +#define HidUsage_BS_EAN99P5128Optional 0x98 +#define HidUsage_BS_EnableEANTwoLabel 0x99 +#define HidUsage_BS_UPCEAN 0x9a +#define HidUsage_BS_UPCEANCouponCode 0x9b +#define HidUsage_BS_UPCEANPeriodicals 0x9c +#define HidUsage_BS_UPCA 0x9d +#define HidUsage_BS_UPCAwith128Mandatory 0x9e +#define HidUsage_BS_UPCAwith128Optional 0x9f +#define HidUsage_BS_UPCAwithP5Optional 0xa0 +#define HidUsage_BS_UPCE 0xa1 +#define HidUsage_BS_UPCE1 0xa2 +#define HidUsage_BS_Periodical 0xa9 +#define HidUsage_BS_PeriodicalAutoDiscriminatePlus2 0xaa +#define HidUsage_BS_PeriodicalOnlyDecodewithPlus2 0xab +#define HidUsage_BS_PeriodicalIgnorePlus2 0xac +#define HidUsage_BS_PeriodicalAutoDiscriminatePlus5 0xad +#define HidUsage_BS_PeriodicalOnlyDecodewithPlus5 0xae +#define HidUsage_BS_PeriodicalIgnorePlus5 0xaf +#define HidUsage_BS_Check 0xb0 +#define HidUsage_BS_CheckDisablePrice 0xb1 +#define HidUsage_BS_CheckEnable4digitPrice 0xb2 +#define HidUsage_BS_CheckEnable5digitPrice 0xb3 +#define HidUsage_BS_CheckEnableEuropean4digitPrice 0xb4 +#define HidUsage_BS_CheckEnableEuropean5digitPrice 0xb5 +#define HidUsage_BS_EANTwoLabel 0xb7 +#define HidUsage_BS_EANThreeLabel 0xb8 +#define HidUsage_BS_EAN8FlagDigit1 0xb9 +#define HidUsage_BS_EAN8FlagDigit2 0xba +#define HidUsage_BS_EAN8FlagDigit3 0xbb +#define HidUsage_BS_EAN13FlagDigit1 0xbc +#define HidUsage_BS_EAN13FlagDigit2 0xbd +#define HidUsage_BS_EAN13FlagDigit3 0xbe +#define HidUsage_BS_AddEAN23LabelDefinition 0xbf +#define HidUsage_BS_ClearallEAN23LabelDefinitions 0xc0 +#define HidUsage_BS_Codabar 0xc3 +#define HidUsage_BS_Code128 0xc4 +#define HidUsage_BS_Code39 0xc7 +#define HidUsage_BS_Code93 0xc8 +#define HidUsage_BS_FullASCIIConversion 0xc9 +#define HidUsage_BS_Interleaved2of5 0xca +#define HidUsage_BS_ItalianPharmacyCode 0xcb +#define HidUsage_BS_MSIPlessey 0xcc +#define HidUsage_BS_Standard2of5IATA 0xcd +#define HidUsage_BS_Standard2of5 0xce +#define HidUsage_BS_TransmitStartStop 0xd3 +#define HidUsage_BS_TriOptic 0xd4 +#define HidUsage_BS_UCCEAN128 0xd5 +#define HidUsage_BS_CheckDigit 0xd6 +#define HidUsage_BS_CheckDigitDisable 0xd7 +#define HidUsage_BS_CheckDigitEnableInterleaved2of5OPCC 0xd8 +#define HidUsage_BS_CheckDigitEnableInterleaved2of5USS 0xd9 +#define HidUsage_BS_CheckDigitEnableStandard2of5OPCC 0xda +#define HidUsage_BS_CheckDigitEnableStandard2of5USS 0xdb +#define HidUsage_BS_CheckDigitEnableOneMSIPlessey 0xdc +#define HidUsage_BS_CheckDigitEnableTwoMSIPlessey 0xdd +#define HidUsage_BS_CheckDigitCodabarEnable 0xde +#define HidUsage_BS_CheckDigitCode39Enable 0xdf +#define HidUsage_BS_TransmitCheckDigit 0xf0 +#define HidUsage_BS_DisableCheckDigitTransmit 0xf1 +#define HidUsage_BS_EnableCheckDigitTransmit 0xf2 +#define HidUsage_BS_SymbologyIdentifier1 0xfb +#define HidUsage_BS_SymbologyIdentifier2 0xfc +#define HidUsage_BS_SymbologyIdentifier3 0xfd +#define HidUsage_BS_DecodedData 0xfe +#define HidUsage_BS_DecodeDataContinued 0xff +#define HidUsage_BS_BarSpaceData 0x100 +#define HidUsage_BS_ScannerDataAccuracy 0x101 +#define HidUsage_BS_RawDataPolarity 0x102 +#define HidUsage_BS_PolarityInvertedBarCode 0x103 +#define HidUsage_BS_PolarityNormalBarCode 0x104 +#define HidUsage_BS_MinimumLengthtoDecode 0x106 +#define HidUsage_BS_MaximumLengthtoDecode 0x107 +#define HidUsage_BS_DiscreteLengthtoDecode1 0x108 +#define HidUsage_BS_DiscreteLengthtoDecode2 0x109 +#define HidUsage_BS_DataLengthMethod 0x10a +#define HidUsage_BS_DLMethodReadany 0x10b +#define HidUsage_BS_DLMethodCheckinRange 0x10c +#define HidUsage_BS_DLMethodCheckforDiscrete 0x10d +#define HidUsage_BS_AztecCode 0x110 +#define HidUsage_BS_BC412 0x111 +#define HidUsage_BS_ChannelCode 0x112 +#define HidUsage_BS_Code16 0x113 +#define HidUsage_BS_Code32 0x114 +#define HidUsage_BS_Code49 0x115 +#define HidUsage_BS_CodeOne 0x116 +#define HidUsage_BS_Colorcode 0x117 +#define HidUsage_BS_DataMatrix 0x118 +#define HidUsage_BS_MaxiCode 0x119 +#define HidUsage_BS_MicroPDF 0x11a +#define HidUsage_BS_PDF417 0x11b +#define HidUsage_BS_PosiCode 0x11c +#define HidUsage_BS_QRCode 0x11d +#define HidUsage_BS_SuperCode 0x11e +#define HidUsage_BS_UltraCode 0x11f +#define HidUsage_BS_USD5SlugCode 0x120 +#define HidUsage_BS_VeriCode 0x121 +#define HidUsage_Sca_Scales 0x01 +#define HidUsage_Sca_ScaleDevice 0x20 +#define HidUsage_Sca_ScaleClass 0x21 +#define HidUsage_Sca_ScaleClassIMetric 0x22 +#define HidUsage_Sca_ScaleClassIIMetric 0x23 +#define HidUsage_Sca_ScaleClassIIIMetric 0x24 +#define HidUsage_Sca_ScaleClassIIILMetric 0x25 +#define HidUsage_Sca_ScaleClassIVMetric 0x26 +#define HidUsage_Sca_ScaleClassIIIEnglish 0x27 +#define HidUsage_Sca_ScaleClassIIILEnglish 0x28 +#define HidUsage_Sca_ScaleClassIVEnglish 0x29 +#define HidUsage_Sca_ScaleClassGeneric 0x2a +#define HidUsage_Sca_ScaleAttributeReport 0x30 +#define HidUsage_Sca_ScaleControlReport 0x31 +#define HidUsage_Sca_ScaleDataReport 0x32 +#define HidUsage_Sca_ScaleStatusReport 0x33 +#define HidUsage_Sca_ScaleWeightLimitReport 0x34 +#define HidUsage_Sca_ScaleStatisticsReport 0x35 +#define HidUsage_Sca_DataWeight 0x40 +#define HidUsage_Sca_DataScaling 0x41 +#define HidUsage_Sca_WeightUnit 0x50 +#define HidUsage_Sca_WeightUnitMilligram 0x51 +#define HidUsage_Sca_WeightUnitGram 0x52 +#define HidUsage_Sca_WeightUnitKilogram 0x53 +#define HidUsage_Sca_WeightUnitCarats 0x54 +#define HidUsage_Sca_WeightUnitTaels 0x55 +#define HidUsage_Sca_WeightUnitGrains 0x56 +#define HidUsage_Sca_WeightUnitPennyweights 0x57 +#define HidUsage_Sca_WeightUnitMetricTon 0x58 +#define HidUsage_Sca_WeightUnitAvoirTon 0x59 +#define HidUsage_Sca_WeightUnitTroyOunce 0x5a +#define HidUsage_Sca_WeightUnitOunce 0x5b +#define HidUsage_Sca_WeightUnitPound 0x5c +#define HidUsage_Sca_CalibrationCount 0x60 +#define HidUsage_Sca_ReZeroCount 0x61 +#define HidUsage_Sca_ScaleStatus 0x70 +#define HidUsage_Sca_ScaleStatusFault 0x71 +#define HidUsage_Sca_ScaleStatusStableatCenterofZero 0x72 +#define HidUsage_Sca_ScaleStatusInMotion 0x73 +#define HidUsage_Sca_ScaleStatusWeightStable 0x74 +#define HidUsage_Sca_ScaleStatusUnderZero 0x75 +#define HidUsage_Sca_ScaleStatusOverWeightLimit 0x76 +#define HidUsage_Sca_ScaleStatusRequiresCalibration 0x77 +#define HidUsage_Sca_ScaleStatusRequiresRezeroing 0x78 +#define HidUsage_Sca_ZeroScale 0x80 +#define HidUsage_Sca_EnforcedZeroReturn 0x81 +#define HidUsage_MSR_MSRDeviceReadOnly 0x01 +#define HidUsage_MSR_Track1Length 0x11 +#define HidUsage_MSR_Track2Length 0x12 +#define HidUsage_MSR_Track3Length 0x13 +#define HidUsage_MSR_TrackJISLength 0x14 +#define HidUsage_MSR_TrackData 0x20 +#define HidUsage_MSR_Track1Data 0x21 +#define HidUsage_MSR_Track2Data 0x22 +#define HidUsage_MSR_Track3Data 0x23 +#define HidUsage_MSR_TrackJISData 0x24 +#define HidUsage_CC_CameraAutofocus 0x20 +#define HidUsage_CC_CameraShutter 0x21 +#define HidUsage_Arc_GeneralPurposeIOCard 0x01 +#define HidUsage_Arc_CoinDoor 0x02 +#define HidUsage_Arc_WatchdogTimer 0x03 +#define HidUsage_Arc_GeneralPurposeAnalogInputState 0x30 +#define HidUsage_Arc_GeneralPurposeDigitalInputState 0x31 +#define HidUsage_Arc_GeneralPurposeOpticalInputState 0x32 +#define HidUsage_Arc_GeneralPurposeDigitalOutputState 0x33 +#define HidUsage_Arc_NumberofCoinDoors 0x34 +#define HidUsage_Arc_CoinDrawerDropCount 0x35 +#define HidUsage_Arc_CoinDrawerStart 0x36 +#define HidUsage_Arc_CoinDrawerService 0x37 +#define HidUsage_Arc_CoinDrawerTilt 0x38 +#define HidUsage_Arc_CoinDoorTest 0x39 +#define HidUsage_Arc_CoinDoorLockout 0x40 +#define HidUsage_Arc_WatchdogTimeout 0x41 +#define HidUsage_Arc_WatchdogAction 0x42 +#define HidUsage_Arc_WatchdogReboot 0x43 +#define HidUsage_Arc_WatchdogRestart 0x44 +#define HidUsage_Arc_AlarmInput 0x45 +#define HidUsage_Arc_CoinDoorCounter 0x46 +#define HidUsage_Arc_IODirectionMapping 0x47 +#define HidUsage_Arc_SetIODirectionMapping 0x48 +#define HidUsage_Arc_ExtendedOpticalInputState 0x49 +#define HidUsage_Arc_PinPadInputState 0x4a +#define HidUsage_Arc_PinPadStatus 0x4b +#define HidUsage_Arc_PinPadOutput 0x4c +#define HidUsage_Arc_PinPadCommand 0x4d +#define HidUsage_FIDOA_U2FAuthenticatorDevice 0x01 +#define HidUsage_FIDOA_InputReportData 0x20 +#define HidUsage_FIDOA_OutputReportData 0x21 diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c index 21e55f3d0d1b..67179e3fe39b 100644 --- a/drivers/hid/hid-alps.c +++ b/drivers/hid/hid-alps.c @@ -437,6 +437,9 @@ static int alps_raw_event(struct hid_device *hdev, int ret = 0; struct alps_dev *hdata = hid_get_drvdata(hdev); + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !hdata->input) + return 0; + switch (hdev->product) { case HID_PRODUCT_ID_T4_BTNLESS: ret = t4_raw_event(hdata, data, size); diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index fc5897a6bb53..bf7dd0fbf249 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -623,17 +623,19 @@ static int apple_fetch_battery(struct hid_device *hdev) struct apple_sc *asc = hid_get_drvdata(hdev); struct hid_report_enum *report_enum; struct hid_report *report; + struct hid_battery *bat; - if (!(asc->quirks & APPLE_RDESC_BATTERY) || !hdev->battery) + bat = hid_get_battery(hdev); + if (!(asc->quirks & APPLE_RDESC_BATTERY) || !bat) return -1; - report_enum = &hdev->report_enum[hdev->battery_report_type]; - report = report_enum->report_id_hash[hdev->battery_report_id]; + report_enum = &hdev->report_enum[bat->report_type]; + report = report_enum->report_id_hash[bat->report_id]; if (!report || report->maxfield < 1) return -1; - if (hdev->battery_capacity == hdev->battery_max) + if (bat->capacity == bat->max) return -1; hid_hw_request(hdev, report, HID_REQ_GET_REPORT); @@ -858,6 +860,7 @@ static int apple_backlight_init(struct hid_device *hdev) asc->backlight->cdev.name = "apple::kbd_backlight"; asc->backlight->cdev.max_brightness = rep->backlight_on_max; asc->backlight->cdev.brightness_set_blocking = apple_backlight_led_set; + asc->backlight->cdev.flags = LED_CORE_SUSPENDRESUME; ret = apple_backlight_set(hdev, 0, 0); if (ret < 0) { @@ -926,6 +929,7 @@ static int apple_magic_backlight_init(struct hid_device *hdev) backlight->cdev.name = ":white:" LED_FUNCTION_KBD_BACKLIGHT; backlight->cdev.max_brightness = backlight->brightness->field[0]->logical_maximum; backlight->cdev.brightness_set_blocking = apple_magic_backlight_led_set; + backlight->cdev.flags = LED_CORE_SUSPENDRESUME; apple_magic_backlight_set(backlight, 0, 0); diff --git a/drivers/hid/hid-appletb-kbd.c b/drivers/hid/hid-appletb-kbd.c index 0fdc0968b9ef..462010a75899 100644 --- a/drivers/hid/hid-appletb-kbd.c +++ b/drivers/hid/hid-appletb-kbd.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include "hid-ids.h" @@ -62,7 +62,8 @@ struct appletb_kbd { struct input_handle kbd_handle; struct input_handle tpd_handle; struct backlight_device *backlight_dev; - struct timer_list inactivity_timer; + struct delayed_work inactivity_work; + struct work_struct restore_brightness_work; bool has_dimmed; bool has_turned_off; u8 saved_mode; @@ -164,16 +165,18 @@ static int appletb_tb_key_to_slot(unsigned int code) } } -static void appletb_inactivity_timer(struct timer_list *t) +static void appletb_inactivity_work(struct work_struct *work) { - struct appletb_kbd *kbd = timer_container_of(kbd, t, inactivity_timer); + struct appletb_kbd *kbd = container_of(to_delayed_work(work), + struct appletb_kbd, + inactivity_work); if (kbd->backlight_dev && appletb_tb_autodim) { if (!kbd->has_dimmed) { backlight_device_set_brightness(kbd->backlight_dev, 1); kbd->has_dimmed = true; - mod_timer(&kbd->inactivity_timer, - jiffies + secs_to_jiffies(appletb_tb_idle_timeout)); + mod_delayed_work(system_wq, &kbd->inactivity_work, + secs_to_jiffies(appletb_tb_idle_timeout)); } else if (!kbd->has_turned_off) { backlight_device_set_brightness(kbd->backlight_dev, 0); kbd->has_turned_off = true; @@ -181,16 +184,25 @@ static void appletb_inactivity_timer(struct timer_list *t) } } +static void appletb_restore_brightness_work(struct work_struct *work) +{ + struct appletb_kbd *kbd = container_of(work, struct appletb_kbd, + restore_brightness_work); + + if (kbd->backlight_dev) + backlight_device_set_brightness(kbd->backlight_dev, 2); +} + static void reset_inactivity_timer(struct appletb_kbd *kbd) { if (kbd->backlight_dev && appletb_tb_autodim) { if (kbd->has_dimmed || kbd->has_turned_off) { - backlight_device_set_brightness(kbd->backlight_dev, 2); kbd->has_dimmed = false; kbd->has_turned_off = false; + schedule_work(&kbd->restore_brightness_work); } - mod_timer(&kbd->inactivity_timer, - jiffies + secs_to_jiffies(appletb_tb_dim_timeout)); + mod_delayed_work(system_wq, &kbd->inactivity_work, + secs_to_jiffies(appletb_tb_dim_timeout)); } } @@ -408,9 +420,11 @@ static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id dev_err_probe(dev, -ENODEV, "Failed to get backlight device\n"); } else { backlight_device_set_brightness(kbd->backlight_dev, 2); - timer_setup(&kbd->inactivity_timer, appletb_inactivity_timer, 0); - mod_timer(&kbd->inactivity_timer, - jiffies + secs_to_jiffies(appletb_tb_dim_timeout)); + INIT_DELAYED_WORK(&kbd->inactivity_work, appletb_inactivity_work); + INIT_WORK(&kbd->restore_brightness_work, + appletb_restore_brightness_work); + mod_delayed_work(system_wq, &kbd->inactivity_work, + secs_to_jiffies(appletb_tb_dim_timeout)); } kbd->inp_handler.event = appletb_kbd_inp_event; @@ -440,13 +454,14 @@ static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id unregister_handler: input_unregister_handler(&kbd->inp_handler); close_hw: - if (kbd->backlight_dev) { - put_device(&kbd->backlight_dev->dev); - timer_delete_sync(&kbd->inactivity_timer); - } hid_hw_close(hdev); stop_hw: hid_hw_stop(hdev); + if (kbd->backlight_dev) { + cancel_delayed_work_sync(&kbd->inactivity_work); + cancel_work_sync(&kbd->restore_brightness_work); + put_device(&kbd->backlight_dev->dev); + } return ret; } @@ -457,13 +472,14 @@ static void appletb_kbd_remove(struct hid_device *hdev) appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF); input_unregister_handler(&kbd->inp_handler); - if (kbd->backlight_dev) { - put_device(&kbd->backlight_dev->dev); - timer_delete_sync(&kbd->inactivity_timer); - } - hid_hw_close(hdev); hid_hw_stop(hdev); + + if (kbd->backlight_dev) { + cancel_delayed_work_sync(&kbd->inactivity_work); + cancel_work_sync(&kbd->restore_brightness_work); + put_device(&kbd->backlight_dev->dev); + } } static int appletb_kbd_suspend(struct hid_device *hdev, pm_message_t msg) diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index bc93b27f9b13..3f5e96900b67 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -20,10 +20,8 @@ * Copyright (c) 2016 Frederik Wenigwieser */ -/* - */ - #include +#include #include #include #include @@ -101,7 +99,6 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); #define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12) #define QUIRK_ROG_ALLY_XPAD BIT(13) #define QUIRK_HID_FN_LOCK BIT(14) -#define QUIRK_ROG_NKEY_ID1ID2_INIT BIT(15) #define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \ @@ -208,6 +205,12 @@ static const struct asus_touchpad_info medion_e1239t_tp = { .report_size = 32 /* 2 byte header + 5 * 5 + 5 byte footer */, }; +static const u8 asus_report_id_init[] = { + FEATURE_KBD_REPORT_ID, + FEATURE_KBD_LED_REPORT_ID1, + FEATURE_KBD_LED_REPORT_ID2 +}; + static void asus_report_contact_down(struct asus_drvdata *drvdat, int toolType, u8 *data) { @@ -354,7 +357,7 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { struct asus_drvdata *drvdata = hid_get_drvdata(hdev); - + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR && (usage->hid & HID_USAGE) != 0x00 && (usage->hid & HID_USAGE) != 0xff && !usage->type) { @@ -443,21 +446,18 @@ static int asus_raw_event(struct hid_device *hdev, /* * G713 and G733 send these codes on some keypresses, depending on * the key pressed it can trigger a shutdown event if not caught. - */ - if (data[0] == 0x02 && data[1] == 0x30) { + */ + if (data[0] == 0x02 && data[1] == 0x30) return -1; - } } if (drvdata->quirks & QUIRK_ROG_CLAYMORE_II_KEYBOARD) { /* * CLAYMORE II keyboard sends this packet when it goes to sleep * this causes the whole system to go into suspend. - */ - - if(size == 2 && data[0] == 0x02 && data[1] == 0x00) { + */ + if (size == 2 && data[0] == 0x02 && data[1] == 0x00) return -1; - } } return 0; @@ -465,23 +465,16 @@ static int asus_raw_event(struct hid_device *hdev, static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t buf_size) { - unsigned char *dmabuf; - int ret; - - dmabuf = kmemdup(buf, buf_size, GFP_KERNEL); + u8 *dmabuf __free(kfree) = kmemdup(buf, buf_size, GFP_KERNEL); if (!dmabuf) return -ENOMEM; /* * The report ID should be set from the incoming buffer due to LED and key * interfaces having different pages - */ - ret = hid_hw_raw_request(hdev, buf[0], dmabuf, - buf_size, HID_FEATURE_REPORT, - HID_REQ_SET_REPORT); - kfree(dmabuf); - - return ret; + */ + return hid_hw_raw_request(hdev, buf[0], dmabuf, buf_size, HID_FEATURE_REPORT, + HID_REQ_SET_REPORT); } static int asus_kbd_init(struct hid_device *hdev, u8 report_id) @@ -722,6 +715,21 @@ static void validate_mcu_fw_version(struct hid_device *hdev, int idProduct) } } +static bool asus_has_report_id(struct hid_device *hdev, u16 report_id) +{ + struct hid_report *report; + int t; + + for (t = HID_INPUT_REPORT; t <= HID_FEATURE_REPORT; t++) { + list_for_each_entry(report, &hdev->report_enum[t].report_list, list) { + if (report->id == report_id) + return true; + } + } + + return false; +} + static int asus_kbd_register_leds(struct hid_device *hdev) { struct asus_drvdata *drvdata = hid_get_drvdata(hdev); @@ -730,10 +738,6 @@ static int asus_kbd_register_leds(struct hid_device *hdev) unsigned char kbd_func; int ret; - ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID); - if (ret < 0) - return ret; - /* Get keyboard functions */ ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID); if (ret < 0) @@ -743,11 +747,6 @@ static int asus_kbd_register_leds(struct hid_device *hdev) if (!(kbd_func & SUPPORT_KBD_BACKLIGHT)) return -ENODEV; - if (drvdata->quirks & QUIRK_ROG_NKEY_ID1ID2_INIT) { - asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1); - asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2); - } - if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) { ret = asus_kbd_disable_oobe(hdev); if (ret < 0) @@ -1163,7 +1162,8 @@ static int asus_start_multitouch(struct hid_device *hdev) return 0; } -static int __maybe_unused asus_resume(struct hid_device *hdev) { +static int __maybe_unused asus_resume(struct hid_device *hdev) +{ struct asus_drvdata *drvdata = hid_get_drvdata(hdev); int ret = 0; @@ -1294,8 +1294,19 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } + for (int r = 0; r < ARRAY_SIZE(asus_report_id_init); r++) { + if (asus_has_report_id(hdev, asus_report_id_init[r])) { + ret = asus_kbd_init(hdev, asus_report_id_init[r]); + if (ret < 0) + hid_warn(hdev, "Failed to initialize 0x%x: %d.\n", + asus_report_id_init[r], ret); + } + } + + /* Laptops keyboard backlight is always at 0x5a */ if (is_vendor && (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) && - asus_kbd_register_leds(hdev)) + (asus_has_report_id(hdev, FEATURE_KBD_REPORT_ID)) && + (asus_kbd_register_leds(hdev))) hid_warn(hdev, "Failed to initialize backlight.\n"); /* @@ -1311,22 +1322,17 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) * were freed during registration due to no usages being mapped, * leaving drvdata->input pointing to freed memory. */ - if (!drvdata->input || !(hdev->claimed & HID_CLAIMED_INPUT)) { - hid_err(hdev, "Asus input not registered\n"); - ret = -ENOMEM; - goto err_stop_hw; - } + if (drvdata->input && (hdev->claimed & HID_CLAIMED_INPUT)) { + if (drvdata->tp) + drvdata->input->name = "Asus TouchPad"; + else + drvdata->input->name = "Asus Keyboard"; - if (drvdata->tp) { - drvdata->input->name = "Asus TouchPad"; - } else { - drvdata->input->name = "Asus Keyboard"; - } - - if (drvdata->tp) { - ret = asus_start_multitouch(hdev); - if (ret) - goto err_stop_hw; + if (drvdata->tp) { + ret = asus_start_multitouch(hdev); + if (ret) + goto err_stop_hw; + } } return 0; @@ -1484,10 +1490,10 @@ static const struct hid_device_id asus_devices[] = { QUIRK_USE_KBD_BACKLIGHT }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_ID1ID2_INIT }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_HID_FN_LOCK | QUIRK_ROG_NKEY_ID1ID2_INIT }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_HID_FN_LOCK }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 833df14ef68f..b3596851c719 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -71,6 +71,9 @@ static u32 s32ton(__s32 value, unsigned int n) if (!value || !n) return 0; + if (n > 32) + n = 32; + a = value >> (n - 1); if (a && a != -1) return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1; @@ -924,7 +927,6 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) */ static int hid_scan_report(struct hid_device *hid) { - struct hid_parser *parser; struct hid_item item; const __u8 *start = hid->dev_rdesc; const __u8 *end = start + hid->dev_rsize; @@ -936,7 +938,7 @@ static int hid_scan_report(struct hid_device *hid) hid_parser_reserved }; - parser = vzalloc(sizeof(struct hid_parser)); + struct hid_parser *parser __free(kvfree) = vzalloc(sizeof(*parser)); if (!parser) return -ENOMEM; @@ -987,7 +989,6 @@ static int hid_scan_report(struct hid_device *hid) } kfree(parser->collection_stack); - vfree(parser); return 0; } @@ -1244,6 +1245,90 @@ void hid_setup_resolution_multiplier(struct hid_device *hid) } EXPORT_SYMBOL_GPL(hid_setup_resolution_multiplier); +static int hid_parse_collections(struct hid_device *device) +{ + struct hid_item item; + const u8 *start = device->rdesc; + const u8 *end = start + device->rsize; + const u8 *next; + int ret; + static typeof(hid_parser_main) (* const dispatch_type[]) = { + hid_parser_main, + hid_parser_global, + hid_parser_local, + hid_parser_reserved + }; + + struct hid_parser *parser __free(kvfree) = vzalloc(sizeof(*parser)); + if (!parser) + return -ENOMEM; + + parser->device = device; + + device->collection = kzalloc_objs(*device->collection, + HID_DEFAULT_NUM_COLLECTIONS); + if (!device->collection) + return -ENOMEM; + + device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; + for (unsigned int i = 0; i < HID_DEFAULT_NUM_COLLECTIONS; i++) + device->collection[i].parent_idx = -1; + + ret = -EINVAL; + if (start == end) { + hid_err(device, "rejecting 0-sized report descriptor\n"); + goto out; + } + + while ((next = fetch_item(start, end, &item)) != NULL) { + start = next; + + if (item.format != HID_ITEM_FORMAT_SHORT) { + hid_err(device, "unexpected long global item\n"); + goto out; + } + + if (dispatch_type[item.type](parser, &item)) { + hid_err(device, "item %u %u %u %u parsing failed\n", + item.format, + (unsigned int)item.size, + (unsigned int)item.type, + (unsigned int)item.tag); + goto out; + } + } + + if (start != end) { + hid_err(device, "item fetching failed at offset %u/%u\n", + device->rsize - (unsigned int)(end - start), + device->rsize); + goto out; + } + + if (parser->collection_stack_ptr) { + hid_err(device, "unbalanced collection at end of report description\n"); + goto out; + } + + if (parser->local.delimiter_depth) { + hid_err(device, "unbalanced delimiter at end of report description\n"); + goto out; + } + + /* + * fetch initial values in case the device's + * default multiplier isn't the recommended 1 + */ + hid_setup_resolution_multiplier(device); + + device->status |= HID_STAT_PARSED; + ret = 0; + +out: + kfree(parser->collection_stack); + return ret; +} + /** * hid_open_report - open a driver-specific device report * @@ -1258,21 +1343,9 @@ EXPORT_SYMBOL_GPL(hid_setup_resolution_multiplier); */ int hid_open_report(struct hid_device *device) { - struct hid_parser *parser; - struct hid_item item; unsigned int size; - const __u8 *start; - const __u8 *end; - const __u8 *next; - int ret; - int i; - static int (*dispatch_type[])(struct hid_parser *parser, - struct hid_item *item) = { - hid_parser_main, - hid_parser_global, - hid_parser_local, - hid_parser_reserved - }; + const u8 *start; + int error; if (WARN_ON(device->status & HID_STAT_PARSED)) return -EBUSY; @@ -1288,9 +1361,9 @@ int hid_open_report(struct hid_device *device) * on a copy of our report descriptor so it can * change it. */ - __u8 *buf = kmemdup(start, size, GFP_KERNEL); + u8 *buf __free(kfree) = kmemdup(start, size, GFP_KERNEL); - if (buf == NULL) + if (!buf) return -ENOMEM; start = device->driver->report_fixup(device, buf, &size); @@ -1301,82 +1374,20 @@ int hid_open_report(struct hid_device *device) * needs to be cleaned up or not at the end. */ start = kmemdup(start, size, GFP_KERNEL); - kfree(buf); - if (start == NULL) + if (!start) return -ENOMEM; } device->rdesc = start; device->rsize = size; - parser = vzalloc(sizeof(struct hid_parser)); - if (!parser) { - ret = -ENOMEM; - goto alloc_err; + error = hid_parse_collections(device); + if (error) { + hid_close_report(device); + return error; } - parser->device = device; - - end = start + size; - - device->collection = kzalloc_objs(struct hid_collection, - HID_DEFAULT_NUM_COLLECTIONS); - if (!device->collection) { - ret = -ENOMEM; - goto err; - } - device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; - for (i = 0; i < HID_DEFAULT_NUM_COLLECTIONS; i++) - device->collection[i].parent_idx = -1; - - ret = -EINVAL; - while ((next = fetch_item(start, end, &item)) != NULL) { - start = next; - - if (item.format != HID_ITEM_FORMAT_SHORT) { - hid_err(device, "unexpected long global item\n"); - goto err; - } - - if (dispatch_type[item.type](parser, &item)) { - hid_err(device, "item %u %u %u %u parsing failed\n", - item.format, (unsigned)item.size, - (unsigned)item.type, (unsigned)item.tag); - goto err; - } - - if (start == end) { - if (parser->collection_stack_ptr) { - hid_err(device, "unbalanced collection at end of report description\n"); - goto err; - } - if (parser->local.delimiter_depth) { - hid_err(device, "unbalanced delimiter at end of report description\n"); - goto err; - } - - /* - * fetch initial values in case the device's - * default multiplier isn't the recommended 1 - */ - hid_setup_resolution_multiplier(device); - - kfree(parser->collection_stack); - vfree(parser); - device->status |= HID_STAT_PARSED; - - return 0; - } - } - - hid_err(device, "item fetching failed at offset %u/%u\n", - size - (unsigned int)(end - start), size); -err: - kfree(parser->collection_stack); -alloc_err: - vfree(parser); - hid_close_report(device); - return ret; + return 0; } EXPORT_SYMBOL_GPL(hid_open_report); @@ -1989,11 +2000,11 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, int __hid_request(struct hid_device *hid, struct hid_report *report, enum hid_class_request reqtype) { - char *buf, *data_buf; + u8 *data_buf; int ret; u32 len; - buf = hid_alloc_report_buf(report, GFP_KERNEL); + u8 *buf __free(kfree) = hid_alloc_report_buf(report, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -2012,38 +2023,42 @@ int __hid_request(struct hid_device *hid, struct hid_report *report, ret = hid_hw_raw_request(hid, report->id, buf, len, report->type, reqtype); if (ret < 0) { dbg_hid("unable to complete request: %d\n", ret); - goto out; + return ret; } if (reqtype == HID_REQ_GET_REPORT) hid_input_report(hid, report->type, buf, ret, 0); - ret = 0; - -out: - kfree(buf); - return ret; + return 0; } EXPORT_SYMBOL_GPL(__hid_request); -int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, - int interrupt) +int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, + size_t bufsize, u32 size, int interrupt) { struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report *report; struct hid_driver *hdrv; int max_buffer_size = HID_MAX_BUFFER_SIZE; u32 rsize, csize = size; + size_t bsize = bufsize; u8 *cdata = data; int ret = 0; report = hid_get_report(report_enum, data); if (!report) - goto out; + return 0; + + if (unlikely(bsize < csize)) { + hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n", + report->id, csize, bsize); + return -EINVAL; + } if (report_enum->numbered) { cdata++; csize--; + bsize--; } rsize = hid_compute_report_size(report); @@ -2056,11 +2071,16 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * else if (rsize > max_buffer_size) rsize = max_buffer_size; + if (bsize < rsize) { + hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n", + report->id, rsize, bsize); + return -EINVAL; + } + if (csize < rsize) { - hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %d)\n", - report->id, rsize, csize); - ret = -EINVAL; - goto out; + dbg_hid("report %d is too short, (%d < %d)\n", report->id, + csize, rsize); + memset(cdata + csize, 0, rsize - csize); } if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) @@ -2068,7 +2088,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * if (hid->claimed & HID_CLAIMED_HIDRAW) { ret = hidraw_report_event(hid, data, size); if (ret) - goto out; + return ret; } if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) { @@ -2080,15 +2100,15 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * if (hid->claimed & HID_CLAIMED_INPUT) hidinput_report_event(hid, report); -out: + return ret; } EXPORT_SYMBOL_GPL(hid_report_raw_event); static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, - u8 *data, u32 size, int interrupt, u64 source, bool from_bpf, - bool lock_already_taken) + u8 *data, size_t bufsize, u32 size, int interrupt, u64 source, + bool from_bpf, bool lock_already_taken) { struct hid_report_enum *report_enum; struct hid_driver *hdrv; @@ -2113,7 +2133,8 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, report_enum = hid->report_enum + type; hdrv = hid->driver; - data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf); + data = dispatch_hid_bpf_device_event(hid, type, data, &bufsize, &size, interrupt, + source, from_bpf); if (IS_ERR(data)) { ret = PTR_ERR(data); goto unlock; @@ -2142,7 +2163,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, goto unlock; } - ret = hid_report_raw_event(hid, type, data, size, interrupt); + ret = hid_report_raw_event(hid, type, data, bufsize, size, interrupt); unlock: if (!lock_already_taken) @@ -2160,16 +2181,41 @@ unlock: * @interrupt: distinguish between interrupt and control transfers * * This is data entry for lower layers. + * Legacy, please use hid_safe_input_report() instead. */ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, int interrupt) { - return __hid_input_report(hid, type, data, size, interrupt, 0, + return __hid_input_report(hid, type, data, size, size, interrupt, 0, false, /* from_bpf */ false /* lock_already_taken */); } EXPORT_SYMBOL_GPL(hid_input_report); +/** + * hid_safe_input_report - report data from lower layer (usb, bt...) + * + * @hid: hid device + * @type: HID report type (HID_*_REPORT) + * @data: report contents + * @bufsize: allocated size of the data buffer + * @size: useful size of data parameter + * @interrupt: distinguish between interrupt and control transfers + * + * This is data entry for lower layers. + * Please use this function instead of the non safe version because we provide + * here the size of the buffer, allowing hid-core to make smarter decisions + * regarding the incoming buffer. + */ +int hid_safe_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, + size_t bufsize, u32 size, int interrupt) +{ + return __hid_input_report(hid, type, data, bufsize, size, interrupt, 0, + false, /* from_bpf */ + false /* lock_already_taken */); +} +EXPORT_SYMBOL_GPL(hid_safe_input_report); + bool hid_match_one_id(const struct hid_device *hdev, const struct hid_device_id *id) { @@ -2888,6 +2934,11 @@ static int hid_uevent(const struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "MODALIAS=hid:b%04Xg%04Xv%08Xp%08X", hdev->bus, hdev->group, hdev->vendor, hdev->product)) return -ENOMEM; + if (hdev->firmware_version) { + if (add_uevent_var(env, "HID_FIRMWARE_VERSION=0x%04llX", + hdev->firmware_version)) + return -ENOMEM; + } return 0; } @@ -2991,6 +3042,10 @@ struct hid_device *hid_allocate_device(void) mutex_init(&hdev->ll_open_lock); kref_init(&hdev->ref); +#ifdef CONFIG_HID_BATTERY_STRENGTH + INIT_LIST_HEAD(&hdev->batteries); +#endif + ret = hid_bpf_device_init(hdev); if (ret) goto out_err; diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index 76d93fc48f6a..0190ad567ce4 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -513,6 +513,7 @@ static const struct hid_device_id elan_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER), .driver_data = ELAN_HAS_LED }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_TOSHIBA_CLICK_L9W) }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_SB974D) }, { } }; MODULE_DEVICE_TABLE(hid, elan_devices); diff --git a/drivers/hid/hid-ft260.c b/drivers/hid/hid-ft260.c index 333341e80b0e..70e2eedb465a 100644 --- a/drivers/hid/hid-ft260.c +++ b/drivers/hid/hid-ft260.c @@ -1068,10 +1068,22 @@ static int ft260_raw_event(struct hid_device *hdev, struct hid_report *report, struct ft260_device *dev = hid_get_drvdata(hdev); struct ft260_i2c_input_report *xfer = (void *)data; + if (size < offsetof(struct ft260_i2c_input_report, data)) { + hid_err(hdev, "short report %d\n", size); + return -1; + } + if (xfer->report >= FT260_I2C_REPORT_MIN && xfer->report <= FT260_I2C_REPORT_MAX) { - ft260_dbg("i2c resp: rep %#02x len %d\n", xfer->report, - xfer->length); + ft260_dbg("i2c resp: rep %#02x len %d size %d\n", + xfer->report, xfer->length, size); + + if (xfer->length > size - + offsetof(struct ft260_i2c_input_report, data)) { + hid_err(hdev, "report %#02x: length %d exceeds HID report size\n", + xfer->report, xfer->length); + return -1; + } if ((dev->read_buf == NULL) || (xfer->length > dev->read_len - dev->read_idx)) { diff --git a/drivers/hid/hid-gfrm.c b/drivers/hid/hid-gfrm.c index 699186ff2349..d2a56bf92b41 100644 --- a/drivers/hid/hid-gfrm.c +++ b/drivers/hid/hid-gfrm.c @@ -66,7 +66,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report, switch (data[1]) { case GFRM100_SEARCH_KEY_DOWN: ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_dn, - sizeof(search_key_dn), 1); + sizeof(search_key_dn), sizeof(search_key_dn), 1); break; case GFRM100_SEARCH_KEY_AUDIO_DATA: @@ -74,7 +74,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report, case GFRM100_SEARCH_KEY_UP: ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_up, - sizeof(search_key_up), 1); + sizeof(search_key_up), sizeof(search_key_up), 1); break; default: diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c index 1af477e58480..c99c3c0d442e 100644 --- a/drivers/hid/hid-google-hammer.c +++ b/drivers/hid/hid-google-hammer.c @@ -496,7 +496,7 @@ static int hammer_probe(struct hid_device *hdev, if (error) return error; - error = devm_add_action(&hdev->dev, hammer_stop, hdev); + error = devm_add_action_or_reset(&hdev->dev, hammer_stop, hdev); if (error) return error; diff --git a/drivers/hid/hid-huawei.c b/drivers/hid/hid-huawei.c new file mode 100644 index 000000000000..6a616bf21b38 --- /dev/null +++ b/drivers/hid/hid-huawei.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * HID driver for some huawei "special" devices + * + * Copyright (c) 2026 Miao Li + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +static const __u8 huawei_cd30_kbd_rdesc_fixed[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x80, /* Usage (System Control) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x85, 0x01, /* Report ID (1) */ + 0x19, 0x81, /* Usage Minimum (System Power Down) */ + 0x29, 0x83, /* Usage Maximum (System Wake Up) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x75, 0x01, /* Report Size (1 bit) */ + 0x95, 0x03, /* Report Count (3) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x95, 0x05, /* Report Count (5) */ + 0x81, 0x01, /* Input (Cnst,Ary,Abs) */ + 0xc0, /* End Collection */ + 0x05, 0x0c, /* Usage Page (Consumer) */ + 0x09, 0x01, /* Usage (Consumer Control) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x85, 0x02, /* Report ID (2) */ + 0x19, 0x00, /* Usage Minimum (0) */ + 0x2a, 0x3c, 0x02, /* Usage Maximum (0x023C) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0x3c, 0x02, /* Logical Maximum (0x023C) */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x10, /* Report Size (16 bits) */ + 0x81, 0x00, /* Input (Data,Ary,Abs) */ + 0xc0 /* End Collection */ +}; + +static const __u8 *huawei_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + switch (hdev->product) { + case USB_DEVICE_ID_HUAWEI_CD30KBD: + if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { + if (*rsize != sizeof(huawei_cd30_kbd_rdesc_fixed) || + memcmp(huawei_cd30_kbd_rdesc_fixed, rdesc, + sizeof(huawei_cd30_kbd_rdesc_fixed)) != 0) { + hid_info(hdev, "Replacing Huawei cd30 keyboard report descriptor.\n"); + *rsize = sizeof(huawei_cd30_kbd_rdesc_fixed); + return huawei_cd30_kbd_rdesc_fixed; + } + } + break; + } + + return rdesc; +} + +static const struct hid_device_id huawei_devices[] = { + /* HUAWEI cd30 keyboard */ + { HID_USB_DEVICE(USB_VENDOR_ID_HUAWEI, USB_DEVICE_ID_HUAWEI_CD30KBD)}, + { } +}; +MODULE_DEVICE_TABLE(hid, huawei_devices); + +static struct hid_driver huawei_driver = { + .name = "huawei", + .id_table = huawei_devices, + .report_fixup = huawei_report_fixup, +}; +module_hid_driver(huawei_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Miao Li "); +MODULE_DESCRIPTION("HID driver for some huawei \"special\" devices"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c1e4a6ce9631..4657d96fb083 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -277,6 +277,9 @@ #define USB_VENDOR_ID_BIGBEN 0x146b #define USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD 0x0902 +#define I2C_VENDOR_ID_BLTP 0x36b6 +#define I2C_PRODUCT_ID_BLTP7853 0xc001 + #define USB_VENDOR_ID_BTC 0x046e #define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578 #define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577 @@ -455,6 +458,7 @@ #define USB_DEVICE_ID_EDIFIER_QR30 0xa101 /* EDIFIER Hal0 2.0 SE */ #define USB_VENDOR_ID_ELAN 0x04f3 +#define USB_DEVICE_ID_SB974D 0x0400 #define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401 #define USB_DEVICE_ID_HP_X2 0x074d #define USB_DEVICE_ID_HP_X2_10_COVER 0x0755 @@ -667,6 +671,18 @@ #define USB_DEVICE_ID_UGCI_FLYING 0x0020 #define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 +#define USB_VENDOR_ID_HARMONIX 0x1bad +#define USB_DEVICE_ID_HARMONIX_WII_RB1_GUITAR 0x0004 +#define USB_DEVICE_ID_HARMONIX_WII_RB2_GUITAR 0x3010 +#define USB_DEVICE_ID_HARMONIX_WII_RB1_DRUMS 0x0005 +#define USB_DEVICE_ID_HARMONIX_WII_RB2_DRUMS 0x3110 +#define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_DRUMS_MODE 0x3138 +#define USB_DEVICE_ID_HARMONIX_WII_RB3_MUSTANG_GUITAR 0x3430 +#define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_MUSTANG_MODE 0x3438 +#define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_SQUIER_MODE 0x3538 +#define USB_DEVICE_ID_HARMONIX_WII_RB3_KEYBOARD 0x3330 +#define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_KEYBOARD_MODE 0x3338 + #define USB_VENDOR_ID_HP 0x03f0 #define USB_PRODUCT_ID_HP_ELITE_PRESENTER_MOUSE_464A 0x464a #define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A 0x0a4a @@ -742,6 +758,10 @@ #define USB_DEVICE_ID_ITE8595 0x8595 #define USB_DEVICE_ID_ITE_MEDION_E1239T 0xce50 +#define USB_VENDOR_ID_QHE 0x1a86 +#define USB_DEVICE_ID_LENOVO_LEGION_GO_S_XINPUT 0xe310 +#define USB_DEVICE_ID_LENOVO_LEGION_GO_S_DINPUT 0xe311 + #define USB_VENDOR_ID_JABRA 0x0b0e #define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 #define USB_DEVICE_ID_JABRA_SPEAK_510 0x0420 @@ -861,7 +881,10 @@ #define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_602E 0x602e #define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6093 0x6093 #define USB_DEVICE_ID_LENOVO_LEGION_GO_DUAL_DINPUT 0x6184 +#define USB_DEVICE_ID_LENOVO_LEGION_GO2_XINPUT 0x61eb +#define USB_DEVICE_ID_LENOVO_LEGION_GO2_DINPUT 0x61ec #define USB_DEVICE_ID_LENOVO_LEGION_GO2_DUAL_DINPUT 0x61ed +#define USB_DEVICE_ID_LENOVO_LEGION_GO2_FPS 0x61ee #define USB_VENDOR_ID_LETSKETCH 0x6161 #define USB_DEVICE_ID_WP9620N 0x4d15 @@ -1301,8 +1324,18 @@ #define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000 #define USB_VENDOR_ID_SONY_RHYTHM 0x12ba -#define USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE 0x074b -#define USB_DEVICE_ID_SONY_PS3_GUITAR_DONGLE 0x0100 +#define USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE 0x074b +#define USB_DEVICE_ID_SONY_PS3_GH_GUITAR 0x0100 +#define USB_DEVICE_ID_SONY_PS3_GH_DRUMS 0x0120 +#define USB_DEVICE_ID_SONY_PS3_DJH_TURNTABLE 0x0140 +#define USB_DEVICE_ID_SONY_PS3_RB_GUITAR 0x0200 +#define USB_DEVICE_ID_SONY_PS3_RB_DRUMS 0x0210 +#define USB_DEVICE_ID_SONY_PS3_RB3_MPA_DRUMS_MODE 0x0218 +#define USB_DEVICE_ID_SONY_PS3_RB3_MUSTANG_GUITAR 0x2430 +#define USB_DEVICE_ID_SONY_PS3_RB3_MPA_MUSTANG_MODE 0x2438 +#define USB_DEVICE_ID_SONY_PS3_RB3_MPA_SQUIER_MODE 0x2538 +#define USB_DEVICE_ID_SONY_PS3_RB3_KEYBOARD 0x2330 +#define USB_DEVICE_ID_SONY_PS3_RB3_MPA_KEYBOARD_MODE 0x2338 #define USB_VENDOR_ID_SINO_LITE 0x1345 #define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008 @@ -1587,4 +1620,7 @@ #define USB_VENDOR_ID_JIELI_SDK_DEFAULT 0x4c4a #define USB_DEVICE_ID_JIELI_SDK_4155 0x4155 +#define USB_VENDOR_ID_HUAWEI 0x12d1 +#define USB_DEVICE_ID_HUAWEI_CD30KBD 0x109b + #endif diff --git a/drivers/hid/hid-input-test.c b/drivers/hid/hid-input-test.c index 6f5c71660d82..c92008dafddf 100644 --- a/drivers/hid/hid-input-test.c +++ b/drivers/hid/hid-input-test.c @@ -9,54 +9,59 @@ static void hid_test_input_update_battery_charge_status(struct kunit *test) { - struct hid_device *dev; + struct hid_battery *bat; bool handled; - dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + bat = kunit_kzalloc(test, sizeof(*bat), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bat); - handled = hidinput_update_battery_charge_status(dev, HID_DG_HEIGHT, 0); + handled = hidinput_update_battery_charge_status(bat, HID_DG_HEIGHT, 0); KUNIT_EXPECT_FALSE(test, handled); - KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_UNKNOWN); + KUNIT_EXPECT_EQ(test, bat->charge_status, POWER_SUPPLY_STATUS_UNKNOWN); - handled = hidinput_update_battery_charge_status(dev, HID_BAT_CHARGING, 0); + handled = hidinput_update_battery_charge_status(bat, HID_BAT_CHARGING, 0); KUNIT_EXPECT_TRUE(test, handled); - KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_DISCHARGING); + KUNIT_EXPECT_EQ(test, bat->charge_status, POWER_SUPPLY_STATUS_DISCHARGING); - handled = hidinput_update_battery_charge_status(dev, HID_BAT_CHARGING, 1); + handled = hidinput_update_battery_charge_status(bat, HID_BAT_CHARGING, 1); KUNIT_EXPECT_TRUE(test, handled); - KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_CHARGING); + KUNIT_EXPECT_EQ(test, bat->charge_status, POWER_SUPPLY_STATUS_CHARGING); } static void hid_test_input_get_battery_property(struct kunit *test) { struct power_supply *psy; + struct hid_battery *bat; struct hid_device *dev; union power_supply_propval val; int ret; dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); - dev->battery_avoid_query = true; + + bat = kunit_kzalloc(test, sizeof(*bat), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bat); + bat->dev = dev; + bat->avoid_query = true; psy = kunit_kzalloc(test, sizeof(*psy), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, psy); - psy->drv_data = dev; + psy->drv_data = bat; - dev->battery_status = HID_BATTERY_UNKNOWN; - dev->battery_charge_status = POWER_SUPPLY_STATUS_CHARGING; + bat->status = HID_BATTERY_UNKNOWN; + bat->charge_status = POWER_SUPPLY_STATUS_CHARGING; ret = hidinput_get_battery_property(psy, POWER_SUPPLY_PROP_STATUS, &val); KUNIT_EXPECT_EQ(test, ret, 0); KUNIT_EXPECT_EQ(test, val.intval, POWER_SUPPLY_STATUS_UNKNOWN); - dev->battery_status = HID_BATTERY_REPORTED; - dev->battery_charge_status = POWER_SUPPLY_STATUS_CHARGING; + bat->status = HID_BATTERY_REPORTED; + bat->charge_status = POWER_SUPPLY_STATUS_CHARGING; ret = hidinput_get_battery_property(psy, POWER_SUPPLY_PROP_STATUS, &val); KUNIT_EXPECT_EQ(test, ret, 0); KUNIT_EXPECT_EQ(test, val.intval, POWER_SUPPLY_STATUS_CHARGING); - dev->battery_status = HID_BATTERY_REPORTED; - dev->battery_charge_status = POWER_SUPPLY_STATUS_DISCHARGING; + bat->status = HID_BATTERY_REPORTED; + bat->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; ret = hidinput_get_battery_property(psy, POWER_SUPPLY_PROP_STATUS, &val); KUNIT_EXPECT_EQ(test, ret, 0); KUNIT_EXPECT_EQ(test, val.intval, POWER_SUPPLY_STATUS_DISCHARGING); diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e824c793f669..d73cfa2e73d3 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -416,43 +416,39 @@ static unsigned find_battery_quirk(struct hid_device *hdev) return quirks; } -static int hidinput_scale_battery_capacity(struct hid_device *dev, +static int hidinput_scale_battery_capacity(struct hid_battery *bat, int value) { - if (dev->battery_min < dev->battery_max && - value >= dev->battery_min && value <= dev->battery_max) - value = ((value - dev->battery_min) * 100) / - (dev->battery_max - dev->battery_min); + if (bat->min < bat->max && + value >= bat->min && value <= bat->max) + value = ((value - bat->min) * 100) / + (bat->max - bat->min); return value; } -static int hidinput_query_battery_capacity(struct hid_device *dev) +static int hidinput_query_battery_capacity(struct hid_battery *bat) { - u8 *buf; int ret; - buf = kmalloc(4, GFP_KERNEL); + u8 *buf __free(kfree) = kmalloc(4, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 4, - dev->battery_report_type, HID_REQ_GET_REPORT); - if (ret < 2) { - kfree(buf); + ret = hid_hw_raw_request(bat->dev, bat->report_id, buf, 4, + bat->report_type, HID_REQ_GET_REPORT); + if (ret < 2) return -ENODATA; - } - ret = hidinput_scale_battery_capacity(dev, buf[1]); - kfree(buf); - return ret; + return hidinput_scale_battery_capacity(bat, buf[1]); } static int hidinput_get_battery_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { - struct hid_device *dev = power_supply_get_drvdata(psy); + struct hid_battery *bat = power_supply_get_drvdata(psy); + struct hid_device *dev = bat->dev; int value; int ret = 0; @@ -462,17 +458,17 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_PRESENT: - val->intval = dev->battery_present; + val->intval = bat->present; break; case POWER_SUPPLY_PROP_CAPACITY: - if (dev->battery_status != HID_BATTERY_REPORTED && - !dev->battery_avoid_query) { - value = hidinput_query_battery_capacity(dev); + if (bat->status != HID_BATTERY_REPORTED && + !bat->avoid_query) { + value = hidinput_query_battery_capacity(bat); if (value < 0) return value; } else { - value = dev->battery_capacity; + value = bat->capacity; } val->intval = value; @@ -483,20 +479,20 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_STATUS: - if (dev->battery_status != HID_BATTERY_REPORTED && - !dev->battery_avoid_query) { - value = hidinput_query_battery_capacity(dev); + if (bat->status != HID_BATTERY_REPORTED && + !bat->avoid_query) { + value = hidinput_query_battery_capacity(bat); if (value < 0) return value; - dev->battery_capacity = value; - dev->battery_status = HID_BATTERY_QUERIED; + bat->capacity = value; + bat->status = HID_BATTERY_QUERIED; } - if (dev->battery_status == HID_BATTERY_UNKNOWN) + if (bat->status == HID_BATTERY_UNKNOWN) val->intval = POWER_SUPPLY_STATUS_UNKNOWN; else - val->intval = dev->battery_charge_status; + val->intval = bat->charge_status; break; case POWER_SUPPLY_PROP_SCOPE: @@ -511,36 +507,59 @@ static int hidinput_get_battery_property(struct power_supply *psy, return ret; } +static struct hid_battery *hidinput_find_battery(struct hid_device *dev, + int report_id) +{ + struct hid_battery *bat; + + list_for_each_entry(bat, &dev->batteries, list) { + if (bat->report_id == report_id) + return bat; + } + return NULL; +} + static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field, bool is_percentage) { + struct hid_battery *bat; struct power_supply_desc *psy_desc; - struct power_supply_config psy_cfg = { .drv_data = dev, }; + struct power_supply_config psy_cfg = { 0 }; unsigned quirks; s32 min, max; int error; - if (dev->battery) - return 0; /* already initialized? */ + /* Check if battery for this report ID already exists */ + if (hidinput_find_battery(dev, field->report->id)) + return 0; quirks = find_battery_quirk(dev); - hid_dbg(dev, "device %x:%x:%x %d quirks %d\n", - dev->bus, dev->vendor, dev->product, dev->version, quirks); + hid_dbg(dev, "device %x:%x:%x %d quirks %d report_id %d\n", + dev->bus, dev->vendor, dev->product, dev->version, quirks, + field->report->id); if (quirks & HID_BATTERY_QUIRK_IGNORE) return 0; - psy_desc = kzalloc_obj(*psy_desc); - if (!psy_desc) + bat = devm_kzalloc(&dev->dev, sizeof(*bat), GFP_KERNEL); + if (!bat) return -ENOMEM; - psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery", - strlen(dev->uniq) ? - dev->uniq : dev_name(&dev->dev)); + psy_desc = devm_kzalloc(&dev->dev, sizeof(*psy_desc), GFP_KERNEL); + if (!psy_desc) { + error = -ENOMEM; + goto err_free_bat; + } + + psy_desc->name = devm_kasprintf(&dev->dev, GFP_KERNEL, + "hid-%s-battery-%d", + strlen(dev->uniq) ? + dev->uniq : dev_name(&dev->dev), + field->report->id); if (!psy_desc->name) { error = -ENOMEM; - goto err_free_mem; + goto err_free_desc; } psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; @@ -560,102 +579,95 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, if (quirks & HID_BATTERY_QUIRK_FEATURE) report_type = HID_FEATURE_REPORT; - dev->battery_min = min; - dev->battery_max = max; - dev->battery_report_type = report_type; - dev->battery_report_id = field->report->id; - dev->battery_charge_status = POWER_SUPPLY_STATUS_DISCHARGING; + bat->dev = dev; + bat->min = min; + bat->max = max; + bat->report_type = report_type; + bat->report_id = field->report->id; + bat->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; + bat->status = HID_BATTERY_UNKNOWN; /* * Stylus is normally not connected to the device and thus we * can't query the device and get meaningful battery strength. * We have to wait for the device to report it on its own. */ - dev->battery_avoid_query = report_type == HID_INPUT_REPORT && - field->physical == HID_DG_STYLUS; + bat->avoid_query = report_type == HID_INPUT_REPORT && + field->physical == HID_DG_STYLUS; if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY) - dev->battery_avoid_query = true; + bat->avoid_query = true; - dev->battery_present = (quirks & HID_BATTERY_QUIRK_DYNAMIC) ? false : true; + bat->present = (quirks & HID_BATTERY_QUIRK_DYNAMIC) ? false : true; - dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg); - if (IS_ERR(dev->battery)) { - error = PTR_ERR(dev->battery); + psy_cfg.drv_data = bat; + bat->ps = devm_power_supply_register(&dev->dev, psy_desc, &psy_cfg); + if (IS_ERR(bat->ps)) { + error = PTR_ERR(bat->ps); hid_warn(dev, "can't register power supply: %d\n", error); goto err_free_name; } - power_supply_powers(dev->battery, &dev->dev); + power_supply_powers(bat->ps, &dev->dev); + list_add_tail(&bat->list, &dev->batteries); return 0; err_free_name: - kfree(psy_desc->name); -err_free_mem: - kfree(psy_desc); - dev->battery = NULL; + devm_kfree(&dev->dev, psy_desc->name); +err_free_desc: + devm_kfree(&dev->dev, psy_desc); +err_free_bat: + devm_kfree(&dev->dev, bat); return error; } -static void hidinput_cleanup_battery(struct hid_device *dev) -{ - const struct power_supply_desc *psy_desc; - - if (!dev->battery) - return; - - psy_desc = dev->battery->desc; - power_supply_unregister(dev->battery); - kfree(psy_desc->name); - kfree(psy_desc); - dev->battery = NULL; -} - -static bool hidinput_update_battery_charge_status(struct hid_device *dev, +static bool hidinput_update_battery_charge_status(struct hid_battery *bat, unsigned int usage, int value) { switch (usage) { case HID_BAT_CHARGING: - dev->battery_charge_status = value ? - POWER_SUPPLY_STATUS_CHARGING : - POWER_SUPPLY_STATUS_DISCHARGING; + bat->charge_status = value ? + POWER_SUPPLY_STATUS_CHARGING : + POWER_SUPPLY_STATUS_DISCHARGING; return true; } return false; } -static void hidinput_update_battery(struct hid_device *dev, unsigned int usage, - int value) +static void hidinput_update_battery(struct hid_device *dev, int report_id, + unsigned int usage, int value) { + struct hid_battery *bat; int capacity; - if (!dev->battery) + bat = hidinput_find_battery(dev, report_id); + if (!bat) return; - if (hidinput_update_battery_charge_status(dev, usage, value)) { - dev->battery_present = true; - power_supply_changed(dev->battery); + if (hidinput_update_battery_charge_status(bat, usage, value)) { + bat->present = true; + power_supply_changed(bat->ps); return; } if ((usage & HID_USAGE_PAGE) == HID_UP_DIGITIZER && value == 0) return; - if (value < dev->battery_min || value > dev->battery_max) + if (value < bat->min || value > bat->max) return; - capacity = hidinput_scale_battery_capacity(dev, value); + capacity = hidinput_scale_battery_capacity(bat, value); - if (dev->battery_status != HID_BATTERY_REPORTED || - capacity != dev->battery_capacity || - ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) { - dev->battery_present = true; - dev->battery_capacity = capacity; - dev->battery_status = HID_BATTERY_REPORTED; - dev->battery_ratelimit_time = + if (bat->status != HID_BATTERY_REPORTED || + capacity != bat->capacity || + ktime_after(ktime_get_coarse(), bat->ratelimit_time)) { + bat->present = true; + bat->capacity = capacity; + bat->status = HID_BATTERY_REPORTED; + bat->ratelimit_time = ktime_add_ms(ktime_get_coarse(), 30 * 1000); - power_supply_changed(dev->battery); + power_supply_changed(bat->ps); } } #else /* !CONFIG_HID_BATTERY_STRENGTH */ @@ -665,12 +677,8 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, return 0; } -static void hidinput_cleanup_battery(struct hid_device *dev) -{ -} - -static void hidinput_update_battery(struct hid_device *dev, unsigned int usage, - int value) +static void hidinput_update_battery(struct hid_device *dev, int report_id, + unsigned int usage, int value) { } #endif /* CONFIG_HID_BATTERY_STRENGTH */ @@ -1557,7 +1565,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; if (usage->type == EV_PWR) { - hidinput_update_battery(hid, usage->hid, value); + hidinput_update_battery(hid, report->id, usage->hid, value); return; } @@ -1839,7 +1847,6 @@ static void hidinput_led_worker(struct work_struct *work) struct hid_report *report; int ret; u32 len; - __u8 *buf; field = hidinput_get_led_field(hid); if (!field) @@ -1866,7 +1873,7 @@ static void hidinput_led_worker(struct work_struct *work) /* fall back to generic raw-output-report */ len = hid_report_len(report); - buf = hid_alloc_report_buf(report, GFP_KERNEL); + u8 *buf __free(kfree) = hid_alloc_report_buf(report, GFP_KERNEL); if (!buf) return; @@ -1876,7 +1883,6 @@ static void hidinput_led_worker(struct work_struct *work) if (ret == -ENOSYS) hid_hw_raw_request(hid, report->id, buf, len, HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); - kfree(buf); } static int hidinput_input_event(struct input_dev *dev, unsigned int type, @@ -2403,8 +2409,6 @@ void hidinput_disconnect(struct hid_device *hid) { struct hid_input *hidinput, *next; - hidinput_cleanup_battery(hid); - list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { list_del(&hidinput->list); if (hidinput->registered) diff --git a/drivers/hid/hid-lenovo-go-s.c b/drivers/hid/hid-lenovo-go-s.c new file mode 100644 index 000000000000..ff1782a75191 --- /dev/null +++ b/drivers/hid/hid-lenovo-go-s.c @@ -0,0 +1,1548 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HID driver for Lenovo Legion Go S devices. + * + * Copyright (c) 2026 Derek J. Clark + * Copyright (c) 2026 Valve Corporation + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hid-ids.h" + +#define GO_S_CFG_INTF_IN 0x84 +#define GO_S_PACKET_SIZE 64 + +static struct hid_gos_cfg { + struct delayed_work gos_cfg_setup; + struct completion send_cmd_complete; + struct led_classdev *led_cdev; + struct hid_device *hdev; + struct mutex cfg_mutex; /*ensure single synchronous output report*/ + u8 gp_auto_sleep_time; + u8 gp_dpad_mode; + u8 gp_mode; + u8 gp_poll_rate; + u8 imu_bypass_en; + u8 imu_manufacturer; + u8 imu_sensor_en; + u8 mcu_id[12]; + u8 mouse_step; + u8 os_mode; + u8 rgb_effect; + u8 rgb_en; + u8 rgb_mode; + u8 rgb_profile; + u8 rgb_speed; + u8 tp_en; + u8 tp_linux_mode; + u8 tp_windows_mode; + u8 tp_version; + u8 tp_manufacturer; +} drvdata; + +struct gos_cfg_attr { + u8 index; +}; + +struct command_report { + u8 cmd; + u8 sub_cmd; + u8 data[63]; +} __packed; + +struct version_report { + u8 cmd; + u32 version; + u8 reserved[59]; +} __packed; + +enum mcu_command_index { + GET_VERSION = 0x01, + GET_MCU_ID, + GET_GAMEPAD_CFG, + SET_GAMEPAD_CFG, + GET_TP_PARAM, + SET_TP_PARAM, + GET_RGB_CFG = 0x0f, + SET_RGB_CFG, + GET_PL_TEST = 0xdf, +}; + +enum feature_enabled_index { + FEATURE_DISABLED, + FEATURE_ENABLED, +}; + +static const char *const feature_enabled_text[] = { + [FEATURE_DISABLED] = "false", + [FEATURE_ENABLED] = "true", +}; + +enum feature_status_index { + FEATURE_NONE = 0x00, + FEATURE_GAMEPAD_MODE = 0x01, + FEATURE_AUTO_SLEEP_TIME = 0x04, + FEATURE_IMU_BYPASS, + FEATURE_RGB_ENABLE, + FEATURE_IMU_ENABLE, + FEATURE_TOUCHPAD_ENABLE, + FEATURE_OS_MODE = 0x0A, + FEATURE_POLL_RATE = 0x10, + FEATURE_DPAD_MODE, + FEATURE_MOUSE_WHEEL_STEP, +}; + +enum gamepad_mode_index { + XINPUT, + DINPUT, +}; + +static const char *const gamepad_mode_text[] = { + [XINPUT] = "xinput", + [DINPUT] = "dinput", +}; + +enum os_type_index { + WINDOWS, + LINUX, +}; + +static const char *const os_type_text[] = { + [WINDOWS] = "windows", + [LINUX] = "linux", +}; + +enum poll_rate_index { + HZ125, + HZ250, + HZ500, + HZ1000, +}; + +static const char *const poll_rate_text[] = { + [HZ125] = "125", + [HZ250] = "250", + [HZ500] = "500", + [HZ1000] = "1000", +}; + +enum dpad_mode_index { + DIR8, + DIR4, +}; + +static const char *const dpad_mode_text[] = { + [DIR8] = "8-way", + [DIR4] = "4-way", +}; + +enum touchpad_mode_index { + TP_REL, + TP_ABS, +}; + +static const char *const touchpad_mode_text[] = { + [TP_REL] = "relative", + [TP_ABS] = "absolute", +}; + +enum touchpad_config_index { + CFG_WINDOWS_MODE = 0x03, + CFG_LINUX_MODE, + +}; + +enum rgb_mode_index { + RGB_MODE_DYNAMIC, + RGB_MODE_CUSTOM, +}; + +static const char *const rgb_mode_text[] = { + [RGB_MODE_DYNAMIC] = "dynamic", + [RGB_MODE_CUSTOM] = "custom", +}; + +enum rgb_effect_index { + RGB_EFFECT_MONO, + RGB_EFFECT_BREATHE, + RGB_EFFECT_CHROMA, + RGB_EFFECT_RAINBOW, +}; + +static const char *const rgb_effect_text[] = { + [RGB_EFFECT_MONO] = "monocolor", + [RGB_EFFECT_BREATHE] = "breathe", + [RGB_EFFECT_CHROMA] = "chroma", + [RGB_EFFECT_RAINBOW] = "rainbow", +}; + +enum rgb_config_index { + LIGHT_MODE_SEL = 0x01, + LIGHT_PROFILE_SEL, + USR_LIGHT_PROFILE_1, + USR_LIGHT_PROFILE_2, + USR_LIGHT_PROFILE_3, +}; + +enum test_command_index { + TEST_TP_MFR = 0x02, + TEST_IMU_MFR, + TEST_TP_VER, +}; + +enum tp_mfr_index { + TP_NONE, + TP_BETTERLIFE, + TP_SIPO, +}; + +static const char *const touchpad_manufacturer_text[] = { + [TP_NONE] = "none", + [TP_BETTERLIFE] = "BetterLife", + [TP_SIPO] = "SIPO", +}; + +enum imu_mfr_index { + IMU_NONE, + IMU_BOSCH, + IMU_ST, +}; + +static const char *const imu_manufacturer_text[] = { + [IMU_NONE] = "none", + [IMU_BOSCH] = "Bosch", + [IMU_ST] = "ST", +}; + +static int hid_gos_version_event(u8 *data) +{ + struct version_report *ver_rep = (struct version_report *)data; + + drvdata.hdev->firmware_version = get_unaligned_le32(&ver_rep->version); + return 0; +} + +static int hid_gos_mcu_id_event(struct command_report *cmd_rep) +{ + drvdata.mcu_id[0] = cmd_rep->sub_cmd; + memcpy(&drvdata.mcu_id[1], cmd_rep->data, 11); + + return 0; +} + +static int hid_gos_gamepad_cfg_event(struct command_report *cmd_rep) +{ + int ret = 0; + + switch (cmd_rep->sub_cmd) { + case FEATURE_GAMEPAD_MODE: + drvdata.gp_mode = cmd_rep->data[0]; + break; + case FEATURE_AUTO_SLEEP_TIME: + drvdata.gp_auto_sleep_time = cmd_rep->data[0]; + break; + case FEATURE_IMU_BYPASS: + drvdata.imu_bypass_en = cmd_rep->data[0]; + break; + case FEATURE_RGB_ENABLE: + drvdata.rgb_en = cmd_rep->data[0]; + break; + case FEATURE_IMU_ENABLE: + drvdata.imu_sensor_en = cmd_rep->data[0]; + break; + case FEATURE_TOUCHPAD_ENABLE: + drvdata.tp_en = cmd_rep->data[0]; + break; + case FEATURE_OS_MODE: + drvdata.os_mode = cmd_rep->data[0]; + break; + case FEATURE_POLL_RATE: + drvdata.gp_poll_rate = cmd_rep->data[0]; + break; + case FEATURE_DPAD_MODE: + drvdata.gp_dpad_mode = cmd_rep->data[0]; + break; + case FEATURE_MOUSE_WHEEL_STEP: + drvdata.mouse_step = cmd_rep->data[0]; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int hid_gos_touchpad_event(struct command_report *cmd_rep) +{ + int ret = 0; + + switch (cmd_rep->sub_cmd) { + case CFG_LINUX_MODE: + drvdata.tp_linux_mode = cmd_rep->data[0]; + break; + case CFG_WINDOWS_MODE: + drvdata.tp_windows_mode = cmd_rep->data[0]; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int hid_gos_pl_test_event(struct command_report *cmd_rep) +{ + int ret = 0; + + switch (cmd_rep->sub_cmd) { + case TEST_TP_MFR: + drvdata.tp_manufacturer = cmd_rep->data[0]; + ret = 0; + break; + case TEST_IMU_MFR: + drvdata.imu_manufacturer = cmd_rep->data[0]; + ret = 0; + break; + case TEST_TP_VER: + drvdata.tp_version = cmd_rep->data[0]; + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int hid_gos_light_event(struct command_report *cmd_rep) +{ + struct led_classdev_mc *mc_cdev; + int ret = 0; + + switch (cmd_rep->sub_cmd) { + case LIGHT_MODE_SEL: + drvdata.rgb_mode = cmd_rep->data[0]; + ret = 0; + break; + case LIGHT_PROFILE_SEL: + drvdata.rgb_profile = cmd_rep->data[0]; + ret = 0; + break; + case USR_LIGHT_PROFILE_1: + case USR_LIGHT_PROFILE_2: + case USR_LIGHT_PROFILE_3: + mc_cdev = lcdev_to_mccdev(drvdata.led_cdev); + drvdata.rgb_effect = cmd_rep->data[0]; + mc_cdev->subled_info[0].intensity = cmd_rep->data[1]; + mc_cdev->subled_info[1].intensity = cmd_rep->data[2]; + mc_cdev->subled_info[2].intensity = cmd_rep->data[3]; + drvdata.led_cdev->brightness = cmd_rep->data[4]; + drvdata.rgb_speed = cmd_rep->data[5]; + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int hid_gos_set_event_return(struct command_report *cmd_rep) +{ + if (cmd_rep->data[0] != 0) + return -EIO; + + return 0; +} + +static int get_endpoint_address(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_host_endpoint *ep; + + if (intf) { + ep = intf->cur_altsetting->endpoint; + if (ep) + return ep->desc.bEndpointAddress; + } + + return -ENODEV; +} + +static int hid_gos_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct command_report *cmd_rep; + int ep, ret; + + ep = get_endpoint_address(hdev); + if (ep != GO_S_CFG_INTF_IN) + return 0; + + if (size != GO_S_PACKET_SIZE) + return -EINVAL; + + cmd_rep = (struct command_report *)data; + + switch (cmd_rep->cmd) { + case GET_VERSION: + ret = hid_gos_version_event(data); + break; + case GET_MCU_ID: + ret = hid_gos_mcu_id_event(cmd_rep); + break; + case GET_GAMEPAD_CFG: + ret = hid_gos_gamepad_cfg_event(cmd_rep); + break; + case GET_TP_PARAM: + ret = hid_gos_touchpad_event(cmd_rep); + break; + case GET_PL_TEST: + ret = hid_gos_pl_test_event(cmd_rep); + break; + case GET_RGB_CFG: + ret = hid_gos_light_event(cmd_rep); + break; + case SET_GAMEPAD_CFG: + case SET_RGB_CFG: + case SET_TP_PARAM: + ret = hid_gos_set_event_return(cmd_rep); + break; + default: + ret = -EINVAL; + break; + } + dev_dbg(&hdev->dev, "Rx data as raw input report: [%*ph]\n", + GO_S_PACKET_SIZE, data); + + complete(&drvdata.send_cmd_complete); + return ret; +} + +static int mcu_property_out(struct hid_device *hdev, u8 command, u8 index, + u8 *data, size_t len) +{ + unsigned char *dmabuf __free(kfree) = NULL; + u8 header[] = { command, index }; + size_t header_size = ARRAY_SIZE(header); + int timeout, ret; + + if (header_size + len > GO_S_PACKET_SIZE) + return -EINVAL; + + guard(mutex)(&drvdata.cfg_mutex); + /* We can't use a devm_alloc reusable buffer without side effects during suspend */ + dmabuf = kzalloc(GO_S_PACKET_SIZE, GFP_KERNEL); + if (!dmabuf) + return -ENOMEM; + + memcpy(dmabuf, header, header_size); + memcpy(dmabuf + header_size, data, len); + + dev_dbg(&hdev->dev, "Send data as raw output report: [%*ph]\n", + GO_S_PACKET_SIZE, dmabuf); + + ret = hid_hw_output_report(hdev, dmabuf, GO_S_PACKET_SIZE); + if (ret < 0) + return ret; + + ret = ret == GO_S_PACKET_SIZE ? 0 : -EINVAL; + if (ret) + return ret; + + /* PL_TEST commands can take longer because they go out to another device */ + timeout = (command == GET_PL_TEST) ? 200 : 5; + ret = wait_for_completion_interruptible_timeout(&drvdata.send_cmd_complete, + msecs_to_jiffies(timeout)); + + if (ret == 0) /* timeout occurred */ + ret = -EBUSY; + + reinit_completion(&drvdata.send_cmd_complete); + return 0; +} + +static ssize_t gamepad_property_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count, + enum feature_status_index index) +{ + size_t size = 1; + u8 val = 0; + int ret; + + switch (index) { + case FEATURE_GAMEPAD_MODE: + ret = sysfs_match_string(gamepad_mode_text, buf); + if (ret < 0) + return ret; + val = ret; + break; + case FEATURE_AUTO_SLEEP_TIME: + ret = kstrtou8(buf, 10, &val); + if (ret) + return ret; + break; + case FEATURE_IMU_ENABLE: + ret = sysfs_match_string(feature_enabled_text, buf); + if (ret < 0) + return ret; + val = ret; + break; + case FEATURE_IMU_BYPASS: + ret = sysfs_match_string(feature_enabled_text, buf); + if (ret < 0) + return ret; + val = ret; + break; + case FEATURE_RGB_ENABLE: + ret = sysfs_match_string(feature_enabled_text, buf); + if (ret < 0) + return ret; + val = ret; + break; + case FEATURE_TOUCHPAD_ENABLE: + ret = sysfs_match_string(feature_enabled_text, buf); + if (ret < 0) + return ret; + val = ret; + break; + case FEATURE_OS_MODE: + ret = sysfs_match_string(os_type_text, buf); + if (ret < 0) + return ret; + val = ret; + break; + case FEATURE_POLL_RATE: + ret = sysfs_match_string(poll_rate_text, buf); + if (ret < 0) + return ret; + val = ret; + break; + case FEATURE_DPAD_MODE: + ret = sysfs_match_string(dpad_mode_text, buf); + if (ret < 0) + return ret; + val = ret; + break; + case FEATURE_MOUSE_WHEEL_STEP: + ret = kstrtou8(buf, 10, &val); + if (ret) + return ret; + if (val < 1 || val > 127) + return -EINVAL; + break; + default: + return -EINVAL; + } + + if (!val) + size = 0; + + ret = mcu_property_out(drvdata.hdev, SET_GAMEPAD_CFG, index, &val, + size); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t gamepad_property_show(struct device *dev, + struct device_attribute *attr, char *buf, + enum feature_status_index index) +{ + ssize_t count = 0; + u8 i; + + count = mcu_property_out(drvdata.hdev, GET_GAMEPAD_CFG, index, NULL, 0); + if (count < 0) + return count; + + switch (index) { + case FEATURE_GAMEPAD_MODE: + i = drvdata.gp_mode; + if (i >= ARRAY_SIZE(gamepad_mode_text)) + return -EINVAL; + count = sysfs_emit(buf, "%s\n", gamepad_mode_text[i]); + break; + case FEATURE_AUTO_SLEEP_TIME: + count = sysfs_emit(buf, "%u\n", drvdata.gp_auto_sleep_time); + break; + case FEATURE_IMU_ENABLE: + i = drvdata.imu_sensor_en; + if (i >= ARRAY_SIZE(feature_enabled_text)) + return -EINVAL; + count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]); + break; + case FEATURE_IMU_BYPASS: + i = drvdata.imu_bypass_en; + if (i >= ARRAY_SIZE(feature_enabled_text)) + return -EINVAL; + count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]); + break; + case FEATURE_RGB_ENABLE: + i = drvdata.rgb_en; + if (i >= ARRAY_SIZE(feature_enabled_text)) + return -EINVAL; + count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]); + break; + case FEATURE_TOUCHPAD_ENABLE: + i = drvdata.tp_en; + if (i >= ARRAY_SIZE(feature_enabled_text)) + return -EINVAL; + count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]); + break; + case FEATURE_OS_MODE: + i = drvdata.os_mode; + if (i >= ARRAY_SIZE(os_type_text)) + return -EINVAL; + count = sysfs_emit(buf, "%s\n", os_type_text[i]); + break; + case FEATURE_POLL_RATE: + i = drvdata.gp_poll_rate; + if (i >= ARRAY_SIZE(poll_rate_text)) + return -EINVAL; + count = sysfs_emit(buf, "%s\n", poll_rate_text[i]); + break; + case FEATURE_DPAD_MODE: + i = drvdata.gp_dpad_mode; + if (i >= ARRAY_SIZE(dpad_mode_text)) + return -EINVAL; + count = sysfs_emit(buf, "%s\n", dpad_mode_text[i]); + break; + case FEATURE_MOUSE_WHEEL_STEP: + i = drvdata.mouse_step; + if (i < 1 || i > 127) + return -EINVAL; + count = sysfs_emit(buf, "%u\n", i); + break; + default: + return -EINVAL; + } + + return count; +} + +static ssize_t gamepad_property_options(struct device *dev, + struct device_attribute *attr, + char *buf, + enum feature_status_index index) +{ + size_t count = 0; + unsigned int i; + + switch (index) { + case FEATURE_GAMEPAD_MODE: + for (i = 0; i < ARRAY_SIZE(gamepad_mode_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + gamepad_mode_text[i]); + } + break; + case FEATURE_AUTO_SLEEP_TIME: + return sysfs_emit(buf, "0-255\n"); + case FEATURE_IMU_ENABLE: + for (i = 0; i < ARRAY_SIZE(feature_enabled_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + feature_enabled_text[i]); + } + break; + case FEATURE_IMU_BYPASS: + case FEATURE_RGB_ENABLE: + case FEATURE_TOUCHPAD_ENABLE: + for (i = 0; i < ARRAY_SIZE(feature_enabled_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + feature_enabled_text[i]); + } + break; + case FEATURE_OS_MODE: + for (i = 0; i < ARRAY_SIZE(os_type_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + os_type_text[i]); + } + break; + case FEATURE_POLL_RATE: + for (i = 0; i < ARRAY_SIZE(poll_rate_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + poll_rate_text[i]); + } + break; + case FEATURE_DPAD_MODE: + for (i = 0; i < ARRAY_SIZE(dpad_mode_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + dpad_mode_text[i]); + } + break; + case FEATURE_MOUSE_WHEEL_STEP: + return sysfs_emit(buf, "1-127\n"); + default: + return count; + } + + if (count) + buf[count - 1] = '\n'; + + return count; +} + +static ssize_t touchpad_property_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count, + enum touchpad_config_index index) +{ + size_t size = 1; + u8 val = 0; + int ret; + + switch (index) { + case CFG_WINDOWS_MODE: + ret = sysfs_match_string(touchpad_mode_text, buf); + if (ret < 0) + return ret; + val = ret; + break; + case CFG_LINUX_MODE: + ret = sysfs_match_string(touchpad_mode_text, buf); + if (ret < 0) + return ret; + val = ret; + break; + default: + return -EINVAL; + } + if (!val) + size = 0; + + ret = mcu_property_out(drvdata.hdev, SET_TP_PARAM, index, &val, size); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t touchpad_property_show(struct device *dev, + struct device_attribute *attr, char *buf, + enum touchpad_config_index index) +{ + int ret = 0; + u8 i; + + ret = mcu_property_out(drvdata.hdev, GET_TP_PARAM, index, NULL, 0); + if (ret < 0) + return ret; + + switch (index) { + case CFG_WINDOWS_MODE: + i = drvdata.tp_windows_mode; + break; + case CFG_LINUX_MODE: + i = drvdata.tp_linux_mode; + break; + default: + return -EINVAL; + } + + if (i >= ARRAY_SIZE(touchpad_mode_text)) + return -EINVAL; + + return sysfs_emit(buf, "%s\n", touchpad_mode_text[i]); +} + +static ssize_t touchpad_property_options(struct device *dev, + struct device_attribute *attr, + char *buf, + enum touchpad_config_index index) +{ + size_t count = 0; + unsigned int i; + + switch (index) { + case CFG_WINDOWS_MODE: + case CFG_LINUX_MODE: + for (i = 0; i < ARRAY_SIZE(touchpad_mode_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + touchpad_mode_text[i]); + } + break; + default: + return count; + } + + if (count) + buf[count - 1] = '\n'; + + return count; +} + +static ssize_t test_property_show(struct device *dev, + struct device_attribute *attr, char *buf, + enum test_command_index index) +{ + size_t count = 0; + u8 i; + + switch (index) { + case TEST_TP_MFR: + i = drvdata.tp_manufacturer; + if (i >= ARRAY_SIZE(touchpad_manufacturer_text)) + return -EINVAL; + count = sysfs_emit(buf, "%s\n", touchpad_manufacturer_text[i]); + break; + case TEST_IMU_MFR: + i = drvdata.imu_manufacturer; + if (i >= ARRAY_SIZE(imu_manufacturer_text)) + return -EINVAL; + count = sysfs_emit(buf, "%s\n", imu_manufacturer_text[i]); + break; + case TEST_TP_VER: + count = sysfs_emit(buf, "%u\n", drvdata.tp_version); + break; + default: + count = -EINVAL; + break; + } + + return count; +} + +static ssize_t mcu_id_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%*phN\n", 12, &drvdata.mcu_id); +} + +static int rgb_cfg_call(struct hid_device *hdev, enum mcu_command_index cmd, + enum rgb_config_index index, u8 *val, size_t size) +{ + if (cmd != SET_RGB_CFG && cmd != GET_RGB_CFG) + return -EINVAL; + + if (index < LIGHT_MODE_SEL || index > USR_LIGHT_PROFILE_3) + return -EINVAL; + + return mcu_property_out(hdev, cmd, index, val, size); +} + +static int rgb_attr_show(void) +{ + enum rgb_config_index index; + + index = drvdata.rgb_profile + 2; + + return rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, index, NULL, 0); +}; + +static ssize_t rgb_effect_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev); + enum rgb_config_index index; + u8 effect; + int ret; + + ret = sysfs_match_string(rgb_effect_text, buf); + if (ret < 0) + return ret; + + effect = ret; + index = drvdata.rgb_profile + 2; + u8 rgb_profile[6] = { effect, + mc_cdev->subled_info[0].intensity, + mc_cdev->subled_info[1].intensity, + mc_cdev->subled_info[2].intensity, + drvdata.led_cdev->brightness, + drvdata.rgb_speed }; + + ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6); + if (ret) + return ret; + + drvdata.rgb_effect = effect; + return count; +}; + +static ssize_t rgb_effect_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + + ret = rgb_attr_show(); + if (ret) + return ret; + + if (drvdata.rgb_effect >= ARRAY_SIZE(rgb_effect_text)) + return -EINVAL; + + return sysfs_emit(buf, "%s\n", rgb_effect_text[drvdata.rgb_effect]); +} + +static ssize_t rgb_effect_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t count = 0; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rgb_effect_text); i++) + count += sysfs_emit_at(buf, count, "%s ", rgb_effect_text[i]); + + if (count) + buf[count - 1] = '\n'; + + return count; +} + +static ssize_t rgb_speed_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev); + enum rgb_config_index index; + int val = 0; + int ret; + + ret = kstrtoint(buf, 10, &val); + if (ret) + return ret; + + if (val < 0 || val > 100) + return -EINVAL; + + index = drvdata.rgb_profile + 2; + u8 rgb_profile[6] = { drvdata.rgb_effect, + mc_cdev->subled_info[0].intensity, + mc_cdev->subled_info[1].intensity, + mc_cdev->subled_info[2].intensity, + drvdata.led_cdev->brightness, + val }; + + ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6); + if (ret) + return ret; + + drvdata.rgb_speed = val; + + return count; +}; + +static ssize_t rgb_speed_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret; + + ret = rgb_attr_show(); + if (ret) + return ret; + + if (drvdata.rgb_speed > 100) + return -EINVAL; + + return sysfs_emit(buf, "%hhu\n", drvdata.rgb_speed); +} + +static ssize_t rgb_speed_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "0-100\n"); +} + +static ssize_t rgb_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + u8 val; + + ret = sysfs_match_string(rgb_mode_text, buf); + if (ret <= 0) + return ret; + + val = ret; + + ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_MODE_SEL, &val, + 1); + if (ret) + return ret; + + drvdata.rgb_mode = val; + + return count; +}; + +static ssize_t rgb_mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret; + + ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_MODE_SEL, NULL, 0); + if (ret) + return ret; + + if (drvdata.rgb_mode >= ARRAY_SIZE(rgb_mode_text)) + return -EINVAL; + + return sysfs_emit(buf, "%s\n", rgb_mode_text[drvdata.rgb_mode]); +}; + +static ssize_t rgb_mode_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t count = 0; + unsigned int i; + + for (i = 1; i < ARRAY_SIZE(rgb_mode_text); i++) + count += sysfs_emit_at(buf, count, "%s ", rgb_mode_text[i]); + + if (count) + buf[count - 1] = '\n'; + + return count; +} + +static ssize_t rgb_profile_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + size_t size = 1; + int ret; + u8 val; + + ret = kstrtou8(buf, 10, &val); + if (ret < 0) + return ret; + + if (val < 1 || val > 3) + return -EINVAL; + + ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_PROFILE_SEL, &val, size); + if (ret) + return ret; + + drvdata.rgb_profile = val; + + return count; +}; + +static ssize_t rgb_profile_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + + ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_PROFILE_SEL, NULL, 0); + if (ret) + return ret; + + if (drvdata.rgb_profile < 1 || drvdata.rgb_profile > 3) + return -EINVAL; + + return sysfs_emit(buf, "%hhu\n", drvdata.rgb_profile); +}; + +static ssize_t rgb_profile_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "1-3\n"); +} + +static void hid_gos_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev); + enum rgb_config_index index; + int ret; + + if (brightness > led_cdev->max_brightness) { + dev_err(led_cdev->dev, "Invalid argument\n"); + return; + } + + index = drvdata.rgb_profile + 2; + u8 rgb_profile[6] = { drvdata.rgb_effect, + mc_cdev->subled_info[0].intensity, + mc_cdev->subled_info[1].intensity, + mc_cdev->subled_info[2].intensity, + brightness, + drvdata.rgb_speed }; + + ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6); + switch (ret) { + case 0: + led_cdev->brightness = brightness; + break; + case -ENODEV: /* during switch to IAP -ENODEV is expected */ + case -ENOSYS: /* during rmmod -ENOSYS is expected */ + dev_dbg(led_cdev->dev, "Failed to write RGB profile: %i\n", + ret); + break; + default: + dev_err(led_cdev->dev, "Failed to write RGB profile: %i\n", + ret); + } +} + +#define LEGOS_DEVICE_ATTR_RW(_name, _attrname, _rtype, _group) \ + static ssize_t _name##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + return _group##_property_store(dev, attr, buf, count, \ + _name.index); \ + } \ + static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ + { \ + return _group##_property_show(dev, attr, buf, _name.index); \ + } \ + static ssize_t _name##_##_rtype##_show( \ + struct device *dev, struct device_attribute *attr, char *buf) \ + { \ + return _group##_property_options(dev, attr, buf, _name.index); \ + } \ + static DEVICE_ATTR_RW_NAMED(_name, _attrname) + +#define LEGOS_DEVICE_ATTR_RO(_name, _attrname, _group) \ + static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ + { \ + return _group##_property_show(dev, attr, buf, _name.index); \ + } \ + static DEVICE_ATTR_RO_NAMED(_name, _attrname) + +/* Gamepad */ +static struct gos_cfg_attr auto_sleep_time = { FEATURE_AUTO_SLEEP_TIME }; +LEGOS_DEVICE_ATTR_RW(auto_sleep_time, "auto_sleep_time", range, gamepad); +static DEVICE_ATTR_RO(auto_sleep_time_range); + +static struct gos_cfg_attr dpad_mode = { FEATURE_DPAD_MODE }; +LEGOS_DEVICE_ATTR_RW(dpad_mode, "dpad_mode", index, gamepad); +static DEVICE_ATTR_RO(dpad_mode_index); + +static struct gos_cfg_attr gamepad_mode = { FEATURE_GAMEPAD_MODE }; +LEGOS_DEVICE_ATTR_RW(gamepad_mode, "mode", index, gamepad); +static DEVICE_ATTR_RO_NAMED(gamepad_mode_index, "mode_index"); + +static struct gos_cfg_attr gamepad_poll_rate = { FEATURE_POLL_RATE }; +LEGOS_DEVICE_ATTR_RW(gamepad_poll_rate, "poll_rate", index, gamepad); +static DEVICE_ATTR_RO_NAMED(gamepad_poll_rate_index, "poll_rate_index"); + +static struct attribute *legos_gamepad_attrs[] = { + &dev_attr_auto_sleep_time.attr, + &dev_attr_auto_sleep_time_range.attr, + &dev_attr_dpad_mode.attr, + &dev_attr_dpad_mode_index.attr, + &dev_attr_gamepad_mode.attr, + &dev_attr_gamepad_mode_index.attr, + &dev_attr_gamepad_poll_rate.attr, + &dev_attr_gamepad_poll_rate_index.attr, + NULL, +}; + +static const struct attribute_group gamepad_attr_group = { + .name = "gamepad", + .attrs = legos_gamepad_attrs, +}; + +/* IMU */ +static struct gos_cfg_attr imu_bypass_enabled = { FEATURE_IMU_BYPASS }; +LEGOS_DEVICE_ATTR_RW(imu_bypass_enabled, "bypass_enabled", index, gamepad); +static DEVICE_ATTR_RO_NAMED(imu_bypass_enabled_index, "bypass_enabled_index"); + +static struct gos_cfg_attr imu_manufacturer = { TEST_IMU_MFR }; +LEGOS_DEVICE_ATTR_RO(imu_manufacturer, "manufacturer", test); + +static struct gos_cfg_attr imu_sensor_enabled = { FEATURE_IMU_ENABLE }; +LEGOS_DEVICE_ATTR_RW(imu_sensor_enabled, "sensor_enabled", index, gamepad); +static DEVICE_ATTR_RO_NAMED(imu_sensor_enabled_index, "sensor_enabled_index"); + +static struct attribute *legos_imu_attrs[] = { + &dev_attr_imu_bypass_enabled.attr, + &dev_attr_imu_bypass_enabled_index.attr, + &dev_attr_imu_manufacturer.attr, + &dev_attr_imu_sensor_enabled.attr, + &dev_attr_imu_sensor_enabled_index.attr, + NULL, +}; + +static const struct attribute_group imu_attr_group = { + .name = "imu", + .attrs = legos_imu_attrs, +}; + +/* MCU */ +static DEVICE_ATTR_RO(mcu_id); + +static struct gos_cfg_attr os_mode = { FEATURE_OS_MODE }; +LEGOS_DEVICE_ATTR_RW(os_mode, "os_mode", index, gamepad); +static DEVICE_ATTR_RO(os_mode_index); + +static struct attribute *legos_mcu_attrs[] = { + &dev_attr_mcu_id.attr, + &dev_attr_os_mode.attr, + &dev_attr_os_mode_index.attr, + NULL, +}; + +static const struct attribute_group mcu_attr_group = { + .attrs = legos_mcu_attrs, +}; + +/* Mouse */ +static struct gos_cfg_attr mouse_wheel_step = { FEATURE_MOUSE_WHEEL_STEP }; +LEGOS_DEVICE_ATTR_RW(mouse_wheel_step, "step", range, gamepad); +static DEVICE_ATTR_RO_NAMED(mouse_wheel_step_range, "step_range"); + +static struct attribute *legos_mouse_attrs[] = { + &dev_attr_mouse_wheel_step.attr, + &dev_attr_mouse_wheel_step_range.attr, + NULL, +}; + +static const struct attribute_group mouse_attr_group = { + .name = "mouse", + .attrs = legos_mouse_attrs, +}; + +/* Touchpad */ +static struct gos_cfg_attr touchpad_enabled = { FEATURE_TOUCHPAD_ENABLE }; +LEGOS_DEVICE_ATTR_RW(touchpad_enabled, "enabled", index, gamepad); +static DEVICE_ATTR_RO_NAMED(touchpad_enabled_index, "enabled_index"); + +static struct gos_cfg_attr touchpad_linux_mode = { CFG_LINUX_MODE }; +LEGOS_DEVICE_ATTR_RW(touchpad_linux_mode, "linux_mode", index, touchpad); +static DEVICE_ATTR_RO_NAMED(touchpad_linux_mode_index, "linux_mode_index"); + +static struct gos_cfg_attr touchpad_manufacturer = { TEST_TP_MFR }; +LEGOS_DEVICE_ATTR_RO(touchpad_manufacturer, "manufacturer", test); + +static struct gos_cfg_attr touchpad_version = { TEST_TP_VER }; +LEGOS_DEVICE_ATTR_RO(touchpad_version, "version", test); + +static struct gos_cfg_attr touchpad_windows_mode = { CFG_WINDOWS_MODE }; +LEGOS_DEVICE_ATTR_RW(touchpad_windows_mode, "windows_mode", index, touchpad); +static DEVICE_ATTR_RO_NAMED(touchpad_windows_mode_index, "windows_mode_index"); + +static struct attribute *legos_touchpad_attrs[] = { + &dev_attr_touchpad_enabled.attr, + &dev_attr_touchpad_enabled_index.attr, + &dev_attr_touchpad_linux_mode.attr, + &dev_attr_touchpad_linux_mode_index.attr, + &dev_attr_touchpad_manufacturer.attr, + &dev_attr_touchpad_version.attr, + &dev_attr_touchpad_windows_mode.attr, + &dev_attr_touchpad_windows_mode_index.attr, + NULL, +}; + +static const struct attribute_group touchpad_attr_group = { + .name = "touchpad", + .attrs = legos_touchpad_attrs, +}; + +static const struct attribute_group *top_level_attr_groups[] = { + &gamepad_attr_group, + &imu_attr_group, + &mcu_attr_group, + &mouse_attr_group, + &touchpad_attr_group, + NULL, +}; + +/* RGB */ +static struct gos_cfg_attr rgb_enabled = { FEATURE_RGB_ENABLE }; +LEGOS_DEVICE_ATTR_RW(rgb_enabled, "enabled", index, gamepad); +static DEVICE_ATTR_RO_NAMED(rgb_enabled_index, "enabled_index"); + +static DEVICE_ATTR_RW_NAMED(rgb_effect, "effect"); +static DEVICE_ATTR_RO_NAMED(rgb_effect_index, "effect_index"); +static DEVICE_ATTR_RW_NAMED(rgb_mode, "mode"); +static DEVICE_ATTR_RO_NAMED(rgb_mode_index, "mode_index"); +static DEVICE_ATTR_RW_NAMED(rgb_profile, "profile"); +static DEVICE_ATTR_RO_NAMED(rgb_profile_range, "profile_range"); +static DEVICE_ATTR_RW_NAMED(rgb_speed, "speed"); +static DEVICE_ATTR_RO_NAMED(rgb_speed_range, "speed_range"); + +static struct attribute *gos_rgb_attrs[] = { + &dev_attr_rgb_enabled.attr, + &dev_attr_rgb_enabled_index.attr, + &dev_attr_rgb_effect.attr, + &dev_attr_rgb_effect_index.attr, + &dev_attr_rgb_mode.attr, + &dev_attr_rgb_mode_index.attr, + &dev_attr_rgb_profile.attr, + &dev_attr_rgb_profile_range.attr, + &dev_attr_rgb_speed.attr, + &dev_attr_rgb_speed_range.attr, + NULL, +}; + +static struct attribute_group rgb_attr_group = { + .attrs = gos_rgb_attrs, +}; + +static struct mc_subled gos_rgb_subled_info[] = { + { + .color_index = LED_COLOR_ID_RED, + .brightness = 0x50, + .intensity = 0x24, + .channel = 0x1, + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = 0x50, + .intensity = 0x22, + .channel = 0x2, + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = 0x50, + .intensity = 0x99, + .channel = 0x3, + }, +}; + +static struct led_classdev_mc gos_cdev_rgb = { + .led_cdev = { + .name = "go_s:rgb:joystick_rings", + .brightness = 0x50, + .max_brightness = 0x64, + .brightness_set = hid_gos_brightness_set, + }, + .num_colors = ARRAY_SIZE(gos_rgb_subled_info), + .subled_info = gos_rgb_subled_info, +}; + +static void cfg_setup(struct work_struct *work) +{ + int ret; + + /* MCU */ + ret = mcu_property_out(drvdata.hdev, GET_MCU_ID, FEATURE_NONE, NULL, 0); + if (ret) { + dev_err(&drvdata.hdev->dev, "Failed to retrieve MCU ID: %i\n", + ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, GET_VERSION, FEATURE_NONE, NULL, 0); + if (ret) { + dev_err(&drvdata.hdev->dev, "Failed to retrieve MCU Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, GET_PL_TEST, TEST_TP_MFR, NULL, 0); + if (ret) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve Touchpad Manufacturer: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, GET_PL_TEST, TEST_TP_VER, NULL, 0); + if (ret) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve Touchpad Firmware Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, GET_PL_TEST, TEST_IMU_MFR, NULL, 0); + if (ret) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve IMU Manufacturer: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, GET_GAMEPAD_CFG, FEATURE_OS_MODE, + NULL, 0); + if (ret) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve OS Mode: %i\n", ret); + return; + } +} + +static int hid_gos_cfg_probe(struct hid_device *hdev, + const struct hid_device_id *_id) +{ + int ret; + + hid_set_drvdata(hdev, &drvdata); + drvdata.hdev = hdev; + mutex_init(&drvdata.cfg_mutex); + + ret = sysfs_create_groups(&hdev->dev.kobj, top_level_attr_groups); + if (ret) { + dev_err_probe(&hdev->dev, ret, + "Failed to create gamepad configuration attributes\n"); + return ret; + } + + ret = devm_led_classdev_multicolor_register(&hdev->dev, &gos_cdev_rgb); + if (ret) { + dev_err_probe(&hdev->dev, ret, "Failed to create RGB device\n"); + return ret; + } + + ret = devm_device_add_group(gos_cdev_rgb.led_cdev.dev, &rgb_attr_group); + if (ret) { + dev_err_probe(&hdev->dev, ret, + "Failed to create RGB configuration attributes\n"); + return ret; + } + + drvdata.led_cdev = &gos_cdev_rgb.led_cdev; + + init_completion(&drvdata.send_cmd_complete); + + /* Executing calls prior to returning from probe will lock the MCU. Schedule + * initial data call after probe has completed and MCU can accept calls. + */ + INIT_DELAYED_WORK(&drvdata.gos_cfg_setup, &cfg_setup); + ret = schedule_delayed_work(&drvdata.gos_cfg_setup, msecs_to_jiffies(2)); + if (!ret) { + dev_err(&hdev->dev, "Failed to schedule startup delayed work\n"); + return -ENODEV; + } + + return 0; +} + +static void hid_gos_cfg_remove(struct hid_device *hdev) +{ + guard(mutex)(&drvdata.cfg_mutex); + cancel_delayed_work_sync(&drvdata.gos_cfg_setup); + sysfs_remove_groups(&hdev->dev.kobj, top_level_attr_groups); + hid_hw_close(hdev); + hid_hw_stop(hdev); + hid_set_drvdata(hdev, NULL); +} + +static int hid_gos_cfg_reset_resume(struct hid_device *hdev) +{ + u8 os_mode = drvdata.os_mode; + int ret; + + ret = mcu_property_out(drvdata.hdev, SET_GAMEPAD_CFG, + FEATURE_OS_MODE, &os_mode, 1); + if (ret < 0) + return ret; + + ret = mcu_property_out(drvdata.hdev, GET_GAMEPAD_CFG, + FEATURE_OS_MODE, NULL, 0); + if (ret < 0) + return ret; + + if (drvdata.os_mode != os_mode) + return -ENODEV; + + return 0; +} + +static int hid_gos_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret, ep; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "Parse failed\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) { + hid_err(hdev, "Failed to start HID device\n"); + return ret; + } + + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, "Failed to open HID device\n"); + hid_hw_stop(hdev); + return ret; + } + + ep = get_endpoint_address(hdev); + if (ep != GO_S_CFG_INTF_IN) { + dev_dbg(&hdev->dev, "Started interface %x as generic HID device.\n", ep); + return 0; + } + + ret = hid_gos_cfg_probe(hdev, id); + if (ret) + dev_err_probe(&hdev->dev, ret, "Failed to start configuration interface"); + + dev_dbg(&hdev->dev, "Started interface %x as Go S configuration interface\n", ep); + return ret; +} + +static void hid_gos_remove(struct hid_device *hdev) +{ + int ep = get_endpoint_address(hdev); + + switch (ep) { + case GO_S_CFG_INTF_IN: + hid_gos_cfg_remove(hdev); + break; + default: + hid_hw_close(hdev); + hid_hw_stop(hdev); + + break; + } +} + +static int hid_gos_reset_resume(struct hid_device *hdev) +{ + int ep = get_endpoint_address(hdev); + + switch (ep) { + case GO_S_CFG_INTF_IN: + return hid_gos_cfg_reset_resume(hdev); + default: + break; + } + + return 0; +} + +static const struct hid_device_id hid_gos_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_QHE, + USB_DEVICE_ID_LENOVO_LEGION_GO_S_XINPUT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_QHE, + USB_DEVICE_ID_LENOVO_LEGION_GO_S_DINPUT) }, + {} +}; + +MODULE_DEVICE_TABLE(hid, hid_gos_devices); +static struct hid_driver hid_lenovo_go_s = { + .name = "hid-lenovo-go-s", + .id_table = hid_gos_devices, + .probe = hid_gos_probe, + .remove = hid_gos_remove, + .raw_event = hid_gos_raw_event, + .reset_resume = hid_gos_reset_resume, +}; +module_hid_driver(hid_lenovo_go_s); + +MODULE_AUTHOR("Derek J. Clark"); +MODULE_DESCRIPTION("HID Driver for Lenovo Legion Go S Series gamepad."); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lenovo-go.c b/drivers/hid/hid-lenovo-go.c new file mode 100644 index 000000000000..d4d26c783356 --- /dev/null +++ b/drivers/hid/hid-lenovo-go.c @@ -0,0 +1,2500 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HID driver for Lenovo Legion Go series gamepads. + * + * Copyright (c) 2026 Derek J. Clark + * Copyright (c) 2026 Valve Corporation + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hid-ids.h" + +#define GO_GP_INTF_IN 0x83 +#define GO_OUTPUT_REPORT_ID 0x05 +#define GO_GP_RESET_SUCCESS 0x01 +#define GO_PACKET_SIZE 64 + +static struct hid_go_cfg { + struct delayed_work go_cfg_setup; + struct completion send_cmd_complete; + struct led_classdev *led_cdev; + struct hid_device *hdev; + struct mutex cfg_mutex; /*ensure single synchronous output report*/ + u8 fps_mode; + u8 gp_left_auto_sleep_time; + u8 gp_left_gyro_cal_status; + u8 gp_left_joy_cal_status; + u8 gp_left_notify_en; + u8 gp_left_rumble_mode; + u8 gp_left_trigg_cal_status; + u32 gp_left_version_firmware; + u8 gp_left_version_gen; + u32 gp_left_version_hardware; + u32 gp_left_version_product; + u32 gp_left_version_protocol; + u8 gp_mode; + u8 gp_right_auto_sleep_time; + u8 gp_right_gyro_cal_status; + u8 gp_right_joy_cal_status; + u8 gp_right_notify_en; + u8 gp_right_rumble_mode; + u8 gp_right_trigg_cal_status; + u32 gp_right_version_firmware; + u8 gp_right_version_gen; + u32 gp_right_version_hardware; + u32 gp_right_version_product; + u32 gp_right_version_protocol; + u8 gp_rumble_intensity; + u8 imu_left_bypass_en; + u8 imu_left_sensor_en; + u8 imu_right_bypass_en; + u8 imu_right_sensor_en; + u32 mcu_version_firmware; + u8 mcu_version_gen; + u32 mcu_version_hardware; + u32 mcu_version_product; + u32 mcu_version_protocol; + u32 mouse_dpi; + u8 os_mode; + u8 rgb_effect; + u8 rgb_en; + u8 rgb_mode; + u8 rgb_profile; + u8 rgb_speed; + u8 tp_en; + u8 tp_vibration_en; + u8 tp_vibration_intensity; + u32 tx_dongle_version_firmware; + u8 tx_dongle_version_gen; + u32 tx_dongle_version_hardware; + u32 tx_dongle_version_product; + u32 tx_dongle_version_protocol; +} drvdata; + +struct go_cfg_attr { + u8 index; +}; + +struct command_report { + u8 report_id; + u8 id; + u8 cmd; + u8 sub_cmd; + u8 device_type; + u8 data[59]; +} __packed; + +enum command_id { + MCU_CONFIG_DATA = 0x00, + OS_MODE_DATA = 0x06, + GAMEPAD_DATA = 0x3c, +}; + +enum mcu_command_index { + GET_VERSION_DATA = 0x02, + GET_FEATURE_STATUS, + SET_FEATURE_STATUS, + GET_MOTOR_CFG, + SET_MOTOR_CFG, + GET_DPI_CFG, + SET_DPI_CFG, + SET_TRIGGER_CFG = 0x0a, + SET_JOYSTICK_CFG = 0x0c, + SET_GYRO_CFG = 0x0e, + GET_RGB_CFG, + SET_RGB_CFG, + GET_DEVICE_STATUS = 0xa0, + +}; + +enum dev_type { + UNSPECIFIED, + USB_MCU, + TX_DONGLE, + LEFT_CONTROLLER, + RIGHT_CONTROLLER, +}; + +enum enabled_status_index { + FEATURE_UNKNOWN, + FEATURE_ENABLED, + FEATURE_DISABLED, +}; + +static const char *const enabled_status_text[] = { + [FEATURE_UNKNOWN] = "unknown", + [FEATURE_ENABLED] = "true", + [FEATURE_DISABLED] = "false", +}; + +enum version_data_index { + PRODUCT_VERSION = 0x02, + PROTOCOL_VERSION, + FIRMWARE_VERSION, + HARDWARE_VERSION, + HARDWARE_GENERATION, +}; + +enum feature_status_index { + FEATURE_RESET_GAMEPAD = 0x02, + FEATURE_IMU_BYPASS, + FEATURE_IMU_ENABLE = 0x05, + FEATURE_TOUCHPAD_ENABLE = 0x07, + FEATURE_LIGHT_ENABLE, + FEATURE_AUTO_SLEEP_TIME, + FEATURE_FPS_SWITCH_STATUS = 0x0b, + FEATURE_GAMEPAD_MODE = 0x0e, +}; + +#define FEATURE_OS_MODE 0x69 + +enum fps_switch_status_index { + FPS_STATUS_UNKNOWN, + GAMEPAD, + FPS, +}; + +static const char *const fps_switch_text[] = { + [FPS_STATUS_UNKNOWN] = "unknown", + [GAMEPAD] = "gamepad", + [FPS] = "fps", +}; + +enum gamepad_mode_index { + GAMEPAD_MODE_UNKNOWN, + XINPUT, + DINPUT, +}; + +static const char *const gamepad_mode_text[] = { + [GAMEPAD_MODE_UNKNOWN] = "unknown", + [XINPUT] = "xinput", + [DINPUT] = "dinput", +}; + +enum motor_cfg_index { + MOTOR_CFG_ALL = 0x01, + MOTOR_INTENSITY, + VIBRATION_NOTIFY_ENABLE, + RUMBLE_MODE, + TP_VIBRATION_ENABLE, + TP_VIBRATION_INTENSITY, +}; + +enum intensity_index { + INTENSITY_UNKNOWN, + INTENSITY_OFF, + INTENSITY_LOW, + INTENSITY_MEDIUM, + INTENSITY_HIGH, +}; + +static const char *const intensity_text[] = { + [INTENSITY_UNKNOWN] = "unknown", + [INTENSITY_OFF] = "off", + [INTENSITY_LOW] = "low", + [INTENSITY_MEDIUM] = "medium", + [INTENSITY_HIGH] = "high", +}; + +enum rumble_mode_index { + RUMBLE_MODE_UNKNOWN, + RUMBLE_MODE_FPS, + RUMBLE_MODE_RACE, + RUMBLE_MODE_AVERAGE, + RUMBLE_MODE_SPG, + RUMBLE_MODE_RPG, +}; + +static const char *const rumble_mode_text[] = { + [RUMBLE_MODE_UNKNOWN] = "unknown", + [RUMBLE_MODE_FPS] = "fps", + [RUMBLE_MODE_RACE] = "racing", + [RUMBLE_MODE_AVERAGE] = "standard", + [RUMBLE_MODE_SPG] = "spg", + [RUMBLE_MODE_RPG] = "rpg", +}; + +#define FPS_MODE_DPI 0x02 +#define TRIGGER_CALIBRATE 0x04 +#define JOYSTICK_CALIBRATE 0x04 +#define GYRO_CALIBRATE 0x06 + +enum cal_device_type { + CALDEV_GYROSCOPE = 0x01, + CALDEV_JOYSTICK, + CALDEV_TRIGGER, + CALDEV_JOY_TRIGGER, +}; + +enum cal_enable { + CAL_UNKNOWN, + CAL_START, + CAL_STOP, +}; + +static const char *const cal_enabled_text[] = { + [CAL_UNKNOWN] = "unknown", + [CAL_START] = "start", + [CAL_STOP] = "stop", +}; + +enum cal_status_index { + CAL_STAT_UNKNOWN, + CAL_STAT_SUCCESS, + CAL_STAT_FAILURE, +}; + +static const char *const cal_status_text[] = { + [CAL_STAT_UNKNOWN] = "unknown", + [CAL_STAT_SUCCESS] = "success", + [CAL_STAT_FAILURE] = "failure", +}; + +enum rgb_config_index { + LIGHT_CFG_ALL = 0x01, + LIGHT_MODE_SEL, + LIGHT_PROFILE_SEL, + USR_LIGHT_PROFILE_1, + USR_LIGHT_PROFILE_2, + USR_LIGHT_PROFILE_3, +}; + +enum rgb_mode_index { + RGB_MODE_UNKNOWN, + RGB_MODE_DYNAMIC, + RGB_MODE_CUSTOM, +}; + +static const char *const rgb_mode_text[] = { + [RGB_MODE_UNKNOWN] = "unknown", + [RGB_MODE_DYNAMIC] = "dynamic", + [RGB_MODE_CUSTOM] = "custom", +}; + +enum rgb_effect_index { + RGB_EFFECT_MONO, + RGB_EFFECT_BREATHE, + RGB_EFFECT_CHROMA, + RGB_EFFECT_RAINBOW, +}; + +static const char *const rgb_effect_text[] = { + [RGB_EFFECT_MONO] = "monocolor", + [RGB_EFFECT_BREATHE] = "breathe", + [RGB_EFFECT_CHROMA] = "chroma", + [RGB_EFFECT_RAINBOW] = "rainbow", +}; + +enum device_status_index { + GET_CAL_STATUS = 0x02, + GET_UPGRADE_STATUS, + GET_MACRO_REC_STATUS, + GET_HOTKEY_TRIGG_STATUS, +}; + +enum os_mode_cfg_index { + SET_OS_MODE = 0x09, + GET_OS_MODE, +}; + +enum os_mode_type_index { + OS_UNKNOWN, + WINDOWS, + LINUX, +}; + +static const char *const os_mode_text[] = { + [OS_UNKNOWN] = "unknown", + [WINDOWS] = "windows", + [LINUX] = "linux", +}; + +static int hid_go_version_event(struct command_report *cmd_rep) +{ + switch (cmd_rep->sub_cmd) { + case PRODUCT_VERSION: + switch (cmd_rep->device_type) { + case USB_MCU: + drvdata.mcu_version_product = + get_unaligned_be32(cmd_rep->data); + return 0; + case TX_DONGLE: + drvdata.tx_dongle_version_product = + get_unaligned_be32(cmd_rep->data); + return 0; + case LEFT_CONTROLLER: + drvdata.gp_left_version_product = + get_unaligned_be32(cmd_rep->data); + return 0; + case RIGHT_CONTROLLER: + drvdata.gp_right_version_product = + get_unaligned_be32(cmd_rep->data); + return 0; + default: + return -EINVAL; + } + case PROTOCOL_VERSION: + switch (cmd_rep->device_type) { + case USB_MCU: + drvdata.mcu_version_protocol = + get_unaligned_be32(cmd_rep->data); + return 0; + case TX_DONGLE: + drvdata.tx_dongle_version_protocol = + get_unaligned_be32(cmd_rep->data); + return 0; + case LEFT_CONTROLLER: + drvdata.gp_left_version_protocol = + get_unaligned_be32(cmd_rep->data); + return 0; + case RIGHT_CONTROLLER: + drvdata.gp_right_version_protocol = + get_unaligned_be32(cmd_rep->data); + return 0; + default: + return -EINVAL; + } + case FIRMWARE_VERSION: + switch (cmd_rep->device_type) { + case USB_MCU: + drvdata.mcu_version_firmware = + get_unaligned_be32(cmd_rep->data); + return 0; + case TX_DONGLE: + drvdata.tx_dongle_version_firmware = + get_unaligned_be32(cmd_rep->data); + return 0; + case LEFT_CONTROLLER: + drvdata.gp_left_version_firmware = + get_unaligned_be32(cmd_rep->data); + return 0; + case RIGHT_CONTROLLER: + drvdata.gp_right_version_firmware = + get_unaligned_be32(cmd_rep->data); + return 0; + default: + return -EINVAL; + } + case HARDWARE_VERSION: + switch (cmd_rep->device_type) { + case USB_MCU: + drvdata.mcu_version_hardware = + get_unaligned_be32(cmd_rep->data); + return 0; + case TX_DONGLE: + drvdata.tx_dongle_version_hardware = + get_unaligned_be32(cmd_rep->data); + return 0; + case LEFT_CONTROLLER: + drvdata.gp_left_version_hardware = + get_unaligned_be32(cmd_rep->data); + return 0; + case RIGHT_CONTROLLER: + drvdata.gp_right_version_hardware = + get_unaligned_be32(cmd_rep->data); + return 0; + default: + return -EINVAL; + } + case HARDWARE_GENERATION: + switch (cmd_rep->device_type) { + case USB_MCU: + drvdata.mcu_version_gen = cmd_rep->data[0]; + return 0; + case TX_DONGLE: + drvdata.tx_dongle_version_gen = cmd_rep->data[0]; + return 0; + case LEFT_CONTROLLER: + drvdata.gp_left_version_gen = cmd_rep->data[0]; + return 0; + case RIGHT_CONTROLLER: + drvdata.gp_right_version_gen = cmd_rep->data[0]; + return 0; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int hid_go_feature_status_event(struct command_report *cmd_rep) +{ + switch (cmd_rep->sub_cmd) { + case FEATURE_RESET_GAMEPAD: + return 0; + case FEATURE_IMU_ENABLE: + switch (cmd_rep->device_type) { + case LEFT_CONTROLLER: + drvdata.imu_left_sensor_en = cmd_rep->data[0]; + return 0; + case RIGHT_CONTROLLER: + drvdata.imu_right_sensor_en = cmd_rep->data[0]; + return 0; + default: + return -EINVAL; + } + case FEATURE_IMU_BYPASS: + switch (cmd_rep->device_type) { + case LEFT_CONTROLLER: + drvdata.imu_left_bypass_en = cmd_rep->data[0]; + return 0; + case RIGHT_CONTROLLER: + drvdata.imu_right_bypass_en = cmd_rep->data[0]; + return 0; + default: + return -EINVAL; + } + break; + case FEATURE_LIGHT_ENABLE: + drvdata.rgb_en = cmd_rep->data[0]; + return 0; + case FEATURE_AUTO_SLEEP_TIME: + switch (cmd_rep->device_type) { + case LEFT_CONTROLLER: + drvdata.gp_left_auto_sleep_time = cmd_rep->data[0]; + return 0; + case RIGHT_CONTROLLER: + drvdata.gp_right_auto_sleep_time = cmd_rep->data[0]; + return 0; + default: + return -EINVAL; + } + break; + case FEATURE_TOUCHPAD_ENABLE: + drvdata.tp_en = cmd_rep->data[0]; + return 0; + case FEATURE_GAMEPAD_MODE: + drvdata.gp_mode = cmd_rep->data[0]; + return 0; + case FEATURE_FPS_SWITCH_STATUS: + drvdata.fps_mode = cmd_rep->data[0]; + return 0; + default: + return -EINVAL; + } +} + +static int hid_go_motor_event(struct command_report *cmd_rep) +{ + switch (cmd_rep->sub_cmd) { + case MOTOR_CFG_ALL: + return -EINVAL; + case MOTOR_INTENSITY: + drvdata.gp_rumble_intensity = cmd_rep->data[0]; + return 0; + case VIBRATION_NOTIFY_ENABLE: + switch (cmd_rep->device_type) { + case LEFT_CONTROLLER: + drvdata.gp_left_notify_en = cmd_rep->data[0]; + return 0; + case RIGHT_CONTROLLER: + drvdata.gp_right_notify_en = cmd_rep->data[0]; + return 0; + default: + return -EINVAL; + } + break; + case RUMBLE_MODE: + switch (cmd_rep->device_type) { + case LEFT_CONTROLLER: + drvdata.gp_left_rumble_mode = cmd_rep->data[0]; + return 0; + case RIGHT_CONTROLLER: + drvdata.gp_right_rumble_mode = cmd_rep->data[0]; + return 0; + default: + return -EINVAL; + } + case TP_VIBRATION_ENABLE: + drvdata.tp_vibration_en = cmd_rep->data[0]; + return 0; + case TP_VIBRATION_INTENSITY: + drvdata.tp_vibration_intensity = cmd_rep->data[0]; + return 0; + } + return -EINVAL; +} + +static int hid_go_fps_dpi_event(struct command_report *cmd_rep) +{ + if (cmd_rep->sub_cmd != FPS_MODE_DPI) + return -EINVAL; + + drvdata.mouse_dpi = get_unaligned_le32(cmd_rep->data); + + return 0; +} + +static int hid_go_light_event(struct command_report *cmd_rep) +{ + struct led_classdev_mc *mc_cdev; + + switch (cmd_rep->sub_cmd) { + case LIGHT_MODE_SEL: + drvdata.rgb_mode = cmd_rep->data[0]; + return 0; + case LIGHT_PROFILE_SEL: + drvdata.rgb_profile = cmd_rep->data[0]; + return 0; + case USR_LIGHT_PROFILE_1: + case USR_LIGHT_PROFILE_2: + case USR_LIGHT_PROFILE_3: + mc_cdev = lcdev_to_mccdev(drvdata.led_cdev); + drvdata.rgb_effect = cmd_rep->data[0]; + mc_cdev->subled_info[0].intensity = cmd_rep->data[1]; + mc_cdev->subled_info[1].intensity = cmd_rep->data[2]; + mc_cdev->subled_info[2].intensity = cmd_rep->data[3]; + drvdata.led_cdev->brightness = cmd_rep->data[4]; + drvdata.rgb_speed = 100 - cmd_rep->data[5]; + return 0; + default: + return -EINVAL; + } +} + +static int hid_go_device_status_event(struct command_report *cmd_rep) +{ + switch (cmd_rep->device_type) { + case LEFT_CONTROLLER: + switch (cmd_rep->data[0]) { + case CALDEV_GYROSCOPE: + drvdata.gp_left_gyro_cal_status = cmd_rep->data[1]; + return 0; + case CALDEV_JOYSTICK: + drvdata.gp_left_joy_cal_status = cmd_rep->data[1]; + return 0; + case CALDEV_TRIGGER: + drvdata.gp_left_trigg_cal_status = cmd_rep->data[1]; + return 0; + default: + return -EINVAL; + } + break; + case RIGHT_CONTROLLER: + switch (cmd_rep->data[0]) { + case CALDEV_GYROSCOPE: + drvdata.gp_right_gyro_cal_status = cmd_rep->data[1]; + return 0; + case CALDEV_JOYSTICK: + drvdata.gp_right_joy_cal_status = cmd_rep->data[1]; + return 0; + case CALDEV_TRIGGER: + drvdata.gp_right_trigg_cal_status = cmd_rep->data[1]; + return 0; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } +} + +static int hid_go_os_mode_cfg_event(struct command_report *cmd_rep) +{ + switch (cmd_rep->sub_cmd) { + case SET_OS_MODE: + if (cmd_rep->data[0] != 1) + return -EIO; + return 0; + case GET_OS_MODE: + drvdata.os_mode = cmd_rep->data[0]; + return 0; + default: + return -EINVAL; + } +} + +static int hid_go_set_event_return(struct command_report *cmd_rep) +{ + if (cmd_rep->data[0] != 0) + return -EIO; + + return 0; +} + +static int get_endpoint_address(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_host_endpoint *ep; + + if (!intf) + return -ENODEV; + + ep = intf->cur_altsetting->endpoint; + if (!ep) + return -ENODEV; + + return ep->desc.bEndpointAddress; +} + +static int hid_go_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct command_report *cmd_rep; + int ep, ret; + + if (size != GO_PACKET_SIZE) + goto passthrough; + + ep = get_endpoint_address(hdev); + if (ep != GO_GP_INTF_IN) + goto passthrough; + + cmd_rep = (struct command_report *)data; + + switch (cmd_rep->id) { + case MCU_CONFIG_DATA: + switch (cmd_rep->cmd) { + case GET_VERSION_DATA: + ret = hid_go_version_event(cmd_rep); + break; + case GET_FEATURE_STATUS: + ret = hid_go_feature_status_event(cmd_rep); + break; + case GET_MOTOR_CFG: + ret = hid_go_motor_event(cmd_rep); + break; + case GET_DPI_CFG: + ret = hid_go_fps_dpi_event(cmd_rep); + break; + case GET_RGB_CFG: + ret = hid_go_light_event(cmd_rep); + break; + case GET_DEVICE_STATUS: + ret = hid_go_device_status_event(cmd_rep); + break; + case SET_FEATURE_STATUS: + case SET_MOTOR_CFG: + case SET_DPI_CFG: + case SET_RGB_CFG: + case SET_TRIGGER_CFG: + case SET_JOYSTICK_CFG: + case SET_GYRO_CFG: + ret = hid_go_set_event_return(cmd_rep); + break; + default: + ret = -EINVAL; + break; + } + break; + case OS_MODE_DATA: + ret = hid_go_os_mode_cfg_event(cmd_rep); + break; + default: + goto passthrough; + } + dev_dbg(&hdev->dev, "Rx data as raw input report: [%*ph]\n", + GO_PACKET_SIZE, data); + + complete(&drvdata.send_cmd_complete); + return ret; + +passthrough: + /* Forward other HID reports so they generate events */ + hid_input_report(hdev, HID_INPUT_REPORT, data, size, 1); + return 0; +} + +static int mcu_property_out(struct hid_device *hdev, u8 id, u8 command, + u8 index, enum dev_type device, u8 *data, size_t len) +{ + unsigned char *dmabuf __free(kfree) = NULL; + u8 header[] = { GO_OUTPUT_REPORT_ID, id, command, index, device }; + size_t header_size = ARRAY_SIZE(header); + int timeout = 50; + int ret; + + if (header_size + len > GO_PACKET_SIZE) + return -EINVAL; + + guard(mutex)(&drvdata.cfg_mutex); + /* We can't use a devm_alloc reusable buffer without side effects during suspend */ + dmabuf = kzalloc(GO_PACKET_SIZE, GFP_KERNEL); + if (!dmabuf) + return -ENOMEM; + + memcpy(dmabuf, header, header_size); + memcpy(dmabuf + header_size, data, len); + + dev_dbg(&hdev->dev, "Send data as raw output report: [%*ph]\n", + GO_PACKET_SIZE, dmabuf); + + ret = hid_hw_output_report(hdev, dmabuf, GO_PACKET_SIZE); + if (ret < 0) + return ret; + + ret = ret == GO_PACKET_SIZE ? 0 : -EINVAL; + if (ret) + return ret; + + ret = wait_for_completion_interruptible_timeout(&drvdata.send_cmd_complete, + msecs_to_jiffies(timeout)); + + if (ret == 0) /* timeout occurred */ + ret = -EBUSY; + + reinit_completion(&drvdata.send_cmd_complete); + return 0; +} + +static ssize_t version_show(struct device *dev, struct device_attribute *attr, + char *buf, enum version_data_index index, + enum dev_type device_type) +{ + ssize_t count = 0; + int ret; + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + index, device_type, NULL, 0); + if (ret) + return ret; + + switch (index) { + case PRODUCT_VERSION: + switch (device_type) { + case USB_MCU: + count = sysfs_emit(buf, "%x\n", + drvdata.mcu_version_product); + break; + case TX_DONGLE: + count = sysfs_emit(buf, "%x\n", + drvdata.tx_dongle_version_product); + break; + case LEFT_CONTROLLER: + count = sysfs_emit(buf, "%x\n", + drvdata.gp_left_version_product); + break; + case RIGHT_CONTROLLER: + count = sysfs_emit(buf, "%x\n", + drvdata.gp_right_version_product); + break; + default: + return -EINVAL; + } + break; + case PROTOCOL_VERSION: + switch (device_type) { + case USB_MCU: + count = sysfs_emit(buf, "%x\n", + drvdata.mcu_version_protocol); + break; + case TX_DONGLE: + count = sysfs_emit(buf, "%x\n", + drvdata.tx_dongle_version_protocol); + break; + case LEFT_CONTROLLER: + count = sysfs_emit(buf, "%x\n", + drvdata.gp_left_version_protocol); + break; + case RIGHT_CONTROLLER: + count = sysfs_emit(buf, "%x\n", + drvdata.gp_right_version_protocol); + break; + default: + return -EINVAL; + } + break; + case FIRMWARE_VERSION: + switch (device_type) { + case USB_MCU: + count = sysfs_emit(buf, "%x\n", + drvdata.mcu_version_firmware); + break; + case TX_DONGLE: + count = sysfs_emit(buf, "%x\n", + drvdata.tx_dongle_version_firmware); + break; + case LEFT_CONTROLLER: + count = sysfs_emit(buf, "%x\n", + drvdata.gp_left_version_firmware); + break; + case RIGHT_CONTROLLER: + count = sysfs_emit(buf, "%x\n", + drvdata.gp_right_version_firmware); + break; + default: + return -EINVAL; + } + break; + case HARDWARE_VERSION: + switch (device_type) { + case USB_MCU: + count = sysfs_emit(buf, "%x\n", + drvdata.mcu_version_hardware); + break; + case TX_DONGLE: + count = sysfs_emit(buf, "%x\n", + drvdata.tx_dongle_version_hardware); + break; + case LEFT_CONTROLLER: + count = sysfs_emit(buf, "%x\n", + drvdata.gp_left_version_hardware); + break; + case RIGHT_CONTROLLER: + count = sysfs_emit(buf, "%x\n", + drvdata.gp_right_version_hardware); + break; + default: + return -EINVAL; + } + break; + case HARDWARE_GENERATION: + switch (device_type) { + case USB_MCU: + count = sysfs_emit(buf, "%x\n", + drvdata.mcu_version_gen); + break; + case TX_DONGLE: + count = sysfs_emit(buf, "%x\n", + drvdata.tx_dongle_version_gen); + break; + case LEFT_CONTROLLER: + count = sysfs_emit(buf, "%x\n", + drvdata.gp_left_version_gen); + break; + case RIGHT_CONTROLLER: + count = sysfs_emit(buf, "%x\n", + drvdata.gp_right_version_gen); + break; + default: + return -EINVAL; + } + break; + } + + return count; +} + +static ssize_t feature_status_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count, + enum feature_status_index index, + enum dev_type device_type) +{ + size_t size = 1; + u8 val = 0; + int ret; + + switch (index) { + case FEATURE_IMU_ENABLE: + case FEATURE_IMU_BYPASS: + case FEATURE_LIGHT_ENABLE: + case FEATURE_TOUCHPAD_ENABLE: + ret = sysfs_match_string(enabled_status_text, buf); + val = ret; + break; + case FEATURE_AUTO_SLEEP_TIME: + ret = kstrtou8(buf, 10, &val); + break; + case FEATURE_RESET_GAMEPAD: + ret = kstrtou8(buf, 10, &val); + if (val != GO_GP_RESET_SUCCESS) + return -EINVAL; + break; + case FEATURE_FPS_SWITCH_STATUS: + ret = sysfs_match_string(fps_switch_text, buf); + val = ret; + break; + case FEATURE_GAMEPAD_MODE: + ret = sysfs_match_string(gamepad_mode_text, buf); + val = ret; + break; + default: + return -EINVAL; + } + + if (ret < 0) + return ret; + + if (!val) + size = 0; + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, + SET_FEATURE_STATUS, index, device_type, &val, + size); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t feature_status_show(struct device *dev, + struct device_attribute *attr, char *buf, + enum feature_status_index index, + enum dev_type device_type) +{ + ssize_t count = 0; + int ret; + u8 i; + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, + GET_FEATURE_STATUS, index, device_type, NULL, 0); + if (ret) + return ret; + + switch (index) { + case FEATURE_IMU_ENABLE: + switch (device_type) { + case LEFT_CONTROLLER: + i = drvdata.imu_left_sensor_en; + break; + case RIGHT_CONTROLLER: + i = drvdata.imu_right_sensor_en; + break; + default: + return -EINVAL; + } + if (i >= ARRAY_SIZE(enabled_status_text)) + return -EINVAL; + + count = sysfs_emit(buf, "%s\n", enabled_status_text[i]); + break; + case FEATURE_IMU_BYPASS: + switch (device_type) { + case LEFT_CONTROLLER: + i = drvdata.imu_left_bypass_en; + break; + case RIGHT_CONTROLLER: + i = drvdata.imu_right_bypass_en; + break; + default: + return -EINVAL; + } + if (i >= ARRAY_SIZE(enabled_status_text)) + return -EINVAL; + + count = sysfs_emit(buf, "%s\n", enabled_status_text[i]); + break; + case FEATURE_LIGHT_ENABLE: + i = drvdata.rgb_en; + if (i >= ARRAY_SIZE(enabled_status_text)) + return -EINVAL; + + count = sysfs_emit(buf, "%s\n", enabled_status_text[i]); + break; + case FEATURE_TOUCHPAD_ENABLE: + i = drvdata.tp_en; + if (i >= ARRAY_SIZE(enabled_status_text)) + return -EINVAL; + + count = sysfs_emit(buf, "%s\n", enabled_status_text[i]); + break; + case FEATURE_AUTO_SLEEP_TIME: + switch (device_type) { + case LEFT_CONTROLLER: + i = drvdata.gp_left_auto_sleep_time; + break; + case RIGHT_CONTROLLER: + i = drvdata.gp_right_auto_sleep_time; + break; + default: + return -EINVAL; + } + count = sysfs_emit(buf, "%u\n", i); + break; + case FEATURE_FPS_SWITCH_STATUS: + i = drvdata.fps_mode; + if (i >= ARRAY_SIZE(fps_switch_text)) + return -EINVAL; + + count = sysfs_emit(buf, "%s\n", fps_switch_text[i]); + break; + case FEATURE_GAMEPAD_MODE: + i = drvdata.gp_mode; + if (i >= ARRAY_SIZE(gamepad_mode_text)) + return -EINVAL; + + count = sysfs_emit(buf, "%s\n", gamepad_mode_text[i]); + break; + default: + return -EINVAL; + } + + return count; +} + +static ssize_t feature_status_options(struct device *dev, + struct device_attribute *attr, char *buf, + enum feature_status_index index) +{ + ssize_t count = 0; + unsigned int i; + + switch (index) { + case FEATURE_IMU_ENABLE: + case FEATURE_IMU_BYPASS: + case FEATURE_LIGHT_ENABLE: + case FEATURE_TOUCHPAD_ENABLE: + for (i = 1; i < ARRAY_SIZE(enabled_status_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + enabled_status_text[i]); + } + break; + case FEATURE_AUTO_SLEEP_TIME: + return sysfs_emit(buf, "0-255\n"); + case FEATURE_FPS_SWITCH_STATUS: + for (i = 1; i < ARRAY_SIZE(fps_switch_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + fps_switch_text[i]); + } + break; + case FEATURE_GAMEPAD_MODE: + for (i = 1; i < ARRAY_SIZE(gamepad_mode_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + gamepad_mode_text[i]); + } + break; + default: + return -EINVAL; + } + + if (count) + buf[count - 1] = '\n'; + + return count; +} + +static ssize_t motor_config_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count, + enum motor_cfg_index index, + enum dev_type device_type) +{ + size_t size = 1; + u8 val = 0; + int ret; + + switch (index) { + case MOTOR_CFG_ALL: + return -EINVAL; + case MOTOR_INTENSITY: + ret = sysfs_match_string(intensity_text, buf); + val = ret; + break; + case VIBRATION_NOTIFY_ENABLE: + ret = sysfs_match_string(enabled_status_text, buf); + val = ret; + break; + case RUMBLE_MODE: + ret = sysfs_match_string(rumble_mode_text, buf); + val = ret; + break; + case TP_VIBRATION_ENABLE: + ret = sysfs_match_string(enabled_status_text, buf); + val = ret; + break; + case TP_VIBRATION_INTENSITY: + ret = sysfs_match_string(intensity_text, buf); + val = ret; + break; + } + + if (ret < 0) + return ret; + + if (!val) + size = 0; + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, SET_MOTOR_CFG, + index, device_type, &val, size); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t motor_config_show(struct device *dev, + struct device_attribute *attr, char *buf, + enum motor_cfg_index index, + enum dev_type device_type) +{ + ssize_t count = 0; + int ret; + u8 i; + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_MOTOR_CFG, + index, device_type, NULL, 0); + if (ret) + return ret; + + switch (index) { + case MOTOR_CFG_ALL: + return -EINVAL; + case MOTOR_INTENSITY: + i = drvdata.gp_rumble_intensity; + if (i >= ARRAY_SIZE(intensity_text)) + return -EINVAL; + + count = sysfs_emit(buf, "%s\n", intensity_text[i]); + break; + case VIBRATION_NOTIFY_ENABLE: + switch (device_type) { + case LEFT_CONTROLLER: + i = drvdata.gp_left_notify_en; + break; + case RIGHT_CONTROLLER: + i = drvdata.gp_right_notify_en; + break; + default: + return -EINVAL; + } + if (i >= ARRAY_SIZE(enabled_status_text)) + return -EINVAL; + + count = sysfs_emit(buf, "%s\n", enabled_status_text[i]); + break; + case RUMBLE_MODE: + switch (device_type) { + case LEFT_CONTROLLER: + i = drvdata.gp_left_rumble_mode; + break; + case RIGHT_CONTROLLER: + i = drvdata.gp_right_rumble_mode; + break; + default: + return -EINVAL; + } + if (i >= ARRAY_SIZE(rumble_mode_text)) + return -EINVAL; + + count = sysfs_emit(buf, "%s\n", rumble_mode_text[i]); + break; + case TP_VIBRATION_ENABLE: + i = drvdata.tp_vibration_en; + if (i >= ARRAY_SIZE(enabled_status_text)) + return -EINVAL; + + count = sysfs_emit(buf, "%s\n", enabled_status_text[i]); + break; + case TP_VIBRATION_INTENSITY: + i = drvdata.tp_vibration_intensity; + if (i >= ARRAY_SIZE(intensity_text)) + return -EINVAL; + + count = sysfs_emit(buf, "%s\n", intensity_text[i]); + break; + } + + return count; +} + +static ssize_t motor_config_options(struct device *dev, + struct device_attribute *attr, char *buf, + enum motor_cfg_index index) +{ + ssize_t count = 0; + unsigned int i; + + switch (index) { + case MOTOR_CFG_ALL: + break; + case RUMBLE_MODE: + for (i = 1; i < ARRAY_SIZE(rumble_mode_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + rumble_mode_text[i]); + } + break; + case MOTOR_INTENSITY: + case TP_VIBRATION_INTENSITY: + for (i = 1; i < ARRAY_SIZE(intensity_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + intensity_text[i]); + } + break; + case VIBRATION_NOTIFY_ENABLE: + case TP_VIBRATION_ENABLE: + for (i = 1; i < ARRAY_SIZE(enabled_status_text); i++) { + count += sysfs_emit_at(buf, count, "%s ", + enabled_status_text[i]); + } + break; + } + + if (count) + buf[count - 1] = '\n'; + + return count; +} + +static ssize_t fps_mode_dpi_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) + +{ + size_t size = 4; + u32 value; + u8 val[4]; + int ret; + + ret = kstrtou32(buf, 10, &value); + if (ret) + return ret; + + if (value != 500 && value != 800 && value != 1200 && value != 1800) + return -EINVAL; + + put_unaligned_le32(value, val); + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, SET_DPI_CFG, + FPS_MODE_DPI, UNSPECIFIED, val, size); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t fps_mode_dpi_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_DPI_CFG, + FPS_MODE_DPI, UNSPECIFIED, NULL, 0); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%u\n", drvdata.mouse_dpi); +} + +static ssize_t fps_mode_dpi_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "500 800 1200 1800\n"); +} + +static ssize_t device_status_show(struct device *dev, + struct device_attribute *attr, char *buf, + enum device_status_index index, + enum dev_type device_type, + enum cal_device_type cal_type) +{ + u8 i; + + switch (index) { + case GET_CAL_STATUS: + switch (device_type) { + case LEFT_CONTROLLER: + switch (cal_type) { + case CALDEV_GYROSCOPE: + i = drvdata.gp_left_gyro_cal_status; + break; + case CALDEV_JOYSTICK: + i = drvdata.gp_left_joy_cal_status; + break; + case CALDEV_TRIGGER: + i = drvdata.gp_left_trigg_cal_status; + break; + default: + return -EINVAL; + } + break; + case RIGHT_CONTROLLER: + switch (cal_type) { + case CALDEV_GYROSCOPE: + i = drvdata.gp_right_gyro_cal_status; + break; + case CALDEV_JOYSTICK: + i = drvdata.gp_right_joy_cal_status; + break; + case CALDEV_TRIGGER: + i = drvdata.gp_right_trigg_cal_status; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + if (i >= ARRAY_SIZE(cal_status_text)) + return -EINVAL; + + return sysfs_emit(buf, "%s\n", cal_status_text[i]); +} + +static ssize_t calibrate_config_store(struct device *dev, + struct device_attribute *attr, + const char *buf, u8 cmd, u8 sub_cmd, + size_t count, enum dev_type device_type) +{ + size_t size = 1; + u8 val = 0; + int ret; + + ret = sysfs_match_string(cal_enabled_text, buf); + if (ret < 0) + return ret; + + val = ret; + if (!val) + size = 0; + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, cmd, sub_cmd, + device_type, &val, size); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t calibrate_config_options(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t count = 0; + unsigned int i; + + for (i = 1; i < ARRAY_SIZE(cal_enabled_text); i++) + count += sysfs_emit_at(buf, count, "%s ", cal_enabled_text[i]); + + buf[count - 1] = '\n'; + + return count; +} + +static ssize_t os_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + size_t size = 1; + int ret; + u8 val; + + ret = sysfs_match_string(os_mode_text, buf); + if (ret <= 0) + return ret; + + val = ret; + ret = mcu_property_out(drvdata.hdev, OS_MODE_DATA, FEATURE_OS_MODE, + SET_OS_MODE, USB_MCU, &val, size); + if (ret < 0) + return ret; + + drvdata.os_mode = val; + + return count; +} + +static ssize_t os_mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t count = 0; + int ret; + u8 i; + + ret = mcu_property_out(drvdata.hdev, OS_MODE_DATA, FEATURE_OS_MODE, + GET_OS_MODE, USB_MCU, NULL, 0); + if (ret) + return ret; + + i = drvdata.os_mode; + if (i >= ARRAY_SIZE(os_mode_text)) + return -EINVAL; + + count = sysfs_emit(buf, "%s\n", os_mode_text[i]); + + return count; +} + +static ssize_t os_mode_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t count = 0; + unsigned int i; + + for (i = 1; i < ARRAY_SIZE(os_mode_text); i++) + count += sysfs_emit_at(buf, count, "%s ", os_mode_text[i]); + + if (count) + buf[count - 1] = '\n'; + + return count; +} + +static int rgb_cfg_call(struct hid_device *hdev, enum mcu_command_index cmd, + enum rgb_config_index index, u8 *val, size_t size) +{ + if (cmd != SET_RGB_CFG && cmd != GET_RGB_CFG) + return -EINVAL; + + if (index < LIGHT_CFG_ALL || index > USR_LIGHT_PROFILE_3) + return -EINVAL; + + return mcu_property_out(hdev, MCU_CONFIG_DATA, cmd, index, UNSPECIFIED, + val, size); +} + +static int rgb_attr_show(void) +{ + enum rgb_config_index index; + + index = drvdata.rgb_profile + 3; + + return rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, index, NULL, 0); +} + +static ssize_t rgb_effect_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev); + enum rgb_config_index index; + u8 effect; + int ret; + + ret = sysfs_match_string(rgb_effect_text, buf); + if (ret < 0) + return ret; + + effect = ret; + index = drvdata.rgb_profile + 3; + u8 rgb_profile[6] = { effect, + mc_cdev->subled_info[0].intensity, + mc_cdev->subled_info[1].intensity, + mc_cdev->subled_info[2].intensity, + drvdata.led_cdev->brightness, + drvdata.rgb_speed }; + + ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6); + if (ret) + return ret; + + drvdata.rgb_effect = effect; + return count; +} + +static ssize_t rgb_effect_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + + ret = rgb_attr_show(); + if (ret) + return ret; + + if (drvdata.rgb_effect >= ARRAY_SIZE(rgb_effect_text)) + return -EINVAL; + + return sysfs_emit(buf, "%s\n", rgb_effect_text[drvdata.rgb_effect]); +} + +static ssize_t rgb_effect_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t count = 0; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rgb_effect_text); i++) + count += sysfs_emit_at(buf, count, "%s ", rgb_effect_text[i]); + + if (count) + buf[count - 1] = '\n'; + + return count; +} + +static ssize_t rgb_speed_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev); + enum rgb_config_index index; + int val = 0; + int ret; + + ret = kstrtoint(buf, 10, &val); + if (ret) + return ret; + + if (val < 0 || val > 100) + return -EINVAL; + + /* This is a delay setting, invert logic for consistency with other drivers */ + val = 100 - val; + + index = drvdata.rgb_profile + 3; + u8 rgb_profile[6] = { drvdata.rgb_effect, + mc_cdev->subled_info[0].intensity, + mc_cdev->subled_info[1].intensity, + mc_cdev->subled_info[2].intensity, + drvdata.led_cdev->brightness, + val }; + + ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6); + if (ret) + return ret; + + drvdata.rgb_speed = val; + + return count; +} + +static ssize_t rgb_speed_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret, val; + + ret = rgb_attr_show(); + if (ret) + return ret; + + if (drvdata.rgb_speed > 100) + return -EINVAL; + + val = drvdata.rgb_speed; + + return sysfs_emit(buf, "%hhu\n", val); +} + +static ssize_t rgb_speed_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "0-100\n"); +} + +static ssize_t rgb_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + u8 val; + + ret = sysfs_match_string(rgb_mode_text, buf); + if (ret <= 0) + return ret; + + val = ret; + + ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_MODE_SEL, &val, 1); + if (ret) + return ret; + + drvdata.rgb_mode = val; + + return count; +} + +static ssize_t rgb_mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret; + + ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_MODE_SEL, NULL, 0); + if (ret) + return ret; + + if (drvdata.rgb_mode >= ARRAY_SIZE(rgb_mode_text)) + return -EINVAL; + + return sysfs_emit(buf, "%s\n", rgb_mode_text[drvdata.rgb_mode]); +} + +static ssize_t rgb_mode_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t count = 0; + unsigned int i; + + for (i = 1; i < ARRAY_SIZE(rgb_mode_text); i++) + count += sysfs_emit_at(buf, count, "%s ", rgb_mode_text[i]); + + if (count) + buf[count - 1] = '\n'; + + return count; +} + +static ssize_t rgb_profile_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + size_t size = 1; + int ret; + u8 val; + + ret = kstrtou8(buf, 10, &val); + if (ret < 0) + return ret; + + if (val < 1 || val > 3) + return -EINVAL; + + ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_PROFILE_SEL, &val, size); + if (ret) + return ret; + + drvdata.rgb_profile = val; + + return count; +} + +static ssize_t rgb_profile_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + + ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_PROFILE_SEL, NULL, 0); + if (ret) + return ret; + + if (drvdata.rgb_profile < 1 || drvdata.rgb_profile > 3) + return -EINVAL; + + return sysfs_emit(buf, "%hhu\n", drvdata.rgb_profile); +} + +static ssize_t rgb_profile_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "1-3\n"); +} + +static void hid_go_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev); + enum rgb_config_index index; + int ret; + + if (brightness > led_cdev->max_brightness) { + dev_err(led_cdev->dev, "Invalid argument\n"); + return; + } + + index = drvdata.rgb_profile + 3; + u8 rgb_profile[6] = { drvdata.rgb_effect, + mc_cdev->subled_info[0].intensity, + mc_cdev->subled_info[1].intensity, + mc_cdev->subled_info[2].intensity, + brightness, + drvdata.rgb_speed }; + + ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6); + switch (ret) { + case 0: + led_cdev->brightness = brightness; + break; + case -ENODEV: /* during switch to IAP -ENODEV is expected */ + case -ENOSYS: /* during rmmod -ENOSYS is expected */ + dev_dbg(led_cdev->dev, "Failed to write RGB profile: %i\n", ret); + break; + default: + dev_err(led_cdev->dev, "Failed to write RGB profile: %i\n", ret); + } +} + +#define LEGO_DEVICE_ATTR_RW(_name, _attrname, _dtype, _rtype, _group) \ + static ssize_t _name##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + return _group##_store(dev, attr, buf, count, _name.index, \ + _dtype); \ + } \ + static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ + { \ + return _group##_show(dev, attr, buf, _name.index, _dtype); \ + } \ + static ssize_t _name##_##_rtype##_show( \ + struct device *dev, struct device_attribute *attr, char *buf) \ + { \ + return _group##_options(dev, attr, buf, _name.index); \ + } \ + static DEVICE_ATTR_RW_NAMED(_name, _attrname) + +#define LEGO_DEVICE_ATTR_WO(_name, _attrname, _dtype, _group) \ + static ssize_t _name##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + return _group##_store(dev, attr, buf, count, _name.index, \ + _dtype); \ + } \ + static DEVICE_ATTR_WO_NAMED(_name, _attrname) + +#define LEGO_DEVICE_ATTR_RO(_name, _attrname, _dtype, _group) \ + static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ + { \ + return _group##_show(dev, attr, buf, _name.index, _dtype); \ + } \ + static DEVICE_ATTR_RO_NAMED(_name, _attrname) + +#define LEGO_CAL_DEVICE_ATTR(_name, _attrname, _scmd, _dtype, _rtype) \ + static ssize_t _name##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + return calibrate_config_store(dev, attr, buf, _name.index, \ + _scmd, count, _dtype); \ + } \ + static ssize_t _name##_##_rtype##_show( \ + struct device *dev, struct device_attribute *attr, char *buf) \ + { \ + return calibrate_config_options(dev, attr, buf); \ + } \ + static DEVICE_ATTR_WO_NAMED(_name, _attrname) + +#define LEGO_DEVICE_STATUS_ATTR(_name, _attrname, _scmd, _dtype) \ + static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ + { \ + return device_status_show(dev, attr, buf, _name.index, _scmd, \ + _dtype); \ + } \ + static DEVICE_ATTR_RO_NAMED(_name, _attrname) + +/* Gamepad - MCU */ +static struct go_cfg_attr version_product_mcu = { PRODUCT_VERSION }; +LEGO_DEVICE_ATTR_RO(version_product_mcu, "product_version", USB_MCU, version); + +static struct go_cfg_attr version_protocol_mcu = { PROTOCOL_VERSION }; +LEGO_DEVICE_ATTR_RO(version_protocol_mcu, "protocol_version", USB_MCU, version); + +static struct go_cfg_attr version_firmware_mcu = { FIRMWARE_VERSION }; +LEGO_DEVICE_ATTR_RO(version_firmware_mcu, "firmware_version", USB_MCU, version); + +static struct go_cfg_attr version_hardware_mcu = { HARDWARE_VERSION }; +LEGO_DEVICE_ATTR_RO(version_hardware_mcu, "hardware_version", USB_MCU, version); + +static struct go_cfg_attr version_gen_mcu = { HARDWARE_GENERATION }; +LEGO_DEVICE_ATTR_RO(version_gen_mcu, "hardware_generation", USB_MCU, version); + +static struct go_cfg_attr fps_switch_status = { FEATURE_FPS_SWITCH_STATUS }; +LEGO_DEVICE_ATTR_RO(fps_switch_status, "fps_switch_status", UNSPECIFIED, + feature_status); + +static struct go_cfg_attr gamepad_mode = { FEATURE_GAMEPAD_MODE }; +LEGO_DEVICE_ATTR_RW(gamepad_mode, "mode", UNSPECIFIED, index, feature_status); +static DEVICE_ATTR_RO_NAMED(gamepad_mode_index, "mode_index"); + +static struct go_cfg_attr reset_mcu = { FEATURE_RESET_GAMEPAD }; +LEGO_DEVICE_ATTR_WO(reset_mcu, "reset_mcu", USB_MCU, feature_status); + +static struct go_cfg_attr gamepad_rumble_intensity = { MOTOR_INTENSITY }; +LEGO_DEVICE_ATTR_RW(gamepad_rumble_intensity, "rumble_intensity", UNSPECIFIED, + index, motor_config); +static DEVICE_ATTR_RO_NAMED(gamepad_rumble_intensity_index, + "rumble_intensity_index"); + +static DEVICE_ATTR_RW(fps_mode_dpi); +static DEVICE_ATTR_RO(fps_mode_dpi_index); + +static DEVICE_ATTR_RW(os_mode); +static DEVICE_ATTR_RO(os_mode_index); + +static struct attribute *mcu_attrs[] = { + &dev_attr_fps_mode_dpi.attr, + &dev_attr_fps_mode_dpi_index.attr, + &dev_attr_fps_switch_status.attr, + &dev_attr_gamepad_mode.attr, + &dev_attr_gamepad_mode_index.attr, + &dev_attr_gamepad_rumble_intensity.attr, + &dev_attr_gamepad_rumble_intensity_index.attr, + &dev_attr_os_mode.attr, + &dev_attr_os_mode_index.attr, + &dev_attr_reset_mcu.attr, + &dev_attr_version_firmware_mcu.attr, + &dev_attr_version_gen_mcu.attr, + &dev_attr_version_hardware_mcu.attr, + &dev_attr_version_product_mcu.attr, + &dev_attr_version_protocol_mcu.attr, + NULL, +}; + +static const struct attribute_group mcu_attr_group = { + .attrs = mcu_attrs, +}; + +/* Gamepad - TX Dongle */ +static struct go_cfg_attr version_product_tx_dongle = { PRODUCT_VERSION }; +LEGO_DEVICE_ATTR_RO(version_product_tx_dongle, "product_version", TX_DONGLE, version); + +static struct go_cfg_attr version_protocol_tx_dongle = { PROTOCOL_VERSION }; +LEGO_DEVICE_ATTR_RO(version_protocol_tx_dongle, "protocol_version", TX_DONGLE, version); + +static struct go_cfg_attr version_firmware_tx_dongle = { FIRMWARE_VERSION }; +LEGO_DEVICE_ATTR_RO(version_firmware_tx_dongle, "firmware_version", TX_DONGLE, version); + +static struct go_cfg_attr version_hardware_tx_dongle = { HARDWARE_VERSION }; +LEGO_DEVICE_ATTR_RO(version_hardware_tx_dongle, "hardware_version", TX_DONGLE, version); + +static struct go_cfg_attr version_gen_tx_dongle = { HARDWARE_GENERATION }; +LEGO_DEVICE_ATTR_RO(version_gen_tx_dongle, "hardware_generation", TX_DONGLE, version); + +static struct go_cfg_attr reset_tx_dongle = { FEATURE_RESET_GAMEPAD }; +LEGO_DEVICE_ATTR_RO(reset_tx_dongle, "reset", TX_DONGLE, feature_status); + +static struct attribute *tx_dongle_attrs[] = { + &dev_attr_reset_tx_dongle.attr, + &dev_attr_version_hardware_tx_dongle.attr, + &dev_attr_version_firmware_tx_dongle.attr, + &dev_attr_version_gen_tx_dongle.attr, + &dev_attr_version_product_tx_dongle.attr, + &dev_attr_version_protocol_tx_dongle.attr, + NULL, +}; + +static const struct attribute_group tx_dongle_attr_group = { + .name = "tx_dongle", + .attrs = tx_dongle_attrs, +}; + +/* Gamepad - Left */ +static struct go_cfg_attr version_product_left = { PRODUCT_VERSION }; +LEGO_DEVICE_ATTR_RO(version_product_left, "product_version", LEFT_CONTROLLER, version); + +static struct go_cfg_attr version_protocol_left = { PROTOCOL_VERSION }; +LEGO_DEVICE_ATTR_RO(version_protocol_left, "protocol_version", LEFT_CONTROLLER, version); + +static struct go_cfg_attr version_firmware_left = { FIRMWARE_VERSION }; +LEGO_DEVICE_ATTR_RO(version_firmware_left, "firmware_version", LEFT_CONTROLLER, version); + +static struct go_cfg_attr version_hardware_left = { HARDWARE_VERSION }; +LEGO_DEVICE_ATTR_RO(version_hardware_left, "hardware_version", LEFT_CONTROLLER, version); + +static struct go_cfg_attr version_gen_left = { HARDWARE_GENERATION }; +LEGO_DEVICE_ATTR_RO(version_gen_left, "hardware_generation", LEFT_CONTROLLER, version); + +static struct go_cfg_attr auto_sleep_time_left = { FEATURE_AUTO_SLEEP_TIME }; +LEGO_DEVICE_ATTR_RW(auto_sleep_time_left, "auto_sleep_time", LEFT_CONTROLLER, + range, feature_status); +static DEVICE_ATTR_RO_NAMED(auto_sleep_time_left_range, + "auto_sleep_time_range"); + +static struct go_cfg_attr imu_bypass_left = { FEATURE_IMU_BYPASS }; +LEGO_DEVICE_ATTR_RW(imu_bypass_left, "imu_bypass_enabled", LEFT_CONTROLLER, + index, feature_status); +static DEVICE_ATTR_RO_NAMED(imu_bypass_left_index, "imu_bypass_enabled_index"); + +static struct go_cfg_attr imu_enabled_left = { FEATURE_IMU_ENABLE }; +LEGO_DEVICE_ATTR_RW(imu_enabled_left, "imu_enabled", LEFT_CONTROLLER, index, + feature_status); +static DEVICE_ATTR_RO_NAMED(imu_enabled_left_index, "imu_enabled_index"); + +static struct go_cfg_attr reset_left = { FEATURE_RESET_GAMEPAD }; +LEGO_DEVICE_ATTR_WO(reset_left, "reset", LEFT_CONTROLLER, feature_status); + +static struct go_cfg_attr rumble_mode_left = { RUMBLE_MODE }; +LEGO_DEVICE_ATTR_RW(rumble_mode_left, "rumble_mode", LEFT_CONTROLLER, index, + motor_config); +static DEVICE_ATTR_RO_NAMED(rumble_mode_left_index, "rumble_mode_index"); + +static struct go_cfg_attr rumble_notification_left = { VIBRATION_NOTIFY_ENABLE }; +LEGO_DEVICE_ATTR_RW(rumble_notification_left, "rumble_notification", + LEFT_CONTROLLER, index, motor_config); +static DEVICE_ATTR_RO_NAMED(rumble_notification_left_index, + "rumble_notification_index"); + +static struct go_cfg_attr cal_trigg_left = { TRIGGER_CALIBRATE }; +LEGO_CAL_DEVICE_ATTR(cal_trigg_left, "calibrate_trigger", SET_TRIGGER_CFG, + LEFT_CONTROLLER, index); +static DEVICE_ATTR_RO_NAMED(cal_trigg_left_index, "calibrate_trigger_index"); + +static struct go_cfg_attr cal_joy_left = { JOYSTICK_CALIBRATE }; +LEGO_CAL_DEVICE_ATTR(cal_joy_left, "calibrate_joystick", SET_JOYSTICK_CFG, + LEFT_CONTROLLER, index); +static DEVICE_ATTR_RO_NAMED(cal_joy_left_index, "calibrate_joystick_index"); + +static struct go_cfg_attr cal_gyro_left = { GYRO_CALIBRATE }; +LEGO_CAL_DEVICE_ATTR(cal_gyro_left, "calibrate_gyro", SET_GYRO_CFG, + LEFT_CONTROLLER, index); +static DEVICE_ATTR_RO_NAMED(cal_gyro_left_index, "calibrate_gyro_index"); + +static struct go_cfg_attr cal_trigg_left_status = { GET_CAL_STATUS }; +LEGO_DEVICE_STATUS_ATTR(cal_trigg_left_status, "calibrate_trigger_status", + LEFT_CONTROLLER, CALDEV_TRIGGER); + +static struct go_cfg_attr cal_joy_left_status = { GET_CAL_STATUS }; +LEGO_DEVICE_STATUS_ATTR(cal_joy_left_status, "calibrate_joystick_status", + LEFT_CONTROLLER, CALDEV_JOYSTICK); + +static struct go_cfg_attr cal_gyro_left_status = { GET_CAL_STATUS }; +LEGO_DEVICE_STATUS_ATTR(cal_gyro_left_status, "calibrate_gyro_status", + LEFT_CONTROLLER, CALDEV_GYROSCOPE); + +static struct attribute *left_gamepad_attrs[] = { + &dev_attr_auto_sleep_time_left.attr, + &dev_attr_auto_sleep_time_left_range.attr, + &dev_attr_cal_gyro_left.attr, + &dev_attr_cal_gyro_left_index.attr, + &dev_attr_cal_gyro_left_status.attr, + &dev_attr_cal_joy_left.attr, + &dev_attr_cal_joy_left_index.attr, + &dev_attr_cal_joy_left_status.attr, + &dev_attr_cal_trigg_left.attr, + &dev_attr_cal_trigg_left_index.attr, + &dev_attr_cal_trigg_left_status.attr, + &dev_attr_imu_bypass_left.attr, + &dev_attr_imu_bypass_left_index.attr, + &dev_attr_imu_enabled_left.attr, + &dev_attr_imu_enabled_left_index.attr, + &dev_attr_reset_left.attr, + &dev_attr_rumble_mode_left.attr, + &dev_attr_rumble_mode_left_index.attr, + &dev_attr_rumble_notification_left.attr, + &dev_attr_rumble_notification_left_index.attr, + &dev_attr_version_hardware_left.attr, + &dev_attr_version_firmware_left.attr, + &dev_attr_version_gen_left.attr, + &dev_attr_version_product_left.attr, + &dev_attr_version_protocol_left.attr, + NULL, +}; + +static const struct attribute_group left_gamepad_attr_group = { + .name = "left_handle", + .attrs = left_gamepad_attrs, +}; + +/* Gamepad - Right */ +static struct go_cfg_attr version_product_right = { PRODUCT_VERSION }; +LEGO_DEVICE_ATTR_RO(version_product_right, "product_version", RIGHT_CONTROLLER, version); + +static struct go_cfg_attr version_protocol_right = { PROTOCOL_VERSION }; +LEGO_DEVICE_ATTR_RO(version_protocol_right, "protocol_version", RIGHT_CONTROLLER, version); + +static struct go_cfg_attr version_firmware_right = { FIRMWARE_VERSION }; +LEGO_DEVICE_ATTR_RO(version_firmware_right, "firmware_version", RIGHT_CONTROLLER, version); + +static struct go_cfg_attr version_hardware_right = { HARDWARE_VERSION }; +LEGO_DEVICE_ATTR_RO(version_hardware_right, "hardware_version", RIGHT_CONTROLLER, version); + +static struct go_cfg_attr version_gen_right = { HARDWARE_GENERATION }; +LEGO_DEVICE_ATTR_RO(version_gen_right, "hardware_generation", RIGHT_CONTROLLER, version); + +static struct go_cfg_attr auto_sleep_time_right = { FEATURE_AUTO_SLEEP_TIME }; +LEGO_DEVICE_ATTR_RW(auto_sleep_time_right, "auto_sleep_time", RIGHT_CONTROLLER, + range, feature_status); +static DEVICE_ATTR_RO_NAMED(auto_sleep_time_right_range, + "auto_sleep_time_range"); + +static struct go_cfg_attr imu_bypass_right = { FEATURE_IMU_BYPASS }; +LEGO_DEVICE_ATTR_RW(imu_bypass_right, "imu_bypass_enabled", RIGHT_CONTROLLER, + index, feature_status); +static DEVICE_ATTR_RO_NAMED(imu_bypass_right_index, "imu_bypass_enabled_index"); + +static struct go_cfg_attr imu_enabled_right = { FEATURE_IMU_BYPASS }; +LEGO_DEVICE_ATTR_RW(imu_enabled_right, "imu_enabled", RIGHT_CONTROLLER, index, + feature_status); +static DEVICE_ATTR_RO_NAMED(imu_enabled_right_index, "imu_enabled_index"); + +static struct go_cfg_attr reset_right = { FEATURE_RESET_GAMEPAD }; +LEGO_DEVICE_ATTR_WO(reset_right, "reset", LEFT_CONTROLLER, feature_status); + +static struct go_cfg_attr rumble_mode_right = { RUMBLE_MODE }; +LEGO_DEVICE_ATTR_RW(rumble_mode_right, "rumble_mode", RIGHT_CONTROLLER, index, + motor_config); +static DEVICE_ATTR_RO_NAMED(rumble_mode_right_index, "rumble_mode_index"); + +static struct go_cfg_attr rumble_notification_right = { VIBRATION_NOTIFY_ENABLE }; +LEGO_DEVICE_ATTR_RW(rumble_notification_right, "rumble_notification", + RIGHT_CONTROLLER, index, motor_config); +static DEVICE_ATTR_RO_NAMED(rumble_notification_right_index, + "rumble_notification_index"); + +static struct go_cfg_attr cal_trigg_right = { TRIGGER_CALIBRATE }; +LEGO_CAL_DEVICE_ATTR(cal_trigg_right, "calibrate_trigger", SET_TRIGGER_CFG, + RIGHT_CONTROLLER, index); +static DEVICE_ATTR_RO_NAMED(cal_trigg_right_index, "calibrate_trigger_index"); + +static struct go_cfg_attr cal_joy_right = { JOYSTICK_CALIBRATE }; +LEGO_CAL_DEVICE_ATTR(cal_joy_right, "calibrate_joystick", SET_JOYSTICK_CFG, + RIGHT_CONTROLLER, index); +static DEVICE_ATTR_RO_NAMED(cal_joy_right_index, "calibrate_joystick_index"); + +static struct go_cfg_attr cal_gyro_right = { GYRO_CALIBRATE }; +LEGO_CAL_DEVICE_ATTR(cal_gyro_right, "calibrate_gyro", SET_GYRO_CFG, + RIGHT_CONTROLLER, index); +static DEVICE_ATTR_RO_NAMED(cal_gyro_right_index, "calibrate_gyro_index"); + +static struct go_cfg_attr cal_trigg_right_status = { GET_CAL_STATUS }; +LEGO_DEVICE_STATUS_ATTR(cal_trigg_right_status, "calibrate_trigger_status", + RIGHT_CONTROLLER, CALDEV_TRIGGER); + +static struct go_cfg_attr cal_joy_right_status = { GET_CAL_STATUS }; +LEGO_DEVICE_STATUS_ATTR(cal_joy_right_status, "calibrate_joystick_status", + RIGHT_CONTROLLER, CALDEV_JOYSTICK); + +static struct go_cfg_attr cal_gyro_right_status = { GET_CAL_STATUS }; +LEGO_DEVICE_STATUS_ATTR(cal_gyro_right_status, "calibrate_gyro_status", + RIGHT_CONTROLLER, CALDEV_GYROSCOPE); + +static struct attribute *right_gamepad_attrs[] = { + &dev_attr_auto_sleep_time_right.attr, + &dev_attr_auto_sleep_time_right_range.attr, + &dev_attr_cal_gyro_right.attr, + &dev_attr_cal_gyro_right_index.attr, + &dev_attr_cal_gyro_right_status.attr, + &dev_attr_cal_joy_right.attr, + &dev_attr_cal_joy_right_index.attr, + &dev_attr_cal_joy_right_status.attr, + &dev_attr_cal_trigg_right.attr, + &dev_attr_cal_trigg_right_index.attr, + &dev_attr_cal_trigg_right_status.attr, + &dev_attr_imu_bypass_right.attr, + &dev_attr_imu_bypass_right_index.attr, + &dev_attr_imu_enabled_right.attr, + &dev_attr_imu_enabled_right_index.attr, + &dev_attr_reset_right.attr, + &dev_attr_rumble_mode_right.attr, + &dev_attr_rumble_mode_right_index.attr, + &dev_attr_rumble_notification_right.attr, + &dev_attr_rumble_notification_right_index.attr, + &dev_attr_version_hardware_right.attr, + &dev_attr_version_firmware_right.attr, + &dev_attr_version_gen_right.attr, + &dev_attr_version_product_right.attr, + &dev_attr_version_protocol_right.attr, + NULL, +}; + +static const struct attribute_group right_gamepad_attr_group = { + .name = "right_handle", + .attrs = right_gamepad_attrs, +}; + +/* Touchpad */ +static struct go_cfg_attr touchpad_enabled = { FEATURE_TOUCHPAD_ENABLE }; +LEGO_DEVICE_ATTR_RW(touchpad_enabled, "enabled", UNSPECIFIED, index, + feature_status); +static DEVICE_ATTR_RO_NAMED(touchpad_enabled_index, "enabled_index"); + +static struct go_cfg_attr touchpad_vibration_enabled = { TP_VIBRATION_ENABLE }; +LEGO_DEVICE_ATTR_RW(touchpad_vibration_enabled, "vibration_enabled", UNSPECIFIED, + index, motor_config); +static DEVICE_ATTR_RO_NAMED(touchpad_vibration_enabled_index, + "vibration_enabled_index"); + +static struct go_cfg_attr touchpad_vibration_intensity = { TP_VIBRATION_INTENSITY }; +LEGO_DEVICE_ATTR_RW(touchpad_vibration_intensity, "vibration_intensity", + UNSPECIFIED, index, motor_config); +static DEVICE_ATTR_RO_NAMED(touchpad_vibration_intensity_index, + "vibration_intensity_index"); + +static struct attribute *touchpad_attrs[] = { + &dev_attr_touchpad_enabled.attr, + &dev_attr_touchpad_enabled_index.attr, + &dev_attr_touchpad_vibration_enabled.attr, + &dev_attr_touchpad_vibration_enabled_index.attr, + &dev_attr_touchpad_vibration_intensity.attr, + &dev_attr_touchpad_vibration_intensity_index.attr, + NULL, +}; + +static const struct attribute_group touchpad_attr_group = { + .name = "touchpad", + .attrs = touchpad_attrs, +}; + +static const struct attribute_group *top_level_attr_groups[] = { + &mcu_attr_group, &tx_dongle_attr_group, + &left_gamepad_attr_group, &right_gamepad_attr_group, + &touchpad_attr_group, NULL, +}; + +/* RGB */ +static struct go_cfg_attr rgb_enabled = { FEATURE_LIGHT_ENABLE }; + +LEGO_DEVICE_ATTR_RW(rgb_enabled, "enabled", UNSPECIFIED, index, feature_status); +static DEVICE_ATTR_RO_NAMED(rgb_effect_index, "effect_index"); +static DEVICE_ATTR_RO_NAMED(rgb_enabled_index, "enabled_index"); +static DEVICE_ATTR_RO_NAMED(rgb_mode_index, "mode_index"); +static DEVICE_ATTR_RO_NAMED(rgb_profile_range, "profile_range"); +static DEVICE_ATTR_RO_NAMED(rgb_speed_range, "speed_range"); +static DEVICE_ATTR_RW_NAMED(rgb_effect, "effect"); +static DEVICE_ATTR_RW_NAMED(rgb_mode, "mode"); +static DEVICE_ATTR_RW_NAMED(rgb_profile, "profile"); +static DEVICE_ATTR_RW_NAMED(rgb_speed, "speed"); + +static struct attribute *go_rgb_attrs[] = { + &dev_attr_rgb_effect.attr, + &dev_attr_rgb_effect_index.attr, + &dev_attr_rgb_enabled.attr, + &dev_attr_rgb_enabled_index.attr, + &dev_attr_rgb_mode.attr, + &dev_attr_rgb_mode_index.attr, + &dev_attr_rgb_profile.attr, + &dev_attr_rgb_profile_range.attr, + &dev_attr_rgb_speed.attr, + &dev_attr_rgb_speed_range.attr, + NULL, +}; + +static struct attribute_group rgb_attr_group = { + .attrs = go_rgb_attrs, +}; + +static struct mc_subled go_rgb_subled_info[] = { + { + .color_index = LED_COLOR_ID_RED, + .brightness = 0x50, + .intensity = 0x24, + .channel = 0x1, + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = 0x50, + .intensity = 0x22, + .channel = 0x2, + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = 0x50, + .intensity = 0x99, + .channel = 0x3, + }, +}; + +static struct led_classdev_mc go_cdev_rgb = { + .led_cdev = { + .name = "go:rgb:joystick_rings", + .color = LED_COLOR_ID_RGB, + .brightness = 0x50, + .max_brightness = 0x64, + .brightness_set = hid_go_brightness_set, + }, + .num_colors = ARRAY_SIZE(go_rgb_subled_info), + .subled_info = go_rgb_subled_info, +}; + +static void cfg_setup(struct work_struct *work) +{ + int ret; + + /* MCU Version Attrs */ + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + PRODUCT_VERSION, USB_MCU, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve USB_MCU Product Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + PROTOCOL_VERSION, USB_MCU, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve USB_MCU Protocol Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + FIRMWARE_VERSION, USB_MCU, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve USB_MCU Firmware Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + HARDWARE_VERSION, USB_MCU, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve USB_MCU Hardware Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + HARDWARE_GENERATION, USB_MCU, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve USB_MCU Hardware Generation: %i\n", ret); + return; + } + + /* TX Dongle Version Attrs */ + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + PRODUCT_VERSION, TX_DONGLE, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve TX_DONGLE Product Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + PROTOCOL_VERSION, TX_DONGLE, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve TX_DONGLE Protocol Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + FIRMWARE_VERSION, TX_DONGLE, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve TX_DONGLE Firmware Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + HARDWARE_VERSION, TX_DONGLE, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve TX_DONGLE Hardware Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + HARDWARE_GENERATION, TX_DONGLE, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve TX_DONGLE Hardware Generation: %i\n", ret); + return; + } + + /* Left Handle Version Attrs */ + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + PRODUCT_VERSION, LEFT_CONTROLLER, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve LEFT_CONTROLLER Product Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + PROTOCOL_VERSION, LEFT_CONTROLLER, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve LEFT_CONTROLLER Protocol Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + FIRMWARE_VERSION, LEFT_CONTROLLER, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve LEFT_CONTROLLER Firmware Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + HARDWARE_VERSION, LEFT_CONTROLLER, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve LEFT_CONTROLLER Hardware Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + HARDWARE_GENERATION, LEFT_CONTROLLER, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve LEFT_CONTROLLER Hardware Generation: %i\n", ret); + return; + } + + /* Right Handle Version Attrs */ + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + PRODUCT_VERSION, RIGHT_CONTROLLER, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve RIGHT_CONTROLLER Product Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + PROTOCOL_VERSION, RIGHT_CONTROLLER, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve RIGHT_CONTROLLER Protocol Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + FIRMWARE_VERSION, RIGHT_CONTROLLER, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve RIGHT_CONTROLLER Firmware Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + HARDWARE_VERSION, RIGHT_CONTROLLER, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve RIGHT_CONTROLLER Hardware Version: %i\n", ret); + return; + } + + ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA, + HARDWARE_GENERATION, RIGHT_CONTROLLER, NULL, 0); + if (ret < 0) { + dev_err(&drvdata.hdev->dev, + "Failed to retrieve RIGHT_CONTROLLER Hardware Generation: %i\n", ret); + return; + } +} + +static int hid_go_cfg_probe(struct hid_device *hdev, + const struct hid_device_id *_id) +{ + unsigned char *buf; + int ret; + + buf = devm_kzalloc(&hdev->dev, GO_PACKET_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + hid_set_drvdata(hdev, &drvdata); + drvdata.hdev = hdev; + mutex_init(&drvdata.cfg_mutex); + + ret = sysfs_create_groups(&hdev->dev.kobj, top_level_attr_groups); + if (ret) { + dev_err_probe(&hdev->dev, ret, + "Failed to create gamepad configuration attributes\n"); + return ret; + } + + ret = devm_led_classdev_multicolor_register(&hdev->dev, &go_cdev_rgb); + if (ret) { + dev_err_probe(&hdev->dev, ret, "Failed to create RGB device\n"); + return ret; + } + + ret = devm_device_add_group(go_cdev_rgb.led_cdev.dev, &rgb_attr_group); + if (ret) { + dev_err_probe(&hdev->dev, ret, + "Failed to create RGB configuration attributes\n"); + return ret; + } + + drvdata.led_cdev = &go_cdev_rgb.led_cdev; + + init_completion(&drvdata.send_cmd_complete); + + /* Executing calls prior to returning from probe will lock the MCU. Schedule + * initial data call after probe has completed and MCU can accept calls. + */ + INIT_DELAYED_WORK(&drvdata.go_cfg_setup, &cfg_setup); + ret = schedule_delayed_work(&drvdata.go_cfg_setup, msecs_to_jiffies(2)); + if (!ret) { + dev_err(&hdev->dev, + "Failed to schedule startup delayed work\n"); + return -ENODEV; + } + return 0; +} + +static void hid_go_cfg_remove(struct hid_device *hdev) +{ + guard(mutex)(&drvdata.cfg_mutex); + sysfs_remove_groups(&hdev->dev.kobj, top_level_attr_groups); + hid_hw_close(hdev); + hid_hw_stop(hdev); + hid_set_drvdata(hdev, NULL); +} + +static int hid_go_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret, ep; + + hdev->quirks |= HID_QUIRK_INPUT_PER_APP | HID_QUIRK_MULTI_INPUT; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "Parse failed\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "Failed to start HID device\n"); + return ret; + } + + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, "Failed to open HID device\n"); + hid_hw_stop(hdev); + return ret; + } + + ep = get_endpoint_address(hdev); + if (ep != GO_GP_INTF_IN) { + dev_dbg(&hdev->dev, "Started interface %x as generic HID device\n", ep); + return 0; + } + + ret = hid_go_cfg_probe(hdev, id); + if (ret) + dev_err_probe(&hdev->dev, ret, "Failed to start configuration interface\n"); + + dev_dbg(&hdev->dev, "Started Legion Go HID Device: %x\n", ep); + + return ret; +} + +static void hid_go_remove(struct hid_device *hdev) +{ + int ep = get_endpoint_address(hdev); + + if (ep <= 0) + return; + + switch (ep) { + case GO_GP_INTF_IN: + hid_go_cfg_remove(hdev); + break; + default: + hid_hw_close(hdev); + hid_hw_stop(hdev); + break; + } +} + +static const struct hid_device_id hid_go_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, + USB_DEVICE_ID_LENOVO_LEGION_GO2_XINPUT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, + USB_DEVICE_ID_LENOVO_LEGION_GO2_DINPUT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, + USB_DEVICE_ID_LENOVO_LEGION_GO2_DUAL_DINPUT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, + USB_DEVICE_ID_LENOVO_LEGION_GO2_FPS) }, + {} +}; +MODULE_DEVICE_TABLE(hid, hid_go_devices); + +static struct hid_driver hid_lenovo_go = { + .name = "hid-lenovo-go", + .id_table = hid_go_devices, + .probe = hid_go_probe, + .remove = hid_go_remove, + .raw_event = hid_go_raw_event, +}; +module_hid_driver(hid_lenovo_go); + +MODULE_AUTHOR("Derek J. Clark"); +MODULE_DESCRIPTION("HID Driver for Lenovo Legion Go Series Gamepads."); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index d9b6c72fc254..381e4dc5aba7 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1858,7 +1858,8 @@ static int logi_dj_raw_event(struct hid_device *hdev, static int logi_dj_probe(struct hid_device *hdev, const struct hid_device_id *id) { - struct hid_report_enum *rep_enum; + struct hid_report_enum *input_report_enum; + struct hid_report_enum *output_report_enum; struct hid_report *rep; struct dj_receiver_dev *djrcv_dev; struct usb_interface *intf; @@ -1903,10 +1904,20 @@ static int logi_dj_probe(struct hid_device *hdev, } } - rep_enum = &hdev->report_enum[HID_INPUT_REPORT]; + output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; + rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; + + if (rep && (rep->maxfield < 1 || + rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1)) { + hid_err(hdev, "Expected size of DJ short report is %d, but got %d", + DJREPORT_SHORT_LENGTH - 1, rep->field[0]->report_count); + return -EINVAL; + } + + input_report_enum = &hdev->report_enum[HID_INPUT_REPORT]; /* no input reports, bail out */ - if (list_empty(&rep_enum->report_list)) + if (list_empty(&input_report_enum->report_list)) return -ENODEV; /* @@ -1914,7 +1925,7 @@ static int logi_dj_probe(struct hid_device *hdev, * Note: we should theoretically check for HID++ and DJ * collections, but this will do. */ - list_for_each_entry(rep, &rep_enum->report_list, list) { + list_for_each_entry(rep, &input_report_enum->report_list, list) { if (rep->application == 0xff000001) has_hidpp = true; } @@ -1927,7 +1938,7 @@ static int logi_dj_probe(struct hid_device *hdev, return -ENODEV; /* get the current application attached to the node */ - rep = list_first_entry(&rep_enum->report_list, struct hid_report, list); + rep = list_first_entry(&input_report_enum->report_list, struct hid_report, list); djrcv_dev = dj_get_receiver_dev(hdev, id->driver_data, rep->application, has_hidpp); if (!djrcv_dev) { @@ -1935,7 +1946,7 @@ static int logi_dj_probe(struct hid_device *hdev, return -ENOMEM; } - if (!rep_enum->numbered) + if (!input_report_enum->numbered) djrcv_dev->unnumbered_application = rep->application; /* Starts the usb device and connects to upper interfaces hiddev and diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index d1dea7297712..ccbf28869a96 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -306,21 +306,22 @@ static int __do_hidpp_send_message_sync(struct hidpp_device *hidpp, if (ret) { dbg_hid("__hidpp_send_report returned err: %d\n", ret); memset(response, 0, sizeof(struct hidpp_report)); - return ret; + goto out; } if (!wait_event_timeout(hidpp->wait, hidpp->answer_available, 5*HZ)) { dbg_hid("%s:timeout waiting for response\n", __func__); memset(response, 0, sizeof(struct hidpp_report)); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto out; } if (response->report_id == REPORT_ID_HIDPP_SHORT && response->rap.sub_id == HIDPP_ERROR) { ret = response->rap.params[1]; dbg_hid("%s:got hidpp error %02X\n", __func__, ret); - return ret; + goto out; } if ((response->report_id == REPORT_ID_HIDPP_LONG || @@ -328,10 +329,14 @@ static int __do_hidpp_send_message_sync(struct hidpp_device *hidpp, response->fap.feature_index == HIDPP20_ERROR) { ret = response->fap.params[1]; dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret); - return ret; + goto out; } - return 0; + ret = 0; + +out: + hidpp->send_receive_buf = NULL; + return ret; } /* @@ -2502,12 +2507,15 @@ static void hidpp_ff_work_handler(struct work_struct *w) } break; case HIDPP_FF_DESTROY_EFFECT: - if (wd->effect_id >= 0) - /* regular effect destroyed */ - data->effect_ids[wd->params[0]-1] = -1; - else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER) - /* autocenter spring destroyed */ - data->slot_autocenter = 0; + slot = wd->params[0]; + if (slot > 0 && slot <= data->num_effects) { + if (wd->effect_id >= 0) + /* regular effect destroyed */ + data->effect_ids[slot-1] = -1; + else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER) + /* autocenter spring destroyed */ + data->slot_autocenter = 0; + } break; case HIDPP_FF_SET_GLOBAL_GAINS: data->gain = (wd->params[0] << 8) + wd->params[1]; @@ -3665,7 +3673,7 @@ static int hidpp10_consumer_keys_raw_event(struct hidpp_device *hidpp, memcpy(&consumer_report[1], &data[3], 4); /* We are called from atomic context */ hid_report_raw_event(hidpp->hid_dev, HID_INPUT_REPORT, - consumer_report, 5, 1); + consumer_report, sizeof(consumer_report), 5, 1); return 1; } @@ -3840,8 +3848,7 @@ static int hidpp_input_configured(struct hid_device *hdev, static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, int size) { - struct hidpp_report *question = hidpp->send_receive_buf; - struct hidpp_report *answer = hidpp->send_receive_buf; + struct hidpp_report *question, *answer; struct hidpp_report *report = (struct hidpp_report *)data; int ret; int last_online; @@ -3851,6 +3858,12 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, * previously sent command. */ if (unlikely(mutex_is_locked(&hidpp->send_mutex))) { + question = hidpp->send_receive_buf; + answer = hidpp->send_receive_buf; + + if (!question) + return 0; + /* * Check for a correct hidpp20 answer or the corresponding * error @@ -4672,6 +4685,44 @@ static const struct hid_device_id hidpp_devices[] = { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb391) }, { /* MX Master 4 mouse over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb042) }, + { /* Logitech Signature K650 over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb36f) }, + { /* Logitech Signature K650 B2B over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb370) }, + { /* Logitech Pebble Keys 2 K380S over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb377) }, + { /* Logitech Casa Pop-Up Desk over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb371) }, + { /* Logitech Casa Pop-Up Desk B2B over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb374) }, + { /* Logitech Wave Keys over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb383) }, + { /* Logitech Wave Keys B2B over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb384) }, + { /* Logitech Signature Slim K950 over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb386) }, + { /* Logitech Signature Slim K950 B2B over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb388) }, + { /* Logitech MX Keys S over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb378) }, + { /* Logitech MX Keys S B2B over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb380) }, + { /* Logitech Keys-To-Go 2 over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb38c) }, + { /* Logitech Pop Icon Keys over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb38f) }, + { /* Logitech MX Keys Mini over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb369) }, + { /* Logitech MX Keys Mini B2B over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb36e) }, + { /* Logitech Signature Slim Solar+ K980 B2B over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb394) }, + { /* Logitech Bluetooth Keyboard K250/K251 over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb397) }, + { /* Logitech Signature Comfort K880 over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb39c) }, + { /* Logitech Signature Comfort K880 B2B over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb39d) }, {} }; diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 9eadf3252d0d..802a3479e24b 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -390,6 +390,10 @@ static int magicmouse_raw_event(struct hid_device *hdev, struct input_dev *input = msc->input; int x = 0, y = 0, ii, clicks = 0, npoints; + /* Protect against zero sized recursive calls from DOUBLE_REPORT_ID */ + if (size < 1) + return 0; + switch (data[0]) { case TRACKPAD_REPORT_ID: case TRACKPAD2_BT_REPORT_ID: @@ -490,6 +494,18 @@ static int magicmouse_raw_event(struct hid_device *hdev, /* Sometimes the trackpad sends two touch reports in one * packet. */ + + /* Ensure that we have at least 2 elements (report type and size) */ + if (size < 2) + return 0; + + if (size < data[1] + 2) { + hid_warn(hdev, + "received report length (%d) was smaller than specified (%d)", + size, data[1] + 2); + return 0; + } + magicmouse_raw_event(hdev, report, data + 2, data[1]); magicmouse_raw_event(hdev, report, data + 2 + data[1], size - 2 - data[1]); @@ -817,19 +833,21 @@ static int magicmouse_fetch_battery(struct hid_device *hdev) #ifdef CONFIG_HID_BATTERY_STRENGTH struct hid_report_enum *report_enum; struct hid_report *report; + struct hid_battery *bat; - if (!hdev->battery || + bat = hid_get_battery(hdev); + if (!bat || (!is_usb_magicmouse2(hdev->vendor, hdev->product) && !is_usb_magictrackpad2(hdev->vendor, hdev->product))) return -1; - report_enum = &hdev->report_enum[hdev->battery_report_type]; - report = report_enum->report_id_hash[hdev->battery_report_id]; + report_enum = &hdev->report_enum[bat->report_type]; + report = report_enum->report_id_hash[bat->report_id]; if (!report || report->maxfield < 1) return -1; - if (hdev->battery_capacity == hdev->battery_max) + if (bat->capacity == bat->max) return -1; hid_hw_request(hdev, report, HID_REQ_GET_REPORT); diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c index ef3b5c77c38e..e4ddd8e9293b 100644 --- a/drivers/hid/hid-mcp2221.c +++ b/drivers/hid/hid-mcp2221.c @@ -19,8 +19,15 @@ #include #include #include +#include #include "hid-ids.h" +static bool gpio_mode_enforce; + +module_param(gpio_mode_enforce, bool, 0644); +MODULE_PARM_DESC(gpio_mode_enforce, + "Enforce GPIO mode for GP0 thru GP3 (default: false, will be used for IIO)"); + /* Commands codes in a raw output report */ enum { MCP2221_I2C_WR_DATA = 0x90, @@ -121,6 +128,7 @@ struct mcp2221 { u8 *rxbuf; u8 txbuf[64]; int rxbuf_idx; + int rxbuf_size; int status; u8 cur_i2c_clk_div; struct gpio_chip *gc; @@ -323,12 +331,14 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp, mcp->txbuf[3] = (u8)(msg->addr << 1); total_len = msg->len; mcp->rxbuf = msg->buf; + mcp->rxbuf_size = msg->len; } else { mcp->txbuf[1] = smbus_len; mcp->txbuf[2] = 0; mcp->txbuf[3] = (u8)(smbus_addr << 1); total_len = smbus_len; mcp->rxbuf = smbus_buf; + mcp->rxbuf_size = smbus_len; } ret = mcp_send_data_req_status(mcp, mcp->txbuf, 4); @@ -536,10 +546,10 @@ static int mcp_smbus_xfer(struct i2c_adapter *adapter, u16 addr, if (ret) goto exit; - mcp->rxbuf_idx = 0; - mcp->rxbuf = data->block; - mcp->txbuf[0] = MCP2221_I2C_GET_DATA; - ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1); + ret = mcp_i2c_smbus_read(mcp, NULL, + MCP2221_I2C_RD_RPT_START, + addr, data->block[0] + 1, + data->block); if (ret) goto exit; } else { @@ -555,14 +565,14 @@ static int mcp_smbus_xfer(struct i2c_adapter *adapter, u16 addr, case I2C_SMBUS_I2C_BLOCK_DATA: if (read_write == I2C_SMBUS_READ) { ret = mcp_smbus_write(mcp, addr, command, NULL, - 0, MCP2221_I2C_WR_NO_STOP, 1); + 0, MCP2221_I2C_WR_NO_STOP, 0); if (ret) goto exit; - mcp->rxbuf_idx = 0; - mcp->rxbuf = data->block; - mcp->txbuf[0] = MCP2221_I2C_GET_DATA; - ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1); + ret = mcp_i2c_smbus_read(mcp, NULL, + MCP2221_I2C_RD_RPT_START, + addr, data->block[0], + &data->block[1]); if (ret) goto exit; } else { @@ -650,7 +660,7 @@ static int mcp2221_check_gpio_pinfunc(struct mcp2221 *mcp) int needgpiofix = 0; int ret; - if (IS_ENABLED(CONFIG_IIO)) + if (IS_ENABLED(CONFIG_IIO) && !gpio_mode_enforce) return 0; ret = mcp_gpio_read_sram(mcp); @@ -912,6 +922,10 @@ static int mcp2221_raw_event(struct hid_device *hdev, mcp->status = -EINVAL; break; } + if (mcp->rxbuf_idx + data[3] > mcp->rxbuf_size) { + mcp->status = -EINVAL; + break; + } buf = mcp->rxbuf; memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]); mcp->rxbuf_idx = mcp->rxbuf_idx + data[3]; @@ -1045,7 +1059,8 @@ static void mcp2221_remove(struct hid_device *hdev) #if IS_REACHABLE(CONFIG_IIO) struct mcp2221 *mcp = hid_get_drvdata(hdev); - cancel_delayed_work_sync(&mcp->init_work); + if (!gpio_mode_enforce) + cancel_delayed_work_sync(&mcp->init_work); #endif } @@ -1319,8 +1334,10 @@ static int mcp2221_probe(struct hid_device *hdev, #endif #if IS_REACHABLE(CONFIG_IIO) - INIT_DELAYED_WORK(&mcp->init_work, mcp_init_work); - schedule_delayed_work(&mcp->init_work, msecs_to_jiffies(100)); + if (!gpio_mode_enforce) { + INIT_DELAYED_WORK(&mcp->init_work, mcp_init_work); + schedule_delayed_work(&mcp->init_work, msecs_to_jiffies(100)); + } #endif return 0; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index e82a3c4e5b44..eeab0b6e32cc 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -533,7 +533,7 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) } ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf, - size, 0); + size, size, 0); if (ret) dev_warn(&hdev->dev, "failed to report feature\n"); } diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index c6c2961dd574..8bba29ef6c7a 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -24,10 +24,6 @@ */ -/* #define DEBUG */ - -#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg) - #include #include #include @@ -53,14 +49,14 @@ static int hid_plff_play(struct input_dev *dev, void *data, left = effect->u.rumble.strong_magnitude; right = effect->u.rumble.weak_magnitude; - debug("called with 0x%04x 0x%04x", left, right); + hid_dbg(dev, "called with 0x%04x 0x%04x", left, right); left = left * plff->maxval / 0xffff; right = right * plff->maxval / 0xffff; *plff->strong = left; *plff->weak = right; - debug("running with 0x%02x 0x%02x", left, right); + hid_dbg(dev, "running with 0x%02x 0x%02x", left, right); hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT); return 0; @@ -119,7 +115,7 @@ static int plff_init(struct hid_device *hid) report->field[0]->value[1] = 0x00; strong = &report->field[0]->value[2]; weak = &report->field[0]->value[3]; - debug("detected single-field device"); + hid_dbg(hid, "detected single-field device"); } else if (report->field[0]->maxusage == 1 && report->field[0]->usage[0].hid == (HID_UP_LED | 0x43) && @@ -134,7 +130,7 @@ static int plff_init(struct hid_device *hid) weak = &report->field[3]->value[0]; if (hid->vendor == USB_VENDOR_ID_JESS2) maxval = 0xff; - debug("detected 4-field device"); + hid_dbg(hid, "detected 4-field device"); } else { hid_err(hid, "not enough fields or values\n"); return -ENODEV; diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 3c0db8f93c82..e48537331675 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -2377,8 +2377,15 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * struct dualshock4_input_report_usb *usb = (struct dualshock4_input_report_usb *)data; + if (usb->num_touch_reports > ARRAY_SIZE(usb->touch_reports)) { + hid_err(hdev, "DualShock4 USB input report has invalid num_touch_reports=%d\n", + usb->num_touch_reports); + return -EINVAL; + } + ds4_report = &usb->common; - num_touch_reports = usb->num_touch_reports; + num_touch_reports = min_t(u8, usb->num_touch_reports, + ARRAY_SIZE(usb->touch_reports)); touch_reports = usb->touch_reports; } else if (hdev->bus == BUS_BLUETOOTH && report->id == DS4_INPUT_REPORT_BT && size == DS4_INPUT_REPORT_BT_SIZE) { @@ -2391,8 +2398,15 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * return -EILSEQ; } + if (bt->num_touch_reports > ARRAY_SIZE(bt->touch_reports)) { + hid_err(hdev, "DualShock4 BT input report has invalid num_touch_reports=%d\n", + bt->num_touch_reports); + return -EINVAL; + } + ds4_report = &bt->common; - num_touch_reports = bt->num_touch_reports; + num_touch_reports = min_t(u8, bt->num_touch_reports, + ARRAY_SIZE(bt->touch_reports)); touch_reports = bt->touch_reports; } else if (hdev->bus == BUS_BLUETOOTH && report->id == DS4_INPUT_REPORT_BT_MINIMAL && diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c index e44d79dff8de..8db054280afb 100644 --- a/drivers/hid/hid-primax.c +++ b/drivers/hid/hid-primax.c @@ -44,7 +44,7 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report, data[0] |= (1 << (data[idx] - 0xE0)); data[idx] = 0; } - hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, 0); + hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, size, 0); return 1; default: /* unknown report */ diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 02f7db5c1056..512049963978 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -134,6 +134,7 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6019), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_602E), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6093), HID_QUIRK_ALWAYS_POLL }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_BOLT_RECEIVER), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C007), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KEYBOARD_G710_PLUS), HID_QUIRK_NOGET }, @@ -234,7 +235,7 @@ static const struct hid_device_id hid_quirks[] = { * used as a driver. See hid_scan_report(). */ static const struct hid_device_id hid_have_special_driver[] = { -#if IS_ENABLED(CONFIG_APPLEDISPLAY) +#if IS_ENABLED(CONFIG_USB_APPLEDISPLAY) { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9218) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9219) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x921c) }, @@ -691,22 +692,54 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) }, #endif #if IS_ENABLED(CONFIG_HID_SONY) + { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG_DONGLE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG_DONGLE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_DRUMS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_GUITAR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_DRUMS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_GUITAR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_KEYBOARD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_DRUMS_MODE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_KEYBOARD_MODE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_MUSTANG_MODE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_SQUIER_MODE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MUSTANG_GUITAR) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_PS4_STRATOCASTER) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_JAGUAR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_RIFFMASTER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS5_RIFFMASTER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_GUITAR_DONGLE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_PS4_GHLIVE_DONGLE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_DJH_TURNTABLE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_DRUMS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_GUITAR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_KEYBOARD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_DRUMS_MODE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_KEYBOARD_MODE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_MUSTANG_MODE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_SQUIER_MODE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MUSTANG_GUITAR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_DRUMS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_GUITAR) }, #endif #if IS_ENABLED(CONFIG_HID_SPEEDLINK) { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) }, diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c index bab93d71b760..963c45113204 100644 --- a/drivers/hid/hid-sjoy.c +++ b/drivers/hid/hid-sjoy.c @@ -91,17 +91,17 @@ static int sjoyff_init(struct hid_device *hid) set_bit(FF_RUMBLE, dev->ffbit); - error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play); - if (error) { - kfree(sjoyff); - return error; - } - sjoyff->report = report; sjoyff->report->field[0]->value[0] = 0x01; sjoyff->report->field[0]->value[1] = 0x00; sjoyff->report->field[0]->value[2] = 0x00; hid_hw_request(hid, sjoyff->report, HID_REQ_SET_REPORT); + + error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play); + if (error) { + kfree(sjoyff); + return error; + } } hid_info(hid, "Force feedback for SmartJoy PLUS PS2/USB adapter\n"); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index a89af14e4acc..315343415e8f 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HID driver for Sony / PS2 / PS3 / PS4 BD devices. + * HID driver for Sony / PS2 / PS3 BD / PS4 / PS5 devices. * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik @@ -12,9 +12,10 @@ * Copyright (c) 2014-2016 Frank Praznik * Copyright (c) 2018 Todd Kelner * Copyright (c) 2020-2021 Pascal Giard - * Copyright (c) 2020 Sanjay Govind + * Copyright (c) 2020-2026 Sanjay Govind * Copyright (c) 2021 Daniel Nguyen * Copyright (c) 2026 Rosalie Wanders + * Copyright (c) 2026 Brenton Simpson */ /* @@ -59,12 +60,15 @@ #define NSG_MR5U_REMOTE_BT BIT(11) #define NSG_MR7U_REMOTE_BT BIT(12) #define SHANWAN_GAMEPAD BIT(13) -#define GH_GUITAR_CONTROLLER BIT(14) -#define GHL_GUITAR_PS3WIIU BIT(15) -#define GHL_GUITAR_PS4 BIT(16) -#define RB4_GUITAR_PS4_USB BIT(17) -#define RB4_GUITAR_PS4_BT BIT(18) -#define RB4_GUITAR_PS5 BIT(19) +#define INSTRUMENT BIT(14) +#define GH_GUITAR_TILT BIT(15) +#define GHL_GUITAR_PS3WIIU BIT(16) +#define GHL_GUITAR_PS4 BIT(17) +#define RB4_GUITAR_PS4_USB BIT(18) +#define RB4_GUITAR_PS4_BT BIT(19) +#define RB4_GUITAR_PS5 BIT(20) +#define RB3_PRO_INSTRUMENT BIT(21) +#define DJH_TURNTABLE BIT(22) #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT) #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT) @@ -72,7 +76,8 @@ NAVIGATION_CONTROLLER_BT) #define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\ MOTION_CONTROLLER | NAVIGATION_CONTROLLER) -#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER) +#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER |\ + RB4_GUITAR_PS5) #define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | MOTION_CONTROLLER) #define SONY_BT_DEVICE (SIXAXIS_CONTROLLER_BT | MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER_BT) #define NSG_MRXU_REMOTE (NSG_MR5U_REMOTE_BT | NSG_MR7U_REMOTE_BT) @@ -87,6 +92,10 @@ #define GHL_GUITAR_POKE_INTERVAL 8 /* In seconds */ #define GUITAR_TILT_USAGE 44 +#define TURNTABLE_EFFECTS_KNOB_USAGE 44 +#define TURNTABLE_PLATTER_BUTTONS_USAGE 45 +#define TURNTABLE_CROSS_FADER_USAGE 46 + /* Magic data taken from GHLtarUtility: * https://github.com/ghlre/GHLtarUtility/blob/master/PS3Guitar.cs * Note: The Wii U and PS3 dongles happen to share the same! @@ -427,20 +436,25 @@ static const unsigned int rb4_absmap[] = { [0x31] = ABS_Y, }; -static const unsigned int rb4_keymap[] = { - [0x1] = BTN_WEST, /* Square */ - [0x2] = BTN_SOUTH, /* Cross */ - [0x3] = BTN_EAST, /* Circle */ - [0x4] = BTN_NORTH, /* Triangle */ - [0x5] = BTN_TL, /* L1 */ - [0x6] = BTN_TR, /* R1 */ - [0x7] = BTN_TL2, /* L2 */ - [0x8] = BTN_TR2, /* R2 */ - [0x9] = BTN_SELECT, /* Share */ - [0xa] = BTN_START, /* Options */ - [0xb] = BTN_THUMBL, /* L3 */ - [0xc] = BTN_THUMBR, /* R3 */ - [0xd] = BTN_MODE, /* PS */ +static const unsigned int ps3_turntable_absmap[] = { + [0x32] = ABS_X, + [0x35] = ABS_Y, +}; + +static const unsigned int instrument_keymap[] = { + [0x1] = BTN_WEST, + [0x2] = BTN_SOUTH, + [0x3] = BTN_EAST, + [0x4] = BTN_NORTH, + [0x5] = BTN_TL, + [0x6] = BTN_TR, + [0x7] = BTN_TL2, + [0x8] = BTN_TR2, + [0x9] = BTN_SELECT, + [0xa] = BTN_START, + [0xb] = BTN_THUMBL, + [0xc] = BTN_THUMBR, + [0xd] = BTN_MODE, }; static enum power_supply_property sony_battery_props[] = { @@ -457,6 +471,7 @@ struct sixaxis_led { u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */ u8 duty_on; /* % of duty_length the led is on (0xff mean 100%) */ } __packed; +static_assert(sizeof(struct sixaxis_led) == 5); struct sixaxis_rumble { u8 padding; @@ -465,6 +480,7 @@ struct sixaxis_rumble { u8 left_duration; /* Left motor duration (0xff means forever) */ u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */ } __packed; +static_assert(sizeof(struct sixaxis_rumble) == 5); struct sixaxis_output_report { u8 report_id; @@ -474,11 +490,13 @@ struct sixaxis_output_report { struct sixaxis_led led[4]; /* LEDx at (4 - x) */ struct sixaxis_led _reserved; /* LED5, not actually soldered */ } __packed; +static_assert(sizeof(struct sixaxis_output_report) == 36); union sixaxis_output_report_01 { struct sixaxis_output_report data; u8 buf[36]; }; +static_assert(sizeof(union sixaxis_output_report_01) == 36); struct motion_output_report_02 { u8 type, zero; @@ -486,10 +504,12 @@ struct motion_output_report_02 { u8 zero2; u8 rumble; }; +static_assert(sizeof(struct motion_output_report_02) == 7); #define SIXAXIS_REPORT_0xF2_SIZE 17 #define SIXAXIS_REPORT_0xF5_SIZE 8 #define MOTION_REPORT_0x02_SIZE 49 +#define PRO_INSTRUMENT_0x00_SIZE 8 #define SENSOR_SUFFIX " Motion Sensors" #define TOUCHPAD_SUFFIX " Touchpad" @@ -515,7 +535,7 @@ struct sony_sc { struct led_classdev *leds[MAX_LEDS]; unsigned long quirks; struct work_struct state_worker; - void (*send_output_report)(struct sony_sc *); + void (*send_output_report)(struct sony_sc *sc); struct power_supply *battery; struct power_supply_desc battery_desc; int device_id; @@ -539,6 +559,9 @@ struct sony_sc { /* GH Live */ struct urb *ghl_urb; struct timer_list ghl_poke_timer; + + /* Rock Band 3 Pro Instruments */ + unsigned long rb3_pro_poke_jiffies; }; static void sony_set_leds(struct sony_sc *sc); @@ -589,11 +612,11 @@ static int ghl_init_urb(struct sony_sc *sc, struct usb_device *usbdev, pipe = usb_sndctrlpipe(usbdev, 0); cr = devm_kzalloc(&sc->hdev->dev, sizeof(*cr), GFP_ATOMIC); - if (cr == NULL) + if (!cr) return -ENOMEM; databuf = devm_kzalloc(&sc->hdev->dev, poke_size, GFP_ATOMIC); - if (databuf == NULL) + if (!databuf) return -ENOMEM; cr->bRequestType = @@ -610,6 +633,88 @@ static int ghl_init_urb(struct sony_sc *sc, struct usb_device *usbdev, return 0; } + + +/* + * Sending HID_REQ_SET_REPORT enables the full report. Without this + * Rock Band 3 Pro instruments only report navigation events + */ +static int rb3_pro_instrument_enable_full_report(struct sony_sc *sc) +{ + struct hid_device *hdev = sc->hdev; + static const u8 report[] = { 0x00, 0xE9, 0x00, 0x89, 0x1B, + 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x89, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE9, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 }; + u8 *buf; + int ret; + + buf = kmemdup(report, sizeof(report), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(report), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + + kfree(buf); + + return ret; +} + +static int djh_turntable_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR) { + unsigned int abs = usage->hid & HID_USAGE; + + if (abs == TURNTABLE_CROSS_FADER_USAGE) { + hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RX); + return 1; + } else if (abs == TURNTABLE_EFFECTS_KNOB_USAGE) { + hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RY); + return 1; + } else if (abs == TURNTABLE_PLATTER_BUTTONS_USAGE) { + hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RZ); + return 1; + } + } else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) { + unsigned int abs = usage->hid & HID_USAGE; + + if (abs >= ARRAY_SIZE(ps3_turntable_absmap)) + return -1; + + abs = ps3_turntable_absmap[abs]; + + hid_map_usage_clear(hi, usage, bit, max, EV_ABS, abs); + return 1; + } + return 0; +} + +static int instrument_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) { + unsigned int key = usage->hid & HID_USAGE; + + if (key >= ARRAY_SIZE(instrument_keymap)) + return 0; + + key = instrument_keymap[key]; + hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key); + return 1; + } + + return 0; +} + static int gh_guitar_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) @@ -629,16 +734,7 @@ static int rb4_guitar_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { - if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) { - unsigned int key = usage->hid & HID_USAGE; - - if (key >= ARRAY_SIZE(rb4_keymap)) - return 0; - - key = rb4_keymap[key]; - hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key); - return 1; - } else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) { + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) { unsigned int abs = usage->hid & HID_USAGE; /* Let the HID parser deal with the HAT. */ @@ -855,6 +951,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size) static const u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 }; unsigned long flags; int offset; + u8 index; u8 battery_capacity; int battery_status; @@ -870,7 +967,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size) battery_capacity = 100; battery_status = (rd[offset] & 0x01) ? POWER_SUPPLY_STATUS_FULL : POWER_SUPPLY_STATUS_CHARGING; } else { - u8 index = rd[offset] <= 5 ? rd[offset] : 5; + index = rd[offset] <= 5 ? rd[offset] : 5; battery_capacity = sixaxis_battery_capacity[index]; battery_status = POWER_SUPPLY_STATUS_DISCHARGING; } @@ -908,7 +1005,7 @@ static void nsg_mrxu_parse_report(struct sony_sc *sc, u8 *rd, int size) * the touch-related data starts at offset 2. * For the first byte, bit 0 is set when touchpad button is pressed. * Bit 2 is set when a touch is active and the drag (Fn) key is pressed. - * This drag key is mapped to BTN_LEFT. It is operational only when a + * This drag key is mapped to BTN_LEFT. It is operational only when a * touch point is active. * Bit 4 is set when only the first touch point is active. * Bit 6 is set when only the second touch point is active. @@ -991,6 +1088,12 @@ static void rb4_ps4_guitar_parse_report(struct sony_sc *sc, u8 *rd, int size) static void rb4_ps5_guitar_parse_report(struct sony_sc *sc, u8 *rd, int size) { + u8 charging_status; + u8 battery_data; + u8 battery_capacity; + u8 battery_status; + unsigned long flags; + /* * Rock Band 4 PS5 guitars have whammy and * tilt functionality, they're located at @@ -1003,6 +1106,37 @@ static void rb4_ps5_guitar_parse_report(struct sony_sc *sc, u8 *rd, int size) input_report_abs(sc->input_dev, ABS_Z, rd[41]); input_report_abs(sc->input_dev, ABS_RZ, rd[42]); + /* + * Rock Band 4 PS5 guitars also report the + * battery status and level at byte 30. + */ + charging_status = (rd[30] >> 4) & 0x0F; + battery_data = rd[30] & 0x0F; + + switch (charging_status) { + case 0x0: + battery_capacity = min(battery_data * 10 + 5, 100); + battery_status = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case 0x1: + battery_capacity = min(battery_data * 10 + 5, 100); + battery_status = POWER_SUPPLY_STATUS_CHARGING; + break; + case 0x2: + battery_capacity = 100; + battery_status = POWER_SUPPLY_STATUS_FULL; + break; + default: + battery_capacity = 0; + battery_status = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + + spin_lock_irqsave(&sc->lock, flags); + sc->battery_capacity = battery_capacity; + sc->battery_status = battery_status; + spin_unlock_irqrestore(&sc->lock, flags); + input_sync(sc->input_dev); } @@ -1035,10 +1169,9 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, sixaxis_parse_report(sc, rd, size); } else if ((sc->quirks & MOTION_CONTROLLER_BT) && rd[0] == 0x01 && size == 49) { sixaxis_parse_report(sc, rd, size); - } else if ((sc->quirks & NAVIGATION_CONTROLLER) && rd[0] == 0x01 && - size == 49) { + } else if ((sc->quirks & NAVIGATION_CONTROLLER) && rd[0] == 0x01 && size == 49) { sixaxis_parse_report(sc, rd, size); - } else if ((sc->quirks & NSG_MRXU_REMOTE) && rd[0] == 0x02) { + } else if ((sc->quirks & NSG_MRXU_REMOTE) && rd[0] == 0x02 && size >= 12) { nsg_mrxu_parse_report(sc, rd, size); return 1; } else if ((sc->quirks & RB4_GUITAR_PS4_USB) && rd[0] == 0x01 && size == 64) { @@ -1052,6 +1185,17 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, return 1; } + /* Rock Band 3 PS3 Pro instruments set rd[24] to 0xE0 when they're + * sending full reports, and 0x02 when only sending navigation. + */ + if ((sc->quirks & RB3_PRO_INSTRUMENT) && size >= 25 && rd[24] == 0x02) { + /* Only attempt to enable full report every 8 seconds */ + if (time_after(jiffies, sc->rb3_pro_poke_jiffies)) { + sc->rb3_pro_poke_jiffies = jiffies + secs_to_jiffies(8); + rb3_pro_instrument_enable_full_report(sc); + } + } + if (sc->defer_initialization) { sc->defer_initialization = 0; sony_schedule_work(sc, SONY_WORKER_STATE); @@ -1065,6 +1209,7 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, unsigned long **bit, int *max) { struct sony_sc *sc = hid_get_drvdata(hdev); + int ret; if (sc->quirks & BUZZ_CONTROLLER) { unsigned int key = usage->hid & HID_USAGE; @@ -1098,9 +1243,19 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, if (sc->quirks & SIXAXIS_CONTROLLER) return sixaxis_mapping(hdev, hi, field, usage, bit, max); - if (sc->quirks & GH_GUITAR_CONTROLLER) + /* INSTRUMENT quirk is used as a base mapping for instruments */ + if (sc->quirks & INSTRUMENT) { + ret = instrument_mapping(hdev, hi, field, usage, bit, max); + if (ret != 0) + return ret; + } + + if (sc->quirks & GH_GUITAR_TILT) return gh_guitar_mapping(hdev, hi, field, usage, bit, max); + if (sc->quirks & DJH_TURNTABLE) + return djh_turntable_mapping(hdev, hi, field, usage, bit, max); + if (sc->quirks & (RB4_GUITAR_PS4_USB | RB4_GUITAR_PS4_BT)) return rb4_guitar_mapping(hdev, hi, field, usage, bit, max); @@ -1153,19 +1308,18 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count, input_set_abs_params(sc->touchpad, ABS_MT_POSITION_Y, 0, h, 0, 0); if (touch_major > 0) { - input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MAJOR, + input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MAJOR, 0, touch_major, 0, 0); if (touch_minor > 0) - input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MINOR, + input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MINOR, 0, touch_minor, 0, 0); if (orientation > 0) - input_set_abs_params(sc->touchpad, ABS_MT_ORIENTATION, + input_set_abs_params(sc->touchpad, ABS_MT_ORIENTATION, 0, orientation, 0, 0); } - if (sc->quirks & NSG_MRXU_REMOTE) { + if (sc->quirks & NSG_MRXU_REMOTE) __set_bit(EV_REL, sc->touchpad->evbit); - } ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER); if (ret < 0) @@ -1320,7 +1474,7 @@ static void sixaxis_set_leds_from_id(struct sony_sc *sc) int id = sc->device_id; - BUILD_BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0])); + BUILD_BUG_ON(ARRAY_SIZE(sixaxis_leds[0]) > MAX_LEDS); if (id < 0) return; @@ -1338,7 +1492,7 @@ static void buzz_set_leds(struct sony_sc *sc) struct hid_report, list); s32 *value = report->field[0]->value; - BUILD_BUG_ON(MAX_LEDS < 4); + BUILD_BUG_ON(4 > MAX_LEDS); value[0] = 0x00; value[1] = sc->led_state[0] ? 0xff : 0x00; @@ -1485,9 +1639,6 @@ static int sony_leds_init(struct sony_sc *sc) u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 }; u8 use_hw_blink[MAX_LEDS] = { 0 }; - if (WARN_ON(!(sc->quirks & SONY_LED_SUPPORT))) - return -EINVAL; - if (sc->quirks & BUZZ_CONTROLLER) { sc->led_count = 4; use_color_names = 0; @@ -1535,15 +1686,12 @@ static int sony_leds_init(struct sony_sc *sc) name_sz = strlen(dev_name(&hdev->dev)) + strlen(color_name_str[n]) + 2; led = devm_kzalloc(&hdev->dev, sizeof(struct led_classdev) + name_sz, GFP_KERNEL); - if (!led) { - hid_err(hdev, "Couldn't allocate memory for LED %d\n", n); + if (!led) return -ENOMEM; - } name = (void *)(&led[1]); if (use_color_names) - snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), - color_name_str[n]); + snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), color_name_str[n]); else snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1); led->name = name; @@ -2060,6 +2208,19 @@ static int sony_input_configured(struct hid_device *hdev, } sony_init_output_report(sc, sixaxis_send_output_report); + } else if (sc->quirks & RB3_PRO_INSTRUMENT) { + /* + * Rock Band 3 PS3 Pro Instruments also do not handle HID Output + * Reports on the interrupt EP like they should, so we need to force + * HID output reports to use HID_REQ_SET_REPORT on the Control EP. + * + * There is also another issue about HID Output Reports via USB, + * these instruments do not want the report_id as part of the data + * packet, so we have to discard buf[0] when sending the actual + * control message, even for numbered reports. + */ + hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; + hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID; } else if (sc->quirks & SIXAXIS_CONTROLLER_USB) { /* * The Sony Sixaxis does not handle HID Output Reports on the @@ -2176,10 +2337,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) quirks |= SHANWAN_GAMEPAD; sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL); - if (sc == NULL) { - hid_err(hdev, "can't alloc sony descriptor\n"); + if (!sc) return -ENOMEM; - } spin_lock_init(&sc->lock); @@ -2227,6 +2386,9 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err; } + if (sc->quirks & RB3_PRO_INSTRUMENT) + sc->rb3_pro_poke_jiffies = 0; + if (sc->quirks & (GHL_GUITAR_PS3WIIU | GHL_GUITAR_PS4)) { if (!hid_is_usb(hdev)) { ret = -EINVAL; @@ -2290,11 +2452,10 @@ static void sony_remove(struct hid_device *hdev) static int sony_suspend(struct hid_device *hdev, pm_message_t message) { #ifdef CONFIG_SONY_FF + struct sony_sc *sc = hid_get_drvdata(hdev); /* On suspend stop any running force-feedback events */ - if (SONY_FF_SUPPORT) { - struct sony_sc *sc = hid_get_drvdata(hdev); - + if (sc->quirks & SONY_FF_SUPPORT) { sc->left = sc->right = 0; sony_send_output_report(sc); } @@ -2364,35 +2525,82 @@ static const struct hid_device_id sony_devices[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE), .driver_data = NSG_MR7U_REMOTE_BT }, /* Guitar Hero Live PS3 and Wii U guitar dongles */ - { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE), - .driver_data = GHL_GUITAR_PS3WIIU | GH_GUITAR_CONTROLLER }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE), + .driver_data = GHL_GUITAR_PS3WIIU | GH_GUITAR_TILT | INSTRUMENT }, /* Guitar Hero PC Guitar Dongle */ { HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_GUITAR_DONGLE), - .driver_data = GH_GUITAR_CONTROLLER }, - /* Guitar Hero PS3 World Tour Guitar Dongle */ - { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GUITAR_DONGLE), - .driver_data = GH_GUITAR_CONTROLLER }, + .driver_data = GH_GUITAR_TILT | INSTRUMENT }, + /* Guitar Hero PS3 Guitar Dongle */ + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_GUITAR), + .driver_data = GH_GUITAR_TILT | INSTRUMENT }, + /* Guitar Hero PS3 Drum Dongle */ + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_DRUMS), + .driver_data = INSTRUMENT }, + /* DJ Hero PS3 Guitar Dongle */ + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_DJH_TURNTABLE), + .driver_data = DJH_TURNTABLE | INSTRUMENT }, /* Guitar Hero Live PS4 guitar dongles */ { HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_PS4_GHLIVE_DONGLE), - .driver_data = GHL_GUITAR_PS4 | GH_GUITAR_CONTROLLER }, + .driver_data = GHL_GUITAR_PS4 | GH_GUITAR_TILT | INSTRUMENT }, + /* Rock Band 1 Wii instruments */ + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_GUITAR), + .driver_data = INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_DRUMS), + .driver_data = INSTRUMENT }, + /* Rock Band 2 Wii instruments */ + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_GUITAR), + .driver_data = INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_DRUMS), + .driver_data = INSTRUMENT }, + /* Rock Band 3 Wii instruments */ + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_DRUMS_MODE), + .driver_data = INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MUSTANG_GUITAR), + .driver_data = INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_MUSTANG_MODE), + .driver_data = INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_SQUIER_MODE), + .driver_data = INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_KEYBOARD), + .driver_data = INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_KEYBOARD_MODE), + .driver_data = INSTRUMENT }, + /* Rock Band 3 PS3 instruments */ + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_GUITAR), + .driver_data = INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_DRUMS), + .driver_data = INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_DRUMS_MODE), + .driver_data = INSTRUMENT }, + /* Rock Band 3 PS3 Pro instruments */ + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MUSTANG_GUITAR), + .driver_data = INSTRUMENT | RB3_PRO_INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_MUSTANG_MODE), + .driver_data = INSTRUMENT | RB3_PRO_INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_SQUIER_MODE), + .driver_data = INSTRUMENT | RB3_PRO_INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_KEYBOARD), + .driver_data = INSTRUMENT | RB3_PRO_INSTRUMENT }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_KEYBOARD_MODE), + .driver_data = INSTRUMENT | RB3_PRO_INSTRUMENT }, /* Rock Band 4 PS4 guitars */ { HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_RIFFMASTER), - .driver_data = RB4_GUITAR_PS4_USB }, + .driver_data = RB4_GUITAR_PS4_USB | INSTRUMENT }, { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG), - .driver_data = RB4_GUITAR_PS4_USB }, + .driver_data = RB4_GUITAR_PS4_USB | INSTRUMENT }, { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG_DONGLE), - .driver_data = RB4_GUITAR_PS4_USB }, + .driver_data = RB4_GUITAR_PS4_USB | INSTRUMENT }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_JAGUAR), - .driver_data = RB4_GUITAR_PS4_BT }, + .driver_data = RB4_GUITAR_PS4_BT | INSTRUMENT }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_PS4_STRATOCASTER), - .driver_data = RB4_GUITAR_PS4_BT }, + .driver_data = RB4_GUITAR_PS4_BT | INSTRUMENT }, /* Rock Band 4 PS5 guitars */ { HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS5_RIFFMASTER), - .driver_data = RB4_GUITAR_PS5 }, + .driver_data = RB4_GUITAR_PS5 | INSTRUMENT }, { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG), - .driver_data = RB4_GUITAR_PS5 }, + .driver_data = RB4_GUITAR_PS5 | INSTRUMENT }, { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG_DONGLE), - .driver_data = RB4_GUITAR_PS5 }, + .driver_data = RB4_GUITAR_PS5 | INSTRUMENT }, { } }; MODULE_DEVICE_TABLE(hid, sony_devices); @@ -2428,5 +2636,5 @@ static void __exit sony_exit(void) module_init(sony_init); module_exit(sony_exit); -MODULE_DESCRIPTION("HID driver for Sony / PS2 / PS3 / PS4 BD devices"); +MODULE_DESCRIPTION("HID driver for Sony / PS2 / PS3 BD / PS4 / PS5 devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index bd7f93e96e4e..b73f09d26688 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -184,7 +184,9 @@ static int uclogic_input_configured(struct hid_device *hdev, suffix = "System Control"; break; } - } else { + } + + if (suffix) { hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s %s", hdev->name, suffix); if (!hi->input->name) diff --git a/drivers/hid/hid-vivaldi-common.c b/drivers/hid/hid-vivaldi-common.c index bf734055d4b6..b12bb5cc091a 100644 --- a/drivers/hid/hid-vivaldi-common.c +++ b/drivers/hid/hid-vivaldi-common.c @@ -85,7 +85,7 @@ void vivaldi_feature_mapping(struct hid_device *hdev, } ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data, - report_len, 0); + report_len, report_len, 0); if (ret) { dev_warn(&hdev->dev, "failed to report feature %d\n", field->report->id); diff --git a/drivers/hid/hid-winwing.c b/drivers/hid/hid-winwing.c index ab65dc12d1e0..9cd25a77999e 100644 --- a/drivers/hid/hid-winwing.c +++ b/drivers/hid/hid-winwing.c @@ -12,6 +12,7 @@ #include #include #include +#include #define MAX_REPORT 16 @@ -35,10 +36,14 @@ static const struct winwing_led_info led_info[3] = { struct winwing_drv_data { struct hid_device *hdev; - __u8 *report_buf; - struct mutex lock; - int map_more_buttons; - unsigned int num_leds; + struct mutex lights_lock; + __u8 *report_lights; + __u8 *report_rumble; + struct work_struct rumble_work; + struct ff_rumble_effect rumble; + int rumble_left; + int rumble_right; + int has_grip15; struct winwing_led leds[]; }; @@ -47,11 +52,15 @@ static int winwing_led_write(struct led_classdev *cdev, { struct winwing_led *led = (struct winwing_led *) cdev; struct winwing_drv_data *data = hid_get_drvdata(led->hdev); - __u8 *buf = data->report_buf; + __u8 *buf = data->report_lights; int ret; - mutex_lock(&data->lock); + mutex_lock(&data->lights_lock); + /* + * Mimicking requests captured by usbmon when LEDs + * are controlled by the vendor's app in a VM. + */ buf[0] = 0x02; buf[1] = 0x60; buf[2] = 0xbe; @@ -69,7 +78,7 @@ static int winwing_led_write(struct led_classdev *cdev, ret = hid_hw_output_report(led->hdev, buf, 14); - mutex_unlock(&data->lock); + mutex_unlock(&data->lights_lock); return ret; } @@ -87,9 +96,9 @@ static int winwing_init_led(struct hid_device *hdev, if (!data) return -EINVAL; - data->report_buf = devm_kmalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL); + data->report_lights = devm_kzalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL); - if (!data->report_buf) + if (!data->report_lights) return -ENOMEM; for (i = 0; i < 3; i += 1) { @@ -117,7 +126,7 @@ static int winwing_init_led(struct hid_device *hdev, return ret; } -static int winwing_map_button(int button, int map_more_buttons) +static int winwing_map_button(int button, int has_grip15) { if (button < 1) return KEY_RESERVED; @@ -141,7 +150,7 @@ static int winwing_map_button(int button, int map_more_buttons) return (button - 65) + BTN_TRIGGER_HAPPY17; } - if (!map_more_buttons) { + if (!has_grip15) { /* * Not mapping numbers [33 .. 64] which * are not assigned to any real buttons @@ -194,13 +203,149 @@ static int winwing_input_mapping(struct hid_device *hdev, /* Button numbers start with 1 */ button = usage->hid & HID_USAGE; - code = winwing_map_button(button, data->map_more_buttons); + code = winwing_map_button(button, data->has_grip15); hid_map_usage(hi, usage, bit, max, EV_KEY, code); return 1; } +/* + * If x ≤ 0, return 0; + * if x is in [1 .. 65535], return a value in [1 .. 255] + */ +static inline int convert_magnitude(int x) +{ + if (x < 1) + return 0; + + return ((x * 255) >> 16) + 1; +} + +static int winwing_haptic_rumble(struct winwing_drv_data *data) +{ + __u8 *buf; + __u8 m; + + if (!data) + return -EINVAL; + + if (!data->hdev) + return -EINVAL; + + buf = data->report_rumble; + + if (!buf) + return -EINVAL; + + m = convert_magnitude(data->rumble.strong_magnitude); + if (m != data->rumble_left) { + int ret; + + /* + * Mimicking requests captured by usbmon when rumble + * is activated by the vendor's app in a VM. + */ + buf[0] = 0x02; + buf[1] = 0x01; + buf[2] = 0xbf; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x03; + buf[6] = 0x49; + buf[7] = 0x00; + buf[8] = m; + buf[9] = 0x00; + buf[10] = 0; + buf[11] = 0; + buf[12] = 0; + buf[13] = 0; + + ret = hid_hw_output_report(data->hdev, buf, 14); + if (ret < 0) { + hid_err(data->hdev, "error %d (%*ph)\n", ret, 14, buf); + return ret; + } + data->rumble_left = m; + } + + m = convert_magnitude(data->rumble.weak_magnitude); + if (m != data->rumble_right) { + int ret; + + /* + * Mimicking requests captured by usbmon when rumble + * is activated by the vendor's app in a VM. + */ + buf[0] = 0x02; + buf[1] = 0x03; + buf[2] = 0xbf; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x03; + buf[6] = 0x49; + buf[7] = 0x00; + buf[8] = m; + buf[9] = 0x00; + buf[10] = 0; + buf[11] = 0; + buf[12] = 0; + buf[13] = 0; + + ret = hid_hw_output_report(data->hdev, buf, 14); + if (ret < 0) { + hid_err(data->hdev, "error %d (%*ph)\n", ret, 14, buf); + return ret; + } + data->rumble_right = m; + } + + return 0; +} + + +static void winwing_haptic_rumble_cb(struct work_struct *work) +{ + struct winwing_drv_data *data; + + data = container_of(work, struct winwing_drv_data, rumble_work); + winwing_haptic_rumble(data); +} + +static int winwing_play_effect(struct input_dev *dev, void *context, + struct ff_effect *effect) +{ + struct winwing_drv_data *data = (struct winwing_drv_data *) context; + + if (effect->type != FF_RUMBLE) + return 0; + + if (!data) + return -EINVAL; + + data->rumble = effect->u.rumble; + + return schedule_work(&data->rumble_work); +} + +static int winwing_init_ff(struct hid_device *hdev, struct hid_input *hidinput) +{ + struct winwing_drv_data *data; + + data = (struct winwing_drv_data *) hid_get_drvdata(hdev); + if (!data) + return -EINVAL; + + data->report_rumble = devm_kzalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL); + data->rumble_left = -1; + data->rumble_right = -1; + + input_set_capability(hidinput->input, EV_FF, FF_RUMBLE); + + return input_ff_create_memless(hidinput->input, data, + winwing_play_effect); +} + static int winwing_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -219,10 +364,12 @@ static int winwing_probe(struct hid_device *hdev, if (!data) return -ENOMEM; - data->map_more_buttons = id->driver_data; - + data->hdev = hdev; + data->has_grip15 = id->driver_data; hid_set_drvdata(hdev, data); + INIT_WORK(&data->rumble_work, winwing_haptic_rumble_cb); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); @@ -232,19 +379,39 @@ static int winwing_probe(struct hid_device *hdev, return 0; } +static void winwing_remove(struct hid_device *hdev) +{ + struct winwing_drv_data *data; + + data = (struct winwing_drv_data *) hid_get_drvdata(hdev); + + if (data) + cancel_work_sync(&data->rumble_work); + + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + static int winwing_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { + struct winwing_drv_data *data; int ret; + data = (struct winwing_drv_data *) hid_get_drvdata(hdev); + ret = winwing_init_led(hdev, hidinput->input); if (ret) hid_err(hdev, "led init failed\n"); + if (data->has_grip15) + winwing_init_ff(hdev, hidinput); + return ret; } +/* Set driver_data to 1 for grips with rumble motor and more than 32 buttons */ static const struct hid_device_id winwing_devices[] = { { HID_USB_DEVICE(0x4098, 0xbd65), .driver_data = 1 }, /* TGRIP-15E */ { HID_USB_DEVICE(0x4098, 0xbd64), .driver_data = 1 }, /* TGRIP-15EX */ @@ -261,6 +428,7 @@ static struct hid_driver winwing_driver = { .input_configured = winwing_input_configured, .input_mapping = winwing_input_mapping, .probe = winwing_probe, + .remove = winwing_remove, }; module_hid_driver(winwing_driver); diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index 5a183af3d5c6..3adb16366e93 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -149,6 +149,8 @@ static const struct i2c_hid_quirks { I2C_HID_QUIRK_BOGUS_IRQ }, { I2C_VENDOR_ID_GOODIX, I2C_DEVICE_ID_GOODIX_0D42, I2C_HID_QUIRK_DELAY_WAKEUP_AFTER_RESUME }, + { I2C_VENDOR_ID_BLTP, I2C_PRODUCT_ID_BLTP7853, + I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, { 0, 0 } }; @@ -574,9 +576,10 @@ static void i2c_hid_get_input(struct i2c_hid *ihid) if (ihid->hid->group != HID_GROUP_RMI) pm_wakeup_event(&ihid->client->dev, 0); - hid_input_report(ihid->hid, HID_INPUT_REPORT, - ihid->inbuf + sizeof(__le16), - ret_size - sizeof(__le16), 1); + hid_safe_input_report(ihid->hid, HID_INPUT_REPORT, + ihid->inbuf + sizeof(__le16), + ihid->bufsize - sizeof(__le16), + ret_size - sizeof(__le16), 1); } return; diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c index b81fcc6ff49e..919e32c47e12 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of-elan.c +++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c @@ -195,12 +195,20 @@ static const struct elan_i2c_hid_chip_data ilitek_ili2901_chip_data = { .main_supply_name = "vcc33", }; +static const struct elan_i2c_hid_chip_data parade_tc3408_chip_data = { + .post_power_delay_ms = 10, + .post_gpio_reset_on_delay_ms = 300, + .hid_descriptor_address = 0x0001, + .main_supply_name = "vcc33", +}; + static const struct of_device_id elan_i2c_hid_of_match[] = { { .compatible = "elan,ekth6915", .data = &elan_ekth6915_chip_data }, { .compatible = "elan,ekth6a12nay", .data = &elan_ekth6a12nay_chip_data }, { .compatible = "focaltech,ft8112", .data = &focaltech_ft8112_chip_data }, { .compatible = "ilitek,ili9882t", .data = &ilitek_ili9882t_chip_data }, { .compatible = "ilitek,ili2901", .data = &ilitek_ili2901_chip_data }, + { .compatible = "parade,tc3408", .data = ¶de_tc3408_chip_data }, { } }; MODULE_DEVICE_TABLE(of, elan_i2c_hid_of_match); diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c index b6a69995692c..f669235f1883 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c @@ -753,9 +753,11 @@ static int quickspi_suspend(struct device *device) if (!qsdev) return -ENODEV; - ret = quickspi_set_power(qsdev, HIDSPI_SLEEP); - if (ret) - return ret; + if (!device_may_wakeup(qsdev->dev)) { + ret = quickspi_set_power(qsdev, HIDSPI_SLEEP); + if (ret) + return ret; + } ret = thc_interrupt_quiesce(qsdev->thc_hw, true); if (ret) @@ -794,9 +796,8 @@ static int quickspi_resume(struct device *device) if (ret) return ret; - ret = quickspi_set_power(qsdev, HIDSPI_ON); - if (ret) - return ret; + if (!device_may_wakeup(qsdev->dev)) + return quickspi_set_power(qsdev, HIDSPI_ON); return 0; } @@ -855,6 +856,9 @@ static int quickspi_poweroff(struct device *device) if (!qsdev) return -ENODEV; + /* Ignore the return value as platform will be poweroff soon */ + quickspi_set_power(qsdev, HIDSPI_OFF); + ret = thc_interrupt_quiesce(qsdev->thc_hw, true); if (ret) return ret; diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c index 16f780bc879b..cb19057f1191 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c @@ -94,7 +94,7 @@ static int quickspi_get_device_descriptor(struct quickspi_device *qsdev) dev_err_once(qsdev->dev, "Read DEVICE_DESCRIPTOR failed, ret = %d\n", ret); dev_err_once(qsdev->dev, "DEVICE_DESCRIPTOR expected len = %u, actual read = %u\n", input_len, read_len); - return ret; + return ret ?: -EINVAL; } input_rep_type = ((struct input_report_body_header *)read_buf)->input_report_type; @@ -318,7 +318,7 @@ int reset_tic(struct quickspi_device *qsdev) dev_err_once(qsdev->dev, "Read RESET_RESPONSE body failed, ret = %d\n", ret); dev_err_once(qsdev->dev, "RESET_RESPONSE body expected len = %u, actual = %u\n", read_len, actual_read_len); - return ret; + return ret ?: -EINVAL; } input_rep_type = FIELD_GET(HIDSPI_IN_REP_BDY_HDR_REP_TYPE, reset_response); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c index d8e195189e4b..9a8449428170 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c @@ -1112,12 +1112,15 @@ int thc_port_select(struct thc_device *dev, enum thc_port_type port_type) EXPORT_SYMBOL_NS_GPL(thc_port_select, "INTEL_THC"); #define THC_SPI_FREQUENCY_7M 7812500 +#define THC_SPI_FREQUENCY_10M 10416700 #define THC_SPI_FREQUENCY_15M 15625000 #define THC_SPI_FREQUENCY_17M 17857100 #define THC_SPI_FREQUENCY_20M 20833000 #define THC_SPI_FREQUENCY_25M 25000000 #define THC_SPI_FREQUENCY_31M 31250000 +#define THC_SPI_FREQUENCY_35M 35714200 #define THC_SPI_FREQUENCY_41M 41666700 +#define THC_SPI_FREQUENCY_50M 50000000 #define THC_SPI_LOW_FREQUENCY THC_SPI_FREQUENCY_17M @@ -1125,21 +1128,27 @@ static u8 thc_get_spi_freq_div_val(struct thc_device *dev, u32 spi_freq_val) { static const int frequency[] = { THC_SPI_FREQUENCY_7M, + THC_SPI_FREQUENCY_10M, THC_SPI_FREQUENCY_15M, THC_SPI_FREQUENCY_17M, THC_SPI_FREQUENCY_20M, THC_SPI_FREQUENCY_25M, THC_SPI_FREQUENCY_31M, + THC_SPI_FREQUENCY_35M, THC_SPI_FREQUENCY_41M, + THC_SPI_FREQUENCY_50M, }; static const u8 frequency_div[] = { THC_SPI_FRQ_DIV_2, THC_SPI_FRQ_DIV_1, + THC_SPI_FRQ_DIV_1, THC_SPI_FRQ_DIV_7, THC_SPI_FRQ_DIV_6, THC_SPI_FRQ_DIV_5, THC_SPI_FRQ_DIV_4, THC_SPI_FRQ_DIV_3, + THC_SPI_FRQ_DIV_3, + THC_SPI_FRQ_DIV_2, }; int size = ARRAY_SIZE(frequency); u32 closest_freq; @@ -1190,6 +1199,25 @@ int thc_spi_read_config(struct thc_device *dev, u32 spi_freq_val, if (spi_freq_val < THC_SPI_LOW_FREQUENCY) is_low_freq = true; + /* 10M, 35M and 50M CLK need 1.5, 3.5 and 2.5 half divider */ + if ((freq_div == THC_SPI_FRQ_DIV_2 && spi_freq_val >= THC_SPI_FREQUENCY_50M) || + (freq_div == THC_SPI_FRQ_DIV_3 && spi_freq_val < THC_SPI_FREQUENCY_41M) || + (freq_div == THC_SPI_FRQ_DIV_1 && spi_freq_val < THC_SPI_FREQUENCY_15M)) { + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET, + THC_M_PRT_SPI_DUTYC_CFG_SPI_TCRF_HALF_DIV_EN, + THC_M_PRT_SPI_DUTYC_CFG_SPI_TCRF_HALF_DIV_EN); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPARE_REG_OFFSET, + THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE, + THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE); + } else { + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET, + THC_M_PRT_SPI_DUTYC_CFG_SPI_TCRF_HALF_DIV_EN, 0); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPARE_REG_OFFSET, + THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE, 0); + } + cfg = FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TCRF, freq_div) | FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TRMODE, io_mode) | (is_low_freq ? THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN : 0) | @@ -1243,6 +1271,25 @@ int thc_spi_write_config(struct thc_device *dev, u32 spi_freq_val, if (spi_freq_val < THC_SPI_LOW_FREQUENCY) is_low_freq = true; + /* 10M, 35M and 50M CLK need 1.5, 3.5 and 2.5 half divider */ + if ((freq_div == THC_SPI_FRQ_DIV_2 && spi_freq_val >= THC_SPI_FREQUENCY_50M) || + (freq_div == THC_SPI_FRQ_DIV_3 && spi_freq_val < THC_SPI_FREQUENCY_41M) || + (freq_div == THC_SPI_FRQ_DIV_1 && spi_freq_val < THC_SPI_FREQUENCY_15M)) { + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET, + THC_M_PRT_SPI_DUTYC_CFG_SPI_TCWF_HALF_DIV_EN, + THC_M_PRT_SPI_DUTYC_CFG_SPI_TCWF_HALF_DIV_EN); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPARE_REG_OFFSET, + THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE, + THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE); + } else { + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET, + THC_M_PRT_SPI_DUTYC_CFG_SPI_TCWF_HALF_DIV_EN, 0); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPARE_REG_OFFSET, + THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE, 0); + } + cfg = FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TCWF, freq_div) | FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TWMODE, io_mode) | (is_low_freq ? THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN : 0) | diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h index 413730f8e3f7..c6d026686b7a 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h @@ -643,6 +643,10 @@ #define THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL GENMASK(3, 0) #define THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN BIT(25) +#define THC_M_PRT_SPI_DUTYC_CFG_SPI_TCRF_HALF_DIV_EN BIT(30) +#define THC_M_PRT_SPI_DUTYC_CFG_SPI_TCWF_HALF_DIV_EN BIT(31) + +#define THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE BIT(2) /* CS Assertion delay default value */ #define THC_CSA_CK_DELAY_VAL_DEFAULT 4 diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index ddd5d77fb5a5..5af93b9b1fb5 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -283,9 +283,9 @@ static void hid_irq_in(struct urb *urb) break; usbhid_mark_busy(usbhid); if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) { - hid_input_report(urb->context, HID_INPUT_REPORT, - urb->transfer_buffer, - urb->actual_length, 1); + hid_safe_input_report(urb->context, HID_INPUT_REPORT, + urb->transfer_buffer, urb->transfer_buffer_length, + urb->actual_length, 1); /* * autosuspend refused while keys are pressed * because most keyboards don't wake up when @@ -482,9 +482,10 @@ static void hid_ctrl(struct urb *urb) switch (status) { case 0: /* success */ if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN) - hid_input_report(urb->context, + hid_safe_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type, - urb->transfer_buffer, urb->actual_length, 0); + urb->transfer_buffer, urb->transfer_buffer_length, + urb->actual_length, 0); break; case -ESHUTDOWN: /* unplug */ unplug = 1; @@ -1363,19 +1364,17 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * { struct usb_host_interface *interface = intf->cur_altsetting; struct usb_device *dev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *ep; struct usbhid_device *usbhid; struct hid_device *hid; - unsigned int n, has_in = 0; size_t len; int ret; dbg_hid("HID probe called for ifnum %d\n", intf->altsetting->desc.bInterfaceNumber); - for (n = 0; n < interface->desc.bNumEndpoints; n++) - if (usb_endpoint_is_int_in(&interface->endpoint[n].desc)) - has_in++; - if (!has_in) { + ret = usb_find_int_in_endpoint(interface, &ep); + if (ret) { hid_err(intf, "couldn't find an input interrupt endpoint\n"); return -ENODEV; } @@ -1552,7 +1551,7 @@ static int hid_post_reset(struct usb_interface *intf) * configuration descriptors passed, we already know that * the size of the HID report descriptor has not changed. */ - rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL); + rdesc = kmalloc(hid->dev_rsize, GFP_NOIO); if (!rdesc) return -ENOMEM; diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index fbf3dbc92e66..c45f182d0448 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -11,8 +11,10 @@ #include "hid-pidff.h" #include #include +#include #include #include +#include #include #define PID_EFFECTS_MAX 64 @@ -81,7 +83,7 @@ static const u8 pidff_set_envelope[] = { 0x22, 0x5b, 0x5c, 0x5d, 0x5e }; #define PID_NEG_COEFFICIENT 4 #define PID_POS_SATURATION 5 #define PID_NEG_SATURATION 6 -#define PID_DEAD_BAND 7 +#define PID_DEADBAND 7 static const u8 pidff_set_condition[] = { 0x22, 0x23, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65 }; @@ -325,8 +327,10 @@ static s32 pidff_clamp(s32 i, struct hid_field *field) */ static int pidff_rescale(int i, int max, struct hid_field *field) { - return i * (field->logical_maximum - field->logical_minimum) / max + - field->logical_minimum; + /* 64 bits needed for big values during rescale */ + s64 result = field->logical_maximum - field->logical_minimum; + + return div_s64(result * i, max) + field->logical_minimum; } /* @@ -618,14 +622,24 @@ static void pidff_set_condition_report(struct pidff_device *pidff, effect->u.condition[i].center); pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT], effect->u.condition[i].right_coeff); - pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT], - effect->u.condition[i].left_coeff); pidff_set(&pidff->set_condition[PID_POS_SATURATION], effect->u.condition[i].right_saturation); - pidff_set(&pidff->set_condition[PID_NEG_SATURATION], - effect->u.condition[i].left_saturation); - pidff_set(&pidff->set_condition[PID_DEAD_BAND], - effect->u.condition[i].deadband); + + /* Omit Negative Coefficient if missing */ + if (!(pidff->quirks & HID_PIDFF_QUIRK_MISSING_NEG_COEFFICIENT)) + pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT], + effect->u.condition[i].left_coeff); + + /* Omit Negative Saturation if missing */ + if (!(pidff->quirks & HID_PIDFF_QUIRK_MISSING_NEG_SATURATION)) + pidff_set_signed(&pidff->set_condition[PID_NEG_SATURATION], + effect->u.condition[i].left_saturation); + + /* Omit Deadband field if missing */ + if (!(pidff->quirks & HID_PIDFF_QUIRK_MISSING_DEADBAND)) + pidff_set(&pidff->set_condition[PID_DEADBAND], + effect->u.condition[i].deadband); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_CONDITION], HID_REQ_SET_REPORT); } @@ -1053,6 +1067,11 @@ static int pidff_find_field_with_usage(int *usage_index, return -1; } +#define PIDFF_MISSING_FIELD(name, quirks) \ + ({ pr_debug("%s field not found, but that's OK\n", __stringify(name)); \ + pr_debug("Setting MISSING_%s quirk\n", __stringify(name)); \ + *quirks |= HID_PIDFF_QUIRK_MISSING_ ## name; }) + /* * Find fields from a report and fill a pidff_usage */ @@ -1060,9 +1079,6 @@ static int pidff_find_fields(struct pidff_usage *usage, const u8 *table, struct hid_report *report, int count, int strict, u32 *quirks) { - const u8 block_offset = pidff_set_condition[PID_PARAM_BLOCK_OFFSET]; - const u8 delay = pidff_set_effect[PID_START_DELAY]; - if (!report) { pr_debug("%s, null report\n", __func__); return -1; @@ -1080,17 +1096,23 @@ static int pidff_find_fields(struct pidff_usage *usage, const u8 *table, continue; } - if (table[i] == delay) { - pr_debug("Delay field not found, but that's OK\n"); - pr_debug("Setting MISSING_DELAY quirk\n"); - *quirks |= HID_PIDFF_QUIRK_MISSING_DELAY; + /* Field quirks auto-detection */ + if (table[i] == pidff_set_effect[PID_START_DELAY]) + PIDFF_MISSING_FIELD(DELAY, quirks); - } else if (table[i] == block_offset) { - pr_debug("PBO field not found, but that's OK\n"); - pr_debug("Setting MISSING_PBO quirk\n"); - *quirks |= HID_PIDFF_QUIRK_MISSING_PBO; + else if (table[i] == pidff_set_condition[PID_PARAM_BLOCK_OFFSET]) + PIDFF_MISSING_FIELD(PBO, quirks); - } else if (strict) { + else if (table[i] == pidff_set_condition[PID_NEG_COEFFICIENT]) + PIDFF_MISSING_FIELD(NEG_COEFFICIENT, quirks); + + else if (table[i] == pidff_set_condition[PID_NEG_SATURATION]) + PIDFF_MISSING_FIELD(NEG_SATURATION, quirks); + + else if (table[i] == pidff_set_condition[PID_DEADBAND]) + PIDFF_MISSING_FIELD(DEADBAND, quirks); + + else if (strict) { pr_debug("failed to locate %d\n", i); return -1; } diff --git a/drivers/hid/usbhid/hid-pidff.h b/drivers/hid/usbhid/hid-pidff.h index f321f675e131..c413aa732842 100644 --- a/drivers/hid/usbhid/hid-pidff.h +++ b/drivers/hid/usbhid/hid-pidff.h @@ -21,6 +21,15 @@ /* Force all periodic effects to be uploaded as SINE */ #define HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY BIT(4) +/* Allow devices with missing negative coefficient in the set condition usage */ +#define HID_PIDFF_QUIRK_MISSING_NEG_COEFFICIENT BIT(5) + +/* Allow devices with missing negative saturation in the set condition usage */ +#define HID_PIDFF_QUIRK_MISSING_NEG_SATURATION BIT(6) + +/* Allow devices with missing deadband in the set condition usage */ +#define HID_PIDFF_QUIRK_MISSING_DEADBAND BIT(7) + #ifdef CONFIG_HID_PID int hid_pidff_init(struct hid_device *hid); int hid_pidff_init_with_quirks(struct hid_device *hid, u32 initial_quirks); diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 0d1c6d90fe21..a32320b351e3 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -90,7 +90,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev, kfree(buf); continue; } - err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, false); + err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, size, false); if (err) { hid_warn(hdev, "%s: unable to flush event due to error %d\n", __func__, err); @@ -334,7 +334,7 @@ static void wacom_feature_mapping(struct hid_device *hdev, data, n, WAC_CMD_RETRIES); if (ret == n && features->type == HID_GENERIC) { ret = hid_report_raw_event(hdev, - HID_FEATURE_REPORT, data, n, 0); + HID_FEATURE_REPORT, data, n, n, 0); } else if (ret == 2 && features->type != HID_GENERIC) { features->touch_max = data[1]; } else { @@ -395,7 +395,7 @@ static void wacom_feature_mapping(struct hid_device *hdev, data, n, WAC_CMD_RETRIES); if (ret == n) { ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, - data, n, 0); + data, n, n, 0); } else { hid_warn(hdev, "%s: could not retrieve sensor offsets\n", __func__); diff --git a/drivers/hsi/clients/cmt_speech.c b/drivers/hsi/clients/cmt_speech.c index d5aa87833768..7226677ebde7 100644 --- a/drivers/hsi/clients/cmt_speech.c +++ b/drivers/hsi/clients/cmt_speech.c @@ -892,7 +892,7 @@ static void cs_hsi_data_enable(struct cs_hsi_iface *hi, data_start = L1_CACHE_ALIGN(sizeof(*hi->mmap_cfg)); dev_dbg(&hi->cl->device, - "setting data start at %u, cfg block %u, align %u\n", + "setting data start at %u, cfg block %zu, align %u\n", data_start, sizeof(*hi->mmap_cfg), L1_CACHE_BYTES); for (i = 0; i < hi->mmap_cfg->rx_bufs; i++) { diff --git a/drivers/hsi/controllers/Kconfig b/drivers/hsi/controllers/Kconfig index 03ca5b558824..d3543197fff9 100644 --- a/drivers/hsi/controllers/Kconfig +++ b/drivers/hsi/controllers/Kconfig @@ -6,7 +6,7 @@ comment "HSI controllers" config OMAP_SSI tristate "OMAP SSI hardware driver" - depends on HSI && OF && ARM && COMMON_CLK + depends on HSI && OF && COMMON_CLK depends on ARCH_OMAP3 || COMPILE_TEST help SSI is a legacy version of HSI. It is usually used to connect diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index 50dde968febe..99904312879b 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -452,7 +452,6 @@ static int ssi_setup(struct hsi_client *cl) void __iomem *sst = omap_port->sst_base; void __iomem *ssr = omap_port->ssr_base; u32 div; - u32 val; int err = 0; pm_runtime_get_sync(omap_port->pdev); @@ -470,7 +469,7 @@ static int ssi_setup(struct hsi_client *cl) writel_relaxed(SSI_MODE_SLEEP, sst + SSI_SST_MODE_REG); writel_relaxed(SSI_MODE_SLEEP, ssr + SSI_SSR_MODE_REG); /* Flush posted write */ - val = readl(ssr + SSI_SSR_MODE_REG); + readl(ssr + SSI_SSR_MODE_REG); /* TX */ writel_relaxed(31, sst + SSI_SST_FRAMESIZE_REG); writel_relaxed(div, sst + SSI_SST_DIVISOR_REG); @@ -1118,7 +1117,7 @@ static int ssi_port_probe(struct platform_device *pd) dev_dbg(&pd->dev, "init ssi port...\n"); - if (!ssi->port || !omap_ssi->port) { + if (!omap_ssi->port) { dev_err(&pd->dev, "ssi controller not initialized!\n"); err = -ENODEV; goto error; @@ -1299,14 +1298,12 @@ static int ssi_restore_port_ctx(struct omap_ssi_port *omap_port) static int ssi_restore_port_mode(struct omap_ssi_port *omap_port) { - u32 mode; - writel_relaxed(omap_port->sst.mode, omap_port->sst_base + SSI_SST_MODE_REG); writel_relaxed(omap_port->ssr.mode, omap_port->ssr_base + SSI_SSR_MODE_REG); /* OCP barrier */ - mode = readl(omap_port->ssr_base + SSI_SSR_MODE_REG); + readl(omap_port->ssr_base + SSI_SSR_MODE_REG); return 0; } diff --git a/drivers/hsi/hsi_core.c b/drivers/hsi/hsi_core.c index 7cb2dcb30fdb..754949f5ebd6 100644 --- a/drivers/hsi/hsi_core.c +++ b/drivers/hsi/hsi_core.c @@ -342,7 +342,6 @@ static void hsi_controller_release(struct device *dev) { struct hsi_controller *hsi = to_hsi_controller(dev); - kfree(hsi->port); kfree(hsi); } @@ -446,7 +445,7 @@ void hsi_put_controller(struct hsi_controller *hsi) return; for (i = 0; i < hsi->num_ports; i++) - if (hsi->port && hsi->port[i]) + if (hsi->port[i]) put_device(&hsi->port[i]->device); put_device(&hsi->device); } @@ -462,39 +461,33 @@ EXPORT_SYMBOL_GPL(hsi_put_controller); struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags) { struct hsi_controller *hsi; - struct hsi_port **port; unsigned int i; if (!n_ports) return NULL; - hsi = kzalloc_obj(*hsi, flags); + hsi = kzalloc_flex(*hsi, port, n_ports, flags); if (!hsi) return NULL; - port = kzalloc_objs(*port, n_ports, flags); - if (!port) { - kfree(hsi); - return NULL; - } + hsi->num_ports = n_ports; - hsi->port = port; hsi->device.release = hsi_controller_release; device_initialize(&hsi->device); for (i = 0; i < n_ports; i++) { - port[i] = kzalloc_obj(**port, flags); - if (port[i] == NULL) + hsi->port[i] = kzalloc_obj(**hsi->port, flags); + if (hsi->port[i] == NULL) goto out; - port[i]->num = i; - port[i]->async = hsi_dummy_msg; - port[i]->setup = hsi_dummy_cl; - port[i]->flush = hsi_dummy_cl; - port[i]->start_tx = hsi_dummy_cl; - port[i]->stop_tx = hsi_dummy_cl; - port[i]->release = hsi_dummy_cl; - mutex_init(&port[i]->lock); - BLOCKING_INIT_NOTIFIER_HEAD(&port[i]->n_head); - dev_set_name(&port[i]->device, "port%d", i); + hsi->port[i]->num = i; + hsi->port[i]->async = hsi_dummy_msg; + hsi->port[i]->setup = hsi_dummy_cl; + hsi->port[i]->flush = hsi_dummy_cl; + hsi->port[i]->start_tx = hsi_dummy_cl; + hsi->port[i]->stop_tx = hsi_dummy_cl; + hsi->port[i]->release = hsi_dummy_cl; + mutex_init(&hsi->port[i]->lock); + BLOCKING_INIT_NOTIFIER_HEAD(&hsi->port[i]->n_head); + dev_set_name(&hsi->port[i]->device, "port%d", i); hsi->port[i]->device.release = hsi_port_release; device_initialize(&hsi->port[i]->device); } diff --git a/drivers/hte/Kconfig b/drivers/hte/Kconfig index 641af722b555..f57bad67deef 100644 --- a/drivers/hte/Kconfig +++ b/drivers/hte/Kconfig @@ -16,13 +16,13 @@ if HTE config HTE_TEGRA194 tristate "NVIDIA Tegra194 HTE Support" - depends on (ARCH_TEGRA_194_SOC || COMPILE_TEST) + depends on (ARCH_TEGRA || COMPILE_TEST) depends on GPIOLIB help Enable this option for integrated hardware timestamping engine also known as generic timestamping engine (GTE) support on NVIDIA Tegra194 - systems-on-chip. The driver supports 352 LIC IRQs and 39 AON GPIOs - lines for timestamping in realtime. + and later systems-on-chip. The driver supports 352 LIC IRQs and 39 + AON GPIOs lines for timestamping in realtime. config HTE_TEGRA194_TEST tristate "NVIDIA Tegra194 HTE Test" diff --git a/drivers/hte/hte-tegra194.c b/drivers/hte/hte-tegra194.c index 690eb9be30fb..4a7702b32b24 100644 --- a/drivers/hte/hte-tegra194.c +++ b/drivers/hte/hte-tegra194.c @@ -20,10 +20,11 @@ #define HTE_SUSPEND 0 -/* HTE source clock TSC is 31.25MHz */ +/* HTE source clock TSC is 1GHz for T264 and 31.25MHz for others */ #define HTE_TS_CLK_RATE_HZ 31250000ULL +#define HTE_TS_CLK_RATE_1G 1000000000ULL #define HTE_CLK_RATE_NS 32 -#define HTE_TS_NS_SHIFT __builtin_ctz(HTE_CLK_RATE_NS) +#define HTE_CLK_RATE_NS_1G 1 #define NV_AON_SLICE_INVALID -1 #define NV_LINES_IN_SLICE 32 @@ -120,6 +121,8 @@ struct tegra_hte_data { u32 slices; u32 map_sz; u32 sec_map_sz; + u64 tsc_clkrate_hz; + u32 tsc_clkrate_ns; const struct tegra_hte_line_mapped *map; const struct tegra_hte_line_mapped *sec_map; }; @@ -317,6 +320,94 @@ static const struct tegra_hte_line_mapped tegra234_aon_gpio_sec_map[] = { [40] = {2, NV_AON_HTE_SLICE2_IRQ_GPIO_23}, }; +static const struct tegra_hte_line_mapped tegra264_aon_gpio_map[] = { + /* gpio, slice, bit_index */ + /* AA port */ + [0] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_29}, + [1] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_28}, + [2] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_27}, + [3] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_26}, + [4] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_25}, + [5] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_24}, + [6] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_23}, + [7] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_22}, + /* BB port */ + [8] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_21}, + [9] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_20}, + /* CC port */ + [10] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_19}, + [11] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_18}, + [12] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_17}, + [13] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_16}, + [14] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_15}, + [15] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_14}, + [16] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_13}, + [17] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_12}, + /* DD port */ + [18] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_11}, + [19] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_10}, + [20] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_9}, + [21] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_8}, + [22] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_7}, + [23] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_6}, + [24] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_5}, + [25] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_4}, + /* EE port */ + [26] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_3}, + [27] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_2}, + [28] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_1}, + [29] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_0}, +}; + +static const struct tegra_hte_line_mapped tegra264_aon_gpio_sec_map[] = { + /* gpio, slice, bit_index */ + /* AA port */ + [0] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_29}, + [1] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_28}, + [2] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_27}, + [3] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_26}, + [4] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_25}, + [5] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_24}, + [6] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_23}, + [7] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_22}, + /* BB port */ + [8] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_21}, + [9] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_20}, + [10] = {NV_AON_SLICE_INVALID, 0}, + [11] = {NV_AON_SLICE_INVALID, 0}, + [12] = {NV_AON_SLICE_INVALID, 0}, + [13] = {NV_AON_SLICE_INVALID, 0}, + [14] = {NV_AON_SLICE_INVALID, 0}, + [15] = {NV_AON_SLICE_INVALID, 0}, + /* CC port */ + [16] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_19}, + [17] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_18}, + [18] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_17}, + [19] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_16}, + [20] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_15}, + [21] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_14}, + [22] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_13}, + [23] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_12}, + /* DD port */ + [24] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_11}, + [25] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_10}, + [26] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_9}, + [27] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_8}, + [28] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_7}, + [29] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_6}, + [30] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_5}, + [31] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_4}, + /* EE port */ + [32] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_3}, + [33] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_2}, + [34] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_1}, + [35] = {3, NV_AON_HTE_SLICE2_IRQ_GPIO_0}, + [36] = {NV_AON_SLICE_INVALID, 0}, + [37] = {NV_AON_SLICE_INVALID, 0}, + [38] = {NV_AON_SLICE_INVALID, 0}, + [39] = {NV_AON_SLICE_INVALID, 0}, +}; + static const struct tegra_hte_data t194_aon_hte = { .map_sz = ARRAY_SIZE(tegra194_aon_gpio_map), .map = tegra194_aon_gpio_map, @@ -324,6 +415,8 @@ static const struct tegra_hte_data t194_aon_hte = { .sec_map = tegra194_aon_gpio_sec_map, .type = HTE_TEGRA_TYPE_GPIO, .slices = 3, + .tsc_clkrate_hz = HTE_TS_CLK_RATE_HZ, + .tsc_clkrate_ns = HTE_CLK_RATE_NS, }; static const struct tegra_hte_data t234_aon_hte = { @@ -333,6 +426,19 @@ static const struct tegra_hte_data t234_aon_hte = { .sec_map = tegra234_aon_gpio_sec_map, .type = HTE_TEGRA_TYPE_GPIO, .slices = 3, + .tsc_clkrate_hz = HTE_TS_CLK_RATE_HZ, + .tsc_clkrate_ns = HTE_CLK_RATE_NS, +}; + +static const struct tegra_hte_data t264_aon_hte = { + .map_sz = ARRAY_SIZE(tegra264_aon_gpio_map), + .map = tegra264_aon_gpio_map, + .sec_map_sz = ARRAY_SIZE(tegra264_aon_gpio_sec_map), + .sec_map = tegra264_aon_gpio_sec_map, + .type = HTE_TEGRA_TYPE_GPIO, + .slices = 4, + .tsc_clkrate_hz = HTE_TS_CLK_RATE_1G, + .tsc_clkrate_ns = HTE_CLK_RATE_NS_1G, }; static const struct tegra_hte_data t194_lic_hte = { @@ -340,6 +446,8 @@ static const struct tegra_hte_data t194_lic_hte = { .map = NULL, .type = HTE_TEGRA_TYPE_LIC, .slices = 11, + .tsc_clkrate_hz = HTE_TS_CLK_RATE_HZ, + .tsc_clkrate_ns = HTE_CLK_RATE_NS, }; static const struct tegra_hte_data t234_lic_hte = { @@ -347,6 +455,17 @@ static const struct tegra_hte_data t234_lic_hte = { .map = NULL, .type = HTE_TEGRA_TYPE_LIC, .slices = 17, + .tsc_clkrate_hz = HTE_TS_CLK_RATE_HZ, + .tsc_clkrate_ns = HTE_CLK_RATE_NS, +}; + +static const struct tegra_hte_data t264_lic_hte = { + .map_sz = 0, + .map = NULL, + .type = HTE_TEGRA_TYPE_LIC, + .slices = 10, + .tsc_clkrate_hz = HTE_TS_CLK_RATE_1G, + .tsc_clkrate_ns = HTE_CLK_RATE_NS_1G, }; static inline u32 tegra_hte_readl(struct tegra_hte_soc *hte, u32 reg) @@ -574,12 +693,12 @@ static int tegra_hte_release(struct hte_chip *chip, struct hte_ts_desc *desc, static int tegra_hte_clk_src_info(struct hte_chip *chip, struct hte_clk_info *ci) { - (void)chip; + struct tegra_hte_soc *hte_dev = chip->data; if (!ci) return -EINVAL; - ci->hz = HTE_TS_CLK_RATE_HZ; + ci->hz = hte_dev->prov_data->tsc_clkrate_hz; ci->type = CLOCK_MONOTONIC; return 0; @@ -602,8 +721,10 @@ static void tegra_hte_read_fifo(struct tegra_hte_soc *gs) { u32 tsh, tsl, src, pv, cv, acv, slice, bit_index, line_id; u64 tsc; + u8 tsc_ns_shift; struct hte_ts_data el; + tsc_ns_shift = __builtin_ctz(gs->prov_data->tsc_clkrate_ns); while ((tegra_hte_readl(gs, HTE_TESTATUS) >> HTE_TESTATUS_OCCUPANCY_SHIFT) & HTE_TESTATUS_OCCUPANCY_MASK) { @@ -621,7 +742,7 @@ static void tegra_hte_read_fifo(struct tegra_hte_soc *gs) while (acv) { bit_index = __builtin_ctz(acv); line_id = bit_index + (slice << 5); - el.tsc = tsc << HTE_TS_NS_SHIFT; + el.tsc = tsc << tsc_ns_shift; el.raw_level = tegra_hte_get_level(gs, line_id); hte_push_ts_ns(gs->chip, line_id, &el); acv &= ~BIT(bit_index); @@ -656,6 +777,8 @@ static const struct of_device_id tegra_hte_of_match[] = { { .compatible = "nvidia,tegra194-gte-aon", .data = &t194_aon_hte}, { .compatible = "nvidia,tegra234-gte-lic", .data = &t234_lic_hte}, { .compatible = "nvidia,tegra234-gte-aon", .data = &t234_aon_hte}, + { .compatible = "nvidia,tegra264-gte-lic", .data = &t264_lic_hte}, + { .compatible = "nvidia,tegra264-gte-aon", .data = &t264_aon_hte}, { } }; MODULE_DEVICE_TABLE(of, tegra_hte_of_match); diff --git a/drivers/hte/hte.c b/drivers/hte/hte.c index fd0f29038a5f..a42350348213 100644 --- a/drivers/hte/hte.c +++ b/drivers/hte/hte.c @@ -826,7 +826,7 @@ int hte_push_ts_ns(const struct hte_chip *chip, u32 xlated_id, ret = ei->cb(data, ei->cl_data); if (ret == HTE_RUN_SECOND_CB && ei->tcb) { - queue_work(system_unbound_wq, &ei->cb_work); + queue_work(system_dfl_wq, &ei->cb_work); set_bit(HTE_TS_QUEUE_WK, &ei->flags); } diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig index 7937ac0cbd0f..2d0b3fcb0ff8 100644 --- a/drivers/hv/Kconfig +++ b/drivers/hv/Kconfig @@ -9,7 +9,6 @@ config HYPERV select PARAVIRT select X86_HV_CALLBACK_VECTOR if X86 select OF_EARLY_FLATTREE if OF - select SYSFB if EFI && !HYPERV_VTL_MODE select IRQ_MSI_LIB if X86 help Select this option to run Linux as a Hyper-V client operating @@ -62,6 +61,7 @@ config HYPERV_VMBUS tristate "Microsoft Hyper-V VMBus driver" depends on HYPERV default HYPERV + select SYSFB if EFI && !HYPERV_VTL_MODE help Select this option to enable Hyper-V Vmbus driver. diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile index 2593711c3628..888a748cc7cb 100644 --- a/drivers/hv/Makefile +++ b/drivers/hv/Makefile @@ -16,6 +16,7 @@ hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_utils_transport.o mshv_root-y := mshv_root_main.o mshv_synic.o mshv_eventfd.o mshv_irq.o \ mshv_root_hv_call.o mshv_portid_table.o mshv_regions.o mshv_root-$(CONFIG_DEBUG_FS) += mshv_debugfs.o +mshv_root-$(CONFIG_TRACEPOINTS) += mshv_trace.o mshv_vtl-y := mshv_vtl_main.o # Code that must be built-in diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 7c77ada12b2e..84eb0a6a0b54 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -384,8 +384,18 @@ static void free_channel(struct vmbus_channel *channel) void vmbus_channel_map_relid(struct vmbus_channel *channel) { - if (WARN_ON(channel->offermsg.child_relid >= MAX_CHANNEL_RELIDS)) + u32 new_relid = channel->offermsg.child_relid; + + if (WARN_ON(new_relid >= MAX_CHANNEL_RELIDS)) return; + + /* + * This function is always called in the tasklet for the connect CPU. + * So updating the relid hiwater mark does not need to be atomic. + */ + if (new_relid > READ_ONCE(vmbus_connection.relid_hiwater)) + WRITE_ONCE(vmbus_connection.relid_hiwater, new_relid); + /* * The mapping of the channel's relid is visible from the CPUs that * execute vmbus_chan_sched() by the time that vmbus_chan_sched() will @@ -411,9 +421,7 @@ void vmbus_channel_map_relid(struct vmbus_channel *channel) * of the VMBus driver and vmbus_chan_sched() can not run before * vmbus_bus_resume() has completed execution (cf. resume_noirq). */ - virt_store_mb( - vmbus_connection.channels[channel->offermsg.child_relid], - channel); + virt_store_mb(vmbus_connection.channels[new_relid], channel); } void vmbus_channel_unmap_relid(struct vmbus_channel *channel) diff --git a/drivers/hv/hv_proc.c b/drivers/hv/hv_proc.c index 3cb4b2a3035c..57b2c64197cb 100644 --- a/drivers/hv/hv_proc.c +++ b/drivers/hv/hv_proc.c @@ -239,3 +239,50 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags) return ret; } EXPORT_SYMBOL_GPL(hv_call_create_vp); + +int hv_call_notify_all_processors_started(void) +{ + struct hv_input_notify_partition_event *input; + u64 status; + unsigned long irq_flags; + int ret = 0; + + local_irq_save(irq_flags); + input = *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + input->event = HV_PARTITION_ALL_LOGICAL_PROCESSORS_STARTED; + status = hv_do_hypercall(HVCALL_NOTIFY_PARTITION_EVENT, + input, NULL); + local_irq_restore(irq_flags); + + if (!hv_result_success(status)) { + hv_status_err(status, "\n"); + ret = hv_result_to_errno(status); + } + return ret; +} + +bool hv_lp_exists(u32 lp_index) +{ + struct hv_input_get_logical_processor_run_time *input; + struct hv_output_get_logical_processor_run_time *output; + unsigned long flags; + u64 status; + + local_irq_save(flags); + input = *this_cpu_ptr(hyperv_pcpu_input_arg); + output = *this_cpu_ptr(hyperv_pcpu_output_arg); + + input->lp_index = lp_index; + status = hv_do_hypercall(HVCALL_GET_LOGICAL_PROCESSOR_RUN_TIME, + input, output); + local_irq_restore(flags); + + if (!hv_result_success(status) && + hv_result(status) != HV_STATUS_INVALID_LP_INDEX) { + hv_status_err(status, "\n"); + BUG(); + } + + return hv_result_success(status); +} diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 31f576464f18..05a36854389a 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -276,8 +276,9 @@ struct vmbus_connection { struct list_head chn_list; struct mutex channel_mutex; - /* Array of channels */ + /* Array of channel pointers, indexed by relid */ struct vmbus_channel **channels; + u32 relid_hiwater; /* * An offer message is handled first on the work_queue, and then diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c index d8471546e6a0..90959f639dc3 100644 --- a/drivers/hv/mshv_eventfd.c +++ b/drivers/hv/mshv_eventfd.c @@ -733,6 +733,14 @@ static int mshv_assign_ioeventfd(struct mshv_partition *pt, ret = mshv_register_doorbell(pt->pt_id, ioeventfd_mmio_write, (void *)pt, p->iovntfd_addr, p->iovntfd_datamatch, doorbell_flags); + + trace_mshv_assign_ioeventfd(pt->pt_id, p->iovntfd_addr, + p->iovntfd_length, + p->iovntfd_datamatch, + p->iovntfd_wildcard, + p->iovntfd_eventfd, + ret); + if (ret < 0) goto unlock_fail; @@ -780,6 +788,12 @@ static int mshv_deassign_ioeventfd(struct mshv_partition *pt, p->iovntfd_datamatch != args->datamatch) continue; + trace_mshv_deassign_ioeventfd(pt->pt_id, p->iovntfd_addr, + p->iovntfd_length, + p->iovntfd_datamatch, + p->iovntfd_wildcard, + p->iovntfd_eventfd); + hlist_del_rcu(&p->iovntfd_hnode); synchronize_rcu(); ioeventfd_release(p, pt->pt_id); diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c index 02c56fc3a498..b3142c84dcbc 100644 --- a/drivers/hv/mshv_irq.c +++ b/drivers/hv/mshv_irq.c @@ -71,6 +71,10 @@ swap_routes: mutex_unlock(&partition->pt_irq_lock); synchronize_srcu_expedited(&partition->pt_irq_srcu); + + trace_mshv_update_routing_table(partition->pt_id, + old, new, numents); + new = old; out: diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h index 826798f1a8ec..1f086dcb7aa1 100644 --- a/drivers/hv/mshv_root.h +++ b/drivers/hv/mshv_root.h @@ -17,6 +17,7 @@ #include #include #include +#include "mshv_trace.h" /* * Hypervisor must be between these version numbers (inclusive) diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c index 7f91096f95a8..cb55d4d4be2e 100644 --- a/drivers/hv/mshv_root_hv_call.c +++ b/drivers/hv/mshv_root_hv_call.c @@ -45,8 +45,7 @@ int hv_call_withdraw_memory(u64 count, int node, u64 partition_id) struct hv_output_withdraw_memory *output_page; struct page *page; u16 completed; - unsigned long remaining = count; - u64 status; + u64 status, withdrawn = 0; int i; unsigned long flags; @@ -55,7 +54,7 @@ int hv_call_withdraw_memory(u64 count, int node, u64 partition_id) return -ENOMEM; output_page = page_address(page); - while (remaining) { + while (withdrawn < count) { local_irq_save(flags); input_page = *this_cpu_ptr(hyperv_pcpu_input_arg); @@ -63,7 +62,7 @@ int hv_call_withdraw_memory(u64 count, int node, u64 partition_id) memset(input_page, 0, sizeof(*input_page)); input_page->partition_id = partition_id; status = hv_do_rep_hypercall(HVCALL_WITHDRAW_MEMORY, - min(remaining, HV_WITHDRAW_BATCH_SIZE), + min(count - withdrawn, HV_WITHDRAW_BATCH_SIZE), 0, input_page, output_page); local_irq_restore(flags); @@ -79,10 +78,12 @@ int hv_call_withdraw_memory(u64 count, int node, u64 partition_id) break; } - remaining -= completed; + withdrawn += completed; } free_page((unsigned long)output_page); + trace_mshv_hvcall_withdraw_memory(partition_id, withdrawn, status); + return hv_result_to_errno(status); } @@ -126,6 +127,8 @@ int hv_call_create_partition(u64 flags, ret = hv_deposit_memory(hv_current_partition_id, status); } while (!ret); + trace_mshv_hvcall_create_partition(flags, ret ? ret : *partition_id); + return ret; } @@ -153,6 +156,8 @@ int hv_call_initialize_partition(u64 partition_id) ret = hv_deposit_memory(partition_id, status); } while (!ret); + trace_mshv_hvcall_initialize_partition(partition_id, status); + return ret; } @@ -165,6 +170,8 @@ int hv_call_finalize_partition(u64 partition_id) status = hv_do_fast_hypercall8(HVCALL_FINALIZE_PARTITION, *(u64 *)&input); + trace_mshv_hvcall_finalize_partition(partition_id, status); + return hv_result_to_errno(status); } @@ -176,6 +183,8 @@ int hv_call_delete_partition(u64 partition_id) input.partition_id = partition_id; status = hv_do_fast_hypercall8(HVCALL_DELETE_PARTITION, *(u64 *)&input); + trace_mshv_hvcall_delete_partition(partition_id, status); + return hv_result_to_errno(status); } @@ -573,6 +582,9 @@ static int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type, ret = hv_deposit_memory(partition_id, status); } while (!ret); + trace_mshv_hvcall_map_vp_state_page(partition_id, vp_index, + type, status); + return ret; } diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index c8e5523a52b6..bd1359eb58dd 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -429,6 +429,17 @@ mshv_vp_dispatch(struct mshv_vp *vp, u32 flags, status = hv_do_hypercall(HVCALL_DISPATCH_VP, input, output); vp->run.flags.root_sched_dispatched = 0; + trace_mshv_hvcall_dispatch_vp(vp->vp_partition->pt_id, + vp->vp_index, flags, + output->dispatch_state, + output->dispatch_event, +#if defined(CONFIG_X86_64) + vp->vp_register_page->interrupt_vectors.as_uint64, +#else + 0, +#endif + status); + *res = *output; preempt_enable(); @@ -451,6 +462,9 @@ mshv_vp_clear_explicit_suspend(struct mshv_vp *vp) ret = mshv_set_vp_registers(vp->vp_index, vp->vp_partition->pt_id, 1, &explicit_suspend); + trace_mshv_vp_clear_explicit_suspend(vp->vp_partition->pt_id, + vp->vp_index, ret); + if (ret) vp_err(vp, "Failed to unsuspend\n"); @@ -493,6 +507,12 @@ mshv_vp_wait_for_hv_kick(struct mshv_vp *vp) if (ret) return -EINTR; + trace_mshv_vp_wait_for_hv_kick(vp->vp_partition->pt_id, + vp->vp_index, + vp->run.kicked_by_hv, + mshv_vp_dispatch_thread_blocked(vp), + mshv_vp_interrupt_pending(vp)); + vp->run.flags.root_sched_blocked = 0; vp->run.kicked_by_hv = 0; @@ -521,6 +541,12 @@ static long mshv_run_vp_with_root_scheduler(struct mshv_vp *vp) if (__xfer_to_guest_mode_work_pending()) { ret = xfer_to_guest_mode_handle_work(); + + trace_mshv_xfer_to_guest_mode_work(vp->vp_partition->pt_id, + vp->vp_index, + read_thread_flags(), + ret); + if (ret) break; } @@ -648,7 +674,7 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp) region = mshv_partition_region_by_gfn_get(p, gfn); if (!region) - return false; + goto out; if (access_type == HV_INTERCEPT_ACCESS_WRITE && !(region->hv_map_flags & HV_MAP_GPA_WRITABLE)) @@ -664,7 +690,9 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp) put_region: mshv_region_put(region); - +out: + trace_mshv_handle_gpa_intercept(p->pt_id, vp->vp_index, gfn, + access_type, ret); return ret; } @@ -681,6 +709,8 @@ static long mshv_vp_ioctl_run_vp(struct mshv_vp *vp, void __user *ret_msg) { long rc; + trace_mshv_run_vp_entry(vp->vp_partition->pt_id, vp->vp_index); + do { if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT) rc = mshv_run_vp_with_root_scheduler(vp); @@ -688,6 +718,10 @@ static long mshv_vp_ioctl_run_vp(struct mshv_vp *vp, void __user *ret_msg) rc = mshv_run_vp_with_hyp_scheduler(vp); } while (rc == 0 && mshv_vp_handle_intercept(vp)); + trace_mshv_run_vp_exit(vp->vp_partition->pt_id, vp->vp_index, + vp->vp_intercept_msg_page->header.message_type, + rc); + if (rc) return rc; @@ -949,6 +983,8 @@ mshv_vp_release(struct inode *inode, struct file *filp) { struct mshv_vp *vp = filp->private_data; + trace_mshv_vp_release(vp->vp_partition->pt_id, vp->vp_index); + /* Rest of VP cleanup happens in destroy_partition() */ mshv_partition_put(vp->vp_partition); return 0; @@ -1121,7 +1157,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, partition->pt_vp_count++; partition->pt_vp_array[args.vp_index] = vp; - return ret; + goto out; remove_debugfs_vp: mshv_debugfs_vp_remove(vp); @@ -1147,6 +1183,8 @@ unmap_intercept_message_page: intercept_msg_page, input_vtl_zero); destroy_vp: hv_call_delete_vp(partition->pt_id, args.vp_index); +out: + trace_mshv_create_vp(partition->pt_id, args.vp_index, ret); return ret; } @@ -1346,6 +1384,10 @@ mshv_map_user_memory(struct mshv_partition *partition, break; } + trace_mshv_map_user_memory(partition->pt_id, region->start_uaddr, + region->start_gfn, region->nr_pages, + region->hv_map_flags, ret); + if (ret) goto errout; @@ -1641,6 +1683,9 @@ disable_vp_dispatch(struct mshv_vp *vp) if (ret) vp_err(vp, "failed to suspend\n"); + trace_mshv_disable_vp_dispatch(vp->vp_partition->pt_id, + vp->vp_index, ret); + return ret; } @@ -1689,6 +1734,8 @@ drain_vp_signals(struct mshv_vp *vp) vp->run.kicked_by_hv = 0; vp_signal_count = atomic64_read(&vp->run.vp_signaled_count); } + + trace_mshv_drain_vp_signals(vp->vp_partition->pt_id, vp->vp_index); } static void drain_all_vps(const struct mshv_partition *partition) @@ -1742,6 +1789,8 @@ static void destroy_partition(struct mshv_partition *partition) return; } + trace_mshv_destroy_partition(partition->pt_id); + if (partition->pt_initialized) { /* * We only need to drain signals for root scheduler. This should be @@ -1848,6 +1897,8 @@ mshv_partition_release(struct inode *inode, struct file *filp) { struct mshv_partition *partition = filp->private_data; + trace_mshv_partition_release(partition->pt_id); + mshv_eventfd_release(partition); cleanup_srcu_struct(&partition->pt_irq_srcu); @@ -1977,6 +2028,7 @@ mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev) struct hv_partition_creation_properties creation_properties; union hv_partition_isolation_properties isolation_properties; struct mshv_partition *partition; + u64 pt_id = -1; long ret; ret = mshv_ioctl_process_pt_flags(user_arg, &creation_flags, @@ -2016,22 +2068,29 @@ mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev) ret = hv_call_create_partition(creation_flags, creation_properties, isolation_properties, - &partition->pt_id); + &pt_id); if (ret) goto cleanup_irq_srcu; + partition->pt_id = pt_id; + ret = add_partition(partition); if (ret) goto delete_partition; ret = mshv_init_async_handler(partition); - if (!ret) { - ret = FD_ADD(O_CLOEXEC, anon_inode_getfile("mshv_partition", - &mshv_partition_fops, - partition, O_RDWR)); - if (ret >= 0) - return ret; - } + if (ret) + goto remove_partition; + + ret = FD_ADD(O_CLOEXEC, anon_inode_getfile("mshv_partition", + &mshv_partition_fops, + partition, O_RDWR)); + if (ret < 0) + goto remove_partition; + + goto out; + +remove_partition: remove_partition(partition); delete_partition: hv_call_delete_partition(partition->pt_id); @@ -2039,7 +2098,8 @@ cleanup_irq_srcu: cleanup_srcu_struct(&partition->pt_irq_srcu); free_partition: kfree(partition); - +out: + trace_mshv_create_partition(pt_id, ret); return ret; } diff --git a/drivers/hv/mshv_trace.c b/drivers/hv/mshv_trace.c new file mode 100644 index 000000000000..0936b2f95edd --- /dev/null +++ b/drivers/hv/mshv_trace.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026, Microsoft Corporation. + * + * Tracepoint definitions for mshv driver. + */ + +#define CREATE_TRACE_POINTS +#include "mshv_trace.h" diff --git a/drivers/hv/mshv_trace.h b/drivers/hv/mshv_trace.h new file mode 100644 index 000000000000..e7280c47e579 --- /dev/null +++ b/drivers/hv/mshv_trace.h @@ -0,0 +1,544 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2026, Microsoft Corporation. + * + * Tracepoint declarations for mshv driver. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mshv + +#if !defined(__MSHV_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _MSHV_TRACE_H_ + +#include +#include + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../drivers/hv + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE mshv_trace + +TRACE_EVENT(mshv_create_partition, + TP_PROTO(u64 partition_id, int vm_fd), + TP_ARGS(partition_id, vm_fd), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(int, vm_fd) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vm_fd = vm_fd; + ), + TP_printk("partition_id=%llu vm_fd=%d", + __entry->partition_id, + __entry->vm_fd + ) +); + +TRACE_EVENT(mshv_hvcall_create_partition, + TP_PROTO(u64 flags, s64 partition_id), + TP_ARGS(flags, partition_id), + TP_STRUCT__entry( + __field(u64, flags) + __field(s64, partition_id) + ), + TP_fast_assign( + __entry->flags = flags; + __entry->partition_id = partition_id; + ), + TP_printk("flags=%#llx partition_id=%lld", + __entry->flags, + __entry->partition_id + ) +); + +TRACE_EVENT(mshv_hvcall_initialize_partition, + TP_PROTO(u64 partition_id, u64 status), + TP_ARGS(partition_id, status), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u64, status) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->status = status; + ), + TP_printk("partition_id=%llu status=%#llx", + __entry->partition_id, + __entry->status + ) +); + +TRACE_EVENT(mshv_partition_release, + TP_PROTO(u64 partition_id), + TP_ARGS(partition_id), + TP_STRUCT__entry( + __field(u64, partition_id) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + ), + TP_printk("partition_id=%llu", + __entry->partition_id + ) +); + +TRACE_EVENT(mshv_destroy_partition, + TP_PROTO(u64 partition_id), + TP_ARGS(partition_id), + TP_STRUCT__entry( + __field(u64, partition_id) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + ), + TP_printk("partition_id=%llu", + __entry->partition_id + ) +); + +TRACE_EVENT(mshv_hvcall_finalize_partition, + TP_PROTO(u64 partition_id, u64 status), + TP_ARGS(partition_id, status), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u64, status) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->status = status; + ), + TP_printk("partition_id=%llu status=%#llx ", + __entry->partition_id, + __entry->status + ) +); + +TRACE_EVENT(mshv_hvcall_withdraw_memory, + TP_PROTO(u64 partition_id, u64 withdrawn, u64 status), + TP_ARGS(partition_id, withdrawn, status), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u64, withdrawn) + __field(u64, status) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->withdrawn = withdrawn; + __entry->status = status; + ), + TP_printk("partition_id=%llu withdrawn=%llu status=%#llx", + __entry->partition_id, + __entry->withdrawn, + __entry->status + ) +); + +TRACE_EVENT(mshv_hvcall_delete_partition, + TP_PROTO(u64 partition_id, u64 status), + TP_ARGS(partition_id, status), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u64, status) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->status = status; + ), + TP_printk("partition_id=%llu status=%#llx", + __entry->partition_id, + __entry->status + ) +); + +TRACE_EVENT(mshv_create_vp, + TP_PROTO(u64 partition_id, u32 vp_index, long vp_fd), + TP_ARGS(partition_id, vp_index, vp_fd), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u32, vp_index) + __field(long, vp_fd) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vp_index = vp_index; + __entry->vp_fd = vp_fd; + ), + TP_printk("partition_id=%llu vp_index=%u vp_fd=%ld", + __entry->partition_id, + __entry->vp_index, + __entry->vp_fd + ) +); + +TRACE_EVENT(mshv_hvcall_map_vp_state_page, + TP_PROTO(u64 partition_id, u32 vp_index, u32 page_type, u64 status), + TP_ARGS(partition_id, vp_index, page_type, status), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u32, vp_index) + __field(u32, page_type) + __field(u64, status) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vp_index = vp_index; + __entry->page_type = page_type; + __entry->status = status; + ), + TP_printk("partition_id=%llu vp_index=%u page_type=%u status=%#llx", + __entry->partition_id, + __entry->vp_index, + __entry->page_type, + __entry->status + ) +); + +TRACE_EVENT(mshv_drain_vp_signals, + TP_PROTO(u64 partition_id, u32 vp_index), + TP_ARGS(partition_id, vp_index), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u32, vp_index) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vp_index = vp_index; + ), + TP_printk("partition_id=%llu vp_index=%u", + __entry->partition_id, + __entry->vp_index + ) +); + +TRACE_EVENT(mshv_disable_vp_dispatch, + TP_PROTO(u64 partition_id, u32 vp_index, int ret), + TP_ARGS(partition_id, vp_index, ret), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u32, vp_index) + __field(int, ret) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vp_index = vp_index; + __entry->ret = ret; + ), + TP_printk("partition_id=%llu vp_index=%u ret=%d", + __entry->partition_id, + __entry->vp_index, + __entry->ret + ) +); + +TRACE_EVENT(mshv_vp_release, + TP_PROTO(u64 partition_id, u32 vp_index), + TP_ARGS(partition_id, vp_index), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u32, vp_index) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vp_index = vp_index; + ), + TP_printk("partition_id=%llu vp_index=%u", + __entry->partition_id, + __entry->vp_index + ) +); + +TRACE_EVENT(mshv_run_vp_entry, + TP_PROTO(u64 partition_id, u32 vp_index), + TP_ARGS(partition_id, vp_index), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u32, vp_index) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vp_index = vp_index; + ), + TP_printk("partition_id=%llu vp_index=%u", + __entry->partition_id, + __entry->vp_index + ) +); + +TRACE_EVENT(mshv_run_vp_exit, + TP_PROTO(u64 partition_id, u32 vp_index, u64 hv_message_type, long ret), + TP_ARGS(partition_id, vp_index, hv_message_type, ret), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u32, vp_index) + __field(u64, hv_message_type) + __field(long, ret) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vp_index = vp_index; + __entry->hv_message_type = hv_message_type; + __entry->ret = ret; + ), + TP_printk("partition_id=%llu vp_index=%u hv_message_type=%#llx ret=%ld", + __entry->partition_id, + __entry->vp_index, + __entry->hv_message_type, + __entry->ret + ) +); + +TRACE_EVENT(mshv_vp_clear_explicit_suspend, + TP_PROTO(u64 partition_id, u32 vp_index, int ret), + TP_ARGS(partition_id, vp_index, ret), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u32, vp_index) + __field(int, ret) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vp_index = vp_index; + __entry->ret = ret; + ), + TP_printk("partition_id=%llu vp_index=%u ret=%d", + __entry->partition_id, + __entry->vp_index, + __entry->ret + ) +); + +TRACE_EVENT(mshv_xfer_to_guest_mode_work, + TP_PROTO(u64 partition_id, u32 vp_index, unsigned long thread_info_flag, long ret), + TP_ARGS(partition_id, vp_index, thread_info_flag, ret), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u32, vp_index) + __field(unsigned long, thread_info_flag) + __field(long, ret) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vp_index = vp_index; + __entry->thread_info_flag = thread_info_flag; + __entry->ret = ret; + ), + TP_printk("partition_id=%llu vp_index=%u thread_info_flag=%#lx ret=%ld", + __entry->partition_id, + __entry->vp_index, + __entry->thread_info_flag, + __entry->ret + ) +); + +TRACE_EVENT(mshv_hvcall_dispatch_vp, + TP_PROTO(u64 partition_id, u32 vp_index, u32 flags, + u32 dispatch_state, u32 dispatch_event, u64 irq_vectors, u64 status), + TP_ARGS(partition_id, vp_index, flags, dispatch_state, dispatch_event, irq_vectors, + status), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u32, vp_index) + __field(u32, flags) + __field(u32, dispatch_state) + __field(u32, dispatch_event) + __field(u64, irq_vectors) + __field(u64, status) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vp_index = vp_index; + __entry->flags = flags; + __entry->dispatch_state = dispatch_state; + __entry->dispatch_event = dispatch_event; + __entry->irq_vectors = irq_vectors; + __entry->status = status; + ), + TP_printk("partition_id=%llu vp_index=%u flags=%#x dispatch_state=%#x dispatch_event=%#x irq_vectors=%#016llx status=%#llx", + __entry->partition_id, + __entry->vp_index, + __entry->flags, + __entry->dispatch_state, + __entry->dispatch_event, + __entry->irq_vectors, + __entry->status + ) +); + +TRACE_EVENT(mshv_update_routing_table, + TP_PROTO(u64 partition_id, void *old, void *new, u32 numents), + TP_ARGS(partition_id, old, new, numents), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(struct mshv_girq_routing_table *, old) + __field(struct mshv_girq_routing_table *, new) + __field(u32, numents) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->old = old; + __entry->new = new; + __entry->numents = numents; + ), + TP_printk("partition_id=%llu old=%p new=%p numents=%u", + __entry->partition_id, + __entry->old, + __entry->new, + __entry->numents + ) +); + +TRACE_EVENT(mshv_map_user_memory, + TP_PROTO(u64 partition_id, u64 start_uaddr, u64 start_gfn, u64 nr_pages, u32 map_flags, + long ret), + TP_ARGS(partition_id, start_uaddr, start_gfn, nr_pages, map_flags, ret), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u64, start_uaddr) + __field(u64, start_gfn) + __field(u64, nr_pages) + __field(u32, map_flags) + __field(long, ret) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->start_uaddr = start_uaddr; + __entry->start_gfn = start_gfn; + __entry->nr_pages = nr_pages; + __entry->map_flags = map_flags; + __entry->ret = ret; + ), + TP_printk("partition_id=%llu start_uaddr=%#llx start_gfn=%#llx nr_pages=%llu map_flags=%#x ret=%ld", + __entry->partition_id, + __entry->start_uaddr, + __entry->start_gfn, + __entry->nr_pages, + __entry->map_flags, + __entry->ret + ) +); + +TRACE_EVENT(mshv_assign_ioeventfd, + TP_PROTO(u64 partition_id, u64 addr, u64 length, u64 datamatch, bool wildcard, + void *eventfd, int ret), + TP_ARGS(partition_id, addr, length, datamatch, wildcard, eventfd, ret), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u64, addr) + __field(u64, length) + __field(u64, datamatch) + __field(bool, wildcard) + __field(struct eventfd_ctx *, eventfd) + __field(int, ret) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->addr = addr; + __entry->length = length; + __entry->datamatch = datamatch; + __entry->wildcard = wildcard; + __entry->eventfd = eventfd; + __entry->ret = ret; + ), + TP_printk("partition_id=%llu addr=%#016llx length=%#llx datamatch=%#llx wildcard=%d eventfd=%p ret=%d", + __entry->partition_id, + __entry->addr, + __entry->length, + __entry->datamatch, + __entry->wildcard, + __entry->eventfd, + __entry->ret + ) +); + +TRACE_EVENT(mshv_deassign_ioeventfd, + TP_PROTO(u64 partition_id, u64 addr, u64 length, u64 datamatch, bool wildcard, + void *eventfd), + TP_ARGS(partition_id, addr, length, datamatch, wildcard, eventfd), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u64, addr) + __field(u64, length) + __field(u64, datamatch) + __field(bool, wildcard) + __field(struct eventfd_ctx *, eventfd) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->addr = addr; + __entry->length = length; + __entry->datamatch = datamatch; + __entry->wildcard = wildcard; + __entry->eventfd = eventfd; + ), + TP_printk("partition_id=%llu addr=%#016llx length=%#llx datamatch=%#llx wildcard=%d eventfd=%p", + __entry->partition_id, + __entry->addr, + __entry->length, + __entry->datamatch, + __entry->wildcard, + __entry->eventfd + ) +); + +TRACE_EVENT(mshv_vp_wait_for_hv_kick, + TP_PROTO(u64 partition_id, u32 vp_index, bool kicked_by_hv, bool blocked, + bool irq_pending), + TP_ARGS(partition_id, vp_index, kicked_by_hv, blocked, irq_pending), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u32, vp_index) + __field(bool, kicked_by_hv) + __field(bool, blocked) + __field(bool, irq_pending) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vp_index = vp_index; + __entry->kicked_by_hv = kicked_by_hv; + __entry->blocked = blocked; + __entry->irq_pending = irq_pending; + ), + TP_printk("partition_id=%llu vp_index=%u kicked_by_hv=%d blocked=%d irq_pending=%d", + __entry->partition_id, + __entry->vp_index, + __entry->kicked_by_hv, + __entry->blocked, + __entry->irq_pending + ) +); + +TRACE_EVENT(mshv_handle_gpa_intercept, + TP_PROTO(u64 partition_id, u32 vp_index, u64 gfn, u8 access_type, bool handled), + TP_ARGS(partition_id, vp_index, gfn, access_type, handled), + TP_STRUCT__entry( + __field(u64, partition_id) + __field(u32, vp_index) + __field(u64, gfn) + __field(u8, access_type) + __field(bool, handled) + ), + TP_fast_assign( + __entry->partition_id = partition_id; + __entry->vp_index = vp_index; + __entry->gfn = gfn; + __entry->access_type = access_type == HV_INTERCEPT_ACCESS_READ ? 'R' : + (access_type == HV_INTERCEPT_ACCESS_WRITE ? 'W' : + (access_type == HV_INTERCEPT_ACCESS_EXECUTE ? 'X' : '?')); + __entry->handled = handled; + ), + TP_printk("partition_id=%llu vp_index=%u gfn=0x%llx access_type=%c handled=%d", + __entry->partition_id, + __entry->vp_index, + __entry->gfn, + __entry->access_type, + __entry->handled + ) +); + +#endif /* _MSHV_TRACE_H_ */ + +/* This part must be outside protection */ +#include diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index 5856975f32e1..c19400701467 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -386,7 +386,6 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg) if (copy_from_user(&vtl0_mem, arg, sizeof(vtl0_mem))) return -EFAULT; - /* vtl0_mem.last_pfn is excluded in the pagemap range for VTL0 as per design */ if (vtl0_mem.last_pfn <= vtl0_mem.start_pfn) { dev_err(vtl->module_dev, "range start pfn (%llx) > end pfn (%llx)\n", vtl0_mem.start_pfn, vtl0_mem.last_pfn); @@ -397,6 +396,10 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg) if (!pgmap) return -ENOMEM; + /* + * vtl0_mem.last_pfn is excluded in the pagemap range for VTL0 as per design. + * last_pfn is not reserved or wasted, and reflects 'start_pfn + size' of pagemap range. + */ pgmap->ranges[0].start = PFN_PHYS(vtl0_mem.start_pfn); pgmap->ranges[0].end = PFN_PHYS(vtl0_mem.last_pfn) - 1; pgmap->nr_range = 1; @@ -405,8 +408,11 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg) /* * Determine the highest page order that can be used for the given memory range. * This works best when the range is aligned; i.e. both the start and the length. + * Clamp to MAX_FOLIO_ORDER to avoid a WARN in memremap_pages() when the range + * alignment exceeds the maximum supported folio order for this kernel config. */ - pgmap->vmemmap_shift = count_trailing_zeros(vtl0_mem.start_pfn | vtl0_mem.last_pfn); + pgmap->vmemmap_shift = min(count_trailing_zeros(vtl0_mem.start_pfn | vtl0_mem.last_pfn), + MAX_FOLIO_ORDER); dev_dbg(vtl->module_dev, "Add VTL0 memory: start: 0x%llx, end_pfn: 0x%llx, page order: %lu\n", vtl0_mem.start_pfn, vtl0_mem.last_pfn, pgmap->vmemmap_shift); @@ -415,7 +421,7 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg) if (IS_ERR(addr)) { dev_err(vtl->module_dev, "devm_memremap_pages error: %ld\n", PTR_ERR(addr)); kfree(pgmap); - return -EFAULT; + return PTR_ERR(addr); } /* Don't free pgmap, since it has to stick around until the memory diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index f0d0803d1e16..d28ff45d4cfd 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -100,13 +100,11 @@ struct device *hv_get_vmbus_root_device(void) } EXPORT_SYMBOL_GPL(hv_get_vmbus_root_device); -static int vmbus_exists(void) +bool hv_vmbus_exists(void) { - if (vmbus_root_device == NULL) - return -ENODEV; - - return 0; + return vmbus_root_device != NULL; } +EXPORT_SYMBOL_GPL(hv_vmbus_exists); static u8 channel_monitor_group(const struct vmbus_channel *channel) { @@ -1257,17 +1255,12 @@ static void vmbus_chan_sched(void *event_page_addr) return; event = (union hv_synic_event_flags *)event_page_addr + VMBUS_MESSAGE_SINT; - maxbits = HV_EVENT_FLAGS_COUNT; + maxbits = READ_ONCE(vmbus_connection.relid_hiwater) + 1; recv_int_page = event->flags; if (unlikely(!recv_int_page)) return; - /* - * Suggested-by: Michael Kelley - * One possible optimization would be to keep track of the largest relID that's in use, - * and only scan up to that relID. - */ for_each_set_bit(relid, recv_int_page, maxbits) { void (*callback_fn)(void *context); struct vmbus_channel *channel; @@ -1427,7 +1420,6 @@ static int vmbus_alloc_synic_and_connect(void) { int ret, cpu; struct work_struct __percpu *works; - int hyperv_cpuhp_online; ret = hv_synic_alloc(); if (ret < 0) @@ -1579,11 +1571,10 @@ int __vmbus_driver_register(struct hv_driver *hv_driver, struct module *owner, c { int ret; - pr_info("registering driver %s\n", hv_driver->name); + if (!hv_vmbus_exists()) + return -ENODEV; - ret = vmbus_exists(); - if (ret < 0) - return ret; + pr_info("registering driver %s\n", hv_driver->name); hv_driver->driver.name = hv_driver->name; hv_driver->driver.owner = owner; @@ -1609,9 +1600,8 @@ EXPORT_SYMBOL_GPL(__vmbus_driver_register); */ void vmbus_driver_unregister(struct hv_driver *hv_driver) { - pr_info("unregistering driver %s\n", hv_driver->name); - - if (!vmbus_exists()) { + if (hv_vmbus_exists()) { + pr_info("unregistering driver %s\n", hv_driver->name); driver_unregister(&hv_driver->driver); vmbus_free_dynids(hv_driver); } @@ -2897,7 +2887,6 @@ static struct platform_driver vmbus_platform_driver = { static void hv_kexec_handler(void) { - hv_stimer_global_cleanup(); vmbus_initiate_unload(false); /* Make sure conn_state is set as hv_synic_cleanup checks for it */ mb(); diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4788996aa137..982ee2c6f9de 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -201,7 +201,6 @@ obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o obj-$(CONFIG_SENSORS_QNAP_MCU_HWMON) += qnap-mcu-hwmon.o obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o obj-$(CONFIG_SENSORS_SBTSI) += sbtsi_temp.o -obj-$(CONFIG_SENSORS_SBRMI) += sbrmi.o obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index be7f702dcde9..0c9b9f4180fb 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -884,10 +884,14 @@ static void acpi_power_meter_notify(acpi_handle handle, u32 event, void *data) static int acpi_power_meter_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_power_meter_resource *resource; + struct acpi_device *device; int res; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + resource = kzalloc_obj(*resource); if (!resource) return -ENOMEM; diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c index 9bfdf9e6bcd7..9ee3ce01f130 100644 --- a/drivers/hwmon/ads7871.c +++ b/drivers/hwmon/ads7871.c @@ -77,9 +77,13 @@ static int ads7871_read_reg8(struct spi_device *spi, int reg) static int ads7871_read_reg16(struct spi_device *spi, int reg) { int ret; + reg = reg | INST_READ_BM | INST_16BIT_BM; ret = spi_w8r16(spi, reg); - return ret; + if (ret < 0) + return ret; + + return le16_to_cpu((__force __le16)ret); } static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val) diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index 5688ff5f7c28..109318b0434d 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -1273,15 +1273,20 @@ static int atk_probe(struct platform_device *pdev) struct acpi_buffer buf; union acpi_object *obj; struct atk_data *data; + acpi_handle handle; dev_dbg(&pdev->dev, "adding...\n"); + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->dev = &pdev->dev; - data->atk_handle = ACPI_HANDLE(&pdev->dev); + data->atk_handle = handle; INIT_LIST_HEAD(&data->sensor_list); data->disable_ec = false; diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index dddbd2463f8d..76f3e1da68d0 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -796,13 +796,13 @@ static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id ret = corsairpsu_init(priv); if (ret < 0) { dev_err(&hdev->dev, "unable to initialize device (%d)\n", ret); - goto fail_and_stop; + goto fail_and_close; } ret = corsairpsu_fwinfo(priv); if (ret < 0) { dev_err(&hdev->dev, "unable to query firmware (%d)\n", ret); - goto fail_and_stop; + goto fail_and_close; } corsairpsu_get_criticals(priv); diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 035176a98ce9..30500b4d2221 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -333,7 +333,13 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm63_data *data = lm63_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index])); + int fan; + + mutex_lock(&data->update_lock); + fan = FAN_FROM_REG(data->fan[attr->index]); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", fan); } static ssize_t set_fan(struct device *dev, struct device_attribute *dummy, @@ -366,12 +372,14 @@ static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr, int nr = attr->index; int pwm; + mutex_lock(&data->update_lock); if (data->pwm_highres) pwm = data->pwm1[nr]; else pwm = data->pwm1[nr] >= 2 * data->pwm1_freq ? 255 : (data->pwm1[nr] * 255 + data->pwm1_freq) / (2 * data->pwm1_freq); + mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", pwm); } @@ -529,6 +537,7 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, int nr = attr->index; int temp; + mutex_lock(&data->update_lock); if (!nr) { /* * Use unsigned temperature unless its value is zero. @@ -544,7 +553,10 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, else temp = TEMP11_FROM_REG(data->temp11[nr]); } - return sprintf(buf, "%d\n", temp + data->temp2_offset); + temp += data->temp2_offset; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", temp); } static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, @@ -592,9 +604,14 @@ static ssize_t temp2_crit_hyst_show(struct device *dev, struct device_attribute *dummy, char *buf) { struct lm63_data *data = lm63_update_device(dev); - return sprintf(buf, "%d\n", temp8_from_reg(data, 2) - + data->temp2_offset - - TEMP8_FROM_REG(data->temp2_crit_hyst)); + int temp; + + mutex_lock(&data->update_lock); + temp = temp8_from_reg(data, 2) + data->temp2_offset + - TEMP8_FROM_REG(data->temp2_crit_hyst); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", temp); } static ssize_t show_lut_temp_hyst(struct device *dev, @@ -602,10 +619,14 @@ static ssize_t show_lut_temp_hyst(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm63_data *data = lm63_update_device(dev); + int temp; - return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index) - + data->temp2_offset - - TEMP8_FROM_REG(data->lut_temp_hyst)); + mutex_lock(&data->update_lock); + temp = lut_temp_from_reg(data, attr->index) + data->temp2_offset + - TEMP8_FROM_REG(data->lut_temp_hyst); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", temp); } /* @@ -616,7 +637,7 @@ static ssize_t temp2_crit_hyst_store(struct device *dev, struct device_attribute *dummy, const char *buf, size_t count) { - struct lm63_data *data = dev_get_drvdata(dev); + struct lm63_data *data = lm63_update_device(dev); struct i2c_client *client = data->client; long val; int err; diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index f1a1e5b888f6..c283443e363b 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -137,7 +137,7 @@ static const struct lm75_params device_params[] = { }, [as6200] = { .config_reg_16bits = true, - .set_mask = 0x94C0, /* 8 sample/s, 4 CF, positive polarity */ + .set_mask = 0xC010, /* 8 sample/s, 4 CF */ .default_resolution = 12, .default_sample_time = 125, .num_sample_times = 4, @@ -286,8 +286,8 @@ static const struct lm75_params device_params[] = { }, [tmp112] = { .config_reg_16bits = true, - .set_mask = 0x60C0, /* 12-bit mode, 8 samples / second */ - .clr_mask = 1 << 15, /* no one-shot mode*/ + .set_mask = 0xC060, /* 12-bit mode, 8 samples / second */ + .clr_mask = 1 << 7, /* no one-shot mode*/ .default_resolution = 12, .default_sample_time = 125, .num_sample_times = 4, @@ -353,7 +353,7 @@ static inline int lm75_write_config(struct lm75_data *data, u16 set_mask, u16 clr_mask) { return regmap_update_bits(data->regmap, LM75_REG_CONF, - clr_mask | LM75_SHUTDOWN, set_mask); + clr_mask | set_mask | LM75_SHUTDOWN, set_mask); } static irqreturn_t lm75_alarm_handler(int irq, void *private) @@ -416,7 +416,7 @@ static int lm75_read(struct device *dev, enum hwmon_sensor_types type, switch (data->kind) { case as6200: case tmp112: - *val = (regval >> 13) & 0x1; + *val = !!(regval & BIT(13)) == !!(regval & BIT(2)); break; default: return -EINVAL; diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 3c10a5066b53..1eeb608e5903 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -736,6 +736,7 @@ struct lm90_data { struct hwmon_chip_info chip; struct delayed_work alert_work; struct work_struct report_work; + bool shutdown; /* true if shutting down */ bool valid; /* true if register values are valid */ bool alarms_valid; /* true if status register values are valid */ unsigned long last_updated; /* in jiffies */ @@ -1154,6 +1155,9 @@ static void lm90_report_alarms(struct work_struct *work) static int lm90_update_alarms_locked(struct lm90_data *data, bool force) { + if (data->shutdown) + return 0; + if (force || !data->alarms_valid || time_after(jiffies, data->alarms_updated + msecs_to_jiffies(data->update_interval))) { struct i2c_client *client = data->client; @@ -2584,15 +2588,23 @@ static void lm90_restore_conf(void *_data) struct lm90_data *data = _data; struct i2c_client *client = data->client; - cancel_delayed_work_sync(&data->alert_work); - cancel_work_sync(&data->report_work); - /* Restore initial configuration */ if (data->flags & LM90_HAVE_CONVRATE) lm90_write_convrate(data, data->convrate_orig); lm90_write_reg(client, LM90_REG_CONFIG1, data->config_orig); } +static void lm90_stop_work(void *_data) +{ + struct lm90_data *data = _data; + + hwmon_lock(data->hwmon_dev); + data->shutdown = true; + hwmon_unlock(data->hwmon_dev); + cancel_delayed_work_sync(&data->alert_work); + cancel_work_sync(&data->report_work); +} + static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) { struct device_node *np = client->dev.of_node; @@ -2902,6 +2914,10 @@ static int lm90_probe(struct i2c_client *client) data->hwmon_dev = hwmon_dev; + err = devm_add_action_or_reset(&client->dev, lm90_stop_work, data); + if (err) + return err; + if (client->irq) { dev_dbg(dev, "IRQ: %d\n", client->irq); err = devm_request_threaded_irq(dev, client->irq, @@ -2930,7 +2946,8 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, */ struct lm90_data *data = i2c_get_clientdata(client); - if ((data->flags & LM90_HAVE_BROKEN_ALERT) && + hwmon_lock(data->hwmon_dev); + if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && (data->current_alarms & data->alert_alarms)) { if (!(data->config & 0x80)) { dev_dbg(&client->dev, "Disabling ALERT#\n"); @@ -2939,6 +2956,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, schedule_delayed_work(&data->alert_work, max_t(int, HZ, msecs_to_jiffies(data->update_interval))); } + hwmon_unlock(data->hwmon_dev); } else { dev_dbg(&client->dev, "Everything OK\n"); } diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c index 1fcd320d6161..2617c4538af9 100644 --- a/drivers/hwmon/ltc2992.c +++ b/drivers/hwmon/ltc2992.c @@ -431,10 +431,16 @@ static int ltc2992_get_voltage(struct ltc2992_state *st, u32 reg, u32 scale, lon static int ltc2992_set_voltage(struct ltc2992_state *st, u32 reg, u32 scale, long val) { - val = DIV_ROUND_CLOSEST(val * 1000, scale); - val = val << 4; + u32 reg_val; + long vmax; - return ltc2992_write_reg(st, reg, 2, val); + vmax = DIV_ROUND_CLOSEST_ULL(0xFFFULL * scale, 1000); + val = max(val, 0L); + val = min(val, vmax); + reg_val = min(DIV_ROUND_CLOSEST_ULL((u64)val * 1000, scale), + 0xFFFULL) << 4; + + return ltc2992_write_reg(st, reg, 2, reg_val); } static int ltc2992_read_gpio_alarm(struct ltc2992_state *st, int nr_gpio, u32 attr, long *val) @@ -559,9 +565,15 @@ static int ltc2992_get_current(struct ltc2992_state *st, u32 reg, u32 channel, l static int ltc2992_set_current(struct ltc2992_state *st, u32 reg, u32 channel, long val) { u32 reg_val; + long cmax; - reg_val = DIV_ROUND_CLOSEST(val * st->r_sense_uohm[channel], LTC2992_IADC_NANOV_LSB); - reg_val = reg_val << 4; + cmax = DIV_ROUND_CLOSEST_ULL(0xFFFULL * LTC2992_IADC_NANOV_LSB, + st->r_sense_uohm[channel]); + val = max(val, 0L); + val = min(val, cmax); + reg_val = min(DIV_ROUND_CLOSEST_ULL((u64)val * st->r_sense_uohm[channel], + LTC2992_IADC_NANOV_LSB), + 0xFFFULL) << 4; return ltc2992_write_reg(st, reg, 2, reg_val); } @@ -625,8 +637,10 @@ static int ltc2992_get_power(struct ltc2992_state *st, u32 reg, u32 channel, lon if (reg_val < 0) return reg_val; - *val = mul_u64_u32_div(reg_val, LTC2992_VADC_UV_LSB * LTC2992_IADC_NANOV_LSB, - st->r_sense_uohm[channel] * 1000); + *val = mul_u64_u32_div(reg_val, + LTC2992_VADC_UV_LSB / 1000 * + LTC2992_IADC_NANOV_LSB, + st->r_sense_uohm[channel]); return 0; } @@ -634,9 +648,18 @@ static int ltc2992_get_power(struct ltc2992_state *st, u32 reg, u32 channel, lon static int ltc2992_set_power(struct ltc2992_state *st, u32 reg, u32 channel, long val) { u32 reg_val; + u64 pmax, uval; - reg_val = mul_u64_u32_div(val, st->r_sense_uohm[channel] * 1000, - LTC2992_VADC_UV_LSB * LTC2992_IADC_NANOV_LSB); + uval = max(val, 0L); + pmax = mul_u64_u32_div(0xFFFFFFULL, + LTC2992_VADC_UV_LSB / 1000 * + LTC2992_IADC_NANOV_LSB, + st->r_sense_uohm[channel]); + uval = min(uval, pmax); + reg_val = min(mul_u64_u32_div(uval, st->r_sense_uohm[channel], + LTC2992_VADC_UV_LSB / 1000 * + LTC2992_IADC_NANOV_LSB), + 0xFFFFFFULL); return ltc2992_write_reg(st, reg, 3, reg_val); } diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig index 3874d15b0e9b..d84e00084ee2 100644 --- a/drivers/hwspinlock/Kconfig +++ b/drivers/hwspinlock/Kconfig @@ -53,14 +53,4 @@ config HWSPINLOCK_SUN6I If unsure, say N. -config HSEM_U8500 - tristate "STE Hardware Semaphore functionality" - depends on ARCH_U8500 || COMPILE_TEST - help - Say y here to support the STE Hardware Semaphore functionality, which - provides a synchronisation mechanism for the various processor on the - SoC. - - If unsure, say N. - endif # HWSPINLOCK diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile index a0f16c9aaa82..3a740805949d 100644 --- a/drivers/hwspinlock/Makefile +++ b/drivers/hwspinlock/Makefile @@ -9,4 +9,3 @@ obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o obj-$(CONFIG_HWSPINLOCK_SPRD) += sprd_hwspinlock.o obj-$(CONFIG_HWSPINLOCK_STM32) += stm32_hwspinlock.o obj-$(CONFIG_HWSPINLOCK_SUN6I) += sun6i_hwspinlock.o -obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o diff --git a/drivers/hwspinlock/u8500_hsem.c b/drivers/hwspinlock/u8500_hsem.c deleted file mode 100644 index 5a2d8c3e0d80..000000000000 --- a/drivers/hwspinlock/u8500_hsem.c +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * u8500 HWSEM driver - * - * Copyright (C) 2010-2011 ST-Ericsson - * - * Implements u8500 semaphore handling for protocol 1, no interrupts. - * - * Author: Mathieu Poirier - * Heavily borrowed from the work of : - * Simon Que - * Hari Kanigeri - * Ohad Ben-Cohen - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "hwspinlock_internal.h" - -/* - * Implementation of STE's HSem protocol 1 without interrutps. - * The only masterID we allow is '0x01' to force people to use - * HSems for synchronisation between processors rather than processes - * on the ARM core. - */ - -#define U8500_MAX_SEMAPHORE 32 /* a total of 32 semaphore */ -#define RESET_SEMAPHORE (0) /* free */ - -/* - * CPU ID for master running u8500 kernel. - * Hswpinlocks should only be used to synchonise operations - * between the Cortex A9 core and the other CPUs. Hence - * forcing the masterID to a preset value. - */ -#define HSEM_MASTER_ID 0x01 - -#define HSEM_REGISTER_OFFSET 0x08 - -#define HSEM_CTRL_REG 0x00 -#define HSEM_ICRALL 0x90 -#define HSEM_PROTOCOL_1 0x01 - -static int u8500_hsem_trylock(struct hwspinlock *lock) -{ - void __iomem *lock_addr = lock->priv; - - writel(HSEM_MASTER_ID, lock_addr); - - /* get only first 4 bit and compare to masterID. - * if equal, we have the semaphore, otherwise - * someone else has it. - */ - return (HSEM_MASTER_ID == (0x0F & readl(lock_addr))); -} - -static void u8500_hsem_unlock(struct hwspinlock *lock) -{ - void __iomem *lock_addr = lock->priv; - - /* release the lock by writing 0 to it */ - writel(RESET_SEMAPHORE, lock_addr); -} - -/* - * u8500: what value is recommended here ? - */ -static void u8500_hsem_relax(struct hwspinlock *lock) -{ - ndelay(50); -} - -static const struct hwspinlock_ops u8500_hwspinlock_ops = { - .trylock = u8500_hsem_trylock, - .unlock = u8500_hsem_unlock, - .relax = u8500_hsem_relax, -}; - -static int u8500_hsem_probe(struct platform_device *pdev) -{ - struct hwspinlock_pdata *pdata = pdev->dev.platform_data; - struct hwspinlock_device *bank; - struct hwspinlock *hwlock; - void __iomem *io_base; - int i, num_locks = U8500_MAX_SEMAPHORE; - ulong val; - - if (!pdata) - return -ENODEV; - - io_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(io_base)) - return PTR_ERR(io_base); - - /* make sure protocol 1 is selected */ - val = readl(io_base + HSEM_CTRL_REG); - writel((val & ~HSEM_PROTOCOL_1), io_base + HSEM_CTRL_REG); - - /* clear all interrupts */ - writel(0xFFFF, io_base + HSEM_ICRALL); - - bank = devm_kzalloc(&pdev->dev, struct_size(bank, lock, num_locks), - GFP_KERNEL); - if (!bank) - return -ENOMEM; - - platform_set_drvdata(pdev, bank); - - for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++) - hwlock->priv = io_base + HSEM_REGISTER_OFFSET + sizeof(u32) * i; - - return devm_hwspin_lock_register(&pdev->dev, bank, - &u8500_hwspinlock_ops, - pdata->base_id, num_locks); -} - -static void u8500_hsem_remove(struct platform_device *pdev) -{ - struct hwspinlock_device *bank = platform_get_drvdata(pdev); - void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET; - - /* clear all interrupts */ - writel(0xFFFF, io_base + HSEM_ICRALL); -} - -static struct platform_driver u8500_hsem_driver = { - .probe = u8500_hsem_probe, - .remove = u8500_hsem_remove, - .driver = { - .name = "u8500_hsem", - }, -}; - -static int __init u8500_hsem_init(void) -{ - return platform_driver_register(&u8500_hsem_driver); -} -/* board init code might need to reserve hwspinlocks for predefined purposes */ -postcore_initcall(u8500_hsem_init); - -static void __exit u8500_hsem_exit(void) -{ - platform_driver_unregister(&u8500_hsem_driver); -} -module_exit(u8500_hsem_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Hardware Spinlock driver for u8500"); -MODULE_AUTHOR("Mathieu Poirier "); diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index dfd035852b12..ce71dcddfca2 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -30,8 +30,6 @@ #define catu_dbg(x, ...) do {} while (0) #endif -DEFINE_CORESIGHT_DEVLIST(catu_devs, "catu"); - struct catu_etr_buf { struct tmc_sg_table *catu_table; dma_addr_t sladdr; @@ -530,7 +528,7 @@ static int __catu_probe(struct device *dev, struct resource *res) if (ret) return ret; - catu_desc.name = coresight_alloc_device_name(&catu_devs, dev); + catu_desc.name = coresight_alloc_device_name("catu", dev); if (!catu_desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 80e26396ad0a..46f247f73cf6 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -53,6 +53,9 @@ struct coresight_node { const u32 coresight_barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff}; EXPORT_SYMBOL_GPL(coresight_barrier_pkt); +/* List maintains the device index */ +static LIST_HEAD(coresight_dev_idx_list); + static const struct cti_assoc_op *cti_assoc_ops; void coresight_set_cti_ops(const struct cti_assoc_op *cti_op) @@ -1149,7 +1152,6 @@ static int coresight_clear_filter_source(struct device *dev, void *data) return 0; } -/* coresight_remove_conns - Remove other device's references to this device */ static void coresight_remove_conns(struct coresight_device *csdev) { int i, j; @@ -1159,10 +1161,6 @@ static void coresight_remove_conns(struct coresight_device *csdev) bus_for_each_dev(&coresight_bustype, NULL, csdev, coresight_clear_filter_source); - /* - * Remove the input connection references from the destination device - * for each output connection. - */ for (i = 0; i < csdev->pdata->nr_outconns; i++) { conn = csdev->pdata->out_conns[i]; if (conn->filter_src_fwnode) { @@ -1173,6 +1171,13 @@ static void coresight_remove_conns(struct coresight_device *csdev) if (!conn->dest_dev) continue; + /* Remove sysfs links for the output connection */ + coresight_remove_links(csdev, conn); + + /* + * Remove the input connection references from the destination + * device for each output connection. + */ for (j = 0; j < conn->dest_dev->pdata->nr_inconns; ++j) if (conn->dest_dev->pdata->in_conns[j] == conn) { conn->dest_dev->pdata->in_conns[j] = NULL; @@ -1195,6 +1200,8 @@ static void coresight_remove_conns(struct coresight_device *csdev) coresight_remove_links(conn->src_dev, conn); conn->dest_dev = NULL; } + + coresight_remove_conns_sysfs_group(csdev); } /** @@ -1295,17 +1302,13 @@ void coresight_write64(struct coresight_device *csdev, u64 val, u32 offset) * coresight_release_platform_data: Release references to the devices connected * to the output port of this device. */ -void coresight_release_platform_data(struct coresight_device *csdev, - struct device *dev, +void coresight_release_platform_data(struct device *dev, struct coresight_platform_data *pdata) { int i; struct coresight_connection **conns = pdata->out_conns; for (i = 0; i < pdata->nr_outconns; i++) { - /* If we have made the links, remove them now */ - if (csdev && conns[i]->dest_dev) - coresight_remove_links(csdev, conns[i]); /* * Drop the refcount and clear the handle as this device * is going away @@ -1317,8 +1320,6 @@ void coresight_release_platform_data(struct coresight_device *csdev, devm_kfree(dev, pdata->out_conns); devm_kfree(dev, pdata->in_conns); devm_kfree(dev, pdata); - if (csdev) - coresight_remove_conns_sysfs_group(csdev); } struct coresight_device *coresight_register(struct coresight_desc *desc) @@ -1346,12 +1347,6 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) csdev->dev.parent = desc->dev; csdev->dev.release = coresight_device_release; csdev->dev.bus = &coresight_bustype; - /* - * Hold the reference to our parent device. This will be - * dropped only in coresight_device_release(). - */ - csdev->dev.fwnode = fwnode_handle_get(dev_fwnode(desc->dev)); - dev_set_name(&csdev->dev, "%s", desc->name); if (csdev->type == CORESIGHT_DEV_TYPE_SINK || csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) { @@ -1363,6 +1358,14 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) goto err_out; } } + + /* + * Hold the reference to our parent device. This will be + * dropped only in coresight_device_release(). + */ + csdev->dev.fwnode = fwnode_handle_get(dev_fwnode(desc->dev)); + dev_set_name(&csdev->dev, "%s", desc->name); + /* * Make sure the device registration and the connection fixup * are synchronised, so that we don't see uninitialised devices @@ -1380,37 +1383,30 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) goto out_unlock; } - if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || - csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && - sink_ops(csdev)->alloc_buffer) { - ret = etm_perf_add_symlink_sink(csdev); - - if (ret) { - device_unregister(&csdev->dev); - /* - * As with the above, all resources are free'd - * explicitly via coresight_device_release() triggered - * from put_device(), which is in turn called from - * function device_unregister(). - */ - goto out_unlock; - } - } /* Device is now registered */ registered = true; + ret = etm_perf_add_symlink_sink(csdev); + if (ret && ret != -EOPNOTSUPP) + goto out_unlock; + ret = coresight_create_conns_sysfs_group(csdev); - if (!ret) - ret = coresight_fixup_orphan_conns(csdev); + if (ret) + goto out_unlock; + + ret = coresight_fixup_orphan_conns(csdev); + if (ret) + goto out_unlock; + + mutex_unlock(&coresight_mutex); + + if (cti_assoc_ops && cti_assoc_ops->add) + cti_assoc_ops->add(csdev); + + return csdev; out_unlock: mutex_unlock(&coresight_mutex); - /* Success */ - if (!ret) { - if (cti_assoc_ops && cti_assoc_ops->add) - cti_assoc_ops->add(csdev); - return csdev; - } /* Unregister the device if needed */ if (registered) { @@ -1419,41 +1415,76 @@ out_unlock: } err_out: - /* Cleanup the connection information */ - coresight_release_platform_data(NULL, desc->dev, desc->pdata); + coresight_release_platform_data(desc->dev, desc->pdata); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(coresight_register); void coresight_unregister(struct coresight_device *csdev) { - etm_perf_del_symlink_sink(csdev); /* Remove references of that device in the topology */ if (cti_assoc_ops && cti_assoc_ops->remove) cti_assoc_ops->remove(csdev); + + mutex_lock(&coresight_mutex); + etm_perf_del_symlink_sink(csdev); coresight_remove_conns(csdev); coresight_clear_default_sink(csdev); - coresight_release_platform_data(csdev, csdev->dev.parent, csdev->pdata); + coresight_release_platform_data(csdev->dev.parent, csdev->pdata); device_unregister(&csdev->dev); + mutex_unlock(&coresight_mutex); } EXPORT_SYMBOL_GPL(coresight_unregister); - -/* - * coresight_search_device_idx - Search the fwnode handle of a device - * in the given dev_idx list. Must be called with the coresight_mutex held. - * - * Returns the index of the entry, when found. Otherwise, -ENOENT. - */ -static int coresight_search_device_idx(struct coresight_dev_list *dict, - struct fwnode_handle *fwnode) +static struct coresight_dev_list * +coresight_allocate_device_list(const char *prefix) { - int i; + struct coresight_dev_list *list; - for (i = 0; i < dict->nr_idx; i++) - if (dict->fwnode_list[i] == fwnode) - return i; - return -ENOENT; + /* Check if have already allocated */ + list_for_each_entry(list, &coresight_dev_idx_list, node) { + if (!strcmp(list->pfx, prefix)) + return list; + } + + list = kzalloc(sizeof(*list), GFP_KERNEL); + if (!list) + return NULL; + + list->pfx = kstrdup(prefix, GFP_KERNEL); + if (!list->pfx) { + kfree(list); + return NULL; + } + + list_add(&list->node, &coresight_dev_idx_list); + return list; +} + +static int coresight_allocate_device_idx(struct coresight_dev_list *list, + struct device *dev) +{ + struct fwnode_handle **fwnode_list; + struct fwnode_handle *fwnode = dev_fwnode(dev); + int idx; + + for (idx = 0; idx < list->nr_idx; idx++) + if (list->fwnode_list[idx] == fwnode) + return idx; + + /* Make space for the new entry */ + idx = list->nr_idx; + fwnode_list = krealloc_array(list->fwnode_list, + idx + 1, sizeof(*list->fwnode_list), + GFP_KERNEL); + if (!fwnode_list) + return -ENOMEM; + + fwnode_list[idx] = fwnode; + list->fwnode_list = fwnode_list; + list->nr_idx = idx + 1; + + return idx; } static bool coresight_compare_type(enum coresight_dev_type type_a, @@ -1527,45 +1558,63 @@ bool coresight_loses_context_with_cpu(struct device *dev) EXPORT_SYMBOL_GPL(coresight_loses_context_with_cpu); /* - * coresight_alloc_device_name - Get an index for a given device in the - * device index list specific to a driver. An index is allocated for a - * device and is tracked with the fwnode_handle to prevent allocating + * coresight_alloc_device_name - Get an index for a given device in the list + * specific to a driver (presented by the prefix string). An index is allocated + * for a device and is tracked with the fwnode_handle to prevent allocating * duplicate indices for the same device (e.g, if we defer probing of * a device due to dependencies), in case the index is requested again. */ -char *coresight_alloc_device_name(struct coresight_dev_list *dict, - struct device *dev) +char *coresight_alloc_device_name(const char *prefix, struct device *dev) { - int idx; + struct coresight_dev_list *list; char *name = NULL; - struct fwnode_handle **list; + int idx; mutex_lock(&coresight_mutex); - idx = coresight_search_device_idx(dict, dev_fwnode(dev)); - if (idx < 0) { - /* Make space for the new entry */ - idx = dict->nr_idx; - list = krealloc_array(dict->fwnode_list, - idx + 1, sizeof(*dict->fwnode_list), - GFP_KERNEL); - if (ZERO_OR_NULL_PTR(list)) { - idx = -ENOMEM; - goto done; - } + list = coresight_allocate_device_list(prefix); + if (!list) + goto done; - list[idx] = dev_fwnode(dev); - dict->fwnode_list = list; - dict->nr_idx = idx + 1; - } + idx = coresight_allocate_device_idx(list, dev); - name = devm_kasprintf(dev, GFP_KERNEL, "%s%d", dict->pfx, idx); + /* + * If index allocation fails, the device list is not released here; + * it is instead freed later by coresight_release_device_list() when + * the coresight_core module is unloaded. + */ + if (idx < 0) + goto done; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s%d", list->pfx, idx); done: mutex_unlock(&coresight_mutex); return name; } EXPORT_SYMBOL_GPL(coresight_alloc_device_name); +static void coresight_release_device_list(void) +{ + struct coresight_dev_list *list, *next; + int i; + + /* + * Here is no need to take coresight_mutex; this is during core module + * unloading, no race condition with other modules. + */ + + list_for_each_entry_safe(list, next, &coresight_dev_idx_list, node) { + for (i = 0; i < list->nr_idx; i++) + list->fwnode_list[i] = NULL; + list->nr_idx = 0; + list_del(&list->node); + + kfree(list->pfx); + kfree(list->fwnode_list); + kfree(list); + } +} + const struct bus_type coresight_bustype = { .name = "coresight", }; @@ -1639,6 +1688,7 @@ static void __exit coresight_exit(void) &coresight_notifier); etm_perf_exit(); bus_unregister(&coresight_bustype); + coresight_release_device_list(); } module_init(coresight_init); diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c index abed15eb72b4..9043cad42f01 100644 --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c @@ -19,8 +19,6 @@ #include "coresight-ctcu.h" #include "coresight-priv.h" -DEFINE_CORESIGHT_DEVLIST(ctcu_devs, "ctcu"); - #define ctcu_writel(drvdata, val, offset) __raw_writel((val), drvdata->base + offset) #define ctcu_readl(drvdata, offset) __raw_readl(drvdata->base + offset) @@ -187,7 +185,7 @@ static int ctcu_probe(struct platform_device *pdev) void __iomem *base; int i, ret; - desc.name = coresight_alloc_device_name(&ctcu_devs, dev); + desc.name = coresight_alloc_device_name("ctcu", dev); if (!desc.name) return -ENOMEM; @@ -228,6 +226,7 @@ static int ctcu_probe(struct platform_device *pdev) desc.dev = dev; desc.ops = &ctcu_ops; desc.access = CSDEV_ACCESS_IOMEM(base); + raw_spin_lock_init(&drvdata->spin_lock); drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index bfbc365bb2ef..2f4c9362709a 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -42,21 +42,6 @@ static DEFINE_MUTEX(ect_mutex); #define csdev_to_cti_drvdata(csdev) \ dev_get_drvdata(csdev->dev.parent) -/* power management handling */ -static int nr_cti_cpu; - -/* quick lookup list for CPU bound CTIs when power handling */ -static struct cti_drvdata *cti_cpu_drvdata[NR_CPUS]; - -/* - * CTI naming. CTI bound to cores will have the name cti_cpu where - * N is the CPU ID. System CTIs will have the name cti_sys where I - * is an index allocated by order of discovery. - * - * CTI device name list - for CTI not bound to cores. - */ -DEFINE_CORESIGHT_DEVLIST(cti_sys_devs, "cti_sys"); - /* write set of regs to hardware - call with spinlock claimed */ void cti_write_all_hw_regs(struct cti_drvdata *drvdata) { @@ -77,7 +62,8 @@ void cti_write_all_hw_regs(struct cti_drvdata *drvdata) /* other regs */ writel_relaxed(config->ctigate, drvdata->base + CTIGATE); - writel_relaxed(config->asicctl, drvdata->base + ASICCTL); + if (config->asicctl_impl) + writel_relaxed(config->asicctl, drvdata->base + ASICCTL); writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET); /* re-enable CTI */ @@ -90,60 +76,24 @@ void cti_write_all_hw_regs(struct cti_drvdata *drvdata) static int cti_enable_hw(struct cti_drvdata *drvdata) { struct cti_config *config = &drvdata->config; - unsigned long flags; - int rc = 0; + int rc; - raw_spin_lock_irqsave(&drvdata->spinlock, flags); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); - /* no need to do anything if enabled or unpowered*/ - if (config->hw_enabled || !config->hw_powered) + /* no need to do anything if enabled */ + if (cti_is_active(config)) goto cti_state_unchanged; /* claim the device */ rc = coresight_claim_device(drvdata->csdev); if (rc) - goto cti_err_not_enabled; + return rc; cti_write_all_hw_regs(drvdata); - config->hw_enabled = true; - drvdata->config.enable_req_count++; - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); - return rc; - cti_state_unchanged: drvdata->config.enable_req_count++; - - /* cannot enable due to error */ -cti_err_not_enabled: - raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); - return rc; -} - -/* re-enable CTI on CPU when using CPU hotplug */ -static void cti_cpuhp_enable_hw(struct cti_drvdata *drvdata) -{ - struct cti_config *config = &drvdata->config; - - raw_spin_lock(&drvdata->spinlock); - config->hw_powered = true; - - /* no need to do anything if no enable request */ - if (!drvdata->config.enable_req_count) - goto cti_hp_not_enabled; - - /* try to claim the device */ - if (coresight_claim_device(drvdata->csdev)) - goto cti_hp_not_enabled; - - cti_write_all_hw_regs(drvdata); - config->hw_enabled = true; - raw_spin_unlock(&drvdata->spinlock); - return; - - /* did not re-enable due to no claim / no request */ -cti_hp_not_enabled: - raw_spin_unlock(&drvdata->spinlock); + return 0; } /* disable hardware */ @@ -151,39 +101,36 @@ static int cti_disable_hw(struct cti_drvdata *drvdata) { struct cti_config *config = &drvdata->config; struct coresight_device *csdev = drvdata->csdev; - int ret = 0; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* don't allow negative refcounts, return an error */ - if (!drvdata->config.enable_req_count) { - ret = -EINVAL; - goto cti_not_disabled; - } + if (!cti_is_active(config)) + return -EINVAL; /* check refcount - disable on 0 */ if (--drvdata->config.enable_req_count > 0) - goto cti_not_disabled; - - /* no need to do anything if disabled or cpu unpowered */ - if (!config->hw_enabled || !config->hw_powered) - goto cti_not_disabled; + return 0; CS_UNLOCK(drvdata->base); /* disable CTI */ writel_relaxed(0, drvdata->base + CTICONTROL); - config->hw_enabled = false; coresight_disclaim_device_unlocked(csdev); CS_LOCK(drvdata->base); - raw_spin_unlock(&drvdata->spinlock); - return ret; + return 0; +} - /* not disabled this call */ -cti_not_disabled: - raw_spin_unlock(&drvdata->spinlock); - return ret; +u32 cti_read_single_reg(struct cti_drvdata *drvdata, int offset) +{ + int val; + + CS_UNLOCK(drvdata->base); + val = readl_relaxed(drvdata->base + offset); + CS_LOCK(drvdata->base); + + return val; } void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) @@ -198,11 +145,11 @@ void cti_write_intack(struct device *dev, u32 ackval) struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cti_config *config = &drvdata->config; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + /* write if enabled */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, CTIINTACK, ackval); - raw_spin_unlock(&drvdata->spinlock); } /* @@ -240,6 +187,8 @@ static void cti_set_default_config(struct device *dev, config->trig_filter_enable = true; config->ctigate = GENMASK(config->nr_ctm_channels - 1, 0); config->enable_req_count = 0; + + config->asicctl_impl = !!FIELD_GET(GENMASK(4, 0), devid); } /* @@ -369,7 +318,7 @@ int cti_channel_trig_op(struct device *dev, enum cti_chan_op op, reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) : CTIOUTEN(trigger_idx)); - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* read - modify write - the trigger / channel enable value */ reg_value = direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] : @@ -386,9 +335,9 @@ int cti_channel_trig_op(struct device *dev, enum cti_chan_op op, config->ctiouten[trigger_idx] = reg_value; /* write through if enabled */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, reg_offset, reg_value); - raw_spin_unlock(&drvdata->spinlock); + return 0; } @@ -406,7 +355,8 @@ int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op, chan_bitmask = BIT(channel_idx); - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + reg_value = config->ctigate; switch (op) { case CTI_GATE_CHAN_ENABLE: @@ -423,10 +373,10 @@ int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op, } if (err == 0) { config->ctigate = reg_value; - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, CTIGATE, reg_value); } - raw_spin_unlock(&drvdata->spinlock); + return err; } @@ -445,7 +395,8 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op, chan_bitmask = BIT(channel_idx); - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + reg_value = config->ctiappset; switch (op) { case CTI_CHAN_SET: @@ -471,9 +422,8 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op, break; } - if ((err == 0) && cti_active(config)) + if ((err == 0) && cti_is_active(config)) cti_write_single_reg(drvdata, reg_offset, reg_value); - raw_spin_unlock(&drvdata->spinlock); return err; } @@ -658,146 +608,6 @@ static void cti_remove_conn_xrefs(struct cti_drvdata *drvdata) } } -/** cti PM callbacks **/ -static int cti_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, - void *v) -{ - struct cti_drvdata *drvdata; - struct coresight_device *csdev; - unsigned int cpu = smp_processor_id(); - int notify_res = NOTIFY_OK; - - if (!cti_cpu_drvdata[cpu]) - return NOTIFY_OK; - - drvdata = cti_cpu_drvdata[cpu]; - csdev = drvdata->csdev; - - if (WARN_ON_ONCE(drvdata->ctidev.cpu != cpu)) - return NOTIFY_BAD; - - raw_spin_lock(&drvdata->spinlock); - - switch (cmd) { - case CPU_PM_ENTER: - /* CTI regs all static - we have a copy & nothing to save */ - drvdata->config.hw_powered = false; - if (drvdata->config.hw_enabled) - coresight_disclaim_device(csdev); - break; - - case CPU_PM_ENTER_FAILED: - drvdata->config.hw_powered = true; - if (drvdata->config.hw_enabled) { - if (coresight_claim_device(csdev)) - drvdata->config.hw_enabled = false; - } - break; - - case CPU_PM_EXIT: - /* write hardware registers to re-enable. */ - drvdata->config.hw_powered = true; - drvdata->config.hw_enabled = false; - - /* check enable reference count to enable HW */ - if (drvdata->config.enable_req_count) { - /* check we can claim the device as we re-power */ - if (coresight_claim_device(csdev)) - goto cti_notify_exit; - - drvdata->config.hw_enabled = true; - cti_write_all_hw_regs(drvdata); - } - break; - - default: - notify_res = NOTIFY_DONE; - break; - } - -cti_notify_exit: - raw_spin_unlock(&drvdata->spinlock); - return notify_res; -} - -static struct notifier_block cti_cpu_pm_nb = { - .notifier_call = cti_cpu_pm_notify, -}; - -/* CPU HP handlers */ -static int cti_starting_cpu(unsigned int cpu) -{ - struct cti_drvdata *drvdata = cti_cpu_drvdata[cpu]; - - if (!drvdata) - return 0; - - cti_cpuhp_enable_hw(drvdata); - return 0; -} - -static int cti_dying_cpu(unsigned int cpu) -{ - struct cti_drvdata *drvdata = cti_cpu_drvdata[cpu]; - - if (!drvdata) - return 0; - - raw_spin_lock(&drvdata->spinlock); - drvdata->config.hw_powered = false; - if (drvdata->config.hw_enabled) - coresight_disclaim_device(drvdata->csdev); - raw_spin_unlock(&drvdata->spinlock); - return 0; -} - -static int cti_pm_setup(struct cti_drvdata *drvdata) -{ - int ret; - - if (drvdata->ctidev.cpu == -1) - return 0; - - if (nr_cti_cpu) - goto done; - - cpus_read_lock(); - ret = cpuhp_setup_state_nocalls_cpuslocked( - CPUHP_AP_ARM_CORESIGHT_CTI_STARTING, - "arm/coresight_cti:starting", - cti_starting_cpu, cti_dying_cpu); - if (ret) { - cpus_read_unlock(); - return ret; - } - - ret = cpu_pm_register_notifier(&cti_cpu_pm_nb); - cpus_read_unlock(); - if (ret) { - cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_CTI_STARTING); - return ret; - } - -done: - nr_cti_cpu++; - cti_cpu_drvdata[drvdata->ctidev.cpu] = drvdata; - - return 0; -} - -/* release PM registrations */ -static void cti_pm_release(struct cti_drvdata *drvdata) -{ - if (drvdata->ctidev.cpu == -1) - return; - - cti_cpu_drvdata[drvdata->ctidev.cpu] = NULL; - if (--nr_cti_cpu == 0) { - cpu_pm_unregister_notifier(&cti_cpu_pm_nb); - cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_CTI_STARTING); - } -} - /** cti ect operations **/ int cti_enable(struct coresight_device *csdev, enum cs_mode mode, struct coresight_path *path) @@ -823,17 +633,13 @@ static const struct coresight_ops cti_ops = { .helper_ops = &cti_ops_ect, }; -/* - * Free up CTI specific resources - * called by dev->release, need to call down to underlying csdev release. - */ -static void cti_device_release(struct device *dev) +static void cti_remove(struct amba_device *adev) { - struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_drvdata *drvdata = dev_get_drvdata(&adev->dev); struct cti_drvdata *ect_item, *ect_tmp; mutex_lock(&ect_mutex); - cti_pm_release(drvdata); + cti_remove_conn_xrefs(drvdata); /* remove from the list */ list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, node) { @@ -844,17 +650,6 @@ static void cti_device_release(struct device *dev) } mutex_unlock(&ect_mutex); - if (drvdata->csdev_release) - drvdata->csdev_release(dev); -} -static void cti_remove(struct amba_device *adev) -{ - struct cti_drvdata *drvdata = dev_get_drvdata(&adev->dev); - - mutex_lock(&ect_mutex); - cti_remove_conn_xrefs(drvdata); - mutex_unlock(&ect_mutex); - coresight_unregister(drvdata->csdev); } @@ -900,29 +695,27 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) return PTR_ERR(pdata); } - /* default to powered - could change on PM notifications */ - drvdata->config.hw_powered = true; - - /* set up device name - will depend if cpu bound or otherwise */ + /* + * Set up device name - will depend if cpu bound or otherwise. + * + * CTI bound to cores will have the name cti_cpu where N is th + * eCPU ID. System CTIs will have the name cti_sys where I is an + * index allocated by order of discovery. + */ if (drvdata->ctidev.cpu >= 0) cti_desc.name = devm_kasprintf(dev, GFP_KERNEL, "cti_cpu%d", drvdata->ctidev.cpu); else - cti_desc.name = coresight_alloc_device_name(&cti_sys_devs, dev); + cti_desc.name = coresight_alloc_device_name("cti_sys", dev); if (!cti_desc.name) return -ENOMEM; - /* setup CPU power management handling for CPU bound CTI devices. */ - ret = cti_pm_setup(drvdata); - if (ret) - return ret; - /* create dynamic attributes for connections */ ret = cti_create_cons_sysfs(dev, drvdata); if (ret) { dev_err(dev, "%s: create dynamic sysfs entries failed\n", cti_desc.name); - goto pm_release; + return ret; } /* set up coresight component description */ @@ -935,10 +728,8 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) coresight_clear_self_claim_tag(&cti_desc.access); drvdata->csdev = coresight_register(&cti_desc); - if (IS_ERR(drvdata->csdev)) { - ret = PTR_ERR(drvdata->csdev); - goto pm_release; - } + if (IS_ERR(drvdata->csdev)) + return PTR_ERR(drvdata->csdev); /* add to list of CTI devices */ mutex_lock(&ect_mutex); @@ -947,18 +738,10 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) cti_update_conn_xrefs(drvdata); mutex_unlock(&ect_mutex); - /* set up release chain */ - drvdata->csdev_release = drvdata->csdev->dev.release; - drvdata->csdev->dev.release = cti_device_release; - /* all done - dec pm refcount */ pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "CTI initialized\n"); return 0; - -pm_release: - cti_pm_release(drvdata); - return ret; } static struct amba_cs_uci_id uci_id_cti[] = { diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 26ec0d8ed181..3fe2c916d228 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -81,19 +81,12 @@ static ssize_t enable_show(struct device *dev, char *buf) { int enable_req; - bool enabled, powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - raw_spin_lock(&drvdata->spinlock); - enable_req = drvdata->config.enable_req_count; - powered = drvdata->config.hw_powered; - enabled = drvdata->config.hw_enabled; - raw_spin_unlock(&drvdata->spinlock); + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) + enable_req = cti_is_active(&drvdata->config); - if (powered) - return sprintf(buf, "%d\n", enabled); - else - return sprintf(buf, "%d\n", !!enable_req); + return sprintf(buf, "%d\n", !!enable_req); } static ssize_t enable_store(struct device *dev, @@ -131,12 +124,7 @@ static ssize_t powered_show(struct device *dev, struct device_attribute *attr, char *buf) { - bool powered; - struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - - raw_spin_lock(&drvdata->spinlock); - powered = drvdata->config.hw_powered; - raw_spin_unlock(&drvdata->spinlock); + bool powered = pm_runtime_active(dev->parent); return sprintf(buf, "%d\n", powered); } @@ -181,10 +169,10 @@ static ssize_t coresight_cti_reg_show(struct device *dev, u32 val = 0; pm_runtime_get_sync(dev->parent); - raw_spin_lock(&drvdata->spinlock); - if (drvdata->config.hw_powered) - val = readl_relaxed(drvdata->base + cti_attr->off); - raw_spin_unlock(&drvdata->spinlock); + + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) + val = cti_read_single_reg(drvdata, cti_attr->off); + pm_runtime_put_sync(dev->parent); return sysfs_emit(buf, "0x%x\n", val); } @@ -202,10 +190,10 @@ static __maybe_unused ssize_t coresight_cti_reg_store(struct device *dev, return -EINVAL; pm_runtime_get_sync(dev->parent); - raw_spin_lock(&drvdata->spinlock); - if (drvdata->config.hw_powered) + + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) cti_write_single_reg(drvdata, cti_attr->off, val); - raw_spin_unlock(&drvdata->spinlock); + pm_runtime_put_sync(dev->parent); return size; } @@ -264,17 +252,19 @@ static ssize_t cti_reg32_show(struct device *dev, char *buf, struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cti_config *config = &drvdata->config; - raw_spin_lock(&drvdata->spinlock); - if ((reg_offset >= 0) && cti_active(config)) { - CS_UNLOCK(drvdata->base); - val = readl_relaxed(drvdata->base + reg_offset); - if (pcached_val) - *pcached_val = val; - CS_LOCK(drvdata->base); - } else if (pcached_val) { - val = *pcached_val; + if (reg_offset < 0) + return -EINVAL; + + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + if (cti_is_active(config)) { + val = cti_read_single_reg(drvdata, reg_offset); + if (pcached_val) + *pcached_val = val; + } else if (pcached_val) { + val = *pcached_val; + } } - raw_spin_unlock(&drvdata->spinlock); + return sprintf(buf, "%#x\n", val); } @@ -293,15 +283,19 @@ static ssize_t cti_reg32_store(struct device *dev, const char *buf, if (kstrtoul(buf, 0, &val)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); - /* local store */ - if (pcached_val) - *pcached_val = (u32)val; + if (reg_offset < 0) + return -EINVAL; + + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + /* local store */ + if (pcached_val) + *pcached_val = (u32)val; + + /* write through if offset and enabled */ + if (cti_is_active(config)) + cti_write_single_reg(drvdata, reg_offset, val); + } - /* write through if offset and enabled */ - if ((reg_offset >= 0) && cti_active(config)) - cti_write_single_reg(drvdata, reg_offset, val); - raw_spin_unlock(&drvdata->spinlock); return size; } @@ -343,15 +337,16 @@ static ssize_t inout_sel_store(struct device *dev, { unsigned long val; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; if (kstrtoul(buf, 0, &val)) return -EINVAL; - if (val > (CTIINOUTEN_MAX - 1)) + if (val >= config->nr_trig_max) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + drvdata->config.ctiinout_sel = val; - raw_spin_unlock(&drvdata->spinlock); return size; } static DEVICE_ATTR_RW(inout_sel); @@ -364,10 +359,11 @@ static ssize_t inen_show(struct device *dev, int index; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - raw_spin_lock(&drvdata->spinlock); - index = drvdata->config.ctiinout_sel; - val = drvdata->config.ctiinen[index]; - raw_spin_unlock(&drvdata->spinlock); + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + index = drvdata->config.ctiinout_sel; + val = drvdata->config.ctiinen[index]; + } + return sprintf(buf, "%#lx\n", val); } @@ -383,14 +379,15 @@ static ssize_t inen_store(struct device *dev, if (kstrtoul(buf, 0, &val)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + index = config->ctiinout_sel; config->ctiinen[index] = val; /* write through if enabled */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, CTIINEN(index), val); - raw_spin_unlock(&drvdata->spinlock); + return size; } static DEVICE_ATTR_RW(inen); @@ -403,10 +400,11 @@ static ssize_t outen_show(struct device *dev, int index; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - raw_spin_lock(&drvdata->spinlock); - index = drvdata->config.ctiinout_sel; - val = drvdata->config.ctiouten[index]; - raw_spin_unlock(&drvdata->spinlock); + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + index = drvdata->config.ctiinout_sel; + val = drvdata->config.ctiouten[index]; + } + return sprintf(buf, "%#lx\n", val); } @@ -422,14 +420,15 @@ static ssize_t outen_store(struct device *dev, if (kstrtoul(buf, 0, &val)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + index = config->ctiinout_sel; config->ctiouten[index] = val; /* write through if enabled */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, CTIOUTEN(index), val); - raw_spin_unlock(&drvdata->spinlock); + return size; } static DEVICE_ATTR_RW(outen); @@ -463,15 +462,15 @@ static ssize_t appclear_store(struct device *dev, if (kstrtoul(buf, 0, &val)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* a 1'b1 in appclr clears down the same bit in appset*/ config->ctiappset &= ~val; /* write through if enabled */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, CTIAPPCLEAR, val); - raw_spin_unlock(&drvdata->spinlock); + return size; } static DEVICE_ATTR_WO(appclear); @@ -487,12 +486,12 @@ static ssize_t apppulse_store(struct device *dev, if (kstrtoul(buf, 0, &val)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* write through if enabled */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_single_reg(drvdata, CTIAPPPULSE, val); - raw_spin_unlock(&drvdata->spinlock); + return size; } static DEVICE_ATTR_WO(apppulse); @@ -530,6 +529,18 @@ static struct attribute *coresight_cti_regs_attrs[] = { NULL, }; +static umode_t coresight_cti_regs_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (attr == &dev_attr_asicctl.attr && !drvdata->config.asicctl_impl) + return 0; + + return attr->mode; +} + /* CTI channel x-trigger programming */ static int cti_trig_op_parse(struct device *dev, enum cti_chan_op op, @@ -678,9 +689,9 @@ static ssize_t trig_filter_enable_show(struct device *dev, u32 val; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - raw_spin_lock(&drvdata->spinlock); - val = drvdata->config.trig_filter_enable; - raw_spin_unlock(&drvdata->spinlock); + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) + val = drvdata->config.trig_filter_enable; + return sprintf(buf, "%d\n", val); } @@ -694,9 +705,9 @@ static ssize_t trig_filter_enable_store(struct device *dev, if (kstrtoul(buf, 0, &val)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + drvdata->config.trig_filter_enable = !!val; - raw_spin_unlock(&drvdata->spinlock); return size; } static DEVICE_ATTR_RW(trig_filter_enable); @@ -726,7 +737,7 @@ static ssize_t chan_xtrigs_reset_store(struct device *dev, struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cti_config *config = &drvdata->config; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); /* clear the CTI trigger / channel programming registers */ for (i = 0; i < config->nr_trig_max; i++) { @@ -742,10 +753,9 @@ static ssize_t chan_xtrigs_reset_store(struct device *dev, config->xtrig_rchan_sel = 0; /* if enabled then write through */ - if (cti_active(config)) + if (cti_is_active(config)) cti_write_all_hw_regs(drvdata); - raw_spin_unlock(&drvdata->spinlock); return size; } static DEVICE_ATTR_WO(chan_xtrigs_reset); @@ -766,9 +776,9 @@ static ssize_t chan_xtrigs_sel_store(struct device *dev, if (val > (drvdata->config.nr_ctm_channels - 1)) return -EINVAL; - raw_spin_lock(&drvdata->spinlock); + guard(raw_spinlock_irqsave)(&drvdata->spinlock); + drvdata->config.xtrig_rchan_sel = val; - raw_spin_unlock(&drvdata->spinlock); return size; } @@ -779,9 +789,8 @@ static ssize_t chan_xtrigs_sel_show(struct device *dev, unsigned long val; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - raw_spin_lock(&drvdata->spinlock); - val = drvdata->config.xtrig_rchan_sel; - raw_spin_unlock(&drvdata->spinlock); + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) + val = drvdata->config.xtrig_rchan_sel; return sprintf(buf, "%ld\n", val); } @@ -836,12 +845,12 @@ static ssize_t print_chan_list(struct device *dev, unsigned long inuse_bits = 0, chan_mask; /* scan regs to get bitmap of channels in use. */ - raw_spin_lock(&drvdata->spinlock); - for (i = 0; i < config->nr_trig_max; i++) { - inuse_bits |= config->ctiinen[i]; - inuse_bits |= config->ctiouten[i]; + scoped_guard(raw_spinlock_irqsave, &drvdata->spinlock) { + for (i = 0; i < config->nr_trig_max; i++) { + inuse_bits |= config->ctiinen[i]; + inuse_bits |= config->ctiouten[i]; + } } - raw_spin_unlock(&drvdata->spinlock); /* inverse bits if printing free channels */ if (!inuse) @@ -1165,6 +1174,7 @@ static const struct attribute_group coresight_cti_mgmt_group = { static const struct attribute_group coresight_cti_regs_group = { .attrs = coresight_cti_regs_attrs, + .is_visible = coresight_cti_regs_is_visible, .name = "regs", }; diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index 4f89091ee93f..c5f9e79fabc6 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -119,9 +119,8 @@ struct cti_device { * @nr_trig_max: Max number of trigger signals implemented on device. * (max of trig_in or trig_out) - from ID register. * @nr_ctm_channels: number of available CTM channels - from ID register. + * @asicctl_impl: true if asicctl is implemented. * @enable_req_count: CTI is enabled alongside >=1 associated devices. - * @hw_enabled: true if hw is currently enabled. - * @hw_powered: true if associated cpu powered on, or no cpu. * @trig_in_use: bitfield of in triggers registered as in use. * @trig_out_use: bitfield of out triggers registered as in use. * @trig_out_filter: bitfield of out triggers that are blocked if filter @@ -140,11 +139,10 @@ struct cti_config { /* hardware description */ int nr_ctm_channels; int nr_trig_max; + bool asicctl_impl; /* cti enable control */ int enable_req_count; - bool hw_enabled; - bool hw_powered; /* registered triggers and filtering */ u32 trig_in_use; @@ -170,7 +168,6 @@ struct cti_config { * @spinlock: Control data access to one at a time. * @config: Configuration data for this CTI device. * @node: List entry of this device in the list of CTI devices. - * @csdev_release: release function for underlying coresight_device. */ struct cti_drvdata { void __iomem *base; @@ -179,7 +176,6 @@ struct cti_drvdata { raw_spinlock_t spinlock; struct cti_config config; struct list_head node; - void (*csdev_release)(struct device *dev); }; /* @@ -222,6 +218,7 @@ int cti_disable(struct coresight_device *csdev, struct coresight_path *path); void cti_write_all_hw_regs(struct cti_drvdata *drvdata); void cti_write_intack(struct device *dev, u32 ackval); void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value); +u32 cti_read_single_reg(struct cti_drvdata *drvdata, int offset); int cti_channel_trig_op(struct device *dev, enum cti_chan_op op, enum cti_trig_dir direction, u32 channel_idx, u32 trigger_idx); @@ -234,10 +231,10 @@ struct coresight_platform_data * coresight_cti_get_platform_data(struct device *dev); const char *cti_plat_get_node_name(struct fwnode_handle *fwnode); -/* cti powered and enabled */ -static inline bool cti_active(struct cti_config *cfg) +/* Check if a cti device is enabled */ +static inline bool cti_is_active(struct cti_config *cfg) { - return cfg->hw_powered && cfg->hw_enabled; + return !!cfg->enable_req_count; } #endif /* _CORESIGHT_CORESIGHT_CTI_H */ diff --git a/drivers/hwtracing/coresight/coresight-dummy.c b/drivers/hwtracing/coresight/coresight-dummy.c index 14322c99e29d..c176a2f57300 100644 --- a/drivers/hwtracing/coresight/coresight-dummy.c +++ b/drivers/hwtracing/coresight/coresight-dummy.c @@ -19,9 +19,6 @@ struct dummy_drvdata { u8 traceid; }; -DEFINE_CORESIGHT_DEVLIST(source_devs, "dummy_source"); -DEFINE_CORESIGHT_DEVLIST(sink_devs, "dummy_sink"); - static int dummy_source_enable(struct coresight_device *csdev, struct perf_event *event, enum cs_mode mode, __maybe_unused struct coresight_path *path) @@ -126,7 +123,7 @@ static int dummy_probe(struct platform_device *pdev) if (of_device_is_compatible(node, "arm,coresight-dummy-source")) { - desc.name = coresight_alloc_device_name(&source_devs, dev); + desc.name = coresight_alloc_device_name("dummy_source", dev); if (!desc.name) return -ENOMEM; @@ -155,7 +152,7 @@ static int dummy_probe(struct platform_device *pdev) drvdata->traceid = (u8)trace_id; } else if (of_device_is_compatible(node, "arm,coresight-dummy-sink")) { - desc.name = coresight_alloc_device_name(&sink_devs, dev); + desc.name = coresight_alloc_device_name("dummy_sink", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 6657602d8f2e..b952a1d47f12 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -63,8 +63,6 @@ #define ETB_FFSR_BIT 1 #define ETB_FRAME_SIZE_WORDS 4 -DEFINE_CORESIGHT_DEVLIST(etb_devs, "etb"); - /** * struct etb_drvdata - specifics associated to an ETB component * @base: memory mapped base address for this component. @@ -722,7 +720,7 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) struct resource *res = &adev->res; struct coresight_desc desc = { 0 }; - desc.name = coresight_alloc_device_name(&etb_devs, dev); + desc.name = coresight_alloc_device_name("etb", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 72017dcc3b7f..f85dedf89a3f 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -902,7 +902,10 @@ int etm_perf_add_symlink_sink(struct coresight_device *csdev) if (csdev->type != CORESIGHT_DEV_TYPE_SINK && csdev->type != CORESIGHT_DEV_TYPE_LINKSINK) - return -EINVAL; + return -EOPNOTSUPP; + + if (!sink_ops(csdev)->alloc_buffer) + return -EOPNOTSUPP; if (csdev->ea != NULL) return -EINVAL; diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 3b248e54471a..3f56ceccd8c9 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -30,8 +30,6 @@ #define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT) #define FUNNEL_ENSx_MASK 0xff -DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel"); - /** * struct funnel_drvdata - specifics associated to a funnel component * @base: memory mapped base address for this component. @@ -223,7 +221,7 @@ static int funnel_probe(struct device *dev, struct resource *res) of_device_is_compatible(dev->of_node, "arm,coresight-funnel")) dev_warn_once(dev, "Uses OBSOLETE CoreSight funnel binding\n"); - desc.name = coresight_alloc_device_name(&funnel_devs, dev); + desc.name = coresight_alloc_device_name("funnel", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c index 0db64c5f4995..e337b6e2bf32 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -220,6 +220,8 @@ static int of_coresight_parse_endpoint(struct device *dev, rparent = of_coresight_get_port_parent(rep); if (!rparent) break; + if (!of_device_is_available(rparent)) + break; if (of_graph_parse_endpoint(rep, &rendpoint)) break; @@ -849,7 +851,7 @@ coresight_get_platform_data(struct device *dev) error: if (!IS_ERR_OR_NULL(pdata)) /* Cleanup the connection information */ - coresight_release_platform_data(NULL, dev, pdata); + coresight_release_platform_data(dev, pdata); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(coresight_get_platform_data); diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index fd896ac07942..1ea882dffd70 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -239,8 +239,7 @@ static inline void *coresight_get_uci_data_from_amba(const struct amba_id *table return NULL; } -void coresight_release_platform_data(struct coresight_device *csdev, - struct device *dev, +void coresight_release_platform_data(struct device *dev, struct coresight_platform_data *pdata); struct coresight_device * coresight_find_csdev_by_fwnode(struct fwnode_handle *r_fwnode); diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index e6472658235d..07fc04f53b88 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -24,8 +24,6 @@ #define REPLICATOR_IDFILTER0 0x000 #define REPLICATOR_IDFILTER1 0x004 -DEFINE_CORESIGHT_DEVLIST(replicator_devs, "replicator"); - /** * struct replicator_drvdata - specifics associated to a replicator component * @base: memory mapped base address for this component. Also indicates @@ -230,7 +228,7 @@ static int replicator_probe(struct device *dev, struct resource *res) dev_warn_once(dev, "Uses OBSOLETE CoreSight replicator binding\n"); - desc.name = coresight_alloc_device_name(&replicator_devs, dev); + desc.name = coresight_alloc_device_name("replicator", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index e68529bf89c9..aca6cec7885a 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -110,8 +110,6 @@ struct channel_space { unsigned long *guaranteed; }; -DEFINE_CORESIGHT_DEVLIST(stm_devs, "stm"); - /** * struct stm_drvdata - specifics associated to an STM component * @base: memory mapped base address for this component. @@ -834,7 +832,7 @@ static int __stm_probe(struct device *dev, struct resource *res) struct resource ch_res; struct coresight_desc desc = { 0 }; - desc.name = coresight_alloc_device_name(&stm_devs, dev); + desc.name = coresight_alloc_device_name("stm", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index 36599c431be6..c89fe996af23 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -32,10 +32,6 @@ #include "coresight-priv.h" #include "coresight-tmc.h" -DEFINE_CORESIGHT_DEVLIST(etb_devs, "tmc_etb"); -DEFINE_CORESIGHT_DEVLIST(etf_devs, "tmc_etf"); -DEFINE_CORESIGHT_DEVLIST(etr_devs, "tmc_etr"); - int tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) { struct coresight_device *csdev = drvdata->csdev; @@ -401,7 +397,6 @@ static ssize_t tmc_crashdata_read(struct file *file, char __user *data, static int tmc_crashdata_release(struct inode *inode, struct file *file) { - int ret = 0; unsigned long flags; struct tmc_resrv_buf *rbuf; struct tmc_drvdata *drvdata = container_of(file->private_data, @@ -414,7 +409,7 @@ static int tmc_crashdata_release(struct inode *inode, struct file *file) raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__); - return ret; + return 0; } static const struct file_operations tmc_crashdata_fops = { @@ -777,7 +772,7 @@ static int __tmc_probe(struct device *dev, struct resource *res) struct coresight_platform_data *pdata = NULL; struct tmc_drvdata *drvdata; struct coresight_desc desc = { 0 }; - struct coresight_dev_list *dev_list = NULL; + const char *dev_list = NULL; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) @@ -827,7 +822,7 @@ static int __tmc_probe(struct device *dev, struct resource *res) desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.ops = &tmc_etb_cs_ops; - dev_list = &etb_devs; + dev_list = "tmc_etb"; break; case TMC_CONFIG_TYPE_ETR: desc.groups = coresight_etr_groups; @@ -839,7 +834,7 @@ static int __tmc_probe(struct device *dev, struct resource *res) goto out; idr_init(&drvdata->idr); mutex_init(&drvdata->idr_mutex); - dev_list = &etr_devs; + dev_list = "tmc_etr"; break; case TMC_CONFIG_TYPE_ETF: desc.groups = coresight_etf_groups; @@ -847,7 +842,7 @@ static int __tmc_probe(struct device *dev, struct resource *res) desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; desc.ops = &tmc_etf_cs_ops; - dev_list = &etf_devs; + dev_list = "tmc_etf"; break; default: pr_err("%s: Unsupported TMC config\n", desc.name); diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c index 1128612e70a7..96a25877b824 100644 --- a/drivers/hwtracing/coresight/coresight-tnoc.c +++ b/drivers/hwtracing/coresight/coresight-tnoc.c @@ -47,8 +47,6 @@ struct trace_noc_drvdata { int atid; }; -DEFINE_CORESIGHT_DEVLIST(trace_noc_devs, "traceNoc"); - static void trace_noc_enable_hw(struct trace_noc_drvdata *drvdata) { u32 val; @@ -191,7 +189,7 @@ static int _tnoc_probe(struct device *dev, struct resource *res) struct coresight_desc desc = { 0 }; int ret; - desc.name = coresight_alloc_device_name(&trace_noc_devs, dev); + desc.name = coresight_alloc_device_name("traceNoc", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c index 7055f8f13427..89c8f71f0aff 100644 --- a/drivers/hwtracing/coresight/coresight-tpda.c +++ b/drivers/hwtracing/coresight/coresight-tpda.c @@ -20,8 +20,6 @@ #include "coresight-trace-id.h" #include "coresight-tpdm.h" -DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda"); - static void tpda_clear_element_size(struct coresight_device *csdev) { struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -585,7 +583,7 @@ static int tpda_probe(struct amba_device *adev, const struct amba_id *id) if (ret) return ret; - desc.name = coresight_alloc_device_name(&tpda_devs, dev); + desc.name = coresight_alloc_device_name("tpda", dev); if (!desc.name) return -ENOMEM; desc.type = CORESIGHT_DEV_TYPE_LINK; diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index 06e0a905a67d..eaf7210af648 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -19,8 +19,6 @@ #include "coresight-priv.h" #include "coresight-tpdm.h" -DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm"); - static bool tpdm_has_dsb_dataset(struct tpdm_drvdata *drvdata) { return (drvdata->datasets & TPDM_PIDR0_DS_DSB); @@ -483,7 +481,7 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata) static int tpdm_enable(struct coresight_device *csdev, struct perf_event *event, enum cs_mode mode, - __maybe_unused struct coresight_path *path) + struct coresight_path *path) { struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -499,6 +497,7 @@ static int tpdm_enable(struct coresight_device *csdev, struct perf_event *event, } __tpdm_enable(drvdata); + drvdata->traceid = path->trace_id; drvdata->enable = true; spin_unlock(&drvdata->spinlock); @@ -695,6 +694,29 @@ static struct attribute_group tpdm_attr_grp = { .attrs = tpdm_attrs, }; +static ssize_t traceid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->traceid; + if (!val) + return -EINVAL; + + return sysfs_emit(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(traceid); + +static struct attribute *traceid_attrs[] = { + &dev_attr_traceid.attr, + NULL, +}; + +static struct attribute_group traceid_attr_grp = { + .attrs = traceid_attrs, +}; + static ssize_t dsb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1369,6 +1391,12 @@ static const struct attribute_group *tpdm_attr_grps[] = { &tpdm_cmb_patt_grp, &tpdm_cmb_msr_grp, &tpdm_mcmb_attr_grp, + &traceid_attr_grp, + NULL, +}; + +static const struct attribute_group *static_tpdm_attr_grps[] = { + &traceid_attr_grp, NULL, }; @@ -1402,6 +1430,7 @@ static int tpdm_probe(struct device *dev, struct resource *res) if (ret) return ret; + desc.access = CSDEV_ACCESS_IOMEM(base); if (tpdm_has_dsb_dataset(drvdata)) of_property_read_u32(drvdata->dev->of_node, "qcom,dsb-msrs-num", &drvdata->dsb_msr_num); @@ -1416,7 +1445,7 @@ static int tpdm_probe(struct device *dev, struct resource *res) } /* Set up coresight component description */ - desc.name = coresight_alloc_device_name(&tpdm_devs, dev); + desc.name = coresight_alloc_device_name("tpdm", dev); if (!desc.name) return -ENOMEM; desc.type = CORESIGHT_DEV_TYPE_SOURCE; @@ -1424,9 +1453,10 @@ static int tpdm_probe(struct device *dev, struct resource *res) desc.ops = &tpdm_cs_ops; desc.pdata = dev->platform_data; desc.dev = dev; - desc.access = CSDEV_ACCESS_IOMEM(base); if (res) desc.groups = tpdm_attr_grps; + else + desc.groups = static_tpdm_attr_grps; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-tpdm.h b/drivers/hwtracing/coresight/coresight-tpdm.h index 2867f3ab8186..11da64e1ade8 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.h +++ b/drivers/hwtracing/coresight/coresight-tpdm.h @@ -300,6 +300,7 @@ struct cmb_dataset { * @cmb Specifics associated to TPDM CMB. * @dsb_msr_num Number of MSR supported by DSB TPDM * @cmb_msr_num Number of MSR supported by CMB TPDM + * @traceid Trace ID of the path. */ struct tpdm_drvdata { @@ -313,6 +314,7 @@ struct tpdm_drvdata { struct cmb_dataset *cmb; u32 dsb_msr_num; u32 cmb_msr_num; + u8 traceid; }; /* Enumerate members of various datasets */ diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index aaa44bc521c3..b8560b140e0f 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -49,8 +49,6 @@ #define FFCR_FON_MAN BIT(6) #define FFCR_STOP_FI BIT(12) -DEFINE_CORESIGHT_DEVLIST(tpiu_devs, "tpiu"); - /* * @base: memory mapped base address for this component. * @atclk: optional clock for the core parts of the TPIU. @@ -134,7 +132,7 @@ static int __tpiu_probe(struct device *dev, struct resource *res) struct coresight_desc desc = { 0 }; int ret; - desc.name = coresight_alloc_device_name(&tpiu_devs, dev); + desc.name = coresight_alloc_device_name("tpiu", dev); if (!desc.name) return -ENOMEM; diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index 8f7922a5e534..5776f63468fa 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -17,8 +17,6 @@ #include "coresight-priv.h" #include "ultrasoc-smb.h" -DEFINE_CORESIGHT_DEVLIST(sink_devs, "ultra_smb"); - #define ULTRASOC_SMB_DSM_UUID "82ae1283-7f6a-4cbe-aa06-53e8fb24db18" static bool smb_buffer_not_empty(struct smb_drv_data *drvdata) @@ -478,7 +476,7 @@ static int smb_register_sink(struct platform_device *pdev, desc.pdata = pdata; desc.dev = &pdev->dev; desc.groups = smb_sink_groups; - desc.name = coresight_alloc_device_name(&sink_devs, &pdev->dev); + desc.name = coresight_alloc_device_name("ultra_smb", &pdev->dev); if (!desc.name) { dev_err(&pdev->dev, "Failed to alloc coresight device name"); return -ENOMEM; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 7cb6b9b864a7..8c935f867a37 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1211,8 +1211,7 @@ config I2C_SYNQUACER config I2C_TEGRA tristate "NVIDIA Tegra internal I2C controller" - depends on ARCH_TEGRA || (COMPILE_TEST && (ARC || ARM || ARM64 || M68K || RISCV || SUPERH || SPARC)) - # COMPILE_TEST needs architectures with readsX()/writesX() primitives + depends on ARCH_TEGRA || COMPILE_TEST depends on PINCTRL # ARCH_TEGRA implies PINCTRL, but the COMPILE_TEST side doesn't. help diff --git a/drivers/i2c/busses/i2c-cp2615.c b/drivers/i2c/busses/i2c-cp2615.c index 8212875700e1..951de6249834 100644 --- a/drivers/i2c/busses/i2c-cp2615.c +++ b/drivers/i2c/busses/i2c-cp2615.c @@ -270,8 +270,7 @@ static struct i2c_adapter_quirks cp2615_i2c_quirks = { .max_comb_2nd_msg_len = MAX_I2C_SIZE }; -static void -cp2615_i2c_remove(struct usb_interface *usbif) +static void cp2615_i2c_disconnect(struct usb_interface *usbif) { struct i2c_adapter *adap = usb_get_intfdata(usbif); @@ -328,7 +327,7 @@ MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver cp2615_i2c_driver = { .name = "i2c-cp2615", .probe = cp2615_i2c_probe, - .disconnect = cp2615_i2c_remove, + .disconnect = cp2615_i2c_disconnect, .id_table = id_table, }; diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 426ffec06e22..3351c4a9ef11 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -268,6 +268,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = { { "AMDI0510", 0 }, { "APMC0D0F", 0 }, { "FUJI200B", 0 }, + { "GOOG5000", 0 }, { "HISI02A1", 0 }, { "HISI02A2", 0 }, { "HISI02A3", 0 }, diff --git a/drivers/i2c/busses/i2c-diolan-u2c.c b/drivers/i2c/busses/i2c-diolan-u2c.c index 077b093ba834..d502f6e4732f 100644 --- a/drivers/i2c/busses/i2c-diolan-u2c.c +++ b/drivers/i2c/busses/i2c-diolan-u2c.c @@ -427,12 +427,6 @@ static const struct usb_device_id diolan_u2c_table[] = { MODULE_DEVICE_TABLE(usb, diolan_u2c_table); -static void diolan_u2c_free(struct i2c_diolan_u2c *dev) -{ - usb_put_dev(dev->usb_dev); - kfree(dev); -} - static int diolan_u2c_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -453,7 +447,7 @@ static int diolan_u2c_probe(struct usb_interface *interface, dev->ep_out = hostif->endpoint[0].desc.bEndpointAddress; dev->ep_in = hostif->endpoint[1].desc.bEndpointAddress; - dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + dev->usb_dev = interface_to_usbdev(interface); dev->interface = interface; /* save our data pointer in this interface device */ @@ -488,7 +482,7 @@ static int diolan_u2c_probe(struct usb_interface *interface, error_free: usb_set_intfdata(interface, NULL); - diolan_u2c_free(dev); + kfree(dev); error: return ret; } @@ -499,7 +493,7 @@ static void diolan_u2c_disconnect(struct usb_interface *interface) i2c_del_adapter(&dev->adapter); usb_set_intfdata(interface, NULL); - diolan_u2c_free(dev); + kfree(dev); dev_dbg(&interface->dev, "disconnected\n"); } diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c index 6e93da576bbd..9152cf436bea 100644 --- a/drivers/i2c/busses/i2c-k1.c +++ b/drivers/i2c/busses/i2c-k1.c @@ -98,6 +98,10 @@ #define SPACEMIT_BUS_RESET_CLK_CNT_MAX 9 +#define SPACEMIT_WAIT_TIMEOUT 1000 /* ms */ +#define SPACEMIT_POLL_TIMEOUT 1000 /* us */ +#define SPACEMIT_POLL_INTERVAL 30 /* us */ + enum spacemit_i2c_state { SPACEMIT_STATE_IDLE, SPACEMIT_STATE_START, @@ -126,6 +130,7 @@ struct spacemit_i2c_dev { enum spacemit_i2c_state state; bool read; + bool use_pio; struct completion complete; u32 status; }; @@ -172,6 +177,14 @@ static int spacemit_i2c_handle_err(struct spacemit_i2c_dev *i2c) return i2c->status & SPACEMIT_SR_ACKNAK ? -ENXIO : -EIO; } +static inline void spacemit_i2c_delay(struct spacemit_i2c_dev *i2c, unsigned int us) +{ + if (i2c->use_pio) + udelay(us); + else + fsleep(us); +} + static void spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev *i2c) { u32 status; @@ -183,7 +196,8 @@ static void spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev *i2c) return; spacemit_i2c_reset(i2c); - usleep_range(10, 20); + + spacemit_i2c_delay(i2c, 10); for (clk_cnt = 0; clk_cnt < SPACEMIT_BUS_RESET_CLK_CNT_MAX; clk_cnt++) { status = readl(i2c->base + SPACEMIT_IBMR); @@ -212,9 +226,15 @@ static int spacemit_i2c_wait_bus_idle(struct spacemit_i2c_dev *i2c) if (!(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB))) return 0; - ret = readl_poll_timeout(i2c->base + SPACEMIT_ISR, - val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)), - 1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT); + if (i2c->use_pio) + ret = readl_poll_timeout_atomic(i2c->base + SPACEMIT_ISR, + val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)), + 1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT); + else + ret = readl_poll_timeout(i2c->base + SPACEMIT_ISR, + val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)), + 1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT); + if (ret) spacemit_i2c_reset(i2c); @@ -226,7 +246,7 @@ static void spacemit_i2c_check_bus_release(struct spacemit_i2c_dev *i2c) /* in case bus is not released after transfer completes */ if (readl(i2c->base + SPACEMIT_ISR) & SPACEMIT_SR_EBB) { spacemit_i2c_conditionally_reset_bus(i2c); - usleep_range(90, 150); + spacemit_i2c_delay(i2c, 90); } } @@ -238,25 +258,33 @@ spacemit_i2c_clear_int_status(struct spacemit_i2c_dev *i2c, u32 mask) static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c) { - u32 val; + u32 val = 0; - /* - * Unmask interrupt bits for all xfer mode: - * bus error, arbitration loss detected. - * For transaction complete signal, we use master stop - * interrupt, so we don't need to unmask SPACEMIT_CR_TXDONEIE. - */ - val = SPACEMIT_CR_BEIE | SPACEMIT_CR_ALDIE; + if (!i2c->use_pio) { + /* + * Enable interrupt bits for all xfer mode: + * bus error, arbitration loss detected. + */ + val |= SPACEMIT_CR_BEIE | SPACEMIT_CR_ALDIE; - /* - * Unmask interrupt bits for interrupt xfer mode: - * When IDBR receives a byte, an interrupt is triggered. - * - * For the tx empty interrupt, it will be enabled in the - * i2c_start function. - * Otherwise, it will cause an erroneous empty interrupt before i2c_start. - */ - val |= SPACEMIT_CR_DRFIE; + /* + * Unmask interrupt bits for interrupt xfer mode: + * When IDBR receives a byte, an interrupt is triggered. + * + * For the tx empty interrupt, it will be enabled in the + * i2c_start(). + * We don't want a TX empty interrupt until we start + * a transfer in i2c_start(). + */ + val |= SPACEMIT_CR_DRFIE; + + /* + * Enable master stop interrupt bit. + * For transaction complete signal, we use master stop + * interrupt, so we don't need to unmask SPACEMIT_CR_TXDONEIE. + */ + val |= SPACEMIT_CR_MSDIE; + } if (i2c->clock_freq == SPACEMIT_I2C_MAX_FAST_MODE_FREQ) val |= SPACEMIT_CR_MODE_FAST; @@ -268,7 +296,7 @@ static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c) val |= SPACEMIT_CR_SCLE; /* enable master stop detected */ - val |= SPACEMIT_CR_MSDE | SPACEMIT_CR_MSDIE; + val |= SPACEMIT_CR_MSDE; writel(val, i2c->base + SPACEMIT_ICR); @@ -301,41 +329,15 @@ static void spacemit_i2c_start(struct spacemit_i2c_dev *i2c) /* send start pulse */ val = readl(i2c->base + SPACEMIT_ICR); val &= ~SPACEMIT_CR_STOP; - val |= SPACEMIT_CR_START | SPACEMIT_CR_TB | SPACEMIT_CR_DTEIE; + val |= SPACEMIT_CR_START | SPACEMIT_CR_TB; + + /* Enable the TX empty interrupt */ + if (!i2c->use_pio) + val |= SPACEMIT_CR_DTEIE; + writel(val, i2c->base + SPACEMIT_ICR); } -static int spacemit_i2c_xfer_msg(struct spacemit_i2c_dev *i2c) -{ - unsigned long time_left; - struct i2c_msg *msg; - - for (i2c->msg_idx = 0; i2c->msg_idx < i2c->msg_num; i2c->msg_idx++) { - msg = &i2c->msgs[i2c->msg_idx]; - i2c->msg_buf = msg->buf; - i2c->unprocessed = msg->len; - i2c->status = 0; - - reinit_completion(&i2c->complete); - - spacemit_i2c_start(i2c); - - time_left = wait_for_completion_timeout(&i2c->complete, - i2c->adapt.timeout); - if (!time_left) { - dev_err(i2c->dev, "msg completion timeout\n"); - spacemit_i2c_conditionally_reset_bus(i2c); - spacemit_i2c_reset(i2c); - return -ETIMEDOUT; - } - - if (i2c->status & SPACEMIT_SR_ERR) - return spacemit_i2c_handle_err(i2c); - } - - return 0; -} - static bool spacemit_i2c_is_last_msg(struct spacemit_i2c_dev *i2c) { if (i2c->msg_idx != i2c->msg_num - 1) @@ -347,8 +349,23 @@ static bool spacemit_i2c_is_last_msg(struct spacemit_i2c_dev *i2c) return !i2c->unprocessed; } +static inline void spacemit_i2c_complete(struct spacemit_i2c_dev *i2c) +{ + /* SPACEMIT_STATE_IDLE avoids triggering the next byte */ + i2c->state = SPACEMIT_STATE_IDLE; + + if (i2c->use_pio) + return; + + complete(&i2c->complete); +} + static void spacemit_i2c_handle_write(struct spacemit_i2c_dev *i2c) { + /* If there's no space in the IDBR, we're done */ + if (!(i2c->status & SPACEMIT_SR_ITE)) + return; + /* if transfer completes, SPACEMIT_ISR will handle it */ if (i2c->status & SPACEMIT_SR_MSD) return; @@ -359,16 +376,19 @@ static void spacemit_i2c_handle_write(struct spacemit_i2c_dev *i2c) return; } - /* SPACEMIT_STATE_IDLE avoids trigger next byte */ - i2c->state = SPACEMIT_STATE_IDLE; - complete(&i2c->complete); + spacemit_i2c_complete(i2c); } static void spacemit_i2c_handle_read(struct spacemit_i2c_dev *i2c) { + /* If there's nothing in the IDBR, we're done */ + if (!(i2c->status & SPACEMIT_SR_IRF)) + return; + if (i2c->unprocessed) { *i2c->msg_buf++ = readl(i2c->base + SPACEMIT_IDBR); i2c->unprocessed--; + return; } /* if transfer completes, SPACEMIT_ISR will handle it */ @@ -379,9 +399,7 @@ static void spacemit_i2c_handle_read(struct spacemit_i2c_dev *i2c) if (i2c->unprocessed) return; - /* SPACEMIT_STATE_IDLE avoids trigger next byte */ - i2c->state = SPACEMIT_STATE_IDLE; - complete(&i2c->complete); + spacemit_i2c_complete(i2c); } static void spacemit_i2c_handle_start(struct spacemit_i2c_dev *i2c) @@ -415,29 +433,16 @@ static void spacemit_i2c_err_check(struct spacemit_i2c_dev *i2c) spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK); - i2c->state = SPACEMIT_STATE_IDLE; - complete(&i2c->complete); + spacemit_i2c_complete(i2c); } -static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid) +static void spacemit_i2c_handle_state(struct spacemit_i2c_dev *i2c) { - struct spacemit_i2c_dev *i2c = devid; - u32 status, val; - - status = readl(i2c->base + SPACEMIT_ISR); - if (!status) - return IRQ_HANDLED; - - i2c->status = status; - - spacemit_i2c_clear_int_status(i2c, status); + u32 val; if (i2c->status & SPACEMIT_SR_ERR) goto err_out; - val = readl(i2c->base + SPACEMIT_ICR); - val &= ~(SPACEMIT_CR_TB | SPACEMIT_CR_ACKNAK | SPACEMIT_CR_STOP | SPACEMIT_CR_START); - switch (i2c->state) { case SPACEMIT_STATE_START: spacemit_i2c_handle_start(i2c); @@ -453,7 +458,12 @@ static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid) } if (i2c->state != SPACEMIT_STATE_IDLE) { - val |= SPACEMIT_CR_TB | SPACEMIT_CR_ALDIE; + val = readl(i2c->base + SPACEMIT_ICR); + val &= ~(SPACEMIT_CR_TB | SPACEMIT_CR_ACKNAK | + SPACEMIT_CR_STOP | SPACEMIT_CR_START); + val |= SPACEMIT_CR_TB; + if (!i2c->use_pio) + val |= SPACEMIT_CR_ALDIE; if (spacemit_i2c_is_last_msg(i2c)) { /* trigger next byte with stop */ @@ -467,6 +477,133 @@ static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid) err_out: spacemit_i2c_err_check(i2c); +} + +/* + * In PIO mode, this function is used as a replacement for + * wait_for_completion_timeout(), whose return value indicates + * the remaining time. + * + * We do not have a meaningful remaining-time value here, so + * return a non-zero value on success to indicate "not timed out". + * Returning 1 ensures callers treating the return value as + * time_left will not incorrectly report a timeout. + */ +static int spacemit_i2c_wait_pio_xfer(struct spacemit_i2c_dev *i2c) +{ + u32 mask, msec = jiffies_to_msecs(i2c->adapt.timeout); + ktime_t timeout = ktime_add_ms(ktime_get(), msec); + int ret; + + mask = SPACEMIT_SR_IRF | SPACEMIT_SR_ITE; + + do { + i2c->status = readl(i2c->base + SPACEMIT_ISR); + + spacemit_i2c_clear_int_status(i2c, i2c->status); + + if (i2c->status & mask) + spacemit_i2c_handle_state(i2c); + else + udelay(SPACEMIT_POLL_INTERVAL); + } while (i2c->unprocessed && ktime_compare(ktime_get(), timeout) < 0); + + if (i2c->unprocessed) + return 0; + + if (i2c->read) + return 1; + + /* + * If this is the last byte to write of the current message, + * we have to wait here. Otherwise, control will proceed directly + * to start(), which would overwrite the current data. + */ + ret = readl_poll_timeout_atomic(i2c->base + SPACEMIT_ISR, + i2c->status, i2c->status & SPACEMIT_SR_ITE, + SPACEMIT_POLL_INTERVAL, SPACEMIT_POLL_TIMEOUT); + if (ret) + return 0; + + /* + * For writes: in interrupt mode, an ITE (write-empty) interrupt is triggered + * after the last byte, and the MSD-related handling takes place there. + * In PIO mode, however, we need to explicitly call err_check() to emulate this + * step, otherwise the next transfer will fail. + */ + if (i2c->msg_idx == i2c->msg_num - 1) { + mask = SPACEMIT_SR_MSD | SPACEMIT_SR_ERR; + /* + * In some cases, MSD may not arrive immediately; + * wait here to handle that. + */ + ret = readl_poll_timeout_atomic(i2c->base + SPACEMIT_ISR, + i2c->status, i2c->status & mask, + SPACEMIT_POLL_INTERVAL, SPACEMIT_POLL_TIMEOUT); + if (ret) + return 0; + + spacemit_i2c_err_check(i2c); + } + + return 1; +} + +static int spacemit_i2c_wait_xfer_complete(struct spacemit_i2c_dev *i2c) +{ + if (i2c->use_pio) + return spacemit_i2c_wait_pio_xfer(i2c); + + return wait_for_completion_timeout(&i2c->complete, + i2c->adapt.timeout); +} + +static int spacemit_i2c_xfer_msg(struct spacemit_i2c_dev *i2c) +{ + unsigned long time_left; + struct i2c_msg *msg; + + for (i2c->msg_idx = 0; i2c->msg_idx < i2c->msg_num; i2c->msg_idx++) { + msg = &i2c->msgs[i2c->msg_idx]; + i2c->msg_buf = msg->buf; + i2c->unprocessed = msg->len; + i2c->status = 0; + + reinit_completion(&i2c->complete); + + spacemit_i2c_start(i2c); + + time_left = spacemit_i2c_wait_xfer_complete(i2c); + + if (!time_left) { + dev_err(i2c->dev, "msg completion timeout\n"); + spacemit_i2c_conditionally_reset_bus(i2c); + spacemit_i2c_reset(i2c); + return -ETIMEDOUT; + } + + if (i2c->status & SPACEMIT_SR_ERR) + return spacemit_i2c_handle_err(i2c); + } + + return 0; +} + +static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid) +{ + struct spacemit_i2c_dev *i2c = devid; + u32 status; + + status = readl(i2c->base + SPACEMIT_ISR); + if (!status) + return IRQ_HANDLED; + + i2c->status = status; + + spacemit_i2c_clear_int_status(i2c, status); + + spacemit_i2c_handle_state(i2c); + return IRQ_HANDLED; } @@ -475,6 +612,11 @@ static void spacemit_i2c_calc_timeout(struct spacemit_i2c_dev *i2c) unsigned long timeout; int idx = 0, cnt = 0; + if (i2c->use_pio) { + i2c->adapt.timeout = msecs_to_jiffies(SPACEMIT_WAIT_TIMEOUT); + return; + } + for (; idx < i2c->msg_num; idx++) cnt += (i2c->msgs + idx)->len + 1; @@ -487,11 +629,14 @@ static void spacemit_i2c_calc_timeout(struct spacemit_i2c_dev *i2c) i2c->adapt.timeout = usecs_to_jiffies(timeout + USEC_PER_SEC / 10) / i2c->msg_num; } -static int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num) +static inline int +spacemit_i2c_xfer_common(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num, bool use_pio) { struct spacemit_i2c_dev *i2c = i2c_get_adapdata(adapt); int ret; + i2c->use_pio = use_pio; + i2c->msgs = msgs; i2c->msg_num = num; @@ -519,6 +664,16 @@ static int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, in return ret < 0 ? ret : num; } +static int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num) +{ + return spacemit_i2c_xfer_common(adapt, msgs, num, false); +} + +static int spacemit_i2c_pio_xfer_atomic(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num) +{ + return spacemit_i2c_xfer_common(adapt, msgs, num, true); +} + static u32 spacemit_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); @@ -526,6 +681,7 @@ static u32 spacemit_i2c_func(struct i2c_adapter *adap) static const struct i2c_algorithm spacemit_i2c_algo = { .xfer = spacemit_i2c_xfer, + .xfer_atomic = spacemit_i2c_pio_xfer_atomic, .functionality = spacemit_i2c_func, }; diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index cb4d3aa709d0..126040ca05f1 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1244,9 +1245,15 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, bool write_then_read_en = false; struct mtk_i2c *i2c = i2c_get_adapdata(adap); + if (i2c->adap.bus_regulator) { + ret = regulator_enable(i2c->adap.bus_regulator); + if (ret) + return ret; + } + ret = clk_bulk_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); if (ret) - return ret; + goto err_regulator; i2c->auto_restart = i2c->dev_comp->auto_restart; @@ -1301,6 +1308,10 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, err_exit: clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); +err_regulator: + if (i2c->adap.bus_regulator) + regulator_disable(i2c->adap.bus_regulator); + return ret; } diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c index 8b7e15240fb0..f667a873b81e 100644 --- a/drivers/i2c/busses/i2c-npcm7xx.c +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -1384,7 +1384,7 @@ static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus) */ bus->operation = I2C_NO_OPER; bus->own_slave_addr = 0xFF; - i2c_slave_event(bus->slave, I2C_SLAVE_STOP, 0); + i2c_slave_event(bus->slave, I2C_SLAVE_STOP, NULL); iowrite8(NPCM_I2CST_SLVSTP, bus->reg + NPCM_I2CST); if (bus->fifo_use) { npcm_i2c_clear_fifo_int(bus); diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 0f67e57cdeff..df6ebf32d6e8 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /* @@ -258,7 +259,7 @@ static void ocores_process_timeout(struct ocores_i2c *i2c) * @reg: register to query * @mask: bitmask to apply on register value * @val: expected result - * @timeout: timeout in jiffies + * @timeout_us: timeout in microseconds * * Timeout is necessary to avoid to stay here forever when the chip * does not answer correctly. @@ -267,21 +268,14 @@ static void ocores_process_timeout(struct ocores_i2c *i2c) */ static int ocores_wait(struct ocores_i2c *i2c, int reg, u8 mask, u8 val, - const unsigned long timeout) + unsigned long timeout_us) { - unsigned long j; + u8 status; - j = jiffies + timeout; - while (1) { - u8 status = oc_getreg(i2c, reg); - - if ((status & mask) == val) - break; - - if (time_after(jiffies, j)) - return -ETIMEDOUT; - } - return 0; + return read_poll_timeout_atomic(oc_getreg, status, + (status & mask) == val, + 0, timeout_us, false, + i2c, reg); } /** @@ -314,7 +308,7 @@ static int ocores_poll_wait(struct ocores_i2c *i2c) * once we are here we expect to get the expected result immediately * so if after 1ms we timeout then something is broken. */ - err = ocores_wait(i2c, OCI2C_STATUS, mask, 0, msecs_to_jiffies(1)); + err = ocores_wait(i2c, OCI2C_STATUS, mask, 0, 1000); if (err) dev_warn(i2c->adap.dev.parent, "%s: STATUS timeout, bit 0x%x did not clear in 1ms\n", diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index f55840b2eb9a..9a8b154ab69e 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -71,6 +71,7 @@ #define ISR_GCAD (1 << 8) /* general call address detected */ #define ISR_SAD (1 << 9) /* slave address detected */ #define ISR_BED (1 << 10) /* bus error no ACK/NAK */ +#define ISR_A3700_EBB (1 << 11) /* early bus busy for armada 3700 */ #define ILCR_SLV_SHIFT 0 #define ILCR_SLV_MASK (0x1FF << ILCR_SLV_SHIFT) @@ -263,6 +264,7 @@ struct pxa_i2c { bool highmode_enter; u32 fm_mask; u32 hs_mask; + u32 busy_mask; struct i2c_bus_recovery_info recovery; struct pinctrl *pinctrl; @@ -430,7 +432,7 @@ static int i2c_pxa_wait_bus_not_busy(struct pxa_i2c *i2c) while (1) { isr = readl(_ISR(i2c)); - if (!(isr & (ISR_IBB | ISR_UB))) + if (!(isr & i2c->busy_mask)) return 0; if (isr & ISR_SAD) @@ -467,7 +469,7 @@ static int i2c_pxa_wait_master(struct pxa_i2c *i2c) * quick check of the i2c lines themselves to ensure they've * gone high... */ - if ((readl(_ISR(i2c)) & (ISR_UB | ISR_IBB)) == 0 && + if ((readl(_ISR(i2c)) & i2c->busy_mask) == 0 && readl(_IBMR(i2c)) == (IBMR_SCLS | IBMR_SDAS)) { if (i2c_debug > 0) dev_dbg(&i2c->adap.dev, "%s: done\n", __func__); @@ -488,7 +490,7 @@ static int i2c_pxa_set_master(struct pxa_i2c *i2c) if (i2c_debug) dev_dbg(&i2c->adap.dev, "setting to bus master\n"); - if ((readl(_ISR(i2c)) & (ISR_UB | ISR_IBB)) != 0) { + if ((readl(_ISR(i2c)) & i2c->busy_mask) != 0) { dev_dbg(&i2c->adap.dev, "%s: unit is busy\n", __func__); if (!i2c_pxa_wait_master(i2c)) { dev_dbg(&i2c->adap.dev, "%s: error: unit busy\n", __func__); @@ -514,7 +516,7 @@ static int i2c_pxa_wait_slave(struct pxa_i2c *i2c) dev_dbg(&i2c->adap.dev, "%s: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n", __func__, (long)jiffies, readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c))); - if ((readl(_ISR(i2c)) & (ISR_UB|ISR_IBB)) == 0 || + if ((readl(_ISR(i2c)) & i2c->busy_mask) == 0 || (readl(_ISR(i2c)) & ISR_SAD) != 0 || (readl(_ICR(i2c)) & ICR_SCLE) == 0) { if (i2c_debug > 1) @@ -1177,7 +1179,7 @@ static int i2c_pxa_pio_set_master(struct pxa_i2c *i2c) /* * Wait for the bus to become free. */ - while (timeout-- && readl(_ISR(i2c)) & (ISR_IBB | ISR_UB)) + while (timeout-- && readl(_ISR(i2c)) & i2c->busy_mask) udelay(1000); if (timeout < 0) { @@ -1322,7 +1324,7 @@ static void i2c_pxa_unprepare_recovery(struct i2c_adapter *adap) * handing control of the bus back to avoid the bus changing state. */ isr = readl(_ISR(i2c)); - if (isr & (ISR_UB | ISR_IBB)) { + if (isr & i2c->busy_mask) { dev_dbg(&i2c->adap.dev, "recovery: resetting controller, ISR=0x%08x\n", isr); i2c_pxa_do_reset(i2c); @@ -1479,6 +1481,10 @@ static int i2c_pxa_probe(struct platform_device *dev) i2c->fm_mask = pxa_reg_layout[i2c_type].fm; i2c->hs_mask = pxa_reg_layout[i2c_type].hs; + i2c->busy_mask = ISR_UB | ISR_IBB; + if (i2c_type == REGS_A3700) + i2c->busy_mask |= ISR_A3700_EBB; + if (i2c_type != REGS_CE4100) i2c->reg_isar = i2c->reg_base + pxa_reg_layout[i2c_type].isar; diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 884055df1560..f3ccfbbc4bea 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -71,9 +71,6 @@ #define NUM_MASTERS 2 #define NUM_QUEUES 2 -/* Max number of resources + 1 for a NULL terminator */ -#define CCI_RES_MAX 6 - #define CCI_I2C_SET_PARAM 1 #define CCI_I2C_REPORT 8 #define CCI_I2C_WRITE 9 diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index a4acb78fafb6..a482a4c60744 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -625,8 +625,8 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], { struct gpi_i2c_config *peripheral; unsigned int flags; - void *dma_buf; - dma_addr_t addr; + void *dma_buf = NULL; + dma_addr_t addr = 0; enum dma_data_direction map_dirn; enum dma_transfer_direction dma_dirn; struct dma_async_tx_descriptor *desc; @@ -639,6 +639,16 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], gi2c_gpi_xfer = &gi2c->i2c_multi_desc_config; msg_idx = gi2c_gpi_xfer->msg_idx_cnt; + /* + * Skip TX DMA mapping for a read message (I2C_M_RD) to avoid + * programming an extra TX DMA TRE that would cause an unintended + * write cycle on the I2C bus before the actual read operation. + */ + if (op == I2C_WRITE && msgs[msg_idx].flags & I2C_M_RD) { + peripheral->multi_msg = true; + goto skip_tx_dma_map; + } + dma_buf = i2c_get_dma_safe_msg_buf(&msgs[msg_idx], 1); if (!dma_buf) { ret = -ENOMEM; @@ -658,6 +668,7 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], goto out; } +skip_tx_dma_map: if (gi2c->is_tx_multi_desc_xfer) { flags = DMA_CTRL_ACK; @@ -740,9 +751,12 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], return 0; err_config: - dma_unmap_single(gi2c->se.dev->parent, addr, - msgs[msg_idx].len, map_dirn); - i2c_put_dma_safe_msg_buf(dma_buf, &msgs[msg_idx], false); + /* Avoid DMA unmap as the write operation skipped DMA mapping */ + if (dma_buf) { + dma_unmap_single(gi2c->se.dev->parent, addr, + msgs[msg_idx].len, map_dirn); + i2c_put_dma_safe_msg_buf(dma_buf, &msgs[msg_idx], false); + } out: gi2c->err = ret; diff --git a/drivers/i2c/busses/i2c-robotfuzz-osif.c b/drivers/i2c/busses/i2c-robotfuzz-osif.c index e0a76fb5bc31..412fa8e37f69 100644 --- a/drivers/i2c/busses/i2c-robotfuzz-osif.c +++ b/drivers/i2c/busses/i2c-robotfuzz-osif.c @@ -141,7 +141,7 @@ static int osif_probe(struct usb_interface *interface, if (!priv) return -ENOMEM; - priv->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + priv->usb_dev = interface_to_usbdev(interface); priv->interface = interface; usb_set_intfdata(interface, priv); @@ -163,7 +163,6 @@ static int osif_probe(struct usb_interface *interface, NULL, 0); if (ret) { dev_err(&interface->dev, "failure sending bit rate"); - usb_put_dev(priv->usb_dev); return ret; } @@ -184,7 +183,6 @@ static void osif_disconnect(struct usb_interface *interface) i2c_del_adapter(&(priv->adapter)); usb_set_intfdata(interface, NULL); - usb_put_dev(priv->usb_dev); } static struct usb_driver osif_driver = { diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c index 672cb978066d..8cedffbb2964 100644 --- a/drivers/i2c/busses/i2c-rtl9300.c +++ b/drivers/i2c/busses/i2c-rtl9300.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include #include #include @@ -11,10 +12,16 @@ #include enum rtl9300_bus_freq { - RTL9300_I2C_STD_FREQ, - RTL9300_I2C_FAST_FREQ, + RTL9300_I2C_STD_FREQ, // 100kHz + RTL9300_I2C_FAST_FREQ, // 400kHz + RTL9300_I2C_SUPER_FAST_FREQ, // 2.5MHz + RTL9300_I2C_SLOW_FREQ, // 50kHz }; +#define RTL9300_I2C_MAX_SUPER_FAST_FREQ 2500000 +#define RTL9300_I2C_MAX_SLOW_FREQ 50000 + + struct rtl9300_i2c; struct rtl9300_i2c_chan { @@ -22,6 +29,7 @@ struct rtl9300_i2c_chan { struct rtl9300_i2c *i2c; enum rtl9300_bus_freq bus_freq; u8 sda_num; + u32 clk_div; }; enum rtl9300_i2c_reg_scope { @@ -47,6 +55,9 @@ enum rtl9300_i2c_reg_fields { F_SCL_SEL, F_SDA_OUT_SEL, F_SDA_SEL, + F_BUSY, + F_CLK_DIV, + F_EXT_SCK_5MS, /* keep last */ F_NUM_FIELDS @@ -55,12 +66,22 @@ enum rtl9300_i2c_reg_fields { struct rtl9300_i2c_drv_data { struct rtl9300_i2c_reg_field field_desc[F_NUM_FIELDS]; int (*select_scl)(struct rtl9300_i2c *i2c, u8 scl); - u32 data_reg; + int (*config_chan)(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan); + void (*config_clock)(u32 clock_freq, struct rtl9300_i2c_chan *chan); + int (*misc_init)(struct rtl9300_i2c *i2c); + u32 rd_reg; + u32 wd_reg; u8 max_nchan; + u8 max_data_len; + u8 reg_addr_8bit_len; }; #define RTL9300_I2C_MUX_NCHAN 8 #define RTL9310_I2C_MUX_NCHAN 12 +#define RTL9607_I2C_MUX_NCHAN 1 + +#define RTL9300_I2C_MAX_DATA_LEN 16 +#define RTL9607_I2C_MAX_DATA_LEN 4 struct rtl9300_i2c { struct regmap *regmap; @@ -68,10 +89,12 @@ struct rtl9300_i2c { struct rtl9300_i2c_chan chans[RTL9310_I2C_MUX_NCHAN]; struct regmap_field *fields[F_NUM_FIELDS]; u32 reg_base; - u32 data_reg; + u32 rd_reg; + u32 wd_reg; u8 scl_num; u8 sda_num; struct mutex lock; + struct clk *clk; }; DEFINE_GUARD(rtl9300_i2c, struct rtl9300_i2c *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock)) @@ -99,6 +122,7 @@ struct rtl9300_i2c_xfer { #define RTL9300_I2C_MST_DATA_WORD2 0x10 #define RTL9300_I2C_MST_DATA_WORD3 0x14 #define RTL9300_I2C_MST_GLB_CTRL 0x384 +#define RTL9300_REG_ADDR_8BIT_LEN 1 #define RTL9310_I2C_MST_IF_CTRL 0x1004 #define RTL9310_I2C_MST_IF_SEL 0x1008 @@ -106,6 +130,14 @@ struct rtl9300_i2c_xfer { #define RTL9310_I2C_MST_MEMADDR_CTRL 0x4 #define RTL9310_I2C_MST_DATA_CTRL 0x8 +#define RTL9607_I2C_CONFIG 0x22f50 +#define RTL9607_IO_MODE_EN 0x23014 +#define RTL9607_I2C_IND_WD 0x0 +#define RTL9607_I2C_IND_ADR 0x8 +#define RTL9607_I2C_IND_CMD 0x10 +#define RTL9607_I2C_IND_RD 0x18 +#define RTL9607_REG_ADDR_8BIT_LEN 0 + static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len) { int ret; @@ -157,6 +189,58 @@ static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c return 0; } +static int rtl9607_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan) +{ + const struct rtl9300_i2c_drv_data *drv_data; + int ret; + + if (i2c->sda_num == chan->sda_num) + return 0; + + ret = regmap_field_write(i2c->fields[F_CLK_DIV], chan->clk_div); + if (ret) + return ret; + + drv_data = device_get_match_data(i2c->dev); + ret = drv_data->select_scl(i2c, i2c->scl_num); + if (ret) + return ret; + + i2c->sda_num = chan->sda_num; + return 0; +} + +static void rtl9300_i2c_config_clock(u32 clock_freq, struct rtl9300_i2c_chan *chan) +{ + struct rtl9300_i2c *i2c = chan->i2c; + + switch (clock_freq) { + case I2C_MAX_STANDARD_MODE_FREQ: + chan->bus_freq = RTL9300_I2C_STD_FREQ; + break; + case I2C_MAX_FAST_MODE_FREQ: + chan->bus_freq = RTL9300_I2C_FAST_FREQ; + break; + case RTL9300_I2C_MAX_SUPER_FAST_FREQ: + chan->bus_freq = RTL9300_I2C_SUPER_FAST_FREQ; + break; + case RTL9300_I2C_MAX_SLOW_FREQ: + chan->bus_freq = RTL9300_I2C_SLOW_FREQ; + break; + default: + dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n", + chan->sda_num, clock_freq); + break; + } +} + +static void rtl9607_i2c_config_clock(u32 clock_freq, struct rtl9300_i2c_chan *chan) +{ + struct rtl9300_i2c *i2c = chan->i2c; + + chan->clk_div = clk_get_rate(i2c->clk) / clock_freq - 1; +} + static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len) { u32 vals[4] = {}; @@ -165,7 +249,7 @@ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len) if (len > 16) return -EIO; - ret = regmap_bulk_read(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals)); + ret = regmap_bulk_read(i2c->regmap, i2c->rd_reg, vals, ARRAY_SIZE(vals)); if (ret) return ret; @@ -192,19 +276,21 @@ static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, u8 len) vals[reg] |= buf[i] << shift; } - return regmap_bulk_write(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals)); + return regmap_bulk_write(i2c->regmap, i2c->wd_reg, vals, ARRAY_SIZE(vals)); } static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data) { - return regmap_write(i2c->regmap, i2c->data_reg, data); + return regmap_write(i2c->regmap, i2c->wd_reg, data); } static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer) { + const struct rtl9300_i2c_drv_data *drv_data; int ret; - if (xfer->data_len < 1 || xfer->data_len > 16) + drv_data = device_get_match_data(i2c->dev); + if (xfer->data_len < 1 || xfer->data_len > drv_data->max_data_len) return -EINVAL; ret = regmap_field_write(i2c->fields[F_DEV_ADDR], xfer->dev_addr); @@ -249,7 +335,7 @@ static int rtl9300_i2c_do_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer if (ret) return ret; - ret = regmap_field_read_poll_timeout(i2c->fields[F_I2C_TRIG], val, !val, 100, 100000); + ret = regmap_field_read_poll_timeout(i2c->fields[F_BUSY], val, !val, 100, 100000); if (ret) return ret; @@ -262,14 +348,14 @@ static int rtl9300_i2c_do_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer if (!xfer->write) { switch (xfer->type) { case RTL9300_I2C_XFER_BYTE: - ret = regmap_read(i2c->regmap, i2c->data_reg, &val); + ret = regmap_read(i2c->regmap, i2c->rd_reg, &val); if (ret) return ret; *xfer->data = val & 0xff; break; case RTL9300_I2C_XFER_WORD: - ret = regmap_read(i2c->regmap, i2c->data_reg, &val); + ret = regmap_read(i2c->regmap, i2c->rd_reg, &val); if (ret) return ret; @@ -291,6 +377,7 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s union i2c_smbus_data *data) { struct rtl9300_i2c_chan *chan = i2c_get_adapdata(adap); + const struct rtl9300_i2c_drv_data *drv_data; struct rtl9300_i2c *i2c = chan->i2c; struct rtl9300_i2c_xfer xfer = {0}; int ret; @@ -300,14 +387,15 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s guard(rtl9300_i2c)(i2c); - ret = rtl9300_i2c_config_chan(i2c, chan); + drv_data = device_get_match_data(i2c->dev); + ret = drv_data->config_chan(i2c, chan); if (ret) return ret; xfer.dev_addr = addr & 0x7f; xfer.write = (read_write == I2C_SMBUS_WRITE); xfer.reg_addr = command; - xfer.reg_addr_len = 1; + xfer.reg_addr_len = drv_data->reg_addr_8bit_len; switch (size) { case I2C_SMBUS_BYTE: @@ -367,6 +455,17 @@ static struct i2c_adapter_quirks rtl9300_i2c_quirks = { .max_write_len = 16, }; +static int rtl9300_i2c_init(struct rtl9300_i2c *i2c) +{ + /* only use standard read format */ + return regmap_field_write(i2c->fields[F_RD_MODE], 0); +} + +static int rtl9607_i2c_init(struct rtl9300_i2c *i2c) +{ + return regmap_field_write(i2c->fields[F_EXT_SCK_5MS], 1); +} + static int rtl9300_i2c_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -402,7 +501,8 @@ static int rtl9300_i2c_probe(struct platform_device *pdev) if (device_get_child_node_count(dev) > drv_data->max_nchan) return dev_err_probe(dev, -EINVAL, "Too many channels\n"); - i2c->data_reg = i2c->reg_base + drv_data->data_reg; + i2c->rd_reg = i2c->reg_base + drv_data->rd_reg; + i2c->wd_reg = i2c->reg_base + drv_data->wd_reg; for (i = 0; i < F_NUM_FIELDS; i++) { fields[i] = drv_data->field_desc[i].field; if (drv_data->field_desc[i].scope == REG_SCOPE_MASTER) @@ -413,6 +513,10 @@ static int rtl9300_i2c_probe(struct platform_device *pdev) if (ret) return ret; + i2c->clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(i2c->clk)) + return dev_err_probe(dev, PTR_ERR(i2c->clk), "Failed to enable i2c clock\n"); + i = 0; for_each_child_of_node_scoped(dev->of_node, child) { struct rtl9300_i2c_chan *chan = &i2c->chans[i]; @@ -426,21 +530,11 @@ static int rtl9300_i2c_probe(struct platform_device *pdev) if (ret) clock_freq = I2C_MAX_STANDARD_MODE_FREQ; - switch (clock_freq) { - case I2C_MAX_STANDARD_MODE_FREQ: - chan->bus_freq = RTL9300_I2C_STD_FREQ; - break; - case I2C_MAX_FAST_MODE_FREQ: - chan->bus_freq = RTL9300_I2C_FAST_FREQ; - break; - default: - dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n", - sda_num, clock_freq); - break; - } - chan->sda_num = sda_num; chan->i2c = i2c; + + drv_data->config_clock(clock_freq, chan); + adap = &i2c->chans[i].adap; adap->owner = THIS_MODULE; adap->algo = &rtl9300_i2c_algo; @@ -458,8 +552,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev) } i2c->sda_num = 0xff; - /* only use standard read format */ - ret = regmap_field_write(i2c->fields[F_RD_MODE], 0); + ret = drv_data->misc_init(i2c); if (ret) return ret; @@ -485,10 +578,17 @@ static const struct rtl9300_i2c_drv_data rtl9300_i2c_drv_data = { [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 2, 3), [F_SCL_FREQ] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 0, 1), [F_SDA_SEL] = GLB_REG_FIELD(RTL9300_I2C_MST_GLB_CTRL, 0, 7), + [F_BUSY] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 0, 0), }, .select_scl = rtl9300_i2c_select_scl, - .data_reg = RTL9300_I2C_MST_DATA_WORD0, + .config_chan = rtl9300_i2c_config_chan, + .config_clock = rtl9300_i2c_config_clock, + .misc_init = rtl9300_i2c_init, + .rd_reg = RTL9300_I2C_MST_DATA_WORD0, + .wd_reg = RTL9300_I2C_MST_DATA_WORD0, .max_nchan = RTL9300_I2C_MUX_NCHAN, + .max_data_len = RTL9300_I2C_MAX_DATA_LEN, + .reg_addr_8bit_len = RTL9300_REG_ADDR_8BIT_LEN, }; static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = { @@ -505,10 +605,42 @@ static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = { [F_I2C_FAIL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 1, 1), [F_I2C_TRIG] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0), [F_MEM_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_MEMADDR_CTRL, 0, 23), + [F_BUSY] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0), }, .select_scl = rtl9310_i2c_select_scl, - .data_reg = RTL9310_I2C_MST_DATA_CTRL, + .config_chan = rtl9300_i2c_config_chan, + .config_clock = rtl9300_i2c_config_clock, + .misc_init = rtl9300_i2c_init, + .rd_reg = RTL9310_I2C_MST_DATA_CTRL, + .wd_reg = RTL9310_I2C_MST_DATA_CTRL, .max_nchan = RTL9310_I2C_MUX_NCHAN, + .max_data_len = RTL9300_I2C_MAX_DATA_LEN, + .reg_addr_8bit_len = RTL9300_REG_ADDR_8BIT_LEN, +}; + +static const struct rtl9300_i2c_drv_data rtl9607_i2c_drv_data = { + .field_desc = { + [F_SCL_SEL] = GLB_REG_FIELD(RTL9607_IO_MODE_EN, 13, 14), + [F_EXT_SCK_5MS] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 26, 26), + [F_DEV_ADDR] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 14, 20), + [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 12, 13), + [F_DATA_WIDTH] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 10, 11), + [F_CLK_DIV] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 0, 9), + [F_I2C_FAIL] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 3, 3), + [F_BUSY] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 2, 2), + [F_RWOP] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 1, 1), + [F_I2C_TRIG] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 0, 0), + [F_MEM_ADDR] = MST_REG_FIELD(RTL9607_I2C_IND_ADR, 0, 31), + }, + .select_scl = rtl9310_i2c_select_scl, + .config_chan = rtl9607_i2c_config_chan, + .config_clock = rtl9607_i2c_config_clock, + .misc_init = rtl9607_i2c_init, + .rd_reg = RTL9607_I2C_IND_RD, + .wd_reg = RTL9607_I2C_IND_WD, + .max_nchan = RTL9607_I2C_MUX_NCHAN, + .max_data_len = RTL9607_I2C_MAX_DATA_LEN, + .reg_addr_8bit_len = RTL9607_REG_ADDR_8BIT_LEN, }; static const struct of_device_id i2c_rtl9300_dt_ids[] = { @@ -520,6 +652,7 @@ static const struct of_device_id i2c_rtl9300_dt_ids[] = { { .compatible = "realtek,rtl9311-i2c", .data = (void *) &rtl9310_i2c_drv_data }, { .compatible = "realtek,rtl9312-i2c", .data = (void *) &rtl9310_i2c_drv_data }, { .compatible = "realtek,rtl9313-i2c", .data = (void *) &rtl9310_i2c_drv_data }, + { .compatible = "realtek,rtl9607-i2c", .data = (void *) &rtl9607_i2c_drv_data }, {} }; MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids); diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 8138f5ef40f0..15e14a6fe6dc 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -503,8 +503,13 @@ static void i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) i2c->msg->buf[i2c->msg_ptr++] = byte; /* Add actual length to read for smbus block read */ - if (i2c->msg->flags & I2C_M_RECV_LEN && i2c->msg->len == 1) + if (i2c->msg->flags & I2C_M_RECV_LEN && i2c->msg->len == 1) { + if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX) { + s3c24xx_i2c_stop(i2c, -EPROTO); + break; + } i2c->msg->len += byte; + } prepare_read: if (is_msglast(i2c)) { /* last byte of buffer */ diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 70cb5822bf17..53d9df70ebe4 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -895,8 +895,6 @@ static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev, f7_msg->result = 0; f7_msg->stop = (i2c_dev->msg_id >= i2c_dev->msg_num - 1); - reinit_completion(&i2c_dev->complete); - cr1 = readl_relaxed(base + STM32F7_I2C_CR1); cr2 = readl_relaxed(base + STM32F7_I2C_CR2); @@ -1728,6 +1726,8 @@ static int stm32f7_i2c_xfer_core(struct i2c_adapter *i2c_adap, if (ret) goto pm_free; + reinit_completion(&i2c_dev->complete); + stm32f7_i2c_xfer_msg(i2c_dev, msgs); if (!i2c_dev->atomic) @@ -2253,7 +2253,7 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) snprintf(adap->name, sizeof(adap->name), "STM32F7 I2C(%pa)", &res->start); adap->owner = THIS_MODULE; - adap->timeout = 2 * HZ; + adap->timeout = 8 * HZ; adap->retries = 3; adap->algo = &stm32f7_i2c_algo; adap->dev.parent = &pdev->dev; diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 4eaeb395d5db..9fd5ade774a0 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -30,38 +30,29 @@ #define BYTES_PER_FIFO_WORD 4 -#define I2C_CNFG 0x000 #define I2C_CNFG_DEBOUNCE_CNT GENMASK(14, 12) #define I2C_CNFG_PACKET_MODE_EN BIT(10) #define I2C_CNFG_NEW_MASTER_FSM BIT(11) #define I2C_CNFG_MULTI_MASTER_MODE BIT(17) -#define I2C_STATUS 0x01c -#define I2C_SL_CNFG 0x020 + #define I2C_SL_CNFG_NACK BIT(1) #define I2C_SL_CNFG_NEWSL BIT(2) -#define I2C_SL_ADDR1 0x02c -#define I2C_SL_ADDR2 0x030 -#define I2C_TLOW_SEXT 0x034 -#define I2C_TX_FIFO 0x050 -#define I2C_RX_FIFO 0x054 -#define I2C_PACKET_TRANSFER_STATUS 0x058 -#define I2C_FIFO_CONTROL 0x05c + #define I2C_FIFO_CONTROL_TX_FLUSH BIT(1) #define I2C_FIFO_CONTROL_RX_FLUSH BIT(0) #define I2C_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 5) #define I2C_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 2) -#define I2C_FIFO_STATUS 0x060 + #define I2C_FIFO_STATUS_TX GENMASK(7, 4) #define I2C_FIFO_STATUS_RX GENMASK(3, 0) -#define I2C_INT_MASK 0x064 -#define I2C_INT_STATUS 0x068 + #define I2C_INT_BUS_CLR_DONE BIT(11) #define I2C_INT_PACKET_XFER_COMPLETE BIT(7) #define I2C_INT_NO_ACK BIT(3) #define I2C_INT_ARBITRATION_LOST BIT(2) #define I2C_INT_TX_FIFO_DATA_REQ BIT(1) #define I2C_INT_RX_FIFO_DATA_REQ BIT(0) -#define I2C_CLK_DIVISOR 0x06c + #define I2C_CLK_DIVISOR_STD_FAST_MODE GENMASK(31, 16) #define I2C_CLK_DIVISOR_HSMODE GENMASK(15, 0) @@ -94,50 +85,38 @@ #define I2C_HEADER_CONTINUE_XFER BIT(15) #define I2C_HEADER_SLAVE_ADDR_SHIFT 1 -#define I2C_BUS_CLEAR_CNFG 0x084 #define I2C_BC_SCLK_THRESHOLD GENMASK(23, 16) #define I2C_BC_STOP_COND BIT(2) #define I2C_BC_TERMINATE BIT(1) #define I2C_BC_ENABLE BIT(0) -#define I2C_BUS_CLEAR_STATUS 0x088 + #define I2C_BC_STATUS BIT(0) -#define I2C_CONFIG_LOAD 0x08c #define I2C_MSTR_CONFIG_LOAD BIT(0) -#define I2C_CLKEN_OVERRIDE 0x090 #define I2C_MST_CORE_CLKEN_OVR BIT(0) -#define I2C_INTERFACE_TIMING_0 0x094 -#define I2C_INTERFACE_TIMING_THIGH GENMASK(13, 8) -#define I2C_INTERFACE_TIMING_TLOW GENMASK(5, 0) -#define I2C_INTERFACE_TIMING_1 0x098 -#define I2C_INTERFACE_TIMING_TBUF GENMASK(29, 24) -#define I2C_INTERFACE_TIMING_TSU_STO GENMASK(21, 16) -#define I2C_INTERFACE_TIMING_THD_STA GENMASK(13, 8) -#define I2C_INTERFACE_TIMING_TSU_STA GENMASK(5, 0) +#define I2C_INTERFACE_TIMING_THIGH GENMASK(13, 8) +#define I2C_INTERFACE_TIMING_TLOW GENMASK(5, 0) +#define I2C_INTERFACE_TIMING_TBUF GENMASK(29, 24) +#define I2C_INTERFACE_TIMING_TSU_STO GENMASK(21, 16) +#define I2C_INTERFACE_TIMING_THD_STA GENMASK(13, 8) +#define I2C_INTERFACE_TIMING_TSU_STA GENMASK(5, 0) -#define I2C_HS_INTERFACE_TIMING_0 0x09c -#define I2C_HS_INTERFACE_TIMING_THIGH GENMASK(13, 8) -#define I2C_HS_INTERFACE_TIMING_TLOW GENMASK(5, 0) -#define I2C_HS_INTERFACE_TIMING_1 0x0a0 -#define I2C_HS_INTERFACE_TIMING_TSU_STO GENMASK(21, 16) -#define I2C_HS_INTERFACE_TIMING_THD_STA GENMASK(13, 8) -#define I2C_HS_INTERFACE_TIMING_TSU_STA GENMASK(5, 0) +#define I2C_HS_INTERFACE_TIMING_THIGH GENMASK(13, 8) +#define I2C_HS_INTERFACE_TIMING_TLOW GENMASK(5, 0) +#define I2C_HS_INTERFACE_TIMING_TSU_STO GENMASK(21, 16) +#define I2C_HS_INTERFACE_TIMING_THD_STA GENMASK(13, 8) +#define I2C_HS_INTERFACE_TIMING_TSU_STA GENMASK(5, 0) -#define I2C_MST_FIFO_CONTROL 0x0b4 #define I2C_MST_FIFO_CONTROL_RX_FLUSH BIT(0) #define I2C_MST_FIFO_CONTROL_TX_FLUSH BIT(1) #define I2C_MST_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 4) #define I2C_MST_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 16) -#define I2C_MST_FIFO_STATUS 0x0b8 #define I2C_MST_FIFO_STATUS_TX GENMASK(23, 16) #define I2C_MST_FIFO_STATUS_RX GENMASK(7, 0) -#define I2C_MASTER_RESET_CNTRL 0x0a8 - -#define I2C_SW_MUTEX 0x0ec #define I2C_SW_MUTEX_REQUEST GENMASK(3, 0) #define I2C_SW_MUTEX_GRANT GENMASK(7, 4) #define I2C_SW_MUTEX_ID_CCPLEX 9 @@ -159,6 +138,171 @@ */ #define I2C_PIO_MODE_PREFERRED_LEN 32 +struct tegra_i2c_regs { + unsigned int cnfg; + unsigned int status; + unsigned int sl_cnfg; + unsigned int sl_addr1; + unsigned int sl_addr2; + unsigned int tlow_sext; + unsigned int tx_fifo; + unsigned int rx_fifo; + unsigned int packet_transfer_status; + unsigned int fifo_control; + unsigned int fifo_status; + unsigned int int_mask; + unsigned int int_status; + unsigned int clk_divisor; + unsigned int bus_clear_cnfg; + unsigned int bus_clear_status; + unsigned int config_load; + unsigned int clken_override; + unsigned int interface_timing_0; + unsigned int interface_timing_1; + unsigned int hs_interface_timing_0; + unsigned int hs_interface_timing_1; + unsigned int master_reset_cntrl; + unsigned int mst_fifo_control; + unsigned int mst_fifo_status; + unsigned int sw_mutex; +}; + +static const struct tegra_i2c_regs tegra20_i2c_regs = { + .cnfg = 0x000, + .status = 0x01c, + .sl_cnfg = 0x020, + .sl_addr1 = 0x02c, + .sl_addr2 = 0x030, + .tx_fifo = 0x050, + .rx_fifo = 0x054, + .packet_transfer_status = 0x058, + .fifo_control = 0x05c, + .fifo_status = 0x060, + .int_mask = 0x064, + .int_status = 0x068, + .clk_divisor = 0x06c, + .bus_clear_cnfg = 0x084, + .bus_clear_status = 0x088, + .config_load = 0x08c, + .clken_override = 0x090, + .interface_timing_0 = 0x094, + .interface_timing_1 = 0x098, + .hs_interface_timing_0 = 0x09c, + .hs_interface_timing_1 = 0x0a0, + .master_reset_cntrl = 0x0a8, + .mst_fifo_control = 0x0b4, + .mst_fifo_status = 0x0b8, +}; + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) +static const struct tegra_i2c_regs tegra20_dvc_i2c_regs = { + .cnfg = 0x040, + .status = 0x05c, + .tx_fifo = 0x060, + .rx_fifo = 0x064, + .packet_transfer_status = 0x068, + .fifo_control = 0x06c, + .fifo_status = 0x070, + .int_mask = 0x074, + .int_status = 0x078, + .clk_divisor = 0x07c, + .bus_clear_cnfg = 0x094, + .bus_clear_status = 0x098, + .config_load = 0x09c, + .clken_override = 0x0a0, + .interface_timing_0 = 0x0a4, + .interface_timing_1 = 0x0a8, + .hs_interface_timing_0 = 0x0ac, + .hs_interface_timing_1 = 0x0b0, + .master_reset_cntrl = 0x0b8, + .mst_fifo_control = 0x0c4, + .mst_fifo_status = 0x0c8, +}; +#endif + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) +static const struct tegra_i2c_regs tegra210_vi_i2c_regs = { + .cnfg = 0x0c00, + .status = 0x0c70, + .tlow_sext = 0x0cd0, + .tx_fifo = 0x0d40, + .rx_fifo = 0x0d50, + .packet_transfer_status = 0x0d60, + .fifo_control = 0x0d70, + .fifo_status = 0x0d80, + .int_mask = 0x0d90, + .int_status = 0x0da0, + .clk_divisor = 0x0db0, + .bus_clear_cnfg = 0x0e10, + .bus_clear_status = 0x0e20, + .config_load = 0x0e30, + .clken_override = 0x0e40, + .interface_timing_0 = 0x0e50, + .interface_timing_1 = 0x0e60, + .hs_interface_timing_0 = 0x0e70, + .hs_interface_timing_1 = 0x0e80, + .master_reset_cntrl = 0x0ea0, + .mst_fifo_control = 0x0ed0, + .mst_fifo_status = 0x0ee0, +}; +#endif + +static const struct tegra_i2c_regs tegra264_i2c_regs = { + .cnfg = 0x000, + .status = 0x01c, + .sl_cnfg = 0x020, + .sl_addr1 = 0x02c, + .sl_addr2 = 0x030, + .tx_fifo = 0x050, + .rx_fifo = 0x054, + .packet_transfer_status = 0x058, + .fifo_control = 0x05c, + .fifo_status = 0x060, + .int_mask = 0x064, + .int_status = 0x068, + .clk_divisor = 0x06c, + .bus_clear_cnfg = 0x084, + .bus_clear_status = 0x088, + .config_load = 0x08c, + .clken_override = 0x090, + .interface_timing_0 = 0x094, + .interface_timing_1 = 0x098, + .hs_interface_timing_0 = 0x09c, + .hs_interface_timing_1 = 0x0a0, + .master_reset_cntrl = 0x0a8, + .mst_fifo_control = 0x0b4, + .mst_fifo_status = 0x0b8, + .sw_mutex = 0x0ec, +}; + +static const struct tegra_i2c_regs tegra410_i2c_regs = { + .cnfg = 0x000, + .status = 0x01c, + .sl_cnfg = 0x020, + .sl_addr1 = 0x02c, + .sl_addr2 = 0x030, + .tx_fifo = 0x054, + .rx_fifo = 0x058, + .packet_transfer_status = 0x05c, + .fifo_control = 0x060, + .fifo_status = 0x064, + .int_mask = 0x068, + .int_status = 0x06c, + .clk_divisor = 0x070, + .bus_clear_cnfg = 0x088, + .bus_clear_status = 0x08c, + .config_load = 0x090, + .clken_override = 0x094, + .interface_timing_0 = 0x098, + .interface_timing_1 = 0x09c, + .hs_interface_timing_0 = 0x0a0, + .hs_interface_timing_1 = 0x0a4, + .master_reset_cntrl = 0x0ac, + .mst_fifo_control = 0x0b8, + .mst_fifo_status = 0x0bc, + .sw_mutex = 0x0f0, +}; + /* * msg_end_type: The bus control which needs to be sent at end of transfer. * @MSG_END_STOP: Send stop pulse. @@ -171,6 +315,18 @@ enum msg_end_type { MSG_END_CONTINUE, }; +/* + * tegra_i2c_variant: Identifies the variant of I2C controller. + * @TEGRA_I2C_VARIANT_DEFAULT: Identifies the default I2C controller. + * @TEGRA_I2C_VARIANT_DVC: Identifies the DVC I2C controller, has a different register layout. + * @TEGRA_I2C_VARIANT_VI: Identifies the VI I2C controller, has a different register layout. + */ +enum tegra_i2c_variant { + TEGRA_I2C_VARIANT_DEFAULT, + TEGRA_I2C_VARIANT_DVC, + TEGRA_I2C_VARIANT_VI, +}; + /** * struct tegra_i2c_hw_feature : per hardware generation features * @has_continue_xfer_support: continue-transfer supported @@ -223,6 +379,8 @@ enum msg_end_type { * timing settings. * @enable_hs_mode_support: Enable support for high speed (HS) mode transfers. * @has_mutex: Has mutex register for mutual exclusion with other firmwares or VMs. + * @variant: This represents the I2C controller variant. + * @regs: Register offsets for the specific SoC variant. */ struct tegra_i2c_hw_feature { bool has_continue_xfer_support; @@ -254,6 +412,8 @@ struct tegra_i2c_hw_feature { bool has_interface_timing_reg; bool enable_hs_mode_support; bool has_mutex; + enum tegra_i2c_variant variant; + const struct tegra_i2c_regs *regs; }; /** @@ -268,8 +428,6 @@ struct tegra_i2c_hw_feature { * @base_phys: physical base address of the I2C controller * @cont_id: I2C controller ID, used for packet header * @irq: IRQ number of transfer complete interrupt - * @is_dvc: identifies the DVC I2C controller, has a different register layout - * @is_vi: identifies the VI I2C controller, has a different register layout * @msg_complete: transfer completion notifier * @msg_buf_remaining: size of unsent data in the message buffer * @msg_len: length of message in current transfer @@ -321,12 +479,12 @@ struct tegra_i2c_dev { bool atomic_mode; bool dma_mode; bool msg_read; - bool is_dvc; - bool is_vi; }; -#define IS_DVC(dev) (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && (dev)->is_dvc) -#define IS_VI(dev) (IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) && (dev)->is_vi) +#define IS_DVC(dev) (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && \ + (dev)->hw->variant == TEGRA_I2C_VARIANT_DVC) +#define IS_VI(dev) (IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) && \ + (dev)->hw->variant == TEGRA_I2C_VARIANT_VI) static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned int reg) @@ -339,40 +497,26 @@ static u32 dvc_readl(struct tegra_i2c_dev *i2c_dev, unsigned int reg) return readl_relaxed(i2c_dev->base + reg); } -/* - * If necessary, i2c_writel() and i2c_readl() will offset the register - * in order to talk to the I2C block inside the DVC block. - */ -static u32 tegra_i2c_reg_addr(struct tegra_i2c_dev *i2c_dev, unsigned int reg) -{ - if (IS_DVC(i2c_dev)) - reg += (reg >= I2C_TX_FIFO) ? 0x10 : 0x40; - else if (IS_VI(i2c_dev)) - reg = 0xc00 + (reg << 2); - - return reg; -} - static void i2c_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned int reg) { - writel_relaxed(val, i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg)); + writel_relaxed(val, i2c_dev->base + reg); /* read back register to make sure that register writes completed */ - if (reg != I2C_TX_FIFO) - readl_relaxed(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg)); + if (reg != i2c_dev->hw->regs->tx_fifo) + readl_relaxed(i2c_dev->base + reg); else if (IS_VI(i2c_dev)) - readl_relaxed(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, I2C_INT_STATUS)); + readl_relaxed(i2c_dev->base + i2c_dev->hw->regs->int_status); } static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned int reg) { - return readl_relaxed(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg)); + return readl_relaxed(i2c_dev->base + reg); } static void i2c_writesl(struct tegra_i2c_dev *i2c_dev, void *data, unsigned int reg, unsigned int len) { - writesl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len); + writesl(i2c_dev->base + reg, data, len); } static void i2c_writesl_vi(struct tegra_i2c_dev *i2c_dev, void *data, @@ -393,12 +537,12 @@ static void i2c_writesl_vi(struct tegra_i2c_dev *i2c_dev, void *data, static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data, unsigned int reg, unsigned int len) { - readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len); + readsl(i2c_dev->base + reg, data, len); } static bool tegra_i2c_mutex_acquired(struct tegra_i2c_dev *i2c_dev) { - unsigned int reg = tegra_i2c_reg_addr(i2c_dev, I2C_SW_MUTEX); + unsigned int reg = i2c_dev->hw->regs->sw_mutex; u32 val, id; val = readl(i2c_dev->base + reg); @@ -409,7 +553,7 @@ static bool tegra_i2c_mutex_acquired(struct tegra_i2c_dev *i2c_dev) static bool tegra_i2c_mutex_trylock(struct tegra_i2c_dev *i2c_dev) { - unsigned int reg = tegra_i2c_reg_addr(i2c_dev, I2C_SW_MUTEX); + unsigned int reg = i2c_dev->hw->regs->sw_mutex; u32 val, id; val = readl(i2c_dev->base + reg); @@ -447,7 +591,7 @@ static int tegra_i2c_mutex_lock(struct tegra_i2c_dev *i2c_dev) static int tegra_i2c_mutex_unlock(struct tegra_i2c_dev *i2c_dev) { - unsigned int reg = tegra_i2c_reg_addr(i2c_dev, I2C_SW_MUTEX); + unsigned int reg = i2c_dev->hw->regs->sw_mutex; u32 val, id; if (!i2c_dev->hw->has_mutex) @@ -470,16 +614,16 @@ static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) { u32 int_mask; - int_mask = i2c_readl(i2c_dev, I2C_INT_MASK) & ~mask; - i2c_writel(i2c_dev, int_mask, I2C_INT_MASK); + int_mask = i2c_readl(i2c_dev, i2c_dev->hw->regs->int_mask) & ~mask; + i2c_writel(i2c_dev, int_mask, i2c_dev->hw->regs->int_mask); } static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) { u32 int_mask; - int_mask = i2c_readl(i2c_dev, I2C_INT_MASK) | mask; - i2c_writel(i2c_dev, int_mask, I2C_INT_MASK); + int_mask = i2c_readl(i2c_dev, i2c_dev->hw->regs->int_mask) | mask; + i2c_writel(i2c_dev, int_mask, i2c_dev->hw->regs->int_mask); } static void tegra_i2c_dma_complete(void *args) @@ -623,34 +767,34 @@ static void tegra_i2c_vi_init(struct tegra_i2c_dev *i2c_dev) value = FIELD_PREP(I2C_INTERFACE_TIMING_THIGH, 2) | FIELD_PREP(I2C_INTERFACE_TIMING_TLOW, 4); - i2c_writel(i2c_dev, value, I2C_INTERFACE_TIMING_0); + i2c_writel(i2c_dev, value, i2c_dev->hw->regs->interface_timing_0); value = FIELD_PREP(I2C_INTERFACE_TIMING_TBUF, 4) | FIELD_PREP(I2C_INTERFACE_TIMING_TSU_STO, 7) | FIELD_PREP(I2C_INTERFACE_TIMING_THD_STA, 4) | FIELD_PREP(I2C_INTERFACE_TIMING_TSU_STA, 4); - i2c_writel(i2c_dev, value, I2C_INTERFACE_TIMING_1); + i2c_writel(i2c_dev, value, i2c_dev->hw->regs->interface_timing_1); value = FIELD_PREP(I2C_HS_INTERFACE_TIMING_THIGH, 3) | FIELD_PREP(I2C_HS_INTERFACE_TIMING_TLOW, 8); - i2c_writel(i2c_dev, value, I2C_HS_INTERFACE_TIMING_0); + i2c_writel(i2c_dev, value, i2c_dev->hw->regs->hs_interface_timing_0); value = FIELD_PREP(I2C_HS_INTERFACE_TIMING_TSU_STO, 11) | FIELD_PREP(I2C_HS_INTERFACE_TIMING_THD_STA, 11) | FIELD_PREP(I2C_HS_INTERFACE_TIMING_TSU_STA, 11); - i2c_writel(i2c_dev, value, I2C_HS_INTERFACE_TIMING_1); + i2c_writel(i2c_dev, value, i2c_dev->hw->regs->hs_interface_timing_1); value = FIELD_PREP(I2C_BC_SCLK_THRESHOLD, 9) | I2C_BC_STOP_COND; - i2c_writel(i2c_dev, value, I2C_BUS_CLEAR_CNFG); + i2c_writel(i2c_dev, value, i2c_dev->hw->regs->bus_clear_cnfg); - i2c_writel(i2c_dev, 0x0, I2C_TLOW_SEXT); + i2c_writel(i2c_dev, 0x0, i2c_dev->hw->regs->tlow_sext); } static int tegra_i2c_poll_register(struct tegra_i2c_dev *i2c_dev, u32 reg, u32 mask, u32 delay_us, u32 timeout_us) { - void __iomem *addr = i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg); + void __iomem *addr = i2c_dev->base + reg; u32 val; if (!i2c_dev->atomic_mode) @@ -669,11 +813,11 @@ static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev) if (i2c_dev->hw->has_mst_fifo) { mask = I2C_MST_FIFO_CONTROL_TX_FLUSH | I2C_MST_FIFO_CONTROL_RX_FLUSH; - offset = I2C_MST_FIFO_CONTROL; + offset = i2c_dev->hw->regs->mst_fifo_control; } else { mask = I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH; - offset = I2C_FIFO_CONTROL; + offset = i2c_dev->hw->regs->fifo_control; } val = i2c_readl(i2c_dev, offset); @@ -696,9 +840,9 @@ static int tegra_i2c_wait_for_config_load(struct tegra_i2c_dev *i2c_dev) if (!i2c_dev->hw->has_config_load_reg) return 0; - i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, I2C_CONFIG_LOAD); + i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, i2c_dev->hw->regs->config_load); - err = tegra_i2c_poll_register(i2c_dev, I2C_CONFIG_LOAD, 0xffffffff, + err = tegra_i2c_poll_register(i2c_dev, i2c_dev->hw->regs->config_load, 0xffffffff, 1000, I2C_CONFIG_LOAD_TIMEOUT); if (err) { dev_err(i2c_dev->dev, "failed to load config\n"); @@ -719,10 +863,10 @@ static int tegra_i2c_master_reset(struct tegra_i2c_dev *i2c_dev) * SW needs to wait for 2us after assertion and de-assertion of this soft * reset. */ - i2c_writel(i2c_dev, 0x1, I2C_MASTER_RESET_CNTRL); + i2c_writel(i2c_dev, 0x1, i2c_dev->hw->regs->master_reset_cntrl); fsleep(2); - i2c_writel(i2c_dev, 0x0, I2C_MASTER_RESET_CNTRL); + i2c_writel(i2c_dev, 0x0, i2c_dev->hw->regs->master_reset_cntrl); fsleep(2); return 0; @@ -764,8 +908,8 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) if (i2c_dev->hw->has_multi_master_mode) val |= I2C_CNFG_MULTI_MASTER_MODE; - i2c_writel(i2c_dev, val, I2C_CNFG); - i2c_writel(i2c_dev, 0, I2C_INT_MASK); + i2c_writel(i2c_dev, val, i2c_dev->hw->regs->cnfg); + i2c_writel(i2c_dev, 0, i2c_dev->hw->regs->int_mask); if (IS_VI(i2c_dev)) tegra_i2c_vi_init(i2c_dev); @@ -810,12 +954,12 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) clk_divisor = FIELD_PREP(I2C_CLK_DIVISOR_HSMODE, i2c_dev->hw->clk_divisor_hs_mode) | FIELD_PREP(I2C_CLK_DIVISOR_STD_FAST_MODE, non_hs_mode); - i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR); + i2c_writel(i2c_dev, clk_divisor, i2c_dev->hw->regs->clk_divisor); if (i2c_dev->hw->has_interface_timing_reg) { val = FIELD_PREP(I2C_INTERFACE_TIMING_THIGH, thigh) | FIELD_PREP(I2C_INTERFACE_TIMING_TLOW, tlow); - i2c_writel(i2c_dev, val, I2C_INTERFACE_TIMING_0); + i2c_writel(i2c_dev, val, i2c_dev->hw->regs->interface_timing_0); } /* @@ -823,7 +967,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) * Otherwise, preserve the chip default values. */ if (i2c_dev->hw->has_interface_timing_reg && tsu_thd) - i2c_writel(i2c_dev, tsu_thd, I2C_INTERFACE_TIMING_1); + i2c_writel(i2c_dev, tsu_thd, i2c_dev->hw->regs->interface_timing_1); /* Write HS mode registers. These will get used only for HS mode*/ if (i2c_dev->hw->enable_hs_mode_support) { @@ -833,8 +977,8 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) val = FIELD_PREP(I2C_HS_INTERFACE_TIMING_THIGH, thigh) | FIELD_PREP(I2C_HS_INTERFACE_TIMING_TLOW, tlow); - i2c_writel(i2c_dev, val, I2C_HS_INTERFACE_TIMING_0); - i2c_writel(i2c_dev, tsu_thd, I2C_HS_INTERFACE_TIMING_1); + i2c_writel(i2c_dev, val, i2c_dev->hw->regs->hs_interface_timing_0); + i2c_writel(i2c_dev, tsu_thd, i2c_dev->hw->regs->hs_interface_timing_1); } clk_multiplier = (tlow + thigh + 2) * (non_hs_mode + 1); @@ -847,12 +991,12 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) } if (!IS_DVC(i2c_dev) && !IS_VI(i2c_dev)) { - u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG); + u32 sl_cfg = i2c_readl(i2c_dev, i2c_dev->hw->regs->sl_cnfg); sl_cfg |= I2C_SL_CNFG_NACK | I2C_SL_CNFG_NEWSL; - i2c_writel(i2c_dev, sl_cfg, I2C_SL_CNFG); - i2c_writel(i2c_dev, 0xfc, I2C_SL_ADDR1); - i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2); + i2c_writel(i2c_dev, sl_cfg, i2c_dev->hw->regs->sl_cnfg); + i2c_writel(i2c_dev, 0xfc, i2c_dev->hw->regs->sl_addr1); + i2c_writel(i2c_dev, 0x00, i2c_dev->hw->regs->sl_addr2); } err = tegra_i2c_flush_fifos(i2c_dev); @@ -860,7 +1004,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) return err; if (i2c_dev->multimaster_mode && i2c_dev->hw->has_slcg_override_reg) - i2c_writel(i2c_dev, I2C_MST_CORE_CLKEN_OVR, I2C_CLKEN_OVERRIDE); + i2c_writel(i2c_dev, I2C_MST_CORE_CLKEN_OVR, i2c_dev->hw->regs->clken_override); err = tegra_i2c_wait_for_config_load(i2c_dev); if (err) @@ -881,9 +1025,9 @@ static int tegra_i2c_disable_packet_mode(struct tegra_i2c_dev *i2c_dev) */ udelay(DIV_ROUND_UP(2 * 1000000, i2c_dev->timings.bus_freq_hz)); - cnfg = i2c_readl(i2c_dev, I2C_CNFG); + cnfg = i2c_readl(i2c_dev, i2c_dev->hw->regs->cnfg); if (cnfg & I2C_CNFG_PACKET_MODE_EN) - i2c_writel(i2c_dev, cnfg & ~I2C_CNFG_PACKET_MODE_EN, I2C_CNFG); + i2c_writel(i2c_dev, cnfg & ~I2C_CNFG_PACKET_MODE_EN, i2c_dev->hw->regs->cnfg); return tegra_i2c_wait_for_config_load(i2c_dev); } @@ -903,10 +1047,10 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev) return -EINVAL; if (i2c_dev->hw->has_mst_fifo) { - val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS); + val = i2c_readl(i2c_dev, i2c_dev->hw->regs->mst_fifo_status); rx_fifo_avail = FIELD_GET(I2C_MST_FIFO_STATUS_RX, val); } else { - val = i2c_readl(i2c_dev, I2C_FIFO_STATUS); + val = i2c_readl(i2c_dev, i2c_dev->hw->regs->fifo_status); rx_fifo_avail = FIELD_GET(I2C_FIFO_STATUS_RX, val); } @@ -915,7 +1059,7 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev) if (words_to_transfer > rx_fifo_avail) words_to_transfer = rx_fifo_avail; - i2c_readsl(i2c_dev, buf, I2C_RX_FIFO, words_to_transfer); + i2c_readsl(i2c_dev, buf, i2c_dev->hw->regs->rx_fifo, words_to_transfer); buf += words_to_transfer * BYTES_PER_FIFO_WORD; buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD; @@ -931,7 +1075,7 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev) * when (words_to_transfer was > rx_fifo_avail) earlier * in this function. */ - val = i2c_readl(i2c_dev, I2C_RX_FIFO); + val = i2c_readl(i2c_dev, i2c_dev->hw->regs->rx_fifo); val = cpu_to_le32(val); memcpy(buf, &val, buf_remaining); buf_remaining = 0; @@ -956,10 +1100,10 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) u32 val; if (i2c_dev->hw->has_mst_fifo) { - val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS); + val = i2c_readl(i2c_dev, i2c_dev->hw->regs->mst_fifo_status); tx_fifo_avail = FIELD_GET(I2C_MST_FIFO_STATUS_TX, val); } else { - val = i2c_readl(i2c_dev, I2C_FIFO_STATUS); + val = i2c_readl(i2c_dev, i2c_dev->hw->regs->fifo_status); tx_fifo_avail = FIELD_GET(I2C_FIFO_STATUS_TX, val); } @@ -990,9 +1134,9 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) i2c_dev->msg_buf = buf + words_to_transfer * BYTES_PER_FIFO_WORD; if (IS_VI(i2c_dev)) - i2c_writesl_vi(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer); + i2c_writesl_vi(i2c_dev, buf, i2c_dev->hw->regs->tx_fifo, words_to_transfer); else - i2c_writesl(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer); + i2c_writesl(i2c_dev, buf, i2c_dev->hw->regs->tx_fifo, words_to_transfer); buf += words_to_transfer * BYTES_PER_FIFO_WORD; } @@ -1014,7 +1158,7 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) i2c_dev->msg_buf_remaining = 0; i2c_dev->msg_buf = NULL; - i2c_writel(i2c_dev, val, I2C_TX_FIFO); + i2c_writel(i2c_dev, val, i2c_dev->hw->regs->tx_fifo); } return 0; @@ -1026,13 +1170,13 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) struct tegra_i2c_dev *i2c_dev = dev_id; u32 status; - status = i2c_readl(i2c_dev, I2C_INT_STATUS); + status = i2c_readl(i2c_dev, i2c_dev->hw->regs->int_status); if (status == 0) { dev_warn(i2c_dev->dev, "IRQ status 0 %08x %08x %08x\n", - i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS), - i2c_readl(i2c_dev, I2C_STATUS), - i2c_readl(i2c_dev, I2C_CNFG)); + i2c_readl(i2c_dev, i2c_dev->hw->regs->packet_transfer_status), + i2c_readl(i2c_dev, i2c_dev->hw->regs->status), + i2c_readl(i2c_dev, i2c_dev->hw->regs->cnfg)); i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT; goto err; } @@ -1075,7 +1219,7 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) } } - i2c_writel(i2c_dev, status, I2C_INT_STATUS); + i2c_writel(i2c_dev, status, i2c_dev->hw->regs->int_status); if (IS_DVC(i2c_dev)) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); @@ -1113,7 +1257,7 @@ err: if (i2c_dev->hw->supports_bus_clear) tegra_i2c_mask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); - i2c_writel(i2c_dev, status, I2C_INT_STATUS); + i2c_writel(i2c_dev, status, i2c_dev->hw->regs->int_status); if (IS_DVC(i2c_dev)) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); @@ -1136,9 +1280,9 @@ static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev, int err; if (i2c_dev->hw->has_mst_fifo) - reg = I2C_MST_FIFO_CONTROL; + reg = i2c_dev->hw->regs->mst_fifo_control; else - reg = I2C_FIFO_CONTROL; + reg = i2c_dev->hw->regs->fifo_control; if (i2c_dev->dma_mode) { if (len & 0xF) @@ -1149,7 +1293,7 @@ static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev, dma_burst = 8; if (i2c_dev->msg_read) { - reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_RX_FIFO); + reg_offset = i2c_dev->hw->regs->rx_fifo; slv_config.src_addr = i2c_dev->base_phys + reg_offset; slv_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; @@ -1160,7 +1304,7 @@ static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev, else val = I2C_FIFO_CONTROL_RX_TRIG(dma_burst); } else { - reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_TX_FIFO); + reg_offset = i2c_dev->hw->regs->tx_fifo; slv_config.dst_addr = i2c_dev->base_phys + reg_offset; slv_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; @@ -1203,7 +1347,7 @@ static unsigned long tegra_i2c_poll_completion(struct tegra_i2c_dev *i2c_dev, ktime_t ktimeout = ktime_add_ms(ktime, timeout_ms); do { - u32 status = i2c_readl(i2c_dev, I2C_INT_STATUS); + u32 status = i2c_readl(i2c_dev, i2c_dev->hw->regs->int_status); if (status) tegra_i2c_isr(i2c_dev->irq, i2c_dev); @@ -1262,14 +1406,14 @@ static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap) val = FIELD_PREP(I2C_BC_SCLK_THRESHOLD, 9) | I2C_BC_STOP_COND | I2C_BC_TERMINATE; - i2c_writel(i2c_dev, val, I2C_BUS_CLEAR_CNFG); + i2c_writel(i2c_dev, val, i2c_dev->hw->regs->bus_clear_cnfg); err = tegra_i2c_wait_for_config_load(i2c_dev); if (err) return err; val |= I2C_BC_ENABLE; - i2c_writel(i2c_dev, val, I2C_BUS_CLEAR_CNFG); + i2c_writel(i2c_dev, val, i2c_dev->hw->regs->bus_clear_cnfg); tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); time_left = tegra_i2c_wait_completion(i2c_dev, &i2c_dev->msg_complete, 50); @@ -1280,7 +1424,7 @@ static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap) return -ETIMEDOUT; } - val = i2c_readl(i2c_dev, I2C_BUS_CLEAR_STATUS); + val = i2c_readl(i2c_dev, i2c_dev->hw->regs->bus_clear_status); if (!(val & I2C_BC_STATUS)) { dev_err(i2c_dev->dev, "un-recovered arbitration lost\n"); return -EIO; @@ -1305,14 +1449,14 @@ static void tegra_i2c_push_packet_header(struct tegra_i2c_dev *i2c_dev, if (i2c_dev->dma_mode && !i2c_dev->msg_read) *dma_buf++ = packet_header; else - i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + i2c_writel(i2c_dev, packet_header, i2c_dev->hw->regs->tx_fifo); packet_header = i2c_dev->msg_len - 1; if (i2c_dev->dma_mode && !i2c_dev->msg_read) *dma_buf++ = packet_header; else - i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + i2c_writel(i2c_dev, packet_header, i2c_dev->hw->regs->tx_fifo); packet_header = I2C_HEADER_IE_ENABLE; @@ -1340,7 +1484,7 @@ static void tegra_i2c_push_packet_header(struct tegra_i2c_dev *i2c_dev, if (i2c_dev->dma_mode && !i2c_dev->msg_read) *dma_buf++ = packet_header; else - i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + i2c_writel(i2c_dev, packet_header, i2c_dev->hw->regs->tx_fifo); } static int tegra_i2c_error_recover(struct tegra_i2c_dev *i2c_dev, @@ -1461,7 +1605,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, tegra_i2c_unmask_irq(i2c_dev, int_mask); dev_dbg(i2c_dev->dev, "unmasked IRQ: %02x\n", - i2c_readl(i2c_dev, I2C_INT_MASK)); + i2c_readl(i2c_dev, i2c_dev->hw->regs->int_mask)); if (i2c_dev->dma_mode) { time_left = tegra_i2c_wait_completion(i2c_dev, @@ -1635,8 +1779,44 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_interface_timing_reg = false, .enable_hs_mode_support = false, .has_mutex = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, }; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) +static const struct tegra_i2c_hw_feature tegra20_dvc_i2c_hw = { + .has_continue_xfer_support = false, + .has_per_pkt_xfer_complete_irq = false, + .clk_divisor_hs_mode = 3, + .clk_divisor_std_mode = 0, + .clk_divisor_fast_mode = 0, + .clk_divisor_fast_plus_mode = 0, + .has_config_load_reg = false, + .has_multi_master_mode = false, + .has_slcg_override_reg = false, + .has_mst_fifo = false, + .has_mst_reset = false, + .quirks = &tegra_i2c_quirks, + .supports_bus_clear = false, + .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_mode = 0x4, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x4, + .thigh_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_mode = 0x0, + .setup_hold_time_fastplus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, + .has_interface_timing_reg = false, + .enable_hs_mode_support = false, + .has_mutex = false, + .variant = TEGRA_I2C_VARIANT_DVC, + .regs = &tegra20_dvc_i2c_regs, +}; +#endif + static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_continue_xfer_support = true, .has_per_pkt_xfer_complete_irq = false, @@ -1665,6 +1845,8 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_interface_timing_reg = false, .enable_hs_mode_support = false, .has_mutex = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -1695,6 +1877,8 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_interface_timing_reg = false, .enable_hs_mode_support = false, .has_mutex = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -1725,6 +1909,8 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = false, .has_mutex = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -1755,8 +1941,44 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = false, .has_mutex = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, }; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) +static const struct tegra_i2c_hw_feature tegra210_vi_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .clk_divisor_hs_mode = 1, + .clk_divisor_std_mode = 0x19, + .clk_divisor_fast_mode = 0x19, + .clk_divisor_fast_plus_mode = 0x10, + .has_config_load_reg = true, + .has_multi_master_mode = false, + .has_slcg_override_reg = true, + .has_mst_fifo = false, + .has_mst_reset = false, + .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, + .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_mode = 0x4, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x4, + .thigh_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0, + .setup_hold_time_fast_mode = 0, + .setup_hold_time_fastplus_mode = 0, + .setup_hold_time_hs_mode = 0, + .has_interface_timing_reg = true, + .enable_hs_mode_support = false, + .has_mutex = false, + .variant = TEGRA_I2C_VARIANT_VI, + .regs = &tegra210_vi_i2c_regs, +}; +#endif + static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { .has_continue_xfer_support = true, .has_per_pkt_xfer_complete_irq = true, @@ -1785,6 +2007,8 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = false, .has_mutex = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, }; static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { @@ -1817,6 +2041,8 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = true, .has_mutex = false, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra20_i2c_regs, }; static const struct tegra_i2c_hw_feature tegra256_i2c_hw = { @@ -1849,6 +2075,8 @@ static const struct tegra_i2c_hw_feature tegra256_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = true, .has_mutex = true, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra264_i2c_regs, }; static const struct tegra_i2c_hw_feature tegra264_i2c_hw = { @@ -1881,6 +2109,42 @@ static const struct tegra_i2c_hw_feature tegra264_i2c_hw = { .has_interface_timing_reg = true, .enable_hs_mode_support = true, .has_mutex = true, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra264_i2c_regs, +}; + +static const struct tegra_i2c_hw_feature tegra410_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .clk_divisor_hs_mode = 1, + .clk_divisor_std_mode = 0x3f, + .clk_divisor_fast_mode = 0x2c, + .clk_divisor_fast_plus_mode = 0x11, + .has_config_load_reg = true, + .has_multi_master_mode = true, + .has_slcg_override_reg = true, + .has_mst_fifo = true, + .has_mst_reset = true, + .quirks = &tegra194_i2c_quirks, + .supports_bus_clear = true, + .has_apb_dma = false, + .tlow_std_mode = 0x8, + .thigh_std_mode = 0x7, + .tlow_fast_mode = 0x2, + .thigh_fast_mode = 0x2, + .tlow_fastplus_mode = 0x2, + .thigh_fastplus_mode = 0x2, + .tlow_hs_mode = 0x8, + .thigh_hs_mode = 0x6, + .setup_hold_time_std_mode = 0x08080808, + .setup_hold_time_fast_mode = 0x02020202, + .setup_hold_time_fastplus_mode = 0x02020202, + .setup_hold_time_hs_mode = 0x0b0b0b, + .has_interface_timing_reg = true, + .enable_hs_mode_support = true, + .has_mutex = true, + .variant = TEGRA_I2C_VARIANT_DEFAULT, + .regs = &tegra410_i2c_regs, }; static const struct of_device_id tegra_i2c_of_match[] = { @@ -1889,7 +2153,7 @@ static const struct of_device_id tegra_i2c_of_match[] = { { .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, }, { .compatible = "nvidia,tegra186-i2c", .data = &tegra186_i2c_hw, }, #if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) - { .compatible = "nvidia,tegra210-i2c-vi", .data = &tegra210_i2c_hw, }, + { .compatible = "nvidia,tegra210-i2c-vi", .data = &tegra210_vi_i2c_hw, }, #endif { .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, }, { .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, }, @@ -1897,7 +2161,7 @@ static const struct of_device_id tegra_i2c_of_match[] = { { .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, }, { .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, }, #if IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) - { .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_i2c_hw, }, + { .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_dvc_i2c_hw, }, #endif {}, }; @@ -1905,21 +2169,12 @@ MODULE_DEVICE_TABLE(of, tegra_i2c_of_match); static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev) { - struct device_node *np = i2c_dev->dev->of_node; bool multi_mode; i2c_parse_fw_timings(i2c_dev->dev, &i2c_dev->timings, true); multi_mode = device_property_read_bool(i2c_dev->dev, "multi-master"); i2c_dev->multimaster_mode = multi_mode; - - if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && - of_device_is_compatible(np, "nvidia,tegra20-i2c-dvc")) - i2c_dev->is_dvc = true; - - if (IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) && - of_device_is_compatible(np, "nvidia,tegra210-i2c-vi")) - i2c_dev->is_vi = true; } static int tegra_i2c_init_clocks(struct tegra_i2c_dev *i2c_dev) @@ -2205,6 +2460,7 @@ static const struct acpi_device_id tegra_i2c_acpi_match[] = { {.id = "NVDA0101", .driver_data = (kernel_ulong_t)&tegra210_i2c_hw}, {.id = "NVDA0201", .driver_data = (kernel_ulong_t)&tegra186_i2c_hw}, {.id = "NVDA0301", .driver_data = (kernel_ulong_t)&tegra194_i2c_hw}, + {.id = "NVDA2017", .driver_data = (kernel_ulong_t)&tegra410_i2c_hw}, { } }; MODULE_DEVICE_TABLE(acpi, tegra_i2c_acpi_match); diff --git a/drivers/i2c/busses/i2c-tiny-usb.c b/drivers/i2c/busses/i2c-tiny-usb.c index 9ef495f88ef2..88d66593d9fc 100644 --- a/drivers/i2c/busses/i2c-tiny-usb.c +++ b/drivers/i2c/busses/i2c-tiny-usb.c @@ -213,12 +213,6 @@ static int usb_write(struct i2c_adapter *adapter, int cmd, return ret; } -static void i2c_tiny_usb_free(struct i2c_tiny_usb *dev) -{ - usb_put_dev(dev->usb_dev); - kfree(dev); -} - static int i2c_tiny_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -237,7 +231,7 @@ static int i2c_tiny_usb_probe(struct usb_interface *interface, if (!dev) goto error; - dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + dev->usb_dev = interface_to_usbdev(interface); dev->interface = interface; /* save our data pointer in this interface device */ @@ -277,8 +271,7 @@ static int i2c_tiny_usb_probe(struct usb_interface *interface, return 0; error: - if (dev) - i2c_tiny_usb_free(dev); + kfree(dev); return retval; } @@ -289,7 +282,7 @@ static void i2c_tiny_usb_disconnect(struct usb_interface *interface) i2c_del_adapter(&dev->adapter); usb_set_intfdata(interface, NULL); - i2c_tiny_usb_free(dev); + kfree(dev); dev_dbg(&interface->dev, "disconnected\n"); } diff --git a/drivers/i2c/busses/i2c-usbio.c b/drivers/i2c/busses/i2c-usbio.c index e7799abf6787..259754e5fd05 100644 --- a/drivers/i2c/busses/i2c-usbio.c +++ b/drivers/i2c/busses/i2c-usbio.c @@ -29,6 +29,7 @@ static const struct acpi_device_id usbio_i2c_acpi_hids[] = { { "INTC10B6" }, /* LNL */ { "INTC10D2" }, /* MTL-CVF */ { "INTC10E3" }, /* PTL */ + { "INTC1118" }, /* NVL */ { } }; diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 28015d77599d..3e7735e1dae0 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -1408,7 +1407,6 @@ static const struct i2c_adapter xiic_adapter = { .algo = &xiic_algorithm, }; -#if defined(CONFIG_OF) static const struct xiic_version_data xiic_2_00 = { .quirks = DYNAMIC_MODE_READ_BROKEN_BIT, }; @@ -1419,28 +1417,26 @@ static const struct of_device_id xiic_of_match[] = { {}, }; MODULE_DEVICE_TABLE(of, xiic_of_match); -#endif static int xiic_i2c_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct fwnode_handle *fwnode = dev_fwnode(dev); struct xiic_i2c *i2c; struct xiic_i2c_platform_data *pdata; - const struct of_device_id *match; + const struct xiic_version_data *data; struct resource *res; int ret, irq; u8 i; u32 sr; - i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) return -ENOMEM; - match = of_match_node(xiic_of_match, pdev->dev.of_node); - if (match && match->data) { - const struct xiic_version_data *data = match->data; - + data = device_get_match_data(dev); + if (data) i2c->quirks = data->quirks; - } i2c->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(i2c->base)) @@ -1450,50 +1446,52 @@ static int xiic_i2c_probe(struct platform_device *pdev) if (irq < 0) return irq; - pdata = dev_get_platdata(&pdev->dev); + pdata = dev_get_platdata(dev); /* hook up driver to tree */ platform_set_drvdata(pdev, i2c); i2c->adap = xiic_adapter; + i2c->adap.nr = pdev->id; i2c_set_adapdata(&i2c->adap, i2c); i2c->adap.dev.parent = &pdev->dev; - i2c->adap.dev.of_node = pdev->dev.of_node; + device_set_node(&i2c->adap.dev, fwnode); snprintf(i2c->adap.name, sizeof(i2c->adap.name), DRIVER_NAME " %s", pdev->name); - mutex_init(&i2c->lock); + ret = devm_mutex_init(dev, &i2c->lock); + if (ret) + return ret; + spin_lock_init(&i2c->atomic_lock); - i2c->clk = devm_clk_get_enabled(&pdev->dev, NULL); - if (IS_ERR(i2c->clk)) - return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk), - "failed to enable input clock.\n"); + if (is_of_node(fwnode)) { + i2c->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(i2c->clk)) + return dev_err_probe(dev, PTR_ERR(i2c->clk), + "failed to enable input clock.\n"); + } - i2c->dev = &pdev->dev; - pm_runtime_set_autosuspend_delay(i2c->dev, XIIC_PM_TIMEOUT); - pm_runtime_use_autosuspend(i2c->dev); - pm_runtime_set_active(i2c->dev); - pm_runtime_enable(i2c->dev); + i2c->dev = dev; + + pm_runtime_set_autosuspend_delay(dev, XIIC_PM_TIMEOUT); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_set_active_enabled(dev); + if (ret) + return ret; /* SCL frequency configuration */ i2c->input_clk = clk_get_rate(i2c->clk); - ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", - &i2c->i2c_clk); + ret = device_property_read_u32(dev, "clock-frequency", &i2c->i2c_clk); /* If clock-frequency not specified in DT, do not configure in SW */ if (ret || i2c->i2c_clk > I2C_MAX_FAST_MODE_PLUS_FREQ) i2c->i2c_clk = 0; - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - xiic_process, IRQF_ONESHOT, - pdev->name, i2c); + ret = devm_request_threaded_irq(dev, irq, NULL, xiic_process, + IRQF_ONESHOT, pdev->name, i2c); + if (ret) + return ret; - if (ret < 0) { - dev_err_probe(&pdev->dev, ret, "Cannot claim IRQ\n"); - goto err_pm_disable; - } - - i2c->singlemaster = - of_property_read_bool(pdev->dev.of_node, "single-master"); + i2c->singlemaster = device_property_read_bool(dev, "single-master"); /* * Detect endianness @@ -1508,16 +1506,14 @@ static int xiic_i2c_probe(struct platform_device *pdev) i2c->endianness = BIG; ret = xiic_reinit(i2c); - if (ret < 0) { - dev_err_probe(&pdev->dev, ret, "Cannot xiic_reinit\n"); - goto err_pm_disable; - } + if (ret) + return dev_err_probe(dev, ret, "Cannot xiic_reinit\n"); /* add i2c adapter to i2c tree */ - ret = i2c_add_adapter(&i2c->adap); + ret = i2c_add_numbered_adapter(&i2c->adap); if (ret) { xiic_deinit(i2c); - goto err_pm_disable; + return ret; } if (pdata) { @@ -1526,38 +1522,29 @@ static int xiic_i2c_probe(struct platform_device *pdev) i2c_new_client_device(&i2c->adap, pdata->devices + i); } - dev_dbg(&pdev->dev, "mmio %08lx irq %d scl clock frequency %d\n", - (unsigned long)res->start, irq, i2c->i2c_clk); + dev_dbg(dev, "mmio %pR irq %d scl clock frequency %d\n", + res, irq, i2c->i2c_clk); return 0; - -err_pm_disable: - pm_runtime_disable(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); - - return ret; } static void xiic_i2c_remove(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct xiic_i2c *i2c = platform_get_drvdata(pdev); int ret; /* remove adapter & data */ i2c_del_adapter(&i2c->adap); - ret = pm_runtime_get_sync(i2c->dev); - + ret = pm_runtime_get_sync(dev); if (ret < 0) - dev_warn(&pdev->dev, "Failed to activate device for removal (%pe)\n", + dev_warn(dev, "Failed to activate device for removal (%pe)\n", ERR_PTR(ret)); else xiic_deinit(i2c); - pm_runtime_put_sync(i2c->dev); - pm_runtime_disable(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(dev); } static const struct dev_pm_ops xiic_dev_pm_ops = { diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c index f9fcb4793aaf..e6d2af659d81 100644 --- a/drivers/i2c/i2c-atr.c +++ b/drivers/i2c/i2c-atr.c @@ -49,8 +49,8 @@ struct i2c_atr_alias_pair { * @shared: Indicates if this alias pool is shared by multiple channels * * @lock: Lock protecting @aliases and @use_mask - * @aliases: Array of aliases, must hold exactly @size elements * @use_mask: Mask of used aliases + * @aliases: Array of aliases, must hold exactly @size elements */ struct i2c_atr_alias_pool { size_t size; @@ -58,8 +58,8 @@ struct i2c_atr_alias_pool { /* Protects aliases and use_mask */ spinlock_t lock; - u16 *aliases; unsigned long *use_mask; + u16 aliases[] __counted_by(size); }; /** @@ -137,22 +137,16 @@ static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases, b struct i2c_atr_alias_pool *alias_pool; int ret; - alias_pool = kzalloc_obj(*alias_pool); + alias_pool = kzalloc_flex(*alias_pool, aliases, num_aliases); if (!alias_pool) return ERR_PTR(-ENOMEM); alias_pool->size = num_aliases; - alias_pool->aliases = kcalloc(num_aliases, sizeof(*alias_pool->aliases), GFP_KERNEL); - if (!alias_pool->aliases) { - ret = -ENOMEM; - goto err_free_alias_pool; - } - alias_pool->use_mask = bitmap_zalloc(num_aliases, GFP_KERNEL); if (!alias_pool->use_mask) { ret = -ENOMEM; - goto err_free_aliases; + goto err_free_alias_pool; } alias_pool->shared = shared; @@ -161,8 +155,6 @@ static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases, b return alias_pool; -err_free_aliases: - kfree(alias_pool->aliases); err_free_alias_pool: kfree(alias_pool); return ERR_PTR(ret); @@ -171,7 +163,6 @@ err_free_alias_pool: static void i2c_atr_free_alias_pool(struct i2c_atr_alias_pool *alias_pool) { bitmap_free(alias_pool->use_mask); - kfree(alias_pool->aliases); kfree(alias_pool); } diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 2cbd31f77667..28c0e4884a7f 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -371,6 +371,7 @@ static const struct acpi_device_id i2c_acpi_force_100khz_device_ids[] = { * a 400KHz frequency. The root cause of the issue is not known. */ { "DLL0945", 0 }, + { "ELAN0678", 0 }, { "ELAN06FA", 0 }, {} }; diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 9c46147e3506..a2132d70fb36 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -445,8 +445,7 @@ static int i2c_init_recovery(struct i2c_adapter *adap) bri->set_scl = set_scl_gpio_value; if (bri->sda_gpiod) { bri->get_sda = get_sda_gpio_value; - /* FIXME: add proper flag instead of '0' once available */ - if (gpiod_get_direction(bri->sda_gpiod) == 0) + if (gpiod_get_direction(bri->sda_gpiod) == GPIO_LINE_DIRECTION_OUT) bri->set_sda = set_sda_gpio_value; } } else if (bri->recover_bus == i2c_generic_scl_recovery) { diff --git a/drivers/i2c/i2c-core-slave.c b/drivers/i2c/i2c-core-slave.c index 02ca55c2246b..bebb7ba67e30 100644 --- a/drivers/i2c/i2c-core-slave.c +++ b/drivers/i2c/i2c-core-slave.c @@ -89,7 +89,7 @@ int i2c_slave_event(struct i2c_client *client, int ret = client->slave_cb(client, event, val); if (trace_i2c_slave_enabled()) - trace_i2c_slave(client, event, val, ret); + trace_call__i2c_slave(client, event, val, ret); return ret; } diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index 71eb1ef56f0c..ad6acb5ebadc 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -566,6 +566,18 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, if (res) return res; + /* Reject invalid caller-supplied block lengths before any + * tracepoint or native smbus_xfer callback runs. + */ + if (data && + (protocol == I2C_SMBUS_I2C_BLOCK_DATA || + protocol == I2C_SMBUS_BLOCK_PROC_CALL || + (protocol == I2C_SMBUS_BLOCK_DATA && + read_write == I2C_SMBUS_WRITE)) && + (data->block[0] == 0 || + data->block[0] > I2C_SMBUS_BLOCK_MAX)) + return -EINVAL; + /* If enabled, the following two tracepoints are conditional on * read_write and protocol. */ diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 7bbe0263411e..ccaac5e29f90 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -487,12 +487,13 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) client->adapter->retries = arg; break; case I2C_TIMEOUT: - if (arg > INT_MAX) + /* + * For historical reasons, user-space sets the timeout value in + * units of 10 ms. + */ + if (arg > INT_MAX / 10) return -EINVAL; - /* For historical reasons, user-space sets the timeout - * value in units of 10 ms. - */ client->adapter->timeout = msecs_to_jiffies(arg * 10); break; default: diff --git a/drivers/i2c/i2c-slave-testunit.c b/drivers/i2c/i2c-slave-testunit.c index 6de4307050dd..871c58461ebc 100644 --- a/drivers/i2c/i2c-slave-testunit.c +++ b/drivers/i2c/i2c-slave-testunit.c @@ -15,7 +15,7 @@ #include #include #include -#include /* FIXME: is system_long_wq the best choice? */ +#include #define TU_VERSION_MAX_LENGTH 128 @@ -124,7 +124,7 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client, case I2C_SLAVE_STOP: if (tu->reg_idx == TU_NUM_REGS) { set_bit(TU_FLAG_IN_PROCESS, &tu->flags); - queue_delayed_work(system_long_wq, &tu->worker, + queue_delayed_work(system_dfl_long_wq, &tu->worker, msecs_to_jiffies(10 * tu->regs[TU_REG_DELAY])); } diff --git a/drivers/i2c/i2c-stub.c b/drivers/i2c/i2c-stub.c index fbb0db41b10e..04314e3ed24c 100644 --- a/drivers/i2c/i2c-stub.c +++ b/drivers/i2c/i2c-stub.c @@ -214,6 +214,11 @@ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags, * We ignore banks here, because banked chips don't use I2C * block transfers */ + if (data->block[0] == 0 || + data->block[0] > I2C_SMBUS_BLOCK_MAX) { + ret = -EINVAL; + break; + } if (data->block[0] > 256 - command) /* Avoid overrun */ data->block[0] = 256 - command; len = data->block[0]; diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 9e6be49bebb2..5cd4e5da2233 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -758,6 +758,32 @@ static ssize_t dev_nack_retry_count_store(struct device *dev, static DEVICE_ATTR_RW(dev_nack_retry_count); +static ssize_t do_daa_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i3c_master_controller *master = dev_to_i3cmaster(dev); + bool val; + int ret; + + if (kstrtobool(buf, &val)) + return -EINVAL; + + if (!val) + return -EINVAL; + + if (!master->init_done) + return -EAGAIN; + + ret = i3c_master_do_daa(master); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_WO(do_daa); + static struct attribute *i3c_masterdev_attrs[] = { &dev_attr_mode.attr, &dev_attr_current_master.attr, @@ -769,6 +795,7 @@ static struct attribute *i3c_masterdev_attrs[] = { &dev_attr_dynamic_address.attr, &dev_attr_hdrcap.attr, &dev_attr_hotjoin.attr, + &dev_attr_do_daa.attr, NULL, }; ATTRIBUTE_GROUPS(i3c_masterdev); @@ -898,11 +925,17 @@ static void i3c_ccc_cmd_init(struct i3c_ccc_cmd *cmd, bool rnw, u8 id, cmd->err = I3C_ERROR_UNKNOWN; } +/** + * i3c_master_send_ccc_cmd_locked() - send a CCC (Common Command Codes) + * @master: master used to send frames on the bus + * @cmd: command to send + * + * Return: 0 in case of success, or a negative error code otherwise. + * I3C Mx error codes are stored in cmd->err. + */ static int i3c_master_send_ccc_cmd_locked(struct i3c_master_controller *master, struct i3c_ccc_cmd *cmd) { - int ret; - if (!cmd || !master) return -EINVAL; @@ -920,15 +953,7 @@ static int i3c_master_send_ccc_cmd_locked(struct i3c_master_controller *master, !master->ops->supports_ccc_cmd(master, cmd)) return -EOPNOTSUPP; - ret = master->ops->send_ccc_cmd(master, cmd); - if (ret) { - if (cmd->err != I3C_ERROR_UNKNOWN) - return cmd->err; - - return ret; - } - - return 0; + return master->ops->send_ccc_cmd(master, cmd); } static struct i2c_dev_desc * @@ -1016,6 +1041,10 @@ static int i3c_master_rstdaa_locked(struct i3c_master_controller *master, ret = i3c_master_send_ccc_cmd_locked(master, &cmd); i3c_ccc_cmd_dest_cleanup(&dest); + /* No active devices on the bus. */ + if (ret && cmd.err == I3C_ERROR_M2) + ret = 0; + return ret; } @@ -1032,8 +1061,7 @@ static int i3c_master_rstdaa_locked(struct i3c_master_controller *master, * * This function must be called with the bus lock held in write mode. * - * Return: 0 in case of success, a positive I3C error code if the error is - * one of the official Mx error codes, and a negative error code otherwise. + * Return: 0 in case of success, or a negative error code otherwise. */ int i3c_master_entdaa_locked(struct i3c_master_controller *master) { @@ -1046,12 +1074,17 @@ int i3c_master_entdaa_locked(struct i3c_master_controller *master) ret = i3c_master_send_ccc_cmd_locked(master, &cmd); i3c_ccc_cmd_dest_cleanup(&dest); + /* No active devices need an address. */ + if (ret && cmd.err == I3C_ERROR_M2) + ret = 0; + return ret; } EXPORT_SYMBOL_GPL(i3c_master_entdaa_locked); static int i3c_master_enec_disec_locked(struct i3c_master_controller *master, - u8 addr, bool enable, u8 evts) + u8 addr, bool enable, u8 evts, + bool suppress_m2) { struct i3c_ccc_events *events; struct i3c_ccc_cmd_dest dest; @@ -1071,6 +1104,9 @@ static int i3c_master_enec_disec_locked(struct i3c_master_controller *master, ret = i3c_master_send_ccc_cmd_locked(master, &cmd); i3c_ccc_cmd_dest_cleanup(&dest); + if (suppress_m2 && ret && cmd.err == I3C_ERROR_M2) + ret = 0; + return ret; } @@ -1085,13 +1121,12 @@ static int i3c_master_enec_disec_locked(struct i3c_master_controller *master, * * This function must be called with the bus lock held in write mode. * - * Return: 0 in case of success, a positive I3C error code if the error is - * one of the official Mx error codes, and a negative error code otherwise. + * Return: 0 in case of success, or a negative error code otherwise. */ int i3c_master_disec_locked(struct i3c_master_controller *master, u8 addr, u8 evts) { - return i3c_master_enec_disec_locked(master, addr, false, evts); + return i3c_master_enec_disec_locked(master, addr, false, evts, false); } EXPORT_SYMBOL_GPL(i3c_master_disec_locked); @@ -1106,13 +1141,12 @@ EXPORT_SYMBOL_GPL(i3c_master_disec_locked); * * This function must be called with the bus lock held in write mode. * - * Return: 0 in case of success, a positive I3C error code if the error is - * one of the official Mx error codes, and a negative error code otherwise. + * Return: 0 in case of success, or a negative error code otherwise. */ int i3c_master_enec_locked(struct i3c_master_controller *master, u8 addr, u8 evts) { - return i3c_master_enec_disec_locked(master, addr, true, evts); + return i3c_master_enec_disec_locked(master, addr, true, evts, false); } EXPORT_SYMBOL_GPL(i3c_master_enec_locked); @@ -1132,8 +1166,7 @@ EXPORT_SYMBOL_GPL(i3c_master_enec_locked); * * This function must be called with the bus lock held in write mode. * - * Return: 0 in case of success, a positive I3C error code if the error is - * one of the official Mx error codes, and a negative error code otherwise. + * Return: 0 in case of success, or a negative error code otherwise. */ int i3c_master_defslvs_locked(struct i3c_master_controller *master) { @@ -1794,11 +1827,8 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) i3c_bus_maintenance_lock(&master->bus); - if (rstdaa) { + if (rstdaa) rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); - if (rstret == I3C_ERROR_M2) - rstret = 0; - } ret = master->ops->do_daa(master); @@ -2093,7 +2123,7 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) * (assigned by the bootloader for example). */ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); - if (ret && ret != I3C_ERROR_M2) + if (ret) goto err_bus_cleanup; if (master->ops->set_speed) { @@ -2102,11 +2132,14 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) goto err_bus_cleanup; } - /* Disable all slave events before starting DAA. */ - ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR, - I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR | - I3C_CCC_EVENT_HJ); - if (ret && ret != I3C_ERROR_M2) + /* + * Disable all slave events before starting DAA. When no active device + * is on the bus, returns Mx error code M2, this error is ignored. + */ + ret = i3c_master_enec_disec_locked(master, I3C_BROADCAST_ADDR, false, + I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR | + I3C_CCC_EVENT_HJ, true); + if (ret) goto err_bus_cleanup; /* @@ -2396,7 +2429,7 @@ of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, * DEFSLVS command. */ if (boardinfo->base.flags & I2C_CLIENT_TEN) { - dev_err(dev, "I2C device with 10 bit address not supported."); + dev_err(dev, "I2C device with 10 bit address not supported.\n"); return -EOPNOTSUPP; } @@ -2793,10 +2826,10 @@ struct i3c_generic_ibi_slot { struct i3c_generic_ibi_pool { spinlock_t lock; unsigned int num_slots; - struct i3c_generic_ibi_slot *slots; void *payload_buf; struct list_head free_slots; struct list_head pending; + struct i3c_generic_ibi_slot slots[] __counted_by(num_slots); }; /** @@ -2824,7 +2857,6 @@ void i3c_generic_ibi_free_pool(struct i3c_generic_ibi_pool *pool) WARN_ON(nslots != pool->num_slots); kfree(pool->payload_buf); - kfree(pool->slots); kfree(pool); } EXPORT_SYMBOL_GPL(i3c_generic_ibi_free_pool); @@ -2847,20 +2879,16 @@ i3c_generic_ibi_alloc_pool(struct i3c_dev_desc *dev, unsigned int i; int ret; - pool = kzalloc_obj(*pool); + pool = kzalloc_flex(*pool, slots, req->num_slots); if (!pool) return ERR_PTR(-ENOMEM); + pool->num_slots = req->num_slots; + spin_lock_init(&pool->lock); INIT_LIST_HEAD(&pool->free_slots); INIT_LIST_HEAD(&pool->pending); - pool->slots = kzalloc_objs(*slot, req->num_slots); - if (!pool->slots) { - ret = -ENOMEM; - goto err_free_pool; - } - if (req->max_payload_len) { pool->payload_buf = kcalloc(req->num_slots, req->max_payload_len, GFP_KERNEL); @@ -2879,7 +2907,6 @@ i3c_generic_ibi_alloc_pool(struct i3c_dev_desc *dev, (i * req->max_payload_len); list_add_tail(&slot->node, &pool->free_slots); - pool->num_slots++; } return pool; diff --git a/drivers/i3c/master/adi-i3c-master.c b/drivers/i3c/master/adi-i3c-master.c index 6616f751075a..047081c9f064 100644 --- a/drivers/i3c/master/adi-i3c-master.c +++ b/drivers/i3c/master/adi-i3c-master.c @@ -361,7 +361,7 @@ static int adi_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, cmd->err = adi_i3c_cmd_get_err(&xfer->cmds[0]); - return 0; + return xfer->ret; } static int adi_i3c_master_i3c_xfers(struct i3c_dev_desc *dev, @@ -655,8 +655,7 @@ static int adi_i3c_master_do_daa(struct i3c_master_controller *m) writel(irq_mask, master->regs + REG_IRQ_MASK); - /* DAA always finishes with CE2_ERROR or NACK_RESP */ - if (ret && ret != I3C_ERROR_M2) + if (ret) return ret; /* Add I3C devices discovered */ diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index d6bdb32397fb..655693a2187e 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -393,11 +394,6 @@ dw_i3c_master_alloc_xfer(struct dw_i3c_master *master, unsigned int ncmds) return xfer; } -static void dw_i3c_master_free_xfer(struct dw_i3c_xfer *xfer) -{ - kfree(xfer); -} - static void dw_i3c_master_start_xfer_locked(struct dw_i3c_master *master) { struct dw_i3c_xfer *xfer = master->xferqueue.cur; @@ -715,7 +711,6 @@ static void dw_i3c_master_bus_cleanup(struct i3c_master_controller *m) static int dw_i3c_ccc_set(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) { - struct dw_i3c_xfer *xfer; struct dw_i3c_cmd *cmd; int ret, pos = 0; @@ -725,7 +720,7 @@ static int dw_i3c_ccc_set(struct dw_i3c_master *master, return pos; } - xfer = dw_i3c_master_alloc_xfer(master, 1); + struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, 1); if (!xfer) return -ENOMEM; @@ -750,14 +745,11 @@ static int dw_i3c_ccc_set(struct dw_i3c_master *master, if (xfer->cmds[0].error == RESPONSE_ERROR_IBA_NACK) ccc->err = I3C_ERROR_M2; - dw_i3c_master_free_xfer(xfer); - return ret; } static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) { - struct dw_i3c_xfer *xfer; struct dw_i3c_cmd *cmd; int ret, pos; @@ -765,7 +757,7 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) if (pos < 0) return pos; - xfer = dw_i3c_master_alloc_xfer(master, 1); + struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, 1); if (!xfer) return -ENOMEM; @@ -790,7 +782,6 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc) ret = xfer->ret; if (xfer->cmds[0].error == RESPONSE_ERROR_IBA_NACK) ccc->err = I3C_ERROR_M2; - dw_i3c_master_free_xfer(xfer); return ret; } @@ -837,12 +828,15 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, static int dw_i3c_master_daa(struct i3c_master_controller *m) { struct dw_i3c_master *master = to_dw_i3c_master(m); - struct dw_i3c_xfer *xfer; struct dw_i3c_cmd *cmd; u32 olddevs, newdevs; u8 last_addr = 0; int ret, pos; + struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, 1); + if (!xfer) + return -ENOMEM; + ret = pm_runtime_resume_and_get(master->dev); if (ret < 0) { dev_err(master->dev, @@ -876,15 +870,8 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) ret = 0; } - xfer = dw_i3c_master_alloc_xfer(master, 1); - if (!xfer) { - ret = -ENOMEM; - goto rpm_out; - } - pos = dw_i3c_master_get_free_pos(master); if (pos < 0) { - dw_i3c_master_free_xfer(xfer); ret = pos; goto rpm_out; } @@ -909,8 +896,6 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) i3c_master_add_i3c_dev_locked(m, master->devs[pos].addr); } - dw_i3c_master_free_xfer(xfer); - rpm_out: pm_runtime_put_autosuspend(master->dev); return ret; @@ -924,7 +909,6 @@ static int dw_i3c_master_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_master_controller *m = i3c_dev_get_master(dev); struct dw_i3c_master *master = to_dw_i3c_master(m); unsigned int nrxwords = 0, ntxwords = 0; - struct dw_i3c_xfer *xfer; int i, ret = 0; if (!i3c_nxfers) @@ -944,7 +928,7 @@ static int dw_i3c_master_i3c_xfers(struct i3c_dev_desc *dev, nrxwords > master->caps.datafifodepth) return -EOPNOTSUPP; - xfer = dw_i3c_master_alloc_xfer(master, i3c_nxfers); + struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, i3c_nxfers); if (!xfer) return -ENOMEM; @@ -995,7 +979,6 @@ static int dw_i3c_master_i3c_xfers(struct i3c_dev_desc *dev, } ret = xfer->ret; - dw_i3c_master_free_xfer(xfer); pm_runtime_put_autosuspend(master->dev); return ret; @@ -1084,7 +1067,6 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, struct i3c_master_controller *m = i2c_dev_get_master(dev); struct dw_i3c_master *master = to_dw_i3c_master(m); unsigned int nrxwords = 0, ntxwords = 0; - struct dw_i3c_xfer *xfer; int i, ret = 0; if (!i2c_nxfers) @@ -1104,7 +1086,7 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, nrxwords > master->caps.datafifodepth) return -EOPNOTSUPP; - xfer = dw_i3c_master_alloc_xfer(master, i2c_nxfers); + struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, i2c_nxfers); if (!xfer) return -ENOMEM; @@ -1113,7 +1095,6 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, dev_err(master->dev, "<%s> cannot resume i3c bus master, err: %d\n", __func__, ret); - dw_i3c_master_free_xfer(xfer); return ret; } @@ -1145,7 +1126,6 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, dw_i3c_master_dequeue_xfer(master, xfer); ret = xfer->ret; - dw_i3c_master_free_xfer(xfer); pm_runtime_put_autosuspend(master->dev); return ret; @@ -1606,13 +1586,11 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, if (IS_ERR(master->pclk)) return PTR_ERR(master->pclk); - master->core_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, - "core_rst"); + master->core_rst = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, + "core_rst"); if (IS_ERR(master->core_rst)) return PTR_ERR(master->core_rst); - reset_control_deassert(master->core_rst); - spin_lock_init(&master->xferqueue.lock); INIT_LIST_HEAD(&master->xferqueue.list); @@ -1624,7 +1602,7 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, dw_i3c_master_irq_handler, 0, dev_name(&pdev->dev), master); if (ret) - goto err_assert_rst; + return ret; platform_set_drvdata(pdev, master); @@ -1669,13 +1647,12 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, return 0; err_disable_pm: + if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK) + pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); -err_assert_rst: - reset_control_assert(master->core_rst); - return ret; } EXPORT_SYMBOL_GPL(dw_i3c_common_probe); diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index b78aebf6b2ca..5cfec6761494 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -1143,7 +1143,7 @@ static int cdns_i3c_master_do_daa(struct i3c_master_controller *m) } ret = i3c_master_entdaa_locked(&master->base); - if (ret && ret != I3C_ERROR_M2) + if (ret) return ret; newdevs = readl(master->regs + DEVS_CTRL) & DEVS_CTRL_DEVS_ACTIVE_MASK; diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 284f3ed7af8c..b781dbed2165 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -759,7 +759,7 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci) return 0; } -static int i3c_hci_runtime_suspend(struct device *dev) +int i3c_hci_rpm_suspend(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); int ret; @@ -776,8 +776,9 @@ static int i3c_hci_runtime_suspend(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(i3c_hci_rpm_suspend); -static int i3c_hci_runtime_resume(struct device *dev) +int i3c_hci_rpm_resume(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); int ret; @@ -800,6 +801,27 @@ static int i3c_hci_runtime_resume(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(i3c_hci_rpm_resume); + +static int i3c_hci_runtime_suspend(struct device *dev) +{ + struct i3c_hci *hci = dev_get_drvdata(dev); + + if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) + return 0; + + return i3c_hci_rpm_suspend(dev); +} + +static int i3c_hci_runtime_resume(struct device *dev) +{ + struct i3c_hci *hci = dev_get_drvdata(dev); + + if (hci->quirks & HCI_QUIRK_RPM_PARENT_MANAGED) + return 0; + + return i3c_hci_rpm_resume(dev); +} static int i3c_hci_suspend(struct device *dev) { @@ -844,8 +866,6 @@ static int i3c_hci_restore(struct device *dev) return i3c_hci_resume_common(dev, true); } -#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000 - static void i3c_hci_rpm_enable(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); @@ -996,6 +1016,9 @@ static int i3c_hci_probe(struct platform_device *pdev) if (hci->quirks & HCI_QUIRK_RPM_ALLOWED) i3c_hci_rpm_enable(&pdev->dev); + if (hci->quirks & HCI_QUIRK_RPM_IBI_ALLOWED) + hci->master.rpm_ibi_allowed = true; + return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false); } @@ -1019,7 +1042,9 @@ static const struct acpi_device_id i3c_hci_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match); static const struct platform_device_id i3c_hci_driver_ids[] = { - { .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED }, + { .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED | + HCI_QUIRK_RPM_IBI_ALLOWED | + HCI_QUIRK_RPM_PARENT_MANAGED }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, i3c_hci_driver_ids); diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index e487ef52f6b4..e4daaa612055 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -754,7 +754,10 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) if (!(ibi_status & IBI_LAST_STATUS)) { ibi_size += chunks * rh->ibi_chunk_sz; } else { - ibi_size += FIELD_GET(IBI_DATA_LENGTH, ibi_status); + if (chunks) { + ibi_size += (chunks - 1) * rh->ibi_chunk_sz; + ibi_size += FIELD_GET(IBI_DATA_LENGTH, ibi_status); + } last_ptr = ptr; break; } diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index 9ac9d0e342f4..f17f43494c1b 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -150,6 +150,8 @@ struct i3c_hci_dev_data { #define HCI_QUIRK_OD_PP_TIMING BIT(3) /* Set OD and PP timings for AMD platforms */ #define HCI_QUIRK_RESP_BUF_THLD BIT(4) /* Set resp buf thld to 0 for AMD platforms */ #define HCI_QUIRK_RPM_ALLOWED BIT(5) /* Runtime PM allowed */ +#define HCI_QUIRK_RPM_IBI_ALLOWED BIT(6) /* IBI and Hot-Join allowed while runtime suspended */ +#define HCI_QUIRK_RPM_PARENT_MANAGED BIT(7) /* Runtime PM managed by parent device */ /* global functions */ void mipi_i3c_hci_resume(struct i3c_hci *hci); @@ -160,4 +162,9 @@ void amd_set_resp_buf_thld(struct i3c_hci *hci); void i3c_hci_sync_irq_inactive(struct i3c_hci *hci); int i3c_hci_process_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n); +#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000 + +int i3c_hci_rpm_suspend(struct device *dev); +int i3c_hci_rpm_resume(struct device *dev); + #endif diff --git a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c b/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c index 30302e4d08e2..9468786fb853 100644 --- a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c +++ b/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -20,16 +21,24 @@ #include #include +#include "hci.h" + /* * There can up to 15 instances, but implementations have at most 2 at this * time. */ #define INST_MAX 2 +struct mipi_i3c_hci_pci_instance { + struct device *dev; + bool operational; +}; + struct mipi_i3c_hci_pci { struct pci_dev *pci; void __iomem *base; const struct mipi_i3c_hci_pci_info *info; + struct mipi_i3c_hci_pci_instance instance[INST_MAX]; void *private; }; @@ -40,6 +49,7 @@ struct mipi_i3c_hci_pci_info { int id[INST_MAX]; u32 instance_offset[INST_MAX]; int instance_count; + bool control_instance_pm; }; #define INTEL_PRIV_OFFSET 0x2b0 @@ -164,6 +174,7 @@ static int intel_i3c_init(struct mipi_i3c_hci_pci *hci) dma_set_mask_and_coherent(&hci->pci->dev, DMA_BIT_MASK(64)); hci->pci->d3cold_delay = 0; + hci->pci->d3hot_delay = 0; hci->private = host; host->priv = priv; @@ -189,6 +200,7 @@ static const struct mipi_i3c_hci_pci_info intel_mi_1_info = { .id = {0, 1}, .instance_offset = {0, 0x400}, .instance_count = 2, + .control_instance_pm = true, }; static const struct mipi_i3c_hci_pci_info intel_mi_2_info = { @@ -198,6 +210,7 @@ static const struct mipi_i3c_hci_pci_info intel_mi_2_info = { .id = {2, 3}, .instance_offset = {0, 0x400}, .instance_count = 2, + .control_instance_pm = true, }; static const struct mipi_i3c_hci_pci_info intel_si_2_info = { @@ -207,8 +220,128 @@ static const struct mipi_i3c_hci_pci_info intel_si_2_info = { .id = {2}, .instance_offset = {0}, .instance_count = 1, + .control_instance_pm = true, }; +static int mipi_i3c_hci_pci_find_instance(struct mipi_i3c_hci_pci *hci, struct device *dev) +{ + for (int i = 0; i < INST_MAX; i++) { + if (!hci->instance[i].dev) + hci->instance[i].dev = dev; + if (hci->instance[i].dev == dev) + return i; + } + + return -1; +} + +#define HC_CONTROL 0x04 +#define HC_CONTROL_BUS_ENABLE BIT(31) + +static bool __mipi_i3c_hci_pci_is_operational(struct device *dev) +{ + const struct mipi_i3c_hci_platform_data *pdata = dev->platform_data; + u32 hc_control = readl(pdata->base_regs + HC_CONTROL); + + return hc_control & HC_CONTROL_BUS_ENABLE; +} + +static bool mipi_i3c_hci_pci_is_operational(struct device *dev, bool update) +{ + struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev->parent); + int pos = mipi_i3c_hci_pci_find_instance(hci, dev); + + if (pos < 0) { + dev_err(dev, "%s: I3C instance not found\n", __func__); + return false; + } + + if (update) + hci->instance[pos].operational = __mipi_i3c_hci_pci_is_operational(dev); + + return hci->instance[pos].operational; +} + +struct mipi_i3c_hci_pci_pm_data { + struct device *dev[INST_MAX]; + int dev_cnt; +}; + +static bool mipi_i3c_hci_pci_is_mfd(struct device *dev) +{ + return dev_is_platform(dev) && mfd_get_cell(to_platform_device(dev)); +} + +static int mipi_i3c_hci_pci_suspend_instance(struct device *dev, void *data) +{ + struct mipi_i3c_hci_pci_pm_data *pm_data = data; + int ret; + + if (!mipi_i3c_hci_pci_is_mfd(dev) || + !mipi_i3c_hci_pci_is_operational(dev, true)) + return 0; + + ret = i3c_hci_rpm_suspend(dev); + if (ret) + return ret; + + pm_data->dev[pm_data->dev_cnt++] = dev; + + return 0; +} + +static int mipi_i3c_hci_pci_resume_instance(struct device *dev, void *data) +{ + struct mipi_i3c_hci_pci_pm_data *pm_data = data; + int ret; + + if (!mipi_i3c_hci_pci_is_mfd(dev) || + !mipi_i3c_hci_pci_is_operational(dev, false)) + return 0; + + ret = i3c_hci_rpm_resume(dev); + if (ret) + return ret; + + pm_data->dev[pm_data->dev_cnt++] = dev; + + return 0; +} + +static int mipi_i3c_hci_pci_suspend(struct device *dev) +{ + struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev); + struct mipi_i3c_hci_pci_pm_data pm_data = {}; + int ret; + + if (!hci->info->control_instance_pm) + return 0; + + ret = device_for_each_child_reverse(dev, &pm_data, mipi_i3c_hci_pci_suspend_instance); + if (ret) + for (int i = 0; i < pm_data.dev_cnt; i++) + i3c_hci_rpm_resume(pm_data.dev[i]); + + return ret; +} + +static int mipi_i3c_hci_pci_resume(struct device *dev) +{ + struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev); + struct mipi_i3c_hci_pci_pm_data pm_data = {}; + int ret; + + if (!hci->info->control_instance_pm) + return 0; + + ret = device_for_each_child(dev, &pm_data, mipi_i3c_hci_pci_resume_instance); + if (ret) + for (int i = 0; i < pm_data.dev_cnt; i++) + i3c_hci_rpm_suspend(pm_data.dev[i]); + + return ret; +} + static void mipi_i3c_hci_pci_rpm_allow(struct device *dev) { pm_runtime_put(dev); @@ -322,6 +455,8 @@ static void mipi_i3c_hci_pci_remove(struct pci_dev *pci) /* PM ops must exist for PCI to put a device to a low power state */ static const struct dev_pm_ops mipi_i3c_hci_pci_pm_ops = { + RUNTIME_PM_OPS(mipi_i3c_hci_pci_suspend, mipi_i3c_hci_pci_resume, NULL) + SYSTEM_SLEEP_PM_OPS(mipi_i3c_hci_pci_suspend, mipi_i3c_hci_pci_resume) }; static const struct pci_device_id mipi_i3c_hci_pci_devices[] = { @@ -337,6 +472,9 @@ static const struct pci_device_id mipi_i3c_hci_pci_devices[] = { /* Nova Lake-S */ { PCI_VDEVICE(INTEL, 0x6e2c), (kernel_ulong_t)&intel_mi_1_info}, { PCI_VDEVICE(INTEL, 0x6e2d), (kernel_ulong_t)&intel_mi_2_info}, + /* Nova Lake-H */ + { PCI_VDEVICE(INTEL, 0xd37c), (kernel_ulong_t)&intel_mi_1_info}, + { PCI_VDEVICE(INTEL, 0xd36f), (kernel_ulong_t)&intel_mi_2_info}, { }, }; MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices); diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c index d9f5b30a4b2f..f39c449922ca 100644 --- a/drivers/i3c/master/renesas-i3c.c +++ b/drivers/i3c/master/renesas-i3c.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -747,7 +748,6 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m, struct i3c_ccc_cmd *ccc) { struct renesas_i3c *i3c = to_renesas_i3c(m); - struct renesas_i3c_xfer *xfer; struct renesas_i3c_cmd *cmd; int ret, pos = 0; @@ -757,7 +757,7 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m, return pos; } - xfer = renesas_i3c_alloc_xfer(i3c, 1); + struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1); if (!xfer) return -ENOMEM; @@ -806,8 +806,6 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m, if (ret) ccc->err = I3C_ERROR_M2; - kfree(xfer); - return ret; } @@ -817,13 +815,12 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_ struct i3c_master_controller *m = i3c_dev_get_master(dev); struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); - struct renesas_i3c_xfer *xfer; int i; /* Enable I3C bus. */ renesas_i3c_bus_enable(m, true); - xfer = renesas_i3c_alloc_xfer(i3c, 1); + struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1); if (!xfer) return -ENOMEM; diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index b84b324e4111..e2d99a3ac07d 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -344,7 +344,7 @@ static void svc_i3c_master_reset_fifo_trigger(struct svc_i3c_master *master) { u32 reg; - /* Set RX and TX tigger levels, flush FIFOs */ + /* Set RX and TX trigger levels, flush FIFOs */ reg = SVC_I3C_MDATACTRL_FLUSHTB | SVC_I3C_MDATACTRL_FLUSHRB | SVC_I3C_MDATACTRL_UNLOCK_TRIG | @@ -572,7 +572,7 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master) * 3. IBI isr writes an AutoIBI request. * 4. The controller will not start AutoIBI process because SDA is not low. * 5. IBIWON polling times out. - * 6. Controller reamins in AutoIBI state and doesn't accept EmitStop request. + * 6. Controller remains in AutoIBI state and doesn't accept EmitStop request. */ writel(SVC_I3C_MCTRL_REQUEST_START_ADDR | SVC_I3C_MCTRL_TYPE_I3C | @@ -774,7 +774,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m) /* * Using I3C Open-Drain mode, target is 4.17MHz/240ns with a - * duty-cycle tuned so that high levels are filetered out by + * duty-cycle tuned so that high levels are filtered out by * the 50ns filter (target being 40ns). */ odhpp = 1; @@ -1268,7 +1268,7 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m) /* Configure IBI auto-rules */ ret = svc_i3c_update_ibirules(master); if (ret) - dev_err(master->dev, "Cannot handle such a list of devices"); + dev_err(master->dev, "Cannot handle such a list of devices\n"); rpm_out: pm_runtime_put_autosuspend(master->dev); diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 3d3f8d8673dd..4094299e2ed8 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -158,24 +158,24 @@ config ADXL372 select IIO_TRIGGERED_BUFFER config ADXL372_SPI - tristate "Analog Devices ADXL372 3-Axis Accelerometer SPI Driver" + tristate "Analog Devices ADXL371/ADXL372 3-Axis Accelerometer SPI Driver" depends on SPI select ADXL372 select REGMAP_SPI help - Say yes here to add support for the Analog Devices ADXL372 triaxial - acceleration sensor. + Say yes here to add support for the Analog Devices ADXL371/ADXL372 + triaxial acceleration sensor. To compile this driver as a module, choose M here: the module will be called adxl372_spi. config ADXL372_I2C - tristate "Analog Devices ADXL372 3-Axis Accelerometer I2C Driver" + tristate "Analog Devices ADXL371/ADXL372 3-Axis Accelerometer I2C Driver" depends on I2C select ADXL372 select REGMAP_I2C help - Say yes here to add support for the Analog Devices ADXL372 triaxial - acceleration sensor. + Say yes here to add support for the Analog Devices ADXL371/ADXL372 + triaxial acceleration sensor. To compile this driver as a module, choose M here: the module will be called adxl372_i2c. diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index 5127e58eebc7..ba0f97944c6d 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -147,7 +147,7 @@ static int adis16201_read_raw(struct iio_dev *indio_dev, /* * The raw ADC value is 1278 when the temperature * is 25 degrees and the scale factor per milli - * degree celcius is -470. + * degree Celsius is -470. */ *val = 25000 / -470 - 1278; return IIO_VAL_INT; diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c index 41ffd92f27fd..04e169f221c4 100644 --- a/drivers/iio/accel/adis16209.c +++ b/drivers/iio/accel/adis16209.c @@ -186,7 +186,7 @@ static int adis16209_read_raw(struct iio_dev *indio_dev, /* * The raw ADC value is 0x4FE when the temperature * is 45 degrees and the scale factor per milli - * degree celcius is -470. + * degree Celsius is -470. */ *val = 25000 / -470 - 0x4FE; return IIO_VAL_INT; diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c index 83dcac17a042..bcc11dabdf22 100644 --- a/drivers/iio/accel/adxl313_core.c +++ b/drivers/iio/accel/adxl313_core.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -356,19 +357,15 @@ static int adxl313_read_axis(struct adxl313_data *data, { int ret; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); ret = regmap_bulk_read(data->regmap, ADXL313_REG_DATA_AXIS(chan->address), &data->transf_buf, sizeof(data->transf_buf)); if (ret) - goto unlock_ret; + return ret; - ret = le16_to_cpu(data->transf_buf); - -unlock_ret: - mutex_unlock(&data->lock); - return ret; + return le16_to_cpu(data->transf_buf); } static int adxl313_read_freq_avail(struct iio_dev *indio_dev, diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 78e3f799ecc1..6c9080d88c60 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -213,6 +213,7 @@ static const struct iio_event_spec adxl345_events[] = { .dir = IIO_EV_DIR_RISING, .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_SCALE) | BIT(IIO_EV_INFO_VALUE), }, { @@ -221,6 +222,7 @@ static const struct iio_event_spec adxl345_events[] = { .dir = IIO_EV_DIR_RISING, .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_SCALE) | BIT(IIO_EV_INFO_VALUE), }, { @@ -228,14 +230,19 @@ static const struct iio_event_spec adxl345_events[] = { .type = IIO_EV_TYPE_GESTURE, .dir = IIO_EV_DIR_SINGLETAP, .mask_separate = BIT(IIO_EV_INFO_ENABLE), - .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + .mask_shared_by_type = + BIT(IIO_EV_INFO_SCALE) | + BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_TIMEOUT), }, { /* double tap */ .type = IIO_EV_TYPE_GESTURE, .dir = IIO_EV_DIR_DOUBLETAP, - .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + .mask_shared_by_type = + BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_SCALE) | + BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_RESET_TIMEOUT) | BIT(IIO_EV_INFO_TAP2_MIN_DELAY), }, @@ -274,6 +281,7 @@ static const struct iio_event_spec adxl345_fake_chan_events[] = { .dir = IIO_EV_DIR_FALLING, .mask_separate = BIT(IIO_EV_INFO_ENABLE), .mask_shared_by_type = + BIT(IIO_EV_INFO_SCALE) | BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_PERIOD), }, @@ -283,6 +291,7 @@ static const struct iio_event_spec adxl345_fake_chan_events[] = { .dir = IIO_EV_DIR_FALLING, .mask_separate = BIT(IIO_EV_INFO_ENABLE), .mask_shared_by_type = + BIT(IIO_EV_INFO_SCALE) | BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_PERIOD), }, @@ -1341,6 +1350,16 @@ static int adxl345_read_event_value(struct iio_dev *indio_dev, unsigned int tap_threshold; int ret; + /* + * The event threshold LSB is fixed at 62.5 mg/LSB + * 0.0625 * 9.80665 = 0.612915625 m/s^2 + */ + if (info == IIO_EV_INFO_SCALE) { + *val = 0; + *val2 = 612915; + return IIO_VAL_INT_PLUS_MICRO; + } + switch (type) { case IIO_EV_TYPE_MAG: return adxl345_read_mag_value(st, dir, info, @@ -1355,12 +1374,6 @@ static int adxl345_read_event_value(struct iio_dev *indio_dev, case IIO_EV_TYPE_GESTURE: switch (info) { case IIO_EV_INFO_VALUE: - /* - * The scale factor would be 62.5mg/LSB (i.e. 0xFF = 16g) but - * not applied here. In context of this general purpose sensor, - * what imports is rather signal intensity than the absolute - * measured g value. - */ ret = regmap_read(st->regmap, ADXL345_REG_THRESH_TAP, &tap_threshold); if (ret) @@ -1401,6 +1414,9 @@ static int adxl345_write_event_value(struct iio_dev *indio_dev, if (ret) return ret; + if (info == IIO_EV_INFO_SCALE) + return -EINVAL; + switch (type) { case IIO_EV_TYPE_MAG: ret = adxl345_write_mag_value(st, dir, info, diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 28a8793a53b6..545a21e5a308 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1,12 +1,13 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * ADXL372 3-Axis Digital Accelerometer core driver + * ADXL371/ADXL372 3-Axis Digital Accelerometer core driver * * Copyright 2018 Analog Devices Inc. */ #include #include +#include #include #include #include @@ -180,6 +181,16 @@ enum adxl372_odr { ADXL372_ODR_1600HZ, ADXL372_ODR_3200HZ, ADXL372_ODR_6400HZ, + ADXL372_ODR_NUM +}; + +enum adxl371_odr { + ADXL371_ODR_320HZ, + ADXL371_ODR_640HZ, + ADXL371_ODR_1280HZ, + ADXL371_ODR_2560HZ, + ADXL371_ODR_5120HZ, + ADXL371_ODR_NUM }; enum adxl372_bandwidth { @@ -214,14 +225,67 @@ enum adxl372_fifo_mode { ADXL372_FIFO_OLD_SAVED }; -static const int adxl372_samp_freq_tbl[5] = { - 400, 800, 1600, 3200, 6400, +static const int adxl372_samp_freq_tbl[ADXL372_ODR_NUM] = { + [ADXL372_ODR_400HZ] = 400, + [ADXL372_ODR_800HZ] = 800, + [ADXL372_ODR_1600HZ] = 1600, + [ADXL372_ODR_3200HZ] = 3200, + [ADXL372_ODR_6400HZ] = 6400, }; -static const int adxl372_bw_freq_tbl[5] = { - 200, 400, 800, 1600, 3200, +static const int adxl372_bw_freq_tbl[ADXL372_ODR_NUM] = { + [ADXL372_BW_200HZ] = 200, + [ADXL372_BW_400HZ] = 400, + [ADXL372_BW_800HZ] = 800, + [ADXL372_BW_1600HZ] = 1600, + [ADXL372_BW_3200HZ] = 3200, }; +static const int adxl371_samp_freq_tbl[ADXL371_ODR_NUM] = { + [ADXL371_ODR_320HZ] = 320, + [ADXL371_ODR_640HZ] = 640, + [ADXL371_ODR_1280HZ] = 1280, + [ADXL371_ODR_2560HZ] = 2560, + [ADXL371_ODR_5120HZ] = 5120, +}; + +static const int adxl371_bw_freq_tbl[ADXL371_ODR_NUM] = { + [ADXL371_ODR_320HZ] = 160, + [ADXL371_ODR_640HZ] = 320, + [ADXL371_ODR_1280HZ] = 640, + [ADXL371_ODR_2560HZ] = 1280, + [ADXL371_ODR_5120HZ] = 2560, +}; + +const struct adxl372_chip_info adxl371_chip_info = { + .name = "adxl371", + .samp_freq_tbl = adxl371_samp_freq_tbl, + .bw_freq_tbl = adxl371_bw_freq_tbl, + .num_freqs = ARRAY_SIZE(adxl371_samp_freq_tbl), + .act_time_scale_us = 4125, + .act_time_scale_low_us = 8250, + .inact_time_scale_ms = 16, + .inact_time_scale_low_ms = 32, + .max_odr = ADXL371_ODR_5120HZ, + /* Silicon erratum (er001) causes FIFO data misalignment on ADXL371 */ + .fifo_supported = false, +}; +EXPORT_SYMBOL_NS_GPL(adxl371_chip_info, "IIO_ADXL372"); + +const struct adxl372_chip_info adxl372_chip_info = { + .name = "adxl372", + .samp_freq_tbl = adxl372_samp_freq_tbl, + .bw_freq_tbl = adxl372_bw_freq_tbl, + .num_freqs = ARRAY_SIZE(adxl372_samp_freq_tbl), + .act_time_scale_us = 3300, + .act_time_scale_low_us = 6600, + .inact_time_scale_ms = 13, + .inact_time_scale_low_ms = 26, + .max_odr = ADXL372_ODR_6400HZ, + .fifo_supported = true, +}; +EXPORT_SYMBOL_NS_GPL(adxl372_chip_info, "IIO_ADXL372"); + struct adxl372_axis_lookup { unsigned int bits; enum adxl372_fifo_format fifo_format; @@ -257,8 +321,12 @@ static const struct iio_event_spec adxl372_events[] = { .modified = 1, \ .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ .scan_index = index, \ .scan_type = { \ @@ -279,6 +347,7 @@ static const struct iio_chan_spec adxl372_channels[] = { }; struct adxl372_state { + const struct adxl372_chip_info *chip_info; int irq; struct device *dev; struct regmap *regmap; @@ -336,18 +405,14 @@ static ssize_t adxl372_write_threshold_value(struct iio_dev *indio_dev, unsigned struct adxl372_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&st->threshold_m); + guard(mutex)(&st->threshold_m); + ret = regmap_write(st->regmap, addr, ADXL372_THRESH_VAL_H_SEL(threshold)); if (ret < 0) - goto unlock; + return ret; - ret = regmap_update_bits(st->regmap, addr + 1, GENMASK(7, 5), - ADXL372_THRESH_VAL_L_SEL(threshold) << 5); - -unlock: - mutex_unlock(&st->threshold_m); - - return ret; + return regmap_update_bits(st->regmap, addr + 1, GENMASK(7, 5), + ADXL372_THRESH_VAL_L_SEL(threshold) << 5); } static int adxl372_read_axis(struct adxl372_state *st, u8 addr) @@ -471,13 +536,14 @@ static int adxl372_set_activity_time_ms(struct adxl372_state *st, int ret; /* - * 3.3 ms per code is the scale factor of the TIME_ACT register for - * ODR = 6400 Hz. It is 6.6 ms per code for ODR = 3200 Hz and below. + * The scale factor of the TIME_ACT register depends on the ODR. + * A higher scale factor is used at the maximum ODR and a lower + * one at all other rates. */ - if (st->odr == ADXL372_ODR_6400HZ) - scale_factor = 3300; + if (st->odr == st->chip_info->max_odr) + scale_factor = st->chip_info->act_time_scale_us; else - scale_factor = 6600; + scale_factor = st->chip_info->act_time_scale_low_us; reg_val = DIV_ROUND_CLOSEST(act_time_ms * 1000, scale_factor); @@ -501,13 +567,14 @@ static int adxl372_set_inactivity_time_ms(struct adxl372_state *st, int ret; /* - * 13 ms per code is the scale factor of the TIME_INACT register for - * ODR = 6400 Hz. It is 26 ms per code for ODR = 3200 Hz and below. + * The scale factor of the TIME_INACT register depends on the ODR. + * A higher scale factor is used at the maximum ODR and a lower + * one at all other rates. */ - if (st->odr == ADXL372_ODR_6400HZ) - scale_factor = 13; + if (st->odr == st->chip_info->max_odr) + scale_factor = st->chip_info->inact_time_scale_ms; else - scale_factor = 26; + scale_factor = st->chip_info->inact_time_scale_low_ms; res = DIV_ROUND_CLOSEST(inact_time_ms, scale_factor); reg_val_h = (res >> 8) & 0xFF; @@ -717,7 +784,7 @@ static int adxl372_setup(struct adxl372_state *st) if (ret < 0) return ret; - ret = adxl372_set_odr(st, ADXL372_ODR_6400HZ); + ret = adxl372_set_odr(st, st->chip_info->max_odr); if (ret < 0) return ret; @@ -777,10 +844,10 @@ static int adxl372_read_raw(struct iio_dev *indio_dev, *val2 = ADXL372_USCALE; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: - *val = adxl372_samp_freq_tbl[st->odr]; + *val = st->chip_info->samp_freq_tbl[st->odr]; return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - *val = adxl372_bw_freq_tbl[st->bw]; + *val = st->chip_info->bw_freq_tbl[st->bw]; return IIO_VAL_INT; } @@ -796,23 +863,17 @@ static int adxl372_write_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: - odr_index = adxl372_find_closest_match(adxl372_samp_freq_tbl, - ARRAY_SIZE(adxl372_samp_freq_tbl), - val); + odr_index = adxl372_find_closest_match(st->chip_info->samp_freq_tbl, + st->chip_info->num_freqs, + val); ret = adxl372_set_odr(st, odr_index); if (ret < 0) return ret; - /* - * The timer period depends on the ODR selected. - * At 3200 Hz and below, it is 6.6 ms; at 6400 Hz, it is 3.3 ms - */ + /* Recalculate activity time as the timer period depends on ODR */ ret = adxl372_set_activity_time_ms(st, st->act_time_ms); if (ret < 0) return ret; - /* - * The timer period depends on the ODR selected. - * At 3200 Hz and below, it is 26 ms; at 6400 Hz, it is 13 ms - */ + /* Recalculate inactivity time as the timer period depends on ODR */ ret = adxl372_set_inactivity_time_ms(st, st->inact_time_ms); if (ret < 0) return ret; @@ -825,9 +886,9 @@ static int adxl372_write_raw(struct iio_dev *indio_dev, return ret; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - bw_index = adxl372_find_closest_match(adxl372_bw_freq_tbl, - ARRAY_SIZE(adxl372_bw_freq_tbl), - val); + bw_index = adxl372_find_closest_match(st->chip_info->bw_freq_tbl, + st->chip_info->num_freqs, + val); return adxl372_set_bandwidth(st, bw_index); default: return -EINVAL; @@ -957,24 +1018,6 @@ static int adxl372_write_event_config(struct iio_dev *indio_dev, const struct ii return adxl372_set_interrupts(st, st->int1_bitmask, 0); } -static ssize_t adxl372_show_filter_freq_avail(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct adxl372_state *st = iio_priv(indio_dev); - int i; - size_t len = 0; - - for (i = 0; i <= st->odr; i++) - len += scnprintf(buf + len, PAGE_SIZE - len, - "%d ", adxl372_bw_freq_tbl[i]); - - buf[len - 1] = '\n'; - - return len; -} - static ssize_t adxl372_get_fifo_enabled(struct device *dev, struct device_attribute *attr, char *buf) @@ -1142,25 +1185,38 @@ static const struct iio_trigger_ops adxl372_peak_data_trigger_ops = { .set_trigger_state = adxl372_peak_dready_trig_set_state, }; -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("400 800 1600 3200 6400"); -static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available, - 0444, adxl372_show_filter_freq_avail, NULL, 0); +static int adxl372_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct adxl372_state *st = iio_priv(indio_dev); -static struct attribute *adxl372_attributes[] = { - &iio_const_attr_sampling_frequency_available.dev_attr.attr, - &iio_dev_attr_in_accel_filter_low_pass_3db_frequency_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group adxl372_attrs_group = { - .attrs = adxl372_attributes, -}; + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = st->chip_info->samp_freq_tbl; + *type = IIO_VAL_INT; + *length = st->chip_info->num_freqs; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = st->chip_info->bw_freq_tbl; + *type = IIO_VAL_INT; + /* + * Bandwidth cannot exceed half the sampling frequency + * (Nyquist), so limit available values based on current ODR. + */ + *length = st->odr + 1; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} static const struct iio_info adxl372_info = { .validate_trigger = &adxl372_validate_trigger, - .attrs = &adxl372_attrs_group, .read_raw = adxl372_read_raw, .write_raw = adxl372_write_raw, + .read_avail = adxl372_read_avail, .read_event_config = adxl372_read_event_config, .write_event_config = adxl372_write_event_config, .read_event_value = adxl372_read_event_value, @@ -1175,8 +1231,57 @@ bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg) } EXPORT_SYMBOL_NS_GPL(adxl372_readable_noinc_reg, "IIO_ADXL372"); +static int adxl372_buffer_setup(struct iio_dev *indio_dev) +{ + struct adxl372_state *st = iio_priv(indio_dev); + struct device *dev = st->dev; + int ret; + + ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL, + adxl372_trigger_handler, + IIO_BUFFER_DIRECTION_IN, + &adxl372_buffer_ops, + adxl372_fifo_attributes); + if (ret) + return ret; + + if (!st->irq) + return 0; + + st->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->dready_trig) + return -ENOMEM; + + st->peak_datardy_trig = devm_iio_trigger_alloc(dev, "%s-dev%d-peak", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->peak_datardy_trig) + return -ENOMEM; + + st->dready_trig->ops = &adxl372_trigger_ops; + st->peak_datardy_trig->ops = &adxl372_peak_data_trigger_ops; + iio_trigger_set_drvdata(st->dready_trig, indio_dev); + iio_trigger_set_drvdata(st->peak_datardy_trig, indio_dev); + ret = devm_iio_trigger_register(dev, st->dready_trig); + if (ret) + return ret; + + ret = devm_iio_trigger_register(dev, st->peak_datardy_trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->dready_trig); + + return devm_request_irq(dev, st->irq, + iio_trigger_generic_data_rdy_poll, + IRQF_TRIGGER_RISING | IRQF_NO_THREAD, + indio_dev->name, st->dready_trig); +} + int adxl372_probe(struct device *dev, struct regmap *regmap, - int irq, const char *name) + int irq, const struct adxl372_chip_info *chip_info) { struct iio_dev *indio_dev; struct adxl372_state *st; @@ -1192,15 +1297,21 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, st->dev = dev; st->regmap = regmap; st->irq = irq; + st->chip_info = chip_info; mutex_init(&st->threshold_m); indio_dev->channels = adxl372_channels; indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); - indio_dev->available_scan_masks = adxl372_channel_masks; - indio_dev->name = name; + indio_dev->name = chip_info->name; indio_dev->info = &adxl372_info; - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + + if (chip_info->fifo_supported) { + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->available_scan_masks = adxl372_channel_masks; + } else { + indio_dev->modes = INDIO_DIRECT_MODE; + } ret = adxl372_setup(st); if (ret < 0) { @@ -1208,48 +1319,8 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, return ret; } - ret = devm_iio_triggered_buffer_setup_ext(dev, - indio_dev, NULL, - adxl372_trigger_handler, - IIO_BUFFER_DIRECTION_IN, - &adxl372_buffer_ops, - adxl372_fifo_attributes); - if (ret < 0) - return ret; - - if (st->irq) { - st->dready_trig = devm_iio_trigger_alloc(dev, - "%s-dev%d", - indio_dev->name, - iio_device_id(indio_dev)); - if (st->dready_trig == NULL) - return -ENOMEM; - - st->peak_datardy_trig = devm_iio_trigger_alloc(dev, - "%s-dev%d-peak", - indio_dev->name, - iio_device_id(indio_dev)); - if (!st->peak_datardy_trig) - return -ENOMEM; - - st->dready_trig->ops = &adxl372_trigger_ops; - st->peak_datardy_trig->ops = &adxl372_peak_data_trigger_ops; - iio_trigger_set_drvdata(st->dready_trig, indio_dev); - iio_trigger_set_drvdata(st->peak_datardy_trig, indio_dev); - ret = devm_iio_trigger_register(dev, st->dready_trig); - if (ret < 0) - return ret; - - ret = devm_iio_trigger_register(dev, st->peak_datardy_trig); - if (ret < 0) - return ret; - - indio_dev->trig = iio_trigger_get(st->dready_trig); - - ret = devm_request_irq(dev, st->irq, - iio_trigger_generic_data_rdy_poll, - IRQF_TRIGGER_RISING | IRQF_NO_THREAD, - indio_dev->name, st->dready_trig); + if (chip_info->fifo_supported) { + ret = adxl372_buffer_setup(indio_dev); if (ret < 0) return ret; } @@ -1259,5 +1330,6 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, EXPORT_SYMBOL_NS_GPL(adxl372_probe, "IIO_ADXL372"); MODULE_AUTHOR("Stefan Popa "); -MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer driver"); +MODULE_AUTHOR("Antoniu Miclaus "); +MODULE_DESCRIPTION("Analog Devices ADXL371/ADXL372 3-axis accelerometer driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/adxl372.h b/drivers/iio/accel/adxl372.h index 80a0aa9714fc..353a8b3a9d76 100644 --- a/drivers/iio/accel/adxl372.h +++ b/drivers/iio/accel/adxl372.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * ADXL372 3-Axis Digital Accelerometer + * ADXL371/ADXL372 3-Axis Digital Accelerometer * * Copyright 2018 Analog Devices Inc. */ @@ -10,8 +10,24 @@ #define ADXL372_REVID 0x03 +struct adxl372_chip_info { + const char *name; + const int *samp_freq_tbl; + const int *bw_freq_tbl; + unsigned int num_freqs; + unsigned int act_time_scale_us; + unsigned int act_time_scale_low_us; + unsigned int inact_time_scale_ms; + unsigned int inact_time_scale_low_ms; + unsigned int max_odr; + bool fifo_supported; +}; + +extern const struct adxl372_chip_info adxl371_chip_info; +extern const struct adxl372_chip_info adxl372_chip_info; + int adxl372_probe(struct device *dev, struct regmap *regmap, - int irq, const char *name); + int irq, const struct adxl372_chip_info *chip_info); bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg); #endif /* _ADXL372_H_ */ diff --git a/drivers/iio/accel/adxl372_i2c.c b/drivers/iio/accel/adxl372_i2c.c index 186d4fe9a556..ca2cabf24938 100644 --- a/drivers/iio/accel/adxl372_i2c.c +++ b/drivers/iio/accel/adxl372_i2c.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * ADXL372 3-Axis Digital Accelerometer I2C driver + * ADXL371/ADXL372 3-Axis Digital Accelerometer I2C driver * * Copyright 2018 Analog Devices Inc. */ @@ -20,11 +20,13 @@ static const struct regmap_config adxl372_regmap_config = { static int adxl372_i2c_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); + const struct adxl372_chip_info *chip_info; struct regmap *regmap; unsigned int regval; int ret; + chip_info = i2c_get_match_data(client); + regmap = devm_regmap_init_i2c(client, &adxl372_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); @@ -38,17 +40,19 @@ static int adxl372_i2c_probe(struct i2c_client *client) dev_warn(&client->dev, "I2C might not work properly with other devices on the bus"); - return adxl372_probe(&client->dev, regmap, client->irq, id->name); + return adxl372_probe(&client->dev, regmap, client->irq, chip_info); } static const struct i2c_device_id adxl372_i2c_id[] = { - { "adxl372" }, + { "adxl371", (kernel_ulong_t)&adxl371_chip_info }, + { "adxl372", (kernel_ulong_t)&adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, adxl372_i2c_id); static const struct of_device_id adxl372_of_match[] = { - { .compatible = "adi,adxl372" }, + { .compatible = "adi,adxl371", .data = &adxl371_chip_info }, + { .compatible = "adi,adxl372", .data = &adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(of, adxl372_of_match); @@ -65,6 +69,7 @@ static struct i2c_driver adxl372_i2c_driver = { module_i2c_driver(adxl372_i2c_driver); MODULE_AUTHOR("Stefan Popa "); -MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer I2C driver"); +MODULE_AUTHOR("Antoniu Miclaus "); +MODULE_DESCRIPTION("Analog Devices ADXL371/ADXL372 3-axis accelerometer I2C driver"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("IIO_ADXL372"); diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c index 39941b519c3b..1f9c1544e547 100644 --- a/drivers/iio/accel/adxl372_spi.c +++ b/drivers/iio/accel/adxl372_spi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * ADXL372 3-Axis Digital Accelerometer SPI driver + * ADXL371/ADXL372 3-Axis Digital Accelerometer SPI driver * * Copyright 2018 Analog Devices Inc. */ @@ -22,24 +22,28 @@ static const struct regmap_config adxl372_spi_regmap_config = { static int adxl372_spi_probe(struct spi_device *spi) { - const struct spi_device_id *id = spi_get_device_id(spi); + const struct adxl372_chip_info *chip_info; struct regmap *regmap; + chip_info = spi_get_device_match_data(spi); + regmap = devm_regmap_init_spi(spi, &adxl372_spi_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); - return adxl372_probe(&spi->dev, regmap, spi->irq, id->name); + return adxl372_probe(&spi->dev, regmap, spi->irq, chip_info); } static const struct spi_device_id adxl372_spi_id[] = { - { "adxl372", 0 }, + { "adxl371", (kernel_ulong_t)&adxl371_chip_info }, + { "adxl372", (kernel_ulong_t)&adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adxl372_spi_id); static const struct of_device_id adxl372_of_match[] = { - { .compatible = "adi,adxl372" }, + { .compatible = "adi,adxl371", .data = &adxl371_chip_info }, + { .compatible = "adi,adxl372", .data = &adxl372_chip_info }, { } }; MODULE_DEVICE_TABLE(of, adxl372_of_match); @@ -56,6 +60,7 @@ static struct spi_driver adxl372_spi_driver = { module_spi_driver(adxl372_spi_driver); MODULE_AUTHOR("Stefan Popa "); -MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer SPI driver"); +MODULE_AUTHOR("Antoniu Miclaus "); +MODULE_DESCRIPTION("Analog Devices ADXL371/ADXL372 3-axis accelerometer SPI driver"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("IIO_ADXL372"); diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c index a51d1d61c412..e7bb32fbc475 100644 --- a/drivers/iio/accel/adxl380.c +++ b/drivers/iio/accel/adxl380.c @@ -31,7 +31,7 @@ #define ADXL319_ID_VAL 382 #define ADXL380_DEVID_AD_REG 0x00 -#define ADLX380_PART_ID_REG 0x02 +#define ADXL380_PART_ID_REG 0x02 #define ADXL380_X_DATA_H_REG 0x15 #define ADXL380_Y_DATA_H_REG 0x17 @@ -1878,7 +1878,7 @@ static int adxl380_setup(struct iio_dev *indio_dev) if (reg_val != ADXL380_DEVID_AD_VAL) dev_warn(st->dev, "Unknown chip id %x\n", reg_val); - ret = regmap_bulk_read(st->regmap, ADLX380_PART_ID_REG, + ret = regmap_bulk_read(st->regmap, ADXL380_PART_ID_REG, &st->transf_buf, 2); if (ret) return ret; diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 42ccf0316ce5..2398eb7e12cd 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -851,7 +851,7 @@ static ssize_t bmc150_accel_get_fifo_watermark(struct device *dev, wm = data->watermark; mutex_unlock(&data->mutex); - return sprintf(buf, "%d\n", wm); + return sysfs_emit(buf, "%d\n", wm); } static ssize_t bmc150_accel_get_fifo_state(struct device *dev, @@ -866,7 +866,7 @@ static ssize_t bmc150_accel_get_fifo_state(struct device *dev, state = data->fifo_mode; mutex_unlock(&data->mutex); - return sprintf(buf, "%d\n", state); + return sysfs_emit(buf, "%d\n", state); } static const struct iio_mount_matrix * diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 4a827be439a2..573831199bba 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -7,6 +7,7 @@ * See industrialio/accels/sca3000.h for comments. */ +#include #include #include #include @@ -171,6 +172,7 @@ struct sca3000_state { /** * struct sca3000_chip_info - model dependent parameters + * @name: name of the chip * @scale: scale * 10^-6 * @temp_output: some devices have temperature sensors. * @measurement_mode_freq: normal mode sampling frequency @@ -193,6 +195,7 @@ struct sca3000_state { * sca3000 variant. **/ struct sca3000_chip_info { + const char *name; unsigned int scale; bool temp_output; int measurement_mode_freq; @@ -207,69 +210,59 @@ struct sca3000_chip_info { int mot_det_mult_y[7]; }; -enum sca3000_variant { - d01, - e02, - e04, - e05, +static const struct sca3000_chip_info sca3000_chip_info_d01 = { + .name = "sca3000_d01", + .scale = 7357, + .temp_output = true, + .measurement_mode_freq = 250, + .measurement_mode_3db_freq = 45, + .option_mode_1 = SCA3000_OP_MODE_BYPASS, + .option_mode_1_freq = 250, + .option_mode_1_3db_freq = 70, + .mot_det_mult_xz = { 50, 100, 200, 350, 650, 1300 }, + .mot_det_mult_y = { 50, 100, 150, 250, 450, 850, 1750 }, }; -/* - * Note where option modes are not defined, the chip simply does not - * support any. - * Other chips in the sca3000 series use i2c and are not included here. - * - * Some of these devices are only listed in the family data sheet and - * do not actually appear to be available. - */ -static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = { - [d01] = { - .scale = 7357, - .temp_output = true, - .measurement_mode_freq = 250, - .measurement_mode_3db_freq = 45, - .option_mode_1 = SCA3000_OP_MODE_BYPASS, - .option_mode_1_freq = 250, - .option_mode_1_3db_freq = 70, - .mot_det_mult_xz = {50, 100, 200, 350, 650, 1300}, - .mot_det_mult_y = {50, 100, 150, 250, 450, 850, 1750}, - }, - [e02] = { - .scale = 9810, - .measurement_mode_freq = 125, - .measurement_mode_3db_freq = 40, - .option_mode_1 = SCA3000_OP_MODE_NARROW, - .option_mode_1_freq = 63, - .option_mode_1_3db_freq = 11, - .mot_det_mult_xz = {100, 150, 300, 550, 1050, 2050}, - .mot_det_mult_y = {50, 100, 200, 350, 700, 1350, 2700}, - }, - [e04] = { - .scale = 19620, - .measurement_mode_freq = 100, - .measurement_mode_3db_freq = 38, - .option_mode_1 = SCA3000_OP_MODE_NARROW, - .option_mode_1_freq = 50, - .option_mode_1_3db_freq = 9, - .option_mode_2 = SCA3000_OP_MODE_WIDE, - .option_mode_2_freq = 400, - .option_mode_2_3db_freq = 70, - .mot_det_mult_xz = {200, 300, 600, 1100, 2100, 4100}, - .mot_det_mult_y = {100, 200, 400, 7000, 1400, 2700, 54000}, - }, - [e05] = { - .scale = 61313, - .measurement_mode_freq = 200, - .measurement_mode_3db_freq = 60, - .option_mode_1 = SCA3000_OP_MODE_NARROW, - .option_mode_1_freq = 50, - .option_mode_1_3db_freq = 9, - .option_mode_2 = SCA3000_OP_MODE_WIDE, - .option_mode_2_freq = 400, - .option_mode_2_3db_freq = 75, - .mot_det_mult_xz = {600, 900, 1700, 3200, 6100, 11900}, - .mot_det_mult_y = {300, 600, 1200, 2000, 4100, 7800, 15600}, - }, +static const struct sca3000_chip_info sca3000_chip_info_e02 = { + .name = "sca3000_e02", + .scale = 9810, + .measurement_mode_freq = 125, + .measurement_mode_3db_freq = 40, + .option_mode_1 = SCA3000_OP_MODE_NARROW, + .option_mode_1_freq = 63, + .option_mode_1_3db_freq = 11, + .mot_det_mult_xz = { 100, 150, 300, 550, 1050, 2050 }, + .mot_det_mult_y = { 50, 100, 200, 350, 700, 1350, 2700 }, +}; + +static const struct sca3000_chip_info sca3000_chip_info_e04 = { + .name = "sca3000_e04", + .scale = 19620, + .measurement_mode_freq = 100, + .measurement_mode_3db_freq = 38, + .option_mode_1 = SCA3000_OP_MODE_NARROW, + .option_mode_1_freq = 50, + .option_mode_1_3db_freq = 9, + .option_mode_2 = SCA3000_OP_MODE_WIDE, + .option_mode_2_freq = 400, + .option_mode_2_3db_freq = 70, + .mot_det_mult_xz = { 200, 300, 600, 1100, 2100, 4100 }, + .mot_det_mult_y = { 100, 200, 400, 7000, 1400, 2700, 54000 }, +}; + +static const struct sca3000_chip_info sca3000_chip_info_e05 = { + .name = "sca3000_e05", + .scale = 61313, + .measurement_mode_freq = 200, + .measurement_mode_3db_freq = 60, + .option_mode_1 = SCA3000_OP_MODE_NARROW, + .option_mode_1_freq = 50, + .option_mode_1_3db_freq = 9, + .option_mode_2 = SCA3000_OP_MODE_WIDE, + .option_mode_2_freq = 400, + .option_mode_2_3db_freq = 75, + .mot_det_mult_xz = { 600, 900, 1700, 3200, 6100, 11900 }, + .mot_det_mult_y = { 300, 600, 1200, 2000, 4100, 7800, 15600 }, }; static int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val) @@ -1435,24 +1428,42 @@ static const struct iio_info sca3000_info = { .write_event_config = &sca3000_write_event_config, }; +static void sca3000_stop_all_interrupts(void *data) +{ + struct iio_dev *indio_dev = data; + struct sca3000_state *st = iio_priv(indio_dev); + int ret; + + guard(mutex)(&st->lock); + + ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1); + if (ret) + return; + + sca3000_write_reg(st, SCA3000_REG_INT_MASK_ADDR, + (st->rx[0] & + ~(SCA3000_REG_INT_MASK_RING_THREE_QUARTER | + SCA3000_REG_INT_MASK_RING_HALF | + SCA3000_REG_INT_MASK_ALL_INTS))); +} + static int sca3000_probe(struct spi_device *spi) { - int ret; + struct device *dev = &spi->dev; struct sca3000_state *st; struct iio_dev *indio_dev; + int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); st->us = spi; mutex_init(&st->lock); - st->info = &sca3000_spi_chip_info_tbl[spi_get_device_id(spi) - ->driver_data]; + st->info = spi_get_device_match_data(spi); - indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->name = st->info->name; indio_dev->info = &sca3000_info; if (st->info->temp_output) { indio_dev->channels = sca3000_channels_with_temp; @@ -1464,78 +1475,39 @@ static int sca3000_probe(struct spi_device *spi) } indio_dev->modes = INDIO_DIRECT_MODE; - ret = devm_iio_kfifo_buffer_setup(&spi->dev, indio_dev, - &sca3000_ring_setup_ops); + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &sca3000_ring_setup_ops); if (ret) return ret; if (spi->irq) { - ret = request_threaded_irq(spi->irq, - NULL, - &sca3000_event_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "sca3000", - indio_dev); + ret = devm_request_threaded_irq(dev, spi->irq, NULL, + &sca3000_event_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "sca3000", + indio_dev); if (ret) return ret; } ret = sca3000_clean_setup(st); if (ret) - goto error_free_irq; + return ret; ret = sca3000_print_rev(indio_dev); if (ret) - goto error_free_irq; + return ret; - ret = iio_device_register(indio_dev); + ret = devm_add_action_or_reset(dev, sca3000_stop_all_interrupts, indio_dev); if (ret) - goto error_free_irq; + return ret; - return 0; - -error_free_irq: - if (spi->irq) - free_irq(spi->irq, indio_dev); - - return ret; -} - -static int sca3000_stop_all_interrupts(struct sca3000_state *st) -{ - int ret; - - mutex_lock(&st->lock); - ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1); - if (ret) - goto error_ret; - ret = sca3000_write_reg(st, SCA3000_REG_INT_MASK_ADDR, - (st->rx[0] & - ~(SCA3000_REG_INT_MASK_RING_THREE_QUARTER | - SCA3000_REG_INT_MASK_RING_HALF | - SCA3000_REG_INT_MASK_ALL_INTS))); -error_ret: - mutex_unlock(&st->lock); - return ret; -} - -static void sca3000_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct sca3000_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - /* Must ensure no interrupts can be generated after this! */ - sca3000_stop_all_interrupts(st); - if (spi->irq) - free_irq(spi->irq, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id sca3000_id[] = { - {"sca3000_d01", d01}, - {"sca3000_e02", e02}, - {"sca3000_e04", e04}, - {"sca3000_e05", e05}, + { "sca3000_d01", (kernel_ulong_t)&sca3000_chip_info_d01 }, + { "sca3000_e02", (kernel_ulong_t)&sca3000_chip_info_e02 }, + { "sca3000_e04", (kernel_ulong_t)&sca3000_chip_info_e04 }, + { "sca3000_e05", (kernel_ulong_t)&sca3000_chip_info_e05 }, { } }; MODULE_DEVICE_TABLE(spi, sca3000_id); @@ -1545,7 +1517,6 @@ static struct spi_driver sca3000_driver = { .name = "sca3000", }, .probe = sca3000_probe, - .remove = sca3000_remove, .id_table = sca3000_id, }; module_spi_driver(sca3000_driver); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 60038ae8dfc4..a9dedbb8eb46 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -60,9 +60,14 @@ config AD4030 tristate "Analog Devices AD4030 ADC Driver" depends on SPI depends on GPIOLIB + depends on PWM select REGMAP select IIO_BUFFER + select IIO_BUFFER_DMA + select IIO_BUFFER_DMAENGINE select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD + select SPI_OFFLOAD_TRIGGER_PWM help Say yes here to build support for Analog Devices AD4030 and AD4630 high speed SPI analog to digital converters (ADC). @@ -413,8 +418,10 @@ config AD7768_1 select REGMAP_SPI select RATIONAL select IIO_BUFFER + select IIO_BUFFER_DMAENGINE select IIO_TRIGGER select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD help Say yes here to build support for Analog Devices AD7768-1 SPI simultaneously sampling sigma-delta analog to digital converter (ADC). @@ -1366,6 +1373,32 @@ config QCOM_SPMI_ADC5 To compile this driver as a module, choose M here: the module will be called qcom-spmi-adc5. +config QCOM_SPMI_ADC5_GEN3 + tristate "Qualcomm Technologies Inc. SPMI PMIC5 GEN3 ADC" + depends on SPMI && THERMAL + select REGMAP_SPMI + select QCOM_VADC_COMMON + select AUXILIARY_BUS + help + IIO Voltage PMIC5 Gen3 ADC driver for Qualcomm Technologies Inc. + + The driver supports reading multiple channels. The ADC is a 16-bit + sigma-delta ADC. The hardware supports calibrated results for + conversion requests and clients include reading phone power supply + voltage, on board system thermistors connected to the PMIC ADC, + PMIC die temperature, charger temperature, battery current, USB + voltage input and voltage signals connected to supported PMIC GPIO + pins. The hardware supports internal pull-up for thermistors and can + choose between a 30k, 100k or 400k ohm pull up using the ADC channels. + + In addition, the same driver supports ADC thermal monitoring devices + too. They appear as thermal zones with multiple trip points. A thermal + client sets threshold temperature for both warm and cool trips and + gets updated when a threshold is reached. + + To compile this driver as a module, choose M here: the module will + be called qcom-spmi-adc5-gen3. + config RCAR_GYRO_ADC tristate "Renesas R-Car GyroADC driver" depends on ARCH_RCAR_GEN2 || COMPILE_TEST diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index c76550415ff1..097357d146ba 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -116,6 +116,7 @@ obj-$(CONFIG_PAC1934) += pac1934.o obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o +obj-$(CONFIG_QCOM_SPMI_ADC5_GEN3) += qcom-spmi-adc5-gen3.o obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o obj-$(CONFIG_QCOM_SPMI_RRADC) += qcom-spmi-rradc.o obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c index 68446db9bef1..9c5f19321e3b 100644 --- a/drivers/iio/adc/ad4030.c +++ b/drivers/iio/adc/ad4030.c @@ -14,15 +14,26 @@ */ #include +#include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include +#include #include #include #include +#include + +#include +#include +#include +#include #define AD4030_REG_INTERFACE_CONFIG_A 0x00 #define AD4030_REG_INTERFACE_CONFIG_A_SW_RESET (BIT(0) | BIT(7)) @@ -37,6 +48,8 @@ #define AD4030_REG_CHIP_GRADE_AD4630_24_GRADE 0x00 #define AD4030_REG_CHIP_GRADE_AD4632_16_GRADE 0x05 #define AD4030_REG_CHIP_GRADE_AD4632_24_GRADE 0x02 +#define AD4030_REG_CHIP_GRADE_ADAQ4216_GRADE 0x1E +#define AD4030_REG_CHIP_GRADE_ADAQ4224_GRADE 0x1C #define AD4030_REG_CHIP_GRADE_MASK_CHIP_GRADE GENMASK(7, 3) #define AD4030_REG_SCRATCH_PAD 0x0A #define AD4030_REG_SPI_REVISION 0x0B @@ -111,6 +124,12 @@ #define AD4632_TCYC_NS 2000 #define AD4632_TCYC_ADJUSTED_NS (AD4632_TCYC_NS - AD4030_TCNVL_NS) #define AD4030_TRESET_COM_DELAY_MS 750 +/* Datasheet says 9.8ns, so use the closest integer value */ +#define AD4030_TQUIET_CNV_DELAY_NS 10 + +/* HARDWARE_GAIN */ +#define ADAQ4616_PGA_PINS 2 +#define ADAQ4616_PGA_GAIN_MAX_NANO (NANO * 2 / 3) enum ad4030_out_mode { AD4030_OUT_DATA_MD_DIFF, @@ -132,15 +151,35 @@ enum { AD4030_SCAN_TYPE_AVG, }; +/* + * Gains computed as fractions of 1000 so they can be expressed by integers. + */ +static const int adaq4216_hw_gains_vpv[] = { + 1 * MILLI / 3, /* 0.333 */ + 5 * MILLI / 9, /* 0.555 */ + 20 * MILLI / 9, /* 0.2222 */ + 20 * MILLI / 3, /* 0.6666 */ +}; + +static const int adaq4216_hw_gains_frac[][2] = { + { 1, 3 }, /* 1/3 V/V gain */ + { 5, 9 }, /* 5/9 V/V gain */ + { 20, 9 }, /* 20/9 V/V gain */ + { 20, 3 }, /* 20/3 V/V gain */ +}; + struct ad4030_chip_info { const char *name; const unsigned long *available_masks; const struct iio_chan_spec channels[AD4030_MAX_IIO_CHANNEL_NB]; + const struct iio_chan_spec offload_channels[AD4030_MAX_IIO_CHANNEL_NB]; u8 grade; u8 precision_bits; + bool has_pga; /* Number of hardware channels */ int num_voltage_inputs; unsigned int tcyc_ns; + unsigned int max_sample_rate_hz; }; struct ad4030_state { @@ -153,6 +192,18 @@ struct ad4030_state { int offset_avail[3]; unsigned int avg_log2; enum ad4030_out_mode mode; + /* Offload sampling */ + struct spi_transfer offload_xfer; + struct spi_message offload_msg; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct spi_offload_trigger_config offload_trigger_config; + struct pwm_device *cnv_trigger; + size_t scale_avail_size; + struct pwm_waveform cnv_wf; + unsigned int scale_avail[ARRAY_SIZE(adaq4216_hw_gains_vpv)][2]; + struct gpio_descs *pga_gpios; + unsigned int pga_index; /* * DMA (thus cache coherency maintenance) requires the transfer buffers @@ -209,8 +260,9 @@ struct ad4030_state { * - voltage0-voltage1 * - voltage2-voltage3 */ -#define AD4030_CHAN_DIFF(_idx, _scan_type) { \ +#define __AD4030_CHAN_DIFF(_idx, _scan_type, _offload, _pga) { \ .info_mask_shared_by_all = \ + (_offload ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0) | \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .info_mask_shared_by_all_available = \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ @@ -219,6 +271,7 @@ struct ad4030_state { BIT(IIO_CHAN_INFO_CALIBBIAS) | \ BIT(IIO_CHAN_INFO_RAW), \ .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + (_pga ? BIT(IIO_CHAN_INFO_SCALE) : 0) | \ BIT(IIO_CHAN_INFO_CALIBSCALE), \ .type = IIO_VOLTAGE, \ .indexed = 1, \ @@ -232,10 +285,33 @@ struct ad4030_state { .num_ext_scan_type = ARRAY_SIZE(_scan_type), \ } +#define AD4030_CHAN_DIFF(_idx, _scan_type) \ + __AD4030_CHAN_DIFF(_idx, _scan_type, 0, 0) + +#define AD4030_OFFLOAD_CHAN_DIFF(_idx, _scan_type) \ + __AD4030_CHAN_DIFF(_idx, _scan_type, 1, 0) + +#define ADAQ4216_CHAN_DIFF(_idx, _scan_type) \ + __AD4030_CHAN_DIFF(_idx, _scan_type, 0, 1) + +#define ADAQ4216_OFFLOAD_CHAN_DIFF(_idx, _scan_type) \ + __AD4030_CHAN_DIFF(_idx, _scan_type, 1, 1) + +/* + * AD4030 can average over 2^N samples, where N = 1, 2, 3, ..., 16. + * We use N = 0 to mean no sample averaging. + */ static const int ad4030_average_modes[] = { - 1, 2, 4, 8, 16, 32, 64, 128, - 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, - 65536, + BIT(0), /* No sampling average */ + BIT(1), BIT(2), BIT(3), BIT(4), + BIT(5), BIT(6), BIT(7), BIT(8), + BIT(9), BIT(10), BIT(11), BIT(12), + BIT(13), BIT(14), BIT(15), BIT(16), +}; + +static const struct spi_offload_config ad4030_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, }; static int ad4030_enter_config_mode(struct ad4030_state *st) @@ -377,6 +453,65 @@ static const struct regmap_config ad4030_regmap_config = { .max_register = AD4030_REG_DIG_ERR, }; +static void ad4030_fill_scale_avail(struct ad4030_state *st) +{ + unsigned int mag_bits, int_part, fract_part; + u64 range; + + /* + * The maximum precision of differential channels is retrieved from the + * chip properties. The output code of differential channels is in two's + * complement format (i.e. signed), so the MSB is the sign bit and only + * (precision_bits - 1) bits express voltage magnitude. + */ + mag_bits = st->chip->precision_bits - 1; + + for (unsigned int i = 0; i < ARRAY_SIZE(adaq4216_hw_gains_frac); i++) { + range = mult_frac(st->vref_uv, adaq4216_hw_gains_frac[i][1], + adaq4216_hw_gains_frac[i][0]); + /* + * If range were in mV, we would multiply it by NANO below. + * Though, range is in µV so multiply it by MICRO only so the + * result after right shift and division scales output codes to + * millivolts. + */ + int_part = div_u64_rem((range * MICRO) >> mag_bits, NANO, &fract_part); + st->scale_avail[i][0] = int_part; + st->scale_avail[i][1] = fract_part; + } +} + +static int ad4030_set_pga_gain(struct ad4030_state *st) +{ + DECLARE_BITMAP(bitmap, ADAQ4616_PGA_PINS) = { }; + + bitmap_write(bitmap, st->pga_index, 0, ADAQ4616_PGA_PINS); + + return gpiod_multi_set_value_cansleep(st->pga_gpios, bitmap); +} + +static int ad4030_set_pga(struct iio_dev *indio_dev, int gain_int, int gain_fract) +{ + struct ad4030_state *st = iio_priv(indio_dev); + unsigned int mag_bits = st->chip->precision_bits - 1; + unsigned int tmp; + u64 gain_nano; + + if (!st->pga_gpios) + return -EINVAL; + + gain_nano = gain_int * NANO + gain_fract; + if (!in_range(gain_nano, 1, ADAQ4616_PGA_GAIN_MAX_NANO)) + return -EINVAL; + + tmp = DIV_ROUND_CLOSEST_ULL(gain_nano << mag_bits, NANO); + gain_nano = DIV_ROUND_CLOSEST(st->vref_uv, tmp); + st->pga_index = find_closest(gain_nano, adaq4216_hw_gains_vpv, + ARRAY_SIZE(adaq4216_hw_gains_vpv)); + + return ad4030_set_pga_gain(st); +} + static int ad4030_get_chan_scale(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, @@ -389,6 +524,13 @@ static int ad4030_get_chan_scale(struct iio_dev *indio_dev, if (IS_ERR(scan_type)) return PTR_ERR(scan_type); + /* The LSB of the 8-bit common-mode data is always vref/256. */ + if (st->chip->has_pga && scan_type->realbits != 8) { + *val = st->scale_avail[st->pga_index][0]; + *val2 = st->scale_avail[st->pga_index][1]; + return IIO_VAL_INT_PLUS_NANO; + } + if (chan->differential) *val = (st->vref_uv * 2) / MILLI; else @@ -451,6 +593,102 @@ static int ad4030_get_chan_calibbias(struct iio_dev *indio_dev, } } +static void ad4030_get_sampling_freq(struct ad4030_state *st, int *freq) +{ + struct spi_offload_trigger_config *config = &st->offload_trigger_config; + + /* + * Conversion data is fetched from the device when the offload transfer + * is triggered. Thus, provide the SPI offload trigger frequency as the + * sampling frequency. + */ + *freq = config->periodic.frequency_hz; +} + +static int ad4030_update_conversion_rate(struct ad4030_state *st, + unsigned int freq_hz, unsigned int avg_log2) +{ + struct spi_offload_trigger_config *config = &st->offload_trigger_config; + unsigned int offload_period_ns, cnv_rate_hz; + struct pwm_waveform cnv_wf = { }; + u64 target = AD4030_TCNVH_NS; + u64 offload_offset_ns; + int ret; + + /* + * When averaging/oversampling over N samples, we fire the offload + * trigger once at every N pulses of the CNV signal. Conversely, the CNV + * signal needs to be N times faster than the offload trigger. Take that + * into account to correctly re-evaluate both the PWM waveform connected + * to CNV and the SPI offload trigger. + */ + cnv_rate_hz = freq_hz << avg_log2; + + cnv_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, cnv_rate_hz); + /* + * The datasheet lists a minimum time of 9.8 ns, but no maximum. If the + * rounded PWM's value is less than 10, increase the target value by 10 + * and attempt to round the waveform again, until the value is at least + * 10 ns. Use a separate variable to represent the target in case the + * rounding is severe enough to keep putting the first few results under + * the minimum 10ns condition checked by the while loop. + */ + do { + cnv_wf.duty_length_ns = target; + ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf); + if (ret) + return ret; + target += AD4030_TCNVH_NS; + } while (cnv_wf.duty_length_ns < AD4030_TCNVH_NS); + + /* + * The CNV waveform period (period_length_ns) might get rounded down by + * pwm_round_waveform_might_sleep(). Check the resultant PWM period + * is not smaller than the minimum data conversion cycle time. + */ + if (!in_range(cnv_wf.period_length_ns, AD4030_TCYC_NS, INT_MAX)) + return -EINVAL; + + offload_period_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq_hz); + + config->periodic.frequency_hz = DIV_ROUND_UP(HZ_PER_GHZ, offload_period_ns); + + /* + * The hardware does the capture on zone 2 (when SPI trigger PWM + * is used). This means that the SPI trigger signal should happen at + * tsync + tquiet_con_delay being tsync the conversion signal period + * and tquiet_con_delay 9.8ns. Hence set the PWM phase accordingly. + * + * The PWM waveform API only supports nanosecond resolution right now, + * so round this setting to the closest available value. + */ + offload_offset_ns = AD4030_TQUIET_CNV_DELAY_NS; + do { + config->periodic.offset_ns = offload_offset_ns; + ret = spi_offload_trigger_validate(st->offload_trigger, config); + if (ret) + return ret; + offload_offset_ns += AD4030_TQUIET_CNV_DELAY_NS; + } while (config->periodic.offset_ns < AD4030_TQUIET_CNV_DELAY_NS); + + st->cnv_wf = cnv_wf; + + return 0; +} + +static int ad4030_set_sampling_freq(struct iio_dev *indio_dev, int freq_hz) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + if (freq_hz == 0) + return -EINVAL; + + if (!in_range(freq_hz, 0, st->chip->max_sample_rate_hz)) + return -ERANGE; + + return ad4030_update_conversion_rate(st, freq_hz, st->avg_log2); +} + static int ad4030_set_chan_calibscale(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int gain_int, @@ -510,11 +748,30 @@ static int ad4030_set_avg_frame_len(struct iio_dev *dev, int avg_val) struct ad4030_state *st = iio_priv(dev); unsigned int avg_log2 = ilog2(avg_val); unsigned int last_avg_idx = ARRAY_SIZE(ad4030_average_modes) - 1; + int freq_hz; int ret; if (avg_val < 0 || avg_val > ad4030_average_modes[last_avg_idx]) return -EINVAL; + if (st->offload_trigger) { + /* + * The sample averaging and sampling frequency configurations + * are mutually dependent on each other. That's because the + * effective data sample rate is fCNV / 2^N, where N is the + * number of samples being averaged. + * + * When SPI offload is supported and we have control over the + * sample rate, the conversion start signal (CNV) and the SPI + * offload trigger frequencies must be re-evaluated so data is + * fetched only after 'avg_val' conversions. + */ + ad4030_get_sampling_freq(st, &freq_hz); + ret = ad4030_update_conversion_rate(st, freq_hz, avg_log2); + if (ret) + return ret; + } + ret = regmap_write(st->regmap, AD4030_REG_AVG, AD4030_REG_AVG_MASK_AVG_SYNC | FIELD_PREP(AD4030_REG_AVG_MASK_AVG_VAL, avg_log2)); @@ -623,7 +880,7 @@ static int ad4030_conversion(struct iio_dev *indio_dev) /* Add one byte if we are using a differential + common byte mode */ bytes_to_read += (st->mode == AD4030_OUT_DATA_MD_24_DIFF_8_COM || st->mode == AD4030_OUT_DATA_MD_16_DIFF_8_COM) ? 1 : 0; - /* Mulitiply by the number of hardware channels */ + /* Multiply by the number of hardware channels */ bytes_to_read *= st->chip->num_voltage_inputs; for (i = 0; i < cnv_nb; i++) { @@ -742,6 +999,15 @@ static int ad4030_read_avail(struct iio_dev *indio_dev, *length = ARRAY_SIZE(ad4030_average_modes); return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + if (st->scale_avail_size == 1) + *vals = (int *)st->scale_avail[st->pga_index]; + else + *vals = (int *)st->scale_avail; + *length = st->scale_avail_size * 2; /* print int and nano part */ + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + default: return -EINVAL; } @@ -767,6 +1033,10 @@ static int ad4030_read_raw_dispatch(struct iio_dev *indio_dev, *val = BIT(st->avg_log2); return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ad4030_get_sampling_freq(st, val); + return IIO_VAL_INT; + default: return -EINVAL; } @@ -807,6 +1077,12 @@ static int ad4030_write_raw_dispatch(struct iio_dev *indio_dev, case IIO_CHAN_INFO_OVERSAMPLING_RATIO: return ad4030_set_avg_frame_len(indio_dev, val); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4030_set_sampling_freq(indio_dev, val); + + case IIO_CHAN_INFO_SCALE: + return ad4030_set_pga(indio_dev, val, val2); + default: return -EINVAL; } @@ -828,6 +1104,17 @@ static int ad4030_write_raw(struct iio_dev *indio_dev, return ret; } +static int ad4030_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + static int ad4030_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval) { @@ -874,6 +1161,7 @@ static const struct iio_info ad4030_iio_info = { .read_avail = ad4030_read_avail, .read_raw = ad4030_read_raw, .write_raw = ad4030_write_raw, + .write_raw_get_fmt = &ad4030_write_raw_get_fmt, .debugfs_reg_access = ad4030_reg_access, .read_label = ad4030_read_label, .get_current_scan_type = ad4030_get_current_scan_type, @@ -896,6 +1184,86 @@ static const struct iio_buffer_setup_ops ad4030_buffer_setup_ops = { .validate_scan_mask = ad4030_validate_scan_mask, }; +static void ad4030_prepare_offload_msg(struct iio_dev *indio_dev) +{ + struct ad4030_state *st = iio_priv(indio_dev); + u8 offload_bpw; + + if (st->mode == AD4030_OUT_DATA_MD_30_AVERAGED_DIFF) + offload_bpw = 32; + else + offload_bpw = st->chip->precision_bits; + + st->offload_xfer.bits_per_word = offload_bpw; + st->offload_xfer.len = spi_bpw_to_bytes(offload_bpw); + st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); +} + +static int ad4030_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4030_state *st = iio_priv(indio_dev); + unsigned int reg_modes; + int ret; + + /* + * When data from 2 analog input channels is output through a single + * bus line (interleaved mode (LANE_MD == 0b11)) and gets pushed through + * DMA, extra hardware is required to do the de-interleaving. While we + * don't support such hardware configurations, disallow interleaved mode + * when using SPI offload. + */ + ret = regmap_read(st->regmap, AD4030_REG_MODES, ®_modes); + if (ret) + return ret; + + if (st->chip->num_voltage_inputs > 1 && + FIELD_GET(AD4030_REG_MODES_MASK_LANE_MODE, reg_modes) == AD4030_LANE_MD_INTERLEAVED) + return -EINVAL; + + ad4030_prepare_offload_msg(indio_dev); + st->offload_msg.offload = st->offload; + ret = spi_optimize_message(st->spi, &st->offload_msg); + if (ret) + return ret; + + ret = pwm_set_waveform_might_sleep(st->cnv_trigger, &st->cnv_wf, false); + if (ret) + goto out_unoptimize; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &st->offload_trigger_config); + if (ret) + goto out_pwm_disable; + + return 0; + +out_pwm_disable: + pwm_disable(st->cnv_trigger); +out_unoptimize: + spi_unoptimize_message(&st->offload_msg); + + return ret; +} + +static int ad4030_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + + pwm_disable(st->cnv_trigger); + + spi_unoptimize_message(&st->offload_msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ad4030_offload_buffer_setup_ops = { + .postenable = &ad4030_offload_buffer_postenable, + .predisable = &ad4030_offload_buffer_predisable, +}; + static int ad4030_regulators_get(struct ad4030_state *st) { struct device *dev = &st->spi->dev; @@ -965,6 +1333,24 @@ static int ad4030_detect_chip_info(const struct ad4030_state *st) return 0; } +static int ad4030_pwm_get(struct ad4030_state *st) +{ + struct device *dev = &st->spi->dev; + + st->cnv_trigger = devm_pwm_get(dev, NULL); + if (IS_ERR(st->cnv_trigger)) + return dev_err_probe(dev, PTR_ERR(st->cnv_trigger), + "Failed to get CNV PWM\n"); + + /* + * Preemptively disable the PWM, since we only want to enable it with + * the buffer. + */ + pwm_disable(st->cnv_trigger); + + return 0; +} + static int ad4030_config(struct ad4030_state *st) { int ret; @@ -992,6 +1378,51 @@ static int ad4030_config(struct ad4030_state *st) return 0; } +static int ad4030_spi_offload_setup(struct iio_dev *indio_dev, + struct ad4030_state *st) +{ + struct device *dev = &st->spi->dev; + struct dma_chan *rx_dma; + + indio_dev->setup_ops = &ad4030_offload_buffer_setup_ops; + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + st->offload_trigger_config.type = SPI_OFFLOAD_TRIGGER_PERIODIC; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); +} + +static int ad4030_setup_pga(struct device *dev, struct iio_dev *indio_dev, + struct ad4030_state *st) +{ + /* Setup GPIOs for PGA control */ + st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW); + if (IS_ERR(st->pga_gpios)) + return dev_err_probe(dev, PTR_ERR(st->pga_gpios), + "Failed to get PGA gpios.\n"); + + if (st->pga_gpios->ndescs != ADAQ4616_PGA_PINS) + return dev_err_probe(dev, -EINVAL, + "Expected %d GPIOs for PGA control.\n", + ADAQ4616_PGA_PINS); + + st->scale_avail_size = ARRAY_SIZE(adaq4216_hw_gains_vpv); + st->pga_index = 0; + + return 0; +} + static int ad4030_probe(struct spi_device *spi) { struct device *dev = &spi->dev; @@ -1034,6 +1465,14 @@ static int ad4030_probe(struct spi_device *spi) if (ret) return ret; + if (st->chip->has_pga) { + ret = ad4030_setup_pga(dev, indio_dev, st); + if (ret) + return ret; + + ad4030_fill_scale_avail(st); + } + ret = ad4030_config(st); if (ret) return ret; @@ -1043,24 +1482,58 @@ static int ad4030_probe(struct spi_device *spi) return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), "Failed to get cnv gpio\n"); - /* - * One hardware channel is split in two software channels when using - * common byte mode. Add one more channel for the timestamp. - */ - indio_dev->num_channels = 2 * st->chip->num_voltage_inputs + 1; indio_dev->name = st->chip->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &ad4030_iio_info; - indio_dev->channels = st->chip->channels; indio_dev->available_scan_masks = st->chip->available_masks; - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, - iio_pollfunc_store_time, - ad4030_trigger_handler, - &ad4030_buffer_setup_ops); - if (ret) - return dev_err_probe(dev, ret, - "Failed to setup triggered buffer\n"); + st->offload = devm_spi_offload_get(dev, spi, &ad4030_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + /* Fall back to low speed usage when no SPI offload is available. */ + if (ret == -ENODEV) { + /* + * One hardware channel is split in two software channels when + * using common byte mode. Add one more channel for the timestamp. + */ + indio_dev->num_channels = 2 * st->chip->num_voltage_inputs + 1; + indio_dev->channels = st->chip->channels; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad4030_trigger_handler, + &ad4030_buffer_setup_ops); + if (ret) + return dev_err_probe(dev, ret, + "Failed to setup triggered buffer\n"); + } else if (ret) { + return dev_err_probe(dev, ret, "failed to get offload\n"); + } else { + /* + * Offloaded SPI transfers can't support software timestamp so + * no additional timestamp channel is added. + */ + indio_dev->num_channels = st->chip->num_voltage_inputs; + indio_dev->channels = st->chip->offload_channels; + ret = ad4030_spi_offload_setup(indio_dev, st); + if (ret) + return dev_err_probe(dev, ret, + "Failed to setup SPI offload\n"); + + ret = ad4030_pwm_get(st); + if (ret) + return dev_err_probe(dev, ret, "Failed to get PWM\n"); + + /* + * Start with a slower sampling rate so there is some room for + * adjusting the sample averaging and the sampling frequency + * without hitting the maximum conversion rate. + */ + ret = ad4030_update_conversion_rate(st, st->chip->max_sample_rate_hz >> 4, + st->avg_log2); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set offload samp freq\n"); + } return devm_iio_device_register(dev, indio_dev); } @@ -1098,11 +1571,28 @@ static const struct iio_scan_type ad4030_24_scan_types[] = { }, }; +static const struct iio_scan_type ad4030_24_offload_scan_types[] = { + [AD4030_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .shift = 0, + .endianness = IIO_CPU, + }, + [AD4030_SCAN_TYPE_AVG] = { + .sign = 's', + .realbits = 30, + .storagebits = 32, + .shift = 2, + .endianness = IIO_CPU, + }, +}; + static const struct iio_scan_type ad4030_16_scan_types[] = { [AD4030_SCAN_TYPE_NORMAL] = { .sign = 's', - .storagebits = 32, .realbits = 16, + .storagebits = 32, .shift = 16, .endianness = IIO_BE, }, @@ -1115,6 +1605,23 @@ static const struct iio_scan_type ad4030_16_scan_types[] = { } }; +static const struct iio_scan_type ad4030_16_offload_scan_types[] = { + [AD4030_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .shift = 0, + .endianness = IIO_CPU, + }, + [AD4030_SCAN_TYPE_AVG] = { + .sign = 's', + .realbits = 30, + .storagebits = 32, + .shift = 2, + .endianness = IIO_CPU, + }, +}; + static const struct ad4030_chip_info ad4030_24_chip_info = { .name = "ad4030-24", .available_masks = ad4030_channel_masks, @@ -1123,10 +1630,14 @@ static const struct ad4030_chip_info ad4030_24_chip_info = { AD4030_CHAN_CMO(1, 0), IIO_CHAN_SOFT_TIMESTAMP(2), }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_24_offload_scan_types), + }, .grade = AD4030_REG_CHIP_GRADE_AD4030_24_GRADE, .precision_bits = 24, .num_voltage_inputs = 1, .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, }; static const struct ad4030_chip_info ad4630_16_chip_info = { @@ -1139,10 +1650,15 @@ static const struct ad4030_chip_info ad4630_16_chip_info = { AD4030_CHAN_CMO(3, 1), IIO_CHAN_SOFT_TIMESTAMP(4), }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_16_offload_scan_types), + AD4030_OFFLOAD_CHAN_DIFF(1, ad4030_16_offload_scan_types), + }, .grade = AD4030_REG_CHIP_GRADE_AD4630_16_GRADE, .precision_bits = 16, .num_voltage_inputs = 2, .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, }; static const struct ad4030_chip_info ad4630_24_chip_info = { @@ -1155,10 +1671,15 @@ static const struct ad4030_chip_info ad4630_24_chip_info = { AD4030_CHAN_CMO(3, 1), IIO_CHAN_SOFT_TIMESTAMP(4), }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_24_offload_scan_types), + AD4030_OFFLOAD_CHAN_DIFF(1, ad4030_24_offload_scan_types), + }, .grade = AD4030_REG_CHIP_GRADE_AD4630_24_GRADE, .precision_bits = 24, .num_voltage_inputs = 2, .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, }; static const struct ad4030_chip_info ad4632_16_chip_info = { @@ -1171,10 +1692,15 @@ static const struct ad4030_chip_info ad4632_16_chip_info = { AD4030_CHAN_CMO(3, 1), IIO_CHAN_SOFT_TIMESTAMP(4), }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_16_offload_scan_types), + AD4030_OFFLOAD_CHAN_DIFF(1, ad4030_16_offload_scan_types), + }, .grade = AD4030_REG_CHIP_GRADE_AD4632_16_GRADE, .precision_bits = 16, .num_voltage_inputs = 2, .tcyc_ns = AD4632_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 500 * HZ_PER_KHZ, }; static const struct ad4030_chip_info ad4632_24_chip_info = { @@ -1187,10 +1713,53 @@ static const struct ad4030_chip_info ad4632_24_chip_info = { AD4030_CHAN_CMO(3, 1), IIO_CHAN_SOFT_TIMESTAMP(4), }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_24_offload_scan_types), + AD4030_OFFLOAD_CHAN_DIFF(1, ad4030_24_offload_scan_types), + }, .grade = AD4030_REG_CHIP_GRADE_AD4632_24_GRADE, .precision_bits = 24, .num_voltage_inputs = 2, .tcyc_ns = AD4632_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 500 * HZ_PER_KHZ, +}; + +static const struct ad4030_chip_info adaq4216_chip_info = { + .name = "adaq4216", + .available_masks = ad4030_channel_masks, + .channels = { + ADAQ4216_CHAN_DIFF(0, ad4030_16_scan_types), + AD4030_CHAN_CMO(1, 0), + IIO_CHAN_SOFT_TIMESTAMP(2), + }, + .offload_channels = { + ADAQ4216_OFFLOAD_CHAN_DIFF(0, ad4030_16_offload_scan_types), + }, + .grade = AD4030_REG_CHIP_GRADE_ADAQ4216_GRADE, + .precision_bits = 16, + .has_pga = true, + .num_voltage_inputs = 1, + .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, +}; + +static const struct ad4030_chip_info adaq4224_chip_info = { + .name = "adaq4224", + .available_masks = ad4030_channel_masks, + .channels = { + ADAQ4216_CHAN_DIFF(0, ad4030_24_scan_types), + AD4030_CHAN_CMO(1, 0), + IIO_CHAN_SOFT_TIMESTAMP(2), + }, + .offload_channels = { + ADAQ4216_OFFLOAD_CHAN_DIFF(0, ad4030_24_offload_scan_types), + }, + .grade = AD4030_REG_CHIP_GRADE_ADAQ4224_GRADE, + .precision_bits = 24, + .has_pga = true, + .num_voltage_inputs = 1, + .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, }; static const struct spi_device_id ad4030_id_table[] = { @@ -1199,6 +1768,8 @@ static const struct spi_device_id ad4030_id_table[] = { { "ad4630-24", (kernel_ulong_t)&ad4630_24_chip_info }, { "ad4632-16", (kernel_ulong_t)&ad4632_16_chip_info }, { "ad4632-24", (kernel_ulong_t)&ad4632_24_chip_info }, + { "adaq4216", (kernel_ulong_t)&adaq4216_chip_info }, + { "adaq4224", (kernel_ulong_t)&adaq4224_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4030_id_table); @@ -1209,6 +1780,8 @@ static const struct of_device_id ad4030_of_match[] = { { .compatible = "adi,ad4630-24", .data = &ad4630_24_chip_info }, { .compatible = "adi,ad4632-16", .data = &ad4632_16_chip_info }, { .compatible = "adi,ad4632-24", .data = &ad4632_24_chip_info }, + { .compatible = "adi,adaq4216", .data = &adaq4216_chip_info }, + { .compatible = "adi,adaq4224", .data = &adaq4224_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad4030_of_match); @@ -1226,3 +1799,4 @@ module_spi_driver(ad4030_driver); MODULE_AUTHOR("Esteban Blanc "); MODULE_DESCRIPTION("Analog Devices AD4630 ADC family driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c index 8b03736d55fc..222499a45b9b 100644 --- a/drivers/iio/adc/ad4062.c +++ b/drivers/iio/adc/ad4062.c @@ -1197,11 +1197,14 @@ static int ad4062_write_event_value(struct iio_dev *indio_dev, * The AD4062 in burst averaging mode increases realbits from 16-bits to * 20-bits, increasing the storagebits from 16-bits to 32-bits. */ -static inline size_t ad4062_sizeof_storagebits(struct ad4062_state *st) +static inline int ad4062_sizeof_storagebits(struct ad4062_state *st) { const struct iio_scan_type *scan_type = iio_get_current_scan_type(st->indio_dev, st->chip->channels); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + return BITS_TO_BYTES(scan_type->storagebits); } @@ -1231,7 +1234,12 @@ static int pm_ad4062_triggered_buffer_postenable(struct ad4062_state *st) if (ret) return ret; - st->conv_sizeof = ad4062_sizeof_storagebits(st); + ret = ad4062_sizeof_storagebits(st); + if (ret < 0) + return ret; + + st->conv_sizeof = ret; + st->conv_addr = ad4062_get_conv_addr(st, st->conv_sizeof); /* CONV_READ requires read to trigger first sample. */ struct i3c_xfer xfer_sample[2] = { diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index 7cf3b6ed7940..204ad198342b 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -127,10 +127,13 @@ #define AD4080_SPI_READ BIT(7) #define AD4080_CHIP_ID 0x0050 #define AD4081_CHIP_ID 0x0051 +#define AD4082_CHIP_ID 0x0052 #define AD4083_CHIP_ID 0x0053 #define AD4084_CHIP_ID 0x0054 +#define AD4085_CHIP_ID 0x0055 #define AD4086_CHIP_ID 0x0056 #define AD4087_CHIP_ID 0x0057 +#define AD4088_CHIP_ID 0x0058 #define AD4080_LVDS_CNV_CLK_CNT_MAX 7 @@ -185,7 +188,6 @@ struct ad4080_state { */ struct mutex lock; unsigned int num_lanes; - unsigned int dec_rate; unsigned long clk_rate; enum ad4080_filter_type filter_type; bool lvds_cnv_en; @@ -442,14 +444,20 @@ static const struct iio_chan_spec ad4080_channel = AD4080_CHANNEL_DEFINE(20, 32) static const struct iio_chan_spec ad4081_channel = AD4080_CHANNEL_DEFINE(20, 32); +static const struct iio_chan_spec ad4082_channel = AD4080_CHANNEL_DEFINE(20, 32); + static const struct iio_chan_spec ad4083_channel = AD4080_CHANNEL_DEFINE(16, 16); static const struct iio_chan_spec ad4084_channel = AD4080_CHANNEL_DEFINE(16, 16); +static const struct iio_chan_spec ad4085_channel = AD4080_CHANNEL_DEFINE(16, 16); + static const struct iio_chan_spec ad4086_channel = AD4080_CHANNEL_DEFINE(14, 16); static const struct iio_chan_spec ad4087_channel = AD4080_CHANNEL_DEFINE(14, 16); +static const struct iio_chan_spec ad4088_channel = AD4080_CHANNEL_DEFINE(14, 16); + static const struct ad4080_chip_info ad4080_chip_info = { .name = "ad4080", .product_id = AD4080_CHIP_ID, @@ -470,6 +478,16 @@ static const struct ad4080_chip_info ad4081_chip_info = { .lvds_cnv_clk_cnt_max = 2, }; +static const struct ad4080_chip_info ad4082_chip_info = { + .name = "ad4082", + .product_id = AD4082_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4082_channel, + .lvds_cnv_clk_cnt_max = 8, +}; + static const struct ad4080_chip_info ad4083_chip_info = { .name = "ad4083", .product_id = AD4083_CHIP_ID, @@ -490,6 +508,16 @@ static const struct ad4080_chip_info ad4084_chip_info = { .lvds_cnv_clk_cnt_max = 2, }; +static const struct ad4080_chip_info ad4085_chip_info = { + .name = "ad4085", + .product_id = AD4085_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4085_channel, + .lvds_cnv_clk_cnt_max = 8, +}; + static const struct ad4080_chip_info ad4086_chip_info = { .name = "ad4086", .product_id = AD4086_CHIP_ID, @@ -510,6 +538,16 @@ static const struct ad4080_chip_info ad4087_chip_info = { .lvds_cnv_clk_cnt_max = 1, }; +static const struct ad4080_chip_info ad4088_chip_info = { + .name = "ad4088", + .product_id = AD4088_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4088_channel, + .lvds_cnv_clk_cnt_max = 8, +}; + static int ad4080_setup(struct iio_dev *indio_dev) { struct ad4080_state *st = iio_priv(indio_dev); @@ -666,10 +704,13 @@ static int ad4080_probe(struct spi_device *spi) static const struct spi_device_id ad4080_id[] = { { "ad4080", (kernel_ulong_t)&ad4080_chip_info }, { "ad4081", (kernel_ulong_t)&ad4081_chip_info }, + { "ad4082", (kernel_ulong_t)&ad4082_chip_info }, { "ad4083", (kernel_ulong_t)&ad4083_chip_info }, { "ad4084", (kernel_ulong_t)&ad4084_chip_info }, + { "ad4085", (kernel_ulong_t)&ad4085_chip_info }, { "ad4086", (kernel_ulong_t)&ad4086_chip_info }, { "ad4087", (kernel_ulong_t)&ad4087_chip_info }, + { "ad4088", (kernel_ulong_t)&ad4088_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4080_id); @@ -677,10 +718,13 @@ MODULE_DEVICE_TABLE(spi, ad4080_id); static const struct of_device_id ad4080_of_match[] = { { .compatible = "adi,ad4080", &ad4080_chip_info }, { .compatible = "adi,ad4081", &ad4081_chip_info }, + { .compatible = "adi,ad4082", &ad4082_chip_info }, { .compatible = "adi,ad4083", &ad4083_chip_info }, { .compatible = "adi,ad4084", &ad4084_chip_info }, + { .compatible = "adi,ad4085", &ad4085_chip_info }, { .compatible = "adi,ad4086", &ad4086_chip_info }, { .compatible = "adi,ad4087", &ad4087_chip_info }, + { .compatible = "adi,ad4088", &ad4088_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad4080_of_match); diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c index 82205bfae531..77af0e6b2c59 100644 --- a/drivers/iio/adc/ad4170-4.c +++ b/drivers/iio/adc/ad4170-4.c @@ -275,9 +275,9 @@ static const unsigned int ad4170_reg_size[] = { }; enum ad4170_ref_buf { - AD4170_REF_BUF_PRE, /* Pre-charge referrence buffer */ - AD4170_REF_BUF_FULL, /* Full referrence buffering */ - AD4170_REF_BUF_BYPASS, /* Bypass referrence buffering */ + AD4170_REF_BUF_PRE, /* Pre-charge reference buffer */ + AD4170_REF_BUF_FULL, /* Full reference buffering */ + AD4170_REF_BUF_BYPASS, /* Bypass reference buffering */ }; /* maps adi,positive/negative-reference-buffer property values to enum */ diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index d36612352b44..f76a9e08f39e 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -1763,7 +1763,8 @@ static int ad7173_validate_openwire_ain_inputs(struct ad7173_state *st, static unsigned int ad7173_calc_openwire_thrsh_raw(struct ad7173_state *st, struct iio_chan_spec *chan, struct ad7173_channel *chan_st_priv, - unsigned int thrsh_mv) { + unsigned int thrsh_mv) +{ unsigned int thrsh_raw; thrsh_raw = diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 530e1d307860..8b1664f6b102 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -1402,9 +1402,6 @@ static int ad7192_probe(struct spi_device *spi) st->int_vref_mv = ret == -ENODEV ? avdd_mv : ret / MILLI; st->chip_info = spi_get_device_match_data(spi); - if (!st->chip_info) - return -ENODEV; - indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = st->chip_info->info; diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 3364ac6c4631..0ef36c249ab8 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -409,10 +409,8 @@ static int ad7266_probe(struct spi_device *spi) st->gpios[i] = devm_gpiod_get(&spi->dev, ad7266_gpio_labels[i], GPIOD_OUT_LOW); - if (IS_ERR(st->gpios[i])) { - ret = PTR_ERR(st->gpios[i]); - return ret; - } + if (IS_ERR(st->gpios[i])) + return PTR_ERR(st->gpios[i]); } } } else { diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c index bfd908deefc0..9f77990a03f9 100644 --- a/drivers/iio/adc/ad7380.c +++ b/drivers/iio/adc/ad7380.c @@ -77,8 +77,7 @@ #define AD7380_CONFIG1_REFSEL BIT(1) #define AD7380_CONFIG1_PMODE BIT(0) -#define AD7380_CONFIG2_SDO2 GENMASK(9, 8) -#define AD7380_CONFIG2_SDO BIT(8) +#define AD7380_CONFIG2_SDO GENMASK(9, 8) #define AD7380_CONFIG2_RESET GENMASK(7, 0) #define AD7380_CONFIG2_RESET_SOFT 0x3C @@ -92,11 +91,6 @@ #define T_CONVERT_X_NS 500 /* xth conversion start time (oversampling) */ #define T_POWERUP_US 5000 /* Power up */ -/* - * AD738x support several SDO lines to increase throughput, but driver currently - * supports only 1 SDO line (standard SPI transaction) - */ -#define AD7380_NUM_SDO_LINES 1 #define AD7380_DEFAULT_GAIN_MILLI 1000 /* @@ -888,6 +882,8 @@ struct ad7380_state { bool resolution_boost_enabled; unsigned int ch; bool seq; + /* How many SDO lines are wired up. */ + u8 num_sdo_lines; unsigned int vref_mv; unsigned int vcm_mv[MAX_NUM_CHANNELS]; unsigned int gain_milli[MAX_NUM_CHANNELS]; @@ -1084,7 +1080,7 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch) if (oversampling_ratio > 1) xfer.delay.value = T_CONVERT_0_NS + T_CONVERT_X_NS * (oversampling_ratio - 1) * - st->chip_info->num_simult_channels / AD7380_NUM_SDO_LINES; + st->chip_info->num_simult_channels / st->num_sdo_lines; return spi_sync_transfer(st->spi, &xfer, 1); } @@ -1113,7 +1109,7 @@ static int ad7380_update_xfers(struct ad7380_state *st, if (oversampling_ratio > 1) t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS * (oversampling_ratio - 1) * - st->chip_info->num_simult_channels / AD7380_NUM_SDO_LINES; + st->chip_info->num_simult_channels / st->num_sdo_lines; if (st->seq) { xfer[0].delay.value = xfer[1].delay.value = t_convert; @@ -1198,6 +1194,8 @@ static int ad7380_init_offload_msg(struct ad7380_state *st, xfer->bits_per_word = scan_type->realbits; xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; xfer->len = AD7380_SPI_BYTES(scan_type) * st->chip_info->num_simult_channels; + if (st->num_sdo_lines > 1) + xfer->multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; spi_message_init_with_transfers(&st->offload_msg, xfer, 1); st->offload_msg.offload = st->offload; @@ -1793,6 +1791,7 @@ static const struct iio_info ad7380_info = { static int ad7380_init(struct ad7380_state *st, bool external_ref_en) { + u32 sdo; int ret; /* perform hard reset */ @@ -1815,11 +1814,24 @@ static int ad7380_init(struct ad7380_state *st, bool external_ref_en) st->ch = 0; st->seq = false; - /* SPI 1-wire mode */ + /* SDO field has an irregular mapping. */ + switch (st->num_sdo_lines) { + case 1: + sdo = 1; + break; + case 2: + sdo = 0; + break; + case 4: + sdo = 2; + break; + default: + return -EINVAL; + } + return regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2, AD7380_CONFIG2_SDO, - FIELD_PREP(AD7380_CONFIG2_SDO, - AD7380_NUM_SDO_LINES)); + FIELD_PREP(AD7380_CONFIG2_SDO, sdo)); } static int ad7380_probe_spi_offload(struct iio_dev *indio_dev, @@ -1842,7 +1854,7 @@ static int ad7380_probe_spi_offload(struct iio_dev *indio_dev, "failed to get offload trigger\n"); sample_rate = st->chip_info->max_conversion_rate_hz * - AD7380_NUM_SDO_LINES / st->chip_info->num_simult_channels; + st->num_sdo_lines / st->chip_info->num_simult_channels; st->sample_freq_range[0] = 1; /* min */ st->sample_freq_range[1] = 1; /* step */ @@ -1850,7 +1862,7 @@ static int ad7380_probe_spi_offload(struct iio_dev *indio_dev, /* * Starting with a quite low frequency, to allow oversampling x32, - * user is then reponsible to adjust the frequency for the specific case. + * user is then responsible to adjust the frequency for the specific case. */ ret = ad7380_set_sample_freq(st, sample_rate / 32); if (ret) @@ -1887,6 +1899,13 @@ static int ad7380_probe(struct spi_device *spi) if (!st->chip_info) return dev_err_probe(dev, -EINVAL, "missing match data\n"); + st->num_sdo_lines = spi->num_rx_lanes; + + if (st->num_sdo_lines < 1 || st->num_sdo_lines > st->chip_info->num_simult_channels) + return dev_err_probe(dev, -EINVAL, + "invalid number of SDO lines (%d)\n", + st->num_sdo_lines); + ret = devm_regulator_bulk_get_enable(dev, st->chip_info->num_supplies, st->chip_info->supplies); @@ -2010,6 +2029,8 @@ static int ad7380_probe(struct spi_device *spi) st->normal_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; st->normal_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; st->normal_xfer[1].rx_buf = st->scan_data; + if (st->num_sdo_lines > 1) + st->normal_xfer[1].multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; spi_message_init_with_transfers(&st->normal_msg, st->normal_xfer, ARRAY_SIZE(st->normal_xfer)); @@ -2031,6 +2052,10 @@ static int ad7380_probe(struct spi_device *spi) st->seq_xfer[2].cs_change = 1; st->seq_xfer[2].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; st->seq_xfer[2].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + if (st->num_sdo_lines > 1) { + st->seq_xfer[2].multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; + st->seq_xfer[3].multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; + } spi_message_init_with_transfers(&st->seq_msg, st->seq_xfer, ARRAY_SIZE(st->seq_xfer)); diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index e16dede687d3..73fb734d06b2 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -25,12 +25,15 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -161,6 +164,8 @@ enum ad7768_filter_regval { enum ad7768_scan_type { AD7768_SCAN_TYPE_NORMAL, AD7768_SCAN_TYPE_HIGH_SPEED, + AD7768_SCAN_TYPE_OFFLOAD_NORMAL, + AD7768_SCAN_TYPE_OFFLOAD_HIGH_SPEED, }; enum { @@ -266,6 +271,18 @@ static const struct iio_scan_type ad7768_scan_type[] = { .storagebits = 16, .endianness = IIO_BE, }, + [AD7768_SCAN_TYPE_OFFLOAD_NORMAL] = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7768_SCAN_TYPE_OFFLOAD_HIGH_SPEED] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_CPU, + }, }; struct ad7768_chip_info { @@ -283,6 +300,8 @@ struct ad7768_chip_info { struct ad7768_state { struct spi_device *spi; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; struct regmap *regmap; struct regmap *regmap24; int vref_uv; @@ -290,7 +309,6 @@ struct ad7768_state { unsigned int vcm_output_sel; struct clk *mclk; unsigned int mclk_freq; - unsigned int mclk_div; unsigned int oversampling_ratio; enum ad7768_filter_type filter_type; unsigned int samp_freq; @@ -306,6 +324,8 @@ struct ad7768_state { struct gpio_desc *gpio_reset; const char *labels[AD7768_MAX_CHANNELS]; struct gpio_chip gpiochip; + struct spi_transfer offload_xfer; + struct spi_message offload_msg; const struct ad7768_chip_info *chip; bool en_spi_sync; struct mutex pga_lock; /* protect device internal state (PGA) */ @@ -464,13 +484,11 @@ static int ad7768_scan_direct(struct iio_dev *indio_dev) int readval, ret; reinit_completion(&st->completion); - - ret = ad7768_set_mode(st, AD7768_ONE_SHOT); - if (ret < 0) - return ret; + enable_irq(st->spi->irq); ret = wait_for_completion_timeout(&st->completion, msecs_to_jiffies(1000)); + disable_irq(st->spi->irq); if (!ret) return -ETIMEDOUT; @@ -487,14 +505,6 @@ static int ad7768_scan_direct(struct iio_dev *indio_dev) if (st->oversampling_ratio == 8) readval >>= 8; - /* - * Any SPI configuration of the AD7768-1 can only be - * performed in continuous conversion mode. - */ - ret = ad7768_set_mode(st, AD7768_CONTINUOUS); - if (ret < 0) - return ret; - return readval; } @@ -1138,6 +1148,10 @@ static int ad7768_get_current_scan_type(const struct iio_dev *indio_dev, { struct ad7768_state *st = iio_priv(indio_dev); + if (st->offload) + return st->oversampling_ratio == 8 ? + AD7768_SCAN_TYPE_OFFLOAD_HIGH_SPEED : AD7768_SCAN_TYPE_OFFLOAD_NORMAL; + return st->oversampling_ratio == 8 ? AD7768_SCAN_TYPE_HIGH_SPEED : AD7768_SCAN_TYPE_NORMAL; } @@ -1252,6 +1266,10 @@ static int ad7768_setup(struct iio_dev *indio_dev) return ret; } + ret = ad7768_set_mode(st, AD7768_CONTINUOUS); + if (ret) + return ret; + /* For backwards compatibility, try the adi,sync-in-gpios property */ st->gpio_sync_in = devm_gpiod_get_optional(&st->spi->dev, "adi,sync-in", GPIOD_OUT_LOW); @@ -1356,8 +1374,94 @@ static const struct iio_buffer_setup_ops ad7768_buffer_ops = { .predisable = &ad7768_buffer_predisable, }; +static int ad7768_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_DATA_READY, + }; + const struct iio_scan_type *scan_type; + unsigned int unused; + int ret; + + scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + st->offload_xfer.len = spi_bpw_to_bytes(scan_type->realbits); + st->offload_xfer.bits_per_word = scan_type->realbits; + st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + + spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); + st->offload_msg.offload = st->offload; + + ret = spi_optimize_message(st->spi, &st->offload_msg); + if (ret) { + dev_err(&st->spi->dev, "failed to prepare offload, err: %d\n", ret); + return ret; + } + + /* + * Write a 1 to the LSB of the INTERFACE_FORMAT register to enter + * continuous read mode. Subsequent data reads do not require an + * initial 8-bit write to query the ADC_DATA register. + */ + ret = regmap_write(st->regmap, AD7768_REG_INTERFACE_FORMAT, 0x01); + if (ret) + goto err_unoptimize_message; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &config); + if (ret) + goto err_exit_continuous_read_mode; + + return 0; + +err_exit_continuous_read_mode: + regmap_read(st->regmap24, AD7768_REG24_ADC_DATA, &unused); + +err_unoptimize_message: + spi_unoptimize_message(&st->offload_msg); + + return ret; +} + +static int ad7768_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + unsigned int unused; + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + spi_unoptimize_message(&st->offload_msg); + + /* + * To exit continuous read mode, perform a single read of the ADC_DATA + * reg (0x2C), which allows further configuration of the device. + */ + return regmap_read(st->regmap24, AD7768_REG24_ADC_DATA, &unused); +} + +static const struct iio_buffer_setup_ops ad7768_offload_buffer_ops = { + .postenable = ad7768_offload_buffer_postenable, + .predisable = ad7768_offload_buffer_predisable, +}; + +static int ad7768_set_trigger_state(struct iio_trigger *trig, bool enable) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct ad7768_state *st = iio_priv(indio_dev); + + if (enable) + enable_irq(st->spi->irq); + else + disable_irq(st->spi->irq); + + return 0; +} + static const struct iio_trigger_ops ad7768_trigger_ops = { .validate_device = iio_trigger_validate_own_device, + .set_trigger_state = ad7768_set_trigger_state, }; static int ad7768_set_channel_label(struct iio_dev *indio_dev, @@ -1590,6 +1694,36 @@ static int ad7768_parse_aaf_gain(struct device *dev, struct ad7768_state *st) return 0; } +static bool ad7768_offload_trigger_match(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + if (type != SPI_OFFLOAD_TRIGGER_DATA_READY) + return false; + + /* Up to 2 args are allowed, but only 1 is used */ + if (nargs == 0 || nargs > 2 || args[0] != AD7768_TRIGGER_SOURCE_DRDY) + return false; + + return true; +} + +static int ad7768_offload_trigger_request(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + /* Should already be validated by match, but just in case */ + if (nargs == 0 || nargs > 2) + return -EINVAL; + + return 0; +} + +static const struct spi_offload_trigger_ops ad7768_offload_trigger_ops = { + .match = ad7768_offload_trigger_match, + .request = ad7768_offload_trigger_request, +}; + static const struct ad7768_chip_info ad7768_chip_info = { .name = "ad7768-1", .channel_spec = ad7768_channels, @@ -1627,10 +1761,51 @@ static const struct ad7768_chip_info adaq7769_chip_info = { .has_variable_aaf = true, }; +static const struct spi_offload_config ad7768_spi_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static int ad7768_spi_offload_probe(struct iio_dev *indio_dev, + struct ad7768_state *st) +{ + struct device *dev = &st->spi->dev; + struct spi_offload_trigger_info trigger_info = { + .fwnode = dev_fwnode(dev), + .ops = &ad7768_offload_trigger_ops, + .priv = st, + }; + struct dma_chan *rx_dma; + int ret; + + ret = devm_spi_offload_trigger_register(dev, &trigger_info); + if (ret) + return dev_err_probe(dev, ret, "failed to register offload trigger\n"); + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), "failed to get offload RX DMA\n"); + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); + if (ret) + return dev_err_probe(dev, ret, "failed to setup offload RX DMA\n"); + + indio_dev->setup_ops = &ad7768_offload_buffer_ops; + + return 0; +} + static int ad7768_probe(struct spi_device *spi) { struct ad7768_state *st; struct iio_dev *indio_dev; + struct device *dev = &spi->dev; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); @@ -1721,14 +1896,25 @@ static int ad7768_probe(struct spi_device *spi) return ret; ret = devm_request_irq(&spi->dev, spi->irq, &ad7768_interrupt, - IRQF_TRIGGER_RISING | IRQF_NO_THREAD, + IRQF_TRIGGER_RISING | IRQF_NO_THREAD | IRQF_NO_AUTOEN, indio_dev->name, indio_dev); if (ret) return ret; - ret = ad7768_triggered_buffer_alloc(indio_dev); - if (ret) - return ret; + st->offload = devm_spi_offload_get(dev, spi, &ad7768_spi_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + if (ret == -ENODEV) { + /* If not using SPI offload, fall back to low speed usage */ + ret = ad7768_triggered_buffer_alloc(indio_dev); + if (ret) + return ret; + } else if (ret) { + return dev_err_probe(dev, ret, "failed to get SPI offload\n"); + } else { + ret = ad7768_spi_offload_probe(indio_dev, st); + if (ret) + return ret; + } return devm_iio_device_register(&spi->dev, indio_dev); } @@ -1764,3 +1950,4 @@ module_spi_driver(ad7768_driver); MODULE_AUTHOR("Stefan Popa "); MODULE_DESCRIPTION("Analog Devices AD7768-1 ADC driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index ccf18ce48e34..8ff7b70d6632 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -152,7 +152,6 @@ struct ad7793_chip_info { struct ad7793_state { const struct ad7793_chip_info *chip_info; - u16 int_vref_mv; u16 mode; u16 conf; u32 scale_avail[8][2]; @@ -805,7 +804,7 @@ static int ad7793_probe(struct spi_device *spi) vref_mv = ret / 1000; } else { - vref_mv = 1170; /* Build-in ref */ + vref_mv = 1170; /* Built-in ref */ } st->chip_info = diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 87ff95643794..068171d54596 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -104,7 +104,7 @@ static int ad7887_ring_postdisable(struct iio_dev *indio_dev) { struct ad7887_state *st = iio_priv(indio_dev); - /* dummy read: restore default CH0 settin */ + /* dummy read: restore default CH0 settings */ return spi_sync(st->spi, &st->msg[AD7887_CH0]); } diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index 0369151c7db1..acc87d486aa4 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -30,7 +30,7 @@ #define AD7923_PM_MODE_AS (1) /* auto shutdown */ #define AD7923_PM_MODE_FS (2) /* full shutdown */ #define AD7923_PM_MODE_OPS (3) /* normal operation */ -#define AD7923_SEQUENCE_OFF (0) /* no sequence fonction */ +#define AD7923_SEQUENCE_OFF (0) /* no sequence function */ #define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */ #define AD7923_SEQUENCE_ON (3) /* continuous sequence */ @@ -39,7 +39,7 @@ #define AD7923_CHANNEL_WRITE(channel) ((channel) << 6) /* write channel */ #define AD7923_SEQUENCE_WRITE(sequence) ((((sequence) & 1) << 3) \ + (((sequence) & 2) << 9)) - /* write sequence fonction */ + /* write sequence function */ /* left shift for CR : bit 11 transmit in first */ #define AD7923_SHIFT_REGISTER 4 diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index 022888545580..0bf67437508f 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -176,7 +176,6 @@ struct ad9467_state { struct clk *clk; /* used for debugfs */ struct ad9467_chan_test_mode *chan_test; - unsigned int output_mode; unsigned int (*scales)[2]; /* * Times 2 because we may also invert the signal polarity and run the @@ -925,7 +924,11 @@ static int __ad9467_update_clock(struct ad9467_state *st, long r_clk) return ret; guard(mutex)(&st->lock); - return ad9467_calibrate(st); + + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) + return ad9467_calibrate(st); + + return 0; } static int ad9467_write_raw(struct iio_dev *indio_dev, @@ -1131,12 +1134,15 @@ static ssize_t ad9467_chan_test_mode_read(struct file *file, len = scnprintf(buf, sizeof(buf), "Running \"%s\" Test:\n\t", ad9467_test_modes[chan->mode]); - ret = iio_backend_debugfs_print_chan_status(st->back, chan->idx, - buf + len, - sizeof(buf) - len); - if (ret < 0) - return ret; - len += ret; + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) { + ret = iio_backend_debugfs_print_chan_status(st->back, + chan->idx, + buf + len, + sizeof(buf) - len); + if (ret < 0) + return ret; + len += ret; + } } else if (chan->mode == AN877_ADC_TESTMODE_OFF) { len = scnprintf(buf, sizeof(buf), "No test Running...\n"); } else { @@ -1175,11 +1181,13 @@ static ssize_t ad9467_chan_test_mode_write(struct file *file, if (mode == AN877_ADC_TESTMODE_OFF) { unsigned int out_mode; - if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ || - chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) { - ret = ad9467_backend_testmode_off(st, chan->idx); - if (ret) - return ret; + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) { + if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ || + chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) { + ret = ad9467_backend_testmode_off(st, chan->idx); + if (ret) + return ret; + } } ret = ad9467_testmode_set(st, chan->idx, mode); @@ -1205,16 +1213,18 @@ static ssize_t ad9467_chan_test_mode_write(struct file *file, return ret; /* some patterns have a backend matching monitoring block */ - if (mode == AN877_ADC_TESTMODE_PN9_SEQ) { - ret = ad9467_backend_testmode_on(st, chan->idx, + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) { + if (mode == AN877_ADC_TESTMODE_PN9_SEQ) { + ret = ad9467_backend_testmode_on(st, chan->idx, IIO_BACKEND_ADI_PRBS_9A); - if (ret) - return ret; - } else if (mode == AN877_ADC_TESTMODE_PN23_SEQ) { - ret = ad9467_backend_testmode_on(st, chan->idx, + if (ret) + return ret; + } else if (mode == AN877_ADC_TESTMODE_PN23_SEQ) { + ret = ad9467_backend_testmode_on(st, chan->idx, IIO_BACKEND_ADI_PRBS_23A); - if (ret) - return ret; + if (ret) + return ret; + } } } @@ -1280,8 +1290,9 @@ static void ad9467_debugfs_init(struct iio_dev *indio_dev) if (!st->chan_test) return; - debugfs_create_file("calibration_table_dump", 0400, d, st, - &ad9467_calib_table_fops); + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) + debugfs_create_file("calibration_table_dump", 0400, d, st, + &ad9467_calib_table_fops); for (chan = 0; chan < st->info->num_channels; chan++) { snprintf(attr_name, sizeof(attr_name), "in_voltage%u_test_mode", @@ -1300,12 +1311,13 @@ static void ad9467_debugfs_init(struct iio_dev *indio_dev) static int ad9467_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct iio_dev *indio_dev; struct ad9467_state *st; unsigned int id; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -1320,16 +1332,15 @@ static int ad9467_probe(struct spi_device *spi) if (AD9467_CAN_INVERT(st)) st->calib_map_size *= 2; - st->clk = devm_clk_get_enabled(&spi->dev, "adc-clk"); + st->clk = devm_clk_get_enabled(dev, "adc-clk"); if (IS_ERR(st->clk)) return PTR_ERR(st->clk); - st->pwrdown_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown", - GPIOD_OUT_LOW); + st->pwrdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW); if (IS_ERR(st->pwrdown_gpio)) return PTR_ERR(st->pwrdown_gpio); - ret = ad9467_reset(&spi->dev); + ret = ad9467_reset(dev); if (ret) return ret; @@ -1339,7 +1350,7 @@ static int ad9467_probe(struct spi_device *spi) id = ad9467_spi_read(st, AN877_ADC_REG_CHIP_ID); if (id != st->info->id) { - dev_err(&spi->dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n", + dev_err(dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n", id, st->info->id); return -ENODEV; } @@ -1356,19 +1367,25 @@ static int ad9467_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_iio_backend_request_buffer(&spi->dev, st->back, indio_dev); - if (ret) - return ret; + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_BUFFER)) { + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + } - ret = devm_iio_backend_enable(&spi->dev, st->back); - if (ret) - return ret; + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_ENABLE)) { + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + } - ret = ad9467_calibrate(st); - if (ret) - return ret; + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) { + ret = ad9467_calibrate(st); + if (ret) + return ret; + } - ret = devm_iio_device_register(&spi->dev, indio_dev); + ret = devm_iio_device_register(dev, indio_dev); if (ret) return ret; diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 7852884703b0..a955556f9ec8 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -51,8 +51,10 @@ */ void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, u8 comm) { - /* Some variants use the lower two bits of the communications register - * to select the channel */ + /* + * Some variants use the lower two bits of the communications register + * to select the channel. + */ sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK; } EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, "IIO_AD_SIGMA_DELTA"); diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index 1abbfdfcd554..c0a9babfa519 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -218,9 +218,6 @@ #define ADE9000_ST1_ERROR1_BIT BIT(29) #define ADE9000_ST1_ERROR2_BIT BIT(30) #define ADE9000_ST1_ERROR3_BIT BIT(31) -#define ADE9000_ST_ERROR \ - (ADE9000_ST1_ERROR0 | ADE9000_ST1_ERROR1 | \ - ADE9000_ST1_ERROR2 | ADE9000_ST1_ERROR3) #define ADE9000_ST1_CROSSING_FIRST 6 #define ADE9000_ST1_CROSSING_DEPTH 25 @@ -283,7 +280,6 @@ enum ade9000_wfb_cfg { #define ADE9000_PHASE_C_POS_BIT BIT(6) #define ADE9000_MAX_PHASE_NR 3 -#define AD9000_CHANNELS_PER_PHASE 10 /* * Calculate register address for multi-phase device. @@ -1549,7 +1545,7 @@ static int ade9000_buffer_postdisable(struct iio_dev *indio_dev) ret = regmap_clear_bits(st->regmap, ADE9000_REG_MASK0, interrupts); if (ret) { - dev_err(dev, "Post-disable update maks0 fail\n"); + dev_err(dev, "Post-disable update mask0 fail\n"); return ret; } @@ -1589,10 +1585,9 @@ static int ade9000_reset(struct ade9000_state *st) /* Only wait for completion if IRQ1 is available to signal reset done */ if (fwnode_irq_get_byname(dev_fwnode(dev), "irq1") >= 0) { if (!wait_for_completion_timeout(&st->reset_completion, - msecs_to_jiffies(1000))) { - dev_err(dev, "Reset timeout after 1s\n"); - return -ETIMEDOUT; - } + msecs_to_jiffies(1000))) + return dev_err_probe(dev, -ETIMEDOUT, + "Reset timeout after 1s\n"); } /* If no IRQ available, reset is already complete after the 50ms delay above */ diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 5f445e0de9ea..ced0a2321ecf 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -621,6 +621,8 @@ static const struct iio_backend_ops adi_axi_adc_ops = { static const struct iio_backend_info adi_axi_adc_generic = { .name = "axi-adc", .ops = &adi_axi_adc_ops, + .caps = IIO_BACKEND_CAP_CALIBRATION | IIO_BACKEND_CAP_BUFFER | + IIO_BACKEND_CAP_ENABLE, }; static const struct iio_backend_ops adi_ad485x_ops = { @@ -645,6 +647,8 @@ static const struct iio_backend_ops adi_ad485x_ops = { static const struct iio_backend_info axi_ad485x = { .name = "axi-ad485x", .ops = &adi_ad485x_ops, + .caps = IIO_BACKEND_CAP_CALIBRATION | IIO_BACKEND_CAP_BUFFER | + IIO_BACKEND_CAP_ENABLE, }; static const struct iio_backend_ops adi_ad408x_ops = { @@ -665,6 +669,7 @@ static const struct iio_backend_ops adi_ad408x_ops = { static const struct iio_backend_info axi_ad408x = { .name = "axi-ad408x", .ops = &adi_ad408x_ops, + .caps = IIO_BACKEND_CAP_BUFFER | IIO_BACKEND_CAP_ENABLE, }; static int adi_axi_adc_probe(struct platform_device *pdev) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 83a9885b9ae4..6169ce28261e 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -75,6 +75,8 @@ #define ASPEED_ADC_INIT_POLLING_TIME 500 #define ASPEED_ADC_INIT_TIMEOUT 500000 +/* Battery sensing is typically on the last channel */ +#define ASPEED_ADC_BATTERY_CHANNEL 7 /* * When the sampling rate is too high, the ADC may not have enough charging * time, resulting in a low voltage value. Thus, the default uses a slow @@ -121,6 +123,31 @@ struct aspeed_adc_data { struct adc_gain battery_mode_gain; }; +/* + * Enable multiple consecutive channels starting from channel 0. + * This creates a bitmask for channels 0 to (num_channels - 1). + * For example: num_channels=3 creates mask 0x0007 (channels 0,1,2) + */ +static inline u32 aspeed_adc_channels_mask(unsigned int num_channels) +{ + if (num_channels > 16) + return GENMASK(15, 0); + + return BIT(num_channels) - 1; +} + +static inline unsigned int aspeed_adc_get_active_channels(const struct aspeed_adc_data *data) +{ + /* + * For controllers with battery sensing capability, the last channel + * is reserved for battery sensing and should not be included in + * normal channel operations. + */ + if (data->model_data->bat_sense_sup) + return data->model_data->num_channels - 1; + return data->model_data->num_channels; +} + #define ASPEED_CHAN(_idx, _data_reg_addr) { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ @@ -236,10 +263,10 @@ static int aspeed_adc_compensation(struct iio_dev *indio_dev) ASPEED_ADC_CTRL_CHANNEL_ENABLE(0), data->base + ASPEED_REG_ENGINE_CONTROL); /* - * After enable compensating sensing mode need to wait some time for ADC stable - * Experiment result is 1ms. + * After enable compensating sensing mode need to wait some time for the + * ADC stablize. Experiment result is 1ms. */ - mdelay(1); + fsleep(1000); for (index = 0; index < 16; index++) { /* @@ -285,31 +312,48 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - if (data->battery_sensing && chan->channel == 7) { - adc_engine_control_reg_val = - readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * For battery sensing capable controllers, we need to enable + * the specific channel before reading. This is required because + * the battery channel may not be enabled by default. + */ + if (data->model_data->bat_sense_sup && + chan->channel == ASPEED_ADC_BATTERY_CHANNEL) { + u32 ctrl_reg = adc_engine_control_reg_val & ~ASPEED_ADC_CTRL_CHANNEL; + + ctrl_reg |= ASPEED_ADC_CTRL_CHANNEL_ENABLE(chan->channel); + writel(ctrl_reg, data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable a new channel need to wait some time for ADC stable + * Experiment result is 1ms. + */ + fsleep(1000); + } + + if (data->battery_sensing && chan->channel == ASPEED_ADC_BATTERY_CHANNEL) { writel(adc_engine_control_reg_val | FIELD_PREP(ASPEED_ADC_CH7_MODE, ASPEED_ADC_CH7_BAT) | ASPEED_ADC_BAT_SENSING_ENABLE, data->base + ASPEED_REG_ENGINE_CONTROL); /* - * After enable battery sensing mode need to wait some time for adc stable + * After enable battery sensing mode need to wait some time for ADC stable * Experiment result is 1ms. */ - mdelay(1); + fsleep(1000); *val = readw(data->base + chan->address); *val = (*val * data->battery_mode_gain.mult) / data->battery_mode_gain.div; - /* Restore control register value */ - writel(adc_engine_control_reg_val, - data->base + ASPEED_REG_ENGINE_CONTROL); } else *val = readw(data->base + chan->address); + /* Restore control register value */ + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: - if (data->battery_sensing && chan->channel == 7) + if (data->battery_sensing && chan->channel == ASPEED_ADC_BATTERY_CHANNEL) *val = (data->cv * data->battery_mode_gain.mult) / data->battery_mode_gain.div; else @@ -611,7 +655,9 @@ static int aspeed_adc_probe(struct platform_device *pdev) /* Start all channels in normal mode. */ adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); - adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL; + FIELD_MODIFY(ASPEED_ADC_CTRL_CHANNEL, &adc_engine_control_reg_val, + aspeed_adc_channels_mask(aspeed_adc_get_active_channels(data))); + writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL); diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index aa4ba3f5a506..255970b2e747 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -2259,7 +2259,7 @@ static int at91_adc_temp_sensor_init(struct at91_adc_state *st, return 0; /* Get the calibration data from NVMEM. */ - temp_calib = devm_nvmem_cell_get(dev, "temperature_calib"); + temp_calib = nvmem_cell_get(dev, "temperature_calib"); if (IS_ERR(temp_calib)) { ret = PTR_ERR(temp_calib); if (ret != -ENOENT) @@ -2268,6 +2268,7 @@ static int at91_adc_temp_sensor_init(struct at91_adc_state *st, } buf = nvmem_cell_read(temp_calib, &len); + nvmem_cell_put(temp_calib); if (IS_ERR(buf)) { dev_err(dev, "Failed to read calibration data!\n"); return PTR_ERR(buf); @@ -2507,7 +2508,7 @@ static int at91_adc_suspend(struct device *dev) at91_adc_buffer_postdisable(indio_dev); /* - * Do a sofware reset of the ADC before we go to suspend. + * Do a software reset of the ADC before we go to suspend. * this will ensure that all pins are free from being muxed by the ADC * and can be used by for other devices. * Otherwise, ADC will hog them and we can't go to suspend mode. diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 920dd9ffd27a..6e1930f7c65d 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -171,7 +171,7 @@ struct at91_adc_trigger { }; /** - * struct at91_adc_reg_desc - Various informations relative to registers + * struct at91_adc_reg_desc - Various information relative to registers * @channel_base: Base offset for the channel data registers * @drdy_mask: Mask of the DRDY field in the relevant registers * (Interruptions registers mostly) @@ -231,7 +231,7 @@ struct at91_adc_state { struct iio_trigger **trig; bool use_external; u32 vref_mv; - u32 res; /* resolution used for convertions */ + u32 res; /* resolution used for conversions */ wait_queue_head_t wq_data_avail; const struct at91_adc_caps *caps; @@ -304,7 +304,7 @@ static void handle_adc_eoc_trigger(int irq, struct iio_dev *idev) } } -static int at91_ts_sample(struct iio_dev *idev) +static void at91_ts_sample(struct iio_dev *idev) { struct at91_adc_state *st = iio_priv(idev); unsigned int xscale, yscale, reg, z1, z2; @@ -323,7 +323,7 @@ static int at91_ts_sample(struct iio_dev *idev) xscale = (reg >> 16) & xyz_mask; if (xscale == 0) { dev_err(&idev->dev, "Error: xscale == 0!\n"); - return -1; + return; } x /= xscale; @@ -334,7 +334,7 @@ static int at91_ts_sample(struct iio_dev *idev) yscale = (reg >> 16) & xyz_mask; if (yscale == 0) { dev_err(&idev->dev, "Error: yscale == 0!\n"); - return -1; + return; } y /= yscale; @@ -363,8 +363,6 @@ static int at91_ts_sample(struct iio_dev *idev) } else { dev_dbg(&idev->dev, "pressure too low: not reporting\n"); } - - return 0; } static irqreturn_t at91_adc_rl_interrupt(int irq, void *private) diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c index d9ee2ea116a7..f6f72efcc6ed 100644 --- a/drivers/iio/adc/cpcap-adc.c +++ b/drivers/iio/adc/cpcap-adc.c @@ -934,6 +934,17 @@ static const struct cpcap_adc_ato mapphone_adc = { .atox_ps_factor_out = 0, }; +static const struct cpcap_adc_ato mot_adc = { + .ato_in = 0x0300, + .atox_in = 0, + .adc_ps_factor_in = 0x0200, + .atox_ps_factor_in = 0, + .ato_out = 0x0780, + .atox_out = 0, + .adc_ps_factor_out = 0x0600, + .atox_ps_factor_out = 0, +}; + static const struct of_device_id cpcap_adc_id_table[] = { { .compatible = "motorola,cpcap-adc", @@ -942,6 +953,10 @@ static const struct of_device_id cpcap_adc_id_table[] = { .compatible = "motorola,mapphone-cpcap-adc", .data = &mapphone_adc, }, + { + .compatible = "motorola,mot-cpcap-adc", + .data = &mot_adc, + }, { } }; MODULE_DEVICE_TABLE(of, cpcap_adc_id_table); diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c index f8c220f6a7b4..e6268f7ac400 100644 --- a/drivers/iio/adc/fsl-imx25-gcq.c +++ b/drivers/iio/adc/fsl-imx25-gcq.c @@ -47,7 +47,7 @@ struct mx25_gcq_priv { * of register writes, then a wait for a completion callback, * and finally a register read, during which userspace could issue * another read request. This lock protects a read access from - * ocurring before another one has finished. + * occurring before another one has finished. */ struct mutex lock; }; diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index c6cded508738..7c04e3ec1cbc 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -119,7 +119,7 @@ static const struct regmap_config ina2xx_regmap_config = { .volatile_reg = ina2xx_is_volatile_reg, }; -enum ina2xx_ids { ina219, ina226 }; +enum ina2xx_ids { ina219, ina226, ina236 }; struct ina2xx_config { const char *name; @@ -173,6 +173,16 @@ static const struct ina2xx_config ina2xx_config[] = { .power_lsb_factor = 25, .chip_id = ina226, }, + [ina236] = { + .name = "ina236", + .config_default = INA226_CONFIG_DEFAULT, + .calibration_value = 2048, + .shunt_voltage_lsb = 2500, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1600, + .power_lsb_factor = 32, + .chip_id = ina236, + }, }; static int ina2xx_read_raw(struct iio_dev *indio_dev, @@ -497,20 +507,26 @@ static int ina2xx_write_raw(struct iio_dev *indio_dev, break; case IIO_CHAN_INFO_INT_TIME: - if (chip->config->chip_id == ina226) { + switch (chip->config->chip_id) { + case ina226: + case ina236: if (chan->address == INA2XX_SHUNT_VOLTAGE) ret = ina226_set_int_time_vshunt(chip, val2, &tmp); else ret = ina226_set_int_time_vbus(chip, val2, &tmp); - } else { + break; + case ina219: if (chan->address == INA2XX_SHUNT_VOLTAGE) ret = ina219_set_int_time_vshunt(chip, val2, &tmp); else ret = ina219_set_int_time_vbus(chip, val2, &tmp); + break; + default: + ret = -EINVAL; } break; @@ -725,19 +741,27 @@ static int ina2xx_conversion_ready(struct iio_dev *indio_dev) * For now, we do an extra read of the MASK_ENABLE register (INA226) * resp. the BUS_VOLTAGE register (INA219). */ - if (chip->config->chip_id == ina226) { + switch (chip->config->chip_id) { + case ina226: + case ina236: ret = regmap_read(chip->regmap, INA226_MASK_ENABLE, &alert); + if (ret < 0) + return ret; + alert &= INA226_CVRF; - } else { + break; + case ina219: ret = regmap_read(chip->regmap, INA2XX_BUS_VOLTAGE, &alert); + if (ret < 0) + return ret; alert &= INA219_CNVR; + break; + default: + return -EINVAL; } - if (ret < 0) - return ret; - return !!alert; } @@ -988,16 +1012,22 @@ static int ina2xx_probe(struct i2c_client *client) /* Patch the current config register with default. */ val = chip->config->config_default; - if (type == ina226) { + switch (type) { + case ina226: + case ina236: ina226_set_average(chip, INA226_DEFAULT_AVG, &val); ina226_set_int_time_vbus(chip, INA226_DEFAULT_IT, &val); ina226_set_int_time_vshunt(chip, INA226_DEFAULT_IT, &val); - } else { + break; + case ina219: chip->avg = 1; ina219_set_int_time_vbus(chip, INA219_DEFAULT_IT, &val); ina219_set_int_time_vshunt(chip, INA219_DEFAULT_IT, &val); ina219_set_vbus_range_denom(chip, INA219_DEFAULT_BRNG, &val); ina219_set_vshunt_pga_gain(chip, INA219_DEFAULT_PGA, &val); + break; + default: + return -EINVAL; } ret = ina2xx_init(chip, val); @@ -1007,14 +1037,20 @@ static int ina2xx_probe(struct i2c_client *client) } indio_dev->modes = INDIO_DIRECT_MODE; - if (type == ina226) { + switch (type) { + case ina226: + case ina236: indio_dev->channels = ina226_channels; indio_dev->num_channels = ARRAY_SIZE(ina226_channels); indio_dev->info = &ina226_info; - } else { + break; + case ina219: indio_dev->channels = ina219_channels; indio_dev->num_channels = ARRAY_SIZE(ina219_channels); indio_dev->info = &ina219_info; + break; + default: + return -EINVAL; } indio_dev->name = id ? id->name : chip->config->name; @@ -1047,6 +1083,7 @@ static const struct i2c_device_id ina2xx_id[] = { { "ina226", ina226 }, { "ina230", ina226 }, { "ina231", ina226 }, + { "ina236", ina236 }, { } }; MODULE_DEVICE_TABLE(i2c, ina2xx_id); @@ -1072,6 +1109,10 @@ static const struct of_device_id ina2xx_of_match[] = { .compatible = "ti,ina231", .data = (void *)ina226 }, + { + .compatible = "ti,ina236", + .data = (void *)ina236 + }, { } }; MODULE_DEVICE_TABLE(of, ina2xx_of_match); diff --git a/drivers/iio/adc/ltc2309.c b/drivers/iio/adc/ltc2309.c index 5f0d947d0615..316256edf150 100644 --- a/drivers/iio/adc/ltc2309.c +++ b/drivers/iio/adc/ltc2309.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* + * The LTC2305 is a 2-Channel, 12-Bit SAR ADC with an I2C Interface. * The LTC2309 is an 8-Channel, 12-Bit SAR ADC with an I2C Interface. * * Datasheet: + * https://www.analog.com/media/en/technical-documentation/data-sheets/23015fb.pdf * https://www.analog.com/media/en/technical-documentation/data-sheets/2309fd.pdf * * Copyright (c) 2023, Liam Beguin @@ -41,23 +43,30 @@ struct ltc2309 { }; /* Order matches expected channel address, See datasheet Table 1. */ +enum ltc2305_channels { + LTC2305_CH0_CH1 = 0x0, + LTC2305_CH1_CH0 = 0x4, + LTC2305_CH0 = 0x8, + LTC2305_CH1 = 0xc, +}; + enum ltc2309_channels { - LTC2309_CH0_CH1 = 0, - LTC2309_CH2_CH3, - LTC2309_CH4_CH5, - LTC2309_CH6_CH7, - LTC2309_CH1_CH0, - LTC2309_CH3_CH2, - LTC2309_CH5_CH4, - LTC2309_CH7_CH6, - LTC2309_CH0, - LTC2309_CH2, - LTC2309_CH4, - LTC2309_CH6, - LTC2309_CH1, - LTC2309_CH3, - LTC2309_CH5, - LTC2309_CH7, + LTC2309_CH0_CH1 = 0x0, + LTC2309_CH2_CH3 = 0x1, + LTC2309_CH4_CH5 = 0x2, + LTC2309_CH6_CH7 = 0x3, + LTC2309_CH1_CH0 = 0x4, + LTC2309_CH3_CH2 = 0x5, + LTC2309_CH5_CH4 = 0x6, + LTC2309_CH7_CH6 = 0x7, + LTC2309_CH0 = 0x8, + LTC2309_CH2 = 0x9, + LTC2309_CH4 = 0xa, + LTC2309_CH6 = 0xb, + LTC2309_CH1 = 0xc, + LTC2309_CH3 = 0xd, + LTC2309_CH5 = 0xe, + LTC2309_CH7 = 0xf, }; #define LTC2309_CHAN(_chan, _addr) { \ @@ -80,6 +89,13 @@ enum ltc2309_channels { .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ } +static const struct iio_chan_spec ltc2305_channels[] = { + LTC2309_CHAN(0, LTC2305_CH0), + LTC2309_CHAN(1, LTC2305_CH1), + LTC2309_DIFF_CHAN(0, 1, LTC2305_CH0_CH1), + LTC2309_DIFF_CHAN(1, 0, LTC2305_CH1_CH0), +}; + static const struct iio_chan_spec ltc2309_channels[] = { LTC2309_CHAN(0, LTC2309_CH0), LTC2309_CHAN(1, LTC2309_CH1), @@ -99,6 +115,24 @@ static const struct iio_chan_spec ltc2309_channels[] = { LTC2309_DIFF_CHAN(7, 6, LTC2309_CH7_CH6), }; +struct ltc2309_chip_info { + const char *name; + const struct iio_chan_spec *channels; + int num_channels; +}; + +static const struct ltc2309_chip_info ltc2305_chip_info = { + .name = "ltc2305", + .channels = ltc2305_channels, + .num_channels = ARRAY_SIZE(ltc2305_channels), +}; + +static const struct ltc2309_chip_info ltc2309_chip_info = { + .name = "ltc2309", + .channels = ltc2309_channels, + .num_channels = ARRAY_SIZE(ltc2309_channels), +}; + static int ltc2309_read_raw_channel(struct ltc2309 *ltc2309, unsigned long address, int *val) { @@ -158,6 +192,7 @@ static const struct iio_info ltc2309_info = { static int ltc2309_probe(struct i2c_client *client) { + const struct ltc2309_chip_info *chip_info; struct iio_dev *indio_dev; struct ltc2309 *ltc2309; int ret; @@ -167,13 +202,15 @@ static int ltc2309_probe(struct i2c_client *client) return -ENOMEM; ltc2309 = iio_priv(indio_dev); + chip_info = i2c_get_match_data(client); + ltc2309->dev = &indio_dev->dev; ltc2309->client = client; - indio_dev->name = "ltc2309"; + indio_dev->name = chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = ltc2309_channels; - indio_dev->num_channels = ARRAY_SIZE(ltc2309_channels); + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; indio_dev->info = <c2309_info; ret = devm_regulator_get_enable_read_voltage(&client->dev, "vref"); @@ -189,13 +226,15 @@ static int ltc2309_probe(struct i2c_client *client) } static const struct of_device_id ltc2309_of_match[] = { - { .compatible = "lltc,ltc2309" }, + { .compatible = "lltc,ltc2305", .data = <c2305_chip_info }, + { .compatible = "lltc,ltc2309", .data = <c2309_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ltc2309_of_match); static const struct i2c_device_id ltc2309_id[] = { - { "ltc2309" }, + { "ltc2305", (kernel_ulong_t)<c2305_chip_info }, + { "ltc2309", (kernel_ulong_t)<c2309_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, ltc2309_id); diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index 511b2f14dfaf..69351f4f10bb 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -912,8 +912,8 @@ static int max11410_self_calibrate(struct max11410_state *st) static int max11410_probe(struct spi_device *spi) { - const char *vrefp_regs[] = { "vref0p", "vref1p", "vref2p" }; - const char *vrefn_regs[] = { "vref0n", "vref1n", "vref2n" }; + static const char * const vrefp_regs[] = { "vref0p", "vref1p", "vref2p" }; + static const char * const vrefn_regs[] = { "vref0n", "vref1n", "vref2n" }; struct device *dev = &spi->dev; struct max11410_state *st; struct iio_dev *indio_dev; diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 9dd547e62b6c..4d0b79cfeb27 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -121,7 +121,7 @@ enum max1363_modes { }; /** - * struct max1363_chip_info - chip specifc information + * struct max1363_chip_info - chip specific information * @info: iio core function callbacks structure * @channels: channel specification * @num_channels: number of channels @@ -149,7 +149,6 @@ struct max1363_chip_info { * @configbyte: cache of current device config byte * @chip_info: chip model specific constants, available modes, etc. * @current_mode: the scan mode of this chip - * @requestedmask: a valid requested set of channels * @lock: lock to ensure state is consistent * @monitor_on: whether monitor mode is enabled * @monitor_speed: parameter corresponding to device monitor speed setting @@ -169,7 +168,6 @@ struct max1363_state { u8 configbyte; const struct max1363_chip_info *chip_info; const struct max1363_mode *current_mode; - u32 requestedmask; struct mutex lock; /* Using monitor modes and buffer at the same time is @@ -637,48 +635,51 @@ static const enum max1363_modes max11644_mode_list[] = { static const struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10); static const struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12); -enum { max1361, - max1362, - max1363, - max1364, - max1036, - max1037, - max1038, - max1039, - max1136, - max1137, - max1138, - max1139, - max1236, - max1237, - max1238, - max1239, - max11600, - max11601, - max11602, - max11603, - max11604, - max11605, - max11606, - max11607, - max11608, - max11609, - max11610, - max11611, - max11612, - max11613, - max11614, - max11615, - max11616, - max11617, - max11644, - max11645, - max11646, - max11647 +enum { + max1361, + max1362, + max1363, + max1364, + max1036, + max1037, + max1038, + max1039, + max1136, + max1137, + max1138, + max1139, + max1236, + max1237, + max1238, + max1239, + max11600, + max11601, + max11602, + max11603, + max11604, + max11605, + max11606, + max11607, + max11608, + max11609, + max11610, + max11611, + max11612, + max11613, + max11614, + max11615, + max11616, + max11617, + max11644, + max11645, + max11646, + max11647, }; -static const int max1363_monitor_speeds[] = { 133000, 665000, 33300, 16600, - 8300, 4200, 2000, 1000 }; +static const int max1363_monitor_speeds[] = { + 133000, 665000, 33300, 16600, + 8300, 4200, 2000, 1000, +}; static ssize_t max1363_monitor_show_freq(struct device *dev, struct device_attribute *attr, diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c index fcdf13f49c48..36675563829e 100644 --- a/drivers/iio/adc/mcp3564.c +++ b/drivers/iio/adc/mcp3564.c @@ -349,7 +349,7 @@ struct mcp3564_chip_info { * struct mcp3564_state - working data for a ADC device * @chip_info: chip specific data * @spi: SPI device structure - * @vref_mv: voltage reference value in miliVolts + * @vref_mv: voltage reference value in millivolts * @lock: synchronize access to driver's state members * @dev_addr: hardware device address * @oversampling: the index inside oversampling list of the ADC diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c index 90919d282e7b..5bd334ec5655 100644 --- a/drivers/iio/adc/men_z188_adc.c +++ b/drivers/iio/adc/men_z188_adc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * MEN 16z188 Analog to Digial Converter + * MEN 16z188 Analog to Digital Converter * * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) * Author: Johannes Thumshirn diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 47cd350498a0..23991a3612bd 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -792,7 +792,7 @@ static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) size_t read_len; int ret; - temperature_calib = devm_nvmem_cell_get(dev, "temperature_calib"); + temperature_calib = nvmem_cell_get(dev, "temperature_calib"); if (IS_ERR(temperature_calib)) { ret = PTR_ERR(temperature_calib); @@ -806,13 +806,9 @@ static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) return dev_err_probe(dev, ret, "failed to get temperature_calib cell\n"); } - priv->tsc_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "amlogic,hhi-sysctrl"); - if (IS_ERR(priv->tsc_regmap)) - return dev_err_probe(dev, PTR_ERR(priv->tsc_regmap), - "failed to get amlogic,hhi-sysctrl regmap\n"); - read_len = MESON_SAR_ADC_EFUSE_BYTES; buf = nvmem_cell_read(temperature_calib, &read_len); + nvmem_cell_put(temperature_calib); if (IS_ERR(buf)) return dev_err_probe(dev, PTR_ERR(buf), "failed to read temperature_calib cell\n"); if (read_len != MESON_SAR_ADC_EFUSE_BYTES) { @@ -820,6 +816,11 @@ static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) return dev_err_probe(dev, -EINVAL, "invalid read size of temperature_calib cell\n"); } + priv->tsc_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "amlogic,hhi-sysctrl"); + if (IS_ERR(priv->tsc_regmap)) + return dev_err_probe(dev, PTR_ERR(priv->tsc_regmap), + "failed to get amlogic,hhi-sysctrl regmap\n"); + trimming_bits = priv->param->temperature_trimming_bits; trimming_mask = BIT(trimming_bits) - 1; @@ -1313,6 +1314,11 @@ static const struct meson_sar_adc_data meson_sar_adc_g12a_data = { .name = "meson-g12a-saradc", }; +static const struct meson_sar_adc_data meson_sar_adc_s4_data = { + .param = &meson_sar_adc_g12a_param, + .name = "meson-s4-saradc", +}; + static const struct of_device_id meson_sar_adc_of_match[] = { { .compatible = "amlogic,meson8-saradc", @@ -1341,6 +1347,9 @@ static const struct of_device_id meson_sar_adc_of_match[] = { }, { .compatible = "amlogic,meson-g12a-saradc", .data = &meson_sar_adc_g12a_data, + }, { + .compatible = "amlogic,meson-s4-saradc", + .data = &meson_sar_adc_s4_data, }, { } }; diff --git a/drivers/iio/adc/mt6359-auxadc.c b/drivers/iio/adc/mt6359-auxadc.c index f426a289e867..6b9ed9b1fde2 100644 --- a/drivers/iio/adc/mt6359-auxadc.c +++ b/drivers/iio/adc/mt6359-auxadc.c @@ -322,7 +322,7 @@ static const struct mtk_pmic_auxadc_chan mt6359_auxadc_ch_desc[] = { MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP1, 15, 128, 7, 2), MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_IMP1, 15, 8, 5, 2), MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_IMP1, 15, 8, 1, 1), - MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP1, 15 ,8, 1, 1), + MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP1, 15, 8, 1, 1), MTK_PMIC_ADC_CHAN(VDCXO, PMIC_AUXADC_RQST0, 6, PMIC_AUXADC_IMP1, 15, 8, 3, 2), MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, PMIC_AUXADC_IMP1, 15, 128, 1, 1), MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, PMIC_AUXADC_IMP1, 15, 256, 1, 1), diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index 458544cb8ee4..970afb27b839 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -55,7 +55,6 @@ struct nau7802_state { struct mutex data_lock; u32 vref_mv; u32 conversion_count; - u32 min_conversions; u8 sample_rate; u32 scale_avail[8]; struct completion value_ok; @@ -257,7 +256,7 @@ static int nau7802_read_poll(struct iio_dev *indio_dev, /* * Because there is actually only one ADC for both channels, we have to * wait for enough conversions to happen before getting a significant - * value when changing channels and the values are far appart. + * value when changing channels and the values are far apart. */ do { ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index c8283873cdee..ddabb9600d46 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -38,7 +38,7 @@ struct npcm_adc { * read access from userspace. Reading a raw value requires a sequence * of register writes, then a wait for a event and finally a register * read, during which userspace could issue another read request. - * This lock protects a read access from ocurring before another one + * This lock protects a read access from occurring before another one * has finished. */ struct mutex lock; diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 58103bf16aff..9d9f2c76bed4 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -247,7 +247,8 @@ static inline void nxp_sar_adc_calibration_start(void __iomem *base) static inline int nxp_sar_adc_calibration_wait(void __iomem *base) { - u32 msr, ret; + u32 msr; + int ret; ret = readl_poll_timeout(NXP_SAR_ADC_MSR(base), msr, !FIELD_GET(NXP_SAR_ADC_MSR_CALBUSY, msr), diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c index a0227b57f238..bce7185953ec 100644 --- a/drivers/iio/adc/pac1921.c +++ b/drivers/iio/adc/pac1921.c @@ -856,7 +856,7 @@ static ssize_t pac1921_format_scale_avail(const int (*const scales_tbl)[2], /* * Read available scales for a specific channel * - * NOTE: using extended info insted of iio.read_avail() because access to + * NOTE: using extended info instead of iio.read_avail() because access to * current scales must be locked as they depend on shunt resistor which may * change runtime. Caller of iio.read_avail() would access the table unlocked * instead. diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c index 712b5e9caba6..23055405a6e0 100644 --- a/drivers/iio/adc/pac1934.c +++ b/drivers/iio/adc/pac1934.c @@ -1351,7 +1351,7 @@ static int pac1934_prep_iio_channels(struct pac1934_chip_info *info, struct iio_ dyn_ch_struct = devm_kzalloc(dev, channel_size, GFP_KERNEL); if (!dyn_ch_struct) - return -EINVAL; + return -ENOMEM; tmp_data = dyn_ch_struct; diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 3f433064618e..f777986a6aba 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -105,7 +105,7 @@ struct palmas_gpadc_thresholds { * of register writes, then a wait for a completion callback, * and finally a register read, during which userspace could issue * another read request. This lock protects a read access from - * ocurring before another one has finished. + * occurring before another one has finished. * * This is the palmas_gpadc structure to store run-time information * and pointers for this driver instance. @@ -521,16 +521,16 @@ static int palmas_gpadc_get_low_threshold_raw(struct palmas_gpadc *adc, val = (val * 1000) / adc->adc_info[adc_chan].gain; - if (adc->adc_info[adc_chan].is_uncalibrated) { + if (adc->adc_info[adc_chan].is_uncalibrated) { /* 2% worse */ min_gain_error -= 20; min_offset_error = -36; - } else { + } else { val = (val * adc->adc_info[adc_chan].gain_error - adc->adc_info[adc_chan].offset) / 1000; min_offset_error = -2; - } + } return palmas_gpadc_threshold_with_tolerance(val, min_INL, diff --git a/drivers/iio/adc/qcom-spmi-adc5-gen3.c b/drivers/iio/adc/qcom-spmi-adc5-gen3.c new file mode 100644 index 000000000000..f8168a14b907 --- /dev/null +++ b/drivers/iio/adc/qcom-spmi-adc5-gen3.c @@ -0,0 +1,860 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADC5_GEN3_VADC_SDAM 0x0 + +struct adc5_chip; + +/** + * struct adc5_channel_prop - ADC channel structure + * @common_props: structure with ADC channel properties (common to TM usage). + * @adc_tm: indicates TM type if the channel is used for TM measurements. + * @chip: pointer to top-level ADC device structure. + */ +struct adc5_channel_prop { + struct adc5_channel_common_prop common_props; + int adc_tm; + struct adc5_chip *chip; +}; + +/** + * struct adc5_chip - ADC private structure. + * @dev: SPMI ADC5 Gen3 device. + * @dev_data: Top-level ADC device data. + * @nchannels: number of ADC channels. + * @chan_props: array of ADC channel properties. + * @iio_chans: array of IIO channels specification. + * @complete: ADC result notification after interrupt is received. + * @lock: ADC lock for access to the peripheral, to prevent concurrent + * requests from multiple clients. + * @data: software configuration data. + * @n_tm_channels: number of ADC channels used for TM measurements. + * @handler: TM callback to be called for threshold violation interrupt + * on first SDAM. + * @tm_aux: pointer to auxiliary TM device. + */ +struct adc5_chip { + struct device *dev; + struct adc5_device_data dev_data; + unsigned int nchannels; + struct adc5_channel_prop *chan_props; + struct iio_chan_spec *iio_chans; + struct completion complete; + struct mutex lock; + const struct adc5_data *data; + unsigned int n_tm_channels; + void (*handler)(struct auxiliary_device *tm_aux); + struct auxiliary_device *tm_aux; +}; + +int adc5_gen3_read(struct adc5_device_data *adc, unsigned int sdam_index, + u16 offset, u8 *data, int len) +{ + return regmap_bulk_read(adc->regmap, + adc->base[sdam_index].base_addr + offset, + data, len); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_read, "QCOM_SPMI_ADC5_GEN3"); + +int adc5_gen3_write(struct adc5_device_data *adc, unsigned int sdam_index, + u16 offset, u8 *data, int len) +{ + return regmap_bulk_write(adc->regmap, + adc->base[sdam_index].base_addr + offset, + data, len); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_write, "QCOM_SPMI_ADC5_GEN3"); + +static int adc5_gen3_read_voltage_data(struct adc5_chip *adc, u16 *data) +{ + u8 rslt[2]; + int ret; + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_CH_DATA0(0), rslt, sizeof(rslt)); + if (ret) + return ret; + + *data = get_unaligned_le16(rslt); + + if (*data == ADC5_USR_DATA_CHECK) { + dev_err(adc->dev, "Invalid data:%#x\n", *data); + return -EINVAL; + } + + dev_dbg(adc->dev, "voltage raw code:%#x\n", *data); + + return 0; +} + +void adc5_gen3_update_dig_param(struct adc5_channel_common_prop *prop, u8 *data) +{ + /* Update calibration select and decimation ratio select */ + *data &= ~(ADC5_GEN3_DIG_PARAM_CAL_SEL_MASK | ADC5_GEN3_DIG_PARAM_DEC_RATIO_SEL_MASK); + *data |= FIELD_PREP(ADC5_GEN3_DIG_PARAM_CAL_SEL_MASK, prop->cal_method); + *data |= FIELD_PREP(ADC5_GEN3_DIG_PARAM_DEC_RATIO_SEL_MASK, prop->decimation); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_update_dig_param, "QCOM_SPMI_ADC5_GEN3"); + +#define ADC5_GEN3_READ_CONFIG_REGS 7 + +static int adc5_gen3_configure(struct adc5_chip *adc, + struct adc5_channel_common_prop *prop) +{ + u8 buf[ADC5_GEN3_READ_CONFIG_REGS]; + u8 conv_req = 0; + int ret; + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, ADC5_GEN3_SID, + buf, sizeof(buf)); + if (ret) + return ret; + + /* Write SID */ + buf[0] = FIELD_PREP(ADC5_GEN3_SID_MASK, prop->sid); + + /* + * Use channel 0 by default for immediate conversion and to indicate + * there is an actual conversion request + */ + buf[1] = ADC5_GEN3_CHAN_CONV_REQ | 0; + + buf[2] = ADC5_GEN3_TIME_IMMEDIATE; + + /* Digital param selection */ + adc5_gen3_update_dig_param(prop, &buf[3]); + + /* Update fast average sample value */ + buf[4] = FIELD_PREP(ADC5_GEN3_FAST_AVG_CTL_SAMPLES_MASK, + prop->avg_samples) | ADC5_GEN3_FAST_AVG_CTL_EN; + + /* Select ADC channel */ + buf[5] = prop->channel; + + /* Select HW settle delay for channel */ + buf[6] = FIELD_PREP(ADC5_GEN3_HW_SETTLE_DELAY_MASK, + prop->hw_settle_time_us); + + reinit_completion(&adc->complete); + + ret = adc5_gen3_write(&adc->dev_data, ADC5_GEN3_VADC_SDAM, ADC5_GEN3_SID, + buf, sizeof(buf)); + if (ret) + return ret; + + conv_req = ADC5_GEN3_CONV_REQ_REQ; + return adc5_gen3_write(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_CONV_REQ, &conv_req, sizeof(conv_req)); +} + +/* + * Worst case delay from PBS in readying handshake bit can be up to 15ms, when + * PBS is busy running other simultaneous transactions, while in the best case, + * it is already ready at this point. Assigning polling delay and retry count + * accordingly. + */ + +#define ADC5_GEN3_HS_DELAY_US 100 +#define ADC5_GEN3_HS_RETRY_COUNT 150 + +int adc5_gen3_poll_wait_hs(struct adc5_device_data *adc, + unsigned int sdam_index) +{ + u8 conv_req = ADC5_GEN3_CONV_REQ_REQ; + int ret, count; + u8 status = 0; + + for (count = 0; count < ADC5_GEN3_HS_RETRY_COUNT; count++) { + ret = adc5_gen3_read(adc, sdam_index, ADC5_GEN3_HS, &status, sizeof(status)); + if (ret) + return ret; + + if (status == ADC5_GEN3_HS_READY) { + ret = adc5_gen3_read(adc, sdam_index, ADC5_GEN3_CONV_REQ, + &conv_req, sizeof(conv_req)); + if (ret) + return ret; + + if (!conv_req) + return 0; + } + + fsleep(ADC5_GEN3_HS_DELAY_US); + } + + pr_err("Setting HS ready bit timed out, sdam_index:%d, status:%#x\n", + sdam_index, status); + return -ETIMEDOUT; +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_poll_wait_hs, "QCOM_SPMI_ADC5_GEN3"); + +int adc5_gen3_status_clear(struct adc5_device_data *adc, + int sdam_index, u16 offset, u8 *val, int len) +{ + u8 value; + int ret; + + ret = adc5_gen3_write(adc, sdam_index, offset, val, len); + if (ret) + return ret; + + /* To indicate conversion request is only to clear a status */ + value = 0; + ret = adc5_gen3_write(adc, sdam_index, ADC5_GEN3_PERPH_CH, &value, + sizeof(value)); + if (ret) + return ret; + + value = ADC5_GEN3_CONV_REQ_REQ; + return adc5_gen3_write(adc, sdam_index, ADC5_GEN3_CONV_REQ, &value, + sizeof(value)); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_status_clear, "QCOM_SPMI_ADC5_GEN3"); + +/* + * Worst case delay from PBS for conversion time can be up to 500ms, when PBS + * has timed out twice, once for the initial attempt and once for a retry of + * the same transaction. + */ + +#define ADC5_GEN3_CONV_TIMEOUT_MS 501 + +static int adc5_gen3_do_conversion(struct adc5_chip *adc, + struct adc5_channel_common_prop *prop, + u16 *data_volt) +{ + unsigned long rc; + int ret; + u8 val; + + guard(mutex)(&adc->lock); + ret = adc5_gen3_poll_wait_hs(&adc->dev_data, ADC5_GEN3_VADC_SDAM); + if (ret) + return ret; + + ret = adc5_gen3_configure(adc, prop); + if (ret) { + dev_err(adc->dev, "ADC configure failed with %d\n", ret); + return ret; + } + + /* No support for polling mode at present */ + rc = wait_for_completion_timeout(&adc->complete, + msecs_to_jiffies(ADC5_GEN3_CONV_TIMEOUT_MS)); + if (!rc) { + dev_err(adc->dev, "Reading ADC channel %s timed out\n", + prop->label); + return -ETIMEDOUT; + } + + ret = adc5_gen3_read_voltage_data(adc, data_volt); + if (ret) + return ret; + + val = BIT(0); + return adc5_gen3_status_clear(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_EOC_CLR, &val, 1); +} + +static irqreturn_t adc5_gen3_isr(int irq, void *dev_id) +{ + struct adc5_chip *adc = dev_id; + struct device *dev = adc->dev; + struct auxiliary_device *adev; + u8 status, eoc_status, val; + u8 tm_status[2]; + int ret; + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_STATUS1, &status, sizeof(status)); + if (ret) { + dev_err(dev, "adc read status1 failed with %d\n", ret); + return IRQ_HANDLED; + } + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_EOC_STS, &eoc_status, sizeof(eoc_status)); + if (ret) { + dev_err(dev, "adc read eoc status failed with %d\n", ret); + return IRQ_HANDLED; + } + + if (status & ADC5_GEN3_STATUS1_CONV_FAULT) { + dev_err_ratelimited(dev, + "Unexpected conversion fault, status:%#x, eoc_status:%#x\n", + status, eoc_status); + val = ADC5_GEN3_CONV_ERR_CLR_REQ; + adc5_gen3_status_clear(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_CONV_ERR_CLR, &val, 1); + return IRQ_HANDLED; + } + + /* CHAN0 is the preconfigured channel for immediate conversion */ + if (eoc_status & ADC5_GEN3_EOC_CHAN_0) + complete(&adc->complete); + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_TM_HIGH_STS, tm_status, sizeof(tm_status)); + if (ret) { + dev_err(dev, "adc read TM status failed with %d\n", ret); + return IRQ_HANDLED; + } + + dev_dbg(dev, "Interrupt status:%#x, EOC status:%#x, high:%#x, low:%#x\n", + status, eoc_status, tm_status[0], tm_status[1]); + + if (tm_status[0] || tm_status[1]) { + adev = adc->tm_aux; + if (!adev || !adev->dev.driver) { + dev_err(dev, "adc_tm auxiliary device not initialized\n"); + return IRQ_HANDLED; + } + + adc->handler(adev); + } + + return IRQ_HANDLED; +} + +static int adc5_gen3_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + int i, v_channel; + + for (i = 0; i < adc->nchannels; i++) { + v_channel = ADC5_GEN3_V_CHAN(adc->chan_props[i].common_props); + if (v_channel == iiospec->args[0]) + return i; + } + + return -ENOENT; +} + +static int adc5_gen3_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + struct adc5_channel_common_prop *prop; + u16 adc_code_volt; + int ret; + + prop = &adc->chan_props[chan->address].common_props; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ret = adc5_gen3_do_conversion(adc, prop, &adc_code_volt); + if (ret) + return ret; + + ret = qcom_adc5_hw_scale(prop->scale_fn_type, prop->prescale, + adc->data, adc_code_volt, val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int adc5_gen3_read_label(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, char *label) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + struct adc5_channel_prop *prop; + + prop = &adc->chan_props[chan->address]; + return sprintf(label, "%s\n", prop->common_props.label); +} + +static const struct iio_info adc5_gen3_info = { + .read_raw = adc5_gen3_read_raw, + .read_label = adc5_gen3_read_label, + .fwnode_xlate = adc5_gen3_fwnode_xlate, +}; + +struct adc5_channels { + unsigned int prescale_index; + enum iio_chan_type type; + long info_mask; + enum vadc_scale_fn_type scale_fn_type; +}; + +/* In these definitions, _pre refers to an index into adc5_prescale_ratios. */ +#define ADC5_CHAN(_type, _mask, _pre, _scale) \ + { \ + .prescale_index = _pre, \ + .type = _type, \ + .info_mask = _mask, \ + .scale_fn_type = _scale, \ + }, \ + +#define ADC5_CHAN_TEMP(_pre, _scale) \ + ADC5_CHAN(IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre, _scale) \ + +#define ADC5_CHAN_VOLT(_pre, _scale) \ + ADC5_CHAN(IIO_VOLTAGE, BIT(IIO_CHAN_INFO_PROCESSED), _pre, _scale) \ + +#define ADC5_CHAN_CUR(_pre, _scale) \ + ADC5_CHAN(IIO_CURRENT, BIT(IIO_CHAN_INFO_PROCESSED), _pre, _scale) \ + +static const struct adc5_channels adc5_gen3_chans_pmic[ADC5_MAX_CHANNEL] = { + [ADC5_GEN3_REF_GND] = ADC5_CHAN_VOLT(0, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_1P25VREF] = ADC5_CHAN_VOLT(0, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_VPH_PWR] = ADC5_CHAN_VOLT(1, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_VBAT_SNS_QBG] = ADC5_CHAN_VOLT(1, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_USB_SNS_V_16] = ADC5_CHAN_TEMP(8, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_VIN_DIV16_MUX] = ADC5_CHAN_TEMP(8, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_DIE_TEMP] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_PMIC_THERM_PM7) + [ADC5_GEN3_AMUX1_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX2_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX3_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX4_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX5_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX6_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX1_GPIO_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX2_GPIO_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX3_GPIO_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX4_GPIO_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) +}; + +static int adc5_gen3_get_fw_channel_data(struct adc5_chip *adc, + struct adc5_channel_prop *prop, + struct fwnode_handle *fwnode) +{ + const char *name = fwnode_get_name(fwnode); + const struct adc5_data *data = adc->data; + struct device *dev = adc->dev; + const char *channel_name; + u32 chan, value, sid; + u32 varr[2]; + int ret; + + ret = fwnode_property_read_u32(fwnode, "reg", &chan); + if (ret < 0) + return dev_err_probe(dev, ret, "invalid channel number %s\n", + name); + + /* + * Value read from "reg" is virtual channel number + * virtual channel number = sid << 8 | channel number + */ + sid = FIELD_GET(ADC5_GEN3_VIRTUAL_SID_MASK, chan); + chan = FIELD_GET(ADC5_GEN3_CHANNEL_MASK, chan); + + if (chan > ADC5_MAX_CHANNEL) + return dev_err_probe(dev, -EINVAL, + "%s invalid channel number %d\n", + name, chan); + + prop->common_props.channel = chan; + prop->common_props.sid = sid; + + if (!adc->data->adc_chans[chan].info_mask) + return dev_err_probe(dev, -EINVAL, "Channel %#x not supported\n", chan); + + channel_name = name; + fwnode_property_read_string(fwnode, "label", &channel_name); + prop->common_props.label = channel_name; + + value = data->decimation[ADC5_DECIMATION_DEFAULT]; + fwnode_property_read_u32(fwnode, "qcom,decimation", &value); + ret = qcom_adc5_decimation_from_dt(value, data->decimation); + if (ret < 0) + return dev_err_probe(dev, ret, "%#x invalid decimation %d\n", + chan, value); + prop->common_props.decimation = ret; + + prop->common_props.prescale = adc->data->adc_chans[chan].prescale_index; + ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2); + if (!ret) { + ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]); + if (ret < 0) + return dev_err_probe(dev, ret, + "%#x invalid pre-scaling <%d %d>\n", + chan, varr[0], varr[1]); + prop->common_props.prescale = ret; + } + + value = data->hw_settle_1[VADC_DEF_HW_SETTLE_TIME]; + fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value); + ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_1); + if (ret < 0) + return dev_err_probe(dev, ret, + "%#x invalid hw-settle-time %d us\n", + chan, value); + prop->common_props.hw_settle_time_us = ret; + + value = BIT(VADC_DEF_AVG_SAMPLES); + fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value); + ret = qcom_adc5_avg_samples_from_dt(value); + if (ret < 0) + return dev_err_probe(dev, ret, "%#x invalid avg-samples %d\n", + chan, value); + prop->common_props.avg_samples = ret; + + if (fwnode_property_read_bool(fwnode, "qcom,ratiometric")) + prop->common_props.cal_method = ADC5_RATIOMETRIC_CAL; + else + prop->common_props.cal_method = ADC5_ABSOLUTE_CAL; + + prop->adc_tm = fwnode_property_read_bool(fwnode, "qcom,adc-tm"); + if (prop->adc_tm) { + adc->n_tm_channels++; + if (adc->n_tm_channels > (adc->dev_data.num_sdams * 8 - 1)) + return dev_err_probe(dev, -EINVAL, + "Number of TM nodes %u greater than channels supported:%u\n", + adc->n_tm_channels, + adc->dev_data.num_sdams * 8 - 1); + } + + return 0; +} + +static const struct adc5_data adc5_gen3_data_pmic = { + .full_scale_code_volt = 0x70e4, + .adc_chans = adc5_gen3_chans_pmic, + .info = &adc5_gen3_info, + .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX]) + { 85, 340, 1360 }, + .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + { 15, 100, 200, 300, + 400, 500, 600, 700, + 1000, 2000, 4000, 8000, + 16000, 32000, 64000, 128000 }, +}; + +static const struct of_device_id adc5_match_table[] = { + { + .compatible = "qcom,spmi-adc5-gen3", + .data = &adc5_gen3_data_pmic, + }, + { } +}; +MODULE_DEVICE_TABLE(of, adc5_match_table); + +static int adc5_get_fw_data(struct adc5_chip *adc) +{ + const struct adc5_channels *adc_chan; + struct adc5_channel_prop *chan_props; + struct iio_chan_spec *iio_chan; + struct device *dev = adc->dev; + unsigned int index = 0; + int ret; + + adc->nchannels = device_get_child_node_count(dev); + if (!adc->nchannels) + return dev_err_probe(dev, -EINVAL, "No ADC channels found\n"); + + adc->iio_chans = devm_kcalloc(dev, adc->nchannels, + sizeof(*adc->iio_chans), GFP_KERNEL); + if (!adc->iio_chans) + return -ENOMEM; + + adc->chan_props = devm_kcalloc(dev, adc->nchannels, + sizeof(*adc->chan_props), GFP_KERNEL); + if (!adc->chan_props) + return -ENOMEM; + + chan_props = adc->chan_props; + adc->n_tm_channels = 0; + iio_chan = adc->iio_chans; + adc->data = device_get_match_data(dev); + + device_for_each_child_node_scoped(dev, child) { + ret = adc5_gen3_get_fw_channel_data(adc, chan_props, child); + if (ret) + return ret; + + chan_props->chip = adc; + adc_chan = &adc->data->adc_chans[chan_props->common_props.channel]; + chan_props->common_props.scale_fn_type = adc_chan->scale_fn_type; + + iio_chan->channel = ADC5_GEN3_V_CHAN(chan_props->common_props); + iio_chan->info_mask_separate = adc_chan->info_mask; + iio_chan->type = adc_chan->type; + iio_chan->address = index; + iio_chan->indexed = 1; + iio_chan++; + chan_props++; + index++; + } + + return 0; +} + +static void adc5_gen3_uninit_aux(void *data) +{ + auxiliary_device_uninit(data); +} + +static void adc5_gen3_delete_aux(void *data) +{ + auxiliary_device_delete(data); +} + +static void adc5_gen3_aux_device_release(struct device *dev) {} + +static int adc5_gen3_add_aux_tm_device(struct adc5_chip *adc) +{ + struct tm5_aux_dev_wrapper *aux_device; + int i, ret, i_tm = 0; + + aux_device = devm_kzalloc(adc->dev, sizeof(*aux_device), GFP_KERNEL); + if (!aux_device) + return -ENOMEM; + + aux_device->aux_dev.name = "adc5_tm_gen3"; + aux_device->aux_dev.dev.parent = adc->dev; + aux_device->aux_dev.dev.release = adc5_gen3_aux_device_release; + + aux_device->tm_props = devm_kcalloc(adc->dev, adc->n_tm_channels, + sizeof(*aux_device->tm_props), + GFP_KERNEL); + if (!aux_device->tm_props) + return -ENOMEM; + + aux_device->dev_data = &adc->dev_data; + + for (i = 0; i < adc->nchannels; i++) { + if (!adc->chan_props[i].adc_tm) + continue; + aux_device->tm_props[i_tm] = adc->chan_props[i].common_props; + i_tm++; + } + + device_set_of_node_from_dev(&aux_device->aux_dev.dev, adc->dev); + + aux_device->n_tm_channels = adc->n_tm_channels; + + ret = auxiliary_device_init(&aux_device->aux_dev); + if (ret) + return ret; + + ret = devm_add_action_or_reset(adc->dev, adc5_gen3_uninit_aux, + &aux_device->aux_dev); + if (ret) + return ret; + + ret = auxiliary_device_add(&aux_device->aux_dev); + if (ret) + return ret; + ret = devm_add_action_or_reset(adc->dev, adc5_gen3_delete_aux, + &aux_device->aux_dev); + if (ret) + return ret; + + adc->tm_aux = &aux_device->aux_dev; + + return 0; +} + +void adc5_gen3_mutex_lock(struct device *dev) + __acquires(&adc->lock) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + + mutex_lock(&adc->lock); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_mutex_lock, "QCOM_SPMI_ADC5_GEN3"); + +void adc5_gen3_mutex_unlock(struct device *dev) + __releases(&adc->lock) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + + mutex_unlock(&adc->lock); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_mutex_unlock, "QCOM_SPMI_ADC5_GEN3"); + +int adc5_gen3_get_scaled_reading(struct device *dev, + struct adc5_channel_common_prop *common_props, + int *val) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + u16 adc_code_volt; + int ret; + + ret = adc5_gen3_do_conversion(adc, common_props, &adc_code_volt); + if (ret) + return ret; + + return qcom_adc5_hw_scale(common_props->scale_fn_type, + common_props->prescale, + adc->data, adc_code_volt, val); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_get_scaled_reading, "QCOM_SPMI_ADC5_GEN3"); + +int adc5_gen3_therm_code_to_temp(struct device *dev, + struct adc5_channel_common_prop *common_props, + u16 code, int *val) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + + return qcom_adc5_hw_scale(common_props->scale_fn_type, + common_props->prescale, + adc->data, code, val); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_therm_code_to_temp, "QCOM_SPMI_ADC5_GEN3"); + +void adc5_gen3_register_tm_event_notifier(struct device *dev, + void (*handler)(struct auxiliary_device *)) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + + adc->handler = handler; +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_register_tm_event_notifier, "QCOM_SPMI_ADC5_GEN3"); + +static int adc5_gen3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct adc5_chip *adc; + struct regmap *regmap; + int ret, i; + u32 *reg; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->dev_data.regmap = regmap; + adc->dev = dev; + + ret = device_property_count_u32(dev, "reg"); + if (ret < 0) + return ret; + + adc->dev_data.num_sdams = ret; + + reg = devm_kcalloc(dev, adc->dev_data.num_sdams, sizeof(u32), + GFP_KERNEL); + if (!reg) + return -ENOMEM; + + ret = device_property_read_u32_array(dev, "reg", reg, + adc->dev_data.num_sdams); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read reg property\n"); + + adc->dev_data.base = devm_kcalloc(dev, adc->dev_data.num_sdams, + sizeof(*adc->dev_data.base), + GFP_KERNEL); + if (!adc->dev_data.base) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + init_completion(&adc->complete); + ret = devm_mutex_init(dev, &adc->lock); + if (ret) + return ret; + + for (i = 0; i < adc->dev_data.num_sdams; i++) { + adc->dev_data.base[i].base_addr = reg[i]; + + ret = platform_get_irq(pdev, i); + if (ret < 0) + return dev_err_probe(dev, ret, + "Getting IRQ %d failed\n", i); + + adc->dev_data.base[i].irq = ret; + + adc->dev_data.base[i].irq_name = devm_kasprintf(dev, GFP_KERNEL, + "sdam%d", i); + if (!adc->dev_data.base[i].irq_name) + return -ENOMEM; + } + + ret = devm_request_irq(dev, adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq, + adc5_gen3_isr, 0, + adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq_name, + adc); + if (ret) + return dev_err_probe(dev, ret, + "Failed to request SDAM%d irq\n", + ADC5_GEN3_VADC_SDAM); + + ret = adc5_get_fw_data(adc); + if (ret) + return ret; + + if (adc->n_tm_channels > 0) { + ret = adc5_gen3_add_aux_tm_device(adc); + if (ret) + dev_err_probe(dev, ret, + "Failed to add auxiliary TM device\n"); + } + + indio_dev->name = "spmi-adc5-gen3"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &adc5_gen3_info; + indio_dev->channels = adc->iio_chans; + indio_dev->num_channels = adc->nchannels; + + return devm_iio_device_register(dev, indio_dev); +} + +static struct platform_driver adc5_gen3_driver = { + .driver = { + .name = "qcom-spmi-adc5-gen3", + .of_match_table = adc5_match_table, + }, + .probe = adc5_gen3_probe, +}; +module_platform_driver(adc5_gen3_driver); + +MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 Gen3 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("QCOM_SPMI_ADC5_GEN3"); diff --git a/drivers/iio/adc/rohm-bd79124.c b/drivers/iio/adc/rohm-bd79124.c index fc0452749b79..40d00bd0cc9d 100644 --- a/drivers/iio/adc/rohm-bd79124.c +++ b/drivers/iio/adc/rohm-bd79124.c @@ -75,7 +75,7 @@ /* * The high limit, low limit and last measurement result are each stored in - * 2 consequtive registers. 4 bits are in the high bits of the first register + * 2 consecutive registers. 4 bits are in the high bits of the first register * and 8 bits in the next register. * * These macros return the address of the first reg for the given channel. @@ -962,7 +962,7 @@ static int bd79124_hw_init(struct bd79124_data *data) if (ret) return ret; - /* Enable writing the measured values to the regsters */ + /* Enable writing the measured values to the registers */ ret = regmap_set_bits(data->map, BD79124_REG_GEN_CFG, BD79124_MSK_STATS_EN); if (ret) diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c index 9f7a75168aac..218117c45ec8 100644 --- a/drivers/iio/adc/sd_adc_modulator.c +++ b/drivers/iio/adc/sd_adc_modulator.c @@ -77,6 +77,7 @@ static const struct iio_backend_ops sd_backend_ops = { static const struct iio_backend_info sd_backend_info = { .name = "sd-modulator", .ops = &sd_backend_ops, + .caps = IIO_BACKEND_CAP_ENABLE, }; static int iio_sd_mod_register(struct platform_device *pdev) diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index 50b0a607baeb..91995489bb1c 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -82,7 +82,7 @@ struct spear_adc_state { * of register writes, then a wait for a completion callback, * and finally a register read, during which userspace could issue * another read request. This lock protects a read access from - * ocurring before another one has finished. + * occurring before another one has finished. */ struct mutex lock; u32 current_clk; diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index e39a4c0db25e..a42d82d61cb8 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -227,7 +227,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev, if (priv->aclk) { /* * Asynchronous clock modes (e.g. ckmode == 0) - * From spec: PLL output musn't exceed max rate + * From spec: PLL output mustn't exceed max rate */ rate = clk_get_rate(priv->aclk); if (!rate) { diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 2d7f88459c7c..46106200bb86 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -1662,7 +1662,7 @@ static irqreturn_t stm32_adc_threaded_isr(int irq, void *data) /* * Clear ovr bit to avoid subsequent calls to IRQ handler. * This requires to stop ADC first. OVR bit state in ISR, - * is propaged to CSR register by hardware. + * is propagated to CSR register by hardware. */ adc->cfg->stop_conv(indio_dev); stm32_adc_irq_clear(indio_dev, regs->isr_ovr.mask); diff --git a/drivers/iio/adc/sun20i-gpadc-iio.c b/drivers/iio/adc/sun20i-gpadc-iio.c index e4dfe76e6362..861c14da75ad 100644 --- a/drivers/iio/adc/sun20i-gpadc-iio.c +++ b/drivers/iio/adc/sun20i-gpadc-iio.c @@ -55,7 +55,7 @@ struct sun20i_gpadc_iio { * of register writes, then a wait for a completion callback, * and finally a register read, during which userspace could issue * another read request. This lock protects a read access from - * ocurring before another one has finished. + * occurring before another one has finished. */ struct mutex lock; }; diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index f2a93c63ca14..c7ffe47449e2 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -231,7 +231,6 @@ static const struct iio_event_spec ads1015_events[] = { } struct ads1015_channel_data { - bool enabled; unsigned int pga; unsigned int data_rate; }; diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index 79be71b4de96..2320be0efb50 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -741,8 +741,7 @@ static int ads1119_probe(struct i2c_client *client) ret = devm_request_irq(dev, client->irq, ads1119_irq_handler, IRQF_NO_THREAD, "ads1119", indio_dev); if (ret) - return dev_err_probe(dev, ret, - "Failed to allocate irq\n"); + return ret; st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index cdc624889559..028acd42741f 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -47,8 +47,6 @@ #define TI_ADS7950_MAX_CHAN 16 #define TI_ADS7950_NUM_GPIOS 4 -#define TI_ADS7950_TIMESTAMP_SIZE (sizeof(int64_t) / sizeof(__be16)) - /* val = value, dec = left shift, bits = number of bits of the mask */ #define TI_ADS7950_EXTRACT(val, dec, bits) \ (((val) >> (dec)) & ((1 << (bits)) - 1)) @@ -105,8 +103,7 @@ struct ti_ads7950_state { * DMA (thus cache coherency maintenance) may require the * transfer buffers to live in their own cache lines. */ - u16 rx_buf[TI_ADS7950_MAX_CHAN + 2 + TI_ADS7950_TIMESTAMP_SIZE] - __aligned(IIO_DMA_MINALIGN); + u16 rx_buf[TI_ADS7950_MAX_CHAN + 2] __aligned(IIO_DMA_MINALIGN); u16 tx_buf[TI_ADS7950_MAX_CHAN + 2]; u16 single_tx; u16 single_rx; @@ -118,21 +115,6 @@ struct ti_ads7950_chip_info { unsigned int num_channels; }; -enum ti_ads7950_id { - TI_ADS7950, - TI_ADS7951, - TI_ADS7952, - TI_ADS7953, - TI_ADS7954, - TI_ADS7955, - TI_ADS7956, - TI_ADS7957, - TI_ADS7958, - TI_ADS7959, - TI_ADS7960, - TI_ADS7961, -}; - #define TI_ADS7950_V_CHAN(index, bits) \ { \ .type = IIO_VOLTAGE, \ @@ -225,55 +207,64 @@ static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7959, 8); static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7960, 8); static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7961, 8); -static const struct ti_ads7950_chip_info ti_ads7950_chip_info[] = { - [TI_ADS7950] = { - .channels = ti_ads7950_channels, - .num_channels = ARRAY_SIZE(ti_ads7950_channels), - }, - [TI_ADS7951] = { - .channels = ti_ads7951_channels, - .num_channels = ARRAY_SIZE(ti_ads7951_channels), - }, - [TI_ADS7952] = { - .channels = ti_ads7952_channels, - .num_channels = ARRAY_SIZE(ti_ads7952_channels), - }, - [TI_ADS7953] = { - .channels = ti_ads7953_channels, - .num_channels = ARRAY_SIZE(ti_ads7953_channels), - }, - [TI_ADS7954] = { - .channels = ti_ads7954_channels, - .num_channels = ARRAY_SIZE(ti_ads7954_channels), - }, - [TI_ADS7955] = { - .channels = ti_ads7955_channels, - .num_channels = ARRAY_SIZE(ti_ads7955_channels), - }, - [TI_ADS7956] = { - .channels = ti_ads7956_channels, - .num_channels = ARRAY_SIZE(ti_ads7956_channels), - }, - [TI_ADS7957] = { - .channels = ti_ads7957_channels, - .num_channels = ARRAY_SIZE(ti_ads7957_channels), - }, - [TI_ADS7958] = { - .channels = ti_ads7958_channels, - .num_channels = ARRAY_SIZE(ti_ads7958_channels), - }, - [TI_ADS7959] = { - .channels = ti_ads7959_channels, - .num_channels = ARRAY_SIZE(ti_ads7959_channels), - }, - [TI_ADS7960] = { - .channels = ti_ads7960_channels, - .num_channels = ARRAY_SIZE(ti_ads7960_channels), - }, - [TI_ADS7961] = { - .channels = ti_ads7961_channels, - .num_channels = ARRAY_SIZE(ti_ads7961_channels), - }, +static const struct ti_ads7950_chip_info ti_ads7950_chip_info = { + .channels = ti_ads7950_channels, + .num_channels = ARRAY_SIZE(ti_ads7950_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7951_chip_info = { + .channels = ti_ads7951_channels, + .num_channels = ARRAY_SIZE(ti_ads7951_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7952_chip_info = { + .channels = ti_ads7952_channels, + .num_channels = ARRAY_SIZE(ti_ads7952_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7953_chip_info = { + .channels = ti_ads7953_channels, + .num_channels = ARRAY_SIZE(ti_ads7953_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7954_chip_info = { + .channels = ti_ads7954_channels, + .num_channels = ARRAY_SIZE(ti_ads7954_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7955_chip_info = { + .channels = ti_ads7955_channels, + .num_channels = ARRAY_SIZE(ti_ads7955_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7956_chip_info = { + .channels = ti_ads7956_channels, + .num_channels = ARRAY_SIZE(ti_ads7956_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7957_chip_info = { + .channels = ti_ads7957_channels, + .num_channels = ARRAY_SIZE(ti_ads7957_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7958_chip_info = { + .channels = ti_ads7958_channels, + .num_channels = ARRAY_SIZE(ti_ads7958_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7959_chip_info = { + .channels = ti_ads7959_channels, + .num_channels = ARRAY_SIZE(ti_ads7959_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7960_chip_info = { + .channels = ti_ads7960_channels, + .num_channels = ARRAY_SIZE(ti_ads7960_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7961_chip_info = { + .channels = ti_ads7961_channels, + .num_channels = ARRAY_SIZE(ti_ads7961_channels), }; /* @@ -313,8 +304,10 @@ static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p) if (ret < 0) goto out; - iio_push_to_buffers_with_timestamp(indio_dev, &st->rx_buf[2], - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts_unaligned(indio_dev, &st->rx_buf[2], + sizeof(*st->rx_buf) * + TI_ADS7950_MAX_CHAN, + iio_get_time_ns(indio_dev)); out: mutex_unlock(&st->slock); @@ -563,7 +556,7 @@ static int ti_ads7950_probe(struct spi_device *spi) st->spi = spi; - info = &ti_ads7950_chip_info[spi_get_device_id(spi)->driver_data]; + info = spi_get_device_match_data(spi); indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; @@ -685,35 +678,35 @@ static void ti_ads7950_remove(struct spi_device *spi) } static const struct spi_device_id ti_ads7950_id[] = { - { "ads7950", TI_ADS7950 }, - { "ads7951", TI_ADS7951 }, - { "ads7952", TI_ADS7952 }, - { "ads7953", TI_ADS7953 }, - { "ads7954", TI_ADS7954 }, - { "ads7955", TI_ADS7955 }, - { "ads7956", TI_ADS7956 }, - { "ads7957", TI_ADS7957 }, - { "ads7958", TI_ADS7958 }, - { "ads7959", TI_ADS7959 }, - { "ads7960", TI_ADS7960 }, - { "ads7961", TI_ADS7961 }, + { "ads7950", (kernel_ulong_t)&ti_ads7950_chip_info }, + { "ads7951", (kernel_ulong_t)&ti_ads7951_chip_info }, + { "ads7952", (kernel_ulong_t)&ti_ads7952_chip_info }, + { "ads7953", (kernel_ulong_t)&ti_ads7953_chip_info }, + { "ads7954", (kernel_ulong_t)&ti_ads7954_chip_info }, + { "ads7955", (kernel_ulong_t)&ti_ads7955_chip_info }, + { "ads7956", (kernel_ulong_t)&ti_ads7956_chip_info }, + { "ads7957", (kernel_ulong_t)&ti_ads7957_chip_info }, + { "ads7958", (kernel_ulong_t)&ti_ads7958_chip_info }, + { "ads7959", (kernel_ulong_t)&ti_ads7959_chip_info }, + { "ads7960", (kernel_ulong_t)&ti_ads7960_chip_info }, + { "ads7961", (kernel_ulong_t)&ti_ads7961_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ti_ads7950_id); static const struct of_device_id ads7950_of_table[] = { - { .compatible = "ti,ads7950", .data = &ti_ads7950_chip_info[TI_ADS7950] }, - { .compatible = "ti,ads7951", .data = &ti_ads7950_chip_info[TI_ADS7951] }, - { .compatible = "ti,ads7952", .data = &ti_ads7950_chip_info[TI_ADS7952] }, - { .compatible = "ti,ads7953", .data = &ti_ads7950_chip_info[TI_ADS7953] }, - { .compatible = "ti,ads7954", .data = &ti_ads7950_chip_info[TI_ADS7954] }, - { .compatible = "ti,ads7955", .data = &ti_ads7950_chip_info[TI_ADS7955] }, - { .compatible = "ti,ads7956", .data = &ti_ads7950_chip_info[TI_ADS7956] }, - { .compatible = "ti,ads7957", .data = &ti_ads7950_chip_info[TI_ADS7957] }, - { .compatible = "ti,ads7958", .data = &ti_ads7950_chip_info[TI_ADS7958] }, - { .compatible = "ti,ads7959", .data = &ti_ads7950_chip_info[TI_ADS7959] }, - { .compatible = "ti,ads7960", .data = &ti_ads7950_chip_info[TI_ADS7960] }, - { .compatible = "ti,ads7961", .data = &ti_ads7950_chip_info[TI_ADS7961] }, + { .compatible = "ti,ads7950", .data = &ti_ads7950_chip_info }, + { .compatible = "ti,ads7951", .data = &ti_ads7951_chip_info }, + { .compatible = "ti,ads7952", .data = &ti_ads7952_chip_info }, + { .compatible = "ti,ads7953", .data = &ti_ads7953_chip_info }, + { .compatible = "ti,ads7954", .data = &ti_ads7954_chip_info }, + { .compatible = "ti,ads7955", .data = &ti_ads7955_chip_info }, + { .compatible = "ti,ads7956", .data = &ti_ads7956_chip_info }, + { .compatible = "ti,ads7957", .data = &ti_ads7957_chip_info }, + { .compatible = "ti,ads7958", .data = &ti_ads7958_chip_info }, + { .compatible = "ti,ads7959", .data = &ti_ads7959_chip_info }, + { .compatible = "ti,ads7960", .data = &ti_ads7960_chip_info }, + { .compatible = "ti,ads7961", .data = &ti_ads7961_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ads7950_of_table); diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index a1a28584de93..1516dd332f90 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -113,10 +113,10 @@ static void tiadc_step_config(struct iio_dev *indio_dev) * There are 16 configurable steps and 8 analog input * lines available which are shared between Touchscreen and ADC. * - * Steps forwards i.e. from 0 towards 16 are used by ADC - * depending on number of input lines needed. + * Steps forward, i.e. from 0 towards 16, are used by ADC + * depending on the number of input lines needed. * Channel would represent which analog input - * needs to be given to ADC to digitalize data. + * needs to be given to ADC to digitize data. */ for (i = 0; i < adc_dev->channels; i++) { int chan; diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index fe3b31ec976e..f0274cd74973 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -252,7 +252,7 @@ static const struct s16_fract twl4030_divider_ratios[16] = { {5, 11}, /* CHANNEL 15 */ }; -/* Conversion table from -3 to 55 degrees Celcius */ +/* Conversion table from -3 to 55 degrees Celsius */ static int twl4030_therm_tbl[] = { 30800, 29500, 28300, 27100, 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index 3ac774ebf678..7810d6b2b668 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -416,7 +416,7 @@ static u8 twl6032_channel_to_reg(int channel) { /* * for any prior chosen channel, when the conversion is ready - * the result is avalable in GPCH0_LSB, GPCH0_MSB. + * the result is available in GPCH0_LSB, GPCH0_MSB. */ return TWL6032_GPADC_GPCH0_LSB; diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index a20b4d48c5f7..fe930ce5ee30 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -839,12 +839,9 @@ static int _ad74413r_get_single_adc_result(struct ad74413r_state *st, if (ret) return ret; - ret = wait_for_completion_timeout(&st->adc_data_completion, - msecs_to_jiffies(1000)); - if (!ret) { - ret = -ETIMEDOUT; - return ret; - } + if (!wait_for_completion_timeout(&st->adc_data_completion, + msecs_to_jiffies(1000))) + return -ETIMEDOUT; ret = regmap_read(st->regmap, AD74413R_REG_ADC_RESULT_X(channel), &uval); diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig index a8a604863eed..9e24421b5e97 100644 --- a/drivers/iio/amplifiers/Kconfig +++ b/drivers/iio/amplifiers/Kconfig @@ -18,7 +18,15 @@ config AD8366 AD8366 Dual-Digital Variable Gain Amplifier (VGA) ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) ADL5240 Digitally controlled variable gain amplifier (VGA) + ADRF5702: 0.125 dB LSB, 8-Bit, Silicon Digital Attenuator + ADRF5703: 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator + ADRF5720: 0.5 dB LSB, 6-Bit, Silicon Digital Attenuator + ADRF5730: 0.5 dB LSB, 6-Bit, Silicon Digital Attenuator + ADRF5731: 2 dB LSB, 4-Bit, Silicon Digital Attenuator + HMC271A: 1dB LSB 5-Bit Digital Attenuator SMT HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator + HMC1018A: 1.0 dB LSB GaAs MMIC 5-BIT Digital Attenuator + HMC1019A: 0.5 dB LSB GaAs MMIC 5-BIT Digital Attenuator HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator To compile this driver as a module, choose M here: the diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index d06ac786501c..bbf41a1fb3a1 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -5,46 +5,52 @@ * AD8366 Dual-Digital Variable Gain Amplifier (VGA) * ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) * ADL5240 Digitally controlled variable gain amplifier (VGA) + * ADRF5702: 0.125 dB LSB, 8-Bit, Silicon Digital Attenuator, 50 MHz to 20 GHz + * ADRF5703: 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator, 9 kHz to 20 GHz + * ADRF5720: 0.5 dB LSB, 6-Bit, Silicon Digital Attenuator, 9 kHz to 40 GHz + * ADRF5730: 0.5 dB LSB, 6-Bit, Silicon Digital Attenuator, 100 MHz to 40 GHz + * ADRF5731: 2 dB LSB, 4-Bit, Silicon Digital Attenuator, 100 MHz to 40 GHz + * HMC271A: 1dB LSB 5-Bit Digital Attenuator SMT, 0.7 - 3.7 GHz * HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator + * HMC1018A: 1.0 dB LSB GaAs MMIC 5-BIT DIGITAL ATTENUATOR, 0.1 - 30 GHz + * HMC1019A: 0.5 dB LSB GaAs MMIC 5-BIT DIGITAL ATTENUATOR, 0.1 - 30 GHz * HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator * - * Copyright 2012-2019 Analog Devices Inc. + * Copyright 2012-2026 Analog Devices Inc. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include - -enum ad8366_type { - ID_AD8366, - ID_ADA4961, - ID_ADL5240, - ID_HMC792, - ID_HMC1119, -}; struct ad8366_info { + const char *name; int gain_min; int gain_max; + int gain_step; + size_t num_channels; + size_t (*pack_code)(const unsigned char *code, size_t num_channels, + unsigned char *data); }; struct ad8366_state { struct spi_device *spi; - struct regulator *reg; struct mutex lock; /* protect sensor state */ - struct gpio_desc *reset_gpio; unsigned char ch[2]; - enum ad8366_type type; const struct ad8366_info *info; /* * DMA (thus cache coherency maintenance) may require the @@ -53,60 +59,148 @@ struct ad8366_state { unsigned char data[2] __aligned(IIO_DMA_MINALIGN); }; -static const struct ad8366_info ad8366_infos[] = { - [ID_AD8366] = { - .gain_min = 4500, - .gain_max = 20500, - }, - [ID_ADA4961] = { - .gain_min = -6000, - .gain_max = 15000, - }, - [ID_ADL5240] = { - .gain_min = -11500, - .gain_max = 20000, - }, - [ID_HMC792] = { - .gain_min = -15750, - .gain_max = 0, - }, - [ID_HMC1119] = { - .gain_min = -31750, - .gain_max = 0, - }, +static size_t ad8366_pack_code(const unsigned char *code, size_t num_channels, + unsigned char *data) +{ + u8 ch_a = bitrev8(code[0]) >> 2; + u8 ch_b = bitrev8(code[1]) >> 2; + + put_unaligned_be16((ch_b << 6) | ch_a, &data[0]); + return sizeof(__be16); +} + +static size_t adrf5731_pack_code(const unsigned char *code, size_t num_channels, + unsigned char *data) +{ + data[0] = code[0] << 2; + return 1; +} + +static size_t hmc271_pack_code(const unsigned char *code, size_t num_channels, + unsigned char *data) +{ + data[0] = bitrev8(code[0]) >> 3; + return 1; +} + +static const struct ad8366_info ad8366_chip_info = { + .name = "ad8366", + .gain_min = 4500, + .gain_max = 20500, + .gain_step = 253, + .num_channels = 2, + .pack_code = ad8366_pack_code, }; -static int ad8366_write(struct iio_dev *indio_dev, - unsigned char ch_a, unsigned char ch_b) +static const struct ad8366_info ada4961_chip_info = { + .name = "ada4961", + .gain_min = -6000, + .gain_max = 15000, + .gain_step = -1000, + .num_channels = 1, +}; + +static const struct ad8366_info adl5240_chip_info = { + .name = "adl5240", + .gain_min = -11500, + .gain_max = 20000, + .gain_step = 500, + .num_channels = 1, +}; + +static const struct ad8366_info adrf5702_chip_info = { + .name = "adrf5702", + .gain_min = -31875, + .gain_max = 0, + .gain_step = -125, + .num_channels = 1, +}; + +static const struct ad8366_info adrf5703_chip_info = { + .name = "adrf5703", + .gain_min = -31750, + .gain_max = 0, + .gain_step = -250, + .num_channels = 1, +}; + +static const struct ad8366_info adrf5720_chip_info = { + .name = "adrf5720", + .gain_min = -31500, + .gain_max = 0, + .gain_step = -500, + .num_channels = 1, +}; + +static const struct ad8366_info adrf5730_chip_info = { + .name = "adrf5730", + .gain_min = -31500, + .gain_max = 0, + .gain_step = -500, + .num_channels = 1, +}; + +static const struct ad8366_info adrf5731_chip_info = { + .name = "adrf5731", + .gain_min = -30000, + .gain_max = 0, + .gain_step = -2000, + .num_channels = 1, + .pack_code = adrf5731_pack_code, +}; + +static const struct ad8366_info hmc271_chip_info = { + .name = "hmc271a", + .gain_min = -31000, + .gain_max = 0, + .gain_step = 1000, + .num_channels = 1, + .pack_code = hmc271_pack_code, +}; + +static const struct ad8366_info hmc792_chip_info = { + .name = "hmc792a", + .gain_min = -15750, + .gain_max = 0, + .gain_step = 250, + .num_channels = 1, +}; + +static const struct ad8366_info hmc1018_chip_info = { + .name = "hmc1018a", + .gain_min = -31000, + .gain_max = 0, + .gain_step = 1000, + .num_channels = 1, +}; + +static const struct ad8366_info hmc1019_chip_info = { + .name = "hmc1019a", + .gain_min = -15500, + .gain_max = 0, + .gain_step = 500, + .num_channels = 1, +}; + +static const struct ad8366_info hmc1119_chip_info = { + .name = "hmc1119", + .gain_min = -31750, + .gain_max = 0, + .gain_step = -250, + .num_channels = 1, +}; + +static int ad8366_write_code(struct ad8366_state *st) { - struct ad8366_state *st = iio_priv(indio_dev); - int ret; + const struct ad8366_info *inf = st->info; + size_t len = 1; - switch (st->type) { - case ID_AD8366: - ch_a = bitrev8(ch_a & 0x3F); - ch_b = bitrev8(ch_b & 0x3F); + if (inf->pack_code) + len = inf->pack_code(st->ch, inf->num_channels, st->data); + else + st->data[0] = st->ch[0]; - st->data[0] = ch_b >> 4; - st->data[1] = (ch_b << 4) | (ch_a >> 2); - break; - case ID_ADA4961: - st->data[0] = ch_a & 0x1F; - break; - case ID_ADL5240: - st->data[0] = (ch_a & 0x3F); - break; - case ID_HMC792: - case ID_HMC1119: - st->data[0] = ch_a; - break; - } - - ret = spi_write(st->spi, st->data, indio_dev->num_channels); - if (ret < 0) - dev_err(&indio_dev->dev, "write failed (%d)", ret); - - return ret; + return spi_write(st->spi, st->data, len); } static int ad8366_read_raw(struct iio_dev *indio_dev, @@ -116,6 +210,7 @@ static int ad8366_read_raw(struct iio_dev *indio_dev, long m) { struct ad8366_state *st = iio_priv(indio_dev); + const struct ad8366_info *inf = st->info; int ret; int code, gain = 0; @@ -123,25 +218,8 @@ static int ad8366_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_HARDWAREGAIN: code = st->ch[chan->channel]; - - switch (st->type) { - case ID_AD8366: - gain = code * 253 + 4500; - break; - case ID_ADA4961: - gain = 15000 - code * 1000; - break; - case ID_ADL5240: - gain = 20000 - 31500 + code * 500; - break; - case ID_HMC792: - gain = -1 * code * 500; - break; - case ID_HMC1119: - gain = -1 * code * 250; - break; - } - + gain = inf->gain_step > 0 ? inf->gain_min : inf->gain_max; + gain += inf->gain_step * code; /* Values in dB */ *val = gain / 1000; *val2 = (gain % 1000) * 1000; @@ -176,29 +254,14 @@ static int ad8366_write_raw(struct iio_dev *indio_dev, if (gain > inf->gain_max || gain < inf->gain_min) return -EINVAL; - switch (st->type) { - case ID_AD8366: - code = (gain - 4500) / 253; - break; - case ID_ADA4961: - code = (15000 - gain) / 1000; - break; - case ID_ADL5240: - code = ((gain - 500 - 20000) / 500) & 0x3F; - break; - case ID_HMC792: - code = (abs(gain) / 500) & 0x3F; - break; - case ID_HMC1119: - code = (abs(gain) / 250) & 0x7F; - break; - } + gain -= inf->gain_step > 0 ? inf->gain_min : inf->gain_max; + code = DIV_ROUND_CLOSEST(gain, inf->gain_step); mutex_lock(&st->lock); switch (mask) { case IIO_CHAN_INFO_HARDWAREGAIN: st->ch[chan->channel] = code; - ret = ad8366_write(indio_dev, st->ch[0], st->ch[1]); + ret = ad8366_write_code(st); break; default: ret = -EINVAL; @@ -239,107 +302,97 @@ static const struct iio_chan_spec ad8366_channels[] = { AD8366_CHAN(1), }; -static const struct iio_chan_spec ada4961_channels[] = { - AD8366_CHAN(0), -}; - static int ad8366_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; + struct gpio_desc *enable_gpio; + struct reset_control *rstc; struct iio_dev *indio_dev; struct ad8366_state *st; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; st = iio_priv(indio_dev); - st->reg = devm_regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(st->reg)) { - ret = regulator_enable(st->reg); - if (ret) - return ret; - } + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + ret = devm_regulator_get_enable(dev, "vcc"); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulator\n"); - spi_set_drvdata(spi, indio_dev); - mutex_init(&st->lock); st->spi = spi; - st->type = spi_get_device_id(spi)->driver_data; + st->info = spi_get_device_match_data(spi); - switch (st->type) { - case ID_AD8366: - indio_dev->channels = ad8366_channels; - indio_dev->num_channels = ARRAY_SIZE(ad8366_channels); - break; - case ID_ADA4961: - case ID_ADL5240: - case ID_HMC792: - case ID_HMC1119: - st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(st->reset_gpio)) { - ret = PTR_ERR(st->reset_gpio); - goto error_disable_reg; - } - indio_dev->channels = ada4961_channels; - indio_dev->num_channels = ARRAY_SIZE(ada4961_channels); - break; - default: - dev_err(&spi->dev, "Invalid device ID\n"); - ret = -EINVAL; - goto error_disable_reg; - } + enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(enable_gpio)) + return dev_err_probe(dev, PTR_ERR(enable_gpio), + "Failed to get enable GPIO\n"); - st->info = &ad8366_infos[st->type]; - indio_dev->name = spi_get_device_id(spi)->name; + rstc = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), + "Failed to get reset controller\n"); + + indio_dev->name = st->info->name; indio_dev->info = &ad8366_info; indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad8366_channels; + indio_dev->num_channels = st->info->num_channels; - ret = ad8366_write(indio_dev, 0, 0); + ret = ad8366_write_code(st); if (ret < 0) - goto error_disable_reg; + return dev_err_probe(dev, ret, "failed to write initial gain\n"); - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); - - return ret; -} - -static void ad8366_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad8366_state *st = iio_priv(indio_dev); - struct regulator *reg = st->reg; - - iio_device_unregister(indio_dev); - - if (!IS_ERR(reg)) - regulator_disable(reg); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id ad8366_id[] = { - {"ad8366", ID_AD8366}, - {"ada4961", ID_ADA4961}, - {"adl5240", ID_ADL5240}, - {"hmc792a", ID_HMC792}, - {"hmc1119", ID_HMC1119}, + { "ad8366", (kernel_ulong_t)&ad8366_chip_info }, + { "ada4961", (kernel_ulong_t)&ada4961_chip_info }, + { "adl5240", (kernel_ulong_t)&adl5240_chip_info }, + { "adrf5702", (kernel_ulong_t)&adrf5702_chip_info }, + { "adrf5703", (kernel_ulong_t)&adrf5703_chip_info }, + { "adrf5720", (kernel_ulong_t)&adrf5720_chip_info }, + { "adrf5730", (kernel_ulong_t)&adrf5730_chip_info }, + { "adrf5731", (kernel_ulong_t)&adrf5731_chip_info }, + { "hmc271a", (kernel_ulong_t)&hmc271_chip_info }, + { "hmc792a", (kernel_ulong_t)&hmc792_chip_info }, + { "hmc1018a", (kernel_ulong_t)&hmc1018_chip_info }, + { "hmc1019a", (kernel_ulong_t)&hmc1019_chip_info }, + { "hmc1119", (kernel_ulong_t)&hmc1119_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad8366_id); +static const struct of_device_id ad8366_of_match[] = { + { .compatible = "adi,ad8366", .data = &ad8366_chip_info }, + { .compatible = "adi,ada4961", .data = &ada4961_chip_info }, + { .compatible = "adi,adl5240", .data = &adl5240_chip_info }, + { .compatible = "adi,adrf5702", .data = &adrf5702_chip_info }, + { .compatible = "adi,adrf5703", .data = &adrf5703_chip_info }, + { .compatible = "adi,adrf5720", .data = &adrf5720_chip_info }, + { .compatible = "adi,adrf5730", .data = &adrf5730_chip_info }, + { .compatible = "adi,adrf5731", .data = &adrf5731_chip_info }, + { .compatible = "adi,hmc271a", .data = &hmc271_chip_info }, + { .compatible = "adi,hmc792a", .data = &hmc792_chip_info }, + { .compatible = "adi,hmc1018a", .data = &hmc1018_chip_info }, + { .compatible = "adi,hmc1019a", .data = &hmc1019_chip_info }, + { .compatible = "adi,hmc1119", .data = &hmc1119_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad8366_of_match); + static struct spi_driver ad8366_driver = { .driver = { - .name = KBUILD_MODNAME, + .name = KBUILD_MODNAME, + .of_match_table = ad8366_of_match, }, .probe = ad8366_probe, - .remove = ad8366_remove, .id_table = ad8366_id, }; diff --git a/drivers/iio/amplifiers/ada4250.c b/drivers/iio/amplifiers/ada4250.c index 40f396ea9069..71e361af2074 100644 --- a/drivers/iio/amplifiers/ada4250.c +++ b/drivers/iio/amplifiers/ada4250.c @@ -109,7 +109,7 @@ static int ada4250_set_offset_uv(struct iio_dev *indio_dev, /* * Compute Range and Voltage per LSB for the Sensor Offset Calibration - * Example of computation for Range 1 and Range 2 (Curren Bias Set = AVDD): + * Example of computation for Range 1 and Range 2 (Current Bias Set = AVDD): * Range 1 Range 2 * Gain | Max Vos(mV) | LSB(mV) | Max Vos(mV) | LSB(mV) | * 2 | X1*127 | X1=0.126(AVDD-1) | X1*3*127 | X1*3 | diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index cb771ef8eeb3..24d7df603760 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -28,7 +28,6 @@ struct hw_consumer_buffer { struct list_head head; struct iio_dev *indio_dev; struct iio_buffer buffer; - long scan_mask[]; }; static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer( @@ -52,7 +51,6 @@ static const struct iio_buffer_access_funcs iio_hw_buf_access = { static struct hw_consumer_buffer *iio_hw_consumer_get_buffer( struct iio_hw_consumer *hwc, struct iio_dev *indio_dev) { - unsigned int mask_longs = BITS_TO_LONGS(iio_get_masklength(indio_dev)); struct hw_consumer_buffer *buf; list_for_each_entry(buf, &hwc->buffers, head) { @@ -60,13 +58,18 @@ static struct hw_consumer_buffer *iio_hw_consumer_get_buffer( return buf; } - buf = kzalloc_flex(*buf, scan_mask, mask_longs); + buf = kzalloc_obj(*buf); if (!buf) return NULL; buf->buffer.access = &iio_hw_buf_access; buf->indio_dev = indio_dev; - buf->buffer.scan_mask = buf->scan_mask; + buf->buffer.scan_mask = bitmap_zalloc(iio_get_masklength(indio_dev), + GFP_KERNEL); + if (!buf->buffer.scan_mask) { + kfree(buf); + return NULL; + } iio_buffer_init(&buf->buffer); list_add_tail(&buf->head, &hwc->buffers); diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index a6ff9085b8a2..b67ea6468226 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -224,35 +224,9 @@ void iio_kfifo_free(struct iio_buffer *r) } EXPORT_SYMBOL(iio_kfifo_free); -static void devm_iio_kfifo_release(struct device *dev, void *res) +static void devm_iio_kfifo_release(void *buffer) { - iio_kfifo_free(*(struct iio_buffer **)res); -} - -/** - * devm_iio_kfifo_allocate - Resource-managed iio_kfifo_allocate() - * @dev: Device to allocate kfifo buffer for - * - * RETURNS: - * Pointer to allocated iio_buffer on success, NULL on failure. - */ -static struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) -{ - struct iio_buffer **ptr, *r; - - ptr = devres_alloc(devm_iio_kfifo_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return NULL; - - r = iio_kfifo_allocate(); - if (r) { - *ptr = r; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return r; + iio_kfifo_free(buffer); } /** @@ -262,10 +236,12 @@ static struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) * @setup_ops: The setup_ops required to configure the HW part of the buffer (optional) * @buffer_attrs: Extra sysfs buffer attributes for this IIO buffer * - * This function allocates a kfifo buffer via devm_iio_kfifo_allocate() and + * This function allocates a kfifo buffer via iio_kfifo_allocate() and * attaches it to the IIO device via iio_device_attach_buffer(). * This is meant to be a bit of a short-hand/helper function as there are a few * drivers that seem to do this. + * + * Return: 0 on success, negative error code on failure. */ int devm_iio_kfifo_buffer_setup_ext(struct device *dev, struct iio_dev *indio_dev, @@ -273,11 +249,16 @@ int devm_iio_kfifo_buffer_setup_ext(struct device *dev, const struct iio_dev_attr **buffer_attrs) { struct iio_buffer *buffer; + int ret; - buffer = devm_iio_kfifo_allocate(dev); + buffer = iio_kfifo_allocate(); if (!buffer) return -ENOMEM; + ret = devm_add_action_or_reset(dev, devm_iio_kfifo_release, buffer); + if (ret) + return ret; + indio_dev->modes |= INDIO_BUFFER_SOFTWARE; indio_dev->setup_ops = setup_ops; diff --git a/drivers/iio/cdc/ad7150.c b/drivers/iio/cdc/ad7150.c index 427d32e398b3..8106a6a83561 100644 --- a/drivers/iio/cdc/ad7150.c +++ b/drivers/iio/cdc/ad7150.c @@ -306,7 +306,7 @@ static int ad7150_write_event_config(struct iio_dev *indio_dev, dir); if (ret) goto error_ret; - /* reenable any irq's we disabled whilst changing mode */ + /* re-enable any IRQs we disabled whilst changing mode */ enable_irq(chip->interrupts[0]); enable_irq(chip->interrupts[1]); } diff --git a/drivers/iio/cdc/ad7746.c b/drivers/iio/cdc/ad7746.c index 8a306d55c72a..cb97e3c978d8 100644 --- a/drivers/iio/cdc/ad7746.c +++ b/drivers/iio/cdc/ad7746.c @@ -606,7 +606,7 @@ static int ad7746_read_channel(struct iio_dev *indio_dev, return ret; /* - * Offset applied internally becaue the _offset userspace interface is + * Offset applied internally because the _offset userspace interface is * needed for the CAP DACs which apply a controllable offset. */ *val = get_unaligned_be24(data) - 0x800000; diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 24e0b59e2fdf..6aeac132394c 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -807,7 +807,7 @@ static int bme680_read_gas(struct bme680_data *data, int *comp_gas_res) adc_gas_res = FIELD_GET(BME680_ADC_GAS_RES, gas_regs_val); /* - * occurs if either the gas heating duration was insuffient + * This may occur if either the gas heating duration was insufficient * to reach the target heater temperature or the target * heater temperature was too high for the heater sink to * reach. diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c index 82cef4a12442..f34e2bbba2d1 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c @@ -106,7 +106,7 @@ static int cros_ec_sensors_read(struct iio_dev *indio_dev, switch (st->core.type) { case MOTIONSENSE_TYPE_ACCEL: /* - * EC returns data in g, iio exepects m/s^2. + * EC returns data in g, IIO expects m/s^2. * Do not use IIO_G_TO_M_S_2 to avoid precision loss. */ *val = div_s64(val64 * 980665, 10); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index a61428bfdce3..c115a72832b2 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -346,7 +346,7 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_rel_value, "IIO_HID"); /* - * This fuction applies the unit exponent to the scale. + * This function applies the unit exponent to the scale. * For example: * 9.806650000 ->exp:2-> val0[980]val1[665000000] * 9.000806000 ->exp:2-> val0[900]val1[80600000] diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c index 97526ba87b93..e0b10366ed2b 100644 --- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c @@ -154,7 +154,7 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, valid = inv_update_chip_period(ts, period); } - /* no previous data, compute theoritical value from interrupt */ + /* no previous data, compute theoretical value from interrupt */ if (ts->timestamp == 0) { /* elapsed time: sensor period * sensor samples number */ interval = (int64_t)ts->period * (int64_t)sample_nb; @@ -185,7 +185,7 @@ void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, /* * After ODR change the time interval with the previous sample is - * undertermined (depends when the change occures). So we compute the + * undertermined (depends when the change occurs). So we compute the * timestamp from the current interrupt using the new FIFO period, the * total number of samples and the current sample numero. */ diff --git a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c index 588470863681..1960a2ce82a8 100644 --- a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c +++ b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c @@ -96,7 +96,7 @@ EXPORT_SYMBOL_NS(ms_sensors_read_prom_word, "IIO_MEAS_SPEC_SENSORS"); * * Generic ADC conversion & read function for Measurement Specialties * devices. - * The function will issue conversion command, sleep appopriate delay, and + * The function will issue conversion command, sleep appropriate delay, and * issue command to read ADC. * * Return: 0 on success, negative errno otherwise. diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index dac593be5695..dbc5e16fbde4 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -501,14 +501,12 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits + ch->scan_type.shift, 8); - outdata = kmalloc(byte_for_channel, GFP_DMA | GFP_KERNEL); - if (!outdata) - return -ENOMEM; + outdata = sdata->buffer_data; err = regmap_bulk_read(sdata->regmap, ch->address, outdata, byte_for_channel); if (err < 0) - goto st_sensors_free_memory; + return err; if (byte_for_channel == 1) *data = (s8)*outdata; @@ -517,10 +515,7 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, else if (byte_for_channel == 3) *data = (s32)sign_extend32(get_unaligned_le24(outdata), 23); -st_sensors_free_memory: - kfree(outdata); - - return err; + return 0; } int st_sensors_read_info_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index db9f5c711b3d..cd4870b65415 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -408,6 +408,7 @@ config DPOT_DAC config DS4424 tristate "Maxim Integrated DS4422/DS4424 DAC driver" depends on I2C + select REGMAP_I2C help If you say yes here you get support for Maxim chips DS4422, DS4424. diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c index bd32fa57b1d7..20316fd568e6 100644 --- a/drivers/iio/dac/ad5360.c +++ b/drivers/iio/dac/ad5360.c @@ -206,14 +206,10 @@ static int ad5360_write_unlocked(struct iio_dev *indio_dev, static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd, unsigned int addr, unsigned int val, unsigned int shift) { - int ret; struct ad5360_state *st = iio_priv(indio_dev); - mutex_lock(&st->lock); - ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift); - mutex_unlock(&st->lock); - - return ret; + guard(mutex)(&st->lock); + return ad5360_write_unlocked(indio_dev, cmd, addr, val, shift); } static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, @@ -232,7 +228,7 @@ static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, }, }; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) | AD5360_ADDR(AD5360_REG_SF_READBACK) | @@ -240,12 +236,10 @@ static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, AD5360_READBACK_ADDR(addr)); ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); - if (ret >= 0) - ret = be32_to_cpu(st->data[1].d32) & 0xffff; + if (ret < 0) + return ret; - mutex_unlock(&st->lock); - - return ret; + return be32_to_cpu(st->data[1].d32) & 0xffff; } static ssize_t ad5360_read_dac_powerdown(struct device *dev, @@ -262,19 +256,14 @@ static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set, unsigned int clr) { struct ad5360_state *st = iio_priv(indio_dev); - int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); st->ctrl |= set; st->ctrl &= ~clr; - ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, - AD5360_REG_SF_CTRL, st->ctrl, 0); - - mutex_unlock(&st->lock); - - return ret; + return ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, + AD5360_REG_SF_CTRL, st->ctrl, 0); } static ssize_t ad5360_write_dac_powerdown(struct device *dev, diff --git a/drivers/iio/dac/ad7293.c b/drivers/iio/dac/ad7293.c index c3797e40cdd9..df6f126abf05 100644 --- a/drivers/iio/dac/ad7293.c +++ b/drivers/iio/dac/ad7293.c @@ -776,27 +776,27 @@ static int ad7293_reset(struct ad7293_state *st) static int ad7293_properties_parse(struct ad7293_state *st) { - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; int ret; - ret = devm_regulator_get_enable(&spi->dev, "avdd"); + ret = devm_regulator_get_enable(dev, "avdd"); if (ret) - return dev_err_probe(&spi->dev, ret, "failed to enable AVDD\n"); + return dev_err_probe(dev, ret, "failed to enable AVDD\n"); - ret = devm_regulator_get_enable(&spi->dev, "vdrive"); + ret = devm_regulator_get_enable(dev, "vdrive"); if (ret) - return dev_err_probe(&spi->dev, ret, "failed to enable VDRIVE\n"); + return dev_err_probe(dev, ret, "failed to enable VDRIVE\n"); - ret = devm_regulator_get_enable_optional(&spi->dev, "vrefin"); + ret = devm_regulator_get_enable_optional(dev, "vrefin"); if (ret < 0 && ret != -ENODEV) - return dev_err_probe(&spi->dev, ret, "failed to enable VREFIN\n"); + return dev_err_probe(dev, ret, "failed to enable VREFIN\n"); st->vrefin_en = ret != -ENODEV; - st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", + st->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(st->gpio_reset)) - return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_reset), + return dev_err_probe(dev, PTR_ERR(st->gpio_reset), "failed to get the reset GPIO\n"); return 0; @@ -806,7 +806,7 @@ static int ad7293_init(struct ad7293_state *st) { int ret; u16 chip_id; - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; ret = ad7293_properties_parse(st); if (ret) @@ -821,10 +821,8 @@ static int ad7293_init(struct ad7293_state *st) if (ret) return ret; - if (chip_id != AD7293_CHIP_ID) { - dev_err(&spi->dev, "Invalid Chip ID.\n"); - return -EINVAL; - } + if (chip_id != AD7293_CHIP_ID) + return dev_err_probe(dev, -EINVAL, "Invalid Chip ID.\n"); if (!st->vrefin_en) return __ad7293_spi_update_bits(st, AD7293_REG_GENERAL, @@ -845,9 +843,10 @@ static int ad7293_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct ad7293_state *st; + struct device *dev = &spi->dev; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -867,7 +866,7 @@ static int ad7293_probe(struct spi_device *spi) if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id ad7293_id[] = { diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c index 9cc895bbe51a..451fad34e7ee 100644 --- a/drivers/iio/dac/adi-axi-dac.c +++ b/drivers/iio/dac/adi-axi-dac.c @@ -114,7 +114,6 @@ struct axi_dac_state { const struct axi_dac_info *info; u64 dac_clk; u32 reg_config; - bool int_tone; int dac_clk_rate; }; @@ -869,11 +868,13 @@ static const struct iio_backend_ops axi_ad3552r_ops = { static const struct iio_backend_info axi_dac_generic = { .name = "axi-dac", .ops = &axi_dac_generic_ops, + .caps = IIO_BACKEND_CAP_BUFFER | IIO_BACKEND_CAP_ENABLE, }; static const struct iio_backend_info axi_ad3552r = { .name = "axi-ad3552r", .ops = &axi_ad3552r_ops, + .caps = IIO_BACKEND_CAP_BUFFER | IIO_BACKEND_CAP_ENABLE, }; static const struct regmap_config axi_dac_regmap_config = { diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index c61868f2de31..085f73de3f02 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -5,22 +5,31 @@ * Copyright (C) 2017 Maxim Integrated */ +#include +#include +#include +#include +#include #include #include -#include +#include +#include #include -#include -#include -#include +#include +#include + #include +#include #include #define DS4422_MAX_DAC_CHANNELS 2 #define DS4424_MAX_DAC_CHANNELS 4 +#define DS4424_DAC_MASK GENMASK(6, 0) +#define DS4404_DAC_MASK GENMASK(4, 0) +#define DS4424_DAC_SOURCE BIT(7) + #define DS4424_DAC_ADDR(chan) ((chan) + 0xf8) -#define DS4424_SOURCE_I 1 -#define DS4424_SINK_I 0 #define DS4424_CHANNEL(chan) { \ .type = IIO_CURRENT, \ @@ -30,33 +39,61 @@ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ } -/* - * DS4424 DAC control register 8 bits - * [7] 0: to sink; 1: to source - * [6:0] steps to sink/source - * bit[7] looks like a sign bit, but the value of the register is - * not a two's complement code considering the bit[6:0] is a absolute - * distance from the zero point. - */ -union ds4424_raw_data { - struct { - u8 dx:7; - u8 source_bit:1; - }; - u8 bits; +#define DS4424_CHANNEL_WITH_SCALE(chan) { \ + .type = IIO_CURRENT, \ + .indexed = 1, \ + .output = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ +} + +struct ds4424_chip_info { + const char *name; + int vref_mV; + int scale_denom; + u8 result_mask; + u8 num_channels; }; -enum ds4424_device_ids { - ID_DS4422, - ID_DS4424, +static const struct ds4424_chip_info ds4402_info = { + .name = "ds4402", + .vref_mV = 1230, + .scale_denom = 4, + .result_mask = DS4404_DAC_MASK, + .num_channels = DS4422_MAX_DAC_CHANNELS, +}; + +static const struct ds4424_chip_info ds4404_info = { + .name = "ds4404", + .vref_mV = 1230, + .scale_denom = 4, + .result_mask = DS4404_DAC_MASK, + .num_channels = DS4424_MAX_DAC_CHANNELS, +}; + +static const struct ds4424_chip_info ds4422_info = { + .name = "ds4422", + .vref_mV = 976, + .scale_denom = 16, + .result_mask = DS4424_DAC_MASK, + .num_channels = DS4422_MAX_DAC_CHANNELS, +}; + +static const struct ds4424_chip_info ds4424_info = { + .name = "ds4424", + .vref_mV = 976, + .scale_denom = 16, + .result_mask = DS4424_DAC_MASK, + .num_channels = DS4424_MAX_DAC_CHANNELS, }; struct ds4424_data { - struct i2c_client *client; - struct mutex lock; - uint8_t save[DS4424_MAX_DAC_CHANNELS]; + struct regmap *regmap; struct regulator *vcc_reg; - uint8_t raw[DS4424_MAX_DAC_CHANNELS]; + const struct ds4424_chip_info *chip_info; + u32 rfs_ohms[DS4424_MAX_DAC_CHANNELS]; + bool has_rfs; }; static const struct iio_chan_spec ds4424_channels[] = { @@ -66,63 +103,114 @@ static const struct iio_chan_spec ds4424_channels[] = { DS4424_CHANNEL(3), }; -static int ds4424_get_value(struct iio_dev *indio_dev, - int *val, int channel) +static const struct iio_chan_spec ds4424_channels_with_scale[] = { + DS4424_CHANNEL_WITH_SCALE(0), + DS4424_CHANNEL_WITH_SCALE(1), + DS4424_CHANNEL_WITH_SCALE(2), + DS4424_CHANNEL_WITH_SCALE(3), +}; + +static const struct regmap_range ds44x2_ranges[] = { + regmap_reg_range(DS4424_DAC_ADDR(0), DS4424_DAC_ADDR(1)), +}; + +static const struct regmap_range ds44x4_ranges[] = { + regmap_reg_range(DS4424_DAC_ADDR(0), DS4424_DAC_ADDR(3)), +}; + +static const struct regmap_access_table ds44x2_table = { + .yes_ranges = ds44x2_ranges, + .n_yes_ranges = ARRAY_SIZE(ds44x2_ranges), +}; + +static const struct regmap_access_table ds44x4_table = { + .yes_ranges = ds44x4_ranges, + .n_yes_ranges = ARRAY_SIZE(ds44x4_ranges), +}; + +static const struct regmap_config ds44x2_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .max_register = DS4424_DAC_ADDR(1), + .rd_table = &ds44x2_table, + .wr_table = &ds44x2_table, +}; + +static const struct regmap_config ds44x4_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .max_register = DS4424_DAC_ADDR(3), + .rd_table = &ds44x4_table, + .wr_table = &ds44x4_table, +}; + +static int ds4424_init_regmap(struct i2c_client *client, + struct iio_dev *indio_dev) { struct ds4424_data *data = iio_priv(indio_dev); + const struct regmap_config *regmap_config; + u8 vals[DS4424_MAX_DAC_CHANNELS]; int ret; - mutex_lock(&data->lock); - ret = i2c_smbus_read_byte_data(data->client, DS4424_DAC_ADDR(channel)); - if (ret < 0) - goto fail; + if (indio_dev->num_channels == DS4424_MAX_DAC_CHANNELS) + regmap_config = &ds44x4_regmap_config; + else + regmap_config = &ds44x2_regmap_config; - *val = ret; + data->regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(&client->dev, PTR_ERR(data->regmap), + "Failed to init regmap.\n"); -fail: - mutex_unlock(&data->lock); - return ret; -} + /* + * Prime the cache with the bootloader's configuration. + * regmap_bulk_read() will automatically populate the cache with + * the values read from the hardware. + */ + ret = regmap_bulk_read(data->regmap, DS4424_DAC_ADDR(0), vals, + indio_dev->num_channels); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to read hardware values\n"); -static int ds4424_set_value(struct iio_dev *indio_dev, - int val, struct iio_chan_spec const *chan) -{ - struct ds4424_data *data = iio_priv(indio_dev); - int ret; - - mutex_lock(&data->lock); - ret = i2c_smbus_write_byte_data(data->client, - DS4424_DAC_ADDR(chan->channel), val); - if (ret < 0) - goto fail; - - data->raw[chan->channel] = val; - -fail: - mutex_unlock(&data->lock); - return ret; + return 0; } static int ds4424_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - union ds4424_raw_data raw; + struct ds4424_data *data = iio_priv(indio_dev); + unsigned int regval; int ret; switch (mask) { case IIO_CHAN_INFO_RAW: - ret = ds4424_get_value(indio_dev, val, chan->channel); + ret = regmap_read(data->regmap, DS4424_DAC_ADDR(chan->channel), + ®val); if (ret < 0) { - pr_err("%s : ds4424_get_value returned %d\n", - __func__, ret); + dev_err_ratelimited(indio_dev->dev.parent, + "Failed to read channel %d: %pe\n", + chan->channel, ERR_PTR(ret)); return ret; } - raw.bits = *val; - *val = raw.dx; - if (raw.source_bit == DS4424_SINK_I) + + *val = regval & data->chip_info->result_mask; + if (!(regval & DS4424_DAC_SOURCE)) *val = -*val; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (!data->has_rfs) + return -EINVAL; + + /* SCALE is mA/step: mV / Ohm = mA. */ + *val = data->chip_info->vref_mV; + *val2 = data->rfs_ohms[chan->channel] * + data->chip_info->scale_denom; + return IIO_VAL_FRACTIONAL; default: return -EINVAL; @@ -133,107 +221,131 @@ static int ds4424_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - union ds4424_raw_data raw; + struct ds4424_data *data = iio_priv(indio_dev); + unsigned int abs_val; if (val2 != 0) return -EINVAL; switch (mask) { case IIO_CHAN_INFO_RAW: - if (val <= S8_MIN || val > S8_MAX) + abs_val = abs(val); + if (abs_val > data->chip_info->result_mask) return -EINVAL; - if (val > 0) { - raw.source_bit = DS4424_SOURCE_I; - raw.dx = val; - } else { - raw.source_bit = DS4424_SINK_I; - raw.dx = -val; - } + /* + * Currents exiting the IC (Source) are positive. 0 is a valid + * value for no current flow; the direction bit (Source vs Sink) + * is treated as don't-care by the hardware at 0. + */ + if (val > 0) + abs_val |= DS4424_DAC_SOURCE; - return ds4424_set_value(indio_dev, raw.bits, chan); + return regmap_write(data->regmap, DS4424_DAC_ADDR(chan->channel), + abs_val); default: return -EINVAL; } } -static int ds4424_verify_chip(struct iio_dev *indio_dev) +static int ds4424_parse_rfs(struct i2c_client *client, + struct ds4424_data *data, + struct iio_dev *indio_dev) { - int ret, val; + struct device *dev = &client->dev; + int count, ret; - ret = ds4424_get_value(indio_dev, &val, 0); - if (ret < 0) - dev_err(&indio_dev->dev, - "%s failed. ret: %d\n", __func__, ret); + if (!device_property_present(dev, "maxim,rfs-ohms")) + return 0; - return ret; + count = device_property_count_u32(dev, "maxim,rfs-ohms"); + if (count < 0) + return dev_err_probe(dev, count, "Failed to count maxim,rfs-ohms entries\n"); + if (count != indio_dev->num_channels) + return dev_err_probe(dev, -EINVAL, "maxim,rfs-ohms must have %u entries\n", + indio_dev->num_channels); + + ret = device_property_read_u32_array(dev, "maxim,rfs-ohms", + data->rfs_ohms, + indio_dev->num_channels); + if (ret) + return dev_err_probe(dev, ret, "Failed to read maxim,rfs-ohms property\n"); + + for (unsigned int i = 0; i < indio_dev->num_channels; i++) { + if (!data->rfs_ohms[i]) + return dev_err_probe(dev, -EINVAL, "maxim,rfs-ohms entry %u is zero\n", i); + } + + data->has_rfs = true; + + return 0; } static int ds4424_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ds4424_data *data = iio_priv(indio_dev); - int ret = 0; - int i; + u8 zero_buf[DS4424_MAX_DAC_CHANNELS] = { }; + int ret; - for (i = 0; i < indio_dev->num_channels; i++) { - data->save[i] = data->raw[i]; - ret = ds4424_set_value(indio_dev, 0, - &indio_dev->channels[i]); - if (ret < 0) - return ret; + /* Disable all outputs, bypass cache so the '0' isn't saved */ + regcache_cache_bypass(data->regmap, true); + ret = regmap_bulk_write(data->regmap, DS4424_DAC_ADDR(0), + zero_buf, indio_dev->num_channels); + regcache_cache_bypass(data->regmap, false); + if (ret) { + dev_err(dev, "Failed to zero outputs: %pe\n", ERR_PTR(ret)); + return ret; } - return ret; + + regcache_cache_only(data->regmap, true); + regcache_mark_dirty(data->regmap); + + return 0; } static int ds4424_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ds4424_data *data = iio_priv(indio_dev); - int ret = 0; - int i; - for (i = 0; i < indio_dev->num_channels; i++) { - ret = ds4424_set_value(indio_dev, data->save[i], - &indio_dev->channels[i]); - if (ret < 0) - return ret; - } - return ret; + regcache_cache_only(data->regmap, false); + return regcache_sync(data->regmap); } static DEFINE_SIMPLE_DEV_PM_OPS(ds4424_pm_ops, ds4424_suspend, ds4424_resume); -static const struct iio_info ds4424_info = { +static const struct iio_info ds4424_iio_info = { .read_raw = ds4424_read_raw, .write_raw = ds4424_write_raw, }; static int ds4424_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); + const struct ds4424_chip_info *chip_info; struct ds4424_data *data; struct iio_dev *indio_dev; int ret; + chip_info = i2c_get_match_data(client); + if (!chip_info) + return -ENODEV; + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); - data->client = client; - indio_dev->name = id->name; + indio_dev->name = chip_info->name; + data->chip_info = chip_info; data->vcc_reg = devm_regulator_get(&client->dev, "vcc"); if (IS_ERR(data->vcc_reg)) return dev_err_probe(&client->dev, PTR_ERR(data->vcc_reg), "Failed to get vcc-supply regulator.\n"); - mutex_init(&data->lock); ret = regulator_enable(data->vcc_reg); if (ret < 0) { dev_err(&client->dev, @@ -241,28 +353,29 @@ static int ds4424_probe(struct i2c_client *client) return ret; } - usleep_range(1000, 1200); - ret = ds4424_verify_chip(indio_dev); - if (ret < 0) - goto fail; + /* + * The datasheet does not specify a power-up to I2C ready time. + * Maintain the existing conservative 1ms delay to ensure the + * device is ready for communication. + */ + fsleep(1 * USEC_PER_MSEC); - switch (id->driver_data) { - case ID_DS4422: - indio_dev->num_channels = DS4422_MAX_DAC_CHANNELS; - break; - case ID_DS4424: - indio_dev->num_channels = DS4424_MAX_DAC_CHANNELS; - break; - default: - dev_err(&client->dev, - "ds4424: Invalid chip id.\n"); - ret = -ENXIO; - goto fail; - } - - indio_dev->channels = ds4424_channels; + indio_dev->num_channels = chip_info->num_channels; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->info = &ds4424_info; + indio_dev->info = &ds4424_iio_info; + + ret = ds4424_init_regmap(client, indio_dev); + if (ret) + goto fail; + + ret = ds4424_parse_rfs(client, data, indio_dev); + if (ret) + goto fail; + + if (data->has_rfs) + indio_dev->channels = ds4424_channels_with_scale; + else + indio_dev->channels = ds4424_channels; ret = iio_device_register(indio_dev); if (ret < 0) { @@ -288,16 +401,20 @@ static void ds4424_remove(struct i2c_client *client) } static const struct i2c_device_id ds4424_id[] = { - { "ds4422", ID_DS4422 }, - { "ds4424", ID_DS4424 }, + { "ds4402", (kernel_ulong_t)&ds4402_info }, + { "ds4404", (kernel_ulong_t)&ds4404_info }, + { "ds4422", (kernel_ulong_t)&ds4422_info }, + { "ds4424", (kernel_ulong_t)&ds4424_info }, { } }; MODULE_DEVICE_TABLE(i2c, ds4424_id); static const struct of_device_id ds4424_of_match[] = { - { .compatible = "maxim,ds4422" }, - { .compatible = "maxim,ds4424" }, + { .compatible = "maxim,ds4402", .data = &ds4402_info }, + { .compatible = "maxim,ds4404", .data = &ds4404_info }, + { .compatible = "maxim,ds4422", .data = &ds4422_info }, + { .compatible = "maxim,ds4424", .data = &ds4424_info }, { } }; diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index 105f939f7e54..d6a3d290e7a8 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -48,27 +48,6 @@ struct ltc2632_state { int vref_mv; }; -enum ltc2632_supported_device_ids { - ID_LTC2632L12, - ID_LTC2632L10, - ID_LTC2632L8, - ID_LTC2632H12, - ID_LTC2632H10, - ID_LTC2632H8, - ID_LTC2634L12, - ID_LTC2634L10, - ID_LTC2634L8, - ID_LTC2634H12, - ID_LTC2634H10, - ID_LTC2634H8, - ID_LTC2636L12, - ID_LTC2636L10, - ID_LTC2636L8, - ID_LTC2636H12, - ID_LTC2636H10, - ID_LTC2636H8, -}; - static int ltc2632_spi_write(struct spi_device *spi, u8 cmd, u8 addr, u16 val, u8 shift) { @@ -79,8 +58,9 @@ static int ltc2632_spi_write(struct spi_device *spi, * The input shift register is 24 bits wide. * The next four are the command bits, C3 to C0, * followed by the 4-bit DAC address, A3 to A0, and then the - * 12-, 10-, 8-bit data-word. The data-word comprises the 12-, - * 10-, 8-bit input code followed by 4, 6, or 8 don't care bits. + * 16-, 12-, 10-, 8-bit data-word. The data-word comprises the + * 16-, 12-, 10-, 8-bit input code followed by 0, 4, 6, or 8 + * don't care bits. */ data = (cmd << 20) | (addr << 16) | (val << shift); put_unaligned_be24(data, &msg[0]); @@ -206,101 +186,129 @@ static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = { LTC2632_CHANNEL(7, _bits), \ } +static DECLARE_LTC2632_CHANNELS(ltc2632x16, 16); static DECLARE_LTC2632_CHANNELS(ltc2632x12, 12); static DECLARE_LTC2632_CHANNELS(ltc2632x10, 10); static DECLARE_LTC2632_CHANNELS(ltc2632x8, 8); -static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = { - [ID_LTC2632L12] = { - .channels = ltc2632x12_channels, - .num_channels = 2, - .vref_mv = 2500, - }, - [ID_LTC2632L10] = { - .channels = ltc2632x10_channels, - .num_channels = 2, - .vref_mv = 2500, - }, - [ID_LTC2632L8] = { - .channels = ltc2632x8_channels, - .num_channels = 2, - .vref_mv = 2500, - }, - [ID_LTC2632H12] = { - .channels = ltc2632x12_channels, - .num_channels = 2, - .vref_mv = 4096, - }, - [ID_LTC2632H10] = { - .channels = ltc2632x10_channels, - .num_channels = 2, - .vref_mv = 4096, - }, - [ID_LTC2632H8] = { - .channels = ltc2632x8_channels, - .num_channels = 2, - .vref_mv = 4096, - }, - [ID_LTC2634L12] = { - .channels = ltc2632x12_channels, - .num_channels = 4, - .vref_mv = 2500, - }, - [ID_LTC2634L10] = { - .channels = ltc2632x10_channels, - .num_channels = 4, - .vref_mv = 2500, - }, - [ID_LTC2634L8] = { - .channels = ltc2632x8_channels, - .num_channels = 4, - .vref_mv = 2500, - }, - [ID_LTC2634H12] = { - .channels = ltc2632x12_channels, - .num_channels = 4, - .vref_mv = 4096, - }, - [ID_LTC2634H10] = { - .channels = ltc2632x10_channels, - .num_channels = 4, - .vref_mv = 4096, - }, - [ID_LTC2634H8] = { - .channels = ltc2632x8_channels, - .num_channels = 4, - .vref_mv = 4096, - }, - [ID_LTC2636L12] = { - .channels = ltc2632x12_channels, - .num_channels = 8, - .vref_mv = 2500, - }, - [ID_LTC2636L10] = { - .channels = ltc2632x10_channels, - .num_channels = 8, - .vref_mv = 2500, - }, - [ID_LTC2636L8] = { - .channels = ltc2632x8_channels, - .num_channels = 8, - .vref_mv = 2500, - }, - [ID_LTC2636H12] = { - .channels = ltc2632x12_channels, - .num_channels = 8, - .vref_mv = 4096, - }, - [ID_LTC2636H10] = { - .channels = ltc2632x10_channels, - .num_channels = 8, - .vref_mv = 4096, - }, - [ID_LTC2636H8] = { - .channels = ltc2632x8_channels, - .num_channels = 8, - .vref_mv = 4096, - }, +static const struct ltc2632_chip_info ltc2632l12_chip_info = { + .channels = ltc2632x12_channels, + .num_channels = 2, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2632l10_chip_info = { + .channels = ltc2632x10_channels, + .num_channels = 2, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2632l8_chip_info = { + .channels = ltc2632x8_channels, + .num_channels = 2, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2632h12_chip_info = { + .channels = ltc2632x12_channels, + .num_channels = 2, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2632h10_chip_info = { + .channels = ltc2632x10_channels, + .num_channels = 2, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2632h8_chip_info = { + .channels = ltc2632x8_channels, + .num_channels = 2, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2634l12_chip_info = { + .channels = ltc2632x12_channels, + .num_channels = 4, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2634l10_chip_info = { + .channels = ltc2632x10_channels, + .num_channels = 4, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2634l8_chip_info = { + .channels = ltc2632x8_channels, + .num_channels = 4, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2634h12_chip_info = { + .channels = ltc2632x12_channels, + .num_channels = 4, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2634h10_chip_info = { + .channels = ltc2632x10_channels, + .num_channels = 4, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2634h8_chip_info = { + .channels = ltc2632x8_channels, + .num_channels = 4, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2636l12_chip_info = { + .channels = ltc2632x12_channels, + .num_channels = 8, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2636l10_chip_info = { + .channels = ltc2632x10_channels, + .num_channels = 8, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2636l8_chip_info = { + .channels = ltc2632x8_channels, + .num_channels = 8, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2636h12_chip_info = { + .channels = ltc2632x12_channels, + .num_channels = 8, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2636h10_chip_info = { + .channels = ltc2632x10_channels, + .num_channels = 8, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2636h8_chip_info = { + .channels = ltc2632x8_channels, + .num_channels = 8, + .vref_mv = 4096, +}; + +static const struct ltc2632_chip_info ltc2654l16_chip_info = { + .channels = ltc2632x16_channels, + .num_channels = 4, + .vref_mv = 2500, +}; + +static const struct ltc2632_chip_info ltc2654h16_chip_info = { + .channels = ltc2632x16_channels, + .num_channels = 4, + .vref_mv = 4096, }; static int ltc2632_probe(struct spi_device *spi) @@ -354,84 +362,53 @@ static int ltc2632_probe(struct spi_device *spi) } static const struct spi_device_id ltc2632_id[] = { - { "ltc2632-l12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L12] }, - { "ltc2632-l10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L10] }, - { "ltc2632-l8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L8] }, - { "ltc2632-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H12] }, - { "ltc2632-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H10] }, - { "ltc2632-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H8] }, - { "ltc2634-l12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634L12] }, - { "ltc2634-l10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634L10] }, - { "ltc2634-l8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634L8] }, - { "ltc2634-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634H12] }, - { "ltc2634-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634H10] }, - { "ltc2634-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634H8] }, - { "ltc2636-l12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L12] }, - { "ltc2636-l10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L10] }, - { "ltc2636-l8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L8] }, - { "ltc2636-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H12] }, - { "ltc2636-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H10] }, - { "ltc2636-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H8] }, + { "ltc2632-l12", (kernel_ulong_t)<c2632l12_chip_info }, + { "ltc2632-l10", (kernel_ulong_t)<c2632l10_chip_info }, + { "ltc2632-l8", (kernel_ulong_t)<c2632l8_chip_info }, + { "ltc2632-h12", (kernel_ulong_t)<c2632h12_chip_info }, + { "ltc2632-h10", (kernel_ulong_t)<c2632h10_chip_info }, + { "ltc2632-h8", (kernel_ulong_t)<c2632h8_chip_info }, + { "ltc2634-l12", (kernel_ulong_t)<c2634l12_chip_info }, + { "ltc2634-l10", (kernel_ulong_t)<c2634l10_chip_info }, + { "ltc2634-l8", (kernel_ulong_t)<c2634l8_chip_info }, + { "ltc2634-h12", (kernel_ulong_t)<c2634h12_chip_info }, + { "ltc2634-h10", (kernel_ulong_t)<c2634h10_chip_info }, + { "ltc2634-h8", (kernel_ulong_t)<c2634h8_chip_info }, + { "ltc2636-l12", (kernel_ulong_t)<c2636l12_chip_info }, + { "ltc2636-l10", (kernel_ulong_t)<c2636l10_chip_info }, + { "ltc2636-l8", (kernel_ulong_t)<c2636l8_chip_info }, + { "ltc2636-h12", (kernel_ulong_t)<c2636h12_chip_info }, + { "ltc2636-h10", (kernel_ulong_t)<c2636h10_chip_info }, + { "ltc2636-h8", (kernel_ulong_t)<c2636h8_chip_info }, + { "ltc2654-l16", (kernel_ulong_t)<c2654l16_chip_info }, + { "ltc2654-l12", (kernel_ulong_t)<c2634l12_chip_info }, + { "ltc2654-h16", (kernel_ulong_t)<c2654h16_chip_info }, + { "ltc2654-h12", (kernel_ulong_t)<c2634h12_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ltc2632_id); static const struct of_device_id ltc2632_of_match[] = { - { - .compatible = "lltc,ltc2632-l12", - .data = <c2632_chip_info_tbl[ID_LTC2632L12] - }, { - .compatible = "lltc,ltc2632-l10", - .data = <c2632_chip_info_tbl[ID_LTC2632L10] - }, { - .compatible = "lltc,ltc2632-l8", - .data = <c2632_chip_info_tbl[ID_LTC2632L8] - }, { - .compatible = "lltc,ltc2632-h12", - .data = <c2632_chip_info_tbl[ID_LTC2632H12] - }, { - .compatible = "lltc,ltc2632-h10", - .data = <c2632_chip_info_tbl[ID_LTC2632H10] - }, { - .compatible = "lltc,ltc2632-h8", - .data = <c2632_chip_info_tbl[ID_LTC2632H8] - }, { - .compatible = "lltc,ltc2634-l12", - .data = <c2632_chip_info_tbl[ID_LTC2634L12] - }, { - .compatible = "lltc,ltc2634-l10", - .data = <c2632_chip_info_tbl[ID_LTC2634L10] - }, { - .compatible = "lltc,ltc2634-l8", - .data = <c2632_chip_info_tbl[ID_LTC2634L8] - }, { - .compatible = "lltc,ltc2634-h12", - .data = <c2632_chip_info_tbl[ID_LTC2634H12] - }, { - .compatible = "lltc,ltc2634-h10", - .data = <c2632_chip_info_tbl[ID_LTC2634H10] - }, { - .compatible = "lltc,ltc2634-h8", - .data = <c2632_chip_info_tbl[ID_LTC2634H8] - }, { - .compatible = "lltc,ltc2636-l12", - .data = <c2632_chip_info_tbl[ID_LTC2636L12] - }, { - .compatible = "lltc,ltc2636-l10", - .data = <c2632_chip_info_tbl[ID_LTC2636L10] - }, { - .compatible = "lltc,ltc2636-l8", - .data = <c2632_chip_info_tbl[ID_LTC2636L8] - }, { - .compatible = "lltc,ltc2636-h12", - .data = <c2632_chip_info_tbl[ID_LTC2636H12] - }, { - .compatible = "lltc,ltc2636-h10", - .data = <c2632_chip_info_tbl[ID_LTC2636H10] - }, { - .compatible = "lltc,ltc2636-h8", - .data = <c2632_chip_info_tbl[ID_LTC2636H8] - }, + { .compatible = "lltc,ltc2632-l12", .data = <c2632l12_chip_info }, + { .compatible = "lltc,ltc2632-l10", .data = <c2632l10_chip_info }, + { .compatible = "lltc,ltc2632-l8", .data = <c2632l8_chip_info }, + { .compatible = "lltc,ltc2632-h12", .data = <c2632h12_chip_info }, + { .compatible = "lltc,ltc2632-h10", .data = <c2632h10_chip_info }, + { .compatible = "lltc,ltc2632-h8", .data = <c2632h8_chip_info }, + { .compatible = "lltc,ltc2634-l12", .data = <c2634l12_chip_info }, + { .compatible = "lltc,ltc2634-l10", .data = <c2634l10_chip_info }, + { .compatible = "lltc,ltc2634-l8", .data = <c2634l8_chip_info }, + { .compatible = "lltc,ltc2634-h12", .data = <c2634h12_chip_info }, + { .compatible = "lltc,ltc2634-h10", .data = <c2634h10_chip_info }, + { .compatible = "lltc,ltc2634-h8", .data = <c2634h8_chip_info }, + { .compatible = "lltc,ltc2636-l12", .data = <c2636l12_chip_info }, + { .compatible = "lltc,ltc2636-l10", .data = <c2636l10_chip_info }, + { .compatible = "lltc,ltc2636-l8", .data = <c2636l8_chip_info }, + { .compatible = "lltc,ltc2636-h12", .data = <c2636h12_chip_info }, + { .compatible = "lltc,ltc2636-h10", .data = <c2636h10_chip_info }, + { .compatible = "lltc,ltc2636-h8", .data = <c2636h8_chip_info }, + { .compatible = "lltc,ltc2654-l16", .data = <c2654l16_chip_info }, + { .compatible = "lltc,ltc2654-h16", .data = <c2654h16_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ltc2632_of_match); @@ -447,5 +424,5 @@ static struct spi_driver ltc2632_driver = { module_spi_driver(ltc2632_driver); MODULE_AUTHOR("Maxime Roussin-Belanger "); -MODULE_DESCRIPTION("LTC2632 DAC SPI driver"); +MODULE_DESCRIPTION("LTC2632 and similar DAC SPI driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c index 1b8fe6b8d26e..b52a9cc1da79 100644 --- a/drivers/iio/dac/max5522.c +++ b/drivers/iio/dac/max5522.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -34,7 +35,7 @@ struct max5522_state { struct regmap *regmap; const struct max5522_chip_info *chip_info; unsigned short dac_cache[2]; - struct regulator *vrefin_reg; + int vref_mV; }; #define MAX5522_CHANNEL(chan) { \ @@ -79,17 +80,13 @@ static int max5522_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long info) { struct max5522_state *state = iio_priv(indio_dev); - int ret; switch (info) { case IIO_CHAN_INFO_RAW: *val = state->dac_cache[chan->channel]; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - ret = regulator_get_voltage(state->vrefin_reg); - if (ret < 0) - return -EINVAL; - *val = ret / 1000; + *val = state->vref_mV; *val2 = 10; return IIO_VAL_FRACTIONAL_LOG2; default: @@ -147,16 +144,11 @@ static int max5522_spi_probe(struct spi_device *spi) if (!state->chip_info) return -EINVAL; - state->vrefin_reg = devm_regulator_get(&spi->dev, "vrefin"); - if (IS_ERR(state->vrefin_reg)) - return dev_err_probe(&spi->dev, PTR_ERR(state->vrefin_reg), - "Vrefin regulator not specified\n"); - - ret = regulator_enable(state->vrefin_reg); - if (ret) { + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vrefin"); + if (ret < 0) return dev_err_probe(&spi->dev, ret, - "Failed to enable vref regulators\n"); - } + "Failed to get vrefin regulator\n"); + state->vref_mV = ret / (MICRO / MILLI); state->regmap = devm_regmap_init_spi(spi, &max5522_regmap_config); diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c index bdc3f94aef98..455d61fc3f13 100644 --- a/drivers/iio/dac/ti-dac5571.c +++ b/drivers/iio/dac/ti-dac5571.c @@ -45,7 +45,6 @@ static const struct dac5571_spec dac5571_spec[] = { struct dac5571_data { struct i2c_client *client; - int id; struct mutex lock; struct regulator *vref; u16 val[4]; diff --git a/drivers/iio/filter/admv8818.c b/drivers/iio/filter/admv8818.c index 19f823446cda..a4984b867248 100644 --- a/drivers/iio/filter/admv8818.c +++ b/drivers/iio/filter/admv8818.c @@ -657,56 +657,49 @@ static void admv8818_clk_disable(void *data) static int admv8818_init(struct admv8818_state *st) { int ret; - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; unsigned int chip_id; ret = regmap_write(st->regmap, ADMV8818_REG_SPI_CONFIG_A, ADMV8818_SOFTRESET_N_MSK | ADMV8818_SOFTRESET_MSK); - if (ret) { - dev_err(&spi->dev, "ADMV8818 Soft Reset failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "ADMV8818 Soft Reset failed.\n"); ret = regmap_write(st->regmap, ADMV8818_REG_SPI_CONFIG_A, ADMV8818_SDOACTIVE_N_MSK | ADMV8818_SDOACTIVE_MSK); - if (ret) { - dev_err(&spi->dev, "ADMV8818 SDO Enable failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "ADMV8818 SDO Enable failed.\n"); ret = regmap_read(st->regmap, ADMV8818_REG_CHIPTYPE, &chip_id); - if (ret) { - dev_err(&spi->dev, "ADMV8818 Chip ID read failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "ADMV8818 Chip ID read failed.\n"); - if (chip_id != 0x1) { - dev_err(&spi->dev, "ADMV8818 Invalid Chip ID.\n"); - return -EINVAL; - } + if (chip_id != 0x1) + return dev_err_probe(dev, -EINVAL, + "ADMV8818 Invalid Chip ID.\n"); ret = regmap_update_bits(st->regmap, ADMV8818_REG_SPI_CONFIG_B, ADMV8818_SINGLE_INSTRUCTION_MSK, FIELD_PREP(ADMV8818_SINGLE_INSTRUCTION_MSK, 1)); - if (ret) { - dev_err(&spi->dev, "ADMV8818 Single Instruction failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "ADMV8818 Single Instruction failed.\n"); if (st->clkin) return admv8818_rfin_band_select(st); - else - return 0; + + return 0; } static int admv8818_clk_setup(struct admv8818_state *st) { - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; int ret; - st->clkin = devm_clk_get_optional(&spi->dev, "rf_in"); + st->clkin = devm_clk_get_optional(dev, "rf_in"); if (IS_ERR(st->clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + return dev_err_probe(dev, PTR_ERR(st->clkin), "failed to get the input clock\n"); else if (!st->clkin) return 0; @@ -715,7 +708,7 @@ static int admv8818_clk_setup(struct admv8818_state *st) if (ret) return ret; - ret = devm_add_action_or_reset(&spi->dev, admv8818_clk_disable, st); + ret = devm_add_action_or_reset(dev, admv8818_clk_disable, st); if (ret) return ret; @@ -724,16 +717,16 @@ static int admv8818_clk_setup(struct admv8818_state *st) if (ret < 0) return ret; - return devm_add_action_or_reset(&spi->dev, admv8818_clk_notifier_unreg, st); + return devm_add_action_or_reset(dev, admv8818_clk_notifier_unreg, st); } static int admv8818_read_properties(struct admv8818_state *st) { - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; u32 mhz; int ret; - ret = device_property_read_u32(&spi->dev, "adi,lpf-margin-mhz", &mhz); + ret = device_property_read_u32(dev, "adi,lpf-margin-mhz", &mhz); if (ret == 0) st->lpf_margin_hz = (u64)mhz * HZ_PER_MHZ; else if (ret == -EINVAL) @@ -742,7 +735,7 @@ static int admv8818_read_properties(struct admv8818_state *st) return ret; - ret = device_property_read_u32(&spi->dev, "adi,hpf-margin-mhz", &mhz); + ret = device_property_read_u32(dev, "adi,hpf-margin-mhz", &mhz); if (ret == 0) st->hpf_margin_hz = (u64)mhz * HZ_PER_MHZ; else if (ret == -EINVAL) @@ -758,9 +751,10 @@ static int admv8818_probe(struct spi_device *spi) struct iio_dev *indio_dev; struct regmap *regmap; struct admv8818_state *st; + struct device *dev = &spi->dev; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -792,7 +786,7 @@ static int admv8818_probe(struct spi_device *spi) if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id admv8818_id[] = { diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index 63c485e9e44c..ea4d2763564a 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -167,9 +167,9 @@ /* AD9523_CHANNEL_CLOCK_DIST */ #define AD9523_CLK_DIST_DIV_PHASE(x) (((x) & 0x3F) << 18) -#define AD9523_CLK_DIST_DIV_PHASE_REV(x) ((ret >> 18) & 0x3F) +#define AD9523_CLK_DIST_DIV_PHASE_REV(x) (((x) >> 18) & 0x3F) #define AD9523_CLK_DIST_DIV(x) ((((x) - 1) & 0x3FF) << 8) -#define AD9523_CLK_DIST_DIV_REV(x) (((ret >> 8) & 0x3FF) + 1) +#define AD9523_CLK_DIST_DIV_REV(x) ((((x) >> 8) & 0x3FF) + 1) #define AD9523_CLK_DIST_INV_DIV_OUTPUT_EN (1 << 7) #define AD9523_CLK_DIST_IGNORE_SYNC_EN (1 << 6) #define AD9523_CLK_DIST_PWR_DOWN_EN (1 << 5) @@ -558,55 +558,35 @@ static ssize_t ad9523_show(struct device *dev, return ret; } -static IIO_DEVICE_ATTR(pll1_locked, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_PLL1_LD); +static IIO_DEVICE_ATTR(pll1_locked, 0444, ad9523_show, NULL, + AD9523_STAT_PLL1_LD); -static IIO_DEVICE_ATTR(pll2_locked, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_PLL2_LD); +static IIO_DEVICE_ATTR(pll2_locked, 0444, ad9523_show, NULL, + AD9523_STAT_PLL2_LD); -static IIO_DEVICE_ATTR(pll1_reference_clk_a_present, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_REFA); +static IIO_DEVICE_ATTR(pll1_reference_clk_a_present, 0444, ad9523_show, NULL, + AD9523_STAT_REFA); -static IIO_DEVICE_ATTR(pll1_reference_clk_b_present, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_REFB); +static IIO_DEVICE_ATTR(pll1_reference_clk_b_present, 0444, ad9523_show, NULL, + AD9523_STAT_REFB); -static IIO_DEVICE_ATTR(pll1_reference_clk_test_present, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_REF_TEST); +static IIO_DEVICE_ATTR(pll1_reference_clk_test_present, 0444, ad9523_show, NULL, + AD9523_STAT_REF_TEST); -static IIO_DEVICE_ATTR(vcxo_clk_present, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_VCXO); +static IIO_DEVICE_ATTR(vcxo_clk_present, 0444, ad9523_show, NULL, + AD9523_STAT_VCXO); -static IIO_DEVICE_ATTR(pll2_feedback_clk_present, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_PLL2_FB_CLK); +static IIO_DEVICE_ATTR(pll2_feedback_clk_present, 0444, ad9523_show, NULL, + AD9523_STAT_PLL2_FB_CLK); -static IIO_DEVICE_ATTR(pll2_reference_clk_present, S_IRUGO, - ad9523_show, - NULL, - AD9523_STAT_PLL2_REF_CLK); +static IIO_DEVICE_ATTR(pll2_reference_clk_present, 0444, ad9523_show, NULL, + AD9523_STAT_PLL2_REF_CLK); -static IIO_DEVICE_ATTR(sync_dividers, S_IWUSR, - NULL, - ad9523_store, - AD9523_SYNC); +static IIO_DEVICE_ATTR(sync_dividers, 0200, NULL, ad9523_store, + AD9523_SYNC); -static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR, - NULL, - ad9523_store, - AD9523_EEPROM); +static IIO_DEVICE_ATTR(store_eeprom, 0200, NULL, ad9523_store, + AD9523_EEPROM); static struct attribute *ad9523_attributes[] = { &iio_dev_attr_sync_dividers.dev_attr.attr, @@ -797,8 +777,7 @@ static int ad9523_setup(struct iio_dev *indio_dev) return ret; ret = ad9523_write(indio_dev, AD9523_PLL1_CHARGE_PUMP_CTRL, - AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(pdata-> - pll1_charge_pump_current_nA) | + AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(pdata->pll1_charge_pump_current_nA) | AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL | AD9523_PLL1_BACKLASH_PW_MIN); if (ret < 0) @@ -842,8 +821,7 @@ static int ad9523_setup(struct iio_dev *indio_dev) */ ret = ad9523_write(indio_dev, AD9523_PLL2_CHARGE_PUMP, - AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(pdata-> - pll2_charge_pump_current_nA)); + AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(pdata->pll2_charge_pump_current_nA)); if (ret < 0) return ret; @@ -970,17 +948,17 @@ static int ad9523_setup(struct iio_dev *indio_dev) static int ad9523_probe(struct spi_device *spi) { - struct ad9523_platform_data *pdata = dev_get_platdata(&spi->dev); + struct device *dev = &spi->dev; + struct ad9523_platform_data *pdata; struct iio_dev *indio_dev; struct ad9523_state *st; int ret; - if (!pdata) { - dev_err(&spi->dev, "no platform data?\n"); - return -EINVAL; - } + pdata = dev_get_platdata(dev); + if (!pdata) + return dev_err_probe(dev, -EINVAL, "no platform data?\n"); - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; @@ -988,16 +966,16 @@ static int ad9523_probe(struct spi_device *spi) mutex_init(&st->lock); - ret = devm_regulator_get_enable(&spi->dev, "vcc"); + ret = devm_regulator_get_enable(dev, "vcc"); if (ret) return ret; - st->pwrdown_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown", + st->pwrdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); if (IS_ERR(st->pwrdown_gpio)) return PTR_ERR(st->pwrdown_gpio); - st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", + st->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(st->reset_gpio)) return PTR_ERR(st->reset_gpio); @@ -1007,7 +985,7 @@ static int ad9523_probe(struct spi_device *spi) gpiod_direction_output(st->reset_gpio, 1); } - st->sync_gpio = devm_gpiod_get_optional(&spi->dev, "sync", + st->sync_gpio = devm_gpiod_get_optional(dev, "sync", GPIOD_OUT_HIGH); if (IS_ERR(st->sync_gpio)) return PTR_ERR(st->sync_gpio); @@ -1027,7 +1005,7 @@ static int ad9523_probe(struct spi_device *spi) if (ret < 0) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id ad9523_id[] = { diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index ed1741165f55..6bbb6a8dd9d0 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -152,10 +152,10 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) st->r4_rf_div_sel = 0; /* - * !\TODO: The below computation is making sure we get a power of 2 - * shift (st->r4_rf_div_sel) so that freq becomes higher or equal to - * ADF4350_MIN_VCO_FREQ. This might be simplified with fls()/fls_long() - * and friends. + * NOTE: This iteratively shifts freq by a power of 2 + * (st->r4_rf_div_sel) to meet or exceed ADF4350_MIN_VCO_FREQ. + * A constant-time approach using fls_long() was attempted but + * deemed more complex without meaningful benefit for init code. */ while (freq < ADF4350_MIN_VCO_FREQ) { freq <<= 1; @@ -607,7 +607,7 @@ static int adf4350_probe(struct spi_device *spi) if (dev_fwnode(&spi->dev)) { pdata = adf4350_parse_dt(&spi->dev); if (pdata == NULL) - return -EINVAL; + return -ENOMEM; } else { pdata = dev_get_platdata(&spi->dev); } diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c index 8e2da218d48a..ff6077e29c73 100644 --- a/drivers/iio/frequency/adf4377.c +++ b/drivers/iio/frequency/adf4377.c @@ -706,23 +706,20 @@ static void adf4377_gpio_init(struct adf4377_state *st) static int adf4377_init(struct adf4377_state *st) { - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; int ret; adf4377_gpio_init(st); ret = adf4377_soft_reset(st); - if (ret) { - dev_err(&spi->dev, "Failed to soft reset.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to soft reset.\n"); ret = regmap_multi_reg_write(st->regmap, adf4377_reg_defaults, ARRAY_SIZE(adf4377_reg_defaults)); - if (ret) { - dev_err(&spi->dev, "Failed to set default registers.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Failed to set default registers.\n"); ret = regmap_update_bits(st->regmap, 0x00, ADF4377_0000_SDO_ACTIVE_MSK | ADF4377_0000_SDO_ACTIVE_R_MSK, @@ -730,10 +727,9 @@ static int adf4377_init(struct adf4377_state *st) ADF4377_0000_SDO_ACTIVE_SPI_4W) | FIELD_PREP(ADF4377_0000_SDO_ACTIVE_R_MSK, ADF4377_0000_SDO_ACTIVE_SPI_4W)); - if (ret) { - dev_err(&spi->dev, "Failed to set 4-Wire Operation.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Failed to set 4-Wire Operation.\n"); st->clkin_freq = clk_get_rate(st->clkin); @@ -747,10 +743,9 @@ static int adf4377_init(struct adf4377_state *st) FIELD_PREP(ADF4377_001A_PD_PFDCP_MSK, 0) | FIELD_PREP(ADF4377_001A_PD_CLKOUT1_MSK, 0) | FIELD_PREP(ADF4377_001A_PD_CLKOUT2_MSK, 0)); - if (ret) { - dev_err(&spi->dev, "Failed to set power down registers.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Failed to set power down registers.\n"); /* Set Mux Output */ ret = regmap_update_bits(st->regmap, 0x1D, @@ -882,35 +877,35 @@ static const struct iio_chan_spec adf4377_channels[] = { static int adf4377_properties_parse(struct adf4377_state *st) { - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; int ret; - st->clkin = devm_clk_get_enabled(&spi->dev, "ref_in"); + st->clkin = devm_clk_get_enabled(dev, "ref_in"); if (IS_ERR(st->clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + return dev_err_probe(dev, PTR_ERR(st->clkin), "failed to get the reference input clock\n"); - st->gpio_ce = devm_gpiod_get_optional(&st->spi->dev, "chip-enable", + st->gpio_ce = devm_gpiod_get_optional(dev, "chip-enable", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_ce)) - return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_ce), + return dev_err_probe(dev, PTR_ERR(st->gpio_ce), "failed to get the CE GPIO\n"); - st->gpio_enclk1 = devm_gpiod_get_optional(&st->spi->dev, "clk1-enable", + st->gpio_enclk1 = devm_gpiod_get_optional(dev, "clk1-enable", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_enclk1)) - return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_enclk1), + return dev_err_probe(dev, PTR_ERR(st->gpio_enclk1), "failed to get the CE GPIO\n"); if (st->chip_info->has_gpio_enclk2) { - st->gpio_enclk2 = devm_gpiod_get_optional(&st->spi->dev, "clk2-enable", + st->gpio_enclk2 = devm_gpiod_get_optional(dev, "clk2-enable", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_enclk2)) - return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_enclk2), + return dev_err_probe(dev, PTR_ERR(st->gpio_enclk2), "failed to get the CE GPIO\n"); } - ret = device_property_match_property_string(&spi->dev, "adi,muxout-select", + ret = device_property_match_property_string(dev, "adi,muxout-select", adf4377_muxout_modes, ARRAY_SIZE(adf4377_muxout_modes)); if (ret >= 0) @@ -1055,9 +1050,10 @@ static int adf4377_probe(struct spi_device *spi) struct iio_dev *indio_dev; struct regmap *regmap; struct adf4377_state *st; + struct device *dev = &spi->dev; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -1080,7 +1076,7 @@ static int adf4377_probe(struct spi_device *spi) return ret; st->nb.notifier_call = adf4377_freq_change; - ret = devm_clk_notifier_register(&spi->dev, st->clkin, &st->nb); + ret = devm_clk_notifier_register(dev, st->clkin, &st->nb); if (ret) return ret; @@ -1097,7 +1093,7 @@ static int adf4377_probe(struct spi_device *spi) indio_dev->num_channels = ARRAY_SIZE(adf4377_channels); } - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id adf4377_id[] = { diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index d8e8d541990f..b852378b3f68 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -85,9 +85,9 @@ enum { }; enum { - ADMV1013_SE_MODE_POS = 6, - ADMV1013_SE_MODE_NEG = 9, - ADMV1013_SE_MODE_DIFF = 12 + ADMV1013_SE_MODE_POS, + ADMV1013_SE_MODE_NEG, + ADMV1013_SE_MODE_DIFF, }; struct admv1013_state { @@ -441,7 +441,7 @@ static int admv1013_init(struct admv1013_state *st, int vcm_uv) { int ret; unsigned int data; - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; /* Perform a software reset */ ret = __admv1013_spi_update_bits(st, ADMV1013_REG_SPI_CONTROL, @@ -461,19 +461,30 @@ static int admv1013_init(struct admv1013_state *st, int vcm_uv) return ret; data = FIELD_GET(ADMV1013_CHIP_ID_MSK, data); - if (data != ADMV1013_CHIP_ID) { - dev_err(&spi->dev, "Invalid Chip ID.\n"); - return -EINVAL; - } + if (data != ADMV1013_CHIP_ID) + return dev_err_probe(dev, -EINVAL, "Invalid Chip ID.\n"); ret = __admv1013_spi_write(st, ADMV1013_REG_VVA_TEMP_COMP, 0xE700); if (ret) return ret; - data = FIELD_PREP(ADMV1013_QUAD_SE_MODE_MSK, st->quad_se_mode); + switch (st->quad_se_mode) { + case ADMV1013_SE_MODE_POS: + data = 6; + break; + case ADMV1013_SE_MODE_NEG: + data = 9; + break; + case ADMV1013_SE_MODE_DIFF: + data = 12; + break; + default: + return -EINVAL; + } ret = __admv1013_spi_update_bits(st, ADMV1013_REG_QUAD, - ADMV1013_QUAD_SE_MODE_MSK, data); + ADMV1013_QUAD_SE_MODE_MSK, + FIELD_PREP(ADMV1013_QUAD_SE_MODE_MSK, data)); if (ret) return ret; @@ -514,43 +525,39 @@ static void admv1013_powerdown(void *data) admv1013_spi_update_bits(data, ADMV1013_REG_ENABLE, enable_reg_msk, enable_reg); } +static const char * const admv1013_input_modes[] = { + [ADMV1013_IQ_MODE] = "iq", + [ADMV1013_IF_MODE] = "if", +}; + +static const char * const admv1013_quad_se_modes[] = { + [ADMV1013_SE_MODE_POS] = "se-pos", + [ADMV1013_SE_MODE_NEG] = "se-neg", + [ADMV1013_SE_MODE_DIFF] = "diff", +}; + static int admv1013_properties_parse(struct admv1013_state *st) { int ret; - const char *str; - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; - st->det_en = device_property_read_bool(&spi->dev, "adi,detector-enable"); + st->det_en = device_property_read_bool(dev, "adi,detector-enable"); - ret = device_property_read_string(&spi->dev, "adi,input-mode", &str); - if (ret) - st->input_mode = ADMV1013_IQ_MODE; + ret = device_property_match_property_string(dev, "adi,input-mode", + admv1013_input_modes, + ARRAY_SIZE(admv1013_input_modes)); + st->input_mode = ret >= 0 ? ret : ADMV1013_IQ_MODE; - if (!strcmp(str, "iq")) - st->input_mode = ADMV1013_IQ_MODE; - else if (!strcmp(str, "if")) - st->input_mode = ADMV1013_IF_MODE; - else - return -EINVAL; + ret = device_property_match_property_string(dev, "adi,quad-se-mode", + admv1013_quad_se_modes, + ARRAY_SIZE(admv1013_quad_se_modes)); + st->quad_se_mode = ret >= 0 ? ret : ADMV1013_SE_MODE_DIFF; - ret = device_property_read_string(&spi->dev, "adi,quad-se-mode", &str); - if (ret) - st->quad_se_mode = ADMV1013_SE_MODE_DIFF; - - if (!strcmp(str, "diff")) - st->quad_se_mode = ADMV1013_SE_MODE_DIFF; - else if (!strcmp(str, "se-pos")) - st->quad_se_mode = ADMV1013_SE_MODE_POS; - else if (!strcmp(str, "se-neg")) - st->quad_se_mode = ADMV1013_SE_MODE_NEG; - else - return -EINVAL; - - ret = devm_regulator_bulk_get_enable(&st->spi->dev, + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(admv1013_vcc_regs), admv1013_vcc_regs); if (ret) { - dev_err_probe(&spi->dev, ret, + dev_err_probe(dev, ret, "Failed to request VCC regulators\n"); return ret; } @@ -562,9 +569,10 @@ static int admv1013_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct admv1013_state *st; + struct device *dev = &spi->dev; int ret, vcm_uv; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -581,36 +589,34 @@ static int admv1013_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vcm"); + ret = devm_regulator_get_enable_read_voltage(dev, "vcm"); if (ret < 0) - return dev_err_probe(&spi->dev, ret, + return dev_err_probe(dev, ret, "failed to get the common-mode voltage\n"); vcm_uv = ret; - st->clkin = devm_clk_get_enabled(&spi->dev, "lo_in"); + st->clkin = devm_clk_get_enabled(dev, "lo_in"); if (IS_ERR(st->clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + return dev_err_probe(dev, PTR_ERR(st->clkin), "failed to get the LO input clock\n"); st->nb.notifier_call = admv1013_freq_change; - ret = devm_clk_notifier_register(&spi->dev, st->clkin, &st->nb); + ret = devm_clk_notifier_register(dev, st->clkin, &st->nb); if (ret) return ret; mutex_init(&st->lock); ret = admv1013_init(st, vcm_uv); - if (ret) { - dev_err(&spi->dev, "admv1013 init failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "admv1013 init failed\n"); - ret = devm_add_action_or_reset(&spi->dev, admv1013_powerdown, st); + ret = devm_add_action_or_reset(dev, admv1013_powerdown, st); if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id admv1013_id[] = { diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c index 7a8f92ec80a2..25e8cd8135ad 100644 --- a/drivers/iio/frequency/admv1014.c +++ b/drivers/iio/frequency/admv1014.c @@ -610,15 +610,14 @@ static int admv1014_init(struct admv1014_state *st) { unsigned int chip_id, enable_reg, enable_reg_msk; struct spi_device *spi = st->spi; + struct device *dev = &spi->dev; int ret; ret = regulator_bulk_enable(ADMV1014_NUM_REGULATORS, st->regulators); - if (ret) { - dev_err(&spi->dev, "Failed to enable regulators"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators"); - ret = devm_add_action_or_reset(&spi->dev, admv1014_reg_disable, st->regulators); + ret = devm_add_action_or_reset(dev, admv1014_reg_disable, st->regulators); if (ret) return ret; @@ -626,16 +625,16 @@ static int admv1014_init(struct admv1014_state *st) if (ret) return ret; - ret = devm_add_action_or_reset(&spi->dev, admv1014_clk_disable, st->clkin); + ret = devm_add_action_or_reset(dev, admv1014_clk_disable, st->clkin); if (ret) return ret; st->nb.notifier_call = admv1014_freq_change; - ret = devm_clk_notifier_register(&spi->dev, st->clkin, &st->nb); + ret = devm_clk_notifier_register(dev, st->clkin, &st->nb); if (ret) return ret; - ret = devm_add_action_or_reset(&spi->dev, admv1014_powerdown, st); + ret = devm_add_action_or_reset(dev, admv1014_powerdown, st); if (ret) return ret; @@ -643,55 +642,47 @@ static int admv1014_init(struct admv1014_state *st) ret = __admv1014_spi_update_bits(st, ADMV1014_REG_SPI_CONTROL, ADMV1014_SPI_SOFT_RESET_MSK, FIELD_PREP(ADMV1014_SPI_SOFT_RESET_MSK, 1)); - if (ret) { - dev_err(&spi->dev, "ADMV1014 SPI software reset failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "ADMV1014 SPI software reset failed.\n"); ret = __admv1014_spi_update_bits(st, ADMV1014_REG_SPI_CONTROL, ADMV1014_SPI_SOFT_RESET_MSK, FIELD_PREP(ADMV1014_SPI_SOFT_RESET_MSK, 0)); - if (ret) { - dev_err(&spi->dev, "ADMV1014 SPI software reset disable failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "ADMV1014 SPI software reset disable failed.\n"); ret = __admv1014_spi_write(st, ADMV1014_REG_VVA_TEMP_COMP, 0x727C); - if (ret) { - dev_err(&spi->dev, "Writing default Temperature Compensation value failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Writing default Temperature Compensation value failed.\n"); ret = __admv1014_spi_read(st, ADMV1014_REG_SPI_CONTROL, &chip_id); if (ret) return ret; chip_id = FIELD_GET(ADMV1014_CHIP_ID_MSK, chip_id); - if (chip_id != ADMV1014_CHIP_ID) { - dev_err(&spi->dev, "Invalid Chip ID.\n"); - return -EINVAL; - } + if (chip_id != ADMV1014_CHIP_ID) + return dev_err_probe(dev, -EINVAL, "Invalid Chip ID.\n"); ret = __admv1014_spi_update_bits(st, ADMV1014_REG_QUAD, ADMV1014_QUAD_SE_MODE_MSK, FIELD_PREP(ADMV1014_QUAD_SE_MODE_MSK, st->quad_se_mode)); - if (ret) { - dev_err(&spi->dev, "Writing Quad SE Mode failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Writing Quad SE Mode failed.\n"); ret = admv1014_update_quad_filters(st); - if (ret) { - dev_err(&spi->dev, "Update Quad Filters failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Update Quad Filters failed.\n"); ret = admv1014_update_vcm_settings(st); - if (ret) { - dev_err(&spi->dev, "Update VCM Settings failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Update VCM Settings failed.\n"); enable_reg_msk = ADMV1014_P1DB_COMPENSATION_MSK | ADMV1014_IF_AMP_PD_MSK | @@ -712,13 +703,14 @@ static int admv1014_properties_parse(struct admv1014_state *st) { unsigned int i; struct spi_device *spi = st->spi; + struct device *dev = &spi->dev; int ret; - st->det_en = device_property_read_bool(&spi->dev, "adi,detector-enable"); + st->det_en = device_property_read_bool(dev, "adi,detector-enable"); - st->p1db_comp = device_property_read_bool(&spi->dev, "adi,p1db-compensation-enable"); + st->p1db_comp = device_property_read_bool(dev, "adi,p1db-compensation-enable"); - ret = device_property_match_property_string(&spi->dev, "adi,input-mode", + ret = device_property_match_property_string(dev, "adi,input-mode", input_mode_names, ARRAY_SIZE(input_mode_names)); if (ret >= 0) @@ -726,7 +718,7 @@ static int admv1014_properties_parse(struct admv1014_state *st) else st->input_mode = ADMV1014_IQ_MODE; - ret = device_property_match_property_string(&spi->dev, "adi,quad-se-mode", + ret = device_property_match_property_string(dev, "adi,quad-se-mode", quad_se_mode_names, ARRAY_SIZE(quad_se_mode_names)); if (ret >= 0) @@ -737,16 +729,14 @@ static int admv1014_properties_parse(struct admv1014_state *st) for (i = 0; i < ADMV1014_NUM_REGULATORS; ++i) st->regulators[i].supply = admv1014_reg_name[i]; - ret = devm_regulator_bulk_get(&st->spi->dev, ADMV1014_NUM_REGULATORS, + ret = devm_regulator_bulk_get(dev, ADMV1014_NUM_REGULATORS, st->regulators); - if (ret) { - dev_err(&spi->dev, "Failed to request regulators"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request regulators"); - st->clkin = devm_clk_get(&spi->dev, "lo_in"); + st->clkin = devm_clk_get(dev, "lo_in"); if (IS_ERR(st->clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + return dev_err_probe(dev, PTR_ERR(st->clkin), "failed to get the LO input clock\n"); return 0; @@ -756,9 +746,10 @@ static int admv1014_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct admv1014_state *st; + struct device *dev = &spi->dev; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -787,7 +778,7 @@ static int admv1014_probe(struct spi_device *spi) if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id admv1014_id[] = { diff --git a/drivers/iio/frequency/admv4420.c b/drivers/iio/frequency/admv4420.c index 3ae462b4f5c9..618511eddfb1 100644 --- a/drivers/iio/frequency/admv4420.c +++ b/drivers/iio/frequency/admv4420.c @@ -243,7 +243,7 @@ static int admv4420_calc_parameters(struct admv4420_state *st) st->n_counter.n_counter = 1; } if (!sol_found) - return -1; + return -EINVAL; st->n_counter.int_val = div_u64_rem(st->n_counter.n_counter, 10, &st->n_counter.frac_val); st->n_counter.mod_val = 10; @@ -279,10 +279,9 @@ static int admv4420_setup(struct iio_dev *indio_dev) if (ret) return ret; - if (val != ADMV4420_SCRATCH_PAD_VAL_1) { - dev_err(dev, "Failed ADMV4420 to read/write scratchpad %x ", val); - return -EIO; - } + if (val != ADMV4420_SCRATCH_PAD_VAL_1) + return dev_err_probe(dev, -EIO, + "Failed ADMV4420 to read/write scratchpad %x\n", val); ret = regmap_write(st->regmap, ADMV4420_SCRATCHPAD, @@ -294,10 +293,9 @@ static int admv4420_setup(struct iio_dev *indio_dev) if (ret) return ret; - if (val != ADMV4420_SCRATCH_PAD_VAL_2) { - dev_err(dev, "Failed to read/write scratchpad %x ", val); - return -EIO; - } + if (val != ADMV4420_SCRATCH_PAD_VAL_2) + return dev_err_probe(dev, -EIO, + "Failed to read/write scratchpad %x\n", val); st->mux_sel = ADMV4420_LOCK_DTCT; st->lo_freq_hz = ADMV4420_DEFAULT_LO_FREQ_HZ; @@ -305,10 +303,10 @@ static int admv4420_setup(struct iio_dev *indio_dev) admv4420_fw_parse(st); ret = admv4420_calc_parameters(st); - if (ret) { - dev_err(dev, "Failed calc parameters for %lld ", st->vco_freq_hz); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "Failed calc parameters for %llu\n", + st->vco_freq_hz); ret = regmap_write(st->regmap, ADMV4420_R_DIV_L, FIELD_GET(0xFF, st->ref_block.divider)); @@ -344,18 +342,19 @@ static int admv4420_setup(struct iio_dev *indio_dev) static int admv4420_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct iio_dev *indio_dev; struct admv4420_state *st; struct regmap *regmap; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; regmap = devm_regmap_init_spi(spi, &admv4420_regmap_config); if (IS_ERR(regmap)) - return dev_err_probe(&spi->dev, PTR_ERR(regmap), + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initializing spi regmap\n"); st = iio_priv(indio_dev); @@ -368,12 +367,10 @@ static int admv4420_probe(struct spi_device *spi) indio_dev->num_channels = ARRAY_SIZE(admv4420_channels); ret = admv4420_setup(indio_dev); - if (ret) { - dev_err(&spi->dev, "Setup ADMV4420 failed (%d)\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Setup ADMV4420 failed\n"); - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct of_device_id admv4420_of_match[] = { diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c index a7a21f929970..257fd31a0b0e 100644 --- a/drivers/iio/frequency/adrf6780.c +++ b/drivers/iio/frequency/adrf6780.c @@ -346,23 +346,21 @@ static const struct iio_chan_spec adrf6780_channels[] = { static int adrf6780_reset(struct adrf6780_state *st) { int ret; - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_CONTROL, ADRF6780_SOFT_RESET_MSK, FIELD_PREP(ADRF6780_SOFT_RESET_MSK, 1)); - if (ret) { - dev_err(&spi->dev, "ADRF6780 SPI software reset failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "ADRF6780 SPI software reset failed.\n"); ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_CONTROL, ADRF6780_SOFT_RESET_MSK, FIELD_PREP(ADRF6780_SOFT_RESET_MSK, 0)); - if (ret) { - dev_err(&spi->dev, "ADRF6780 SPI software reset disable failed.\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "ADRF6780 SPI software reset disable failed.\n"); return 0; } @@ -371,7 +369,7 @@ static int adrf6780_init(struct adrf6780_state *st) { int ret; unsigned int chip_id, enable_reg, enable_reg_msk; - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; /* Perform a software reset */ ret = adrf6780_reset(st); @@ -383,10 +381,9 @@ static int adrf6780_init(struct adrf6780_state *st) return ret; chip_id = FIELD_GET(ADRF6780_CHIP_ID_MSK, chip_id); - if (chip_id != ADRF6780_CHIP_ID) { - dev_err(&spi->dev, "ADRF6780 Invalid Chip ID.\n"); - return -EINVAL; - } + if (chip_id != ADRF6780_CHIP_ID) + return dev_err_probe(dev, -EINVAL, + "ADRF6780 Invalid Chip ID.\n"); enable_reg_msk = ADRF6780_VGA_BUFFER_EN_MSK | ADRF6780_DETECTOR_EN_MSK | @@ -426,18 +423,18 @@ static int adrf6780_init(struct adrf6780_state *st) static void adrf6780_properties_parse(struct adrf6780_state *st) { - struct spi_device *spi = st->spi; + struct device *dev = &st->spi->dev; - st->vga_buff_en = device_property_read_bool(&spi->dev, "adi,vga-buff-en"); - st->lo_buff_en = device_property_read_bool(&spi->dev, "adi,lo-buff-en"); - st->if_mode_en = device_property_read_bool(&spi->dev, "adi,if-mode-en"); - st->iq_mode_en = device_property_read_bool(&spi->dev, "adi,iq-mode-en"); - st->lo_x2_en = device_property_read_bool(&spi->dev, "adi,lo-x2-en"); - st->lo_ppf_en = device_property_read_bool(&spi->dev, "adi,lo-ppf-en"); - st->lo_en = device_property_read_bool(&spi->dev, "adi,lo-en"); - st->uc_bias_en = device_property_read_bool(&spi->dev, "adi,uc-bias-en"); - st->lo_sideband = device_property_read_bool(&spi->dev, "adi,lo-sideband"); - st->vdet_out_en = device_property_read_bool(&spi->dev, "adi,vdet-out-en"); + st->vga_buff_en = device_property_read_bool(dev, "adi,vga-buff-en"); + st->lo_buff_en = device_property_read_bool(dev, "adi,lo-buff-en"); + st->if_mode_en = device_property_read_bool(dev, "adi,if-mode-en"); + st->iq_mode_en = device_property_read_bool(dev, "adi,iq-mode-en"); + st->lo_x2_en = device_property_read_bool(dev, "adi,lo-x2-en"); + st->lo_ppf_en = device_property_read_bool(dev, "adi,lo-ppf-en"); + st->lo_en = device_property_read_bool(dev, "adi,lo-en"); + st->uc_bias_en = device_property_read_bool(dev, "adi,uc-bias-en"); + st->lo_sideband = device_property_read_bool(dev, "adi,lo-sideband"); + st->vdet_out_en = device_property_read_bool(dev, "adi,vdet-out-en"); } static void adrf6780_powerdown(void *data) @@ -450,9 +447,10 @@ static int adrf6780_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct adrf6780_state *st; + struct device *dev = &spi->dev; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -467,9 +465,9 @@ static int adrf6780_probe(struct spi_device *spi) adrf6780_properties_parse(st); - st->clkin = devm_clk_get_enabled(&spi->dev, "lo_in"); + st->clkin = devm_clk_get_enabled(dev, "lo_in"); if (IS_ERR(st->clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + return dev_err_probe(dev, PTR_ERR(st->clkin), "failed to get the LO input clock\n"); mutex_init(&st->lock); @@ -478,11 +476,11 @@ static int adrf6780_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_add_action_or_reset(&spi->dev, adrf6780_powerdown, st); + ret = devm_add_action_or_reset(dev, adrf6780_powerdown, st); if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct spi_device_id adrf6780_id[] = { diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index c43990c518f7..c340cc899a7c 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -42,7 +42,7 @@ static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = { HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS }; -static const u32 gryo_3d_sensitivity_addresses[] = { +static const u32 gyro_3d_sensitivity_addresses[] = { HID_USAGE_SENSOR_DATA_ANGL_VELOCITY, }; @@ -297,8 +297,8 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_GYRO_3D, &gyro_state->common_attributes, - gryo_3d_sensitivity_addresses, - ARRAY_SIZE(gryo_3d_sensitivity_addresses)); + gyro_3d_sensitivity_addresses, + ARRAY_SIZE(gyro_3d_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/humidity/hdc2010.c b/drivers/iio/humidity/hdc2010.c index 894a8b4ab193..1a0f18251381 100644 --- a/drivers/iio/humidity/hdc2010.c +++ b/drivers/iio/humidity/hdc2010.c @@ -44,7 +44,6 @@ struct hdc2010_data { struct i2c_client *client; struct mutex lock; u8 measurement_config; - u8 interrupt_config; u8 drdy_config; }; diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c index 6bcb9a436581..f3d499423399 100644 --- a/drivers/iio/imu/bmi323/bmi323_core.c +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -156,7 +156,6 @@ struct bmi323_data { struct iio_mount_matrix orientation; enum bmi323_irq_pin irq_pin; struct iio_trigger *trig; - bool drdy_trigger_enabled; enum bmi323_state state; s64 fifo_tstamp, old_fifo_tstamp; u32 odrns[BMI323_SENSORS_CNT]; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 0ab6eddf0543..532d5fdffaf8 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -1211,7 +1211,7 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev) ssize_t i, size; unsigned int no; const void *accel, *gyro, *timestamp; - const int8_t *temp; + const s8 *temp; unsigned int odr; int64_t ts_val; /* buffer is copied to userspace, zeroing it to avoid any data leak */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c index 6aee6c989485..47394594d17a 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -125,7 +125,7 @@ static int inv_magn_init(struct inv_mpu6050_state *st) } /* - * Sensitivity adjustement and scale to Gauss + * Sensitivity adjustment and scale to Gauss * * Hadj = H * (((ASA - 128) * 0.5 / 128) + 1) * Factor simplification: diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index 7c933218036b..b2a7c2eaf50d 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -144,6 +144,7 @@ MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); static const struct acpi_device_id st_lsm6dsx_i2c_acpi_match[] = { { "SMO8B30", ST_LSM6DS3TRC_ID, }, + { "SMOCF00", ST_LSM6DSO_ID, }, { } }; MODULE_DEVICE_TABLE(acpi, st_lsm6dsx_i2c_acpi_match); diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 447b694d6d5f..10e689f49441 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -56,6 +56,7 @@ struct iio_backend { void *priv; const char *name; unsigned int cached_reg_addr; + u32 caps; /* * This index is relative to the frontend. Meaning that for * frontends with multiple backends, this will be the index of this @@ -774,6 +775,20 @@ int iio_backend_extend_chan_spec(struct iio_backend *back, } EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, "IIO_BACKEND"); +/** + * iio_backend_has_caps - Check if backend has specific capabilities + * @back: Backend device + * @caps: Capabilities to check + * + * RETURNS: + * True if backend has all the requested capabilities, false otherwise. + */ +bool iio_backend_has_caps(struct iio_backend *back, u32 caps) +{ + return (back->caps & caps) == caps; +} +EXPORT_SYMBOL_NS_GPL(iio_backend_has_caps, "IIO_BACKEND"); + static void iio_backend_release(void *arg) { struct iio_backend *back = arg; @@ -952,7 +967,6 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_data_transfer_addr, "IIO_BACKEND"); static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, const char *name, struct fwnode_handle *fwnode) { - struct fwnode_handle *fwnode_back; struct iio_backend *back; unsigned int index; int ret; @@ -967,7 +981,8 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con index = 0; } - fwnode_back = fwnode_find_reference(fwnode, "io-backends", index); + struct fwnode_handle *fwnode_back __free(fwnode_handle) = + fwnode_find_reference(fwnode, "io-backends", index); if (IS_ERR(fwnode_back)) return dev_err_cast_probe(dev, fwnode_back, "Cannot get Firmware reference\n"); @@ -977,7 +992,6 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con if (!device_match_fwnode(back->dev, fwnode_back)) continue; - fwnode_handle_put(fwnode_back); ret = __devm_iio_backend_get(dev, back); if (ret) return ERR_PTR(ret); @@ -988,7 +1002,6 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con return back; } - fwnode_handle_put(fwnode_back); return ERR_PTR(-EPROBE_DEFER); } @@ -1114,6 +1127,7 @@ int devm_iio_backend_register(struct device *dev, back->ops = info->ops; back->name = info->name; + back->caps = info->caps; back->owner = dev->driver->owner; back->dev = dev; back->priv = priv; diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 4149efcd5539..a0d6fcf2a9c9 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -256,6 +256,7 @@ static const char * const iio_ev_info_text[] = { [IIO_EV_INFO_TAP2_MIN_DELAY] = "tap2_min_delay", [IIO_EV_INFO_RUNNING_PERIOD] = "runningperiod", [IIO_EV_INFO_RUNNING_COUNT] = "runningcount", + [IIO_EV_INFO_SCALE] = "scale", }; static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr) diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 7f34fe7bad07..17781c12bc85 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -561,10 +561,6 @@ struct iio_trigger *viio_trigger_alloc(struct device *parent, if (!trig) return NULL; - trig->dev.parent = parent; - trig->dev.type = &iio_trig_type; - trig->dev.bus = &iio_bus_type; - device_initialize(&trig->dev); INIT_WORK(&trig->reenable_work, iio_reenable_work_fn); mutex_init(&trig->pool_lock); @@ -592,6 +588,11 @@ struct iio_trigger *viio_trigger_alloc(struct device *parent, IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE); } + trig->dev.parent = parent; + trig->dev.type = &iio_trig_type; + trig->dev.bus = &iio_bus_type; + device_initialize(&trig->dev); + return trig; free_descs: @@ -634,9 +635,9 @@ void iio_trigger_free(struct iio_trigger *trig) } EXPORT_SYMBOL(iio_trigger_free); -static void devm_iio_trigger_release(struct device *dev, void *res) +static void devm_iio_trigger_release(void *trig) { - iio_trigger_free(*(struct iio_trigger **)res); + iio_trigger_free(trig); } /** @@ -658,24 +659,20 @@ struct iio_trigger *__devm_iio_trigger_alloc(struct device *parent, struct module *this_mod, const char *fmt, ...) { - struct iio_trigger **ptr, *trig; + struct iio_trigger *trig; va_list vargs; - - ptr = devres_alloc(devm_iio_trigger_release, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) - return NULL; + int ret; /* use raw alloc_dr for kmalloc caller tracing */ va_start(vargs, fmt); trig = viio_trigger_alloc(parent, this_mod, fmt, vargs); va_end(vargs); - if (trig) { - *ptr = trig; - devres_add(parent, ptr); - } else { - devres_free(ptr); - } + if (!trig) + return NULL; + + ret = devm_add_action_or_reset(parent, devm_iio_trigger_release, trig); + if (ret) + return NULL; return trig; } diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index ac1408d374c9..eff33e456c70 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -359,7 +359,7 @@ config ROHM_BU27034 select IIO_KFIFO_BUF help Enable support for the ROHM BU27034 ambient light sensor. ROHM BU27034 - is an ambient light sesnor with 3 channels and 3 photo diodes capable + is an ambient light sensor with 3 channels and 3 photo diodes capable of detecting a very wide range of illuminance. Typical application is adjusting LCD and backlight power of TVs and mobile phones. diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index d5d1a8b9c035..ab229318dce9 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -90,9 +91,9 @@ static int acpi_als_read_value(struct acpi_als *als, char *prop, s32 *val) return 0; } -static void acpi_als_notify(struct acpi_device *device, u32 event) +static void acpi_als_notify(acpi_handle handle, u32 event, void *data) { - struct iio_dev *indio_dev = acpi_driver_data(device); + struct iio_dev *indio_dev = data; struct acpi_als *als = iio_priv(indio_dev); if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) { @@ -102,7 +103,7 @@ static void acpi_als_notify(struct acpi_device *device, u32 event) break; default: /* Unhandled event */ - dev_dbg(&device->dev, + dev_dbg(&als->device->dev, "Unhandled ACPI ALS event (%08x)!\n", event); } @@ -175,9 +176,10 @@ out: return IRQ_HANDLED; } -static int acpi_als_add(struct acpi_device *device) +static int acpi_als_probe(struct platform_device *pdev) { - struct device *dev = &device->dev; + struct device *dev = &pdev->dev; + struct acpi_device *device = ACPI_COMPANION(dev); struct iio_dev *indio_dev; struct acpi_als *als; int ret; @@ -188,7 +190,6 @@ static int acpi_als_add(struct acpi_device *device) als = iio_priv(indio_dev); - device->driver_data = indio_dev; als->device = device; mutex_init(&als->lock); @@ -218,7 +219,18 @@ static int acpi_als_add(struct acpi_device *device) if (ret) return ret; - return devm_iio_device_register(dev, indio_dev); + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + return acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_als_notify, indio_dev); +} + +static void acpi_als_remove(struct platform_device *pdev) +{ + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, acpi_als_notify); } static const struct acpi_device_id acpi_als_device_ids[] = { @@ -228,17 +240,15 @@ static const struct acpi_device_id acpi_als_device_ids[] = { MODULE_DEVICE_TABLE(acpi, acpi_als_device_ids); -static struct acpi_driver acpi_als_driver = { - .name = "acpi_als", - .class = ACPI_ALS_CLASS, - .ids = acpi_als_device_ids, - .ops = { - .add = acpi_als_add, - .notify = acpi_als_notify, +static struct platform_driver acpi_als_driver = { + .probe = acpi_als_probe, + .remove = acpi_als_remove, + .driver = { + .name = "acpi_als", + .acpi_match_table = acpi_als_device_ids, }, }; - -module_acpi_driver(acpi_als_driver); +module_platform_driver(acpi_als_driver); MODULE_AUTHOR("Zhang Rui "); MODULE_AUTHOR("Martin Liska "); diff --git a/drivers/iio/light/apds9160.c b/drivers/iio/light/apds9160.c index 9b8af11b7b67..3da0bdac04cf 100644 --- a/drivers/iio/light/apds9160.c +++ b/drivers/iio/light/apds9160.c @@ -620,7 +620,7 @@ static int apds9160_set_ps_gain(struct apds9160_chip *data, int val) /* * The PS intelligent cancellation level register allows - * for an on-chip substraction of the ADC count caused by + * for an on-chip subtraction of the ADC count caused by * unwanted reflected light from PS ADC output. */ static int apds9160_set_ps_cancellation_level(struct apds9160_chip *data, diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c index 7e68cca0edfa..5ca4c87524fe 100644 --- a/drivers/iio/light/apds9306.c +++ b/drivers/iio/light/apds9306.c @@ -168,7 +168,6 @@ struct apds9306_regfields { * respectively. * @regmap: Regmap structure pointer * @rf: Regmap register fields structure - * @nlux_per_count: Nano lux per ADC count for a particular model * @read_data_available: Flag set by IRQ handler for ADC data available */ struct apds9306_data { @@ -180,7 +179,6 @@ struct apds9306_data { struct regmap *regmap; struct apds9306_regfields rf; - int nlux_per_count; int read_data_available; }; @@ -1176,7 +1174,7 @@ static int apds9306_init_iio_gts(struct apds9306_data *data) static void apds9306_powerdown(void *ptr) { - struct apds9306_data *data = (struct apds9306_data *)ptr; + struct apds9306_data *data = ptr; struct apds9306_regfields *rf = &data->rf; int ret; diff --git a/drivers/iio/light/as73211.c b/drivers/iio/light/as73211.c index 32719f584c47..9fe830dac679 100644 --- a/drivers/iio/light/as73211.c +++ b/drivers/iio/light/as73211.c @@ -677,9 +677,6 @@ static irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p) (char *)&scan.chan[0], 3 * sizeof(scan.chan[0])); if (ret < 0) goto done; - - /* Avoid pushing uninitialized data */ - scan.chan[3] = 0; } if (data_result) { diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index c7df4b258e2c..7e388319ee2e 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -31,12 +31,14 @@ * the other one. */ +#include #include #include #include #include #include #include +#include #include #include #include @@ -44,6 +46,7 @@ #include #include #include + #include #include #include @@ -52,8 +55,6 @@ #include #include -#define GP2A_I2C_NAME "gp2ap020a00f" - /* Registers */ #define GP2AP020A00F_OP_REG 0x00 /* Basic operations */ #define GP2AP020A00F_ALS_REG 0x01 /* ALS related settings */ @@ -174,10 +175,8 @@ #define GP2AP020A00F_CHAN_TIMESTAMP 3 #define GP2AP020A00F_DATA_READY_TIMEOUT msecs_to_jiffies(1000) -#define GP2AP020A00F_DATA_REG(chan) (GP2AP020A00F_D0_L_REG + \ - (chan) * 2) -#define GP2AP020A00F_THRESH_REG(th_val_id) (GP2AP020A00F_TL_L_REG + \ - (th_val_id) * 2) +#define GP2AP020A00F_DATA_REG(chan) (GP2AP020A00F_D0_L_REG + (chan) * 2) +#define GP2AP020A00F_THRESH_REG(th_val_id) (GP2AP020A00F_TL_L_REG + (th_val_id) * 2) #define GP2AP020A00F_THRESH_VAL_ID(reg_addr) ((reg_addr - 4) / 2) #define GP2AP020A00F_SUBTRACT_MODE 0 @@ -194,7 +193,7 @@ enum gp2ap020a00f_opmode { GP2AP020A00F_OPMODE_ALS_AND_PS, GP2AP020A00F_OPMODE_PROX_DETECT, GP2AP020A00F_OPMODE_SHUTDOWN, - GP2AP020A00F_NUM_OPMODES, + GP2AP020A00F_NUM_OPMODES }; enum gp2ap020a00f_cmd { @@ -246,7 +245,6 @@ struct gp2ap020a00f_data { struct iio_trigger *trig; struct regmap *regmap; unsigned int thresh_val[4]; - u8 debug_reg_addr; struct irq_work work; wait_queue_head_t data_ready_queue; }; @@ -389,20 +387,17 @@ static int gp2ap020a00f_set_operation_mode(struct gp2ap020a00f_data *data, } err = regmap_update_bits(data->regmap, GP2AP020A00F_ALS_REG, - GP2AP020A00F_PRST_MASK, opmode_regs_settings[op] - .als_reg); + GP2AP020A00F_PRST_MASK, opmode_regs_settings[op].als_reg); if (err < 0) return err; err = regmap_update_bits(data->regmap, GP2AP020A00F_PS_REG, - GP2AP020A00F_INTTYPE_MASK, opmode_regs_settings[op] - .ps_reg); + GP2AP020A00F_INTTYPE_MASK, opmode_regs_settings[op].ps_reg); if (err < 0) return err; err = regmap_update_bits(data->regmap, GP2AP020A00F_LED_REG, - GP2AP020A00F_PIN_MASK, opmode_regs_settings[op] - .led_reg); + GP2AP020A00F_PIN_MASK, opmode_regs_settings[op].led_reg); if (err < 0) return err; } @@ -453,15 +448,13 @@ static int gp2ap020a00f_write_event_threshold(struct gp2ap020a00f_data *data, */ thresh_reg_val = data->thresh_val[th_val_id] / 16; else - thresh_reg_val = data->thresh_val[th_val_id] > 16000 ? - 16000 : - data->thresh_val[th_val_id]; + thresh_reg_val = min(data->thresh_val[th_val_id], 16000U); thresh_buf = cpu_to_le16(thresh_reg_val); return regmap_bulk_write(data->regmap, GP2AP020A00F_THRESH_REG(th_val_id), - (u8 *)&thresh_buf, 2); + &thresh_buf, sizeof(thresh_buf)); } static int gp2ap020a00f_alter_opmode(struct gp2ap020a00f_data *data, @@ -493,27 +486,24 @@ static int gp2ap020a00f_alter_opmode(struct gp2ap020a00f_data *data, static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, enum gp2ap020a00f_cmd cmd) { - int err = 0; + int err; switch (cmd) { case GP2AP020A00F_CMD_READ_RAW_CLEAR: if (data->cur_opmode != GP2AP020A00F_OPMODE_SHUTDOWN) return -EBUSY; - err = gp2ap020a00f_set_operation_mode(data, + return gp2ap020a00f_set_operation_mode(data, GP2AP020A00F_OPMODE_READ_RAW_CLEAR); - break; case GP2AP020A00F_CMD_READ_RAW_IR: if (data->cur_opmode != GP2AP020A00F_OPMODE_SHUTDOWN) return -EBUSY; - err = gp2ap020a00f_set_operation_mode(data, + return gp2ap020a00f_set_operation_mode(data, GP2AP020A00F_OPMODE_READ_RAW_IR); - break; case GP2AP020A00F_CMD_READ_RAW_PROXIMITY: if (data->cur_opmode != GP2AP020A00F_OPMODE_SHUTDOWN) return -EBUSY; - err = gp2ap020a00f_set_operation_mode(data, + return gp2ap020a00f_set_operation_mode(data, GP2AP020A00F_OPMODE_READ_RAW_PROXIMITY); - break; case GP2AP020A00F_CMD_TRIGGER_CLEAR_EN: if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) return -EBUSY; @@ -521,16 +511,17 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, err = gp2ap020a00f_alter_opmode(data, GP2AP020A00F_OPMODE_ALS, GP2AP020A00F_ADD_MODE); + else + err = 0; set_bit(GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, &data->flags); - break; + return err; case GP2AP020A00F_CMD_TRIGGER_CLEAR_DIS: clear_bit(GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, &data->flags); if (gp2ap020a00f_als_enabled(data)) break; - err = gp2ap020a00f_alter_opmode(data, + return gp2ap020a00f_alter_opmode(data, GP2AP020A00F_OPMODE_ALS, GP2AP020A00F_SUBTRACT_MODE); - break; case GP2AP020A00F_CMD_TRIGGER_IR_EN: if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) return -EBUSY; @@ -538,16 +529,17 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, err = gp2ap020a00f_alter_opmode(data, GP2AP020A00F_OPMODE_ALS, GP2AP020A00F_ADD_MODE); + else + err = 0; set_bit(GP2AP020A00F_FLAG_ALS_IR_TRIGGER, &data->flags); - break; + return err; case GP2AP020A00F_CMD_TRIGGER_IR_DIS: clear_bit(GP2AP020A00F_FLAG_ALS_IR_TRIGGER, &data->flags); if (gp2ap020a00f_als_enabled(data)) break; - err = gp2ap020a00f_alter_opmode(data, + return gp2ap020a00f_alter_opmode(data, GP2AP020A00F_OPMODE_ALS, GP2AP020A00F_SUBTRACT_MODE); - break; case GP2AP020A00F_CMD_TRIGGER_PROX_EN: if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) return -EBUSY; @@ -555,13 +547,12 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, GP2AP020A00F_OPMODE_PS, GP2AP020A00F_ADD_MODE); set_bit(GP2AP020A00F_FLAG_PROX_TRIGGER, &data->flags); - break; + return err; case GP2AP020A00F_CMD_TRIGGER_PROX_DIS: clear_bit(GP2AP020A00F_FLAG_PROX_TRIGGER, &data->flags); - err = gp2ap020a00f_alter_opmode(data, + return gp2ap020a00f_alter_opmode(data, GP2AP020A00F_OPMODE_PS, GP2AP020A00F_SUBTRACT_MODE); - break; case GP2AP020A00F_CMD_ALS_HIGH_EV_EN: if (test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags)) return 0; @@ -575,9 +566,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, return err; } set_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags); - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_TH, true); - break; case GP2AP020A00F_CMD_ALS_HIGH_EV_DIS: if (!test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags)) return 0; @@ -589,9 +579,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, if (err < 0) return err; } - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_TH, false); - break; case GP2AP020A00F_CMD_ALS_LOW_EV_EN: if (test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags)) return 0; @@ -605,9 +594,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, return err; } set_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags); - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_TL, true); - break; case GP2AP020A00F_CMD_ALS_LOW_EV_DIS: if (!test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags)) return 0; @@ -619,9 +607,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, if (err < 0) return err; } - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_TL, false); - break; case GP2AP020A00F_CMD_PROX_HIGH_EV_EN: if (test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags)) return 0; @@ -635,9 +622,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, return err; } set_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags); - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_PH, true); - break; case GP2AP020A00F_CMD_PROX_HIGH_EV_DIS: if (!test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags)) return 0; @@ -646,9 +632,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, GP2AP020A00F_OPMODE_SHUTDOWN); if (err < 0) return err; - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_PH, false); - break; case GP2AP020A00F_CMD_PROX_LOW_EV_EN: if (test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags)) return 0; @@ -662,9 +647,8 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, return err; } set_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_PL, true); - break; case GP2AP020A00F_CMD_PROX_LOW_EV_DIS: if (!test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags)) return 0; @@ -673,12 +657,11 @@ static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, GP2AP020A00F_OPMODE_SHUTDOWN); if (err < 0) return err; - err = gp2ap020a00f_write_event_threshold(data, + return gp2ap020a00f_write_event_threshold(data, GP2AP020A00F_THRESH_PL, false); - break; } - return err; + return 0; } static int wait_conversion_complete_irq(struct gp2ap020a00f_data *data) @@ -697,18 +680,18 @@ static int wait_conversion_complete_irq(struct gp2ap020a00f_data *data) static int gp2ap020a00f_read_output(struct gp2ap020a00f_data *data, unsigned int output_reg, int *val) { - u8 reg_buf[2]; + __le16 reg_buf; int err; err = wait_conversion_complete_irq(data); if (err < 0) dev_dbg(&data->client->dev, "data ready timeout\n"); - err = regmap_bulk_read(data->regmap, output_reg, reg_buf, 2); + err = regmap_bulk_read(data->regmap, output_reg, ®_buf, sizeof(reg_buf)); if (err < 0) return err; - *val = le16_to_cpup((__le16 *)reg_buf); + *val = le16_to_cpu(reg_buf); return err; } @@ -866,13 +849,13 @@ static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) { struct iio_dev *indio_dev = data; struct gp2ap020a00f_data *priv = iio_priv(indio_dev); - u8 op_reg_flags, d0_reg_buf[2]; unsigned int output_val, op_reg_val; + __le16 d0_reg_buf; + u8 op_reg_flags; int thresh_val_id, ret; /* Read interrupt flags */ - ret = regmap_read(priv->regmap, GP2AP020A00F_OP_REG, - &op_reg_val); + ret = regmap_read(priv->regmap, GP2AP020A00F_OP_REG, &op_reg_val); if (ret < 0) goto done; @@ -884,8 +867,7 @@ static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) /* Clear interrupt flags (if not in INTTYPE_PULSE mode) */ if (priv->cur_opmode != GP2AP020A00F_OPMODE_PROX_DETECT) { - ret = regmap_write(priv->regmap, GP2AP020A00F_OP_REG, - op_reg_val); + ret = regmap_write(priv->regmap, GP2AP020A00F_OP_REG, op_reg_val); if (ret < 0) goto done; } @@ -895,11 +877,11 @@ static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) * transition is required. */ ret = regmap_bulk_read(priv->regmap, GP2AP020A00F_D0_L_REG, - d0_reg_buf, 2); + &d0_reg_buf, sizeof(d0_reg_buf)); if (ret < 0) goto done; - output_val = le16_to_cpup((__le16 *)d0_reg_buf); + output_val = le16_to_cpu(d0_reg_buf); if (gp2ap020a00f_adjust_lux_mode(priv, output_val)) goto done; @@ -966,17 +948,15 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data) int i, out_val, ret; iio_for_each_active_channel(indio_dev, i) { - ret = regmap_bulk_read(priv->regmap, - GP2AP020A00F_DATA_REG(i), - &priv->buffer[d_size], 2); + ret = regmap_bulk_read(priv->regmap, GP2AP020A00F_DATA_REG(i), + &priv->buffer[d_size], 2); if (ret < 0) goto done; if (i == GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR || i == GP2AP020A00F_SCAN_MODE_LIGHT_IR) { - out_val = le16_to_cpup((__le16 *)&priv->buffer[d_size]); + out_val = get_unaligned_le16(&priv->buffer[d_size]); gp2ap020a00f_output_to_lux(priv, &out_val); - put_unaligned_le32(out_val, &priv->buffer[d_size]); d_size += 4; } else { @@ -984,16 +964,15 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data) } } - iio_push_to_buffers_with_timestamp(indio_dev, priv->buffer, - pf->timestamp); + iio_push_to_buffers_with_timestamp(indio_dev, priv->buffer, pf->timestamp); done: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; } -static u8 gp2ap020a00f_get_thresh_reg(const struct iio_chan_spec *chan, - enum iio_event_direction event_dir) +static int gp2ap020a00f_get_thresh_reg(const struct iio_chan_spec *chan, + enum iio_event_direction event_dir) { switch (chan->type) { case IIO_PROXIMITY: @@ -1007,10 +986,8 @@ static u8 gp2ap020a00f_get_thresh_reg(const struct iio_chan_spec *chan, else return GP2AP020A00F_TL_L_REG; default: - break; + return -EINVAL; } - - return -EINVAL; } static int gp2ap020a00f_write_event_val(struct iio_dev *indio_dev, @@ -1023,53 +1000,41 @@ static int gp2ap020a00f_write_event_val(struct iio_dev *indio_dev, struct gp2ap020a00f_data *data = iio_priv(indio_dev); bool event_en = false; u8 thresh_val_id; - u8 thresh_reg_l; - int err = 0; + int thresh_reg_l; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); thresh_reg_l = gp2ap020a00f_get_thresh_reg(chan, dir); - thresh_val_id = GP2AP020A00F_THRESH_VAL_ID(thresh_reg_l); + if (thresh_reg_l < 0) + return thresh_reg_l; - if (thresh_val_id > GP2AP020A00F_THRESH_PH) { - err = -EINVAL; - goto error_unlock; - } + thresh_val_id = GP2AP020A00F_THRESH_VAL_ID(thresh_reg_l); + if (thresh_val_id > GP2AP020A00F_THRESH_PH) + return -EINVAL; switch (thresh_reg_l) { case GP2AP020A00F_TH_L_REG: - event_en = test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, - &data->flags); + event_en = test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags); break; case GP2AP020A00F_TL_L_REG: - event_en = test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, - &data->flags); + event_en = test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags); break; case GP2AP020A00F_PH_L_REG: - if (val == 0) { - err = -EINVAL; - goto error_unlock; - } - event_en = test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, - &data->flags); + if (val == 0) + return -EINVAL; + + event_en = test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags); break; case GP2AP020A00F_PL_L_REG: - if (val == 0) { - err = -EINVAL; - goto error_unlock; - } - event_en = test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, - &data->flags); + if (val == 0) + return -EINVAL; + + event_en = test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); break; } data->thresh_val[thresh_val_id] = val; - err = gp2ap020a00f_write_event_threshold(data, thresh_val_id, - event_en); -error_unlock: - mutex_unlock(&data->lock); - - return err; + return gp2ap020a00f_write_event_threshold(data, thresh_val_id, event_en); } static int gp2ap020a00f_read_event_val(struct iio_dev *indio_dev, @@ -1080,24 +1045,19 @@ static int gp2ap020a00f_read_event_val(struct iio_dev *indio_dev, int *val, int *val2) { struct gp2ap020a00f_data *data = iio_priv(indio_dev); - u8 thresh_reg_l; - int err = IIO_VAL_INT; + int thresh_reg_l; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); thresh_reg_l = gp2ap020a00f_get_thresh_reg(chan, dir); - - if (thresh_reg_l > GP2AP020A00F_PH_L_REG) { - err = -EINVAL; - goto error_unlock; - } + if (thresh_reg_l < 0) + return thresh_reg_l; + if (thresh_reg_l > GP2AP020A00F_PH_L_REG) + return -EINVAL; *val = data->thresh_val[GP2AP020A00F_THRESH_VAL_ID(thresh_reg_l)]; -error_unlock: - mutex_unlock(&data->lock); - - return err; + return IIO_VAL_INT; } static int gp2ap020a00f_write_prox_event_config(struct iio_dev *indio_dev, @@ -1163,32 +1123,25 @@ static int gp2ap020a00f_write_event_config(struct iio_dev *indio_dev, { struct gp2ap020a00f_data *data = iio_priv(indio_dev); enum gp2ap020a00f_cmd cmd; - int err; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); switch (chan->type) { case IIO_PROXIMITY: - err = gp2ap020a00f_write_prox_event_config(indio_dev, state); - break; + return gp2ap020a00f_write_prox_event_config(indio_dev, state); case IIO_LIGHT: if (dir == IIO_EV_DIR_RISING) { cmd = state ? GP2AP020A00F_CMD_ALS_HIGH_EV_EN : GP2AP020A00F_CMD_ALS_HIGH_EV_DIS; - err = gp2ap020a00f_exec_cmd(data, cmd); + return gp2ap020a00f_exec_cmd(data, cmd); } else { cmd = state ? GP2AP020A00F_CMD_ALS_LOW_EV_EN : GP2AP020A00F_CMD_ALS_LOW_EV_DIS; - err = gp2ap020a00f_exec_cmd(data, cmd); + return gp2ap020a00f_exec_cmd(data, cmd); } - break; default: - err = -EINVAL; + return -EINVAL; } - - mutex_unlock(&data->lock); - - return err; } static int gp2ap020a00f_read_event_config(struct iio_dev *indio_dev, @@ -1197,40 +1150,29 @@ static int gp2ap020a00f_read_event_config(struct iio_dev *indio_dev, enum iio_event_direction dir) { struct gp2ap020a00f_data *data = iio_priv(indio_dev); - int event_en = 0; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); switch (chan->type) { case IIO_PROXIMITY: if (dir == IIO_EV_DIR_RISING) - event_en = test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, - &data->flags); + return test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags); else - event_en = test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, - &data->flags); - break; + return test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); case IIO_LIGHT: if (dir == IIO_EV_DIR_RISING) - event_en = test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, - &data->flags); + return test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags); else - event_en = test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, - &data->flags); - break; + return test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags); default: - event_en = -EINVAL; - break; + return -EINVAL; } - - mutex_unlock(&data->lock); - - return event_en; } static int gp2ap020a00f_read_channel(struct gp2ap020a00f_data *data, struct iio_chan_spec const *chan, int *val) { + struct device *dev = &data->client->dev; enum gp2ap020a00f_cmd cmd; int err; @@ -1250,27 +1192,23 @@ static int gp2ap020a00f_read_channel(struct gp2ap020a00f_data *data, err = gp2ap020a00f_exec_cmd(data, cmd); if (err < 0) { - dev_err(&data->client->dev, - "gp2ap020a00f_exec_cmd failed\n"); - goto error_ret; + dev_err(dev, "gp2ap020a00f_exec_cmd failed\n"); + return err; } err = gp2ap020a00f_read_output(data, chan->address, val); if (err < 0) - dev_err(&data->client->dev, - "gp2ap020a00f_read_output failed\n"); + dev_err(dev, "gp2ap020a00f_read_output failed\n"); err = gp2ap020a00f_set_operation_mode(data, GP2AP020A00F_OPMODE_SHUTDOWN); if (err < 0) - dev_err(&data->client->dev, - "Failed to shut down the device.\n"); + dev_err(dev, "Failed to shut down the device.\n"); if (cmd == GP2AP020A00F_CMD_READ_RAW_CLEAR || cmd == GP2AP020A00F_CMD_READ_RAW_IR) gp2ap020a00f_output_to_lux(data, val); -error_ret: return err; } @@ -1383,9 +1321,9 @@ static const struct iio_info gp2ap020a00f_info = { static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) { struct gp2ap020a00f_data *data = iio_priv(indio_dev); - int i, err = 0; + int i, err; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); /* * Enable triggers according to the scan_mask. Enabling either @@ -1409,28 +1347,27 @@ static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) err = gp2ap020a00f_exec_cmd(data, GP2AP020A00F_CMD_TRIGGER_PROX_EN); break; + default: + err = -EINVAL; + break; } + if (err) + return err; } - if (err < 0) - goto error_unlock; - data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); if (!data->buffer) - err = -ENOMEM; + return -ENOMEM; -error_unlock: - mutex_unlock(&data->lock); - - return err; + return 0; } static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev) { struct gp2ap020a00f_data *data = iio_priv(indio_dev); - int i, err = 0; + int i, err; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); iio_for_each_active_channel(indio_dev, i) { switch (i) { @@ -1446,15 +1383,16 @@ static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev) err = gp2ap020a00f_exec_cmd(data, GP2AP020A00F_CMD_TRIGGER_PROX_DIS); break; + default: + err = -EINVAL; + break; } + if (err) + return err; } - if (err == 0) - kfree(data->buffer); - - mutex_unlock(&data->lock); - - return err; + kfree(data->buffer); + return 0; } static const struct iio_buffer_setup_ops gp2ap020a00f_buffer_setup_ops = { @@ -1465,18 +1403,19 @@ static const struct iio_buffer_setup_ops gp2ap020a00f_buffer_setup_ops = { static int gp2ap020a00f_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct device *dev = &client->dev; struct gp2ap020a00f_data *data; struct iio_dev *indio_dev; struct regmap *regmap; int err; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); - data->vled_reg = devm_regulator_get(&client->dev, "vled"); + data->vled_reg = devm_regulator_get(dev, "vled"); if (IS_ERR(data->vled_reg)) return PTR_ERR(data->vled_reg); @@ -1486,7 +1425,7 @@ static int gp2ap020a00f_probe(struct i2c_client *client) regmap = devm_regmap_init_i2c(client, &gp2ap020a00f_regmap_config); if (IS_ERR(regmap)) { - dev_err(&client->dev, "Regmap initialization failed.\n"); + dev_err(dev, "Regmap initialization failed.\n"); err = PTR_ERR(regmap); goto error_regulator_disable; } @@ -1497,7 +1436,7 @@ static int gp2ap020a00f_probe(struct i2c_client *client) ARRAY_SIZE(gp2ap020a00f_reg_init_tab)); if (err < 0) { - dev_err(&client->dev, "Device initialization failed.\n"); + dev_err(dev, "Device initialization failed.\n"); goto error_regulator_disable; } @@ -1522,11 +1461,10 @@ static int gp2ap020a00f_probe(struct i2c_client *client) goto error_regulator_disable; /* Allocate trigger */ - data->trig = devm_iio_trigger_alloc(&client->dev, "%s-trigger", - indio_dev->name); + data->trig = devm_iio_trigger_alloc(dev, "%s-trigger", indio_dev->name); if (data->trig == NULL) { err = -ENOMEM; - dev_err(&indio_dev->dev, "Failed to allocate iio trigger.\n"); + dev_err(dev, "Failed to allocate iio trigger.\n"); goto error_uninit_buffer; } @@ -1538,7 +1476,7 @@ static int gp2ap020a00f_probe(struct i2c_client *client) "gp2ap020a00f_als_event", indio_dev); if (err < 0) { - dev_err(&client->dev, "Irq request failed.\n"); + dev_err(dev, "Irq request failed.\n"); goto error_uninit_buffer; } @@ -1546,7 +1484,7 @@ static int gp2ap020a00f_probe(struct i2c_client *client) err = iio_trigger_register(data->trig); if (err < 0) { - dev_err(&client->dev, "Failed to register iio trigger.\n"); + dev_err(dev, "Failed to register iio trigger.\n"); goto error_free_irq; } @@ -1572,12 +1510,12 @@ static void gp2ap020a00f_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct gp2ap020a00f_data *data = iio_priv(indio_dev); + struct device *dev = &client->dev; int err; - err = gp2ap020a00f_set_operation_mode(data, - GP2AP020A00F_OPMODE_SHUTDOWN); + err = gp2ap020a00f_set_operation_mode(data, GP2AP020A00F_OPMODE_SHUTDOWN); if (err < 0) - dev_err(&indio_dev->dev, "Failed to power off the device.\n"); + dev_err(dev, "Failed to power off the device.\n"); iio_device_unregister(indio_dev); iio_trigger_unregister(data->trig); @@ -1587,10 +1525,9 @@ static void gp2ap020a00f_remove(struct i2c_client *client) } static const struct i2c_device_id gp2ap020a00f_id[] = { - { GP2A_I2C_NAME }, + { "gp2ap020a00f" }, { } }; - MODULE_DEVICE_TABLE(i2c, gp2ap020a00f_id); static const struct of_device_id gp2ap020a00f_of_match[] = { @@ -1601,14 +1538,13 @@ MODULE_DEVICE_TABLE(of, gp2ap020a00f_of_match); static struct i2c_driver gp2ap020a00f_driver = { .driver = { - .name = GP2A_I2C_NAME, + .name = "gp2ap020a00f", .of_match_table = gp2ap020a00f_of_match, }, .probe = gp2ap020a00f_probe, .remove = gp2ap020a00f_remove, .id_table = gp2ap020a00f_id, }; - module_i2c_driver(gp2ap020a00f_driver); MODULE_AUTHOR("Jacek Anaszewski "); diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c index fc387426fa87..f1702aca582d 100644 --- a/drivers/iio/light/ltr390.c +++ b/drivers/iio/light/ltr390.c @@ -101,7 +101,7 @@ enum ltr390_meas_rate { struct ltr390_data { struct regmap *regmap; struct i2c_client *client; - /* Protects device from simulataneous reads */ + /* Protects device from simultaneous reads */ struct mutex lock; enum ltr390_mode mode; int gain; diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 022e0693983b..4d99ae336f61 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -754,7 +754,7 @@ static int ltr501_get_gain_index(const struct ltr501_gain *gain, int size, if (val == gain[i].scale && val2 == gain[i].uscale) return i; - return -1; + return -EINVAL; } static int __ltr501_write_raw(struct iio_dev *indio_dev, @@ -773,7 +773,7 @@ static int __ltr501_write_raw(struct iio_dev *indio_dev, info->als_gain_tbl_size, val, val2); if (i < 0) - return -EINVAL; + return i; data->als_contr &= ~info->als_gain_mask; data->als_contr |= i << info->als_gain_shift; @@ -785,7 +785,7 @@ static int __ltr501_write_raw(struct iio_dev *indio_dev, info->ps_gain_tbl_size, val, val2); if (i < 0) - return -EINVAL; + return i; data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK; data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT; diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 393a3d2fbe1d..53bc455b7bad 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -91,7 +91,7 @@ struct opt3001_chip_info { */ int factor_integer; /* - * Factor used to align decimal part of proccessed value to six decimal + * Factor used to align decimal part of processed value to six decimal * places. */ int factor_decimal; diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index 0b171106441a..c8f15ba95267 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -190,7 +190,7 @@ struct tsl2772_chip { }; /* - * Different devices require different coefficents, and these numbers were + * Different devices require different coefficients, and these numbers were * derived from the 'Lux Equation' section of the various device datasheets. * All of these coefficients assume a Glass Attenuation (GA) factor of 1. * The coefficients are multiplied by 1000 to avoid floating point operations. diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index a36c23813679..9650dbc41f2b 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -18,12 +18,13 @@ */ #include -#include -#include -#include #include -#include +#include +#include #include +#include +#include +#include #include #include @@ -185,6 +186,7 @@ static const int vcnl4040_ps_oversampling_ratio[] = {1, 2, 4, 8}; #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ enum vcnl4000_device_ids { + CM36672P, VCNL4000, VCNL4010, VCNL4040, @@ -235,6 +237,8 @@ struct vcnl4000_chip_spec { }; static const struct i2c_device_id vcnl4000_id[] = { + { "cm36672p", CM36672P }, + { "cm36686", VCNL4040 }, { "vcnl4000", VCNL4000 }, { "vcnl4010", VCNL4010 }, { "vcnl4020", VCNL4010 }, @@ -277,7 +281,7 @@ static int vcnl4000_init(struct vcnl4000_data *data) data->rev = ret & 0xf; data->al_scale = 250000; - return data->chip_spec->set_power_state(data, true); + return 0; }; static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) @@ -353,17 +357,19 @@ static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) static int vcnl4200_init(struct vcnl4000_data *data) { + struct i2c_client *client = data->client; + struct device *dev = &client->dev; int ret, id; u16 regval; - ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); + ret = i2c_smbus_read_word_data(client, VCNL4200_DEV_ID); if (ret < 0) return ret; id = ret & 0xff; if (id != VCNL4200_PROD_ID) { - ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); + ret = i2c_smbus_read_word_data(client, VCNL4040_DEV_ID); if (ret < 0) return ret; @@ -373,7 +379,7 @@ static int vcnl4200_init(struct vcnl4000_data *data) return -ENODEV; } - dev_dbg(&data->client->dev, "device id 0x%x", id); + dev_dbg(dev, "device id 0x%x", id); data->rev = (ret >> 8) & 0xf; data->ps_int = 0; @@ -397,32 +403,32 @@ static int vcnl4200_init(struct vcnl4000_data *data) } data->al_scale = data->chip_spec->ulux_step; data->ps_scale = 16; - mutex_init(&data->vcnl4200_al.lock); - mutex_init(&data->vcnl4200_ps.lock); + + ret = devm_mutex_init(dev, &data->vcnl4200_al.lock); + if (ret) + return ret; + + ret = devm_mutex_init(dev, &data->vcnl4200_ps.lock); + if (ret) + return ret; /* Use 16 bits proximity sensor readings */ - ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + ret = i2c_smbus_read_word_data(client, VCNL4200_PS_CONF1); if (ret < 0) return ret; regval = ret | VCNL4040_PS_CONF2_PS_HD; - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, - regval); + ret = i2c_smbus_write_word_data(client, VCNL4200_PS_CONF1, regval); if (ret < 0) return ret; /* Align proximity sensor sample rate to 16 bits data width */ - ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); + ret = i2c_smbus_read_word_data(client, VCNL4200_PS_CONF3); if (ret < 0) return ret; regval = ret | VCNL4040_CONF3_PS_SAMPLE_16BITS; - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, - regval); - if (ret < 0) - return ret; - - ret = data->chip_spec->set_power_state(data, true); + ret = i2c_smbus_write_word_data(client, VCNL4200_PS_CONF3, regval); if (ret < 0) return ret; @@ -1842,6 +1848,22 @@ static const struct iio_chan_spec vcnl4040_channels[] = { } }; +static const struct iio_chan_spec cm36672p_channels[] = { + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .ext_info = vcnl4000_ext_info, + .event_spec = vcnl4040_event_spec, + .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), + }, +}; + static const struct iio_info vcnl4000_info = { .read_raw = vcnl4000_read_raw, }; @@ -1867,6 +1889,19 @@ static const struct iio_info vcnl4040_info = { }; static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { + [CM36672P] = { + .prod = "CM36672P", + .init = vcnl4200_init, + .measure_proximity = vcnl4200_measure_proximity, + .set_power_state = vcnl4200_set_power_state, + .channels = cm36672p_channels, + .num_channels = ARRAY_SIZE(cm36672p_channels), + .info = &vcnl4040_info, + .irq_thread = vcnl4040_irq_thread, + .int_reg = VCNL4040_INT_FLAGS, + .ps_it_times = &vcnl4040_ps_it_times, + .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), + }, [VCNL4000] = { .prod = "VCNL4000", .init = vcnl4000_init, @@ -1951,11 +1986,13 @@ static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) static int vcnl4000_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); + const char * const regulator_names[] = { "vdd", "vio", "vled" }; + struct device *dev = &client->dev; struct vcnl4000_data *data; struct iio_dev *indio_dev; int ret; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; @@ -1965,18 +2002,27 @@ static int vcnl4000_probe(struct i2c_client *client) data->id = id->driver_data; data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; - mutex_init(&data->vcnl4000_lock); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulator_names), + regulator_names); + if (ret) + return ret; + + ret = devm_mutex_init(dev, &data->vcnl4000_lock); + if (ret) + return ret; ret = data->chip_spec->init(data); if (ret < 0) return ret; - dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", + ret = data->chip_spec->set_power_state(data, true); + if (ret) + return ret; + + dev_dbg(dev, "%s Ambient light/proximity sensor, Rev: %02x\n", data->chip_spec->prod, data->rev); - if (device_property_read_u32(&client->dev, "proximity-near-level", - &data->near_level)) - data->near_level = 0; + device_property_read_u32(dev, "proximity-near-level", &data->near_level); indio_dev->info = data->chip_spec->info; indio_dev->channels = data->chip_spec->channels; @@ -1986,35 +2032,29 @@ static int vcnl4000_probe(struct i2c_client *client) if (data->chip_spec->trig_buffer_func && data->chip_spec->buffer_setup_ops) { - ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, - NULL, + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, data->chip_spec->trig_buffer_func, data->chip_spec->buffer_setup_ops); - if (ret < 0) { - dev_err(&client->dev, - "unable to setup iio triggered buffer\n"); + if (ret < 0) return ret; - } } if (client->irq && data->chip_spec->irq_thread) { - ret = devm_request_threaded_irq(&client->dev, client->irq, - NULL, data->chip_spec->irq_thread, + ret = devm_request_threaded_irq(dev, client->irq, NULL, + data->chip_spec->irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "vcnl4000_irq", indio_dev); - if (ret < 0) { - dev_err(&client->dev, "irq request failed\n"); + if (ret < 0) return ret; - } ret = vcnl4010_probe_trigger(indio_dev); if (ret < 0) return ret; } - ret = pm_runtime_set_active(&client->dev); + ret = pm_runtime_set_active(dev); if (ret < 0) goto fail_poweroff; @@ -2022,9 +2062,9 @@ static int vcnl4000_probe(struct i2c_client *client) if (ret < 0) goto fail_poweroff; - pm_runtime_enable(&client->dev); - pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); - pm_runtime_use_autosuspend(&client->dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, VCNL4000_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(dev); return 0; fail_poweroff: @@ -2033,6 +2073,15 @@ fail_poweroff: } static const struct of_device_id vcnl_4000_of_match[] = { + { + .compatible = "capella,cm36672p", + .data = (void *)CM36672P, + }, + /* Capella CM36686 is fully compatible with Vishay VCNL4040 */ + { + .compatible = "capella,cm36686", + .data = (void *)VCNL4040, + }, { .compatible = "vishay,vcnl4000", .data = (void *)VCNL4000, diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c index 1dba1b949cc3..0990e4d266eb 100644 --- a/drivers/iio/light/zopt2201.c +++ b/drivers/iio/light/zopt2201.c @@ -10,17 +10,17 @@ * TODO: interrupt support, ALS/UVB raw mode */ -#include -#include -#include -#include +#include #include +#include +#include +#include +#include +#include #include #include -#include - #define ZOPT2201_DRV_NAME "zopt2201" /* Registers */ @@ -186,10 +186,10 @@ static int zopt2201_read(struct zopt2201_data *data, u8 reg) u8 buf[3]; int ret; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); ret = zopt2201_enable_mode(data, reg == ZOPT2201_UVB_DATA); if (ret < 0) - goto fail; + return ret; while (tries--) { unsigned long t = zopt2201_resolution[data->res].us; @@ -200,30 +200,25 @@ static int zopt2201_read(struct zopt2201_data *data, u8 reg) msleep(t / 1000); ret = i2c_smbus_read_byte_data(client, ZOPT2201_MAIN_STATUS); if (ret < 0) - goto fail; + return ret; if (ret & ZOPT2201_MAIN_STATUS_DRDY) break; } if (tries < 0) { ret = -ETIMEDOUT; - goto fail; + return ret; } ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf); if (ret < 0) - goto fail; + return ret; ret = i2c_smbus_write_byte_data(client, ZOPT2201_MAIN_CTRL, 0x00); if (ret < 0) - goto fail; - mutex_unlock(&data->lock); + return ret; return get_unaligned_le24(&buf[0]); - -fail: - mutex_unlock(&data->lock); - return ret; } static const struct iio_chan_spec zopt2201_channels[] = { @@ -317,17 +312,15 @@ static int zopt2201_set_resolution(struct zopt2201_data *data, u8 res) static int zopt2201_write_resolution(struct zopt2201_data *data, int val, int val2) { - int i, ret; + int i; if (val != 0) return -EINVAL; for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++) if (val2 == zopt2201_resolution[i].us) { - mutex_lock(&data->lock); - ret = zopt2201_set_resolution(data, i); - mutex_unlock(&data->lock); - return ret; + guard(mutex)(&data->lock); + return zopt2201_set_resolution(data, i); } return -EINVAL; @@ -351,16 +344,12 @@ static int zopt2201_write_scale_by_idx(struct zopt2201_data *data, int idx, { int ret; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); ret = zopt2201_set_resolution(data, zopt2201_scale_array[idx].res); if (ret < 0) - goto unlock; + return ret; - ret = zopt2201_set_gain(data, zopt2201_scale_array[idx].gain); - -unlock: - mutex_unlock(&data->lock); - return ret; + return zopt2201_set_gain(data, zopt2201_scale_array[idx].gain); } static int zopt2201_write_scale_als(struct zopt2201_data *data, diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index 68ece700c7ce..817b18257608 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -577,7 +577,7 @@ static int ak8974_measure_channel(struct ak8974 *ak8974, unsigned long address, /* * This explicit cast to (s16) is necessary as the measurement * is done in 2's complement with positive and negative values. - * The follwing assignment to *val will then convert the signed + * The following assignment to *val will then convert the signed * s16 value to a signed int value. */ *val = (s16)le16_to_cpu(hw_values[address]); diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index d30315ad85de..b648b0afa573 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -545,7 +545,7 @@ static int ak8975_set_mode(struct ak8975_data *data, enum ak_ctrl_mode mode) return ret; } data->cntl_cache = regval; - /* After mode change wait atleast 100us */ + /* After mode change wait at least 100us */ usleep_range(100, 500); return 0; @@ -697,7 +697,7 @@ static int wait_conversion_complete_polled(struct ak8975_data *data) return read_status; } -/* Returns 0 if the end of conversion interrupt occured or -ETIME otherwise */ +/* Returns 0 if the end of conversion interrupt occurred or -ETIME otherwise */ static int wait_conversion_complete_interrupt(struct ak8975_data *data) { int ret; @@ -759,7 +759,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) if (ret < 0) goto exit; - /* Read out ST2 for release lock on measurment data. */ + /* Read out ST2 for release lock on measurement data. */ ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST2]); if (ret < 0) { dev_err(&client->dev, "Error in reading ST2\n"); diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index a022e1805dff..bf2551988008 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -148,14 +149,16 @@ struct bmc150_magn_data { static const struct { int freq; u8 reg_val; -} bmc150_magn_samp_freq_table[] = { {2, 0x01}, - {6, 0x02}, - {8, 0x03}, - {10, 0x00}, - {15, 0x04}, - {20, 0x05}, - {25, 0x06}, - {30, 0x07} }; +} bmc150_magn_samp_freq_table[] = { + { 2, 0x01 }, + { 6, 0x02 }, + { 8, 0x03 }, + { 10, 0x00 }, + { 15, 0x04 }, + { 20, 0x05 }, + { 25, 0x06 }, + { 30, 0x07 }, +}; enum bmc150_magn_presets { LOW_POWER_PRESET, @@ -169,10 +172,10 @@ static const struct bmc150_magn_preset { u8 rep_z; u8 odr; } bmc150_magn_presets_table[] = { - [LOW_POWER_PRESET] = {3, 3, 10}, - [REGULAR_PRESET] = {9, 15, 10}, - [ENHANCED_REGULAR_PRESET] = {15, 27, 10}, - [HIGH_ACCURACY_PRESET] = {47, 83, 20}, + [LOW_POWER_PRESET] = { 3, 3, 10 }, + [REGULAR_PRESET] = { 9, 15, 10 }, + [ENHANCED_REGULAR_PRESET] = { 15, 27, 10 }, + [HIGH_ACCURACY_PRESET] = { 47, 83, 20 }, }; #define BMC150_MAGN_DEFAULT_PRESET REGULAR_PRESET @@ -255,6 +258,13 @@ static int bmc150_magn_set_power_mode(struct bmc150_magn_data *data, return -EINVAL; } +static int bmc150_magn_set_power_mode_locked(struct bmc150_magn_data *data, + enum bmc150_magn_power_modes mode) +{ + guard(mutex)(&data->mutex); + return bmc150_magn_set_power_mode(data, mode, true); +} + static int bmc150_magn_set_power_state(struct bmc150_magn_data *data, bool on) { int ret = 0; @@ -453,33 +463,29 @@ static int bmc150_magn_read_raw(struct iio_dev *indio_dev, s32 values[AXIS_XYZ_MAX]; switch (mask) { - case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_RAW: { if (iio_buffer_enabled(indio_dev)) return -EBUSY; - mutex_lock(&data->mutex); + + guard(mutex)(&data->mutex); ret = bmc150_magn_set_power_state(data, true); - if (ret < 0) { - mutex_unlock(&data->mutex); + if (ret < 0) return ret; - } ret = bmc150_magn_read_xyz(data, values); if (ret < 0) { bmc150_magn_set_power_state(data, false); - mutex_unlock(&data->mutex); return ret; } *val = values[chan->scan_index]; ret = bmc150_magn_set_power_state(data, false); - if (ret < 0) { - mutex_unlock(&data->mutex); + if (ret < 0) return ret; - } - mutex_unlock(&data->mutex); return IIO_VAL_INT; + } case IIO_CHAN_INFO_SCALE: /* * The API/driver performs an off-chip temperature @@ -527,48 +533,39 @@ static int bmc150_magn_write_raw(struct iio_dev *indio_dev, int ret; switch (mask) { - case IIO_CHAN_INFO_SAMP_FREQ: + case IIO_CHAN_INFO_SAMP_FREQ: { if (val > data->max_odr) return -EINVAL; - mutex_lock(&data->mutex); - ret = bmc150_magn_set_odr(data, val); - mutex_unlock(&data->mutex); - return ret; + guard(mutex)(&data->mutex); + return bmc150_magn_set_odr(data, val); + } case IIO_CHAN_INFO_OVERSAMPLING_RATIO: switch (chan->channel2) { case IIO_MOD_X: - case IIO_MOD_Y: + case IIO_MOD_Y: { if (val < 1 || val > 511) return -EINVAL; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = bmc150_magn_set_max_odr(data, val, 0, 0); - if (ret < 0) { - mutex_unlock(&data->mutex); + if (ret < 0) return ret; - } - ret = regmap_update_bits(data->regmap, + return regmap_update_bits(data->regmap, BMC150_MAGN_REG_REP_XY, BMC150_MAGN_REG_REP_DATAMASK, - BMC150_MAGN_REPXY_TO_REGVAL - (val)); - mutex_unlock(&data->mutex); - return ret; - case IIO_MOD_Z: + BMC150_MAGN_REPXY_TO_REGVAL(val)); + } + case IIO_MOD_Z: { if (val < 1 || val > 256) return -EINVAL; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = bmc150_magn_set_max_odr(data, 0, val, 0); - if (ret < 0) { - mutex_unlock(&data->mutex); + if (ret < 0) return ret; - } - ret = regmap_update_bits(data->regmap, + return regmap_update_bits(data->regmap, BMC150_MAGN_REG_REP_Z, BMC150_MAGN_REG_REP_DATAMASK, - BMC150_MAGN_REPZ_TO_REGVAL - (val)); - mutex_unlock(&data->mutex); - return ret; + BMC150_MAGN_REPZ_TO_REGVAL(val)); + } default: return -EINVAL; } @@ -655,8 +652,9 @@ static const struct iio_info bmc150_magn_info = { }; static const unsigned long bmc150_magn_scan_masks[] = { - BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), - 0}; + BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), + 0 +}; static irqreturn_t bmc150_magn_trigger_handler(int irq, void *p) { @@ -695,7 +693,7 @@ static int bmc150_magn_init(struct bmc150_magn_data *data) * 3ms power-on time according to datasheet, let's better * be safe than sorry and set this delay to 5ms. */ - msleep(5); + fsleep(5 * USEC_PER_MSEC); ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, false); @@ -782,9 +780,8 @@ static void bmc150_magn_trig_reen(struct iio_trigger *trig) if (!data->dready_trigger_on) return; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = bmc150_magn_reset_intr(data); - mutex_unlock(&data->mutex); if (ret) dev_err(data->dev, "Failed to reset interrupt\n"); } @@ -794,32 +791,28 @@ static int bmc150_magn_data_rdy_trigger_set_state(struct iio_trigger *trig, { struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct bmc150_magn_data *data = iio_priv(indio_dev); - int ret = 0; + int ret; + + guard(mutex)(&data->mutex); - mutex_lock(&data->mutex); if (state == data->dready_trigger_on) - goto err_unlock; + return 0; ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_INT_DRDY, BMC150_MAGN_MASK_DRDY_EN, state << BMC150_MAGN_SHIFT_DRDY_EN); if (ret < 0) - goto err_unlock; + return ret; data->dready_trigger_on = state; if (state) { ret = bmc150_magn_reset_intr(data); if (ret < 0) - goto err_unlock; + return ret; } - mutex_unlock(&data->mutex); return 0; - -err_unlock: - mutex_unlock(&data->mutex); - return ret; } static const struct iio_trigger_ops bmc150_magn_trigger_ops = { @@ -977,9 +970,7 @@ void bmc150_magn_remove(struct device *dev) if (data->dready_trig) iio_trigger_unregister(data->dready_trig); - mutex_lock(&data->mutex); - bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true); - mutex_unlock(&data->mutex); + bmc150_magn_set_power_mode_locked(data, BMC150_MAGN_POWER_MODE_SUSPEND); regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); } @@ -992,10 +983,8 @@ static int bmc150_magn_runtime_suspend(struct device *dev) struct bmc150_magn_data *data = iio_priv(indio_dev); int ret; - mutex_lock(&data->mutex); - ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SLEEP, - true); - mutex_unlock(&data->mutex); + ret = bmc150_magn_set_power_mode_locked(data, + BMC150_MAGN_POWER_MODE_SLEEP); if (ret < 0) { dev_err(dev, "powering off device failed\n"); return ret; @@ -1021,28 +1010,18 @@ static int bmc150_magn_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmc150_magn_data *data = iio_priv(indio_dev); - int ret; - mutex_lock(&data->mutex); - ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SLEEP, - true); - mutex_unlock(&data->mutex); - - return ret; + return bmc150_magn_set_power_mode_locked(data, + BMC150_MAGN_POWER_MODE_SLEEP); } static int bmc150_magn_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmc150_magn_data *data = iio_priv(indio_dev); - int ret; - mutex_lock(&data->mutex); - ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL, - true); - mutex_unlock(&data->mutex); - - return ret; + return bmc150_magn_set_power_mode_locked(data, + BMC150_MAGN_POWER_MODE_NORMAL); } #endif diff --git a/drivers/iio/magnetometer/si7210.c b/drivers/iio/magnetometer/si7210.c index 27e3feba7a0f..2a36abd1c99d 100644 --- a/drivers/iio/magnetometer/si7210.c +++ b/drivers/iio/magnetometer/si7210.c @@ -128,8 +128,8 @@ static const struct regmap_config si7210_regmap_conf = { struct si7210_data { struct regmap *regmap; struct i2c_client *client; - struct regulator *vdd; struct mutex fetch_lock; /* lock for a single measurement fetch */ + unsigned int vdd_uV; s8 temp_offset; s8 temp_gain; s8 scale_20_a[A_REGS_COUNT]; @@ -221,12 +221,8 @@ static int si7210_read_raw(struct iio_dev *indio_dev, temp *= (1 + (data->temp_gain / 2048)); temp += (int)(MICRO / 16) * data->temp_offset; - ret = regulator_get_voltage(data->vdd); - if (ret < 0) - return ret; - /* temp -= 0.222 * VDD */ - temp -= 222 * div_s64(ret, MILLI); + temp -= 222 * (data->vdd_uV / MILLI); *val = div_s64(temp, MILLI); @@ -396,14 +392,11 @@ static int si7210_probe(struct i2c_client *client) return dev_err_probe(&client->dev, PTR_ERR(data->regmap), "failed to register regmap\n"); - data->vdd = devm_regulator_get(&client->dev, "vdd"); - if (IS_ERR(data->vdd)) - return dev_err_probe(&client->dev, PTR_ERR(data->vdd), - "failed to get VDD regulator\n"); - - ret = regulator_enable(data->vdd); - if (ret) - return ret; + ret = devm_regulator_get_enable_read_voltage(&client->dev, "vdd"); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to get vdd regulator\n"); + data->vdd_uV = ret; indio_dev->name = dev_name(&client->dev); indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index d49e37edcbed..140c422773f6 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -1223,7 +1223,7 @@ static int yas530_measure_offsets(struct yas5xx *yas5xx) * as the values for [x, y1, y2]. The value is +/-31 * but the effect on the raw values is much larger. * The effect of the offset is to bring the measure - * rougly to the center. + * roughly to the center. */ ox = 0; oy1 = 0; diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 3b90384f17d7..04bf2f5be5b1 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -47,7 +47,7 @@ static int bmp380_regmap_spi_read(void *context, const void *reg, return -EINVAL; /* - * According to the BMP3xx datasheets, for a basic SPI read opertion, + * According to the BMP3xx datasheets, for a basic SPI read operation, * the first byte needs to be dropped and the rest are the requested * data. */ diff --git a/drivers/iio/pressure/hsc030pa.c b/drivers/iio/pressure/hsc030pa.c index 2d00c0656259..d6b18a84f0ab 100644 --- a/drivers/iio/pressure/hsc030pa.c +++ b/drivers/iio/pressure/hsc030pa.c @@ -273,7 +273,7 @@ static const struct hsc_range_config hsc_range_config[HSC_VARIANTS_MAX] = { * @data: structure containing instantiated sensor data * Return: true only if both status bits are zero * - * the two MSB from the first transfered byte contain a status code + * The two MSB from the first transferred byte contain a status code * 00 - normal operation, valid data * 01 - device in factory programming mode * 10 - stale data diff --git a/drivers/iio/pressure/hsc030pa_i2c.c b/drivers/iio/pressure/hsc030pa_i2c.c index a34ef4653f34..3500bda03d75 100644 --- a/drivers/iio/pressure/hsc030pa_i2c.c +++ b/drivers/iio/pressure/hsc030pa_i2c.c @@ -34,8 +34,13 @@ static int hsc_i2c_recv(struct hsc_data *data) msg.buf = data->buffer; ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + return ret; - return (ret == 2) ? 0 : ret; + if (ret != 1) + return -EIO; + + return 0; } static int hsc_i2c_probe(struct i2c_client *client) diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index dac27fd359ad..08146ca0f91d 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -440,7 +440,7 @@ static int bm1390_fifo_flush(struct iio_dev *idev, unsigned int samples) * the timestamps. If we are ran from IRQ, then the * IRQF_ONESHOT has us covered - but if we are ran by the * user-space read we need to disable the IRQ to be on a safe - * side. We do this usng synchronous disable so that if the + * side. We do this using synchronous disable so that if the * IRQ thread is being ran on other CPU we wait for it to be * finished. */ diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 4923a558a26a..2c68fdf2744e 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -840,7 +840,7 @@ static irqreturn_t zpa2326_handle_threaded_irq(int irq, void *data) complete: /* - * Wake up direct or externaly triggered buffer mode waiters: see + * Wake up direct or externally triggered buffer mode waiters: see * zpa2326_sample_oneshot() and zpa2326_trigger_handler(). */ complete(&priv->data_ready); diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index 6070974c2c85..bb77fad2a1b3 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -244,6 +244,21 @@ config VL53L0X_I2C To compile this driver as a module, choose M here: the module will be called vl53l0x-i2c. +config VL53L1X_I2C + tristate "STMicroelectronics VL53L1X ToF ranger sensor (I2C)" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_I2C + select RESET_CONTROLLER + help + Say Y here to build a driver for STMicroelectronics VL53L1X + ToF ranger sensors with i2c interface. + This driver can be used to measure the distance of objects. + + To compile this driver as a module, choose M here: the + module will be called vl53l1x-i2c. + config AW96103 tristate "AW96103/AW96105 Awinic proximity sensor" select REGMAP_I2C diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index 152034d38c49..4352833dd8a4 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -23,5 +23,6 @@ obj-$(CONFIG_SX_COMMON) += sx_common.o obj-$(CONFIG_SX9500) += sx9500.o obj-$(CONFIG_VCNL3020) += vcnl3020.o obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o +obj-$(CONFIG_VL53L1X_I2C) += vl53l1x-i2c.o obj-$(CONFIG_AW96103) += aw96103.o diff --git a/drivers/iio/proximity/hx9023s.c b/drivers/iio/proximity/hx9023s.c index 17e00ee2b6f8..9efaa5b6b5bd 100644 --- a/drivers/iio/proximity/hx9023s.c +++ b/drivers/iio/proximity/hx9023s.c @@ -1088,6 +1088,7 @@ static int hx9023s_probe(struct i2c_client *client) struct device *dev = &client->dev; struct iio_dev *indio_dev; struct hx9023s_data *data; + const char *fw_name; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); @@ -1125,7 +1126,9 @@ static int hx9023s_probe(struct i2c_client *client) if (ret) return dev_err_probe(dev, ret, "channel config failed\n"); - ret = request_firmware_nowait(THIS_MODULE, true, "hx9023s.bin", dev, + fw_name = "hx9023s.bin"; + device_property_read_string(dev, "firmware-name", &fw_name); + ret = request_firmware_nowait(THIS_MODULE, true, fw_name, dev, GFP_KERNEL, data, hx9023s_cfg_update); if (ret) return dev_err_probe(dev, ret, "reg config failed\n"); diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index c7b2d03c23bc..f61eff39751d 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -821,7 +821,7 @@ static const struct sx_common_reg_default sx9324_default_regs[] = { { SX9324_REG_ADV_CTRL10, 0x00, "adv_ctrl10" }, { SX9324_REG_ADV_CTRL11, 0x00, "adv_ctrl11" }, { SX9324_REG_ADV_CTRL12, 0x00, "adv_ctrl12" }, - /* TODO(gwendal): SAR currenly disabled */ + /* TODO(gwendal): SAR currently disabled */ { SX9324_REG_ADV_CTRL13, 0x00, "adv_ctrl13" }, { SX9324_REG_ADV_CTRL14, 0x00, "adv_ctrl14" }, { SX9324_REG_ADV_CTRL15, 0x00, "adv_ctrl15" }, diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c new file mode 100644 index 000000000000..4d9cb3983dba --- /dev/null +++ b/drivers/iio/proximity/vl53l1x-i2c.c @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* + * Support for ST VL53L1X FlightSense ToF Ranging Sensor on a i2c bus. + * + * Copyright (C) 2026 Siratul Islam + * + * Datasheet available at + * + * + * Default 7-bit i2c slave address 0x29. + * + * The VL53L1X requires a firmware configuration blob to be loaded at boot. + * Register values for the default configuration are taken from + * ST's VL53L1X Ultra Lite Driver (STSW-IMG009). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#define VL53L1X_REG_SOFT_RESET 0x0000 +#define VL53L1X_REG_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND 0x0008 +#define VL53L1X_REG_VHV_CONFIG__INIT 0x000B +#define VL53L1X_REG_GPIO_HV_MUX__CTRL 0x0030 +#define VL53L1X_REG_GPIO__TIO_HV_STATUS 0x0031 +#define VL53L1X_REG_SYSTEM__INTERRUPT_CONFIG_GPIO 0x0046 +#define VL53L1X_REG_PHASECAL_CONFIG__TIMEOUT_MACROP 0x004B +#define VL53L1X_REG_RANGE_CONFIG__TIMEOUT_MACROP_A 0x005E +#define VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_A 0x0060 +#define VL53L1X_REG_RANGE_CONFIG__TIMEOUT_MACROP_B 0x0061 +#define VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_B 0x0063 +#define VL53L1X_REG_RANGE_CONFIG__VALID_PHASE_HIGH 0x0069 +#define VL53L1X_REG_SYSTEM__INTERMEASUREMENT_PERIOD 0x006C +#define VL53L1X_REG_SD_CONFIG__WOI_SD0 0x0078 +#define VL53L1X_REG_SD_CONFIG__WOI_SD1 0x0079 +#define VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD0 0x007A +#define VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD1 0x007B +#define VL53L1X_REG_SYSTEM__INTERRUPT_CLEAR 0x0086 +#define VL53L1X_REG_SYSTEM__MODE_START 0x0087 +#define VL53L1X_REG_RESULT__RANGE_STATUS 0x0089 +#define VL53L1X_REG_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 0x0096 +#define VL53L1X_REG_RESULT__OSC_CALIBRATE_VAL 0x00DE +#define VL53L1X_REG_FIRMWARE__SYSTEM_STATUS 0x00E5 +#define VL53L1X_REG_IDENTIFICATION__MODEL_ID 0x010F +#define VL53L1X_REG_DEFAULT_CONFIG 0x002D + +#define VL53L1X_MODEL_ID_VAL 0xEACC + +#define VL53L1X_MODE_START_TIMED 0x40 +#define VL53L1X_MODE_START_STOP 0x00 + +#define VL53L1X_INT_NEW_SAMPLE_READY 0x02 + +#define VL53L1X_GPIO_HV_MUX_POLARITY BIT(4) + +#define VL53L1X_VHV_LOOP_BOUND_TWO 0x09 + +#define VL53L1X_RANGE_STATUS_MASK GENMASK(4, 0) +#define VL53L1X_RANGE_STATUS_VALID 9 + +#define VL53L1X_OSC_CALIBRATE_MASK GENMASK(9, 0) + +/* Inter-measurement period uses PLL divider with 1.075 oscillator correction */ +static const struct u32_fract vl53l1x_osc_correction = { + .numerator = 1075, + .denominator = 1000, +}; + +enum vl53l1x_distance_mode { + VL53L1X_SHORT, + VL53L1X_LONG, +}; + +struct vl53l1x_data { + struct regmap *regmap; + struct completion completion; + struct reset_control *xshut_reset; + enum vl53l1x_distance_mode distance_mode; + u8 gpio_polarity; + int irq; +}; + +static const struct regmap_range vl53l1x_volatile_ranges[] = { + regmap_reg_range(VL53L1X_REG_GPIO__TIO_HV_STATUS, + VL53L1X_REG_GPIO__TIO_HV_STATUS), + regmap_reg_range(VL53L1X_REG_RESULT__RANGE_STATUS, + VL53L1X_REG_RESULT__RANGE_STATUS), + regmap_reg_range(VL53L1X_REG_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, + VL53L1X_REG_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 + 1), + regmap_reg_range(VL53L1X_REG_RESULT__OSC_CALIBRATE_VAL, + VL53L1X_REG_RESULT__OSC_CALIBRATE_VAL + 1), + regmap_reg_range(VL53L1X_REG_FIRMWARE__SYSTEM_STATUS, + VL53L1X_REG_FIRMWARE__SYSTEM_STATUS), +}; + +static const struct regmap_access_table vl53l1x_volatile_table = { + .yes_ranges = vl53l1x_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(vl53l1x_volatile_ranges), +}; + +static const struct regmap_range vl53l1x_write_only_ranges[] = { + regmap_reg_range(VL53L1X_REG_SOFT_RESET, VL53L1X_REG_SOFT_RESET), + regmap_reg_range(VL53L1X_REG_SYSTEM__INTERRUPT_CLEAR, + VL53L1X_REG_SYSTEM__MODE_START), +}; + +static const struct regmap_access_table vl53l1x_readable_table = { + .no_ranges = vl53l1x_write_only_ranges, + .n_no_ranges = ARRAY_SIZE(vl53l1x_write_only_ranges), +}; + +static const struct regmap_config vl53l1x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + /* MODEL_ID is 16-bit. +1 covers the second byte at 0x0110 */ + .max_register = VL53L1X_REG_IDENTIFICATION__MODEL_ID + 1, + .cache_type = REGCACHE_MAPLE, + .volatile_table = &vl53l1x_volatile_table, + .rd_table = &vl53l1x_readable_table, +}; + +static int vl53l1x_read_u16(struct vl53l1x_data *data, u16 reg, u16 *val) +{ + __be16 buf; + int ret; + + ret = regmap_bulk_read(data->regmap, reg, &buf, sizeof(buf)); + if (ret) + return ret; + + *val = be16_to_cpu(buf); + return 0; +} + +static int vl53l1x_write_u16(struct vl53l1x_data *data, u16 reg, u16 val) +{ + __be16 buf = cpu_to_be16(val); + + return regmap_bulk_write(data->regmap, reg, &buf, sizeof(buf)); +} + +static int vl53l1x_write_u32(struct vl53l1x_data *data, u16 reg, u32 val) +{ + __be32 buf = cpu_to_be32(val); + + return regmap_bulk_write(data->regmap, reg, &buf, sizeof(buf)); +} + +static int vl53l1x_clear_irq(struct vl53l1x_data *data) +{ + return regmap_write(data->regmap, VL53L1X_REG_SYSTEM__INTERRUPT_CLEAR, 0x01); +} + +static int vl53l1x_start_ranging(struct vl53l1x_data *data) +{ + int ret; + + ret = vl53l1x_clear_irq(data); + if (ret) + return ret; + + return regmap_write(data->regmap, VL53L1X_REG_SYSTEM__MODE_START, + VL53L1X_MODE_START_TIMED); +} + +static int vl53l1x_stop_ranging(struct vl53l1x_data *data) +{ + return regmap_write(data->regmap, VL53L1X_REG_SYSTEM__MODE_START, + VL53L1X_MODE_START_STOP); +} + +/* + * Default configuration blob from ST's VL53L1X Ultra Lite Driver + * (STSW-IMG009). + */ +static const u8 vl53l1x_default_config[] = { + 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x02, 0x08, /* reg 0x2d..0x34 */ + 0x00, 0x08, 0x10, 0x01, 0x01, 0x00, 0x00, 0x00, /* reg 0x35..0x3c */ + 0x00, 0xFF, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, /* reg 0x3d..0x44 */ + 0x00, 0x20, 0x0B, 0x00, 0x00, 0x02, 0x0A, 0x21, /* reg 0x45..0x4c */ + 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0xC8, /* reg 0x4d..0x54 */ + 0x00, 0x00, 0x38, 0xFF, 0x01, 0x00, 0x08, 0x00, /* reg 0x55..0x5c */ + 0x00, 0x01, 0xCC, 0x0F, 0x01, 0xF1, 0x0D, 0x01, /* reg 0x5d..0x64 */ + 0x68, 0x00, 0x80, 0x08, 0xB8, 0x00, 0x00, 0x00, /* reg 0x65..0x6c */ + 0x00, 0x0F, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg 0x6d..0x74 */ + 0x00, 0x00, 0x01, 0x0F, 0x0D, 0x0E, 0x0E, 0x00, /* reg 0x75..0x7c */ + 0x00, 0x02, 0xC7, 0xFF, 0x9B, 0x00, 0x00, 0x00, /* reg 0x7d..0x84 */ + 0x01, 0x00, 0x00, /* reg 0x85..0x87 */ +}; + +static int vl53l1x_chip_init(struct vl53l1x_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + unsigned int val; + u16 model_id; + int ret; + + if (!data->xshut_reset) { + ret = regmap_write(data->regmap, VL53L1X_REG_SOFT_RESET, 0x00); + if (ret) + return ret; + fsleep(100); /* conservative reset pulse, no spec */ + + ret = regmap_write(data->regmap, VL53L1X_REG_SOFT_RESET, 0x01); + if (ret) + return ret; + fsleep(1000); /* conservative boot wait, no spec */ + } + + ret = regmap_read_poll_timeout(data->regmap, + VL53L1X_REG_FIRMWARE__SYSTEM_STATUS, val, + val & BIT(0), + 1 * USEC_PER_MSEC, + 100 * USEC_PER_MSEC); + if (ret) + return dev_err_probe(dev, ret, "firmware boot timeout\n"); + + ret = vl53l1x_read_u16(data, VL53L1X_REG_IDENTIFICATION__MODEL_ID, + &model_id); + if (ret) + return ret; + + if (model_id != VL53L1X_MODEL_ID_VAL) + dev_info(dev, "unknown model id: 0x%04x, continuing\n", model_id); + + ret = regmap_bulk_write(data->regmap, VL53L1X_REG_DEFAULT_CONFIG, + vl53l1x_default_config, + sizeof(vl53l1x_default_config)); + if (ret) + return ret; + + ret = regmap_read(data->regmap, VL53L1X_REG_GPIO_HV_MUX__CTRL, &val); + if (ret) + return ret; + data->gpio_polarity = !!(val & VL53L1X_GPIO_HV_MUX_POLARITY); + + /* Initial ranging cycle for VHV calibration */ + ret = vl53l1x_start_ranging(data); + if (ret) + return ret; + + /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ + ret = regmap_read_poll_timeout(data->regmap, + VL53L1X_REG_GPIO__TIO_HV_STATUS, val, + (val & 1) != data->gpio_polarity, + 1 * USEC_PER_MSEC, + 1000 * USEC_PER_MSEC); + if (ret) + return ret; + + ret = vl53l1x_clear_irq(data); + if (ret) + return ret; + + ret = vl53l1x_stop_ranging(data); + if (ret) + return ret; + + ret = regmap_write(data->regmap, + VL53L1X_REG_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, + VL53L1X_VHV_LOOP_BOUND_TWO); + if (ret) + return ret; + + return regmap_write(data->regmap, VL53L1X_REG_VHV_CONFIG__INIT, 0x00); +} + +static const struct reg_sequence vl53l1x_mode_short[] = { + { VL53L1X_REG_PHASECAL_CONFIG__TIMEOUT_MACROP, 0x14 }, + { VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_A, 0x07 }, + { VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_B, 0x05 }, + { VL53L1X_REG_RANGE_CONFIG__VALID_PHASE_HIGH, 0x38 }, + { VL53L1X_REG_SD_CONFIG__WOI_SD0, 0x07 }, + { VL53L1X_REG_SD_CONFIG__WOI_SD1, 0x05 }, + { VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD0, 0x06 }, + { VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD1, 0x06 }, +}; + +static const struct reg_sequence vl53l1x_mode_long[] = { + { VL53L1X_REG_PHASECAL_CONFIG__TIMEOUT_MACROP, 0x0A }, + { VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_A, 0x0F }, + { VL53L1X_REG_RANGE_CONFIG__VCSEL_PERIOD_B, 0x0D }, + { VL53L1X_REG_RANGE_CONFIG__VALID_PHASE_HIGH, 0xB8 }, + { VL53L1X_REG_SD_CONFIG__WOI_SD0, 0x0F }, + { VL53L1X_REG_SD_CONFIG__WOI_SD1, 0x0D }, + { VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD0, 0x0E }, + { VL53L1X_REG_SD_CONFIG__INITIAL_PHASE_SD1, 0x0E }, +}; + +static const struct { + const struct reg_sequence *regs; + size_t num_regs; +} vl53l1x_mode_configs[] = { + [VL53L1X_SHORT] = { vl53l1x_mode_short, ARRAY_SIZE(vl53l1x_mode_short) }, + [VL53L1X_LONG] = { vl53l1x_mode_long, ARRAY_SIZE(vl53l1x_mode_long) }, +}; + +static int vl53l1x_set_distance_mode(struct vl53l1x_data *data, + enum vl53l1x_distance_mode mode) +{ + int ret; + + if (mode >= ARRAY_SIZE(vl53l1x_mode_configs)) + return -EINVAL; + + ret = regmap_multi_reg_write(data->regmap, + vl53l1x_mode_configs[mode].regs, + vl53l1x_mode_configs[mode].num_regs); + if (ret) + return ret; + + data->distance_mode = mode; + return 0; +} + +/* + * The timing budget controls how long the sensor spends collecting + * a single range measurement. Pre-computed TIMEOUT_MACROP register + * values from ST's VL53L1X Ultra Lite Driver. + */ +static int vl53l1x_set_timing_budget(struct vl53l1x_data *data, u16 budget_ms) +{ + u16 timeout_a, timeout_b; + int ret; + + switch (data->distance_mode) { + case VL53L1X_SHORT: + switch (budget_ms) { + case 15: + timeout_a = 0x001D; + timeout_b = 0x0027; + break; + case 20: + timeout_a = 0x0051; + timeout_b = 0x006E; + break; + case 33: + timeout_a = 0x00D6; + timeout_b = 0x006E; + break; + case 50: + timeout_a = 0x01AE; + timeout_b = 0x01E8; + break; + case 100: + timeout_a = 0x02E1; + timeout_b = 0x0388; + break; + case 200: + timeout_a = 0x03E1; + timeout_b = 0x0496; + break; + case 500: + timeout_a = 0x0591; + timeout_b = 0x05C1; + break; + default: + return -EINVAL; + } + break; + case VL53L1X_LONG: + switch (budget_ms) { + case 20: + timeout_a = 0x001E; + timeout_b = 0x0022; + break; + case 33: + timeout_a = 0x0060; + timeout_b = 0x006E; + break; + case 50: + timeout_a = 0x00AD; + timeout_b = 0x00C6; + break; + case 100: + timeout_a = 0x01CC; + timeout_b = 0x01EA; + break; + case 200: + timeout_a = 0x02D9; + timeout_b = 0x02F8; + break; + case 500: + timeout_a = 0x048F; + timeout_b = 0x04A4; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + ret = vl53l1x_write_u16(data, VL53L1X_REG_RANGE_CONFIG__TIMEOUT_MACROP_A, + timeout_a); + if (ret) + return ret; + + return vl53l1x_write_u16(data, VL53L1X_REG_RANGE_CONFIG__TIMEOUT_MACROP_B, + timeout_b); +} + +static int vl53l1x_set_inter_measurement_ms(struct vl53l1x_data *data, + u16 period_ms) +{ + u16 osc_calibrate_val; + u16 clock_pll; + u32 inter_meas; + int ret; + + ret = vl53l1x_read_u16(data, VL53L1X_REG_RESULT__OSC_CALIBRATE_VAL, + &osc_calibrate_val); + if (ret) + return ret; + + clock_pll = osc_calibrate_val & VL53L1X_OSC_CALIBRATE_MASK; + inter_meas = (clock_pll * period_ms * vl53l1x_osc_correction.numerator) / + vl53l1x_osc_correction.denominator; + + return vl53l1x_write_u32(data, + VL53L1X_REG_SYSTEM__INTERMEASUREMENT_PERIOD, + inter_meas); +} + +static int vl53l1x_read_proximity(struct vl53l1x_data *data, int *val) +{ + unsigned int range_status; + u16 distance; + int ret; + + if (data->irq) { + reinit_completion(&data->completion); + + ret = vl53l1x_clear_irq(data); + if (ret) + return ret; + + if (!wait_for_completion_timeout(&data->completion, HZ)) + return -ETIMEDOUT; + } else { + unsigned int rdy; + + /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ + ret = regmap_read_poll_timeout(data->regmap, + VL53L1X_REG_GPIO__TIO_HV_STATUS, rdy, + (rdy & 1) != data->gpio_polarity, + 1 * USEC_PER_MSEC, + 1000 * USEC_PER_MSEC); + if (ret) + return ret; + } + + ret = regmap_read(data->regmap, VL53L1X_REG_RESULT__RANGE_STATUS, + &range_status); + if (ret) + goto clear_irq; + + if (FIELD_GET(VL53L1X_RANGE_STATUS_MASK, range_status) != + VL53L1X_RANGE_STATUS_VALID) { + ret = -EIO; + goto clear_irq; + } + + ret = vl53l1x_read_u16(data, + VL53L1X_REG_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, + &distance); + if (ret) + goto clear_irq; + + *val = distance; + +clear_irq: + vl53l1x_clear_irq(data); + return ret; +} + +static const struct iio_chan_spec vl53l1x_channels[] = { + { + .type = IIO_DISTANCE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static int vl53l1x_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct vl53l1x_data *data = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_DISTANCE) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = vl53l1x_read_proximity(data, val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info vl53l1x_info = { + .read_raw = vl53l1x_read_raw, + .validate_trigger = iio_validate_own_trigger, +}; + +static irqreturn_t vl53l1x_trigger_handler(int irq, void *priv) +{ + struct iio_poll_func *pf = priv; + struct iio_dev *indio_dev = pf->indio_dev; + struct vl53l1x_data *data = iio_priv(indio_dev); + struct { + u16 distance; + aligned_s64 timestamp; + } scan = { }; + unsigned int range_status; + int ret; + + ret = regmap_read(data->regmap, VL53L1X_REG_RESULT__RANGE_STATUS, + &range_status); + if (ret) + goto notify_and_clear_irq; + if (FIELD_GET(VL53L1X_RANGE_STATUS_MASK, range_status) != + VL53L1X_RANGE_STATUS_VALID) + goto notify_and_clear_irq; + + ret = vl53l1x_read_u16(data, + VL53L1X_REG_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, + &scan.distance); + if (ret) + goto notify_and_clear_irq; + + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + iio_get_time_ns(indio_dev)); + +notify_and_clear_irq: + iio_trigger_notify_done(indio_dev->trig); + vl53l1x_clear_irq(data); + + return IRQ_HANDLED; +} + +static irqreturn_t vl53l1x_irq_handler(int irq, void *priv) +{ + struct iio_dev *indio_dev = priv; + struct vl53l1x_data *data = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(indio_dev->trig); + else + complete(&data->completion); + + return IRQ_HANDLED; +} + +static const struct iio_trigger_ops vl53l1x_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static void vl53l1x_stop_ranging_action(void *priv) +{ + vl53l1x_stop_ranging(priv); +} + +static int vl53l1x_configure_irq(struct device *dev, int irq, + struct iio_dev *indio_dev) +{ + struct vl53l1x_data *data = iio_priv(indio_dev); + int ret; + + ret = devm_request_irq(dev, irq, vl53l1x_irq_handler, IRQF_NO_THREAD, + indio_dev->name, indio_dev); + if (ret) + return ret; + + ret = regmap_write(data->regmap, VL53L1X_REG_SYSTEM__INTERRUPT_CONFIG_GPIO, + VL53L1X_INT_NEW_SAMPLE_READY); + if (ret) + return dev_err_probe(dev, ret, "failed to configure IRQ\n"); + + return 0; +} + +static int vl53l1x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct vl53l1x_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->irq = client->irq; + + data->regmap = devm_regmap_init_i2c(client, &vl53l1x_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "regmap initialization failed\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable VDD regulator\n"); + + /* + * XSHUT held low puts the chip in hardware standby. All register + * state is lost on de-assert so this is functionally a reset. + */ + data->xshut_reset = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); + if (IS_ERR(data->xshut_reset)) + return dev_err_probe(dev, PTR_ERR(data->xshut_reset), + "Cannot get reset control\n"); + + /* + * 1.2 ms max boot duration. + * Datasheet Section 3.6 "Power up and boot sequence". + */ + fsleep(1200); + + ret = vl53l1x_chip_init(data); + if (ret) + return ret; + + ret = vl53l1x_set_distance_mode(data, VL53L1X_LONG); + if (ret) + return ret; + + /* 50 ms timing budget (per ST Ultra Lite Driver) */ + ret = vl53l1x_set_timing_budget(data, 50); + if (ret) + return ret; + + /* 50 ms inter-measurement period (per ST Ultra Lite Driver) */ + ret = vl53l1x_set_inter_measurement_ms(data, 50); + if (ret) + return ret; + + /* + * The hardware only supports "autonomous" continuous ranging mode. + * Start ranging here and leave it running for the lifetime of + * the device. Both direct reads and the buffer path rely on this. + */ + ret = vl53l1x_start_ranging(data); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, vl53l1x_stop_ranging_action, data); + if (ret) + return ret; + + indio_dev->name = "vl53l1x"; + indio_dev->info = &vl53l1x_info; + indio_dev->channels = vl53l1x_channels; + indio_dev->num_channels = ARRAY_SIZE(vl53l1x_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + if (client->irq) { + struct iio_trigger *trig; + + init_completion(&data->completion); + + trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); + if (!trig) + return -ENOMEM; + + trig->ops = &vl53l1x_trigger_ops; + iio_trigger_set_drvdata(trig, indio_dev); + ret = devm_iio_trigger_register(dev, trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(trig); + + ret = vl53l1x_configure_irq(dev, client->irq, indio_dev); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + &vl53l1x_trigger_handler, + NULL); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct i2c_device_id vl53l1x_id[] = { + { "vl53l1x" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vl53l1x_id); + +static const struct of_device_id st_vl53l1x_dt_match[] = { + { .compatible = "st,vl53l1x" }, + { } +}; +MODULE_DEVICE_TABLE(of, st_vl53l1x_dt_match); + +static struct i2c_driver vl53l1x_driver = { + .driver = { + .name = "vl53l1x-i2c", + .of_match_table = st_vl53l1x_dt_match, + }, + .probe = vl53l1x_probe, + .id_table = vl53l1x_id, +}; +module_i2c_driver(vl53l1x_driver); + +MODULE_AUTHOR("Siratul Islam "); +MODULE_DESCRIPTION("ST VL53L1X ToF ranging sensor driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index 06d9c784f93e..774728c804c0 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -896,14 +896,14 @@ static const struct iio_event_spec ad2s1210_monitor_signal_event_spec[] = { .mask_separate = BIT(IIO_EV_INFO_VALUE), }, { - /* Sine/cosine DOS overrange fault.*/ + /* Sine/cosine DOS overrange fault. */ .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_RISING, - /* Degredation of signal overrange threshold. */ + /* Degradation of signal overrange threshold. */ .mask_separate = BIT(IIO_EV_INFO_VALUE), }, { - /* Sine/cosine DOS mismatch fault.*/ + /* Sine/cosine DOS mismatch fault. */ .type = IIO_EV_TYPE_MAG, .dir = IIO_EV_DIR_RISING, .mask_separate = BIT(IIO_EV_INFO_VALUE), diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 7dd40d69cce6..38e6f8dfd3b8 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -709,7 +709,7 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data ret = fwnode_property_read_u32(ref, "reg", &thermo->cold_junction_chan); if (ret) /* - * This would be catched later but we can just return + * This would be caught later but we can just return * the error right away. */ return dev_err_ptr_probe(&st->spi->dev, ret, @@ -798,7 +798,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, * For 4wire RTD with rotation, the channel selection cannot be * >=19 since the chann + 1 is used in this configuration. * For 4wire RTDs with kelvin rsense, the rsense channel cannot be - * <=1 since chanel - 1 and channel - 2 are used. + * <=1 since channel - 1 and channel - 2 are used. */ if (rtd->sensor_config & LTC2983_RTD_4_WIRE_MASK) { /* 4-wire */ diff --git a/drivers/iio/test/iio-test-gts.c b/drivers/iio/test/iio-test-gts.c index 11250bc905c9..6ffff85ba853 100644 --- a/drivers/iio/test/iio-test-gts.c +++ b/drivers/iio/test/iio-test-gts.c @@ -20,7 +20,7 @@ * * If yes, then adding a test is probably a good idea but please stop for a * moment and consider the effort of changing all the tests when code gets - * refactored. Eventually it neeeds to be. + * refactored. Eventually it needs to be. */ #define TEST_TSEL_50 1 diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig index 23f4245f7d7d..a7e3f29dc037 100644 --- a/drivers/infiniband/Kconfig +++ b/drivers/infiniband/Kconfig @@ -111,6 +111,4 @@ source "drivers/infiniband/ulp/iser/Kconfig" source "drivers/infiniband/ulp/isert/Kconfig" source "drivers/infiniband/ulp/rtrs/Kconfig" -source "drivers/infiniband/ulp/opa_vnic/Kconfig" - endif # INFINIBAND diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile index a2a7a9d2e0d3..dce798d8cfe6 100644 --- a/drivers/infiniband/core/Makefile +++ b/drivers/infiniband/core/Makefile @@ -12,10 +12,12 @@ ib_core-y := packer.o ud_header.o verbs.o cq.o rw.o sysfs.o \ roce_gid_mgmt.o mr_pool.o addr.o sa_query.o \ multicast.o mad.o smi.o agent.o mad_rmpp.o \ nldev.o restrack.o counters.o ib_core_uverbs.o \ - trace.o lag.o + trace.o lag.o iter.o frmr_pools.o ib_core-$(CONFIG_SECURITY_INFINIBAND) += security.o ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o +ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o umem_dmabuf.o +ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o ib_cm-y := cm.o cm_trace.o @@ -43,5 +45,3 @@ ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o \ uverbs_std_types_wq.o \ uverbs_std_types_qp.o \ ucaps.o -ib_uverbs-$(CONFIG_INFINIBAND_USER_MEM) += umem.o umem_dmabuf.o -ib_uverbs-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 48d4b06384ec..27992c38ad90 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -149,7 +149,7 @@ static int ib_nl_ip_send_msg(struct rdma_dev_addr *dev_addr, attrtype = RDMA_NLA_F_MANDATORY | LS_NLA_TYPE_IPV6; } - len = nla_total_size(sizeof(size)); + len = nla_total_size(size); len += NLMSG_ALIGN(sizeof(*header)); skb = nlmsg_new(len, GFP_KERNEL); @@ -320,11 +320,14 @@ static int dst_fetch_ha(const struct dst_entry *dst, if (!n) return -ENODATA; + read_lock_bh(&n->lock); if (!(n->nud_state & NUD_VALID)) { + read_unlock_bh(&n->lock); neigh_event_send(n, NULL); ret = -ENODATA; } else { neigh_ha_snapshot(dev_addr->dst_dev_addr, n, dst->dev); + read_unlock_bh(&n->lock); } neigh_release(n); diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index ee4a2bc68fb2..647a547e2d7f 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -116,9 +116,9 @@ struct ib_gid_table { /* rwlock protects data_vec[ix]->state and entry pointer. */ rwlock_t rwlock; - struct ib_gid_table_entry **data_vec; /* bit field, each bit indicates the index of default GID */ u32 default_gid_indices; + struct ib_gid_table_entry *data_vec[] __counted_by(sz); }; static void dispatch_gid_change_event(struct ib_device *ib_dev, u32 port) @@ -770,24 +770,16 @@ const struct ib_gid_attr *rdma_find_gid_by_filter( static struct ib_gid_table *alloc_gid_table(int sz) { - struct ib_gid_table *table = kzalloc_obj(*table); + struct ib_gid_table *table = kzalloc_flex(*table, data_vec, sz); if (!table) return NULL; - table->data_vec = kzalloc_objs(*table->data_vec, sz); - if (!table->data_vec) - goto err_free_table; + table->sz = sz; mutex_init(&table->lock); - - table->sz = sz; rwlock_init(&table->rwlock); return table; - -err_free_table: - kfree(table); - return NULL; } static void release_gid_table(struct ib_device *device, @@ -809,7 +801,6 @@ static void release_gid_table(struct ib_device *device, } mutex_destroy(&table->lock); - kfree(table->data_vec); kfree(table); } diff --git a/drivers/infiniband/core/cq.c b/drivers/infiniband/core/cq.c index 81316228f614..3d7b6cddd131 100644 --- a/drivers/infiniband/core/cq.c +++ b/drivers/infiniband/core/cq.c @@ -220,6 +220,9 @@ struct ib_cq *__ib_alloc_cq(struct ib_device *dev, void *private, int nr_cqe, struct ib_cq *cq; int ret = -ENOMEM; + if (WARN_ON_ONCE(!nr_cqe)) + return ERR_PTR(-EINVAL); + cq = rdma_zalloc_drv_obj(dev, ib_cq); if (!cq) return ERR_PTR(ret); diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 7945614be36d..b89efaaa81ec 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -2707,6 +2707,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) dev_ops->uverbs_no_driver_id_binding |= ops->uverbs_no_driver_id_binding; + dev_ops->uverbs_robust_udata |= ops->uverbs_robust_udata; SET_DEVICE_OP(dev_ops, add_gid); SET_DEVICE_OP(dev_ops, add_sub_dev); @@ -2733,7 +2734,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_DEVICE_OP(dev_ops, create_ah); SET_DEVICE_OP(dev_ops, create_counters); SET_DEVICE_OP(dev_ops, create_cq); - SET_DEVICE_OP(dev_ops, create_cq_umem); + SET_DEVICE_OP(dev_ops, create_user_cq); SET_DEVICE_OP(dev_ops, create_flow); SET_DEVICE_OP(dev_ops, create_qp); SET_DEVICE_OP(dev_ops, create_rwq_ind_table); @@ -2782,7 +2783,6 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_DEVICE_OP(dev_ops, get_netdev); SET_DEVICE_OP(dev_ops, get_numa_node); SET_DEVICE_OP(dev_ops, get_port_immutable); - SET_DEVICE_OP(dev_ops, get_vector_affinity); SET_DEVICE_OP(dev_ops, get_vf_config); SET_DEVICE_OP(dev_ops, get_vf_guid); SET_DEVICE_OP(dev_ops, get_vf_stats); @@ -2833,7 +2833,7 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_DEVICE_OP(dev_ops, reg_user_mr_dmabuf); SET_DEVICE_OP(dev_ops, req_notify_cq); SET_DEVICE_OP(dev_ops, rereg_user_mr); - SET_DEVICE_OP(dev_ops, resize_cq); + SET_DEVICE_OP(dev_ops, resize_user_cq); SET_DEVICE_OP(dev_ops, set_vf_guid); SET_DEVICE_OP(dev_ops, set_vf_link_state); SET_DEVICE_OP(dev_ops, ufile_hw_cleanup); diff --git a/drivers/infiniband/core/frmr_pools.c b/drivers/infiniband/core/frmr_pools.c new file mode 100644 index 000000000000..5e992ff3d7cf --- /dev/null +++ b/drivers/infiniband/core/frmr_pools.c @@ -0,0 +1,547 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include "frmr_pools.h" + +#define FRMR_POOLS_DEFAULT_AGING_PERIOD_SECS 60 + +static int push_handle_to_queue_locked(struct frmr_queue *queue, u32 handle) +{ + u32 tmp = queue->ci % NUM_HANDLES_PER_PAGE; + struct frmr_handles_page *page; + + if (queue->ci >= queue->num_pages * NUM_HANDLES_PER_PAGE) { + page = kzalloc_obj(*page, GFP_ATOMIC); + if (!page) + return -ENOMEM; + queue->num_pages++; + list_add_tail(&page->list, &queue->pages_list); + } else { + page = list_last_entry(&queue->pages_list, + struct frmr_handles_page, list); + } + + page->handles[tmp] = handle; + queue->ci++; + return 0; +} + +static u32 pop_handle_from_queue_locked(struct frmr_queue *queue) +{ + u32 tmp = (queue->ci - 1) % NUM_HANDLES_PER_PAGE; + struct frmr_handles_page *page; + u32 handle; + + page = list_last_entry(&queue->pages_list, struct frmr_handles_page, + list); + handle = page->handles[tmp]; + queue->ci--; + + if (!tmp) { + list_del(&page->list); + queue->num_pages--; + kfree(page); + } + + return handle; +} + +static bool pop_frmr_handles_page(struct ib_frmr_pool *pool, + struct frmr_queue *queue, + struct frmr_handles_page **page, u32 *count) +{ + spin_lock(&pool->lock); + if (list_empty(&queue->pages_list)) { + spin_unlock(&pool->lock); + return false; + } + + *page = list_first_entry(&queue->pages_list, struct frmr_handles_page, + list); + list_del(&(*page)->list); + queue->num_pages--; + + /* If this is the last page, count may be less than + * NUM_HANDLES_PER_PAGE. + */ + if (queue->ci >= NUM_HANDLES_PER_PAGE) + *count = NUM_HANDLES_PER_PAGE; + else + *count = queue->ci; + + queue->ci -= *count; + spin_unlock(&pool->lock); + return true; +} + +static void destroy_all_handles_in_queue(struct ib_device *device, + struct ib_frmr_pool *pool, + struct frmr_queue *queue) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + struct frmr_handles_page *page; + u32 count; + + while (pop_frmr_handles_page(pool, queue, &page, &count)) { + pools->pool_ops->destroy_frmrs(device, page->handles, count); + kfree(page); + } +} + +static bool age_pinned_pool(struct ib_device *device, struct ib_frmr_pool *pool) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + u32 total, to_destroy, destroyed = 0; + bool has_work = false; + u32 *handles; + u32 handle; + + spin_lock(&pool->lock); + total = pool->queue.ci + pool->inactive_queue.ci + pool->in_use; + if (total <= pool->pinned_handles) { + spin_unlock(&pool->lock); + return false; + } + + to_destroy = total - pool->pinned_handles; + + handles = kcalloc(to_destroy, sizeof(*handles), GFP_ATOMIC); + if (!handles) { + spin_unlock(&pool->lock); + return true; + } + + /* Destroy all excess handles in the inactive queue */ + while (pool->inactive_queue.ci && destroyed < to_destroy) { + handles[destroyed++] = pop_handle_from_queue_locked( + &pool->inactive_queue); + } + + /* Move all handles from regular queue to inactive queue */ + while (pool->queue.ci) { + handle = pop_handle_from_queue_locked(&pool->queue); + push_handle_to_queue_locked(&pool->inactive_queue, handle); + has_work = true; + } + + spin_unlock(&pool->lock); + + if (destroyed) + pools->pool_ops->destroy_frmrs(device, handles, destroyed); + kfree(handles); + return has_work; +} + +static void pool_aging_work(struct work_struct *work) +{ + struct ib_frmr_pool *pool = container_of( + to_delayed_work(work), struct ib_frmr_pool, aging_work); + struct ib_frmr_pools *pools = pool->device->frmr_pools; + bool has_work = false; + + if (pool->pinned_handles) { + has_work = age_pinned_pool(pool->device, pool); + goto out; + } + + destroy_all_handles_in_queue(pool->device, pool, &pool->inactive_queue); + + /* Move all pages from regular queue to inactive queue */ + spin_lock(&pool->lock); + if (pool->queue.ci > 0) { + list_splice_tail_init(&pool->queue.pages_list, + &pool->inactive_queue.pages_list); + pool->inactive_queue.num_pages = pool->queue.num_pages; + pool->inactive_queue.ci = pool->queue.ci; + + pool->queue.num_pages = 0; + pool->queue.ci = 0; + has_work = true; + } + spin_unlock(&pool->lock); + +out: + /* Reschedule if there are handles to age in next aging period */ + if (has_work) + queue_delayed_work( + pools->aging_wq, &pool->aging_work, + secs_to_jiffies(READ_ONCE(pools->aging_period_sec))); +} + +static void destroy_frmr_pool(struct ib_device *device, + struct ib_frmr_pool *pool) +{ + cancel_delayed_work_sync(&pool->aging_work); + destroy_all_handles_in_queue(device, pool, &pool->queue); + destroy_all_handles_in_queue(device, pool, &pool->inactive_queue); + + kfree(pool); +} + +/* + * Initialize the FRMR pools for a device. + * + * @device: The device to initialize the FRMR pools for. + * @pool_ops: The pool operations to use. + * + * Returns 0 on success, negative error code on failure. + */ +int ib_frmr_pools_init(struct ib_device *device, + const struct ib_frmr_pool_ops *pool_ops) +{ + struct ib_frmr_pools *pools; + + pools = kzalloc_obj(*pools); + if (!pools) + return -ENOMEM; + + pools->rb_root = RB_ROOT; + rwlock_init(&pools->rb_lock); + pools->pool_ops = pool_ops; + pools->aging_wq = create_singlethread_workqueue("frmr_aging_wq"); + if (!pools->aging_wq) { + kfree(pools); + return -ENOMEM; + } + + pools->aging_period_sec = FRMR_POOLS_DEFAULT_AGING_PERIOD_SECS; + + device->frmr_pools = pools; + return 0; +} +EXPORT_SYMBOL(ib_frmr_pools_init); + +/* + * Clean up the FRMR pools for a device. + * + * @device: The device to clean up the FRMR pools for. + * + * Call cleanup only after all FRMR handles have been pushed back to the pool + * and no other FRMR operations are allowed to run in parallel. + * Ensuring this allows us to save synchronization overhead in pop and push + * operations. + */ +void ib_frmr_pools_cleanup(struct ib_device *device) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + struct ib_frmr_pool *pool, *next; + + if (!pools) + return; + + rbtree_postorder_for_each_entry_safe(pool, next, &pools->rb_root, node) + destroy_frmr_pool(device, pool); + + destroy_workqueue(pools->aging_wq); + kfree(pools); + device->frmr_pools = NULL; +} +EXPORT_SYMBOL(ib_frmr_pools_cleanup); + +int ib_frmr_pools_set_aging_period(struct ib_device *device, u32 period_sec) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + struct ib_frmr_pool *pool; + struct rb_node *node; + + if (!pools) + return -EINVAL; + + if (period_sec == 0) + return -EINVAL; + + WRITE_ONCE(pools->aging_period_sec, period_sec); + + read_lock(&pools->rb_lock); + for (node = rb_first(&pools->rb_root); node; node = rb_next(node)) { + pool = rb_entry(node, struct ib_frmr_pool, node); + mod_delayed_work(pools->aging_wq, &pool->aging_work, + secs_to_jiffies(period_sec)); + } + read_unlock(&pools->rb_lock); + + return 0; +} + +static inline int compare_keys(struct ib_frmr_key *key1, + struct ib_frmr_key *key2) +{ + int res; + + res = cmp_int(key1->ats, key2->ats); + if (res) + return res; + + res = cmp_int(key1->access_flags, key2->access_flags); + if (res) + return res; + + res = cmp_int(key1->vendor_key, key2->vendor_key); + if (res) + return res; + + res = cmp_int(key1->kernel_vendor_key, key2->kernel_vendor_key); + if (res) + return res; + + /* + * allow using handles that support more DMA blocks, up to twice the + * requested number + */ + res = cmp_int(key1->num_dma_blocks, key2->num_dma_blocks); + if (res > 0) { + if (key1->num_dma_blocks - key2->num_dma_blocks < + key2->num_dma_blocks) + return 0; + } + + return res; +} + +static int frmr_pool_cmp_find(const void *key, const struct rb_node *node) +{ + struct ib_frmr_pool *pool = rb_entry(node, struct ib_frmr_pool, node); + + return compare_keys(&pool->key, (struct ib_frmr_key *)key); +} + +static int frmr_pool_cmp_add(struct rb_node *new, const struct rb_node *node) +{ + struct ib_frmr_pool *new_pool = + rb_entry(new, struct ib_frmr_pool, node); + struct ib_frmr_pool *pool = rb_entry(node, struct ib_frmr_pool, node); + + return compare_keys(&pool->key, &new_pool->key); +} + +static struct ib_frmr_pool *ib_frmr_pool_find(struct ib_frmr_pools *pools, + struct ib_frmr_key *key) +{ + struct ib_frmr_pool *pool; + struct rb_node *node; + + /* find operation is done under read lock for performance reasons. + * The case of threads failing to find the same pool and creating it + * is handled by the create_frmr_pool function. + */ + read_lock(&pools->rb_lock); + node = rb_find(key, &pools->rb_root, frmr_pool_cmp_find); + pool = rb_entry_safe(node, struct ib_frmr_pool, node); + read_unlock(&pools->rb_lock); + + return pool; +} + +static struct ib_frmr_pool *create_frmr_pool(struct ib_device *device, + struct ib_frmr_key *key) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + struct ib_frmr_pool *pool; + struct rb_node *existing; + + pool = kzalloc_obj(*pool); + if (!pool) + return ERR_PTR(-ENOMEM); + + memcpy(&pool->key, key, sizeof(*key)); + INIT_LIST_HEAD(&pool->queue.pages_list); + INIT_LIST_HEAD(&pool->inactive_queue.pages_list); + spin_lock_init(&pool->lock); + INIT_DELAYED_WORK(&pool->aging_work, pool_aging_work); + pool->device = device; + + write_lock(&pools->rb_lock); + existing = rb_find_add(&pool->node, &pools->rb_root, frmr_pool_cmp_add); + write_unlock(&pools->rb_lock); + + /* If a different thread has already created the pool, return it. + * The insert operation is done under the write lock so we are sure + * that the pool is not inserted twice. + */ + if (existing) { + kfree(pool); + return rb_entry(existing, struct ib_frmr_pool, node); + } + + return pool; +} + +int ib_frmr_pools_set_pinned(struct ib_device *device, struct ib_frmr_key *key, + u32 pinned_handles) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + struct ib_frmr_key driver_key = {}; + struct ib_frmr_pool *pool; + u32 needed_handles; + u32 current_total; + int i, ret = 0; + u32 *handles; + + if (!pools) + return -EINVAL; + + ret = ib_check_mr_access(device, key->access_flags); + if (ret) + return ret; + + if (pools->pool_ops->build_key) { + ret = pools->pool_ops->build_key(device, key, &driver_key); + if (ret) + return ret; + } else { + memcpy(&driver_key, key, sizeof(*key)); + } + + pool = ib_frmr_pool_find(pools, &driver_key); + if (!pool) { + pool = create_frmr_pool(device, &driver_key); + if (IS_ERR(pool)) + return PTR_ERR(pool); + } + + spin_lock(&pool->lock); + current_total = pool->in_use + pool->queue.ci + pool->inactive_queue.ci; + + if (current_total < pinned_handles) + needed_handles = pinned_handles - current_total; + else + needed_handles = 0; + + pool->pinned_handles = pinned_handles; + spin_unlock(&pool->lock); + + if (!needed_handles) + goto schedule_aging; + + handles = kcalloc(needed_handles, sizeof(*handles), GFP_KERNEL); + if (!handles) + return -ENOMEM; + + ret = pools->pool_ops->create_frmrs(device, key, handles, + needed_handles); + if (ret) { + kfree(handles); + return ret; + } + + spin_lock(&pool->lock); + for (i = 0; i < needed_handles; i++) { + ret = push_handle_to_queue_locked(&pool->queue, + handles[i]); + if (ret) + goto end; + } + +end: + spin_unlock(&pool->lock); + kfree(handles); + +schedule_aging: + /* Ensure aging is scheduled to adjust to new pinned handles count */ + mod_delayed_work(pools->aging_wq, &pool->aging_work, 0); + + return ret; +} + +static int get_frmr_from_pool(struct ib_device *device, + struct ib_frmr_pool *pool, struct ib_mr *mr) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + u32 handle; + int err; + + spin_lock(&pool->lock); + if (pool->queue.ci == 0) { + if (pool->inactive_queue.ci > 0) { + handle = pop_handle_from_queue_locked( + &pool->inactive_queue); + } else { + spin_unlock(&pool->lock); + err = pools->pool_ops->create_frmrs(device, &pool->key, + &handle, 1); + if (err) + return err; + spin_lock(&pool->lock); + } + } else { + handle = pop_handle_from_queue_locked(&pool->queue); + } + + pool->in_use++; + if (pool->in_use > pool->max_in_use) + pool->max_in_use = pool->in_use; + + spin_unlock(&pool->lock); + + mr->frmr.pool = pool; + mr->frmr.handle = handle; + + return 0; +} + +/* + * Pop an FRMR handle from the pool. + * + * @device: The device to pop the FRMR handle from. + * @mr: The MR to pop the FRMR handle from. + * + * Returns 0 on success, negative error code on failure. + */ +int ib_frmr_pool_pop(struct ib_device *device, struct ib_mr *mr) +{ + struct ib_frmr_pools *pools = device->frmr_pools; + struct ib_frmr_pool *pool; + + WARN_ON_ONCE(!device->frmr_pools); + pool = ib_frmr_pool_find(pools, &mr->frmr.key); + if (!pool) { + pool = create_frmr_pool(device, &mr->frmr.key); + if (IS_ERR(pool)) + return PTR_ERR(pool); + } + + return get_frmr_from_pool(device, pool, mr); +} +EXPORT_SYMBOL(ib_frmr_pool_pop); + +/* + * Push an FRMR handle back to the pool. + * + * @device: The device to push the FRMR handle to. + * @mr: The MR containing the FRMR handle to push back to the pool. + * + * Returns 0 on success, negative error code on failure. + */ +int ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr) +{ + struct ib_frmr_pool *pool = mr->frmr.pool; + struct ib_frmr_pools *pools = device->frmr_pools; + bool schedule_aging = false; + int ret; + + spin_lock(&pool->lock); + /* Schedule aging every time an empty pool becomes non-empty */ + if (pool->queue.ci == 0) + schedule_aging = true; + ret = push_handle_to_queue_locked(&pool->queue, mr->frmr.handle); + if (ret == 0) + pool->in_use--; + + spin_unlock(&pool->lock); + + if (ret == 0 && schedule_aging) + queue_delayed_work(pools->aging_wq, &pool->aging_work, + secs_to_jiffies(READ_ONCE(pools->aging_period_sec))); + + return ret; +} +EXPORT_SYMBOL(ib_frmr_pool_push); diff --git a/drivers/infiniband/core/frmr_pools.h b/drivers/infiniband/core/frmr_pools.h new file mode 100644 index 000000000000..67e1402169ae --- /dev/null +++ b/drivers/infiniband/core/frmr_pools.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB + * + * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#ifndef RDMA_CORE_FRMR_POOLS_H +#define RDMA_CORE_FRMR_POOLS_H + +#include +#include +#include +#include +#include +#include + +#define NUM_HANDLES_PER_PAGE \ + ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(u32)) + +struct frmr_handles_page { + struct list_head list; + u32 handles[NUM_HANDLES_PER_PAGE]; +}; + +/* FRMR queue holds a list of frmr_handles_page. + * num_pages: number of pages in the queue. + * ci: current index in the handles array across all pages. + */ +struct frmr_queue { + struct list_head pages_list; + u32 num_pages; + unsigned long ci; +}; + +struct ib_frmr_pool { + struct rb_node node; + struct ib_frmr_key key; /* Pool key */ + + /* Protect access to the queue */ + spinlock_t lock; + struct frmr_queue queue; + struct frmr_queue inactive_queue; + + struct delayed_work aging_work; + struct ib_device *device; + + u32 max_in_use; + u32 in_use; + u32 pinned_handles; +}; + +struct ib_frmr_pools { + struct rb_root rb_root; + rwlock_t rb_lock; + const struct ib_frmr_pool_ops *pool_ops; + + struct workqueue_struct *aging_wq; + u32 aging_period_sec; +}; + +int ib_frmr_pools_set_pinned(struct ib_device *device, struct ib_frmr_key *key, + u32 pinned_handles); +int ib_frmr_pools_set_aging_period(struct ib_device *device, u32 period_sec); +#endif /* RDMA_CORE_FRMR_POOLS_H */ diff --git a/drivers/infiniband/core/ib_core_uverbs.c b/drivers/infiniband/core/ib_core_uverbs.c index d6e99c79cf18..1f7a5c119cc9 100644 --- a/drivers/infiniband/core/ib_core_uverbs.c +++ b/drivers/infiniband/core/ib_core_uverbs.c @@ -389,3 +389,30 @@ int rdma_user_mmap_entry_insert(struct ib_ucontext *ucontext, U32_MAX); } EXPORT_SYMBOL(rdma_user_mmap_entry_insert); + +/** + * rdma_udata_to_dev - Get a ib_device from a udata + * @udata: The system calls ib_udata struct + * + * The struct ib_device that is handling the uverbs call. Must not be called if + * udata is NULL. The result can be NULL. + */ +struct ib_device *rdma_udata_to_dev(struct ib_udata *udata) +{ + struct uverbs_attr_bundle *bundle = + rdma_udata_to_uverbs_attr_bundle(udata); + + lockdep_assert_held(&bundle->ufile->device->disassociate_srcu); + + if (bundle->context) + return bundle->context->device; + + /* + * If the context hasn't been created yet use the ufile's dev, but it + * might be NULL if we are racing with disassociate. + */ + return srcu_dereference(bundle->ufile->device->ib_dev, + &bundle->ufile->device->disassociate_srcu); +} +EXPORT_SYMBOL(rdma_udata_to_dev); + diff --git a/drivers/infiniband/core/iter.c b/drivers/infiniband/core/iter.c new file mode 100644 index 000000000000..8e543d100657 --- /dev/null +++ b/drivers/infiniband/core/iter.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. */ + +#include +#include + +void __rdma_block_iter_start(struct ib_block_iter *biter, + struct scatterlist *sglist, unsigned int nents, + unsigned long pgsz) +{ + memset(biter, 0, sizeof(struct ib_block_iter)); + biter->__sg = sglist; + biter->__sg_nents = nents; + + /* Driver provides best block size to use */ + biter->__pg_bit = __fls(pgsz); +} +EXPORT_SYMBOL(__rdma_block_iter_start); + +bool __rdma_block_iter_next(struct ib_block_iter *biter) +{ + unsigned int block_offset; + unsigned int delta; + + if (!biter->__sg_nents || !biter->__sg) + return false; + + biter->__dma_addr = sg_dma_address(biter->__sg) + biter->__sg_advance; + block_offset = biter->__dma_addr & (BIT_ULL(biter->__pg_bit) - 1); + delta = BIT_ULL(biter->__pg_bit) - block_offset; + + while (biter->__sg_nents && biter->__sg && + sg_dma_len(biter->__sg) - biter->__sg_advance <= delta) { + delta -= sg_dma_len(biter->__sg) - biter->__sg_advance; + biter->__sg_advance = 0; + biter->__sg = sg_next(biter->__sg); + biter->__sg_nents--; + } + biter->__sg_advance += delta; + + return true; +} +EXPORT_SYMBOL(__rdma_block_iter_next); diff --git a/drivers/infiniband/core/iwpm_msg.c b/drivers/infiniband/core/iwpm_msg.c index 69c85249b465..4625abd29ac0 100644 --- a/drivers/infiniband/core/iwpm_msg.c +++ b/drivers/infiniband/core/iwpm_msg.c @@ -365,9 +365,9 @@ remove_mapping_error: /* netlink attribute policy for the received response to register pid request */ static const struct nla_policy resp_reg_policy[IWPM_NLA_RREG_PID_MAX] = { [IWPM_NLA_RREG_PID_SEQ] = { .type = NLA_U32 }, - [IWPM_NLA_RREG_IBDEV_NAME] = { .type = NLA_STRING, + [IWPM_NLA_RREG_IBDEV_NAME] = { .type = NLA_NUL_STRING, .len = IWPM_DEVNAME_SIZE - 1 }, - [IWPM_NLA_RREG_ULIB_NAME] = { .type = NLA_STRING, + [IWPM_NLA_RREG_ULIB_NAME] = { .type = NLA_NUL_STRING, .len = IWPM_ULIBNAME_SIZE - 1 }, [IWPM_NLA_RREG_ULIB_VER] = { .type = NLA_U16 }, [IWPM_NLA_RREG_PID_ERR] = { .type = NLA_U16 } @@ -677,7 +677,7 @@ int iwpm_remote_info_cb(struct sk_buff *skb, struct netlink_callback *cb) /* netlink attribute policy for the received request for mapping info */ static const struct nla_policy resp_mapinfo_policy[IWPM_NLA_MAPINFO_REQ_MAX] = { - [IWPM_NLA_MAPINFO_ULIB_NAME] = { .type = NLA_STRING, + [IWPM_NLA_MAPINFO_ULIB_NAME] = { .type = NLA_NUL_STRING, .len = IWPM_ULIBNAME_SIZE - 1 }, [IWPM_NLA_MAPINFO_ULIB_VER] = { .type = NLA_U16 } }; diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 2220a2dfab24..96c745d5bac4 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -37,11 +37,13 @@ #include #include #include +#include #include "core_priv.h" #include "cma_priv.h" #include "restrack.h" #include "uverbs.h" +#include "frmr_pools.h" /* * This determines whether a non-privileged user is allowed to specify a @@ -172,6 +174,19 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = { [RDMA_NLDEV_ATTR_NAME_ASSIGN_TYPE] = { .type = NLA_U8 }, [RDMA_NLDEV_ATTR_EVENT_TYPE] = { .type = NLA_U8 }, [RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED] = { .type = NLA_U8 }, + [RDMA_NLDEV_ATTR_FRMR_POOLS] = { .type = NLA_NESTED }, + [RDMA_NLDEV_ATTR_FRMR_POOL_ENTRY] = { .type = NLA_NESTED }, + [RDMA_NLDEV_ATTR_FRMR_POOL_KEY] = { .type = NLA_NESTED }, + [RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS] = { .type = NLA_U8 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ACCESS_FLAGS] = { .type = NLA_U32 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_KEY_VENDOR_KEY] = { .type = NLA_U64 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS] = { .type = NLA_U64 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_QUEUE_HANDLES] = { .type = NLA_U32 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_MAX_IN_USE] = { .type = NLA_U64 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE] = { .type = NLA_U64 }, + [RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD] = { .type = NLA_U32 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES] = { .type = NLA_U32 }, + [RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY] = { .type = NLA_U64 }, }; static int put_driver_name_print_type(struct sk_buff *msg, const char *name, @@ -1824,6 +1839,18 @@ static int nldev_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, return -EINVAL; } + /* + * This path is triggered by the 'rdma link delete' administrative command. + * For Soft-RoCE (RXE), we ensure that transport sockets are closed here. + * Note: iWARP driver does not implement .dellink, so this logic is + * implicitly scoped to the driver supporting dynamic link deletion like RXE. + */ + if (device->link_ops && device->link_ops->dellink) { + err = device->link_ops->dellink(device); + if (err) + return err; + } + ib_unregister_device_and_put(device); return 0; } @@ -2637,6 +2664,270 @@ static int nldev_deldev(struct sk_buff *skb, struct nlmsghdr *nlh, return ib_del_sub_device_and_put(device); } +static int fill_frmr_pool_key(struct sk_buff *msg, struct ib_frmr_key *key) +{ + struct nlattr *key_attr; + + key_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_FRMR_POOL_KEY); + if (!key_attr) + return -EMSGSIZE; + + if (nla_put_u8(msg, RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS, key->ats)) + goto err; + if (nla_put_u32(msg, RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ACCESS_FLAGS, + key->access_flags)) + goto err; + if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_FRMR_POOL_KEY_VENDOR_KEY, + key->vendor_key, RDMA_NLDEV_ATTR_PAD)) + goto err; + if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS, + key->num_dma_blocks, RDMA_NLDEV_ATTR_PAD)) + goto err; + + if (key->kernel_vendor_key && + nla_put_u64_64bit(msg, + RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY, + key->kernel_vendor_key, RDMA_NLDEV_ATTR_PAD)) + goto err; + + nla_nest_end(msg, key_attr); + return 0; + +err: + return -EMSGSIZE; +} + +static int fill_frmr_pool_entry(struct sk_buff *msg, struct ib_frmr_pool *pool) +{ + if (fill_frmr_pool_key(msg, &pool->key)) + return -EMSGSIZE; + + spin_lock(&pool->lock); + if (nla_put_u32(msg, RDMA_NLDEV_ATTR_FRMR_POOL_QUEUE_HANDLES, + pool->queue.ci + pool->inactive_queue.ci)) + goto err_unlock; + if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_FRMR_POOL_MAX_IN_USE, + pool->max_in_use, RDMA_NLDEV_ATTR_PAD)) + goto err_unlock; + if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE, + pool->in_use, RDMA_NLDEV_ATTR_PAD)) + goto err_unlock; + if (nla_put_u32(msg, RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES, + pool->pinned_handles)) + goto err_unlock; + spin_unlock(&pool->lock); + + return 0; + +err_unlock: + spin_unlock(&pool->lock); + return -EMSGSIZE; +} + +static int nldev_frmr_pools_parse_key(struct nlattr *tb[], + struct ib_frmr_key *key, + struct netlink_ext_ack *extack) +{ + if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS]) + key->ats = nla_get_u8(tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS]); + + if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ACCESS_FLAGS]) + key->access_flags = nla_get_u32( + tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ACCESS_FLAGS]); + + if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_VENDOR_KEY]) + key->vendor_key = nla_get_u64( + tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_VENDOR_KEY]); + + if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS]) + key->num_dma_blocks = nla_get_u64( + tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS]); + + if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY]) + return -EINVAL; + + return 0; +} + +static int nldev_frmr_pools_set_pinned(struct ib_device *device, + struct nlattr *tb[], + struct netlink_ext_ack *extack) +{ + struct nlattr *key_tb[RDMA_NLDEV_ATTR_MAX]; + struct ib_frmr_key key = { 0 }; + u32 pinned_handles = 0; + int err = 0; + + pinned_handles = + nla_get_u32(tb[RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES]); + + if (!tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY]) + return -EINVAL; + + err = nla_parse_nested(key_tb, RDMA_NLDEV_ATTR_MAX - 1, + tb[RDMA_NLDEV_ATTR_FRMR_POOL_KEY], nldev_policy, + extack); + if (err) + return err; + + err = nldev_frmr_pools_parse_key(key_tb, &key, extack); + if (err) + return err; + + err = ib_frmr_pools_set_pinned(device, &key, pinned_handles); + + return err; +} + +static int nldev_frmr_pools_get_dumpit(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; + struct ib_frmr_pools *pools; + int err, ret = 0, idx = 0; + struct ib_frmr_pool *pool; + struct nlattr *table_attr; + struct nlattr *entry_attr; + bool show_details = false; + struct ib_device *device; + int start = cb->args[0]; + struct rb_node *node; + struct nlmsghdr *nlh; + bool filled = false; + + err = __nlmsg_parse(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, + nldev_policy, NL_VALIDATE_LIBERAL, NULL); + if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX]) + return -EINVAL; + + device = ib_device_get_by_index( + sock_net(skb->sk), nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX])); + if (!device) + return -EINVAL; + + if (tb[RDMA_NLDEV_ATTR_DRIVER_DETAILS]) + show_details = nla_get_u8(tb[RDMA_NLDEV_ATTR_DRIVER_DETAILS]); + + pools = device->frmr_pools; + if (!pools) { + ib_device_put(device); + return 0; + } + + nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, + RDMA_NLDEV_CMD_FRMR_POOLS_GET), + 0, NLM_F_MULTI); + + if (!nlh || fill_nldev_handle(skb, device)) { + ret = -EMSGSIZE; + goto err; + } + + table_attr = nla_nest_start_noflag(skb, RDMA_NLDEV_ATTR_FRMR_POOLS); + if (!table_attr) { + ret = -EMSGSIZE; + goto err; + } + + read_lock(&pools->rb_lock); + for (node = rb_first(&pools->rb_root); node; node = rb_next(node)) { + pool = rb_entry(node, struct ib_frmr_pool, node); + if (pool->key.kernel_vendor_key && !show_details) + continue; + + if (idx < start) { + idx++; + continue; + } + + filled = true; + + entry_attr = nla_nest_start_noflag( + skb, RDMA_NLDEV_ATTR_FRMR_POOL_ENTRY); + if (!entry_attr) { + ret = -EMSGSIZE; + goto end_msg; + } + + if (fill_frmr_pool_entry(skb, pool)) { + nla_nest_cancel(skb, entry_attr); + ret = -EMSGSIZE; + goto end_msg; + } + + nla_nest_end(skb, entry_attr); + idx++; + } +end_msg: + read_unlock(&pools->rb_lock); + + nla_nest_end(skb, table_attr); + nlmsg_end(skb, nlh); + cb->args[0] = idx; + + /* + * No more entries to fill, cancel the message and + * return 0 to mark end of dumpit. + */ + if (!filled) + goto err; + + ib_device_put(device); + return skb->len; + +err: + nlmsg_cancel(skb, nlh); + ib_device_put(device); + return ret; +} + +static int nldev_frmr_pools_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) +{ + struct ib_device *device; + struct nlattr **tb; + u32 aging_period; + int err; + + tb = kzalloc_objs(*tb, RDMA_NLDEV_ATTR_MAX, GFP_KERNEL); + if (!tb) + return -ENOMEM; + + err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, nldev_policy, + extack); + if (err) + goto free_tb; + + if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX]) { + err = -EINVAL; + goto free_tb; + } + + device = ib_device_get_by_index( + sock_net(skb->sk), nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX])); + if (!device) { + err = -EINVAL; + goto free_tb; + } + + if (tb[RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD]) { + aging_period = nla_get_u32( + tb[RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD]); + err = ib_frmr_pools_set_aging_period(device, aging_period); + goto done; + } + + if (tb[RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES]) + err = nldev_frmr_pools_set_pinned(device, tb, extack); + +done: + ib_device_put(device); +free_tb: + kfree(tb); + return err; +} + static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = { [RDMA_NLDEV_CMD_GET] = { .doit = nldev_get_doit, @@ -2743,6 +3034,13 @@ static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = { .doit = nldev_deldev, .flags = RDMA_NL_ADMIN_PERM, }, + [RDMA_NLDEV_CMD_FRMR_POOLS_GET] = { + .dump = nldev_frmr_pools_get_dumpit, + }, + [RDMA_NLDEV_CMD_FRMR_POOLS_SET] = { + .doit = nldev_frmr_pools_set_doit, + .flags = RDMA_NL_ADMIN_PERM, + }, }; static int fill_mon_netdev_rename(struct sk_buff *msg, diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c index 3e0a8b9cd288..5018ec837056 100644 --- a/drivers/infiniband/core/rdma_core.c +++ b/drivers/infiniband/core/rdma_core.c @@ -590,11 +590,11 @@ static void alloc_commit_idr_uobject(struct ib_uobject *uobj) void *old; /* - * We already allocated this IDR with a NULL object, so + * We already allocated this XArray entry with a NULL pointer, so * this shouldn't fail. * * NOTE: Storing the uobj transfers our kref on uobj to the XArray. - * It will be put by remove_commit_idr_uobject() + * It will be put by remove_handle_idr_uobject() */ old = xa_store(&ufile->idr, uobj->id, uobj, GFP_KERNEL); WARN_ON(old != NULL); diff --git a/drivers/infiniband/core/rdma_core.h b/drivers/infiniband/core/rdma_core.h index 55f1e3558856..269b393799ab 100644 --- a/drivers/infiniband/core/rdma_core.h +++ b/drivers/infiniband/core/rdma_core.h @@ -151,6 +151,9 @@ void uapi_compute_bundle_size(struct uverbs_api_ioctl_method *method_elm, unsigned int num_attrs); void uverbs_user_mmap_disassociate(struct ib_uverbs_file *ufile); +typedef int (*uverbs_api_ioctl_handler_fn)(struct uverbs_attr_bundle *attrs); +uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata); + extern const struct uapi_definition uverbs_def_obj_async_fd[]; extern const struct uapi_definition uverbs_def_obj_counters[]; extern const struct uapi_definition uverbs_def_obj_cq[]; diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index edc34c69f0f2..786fa1aa8e55 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -55,8 +55,7 @@ static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int d if (dirty) ib_dma_unmap_sgtable_attrs(dev, &umem->sgt_append.sgt, - DMA_BIDIRECTIONAL, - DMA_ATTR_REQUIRE_COHERENT); + DMA_BIDIRECTIONAL, umem->dma_attrs); for_each_sgtable_sg(&umem->sgt_append.sgt, sg, i) { unpin_user_page_range_dirty_lock(sg_page(sg), @@ -170,7 +169,6 @@ struct ib_umem *ib_umem_get(struct ib_device *device, unsigned long addr, unsigned long lock_limit; unsigned long new_pinned; unsigned long cur_base; - unsigned long dma_attr = DMA_ATTR_REQUIRE_COHERENT; struct mm_struct *mm; unsigned long npages; int pinned, ret; @@ -203,6 +201,10 @@ struct ib_umem *ib_umem_get(struct ib_device *device, unsigned long addr, umem->iova = addr; umem->writable = ib_access_writable(access); umem->owning_mm = mm = current->mm; + umem->dma_attrs = DMA_ATTR_REQUIRE_COHERENT; + if (access & IB_ACCESS_RELAXED_ORDERING) + umem->dma_attrs |= DMA_ATTR_WEAK_ORDERING; + mmgrab(mm); page_list = (struct page **) __get_free_page(GFP_KERNEL); @@ -255,11 +257,8 @@ struct ib_umem *ib_umem_get(struct ib_device *device, unsigned long addr, } } - if (access & IB_ACCESS_RELAXED_ORDERING) - dma_attr |= DMA_ATTR_WEAK_ORDERING; - ret = ib_dma_map_sgtable_attrs(device, &umem->sgt_append.sgt, - DMA_BIDIRECTIONAL, dma_attr); + DMA_BIDIRECTIONAL, umem->dma_attrs); if (ret) goto umem_release; goto out; @@ -284,7 +283,7 @@ EXPORT_SYMBOL(ib_umem_get); */ void ib_umem_release(struct ib_umem *umem) { - if (!umem) + if (IS_ERR_OR_NULL(umem)) return; if (umem->is_dmabuf) return ib_umem_dmabuf_release(to_ib_umem_dmabuf(umem)); diff --git a/drivers/infiniband/core/umem_dmabuf.c b/drivers/infiniband/core/umem_dmabuf.c index d7e1d2adb6e9..ad023c2d84d8 100644 --- a/drivers/infiniband/core/umem_dmabuf.c +++ b/drivers/infiniband/core/umem_dmabuf.c @@ -185,18 +185,44 @@ static struct dma_buf_attach_ops ib_umem_dmabuf_attach_pinned_ops = { .allow_peer2peer = true, }; -struct ib_umem_dmabuf * -ib_umem_dmabuf_get_pinned_with_dma_device(struct ib_device *device, - struct device *dma_device, - unsigned long offset, size_t size, - int fd, int access) +static void ib_umem_dmabuf_revoke_locked(struct dma_buf_attachment *attach) +{ + struct ib_umem_dmabuf *umem_dmabuf = attach->importer_priv; + + dma_resv_assert_held(attach->dmabuf->resv); + + if (umem_dmabuf->revoked) + return; + + if (umem_dmabuf->pinned_revoke) + umem_dmabuf->pinned_revoke(umem_dmabuf->private); + + ib_umem_dmabuf_unmap_pages(umem_dmabuf); + if (umem_dmabuf->pinned) { + dma_buf_unpin(umem_dmabuf->attach); + umem_dmabuf->pinned = 0; + } + umem_dmabuf->revoked = 1; +} + +static struct dma_buf_attach_ops ib_umem_dmabuf_attach_pinned_revocable_ops = { + .allow_peer2peer = true, + .invalidate_mappings = ib_umem_dmabuf_revoke_locked, +}; + +static struct ib_umem_dmabuf * +ib_umem_dmabuf_get_pinned_and_lock(struct ib_device *device, + struct device *dma_device, + unsigned long offset, + size_t size, int fd, int access, + const struct dma_buf_attach_ops *ops) { struct ib_umem_dmabuf *umem_dmabuf; int err; - umem_dmabuf = ib_umem_dmabuf_get_with_dma_device(device, dma_device, offset, - size, fd, access, - &ib_umem_dmabuf_attach_pinned_ops); + umem_dmabuf = + ib_umem_dmabuf_get_with_dma_device(device, dma_device, offset, + size, fd, access, ops); if (IS_ERR(umem_dmabuf)) return umem_dmabuf; @@ -209,7 +235,6 @@ ib_umem_dmabuf_get_pinned_with_dma_device(struct ib_device *device, err = ib_umem_dmabuf_map_pages(umem_dmabuf); if (err) goto err_release; - dma_resv_unlock(umem_dmabuf->attach->dmabuf->resv); return umem_dmabuf; @@ -218,8 +243,77 @@ err_release: ib_umem_release(&umem_dmabuf->umem); return ERR_PTR(err); } + +struct ib_umem_dmabuf * +ib_umem_dmabuf_get_pinned_with_dma_device(struct ib_device *device, + struct device *dma_device, + unsigned long offset, size_t size, + int fd, int access) +{ + struct ib_umem_dmabuf *umem_dmabuf = + ib_umem_dmabuf_get_pinned_and_lock(device, dma_device, offset, + size, fd, access, + &ib_umem_dmabuf_attach_pinned_ops); + if (IS_ERR(umem_dmabuf)) + return umem_dmabuf; + + dma_resv_unlock(umem_dmabuf->attach->dmabuf->resv); + return umem_dmabuf; +} EXPORT_SYMBOL(ib_umem_dmabuf_get_pinned_with_dma_device); +/** + * ib_umem_dmabuf_get_pinned_revocable_and_lock - Map & pin a revocable dmabuf + * @device: IB device. + * @offset: Start offset. + * @size: Length. + * @fd: dmabuf fd. + * @access: Access flags. + * + * Obtains a umem from a dmabuf for drivers/devices that can support revocation. + * + * Returns with dma_resv_lock held upon success. The driver must set the revoke + * callback prior to unlock by calling ib_umem_dmabuf_set_revoke_locked(). + * + * When a revocation occurs, the revoke callback will be called. The driver must + * ensure that the region is no longer accessed when the callback returns. Any + * subsequent access attempts should also probably cause an AE for MRs. + * + * If the umem is used for an MR, the driver must ensure that the key remains in + * use such that it cannot be obtained by a new region until this region is + * fully deregistered (i.e., ibv_dereg_mr). If a driver needs to serialize with + * revoke calls, it can use dma_resv_lock. + * + * If successful, then the revoke callback may be called at any time and will + * also be called automatically upon ib_umem_release (serialized). The revoke + * callback will be called one time at most. + * + * Return: A pointer to ib_umem_dmabuf on success, or an ERR_PTR on failure. + */ +struct ib_umem_dmabuf * +ib_umem_dmabuf_get_pinned_revocable_and_lock(struct ib_device *device, + unsigned long offset, size_t size, + int fd, int access) +{ + const struct dma_buf_attach_ops *ops = + &ib_umem_dmabuf_attach_pinned_revocable_ops; + + return ib_umem_dmabuf_get_pinned_and_lock(device, device->dma_device, + offset, size, fd, access, + ops); +} +EXPORT_SYMBOL(ib_umem_dmabuf_get_pinned_revocable_and_lock); + +void ib_umem_dmabuf_set_revoke_locked(struct ib_umem_dmabuf *umem_dmabuf, + void (*revoke)(void *priv), void *priv) +{ + dma_resv_assert_held(umem_dmabuf->attach->dmabuf->resv); + + umem_dmabuf->pinned_revoke = revoke; + umem_dmabuf->private = priv; +} +EXPORT_SYMBOL(ib_umem_dmabuf_set_revoke_locked); + struct ib_umem_dmabuf *ib_umem_dmabuf_get_pinned(struct ib_device *device, unsigned long offset, size_t size, int fd, @@ -230,20 +324,28 @@ struct ib_umem_dmabuf *ib_umem_dmabuf_get_pinned(struct ib_device *device, } EXPORT_SYMBOL(ib_umem_dmabuf_get_pinned); +void ib_umem_dmabuf_revoke_lock(struct ib_umem_dmabuf *umem_dmabuf) +{ + struct dma_buf *dmabuf = umem_dmabuf->attach->dmabuf; + + dma_resv_lock(dmabuf->resv, NULL); +} +EXPORT_SYMBOL(ib_umem_dmabuf_revoke_lock); + +void ib_umem_dmabuf_revoke_unlock(struct ib_umem_dmabuf *umem_dmabuf) +{ + struct dma_buf *dmabuf = umem_dmabuf->attach->dmabuf; + + dma_resv_unlock(dmabuf->resv); +} +EXPORT_SYMBOL(ib_umem_dmabuf_revoke_unlock); + void ib_umem_dmabuf_revoke(struct ib_umem_dmabuf *umem_dmabuf) { struct dma_buf *dmabuf = umem_dmabuf->attach->dmabuf; dma_resv_lock(dmabuf->resv, NULL); - if (umem_dmabuf->revoked) - goto end; - ib_umem_dmabuf_unmap_pages(umem_dmabuf); - if (umem_dmabuf->pinned) { - dma_buf_unpin(umem_dmabuf->attach); - umem_dmabuf->pinned = 0; - } - umem_dmabuf->revoked = 1; -end: + ib_umem_dmabuf_revoke_locked(umem_dmabuf->attach); dma_resv_unlock(dmabuf->resv); } EXPORT_SYMBOL(ib_umem_dmabuf_revoke); diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 758ed4ae5f7a..91a62d2ade4d 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -83,28 +83,16 @@ static int uverbs_response(struct uverbs_attr_bundle *attrs, const void *resp, return 0; } -/* - * Copy a request from userspace. If the provided 'req' is larger than the - * user buffer then the user buffer is zero extended into the 'req'. If 'req' - * is smaller than the user buffer then the uncopied bytes in the user buffer - * must be zero. - */ static int uverbs_request(struct uverbs_attr_bundle *attrs, void *req, size_t req_len) { - if (copy_from_user(req, attrs->ucore.inbuf, - min(attrs->ucore.inlen, req_len))) - return -EFAULT; + int ret; - if (attrs->ucore.inlen < req_len) { - memset(req + attrs->ucore.inlen, 0, - req_len - attrs->ucore.inlen); - } else if (attrs->ucore.inlen > req_len) { - if (!ib_is_buffer_cleared(attrs->ucore.inbuf + req_len, - attrs->ucore.inlen - req_len)) - return -EOPNOTSUPP; - } - return 0; + ret = copy_struct_from_user(req, req_len, attrs->ucore.inbuf, + attrs->ucore.inlen); + if (ret == -E2BIG) + ret = -EOPNOTSUPP; + return ret; } /* @@ -790,6 +778,7 @@ static int ib_uverbs_rereg_mr(struct uverbs_attr_bundle *attrs) struct ib_pd *orig_pd; struct ib_pd *new_pd; struct ib_mr *new_mr; + u32 lkey, rkey; ret = uverbs_request(attrs, &cmd, sizeof(cmd)); if (ret) @@ -858,6 +847,8 @@ static int ib_uverbs_rereg_mr(struct uverbs_attr_bundle *attrs) new_mr->uobject = uobj; atomic_inc(&new_pd->usecnt); new_uobj->object = new_mr; + lkey = new_mr->lkey; + rkey = new_mr->rkey; rdma_restrack_new(&new_mr->res, RDMA_RESTRACK_MR); rdma_restrack_set_name(&new_mr->res, NULL); @@ -883,11 +874,13 @@ static int ib_uverbs_rereg_mr(struct uverbs_attr_bundle *attrs) mr->iova = cmd.hca_va; mr->length = cmd.length; } + lkey = mr->lkey; + rkey = mr->rkey; } memset(&resp, 0, sizeof(resp)); - resp.lkey = mr->lkey; - resp.rkey = mr->rkey; + resp.lkey = lkey; + resp.rkey = rkey; ret = uverbs_response(attrs, &resp, sizeof(resp)); @@ -1032,6 +1025,9 @@ static int create_cq(struct uverbs_attr_bundle *attrs, if (cmd->comp_vector >= attrs->ufile->device->num_comp_vectors) return -EINVAL; + if (!cmd->cqe) + return -EINVAL; + obj = (struct ib_ucq_object *)uobj_alloc(UVERBS_OBJECT_CQ, attrs, &ib_dev); if (IS_ERR(obj)) @@ -1068,7 +1064,10 @@ static int create_cq(struct uverbs_attr_bundle *attrs, rdma_restrack_new(&cq->res, RDMA_RESTRACK_CQ); rdma_restrack_set_name(&cq->res, NULL); - ret = ib_dev->ops.create_cq(cq, &attr, attrs); + if (ib_dev->ops.create_user_cq) + ret = ib_dev->ops.create_user_cq(cq, &attr, attrs); + else + ret = ib_dev->ops.create_cq(cq, &attr, attrs); if (ret) goto err_free; rdma_restrack_add(&cq->res); @@ -1085,6 +1084,7 @@ static int create_cq(struct uverbs_attr_bundle *attrs, return uverbs_response(attrs, &resp, sizeof(resp)); err_free: + ib_umem_release(cq->umem); rdma_restrack_put(&cq->res); kfree(cq); err_file: @@ -1143,11 +1143,14 @@ static int ib_uverbs_resize_cq(struct uverbs_attr_bundle *attrs) if (ret) return ret; + if (!cmd.cqe) + return -EINVAL; + cq = uobj_get_obj_read(cq, UVERBS_OBJECT_CQ, cmd.cq_handle, attrs); if (IS_ERR(cq)) return PTR_ERR(cq); - ret = cq->device->ops.resize_cq(cq, cmd.cqe, &attrs->driver_udata); + ret = cq->device->ops.resize_user_cq(cq, cmd.cqe, &attrs->driver_udata); if (ret) goto out; @@ -3806,7 +3809,7 @@ const struct uapi_definition uverbs_def_write_intf[] = { UAPI_DEF_WRITE_UDATA_IO( struct ib_uverbs_resize_cq, struct ib_uverbs_resize_cq_resp), - UAPI_DEF_METHOD_NEEDS_FN(resize_cq)), + UAPI_DEF_METHOD_NEEDS_FN(resize_user_cq)), DECLARE_UVERBS_WRITE_EX( IB_USER_VERBS_EX_CMD_CREATE_CQ, ib_uverbs_ex_create_cq, diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c index f37bb447c230..b61af625e679 100644 --- a/drivers/infiniband/core/uverbs_ioctl.c +++ b/drivers/infiniband/core/uverbs_ioctl.c @@ -70,6 +70,19 @@ struct bundle_priv { u64 internal_buffer[32]; }; +uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata) +{ + struct uverbs_attr_bundle *bundle = + rdma_udata_to_uverbs_attr_bundle(udata); + struct bundle_priv *pbundle = + container_of(&bundle->hdr, struct bundle_priv, bundle); + + lockdep_assert_held(&bundle->ufile->device->disassociate_srcu); + + return srcu_dereference(pbundle->method_elm->handler, + &bundle->ufile->device->disassociate_srcu); +} + /* * Each method has an absolute minimum amount of memory it needs to allocate, * precompute that amount and determine if the onstack memory can be used or @@ -847,3 +860,77 @@ void uverbs_finalize_uobj_create(const struct uverbs_attr_bundle *bundle, pbundle->uobj_hw_obj_valid); } EXPORT_SYMBOL(uverbs_finalize_uobj_create); + +int _ib_copy_validate_udata_in(struct ib_udata *udata, void *req, + size_t kernel_size, size_t minimum_size) +{ + int err; + + if (udata->inlen < minimum_size) { + ibdev_dbg( + rdma_udata_to_dev(udata), + "System call driver input udata too small (%zu < %zu) for ioctl %ps called by %pSR\n", + udata->inlen, minimum_size, + uverbs_get_handler_fn(udata), + __builtin_return_address(0)); + return -EINVAL; + } + + err = copy_struct_from_user(req, kernel_size, udata->inbuf, + udata->inlen); + if (err) { + if (err == -E2BIG) { + ibdev_dbg( + rdma_udata_to_dev(udata), + "System call driver input udata not zero from %zu -> %zu for ioctl %ps called by %pSR\n", + minimum_size, udata->inlen, + uverbs_get_handler_fn(udata), + __builtin_return_address(0)); + return -EOPNOTSUPP; + } + ibdev_dbg( + rdma_udata_to_dev(udata), + "System call driver input udata EFAULT for ioctl %ps called by %pSR\n", + uverbs_get_handler_fn(udata), + __builtin_return_address(0)); + return err; + } + return 0; +} +EXPORT_SYMBOL(_ib_copy_validate_udata_in); + +int _ib_copy_validate_udata_cm_fail(struct ib_udata *udata, u64 req_cm, + u64 valid_cm) +{ + ibdev_dbg( + rdma_udata_to_dev(udata), + "System call driver input udata has unsupported comp_mask %llx & ~%llx = %llx for ioctl %ps called by %pSR\n", + req_cm, valid_cm, req_cm & ~valid_cm, + uverbs_get_handler_fn(udata), __builtin_return_address(0)); + return -EOPNOTSUPP; +} +EXPORT_SYMBOL(_ib_copy_validate_udata_cm_fail); + +int _ib_respond_udata(struct ib_udata *udata, const void *src, size_t len) +{ + size_t copy_len; + + /* 0 length copy_len is a NOP for copy_to_user() and doesn't fail. */ + copy_len = min(len, udata->outlen); + if (copy_to_user(udata->outbuf, src, copy_len)) + goto err_fault; + if (copy_len < udata->outlen) { + if (clear_user(udata->outbuf + copy_len, + udata->outlen - copy_len)) + goto err_fault; + } + return 0; +err_fault: + ibdev_dbg( + rdma_udata_to_dev(udata), + "System call driver out udata has EFAULT (%zu into %zu) for ioctl %ps called by %pSR\n", + len, udata->outlen, uverbs_get_handler_fn(udata), + __builtin_return_address(0)); + return -EFAULT; +} +EXPORT_SYMBOL(_ib_respond_udata); diff --git a/drivers/infiniband/core/uverbs_std_types_cq.c b/drivers/infiniband/core/uverbs_std_types_cq.c index fab5d914029d..79b51f60ce2a 100644 --- a/drivers/infiniband/core/uverbs_std_types_cq.c +++ b/drivers/infiniband/core/uverbs_std_types_cq.c @@ -78,17 +78,21 @@ static int UVERBS_HANDLER(UVERBS_METHOD_CQ_CREATE)( int buffer_fd; int ret; - if ((!ib_dev->ops.create_cq && !ib_dev->ops.create_cq_umem) || !ib_dev->ops.destroy_cq) + if ((!ib_dev->ops.create_cq && !ib_dev->ops.create_user_cq) || + !ib_dev->ops.destroy_cq) return -EOPNOTSUPP; ret = uverbs_copy_from(&attr.comp_vector, attrs, UVERBS_ATTR_CREATE_CQ_COMP_VECTOR); - if (!ret) - ret = uverbs_copy_from(&attr.cqe, attrs, - UVERBS_ATTR_CREATE_CQ_CQE); - if (!ret) - ret = uverbs_copy_from(&user_handle, attrs, - UVERBS_ATTR_CREATE_CQ_USER_HANDLE); + if (ret) + return ret; + + ret = uverbs_copy_from(&attr.cqe, attrs, UVERBS_ATTR_CREATE_CQ_CQE); + if (ret || !attr.cqe) + return ret ? : -EINVAL; + + ret = uverbs_copy_from(&user_handle, attrs, + UVERBS_ATTR_CREATE_CQ_USER_HANDLE); if (ret) return ret; @@ -130,7 +134,7 @@ static int UVERBS_HANDLER(UVERBS_METHOD_CQ_CREATE)( if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_CREATE_CQ_BUFFER_FD) || uverbs_attr_is_valid(attrs, UVERBS_ATTR_CREATE_CQ_BUFFER_OFFSET) || - !ib_dev->ops.create_cq_umem) { + !ib_dev->ops.create_user_cq) { ret = -EINVAL; goto err_event_file; } @@ -155,7 +159,7 @@ static int UVERBS_HANDLER(UVERBS_METHOD_CQ_CREATE)( goto err_event_file; if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_CREATE_CQ_BUFFER_VA) || - !ib_dev->ops.create_cq_umem) { + !ib_dev->ops.create_user_cq) { ret = -EINVAL; goto err_event_file; } @@ -168,8 +172,7 @@ static int UVERBS_HANDLER(UVERBS_METHOD_CQ_CREATE)( } umem = &umem_dmabuf->umem; } else if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_CREATE_CQ_BUFFER_OFFSET) || - uverbs_attr_is_valid(attrs, UVERBS_ATTR_CREATE_CQ_BUFFER_LENGTH) || - !ib_dev->ops.create_cq) { + uverbs_attr_is_valid(attrs, UVERBS_ATTR_CREATE_CQ_BUFFER_LENGTH)) { ret = -EINVAL; goto err_event_file; } @@ -186,16 +189,26 @@ static int UVERBS_HANDLER(UVERBS_METHOD_CQ_CREATE)( cq->comp_handler = ib_uverbs_comp_handler; cq->event_handler = ib_uverbs_cq_event_handler; cq->cq_context = ev_file ? &ev_file->ev_queue : NULL; + /* + * If UMEM is not provided here, legacy drivers will set it during + * CQ creation based on their internal udata. + */ + cq->umem = umem; atomic_set(&cq->usecnt, 0); rdma_restrack_new(&cq->res, RDMA_RESTRACK_CQ); rdma_restrack_set_name(&cq->res, NULL); - ret = umem ? ib_dev->ops.create_cq_umem(cq, &attr, umem, attrs) : - ib_dev->ops.create_cq(cq, &attr, attrs); + if (ib_dev->ops.create_user_cq) + ret = ib_dev->ops.create_user_cq(cq, &attr, attrs); + else + ret = ib_dev->ops.create_cq(cq, &attr, attrs); if (ret) goto err_free; + /* Check that driver didn't overrun existing umem */ + WARN_ON(umem && cq->umem != umem); + obj->uevent.uobject.object = cq; obj->uevent.uobject.user_handle = user_handle; rdma_restrack_add(&cq->res); @@ -206,7 +219,7 @@ static int UVERBS_HANDLER(UVERBS_METHOD_CQ_CREATE)( return ret; err_free: - ib_umem_release(umem); + ib_umem_release(cq->umem); rdma_restrack_put(&cq->res); kfree(cq); err_event_file: diff --git a/drivers/infiniband/core/uverbs_std_types_device.c b/drivers/infiniband/core/uverbs_std_types_device.c index a28f9f21bed8..12ca15739cd2 100644 --- a/drivers/infiniband/core/uverbs_std_types_device.c +++ b/drivers/infiniband/core/uverbs_std_types_device.c @@ -247,13 +247,21 @@ static int UVERBS_HANDLER(UVERBS_METHOD_GET_CONTEXT)( { u32 num_comp = attrs->ufile->device->num_comp_vectors; u64 core_support = IB_UVERBS_CORE_SUPPORT_OPTIONAL_MR_ACCESS; + struct ib_device *ib_dev; int ret; + ib_dev = srcu_dereference(attrs->ufile->device->ib_dev, + &attrs->ufile->device->disassociate_srcu); + if (!ib_dev) + return -EIO; + ret = uverbs_copy_to(attrs, UVERBS_ATTR_GET_CONTEXT_NUM_COMP_VECTORS, &num_comp, sizeof(num_comp)); if (IS_UVERBS_COPY_ERR(ret)) return ret; + if (ib_dev->ops.uverbs_robust_udata) + core_support |= IB_UVERBS_CORE_SUPPORT_ROBUST_UDATA; ret = uverbs_copy_to(attrs, UVERBS_ATTR_GET_CONTEXT_CORE_SUPPORT, &core_support, sizeof(core_support)); if (IS_UVERBS_COPY_ERR(ret)) diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 575b4a4b200b..bac87de9cc67 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -2202,8 +2203,10 @@ struct ib_cq *__ib_create_cq(struct ib_device *device, if (!cq) return ERR_PTR(-ENOMEM); + if (WARN_ON_ONCE(!cq_attr->cqe)) + return ERR_PTR(-EINVAL); + cq->device = device; - cq->uobject = NULL; cq->comp_handler = comp_handler; cq->event_handler = event_handler; cq->cq_context = cq_context; @@ -2218,6 +2221,11 @@ struct ib_cq *__ib_create_cq(struct ib_device *device, kfree(cq); return ERR_PTR(ret); } + /* + * We are in kernel verbs flow and drivers are not allowed + * to set umem pointer, it needs to stay NULL. + */ + WARN_ON_ONCE(cq->umem); rdma_restrack_add(&cq->res); return cq; @@ -2249,22 +2257,13 @@ int ib_destroy_cq_user(struct ib_cq *cq, struct ib_udata *udata) if (ret) return ret; + ib_umem_release(cq->umem); rdma_restrack_del(&cq->res); kfree(cq); return ret; } EXPORT_SYMBOL(ib_destroy_cq_user); -int ib_resize_cq(struct ib_cq *cq, int cqe) -{ - if (cq->shared) - return -EOPNOTSUPP; - - return cq->device->ops.resize_cq ? - cq->device->ops.resize_cq(cq, cqe, NULL) : -EOPNOTSUPP; -} -EXPORT_SYMBOL(ib_resize_cq); - /* Memory regions */ struct ib_mr *ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, @@ -3154,44 +3153,6 @@ int rdma_init_netdev(struct ib_device *device, u32 port_num, } EXPORT_SYMBOL(rdma_init_netdev); -void __rdma_block_iter_start(struct ib_block_iter *biter, - struct scatterlist *sglist, unsigned int nents, - unsigned long pgsz) -{ - memset(biter, 0, sizeof(struct ib_block_iter)); - biter->__sg = sglist; - biter->__sg_nents = nents; - - /* Driver provides best block size to use */ - biter->__pg_bit = __fls(pgsz); -} -EXPORT_SYMBOL(__rdma_block_iter_start); - -bool __rdma_block_iter_next(struct ib_block_iter *biter) -{ - unsigned int block_offset; - unsigned int delta; - - if (!biter->__sg_nents || !biter->__sg) - return false; - - biter->__dma_addr = sg_dma_address(biter->__sg) + biter->__sg_advance; - block_offset = biter->__dma_addr & (BIT_ULL(biter->__pg_bit) - 1); - delta = BIT_ULL(biter->__pg_bit) - block_offset; - - while (biter->__sg_nents && biter->__sg && - sg_dma_len(biter->__sg) - biter->__sg_advance <= delta) { - delta -= sg_dma_len(biter->__sg) - biter->__sg_advance; - biter->__sg_advance = 0; - biter->__sg = sg_next(biter->__sg); - biter->__sg_nents--; - } - biter->__sg_advance += delta; - - return true; -} -EXPORT_SYMBOL(__rdma_block_iter_next); - /** * rdma_alloc_hw_stats_struct - Helper function to allocate dynamic struct * for the drivers. diff --git a/drivers/infiniband/hw/bnxt_re/Makefile b/drivers/infiniband/hw/bnxt_re/Makefile index f63417d2ccc6..1533c079c9da 100644 --- a/drivers/infiniband/hw/bnxt_re/Makefile +++ b/drivers/infiniband/hw/bnxt_re/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_INFINIBAND_BNXT_RE) += bnxt_re.o bnxt_re-y := main.o ib_verbs.o \ qplib_res.o qplib_rcfw.o \ qplib_sp.o qplib_fp.o hw_counters.o \ - debugfs.o + debugfs.o uapi.o diff --git a/drivers/infiniband/hw/bnxt_re/debugfs.c b/drivers/infiniband/hw/bnxt_re/debugfs.c index a2ad79c3bbd0..5fed2cf66be3 100644 --- a/drivers/infiniband/hw/bnxt_re/debugfs.c +++ b/drivers/infiniband/hw/bnxt_re/debugfs.c @@ -10,8 +10,8 @@ #include #include #include +#include -#include "bnxt_ulp.h" #include "roce_hsi.h" #include "qplib_res.h" #include "qplib_sp.h" diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 5c5ecfacf506..7ed294516b7e 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -187,9 +187,12 @@ int bnxt_re_query_device(struct ib_device *ibdev, struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); struct bnxt_qplib_dev_attr *dev_attr = rdev->dev_attr; struct bnxt_re_query_device_ex_resp resp = {}; - size_t outlen = (udata) ? udata->outlen : 0; int rc = 0; + rc = ib_is_udata_in_empty(udata); + if (rc) + return rc; + memset(ib_attr, 0, sizeof(*ib_attr)); memcpy(&ib_attr->fw_ver, dev_attr->fw_ver, min(sizeof(dev_attr->fw_ver), @@ -254,8 +257,7 @@ int bnxt_re_query_device(struct ib_device *ibdev, ib_attr->max_pkeys = 1; ib_attr->local_ca_ack_delay = BNXT_RE_DEFAULT_ACK_DELAY; - if ((offsetofend(typeof(resp), packet_pacing_caps) <= outlen) && - _is_modify_qp_rate_limit_supported(dev_attr->dev_cap_flags2)) { + if (_is_modify_qp_rate_limit_supported(dev_attr->dev_cap_flags2)) { resp.packet_pacing_caps.qp_rate_limit_min = dev_attr->rate_limit_min; resp.packet_pacing_caps.qp_rate_limit_max = @@ -263,11 +265,7 @@ int bnxt_re_query_device(struct ib_device *ibdev, resp.packet_pacing_caps.supported_qpts = 1 << IB_QPT_RC; } - if (outlen) - rc = ib_copy_to_udata(udata, &resp, - min(sizeof(resp), outlen)); - - return rc; + return ib_respond_udata(udata, resp); } int bnxt_re_modify_device(struct ib_device *ibdev, @@ -644,7 +642,7 @@ fail: return rc; } -static struct bnxt_re_user_mmap_entry* +struct bnxt_re_user_mmap_entry* bnxt_re_mmap_entry_insert(struct bnxt_re_ucontext *uctx, u64 mem_offset, enum bnxt_re_mmap_flag mmap_flag, u64 *offset) { @@ -692,6 +690,11 @@ int bnxt_re_dealloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata) { struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); struct bnxt_re_dev *rdev = pd->rdev; + int ret; + + ret = ib_is_udata_in_empty(udata); + if (ret) + return ret; if (udata) { rdma_user_mmap_entry_remove(pd->pd_db_mmap); @@ -706,7 +709,7 @@ int bnxt_re_dealloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata) &pd->qplib_pd)) atomic_dec(&rdev->stats.res.pd_count); } - return 0; + return ib_respond_empty_udata(udata); } int bnxt_re_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) @@ -720,6 +723,10 @@ int bnxt_re_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) u32 active_pds; int rc = 0; + rc = ib_is_udata_in_empty(udata); + if (rc) + return rc; + pd->rdev = rdev; if (bnxt_qplib_alloc_pd(&rdev->qplib_res, &pd->qplib_pd)) { ibdev_err(&rdev->ibdev, "Failed to allocate HW PD"); @@ -756,7 +763,7 @@ int bnxt_re_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) pd->pd_db_mmap = &entry->rdma_entry; - rc = ib_copy_to_udata(udata, &resp, min(sizeof(resp), udata->outlen)); + rc = ib_respond_udata(udata, resp); if (rc) { rdma_user_mmap_entry_remove(pd->pd_db_mmap); rc = -EFAULT; @@ -834,6 +841,10 @@ int bnxt_re_create_ah(struct ib_ah *ib_ah, struct rdma_ah_init_attr *init_attr, u8 nw_type; int rc; + rc = ib_is_udata_in_empty(udata); + if (rc) + return rc; + if (!(rdma_ah_get_ah_flags(ah_attr) & IB_AH_GRH)) { ibdev_err(&rdev->ibdev, "Failed to alloc AH: GRH not set"); return -EINVAL; @@ -887,7 +898,7 @@ int bnxt_re_create_ah(struct ib_ah *ib_ah, struct rdma_ah_init_attr *init_attr, if (active_ahs > rdev->stats.res.ah_watermark) rdev->stats.res.ah_watermark = active_ahs; - return 0; + return ib_respond_empty_udata(udata); } int bnxt_re_query_ah(struct ib_ah *ib_ah, struct rdma_ah_attr *ah_attr) @@ -984,6 +995,12 @@ static void bnxt_re_del_unique_gid(struct bnxt_re_dev *rdev) dev_err(rdev_to_dev(rdev), "Failed to delete unique GID, rc: %d\n", rc); } +static void bnxt_re_qp_free_umem(struct bnxt_re_qp *qp) +{ + ib_umem_release(qp->rumem); + ib_umem_release(qp->sumem); +} + /* Queue Pairs */ int bnxt_re_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata) { @@ -995,6 +1012,10 @@ int bnxt_re_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata) unsigned int flags; int rc; + rc = ib_is_udata_in_empty(udata); + if (rc) + return rc; + bnxt_re_debug_rem_qpinfo(rdev, qp); bnxt_qplib_flush_cqn_wq(&qp->qplib_qp); @@ -1026,8 +1047,7 @@ int bnxt_re_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata) if (qp->qplib_qp.type == CMDQ_CREATE_QP_TYPE_RAW_ETHERTYPE) bnxt_re_del_unique_gid(rdev); - ib_umem_release(qp->rumem); - ib_umem_release(qp->sumem); + bnxt_re_qp_free_umem(qp); /* Flush all the entries of notification queue associated with * given qp. @@ -1038,7 +1058,7 @@ int bnxt_re_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata) if (scq_nq != rcq_nq) bnxt_re_synchronize_nq(rcq_nq); - return 0; + return ib_respond_empty_udata(udata); } static u8 __from_ib_qp_type(enum ib_qp_type type) @@ -1171,6 +1191,7 @@ static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, } qplib_qp->dpi = &cntx->dpi; + qplib_qp->is_user = true; return 0; rqfail: ib_umem_release(qp->sumem); @@ -1228,6 +1249,114 @@ fail: return NULL; } +static int bnxt_re_qp_alloc_init_xrrq(struct bnxt_re_qp *qp) +{ + struct bnxt_qplib_res *res = &qp->rdev->qplib_res; + struct bnxt_qplib_qp *qplib_qp = &qp->qplib_qp; + struct bnxt_qplib_hwq_attr hwq_attr = {}; + struct bnxt_qplib_sg_info sginfo = {}; + struct bnxt_qplib_hwq *irrq, *orrq; + int rc, req_size; + + orrq = &qplib_qp->orrq; + orrq->max_elements = + ORD_LIMIT_TO_ORRQ_SLOTS(qplib_qp->max_rd_atomic); + req_size = orrq->max_elements * + BNXT_QPLIB_MAX_ORRQE_ENTRY_SIZE + PAGE_SIZE - 1; + req_size &= ~(PAGE_SIZE - 1); + sginfo.pgsize = req_size; + sginfo.pgshft = PAGE_SHIFT; + + hwq_attr.res = res; + hwq_attr.sginfo = &sginfo; + hwq_attr.depth = orrq->max_elements; + hwq_attr.stride = BNXT_QPLIB_MAX_ORRQE_ENTRY_SIZE; + hwq_attr.aux_stride = 0; + hwq_attr.aux_depth = 0; + hwq_attr.type = HWQ_TYPE_CTX; + rc = bnxt_qplib_alloc_init_hwq(orrq, &hwq_attr); + if (rc) + return rc; + + irrq = &qplib_qp->irrq; + irrq->max_elements = + IRD_LIMIT_TO_IRRQ_SLOTS(qplib_qp->max_dest_rd_atomic); + req_size = irrq->max_elements * + BNXT_QPLIB_MAX_IRRQE_ENTRY_SIZE + PAGE_SIZE - 1; + req_size &= ~(PAGE_SIZE - 1); + sginfo.pgsize = req_size; + hwq_attr.sginfo = &sginfo; + hwq_attr.depth = irrq->max_elements; + hwq_attr.stride = BNXT_QPLIB_MAX_IRRQE_ENTRY_SIZE; + rc = bnxt_qplib_alloc_init_hwq(irrq, &hwq_attr); + if (rc) + goto free_orrq_hwq; + return 0; +free_orrq_hwq: + bnxt_qplib_free_hwq(res, orrq); + return rc; +} + +static int bnxt_re_setup_qp_hwqs(struct bnxt_re_qp *qp) +{ + struct bnxt_qplib_res *res = &qp->rdev->qplib_res; + struct bnxt_qplib_qp *qplib_qp = &qp->qplib_qp; + struct bnxt_qplib_hwq_attr hwq_attr = {}; + struct bnxt_qplib_q *sq = &qplib_qp->sq; + struct bnxt_qplib_q *rq = &qplib_qp->rq; + u8 wqe_mode = qplib_qp->wqe_mode; + u8 pg_sz_lvl; + int rc; + + hwq_attr.res = res; + hwq_attr.sginfo = &sq->sg_info; + hwq_attr.stride = bnxt_qplib_get_stride(); + hwq_attr.depth = bnxt_qplib_get_depth(sq, wqe_mode, true); + hwq_attr.aux_stride = qplib_qp->psn_sz; + hwq_attr.aux_depth = (qplib_qp->psn_sz) ? + bnxt_qplib_set_sq_size(sq, wqe_mode) : 0; + if (qplib_qp->is_host_msn_tbl && qplib_qp->psn_sz) + hwq_attr.aux_depth = qplib_qp->msn_tbl_sz; + hwq_attr.type = HWQ_TYPE_QUEUE; + rc = bnxt_qplib_alloc_init_hwq(&sq->hwq, &hwq_attr); + if (rc) + return rc; + + pg_sz_lvl = bnxt_qplib_base_pg_size(&sq->hwq) << CMDQ_CREATE_QP_SQ_PG_SIZE_SFT; + pg_sz_lvl |= ((sq->hwq.level & CMDQ_CREATE_QP_SQ_LVL_MASK) << + CMDQ_CREATE_QP_SQ_LVL_SFT); + sq->hwq.pg_sz_lvl = pg_sz_lvl; + + hwq_attr.res = res; + hwq_attr.sginfo = &rq->sg_info; + hwq_attr.stride = bnxt_qplib_get_stride(); + hwq_attr.depth = bnxt_qplib_get_depth(rq, qplib_qp->wqe_mode, false); + hwq_attr.aux_stride = 0; + hwq_attr.aux_depth = 0; + hwq_attr.type = HWQ_TYPE_QUEUE; + rc = bnxt_qplib_alloc_init_hwq(&rq->hwq, &hwq_attr); + if (rc) + goto free_sq_hwq; + pg_sz_lvl = bnxt_qplib_base_pg_size(&rq->hwq) << + CMDQ_CREATE_QP_RQ_PG_SIZE_SFT; + pg_sz_lvl |= ((rq->hwq.level & CMDQ_CREATE_QP_RQ_LVL_MASK) << + CMDQ_CREATE_QP_RQ_LVL_SFT); + rq->hwq.pg_sz_lvl = pg_sz_lvl; + + if (qplib_qp->psn_sz) { + rc = bnxt_re_qp_alloc_init_xrrq(qp); + if (rc) + goto free_rq_hwq; + } + + return 0; +free_rq_hwq: + bnxt_qplib_free_hwq(res, &rq->hwq); +free_sq_hwq: + bnxt_qplib_free_hwq(res, &sq->hwq); + return rc; +} + static struct bnxt_re_qp *bnxt_re_create_shadow_qp (struct bnxt_re_pd *pd, struct bnxt_qplib_res *qp1_res, @@ -1249,6 +1378,7 @@ static struct bnxt_re_qp *bnxt_re_create_shadow_qp qp->qplib_qp.pd = &pd->qplib_pd; qp->qplib_qp.qp_handle = (u64)(unsigned long)(&qp->qplib_qp); qp->qplib_qp.type = IB_QPT_UD; + qp->qplib_qp.cctx = rdev->chip_ctx; qp->qplib_qp.max_inline_data = 0; qp->qplib_qp.sig_type = true; @@ -1281,10 +1411,14 @@ static struct bnxt_re_qp *bnxt_re_create_shadow_qp qp->qplib_qp.rq_hdr_buf_size = BNXT_QPLIB_MAX_GRH_HDR_SIZE_IPV6; qp->qplib_qp.dpi = &rdev->dpi_privileged; - rc = bnxt_qplib_create_qp(qp1_res, &qp->qplib_qp); + rc = bnxt_re_setup_qp_hwqs(qp); if (rc) goto fail; + rc = bnxt_qplib_create_qp(qp1_res, &qp->qplib_qp); + if (rc) + goto free_hwq; + spin_lock_init(&qp->sq_lock); INIT_LIST_HEAD(&qp->list); mutex_lock(&rdev->qp_lock); @@ -1292,6 +1426,9 @@ static struct bnxt_re_qp *bnxt_re_create_shadow_qp atomic_inc(&rdev->stats.res.qp_count); mutex_unlock(&rdev->qp_lock); return qp; + +free_hwq: + bnxt_qplib_free_qp_res(&rdev->qplib_res, &qp->qplib_qp); fail: kfree(qp); return NULL; @@ -1305,7 +1442,6 @@ static int bnxt_re_init_rq_attr(struct bnxt_re_qp *qp, struct bnxt_qplib_qp *qplqp; struct bnxt_re_dev *rdev; struct bnxt_qplib_q *rq; - int entries; rdev = qp->rdev; qplqp = &qp->qplib_qp; @@ -1328,8 +1464,9 @@ static int bnxt_re_init_rq_attr(struct bnxt_re_qp *qp, /* Allocate 1 more than what's provided so posting max doesn't * mean empty. */ - entries = bnxt_re_init_depth(init_attr->cap.max_recv_wr + 1, uctx); - rq->max_wqe = min_t(u32, entries, dev_attr->max_qp_wqes + 1); + rq->max_wqe = bnxt_re_init_depth(init_attr->cap.max_recv_wr + 1, + dev_attr->max_qp_wqes + 1, + uctx); rq->max_sw_wqe = rq->max_wqe; rq->q_full_delta = 0; rq->sg_info.pgsize = PAGE_SIZE; @@ -1367,7 +1504,6 @@ static int bnxt_re_init_sq_attr(struct bnxt_re_qp *qp, struct bnxt_re_dev *rdev; struct bnxt_qplib_q *sq; int diff = 0; - int entries; int rc; rdev = qp->rdev; @@ -1376,7 +1512,6 @@ static int bnxt_re_init_sq_attr(struct bnxt_re_qp *qp, dev_attr = rdev->dev_attr; sq->max_sge = init_attr->cap.max_send_sge; - entries = init_attr->cap.max_send_wr; if (uctx && qplqp->wqe_mode == BNXT_QPLIB_WQE_MODE_VARIABLE) { sq->max_wqe = ureq->sq_slots; sq->max_sw_wqe = ureq->sq_slots; @@ -1392,10 +1527,11 @@ static int bnxt_re_init_sq_attr(struct bnxt_re_qp *qp, return rc; /* Allocate 128 + 1 more than what's provided */ - diff = (qplqp->wqe_mode == BNXT_QPLIB_WQE_MODE_VARIABLE) ? - 0 : BNXT_QPLIB_RESERVED_QP_WRS; - entries = bnxt_re_init_depth(entries + diff + 1, uctx); - sq->max_wqe = min_t(u32, entries, dev_attr->max_qp_wqes + diff + 1); + if (qplqp->wqe_mode != BNXT_QPLIB_WQE_MODE_VARIABLE) + diff = BNXT_QPLIB_RESERVED_QP_WRS; + sq->max_wqe = bnxt_re_init_depth( + init_attr->cap.max_send_wr + diff + 1, + dev_attr->max_qp_wqes + diff + 1, uctx); if (qplqp->wqe_mode == BNXT_QPLIB_WQE_MODE_VARIABLE) sq->max_sw_wqe = bnxt_qplib_get_depth(sq, qplqp->wqe_mode, true); else @@ -1422,16 +1558,15 @@ static void bnxt_re_adjust_gsi_sq_attr(struct bnxt_re_qp *qp, struct bnxt_qplib_dev_attr *dev_attr; struct bnxt_qplib_qp *qplqp; struct bnxt_re_dev *rdev; - int entries; rdev = qp->rdev; qplqp = &qp->qplib_qp; dev_attr = rdev->dev_attr; if (!bnxt_qplib_is_chip_gen_p5_p7(rdev->chip_ctx)) { - entries = bnxt_re_init_depth(init_attr->cap.max_send_wr + 1, uctx); - qplqp->sq.max_wqe = min_t(u32, entries, - dev_attr->max_qp_wqes + 1); + qplqp->sq.max_wqe = + bnxt_re_init_depth(init_attr->cap.max_send_wr + 1, + dev_attr->max_qp_wqes + 1, uctx); qplqp->sq.q_full_delta = qplqp->sq.max_wqe - init_attr->cap.max_send_wr; qplqp->sq.max_sge++; /* Need one extra sge to put UD header */ @@ -1462,6 +1597,39 @@ out: return qptype; } +static void bnxt_re_qp_calculate_msn_psn_size(struct bnxt_re_qp *qp) +{ + struct bnxt_qplib_qp *qplib_qp = &qp->qplib_qp; + struct bnxt_qplib_q *sq = &qplib_qp->sq; + struct bnxt_re_dev *rdev = qp->rdev; + u8 wqe_mode = qplib_qp->wqe_mode; + + if (rdev->dev_attr) + qplib_qp->is_host_msn_tbl = + _is_host_msn_table(rdev->dev_attr->dev_cap_flags2); + + if (qplib_qp->type == CMDQ_CREATE_QP_TYPE_RC) { + qplib_qp->psn_sz = bnxt_qplib_is_chip_gen_p5_p7(rdev->chip_ctx) ? + sizeof(struct sq_psn_search_ext) : + sizeof(struct sq_psn_search); + if (qplib_qp->is_host_msn_tbl) { + qplib_qp->psn_sz = sizeof(struct sq_msn_search); + qplib_qp->msn = 0; + } + } + + /* Update msn tbl size */ + if (qplib_qp->is_host_msn_tbl && qplib_qp->psn_sz) { + if (wqe_mode == BNXT_QPLIB_WQE_MODE_STATIC) + qplib_qp->msn_tbl_sz = + roundup_pow_of_two(bnxt_qplib_set_sq_size(sq, wqe_mode)); + else + qplib_qp->msn_tbl_sz = + roundup_pow_of_two(bnxt_qplib_set_sq_size(sq, wqe_mode)) / 2; + qplib_qp->msn = 0; + } +} + static int bnxt_re_init_qp_attr(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, struct ib_qp_init_attr *init_attr, struct bnxt_re_ucontext *uctx, @@ -1484,12 +1652,12 @@ static int bnxt_re_init_qp_attr(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, qplqp->max_inline_data = init_attr->cap.max_inline_data; qplqp->sig_type = init_attr->sq_sig_type == IB_SIGNAL_ALL_WR; qptype = bnxt_re_init_qp_type(rdev, init_attr); - if (qptype < 0) { - rc = qptype; - goto out; - } + if (qptype < 0) + return qptype; qplqp->type = (u8)qptype; qplqp->wqe_mode = bnxt_re_is_var_size_supported(rdev, uctx); + qplqp->dev_cap_flags = dev_attr->dev_cap_flags; + qplqp->cctx = rdev->chip_ctx; if (init_attr->qp_type == IB_QPT_RC) { qplqp->max_rd_atomic = dev_attr->max_qp_rd_atom; qplqp->max_dest_rd_atomic = dev_attr->max_qp_init_rd_atom; @@ -1519,20 +1687,32 @@ static int bnxt_re_init_qp_attr(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, /* Setup RQ/SRQ */ rc = bnxt_re_init_rq_attr(qp, init_attr, uctx); if (rc) - goto out; + return rc; if (init_attr->qp_type == IB_QPT_GSI) bnxt_re_adjust_gsi_rq_attr(qp); /* Setup SQ */ rc = bnxt_re_init_sq_attr(qp, init_attr, uctx, ureq); if (rc) - goto out; + return rc; if (init_attr->qp_type == IB_QPT_GSI) bnxt_re_adjust_gsi_sq_attr(qp, init_attr, uctx); - if (uctx) /* This will update DPI and qp_handle */ + if (uctx) { /* This will update DPI and qp_handle */ rc = bnxt_re_init_user_qp(rdev, pd, qp, uctx, ureq); -out: + if (rc) + return rc; + } + + bnxt_re_qp_calculate_msn_psn_size(qp); + + rc = bnxt_re_setup_qp_hwqs(qp); + if (rc) + goto free_umem; + + return 0; +free_umem: + bnxt_re_qp_free_umem(qp); return rc; } @@ -1589,6 +1769,7 @@ static int bnxt_re_create_gsi_qp(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, rdev = qp->rdev; qplqp = &qp->qplib_qp; + qplqp->cctx = rdev->chip_ctx; qplqp->rq_hdr_buf_size = BNXT_QPLIB_MAX_QP1_RQ_HDR_SIZE_V2; qplqp->sq_hdr_buf_size = BNXT_QPLIB_MAX_QP1_SQ_HDR_SIZE_V2; @@ -1671,9 +1852,11 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, qp = container_of(ib_qp, struct bnxt_re_qp, ib_qp); uctx = rdma_udata_to_drv_context(udata, struct bnxt_re_ucontext, ib_uctx); - if (udata) - if (ib_copy_from_udata(&ureq, udata, min(udata->inlen, sizeof(ureq)))) - return -EFAULT; + if (udata) { + rc = ib_copy_validate_udata_in_cm(udata, ureq, qp_handle, 0); + if (rc) + return rc; + } rc = bnxt_re_test_qp_limits(rdev, qp_init_attr, dev_attr); if (!rc) { @@ -1692,23 +1875,22 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, if (rc == -ENODEV) goto qp_destroy; if (rc) - goto fail; + goto free_hwq; } else { rc = bnxt_qplib_create_qp(&rdev->qplib_res, &qp->qplib_qp); if (rc) { ibdev_err(&rdev->ibdev, "Failed to create HW QP"); - goto free_umem; + goto free_hwq; } + if (udata) { struct bnxt_re_qp_resp resp; resp.qpid = qp->qplib_qp.id; resp.rsvd = 0; - rc = ib_copy_to_udata(udata, &resp, sizeof(resp)); - if (rc) { - ibdev_err(&rdev->ibdev, "Failed to copy QP udata"); + rc = ib_respond_udata(udata, resp); + if (rc) goto qp_destroy; - } } } @@ -1749,9 +1931,9 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, return 0; qp_destroy: bnxt_qplib_destroy_qp(&rdev->qplib_res, &qp->qplib_qp); -free_umem: - ib_umem_release(qp->rumem); - ib_umem_release(qp->sumem); +free_hwq: + bnxt_qplib_free_qp_res(&rdev->qplib_res, &qp->qplib_qp); + bnxt_re_qp_free_umem(qp); fail: return rc; } @@ -1841,6 +2023,11 @@ int bnxt_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata) ib_srq); struct bnxt_re_dev *rdev = srq->rdev; struct bnxt_qplib_srq *qplib_srq = &srq->qplib_srq; + int ret; + + ret = ib_is_udata_in_empty(udata); + if (ret) + return ret; if (rdev->chip_ctx->modes.toggle_bits & BNXT_QPLIB_SRQ_TOGGLE_BIT) { free_page((unsigned long)srq->uctx_srq_page); @@ -1849,7 +2036,7 @@ int bnxt_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata) bnxt_qplib_destroy_srq(&rdev->qplib_res, qplib_srq); ib_umem_release(srq->umem); atomic_dec(&rdev->stats.res.srq_count); - return 0; + return ib_respond_empty_udata(udata); } static int bnxt_re_init_user_srq(struct bnxt_re_dev *rdev, @@ -1863,9 +2050,11 @@ static int bnxt_re_init_user_srq(struct bnxt_re_dev *rdev, int bytes = 0; struct bnxt_re_ucontext *cntx = rdma_udata_to_drv_context( udata, struct bnxt_re_ucontext, ib_uctx); + int rc; - if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) - return -EFAULT; + rc = ib_copy_validate_udata_in(udata, ureq, srq_handle); + if (rc) + return rc; bytes = (qplib_srq->max_wqe * qplib_srq->wqe_size); bytes = PAGE_ALIGN(bytes); @@ -1895,7 +2084,7 @@ int bnxt_re_create_srq(struct ib_srq *ib_srq, struct bnxt_re_pd *pd; struct ib_pd *ib_pd; u32 active_srqs; - int rc, entries; + int rc; ib_pd = ib_srq->pd; pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); @@ -1921,10 +2110,9 @@ int bnxt_re_create_srq(struct ib_srq *ib_srq, /* Allocate 1 more than what's provided so posting max doesn't * mean empty */ - entries = bnxt_re_init_depth(srq_init_attr->attr.max_wr + 1, uctx); - if (entries > dev_attr->max_srq_wqes + 1) - entries = dev_attr->max_srq_wqes + 1; - srq->qplib_srq.max_wqe = entries; + srq->qplib_srq.max_wqe = + bnxt_re_init_depth(srq_init_attr->attr.max_wr + 1, + dev_attr->max_srq_wqes + 1, uctx); srq->qplib_srq.max_sge = srq_init_attr->attr.max_sge; /* 128 byte wqe size for SRQ . So use max sges */ @@ -1960,9 +2148,8 @@ int bnxt_re_create_srq(struct ib_srq *ib_srq, } resp.comp_mask |= BNXT_RE_SRQ_TOGGLE_PAGE_SUPPORT; } - rc = ib_copy_to_udata(udata, &resp, sizeof(resp)); + rc = ib_respond_udata(udata, resp); if (rc) { - ibdev_err(&rdev->ibdev, "SRQ copy to udata failed!"); bnxt_qplib_destroy_srq(&rdev->qplib_res, &srq->qplib_srq); goto fail; @@ -1988,6 +2175,11 @@ int bnxt_re_modify_srq(struct ib_srq *ib_srq, struct ib_srq_attr *srq_attr, struct bnxt_re_srq *srq = container_of(ib_srq, struct bnxt_re_srq, ib_srq); struct bnxt_re_dev *rdev = srq->rdev; + int ret; + + ret = ib_is_udata_in_empty(udata); + if (ret) + return ret; switch (srq_attr_mask) { case IB_SRQ_MAX_WR: @@ -2004,7 +2196,7 @@ int bnxt_re_modify_srq(struct ib_srq *ib_srq, struct ib_srq_attr *srq_attr, /* On success, update the shadow */ srq->srq_limit = srq_attr->srq_limit; /* No need to Build and send response back to udata */ - return 0; + return ib_respond_empty_udata(udata); default: ibdev_err(&rdev->ibdev, "Unsupported srq_attr_mask 0x%x", srq_attr_mask); @@ -2101,10 +2293,14 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr, struct bnxt_re_dev *rdev = qp->rdev; struct bnxt_qplib_dev_attr *dev_attr = rdev->dev_attr; enum ib_qp_state curr_qp_state, new_qp_state; - int rc, entries; + int rc; unsigned int flags; u8 nw_type; + rc = ib_is_udata_in_empty(udata); + if (rc) + return rc; + if (qp_attr_mask & ~(IB_QP_ATTR_STANDARD_BITS | IB_QP_RATE_LIMIT)) return -EOPNOTSUPP; @@ -2311,9 +2507,9 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr, "Create QP failed - max exceeded"); return -EINVAL; } - entries = bnxt_re_init_depth(qp_attr->cap.max_send_wr, uctx); - qp->qplib_qp.sq.max_wqe = min_t(u32, entries, - dev_attr->max_qp_wqes + 1); + qp->qplib_qp.sq.max_wqe = + bnxt_re_init_depth(qp_attr->cap.max_send_wr, + dev_attr->max_qp_wqes + 1, uctx); qp->qplib_qp.sq.q_full_delta = qp->qplib_qp.sq.max_wqe - qp_attr->cap.max_send_wr; /* @@ -2324,9 +2520,9 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr, qp->qplib_qp.sq.q_full_delta -= 1; qp->qplib_qp.sq.max_sge = qp_attr->cap.max_send_sge; if (qp->qplib_qp.rq.max_wqe) { - entries = bnxt_re_init_depth(qp_attr->cap.max_recv_wr, uctx); - qp->qplib_qp.rq.max_wqe = - min_t(u32, entries, dev_attr->max_qp_wqes + 1); + qp->qplib_qp.rq.max_wqe = bnxt_re_init_depth( + qp_attr->cap.max_recv_wr, + dev_attr->max_qp_wqes + 1, uctx); qp->qplib_qp.rq.max_sw_wqe = qp->qplib_qp.rq.max_wqe; qp->qplib_qp.rq.q_full_delta = qp->qplib_qp.rq.max_wqe - qp_attr->cap.max_recv_wr; @@ -2345,9 +2541,12 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr, ibdev_err(&rdev->ibdev, "Failed to modify HW QP"); return rc; } - if (ib_qp->qp_type == IB_QPT_GSI && rdev->gsi_ctx.gsi_sqp) + if (ib_qp->qp_type == IB_QPT_GSI && rdev->gsi_ctx.gsi_sqp) { rc = bnxt_re_modify_shadow_qp(rdev, qp, qp_attr_mask); - return rc; + if (rc) + return rc; + } + return ib_respond_empty_udata(udata); } int bnxt_re_query_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr, @@ -3122,12 +3321,17 @@ int bnxt_re_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) struct bnxt_qplib_nq *nq; struct bnxt_re_dev *rdev; struct bnxt_re_cq *cq; + int ret; cq = container_of(ib_cq, struct bnxt_re_cq, ib_cq); rdev = cq->rdev; nq = cq->qplib_cq.nq; cctx = rdev->chip_ctx; + ret = ib_is_udata_in_empty(udata); + if (ret) + return ret; + if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) { free_page((unsigned long)cq->uctx_cq_page); hash_del(&cq->hash_entry); @@ -3135,15 +3339,34 @@ int bnxt_re_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) bnxt_qplib_destroy_cq(&rdev->qplib_res, &cq->qplib_cq); bnxt_re_put_nq(rdev, nq); - ib_umem_release(cq->umem); atomic_dec(&rdev->stats.res.cq_count); kfree(cq->cql); + return ib_respond_empty_udata(udata); +} + +static int bnxt_re_setup_sginfo(struct bnxt_re_dev *rdev, + struct ib_umem *umem, + struct bnxt_qplib_sg_info *sginfo) +{ + unsigned long page_size; + + if (!umem) + return -EINVAL; + + page_size = ib_umem_find_best_pgsz(umem, SZ_4K, 0); + if (!page_size || page_size != SZ_4K) + return -EINVAL; + + sginfo->umem = umem; + sginfo->npages = ib_umem_num_dma_blocks(umem, page_size); + sginfo->pgsize = page_size; + sginfo->pgshft = __builtin_ctz(page_size); return 0; } -int bnxt_re_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, - struct uverbs_attr_bundle *attrs) +int bnxt_re_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs) { struct bnxt_re_cq *cq = container_of(ibcq, struct bnxt_re_cq, ib_cq); struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibcq->device, ibdev); @@ -3152,56 +3375,115 @@ int bnxt_re_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, rdma_udata_to_drv_context(udata, struct bnxt_re_ucontext, ib_uctx); struct bnxt_qplib_dev_attr *dev_attr = rdev->dev_attr; struct bnxt_qplib_chip_ctx *cctx; - int cqe = attr->cqe; - int rc, entries; + struct bnxt_re_cq_resp resp = {}; + struct bnxt_re_cq_req req; + int rc; + u32 active_cqs, entries; + + if (attr->flags) + return -EOPNOTSUPP; + + /* Validate CQ fields */ + if (attr->cqe > dev_attr->max_cq_wqes) + return -EINVAL; + + cq->rdev = rdev; + cctx = rdev->chip_ctx; + cq->qplib_cq.cq_handle = (u64)(unsigned long)(&cq->qplib_cq); + + rc = ib_copy_validate_udata_in_cm(udata, req, cq_handle, + BNXT_RE_CQ_FIXED_NUM_CQE_ENABLE); + if (rc) + return rc; + + if (req.comp_mask & BNXT_RE_CQ_FIXED_NUM_CQE_ENABLE) + entries = attr->cqe; + else + entries = bnxt_re_init_depth(attr->cqe + 1, + dev_attr->max_cq_wqes + 1, uctx); + + if (!ibcq->umem) { + ibcq->umem = ib_umem_get(&rdev->ibdev, req.cq_va, + entries * sizeof(struct cq_base), + IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(ibcq->umem)) + return PTR_ERR(ibcq->umem); + } + + rc = bnxt_re_setup_sginfo(rdev, ibcq->umem, &cq->qplib_cq.sg_info); + if (rc) + return rc; + + cq->qplib_cq.dpi = &uctx->dpi; + cq->qplib_cq.max_wqe = entries; + cq->qplib_cq.coalescing = &rdev->cq_coalescing; + cq->qplib_cq.nq = bnxt_re_get_nq(rdev); + cq->qplib_cq.cnq_hw_ring_id = cq->qplib_cq.nq->ring_id; + + rc = bnxt_qplib_create_cq(&rdev->qplib_res, &cq->qplib_cq); + if (rc) + return rc; + + cq->ib_cq.cqe = entries; + cq->cq_period = cq->qplib_cq.period; + active_cqs = atomic_inc_return(&rdev->stats.res.cq_count); + if (active_cqs > rdev->stats.res.cq_watermark) + rdev->stats.res.cq_watermark = active_cqs; + spin_lock_init(&cq->cq_lock); + + if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) { + hash_add(rdev->cq_hash, &cq->hash_entry, cq->qplib_cq.id); + /* Allocate a page */ + cq->uctx_cq_page = (void *)get_zeroed_page(GFP_KERNEL); + if (!cq->uctx_cq_page) + return -ENOMEM; + + resp.comp_mask |= BNXT_RE_CQ_TOGGLE_PAGE_SUPPORT; + } + resp.cqid = cq->qplib_cq.id; + resp.tail = cq->qplib_cq.hwq.cons; + resp.phase = cq->qplib_cq.period; + rc = ib_respond_udata(udata, resp); + if (rc) { + bnxt_qplib_destroy_cq(&rdev->qplib_res, &cq->qplib_cq); + goto free_mem; + } + + return 0; + +free_mem: + free_page((unsigned long)cq->uctx_cq_page); + return rc; +} + +int bnxt_re_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs) +{ + struct bnxt_re_cq *cq = container_of(ibcq, struct bnxt_re_cq, ib_cq); + struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibcq->device, ibdev); + struct bnxt_qplib_dev_attr *dev_attr = rdev->dev_attr; + int rc; u32 active_cqs; if (attr->flags) return -EOPNOTSUPP; /* Validate CQ fields */ - if (cqe < 1 || cqe > dev_attr->max_cq_wqes) { - ibdev_err(&rdev->ibdev, "Failed to create CQ -max exceeded"); + if (attr->cqe > dev_attr->max_cq_wqes) return -EINVAL; - } cq->rdev = rdev; - cctx = rdev->chip_ctx; cq->qplib_cq.cq_handle = (u64)(unsigned long)(&cq->qplib_cq); - entries = bnxt_re_init_depth(cqe + 1, uctx); - if (entries > dev_attr->max_cq_wqes + 1) - entries = dev_attr->max_cq_wqes + 1; + cq->max_cql = attr->cqe + 1; + cq->cql = kzalloc_objs(struct bnxt_qplib_cqe, cq->max_cql); + if (!cq->cql) + return -ENOMEM; - cq->qplib_cq.sg_info.pgsize = PAGE_SIZE; - cq->qplib_cq.sg_info.pgshft = PAGE_SHIFT; - if (udata) { - struct bnxt_re_cq_req req; - if (ib_copy_from_udata(&req, udata, sizeof(req))) { - rc = -EFAULT; - goto fail; - } - - cq->umem = ib_umem_get(&rdev->ibdev, req.cq_va, - entries * sizeof(struct cq_base), - IB_ACCESS_LOCAL_WRITE); - if (IS_ERR(cq->umem)) { - rc = PTR_ERR(cq->umem); - goto fail; - } - cq->qplib_cq.sg_info.umem = cq->umem; - cq->qplib_cq.dpi = &uctx->dpi; - } else { - cq->max_cql = min_t(u32, entries, MAX_CQL_PER_POLL); - cq->cql = kzalloc_objs(struct bnxt_qplib_cqe, cq->max_cql); - if (!cq->cql) { - rc = -ENOMEM; - goto fail; - } - - cq->qplib_cq.dpi = &rdev->dpi_privileged; - } - cq->qplib_cq.max_wqe = entries; + cq->qplib_cq.sg_info.pgsize = SZ_4K; + cq->qplib_cq.sg_info.pgshft = __builtin_ctz(SZ_4K); + cq->qplib_cq.dpi = &rdev->dpi_privileged; + cq->qplib_cq.max_wqe = cq->max_cql; cq->qplib_cq.coalescing = &rdev->cq_coalescing; cq->qplib_cq.nq = bnxt_re_get_nq(rdev); cq->qplib_cq.cnq_hw_ring_id = cq->qplib_cq.nq->ring_id; @@ -3212,45 +3494,15 @@ int bnxt_re_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, goto fail; } - cq->ib_cq.cqe = entries; + cq->ib_cq.cqe = cq->max_cql; cq->cq_period = cq->qplib_cq.period; - active_cqs = atomic_inc_return(&rdev->stats.res.cq_count); if (active_cqs > rdev->stats.res.cq_watermark) rdev->stats.res.cq_watermark = active_cqs; spin_lock_init(&cq->cq_lock); - if (udata) { - struct bnxt_re_cq_resp resp = {}; - - if (cctx->modes.toggle_bits & BNXT_QPLIB_CQ_TOGGLE_BIT) { - hash_add(rdev->cq_hash, &cq->hash_entry, cq->qplib_cq.id); - /* Allocate a page */ - cq->uctx_cq_page = (void *)get_zeroed_page(GFP_KERNEL); - if (!cq->uctx_cq_page) { - rc = -ENOMEM; - goto c2fail; - } - resp.comp_mask |= BNXT_RE_CQ_TOGGLE_PAGE_SUPPORT; - } - resp.cqid = cq->qplib_cq.id; - resp.tail = cq->qplib_cq.hwq.cons; - resp.phase = cq->qplib_cq.period; - resp.rsvd = 0; - rc = ib_copy_to_udata(udata, &resp, min(sizeof(resp), udata->outlen)); - if (rc) { - ibdev_err(&rdev->ibdev, "Failed to copy CQ udata"); - bnxt_qplib_destroy_cq(&rdev->qplib_res, &cq->qplib_cq); - goto free_mem; - } - } - return 0; -free_mem: - free_page((unsigned long)cq->uctx_cq_page); -c2fail: - ib_umem_release(cq->umem); fail: kfree(cq->cql); return rc; @@ -3264,14 +3516,15 @@ static void bnxt_re_resize_cq_complete(struct bnxt_re_cq *cq) cq->qplib_cq.max_wqe = cq->resize_cqe; if (cq->resize_umem) { - ib_umem_release(cq->umem); - cq->umem = cq->resize_umem; + ib_umem_release(cq->ib_cq.umem); + cq->ib_cq.umem = cq->resize_umem; cq->resize_umem = NULL; cq->resize_cqe = 0; } } -int bnxt_re_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) +int bnxt_re_resize_cq(struct ib_cq *ibcq, unsigned int cqe, + struct ib_udata *udata) { struct bnxt_qplib_sg_info sg_info = {}; struct bnxt_qplib_dpi *orig_dpi = NULL; @@ -3280,7 +3533,8 @@ int bnxt_re_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) struct bnxt_re_resize_cq_req req; struct bnxt_re_dev *rdev; struct bnxt_re_cq *cq; - int rc, entries; + int rc; + u32 entries; cq = container_of(ibcq, struct bnxt_re_cq, ib_cq); rdev = cq->rdev; @@ -3297,22 +3551,16 @@ int bnxt_re_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) } /* Check the requested cq depth out of supported depth */ - if (cqe < 1 || cqe > dev_attr->max_cq_wqes) { - ibdev_err(&rdev->ibdev, "Resize CQ %#x failed - out of range cqe %d", - cq->qplib_cq.id, cqe); + if (cqe > dev_attr->max_cq_wqes) return -EINVAL; - } uctx = rdma_udata_to_drv_context(udata, struct bnxt_re_ucontext, ib_uctx); - entries = bnxt_re_init_depth(cqe + 1, uctx); - if (entries > dev_attr->max_cq_wqes + 1) - entries = dev_attr->max_cq_wqes + 1; + entries = bnxt_re_init_depth(cqe + 1, dev_attr->max_cq_wqes + 1, uctx); /* uverbs consumer */ - if (ib_copy_from_udata(&req, udata, sizeof(req))) { - rc = -EFAULT; + rc = ib_copy_validate_udata_in(udata, req, cq_va); + if (rc) goto fail; - } cq->resize_umem = ib_umem_get(&rdev->ibdev, req.cq_va, entries * sizeof(struct cq_base), @@ -3343,7 +3591,7 @@ int bnxt_re_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) cq->ib_cq.cqe = cq->resize_cqe; atomic_inc(&rdev->stats.res.resize_count); - return 0; + return ib_respond_empty_udata(udata); fail: if (cq->resize_umem) { @@ -3865,7 +4113,7 @@ int bnxt_re_poll_cq(struct ib_cq *ib_cq, int num_entries, struct ib_wc *wc) /* User CQ; the only processing we do is to * complete any pending CQ resize operation. */ - if (cq->umem) { + if (cq->ib_cq.umem) { if (cq->resize_umem) bnxt_re_resize_cq_complete(cq); return 0; @@ -4075,6 +4323,10 @@ int bnxt_re_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata) struct bnxt_re_dev *rdev = mr->rdev; int rc; + rc = ib_is_udata_in_empty(udata); + if (rc) + return rc; + rc = bnxt_qplib_free_mrw(&rdev->qplib_res, &mr->qplib_mr); if (rc) { ibdev_err(&rdev->ibdev, "Dereg MR failed: %#x\n", rc); @@ -4092,7 +4344,9 @@ int bnxt_re_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata) kfree(mr); atomic_dec(&rdev->stats.res.mr_count); - return rc; + if (rc) + return rc; + return ib_respond_empty_udata(udata); } static int bnxt_re_set_page(struct ib_mr *ib_mr, u64 addr) @@ -4147,7 +4401,7 @@ struct ib_mr *bnxt_re_alloc_mr(struct ib_pd *ib_pd, enum ib_mr_type type, mr->ib_mr.lkey = mr->qplib_mr.lkey; mr->ib_mr.rkey = mr->ib_mr.lkey; - mr->pages = kcalloc(max_num_sg, sizeof(u64), GFP_KERNEL); + mr->pages = kzalloc_objs(u64, max_num_sg); if (!mr->pages) { rc = -ENOMEM; goto fail; @@ -4183,6 +4437,10 @@ struct ib_mw *bnxt_re_alloc_mw(struct ib_pd *ib_pd, enum ib_mw_type type, u32 active_mws; int rc; + rc = ib_is_udata_in_empty(udata); + if (rc) + return ERR_PTR(rc); + mw = kzalloc_obj(*mw); if (!mw) return ERR_PTR(-ENOMEM); @@ -4310,6 +4568,11 @@ struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, struct bnxt_re_dev *rdev = pd->rdev; struct ib_umem *umem; struct ib_mr *ib_mr; + int ret; + + ret = ib_is_udata_in_empty(udata); + if (ret) + return ERR_PTR(ret); if (dmah) return ERR_PTR(-EOPNOTSUPP); @@ -4414,8 +4677,11 @@ int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata) if (_is_modify_qp_rate_limit_supported(dev_attr->dev_cap_flags2)) resp.comp_mask |= BNXT_RE_UCNTX_CMASK_QP_RATE_LIMIT_ENABLED; - if (udata->inlen >= sizeof(ureq)) { - rc = ib_copy_from_udata(&ureq, udata, min(udata->inlen, sizeof(ureq))); + if (udata->inlen) { + rc = ib_copy_validate_udata_in_cm( + udata, ureq, comp_mask, + BNXT_RE_COMP_MASK_REQ_UCNTX_POW2_SUPPORT | + BNXT_RE_COMP_MASK_REQ_UCNTX_VAR_WQE_SUPPORT); if (rc) goto cfail; if (ureq.comp_mask & BNXT_RE_COMP_MASK_REQ_UCNTX_POW2_SUPPORT) { @@ -4430,12 +4696,9 @@ int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata) } } - rc = ib_copy_to_udata(udata, &resp, min(udata->outlen, sizeof(resp))); - if (rc) { - ibdev_err(ibdev, "Failed to copy user context"); - rc = -EFAULT; + rc = ib_respond_udata(udata, resp); + if (rc) goto cfail; - } return 0; cfail: @@ -4494,6 +4757,10 @@ struct ib_flow *bnxt_re_create_flow(struct ib_qp *ib_qp, struct bnxt_re_flow *flow; int rc; + rc = ib_is_udata_in_empty(udata); + if (rc) + return ERR_PTR(rc); + if (attr->type != IB_FLOW_ATTR_SNIFFER || !rdev->rcfw.roce_mirror) return ERR_PTR(-EOPNOTSUPP); @@ -4554,32 +4821,6 @@ int bnxt_re_destroy_flow(struct ib_flow *flow_id) return rc; } -static struct bnxt_re_cq *bnxt_re_search_for_cq(struct bnxt_re_dev *rdev, u32 cq_id) -{ - struct bnxt_re_cq *cq = NULL, *tmp_cq; - - hash_for_each_possible(rdev->cq_hash, tmp_cq, hash_entry, cq_id) { - if (tmp_cq->qplib_cq.id == cq_id) { - cq = tmp_cq; - break; - } - } - return cq; -} - -static struct bnxt_re_srq *bnxt_re_search_for_srq(struct bnxt_re_dev *rdev, u32 srq_id) -{ - struct bnxt_re_srq *srq = NULL, *tmp_srq; - - hash_for_each_possible(rdev->srq_hash, tmp_srq, hash_entry, srq_id) { - if (tmp_srq->qplib_srq.id == srq_id) { - srq = tmp_srq; - break; - } - } - return srq; -} - /* Helper function to mmap the virtual memory from user app */ int bnxt_re_mmap(struct ib_ucontext *ib_uctx, struct vm_area_struct *vma) { @@ -4682,280 +4923,3 @@ int bnxt_re_process_mad(struct ib_device *ibdev, int mad_flags, ret |= IB_MAD_RESULT_REPLY; return ret; } - -static int UVERBS_HANDLER(BNXT_RE_METHOD_NOTIFY_DRV)(struct uverbs_attr_bundle *attrs) -{ - struct bnxt_re_ucontext *uctx; - - uctx = container_of(ib_uverbs_get_ucontext(attrs), struct bnxt_re_ucontext, ib_uctx); - bnxt_re_pacing_alert(uctx->rdev); - return 0; -} - -static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle *attrs) -{ - struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, BNXT_RE_ALLOC_PAGE_HANDLE); - enum bnxt_re_alloc_page_type alloc_type; - struct bnxt_re_user_mmap_entry *entry; - enum bnxt_re_mmap_flag mmap_flag; - struct bnxt_qplib_chip_ctx *cctx; - struct bnxt_re_ucontext *uctx; - struct bnxt_re_dev *rdev; - u64 mmap_offset; - u32 length; - u32 dpi; - u64 addr; - int err; - - uctx = container_of(ib_uverbs_get_ucontext(attrs), struct bnxt_re_ucontext, ib_uctx); - if (IS_ERR(uctx)) - return PTR_ERR(uctx); - - err = uverbs_get_const(&alloc_type, attrs, BNXT_RE_ALLOC_PAGE_TYPE); - if (err) - return err; - - rdev = uctx->rdev; - cctx = rdev->chip_ctx; - - switch (alloc_type) { - case BNXT_RE_ALLOC_WC_PAGE: - if (cctx->modes.db_push) { - if (bnxt_qplib_alloc_dpi(&rdev->qplib_res, &uctx->wcdpi, - uctx, BNXT_QPLIB_DPI_TYPE_WC)) - return -ENOMEM; - length = PAGE_SIZE; - dpi = uctx->wcdpi.dpi; - addr = (u64)uctx->wcdpi.umdbr; - mmap_flag = BNXT_RE_MMAP_WC_DB; - } else { - return -EINVAL; - } - - break; - case BNXT_RE_ALLOC_DBR_BAR_PAGE: - length = PAGE_SIZE; - addr = (u64)rdev->pacing.dbr_bar_addr; - mmap_flag = BNXT_RE_MMAP_DBR_BAR; - break; - - case BNXT_RE_ALLOC_DBR_PAGE: - length = PAGE_SIZE; - addr = (u64)rdev->pacing.dbr_page; - mmap_flag = BNXT_RE_MMAP_DBR_PAGE; - break; - - default: - return -EOPNOTSUPP; - } - - entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mmap_offset); - if (!entry) - return -ENOMEM; - - uobj->object = entry; - uverbs_finalize_uobj_create(attrs, BNXT_RE_ALLOC_PAGE_HANDLE); - err = uverbs_copy_to(attrs, BNXT_RE_ALLOC_PAGE_MMAP_OFFSET, - &mmap_offset, sizeof(mmap_offset)); - if (err) - return err; - - err = uverbs_copy_to(attrs, BNXT_RE_ALLOC_PAGE_MMAP_LENGTH, - &length, sizeof(length)); - if (err) - return err; - - err = uverbs_copy_to(attrs, BNXT_RE_ALLOC_PAGE_DPI, - &dpi, sizeof(dpi)); - if (err) - return err; - - return 0; -} - -static int alloc_page_obj_cleanup(struct ib_uobject *uobject, - enum rdma_remove_reason why, - struct uverbs_attr_bundle *attrs) -{ - struct bnxt_re_user_mmap_entry *entry = uobject->object; - struct bnxt_re_ucontext *uctx = entry->uctx; - - switch (entry->mmap_flag) { - case BNXT_RE_MMAP_WC_DB: - if (uctx && uctx->wcdpi.dbr) { - struct bnxt_re_dev *rdev = uctx->rdev; - - bnxt_qplib_dealloc_dpi(&rdev->qplib_res, &uctx->wcdpi); - uctx->wcdpi.dbr = NULL; - } - break; - case BNXT_RE_MMAP_DBR_BAR: - case BNXT_RE_MMAP_DBR_PAGE: - break; - default: - goto exit; - } - rdma_user_mmap_entry_remove(&entry->rdma_entry); -exit: - return 0; -} - -DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_ALLOC_PAGE, - UVERBS_ATTR_IDR(BNXT_RE_ALLOC_PAGE_HANDLE, - BNXT_RE_OBJECT_ALLOC_PAGE, - UVERBS_ACCESS_NEW, - UA_MANDATORY), - UVERBS_ATTR_CONST_IN(BNXT_RE_ALLOC_PAGE_TYPE, - enum bnxt_re_alloc_page_type, - UA_MANDATORY), - UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_PAGE_MMAP_OFFSET, - UVERBS_ATTR_TYPE(u64), - UA_MANDATORY), - UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_PAGE_MMAP_LENGTH, - UVERBS_ATTR_TYPE(u32), - UA_MANDATORY), - UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_PAGE_DPI, - UVERBS_ATTR_TYPE(u32), - UA_MANDATORY)); - -DECLARE_UVERBS_NAMED_METHOD_DESTROY(BNXT_RE_METHOD_DESTROY_PAGE, - UVERBS_ATTR_IDR(BNXT_RE_DESTROY_PAGE_HANDLE, - BNXT_RE_OBJECT_ALLOC_PAGE, - UVERBS_ACCESS_DESTROY, - UA_MANDATORY)); - -DECLARE_UVERBS_NAMED_OBJECT(BNXT_RE_OBJECT_ALLOC_PAGE, - UVERBS_TYPE_ALLOC_IDR(alloc_page_obj_cleanup), - &UVERBS_METHOD(BNXT_RE_METHOD_ALLOC_PAGE), - &UVERBS_METHOD(BNXT_RE_METHOD_DESTROY_PAGE)); - -DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_NOTIFY_DRV); - -DECLARE_UVERBS_GLOBAL_METHODS(BNXT_RE_OBJECT_NOTIFY_DRV, - &UVERBS_METHOD(BNXT_RE_METHOD_NOTIFY_DRV)); - -/* Toggle MEM */ -static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bundle *attrs) -{ - struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, BNXT_RE_TOGGLE_MEM_HANDLE); - enum bnxt_re_mmap_flag mmap_flag = BNXT_RE_MMAP_TOGGLE_PAGE; - enum bnxt_re_get_toggle_mem_type res_type; - struct bnxt_re_user_mmap_entry *entry; - struct bnxt_re_ucontext *uctx; - struct ib_ucontext *ib_uctx; - struct bnxt_re_dev *rdev; - struct bnxt_re_srq *srq; - u32 length = PAGE_SIZE; - struct bnxt_re_cq *cq; - u64 mem_offset; - u32 offset = 0; - u64 addr = 0; - u32 res_id; - int err; - - ib_uctx = ib_uverbs_get_ucontext(attrs); - if (IS_ERR(ib_uctx)) - return PTR_ERR(ib_uctx); - - err = uverbs_get_const(&res_type, attrs, BNXT_RE_TOGGLE_MEM_TYPE); - if (err) - return err; - - uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); - rdev = uctx->rdev; - err = uverbs_copy_from(&res_id, attrs, BNXT_RE_TOGGLE_MEM_RES_ID); - if (err) - return err; - - switch (res_type) { - case BNXT_RE_CQ_TOGGLE_MEM: - cq = bnxt_re_search_for_cq(rdev, res_id); - if (!cq) - return -EINVAL; - - addr = (u64)cq->uctx_cq_page; - break; - case BNXT_RE_SRQ_TOGGLE_MEM: - srq = bnxt_re_search_for_srq(rdev, res_id); - if (!srq) - return -EINVAL; - - addr = (u64)srq->uctx_srq_page; - break; - - default: - return -EOPNOTSUPP; - } - - entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mem_offset); - if (!entry) - return -ENOMEM; - - uobj->object = entry; - uverbs_finalize_uobj_create(attrs, BNXT_RE_TOGGLE_MEM_HANDLE); - err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_PAGE, - &mem_offset, sizeof(mem_offset)); - if (err) - return err; - - err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_LENGTH, - &length, sizeof(length)); - if (err) - return err; - - err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_OFFSET, - &offset, sizeof(offset)); - if (err) - return err; - - return 0; -} - -static int get_toggle_mem_obj_cleanup(struct ib_uobject *uobject, - enum rdma_remove_reason why, - struct uverbs_attr_bundle *attrs) -{ - struct bnxt_re_user_mmap_entry *entry = uobject->object; - - rdma_user_mmap_entry_remove(&entry->rdma_entry); - return 0; -} - -DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_GET_TOGGLE_MEM, - UVERBS_ATTR_IDR(BNXT_RE_TOGGLE_MEM_HANDLE, - BNXT_RE_OBJECT_GET_TOGGLE_MEM, - UVERBS_ACCESS_NEW, - UA_MANDATORY), - UVERBS_ATTR_CONST_IN(BNXT_RE_TOGGLE_MEM_TYPE, - enum bnxt_re_get_toggle_mem_type, - UA_MANDATORY), - UVERBS_ATTR_PTR_IN(BNXT_RE_TOGGLE_MEM_RES_ID, - UVERBS_ATTR_TYPE(u32), - UA_MANDATORY), - UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_PAGE, - UVERBS_ATTR_TYPE(u64), - UA_MANDATORY), - UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_OFFSET, - UVERBS_ATTR_TYPE(u32), - UA_MANDATORY), - UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_LENGTH, - UVERBS_ATTR_TYPE(u32), - UA_MANDATORY)); - -DECLARE_UVERBS_NAMED_METHOD_DESTROY(BNXT_RE_METHOD_RELEASE_TOGGLE_MEM, - UVERBS_ATTR_IDR(BNXT_RE_RELEASE_TOGGLE_MEM_HANDLE, - BNXT_RE_OBJECT_GET_TOGGLE_MEM, - UVERBS_ACCESS_DESTROY, - UA_MANDATORY)); - -DECLARE_UVERBS_NAMED_OBJECT(BNXT_RE_OBJECT_GET_TOGGLE_MEM, - UVERBS_TYPE_ALLOC_IDR(get_toggle_mem_obj_cleanup), - &UVERBS_METHOD(BNXT_RE_METHOD_GET_TOGGLE_MEM), - &UVERBS_METHOD(BNXT_RE_METHOD_RELEASE_TOGGLE_MEM)); - -const struct uapi_definition bnxt_re_uapi_defs[] = { - UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_ALLOC_PAGE), - UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_NOTIFY_DRV), - UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_GET_TOGGLE_MEM), - {} -}; diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h index 76ba9ab04d5c..08f71a94d55d 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -108,7 +108,6 @@ struct bnxt_re_cq { struct bnxt_qplib_cqe *cql; #define MAX_CQL_PER_POLL 1024 u32 max_cql; - struct ib_umem *umem; struct ib_umem *resize_umem; int resize_cqe; void *uctx_cq_page; @@ -164,6 +163,13 @@ struct bnxt_re_user_mmap_entry { u8 mmap_flag; }; +struct bnxt_re_dbr_obj { + struct bnxt_re_dev *rdev; + struct bnxt_qplib_dpi dpi; + struct bnxt_re_user_mmap_entry *entry; + atomic_t usecnt; /* QPs using this dbr */ +}; + struct bnxt_re_flow { struct ib_flow ib_flow; struct bnxt_re_dev *rdev; @@ -184,10 +190,13 @@ enum { BNXT_RE_UCNTX_CAP_VAR_WQE_ENABLED = 0x2ULL, }; -static inline u32 bnxt_re_init_depth(u32 ent, struct bnxt_re_ucontext *uctx) +static inline u32 bnxt_re_init_depth(u32 ent, u32 max, + struct bnxt_re_ucontext *uctx) { - return uctx ? (uctx->cmask & BNXT_RE_UCNTX_CAP_POW2_DISABLED) ? - ent : roundup_pow_of_two(ent) : ent; + if (uctx && !(uctx->cmask & BNXT_RE_UCNTX_CAP_POW2_DISABLED)) + return min(roundup_pow_of_two(ent), max); + + return ent; } static inline bool bnxt_re_is_var_size_supported(struct bnxt_re_dev *rdev, @@ -247,7 +256,10 @@ int bnxt_re_post_recv(struct ib_qp *qp, const struct ib_recv_wr *recv_wr, const struct ib_recv_wr **bad_recv_wr); int bnxt_re_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, struct uverbs_attr_bundle *attrs); -int bnxt_re_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata); +int bnxt_re_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs); +int bnxt_re_resize_cq(struct ib_cq *ibcq, unsigned int cqe, + struct ib_udata *udata); int bnxt_re_destroy_cq(struct ib_cq *cq, struct ib_udata *udata); int bnxt_re_poll_cq(struct ib_cq *cq, int num_entries, struct ib_wc *wc); int bnxt_re_req_notify_cq(struct ib_cq *cq, enum ib_cq_notify_flags flags); @@ -293,4 +305,7 @@ static inline u32 __to_ib_port_num(u16 port_id) unsigned long bnxt_re_lock_cqs(struct bnxt_re_qp *qp); void bnxt_re_unlock_cqs(struct bnxt_re_qp *qp, unsigned long flags); +struct bnxt_re_user_mmap_entry* +bnxt_re_mmap_entry_insert(struct bnxt_re_ucontext *uctx, u64 mem_offset, + enum bnxt_re_mmap_flag mmap_flag, u64 *offset); #endif /* __BNXT_RE_IB_VERBS_H__ */ diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index b576f05e3b26..a892f1172917 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -55,8 +55,8 @@ #include #include #include +#include -#include "bnxt_ulp.h" #include "roce_hsi.h" #include "qplib_res.h" #include "qplib_sp.h" @@ -1326,6 +1326,7 @@ static const struct ib_device_ops bnxt_re_dev_ops = { .owner = THIS_MODULE, .driver_id = RDMA_DRIVER_BNXT_RE, .uverbs_abi_ver = BNXT_RE_ABI_VERSION, + .uverbs_robust_udata = true, .add_gid = bnxt_re_add_gid, .alloc_hw_port_stats = bnxt_re_ib_alloc_hw_port_stats, @@ -1334,6 +1335,7 @@ static const struct ib_device_ops bnxt_re_dev_ops = { .alloc_ucontext = bnxt_re_alloc_ucontext, .create_ah = bnxt_re_create_ah, .create_cq = bnxt_re_create_cq, + .create_user_cq = bnxt_re_create_user_cq, .create_qp = bnxt_re_create_qp, .create_srq = bnxt_re_create_srq, .create_user_ah = bnxt_re_create_ah, @@ -1372,7 +1374,7 @@ static const struct ib_device_ops bnxt_re_dev_ops = { .reg_user_mr = bnxt_re_reg_user_mr, .reg_user_mr_dmabuf = bnxt_re_reg_user_mr_dmabuf, .req_notify_cq = bnxt_re_req_notify_cq, - .resize_cq = bnxt_re_resize_cq, + .resize_user_cq = bnxt_re_resize_cq, .create_flow = bnxt_re_create_flow, .destroy_flow = bnxt_re_destroy_flow, INIT_RDMA_OBJ_SIZE(ib_ah, bnxt_re_ah, ib_ah), diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index 2d7932b3c492..5301e4f21ade 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include "roce_hsi.h" @@ -55,7 +56,6 @@ #include "qplib_sp.h" #include "qplib_fp.h" #include -#include "bnxt_ulp.h" #include "bnxt_re.h" #include "ib_verbs.h" @@ -792,8 +792,6 @@ int bnxt_qplib_post_srq_recv(struct bnxt_qplib_srq *srq, return 0; } -/* QP */ - static int bnxt_qplib_alloc_init_swq(struct bnxt_qplib_q *que) { int indx; @@ -812,9 +810,71 @@ static int bnxt_qplib_alloc_init_swq(struct bnxt_qplib_q *que) return 0; } +static int bnxt_re_setup_qp_swqs(struct bnxt_qplib_qp *qplqp) +{ + struct bnxt_qplib_q *sq = &qplqp->sq; + struct bnxt_qplib_q *rq = &qplqp->rq; + int rc; + + if (qplqp->is_user) + return 0; + + rc = bnxt_qplib_alloc_init_swq(sq); + if (rc) + return rc; + + if (!qplqp->srq) { + rc = bnxt_qplib_alloc_init_swq(rq); + if (rc) + goto free_sq_swq; + } + + return 0; +free_sq_swq: + kfree(sq->swq); + return rc; +} + +static void bnxt_qp_init_dbinfo(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) +{ + struct bnxt_qplib_q *sq = &qp->sq; + struct bnxt_qplib_q *rq = &qp->rq; + + sq->dbinfo.hwq = &sq->hwq; + sq->dbinfo.xid = qp->id; + sq->dbinfo.db = qp->dpi->dbr; + sq->dbinfo.max_slot = bnxt_qplib_set_sq_max_slot(qp->wqe_mode); + sq->dbinfo.flags = 0; + if (rq->max_wqe) { + rq->dbinfo.hwq = &rq->hwq; + rq->dbinfo.xid = qp->id; + rq->dbinfo.db = qp->dpi->dbr; + rq->dbinfo.max_slot = bnxt_qplib_set_rq_max_slot(rq->wqe_size); + rq->dbinfo.flags = 0; + } +} + +static void bnxt_qplib_init_psn_ptr(struct bnxt_qplib_qp *qp, int size) +{ + struct bnxt_qplib_hwq *sq_hwq; + struct bnxt_qplib_q *sq; + u64 fpsne, psn_pg; + u16 indx_pad = 0; + + sq = &qp->sq; + sq_hwq = &sq->hwq; + /* First psn entry */ + fpsne = (u64)bnxt_qplib_get_qe(sq_hwq, sq_hwq->depth, &psn_pg); + if (!IS_ALIGNED(fpsne, PAGE_SIZE)) + indx_pad = (fpsne & ~PAGE_MASK) / size; + sq_hwq->pad_pgofft = indx_pad; + sq_hwq->pad_pg = (u64 *)psn_pg; + sq_hwq->pad_stride = size; +} + +/* QP */ int bnxt_qplib_create_qp1(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) { - struct bnxt_qplib_hwq_attr hwq_attr = {}; struct bnxt_qplib_rcfw *rcfw = res->rcfw; struct creq_create_qp1_resp resp = {}; struct bnxt_qplib_cmdqmsg msg = {}; @@ -823,7 +883,6 @@ int bnxt_qplib_create_qp1(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) struct cmdq_create_qp1 req = {}; struct bnxt_qplib_pbl *pbl; u32 qp_flags = 0; - u8 pg_sz_lvl; u32 tbl_indx; int rc; @@ -837,26 +896,12 @@ int bnxt_qplib_create_qp1(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) req.qp_handle = cpu_to_le64(qp->qp_handle); /* SQ */ - hwq_attr.res = res; - hwq_attr.sginfo = &sq->sg_info; - hwq_attr.stride = sizeof(struct sq_sge); - hwq_attr.depth = bnxt_qplib_get_depth(sq, qp->wqe_mode, false); - hwq_attr.type = HWQ_TYPE_QUEUE; - rc = bnxt_qplib_alloc_init_hwq(&sq->hwq, &hwq_attr); - if (rc) - return rc; + sq->max_sw_wqe = bnxt_qplib_get_depth(sq, qp->wqe_mode, true); + req.sq_size = cpu_to_le32(sq->max_sw_wqe); + req.sq_pg_size_sq_lvl = sq->hwq.pg_sz_lvl; - rc = bnxt_qplib_alloc_init_swq(sq); - if (rc) - goto fail_sq; - - req.sq_size = cpu_to_le32(bnxt_qplib_set_sq_size(sq, qp->wqe_mode)); pbl = &sq->hwq.pbl[PBL_LVL_0]; req.sq_pbl = cpu_to_le64(pbl->pg_map_arr[0]); - pg_sz_lvl = (bnxt_qplib_base_pg_size(&sq->hwq) << - CMDQ_CREATE_QP1_SQ_PG_SIZE_SFT); - pg_sz_lvl |= (sq->hwq.level & CMDQ_CREATE_QP1_SQ_LVL_MASK); - req.sq_pg_size_sq_lvl = pg_sz_lvl; req.sq_fwo_sq_sge = cpu_to_le16((sq->max_sge & CMDQ_CREATE_QP1_SQ_SGE_MASK) << CMDQ_CREATE_QP1_SQ_SGE_SFT); @@ -865,24 +910,10 @@ int bnxt_qplib_create_qp1(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) /* RQ */ if (rq->max_wqe) { rq->dbinfo.flags = 0; - hwq_attr.res = res; - hwq_attr.sginfo = &rq->sg_info; - hwq_attr.stride = sizeof(struct sq_sge); - hwq_attr.depth = bnxt_qplib_get_depth(rq, qp->wqe_mode, false); - hwq_attr.type = HWQ_TYPE_QUEUE; - rc = bnxt_qplib_alloc_init_hwq(&rq->hwq, &hwq_attr); - if (rc) - goto sq_swq; - rc = bnxt_qplib_alloc_init_swq(rq); - if (rc) - goto fail_rq; req.rq_size = cpu_to_le32(rq->max_wqe); pbl = &rq->hwq.pbl[PBL_LVL_0]; req.rq_pbl = cpu_to_le64(pbl->pg_map_arr[0]); - pg_sz_lvl = (bnxt_qplib_base_pg_size(&rq->hwq) << - CMDQ_CREATE_QP1_RQ_PG_SIZE_SFT); - pg_sz_lvl |= (rq->hwq.level & CMDQ_CREATE_QP1_RQ_LVL_MASK); - req.rq_pg_size_rq_lvl = pg_sz_lvl; + req.rq_pg_size_rq_lvl = rq->hwq.pg_sz_lvl; req.rq_fwo_rq_sge = cpu_to_le16((rq->max_sge & CMDQ_CREATE_QP1_RQ_SGE_MASK) << @@ -893,7 +924,7 @@ int bnxt_qplib_create_qp1(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) rc = bnxt_qplib_alloc_qp_hdr_buf(res, qp); if (rc) { rc = -ENOMEM; - goto rq_rwq; + return rc; } qp_flags |= CMDQ_CREATE_QP1_QP_FLAGS_RESERVED_LKEY_ENABLE; req.qp_flags = cpu_to_le32(qp_flags); @@ -906,73 +937,39 @@ int bnxt_qplib_create_qp1(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) qp->id = le32_to_cpu(resp.xid); qp->cur_qp_state = CMDQ_MODIFY_QP_NEW_STATE_RESET; - qp->cctx = res->cctx; - sq->dbinfo.hwq = &sq->hwq; - sq->dbinfo.xid = qp->id; - sq->dbinfo.db = qp->dpi->dbr; - sq->dbinfo.max_slot = bnxt_qplib_set_sq_max_slot(qp->wqe_mode); - if (rq->max_wqe) { - rq->dbinfo.hwq = &rq->hwq; - rq->dbinfo.xid = qp->id; - rq->dbinfo.db = qp->dpi->dbr; - rq->dbinfo.max_slot = bnxt_qplib_set_rq_max_slot(rq->wqe_size); - } + + rc = bnxt_re_setup_qp_swqs(qp); + if (rc) + goto destroy_qp; + bnxt_qp_init_dbinfo(res, qp); + tbl_indx = map_qp_id_to_tbl_indx(qp->id, rcfw); rcfw->qp_tbl[tbl_indx].qp_id = qp->id; rcfw->qp_tbl[tbl_indx].qp_handle = (void *)qp; return 0; +destroy_qp: + bnxt_qplib_destroy_qp(res, qp); fail: bnxt_qplib_free_qp_hdr_buf(res, qp); -rq_rwq: - kfree(rq->swq); -fail_rq: - bnxt_qplib_free_hwq(res, &rq->hwq); -sq_swq: - kfree(sq->swq); -fail_sq: - bnxt_qplib_free_hwq(res, &sq->hwq); return rc; } -static void bnxt_qplib_init_psn_ptr(struct bnxt_qplib_qp *qp, int size) -{ - struct bnxt_qplib_hwq *hwq; - struct bnxt_qplib_q *sq; - u64 fpsne, psn_pg; - u16 indx_pad = 0; - - sq = &qp->sq; - hwq = &sq->hwq; - /* First psn entry */ - fpsne = (u64)bnxt_qplib_get_qe(hwq, hwq->depth, &psn_pg); - if (!IS_ALIGNED(fpsne, PAGE_SIZE)) - indx_pad = (fpsne & ~PAGE_MASK) / size; - hwq->pad_pgofft = indx_pad; - hwq->pad_pg = (u64 *)psn_pg; - hwq->pad_stride = size; -} - int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) { struct bnxt_qplib_rcfw *rcfw = res->rcfw; - struct bnxt_qplib_hwq_attr hwq_attr = {}; - struct bnxt_qplib_sg_info sginfo = {}; struct creq_create_qp_resp resp = {}; struct bnxt_qplib_cmdqmsg msg = {}; struct bnxt_qplib_q *sq = &qp->sq; struct bnxt_qplib_q *rq = &qp->rq; struct cmdq_create_qp req = {}; - int rc, req_size, psn_sz = 0; - struct bnxt_qplib_hwq *xrrq; struct bnxt_qplib_pbl *pbl; u32 qp_flags = 0; - u8 pg_sz_lvl; u32 tbl_indx; u16 nsge; + int rc; - qp->is_host_msn_tbl = _is_host_msn_table(res->dattr->dev_cap_flags2); sq->dbinfo.flags = 0; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_CREATE_QP, @@ -984,56 +981,10 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) req.qp_handle = cpu_to_le64(qp->qp_handle); /* SQ */ - if (qp->type == CMDQ_CREATE_QP_TYPE_RC) { - psn_sz = bnxt_qplib_is_chip_gen_p5_p7(res->cctx) ? - sizeof(struct sq_psn_search_ext) : - sizeof(struct sq_psn_search); - - if (qp->is_host_msn_tbl) { - psn_sz = sizeof(struct sq_msn_search); - qp->msn = 0; - } - } - - hwq_attr.res = res; - hwq_attr.sginfo = &sq->sg_info; - hwq_attr.stride = sizeof(struct sq_sge); - hwq_attr.depth = bnxt_qplib_get_depth(sq, qp->wqe_mode, true); - hwq_attr.aux_stride = psn_sz; - hwq_attr.aux_depth = psn_sz ? bnxt_qplib_set_sq_size(sq, qp->wqe_mode) - : 0; - /* Update msn tbl size */ - if (qp->is_host_msn_tbl && psn_sz) { - if (qp->wqe_mode == BNXT_QPLIB_WQE_MODE_STATIC) - hwq_attr.aux_depth = - roundup_pow_of_two(bnxt_qplib_set_sq_size(sq, qp->wqe_mode)); - else - hwq_attr.aux_depth = - roundup_pow_of_two(bnxt_qplib_set_sq_size(sq, qp->wqe_mode)) / 2; - qp->msn_tbl_sz = hwq_attr.aux_depth; - qp->msn = 0; - } - - hwq_attr.type = HWQ_TYPE_QUEUE; - rc = bnxt_qplib_alloc_init_hwq(&sq->hwq, &hwq_attr); - if (rc) - return rc; - - if (!sq->hwq.is_user) { - rc = bnxt_qplib_alloc_init_swq(sq); - if (rc) - goto fail_sq; - - if (psn_sz) - bnxt_qplib_init_psn_ptr(qp, psn_sz); - } - req.sq_size = cpu_to_le32(bnxt_qplib_set_sq_size(sq, qp->wqe_mode)); + req.sq_size = cpu_to_le32(sq->max_sw_wqe); pbl = &sq->hwq.pbl[PBL_LVL_0]; req.sq_pbl = cpu_to_le64(pbl->pg_map_arr[0]); - pg_sz_lvl = (bnxt_qplib_base_pg_size(&sq->hwq) << - CMDQ_CREATE_QP_SQ_PG_SIZE_SFT); - pg_sz_lvl |= (sq->hwq.level & CMDQ_CREATE_QP_SQ_LVL_MASK); - req.sq_pg_size_sq_lvl = pg_sz_lvl; + req.sq_pg_size_sq_lvl = sq->hwq.pg_sz_lvl; req.sq_fwo_sq_sge = cpu_to_le16(((sq->max_sge & CMDQ_CREATE_QP_SQ_SGE_MASK) << CMDQ_CREATE_QP_SQ_SGE_SFT) | 0); @@ -1042,29 +993,10 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) /* RQ */ if (!qp->srq) { rq->dbinfo.flags = 0; - hwq_attr.res = res; - hwq_attr.sginfo = &rq->sg_info; - hwq_attr.stride = sizeof(struct sq_sge); - hwq_attr.depth = bnxt_qplib_get_depth(rq, qp->wqe_mode, false); - hwq_attr.aux_stride = 0; - hwq_attr.aux_depth = 0; - hwq_attr.type = HWQ_TYPE_QUEUE; - rc = bnxt_qplib_alloc_init_hwq(&rq->hwq, &hwq_attr); - if (rc) - goto sq_swq; - if (!rq->hwq.is_user) { - rc = bnxt_qplib_alloc_init_swq(rq); - if (rc) - goto fail_rq; - } - req.rq_size = cpu_to_le32(rq->max_wqe); pbl = &rq->hwq.pbl[PBL_LVL_0]; req.rq_pbl = cpu_to_le64(pbl->pg_map_arr[0]); - pg_sz_lvl = (bnxt_qplib_base_pg_size(&rq->hwq) << - CMDQ_CREATE_QP_RQ_PG_SIZE_SFT); - pg_sz_lvl |= (rq->hwq.level & CMDQ_CREATE_QP_RQ_LVL_MASK); - req.rq_pg_size_rq_lvl = pg_sz_lvl; + req.rq_pg_size_rq_lvl = rq->hwq.pg_sz_lvl; nsge = (qp->wqe_mode == BNXT_QPLIB_WQE_MODE_STATIC) ? 6 : rq->max_sge; req.rq_fwo_rq_sge = @@ -1090,44 +1022,9 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) req.qp_flags = cpu_to_le32(qp_flags); /* ORRQ and IRRQ */ - if (psn_sz) { - xrrq = &qp->orrq; - xrrq->max_elements = - ORD_LIMIT_TO_ORRQ_SLOTS(qp->max_rd_atomic); - req_size = xrrq->max_elements * - BNXT_QPLIB_MAX_ORRQE_ENTRY_SIZE + PAGE_SIZE - 1; - req_size &= ~(PAGE_SIZE - 1); - sginfo.pgsize = req_size; - sginfo.pgshft = PAGE_SHIFT; - - hwq_attr.res = res; - hwq_attr.sginfo = &sginfo; - hwq_attr.depth = xrrq->max_elements; - hwq_attr.stride = BNXT_QPLIB_MAX_ORRQE_ENTRY_SIZE; - hwq_attr.aux_stride = 0; - hwq_attr.aux_depth = 0; - hwq_attr.type = HWQ_TYPE_CTX; - rc = bnxt_qplib_alloc_init_hwq(xrrq, &hwq_attr); - if (rc) - goto rq_swq; - pbl = &xrrq->pbl[PBL_LVL_0]; - req.orrq_addr = cpu_to_le64(pbl->pg_map_arr[0]); - - xrrq = &qp->irrq; - xrrq->max_elements = IRD_LIMIT_TO_IRRQ_SLOTS( - qp->max_dest_rd_atomic); - req_size = xrrq->max_elements * - BNXT_QPLIB_MAX_IRRQE_ENTRY_SIZE + PAGE_SIZE - 1; - req_size &= ~(PAGE_SIZE - 1); - sginfo.pgsize = req_size; - hwq_attr.depth = xrrq->max_elements; - hwq_attr.stride = BNXT_QPLIB_MAX_IRRQE_ENTRY_SIZE; - rc = bnxt_qplib_alloc_init_hwq(xrrq, &hwq_attr); - if (rc) - goto fail_orrq; - - pbl = &xrrq->pbl[PBL_LVL_0]; - req.irrq_addr = cpu_to_le64(pbl->pg_map_arr[0]); + if (qp->psn_sz) { + req.orrq_addr = cpu_to_le64(bnxt_qplib_get_base_addr(&qp->orrq)); + req.irrq_addr = cpu_to_le64(bnxt_qplib_get_base_addr(&qp->irrq)); } req.pd_id = cpu_to_le32(qp->pd->id); @@ -1135,23 +1032,23 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) sizeof(resp), 0); rc = bnxt_qplib_rcfw_send_message(rcfw, &msg); if (rc) - goto fail; + return rc; qp->id = le32_to_cpu(resp.xid); + + if (!qp->is_user) { + rc = bnxt_re_setup_qp_swqs(qp); + if (rc) + goto destroy_qp; + } + bnxt_qp_init_dbinfo(res, qp); + if (qp->psn_sz) + bnxt_qplib_init_psn_ptr(qp, qp->psn_sz); + qp->cur_qp_state = CMDQ_MODIFY_QP_NEW_STATE_RESET; INIT_LIST_HEAD(&qp->sq_flush); INIT_LIST_HEAD(&qp->rq_flush); qp->cctx = res->cctx; - sq->dbinfo.hwq = &sq->hwq; - sq->dbinfo.xid = qp->id; - sq->dbinfo.db = qp->dpi->dbr; - sq->dbinfo.max_slot = bnxt_qplib_set_sq_max_slot(qp->wqe_mode); - if (rq->max_wqe) { - rq->dbinfo.hwq = &rq->hwq; - rq->dbinfo.xid = qp->id; - rq->dbinfo.db = qp->dpi->dbr; - rq->dbinfo.max_slot = bnxt_qplib_set_rq_max_slot(rq->wqe_size); - } spin_lock_bh(&rcfw->tbl_lock); tbl_indx = map_qp_id_to_tbl_indx(qp->id, rcfw); rcfw->qp_tbl[tbl_indx].qp_id = qp->id; @@ -1159,18 +1056,8 @@ int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) spin_unlock_bh(&rcfw->tbl_lock); return 0; -fail: - bnxt_qplib_free_hwq(res, &qp->irrq); -fail_orrq: - bnxt_qplib_free_hwq(res, &qp->orrq); -rq_swq: - kfree(rq->swq); -fail_rq: - bnxt_qplib_free_hwq(res, &rq->hwq); -sq_swq: - kfree(sq->swq); -fail_sq: - bnxt_qplib_free_hwq(res, &sq->hwq); +destroy_qp: + bnxt_qplib_destroy_qp(res, qp); return rc; } diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h index 30c3f99be07b..5f671cc59100 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h @@ -279,6 +279,7 @@ struct bnxt_qplib_qp { u8 wqe_mode; u8 state; u8 cur_qp_state; + u8 is_user; u64 modify_flags; u32 ext_modify_flags; u32 max_inline_data; @@ -344,9 +345,11 @@ struct bnxt_qplib_qp { struct list_head rq_flush; u32 msn; u32 msn_tbl_sz; + u32 psn_sz; bool is_host_msn_tbl; u8 tos_dscp; u32 ugid_index; + u16 dev_cap_flags; u32 rate_limit; u8 shaper_allocation_status; }; @@ -617,6 +620,11 @@ static inline void bnxt_qplib_swq_mod_start(struct bnxt_qplib_q *que, u32 idx) que->swq_start = que->swq[idx].next_idx; } +static inline u32 bnxt_qplib_get_stride(void) +{ + return sizeof(struct sq_sge); +} + static inline u32 bnxt_qplib_get_depth(struct bnxt_qplib_q *que, u8 wqe_mode, bool is_sq) { u32 slots; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.c b/drivers/infiniband/hw/bnxt_re/qplib_res.c index 341bae3d8a1d..95e0489c53c3 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.c @@ -46,7 +46,7 @@ #include #include #include -#include +#include #include "roce_hsi.h" #include "qplib_res.h" @@ -683,6 +683,49 @@ static int bnxt_qplib_alloc_pd_tbl(struct bnxt_qplib_res *res, } /* DPIs */ +int bnxt_qplib_alloc_uc_dpi(struct bnxt_qplib_res *res, struct bnxt_qplib_dpi *dpi) +{ + struct bnxt_qplib_dpi_tbl *dpit = &res->dpi_tbl; + struct bnxt_qplib_reg_desc *reg; + u32 bit_num; + int rc = 0; + + reg = &dpit->wcreg; + mutex_lock(&res->dpi_tbl_lock); + bit_num = find_first_bit(dpit->tbl, dpit->max); + if (bit_num >= dpit->max) { + rc = -ENOMEM; + goto unlock; + } + /* Found unused DPI */ + clear_bit(bit_num, dpit->tbl); + dpi->bit = bit_num; + dpi->dpi = bit_num + (reg->offset - dpit->ucreg.offset) / PAGE_SIZE; + dpi->umdbr = reg->bar_base + reg->offset + bit_num * PAGE_SIZE; +unlock: + mutex_unlock(&res->dpi_tbl_lock); + return rc; +} + +int bnxt_qplib_free_uc_dpi(struct bnxt_qplib_res *res, struct bnxt_qplib_dpi *dpi) +{ + struct bnxt_qplib_dpi_tbl *dpit = &res->dpi_tbl; + int rc = 0; + + mutex_lock(&res->dpi_tbl_lock); + if (dpi->bit >= dpit->max) { + rc = -EINVAL; + goto unlock; + } + + if (test_and_set_bit(dpi->bit, dpit->tbl)) + rc = -EINVAL; + memset(dpi, 0, sizeof(*dpi)); +unlock: + mutex_unlock(&res->dpi_tbl_lock); + return rc; +} + int bnxt_qplib_alloc_dpi(struct bnxt_qplib_res *res, struct bnxt_qplib_dpi *dpi, void *app, u8 type) @@ -790,7 +833,7 @@ static int bnxt_qplib_alloc_dpi_tbl(struct bnxt_qplib_res *res, if (dev_attr->max_dpi) dpit->max = min_t(u32, dpit->max, dev_attr->max_dpi); - dpit->app_tbl = kcalloc(dpit->max, sizeof(void *), GFP_KERNEL); + dpit->app_tbl = kzalloc_objs(void *, dpit->max); if (!dpit->app_tbl) return -ENOMEM; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.h b/drivers/infiniband/hw/bnxt_re/qplib_res.h index 9a5dcf97b6f4..b925db796729 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.h @@ -39,7 +39,7 @@ #ifndef __BNXT_QPLIB_RES_H__ #define __BNXT_QPLIB_RES_H__ -#include "bnxt_ulp.h" +#include extern const struct bnxt_qplib_gid bnxt_qplib_gid_zero; @@ -198,6 +198,7 @@ struct bnxt_qplib_hwq { u32 cons; /* raw */ u8 cp_bit; u8 is_user; + u8 pg_sz_lvl; u64 *pad_pg; u32 pad_stride; u32 pad_pgofft; @@ -358,6 +359,11 @@ static inline u8 bnxt_qplib_get_ring_type(struct bnxt_qplib_chip_ctx *cctx) RING_ALLOC_REQ_RING_TYPE_ROCE_CMPL; } +static inline u64 bnxt_qplib_get_base_addr(struct bnxt_qplib_hwq *hwq) +{ + return hwq->pbl[PBL_LVL_0].pg_map_arr[0]; +} + static inline u8 bnxt_qplib_base_pg_size(struct bnxt_qplib_hwq *hwq) { u8 pg_size = BNXT_QPLIB_HWRM_PG_SIZE_4K; @@ -430,6 +436,10 @@ int bnxt_qplib_alloc_dpi(struct bnxt_qplib_res *res, void *app, u8 type); int bnxt_qplib_dealloc_dpi(struct bnxt_qplib_res *res, struct bnxt_qplib_dpi *dpi); +int bnxt_qplib_alloc_uc_dpi(struct bnxt_qplib_res *res, + struct bnxt_qplib_dpi *dpi); +int bnxt_qplib_free_uc_dpi(struct bnxt_qplib_res *res, + struct bnxt_qplib_dpi *dpi); void bnxt_qplib_cleanup_res(struct bnxt_qplib_res *res); int bnxt_qplib_init_res(struct bnxt_qplib_res *res); void bnxt_qplib_free_res(struct bnxt_qplib_res *res); diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c new file mode 100644 index 000000000000..3eaee7101615 --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* + * Copyright (c) 2025, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * Description: uapi interpreter + */ + +#include +#include +#include +#include +#define UVERBS_MODULE_NAME bnxt_re +#include +#include + +#include "roce_hsi.h" +#include "qplib_res.h" +#include "qplib_sp.h" +#include "qplib_fp.h" +#include "qplib_rcfw.h" +#include "bnxt_re.h" +#include "ib_verbs.h" + +static struct bnxt_re_cq *bnxt_re_search_for_cq(struct bnxt_re_dev *rdev, u32 cq_id) +{ + struct bnxt_re_cq *cq = NULL, *tmp_cq; + + hash_for_each_possible(rdev->cq_hash, tmp_cq, hash_entry, cq_id) { + if (tmp_cq->qplib_cq.id == cq_id) { + cq = tmp_cq; + break; + } + } + return cq; +} + +static struct bnxt_re_srq *bnxt_re_search_for_srq(struct bnxt_re_dev *rdev, u32 srq_id) +{ + struct bnxt_re_srq *srq = NULL, *tmp_srq; + + hash_for_each_possible(rdev->srq_hash, tmp_srq, hash_entry, srq_id) { + if (tmp_srq->qplib_srq.id == srq_id) { + srq = tmp_srq; + break; + } + } + return srq; +} + +static int UVERBS_HANDLER(BNXT_RE_METHOD_NOTIFY_DRV)(struct uverbs_attr_bundle *attrs) +{ + struct bnxt_re_ucontext *uctx; + struct ib_ucontext *ib_uctx; + + ib_uctx = ib_uverbs_get_ucontext(attrs); + if (IS_ERR(ib_uctx)) + return PTR_ERR(ib_uctx); + + uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); + if (IS_ERR(uctx)) + return PTR_ERR(uctx); + + bnxt_re_pacing_alert(uctx->rdev); + return 0; +} + +static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle *attrs) +{ + struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, BNXT_RE_ALLOC_PAGE_HANDLE); + enum bnxt_re_alloc_page_type alloc_type; + struct bnxt_re_user_mmap_entry *entry; + enum bnxt_re_mmap_flag mmap_flag; + struct bnxt_qplib_chip_ctx *cctx; + struct bnxt_re_ucontext *uctx; + struct ib_ucontext *ib_uctx; + struct bnxt_re_dev *rdev; + u64 mmap_offset; + u32 length; + u32 dpi; + u64 addr; + int err; + + ib_uctx = ib_uverbs_get_ucontext(attrs); + if (IS_ERR(ib_uctx)) + return PTR_ERR(ib_uctx); + + uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); + if (IS_ERR(uctx)) + return PTR_ERR(uctx); + + err = uverbs_get_const(&alloc_type, attrs, BNXT_RE_ALLOC_PAGE_TYPE); + if (err) + return err; + + rdev = uctx->rdev; + cctx = rdev->chip_ctx; + + switch (alloc_type) { + case BNXT_RE_ALLOC_WC_PAGE: + if (cctx->modes.db_push) { + if (bnxt_qplib_alloc_dpi(&rdev->qplib_res, &uctx->wcdpi, + uctx, BNXT_QPLIB_DPI_TYPE_WC)) + return -ENOMEM; + length = PAGE_SIZE; + dpi = uctx->wcdpi.dpi; + addr = (u64)uctx->wcdpi.umdbr; + mmap_flag = BNXT_RE_MMAP_WC_DB; + } else { + return -EINVAL; + } + + break; + case BNXT_RE_ALLOC_DBR_BAR_PAGE: + length = PAGE_SIZE; + addr = (u64)rdev->pacing.dbr_bar_addr; + mmap_flag = BNXT_RE_MMAP_DBR_BAR; + break; + + case BNXT_RE_ALLOC_DBR_PAGE: + length = PAGE_SIZE; + addr = (u64)rdev->pacing.dbr_page; + mmap_flag = BNXT_RE_MMAP_DBR_PAGE; + break; + + default: + return -EOPNOTSUPP; + } + + entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mmap_offset); + if (!entry) + return -ENOMEM; + + uobj->object = entry; + uverbs_finalize_uobj_create(attrs, BNXT_RE_ALLOC_PAGE_HANDLE); + err = uverbs_copy_to(attrs, BNXT_RE_ALLOC_PAGE_MMAP_OFFSET, + &mmap_offset, sizeof(mmap_offset)); + if (err) + return err; + + err = uverbs_copy_to(attrs, BNXT_RE_ALLOC_PAGE_MMAP_LENGTH, + &length, sizeof(length)); + if (err) + return err; + + err = uverbs_copy_to(attrs, BNXT_RE_ALLOC_PAGE_DPI, + &dpi, sizeof(dpi)); + if (err) + return err; + + return 0; +} + +static int alloc_page_obj_cleanup(struct ib_uobject *uobject, + enum rdma_remove_reason why, + struct uverbs_attr_bundle *attrs) +{ + struct bnxt_re_user_mmap_entry *entry = uobject->object; + struct bnxt_re_ucontext *uctx = entry->uctx; + + switch (entry->mmap_flag) { + case BNXT_RE_MMAP_WC_DB: + if (uctx && uctx->wcdpi.dbr) { + struct bnxt_re_dev *rdev = uctx->rdev; + + bnxt_qplib_dealloc_dpi(&rdev->qplib_res, &uctx->wcdpi); + uctx->wcdpi.dbr = NULL; + } + break; + case BNXT_RE_MMAP_DBR_BAR: + case BNXT_RE_MMAP_DBR_PAGE: + break; + default: + goto exit; + } + rdma_user_mmap_entry_remove(&entry->rdma_entry); +exit: + return 0; +} + +DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_ALLOC_PAGE, + UVERBS_ATTR_IDR(BNXT_RE_ALLOC_PAGE_HANDLE, + BNXT_RE_OBJECT_ALLOC_PAGE, + UVERBS_ACCESS_NEW, + UA_MANDATORY), + UVERBS_ATTR_CONST_IN(BNXT_RE_ALLOC_PAGE_TYPE, + enum bnxt_re_alloc_page_type, + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_PAGE_MMAP_OFFSET, + UVERBS_ATTR_TYPE(u64), + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_PAGE_MMAP_LENGTH, + UVERBS_ATTR_TYPE(u32), + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_PAGE_DPI, + UVERBS_ATTR_TYPE(u32), + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_METHOD_DESTROY(BNXT_RE_METHOD_DESTROY_PAGE, + UVERBS_ATTR_IDR(BNXT_RE_DESTROY_PAGE_HANDLE, + BNXT_RE_OBJECT_ALLOC_PAGE, + UVERBS_ACCESS_DESTROY, + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_OBJECT(BNXT_RE_OBJECT_ALLOC_PAGE, + UVERBS_TYPE_ALLOC_IDR(alloc_page_obj_cleanup), + &UVERBS_METHOD(BNXT_RE_METHOD_ALLOC_PAGE), + &UVERBS_METHOD(BNXT_RE_METHOD_DESTROY_PAGE)); + +DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_NOTIFY_DRV); + +DECLARE_UVERBS_GLOBAL_METHODS(BNXT_RE_OBJECT_NOTIFY_DRV, + &UVERBS_METHOD(BNXT_RE_METHOD_NOTIFY_DRV)); + +/* Toggle MEM */ +static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_TOGGLE_MEM)(struct uverbs_attr_bundle *attrs) +{ + struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, BNXT_RE_TOGGLE_MEM_HANDLE); + enum bnxt_re_mmap_flag mmap_flag = BNXT_RE_MMAP_TOGGLE_PAGE; + enum bnxt_re_get_toggle_mem_type res_type; + struct bnxt_re_user_mmap_entry *entry; + struct bnxt_re_ucontext *uctx; + struct ib_ucontext *ib_uctx; + struct bnxt_re_dev *rdev; + struct bnxt_re_srq *srq; + u32 length = PAGE_SIZE; + struct bnxt_re_cq *cq; + u64 mem_offset; + u32 offset = 0; + u64 addr = 0; + u32 res_id; + int err; + + ib_uctx = ib_uverbs_get_ucontext(attrs); + if (IS_ERR(ib_uctx)) + return PTR_ERR(ib_uctx); + + err = uverbs_get_const(&res_type, attrs, BNXT_RE_TOGGLE_MEM_TYPE); + if (err) + return err; + + uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); + rdev = uctx->rdev; + err = uverbs_copy_from(&res_id, attrs, BNXT_RE_TOGGLE_MEM_RES_ID); + if (err) + return err; + + switch (res_type) { + case BNXT_RE_CQ_TOGGLE_MEM: + cq = bnxt_re_search_for_cq(rdev, res_id); + if (!cq) + return -EINVAL; + + addr = (u64)cq->uctx_cq_page; + break; + case BNXT_RE_SRQ_TOGGLE_MEM: + srq = bnxt_re_search_for_srq(rdev, res_id); + if (!srq) + return -EINVAL; + + addr = (u64)srq->uctx_srq_page; + break; + + default: + return -EOPNOTSUPP; + } + + entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mem_offset); + if (!entry) + return -ENOMEM; + + uobj->object = entry; + uverbs_finalize_uobj_create(attrs, BNXT_RE_TOGGLE_MEM_HANDLE); + err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_PAGE, + &mem_offset, sizeof(mem_offset)); + if (err) + return err; + + err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_LENGTH, + &length, sizeof(length)); + if (err) + return err; + + err = uverbs_copy_to(attrs, BNXT_RE_TOGGLE_MEM_MMAP_OFFSET, + &offset, sizeof(offset)); + if (err) + return err; + + return 0; +} + +static int get_toggle_mem_obj_cleanup(struct ib_uobject *uobject, + enum rdma_remove_reason why, + struct uverbs_attr_bundle *attrs) +{ + struct bnxt_re_user_mmap_entry *entry = uobject->object; + + rdma_user_mmap_entry_remove(&entry->rdma_entry); + return 0; +} + +DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_GET_TOGGLE_MEM, + UVERBS_ATTR_IDR(BNXT_RE_TOGGLE_MEM_HANDLE, + BNXT_RE_OBJECT_GET_TOGGLE_MEM, + UVERBS_ACCESS_NEW, + UA_MANDATORY), + UVERBS_ATTR_CONST_IN(BNXT_RE_TOGGLE_MEM_TYPE, + enum bnxt_re_get_toggle_mem_type, + UA_MANDATORY), + UVERBS_ATTR_PTR_IN(BNXT_RE_TOGGLE_MEM_RES_ID, + UVERBS_ATTR_TYPE(u32), + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_PAGE, + UVERBS_ATTR_TYPE(u64), + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_OFFSET, + UVERBS_ATTR_TYPE(u32), + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_TOGGLE_MEM_MMAP_LENGTH, + UVERBS_ATTR_TYPE(u32), + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_METHOD_DESTROY(BNXT_RE_METHOD_RELEASE_TOGGLE_MEM, + UVERBS_ATTR_IDR(BNXT_RE_RELEASE_TOGGLE_MEM_HANDLE, + BNXT_RE_OBJECT_GET_TOGGLE_MEM, + UVERBS_ACCESS_DESTROY, + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_OBJECT(BNXT_RE_OBJECT_GET_TOGGLE_MEM, + UVERBS_TYPE_ALLOC_IDR(get_toggle_mem_obj_cleanup), + &UVERBS_METHOD(BNXT_RE_METHOD_GET_TOGGLE_MEM), + &UVERBS_METHOD(BNXT_RE_METHOD_RELEASE_TOGGLE_MEM)); + +static int UVERBS_HANDLER(BNXT_RE_METHOD_DBR_ALLOC)(struct uverbs_attr_bundle *attrs) +{ + struct bnxt_re_db_region dbr = {}; + struct bnxt_re_ucontext *uctx; + struct bnxt_re_dbr_obj *obj; + struct ib_ucontext *ib_uctx; + struct bnxt_qplib_dpi *dpi; + struct bnxt_re_dev *rdev; + struct ib_uobject *uobj; + u64 mmap_offset; + int ret; + + ib_uctx = ib_uverbs_get_ucontext(attrs); + if (IS_ERR(ib_uctx)) + return PTR_ERR(ib_uctx); + + uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); + rdev = uctx->rdev; + uobj = uverbs_attr_get_uobject(attrs, BNXT_RE_ALLOC_DBR_HANDLE); + + obj = kzalloc_obj(*obj); + if (!obj) + return -ENOMEM; + + dpi = &obj->dpi; + ret = bnxt_qplib_alloc_uc_dpi(&rdev->qplib_res, dpi); + if (ret) + goto free_mem; + + obj->entry = bnxt_re_mmap_entry_insert(uctx, dpi->umdbr, + BNXT_RE_MMAP_UC_DB, + &mmap_offset); + if (!obj->entry) { + ret = -ENOMEM; + goto free_dpi; + } + + obj->rdev = rdev; + uobj->object = obj; + uverbs_finalize_uobj_create(attrs, BNXT_RE_ALLOC_DBR_HANDLE); + + dbr.umdbr = dpi->umdbr; + dbr.dpi = dpi->dpi; + ret = uverbs_copy_to_struct_or_zero(attrs, BNXT_RE_ALLOC_DBR_ATTR, + &dbr, sizeof(dbr)); + if (ret) + return ret; + + ret = uverbs_copy_to(attrs, BNXT_RE_ALLOC_DBR_OFFSET, + &mmap_offset, sizeof(mmap_offset)); + if (ret) + return ret; + return 0; +free_dpi: + bnxt_qplib_free_uc_dpi(&rdev->qplib_res, dpi); +free_mem: + kfree(obj); + return ret; +} + +static int bnxt_re_dbr_cleanup(struct ib_uobject *uobject, + enum rdma_remove_reason why, + struct uverbs_attr_bundle *attrs) +{ + struct bnxt_re_dbr_obj *obj = uobject->object; + struct bnxt_re_dev *rdev = obj->rdev; + + rdma_user_mmap_entry_remove(&obj->entry->rdma_entry); + bnxt_qplib_free_uc_dpi(&rdev->qplib_res, &obj->dpi); + return 0; +} + +static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_DEFAULT_DBR)(struct uverbs_attr_bundle *attrs) +{ + struct bnxt_re_db_region dpi = {}; + struct bnxt_re_ucontext *uctx; + struct ib_ucontext *ib_uctx; + int ret; + + ib_uctx = ib_uverbs_get_ucontext(attrs); + if (IS_ERR(ib_uctx)) + return PTR_ERR(ib_uctx); + + uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); + dpi.umdbr = uctx->dpi.umdbr; + dpi.dpi = uctx->dpi.dpi; + + ret = uverbs_copy_to_struct_or_zero(attrs, BNXT_RE_DEFAULT_DBR_ATTR, + &dpi, sizeof(dpi)); + if (ret) + return ret; + + return 0; +} + +DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_DBR_ALLOC, + UVERBS_ATTR_IDR(BNXT_RE_ALLOC_DBR_HANDLE, + BNXT_RE_OBJECT_DBR, + UVERBS_ACCESS_NEW, + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_DBR_ATTR, + UVERBS_ATTR_STRUCT(struct bnxt_re_db_region, + umdbr), + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_DBR_OFFSET, + UVERBS_ATTR_TYPE(u64), + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_METHOD_DESTROY(BNXT_RE_METHOD_DBR_FREE, + UVERBS_ATTR_IDR(BNXT_RE_FREE_DBR_HANDLE, + BNXT_RE_OBJECT_DBR, + UVERBS_ACCESS_DESTROY, + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_OBJECT(BNXT_RE_OBJECT_DBR, + UVERBS_TYPE_ALLOC_IDR(bnxt_re_dbr_cleanup), + &UVERBS_METHOD(BNXT_RE_METHOD_DBR_ALLOC), + &UVERBS_METHOD(BNXT_RE_METHOD_DBR_FREE)); + +DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_GET_DEFAULT_DBR, + UVERBS_ATTR_PTR_OUT(BNXT_RE_DEFAULT_DBR_ATTR, + UVERBS_ATTR_STRUCT(struct bnxt_re_db_region, + umdbr), + UA_MANDATORY)); + +DECLARE_UVERBS_GLOBAL_METHODS(BNXT_RE_OBJECT_DEFAULT_DBR, + &UVERBS_METHOD(BNXT_RE_METHOD_GET_DEFAULT_DBR)); + +const struct uapi_definition bnxt_re_uapi_defs[] = { + UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_ALLOC_PAGE), + UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_NOTIFY_DRV), + UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_GET_TOGGLE_MEM), + UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_DBR), + UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_DEFAULT_DBR), + {} +}; diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c index b8d49abde099..9fde78b74690 100644 --- a/drivers/infiniband/hw/cxgb4/mem.c +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -32,9 +32,9 @@ #include #include -#include #include #include +#include #include "iw_cxgb4.h" diff --git a/drivers/infiniband/hw/efa/efa.h b/drivers/infiniband/hw/efa/efa.h index 96f9c3bc98b2..00b19f2ba3da 100644 --- a/drivers/infiniband/hw/efa/efa.h +++ b/drivers/infiniband/hw/efa/efa.h @@ -161,10 +161,8 @@ int efa_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata); int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, struct ib_udata *udata); int efa_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata); -int efa_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, - struct uverbs_attr_bundle *attrs); -int efa_create_cq_umem(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, - struct ib_umem *umem, struct uverbs_attr_bundle *attrs); +int efa_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs); struct ib_mr *efa_reg_mr(struct ib_pd *ibpd, u64 start, u64 length, u64 virt_addr, int access_flags, struct ib_dmah *dmah, diff --git a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h index 57178dad5eb7..ad34ea5da6b0 100644 --- a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h +++ b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ /* - * Copyright 2018-2025 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2026 Amazon.com, Inc. or its affiliates. All rights reserved. */ #ifndef _EFA_ADMIN_CMDS_H_ @@ -38,10 +38,11 @@ enum efa_admin_aq_feature_id { EFA_ADMIN_DEVICE_ATTR = 1, EFA_ADMIN_AENQ_CONFIG = 2, EFA_ADMIN_NETWORK_ATTR = 3, - EFA_ADMIN_QUEUE_ATTR = 4, + EFA_ADMIN_QUEUE_ATTR_1 = 4, EFA_ADMIN_HW_HINTS = 5, EFA_ADMIN_HOST_INFO = 6, EFA_ADMIN_EVENT_QUEUE_ATTR = 7, + EFA_ADMIN_QUEUE_ATTR_2 = 9, }; /* QP transport type */ @@ -744,14 +745,19 @@ struct efa_admin_feature_device_attr_desc { u32 reserved1; }; -struct efa_admin_feature_queue_attr_desc { +struct efa_admin_feature_queue_attr_desc_1 { /* The maximum number of queue pairs supported */ u32 max_qp; /* Maximum number of WQEs per Send Queue */ u32 max_sq_depth; - /* Maximum size of data that can be sent inline in a Send WQE */ + /* + * Maximum size of data that can be sent inline in a Send WQE + * (deprecated by + * efa_admin_feature_queue_attr_desc_2::inline_buf_size_ex on + * supporting devices) + */ u32 inline_buf_size; /* Maximum number of buffer descriptors per Recv Queue */ @@ -805,6 +811,11 @@ struct efa_admin_feature_queue_attr_desc { u16 max_tx_batch; }; +struct efa_admin_feature_queue_attr_desc_2 { + /* Maximum size of data that can be sent inline in a Send WQE */ + u16 inline_buf_size_ex; +}; + struct efa_admin_event_queue_attr_desc { /* The maximum number of event queues supported */ u32 max_eq; @@ -872,7 +883,9 @@ struct efa_admin_get_feature_resp { struct efa_admin_feature_network_attr_desc network_attr; - struct efa_admin_feature_queue_attr_desc queue_attr; + struct efa_admin_feature_queue_attr_desc_1 queue_attr_1; + + struct efa_admin_feature_queue_attr_desc_2 queue_attr_2; struct efa_admin_event_queue_attr_desc event_queue_attr; diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.c b/drivers/infiniband/hw/efa/efa_com_cmd.c index 9ead02800ac7..63c7f07806a8 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.c +++ b/drivers/infiniband/hw/efa/efa_com_cmd.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause /* - * Copyright 2018-2025 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2026 Amazon.com, Inc. or its affiliates. All rights reserved. */ #include "efa_com.h" @@ -479,31 +479,46 @@ int efa_com_get_device_attr(struct efa_com_dev *edev, edev->supported_features = resp.u.device_attr.supported_features; err = efa_com_get_feature(edev, &resp, - EFA_ADMIN_QUEUE_ATTR); + EFA_ADMIN_QUEUE_ATTR_1); if (err) { ibdev_err_ratelimited(edev->efa_dev, - "Failed to get queue attributes %d\n", + "Failed to get queue attributes1 %d\n", err); return err; } - result->max_qp = resp.u.queue_attr.max_qp; - result->max_sq_depth = resp.u.queue_attr.max_sq_depth; - result->max_rq_depth = resp.u.queue_attr.max_rq_depth; - result->max_cq = resp.u.queue_attr.max_cq; - result->max_cq_depth = resp.u.queue_attr.max_cq_depth; - result->inline_buf_size = resp.u.queue_attr.inline_buf_size; - result->max_sq_sge = resp.u.queue_attr.max_wr_send_sges; - result->max_rq_sge = resp.u.queue_attr.max_wr_recv_sges; - result->max_mr = resp.u.queue_attr.max_mr; - result->max_mr_pages = resp.u.queue_attr.max_mr_pages; - result->max_pd = resp.u.queue_attr.max_pd; - result->max_ah = resp.u.queue_attr.max_ah; - result->max_llq_size = resp.u.queue_attr.max_llq_size; - result->sub_cqs_per_cq = resp.u.queue_attr.sub_cqs_per_cq; - result->max_wr_rdma_sge = resp.u.queue_attr.max_wr_rdma_sges; - result->max_tx_batch = resp.u.queue_attr.max_tx_batch; - result->min_sq_depth = resp.u.queue_attr.min_sq_depth; + result->max_qp = resp.u.queue_attr_1.max_qp; + result->max_sq_depth = resp.u.queue_attr_1.max_sq_depth; + result->max_rq_depth = resp.u.queue_attr_1.max_rq_depth; + result->max_cq = resp.u.queue_attr_1.max_cq; + result->max_cq_depth = resp.u.queue_attr_1.max_cq_depth; + result->inline_buf_size = resp.u.queue_attr_1.inline_buf_size; + result->max_sq_sge = resp.u.queue_attr_1.max_wr_send_sges; + result->max_rq_sge = resp.u.queue_attr_1.max_wr_recv_sges; + result->max_mr = resp.u.queue_attr_1.max_mr; + result->max_mr_pages = resp.u.queue_attr_1.max_mr_pages; + result->max_pd = resp.u.queue_attr_1.max_pd; + result->max_ah = resp.u.queue_attr_1.max_ah; + result->max_llq_size = resp.u.queue_attr_1.max_llq_size; + result->sub_cqs_per_cq = resp.u.queue_attr_1.sub_cqs_per_cq; + result->max_wr_rdma_sge = resp.u.queue_attr_1.max_wr_rdma_sges; + result->max_tx_batch = resp.u.queue_attr_1.max_tx_batch; + result->min_sq_depth = resp.u.queue_attr_1.min_sq_depth; + + if (efa_com_check_supported_feature_id(edev, EFA_ADMIN_QUEUE_ATTR_2)) { + err = efa_com_get_feature(edev, &resp, + EFA_ADMIN_QUEUE_ATTR_2); + if (err) { + ibdev_err_ratelimited( + edev->efa_dev, + "Failed to get queue attributes2 %d\n", err); + return err; + } + + result->inline_buf_size_ex = resp.u.queue_attr_2.inline_buf_size_ex; + } else { + result->inline_buf_size_ex = result->inline_buf_size; + } err = efa_com_get_feature(edev, &resp, EFA_ADMIN_NETWORK_ATTR); if (err) { diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.h b/drivers/infiniband/hw/efa/efa_com_cmd.h index 3ac2686abba1..ef15b3c38429 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.h +++ b/drivers/infiniband/hw/efa/efa_com_cmd.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ /* - * Copyright 2018-2025 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2026 Amazon.com, Inc. or its affiliates. All rights reserved. */ #ifndef _EFA_COM_CMD_H_ @@ -127,6 +127,7 @@ struct efa_com_get_device_attr_result { u32 max_cq; u32 max_cq_depth; /* cqes */ u32 inline_buf_size; + u32 inline_buf_size_ex; u32 max_mr; u32 max_pd; u32 max_ah; diff --git a/drivers/infiniband/hw/efa/efa_main.c b/drivers/infiniband/hw/efa/efa_main.c index c1397086dc47..03c237c8c81e 100644 --- a/drivers/infiniband/hw/efa/efa_main.c +++ b/drivers/infiniband/hw/efa/efa_main.c @@ -371,8 +371,7 @@ static const struct ib_device_ops efa_dev_ops = { .alloc_hw_device_stats = efa_alloc_hw_device_stats, .alloc_pd = efa_alloc_pd, .alloc_ucontext = efa_alloc_ucontext, - .create_cq = efa_create_cq, - .create_cq_umem = efa_create_cq_umem, + .create_user_cq = efa_create_user_cq, .create_qp = efa_create_qp, .create_user_ah = efa_create_ah, .dealloc_pd = efa_dealloc_pd, diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index b0aa7c0238ed..7bd0838ebc99 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB /* - * Copyright 2018-2024 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2026 Amazon.com, Inc. or its affiliates. All rights reserved. */ #include @@ -9,9 +9,9 @@ #include #include -#include #include #include +#include #include #define UVERBS_MODULE_NAME efa_ib #include @@ -579,7 +579,7 @@ static int qp_mmap_entries_setup(struct efa_qp *qp, resp->llq_desc_offset &= ~PAGE_MASK; - if (qp->rq_size) { + if (qp->rq_cpu_addr) { address = dev->db_bar_addr + resp->rq_db_offset; qp->rq_db_mmap_entry = @@ -641,11 +641,11 @@ static int efa_qp_validate_cap(struct efa_dev *dev, init_attr->cap.max_recv_sge, dev->dev_attr.max_rq_sge); return -EINVAL; } - if (init_attr->cap.max_inline_data > dev->dev_attr.inline_buf_size) { + if (init_attr->cap.max_inline_data > dev->dev_attr.inline_buf_size_ex) { ibdev_dbg(&dev->ibdev, "qp: requested inline data[%u] exceeds the max[%u]\n", init_attr->cap.max_inline_data, - dev->dev_attr.inline_buf_size); + dev->dev_attr.inline_buf_size_ex); return -EINVAL; } @@ -682,7 +682,7 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, struct efa_com_create_qp_result create_qp_resp; struct efa_dev *dev = to_edev(ibqp->device); struct efa_ibv_create_qp_resp resp = {}; - struct efa_ibv_create_qp cmd = {}; + struct efa_ibv_create_qp cmd; struct efa_qp *qp = to_eqp(ibqp); struct efa_ucontext *ucontext; u16 supported_efa_flags = 0; @@ -699,31 +699,11 @@ int efa_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, if (err) goto err_out; - if (offsetofend(typeof(cmd), driver_qp_type) > udata->inlen) { - ibdev_dbg(&dev->ibdev, - "Incompatible ABI params, no input udata\n"); - err = -EINVAL; + err = ib_copy_validate_udata_in_cm(udata, cmd, driver_qp_type, 0); + if (err) goto err_out; - } - if (udata->inlen > sizeof(cmd) && - !ib_is_udata_cleared(udata, sizeof(cmd), - udata->inlen - sizeof(cmd))) { - ibdev_dbg(&dev->ibdev, - "Incompatible ABI params, unknown fields in udata\n"); - err = -EINVAL; - goto err_out; - } - - err = ib_copy_from_udata(&cmd, udata, - min(sizeof(cmd), udata->inlen)); - if (err) { - ibdev_dbg(&dev->ibdev, - "Cannot copy udata for create_qp\n"); - goto err_out; - } - - if (cmd.comp_mask || !is_reserved_cleared(cmd.reserved_98)) { + if (!is_reserved_cleared(cmd.reserved_98)) { ibdev_dbg(&dev->ibdev, "Incompatible ABI params, unknown fields in udata\n"); err = -EINVAL; @@ -828,7 +808,7 @@ err_remove_mmap_entries: err_destroy_qp: efa_destroy_qp_handle(dev, create_qp_resp.qp_handle); err_free_mapped: - if (qp->rq_size) + if (qp->rq_cpu_addr) efa_free_mapped(dev, qp->rq_cpu_addr, qp->rq_dma_addr, qp->rq_size, DMA_TO_DEVICE); err_out: @@ -1083,15 +1063,14 @@ int efa_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) cq->cq_idx, cq->cpu_addr, cq->size, &cq->dma_addr); efa_destroy_cq_idx(dev, cq->cq_idx); - efa_cq_user_mmap_entries_remove(cq); + if (cq->cpu_addr) + efa_cq_user_mmap_entries_remove(cq); if (cq->eq) { xa_erase(&dev->cqs_xa, cq->cq_idx); synchronize_irq(cq->eq->irq.irqn); } - if (cq->umem) - ib_umem_release(cq->umem); - else + if (cq->cpu_addr) efa_free_mapped(dev, cq->cpu_addr, cq->dma_addr, cq->size, DMA_FROM_DEVICE); return 0; } @@ -1131,8 +1110,8 @@ static int cq_mmap_entries_setup(struct efa_dev *dev, struct efa_cq *cq, return 0; } -int efa_create_cq_umem(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, - struct ib_umem *umem, struct uverbs_attr_bundle *attrs) +int efa_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs) { struct ib_udata *udata = &attrs->driver_udata; struct efa_ucontext *ucontext = rdma_udata_to_drv_context( @@ -1142,7 +1121,7 @@ int efa_create_cq_umem(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, struct efa_com_create_cq_result result; struct ib_device *ibdev = ibcq->device; struct efa_dev *dev = to_edev(ibdev); - struct efa_ibv_create_cq cmd = {}; + struct efa_ibv_create_cq cmd; struct efa_cq *cq = to_ecq(ibcq); int entries = attr->cqe; bool set_src_addr; @@ -1153,38 +1132,19 @@ int efa_create_cq_umem(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, if (attr->flags) return -EOPNOTSUPP; - if (entries < 1 || entries > dev->dev_attr.max_cq_depth) { + if (entries > dev->dev_attr.max_cq_depth) { ibdev_dbg(ibdev, - "cq: requested entries[%u] non-positive or greater than max[%u]\n", + "cq: requested entries[%u] greater than max[%u]\n", entries, dev->dev_attr.max_cq_depth); err = -EINVAL; goto err_out; } - if (offsetofend(typeof(cmd), num_sub_cqs) > udata->inlen) { - ibdev_dbg(ibdev, - "Incompatible ABI params, no input udata\n"); - err = -EINVAL; + err = ib_copy_validate_udata_in_cm(udata, cmd, num_sub_cqs, 0); + if (err) goto err_out; - } - if (udata->inlen > sizeof(cmd) && - !ib_is_udata_cleared(udata, sizeof(cmd), - udata->inlen - sizeof(cmd))) { - ibdev_dbg(ibdev, - "Incompatible ABI params, unknown fields in udata\n"); - err = -EINVAL; - goto err_out; - } - - err = ib_copy_from_udata(&cmd, udata, - min(sizeof(cmd), udata->inlen)); - if (err) { - ibdev_dbg(ibdev, "Cannot copy udata for create_cq\n"); - goto err_out; - } - - if (cmd.comp_mask || !is_reserved_cleared(cmd.reserved_58)) { + if (!is_reserved_cleared(cmd.reserved_58)) { ibdev_dbg(ibdev, "Incompatible ABI params, unknown fields in udata\n"); err = -EINVAL; @@ -1212,22 +1172,20 @@ int efa_create_cq_umem(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, cq->ucontext = ucontext; cq->size = PAGE_ALIGN(cmd.cq_entry_size * entries * cmd.num_sub_cqs); - if (umem) { - if (umem->length < cq->size) { + if (ibcq->umem) { + if (ibcq->umem->length < cq->size) { ibdev_dbg(&dev->ibdev, "External memory too small\n"); err = -EINVAL; goto err_out; } - if (!ib_umem_is_contiguous(umem)) { + if (!ib_umem_is_contiguous(ibcq->umem)) { ibdev_dbg(&dev->ibdev, "Non contiguous CQ unsupported\n"); err = -EINVAL; goto err_out; } - cq->cpu_addr = NULL; - cq->dma_addr = ib_umem_start_dma_addr(umem); - cq->umem = umem; + cq->dma_addr = ib_umem_start_dma_addr(ibcq->umem); } else { cq->cpu_addr = efa_zalloc_mapped(dev, &cq->dma_addr, cq->size, DMA_FROM_DEVICE); @@ -1259,7 +1217,7 @@ int efa_create_cq_umem(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, cq->ibcq.cqe = result.actual_depth; WARN_ON_ONCE(entries != result.actual_depth); - if (!umem) + if (cq->cpu_addr) err = cq_mmap_entries_setup(dev, cq, &resp, result.db_valid); if (err) { @@ -1296,11 +1254,12 @@ err_xa_erase: if (cq->eq) xa_erase(&dev->cqs_xa, cq->cq_idx); err_remove_mmap: - efa_cq_user_mmap_entries_remove(cq); + if (cq->cpu_addr) + efa_cq_user_mmap_entries_remove(cq); err_destroy_cq: efa_destroy_cq_idx(dev, cq->cq_idx); err_free_mapped: - if (!umem) + if (cq->cpu_addr) efa_free_mapped(dev, cq->cpu_addr, cq->dma_addr, cq->size, DMA_FROM_DEVICE); err_out: @@ -1308,12 +1267,6 @@ err_out: return err; } -int efa_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, - struct uverbs_attr_bundle *attrs) -{ - return efa_create_cq_umem(ibcq, attr, NULL, attrs); -} - static int umem_to_page_list(struct efa_dev *dev, struct ib_umem *umem, u64 *page_list, @@ -1925,22 +1878,23 @@ static int efa_dealloc_uar(struct efa_dev *dev, u16 uarn) return efa_com_dealloc_uar(&dev->edev, ¶ms); } -#define EFA_CHECK_USER_COMP(_dev, _comp_mask, _attr, _mask, _attr_str) \ - (_attr_str = (!(_dev)->dev_attr._attr || ((_comp_mask) & (_mask))) ? \ +#define EFA_CHECK_USER_SUPP(_dev, _supported_caps, _attr, _mask, _attr_str) \ + (_attr_str = (!(_dev)->dev_attr._attr || ((_supported_caps) & (_mask))) ? \ NULL : #_attr) -static int efa_user_comp_handshake(const struct ib_ucontext *ibucontext, +static int efa_user_supp_handshake(const struct ib_ucontext *ibucontext, const struct efa_ibv_alloc_ucontext_cmd *cmd) { struct efa_dev *dev = to_edev(ibucontext->device); char *attr_str; - if (EFA_CHECK_USER_COMP(dev, cmd->comp_mask, max_tx_batch, - EFA_ALLOC_UCONTEXT_CMD_COMP_TX_BATCH, attr_str)) + if (EFA_CHECK_USER_SUPP(dev, cmd->supported_caps, max_tx_batch, + EFA_ALLOC_UCONTEXT_CMD_SUPP_CAPS_TX_BATCH, + attr_str)) goto err; - if (EFA_CHECK_USER_COMP(dev, cmd->comp_mask, min_sq_depth, - EFA_ALLOC_UCONTEXT_CMD_COMP_MIN_SQ_WR, + if (EFA_CHECK_USER_SUPP(dev, cmd->supported_caps, min_sq_depth, + EFA_ALLOC_UCONTEXT_CMD_SUPP_CAPS_MIN_SQ_WR, attr_str)) goto err; @@ -1974,7 +1928,7 @@ int efa_alloc_ucontext(struct ib_ucontext *ibucontext, struct ib_udata *udata) goto err_out; } - err = efa_user_comp_handshake(ibucontext, &cmd); + err = efa_user_supp_handshake(ibucontext, &cmd); if (err) goto err_out; @@ -1988,6 +1942,7 @@ int efa_alloc_ucontext(struct ib_ucontext *ibucontext, struct ib_udata *udata) resp.cmds_supp_udata_mask |= EFA_USER_CMDS_SUPP_UDATA_CREATE_AH; resp.sub_cqs_per_cq = dev->dev_attr.sub_cqs_per_cq; resp.inline_buf_size = dev->dev_attr.inline_buf_size; + resp.inline_buf_size_ex = dev->dev_attr.inline_buf_size_ex; resp.max_llq_size = dev->dev_attr.max_llq_size; resp.max_tx_batch = dev->dev_attr.max_tx_batch; resp.min_sq_wr = dev->dev_attr.min_sq_depth; diff --git a/drivers/infiniband/hw/erdma/erdma.h b/drivers/infiniband/hw/erdma/erdma.h index 2a023b99f992..ceabbdf2556f 100644 --- a/drivers/infiniband/hw/erdma/erdma.h +++ b/drivers/infiniband/hw/erdma/erdma.h @@ -127,7 +127,6 @@ struct erdma_devattr { unsigned char peer_addr[ETH_ALEN]; unsigned long cap_flags; - int numa_node; enum erdma_cc_alg cc; u32 irq_num; diff --git a/drivers/infiniband/hw/erdma/erdma_eq.c b/drivers/infiniband/hw/erdma/erdma_eq.c index 6486234a2360..d5b9d19882b2 100644 --- a/drivers/infiniband/hw/erdma/erdma_eq.c +++ b/drivers/infiniband/hw/erdma/erdma_eq.c @@ -197,7 +197,8 @@ static int erdma_set_ceq_irq(struct erdma_dev *dev, u16 ceqn) tasklet_init(&dev->ceqs[ceqn].tasklet, erdma_intr_ceq_task, (unsigned long)&dev->ceqs[ceqn]); - cpumask_set_cpu(cpumask_local_spread(ceqn + 1, dev->attrs.numa_node), + cpumask_set_cpu(cpumask_local_spread(ceqn + 1, + dev_to_node(&dev->pdev->dev)), &eqc->irq.affinity_hint_mask); err = request_irq(eqc->irq.msix_vector, erdma_intr_ceq_handler, 0, diff --git a/drivers/infiniband/hw/erdma/erdma_main.c b/drivers/infiniband/hw/erdma/erdma_main.c index f35b30235018..7e87a815e853 100644 --- a/drivers/infiniband/hw/erdma/erdma_main.c +++ b/drivers/infiniband/hw/erdma/erdma_main.c @@ -261,7 +261,6 @@ static int erdma_probe_dev(struct pci_dev *pdev) pci_set_drvdata(pdev, dev); dev->pdev = pdev; - dev->attrs.numa_node = dev_to_node(&pdev->dev); bars = pci_select_bars(pdev, IORESOURCE_MEM); err = pci_request_selected_regions(pdev, bars, DRV_MODULE_NAME); diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.c b/drivers/infiniband/hw/erdma/erdma_verbs.c index 9f74aadc3047..5523b4e151e1 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.c +++ b/drivers/infiniband/hw/erdma/erdma_verbs.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include "erdma.h" @@ -1039,8 +1039,7 @@ int erdma_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, qp->attrs.rq_size = roundup_pow_of_two(attrs->cap.max_recv_wr); if (uctx) { - ret = ib_copy_from_udata(&ureq, udata, - min(sizeof(ureq), udata->inlen)); + ret = ib_copy_validate_udata_in(udata, ureq, rsvd0); if (ret) goto err_out_xa; @@ -1980,8 +1979,7 @@ int erdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, struct erdma_ureq_create_cq ureq; struct erdma_uresp_create_cq uresp; - ret = ib_copy_from_udata(&ureq, udata, - min(udata->inlen, sizeof(ureq))); + ret = ib_copy_validate_udata_in(udata, ureq, rsvd0); if (ret) goto err_out_xa; diff --git a/drivers/infiniband/hw/hfi1/Makefile b/drivers/infiniband/hw/hfi1/Makefile index 5d977f363684..b5551bd4703b 100644 --- a/drivers/infiniband/hw/hfi1/Makefile +++ b/drivers/infiniband/hw/hfi1/Makefile @@ -49,9 +49,7 @@ hfi1-y := \ user_pages.o \ user_sdma.o \ verbs.o \ - verbs_txreq.o \ - vnic_main.o \ - vnic_sdma.o + verbs_txreq.o ifdef CONFIG_DEBUG_FS hfi1-y += debugfs.o diff --git a/drivers/infiniband/hw/hfi1/aspm.c b/drivers/infiniband/hw/hfi1/aspm.c index 79990d09522b..61455d4ac6c2 100644 --- a/drivers/infiniband/hw/hfi1/aspm.c +++ b/drivers/infiniband/hw/hfi1/aspm.c @@ -179,7 +179,7 @@ static void aspm_ctx_timer_function(struct timer_list *t) } /* - * Disable interrupt processing for verbs contexts when PSM or VNIC contexts + * Disable interrupt processing for verbs contexts when PSM contexts * are open. */ void aspm_disable_all(struct hfi1_devdata *dd) diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index 6a9db4d17c5a..44c524e45396 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -85,12 +85,12 @@ struct flag_table { /* * RSM instance allocation * 0 - User Fecn Handling - * 1 - Vnic + * 1 - Deprecated * 2 - AIP * 3 - Verbs */ #define RSM_INS_FECN 0 -#define RSM_INS_VNIC 1 +#define RSM_INS_DEPRECATED 1 #define RSM_INS_AIP 2 #define RSM_INS_VERBS 3 @@ -152,15 +152,6 @@ struct flag_table { #define DETH_AIP_SQPN_SELECT_OFFSET \ DETH_AIP_SQPN_OFFSET(DETH_AIP_SQPN_BIT_OFFSET) -/* RSM fields for Vnic */ -/* L2_TYPE: QW 0, OFFSET 61 - for match */ -#define L2_TYPE_QW 0ull -#define L2_TYPE_BIT_OFFSET 61ull -#define L2_TYPE_OFFSET(off) ((L2_TYPE_QW << QW_SHIFT) | (off)) -#define L2_TYPE_MATCH_OFFSET L2_TYPE_OFFSET(L2_TYPE_BIT_OFFSET) -#define L2_TYPE_MASK 3ull -#define L2_16B_VALUE 2ull - /* L4_TYPE QW 1, OFFSET 0 - for match */ #define L4_TYPE_QW 1ull #define L4_TYPE_BIT_OFFSET 0ull @@ -6844,9 +6835,9 @@ static void rxe_kernel_unfreeze(struct hfi1_devdata *dd) for (i = 0; i < dd->num_rcv_contexts; i++) { rcd = hfi1_rcd_get_by_index(dd, i); - /* Ensure all non-user contexts(including vnic) are enabled */ + /* Ensure all non-user contexts are enabled */ if (!rcd || - (i >= dd->first_dyn_alloc_ctxt && !rcd->is_vnic)) { + (i >= dd->first_dyn_alloc_ctxt)) { hfi1_rcd_put(rcd); continue; } @@ -8467,7 +8458,7 @@ int hfi1_netdev_rx_napi(struct napi_struct *napi, int budget) return work_done; } -/* Receive packet napi handler for netdevs VNIC and AIP */ +/* Receive packet napi handler for netdevs AIP */ irqreturn_t receive_context_interrupt_napi(int irq, void *data) { struct hfi1_ctxtdata *rcd = data; @@ -14506,7 +14497,7 @@ static bool hfi1_netdev_update_rmt(struct hfi1_devdata *dd) int ctxt_count = hfi1_netdev_ctxt_count(dd); /* We already have contexts mapped in RMT */ - if (has_rsm_rule(dd, RSM_INS_VNIC) || has_rsm_rule(dd, RSM_INS_AIP)) { + if (has_rsm_rule(dd, RSM_INS_AIP)) { dd_dev_info(dd, "Contexts are already mapped in RMT\n"); return true; } @@ -14587,37 +14578,6 @@ void hfi1_init_aip_rsm(struct hfi1_devdata *dd) } } -/* Initialize RSM for VNIC */ -void hfi1_init_vnic_rsm(struct hfi1_devdata *dd) -{ - int rmt_start = hfi1_netdev_get_free_rmt_idx(dd); - struct rsm_rule_data rrd = { - /* Add rule for vnic */ - .offset = rmt_start, - .pkt_type = 4, - /* Match 16B packets */ - .field1_off = L2_TYPE_MATCH_OFFSET, - .mask1 = L2_TYPE_MASK, - .value1 = L2_16B_VALUE, - /* Match ETH L4 packets */ - .field2_off = L4_TYPE_MATCH_OFFSET, - .mask2 = L4_16B_TYPE_MASK, - .value2 = L4_16B_ETH_VALUE, - /* Calc context from veswid and entropy */ - .index1_off = L4_16B_HDR_VESWID_OFFSET, - .index1_width = ilog2(NUM_NETDEV_MAP_ENTRIES), - .index2_off = L2_16B_ENTROPY_OFFSET, - .index2_width = ilog2(NUM_NETDEV_MAP_ENTRIES) - }; - - hfi1_enable_rsm_rule(dd, RSM_INS_VNIC, &rrd); -} - -void hfi1_deinit_vnic_rsm(struct hfi1_devdata *dd) -{ - clear_rsm_rule(dd, RSM_INS_VNIC); -} - void hfi1_deinit_aip_rsm(struct hfi1_devdata *dd) { /* only actually clear the rule if it's the last user asking to do so */ @@ -15195,7 +15155,7 @@ int hfi1_init_dd(struct hfi1_devdata *dd) (dd->revision >> CCE_REVISION_SW_SHIFT) & CCE_REVISION_SW_MASK); - /* alloc VNIC/AIP rx data */ + /* alloc AIP rx data */ ret = hfi1_alloc_rx(dd); if (ret) goto bail_cleanup; diff --git a/drivers/infiniband/hw/hfi1/chip.h b/drivers/infiniband/hw/hfi1/chip.h index 6992f6d40255..56e03d486ace 100644 --- a/drivers/infiniband/hw/hfi1/chip.h +++ b/drivers/infiniband/hw/hfi1/chip.h @@ -1392,8 +1392,6 @@ int hfi1_set_ctxt_pkey(struct hfi1_devdata *dd, struct hfi1_ctxtdata *ctxt, u16 pkey); int hfi1_clear_ctxt_pkey(struct hfi1_devdata *dd, struct hfi1_ctxtdata *ctxt); void hfi1_read_link_quality(struct hfi1_devdata *dd, u8 *link_quality); -void hfi1_init_vnic_rsm(struct hfi1_devdata *dd); -void hfi1_deinit_vnic_rsm(struct hfi1_devdata *dd); irqreturn_t general_interrupt(int irq, void *data); irqreturn_t sdma_interrupt(int irq, void *data); diff --git a/drivers/infiniband/hw/hfi1/driver.c b/drivers/infiniband/hw/hfi1/driver.c index 06487e20f723..c7259cc39013 100644 --- a/drivers/infiniband/hw/hfi1/driver.c +++ b/drivers/infiniband/hw/hfi1/driver.c @@ -20,7 +20,6 @@ #include "qp.h" #include "sdma.h" #include "debugfs.h" -#include "vnic.h" #include "fault.h" #include "ipoib.h" @@ -909,11 +908,11 @@ static void set_all_fastpath(struct hfi1_devdata *dd, struct hfi1_ctxtdata *rcd) u16 i; /* - * For dynamically allocated kernel contexts (like vnic) switch + * For dynamically allocated kernel contexts switch * interrupt handler only for that context. Otherwise, switch * interrupt handler for all statically allocated kernel contexts. */ - if (rcd->ctxt >= dd->first_dyn_alloc_ctxt && !rcd->is_vnic) { + if (rcd->ctxt >= dd->first_dyn_alloc_ctxt) { hfi1_rcd_get(rcd); hfi1_set_fast(rcd); hfi1_rcd_put(rcd); @@ -922,7 +921,7 @@ static void set_all_fastpath(struct hfi1_devdata *dd, struct hfi1_ctxtdata *rcd) for (i = HFI1_CTRL_CTXT + 1; i < dd->num_rcv_contexts; i++) { rcd = hfi1_rcd_get_by_index(dd, i); - if (rcd && (i < dd->first_dyn_alloc_ctxt || rcd->is_vnic)) + if (rcd && (i < dd->first_dyn_alloc_ctxt)) hfi1_set_fast(rcd); hfi1_rcd_put(rcd); } @@ -938,7 +937,7 @@ void set_all_slowpath(struct hfi1_devdata *dd) rcd = hfi1_rcd_get_by_index(dd, i); if (!rcd) continue; - if (i < dd->first_dyn_alloc_ctxt || rcd->is_vnic) + if (i < dd->first_dyn_alloc_ctxt) rcd->do_interrupt = rcd->slow_handler; hfi1_rcd_put(rcd); @@ -1400,7 +1399,7 @@ int hfi1_reset_device(int unit) goto bail; } - /* If there are any user/vnic contexts, we cannot reset */ + /* If there are any user contexts, we cannot reset */ mutex_lock(&hfi1_mutex); if (dd->rcd) if (hfi1_stats.sps_ctxts) { @@ -1899,7 +1898,7 @@ const rhf_rcv_function_ptr netdev_rhf_rcv_functions[] = { [RHF_RCV_TYPE_EAGER] = process_receive_invalid, [RHF_RCV_TYPE_IB] = hfi1_ipoib_ib_rcv, [RHF_RCV_TYPE_ERROR] = process_receive_error, - [RHF_RCV_TYPE_BYPASS] = hfi1_vnic_bypass_rcv, + [RHF_RCV_TYPE_BYPASS] = process_receive_invalid, [RHF_RCV_TYPE_INVALID5] = process_receive_invalid, [RHF_RCV_TYPE_INVALID6] = process_receive_invalid, [RHF_RCV_TYPE_INVALID7] = process_receive_invalid, diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index cb630551cf1a..5a0310f758dc 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -212,10 +212,6 @@ struct hfi1_ctxtdata { u8 rhf_offset; /* dynamic receive available interrupt timeout */ u8 rcvavail_timeout; - /* Indicates that this is vnic context */ - bool is_vnic; - /* vnic queue index this context is mapped to */ - u8 vnic_q_idx; /* Is ASPM interrupt supported for this context */ bool aspm_intr_supported; /* ASPM state (enabled/disabled) for this context */ @@ -402,7 +398,6 @@ struct hfi1_packet { #define OPA_16B_L4_FM 0x08 #define OPA_16B_L4_IB_LOCAL 0x09 #define OPA_16B_L4_IB_GLOBAL 0x0A -#define OPA_16B_L4_ETHR OPA_VNIC_L4_ETHR /* * OPA 16B Management @@ -997,14 +992,6 @@ struct hfi1_asic_data { #define NUM_MAP_ENTRIES 256 #define NUM_MAP_REGS 32 -/* Virtual NIC information */ -struct hfi1_vnic_data { - struct kmem_cache *txreq_cache; - u8 num_vports; -}; - -struct hfi1_vnic_vport_info; - /* device data struct now contains only "general per-device" info. * fields related to a physical IB port are in a hfi1_pportdata struct. */ @@ -1298,9 +1285,6 @@ struct hfi1_devdata { send_routine process_dma_send; void (*pio_inline_send)(struct hfi1_devdata *dd, struct pio_buf *pbuf, u64 pbc, const void *from, size_t count); - int (*process_vnic_dma_send)(struct hfi1_devdata *dd, u8 q_idx, - struct hfi1_vnic_vport_info *vinfo, - struct sk_buff *skb, u64 pbc, u8 plen); /* hfi1_pportdata, points to array of (physical) port-specific * data structs, indexed by pidx (0..n-1) */ @@ -1314,7 +1298,6 @@ struct hfi1_devdata { u16 flags; /* Number of physical ports available */ u8 num_pports; - /* Lowest context number which can be used by user processes or VNIC */ u8 first_dyn_alloc_ctxt; /* adding a new field here would make it part of this cacheline */ @@ -1353,11 +1336,8 @@ struct hfi1_devdata { bool aspm_enabled; /* ASPM state: enabled/disabled */ struct rhashtable *sdma_rht; - /* vnic data */ - struct hfi1_vnic_data vnic; /* Lock to protect IRQ SRC register access */ spinlock_t irq_src_lock; - int vnic_num_vports; struct hfi1_netdev_rx *netdev_rx; struct hfi1_affinity_node *affinity_entry; diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index 07333dd37652..8b5a5b32b0fa 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -26,7 +26,6 @@ #include "verbs.h" #include "aspm.h" #include "affinity.h" -#include "vnic.h" #include "exp_rcv.h" #include "netdev.h" @@ -349,7 +348,7 @@ int hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, int numa, * We do this here because we have to take into account all * the RcvArray entries that previous context would have * taken and we have to account for any extra groups assigned - * to the static (kernel) or dynamic (vnic/user) contexts. + * to the static (kernel) or dynamic (user) contexts. */ if (ctxt < dd->first_dyn_alloc_ctxt) { if (ctxt < kctxt_ngroups) { @@ -851,7 +850,6 @@ int hfi1_init(struct hfi1_devdata *dd, int reinit) dd->process_pio_send = hfi1_verbs_send_pio; dd->process_dma_send = hfi1_verbs_send_dma; dd->pio_inline_send = pio_copy; - dd->process_vnic_dma_send = hfi1_vnic_send_dma; if (is_ax(dd)) { atomic_set(&dd->drop_packet, DROP_PACKET_ON); diff --git a/drivers/infiniband/hw/hfi1/mad.c b/drivers/infiniband/hw/hfi1/mad.c index 03467e6c19a0..585f1d99b91b 100644 --- a/drivers/infiniband/hw/hfi1/mad.c +++ b/drivers/infiniband/hw/hfi1/mad.c @@ -12,7 +12,6 @@ #include "mad.h" #include "trace.h" #include "qp.h" -#include "vnic.h" /* the reset value from the FM is supposed to be 0xffff, handle both */ #define OPA_LINK_WIDTH_RESET_OLD 0x0fff diff --git a/drivers/infiniband/hw/hfi1/msix.c b/drivers/infiniband/hw/hfi1/msix.c index 3ac50ca4afcc..c06f4741c89e 100644 --- a/drivers/infiniband/hw/hfi1/msix.c +++ b/drivers/infiniband/hw/hfi1/msix.c @@ -24,7 +24,6 @@ int msix_initialize(struct hfi1_devdata *dd) * one for the general, "slow path" interrupt * one per used SDMA engine * one per kernel receive context - * one for each VNIC context * ...any new IRQs should be added here. */ total = 1 + dd->num_sdma + dd->n_krcv_queues + dd->num_netdev_contexts; @@ -127,8 +126,7 @@ static int msix_request_rcd_irq_common(struct hfi1_ctxtdata *rcd, irq_handler_t thread, const char *name) { - int nr = msix_request_irq(rcd->dd, rcd, handler, thread, - rcd->is_vnic ? IRQ_NETDEVCTXT : IRQ_RCVCTXT, + int nr = msix_request_irq(rcd->dd, rcd, handler, thread, IRQ_RCVCTXT, name); if (nr < 0) return nr; diff --git a/drivers/infiniband/hw/hfi1/netdev.h b/drivers/infiniband/hw/hfi1/netdev.h index 07c8f77c9181..c6440bd07d2e 100644 --- a/drivers/infiniband/hw/hfi1/netdev.h +++ b/drivers/infiniband/hw/hfi1/netdev.h @@ -14,7 +14,7 @@ /** * struct hfi1_netdev_rxq - Receive Queue for HFI - * Both IPoIB and VNIC netdevices will be working on the rx abstraction. + * IPoIB netdevices will be working on the rx abstraction. * @napi: napi object * @rx: ptr to netdev_rx * @rcd: ptr to receive context data @@ -25,10 +25,6 @@ struct hfi1_netdev_rxq { struct hfi1_ctxtdata *rcd; }; -/* - * Number of netdev contexts used. Ensure it is less than or equal to - * max queues supported by VNIC (HFI1_VNIC_MAX_QUEUE). - */ #define HFI1_MAX_NETDEV_CTXTS 8 /* Number of NETDEV RSM entries */ @@ -42,7 +38,7 @@ struct hfi1_netdev_rxq { * @num_rx_q: number of receive queues * @rmt_index: first free index in RMT Array * @msix_start: first free MSI-X interrupt vector. - * @dev_tbl: netdev table for unique identifier VNIC and IPoIb VLANs. + * @dev_tbl: netdev table for unique identifier IPoIb VLANs. * @enabled: atomic counter of netdevs enabling receive queues. * When 0 NAPI will be disabled. * @netdevs: atomic counter of netdevs using dummy netdev. diff --git a/drivers/infiniband/hw/hfi1/netdev_rx.c b/drivers/infiniband/hw/hfi1/netdev_rx.c index 8608044203bb..ca2ae52b21e3 100644 --- a/drivers/infiniband/hw/hfi1/netdev_rx.c +++ b/drivers/infiniband/hw/hfi1/netdev_rx.c @@ -78,7 +78,6 @@ static int hfi1_netdev_allocate_ctxt(struct hfi1_devdata *dd, uctxt->fast_handler = handle_receive_interrupt_napi_fp; uctxt->slow_handler = handle_receive_interrupt_napi_sp; hfi1_set_seq_cnt(uctxt, 1); - uctxt->is_vnic = true; hfi1_stats.sps_ctxts++; @@ -427,7 +426,7 @@ void hfi1_netdev_disable_queues(struct hfi1_devdata *dd) /** * hfi1_netdev_add_data - Registers data with unique identifier - * to be requested later this is needed for VNIC and IPoIB VLANs + * to be requested later this is needed for IPoIB VLANs * implementations. * This call is protected by mutex idr_lock. * diff --git a/drivers/infiniband/hw/hfi1/pio.c b/drivers/infiniband/hw/hfi1/pio.c index 51afaac88c72..9121d83bf88a 100644 --- a/drivers/infiniband/hw/hfi1/pio.c +++ b/drivers/infiniband/hw/hfi1/pio.c @@ -1942,13 +1942,16 @@ bail: void free_pio_map(struct hfi1_devdata *dd) { + struct pio_vl_map *map; + /* Free PIO map if allocated */ if (rcu_access_pointer(dd->pio_map)) { spin_lock_irq(&dd->pio_map_lock); - pio_map_free(rcu_access_pointer(dd->pio_map)); + map = rcu_access_pointer(dd->pio_map); RCU_INIT_POINTER(dd->pio_map, NULL); spin_unlock_irq(&dd->pio_map_lock); synchronize_rcu(); + pio_map_free(map); } kfree(dd->kernel_send_context); dd->kernel_send_context = NULL; diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c index f3d8c0c193ac..3a0caee95607 100644 --- a/drivers/infiniband/hw/hfi1/qp.c +++ b/drivers/infiniband/hw/hfi1/qp.c @@ -404,7 +404,6 @@ void hfi1_qp_wakeup(struct rvt_qp *qp, u32 flag) hfi1_qp_schedule(qp); } spin_unlock_irqrestore(&qp->s_lock, flags); - /* Notify hfi1_destroy_qp() if it is waiting. */ rvt_put_qp(qp); } diff --git a/drivers/infiniband/hw/hfi1/sdma.c b/drivers/infiniband/hw/hfi1/sdma.c index e5f442938177..cfd9dd0f7e81 100644 --- a/drivers/infiniband/hw/hfi1/sdma.c +++ b/drivers/infiniband/hw/hfi1/sdma.c @@ -1255,6 +1255,7 @@ void sdma_clean(struct hfi1_devdata *dd, size_t num_engines) { size_t i; struct sdma_engine *sde; + struct sdma_vl_map *map; if (dd->sdma_pad_dma) { dma_free_coherent(&dd->pcidev->dev, SDMA_PAD, @@ -1291,10 +1292,11 @@ void sdma_clean(struct hfi1_devdata *dd, size_t num_engines) } if (rcu_access_pointer(dd->sdma_map)) { spin_lock_irq(&dd->sde_map_lock); - sdma_map_free(rcu_access_pointer(dd->sdma_map)); + map = rcu_access_pointer(dd->sdma_map); RCU_INIT_POINTER(dd->sdma_map, NULL); spin_unlock_irq(&dd->sde_map_lock); synchronize_rcu(); + sdma_map_free(map); } kfree(dd->per_sdma); dd->per_sdma = NULL; diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.c b/drivers/infiniband/hw/hfi1/user_exp_rcv.c index 5b01070ed66f..8b50a2ad792c 100644 --- a/drivers/infiniband/hw/hfi1/user_exp_rcv.c +++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.c @@ -257,7 +257,7 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd, if (tinfo->length == 0) return -EINVAL; - tidbuf = kzalloc(sizeof(*tidbuf), GFP_KERNEL); + tidbuf = kzalloc_flex(*tidbuf, psets, uctxt->expected_count); if (!tidbuf) return -ENOMEM; @@ -265,12 +265,6 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd, tidbuf->vaddr = tinfo->vaddr; tidbuf->length = tinfo->length; tidbuf->npages = num_user_pages(tidbuf->vaddr, tidbuf->length); - tidbuf->psets = kcalloc(uctxt->expected_count, sizeof(*tidbuf->psets), - GFP_KERNEL); - if (!tidbuf->psets) { - ret = -ENOMEM; - goto fail_release_mem; - } if (fd->use_mn) { ret = mmu_interval_notifier_insert( @@ -306,7 +300,7 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd, } ngroups = pageset_count / dd->rcv_entries.group_size; - tidlist = kcalloc(pageset_count, sizeof(*tidlist), GFP_KERNEL); + tidlist = kzalloc_objs(*tidlist, pageset_count); if (!tidlist) { ret = -ENOMEM; goto fail_unreserve; @@ -448,7 +442,6 @@ unlock: if (fd->use_mn) mmu_interval_notifier_remove(&tidbuf->notifier); kfree(tidbuf->pages); - kfree(tidbuf->psets); kfree(tidbuf); kfree(tidlist); return 0; @@ -471,7 +464,6 @@ fail_unpin: unpin_rcv_pages(fd, tidbuf, NULL, 0, pinned, false); fail_release_mem: kfree(tidbuf->pages); - kfree(tidbuf->psets); kfree(tidbuf); kfree(tidlist); return ret; @@ -527,7 +519,7 @@ int hfi1_user_exp_rcv_invalid(struct hfi1_filedata *fd, * for a long time. * Copy the data to a local buffer so we can release the lock. */ - array = kcalloc(uctxt->expected_count, sizeof(*array), GFP_KERNEL); + array = kzalloc_objs(*array, uctxt->expected_count); if (!array) return -EFAULT; diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.h b/drivers/infiniband/hw/hfi1/user_exp_rcv.h index 055726f7c139..b4a309a051f9 100644 --- a/drivers/infiniband/hw/hfi1/user_exp_rcv.h +++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.h @@ -22,8 +22,8 @@ struct tid_user_buf { unsigned long length; unsigned int npages; struct page **pages; - struct tid_pageset *psets; unsigned int n_psets; + struct tid_pageset psets[]; }; struct tid_rb_node { diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index 3cbbfccdd8cd..e569b647d611 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -21,7 +21,6 @@ #include "qp.h" #include "verbs_txreq.h" #include "debugfs.h" -#include "vnic.h" #include "fault.h" #include "affinity.h" #include "ipoib.h" @@ -1729,7 +1728,6 @@ static const struct ib_device_ops hfi1_dev_ops = { .alloc_hw_device_stats = hfi1_alloc_hw_device_stats, .alloc_hw_port_stats = hfi_alloc_hw_port_stats, - .alloc_rdma_netdev = hfi1_vnic_alloc_rn, .device_group = &ib_hfi1_attr_group, .get_dev_fw_str = hfi1_get_dev_fw_str, .get_hw_stats = get_hw_stats, diff --git a/drivers/infiniband/hw/hfi1/vnic.h b/drivers/infiniband/hw/hfi1/vnic.h deleted file mode 100644 index bbafeb5fc0ec..000000000000 --- a/drivers/infiniband/hw/hfi1/vnic.h +++ /dev/null @@ -1,126 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ -/* - * Copyright(c) 2017 - 2020 Intel Corporation. - */ - -#ifndef _HFI1_VNIC_H -#define _HFI1_VNIC_H -#include -#include "hfi.h" -#include "sdma.h" - -#define HFI1_VNIC_MAX_TXQ 16 -#define HFI1_VNIC_MAX_PAD 12 - -/* L4 header definitions */ -#define HFI1_VNIC_L4_HDR_OFFSET OPA_VNIC_L2_HDR_LEN - -#define HFI1_VNIC_GET_L4_HDR(data) \ - (*((u16 *)((u8 *)(data) + HFI1_VNIC_L4_HDR_OFFSET))) - -#define HFI1_VNIC_GET_VESWID(data) \ - (HFI1_VNIC_GET_L4_HDR(data) & 0xFFF) - -/* Service class */ -#define HFI1_VNIC_SC_OFFSET_LOW 6 -#define HFI1_VNIC_SC_OFFSET_HI 7 -#define HFI1_VNIC_SC_SHIFT 4 - -#define HFI1_VNIC_MAX_QUEUE 16 -#define HFI1_NUM_VNIC_CTXT 8 - -/** - * struct hfi1_vnic_sdma - VNIC per Tx ring SDMA information - * @dd - device data pointer - * @sde - sdma engine - * @vinfo - vnic info pointer - * @wait - iowait structure - * @stx - sdma tx request - * @state - vnic Tx ring SDMA state - * @q_idx - vnic Tx queue index - */ -struct hfi1_vnic_sdma { - struct hfi1_devdata *dd; - struct sdma_engine *sde; - struct hfi1_vnic_vport_info *vinfo; - struct iowait wait; - struct sdma_txreq stx; - unsigned int state; - u8 q_idx; - bool pkts_sent; -}; - -/** - * struct hfi1_vnic_rx_queue - HFI1 VNIC receive queue - * @idx: queue index - * @vinfo: pointer to vport information - * @netdev: network device - * @napi: netdev napi structure - * @skbq: queue of received socket buffers - */ -struct hfi1_vnic_rx_queue { - u8 idx; - struct hfi1_vnic_vport_info *vinfo; - struct net_device *netdev; - struct napi_struct napi; -}; - -/** - * struct hfi1_vnic_vport_info - HFI1 VNIC virtual port information - * @dd: device data pointer - * @netdev: net device pointer - * @flags: state flags - * @lock: vport lock - * @num_tx_q: number of transmit queues - * @num_rx_q: number of receive queues - * @vesw_id: virtual switch id - * @rxq: Array of receive queues - * @stats: per queue stats - * @sdma: VNIC SDMA structure per TXQ - */ -struct hfi1_vnic_vport_info { - struct hfi1_devdata *dd; - struct net_device *netdev; - unsigned long flags; - - /* Lock used around state updates */ - struct mutex lock; - - u8 num_tx_q; - u8 num_rx_q; - u16 vesw_id; - struct hfi1_vnic_rx_queue rxq[HFI1_NUM_VNIC_CTXT]; - - struct opa_vnic_stats stats[HFI1_VNIC_MAX_QUEUE]; - struct hfi1_vnic_sdma sdma[HFI1_VNIC_MAX_TXQ]; -}; - -#define v_dbg(format, arg...) \ - netdev_dbg(vinfo->netdev, format, ## arg) -#define v_err(format, arg...) \ - netdev_err(vinfo->netdev, format, ## arg) -#define v_info(format, arg...) \ - netdev_info(vinfo->netdev, format, ## arg) - -/* vnic hfi1 internal functions */ -void hfi1_vnic_setup(struct hfi1_devdata *dd); -int hfi1_vnic_txreq_init(struct hfi1_devdata *dd); -void hfi1_vnic_txreq_deinit(struct hfi1_devdata *dd); - -void hfi1_vnic_bypass_rcv(struct hfi1_packet *packet); -void hfi1_vnic_sdma_init(struct hfi1_vnic_vport_info *vinfo); -bool hfi1_vnic_sdma_write_avail(struct hfi1_vnic_vport_info *vinfo, - u8 q_idx); - -/* vnic rdma netdev operations */ -struct net_device *hfi1_vnic_alloc_rn(struct ib_device *device, - u32 port_num, - enum rdma_netdev_t type, - const char *name, - unsigned char name_assign_type, - void (*setup)(struct net_device *)); -int hfi1_vnic_send_dma(struct hfi1_devdata *dd, u8 q_idx, - struct hfi1_vnic_vport_info *vinfo, - struct sk_buff *skb, u64 pbc, u8 plen); - -#endif /* _HFI1_VNIC_H */ diff --git a/drivers/infiniband/hw/hfi1/vnic_main.c b/drivers/infiniband/hw/hfi1/vnic_main.c deleted file mode 100644 index 16a4c297a897..000000000000 --- a/drivers/infiniband/hw/hfi1/vnic_main.c +++ /dev/null @@ -1,615 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -/* - * Copyright(c) 2017 - 2020 Intel Corporation. - */ - -/* - * This file contains HFI1 support for VNIC functionality - */ - -#include -#include - -#include "vnic.h" -#include "netdev.h" - -#define HFI_TX_TIMEOUT_MS 1000 - -#define HFI1_VNIC_RCV_Q_SIZE 1024 - -#define HFI1_VNIC_UP 0 - -static DEFINE_SPINLOCK(vport_cntr_lock); - -#define SUM_GRP_COUNTERS(stats, qstats, x_grp) do { \ - u64 *src64, *dst64; \ - for (src64 = &qstats->x_grp.unicast, \ - dst64 = &stats->x_grp.unicast; \ - dst64 <= &stats->x_grp.s_1519_max;) { \ - *dst64++ += *src64++; \ - } \ - } while (0) - -#define VNIC_MASK (0xFF) -#define VNIC_ID(val) ((1ull << 24) | ((val) & VNIC_MASK)) - -/* hfi1_vnic_update_stats - update statistics */ -static void hfi1_vnic_update_stats(struct hfi1_vnic_vport_info *vinfo, - struct opa_vnic_stats *stats) -{ - struct net_device *netdev = vinfo->netdev; - u8 i; - - /* add tx counters on different queues */ - for (i = 0; i < vinfo->num_tx_q; i++) { - struct opa_vnic_stats *qstats = &vinfo->stats[i]; - struct rtnl_link_stats64 *qnstats = &vinfo->stats[i].netstats; - - stats->netstats.tx_fifo_errors += qnstats->tx_fifo_errors; - stats->netstats.tx_carrier_errors += qnstats->tx_carrier_errors; - stats->tx_drop_state += qstats->tx_drop_state; - stats->tx_dlid_zero += qstats->tx_dlid_zero; - - SUM_GRP_COUNTERS(stats, qstats, tx_grp); - stats->netstats.tx_packets += qnstats->tx_packets; - stats->netstats.tx_bytes += qnstats->tx_bytes; - } - - /* add rx counters on different queues */ - for (i = 0; i < vinfo->num_rx_q; i++) { - struct opa_vnic_stats *qstats = &vinfo->stats[i]; - struct rtnl_link_stats64 *qnstats = &vinfo->stats[i].netstats; - - stats->netstats.rx_fifo_errors += qnstats->rx_fifo_errors; - stats->netstats.rx_nohandler += qnstats->rx_nohandler; - stats->rx_drop_state += qstats->rx_drop_state; - stats->rx_oversize += qstats->rx_oversize; - stats->rx_runt += qstats->rx_runt; - - SUM_GRP_COUNTERS(stats, qstats, rx_grp); - stats->netstats.rx_packets += qnstats->rx_packets; - stats->netstats.rx_bytes += qnstats->rx_bytes; - } - - stats->netstats.tx_errors = stats->netstats.tx_fifo_errors + - stats->netstats.tx_carrier_errors + - stats->tx_drop_state + stats->tx_dlid_zero; - stats->netstats.tx_dropped = stats->netstats.tx_errors; - - stats->netstats.rx_errors = stats->netstats.rx_fifo_errors + - stats->netstats.rx_nohandler + - stats->rx_drop_state + stats->rx_oversize + - stats->rx_runt; - stats->netstats.rx_dropped = stats->netstats.rx_errors; - - netdev->stats.tx_packets = stats->netstats.tx_packets; - netdev->stats.tx_bytes = stats->netstats.tx_bytes; - netdev->stats.tx_fifo_errors = stats->netstats.tx_fifo_errors; - netdev->stats.tx_carrier_errors = stats->netstats.tx_carrier_errors; - netdev->stats.tx_errors = stats->netstats.tx_errors; - netdev->stats.tx_dropped = stats->netstats.tx_dropped; - - netdev->stats.rx_packets = stats->netstats.rx_packets; - netdev->stats.rx_bytes = stats->netstats.rx_bytes; - netdev->stats.rx_fifo_errors = stats->netstats.rx_fifo_errors; - netdev->stats.multicast = stats->rx_grp.mcastbcast; - netdev->stats.rx_length_errors = stats->rx_oversize + stats->rx_runt; - netdev->stats.rx_errors = stats->netstats.rx_errors; - netdev->stats.rx_dropped = stats->netstats.rx_dropped; -} - -/* update_len_counters - update pkt's len histogram counters */ -static inline void update_len_counters(struct opa_vnic_grp_stats *grp, - int len) -{ - /* account for 4 byte FCS */ - if (len >= 1515) - grp->s_1519_max++; - else if (len >= 1020) - grp->s_1024_1518++; - else if (len >= 508) - grp->s_512_1023++; - else if (len >= 252) - grp->s_256_511++; - else if (len >= 124) - grp->s_128_255++; - else if (len >= 61) - grp->s_65_127++; - else - grp->s_64++; -} - -/* hfi1_vnic_update_tx_counters - update transmit counters */ -static void hfi1_vnic_update_tx_counters(struct hfi1_vnic_vport_info *vinfo, - u8 q_idx, struct sk_buff *skb, int err) -{ - struct ethhdr *mac_hdr = (struct ethhdr *)skb_mac_header(skb); - struct opa_vnic_stats *stats = &vinfo->stats[q_idx]; - struct opa_vnic_grp_stats *tx_grp = &stats->tx_grp; - u16 vlan_tci; - - stats->netstats.tx_packets++; - stats->netstats.tx_bytes += skb->len + ETH_FCS_LEN; - - update_len_counters(tx_grp, skb->len); - - /* rest of the counts are for good packets only */ - if (unlikely(err)) - return; - - if (is_multicast_ether_addr(mac_hdr->h_dest)) - tx_grp->mcastbcast++; - else - tx_grp->unicast++; - - if (!__vlan_get_tag(skb, &vlan_tci)) - tx_grp->vlan++; - else - tx_grp->untagged++; -} - -/* hfi1_vnic_update_rx_counters - update receive counters */ -static void hfi1_vnic_update_rx_counters(struct hfi1_vnic_vport_info *vinfo, - u8 q_idx, struct sk_buff *skb, int err) -{ - struct ethhdr *mac_hdr = (struct ethhdr *)skb->data; - struct opa_vnic_stats *stats = &vinfo->stats[q_idx]; - struct opa_vnic_grp_stats *rx_grp = &stats->rx_grp; - u16 vlan_tci; - - stats->netstats.rx_packets++; - stats->netstats.rx_bytes += skb->len + ETH_FCS_LEN; - - update_len_counters(rx_grp, skb->len); - - /* rest of the counts are for good packets only */ - if (unlikely(err)) - return; - - if (is_multicast_ether_addr(mac_hdr->h_dest)) - rx_grp->mcastbcast++; - else - rx_grp->unicast++; - - if (!__vlan_get_tag(skb, &vlan_tci)) - rx_grp->vlan++; - else - rx_grp->untagged++; -} - -/* This function is overloaded for opa_vnic specific implementation */ -static void hfi1_vnic_get_stats64(struct net_device *netdev, - struct rtnl_link_stats64 *stats) -{ - struct opa_vnic_stats *vstats = (struct opa_vnic_stats *)stats; - struct hfi1_vnic_vport_info *vinfo = opa_vnic_dev_priv(netdev); - - hfi1_vnic_update_stats(vinfo, vstats); -} - -static u64 create_bypass_pbc(u32 vl, u32 dw_len) -{ - u64 pbc; - - pbc = ((u64)PBC_IHCRC_NONE << PBC_INSERT_HCRC_SHIFT) - | PBC_INSERT_BYPASS_ICRC | PBC_CREDIT_RETURN - | PBC_PACKET_BYPASS - | ((vl & PBC_VL_MASK) << PBC_VL_SHIFT) - | (dw_len & PBC_LENGTH_DWS_MASK) << PBC_LENGTH_DWS_SHIFT; - - return pbc; -} - -/* hfi1_vnic_maybe_stop_tx - stop tx queue if required */ -static void hfi1_vnic_maybe_stop_tx(struct hfi1_vnic_vport_info *vinfo, - u8 q_idx) -{ - netif_stop_subqueue(vinfo->netdev, q_idx); - if (!hfi1_vnic_sdma_write_avail(vinfo, q_idx)) - return; - - netif_start_subqueue(vinfo->netdev, q_idx); -} - -static netdev_tx_t hfi1_netdev_start_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct hfi1_vnic_vport_info *vinfo = opa_vnic_dev_priv(netdev); - u8 pad_len, q_idx = skb->queue_mapping; - struct hfi1_devdata *dd = vinfo->dd; - struct opa_vnic_skb_mdata *mdata; - u32 pkt_len, total_len; - int err = -EINVAL; - u64 pbc; - - v_dbg("xmit: queue %d skb len %d\n", q_idx, skb->len); - if (unlikely(!netif_oper_up(netdev))) { - vinfo->stats[q_idx].tx_drop_state++; - goto tx_finish; - } - - /* take out meta data */ - mdata = (struct opa_vnic_skb_mdata *)skb->data; - skb_pull(skb, sizeof(*mdata)); - if (unlikely(mdata->flags & OPA_VNIC_SKB_MDATA_ENCAP_ERR)) { - vinfo->stats[q_idx].tx_dlid_zero++; - goto tx_finish; - } - - /* add tail padding (for 8 bytes size alignment) and icrc */ - pad_len = -(skb->len + OPA_VNIC_ICRC_TAIL_LEN) & 0x7; - pad_len += OPA_VNIC_ICRC_TAIL_LEN; - - /* - * pkt_len is how much data we have to write, includes header and data. - * total_len is length of the packet in Dwords plus the PBC should not - * include the CRC. - */ - pkt_len = (skb->len + pad_len) >> 2; - total_len = pkt_len + 2; /* PBC + packet */ - - pbc = create_bypass_pbc(mdata->vl, total_len); - - skb_get(skb); - v_dbg("pbc 0x%016llX len %d pad_len %d\n", pbc, skb->len, pad_len); - err = dd->process_vnic_dma_send(dd, q_idx, vinfo, skb, pbc, pad_len); - if (unlikely(err)) { - if (err == -ENOMEM) - vinfo->stats[q_idx].netstats.tx_fifo_errors++; - else if (err != -EBUSY) - vinfo->stats[q_idx].netstats.tx_carrier_errors++; - } - /* remove the header before updating tx counters */ - skb_pull(skb, OPA_VNIC_HDR_LEN); - - if (unlikely(err == -EBUSY)) { - hfi1_vnic_maybe_stop_tx(vinfo, q_idx); - dev_kfree_skb_any(skb); - return NETDEV_TX_BUSY; - } - -tx_finish: - /* update tx counters */ - hfi1_vnic_update_tx_counters(vinfo, q_idx, skb, err); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; -} - -static u16 hfi1_vnic_select_queue(struct net_device *netdev, - struct sk_buff *skb, - struct net_device *sb_dev) -{ - struct hfi1_vnic_vport_info *vinfo = opa_vnic_dev_priv(netdev); - struct opa_vnic_skb_mdata *mdata; - struct sdma_engine *sde; - - mdata = (struct opa_vnic_skb_mdata *)skb->data; - sde = sdma_select_engine_vl(vinfo->dd, mdata->entropy, mdata->vl); - return sde->this_idx; -} - -/* hfi1_vnic_decap_skb - strip OPA header from the skb (ethernet) packet */ -static inline int hfi1_vnic_decap_skb(struct hfi1_vnic_rx_queue *rxq, - struct sk_buff *skb) -{ - struct hfi1_vnic_vport_info *vinfo = rxq->vinfo; - int max_len = vinfo->netdev->mtu + VLAN_ETH_HLEN; - int rc = -EFAULT; - - skb_pull(skb, OPA_VNIC_HDR_LEN); - - /* Validate Packet length */ - if (unlikely(skb->len > max_len)) - vinfo->stats[rxq->idx].rx_oversize++; - else if (unlikely(skb->len < ETH_ZLEN)) - vinfo->stats[rxq->idx].rx_runt++; - else - rc = 0; - return rc; -} - -static struct hfi1_vnic_vport_info *get_vnic_port(struct hfi1_devdata *dd, - int vesw_id) -{ - int vnic_id = VNIC_ID(vesw_id); - - return hfi1_netdev_get_data(dd, vnic_id); -} - -static struct hfi1_vnic_vport_info *get_first_vnic_port(struct hfi1_devdata *dd) -{ - struct hfi1_vnic_vport_info *vinfo; - int next_id = VNIC_ID(0); - - vinfo = hfi1_netdev_get_first_data(dd, &next_id); - - if (next_id > VNIC_ID(VNIC_MASK)) - return NULL; - - return vinfo; -} - -void hfi1_vnic_bypass_rcv(struct hfi1_packet *packet) -{ - struct hfi1_devdata *dd = packet->rcd->dd; - struct hfi1_vnic_vport_info *vinfo = NULL; - struct hfi1_vnic_rx_queue *rxq; - struct sk_buff *skb; - int l4_type, vesw_id = -1, rc; - u8 q_idx; - unsigned char *pad_info; - - l4_type = hfi1_16B_get_l4(packet->ebuf); - if (likely(l4_type == OPA_16B_L4_ETHR)) { - vesw_id = HFI1_VNIC_GET_VESWID(packet->ebuf); - vinfo = get_vnic_port(dd, vesw_id); - - /* - * In case of invalid vesw id, count the error on - * the first available vport. - */ - if (unlikely(!vinfo)) { - struct hfi1_vnic_vport_info *vinfo_tmp; - - vinfo_tmp = get_first_vnic_port(dd); - if (vinfo_tmp) { - spin_lock(&vport_cntr_lock); - vinfo_tmp->stats[0].netstats.rx_nohandler++; - spin_unlock(&vport_cntr_lock); - } - } - } - - if (unlikely(!vinfo)) { - dd_dev_warn(dd, "vnic rcv err: l4 %d vesw id %d ctx %d\n", - l4_type, vesw_id, packet->rcd->ctxt); - return; - } - - q_idx = packet->rcd->vnic_q_idx; - rxq = &vinfo->rxq[q_idx]; - if (unlikely(!netif_oper_up(vinfo->netdev))) { - vinfo->stats[q_idx].rx_drop_state++; - return; - } - - skb = netdev_alloc_skb(vinfo->netdev, packet->tlen); - if (unlikely(!skb)) { - vinfo->stats[q_idx].netstats.rx_fifo_errors++; - return; - } - - memcpy(skb->data, packet->ebuf, packet->tlen); - skb_put(skb, packet->tlen); - - pad_info = skb->data + skb->len - 1; - skb_trim(skb, (skb->len - OPA_VNIC_ICRC_TAIL_LEN - - ((*pad_info) & 0x7))); - - rc = hfi1_vnic_decap_skb(rxq, skb); - - /* update rx counters */ - hfi1_vnic_update_rx_counters(vinfo, rxq->idx, skb, rc); - if (unlikely(rc)) { - dev_kfree_skb_any(skb); - return; - } - - skb_checksum_none_assert(skb); - skb->protocol = eth_type_trans(skb, rxq->netdev); - - napi_gro_receive(&rxq->napi, skb); -} - -static int hfi1_vnic_up(struct hfi1_vnic_vport_info *vinfo) -{ - struct hfi1_devdata *dd = vinfo->dd; - struct net_device *netdev = vinfo->netdev; - int rc; - - /* ensure virtual eth switch id is valid */ - if (!vinfo->vesw_id) - return -EINVAL; - - rc = hfi1_netdev_add_data(dd, VNIC_ID(vinfo->vesw_id), vinfo); - if (rc < 0) - return rc; - - rc = hfi1_netdev_rx_init(dd); - if (rc) - goto err_remove; - - netif_carrier_on(netdev); - netif_tx_start_all_queues(netdev); - set_bit(HFI1_VNIC_UP, &vinfo->flags); - - return 0; - -err_remove: - hfi1_netdev_remove_data(dd, VNIC_ID(vinfo->vesw_id)); - return rc; -} - -static void hfi1_vnic_down(struct hfi1_vnic_vport_info *vinfo) -{ - struct hfi1_devdata *dd = vinfo->dd; - - clear_bit(HFI1_VNIC_UP, &vinfo->flags); - netif_carrier_off(vinfo->netdev); - netif_tx_disable(vinfo->netdev); - hfi1_netdev_remove_data(dd, VNIC_ID(vinfo->vesw_id)); - - hfi1_netdev_rx_destroy(dd); -} - -static int hfi1_netdev_open(struct net_device *netdev) -{ - struct hfi1_vnic_vport_info *vinfo = opa_vnic_dev_priv(netdev); - int rc; - - mutex_lock(&vinfo->lock); - rc = hfi1_vnic_up(vinfo); - mutex_unlock(&vinfo->lock); - return rc; -} - -static int hfi1_netdev_close(struct net_device *netdev) -{ - struct hfi1_vnic_vport_info *vinfo = opa_vnic_dev_priv(netdev); - - mutex_lock(&vinfo->lock); - if (test_bit(HFI1_VNIC_UP, &vinfo->flags)) - hfi1_vnic_down(vinfo); - mutex_unlock(&vinfo->lock); - return 0; -} - -static int hfi1_vnic_init(struct hfi1_vnic_vport_info *vinfo) -{ - struct hfi1_devdata *dd = vinfo->dd; - int rc = 0; - - mutex_lock(&hfi1_mutex); - if (!dd->vnic_num_vports) { - rc = hfi1_vnic_txreq_init(dd); - if (rc) - goto txreq_fail; - } - - rc = hfi1_netdev_rx_init(dd); - if (rc) { - dd_dev_err(dd, "Unable to initialize netdev contexts\n"); - goto alloc_fail; - } - - hfi1_init_vnic_rsm(dd); - - dd->vnic_num_vports++; - hfi1_vnic_sdma_init(vinfo); - -alloc_fail: - if (!dd->vnic_num_vports) - hfi1_vnic_txreq_deinit(dd); -txreq_fail: - mutex_unlock(&hfi1_mutex); - return rc; -} - -static void hfi1_vnic_deinit(struct hfi1_vnic_vport_info *vinfo) -{ - struct hfi1_devdata *dd = vinfo->dd; - - mutex_lock(&hfi1_mutex); - if (--dd->vnic_num_vports == 0) { - hfi1_deinit_vnic_rsm(dd); - hfi1_vnic_txreq_deinit(dd); - } - mutex_unlock(&hfi1_mutex); - hfi1_netdev_rx_destroy(dd); -} - -static void hfi1_vnic_set_vesw_id(struct net_device *netdev, int id) -{ - struct hfi1_vnic_vport_info *vinfo = opa_vnic_dev_priv(netdev); - bool reopen = false; - - /* - * If vesw_id is being changed, and if the vnic port is up, - * reset the vnic port to ensure new vesw_id gets picked up - */ - if (id != vinfo->vesw_id) { - mutex_lock(&vinfo->lock); - if (test_bit(HFI1_VNIC_UP, &vinfo->flags)) { - hfi1_vnic_down(vinfo); - reopen = true; - } - - vinfo->vesw_id = id; - if (reopen) - hfi1_vnic_up(vinfo); - - mutex_unlock(&vinfo->lock); - } -} - -/* netdev ops */ -static const struct net_device_ops hfi1_netdev_ops = { - .ndo_open = hfi1_netdev_open, - .ndo_stop = hfi1_netdev_close, - .ndo_start_xmit = hfi1_netdev_start_xmit, - .ndo_select_queue = hfi1_vnic_select_queue, - .ndo_get_stats64 = hfi1_vnic_get_stats64, -}; - -static void hfi1_vnic_free_rn(struct net_device *netdev) -{ - struct hfi1_vnic_vport_info *vinfo = opa_vnic_dev_priv(netdev); - - hfi1_vnic_deinit(vinfo); - mutex_destroy(&vinfo->lock); - free_netdev(netdev); -} - -struct net_device *hfi1_vnic_alloc_rn(struct ib_device *device, - u32 port_num, - enum rdma_netdev_t type, - const char *name, - unsigned char name_assign_type, - void (*setup)(struct net_device *)) -{ - struct hfi1_devdata *dd = dd_from_ibdev(device); - struct hfi1_vnic_vport_info *vinfo; - struct net_device *netdev; - struct rdma_netdev *rn; - int i, size, rc; - - if (!dd->num_netdev_contexts) - return ERR_PTR(-ENOMEM); - - if (!port_num || (port_num > dd->num_pports)) - return ERR_PTR(-EINVAL); - - if (type != RDMA_NETDEV_OPA_VNIC) - return ERR_PTR(-EOPNOTSUPP); - - size = sizeof(struct opa_vnic_rdma_netdev) + sizeof(*vinfo); - netdev = alloc_netdev_mqs(size, name, name_assign_type, setup, - chip_sdma_engines(dd), - dd->num_netdev_contexts); - if (!netdev) - return ERR_PTR(-ENOMEM); - - rn = netdev_priv(netdev); - vinfo = opa_vnic_dev_priv(netdev); - vinfo->dd = dd; - vinfo->num_tx_q = chip_sdma_engines(dd); - vinfo->num_rx_q = dd->num_netdev_contexts; - vinfo->netdev = netdev; - rn->free_rdma_netdev = hfi1_vnic_free_rn; - rn->set_id = hfi1_vnic_set_vesw_id; - - netdev->features = NETIF_F_HIGHDMA | NETIF_F_SG; - netdev->hw_features = netdev->features; - netdev->vlan_features = netdev->features; - netdev->watchdog_timeo = msecs_to_jiffies(HFI_TX_TIMEOUT_MS); - netdev->netdev_ops = &hfi1_netdev_ops; - mutex_init(&vinfo->lock); - - for (i = 0; i < vinfo->num_rx_q; i++) { - struct hfi1_vnic_rx_queue *rxq = &vinfo->rxq[i]; - - rxq->idx = i; - rxq->vinfo = vinfo; - rxq->netdev = netdev; - } - - rc = hfi1_vnic_init(vinfo); - if (rc) - goto init_fail; - - return netdev; -init_fail: - mutex_destroy(&vinfo->lock); - free_netdev(netdev); - return ERR_PTR(rc); -} diff --git a/drivers/infiniband/hw/hfi1/vnic_sdma.c b/drivers/infiniband/hw/hfi1/vnic_sdma.c deleted file mode 100644 index 6caf01ba0bca..000000000000 --- a/drivers/infiniband/hw/hfi1/vnic_sdma.c +++ /dev/null @@ -1,282 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -/* - * Copyright(c) 2017 - 2018 Intel Corporation. - */ - -/* - * This file contains HFI1 support for VNIC SDMA functionality - */ - -#include "sdma.h" -#include "vnic.h" - -#define HFI1_VNIC_SDMA_Q_ACTIVE BIT(0) -#define HFI1_VNIC_SDMA_Q_DEFERRED BIT(1) - -#define HFI1_VNIC_TXREQ_NAME_LEN 32 -#define HFI1_VNIC_SDMA_DESC_WTRMRK 64 - -/* - * struct vnic_txreq - VNIC transmit descriptor - * @txreq: sdma transmit request - * @sdma: vnic sdma pointer - * @skb: skb to send - * @pad: pad buffer - * @plen: pad length - * @pbc_val: pbc value - */ -struct vnic_txreq { - struct sdma_txreq txreq; - struct hfi1_vnic_sdma *sdma; - - struct sk_buff *skb; - unsigned char pad[HFI1_VNIC_MAX_PAD]; - u16 plen; - __le64 pbc_val; -}; - -static void vnic_sdma_complete(struct sdma_txreq *txreq, - int status) -{ - struct vnic_txreq *tx = container_of(txreq, struct vnic_txreq, txreq); - struct hfi1_vnic_sdma *vnic_sdma = tx->sdma; - - sdma_txclean(vnic_sdma->dd, txreq); - dev_kfree_skb_any(tx->skb); - kmem_cache_free(vnic_sdma->dd->vnic.txreq_cache, tx); -} - -static noinline int build_vnic_ulp_payload(struct sdma_engine *sde, - struct vnic_txreq *tx) -{ - int i, ret = 0; - - ret = sdma_txadd_kvaddr( - sde->dd, - &tx->txreq, - tx->skb->data, - skb_headlen(tx->skb)); - if (unlikely(ret)) - goto bail_txadd; - - for (i = 0; i < skb_shinfo(tx->skb)->nr_frags; i++) { - skb_frag_t *frag = &skb_shinfo(tx->skb)->frags[i]; - - /* combine physically continuous fragments later? */ - ret = sdma_txadd_page(sde->dd, - &tx->txreq, - skb_frag_page(frag), - skb_frag_off(frag), - skb_frag_size(frag), - NULL, NULL, NULL); - if (unlikely(ret)) - goto bail_txadd; - } - - if (tx->plen) - ret = sdma_txadd_kvaddr(sde->dd, &tx->txreq, - tx->pad + HFI1_VNIC_MAX_PAD - tx->plen, - tx->plen); - -bail_txadd: - return ret; -} - -static int build_vnic_tx_desc(struct sdma_engine *sde, - struct vnic_txreq *tx, - u64 pbc) -{ - int ret = 0; - u16 hdrbytes = 2 << 2; /* PBC */ - - ret = sdma_txinit_ahg( - &tx->txreq, - 0, - hdrbytes + tx->skb->len + tx->plen, - 0, - 0, - NULL, - 0, - vnic_sdma_complete); - if (unlikely(ret)) - goto bail_txadd; - - /* add pbc */ - tx->pbc_val = cpu_to_le64(pbc); - ret = sdma_txadd_kvaddr( - sde->dd, - &tx->txreq, - &tx->pbc_val, - hdrbytes); - if (unlikely(ret)) - goto bail_txadd; - - /* add the ulp payload */ - ret = build_vnic_ulp_payload(sde, tx); -bail_txadd: - return ret; -} - -/* setup the last plen bypes of pad */ -static inline void hfi1_vnic_update_pad(unsigned char *pad, u8 plen) -{ - pad[HFI1_VNIC_MAX_PAD - 1] = plen - OPA_VNIC_ICRC_TAIL_LEN; -} - -int hfi1_vnic_send_dma(struct hfi1_devdata *dd, u8 q_idx, - struct hfi1_vnic_vport_info *vinfo, - struct sk_buff *skb, u64 pbc, u8 plen) -{ - struct hfi1_vnic_sdma *vnic_sdma = &vinfo->sdma[q_idx]; - struct sdma_engine *sde = vnic_sdma->sde; - struct vnic_txreq *tx; - int ret = -ECOMM; - - if (unlikely(READ_ONCE(vnic_sdma->state) != HFI1_VNIC_SDMA_Q_ACTIVE)) - goto tx_err; - - if (unlikely(!sde || !sdma_running(sde))) - goto tx_err; - - tx = kmem_cache_alloc(dd->vnic.txreq_cache, GFP_ATOMIC); - if (unlikely(!tx)) { - ret = -ENOMEM; - goto tx_err; - } - - tx->sdma = vnic_sdma; - tx->skb = skb; - hfi1_vnic_update_pad(tx->pad, plen); - tx->plen = plen; - ret = build_vnic_tx_desc(sde, tx, pbc); - if (unlikely(ret)) - goto free_desc; - - ret = sdma_send_txreq(sde, iowait_get_ib_work(&vnic_sdma->wait), - &tx->txreq, vnic_sdma->pkts_sent); - /* When -ECOMM, sdma callback will be called with ABORT status */ - if (unlikely(ret && unlikely(ret != -ECOMM))) - goto free_desc; - - if (!ret) { - vnic_sdma->pkts_sent = true; - iowait_starve_clear(vnic_sdma->pkts_sent, &vnic_sdma->wait); - } - return ret; - -free_desc: - sdma_txclean(dd, &tx->txreq); - kmem_cache_free(dd->vnic.txreq_cache, tx); -tx_err: - if (ret != -EBUSY) - dev_kfree_skb_any(skb); - else - vnic_sdma->pkts_sent = false; - return ret; -} - -/* - * hfi1_vnic_sdma_sleep - vnic sdma sleep function - * - * This function gets called from sdma_send_txreq() when there are not enough - * sdma descriptors available to send the packet. It adds Tx queue's wait - * structure to sdma engine's dmawait list to be woken up when descriptors - * become available. - */ -static int hfi1_vnic_sdma_sleep(struct sdma_engine *sde, - struct iowait_work *wait, - struct sdma_txreq *txreq, - uint seq, - bool pkts_sent) -{ - struct hfi1_vnic_sdma *vnic_sdma = - container_of(wait->iow, struct hfi1_vnic_sdma, wait); - - write_seqlock(&sde->waitlock); - if (sdma_progress(sde, seq, txreq)) { - write_sequnlock(&sde->waitlock); - return -EAGAIN; - } - - vnic_sdma->state = HFI1_VNIC_SDMA_Q_DEFERRED; - if (list_empty(&vnic_sdma->wait.list)) { - iowait_get_priority(wait->iow); - iowait_queue(pkts_sent, wait->iow, &sde->dmawait); - } - write_sequnlock(&sde->waitlock); - return -EBUSY; -} - -/* - * hfi1_vnic_sdma_wakeup - vnic sdma wakeup function - * - * This function gets called when SDMA descriptors becomes available and Tx - * queue's wait structure was previously added to sdma engine's dmawait list. - * It notifies the upper driver about Tx queue wakeup. - */ -static void hfi1_vnic_sdma_wakeup(struct iowait *wait, int reason) -{ - struct hfi1_vnic_sdma *vnic_sdma = - container_of(wait, struct hfi1_vnic_sdma, wait); - struct hfi1_vnic_vport_info *vinfo = vnic_sdma->vinfo; - - vnic_sdma->state = HFI1_VNIC_SDMA_Q_ACTIVE; - if (__netif_subqueue_stopped(vinfo->netdev, vnic_sdma->q_idx)) - netif_wake_subqueue(vinfo->netdev, vnic_sdma->q_idx); -}; - -inline bool hfi1_vnic_sdma_write_avail(struct hfi1_vnic_vport_info *vinfo, - u8 q_idx) -{ - struct hfi1_vnic_sdma *vnic_sdma = &vinfo->sdma[q_idx]; - - return (READ_ONCE(vnic_sdma->state) == HFI1_VNIC_SDMA_Q_ACTIVE); -} - -void hfi1_vnic_sdma_init(struct hfi1_vnic_vport_info *vinfo) -{ - int i; - - for (i = 0; i < vinfo->num_tx_q; i++) { - struct hfi1_vnic_sdma *vnic_sdma = &vinfo->sdma[i]; - - iowait_init(&vnic_sdma->wait, 0, NULL, NULL, - hfi1_vnic_sdma_sleep, - hfi1_vnic_sdma_wakeup, NULL, NULL); - vnic_sdma->sde = &vinfo->dd->per_sdma[i]; - vnic_sdma->dd = vinfo->dd; - vnic_sdma->vinfo = vinfo; - vnic_sdma->q_idx = i; - vnic_sdma->state = HFI1_VNIC_SDMA_Q_ACTIVE; - - /* Add a free descriptor watermark for wakeups */ - if (vnic_sdma->sde->descq_cnt > HFI1_VNIC_SDMA_DESC_WTRMRK) { - struct iowait_work *work; - - INIT_LIST_HEAD(&vnic_sdma->stx.list); - vnic_sdma->stx.num_desc = HFI1_VNIC_SDMA_DESC_WTRMRK; - work = iowait_get_ib_work(&vnic_sdma->wait); - list_add_tail(&vnic_sdma->stx.list, &work->tx_head); - } - } -} - -int hfi1_vnic_txreq_init(struct hfi1_devdata *dd) -{ - char buf[HFI1_VNIC_TXREQ_NAME_LEN]; - - snprintf(buf, sizeof(buf), "hfi1_%u_vnic_txreq_cache", dd->unit); - dd->vnic.txreq_cache = kmem_cache_create(buf, - sizeof(struct vnic_txreq), - 0, SLAB_HWCACHE_ALIGN, - NULL); - if (!dd->vnic.txreq_cache) - return -ENOMEM; - return 0; -} - -void hfi1_vnic_txreq_deinit(struct hfi1_devdata *dd) -{ - kmem_cache_destroy(dd->vnic.txreq_cache); - dd->vnic.txreq_cache = NULL; -} diff --git a/drivers/infiniband/hw/hns/hns_roce_alloc.c b/drivers/infiniband/hw/hns/hns_roce_alloc.c index 8e802f118bc9..142c86f462fa 100644 --- a/drivers/infiniband/hw/hns/hns_roce_alloc.c +++ b/drivers/infiniband/hw/hns/hns_roce_alloc.c @@ -32,7 +32,7 @@ */ #include -#include +#include #include "hns_roce_device.h" void hns_roce_buf_free(struct hns_roce_dev *hr_dev, struct hns_roce_buf *buf) diff --git a/drivers/infiniband/hw/hns/hns_roce_cq.c b/drivers/infiniband/hw/hns/hns_roce_cq.c index 857a913326cd..621568e11405 100644 --- a/drivers/infiniband/hw/hns/hns_roce_cq.c +++ b/drivers/infiniband/hw/hns/hns_roce_cq.c @@ -350,20 +350,6 @@ static int verify_cq_create_attr(struct hns_roce_dev *hr_dev, return 0; } -static int get_cq_ucmd(struct hns_roce_cq *hr_cq, struct ib_udata *udata, - struct hns_roce_ib_create_cq *ucmd) -{ - struct ib_device *ibdev = hr_cq->ib_cq.device; - int ret; - - ret = ib_copy_from_udata(ucmd, udata, min(udata->inlen, sizeof(*ucmd))); - if (ret) { - ibdev_err(ibdev, "failed to copy CQ udata, ret = %d.\n", ret); - return ret; - } - - return 0; -} static void set_cq_param(struct hns_roce_cq *hr_cq, u32 cq_entries, int vector, struct hns_roce_ib_create_cq *ucmd) @@ -428,7 +414,7 @@ int hns_roce_create_cq(struct ib_cq *ib_cq, const struct ib_cq_init_attr *attr, goto err_out; if (udata) { - ret = get_cq_ucmd(hr_cq, udata, &ucmd); + ret = ib_copy_validate_udata_in(udata, ucmd, db_addr); if (ret) goto err_out; } diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c index 4eaaedcc7652..e7c9e30ad2d8 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.c +++ b/drivers/infiniband/hw/hns/hns_roce_hem.c @@ -771,9 +771,7 @@ int hns_roce_init_hem_table(struct hns_roce_dev *hr_dev, unsigned long num_bt_l1; num_bt_l1 = DIV_ROUND_UP(num_hem, bt_chunk_num); - table->bt_l1 = kcalloc(num_bt_l1, - sizeof(*table->bt_l1), - GFP_KERNEL); + table->bt_l1 = kzalloc_objs(*table->bt_l1, num_bt_l1); if (!table->bt_l1) goto err_kcalloc_bt_l1; @@ -786,8 +784,7 @@ int hns_roce_init_hem_table(struct hns_roce_dev *hr_dev, if (check_whether_bt_num_2(type, hop_num) || check_whether_bt_num_3(type, hop_num)) { - table->bt_l0 = kcalloc(num_bt_l0, sizeof(*table->bt_l0), - GFP_KERNEL); + table->bt_l0 = kzalloc_objs(*table->bt_l0, num_bt_l0); if (!table->bt_l0) goto err_kcalloc_bt_l0; diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 1148d732f94f..0dbe99aab6ad 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "hns_roce_common.h" #include "hns_roce_device.h" #include "hns_roce_hem.h" @@ -424,7 +425,7 @@ static int hns_roce_alloc_ucontext(struct ib_ucontext *uctx, struct hns_roce_ucontext *context = to_hr_ucontext(uctx); struct hns_roce_dev *hr_dev = to_hr_dev(uctx->device); struct hns_roce_ib_alloc_ucontext_resp resp = {}; - struct hns_roce_ib_alloc_ucontext ucmd = {}; + struct hns_roce_ib_alloc_ucontext ucmd; int ret = -EAGAIN; if (!hr_dev->active) @@ -433,8 +434,7 @@ static int hns_roce_alloc_ucontext(struct ib_ucontext *uctx, resp.qp_tab_size = hr_dev->caps.num_qps; resp.srq_tab_size = hr_dev->caps.num_srqs; - ret = ib_copy_from_udata(&ucmd, udata, - min(udata->inlen, sizeof(ucmd))); + ret = ib_copy_validate_udata_in(udata, ucmd, reserved); if (ret) goto error_out; diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index 5f7ea6c16644..bf04ee84a943 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -47,8 +47,8 @@ static struct hns_roce_qp *hns_roce_qp_lookup(struct hns_roce_dev *hr_dev, xa_lock_irqsave(&hr_dev->qp_table_xa, flags); qp = __hns_roce_qp_lookup(hr_dev, qpn); - if (qp) - refcount_inc(&qp->refcount); + if (qp && !refcount_inc_not_zero(&qp->refcount)) + qp = NULL; xa_unlock_irqrestore(&hr_dev->qp_table_xa, flags); if (!qp) @@ -1023,21 +1023,16 @@ static void free_qp_db(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp, static int alloc_kernel_wrid(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp) { - struct ib_device *ibdev = &hr_dev->ib_dev; - u64 *sq_wrid = NULL; - u64 *rq_wrid = NULL; + u64 *sq_wrid, *rq_wrid = NULL; int ret; - sq_wrid = kcalloc(hr_qp->sq.wqe_cnt, sizeof(u64), GFP_KERNEL); - if (!sq_wrid) { - ibdev_err(ibdev, "failed to alloc SQ wrid.\n"); + sq_wrid = kzalloc_objs(*sq_wrid, hr_qp->sq.wqe_cnt); + if (!sq_wrid) return -ENOMEM; - } if (hr_qp->rq.wqe_cnt) { - rq_wrid = kcalloc(hr_qp->rq.wqe_cnt, sizeof(u64), GFP_KERNEL); + rq_wrid = kzalloc_objs(*rq_wrid, hr_qp->rq.wqe_cnt); if (!rq_wrid) { - ibdev_err(ibdev, "failed to alloc RQ wrid.\n"); ret = -ENOMEM; goto err_sq; } @@ -1135,13 +1130,11 @@ static int set_qp_param(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp, } if (udata) { - ret = ib_copy_from_udata(ucmd, udata, - min(udata->inlen, sizeof(*ucmd))); - if (ret) { - ibdev_err(ibdev, - "failed to copy QP ucmd, ret = %d\n", ret); + ret = ib_copy_validate_udata_in_cm( + udata, *ucmd, reserved, + HNS_ROCE_CREATE_QP_MASK_CONGEST_TYPE); + if (ret) return ret; - } uctx = rdma_udata_to_drv_context(udata, struct hns_roce_ucontext, ibucontext); @@ -1178,6 +1171,7 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, struct hns_roce_ib_create_qp_resp resp = {}; struct ib_device *ibdev = &hr_dev->ib_dev; struct hns_roce_ib_create_qp ucmd = {}; + unsigned long flags; int ret; mutex_init(&hr_qp->mutex); @@ -1258,13 +1252,19 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, hr_qp->ibqp.qp_num = hr_qp->qpn; hr_qp->event = hns_roce_ib_qp_event; - refcount_set(&hr_qp->refcount, 1); init_completion(&hr_qp->free); + refcount_set_release(&hr_qp->refcount, 1); return 0; err_flow_ctrl: + spin_lock_irqsave(&hr_dev->qp_list_lock, flags); + hns_roce_lock_cqs(init_attr->send_cq ? to_hr_cq(init_attr->send_cq) : NULL, + init_attr->recv_cq ? to_hr_cq(init_attr->recv_cq) : NULL); hns_roce_qp_remove(hr_dev, hr_qp); + hns_roce_unlock_cqs(init_attr->send_cq ? to_hr_cq(init_attr->send_cq) : NULL, + init_attr->recv_cq ? to_hr_cq(init_attr->recv_cq) : NULL); + spin_unlock_irqrestore(&hr_dev->qp_list_lock, flags); err_store: free_qpc(hr_dev, hr_qp); err_qpc: diff --git a/drivers/infiniband/hw/hns/hns_roce_srq.c b/drivers/infiniband/hw/hns/hns_roce_srq.c index 8a6efb6b9c9e..8b94cbdfa54d 100644 --- a/drivers/infiniband/hw/hns/hns_roce_srq.c +++ b/drivers/infiniband/hw/hns/hns_roce_srq.c @@ -16,8 +16,8 @@ void hns_roce_srq_event(struct hns_roce_dev *hr_dev, u32 srqn, int event_type) xa_lock(&srq_table->xa); srq = xa_load(&srq_table->xa, srqn & (hr_dev->caps.num_srqs - 1)); - if (srq) - refcount_inc(&srq->refcount); + if (srq && !refcount_inc_not_zero(&srq->refcount)) + srq = NULL; xa_unlock(&srq_table->xa); if (!srq) { @@ -340,27 +340,16 @@ static int set_srq_param(struct hns_roce_srq *srq, } static int alloc_srq_buf(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq, - struct ib_udata *udata) + struct ib_udata *udata, + struct hns_roce_ib_create_srq *ucmd) { - struct hns_roce_ib_create_srq ucmd = {}; int ret; - if (udata) { - ret = ib_copy_from_udata(&ucmd, udata, - min(udata->inlen, sizeof(ucmd))); - if (ret) { - ibdev_err(&hr_dev->ib_dev, - "failed to copy SRQ udata, ret = %d.\n", - ret); - return ret; - } - } - - ret = alloc_srq_idx(hr_dev, srq, udata, ucmd.que_addr); + ret = alloc_srq_idx(hr_dev, srq, udata, ucmd->que_addr); if (ret) return ret; - ret = alloc_srq_wqe_buf(hr_dev, srq, udata, ucmd.buf_addr); + ret = alloc_srq_wqe_buf(hr_dev, srq, udata, ucmd->buf_addr); if (ret) goto err_idx; @@ -387,20 +376,6 @@ static void free_srq_buf(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq) free_srq_idx(hr_dev, srq); } -static int get_srq_ucmd(struct hns_roce_srq *srq, struct ib_udata *udata, - struct hns_roce_ib_create_srq *ucmd) -{ - struct ib_device *ibdev = srq->ibsrq.device; - int ret; - - ret = ib_copy_from_udata(ucmd, udata, min(udata->inlen, sizeof(*ucmd))); - if (ret) { - ibdev_err(ibdev, "failed to copy SRQ udata, ret = %d.\n", ret); - return ret; - } - - return 0; -} static void free_srq_db(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq, struct ib_udata *udata) @@ -423,22 +398,18 @@ static void free_srq_db(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq, static int alloc_srq_db(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq, struct ib_udata *udata, + struct hns_roce_ib_create_srq *ucmd, struct hns_roce_ib_create_srq_resp *resp) { - struct hns_roce_ib_create_srq ucmd = {}; struct hns_roce_ucontext *uctx; int ret; if (udata) { - ret = get_srq_ucmd(srq, udata, &ucmd); - if (ret) - return ret; - if ((hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_SRQ_RECORD_DB) && - (ucmd.req_cap_flags & HNS_ROCE_SRQ_CAP_RECORD_DB)) { + (ucmd->req_cap_flags & HNS_ROCE_SRQ_CAP_RECORD_DB)) { uctx = rdma_udata_to_drv_context(udata, struct hns_roce_ucontext, ibucontext); - ret = hns_roce_db_map_user(uctx, ucmd.db_addr, + ret = hns_roce_db_map_user(uctx, ucmd->db_addr, &srq->rdb); if (ret) return ret; @@ -467,6 +438,7 @@ int hns_roce_create_srq(struct ib_srq *ib_srq, struct hns_roce_dev *hr_dev = to_hr_dev(ib_srq->device); struct hns_roce_ib_create_srq_resp resp = {}; struct hns_roce_srq *srq = to_hr_srq(ib_srq); + struct hns_roce_ib_create_srq ucmd = {}; int ret; mutex_init(&srq->mutex); @@ -476,11 +448,17 @@ int hns_roce_create_srq(struct ib_srq *ib_srq, if (ret) goto err_out; - ret = alloc_srq_buf(hr_dev, srq, udata); + if (udata) { + ret = ib_copy_validate_udata_in(udata, ucmd, que_addr); + if (ret) + goto err_out; + } + + ret = alloc_srq_buf(hr_dev, srq, udata, &ucmd); if (ret) goto err_out; - ret = alloc_srq_db(hr_dev, srq, udata, &resp); + ret = alloc_srq_db(hr_dev, srq, udata, &ucmd, &resp); if (ret) goto err_srq_buf; @@ -492,6 +470,10 @@ int hns_roce_create_srq(struct ib_srq *ib_srq, if (ret) goto err_srqn; + srq->event = hns_roce_ib_srq_event; + init_completion(&srq->free); + refcount_set_release(&srq->refcount, 1); + if (udata) { resp.cap_flags = srq->cap_flags; resp.srqn = srq->srqn; @@ -502,10 +484,6 @@ int hns_roce_create_srq(struct ib_srq *ib_srq, } } - srq->event = hns_roce_ib_srq_event; - refcount_set(&srq->refcount, 1); - init_completion(&srq->free); - return 0; err_srqc: diff --git a/drivers/infiniband/hw/ionic/ionic_controlpath.c b/drivers/infiniband/hw/ionic/ionic_controlpath.c index a5671da3db64..7051a81cca94 100644 --- a/drivers/infiniband/hw/ionic/ionic_controlpath.c +++ b/drivers/infiniband/hw/ionic/ionic_controlpath.c @@ -373,7 +373,7 @@ int ionic_alloc_ucontext(struct ib_ucontext *ibctx, struct ib_udata *udata) phys_addr_t db_phys = 0; int rc; - rc = ib_copy_from_udata(&req, udata, sizeof(req)); + rc = ib_copy_validate_udata_in(udata, req, rsvd); if (rc) return rc; @@ -1225,7 +1225,7 @@ int ionic_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, int udma_idx = 0, rc; if (udata) { - rc = ib_copy_from_udata(&req, udata, sizeof(req)); + rc = ib_copy_validate_udata_in(udata, req, rsvd); if (rc) return rc; } @@ -2154,7 +2154,7 @@ int ionic_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attr, int rc; if (udata) { - rc = ib_copy_from_udata(&req, udata, sizeof(req)); + rc = ib_copy_validate_udata_in(udata, req, rsvd); if (rc) return rc; } else { diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.c b/drivers/infiniband/hw/ionic/ionic_ibdev.c index bd4c73e530d0..73a616ae3502 100644 --- a/drivers/infiniband/hw/ionic/ionic_ibdev.c +++ b/drivers/infiniband/hw/ionic/ionic_ibdev.c @@ -185,7 +185,7 @@ static ssize_t hca_type_show(struct device *device, struct ionic_ibdev *dev = rdma_device_to_drv_device(device, struct ionic_ibdev, ibdev); - return sysfs_emit(buf, "%s\n", dev->ibdev.node_desc); + return sysfs_emit(buf, "%.64s\n", dev->ibdev.node_desc); } static DEVICE_ATTR_RO(hca_type); diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.h b/drivers/infiniband/hw/ionic/ionic_ibdev.h index 82fda1e3cdb6..63828240d659 100644 --- a/drivers/infiniband/hw/ionic/ionic_ibdev.h +++ b/drivers/infiniband/hw/ionic/ionic_ibdev.h @@ -4,9 +4,9 @@ #ifndef _IONIC_IBDEV_H_ #define _IONIC_IBDEV_H_ -#include #include #include +#include #include #include diff --git a/drivers/infiniband/hw/irdma/ctrl.c b/drivers/infiniband/hw/irdma/ctrl.c index 45c7433c96f3..335ae3c82e17 100644 --- a/drivers/infiniband/hw/irdma/ctrl.c +++ b/drivers/infiniband/hw/irdma/ctrl.c @@ -3570,6 +3570,41 @@ static int irdma_sc_parse_fpm_query_buf(struct irdma_sc_dev *dev, __le64 *buf, hmc_fpm_misc->loc_mem_pages = (u32)FIELD_GET(IRDMA_QUERY_FPM_LOC_MEM_PAGES, temp); if (!hmc_fpm_misc->loc_mem_pages) return -EINVAL; + + get_64bit_val(buf, 184, &temp); + if (temp) { + hmc_fpm_misc->fw_scratch_buf0.size = temp; + hmc_fpm_misc->fw_scratch_buf0.va = + dma_alloc_coherent(dev->hw->device, + hmc_fpm_misc->fw_scratch_buf0.size, + &hmc_fpm_misc->fw_scratch_buf0.pa, + GFP_KERNEL); + + if (!hmc_fpm_misc->fw_scratch_buf0.va) { + hmc_fpm_misc->fw_scratch_buf0.size = 0; + return -ENOMEM; + } + } + get_64bit_val(buf, 192, &temp); + if (temp) { + hmc_fpm_misc->fw_scratch_buf1.size = temp; + hmc_fpm_misc->fw_scratch_buf1.va = + dma_alloc_coherent(dev->hw->device, + hmc_fpm_misc->fw_scratch_buf1.size, + &hmc_fpm_misc->fw_scratch_buf1.pa, + GFP_KERNEL); + + if (!hmc_fpm_misc->fw_scratch_buf1.va) { + hmc_fpm_misc->fw_scratch_buf1.size = 0; + dma_free_coherent(dev->hw->device, + hmc_fpm_misc->fw_scratch_buf0.size, + hmc_fpm_misc->fw_scratch_buf0.va, + hmc_fpm_misc->fw_scratch_buf0.pa); + hmc_fpm_misc->fw_scratch_buf0.va = NULL; + hmc_fpm_misc->fw_scratch_buf0.size = 0; + return -ENOMEM; + } + } } return 0; @@ -4187,6 +4222,8 @@ static int irdma_sc_commit_fpm_val(struct irdma_sc_cqp *cqp, u64 scratch, hdr = FIELD_PREP(IRDMA_CQPSQ_BUFSIZE, IRDMA_COMMIT_FPM_BUF_SIZE) | FIELD_PREP(IRDMA_CQPSQ_OPCODE, IRDMA_CQP_OP_COMMIT_FPM_VAL) | + FIELD_PREP(IRDMA_CQPSQ_CFPM_FW_SCRATCH_BUF_PRESENT, + cqp->dev->hmc_fpm_misc.fw_scratch_buf0.va != NULL) | FIELD_PREP(IRDMA_CQPSQ_WQEVALID, cqp->polarity); dma_wmb(); /* make sure WQE is written before valid bit is set */ @@ -5034,7 +5071,9 @@ static void irdma_set_loc_mem(__le64 *buf) for (offset = 0; offset < IRDMA_COMMIT_FPM_BUF_SIZE; offset += sizeof(__le64)) { - if (offset == IRDMA_PBLE_COMMIT_OFFSET) + if (offset == IRDMA_PBLE_COMMIT_OFFSET || + offset == IRDMA_SCRATCH_BUF0_COMMIT_OFFSET || + offset == IRDMA_SCRATCH_BUF1_COMMIT_OFFSET) continue; get_64bit_val(buf, offset, &temp); if (temp) @@ -5090,6 +5129,8 @@ static int irdma_sc_cfg_iw_fpm(struct irdma_sc_dev *dev, u8 hmc_fn_id) (u64)obj_info[IRDMA_HMC_IW_OOISC].cnt); set_64bit_val(buf, 168, (u64)obj_info[IRDMA_HMC_IW_OOISCFFL].cnt); + set_64bit_val(buf, 192, dev->hmc_fpm_misc.fw_scratch_buf0.pa); + set_64bit_val(buf, 200, dev->hmc_fpm_misc.fw_scratch_buf1.pa); if (dev->hw_attrs.uk_attrs.hw_rev >= IRDMA_GEN_3 && dev->hmc_fpm_misc.loc_mem_pages) irdma_set_loc_mem(buf); @@ -6424,6 +6465,7 @@ static inline void irdma_sc_init_hw(struct irdma_sc_dev *dev) icrdma_init_hw(dev); break; case IRDMA_GEN_3: + case IRDMA_GEN_4: ig3rdma_init_hw(dev); break; } diff --git a/drivers/infiniband/hw/irdma/defs.h b/drivers/infiniband/hw/irdma/defs.h index 983b22d7ae23..d6a3152959dd 100644 --- a/drivers/infiniband/hw/irdma/defs.h +++ b/drivers/infiniband/hw/irdma/defs.h @@ -133,6 +133,8 @@ enum irdma_protocol_used { #define MAX_MR_PER_SD 0x8000 #define MAX_MR_SD_PER_FCN 0x80 #define IRDMA_PBLE_COMMIT_OFFSET 112 +#define IRDMA_SCRATCH_BUF0_COMMIT_OFFSET 192 +#define IRDMA_SCRATCH_BUF1_COMMIT_OFFSET 200 #define IRDMA_MAX_QUANTA_PER_WR 8 #define IRDMA_QP_SW_MAX_WQ_QUANTA 32768 @@ -658,6 +660,8 @@ enum irdma_cqp_op_type { #define IRDMA_COMMIT_FPM_QPCNT GENMASK_ULL(20, 0) #define IRDMA_COMMIT_FPM_BASE_S 32 #define IRDMA_CQPSQ_CFPM_HMCFNID GENMASK_ULL(15, 0) +#define IRDMA_CQPSQ_CFPM_FW_SCRATCH_BUF_PRESENT_S 38 +#define IRDMA_CQPSQ_CFPM_FW_SCRATCH_BUF_PRESENT BIT_ULL(38) #define IRDMA_CQPSQ_FWQE_AECODE GENMASK_ULL(15, 0) #define IRDMA_CQPSQ_FWQE_AESOURCE GENMASK_ULL(19, 16) diff --git a/drivers/infiniband/hw/irdma/hw.c b/drivers/infiniband/hw/irdma/hw.c index f4ae530f56db..f9be467d137f 100644 --- a/drivers/infiniband/hw/irdma/hw.c +++ b/drivers/infiniband/hw/irdma/hw.c @@ -1033,7 +1033,7 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) if (!cqp->cqp_requests) return -ENOMEM; - cqp->scratch_array = kcalloc(sqsize, sizeof(*cqp->scratch_array), GFP_KERNEL); + cqp->scratch_array = kzalloc_objs(*cqp->scratch_array, sqsize); if (!cqp->scratch_array) { status = -ENOMEM; goto err_scratch; @@ -1082,6 +1082,7 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) cqp_init_info.hw_maj_ver = IRDMA_CQPHC_HW_MAJVER_GEN_2; break; case IRDMA_GEN_3: + case IRDMA_GEN_4: cqp_init_info.hw_maj_ver = IRDMA_CQPHC_HW_MAJVER_GEN_3; cqp_init_info.ts_override = 1; break; @@ -1508,7 +1509,7 @@ static int irdma_create_aeq(struct irdma_pci_f *rf) hmc_info->hmc_obj[IRDMA_HMC_IW_CQ].cnt; aeq_size = min(aeq_size, dev->hw_attrs.max_hw_aeq_size); /* GEN_3 does not support virtual AEQ. Cap at max Kernel alloc size */ - if (rf->rdma_ver == IRDMA_GEN_3) + if (rf->rdma_ver >= IRDMA_GEN_3) aeq_size = min(aeq_size, (u32)((PAGE_SIZE << MAX_PAGE_ORDER) / sizeof(struct irdma_sc_aeqe))); aeq->mem.size = ALIGN(sizeof(struct irdma_sc_aeqe) * aeq_size, @@ -1518,7 +1519,7 @@ static int irdma_create_aeq(struct irdma_pci_f *rf) GFP_KERNEL | __GFP_NOWARN); if (aeq->mem.va) goto skip_virt_aeq; - else if (rf->rdma_ver == IRDMA_GEN_3) + else if (rf->rdma_ver >= IRDMA_GEN_3) return -ENOMEM; /* physically mapped aeq failed. setup virtual aeq */ @@ -1693,6 +1694,8 @@ static int irdma_hmc_setup(struct irdma_pci_f *rf) static void irdma_del_init_mem(struct irdma_pci_f *rf) { struct irdma_sc_dev *dev = &rf->sc_dev; + struct irdma_dma_mem *fw_scratch_buf0; + struct irdma_dma_mem *fw_scratch_buf1; if (!rf->sc_dev.privileged) irdma_vchnl_req_put_hmc_fcn(&rf->sc_dev); @@ -1713,6 +1716,15 @@ static void irdma_del_init_mem(struct irdma_pci_f *rf) rf->iw_msixtbl = NULL; kfree(rf->hmc_info_mem); rf->hmc_info_mem = NULL; + + fw_scratch_buf0 = &dev->hmc_fpm_misc.fw_scratch_buf0; + fw_scratch_buf1 = &dev->hmc_fpm_misc.fw_scratch_buf1; + if (fw_scratch_buf0->va) + dma_free_coherent(dev->hw->device, fw_scratch_buf0->size, + fw_scratch_buf0->va, fw_scratch_buf0->pa); + if (fw_scratch_buf1->va) + dma_free_coherent(dev->hw->device, fw_scratch_buf1->size, + fw_scratch_buf1->va, fw_scratch_buf1->pa); } /** @@ -1942,7 +1954,7 @@ int irdma_rt_init_hw(struct irdma_device *iwdev, if (status) return status; - stats_info.pestat = kzalloc(sizeof(*stats_info.pestat), GFP_KERNEL); + stats_info.pestat = kzalloc_obj(*stats_info.pestat); if (!stats_info.pestat) { irdma_cleanup_cm_core(&iwdev->cm_core); return -ENOMEM; @@ -2181,8 +2193,13 @@ u32 irdma_initialize_hw_rsrc(struct irdma_pci_f *rf) set_bit(2, rf->allocated_pds); INIT_LIST_HEAD(&rf->mc_qht_list.list); - /* stag index mask has a minimum of 14 bits */ - mrdrvbits = 24 - max(get_count_order(rf->max_mr), 14); + + if (rf->rdma_ver >= IRDMA_GEN_4) + mrdrvbits = 24 - max(get_count_order(rf->max_mr), 16); + else + /* stag index mask has a minimum of 14 bits */ + mrdrvbits = 24 - max(get_count_order(rf->max_mr), 14); + rf->mr_stagmask = ~(((1 << mrdrvbits) - 1) << (32 - mrdrvbits)); return 0; diff --git a/drivers/infiniband/hw/irdma/ig3rdma_hw.c b/drivers/infiniband/hw/irdma/ig3rdma_hw.c index 2e8bb475e22a..f0361675c2de 100644 --- a/drivers/infiniband/hw/irdma/ig3rdma_hw.c +++ b/drivers/infiniband/hw/irdma/ig3rdma_hw.c @@ -113,7 +113,6 @@ void ig3rdma_init_hw(struct irdma_sc_dev *dev) dev->irq_ops = &ig3rdma_irq_ops; dev->hw_stats_map = ig3rdma_hw_stat_map; - dev->hw_attrs.uk_attrs.hw_rev = IRDMA_GEN_3; dev->hw_attrs.uk_attrs.max_hw_wq_frags = IG3RDMA_MAX_WQ_FRAGMENT_COUNT; dev->hw_attrs.uk_attrs.max_hw_read_sges = IG3RDMA_MAX_SGE_RD; dev->hw_attrs.uk_attrs.max_hw_sq_chunk = IRDMA_MAX_QUANTA_PER_WR; diff --git a/drivers/infiniband/hw/irdma/irdma.h b/drivers/infiniband/hw/irdma/irdma.h index ff938a01d70c..b5ce515f4ee8 100644 --- a/drivers/infiniband/hw/irdma/irdma.h +++ b/drivers/infiniband/hw/irdma/irdma.h @@ -119,6 +119,7 @@ enum irdma_vers { IRDMA_GEN_1, IRDMA_GEN_2, IRDMA_GEN_3, + IRDMA_GEN_4, IRDMA_GEN_NEXT, IRDMA_GEN_MAX = IRDMA_GEN_NEXT-1 }; diff --git a/drivers/infiniband/hw/irdma/main.h b/drivers/infiniband/hw/irdma/main.h index d320d1a228b3..3d49bd57bae7 100644 --- a/drivers/infiniband/hw/irdma/main.h +++ b/drivers/infiniband/hw/irdma/main.h @@ -37,8 +37,8 @@ #include #include #include -#include #include +#include #include #include "osdep.h" #include "defs.h" diff --git a/drivers/infiniband/hw/irdma/type.h b/drivers/infiniband/hw/irdma/type.h index da8c54d1f035..5557d9338796 100644 --- a/drivers/infiniband/hw/irdma/type.h +++ b/drivers/infiniband/hw/irdma/type.h @@ -622,6 +622,8 @@ struct irdma_hmc_fpm_misc { u32 timer_bucket; u32 rrf_block_size; u32 ooiscf_block_size; + struct irdma_dma_mem fw_scratch_buf0; + struct irdma_dma_mem fw_scratch_buf1; }; #define IRDMA_VCHNL_MAX_MSG_SIZE 512 diff --git a/drivers/infiniband/hw/irdma/user.h b/drivers/infiniband/hw/irdma/user.h index 9eb7fd0b1cbf..008af1acc928 100644 --- a/drivers/infiniband/hw/irdma/user.h +++ b/drivers/infiniband/hw/irdma/user.h @@ -159,8 +159,8 @@ enum irdma_device_caps_const { IRDMA_CEQE_SIZE = 1, IRDMA_CQP_CTX_SIZE = 8, IRDMA_SHADOW_AREA_SIZE = 8, - IRDMA_QUERY_FPM_BUF_SIZE = 192, - IRDMA_COMMIT_FPM_BUF_SIZE = 192, + IRDMA_QUERY_FPM_BUF_SIZE = 200, + IRDMA_COMMIT_FPM_BUF_SIZE = 208, IRDMA_GATHER_STATS_BUF_SIZE = 1024, IRDMA_MIN_IW_QP_ID = 0, IRDMA_MAX_IW_QP_ID = 262143, diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 95f590c10c05..17086048d2d7 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -284,7 +284,6 @@ static void irdma_alloc_push_page(struct irdma_qp *iwqp) static int irdma_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata) { -#define IRDMA_ALLOC_UCTX_MIN_REQ_LEN offsetofend(struct irdma_alloc_ucontext_req, rsvd8) #define IRDMA_ALLOC_UCTX_MIN_RESP_LEN offsetofend(struct irdma_alloc_ucontext_resp, rsvd) struct ib_device *ibdev = uctx->device; struct irdma_device *iwdev = to_iwdev(ibdev); @@ -292,13 +291,16 @@ static int irdma_alloc_ucontext(struct ib_ucontext *uctx, struct irdma_alloc_ucontext_resp uresp = {}; struct irdma_ucontext *ucontext = to_ucontext(uctx); struct irdma_uk_attrs *uk_attrs = &iwdev->rf->sc_dev.hw_attrs.uk_attrs; + int ret; - if (udata->inlen < IRDMA_ALLOC_UCTX_MIN_REQ_LEN || - udata->outlen < IRDMA_ALLOC_UCTX_MIN_RESP_LEN) + if (udata->outlen < IRDMA_ALLOC_UCTX_MIN_RESP_LEN) return -EINVAL; - if (ib_copy_from_udata(&req, udata, min(sizeof(req), udata->inlen))) - return -EINVAL; + ret = ib_copy_validate_udata_in_cm(udata, req, rsvd8, + IRDMA_ALLOC_UCTX_USE_RAW_ATTR | + IRDMA_SUPPORT_WQE_FORMAT_V2); + if (ret) + return ret; if (req.userspace_ver < 4 || req.userspace_ver > IRDMA_ABI_VER) goto ver_error; @@ -2013,7 +2015,7 @@ static int irdma_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) * @entries: desired cq size * @udata: user data */ -static int irdma_resize_cq(struct ib_cq *ibcq, int entries, +static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries, struct ib_udata *udata) { #define IRDMA_RESIZE_CQ_MIN_REQ_LEN offsetofend(struct irdma_resize_cq_req, user_cq_buffer) @@ -3591,6 +3593,36 @@ error: return ERR_PTR(err); } +static int irdma_hwdereg_mr(struct ib_mr *ib_mr); + +static void irdma_umem_dmabuf_revoke(void *priv) +{ + /* priv is guaranteed to be valid any time this callback is invoked + * because we do not set the callback until after successful iwmr + * allocation and initialization. + */ + struct irdma_mr *iwmr = priv; + int err; + + /* Invalidate the key in hardware. This does not actually release the + * key for potential reuse - that only occurs when the region is fully + * deregistered. + * + * The irdma_hwdereg_mr call is a no-op if the region is not currently + * registered with hardware. + */ + err = irdma_hwdereg_mr(&iwmr->ibmr); + if (err) { + struct irdma_device *iwdev = to_iwdev(iwmr->ibmr.device); + + ibdev_err(&iwdev->ibdev, "dmabuf mr revoke failed %d", err); + if (!iwdev->rf->reset) { + iwdev->rf->reset = true; + iwdev->rf->gen_ops.request_reset(iwdev->rf); + } + } +} + static struct ib_mr *irdma_reg_user_mr_dmabuf(struct ib_pd *pd, u64 start, u64 len, u64 virt, int fd, int access, @@ -3608,7 +3640,9 @@ static struct ib_mr *irdma_reg_user_mr_dmabuf(struct ib_pd *pd, u64 start, if (len > iwdev->rf->sc_dev.hw_attrs.max_mr_size) return ERR_PTR(-EINVAL); - umem_dmabuf = ib_umem_dmabuf_get_pinned(pd->device, start, len, fd, access); + umem_dmabuf = + ib_umem_dmabuf_get_pinned_revocable_and_lock(pd->device, start, + len, fd, access); if (IS_ERR(umem_dmabuf)) { ibdev_dbg(&iwdev->ibdev, "Failed to get dmabuf umem[%pe]\n", umem_dmabuf); @@ -3625,12 +3659,20 @@ static struct ib_mr *irdma_reg_user_mr_dmabuf(struct ib_pd *pd, u64 start, if (err) goto err_iwmr; + ib_umem_dmabuf_set_revoke_locked(umem_dmabuf, irdma_umem_dmabuf_revoke, + iwmr); + ib_umem_dmabuf_revoke_unlock(umem_dmabuf); return &iwmr->ibmr; err_iwmr: irdma_free_iwmr(iwmr); err_release: + ib_umem_dmabuf_revoke_unlock(umem_dmabuf); + + /* Will result in a call to revoke, but driver callback is not set and + * is therefore skipped. + */ ib_umem_release(&umem_dmabuf->umem); return ERR_PTR(err); @@ -3751,6 +3793,8 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, struct irdma_device *iwdev = to_iwdev(ib_mr->device); struct irdma_mr *iwmr = to_iwmr(ib_mr); struct irdma_pbl *iwpbl = &iwmr->iwpbl; + bool dmabuf_revocable = iwmr->region && iwmr->region->is_dmabuf; + struct ib_umem_dmabuf *umem_dmabuf; int ret; if (len > iwdev->rf->sc_dev.hw_attrs.max_mr_size) @@ -3759,9 +3803,26 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, if (flags & ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS)) return ERR_PTR(-EOPNOTSUPP); + if (dmabuf_revocable) { + umem_dmabuf = to_ib_umem_dmabuf(iwmr->region); + + ib_umem_dmabuf_revoke_lock(umem_dmabuf); + + /* If the dmabuf has been revoked, it means that the region has + * been invalidated in HW. We must not allow it to become valid + * again unless the user is requesting a change in translation + * which will end up dropping the umem dmabuf and allocating an + * entirely new umem anyway. + */ + if (umem_dmabuf->revoked && !(flags & IB_MR_REREG_TRANS)) { + ret = -EINVAL; + goto err_unlock; + } + } + ret = irdma_hwdereg_mr(ib_mr); if (ret) - return ERR_PTR(ret); + goto err_unlock; if (flags & IB_MR_REREG_ACCESS) iwmr->access = new_access; @@ -3777,18 +3838,28 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags, &iwpbl->pble_alloc); iwpbl->pbl_allocated = false; } + + if (dmabuf_revocable) { + /* Must unlock before release to prevent deadlock */ + ib_umem_dmabuf_revoke_unlock(umem_dmabuf); + dmabuf_revocable = false; + } + if (iwmr->region) { ib_umem_release(iwmr->region); iwmr->region = NULL; } ret = irdma_rereg_mr_trans(iwmr, start, len, virt); - } else + } else { ret = irdma_hwreg_mr(iwdev, iwmr, iwmr->access); - if (ret) - return ERR_PTR(ret); + } - return NULL; +err_unlock: + if (dmabuf_revocable) + ib_umem_dmabuf_revoke_unlock(umem_dmabuf); + + return ret ? ERR_PTR(ret) : NULL; } /** @@ -3911,6 +3982,7 @@ static int irdma_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata) struct irdma_mr *iwmr = to_iwmr(ib_mr); struct irdma_device *iwdev = to_iwdev(ib_mr->device); struct irdma_pbl *iwpbl = &iwmr->iwpbl; + bool dmabuf_revocable = iwmr->region && iwmr->region->is_dmabuf; int ret; if (iwmr->type != IRDMA_MEMREG_TYPE_MEM) { @@ -3925,17 +3997,28 @@ static int irdma_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata) goto done; } - ret = irdma_hwdereg_mr(ib_mr); - if (ret) - return ret; + if (!dmabuf_revocable) { + ret = irdma_hwdereg_mr(ib_mr); + if (ret) + return ret; - irdma_free_stag(iwdev, iwmr->stag); + irdma_free_stag(iwdev, iwmr->stag); + } done: + if (iwmr->region) + /* For dmabuf MRs, ib_umem_release will trigger a synchronous + * call to the revoke callback which will perform the actual HW + * invalidation via irdma_hwdereg_mr. We rely on this for its + * implicit serialization w.r.t. concurrent revocations. This + * must be done before freeing the PBLEs. + */ + ib_umem_release(iwmr->region); + if (iwpbl->pbl_allocated) irdma_free_pble(iwdev->rf->pble_rsrc, &iwpbl->pble_alloc); - if (iwmr->region) - ib_umem_release(iwmr->region); + if (dmabuf_revocable) + irdma_free_stag(iwdev, iwmr->stag); kfree(iwmr); @@ -5382,7 +5465,7 @@ static const struct ib_device_ops irdma_dev_ops = { .reg_user_mr_dmabuf = irdma_reg_user_mr_dmabuf, .rereg_user_mr = irdma_rereg_user_mr, .req_notify_cq = irdma_req_notify_cq, - .resize_cq = irdma_resize_cq, + .resize_user_cq = irdma_resize_cq, INIT_RDMA_OBJ_SIZE(ib_pd, irdma_pd, ibpd), INIT_RDMA_OBJ_SIZE(ib_ucontext, irdma_ucontext, ibucontext), INIT_RDMA_OBJ_SIZE(ib_ah, irdma_ah, ibah), diff --git a/drivers/infiniband/hw/mana/cq.c b/drivers/infiniband/hw/mana/cq.c index b2749f971cd0..2d682428ef20 100644 --- a/drivers/infiniband/hw/mana/cq.c +++ b/drivers/infiniband/hw/mana/cq.c @@ -13,7 +13,7 @@ int mana_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, struct mana_ib_create_cq_resp resp = {}; struct mana_ib_ucontext *mana_ucontext; struct ib_device *ibdev = ibcq->device; - struct mana_ib_create_cq ucmd = {}; + struct mana_ib_create_cq ucmd; struct mana_ib_dev *mdev; bool is_rnic_cq; u32 doorbell; @@ -27,14 +27,9 @@ int mana_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, is_rnic_cq = mana_ib_is_rnic(mdev); if (udata) { - if (udata->inlen < offsetof(struct mana_ib_create_cq, flags)) - return -EINVAL; - - err = ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen)); - if (err) { - ibdev_dbg(ibdev, "Failed to copy from udata for create cq, %d\n", err); + err = ib_copy_validate_udata_in(udata, ucmd, buf_addr); + if (err) return err; - } if ((!is_rnic_cq && attr->cqe > mdev->adapter_caps.max_qp_wr) || attr->cqe > U32_MAX / COMP_ENTRY_SIZE) { @@ -142,8 +137,9 @@ int mana_ib_install_cq_cb(struct mana_ib_dev *mdev, struct mana_ib_cq *cq) if (cq->queue.id >= gc->max_num_cqs) return -EINVAL; - /* Create CQ table entry */ - WARN_ON(gc->cq_table[cq->queue.id]); + /* Create CQ table entry, sharing a CQ between WQs is not supported */ + if (gc->cq_table[cq->queue.id]) + return -EINVAL; if (cq->queue.kmem) gdma_cq = cq->queue.kmem; else diff --git a/drivers/infiniband/hw/mana/device.c b/drivers/infiniband/hw/mana/device.c index ccc2279ca63c..9811570ab8f8 100644 --- a/drivers/infiniband/hw/mana/device.c +++ b/drivers/infiniband/hw/mana/device.c @@ -17,6 +17,7 @@ static const struct ib_device_ops mana_ib_dev_ops = { .uverbs_abi_ver = MANA_IB_UVERBS_ABI_VERSION, .add_gid = mana_ib_gd_add_gid, + .alloc_mw = mana_ib_alloc_mw, .alloc_pd = mana_ib_alloc_pd, .alloc_ucontext = mana_ib_alloc_ucontext, .create_ah = mana_ib_create_ah, @@ -24,6 +25,7 @@ static const struct ib_device_ops mana_ib_dev_ops = { .create_qp = mana_ib_create_qp, .create_rwq_ind_table = mana_ib_create_rwq_ind_table, .create_wq = mana_ib_create_wq, + .dealloc_mw = mana_ib_dealloc_mw, .dealloc_pd = mana_ib_dealloc_pd, .dealloc_ucontext = mana_ib_dealloc_ucontext, .del_gid = mana_ib_gd_del_gid, @@ -53,6 +55,7 @@ static const struct ib_device_ops mana_ib_dev_ops = { INIT_RDMA_OBJ_SIZE(ib_ah, mana_ib_ah, ibah), INIT_RDMA_OBJ_SIZE(ib_cq, mana_ib_cq, ibcq), + INIT_RDMA_OBJ_SIZE(ib_mw, mana_ib_mw, ibmw), INIT_RDMA_OBJ_SIZE(ib_pd, mana_ib_pd, ibpd), INIT_RDMA_OBJ_SIZE(ib_qp, mana_ib_qp, ibqp), INIT_RDMA_OBJ_SIZE(ib_ucontext, mana_ib_ucontext, ibucontext), diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c index 8d99cd00f002..ac5e75dd3494 100644 --- a/drivers/infiniband/hw/mana/main.c +++ b/drivers/infiniband/hw/mana/main.c @@ -87,18 +87,9 @@ int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) flags |= GDMA_PD_FLAG_ALLOW_GPA_MR; req.flags = flags; - err = mana_gd_send_request(gc, sizeof(req), &req, - sizeof(resp), &resp); - - if (err || resp.hdr.status) { - ibdev_dbg(&dev->ib_dev, - "Failed to get pd_id err %d status %u\n", err, - resp.hdr.status); - if (!err) - err = -EPROTO; - + err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); + if (err) return err; - } pd->pd_handle = resp.pd_handle; pd->pdn = resp.pd_id; @@ -118,7 +109,6 @@ int mana_ib_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) struct gdma_destroy_pd_req req = {}; struct mana_ib_dev *dev; struct gdma_context *gc; - int err; dev = container_of(ibdev, struct mana_ib_dev, ib_dev); gc = mdev_to_gc(dev); @@ -127,18 +117,8 @@ int mana_ib_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) sizeof(resp)); req.pd_handle = pd->pd_handle; - err = mana_gd_send_request(gc, sizeof(req), &req, - sizeof(resp), &resp); - if (err || resp.hdr.status) { - ibdev_dbg(&dev->ib_dev, - "Failed to destroy pd_handle 0x%llx err %d status %u", - pd->pd_handle, err, resp.hdr.status); - if (!err) - err = -EPROTO; - } - - return err; + return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } static int mana_gd_destroy_doorbell_page(struct gdma_context *gc, @@ -146,7 +126,6 @@ static int mana_gd_destroy_doorbell_page(struct gdma_context *gc, { struct gdma_destroy_resource_range_req req = {}; struct gdma_resp_hdr resp = {}; - int err; mana_gd_init_req_hdr(&req.hdr, GDMA_DESTROY_RESOURCE_RANGE, sizeof(req), sizeof(resp)); @@ -155,15 +134,7 @@ static int mana_gd_destroy_doorbell_page(struct gdma_context *gc, req.num_resources = 1; req.allocated_resources = doorbell_page; - err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err || resp.status) { - dev_err(gc->dev, - "Failed to destroy doorbell page: ret %d, 0x%x\n", - err, resp.status); - return err ?: -EPROTO; - } - - return 0; + return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } static int mana_gd_allocate_doorbell_page(struct gdma_context *gc, @@ -184,12 +155,8 @@ static int mana_gd_allocate_doorbell_page(struct gdma_context *gc, req.allocated_resources = 0; err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err || resp.hdr.status) { - dev_err(gc->dev, - "Failed to allocate doorbell page: ret %d, 0x%x\n", - err, resp.hdr.status); - return err ?: -EPROTO; - } + if (err) + return err; *doorbell_page = resp.allocated_resources; @@ -682,14 +649,10 @@ int mana_ib_gd_query_adapter_caps(struct mana_ib_dev *dev) req.hdr.resp.msg_version = GDMA_MESSAGE_V4; req.hdr.dev_id = dev->gdma_dev->dev_id; - err = mana_gd_send_request(mdev_to_gc(dev), sizeof(req), - &req, sizeof(resp), &resp); - - if (err) { - ibdev_err(&dev->ib_dev, - "Failed to query adapter caps err %d", err); + err = mana_gd_send_request(mdev_to_gc(dev), sizeof(req), &req, + sizeof(resp), &resp); + if (err) return err; - } caps->max_sq_id = resp.max_sq_id; caps->max_rq_id = resp.max_rq_id; @@ -727,12 +690,10 @@ int mana_eth_query_adapter_caps(struct mana_ib_dev *dev) mana_gd_init_req_hdr(&req.hdr, GDMA_QUERY_MAX_RESOURCES, sizeof(req), sizeof(resp)); - err = mana_gd_send_request(mdev_to_gc(dev), sizeof(req), &req, sizeof(resp), &resp); - if (err) { - ibdev_err(&dev->ib_dev, - "Failed to query adapter caps err %d", err); + err = mana_gd_send_request(mdev_to_gc(dev), sizeof(req), &req, + sizeof(resp), &resp); + if (err) return err; - } caps->max_qp_count = min_t(u32, resp.max_sq, resp.max_rq); caps->max_cq_count = resp.max_cq; @@ -847,10 +808,8 @@ int mana_ib_gd_create_rnic_adapter(struct mana_ib_dev *mdev) req.feature_flags |= MANA_IB_FEATURE_CLIENT_ERROR_CQE_REQUEST; err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err) { - ibdev_err(&mdev->ib_dev, "Failed to create RNIC adapter err %d", err); + if (err) return err; - } mdev->adapter_handle = resp.adapter; return 0; @@ -861,20 +820,13 @@ int mana_ib_gd_destroy_rnic_adapter(struct mana_ib_dev *mdev) struct mana_rnic_destroy_adapter_resp resp = {}; struct mana_rnic_destroy_adapter_req req = {}; struct gdma_context *gc; - int err; gc = mdev_to_gc(mdev); mana_gd_init_req_hdr(&req.hdr, MANA_IB_DESTROY_ADAPTER, sizeof(req), sizeof(resp)); req.hdr.dev_id = mdev->gdma_dev->dev_id; req.adapter = mdev->adapter_handle; - err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err) { - ibdev_err(&mdev->ib_dev, "Failed to destroy RNIC adapter err %d", err); - return err; - } - - return 0; + return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } int mana_ib_gd_add_gid(const struct ib_gid_attr *attr, void **context) @@ -884,7 +836,6 @@ int mana_ib_gd_add_gid(const struct ib_gid_attr *attr, void **context) struct mana_rnic_config_addr_resp resp = {}; struct gdma_context *gc = mdev_to_gc(mdev); struct mana_rnic_config_addr_req req = {}; - int err; if (ntype != RDMA_NETWORK_IPV4 && ntype != RDMA_NETWORK_IPV6) { ibdev_dbg(&mdev->ib_dev, "Unsupported rdma network type %d", ntype); @@ -898,13 +849,7 @@ int mana_ib_gd_add_gid(const struct ib_gid_attr *attr, void **context) req.sgid_type = (ntype == RDMA_NETWORK_IPV6) ? SGID_TYPE_IPV6 : SGID_TYPE_IPV4; copy_in_reverse(req.ip_addr, attr->gid.raw, sizeof(union ib_gid)); - err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err) { - ibdev_err(&mdev->ib_dev, "Failed to config IP addr err %d\n", err); - return err; - } - - return 0; + return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } int mana_ib_gd_del_gid(const struct ib_gid_attr *attr, void **context) @@ -914,7 +859,6 @@ int mana_ib_gd_del_gid(const struct ib_gid_attr *attr, void **context) struct mana_rnic_config_addr_resp resp = {}; struct gdma_context *gc = mdev_to_gc(mdev); struct mana_rnic_config_addr_req req = {}; - int err; if (ntype != RDMA_NETWORK_IPV4 && ntype != RDMA_NETWORK_IPV6) { ibdev_dbg(&mdev->ib_dev, "Unsupported rdma network type %d", ntype); @@ -928,13 +872,7 @@ int mana_ib_gd_del_gid(const struct ib_gid_attr *attr, void **context) req.sgid_type = (ntype == RDMA_NETWORK_IPV6) ? SGID_TYPE_IPV6 : SGID_TYPE_IPV4; copy_in_reverse(req.ip_addr, attr->gid.raw, sizeof(union ib_gid)); - err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err) { - ibdev_err(&mdev->ib_dev, "Failed to config IP addr err %d\n", err); - return err; - } - - return 0; + return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } int mana_ib_gd_config_mac(struct mana_ib_dev *mdev, enum mana_ib_addr_op op, u8 *mac) @@ -942,7 +880,6 @@ int mana_ib_gd_config_mac(struct mana_ib_dev *mdev, enum mana_ib_addr_op op, u8 struct mana_rnic_config_mac_addr_resp resp = {}; struct mana_rnic_config_mac_addr_req req = {}; struct gdma_context *gc = mdev_to_gc(mdev); - int err; mana_gd_init_req_hdr(&req.hdr, MANA_IB_CONFIG_MAC_ADDR, sizeof(req), sizeof(resp)); req.hdr.dev_id = mdev->gdma_dev->dev_id; @@ -950,13 +887,7 @@ int mana_ib_gd_config_mac(struct mana_ib_dev *mdev, enum mana_ib_addr_op op, u8 req.op = op; copy_in_reverse(req.mac_addr, mac, ETH_ALEN); - err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err) { - ibdev_err(&mdev->ib_dev, "Failed to config Mac addr err %d", err); - return err; - } - - return 0; + return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } int mana_ib_gd_create_cq(struct mana_ib_dev *mdev, struct mana_ib_cq *cq, u32 doorbell) @@ -996,7 +927,6 @@ int mana_ib_gd_destroy_cq(struct mana_ib_dev *mdev, struct mana_ib_cq *cq) struct gdma_context *gc = mdev_to_gc(mdev); struct mana_rnic_destroy_cq_resp resp = {}; struct mana_rnic_destroy_cq_req req = {}; - int err; if (cq->cq_handle == INVALID_MANA_HANDLE) return 0; @@ -1006,14 +936,7 @@ int mana_ib_gd_destroy_cq(struct mana_ib_dev *mdev, struct mana_ib_cq *cq) req.adapter = mdev->adapter_handle; req.cq_handle = cq->cq_handle; - err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - - if (err) { - ibdev_err(&mdev->ib_dev, "Failed to destroy cq err %d", err); - return err; - } - - return 0; + return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } int mana_ib_gd_create_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp, @@ -1043,10 +966,9 @@ int mana_ib_gd_create_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp, req.flags = flags; err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err) { - ibdev_err(&mdev->ib_dev, "Failed to create rc qp err %d", err); + if (err) return err; - } + qp->qp_handle = resp.rc_qp_handle; for (i = 0; i < MANA_RC_QUEUE_TYPE_MAX; i++) { qp->rc_qp.queues[i].id = resp.queue_ids[i]; @@ -1061,18 +983,13 @@ int mana_ib_gd_destroy_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp) struct mana_rnic_destroy_rc_qp_resp resp = {0}; struct mana_rnic_destroy_rc_qp_req req = {0}; struct gdma_context *gc = mdev_to_gc(mdev); - int err; mana_gd_init_req_hdr(&req.hdr, MANA_IB_DESTROY_RC_QP, sizeof(req), sizeof(resp)); req.hdr.dev_id = mdev->gdma_dev->dev_id; req.adapter = mdev->adapter_handle; req.rc_qp_handle = qp->qp_handle; - err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err) { - ibdev_err(&mdev->ib_dev, "Failed to destroy rc qp err %d", err); - return err; - } - return 0; + + return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } int mana_ib_gd_create_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp, @@ -1101,10 +1018,9 @@ int mana_ib_gd_create_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp, req.max_recv_sge = attr->cap.max_recv_sge; req.qp_type = type; err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err) { - ibdev_err(&mdev->ib_dev, "Failed to create ud qp err %d", err); + if (err) return err; - } + qp->qp_handle = resp.qp_handle; for (i = 0; i < MANA_UD_QUEUE_TYPE_MAX; i++) { qp->ud_qp.queues[i].id = resp.queue_ids[i]; @@ -1119,16 +1035,11 @@ int mana_ib_gd_destroy_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp) struct mana_rnic_destroy_udqp_resp resp = {0}; struct mana_rnic_destroy_udqp_req req = {0}; struct gdma_context *gc = mdev_to_gc(mdev); - int err; mana_gd_init_req_hdr(&req.hdr, MANA_IB_DESTROY_UD_QP, sizeof(req), sizeof(resp)); req.hdr.dev_id = mdev->gdma_dev->dev_id; req.adapter = mdev->adapter_handle; req.qp_handle = qp->qp_handle; - err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err) { - ibdev_err(&mdev->ib_dev, "Failed to destroy ud qp err %d", err); - return err; - } - return 0; + + return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } diff --git a/drivers/infiniband/hw/mana/mana_ib.h b/drivers/infiniband/hw/mana/mana_ib.h index e447acfd2071..c9c94e86a72b 100644 --- a/drivers/infiniband/hw/mana/mana_ib.h +++ b/drivers/infiniband/hw/mana/mana_ib.h @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include #include @@ -125,6 +125,11 @@ struct mana_ib_ah { dma_addr_t dma_handle; }; +struct mana_ib_mw { + struct ib_mw ibmw; + mana_handle_t mw_handle; +}; + struct mana_ib_mr { struct ib_mr ibmr; struct ib_umem *umem; @@ -736,6 +741,9 @@ void mana_drain_gsi_sqs(struct mana_ib_dev *mdev); int mana_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc); int mana_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags); +int mana_ib_alloc_mw(struct ib_mw *mw, struct ib_udata *udata); +int mana_ib_dealloc_mw(struct ib_mw *mw); + struct ib_mr *mana_ib_reg_user_mr_dmabuf(struct ib_pd *ibpd, u64 start, u64 length, u64 iova, int fd, int mr_access_flags, struct ib_dmah *dmah, diff --git a/drivers/infiniband/hw/mana/mr.c b/drivers/infiniband/hw/mana/mr.c index 9613b225dad4..8092a7bb785b 100644 --- a/drivers/infiniband/hw/mana/mr.c +++ b/drivers/infiniband/hw/mana/mr.c @@ -6,7 +6,7 @@ #include "mana_ib.h" #define VALID_MR_FLAGS (IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_WRITE | IB_ACCESS_REMOTE_READ |\ - IB_ACCESS_REMOTE_ATOMIC | IB_ZERO_BASED) + IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_MW_BIND | IB_ZERO_BASED) #define VALID_DMA_MR_FLAGS (IB_ACCESS_LOCAL_WRITE) @@ -27,6 +27,9 @@ mana_ib_verbs_to_gdma_access_flags(int access_flags) if (access_flags & IB_ACCESS_REMOTE_ATOMIC) flags |= GDMA_ACCESS_FLAG_REMOTE_ATOMIC; + if (access_flags & IB_ACCESS_MW_BIND) + flags |= GDMA_ACCESS_FLAG_BIND_MW; + return flags; } @@ -70,15 +73,8 @@ static int mana_ib_gd_create_mr(struct mana_ib_dev *dev, struct mana_ib_mr *mr, } err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - - if (err || resp.hdr.status) { - ibdev_dbg(&dev->ib_dev, "Failed to create mr %d, %u", err, - resp.hdr.status); - if (!err) - err = -EPROTO; - + if (err) return err; - } mr->ibmr.lkey = resp.lkey; mr->ibmr.rkey = resp.rkey; @@ -92,23 +88,13 @@ static int mana_ib_gd_destroy_mr(struct mana_ib_dev *dev, u64 mr_handle) struct gdma_destroy_mr_response resp = {}; struct gdma_destroy_mr_request req = {}; struct gdma_context *gc = mdev_to_gc(dev); - int err; mana_gd_init_req_hdr(&req.hdr, GDMA_DESTROY_MR, sizeof(req), sizeof(resp)); req.mr_handle = mr_handle; - err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err || resp.hdr.status) { - dev_err(gc->dev, "Failed to destroy MR: %d, 0x%x\n", err, - resp.hdr.status); - if (!err) - err = -EPROTO; - return err; - } - - return 0; + return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } struct ib_mr *mana_ib_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 length, @@ -304,6 +290,55 @@ err_free: return ERR_PTR(err); } +static int mana_ib_gd_create_mw(struct mana_ib_dev *dev, struct mana_ib_pd *pd, struct ib_mw *ibmw) +{ + struct mana_ib_mw *mw = container_of(ibmw, struct mana_ib_mw, ibmw); + struct gdma_context *gc = mdev_to_gc(dev); + struct gdma_create_mr_response resp = {}; + struct gdma_create_mr_request req = {}; + int err; + + mana_gd_init_req_hdr(&req.hdr, GDMA_CREATE_MR, sizeof(req), sizeof(resp)); + req.hdr.req.msg_version = GDMA_MESSAGE_V2; + req.pd_handle = pd->pd_handle; + + switch (mw->ibmw.type) { + case IB_MW_TYPE_1: + req.mr_type = GDMA_MR_TYPE_MW1; + break; + case IB_MW_TYPE_2: + req.mr_type = GDMA_MR_TYPE_MW2; + break; + default: + return -EINVAL; + } + + err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); + if (err) + return err; + + mw->ibmw.rkey = resp.rkey; + mw->mw_handle = resp.mr_handle; + + return 0; +} + +int mana_ib_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata) +{ + struct mana_ib_dev *mdev = container_of(ibmw->device, struct mana_ib_dev, ib_dev); + struct mana_ib_pd *pd = container_of(ibmw->pd, struct mana_ib_pd, ibpd); + + return mana_ib_gd_create_mw(mdev, pd, ibmw); +} + +int mana_ib_dealloc_mw(struct ib_mw *ibmw) +{ + struct mana_ib_dev *dev = container_of(ibmw->device, struct mana_ib_dev, ib_dev); + struct mana_ib_mw *mw = container_of(ibmw, struct mana_ib_mw, ibmw); + + return mana_ib_gd_destroy_mr(dev, mw->mw_handle); +} + int mana_ib_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata) { struct mana_ib_mr *mr = container_of(ibmr, struct mana_ib_mr, ibmr); @@ -339,12 +374,8 @@ static int mana_ib_gd_alloc_dm(struct mana_ib_dev *mdev, struct mana_ib_dm *dm, req.flags = attr->flags; err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err || resp.hdr.status) { - if (!err) - err = -EPROTO; - + if (err) return err; - } dm->dm_handle = resp.dm_handle; @@ -380,20 +411,11 @@ static int mana_ib_gd_destroy_dm(struct mana_ib_dev *mdev, struct mana_ib_dm *dm struct gdma_context *gc = mdev_to_gc(mdev); struct gdma_destroy_dm_resp resp = {}; struct gdma_destroy_dm_req req = {}; - int err; mana_gd_init_req_hdr(&req.hdr, GDMA_DESTROY_DM, sizeof(req), sizeof(resp)); req.dm_handle = dm->dm_handle; - err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err || resp.hdr.status) { - if (!err) - err = -EPROTO; - - return err; - } - - return 0; + return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } int mana_ib_dealloc_dm(struct ib_dm *ibdm, struct uverbs_attr_bundle *attrs) diff --git a/drivers/infiniband/hw/mana/qp.c b/drivers/infiniband/hw/mana/qp.c index 82f84f7ad37a..0fbcf449c134 100644 --- a/drivers/infiniband/hw/mana/qp.c +++ b/drivers/infiniband/hw/mana/qp.c @@ -21,6 +21,9 @@ static int mana_ib_cfg_vport_steering(struct mana_ib_dev *dev, gc = mdev_to_gc(dev); + if (rx_hash_key_len > sizeof(req->hashkey)) + return -EINVAL; + req_buf_size = struct_size(req, indir_tab, MANA_INDIRECT_TABLE_DEF_SIZE); req = kzalloc(req_buf_size, GFP_KERNEL); if (!req) @@ -68,22 +71,6 @@ static int mana_ib_cfg_vport_steering(struct mana_ib_dev *dev, req->vport, default_rxobj); err = mana_gd_send_request(gc, req_buf_size, req, sizeof(resp), &resp); - if (err) { - netdev_err(ndev, "Failed to configure vPort RX: %d\n", err); - goto out; - } - - if (resp.hdr.status) { - netdev_err(ndev, "vPort RX configuration failed: 0x%x\n", - resp.hdr.status); - err = -EPROTO; - goto out; - } - - netdev_info(ndev, "Configured steering vPort %llu log_entries %u\n", - mpc->port_handle, log_ind_tbl_size); - -out: kfree(req); return err; } @@ -97,7 +84,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, container_of(pd->device, struct mana_ib_dev, ib_dev); struct ib_rwq_ind_table *ind_tbl = attr->rwq_ind_tbl; struct mana_ib_create_qp_rss_resp resp = {}; - struct mana_ib_create_qp_rss ucmd = {}; + struct mana_ib_create_qp_rss ucmd; mana_handle_t *mana_ind_table; struct mana_port_context *mpc; unsigned int ind_tbl_size; @@ -111,16 +98,12 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, u32 port; int ret; - if (!udata || udata->inlen < sizeof(ucmd)) + if (!udata) return -EINVAL; - ret = ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen)); - if (ret) { - ibdev_dbg(&mdev->ib_dev, - "Failed copy from udata for create rss-qp, err %d\n", - ret); + ret = ib_copy_validate_udata_in(udata, ucmd, port); + if (ret) return ret; - } if (attr->cap.max_recv_wr > mdev->adapter_caps.max_qp_wr) { ibdev_dbg(&mdev->ib_dev, @@ -193,11 +176,8 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, ret = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_RQ, &wq_spec, &cq_spec, &wq->rx_object); - if (ret) { - /* Do cleanup starting with index i-1 */ - i--; + if (ret) goto fail; - } /* The GDMA regions are now owned by the WQ object */ wq->queue.gdma_region = GDMA_INVALID_DMA_REGION; @@ -217,8 +197,10 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, /* Create CQ table entry */ ret = mana_ib_install_cq_cb(mdev, cq); - if (ret) + if (ret) { + mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object); goto fail; + } } resp.num_entries = i; @@ -235,13 +217,15 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, ibdev_dbg(&mdev->ib_dev, "Failed to copy to udata create rss-qp, %d\n", ret); - goto fail; + goto err_disable_vport_rx; } kfree(mana_ind_table); return 0; +err_disable_vport_rx: + mana_disable_vport_rx(mpc); fail: while (i-- > 0) { ibwq = ind_tbl->ind_tbl[i]; @@ -282,15 +266,12 @@ static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd, u32 port; int err; - if (!mana_ucontext || udata->inlen < sizeof(ucmd)) + if (!mana_ucontext) return -EINVAL; - err = ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen)); - if (err) { - ibdev_dbg(&mdev->ib_dev, - "Failed to copy from udata create qp-raw, %d\n", err); + err = ib_copy_validate_udata_in(udata, ucmd, port); + if (err) return err; - } if (attr->cap.max_send_wr > mdev->adapter_caps.max_qp_wr) { ibdev_dbg(&mdev->ib_dev, @@ -535,17 +516,15 @@ static int mana_ib_create_rc_qp(struct ib_qp *ibqp, struct ib_pd *ibpd, u64 flags = 0; u32 doorbell; - if (!udata || udata->inlen < sizeof(ucmd)) + if (!udata) return -EINVAL; mana_ucontext = rdma_udata_to_drv_context(udata, struct mana_ib_ucontext, ibucontext); doorbell = mana_ucontext->doorbell; flags = MANA_RC_FLAG_NO_FMR; - err = ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen)); - if (err) { - ibdev_dbg(&mdev->ib_dev, "Failed to copy from udata, %d\n", err); + err = ib_copy_validate_udata_in(udata, ucmd, queue_size); + if (err) return err; - } for (i = 0, j = 0; i < MANA_RC_QUEUE_TYPE_MAX; ++i) { /* skip FMR for user-level RC QPs */ @@ -731,7 +710,6 @@ static int mana_ib_gd_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, struct gdma_context *gc = mdev_to_gc(mdev); struct mana_port_context *mpc; struct net_device *ndev; - int err; mana_gd_init_req_hdr(&req.hdr, MANA_IB_SET_QP_STATE, sizeof(req), sizeof(resp)); @@ -784,13 +762,7 @@ static int mana_ib_gd_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, req.ah_attr.flow_label = attr->ah_attr.grh.flow_label; } - err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); - if (err) { - ibdev_err(&mdev->ib_dev, "Failed modify qp err %d", err); - return err; - } - - return 0; + return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp); } int mana_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, @@ -822,6 +794,21 @@ static int mana_ib_destroy_qp_rss(struct mana_ib_qp *qp, ndev = mana_ib_get_netdev(qp->ibqp.device, qp->port); mpc = netdev_priv(ndev); + /* Disable vPort RX steering before destroying RX WQ objects. + * Otherwise firmware still routes traffic to the destroyed queues, + * which can cause bogus completions on reused CQ IDs when the + * ethernet driver later creates new queues on mana_open(). + * + * Unlike the ethernet teardown path, mana_fence_rqs() cannot be + * used here because the fence completion CQE is delivered on the + * CQ which is polled by userspace (e.g. DPDK), so there is no way + * for the kernel to wait for fence completion. + * + * This is best effort — if it fails there is not much we can do, + * and mana_cfg_vport_steering() already logs the error. + */ + mana_disable_vport_rx(mpc); + for (i = 0; i < (1 << ind_tbl->log_ind_tbl_size); i++) { ibwq = ind_tbl->ind_tbl[i]; wq = container_of(ibwq, struct mana_ib_wq, ibwq); diff --git a/drivers/infiniband/hw/mana/wq.c b/drivers/infiniband/hw/mana/wq.c index 6206244f762e..5c2134a0b1a1 100644 --- a/drivers/infiniband/hw/mana/wq.c +++ b/drivers/infiniband/hw/mana/wq.c @@ -11,19 +11,13 @@ struct ib_wq *mana_ib_create_wq(struct ib_pd *pd, { struct mana_ib_dev *mdev = container_of(pd->device, struct mana_ib_dev, ib_dev); - struct mana_ib_create_wq ucmd = {}; + struct mana_ib_create_wq ucmd; struct mana_ib_wq *wq; int err; - if (udata->inlen < sizeof(ucmd)) - return ERR_PTR(-EINVAL); - - err = ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen)); - if (err) { - ibdev_dbg(&mdev->ib_dev, - "Failed to copy from udata for create wq, %d\n", err); + err = ib_copy_validate_udata_in(udata, ucmd, reserved); + if (err) return ERR_PTR(err); - } wq = kzalloc_obj(*wq); if (!wq) diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c index d98892283065..7a6eb602d4a6 100644 --- a/drivers/infiniband/hw/mlx4/cq.c +++ b/drivers/infiniband/hw/mlx4/cq.c @@ -135,48 +135,10 @@ static void mlx4_ib_free_cq_buf(struct mlx4_ib_dev *dev, struct mlx4_ib_cq_buf * mlx4_buf_free(dev->dev, (cqe + 1) * buf->entry_size, &buf->buf); } -static int mlx4_ib_get_cq_umem(struct mlx4_ib_dev *dev, - struct mlx4_ib_cq_buf *buf, - struct ib_umem **umem, u64 buf_addr, int cqe) -{ - int err; - int cqe_size = dev->dev->caps.cqe_size; - int shift; - int n; - - *umem = ib_umem_get(&dev->ib_dev, buf_addr, cqe * cqe_size, - IB_ACCESS_LOCAL_WRITE); - if (IS_ERR(*umem)) - return PTR_ERR(*umem); - - shift = mlx4_ib_umem_calc_optimal_mtt_size(*umem, 0, &n); - if (shift < 0) { - err = shift; - goto err_buf; - } - - err = mlx4_mtt_init(dev->dev, n, shift, &buf->mtt); - if (err) - goto err_buf; - - err = mlx4_ib_umem_write_mtt(dev, &buf->mtt, *umem); - if (err) - goto err_mtt; - - return 0; - -err_mtt: - mlx4_mtt_cleanup(dev->dev, &buf->mtt); - -err_buf: - ib_umem_release(*umem); - - return err; -} - #define CQ_CREATE_FLAGS_SUPPORTED IB_UVERBS_CQ_FLAGS_TIMESTAMP_COMPLETION -int mlx4_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, - struct uverbs_attr_bundle *attrs) +int mlx4_ib_create_user_cq(struct ib_cq *ibcq, + const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs) { struct ib_udata *udata = &attrs->driver_udata; struct ib_device *ibdev = ibcq->device; @@ -184,13 +146,16 @@ int mlx4_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, int vector = attr->comp_vector; struct mlx4_ib_dev *dev = to_mdev(ibdev); struct mlx4_ib_cq *cq = to_mcq(ibcq); - struct mlx4_uar *uar; + struct mlx4_ib_create_cq ucmd; + int cqe_size = dev->dev->caps.cqe_size; void *buf_addr; + int shift; + int n; int err; struct mlx4_ib_ucontext *context = rdma_udata_to_drv_context( udata, struct mlx4_ib_ucontext, ibucontext); - if (entries < 1 || entries > dev->dev->caps.max_cqes) + if (attr->cqe > dev->dev->caps.max_cqes) return -EINVAL; if (attr->flags & ~CQ_CREATE_FLAGS_SUPPORTED) @@ -200,74 +165,64 @@ int mlx4_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, cq->ibcq.cqe = entries - 1; mutex_init(&cq->resize_mutex); spin_lock_init(&cq->lock); - cq->resize_buf = NULL; - cq->resize_umem = NULL; - cq->create_flags = attr->flags; INIT_LIST_HEAD(&cq->send_qp_list); INIT_LIST_HEAD(&cq->recv_qp_list); - if (udata) { - struct mlx4_ib_create_cq ucmd; + err = ib_copy_validate_udata_in(udata, ucmd, db_addr); + if (err) + goto err_cq; - if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) { - err = -EFAULT; - goto err_cq; - } + if (ibcq->umem && + (dev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_SW_CQ_INIT)) + return -EOPNOTSUPP; - buf_addr = (void *)(unsigned long)ucmd.buf_addr; - err = mlx4_ib_get_cq_umem(dev, &cq->buf, &cq->umem, - ucmd.buf_addr, entries); - if (err) - goto err_cq; + buf_addr = (void *)(unsigned long)ucmd.buf_addr; - err = mlx4_ib_db_map_user(udata, ucmd.db_addr, &cq->db); - if (err) - goto err_mtt; - - uar = &context->uar; - cq->mcq.usage = MLX4_RES_USAGE_USER_VERBS; - } else { - err = mlx4_db_alloc(dev->dev, &cq->db, 1); - if (err) - goto err_cq; - - cq->mcq.set_ci_db = cq->db.db; - cq->mcq.arm_db = cq->db.db + 1; - *cq->mcq.set_ci_db = 0; - *cq->mcq.arm_db = 0; - - err = mlx4_ib_alloc_cq_buf(dev, &cq->buf, entries); - if (err) - goto err_db; - - buf_addr = &cq->buf.buf; - - uar = &dev->priv_uar; - cq->mcq.usage = MLX4_RES_USAGE_DRIVER; + if (!ibcq->umem) + ibcq->umem = ib_umem_get(&dev->ib_dev, ucmd.buf_addr, + entries * cqe_size, + IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(ibcq->umem)) { + err = PTR_ERR(ibcq->umem); + goto err_cq; } + shift = mlx4_ib_umem_calc_optimal_mtt_size(cq->ibcq.umem, 0, &n); + if (shift < 0) { + err = shift; + goto err_cq; + } + + err = mlx4_mtt_init(dev->dev, n, shift, &cq->buf.mtt); + if (err) + goto err_cq; + + err = mlx4_ib_umem_write_mtt(dev, &cq->buf.mtt, cq->ibcq.umem); + if (err) + goto err_mtt; + + err = mlx4_ib_db_map_user(udata, ucmd.db_addr, &cq->db); + if (err) + goto err_mtt; + if (dev->eq_table) vector = dev->eq_table[vector % ibdev->num_comp_vectors]; - err = mlx4_cq_alloc(dev->dev, entries, &cq->buf.mtt, uar, cq->db.dma, - &cq->mcq, vector, 0, - !!(cq->create_flags & - IB_UVERBS_CQ_FLAGS_TIMESTAMP_COMPLETION), - buf_addr, !!udata); + err = mlx4_cq_alloc(dev->dev, entries, &cq->buf.mtt, &context->uar, + cq->db.dma, &cq->mcq, vector, 0, + attr->flags & IB_UVERBS_CQ_FLAGS_TIMESTAMP_COMPLETION, + buf_addr, true); if (err) goto err_dbmap; - if (udata) - cq->mcq.tasklet_ctx.comp = mlx4_ib_cq_comp; - else - cq->mcq.comp = mlx4_ib_cq_comp; + cq->mcq.tasklet_ctx.comp = mlx4_ib_cq_comp; cq->mcq.event = mlx4_ib_cq_event; + cq->mcq.usage = MLX4_RES_USAGE_USER_VERBS; - if (udata) - if (ib_copy_to_udata(udata, &cq->mcq.cqn, sizeof (__u32))) { - err = -EFAULT; - goto err_cq_free; - } + if (ib_copy_to_udata(udata, &cq->mcq.cqn, sizeof(__u32))) { + err = -EFAULT; + goto err_cq_free; + } return 0; @@ -275,20 +230,72 @@ err_cq_free: mlx4_cq_free(dev->dev, &cq->mcq); err_dbmap: - if (udata) - mlx4_ib_db_unmap_user(context, &cq->db); + mlx4_ib_db_unmap_user(context, &cq->db); err_mtt: mlx4_mtt_cleanup(dev->dev, &cq->buf.mtt); + /* UMEM is released by ib_core */ - ib_umem_release(cq->umem); - if (!udata) - mlx4_ib_free_cq_buf(dev, &cq->buf, cq->ibcq.cqe); +err_cq: + return err; +} + +int mlx4_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs) +{ + struct ib_device *ibdev = ibcq->device; + int entries = attr->cqe; + int vector = attr->comp_vector; + struct mlx4_ib_dev *dev = to_mdev(ibdev); + struct mlx4_ib_cq *cq = to_mcq(ibcq); + void *buf_addr; + int err; + + if (attr->cqe > dev->dev->caps.max_cqes) + return -EINVAL; + + entries = roundup_pow_of_two(entries + 1); + cq->ibcq.cqe = entries - 1; + mutex_init(&cq->resize_mutex); + spin_lock_init(&cq->lock); + INIT_LIST_HEAD(&cq->send_qp_list); + INIT_LIST_HEAD(&cq->recv_qp_list); + + err = mlx4_db_alloc(dev->dev, &cq->db, 1); + if (err) + return err; + + cq->mcq.set_ci_db = cq->db.db; + cq->mcq.arm_db = cq->db.db + 1; + *cq->mcq.set_ci_db = 0; + *cq->mcq.arm_db = 0; + + err = mlx4_ib_alloc_cq_buf(dev, &cq->buf, entries); + if (err) + goto err_db; + + buf_addr = &cq->buf.buf; + + if (dev->eq_table) + vector = dev->eq_table[vector % ibdev->num_comp_vectors]; + + err = mlx4_cq_alloc(dev->dev, entries, &cq->buf.mtt, &dev->priv_uar, + cq->db.dma, &cq->mcq, vector, 0, 0, + buf_addr, false); + if (err) + goto err_buf; + + cq->mcq.comp = mlx4_ib_cq_comp; + cq->mcq.event = mlx4_ib_cq_event; + cq->mcq.usage = MLX4_RES_USAGE_DRIVER; + + return 0; + +err_buf: + mlx4_ib_free_cq_buf(dev, &cq->buf, cq->ibcq.cqe); err_db: - if (!udata) - mlx4_db_free(dev->dev, &cq->db); -err_cq: + mlx4_db_free(dev->dev, &cq->db); return err; } @@ -320,29 +327,59 @@ static int mlx4_alloc_resize_umem(struct mlx4_ib_dev *dev, struct mlx4_ib_cq *cq int entries, struct ib_udata *udata) { struct mlx4_ib_resize_cq ucmd; + int cqe_size = dev->dev->caps.cqe_size; + int shift; + int n; int err; if (cq->resize_umem) return -EBUSY; - if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) - return -EFAULT; + err = ib_copy_validate_udata_in(udata, ucmd, buf_addr); + if (err) + return err; cq->resize_buf = kmalloc_obj(*cq->resize_buf); if (!cq->resize_buf) return -ENOMEM; - err = mlx4_ib_get_cq_umem(dev, &cq->resize_buf->buf, &cq->resize_umem, - ucmd.buf_addr, entries); - if (err) { - kfree(cq->resize_buf); - cq->resize_buf = NULL; - return err; + cq->resize_umem = ib_umem_get(&dev->ib_dev, ucmd.buf_addr, + entries * cqe_size, + IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(cq->resize_umem)) { + err = PTR_ERR(cq->resize_umem); + goto err_buf; } + shift = mlx4_ib_umem_calc_optimal_mtt_size(cq->resize_umem, 0, &n); + if (shift < 0) { + err = shift; + goto err_umem; + } + + err = mlx4_mtt_init(dev->dev, n, shift, &cq->resize_buf->buf.mtt); + if (err) + goto err_umem; + + err = mlx4_ib_umem_write_mtt(dev, &cq->resize_buf->buf.mtt, + cq->resize_umem); + if (err) + goto err_mtt; + cq->resize_buf->cqe = entries - 1; return 0; + +err_mtt: + mlx4_mtt_cleanup(dev->dev, &cq->resize_buf->buf.mtt); + +err_umem: + ib_umem_release(cq->resize_umem); + +err_buf: + kfree(cq->resize_buf); + cq->resize_buf = NULL; + return err; } static int mlx4_ib_get_outstanding_cqes(struct mlx4_ib_cq *cq) @@ -381,7 +418,8 @@ static void mlx4_ib_cq_resize_copy_cqes(struct mlx4_ib_cq *cq) ++cq->mcq.cons_index; } -int mlx4_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata) +int mlx4_ib_resize_cq(struct ib_cq *ibcq, unsigned int entries, + struct ib_udata *udata) { struct mlx4_ib_dev *dev = to_mdev(ibcq->device); struct mlx4_ib_cq *cq = to_mcq(ibcq); @@ -390,7 +428,7 @@ int mlx4_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata) int err; mutex_lock(&cq->resize_mutex); - if (entries < 1 || entries > dev->dev->caps.max_cqes) { + if (entries > dev->dev->caps.max_cqes) { err = -EINVAL; goto out; } @@ -433,8 +471,8 @@ int mlx4_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata) if (ibcq->uobject) { cq->buf = cq->resize_buf->buf; cq->ibcq.cqe = cq->resize_buf->cqe; - ib_umem_release(cq->umem); - cq->umem = cq->resize_umem; + ib_umem_release(cq->ibcq.umem); + cq->ibcq.umem = cq->resize_umem; kfree(cq->resize_buf); cq->resize_buf = NULL; @@ -494,11 +532,11 @@ int mlx4_ib_destroy_cq(struct ib_cq *cq, struct ib_udata *udata) struct mlx4_ib_ucontext, ibucontext), &mcq->db); + /* UMEM is released by ib_core */ } else { mlx4_ib_free_cq_buf(dev, &mcq->buf, cq->cqe); mlx4_db_free(dev->dev, &mcq->db); } - ib_umem_release(mcq->umem); return 0; } diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 63360c5a73c7..464c9ab42516 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -50,6 +50,7 @@ #include #include #include +#include #include @@ -445,16 +446,10 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, struct mlx4_clock_params clock_params; if (uhw->inlen) { - if (uhw->inlen < sizeof(cmd)) - return -EINVAL; - - err = ib_copy_from_udata(&cmd, uhw, sizeof(cmd)); + err = ib_copy_validate_udata_in_cm(uhw, cmd, reserved, 0); if (err) return err; - if (cmd.comp_mask) - return -EINVAL; - if (cmd.reserved) return -EINVAL; } @@ -2161,7 +2156,7 @@ static int __mlx4_ib_alloc_diag_counters(struct mlx4_ib_dev *ibdev, if (!*pdescs) return -ENOMEM; - *offset = kcalloc(num_counters, sizeof(**offset), GFP_KERNEL); + *offset = kzalloc_objs(**offset, num_counters); if (!*offset) goto err; @@ -2525,6 +2520,7 @@ static const struct ib_device_ops mlx4_ib_dev_ops = { .attach_mcast = mlx4_ib_mcg_attach, .create_ah = mlx4_ib_create_ah, .create_cq = mlx4_ib_create_cq, + .create_user_cq = mlx4_ib_create_user_cq, .create_qp = mlx4_ib_create_qp, .create_srq = mlx4_ib_create_srq, .dealloc_pd = mlx4_ib_dealloc_pd, @@ -2567,7 +2563,7 @@ static const struct ib_device_ops mlx4_ib_dev_ops = { .reg_user_mr = mlx4_ib_reg_user_mr, .req_notify_cq = mlx4_ib_arm_cq, .rereg_user_mr = mlx4_ib_rereg_user_mr, - .resize_cq = mlx4_ib_resize_cq, + .resize_user_cq = mlx4_ib_resize_cq, .report_port_event = mlx4_ib_port_event, INIT_RDMA_OBJ_SIZE(ib_ah, mlx4_ib_ah, ibah), diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index 5df5b955114e..5a799d6df93e 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -121,9 +121,7 @@ struct mlx4_ib_cq { struct mlx4_db db; spinlock_t lock; struct mutex resize_mutex; - struct ib_umem *umem; struct ib_umem *resize_umem; - int create_flags; /* List of qps that it serves.*/ struct list_head send_qp_list; struct list_head recv_qp_list; @@ -769,9 +767,13 @@ struct ib_mr *mlx4_ib_alloc_mr(struct ib_pd *pd, enum ib_mr_type mr_type, int mlx4_ib_map_mr_sg(struct ib_mr *ibmr, struct scatterlist *sg, int sg_nents, unsigned int *sg_offset); int mlx4_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period); -int mlx4_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata); +int mlx4_ib_resize_cq(struct ib_cq *ibcq, unsigned int entries, + struct ib_udata *udata); int mlx4_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, struct uverbs_attr_bundle *attrs); +int mlx4_ib_create_user_cq(struct ib_cq *ibcq, + const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs); int mlx4_ib_destroy_cq(struct ib_cq *cq, struct ib_udata *udata); int mlx4_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc); int mlx4_ib_arm_cq(struct ib_cq *cq, enum ib_cq_notify_flags flags); diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 77a72d2b0dd2..650b4a9121ff 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -33,6 +33,7 @@ #include #include +#include #include "mlx4_ib.h" diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 1cb890d3d93c..790be09d985a 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -709,8 +709,7 @@ static int _mlx4_ib_create_qp_rss(struct ib_pd *pd, struct mlx4_ib_qp *qp, struct ib_qp_init_attr *init_attr, struct ib_udata *udata) { - struct mlx4_ib_create_qp_rss ucmd = {}; - size_t required_cmd_sz; + struct mlx4_ib_create_qp_rss ucmd; int err; if (!udata) { @@ -721,31 +720,18 @@ static int _mlx4_ib_create_qp_rss(struct ib_pd *pd, struct mlx4_ib_qp *qp, if (udata->outlen) return -EOPNOTSUPP; - required_cmd_sz = offsetof(typeof(ucmd), reserved1) + - sizeof(ucmd.reserved1); - if (udata->inlen < required_cmd_sz) { - pr_debug("invalid inlen\n"); - return -EINVAL; - } - - if (ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen))) { + err = ib_copy_validate_udata_in_cm(udata, ucmd, reserved1, 0); + if (err) { pr_debug("copy failed\n"); - return -EFAULT; + return err; } if (memchr_inv(ucmd.reserved, 0, sizeof(ucmd.reserved))) return -EOPNOTSUPP; - if (ucmd.comp_mask || ucmd.reserved1) + if (ucmd.reserved1) return -EOPNOTSUPP; - if (udata->inlen > sizeof(ucmd) && - !ib_is_udata_cleared(udata, sizeof(ucmd), - udata->inlen - sizeof(ucmd))) { - pr_debug("inlen is not supported\n"); - return -EOPNOTSUPP; - } - if (init_attr->qp_type != IB_QPT_RAW_PACKET) { pr_debug("RSS QP with unsupported QP type %d\n", init_attr->qp_type); @@ -868,7 +854,6 @@ static int create_rq(struct ib_pd *pd, struct ib_qp_init_attr *init_attr, unsigned long flags; int range_size; struct mlx4_ib_create_wq wq; - size_t copy_len; int shift; int n; @@ -881,15 +866,11 @@ static int create_rq(struct ib_pd *pd, struct ib_qp_init_attr *init_attr, qp->state = IB_QPS_RESET; - copy_len = min(sizeof(struct mlx4_ib_create_wq), udata->inlen); - - if (ib_copy_from_udata(&wq, udata, copy_len)) { - err = -EFAULT; + err = ib_copy_validate_udata_in_cm(udata, wq, comp_mask, 0); + if (err) goto err; - } - if (wq.comp_mask || wq.reserved[0] || wq.reserved[1] || - wq.reserved[2]) { + if (wq.reserved[0] || wq.reserved[1] || wq.reserved[2]) { pr_debug("user command isn't supported\n"); err = -EOPNOTSUPP; goto err; @@ -1067,16 +1048,12 @@ static int create_qp_common(struct ib_pd *pd, struct ib_qp_init_attr *init_attr, if (udata) { struct mlx4_ib_create_qp ucmd; - size_t copy_len; int shift; int n; - copy_len = sizeof(struct mlx4_ib_create_qp); - - if (ib_copy_from_udata(&ucmd, udata, copy_len)) { - err = -EFAULT; + err = ib_copy_validate_udata_in(udata, ucmd, sq_no_prefetch); + if (err) goto err; - } qp->inl_recv_sz = ucmd.inl_recv_sz; @@ -4130,26 +4107,11 @@ struct ib_wq *mlx4_ib_create_wq(struct ib_pd *pd, struct mlx4_dev *dev = to_mdev(pd->device)->dev; struct ib_qp_init_attr ib_qp_init_attr = {}; struct mlx4_ib_qp *qp; - struct mlx4_ib_create_wq ucmd; - int err, required_cmd_sz; + int err; if (!udata) return ERR_PTR(-EINVAL); - required_cmd_sz = offsetof(typeof(ucmd), comp_mask) + - sizeof(ucmd.comp_mask); - if (udata->inlen < required_cmd_sz) { - pr_debug("invalid inlen\n"); - return ERR_PTR(-EINVAL); - } - - if (udata->inlen > sizeof(ucmd) && - !ib_is_udata_cleared(udata, sizeof(ucmd), - udata->inlen - sizeof(ucmd))) { - pr_debug("inlen is not supported\n"); - return ERR_PTR(-EOPNOTSUPP); - } - if (udata->outlen) return ERR_PTR(-EOPNOTSUPP); @@ -4268,25 +4230,15 @@ int mlx4_ib_modify_wq(struct ib_wq *ibwq, struct ib_wq_attr *wq_attr, u32 wq_attr_mask, struct ib_udata *udata) { struct mlx4_ib_qp *qp = to_mqp((struct ib_qp *)ibwq); - struct mlx4_ib_modify_wq ucmd = {}; - size_t required_cmd_sz; + struct mlx4_ib_modify_wq ucmd; enum ib_wq_state cur_state, new_state; - int err = 0; + int err; - required_cmd_sz = offsetof(typeof(ucmd), reserved) + - sizeof(ucmd.reserved); - if (udata->inlen < required_cmd_sz) - return -EINVAL; + err = ib_copy_validate_udata_in_cm(udata, ucmd, reserved, 0); + if (err) + return err; - if (udata->inlen > sizeof(ucmd) && - !ib_is_udata_cleared(udata, sizeof(ucmd), - udata->inlen - sizeof(ucmd))) - return -EOPNOTSUPP; - - if (ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen))) - return -EFAULT; - - if (ucmd.comp_mask || ucmd.reserved) + if (ucmd.reserved) return -EOPNOTSUPP; if (wq_attr_mask & IB_WQ_FLAGS) diff --git a/drivers/infiniband/hw/mlx4/srq.c b/drivers/infiniband/hw/mlx4/srq.c index c4cf91235eee..767840736d58 100644 --- a/drivers/infiniband/hw/mlx4/srq.c +++ b/drivers/infiniband/hw/mlx4/srq.c @@ -111,8 +111,9 @@ int mlx4_ib_create_srq(struct ib_srq *ib_srq, if (udata) { struct mlx4_ib_create_srq ucmd; - if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) - return -EFAULT; + err = ib_copy_validate_udata_in(udata, ucmd, db_addr); + if (err) + return err; srq->umem = ib_umem_get(ib_srq->device, ucmd.buf_addr, buf_size, 0); @@ -193,13 +194,15 @@ int mlx4_ib_create_srq(struct ib_srq *ib_srq, if (udata) if (ib_copy_to_udata(udata, &srq->msrq.srqn, sizeof (__u32))) { err = -EFAULT; - goto err_wrid; + goto err_srq; } init_attr->attr.max_wr = srq->msrq.max - 1; return 0; +err_srq: + mlx4_srq_free(dev->dev, &srq->msrq); err_wrid: if (udata) mlx4_ib_db_unmap_user(ucontext, &srq->db); diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index d0cd3f805bcb..a76b7a36087d 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -720,10 +720,9 @@ static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata, int *cqe_size, int *index, int *inlen, struct uverbs_attr_bundle *attrs) { - struct mlx5_ib_create_cq ucmd = {}; + struct mlx5_ib_create_cq ucmd; unsigned long page_size; unsigned int page_offset_quantized; - size_t ucmdlen; __be64 *pas; int ncont; void *cqc; @@ -731,12 +730,9 @@ static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata, struct mlx5_ib_ucontext *context = rdma_udata_to_drv_context( udata, struct mlx5_ib_ucontext, ibucontext); - ucmdlen = min(udata->inlen, sizeof(ucmd)); - if (ucmdlen < offsetof(struct mlx5_ib_create_cq, flags)) - return -EINVAL; - - if (ib_copy_from_udata(&ucmd, udata, ucmdlen)) - return -EFAULT; + err = ib_copy_validate_udata_in(udata, ucmd, cqe_comp_res_format); + if (err) + return err; if ((ucmd.flags & ~(MLX5_IB_CREATE_CQ_FLAGS_CQE_128B_PAD | MLX5_IB_CREATE_CQ_FLAGS_UAR_PAGE_INDEX | @@ -749,16 +745,15 @@ static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata, *cqe_size = ucmd.cqe_size; - cq->buf.umem = - ib_umem_get(&dev->ib_dev, ucmd.buf_addr, - entries * ucmd.cqe_size, IB_ACCESS_LOCAL_WRITE); - if (IS_ERR(cq->buf.umem)) { - err = PTR_ERR(cq->buf.umem); - return err; - } + if (!cq->ibcq.umem) + cq->ibcq.umem = ib_umem_get(&dev->ib_dev, ucmd.buf_addr, + entries * ucmd.cqe_size, + IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(cq->ibcq.umem)) + return PTR_ERR(cq->ibcq.umem); page_size = mlx5_umem_find_best_cq_quantized_pgoff( - cq->buf.umem, cqc, log_page_size, MLX5_ADAPTER_PAGE_SHIFT, + cq->ibcq.umem, cqc, log_page_size, MLX5_ADAPTER_PAGE_SHIFT, page_offset, 64, &page_offset_quantized); if (!page_size) { err = -EINVAL; @@ -769,12 +764,12 @@ static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata, if (err) goto err_umem; - ncont = ib_umem_num_dma_blocks(cq->buf.umem, page_size); + ncont = ib_umem_num_dma_blocks(cq->ibcq.umem, page_size); mlx5_ib_dbg( dev, "addr 0x%llx, size %u, npages %zu, page_size %lu, ncont %d\n", ucmd.buf_addr, entries * ucmd.cqe_size, - ib_umem_num_pages(cq->buf.umem), page_size, ncont); + ib_umem_num_pages(cq->ibcq.umem), page_size, ncont); *inlen = MLX5_ST_SZ_BYTES(create_cq_in) + MLX5_FLD_SZ_BYTES(create_cq_in, pas[0]) * ncont; @@ -785,7 +780,7 @@ static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata, } pas = (__be64 *)MLX5_ADDR_OF(create_cq_in, *cqb, pas); - mlx5_ib_populate_pas(cq->buf.umem, page_size, pas, 0); + mlx5_ib_populate_pas(cq->ibcq.umem, page_size, pas, 0); cqc = MLX5_ADDR_OF(create_cq_in, *cqb, cq_context); MLX5_SET(cqc, cqc, log_page_size, @@ -858,7 +853,7 @@ err_db: mlx5_ib_db_unmap_user(context, &cq->db); err_umem: - ib_umem_release(cq->buf.umem); + /* UMEM is released by ib_core */ return err; } @@ -868,7 +863,6 @@ static void destroy_cq_user(struct mlx5_ib_cq *cq, struct ib_udata *udata) udata, struct mlx5_ib_ucontext, ibucontext); mlx5_ib_db_unmap_user(context, &cq->db); - ib_umem_release(cq->buf.umem); } static void init_cq_frag_buf(struct mlx5_ib_cq_buf *buf) @@ -949,8 +943,9 @@ static void notify_soft_wc_handler(struct work_struct *work) cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context); } -int mlx5_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, - struct uverbs_attr_bundle *attrs) +int mlx5_ib_create_user_cq(struct ib_cq *ibcq, + const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs) { struct ib_udata *udata = &attrs->driver_udata; struct ib_device *ibdev = ibcq->device; @@ -967,8 +962,7 @@ int mlx5_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, int eqn; int err; - if (entries < 0 || - (entries > (1 << MLX5_CAP_GEN(dev->mdev, log_max_cq_sz)))) + if (attr->cqe > (1 << MLX5_CAP_GEN(dev->mdev, log_max_cq_sz))) return -EINVAL; if (check_cq_create_flags(attr->flags)) @@ -981,26 +975,15 @@ int mlx5_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, cq->ibcq.cqe = entries - 1; mutex_init(&cq->resize_mutex); spin_lock_init(&cq->lock); - cq->resize_buf = NULL; - cq->resize_umem = NULL; - cq->create_flags = attr->flags; + if (attr->flags & IB_UVERBS_CQ_FLAGS_TIMESTAMP_COMPLETION) + cq->private_flags |= MLX5_IB_CQ_PR_TIMESTAMP_COMPLETION; INIT_LIST_HEAD(&cq->list_send_qp); INIT_LIST_HEAD(&cq->list_recv_qp); - if (udata) { - err = create_cq_user(dev, udata, cq, entries, &cqb, &cqe_size, - &index, &inlen, attrs); - if (err) - return err; - } else { - cqe_size = cache_line_size() == 128 ? 128 : 64; - err = create_cq_kernel(dev, cq, entries, cqe_size, &cqb, - &index, &inlen); - if (err) - return err; - - INIT_WORK(&cq->notify_work, notify_soft_wc_handler); - } + err = create_cq_user(dev, udata, cq, entries, &cqb, &cqe_size, &index, + &inlen, attrs); + if (err) + return err; err = mlx5_comp_eqn_get(dev->mdev, vector, &eqn); if (err) @@ -1017,15 +1000,11 @@ int mlx5_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, MLX5_SET(cqc, cqc, uar_page, index); MLX5_SET(cqc, cqc, c_eqn_or_apu_element, eqn); MLX5_SET64(cqc, cqc, dbr_addr, cq->db.dma); - if (cq->create_flags & IB_UVERBS_CQ_FLAGS_IGNORE_OVERRUN) + if (attr->flags & IB_UVERBS_CQ_FLAGS_IGNORE_OVERRUN) MLX5_SET(cqc, cqc, oi, 1); - if (udata) { - cq->mcq.comp = mlx5_add_cq_to_tasklet; - cq->mcq.tasklet_ctx.comp = mlx5_ib_cq_comp; - } else { - cq->mcq.comp = mlx5_ib_cq_comp; - } + cq->mcq.comp = mlx5_add_cq_to_tasklet; + cq->mcq.tasklet_ctx.comp = mlx5_ib_cq_comp; err = mlx5_core_create_cq(dev->mdev, &cq->mcq, cqb, inlen, out, sizeof(out)); if (err) @@ -1036,12 +1015,10 @@ int mlx5_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, INIT_LIST_HEAD(&cq->wc_list); - if (udata) - if (ib_copy_to_udata(udata, &cq->mcq.cqn, sizeof(__u32))) { - err = -EFAULT; - goto err_cmd; - } - + if (ib_copy_to_udata(udata, &cq->mcq.cqn, sizeof(__u32))) { + err = -EFAULT; + goto err_cmd; + } kvfree(cqb); return 0; @@ -1051,10 +1028,82 @@ err_cmd: err_cqb: kvfree(cqb); - if (udata) - destroy_cq_user(cq, udata); - else - destroy_cq_kernel(dev, cq); + destroy_cq_user(cq, udata); + return err; +} + + +int mlx5_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs) +{ + struct ib_device *ibdev = ibcq->device; + int entries = attr->cqe; + int vector = attr->comp_vector; + struct mlx5_ib_dev *dev = to_mdev(ibdev); + struct mlx5_ib_cq *cq = to_mcq(ibcq); + u32 out[MLX5_ST_SZ_DW(create_cq_out)]; + int index; + int inlen; + u32 *cqb = NULL; + void *cqc; + int cqe_size; + int eqn; + int err; + + if (attr->cqe > (1 << MLX5_CAP_GEN(dev->mdev, log_max_cq_sz))) + return -EINVAL; + + entries = roundup_pow_of_two(entries + 1); + if (entries > (1 << MLX5_CAP_GEN(dev->mdev, log_max_cq_sz))) + return -EINVAL; + + cq->ibcq.cqe = entries - 1; + mutex_init(&cq->resize_mutex); + spin_lock_init(&cq->lock); + INIT_LIST_HEAD(&cq->list_send_qp); + INIT_LIST_HEAD(&cq->list_recv_qp); + + cqe_size = cache_line_size() == 128 ? 128 : 64; + err = create_cq_kernel(dev, cq, entries, cqe_size, &cqb, &index, + &inlen); + if (err) + return err; + + INIT_WORK(&cq->notify_work, notify_soft_wc_handler); + + err = mlx5_comp_eqn_get(dev->mdev, vector, &eqn); + if (err) + goto err_cqb; + + cq->cqe_size = cqe_size; + + cqc = MLX5_ADDR_OF(create_cq_in, cqb, cq_context); + MLX5_SET(cqc, cqc, cqe_sz, + cqe_sz_to_mlx_sz(cqe_size, + cq->private_flags & + MLX5_IB_CQ_PR_FLAGS_CQE_128_PAD)); + MLX5_SET(cqc, cqc, log_cq_size, ilog2(entries)); + MLX5_SET(cqc, cqc, uar_page, index); + MLX5_SET(cqc, cqc, c_eqn_or_apu_element, eqn); + MLX5_SET64(cqc, cqc, dbr_addr, cq->db.dma); + + cq->mcq.comp = mlx5_ib_cq_comp; + + err = mlx5_core_create_cq(dev->mdev, &cq->mcq, cqb, inlen, out, + sizeof(out)); + if (err) + goto err_cqb; + + mlx5_ib_dbg(dev, "cqn 0x%x\n", cq->mcq.cqn); + cq->mcq.event = mlx5_ib_cq_event; + + INIT_LIST_HEAD(&cq->wc_list); + kvfree(cqb); + return 0; + +err_cqb: + kvfree(cqb); + destroy_cq_kernel(dev, cq); return err; } @@ -1180,7 +1229,7 @@ static int resize_user(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq, struct ib_umem *umem; int err; - err = ib_copy_from_udata(&ucmd, udata, sizeof(ucmd)); + err = ib_copy_validate_udata_in(udata, ucmd, reserved1); if (err) return err; @@ -1282,7 +1331,8 @@ static int copy_resize_cqes(struct mlx5_ib_cq *cq) return 0; } -int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata) +int mlx5_ib_resize_cq(struct ib_cq *ibcq, unsigned int entries, + struct ib_udata *udata) { struct mlx5_ib_dev *dev = to_mdev(ibcq->device); struct mlx5_ib_cq *cq = to_mcq(ibcq); @@ -1302,13 +1352,8 @@ int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata) return -ENOSYS; } - if (entries < 1 || - entries > (1 << MLX5_CAP_GEN(dev->mdev, log_max_cq_sz))) { - mlx5_ib_warn(dev, "wrong entries number %d, max %d\n", - entries, - 1 << MLX5_CAP_GEN(dev->mdev, log_max_cq_sz)); + if (entries > (1 << MLX5_CAP_GEN(dev->mdev, log_max_cq_sz))) return -EINVAL; - } entries = roundup_pow_of_two(entries + 1); if (entries > (1 << MLX5_CAP_GEN(dev->mdev, log_max_cq_sz)) + 1) @@ -1389,8 +1434,8 @@ int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata) if (udata) { cq->ibcq.cqe = entries - 1; - ib_umem_release(cq->buf.umem); - cq->buf.umem = cq->resize_umem; + ib_umem_release(cq->ibcq.umem); + cq->ibcq.umem = cq->resize_umem; cq->resize_umem = NULL; } else { struct mlx5_ib_cq_buf tbuf; diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index 0066b2738ac8..645ebcc0832d 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -1557,7 +1557,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)( if (IS_ERR(cmd_out)) return PTR_ERR(cmd_out); - obj = kzalloc(sizeof(struct devx_obj), GFP_KERNEL); + obj = kzalloc_obj(*obj); if (!obj) return -ENOMEM; @@ -2158,7 +2158,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT)( if (err) goto err; - event_sub = kzalloc(sizeof(*event_sub), GFP_KERNEL); + event_sub = kzalloc_obj(*event_sub); if (!event_sub) { err = -ENOMEM; goto err; @@ -2398,7 +2398,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_UMEM_REG)( if (err) return err; - obj = kzalloc(sizeof(struct devx_umem), GFP_KERNEL); + obj = kzalloc_obj(*obj); if (!obj) return -ENOMEM; diff --git a/drivers/infiniband/hw/mlx5/dm.c b/drivers/infiniband/hw/mlx5/dm.c index 9972f38ba88a..7a6fe4fea3e2 100644 --- a/drivers/infiniband/hw/mlx5/dm.c +++ b/drivers/infiniband/hw/mlx5/dm.c @@ -228,7 +228,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DM_MAP_OP_ADDR)( if (!err || err != -ENOENT) goto err_unlock; - op_entry = kzalloc(sizeof(*op_entry), GFP_KERNEL); + op_entry = kzalloc_obj(*op_entry); if (!op_entry) goto err_unlock; diff --git a/drivers/infiniband/hw/mlx5/fs.c b/drivers/infiniband/hw/mlx5/fs.c index cbccb0b9ac10..b155baee0017 100644 --- a/drivers/infiniband/hw/mlx5/fs.c +++ b/drivers/infiniband/hw/mlx5/fs.c @@ -2917,7 +2917,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_FLOW_MATCHER_CREATE)( struct mlx5_ib_flow_matcher *obj; int err; - obj = kzalloc(sizeof(struct mlx5_ib_flow_matcher), GFP_KERNEL); + obj = kzalloc_obj(*obj); if (!obj) return -ENOMEM; @@ -3017,7 +3017,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_STEERING_ANCHOR_CREATE)( if (err) return err; - obj = kzalloc(sizeof(*obj), GFP_KERNEL); + obj = kzalloc_obj(*obj); if (!obj) return -ENOMEM; @@ -3259,7 +3259,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_FLOW_ACTION_CREATE_PACKET_REFORMAT)( if (!mlx5_ib_flow_action_packet_reformat_valid(mdev, dv_prt, ft_type)) return -EOPNOTSUPP; - maction = kzalloc(sizeof(*maction), GFP_KERNEL); + maction = kzalloc_obj(*maction); if (!maction) return -ENOMEM; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 5b8987ddaa8e..61078281953d 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -2179,7 +2179,7 @@ static int mlx5_ib_alloc_ucontext(struct ib_ucontext *uctx, { struct ib_device *ibdev = uctx->device; struct mlx5_ib_dev *dev = to_mdev(ibdev); - struct mlx5_ib_alloc_ucontext_req_v2 req = {}; + struct mlx5_ib_alloc_ucontext_req_v2 req; struct mlx5_ib_alloc_ucontext_resp resp = {}; struct mlx5_ib_ucontext *context = to_mucontext(uctx); struct mlx5_bfreg_info *bfregi; @@ -2245,16 +2245,14 @@ static int mlx5_ib_alloc_ucontext(struct ib_ucontext *uctx, mutex_init(&bfregi->lock); bfregi->lib_uar_4k = lib_uar_4k; - bfregi->count = kcalloc(bfregi->total_num_bfregs, sizeof(*bfregi->count), - GFP_KERNEL); + bfregi->count = kzalloc_objs(*bfregi->count, bfregi->total_num_bfregs); if (!bfregi->count) { err = -ENOMEM; goto out_ucap; } - bfregi->sys_pages = kcalloc(bfregi->num_sys_pages, - sizeof(*bfregi->sys_pages), - GFP_KERNEL); + bfregi->sys_pages = + kzalloc_objs(*bfregi->sys_pages, bfregi->num_sys_pages); if (!bfregi->sys_pages) { err = -ENOMEM; goto out_count; @@ -2519,12 +2517,22 @@ mlx5_ib_pgoff_to_mmap_entry(struct ib_ucontext *ucontext, off_t pg_off) return rdma_user_mmap_entry_get_pgoff(ucontext, entry_pgoff); } +static void mlx5_ib_free_var_mmap_entry(struct mlx5_user_mmap_entry *mentry, + struct mlx5_var_region *var_region) +{ + mutex_lock(&var_region->bitmap_lock); + clear_bit(mentry->page_idx, var_region->bitmap); + mutex_unlock(&var_region->bitmap_lock); + kfree(mentry); +} + static void mlx5_ib_mmap_free(struct rdma_user_mmap_entry *entry) { struct mlx5_user_mmap_entry *mentry = to_mmmap(entry); struct mlx5_ib_dev *dev = to_mdev(entry->ucontext->device); struct mlx5_var_table *var_table = &dev->var_table; struct mlx5_ib_ucontext *context = to_mucontext(entry->ucontext); + struct mlx5_var_region *var_region; switch (mentry->mmap_flag) { case MLX5_IB_MMAP_TYPE_MEMIC: @@ -2532,10 +2540,12 @@ static void mlx5_ib_mmap_free(struct rdma_user_mmap_entry *entry) mlx5_ib_dm_mmap_free(dev, mentry); break; case MLX5_IB_MMAP_TYPE_VAR: - mutex_lock(&var_table->bitmap_lock); - clear_bit(mentry->page_idx, var_table->bitmap); - mutex_unlock(&var_table->bitmap_lock); - kfree(mentry); + var_region = &var_table->var_region; + mlx5_ib_free_var_mmap_entry(mentry, var_region); + break; + case MLX5_IB_MMAP_TYPE_TLP_VAR: + var_region = &var_table->tlp_var_region; + mlx5_ib_free_var_mmap_entry(mentry, var_region); break; case MLX5_IB_MMAP_TYPE_UAR_WC: case MLX5_IB_MMAP_TYPE_UAR_NC: @@ -2686,6 +2696,7 @@ static int mlx5_ib_mmap_offset(struct mlx5_ib_dev *dev, mentry = to_mmmap(entry); pfn = (mentry->address >> PAGE_SHIFT); if (mentry->mmap_flag == MLX5_IB_MMAP_TYPE_VAR || + mentry->mmap_flag == MLX5_IB_MMAP_TYPE_TLP_VAR || mentry->mmap_flag == MLX5_IB_MMAP_TYPE_UAR_NC) prot = pgprot_noncached(vma->vm_page_prot); else @@ -3299,7 +3310,7 @@ int mlx5_ib_dev_res_cq_init(struct mlx5_ib_dev *dev) * devr->c0 is set once, never changed until device unload. * Avoid taking the mutex if initialization is already done. */ - if (devr->c0) + if (smp_load_acquire(&devr->c0)) return 0; mutex_lock(&devr->cq_lock); @@ -3325,7 +3336,7 @@ int mlx5_ib_dev_res_cq_init(struct mlx5_ib_dev *dev) } devr->p0 = pd; - devr->c0 = cq; + smp_store_release(&devr->c0, cq); unlock: mutex_unlock(&devr->cq_lock); @@ -3343,7 +3354,7 @@ int mlx5_ib_dev_res_srq_init(struct mlx5_ib_dev *dev) * devr->s1 is set once, never changed until device unload. * Avoid taking the mutex if initialization is already done. */ - if (devr->s1) + if (smp_load_acquire(&devr->s1)) return 0; mutex_lock(&devr->srq_lock); @@ -3381,10 +3392,11 @@ int mlx5_ib_dev_res_srq_init(struct mlx5_ib_dev *dev) "Couldn't create SRQ 1 for res init, err=%pe\n", s1); ib_destroy_srq(s0); + goto unlock; } devr->s0 = s0; - devr->s1 = s1; + smp_store_release(&devr->s1, s1); unlock: mutex_unlock(&devr->srq_lock); @@ -4141,46 +4153,54 @@ static int mlx5_rdma_user_mmap_entry_insert(struct mlx5_ib_ucontext *c, } static struct mlx5_user_mmap_entry * -alloc_var_entry(struct mlx5_ib_ucontext *c) +alloc_var_entry(struct mlx5_ib_ucontext *c, u32 flags) { struct mlx5_user_mmap_entry *entry; + struct mlx5_var_region *var_region; struct mlx5_var_table *var_table; u32 page_idx; int err; var_table = &to_mdev(c->ibucontext.device)->var_table; + if (flags & MLX5_IB_UAPI_VAR_ALLOC_FLAG_TLP) + var_region = &var_table->tlp_var_region; + else + var_region = &var_table->var_region; + entry = kzalloc_obj(*entry); if (!entry) return ERR_PTR(-ENOMEM); - mutex_lock(&var_table->bitmap_lock); - page_idx = find_first_zero_bit(var_table->bitmap, - var_table->num_var_hw_entries); - if (page_idx >= var_table->num_var_hw_entries) { + mutex_lock(&var_region->bitmap_lock); + page_idx = find_first_zero_bit(var_region->bitmap, + var_region->num_var_hw_entries); + if (page_idx >= var_region->num_var_hw_entries) { err = -ENOSPC; - mutex_unlock(&var_table->bitmap_lock); + mutex_unlock(&var_region->bitmap_lock); goto end; } - set_bit(page_idx, var_table->bitmap); - mutex_unlock(&var_table->bitmap_lock); + set_bit(page_idx, var_region->bitmap); + mutex_unlock(&var_region->bitmap_lock); - entry->address = var_table->hw_start_addr + - (page_idx * var_table->stride_size); + entry->address = var_region->hw_start_addr + + (page_idx * var_region->stride_size); entry->page_idx = page_idx; - entry->mmap_flag = MLX5_IB_MMAP_TYPE_VAR; + entry->mmap_flag = flags & MLX5_IB_UAPI_VAR_ALLOC_FLAG_TLP ? + MLX5_IB_MMAP_TYPE_TLP_VAR : + MLX5_IB_MMAP_TYPE_VAR; err = mlx5_rdma_user_mmap_entry_insert(c, entry, - var_table->stride_size); + var_region->stride_size); if (err) goto err_insert; return entry; err_insert: - mutex_lock(&var_table->bitmap_lock); - clear_bit(page_idx, var_table->bitmap); - mutex_unlock(&var_table->bitmap_lock); + mutex_lock(&var_region->bitmap_lock); + clear_bit(page_idx, var_region->bitmap); + mutex_unlock(&var_region->bitmap_lock); end: kfree(entry); return ERR_PTR(err); @@ -4191,9 +4211,10 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_VAR_OBJ_ALLOC)( { struct ib_uobject *uobj = uverbs_attr_get_uobject( attrs, MLX5_IB_ATTR_VAR_OBJ_ALLOC_HANDLE); - struct mlx5_ib_ucontext *c; struct mlx5_user_mmap_entry *entry; + struct mlx5_ib_ucontext *c; u64 mmap_offset; + u32 flags = 0; u32 length; int err; @@ -4201,7 +4222,24 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_VAR_OBJ_ALLOC)( if (IS_ERR(c)) return PTR_ERR(c); - entry = alloc_var_entry(c); + err = uverbs_get_flags32(&flags, attrs, + MLX5_IB_ATTR_VAR_OBJ_ALLOC_FLAGS, + MLX5_IB_UAPI_VAR_ALLOC_FLAG_TLP); + if (err) + return err; + + if (flags & MLX5_IB_UAPI_VAR_ALLOC_FLAG_TLP) { + if (!MLX5_CAP_GEN(to_mdev(c->ibucontext.device)->mdev, + tlp_device_emulation_manager)) + return -EOPNOTSUPP; + } else { + if (!(MLX5_CAP_GEN_64(to_mdev(c->ibucontext.device)->mdev, + general_obj_types) & + MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q)) + return -EOPNOTSUPP; + } + + entry = alloc_var_entry(c, flags); if (IS_ERR(entry)) return PTR_ERR(entry); @@ -4231,6 +4269,9 @@ DECLARE_UVERBS_NAMED_METHOD( MLX5_IB_OBJECT_VAR, UVERBS_ACCESS_NEW, UA_MANDATORY), + UVERBS_ATTR_FLAGS_IN(MLX5_IB_ATTR_VAR_OBJ_ALLOC_FLAGS, + enum mlx5_ib_uapi_var_alloc_flags, + UA_OPTIONAL), UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_VAR_OBJ_ALLOC_PAGE_ID, UVERBS_ATTR_TYPE(u32), UA_MANDATORY), @@ -4258,7 +4299,8 @@ static bool var_is_supported(struct ib_device *device) struct mlx5_ib_dev *dev = to_mdev(device); return (MLX5_CAP_GEN_64(dev->mdev, general_obj_types) & - MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q); + MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q) || + MLX5_CAP_GEN(dev->mdev, tlp_device_emulation_manager); } static struct mlx5_user_mmap_entry * @@ -4419,6 +4461,7 @@ static const struct uapi_definition mlx5_ib_defs[] = { static void mlx5_ib_stage_init_cleanup(struct mlx5_ib_dev *dev) { + mlx5_cmd_cleanup_async_ctx(&dev->async_ctx); mlx5_ib_data_direct_cleanup(dev); mlx5_ib_cleanup_multiport_master(dev); WARN_ON(!xa_empty(&dev->odp_mkeys)); @@ -4488,6 +4531,8 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev) if (err && err != -EOPNOTSUPP) goto err_dd; + mlx5_cmd_init_async_ctx(mdev, &dev->async_ctx); + return 0; err_dd: mlx5_ib_data_direct_cleanup(dev); @@ -4518,6 +4563,7 @@ static const struct ib_device_ops mlx5_ib_dev_ops = { .check_mr_status = mlx5_ib_check_mr_status, .create_ah = mlx5_ib_create_ah, .create_cq = mlx5_ib_create_cq, + .create_user_cq = mlx5_ib_create_user_cq, .create_qp = mlx5_ib_create_qp, .create_srq = mlx5_ib_create_srq, .create_user_ah = mlx5_ib_create_ah, @@ -4568,7 +4614,7 @@ static const struct ib_device_ops mlx5_ib_dev_ops = { .reg_user_mr_dmabuf = mlx5_ib_reg_user_mr_dmabuf, .req_notify_cq = mlx5_ib_arm_cq, .rereg_user_mr = mlx5_ib_rereg_user_mr, - .resize_cq = mlx5_ib_resize_cq, + .resize_user_cq = mlx5_ib_resize_cq, .ufile_hw_cleanup = mlx5_ib_ufile_hw_cleanup, INIT_RDMA_OBJ_SIZE(ib_ah, mlx5_ib_ah, ibah), @@ -4607,10 +4653,10 @@ static const struct ib_device_ops mlx5_ib_dev_xrc_ops = { INIT_RDMA_OBJ_SIZE(ib_xrcd, mlx5_ib_xrcd, ibxrcd), }; -static int mlx5_ib_init_var_table(struct mlx5_ib_dev *dev) +static int mlx5_ib_init_var_region(struct mlx5_ib_dev *dev) { + struct mlx5_var_region *var_region = &dev->var_table.var_region; struct mlx5_core_dev *mdev = dev->mdev; - struct mlx5_var_table *var_table = &dev->var_table; u8 log_doorbell_bar_size; u8 log_doorbell_stride; u64 bar_size; @@ -4619,17 +4665,39 @@ static int mlx5_ib_init_var_table(struct mlx5_ib_dev *dev) log_doorbell_bar_size); log_doorbell_stride = MLX5_CAP_DEV_VDPA_EMULATION(mdev, log_doorbell_stride); - var_table->hw_start_addr = dev->mdev->bar_addr + + var_region->hw_start_addr = dev->mdev->bar_addr + MLX5_CAP64_DEV_VDPA_EMULATION(mdev, doorbell_bar_offset); bar_size = (1ULL << log_doorbell_bar_size) * 4096; - var_table->stride_size = 1ULL << log_doorbell_stride; - var_table->num_var_hw_entries = div_u64(bar_size, - var_table->stride_size); - mutex_init(&var_table->bitmap_lock); - var_table->bitmap = bitmap_zalloc(var_table->num_var_hw_entries, - GFP_KERNEL); - return (var_table->bitmap) ? 0 : -ENOMEM; + var_region->stride_size = 1ULL << log_doorbell_stride; + var_region->num_var_hw_entries = div_u64(bar_size, + var_region->stride_size); + mutex_init(&var_region->bitmap_lock); + var_region->bitmap = bitmap_zalloc(var_region->num_var_hw_entries, + GFP_KERNEL); + return (var_region->bitmap) ? 0 : -ENOMEM; +} + +static int mlx5_ib_init_tlp_var_region(struct mlx5_ib_dev *dev) +{ + struct mlx5_var_region *var_region = &dev->var_table.tlp_var_region; + struct mlx5_core_dev *mdev = dev->mdev; + u8 log_tlp_var_stride; + + log_tlp_var_stride = + MLX5_CAP_DEV_TLP_EMULATION(mdev, log_tlp_rsp_gw_page_stride); + var_region->hw_start_addr = + dev->mdev->bar_addr + + MLX5_CAP64_DEV_TLP_EMULATION(mdev, tlp_rsp_gw_pages_bar_offset); + + var_region->stride_size = (1ULL << log_tlp_var_stride) * 4096; + var_region->num_var_hw_entries = + MLX5_CAP_DEV_TLP_EMULATION(mdev, tlp_rsp_gw_num_pages); + + mutex_init(&var_region->bitmap_lock); + var_region->bitmap = bitmap_zalloc(var_region->num_var_hw_entries, + GFP_KERNEL); + return (var_region->bitmap) ? 0 : -ENOMEM; } static void mlx5_ib_cleanup_ucaps(struct mlx5_ib_dev *dev) @@ -4667,13 +4735,19 @@ remove_local: return ret; } +static void mlx5_ib_cleanup_var_table(struct mlx5_ib_dev *dev) +{ + bitmap_free(dev->var_table.var_region.bitmap); + bitmap_free(dev->var_table.tlp_var_region.bitmap); +} + static void mlx5_ib_stage_caps_cleanup(struct mlx5_ib_dev *dev) { if (MLX5_CAP_GEN_2_64(dev->mdev, general_obj_types_127_64) & MLX5_HCA_CAP_2_GENERAL_OBJECT_TYPES_RDMA_CTRL) mlx5_ib_cleanup_ucaps(dev); - bitmap_free(dev->var_table.bitmap); + mlx5_ib_cleanup_var_table(dev); } static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev) @@ -4721,7 +4795,7 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev) if (MLX5_CAP_GEN_64(dev->mdev, general_obj_types) & MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q) { - err = mlx5_ib_init_var_table(dev); + err = mlx5_ib_init_var_region(dev); if (err) return err; } @@ -4733,12 +4807,20 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev) goto err_ucaps; } + if (MLX5_CAP_GEN(dev->mdev, tlp_device_emulation_manager)) { + err = mlx5_ib_init_tlp_var_region(dev); + if (err) + goto err_tlp_var; + } + dev->ib_dev.use_cq_dim = true; return 0; +err_tlp_var: + mlx5_ib_cleanup_ucaps(dev); err_ucaps: - bitmap_free(dev->var_table.bitmap); + bitmap_free(dev->var_table.var_region.bitmap); return err; } @@ -4874,7 +4956,7 @@ static int mlx5_ib_stage_ib_reg_init(struct mlx5_ib_dev *dev) static void mlx5_ib_stage_pre_ib_reg_umr_cleanup(struct mlx5_ib_dev *dev) { - mlx5_mkey_cache_cleanup(dev); + mlx5r_frmr_pools_cleanup(&dev->ib_dev); mlx5r_umr_resource_cleanup(dev); mlx5r_umr_cleanup(dev); } @@ -4892,9 +4974,10 @@ static int mlx5_ib_stage_post_ib_reg_umr_init(struct mlx5_ib_dev *dev) if (ret) return ret; - ret = mlx5_mkey_cache_init(dev); + ret = mlx5r_frmr_pools_init(&dev->ib_dev); if (ret) - mlx5_ib_warn(dev, "mr cache init failed %d\n", ret); + mlx5_ib_warn(dev, "frmr pools init failed %d\n", ret); + return ret; } diff --git a/drivers/infiniband/hw/mlx5/mem.c b/drivers/infiniband/hw/mlx5/mem.c index af321f6ef7f5..75d5b5672b5c 100644 --- a/drivers/infiniband/hw/mlx5/mem.c +++ b/drivers/infiniband/hw/mlx5/mem.c @@ -31,6 +31,7 @@ */ #include +#include #include "mlx5_ib.h" /* diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 3fc31415e107..e156dc4d7529 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -162,6 +162,7 @@ enum mlx5_ib_mmap_type { MLX5_IB_MMAP_TYPE_UAR_WC = 3, MLX5_IB_MMAP_TYPE_UAR_NC = 4, MLX5_IB_MMAP_TYPE_MEMIC_OP = 5, + MLX5_IB_MMAP_TYPE_TLP_VAR = 6, }; struct mlx5_bfreg_info { @@ -560,6 +561,7 @@ struct mlx5_ib_cq_buf { enum mlx5_ib_cq_pr_flags { MLX5_IB_CQ_PR_FLAGS_CQE_128_PAD = 1 << 0, MLX5_IB_CQ_PR_FLAGS_REAL_TIME_TS = 1 << 1, + MLX5_IB_CQ_PR_TIMESTAMP_COMPLETION = 1 << 2, }; struct mlx5_ib_cq { @@ -580,7 +582,6 @@ struct mlx5_ib_cq { int cqe_size; struct list_head list_send_qp; struct list_head list_recv_qp; - u32 create_flags; struct list_head wc_list; enum ib_cq_notify_flags notify_flags; struct work_struct notify_work; @@ -640,25 +641,12 @@ enum mlx5_mkey_type { /* Used for non-existent ph value */ #define MLX5_IB_NO_PH 0xff -struct mlx5r_cache_rb_key { - u8 ats:1; - u8 ph; - u16 st_index; - unsigned int access_mode; - unsigned int access_flags; - unsigned int ndescs; -}; - struct mlx5_ib_mkey { u32 key; enum mlx5_mkey_type type; unsigned int ndescs; struct wait_queue_head wait; refcount_t usecount; - /* Cacheable user Mkey must hold either a rb_key or a cache_ent. */ - struct mlx5r_cache_rb_key rb_key; - struct mlx5_cache_ent *cache_ent; - u8 cacheable : 1; }; #define MLX5_IB_MTT_PRESENT (MLX5_IB_MTT_READ | MLX5_IB_MTT_WRITE) @@ -783,68 +771,6 @@ struct umr_common { struct mutex init_lock; }; -#define NUM_MKEYS_PER_PAGE \ - ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(u32)) - -struct mlx5_mkeys_page { - u32 mkeys[NUM_MKEYS_PER_PAGE]; - struct list_head list; -}; -static_assert(sizeof(struct mlx5_mkeys_page) == PAGE_SIZE); - -struct mlx5_mkeys_queue { - struct list_head pages_list; - u32 num_pages; - unsigned long ci; - spinlock_t lock; /* sync list ops */ -}; - -struct mlx5_cache_ent { - struct mlx5_mkeys_queue mkeys_queue; - u32 pending; - - char name[4]; - - struct rb_node node; - struct mlx5r_cache_rb_key rb_key; - - u8 is_tmp:1; - u8 disabled:1; - u8 fill_to_high_water:1; - u8 tmp_cleanup_scheduled:1; - - /* - * - limit is the low water mark for stored mkeys, 2* limit is the - * upper water mark. - */ - u32 in_use; - u32 limit; - - /* Statistics */ - u32 miss; - - struct mlx5_ib_dev *dev; - struct delayed_work dwork; -}; - -struct mlx5r_async_create_mkey { - union { - u32 in[MLX5_ST_SZ_BYTES(create_mkey_in)]; - u32 out[MLX5_ST_SZ_DW(create_mkey_out)]; - }; - struct mlx5_async_work cb_work; - struct mlx5_cache_ent *ent; - u32 mkey; -}; - -struct mlx5_mkey_cache { - struct workqueue_struct *wq; - struct rb_root rb_root; - struct mutex rb_lock; - struct dentry *fs_root; - unsigned long last_add; -}; - struct mlx5_ib_port_resources { struct mlx5_ib_gsi_qp *gsi; struct work_struct pkey_change_work; @@ -1131,7 +1057,7 @@ struct mlx5_devx_event_table { struct xarray event_xa; }; -struct mlx5_var_table { +struct mlx5_var_region { /* serialize updating the bitmap */ struct mutex bitmap_lock; unsigned long *bitmap; @@ -1140,6 +1066,11 @@ struct mlx5_var_table { u64 num_var_hw_entries; }; +struct mlx5_var_table { + struct mlx5_var_region var_region; + struct mlx5_var_region tlp_var_region; +}; + struct mlx5_port_caps { bool has_smi; u8 ext_port_cap; @@ -1181,8 +1112,6 @@ struct mlx5_ib_dev { struct mlx5_ib_resources devr; atomic_t mkey_var; - struct mlx5_mkey_cache cache; - struct timer_list delay_timer; /* Prevents soft lock on massive reg MRs */ struct mutex slow_path_mutex; struct ib_odp_caps odp_caps; @@ -1370,13 +1299,17 @@ int mlx5_ib_read_wqe_srq(struct mlx5_ib_srq *srq, int wqe_index, void *buffer, size_t buflen, size_t *bc); int mlx5_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, struct uverbs_attr_bundle *attrs); +int mlx5_ib_create_user_cq(struct ib_cq *ibcq, + const struct ib_cq_init_attr *attr, + struct uverbs_attr_bundle *attrs); int mlx5_ib_destroy_cq(struct ib_cq *cq, struct ib_udata *udata); int mlx5_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc); int mlx5_ib_pre_destroy_cq(struct ib_cq *cq); void mlx5_ib_post_destroy_cq(struct ib_cq *cq); int mlx5_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags); int mlx5_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period); -int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata); +int mlx5_ib_resize_cq(struct ib_cq *ibcq, unsigned int entries, + struct ib_udata *udata); struct ib_mr *mlx5_ib_get_dma_mr(struct ib_pd *pd, int acc); struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt_addr, int access_flags, @@ -1441,13 +1374,8 @@ int mlx5_ib_query_port_speed(struct ib_device *ibdev, u32 port_num, void mlx5_ib_populate_pas(struct ib_umem *umem, size_t page_size, __be64 *pas, u64 access_flags); int mlx5_ib_get_cqe_size(struct ib_cq *ibcq); -int mlx5_mkey_cache_init(struct mlx5_ib_dev *dev); -void mlx5_mkey_cache_cleanup(struct mlx5_ib_dev *dev); -struct mlx5_cache_ent * -mlx5r_cache_create_ent_locked(struct mlx5_ib_dev *dev, - struct mlx5r_cache_rb_key rb_key, - bool persistent_entry); - +int mlx5r_frmr_pools_init(struct ib_device *device); +void mlx5r_frmr_pools_cleanup(struct ib_device *device); struct mlx5_ib_mr *mlx5_mr_cache_alloc(struct mlx5_ib_dev *dev, int access_flags, int access_mode, int ndescs); diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index cf26c3f3f09a..3b6da45061a5 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -31,7 +31,6 @@ * SOFTWARE. */ - #include #include #include @@ -39,6 +38,7 @@ #include #include #include +#include #include #include "dm.h" #include "mlx5_ib.h" @@ -46,14 +46,13 @@ #include "data_direct.h" #include "dmah.h" -enum { - MAX_PENDING_REG_MR = 8, -}; +static int mkey_max_umr_order(struct mlx5_ib_dev *dev) +{ + if (MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset)) + return MLX5_MAX_UMR_EXTENDED_SHIFT; + return MLX5_MAX_UMR_SHIFT; +} -#define MLX5_MR_CACHE_PERSISTENT_ENTRY_MIN_DESCS 4 - -static void -create_mkey_callback(int status, struct mlx5_async_work *context); static struct mlx5_ib_mr *reg_create(struct ib_pd *pd, struct ib_umem *umem, u64 iova, int access_flags, unsigned long page_size, bool populate, @@ -110,23 +109,6 @@ static int mlx5_ib_create_mkey(struct mlx5_ib_dev *dev, return ret; } -static int mlx5_ib_create_mkey_cb(struct mlx5r_async_create_mkey *async_create) -{ - struct mlx5_ib_dev *dev = async_create->ent->dev; - size_t inlen = MLX5_ST_SZ_BYTES(create_mkey_in); - size_t outlen = MLX5_ST_SZ_BYTES(create_mkey_out); - - MLX5_SET(create_mkey_in, async_create->in, opcode, - MLX5_CMD_OP_CREATE_MKEY); - assign_mkey_variant(dev, &async_create->mkey, async_create->in); - return mlx5_cmd_exec_cb(&dev->async_ctx, async_create->in, inlen, - async_create->out, outlen, create_mkey_callback, - &async_create->cb_work); -} - -static int mkey_cache_max_order(struct mlx5_ib_dev *dev); -static void queue_adjust_cache_locked(struct mlx5_cache_ent *ent); - static int destroy_mkey(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) { WARN_ON(xa_load(&dev->odp_mkeys, mlx5_base_mkey(mr->mmkey.key))); @@ -134,94 +116,6 @@ static int destroy_mkey(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) return mlx5_core_destroy_mkey(dev->mdev, mr->mmkey.key); } -static void create_mkey_warn(struct mlx5_ib_dev *dev, int status, void *out) -{ - if (status == -ENXIO) /* core driver is not available */ - return; - - mlx5_ib_warn(dev, "async reg mr failed. status %d\n", status); - if (status != -EREMOTEIO) /* driver specific failure */ - return; - - /* Failed in FW, print cmd out failure details */ - mlx5_cmd_out_err(dev->mdev, MLX5_CMD_OP_CREATE_MKEY, 0, out); -} - -static int push_mkey_locked(struct mlx5_cache_ent *ent, u32 mkey) -{ - unsigned long tmp = ent->mkeys_queue.ci % NUM_MKEYS_PER_PAGE; - struct mlx5_mkeys_page *page; - - lockdep_assert_held(&ent->mkeys_queue.lock); - if (ent->mkeys_queue.ci >= - ent->mkeys_queue.num_pages * NUM_MKEYS_PER_PAGE) { - page = kzalloc_obj(*page, GFP_ATOMIC); - if (!page) - return -ENOMEM; - ent->mkeys_queue.num_pages++; - list_add_tail(&page->list, &ent->mkeys_queue.pages_list); - } else { - page = list_last_entry(&ent->mkeys_queue.pages_list, - struct mlx5_mkeys_page, list); - } - - page->mkeys[tmp] = mkey; - ent->mkeys_queue.ci++; - return 0; -} - -static int pop_mkey_locked(struct mlx5_cache_ent *ent) -{ - unsigned long tmp = (ent->mkeys_queue.ci - 1) % NUM_MKEYS_PER_PAGE; - struct mlx5_mkeys_page *last_page; - u32 mkey; - - lockdep_assert_held(&ent->mkeys_queue.lock); - last_page = list_last_entry(&ent->mkeys_queue.pages_list, - struct mlx5_mkeys_page, list); - mkey = last_page->mkeys[tmp]; - last_page->mkeys[tmp] = 0; - ent->mkeys_queue.ci--; - if (ent->mkeys_queue.num_pages > 1 && !tmp) { - list_del(&last_page->list); - ent->mkeys_queue.num_pages--; - kfree(last_page); - } - return mkey; -} - -static void create_mkey_callback(int status, struct mlx5_async_work *context) -{ - struct mlx5r_async_create_mkey *mkey_out = - container_of(context, struct mlx5r_async_create_mkey, cb_work); - struct mlx5_cache_ent *ent = mkey_out->ent; - struct mlx5_ib_dev *dev = ent->dev; - unsigned long flags; - - if (status) { - create_mkey_warn(dev, status, mkey_out->out); - kfree(mkey_out); - spin_lock_irqsave(&ent->mkeys_queue.lock, flags); - ent->pending--; - WRITE_ONCE(dev->fill_delay, 1); - spin_unlock_irqrestore(&ent->mkeys_queue.lock, flags); - mod_timer(&dev->delay_timer, jiffies + HZ); - return; - } - - mkey_out->mkey |= mlx5_idx_to_mkey( - MLX5_GET(create_mkey_out, mkey_out->out, mkey_index)); - WRITE_ONCE(dev->cache.last_add, jiffies); - - spin_lock_irqsave(&ent->mkeys_queue.lock, flags); - push_mkey_locked(ent, mkey_out->mkey); - ent->pending--; - /* If we are doing fill_to_high_water then keep going. */ - queue_adjust_cache_locked(ent); - spin_unlock_irqrestore(&ent->mkeys_queue.lock, flags); - kfree(mkey_out); -} - static int get_mkc_octo_size(unsigned int access_mode, unsigned int ndescs) { int ret = 0; @@ -241,537 +135,6 @@ static int get_mkc_octo_size(unsigned int access_mode, unsigned int ndescs) return ret; } -static void set_cache_mkc(struct mlx5_cache_ent *ent, void *mkc) -{ - set_mkc_access_pd_addr_fields(mkc, ent->rb_key.access_flags, 0, - ent->dev->umrc.pd); - MLX5_SET(mkc, mkc, free, 1); - MLX5_SET(mkc, mkc, umr_en, 1); - MLX5_SET(mkc, mkc, access_mode_1_0, ent->rb_key.access_mode & 0x3); - MLX5_SET(mkc, mkc, access_mode_4_2, - (ent->rb_key.access_mode >> 2) & 0x7); - MLX5_SET(mkc, mkc, ma_translation_mode, !!ent->rb_key.ats); - - MLX5_SET(mkc, mkc, translations_octword_size, - get_mkc_octo_size(ent->rb_key.access_mode, - ent->rb_key.ndescs)); - MLX5_SET(mkc, mkc, log_page_size, PAGE_SHIFT); - - if (ent->rb_key.ph != MLX5_IB_NO_PH) { - MLX5_SET(mkc, mkc, pcie_tph_en, 1); - MLX5_SET(mkc, mkc, pcie_tph_ph, ent->rb_key.ph); - if (ent->rb_key.st_index != MLX5_MKC_PCIE_TPH_NO_STEERING_TAG_INDEX) - MLX5_SET(mkc, mkc, pcie_tph_steering_tag_index, - ent->rb_key.st_index); - } -} - -/* Asynchronously schedule new MRs to be populated in the cache. */ -static int add_keys(struct mlx5_cache_ent *ent, unsigned int num) -{ - struct mlx5r_async_create_mkey *async_create; - void *mkc; - int err = 0; - int i; - - for (i = 0; i < num; i++) { - async_create = kzalloc_obj(struct mlx5r_async_create_mkey); - if (!async_create) - return -ENOMEM; - mkc = MLX5_ADDR_OF(create_mkey_in, async_create->in, - memory_key_mkey_entry); - set_cache_mkc(ent, mkc); - async_create->ent = ent; - - spin_lock_irq(&ent->mkeys_queue.lock); - if (ent->pending >= MAX_PENDING_REG_MR) { - err = -EAGAIN; - goto free_async_create; - } - ent->pending++; - spin_unlock_irq(&ent->mkeys_queue.lock); - - err = mlx5_ib_create_mkey_cb(async_create); - if (err) { - mlx5_ib_warn(ent->dev, "create mkey failed %d\n", err); - goto err_create_mkey; - } - } - - return 0; - -err_create_mkey: - spin_lock_irq(&ent->mkeys_queue.lock); - ent->pending--; -free_async_create: - spin_unlock_irq(&ent->mkeys_queue.lock); - kfree(async_create); - return err; -} - -/* Synchronously create a MR in the cache */ -static int create_cache_mkey(struct mlx5_cache_ent *ent, u32 *mkey) -{ - size_t inlen = MLX5_ST_SZ_BYTES(create_mkey_in); - void *mkc; - u32 *in; - int err; - - in = kzalloc(inlen, GFP_KERNEL); - if (!in) - return -ENOMEM; - mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); - set_cache_mkc(ent, mkc); - - err = mlx5_core_create_mkey(ent->dev->mdev, mkey, in, inlen); - if (err) - goto free_in; - - WRITE_ONCE(ent->dev->cache.last_add, jiffies); -free_in: - kfree(in); - return err; -} - -static void remove_cache_mr_locked(struct mlx5_cache_ent *ent) -{ - u32 mkey; - - lockdep_assert_held(&ent->mkeys_queue.lock); - if (!ent->mkeys_queue.ci) - return; - mkey = pop_mkey_locked(ent); - spin_unlock_irq(&ent->mkeys_queue.lock); - mlx5_core_destroy_mkey(ent->dev->mdev, mkey); - spin_lock_irq(&ent->mkeys_queue.lock); -} - -static int resize_available_mrs(struct mlx5_cache_ent *ent, unsigned int target, - bool limit_fill) - __acquires(&ent->mkeys_queue.lock) __releases(&ent->mkeys_queue.lock) -{ - int err; - - lockdep_assert_held(&ent->mkeys_queue.lock); - - while (true) { - if (limit_fill) - target = ent->limit * 2; - if (target == ent->pending + ent->mkeys_queue.ci) - return 0; - if (target > ent->pending + ent->mkeys_queue.ci) { - u32 todo = target - (ent->pending + ent->mkeys_queue.ci); - - spin_unlock_irq(&ent->mkeys_queue.lock); - err = add_keys(ent, todo); - if (err == -EAGAIN) - usleep_range(3000, 5000); - spin_lock_irq(&ent->mkeys_queue.lock); - if (err) { - if (err != -EAGAIN) - return err; - } else - return 0; - } else { - remove_cache_mr_locked(ent); - } - } -} - -static ssize_t size_write(struct file *filp, const char __user *buf, - size_t count, loff_t *pos) -{ - struct mlx5_cache_ent *ent = filp->private_data; - u32 target; - int err; - - err = kstrtou32_from_user(buf, count, 0, &target); - if (err) - return err; - - /* - * Target is the new value of total_mrs the user requests, however we - * cannot free MRs that are in use. Compute the target value for stored - * mkeys. - */ - spin_lock_irq(&ent->mkeys_queue.lock); - if (target < ent->in_use) { - err = -EINVAL; - goto err_unlock; - } - target = target - ent->in_use; - if (target < ent->limit || target > ent->limit*2) { - err = -EINVAL; - goto err_unlock; - } - err = resize_available_mrs(ent, target, false); - if (err) - goto err_unlock; - spin_unlock_irq(&ent->mkeys_queue.lock); - - return count; - -err_unlock: - spin_unlock_irq(&ent->mkeys_queue.lock); - return err; -} - -static ssize_t size_read(struct file *filp, char __user *buf, size_t count, - loff_t *pos) -{ - struct mlx5_cache_ent *ent = filp->private_data; - char lbuf[20]; - int err; - - err = snprintf(lbuf, sizeof(lbuf), "%ld\n", - ent->mkeys_queue.ci + ent->in_use); - if (err < 0) - return err; - - return simple_read_from_buffer(buf, count, pos, lbuf, err); -} - -static const struct file_operations size_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .write = size_write, - .read = size_read, -}; - -static ssize_t limit_write(struct file *filp, const char __user *buf, - size_t count, loff_t *pos) -{ - struct mlx5_cache_ent *ent = filp->private_data; - u32 var; - int err; - - err = kstrtou32_from_user(buf, count, 0, &var); - if (err) - return err; - - /* - * Upon set we immediately fill the cache to high water mark implied by - * the limit. - */ - spin_lock_irq(&ent->mkeys_queue.lock); - ent->limit = var; - err = resize_available_mrs(ent, 0, true); - spin_unlock_irq(&ent->mkeys_queue.lock); - if (err) - return err; - return count; -} - -static ssize_t limit_read(struct file *filp, char __user *buf, size_t count, - loff_t *pos) -{ - struct mlx5_cache_ent *ent = filp->private_data; - char lbuf[20]; - int err; - - err = snprintf(lbuf, sizeof(lbuf), "%d\n", ent->limit); - if (err < 0) - return err; - - return simple_read_from_buffer(buf, count, pos, lbuf, err); -} - -static const struct file_operations limit_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .write = limit_write, - .read = limit_read, -}; - -static bool someone_adding(struct mlx5_mkey_cache *cache) -{ - struct mlx5_cache_ent *ent; - struct rb_node *node; - bool ret; - - mutex_lock(&cache->rb_lock); - for (node = rb_first(&cache->rb_root); node; node = rb_next(node)) { - ent = rb_entry(node, struct mlx5_cache_ent, node); - spin_lock_irq(&ent->mkeys_queue.lock); - ret = ent->mkeys_queue.ci < ent->limit; - spin_unlock_irq(&ent->mkeys_queue.lock); - if (ret) { - mutex_unlock(&cache->rb_lock); - return true; - } - } - mutex_unlock(&cache->rb_lock); - return false; -} - -/* - * Check if the bucket is outside the high/low water mark and schedule an async - * update. The cache refill has hysteresis, once the low water mark is hit it is - * refilled up to the high mark. - */ -static void queue_adjust_cache_locked(struct mlx5_cache_ent *ent) -{ - lockdep_assert_held(&ent->mkeys_queue.lock); - - if (ent->disabled || READ_ONCE(ent->dev->fill_delay) || ent->is_tmp) - return; - if (ent->mkeys_queue.ci < ent->limit) { - ent->fill_to_high_water = true; - mod_delayed_work(ent->dev->cache.wq, &ent->dwork, 0); - } else if (ent->fill_to_high_water && - ent->mkeys_queue.ci + ent->pending < 2 * ent->limit) { - /* - * Once we start populating due to hitting a low water mark - * continue until we pass the high water mark. - */ - mod_delayed_work(ent->dev->cache.wq, &ent->dwork, 0); - } else if (ent->mkeys_queue.ci == 2 * ent->limit) { - ent->fill_to_high_water = false; - } else if (ent->mkeys_queue.ci > 2 * ent->limit) { - /* Queue deletion of excess entries */ - ent->fill_to_high_water = false; - if (ent->pending) - queue_delayed_work(ent->dev->cache.wq, &ent->dwork, - secs_to_jiffies(1)); - else - mod_delayed_work(ent->dev->cache.wq, &ent->dwork, 0); - } -} - -static void clean_keys(struct mlx5_ib_dev *dev, struct mlx5_cache_ent *ent) -{ - u32 mkey; - - spin_lock_irq(&ent->mkeys_queue.lock); - while (ent->mkeys_queue.ci) { - mkey = pop_mkey_locked(ent); - spin_unlock_irq(&ent->mkeys_queue.lock); - mlx5_core_destroy_mkey(dev->mdev, mkey); - spin_lock_irq(&ent->mkeys_queue.lock); - } - ent->tmp_cleanup_scheduled = false; - spin_unlock_irq(&ent->mkeys_queue.lock); -} - -static void __cache_work_func(struct mlx5_cache_ent *ent) -{ - struct mlx5_ib_dev *dev = ent->dev; - struct mlx5_mkey_cache *cache = &dev->cache; - int err; - - spin_lock_irq(&ent->mkeys_queue.lock); - if (ent->disabled) - goto out; - - if (ent->fill_to_high_water && - ent->mkeys_queue.ci + ent->pending < 2 * ent->limit && - !READ_ONCE(dev->fill_delay)) { - spin_unlock_irq(&ent->mkeys_queue.lock); - err = add_keys(ent, 1); - spin_lock_irq(&ent->mkeys_queue.lock); - if (ent->disabled) - goto out; - if (err) { - /* - * EAGAIN only happens if there are pending MRs, so we - * will be rescheduled when storing them. The only - * failure path here is ENOMEM. - */ - if (err != -EAGAIN) { - mlx5_ib_warn( - dev, - "add keys command failed, err %d\n", - err); - queue_delayed_work(cache->wq, &ent->dwork, - secs_to_jiffies(1)); - } - } - } else if (ent->mkeys_queue.ci > 2 * ent->limit) { - bool need_delay; - - /* - * The remove_cache_mr() logic is performed as garbage - * collection task. Such task is intended to be run when no - * other active processes are running. - * - * The need_resched() will return TRUE if there are user tasks - * to be activated in near future. - * - * In such case, we don't execute remove_cache_mr() and postpone - * the garbage collection work to try to run in next cycle, in - * order to free CPU resources to other tasks. - */ - spin_unlock_irq(&ent->mkeys_queue.lock); - need_delay = need_resched() || someone_adding(cache) || - !time_after(jiffies, - READ_ONCE(cache->last_add) + 300 * HZ); - spin_lock_irq(&ent->mkeys_queue.lock); - if (ent->disabled) - goto out; - if (need_delay) { - queue_delayed_work(cache->wq, &ent->dwork, 300 * HZ); - goto out; - } - remove_cache_mr_locked(ent); - queue_adjust_cache_locked(ent); - } -out: - spin_unlock_irq(&ent->mkeys_queue.lock); -} - -static void delayed_cache_work_func(struct work_struct *work) -{ - struct mlx5_cache_ent *ent; - - ent = container_of(work, struct mlx5_cache_ent, dwork.work); - /* temp entries are never filled, only cleaned */ - if (ent->is_tmp) - clean_keys(ent->dev, ent); - else - __cache_work_func(ent); -} - -static int cache_ent_key_cmp(struct mlx5r_cache_rb_key key1, - struct mlx5r_cache_rb_key key2) -{ - int res; - - res = key1.ats - key2.ats; - if (res) - return res; - - res = key1.access_mode - key2.access_mode; - if (res) - return res; - - res = key1.access_flags - key2.access_flags; - if (res) - return res; - - res = key1.st_index - key2.st_index; - if (res) - return res; - - res = key1.ph - key2.ph; - if (res) - return res; - - /* - * keep ndescs the last in the compare table since the find function - * searches for an exact match on all properties and only closest - * match in size. - */ - return key1.ndescs - key2.ndescs; -} - -static int mlx5_cache_ent_insert(struct mlx5_mkey_cache *cache, - struct mlx5_cache_ent *ent) -{ - struct rb_node **new = &cache->rb_root.rb_node, *parent = NULL; - struct mlx5_cache_ent *cur; - int cmp; - - /* Figure out where to put new node */ - while (*new) { - cur = rb_entry(*new, struct mlx5_cache_ent, node); - parent = *new; - cmp = cache_ent_key_cmp(cur->rb_key, ent->rb_key); - if (cmp > 0) - new = &((*new)->rb_left); - if (cmp < 0) - new = &((*new)->rb_right); - if (cmp == 0) - return -EEXIST; - } - - /* Add new node and rebalance tree. */ - rb_link_node(&ent->node, parent, new); - rb_insert_color(&ent->node, &cache->rb_root); - - return 0; -} - -static struct mlx5_cache_ent * -mkey_cache_ent_from_rb_key(struct mlx5_ib_dev *dev, - struct mlx5r_cache_rb_key rb_key) -{ - struct rb_node *node = dev->cache.rb_root.rb_node; - struct mlx5_cache_ent *cur, *smallest = NULL; - u64 ndescs_limit; - int cmp; - - /* - * Find the smallest ent with order >= requested_order. - */ - while (node) { - cur = rb_entry(node, struct mlx5_cache_ent, node); - cmp = cache_ent_key_cmp(cur->rb_key, rb_key); - if (cmp > 0) { - smallest = cur; - node = node->rb_left; - } - if (cmp < 0) - node = node->rb_right; - if (cmp == 0) - return cur; - } - - /* - * Limit the usage of mkeys larger than twice the required size while - * also allowing the usage of smallest cache entry for small MRs. - */ - ndescs_limit = max_t(u64, rb_key.ndescs * 2, - MLX5_MR_CACHE_PERSISTENT_ENTRY_MIN_DESCS); - - return (smallest && - smallest->rb_key.access_mode == rb_key.access_mode && - smallest->rb_key.access_flags == rb_key.access_flags && - smallest->rb_key.ats == rb_key.ats && - smallest->rb_key.st_index == rb_key.st_index && - smallest->rb_key.ph == rb_key.ph && - smallest->rb_key.ndescs <= ndescs_limit) ? - smallest : - NULL; -} - -static struct mlx5_ib_mr *_mlx5_mr_cache_alloc(struct mlx5_ib_dev *dev, - struct mlx5_cache_ent *ent) -{ - struct mlx5_ib_mr *mr; - int err; - - mr = kzalloc_obj(*mr); - if (!mr) - return ERR_PTR(-ENOMEM); - - spin_lock_irq(&ent->mkeys_queue.lock); - ent->in_use++; - - if (!ent->mkeys_queue.ci) { - queue_adjust_cache_locked(ent); - ent->miss++; - spin_unlock_irq(&ent->mkeys_queue.lock); - err = create_cache_mkey(ent, &mr->mmkey.key); - if (err) { - spin_lock_irq(&ent->mkeys_queue.lock); - ent->in_use--; - spin_unlock_irq(&ent->mkeys_queue.lock); - kfree(mr); - return ERR_PTR(err); - } - } else { - mr->mmkey.key = pop_mkey_locked(ent); - queue_adjust_cache_locked(ent); - spin_unlock_irq(&ent->mkeys_queue.lock); - } - mr->mmkey.cache_ent = ent; - mr->mmkey.type = MLX5_MKEY_MR; - mr->mmkey.rb_key = ent->rb_key; - mr->mmkey.cacheable = true; - init_waitqueue_head(&mr->mmkey.wait); - return mr; -} - static int get_unchangeable_access_flags(struct mlx5_ib_dev *dev, int access_flags) { @@ -796,256 +159,201 @@ static int get_unchangeable_access_flags(struct mlx5_ib_dev *dev, return ret; } +#define MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK 1ULL +#define MLX5_FRMR_POOLS_KEY_VENDOR_KEY_SUPPORTED \ + MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK + +#define MLX5_FRMR_POOLS_KERNEL_KEY_PH_SHIFT 16 +#define MLX5_FRMR_POOLS_KERNEL_KEY_PH_MASK 0xFF0000 +#define MLX5_FRMR_POOLS_KERNEL_KEY_ST_INDEX_MASK 0xFFFF + +static struct mlx5_ib_mr * +_mlx5_frmr_pool_alloc(struct mlx5_ib_dev *dev, struct ib_umem *umem, + int access_flags, int access_mode, + unsigned long page_size, u16 st_index, u8 ph) +{ + struct mlx5_ib_mr *mr; + int err; + + mr = kzalloc_obj(*mr); + if (!mr) + return ERR_PTR(-ENOMEM); + + mr->ibmr.frmr.key.ats = mlx5_umem_needs_ats(dev, umem, access_flags); + mr->ibmr.frmr.key.access_flags = + get_unchangeable_access_flags(dev, access_flags); + mr->ibmr.frmr.key.num_dma_blocks = + ib_umem_num_dma_blocks(umem, page_size); + mr->ibmr.frmr.key.vendor_key = + access_mode == MLX5_MKC_ACCESS_MODE_KSM ? + MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK : + 0; + + /* Normalize ph: swap 0 and MLX5_IB_NO_PH */ + if (ph == MLX5_IB_NO_PH || ph == 0) + ph ^= MLX5_IB_NO_PH; + + mr->ibmr.frmr.key.kernel_vendor_key = + st_index | (ph << MLX5_FRMR_POOLS_KERNEL_KEY_PH_SHIFT); + err = ib_frmr_pool_pop(&dev->ib_dev, &mr->ibmr); + if (err) { + kfree(mr); + return ERR_PTR(err); + } + mr->mmkey.key = mr->ibmr.frmr.handle; + init_waitqueue_head(&mr->mmkey.wait); + + return mr; +} + struct mlx5_ib_mr *mlx5_mr_cache_alloc(struct mlx5_ib_dev *dev, int access_flags, int access_mode, int ndescs) { - struct mlx5r_cache_rb_key rb_key = { - .ndescs = ndescs, - .access_mode = access_mode, - .access_flags = get_unchangeable_access_flags(dev, access_flags), - .ph = MLX5_IB_NO_PH, + struct ib_frmr_key key = { + .access_flags = + get_unchangeable_access_flags(dev, access_flags), + .vendor_key = access_mode == MLX5_MKC_ACCESS_MODE_MTT ? + 0 : + MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK, + .num_dma_blocks = ndescs, + .kernel_vendor_key = 0, /* no PH and no ST index */ }; - struct mlx5_cache_ent *ent = mkey_cache_ent_from_rb_key(dev, rb_key); - - if (!ent) - return ERR_PTR(-EOPNOTSUPP); - - return _mlx5_mr_cache_alloc(dev, ent); -} - -static void mlx5_mkey_cache_debugfs_cleanup(struct mlx5_ib_dev *dev) -{ - if (!mlx5_debugfs_root || dev->is_rep) - return; - - debugfs_remove_recursive(dev->cache.fs_root); - dev->cache.fs_root = NULL; -} - -static void mlx5_mkey_cache_debugfs_add_ent(struct mlx5_ib_dev *dev, - struct mlx5_cache_ent *ent) -{ - int order = order_base_2(ent->rb_key.ndescs); - struct dentry *dir; - - if (!mlx5_debugfs_root || dev->is_rep) - return; - - if (ent->rb_key.access_mode == MLX5_MKC_ACCESS_MODE_KSM) - order = MLX5_IMR_KSM_CACHE_ENTRY + 2; - - sprintf(ent->name, "%d", order); - dir = debugfs_create_dir(ent->name, dev->cache.fs_root); - debugfs_create_file("size", 0600, dir, ent, &size_fops); - debugfs_create_file("limit", 0600, dir, ent, &limit_fops); - debugfs_create_ulong("cur", 0400, dir, &ent->mkeys_queue.ci); - debugfs_create_u32("miss", 0600, dir, &ent->miss); -} - -static void mlx5_mkey_cache_debugfs_init(struct mlx5_ib_dev *dev) -{ - struct dentry *dbg_root = mlx5_debugfs_get_dev_root(dev->mdev); - struct mlx5_mkey_cache *cache = &dev->cache; - - if (!mlx5_debugfs_root || dev->is_rep) - return; - - cache->fs_root = debugfs_create_dir("mr_cache", dbg_root); -} - -static void delay_time_func(struct timer_list *t) -{ - struct mlx5_ib_dev *dev = timer_container_of(dev, t, delay_timer); - - WRITE_ONCE(dev->fill_delay, 0); -} - -static int mlx5r_mkeys_init(struct mlx5_cache_ent *ent) -{ - struct mlx5_mkeys_page *page; - - page = kzalloc_obj(*page); - if (!page) - return -ENOMEM; - INIT_LIST_HEAD(&ent->mkeys_queue.pages_list); - spin_lock_init(&ent->mkeys_queue.lock); - list_add_tail(&page->list, &ent->mkeys_queue.pages_list); - ent->mkeys_queue.num_pages++; - return 0; -} - -static void mlx5r_mkeys_uninit(struct mlx5_cache_ent *ent) -{ - struct mlx5_mkeys_page *page; - - WARN_ON(ent->mkeys_queue.ci || ent->mkeys_queue.num_pages > 1); - page = list_last_entry(&ent->mkeys_queue.pages_list, - struct mlx5_mkeys_page, list); - list_del(&page->list); - kfree(page); -} - -struct mlx5_cache_ent * -mlx5r_cache_create_ent_locked(struct mlx5_ib_dev *dev, - struct mlx5r_cache_rb_key rb_key, - bool persistent_entry) -{ - struct mlx5_cache_ent *ent; - int order; + struct mlx5_ib_mr *mr; int ret; - ent = kzalloc_obj(*ent); - if (!ent) + mr = kzalloc_obj(*mr); + if (!mr) return ERR_PTR(-ENOMEM); - ret = mlx5r_mkeys_init(ent); - if (ret) - goto mkeys_err; - ent->rb_key = rb_key; - ent->dev = dev; - ent->is_tmp = !persistent_entry; + init_waitqueue_head(&mr->mmkey.wait); - INIT_DELAYED_WORK(&ent->dwork, delayed_cache_work_func); - - ret = mlx5_cache_ent_insert(&dev->cache, ent); - if (ret) - goto ent_insert_err; - - if (persistent_entry) { - if (rb_key.access_mode == MLX5_MKC_ACCESS_MODE_KSM) - order = MLX5_IMR_KSM_CACHE_ENTRY; - else - order = order_base_2(rb_key.ndescs) - 2; - - if ((dev->mdev->profile.mask & MLX5_PROF_MASK_MR_CACHE) && - !dev->is_rep && mlx5_core_is_pf(dev->mdev) && - mlx5r_umr_can_load_pas(dev, 0)) - ent->limit = dev->mdev->profile.mr_cache[order].limit; - else - ent->limit = 0; - - mlx5_mkey_cache_debugfs_add_ent(dev, ent); + mr->ibmr.frmr.key = key; + ret = ib_frmr_pool_pop(&dev->ib_dev, &mr->ibmr); + if (ret) { + kfree(mr); + return ERR_PTR(ret); } + mr->mmkey.key = mr->ibmr.frmr.handle; + mr->mmkey.type = MLX5_MKEY_MR; - return ent; -ent_insert_err: - mlx5r_mkeys_uninit(ent); -mkeys_err: - kfree(ent); - return ERR_PTR(ret); + return mr; } -static void mlx5r_destroy_cache_entries(struct mlx5_ib_dev *dev) +static int mlx5r_create_mkeys(struct ib_device *device, struct ib_frmr_key *key, + u32 *handles, unsigned int count) { - struct rb_root *root = &dev->cache.rb_root; - struct mlx5_cache_ent *ent; - struct rb_node *node; + int access_mode = + key->vendor_key & MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK ? + MLX5_MKC_ACCESS_MODE_KSM : + MLX5_MKC_ACCESS_MODE_MTT; - mutex_lock(&dev->cache.rb_lock); - node = rb_first(root); - while (node) { - ent = rb_entry(node, struct mlx5_cache_ent, node); - node = rb_next(node); - clean_keys(dev, ent); - rb_erase(&ent->node, root); - mlx5r_mkeys_uninit(ent); - kfree(ent); - } - mutex_unlock(&dev->cache.rb_lock); -} + struct mlx5_ib_dev *dev = to_mdev(device); + size_t inlen = MLX5_ST_SZ_BYTES(create_mkey_in); + u16 st_index; + void *mkc; + u32 *in; + int err, i; + u8 ph; -int mlx5_mkey_cache_init(struct mlx5_ib_dev *dev) -{ - struct mlx5_mkey_cache *cache = &dev->cache; - struct rb_root *root = &dev->cache.rb_root; - struct mlx5r_cache_rb_key rb_key = { - .access_mode = MLX5_MKC_ACCESS_MODE_MTT, - .ph = MLX5_IB_NO_PH, - }; - struct mlx5_cache_ent *ent; - struct rb_node *node; - int ret; - int i; - - mutex_init(&dev->slow_path_mutex); - mutex_init(&dev->cache.rb_lock); - dev->cache.rb_root = RB_ROOT; - cache->wq = alloc_ordered_workqueue("mkey_cache", WQ_MEM_RECLAIM); - if (!cache->wq) { - mlx5_ib_warn(dev, "failed to create work queue\n"); + in = kzalloc(inlen, GFP_KERNEL); + if (!in) return -ENOMEM; + mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); + + set_mkc_access_pd_addr_fields(mkc, key->access_flags, 0, dev->umrc.pd); + MLX5_SET(mkc, mkc, free, 1); + MLX5_SET(mkc, mkc, umr_en, 1); + MLX5_SET(mkc, mkc, access_mode_1_0, access_mode & 0x3); + MLX5_SET(mkc, mkc, access_mode_4_2, (access_mode >> 2) & 0x7); + MLX5_SET(mkc, mkc, ma_translation_mode, !!key->ats); + MLX5_SET(mkc, mkc, translations_octword_size, + get_mkc_octo_size(access_mode, key->num_dma_blocks)); + MLX5_SET(mkc, mkc, log_page_size, PAGE_SHIFT); + + st_index = key->kernel_vendor_key & + MLX5_FRMR_POOLS_KERNEL_KEY_ST_INDEX_MASK; + ph = key->kernel_vendor_key & MLX5_FRMR_POOLS_KERNEL_KEY_PH_MASK; + if (ph) { + /* Normalize ph: swap MLX5_IB_NO_PH for 0 */ + if (ph == MLX5_IB_NO_PH) + ph = 0; + MLX5_SET(mkc, mkc, pcie_tph_en, 1); + MLX5_SET(mkc, mkc, pcie_tph_ph, ph); + if (st_index != MLX5_MKC_PCIE_TPH_NO_STEERING_TAG_INDEX) + MLX5_SET(mkc, mkc, pcie_tph_steering_tag_index, + st_index); } - mlx5_cmd_init_async_ctx(dev->mdev, &dev->async_ctx); - timer_setup(&dev->delay_timer, delay_time_func, 0); - mlx5_mkey_cache_debugfs_init(dev); - mutex_lock(&cache->rb_lock); - for (i = 0; i <= mkey_cache_max_order(dev); i++) { - rb_key.ndescs = MLX5_MR_CACHE_PERSISTENT_ENTRY_MIN_DESCS << i; - ent = mlx5r_cache_create_ent_locked(dev, rb_key, true); - if (IS_ERR(ent)) { - ret = PTR_ERR(ent); - goto err; - } + for (i = 0; i < count; i++) { + assign_mkey_variant(dev, handles + i, in); + err = mlx5_core_create_mkey(dev->mdev, handles + i, in, inlen); + if (err) + goto free_in; } +free_in: + kfree(in); + if (err) + for (; i > 0; i--) + mlx5_core_destroy_mkey(dev->mdev, handles[i]); + return err; +} - ret = mlx5_odp_init_mkey_cache(dev); - if (ret) - goto err; +static void mlx5r_destroy_mkeys(struct ib_device *device, u32 *handles, + unsigned int count) +{ + struct mlx5_ib_dev *dev = to_mdev(device); + int i, err; - mutex_unlock(&cache->rb_lock); - for (node = rb_first(root); node; node = rb_next(node)) { - ent = rb_entry(node, struct mlx5_cache_ent, node); - spin_lock_irq(&ent->mkeys_queue.lock); - queue_adjust_cache_locked(ent); - spin_unlock_irq(&ent->mkeys_queue.lock); + for (i = 0; i < count; i++) { + err = mlx5_core_destroy_mkey(dev->mdev, handles[i]); + if (err) + pr_warn_ratelimited( + "mlx5_ib: failed to destroy mkey %d: %d", + handles[i], err); } +} + +static int mlx5r_build_frmr_key(struct ib_device *device, + const struct ib_frmr_key *in, + struct ib_frmr_key *out) +{ + struct mlx5_ib_dev *dev = to_mdev(device); + + /* check HW capabilities of users requested frmr key */ + if ((in->ats && !MLX5_CAP_GEN(dev->mdev, ats)) || + ilog2(in->num_dma_blocks) > mkey_max_umr_order(dev)) + return -EOPNOTSUPP; + + if (in->vendor_key & ~MLX5_FRMR_POOLS_KEY_VENDOR_KEY_SUPPORTED) + return -EOPNOTSUPP; + + out->ats = in->ats; + out->access_flags = + get_unchangeable_access_flags(dev, in->access_flags); + out->vendor_key = in->vendor_key; + out->num_dma_blocks = in->num_dma_blocks; return 0; - -err: - mutex_unlock(&cache->rb_lock); - mlx5_mkey_cache_debugfs_cleanup(dev); - mlx5r_destroy_cache_entries(dev); - destroy_workqueue(cache->wq); - mlx5_ib_warn(dev, "failed to create mkey cache entry\n"); - return ret; } -void mlx5_mkey_cache_cleanup(struct mlx5_ib_dev *dev) +static struct ib_frmr_pool_ops mlx5r_frmr_pool_ops = { + .create_frmrs = mlx5r_create_mkeys, + .destroy_frmrs = mlx5r_destroy_mkeys, + .build_key = mlx5r_build_frmr_key, +}; + +int mlx5r_frmr_pools_init(struct ib_device *device) { - struct rb_root *root = &dev->cache.rb_root; - struct mlx5_cache_ent *ent; - struct rb_node *node; + struct mlx5_ib_dev *dev = to_mdev(device); - if (!dev->cache.wq) - return; + mutex_init(&dev->slow_path_mutex); + return ib_frmr_pools_init(device, &mlx5r_frmr_pool_ops); +} - mutex_lock(&dev->cache.rb_lock); - for (node = rb_first(root); node; node = rb_next(node)) { - ent = rb_entry(node, struct mlx5_cache_ent, node); - spin_lock_irq(&ent->mkeys_queue.lock); - ent->disabled = true; - spin_unlock_irq(&ent->mkeys_queue.lock); - cancel_delayed_work(&ent->dwork); - } - mutex_unlock(&dev->cache.rb_lock); - - /* - * After all entries are disabled and will not reschedule on WQ, - * flush it and all async commands. - */ - flush_workqueue(dev->cache.wq); - - mlx5_mkey_cache_debugfs_cleanup(dev); - mlx5_cmd_cleanup_async_ctx(&dev->async_ctx); - - /* At this point all entries are disabled and have no concurrent work. */ - mlx5r_destroy_cache_entries(dev); - - destroy_workqueue(dev->cache.wq); - timer_delete_sync(&dev->delay_timer); +void mlx5r_frmr_pools_cleanup(struct ib_device *device) +{ + ib_frmr_pools_cleanup(device); } struct ib_mr *mlx5_ib_get_dma_mr(struct ib_pd *pd, int acc) @@ -1107,13 +415,6 @@ static int get_octo_len(u64 addr, u64 len, int page_shift) return (npages + 1) / 2; } -static int mkey_cache_max_order(struct mlx5_ib_dev *dev) -{ - if (MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset)) - return MKEY_CACHE_LAST_STD_ENTRY; - return MLX5_MAX_UMR_SHIFT; -} - static void set_mr_fields(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr, u64 length, int access_flags, u64 iova) { @@ -1142,8 +443,6 @@ static struct mlx5_ib_mr *alloc_cacheable_mr(struct ib_pd *pd, u16 st_index, u8 ph) { struct mlx5_ib_dev *dev = to_mdev(pd->device); - struct mlx5r_cache_rb_key rb_key = {}; - struct mlx5_cache_ent *ent; struct mlx5_ib_mr *mr; unsigned long page_size; @@ -1155,33 +454,12 @@ static struct mlx5_ib_mr *alloc_cacheable_mr(struct ib_pd *pd, if (WARN_ON(!page_size)) return ERR_PTR(-EINVAL); - rb_key.access_mode = access_mode; - rb_key.ndescs = ib_umem_num_dma_blocks(umem, page_size); - rb_key.ats = mlx5_umem_needs_ats(dev, umem, access_flags); - rb_key.access_flags = get_unchangeable_access_flags(dev, access_flags); - rb_key.st_index = st_index; - rb_key.ph = ph; - ent = mkey_cache_ent_from_rb_key(dev, rb_key); - /* - * If the MR can't come from the cache then synchronously create an uncached - * one. - */ - if (!ent) { - mutex_lock(&dev->slow_path_mutex); - mr = reg_create(pd, umem, iova, access_flags, page_size, false, access_mode, - st_index, ph); - mutex_unlock(&dev->slow_path_mutex); - if (IS_ERR(mr)) - return mr; - mr->mmkey.rb_key = rb_key; - mr->mmkey.cacheable = true; - return mr; - } - - mr = _mlx5_mr_cache_alloc(dev, ent); + mr = _mlx5_frmr_pool_alloc(dev, umem, access_flags, access_mode, + page_size, st_index, ph); if (IS_ERR(mr)) return mr; + mr->mmkey.type = MLX5_MKEY_MR; mr->ibmr.pd = pd; mr->umem = umem; mr->page_shift = order_base_2(page_size); @@ -1811,18 +1089,24 @@ static bool can_use_umr_rereg_pas(struct mlx5_ib_mr *mr, unsigned long *page_size) { struct mlx5_ib_dev *dev = to_mdev(mr->ibmr.device); + u8 access_mode; - /* We only track the allocated sizes of MRs from the cache */ - if (!mr->mmkey.cache_ent) + /* We only track the allocated sizes of MRs from the frmr pools */ + if (!mr->ibmr.frmr.pool) return false; if (!mlx5r_umr_can_load_pas(dev, new_umem->length)) return false; - *page_size = mlx5_umem_mkc_find_best_pgsz( - dev, new_umem, iova, mr->mmkey.cache_ent->rb_key.access_mode); + access_mode = mr->ibmr.frmr.key.vendor_key & + MLX5_FRMR_POOLS_KEY_ACCESS_MODE_KSM_MASK ? + MLX5_MKC_ACCESS_MODE_KSM : + MLX5_MKC_ACCESS_MODE_MTT; + + *page_size = + mlx5_umem_mkc_find_best_pgsz(dev, new_umem, iova, access_mode); if (WARN_ON(!*page_size)) return false; - return (mr->mmkey.cache_ent->rb_key.ndescs) >= + return (mr->ibmr.frmr.key.num_dma_blocks) >= ib_umem_num_dma_blocks(new_umem, *page_size); } @@ -1883,7 +1167,8 @@ struct ib_mr *mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, int err; if (!IS_ENABLED(CONFIG_INFINIBAND_USER_MEM) || mr->data_direct || - mr->mmkey.rb_key.ph != MLX5_IB_NO_PH) + (mr->ibmr.frmr.key.kernel_vendor_key & + MLX5_FRMR_POOLS_KERNEL_KEY_PH_MASK) != 0) return ERR_PTR(-EOPNOTSUPP); mlx5_ib_dbg( @@ -2024,47 +1309,6 @@ mlx5_free_priv_descs(struct mlx5_ib_mr *mr) } } -static int cache_ent_find_and_store(struct mlx5_ib_dev *dev, - struct mlx5_ib_mr *mr) -{ - struct mlx5_mkey_cache *cache = &dev->cache; - struct mlx5_cache_ent *ent; - int ret; - - if (mr->mmkey.cache_ent) { - spin_lock_irq(&mr->mmkey.cache_ent->mkeys_queue.lock); - goto end; - } - - mutex_lock(&cache->rb_lock); - ent = mkey_cache_ent_from_rb_key(dev, mr->mmkey.rb_key); - if (ent) { - if (ent->rb_key.ndescs == mr->mmkey.rb_key.ndescs) { - if (ent->disabled) { - mutex_unlock(&cache->rb_lock); - return -EOPNOTSUPP; - } - mr->mmkey.cache_ent = ent; - spin_lock_irq(&mr->mmkey.cache_ent->mkeys_queue.lock); - mutex_unlock(&cache->rb_lock); - goto end; - } - } - - ent = mlx5r_cache_create_ent_locked(dev, mr->mmkey.rb_key, false); - mutex_unlock(&cache->rb_lock); - if (IS_ERR(ent)) - return PTR_ERR(ent); - - mr->mmkey.cache_ent = ent; - spin_lock_irq(&mr->mmkey.cache_ent->mkeys_queue.lock); - -end: - ret = push_mkey_locked(mr->mmkey.cache_ent, mr->mmkey.key); - spin_unlock_irq(&mr->mmkey.cache_ent->mkeys_queue.lock); - return ret; -} - static int mlx5_ib_revoke_data_direct_mr(struct mlx5_ib_mr *mr) { struct mlx5_ib_dev *dev = to_mdev(mr->ibmr.device); @@ -2130,33 +1374,12 @@ static int mlx5r_handle_mkey_cleanup(struct mlx5_ib_mr *mr) bool is_odp_dma_buf = is_dmabuf_mr(mr) && !to_ib_umem_dmabuf(mr->umem)->pinned; struct mlx5_ib_dev *dev = to_mdev(mr->ibmr.device); - struct mlx5_cache_ent *ent = mr->mmkey.cache_ent; bool is_odp = is_odp_mr(mr); - bool from_cache = !!ent; int ret; - if (mr->mmkey.cacheable && !mlx5_umr_revoke_mr_with_lock(mr) && - !cache_ent_find_and_store(dev, mr)) { - ent = mr->mmkey.cache_ent; - /* upon storing to a clean temp entry - schedule its cleanup */ - spin_lock_irq(&ent->mkeys_queue.lock); - if (from_cache) - ent->in_use--; - if (ent->is_tmp && !ent->tmp_cleanup_scheduled) { - mod_delayed_work(ent->dev->cache.wq, &ent->dwork, - secs_to_jiffies(30)); - ent->tmp_cleanup_scheduled = true; - } - spin_unlock_irq(&ent->mkeys_queue.lock); + if (mr->ibmr.frmr.pool && !mlx5_umr_revoke_mr_with_lock(mr) && + !ib_frmr_pool_push(mr->ibmr.device, &mr->ibmr)) return 0; - } - - if (ent) { - spin_lock_irq(&ent->mkeys_queue.lock); - ent->in_use--; - mr->mmkey.cache_ent = NULL; - spin_unlock_irq(&ent->mkeys_queue.lock); - } if (is_odp) mutex_lock(&to_ib_umem_odp(mr->umem)->umem_mutex); @@ -2240,7 +1463,7 @@ static int __mlx5_ib_dereg_mr(struct ib_mr *ibmr) mlx5_ib_free_odp_mr(mr); } - if (!mr->mmkey.cache_ent) + if (!mr->ibmr.frmr.pool) mlx5_free_priv_descs(mr); kfree(mr); @@ -2549,16 +1772,13 @@ int mlx5_ib_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata) __u32 response_length; } resp = {}; - err = ib_copy_from_udata(&req, udata, min(udata->inlen, sizeof(req))); - if (err) - return err; + if (udata->inlen) { + err = ib_copy_validate_udata_in_cm(udata, req, reserved2, 0); + if (err) + return err; + } - if (req.comp_mask || req.reserved1 || req.reserved2) - return -EOPNOTSUPP; - - if (udata->inlen > sizeof(req) && - !ib_is_udata_cleared(udata, sizeof(req), - udata->inlen - sizeof(req))) + if (req.reserved1 || req.reserved2) return -EOPNOTSUPP; ndescs = req.num_klms ? roundup(req.num_klms, 4) : roundup(1, 4); diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 4c1dea4fd526..1119ce163ea7 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -1875,25 +1875,6 @@ mlx5_ib_odp_destroy_eq(struct mlx5_ib_dev *dev, struct mlx5_ib_pf_eq *eq) return err; } -int mlx5_odp_init_mkey_cache(struct mlx5_ib_dev *dev) -{ - struct mlx5r_cache_rb_key rb_key = { - .access_mode = MLX5_MKC_ACCESS_MODE_KSM, - .ndescs = mlx5_imr_ksm_entries, - .ph = MLX5_IB_NO_PH, - }; - struct mlx5_cache_ent *ent; - - if (!(dev->odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT)) - return 0; - - ent = mlx5r_cache_create_ent_locked(dev, rb_key, true); - if (IS_ERR(ent)) - return PTR_ERR(ent); - - return 0; -} - static const struct ib_device_ops mlx5_ib_dev_odp_ops = { .advise_mr = mlx5_ib_advise_mr, }; diff --git a/drivers/infiniband/hw/mlx5/qos.c b/drivers/infiniband/hw/mlx5/qos.c index dce92554142a..ab7f5db18c93 100644 --- a/drivers/infiniband/hw/mlx5/qos.c +++ b/drivers/infiniband/hw/mlx5/qos.c @@ -45,7 +45,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_PP_OBJ_ALLOC)( return -EINVAL; dev = to_mdev(c->ibucontext.device); - pp_entry = kzalloc(sizeof(*pp_entry), GFP_KERNEL); + pp_entry = kzalloc_obj(*pp_entry); if (!pp_entry) return -ENOMEM; diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 47de8cc9937e..8fd05532c09c 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -1273,7 +1273,7 @@ static int get_ts_format(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq, } return MLX5_TIMESTAMP_FORMAT_REAL_TIME; } - if (cq->create_flags & IB_UVERBS_CQ_FLAGS_TIMESTAMP_COMPLETION) { + if (cq->private_flags & MLX5_IB_CQ_PR_TIMESTAMP_COMPLETION) { if (!fr_sup) { mlx5_ib_dbg(dev, "Free running TS format is not supported\n"); @@ -1603,6 +1603,11 @@ static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, } if (qp->rq.wqe_cnt) { + if (!rq->base.ubuffer.umem) { + err = -EINVAL; + goto err_destroy_sq; + } + rq->base.container_mibqp = qp; if (qp->flags & IB_QP_CREATE_CVLAN_STRIPPING) @@ -4707,20 +4712,12 @@ int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, return -ENOSYS; if (udata && udata->inlen) { - if (udata->inlen < offsetofend(typeof(ucmd), ece_options)) - return -EINVAL; + err = ib_copy_validate_udata_in_cm(udata, ucmd, ece_options, + MLX5_IB_MODIFY_QP_OOO_DP); + if (err) + return err; - if (udata->inlen > sizeof(ucmd) && - !ib_is_udata_cleared(udata, sizeof(ucmd), - udata->inlen - sizeof(ucmd))) - return -EOPNOTSUPP; - - if (ib_copy_from_udata(&ucmd, udata, - min(udata->inlen, sizeof(ucmd)))) - return -EFAULT; - - if (ucmd.comp_mask & ~MLX5_IB_MODIFY_QP_OOO_DP || - memchr_inv(&ucmd.burst_info.reserved, 0, + if (memchr_inv(&ucmd.burst_info.reserved, 0, sizeof(ucmd.burst_info.reserved))) return -EOPNOTSUPP; @@ -5387,33 +5384,18 @@ static int prepare_user_rq(struct ib_pd *pd, struct mlx5_ib_rwq *rwq) { struct mlx5_ib_dev *dev = to_mdev(pd->device); - struct mlx5_ib_create_wq ucmd = {}; + struct mlx5_ib_create_wq ucmd; int err; - size_t required_cmd_sz; - required_cmd_sz = offsetofend(struct mlx5_ib_create_wq, - single_stride_log_num_of_bytes); - if (udata->inlen < required_cmd_sz) { - mlx5_ib_dbg(dev, "invalid inlen\n"); - return -EINVAL; - } - - if (udata->inlen > sizeof(ucmd) && - !ib_is_udata_cleared(udata, sizeof(ucmd), - udata->inlen - sizeof(ucmd))) { - mlx5_ib_dbg(dev, "inlen is not supported\n"); - return -EOPNOTSUPP; - } - - if (ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen))) { + err = ib_copy_validate_udata_in_cm(udata, ucmd, + single_stride_log_num_of_bytes, + MLX5_IB_CREATE_WQ_STRIDING_RQ); + if (err) { mlx5_ib_dbg(dev, "copy failed\n"); - return -EFAULT; + return err; } - if (ucmd.comp_mask & (~MLX5_IB_CREATE_WQ_STRIDING_RQ)) { - mlx5_ib_dbg(dev, "invalid comp mask\n"); - return -EOPNOTSUPP; - } else if (ucmd.comp_mask & MLX5_IB_CREATE_WQ_STRIDING_RQ) { + if (ucmd.comp_mask & MLX5_IB_CREATE_WQ_STRIDING_RQ) { if (!MLX5_CAP_GEN(dev->mdev, striding_rq)) { mlx5_ib_dbg(dev, "Striding RQ is not supported\n"); return -EOPNOTSUPP; @@ -5626,7 +5608,6 @@ int mlx5_ib_modify_wq(struct ib_wq *wq, struct ib_wq_attr *wq_attr, struct mlx5_ib_dev *dev = to_mdev(wq->device); struct mlx5_ib_rwq *rwq = to_mrwq(wq); struct mlx5_ib_modify_wq ucmd = {}; - size_t required_cmd_sz; int curr_wq_state; int wq_state; int inlen; @@ -5634,19 +5615,11 @@ int mlx5_ib_modify_wq(struct ib_wq *wq, struct ib_wq_attr *wq_attr, void *rqc; void *in; - required_cmd_sz = offsetofend(struct mlx5_ib_modify_wq, reserved); - if (udata->inlen < required_cmd_sz) - return -EINVAL; + err = ib_copy_validate_udata_in_cm(udata, ucmd, reserved, 0); + if (err) + return err; - if (udata->inlen > sizeof(ucmd) && - !ib_is_udata_cleared(udata, sizeof(ucmd), - udata->inlen - sizeof(ucmd))) - return -EOPNOTSUPP; - - if (ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen))) - return -EFAULT; - - if (ucmd.comp_mask || ucmd.reserved) + if (ucmd.reserved) return -EOPNOTSUPP; inlen = MLX5_ST_SZ_BYTES(modify_rq_in); diff --git a/drivers/infiniband/hw/mlx5/qpc.c b/drivers/infiniband/hw/mlx5/qpc.c index 146d03ae40bd..a7a4f9420271 100644 --- a/drivers/infiniband/hw/mlx5/qpc.c +++ b/drivers/infiniband/hw/mlx5/qpc.c @@ -314,7 +314,14 @@ destroy: xa_cmpxchg_irq(&table->dct_xa, dct->mqp.qpn, XA_ZERO_ENTRY, dct, 0); return err; } - xa_erase_irq(&table->dct_xa, dct->mqp.qpn); + + /* + * A race can occur where a concurrent create gets the same dctn + * (after hardware released it) and overwrites XA_ZERO_ENTRY with + * its new DCT before we reach here. In that case, we must not erase + * the entry as it now belongs to the new DCT. + */ + xa_cmpxchg_irq(&table->dct_xa, dct->mqp.qpn, XA_ZERO_ENTRY, NULL, 0); return 0; } diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c index 17e018554d81..852f6f502d14 100644 --- a/drivers/infiniband/hw/mlx5/srq.c +++ b/drivers/infiniband/hw/mlx5/srq.c @@ -45,28 +45,19 @@ static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq, struct ib_udata *udata, int buf_size) { struct mlx5_ib_dev *dev = to_mdev(pd->device); - struct mlx5_ib_create_srq ucmd = {}; + struct mlx5_ib_create_srq ucmd; struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context( udata, struct mlx5_ib_ucontext, ibucontext); - size_t ucmdlen; int err; u32 uidx = MLX5_IB_DEFAULT_UIDX; - ucmdlen = min(udata->inlen, sizeof(ucmd)); - - if (ib_copy_from_udata(&ucmd, udata, ucmdlen)) { - mlx5_ib_dbg(dev, "failed copy udata\n"); - return -EFAULT; - } + err = ib_copy_validate_udata_in(udata, ucmd, flags); + if (err) + return err; if (ucmd.reserved0 || ucmd.reserved1) return -EINVAL; - if (udata->inlen > sizeof(ucmd) && - !ib_is_udata_cleared(udata, sizeof(ucmd), - udata->inlen - sizeof(ucmd))) - return -EINVAL; - if (in->type != IB_SRQT_BASIC) { err = get_srq_user_index(ucontext, &ucmd, udata->inlen, &uidx); if (err) diff --git a/drivers/infiniband/hw/mlx5/srq_cmd.c b/drivers/infiniband/hw/mlx5/srq_cmd.c index 8b3385396599..c1a088120915 100644 --- a/drivers/infiniband/hw/mlx5/srq_cmd.c +++ b/drivers/infiniband/hw/mlx5/srq_cmd.c @@ -683,7 +683,14 @@ int mlx5_cmd_destroy_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq) xa_cmpxchg_irq(&table->array, srq->srqn, XA_ZERO_ENTRY, srq, 0); return err; } - xa_erase_irq(&table->array, srq->srqn); + + /* + * A race can occur where a concurrent create gets the same srqn + * (after hardware released it) and overwrites XA_ZERO_ENTRY with + * its new SRQ before we reach here. In that case, we must not erase + * the entry as it now belongs to the new SRQ. + */ + xa_cmpxchg_irq(&table->array, srq->srqn, XA_ZERO_ENTRY, NULL, 0); mlx5_core_res_put(&srq->common); wait_for_completion(&srq->common.free); diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c index 4e562e0dd9e1..f2139474be37 100644 --- a/drivers/infiniband/hw/mlx5/umr.c +++ b/drivers/infiniband/hw/mlx5/umr.c @@ -2,6 +2,7 @@ /* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. */ #include +#include #include "mlx5_ib.h" #include "umr.h" #include "wr.h" @@ -146,7 +147,7 @@ int mlx5r_umr_resource_init(struct mlx5_ib_dev *dev) * UMR qp is set once, never changed until device unload. * Avoid taking the mutex if initialization is already done. */ - if (dev->umrc.qp) + if (smp_load_acquire(&dev->umrc.qp)) return 0; mutex_lock(&dev->umrc.init_lock); @@ -184,7 +185,7 @@ int mlx5r_umr_resource_init(struct mlx5_ib_dev *dev) sema_init(&dev->umrc.sem, MAX_UMR_WR); mutex_init(&dev->umrc.lock); dev->umrc.state = MLX5_UMR_STATE_ACTIVE; - dev->umrc.qp = qp; + smp_store_release(&dev->umrc.qp, qp); mutex_unlock(&dev->umrc.init_lock); return 0; diff --git a/drivers/infiniband/hw/mlx5/umr.h b/drivers/infiniband/hw/mlx5/umr.h index e9361f0140e7..7eeaf6a94c97 100644 --- a/drivers/infiniband/hw/mlx5/umr.h +++ b/drivers/infiniband/hw/mlx5/umr.h @@ -9,6 +9,7 @@ #define MLX5_MAX_UMR_SHIFT 16 #define MLX5_MAX_UMR_PAGES (1 << MLX5_MAX_UMR_SHIFT) +#define MLX5_MAX_UMR_EXTENDED_SHIFT 43 #define MLX5_IB_UMR_OCTOWORD 16 #define MLX5_IB_UMR_XLT_ALIGNMENT 64 diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 5c182ae4fc2f..e8d5d865c1f1 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -35,8 +35,8 @@ */ #include -#include #include +#include #include #include @@ -402,8 +402,9 @@ static int mthca_create_srq(struct ib_srq *ibsrq, return -EOPNOTSUPP; if (udata) { - if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) - return -EFAULT; + err = ib_copy_validate_udata_in(udata, ucmd, db_page); + if (err) + return err; err = mthca_map_user_db(to_mdev(ibsrq->device), &context->uar, context->db_tab, ucmd.db_index, @@ -472,8 +473,9 @@ static int mthca_create_qp(struct ib_qp *ibqp, case IB_QPT_UD: { if (udata) { - if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) - return -EFAULT; + err = ib_copy_validate_udata_in(udata, ucmd, rq_db_index); + if (err) + return err; err = mthca_map_user_db(dev, &context->uar, context->db_tab, @@ -594,8 +596,9 @@ static int mthca_create_cq(struct ib_cq *ibcq, return -EINVAL; if (udata) { - if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) - return -EFAULT; + err = ib_copy_validate_udata_in(udata, ucmd, set_db_index); + if (err) + return err; err = mthca_map_user_db(to_mdev(ibdev), &context->uar, context->db_tab, ucmd.set_db_index, @@ -695,7 +698,8 @@ unlock: return 0; } -static int mthca_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata) +static int mthca_resize_cq(struct ib_cq *ibcq, unsigned int entries, + struct ib_udata *udata) { struct mthca_dev *dev = to_mdev(ibcq->device); struct mthca_cq *cq = to_mcq(ibcq); @@ -703,7 +707,7 @@ static int mthca_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *uda u32 lkey; int ret; - if (entries < 1 || entries > dev->limits.max_cqes) + if (entries > dev->limits.max_cqes) return -EINVAL; mutex_lock(&cq->mutex); @@ -720,10 +724,9 @@ static int mthca_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *uda goto out; lkey = cq->resize_buf->buf.mr.ibmr.lkey; } else { - if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) { - ret = -EFAULT; + ret = ib_copy_validate_udata_in(udata, ucmd, reserved); + if (ret) goto out; - } lkey = ucmd.lkey; } @@ -851,8 +854,11 @@ static struct ib_mr *mthca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, } ++context->reg_mr_warned; ucmd.mr_attrs = 0; - } else if (ib_copy_from_udata(&ucmd, udata, sizeof ucmd)) - return ERR_PTR(-EFAULT); + } else { + err = ib_copy_validate_udata_in(udata, ucmd, reserved); + if (err) + return ERR_PTR(err); + } mr = kmalloc_obj(*mr); if (!mr) @@ -1096,7 +1102,7 @@ static const struct ib_device_ops mthca_dev_ops = { .query_port = mthca_query_port, .query_qp = mthca_query_qp, .reg_user_mr = mthca_reg_user_mr, - .resize_cq = mthca_resize_cq, + .resize_user_cq = mthca_resize_cq, INIT_RDMA_OBJ_SIZE(ib_ah, mthca_ah, ibah), INIT_RDMA_OBJ_SIZE(ib_cq, mthca_cq, ibcq), diff --git a/drivers/infiniband/hw/ocrdma/ocrdma.h b/drivers/infiniband/hw/ocrdma/ocrdma.h index 5584b781e2e8..da2deae6857b 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma.h +++ b/drivers/infiniband/hw/ocrdma/ocrdma.h @@ -190,8 +190,8 @@ struct ocrdma_mr { struct ib_mr ibmr; struct ib_umem *umem; struct ocrdma_hw_mr hwmr; - u64 *pages; u32 npages; + u64 pages[]; }; struct ocrdma_stats { diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c index b62a9bf160c5..a44874847208 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c @@ -166,7 +166,7 @@ static const struct ib_device_ops ocrdma_dev_ops = { .query_qp = ocrdma_query_qp, .reg_user_mr = ocrdma_reg_user_mr, .req_notify_cq = ocrdma_arm_cq, - .resize_cq = ocrdma_resize_cq, + .resize_user_cq = ocrdma_resize_cq, INIT_RDMA_OBJ_SIZE(ib_ah, ocrdma_ah, ibah), INIT_RDMA_OBJ_SIZE(ib_cq, ocrdma_cq, ibcq), diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index e89be2fbd5eb..a88cc5d84af8 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -45,9 +45,9 @@ #include #include #include -#include #include #include +#include #include #include "ocrdma.h" @@ -215,7 +215,7 @@ static void ocrdma_del_mmap(struct ocrdma_ucontext *uctx, u64 phy_addr, mutex_lock(&uctx->mm_list_lock); list_for_each_entry_safe(mm, tmp, &uctx->mm_head, entry) { - if (len != mm->key.len && phy_addr != mm->key.phy_addr) + if (len != mm->key.len || phy_addr != mm->key.phy_addr) continue; list_del(&mm->entry); @@ -233,7 +233,7 @@ static bool ocrdma_search_mmap(struct ocrdma_ucontext *uctx, u64 phy_addr, mutex_lock(&uctx->mm_list_lock); list_for_each_entry(mm, &uctx->mm_head, entry) { - if (len != mm->key.len && phy_addr != mm->key.phy_addr) + if (len != mm->key.len || phy_addr != mm->key.phy_addr) continue; found = true; @@ -620,9 +620,9 @@ static int ocrdma_copy_pd_uresp(struct ocrdma_dev *dev, struct ocrdma_pd *pd, ucopy_err: if (pd->dpp_enabled) - ocrdma_del_mmap(pd->uctx, dpp_page_addr, PAGE_SIZE); + ocrdma_del_mmap(uctx, dpp_page_addr, PAGE_SIZE); dpp_map_err: - ocrdma_del_mmap(pd->uctx, db_page_addr, db_page_size); + ocrdma_del_mmap(uctx, db_page_addr, db_page_size); return status; } @@ -794,7 +794,7 @@ static int ocrdma_build_pbl_tbl(struct ocrdma_dev *dev, struct ocrdma_hw_mr *mr) void *va; dma_addr_t pa; - mr->pbl_table = kzalloc_objs(struct ocrdma_pbl, mr->num_pbls); + mr->pbl_table = kzalloc_objs(*mr->pbl_table, mr->num_pbls); if (!mr->pbl_table) return -ENOMEM; @@ -910,7 +910,6 @@ int ocrdma_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata) (void) ocrdma_mbx_dealloc_lkey(dev, mr->hwmr.fr_mr, mr->hwmr.lkey); - kfree(mr->pages); ocrdma_free_mr_pbl_tbl(dev, &mr->hwmr); /* it could be user registered memory. */ @@ -983,8 +982,9 @@ int ocrdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, return -EOPNOTSUPP; if (udata) { - if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) - return -EFAULT; + status = ib_copy_validate_udata_in(udata, ureq, rsvd); + if (status) + return status; } else ureq.dpp_cq = 0; @@ -1014,18 +1014,16 @@ ctx_err: return status; } -int ocrdma_resize_cq(struct ib_cq *ibcq, int new_cnt, +int ocrdma_resize_cq(struct ib_cq *ibcq, unsigned int new_cnt, struct ib_udata *udata) { - int status = 0; struct ocrdma_cq *cq = get_ocrdma_cq(ibcq); - if (new_cnt < 1 || new_cnt > cq->max_hw_cqe) { - status = -EINVAL; - return status; - } + if (new_cnt > cq->max_hw_cqe) + return -EINVAL; + ibcq->cqe = new_cnt; - return status; + return 0; } static void ocrdma_flush_cq(struct ocrdma_cq *cq) @@ -1253,12 +1251,11 @@ static void ocrdma_set_qp_db(struct ocrdma_dev *dev, struct ocrdma_qp *qp, static int ocrdma_alloc_wr_id_tbl(struct ocrdma_qp *qp) { - qp->wqe_wr_id_tbl = - kzalloc_objs(*(qp->wqe_wr_id_tbl), qp->sq.max_cnt); + qp->wqe_wr_id_tbl = kzalloc_objs(*qp->wqe_wr_id_tbl, qp->sq.max_cnt); if (qp->wqe_wr_id_tbl == NULL) return -ENOMEM; - qp->rqe_wr_id_tbl = - kcalloc(qp->rq.max_cnt, sizeof(u64), GFP_KERNEL); + + qp->rqe_wr_id_tbl = kzalloc_objs(*qp->rqe_wr_id_tbl, qp->rq.max_cnt); if (qp->rqe_wr_id_tbl == NULL) return -ENOMEM; @@ -1311,11 +1308,14 @@ int ocrdma_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, if (status) goto gen_err; - memset(&ureq, 0, sizeof(ureq)); if (udata) { - if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) - return -EFAULT; + status = ib_copy_validate_udata_in(udata, ureq, rsvd1); + if (status) + return status; + } else { + memset(&ureq, 0, sizeof(ureq)); } + ocrdma_set_qp_init_params(qp, pd, attrs); if (udata == NULL) qp->cap_flags |= (OCRDMA_QP_MW_BIND | OCRDMA_QP_LKEY0 | @@ -1788,8 +1788,8 @@ int ocrdma_create_srq(struct ib_srq *ibsrq, struct ib_srq_init_attr *init_attr, return status; if (!udata) { - srq->rqe_wr_id_tbl = kcalloc(srq->rq.max_cnt, sizeof(u64), - GFP_KERNEL); + srq->rqe_wr_id_tbl = + kzalloc_objs(*srq->rqe_wr_id_tbl, srq->rq.max_cnt); if (!srq->rqe_wr_id_tbl) { status = -ENOMEM; goto arm_err; @@ -2909,19 +2909,13 @@ struct ib_mr *ocrdma_alloc_mr(struct ib_pd *ibpd, enum ib_mr_type mr_type, if (max_num_sg > dev->attr.max_pages_per_frmr) return ERR_PTR(-EINVAL); - mr = kzalloc_obj(*mr); + mr = kzalloc_flex(*mr, pages, max_num_sg); if (!mr) return ERR_PTR(-ENOMEM); - mr->pages = kcalloc(max_num_sg, sizeof(u64), GFP_KERNEL); - if (!mr->pages) { - status = -ENOMEM; - goto pl_err; - } - status = ocrdma_get_pbl_info(dev, mr, max_num_sg); if (status) - goto pbl_err; + goto pl_err; mr->hwmr.fr_mr = 1; mr->hwmr.remote_rd = 0; mr->hwmr.remote_wr = 0; @@ -2930,7 +2924,7 @@ struct ib_mr *ocrdma_alloc_mr(struct ib_pd *ibpd, enum ib_mr_type mr_type, mr->hwmr.mw_bind = 0; status = ocrdma_build_pbl_tbl(dev, &mr->hwmr); if (status) - goto pbl_err; + goto pl_err; status = ocrdma_reg_mr(dev, &mr->hwmr, pd->id, 0); if (status) goto mbx_err; @@ -2941,8 +2935,6 @@ struct ib_mr *ocrdma_alloc_mr(struct ib_pd *ibpd, enum ib_mr_type mr_type, return &mr->ibmr; mbx_err: ocrdma_free_mr_pbl_tbl(dev, &mr->hwmr); -pbl_err: - kfree(mr->pages); pl_err: kfree(mr); return ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h index 6c5c3755b8a9..056562d9a01a 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h @@ -71,7 +71,7 @@ int ocrdma_dealloc_pd(struct ib_pd *pd, struct ib_udata *udata); int ocrdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, struct uverbs_attr_bundle *attrs); -int ocrdma_resize_cq(struct ib_cq *, int cqe, struct ib_udata *); +int ocrdma_resize_cq(struct ib_cq *, unsigned int cqe, struct ib_udata *); int ocrdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata); int ocrdma_create_qp(struct ib_qp *qp, struct ib_qp_init_attr *attrs, diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index 33b4a0e6d3a8..679aa6f3a63b 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -39,9 +39,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -264,7 +264,7 @@ int qedr_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata) int rc; struct qedr_ucontext *ctx = get_qedr_ucontext(uctx); struct qedr_alloc_ucontext_resp uresp = {}; - struct qedr_alloc_ucontext_req ureq = {}; + struct qedr_alloc_ucontext_req ureq; struct qedr_dev *dev = get_qedr_dev(ibdev); struct qed_rdma_add_user_out_params oparams; struct qedr_user_mmap_entry *entry; @@ -273,12 +273,9 @@ int qedr_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata) return -EFAULT; if (udata->inlen) { - rc = ib_copy_from_udata(&ureq, udata, - min(sizeof(ureq), udata->inlen)); - if (rc) { - DP_ERR(dev, "Problem copying data from user space\n"); - return -EFAULT; - } + rc = ib_copy_validate_udata_in(udata, ureq, reserved); + if (rc) + return rc; ctx->edpm_mode = !!(ureq.context_flags & QEDR_ALLOC_UCTX_EDPM_MODE); ctx->db_rec = !!(ureq.context_flags & QEDR_ALLOC_UCTX_DB_REC); @@ -916,7 +913,7 @@ int qedr_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, }; struct qedr_dev *dev = get_qedr_dev(ibdev); struct qed_rdma_create_cq_in_params params; - struct qedr_create_cq_ureq ureq = {}; + struct qedr_create_cq_ureq ureq; int vector = attr->comp_vector; int entries = attr->cqe; struct qedr_cq *cq = get_qedr_cq(ibcq); @@ -949,12 +946,9 @@ int qedr_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, db_offset = DB_ADDR_SHIFT(DQ_PWM_OFFSET_UCM_RDMA_CQ_CONS_32BIT); if (udata) { - if (ib_copy_from_udata(&ureq, udata, min(sizeof(ureq), - udata->inlen))) { - DP_ERR(dev, - "create cq: problem copying data from user space\n"); - goto err0; - } + rc = ib_copy_validate_udata_in(udata, ureq, len); + if (rc) + return rc; if (!ureq.len) { DP_ERR(dev, @@ -1547,7 +1541,7 @@ int qedr_create_srq(struct ib_srq *ibsrq, struct ib_srq_init_attr *init_attr, struct qedr_dev *dev = get_qedr_dev(ibsrq->device); struct qed_rdma_create_srq_out_params out_params; struct qedr_pd *pd = get_qedr_pd(ibsrq->pd); - struct qedr_create_srq_ureq ureq = {}; + struct qedr_create_srq_ureq ureq; u64 pbl_base_addr, phy_prod_pair_addr; struct qedr_srq_hwq_info *hw_srq; u32 page_cnt, page_size; @@ -1575,12 +1569,9 @@ int qedr_create_srq(struct ib_srq *ibsrq, struct ib_srq_init_attr *init_attr, hw_srq->max_sges = init_attr->attr.max_sge; if (udata) { - if (ib_copy_from_udata(&ureq, udata, min(sizeof(ureq), - udata->inlen))) { - DP_ERR(dev, - "create srq: problem copying data from user space\n"); - goto err0; - } + rc = ib_copy_validate_udata_in(udata, ureq, srq_len); + if (rc) + return rc; rc = qedr_init_srq_user_params(udata, srq, &ureq, 0); if (rc) @@ -1846,7 +1837,7 @@ static int qedr_create_user_qp(struct qedr_dev *dev, struct qed_rdma_create_qp_in_params in_params; struct qed_rdma_create_qp_out_params out_params; struct qedr_create_qp_uresp uresp = {}; - struct qedr_create_qp_ureq ureq = {}; + struct qedr_create_qp_ureq ureq; int alloc_and_init = rdma_protocol_roce(&dev->ibdev, 1); struct qedr_ucontext *ctx = NULL; struct qedr_pd *pd = NULL; @@ -1860,12 +1851,9 @@ static int qedr_create_user_qp(struct qedr_dev *dev, } if (udata) { - rc = ib_copy_from_udata(&ureq, udata, min(sizeof(ureq), - udata->inlen)); - if (rc) { - DP_ERR(dev, "Problem copying data from user space\n"); + rc = ib_copy_validate_udata_in(udata, ureq, rq_len); + if (rc) return rc; - } } if (qedr_qp_has_sq(qp)) { diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c index 16b269128f52..615de9c4209b 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c @@ -476,7 +476,7 @@ int usnic_ib_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, if (init_attr->create_flags) return -EOPNOTSUPP; - err = ib_copy_from_udata(&cmd, udata, sizeof(cmd)); + err = ib_copy_validate_udata_in(udata, cmd, spec); if (err) { usnic_err("%s: cannot copy udata for create_qp\n", dev_name(&us_ibdev->ib_dev.dev)); diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma.h b/drivers/infiniband/hw/vmw_pvrdma/pvrdma.h index 763ddc6f25d1..23e547d4b3a7 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma.h +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma.h @@ -53,8 +53,8 @@ #include #include #include -#include #include +#include #include #include "pvrdma_ring.h" diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c index b3df6eb9b8ef..bc3adcc1ae67 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c @@ -134,10 +134,9 @@ int pvrdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, cq->is_kernel = !udata; if (!cq->is_kernel) { - if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) { - ret = -EFAULT; + ret = ib_copy_validate_udata_in(udata, ucmd, reserved); + if (ret) goto err_cq; - } cq->umem = ib_umem_get(ibdev, ucmd.buf_addr, ucmd.buf_size, IB_ACCESS_LOCAL_WRITE); diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c index 0864ad25b9d2..64ce5cf5fb96 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c @@ -65,8 +65,7 @@ int pvrdma_page_dir_init(struct pvrdma_dev *dev, struct pvrdma_page_dir *pdir, goto err; pdir->ntables = PVRDMA_PAGE_DIR_TABLE(npages - 1) + 1; - pdir->tables = kcalloc(pdir->ntables, sizeof(*pdir->tables), - GFP_KERNEL); + pdir->tables = kzalloc_objs(*pdir->tables, pdir->ntables); if (!pdir->tables) goto err; diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c index 98b2a0090bf2..16aab967a203 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c @@ -49,6 +49,7 @@ #include #include #include +#include #include "pvrdma.h" @@ -252,10 +253,9 @@ int pvrdma_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init_attr, dev_dbg(&dev->pdev->dev, "create queuepair from user space\n"); - if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) { - ret = -EFAULT; + ret = ib_copy_validate_udata_in(udata, ucmd, qp_addr); + if (ret) goto err_qp; - } /* Userspace supports qpn and qp handles? */ if (dev->dsr_version >= PVRDMA_QPHANDLE_VERSION && diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c index bdc2703532c6..d31fb692fcaa 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c @@ -49,6 +49,7 @@ #include #include #include +#include #include "pvrdma.h" @@ -141,10 +142,9 @@ int pvrdma_create_srq(struct ib_srq *ibsrq, struct ib_srq_init_attr *init_attr, dev_dbg(&dev->pdev->dev, "create shared receive queue from user space\n"); - if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) { - ret = -EFAULT; + ret = ib_copy_validate_udata_in(udata, ucmd, reserved); + if (ret) goto err_srq; - } srq->umem = ib_umem_get(ibsrq->device, ucmd.buf_addr, ucmd.buf_size, 0); if (IS_ERR(srq->umem)) { diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c index bcd43dc30e21..c7c2b41060e5 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c @@ -322,7 +322,7 @@ int pvrdma_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata) uresp.qp_tab_size = vdev->dsr->caps.max_qp; ret = ib_copy_to_udata(udata, &uresp, sizeof(uresp)); if (ret) { - pvrdma_uar_free(vdev, &context->uar); + /* pvrdma_dealloc_ucontext() also frees the UAR */ pvrdma_dealloc_ucontext(&context->ibucontext); return -EFAULT; } diff --git a/drivers/infiniband/sw/rdmavt/cq.c b/drivers/infiniband/sw/rdmavt/cq.c index e7835ca70e2b..30904c6ae852 100644 --- a/drivers/infiniband/sw/rdmavt/cq.c +++ b/drivers/infiniband/sw/rdmavt/cq.c @@ -337,7 +337,7 @@ int rvt_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags notify_flags) * * Return: 0 for success. */ -int rvt_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) +int rvt_resize_cq(struct ib_cq *ibcq, unsigned int cqe, struct ib_udata *udata) { struct rvt_cq *cq = ibcq_to_rvtcq(ibcq); u32 head, tail, n; @@ -349,7 +349,7 @@ int rvt_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) struct rvt_k_cq_wc *k_wc = NULL; struct rvt_k_cq_wc *old_k_wc = NULL; - if (cqe < 1 || cqe > rdi->dparms.props.max_cqe) + if (cqe > rdi->dparms.props.max_cqe) return -EINVAL; /* diff --git a/drivers/infiniband/sw/rdmavt/cq.h b/drivers/infiniband/sw/rdmavt/cq.h index 4028702a7b2f..82c902c98c8e 100644 --- a/drivers/infiniband/sw/rdmavt/cq.h +++ b/drivers/infiniband/sw/rdmavt/cq.h @@ -13,7 +13,7 @@ int rvt_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, struct uverbs_attr_bundle *attrs); int rvt_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata); int rvt_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags notify_flags); -int rvt_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata); +int rvt_resize_cq(struct ib_cq *ibcq, unsigned int cqe, struct ib_udata *udata); int rvt_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry); int rvt_driver_cq_init(void); void rvt_cq_exit(void); diff --git a/drivers/infiniband/sw/rdmavt/mcast.c b/drivers/infiniband/sw/rdmavt/mcast.c index 1fda344d2056..b41fe4c069dd 100644 --- a/drivers/infiniband/sw/rdmavt/mcast.c +++ b/drivers/infiniband/sw/rdmavt/mcast.c @@ -49,7 +49,6 @@ static void rvt_mcast_qp_free(struct rvt_mcast_qp *mqp) { struct rvt_qp *qp = mqp->qp; - /* Notify hfi1_destroy_qp() if it is waiting. */ rvt_put_qp(qp); kfree(mqp); diff --git a/drivers/infiniband/sw/rdmavt/mmap.c b/drivers/infiniband/sw/rdmavt/mmap.c index 46e3b3e0643a..473f464f33fa 100644 --- a/drivers/infiniband/sw/rdmavt/mmap.c +++ b/drivers/infiniband/sw/rdmavt/mmap.c @@ -9,6 +9,11 @@ #include #include "mmap.h" +/* number of reserved mmaps for the driver */ +#define MMAP_RESERVED 256 +/* start point for dynamic offsets */ +#define MMAP_OFFSET_START (MMAP_RESERVED * PAGE_SIZE) + /** * rvt_mmap_init - init link list and lock for mem map * @rdi: rvt dev struct @@ -17,7 +22,7 @@ void rvt_mmap_init(struct rvt_dev_info *rdi) { INIT_LIST_HEAD(&rdi->pending_mmaps); spin_lock_init(&rdi->pending_lock); - rdi->mmap_offset = PAGE_SIZE; + rdi->mmap_offset = MMAP_OFFSET_START; spin_lock_init(&rdi->mmap_offset_lock); } @@ -73,6 +78,13 @@ int rvt_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) struct rvt_mmap_info *ip, *pp; int ret = -EINVAL; + /* call driver if in reserved range */ + if (offset < MMAP_OFFSET_START) { + if (rdi->driver_f.mmap) + return rdi->driver_f.mmap(context, vma); + return -EINVAL; + } + /* * Search the device's list of objects waiting for a mmap call. * Normally, this list is very short since a call to create a @@ -129,9 +141,9 @@ struct rvt_mmap_info *rvt_create_mmap_info(struct rvt_dev_info *rdi, u32 size, spin_lock_irq(&rdi->mmap_offset_lock); if (rdi->mmap_offset == 0) - rdi->mmap_offset = ALIGN(PAGE_SIZE, SHMLBA); + rdi->mmap_offset = MMAP_OFFSET_START; ip->offset = rdi->mmap_offset; - rdi->mmap_offset += ALIGN(size, SHMLBA); + rdi->mmap_offset += PAGE_SIZE; spin_unlock_irq(&rdi->mmap_offset_lock); INIT_LIST_HEAD(&ip->pending_mmaps); @@ -159,9 +171,9 @@ void rvt_update_mmap_info(struct rvt_dev_info *rdi, struct rvt_mmap_info *ip, spin_lock_irq(&rdi->mmap_offset_lock); if (rdi->mmap_offset == 0) - rdi->mmap_offset = PAGE_SIZE; + rdi->mmap_offset = MMAP_OFFSET_START; ip->offset = rdi->mmap_offset; - rdi->mmap_offset += size; + rdi->mmap_offset += PAGE_SIZE; spin_unlock_irq(&rdi->mmap_offset_lock); ip->size = size; diff --git a/drivers/infiniband/sw/rdmavt/qp.c b/drivers/infiniband/sw/rdmavt/qp.c index 3c7ee7ddc5dd..816624e0991a 100644 --- a/drivers/infiniband/sw/rdmavt/qp.c +++ b/drivers/infiniband/sw/rdmavt/qp.c @@ -2705,7 +2705,7 @@ int rvt_qp_iter_next(struct rvt_qp_iter *iter) struct rvt_ibport *rvp; int pidx; - pidx = n % rdi->ibdev.phys_port_cnt; + pidx = n / 2; /* QP0 and QP1 */ rvp = rdi->ports[pidx]; qp = rcu_dereference(rvp->qp[n & 1]); } else { diff --git a/drivers/infiniband/sw/rdmavt/vt.c b/drivers/infiniband/sw/rdmavt/vt.c index 0c28b412d81a..40aa64208364 100644 --- a/drivers/infiniband/sw/rdmavt/vt.c +++ b/drivers/infiniband/sw/rdmavt/vt.c @@ -244,6 +244,10 @@ static int rvt_query_gid(struct ib_device *ibdev, u32 port_num, */ static int rvt_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata) { + struct rvt_dev_info *rdi = ib_to_rvt(uctx->device); + + if (rdi->driver_f.alloc_ucontext) + return rdi->driver_f.alloc_ucontext(uctx, udata); return 0; } @@ -253,6 +257,10 @@ static int rvt_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata) */ static void rvt_dealloc_ucontext(struct ib_ucontext *context) { + struct rvt_dev_info *rdi = ib_to_rvt(context->device); + + if (rdi->driver_f.dealloc_ucontext) + rdi->driver_f.dealloc_ucontext(context); return; } @@ -367,7 +375,7 @@ static const struct ib_device_ops rvt_dev_ops = { .query_srq = rvt_query_srq, .reg_user_mr = rvt_reg_user_mr, .req_notify_cq = rvt_req_notify_cq, - .resize_cq = rvt_resize_cq, + .resize_user_cq = rvt_resize_cq, INIT_RDMA_OBJ_SIZE(ib_ah, rvt_ah, ibah), INIT_RDMA_OBJ_SIZE(ib_cq, rvt_cq, ibcq), diff --git a/drivers/infiniband/sw/rxe/Makefile b/drivers/infiniband/sw/rxe/Makefile index 93134f1d1d0c..3977f4f13258 100644 --- a/drivers/infiniband/sw/rxe/Makefile +++ b/drivers/infiniband/sw/rxe/Makefile @@ -22,6 +22,7 @@ rdma_rxe-y := \ rxe_mcast.o \ rxe_task.o \ rxe_net.o \ - rxe_hw_counters.o + rxe_hw_counters.o \ + rxe_ns.o rdma_rxe-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += rxe_odp.o diff --git a/drivers/infiniband/sw/rxe/rxe.c b/drivers/infiniband/sw/rxe/rxe.c index e891199cbdef..b0714f9abe3d 100644 --- a/drivers/infiniband/sw/rxe/rxe.c +++ b/drivers/infiniband/sw/rxe/rxe.c @@ -8,6 +8,8 @@ #include #include "rxe.h" #include "rxe_loc.h" +#include "rxe_net.h" +#include "rxe_ns.h" MODULE_AUTHOR("Bob Pearson, Frank Zago, John Groves, Kamal Heib"); MODULE_DESCRIPTION("Soft RDMA transport"); @@ -200,6 +202,8 @@ void rxe_set_mtu(struct rxe_dev *rxe, unsigned int ndev_mtu) port->mtu_cap = ib_mtu_enum_to_int(mtu); } +static struct rdma_link_ops rxe_link_ops; + /* called by ifc layer to create new rxe device. * The caller should allocate memory for rxe by calling ib_alloc_device. */ @@ -208,6 +212,7 @@ int rxe_add(struct rxe_dev *rxe, unsigned int mtu, const char *ibdev_name, { rxe_init(rxe, ndev); rxe_set_mtu(rxe, mtu); + rxe->ib_dev.link_ops = &rxe_link_ops; return rxe_register_device(rxe, ibdev_name, ndev); } @@ -231,6 +236,10 @@ static int rxe_newlink(const char *ibdev_name, struct net_device *ndev) goto err; } + err = rxe_net_init(ndev); + if (err) + return err; + err = rxe_net_add(ibdev_name, ndev); if (err) { rxe_err("failed to add %s\n", ndev->name); @@ -240,9 +249,17 @@ err: return err; } +static int rxe_dellink(struct ib_device *dev) +{ + rxe_net_del(dev); + + return 0; +} + static struct rdma_link_ops rxe_link_ops = { .type = "rxe", .newlink = rxe_newlink, + .dellink = rxe_dellink, }; static int __init rxe_module_init(void) @@ -253,15 +270,24 @@ static int __init rxe_module_init(void) if (err) return err; - err = rxe_net_init(); - if (err) { - rxe_destroy_wq(); - return err; - } + err = rxe_namespace_init(); + if (err) + goto err_destroy_wq; + + err = rxe_register_notifier(); + if (err) + goto err_namespace_exit; rdma_link_register(&rxe_link_ops); + pr_info("loaded\n"); return 0; + +err_namespace_exit: + rxe_namespace_exit(); +err_destroy_wq: + rxe_destroy_wq(); + return err; } static void __exit rxe_module_exit(void) @@ -271,6 +297,8 @@ static void __exit rxe_module_exit(void) rxe_net_exit(); rxe_destroy_wq(); + rxe_namespace_exit(); + pr_info("unloaded\n"); } diff --git a/drivers/infiniband/sw/rxe/rxe.h b/drivers/infiniband/sw/rxe/rxe.h index ff8cd53f5f28..c56bae376c7f 100644 --- a/drivers/infiniband/sw/rxe/rxe.h +++ b/drivers/infiniband/sw/rxe/rxe.h @@ -121,4 +121,6 @@ void rxe_port_up(struct rxe_dev *rxe); void rxe_port_down(struct rxe_dev *rxe); void rxe_set_port_state(struct rxe_dev *rxe); +extern struct workqueue_struct *rxe_wq; + #endif /* RXE_H */ diff --git a/drivers/infiniband/sw/rxe/rxe_cq.c b/drivers/infiniband/sw/rxe/rxe_cq.c index fffd144d509e..eaf7802a5cbe 100644 --- a/drivers/infiniband/sw/rxe/rxe_cq.c +++ b/drivers/infiniband/sw/rxe/rxe_cq.c @@ -8,37 +8,6 @@ #include "rxe_loc.h" #include "rxe_queue.h" -int rxe_cq_chk_attr(struct rxe_dev *rxe, struct rxe_cq *cq, - int cqe, int comp_vector) -{ - int count; - - if (cqe <= 0) { - rxe_dbg_dev(rxe, "cqe(%d) <= 0\n", cqe); - goto err1; - } - - if (cqe > rxe->attr.max_cqe) { - rxe_dbg_dev(rxe, "cqe(%d) > max_cqe(%d)\n", - cqe, rxe->attr.max_cqe); - goto err1; - } - - if (cq) { - count = queue_count(cq->queue, QUEUE_TYPE_TO_CLIENT); - if (cqe < count) { - rxe_dbg_cq(cq, "cqe(%d) < current # elements in queue (%d)\n", - cqe, count); - goto err1; - } - } - - return 0; - -err1: - return -EINVAL; -} - int rxe_cq_from_init(struct rxe_dev *rxe, struct rxe_cq *cq, int cqe, int comp_vector, struct ib_udata *udata, struct rxe_create_cq_resp __user *uresp) diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h index 7992290886e1..e095c12699cb 100644 --- a/drivers/infiniband/sw/rxe/rxe_loc.h +++ b/drivers/infiniband/sw/rxe/rxe_loc.h @@ -18,9 +18,6 @@ void rxe_av_fill_ip_info(struct rxe_av *av, struct rdma_ah_attr *attr); struct rxe_av *rxe_get_av(struct rxe_pkt_info *pkt, struct rxe_ah **ahp); /* rxe_cq.c */ -int rxe_cq_chk_attr(struct rxe_dev *rxe, struct rxe_cq *cq, - int cqe, int comp_vector); - int rxe_cq_from_init(struct rxe_dev *rxe, struct rxe_cq *cq, int cqe, int comp_vector, struct ib_udata *udata, struct rxe_create_cq_resp __user *uresp); diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c index cbc646a30003..50a2cb5405e2 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.c +++ b/drivers/infiniband/sw/rxe/rxe_net.c @@ -17,8 +17,11 @@ #include "rxe.h" #include "rxe_net.h" #include "rxe_loc.h" +#include "rxe_ns.h" -static struct rxe_recv_sockets recv_sockets; +#ifndef SK_REF_FOR_TUNNEL +#define SK_REF_FOR_TUNNEL 2 +#endif #ifdef CONFIG_DEBUG_LOCK_ALLOC /* @@ -101,20 +104,20 @@ static inline void rxe_reclassify_recv_socket(struct socket *sock) } static struct dst_entry *rxe_find_route4(struct rxe_qp *qp, + struct net *net, struct net_device *ndev, struct in_addr *saddr, struct in_addr *daddr) { struct rtable *rt; - struct flowi4 fl = { { 0 } }; + struct flowi4 fl = {}; - memset(&fl, 0, sizeof(fl)); fl.flowi4_oif = ndev->ifindex; memcpy(&fl.saddr, saddr, sizeof(*saddr)); memcpy(&fl.daddr, daddr, sizeof(*daddr)); fl.flowi4_proto = IPPROTO_UDP; - rt = ip_route_output_key(&init_net, &fl); + rt = ip_route_output_key(net, &fl); if (IS_ERR(rt)) { rxe_dbg_qp(qp, "no route to %pI4\n", &daddr->s_addr); return NULL; @@ -125,22 +128,20 @@ static struct dst_entry *rxe_find_route4(struct rxe_qp *qp, #if IS_ENABLED(CONFIG_IPV6) static struct dst_entry *rxe_find_route6(struct rxe_qp *qp, + struct net *net, struct net_device *ndev, struct in6_addr *saddr, struct in6_addr *daddr) { struct dst_entry *ndst; - struct flowi6 fl6 = { { 0 } }; + struct flowi6 fl6 = {}; - memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_oif = ndev->ifindex; memcpy(&fl6.saddr, saddr, sizeof(*saddr)); memcpy(&fl6.daddr, daddr, sizeof(*daddr)); fl6.flowi6_proto = IPPROTO_UDP; - ndst = ip6_dst_lookup_flow(sock_net(recv_sockets.sk6->sk), - recv_sockets.sk6->sk, &fl6, - NULL); + ndst = ip6_dst_lookup_flow(net, rxe_ns_pernet_sk6(net), &fl6, NULL); if (IS_ERR(ndst)) { rxe_dbg_qp(qp, "no route to %pI6\n", daddr); return NULL; @@ -160,6 +161,7 @@ put: #else static struct dst_entry *rxe_find_route6(struct rxe_qp *qp, + struct net *net, struct net_device *ndev, struct in6_addr *saddr, struct in6_addr *daddr) @@ -174,6 +176,7 @@ static struct dst_entry *rxe_find_route(struct net_device *ndev, struct rxe_av *av) { struct dst_entry *dst = NULL; + struct net *net; if (qp_type(qp) == IB_QPT_RC) dst = sk_dst_get(qp->sk->sk); @@ -182,20 +185,22 @@ static struct dst_entry *rxe_find_route(struct net_device *ndev, if (dst) dst_release(dst); + net = dev_net(ndev); + if (av->network_type == RXE_NETWORK_TYPE_IPV4) { struct in_addr *saddr; struct in_addr *daddr; saddr = &av->sgid_addr._sockaddr_in.sin_addr; daddr = &av->dgid_addr._sockaddr_in.sin_addr; - dst = rxe_find_route4(qp, ndev, saddr, daddr); + dst = rxe_find_route4(qp, net, ndev, saddr, daddr); } else if (av->network_type == RXE_NETWORK_TYPE_IPV6) { struct in6_addr *saddr6; struct in6_addr *daddr6; saddr6 = &av->sgid_addr._sockaddr_in6.sin6_addr; daddr6 = &av->dgid_addr._sockaddr_in6.sin6_addr; - dst = rxe_find_route6(qp, ndev, saddr6, daddr6); + dst = rxe_find_route6(qp, net, ndev, saddr6, daddr6); #if IS_ENABLED(CONFIG_IPV6) if (dst) qp->dst_cookie = @@ -624,6 +629,43 @@ int rxe_net_add(const char *ibdev_name, struct net_device *ndev) return 0; } +static void rxe_sock_put(struct sock *sk, + void (*set_sk)(struct net *, struct sock *), + struct net *net) +{ + if (refcount_read(&sk->sk_refcnt) > SK_REF_FOR_TUNNEL) { + __sock_put(sk); + } else { + rxe_release_udp_tunnel(sk->sk_socket); + sk = NULL; + set_sk(net, sk); + } +} + +void rxe_net_del(struct ib_device *dev) +{ + struct rxe_dev *rxe = container_of(dev, struct rxe_dev, ib_dev); + struct net_device *ndev; + struct sock *sk; + struct net *net; + + ndev = rxe_ib_device_get_netdev(&rxe->ib_dev); + if (!ndev) + return; + + net = dev_net(ndev); + + sk = rxe_ns_pernet_sk4(net); + if (sk) + rxe_sock_put(sk, rxe_ns_pernet_set_sk4, net); + + sk = rxe_ns_pernet_sk6(net); + if (sk) + rxe_sock_put(sk, rxe_ns_pernet_set_sk6, net); + + dev_put(ndev); +} + static void rxe_port_event(struct rxe_dev *rxe, enum ib_event_type event) { @@ -680,6 +722,7 @@ static int rxe_notify(struct notifier_block *not_blk, switch (event) { case NETDEV_UNREGISTER: ib_unregister_device_queued(&rxe->ib_dev); + rxe_net_del(&rxe->ib_dev); break; case NETDEV_CHANGEMTU: rxe_dbg_dev(rxe, "%s changed mtu to %d\n", ndev->name, ndev->mtu); @@ -709,66 +752,97 @@ static struct notifier_block rxe_net_notifier = { .notifier_call = rxe_notify, }; -static int rxe_net_ipv4_init(void) +static int rxe_net_ipv4_init(struct net *net) { - recv_sockets.sk4 = rxe_setup_udp_tunnel(&init_net, - htons(ROCE_V2_UDP_DPORT), false); - if (IS_ERR(recv_sockets.sk4)) { - recv_sockets.sk4 = NULL; + struct sock *sk; + struct socket *sock; + + sk = rxe_ns_pernet_sk4(net); + if (sk) { + sock_hold(sk); + return 0; + } + + sock = rxe_setup_udp_tunnel(net, htons(ROCE_V2_UDP_DPORT), false); + if (IS_ERR(sock)) { pr_err("Failed to create IPv4 UDP tunnel\n"); return -1; } + rxe_ns_pernet_set_sk4(net, sock->sk); return 0; } -static int rxe_net_ipv6_init(void) +static int rxe_net_ipv6_init(struct net *net) { #if IS_ENABLED(CONFIG_IPV6) + struct sock *sk; + struct socket *sock; - recv_sockets.sk6 = rxe_setup_udp_tunnel(&init_net, - htons(ROCE_V2_UDP_DPORT), true); - if (PTR_ERR(recv_sockets.sk6) == -EAFNOSUPPORT) { - recv_sockets.sk6 = NULL; + sk = rxe_ns_pernet_sk6(net); + if (sk) { + sock_hold(sk); + return 0; + } + + sock = rxe_setup_udp_tunnel(net, htons(ROCE_V2_UDP_DPORT), true); + if (PTR_ERR(sock) == -EAFNOSUPPORT) { pr_warn("IPv6 is not supported, can not create a UDPv6 socket\n"); return 0; } - if (IS_ERR(recv_sockets.sk6)) { - recv_sockets.sk6 = NULL; + if (IS_ERR(sock)) { pr_err("Failed to create IPv6 UDP tunnel\n"); return -1; } + + rxe_ns_pernet_set_sk6(net, sock->sk); + #endif return 0; } +int rxe_register_notifier(void) +{ + int err; + + err = register_netdevice_notifier(&rxe_net_notifier); + if (err) { + pr_err("Failed to register netdev notifier\n"); + return -1; + } + + return 0; +} + void rxe_net_exit(void) { - rxe_release_udp_tunnel(recv_sockets.sk6); - rxe_release_udp_tunnel(recv_sockets.sk4); unregister_netdevice_notifier(&rxe_net_notifier); } -int rxe_net_init(void) +int rxe_net_init(struct net_device *ndev) { + struct net *net; + struct sock *sk; int err; - recv_sockets.sk6 = NULL; + net = dev_net(ndev); - err = rxe_net_ipv4_init(); + err = rxe_net_ipv4_init(net); if (err) return err; - err = rxe_net_ipv6_init(); + + err = rxe_net_ipv6_init(net); if (err) goto err_out; - err = register_netdevice_notifier(&rxe_net_notifier); - if (err) { - pr_err("Failed to register netdev notifier\n"); - goto err_out; - } + return 0; + err_out: - rxe_net_exit(); + /* If ipv6 error, release ipv4 resource */ + sk = rxe_ns_pernet_sk4(net); + if (sk) + rxe_sock_put(sk, rxe_ns_pernet_set_sk4, net); + return err; } diff --git a/drivers/infiniband/sw/rxe/rxe_net.h b/drivers/infiniband/sw/rxe/rxe_net.h index 45d80d00f86b..56249677d692 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.h +++ b/drivers/infiniband/sw/rxe/rxe_net.h @@ -11,14 +11,11 @@ #include #include -struct rxe_recv_sockets { - struct socket *sk4; - struct socket *sk6; -}; - int rxe_net_add(const char *ibdev_name, struct net_device *ndev); +void rxe_net_del(struct ib_device *dev); -int rxe_net_init(void); +int rxe_register_notifier(void); +int rxe_net_init(struct net_device *ndev); void rxe_net_exit(void); #endif /* RXE_NET_H */ diff --git a/drivers/infiniband/sw/rxe/rxe_ns.c b/drivers/infiniband/sw/rxe/rxe_ns.c new file mode 100644 index 000000000000..8b9d734229b2 --- /dev/null +++ b/drivers/infiniband/sw/rxe/rxe_ns.c @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ + +#include +#include +#include +#include +#include +#include +#include + +#include "rxe_ns.h" + +/* + * Per network namespace data + */ +struct rxe_ns_sock { + struct sock __rcu *rxe_sk4; + struct sock __rcu *rxe_sk6; +}; + +/* + * Index to store custom data for each network namespace. + */ +static unsigned int rxe_pernet_id; + +/* + * Called for every existing and added network namespaces + */ +static int rxe_ns_init(struct net *net) +{ + /* defer socket create in the namespace to the first + * device create. + */ + + return 0; +} + +static void rxe_ns_exit(struct net *net) +{ + /* called when the network namespace is removed + */ + struct rxe_ns_sock *ns_sk = net_generic(net, rxe_pernet_id); + struct sock *sk; + + rcu_read_lock(); + sk = rcu_dereference(ns_sk->rxe_sk4); + rcu_read_unlock(); + if (sk) { + rcu_assign_pointer(ns_sk->rxe_sk4, NULL); + udp_tunnel_sock_release(sk->sk_socket); + } + +#if IS_ENABLED(CONFIG_IPV6) + rcu_read_lock(); + sk = rcu_dereference(ns_sk->rxe_sk6); + rcu_read_unlock(); + if (sk) { + rcu_assign_pointer(ns_sk->rxe_sk6, NULL); + udp_tunnel_sock_release(sk->sk_socket); + } +#endif +} + +/* + * callback to make the module network namespace aware + */ +static struct pernet_operations rxe_net_ops = { + .init = rxe_ns_init, + .exit = rxe_ns_exit, + .id = &rxe_pernet_id, + .size = sizeof(struct rxe_ns_sock), +}; + +struct sock *rxe_ns_pernet_sk4(struct net *net) +{ + struct rxe_ns_sock *ns_sk = net_generic(net, rxe_pernet_id); + struct sock *sk; + + rcu_read_lock(); + sk = rcu_dereference(ns_sk->rxe_sk4); + rcu_read_unlock(); + + return sk; +} + +void rxe_ns_pernet_set_sk4(struct net *net, struct sock *sk) +{ + struct rxe_ns_sock *ns_sk = net_generic(net, rxe_pernet_id); + + rcu_assign_pointer(ns_sk->rxe_sk4, sk); + synchronize_rcu(); +} + +#if IS_ENABLED(CONFIG_IPV6) +struct sock *rxe_ns_pernet_sk6(struct net *net) +{ + struct rxe_ns_sock *ns_sk = net_generic(net, rxe_pernet_id); + struct sock *sk; + + rcu_read_lock(); + sk = rcu_dereference(ns_sk->rxe_sk6); + rcu_read_unlock(); + + return sk; +} + +void rxe_ns_pernet_set_sk6(struct net *net, struct sock *sk) +{ + struct rxe_ns_sock *ns_sk = net_generic(net, rxe_pernet_id); + + rcu_assign_pointer(ns_sk->rxe_sk6, sk); + synchronize_rcu(); +} +#endif /* IPV6 */ + +int rxe_namespace_init(void) +{ + return register_pernet_subsys(&rxe_net_ops); +} + +void rxe_namespace_exit(void) +{ + unregister_pernet_subsys(&rxe_net_ops); +} diff --git a/drivers/infiniband/sw/rxe/rxe_ns.h b/drivers/infiniband/sw/rxe/rxe_ns.h new file mode 100644 index 000000000000..4da2709e6b71 --- /dev/null +++ b/drivers/infiniband/sw/rxe/rxe_ns.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ + +#ifndef RXE_NS_H +#define RXE_NS_H + +struct sock *rxe_ns_pernet_sk4(struct net *net); +void rxe_ns_pernet_set_sk4(struct net *net, struct sock *sk); + +#if IS_ENABLED(CONFIG_IPV6) +void rxe_ns_pernet_set_sk6(struct net *net, struct sock *sk); +struct sock *rxe_ns_pernet_sk6(struct net *net); +#else /* IPv6 */ +static inline struct sock *rxe_ns_pernet_sk6(struct net *net) +{ + return NULL; +} + +static inline void rxe_ns_pernet_set_sk6(struct net *net, struct sock *sk) +{ +} +#endif /* IPv6 */ + +int rxe_namespace_init(void); +void rxe_namespace_exit(void); + +#endif /* RXE_NS_H */ diff --git a/drivers/infiniband/sw/rxe/rxe_odp.c b/drivers/infiniband/sw/rxe/rxe_odp.c index bc11b1ec59ac..ff904d5e54a7 100644 --- a/drivers/infiniband/sw/rxe/rxe_odp.c +++ b/drivers/infiniband/sw/rxe/rxe_odp.c @@ -545,7 +545,7 @@ static int rxe_ib_advise_mr_prefetch(struct ib_pd *ibpd, work->frags[i].mr = mr; } - queue_work(system_unbound_wq, &work->work); + queue_work(rxe_wq, &work->work); return 0; diff --git a/drivers/infiniband/sw/rxe/rxe_recv.c b/drivers/infiniband/sw/rxe/rxe_recv.c index 5861e4244049..2d5e701ff961 100644 --- a/drivers/infiniband/sw/rxe/rxe_recv.c +++ b/drivers/infiniband/sw/rxe/rxe_recv.c @@ -330,7 +330,19 @@ void rxe_rcv(struct sk_buff *skb) pkt->qp = NULL; pkt->mask |= rxe_opcode[pkt->opcode].mask; - if (unlikely(skb->len < header_size(pkt))) + /* + * Unknown opcodes have a zero-initialized rxe_opcode[] entry, so + * both mask and length are 0. Reject them before any length math: + * rxe_icrc_hdr() would otherwise compute length - RXE_BTH_BYTES + * and pass the underflowed value to rxe_crc32(), producing an + * out-of-bounds read. + */ + if (unlikely(!rxe_opcode[pkt->opcode].mask || + !rxe_opcode[pkt->opcode].length)) + goto drop; + + if (unlikely(pkt->paylen < header_size(pkt) + bth_pad(pkt) + + RXE_ICRC_SIZE)) goto drop; err = hdr_check(pkt); diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index 711f73e0bbb1..9cb2f6fbf2dd 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -37,6 +37,7 @@ static char *resp_state_name[] = { [RESPST_ERR_MISSING_OPCODE_LAST_D1E] = "ERR_MISSING_OPCODE_LAST_D1E", [RESPST_ERR_TOO_MANY_RDMA_ATM_REQ] = "ERR_TOO_MANY_RDMA_ATM_REQ", [RESPST_ERR_RNR] = "ERR_RNR", + [RESPST_ERR_RKEY_VIOLATION_EVENT] = "ERR_RKEY_VIOLATION_EVENT", [RESPST_ERR_RKEY_VIOLATION] = "ERR_RKEY_VIOLATION", [RESPST_ERR_INVALIDATE_RKEY] = "ERR_INVALIDATE_RKEY_VIOLATION", [RESPST_ERR_LENGTH] = "ERR_LENGTH", @@ -423,6 +424,19 @@ static void qp_resp_from_atmeth(struct rxe_qp *qp, struct rxe_pkt_info *pkt) qp->resp.resid = sizeof(u64); } +/* Transition to an rkey violation state. C9-222.1 requires an async event + * at the responder, but only if the error cannot be attached to an RX WQE. + * WRITE_WITH_IMM is the only op that might have that more precise RX WQE + * to pin the error on. + */ +static enum resp_states get_rkey_violation_state(struct rxe_pkt_info *pkt) +{ + if (pkt->mask & RXE_IMMDT_MASK) + return RESPST_ERR_RKEY_VIOLATION; + + return RESPST_ERR_RKEY_VIOLATION_EVENT; +} + /* resolve the packet rkey to qp->resp.mr or set qp->resp.mr to NULL * if an invalid rkey is received or the rdma length is zero. For middle * or last packets use the stored value of mr. @@ -486,14 +500,14 @@ static enum resp_states check_rkey(struct rxe_qp *qp, mw = rxe_lookup_mw(qp, access, rkey); if (!mw) { rxe_dbg_qp(qp, "no MW matches rkey %#x\n", rkey); - state = RESPST_ERR_RKEY_VIOLATION; + state = get_rkey_violation_state(pkt); goto err; } mr = mw->mr; if (!mr) { rxe_dbg_qp(qp, "MW doesn't have an MR\n"); - state = RESPST_ERR_RKEY_VIOLATION; + state = get_rkey_violation_state(pkt); goto err; } @@ -507,7 +521,7 @@ static enum resp_states check_rkey(struct rxe_qp *qp, mr = lookup_mr(qp->pd, access, rkey, RXE_LOOKUP_REMOTE); if (!mr) { rxe_dbg_qp(qp, "no MR matches rkey %#x\n", rkey); - state = RESPST_ERR_RKEY_VIOLATION; + state = get_rkey_violation_state(pkt); goto err; } } @@ -521,12 +535,24 @@ static enum resp_states check_rkey(struct rxe_qp *qp, } if (mr_check_range(mr, va + qp->resp.offset, resid)) { - state = RESPST_ERR_RKEY_VIOLATION; + state = get_rkey_violation_state(pkt); goto err; } skip_check_range: - if (pkt->mask & (RXE_WRITE_MASK | RXE_ATOMIC_WRITE_MASK)) { + if (pkt->mask & RXE_ATOMIC_WRITE_MASK) { + /* IBA oA19-28: ATOMIC_WRITE payload is exactly 8 bytes. + * Reject any other length before the responder reads + * sizeof(u64) bytes from payload_addr(pkt); a shorter + * payload would read past the logical end of the packet + * into skb->head tailroom. + */ + if (resid != sizeof(u64) || pktlen != sizeof(u64) || + bth_pad(pkt)) { + state = RESPST_ERR_LENGTH; + goto err; + } + } else if (pkt->mask & RXE_WRITE_MASK) { if (resid > mtu) { if (pktlen != mtu || bth_pad(pkt)) { state = RESPST_ERR_LENGTH; @@ -586,7 +612,7 @@ static enum resp_states write_data_in(struct rxe_qp *qp, err = rxe_mr_copy(qp->resp.mr, qp->resp.va + qp->resp.offset, payload_addr(pkt), data_len, RXE_TO_MR_OBJ); if (err) { - rc = RESPST_ERR_RKEY_VIOLATION; + rc = get_rkey_violation_state(pkt); goto out; } @@ -667,7 +693,7 @@ static enum resp_states process_flush(struct rxe_qp *qp, if (res->flush.type & IB_FLUSH_PERSISTENT) { if (rxe_flush_pmem_iova(mr, start, length)) - return RESPST_ERR_RKEY_VIOLATION; + return get_rkey_violation_state(pkt); /* Make data persistent. */ wmb(); } else if (res->flush.type & IB_FLUSH_GLOBAL) { @@ -1383,6 +1409,20 @@ out: return rc; } +static void do_qp_event(struct rxe_qp *qp, enum ib_event_type etype) +{ + struct ib_event event; + struct ib_qp *ibqp = &qp->ibqp; + + event.event = etype; + event.device = ibqp->device; + event.element.qp = ibqp; + if (ibqp->event_handler) { + rxe_dbg_qp(qp, "reporting QP event %d\n", etype); + ibqp->event_handler(&event, ibqp->qp_context); + } +} + /* Process a class A or C. Both are treated the same in this implementation. */ static void do_class_ac_error(struct rxe_qp *qp, u8 syndrome, enum ib_wc_status status) @@ -1476,14 +1516,9 @@ static void flush_recv_queue(struct rxe_qp *qp, bool notify) int err; if (qp->srq) { - if (notify && qp->ibqp.event_handler) { - struct ib_event ev; + if (notify && qp->ibqp.event_handler) + do_qp_event(qp, IB_EVENT_QP_LAST_WQE_REACHED); - ev.device = qp->ibqp.device; - ev.element.qp = &qp->ibqp; - ev.event = IB_EVENT_QP_LAST_WQE_REACHED; - qp->ibqp.event_handler(&ev, qp->ibqp.qp_context); - } return; } @@ -1613,6 +1648,13 @@ int rxe_receiver(struct rxe_qp *qp) state = RESPST_CLEANUP; break; + case RESPST_ERR_RKEY_VIOLATION_EVENT: + if (qp_type(qp) == IB_QPT_RC) + do_qp_event(qp, IB_EVENT_QP_ACCESS_ERR); + + state = RESPST_ERR_RKEY_VIOLATION; + break; + case RESPST_ERR_RKEY_VIOLATION: if (qp_type(qp) == IB_QPT_RC) { /* Class C */ diff --git a/drivers/infiniband/sw/rxe/rxe_task.c b/drivers/infiniband/sw/rxe/rxe_task.c index f522820b950c..801d06c969c9 100644 --- a/drivers/infiniband/sw/rxe/rxe_task.c +++ b/drivers/infiniband/sw/rxe/rxe_task.c @@ -6,7 +6,7 @@ #include "rxe.h" -static struct workqueue_struct *rxe_wq; +struct workqueue_struct *rxe_wq; int rxe_alloc_wq(void) { diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index fe41362c5144..4d4891dc2884 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -452,18 +452,9 @@ static int rxe_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr, int err; if (udata) { - if (udata->inlen < sizeof(cmd)) { - err = -EINVAL; - rxe_dbg_srq(srq, "malformed udata\n"); + err = ib_copy_validate_udata_in(udata, cmd, mmap_info_addr); + if (err) goto err_out; - } - - err = ib_copy_from_udata(&cmd, udata, sizeof(cmd)); - if (err) { - err = -EFAULT; - rxe_dbg_srq(srq, "unable to read udata\n"); - goto err_out; - } } err = rxe_srq_chk_attr(rxe, srq, attr, mask); @@ -1097,11 +1088,8 @@ static int rxe_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, goto err_out; } - err = rxe_cq_chk_attr(rxe, NULL, attr->cqe, attr->comp_vector); - if (err) { - rxe_dbg_dev(rxe, "bad init attributes, err = %d\n", err); - goto err_out; - } + if (attr->cqe > rxe->attr.max_cqe) + return -EINVAL; err = rxe_add_to_pool(&rxe->cq_pool, cq); if (err) { @@ -1127,7 +1115,8 @@ err_out: return err; } -static int rxe_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) +static int rxe_resize_cq(struct ib_cq *ibcq, unsigned int cqe, + struct ib_udata *udata) { struct rxe_cq *cq = to_rcq(ibcq); struct rxe_dev *rxe = to_rdev(ibcq->device); @@ -1143,11 +1132,9 @@ static int rxe_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) uresp = udata->outbuf; } - err = rxe_cq_chk_attr(rxe, cq, cqe, 0); - if (err) { - rxe_dbg_cq(cq, "bad attr, err = %d\n", err); - goto err_out; - } + if (cqe > rxe->attr.max_cqe || + cqe < queue_count(cq->queue, QUEUE_TYPE_TO_CLIENT)) + return -EINVAL; err = rxe_cq_resize_queue(cq, cqe, uresp, udata); if (err) { @@ -1519,7 +1506,7 @@ static const struct ib_device_ops rxe_dev_ops = { .reg_user_mr = rxe_reg_user_mr, .req_notify_cq = rxe_req_notify_cq, .rereg_user_mr = rxe_rereg_user_mr, - .resize_cq = rxe_resize_cq, + .resize_user_cq = rxe_resize_cq, INIT_RDMA_OBJ_SIZE(ib_ah, rxe_ah, ibah), INIT_RDMA_OBJ_SIZE(ib_cq, rxe_cq, ibcq), diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.h b/drivers/infiniband/sw/rxe/rxe_verbs.h index fb149f37e91d..d92f80d16f78 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.h +++ b/drivers/infiniband/sw/rxe/rxe_verbs.h @@ -154,6 +154,7 @@ enum resp_states { RESPST_ERR_MISSING_OPCODE_LAST_D1E, RESPST_ERR_TOO_MANY_RDMA_ATM_REQ, RESPST_ERR_RNR, + RESPST_ERR_RKEY_VIOLATION_EVENT, RESPST_ERR_RKEY_VIOLATION, RESPST_ERR_INVALIDATE_RKEY, RESPST_ERR_LENGTH, diff --git a/drivers/infiniband/sw/siw/siw_verbs.c b/drivers/infiniband/sw/siw/siw_verbs.c index ef504db8f2b4..1e1d262a4ae2 100644 --- a/drivers/infiniband/sw/siw/siw_verbs.c +++ b/drivers/infiniband/sw/siw/siw_verbs.c @@ -1373,11 +1373,7 @@ struct ib_mr *siw_reg_user_mr(struct ib_pd *pd, u64 start, u64 len, struct siw_uresp_reg_mr uresp = {}; struct siw_mem *mem = mr->mem; - if (udata->inlen < sizeof(ureq)) { - rv = -EINVAL; - goto err_out; - } - rv = ib_copy_from_udata(&ureq, udata, sizeof(ureq)); + rv = ib_copy_validate_udata_in(udata, ureq, pad); if (rv) goto err_out; diff --git a/drivers/infiniband/ulp/Makefile b/drivers/infiniband/ulp/Makefile index 4d0004b58377..51b0d41699b8 100644 --- a/drivers/infiniband/ulp/Makefile +++ b/drivers/infiniband/ulp/Makefile @@ -4,5 +4,4 @@ obj-$(CONFIG_INFINIBAND_SRP) += srp/ obj-$(CONFIG_INFINIBAND_SRPT) += srpt/ obj-$(CONFIG_INFINIBAND_ISER) += iser/ obj-$(CONFIG_INFINIBAND_ISERT) += isert/ -obj-$(CONFIG_INFINIBAND_OPA_VNIC) += opa_vnic/ obj-$(CONFIG_INFINIBAND_RTRS) += rtrs/ diff --git a/drivers/infiniband/ulp/opa_vnic/Kconfig b/drivers/infiniband/ulp/opa_vnic/Kconfig deleted file mode 100644 index 4d43d055fa8e..000000000000 --- a/drivers/infiniband/ulp/opa_vnic/Kconfig +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config INFINIBAND_OPA_VNIC - tristate "Cornelis OPX VNIC support" - depends on X86_64 && INFINIBAND - help - This is Omni-Path Express (OPX) Virtual Network Interface Controller (VNIC) - driver for Ethernet over Omni-Path feature. It implements the HW - independent VNIC functionality. It interfaces with Linux stack for - data path and IB MAD for the control path. diff --git a/drivers/infiniband/ulp/opa_vnic/Makefile b/drivers/infiniband/ulp/opa_vnic/Makefile deleted file mode 100644 index 196183817cdc..000000000000 --- a/drivers/infiniband/ulp/opa_vnic/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# Makefile - Cornelis Omni-Path Express Virtual Network Controller driver -# Copyright(c) 2017, Intel Corporation. -# Copyright(c) 2021, Cornelis Networks. -# -obj-$(CONFIG_INFINIBAND_OPA_VNIC) += opa_vnic.o - -opa_vnic-y := opa_vnic_netdev.o opa_vnic_encap.o opa_vnic_ethtool.o \ - opa_vnic_vema.o opa_vnic_vema_iface.o diff --git a/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.c b/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.c deleted file mode 100644 index 53dcf06fbee0..000000000000 --- a/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.c +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Copyright(c) 2017 Intel Corporation. - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* - * This file contains OPA VNIC encapsulation/decapsulation function. - */ - -#include -#include - -#include "opa_vnic_internal.h" - -/* OPA 16B Header fields */ -#define OPA_16B_LID_MASK 0xFFFFFull -#define OPA_16B_SLID_HIGH_SHFT 8 -#define OPA_16B_SLID_MASK 0xF00ull -#define OPA_16B_DLID_MASK 0xF000ull -#define OPA_16B_DLID_HIGH_SHFT 12 -#define OPA_16B_LEN_SHFT 20 -#define OPA_16B_SC_SHFT 20 -#define OPA_16B_RC_SHFT 25 -#define OPA_16B_PKEY_SHFT 16 - -#define OPA_VNIC_L4_HDR_SHFT 16 - -/* L2+L4 hdr len is 20 bytes (5 quad words) */ -#define OPA_VNIC_HDR_QW_LEN 5 - -static inline void opa_vnic_make_header(u8 *hdr, u32 slid, u32 dlid, u16 len, - u16 pkey, u16 entropy, u8 sc, u8 rc, - u8 l4_type, u16 l4_hdr) -{ - /* h[1]: LT=1, 16B L2=10 */ - u32 h[OPA_VNIC_HDR_QW_LEN] = {0, 0xc0000000, 0, 0, 0}; - - h[2] = l4_type; - h[3] = entropy; - h[4] = l4_hdr << OPA_VNIC_L4_HDR_SHFT; - - /* Extract and set 4 upper bits and 20 lower bits of the lids */ - h[0] |= (slid & OPA_16B_LID_MASK); - h[2] |= ((slid >> (20 - OPA_16B_SLID_HIGH_SHFT)) & OPA_16B_SLID_MASK); - - h[1] |= (dlid & OPA_16B_LID_MASK); - h[2] |= ((dlid >> (20 - OPA_16B_DLID_HIGH_SHFT)) & OPA_16B_DLID_MASK); - - h[0] |= (len << OPA_16B_LEN_SHFT); - h[1] |= (rc << OPA_16B_RC_SHFT); - h[1] |= (sc << OPA_16B_SC_SHFT); - h[2] |= ((u32)pkey << OPA_16B_PKEY_SHFT); - - memcpy(hdr, h, OPA_VNIC_HDR_LEN); -} - -/* - * Using a simple hash table for mac table implementation with the last octet - * of mac address as a key. - */ -static void opa_vnic_free_mac_tbl(struct hlist_head *mactbl) -{ - struct opa_vnic_mac_tbl_node *node; - struct hlist_node *tmp; - int bkt; - - if (!mactbl) - return; - - vnic_hash_for_each_safe(mactbl, bkt, tmp, node, hlist) { - hash_del(&node->hlist); - kfree(node); - } - kfree(mactbl); -} - -static struct hlist_head *opa_vnic_alloc_mac_tbl(void) -{ - u32 size = sizeof(struct hlist_head) * OPA_VNIC_MAC_TBL_SIZE; - struct hlist_head *mactbl; - - mactbl = kzalloc(size, GFP_KERNEL); - if (!mactbl) - return ERR_PTR(-ENOMEM); - - vnic_hash_init(mactbl); - return mactbl; -} - -/* opa_vnic_release_mac_tbl - empty and free the mac table */ -void opa_vnic_release_mac_tbl(struct opa_vnic_adapter *adapter) -{ - struct hlist_head *mactbl; - - mutex_lock(&adapter->mactbl_lock); - mactbl = rcu_access_pointer(adapter->mactbl); - rcu_assign_pointer(adapter->mactbl, NULL); - synchronize_rcu(); - opa_vnic_free_mac_tbl(mactbl); - adapter->info.vport.mac_tbl_digest = 0; - mutex_unlock(&adapter->mactbl_lock); -} - -/* - * opa_vnic_query_mac_tbl - query the mac table for a section - * - * This function implements query of specific function of the mac table. - * The function also expects the requested range to be valid. - */ -void opa_vnic_query_mac_tbl(struct opa_vnic_adapter *adapter, - struct opa_veswport_mactable *tbl) -{ - struct opa_vnic_mac_tbl_node *node; - struct hlist_head *mactbl; - int bkt; - u16 loffset, lnum_entries; - - rcu_read_lock(); - mactbl = rcu_dereference(adapter->mactbl); - if (!mactbl) - goto get_mac_done; - - loffset = be16_to_cpu(tbl->offset); - lnum_entries = be16_to_cpu(tbl->num_entries); - - vnic_hash_for_each(mactbl, bkt, node, hlist) { - struct __opa_vnic_mactable_entry *nentry = &node->entry; - struct opa_veswport_mactable_entry *entry; - - if ((node->index < loffset) || - (node->index >= (loffset + lnum_entries))) - continue; - - /* populate entry in the tbl corresponding to the index */ - entry = &tbl->tbl_entries[node->index - loffset]; - memcpy(entry->mac_addr, nentry->mac_addr, - ARRAY_SIZE(entry->mac_addr)); - memcpy(entry->mac_addr_mask, nentry->mac_addr_mask, - ARRAY_SIZE(entry->mac_addr_mask)); - entry->dlid_sd = cpu_to_be32(nentry->dlid_sd); - } - tbl->mac_tbl_digest = cpu_to_be32(adapter->info.vport.mac_tbl_digest); -get_mac_done: - rcu_read_unlock(); -} - -/* - * opa_vnic_update_mac_tbl - update mac table section - * - * This function updates the specified section of the mac table. - * The procedure includes following steps. - * - Allocate a new mac (hash) table. - * - Add the specified entries to the new table. - * (except the ones that are requested to be deleted). - * - Add all the other entries from the old mac table. - * - If there is a failure, free the new table and return. - * - Switch to the new table. - * - Free the old table and return. - * - * The function also expects the requested range to be valid. - */ -int opa_vnic_update_mac_tbl(struct opa_vnic_adapter *adapter, - struct opa_veswport_mactable *tbl) -{ - struct opa_vnic_mac_tbl_node *node, *new_node; - struct hlist_head *new_mactbl, *old_mactbl; - int i, bkt, rc = 0; - u8 key; - u16 loffset, lnum_entries; - - mutex_lock(&adapter->mactbl_lock); - /* allocate new mac table */ - new_mactbl = opa_vnic_alloc_mac_tbl(); - if (IS_ERR(new_mactbl)) { - mutex_unlock(&adapter->mactbl_lock); - return PTR_ERR(new_mactbl); - } - - loffset = be16_to_cpu(tbl->offset); - lnum_entries = be16_to_cpu(tbl->num_entries); - - /* add updated entries to the new mac table */ - for (i = 0; i < lnum_entries; i++) { - struct __opa_vnic_mactable_entry *nentry; - struct opa_veswport_mactable_entry *entry = - &tbl->tbl_entries[i]; - u8 *mac_addr = entry->mac_addr; - u8 empty_mac[ETH_ALEN] = { 0 }; - - v_dbg("new mac entry %4d: %02x:%02x:%02x:%02x:%02x:%02x %x\n", - loffset + i, mac_addr[0], mac_addr[1], mac_addr[2], - mac_addr[3], mac_addr[4], mac_addr[5], - entry->dlid_sd); - - /* if the entry is being removed, do not add it */ - if (!memcmp(mac_addr, empty_mac, ARRAY_SIZE(empty_mac))) - continue; - - node = kzalloc_obj(*node); - if (!node) { - rc = -ENOMEM; - goto updt_done; - } - - node->index = loffset + i; - nentry = &node->entry; - memcpy(nentry->mac_addr, entry->mac_addr, - ARRAY_SIZE(nentry->mac_addr)); - memcpy(nentry->mac_addr_mask, entry->mac_addr_mask, - ARRAY_SIZE(nentry->mac_addr_mask)); - nentry->dlid_sd = be32_to_cpu(entry->dlid_sd); - key = node->entry.mac_addr[OPA_VNIC_MAC_HASH_IDX]; - vnic_hash_add(new_mactbl, &node->hlist, key); - } - - /* add other entries from current mac table to new mac table */ - old_mactbl = rcu_access_pointer(adapter->mactbl); - if (!old_mactbl) - goto switch_tbl; - - vnic_hash_for_each(old_mactbl, bkt, node, hlist) { - if ((node->index >= loffset) && - (node->index < (loffset + lnum_entries))) - continue; - - new_node = kzalloc_obj(*new_node); - if (!new_node) { - rc = -ENOMEM; - goto updt_done; - } - - new_node->index = node->index; - memcpy(&new_node->entry, &node->entry, sizeof(node->entry)); - key = new_node->entry.mac_addr[OPA_VNIC_MAC_HASH_IDX]; - vnic_hash_add(new_mactbl, &new_node->hlist, key); - } - -switch_tbl: - /* switch to new table */ - rcu_assign_pointer(adapter->mactbl, new_mactbl); - synchronize_rcu(); - - adapter->info.vport.mac_tbl_digest = be32_to_cpu(tbl->mac_tbl_digest); -updt_done: - /* upon failure, free the new table; otherwise, free the old table */ - if (rc) - opa_vnic_free_mac_tbl(new_mactbl); - else - opa_vnic_free_mac_tbl(old_mactbl); - - mutex_unlock(&adapter->mactbl_lock); - return rc; -} - -/* opa_vnic_chk_mac_tbl - check mac table for dlid */ -static uint32_t opa_vnic_chk_mac_tbl(struct opa_vnic_adapter *adapter, - struct ethhdr *mac_hdr) -{ - struct opa_vnic_mac_tbl_node *node; - struct hlist_head *mactbl; - u32 dlid = 0; - u8 key; - - rcu_read_lock(); - mactbl = rcu_dereference(adapter->mactbl); - if (unlikely(!mactbl)) - goto chk_done; - - key = mac_hdr->h_dest[OPA_VNIC_MAC_HASH_IDX]; - vnic_hash_for_each_possible(mactbl, node, hlist, key) { - struct __opa_vnic_mactable_entry *entry = &node->entry; - - /* if related to source mac, skip */ - if (unlikely(OPA_VNIC_DLID_SD_IS_SRC_MAC(entry->dlid_sd))) - continue; - - if (!memcmp(node->entry.mac_addr, mac_hdr->h_dest, - ARRAY_SIZE(node->entry.mac_addr))) { - /* mac address found */ - dlid = OPA_VNIC_DLID_SD_GET_DLID(node->entry.dlid_sd); - break; - } - } - -chk_done: - rcu_read_unlock(); - return dlid; -} - -/* opa_vnic_get_dlid - find and return the DLID */ -static uint32_t opa_vnic_get_dlid(struct opa_vnic_adapter *adapter, - struct sk_buff *skb, u8 def_port) -{ - struct __opa_veswport_info *info = &adapter->info; - struct ethhdr *mac_hdr = (struct ethhdr *)skb_mac_header(skb); - u32 dlid; - - dlid = opa_vnic_chk_mac_tbl(adapter, mac_hdr); - if (dlid) - return dlid; - - if (is_multicast_ether_addr(mac_hdr->h_dest)) { - dlid = info->vesw.u_mcast_dlid; - } else { - if (is_local_ether_addr(mac_hdr->h_dest)) { - dlid = ((uint32_t)mac_hdr->h_dest[5] << 16) | - ((uint32_t)mac_hdr->h_dest[4] << 8) | - mac_hdr->h_dest[3]; - if (unlikely(!dlid)) - v_warn("Null dlid in MAC address\n"); - } else if (def_port != OPA_VNIC_INVALID_PORT) { - if (def_port < OPA_VESW_MAX_NUM_DEF_PORT) - dlid = info->vesw.u_ucast_dlid[def_port]; - } - } - - return dlid; -} - -/* opa_vnic_get_sc - return the service class */ -static u8 opa_vnic_get_sc(struct __opa_veswport_info *info, - struct sk_buff *skb) -{ - struct ethhdr *mac_hdr = (struct ethhdr *)skb_mac_header(skb); - u16 vlan_tci; - u8 sc; - - if (!__vlan_get_tag(skb, &vlan_tci)) { - u8 pcp = OPA_VNIC_VLAN_PCP(vlan_tci); - - if (is_multicast_ether_addr(mac_hdr->h_dest)) - sc = info->vport.pcp_to_sc_mc[pcp]; - else - sc = info->vport.pcp_to_sc_uc[pcp]; - } else { - if (is_multicast_ether_addr(mac_hdr->h_dest)) - sc = info->vport.non_vlan_sc_mc; - else - sc = info->vport.non_vlan_sc_uc; - } - - return sc; -} - -u8 opa_vnic_get_vl(struct opa_vnic_adapter *adapter, struct sk_buff *skb) -{ - struct ethhdr *mac_hdr = (struct ethhdr *)skb_mac_header(skb); - struct __opa_veswport_info *info = &adapter->info; - u8 vl; - - if (skb_vlan_tag_present(skb)) { - u8 pcp = skb_vlan_tag_get(skb) >> VLAN_PRIO_SHIFT; - - if (is_multicast_ether_addr(mac_hdr->h_dest)) - vl = info->vport.pcp_to_vl_mc[pcp]; - else - vl = info->vport.pcp_to_vl_uc[pcp]; - } else { - if (is_multicast_ether_addr(mac_hdr->h_dest)) - vl = info->vport.non_vlan_vl_mc; - else - vl = info->vport.non_vlan_vl_uc; - } - - return vl; -} - -/* opa_vnic_get_rc - return the routing control */ -static u8 opa_vnic_get_rc(struct __opa_veswport_info *info, - struct sk_buff *skb) -{ - u8 proto, rout_ctrl; - - switch (vlan_get_protocol(skb)) { - case htons(ETH_P_IPV6): - proto = ipv6_hdr(skb)->nexthdr; - if (proto == IPPROTO_TCP) - rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc, - IPV6_TCP); - else if (proto == IPPROTO_UDP) - rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc, - IPV6_UDP); - else - rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc, IPV6); - break; - case htons(ETH_P_IP): - proto = ip_hdr(skb)->protocol; - if (proto == IPPROTO_TCP) - rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc, - IPV4_TCP); - else if (proto == IPPROTO_UDP) - rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc, - IPV4_UDP); - else - rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc, IPV4); - break; - default: - rout_ctrl = OPA_VNIC_ENCAP_RC_EXT(info->vesw.rc, DEFAULT); - } - - return rout_ctrl; -} - -/* opa_vnic_calc_entropy - calculate the packet entropy */ -u8 opa_vnic_calc_entropy(struct sk_buff *skb) -{ - u32 hash = skb_get_hash(skb); - - /* store XOR of all bytes in lower 8 bits */ - hash ^= hash >> 8; - hash ^= hash >> 16; - - /* return lower 8 bits as entropy */ - return (u8)(hash & 0xFF); -} - -/* opa_vnic_get_def_port - get default port based on entropy */ -static inline u8 opa_vnic_get_def_port(struct opa_vnic_adapter *adapter, - u8 entropy) -{ - u8 flow_id; - - /* Add the upper and lower 4-bits of entropy to get the flow id */ - flow_id = ((entropy & 0xf) + (entropy >> 4)); - return adapter->flow_tbl[flow_id & (OPA_VNIC_FLOW_TBL_SIZE - 1)]; -} - -/* Calculate packet length including OPA header, crc and padding */ -static inline int opa_vnic_wire_length(struct sk_buff *skb) -{ - u32 pad_len; - - /* padding for 8 bytes size alignment */ - pad_len = -(skb->len + OPA_VNIC_ICRC_TAIL_LEN) & 0x7; - pad_len += OPA_VNIC_ICRC_TAIL_LEN; - - return (skb->len + pad_len) >> 3; -} - -/* opa_vnic_encap_skb - encapsulate skb packet with OPA header and meta data */ -void opa_vnic_encap_skb(struct opa_vnic_adapter *adapter, struct sk_buff *skb) -{ - struct __opa_veswport_info *info = &adapter->info; - struct opa_vnic_skb_mdata *mdata; - u8 def_port, sc, rc, entropy, *hdr; - u16 len, l4_hdr; - u32 dlid; - - hdr = skb_push(skb, OPA_VNIC_HDR_LEN); - - entropy = opa_vnic_calc_entropy(skb); - def_port = opa_vnic_get_def_port(adapter, entropy); - len = opa_vnic_wire_length(skb); - dlid = opa_vnic_get_dlid(adapter, skb, def_port); - sc = opa_vnic_get_sc(info, skb); - rc = opa_vnic_get_rc(info, skb); - l4_hdr = info->vesw.vesw_id; - - mdata = skb_push(skb, sizeof(*mdata)); - mdata->vl = opa_vnic_get_vl(adapter, skb); - mdata->entropy = entropy; - mdata->flags = 0; - if (unlikely(!dlid)) { - mdata->flags = OPA_VNIC_SKB_MDATA_ENCAP_ERR; - return; - } - - opa_vnic_make_header(hdr, info->vport.encap_slid, dlid, len, - info->vesw.pkey, entropy, sc, rc, - OPA_VNIC_L4_ETHR, l4_hdr); -} diff --git a/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.h b/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.h deleted file mode 100644 index 012fc27c5c93..000000000000 --- a/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.h +++ /dev/null @@ -1,524 +0,0 @@ -#ifndef _OPA_VNIC_ENCAP_H -#define _OPA_VNIC_ENCAP_H -/* - * Copyright(c) 2017 Intel Corporation. - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* - * This file contains all OPA VNIC declaration required for encapsulation - * and decapsulation of Ethernet packets - */ - -#include -#include - -/* EMA class version */ -#define OPA_EMA_CLASS_VERSION 0x80 - -/* - * Define the Intel vendor management class for OPA - * ETHERNET MANAGEMENT - */ -#define OPA_MGMT_CLASS_INTEL_EMA 0x34 - -/* EM attribute IDs */ -#define OPA_EM_ATTR_CLASS_PORT_INFO 0x0001 -#define OPA_EM_ATTR_VESWPORT_INFO 0x0011 -#define OPA_EM_ATTR_VESWPORT_MAC_ENTRIES 0x0012 -#define OPA_EM_ATTR_IFACE_UCAST_MACS 0x0013 -#define OPA_EM_ATTR_IFACE_MCAST_MACS 0x0014 -#define OPA_EM_ATTR_DELETE_VESW 0x0015 -#define OPA_EM_ATTR_VESWPORT_SUMMARY_COUNTERS 0x0020 -#define OPA_EM_ATTR_VESWPORT_ERROR_COUNTERS 0x0022 - -/* VNIC configured and operational state values */ -#define OPA_VNIC_STATE_DROP_ALL 0x1 -#define OPA_VNIC_STATE_FORWARDING 0x3 - -#define OPA_VESW_MAX_NUM_DEF_PORT 16 -#define OPA_VNIC_MAX_NUM_PCP 8 - -#define OPA_VNIC_EMA_DATA (OPA_MGMT_MAD_SIZE - IB_MGMT_VENDOR_HDR) - -/* Defines for vendor specific notice(trap) attributes */ -#define OPA_INTEL_EMA_NOTICE_TYPE_INFO 0x04 - -/* INTEL OUI */ -#define INTEL_OUI_1 0x00 -#define INTEL_OUI_2 0x06 -#define INTEL_OUI_3 0x6a - -/* Trap opcodes sent from VNIC */ -#define OPA_VESWPORT_TRAP_IFACE_UCAST_MAC_CHANGE 0x1 -#define OPA_VESWPORT_TRAP_IFACE_MCAST_MAC_CHANGE 0x2 -#define OPA_VESWPORT_TRAP_ETH_LINK_STATUS_CHANGE 0x3 - -#define OPA_VNIC_DLID_SD_IS_SRC_MAC(dlid_sd) (!!((dlid_sd) & 0x20)) -#define OPA_VNIC_DLID_SD_GET_DLID(dlid_sd) ((dlid_sd) >> 8) - -/* VNIC Ethernet link status */ -#define OPA_VNIC_ETH_LINK_UP 1 -#define OPA_VNIC_ETH_LINK_DOWN 2 - -/* routing control */ -#define OPA_VNIC_ENCAP_RC_DEFAULT 0 -#define OPA_VNIC_ENCAP_RC_IPV4 4 -#define OPA_VNIC_ENCAP_RC_IPV4_UDP 8 -#define OPA_VNIC_ENCAP_RC_IPV4_TCP 12 -#define OPA_VNIC_ENCAP_RC_IPV6 16 -#define OPA_VNIC_ENCAP_RC_IPV6_TCP 20 -#define OPA_VNIC_ENCAP_RC_IPV6_UDP 24 - -#define OPA_VNIC_ENCAP_RC_EXT(w, b) (((w) >> OPA_VNIC_ENCAP_RC_ ## b) & 0x7) - -/** - * struct opa_vesw_info - OPA vnic switch information - * @fabric_id: 10-bit fabric id - * @vesw_id: 12-bit virtual ethernet switch id - * @rsvd0: reserved bytes - * @def_port_mask: bitmask of default ports - * @rsvd1: reserved bytes - * @pkey: partition key - * @rsvd2: reserved bytes - * @u_mcast_dlid: unknown multicast dlid - * @u_ucast_dlid: array of unknown unicast dlids - * @rsvd3: reserved bytes - * @rc: routing control - * @eth_mtu: Ethernet MTU - * @rsvd4: reserved bytes - */ -struct opa_vesw_info { - __be16 fabric_id; - __be16 vesw_id; - - u8 rsvd0[6]; - __be16 def_port_mask; - - u8 rsvd1[2]; - __be16 pkey; - - u8 rsvd2[4]; - __be32 u_mcast_dlid; - __be32 u_ucast_dlid[OPA_VESW_MAX_NUM_DEF_PORT]; - - __be32 rc; - - u8 rsvd3[56]; - __be16 eth_mtu; - u8 rsvd4[2]; -} __packed; - -/** - * struct opa_per_veswport_info - OPA vnic per port information - * @port_num: port number - * @eth_link_status: current ethernet link state - * @rsvd0: reserved bytes - * @base_mac_addr: base mac address - * @config_state: configured port state - * @oper_state: operational port state - * @max_mac_tbl_ent: max number of mac table entries - * @max_smac_ent: max smac entries in mac table - * @mac_tbl_digest: mac table digest - * @rsvd1: reserved bytes - * @encap_slid: base slid for the port - * @pcp_to_sc_uc: sc by pcp index for unicast ethernet packets - * @pcp_to_vl_uc: vl by pcp index for unicast ethernet packets - * @pcp_to_sc_mc: sc by pcp index for multicast ethernet packets - * @pcp_to_vl_mc: vl by pcp index for multicast ethernet packets - * @non_vlan_sc_uc: sc for non-vlan unicast ethernet packets - * @non_vlan_vl_uc: vl for non-vlan unicast ethernet packets - * @non_vlan_sc_mc: sc for non-vlan multicast ethernet packets - * @non_vlan_vl_mc: vl for non-vlan multicast ethernet packets - * @rsvd2: reserved bytes - * @uc_macs_gen_count: generation count for unicast macs list - * @mc_macs_gen_count: generation count for multicast macs list - * @rsvd3: reserved bytes - */ -struct opa_per_veswport_info { - __be32 port_num; - - u8 eth_link_status; - u8 rsvd0[3]; - - u8 base_mac_addr[ETH_ALEN]; - u8 config_state; - u8 oper_state; - - __be16 max_mac_tbl_ent; - __be16 max_smac_ent; - __be32 mac_tbl_digest; - u8 rsvd1[4]; - - __be32 encap_slid; - - u8 pcp_to_sc_uc[OPA_VNIC_MAX_NUM_PCP]; - u8 pcp_to_vl_uc[OPA_VNIC_MAX_NUM_PCP]; - u8 pcp_to_sc_mc[OPA_VNIC_MAX_NUM_PCP]; - u8 pcp_to_vl_mc[OPA_VNIC_MAX_NUM_PCP]; - - u8 non_vlan_sc_uc; - u8 non_vlan_vl_uc; - u8 non_vlan_sc_mc; - u8 non_vlan_vl_mc; - - u8 rsvd2[48]; - - __be16 uc_macs_gen_count; - __be16 mc_macs_gen_count; - - u8 rsvd3[8]; -} __packed; - -/** - * struct opa_veswport_info - OPA vnic port information - * @vesw: OPA vnic switch information - * @vport: OPA vnic per port information - * - * On host, each of the virtual ethernet ports belongs - * to a different virtual ethernet switches. - */ -struct opa_veswport_info { - struct opa_vesw_info vesw; - struct opa_per_veswport_info vport; -}; - -/** - * struct opa_veswport_mactable_entry - single entry in the forwarding table - * @mac_addr: MAC address - * @mac_addr_mask: MAC address bit mask - * @dlid_sd: Matching DLID and side data - * - * On the host each virtual ethernet port will have - * a forwarding table. These tables are used to - * map a MAC to a LID and other data. For more - * details see struct opa_veswport_mactable_entries. - * This is the structure of a single mactable entry - */ -struct opa_veswport_mactable_entry { - u8 mac_addr[ETH_ALEN]; - u8 mac_addr_mask[ETH_ALEN]; - __be32 dlid_sd; -} __packed; - -/** - * struct opa_veswport_mactable - Forwarding table array - * @offset: mac table starting offset - * @num_entries: Number of entries to get or set - * @mac_tbl_digest: mac table digest - * @tbl_entries: Array of table entries - * - * The EM sends down this structure in a MAD indicating - * the starting offset in the forwarding table that this - * entry is to be loaded into and the number of entries - * that that this MAD instance contains - * The mac_tbl_digest has been added to this MAD structure. It will be set by - * the EM and it will be used by the EM to check if there are any - * discrepancies with this value and the value - * maintained by the EM in the case of VNIC port being deleted or unloaded - * A new instantiation of a VNIC will always have a value of zero. - * This value is stored as part of the vnic adapter structure and will be - * accessed by the GET and SET routines for both the mactable entries and the - * veswport info. - */ -struct opa_veswport_mactable { - __be16 offset; - __be16 num_entries; - __be32 mac_tbl_digest; - struct opa_veswport_mactable_entry tbl_entries[]; -} __packed; - -/** - * struct opa_veswport_summary_counters - summary counters - * @vp_instance: vport instance on the OPA port - * @vesw_id: virtual ethernet switch id - * @veswport_num: virtual ethernet switch port number - * @tx_errors: transmit errors - * @rx_errors: receive errors - * @tx_packets: transmit packets - * @rx_packets: receive packets - * @tx_bytes: transmit bytes - * @rx_bytes: receive bytes - * @tx_unicast: unicast packets transmitted - * @tx_mcastbcast: multicast/broadcast packets transmitted - * @tx_untagged: non-vlan packets transmitted - * @tx_vlan: vlan packets transmitted - * @tx_64_size: transmit packet length is 64 bytes - * @tx_65_127: transmit packet length is >=65 and < 127 bytes - * @tx_128_255: transmit packet length is >=128 and < 255 bytes - * @tx_256_511: transmit packet length is >=256 and < 511 bytes - * @tx_512_1023: transmit packet length is >=512 and < 1023 bytes - * @tx_1024_1518: transmit packet length is >=1024 and < 1518 bytes - * @tx_1519_max: transmit packet length >= 1519 bytes - * @rx_unicast: unicast packets received - * @rx_mcastbcast: multicast/broadcast packets received - * @rx_untagged: non-vlan packets received - * @rx_vlan: vlan packets received - * @rx_64_size: received packet length is 64 bytes - * @rx_65_127: received packet length is >=65 and < 127 bytes - * @rx_128_255: received packet length is >=128 and < 255 bytes - * @rx_256_511: received packet length is >=256 and < 511 bytes - * @rx_512_1023: received packet length is >=512 and < 1023 bytes - * @rx_1024_1518: received packet length is >=1024 and < 1518 bytes - * @rx_1519_max: received packet length >= 1519 bytes - * @reserved: reserved bytes - * - * All the above are counters of corresponding conditions. - */ -struct opa_veswport_summary_counters { - __be16 vp_instance; - __be16 vesw_id; - __be32 veswport_num; - - __be64 tx_errors; - __be64 rx_errors; - __be64 tx_packets; - __be64 rx_packets; - __be64 tx_bytes; - __be64 rx_bytes; - - __be64 tx_unicast; - __be64 tx_mcastbcast; - - __be64 tx_untagged; - __be64 tx_vlan; - - __be64 tx_64_size; - __be64 tx_65_127; - __be64 tx_128_255; - __be64 tx_256_511; - __be64 tx_512_1023; - __be64 tx_1024_1518; - __be64 tx_1519_max; - - __be64 rx_unicast; - __be64 rx_mcastbcast; - - __be64 rx_untagged; - __be64 rx_vlan; - - __be64 rx_64_size; - __be64 rx_65_127; - __be64 rx_128_255; - __be64 rx_256_511; - __be64 rx_512_1023; - __be64 rx_1024_1518; - __be64 rx_1519_max; - - __be64 reserved[16]; -} __packed; - -/** - * struct opa_veswport_error_counters - error counters - * @vp_instance: vport instance on the OPA port - * @vesw_id: virtual ethernet switch id - * @veswport_num: virtual ethernet switch port number - * @tx_errors: transmit errors - * @rx_errors: receive errors - * @rsvd0: reserved bytes - * @tx_smac_filt: smac filter errors - * @rsvd1: reserved bytes - * @rsvd2: reserved bytes - * @rsvd3: reserved bytes - * @tx_dlid_zero: transmit packets with invalid dlid - * @rsvd4: reserved bytes - * @tx_logic: other transmit errors - * @rsvd5: reserved bytes - * @tx_drop_state: packet tansmission in non-forward port state - * @rx_bad_veswid: received packet with invalid vesw id - * @rsvd6: reserved bytes - * @rx_runt: received ethernet packet with length < 64 bytes - * @rx_oversize: received ethernet packet with length > MTU size - * @rsvd7: reserved bytes - * @rx_eth_down: received packets when interface is down - * @rx_drop_state: received packets in non-forwarding port state - * @rx_logic: other receive errors - * @rsvd8: reserved bytes - * @rsvd9: reserved bytes - * - * All the above are counters of corresponding error conditions. - */ -struct opa_veswport_error_counters { - __be16 vp_instance; - __be16 vesw_id; - __be32 veswport_num; - - __be64 tx_errors; - __be64 rx_errors; - - __be64 rsvd0; - __be64 tx_smac_filt; - __be64 rsvd1; - __be64 rsvd2; - __be64 rsvd3; - __be64 tx_dlid_zero; - __be64 rsvd4; - __be64 tx_logic; - __be64 rsvd5; - __be64 tx_drop_state; - - __be64 rx_bad_veswid; - __be64 rsvd6; - __be64 rx_runt; - __be64 rx_oversize; - __be64 rsvd7; - __be64 rx_eth_down; - __be64 rx_drop_state; - __be64 rx_logic; - __be64 rsvd8; - - __be64 rsvd9[16]; -} __packed; - -/** - * struct opa_veswport_trap - Trap message sent to EM by VNIC - * @fabric_id: 10 bit fabric id - * @veswid: 12 bit virtual ethernet switch id - * @veswportnum: logical port number on the Virtual switch - * @opaportnum: physical port num (redundant on host) - * @veswportindex: switch port index on opa port 0 based - * @opcode: operation - * @reserved: 32 bit for alignment - * - * The VNIC will send trap messages to the Ethernet manager to - * inform it about changes to the VNIC config, behaviour etc. - * This is the format of the trap payload. - */ -struct opa_veswport_trap { - __be16 fabric_id; - __be16 veswid; - __be32 veswportnum; - __be16 opaportnum; - u8 veswportindex; - u8 opcode; - __be32 reserved; -} __packed; - -/** - * struct opa_vnic_iface_mac_entry - single entry in the mac list - * @mac_addr: MAC address - */ -struct opa_vnic_iface_mac_entry { - u8 mac_addr[ETH_ALEN]; -}; - -/** - * struct opa_veswport_iface_macs - Msg to set globally administered MAC - * @start_idx: position of first entry (0 based) - * @num_macs_in_msg: number of MACs in this message - * @tot_macs_in_lst: The total number of MACs the agent has - * @gen_count: gen_count to indicate change - * @entry: The mac list entry - * - * Same attribute IDS and attribute modifiers as in locally administered - * addresses used to set globally administered addresses - */ -struct opa_veswport_iface_macs { - __be16 start_idx; - __be16 num_macs_in_msg; - __be16 tot_macs_in_lst; - __be16 gen_count; - struct opa_vnic_iface_mac_entry entry[]; -} __packed; - -/** - * struct opa_vnic_vema_mad - Generic VEMA MAD - * @mad_hdr: Generic MAD header - * @rmpp_hdr: RMPP header for vendor specific MADs - * @reserved: reserved bytes - * @oui: Unique org identifier - * @data: MAD data - */ -struct opa_vnic_vema_mad { - struct ib_mad_hdr mad_hdr; - struct ib_rmpp_hdr rmpp_hdr; - u8 reserved; - u8 oui[3]; - u8 data[OPA_VNIC_EMA_DATA]; -}; - -/** - * struct opa_vnic_notice_attr - Generic Notice MAD - * @gen_type: Generic/Specific bit and type of notice - * @oui_1: Vendor ID byte 1 - * @oui_2: Vendor ID byte 2 - * @oui_3: Vendor ID byte 3 - * @trap_num: Trap number - * @toggle_count: Notice toggle bit and count value - * @issuer_lid: Trap issuer's lid - * @reserved: reserved bytes - * @issuer_gid: Issuer GID (only if Report method) - * @raw_data: Trap message body - */ -struct opa_vnic_notice_attr { - u8 gen_type; - u8 oui_1; - u8 oui_2; - u8 oui_3; - __be16 trap_num; - __be16 toggle_count; - __be32 issuer_lid; - __be32 reserved; - u8 issuer_gid[16]; - u8 raw_data[64]; -} __packed; - -/** - * struct opa_vnic_vema_mad_trap - Generic VEMA MAD Trap - * @mad_hdr: Generic MAD header - * @rmpp_hdr: RMPP header for vendor specific MADs - * @reserved: reserved bytes - * @oui: Unique org identifier - * @notice: Notice structure - */ -struct opa_vnic_vema_mad_trap { - struct ib_mad_hdr mad_hdr; - struct ib_rmpp_hdr rmpp_hdr; - u8 reserved; - u8 oui[3]; - struct opa_vnic_notice_attr notice; -}; - -#endif /* _OPA_VNIC_ENCAP_H */ diff --git a/drivers/infiniband/ulp/opa_vnic/opa_vnic_ethtool.c b/drivers/infiniband/ulp/opa_vnic/opa_vnic_ethtool.c deleted file mode 100644 index 316959940d2f..000000000000 --- a/drivers/infiniband/ulp/opa_vnic/opa_vnic_ethtool.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright(c) 2017 Intel Corporation. - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* - * This file contains OPA VNIC ethtool functions - */ - -#include - -#include "opa_vnic_internal.h" - -enum {NETDEV_STATS, VNIC_STATS}; - -struct vnic_stats { - char stat_string[ETH_GSTRING_LEN]; - struct { - int sizeof_stat; - int stat_offset; - }; -}; - -#define VNIC_STAT(m) { sizeof_field(struct opa_vnic_stats, m), \ - offsetof(struct opa_vnic_stats, m) } - -static struct vnic_stats vnic_gstrings_stats[] = { - /* NETDEV stats */ - {"rx_packets", VNIC_STAT(netstats.rx_packets)}, - {"tx_packets", VNIC_STAT(netstats.tx_packets)}, - {"rx_bytes", VNIC_STAT(netstats.rx_bytes)}, - {"tx_bytes", VNIC_STAT(netstats.tx_bytes)}, - {"rx_errors", VNIC_STAT(netstats.rx_errors)}, - {"tx_errors", VNIC_STAT(netstats.tx_errors)}, - {"rx_dropped", VNIC_STAT(netstats.rx_dropped)}, - {"tx_dropped", VNIC_STAT(netstats.tx_dropped)}, - - /* SUMMARY counters */ - {"tx_unicast", VNIC_STAT(tx_grp.unicast)}, - {"tx_mcastbcast", VNIC_STAT(tx_grp.mcastbcast)}, - {"tx_untagged", VNIC_STAT(tx_grp.untagged)}, - {"tx_vlan", VNIC_STAT(tx_grp.vlan)}, - - {"tx_64_size", VNIC_STAT(tx_grp.s_64)}, - {"tx_65_127", VNIC_STAT(tx_grp.s_65_127)}, - {"tx_128_255", VNIC_STAT(tx_grp.s_128_255)}, - {"tx_256_511", VNIC_STAT(tx_grp.s_256_511)}, - {"tx_512_1023", VNIC_STAT(tx_grp.s_512_1023)}, - {"tx_1024_1518", VNIC_STAT(tx_grp.s_1024_1518)}, - {"tx_1519_max", VNIC_STAT(tx_grp.s_1519_max)}, - - {"rx_unicast", VNIC_STAT(rx_grp.unicast)}, - {"rx_mcastbcast", VNIC_STAT(rx_grp.mcastbcast)}, - {"rx_untagged", VNIC_STAT(rx_grp.untagged)}, - {"rx_vlan", VNIC_STAT(rx_grp.vlan)}, - - {"rx_64_size", VNIC_STAT(rx_grp.s_64)}, - {"rx_65_127", VNIC_STAT(rx_grp.s_65_127)}, - {"rx_128_255", VNIC_STAT(rx_grp.s_128_255)}, - {"rx_256_511", VNIC_STAT(rx_grp.s_256_511)}, - {"rx_512_1023", VNIC_STAT(rx_grp.s_512_1023)}, - {"rx_1024_1518", VNIC_STAT(rx_grp.s_1024_1518)}, - {"rx_1519_max", VNIC_STAT(rx_grp.s_1519_max)}, - - /* ERROR counters */ - {"rx_fifo_errors", VNIC_STAT(netstats.rx_fifo_errors)}, - {"rx_length_errors", VNIC_STAT(netstats.rx_length_errors)}, - - {"tx_fifo_errors", VNIC_STAT(netstats.tx_fifo_errors)}, - {"tx_carrier_errors", VNIC_STAT(netstats.tx_carrier_errors)}, - - {"tx_dlid_zero", VNIC_STAT(tx_dlid_zero)}, - {"tx_drop_state", VNIC_STAT(tx_drop_state)}, - {"rx_drop_state", VNIC_STAT(rx_drop_state)}, - {"rx_oversize", VNIC_STAT(rx_oversize)}, - {"rx_runt", VNIC_STAT(rx_runt)}, -}; - -#define VNIC_STATS_LEN ARRAY_SIZE(vnic_gstrings_stats) - -/* vnic_get_drvinfo - get driver info */ -static void vnic_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *drvinfo) -{ - strscpy(drvinfo->driver, opa_vnic_driver_name, sizeof(drvinfo->driver)); - strscpy(drvinfo->bus_info, dev_name(netdev->dev.parent), - sizeof(drvinfo->bus_info)); -} - -/* vnic_get_sset_count - get string set count */ -static int vnic_get_sset_count(struct net_device *netdev, int sset) -{ - return (sset == ETH_SS_STATS) ? VNIC_STATS_LEN : -EOPNOTSUPP; -} - -/* vnic_get_ethtool_stats - get statistics */ -static void vnic_get_ethtool_stats(struct net_device *netdev, - struct ethtool_stats *stats, u64 *data) -{ - struct opa_vnic_adapter *adapter = opa_vnic_priv(netdev); - struct opa_vnic_stats vstats; - int i; - - memset(&vstats, 0, sizeof(vstats)); - spin_lock(&adapter->stats_lock); - adapter->rn_ops->ndo_get_stats64(netdev, &vstats.netstats); - spin_unlock(&adapter->stats_lock); - for (i = 0; i < VNIC_STATS_LEN; i++) { - char *p = (char *)&vstats + vnic_gstrings_stats[i].stat_offset; - - data[i] = (vnic_gstrings_stats[i].sizeof_stat == - sizeof(u64)) ? *(u64 *)p : *(u32 *)p; - } -} - -/* vnic_get_strings - get strings */ -static void vnic_get_strings(struct net_device *netdev, u32 stringset, u8 *data) -{ - int i; - - if (stringset != ETH_SS_STATS) - return; - - for (i = 0; i < VNIC_STATS_LEN; i++) - ethtool_puts(&data, vnic_gstrings_stats[i].stat_string); -} - -/* ethtool ops */ -static const struct ethtool_ops opa_vnic_ethtool_ops = { - .get_drvinfo = vnic_get_drvinfo, - .get_link = ethtool_op_get_link, - .get_strings = vnic_get_strings, - .get_sset_count = vnic_get_sset_count, - .get_ethtool_stats = vnic_get_ethtool_stats, -}; - -/* opa_vnic_set_ethtool_ops - set ethtool ops */ -void opa_vnic_set_ethtool_ops(struct net_device *netdev) -{ - netdev->ethtool_ops = &opa_vnic_ethtool_ops; -} diff --git a/drivers/infiniband/ulp/opa_vnic/opa_vnic_internal.h b/drivers/infiniband/ulp/opa_vnic/opa_vnic_internal.h deleted file mode 100644 index dd942dd642bd..000000000000 --- a/drivers/infiniband/ulp/opa_vnic/opa_vnic_internal.h +++ /dev/null @@ -1,329 +0,0 @@ -#ifndef _OPA_VNIC_INTERNAL_H -#define _OPA_VNIC_INTERNAL_H -/* - * Copyright(c) 2017 Intel Corporation. - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* - * This file contains OPA VNIC driver internal declarations - */ - -#include -#include -#include -#include -#include - -#include "opa_vnic_encap.h" - -#define OPA_VNIC_VLAN_PCP(vlan_tci) \ - (((vlan_tci) & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT) - -/* Flow to default port redirection table size */ -#define OPA_VNIC_FLOW_TBL_SIZE 32 - -/* Invalid port number */ -#define OPA_VNIC_INVALID_PORT 0xff - -struct opa_vnic_adapter; - -/* - * struct __opa_vesw_info - OPA vnic virtual switch info - * - * Same as opa_vesw_info without bitwise attribute. - */ -struct __opa_vesw_info { - u16 fabric_id; - u16 vesw_id; - - u8 rsvd0[6]; - u16 def_port_mask; - - u8 rsvd1[2]; - u16 pkey; - - u8 rsvd2[4]; - u32 u_mcast_dlid; - u32 u_ucast_dlid[OPA_VESW_MAX_NUM_DEF_PORT]; - - u32 rc; - - u8 rsvd3[56]; - u16 eth_mtu; - u8 rsvd4[2]; -} __packed; - -/* - * struct __opa_per_veswport_info - OPA vnic per port info - * - * Same as opa_per_veswport_info without bitwise attribute. - */ -struct __opa_per_veswport_info { - u32 port_num; - - u8 eth_link_status; - u8 rsvd0[3]; - - u8 base_mac_addr[ETH_ALEN]; - u8 config_state; - u8 oper_state; - - u16 max_mac_tbl_ent; - u16 max_smac_ent; - u32 mac_tbl_digest; - u8 rsvd1[4]; - - u32 encap_slid; - - u8 pcp_to_sc_uc[OPA_VNIC_MAX_NUM_PCP]; - u8 pcp_to_vl_uc[OPA_VNIC_MAX_NUM_PCP]; - u8 pcp_to_sc_mc[OPA_VNIC_MAX_NUM_PCP]; - u8 pcp_to_vl_mc[OPA_VNIC_MAX_NUM_PCP]; - - u8 non_vlan_sc_uc; - u8 non_vlan_vl_uc; - u8 non_vlan_sc_mc; - u8 non_vlan_vl_mc; - - u8 rsvd2[48]; - - u16 uc_macs_gen_count; - u16 mc_macs_gen_count; - - u8 rsvd3[8]; -} __packed; - -/* - * struct __opa_veswport_info - OPA vnic port info - * - * Same as opa_veswport_info without bitwise attribute. - */ -struct __opa_veswport_info { - struct __opa_vesw_info vesw; - struct __opa_per_veswport_info vport; -}; - -/* - * struct __opa_veswport_trap - OPA vnic trap info - * - * Same as opa_veswport_trap without bitwise attribute. - */ -struct __opa_veswport_trap { - u16 fabric_id; - u16 veswid; - u32 veswportnum; - u16 opaportnum; - u8 veswportindex; - u8 opcode; - u32 reserved; -} __packed; - -/** - * struct opa_vnic_ctrl_port - OPA virtual NIC control port - * @ibdev: pointer to ib device - * @ops: opa vnic control operations - * @num_ports: number of opa ports - */ -struct opa_vnic_ctrl_port { - struct ib_device *ibdev; - struct opa_vnic_ctrl_ops *ops; - u8 num_ports; -}; - -/** - * struct opa_vnic_adapter - OPA VNIC netdev private data structure - * @netdev: pointer to associated netdev - * @ibdev: ib device - * @cport: pointer to opa vnic control port - * @rn_ops: rdma netdev's net_device_ops - * @port_num: OPA port number - * @vport_num: vesw port number - * @lock: adapter lock - * @info: virtual ethernet switch port information - * @vema_mac_addr: mac address configured by vema - * @umac_hash: unicast maclist hash - * @mmac_hash: multicast maclist hash - * @mactbl: hash table of MAC entries - * @mactbl_lock: mac table lock - * @stats_lock: statistics lock - * @flow_tbl: flow to default port redirection table - * @trap_timeout: trap timeout - * @trap_count: no. of traps allowed within timeout period - */ -struct opa_vnic_adapter { - struct net_device *netdev; - struct ib_device *ibdev; - struct opa_vnic_ctrl_port *cport; - const struct net_device_ops *rn_ops; - - u8 port_num; - u8 vport_num; - - /* Lock used around concurrent updates to netdev */ - struct mutex lock; - - struct __opa_veswport_info info; - u8 vema_mac_addr[ETH_ALEN]; - u32 umac_hash; - u32 mmac_hash; - struct hlist_head __rcu *mactbl; - - /* Lock used to protect updates to mac table */ - struct mutex mactbl_lock; - - /* Lock used to protect access to vnic counters */ - spinlock_t stats_lock; - - u8 flow_tbl[OPA_VNIC_FLOW_TBL_SIZE]; - - unsigned long trap_timeout; - u8 trap_count; -}; - -/* Same as opa_veswport_mactable_entry, but without bitwise attribute */ -struct __opa_vnic_mactable_entry { - u8 mac_addr[ETH_ALEN]; - u8 mac_addr_mask[ETH_ALEN]; - u32 dlid_sd; -} __packed; - -/** - * struct opa_vnic_mac_tbl_node - OPA VNIC mac table node - * @hlist: hash list handle - * @index: index of entry in the mac table - * @entry: entry in the table - */ -struct opa_vnic_mac_tbl_node { - struct hlist_node hlist; - u16 index; - struct __opa_vnic_mactable_entry entry; -}; - -#define v_dbg(format, arg...) \ - netdev_dbg(adapter->netdev, format, ## arg) -#define v_err(format, arg...) \ - netdev_err(adapter->netdev, format, ## arg) -#define v_info(format, arg...) \ - netdev_info(adapter->netdev, format, ## arg) -#define v_warn(format, arg...) \ - netdev_warn(adapter->netdev, format, ## arg) - -#define c_err(format, arg...) \ - dev_err(&cport->ibdev->dev, format, ## arg) -#define c_info(format, arg...) \ - dev_info(&cport->ibdev->dev, format, ## arg) -#define c_dbg(format, arg...) \ - dev_dbg(&cport->ibdev->dev, format, ## arg) - -/* The maximum allowed entries in the mac table */ -#define OPA_VNIC_MAC_TBL_MAX_ENTRIES 2048 -/* Limit of smac entries in mac table */ -#define OPA_VNIC_MAX_SMAC_LIMIT 256 - -/* The last octet of the MAC address is used as the key to the hash table */ -#define OPA_VNIC_MAC_HASH_IDX 5 - -/* The VNIC MAC hash table is of size 2^8 */ -#define OPA_VNIC_MAC_TBL_HASH_BITS 8 -#define OPA_VNIC_MAC_TBL_SIZE BIT(OPA_VNIC_MAC_TBL_HASH_BITS) - -/* VNIC HASH MACROS */ -#define vnic_hash_init(hashtable) __hash_init(hashtable, OPA_VNIC_MAC_TBL_SIZE) - -#define vnic_hash_add(hashtable, node, key) \ - hlist_add_head(node, \ - &hashtable[hash_min(key, ilog2(OPA_VNIC_MAC_TBL_SIZE))]) - -#define vnic_hash_for_each_safe(name, bkt, tmp, obj, member) \ - for ((bkt) = 0, obj = NULL; \ - !obj && (bkt) < OPA_VNIC_MAC_TBL_SIZE; (bkt)++) \ - hlist_for_each_entry_safe(obj, tmp, &name[bkt], member) - -#define vnic_hash_for_each_possible(name, obj, member, key) \ - hlist_for_each_entry(obj, \ - &name[hash_min(key, ilog2(OPA_VNIC_MAC_TBL_SIZE))], member) - -#define vnic_hash_for_each(name, bkt, obj, member) \ - for ((bkt) = 0, obj = NULL; \ - !obj && (bkt) < OPA_VNIC_MAC_TBL_SIZE; (bkt)++) \ - hlist_for_each_entry(obj, &name[bkt], member) - -extern char opa_vnic_driver_name[]; - -struct opa_vnic_adapter *opa_vnic_add_netdev(struct ib_device *ibdev, - u8 port_num, u8 vport_num); -void opa_vnic_rem_netdev(struct opa_vnic_adapter *adapter); -void opa_vnic_encap_skb(struct opa_vnic_adapter *adapter, struct sk_buff *skb); -u8 opa_vnic_get_vl(struct opa_vnic_adapter *adapter, struct sk_buff *skb); -u8 opa_vnic_calc_entropy(struct sk_buff *skb); -void opa_vnic_process_vema_config(struct opa_vnic_adapter *adapter); -void opa_vnic_release_mac_tbl(struct opa_vnic_adapter *adapter); -void opa_vnic_query_mac_tbl(struct opa_vnic_adapter *adapter, - struct opa_veswport_mactable *tbl); -int opa_vnic_update_mac_tbl(struct opa_vnic_adapter *adapter, - struct opa_veswport_mactable *tbl); -void opa_vnic_query_ucast_macs(struct opa_vnic_adapter *adapter, - struct opa_veswport_iface_macs *macs); -void opa_vnic_query_mcast_macs(struct opa_vnic_adapter *adapter, - struct opa_veswport_iface_macs *macs); -void opa_vnic_get_summary_counters(struct opa_vnic_adapter *adapter, - struct opa_veswport_summary_counters *cntrs); -void opa_vnic_get_error_counters(struct opa_vnic_adapter *adapter, - struct opa_veswport_error_counters *cntrs); -void opa_vnic_get_vesw_info(struct opa_vnic_adapter *adapter, - struct opa_vesw_info *info); -void opa_vnic_set_vesw_info(struct opa_vnic_adapter *adapter, - struct opa_vesw_info *info); -void opa_vnic_get_per_veswport_info(struct opa_vnic_adapter *adapter, - struct opa_per_veswport_info *info); -void opa_vnic_set_per_veswport_info(struct opa_vnic_adapter *adapter, - struct opa_per_veswport_info *info); -void opa_vnic_vema_report_event(struct opa_vnic_adapter *adapter, u8 event); -void opa_vnic_set_ethtool_ops(struct net_device *netdev); -void opa_vnic_vema_send_trap(struct opa_vnic_adapter *adapter, - struct __opa_veswport_trap *data, u32 lid); - -#endif /* _OPA_VNIC_INTERNAL_H */ diff --git a/drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c b/drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c deleted file mode 100644 index 1c3e7251f0f4..000000000000 --- a/drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright(c) 2017 Intel Corporation. - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* - * This file contains OPA Virtual Network Interface Controller (VNIC) driver - * netdev functionality. - */ - -#include -#include - -#include "opa_vnic_internal.h" - -#define OPA_TX_TIMEOUT_MS 1000 - -#define OPA_VNIC_SKB_HEADROOM \ - ALIGN((OPA_VNIC_HDR_LEN + OPA_VNIC_SKB_MDATA_LEN), 8) - -/* This function is overloaded for opa_vnic specific implementation */ -static void opa_vnic_get_stats64(struct net_device *netdev, - struct rtnl_link_stats64 *stats) -{ - struct opa_vnic_adapter *adapter = opa_vnic_priv(netdev); - struct opa_vnic_stats vstats; - - memset(&vstats, 0, sizeof(vstats)); - spin_lock(&adapter->stats_lock); - adapter->rn_ops->ndo_get_stats64(netdev, &vstats.netstats); - spin_unlock(&adapter->stats_lock); - memcpy(stats, &vstats.netstats, sizeof(*stats)); -} - -/* opa_netdev_start_xmit - transmit function */ -static netdev_tx_t opa_netdev_start_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct opa_vnic_adapter *adapter = opa_vnic_priv(netdev); - - v_dbg("xmit: queue %d skb len %d\n", skb->queue_mapping, skb->len); - /* pad to ensure mininum ethernet packet length */ - if (unlikely(skb->len < ETH_ZLEN)) { - if (skb_padto(skb, ETH_ZLEN)) - return NETDEV_TX_OK; - - skb_put(skb, ETH_ZLEN - skb->len); - } - - opa_vnic_encap_skb(adapter, skb); - return adapter->rn_ops->ndo_start_xmit(skb, netdev); -} - -static u16 opa_vnic_select_queue(struct net_device *netdev, struct sk_buff *skb, - struct net_device *sb_dev) -{ - struct opa_vnic_adapter *adapter = opa_vnic_priv(netdev); - struct opa_vnic_skb_mdata *mdata; - int rc; - - /* pass entropy and vl as metadata in skb */ - mdata = skb_push(skb, sizeof(*mdata)); - mdata->entropy = opa_vnic_calc_entropy(skb); - mdata->vl = opa_vnic_get_vl(adapter, skb); - rc = adapter->rn_ops->ndo_select_queue(netdev, skb, sb_dev); - skb_pull(skb, sizeof(*mdata)); - return rc; -} - -static void opa_vnic_update_state(struct opa_vnic_adapter *adapter, bool up) -{ - struct __opa_veswport_info *info = &adapter->info; - - mutex_lock(&adapter->lock); - /* Operational state can only be DROP_ALL or FORWARDING */ - if ((info->vport.config_state == OPA_VNIC_STATE_FORWARDING) && up) { - info->vport.oper_state = OPA_VNIC_STATE_FORWARDING; - info->vport.eth_link_status = OPA_VNIC_ETH_LINK_UP; - } else { - info->vport.oper_state = OPA_VNIC_STATE_DROP_ALL; - info->vport.eth_link_status = OPA_VNIC_ETH_LINK_DOWN; - } - - if (info->vport.config_state == OPA_VNIC_STATE_FORWARDING) - netif_dormant_off(adapter->netdev); - else - netif_dormant_on(adapter->netdev); - mutex_unlock(&adapter->lock); -} - -/* opa_vnic_process_vema_config - process vema configuration updates */ -void opa_vnic_process_vema_config(struct opa_vnic_adapter *adapter) -{ - struct __opa_veswport_info *info = &adapter->info; - struct rdma_netdev *rn = netdev_priv(adapter->netdev); - u8 port_num[OPA_VESW_MAX_NUM_DEF_PORT] = { 0 }; - struct net_device *netdev = adapter->netdev; - u8 i, port_count = 0; - u16 port_mask; - - /* If the base_mac_addr is changed, update the interface mac address */ - if (memcmp(info->vport.base_mac_addr, adapter->vema_mac_addr, - ARRAY_SIZE(info->vport.base_mac_addr))) { - struct sockaddr saddr; - - memcpy(saddr.sa_data, info->vport.base_mac_addr, - ARRAY_SIZE(info->vport.base_mac_addr)); - mutex_lock(&adapter->lock); - eth_commit_mac_addr_change(netdev, &saddr); - memcpy(adapter->vema_mac_addr, - info->vport.base_mac_addr, ETH_ALEN); - mutex_unlock(&adapter->lock); - } - - rn->set_id(netdev, info->vesw.vesw_id); - - /* Handle MTU limit change */ - rtnl_lock(); - netdev->max_mtu = max_t(unsigned int, info->vesw.eth_mtu, - netdev->min_mtu); - if (netdev->mtu > netdev->max_mtu) - dev_set_mtu(netdev, netdev->max_mtu); - rtnl_unlock(); - - /* Update flow to default port redirection table */ - port_mask = info->vesw.def_port_mask; - for (i = 0; i < OPA_VESW_MAX_NUM_DEF_PORT; i++) { - if (port_mask & 1) - port_num[port_count++] = i; - port_mask >>= 1; - } - - /* - * Build the flow table. Flow table is required when destination LID - * is not available. Up to OPA_VNIC_FLOW_TBL_SIZE flows supported. - * Each flow need a default port number to get its dlid from the - * u_ucast_dlid array. - */ - for (i = 0; i < OPA_VNIC_FLOW_TBL_SIZE; i++) - adapter->flow_tbl[i] = port_count ? port_num[i % port_count] : - OPA_VNIC_INVALID_PORT; - - /* update state */ - opa_vnic_update_state(adapter, !!(netdev->flags & IFF_UP)); -} - -/* - * Set the power on default values in adapter's vema interface structure. - */ -static inline void opa_vnic_set_pod_values(struct opa_vnic_adapter *adapter) -{ - adapter->info.vport.max_mac_tbl_ent = OPA_VNIC_MAC_TBL_MAX_ENTRIES; - adapter->info.vport.max_smac_ent = OPA_VNIC_MAX_SMAC_LIMIT; - adapter->info.vport.config_state = OPA_VNIC_STATE_DROP_ALL; - adapter->info.vport.eth_link_status = OPA_VNIC_ETH_LINK_DOWN; - adapter->info.vesw.eth_mtu = ETH_DATA_LEN; -} - -/* opa_vnic_set_mac_addr - change mac address */ -static int opa_vnic_set_mac_addr(struct net_device *netdev, void *addr) -{ - struct opa_vnic_adapter *adapter = opa_vnic_priv(netdev); - struct sockaddr *sa = addr; - int rc; - - if (!memcmp(netdev->dev_addr, sa->sa_data, ETH_ALEN)) - return 0; - - mutex_lock(&adapter->lock); - rc = eth_mac_addr(netdev, addr); - mutex_unlock(&adapter->lock); - if (rc) - return rc; - - adapter->info.vport.uc_macs_gen_count++; - opa_vnic_vema_report_event(adapter, - OPA_VESWPORT_TRAP_IFACE_UCAST_MAC_CHANGE); - return 0; -} - -/* - * opa_vnic_mac_send_event - post event on possible mac list exchange - * Send trap when digest from uc/mc mac list differs from previous run. - * Digest is evaluated similar to how cksum does. - */ -static void opa_vnic_mac_send_event(struct net_device *netdev, u8 event) -{ - struct opa_vnic_adapter *adapter = opa_vnic_priv(netdev); - struct netdev_hw_addr *ha; - struct netdev_hw_addr_list *hw_list; - u32 *ref_crc; - u32 l, crc = 0; - - switch (event) { - case OPA_VESWPORT_TRAP_IFACE_UCAST_MAC_CHANGE: - hw_list = &netdev->uc; - adapter->info.vport.uc_macs_gen_count++; - ref_crc = &adapter->umac_hash; - break; - case OPA_VESWPORT_TRAP_IFACE_MCAST_MAC_CHANGE: - hw_list = &netdev->mc; - adapter->info.vport.mc_macs_gen_count++; - ref_crc = &adapter->mmac_hash; - break; - default: - return; - } - netdev_hw_addr_list_for_each(ha, hw_list) { - crc = crc32_le(crc, ha->addr, ETH_ALEN); - } - l = netdev_hw_addr_list_count(hw_list) * ETH_ALEN; - crc = ~crc32_le(crc, (void *)&l, sizeof(l)); - - if (crc != *ref_crc) { - *ref_crc = crc; - opa_vnic_vema_report_event(adapter, event); - } -} - -/* opa_vnic_set_rx_mode - handle uc/mc mac list change */ -static void opa_vnic_set_rx_mode(struct net_device *netdev) -{ - opa_vnic_mac_send_event(netdev, - OPA_VESWPORT_TRAP_IFACE_UCAST_MAC_CHANGE); - - opa_vnic_mac_send_event(netdev, - OPA_VESWPORT_TRAP_IFACE_MCAST_MAC_CHANGE); -} - -/* opa_netdev_open - activate network interface */ -static int opa_netdev_open(struct net_device *netdev) -{ - struct opa_vnic_adapter *adapter = opa_vnic_priv(netdev); - int rc; - - rc = adapter->rn_ops->ndo_open(adapter->netdev); - if (rc) { - v_dbg("open failed %d\n", rc); - return rc; - } - - /* Update status and send trap */ - opa_vnic_update_state(adapter, true); - opa_vnic_vema_report_event(adapter, - OPA_VESWPORT_TRAP_ETH_LINK_STATUS_CHANGE); - return 0; -} - -/* opa_netdev_close - disable network interface */ -static int opa_netdev_close(struct net_device *netdev) -{ - struct opa_vnic_adapter *adapter = opa_vnic_priv(netdev); - int rc; - - rc = adapter->rn_ops->ndo_stop(adapter->netdev); - if (rc) { - v_dbg("close failed %d\n", rc); - return rc; - } - - /* Update status and send trap */ - opa_vnic_update_state(adapter, false); - opa_vnic_vema_report_event(adapter, - OPA_VESWPORT_TRAP_ETH_LINK_STATUS_CHANGE); - return 0; -} - -/* netdev ops */ -static const struct net_device_ops opa_netdev_ops = { - .ndo_open = opa_netdev_open, - .ndo_stop = opa_netdev_close, - .ndo_start_xmit = opa_netdev_start_xmit, - .ndo_get_stats64 = opa_vnic_get_stats64, - .ndo_set_rx_mode = opa_vnic_set_rx_mode, - .ndo_select_queue = opa_vnic_select_queue, - .ndo_set_mac_address = opa_vnic_set_mac_addr, -}; - -/* opa_vnic_add_netdev - create vnic netdev interface */ -struct opa_vnic_adapter *opa_vnic_add_netdev(struct ib_device *ibdev, - u8 port_num, u8 vport_num) -{ - struct opa_vnic_adapter *adapter; - struct net_device *netdev; - struct rdma_netdev *rn; - int rc; - - netdev = ibdev->ops.alloc_rdma_netdev(ibdev, port_num, - RDMA_NETDEV_OPA_VNIC, - "veth%d", NET_NAME_UNKNOWN, - ether_setup); - if (!netdev) - return ERR_PTR(-ENOMEM); - else if (IS_ERR(netdev)) - return ERR_CAST(netdev); - - rn = netdev_priv(netdev); - adapter = kzalloc_obj(*adapter); - if (!adapter) { - rc = -ENOMEM; - goto adapter_err; - } - - rn->clnt_priv = adapter; - rn->hca = ibdev; - rn->port_num = port_num; - adapter->netdev = netdev; - adapter->ibdev = ibdev; - adapter->port_num = port_num; - adapter->vport_num = vport_num; - adapter->rn_ops = netdev->netdev_ops; - - netdev->netdev_ops = &opa_netdev_ops; - netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE; - netdev->hard_header_len += OPA_VNIC_SKB_HEADROOM; - mutex_init(&adapter->lock); - mutex_init(&adapter->mactbl_lock); - spin_lock_init(&adapter->stats_lock); - - SET_NETDEV_DEV(netdev, ibdev->dev.parent); - - opa_vnic_set_ethtool_ops(netdev); - - opa_vnic_set_pod_values(adapter); - - rc = register_netdev(netdev); - if (rc) - goto netdev_err; - - netif_carrier_off(netdev); - netif_dormant_on(netdev); - v_info("initialized\n"); - - return adapter; -netdev_err: - mutex_destroy(&adapter->lock); - mutex_destroy(&adapter->mactbl_lock); - kfree(adapter); -adapter_err: - rn->free_rdma_netdev(netdev); - - return ERR_PTR(rc); -} - -/* opa_vnic_rem_netdev - remove vnic netdev interface */ -void opa_vnic_rem_netdev(struct opa_vnic_adapter *adapter) -{ - struct net_device *netdev = adapter->netdev; - struct rdma_netdev *rn = netdev_priv(netdev); - - v_info("removing\n"); - unregister_netdev(netdev); - opa_vnic_release_mac_tbl(adapter); - mutex_destroy(&adapter->lock); - mutex_destroy(&adapter->mactbl_lock); - kfree(adapter); - rn->free_rdma_netdev(netdev); -} diff --git a/drivers/infiniband/ulp/opa_vnic/opa_vnic_vema.c b/drivers/infiniband/ulp/opa_vnic/opa_vnic_vema.c deleted file mode 100644 index 21c6cea8b1db..000000000000 --- a/drivers/infiniband/ulp/opa_vnic/opa_vnic_vema.c +++ /dev/null @@ -1,1056 +0,0 @@ -/* - * Copyright(c) 2017 Intel Corporation. - * Copyright(c) 2021 Cornelis Networks. - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* - * This file contains OPX Virtual Network Interface Controller (VNIC) - * Ethernet Management Agent (EMA) driver - */ - -#include -#include -#include -#include -#include -#include - -#include "opa_vnic_internal.h" - -char opa_vnic_driver_name[] = "opa_vnic"; - -/* - * The trap service level is kept in bits 3 to 7 in the trap_sl_rsvd - * field in the class port info MAD. - */ -#define GET_TRAP_SL_FROM_CLASS_PORT_INFO(x) (((x) >> 3) & 0x1f) - -/* Cap trap bursts to a reasonable limit good for normal cases */ -#define OPA_VNIC_TRAP_BURST_LIMIT 4 - -/* - * VNIC trap limit timeout. - * Inverse of cap2_mask response time out (1.0737 secs) = 0.9 - * secs approx IB spec 13.4.6.2.1 PortInfoSubnetTimeout and - * 13.4.9 Traps. - */ -#define OPA_VNIC_TRAP_TIMEOUT ((4096 * (1UL << 18)) / 1000) - -#define OPA_VNIC_UNSUP_ATTR \ - cpu_to_be16(IB_MGMT_MAD_STATUS_UNSUPPORTED_METHOD_ATTRIB) - -#define OPA_VNIC_INVAL_ATTR \ - cpu_to_be16(IB_MGMT_MAD_STATUS_INVALID_ATTRIB_VALUE) - -#define OPA_VNIC_CLASS_CAP_TRAP 0x1 - -/* Maximum number of VNIC ports supported */ -#define OPA_VNIC_MAX_NUM_VPORT 255 - -/** - * struct opa_vnic_vema_port -- VNIC VEMA port details - * @cport: pointer to port - * @mad_agent: pointer to mad agent for port - * @class_port_info: Class port info information. - * @tid: Transaction id - * @port_num: OPA port number - * @vports: vnic ports - * @event_handler: ib event handler - * @lock: adapter interface lock - */ -struct opa_vnic_vema_port { - struct opa_vnic_ctrl_port *cport; - struct ib_mad_agent *mad_agent; - struct opa_class_port_info class_port_info; - u64 tid; - u8 port_num; - struct xarray vports; - struct ib_event_handler event_handler; - - /* Lock to query/update network adapter */ - struct mutex lock; -}; - -static int opa_vnic_vema_add_one(struct ib_device *device); -static void opa_vnic_vema_rem_one(struct ib_device *device, - void *client_data); - -static struct ib_client opa_vnic_client = { - .name = opa_vnic_driver_name, - .add = opa_vnic_vema_add_one, - .remove = opa_vnic_vema_rem_one, -}; - -/** - * vema_get_vport_num -- Get the vnic from the mad - * @recvd_mad: Received mad - * - * Return: returns value of the vnic port number - */ -static inline u8 vema_get_vport_num(struct opa_vnic_vema_mad *recvd_mad) -{ - return be32_to_cpu(recvd_mad->mad_hdr.attr_mod) & 0xff; -} - -/** - * vema_get_vport_adapter -- Get vnic port adapter from recvd mad - * @recvd_mad: received mad - * @port: ptr to port struct on which MAD was recvd - * - * Return: vnic adapter - */ -static inline struct opa_vnic_adapter * -vema_get_vport_adapter(struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_port *port) -{ - u8 vport_num = vema_get_vport_num(recvd_mad); - - return xa_load(&port->vports, vport_num); -} - -/** - * vema_mac_tbl_req_ok -- Check if mac request has correct values - * @mac_tbl: mac table - * - * This function checks for the validity of the offset and number of - * entries required. - * - * Return: true if offset and num_entries are valid - */ -static inline bool vema_mac_tbl_req_ok(struct opa_veswport_mactable *mac_tbl) -{ - u16 offset, num_entries; - u16 req_entries = ((OPA_VNIC_EMA_DATA - sizeof(*mac_tbl)) / - sizeof(mac_tbl->tbl_entries[0])); - - offset = be16_to_cpu(mac_tbl->offset); - num_entries = be16_to_cpu(mac_tbl->num_entries); - - return ((num_entries <= req_entries) && - (offset + num_entries <= OPA_VNIC_MAC_TBL_MAX_ENTRIES)); -} - -/* - * Return the power on default values in the port info structure - * in big endian format as required by MAD. - */ -static inline void vema_get_pod_values(struct opa_veswport_info *port_info) -{ - memset(port_info, 0, sizeof(*port_info)); - port_info->vport.max_mac_tbl_ent = - cpu_to_be16(OPA_VNIC_MAC_TBL_MAX_ENTRIES); - port_info->vport.max_smac_ent = - cpu_to_be16(OPA_VNIC_MAX_SMAC_LIMIT); - port_info->vport.oper_state = OPA_VNIC_STATE_DROP_ALL; - port_info->vport.config_state = OPA_VNIC_STATE_DROP_ALL; - port_info->vesw.eth_mtu = cpu_to_be16(ETH_DATA_LEN); -} - -/** - * vema_add_vport -- Add a new vnic port - * @port: ptr to opa_vnic_vema_port struct - * @vport_num: vnic port number (to be added) - * - * Return a pointer to the vnic adapter structure - */ -static struct opa_vnic_adapter *vema_add_vport(struct opa_vnic_vema_port *port, - u8 vport_num) -{ - struct opa_vnic_ctrl_port *cport = port->cport; - struct opa_vnic_adapter *adapter; - - adapter = opa_vnic_add_netdev(cport->ibdev, port->port_num, vport_num); - if (!IS_ERR(adapter)) { - int rc; - - adapter->cport = cport; - rc = xa_insert(&port->vports, vport_num, adapter, GFP_KERNEL); - if (rc < 0) { - opa_vnic_rem_netdev(adapter); - adapter = ERR_PTR(rc); - } - } - - return adapter; -} - -/** - * vema_get_class_port_info -- Get class info for port - * @port: Port on whic MAD was received - * @recvd_mad: pointer to the received mad - * @rsp_mad: pointer to respose mad - * - * This function copies the latest class port info value set for the - * port and stores it for generating traps - */ -static void vema_get_class_port_info(struct opa_vnic_vema_port *port, - struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_mad *rsp_mad) -{ - struct opa_class_port_info *port_info; - - port_info = (struct opa_class_port_info *)rsp_mad->data; - memcpy(port_info, &port->class_port_info, sizeof(*port_info)); - port_info->base_version = OPA_MGMT_BASE_VERSION; - port_info->class_version = OPA_EMA_CLASS_VERSION; - - /* - * Set capability mask bit indicating agent generates traps, - * and set the maximum number of VNIC ports supported. - */ - port_info->cap_mask = cpu_to_be16((OPA_VNIC_CLASS_CAP_TRAP | - (OPA_VNIC_MAX_NUM_VPORT << 8))); - - /* - * Since a get routine is always sent by the EM first we - * set the expected response time to - * 4.096 usec * 2^18 == 1.0737 sec here. - */ - port_info->cap_mask2_resp_time = cpu_to_be32(18); -} - -/** - * vema_set_class_port_info -- Get class info for port - * @port: Port on whic MAD was received - * @recvd_mad: pointer to the received mad - * @rsp_mad: pointer to respose mad - * - * This function updates the port class info for the specific vnic - * and sets up the response mad data - */ -static void vema_set_class_port_info(struct opa_vnic_vema_port *port, - struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_mad *rsp_mad) -{ - memcpy(&port->class_port_info, recvd_mad->data, - sizeof(port->class_port_info)); - - vema_get_class_port_info(port, recvd_mad, rsp_mad); -} - -/** - * vema_get_veswport_info -- Get veswport info - * @port: source port on which MAD was received - * @recvd_mad: pointer to the received mad - * @rsp_mad: pointer to respose mad - */ -static void vema_get_veswport_info(struct opa_vnic_vema_port *port, - struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_mad *rsp_mad) -{ - struct opa_veswport_info *port_info = - (struct opa_veswport_info *)rsp_mad->data; - struct opa_vnic_adapter *adapter; - - adapter = vema_get_vport_adapter(recvd_mad, port); - if (adapter) { - memset(port_info, 0, sizeof(*port_info)); - opa_vnic_get_vesw_info(adapter, &port_info->vesw); - opa_vnic_get_per_veswport_info(adapter, - &port_info->vport); - } else { - vema_get_pod_values(port_info); - } -} - -/** - * vema_set_veswport_info -- Set veswport info - * @port: source port on which MAD was received - * @recvd_mad: pointer to the received mad - * @rsp_mad: pointer to respose mad - * - * This function gets the port class infor for vnic - */ -static void vema_set_veswport_info(struct opa_vnic_vema_port *port, - struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_mad *rsp_mad) -{ - struct opa_vnic_ctrl_port *cport = port->cport; - struct opa_veswport_info *port_info; - struct opa_vnic_adapter *adapter; - u8 vport_num; - - vport_num = vema_get_vport_num(recvd_mad); - - adapter = vema_get_vport_adapter(recvd_mad, port); - if (!adapter) { - adapter = vema_add_vport(port, vport_num); - if (IS_ERR(adapter)) { - c_err("failed to add vport %d: %ld\n", - vport_num, PTR_ERR(adapter)); - goto err_exit; - } - } - - port_info = (struct opa_veswport_info *)recvd_mad->data; - opa_vnic_set_vesw_info(adapter, &port_info->vesw); - opa_vnic_set_per_veswport_info(adapter, &port_info->vport); - - /* Process the new config settings */ - opa_vnic_process_vema_config(adapter); - - vema_get_veswport_info(port, recvd_mad, rsp_mad); - return; - -err_exit: - rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; -} - -/** - * vema_get_mac_entries -- Get MAC entries in VNIC MAC table - * @port: source port on which MAD was received - * @recvd_mad: pointer to the received mad - * @rsp_mad: pointer to respose mad - * - * This function gets the MAC entries that are programmed into - * the VNIC MAC forwarding table. It checks for the validity of - * the index into the MAC table and the number of entries that - * are to be retrieved. - */ -static void vema_get_mac_entries(struct opa_vnic_vema_port *port, - struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_mad *rsp_mad) -{ - struct opa_veswport_mactable *mac_tbl_in, *mac_tbl_out; - struct opa_vnic_adapter *adapter; - - adapter = vema_get_vport_adapter(recvd_mad, port); - if (!adapter) { - rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; - return; - } - - mac_tbl_in = (struct opa_veswport_mactable *)recvd_mad->data; - mac_tbl_out = (struct opa_veswport_mactable *)rsp_mad->data; - - if (vema_mac_tbl_req_ok(mac_tbl_in)) { - mac_tbl_out->offset = mac_tbl_in->offset; - mac_tbl_out->num_entries = mac_tbl_in->num_entries; - opa_vnic_query_mac_tbl(adapter, mac_tbl_out); - } else { - rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; - } -} - -/** - * vema_set_mac_entries -- Set MAC entries in VNIC MAC table - * @port: source port on which MAD was received - * @recvd_mad: pointer to the received mad - * @rsp_mad: pointer to respose mad - * - * This function sets the MAC entries in the VNIC forwarding table - * It checks for the validity of the index and the number of forwarding - * table entries to be programmed. - */ -static void vema_set_mac_entries(struct opa_vnic_vema_port *port, - struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_mad *rsp_mad) -{ - struct opa_veswport_mactable *mac_tbl; - struct opa_vnic_adapter *adapter; - - adapter = vema_get_vport_adapter(recvd_mad, port); - if (!adapter) { - rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; - return; - } - - mac_tbl = (struct opa_veswport_mactable *)recvd_mad->data; - if (vema_mac_tbl_req_ok(mac_tbl)) { - if (opa_vnic_update_mac_tbl(adapter, mac_tbl)) - rsp_mad->mad_hdr.status = OPA_VNIC_UNSUP_ATTR; - } else { - rsp_mad->mad_hdr.status = OPA_VNIC_UNSUP_ATTR; - } - vema_get_mac_entries(port, recvd_mad, rsp_mad); -} - -/** - * vema_set_delete_vesw -- Reset VESW info to POD values - * @port: source port on which MAD was received - * @recvd_mad: pointer to the received mad - * @rsp_mad: pointer to respose mad - * - * This function clears all the fields of veswport info for the requested vesw - * and sets them back to the power-on default values. It does not delete the - * vesw. - */ -static void vema_set_delete_vesw(struct opa_vnic_vema_port *port, - struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_mad *rsp_mad) -{ - struct opa_veswport_info *port_info = - (struct opa_veswport_info *)rsp_mad->data; - struct opa_vnic_adapter *adapter; - - adapter = vema_get_vport_adapter(recvd_mad, port); - if (!adapter) { - rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; - return; - } - - vema_get_pod_values(port_info); - opa_vnic_set_vesw_info(adapter, &port_info->vesw); - opa_vnic_set_per_veswport_info(adapter, &port_info->vport); - - /* Process the new config settings */ - opa_vnic_process_vema_config(adapter); - - opa_vnic_release_mac_tbl(adapter); - - vema_get_veswport_info(port, recvd_mad, rsp_mad); -} - -/** - * vema_get_mac_list -- Get the unicast/multicast macs. - * @port: source port on which MAD was received - * @recvd_mad: Received mad contains fields to set vnic parameters - * @rsp_mad: Response mad to be built - * @attr_id: Attribute ID indicating multicast or unicast mac list - */ -static void vema_get_mac_list(struct opa_vnic_vema_port *port, - struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_mad *rsp_mad, - u16 attr_id) -{ - struct opa_veswport_iface_macs *macs_in, *macs_out; - int max_entries = (OPA_VNIC_EMA_DATA - sizeof(*macs_out)) / ETH_ALEN; - struct opa_vnic_adapter *adapter; - - adapter = vema_get_vport_adapter(recvd_mad, port); - if (!adapter) { - rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; - return; - } - - macs_in = (struct opa_veswport_iface_macs *)recvd_mad->data; - macs_out = (struct opa_veswport_iface_macs *)rsp_mad->data; - - macs_out->start_idx = macs_in->start_idx; - if (macs_in->num_macs_in_msg) - macs_out->num_macs_in_msg = macs_in->num_macs_in_msg; - else - macs_out->num_macs_in_msg = cpu_to_be16(max_entries); - - if (attr_id == OPA_EM_ATTR_IFACE_MCAST_MACS) - opa_vnic_query_mcast_macs(adapter, macs_out); - else - opa_vnic_query_ucast_macs(adapter, macs_out); -} - -/** - * vema_get_summary_counters -- Gets summary counters. - * @port: source port on which MAD was received - * @recvd_mad: Received mad contains fields to set vnic parameters - * @rsp_mad: Response mad to be built - */ -static void vema_get_summary_counters(struct opa_vnic_vema_port *port, - struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_mad *rsp_mad) -{ - struct opa_veswport_summary_counters *cntrs; - struct opa_vnic_adapter *adapter; - - adapter = vema_get_vport_adapter(recvd_mad, port); - if (adapter) { - cntrs = (struct opa_veswport_summary_counters *)rsp_mad->data; - opa_vnic_get_summary_counters(adapter, cntrs); - } else { - rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; - } -} - -/** - * vema_get_error_counters -- Gets summary counters. - * @port: source port on which MAD was received - * @recvd_mad: Received mad contains fields to set vnic parameters - * @rsp_mad: Response mad to be built - */ -static void vema_get_error_counters(struct opa_vnic_vema_port *port, - struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_mad *rsp_mad) -{ - struct opa_veswport_error_counters *cntrs; - struct opa_vnic_adapter *adapter; - - adapter = vema_get_vport_adapter(recvd_mad, port); - if (adapter) { - cntrs = (struct opa_veswport_error_counters *)rsp_mad->data; - opa_vnic_get_error_counters(adapter, cntrs); - } else { - rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; - } -} - -/** - * vema_get -- Process received get MAD - * @port: source port on which MAD was received - * @recvd_mad: Received mad - * @rsp_mad: Response mad to be built - */ -static void vema_get(struct opa_vnic_vema_port *port, - struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_mad *rsp_mad) -{ - u16 attr_id = be16_to_cpu(recvd_mad->mad_hdr.attr_id); - - switch (attr_id) { - case OPA_EM_ATTR_CLASS_PORT_INFO: - vema_get_class_port_info(port, recvd_mad, rsp_mad); - break; - case OPA_EM_ATTR_VESWPORT_INFO: - vema_get_veswport_info(port, recvd_mad, rsp_mad); - break; - case OPA_EM_ATTR_VESWPORT_MAC_ENTRIES: - vema_get_mac_entries(port, recvd_mad, rsp_mad); - break; - case OPA_EM_ATTR_IFACE_UCAST_MACS: - case OPA_EM_ATTR_IFACE_MCAST_MACS: - vema_get_mac_list(port, recvd_mad, rsp_mad, attr_id); - break; - case OPA_EM_ATTR_VESWPORT_SUMMARY_COUNTERS: - vema_get_summary_counters(port, recvd_mad, rsp_mad); - break; - case OPA_EM_ATTR_VESWPORT_ERROR_COUNTERS: - vema_get_error_counters(port, recvd_mad, rsp_mad); - break; - default: - rsp_mad->mad_hdr.status = OPA_VNIC_UNSUP_ATTR; - break; - } -} - -/** - * vema_set -- Process received set MAD - * @port: source port on which MAD was received - * @recvd_mad: Received mad contains fields to set vnic parameters - * @rsp_mad: Response mad to be built - */ -static void vema_set(struct opa_vnic_vema_port *port, - struct opa_vnic_vema_mad *recvd_mad, - struct opa_vnic_vema_mad *rsp_mad) -{ - u16 attr_id = be16_to_cpu(recvd_mad->mad_hdr.attr_id); - - switch (attr_id) { - case OPA_EM_ATTR_CLASS_PORT_INFO: - vema_set_class_port_info(port, recvd_mad, rsp_mad); - break; - case OPA_EM_ATTR_VESWPORT_INFO: - vema_set_veswport_info(port, recvd_mad, rsp_mad); - break; - case OPA_EM_ATTR_VESWPORT_MAC_ENTRIES: - vema_set_mac_entries(port, recvd_mad, rsp_mad); - break; - case OPA_EM_ATTR_DELETE_VESW: - vema_set_delete_vesw(port, recvd_mad, rsp_mad); - break; - default: - rsp_mad->mad_hdr.status = OPA_VNIC_UNSUP_ATTR; - break; - } -} - -/** - * vema_send -- Send handler for VEMA MAD agent - * @mad_agent: pointer to the mad agent - * @mad_wc: pointer to mad send work completion information - * - * Free all the data structures associated with the sent MAD - */ -static void vema_send(struct ib_mad_agent *mad_agent, - struct ib_mad_send_wc *mad_wc) -{ - rdma_destroy_ah(mad_wc->send_buf->ah, RDMA_DESTROY_AH_SLEEPABLE); - ib_free_send_mad(mad_wc->send_buf); -} - -/** - * vema_recv -- Recv handler for VEMA MAD agent - * @mad_agent: pointer to the mad agent - * @send_buf: Send buffer if found, else NULL - * @mad_wc: pointer to mad send work completion information - * - * Handle only set and get methods and respond to other methods - * as unsupported. Allocate response buffer and address handle - * for the response MAD. - */ -static void vema_recv(struct ib_mad_agent *mad_agent, - struct ib_mad_send_buf *send_buf, - struct ib_mad_recv_wc *mad_wc) -{ - struct opa_vnic_vema_port *port; - struct ib_ah *ah; - struct ib_mad_send_buf *rsp; - struct opa_vnic_vema_mad *vema_mad; - - if (!mad_wc || !mad_wc->recv_buf.mad) - return; - - port = mad_agent->context; - ah = ib_create_ah_from_wc(mad_agent->qp->pd, mad_wc->wc, - mad_wc->recv_buf.grh, mad_agent->port_num); - if (IS_ERR(ah)) - goto free_recv_mad; - - rsp = ib_create_send_mad(mad_agent, mad_wc->wc->src_qp, - mad_wc->wc->pkey_index, 0, - IB_MGMT_VENDOR_HDR, OPA_VNIC_EMA_DATA, - GFP_KERNEL, OPA_MGMT_BASE_VERSION); - if (IS_ERR(rsp)) - goto err_rsp; - - rsp->ah = ah; - vema_mad = rsp->mad; - memcpy(vema_mad, mad_wc->recv_buf.mad, IB_MGMT_VENDOR_HDR); - vema_mad->mad_hdr.method = IB_MGMT_METHOD_GET_RESP; - vema_mad->mad_hdr.status = 0; - - /* Lock ensures network adapter is not removed */ - mutex_lock(&port->lock); - - switch (mad_wc->recv_buf.mad->mad_hdr.method) { - case IB_MGMT_METHOD_GET: - vema_get(port, (struct opa_vnic_vema_mad *)mad_wc->recv_buf.mad, - vema_mad); - break; - case IB_MGMT_METHOD_SET: - vema_set(port, (struct opa_vnic_vema_mad *)mad_wc->recv_buf.mad, - vema_mad); - break; - default: - vema_mad->mad_hdr.status = OPA_VNIC_UNSUP_ATTR; - break; - } - mutex_unlock(&port->lock); - - if (!ib_post_send_mad(rsp, NULL)) { - /* - * with post send successful ah and send mad - * will be destroyed in send handler - */ - goto free_recv_mad; - } - - ib_free_send_mad(rsp); - -err_rsp: - rdma_destroy_ah(ah, RDMA_DESTROY_AH_SLEEPABLE); -free_recv_mad: - ib_free_recv_mad(mad_wc); -} - -/** - * vema_get_port -- Gets the opa_vnic_vema_port - * @cport: pointer to control dev - * @port_num: Port number - * - * This function loops through the ports and returns - * the opa_vnic_vema port structure that is associated - * with the OPA port number - * - * Return: ptr to requested opa_vnic_vema_port strucure - * if success, NULL if not - */ -static struct opa_vnic_vema_port * -vema_get_port(struct opa_vnic_ctrl_port *cport, u8 port_num) -{ - struct opa_vnic_vema_port *port = (void *)cport + sizeof(*cport); - - if (port_num > cport->num_ports) - return NULL; - - return port + (port_num - 1); -} - -/** - * opa_vnic_vema_send_trap -- This function sends a trap to the EM - * @adapter: pointer to vnic adapter - * @data: pointer to trap data filled by calling function - * @lid: issuers lid (encap_slid from vesw_port_info) - * - * This function is called from the VNIC driver to send a trap if there - * is somethng the EM should be notified about. These events currently - * are - * 1) UNICAST INTERFACE MACADDRESS changes - * 2) MULTICAST INTERFACE MACADDRESS changes - * 3) ETHERNET LINK STATUS changes - * While allocating the send mad the remote site qpn used is 1 - * as this is the well known QP. - * - */ -void opa_vnic_vema_send_trap(struct opa_vnic_adapter *adapter, - struct __opa_veswport_trap *data, u32 lid) -{ - struct opa_vnic_ctrl_port *cport = adapter->cport; - struct ib_mad_send_buf *send_buf; - struct opa_vnic_vema_port *port; - struct ib_device *ibp; - struct opa_vnic_vema_mad_trap *trap_mad; - struct opa_class_port_info *class; - struct rdma_ah_attr ah_attr; - struct ib_ah *ah; - struct opa_veswport_trap *trap; - u32 trap_lid; - u16 pkey_idx; - - if (!cport) - goto err_exit; - ibp = cport->ibdev; - port = vema_get_port(cport, data->opaportnum); - if (!port || !port->mad_agent) - goto err_exit; - - if (time_before(jiffies, adapter->trap_timeout)) { - if (adapter->trap_count == OPA_VNIC_TRAP_BURST_LIMIT) { - v_warn("Trap rate exceeded\n"); - goto err_exit; - } else { - adapter->trap_count++; - } - } else { - adapter->trap_count = 0; - } - - class = &port->class_port_info; - /* Set up address handle */ - memset(&ah_attr, 0, sizeof(ah_attr)); - ah_attr.type = rdma_ah_find_type(ibp, port->port_num); - rdma_ah_set_sl(&ah_attr, - GET_TRAP_SL_FROM_CLASS_PORT_INFO(class->trap_sl_rsvd)); - rdma_ah_set_port_num(&ah_attr, port->port_num); - trap_lid = be32_to_cpu(class->trap_lid); - /* - * check for trap lid validity, must not be zero - * The trap sink could change after we fashion the MAD but since traps - * are not guaranteed we won't use a lock as anyway the change will take - * place even with locking. - */ - if (!trap_lid) { - c_err("%s: Invalid dlid\n", __func__); - goto err_exit; - } - - rdma_ah_set_dlid(&ah_attr, trap_lid); - ah = rdma_create_ah(port->mad_agent->qp->pd, &ah_attr, 0); - if (IS_ERR(ah)) { - c_err("%s:Couldn't create new AH = %p\n", __func__, ah); - c_err("%s:dlid = %d, sl = %d, port = %d\n", __func__, - rdma_ah_get_dlid(&ah_attr), rdma_ah_get_sl(&ah_attr), - rdma_ah_get_port_num(&ah_attr)); - goto err_exit; - } - - if (ib_find_pkey(ibp, data->opaportnum, IB_DEFAULT_PKEY_FULL, - &pkey_idx) < 0) { - c_err("%s:full key not found, defaulting to partial\n", - __func__); - if (ib_find_pkey(ibp, data->opaportnum, IB_DEFAULT_PKEY_PARTIAL, - &pkey_idx) < 0) - pkey_idx = 1; - } - - send_buf = ib_create_send_mad(port->mad_agent, 1, pkey_idx, 0, - IB_MGMT_VENDOR_HDR, IB_MGMT_MAD_DATA, - GFP_ATOMIC, OPA_MGMT_BASE_VERSION); - if (IS_ERR(send_buf)) { - c_err("%s:Couldn't allocate send buf\n", __func__); - goto err_sndbuf; - } - - send_buf->ah = ah; - - /* Set up common MAD hdr */ - trap_mad = send_buf->mad; - trap_mad->mad_hdr.base_version = OPA_MGMT_BASE_VERSION; - trap_mad->mad_hdr.mgmt_class = OPA_MGMT_CLASS_INTEL_EMA; - trap_mad->mad_hdr.class_version = OPA_EMA_CLASS_VERSION; - trap_mad->mad_hdr.method = IB_MGMT_METHOD_TRAP; - port->tid++; - trap_mad->mad_hdr.tid = cpu_to_be64(port->tid); - trap_mad->mad_hdr.attr_id = IB_SMP_ATTR_NOTICE; - - /* Set up vendor OUI */ - trap_mad->oui[0] = INTEL_OUI_1; - trap_mad->oui[1] = INTEL_OUI_2; - trap_mad->oui[2] = INTEL_OUI_3; - - /* Setup notice attribute portion */ - trap_mad->notice.gen_type = OPA_INTEL_EMA_NOTICE_TYPE_INFO << 1; - trap_mad->notice.oui_1 = INTEL_OUI_1; - trap_mad->notice.oui_2 = INTEL_OUI_2; - trap_mad->notice.oui_3 = INTEL_OUI_3; - trap_mad->notice.issuer_lid = cpu_to_be32(lid); - - /* copy the actual trap data */ - trap = (struct opa_veswport_trap *)trap_mad->notice.raw_data; - trap->fabric_id = cpu_to_be16(data->fabric_id); - trap->veswid = cpu_to_be16(data->veswid); - trap->veswportnum = cpu_to_be32(data->veswportnum); - trap->opaportnum = cpu_to_be16(data->opaportnum); - trap->veswportindex = data->veswportindex; - trap->opcode = data->opcode; - - /* If successful send set up rate limit timeout else bail */ - if (ib_post_send_mad(send_buf, NULL)) { - ib_free_send_mad(send_buf); - } else { - if (adapter->trap_count) - return; - adapter->trap_timeout = jiffies + - usecs_to_jiffies(OPA_VNIC_TRAP_TIMEOUT); - return; - } - -err_sndbuf: - rdma_destroy_ah(ah, 0); -err_exit: - v_err("Aborting trap\n"); -} - -static void opa_vnic_event(struct ib_event_handler *handler, - struct ib_event *record) -{ - struct opa_vnic_vema_port *port = - container_of(handler, struct opa_vnic_vema_port, event_handler); - struct opa_vnic_ctrl_port *cport = port->cport; - struct opa_vnic_adapter *adapter; - unsigned long index; - - if (record->element.port_num != port->port_num) - return; - - c_dbg("OPA_VNIC received event %d on device %s port %d\n", - record->event, dev_name(&record->device->dev), - record->element.port_num); - - if (record->event != IB_EVENT_PORT_ERR && - record->event != IB_EVENT_PORT_ACTIVE) - return; - - xa_for_each(&port->vports, index, adapter) { - if (record->event == IB_EVENT_PORT_ACTIVE) - netif_carrier_on(adapter->netdev); - else - netif_carrier_off(adapter->netdev); - } -} - -/** - * vema_unregister -- Unregisters agent - * @cport: pointer to control port - * - * This deletes the registration by VEMA for MADs - */ -static void vema_unregister(struct opa_vnic_ctrl_port *cport) -{ - struct opa_vnic_adapter *adapter; - unsigned long index; - int i; - - for (i = 1; i <= cport->num_ports; i++) { - struct opa_vnic_vema_port *port = vema_get_port(cport, i); - - if (!port->mad_agent) - continue; - - /* Lock ensures no MAD is being processed */ - mutex_lock(&port->lock); - xa_for_each(&port->vports, index, adapter) - opa_vnic_rem_netdev(adapter); - mutex_unlock(&port->lock); - - ib_unregister_mad_agent(port->mad_agent); - port->mad_agent = NULL; - mutex_destroy(&port->lock); - xa_destroy(&port->vports); - ib_unregister_event_handler(&port->event_handler); - } -} - -/** - * vema_register -- Registers agent - * @cport: pointer to control port - * - * This function registers the handlers for the VEMA MADs - * - * Return: returns 0 on success. non zero otherwise - */ -static int vema_register(struct opa_vnic_ctrl_port *cport) -{ - struct ib_mad_reg_req reg_req = { - .mgmt_class = OPA_MGMT_CLASS_INTEL_EMA, - .mgmt_class_version = OPA_MGMT_BASE_VERSION, - .oui = { INTEL_OUI_1, INTEL_OUI_2, INTEL_OUI_3 } - }; - int i; - - set_bit(IB_MGMT_METHOD_GET, reg_req.method_mask); - set_bit(IB_MGMT_METHOD_SET, reg_req.method_mask); - - /* register ib event handler and mad agent for each port on dev */ - for (i = 1; i <= cport->num_ports; i++) { - struct opa_vnic_vema_port *port = vema_get_port(cport, i); - int ret; - - port->cport = cport; - port->port_num = i; - - INIT_IB_EVENT_HANDLER(&port->event_handler, - cport->ibdev, opa_vnic_event); - ib_register_event_handler(&port->event_handler); - - xa_init(&port->vports); - mutex_init(&port->lock); - port->mad_agent = ib_register_mad_agent(cport->ibdev, i, - IB_QPT_GSI, ®_req, - IB_MGMT_RMPP_VERSION, - vema_send, vema_recv, - port, 0); - if (IS_ERR(port->mad_agent)) { - ret = PTR_ERR(port->mad_agent); - port->mad_agent = NULL; - mutex_destroy(&port->lock); - vema_unregister(cport); - return ret; - } - } - - return 0; -} - -/** - * opa_vnic_ctrl_config_dev -- This function sends a trap to the EM - * by way of ib_modify_port to indicate support for ethernet on the - * fabric. - * @cport: pointer to control port - * @en: enable or disable ethernet on fabric support - */ -static void opa_vnic_ctrl_config_dev(struct opa_vnic_ctrl_port *cport, bool en) -{ - struct ib_port_modify pm = { 0 }; - int i; - - if (en) - pm.set_port_cap_mask = OPA_CAP_MASK3_IsEthOnFabricSupported; - else - pm.clr_port_cap_mask = OPA_CAP_MASK3_IsEthOnFabricSupported; - - for (i = 1; i <= cport->num_ports; i++) - ib_modify_port(cport->ibdev, i, IB_PORT_OPA_MASK_CHG, &pm); -} - -/** - * opa_vnic_vema_add_one -- Handle new ib device - * @device: ib device pointer - * - * Allocate the vnic control port and initialize it. - */ -static int opa_vnic_vema_add_one(struct ib_device *device) -{ - struct opa_vnic_ctrl_port *cport; - int rc, size = sizeof(*cport); - - if (!rdma_cap_opa_vnic(device)) - return -EOPNOTSUPP; - - size += device->phys_port_cnt * sizeof(struct opa_vnic_vema_port); - cport = kzalloc(size, GFP_KERNEL); - if (!cport) - return -ENOMEM; - - cport->num_ports = device->phys_port_cnt; - cport->ibdev = device; - - /* Initialize opa vnic management agent (vema) */ - rc = vema_register(cport); - if (!rc) - c_info("VNIC client initialized\n"); - - ib_set_client_data(device, &opa_vnic_client, cport); - opa_vnic_ctrl_config_dev(cport, true); - return 0; -} - -/** - * opa_vnic_vema_rem_one -- Handle ib device removal - * @device: ib device pointer - * @client_data: ib client data - * - * Uninitialize and free the vnic control port. - */ -static void opa_vnic_vema_rem_one(struct ib_device *device, - void *client_data) -{ - struct opa_vnic_ctrl_port *cport = client_data; - - c_info("removing VNIC client\n"); - opa_vnic_ctrl_config_dev(cport, false); - vema_unregister(cport); - kfree(cport); -} - -static int __init opa_vnic_init(void) -{ - int rc; - - rc = ib_register_client(&opa_vnic_client); - if (rc) - pr_err("VNIC driver register failed %d\n", rc); - - return rc; -} -module_init(opa_vnic_init); - -static void opa_vnic_deinit(void) -{ - ib_unregister_client(&opa_vnic_client); -} -module_exit(opa_vnic_deinit); - -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_AUTHOR("Cornelis Networks"); -MODULE_DESCRIPTION("Cornelis OPX Virtual Network driver"); diff --git a/drivers/infiniband/ulp/opa_vnic/opa_vnic_vema_iface.c b/drivers/infiniband/ulp/opa_vnic/opa_vnic_vema_iface.c deleted file mode 100644 index 292c037aa239..000000000000 --- a/drivers/infiniband/ulp/opa_vnic/opa_vnic_vema_iface.c +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright(c) 2017 Intel Corporation. - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* - * This file contains OPA VNIC EMA Interface functions. - */ - -#include "opa_vnic_internal.h" - -/** - * opa_vnic_vema_report_event - sent trap to report the specified event - * @adapter: vnic port adapter - * @event: event to be reported - * - * This function calls vema api to sent a trap for the given event. - */ -void opa_vnic_vema_report_event(struct opa_vnic_adapter *adapter, u8 event) -{ - struct __opa_veswport_info *info = &adapter->info; - struct __opa_veswport_trap trap_data; - - trap_data.fabric_id = info->vesw.fabric_id; - trap_data.veswid = info->vesw.vesw_id; - trap_data.veswportnum = info->vport.port_num; - trap_data.opaportnum = adapter->port_num; - trap_data.veswportindex = adapter->vport_num; - trap_data.opcode = event; - - opa_vnic_vema_send_trap(adapter, &trap_data, info->vport.encap_slid); -} - -/** - * opa_vnic_get_summary_counters - get summary counters - * @adapter: vnic port adapter - * @cntrs: pointer to destination summary counters structure - * - * This function populates the summary counters that is maintained by the - * given adapter to destination address provided. - */ -void opa_vnic_get_summary_counters(struct opa_vnic_adapter *adapter, - struct opa_veswport_summary_counters *cntrs) -{ - struct opa_vnic_stats vstats; - __be64 *dst; - u64 *src; - - memset(&vstats, 0, sizeof(vstats)); - spin_lock(&adapter->stats_lock); - adapter->rn_ops->ndo_get_stats64(adapter->netdev, &vstats.netstats); - spin_unlock(&adapter->stats_lock); - - cntrs->vp_instance = cpu_to_be16(adapter->vport_num); - cntrs->vesw_id = cpu_to_be16(adapter->info.vesw.vesw_id); - cntrs->veswport_num = cpu_to_be32(adapter->port_num); - - cntrs->tx_errors = cpu_to_be64(vstats.netstats.tx_errors); - cntrs->rx_errors = cpu_to_be64(vstats.netstats.rx_errors); - cntrs->tx_packets = cpu_to_be64(vstats.netstats.tx_packets); - cntrs->rx_packets = cpu_to_be64(vstats.netstats.rx_packets); - cntrs->tx_bytes = cpu_to_be64(vstats.netstats.tx_bytes); - cntrs->rx_bytes = cpu_to_be64(vstats.netstats.rx_bytes); - - /* - * This loop depends on layout of - * opa_veswport_summary_counters opa_vnic_stats structures. - */ - for (dst = &cntrs->tx_unicast, src = &vstats.tx_grp.unicast; - dst < &cntrs->reserved[0]; dst++, src++) { - *dst = cpu_to_be64(*src); - } -} - -/** - * opa_vnic_get_error_counters - get error counters - * @adapter: vnic port adapter - * @cntrs: pointer to destination error counters structure - * - * This function populates the error counters that is maintained by the - * given adapter to destination address provided. - */ -void opa_vnic_get_error_counters(struct opa_vnic_adapter *adapter, - struct opa_veswport_error_counters *cntrs) -{ - struct opa_vnic_stats vstats; - - memset(&vstats, 0, sizeof(vstats)); - spin_lock(&adapter->stats_lock); - adapter->rn_ops->ndo_get_stats64(adapter->netdev, &vstats.netstats); - spin_unlock(&adapter->stats_lock); - - cntrs->vp_instance = cpu_to_be16(adapter->vport_num); - cntrs->vesw_id = cpu_to_be16(adapter->info.vesw.vesw_id); - cntrs->veswport_num = cpu_to_be32(adapter->port_num); - - cntrs->tx_errors = cpu_to_be64(vstats.netstats.tx_errors); - cntrs->rx_errors = cpu_to_be64(vstats.netstats.rx_errors); - cntrs->tx_dlid_zero = cpu_to_be64(vstats.tx_dlid_zero); - cntrs->tx_drop_state = cpu_to_be64(vstats.tx_drop_state); - cntrs->tx_logic = cpu_to_be64(vstats.netstats.tx_fifo_errors + - vstats.netstats.tx_carrier_errors); - - cntrs->rx_bad_veswid = cpu_to_be64(vstats.netstats.rx_nohandler); - cntrs->rx_runt = cpu_to_be64(vstats.rx_runt); - cntrs->rx_oversize = cpu_to_be64(vstats.rx_oversize); - cntrs->rx_drop_state = cpu_to_be64(vstats.rx_drop_state); - cntrs->rx_logic = cpu_to_be64(vstats.netstats.rx_fifo_errors); -} - -/** - * opa_vnic_get_vesw_info -- Get the vesw information - * @adapter: vnic port adapter - * @info: pointer to destination vesw info structure - * - * This function copies the vesw info that is maintained by the - * given adapter to destination address provided. - */ -void opa_vnic_get_vesw_info(struct opa_vnic_adapter *adapter, - struct opa_vesw_info *info) -{ - struct __opa_vesw_info *src = &adapter->info.vesw; - int i; - - info->fabric_id = cpu_to_be16(src->fabric_id); - info->vesw_id = cpu_to_be16(src->vesw_id); - memcpy(info->rsvd0, src->rsvd0, ARRAY_SIZE(src->rsvd0)); - info->def_port_mask = cpu_to_be16(src->def_port_mask); - memcpy(info->rsvd1, src->rsvd1, ARRAY_SIZE(src->rsvd1)); - info->pkey = cpu_to_be16(src->pkey); - - memcpy(info->rsvd2, src->rsvd2, ARRAY_SIZE(src->rsvd2)); - info->u_mcast_dlid = cpu_to_be32(src->u_mcast_dlid); - for (i = 0; i < OPA_VESW_MAX_NUM_DEF_PORT; i++) - info->u_ucast_dlid[i] = cpu_to_be32(src->u_ucast_dlid[i]); - - info->rc = cpu_to_be32(src->rc); - - memcpy(info->rsvd3, src->rsvd3, ARRAY_SIZE(src->rsvd3)); - info->eth_mtu = cpu_to_be16(src->eth_mtu); - memcpy(info->rsvd4, src->rsvd4, ARRAY_SIZE(src->rsvd4)); -} - -/** - * opa_vnic_set_vesw_info -- Set the vesw information - * @adapter: vnic port adapter - * @info: pointer to vesw info structure - * - * This function updates the vesw info that is maintained by the - * given adapter with vesw info provided. Reserved fields are stored - * and returned back to EM as is. - */ -void opa_vnic_set_vesw_info(struct opa_vnic_adapter *adapter, - struct opa_vesw_info *info) -{ - struct __opa_vesw_info *dst = &adapter->info.vesw; - int i; - - dst->fabric_id = be16_to_cpu(info->fabric_id); - dst->vesw_id = be16_to_cpu(info->vesw_id); - memcpy(dst->rsvd0, info->rsvd0, ARRAY_SIZE(info->rsvd0)); - dst->def_port_mask = be16_to_cpu(info->def_port_mask); - memcpy(dst->rsvd1, info->rsvd1, ARRAY_SIZE(info->rsvd1)); - dst->pkey = be16_to_cpu(info->pkey); - - memcpy(dst->rsvd2, info->rsvd2, ARRAY_SIZE(info->rsvd2)); - dst->u_mcast_dlid = be32_to_cpu(info->u_mcast_dlid); - for (i = 0; i < OPA_VESW_MAX_NUM_DEF_PORT; i++) - dst->u_ucast_dlid[i] = be32_to_cpu(info->u_ucast_dlid[i]); - - dst->rc = be32_to_cpu(info->rc); - - memcpy(dst->rsvd3, info->rsvd3, ARRAY_SIZE(info->rsvd3)); - dst->eth_mtu = be16_to_cpu(info->eth_mtu); - memcpy(dst->rsvd4, info->rsvd4, ARRAY_SIZE(info->rsvd4)); -} - -/** - * opa_vnic_get_per_veswport_info -- Get the vesw per port information - * @adapter: vnic port adapter - * @info: pointer to destination vport info structure - * - * This function copies the vesw per port info that is maintained by the - * given adapter to destination address provided. - * Note that the read only fields are not copied. - */ -void opa_vnic_get_per_veswport_info(struct opa_vnic_adapter *adapter, - struct opa_per_veswport_info *info) -{ - struct __opa_per_veswport_info *src = &adapter->info.vport; - - info->port_num = cpu_to_be32(src->port_num); - info->eth_link_status = src->eth_link_status; - memcpy(info->rsvd0, src->rsvd0, ARRAY_SIZE(src->rsvd0)); - - memcpy(info->base_mac_addr, src->base_mac_addr, - ARRAY_SIZE(info->base_mac_addr)); - info->config_state = src->config_state; - info->oper_state = src->oper_state; - info->max_mac_tbl_ent = cpu_to_be16(src->max_mac_tbl_ent); - info->max_smac_ent = cpu_to_be16(src->max_smac_ent); - info->mac_tbl_digest = cpu_to_be32(src->mac_tbl_digest); - memcpy(info->rsvd1, src->rsvd1, ARRAY_SIZE(src->rsvd1)); - - info->encap_slid = cpu_to_be32(src->encap_slid); - memcpy(info->pcp_to_sc_uc, src->pcp_to_sc_uc, - ARRAY_SIZE(info->pcp_to_sc_uc)); - memcpy(info->pcp_to_vl_uc, src->pcp_to_vl_uc, - ARRAY_SIZE(info->pcp_to_vl_uc)); - memcpy(info->pcp_to_sc_mc, src->pcp_to_sc_mc, - ARRAY_SIZE(info->pcp_to_sc_mc)); - memcpy(info->pcp_to_vl_mc, src->pcp_to_vl_mc, - ARRAY_SIZE(info->pcp_to_vl_mc)); - info->non_vlan_sc_uc = src->non_vlan_sc_uc; - info->non_vlan_vl_uc = src->non_vlan_vl_uc; - info->non_vlan_sc_mc = src->non_vlan_sc_mc; - info->non_vlan_vl_mc = src->non_vlan_vl_mc; - memcpy(info->rsvd2, src->rsvd2, ARRAY_SIZE(src->rsvd2)); - - info->uc_macs_gen_count = cpu_to_be16(src->uc_macs_gen_count); - info->mc_macs_gen_count = cpu_to_be16(src->mc_macs_gen_count); - memcpy(info->rsvd3, src->rsvd3, ARRAY_SIZE(src->rsvd3)); -} - -/** - * opa_vnic_set_per_veswport_info -- Set vesw per port information - * @adapter: vnic port adapter - * @info: pointer to vport info structure - * - * This function updates the vesw per port info that is maintained by the - * given adapter with vesw per port info provided. Reserved fields are - * stored and returned back to EM as is. - */ -void opa_vnic_set_per_veswport_info(struct opa_vnic_adapter *adapter, - struct opa_per_veswport_info *info) -{ - struct __opa_per_veswport_info *dst = &adapter->info.vport; - - dst->port_num = be32_to_cpu(info->port_num); - memcpy(dst->rsvd0, info->rsvd0, ARRAY_SIZE(info->rsvd0)); - - memcpy(dst->base_mac_addr, info->base_mac_addr, - ARRAY_SIZE(dst->base_mac_addr)); - dst->config_state = info->config_state; - memcpy(dst->rsvd1, info->rsvd1, ARRAY_SIZE(info->rsvd1)); - - dst->encap_slid = be32_to_cpu(info->encap_slid); - memcpy(dst->pcp_to_sc_uc, info->pcp_to_sc_uc, - ARRAY_SIZE(dst->pcp_to_sc_uc)); - memcpy(dst->pcp_to_vl_uc, info->pcp_to_vl_uc, - ARRAY_SIZE(dst->pcp_to_vl_uc)); - memcpy(dst->pcp_to_sc_mc, info->pcp_to_sc_mc, - ARRAY_SIZE(dst->pcp_to_sc_mc)); - memcpy(dst->pcp_to_vl_mc, info->pcp_to_vl_mc, - ARRAY_SIZE(dst->pcp_to_vl_mc)); - dst->non_vlan_sc_uc = info->non_vlan_sc_uc; - dst->non_vlan_vl_uc = info->non_vlan_vl_uc; - dst->non_vlan_sc_mc = info->non_vlan_sc_mc; - dst->non_vlan_vl_mc = info->non_vlan_vl_mc; - memcpy(dst->rsvd2, info->rsvd2, ARRAY_SIZE(info->rsvd2)); - memcpy(dst->rsvd3, info->rsvd3, ARRAY_SIZE(info->rsvd3)); -} - -/** - * opa_vnic_query_mcast_macs - query multicast mac list - * @adapter: vnic port adapter - * @macs: pointer mac list - * - * This function populates the provided mac list with the configured - * multicast addresses in the adapter. - */ -void opa_vnic_query_mcast_macs(struct opa_vnic_adapter *adapter, - struct opa_veswport_iface_macs *macs) -{ - u16 start_idx, num_macs, idx = 0, count = 0; - struct netdev_hw_addr *ha; - - start_idx = be16_to_cpu(macs->start_idx); - num_macs = be16_to_cpu(macs->num_macs_in_msg); - netdev_for_each_mc_addr(ha, adapter->netdev) { - struct opa_vnic_iface_mac_entry *entry = &macs->entry[count]; - - if (start_idx > idx++) - continue; - else if (num_macs == count) - break; - memcpy(entry, ha->addr, sizeof(*entry)); - count++; - } - - macs->tot_macs_in_lst = cpu_to_be16(netdev_mc_count(adapter->netdev)); - macs->num_macs_in_msg = cpu_to_be16(count); - macs->gen_count = cpu_to_be16(adapter->info.vport.mc_macs_gen_count); -} - -/** - * opa_vnic_query_ucast_macs - query unicast mac list - * @adapter: vnic port adapter - * @macs: pointer mac list - * - * This function populates the provided mac list with the configured - * unicast addresses in the adapter. - */ -void opa_vnic_query_ucast_macs(struct opa_vnic_adapter *adapter, - struct opa_veswport_iface_macs *macs) -{ - u16 start_idx, tot_macs, num_macs, idx = 0, count = 0, em_macs = 0; - struct netdev_hw_addr *ha; - - start_idx = be16_to_cpu(macs->start_idx); - num_macs = be16_to_cpu(macs->num_macs_in_msg); - /* loop through dev_addrs list first */ - for_each_dev_addr(adapter->netdev, ha) { - struct opa_vnic_iface_mac_entry *entry = &macs->entry[count]; - - /* Do not include EM specified MAC address */ - if (!memcmp(adapter->info.vport.base_mac_addr, ha->addr, - ARRAY_SIZE(adapter->info.vport.base_mac_addr))) { - em_macs++; - continue; - } - - if (start_idx > idx++) - continue; - else if (num_macs == count) - break; - memcpy(entry, ha->addr, sizeof(*entry)); - count++; - } - - /* loop through uc list */ - netdev_for_each_uc_addr(ha, adapter->netdev) { - struct opa_vnic_iface_mac_entry *entry = &macs->entry[count]; - - if (start_idx > idx++) - continue; - else if (num_macs == count) - break; - memcpy(entry, ha->addr, sizeof(*entry)); - count++; - } - - tot_macs = netdev_hw_addr_list_count(&adapter->netdev->dev_addrs) + - netdev_uc_count(adapter->netdev) - em_macs; - macs->tot_macs_in_lst = cpu_to_be16(tot_macs); - macs->num_macs_in_msg = cpu_to_be16(count); - macs->gen_count = cpu_to_be16(adapter->info.vport.uc_macs_gen_count); -} diff --git a/drivers/infiniband/ulp/rtrs/rtrs-clt.c b/drivers/infiniband/ulp/rtrs/rtrs-clt.c index 3362362f9e2e..e351552733df 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-clt.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-clt.c @@ -3219,7 +3219,7 @@ static int __init rtrs_client_init(void) pr_err("Failed to create rtrs-client dev class\n"); return ret; } - rtrs_wq = alloc_workqueue("rtrs_client_wq", 0, 0); + rtrs_wq = alloc_workqueue("rtrs_client_wq", WQ_PERCPU, 0); if (!rtrs_wq) { class_unregister(&rtrs_clt_dev_class); return -ENOMEM; diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv.c b/drivers/infiniband/ulp/rtrs/rtrs-srv.c index 0140bfaed721..6482ad859bd1 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv.c @@ -2385,7 +2385,7 @@ static int __init rtrs_server_init(void) if (err) goto out_err; - rtrs_wq = alloc_workqueue("rtrs_server_wq", 0, 0); + rtrs_wq = alloc_workqueue("rtrs_server_wq", WQ_PERCPU, 0); if (!rtrs_wq) { err = -ENOMEM; goto out_dev_class; diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index e00b87acf481..9aec5d80117f 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -3925,6 +3925,7 @@ static const struct target_core_fabric_ops srpt_template = { .tfc_wwn_attrs = srpt_wwn_attrs, .tfc_tpg_attrib_attrs = srpt_tpg_attrib_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_DIRECT_SUBMIT, .direct_submit_supp = 1, }; diff --git a/drivers/input/input.c b/drivers/input/input.c index 7b7490a1fbc6..39d9d2b1e3ca 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -800,14 +800,30 @@ static int input_default_getkeycode(struct input_dev *dev, return 0; } -static int input_default_setkeycode(struct input_dev *dev, - const struct input_keymap_entry *ke, - unsigned int *old_keycode) +/** + * input_default_setkeycode - default setkeycode method + * @dev: input device which keymap is being updated. + * @ke: new keymap entry. + * @old_keycode: pointer to the location where old keycode should be stored. + * + * This function is the default implementation of &input_dev.setkeycode() + * method. It is typically used when a driver does not provide its own + * implementation, but it is also exported so drivers can extend it. + * + * The function must be called with &input_dev.event_lock held. + * + * Return: 0 on success, or a negative error code on failure. + */ +int input_default_setkeycode(struct input_dev *dev, + const struct input_keymap_entry *ke, + unsigned int *old_keycode) { unsigned int index; int error; int i; + lockdep_assert_held(&dev->event_lock); + if (!dev->keycodesize) return -EINVAL; @@ -861,6 +877,7 @@ static int input_default_setkeycode(struct input_dev *dev, __set_bit(ke->keycode, dev->keybit); return 0; } +EXPORT_SYMBOL(input_default_setkeycode); /** * input_get_keycode - retrieve keycode currently mapped to a given scancode diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c index 5a1cdce0bc48..1d843115d674 100644 --- a/drivers/input/joystick/gf2k.c +++ b/drivers/input/joystick/gf2k.c @@ -165,8 +165,10 @@ static void gf2k_read(struct gf2k *gf2k, unsigned char *data) t = GB(40,4,0); - for (i = 0; i < gf2k_hats[gf2k->id]; i++) - input_report_abs(dev, ABS_HAT0X + i, gf2k_hat_to_axis[t][i]); + if (t < ARRAY_SIZE(gf2k_hat_to_axis)) + for (i = 0; i < gf2k_hats[gf2k->id]; i++) + input_report_abs(dev, ABS_HAT0X + i, + gf2k_hat_to_axis[t][i]); t = GB(44,2,0) | GB(32,8,2) | GB(78,2,10); diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index d6fc3d6006bb..0549fdc5a985 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -25,40 +25,6 @@ * - Greg Kroah-Hartman - usb-skeleton driver * - Xbox Linux project - extra USB IDs * - Pekka Pöyry (quantus) - Xbox One controller reverse-engineering - * - * TODO: - * - fine tune axes (especially trigger axes) - * - fix "analog" buttons (reported as digital now) - * - get rumble working - * - need USB IDs for other dance pads - * - * History: - * - * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller" - * - * 2002-07-02 - 0.0.2 : basic working version - * - all axes and 9 of the 10 buttons work (german InterAct device) - * - the black button does not work - * - * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik - * - indentation fixes - * - usb + input init sequence fixes - * - * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3 - * - verified the lack of HID and report descriptors - * - verified that ALL buttons WORK - * - fixed d-pad to axes mapping - * - * 2002-07-17 - 0.0.5 : simplified d-pad handling - * - * 2004-10-02 - 0.0.6 : DDR pad support - * - borrowed from the Xbox Linux kernel - * - USB id's for commonly used dance pads are present - * - dance pads will map D-PAD to buttons, not axes - * - pass the module paramater 'dpad_to_buttons' to force - * the D-PAD to map to buttons if your pad is not detected - * - * Later changes can be tracked in SCM. */ #include @@ -590,6 +556,7 @@ static const struct usb_device_id xpad_table[] = { XPAD_XBOX360_VENDOR(0x3651), /* CRKD Controllers */ XPAD_XBOXONE_VENDOR(0x366c), /* ByoWave controllers */ XPAD_XBOX360_VENDOR(0x37d7), /* Flydigi Controllers */ + XPAD_XBOX360_VENDOR(0x3958), /* RedOctane Games Controllers */ XPAD_XBOX360_VENDOR(0x413d), /* Black Shark Green Ghost Controller */ { } }; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 2ff4fef322c2..9d1019ba0245 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -289,6 +289,20 @@ config KEYBOARD_MATRIX To compile this driver as a module, choose M here: the module will be called matrix_keypad. +config KEYBOARD_CHARLIEPLEX + tristate "GPIO driven charlieplex keypad support" + depends on GPIOLIB || COMPILE_TEST + select INPUT_MATRIXKMAP + help + Enable support for GPIO driven charlieplex keypad. A charlieplex + keypad allows to use fewer GPIO lines to interface to key switches. + For example, an N lines charlieplex keypad can be used to interface + to N^2-N different key switches. However, this type of keypad + cannot detect more than one key press at a time. + + To compile this driver as a module, choose M here: the + module will be called charlieplex_keypad. + config KEYBOARD_HIL_OLD tristate "HP HIL keyboard support (simple driver)" depends on GSC || HP300 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 2d906e14f3e2..60bb7baf802f 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o +obj-$(CONFIG_KEYBOARD_CHARLIEPLEX) += charlieplex_keypad.o obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o obj-$(CONFIG_KEYBOARD_CYPRESS_SF) += cypress-sf.o diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 840cdf5878dc..c8ad55f26ea8 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -3,10 +3,7 @@ * AT and PS/2 keyboard driver * * Copyright (c) 1999-2002 Vojtech Pavlik - */ - - -/* + * * This driver can handle standard AT keyboards and PS/2 keyboards in * Translated and Raw Set 2 and Set 3, as well as AT keyboards on dumb * input-only controllers and AT keyboards connected over a one way RS232 @@ -65,8 +62,8 @@ static bool atkbd_terminal; module_param_named(terminal, atkbd_terminal, bool, 0); MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2"); -#define SCANCODE(keymap) ((keymap >> 16) & 0xFFFF) -#define KEYCODE(keymap) (keymap & 0xFFFF) +#define SCANCODE(keymap) (((keymap) >> 16) & 0xFFFF) +#define KEYCODE(keymap) ((keymap) & 0xFFFF) /* * Scancode to keycode tables. These are just the default setting, and @@ -76,7 +73,6 @@ MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard conne #define ATKBD_KEYMAP_SIZE 512 static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { - #ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES /* XXX: need a more general approach */ @@ -107,7 +103,6 @@ static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { }; static const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = { - 0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60, 131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62, 134, 46, 45, 32, 18, 5, 4, 63,135, 57, 47, 33, 20, 19, 6, 64, @@ -122,15 +117,15 @@ static const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = { 148,149,147,140 }; -static const unsigned short atkbd_unxlate_table[128] = { - 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, - 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, - 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, - 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, - 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, - 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, - 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, - 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 +static const u8 atkbd_unxlate_table[128] = { + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 }; #define ATKBD_CMD_SETLEDS 0x10ed @@ -184,7 +179,7 @@ static const unsigned short atkbd_unxlate_table[128] = { static const struct { unsigned short keycode; - unsigned char set2; + u8 set2; } atkbd_scroll_keys[] = { { ATKBD_SCR_1, 0xc5 }, { ATKBD_SCR_2, 0x9d }, @@ -200,7 +195,6 @@ static const struct { */ struct atkbd { - struct ps2dev ps2dev; struct input_dev *dev; @@ -211,7 +205,7 @@ struct atkbd { unsigned short id; unsigned short keycode[ATKBD_KEYMAP_SIZE]; DECLARE_BITMAP(force_release_mask, ATKBD_KEYMAP_SIZE); - unsigned char set; + u8 set; bool translated; bool extra; bool write; @@ -221,7 +215,7 @@ struct atkbd { bool enabled; /* Accessed only from interrupt */ - unsigned char emul; + u8 emul; bool resend; bool release; unsigned long xl_bit; @@ -253,9 +247,9 @@ static unsigned int (*atkbd_platform_scancode_fixup)(struct atkbd *, unsigned in static bool atkbd_skip_deactivate; static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, - ssize_t (*handler)(struct atkbd *, char *)); + ssize_t (*handler)(struct atkbd *, char *)); static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, - ssize_t (*handler)(struct atkbd *, const char *, size_t)); + ssize_t (*handler)(struct atkbd *, const char *, size_t)); #define ATKBD_DEFINE_ATTR(_name) \ static ssize_t atkbd_show_##_name(struct atkbd *, char *); \ static ssize_t atkbd_set_##_name(struct atkbd *, const char *, size_t); \ @@ -270,7 +264,7 @@ static ssize_t atkbd_do_set_##_name(struct device *d, \ return atkbd_attr_set_helper(d, b, s, atkbd_set_##_name); \ } \ static struct device_attribute atkbd_attr_##_name = \ - __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name); + __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name) ATKBD_DEFINE_ATTR(extra); ATKBD_DEFINE_ATTR(force_release); @@ -287,7 +281,7 @@ static ssize_t atkbd_do_show_##_name(struct device *d, \ return atkbd_attr_show_helper(d, b, atkbd_show_##_name); \ } \ static struct device_attribute atkbd_attr_##_name = \ - __ATTR(_name, S_IRUGO, atkbd_do_show_##_name, NULL); + __ATTR(_name, S_IRUGO, atkbd_do_show_##_name, NULL) ATKBD_DEFINE_RO_ATTR(err_count); ATKBD_DEFINE_RO_ATTR(function_row_physmap); @@ -317,7 +311,7 @@ static struct atkbd *atkbd_from_serio(struct serio *serio) } static umode_t atkbd_attr_is_visible(struct kobject *kobj, - struct attribute *attr, int i) + struct attribute *attr, int i) { struct device *dev = kobj_to_dev(kobj); struct serio *serio = to_serio_port(dev); @@ -337,7 +331,7 @@ static const struct attribute_group atkbd_attribute_group = { __ATTRIBUTE_GROUPS(atkbd_attribute); -static const unsigned int xl_table[] = { +static const u8 xl_table[] = { ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK, ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL, }; @@ -346,7 +340,7 @@ static const unsigned int xl_table[] = { * Checks if we should mangle the scancode to extract 'release' bit * in translated mode. */ -static bool atkbd_need_xlate(unsigned long xl_bit, unsigned char code) +static bool atkbd_need_xlate(unsigned long xl_bit, u8 code) { int i; @@ -365,7 +359,7 @@ static bool atkbd_need_xlate(unsigned long xl_bit, unsigned char code) * between make/break pair of scancodes for select keys and PS/2 * protocol responses. */ -static void atkbd_calculate_xl_bit(struct atkbd *atkbd, unsigned char code) +static void atkbd_calculate_xl_bit(struct atkbd *atkbd, u8 code) { int i; @@ -389,7 +383,7 @@ static unsigned int atkbd_compat_scancode(struct atkbd *atkbd, unsigned int code if (atkbd->set == 3) { if (atkbd->emul == 1) code |= 0x100; - } else { + } else { code = (code & 0x7f) | ((code & 0x80) << 1); if (atkbd->emul == 1) code |= 0x80; @@ -431,7 +425,7 @@ static enum ps2_disposition atkbd_pre_receive_byte(struct ps2dev *ps2dev, dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, flags); -#if !defined(__i386__) && !defined (__x86_64__) +#if !defined(__i386__) && !defined(__x86_64__) if (atkbd_handle_frame_error(ps2dev, data, flags)) return PS2_IGNORE; #endif @@ -460,7 +454,6 @@ static void atkbd_receive_byte(struct ps2dev *ps2dev, u8 data) code = atkbd_platform_scancode_fixup(atkbd, code); if (atkbd->translated) { - if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) { atkbd->release = code >> 7; code &= 0x7f; @@ -486,11 +479,9 @@ static void atkbd_receive_byte(struct ps2dev *ps2dev, u8 data) return; case ATKBD_RET_ACK: case ATKBD_RET_NAK: - if (printk_ratelimit()) - dev_warn(&serio->dev, - "Spurious %s on %s. " - "Some program might be trying to access hardware directly.\n", - data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); + dev_warn_ratelimited(&serio->dev, + "Spurious %s on %s. Some program might be trying to access hardware directly.\n", + data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); return; case ATKBD_RET_ERR: atkbd->err_count++; @@ -582,14 +573,14 @@ static void atkbd_receive_byte(struct ps2dev *ps2dev, u8 data) static int atkbd_set_repeat_rate(struct atkbd *atkbd) { - const short period[32] = - { 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125, - 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 }; - const short delay[4] = - { 250, 500, 750, 1000 }; + const short period[32] = { + 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125, + 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 + }; + const short delay[4] = { 250, 500, 750, 1000 }; struct input_dev *dev = atkbd->dev; - unsigned char param; + u8 param; int i = 0, j = 0; while (i < ARRAY_SIZE(period) - 1 && period[i] < dev->rep[REP_PERIOD]) @@ -607,7 +598,7 @@ static int atkbd_set_repeat_rate(struct atkbd *atkbd) static int atkbd_set_leds(struct atkbd *atkbd) { struct input_dev *dev = atkbd->dev; - unsigned char param[2]; + u8 param[2]; param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0) | (test_bit(LED_NUML, dev->led) ? 2 : 0) @@ -648,8 +639,7 @@ static void atkbd_event_work(struct work_struct *work) * it may not be ready yet. In this case we need to keep * rescheduling till reconnect completes. */ - schedule_delayed_work(&atkbd->event_work, - msecs_to_jiffies(100)); + schedule_delayed_work(&atkbd->event_work, msecs_to_jiffies(100)); } else { if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) atkbd_set_leds(atkbd); @@ -683,7 +673,7 @@ static void atkbd_schedule_event_work(struct atkbd *atkbd, int event_bit) */ static int atkbd_event(struct input_dev *dev, - unsigned int type, unsigned int code, int value) + unsigned int type, unsigned int code, int value) { struct atkbd *atkbd = input_get_drvdata(dev); @@ -691,7 +681,6 @@ static int atkbd_event(struct input_dev *dev, return -1; switch (type) { - case EV_LED: atkbd_schedule_event_work(atkbd, ATKBD_LED_EVENT_BIT); return 0; @@ -808,7 +797,7 @@ static inline bool atkbd_skip_getid(struct atkbd *atkbd) { return false; } static int atkbd_probe(struct atkbd *atkbd) { struct ps2dev *ps2dev = &atkbd->ps2dev; - unsigned char param[2]; + u8 param[2]; /* * Some systems, where the bit-twiddling when testing the io-lines of the @@ -836,7 +825,6 @@ static int atkbd_probe(struct atkbd *atkbd) param[0] = param[1] = 0xa5; /* initialize with invalid values */ if (ps2_command(ps2dev, param, ATKBD_CMD_GETID)) { - /* * If the get ID command failed, we check if we can at least set * the LEDs on the keyboard. This should work on every keyboard out there. @@ -856,8 +844,7 @@ static int atkbd_probe(struct atkbd *atkbd) if (atkbd->id == 0xaca1 && atkbd->translated) { dev_err(&ps2dev->serio->dev, - "NCD terminal keyboards are only supported on non-translating controllers. " - "Use i8042.direct=1 to disable translation.\n"); + "NCD terminal keyboards are only supported on non-translating controllers. Use i8042.direct=1 to disable translation.\n"); return -1; } @@ -881,7 +868,7 @@ deactivate_kbd: static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra) { struct ps2dev *ps2dev = &atkbd->ps2dev; - unsigned char param[2]; + u8 param[2]; atkbd->extra = false; /* @@ -941,8 +928,8 @@ static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra static int atkbd_reset_state(struct atkbd *atkbd) { - struct ps2dev *ps2dev = &atkbd->ps2dev; - unsigned char param[1]; + struct ps2dev *ps2dev = &atkbd->ps2dev; + u8 param[1]; /* * Set the LEDs to a predefined state (all off). @@ -967,7 +954,6 @@ static int atkbd_reset_state(struct atkbd *atkbd) * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a * reboot. */ - static void atkbd_cleanup(struct serio *serio) { struct atkbd *atkbd = atkbd_from_serio(serio); @@ -976,11 +962,9 @@ static void atkbd_cleanup(struct serio *serio) ps2_command(&atkbd->ps2dev, NULL, ATKBD_CMD_RESET_DEF); } - /* * atkbd_disconnect() closes and frees. */ - static void atkbd_disconnect(struct serio *serio) { struct atkbd *atkbd = atkbd_from_serio(serio); @@ -1005,8 +989,7 @@ static void atkbd_disconnect(struct serio *serio) /* * generate release events for the keycodes given in data */ -static void atkbd_apply_forced_release_keylist(struct atkbd* atkbd, - const void *data) +static void atkbd_apply_forced_release_keylist(struct atkbd *atkbd, const void *data) { const unsigned int *keys = data; unsigned int i; @@ -1088,7 +1071,6 @@ static int atkbd_get_keymap_from_fwnode(struct atkbd *atkbd) { struct device *dev = &atkbd->ps2dev.serio->dev; int i, n; - u32 *ptr; u16 scancode, keycode; /* Parse "linux,keymap" property */ @@ -1096,13 +1078,12 @@ static int atkbd_get_keymap_from_fwnode(struct atkbd *atkbd) if (n <= 0 || n > ATKBD_KEYMAP_SIZE) return -ENXIO; - ptr = kcalloc(n, sizeof(u32), GFP_KERNEL); + u32 *ptr __free(kfree) = kcalloc(n, sizeof(*ptr), GFP_KERNEL); if (!ptr) return -ENOMEM; if (device_property_read_u32_array(dev, "linux,keymap", ptr, n)) { dev_err(dev, "problem parsing FW keymap property\n"); - kfree(ptr); return -EINVAL; } @@ -1110,10 +1091,14 @@ static int atkbd_get_keymap_from_fwnode(struct atkbd *atkbd) for (i = 0; i < n; i++) { scancode = SCANCODE(ptr[i]); keycode = KEYCODE(ptr[i]); + if (scancode >= ATKBD_KEYMAP_SIZE) { + dev_warn(dev, "invalid scancode %#x in FW keymap entry %d\n", + scancode, i); + return -EINVAL; + } atkbd->keycode[scancode] = keycode; } - kfree(ptr); return 0; } @@ -1235,7 +1220,7 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd) } input_dev->keycode = atkbd->keycode; - input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodesize = sizeof(atkbd->keycode[0]); input_dev->keycodemax = ARRAY_SIZE(atkbd_set2_keycode); for (i = 0; i < ATKBD_KEYMAP_SIZE; i++) { @@ -1289,7 +1274,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) mutex_init(&atkbd->mutex); switch (serio->id.type) { - case SERIO_8042_XL: atkbd->translated = true; fallthrough; @@ -1314,7 +1298,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) goto fail2; if (atkbd->write) { - if (atkbd_probe(atkbd)) { err = -ENODEV; goto fail3; @@ -1354,7 +1337,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) * atkbd_reconnect() tries to restore keyboard into a sane state and is * most likely called on resume. */ - static int atkbd_reconnect(struct serio *serio) { struct atkbd *atkbd = atkbd_from_serio(serio); @@ -1389,7 +1371,6 @@ static int atkbd_reconnect(struct serio *serio) atkbd_set_leds(atkbd); if (!atkbd->softrepeat) atkbd_set_repeat_rate(atkbd); - } /* @@ -1445,7 +1426,7 @@ static struct serio_driver atkbd_drv = { }; static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, - ssize_t (*handler)(struct atkbd *, char *)) + ssize_t (*handler)(struct atkbd *, char *)) { struct serio *serio = to_serio_port(dev); struct atkbd *atkbd = atkbd_from_serio(serio); @@ -1454,7 +1435,7 @@ static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, } static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, - ssize_t (*handler)(struct atkbd *, const char *, size_t)) + ssize_t (*handler)(struct atkbd *, const char *, size_t)) { struct serio *serio = to_serio_port(dev); struct atkbd *atkbd = atkbd_from_serio(serio); @@ -1482,7 +1463,7 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun unsigned int value; int err; bool old_extra; - unsigned char old_set; + u8 old_set; if (!atkbd->write) return -EIO; @@ -1527,8 +1508,8 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun return err; } input_unregister_device(old_dev); - } + return count; } @@ -1544,7 +1525,7 @@ static ssize_t atkbd_show_force_release(struct atkbd *atkbd, char *buf) } static ssize_t atkbd_set_force_release(struct atkbd *atkbd, - const char *buf, size_t count) + const char *buf, size_t count) { /* 64 bytes on stack should be acceptable */ DECLARE_BITMAP(new_mask, ATKBD_KEYMAP_SIZE); @@ -1558,7 +1539,6 @@ static ssize_t atkbd_set_force_release(struct atkbd *atkbd, return count; } - static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf) { return sprintf(buf, "%d\n", atkbd->scroll ? 1 : 0); @@ -1617,7 +1597,7 @@ static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count) struct input_dev *old_dev, *new_dev; unsigned int value; int err; - unsigned char old_set; + u8 old_set; bool old_extra; if (!atkbd->write) @@ -1715,7 +1695,6 @@ static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t return count; } - static ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf) { return sprintf(buf, "%d\n", atkbd->softraw ? 1 : 0); diff --git a/drivers/input/keyboard/charlieplex_keypad.c b/drivers/input/keyboard/charlieplex_keypad.c new file mode 100644 index 000000000000..6dbb5c183f02 --- /dev/null +++ b/drivers/input/keyboard/charlieplex_keypad.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * GPIO driven charlieplex keypad driver + * + * Copyright (c) 2026 Hugo Villeneuve + * + * Based on matrix_keyboard.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct charlieplex_keypad { + struct input_dev *input_dev; + struct gpio_descs *line_gpios; + unsigned int nlines; + unsigned int settling_time_us; + unsigned int debounce_threshold; + unsigned int debounce_count; + int debounce_code; + int current_code; +}; + +static void charlieplex_keypad_report_key(struct input_dev *input) +{ + struct charlieplex_keypad *keypad = input_get_drvdata(input); + const unsigned short *keycodes = input->keycode; + + if (keypad->current_code > 0) { + input_event(input, EV_MSC, MSC_SCAN, keypad->current_code); + input_report_key(input, keycodes[keypad->current_code], 0); + input_sync(input); + } + + if (keypad->debounce_code) { + input_event(input, EV_MSC, MSC_SCAN, keypad->debounce_code); + input_report_key(input, keycodes[keypad->debounce_code], 1); + input_sync(input); + } + + keypad->current_code = keypad->debounce_code; +} + +static void charlieplex_keypad_check_switch_change(struct input_dev *input, + unsigned int code) +{ + struct charlieplex_keypad *keypad = input_get_drvdata(input); + + if (code != keypad->debounce_code) { + keypad->debounce_count = 0; + keypad->debounce_code = code; + } + + if (keypad->debounce_code != keypad->current_code) { + if (keypad->debounce_count++ >= keypad->debounce_threshold) + charlieplex_keypad_report_key(input); + } +} + +static int charlieplex_keypad_scan_line(struct charlieplex_keypad *keypad, + unsigned int oline) +{ + struct gpio_descs *line_gpios = keypad->line_gpios; + DECLARE_BITMAP(values, MATRIX_MAX_ROWS); + int err; + + /* Activate only one line as output at a time. */ + gpiod_direction_output(line_gpios->desc[oline], 1); + + if (keypad->settling_time_us) + fsleep(keypad->settling_time_us); + + /* Read input on all other lines. */ + err = gpiod_get_array_value_cansleep(line_gpios->ndescs, line_gpios->desc, + line_gpios->info, values); + + gpiod_direction_input(line_gpios->desc[oline]); + + if (err) + return err; + + for (unsigned int iline = 0; iline < keypad->nlines; iline++) { + if (iline == oline) + continue; /* Do not read active output line. */ + + /* Check if GPIO is asserted. */ + if (test_bit(iline, values)) + return MATRIX_SCAN_CODE(oline, iline, + get_count_order(keypad->nlines)); + } + + return 0; +} + +static void charlieplex_keypad_poll(struct input_dev *input) +{ + struct charlieplex_keypad *keypad = input_get_drvdata(input); + int code = 0; + + for (unsigned int oline = 0; oline < keypad->nlines; oline++) { + code = charlieplex_keypad_scan_line(keypad, oline); + if (code != 0) + break; + } + + if (code >= 0) + charlieplex_keypad_check_switch_change(input, code); +} + +static int charlieplex_keypad_init_gpio(struct platform_device *pdev, + struct charlieplex_keypad *keypad) +{ + char **pin_names; + char label[32]; + + snprintf(label, sizeof(label), "%s-pin", pdev->name); + + keypad->line_gpios = devm_gpiod_get_array(&pdev->dev, "line", GPIOD_IN); + if (IS_ERR(keypad->line_gpios)) + return PTR_ERR(keypad->line_gpios); + + keypad->nlines = keypad->line_gpios->ndescs; + + if (keypad->nlines > MATRIX_MAX_ROWS) + return -EINVAL; + + pin_names = devm_kasprintf_strarray(&pdev->dev, label, keypad->nlines); + if (IS_ERR(pin_names)) + return PTR_ERR(pin_names); + + for (unsigned int i = 0; i < keypad->line_gpios->ndescs; i++) + gpiod_set_consumer_name(keypad->line_gpios->desc[i], pin_names[i]); + + return 0; +} + +static int charlieplex_keypad_probe(struct platform_device *pdev) +{ + struct charlieplex_keypad *keypad; + struct input_dev *input_dev; + unsigned int debounce_interval_ms = 5; + unsigned int poll_interval_ms; + int err; + + keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); + if (!keypad) + return -ENOMEM; + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; + + keypad->input_dev = input_dev; + + err = device_property_read_u32(&pdev->dev, "poll-interval", &poll_interval_ms); + if (err) + return dev_err_probe(&pdev->dev, err, + "failed to parse 'poll-interval' property\n"); + + if (poll_interval_ms == 0) + return dev_err_probe(&pdev->dev, -EINVAL, "invalid 'poll-interval' value\n"); + + device_property_read_u32(&pdev->dev, "debounce-delay-ms", &debounce_interval_ms); + device_property_read_u32(&pdev->dev, "settling-time-us", &keypad->settling_time_us); + + keypad->current_code = -1; + keypad->debounce_code = -1; + keypad->debounce_threshold = DIV_ROUND_UP(debounce_interval_ms, poll_interval_ms); + + err = charlieplex_keypad_init_gpio(pdev, keypad); + if (err) + return err; + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + + err = matrix_keypad_build_keymap(NULL, NULL, keypad->nlines, + keypad->nlines, NULL, input_dev); + if (err) + return dev_err_probe(&pdev->dev, err, "failed to build keymap\n"); + + if (device_property_read_bool(&pdev->dev, "autorepeat")) + __set_bit(EV_REP, input_dev->evbit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + err = input_setup_polling(input_dev, charlieplex_keypad_poll); + if (err) + return dev_err_probe(&pdev->dev, err, "unable to set up polling\n"); + + input_set_poll_interval(input_dev, poll_interval_ms); + + input_set_drvdata(input_dev, keypad); + + err = input_register_device(keypad->input_dev); + if (err) + return err; + + return 0; +} + +static const struct of_device_id charlieplex_keypad_dt_match[] = { + { .compatible = "gpio-charlieplex-keypad" }, + { } +}; +MODULE_DEVICE_TABLE(of, charlieplex_keypad_dt_match); + +static struct platform_driver charlieplex_keypad_driver = { + .probe = charlieplex_keypad_probe, + .driver = { + .name = "charlieplex-keypad", + .of_match_table = charlieplex_keypad_dt_match, + }, +}; +module_platform_driver(charlieplex_keypad_driver); + +MODULE_AUTHOR("Hugo Villeneuve "); +MODULE_DESCRIPTION("GPIO driven charlieplex keypad driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 2822c592880b..177e5d4a3382 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -29,6 +29,12 @@ #include +/* + * Maximum size of the normal key matrix, this is limited by the host command + * key_matrix field defined in ec_response_get_next_data_v3 + */ +#define CROS_EC_KEYBOARD_COLS_MAX 18 + /** * struct cros_ec_keyb - Structure representing EC keyboard device * @@ -44,14 +50,17 @@ * @bs_idev: The input device for non-matrix buttons and switches (or NULL). * @notifier: interrupt event notifier for transport devices * @vdata: vivaldi function row data + * @has_fn_map: whether the driver uses an fn function-map layer + * @fn_active: tracks whether the function key is currently pressed + * @fn_combo_active: tracks whether another key was pressed while fn is active */ struct cros_ec_keyb { unsigned int rows; unsigned int cols; int row_shift; bool ghost_filter; - uint8_t *valid_keys; - uint8_t *old_kb_state; + u8 valid_keys[CROS_EC_KEYBOARD_COLS_MAX]; + u8 old_kb_state[CROS_EC_KEYBOARD_COLS_MAX]; struct device *dev; struct cros_ec_device *ec; @@ -61,6 +70,10 @@ struct cros_ec_keyb { struct notifier_block notifier; struct vivaldi_data vdata; + + bool has_fn_map; + bool fn_active; + bool fn_combo_active; }; /** @@ -132,11 +145,11 @@ static const struct cros_ec_bs_map cros_ec_keyb_bs[] = { * Returns true when there is at least one combination of pressed keys that * results in ghosting. */ -static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) +static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, u8 *buf) { int col1, col2, buf1, buf2; struct device *dev = ckdev->dev; - uint8_t *valid_keys = ckdev->valid_keys; + u8 *valid_keys = ckdev->valid_keys; /* * Ghosting happens if for any pressed key X there are other keys @@ -166,20 +179,108 @@ static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) return false; } +static void cros_ec_emit_fn_key(struct input_dev *input, unsigned int pos) +{ + input_event(input, EV_MSC, MSC_SCAN, pos); + input_report_key(input, KEY_FN, true); + input_sync(input); + + input_event(input, EV_MSC, MSC_SCAN, pos); + input_report_key(input, KEY_FN, false); +} + +static void cros_ec_keyb_process_key_plain(struct cros_ec_keyb *ckdev, + int row, int col, bool state) +{ + struct input_dev *idev = ckdev->idev; + const unsigned short *keycodes = idev->keycode; + int pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); + + input_event(idev, EV_MSC, MSC_SCAN, pos); + input_report_key(idev, keycodes[pos], state); +} + +static void cros_ec_keyb_process_key_fn_map(struct cros_ec_keyb *ckdev, + int row, int col, bool state) +{ + struct input_dev *idev = ckdev->idev; + const unsigned short *keycodes = idev->keycode; + unsigned int pos, fn_pos; + unsigned int code, fn_code; + + pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); + code = keycodes[pos]; + + if (code == KEY_FN) { + ckdev->fn_active = state; + if (state) { + ckdev->fn_combo_active = false; + } else if (!ckdev->fn_combo_active) { + /* + * Send both Fn press and release events if nothing + * else has been pressed together with Fn. + */ + cros_ec_emit_fn_key(idev, pos); + } + return; + } + + fn_pos = MATRIX_SCAN_CODE(row + ckdev->rows, col, ckdev->row_shift); + fn_code = keycodes[fn_pos]; + + if (state) { + if (ckdev->fn_active) { + ckdev->fn_combo_active = true; + if (!fn_code) + return; /* Discard if no Fn mapping exists */ + + pos = fn_pos; + code = fn_code; + } + } else { + /* + * If the Fn-remapped code is currently pressed, release it. + * Otherwise, release the standard code (if it was pressed). + */ + if (fn_code && test_bit(fn_code, idev->key)) { + pos = fn_pos; + code = fn_code; + } else if (!test_bit(code, idev->key)) { + return; /* Discard, key press code was not sent */ + } + } + + input_event(idev, EV_MSC, MSC_SCAN, pos); + input_report_key(idev, code, state); +} + +static void cros_ec_keyb_process_col(struct cros_ec_keyb *ckdev, int col, + u8 col_state, u8 changed) +{ + for (int row = 0; row < ckdev->rows; row++) { + if (changed & BIT(row)) { + u8 key_state = col_state & BIT(row); + + dev_dbg(ckdev->dev, "changed: [r%d c%d]: byte %02x\n", + row, col, key_state); + + if (ckdev->has_fn_map) + cros_ec_keyb_process_key_fn_map(ckdev, row, col, + key_state); + else + cros_ec_keyb_process_key_plain(ckdev, row, col, + key_state); + } + } +} /* * Compares the new keyboard state to the old one and produces key - * press/release events accordingly. The keyboard state is 13 bytes (one byte - * per column) + * press/release events accordingly. The keyboard state is one byte + * per column. */ -static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, - uint8_t *kb_state, int len) +static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, u8 *kb_state, int len) { - struct input_dev *idev = ckdev->idev; - int col, row; - int new_state; - int old_state; - if (ckdev->ghost_filter && cros_ec_keyb_has_ghosting(ckdev, kb_state)) { /* * Simple-minded solution: ignore this state. The obvious @@ -190,25 +291,15 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, return; } - for (col = 0; col < ckdev->cols; col++) { - for (row = 0; row < ckdev->rows; row++) { - int pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); - const unsigned short *keycodes = idev->keycode; + for (int col = 0; col < ckdev->cols; col++) { + u8 changed = kb_state[col] ^ ckdev->old_kb_state[col]; - new_state = kb_state[col] & (1 << row); - old_state = ckdev->old_kb_state[col] & (1 << row); - if (new_state != old_state) { - dev_dbg(ckdev->dev, - "changed: [r%d c%d]: byte %02x\n", - row, col, new_state); - - input_event(idev, EV_MSC, MSC_SCAN, pos); - input_report_key(idev, keycodes[pos], - new_state); - } - } - ckdev->old_kb_state[col] = kb_state[col]; + if (changed) + cros_ec_keyb_process_col(ckdev, col, kb_state[col], + changed); } + + memcpy(ckdev->old_kb_state, kb_state, sizeof(ckdev->old_kb_state)); input_sync(ckdev->idev); } @@ -246,8 +337,10 @@ static int cros_ec_keyb_work(struct notifier_block *nb, { struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, notifier); - u32 val; + struct ec_response_get_next_event_v3 *event_data; + unsigned int event_size; unsigned int ev_type; + u32 val; /* * If not wake enabled, discard key state changes during @@ -257,32 +350,32 @@ static int cros_ec_keyb_work(struct notifier_block *nb, if (queued_during_suspend && !device_may_wakeup(ckdev->dev)) return NOTIFY_OK; - switch (ckdev->ec->event_data.event_type) { + event_data = &ckdev->ec->event_data; + event_size = ckdev->ec->event_size; + + switch (event_data->event_type) { case EC_MKBP_EVENT_KEY_MATRIX: pm_wakeup_event(ckdev->dev, 0); if (!ckdev->idev) { - dev_warn_once(ckdev->dev, - "Unexpected key matrix event\n"); + dev_warn_once(ckdev->dev, "Unexpected key matrix event\n"); return NOTIFY_OK; } - if (ckdev->ec->event_size != ckdev->cols) { + if (event_size != ckdev->cols) { dev_err(ckdev->dev, "Discarded key matrix event, unexpected length: %d != %d\n", ckdev->ec->event_size, ckdev->cols); return NOTIFY_OK; } - cros_ec_keyb_process(ckdev, - ckdev->ec->event_data.data.key_matrix, - ckdev->ec->event_size); + cros_ec_keyb_process(ckdev, event_data->data.key_matrix, event_size); break; case EC_MKBP_EVENT_SYSRQ: pm_wakeup_event(ckdev->dev, 0); - val = get_unaligned_le32(&ckdev->ec->event_data.data.sysrq); + val = get_unaligned_le32(&event_data->data.sysrq); dev_dbg(ckdev->dev, "sysrq code from EC: %#x\n", val); handle_sysrq(val); break; @@ -291,13 +384,11 @@ static int cros_ec_keyb_work(struct notifier_block *nb, case EC_MKBP_EVENT_SWITCH: pm_wakeup_event(ckdev->dev, 0); - if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) { - val = get_unaligned_le32( - &ckdev->ec->event_data.data.buttons); + if (event_data->event_type == EC_MKBP_EVENT_BUTTON) { + val = get_unaligned_le32(&event_data->data.buttons); ev_type = EV_KEY; } else { - val = get_unaligned_le32( - &ckdev->ec->event_data.data.switches); + val = get_unaligned_le32(&event_data->data.switches); ev_type = EV_SW; } cros_ec_keyb_report_bs(ckdev, ev_type, val); @@ -326,8 +417,8 @@ static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev) for (col = 0; col < ckdev->cols; col++) { for (row = 0; row < ckdev->rows; row++) { code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; - if (code && (code != KEY_BATTERY)) - ckdev->valid_keys[col] |= 1 << row; + if (code != KEY_RESERVED && code != KEY_BATTERY) + ckdev->valid_keys[col] |= BIT(row); } dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n", col, ckdev->valid_keys[col]); @@ -583,6 +674,62 @@ static void cros_ec_keyb_parse_vivaldi_physmap(struct cros_ec_keyb *ckdev) ckdev->vdata.num_function_row_keys = n_physmap; } +/* Returns true if there is a KEY_FN code defined in the normal keymap */ +static bool cros_ec_keyb_has_fn_key(struct cros_ec_keyb *ckdev) +{ + const unsigned short *keycodes = ckdev->idev->keycode; + int i; + + for (i = 0; i < MATRIX_SCAN_CODE(ckdev->rows, 0, ckdev->row_shift); i++) { + if (keycodes[i] == KEY_FN) + return true; + } + + return false; +} + +/* + * Returns true if there is a KEY_FN defined and at least one key in the fn + * layer keymap + */ +static bool cros_ec_keyb_has_fn_map(struct cros_ec_keyb *ckdev) +{ + struct input_dev *idev = ckdev->idev; + const unsigned short *keycodes = ckdev->idev->keycode; + int i; + + if (!cros_ec_keyb_has_fn_key(ckdev)) + return false; + + for (i = MATRIX_SCAN_CODE(ckdev->rows, 0, ckdev->row_shift); + i < idev->keycodemax; i++) { + if (keycodes[i] != KEY_RESERVED) + return true; + } + + return false; +} + +/* + * Custom handler for the set keycode ioctl, calls the default handler and + * recomputes has_fn_map. + */ +static int cros_ec_keyb_setkeycode(struct input_dev *idev, + const struct input_keymap_entry *ke, + unsigned int *old_keycode) +{ + struct cros_ec_keyb *ckdev = input_get_drvdata(idev); + int ret; + + ret = input_default_setkeycode(idev, ke, old_keycode); + if (ret) + return ret; + + ckdev->has_fn_map = cros_ec_keyb_has_fn_map(ckdev); + + return 0; +} + /** * cros_ec_keyb_register_matrix - Register matrix keys * @@ -604,13 +751,11 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) if (err) return err; - ckdev->valid_keys = devm_kzalloc(dev, ckdev->cols, GFP_KERNEL); - if (!ckdev->valid_keys) - return -ENOMEM; - - ckdev->old_kb_state = devm_kzalloc(dev, ckdev->cols, GFP_KERNEL); - if (!ckdev->old_kb_state) - return -ENOMEM; + if (ckdev->cols > CROS_EC_KEYBOARD_COLS_MAX) { + dev_err(dev, "keypad,num-columns too large: %d (max: %d)\n", + ckdev->cols, CROS_EC_KEYBOARD_COLS_MAX); + return -EINVAL; + } /* * We call the keyboard matrix 'input0'. Allocate phys before input @@ -632,11 +777,11 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) idev->id.version = 1; idev->id.product = 0; idev->dev.parent = dev; + idev->setkeycode = cros_ec_keyb_setkeycode; - ckdev->ghost_filter = device_property_read_bool(dev, - "google,needs-ghost-filter"); + ckdev->ghost_filter = device_property_read_bool(dev, "google,needs-ghost-filter"); - err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols, + err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows * 2, ckdev->cols, NULL, idev); if (err) { dev_err(dev, "cannot build key matrix\n"); @@ -651,6 +796,8 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) cros_ec_keyb_compute_valid_keys(ckdev); cros_ec_keyb_parse_vivaldi_physmap(ckdev); + ckdev->has_fn_map = cros_ec_keyb_has_fn_map(ckdev); + err = input_register_device(ckdev->idev); if (err) { dev_err(dev, "cannot register input device\n"); diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 069c1d6376e1..ccde60cd6bb3 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -324,7 +324,7 @@ static void imx_keypad_config(struct imx_keypad *keypad) reg_val |= (keypad->cols_en_mask & 0xff) << 8; /* cols */ writew(reg_val, keypad->mmio_base + KPCR); - /* Write 0's to KPDR[15:8] (Colums) */ + /* Write 0's to KPDR[15:8] (Columns) */ reg_val = readw(keypad->mmio_base + KPDR); reg_val &= 0x00ff; writew(reg_val, keypad->mmio_base + KPDR); @@ -357,7 +357,7 @@ static void imx_keypad_inhibit(struct imx_keypad *keypad) reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD; writew(reg_val, keypad->mmio_base + KPSR); - /* Colums as open drain and disable all rows */ + /* Columns as open drain and disable all rows */ reg_val = (keypad->cols_en_mask & 0xff) << 8; writew(reg_val, keypad->mmio_base + KPCR); } diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index bd1a944ded46..47edc161ec77 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -295,8 +295,6 @@ static int mpr_touchkey_probe(struct i2c_client *client) return error; i2c_set_clientdata(client, mpr121); - device_init_wakeup(dev, - device_property_read_bool(dev, "wakeup-source")); return 0; } @@ -305,9 +303,6 @@ static int mpr_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - if (device_may_wakeup(&client->dev)) - enable_irq_wake(client->irq); - i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00); return 0; @@ -318,9 +313,6 @@ static int mpr_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client); - if (device_may_wakeup(&client->dev)) - disable_irq_wake(client->irq); - i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, mpr121->keycount); diff --git a/drivers/input/keyboard/qt1050.c b/drivers/input/keyboard/qt1050.c index bce8157d1871..f9f480c91032 100644 --- a/drivers/input/keyboard/qt1050.c +++ b/drivers/input/keyboard/qt1050.c @@ -435,8 +435,7 @@ static int qt1050_probe(struct i2c_client *client) int err; /* Check basic functionality */ - err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE); - if (!err) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { dev_err(&client->dev, "%s adapter not supported\n", dev_driver_string(&client->adapter->dev)); return -ENODEV; diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c index b3db2c7d0957..b255b997e279 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -133,8 +133,7 @@ static int qt1070_probe(struct i2c_client *client) int i; int err; - err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE); - if (!err) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { dev_err(&client->dev, "%s adapter not supported\n", dev_driver_string(&client->adapter->dev)); return -ENODEV; diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index c05d898898e8..5ea0ce42a507 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -77,11 +77,8 @@ static const struct adxl34x_bus_ops adxl34x_i2c_bops = { static int adxl34x_i2c_probe(struct i2c_client *client) { struct adxl34x *ac; - int error; - error = i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA); - if (!error) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); return -EIO; } diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index 5b9be2957746..47b31725e850 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #define ACPI_ATLAS_NAME "Atlas ACPI" @@ -57,8 +58,9 @@ static acpi_status acpi_atlas_button_handler(u32 function, return status; } -static int atlas_acpi_button_add(struct acpi_device *device) +static int atlas_acpi_button_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_status status; int i; int err; @@ -106,8 +108,9 @@ static int atlas_acpi_button_add(struct acpi_device *device) return err; } -static void atlas_acpi_button_remove(struct acpi_device *device) +static void atlas_acpi_button_remove(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_status status; status = acpi_remove_address_space_handler(device->handle, @@ -124,16 +127,15 @@ static const struct acpi_device_id atlas_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, atlas_device_ids); -static struct acpi_driver atlas_acpi_driver = { - .name = ACPI_ATLAS_NAME, - .class = ACPI_ATLAS_CLASS, - .ids = atlas_device_ids, - .ops = { - .add = atlas_acpi_button_add, - .remove = atlas_acpi_button_remove, +static struct platform_driver atlas_acpi_driver = { + .probe = atlas_acpi_button_probe, + .remove = atlas_acpi_button_remove, + .driver = { + .name = ACPI_ATLAS_NAME, + .acpi_match_table = atlas_device_ids, }, }; -module_acpi_driver(atlas_acpi_driver); +module_platform_driver(atlas_acpi_driver); MODULE_AUTHOR("Jaya Kumar"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/aw86927.c b/drivers/input/misc/aw86927.c index 8ad361239cfe..f53b8f004cb3 100644 --- a/drivers/input/misc/aw86927.c +++ b/drivers/input/misc/aw86927.c @@ -43,6 +43,12 @@ #define AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK GENMASK(6, 0) #define AW86927_PLAYCFG1_BST_8500MV 0x50 +#define AW86938_PLAYCFG1_REG 0x06 +#define AW86938_PLAYCFG1_BST_MODE_MASK GENMASK(5, 5) +#define AW86938_PLAYCFG1_BST_MODE_BYPASS 0 +#define AW86938_PLAYCFG1_BST_VOUT_VREFSET_MASK GENMASK(4, 0) +#define AW86938_PLAYCFG1_BST_7000MV 0x11 + #define AW86927_PLAYCFG2_REG 0x07 #define AW86927_PLAYCFG3_REG 0x08 @@ -140,6 +146,7 @@ #define AW86927_CHIPIDH_REG 0x57 #define AW86927_CHIPIDL_REG 0x58 #define AW86927_CHIPID 0x9270 +#define AW86938_CHIPID 0x9380 #define AW86927_TMCFG_REG 0x5b #define AW86927_TMCFG_UNLOCK 0x7d @@ -173,14 +180,20 @@ enum aw86927_work_mode { AW86927_RAM_MODE, }; +enum aw86927_model { + AW86927, + AW86938, +}; + struct aw86927_data { + enum aw86927_model model; struct work_struct play_work; struct device *dev; struct input_dev *input_dev; struct i2c_client *client; struct regmap *regmap; struct gpio_desc *reset_gpio; - bool running; + u16 level; }; static const struct regmap_config aw86927_regmap_config = { @@ -325,11 +338,12 @@ static int aw86927_haptics_play(struct input_dev *dev, void *data, struct ff_eff if (!level) level = effect->u.rumble.weak_magnitude; - /* If already running, don't restart playback */ - if (haptics->running && level) + /* If level does not change, don't restart playback */ + if (haptics->level == level) return 0; - haptics->running = level; + haptics->level = level; + schedule_work(&haptics->play_work); return 0; @@ -376,8 +390,7 @@ static int aw86927_play_sine(struct aw86927_data *haptics) if (err) return err; - /* set gain to value lower than 0x80 to avoid distorted playback */ - err = regmap_write(haptics->regmap, AW86927_PLAYCFG2_REG, 0x7c); + err = regmap_write(haptics->regmap, AW86927_PLAYCFG2_REG, haptics->level * 0x80 / 0xffff); if (err) return err; @@ -409,7 +422,7 @@ static void aw86927_haptics_play_work(struct work_struct *work) struct device *dev = &haptics->client->dev; int err; - if (haptics->running) + if (haptics->level) err = aw86927_play_sine(haptics); else err = aw86927_stop(haptics); @@ -565,13 +578,26 @@ static int aw86927_haptic_init(struct aw86927_data *haptics) if (err) return err; - err = regmap_update_bits(haptics->regmap, - AW86927_PLAYCFG1_REG, - AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK, - FIELD_PREP(AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK, - AW86927_PLAYCFG1_BST_8500MV)); - if (err) - return err; + switch (haptics->model) { + case AW86927: + err = regmap_update_bits(haptics->regmap, + AW86927_PLAYCFG1_REG, + AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK, + FIELD_PREP(AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK, + AW86927_PLAYCFG1_BST_8500MV)); + if (err) + return err; + break; + case AW86938: + err = regmap_update_bits(haptics->regmap, + AW86938_PLAYCFG1_REG, + AW86938_PLAYCFG1_BST_VOUT_VREFSET_MASK, + FIELD_PREP(AW86938_PLAYCFG1_BST_VOUT_VREFSET_MASK, + AW86938_PLAYCFG1_BST_7000MV)); + if (err) + return err; + break; + } err = regmap_update_bits(haptics->regmap, AW86927_PLAYCFG3_REG, @@ -599,6 +625,9 @@ static int aw86927_ram_init(struct aw86927_data *haptics) FIELD_PREP(AW86927_SYSCTRL3_EN_RAMINIT_MASK, AW86927_SYSCTRL3_EN_RAMINIT_ON)); + /* AW86938 wants a 1ms delay here */ + usleep_range(1000, 1500); + /* Set base address for the start of the SRAM waveforms */ err = regmap_write(haptics->regmap, AW86927_BASEADDRH_REG, AW86927_BASEADDRH_VAL); @@ -717,7 +746,14 @@ static int aw86927_detect(struct aw86927_data *haptics) chip_id = be16_to_cpu(read_buf); - if (chip_id != AW86927_CHIPID) { + switch (chip_id) { + case AW86927_CHIPID: + haptics->model = AW86927; + break; + case AW86938_CHIPID: + haptics->model = AW86938; + break; + default: dev_err(haptics->dev, "Unexpected CHIPID value 0x%x\n", chip_id); return -ENODEV; } diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 96cd6a078c8a..e2089d6ac850 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -7,14 +7,16 @@ * Copyright: (C) 2014 Texas Instruments, Inc. */ +#include +#include +#include +#include #include #include #include #include -#include -#include -#include #include +#include #include @@ -165,6 +167,12 @@ #define DRV260X_AUTOCAL_TIME_500MS (2 << 4) #define DRV260X_AUTOCAL_TIME_1000MS (3 << 4) +/* + * Timeout for waiting for the GO status bit, in seconds. Should be reasonably + * large to wait for a auto-calibration cycle completion. + */ +#define DRV260X_GO_TIMEOUT_S 5 + /** * struct drv260x_data - * @input_dev: Pointer to the input device @@ -308,6 +316,7 @@ static int drv260x_init(struct drv260x_data *haptics) { int error; unsigned int cal_buf; + unsigned long timeout; error = regmap_write(haptics->regmap, DRV260X_RATED_VOLT, haptics->rated_voltage); @@ -397,6 +406,7 @@ static int drv260x_init(struct drv260x_data *haptics) return error; } + timeout = jiffies + DRV260X_GO_TIMEOUT_S * HZ; do { usleep_range(15000, 15500); error = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf); @@ -406,6 +416,11 @@ static int drv260x_init(struct drv260x_data *haptics) error); return error; } + if (time_after(jiffies, timeout)) { + dev_err(&haptics->client->dev, + "Calibration timeout. The device cannot be used.\n"); + return -ETIMEDOUT; + } } while (cal_buf == DRV260X_GO_BIT); return 0; @@ -419,6 +434,13 @@ static const struct regmap_config drv260x_regmap_config = { .cache_type = REGCACHE_NONE, }; +static void drv260x_power_off(void *data) +{ + struct drv260x_data *haptics = data; + + regulator_disable(haptics->regulator); +} + static int drv260x_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -484,6 +506,16 @@ static int drv260x_probe(struct i2c_client *client) return error; } + error = regulator_enable(haptics->regulator); + if (error) { + dev_err(dev, "Failed to enable regulator: %d\n", error); + return error; + } + + error = devm_add_action_or_reset(dev, drv260x_power_off, haptics); + if (error) + return error; + haptics->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); if (IS_ERR(haptics->enable_gpio)) @@ -598,11 +630,22 @@ static int drv260x_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume); static const struct i2c_device_id drv260x_id[] = { + { "drv2604" }, + { "drv2604l" }, + { "drv2605" }, { "drv2605l" }, { } }; MODULE_DEVICE_TABLE(i2c, drv260x_id); +#ifdef CONFIG_ACPI +static const struct acpi_device_id drv260x_acpi_match[] = { + { "DRV2604" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, drv260x_acpi_match); +#endif + static const struct of_device_id drv260x_of_match[] = { { .compatible = "ti,drv2604", }, { .compatible = "ti,drv2604l", }, @@ -616,6 +659,7 @@ static struct i2c_driver drv260x_driver = { .probe = drv260x_probe, .driver = { .name = "drv260x-haptics", + .acpi_match_table = ACPI_PTR(drv260x_acpi_match), .of_match_table = drv260x_of_match, .pm = pm_sleep_ptr(&drv260x_pm_ops), }, diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index f69de9762c4e..4c022a36dbe8 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -438,6 +438,14 @@ static void ims_pcu_handle_response(struct ims_pcu *pcu) } } +static void ims_pcu_reset_packet(struct ims_pcu *pcu) +{ + pcu->have_stx = true; + pcu->have_dle = false; + pcu->read_pos = 0; + pcu->check_sum = 0; +} + static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) { int i; @@ -450,6 +458,14 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) continue; if (pcu->have_dle) { + if (pcu->read_pos >= IMS_PCU_BUF_SIZE) { + dev_warn(pcu->dev, + "Packet too long (%d bytes), discarding\n", + pcu->read_pos); + ims_pcu_reset_packet(pcu); + continue; + } + pcu->have_dle = false; pcu->read_buf[pcu->read_pos++] = data; pcu->check_sum += data; @@ -462,10 +478,8 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) dev_warn(pcu->dev, "Unexpected STX at byte %d, discarding old data\n", pcu->read_pos); + ims_pcu_reset_packet(pcu); pcu->have_stx = true; - pcu->have_dle = false; - pcu->read_pos = 0; - pcu->check_sum = 0; break; case IMS_PCU_PROTOCOL_DLE: @@ -485,12 +499,18 @@ static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) ims_pcu_handle_response(pcu); } - pcu->have_stx = false; - pcu->have_dle = false; - pcu->read_pos = 0; + ims_pcu_reset_packet(pcu); break; default: + if (pcu->read_pos >= IMS_PCU_BUF_SIZE) { + dev_warn(pcu->dev, + "Packet too long (%d bytes), discarding\n", + pcu->read_pos); + ims_pcu_reset_packet(pcu); + continue; + } + pcu->read_buf[pcu->read_pos++] = data; pcu->check_sum += data; break; diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c index 152633bd2266..70cd6586459e 100644 --- a/drivers/input/misc/keyspan_remote.c +++ b/drivers/input/misc/keyspan_remote.c @@ -420,24 +420,6 @@ static void keyspan_close(struct input_dev *dev) usb_kill_urb(remote->irq_urb); } -static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_interface *iface) -{ - - struct usb_endpoint_descriptor *endpoint; - int i; - - for (i = 0; i < iface->desc.bNumEndpoints; ++i) { - endpoint = &iface->endpoint[i].desc; - - if (usb_endpoint_is_int_in(endpoint)) { - /* we found our interrupt in endpoint */ - return endpoint; - } - } - - return NULL; -} - /* * Routine that sets up the driver to handle a specific USB device detected on the bus. */ @@ -449,8 +431,8 @@ static int keyspan_probe(struct usb_interface *interface, const struct usb_devic struct input_dev *input_dev; int i, error; - endpoint = keyspan_get_in_endpoint(interface->cur_altsetting); - if (!endpoint) + error = usb_find_int_in_endpoint(interface->cur_altsetting, &endpoint); + if (error) return -ENODEV; remote = kzalloc_obj(*remote); diff --git a/drivers/input/misc/rb532_button.c b/drivers/input/misc/rb532_button.c index 190a80e1e2c1..40173bf7a235 100644 --- a/drivers/input/misc/rb532_button.c +++ b/drivers/input/misc/rb532_button.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include @@ -18,6 +18,14 @@ #define RB532_BTN_RATE 100 /* msec */ #define RB532_BTN_KSYM BTN_0 +/** + * struct rb532_button - RB532 button information + * @gpio: GPIO connected to the button + */ +struct rb532_button { + struct gpio_desc *gpio; +}; + /* The S1 button state is provided by GPIO pin 1. But as this * pin is also used for uart input as alternate function, the * operational modes must be switched first: @@ -31,35 +39,48 @@ * The GPIO value occurs to be inverted, so pin high means * button is not pressed. */ -static bool rb532_button_pressed(void) +static bool rb532_button_pressed(struct rb532_button *button) { int val; set_latch_u5(0, LO_FOFF); - gpio_direction_input(GPIO_BTN_S1); + gpiod_direction_input(button->gpio); - val = gpio_get_value(GPIO_BTN_S1); + val = gpiod_get_value(button->gpio); rb532_gpio_set_func(GPIO_BTN_S1); set_latch_u5(LO_FOFF, 0); - return !val; + return val; } static void rb532_button_poll(struct input_dev *input) { - input_report_key(input, RB532_BTN_KSYM, rb532_button_pressed()); + struct rb532_button *button = input_get_drvdata(input); + + input_report_key(input, RB532_BTN_KSYM, rb532_button_pressed(button)); input_sync(input); } static int rb532_button_probe(struct platform_device *pdev) { + struct rb532_button *button; struct input_dev *input; int error; + button = devm_kzalloc(&pdev->dev, sizeof(*button), GFP_KERNEL); + if (!button) + return -ENOMEM; + + button->gpio = devm_gpiod_get(&pdev->dev, "button", GPIOD_IN); + if (IS_ERR(button->gpio)) + return dev_err_probe(&pdev->dev, PTR_ERR(button->gpio), + "error getting button GPIO\n"); + input = devm_input_allocate_device(&pdev->dev); if (!input) return -ENOMEM; + input_set_drvdata(input, button); input->name = "rb532 button"; input->phys = "rb532/button0"; @@ -77,6 +98,8 @@ static int rb532_button_probe(struct platform_device *pdev) if (error) return error; + platform_set_drvdata(pdev, button); + return 0; } diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index c8b87a87744c..074130d88953 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -164,16 +164,6 @@ config MOUSE_PS2_TOUCHKIT If unsure, say N. -config MOUSE_PS2_OLPC - bool "OLPC PS/2 mouse protocol extension" - depends on MOUSE_PS2 && OLPC - help - Say Y here if you have an OLPC XO-1 laptop (with built-in - PS/2 touchpad/tablet device). The manufacturer calls the - touchpad an HGPK. - - If unsure, say N. - config MOUSE_PS2_FOCALTECH bool "FocalTech PS/2 mouse protocol extension" if EXPERT default y @@ -300,32 +290,6 @@ config MOUSE_ELAN_I2C_SMBUS If unsure, say Y. -config MOUSE_INPORT - tristate "InPort/MS/ATIXL busmouse" - depends on ISA - help - Say Y here if you have an InPort, Microsoft or ATI XL busmouse. - They are rather rare these days. - - To compile this driver as a module, choose M here: the - module will be called inport. - -config MOUSE_ATIXL - bool "ATI XL variant" - depends on MOUSE_INPORT - help - Say Y here if your mouse is of the ATI XL variety. - -config MOUSE_LOGIBM - tristate "Logitech busmouse" - depends on ISA - help - Say Y here if you have a Logitech busmouse. - They are rather rare these days. - - To compile this driver as a module, choose M here: the - module will be called logibm. - config MOUSE_AMIGA tristate "Amiga mouse" depends on AMIGA diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 78ab5db28d7c..ed40f141691e 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -12,8 +12,6 @@ obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o obj-$(CONFIG_MOUSE_CYAPA) += cyapatp.o obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o -obj-$(CONFIG_MOUSE_INPORT) += inport.o -obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o obj-$(CONFIG_MOUSE_PS2) += psmouse.o obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o @@ -28,7 +26,6 @@ psmouse-objs := psmouse-base.o synaptics.o focaltech.o psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o psmouse-$(CONFIG_MOUSE_PS2_BYD) += byd.o psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o -psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index f3d3b6b4e02d..0ee1a30b9aaf 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -12,6 +12,7 @@ * tpconfig utility (by C. Scott Ananian and Bruce Kall). */ +#include "linux/workqueue.h" #include #include #include @@ -1452,7 +1453,7 @@ err_free_input: static void alps_register_bare_ps2_mouse(struct work_struct *work) { struct alps_data *priv = container_of(work, struct alps_data, - dev3_register_work.work); + dev3_register_work); int error; guard(mutex)(&alps_mutex); @@ -1485,8 +1486,7 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse, } else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) { /* Register dev3 mouse if we received PS/2 packet first time */ if (!IS_ERR(priv->dev3)) - psmouse_queue_work(psmouse, &priv->dev3_register_work, - 0); + schedule_work(&priv->dev3_register_work); return; } else { dev = priv->dev3; @@ -2975,7 +2975,7 @@ static void alps_disconnect(struct psmouse *psmouse) psmouse_reset(psmouse); timer_shutdown_sync(&priv->timer); - disable_delayed_work_sync(&priv->dev3_register_work); + disable_work_sync(&priv->dev3_register_work); if (priv->dev2) input_unregister_device(priv->dev2); if (!IS_ERR_OR_NULL(priv->dev3)) @@ -3147,8 +3147,7 @@ int alps_init(struct psmouse *psmouse) priv->psmouse = psmouse; - INIT_DELAYED_WORK(&priv->dev3_register_work, - alps_register_bare_ps2_mouse); + INIT_WORK(&priv->dev3_register_work, alps_register_bare_ps2_mouse); psmouse->protocol_handler = alps_process_byte; psmouse->poll = alps_poll; diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 0a1048cf23f6..17bbf6cdba55 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -257,7 +257,7 @@ struct alps_fields { * @dev3: Generic PS/2 mouse (can be NULL, delayed registering). * @phys2: Physical path for the trackstick device. * @phys3: Physical path for the generic PS/2 mouse. - * @dev3_register_work: Delayed work for registering PS/2 mouse. + * @dev3_register_work: A work instance for registering PS/2 mouse. * @nibble_commands: Command mapping used for touchpad register accesses. * @addr_command: Command used to tell the touchpad that a register address * follows. @@ -289,7 +289,7 @@ struct alps_data { struct input_dev *dev3; char phys2[32]; char phys3[32]; - struct delayed_work dev3_register_work; + struct work_struct dev3_register_work; /* these are autodetected when the device is identified */ const struct alps_nibble_commands *nibble_commands; diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index 87d8f5afdfd9..eebeb57515e1 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -829,29 +829,20 @@ static int atp_probe(struct usb_interface *iface, struct atp *dev; struct input_dev *input_dev; struct usb_device *udev = interface_to_usbdev(iface); - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - int int_in_endpointAddr = 0; - int i, error = -ENOMEM; + struct usb_endpoint_descriptor *ep; + int error; const struct atp_info *info = (const struct atp_info *)id->driver_info; /* set up the endpoint information */ /* use only the first interrupt-in endpoint */ - iface_desc = iface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { - endpoint = &iface_desc->endpoint[i].desc; - if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) { - /* we found an interrupt in endpoint */ - int_in_endpointAddr = endpoint->bEndpointAddress; - break; - } - } - if (!int_in_endpointAddr) { + error = usb_find_int_in_endpoint(iface->cur_altsetting, &ep); + if (error) { dev_err(&iface->dev, "Could not find int-in endpoint\n"); return -EIO; } /* allocate memory for our device state and initialize it */ + error = -ENOMEM; dev = kzalloc_obj(*dev); input_dev = input_allocate_device(); if (!dev || !input_dev) { @@ -875,7 +866,7 @@ static int atp_probe(struct usb_interface *iface, goto err_free_urb; usb_fill_int_urb(dev->urb, udev, - usb_rcvintpipe(udev, int_in_endpointAddr), + usb_rcvintpipe(udev, usb_endpoint_num(ep)), dev->data, dev->info->datalen, dev->info->callback, dev, 1); diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c deleted file mode 100644 index 3c4d16ed88a9..000000000000 --- a/drivers/input/mouse/hgpk.c +++ /dev/null @@ -1,1063 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * OLPC HGPK (XO-1) touchpad PS/2 mouse driver - * - * Copyright (c) 2006-2008 One Laptop Per Child - * Authors: - * Zephaniah E. Hull - * Andres Salomon - * - * This driver is partly based on the ALPS driver, which is: - * - * Copyright (c) 2003 Neil Brown - * Copyright (c) 2003-2005 Peter Osterlund - * Copyright (c) 2004 Dmitry Torokhov - * Copyright (c) 2005 Vojtech Pavlik - */ - -/* - * The spec from ALPS is available from - * . It refers to this - * device as HGPK (Hybrid GS, PT, and Keymatrix). - * - * The earliest versions of the device had simultaneous reporting; that - * was removed. After that, the device used the Advanced Mode GS/PT streaming - * stuff. That turned out to be too buggy to support, so we've finally - * switched to Mouse Mode (which utilizes only the center 1/3 of the touchpad). - */ - -#define DEBUG -#include -#include -#include -#include -#include -#include -#include - -#include "psmouse.h" -#include "hgpk.h" - -#define ILLEGAL_XY 999999 - -static bool tpdebug; -module_param(tpdebug, bool, 0644); -MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG."); - -static int recalib_delta = 100; -module_param(recalib_delta, int, 0644); -MODULE_PARM_DESC(recalib_delta, - "packets containing a delta this large will be discarded, and a " - "recalibration may be scheduled."); - -static int jumpy_delay = 20; -module_param(jumpy_delay, int, 0644); -MODULE_PARM_DESC(jumpy_delay, - "delay (ms) before recal after jumpiness detected"); - -static int spew_delay = 1; -module_param(spew_delay, int, 0644); -MODULE_PARM_DESC(spew_delay, - "delay (ms) before recal after packet spew detected"); - -static int recal_guard_time; -module_param(recal_guard_time, int, 0644); -MODULE_PARM_DESC(recal_guard_time, - "interval (ms) during which recal will be restarted if packet received"); - -static int post_interrupt_delay = 40; -module_param(post_interrupt_delay, int, 0644); -MODULE_PARM_DESC(post_interrupt_delay, - "delay (ms) before recal after recal interrupt detected"); - -static bool autorecal = true; -module_param(autorecal, bool, 0644); -MODULE_PARM_DESC(autorecal, "enable recalibration in the driver"); - -static char hgpk_mode_name[16]; -module_param_string(hgpk_mode, hgpk_mode_name, sizeof(hgpk_mode_name), 0644); -MODULE_PARM_DESC(hgpk_mode, - "default hgpk mode: mouse, glidesensor or pentablet"); - -static int hgpk_default_mode = HGPK_MODE_MOUSE; - -static const char * const hgpk_mode_names[] = { - [HGPK_MODE_MOUSE] = "Mouse", - [HGPK_MODE_GLIDESENSOR] = "GlideSensor", - [HGPK_MODE_PENTABLET] = "PenTablet", -}; - -static int hgpk_mode_from_name(const char *buf, int len) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hgpk_mode_names); i++) { - const char *name = hgpk_mode_names[i]; - if (strlen(name) == len && !strncasecmp(name, buf, len)) - return i; - } - - return HGPK_MODE_INVALID; -} - -/* - * see if new value is within 20% of half of old value - */ -static int approx_half(int curr, int prev) -{ - int belowhalf, abovehalf; - - if (curr < 5 || prev < 5) - return 0; - - belowhalf = (prev * 8) / 20; - abovehalf = (prev * 12) / 20; - - return belowhalf < curr && curr <= abovehalf; -} - -/* - * Throw out oddly large delta packets, and any that immediately follow whose - * values are each approximately half of the previous. It seems that the ALPS - * firmware emits errant packets, and they get averaged out slowly. - */ -static int hgpk_discard_decay_hack(struct psmouse *psmouse, int x, int y) -{ - struct hgpk_data *priv = psmouse->private; - int avx, avy; - bool do_recal = false; - - avx = abs(x); - avy = abs(y); - - /* discard if too big, or half that but > 4 times the prev delta */ - if (avx > recalib_delta || - (avx > recalib_delta / 2 && ((avx / 4) > priv->xlast))) { - psmouse_warn(psmouse, "detected %dpx jump in x\n", x); - priv->xbigj = avx; - } else if (approx_half(avx, priv->xbigj)) { - psmouse_warn(psmouse, "detected secondary %dpx jump in x\n", x); - priv->xbigj = avx; - priv->xsaw_secondary++; - } else { - if (priv->xbigj && priv->xsaw_secondary > 1) - do_recal = true; - priv->xbigj = 0; - priv->xsaw_secondary = 0; - } - - if (avy > recalib_delta || - (avy > recalib_delta / 2 && ((avy / 4) > priv->ylast))) { - psmouse_warn(psmouse, "detected %dpx jump in y\n", y); - priv->ybigj = avy; - } else if (approx_half(avy, priv->ybigj)) { - psmouse_warn(psmouse, "detected secondary %dpx jump in y\n", y); - priv->ybigj = avy; - priv->ysaw_secondary++; - } else { - if (priv->ybigj && priv->ysaw_secondary > 1) - do_recal = true; - priv->ybigj = 0; - priv->ysaw_secondary = 0; - } - - priv->xlast = avx; - priv->ylast = avy; - - if (do_recal && jumpy_delay) { - psmouse_warn(psmouse, "scheduling recalibration\n"); - psmouse_queue_work(psmouse, &priv->recalib_wq, - msecs_to_jiffies(jumpy_delay)); - } - - return priv->xbigj || priv->ybigj; -} - -static void hgpk_reset_spew_detection(struct hgpk_data *priv) -{ - priv->spew_count = 0; - priv->dupe_count = 0; - priv->x_tally = 0; - priv->y_tally = 0; - priv->spew_flag = NO_SPEW; -} - -static void hgpk_reset_hack_state(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - - priv->abs_x = priv->abs_y = -1; - priv->xlast = priv->ylast = ILLEGAL_XY; - priv->xbigj = priv->ybigj = 0; - priv->xsaw_secondary = priv->ysaw_secondary = 0; - hgpk_reset_spew_detection(priv); -} - -/* - * We have no idea why this particular hardware bug occurs. The touchpad - * will randomly start spewing packets without anything touching the - * pad. This wouldn't necessarily be bad, but it's indicative of a - * severely miscalibrated pad; attempting to use the touchpad while it's - * spewing means the cursor will jump all over the place, and act "drunk". - * - * The packets that are spewed tend to all have deltas between -2 and 2, and - * the cursor will move around without really going very far. It will - * tend to end up in the same location; if we tally up the changes over - * 100 packets, we end up w/ a final delta of close to 0. This happens - * pretty regularly when the touchpad is spewing, and is pretty hard to - * manually trigger (at least for *my* fingers). So, it makes a perfect - * scheme for detecting spews. - */ -static void hgpk_spewing_hack(struct psmouse *psmouse, - int l, int r, int x, int y) -{ - struct hgpk_data *priv = psmouse->private; - - /* ignore button press packets; many in a row could trigger - * a false-positive! */ - if (l || r) - return; - - /* don't track spew if the workaround feature has been turned off */ - if (!spew_delay) - return; - - if (abs(x) > 3 || abs(y) > 3) { - /* no spew, or spew ended */ - hgpk_reset_spew_detection(priv); - return; - } - - /* Keep a tally of the overall delta to the cursor position caused by - * the spew */ - priv->x_tally += x; - priv->y_tally += y; - - switch (priv->spew_flag) { - case NO_SPEW: - /* we're not spewing, but this packet might be the start */ - priv->spew_flag = MAYBE_SPEWING; - - fallthrough; - - case MAYBE_SPEWING: - priv->spew_count++; - - if (priv->spew_count < SPEW_WATCH_COUNT) - break; - - /* excessive spew detected, request recalibration */ - priv->spew_flag = SPEW_DETECTED; - - fallthrough; - - case SPEW_DETECTED: - /* only recalibrate when the overall delta to the cursor - * is really small. if the spew is causing significant cursor - * movement, it is probably a case of the user moving the - * cursor very slowly across the screen. */ - if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) { - psmouse_warn(psmouse, "packet spew detected (%d,%d)\n", - priv->x_tally, priv->y_tally); - priv->spew_flag = RECALIBRATING; - psmouse_queue_work(psmouse, &priv->recalib_wq, - msecs_to_jiffies(spew_delay)); - } - - break; - case RECALIBRATING: - /* we already detected a spew and requested a recalibration, - * just wait for the queue to kick into action. */ - break; - } -} - -/* - * HGPK Mouse Mode format (standard mouse format, sans middle button) - * - * byte 0: y-over x-over y-neg x-neg 1 0 swr swl - * byte 1: x7 x6 x5 x4 x3 x2 x1 x0 - * byte 2: y7 y6 y5 y4 y3 y2 y1 y0 - * - * swr/swl are the left/right buttons. - * x-neg/y-neg are the x and y delta negative bits - * x-over/y-over are the x and y overflow bits - * - * --- - * - * HGPK Advanced Mode - single-mode format - * - * byte 0(PT): 1 1 0 0 1 1 1 1 - * byte 0(GS): 1 1 1 1 1 1 1 1 - * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 - * byte 2(PT): 0 0 x9 x8 x7 ? pt-dsw 0 - * byte 2(GS): 0 x10 x9 x8 x7 ? gs-dsw pt-dsw - * byte 3: 0 y9 y8 y7 1 0 swr swl - * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 - * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 - * - * ?'s are not defined in the protocol spec, may vary between models. - * - * swr/swl are the left/right buttons. - * - * pt-dsw/gs-dsw indicate that the pt/gs sensor is detecting a - * pen/finger - */ -static bool hgpk_is_byte_valid(struct psmouse *psmouse, unsigned char *packet) -{ - struct hgpk_data *priv = psmouse->private; - int pktcnt = psmouse->pktcnt; - bool valid; - - switch (priv->mode) { - case HGPK_MODE_MOUSE: - valid = (packet[0] & 0x0C) == 0x08; - break; - - case HGPK_MODE_GLIDESENSOR: - valid = pktcnt == 1 ? - packet[0] == HGPK_GS : !(packet[pktcnt - 1] & 0x80); - break; - - case HGPK_MODE_PENTABLET: - valid = pktcnt == 1 ? - packet[0] == HGPK_PT : !(packet[pktcnt - 1] & 0x80); - break; - - default: - valid = false; - break; - } - - if (!valid) - psmouse_dbg(psmouse, - "bad data, mode %d (%d) %*ph\n", - priv->mode, pktcnt, 6, psmouse->packet); - - return valid; -} - -static void hgpk_process_advanced_packet(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - struct input_dev *idev = psmouse->dev; - unsigned char *packet = psmouse->packet; - int down = !!(packet[2] & 2); - int left = !!(packet[3] & 1); - int right = !!(packet[3] & 2); - int x = packet[1] | ((packet[2] & 0x78) << 4); - int y = packet[4] | ((packet[3] & 0x70) << 3); - - if (priv->mode == HGPK_MODE_GLIDESENSOR) { - int pt_down = !!(packet[2] & 1); - int finger_down = !!(packet[2] & 2); - int z = packet[5]; - - input_report_abs(idev, ABS_PRESSURE, z); - if (tpdebug) - psmouse_dbg(psmouse, "pd=%d fd=%d z=%d", - pt_down, finger_down, z); - } else { - /* - * PenTablet mode does not report pressure, so we don't - * report it here - */ - if (tpdebug) - psmouse_dbg(psmouse, "pd=%d ", down); - } - - if (tpdebug) - psmouse_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", - left, right, x, y); - - input_report_key(idev, BTN_TOUCH, down); - input_report_key(idev, BTN_LEFT, left); - input_report_key(idev, BTN_RIGHT, right); - - /* - * If this packet says that the finger was removed, reset our position - * tracking so that we don't erroneously detect a jump on next press. - */ - if (!down) { - hgpk_reset_hack_state(psmouse); - goto done; - } - - /* - * Weed out duplicate packets (we get quite a few, and they mess up - * our jump detection) - */ - if (x == priv->abs_x && y == priv->abs_y) { - if (++priv->dupe_count > SPEW_WATCH_COUNT) { - if (tpdebug) - psmouse_dbg(psmouse, "hard spew detected\n"); - priv->spew_flag = RECALIBRATING; - psmouse_queue_work(psmouse, &priv->recalib_wq, - msecs_to_jiffies(spew_delay)); - } - goto done; - } - - /* not a duplicate, continue with position reporting */ - priv->dupe_count = 0; - - /* Don't apply hacks in PT mode, it seems reliable */ - if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) { - int x_diff = priv->abs_x - x; - int y_diff = priv->abs_y - y; - if (hgpk_discard_decay_hack(psmouse, x_diff, y_diff)) { - if (tpdebug) - psmouse_dbg(psmouse, "discarding\n"); - goto done; - } - hgpk_spewing_hack(psmouse, left, right, x_diff, y_diff); - } - - input_report_abs(idev, ABS_X, x); - input_report_abs(idev, ABS_Y, y); - priv->abs_x = x; - priv->abs_y = y; - -done: - input_sync(idev); -} - -static void hgpk_process_simple_packet(struct psmouse *psmouse) -{ - struct input_dev *dev = psmouse->dev; - unsigned char *packet = psmouse->packet; - int left = packet[0] & 1; - int right = (packet[0] >> 1) & 1; - int x = packet[1] - ((packet[0] << 4) & 0x100); - int y = ((packet[0] << 3) & 0x100) - packet[2]; - - if (packet[0] & 0xc0) - psmouse_dbg(psmouse, - "overflow -- 0x%02x 0x%02x 0x%02x\n", - packet[0], packet[1], packet[2]); - - if (hgpk_discard_decay_hack(psmouse, x, y)) { - if (tpdebug) - psmouse_dbg(psmouse, "discarding\n"); - return; - } - - hgpk_spewing_hack(psmouse, left, right, x, y); - - if (tpdebug) - psmouse_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", - left, right, x, y); - - input_report_key(dev, BTN_LEFT, left); - input_report_key(dev, BTN_RIGHT, right); - - input_report_rel(dev, REL_X, x); - input_report_rel(dev, REL_Y, y); - - input_sync(dev); -} - -static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - - if (!hgpk_is_byte_valid(psmouse, psmouse->packet)) - return PSMOUSE_BAD_DATA; - - if (psmouse->pktcnt >= psmouse->pktsize) { - if (priv->mode == HGPK_MODE_MOUSE) - hgpk_process_simple_packet(psmouse); - else - hgpk_process_advanced_packet(psmouse); - return PSMOUSE_FULL_PACKET; - } - - if (priv->recalib_window) { - if (time_before(jiffies, priv->recalib_window)) { - /* - * ugh, got a packet inside our recalibration - * window, schedule another recalibration. - */ - psmouse_dbg(psmouse, - "packet inside calibration window, queueing another recalibration\n"); - psmouse_queue_work(psmouse, &priv->recalib_wq, - msecs_to_jiffies(post_interrupt_delay)); - } - priv->recalib_window = 0; - } - - return PSMOUSE_GOOD_DATA; -} - -static int hgpk_select_mode(struct psmouse *psmouse) -{ - struct ps2dev *ps2dev = &psmouse->ps2dev; - struct hgpk_data *priv = psmouse->private; - int i; - int cmd; - - /* - * 4 disables to enable advanced mode - * then 3 0xf2 bytes as the preamble for GS/PT selection - */ - const int advanced_init[] = { - PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE, - PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE, - 0xf2, 0xf2, 0xf2, - }; - - switch (priv->mode) { - case HGPK_MODE_MOUSE: - psmouse->pktsize = 3; - break; - - case HGPK_MODE_GLIDESENSOR: - case HGPK_MODE_PENTABLET: - psmouse->pktsize = 6; - - /* Switch to 'Advanced mode.', four disables in a row. */ - for (i = 0; i < ARRAY_SIZE(advanced_init); i++) - if (ps2_command(ps2dev, NULL, advanced_init[i])) - return -EIO; - - /* select between GlideSensor (mouse) or PenTablet */ - cmd = priv->mode == HGPK_MODE_GLIDESENSOR ? - PSMOUSE_CMD_SETSCALE11 : PSMOUSE_CMD_SETSCALE21; - - if (ps2_command(ps2dev, NULL, cmd)) - return -EIO; - break; - - default: - return -EINVAL; - } - - return 0; -} - -static void hgpk_setup_input_device(struct input_dev *input, - struct input_dev *old_input, - enum hgpk_mode mode) -{ - if (old_input) { - input->name = old_input->name; - input->phys = old_input->phys; - input->id = old_input->id; - input->dev.parent = old_input->dev.parent; - } - - memset(input->evbit, 0, sizeof(input->evbit)); - memset(input->relbit, 0, sizeof(input->relbit)); - memset(input->keybit, 0, sizeof(input->keybit)); - - /* All modes report left and right buttons */ - __set_bit(EV_KEY, input->evbit); - __set_bit(BTN_LEFT, input->keybit); - __set_bit(BTN_RIGHT, input->keybit); - - switch (mode) { - case HGPK_MODE_MOUSE: - __set_bit(EV_REL, input->evbit); - __set_bit(REL_X, input->relbit); - __set_bit(REL_Y, input->relbit); - break; - - case HGPK_MODE_GLIDESENSOR: - __set_bit(BTN_TOUCH, input->keybit); - __set_bit(BTN_TOOL_FINGER, input->keybit); - - __set_bit(EV_ABS, input->evbit); - - /* GlideSensor has pressure sensor, PenTablet does not */ - input_set_abs_params(input, ABS_PRESSURE, 0, 15, 0, 0); - - /* From device specs */ - input_set_abs_params(input, ABS_X, 0, 399, 0, 0); - input_set_abs_params(input, ABS_Y, 0, 290, 0, 0); - - /* Calculated by hand based on usable size (52mm x 38mm) */ - input_abs_set_res(input, ABS_X, 8); - input_abs_set_res(input, ABS_Y, 8); - break; - - case HGPK_MODE_PENTABLET: - __set_bit(BTN_TOUCH, input->keybit); - __set_bit(BTN_TOOL_FINGER, input->keybit); - - __set_bit(EV_ABS, input->evbit); - - /* From device specs */ - input_set_abs_params(input, ABS_X, 0, 999, 0, 0); - input_set_abs_params(input, ABS_Y, 5, 239, 0, 0); - - /* Calculated by hand based on usable size (156mm x 38mm) */ - input_abs_set_res(input, ABS_X, 6); - input_abs_set_res(input, ABS_Y, 8); - break; - - default: - BUG(); - } -} - -static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate) -{ - int err; - - psmouse_reset(psmouse); - - if (recalibrate) { - struct ps2dev *ps2dev = &psmouse->ps2dev; - - /* send the recalibrate request */ - if (ps2_command(ps2dev, NULL, 0xf5) || - ps2_command(ps2dev, NULL, 0xf5) || - ps2_command(ps2dev, NULL, 0xe6) || - ps2_command(ps2dev, NULL, 0xf5)) { - return -1; - } - - /* according to ALPS, 150mS is required for recalibration */ - msleep(150); - } - - err = hgpk_select_mode(psmouse); - if (err) { - psmouse_err(psmouse, "failed to select mode\n"); - return err; - } - - hgpk_reset_hack_state(psmouse); - - return 0; -} - -static int hgpk_force_recalibrate(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - int err; - - /* C-series touchpads added the recalibrate command */ - if (psmouse->model < HGPK_MODEL_C) - return 0; - - if (!autorecal) { - psmouse_dbg(psmouse, "recalibration disabled, ignoring\n"); - return 0; - } - - psmouse_dbg(psmouse, "recalibrating touchpad..\n"); - - /* we don't want to race with the irq handler, nor with resyncs */ - psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); - - /* start by resetting the device */ - err = hgpk_reset_device(psmouse, true); - if (err) - return err; - - /* - * XXX: If a finger is down during this delay, recalibration will - * detect capacitance incorrectly. This is a hardware bug, and - * we don't have a good way to deal with it. The 2s window stuff - * (below) is our best option for now. - */ - if (psmouse_activate(psmouse)) - return -1; - - if (tpdebug) - psmouse_dbg(psmouse, "touchpad reactivated\n"); - - /* - * If we get packets right away after recalibrating, it's likely - * that a finger was on the touchpad. If so, it's probably - * miscalibrated, so we optionally schedule another. - */ - if (recal_guard_time) - priv->recalib_window = jiffies + - msecs_to_jiffies(recal_guard_time); - - return 0; -} - -/* - * This puts the touchpad in a power saving mode; according to ALPS, current - * consumption goes down to 50uA after running this. To turn power back on, - * we drive MS-DAT low. Measuring with a 1mA resolution ammeter says that - * the current on the SUS_3.3V rail drops from 3mA or 4mA to 0 when we do this. - * - * We have no formal spec that details this operation -- the low-power - * sequence came from a long-lost email trail. - */ -static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable) -{ - struct ps2dev *ps2dev = &psmouse->ps2dev; - int timeo; - int err; - - /* Added on D-series touchpads */ - if (psmouse->model < HGPK_MODEL_D) - return 0; - - if (enable) { - psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); - - /* - * Sending a byte will drive MS-DAT low; this will wake up - * the controller. Once we get an ACK back from it, it - * means we can continue with the touchpad re-init. ALPS - * tells us that 1s should be long enough, so set that as - * the upper bound. (in practice, it takes about 3 loops.) - */ - for (timeo = 20; timeo > 0; timeo--) { - if (!ps2_sendbyte(ps2dev, PSMOUSE_CMD_DISABLE, 20)) - break; - msleep(25); - } - - err = hgpk_reset_device(psmouse, false); - if (err) { - psmouse_err(psmouse, "Failed to reset device!\n"); - return err; - } - - /* should be all set, enable the touchpad */ - psmouse_activate(psmouse); - psmouse_dbg(psmouse, "Touchpad powered up.\n"); - } else { - psmouse_dbg(psmouse, "Powering off touchpad.\n"); - - if (ps2_command(ps2dev, NULL, 0xec) || - ps2_command(ps2dev, NULL, 0xec) || - ps2_command(ps2dev, NULL, 0xea)) { - return -1; - } - - psmouse_set_state(psmouse, PSMOUSE_IGNORE); - - /* probably won't see an ACK, the touchpad will be off */ - ps2_sendbyte(ps2dev, 0xec, 20); - } - - return 0; -} - -static int hgpk_poll(struct psmouse *psmouse) -{ - /* We can't poll, so always return failure. */ - return -1; -} - -static int hgpk_reconnect(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - - /* - * During suspend/resume the ps2 rails remain powered. We don't want - * to do a reset because it's flush data out of buffers; however, - * earlier prototypes (B1) had some brokenness that required a reset. - */ - if (olpc_board_at_least(olpc_board(0xb2))) - if (psmouse->ps2dev.serio->dev.power.power_state.event != - PM_EVENT_ON) - return 0; - - priv->powered = 1; - return hgpk_reset_device(psmouse, false); -} - -static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf) -{ - struct hgpk_data *priv = psmouse->private; - - return sprintf(buf, "%d\n", priv->powered); -} - -static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data, - const char *buf, size_t count) -{ - struct hgpk_data *priv = psmouse->private; - unsigned int value; - int err; - - err = kstrtouint(buf, 10, &value); - if (err) - return err; - - if (value > 1) - return -EINVAL; - - if (value != priv->powered) { - /* - * hgpk_toggle_power will deal w/ state so - * we're not racing w/ irq - */ - err = hgpk_toggle_powersave(psmouse, value); - if (!err) - priv->powered = value; - } - - return err ? err : count; -} - -__PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL, - hgpk_show_powered, hgpk_set_powered, false); - -static ssize_t attr_show_mode(struct psmouse *psmouse, void *data, char *buf) -{ - struct hgpk_data *priv = psmouse->private; - - return sprintf(buf, "%s\n", hgpk_mode_names[priv->mode]); -} - -static ssize_t attr_set_mode(struct psmouse *psmouse, void *data, - const char *buf, size_t len) -{ - struct hgpk_data *priv = psmouse->private; - enum hgpk_mode old_mode = priv->mode; - enum hgpk_mode new_mode = hgpk_mode_from_name(buf, len); - struct input_dev *old_dev = psmouse->dev; - struct input_dev *new_dev; - int err; - - if (new_mode == HGPK_MODE_INVALID) - return -EINVAL; - - if (old_mode == new_mode) - return len; - - new_dev = input_allocate_device(); - if (!new_dev) - return -ENOMEM; - - psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); - - /* Switch device into the new mode */ - priv->mode = new_mode; - err = hgpk_reset_device(psmouse, false); - if (err) - goto err_try_restore; - - hgpk_setup_input_device(new_dev, old_dev, new_mode); - - psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - - err = input_register_device(new_dev); - if (err) - goto err_try_restore; - - psmouse->dev = new_dev; - input_unregister_device(old_dev); - - return len; - -err_try_restore: - input_free_device(new_dev); - priv->mode = old_mode; - hgpk_reset_device(psmouse, false); - - return err; -} - -PSMOUSE_DEFINE_ATTR(hgpk_mode, S_IWUSR | S_IRUGO, NULL, - attr_show_mode, attr_set_mode); - -static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse, - void *data, char *buf) -{ - return -EINVAL; -} - -static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data, - const char *buf, size_t count) -{ - struct hgpk_data *priv = psmouse->private; - unsigned int value; - int err; - - err = kstrtouint(buf, 10, &value); - if (err) - return err; - - if (value != 1) - return -EINVAL; - - /* - * We queue work instead of doing recalibration right here - * to avoid adding locking to hgpk_force_recalibrate() - * since workqueue provides serialization. - */ - psmouse_queue_work(psmouse, &priv->recalib_wq, 0); - return count; -} - -__PSMOUSE_DEFINE_ATTR(recalibrate, S_IWUSR | S_IRUGO, NULL, - hgpk_trigger_recal_show, hgpk_trigger_recal, false); - -static void hgpk_disconnect(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - - device_remove_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_powered.dattr); - device_remove_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_hgpk_mode.dattr); - - if (psmouse->model >= HGPK_MODEL_C) - device_remove_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_recalibrate.dattr); - - psmouse_reset(psmouse); - kfree(priv); -} - -static void hgpk_recalib_work(struct work_struct *work) -{ - struct delayed_work *w = to_delayed_work(work); - struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq); - struct psmouse *psmouse = priv->psmouse; - - if (hgpk_force_recalibrate(psmouse)) - psmouse_err(psmouse, "recalibration failed!\n"); -} - -static int hgpk_register(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - int err; - - /* register handlers */ - psmouse->protocol_handler = hgpk_process_byte; - psmouse->poll = hgpk_poll; - psmouse->disconnect = hgpk_disconnect; - psmouse->reconnect = hgpk_reconnect; - - /* Disable the idle resync. */ - psmouse->resync_time = 0; - /* Reset after a lot of bad bytes. */ - psmouse->resetafter = 1024; - - hgpk_setup_input_device(psmouse->dev, NULL, priv->mode); - - err = device_create_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_powered.dattr); - if (err) { - psmouse_err(psmouse, "Failed creating 'powered' sysfs node\n"); - return err; - } - - err = device_create_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_hgpk_mode.dattr); - if (err) { - psmouse_err(psmouse, - "Failed creating 'hgpk_mode' sysfs node\n"); - goto err_remove_powered; - } - - /* C-series touchpads added the recalibrate command */ - if (psmouse->model >= HGPK_MODEL_C) { - err = device_create_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_recalibrate.dattr); - if (err) { - psmouse_err(psmouse, - "Failed creating 'recalibrate' sysfs node\n"); - goto err_remove_mode; - } - } - - return 0; - -err_remove_mode: - device_remove_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_hgpk_mode.dattr); -err_remove_powered: - device_remove_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_powered.dattr); - return err; -} - -int hgpk_init(struct psmouse *psmouse) -{ - struct hgpk_data *priv; - int err; - - priv = kzalloc_obj(*priv); - if (!priv) { - err = -ENOMEM; - goto alloc_fail; - } - - psmouse->private = priv; - - priv->psmouse = psmouse; - priv->powered = true; - priv->mode = hgpk_default_mode; - INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work); - - err = hgpk_reset_device(psmouse, false); - if (err) - goto init_fail; - - err = hgpk_register(psmouse); - if (err) - goto init_fail; - - return 0; - -init_fail: - kfree(priv); -alloc_fail: - return err; -} - -static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse) -{ - struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[3]; - - /* E7, E7, E7, E9 gets us a 3 byte identifier */ - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { - return -EIO; - } - - psmouse_dbg(psmouse, "ID: %*ph\n", 3, param); - - /* HGPK signature: 0x67, 0x00, 0x */ - if (param[0] != 0x67 || param[1] != 0x00) - return -ENODEV; - - psmouse_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]); - - return param[2]; -} - -int hgpk_detect(struct psmouse *psmouse, bool set_properties) -{ - int version; - - version = hgpk_get_model(psmouse); - if (version < 0) - return version; - - if (set_properties) { - psmouse->vendor = "ALPS"; - psmouse->name = "HGPK"; - psmouse->model = version; - } - - return 0; -} - -void hgpk_module_init(void) -{ - hgpk_default_mode = hgpk_mode_from_name(hgpk_mode_name, - strlen(hgpk_mode_name)); - if (hgpk_default_mode == HGPK_MODE_INVALID) { - hgpk_default_mode = HGPK_MODE_MOUSE; - strscpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE], - sizeof(hgpk_mode_name)); - } -} diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h deleted file mode 100644 index ce041591f1a8..000000000000 --- a/drivers/input/mouse/hgpk.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * OLPC HGPK (XO-1) touchpad PS/2 mouse driver - */ - -#ifndef _HGPK_H -#define _HGPK_H - -#define HGPK_GS 0xff /* The GlideSensor */ -#define HGPK_PT 0xcf /* The PenTablet */ - -enum hgpk_model_t { - HGPK_MODEL_PREA = 0x0a, /* pre-B1s */ - HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */ - HGPK_MODEL_B = 0x28, /* B2s, has capacitance issues */ - HGPK_MODEL_C = 0x3c, - HGPK_MODEL_D = 0x50, /* C1, mass production */ -}; - -enum hgpk_spew_flag { - NO_SPEW, - MAYBE_SPEWING, - SPEW_DETECTED, - RECALIBRATING, -}; - -#define SPEW_WATCH_COUNT 42 /* at 12ms/packet, this is 1/2 second */ - -enum hgpk_mode { - HGPK_MODE_MOUSE, - HGPK_MODE_GLIDESENSOR, - HGPK_MODE_PENTABLET, - HGPK_MODE_INVALID -}; - -struct hgpk_data { - struct psmouse *psmouse; - enum hgpk_mode mode; - bool powered; - enum hgpk_spew_flag spew_flag; - int spew_count, x_tally, y_tally; /* spew detection */ - unsigned long recalib_window; - struct delayed_work recalib_wq; - int abs_x, abs_y; - int dupe_count; - int xbigj, ybigj, xlast, ylast; /* jumpiness detection */ - int xsaw_secondary, ysaw_secondary; /* jumpiness detection */ -}; - -int hgpk_detect(struct psmouse *psmouse, bool set_properties); -int hgpk_init(struct psmouse *psmouse); - -#ifdef CONFIG_MOUSE_PS2_OLPC -void hgpk_module_init(void); -#else -static inline void hgpk_module_init(void) -{ -} -#endif - -#endif diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c deleted file mode 100644 index 401d8bff8e84..000000000000 --- a/drivers/input/mouse/inport.c +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 1999-2001 Vojtech Pavlik - * - * Based on the work of: - * Teemu Rantanen Derrick Cole - * Peter Cervasio Christoph Niemann - * Philip Blundell Russell King - * Bob Harris - */ - -/* - * Inport (ATI XL and Microsoft) busmouse driver for Linux - */ - -#include -#include -#include -#include -#include - -#include -#include - -MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Inport (ATI XL and Microsoft) busmouse driver"); -MODULE_LICENSE("GPL"); - -#define INPORT_BASE 0x23c -#define INPORT_EXTENT 4 - -#define INPORT_CONTROL_PORT INPORT_BASE + 0 -#define INPORT_DATA_PORT INPORT_BASE + 1 -#define INPORT_SIGNATURE_PORT INPORT_BASE + 2 - -#define INPORT_REG_BTNS 0x00 -#define INPORT_REG_X 0x01 -#define INPORT_REG_Y 0x02 -#define INPORT_REG_MODE 0x07 -#define INPORT_RESET 0x80 - -#ifdef CONFIG_MOUSE_ATIXL -#define INPORT_NAME "ATI XL Mouse" -#define INPORT_VENDOR 0x0002 -#define INPORT_SPEED_30HZ 0x01 -#define INPORT_SPEED_50HZ 0x02 -#define INPORT_SPEED_100HZ 0x03 -#define INPORT_SPEED_200HZ 0x04 -#define INPORT_MODE_BASE INPORT_SPEED_100HZ -#define INPORT_MODE_IRQ 0x08 -#else -#define INPORT_NAME "Microsoft InPort Mouse" -#define INPORT_VENDOR 0x0001 -#define INPORT_MODE_BASE 0x10 -#define INPORT_MODE_IRQ 0x01 -#endif -#define INPORT_MODE_HOLD 0x20 - -#define INPORT_IRQ 5 - -static int inport_irq = INPORT_IRQ; -module_param_hw_named(irq, inport_irq, uint, irq, 0); -MODULE_PARM_DESC(irq, "IRQ number (5=default)"); - -static struct input_dev *inport_dev; - -static irqreturn_t inport_interrupt(int irq, void *dev_id) -{ - unsigned char buttons; - - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); - - outb(INPORT_REG_X, INPORT_CONTROL_PORT); - input_report_rel(inport_dev, REL_X, inb(INPORT_DATA_PORT)); - - outb(INPORT_REG_Y, INPORT_CONTROL_PORT); - input_report_rel(inport_dev, REL_Y, inb(INPORT_DATA_PORT)); - - outb(INPORT_REG_BTNS, INPORT_CONTROL_PORT); - buttons = inb(INPORT_DATA_PORT); - - input_report_key(inport_dev, BTN_MIDDLE, buttons & 1); - input_report_key(inport_dev, BTN_LEFT, buttons & 2); - input_report_key(inport_dev, BTN_RIGHT, buttons & 4); - - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); - - input_sync(inport_dev); - return IRQ_HANDLED; -} - -static int inport_open(struct input_dev *dev) -{ - if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL)) - return -EBUSY; - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); - - return 0; -} - -static void inport_close(struct input_dev *dev) -{ - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_BASE, INPORT_DATA_PORT); - free_irq(inport_irq, NULL); -} - -static int __init inport_init(void) -{ - unsigned char a, b, c; - int err; - - if (!request_region(INPORT_BASE, INPORT_EXTENT, "inport")) { - printk(KERN_ERR "inport.c: Can't allocate ports at %#x\n", INPORT_BASE); - return -EBUSY; - } - - a = inb(INPORT_SIGNATURE_PORT); - b = inb(INPORT_SIGNATURE_PORT); - c = inb(INPORT_SIGNATURE_PORT); - if (a == b || a != c) { - printk(KERN_INFO "inport.c: Didn't find InPort mouse at %#x\n", INPORT_BASE); - err = -ENODEV; - goto err_release_region; - } - - inport_dev = input_allocate_device(); - if (!inport_dev) { - printk(KERN_ERR "inport.c: Not enough memory for input device\n"); - err = -ENOMEM; - goto err_release_region; - } - - inport_dev->name = INPORT_NAME; - inport_dev->phys = "isa023c/input0"; - inport_dev->id.bustype = BUS_ISA; - inport_dev->id.vendor = INPORT_VENDOR; - inport_dev->id.product = 0x0001; - inport_dev->id.version = 0x0100; - - inport_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - inport_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); - inport_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - - inport_dev->open = inport_open; - inport_dev->close = inport_close; - - outb(INPORT_RESET, INPORT_CONTROL_PORT); - outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); - outb(INPORT_MODE_BASE, INPORT_DATA_PORT); - - err = input_register_device(inport_dev); - if (err) - goto err_free_dev; - - return 0; - - err_free_dev: - input_free_device(inport_dev); - err_release_region: - release_region(INPORT_BASE, INPORT_EXTENT); - - return err; -} - -static void __exit inport_exit(void) -{ - input_unregister_device(inport_dev); - release_region(INPORT_BASE, INPORT_EXTENT); -} - -module_init(inport_init); -module_exit(inport_exit); diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c deleted file mode 100644 index 0aab63dbc30a..000000000000 --- a/drivers/input/mouse/logibm.c +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 1999-2001 Vojtech Pavlik - * - * Based on the work of: - * James Banks Matthew Dillon - * David Giller Nathan Laredo - * Linus Torvalds Johan Myreen - * Cliff Matthews Philip Blundell - * Russell King - */ - -/* - * Logitech Bus Mouse Driver for Linux - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Logitech busmouse driver"); -MODULE_LICENSE("GPL"); - -#define LOGIBM_BASE 0x23c -#define LOGIBM_EXTENT 4 - -#define LOGIBM_DATA_PORT LOGIBM_BASE + 0 -#define LOGIBM_SIGNATURE_PORT LOGIBM_BASE + 1 -#define LOGIBM_CONTROL_PORT LOGIBM_BASE + 2 -#define LOGIBM_CONFIG_PORT LOGIBM_BASE + 3 - -#define LOGIBM_ENABLE_IRQ 0x00 -#define LOGIBM_DISABLE_IRQ 0x10 -#define LOGIBM_READ_X_LOW 0x80 -#define LOGIBM_READ_X_HIGH 0xa0 -#define LOGIBM_READ_Y_LOW 0xc0 -#define LOGIBM_READ_Y_HIGH 0xe0 - -#define LOGIBM_DEFAULT_MODE 0x90 -#define LOGIBM_CONFIG_BYTE 0x91 -#define LOGIBM_SIGNATURE_BYTE 0xa5 - -#define LOGIBM_IRQ 5 - -static int logibm_irq = LOGIBM_IRQ; -module_param_hw_named(irq, logibm_irq, uint, irq, 0); -MODULE_PARM_DESC(irq, "IRQ number (5=default)"); - -static struct input_dev *logibm_dev; - -static irqreturn_t logibm_interrupt(int irq, void *dev_id) -{ - char dx, dy; - unsigned char buttons; - - outb(LOGIBM_READ_X_LOW, LOGIBM_CONTROL_PORT); - dx = (inb(LOGIBM_DATA_PORT) & 0xf); - outb(LOGIBM_READ_X_HIGH, LOGIBM_CONTROL_PORT); - dx |= (inb(LOGIBM_DATA_PORT) & 0xf) << 4; - outb(LOGIBM_READ_Y_LOW, LOGIBM_CONTROL_PORT); - dy = (inb(LOGIBM_DATA_PORT) & 0xf); - outb(LOGIBM_READ_Y_HIGH, LOGIBM_CONTROL_PORT); - buttons = inb(LOGIBM_DATA_PORT); - dy |= (buttons & 0xf) << 4; - buttons = ~buttons >> 5; - - input_report_rel(logibm_dev, REL_X, dx); - input_report_rel(logibm_dev, REL_Y, dy); - input_report_key(logibm_dev, BTN_RIGHT, buttons & 1); - input_report_key(logibm_dev, BTN_MIDDLE, buttons & 2); - input_report_key(logibm_dev, BTN_LEFT, buttons & 4); - input_sync(logibm_dev); - - outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT); - return IRQ_HANDLED; -} - -static int logibm_open(struct input_dev *dev) -{ - if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) { - printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq); - return -EBUSY; - } - outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT); - return 0; -} - -static void logibm_close(struct input_dev *dev) -{ - outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); - free_irq(logibm_irq, NULL); -} - -static int __init logibm_init(void) -{ - int err; - - if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) { - printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE); - return -EBUSY; - } - - outb(LOGIBM_CONFIG_BYTE, LOGIBM_CONFIG_PORT); - outb(LOGIBM_SIGNATURE_BYTE, LOGIBM_SIGNATURE_PORT); - udelay(100); - - if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) { - printk(KERN_INFO "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE); - err = -ENODEV; - goto err_release_region; - } - - outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT); - outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); - - logibm_dev = input_allocate_device(); - if (!logibm_dev) { - printk(KERN_ERR "logibm.c: Not enough memory for input device\n"); - err = -ENOMEM; - goto err_release_region; - } - - logibm_dev->name = "Logitech bus mouse"; - logibm_dev->phys = "isa023c/input0"; - logibm_dev->id.bustype = BUS_ISA; - logibm_dev->id.vendor = 0x0003; - logibm_dev->id.product = 0x0001; - logibm_dev->id.version = 0x0100; - - logibm_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - logibm_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); - logibm_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - - logibm_dev->open = logibm_open; - logibm_dev->close = logibm_close; - - err = input_register_device(logibm_dev); - if (err) - goto err_free_dev; - - return 0; - - err_free_dev: - input_free_device(logibm_dev); - err_release_region: - release_region(LOGIBM_BASE, LOGIBM_EXTENT); - - return err; -} - -static void __exit logibm_exit(void) -{ - input_unregister_device(logibm_dev); - release_region(LOGIBM_BASE, LOGIBM_EXTENT); -} - -module_init(logibm_init); -module_exit(logibm_exit); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index ff8cd2d68a3c..6ab5f1d96eae 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -26,7 +26,6 @@ #include "synaptics.h" #include "logips2pp.h" #include "alps.h" -#include "hgpk.h" #include "lifebook.h" #include "trackpoint.h" #include "touchkit_ps2.h" @@ -114,8 +113,6 @@ ATTRIBUTE_GROUPS(psmouse_dev); */ static DEFINE_MUTEX(psmouse_mutex); -static struct workqueue_struct *kpsmoused_wq; - struct psmouse *psmouse_from_serio(struct serio *serio) { struct ps2dev *ps2dev = serio_get_drvdata(serio); @@ -241,12 +238,6 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) return PSMOUSE_FULL_PACKET; } -void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, - unsigned long delay) -{ - queue_delayed_work(kpsmoused_wq, work, delay); -} - /* * __psmouse_set_state() sets new psmouse state and resets all flags. */ @@ -380,7 +371,7 @@ static void psmouse_receive_byte(struct ps2dev *ps2dev, u8 data) psmouse->name, psmouse->phys, psmouse->pktcnt); psmouse->badbyte = psmouse->packet[0]; __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); - psmouse_queue_work(psmouse, &psmouse->resync_work, 0); + schedule_work(&psmouse->resync_work); return; } @@ -393,9 +384,7 @@ static void psmouse_receive_byte(struct ps2dev *ps2dev, u8 data) return; } - if (psmouse->packet[1] == PSMOUSE_RET_ID || - (psmouse->protocol->type == PSMOUSE_HGPK && - psmouse->packet[1] == PSMOUSE_RET_BAT)) { + if (psmouse->packet[1] == PSMOUSE_RET_ID) { __psmouse_set_state(psmouse, PSMOUSE_IGNORE); serio_reconnect(ps2dev->serio); return; @@ -418,7 +407,7 @@ static void psmouse_receive_byte(struct ps2dev *ps2dev, u8 data) time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) { psmouse->badbyte = psmouse->packet[0]; __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); - psmouse_queue_work(psmouse, &psmouse->resync_work, 0); + schedule_work(&psmouse->resync_work); return; } @@ -837,14 +826,6 @@ static const struct psmouse_protocol psmouse_protocols[] = { .detect = touchkit_ps2_detect, }, #endif -#ifdef CONFIG_MOUSE_PS2_OLPC - { - .type = PSMOUSE_HGPK, - .name = "OLPC HGPK", - .alias = "hgpk", - .detect = hgpk_detect, - }, -#endif #ifdef CONFIG_MOUSE_PS2_ELANTECH { .type = PSMOUSE_ELANTECH, @@ -1153,13 +1134,6 @@ static int psmouse_extensions(struct psmouse *psmouse, return PSMOUSE_ALPS; } - /* Try OLPC HGPK touchpad */ - if (max_proto > PSMOUSE_IMEX && - psmouse_try_protocol(psmouse, PSMOUSE_HGPK, &max_proto, - set_properties, true)) { - return PSMOUSE_HGPK; - } - /* Try Elantech touchpad */ if (max_proto > PSMOUSE_IMEX && psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH, @@ -1331,7 +1305,7 @@ int psmouse_deactivate(struct psmouse *psmouse) static void psmouse_resync(struct work_struct *work) { struct psmouse *parent = NULL, *psmouse = - container_of(work, struct psmouse, resync_work.work); + container_of(work, struct psmouse, resync_work); struct serio *serio = psmouse->ps2dev.serio; psmouse_ret_t rc = PSMOUSE_GOOD_DATA; bool failed = false, enabled = false; @@ -1484,7 +1458,7 @@ static void psmouse_disconnect(struct serio *serio) /* make sure we don't have a resync in progress */ mutex_unlock(&psmouse_mutex); - flush_workqueue(kpsmoused_wq); + disable_work_sync(&psmouse->resync_work); mutex_lock(&psmouse_mutex); if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { @@ -1598,7 +1572,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) ps2_init(&psmouse->ps2dev, serio, psmouse_pre_receive_byte, psmouse_receive_byte); - INIT_DELAYED_WORK(&psmouse->resync_work, psmouse_resync); + INIT_WORK(&psmouse->resync_work, psmouse_resync); psmouse->dev = input_dev; scnprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0", serio->phys); @@ -2035,27 +2009,17 @@ static int __init psmouse_init(void) lifebook_module_init(); synaptics_module_init(); - hgpk_module_init(); err = psmouse_smbus_module_init(); if (err) return err; - kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0); - if (!kpsmoused_wq) { - pr_err("failed to create kpsmoused workqueue\n"); - err = -ENOMEM; - goto err_smbus_exit; - } - err = serio_register_driver(&psmouse_drv); if (err) - goto err_destroy_wq; + goto err_smbus_exit; return 0; -err_destroy_wq: - destroy_workqueue(kpsmoused_wq); err_smbus_exit: psmouse_smbus_module_exit(); return err; @@ -2064,7 +2028,6 @@ err_smbus_exit: static void __exit psmouse_exit(void) { serio_unregister_driver(&psmouse_drv); - destroy_workqueue(kpsmoused_wq); psmouse_smbus_module_exit(); } diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 4d8acfe0d82a..90ed8cd15d85 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -59,7 +59,7 @@ enum psmouse_type { PSMOUSE_TRACKPOINT, PSMOUSE_TOUCHKIT_PS2, PSMOUSE_CORTRON, - PSMOUSE_HGPK, + PSMOUSE_HGPK, /* No longer used */ PSMOUSE_ELANTECH, PSMOUSE_FSP, PSMOUSE_SYNAPTICS_RELATIVE, @@ -90,7 +90,7 @@ struct psmouse { void *private; struct input_dev *dev; struct ps2dev ps2dev; - struct delayed_work resync_work; + struct work_struct resync_work; const char *vendor; const char *name; const struct psmouse_protocol *protocol; @@ -132,8 +132,6 @@ struct psmouse { struct psmouse *psmouse_from_serio(struct serio *serio); -void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, - unsigned long delay); int psmouse_reset(struct psmouse *psmouse); void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state); void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c index 5a86f6f387d8..880a0c79148c 100644 --- a/drivers/input/mouse/synaptics_usb.c +++ b/drivers/input/mouse/synaptics_usb.c @@ -220,25 +220,6 @@ resubmit: __func__, error); } -static struct usb_endpoint_descriptor * -synusb_get_in_endpoint(struct usb_host_interface *iface) -{ - - struct usb_endpoint_descriptor *endpoint; - int i; - - for (i = 0; i < iface->desc.bNumEndpoints; ++i) { - endpoint = &iface->endpoint[i].desc; - - if (usb_endpoint_is_int_in(endpoint)) { - /* we found our interrupt in endpoint */ - return endpoint; - } - } - - return NULL; -} - static int synusb_open(struct input_dev *dev) { struct synusb *synusb = input_get_drvdata(dev); @@ -307,8 +288,8 @@ static int synusb_probe(struct usb_interface *intf, return error; } - ep = synusb_get_in_endpoint(intf->cur_altsetting); - if (!ep) + error = usb_find_int_in_endpoint(intf->cur_altsetting, &ep); + if (error) return -ENODEV; synusb = kzalloc_obj(*synusb); diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index b06c7ad721fe..3bd8fdf56cd3 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -5,6 +5,7 @@ * Trademarks are the property of their respective owners. */ +#include #include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include "psmouse.h" #include "trackpoint.h" @@ -393,6 +395,44 @@ static int trackpoint_reconnect(struct psmouse *psmouse) return 0; } +/* List of known incapable device PNP IDs */ +static const char * const dt_incompatible_devices[] = { + "LEN0304", + "LEN0306", + "LEN0317", + "LEN031A", + "LEN031B", + "LEN031C", + "LEN031D", +}; + +/* + * Checks if it's a doubletap capable device. + * The PNP ID format is "PNP: LEN030d PNP0f13". + */ +static bool trackpoint_is_dt_capable(const char *pnp_id) +{ + size_t i; + + if (!pnp_id) + return false; + + /* Must start with "PNP: LEN03" */ + if (!strstarts(pnp_id, "PNP: LEN03")) + return false; + + /* Ensure enough length before comparing */ + if (strlen(pnp_id) < 12) + return false; + + /* Check deny-list */ + for (i = 0; i < ARRAY_SIZE(dt_incompatible_devices); i++) { + if (!strncmp(pnp_id + 5, dt_incompatible_devices[i], 7)) + return false; + } + return true; +} + int trackpoint_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; @@ -470,6 +510,12 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) psmouse->vendor, firmware_id, (button_info & 0xf0) >> 4, button_info & 0x0f); + if (trackpoint_is_dt_capable(ps2dev->serio->firmware_id)) { + error = trackpoint_write(ps2dev, TP_DOUBLETAP, TP_DOUBLETAP_ENABLE); + if (error) + psmouse_warn(psmouse, "Failed to enable doubletap: %d\n", error); + } + return 0; } diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h index eb5412904fe0..3e03cdb39449 100644 --- a/drivers/input/mouse/trackpoint.h +++ b/drivers/input/mouse/trackpoint.h @@ -69,6 +69,8 @@ /* (how hard it is to drag */ /* with Z-axis pressed) */ +#define TP_DOUBLETAP 0x58 /* TrackPoint doubletap register */ + #define TP_MINDRAG 0x59 /* Minimum amount of force needed */ /* to trigger dragging */ @@ -110,6 +112,9 @@ external device will be forced to 1 */ #define TP_MASK_EXT_TAG 0x04 +/* Doubletap register values */ +#define TP_DOUBLETAP_ENABLE 0xFF /* Enable value */ +#define TP_DOUBLETAP_DISABLE 0xFE /* Disable value */ /* Power on Self Test Results */ #define TP_POR_SUCCESS 0x3B diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index c7ef347a4dff..5f15a6462056 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -55,19 +55,6 @@ config SERIO_SERPORT To compile this driver as a module, choose M here: the module will be called serport. -config SERIO_CT82C710 - tristate "ct82c710 Aux port controller" - depends on X86 - help - Say Y here if you have a Texas Instruments TravelMate notebook - equipped with the ct82c710 chip and want to use a mouse connected - to the "QuickPort". - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called ct82c710. - config SERIO_Q40KBD tristate "Q40 keyboard controller" depends on Q40 diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 6d97bad7b844..8ab98f4aa28d 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_SERIO) += serio.o obj-$(CONFIG_SERIO_I8042) += i8042.o obj-$(CONFIG_SERIO_PARKBD) += parkbd.o obj-$(CONFIG_SERIO_SERPORT) += serport.o -obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o obj-$(CONFIG_SERIO_SA1111) += sa1111ps2.o obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c deleted file mode 100644 index fe5f9eda4b77..000000000000 --- a/drivers/input/serio/ct82c710.c +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 1999-2001 Vojtech Pavlik - */ - -/* - * 82C710 C&T mouse port chip driver for Linux - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("82C710 C&T mouse port chip driver"); -MODULE_LICENSE("GPL"); - -/* - * ct82c710 interface - */ - -#define CT82C710_DEV_IDLE 0x01 /* Device Idle */ -#define CT82C710_RX_FULL 0x02 /* Device Char received */ -#define CT82C710_TX_IDLE 0x04 /* Device XMIT Idle */ -#define CT82C710_RESET 0x08 /* Device Reset */ -#define CT82C710_INTS_ON 0x10 /* Device Interrupt On */ -#define CT82C710_ERROR_FLAG 0x20 /* Device Error */ -#define CT82C710_CLEAR 0x40 /* Device Clear */ -#define CT82C710_ENABLE 0x80 /* Device Enable */ - -#define CT82C710_IRQ 12 - -#define CT82C710_DATA ct82c710_iores.start -#define CT82C710_STATUS (ct82c710_iores.start + 1) - -static struct serio *ct82c710_port; -static struct platform_device *ct82c710_device; -static struct resource ct82c710_iores; - -/* - * Interrupt handler for the 82C710 mouse port. A character - * is waiting in the 82C710. - */ - -static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id) -{ - return serio_interrupt(ct82c710_port, inb(CT82C710_DATA), 0); -} - -/* - * Wait for device to send output char and flush any input char. - */ - -static int ct82c170_wait(void) -{ - int timeout = 60000; - - while ((inb(CT82C710_STATUS) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE)) - != (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) { - - if (inb_p(CT82C710_STATUS) & CT82C710_RX_FULL) inb_p(CT82C710_DATA); - - udelay(1); - timeout--; - } - - return !timeout; -} - -static void ct82c710_close(struct serio *serio) -{ - if (ct82c170_wait()) - printk(KERN_WARNING "ct82c710.c: Device busy in close()\n"); - - outb_p(inb_p(CT82C710_STATUS) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), CT82C710_STATUS); - - if (ct82c170_wait()) - printk(KERN_WARNING "ct82c710.c: Device busy in close()\n"); - - free_irq(CT82C710_IRQ, NULL); -} - -static int ct82c710_open(struct serio *serio) -{ - unsigned char status; - int err; - - err = request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL); - if (err) - return err; - - status = inb_p(CT82C710_STATUS); - - status |= (CT82C710_ENABLE | CT82C710_RESET); - outb_p(status, CT82C710_STATUS); - - status &= ~(CT82C710_RESET); - outb_p(status, CT82C710_STATUS); - - status |= CT82C710_INTS_ON; - outb_p(status, CT82C710_STATUS); /* Enable interrupts */ - - while (ct82c170_wait()) { - printk(KERN_ERR "ct82c710: Device busy in open()\n"); - status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON); - outb_p(status, CT82C710_STATUS); - free_irq(CT82C710_IRQ, NULL); - return -EBUSY; - } - - return 0; -} - -/* - * Write to the 82C710 mouse device. - */ - -static int ct82c710_write(struct serio *port, unsigned char c) -{ - if (ct82c170_wait()) return -1; - outb_p(c, CT82C710_DATA); - return 0; -} - -/* - * See if we can find a 82C710 device. Read mouse address. - */ - -static int __init ct82c710_detect(void) -{ - outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */ - outb_p(0xaa, 0x3fa); /* Inverse of 55 */ - outb_p(0x36, 0x3fa); /* Address the chip */ - outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */ - outb_p(0x1b, 0x2fa); /* Inverse of e4 */ - outb_p(0x0f, 0x390); /* Write index */ - if (inb_p(0x391) != 0xe4) /* Config address found? */ - return -ENODEV; /* No: no 82C710 here */ - - outb_p(0x0d, 0x390); /* Write index */ - ct82c710_iores.start = inb_p(0x391) << 2; /* Get mouse I/O address */ - ct82c710_iores.end = ct82c710_iores.start + 1; - ct82c710_iores.flags = IORESOURCE_IO; - outb_p(0x0f, 0x390); - outb_p(0x0f, 0x391); /* Close config mode */ - - return 0; -} - -static int ct82c710_probe(struct platform_device *dev) -{ - ct82c710_port = kzalloc_obj(*ct82c710_port); - if (!ct82c710_port) - return -ENOMEM; - - ct82c710_port->id.type = SERIO_8042; - ct82c710_port->dev.parent = &dev->dev; - ct82c710_port->open = ct82c710_open; - ct82c710_port->close = ct82c710_close; - ct82c710_port->write = ct82c710_write; - strscpy(ct82c710_port->name, "C&T 82c710 mouse port", - sizeof(ct82c710_port->name)); - snprintf(ct82c710_port->phys, sizeof(ct82c710_port->phys), - "isa%16llx/serio0", (unsigned long long)CT82C710_DATA); - - serio_register_port(ct82c710_port); - - printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n", - (unsigned long long)CT82C710_DATA, CT82C710_IRQ); - - return 0; -} - -static void ct82c710_remove(struct platform_device *dev) -{ - serio_unregister_port(ct82c710_port); -} - -static struct platform_driver ct82c710_driver = { - .driver = { - .name = "ct82c710", - }, - .probe = ct82c710_probe, - .remove = ct82c710_remove, -}; - - -static int __init ct82c710_init(void) -{ - int error; - - error = ct82c710_detect(); - if (error) - return error; - - error = platform_driver_register(&ct82c710_driver); - if (error) - return error; - - ct82c710_device = platform_device_alloc("ct82c710", -1); - if (!ct82c710_device) { - error = -ENOMEM; - goto err_unregister_driver; - } - - error = platform_device_add_resources(ct82c710_device, &ct82c710_iores, 1); - if (error) - goto err_free_device; - - error = platform_device_add(ct82c710_device); - if (error) - goto err_free_device; - - return 0; - - err_free_device: - platform_device_put(ct82c710_device); - err_unregister_driver: - platform_driver_unregister(&ct82c710_driver); - return error; -} - -static void __exit ct82c710_exit(void) -{ - platform_device_unregister(ct82c710_device); - platform_driver_unregister(&ct82c710_driver); -} - -module_init(ct82c710_init); -module_exit(ct82c710_exit); diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 269df83a167d..05b64277aecd 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -154,10 +154,8 @@ EXPORT_SYMBOL(ps2_end_command); */ void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout) { - if (maxbytes > sizeof(ps2dev->cmdbuf)) { - WARN_ON(1); + if (WARN_ON(maxbytes > sizeof(ps2dev->cmdbuf))) maxbytes = sizeof(ps2dev->cmdbuf); - } ps2_begin_command(ps2dev); @@ -270,15 +268,11 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) int i; u8 send_param[16]; - if (receive > sizeof(ps2dev->cmdbuf)) { - WARN_ON(1); + if (WARN_ON(receive > sizeof(ps2dev->cmdbuf))) return -EINVAL; - } - if (send && !param) { - WARN_ON(1); + if (WARN_ON(send && !param)) return -EINVAL; - } memcpy(send_param, param, send); diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index 6df24cee3c9d..c850b5890070 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -57,6 +57,7 @@ * http://aiptektablet.sourceforge.net. */ +#include #include #include #include @@ -164,8 +165,6 @@ #define USB_VENDOR_ID_AIPTEK 0x08ca #define USB_VENDOR_ID_KYE 0x0458 -#define USB_REQ_GET_REPORT 0x01 -#define USB_REQ_SET_REPORT 0x09 /* PointerMode codes */ @@ -658,6 +657,8 @@ static void aiptek_irq(struct urb *urb) pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0; macro = dv && p && tip && !(data[3] & 1) ? (data[3] >> 1) : -1; + if (macro >= ARRAY_SIZE(macroKeyEvents)) + macro = -1; z = get_unaligned_le16(data + 4); if (dv) { @@ -699,7 +700,9 @@ static void aiptek_irq(struct urb *urb) left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; - macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : 0; + macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : -1; + if (macro >= ARRAY_SIZE(macroKeyEvents)) + macro = -1; if (dv) { /* If the selected tool changed, reset the old @@ -737,11 +740,11 @@ static void aiptek_irq(struct urb *urb) */ else if (data[0] == 6) { macro = get_unaligned_le16(data + 1); - if (macro > 0) { + if (macro > 0 && macro - 1 < ARRAY_SIZE(macroKeyEvents)) { input_report_key(inputdev, macroKeyEvents[macro - 1], 0); } - if (macro < 25) { + if (macro + 1 < ARRAY_SIZE(macroKeyEvents)) { input_report_key(inputdev, macroKeyEvents[macro + 1], 0); } @@ -760,7 +763,8 @@ static void aiptek_irq(struct urb *urb) aiptek->curSetting.toolMode; } - input_report_key(inputdev, macroKeyEvents[macro], 1); + if (macro < ARRAY_SIZE(macroKeyEvents)) + input_report_key(inputdev, macroKeyEvents[macro], 1); input_report_abs(inputdev, ABS_MISC, 1 | AIPTEK_REPORT_TOOL_UNKNOWN); input_sync(inputdev); @@ -856,7 +860,7 @@ aiptek_set_report(struct aiptek *aiptek, return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_SET_REPORT, + HID_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, (report_type << 8) + report_id, aiptek->ifnum, buffer, size, 5000); @@ -871,7 +875,7 @@ aiptek_get_report(struct aiptek *aiptek, return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - USB_REQ_GET_REPORT, + HID_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, (report_type << 8) + report_id, aiptek->ifnum, buffer, size, 5000); diff --git a/drivers/input/tablet/pegasus_notetaker.c b/drivers/input/tablet/pegasus_notetaker.c index 4ce20befc657..85390ae42307 100644 --- a/drivers/input/tablet/pegasus_notetaker.c +++ b/drivers/input/tablet/pegasus_notetaker.c @@ -36,6 +36,7 @@ * T Tip */ +#include #include #include #include @@ -44,10 +45,6 @@ #include #include -/* USB HID defines */ -#define USB_REQ_GET_REPORT 0x01 -#define USB_REQ_SET_REPORT 0x09 - #define USB_VENDOR_ID_PEGASUSTECH 0x0e20 #define USB_DEVICE_ID_PEGASUS_NOTETAKER_EN100 0x0101 @@ -108,7 +105,7 @@ static int pegasus_control_msg(struct pegasus *pegasus, u8 *data, int len) result = usb_control_msg(pegasus->usbdev, usb_sndctrlpipe(pegasus->usbdev, 0), - USB_REQ_SET_REPORT, + HID_REQ_SET_REPORT, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, cmd_buf, sizeof_buf, USB_CTRL_SET_TIMEOUT); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 7d5b72ee07fa..aeaf9a9cbb41 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -723,18 +723,6 @@ config TOUCHSCREEN_INEXIO To compile this driver as a module, choose M here: the module will be called inexio. -config TOUCHSCREEN_MK712 - tristate "ICS MicroClock MK712 touchscreen" - depends on ISA - help - Say Y here if you have the ICS MicroClock MK712 touchscreen - controller chip in your system. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called mk712. - config TOUCHSCREEN_HP600 tristate "HP Jornada 6xx touchscreen" depends on SH_HP6XX && SH_ADC diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index ab9abd151078..f2b002abebe8 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -68,7 +68,6 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o obj-$(CONFIG_TOUCHSCREEN_MSG2638) += msg2638.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o -obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_NOVATEK_NVT_TS) += novatek-nvt-ts.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index 33c5eb522389..cc39057d0117 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -376,17 +376,14 @@ static inline void ad7877_ts_event_release(struct ad7877 *ts) static void ad7877_timer(struct timer_list *t) { struct ad7877 *ts = timer_container_of(ts, t, timer); - unsigned long flags; - spin_lock_irqsave(&ts->lock, flags); + guard(spinlock_irqsave)(&ts->lock); ad7877_ts_event_release(ts); - spin_unlock_irqrestore(&ts->lock, flags); } static irqreturn_t ad7877_irq(int irq, void *handle) { struct ad7877 *ts = handle; - unsigned long flags; int error; error = spi_sync(ts->spi, &ts->msg); @@ -395,11 +392,13 @@ static irqreturn_t ad7877_irq(int irq, void *handle) goto out; } - spin_lock_irqsave(&ts->lock, flags); - error = ad7877_process_data(ts); - if (!error) + scoped_guard(spinlock_irqsave, &ts->lock) { + error = ad7877_process_data(ts); + if (error) + goto out; + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); - spin_unlock_irqrestore(&ts->lock, flags); + } out: return IRQ_HANDLED; @@ -409,7 +408,7 @@ static void ad7877_disable(void *data) { struct ad7877 *ts = data; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); if (!ts->disabled) { ts->disabled = true; @@ -423,20 +422,16 @@ static void ad7877_disable(void *data) * We know the chip's in lowpower mode since we always * leave it that way after every request */ - - mutex_unlock(&ts->mutex); } static void ad7877_enable(struct ad7877 *ts) { - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); if (ts->disabled) { ts->disabled = false; enable_irq(ts->spi->irq); } - - mutex_unlock(&ts->mutex); } #define SHOW(name) static ssize_t \ @@ -509,10 +504,9 @@ static ssize_t ad7877_dac_store(struct device *dev, if (error) return error; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); ts->dac = val & 0xFF; ad7877_write(ts->spi, AD7877_REG_DAC, (ts->dac << 4) | AD7877_DAC_CONF); - mutex_unlock(&ts->mutex); return count; } @@ -539,11 +533,10 @@ static ssize_t ad7877_gpio3_store(struct device *dev, if (error) return error; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); ts->gpio3 = !!val; ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA | (ts->gpio4 << 4) | (ts->gpio3 << 5)); - mutex_unlock(&ts->mutex); return count; } @@ -570,11 +563,10 @@ static ssize_t ad7877_gpio4_store(struct device *dev, if (error) return error; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); ts->gpio4 = !!val; ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA | (ts->gpio4 << 4) | (ts->gpio3 << 5)); - mutex_unlock(&ts->mutex); return count; } diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 4c448f39bf57..31d2a3029d5f 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -305,15 +305,13 @@ static int __maybe_unused ad7879_suspend(struct device *dev) { struct ad7879 *ts = dev_get_drvdata(dev); - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); if (!ts->suspended && !ts->disabled && input_device_enabled(ts->input)) __ad7879_disable(ts); ts->suspended = true; - mutex_unlock(&ts->input->mutex); - return 0; } @@ -321,15 +319,13 @@ static int __maybe_unused ad7879_resume(struct device *dev) { struct ad7879 *ts = dev_get_drvdata(dev); - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); if (ts->suspended && !ts->disabled && input_device_enabled(ts->input)) __ad7879_enable(ts); ts->suspended = false; - mutex_unlock(&ts->input->mutex); - return 0; } @@ -338,7 +334,7 @@ EXPORT_SYMBOL(ad7879_pm_ops); static void ad7879_toggle(struct ad7879 *ts, bool disable) { - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); if (!ts->suspended && input_device_enabled(ts->input)) { @@ -352,8 +348,6 @@ static void ad7879_toggle(struct ad7879 *ts, bool disable) } ts->disabled = disable; - - mutex_unlock(&ts->input->mutex); } static ssize_t ad7879_disable_show(struct device *dev, @@ -403,23 +397,20 @@ static int ad7879_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { struct ad7879 *ts = gpiochip_get_data(chip); - int err; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); + ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL; - err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); - mutex_unlock(&ts->mutex); - - return err; + return ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); } static int ad7879_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int level) { struct ad7879 *ts = gpiochip_get_data(chip); - int err; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); + ts->cmd_crtl2 &= ~AD7879_GPIODIR; ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIOPOL; if (level) @@ -427,21 +418,17 @@ static int ad7879_gpio_direction_output(struct gpio_chip *chip, else ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; - err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); - mutex_unlock(&ts->mutex); - - return err; + return ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); } -static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned int gpio) { struct ad7879 *ts = gpiochip_get_data(chip); u16 val; - mutex_lock(&ts->mutex); - val = ad7879_read(ts, AD7879_REG_CTRL2); - mutex_unlock(&ts->mutex); + guard(mutex)(&ts->mutex); + val = ad7879_read(ts, AD7879_REG_CTRL2); return !!(val & AD7879_GPIO_DATA); } @@ -449,18 +436,15 @@ static int ad7879_gpio_set_value(struct gpio_chip *chip, unsigned int gpio, int value) { struct ad7879 *ts = gpiochip_get_data(chip); - int ret; - mutex_lock(&ts->mutex); + guard(mutex)(&ts->mutex); + if (value) ts->cmd_crtl2 |= AD7879_GPIO_DATA; else ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; - ret = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); - mutex_unlock(&ts->mutex); - - return ret; + return ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); } static int ad7879_gpio_add(struct ad7879 *ts) diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 0963b1a78a0c..4b39f7212d35 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -289,7 +289,7 @@ static void __ads7846_enable(struct ads7846 *ts) static void ads7846_disable(struct ads7846 *ts) { - mutex_lock(&ts->lock); + guard(mutex)(&ts->lock); if (!ts->disabled) { @@ -298,13 +298,11 @@ static void ads7846_disable(struct ads7846 *ts) ts->disabled = true; } - - mutex_unlock(&ts->lock); } static void ads7846_enable(struct ads7846 *ts) { - mutex_lock(&ts->lock); + guard(mutex)(&ts->lock); if (ts->disabled) { @@ -313,8 +311,6 @@ static void ads7846_enable(struct ads7846 *ts) if (!ts->suspended) __ads7846_enable(ts); } - - mutex_unlock(&ts->lock); } /*--------------------------------------------------------------------------*/ @@ -354,10 +350,9 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) { struct spi_device *spi = to_spi_device(dev); struct ads7846 *ts = dev_get_drvdata(dev); - struct ser_req *req; int status; - req = kzalloc_obj(*req); + struct ser_req *req __free(kfree) = kzalloc_obj(*req); if (!req) return -ENOMEM; @@ -418,11 +413,11 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) CS_CHANGE(req->xfer[7]); spi_message_add_tail(&req->xfer[7], &req->msg); - mutex_lock(&ts->lock); - ads7846_stop(ts); - status = spi_sync(spi, &req->msg); - ads7846_restart(ts); - mutex_unlock(&ts->lock); + scoped_guard(mutex, &ts->lock) { + ads7846_stop(ts); + status = spi_sync(spi, &req->msg); + ads7846_restart(ts); + } if (status == 0) { /* on-wire is a must-ignore bit, a BE12 value, then padding */ @@ -431,7 +426,6 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) status &= 0x0fff; } - kfree(req); return status; } @@ -439,10 +433,9 @@ static int ads7845_read12_ser(struct device *dev, unsigned command) { struct spi_device *spi = to_spi_device(dev); struct ads7846 *ts = dev_get_drvdata(dev); - struct ads7845_ser_req *req; int status; - req = kzalloc_obj(*req); + struct ads7845_ser_req *req __free(kfree) = kzalloc_obj(*req); if (!req) return -ENOMEM; @@ -454,11 +447,11 @@ static int ads7845_read12_ser(struct device *dev, unsigned command) req->xfer[0].len = 3; spi_message_add_tail(&req->xfer[0], &req->msg); - mutex_lock(&ts->lock); - ads7846_stop(ts); - status = spi_sync(spi, &req->msg); - ads7846_restart(ts); - mutex_unlock(&ts->lock); + scoped_guard(mutex, &ts->lock) { + ads7846_stop(ts); + status = spi_sync(spi, &req->msg); + ads7846_restart(ts); + } if (status == 0) { /* BE12 value, then padding */ @@ -467,7 +460,6 @@ static int ads7845_read12_ser(struct device *dev, unsigned command) status &= 0x0fff; } - kfree(req); return status; } @@ -966,7 +958,7 @@ static int ads7846_suspend(struct device *dev) { struct ads7846 *ts = dev_get_drvdata(dev); - mutex_lock(&ts->lock); + guard(mutex)(&ts->lock); if (!ts->suspended) { @@ -979,8 +971,6 @@ static int ads7846_suspend(struct device *dev) ts->suspended = true; } - mutex_unlock(&ts->lock); - return 0; } @@ -988,7 +978,7 @@ static int ads7846_resume(struct device *dev) { struct ads7846 *ts = dev_get_drvdata(dev); - mutex_lock(&ts->lock); + guard(mutex)(&ts->lock); if (ts->suspended) { @@ -1001,8 +991,6 @@ static int ads7846_resume(struct device *dev) __ads7846_enable(ts); } - mutex_unlock(&ts->lock); - return 0; } diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index dd0544cc1bc1..87c6a10381f2 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -713,12 +713,11 @@ static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, const void *val) { bool retried = false; - u8 *buf; - size_t count; + size_t count = len + 2; + int error; int ret; - count = len + 2; - buf = kmalloc(count, GFP_KERNEL); + u8 *buf __free(kfree) = kmalloc(count, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -728,20 +727,17 @@ static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, retry: ret = i2c_master_send(client, buf, count); - if (ret == count) { - ret = 0; - } else if (!retried && mxt_wakeup_toggle(client, true, true)) { + if (ret == count) + return 0; + + if (!retried && mxt_wakeup_toggle(client, true, true)) { retried = true; goto retry; - } else { - if (ret >= 0) - ret = -EIO; - dev_err(&client->dev, "%s: i2c send failed (%d)\n", - __func__, ret); } - kfree(buf); - return ret; + error = ret < 0 ? ret : -EIO; + dev_err(&client->dev, "%s: i2c send failed (%d)\n", __func__, error); + return error; } static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val) @@ -1547,14 +1543,15 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) { struct device *dev = &data->client->dev; struct mxt_cfg cfg; - int ret; + int error; int offset; int i; u32 info_crc, config_crc, calculated_crc; u16 crc_start = 0; /* Make zero terminated copy of the OBP_RAW file */ - cfg.raw = kmemdup_nul(fw->data, fw->size, GFP_KERNEL); + u8 *raw_buf __free(kfree) = cfg.raw = kmemdup_nul(fw->data, fw->size, + GFP_KERNEL); if (!cfg.raw) return -ENOMEM; @@ -1564,21 +1561,17 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) if (strncmp(cfg.raw, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { dev_err(dev, "Unrecognised config file\n"); - ret = -EINVAL; - goto release_raw; + return -EINVAL; } cfg.raw_pos = strlen(MXT_CFG_MAGIC); /* Load information block and check */ for (i = 0; i < sizeof(struct mxt_info); i++) { - ret = sscanf(cfg.raw + cfg.raw_pos, "%hhx%n", - (unsigned char *)&cfg.info + i, - &offset); - if (ret != 1) { + if (sscanf(cfg.raw + cfg.raw_pos, "%hhx%n", + (unsigned char *)&cfg.info + i, &offset) != 1) { dev_err(dev, "Bad format\n"); - ret = -EINVAL; - goto release_raw; + return -EINVAL; } cfg.raw_pos += offset; @@ -1586,30 +1579,24 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) if (cfg.info.family_id != data->info->family_id) { dev_err(dev, "Family ID mismatch!\n"); - ret = -EINVAL; - goto release_raw; + return -EINVAL; } if (cfg.info.variant_id != data->info->variant_id) { dev_err(dev, "Variant ID mismatch!\n"); - ret = -EINVAL; - goto release_raw; + return -EINVAL; } /* Read CRCs */ - ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &info_crc, &offset); - if (ret != 1) { + if (sscanf(cfg.raw + cfg.raw_pos, "%x%n", &info_crc, &offset) != 1) { dev_err(dev, "Bad format: failed to parse Info CRC\n"); - ret = -EINVAL; - goto release_raw; + return -EINVAL; } cfg.raw_pos += offset; - ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &config_crc, &offset); - if (ret != 1) { + if (sscanf(cfg.raw + cfg.raw_pos, "%x%n", &config_crc, &offset) != 1) { dev_err(dev, "Bad format: failed to parse Config CRC\n"); - ret = -EINVAL; - goto release_raw; + return -EINVAL; } cfg.raw_pos += offset; @@ -1625,8 +1612,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) } else if (config_crc == data->config_crc) { dev_dbg(dev, "Config CRC 0x%06X: OK\n", data->config_crc); - ret = 0; - goto release_raw; + return 0; } else { dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", data->config_crc, config_crc); @@ -1642,15 +1628,14 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) data->info->object_num * sizeof(struct mxt_object) + MXT_INFO_CHECKSUM_SIZE; cfg.mem_size = data->mem_size - cfg.start_ofs; - cfg.mem = kzalloc(cfg.mem_size, GFP_KERNEL); - if (!cfg.mem) { - ret = -ENOMEM; - goto release_raw; - } - ret = mxt_prepare_cfg_mem(data, &cfg); - if (ret) - goto release_mem; + u8 *mem_buf __free(kfree) = cfg.mem = kzalloc(cfg.mem_size, GFP_KERNEL); + if (!cfg.mem) + return -ENOMEM; + + error = mxt_prepare_cfg_mem(data, &cfg); + if (error) + return error; /* Calculate crc of the received configs (not the raw config file) */ if (data->T71_address) @@ -1670,30 +1655,26 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) calculated_crc, config_crc); } - ret = mxt_upload_cfg_mem(data, &cfg); - if (ret) - goto release_mem; + error = mxt_upload_cfg_mem(data, &cfg); + if (error) + return error; mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); - ret = mxt_check_retrigen(data); - if (ret) - goto release_mem; + error = mxt_check_retrigen(data); + if (error) + return error; - ret = mxt_soft_reset(data); - if (ret) - goto release_mem; + error = mxt_soft_reset(data); + if (error) + return error; dev_info(dev, "Config successfully updated\n"); /* T7 config may have changed */ mxt_init_t7_power_cfg(data); -release_mem: - kfree(cfg.mem); -release_raw: - kfree(cfg.raw); - return ret; + return 0; } static void mxt_free_input_device(struct mxt_data *data) @@ -1857,7 +1838,6 @@ static int mxt_read_info_block(struct mxt_data *data) struct i2c_client *client = data->client; int error; size_t size; - void *id_buf, *buf; uint8_t num_objects; u32 calculated_crc; u8 *crc_ptr; @@ -1868,24 +1848,23 @@ static int mxt_read_info_block(struct mxt_data *data) /* Read 7-byte ID information block starting at address 0 */ size = sizeof(struct mxt_info); - id_buf = kzalloc(size, GFP_KERNEL); + void *id_buf __free(kfree) = kzalloc(size, GFP_KERNEL); if (!id_buf) return -ENOMEM; error = __mxt_read_reg(client, 0, size, id_buf); if (error) - goto err_free_mem; + return error; /* Resize buffer to give space for rest of info block */ num_objects = ((struct mxt_info *)id_buf)->object_num; size += (num_objects * sizeof(struct mxt_object)) + MXT_INFO_CHECKSUM_SIZE; - buf = krealloc(id_buf, size, GFP_KERNEL); - if (!buf) { - error = -ENOMEM; - goto err_free_mem; - } + void *buf = krealloc(id_buf, size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + id_buf = buf; /* Read rest of info block */ @@ -1893,7 +1872,7 @@ static int mxt_read_info_block(struct mxt_data *data) size - MXT_OBJECT_START, id_buf + MXT_OBJECT_START); if (error) - goto err_free_mem; + return error; /* Extract & calculate checksum */ crc_ptr = id_buf + size - MXT_INFO_CHECKSUM_SIZE; @@ -1910,12 +1889,11 @@ static int mxt_read_info_block(struct mxt_data *data) dev_err(&client->dev, "Info Block CRC error calculated=0x%06X read=0x%06X\n", calculated_crc, data->info_crc); - error = -EIO; - goto err_free_mem; + return -EIO; } - data->raw_info_block = id_buf; - data->info = (struct mxt_info *)id_buf; + data->raw_info_block = no_free_ptr(id_buf); + data->info = (struct mxt_info *)data->raw_info_block; dev_info(&client->dev, "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", @@ -1924,20 +1902,18 @@ static int mxt_read_info_block(struct mxt_data *data) data->info->build, data->info->object_num); /* Parse object table information */ - error = mxt_parse_object_table(data, id_buf + MXT_OBJECT_START); + error = mxt_parse_object_table(data, + data->raw_info_block + MXT_OBJECT_START); if (error) { dev_err(&client->dev, "Error %d parsing object table\n", error); mxt_free_object_table(data); return error; } - data->object_table = (struct mxt_object *)(id_buf + MXT_OBJECT_START); + data->object_table = + (struct mxt_object *)(data->raw_info_block + MXT_OBJECT_START); return 0; - -err_free_mem: - kfree(id_buf); - return error; } static int mxt_read_t9_resolution(struct mxt_data *data) @@ -2914,70 +2890,38 @@ static int mxt_check_firmware_format(struct device *dev, return -EINVAL; } -static int mxt_load_fw(struct device *dev, const char *fn) +static int mxt_flash_fw(struct mxt_data *data, const struct firmware *fw) { - struct mxt_data *data = dev_get_drvdata(dev); - const struct firmware *fw = NULL; + struct device *dev = &data->client->dev; unsigned int frame_size; unsigned int pos = 0; unsigned int retry = 0; unsigned int frame = 0; - int ret; - - ret = request_firmware(&fw, fn, dev); - if (ret) { - dev_err(dev, "Unable to open firmware %s\n", fn); - return ret; - } - - /* Check for incorrect enc file */ - ret = mxt_check_firmware_format(dev, fw); - if (ret) - goto release_firmware; - - if (!data->in_bootloader) { - /* Change to the bootloader mode */ - data->in_bootloader = true; - - ret = mxt_t6_command(data, MXT_COMMAND_RESET, - MXT_BOOT_VALUE, false); - if (ret) - goto release_firmware; - - msleep(MXT_RESET_TIME); - - /* Do not need to scan since we know family ID */ - ret = mxt_lookup_bootloader_address(data, 0); - if (ret) - goto release_firmware; - - mxt_free_input_device(data); - mxt_free_object_table(data); - } else { - enable_irq(data->irq); - } + int error; reinit_completion(&data->bl_completion); - ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); - if (ret) { + error = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); + if (error) { /* Bootloader may still be unlocked from previous attempt */ - ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false); - if (ret) - goto disable_irq; + error = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, + false); + if (error) + return error; } else { dev_info(dev, "Unlocking bootloader\n"); /* Unlock bootloader */ - ret = mxt_send_bootloader_cmd(data, true); - if (ret) - goto disable_irq; + error = mxt_send_bootloader_cmd(data, true); + if (error) + return error; } while (pos < fw->size) { - ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true); - if (ret) - goto disable_irq; + error = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, + true); + if (error) + return error; frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); @@ -2985,12 +2929,12 @@ static int mxt_load_fw(struct device *dev, const char *fn) frame_size += 2; /* Write one frame to device */ - ret = mxt_bootloader_write(data, fw->data + pos, frame_size); - if (ret) - goto disable_irq; + error = mxt_bootloader_write(data, fw->data + pos, frame_size); + if (error) + return error; - ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); - if (ret) { + error = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); + if (error) { retry++; /* Back off by 20ms per retry */ @@ -2998,7 +2942,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (retry > 20) { dev_err(dev, "Retry count exceeded\n"); - goto disable_irq; + return error; } } else { retry = 0; @@ -3012,10 +2956,10 @@ static int mxt_load_fw(struct device *dev, const char *fn) } /* Wait for flash. */ - ret = mxt_wait_for_completion(data, &data->bl_completion, - MXT_FW_RESET_TIME); - if (ret) - goto disable_irq; + error = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_RESET_TIME); + if (error) + return error; dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos); @@ -3025,14 +2969,56 @@ static int mxt_load_fw(struct device *dev, const char *fn) * errors. */ mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME); - data->in_bootloader = false; -disable_irq: + return 0; +} + +static int mxt_load_fw(struct device *dev, const char *fn) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int retval; + int error; + + const struct firmware *fw __free(firmware) = NULL; + error = request_firmware(&fw, fn, dev); + if (error) { + dev_err(dev, "Unable to open firmware %s\n", fn); + return error; + } + + /* Check for incorrect enc file */ + error = mxt_check_firmware_format(dev, fw); + if (error) + return error; + + if (!data->in_bootloader) { + /* Change to the bootloader mode */ + data->in_bootloader = true; + + error = mxt_t6_command(data, MXT_COMMAND_RESET, + MXT_BOOT_VALUE, false); + if (error) + return error; + + msleep(MXT_RESET_TIME); + + /* Do not need to scan since we know family ID */ + error = mxt_lookup_bootloader_address(data, 0); + if (error) + return error; + + mxt_free_input_device(data); + mxt_free_object_table(data); + } else { + enable_irq(data->irq); + } + + retval = mxt_flash_fw(data, fw); + disable_irq(data->irq); -release_firmware: - release_firmware(fw); - return ret; + + return retval; } static ssize_t mxt_update_fw_store(struct device *dev, @@ -3375,12 +3361,10 @@ static int mxt_suspend(struct device *dev) if (!input_dev) return 0; - mutex_lock(&input_dev->mutex); - - if (input_device_enabled(input_dev)) - mxt_stop(data); - - mutex_unlock(&input_dev->mutex); + scoped_guard(mutex, &input_dev->mutex) { + if (input_device_enabled(input_dev)) + mxt_stop(data); + } disable_irq(data->irq); @@ -3398,12 +3382,10 @@ static int mxt_resume(struct device *dev) enable_irq(data->irq); - mutex_lock(&input_dev->mutex); - - if (input_device_enabled(input_dev)) - mxt_start(data); - - mutex_unlock(&input_dev->mutex); + scoped_guard(mutex, &input_dev->mutex) { + if (input_device_enabled(input_dev)) + mxt_start(data); + } return 0; } diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c index 363a4a1f1560..401b2264f585 100644 --- a/drivers/input/touchscreen/auo-pixcir-ts.c +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -415,9 +415,9 @@ static int auo_pixcir_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct auo_pixcir_ts *ts = i2c_get_clientdata(client); struct input_dev *input = ts->input; - int ret = 0; + int error; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); /* when configured as wakeup source, device should always wake system * therefore start device if necessary @@ -425,21 +425,23 @@ static int auo_pixcir_suspend(struct device *dev) if (device_may_wakeup(&client->dev)) { /* need to start device if not open, to be wakeup source */ if (!input_device_enabled(input)) { - ret = auo_pixcir_start(ts); - if (ret) - goto unlock; + error = auo_pixcir_start(ts); + if (error) + return error; } enable_irq_wake(client->irq); - ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_SLEEP); + error = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_SLEEP); + if (error) + return error; + } else if (input_device_enabled(input)) { - ret = auo_pixcir_stop(ts); + error = auo_pixcir_stop(ts); + if (error) + return error; } -unlock: - mutex_unlock(&input->mutex); - - return ret; + return 0; } static int auo_pixcir_resume(struct device *dev) @@ -447,29 +449,28 @@ static int auo_pixcir_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct auo_pixcir_ts *ts = i2c_get_clientdata(client); struct input_dev *input = ts->input; - int ret = 0; + int error; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (device_may_wakeup(&client->dev)) { disable_irq_wake(client->irq); /* need to stop device if it was not open on suspend */ if (!input_device_enabled(input)) { - ret = auo_pixcir_stop(ts); - if (ret) - goto unlock; + error = auo_pixcir_stop(ts); + if (error) + return error; } /* device wakes automatically from SLEEP */ } else if (input_device_enabled(input)) { - ret = auo_pixcir_start(ts); + error = auo_pixcir_start(ts); + if (error) + return error; } -unlock: - mutex_unlock(&input->mutex); - - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, diff --git a/drivers/input/touchscreen/bu21029_ts.c b/drivers/input/touchscreen/bu21029_ts.c index 64f474e67312..68d9cd55ceeb 100644 --- a/drivers/input/touchscreen/bu21029_ts.c +++ b/drivers/input/touchscreen/bu21029_ts.c @@ -416,10 +416,10 @@ static int bu21029_suspend(struct device *dev) struct bu21029_ts_data *bu21029 = i2c_get_clientdata(i2c); if (!device_may_wakeup(dev)) { - mutex_lock(&bu21029->in_dev->mutex); + guard(mutex)(&bu21029->in_dev->mutex); + if (input_device_enabled(bu21029->in_dev)) bu21029_stop_chip(bu21029->in_dev); - mutex_unlock(&bu21029->in_dev->mutex); } return 0; @@ -431,10 +431,10 @@ static int bu21029_resume(struct device *dev) struct bu21029_ts_data *bu21029 = i2c_get_clientdata(i2c); if (!device_may_wakeup(dev)) { - mutex_lock(&bu21029->in_dev->mutex); + guard(mutex)(&bu21029->in_dev->mutex); + if (input_device_enabled(bu21029->in_dev)) bu21029_start_chip(bu21029->in_dev); - mutex_unlock(&bu21029->in_dev->mutex); } return 0; diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c index d6876d10b252..1b10a757313c 100644 --- a/drivers/input/touchscreen/chipone_icn8318.c +++ b/drivers/input/touchscreen/chipone_icn8318.c @@ -152,10 +152,10 @@ static int icn8318_suspend(struct device *dev) { struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&data->input->mutex); + guard(mutex)(&data->input->mutex); + if (input_device_enabled(data->input)) icn8318_stop(data->input); - mutex_unlock(&data->input->mutex); return 0; } @@ -164,10 +164,10 @@ static int icn8318_resume(struct device *dev) { struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&data->input->mutex); + guard(mutex)(&data->input->mutex); + if (input_device_enabled(data->input)) icn8318_start(data->input); - mutex_unlock(&data->input->mutex); return 0; } diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 9e729910fbc8..012dfcae01cc 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -494,34 +494,30 @@ static int cyttsp_disable(struct cyttsp *ts) static int cyttsp_suspend(struct device *dev) { struct cyttsp *ts = dev_get_drvdata(dev); - int retval = 0; + int error; - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); if (input_device_enabled(ts->input)) { - retval = cyttsp_disable(ts); - if (retval == 0) - ts->suspended = true; + error = cyttsp_disable(ts); + if (error) + return error; } - mutex_unlock(&ts->input->mutex); - - return retval; + ts->suspended = true; + return 0; } static int cyttsp_resume(struct device *dev) { struct cyttsp *ts = dev_get_drvdata(dev); - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); if (input_device_enabled(ts->input)) cyttsp_enable(ts); ts->suspended = false; - - mutex_unlock(&ts->input->mutex); - return 0; } diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index d0ab644be006..d3b1177185a3 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -380,16 +380,13 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, container_of(dattr, struct edt_ft5x06_attribute, dattr); u8 *field = (u8 *)tsdata + attr->field_offset; unsigned int val; - size_t count = 0; - int error = 0; + int error; u8 addr; - mutex_lock(&tsdata->mutex); + guard(mutex)(&tsdata->mutex); - if (tsdata->factory_mode) { - error = -EIO; - goto out; - } + if (tsdata->factory_mode) + return -EIO; switch (tsdata->version) { case EDT_M06: @@ -407,8 +404,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, break; default: - error = -ENODEV; - goto out; + return -ENODEV; } if (addr != NO_REGISTER) { @@ -417,7 +413,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, dev_err(&tsdata->client->dev, "Failed to fetch attribute %s, error %d\n", dattr->attr.name, error); - goto out; + return error; } } else { val = *field; @@ -430,10 +426,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, *field = val; } - count = sysfs_emit(buf, "%d\n", val); -out: - mutex_unlock(&tsdata->mutex); - return error ?: count; + return sysfs_emit(buf, "%d\n", val); } static ssize_t edt_ft5x06_setting_store(struct device *dev, @@ -449,21 +442,17 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, int error; u8 addr; - mutex_lock(&tsdata->mutex); + guard(mutex)(&tsdata->mutex); - if (tsdata->factory_mode) { - error = -EIO; - goto out; - } + if (tsdata->factory_mode) + return -EIO; error = kstrtouint(buf, 0, &val); if (error) - goto out; + return error; - if (val < attr->limit_low || val > attr->limit_high) { - error = -ERANGE; - goto out; - } + if (val < attr->limit_low || val > attr->limit_high) + return -ERANGE; switch (tsdata->version) { case EDT_M06: @@ -481,8 +470,7 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, break; default: - error = -ENODEV; - goto out; + return -ENODEV; } if (addr != NO_REGISTER) { @@ -491,14 +479,12 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, dev_err(&tsdata->client->dev, "Failed to update attribute %s, error: %d\n", dattr->attr.name, error); - goto out; + return error; } } *field = val; -out: - mutex_unlock(&tsdata->mutex); - return error ?: count; + return count; } /* m06, m09: range 0-31, m12: range 0-5 */ @@ -714,21 +700,17 @@ static int edt_ft5x06_debugfs_mode_get(void *data, u64 *mode) static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode) { struct edt_ft5x06_ts_data *tsdata = data; - int retval = 0; if (mode > 1) return -ERANGE; - mutex_lock(&tsdata->mutex); + guard(mutex)(&tsdata->mutex); - if (mode != tsdata->factory_mode) { - retval = mode ? edt_ft5x06_factory_mode(tsdata) : - edt_ft5x06_work_mode(tsdata); - } + if (mode == tsdata->factory_mode) + return 0; - mutex_unlock(&tsdata->mutex); - - return retval; + return mode ? edt_ft5x06_factory_mode(tsdata) : + edt_ft5x06_work_mode(tsdata); }; DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get, @@ -750,18 +732,16 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, if (*off < 0 || *off >= tsdata->raw_bufsize) return 0; - mutex_lock(&tsdata->mutex); + guard(mutex)(&tsdata->mutex); - if (!tsdata->factory_mode || !tsdata->raw_buffer) { - error = -EIO; - goto out; - } + if (!tsdata->factory_mode || !tsdata->raw_buffer) + return -EIO; error = regmap_write(tsdata->regmap, 0x08, 0x01); if (error) { dev_err(&client->dev, "failed to write 0x08 register, error %d\n", error); - goto out; + return error; } do { @@ -771,7 +751,7 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, dev_err(&client->dev, "failed to read 0x08 register, error %d\n", error); - goto out; + return error; } if (val == 1) @@ -781,8 +761,7 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, if (retries == 0) { dev_err(&client->dev, "timed out waiting for register to settle\n"); - error = -ETIMEDOUT; - goto out; + return -ETIMEDOUT; } rdbuf = tsdata->raw_buffer; @@ -792,21 +771,17 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, rdbuf[0] = i; /* column index */ error = regmap_bulk_read(tsdata->regmap, 0xf5, rdbuf, colbytes); if (error) - goto out; + return error; rdbuf += colbytes; } read = min_t(size_t, count, tsdata->raw_bufsize - *off); - if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) { - error = -EFAULT; - goto out; - } + if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) + return -EFAULT; *off += read; -out: - mutex_unlock(&tsdata->mutex); - return error ?: read; + return read; }; static const struct file_operations debugfs_raw_data_fops = { @@ -829,7 +804,10 @@ static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata) static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) { + guard(mutex)(&tsdata->mutex); + kfree(tsdata->raw_buffer); + tsdata->raw_buffer = NULL; } #else diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 87eb18977b71..492e19a28a74 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -89,7 +89,7 @@ static irqreturn_t eeti_ts_isr(int irq, void *dev_id) struct eeti_ts *eeti = dev_id; int error; - mutex_lock(&eeti->mutex); + guard(mutex)(&eeti->mutex); do { /* @@ -109,13 +109,12 @@ static irqreturn_t eeti_ts_isr(int irq, void *dev_id) } while (eeti->running && eeti->attn_gpio); - mutex_unlock(&eeti->mutex); return IRQ_HANDLED; } static void eeti_ts_start(struct eeti_ts *eeti) { - mutex_lock(&eeti->mutex); + guard(mutex)(&eeti->mutex); eeti->running = true; enable_irq(eeti->client->irq); @@ -127,8 +126,6 @@ static void eeti_ts_start(struct eeti_ts *eeti) */ if (eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio)) eeti_ts_read(eeti); - - mutex_unlock(&eeti->mutex); } static void eeti_ts_stop(struct eeti_ts *eeti) @@ -238,12 +235,10 @@ static int eeti_ts_suspend(struct device *dev) struct eeti_ts *eeti = i2c_get_clientdata(client); struct input_dev *input_dev = eeti->input; - mutex_lock(&input_dev->mutex); - - if (input_device_enabled(input_dev)) - eeti_ts_stop(eeti); - - mutex_unlock(&input_dev->mutex); + scoped_guard(mutex, &input_dev->mutex) { + if (input_device_enabled(input_dev)) + eeti_ts_stop(eeti); + } if (device_may_wakeup(&client->dev)) enable_irq_wake(client->irq); @@ -260,12 +255,10 @@ static int eeti_ts_resume(struct device *dev) if (device_may_wakeup(&client->dev)) disable_irq_wake(client->irq); - mutex_lock(&input_dev->mutex); - - if (input_device_enabled(input_dev)) - eeti_ts_start(eeti); - - mutex_unlock(&input_dev->mutex); + scoped_guard(mutex, &input_dev->mutex) { + if (input_device_enabled(input_dev)) + eeti_ts_start(eeti); + } return 0; } diff --git a/drivers/input/touchscreen/ektf2127.c b/drivers/input/touchscreen/ektf2127.c index 46a0611fac82..572a37d4e0aa 100644 --- a/drivers/input/touchscreen/ektf2127.c +++ b/drivers/input/touchscreen/ektf2127.c @@ -187,10 +187,10 @@ static int ektf2127_suspend(struct device *dev) { struct ektf2127_ts *ts = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); + if (input_device_enabled(ts->input)) ektf2127_stop(ts->input); - mutex_unlock(&ts->input->mutex); return 0; } @@ -199,10 +199,10 @@ static int ektf2127_resume(struct device *dev) { struct ektf2127_ts *ts = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); + if (input_device_enabled(ts->input)) ektf2127_start(ts->input); - mutex_unlock(&ts->input->mutex); return 0; } diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 3fd170f75b4a..835d91dff0a4 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -303,15 +303,13 @@ static int elants_i2c_calibrate(struct elants_data *ts) static const u8 rek[] = { CMD_HEADER_WRITE, 0x29, 0x00, 0x01 }; static const u8 rek_resp[] = { CMD_HEADER_REK, 0x66, 0x66, 0x66 }; - disable_irq(client->irq); + scoped_guard(disable_irq, &client->irq) { + ts->state = ELAN_WAIT_RECALIBRATION; + reinit_completion(&ts->cmd_done); - ts->state = ELAN_WAIT_RECALIBRATION; - reinit_completion(&ts->cmd_done); - - elants_i2c_send(client, w_flashkey, sizeof(w_flashkey)); - elants_i2c_send(client, rek, sizeof(rek)); - - enable_irq(client->irq); + elants_i2c_send(client, w_flashkey, sizeof(w_flashkey)); + elants_i2c_send(client, rek, sizeof(rek)); + } ret = wait_for_completion_interruptible_timeout(&ts->cmd_done, msecs_to_jiffies(ELAN_CALI_TIMEOUT_MSEC)); @@ -906,17 +904,17 @@ static int elants_i2c_do_update_firmware(struct i2c_client *client, static int elants_i2c_fw_update(struct elants_data *ts) { struct i2c_client *client = ts->client; - const struct firmware *fw; - char *fw_name; int error; - fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%04x.bin", ts->hw_version); + const char *fw_name __free(kfree) = + kasprintf(GFP_KERNEL, "elants_i2c_%04x.bin", ts->hw_version); if (!fw_name) return -ENOMEM; dev_info(&client->dev, "requesting fw name = %s\n", fw_name); + + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, fw_name, &client->dev); - kfree(fw_name); if (error) { dev_err(&client->dev, "failed to request firmware: %d\n", error); @@ -926,40 +924,32 @@ static int elants_i2c_fw_update(struct elants_data *ts) if (fw->size % ELAN_FW_PAGESIZE) { dev_err(&client->dev, "invalid firmware length: %zu\n", fw->size); - error = -EINVAL; - goto out; + return -EINVAL; } - disable_irq(client->irq); + scoped_guard(disable_irq, &client->irq) { + bool force_update = ts->iap_mode == ELAN_IAP_RECOVERY; - error = elants_i2c_do_update_firmware(client, fw, - ts->iap_mode == ELAN_IAP_RECOVERY); - if (error) { - dev_err(&client->dev, "firmware update failed: %d\n", error); - ts->iap_mode = ELAN_IAP_RECOVERY; - goto out_enable_irq; + error = elants_i2c_do_update_firmware(client, fw, force_update); + if (error) { + dev_err(&client->dev, "firmware update failed: %d\n", + error); + } else { + error = elants_i2c_initialize(ts); + if (error) + dev_err(&client->dev, + "failed to initialize device after firmware update: %d\n", + error); + } + + ts->iap_mode = error ? ELAN_IAP_RECOVERY : ELAN_IAP_OPERATIONAL; + ts->state = ELAN_STATE_NORMAL; } - - error = elants_i2c_initialize(ts); - if (error) { - dev_err(&client->dev, - "failed to initialize device after firmware update: %d\n", - error); - ts->iap_mode = ELAN_IAP_RECOVERY; - goto out_enable_irq; - } - - ts->iap_mode = ELAN_IAP_OPERATIONAL; - -out_enable_irq: - ts->state = ELAN_STATE_NORMAL; - enable_irq(client->irq); msleep(100); if (!error) elants_i2c_calibrate(ts); -out: - release_firmware(fw); + return error; } @@ -1186,14 +1176,13 @@ static ssize_t calibrate_store(struct device *dev, struct elants_data *ts = i2c_get_clientdata(client); int error; - error = mutex_lock_interruptible(&ts->sysfs_mutex); - if (error) - return error; + scoped_cond_guard(mutex_intr, return -EINTR, &ts->sysfs_mutex) { + error = elants_i2c_calibrate(ts); + if (error) + return error; + } - error = elants_i2c_calibrate(ts); - - mutex_unlock(&ts->sysfs_mutex); - return error ?: count; + return count; } static ssize_t write_update_fw(struct device *dev, @@ -1204,15 +1193,13 @@ static ssize_t write_update_fw(struct device *dev, struct elants_data *ts = i2c_get_clientdata(client); int error; - error = mutex_lock_interruptible(&ts->sysfs_mutex); - if (error) - return error; + scoped_cond_guard(mutex_intr, return -EINTR, &ts->sysfs_mutex) { + error = elants_i2c_fw_update(ts); + if (error) + return error; + } - error = elants_i2c_fw_update(ts); - dev_dbg(dev, "firmware update result: %d\n", error); - - mutex_unlock(&ts->sysfs_mutex); - return error ?: count; + return count; } static ssize_t show_iap_mode(struct device *dev, diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index 434b9b47e964..6814d5789b6f 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -219,40 +219,40 @@ static irqreturn_t elo_interrupt(struct serio *serio, static int elo_command_10(struct elo *elo, unsigned char *packet) { - int rc = -1; + int error; int i; unsigned char csum = 0xaa + ELO10_LEAD_BYTE; - mutex_lock(&elo->cmd_mutex); + guard(mutex)(&elo->cmd_mutex); scoped_guard(serio_pause_rx, elo->serio) { elo->expected_packet = toupper(packet[0]); init_completion(&elo->cmd_done); } - if (serio_write(elo->serio, ELO10_LEAD_BYTE)) - goto out; + error = serio_write(elo->serio, ELO10_LEAD_BYTE); + if (error) + return error; for (i = 0; i < ELO10_PACKET_LEN; i++) { csum += packet[i]; - if (serio_write(elo->serio, packet[i])) - goto out; + error = serio_write(elo->serio, packet[i]); + if (error) + return error; } - if (serio_write(elo->serio, csum)) - goto out; + error = serio_write(elo->serio, csum); + if (error) + return error; wait_for_completion_timeout(&elo->cmd_done, HZ); - if (elo->expected_packet == ELO10_TOUCH_PACKET) { - /* We are back in reporting mode, the command was ACKed */ - memcpy(packet, elo->response, ELO10_PACKET_LEN); - rc = 0; - } + if (elo->expected_packet != ELO10_TOUCH_PACKET) + return -EIO; - out: - mutex_unlock(&elo->cmd_mutex); - return rc; + /* We are back in reporting mode, the command was ACKed */ + memcpy(packet, elo->response, ELO10_PACKET_LEN); + return 0; } static int elo_setup_10(struct elo *elo) diff --git a/drivers/input/touchscreen/exc3000.c b/drivers/input/touchscreen/exc3000.c index 28da7ba55a4b..78c0911ba6e2 100644 --- a/drivers/input/touchscreen/exc3000.c +++ b/drivers/input/touchscreen/exc3000.c @@ -234,7 +234,7 @@ static int exc3000_vendor_data_request(struct exc3000_data *data, u8 *request, int ret; unsigned long time_left; - mutex_lock(&data->query_lock); + guard(mutex)(&data->query_lock); reinit_completion(&data->wait_event); @@ -243,29 +243,18 @@ static int exc3000_vendor_data_request(struct exc3000_data *data, u8 *request, ret = i2c_master_send(data->client, buf, EXC3000_LEN_VENDOR_REQUEST); if (ret < 0) - goto out_unlock; + return ret; - if (response) { - time_left = wait_for_completion_timeout(&data->wait_event, - timeout * HZ); - if (time_left == 0) { - ret = -ETIMEDOUT; - goto out_unlock; - } + time_left = wait_for_completion_timeout(&data->wait_event, + timeout * HZ); + if (time_left == 0) + return -ETIMEDOUT; - if (data->buf[3] >= EXC3000_LEN_FRAME) { - ret = -ENOSPC; - goto out_unlock; - } + if (data->buf[3] >= EXC3000_LEN_FRAME) + return -ENOSPC; - memcpy(response, &data->buf[4], data->buf[3]); - ret = data->buf[3]; - } - -out_unlock: - mutex_unlock(&data->query_lock); - - return ret; + memcpy(response, &data->buf[4], data->buf[3]); + return data->buf[3]; } static ssize_t fw_version_show(struct device *dev, diff --git a/drivers/input/touchscreen/goodix_berlin_core.c b/drivers/input/touchscreen/goodix_berlin_core.c index 83f28b870531..b0938a4f3fec 100644 --- a/drivers/input/touchscreen/goodix_berlin_core.c +++ b/drivers/input/touchscreen/goodix_berlin_core.c @@ -628,6 +628,14 @@ static int goodix_berlin_input_dev_config(struct goodix_berlin_core *cd, touchscreen_parse_properties(cd->input_dev, true, &cd->props); + /* + * The resolution of these touchscreens is about 10 units/mm, the actual + * resolution does not matter much since we set INPUT_PROP_DIRECT. + * Set it to 10 to ensure userspace isn't off by an order of magnitude. + */ + input_abs_set_res(cd->input_dev, ABS_MT_POSITION_X, 10); + input_abs_set_res(cd->input_dev, ABS_MT_POSITION_Y, 10); + error = input_mt_init_slots(cd->input_dev, GOODIX_BERLIN_MAX_TOUCH, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); if (error) diff --git a/drivers/input/touchscreen/goodix_fwupload.c b/drivers/input/touchscreen/goodix_fwupload.c index 191d4f38d991..5ae9a109ba0d 100644 --- a/drivers/input/touchscreen/goodix_fwupload.c +++ b/drivers/input/touchscreen/goodix_fwupload.c @@ -188,13 +188,13 @@ static int goodix_start_firmware(struct i2c_client *client) static int goodix_firmware_upload(struct goodix_ts_data *ts) { - const struct firmware *fw; char fw_name[64]; const u8 *data; int error; snprintf(fw_name, sizeof(fw_name), "goodix/%s", ts->firmware_name); + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, fw_name, &ts->client->dev); if (error) { dev_err(&ts->client->dev, "Firmware request error %d\n", error); @@ -203,60 +203,61 @@ static int goodix_firmware_upload(struct goodix_ts_data *ts) error = goodix_firmware_verify(&ts->client->dev, fw); if (error) - goto release; + return error; error = goodix_reset_no_int_sync(ts); if (error) - goto release; + return error; error = goodix_enter_upload_mode(ts->client); if (error) - goto release; + return error; /* Select SRAM bank 0 and upload section 1 & 2 */ error = goodix_i2c_write_u8(ts->client, GOODIX_REG_MISCTL_SRAM_BANK, 0x00); if (error) - goto release; + return error; data = fw->data + GOODIX_FW_HEADER_LENGTH; error = goodix_i2c_write(ts->client, GOODIX_FW_UPLOAD_ADDRESS, data, 2 * GOODIX_FW_SECTION_LENGTH); if (error) - goto release; + return error; /* Select SRAM bank 1 and upload section 3 & 4 */ error = goodix_i2c_write_u8(ts->client, GOODIX_REG_MISCTL_SRAM_BANK, 0x01); if (error) - goto release; + return error; data += 2 * GOODIX_FW_SECTION_LENGTH; error = goodix_i2c_write(ts->client, GOODIX_FW_UPLOAD_ADDRESS, data, 2 * GOODIX_FW_SECTION_LENGTH); if (error) - goto release; + return error; /* Select SRAM bank 2 and upload the DSP firmware */ error = goodix_i2c_write_u8(ts->client, GOODIX_REG_MISCTL_SRAM_BANK, 0x02); if (error) - goto release; + return error; data += 2 * GOODIX_FW_SECTION_LENGTH; error = goodix_i2c_write(ts->client, GOODIX_FW_UPLOAD_ADDRESS, data, GOODIX_FW_DSP_LENGTH); if (error) - goto release; + return error; error = goodix_start_firmware(ts->client); if (error) - goto release; + return error; error = goodix_int_sync(ts); -release: - release_firmware(fw); - return error; + if (error) + return error; + + return 0; } static int goodix_prepare_bak_ref(struct goodix_ts_data *ts) diff --git a/drivers/input/touchscreen/hideep.c b/drivers/input/touchscreen/hideep.c index a73369e15dda..62041bcca83d 100644 --- a/drivers/input/touchscreen/hideep.c +++ b/drivers/input/touchscreen/hideep.c @@ -869,8 +869,6 @@ static ssize_t hideep_update_fw(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct hideep_ts *ts = i2c_get_clientdata(client); - const struct firmware *fw_entry; - char *fw_name; int mode; int error; @@ -878,46 +876,42 @@ static ssize_t hideep_update_fw(struct device *dev, if (error) return error; - fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin", - be16_to_cpu(ts->dwz_info.product_id)); + const char *fw_name __free(kfree) = + kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin", + be16_to_cpu(ts->dwz_info.product_id)); if (!fw_name) return -ENOMEM; + const struct firmware *fw_entry __free(firmware) = NULL; error = request_firmware(&fw_entry, fw_name, dev); if (error) { dev_err(dev, "failed to request firmware %s: %d", fw_name, error); - goto out_free_fw_name; + return error; } if (fw_entry->size % sizeof(__be32)) { dev_err(dev, "invalid firmware size %zu\n", fw_entry->size); - error = -EINVAL; - goto out_release_fw; + return -EINVAL; } if (fw_entry->size > ts->fw_size) { dev_err(dev, "fw size (%zu) is too big (memory size %d)\n", fw_entry->size, ts->fw_size); - error = -EFBIG; - goto out_release_fw; + return -EFBIG; } - mutex_lock(&ts->dev_mutex); - disable_irq(client->irq); + scoped_guard(mutex, &ts->dev_mutex) { + guard(disable_irq)(&client->irq); - error = hideep_update_firmware(ts, (const __be32 *)fw_entry->data, - fw_entry->size); + error = hideep_update_firmware(ts, + (const __be32 *)fw_entry->data, + fw_entry->size); + if (error) + return error; + } - enable_irq(client->irq); - mutex_unlock(&ts->dev_mutex); - -out_release_fw: - release_firmware(fw_entry); -out_free_fw_name: - kfree(fw_name); - - return error ?: count; + return count; } static ssize_t hideep_fw_version_show(struct device *dev, @@ -925,13 +919,9 @@ static ssize_t hideep_fw_version_show(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct hideep_ts *ts = i2c_get_clientdata(client); - ssize_t len; - mutex_lock(&ts->dev_mutex); - len = sysfs_emit(buf, "%04x\n", be16_to_cpu(ts->dwz_info.release_ver)); - mutex_unlock(&ts->dev_mutex); - - return len; + guard(mutex)(&ts->dev_mutex); + return sysfs_emit(buf, "%04x\n", be16_to_cpu(ts->dwz_info.release_ver)); } static ssize_t hideep_product_id_show(struct device *dev, @@ -939,13 +929,9 @@ static ssize_t hideep_product_id_show(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct hideep_ts *ts = i2c_get_clientdata(client); - ssize_t len; - mutex_lock(&ts->dev_mutex); - len = sysfs_emit(buf, "%04x\n", be16_to_cpu(ts->dwz_info.product_id)); - mutex_unlock(&ts->dev_mutex); - - return len; + guard(mutex)(&ts->dev_mutex); + return sysfs_emit(buf, "%04x\n", be16_to_cpu(ts->dwz_info.product_id)); } static DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL); diff --git a/drivers/input/touchscreen/hycon-hy46xx.c b/drivers/input/touchscreen/hycon-hy46xx.c index b2ff7a45b908..1513f20cbf51 100644 --- a/drivers/input/touchscreen/hycon-hy46xx.c +++ b/drivers/input/touchscreen/hycon-hy46xx.c @@ -181,18 +181,17 @@ static ssize_t hycon_hy46xx_setting_show(struct device *dev, struct hycon_hy46xx_attribute *attr = container_of(dattr, struct hycon_hy46xx_attribute, dattr); u8 *field = (u8 *)tsdata + attr->field_offset; - size_t count = 0; int error = 0; int val; - mutex_lock(&tsdata->mutex); + guard(mutex)(&tsdata->mutex); error = regmap_read(tsdata->regmap, attr->address, &val); - if (error < 0) { + if (error) { dev_err(&tsdata->client->dev, "Failed to fetch attribute %s, error %d\n", dattr->attr.name, error); - goto out; + return error; } if (val != *field) { @@ -202,11 +201,7 @@ static ssize_t hycon_hy46xx_setting_show(struct device *dev, *field = val; } - count = sysfs_emit(buf, "%d\n", val); - -out: - mutex_unlock(&tsdata->mutex); - return error ?: count; + return sysfs_emit(buf, "%d\n", val); } static ssize_t hycon_hy46xx_setting_store(struct device *dev, @@ -221,29 +216,25 @@ static ssize_t hycon_hy46xx_setting_store(struct device *dev, unsigned int val; int error; - mutex_lock(&tsdata->mutex); + guard(mutex)(&tsdata->mutex); error = kstrtouint(buf, 0, &val); if (error) - goto out; + return error; - if (val < attr->limit_low || val > attr->limit_high) { - error = -ERANGE; - goto out; - } + if (val < attr->limit_low || val > attr->limit_high) + return -ERANGE; error = regmap_write(tsdata->regmap, attr->address, val); - if (error < 0) { + if (error) { dev_err(&tsdata->client->dev, "Failed to update attribute %s, error: %d\n", dattr->attr.name, error); - goto out; + return error; } *field = val; -out: - mutex_unlock(&tsdata->mutex); - return error ?: count; + return count; } static HYCON_ATTR_U8(threshold, 0644, HY46XX_THRESHOLD, 0, 255); diff --git a/drivers/input/touchscreen/imagis.c b/drivers/input/touchscreen/imagis.c index 3c8bbe284b73..7bbb00beec3b 100644 --- a/drivers/input/touchscreen/imagis.c +++ b/drivers/input/touchscreen/imagis.c @@ -366,32 +366,34 @@ static int imagis_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct imagis_ts *ts = i2c_get_clientdata(client); - int retval = 0; + int error; - mutex_lock(&ts->input_dev->mutex); + guard(mutex)(&ts->input_dev->mutex); - if (input_device_enabled(ts->input_dev)) - retval = imagis_stop(ts); + if (input_device_enabled(ts->input_dev)) { + error = imagis_stop(ts); + if (error) + return error; + } - mutex_unlock(&ts->input_dev->mutex); - - return retval; + return 0; } static int imagis_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct imagis_ts *ts = i2c_get_clientdata(client); - int retval = 0; + int error; - mutex_lock(&ts->input_dev->mutex); + guard(mutex)(&ts->input_dev->mutex); - if (input_device_enabled(ts->input_dev)) - retval = imagis_start(ts); + if (input_device_enabled(ts->input_dev)) { + error = imagis_start(ts); + if (error) + return error; + } - mutex_unlock(&ts->input_dev->mutex); - - return retval; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(imagis_pm_ops, imagis_suspend, imagis_resume); diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c index 85f697de2b7e..43d6c69e3088 100644 --- a/drivers/input/touchscreen/imx6ul_tsc.c +++ b/drivers/input/touchscreen/imx6ul_tsc.c @@ -551,13 +551,11 @@ static int imx6ul_tsc_suspend(struct device *dev) struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); struct input_dev *input_dev = tsc->input; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); if (input_device_enabled(input_dev)) imx6ul_tsc_stop(tsc); - mutex_unlock(&input_dev->mutex); - return 0; } @@ -566,16 +564,17 @@ static int imx6ul_tsc_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct imx6ul_tsc *tsc = platform_get_drvdata(pdev); struct input_dev *input_dev = tsc->input; - int retval = 0; + int error; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); - if (input_device_enabled(input_dev)) - retval = imx6ul_tsc_start(tsc); + if (input_device_enabled(input_dev)) { + error = imx6ul_tsc_start(tsc); + if (error) + return error; + } - mutex_unlock(&input_dev->mutex); - - return retval; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(imx6ul_tsc_pm_ops, diff --git a/drivers/input/touchscreen/ipaq-micro-ts.c b/drivers/input/touchscreen/ipaq-micro-ts.c index 94720c41c9be..243e6465d9bd 100644 --- a/drivers/input/touchscreen/ipaq-micro-ts.c +++ b/drivers/input/touchscreen/ipaq-micro-ts.c @@ -47,7 +47,7 @@ static void micro_ts_toggle_receive(struct touchscreen_data *ts, bool enable) { struct ipaq_micro *micro = ts->micro; - spin_lock_irq(µ->lock); + guard(spinlock_irq)(µ->lock); if (enable) { micro->ts = micro_ts_receive; @@ -56,8 +56,6 @@ static void micro_ts_toggle_receive(struct touchscreen_data *ts, bool enable) micro->ts = NULL; micro->ts_data = NULL; } - - spin_unlock_irq(&ts->micro->lock); } static int micro_ts_open(struct input_dev *input) @@ -133,13 +131,11 @@ static int micro_ts_resume(struct device *dev) struct touchscreen_data *ts = dev_get_drvdata(dev); struct input_dev *input = ts->input; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (input_device_enabled(input)) micro_ts_toggle_receive(ts, true); - mutex_unlock(&input->mutex); - return 0; } diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index c63819abaf9b..587665e4e6fd 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -73,8 +73,11 @@ #define IQS5XX_CSTM_LEN (IQS5XX_PMAP_END + 1 - IQS5XX_CSTM) #define IQS5XX_PMAP_LEN (IQS5XX_PMAP_END + 1 - IQS5XX_CHKSM) -#define IQS5XX_REC_HDR_LEN 4 -#define IQS5XX_REC_LEN_MAX 255 +/* Length of firmware header in hexadecimal characters */ +#define IQS5XX_REC_HDR_LEN_HEX (1 /* start */ + 2 /* size */ + \ + 4 /* addr */ + 2 /* type */) +#define IQS5XX_REC_HDR_SIZE 4 /* size + addr (2 bytes) + type, in bytes*/ +#define IQS5XX_REC_DATA_SIZE 255 /* maximum size of the data portion */ #define IQS5XX_REC_TYPE_DATA 0x00 #define IQS5XX_REC_TYPE_EOF 0x01 @@ -98,14 +101,6 @@ struct iqs5xx_dev_id_info { u8 bl_status; } __packed; -struct iqs5xx_ihex_rec { - char start; - char len[2]; - char addr[4]; - char type[2]; - char data[2]; -} __packed; - struct iqs5xx_touch_data { __be16 abs_x; __be16 abs_y; @@ -356,7 +351,7 @@ static int iqs5xx_bl_open(struct i2c_client *client) } static int iqs5xx_bl_write(struct i2c_client *client, - u16 bl_addr, u8 *pmap_data, u16 pmap_len) + u16 bl_addr, const u8 *pmap_data, u16 pmap_len) { struct i2c_msg msg; int ret, i; @@ -395,7 +390,7 @@ msg_fail: } static int iqs5xx_bl_verify(struct i2c_client *client, - u16 bl_addr, u8 *pmap_data, u16 pmap_len) + u16 bl_addr, const u8 *pmap_data, u16 pmap_len) { struct i2c_msg msg; int ret, i; @@ -446,27 +441,21 @@ static int iqs5xx_set_state(struct i2c_client *client, u8 state) if (!iqs5xx->dev_id_info.bl_status) return 0; - mutex_lock(&iqs5xx->lock); + guard(mutex)(&iqs5xx->lock); /* * Addressing the device outside of a communication window prompts it * to assert the RDY output, so disable the interrupt line to prevent * the handler from servicing a false interrupt. */ - disable_irq(client->irq); + guard(disable_irq)(&client->irq); error1 = iqs5xx_write_byte(client, IQS5XX_SYS_CTRL1, state); error2 = iqs5xx_write_byte(client, IQS5XX_END_COMM, 0); usleep_range(50, 100); - enable_irq(client->irq); - mutex_unlock(&iqs5xx->lock); - - if (error1) - return error1; - - return error2; + return error1 ?: error2; } static int iqs5xx_open(struct input_dev *input) @@ -703,15 +692,13 @@ static irqreturn_t iqs5xx_irq(int irq, void *data) static int iqs5xx_fw_file_parse(struct i2c_client *client, const char *fw_file, u8 *pmap) { - const struct firmware *fw; - struct iqs5xx_ihex_rec *rec; size_t pos = 0; int error, i; u16 rec_num = 1; u16 rec_addr; u8 rec_len, rec_type, rec_chksm, chksm; - u8 rec_hdr[IQS5XX_REC_HDR_LEN]; - u8 rec_data[IQS5XX_REC_LEN_MAX]; + u8 rec_hdr[IQS5XX_REC_HDR_SIZE]; + u8 rec_data[IQS5XX_REC_DATA_SIZE]; /* * Firmware exported from the vendor's configuration tool deviates from @@ -722,6 +709,7 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, * Because the ihex2fw tool tolerates neither (1) nor (2), the slightly * nonstandard ihex firmware is parsed directly by the driver. */ + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, fw_file, &client->dev); if (error) { dev_err(&client->dev, "Failed to request firmware %s: %d\n", @@ -730,53 +718,55 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, } do { - if (pos + sizeof(*rec) > fw->size) { + if (pos + IQS5XX_REC_HDR_LEN_HEX > fw->size) { dev_err(&client->dev, "Insufficient firmware size\n"); - error = -EINVAL; - break; + return -EINVAL; } - rec = (struct iqs5xx_ihex_rec *)(fw->data + pos); - pos += sizeof(*rec); - if (rec->start != ':') { + if (fw->data[pos] != ':') { dev_err(&client->dev, "Invalid start at record %u\n", rec_num); - error = -EINVAL; - break; + return -EINVAL; } - error = hex2bin(rec_hdr, rec->len, sizeof(rec_hdr)); + /* Convert all 3 fields (length, address, and type) in one go */ + error = hex2bin(rec_hdr, &fw->data[pos + 1], sizeof(rec_hdr)); if (error) { dev_err(&client->dev, "Invalid header at record %u\n", rec_num); - break; + return error; } + pos += IQS5XX_REC_HDR_LEN_HEX; rec_len = *rec_hdr; rec_addr = get_unaligned_be16(rec_hdr + sizeof(rec_len)); rec_type = *(rec_hdr + sizeof(rec_len) + sizeof(rec_addr)); - if (pos + rec_len * 2 > fw->size) { + /* + * Check if we have enough data for the data portion of the + * record, as well as the checksum byte. Everything is doubled + * because data is in ASCII HEX and not binary format. + */ + if (pos + (rec_len + sizeof(rec_chksm)) * 2 > fw->size) { dev_err(&client->dev, "Insufficient firmware size\n"); - error = -EINVAL; - break; + return -EINVAL; } - pos += (rec_len * 2); - error = hex2bin(rec_data, rec->data, rec_len); + error = hex2bin(rec_data, &fw->data[pos], rec_len); if (error) { dev_err(&client->dev, "Invalid data at record %u\n", rec_num); - break; + return error; } + pos += rec_len * 2; - error = hex2bin(&rec_chksm, - rec->data + rec_len * 2, sizeof(rec_chksm)); + error = hex2bin(&rec_chksm, &fw->data[pos], sizeof(rec_chksm)); if (error) { dev_err(&client->dev, "Invalid checksum at record %u\n", rec_num); - break; + return error; } + pos += 2; chksm = 0; for (i = 0; i < sizeof(rec_hdr); i++) @@ -800,23 +790,22 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, dev_err(&client->dev, "Invalid address at record %u\n", rec_num); - error = -EINVAL; - } else { - memcpy(pmap + rec_addr - IQS5XX_CHKSM, - rec_data, rec_len); + return -EINVAL; } + + memcpy(pmap + rec_addr - IQS5XX_CHKSM, + rec_data, rec_len); break; + case IQS5XX_REC_TYPE_EOF: break; + default: dev_err(&client->dev, "Invalid type at record %u\n", rec_num); - error = -EINVAL; + return -EINVAL; } - if (error) - break; - rec_num++; while (pos < fw->size) { if (*(fw->data + pos) == ':') @@ -825,33 +814,13 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, } } while (rec_type != IQS5XX_REC_TYPE_EOF); - release_firmware(fw); - - return error; + return 0; } -static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file) +static int iqs5xx_update_firmware(struct iqs5xx_private *iqs5xx, const u8 *pmap) { - struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client); - int error, error_init = 0; - u8 *pmap; - - pmap = kzalloc(IQS5XX_PMAP_LEN, GFP_KERNEL); - if (!pmap) - return -ENOMEM; - - error = iqs5xx_fw_file_parse(client, fw_file, pmap); - if (error) - goto err_kfree; - - mutex_lock(&iqs5xx->lock); - - /* - * Disable the interrupt line in case the first attempt(s) to enter the - * bootloader don't happen quickly enough, in which case the device may - * assert the RDY output until the next attempt. - */ - disable_irq(client->irq); + struct i2c_client *client = iqs5xx->client; + int error; iqs5xx->dev_id_info.bl_status = 0; @@ -859,22 +828,50 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file) if (error) { error = iqs5xx_bl_open(client); if (error) - goto err_reset; + return error; } error = iqs5xx_bl_write(client, IQS5XX_CHKSM, pmap, IQS5XX_PMAP_LEN); if (error) - goto err_reset; + return error; error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_CRC, 0); if (error) - goto err_reset; + return error; error = iqs5xx_bl_verify(client, IQS5XX_CSTM, pmap + IQS5XX_CHKSM_LEN + IQS5XX_APP_LEN, IQS5XX_CSTM_LEN); + if (error) + return error; + + return 0; +} + +static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file) +{ + struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client); + int error, error_init = 0; + + u8 *pmap __free(kfree) = kzalloc(IQS5XX_PMAP_LEN, GFP_KERNEL); + if (!pmap) + return -ENOMEM; + + error = iqs5xx_fw_file_parse(client, fw_file, pmap); + if (error) + return error; + + guard(mutex)(&iqs5xx->lock); + + /* + * Disable the interrupt line in case the first attempt(s) to enter the + * bootloader don't happen quickly enough, in which case the device may + * assert the RDY output until the next attempt. + */ + guard(disable_irq)(&client->irq); + + error = iqs5xx_update_firmware(iqs5xx, pmap); -err_reset: iqs5xx_reset(client); usleep_range(15000, 15100); @@ -882,14 +879,7 @@ err_reset: if (!iqs5xx->dev_id_info.bl_status) error_init = error_init ? : -EINVAL; - enable_irq(client->irq); - - mutex_unlock(&iqs5xx->lock); - -err_kfree: - kfree(pmap); - - return error ? : error_init; + return error ?: error_init; } static ssize_t fw_file_store(struct device *dev, @@ -985,38 +975,30 @@ static int iqs5xx_suspend(struct device *dev) { struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev); struct input_dev *input = iqs5xx->input; - int error = 0; if (!input || device_may_wakeup(dev)) - return error; - - mutex_lock(&input->mutex); + return 0; + guard(mutex)(&input->mutex); if (input_device_enabled(input)) - error = iqs5xx_set_state(iqs5xx->client, IQS5XX_SUSPEND); + return iqs5xx_set_state(iqs5xx->client, IQS5XX_SUSPEND); - mutex_unlock(&input->mutex); - - return error; + return 0; } static int iqs5xx_resume(struct device *dev) { struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev); struct input_dev *input = iqs5xx->input; - int error = 0; if (!input || device_may_wakeup(dev)) - return error; - - mutex_lock(&input->mutex); + return 0; + guard(mutex)(&input->mutex); if (input_device_enabled(input)) - error = iqs5xx_set_state(iqs5xx->client, IQS5XX_RESUME); + return iqs5xx_set_state(iqs5xx->client, IQS5XX_RESUME); - mutex_unlock(&input->mutex); - - return error; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(iqs5xx_pm, iqs5xx_suspend, iqs5xx_resume); diff --git a/drivers/input/touchscreen/iqs7211.c b/drivers/input/touchscreen/iqs7211.c index c5d447ee6f53..26e0b501c5fc 100644 --- a/drivers/input/touchscreen/iqs7211.c +++ b/drivers/input/touchscreen/iqs7211.c @@ -2060,19 +2060,16 @@ static int iqs7211_parse_reg_grp(struct iqs7211_private *iqs7211, for (i = 0; i < dev_desc->num_kp_events; i++) { const char *event_name = dev_desc->kp_events[i].name; - struct fwnode_handle *event_node; if (dev_desc->kp_events[i].reg_grp != reg_grp) continue; reg_field.mask |= dev_desc->kp_events[i].enable; - if (event_name) - event_node = fwnode_get_named_child_node(reg_grp_node, - event_name); - else - event_node = fwnode_handle_get(reg_grp_node); - + struct fwnode_handle *event_node __free(fwnode_handle) = + event_name ? fwnode_get_named_child_node(reg_grp_node, + event_name) : + fwnode_handle_get(reg_grp_node); if (!event_node) continue; @@ -2080,7 +2077,6 @@ static int iqs7211_parse_reg_grp(struct iqs7211_private *iqs7211, dev_desc->kp_events[i].reg_grp, dev_desc->kp_events[i].reg_key, &iqs7211->kp_code[i]); - fwnode_handle_put(event_node); if (error) return error; @@ -2496,19 +2492,15 @@ static int iqs7211_probe(struct i2c_client *client) for (reg_grp = 0; reg_grp < IQS7211_NUM_REG_GRPS; reg_grp++) { const char *reg_grp_name = iqs7211_reg_grp_names[reg_grp]; - struct fwnode_handle *reg_grp_node; - - if (reg_grp_name) - reg_grp_node = device_get_named_child_node(&client->dev, - reg_grp_name); - else - reg_grp_node = fwnode_handle_get(dev_fwnode(&client->dev)); + struct fwnode_handle *reg_grp_node __free(fwnode_handle) = + reg_grp_name ? device_get_named_child_node(&client->dev, + reg_grp_name) : + fwnode_handle_get(dev_fwnode(&client->dev)); if (!reg_grp_node) continue; error = iqs7211_parse_reg_grp(iqs7211, reg_grp_node, reg_grp); - fwnode_handle_put(reg_grp_node); if (error) return error; } diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c index 9bad8b93c039..5de752a06b26 100644 --- a/drivers/input/touchscreen/lpc32xx_ts.c +++ b/drivers/input/touchscreen/lpc32xx_ts.c @@ -279,7 +279,7 @@ static int lpc32xx_ts_suspend(struct device *dev) * avoid calling the TSC stop and start functions as the TSC * isn't yet clocked. */ - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (input_device_enabled(input)) { if (device_may_wakeup(dev)) @@ -288,8 +288,6 @@ static int lpc32xx_ts_suspend(struct device *dev) lpc32xx_stop_tsc(tsc); } - mutex_unlock(&input->mutex); - return 0; } @@ -298,7 +296,7 @@ static int lpc32xx_ts_resume(struct device *dev) struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); struct input_dev *input = tsc->dev; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (input_device_enabled(input)) { if (device_may_wakeup(dev)) @@ -307,8 +305,6 @@ static int lpc32xx_ts_resume(struct device *dev) lpc32xx_setup_tsc(tsc); } - mutex_unlock(&input->mutex); - return 0; } diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c index 869884219908..10fccf4e5bb1 100644 --- a/drivers/input/touchscreen/melfas_mip4.c +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -881,8 +881,6 @@ static int mip4_bl_program_page(struct mip4_ts *ts, int offset, const u8 *data, int length, u16 buf_addr) { u8 cmd[6]; - u8 *data_buf; - u16 buf_offset; int ret; int error; @@ -895,7 +893,8 @@ static int mip4_bl_program_page(struct mip4_ts *ts, int offset, return -EINVAL; } - data_buf = kmalloc(2 + MIP4_BL_PACKET_SIZE, GFP_KERNEL); + u8 *data_buf __free(kfree) = kmalloc(2 + MIP4_BL_PACKET_SIZE, + GFP_KERNEL); if (!data_buf) return -ENOMEM; @@ -908,7 +907,7 @@ static int mip4_bl_program_page(struct mip4_ts *ts, int offset, error = ret < 0 ? ret : -EIO; dev_err(&ts->client->dev, "Failed to send write page address: %d\n", error); - goto out; + return error; } /* Size */ @@ -920,11 +919,11 @@ static int mip4_bl_program_page(struct mip4_ts *ts, int offset, error = ret < 0 ? ret : -EIO; dev_err(&ts->client->dev, "Failed to send write page size: %d\n", error); - goto out; + return error; } /* Data */ - for (buf_offset = 0; + for (int buf_offset = 0; buf_offset < length; buf_offset += MIP4_BL_PACKET_SIZE) { dev_dbg(&ts->client->dev, @@ -939,7 +938,7 @@ static int mip4_bl_program_page(struct mip4_ts *ts, int offset, dev_err(&ts->client->dev, "Failed to read chunk at %#04x (size %d): %d\n", buf_offset, MIP4_BL_PACKET_SIZE, error); - goto out; + return error; } } @@ -952,35 +951,21 @@ static int mip4_bl_program_page(struct mip4_ts *ts, int offset, error = ret < 0 ? ret : -EIO; dev_err(&ts->client->dev, "Failed to send 'write' command: %d\n", error); - goto out; + return error; } /* Status */ error = mip4_bl_read_status(ts); + if (error) + return error; -out: - kfree(data_buf); - return error ? error : 0; + return 0; } static int mip4_bl_verify_page(struct mip4_ts *ts, int offset, const u8 *data, int length, int buf_addr) { u8 cmd[8]; - u8 *read_buf; - int buf_offset; - struct i2c_msg msg[] = { - { - .addr = ts->client->addr, - .flags = 0, - .buf = cmd, - .len = 2, - }, { - .addr = ts->client->addr, - .flags = I2C_M_RD, - .len = MIP4_BL_PACKET_SIZE, - }, - }; int ret; int error; @@ -1029,11 +1014,25 @@ static int mip4_bl_verify_page(struct mip4_ts *ts, int offset, return error; /* Read */ - msg[1].buf = read_buf = kmalloc(MIP4_BL_PACKET_SIZE, GFP_KERNEL); + u8 *read_buf __free(kfree) = kmalloc(MIP4_BL_PACKET_SIZE, GFP_KERNEL); if (!read_buf) return -ENOMEM; - for (buf_offset = 0; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = cmd, + .len = 2, + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = read_buf, + .len = MIP4_BL_PACKET_SIZE, + }, + }; + + for (int buf_offset = 0; buf_offset < length; buf_offset += MIP4_BL_PACKET_SIZE) { dev_dbg(&ts->client->dev, @@ -1046,7 +1045,7 @@ static int mip4_bl_verify_page(struct mip4_ts *ts, int offset, dev_err(&ts->client->dev, "Failed to read chunk at %#04x (size %d): %d\n", buf_offset, MIP4_BL_PACKET_SIZE, error); - break; + return error; } if (memcmp(&data[buf_offset], read_buf, MIP4_BL_PACKET_SIZE)) { @@ -1064,13 +1063,11 @@ static int mip4_bl_verify_page(struct mip4_ts *ts, int offset, DUMP_PREFIX_OFFSET, 16, 1, read_buf, MIP4_BL_PAGE_SIZE, false); #endif - error = -EINVAL; - break; + return -EINVAL; } } - kfree(read_buf); - return error ? error : 0; + return 0; } /* @@ -1290,9 +1287,9 @@ static ssize_t mip4_sysfs_fw_update(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct mip4_ts *ts = i2c_get_clientdata(client); - const struct firmware *fw; int error; + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, ts->fw_name, dev); if (error) { dev_err(&ts->client->dev, @@ -1306,14 +1303,9 @@ static ssize_t mip4_sysfs_fw_update(struct device *dev, * userspace opening and closing the device and also suspend/resume * transitions. */ - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); error = mip4_execute_fw_update(ts, fw); - - mutex_unlock(&ts->input->mutex); - - release_firmware(fw); - if (error) { dev_err(&ts->client->dev, "Firmware update failed: %d\n", error); @@ -1331,18 +1323,13 @@ static ssize_t mip4_sysfs_read_fw_version(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct mip4_ts *ts = i2c_get_clientdata(client); - size_t count; /* Take lock to prevent racing with firmware update */ - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); - count = sysfs_emit(buf, "%04X %04X %04X %04X\n", - ts->fw_version.boot, ts->fw_version.core, - ts->fw_version.app, ts->fw_version.param); - - mutex_unlock(&ts->input->mutex); - - return count; + return sysfs_emit(buf, "%04X %04X %04X %04X\n", + ts->fw_version.boot, ts->fw_version.core, + ts->fw_version.app, ts->fw_version.param); } static DEVICE_ATTR(fw_version, S_IRUGO, mip4_sysfs_read_fw_version, NULL); @@ -1353,21 +1340,16 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct mip4_ts *ts = i2c_get_clientdata(client); - size_t count; /* Take lock to prevent racing with firmware update */ - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); /* * product_name shows the name or version of the hardware * paired with current firmware in the chip. */ - count = sysfs_emit(buf, "%.*s\n", - (int)sizeof(ts->product_name), ts->product_name); - - mutex_unlock(&ts->input->mutex); - - return count; + return sysfs_emit(buf, "%.*s\n", + (int)sizeof(ts->product_name), ts->product_name); } static DEVICE_ATTR(hw_version, S_IRUGO, mip4_sysfs_read_hw_version, NULL); @@ -1378,15 +1360,10 @@ static ssize_t mip4_sysfs_read_product_id(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct mip4_ts *ts = i2c_get_clientdata(client); - size_t count; - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); - count = sysfs_emit(buf, "%04X\n", ts->product_id); - - mutex_unlock(&ts->input->mutex); - - return count; + return sysfs_emit(buf, "%04X\n", ts->product_id); } static DEVICE_ATTR(product_id, S_IRUGO, mip4_sysfs_read_product_id, NULL); @@ -1397,16 +1374,10 @@ static ssize_t mip4_sysfs_read_ic_name(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct mip4_ts *ts = i2c_get_clientdata(client); - size_t count; - mutex_lock(&ts->input->mutex); + guard(mutex)(&ts->input->mutex); - count = sysfs_emit(buf, "%.*s\n", - (int)sizeof(ts->ic_name), ts->ic_name); - - mutex_unlock(&ts->input->mutex); - - return count; + return sysfs_emit(buf, "%.*s\n", (int)sizeof(ts->ic_name), ts->ic_name); } static DEVICE_ATTR(ic_name, S_IRUGO, mip4_sysfs_read_ic_name, NULL); @@ -1520,15 +1491,13 @@ static int mip4_suspend(struct device *dev) struct mip4_ts *ts = i2c_get_clientdata(client); struct input_dev *input = ts->input; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (device_may_wakeup(dev)) ts->wake_irq_enabled = enable_irq_wake(client->irq) == 0; else if (input_device_enabled(input)) mip4_disable(ts); - mutex_unlock(&input->mutex); - return 0; } @@ -1538,15 +1507,13 @@ static int mip4_resume(struct device *dev) struct mip4_ts *ts = i2c_get_clientdata(client); struct input_dev *input = ts->input; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (ts->wake_irq_enabled) disable_irq_wake(client->irq); else if (input_device_enabled(input)) mip4_enable(ts); - mutex_unlock(&input->mutex); - return 0; } diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c deleted file mode 100644 index 753d9cc1de1f..000000000000 --- a/drivers/input/touchscreen/mk712.c +++ /dev/null @@ -1,215 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * ICS MK712 touchscreen controller driver - * - * Copyright (c) 1999-2002 Transmeta Corporation - * Copyright (c) 2005 Rick Koch - * Copyright (c) 2005 Vojtech Pavlik - */ - - -/* - * This driver supports the ICS MicroClock MK712 TouchScreen controller, - * found in Gateway AOL Connected Touchpad computers. - * - * Documentation for ICS MK712 can be found at: - * https://www.idt.com/general-parts/mk712-touch-screen-controller - */ - -/* - * 1999-12-18: original version, Daniel Quinlan - * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll - * to use queue_empty, Nathan Laredo - * 1999-12-20: improved random point rejection, Nathan Laredo - * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed - * queue code, added module options, other fixes, Daniel Quinlan - * 2002-03-15: Clean up for kernel merge - * Fixed multi open race, fixed memory checks, fixed resource - * allocation, fixed close/powerdown bug, switched to new init - * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch - * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Daniel Quinlan , Vojtech Pavlik "); -MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver"); -MODULE_LICENSE("GPL"); - -static unsigned int mk712_io = 0x260; /* Also 0x200, 0x208, 0x300 */ -module_param_hw_named(io, mk712_io, uint, ioport, 0); -MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller"); - -static unsigned int mk712_irq = 10; /* Also 12, 14, 15 */ -module_param_hw_named(irq, mk712_irq, uint, irq, 0); -MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller"); - -/* eight 8-bit registers */ -#define MK712_STATUS 0 -#define MK712_X 2 -#define MK712_Y 4 -#define MK712_CONTROL 6 -#define MK712_RATE 7 - -/* status */ -#define MK712_STATUS_TOUCH 0x10 -#define MK712_CONVERSION_COMPLETE 0x80 - -/* control */ -#define MK712_ENABLE_INT 0x01 -#define MK712_INT_ON_CONVERSION_COMPLETE 0x02 -#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS 0x04 -#define MK712_ENABLE_PERIODIC_CONVERSIONS 0x10 -#define MK712_READ_ONE_POINT 0x20 -#define MK712_POWERUP 0x40 - -static struct input_dev *mk712_dev; -static DEFINE_SPINLOCK(mk712_lock); - -static irqreturn_t mk712_interrupt(int irq, void *dev_id) -{ - unsigned char status; - static int debounce = 1; - static unsigned short last_x; - static unsigned short last_y; - - spin_lock(&mk712_lock); - - status = inb(mk712_io + MK712_STATUS); - - if (~status & MK712_CONVERSION_COMPLETE) { - debounce = 1; - goto end; - } - - if (~status & MK712_STATUS_TOUCH) { - debounce = 1; - input_report_key(mk712_dev, BTN_TOUCH, 0); - goto end; - } - - if (debounce) { - debounce = 0; - goto end; - } - - input_report_key(mk712_dev, BTN_TOUCH, 1); - input_report_abs(mk712_dev, ABS_X, last_x); - input_report_abs(mk712_dev, ABS_Y, last_y); - - end: - last_x = inw(mk712_io + MK712_X) & 0x0fff; - last_y = inw(mk712_io + MK712_Y) & 0x0fff; - input_sync(mk712_dev); - spin_unlock(&mk712_lock); - return IRQ_HANDLED; -} - -static int mk712_open(struct input_dev *dev) -{ - unsigned long flags; - - spin_lock_irqsave(&mk712_lock, flags); - - outb(0, mk712_io + MK712_CONTROL); /* Reset */ - - outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE | - MK712_INT_ON_CHANGE_IN_TOUCH_STATUS | - MK712_ENABLE_PERIODIC_CONVERSIONS | - MK712_POWERUP, mk712_io + MK712_CONTROL); - - outb(10, mk712_io + MK712_RATE); /* 187 points per second */ - - spin_unlock_irqrestore(&mk712_lock, flags); - - return 0; -} - -static void mk712_close(struct input_dev *dev) -{ - unsigned long flags; - - spin_lock_irqsave(&mk712_lock, flags); - - outb(0, mk712_io + MK712_CONTROL); - - spin_unlock_irqrestore(&mk712_lock, flags); -} - -static int __init mk712_init(void) -{ - int err; - - if (!request_region(mk712_io, 8, "mk712")) { - printk(KERN_WARNING "mk712: unable to get IO region\n"); - return -ENODEV; - } - - outb(0, mk712_io + MK712_CONTROL); - - if ((inw(mk712_io + MK712_X) & 0xf000) || /* Sanity check */ - (inw(mk712_io + MK712_Y) & 0xf000) || - (inw(mk712_io + MK712_STATUS) & 0xf333)) { - printk(KERN_WARNING "mk712: device not present\n"); - err = -ENODEV; - goto fail1; - } - - mk712_dev = input_allocate_device(); - if (!mk712_dev) { - printk(KERN_ERR "mk712: not enough memory\n"); - err = -ENOMEM; - goto fail1; - } - - mk712_dev->name = "ICS MicroClock MK712 TouchScreen"; - mk712_dev->phys = "isa0260/input0"; - mk712_dev->id.bustype = BUS_ISA; - mk712_dev->id.vendor = 0x0005; - mk712_dev->id.product = 0x0001; - mk712_dev->id.version = 0x0100; - - mk712_dev->open = mk712_open; - mk712_dev->close = mk712_close; - - mk712_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - mk712_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - input_set_abs_params(mk712_dev, ABS_X, 0, 0xfff, 88, 0); - input_set_abs_params(mk712_dev, ABS_Y, 0, 0xfff, 88, 0); - - if (request_irq(mk712_irq, mk712_interrupt, 0, "mk712", mk712_dev)) { - printk(KERN_WARNING "mk712: unable to get IRQ\n"); - err = -EBUSY; - goto fail1; - } - - err = input_register_device(mk712_dev); - if (err) - goto fail2; - - return 0; - - fail2: free_irq(mk712_irq, mk712_dev); - fail1: input_free_device(mk712_dev); - release_region(mk712_io, 8); - return err; -} - -static void __exit mk712_exit(void) -{ - input_unregister_device(mk712_dev); - free_irq(mk712_irq, mk712_dev); - release_region(mk712_io, 8); -} - -module_init(mk712_init); -module_exit(mk712_exit); diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 9f947044c4d9..af462086a65c 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -216,20 +216,12 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) { struct mms114_data *data = dev_id; struct i2c_client *client = data->client; - struct input_dev *input_dev = data->input_dev; struct mms114_touch touch[MMS114_MAX_TOUCH]; int packet_size; int touch_size; int index; int error; - mutex_lock(&input_dev->mutex); - if (!input_device_enabled(input_dev)) { - mutex_unlock(&input_dev->mutex); - goto out; - } - mutex_unlock(&input_dev->mutex); - packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE); if (packet_size <= 0) goto out; @@ -646,10 +638,10 @@ static int mms114_suspend(struct device *dev) input_mt_report_pointer_emulation(input_dev, true); input_sync(input_dev); - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); + if (input_device_enabled(input_dev)) mms114_stop(data); - mutex_unlock(&input_dev->mutex); return 0; } @@ -661,15 +653,13 @@ static int mms114_resume(struct device *dev) struct input_dev *input_dev = data->input_dev; int error; - mutex_lock(&input_dev->mutex); + guard(mutex)(&input_dev->mutex); + if (input_device_enabled(input_dev)) { error = mms114_start(data); - if (error < 0) { - mutex_unlock(&input_dev->mutex); + if (error) return error; - } } - mutex_unlock(&input_dev->mutex); return 0; } diff --git a/drivers/input/touchscreen/msg2638.c b/drivers/input/touchscreen/msg2638.c index a38af3fee34a..240d2eebf1c9 100644 --- a/drivers/input/touchscreen/msg2638.c +++ b/drivers/input/touchscreen/msg2638.c @@ -446,13 +446,11 @@ static int msg2638_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct msg2638_ts_data *msg2638 = i2c_get_clientdata(client); - mutex_lock(&msg2638->input_dev->mutex); + guard(mutex)(&msg2638->input_dev->mutex); if (input_device_enabled(msg2638->input_dev)) msg2638_stop(msg2638); - mutex_unlock(&msg2638->input_dev->mutex); - return 0; } @@ -460,16 +458,17 @@ static int msg2638_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct msg2638_ts_data *msg2638 = i2c_get_clientdata(client); - int ret = 0; + int error; - mutex_lock(&msg2638->input_dev->mutex); + guard(mutex)(&msg2638->input_dev->mutex); - if (input_device_enabled(msg2638->input_dev)) - ret = msg2638_start(msg2638); + if (input_device_enabled(msg2638->input_dev)) { + error = msg2638_start(msg2638); + if (error) + return error; + } - mutex_unlock(&msg2638->input_dev->mutex); - - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(msg2638_pm_ops, msg2638_suspend, msg2638_resume); diff --git a/drivers/input/touchscreen/mxs-lradc-ts.c b/drivers/input/touchscreen/mxs-lradc-ts.c index 9e36fee38d61..137ab3b60f2d 100644 --- a/drivers/input/touchscreen/mxs-lradc-ts.c +++ b/drivers/input/touchscreen/mxs-lradc-ts.c @@ -500,15 +500,14 @@ static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data) LRADC_CTRL1_TOUCH_DETECT_IRQ | LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2); - unsigned long flags; if (!(reg & mxs_lradc_irq_mask(lradc))) return IRQ_NONE; if (reg & ts_irq_mask) { - spin_lock_irqsave(&ts->lock, flags); - mxs_lradc_handle_touch(ts); - spin_unlock_irqrestore(&ts->lock, flags); + scoped_guard(spinlock_irqsave, &ts->lock) { + mxs_lradc_handle_touch(ts); + } /* Make sure we don't clear the next conversion's interrupt. */ clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) | LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2)); diff --git a/drivers/input/touchscreen/novatek-nvt-ts.c b/drivers/input/touchscreen/novatek-nvt-ts.c index 3e6e2ee0ba8f..708bfb933ddd 100644 --- a/drivers/input/touchscreen/novatek-nvt-ts.c +++ b/drivers/input/touchscreen/novatek-nvt-ts.c @@ -170,10 +170,10 @@ static int nvt_ts_suspend(struct device *dev) { struct nvt_ts_data *data = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&data->input->mutex); + guard(mutex)(&data->input->mutex); + if (input_device_enabled(data->input)) nvt_ts_stop(data->input); - mutex_unlock(&data->input->mutex); return 0; } @@ -182,10 +182,10 @@ static int nvt_ts_resume(struct device *dev) { struct nvt_ts_data *data = i2c_get_clientdata(to_i2c_client(dev)); - mutex_lock(&data->input->mutex); + guard(mutex)(&data->input->mutex); + if (input_device_enabled(data->input)) nvt_ts_start(data->input); - mutex_unlock(&data->input->mutex); return 0; } diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index dad5786e82a4..c6b3615c8775 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -410,26 +410,25 @@ static int pixcir_i2c_ts_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client); struct input_dev *input = ts->input; - int ret = 0; + int error; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (device_may_wakeup(&client->dev)) { if (!input_device_enabled(input)) { - ret = pixcir_start(ts); - if (ret) { + error = pixcir_start(ts); + if (error) { dev_err(dev, "Failed to start\n"); - goto unlock; + return error; } } } else if (input_device_enabled(input)) { - ret = pixcir_stop(ts); + error = pixcir_stop(ts); + if (error) + return error; } -unlock: - mutex_unlock(&input->mutex); - - return ret; + return 0; } static int pixcir_i2c_ts_resume(struct device *dev) @@ -437,26 +436,25 @@ static int pixcir_i2c_ts_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client); struct input_dev *input = ts->input; - int ret = 0; + int error; - mutex_lock(&input->mutex); + guard(mutex)(&input->mutex); if (device_may_wakeup(&client->dev)) { if (!input_device_enabled(input)) { - ret = pixcir_stop(ts); - if (ret) { + error = pixcir_stop(ts); + if (error) { dev_err(dev, "Failed to stop\n"); - goto unlock; + return error; } } } else if (input_device_enabled(input)) { - ret = pixcir_start(ts); + error = pixcir_start(ts); + if (error) + return error; } -unlock: - mutex_unlock(&input->mutex); - - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops, diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c index f975b53e8825..f2d33ad86fd2 100644 --- a/drivers/input/touchscreen/raydium_i2c_ts.c +++ b/drivers/input/touchscreen/raydium_i2c_ts.c @@ -169,10 +169,9 @@ static int raydium_i2c_send(struct i2c_client *client, { int tries = 0; int error; - u8 *tx_buf; u8 reg_addr = addr & 0xff; - tx_buf = kmalloc(len + 1, GFP_KERNEL); + u8 *tx_buf __free(kfree) = kmalloc(len + 1, GFP_KERNEL); if (!tx_buf) return -ENOMEM; @@ -210,14 +209,12 @@ static int raydium_i2c_send(struct i2c_client *client, error = raydium_i2c_xfer(client, addr, xfer, ARRAY_SIZE(xfer)); if (likely(!error)) - goto out; + return 0; msleep(RM_RETRY_DELAY_MS); } while (++tries < RM_MAX_RETRIES); dev_err(&client->dev, "%s failed: %d\n", __func__, error); -out: - kfree(tx_buf); return error; } @@ -815,21 +812,21 @@ static int raydium_i2c_do_update_firmware(struct raydium_data *ts, static int raydium_i2c_fw_update(struct raydium_data *ts) { struct i2c_client *client = ts->client; - const struct firmware *fw = NULL; - char *fw_file; int error; - fw_file = kasprintf(GFP_KERNEL, "raydium_%#04x.fw", - le32_to_cpu(ts->info.hw_ver)); + const char *fw_file __free(kfree) = + kasprintf(GFP_KERNEL, "raydium_%#04x.fw", + le32_to_cpu(ts->info.hw_ver)); if (!fw_file) return -ENOMEM; dev_dbg(&client->dev, "firmware name: %s\n", fw_file); + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, fw_file, &client->dev); if (error) { dev_err(&client->dev, "Unable to open firmware %s\n", fw_file); - goto out_free_fw_file; + return error; } disable_irq(client->irq); @@ -856,11 +853,6 @@ out_enable_irq: enable_irq(client->irq); msleep(100); - release_firmware(fw); - -out_free_fw_file: - kfree(fw_file); - return error; } @@ -965,15 +957,12 @@ static ssize_t raydium_i2c_update_fw_store(struct device *dev, struct raydium_data *ts = i2c_get_clientdata(client); int error; - error = mutex_lock_interruptible(&ts->sysfs_mutex); - if (error) - return error; + scoped_guard(mutex_intr, &ts->sysfs_mutex) { + error = raydium_i2c_fw_update(ts); + return error ?: count; + } - error = raydium_i2c_fw_update(ts); - - mutex_unlock(&ts->sysfs_mutex); - - return error ?: count; + return -EINTR; } static ssize_t raydium_i2c_calibrate_store(struct device *dev, @@ -985,17 +974,20 @@ static ssize_t raydium_i2c_calibrate_store(struct device *dev, static const u8 cal_cmd[] = { 0x00, 0x01, 0x9E }; int error; - error = mutex_lock_interruptible(&ts->sysfs_mutex); - if (error) - return error; + scoped_guard(mutex_intr, &ts->sysfs_mutex) { + error = raydium_i2c_write_object(client, + cal_cmd, sizeof(cal_cmd), + RAYDIUM_WAIT_READY); + if (error) { + dev_err(&client->dev, + "calibrate command failed: %d\n", error); + return error; + } - error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd), - RAYDIUM_WAIT_READY); - if (error) - dev_err(&client->dev, "calibrate command failed: %d\n", error); + return count; + } - mutex_unlock(&ts->sysfs_mutex); - return error ?: count; + return -EINTR; } static DEVICE_ATTR(fw_version, S_IRUGO, raydium_i2c_fw_ver_show, NULL); diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 9b3901eec0a5..9b266927b7fe 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -22,11 +23,14 @@ #include #include #include -#include +#include #define ST1232_TS_NAME "st1232-ts" #define ST1633_TS_NAME "st1633-ts" +#define REG_FIRMWARE_VERSION 0x00 +#define REG_FIRMWARE_REVISION_3 0x0C + #define REG_STATUS 0x01 /* Device Status | Error Code */ #define STATUS_NORMAL 0x00 @@ -61,8 +65,38 @@ struct st1232_ts_data { struct list_head touch_overlay_list; int read_buf_len; u8 *read_buf; + u8 fw_version; + u32 fw_revision; }; +static ssize_t fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct st1232_ts_data *st1232_ts = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%u\n", st1232_ts->fw_version); +} + +static ssize_t fw_revision_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct st1232_ts_data *st1232_ts = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%08x\n", st1232_ts->fw_revision); +} + +static DEVICE_ATTR_RO(fw_version); +static DEVICE_ATTR_RO(fw_revision); + +static struct attribute *st1232_attrs[] = { + &dev_attr_fw_version.attr, + &dev_attr_fw_revision.attr, + NULL, +}; +ATTRIBUTE_GROUPS(st1232); + static int st1232_ts_read_data(struct st1232_ts_data *ts, u8 reg, unsigned int n) { @@ -110,6 +144,26 @@ static int st1232_ts_wait_ready(struct st1232_ts_data *ts) return -ENXIO; } +static int st1232_ts_read_fw_version(struct st1232_ts_data *ts, + u8 *fw_version, u32 *fw_revision) +{ + int error; + + /* select firmware version register */ + error = st1232_ts_read_data(ts, REG_FIRMWARE_VERSION, 1); + if (error) + return error; + *fw_version = ts->read_buf[0]; + + /* select firmware revision register */ + error = st1232_ts_read_data(ts, REG_FIRMWARE_REVISION_3, 4); + if (error) + return error; + *fw_revision = le32_to_cpup((__le32 *)ts->read_buf); + + return 0; +} + static int st1232_ts_read_resolution(struct st1232_ts_data *ts, u16 *max_x, u16 *max_y) { @@ -299,6 +353,16 @@ static int st1232_ts_probe(struct i2c_client *client) if (error) return error; + /* Read firmware version from the chip */ + error = st1232_ts_read_fw_version(ts, &ts->fw_version, &ts->fw_revision); + if (error) { + dev_err(&client->dev, + "Failed to read firmware version: %d\n", error); + return error; + } + dev_dbg(&client->dev, "Detected firmware version %u, rev %08x\n", + ts->fw_version, ts->fw_revision); + if (ts->chip_info->have_z) input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, ts->chip_info->max_area, 0, 0); @@ -408,6 +472,7 @@ static struct i2c_driver st1232_ts_driver = { .driver = { .name = ST1232_TS_NAME, .of_match_table = st1232_ts_dt_ids, + .dev_groups = st1232_groups, .probe_type = PROBE_PREFER_ASYNCHRONOUS, .pm = pm_sleep_ptr(&st1232_ts_pm_ops), }, diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c index 4b166b0a9a5a..8af87d0b6eb6 100644 --- a/drivers/input/touchscreen/stmfts.c +++ b/drivers/input/touchscreen/stmfts.c @@ -302,7 +302,7 @@ static irqreturn_t stmfts_irq_handler(int irq, void *dev) struct stmfts_data *sdata = dev; int err; - mutex_lock(&sdata->mutex); + guard(mutex)(&sdata->mutex); err = stmfts_read_events(sdata); if (unlikely(err)) @@ -311,7 +311,6 @@ static irqreturn_t stmfts_irq_handler(int irq, void *dev) else stmfts_parse_events(sdata); - mutex_unlock(&sdata->mutex); return IRQ_HANDLED; } @@ -347,17 +346,17 @@ static int stmfts_input_open(struct input_dev *dev) return err; } - mutex_lock(&sdata->mutex); - sdata->running = true; + scoped_guard(mutex, &sdata->mutex) { + sdata->running = true; - if (sdata->hover_enabled) { - err = i2c_smbus_write_byte(sdata->client, - STMFTS_SS_HOVER_SENSE_ON); - if (err) - dev_warn(&sdata->client->dev, - "failed to enable hover\n"); + if (sdata->hover_enabled) { + err = i2c_smbus_write_byte(sdata->client, + STMFTS_SS_HOVER_SENSE_ON); + if (err) + dev_warn(&sdata->client->dev, + "failed to enable hover\n"); + } } - mutex_unlock(&sdata->mutex); if (sdata->use_key) { err = i2c_smbus_write_byte(sdata->client, @@ -381,18 +380,17 @@ static void stmfts_input_close(struct input_dev *dev) dev_warn(&sdata->client->dev, "failed to disable touchscreen: %d\n", err); - mutex_lock(&sdata->mutex); + scoped_guard(mutex, &sdata->mutex) { + sdata->running = false; - sdata->running = false; - - if (sdata->hover_enabled) { - err = i2c_smbus_write_byte(sdata->client, - STMFTS_SS_HOVER_SENSE_OFF); - if (err) - dev_warn(&sdata->client->dev, - "failed to disable hover: %d\n", err); + if (sdata->hover_enabled) { + err = i2c_smbus_write_byte(sdata->client, + STMFTS_SS_HOVER_SENSE_OFF); + if (err) + dev_warn(&sdata->client->dev, + "failed to disable hover: %d\n", err); + } } - mutex_unlock(&sdata->mutex); if (sdata->use_key) { err = i2c_smbus_write_byte(sdata->client, @@ -474,26 +472,27 @@ static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev, { struct stmfts_data *sdata = dev_get_drvdata(dev); unsigned long value; - int err = 0; + bool hover; + int err; if (kstrtoul(buf, 0, &value)) return -EINVAL; - mutex_lock(&sdata->mutex); + hover = !!value; - if (value && sdata->hover_enabled) - goto out; + guard(mutex)(&sdata->mutex); - if (sdata->running) - err = i2c_smbus_write_byte(sdata->client, + if (hover != sdata->hover_enabled) { + if (sdata->running) { + err = i2c_smbus_write_byte(sdata->client, value ? STMFTS_SS_HOVER_SENSE_ON : STMFTS_SS_HOVER_SENSE_OFF); + if (err) + return err; + } - if (!err) - sdata->hover_enabled = !!value; - -out: - mutex_unlock(&sdata->mutex); + sdata->hover_enabled = hover; + } return len; } diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 877eae34fb5a..fe63d53d56db 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -538,15 +538,15 @@ static void sur40_process_video(struct sur40_state *sur40) return; /* get a new buffer from the list */ - spin_lock(&sur40->qlock); - if (list_empty(&sur40->buf_list)) { - dev_dbg(sur40->dev, "buffer queue empty\n"); - spin_unlock(&sur40->qlock); - return; + scoped_guard(spinlock, &sur40->qlock) { + if (list_empty(&sur40->buf_list)) { + dev_dbg(sur40->dev, "buffer queue empty\n"); + return; + } + new_buf = list_first_entry(&sur40->buf_list, + struct sur40_buffer, list); + list_del(&new_buf->list); } - new_buf = list_entry(sur40->buf_list.next, struct sur40_buffer, list); - list_del(&new_buf->list); - spin_unlock(&sur40->qlock); dev_dbg(sur40->dev, "buffer acquired\n"); @@ -888,9 +888,8 @@ static void sur40_buffer_queue(struct vb2_buffer *vb) struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue); struct sur40_buffer *buf = (struct sur40_buffer *)vb; - spin_lock(&sur40->qlock); + guard(spinlock)(&sur40->qlock); list_add_tail(&buf->list, &sur40->buf_list); - spin_unlock(&sur40->qlock); } static void return_all_buffers(struct sur40_state *sur40, @@ -898,12 +897,12 @@ static void return_all_buffers(struct sur40_state *sur40, { struct sur40_buffer *buf, *node; - spin_lock(&sur40->qlock); + guard(spinlock)(&sur40->qlock); + list_for_each_entry_safe(buf, node, &sur40->buf_list, list) { vb2_buffer_done(&buf->vb.vb2_buf, state); list_del(&buf->list); } - spin_unlock(&sur40->qlock); } /* diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index 5fa47a1a6fdc..0d92aaeea3e0 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -117,12 +117,11 @@ static inline void sx865x_penrelease(struct sx8654 *ts) static void sx865x_penrelease_timer_handler(struct timer_list *t) { struct sx8654 *ts = timer_container_of(ts, t, timer); - unsigned long flags; - spin_lock_irqsave(&ts->lock, flags); - sx865x_penrelease(ts); - spin_unlock_irqrestore(&ts->lock, flags); dev_dbg(&ts->client->dev, "penrelease by timer\n"); + + guard(spinlock_irqsave)(&ts->lock); + sx865x_penrelease(ts); } static irqreturn_t sx8650_irq(int irq, void *handle) @@ -130,7 +129,6 @@ static irqreturn_t sx8650_irq(int irq, void *handle) struct sx8654 *ts = handle; struct device *dev = &ts->client->dev; int len, i; - unsigned long flags; u8 stat; u16 x, y; u16 ch; @@ -153,7 +151,7 @@ static irqreturn_t sx8650_irq(int irq, void *handle) return IRQ_HANDLED; } - spin_lock_irqsave(&ts->lock, flags); + guard(spinlock_irqsave)(&ts->lock); x = 0; y = 0; @@ -184,7 +182,6 @@ static irqreturn_t sx8650_irq(int irq, void *handle) dev_dbg(dev, "point(%4d,%4d)\n", x, y); mod_timer(&ts->timer, jiffies + SX8650_PENIRQ_TIMEOUT); - spin_unlock_irqrestore(&ts->lock, flags); return IRQ_HANDLED; } @@ -394,9 +391,13 @@ static int sx8654_probe(struct i2c_client *client) return error; } + /* + * Start with the interrupt disabled, it will be enabled in + * sx8654_open(). + */ error = devm_request_threaded_irq(&client->dev, client->irq, NULL, sx8654->data->irqh, - IRQF_ONESHOT, + IRQF_ONESHOT | IRQF_NO_AUTOEN, client->name, sx8654); if (error) { dev_err(&client->dev, @@ -405,9 +406,6 @@ static int sx8654_probe(struct i2c_client *client) return error; } - /* Disable the IRQ, we'll enable it in sx8654_open() */ - disable_irq(client->irq); - error = input_register_device(sx8654->input); if (error) return error; diff --git a/drivers/input/touchscreen/tsc2007_core.c b/drivers/input/touchscreen/tsc2007_core.c index 948935de894b..524f14eb3da2 100644 --- a/drivers/input/touchscreen/tsc2007_core.c +++ b/drivers/input/touchscreen/tsc2007_core.c @@ -122,9 +122,10 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle) /* pen is down, continue with the measurement */ - mutex_lock(&ts->mlock); - tsc2007_read_values(ts, &tc); - mutex_unlock(&ts->mlock); + /* Serialize access between the ISR and IIO reads. */ + scoped_guard(mutex, &ts->mlock) { + tsc2007_read_values(ts, &tc); + } rt = tsc2007_calculate_resistance(ts, &tc); diff --git a/drivers/input/touchscreen/tsc2007_iio.c b/drivers/input/touchscreen/tsc2007_iio.c index 752eb7fe5da3..e9e495ec0b6c 100644 --- a/drivers/input/touchscreen/tsc2007_iio.c +++ b/drivers/input/touchscreen/tsc2007_iio.c @@ -41,7 +41,6 @@ static int tsc2007_read_raw(struct iio_dev *indio_dev, struct tsc2007_iio *iio = iio_priv(indio_dev); struct tsc2007 *tsc = iio->ts; int adc_chan = chan->channel; - int ret = 0; if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel)) return -EINVAL; @@ -49,7 +48,7 @@ static int tsc2007_read_raw(struct iio_dev *indio_dev, if (mask != IIO_CHAN_INFO_RAW) return -EINVAL; - mutex_lock(&tsc->mlock); + guard(mutex)(&tsc->mlock); switch (chan->channel) { case 0: @@ -92,11 +91,7 @@ static int tsc2007_read_raw(struct iio_dev *indio_dev, /* Prepare for next touch reading - power down ADC, enable PENIRQ */ tsc2007_xfer(tsc, PWRDOWN); - mutex_unlock(&tsc->mlock); - - ret = IIO_VAL_INT; - - return ret; + return IIO_VAL_INT; } static const struct iio_info tsc2007_iio_info = { diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 657555c8796c..daa28135f887 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -969,24 +969,21 @@ static int nexio_init(struct usbtouch_usb *usbtouch) { struct usb_device *dev = interface_to_usbdev(usbtouch->interface); struct usb_host_interface *interface = usbtouch->interface->cur_altsetting; + struct usb_endpoint_descriptor *ep_in, *ep_out; struct nexio_priv *priv = usbtouch->priv; - int ret = -ENOMEM; int actual_len, i; char *firmware_ver = NULL, *device_name = NULL; - int input_ep = 0, output_ep = 0; + int input_ep, output_ep; + int ret; /* find first input and output endpoint */ - for (i = 0; i < interface->desc.bNumEndpoints; i++) { - if (!input_ep && - usb_endpoint_dir_in(&interface->endpoint[i].desc)) - input_ep = interface->endpoint[i].desc.bEndpointAddress; - if (!output_ep && - usb_endpoint_dir_out(&interface->endpoint[i].desc)) - output_ep = interface->endpoint[i].desc.bEndpointAddress; - } - if (!input_ep || !output_ep) + ret = usb_find_common_endpoints(interface, &ep_in, &ep_out, NULL, NULL); + if (ret) return -ENXIO; + input_ep = usb_endpoint_num(ep_in); + output_ep = usb_endpoint_num(ep_out); + u8 *buf __free(kfree) = kmalloc(NEXIO_BUFSIZE, GFP_NOIO); if (!buf) return -ENOMEM; @@ -1427,18 +1424,6 @@ static void usbtouch_free_buffers(struct usb_device *udev, kfree(usbtouch->buffer); } -static struct usb_endpoint_descriptor * -usbtouch_get_input_endpoint(struct usb_host_interface *interface) -{ - int i; - - for (i = 0; i < interface->desc.bNumEndpoints; i++) - if (usb_endpoint_dir_in(&interface->endpoint[i].desc)) - return &interface->endpoint[i].desc; - - return NULL; -} - static int usbtouch_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1447,17 +1432,21 @@ static int usbtouch_probe(struct usb_interface *intf, struct usb_endpoint_descriptor *endpoint; struct usb_device *udev = interface_to_usbdev(intf); const struct usbtouch_device_info *type; - int err = -ENOMEM; + int err; /* some devices are ignored */ type = (const struct usbtouch_device_info *)id->driver_info; if (!type) return -ENODEV; - endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting); - if (!endpoint) - return -ENXIO; + err = usb_find_int_in_endpoint(intf->cur_altsetting, &endpoint); + if (err) { + err = usb_find_bulk_in_endpoint(intf->cur_altsetting, &endpoint); + if (err) + return -ENXIO; + } + err = -ENOMEM; usbtouch = kzalloc_obj(*usbtouch); input_dev = input_allocate_device(); if (!usbtouch || !input_dev) diff --git a/drivers/input/touchscreen/wdt87xx_i2c.c b/drivers/input/touchscreen/wdt87xx_i2c.c index 1e2a6bbb88cb..bdaabb14dc8c 100644 --- a/drivers/input/touchscreen/wdt87xx_i2c.c +++ b/drivers/input/touchscreen/wdt87xx_i2c.c @@ -813,56 +813,46 @@ static int wdt87xx_load_chunk(struct i2c_client *client, return 0; } -static int wdt87xx_do_update_firmware(struct i2c_client *client, +static int wdt87xx_do_update_firmware(struct wdt87xx_data *wdt, const struct firmware *fw, unsigned int chunk_id) { - struct wdt87xx_data *wdt = i2c_get_clientdata(client); + struct i2c_client *client = wdt->client; int error; - error = wdt87xx_validate_firmware(wdt, fw); - if (error) - return error; - - error = mutex_lock_interruptible(&wdt->fw_mutex); - if (error) - return error; - - disable_irq(client->irq); - error = wdt87xx_load_chunk(client, fw, chunk_id); if (error) { dev_err(&client->dev, "firmware load failed (type: %d): %d\n", chunk_id, error); - goto out; + return error; } error = wdt87xx_sw_reset(client); if (error) { dev_err(&client->dev, "soft reset failed: %d\n", error); - goto out; + return error; } /* Refresh the parameters */ error = wdt87xx_get_sysparam(client, &wdt->param); - if (error) + if (error) { dev_err(&client->dev, "failed to refresh system parameters: %d\n", error); -out: - enable_irq(client->irq); - mutex_unlock(&wdt->fw_mutex); + return error; + } - return error ? error : 0; + return 0; } static int wdt87xx_update_firmware(struct device *dev, const char *fw_name, unsigned int chunk_id) { struct i2c_client *client = to_i2c_client(dev); - const struct firmware *fw; + struct wdt87xx_data *wdt = i2c_get_clientdata(client); int error; + const struct firmware *fw __free(firmware) = NULL; error = request_firmware(&fw, fw_name, dev); if (error) { dev_err(&client->dev, "unable to retrieve firmware %s: %d\n", @@ -870,11 +860,19 @@ static int wdt87xx_update_firmware(struct device *dev, return error; } - error = wdt87xx_do_update_firmware(client, fw, chunk_id); + error = wdt87xx_validate_firmware(wdt, fw); + if (error) + return error; - release_firmware(fw); + scoped_cond_guard(mutex_intr, return -EINTR, &wdt->fw_mutex) { + guard(disable_irq)(&client->irq); - return error ? error : 0; + error = wdt87xx_do_update_firmware(wdt, fw, chunk_id); + if (error) + return error; + } + + return 0; } static ssize_t config_csum_show(struct device *dev, diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 96354c44af87..c51822563f3f 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -126,7 +126,7 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) int timeout = 0; /* get codec */ - mutex_lock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); /* When the touchscreen is not in use, we may have to power up * the AUX ADC before we can use sample the AUX inputs-> @@ -160,7 +160,6 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) wm->codec->dig_enable(wm, false); } - mutex_unlock(&wm->codec_mutex); return (rc == RC_VALID ? auxval & 0xfff : -EBUSY); } EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); @@ -176,18 +175,11 @@ EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio) { u16 status; - enum wm97xx_gpio_status ret; - mutex_lock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); + status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); - - if (status & gpio) - ret = WM97XX_GPIO_HIGH; - else - ret = WM97XX_GPIO_LOW; - - mutex_unlock(&wm->codec_mutex); - return ret; + return (status & gpio) ? WM97XX_GPIO_HIGH : WM97XX_GPIO_LOW; } EXPORT_SYMBOL_GPL(wm97xx_get_gpio); @@ -205,7 +197,8 @@ void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, { u16 reg; - mutex_lock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); + reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS); if (status == WM97XX_GPIO_HIGH) @@ -217,7 +210,6 @@ void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1); else wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg); - mutex_unlock(&wm->codec_mutex); } EXPORT_SYMBOL_GPL(wm97xx_set_gpio); @@ -231,7 +223,8 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir, { u16 reg; - mutex_lock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); + reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); if (pol == WM97XX_GPIO_POL_HIGH) @@ -264,7 +257,6 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir, reg &= ~gpio; wm97xx_reg_write(wm, AC97_GPIO_CFG, reg); - mutex_unlock(&wm->codec_mutex); } EXPORT_SYMBOL_GPL(wm97xx_config_gpio); @@ -303,7 +295,9 @@ static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) wm->pen_is_down = 0; } else { u16 status, pol; - mutex_lock(&wm->codec_mutex); + + guard(mutex)(&wm->codec_mutex); + status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); @@ -323,7 +317,6 @@ static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) else wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & ~WM97XX_GPIO_13); - mutex_unlock(&wm->codec_mutex); } /* If the system is not using continuous mode or it provides a @@ -382,7 +375,7 @@ static int wm97xx_read_samples(struct wm97xx *wm) struct wm97xx_data data; int rc; - mutex_lock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); if (wm->mach_ops && wm->mach_ops->acc_enabled) rc = wm->mach_ops->acc_pen_down(wm); @@ -422,8 +415,7 @@ static int wm97xx_read_samples(struct wm97xx *wm) abs_y[0] > (data.y & 0xfff) || abs_y[1] < (data.y & 0xfff)) { dev_dbg(wm->dev, "Measurement out of range, dropping it\n"); - rc = RC_AGAIN; - goto out; + return RC_AGAIN; } input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff); @@ -439,8 +431,6 @@ static int wm97xx_read_samples(struct wm97xx *wm) wm->ts_reader_interval = wm->ts_reader_min_interval; } -out: - mutex_unlock(&wm->codec_mutex); return rc; } @@ -773,7 +763,8 @@ static int wm97xx_suspend(struct device *dev) else suspend_mode = 0; - mutex_lock(&wm->input_dev->mutex); + guard(mutex)(&wm->input_dev->mutex); + if (input_device_enabled(wm->input_dev)) cancel_delayed_work_sync(&wm->ts_reader); @@ -791,7 +782,6 @@ static int wm97xx_suspend(struct device *dev) reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) | 0x8000; wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg); } - mutex_unlock(&wm->input_dev->mutex); return 0; } @@ -800,7 +790,8 @@ static int wm97xx_resume(struct device *dev) { struct wm97xx *wm = dev_get_drvdata(dev); - mutex_lock(&wm->input_dev->mutex); + guard(mutex)(&wm->input_dev->mutex); + /* restore digitiser and gpios */ if (wm->id == WM9713_ID2) { wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]); @@ -827,7 +818,6 @@ static int wm97xx_resume(struct device *dev) queue_delayed_work(wm->ts_workq, &wm->ts_reader, wm->ts_reader_interval); } - mutex_unlock(&wm->input_dev->mutex); return 0; } @@ -840,13 +830,12 @@ static DEFINE_SIMPLE_DEV_PM_OPS(wm97xx_pm_ops, wm97xx_suspend, wm97xx_resume); int wm97xx_register_mach_ops(struct wm97xx *wm, struct wm97xx_mach_ops *mach_ops) { - mutex_lock(&wm->codec_mutex); - if (wm->mach_ops) { - mutex_unlock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); + + if (wm->mach_ops) return -EINVAL; - } + wm->mach_ops = mach_ops; - mutex_unlock(&wm->codec_mutex); return 0; } @@ -854,9 +843,9 @@ EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops); void wm97xx_unregister_mach_ops(struct wm97xx *wm) { - mutex_lock(&wm->codec_mutex); + guard(mutex)(&wm->codec_mutex); + wm->mach_ops = NULL; - mutex_unlock(&wm->codec_mutex); } EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops); diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c index 716d6fa60f86..0c36765bd79f 100644 --- a/drivers/input/touchscreen/zinitix.c +++ b/drivers/input/touchscreen/zinitix.c @@ -703,13 +703,11 @@ static int zinitix_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct bt541_ts_data *bt541 = i2c_get_clientdata(client); - mutex_lock(&bt541->input_dev->mutex); + guard(mutex)(&bt541->input_dev->mutex); if (input_device_enabled(bt541->input_dev)) zinitix_stop(bt541); - mutex_unlock(&bt541->input_dev->mutex); - return 0; } @@ -717,16 +715,17 @@ static int zinitix_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct bt541_ts_data *bt541 = i2c_get_clientdata(client); - int ret = 0; + int error; - mutex_lock(&bt541->input_dev->mutex); + guard(mutex)(&bt541->input_dev->mutex); - if (input_device_enabled(bt541->input_dev)) - ret = zinitix_start(bt541); + if (input_device_enabled(bt541->input_dev)) { + error = zinitix_start(bt541); + if (error) + return error; + } - mutex_unlock(&bt541->input_dev->mutex); - - return ret; + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(zinitix_pm_ops, zinitix_suspend, zinitix_resume); diff --git a/drivers/interconnect/debugfs-client.c b/drivers/interconnect/debugfs-client.c index 5107bff53173..08df9188ef94 100644 --- a/drivers/interconnect/debugfs-client.c +++ b/drivers/interconnect/debugfs-client.c @@ -150,10 +150,13 @@ int icc_debugfs_client_init(struct dentry *icc_dir) return ret; } - src_node = devm_kstrdup(&pdev->dev, "", GFP_KERNEL); - dst_node = devm_kstrdup(&pdev->dev, "", GFP_KERNEL); - if (!src_node || !dst_node) + src_node = kstrdup("", GFP_KERNEL); + dst_node = kstrdup("", GFP_KERNEL); + if (!src_node || !dst_node) { + kfree(dst_node); + kfree(src_node); return -ENOMEM; + } client_dir = debugfs_create_dir("test_client", icc_dir); diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index bb1cb8a640c1..786b4eda44b4 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -8,8 +8,17 @@ config INTERCONNECT_QCOM config INTERCONNECT_QCOM_BCM_VOTER tristate +config INTERCONNECT_QCOM_ELIZA + tristate "Qualcomm Eliza interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Network-on-Chip on Eliza-based + platforms. + config INTERCONNECT_QCOM_GLYMUR - tristate "Qualcomm GLYMUR interconnect driver" + tristate "Qualcomm Glymur interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER @@ -18,7 +27,7 @@ config INTERCONNECT_QCOM_GLYMUR platforms. config INTERCONNECT_QCOM_KAANAPALI - tristate "Qualcomm KAANAPALI interconnect driver" + tristate "Qualcomm Kaanapali interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE select INTERCONNECT_QCOM_RPMH select INTERCONNECT_QCOM_BCM_VOTER diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 6eedff043b41..cdf2c6c9fbf3 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM) += interconnect_qcom.o interconnect_qcom-y := icc-common.o icc-bcm-voter-objs := bcm-voter.o +qnoc-eliza-objs := eliza.o qnoc-glymur-objs := glymur.o qnoc-kaanapali-objs := kaanapali.o qnoc-milos-objs := milos.o @@ -48,6 +49,7 @@ qnoc-x1e80100-objs := x1e80100.o icc-smd-rpm-objs := smd-rpm.o icc-rpm.o icc-rpm-clocks.o obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o +obj-$(CONFIG_INTERCONNECT_QCOM_ELIZA) += qnoc-eliza.o obj-$(CONFIG_INTERCONNECT_QCOM_GLYMUR) += qnoc-glymur.o obj-$(CONFIG_INTERCONNECT_QCOM_KAANAPALI) += qnoc-kaanapali.o obj-$(CONFIG_INTERCONNECT_QCOM_MILOS) += qnoc-milos.o diff --git a/drivers/interconnect/qcom/eliza.c b/drivers/interconnect/qcom/eliza.c new file mode 100644 index 000000000000..a4f7903f0524 --- /dev/null +++ b/drivers/interconnect/qcom/eliza.c @@ -0,0 +1,1585 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +#include "bcm-voter.h" +#include "icc-rpmh.h" + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup2_core_slave = { + .name = "qup2_core_slave", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ahb2phy1 = { + .name = "qhs_ahb2phy1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_i3c_ibi0_cfg = { + .name = "qhs_i3c_ibi0_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_i3c_ibi1_cfg = { + .name = "qhs_i3c_ibi1_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_mss_cfg = { + .name = "qhs_mss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_pcie_0_cfg = { + .name = "qhs_pcie_0_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_qup2 = { + .name = "qhs_qup2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_usb3_0 = { + .name = "qhs_usb3_0", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .name = "qhs_vsense_ctrl_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_soccp = { + .name = "qhs_soccp", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qhs_tme_cfg = { + .name = "qhs_tme_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_apss = { + .name = "qss_apss", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qss_ddrss_cfg = { + .name = "qss_ddrss_cfg", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qxs_boot_imem = { + .name = "qxs_boot_imem", + .channels = 1, + .buswidth = 16, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node qxs_modem_boot_imem = { + .name = "qxs_modem_boot_imem", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node srvc_cnoc_main = { + .name = "srvc_cnoc_main", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node xs_pcie_0 = { + .name = "xs_pcie_0", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node xs_pcie_1 = { + .name = "xs_pcie_1", + .channels = 1, + .buswidth = 8, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .channels = 4, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_mnoc_sf = { + .name = "srvc_mnoc_sf", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_mnoc_hf = { + .name = "srvc_mnoc_hf", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node srvc_pcie_aggre_noc = { + .name = "srvc_pcie_aggre_noc", + .channels = 1, + .buswidth = 4, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_node qup2_core_master = { + .name = "qup2_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .channels = 1, + .buswidth = 16, + .num_links = 2, + .link_nodes = { &xs_pcie_0, &xs_pcie_1 }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .channels = 4, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &ebi }, +}; + +static struct qcom_icc_node qsm_sf_mnoc_cfg = { + .name = "qsm_sf_mnoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &srvc_mnoc_sf }, +}; + +static struct qcom_icc_node qsm_hf_mnoc_cfg = { + .name = "qsm_hf_mnoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &srvc_mnoc_hf }, +}; + +static struct qcom_icc_node qsm_pcie_anoc_cfg = { + .name = "qsm_pcie_anoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &srvc_pcie_aggre_noc }, +}; + +static struct qcom_icc_node qss_mnoc_hf_cfg = { + .name = "qss_mnoc_hf_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_hf_mnoc_cfg }, +}; + +static struct qcom_icc_node qss_mnoc_sf_cfg = { + .name = "qss_mnoc_sf_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_sf_mnoc_cfg }, +}; + +static struct qcom_icc_node qss_pcie_anoc_cfg = { + .name = "qss_pcie_anoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_pcie_anoc_cfg }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .channels = 2, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &llcc_mc }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_gemnoc_pcie }, +}; + +static struct qcom_icc_node qsm_cfg = { + .name = "qsm_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 29, + .link_nodes = { &qhs_ahb2phy0, &qhs_ahb2phy1, + &qhs_camera_cfg, &qhs_clk_ctl, + &qhs_crypto0_cfg, &qhs_display_cfg, + &qhs_gpuss_cfg, &qhs_i3c_ibi0_cfg, + &qhs_i3c_ibi1_cfg, &qhs_imem_cfg, + &qhs_mss_cfg, &qhs_pcie_0_cfg, + &qhs_prng, &qhs_qdss_cfg, + &qhs_qspi, &qhs_qup1, + &qhs_qup2, &qhs_sdc2, + &qhs_tcsr, &qhs_tlmm, + &qhs_ufs_mem_cfg, &qhs_usb3_0, + &qhs_venus_cfg, &qhs_vsense_ctrl_cfg, + &qss_mnoc_hf_cfg, &qss_mnoc_sf_cfg, + &qss_pcie_anoc_cfg, &xs_qdss_stm, + &xs_sys_tcu_cfg }, +}; + +static struct qcom_icc_node xm_gic = { + .name = "xm_gic", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15d000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_llcc }, +}; + +static struct qcom_icc_node qss_cfg = { + .name = "qss_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = { &qsm_cfg }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 12, + .link_nodes = { &qhs_aoss, &qhs_ipa, + &qhs_ipc_router, &qhs_soccp, + &qhs_tme_cfg, &qss_apss, + &qss_cfg, &qss_ddrss_cfg, + &qxs_boot_imem, &qxs_imem, + &qxs_modem_boot_imem, &srvc_cnoc_main }, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_gemnoc_cnoc }, +}; + +static struct qcom_icc_qosbox alm_gpu_tcu_qos = { + .num_ports = 1, + .port_offsets = { 0x155000 }, + .prio = 1, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &alm_gpu_tcu_qos, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox alm_sys_tcu_qos = { + .num_ports = 1, + .port_offsets = { 0x157000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &alm_sys_tcu_qos, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .channels = 3, + .buswidth = 32, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_qosbox qnm_gpu_qos = { + .num_ports = 2, + .port_offsets = { 0x31000, 0xb1000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_gpu_qos, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox qnm_lpass_gemnoc_qos = { + .num_ports = 1, + .port_offsets = { 0x159000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_lpass_gemnoc = { + .name = "qnm_lpass_gemnoc", + .channels = 1, + .buswidth = 16, + .qosbox = &qnm_lpass_gemnoc_qos, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mdsp = { + .name = "qnm_mdsp", + .channels = 1, + .buswidth = 16, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_qosbox qnm_mnoc_hf_qos = { + .num_ports = 2, + .port_offsets = { 0x33000, 0xb3000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_mnoc_hf_qos, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox qnm_mnoc_sf_qos = { + .num_ports = 2, + .port_offsets = { 0x35000, 0xb5000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_mnoc_sf_qos, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox qnm_nsp_gemnoc_qos = { + .num_ports = 2, + .port_offsets = { 0x37000, 0xb7000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_nsp_gemnoc = { + .name = "qnm_nsp_gemnoc", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_nsp_gemnoc_qos, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_qosbox qnm_pcie_qos = { + .num_ports = 1, + .port_offsets = { 0x15b000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .channels = 1, + .buswidth = 16, + .qosbox = &qnm_pcie_qos, + .num_links = 2, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15f000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qxm_wlan_q6 = { + .name = "qxm_wlan_q6", + .channels = 1, + .buswidth = 8, + .num_links = 3, + .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qns_lpass_ag_noc_gemnoc = { + .name = "qns_lpass_ag_noc_gemnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_lpass_gemnoc }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_mnoc_sf }, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_mnoc_hf }, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qnm_nsp_gemnoc }, +}; + +static struct qcom_icc_node qns_pcie_mem_noc = { + .name = "qns_pcie_mem_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_pcie }, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_snoc_sf }, +}; + +static struct qcom_icc_node qnm_lpiaon_noc = { + .name = "qnm_lpiaon_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qns_lpass_ag_noc_gemnoc }, +}; + +static struct qcom_icc_qosbox qnm_camnoc_nrt_icp_sf_qos = { + .num_ports = 1, + .port_offsets = { 0x25000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_camnoc_nrt_icp_sf = { + .name = "qnm_camnoc_nrt_icp_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &qnm_camnoc_nrt_icp_sf_qos, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_rt_cdm_sf = { + .name = "qnm_camnoc_rt_cdm_sf", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2c000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x26000, 0x27000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_mvp = { + .name = "qnm_video_mvp", + .channels = 1, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x28000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_video_v_cpu = { + .name = "qnm_video_v_cpu", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2b000 }, + .prio = 4, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x64000, 0x65000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qnm_mdp = { + .name = "qnm_mdp", + .channels = 2, + .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x66000, 0x67000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, + }, + .num_links = 1, + .link_nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_node qxm_nsp = { + .name = "qxm_nsp", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = { &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_node xm_pcie3_0 = { + .name = "xm_pcie3_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_node xm_pcie3_1 = { + .name = "xm_pcie3_1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 3, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_node qnm_aggre1_noc = { + .name = "qnm_aggre1_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_aggre2_noc = { + .name = "qnm_aggre2_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_cnoc_data = { + .name = "qnm_cnoc_data", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1d000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_nsinoc_snoc = { + .name = "qnm_nsinoc_snoc", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1c000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_aggre1_noc }, +}; + +static struct qcom_icc_node qns_a2noc_snoc = { + .name = "qns_a2noc_snoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_aggre2_noc }, +}; + +static struct qcom_icc_node qns_lpass_aggnoc = { + .name = "qns_lpass_aggnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_lpiaon_noc }, +}; + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup1 = { + .name = "qhm_qup1", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .channels = 1, + .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_node qhm_qup2 = { + .name = "qhm_qup2", + .channels = 1, + .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node qxm_soccp = { + .name = "qxm_soccp", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1a000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x18000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node xm_sdc1 = { + .name = "xm_sdc1", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .channels = 1, + .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x19000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, + }, + .num_links = 1, + .link_nodes = { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node qnm_lpass_lpinoc = { + .name = "qnm_lpass_lpinoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qns_lpass_aggnoc }, +}; + +static struct qcom_icc_node qns_lpi_aon_noc = { + .name = "qns_lpi_aon_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qnm_lpass_lpinoc }, +}; + +static struct qcom_icc_node qxm_lpinoc_dsp_axim = { + .name = "qxm_lpinoc_dsp_axim", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = { &qns_lpi_aon_noc }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .enable_mask = BIT(0), + .keepalive = true, + .num_nodes = 43, + .nodes = { &qsm_cfg, &qhs_ahb2phy0, + &qhs_ahb2phy1, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_crypto0_cfg, + &qhs_gpuss_cfg, &qhs_i3c_ibi0_cfg, + &qhs_i3c_ibi1_cfg, &qhs_imem_cfg, + &qhs_mss_cfg, &qhs_pcie_0_cfg, + &qhs_prng, &qhs_qdss_cfg, + &qhs_qspi, &qhs_sdc2, + &qhs_tcsr, &qhs_tlmm, + &qhs_ufs_mem_cfg, &qhs_usb3_0, + &qhs_venus_cfg, &qhs_vsense_ctrl_cfg, + &qss_mnoc_hf_cfg, &qss_mnoc_sf_cfg, + &qss_pcie_anoc_cfg, &xs_qdss_stm, + &xs_sys_tcu_cfg, &qnm_gemnoc_cnoc, + &qnm_gemnoc_pcie, &qhs_aoss, + &qhs_ipa, &qhs_ipc_router, + &qhs_soccp, &qhs_tme_cfg, + &qss_apss, &qss_cfg, + &qss_ddrss_cfg, &qxs_boot_imem, + &qxs_imem, &qxs_modem_boot_imem, + &srvc_cnoc_main, &xs_pcie_0, + &xs_pcie_1 }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 3, + .nodes = { &qhs_display_cfg, &qhs_qup1, + &qhs_qup2 }, +}; + +static struct qcom_icc_bcm bcm_co0 = { + .name = "CO0", + .enable_mask = BIT(0), + .num_nodes = 2, + .nodes = { &qxm_nsp, &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_bcm bcm_lp0 = { + .name = "LP0", + .num_nodes = 2, + .nodes = { &qnm_lpass_lpinoc, &qns_lpass_aggnoc }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .enable_mask = BIT(0), + .num_nodes = 7, + .nodes = { &qnm_camnoc_nrt_icp_sf, &qnm_camnoc_rt_cdm_sf, + &qnm_camnoc_sf, &qnm_video_mvp, + &qnm_video_v_cpu, &qnm_camnoc_hf, + &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_qup1 = { + .name = "QUP1", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup2 = { + .name = "QUP2", + .vote_scale = 1, + .keepalive = true, + .num_nodes = 1, + .nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh1 = { + .name = "SH1", + .enable_mask = BIT(0), + .num_nodes = 14, + .nodes = { &alm_gpu_tcu, &alm_sys_tcu, + &chm_apps, &qnm_gpu, + &qnm_mdsp, &qnm_mnoc_hf, + &qnm_mnoc_sf, &qnm_nsp_gemnoc, + &qnm_pcie, &qnm_snoc_sf, + &qxm_wlan_q6, &xm_gic, + &qns_gem_noc_cnoc, &qns_pcie }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn2 = { + .name = "SN2", + .num_nodes = 1, + .nodes = { &qnm_aggre1_noc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 1, + .nodes = { &qnm_aggre2_noc }, +}; + +static struct qcom_icc_bcm bcm_sn4 = { + .name = "SN4", + .num_nodes = 1, + .nodes = { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_node * const aggre1_noc_nodes[] = { + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_QUP_1] = &qhm_qup1, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB3_0] = &xm_usb3_0, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, +}; + +static const struct qcom_icc_desc eliza_aggre1_noc = { + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), + .qos_requires_clocks = true, +}; + +static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { + &bcm_ce0, +}; + +static struct qcom_icc_node * const aggre2_noc_nodes[] = { + [MASTER_QUP_2] = &qhm_qup2, + [MASTER_CRYPTO] = &qxm_crypto, + [MASTER_IPA] = &qxm_ipa, + [MASTER_SOCCP_AGGR_NOC] = &qxm_soccp, + [MASTER_QDSS_ETR] = &xm_qdss_etr_0, + [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1, + [MASTER_SDCC_1] = &xm_sdc1, + [MASTER_SDCC_2] = &xm_sdc2, + [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, +}; + +static const struct qcom_icc_desc eliza_aggre2_noc = { + .nodes = aggre2_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), + .bcms = aggre2_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), + .qos_requires_clocks = true, +}; + +static struct qcom_icc_bcm * const clk_virt_bcms[] = { + &bcm_qup1, + &bcm_qup2, +}; + +static struct qcom_icc_node * const clk_virt_nodes[] = { + [MASTER_QUP_CORE_1] = &qup1_core_master, + [MASTER_QUP_CORE_2] = &qup2_core_master, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, + [SLAVE_QUP_CORE_2] = &qup2_core_slave, +}; + +static const struct qcom_icc_desc eliza_clk_virt = { + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .bcms = clk_virt_bcms, + .num_bcms = ARRAY_SIZE(clk_virt_bcms), +}; + +static struct qcom_icc_bcm * const cnoc_cfg_bcms[] = { + &bcm_cn0, + &bcm_cn1, +}; + +static struct qcom_icc_node * const cnoc_cfg_nodes[] = { + [MASTER_CNOC_CFG] = &qsm_cfg, + [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, + [SLAVE_AHB2PHY_NORTH] = &qhs_ahb2phy1, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_I3C_IBI0_CFG] = &qhs_i3c_ibi0_cfg, + [SLAVE_I3C_IBI1_CFG] = &qhs_i3c_ibi1_cfg, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_CNOC_MSS] = &qhs_mss_cfg, + [SLAVE_PCIE_0_CFG] = &qhs_pcie_0_cfg, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_QUP_2] = &qhs_qup2, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_TCSR] = &qhs_tcsr, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB3_0] = &qhs_usb3_0, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_CNOC_MNOC_HF_CFG] = &qss_mnoc_hf_cfg, + [SLAVE_CNOC_MNOC_SF_CFG] = &qss_mnoc_sf_cfg, + [SLAVE_PCIE_ANOC_CFG] = &qss_pcie_anoc_cfg, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct qcom_icc_desc eliza_cnoc_cfg = { + .nodes = cnoc_cfg_nodes, + .num_nodes = ARRAY_SIZE(cnoc_cfg_nodes), + .bcms = cnoc_cfg_bcms, + .num_bcms = ARRAY_SIZE(cnoc_cfg_bcms), +}; + +static struct qcom_icc_bcm * const cnoc_main_bcms[] = { + &bcm_cn0, +}; + +static struct qcom_icc_node * const cnoc_main_nodes[] = { + [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc, + [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_IPA_CFG] = &qhs_ipa, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_SOCCP] = &qhs_soccp, + [SLAVE_TME_CFG] = &qhs_tme_cfg, + [SLAVE_APPSS] = &qss_apss, + [SLAVE_CNOC_CFG] = &qss_cfg, + [SLAVE_DDRSS_CFG] = &qss_ddrss_cfg, + [SLAVE_BOOT_IMEM] = &qxs_boot_imem, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_BOOT_IMEM_2] = &qxs_modem_boot_imem, + [SLAVE_SERVICE_CNOC] = &srvc_cnoc_main, + [SLAVE_PCIE_0] = &xs_pcie_0, + [SLAVE_PCIE_1] = &xs_pcie_1, +}; + +static const struct qcom_icc_desc eliza_cnoc_main = { + .nodes = cnoc_main_nodes, + .num_nodes = ARRAY_SIZE(cnoc_main_nodes), + .bcms = cnoc_main_bcms, + .num_bcms = ARRAY_SIZE(cnoc_main_bcms), +}; + +static struct qcom_icc_bcm * const gem_noc_bcms[] = { + &bcm_sh0, + &bcm_sh1, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_LPASS_GEM_NOC] = &qnm_lpass_gemnoc, + [MASTER_MSS_PROC] = &qnm_mdsp, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_COMPUTE_NOC] = &qnm_nsp_gemnoc, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_WLAN_Q6] = &qxm_wlan_q6, + [MASTER_GIC] = &xm_gic, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, +}; + +static const struct qcom_icc_desc eliza_gem_noc = { + .nodes = gem_noc_nodes, + .num_nodes = ARRAY_SIZE(gem_noc_nodes), + .bcms = gem_noc_bcms, + .num_bcms = ARRAY_SIZE(gem_noc_bcms), +}; + +static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { + [MASTER_LPIAON_NOC] = &qnm_lpiaon_noc, + [SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc, +}; + +static const struct qcom_icc_desc eliza_lpass_ag_noc = { + .nodes = lpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), +}; + +static struct qcom_icc_bcm * const lpass_lpiaon_noc_bcms[] = { + &bcm_lp0, +}; + +static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = { + [MASTER_LPASS_LPINOC] = &qnm_lpass_lpinoc, + [SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc, +}; + +static const struct qcom_icc_desc eliza_lpass_lpiaon_noc = { + .nodes = lpass_lpiaon_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes), + .bcms = lpass_lpiaon_noc_bcms, + .num_bcms = ARRAY_SIZE(lpass_lpiaon_noc_bcms), +}; + +static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = { + [MASTER_LPASS_PROC] = &qxm_lpinoc_dsp_axim, + [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc, +}; + +static const struct qcom_icc_desc eliza_lpass_lpicx_noc = { + .nodes = lpass_lpicx_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes), +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_mc0, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI1] = &ebi, +}; + +static const struct qcom_icc_desc eliza_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), +}; + +static struct qcom_icc_bcm * const mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, +}; + +static struct qcom_icc_node * const mmss_noc_nodes[] = { + [MASTER_CAMNOC_NRT_ICP_SF] = &qnm_camnoc_nrt_icp_sf, + [MASTER_CAMNOC_RT_CDM_SF] = &qnm_camnoc_rt_cdm_sf, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_VIDEO_MVP] = &qnm_video_mvp, + [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu, + [MASTER_CNOC_MNOC_SF_CFG] = &qsm_sf_mnoc_cfg, + [MASTER_CAMNOC_HF] = &qnm_camnoc_hf, + [MASTER_MDP] = &qnm_mdp, + [MASTER_CNOC_MNOC_HF_CFG] = &qsm_hf_mnoc_cfg, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, + [SLAVE_SERVICE_MNOC_SF] = &srvc_mnoc_sf, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_SERVICE_MNOC_HF] = &srvc_mnoc_hf, +}; + +static const struct qcom_icc_desc eliza_mmss_noc = { + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm * const nsp_noc_bcms[] = { + &bcm_co0, +}; + +static struct qcom_icc_node * const nsp_noc_nodes[] = { + [MASTER_CDSP_PROC] = &qxm_nsp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, +}; + +static const struct qcom_icc_desc eliza_nsp_noc = { + .nodes = nsp_noc_nodes, + .num_nodes = ARRAY_SIZE(nsp_noc_nodes), + .bcms = nsp_noc_bcms, + .num_bcms = ARRAY_SIZE(nsp_noc_bcms), +}; + +static struct qcom_icc_bcm * const pcie_anoc_bcms[] = { + &bcm_sn4, +}; + +static struct qcom_icc_node * const pcie_anoc_nodes[] = { + [MASTER_PCIE_ANOC_CFG] = &qsm_pcie_anoc_cfg, + [MASTER_PCIE_0] = &xm_pcie3_0, + [MASTER_PCIE_1] = &xm_pcie3_1, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, + [SLAVE_SERVICE_PCIE_ANOC] = &srvc_pcie_aggre_noc, +}; + +static const struct qcom_icc_desc eliza_pcie_anoc = { + .nodes = pcie_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_anoc_nodes), + .bcms = pcie_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_anoc_bcms), + .qos_requires_clocks = true, +}; + +static struct qcom_icc_bcm * const system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn2, + &bcm_sn3, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc, + [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc, + [MASTER_CNOC_SNOC] = &qnm_cnoc_data, + [MASTER_NSINOC_SNOC] = &qnm_nsinoc_snoc, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, +}; + +static const struct qcom_icc_desc eliza_system_noc = { + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,eliza-aggre1-noc", .data = &eliza_aggre1_noc }, + { .compatible = "qcom,eliza-aggre2-noc", .data = &eliza_aggre2_noc }, + { .compatible = "qcom,eliza-clk-virt", .data = &eliza_clk_virt }, + { .compatible = "qcom,eliza-cnoc-cfg", .data = &eliza_cnoc_cfg }, + { .compatible = "qcom,eliza-cnoc-main", .data = &eliza_cnoc_main }, + { .compatible = "qcom,eliza-gem-noc", .data = &eliza_gem_noc }, + { .compatible = "qcom,eliza-lpass-ag-noc", .data = &eliza_lpass_ag_noc }, + { .compatible = "qcom,eliza-lpass-lpiaon-noc", .data = &eliza_lpass_lpiaon_noc }, + { .compatible = "qcom,eliza-lpass-lpicx-noc", .data = &eliza_lpass_lpicx_noc }, + { .compatible = "qcom,eliza-mc-virt", .data = &eliza_mc_virt }, + { .compatible = "qcom,eliza-mmss-noc", .data = &eliza_mmss_noc }, + { .compatible = "qcom,eliza-nsp-noc", .data = &eliza_nsp_noc }, + { .compatible = "qcom,eliza-pcie-anoc", .data = &eliza_pcie_anoc }, + { .compatible = "qcom,eliza-system-noc", .data = &eliza_system_noc }, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-eliza", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} +module_exit(qnoc_driver_exit); + +MODULE_DESCRIPTION(" Qualcomm Eliza NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/glymur.c b/drivers/interconnect/qcom/glymur.c index e5c07795a6c6..9b9ed99ef242 100644 --- a/drivers/interconnect/qcom/glymur.c +++ b/drivers/interconnect/qcom/glymur.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "bcm-voter.h" @@ -1985,7 +1986,7 @@ static struct qcom_icc_bcm * const cnoc_cfg_bcms[] = { &bcm_cn1, }; -static struct qcom_icc_node * const cnoc_cfg_nodes[] = { +static struct qcom_icc_node *cnoc_cfg_nodes[] = { [MASTER_CNOC_CFG] = &qsm_cfg, [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, [SLAVE_AHB2PHY_NORTH] = &qhs_ahb2phy1, @@ -2093,7 +2094,7 @@ static struct qcom_icc_bcm * const hscnoc_bcms[] = { &bcm_sh1, }; -static struct qcom_icc_node * const hscnoc_nodes[] = { +static struct qcom_icc_node *hscnoc_nodes[] = { [MASTER_GPU_TCU] = &alm_gpu_tcu, [MASTER_PCIE_TCU] = &alm_pcie_qtc, [MASTER_SYS_TCU] = &alm_sys_tcu, @@ -2377,7 +2378,7 @@ static struct qcom_icc_bcm * const pcie_west_anoc_bcms[] = { &bcm_sn6, }; -static struct qcom_icc_node * const pcie_west_anoc_nodes[] = { +static struct qcom_icc_node *pcie_west_anoc_nodes[] = { [MASTER_PCIE_WEST_ANOC_CFG] = &qsm_pcie_west_anoc_cfg, [MASTER_PCIE_2] = &xm_pcie_2, [MASTER_PCIE_3A] = &xm_pcie_3a, @@ -2409,7 +2410,7 @@ static struct qcom_icc_bcm * const pcie_west_slv_noc_bcms[] = { &bcm_sn6, }; -static struct qcom_icc_node * const pcie_west_slv_noc_nodes[] = { +static struct qcom_icc_node *pcie_west_slv_noc_nodes[] = { [MASTER_HSCNOC_PCIE_WEST] = &qnm_hscnoc_pcie_west, [MASTER_CNOC_PCIE_WEST_SLAVE_CFG] = &qsm_cnoc_pcie_west_slave_cfg, [SLAVE_HSCNOC_PCIE_WEST_MS_MPU_CFG] = &qhs_hscnoc_pcie_west_ms_mpu_cfg, @@ -2470,6 +2471,28 @@ static const struct qcom_icc_desc glymur_system_noc = { .num_bcms = ARRAY_SIZE(system_noc_bcms), }; +static int glymur_qnoc_probe(struct platform_device *pdev) +{ + if (device_is_compatible(&pdev->dev, "qcom,mahua-mc-virt")) { + llcc_mc.channels = 8; + ebi.channels = 8; + } else if (device_is_compatible(&pdev->dev, "qcom,mahua-hscnoc")) { + qns_llcc.channels = 8; + chm_apps.channels = 4; + qnm_pcie_west.buswidth = 32; + hscnoc_nodes[MASTER_WLAN_Q6] = NULL; + } else if (device_is_compatible(&pdev->dev, "qcom,mahua-pcie-west-anoc")) { + qns_pcie_west_mem_noc.buswidth = 32; + pcie_west_anoc_nodes[MASTER_PCIE_3A] = NULL; + } else if (device_is_compatible(&pdev->dev, "qcom,mahua-cnoc-cfg")) { + cnoc_cfg_nodes[SLAVE_PCIE_3A_CFG] = NULL; + } else if (device_is_compatible(&pdev->dev, "qcom,mahua-pcie-west-slv-noc")) { + pcie_west_slv_noc_nodes[SLAVE_PCIE_3A] = NULL; + } + + return qcom_icc_rpmh_probe(pdev); +} + static const struct of_device_id qnoc_of_match[] = { { .compatible = "qcom,glymur-aggre1-noc", .data = &glymur_aggre1_noc}, { .compatible = "qcom,glymur-aggre2-noc", .data = &glymur_aggre2_noc}, @@ -2477,12 +2500,15 @@ static const struct of_device_id qnoc_of_match[] = { { .compatible = "qcom,glymur-aggre4-noc", .data = &glymur_aggre4_noc}, { .compatible = "qcom,glymur-clk-virt", .data = &glymur_clk_virt}, { .compatible = "qcom,glymur-cnoc-cfg", .data = &glymur_cnoc_cfg}, + { .compatible = "qcom,mahua-cnoc-cfg", .data = &glymur_cnoc_cfg}, { .compatible = "qcom,glymur-cnoc-main", .data = &glymur_cnoc_main}, { .compatible = "qcom,glymur-hscnoc", .data = &glymur_hscnoc}, + { .compatible = "qcom,mahua-hscnoc", .data = &glymur_hscnoc}, { .compatible = "qcom,glymur-lpass-ag-noc", .data = &glymur_lpass_ag_noc}, { .compatible = "qcom,glymur-lpass-lpiaon-noc", .data = &glymur_lpass_lpiaon_noc}, { .compatible = "qcom,glymur-lpass-lpicx-noc", .data = &glymur_lpass_lpicx_noc}, { .compatible = "qcom,glymur-mc-virt", .data = &glymur_mc_virt}, + { .compatible = "qcom,mahua-mc-virt", .data = &glymur_mc_virt}, { .compatible = "qcom,glymur-mmss-noc", .data = &glymur_mmss_noc}, { .compatible = "qcom,glymur-nsinoc", .data = &glymur_nsinoc}, { .compatible = "qcom,glymur-nsp-noc", .data = &glymur_nsp_noc}, @@ -2490,14 +2516,16 @@ static const struct of_device_id qnoc_of_match[] = { { .compatible = "qcom,glymur-pcie-east-anoc", .data = &glymur_pcie_east_anoc}, { .compatible = "qcom,glymur-pcie-east-slv-noc", .data = &glymur_pcie_east_slv_noc}, { .compatible = "qcom,glymur-pcie-west-anoc", .data = &glymur_pcie_west_anoc}, + { .compatible = "qcom,mahua-pcie-west-anoc", .data = &glymur_pcie_west_anoc}, { .compatible = "qcom,glymur-pcie-west-slv-noc", .data = &glymur_pcie_west_slv_noc}, + { .compatible = "qcom,mahua-pcie-west-slv-noc", .data = &glymur_pcie_west_slv_noc}, { .compatible = "qcom,glymur-system-noc", .data = &glymur_system_noc}, { } }; MODULE_DEVICE_TABLE(of, qnoc_of_match); static struct platform_driver qnoc_driver = { - .probe = qcom_icc_rpmh_probe, + .probe = glymur_qnoc_probe, .remove = qcom_icc_rpmh_remove, .driver = { .name = "qnoc-glymur", @@ -2518,5 +2546,5 @@ static void __exit qnoc_driver_exit(void) } module_exit(qnoc_driver_exit); -MODULE_DESCRIPTION("GLYMUR NoC driver"); +MODULE_DESCRIPTION("Glymur NoC driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/icc-rpm-clocks.c b/drivers/interconnect/qcom/icc-rpm-clocks.c index ac1677de7dfd..69846e26f46a 100644 --- a/drivers/interconnect/qcom/icc-rpm-clocks.c +++ b/drivers/interconnect/qcom/icc-rpm-clocks.c @@ -31,6 +31,12 @@ const struct rpm_clk_resource mem_1_clk = { }; EXPORT_SYMBOL_GPL(mem_1_clk); +const struct rpm_clk_resource gpu_mem_2_clk = { + .resource_type = QCOM_SMD_RPM_MEM_CLK, + .clock_id = 2, +}; +EXPORT_SYMBOL_GPL(gpu_mem_2_clk); + const struct rpm_clk_resource bus_0_clk = { .resource_type = QCOM_SMD_RPM_BUS_CLK, .clock_id = 0, diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index ea1042d38128..23a1d116e79a 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -204,7 +204,7 @@ static int qcom_icc_qos_set(struct icc_node *node) } } -static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw) +static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw, bool ignore_enxio) { int ret, rpm_ctx = 0; u64 bw_bps; @@ -222,8 +222,9 @@ static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw) bw_bps); if (ret) { pr_err("qcom_icc_rpm_smd_send mas %d error %d\n", - qn->mas_rpm_id, ret); - return ret; + qn->mas_rpm_id, ret); + if (ret != -ENXIO || !ignore_enxio) + return ret; } } @@ -234,8 +235,9 @@ static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw) bw_bps); if (ret) { pr_err("qcom_icc_rpm_smd_send slv %d error %d\n", - qn->slv_rpm_id, ret); - return ret; + qn->slv_rpm_id, ret); + if (ret != -ENXIO || !ignore_enxio) + return ret; } } } @@ -361,12 +363,12 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) active_rate = agg_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE]; sleep_rate = agg_clk_rate[QCOM_SMD_RPM_SLEEP_STATE]; - ret = qcom_icc_rpm_set(src_qn, src_qn->sum_avg); + ret = qcom_icc_rpm_set(src_qn, src_qn->sum_avg, qp->ignore_enxio); if (ret) return ret; if (dst_qn) { - ret = qcom_icc_rpm_set(dst_qn, dst_qn->sum_avg); + ret = qcom_icc_rpm_set(dst_qn, dst_qn->sum_avg, qp->ignore_enxio); if (ret) return ret; } @@ -509,6 +511,7 @@ int qnoc_probe(struct platform_device *pdev) for (i = 0; i < cd_num; i++) qp->intf_clks[i].id = cds[i]; + qp->ignore_enxio = desc->ignore_enxio; qp->keep_alive = desc->keep_alive; qp->type = desc->type; qp->qos_offset = desc->qos_offset; @@ -553,6 +556,7 @@ regmap_done: provider->aggregate = qcom_icc_bw_aggregate; provider->xlate_extended = qcom_icc_xlate_extended; provider->data = data; + provider->get_bw = desc->get_bw; icc_provider_init(provider); diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index f4883d43eae4..7d1cb2efa9ee 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -51,7 +51,7 @@ struct rpm_clk_resource { * @bus_clk: a pointer to a HLOS-owned bus clock * @intf_clks: a clk_bulk_data array of interface clocks * @keep_alive: whether to always keep a minimum vote on the bus clocks - * @is_on: whether the bus is powered on + * @ignore_enxio: whether to ignore ENXIO errors (for MSM8974) */ struct qcom_icc_provider { struct icc_provider provider; @@ -66,7 +66,7 @@ struct qcom_icc_provider { struct clk *bus_clk; struct clk_bulk_data *intf_clks; bool keep_alive; - bool is_on; + bool ignore_enxio; }; /** @@ -137,6 +137,8 @@ struct qcom_icc_desc { unsigned int qos_offset; u16 ab_coeff; u16 ib_coeff; + int (*get_bw)(struct icc_node *node, u32 *avg, u32 *peak); + bool ignore_enxio; }; /* Valid for all bus types */ @@ -152,6 +154,7 @@ extern const struct rpm_clk_resource bimc_clk; extern const struct rpm_clk_resource bus_0_clk; extern const struct rpm_clk_resource bus_1_clk; extern const struct rpm_clk_resource bus_2_clk; +extern const struct rpm_clk_resource gpu_mem_2_clk; extern const struct rpm_clk_resource mem_1_clk; extern const struct rpm_clk_resource mmaxi_0_clk; extern const struct rpm_clk_resource mmaxi_1_clk; diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c index 3239edc37f02..c020c61126ca 100644 --- a/drivers/interconnect/qcom/msm8974.c +++ b/drivers/interconnect/qcom/msm8974.c @@ -173,76 +173,85 @@ enum { MSM8974_SNOC_SLV_QDSS_STM, }; -#define to_msm8974_icc_provider(_provider) \ - container_of(_provider, struct msm8974_icc_provider, provider) +static int msm8974_get_bw(struct icc_node *node, u32 *avg, u32 *peak) +{ + *avg = 0; + *peak = 0; -static const struct clk_bulk_data msm8974_icc_bus_clocks[] = { - { .id = "bus" }, - { .id = "bus_a" }, + return 0; }; -/** - * struct msm8974_icc_provider - Qualcomm specific interconnect provider - * @provider: generic interconnect provider - * @bus_clks: the clk_bulk_data table of bus clocks - * @num_clks: the total number of clk_bulk_data entries - */ -struct msm8974_icc_provider { - struct icc_provider provider; - struct clk_bulk_data *bus_clks; - int num_clks; +static struct qcom_icc_node mas_ampss_m0 = { + .name = "mas_ampss_m0", + .id = MSM8974_BIMC_MAS_AMPSS_M0, + .buswidth = 8, + .mas_rpm_id = 0, + .slv_rpm_id = -1, }; -#define MSM8974_ICC_MAX_LINKS 3 - -/** - * struct msm8974_icc_node - Qualcomm specific interconnect nodes - * @name: the node name used in debugfs - * @id: a unique node identifier - * @links: an array of nodes where we can go next while traversing - * @num_links: the total number of @links - * @buswidth: width of the interconnect between a node and the bus (bytes) - * @mas_rpm_id: RPM ID for devices that are bus masters - * @slv_rpm_id: RPM ID for devices that are bus slaves - * @rate: current bus clock rate in Hz - */ -struct msm8974_icc_node { - unsigned char *name; - u16 id; - u16 links[MSM8974_ICC_MAX_LINKS]; - u16 num_links; - u16 buswidth; - int mas_rpm_id; - int slv_rpm_id; - u64 rate; +static struct qcom_icc_node mas_ampss_m1 = { + .name = "mas_ampss_m1", + .id = MSM8974_BIMC_MAS_AMPSS_M1, + .buswidth = 8, + .mas_rpm_id = 0, + .slv_rpm_id = -1, }; -struct msm8974_icc_desc { - struct msm8974_icc_node * const *nodes; - size_t num_nodes; +static struct qcom_icc_node mas_mss_proc = { + .name = "mas_mss_proc", + .id = MSM8974_BIMC_MAS_MSS_PROC, + .buswidth = 8, + .mas_rpm_id = 1, + .slv_rpm_id = -1, }; -#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ - ...) \ - static struct msm8974_icc_node _name = { \ - .name = #_name, \ - .id = _id, \ - .buswidth = _buswidth, \ - .mas_rpm_id = _mas_rpm_id, \ - .slv_rpm_id = _slv_rpm_id, \ - .num_links = COUNT_ARGS(__VA_ARGS__), \ - .links = { __VA_ARGS__ }, \ - } +static const u16 bimc_to_mnoc_links[] = { + MSM8974_BIMC_SLV_EBI_CH0 +}; -DEFINE_QNODE(mas_ampss_m0, MSM8974_BIMC_MAS_AMPSS_M0, 8, 0, -1); -DEFINE_QNODE(mas_ampss_m1, MSM8974_BIMC_MAS_AMPSS_M1, 8, 0, -1); -DEFINE_QNODE(mas_mss_proc, MSM8974_BIMC_MAS_MSS_PROC, 8, 1, -1); -DEFINE_QNODE(bimc_to_mnoc, MSM8974_BIMC_TO_MNOC, 8, 2, -1, MSM8974_BIMC_SLV_EBI_CH0); -DEFINE_QNODE(bimc_to_snoc, MSM8974_BIMC_TO_SNOC, 8, 3, 2, MSM8974_SNOC_TO_BIMC, MSM8974_BIMC_SLV_EBI_CH0, MSM8974_BIMC_MAS_AMPSS_M0); -DEFINE_QNODE(slv_ebi_ch0, MSM8974_BIMC_SLV_EBI_CH0, 8, -1, 0); -DEFINE_QNODE(slv_ampss_l2, MSM8974_BIMC_SLV_AMPSS_L2, 8, -1, 1); +static struct qcom_icc_node bimc_to_mnoc = { + .name = "bimc_to_mnoc", + .id = MSM8974_BIMC_TO_MNOC, + .buswidth = 8, + .mas_rpm_id = 2, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(bimc_to_mnoc_links), + .links = bimc_to_mnoc_links, +}; -static struct msm8974_icc_node * const msm8974_bimc_nodes[] = { +static const u16 bimc_to_snoc_links[] = { + MSM8974_SNOC_TO_BIMC, + MSM8974_BIMC_SLV_EBI_CH0, + MSM8974_BIMC_MAS_AMPSS_M0 +}; + +static struct qcom_icc_node bimc_to_snoc = { + .name = "bimc_to_snoc", + .id = MSM8974_BIMC_TO_SNOC, + .buswidth = 8, + .mas_rpm_id = 3, + .slv_rpm_id = 2, + .num_links = ARRAY_SIZE(bimc_to_snoc_links), + .links = bimc_to_snoc_links, +}; + +static struct qcom_icc_node slv_ebi_ch0 = { + .name = "slv_ebi_ch0", + .id = MSM8974_BIMC_SLV_EBI_CH0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 0, +}; + +static struct qcom_icc_node slv_ampss_l2 = { + .name = "slv_ampss_l2", + .id = MSM8974_BIMC_SLV_AMPSS_L2, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 1, +}; + +static struct qcom_icc_node * const msm8974_bimc_nodes[] = { [BIMC_MAS_AMPSS_M0] = &mas_ampss_m0, [BIMC_MAS_AMPSS_M1] = &mas_ampss_m1, [BIMC_MAS_MSS_PROC] = &mas_mss_proc, @@ -252,50 +261,311 @@ static struct msm8974_icc_node * const msm8974_bimc_nodes[] = { [BIMC_SLV_AMPSS_L2] = &slv_ampss_l2, }; -static const struct msm8974_icc_desc msm8974_bimc = { +static const struct qcom_icc_desc msm8974_bimc = { .nodes = msm8974_bimc_nodes, .num_nodes = ARRAY_SIZE(msm8974_bimc_nodes), + .bus_clk_desc = &bimc_clk, + .get_bw = msm8974_get_bw, + .ignore_enxio = true, }; -DEFINE_QNODE(mas_rpm_inst, MSM8974_CNOC_MAS_RPM_INST, 8, 45, -1); -DEFINE_QNODE(mas_rpm_data, MSM8974_CNOC_MAS_RPM_DATA, 8, 46, -1); -DEFINE_QNODE(mas_rpm_sys, MSM8974_CNOC_MAS_RPM_SYS, 8, 47, -1); -DEFINE_QNODE(mas_dehr, MSM8974_CNOC_MAS_DEHR, 8, 48, -1); -DEFINE_QNODE(mas_qdss_dap, MSM8974_CNOC_MAS_QDSS_DAP, 8, 49, -1); -DEFINE_QNODE(mas_spdm, MSM8974_CNOC_MAS_SPDM, 8, 50, -1); -DEFINE_QNODE(mas_tic, MSM8974_CNOC_MAS_TIC, 8, 51, -1); -DEFINE_QNODE(slv_clk_ctl, MSM8974_CNOC_SLV_CLK_CTL, 8, -1, 47); -DEFINE_QNODE(slv_cnoc_mss, MSM8974_CNOC_SLV_CNOC_MSS, 8, -1, 48); -DEFINE_QNODE(slv_security, MSM8974_CNOC_SLV_SECURITY, 8, -1, 49); -DEFINE_QNODE(slv_tcsr, MSM8974_CNOC_SLV_TCSR, 8, -1, 50); -DEFINE_QNODE(slv_tlmm, MSM8974_CNOC_SLV_TLMM, 8, -1, 51); -DEFINE_QNODE(slv_crypto_0_cfg, MSM8974_CNOC_SLV_CRYPTO_0_CFG, 8, -1, 52); -DEFINE_QNODE(slv_crypto_1_cfg, MSM8974_CNOC_SLV_CRYPTO_1_CFG, 8, -1, 53); -DEFINE_QNODE(slv_imem_cfg, MSM8974_CNOC_SLV_IMEM_CFG, 8, -1, 54); -DEFINE_QNODE(slv_message_ram, MSM8974_CNOC_SLV_MESSAGE_RAM, 8, -1, 55); -DEFINE_QNODE(slv_bimc_cfg, MSM8974_CNOC_SLV_BIMC_CFG, 8, -1, 56); -DEFINE_QNODE(slv_boot_rom, MSM8974_CNOC_SLV_BOOT_ROM, 8, -1, 57); -DEFINE_QNODE(slv_pmic_arb, MSM8974_CNOC_SLV_PMIC_ARB, 8, -1, 59); -DEFINE_QNODE(slv_spdm_wrapper, MSM8974_CNOC_SLV_SPDM_WRAPPER, 8, -1, 60); -DEFINE_QNODE(slv_dehr_cfg, MSM8974_CNOC_SLV_DEHR_CFG, 8, -1, 61); -DEFINE_QNODE(slv_mpm, MSM8974_CNOC_SLV_MPM, 8, -1, 62); -DEFINE_QNODE(slv_qdss_cfg, MSM8974_CNOC_SLV_QDSS_CFG, 8, -1, 63); -DEFINE_QNODE(slv_rbcpr_cfg, MSM8974_CNOC_SLV_RBCPR_CFG, 8, -1, 64); -DEFINE_QNODE(slv_rbcpr_qdss_apu_cfg, MSM8974_CNOC_SLV_RBCPR_QDSS_APU_CFG, 8, -1, 65); -DEFINE_QNODE(cnoc_to_snoc, MSM8974_CNOC_TO_SNOC, 8, 52, 75); -DEFINE_QNODE(slv_cnoc_onoc_cfg, MSM8974_CNOC_SLV_CNOC_ONOC_CFG, 8, -1, 68); -DEFINE_QNODE(slv_cnoc_mnoc_mmss_cfg, MSM8974_CNOC_SLV_CNOC_MNOC_MMSS_CFG, 8, -1, 58); -DEFINE_QNODE(slv_cnoc_mnoc_cfg, MSM8974_CNOC_SLV_CNOC_MNOC_CFG, 8, -1, 66); -DEFINE_QNODE(slv_pnoc_cfg, MSM8974_CNOC_SLV_PNOC_CFG, 8, -1, 69); -DEFINE_QNODE(slv_snoc_mpu_cfg, MSM8974_CNOC_SLV_SNOC_MPU_CFG, 8, -1, 67); -DEFINE_QNODE(slv_snoc_cfg, MSM8974_CNOC_SLV_SNOC_CFG, 8, -1, 70); -DEFINE_QNODE(slv_ebi1_dll_cfg, MSM8974_CNOC_SLV_EBI1_DLL_CFG, 8, -1, 71); -DEFINE_QNODE(slv_phy_apu_cfg, MSM8974_CNOC_SLV_PHY_APU_CFG, 8, -1, 72); -DEFINE_QNODE(slv_ebi1_phy_cfg, MSM8974_CNOC_SLV_EBI1_PHY_CFG, 8, -1, 73); -DEFINE_QNODE(slv_rpm, MSM8974_CNOC_SLV_RPM, 8, -1, 74); -DEFINE_QNODE(slv_service_cnoc, MSM8974_CNOC_SLV_SERVICE_CNOC, 8, -1, 76); +static struct qcom_icc_node mas_rpm_inst = { + .name = "mas_rpm_inst", + .id = MSM8974_CNOC_MAS_RPM_INST, + .buswidth = 8, + .mas_rpm_id = 45, + .slv_rpm_id = -1, +}; -static struct msm8974_icc_node * const msm8974_cnoc_nodes[] = { +static struct qcom_icc_node mas_rpm_data = { + .name = "mas_rpm_data", + .id = MSM8974_CNOC_MAS_RPM_DATA, + .buswidth = 8, + .mas_rpm_id = 46, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_rpm_sys = { + .name = "mas_rpm_sys", + .id = MSM8974_CNOC_MAS_RPM_SYS, + .buswidth = 8, + .mas_rpm_id = 47, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_dehr = { + .name = "mas_dehr", + .id = MSM8974_CNOC_MAS_DEHR, + .buswidth = 8, + .mas_rpm_id = 48, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_qdss_dap = { + .name = "mas_qdss_dap", + .id = MSM8974_CNOC_MAS_QDSS_DAP, + .buswidth = 8, + .mas_rpm_id = 49, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_spdm = { + .name = "mas_spdm", + .id = MSM8974_CNOC_MAS_SPDM, + .buswidth = 8, + .mas_rpm_id = 50, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_tic = { + .name = "mas_tic", + .id = MSM8974_CNOC_MAS_TIC, + .buswidth = 8, + .mas_rpm_id = 51, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_clk_ctl = { + .name = "slv_clk_ctl", + .id = MSM8974_CNOC_SLV_CLK_CTL, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 47, +}; + +static struct qcom_icc_node slv_cnoc_mss = { + .name = "slv_cnoc_mss", + .id = MSM8974_CNOC_SLV_CNOC_MSS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 48, +}; + +static struct qcom_icc_node slv_security = { + .name = "slv_security", + .id = MSM8974_CNOC_SLV_SECURITY, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 49, +}; + +static struct qcom_icc_node slv_tcsr = { + .name = "slv_tcsr", + .id = MSM8974_CNOC_SLV_TCSR, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 50, +}; + +static struct qcom_icc_node slv_tlmm = { + .name = "slv_tlmm", + .id = MSM8974_CNOC_SLV_TLMM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 51, +}; + +static struct qcom_icc_node slv_crypto_0_cfg = { + .name = "slv_crypto_0_cfg", + .id = MSM8974_CNOC_SLV_CRYPTO_0_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 52, +}; + +static struct qcom_icc_node slv_crypto_1_cfg = { + .name = "slv_crypto_1_cfg", + .id = MSM8974_CNOC_SLV_CRYPTO_1_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 53, +}; + +static struct qcom_icc_node slv_imem_cfg = { + .name = "slv_imem_cfg", + .id = MSM8974_CNOC_SLV_IMEM_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 54, +}; + +static struct qcom_icc_node slv_message_ram = { + .name = "slv_message_ram", + .id = MSM8974_CNOC_SLV_MESSAGE_RAM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 55, +}; + +static struct qcom_icc_node slv_bimc_cfg = { + .name = "slv_bimc_cfg", + .id = MSM8974_CNOC_SLV_BIMC_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 56, +}; + +static struct qcom_icc_node slv_boot_rom = { + .name = "slv_boot_rom", + .id = MSM8974_CNOC_SLV_BOOT_ROM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 57, +}; + +static struct qcom_icc_node slv_pmic_arb = { + .name = "slv_pmic_arb", + .id = MSM8974_CNOC_SLV_PMIC_ARB, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 59, +}; + +static struct qcom_icc_node slv_spdm_wrapper = { + .name = "slv_spdm_wrapper", + .id = MSM8974_CNOC_SLV_SPDM_WRAPPER, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 60, +}; + +static struct qcom_icc_node slv_dehr_cfg = { + .name = "slv_dehr_cfg", + .id = MSM8974_CNOC_SLV_DEHR_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 61, +}; + +static struct qcom_icc_node slv_mpm = { + .name = "slv_mpm", + .id = MSM8974_CNOC_SLV_MPM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 62, +}; + +static struct qcom_icc_node slv_qdss_cfg = { + .name = "slv_qdss_cfg", + .id = MSM8974_CNOC_SLV_QDSS_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 63, +}; + +static struct qcom_icc_node slv_rbcpr_cfg = { + .name = "slv_rbcpr_cfg", + .id = MSM8974_CNOC_SLV_RBCPR_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 64, +}; + +static struct qcom_icc_node slv_rbcpr_qdss_apu_cfg = { + .name = "slv_rbcpr_qdss_apu_cfg", + .id = MSM8974_CNOC_SLV_RBCPR_QDSS_APU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 65, +}; + +static struct qcom_icc_node cnoc_to_snoc = { + .name = "cnoc_to_snoc", + .id = MSM8974_CNOC_TO_SNOC, + .buswidth = 8, + .mas_rpm_id = 52, + .slv_rpm_id = 75, +}; + +static struct qcom_icc_node slv_cnoc_onoc_cfg = { + .name = "slv_cnoc_onoc_cfg", + .id = MSM8974_CNOC_SLV_CNOC_ONOC_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 68, +}; + +static struct qcom_icc_node slv_cnoc_mnoc_mmss_cfg = { + .name = "slv_cnoc_mnoc_mmss_cfg", + .id = MSM8974_CNOC_SLV_CNOC_MNOC_MMSS_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 58, +}; + +static struct qcom_icc_node slv_cnoc_mnoc_cfg = { + .name = "slv_cnoc_mnoc_cfg", + .id = MSM8974_CNOC_SLV_CNOC_MNOC_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 66, +}; + +static struct qcom_icc_node slv_pnoc_cfg = { + .name = "slv_pnoc_cfg", + .id = MSM8974_CNOC_SLV_PNOC_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 69, +}; + +static struct qcom_icc_node slv_snoc_mpu_cfg = { + .name = "slv_snoc_mpu_cfg", + .id = MSM8974_CNOC_SLV_SNOC_MPU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 67, +}; + +static struct qcom_icc_node slv_snoc_cfg = { + .name = "slv_snoc_cfg", + .id = MSM8974_CNOC_SLV_SNOC_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 70, +}; + +static struct qcom_icc_node slv_ebi1_dll_cfg = { + .name = "slv_ebi1_dll_cfg", + .id = MSM8974_CNOC_SLV_EBI1_DLL_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 71, +}; + +static struct qcom_icc_node slv_phy_apu_cfg = { + .name = "slv_phy_apu_cfg", + .id = MSM8974_CNOC_SLV_PHY_APU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 72, +}; + +static struct qcom_icc_node slv_ebi1_phy_cfg = { + .name = "slv_ebi1_phy_cfg", + .id = MSM8974_CNOC_SLV_EBI1_PHY_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 73, +}; + +static struct qcom_icc_node slv_rpm = { + .name = "slv_rpm", + .id = MSM8974_CNOC_SLV_RPM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 74, +}; + +static struct qcom_icc_node slv_service_cnoc = { + .name = "slv_service_cnoc", + .id = MSM8974_CNOC_SLV_SERVICE_CNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 76, +}; + +static struct qcom_icc_node * const msm8974_cnoc_nodes[] = { [CNOC_MAS_RPM_INST] = &mas_rpm_inst, [CNOC_MAS_RPM_DATA] = &mas_rpm_data, [CNOC_MAS_RPM_SYS] = &mas_rpm_sys, @@ -335,35 +605,221 @@ static struct msm8974_icc_node * const msm8974_cnoc_nodes[] = { [CNOC_SLV_SERVICE_CNOC] = &slv_service_cnoc, }; -static const struct msm8974_icc_desc msm8974_cnoc = { +static const struct qcom_icc_desc msm8974_cnoc = { .nodes = msm8974_cnoc_nodes, .num_nodes = ARRAY_SIZE(msm8974_cnoc_nodes), + .bus_clk_desc = &bus_2_clk, + .get_bw = msm8974_get_bw, + .ignore_enxio = true, }; -DEFINE_QNODE(mas_graphics_3d, MSM8974_MNOC_MAS_GRAPHICS_3D, 16, 6, -1, MSM8974_MNOC_TO_BIMC); -DEFINE_QNODE(mas_jpeg, MSM8974_MNOC_MAS_JPEG, 16, 7, -1, MSM8974_MNOC_TO_BIMC); -DEFINE_QNODE(mas_mdp_port0, MSM8974_MNOC_MAS_MDP_PORT0, 16, 8, -1, MSM8974_MNOC_TO_BIMC); -DEFINE_QNODE(mas_video_p0, MSM8974_MNOC_MAS_VIDEO_P0, 16, 9, -1); -DEFINE_QNODE(mas_video_p1, MSM8974_MNOC_MAS_VIDEO_P1, 16, 10, -1); -DEFINE_QNODE(mas_vfe, MSM8974_MNOC_MAS_VFE, 16, 11, -1, MSM8974_MNOC_TO_BIMC); -DEFINE_QNODE(mnoc_to_cnoc, MSM8974_MNOC_TO_CNOC, 16, 4, -1); -DEFINE_QNODE(mnoc_to_bimc, MSM8974_MNOC_TO_BIMC, 16, -1, 16, MSM8974_BIMC_TO_MNOC); -DEFINE_QNODE(slv_camera_cfg, MSM8974_MNOC_SLV_CAMERA_CFG, 16, -1, 3); -DEFINE_QNODE(slv_display_cfg, MSM8974_MNOC_SLV_DISPLAY_CFG, 16, -1, 4); -DEFINE_QNODE(slv_ocmem_cfg, MSM8974_MNOC_SLV_OCMEM_CFG, 16, -1, 5); -DEFINE_QNODE(slv_cpr_cfg, MSM8974_MNOC_SLV_CPR_CFG, 16, -1, 6); -DEFINE_QNODE(slv_cpr_xpu_cfg, MSM8974_MNOC_SLV_CPR_XPU_CFG, 16, -1, 7); -DEFINE_QNODE(slv_misc_cfg, MSM8974_MNOC_SLV_MISC_CFG, 16, -1, 8); -DEFINE_QNODE(slv_misc_xpu_cfg, MSM8974_MNOC_SLV_MISC_XPU_CFG, 16, -1, 9); -DEFINE_QNODE(slv_venus_cfg, MSM8974_MNOC_SLV_VENUS_CFG, 16, -1, 10); -DEFINE_QNODE(slv_graphics_3d_cfg, MSM8974_MNOC_SLV_GRAPHICS_3D_CFG, 16, -1, 11); -DEFINE_QNODE(slv_mmss_clk_cfg, MSM8974_MNOC_SLV_MMSS_CLK_CFG, 16, -1, 12); -DEFINE_QNODE(slv_mmss_clk_xpu_cfg, MSM8974_MNOC_SLV_MMSS_CLK_XPU_CFG, 16, -1, 13); -DEFINE_QNODE(slv_mnoc_mpu_cfg, MSM8974_MNOC_SLV_MNOC_MPU_CFG, 16, -1, 14); -DEFINE_QNODE(slv_onoc_mpu_cfg, MSM8974_MNOC_SLV_ONOC_MPU_CFG, 16, -1, 15); -DEFINE_QNODE(slv_service_mnoc, MSM8974_MNOC_SLV_SERVICE_MNOC, 16, -1, 17); +static const u16 mas_graphics_3d_links[] = { + MSM8974_MNOC_TO_BIMC +}; -static struct msm8974_icc_node * const msm8974_mnoc_nodes[] = { +static struct qcom_icc_node mas_graphics_3d = { + .name = "mas_graphics_3d", + .id = MSM8974_MNOC_MAS_GRAPHICS_3D, + .buswidth = 16, + .mas_rpm_id = 6, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_graphics_3d_links), + .links = mas_graphics_3d_links, +}; + +static const u16 mas_jpeg_links[] = { + MSM8974_MNOC_TO_BIMC +}; + +static struct qcom_icc_node mas_jpeg = { + .name = "mas_jpeg", + .id = MSM8974_MNOC_MAS_JPEG, + .buswidth = 16, + .mas_rpm_id = 7, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_jpeg_links), + .links = mas_jpeg_links, +}; + +static const u16 mas_mdp_port0_links[] = { + MSM8974_MNOC_TO_BIMC +}; + +static struct qcom_icc_node mas_mdp_port0 = { + .name = "mas_mdp_port0", + .id = MSM8974_MNOC_MAS_MDP_PORT0, + .buswidth = 16, + .mas_rpm_id = 8, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_mdp_port0_links), + .links = mas_mdp_port0_links, +}; + +static struct qcom_icc_node mas_video_p0 = { + .name = "mas_video_p0", + .id = MSM8974_MNOC_MAS_VIDEO_P0, + .buswidth = 16, + .mas_rpm_id = 9, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_video_p1 = { + .name = "mas_video_p1", + .id = MSM8974_MNOC_MAS_VIDEO_P1, + .buswidth = 16, + .mas_rpm_id = 10, + .slv_rpm_id = -1, +}; + +static const u16 mas_vfe_links[] = { + MSM8974_MNOC_TO_BIMC +}; + +static struct qcom_icc_node mas_vfe = { + .name = "mas_vfe", + .id = MSM8974_MNOC_MAS_VFE, + .buswidth = 16, + .mas_rpm_id = 11, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_vfe_links), + .links = mas_vfe_links, +}; + +static struct qcom_icc_node mnoc_to_cnoc = { + .name = "mnoc_to_cnoc", + .id = MSM8974_MNOC_TO_CNOC, + .buswidth = 16, + .mas_rpm_id = 4, + .slv_rpm_id = -1, +}; + +static const u16 mnoc_to_bimc_links[] = { + MSM8974_BIMC_TO_MNOC +}; + +static struct qcom_icc_node mnoc_to_bimc = { + .name = "mnoc_to_bimc", + .id = MSM8974_MNOC_TO_BIMC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 16, + .num_links = ARRAY_SIZE(mnoc_to_bimc_links), + .links = mnoc_to_bimc_links, +}; + +static struct qcom_icc_node slv_camera_cfg = { + .name = "slv_camera_cfg", + .id = MSM8974_MNOC_SLV_CAMERA_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 3, +}; + +static struct qcom_icc_node slv_display_cfg = { + .name = "slv_display_cfg", + .id = MSM8974_MNOC_SLV_DISPLAY_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 4, +}; + +static struct qcom_icc_node slv_ocmem_cfg = { + .name = "slv_ocmem_cfg", + .id = MSM8974_MNOC_SLV_OCMEM_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 5, +}; + +static struct qcom_icc_node slv_cpr_cfg = { + .name = "slv_cpr_cfg", + .id = MSM8974_MNOC_SLV_CPR_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 6, +}; + +static struct qcom_icc_node slv_cpr_xpu_cfg = { + .name = "slv_cpr_xpu_cfg", + .id = MSM8974_MNOC_SLV_CPR_XPU_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 7, +}; + +static struct qcom_icc_node slv_misc_cfg = { + .name = "slv_misc_cfg", + .id = MSM8974_MNOC_SLV_MISC_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 8, +}; + +static struct qcom_icc_node slv_misc_xpu_cfg = { + .name = "slv_misc_xpu_cfg", + .id = MSM8974_MNOC_SLV_MISC_XPU_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 9, +}; + +static struct qcom_icc_node slv_venus_cfg = { + .name = "slv_venus_cfg", + .id = MSM8974_MNOC_SLV_VENUS_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 10, +}; + +static struct qcom_icc_node slv_graphics_3d_cfg = { + .name = "slv_graphics_3d_cfg", + .id = MSM8974_MNOC_SLV_GRAPHICS_3D_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 11, +}; + +static struct qcom_icc_node slv_mmss_clk_cfg = { + .name = "slv_mmss_clk_cfg", + .id = MSM8974_MNOC_SLV_MMSS_CLK_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 12, +}; + +static struct qcom_icc_node slv_mmss_clk_xpu_cfg = { + .name = "slv_mmss_clk_xpu_cfg", + .id = MSM8974_MNOC_SLV_MMSS_CLK_XPU_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 13, +}; + +static struct qcom_icc_node slv_mnoc_mpu_cfg = { + .name = "slv_mnoc_mpu_cfg", + .id = MSM8974_MNOC_SLV_MNOC_MPU_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 14, +}; + +static struct qcom_icc_node slv_onoc_mpu_cfg = { + .name = "slv_onoc_mpu_cfg", + .id = MSM8974_MNOC_SLV_ONOC_MPU_CFG, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 15, +}; + +static struct qcom_icc_node slv_service_mnoc = { + .name = "slv_service_mnoc", + .id = MSM8974_MNOC_SLV_SERVICE_MNOC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 17, +}; + +static struct qcom_icc_node * const msm8974_mnoc_nodes[] = { [MNOC_MAS_GRAPHICS_3D] = &mas_graphics_3d, [MNOC_MAS_JPEG] = &mas_jpeg, [MNOC_MAS_MDP_PORT0] = &mas_mdp_port0, @@ -388,27 +844,130 @@ static struct msm8974_icc_node * const msm8974_mnoc_nodes[] = { [MNOC_SLV_SERVICE_MNOC] = &slv_service_mnoc, }; -static const struct msm8974_icc_desc msm8974_mnoc = { +static const struct qcom_icc_desc msm8974_mnoc = { .nodes = msm8974_mnoc_nodes, .num_nodes = ARRAY_SIZE(msm8974_mnoc_nodes), + .get_bw = msm8974_get_bw, + .ignore_enxio = true, }; -DEFINE_QNODE(ocmem_noc_to_ocmem_vnoc, MSM8974_OCMEM_NOC_TO_OCMEM_VNOC, 16, 54, 78, MSM8974_OCMEM_SLV_OCMEM); -DEFINE_QNODE(mas_jpeg_ocmem, MSM8974_OCMEM_MAS_JPEG_OCMEM, 16, 13, -1); -DEFINE_QNODE(mas_mdp_ocmem, MSM8974_OCMEM_MAS_MDP_OCMEM, 16, 14, -1); -DEFINE_QNODE(mas_video_p0_ocmem, MSM8974_OCMEM_MAS_VIDEO_P0_OCMEM, 16, 15, -1); -DEFINE_QNODE(mas_video_p1_ocmem, MSM8974_OCMEM_MAS_VIDEO_P1_OCMEM, 16, 16, -1); -DEFINE_QNODE(mas_vfe_ocmem, MSM8974_OCMEM_MAS_VFE_OCMEM, 16, 17, -1); -DEFINE_QNODE(mas_cnoc_onoc_cfg, MSM8974_OCMEM_MAS_CNOC_ONOC_CFG, 16, 12, -1); -DEFINE_QNODE(slv_service_onoc, MSM8974_OCMEM_SLV_SERVICE_ONOC, 16, -1, 19); -DEFINE_QNODE(slv_ocmem, MSM8974_OCMEM_SLV_OCMEM, 16, -1, 18); +static const u16 ocmem_noc_to_ocmem_vnoc_links[] = { + MSM8974_OCMEM_SLV_OCMEM +}; + +static struct qcom_icc_node ocmem_noc_to_ocmem_vnoc = { + .name = "ocmem_noc_to_ocmem_vnoc", + .id = MSM8974_OCMEM_NOC_TO_OCMEM_VNOC, + .buswidth = 16, + .mas_rpm_id = 54, + .slv_rpm_id = 78, + .num_links = ARRAY_SIZE(ocmem_noc_to_ocmem_vnoc_links), + .links = ocmem_noc_to_ocmem_vnoc_links, +}; + +static struct qcom_icc_node mas_jpeg_ocmem = { + .name = "mas_jpeg_ocmem", + .id = MSM8974_OCMEM_MAS_JPEG_OCMEM, + .buswidth = 16, + .mas_rpm_id = 13, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_mdp_ocmem = { + .name = "mas_mdp_ocmem", + .id = MSM8974_OCMEM_MAS_MDP_OCMEM, + .buswidth = 16, + .mas_rpm_id = 14, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_video_p0_ocmem = { + .name = "mas_video_p0_ocmem", + .id = MSM8974_OCMEM_MAS_VIDEO_P0_OCMEM, + .buswidth = 16, + .mas_rpm_id = 15, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_video_p1_ocmem = { + .name = "mas_video_p1_ocmem", + .id = MSM8974_OCMEM_MAS_VIDEO_P1_OCMEM, + .buswidth = 16, + .mas_rpm_id = 16, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_vfe_ocmem = { + .name = "mas_vfe_ocmem", + .id = MSM8974_OCMEM_MAS_VFE_OCMEM, + .buswidth = 16, + .mas_rpm_id = 17, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_cnoc_onoc_cfg = { + .name = "mas_cnoc_onoc_cfg", + .id = MSM8974_OCMEM_MAS_CNOC_ONOC_CFG, + .buswidth = 16, + .mas_rpm_id = 12, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node slv_service_onoc = { + .name = "slv_service_onoc", + .id = MSM8974_OCMEM_SLV_SERVICE_ONOC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 19, +}; + +static struct qcom_icc_node slv_ocmem = { + .name = "slv_ocmem", + .id = MSM8974_OCMEM_SLV_OCMEM, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 18, +}; /* Virtual NoC is needed for connection to OCMEM */ -DEFINE_QNODE(ocmem_vnoc_to_onoc, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC, 16, 56, 79, MSM8974_OCMEM_NOC_TO_OCMEM_VNOC); -DEFINE_QNODE(ocmem_vnoc_to_snoc, MSM8974_OCMEM_VNOC_TO_SNOC, 8, 57, 80); -DEFINE_QNODE(mas_v_ocmem_gfx3d, MSM8974_OCMEM_VNOC_MAS_GFX3D, 8, 55, -1, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC); +static const u16 ocmem_vnoc_to_onoc_links[] = { + MSM8974_OCMEM_NOC_TO_OCMEM_VNOC +}; -static struct msm8974_icc_node * const msm8974_onoc_nodes[] = { +static struct qcom_icc_node ocmem_vnoc_to_onoc = { + .name = "ocmem_vnoc_to_onoc", + .id = MSM8974_OCMEM_VNOC_TO_OCMEM_NOC, + .buswidth = 16, + .mas_rpm_id = 56, + .slv_rpm_id = 79, + .num_links = ARRAY_SIZE(ocmem_vnoc_to_onoc_links), + .links = ocmem_vnoc_to_onoc_links, +}; + +static struct qcom_icc_node ocmem_vnoc_to_snoc = { + .name = "ocmem_vnoc_to_snoc", + .id = MSM8974_OCMEM_VNOC_TO_SNOC, + .buswidth = 8, + .mas_rpm_id = 57, + .slv_rpm_id = 80, +}; + +static const u16 mas_v_ocmem_gfx3d_links[] = { + MSM8974_OCMEM_VNOC_TO_OCMEM_NOC +}; + +static struct qcom_icc_node mas_v_ocmem_gfx3d = { + .name = "mas_v_ocmem_gfx3d", + .id = MSM8974_OCMEM_VNOC_MAS_GFX3D, + .buswidth = 8, + .mas_rpm_id = 55, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_v_ocmem_gfx3d_links), + .links = mas_v_ocmem_gfx3d_links, +}; + + +static struct qcom_icc_node * const msm8974_onoc_nodes[] = { [OCMEM_NOC_TO_OCMEM_VNOC] = &ocmem_noc_to_ocmem_vnoc, [OCMEM_MAS_JPEG_OCMEM] = &mas_jpeg_ocmem, [OCMEM_MAS_MDP_OCMEM] = &mas_mdp_ocmem, @@ -423,40 +982,298 @@ static struct msm8974_icc_node * const msm8974_onoc_nodes[] = { [OCMEM_SLV_OCMEM] = &slv_ocmem, }; -static const struct msm8974_icc_desc msm8974_onoc = { +static const struct qcom_icc_desc msm8974_onoc = { .nodes = msm8974_onoc_nodes, .num_nodes = ARRAY_SIZE(msm8974_onoc_nodes), + .bus_clk_desc = &gpu_mem_2_clk, + .get_bw = msm8974_get_bw, + .ignore_enxio = true, }; -DEFINE_QNODE(mas_pnoc_cfg, MSM8974_PNOC_MAS_PNOC_CFG, 8, 43, -1); -DEFINE_QNODE(mas_sdcc_1, MSM8974_PNOC_MAS_SDCC_1, 8, 33, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_sdcc_3, MSM8974_PNOC_MAS_SDCC_3, 8, 34, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_sdcc_4, MSM8974_PNOC_MAS_SDCC_4, 8, 36, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_sdcc_2, MSM8974_PNOC_MAS_SDCC_2, 8, 35, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_tsif, MSM8974_PNOC_MAS_TSIF, 8, 37, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_bam_dma, MSM8974_PNOC_MAS_BAM_DMA, 8, 38, -1); -DEFINE_QNODE(mas_blsp_2, MSM8974_PNOC_MAS_BLSP_2, 8, 39, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_usb_hsic, MSM8974_PNOC_MAS_USB_HSIC, 8, 40, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_blsp_1, MSM8974_PNOC_MAS_BLSP_1, 8, 41, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(mas_usb_hs, MSM8974_PNOC_MAS_USB_HS, 8, 42, -1, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(pnoc_to_snoc, MSM8974_PNOC_TO_SNOC, 8, 44, 45, MSM8974_SNOC_TO_PNOC, MSM8974_PNOC_SLV_PRNG); -DEFINE_QNODE(slv_sdcc_1, MSM8974_PNOC_SLV_SDCC_1, 8, -1, 31); -DEFINE_QNODE(slv_sdcc_3, MSM8974_PNOC_SLV_SDCC_3, 8, -1, 32); -DEFINE_QNODE(slv_sdcc_2, MSM8974_PNOC_SLV_SDCC_2, 8, -1, 33); -DEFINE_QNODE(slv_sdcc_4, MSM8974_PNOC_SLV_SDCC_4, 8, -1, 34); -DEFINE_QNODE(slv_tsif, MSM8974_PNOC_SLV_TSIF, 8, -1, 35); -DEFINE_QNODE(slv_bam_dma, MSM8974_PNOC_SLV_BAM_DMA, 8, -1, 36); -DEFINE_QNODE(slv_blsp_2, MSM8974_PNOC_SLV_BLSP_2, 8, -1, 37); -DEFINE_QNODE(slv_usb_hsic, MSM8974_PNOC_SLV_USB_HSIC, 8, -1, 38); -DEFINE_QNODE(slv_blsp_1, MSM8974_PNOC_SLV_BLSP_1, 8, -1, 39); -DEFINE_QNODE(slv_usb_hs, MSM8974_PNOC_SLV_USB_HS, 8, -1, 40); -DEFINE_QNODE(slv_pdm, MSM8974_PNOC_SLV_PDM, 8, -1, 41); -DEFINE_QNODE(slv_periph_apu_cfg, MSM8974_PNOC_SLV_PERIPH_APU_CFG, 8, -1, 42); -DEFINE_QNODE(slv_pnoc_mpu_cfg, MSM8974_PNOC_SLV_PNOC_MPU_CFG, 8, -1, 43); -DEFINE_QNODE(slv_prng, MSM8974_PNOC_SLV_PRNG, 8, -1, 44, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(slv_service_pnoc, MSM8974_PNOC_SLV_SERVICE_PNOC, 8, -1, 46); +static struct qcom_icc_node mas_pnoc_cfg = { + .name = "mas_pnoc_cfg", + .id = MSM8974_PNOC_MAS_PNOC_CFG, + .buswidth = 8, + .mas_rpm_id = 43, + .slv_rpm_id = -1, +}; -static struct msm8974_icc_node * const msm8974_pnoc_nodes[] = { +static const u16 mas_sdcc_1_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_sdcc_1 = { + .name = "mas_sdcc_1", + .id = MSM8974_PNOC_MAS_SDCC_1, + .buswidth = 8, + .mas_rpm_id = 33, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_sdcc_1_links), + .links = mas_sdcc_1_links, +}; + +static const u16 mas_sdcc_3_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_sdcc_3 = { + .name = "mas_sdcc_3", + .id = MSM8974_PNOC_MAS_SDCC_3, + .buswidth = 8, + .mas_rpm_id = 34, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_sdcc_3_links), + .links = mas_sdcc_3_links, +}; + +static const u16 mas_sdcc_4_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_sdcc_4 = { + .name = "mas_sdcc_4", + .id = MSM8974_PNOC_MAS_SDCC_4, + .buswidth = 8, + .mas_rpm_id = 36, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_sdcc_4_links), + .links = mas_sdcc_4_links, +}; + +static const u16 mas_sdcc_2_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_sdcc_2 = { + .name = "mas_sdcc_2", + .id = MSM8974_PNOC_MAS_SDCC_2, + .buswidth = 8, + .mas_rpm_id = 35, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_sdcc_2_links), + .links = mas_sdcc_2_links, +}; + +static const u16 mas_tsif_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_tsif = { + .name = "mas_tsif", + .id = MSM8974_PNOC_MAS_TSIF, + .buswidth = 8, + .mas_rpm_id = 37, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_tsif_links), + .links = mas_tsif_links, +}; + +static struct qcom_icc_node mas_bam_dma = { + .name = "mas_bam_dma", + .id = MSM8974_PNOC_MAS_BAM_DMA, + .buswidth = 8, + .mas_rpm_id = 38, + .slv_rpm_id = -1, +}; + +static const u16 mas_blsp_2_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_blsp_2 = { + .name = "mas_blsp_2", + .id = MSM8974_PNOC_MAS_BLSP_2, + .buswidth = 8, + .mas_rpm_id = 39, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_blsp_2_links), + .links = mas_blsp_2_links, +}; + +static const u16 mas_usb_hsic_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_usb_hsic = { + .name = "mas_usb_hsic", + .id = MSM8974_PNOC_MAS_USB_HSIC, + .buswidth = 8, + .mas_rpm_id = 40, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb_hsic_links), + .links = mas_usb_hsic_links, +}; + +static const u16 mas_blsp_1_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_blsp_1 = { + .name = "mas_blsp_1", + .id = MSM8974_PNOC_MAS_BLSP_1, + .buswidth = 8, + .mas_rpm_id = 41, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_blsp_1_links), + .links = mas_blsp_1_links, +}; + +static const u16 mas_usb_hs_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node mas_usb_hs = { + .name = "mas_usb_hs", + .id = MSM8974_PNOC_MAS_USB_HS, + .buswidth = 8, + .mas_rpm_id = 42, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb_hs_links), + .links = mas_usb_hs_links, +}; + +static const u16 pnoc_to_snoc_links[] = { + MSM8974_SNOC_TO_PNOC, + MSM8974_PNOC_SLV_PRNG +}; + +static struct qcom_icc_node pnoc_to_snoc = { + .name = "pnoc_to_snoc", + .id = MSM8974_PNOC_TO_SNOC, + .buswidth = 8, + .mas_rpm_id = 44, + .slv_rpm_id = 45, + .num_links = ARRAY_SIZE(pnoc_to_snoc_links), + .links = pnoc_to_snoc_links, +}; + +static struct qcom_icc_node slv_sdcc_1 = { + .name = "slv_sdcc_1", + .id = MSM8974_PNOC_SLV_SDCC_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 31, +}; + +static struct qcom_icc_node slv_sdcc_3 = { + .name = "slv_sdcc_3", + .id = MSM8974_PNOC_SLV_SDCC_3, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 32, +}; + +static struct qcom_icc_node slv_sdcc_2 = { + .name = "slv_sdcc_2", + .id = MSM8974_PNOC_SLV_SDCC_2, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 33, +}; + +static struct qcom_icc_node slv_sdcc_4 = { + .name = "slv_sdcc_4", + .id = MSM8974_PNOC_SLV_SDCC_4, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 34, +}; + +static struct qcom_icc_node slv_tsif = { + .name = "slv_tsif", + .id = MSM8974_PNOC_SLV_TSIF, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 35, +}; + +static struct qcom_icc_node slv_bam_dma = { + .name = "slv_bam_dma", + .id = MSM8974_PNOC_SLV_BAM_DMA, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 36, +}; + +static struct qcom_icc_node slv_blsp_2 = { + .name = "slv_blsp_2", + .id = MSM8974_PNOC_SLV_BLSP_2, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 37, +}; + +static struct qcom_icc_node slv_usb_hsic = { + .name = "slv_usb_hsic", + .id = MSM8974_PNOC_SLV_USB_HSIC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 38, +}; + +static struct qcom_icc_node slv_blsp_1 = { + .name = "slv_blsp_1", + .id = MSM8974_PNOC_SLV_BLSP_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 39, +}; + +static struct qcom_icc_node slv_usb_hs = { + .name = "slv_usb_hs", + .id = MSM8974_PNOC_SLV_USB_HS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 40, +}; + +static struct qcom_icc_node slv_pdm = { + .name = "slv_pdm", + .id = MSM8974_PNOC_SLV_PDM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 41, +}; + +static struct qcom_icc_node slv_periph_apu_cfg = { + .name = "slv_periph_apu_cfg", + .id = MSM8974_PNOC_SLV_PERIPH_APU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 42, +}; + +static struct qcom_icc_node slv_pnoc_mpu_cfg = { + .name = "slv_pnoc_mpu_cfg", + .id = MSM8974_PNOC_SLV_PNOC_MPU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 43, +}; + +static const u16 slv_prng_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node slv_prng = { + .name = "slv_prng", + .id = MSM8974_PNOC_SLV_PRNG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 44, + .num_links = ARRAY_SIZE(slv_prng_links), + .links = slv_prng_links, +}; + +static struct qcom_icc_node slv_service_pnoc = { + .name = "slv_service_pnoc", + .id = MSM8974_PNOC_SLV_SERVICE_PNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 46, +}; + +static struct qcom_icc_node * const msm8974_pnoc_nodes[] = { [PNOC_MAS_PNOC_CFG] = &mas_pnoc_cfg, [PNOC_MAS_SDCC_1] = &mas_sdcc_1, [PNOC_MAS_SDCC_3] = &mas_sdcc_3, @@ -486,37 +1303,244 @@ static struct msm8974_icc_node * const msm8974_pnoc_nodes[] = { [PNOC_SLV_SERVICE_PNOC] = &slv_service_pnoc, }; -static const struct msm8974_icc_desc msm8974_pnoc = { +static const struct qcom_icc_desc msm8974_pnoc = { .nodes = msm8974_pnoc_nodes, .num_nodes = ARRAY_SIZE(msm8974_pnoc_nodes), + .bus_clk_desc = &bus_0_clk, + .get_bw = msm8974_get_bw, + .keep_alive = true, + .ignore_enxio = true, }; -DEFINE_QNODE(mas_lpass_ahb, MSM8974_SNOC_MAS_LPASS_AHB, 8, 18, -1); -DEFINE_QNODE(mas_qdss_bam, MSM8974_SNOC_MAS_QDSS_BAM, 8, 19, -1); -DEFINE_QNODE(mas_snoc_cfg, MSM8974_SNOC_MAS_SNOC_CFG, 8, 20, -1); -DEFINE_QNODE(snoc_to_bimc, MSM8974_SNOC_TO_BIMC, 8, 21, 24, MSM8974_BIMC_TO_SNOC); -DEFINE_QNODE(snoc_to_cnoc, MSM8974_SNOC_TO_CNOC, 8, 22, 25); -DEFINE_QNODE(snoc_to_pnoc, MSM8974_SNOC_TO_PNOC, 8, 29, 28, MSM8974_PNOC_TO_SNOC); -DEFINE_QNODE(snoc_to_ocmem_vnoc, MSM8974_SNOC_TO_OCMEM_VNOC, 8, 53, 77, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC); -DEFINE_QNODE(mas_crypto_core0, MSM8974_SNOC_MAS_CRYPTO_CORE0, 8, 23, -1, MSM8974_SNOC_TO_BIMC); -DEFINE_QNODE(mas_crypto_core1, MSM8974_SNOC_MAS_CRYPTO_CORE1, 8, 24, -1); -DEFINE_QNODE(mas_lpass_proc, MSM8974_SNOC_MAS_LPASS_PROC, 8, 25, -1, MSM8974_SNOC_TO_OCMEM_VNOC); -DEFINE_QNODE(mas_mss, MSM8974_SNOC_MAS_MSS, 8, 26, -1); -DEFINE_QNODE(mas_mss_nav, MSM8974_SNOC_MAS_MSS_NAV, 8, 27, -1); -DEFINE_QNODE(mas_ocmem_dma, MSM8974_SNOC_MAS_OCMEM_DMA, 8, 28, -1); -DEFINE_QNODE(mas_wcss, MSM8974_SNOC_MAS_WCSS, 8, 30, -1); -DEFINE_QNODE(mas_qdss_etr, MSM8974_SNOC_MAS_QDSS_ETR, 8, 31, -1); -DEFINE_QNODE(mas_usb3, MSM8974_SNOC_MAS_USB3, 8, 32, -1, MSM8974_SNOC_TO_BIMC); -DEFINE_QNODE(slv_ampss, MSM8974_SNOC_SLV_AMPSS, 8, -1, 20); -DEFINE_QNODE(slv_lpass, MSM8974_SNOC_SLV_LPASS, 8, -1, 21); -DEFINE_QNODE(slv_usb3, MSM8974_SNOC_SLV_USB3, 8, -1, 22); -DEFINE_QNODE(slv_wcss, MSM8974_SNOC_SLV_WCSS, 8, -1, 23); -DEFINE_QNODE(slv_ocimem, MSM8974_SNOC_SLV_OCIMEM, 8, -1, 26); -DEFINE_QNODE(slv_snoc_ocmem, MSM8974_SNOC_SLV_SNOC_OCMEM, 8, -1, 27); -DEFINE_QNODE(slv_service_snoc, MSM8974_SNOC_SLV_SERVICE_SNOC, 8, -1, 29); -DEFINE_QNODE(slv_qdss_stm, MSM8974_SNOC_SLV_QDSS_STM, 8, -1, 30); +static struct qcom_icc_node mas_lpass_ahb = { + .name = "mas_lpass_ahb", + .id = MSM8974_SNOC_MAS_LPASS_AHB, + .buswidth = 8, + .mas_rpm_id = 18, + .slv_rpm_id = -1, +}; -static struct msm8974_icc_node * const msm8974_snoc_nodes[] = { +static struct qcom_icc_node mas_qdss_bam = { + .name = "mas_qdss_bam", + .id = MSM8974_SNOC_MAS_QDSS_BAM, + .buswidth = 8, + .mas_rpm_id = 19, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_snoc_cfg = { + .name = "mas_snoc_cfg", + .id = MSM8974_SNOC_MAS_SNOC_CFG, + .buswidth = 8, + .mas_rpm_id = 20, + .slv_rpm_id = -1, +}; + +static const u16 snoc_to_bimc_links[] = { + MSM8974_BIMC_TO_SNOC +}; + +static struct qcom_icc_node snoc_to_bimc = { + .name = "snoc_to_bimc", + .id = MSM8974_SNOC_TO_BIMC, + .buswidth = 8, + .mas_rpm_id = 21, + .slv_rpm_id = 24, + .num_links = ARRAY_SIZE(snoc_to_bimc_links), + .links = snoc_to_bimc_links, +}; + +static struct qcom_icc_node snoc_to_cnoc = { + .name = "snoc_to_cnoc", + .id = MSM8974_SNOC_TO_CNOC, + .buswidth = 8, + .mas_rpm_id = 22, + .slv_rpm_id = 25, +}; + +static const u16 snoc_to_pnoc_links[] = { + MSM8974_PNOC_TO_SNOC +}; + +static struct qcom_icc_node snoc_to_pnoc = { + .name = "snoc_to_pnoc", + .id = MSM8974_SNOC_TO_PNOC, + .buswidth = 8, + .mas_rpm_id = 29, + .slv_rpm_id = 28, + .num_links = ARRAY_SIZE(snoc_to_pnoc_links), + .links = snoc_to_pnoc_links, +}; + +static const u16 snoc_to_ocmem_vnoc_links[] = { + MSM8974_OCMEM_VNOC_TO_OCMEM_NOC +}; + +static struct qcom_icc_node snoc_to_ocmem_vnoc = { + .name = "snoc_to_ocmem_vnoc", + .id = MSM8974_SNOC_TO_OCMEM_VNOC, + .buswidth = 8, + .mas_rpm_id = 53, + .slv_rpm_id = 77, + .num_links = ARRAY_SIZE(snoc_to_ocmem_vnoc_links), + .links = snoc_to_ocmem_vnoc_links, +}; + +static const u16 mas_crypto_core0_links[] = { + MSM8974_SNOC_TO_BIMC +}; + +static struct qcom_icc_node mas_crypto_core0 = { + .name = "mas_crypto_core0", + .id = MSM8974_SNOC_MAS_CRYPTO_CORE0, + .buswidth = 8, + .mas_rpm_id = 23, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_crypto_core0_links), + .links = mas_crypto_core0_links, +}; + +static struct qcom_icc_node mas_crypto_core1 = { + .name = "mas_crypto_core1", + .id = MSM8974_SNOC_MAS_CRYPTO_CORE1, + .buswidth = 8, + .mas_rpm_id = 24, + .slv_rpm_id = -1, +}; + +static const u16 mas_lpass_proc_links[] = { + MSM8974_SNOC_TO_OCMEM_VNOC +}; + +static struct qcom_icc_node mas_lpass_proc = { + .name = "mas_lpass_proc", + .id = MSM8974_SNOC_MAS_LPASS_PROC, + .buswidth = 8, + .mas_rpm_id = 25, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_lpass_proc_links), + .links = mas_lpass_proc_links, +}; + +static struct qcom_icc_node mas_mss = { + .name = "mas_mss", + .id = MSM8974_SNOC_MAS_MSS, + .buswidth = 8, + .mas_rpm_id = 26, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_mss_nav = { + .name = "mas_mss_nav", + .id = MSM8974_SNOC_MAS_MSS_NAV, + .buswidth = 8, + .mas_rpm_id = 27, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_ocmem_dma = { + .name = "mas_ocmem_dma", + .id = MSM8974_SNOC_MAS_OCMEM_DMA, + .buswidth = 8, + .mas_rpm_id = 28, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_wcss = { + .name = "mas_wcss", + .id = MSM8974_SNOC_MAS_WCSS, + .buswidth = 8, + .mas_rpm_id = 30, + .slv_rpm_id = -1, +}; + +static struct qcom_icc_node mas_qdss_etr = { + .name = "mas_qdss_etr", + .id = MSM8974_SNOC_MAS_QDSS_ETR, + .buswidth = 8, + .mas_rpm_id = 31, + .slv_rpm_id = -1, +}; + +static const u16 mas_usb3_links[] = { + MSM8974_SNOC_TO_BIMC +}; + +static struct qcom_icc_node mas_usb3 = { + .name = "mas_usb3", + .id = MSM8974_SNOC_MAS_USB3, + .buswidth = 8, + .mas_rpm_id = 32, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb3_links), + .links = mas_usb3_links, +}; + +static struct qcom_icc_node slv_ampss = { + .name = "slv_ampss", + .id = MSM8974_SNOC_SLV_AMPSS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 20, +}; + +static struct qcom_icc_node slv_lpass = { + .name = "slv_lpass", + .id = MSM8974_SNOC_SLV_LPASS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 21, +}; + +static struct qcom_icc_node slv_usb3 = { + .name = "slv_usb3", + .id = MSM8974_SNOC_SLV_USB3, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 22, +}; + +static struct qcom_icc_node slv_wcss = { + .name = "slv_wcss", + .id = MSM8974_SNOC_SLV_WCSS, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 23, +}; + +static struct qcom_icc_node slv_ocimem = { + .name = "slv_ocimem", + .id = MSM8974_SNOC_SLV_OCIMEM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 26, +}; + +static struct qcom_icc_node slv_snoc_ocmem = { + .name = "slv_snoc_ocmem", + .id = MSM8974_SNOC_SLV_SNOC_OCMEM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 27, +}; + +static struct qcom_icc_node slv_service_snoc = { + .name = "slv_service_snoc", + .id = MSM8974_SNOC_SLV_SERVICE_SNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 29, +}; + +static struct qcom_icc_node slv_qdss_stm = { + .name = "slv_qdss_stm", + .id = MSM8974_SNOC_SLV_QDSS_STM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 30, +}; + +static struct qcom_icc_node * const msm8974_snoc_nodes[] = { [SNOC_MAS_LPASS_AHB] = &mas_lpass_ahb, [SNOC_MAS_QDSS_BAM] = &mas_qdss_bam, [SNOC_MAS_SNOC_CFG] = &mas_snoc_cfg, @@ -543,209 +1567,14 @@ static struct msm8974_icc_node * const msm8974_snoc_nodes[] = { [SNOC_SLV_QDSS_STM] = &slv_qdss_stm, }; -static const struct msm8974_icc_desc msm8974_snoc = { +static const struct qcom_icc_desc msm8974_snoc = { .nodes = msm8974_snoc_nodes, .num_nodes = ARRAY_SIZE(msm8974_snoc_nodes), + .bus_clk_desc = &bus_1_clk, + .get_bw = msm8974_get_bw, + .ignore_enxio = true, }; -static void msm8974_icc_rpm_smd_send(struct device *dev, int rsc_type, - char *name, int id, u64 val) -{ - int ret; - - if (id == -1) - return; - - /* - * Setting the bandwidth requests for some nodes fails and this same - * behavior occurs on the downstream MSM 3.4 kernel sources based on - * errors like this in that kernel: - * - * msm_rpm_get_error_from_ack(): RPM NACK Unsupported resource - * AXI: msm_bus_rpm_req(): RPM: Ack failed - * AXI: msm_bus_rpm_commit_arb(): RPM: Req fail: mas:32, bw:240000000 - * - * Since there's no publicly available documentation for this hardware, - * and the bandwidth for some nodes in the path can be set properly, - * let's not return an error. - */ - ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, rsc_type, id, - val); - if (ret) - dev_dbg(dev, "Cannot set bandwidth for node %s (%d): %d\n", - name, id, ret); -} - -static int msm8974_icc_set(struct icc_node *src, struct icc_node *dst) -{ - struct msm8974_icc_node *src_qn, *dst_qn; - struct msm8974_icc_provider *qp; - u64 sum_bw, max_peak_bw, rate; - u32 agg_avg = 0, agg_peak = 0; - struct icc_provider *provider; - struct icc_node *n; - int ret, i; - - src_qn = src->data; - dst_qn = dst->data; - provider = src->provider; - qp = to_msm8974_icc_provider(provider); - - list_for_each_entry(n, &provider->nodes, node_list) - provider->aggregate(n, 0, n->avg_bw, n->peak_bw, - &agg_avg, &agg_peak); - - sum_bw = icc_units_to_bps(agg_avg); - max_peak_bw = icc_units_to_bps(agg_peak); - - /* Set bandwidth on source node */ - msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_MASTER_REQ, - src_qn->name, src_qn->mas_rpm_id, sum_bw); - - msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_SLAVE_REQ, - src_qn->name, src_qn->slv_rpm_id, sum_bw); - - /* Set bandwidth on destination node */ - msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_MASTER_REQ, - dst_qn->name, dst_qn->mas_rpm_id, sum_bw); - - msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_SLAVE_REQ, - dst_qn->name, dst_qn->slv_rpm_id, sum_bw); - - rate = max(sum_bw, max_peak_bw); - - do_div(rate, src_qn->buswidth); - - rate = min_t(u32, rate, INT_MAX); - - if (src_qn->rate == rate) - return 0; - - for (i = 0; i < qp->num_clks; i++) { - ret = clk_set_rate(qp->bus_clks[i].clk, rate); - if (ret) { - dev_err(provider->dev, "%s clk_set_rate error: %d\n", - qp->bus_clks[i].id, ret); - ret = 0; - } - } - - src_qn->rate = rate; - - return 0; -} - -static int msm8974_get_bw(struct icc_node *node, u32 *avg, u32 *peak) -{ - *avg = 0; - *peak = 0; - - return 0; -} - -static int msm8974_icc_probe(struct platform_device *pdev) -{ - const struct msm8974_icc_desc *desc; - struct msm8974_icc_node * const *qnodes; - struct msm8974_icc_provider *qp; - struct device *dev = &pdev->dev; - struct icc_onecell_data *data; - struct icc_provider *provider; - struct icc_node *node; - size_t num_nodes, i; - int ret; - - /* wait for the RPM proxy */ - if (!qcom_icc_rpm_smd_available()) - return -EPROBE_DEFER; - - desc = of_device_get_match_data(dev); - if (!desc) - return -EINVAL; - - qnodes = desc->nodes; - num_nodes = desc->num_nodes; - - qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); - if (!qp) - return -ENOMEM; - - data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), - GFP_KERNEL); - if (!data) - return -ENOMEM; - data->num_nodes = num_nodes; - - qp->bus_clks = devm_kmemdup(dev, msm8974_icc_bus_clocks, - sizeof(msm8974_icc_bus_clocks), GFP_KERNEL); - if (!qp->bus_clks) - return -ENOMEM; - - qp->num_clks = ARRAY_SIZE(msm8974_icc_bus_clocks); - ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks); - if (ret) - return ret; - - ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks); - if (ret) - return ret; - - provider = &qp->provider; - provider->dev = dev; - provider->set = msm8974_icc_set; - provider->aggregate = icc_std_aggregate; - provider->xlate = of_icc_xlate_onecell; - provider->data = data; - provider->get_bw = msm8974_get_bw; - - icc_provider_init(provider); - - for (i = 0; i < num_nodes; i++) { - size_t j; - - node = icc_node_create(qnodes[i]->id); - if (IS_ERR(node)) { - ret = PTR_ERR(node); - goto err_remove_nodes; - } - - node->name = qnodes[i]->name; - node->data = qnodes[i]; - icc_node_add(node, provider); - - dev_dbg(dev, "registered node %s\n", node->name); - - /* populate links */ - for (j = 0; j < qnodes[i]->num_links; j++) - icc_link_create(node, qnodes[i]->links[j]); - - data->nodes[i] = node; - } - - ret = icc_provider_register(provider); - if (ret) - goto err_remove_nodes; - - platform_set_drvdata(pdev, qp); - - return 0; - -err_remove_nodes: - icc_nodes_remove(provider); - clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); - - return ret; -} - -static void msm8974_icc_remove(struct platform_device *pdev) -{ - struct msm8974_icc_provider *qp = platform_get_drvdata(pdev); - - icc_provider_deregister(&qp->provider); - icc_nodes_remove(&qp->provider); - clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); -} - static const struct of_device_id msm8974_noc_of_match[] = { { .compatible = "qcom,msm8974-bimc", .data = &msm8974_bimc}, { .compatible = "qcom,msm8974-cnoc", .data = &msm8974_cnoc}, @@ -758,8 +1587,8 @@ static const struct of_device_id msm8974_noc_of_match[] = { MODULE_DEVICE_TABLE(of, msm8974_noc_of_match); static struct platform_driver msm8974_noc_driver = { - .probe = msm8974_icc_probe, - .remove = msm8974_icc_remove, + .probe = qnoc_probe, + .remove = qnoc_remove, .driver = { .name = "qnoc-msm8974", .of_match_table = msm8974_noc_of_match, diff --git a/drivers/interconnect/qcom/qcs615.c b/drivers/interconnect/qcom/qcs615.c index 797956eb6ff5..017a6017421f 100644 --- a/drivers/interconnect/qcom/qcs615.c +++ b/drivers/interconnect/qcom/qcs615.c @@ -142,6 +142,12 @@ static struct qcom_icc_node qhm_qdss_bam = { .name = "qhm_qdss_bam", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -150,6 +156,12 @@ static struct qcom_icc_node qhm_qspi = { .name = "qhm_qspi", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -158,6 +170,12 @@ static struct qcom_icc_node qhm_qup0 = { .name = "qhm_qup0", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -166,6 +184,12 @@ static struct qcom_icc_node qhm_qup1 = { .name = "qhm_qup1", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -174,6 +198,12 @@ static struct qcom_icc_node qnm_cnoc = { .name = "qnm_cnoc", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x4000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -182,6 +212,12 @@ static struct qcom_icc_node qxm_crypto = { .name = "qxm_crypto", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x5000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -190,6 +226,12 @@ static struct qcom_icc_node qxm_ipa = { .name = "qxm_ipa", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x6000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_lpass_snoc }, }; @@ -198,6 +240,12 @@ static struct qcom_icc_node xm_emac_avb = { .name = "xm_emac_avb", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -206,6 +254,12 @@ static struct qcom_icc_node xm_pcie = { .name = "xm_pcie", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio = 0, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_pcie_snoc }, }; @@ -214,6 +268,12 @@ static struct qcom_icc_node xm_qdss_etr = { .name = "xm_qdss_etr", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -222,6 +282,12 @@ static struct qcom_icc_node xm_sdc1 = { .name = "xm_sdc1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -230,6 +296,12 @@ static struct qcom_icc_node xm_sdc2 = { .name = "xm_sdc2", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -238,6 +310,12 @@ static struct qcom_icc_node xm_ufs_mem = { .name = "xm_ufs_mem", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -246,6 +324,12 @@ static struct qcom_icc_node xm_usb2 = { .name = "xm_usb2", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -254,6 +338,12 @@ static struct qcom_icc_node xm_usb3_0 = { .name = "xm_usb3_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -356,6 +446,12 @@ static struct qcom_icc_node acm_apps = { .name = "acm_apps", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x2e000, 0x2e100 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_snoc, &qns_llcc, &qns_sys_pcie }, @@ -365,6 +461,12 @@ static struct qcom_icc_node acm_gpu_tcu = { .name = "acm_gpu_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x36000 }, + .prio = 6, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_snoc, &qns_llcc }, }; @@ -373,6 +475,12 @@ static struct qcom_icc_node acm_sys_tcu = { .name = "acm_sys_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x37000 }, + .prio = 6, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_snoc, &qns_llcc }, }; @@ -389,6 +497,12 @@ static struct qcom_icc_node qnm_gpu = { .name = "qnm_gpu", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0x34000, 0x34080 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_snoc, &qns_llcc }, }; @@ -397,6 +511,12 @@ static struct qcom_icc_node qnm_mnoc_hf = { .name = "qnm_mnoc_hf", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2f000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -405,6 +525,12 @@ static struct qcom_icc_node qnm_mnoc_sf = { .name = "qnm_mnoc_sf", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x35000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_snoc, &qns_llcc }, }; @@ -413,6 +539,12 @@ static struct qcom_icc_node qnm_snoc_gc = { .name = "qnm_snoc_gc", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x31000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -421,6 +553,12 @@ static struct qcom_icc_node qnm_snoc_sf = { .name = "qnm_snoc_sf", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x30000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -445,6 +583,12 @@ static struct qcom_icc_node qxm_camnoc_hf0 = { .name = "qxm_camnoc_hf0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -453,6 +597,12 @@ static struct qcom_icc_node qxm_camnoc_hf1 = { .name = "qxm_camnoc_hf1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -461,6 +611,12 @@ static struct qcom_icc_node qxm_camnoc_sf = { .name = "qxm_camnoc_sf", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x9000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns2_mem_noc }, }; @@ -469,6 +625,12 @@ static struct qcom_icc_node qxm_mdp0 = { .name = "qxm_mdp0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -477,6 +639,12 @@ static struct qcom_icc_node qxm_rot = { .name = "qxm_rot", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xe000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns2_mem_noc }, }; @@ -485,6 +653,12 @@ static struct qcom_icc_node qxm_venus0 = { .name = "qxm_venus0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xf000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns2_mem_noc }, }; @@ -493,6 +667,12 @@ static struct qcom_icc_node qxm_venus_arm9 = { .name = "qxm_venus_arm9", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns2_mem_noc }, }; @@ -559,6 +739,12 @@ static struct qcom_icc_node qxm_pimem = { .name = "qxm_pimem", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_memnoc_gc, &qxs_imem }, }; @@ -567,6 +753,12 @@ static struct qcom_icc_node xm_gic = { .name = "xm_gic", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xd000 }, + .prio = 2, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_memnoc_gc, &qxs_imem }, }; @@ -1213,11 +1405,21 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = { [SLAVE_SERVICE_A2NOC] = &srvc_aggre2_noc, }; +static const struct regmap_config qcs615_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3f200, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs615_aggre1_noc = { + .config = &qcs615_aggre1_noc_regmap_config, .nodes = aggre1_noc_nodes, .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), .bcms = aggre1_noc_bcms, .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const camnoc_virt_bcms[] = { @@ -1289,7 +1491,16 @@ static struct qcom_icc_node * const config_noc_nodes[] = { [SLAVE_SERVICE_CNOC] = &srvc_cnoc, }; +static const struct regmap_config qcs615_config_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs615_config_noc = { + .config = &qcs615_config_noc_regmap_config, .nodes = config_noc_nodes, .num_nodes = ARRAY_SIZE(config_noc_nodes), .bcms = config_noc_bcms, @@ -1302,7 +1513,16 @@ static struct qcom_icc_node * const dc_noc_nodes[] = { [SLAVE_LLCC_CFG] = &qhs_llcc, }; +static const struct regmap_config qcs615_dc_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3200, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs615_dc_noc = { + .config = &qcs615_dc_noc_regmap_config, .nodes = dc_noc_nodes, .num_nodes = ARRAY_SIZE(dc_noc_nodes), }; @@ -1331,7 +1551,16 @@ static struct qcom_icc_node * const gem_noc_nodes[] = { [SLAVE_SERVICE_GEM_NOC] = &srvc_gemnoc, }; +static const struct regmap_config qcs615_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3e200, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs615_gem_noc = { + .config = &qcs615_gem_noc_regmap_config, .nodes = gem_noc_nodes, .num_nodes = ARRAY_SIZE(gem_noc_nodes), .bcms = gem_noc_bcms, @@ -1376,7 +1605,16 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = { [SLAVE_SERVICE_MNOC] = &srvc_mnoc, }; +static const struct regmap_config qcs615_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1c100, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs615_mmss_noc = { + .config = &qcs615_mmss_noc_regmap_config, .nodes = mmss_noc_nodes, .num_nodes = ARRAY_SIZE(mmss_noc_nodes), .bcms = mmss_noc_bcms, @@ -1418,7 +1656,16 @@ static struct qcom_icc_node * const system_noc_nodes[] = { [SLAVE_TCU] = &xs_sys_tcu_cfg, }; +static const struct regmap_config qcs615_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1f300, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs615_system_noc = { + .config = &qcs615_system_noc_regmap_config, .nodes = system_noc_nodes, .num_nodes = ARRAY_SIZE(system_noc_nodes), .bcms = system_noc_bcms, diff --git a/drivers/interconnect/qcom/qcs8300.c b/drivers/interconnect/qcom/qcs8300.c index bc403a9bf68c..ebf167182572 100644 --- a/drivers/interconnect/qcom/qcs8300.c +++ b/drivers/interconnect/qcom/qcs8300.c @@ -186,6 +186,13 @@ static struct qcom_icc_node qxm_qup3 = { .name = "qxm_qup3", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -194,6 +201,13 @@ static struct qcom_icc_node xm_emac_0 = { .name = "xm_emac_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -202,6 +216,13 @@ static struct qcom_icc_node xm_sdc1 = { .name = "xm_sdc1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -210,6 +231,13 @@ static struct qcom_icc_node xm_ufs_mem = { .name = "xm_ufs_mem", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -218,6 +246,13 @@ static struct qcom_icc_node xm_usb2_2 = { .name = "xm_usb2_2", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -226,6 +261,13 @@ static struct qcom_icc_node xm_usb3_0 = { .name = "xm_usb3_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a1noc_snoc }, }; @@ -234,6 +276,13 @@ static struct qcom_icc_node qhm_qdss_bam = { .name = "qhm_qdss_bam", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -242,6 +291,13 @@ static struct qcom_icc_node qhm_qup0 = { .name = "qhm_qup0", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -250,6 +306,13 @@ static struct qcom_icc_node qhm_qup1 = { .name = "qhm_qup1", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -258,6 +321,13 @@ static struct qcom_icc_node qnm_cnoc_datapath = { .name = "qnm_cnoc_datapath", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -266,6 +336,13 @@ static struct qcom_icc_node qxm_crypto_0 = { .name = "qxm_crypto_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x18000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -274,6 +351,13 @@ static struct qcom_icc_node qxm_crypto_1 = { .name = "qxm_crypto_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x1a000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -282,6 +366,13 @@ static struct qcom_icc_node qxm_ipa = { .name = "qxm_ipa", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x11000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -290,6 +381,13 @@ static struct qcom_icc_node xm_qdss_etr_0 = { .name = "xm_qdss_etr_0", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -298,6 +396,13 @@ static struct qcom_icc_node xm_qdss_etr_1 = { .name = "xm_qdss_etr_1", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x19000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_a2noc_snoc }, }; @@ -390,6 +495,13 @@ static struct qcom_icc_node alm_gpu_tcu = { .name = "alm_gpu_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xaf000 }, + .prio_fwd_disable = 1, + .prio = 1, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -398,6 +510,13 @@ static struct qcom_icc_node alm_pcie_tcu = { .name = "alm_pcie_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb0000 }, + .prio_fwd_disable = 1, + .prio = 3, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -406,6 +525,13 @@ static struct qcom_icc_node alm_sys_tcu = { .name = "alm_sys_tcu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb1000 }, + .prio_fwd_disable = 1, + .prio = 6, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -423,6 +549,13 @@ static struct qcom_icc_node qnm_cmpnoc0 = { .name = "qnm_cmpnoc0", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0xf6000, 0xf7000 }, + .prio_fwd_disable = 1, + .prio = 0, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -448,6 +581,13 @@ static struct qcom_icc_node qnm_gpu = { .name = "qnm_gpu", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0xf0000, 0xf1000 }, + .prio_fwd_disable = 1, + .prio = 0, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -456,6 +596,13 @@ static struct qcom_icc_node qnm_mnoc_hf = { .name = "qnm_mnoc_hf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0xf2000, 0xf3000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 2, .link_nodes = { &qns_llcc, &qns_pcie }, }; @@ -464,6 +611,13 @@ static struct qcom_icc_node qnm_mnoc_sf = { .name = "qnm_mnoc_sf", .channels = 2, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 2, + .port_offsets = { 0xf4000, 0xf5000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -473,6 +627,13 @@ static struct qcom_icc_node qnm_pcie = { .name = "qnm_pcie", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb3000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 2, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc }, }; @@ -481,6 +642,13 @@ static struct qcom_icc_node qnm_snoc_gc = { .name = "qnm_snoc_gc", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb4000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_llcc }, }; @@ -489,6 +657,13 @@ static struct qcom_icc_node qnm_snoc_sf = { .name = "qnm_snoc_sf", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb5000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 3, .link_nodes = { &qns_gem_noc_cnoc, &qns_llcc, &qns_pcie }, @@ -541,6 +716,13 @@ static struct qcom_icc_node qnm_camnoc_hf = { .name = "qnm_camnoc_hf", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -549,6 +731,13 @@ static struct qcom_icc_node qnm_camnoc_icp = { .name = "qnm_camnoc_icp", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2a000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -557,6 +746,13 @@ static struct qcom_icc_node qnm_camnoc_sf = { .name = "qnm_camnoc_sf", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2a080 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -565,6 +761,13 @@ static struct qcom_icc_node qnm_mdp0_0 = { .name = "qnm_mdp0_0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa080 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -573,6 +776,13 @@ static struct qcom_icc_node qnm_mdp0_1 = { .name = "qnm_mdp0_1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xa180 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_hf }, }; @@ -597,6 +807,13 @@ static struct qcom_icc_node qnm_video0 = { .name = "qnm_video0", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2a100 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -605,6 +822,13 @@ static struct qcom_icc_node qnm_video_cvp = { .name = "qnm_video_cvp", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2a200 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -613,6 +837,13 @@ static struct qcom_icc_node qnm_video_v_cpu = { .name = "qnm_video_v_cpu", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x2a280 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_mem_noc_sf }, }; @@ -637,6 +868,13 @@ static struct qcom_icc_node xm_pcie3_0 = { .name = "xm_pcie3_0", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_pcie_mem_noc }, }; @@ -645,6 +883,13 @@ static struct qcom_icc_node xm_pcie3_1 = { .name = "xm_pcie3_1", .channels = 1, .buswidth = 32, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_pcie_mem_noc }, }; @@ -653,6 +898,13 @@ static struct qcom_icc_node qhm_gic = { .name = "qhm_gic", .channels = 1, .buswidth = 4, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_sf }, }; @@ -677,6 +929,13 @@ static struct qcom_icc_node qnm_lpass_noc = { .name = "qnm_lpass_noc", .channels = 1, .buswidth = 16, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio_fwd_disable = 0, + .prio = 0, + .urg_fwd = 1, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_sf }, }; @@ -693,6 +952,13 @@ static struct qcom_icc_node qxm_pimem = { .name = "qxm_pimem", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_gc }, }; @@ -701,6 +967,13 @@ static struct qcom_icc_node xm_gic = { .name = "xm_gic", .channels = 1, .buswidth = 8, + .qosbox = &(const struct qcom_icc_qosbox) { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio_fwd_disable = 1, + .prio = 2, + .urg_fwd = 0, + }, .num_links = 1, .link_nodes = { &qns_gemnoc_gc }, }; @@ -1599,11 +1872,21 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = { [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, }; +static const struct regmap_config qcs8300_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x17080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_aggre1_noc = { + .config = &qcs8300_aggre1_noc_regmap_config, .nodes = aggre1_noc_nodes, .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), .bcms = aggre1_noc_bcms, .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { @@ -1624,11 +1907,21 @@ static struct qcom_icc_node * const aggre2_noc_nodes[] = { [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, }; +static const struct regmap_config qcs8300_aggre2_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1a080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_aggre2_noc = { + .config = &qcs8300_aggre2_noc_regmap_config, .nodes = aggre2_noc_nodes, .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), .bcms = aggre2_noc_bcms, .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const clk_virt_bcms[] = { @@ -1740,7 +2033,16 @@ static struct qcom_icc_node * const config_noc_nodes[] = { [SLAVE_TCU] = &xs_sys_tcu_cfg, }; +static const struct regmap_config qcs8300_config_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x13080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_config_noc = { + .config = &qcs8300_config_noc_regmap_config, .nodes = config_noc_nodes, .num_nodes = ARRAY_SIZE(config_noc_nodes), .bcms = config_noc_bcms, @@ -1753,7 +2055,16 @@ static struct qcom_icc_node * const dc_noc_nodes[] = { [SLAVE_GEM_NOC_CFG] = &qns_gemnoc, }; +static const struct regmap_config qcs8300_dc_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_dc_noc = { + .config = &qcs8300_dc_noc_regmap_config, .nodes = dc_noc_nodes, .num_nodes = ARRAY_SIZE(dc_noc_nodes), }; @@ -1786,11 +2097,21 @@ static struct qcom_icc_node * const gem_noc_nodes[] = { [SLAVE_SERVICE_GEM_NOC2] = &srvc_sys_gemnoc_2, }; +static const struct regmap_config qcs8300_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xf7080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_gem_noc = { + .config = &qcs8300_gem_noc_regmap_config, .nodes = gem_noc_nodes, .num_nodes = ARRAY_SIZE(gem_noc_nodes), .bcms = gem_noc_bcms, .num_bcms = ARRAY_SIZE(gem_noc_bcms), + .qos_requires_clocks = true, }; static struct qcom_icc_bcm * const gpdsp_anoc_bcms[] = { @@ -1803,7 +2124,16 @@ static struct qcom_icc_node * const gpdsp_anoc_nodes[] = { [SLAVE_GP_DSP_SAIL_NOC] = &qns_gp_dsp_sail_noc, }; +static const struct regmap_config qcs8300_gpdsp_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xd080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_gpdsp_anoc = { + .config = &qcs8300_gpdsp_anoc_regmap_config, .nodes = gpdsp_anoc_nodes, .num_nodes = ARRAY_SIZE(gpdsp_anoc_nodes), .bcms = gpdsp_anoc_bcms, @@ -1826,7 +2156,16 @@ static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { [SLAVE_SERVICE_LPASS_AG_NOC] = &srvc_niu_lpass_agnoc, }; +static const struct regmap_config qcs8300_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x17200, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_lpass_ag_noc = { + .config = &qcs8300_lpass_ag_noc_regmap_config, .nodes = lpass_ag_noc_nodes, .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), .bcms = lpass_ag_noc_bcms, @@ -1872,7 +2211,16 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = { [SLAVE_SERVICE_MNOC_SF] = &srvc_mnoc_sf, }; +static const struct regmap_config qcs8300_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x40000, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_mmss_noc = { + .config = &qcs8300_mmss_noc_regmap_config, .nodes = mmss_noc_nodes, .num_nodes = ARRAY_SIZE(mmss_noc_nodes), .bcms = mmss_noc_bcms, @@ -1892,7 +2240,16 @@ static struct qcom_icc_node * const nspa_noc_nodes[] = { [SLAVE_SERVICE_NSP_NOC] = &service_nsp_noc, }; +static const struct regmap_config qcs8300_nspa_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x16080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_nspa_noc = { + .config = &qcs8300_nspa_noc_regmap_config, .nodes = nspa_noc_nodes, .num_nodes = ARRAY_SIZE(nspa_noc_nodes), .bcms = nspa_noc_bcms, @@ -1909,7 +2266,16 @@ static struct qcom_icc_node * const pcie_anoc_nodes[] = { [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, }; +static const struct regmap_config qcs8300_pcie_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xc080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_pcie_anoc = { + .config = &qcs8300_pcie_anoc_regmap_config, .nodes = pcie_anoc_nodes, .num_nodes = ARRAY_SIZE(pcie_anoc_nodes), .bcms = pcie_anoc_bcms, @@ -1937,7 +2303,16 @@ static struct qcom_icc_node * const system_noc_nodes[] = { [SLAVE_SERVICE_SNOC] = &srvc_snoc, }; +static const struct regmap_config qcs8300_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x15080, + .fast_io = true, +}; + static const struct qcom_icc_desc qcs8300_system_noc = { + .config = &qcs8300_system_noc_regmap_config, .nodes = system_noc_nodes, .num_nodes = ARRAY_SIZE(system_noc_nodes), .bcms = system_noc_bcms, diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h index 1342e764a548..834d8fabfba3 100644 --- a/drivers/iommu/amd/amd_iommu.h +++ b/drivers/iommu/amd/amd_iommu.h @@ -11,6 +11,9 @@ #include "amd_iommu_types.h" +extern int amd_iommu_evtlog_size; +extern int amd_iommu_pprlog_size; + irqreturn_t amd_iommu_int_thread(int irq, void *data); irqreturn_t amd_iommu_int_thread_evtlog(int irq, void *data); irqreturn_t amd_iommu_int_thread_pprlog(int irq, void *data); diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h index c685d3771436..f9f718087893 100644 --- a/drivers/iommu/amd/amd_iommu_types.h +++ b/drivers/iommu/amd/amd_iommu_types.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -141,7 +142,6 @@ #define MMIO_STATUS_GALOG_INT_MASK BIT(10) /* event logging constants */ -#define EVENT_ENTRY_SIZE 0x10 #define EVENT_TYPE_SHIFT 28 #define EVENT_TYPE_MASK 0xf #define EVENT_TYPE_ILL_DEV 0x1 @@ -259,15 +259,20 @@ #define MMIO_CMD_BUFFER_TAIL(x) FIELD_GET(MMIO_CMD_TAIL_MASK, (x)) /* constants for event buffer handling */ -#define EVT_BUFFER_SIZE 8192 /* 512 entries */ -#define EVT_LEN_MASK (0x9ULL << 56) +#define EVTLOG_ENTRY_SIZE 0x10 +#define EVTLOG_SIZE_SHIFT 56 +#define EVTLOG_SIZE_DEF SZ_8K /* 512 entries */ +#define EVTLOG_LEN_MASK_DEF (0x9ULL << EVTLOG_SIZE_SHIFT) +#define EVTLOG_SIZE_MAX SZ_512K /* 32K entries */ +#define EVTLOG_LEN_MASK_MAX (0xFULL << EVTLOG_SIZE_SHIFT) /* Constants for PPR Log handling */ -#define PPR_LOG_ENTRIES 512 -#define PPR_LOG_SIZE_SHIFT 56 -#define PPR_LOG_SIZE_512 (0x9ULL << PPR_LOG_SIZE_SHIFT) -#define PPR_ENTRY_SIZE 16 -#define PPR_LOG_SIZE (PPR_ENTRY_SIZE * PPR_LOG_ENTRIES) +#define PPRLOG_ENTRY_SIZE 0x10 +#define PPRLOG_SIZE_SHIFT 56 +#define PPRLOG_SIZE_DEF SZ_8K /* 512 entries */ +#define PPRLOG_LEN_MASK_DEF (0x9ULL << PPRLOG_SIZE_SHIFT) +#define PPRLOG_SIZE_MAX SZ_512K /* 32K entries */ +#define PPRLOG_LEN_MASK_MAX (0xFULL << PPRLOG_SIZE_SHIFT) /* PAGE_SERVICE_REQUEST PPR Log Buffer Entry flags */ #define PPR_FLAG_EXEC 0x002 /* Execute permission requested */ diff --git a/drivers/iommu/amd/debugfs.c b/drivers/iommu/amd/debugfs.c index 4e66473d7cea..4c53b6361314 100644 --- a/drivers/iommu/amd/debugfs.c +++ b/drivers/iommu/amd/debugfs.c @@ -31,11 +31,12 @@ static ssize_t iommu_mmio_write(struct file *filp, const char __user *ubuf, if (cnt > OFS_IN_SZ) return -EINVAL; - ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_mmio_offset); + ret = kstrtos32_from_user(ubuf, cnt, 0, &dbg_mmio_offset); if (ret) return ret; - if (dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64)) + if (dbg_mmio_offset < 0 || dbg_mmio_offset > + iommu->mmio_phys_end - sizeof(u64)) return -EINVAL; iommu->dbg_mmio_offset = dbg_mmio_offset; @@ -71,12 +72,12 @@ static ssize_t iommu_capability_write(struct file *filp, const char __user *ubuf if (cnt > OFS_IN_SZ) return -EINVAL; - ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_cap_offset); + ret = kstrtos32_from_user(ubuf, cnt, 0, &dbg_cap_offset); if (ret) return ret; /* Capability register at offset 0x14 is the last IOMMU capability register. */ - if (dbg_cap_offset > 0x14) + if (dbg_cap_offset < 0 || dbg_cap_offset > 0x14) return -EINVAL; iommu->dbg_cap_offset = dbg_cap_offset; diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index 56ad020df494..3bdb380d23e9 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -132,6 +132,9 @@ struct ivhd_entry { u8 uid; } __attribute__((packed)); +int amd_iommu_evtlog_size = EVTLOG_SIZE_DEF; +int amd_iommu_pprlog_size = PPRLOG_SIZE_DEF; + /* * An AMD IOMMU memory definition structure. It defines things like exclusion * ranges for devices and regions that should be unity mapped. @@ -865,35 +868,47 @@ void *__init iommu_alloc_4k_pages(struct amd_iommu *iommu, gfp_t gfp, } /* allocates the memory where the IOMMU will log its events to */ -static int __init alloc_event_buffer(struct amd_iommu *iommu) +static int __init alloc_event_buffer(void) { - iommu->evt_buf = iommu_alloc_4k_pages(iommu, GFP_KERNEL, - EVT_BUFFER_SIZE); + struct amd_iommu *iommu; - return iommu->evt_buf ? 0 : -ENOMEM; -} - -static void iommu_enable_event_buffer(struct amd_iommu *iommu) -{ - u64 entry; - - BUG_ON(iommu->evt_buf == NULL); - - if (!is_kdump_kernel()) { - /* - * Event buffer is re-used for kdump kernel and setting - * of MMIO register is not required. - */ - entry = iommu_virt_to_phys(iommu->evt_buf) | EVT_LEN_MASK; - memcpy_toio(iommu->mmio_base + MMIO_EVT_BUF_OFFSET, - &entry, sizeof(entry)); + for_each_iommu(iommu) { + iommu->evt_buf = iommu_alloc_4k_pages(iommu, GFP_KERNEL, + amd_iommu_evtlog_size); + if (!iommu->evt_buf) + return -ENOMEM; } - /* set head and tail to zero manually */ - writel(0x00, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET); - writel(0x00, iommu->mmio_base + MMIO_EVT_TAIL_OFFSET); + return 0; +} - iommu_feature_enable(iommu, CONTROL_EVT_LOG_EN); +static void iommu_enable_event_buffer(void) +{ + struct amd_iommu *iommu; + u64 entry; + + for_each_iommu(iommu) { + BUG_ON(iommu->evt_buf == NULL); + + if (!is_kdump_kernel()) { + /* + * Event buffer is re-used for kdump kernel and setting + * of MMIO register is not required. + */ + entry = iommu_virt_to_phys(iommu->evt_buf); + entry |= (amd_iommu_evtlog_size == EVTLOG_SIZE_DEF) ? + EVTLOG_LEN_MASK_DEF : EVTLOG_LEN_MASK_MAX; + + memcpy_toio(iommu->mmio_base + MMIO_EVT_BUF_OFFSET, + &entry, sizeof(entry)); + } + + /* set head and tail to zero manually */ + writel(0x00, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET); + writel(0x00, iommu->mmio_base + MMIO_EVT_TAIL_OFFSET); + + iommu_feature_enable(iommu, CONTROL_EVT_LOG_EN); + } } /* @@ -984,15 +999,20 @@ static int __init alloc_cwwb_sem(struct amd_iommu *iommu) return 0; } -static int __init remap_event_buffer(struct amd_iommu *iommu) +static int __init remap_event_buffer(void) { + struct amd_iommu *iommu; u64 paddr; pr_info_once("Re-using event buffer from the previous kernel\n"); - paddr = readq(iommu->mmio_base + MMIO_EVT_BUF_OFFSET) & PM_ADDR_MASK; - iommu->evt_buf = iommu_memremap(paddr, EVT_BUFFER_SIZE); + for_each_iommu(iommu) { + paddr = readq(iommu->mmio_base + MMIO_EVT_BUF_OFFSET) & PM_ADDR_MASK; + iommu->evt_buf = iommu_memremap(paddr, amd_iommu_evtlog_size); + if (!iommu->evt_buf) + return -ENOMEM; + } - return iommu->evt_buf ? 0 : -ENOMEM; + return 0; } static int __init remap_command_buffer(struct amd_iommu *iommu) @@ -1044,10 +1064,6 @@ static int __init alloc_iommu_buffers(struct amd_iommu *iommu) ret = remap_command_buffer(iommu); if (ret) return ret; - - ret = remap_event_buffer(iommu); - if (ret) - return ret; } else { ret = alloc_cwwb_sem(iommu); if (ret) @@ -1056,10 +1072,6 @@ static int __init alloc_iommu_buffers(struct amd_iommu *iommu) ret = alloc_command_buffer(iommu); if (ret) return ret; - - ret = alloc_event_buffer(iommu); - if (ret) - return ret; } return 0; @@ -2893,7 +2905,6 @@ static void early_enable_iommu(struct amd_iommu *iommu) iommu_init_flags(iommu); iommu_set_device_table(iommu); iommu_enable_command_buffer(iommu); - iommu_enable_event_buffer(iommu); iommu_set_exclusion_range(iommu); iommu_enable_gt(iommu); iommu_enable_ga(iommu); @@ -2957,7 +2968,6 @@ static void early_enable_iommus(void) iommu_disable_event_buffer(iommu); iommu_disable_irtcachedis(iommu); iommu_enable_command_buffer(iommu); - iommu_enable_event_buffer(iommu); iommu_enable_ga(iommu); iommu_enable_xt(iommu); iommu_enable_irtcachedis(iommu); @@ -3070,6 +3080,7 @@ static void amd_iommu_resume(void *data) for_each_iommu(iommu) early_enable_iommu(iommu); + iommu_enable_event_buffer(); amd_iommu_enable_interrupts(); } @@ -3399,6 +3410,33 @@ disable_snp: #endif } +static void amd_iommu_apply_erratum_snp(void) +{ +#ifdef CONFIG_KVM_AMD_SEV + if (!amd_iommu_snp_en) + return; + + /* Errata fix for Family 0x19 */ + if (boot_cpu_data.x86 != 0x19) + return; + + /* Set event log buffer size to max */ + amd_iommu_evtlog_size = EVTLOG_SIZE_MAX; + pr_info("Applying erratum: Increase Event log size to 0x%x\n", + amd_iommu_evtlog_size); + + /* + * Set PPR log buffer size to max. + * (Family 0x19, model < 0x10 doesn't support PPR when SNP is enabled). + */ + if (boot_cpu_data.x86_model >= 0x10) { + amd_iommu_pprlog_size = PPRLOG_SIZE_MAX; + pr_info("Applying erratum: Increase PPR log size to 0x%x\n", + amd_iommu_pprlog_size); + } +#endif +} + /**************************************************************************** * * AMD IOMMU Initialization State Machine @@ -3435,6 +3473,21 @@ static int __init state_next(void) case IOMMU_ENABLED: register_syscore(&amd_iommu_syscore); iommu_snp_enable(); + + amd_iommu_apply_erratum_snp(); + + /* Allocate/enable event log buffer */ + if (is_kdump_kernel()) + ret = remap_event_buffer(); + else + ret = alloc_event_buffer(); + + if (ret) { + init_state = IOMMU_INIT_ERROR; + break; + } + iommu_enable_event_buffer(); + ret = amd_iommu_init_pci(); init_state = ret ? IOMMU_INIT_ERROR : IOMMU_PCI_INIT; break; @@ -4037,11 +4090,11 @@ int amd_iommu_snp_disable(void) return 0; for_each_iommu(iommu) { - ret = iommu_make_shared(iommu->evt_buf, EVT_BUFFER_SIZE); + ret = iommu_make_shared(iommu->evt_buf, amd_iommu_evtlog_size); if (ret) return ret; - ret = iommu_make_shared(iommu->ppr_log, PPR_LOG_SIZE); + ret = iommu_make_shared(iommu->ppr_log, amd_iommu_pprlog_size); if (ret) return ret; diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 01171361f9bc..57dc8fabc7d9 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -351,8 +351,12 @@ static struct amd_iommu *__rlookup_amd_iommu(u16 seg, u16 devid) struct amd_iommu_pci_seg *pci_seg; for_each_pci_segment(pci_seg) { - if (pci_seg->id == seg) - return pci_seg->rlookup_table[devid]; + if (pci_seg->id != seg) + continue; + /* IVRS may not describe every device on the bus */ + if (devid > pci_seg->last_bdf) + return NULL; + return pci_seg->rlookup_table[devid]; } return NULL; } @@ -1010,7 +1014,7 @@ static void iommu_poll_events(struct amd_iommu *iommu) iommu_print_event(iommu, iommu->evt_buf + head); /* Update head pointer of hardware ring-buffer */ - head = (head + EVENT_ENTRY_SIZE) % EVT_BUFFER_SIZE; + head = (head + EVTLOG_ENTRY_SIZE) % amd_iommu_evtlog_size; writel(head, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET); } @@ -2149,7 +2153,8 @@ static void set_dte_passthrough(struct iommu_dev_data *dev_data, new->data[0] |= DTE_FLAG_TV | DTE_FLAG_IR | DTE_FLAG_IW; new->data[1] |= FIELD_PREP(DTE_DOMID_MASK, domain->id) | - (dev_data->ats_enabled) ? DTE_FLAG_IOTLB : 0; + (dev_data->ats_enabled ? DTE_FLAG_IOTLB : 0); + } static void set_dte_entry(struct amd_iommu *iommu, diff --git a/drivers/iommu/amd/ppr.c b/drivers/iommu/amd/ppr.c index e6767c057d01..1f8d2823bea4 100644 --- a/drivers/iommu/amd/ppr.c +++ b/drivers/iommu/amd/ppr.c @@ -20,7 +20,7 @@ int __init amd_iommu_alloc_ppr_log(struct amd_iommu *iommu) { iommu->ppr_log = iommu_alloc_4k_pages(iommu, GFP_KERNEL | __GFP_ZERO, - PPR_LOG_SIZE); + amd_iommu_pprlog_size); return iommu->ppr_log ? 0 : -ENOMEM; } @@ -33,7 +33,9 @@ void amd_iommu_enable_ppr_log(struct amd_iommu *iommu) iommu_feature_enable(iommu, CONTROL_PPR_EN); - entry = iommu_virt_to_phys(iommu->ppr_log) | PPR_LOG_SIZE_512; + entry = iommu_virt_to_phys(iommu->ppr_log); + entry |= (amd_iommu_pprlog_size == PPRLOG_SIZE_DEF) ? + PPRLOG_LEN_MASK_DEF : PPRLOG_LEN_MASK_MAX; memcpy_toio(iommu->mmio_base + MMIO_PPR_LOG_OFFSET, &entry, sizeof(entry)); @@ -201,7 +203,7 @@ void amd_iommu_poll_ppr_log(struct amd_iommu *iommu) raw[0] = raw[1] = 0UL; /* Update head pointer of hardware ring-buffer */ - head = (head + PPR_ENTRY_SIZE) % PPR_LOG_SIZE; + head = (head + PPRLOG_ENTRY_SIZE) % amd_iommu_pprlog_size; writel(head, iommu->mmio_base + MMIO_PPR_HEAD_OFFSET); /* Handle PPR entry */ diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 095235334eaf..54d96e847f16 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -1106,8 +1106,10 @@ void iommu_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, return; phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dma_handle); - if (!dev_is_dma_coherent(dev)) + if (!dev_is_dma_coherent(dev)) { arch_sync_dma_for_cpu(phys, size, dir); + arch_sync_dma_flush(); + } swiotlb_sync_single_for_cpu(dev, phys, size, dir); } @@ -1123,8 +1125,10 @@ void iommu_dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dma_handle); swiotlb_sync_single_for_device(dev, phys, size, dir); - if (!dev_is_dma_coherent(dev)) + if (!dev_is_dma_coherent(dev)) { arch_sync_dma_for_device(phys, size, dir); + arch_sync_dma_flush(); + } } void iommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl, @@ -1133,13 +1137,15 @@ void iommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl, struct scatterlist *sg; int i; - if (sg_dma_is_swiotlb(sgl)) + if (sg_dma_is_swiotlb(sgl)) { for_each_sg(sgl, sg, nelems, i) iommu_dma_sync_single_for_cpu(dev, sg_dma_address(sg), sg->length, dir); - else if (!dev_is_dma_coherent(dev)) + } else if (!dev_is_dma_coherent(dev)) { for_each_sg(sgl, sg, nelems, i) arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir); + arch_sync_dma_flush(); + } } void iommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl, @@ -1148,14 +1154,16 @@ void iommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl, struct scatterlist *sg; int i; - if (sg_dma_is_swiotlb(sgl)) + if (sg_dma_is_swiotlb(sgl)) { for_each_sg(sgl, sg, nelems, i) iommu_dma_sync_single_for_device(dev, sg_dma_address(sg), sg->length, dir); - else if (!dev_is_dma_coherent(dev)) + } else if (!dev_is_dma_coherent(dev)) { for_each_sg(sgl, sg, nelems, i) arch_sync_dma_for_device(sg_phys(sg), sg->length, dir); + arch_sync_dma_flush(); + } } static phys_addr_t iommu_dma_map_swiotlb(struct device *dev, phys_addr_t phys, @@ -1230,8 +1238,10 @@ dma_addr_t iommu_dma_map_phys(struct device *dev, phys_addr_t phys, size_t size, return DMA_MAPPING_ERROR; } - if (!coherent && !(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))) + if (!coherent && !(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))) { arch_sync_dma_for_device(phys, size, dir); + arch_sync_dma_flush(); + } iova = __iommu_dma_map(dev, phys, size, prot, dma_mask); if (iova == DMA_MAPPING_ERROR && @@ -1254,8 +1264,10 @@ void iommu_dma_unmap_phys(struct device *dev, dma_addr_t dma_handle, if (WARN_ON(!phys)) return; - if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) && !dev_is_dma_coherent(dev)) + if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) && !dev_is_dma_coherent(dev)) { arch_sync_dma_for_cpu(phys, size, dir); + arch_sync_dma_flush(); + } __iommu_dma_unmap(dev, dma_handle, size); @@ -2004,6 +2016,8 @@ int dma_iova_sync(struct device *dev, struct dma_iova_state *state, dma_addr_t addr = state->addr + offset; size_t iova_start_pad = iova_offset(iovad, addr); + if (!dev_is_dma_coherent(dev)) + arch_sync_dma_flush(); return iommu_sync_map(domain, addr - iova_start_pad, iova_align(iovad, size + iova_start_pad)); } @@ -2017,6 +2031,8 @@ static void iommu_dma_iova_unlink_range_slow(struct device *dev, struct iommu_dma_cookie *cookie = domain->iova_cookie; struct iova_domain *iovad = &cookie->iovad; size_t iova_start_pad = iova_offset(iovad, addr); + bool need_sync_dma = !dev_is_dma_coherent(dev) && + !(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO)); dma_addr_t end = addr + size; do { @@ -2040,6 +2056,9 @@ static void iommu_dma_iova_unlink_range_slow(struct device *dev, addr += len; iova_start_pad = 0; } while (addr < end); + + if (need_sync_dma) + arch_sync_dma_flush(); } static void __iommu_dma_iova_unlink(struct device *dev, diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h index 19b6daf88f2a..dc91fb4e2f61 100644 --- a/drivers/iommu/generic_pt/iommu_pt.h +++ b/drivers/iommu/generic_pt/iommu_pt.h @@ -534,10 +534,12 @@ static int __map_range_leaf(struct pt_range *range, void *arg, struct pt_state pts = pt_init(range, level, table); struct pt_iommu_map_args *map = arg; unsigned int leaf_pgsize_lg2 = map->leaf_pgsize_lg2; + unsigned int leaves_avail; unsigned int start_index; pt_oaddr_t oa = map->oa; - unsigned int num_leaves; + pt_vaddr_t num_leaves; unsigned int orig_end; + unsigned int step_lg2; pt_vaddr_t last_va; unsigned int step; bool need_contig; @@ -546,21 +548,25 @@ static int __map_range_leaf(struct pt_range *range, void *arg, PT_WARN_ON(map->leaf_level != level); PT_WARN_ON(!pt_can_have_leaf(&pts)); - step = log2_to_int_t(unsigned int, - leaf_pgsize_lg2 - pt_table_item_lg2sz(&pts)); - need_contig = leaf_pgsize_lg2 != pt_table_item_lg2sz(&pts); + step_lg2 = leaf_pgsize_lg2 - pt_table_item_lg2sz(&pts); + step = log2_to_int_t(unsigned int, step_lg2); + need_contig = step_lg2 != 0; _pt_iter_first(&pts); start_index = pts.index; orig_end = pts.end_index; - if (pts.index + map->num_leaves < pts.end_index) { + leaves_avail = + log2_div_t(unsigned int, pts.end_index - pts.index, step_lg2); + if (map->num_leaves <= leaves_avail) { /* Need to stop in the middle of the table to change sizes */ - pts.end_index = pts.index + map->num_leaves; + pts.end_index = pts.index + log2_mul(map->num_leaves, step_lg2); num_leaves = 0; } else { - num_leaves = map->num_leaves - (pts.end_index - pts.index); + num_leaves = map->num_leaves - leaves_avail; } + PT_WARN_ON( + log2_mod_t(unsigned int, pts.end_index - pts.index, step_lg2)); do { pts.type = pt_load_entry_raw(&pts); if (pts.type != PT_ENTRY_EMPTY || need_contig) { @@ -920,8 +926,8 @@ static int NS(map_range)(struct pt_iommu *iommu_table, dma_addr_t iova, return ret; /* Calculate target page size and level for the leaves */ - if (pt_has_system_page_size(common) && len == PAGE_SIZE) { - PT_WARN_ON(!(pgsize_bitmap & PAGE_SIZE)); + if (pt_has_system_page_size(common) && len == PAGE_SIZE && + likely(pgsize_bitmap & PAGE_SIZE)) { if (log2_mod(iova | paddr, PAGE_SHIFT)) return -ENXIO; map.leaf_pgsize_lg2 = PAGE_SHIFT; diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index c3d18cd77d2f..4d0e65bc131d 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -3530,8 +3530,8 @@ void domain_remove_dev_pasid(struct iommu_domain *domain, if (!domain) return; - /* Identity domain has no meta data for pasid. */ - if (domain->type == IOMMU_DOMAIN_IDENTITY) + /* Identity domain and blocked domain have no meta data for pasid. */ + if (domain->type == IOMMU_DOMAIN_IDENTITY || domain->type == IOMMU_DOMAIN_BLOCKED) return; dmar_domain = to_dmar_domain(domain); @@ -3545,12 +3545,13 @@ void domain_remove_dev_pasid(struct iommu_domain *domain, } spin_unlock_irqrestore(&dmar_domain->lock, flags); + if (WARN_ON_ONCE(!dev_pasid)) + return; + cache_tag_unassign_domain(dmar_domain, dev, pasid); domain_detach_iommu(dmar_domain, iommu); - if (!WARN_ON_ONCE(!dev_pasid)) { - intel_iommu_debugfs_remove_dev_pasid(dev_pasid); - kfree(dev_pasid); - } + intel_iommu_debugfs_remove_dev_pasid(dev_pasid); + kfree(dev_pasid); } static int blocking_domain_set_dev_pasid(struct iommu_domain *domain, @@ -3937,6 +3938,9 @@ static void quirk_iommu_igfx(struct pci_dev *dev) disable_igfx_iommu = 1; } +/* Q35 integrated gfx dmar support is totally busted. */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x29b2, quirk_iommu_igfx); + /* G4x/GM45 integrated gfx dmar support is totally busted. */ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx); diff --git a/drivers/iommu/iommu-pages.h b/drivers/iommu/iommu-pages.h index ae9da4f571f6..e9e605b5fa3a 100644 --- a/drivers/iommu/iommu-pages.h +++ b/drivers/iommu/iommu-pages.h @@ -137,7 +137,7 @@ static inline void iommu_pages_flush_incoherent(struct device *dma_dev, void *virt, size_t offset, size_t len) { - dma_sync_single_for_device(dma_dev, (uintptr_t)virt + offset, len, + dma_sync_single_for_device(dma_dev, virt_to_phys(virt) + offset, len, DMA_TO_DEVICE); } void iommu_pages_stop_incoherent_list(struct iommu_pages_list *list, diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 61c12ba78206..d1a9e713d3a0 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -62,14 +62,14 @@ struct iommu_group { int id; struct iommu_domain *default_domain; struct iommu_domain *blocking_domain; - /* - * During a group device reset, @resetting_domain points to the physical - * domain, while @domain points to the attached domain before the reset. - */ - struct iommu_domain *resetting_domain; struct iommu_domain *domain; struct list_head entry; unsigned int owner_cnt; + /* + * Number of devices in the group undergoing or awaiting recovery. + * If non-zero, concurrent domain attachments are rejected. + */ + unsigned int recovery_cnt; void *owner; }; @@ -77,12 +77,33 @@ struct group_device { struct list_head list; struct device *dev; char *name; + /* + * Device is blocked for a pending recovery while its group->domain is + * retained. This can happen when: + * - Device is undergoing a reset + */ + bool blocked; + unsigned int reset_depth; }; /* Iterate over each struct group_device in a struct iommu_group */ #define for_each_group_device(group, pos) \ list_for_each_entry(pos, &(group)->devices, list) +static struct group_device *__dev_to_gdev(struct device *dev) +{ + struct iommu_group *group = dev->iommu_group; + struct group_device *gdev; + + lockdep_assert_held(&group->mutex); + + for_each_group_device(group, gdev) { + if (gdev->dev == dev) + return gdev; + } + return NULL; +} + struct iommu_group_attribute { struct attribute attr; ssize_t (*show)(struct iommu_group *group, char *buf); @@ -2196,6 +2217,8 @@ EXPORT_SYMBOL_GPL(iommu_attach_device); int iommu_deferred_attach(struct device *dev, struct iommu_domain *domain) { + struct group_device *gdev; + /* * This is called on the dma mapping fast path so avoid locking. This is * racy, but we have an expectation that the driver will setup its DMAs @@ -2206,14 +2229,18 @@ int iommu_deferred_attach(struct device *dev, struct iommu_domain *domain) guard(mutex)(&dev->iommu_group->mutex); + gdev = __dev_to_gdev(dev); + if (WARN_ON(!gdev)) + return -ENODEV; + /* - * This is a concurrent attach during a device reset. Reject it until + * This is a concurrent attach during device recovery. Reject it until * pci_dev_reset_iommu_done() attaches the device to group->domain. * * Note that this might fail the iommu_dma_map(). But there's nothing * more we can do here. */ - if (dev->iommu_group->resetting_domain) + if (gdev->blocked) return -EBUSY; return __iommu_attach_device(domain, dev, NULL); } @@ -2270,19 +2297,24 @@ EXPORT_SYMBOL_GPL(iommu_get_domain_for_dev); struct iommu_domain *iommu_driver_get_domain_for_dev(struct device *dev) { struct iommu_group *group = dev->iommu_group; + struct group_device *gdev; lockdep_assert_held(&group->mutex); + gdev = __dev_to_gdev(dev); + if (WARN_ON(!gdev)) + return NULL; + /* * Driver handles the low-level __iommu_attach_device(), including the * one invoked by pci_dev_reset_iommu_done() re-attaching the device to * the cached group->domain. In this case, the driver must get the old - * domain from group->resetting_domain rather than group->domain. This + * domain from group->blocking_domain rather than group->domain. This * prevents it from re-attaching the device from group->domain (old) to * group->domain (new). */ - if (group->resetting_domain) - return group->resetting_domain; + if (gdev->blocked) + return group->blocking_domain; return group->domain; } @@ -2441,10 +2473,11 @@ static int __iommu_group_set_domain_internal(struct iommu_group *group, return -EINVAL; /* - * This is a concurrent attach during a device reset. Reject it until - * pci_dev_reset_iommu_done() attaches the device to group->domain. + * This is a concurrent attach during device recovery. Reject it until + * pci_dev_reset_iommu_done() attaches the device to group->domain, if + * IOMMU_SET_DOMAIN_MUST_SUCCEED is not set. */ - if (group->resetting_domain) + if (group->recovery_cnt && !(flags & IOMMU_SET_DOMAIN_MUST_SUCCEED)) return -EBUSY; /* @@ -2455,6 +2488,13 @@ static int __iommu_group_set_domain_internal(struct iommu_group *group, */ result = 0; for_each_group_device(group, gdev) { + /* + * Device under recovery is attached to group->blocking_domain. + * Don't change that. pci_dev_reset_iommu_done() will re-attach + * its domain to the updated group->domain, after the recovery. + */ + if (gdev->blocked) + continue; ret = __iommu_device_set_domain(group, gdev->dev, new_domain, group->domain, flags); if (ret) { @@ -2575,27 +2615,16 @@ out_set_count: static int __iommu_map_domain_pgtbl(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, - size_t size, int prot, gfp_t gfp) + size_t size, int prot, gfp_t gfp, + size_t *mapped) { const struct iommu_domain_ops *ops = domain->ops; - unsigned long orig_iova = iova; unsigned int min_pagesz; - size_t orig_size = size; int ret = 0; - might_sleep_if(gfpflags_allow_blocking(gfp)); - - if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING))) - return -EINVAL; - - if (WARN_ON(!ops->map_pages || domain->pgsize_bitmap == 0UL)) + if (WARN_ON(!ops->map_pages)) return -ENODEV; - /* Discourage passing strange GFP flags */ - if (WARN_ON_ONCE(gfp & (__GFP_COMP | __GFP_DMA | __GFP_DMA32 | - __GFP_HIGHMEM))) - return -EINVAL; - /* find out the minimum page size supported */ min_pagesz = 1 << __ffs(domain->pgsize_bitmap); @@ -2613,31 +2642,25 @@ static int __iommu_map_domain_pgtbl(struct iommu_domain *domain, pr_debug("map: iova 0x%lx pa %pa size 0x%zx\n", iova, &paddr, size); while (size) { - size_t pgsize, count, mapped = 0; + size_t pgsize, count, op_mapped = 0; pgsize = iommu_pgsize(domain, iova, paddr, size, &count); pr_debug("mapping: iova 0x%lx pa %pa pgsize 0x%zx count %zu\n", iova, &paddr, pgsize, count); ret = ops->map_pages(domain, iova, paddr, pgsize, count, prot, - gfp, &mapped); + gfp, &op_mapped); /* * Some pages may have been mapped, even if an error occurred, * so we should account for those so they can be unmapped. */ - size -= mapped; - + *mapped += op_mapped; if (ret) - break; + return ret; - iova += mapped; - paddr += mapped; - } - - /* unroll mapping in case something went wrong */ - if (ret) { - iommu_unmap(domain, orig_iova, orig_size - size); - return ret; + size -= op_mapped; + iova += op_mapped; + paddr += op_mapped; } return 0; } @@ -2655,25 +2678,31 @@ int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot, gfp_t gfp) { struct pt_iommu *pt = iommupt_from_domain(domain); + size_t mapped = 0; int ret; - if (pt) { - size_t mapped = 0; + might_sleep_if(gfpflags_allow_blocking(gfp)); + /* Discourage passing strange GFP flags or illegal domains */ + if (WARN_ON_ONCE(!(domain->type & __IOMMU_DOMAIN_PAGING) || + !domain->pgsize_bitmap || + (gfp & (__GFP_COMP | __GFP_DMA | __GFP_DMA32 | + __GFP_HIGHMEM)))) + return -EINVAL; + + if (pt) ret = pt->ops->map_range(pt, iova, paddr, size, prot, gfp, &mapped); - if (ret) { - iommu_unmap(domain, iova, mapped); - return ret; - } - return 0; - } - ret = __iommu_map_domain_pgtbl(domain, iova, paddr, size, prot, gfp); - if (!ret) - return ret; + else + ret = __iommu_map_domain_pgtbl(domain, iova, paddr, size, prot, + gfp, &mapped); - trace_map(iova, paddr, size); - iommu_debug_map(domain, paddr, size); + trace_map(iova, paddr, mapped); + iommu_debug_map(domain, paddr, mapped); + if (ret) { + iommu_unmap(domain, iova, mapped); + return ret; + } return 0; } @@ -2702,10 +2731,7 @@ __iommu_unmap_domain_pgtbl(struct iommu_domain *domain, unsigned long iova, size_t unmapped_page, unmapped = 0; unsigned int min_pagesz; - if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING))) - return 0; - - if (WARN_ON(!ops->unmap_pages || domain->pgsize_bitmap == 0UL)) + if (WARN_ON(!ops->unmap_pages)) return 0; /* find out the minimum page size supported */ @@ -2724,8 +2750,6 @@ __iommu_unmap_domain_pgtbl(struct iommu_domain *domain, unsigned long iova, pr_debug("unmap this: iova 0x%lx size 0x%zx\n", iova, size); - iommu_debug_unmap_begin(domain, iova, size); - /* * Keep iterating until we either unmap 'size' bytes (or more) * or we hit an area that isn't mapped. @@ -2761,6 +2785,12 @@ static size_t __iommu_unmap(struct iommu_domain *domain, unsigned long iova, struct pt_iommu *pt = iommupt_from_domain(domain); size_t unmapped; + if (WARN_ON_ONCE(!(domain->type & __IOMMU_DOMAIN_PAGING) || + !domain->pgsize_bitmap)) + return 0; + + iommu_debug_unmap_begin(domain, iova, size); + if (pt) unmapped = pt->ops->unmap_range(pt, iova, size, iotlb_gather); else @@ -3570,7 +3600,12 @@ static void __iommu_remove_group_pasid(struct iommu_group *group, struct group_device *device; for_each_group_device(group, device) { - if (device->dev->iommu->max_pasids > 0) + /* + * A group-level detach cannot fail, even if there is a blocked + * device. In fact, blocked devices must be already detached for + * a pending device recovery. + */ + if (!device->blocked && device->dev->iommu->max_pasids > 0) iommu_remove_dev_pasid(device->dev, pasid, domain); } } @@ -3615,10 +3650,10 @@ int iommu_attach_device_pasid(struct iommu_domain *domain, mutex_lock(&group->mutex); /* - * This is a concurrent attach during a device reset. Reject it until + * This is a concurrent attach during device recovery. Reject it until * pci_dev_reset_iommu_done() attaches the device to group->domain. */ - if (group->resetting_domain) { + if (group->recovery_cnt) { ret = -EBUSY; goto out_unlock; } @@ -3708,10 +3743,10 @@ int iommu_replace_device_pasid(struct iommu_domain *domain, mutex_lock(&group->mutex); /* - * This is a concurrent attach during a device reset. Reject it until + * This is a concurrent attach during device recovery. Reject it until * pci_dev_reset_iommu_done() attaches the device to group->domain. */ - if (group->resetting_domain) { + if (group->recovery_cnt) { ret = -EBUSY; goto out_unlock; } @@ -3982,12 +4017,12 @@ EXPORT_SYMBOL_NS_GPL(iommu_replace_group_handle, "IOMMUFD_INTERNAL"); * routine wants to block any IOMMU activity: translation and ATS invalidation. * * This function attaches the device's RID/PASID(s) the group->blocking_domain, - * setting the group->resetting_domain. This allows the IOMMU driver pausing any + * incrementing the group->recovery_cnt, to allow the IOMMU driver pausing any * IOMMU activity while leaving the group->domain pointer intact. Later when the * reset is finished, pci_dev_reset_iommu_done() can restore everything. * * Caller must use pci_dev_reset_iommu_prepare() with pci_dev_reset_iommu_done() - * before/after the core-level reset routine, to unset the resetting_domain. + * before/after the core-level reset routine, to decrement the recovery_cnt. * * Return: 0 on success or negative error code if the preparation failed. * @@ -4000,6 +4035,7 @@ EXPORT_SYMBOL_NS_GPL(iommu_replace_group_handle, "IOMMUFD_INTERNAL"); int pci_dev_reset_iommu_prepare(struct pci_dev *pdev) { struct iommu_group *group = pdev->dev.iommu_group; + struct group_device *gdev; unsigned long pasid; void *entry; int ret; @@ -4009,45 +4045,99 @@ int pci_dev_reset_iommu_prepare(struct pci_dev *pdev) guard(mutex)(&group->mutex); - /* Re-entry is not allowed */ - if (WARN_ON(group->resetting_domain)) - return -EBUSY; + gdev = __dev_to_gdev(&pdev->dev); + if (WARN_ON(!gdev)) + return -ENODEV; + + if (gdev->reset_depth++) + return 0; ret = __iommu_group_alloc_blocking_domain(group); - if (ret) + if (ret) { + gdev->reset_depth--; return ret; + } /* Stage RID domain at blocking_domain while retaining group->domain */ if (group->domain != group->blocking_domain) { ret = __iommu_attach_device(group->blocking_domain, &pdev->dev, group->domain); - if (ret) + if (ret) { + gdev->reset_depth--; return ret; + } } + /* + * Update gdev->blocked upon the domain change, as it is used to return + * the correct domain in iommu_driver_get_domain_for_dev() that might be + * called in a set_dev_pasid callback function. + */ + gdev->blocked = true; + /* * Stage PASID domains at blocking_domain while retaining pasid_array. * * The pasid_array is mostly fenced by group->mutex, except one reader * in iommu_attach_handle_get(), so it's safe to read without xa_lock. */ - xa_for_each_start(&group->pasid_array, pasid, entry, 1) - iommu_remove_dev_pasid(&pdev->dev, pasid, - pasid_array_entry_to_domain(entry)); + if (pdev->dev.iommu->max_pasids > 0) { + xa_for_each_start(&group->pasid_array, pasid, entry, 1) { + struct iommu_domain *pasid_dom = + pasid_array_entry_to_domain(entry); - group->resetting_domain = group->blocking_domain; + iommu_remove_dev_pasid(&pdev->dev, pasid, pasid_dom); + } + } + + group->recovery_cnt++; return ret; } EXPORT_SYMBOL_GPL(pci_dev_reset_iommu_prepare); +static int __group_device_cmp_dma_alias(struct pci_dev *dev, u16 alias, + void *data) +{ + return alias == *(u16 *)data; +} + +static int group_device_cmp_dma_alias(struct pci_dev *dev, u16 alias, + void *data) +{ + return pci_for_each_dma_alias(data, __group_device_cmp_dma_alias, + &alias); +} + +static bool group_device_dma_alias_is_blocked(struct iommu_group *group, + struct group_device *gdev) +{ + struct group_device *sibling; + + lockdep_assert_held(&group->mutex); + + if (!dev_is_pci(gdev->dev)) + return false; + + for_each_group_device(group, sibling) { + if (sibling == gdev || !sibling->blocked || + !dev_is_pci(sibling->dev)) + continue; + if (pci_for_each_dma_alias(to_pci_dev(gdev->dev), + group_device_cmp_dma_alias, + to_pci_dev(sibling->dev))) + return true; + } + return false; +} + /** * pci_dev_reset_iommu_done() - Restore IOMMU after a PCI device reset is done * @pdev: PCI device that has finished a reset routine * * After a PCIe device finishes a reset routine, it wants to restore its IOMMU - * IOMMU activity, including new translation as well as cache invalidation, by - * re-attaching all RID/PASID of the device's back to the domains retained in - * the core-level structure. + * activity, including new translation and cache invalidation, by re-attaching + * all RID/PASID of the device back to the domains retained in the core-level + * structure. * * Caller must pair it with a successful pci_dev_reset_iommu_prepare(). * @@ -4057,6 +4147,7 @@ EXPORT_SYMBOL_GPL(pci_dev_reset_iommu_prepare); void pci_dev_reset_iommu_done(struct pci_dev *pdev) { struct iommu_group *group = pdev->dev.iommu_group; + struct group_device *gdev; unsigned long pasid; void *entry; @@ -4065,32 +4156,70 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev) guard(mutex)(&group->mutex); - /* pci_dev_reset_iommu_prepare() was bypassed for the device */ - if (!group->resetting_domain) + gdev = __dev_to_gdev(&pdev->dev); + if (WARN_ON(!gdev)) + return; + + /* Unbalanced done() calls would underflow the counter */ + if (WARN_ON(gdev->reset_depth == 0)) + return; + if (--gdev->reset_depth) return; - /* pci_dev_reset_iommu_prepare() was not successfully called */ if (WARN_ON(!group->blocking_domain)) return; - /* Re-attach RID domain back to group->domain */ - if (group->domain != group->blocking_domain) { + if (group_device_dma_alias_is_blocked(group, gdev)) { + /* + * FIXME: DMA aliased devices share the same RID, which would be + * convoluted to handle, as "gdev->blocked" is not sufficient: + * - "blocked" state is effectively shared across these devices + * - if the core skipped the blocking on the second device, the + * IOMMU driver's attachment state would diverge from the HW + * state + * For now, just warn and see whether real ATS use cases hit it. + */ + pci_warn(pdev, + "DMA-aliased sibling may be prematurely unblocked\n"); + } + + /* + * Re-attach RID domain back to group->domain + * + * Leave the device parked in the blocking_domain if group->domain isn't + * initialized yet + */ + if (group->domain && group->domain != group->blocking_domain) { WARN_ON(__iommu_attach_device(group->domain, &pdev->dev, group->blocking_domain)); } + /* + * Update gdev->blocked upon the domain change, as it is used to return + * the correct domain in iommu_driver_get_domain_for_dev() that might be + * called in a set_dev_pasid callback function. + */ + gdev->blocked = false; + /* * Re-attach PASID domains back to the domains retained in pasid_array. * * The pasid_array is mostly fenced by group->mutex, except one reader * in iommu_attach_handle_get(), so it's safe to read without xa_lock. */ - xa_for_each_start(&group->pasid_array, pasid, entry, 1) - WARN_ON(__iommu_set_group_pasid( - pasid_array_entry_to_domain(entry), group, pasid, - group->blocking_domain)); + if (pdev->dev.iommu->max_pasids > 0) { + xa_for_each_start(&group->pasid_array, pasid, entry, 1) { + struct iommu_domain *pasid_dom = + pasid_array_entry_to_domain(entry); - group->resetting_domain = NULL; + WARN_ON(pasid_dom->ops->set_dev_pasid( + pasid_dom, &pdev->dev, pasid, + group->blocking_domain)); + } + } + + if (!WARN_ON(group->recovery_cnt == 0)) + group->recovery_cnt--; } EXPORT_SYMBOL_GPL(pci_dev_reset_iommu_done); diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 92c5d5ef8d00..170a7005f0bc 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -866,7 +866,7 @@ iommufd_device_auto_get_domain(struct iommufd_device *idev, ioasid_t pasid, { /* * iommufd_hw_pagetable_attach() is called by - * iommufd_hw_pagetable_alloc() in immediate attachment mode, same as + * iommufd_hwpt_paging_alloc() in immediate attachment mode, same as * iommufd_device_do_attach(). So if we are in this mode then we prefer * to use the immediate_attach path as it supports drivers that can't * directly allocate a domain. diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index f1e686b3a265..710eef0b6004 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -187,9 +187,10 @@ static ssize_t iommufd_fault_fops_write(struct file *filep, const char __user *b mutex_lock(&fault->mutex); while (count > done) { - rc = copy_from_user(&response, buf + done, response_size); - if (rc) + if (copy_from_user(&response, buf + done, response_size)) { + rc = -EFAULT; break; + } static_assert((int)IOMMUFD_PAGE_RESP_SUCCESS == (int)IOMMU_PAGE_RESP_SUCCESS); diff --git a/drivers/iommu/iommufd/io_pagetable.c b/drivers/iommu/iommufd/io_pagetable.c index ee003bb2f647..24d4917105d9 100644 --- a/drivers/iommu/iommufd/io_pagetable.c +++ b/drivers/iommu/iommufd/io_pagetable.c @@ -814,6 +814,16 @@ again: unmapped_bytes += area_last - area_first + 1; down_write(&iopt->iova_rwsem); + + /* + * After releasing the iova_rwsem concurrent allocation could + * place new areas at IOVAs we have already unmapped. Keep + * moving the start of the search forward to ignore the area + * already unmapped. + */ + if (area_last >= last) + break; + start = area_last + 1; } out_unlock_iova: diff --git a/drivers/iommu/iommufd/iommufd_test.h b/drivers/iommu/iommufd/iommufd_test.h index 73e73e1ec158..52b78cbcc920 100644 --- a/drivers/iommu/iommufd/iommufd_test.h +++ b/drivers/iommu/iommufd/iommufd_test.h @@ -36,7 +36,6 @@ enum { enum { MOCK_IOMMUPT_DEFAULT = 0, MOCK_IOMMUPT_HUGE, - MOCK_IOMMUPT_AMDV1, }; /* These values are true for MOCK_IOMMUPT_DEFAULT */ diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c index 8124c554f2cc..9bdb2945afe1 100644 --- a/drivers/iommu/iommufd/pages.c +++ b/drivers/iommu/iommufd/pages.c @@ -1450,7 +1450,7 @@ static void iopt_revoke_notify(struct dma_buf_attachment *attach) pages->dmabuf.phys.len = 0; } -static struct dma_buf_attach_ops iopt_dmabuf_attach_revoke_ops = { +static const struct dma_buf_attach_ops iopt_dmabuf_attach_revoke_ops = { .allow_peer2peer = true, .invalidate_mappings = iopt_revoke_notify, }; diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c index 2a82cee7921b..af07c642a526 100644 --- a/drivers/iommu/iommufd/selftest.c +++ b/drivers/iommu/iommufd/selftest.c @@ -421,19 +421,6 @@ static const struct iommu_dirty_ops amdv1_mock_dirty_ops = { .set_dirty_tracking = mock_domain_set_dirty_tracking, }; -static const struct iommu_domain_ops amdv1_ops = { - IOMMU_PT_DOMAIN_OPS(amdv1), - .free = mock_domain_free, - .attach_dev = mock_domain_nop_attach, - .set_dev_pasid = mock_domain_set_dev_pasid_nop, - .iotlb_sync = &mock_iotlb_sync, -}; - -static const struct iommu_dirty_ops amdv1_dirty_ops = { - IOMMU_PT_DIRTY_OPS(amdv1), - .set_dirty_tracking = mock_domain_set_dirty_tracking, -}; - static struct mock_iommu_domain * mock_domain_alloc_pgtable(struct device *dev, const struct iommu_hwpt_selftest *user_cfg, u32 flags) @@ -477,24 +464,6 @@ mock_domain_alloc_pgtable(struct device *dev, mock->domain.dirty_ops = &amdv1_mock_dirty_ops; break; } - - case MOCK_IOMMUPT_AMDV1: { - struct pt_iommu_amdv1_cfg cfg = {}; - - cfg.common.hw_max_vasz_lg2 = 64; - cfg.common.hw_max_oasz_lg2 = 52; - cfg.common.features = BIT(PT_FEAT_DYNAMIC_TOP) | - BIT(PT_FEAT_AMDV1_ENCRYPT_TABLES) | - BIT(PT_FEAT_AMDV1_FORCE_COHERENCE); - cfg.starting_level = 2; - mock->domain.ops = &amdv1_ops; - rc = pt_iommu_amdv1_init(&mock->amdv1, &cfg, GFP_KERNEL); - if (rc) - goto err_free; - if (flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING) - mock->domain.dirty_ops = &amdv1_dirty_ops; - break; - } default: rc = -EOPNOTSUPP; goto err_free; @@ -636,7 +605,7 @@ static void mock_viommu_destroy(struct iommufd_viommu *viommu) if (mock_viommu->mmap_offset) iommufd_viommu_destroy_mmap(&mock_viommu->core, mock_viommu->mmap_offset); - free_page((unsigned long)mock_viommu->page); + free_pages((unsigned long)mock_viommu->page, 1); mutex_destroy(&mock_viommu->queue_mutex); /* iommufd core frees mock_viommu and viommu */ @@ -870,7 +839,7 @@ err_destroy_mmap: iommufd_viommu_destroy_mmap(&mock_viommu->core, mock_viommu->mmap_offset); err_free_page: - free_page((unsigned long)mock_viommu->page); + free_pages((unsigned long)mock_viommu->page, 1); return rc; } diff --git a/drivers/iommu/iommufd/vfio_compat.c b/drivers/iommu/iommufd/vfio_compat.c index a258ee2f4579..acb48cdd3b00 100644 --- a/drivers/iommu/iommufd/vfio_compat.c +++ b/drivers/iommu/iommufd/vfio_compat.c @@ -283,7 +283,7 @@ static int iommufd_vfio_check_extension(struct iommufd_ctx *ictx, case VFIO_TYPE1_IOMMU: case VFIO_TYPE1v2_IOMMU: case VFIO_UNMAP_ALL: - return 1; + return !ictx->no_iommu_mode; case VFIO_NOIOMMU_IOMMU: return IS_ENABLED(CONFIG_VFIO_NOIOMMU); diff --git a/drivers/irqchip/irq-ath79-cpu.c b/drivers/irqchip/irq-ath79-cpu.c index 923e4bba3776..9b7273a7f8ce 100644 --- a/drivers/irqchip/irq-ath79-cpu.c +++ b/drivers/irqchip/irq-ath79-cpu.c @@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init( } IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", ar79_cpu_intc_of_init); - -void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) -{ - irq_wb_chan[2] = irq_wb_chan2; - irq_wb_chan[3] = irq_wb_chan3; - mips_cpu_irq_init(); -} diff --git a/drivers/irqchip/irq-gic-v5-its.c b/drivers/irqchip/irq-gic-v5-its.c index 36a8d1368f0e..28e39b065de0 100644 --- a/drivers/irqchip/irq-gic-v5-its.c +++ b/drivers/irqchip/irq-gic-v5-its.c @@ -929,14 +929,15 @@ static void gicv5_its_free_eventid(struct gicv5_its_dev *its_dev, u32 event_id_b static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { - u32 device_id, event_id_base, lpi; struct gicv5_its_dev *its_dev; + u32 device_id, event_id_base; msi_alloc_info_t *info = arg; irq_hw_number_t hwirq; struct irq_data *irqd; int ret, i; its_dev = info->scratchpad[0].ptr; + device_id = its_dev->device_id; ret = gicv5_its_alloc_eventid(its_dev, info, nr_irqs, &event_id_base); if (ret) @@ -946,22 +947,11 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi if (ret) goto out_eventid; - device_id = its_dev->device_id; + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, NULL); + if (ret) + goto out_eventid; for (i = 0; i < nr_irqs; i++) { - ret = gicv5_alloc_lpi(); - if (ret < 0) { - pr_debug("Failed to find free LPI!\n"); - goto out_free_irqs; - } - lpi = ret; - - ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi); - if (ret) { - gicv5_free_lpi(lpi); - goto out_free_irqs; - } - /* * Store eventid and deviceid into the hwirq for later use. * @@ -980,13 +970,6 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi return 0; -out_free_irqs: - while (--i >= 0) { - irqd = irq_domain_get_irq_data(domain, virq + i); - gicv5_free_lpi(irqd->parent_data->hwirq); - irq_domain_reset_irq_data(irqd); - irq_domain_free_irqs_parent(domain, virq + i, 1); - } out_eventid: gicv5_its_free_eventid(its_dev, event_id_base, nr_irqs); return ret; @@ -1009,15 +992,14 @@ static void gicv5_its_irq_domain_free(struct irq_domain *domain, unsigned int vi bitmap_release_region(its_dev->event_map, event_id_base, get_count_order(nr_irqs)); - /* Hierarchically free irq data */ for (i = 0; i < nr_irqs; i++) { d = irq_domain_get_irq_data(domain, virq + i); - - gicv5_free_lpi(d->parent_data->hwirq); irq_domain_reset_irq_data(d); - irq_domain_free_irqs_parent(domain, virq + i, 1); } + /* Hierarchically free irq data */ + irq_domain_free_irqs_parent(domain, virq, nr_irqs); + gicv5_its_syncr(its, its_dev); gicv5_irs_syncr(); } diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c index 405a5eee847b..c1af07083cef 100644 --- a/drivers/irqchip/irq-gic-v5.c +++ b/drivers/irqchip/irq-gic-v5.c @@ -59,16 +59,6 @@ static void release_lpi(u32 lpi) ida_free(&lpi_ida, lpi); } -int gicv5_alloc_lpi(void) -{ - return alloc_lpi(); -} - -void gicv5_free_lpi(u32 lpi) -{ - release_lpi(lpi); -} - static void gicv5_ppi_priority_init(void) { write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR0_EL1); @@ -511,6 +501,23 @@ static bool gicv5_ppi_irq_is_level(irq_hw_number_t hwirq) return !!(read_ppi_sysreg_s(hwirq, PPI_HM) & bit); } +static int gicv5_ppi_irq_set_type(struct irq_data *d, unsigned int type) +{ + /* + * GICv5's PPIs do not have a configurable trigger or handling + * mode. Check that the attempt to set a type matches what the + * hardware reports in the HMR, and error on a mismatch. + */ + + if (type & IRQ_TYPE_EDGE_BOTH && gicv5_ppi_irq_is_level(d->hwirq)) + return -EINVAL; + + if (type & IRQ_TYPE_LEVEL_MASK && !gicv5_ppi_irq_is_level(d->hwirq)) + return -EINVAL; + + return 0; +} + static int gicv5_ppi_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu) { if (vcpu) @@ -526,6 +533,7 @@ static const struct irq_chip gicv5_ppi_irq_chip = { .irq_mask = gicv5_ppi_irq_mask, .irq_unmask = gicv5_ppi_irq_unmask, .irq_eoi = gicv5_ppi_irq_eoi, + .irq_set_type = gicv5_ppi_irq_set_type, .irq_get_irqchip_state = gicv5_ppi_irq_get_irqchip_state, .irq_set_irqchip_state = gicv5_ppi_irq_set_irqchip_state, .irq_set_vcpu_affinity = gicv5_ppi_irq_set_vcpu_affinity, @@ -788,38 +796,64 @@ static void gicv5_lpi_config_reset(struct irq_data *d) gicv5_lpi_irq_write_pending_state(d, false); } +static void gicv5_irq_lpi_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *d; + + for (unsigned int i = 0; i < nr_irqs; i++, virq++) { + d = irq_domain_get_irq_data(domain, virq); + + release_lpi(d->hwirq); + + irq_set_handler(virq, NULL); + irq_domain_reset_irq_data(d); + } +} + static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { irq_hw_number_t hwirq; struct irq_data *irqd; - u32 *lpi = arg; + unsigned int i; int ret; - if (WARN_ON_ONCE(nr_irqs != 1)) - return -EINVAL; + for (i = 0; i < nr_irqs; i++) { + ret = alloc_lpi(); + if (ret < 0) + goto out_free_lpis; + hwirq = ret; - hwirq = *lpi; + ret = gicv5_irs_iste_alloc(hwirq); + if (ret < 0) { + /* Undo partial state first, then clean up the rest */ + release_lpi(hwirq); + goto out_free_lpis; + } - irqd = irq_domain_get_irq_data(domain, virq); + irqd = irq_domain_get_irq_data(domain, virq + i); - irq_domain_set_info(domain, virq, hwirq, &gicv5_lpi_irq_chip, NULL, - handle_fasteoi_irq, NULL, NULL); - irqd_set_single_target(irqd); + irq_domain_set_info(domain, virq + i, hwirq, &gicv5_lpi_irq_chip, + NULL, handle_fasteoi_irq, NULL, NULL); + irqd_set_single_target(irqd); - ret = gicv5_irs_iste_alloc(hwirq); - if (ret < 0) - return ret; - - gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI); - gicv5_lpi_config_reset(irqd); + gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI); + gicv5_lpi_config_reset(irqd); + } return 0; + +out_free_lpis: + if (i) + gicv5_irq_lpi_domain_free(domain, virq, i); + + return ret; } static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = { .alloc = gicv5_irq_lpi_domain_alloc, - .free = gicv5_irq_domain_free, + .free = gicv5_irq_lpi_domain_free, }; void __init gicv5_init_lpi_domain(void) @@ -840,30 +874,21 @@ static int gicv5_irq_ipi_domain_alloc(struct irq_domain *domain, unsigned int vi unsigned int nr_irqs, void *arg) { struct irq_data *irqd; - int ret, i; - u32 lpi; + int ret; - for (i = 0; i < nr_irqs; i++) { - ret = gicv5_alloc_lpi(); - if (ret < 0) - return ret; + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); + if (ret) + return ret; - lpi = ret; + for (unsigned int i = 0; i < nr_irqs; i++, virq++) { + irqd = irq_domain_get_irq_data(domain, virq); - ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi); - if (ret) { - gicv5_free_lpi(lpi); - return ret; - } - - irqd = irq_domain_get_irq_data(domain, virq + i); - - irq_domain_set_hwirq_and_chip(domain, virq + i, i, - &gicv5_ipi_irq_chip, NULL); + irq_domain_set_hwirq_and_chip(domain, virq, i, + &gicv5_ipi_irq_chip, NULL); irqd_set_single_target(irqd); - irq_set_handler(virq + i, handle_percpu_irq); + irq_set_handler(virq, handle_percpu_irq); } return 0; @@ -881,12 +906,11 @@ static void gicv5_irq_ipi_domain_free(struct irq_domain *domain, unsigned int vi if (!d) return; - gicv5_free_lpi(d->parent_data->hwirq); - irq_set_handler(virq + i, NULL); irq_domain_reset_irq_data(d); - irq_domain_free_irqs_parent(domain, virq + i, 1); } + + irq_domain_free_irqs_parent(domain, virq, nr_irqs); } static const struct irq_domain_ops gicv5_irq_ipi_domain_ops = { diff --git a/drivers/irqchip/irq-meson-gpio.c b/drivers/irqchip/irq-meson-gpio.c index f722e9c57e2e..74a376ef452e 100644 --- a/drivers/irqchip/irq-meson-gpio.c +++ b/drivers/irqchip/irq-meson-gpio.c @@ -415,8 +415,7 @@ static int meson_s4_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl, if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) val |= BIT(ctl->params->edge_single_offset + idx); - meson_gpio_irq_update_bits(ctl, params->edge_pol_reg, - BIT(idx) | BIT(12 + idx), val); + meson_gpio_irq_update_bits(ctl, REG_EDGE_POL, BIT(idx) | BIT(12 + idx), val); return 0; }; diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c index ba903fa689bd..a7a1852b548c 100644 --- a/drivers/irqchip/irq-riscv-imsic-early.c +++ b/drivers/irqchip/irq-riscv-imsic-early.c @@ -158,6 +158,8 @@ static int imsic_dying_cpu(unsigned int cpu) /* Cleanup IPIs */ imsic_ipi_dying_cpu(); + imsic_local_sync_all(false); + /* Mark per-CPU IMSIC state as offline */ imsic_state_offline(); diff --git a/drivers/isdn/Kconfig b/drivers/isdn/Kconfig deleted file mode 100644 index 6fd1b3f84a29..000000000000 --- a/drivers/isdn/Kconfig +++ /dev/null @@ -1,27 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# ISDN device configuration -# - -menuconfig ISDN - bool "ISDN support" - depends on NET && NETDEVICES - help - ISDN ("Integrated Services Digital Network", called RNIS in France) - is a fully digital telephone service that can be used for voice and - data connections. If your computer is equipped with an ISDN - adapter you can use it to connect to your Internet service provider - (with SLIP or PPP) faster than via a conventional telephone modem - (though still much slower than with DSL) or to make and accept - voice calls (eg. turning your PC into a software answering machine - or PABX). - - Select this option if you want your kernel to support ISDN. - -if ISDN - -source "drivers/isdn/capi/Kconfig" - -source "drivers/isdn/mISDN/Kconfig" - -endif # ISDN diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile deleted file mode 100644 index d14334f4007e..000000000000 --- a/drivers/isdn/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Makefile for the kernel ISDN subsystem and device drivers. - -# Object files in subdirectories - -obj-$(CONFIG_BT_CMTP) += capi/ -obj-$(CONFIG_MISDN) += mISDN/ -obj-$(CONFIG_ISDN) += hardware/ diff --git a/drivers/isdn/capi/Kconfig b/drivers/isdn/capi/Kconfig deleted file mode 100644 index fdb43a632215..000000000000 --- a/drivers/isdn/capi/Kconfig +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config ISDN_CAPI - def_bool ISDN && BT - help - This provides CAPI (the Common ISDN Application Programming - Interface) Version 2.0, a standard making it easy for programs to - access ISDN hardware in a device independent way. (For details see - .) CAPI supports making and accepting voice - and data connections, controlling call options and protocols, - as well as ISDN supplementary services like call forwarding or - three-party conferences (if supported by the specific hardware - driver). - - This subsystem requires a hardware specific driver. - See CONFIG_BT_CMTP for the last remaining regular driver - in the kernel that uses the CAPI subsystem. - -config CAPI_TRACE - def_bool BT_CMTP - help - If you say Y here, the kernelcapi driver can make verbose traces - of CAPI messages. This feature can be enabled/disabled via IOCTL for - every controller (default disabled). - -config ISDN_CAPI_MIDDLEWARE - def_bool BT_CMTP && TTY - help - This option will enhance the capabilities of the /dev/capi20 - interface. It will provide a means of moving a data connection, - established via the usual /dev/capi20 interface to a special tty - device. If you want to use pppd with pppdcapiplugin to dial up to - your ISP, say Y here. diff --git a/drivers/isdn/capi/Makefile b/drivers/isdn/capi/Makefile deleted file mode 100644 index 4fd3a4d7133f..000000000000 --- a/drivers/isdn/capi/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Makefile for the CAPI subsystem used by BT_CMTP - -obj-$(CONFIG_BT_CMTP) += kernelcapi.o -kernelcapi-y := kcapi.o capiutil.o capi.o -kernelcapi-$(CONFIG_PROC_FS) += kcapi_proc.o diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c deleted file mode 100644 index aa28b1d32c4e..000000000000 --- a/drivers/isdn/capi/capi.c +++ /dev/null @@ -1,1435 +0,0 @@ -/* $Id: capi.c,v 1.1.2.7 2004/04/28 09:48:59 armin Exp $ - * - * CAPI 2.0 Interface for Linux - * - * Copyright 1996 by Carsten Paeth - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "kcapi.h" - -MODULE_DESCRIPTION("CAPI4Linux: kernel CAPI layer and /dev/capi20 interface"); -MODULE_AUTHOR("Carsten Paeth"); -MODULE_LICENSE("GPL"); - -/* -------- driver information -------------------------------------- */ - -static DEFINE_MUTEX(capi_mutex); -static const struct class capi_class = { - .name = "capi", -}; -static int capi_major = 68; /* allocated */ - -module_param_named(major, capi_major, uint, 0); - -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE -#define CAPINC_NR_PORTS 32 -#define CAPINC_MAX_PORTS 256 - -static int capi_ttyminors = CAPINC_NR_PORTS; - -module_param_named(ttyminors, capi_ttyminors, uint, 0); -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - -/* -------- defines ------------------------------------------------- */ - -#define CAPINC_MAX_RECVQUEUE 10 -#define CAPINC_MAX_SENDQUEUE 10 -#define CAPI_MAX_BLKSIZE 2048 - -/* -------- data structures ----------------------------------------- */ - -struct capidev; -struct capincci; -struct capiminor; - -struct ackqueue_entry { - struct list_head list; - u16 datahandle; -}; - -struct capiminor { - unsigned int minor; - - struct capi20_appl *ap; - u32 ncci; - atomic_t datahandle; - atomic_t msgid; - - struct tty_port port; - int ttyinstop; - int ttyoutstop; - - struct sk_buff_head inqueue; - - struct sk_buff_head outqueue; - int outbytes; - struct sk_buff *outskb; - spinlock_t outlock; - - /* transmit path */ - struct list_head ackqueue; - int nack; - spinlock_t ackqlock; -}; - -struct capincci { - struct list_head list; - u32 ncci; - struct capidev *cdev; -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - struct capiminor *minorp; -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ -}; - -struct capidev { - struct list_head list; - struct capi20_appl ap; - u16 errcode; - unsigned userflags; - - struct sk_buff_head recvqueue; - wait_queue_head_t recvwait; - - struct list_head nccis; - - struct mutex lock; -}; - -/* -------- global variables ---------------------------------------- */ - -static DEFINE_MUTEX(capidev_list_lock); -static LIST_HEAD(capidev_list); - -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - -static DEFINE_SPINLOCK(capiminors_lock); -static struct capiminor **capiminors; - -static struct tty_driver *capinc_tty_driver; - -/* -------- datahandles --------------------------------------------- */ - -static int capiminor_add_ack(struct capiminor *mp, u16 datahandle) -{ - struct ackqueue_entry *n; - - n = kmalloc_obj(*n, GFP_ATOMIC); - if (unlikely(!n)) { - printk(KERN_ERR "capi: alloc datahandle failed\n"); - return -1; - } - n->datahandle = datahandle; - INIT_LIST_HEAD(&n->list); - spin_lock_bh(&mp->ackqlock); - list_add_tail(&n->list, &mp->ackqueue); - mp->nack++; - spin_unlock_bh(&mp->ackqlock); - return 0; -} - -static int capiminor_del_ack(struct capiminor *mp, u16 datahandle) -{ - struct ackqueue_entry *p, *tmp; - - spin_lock_bh(&mp->ackqlock); - list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) { - if (p->datahandle == datahandle) { - list_del(&p->list); - mp->nack--; - spin_unlock_bh(&mp->ackqlock); - kfree(p); - return 0; - } - } - spin_unlock_bh(&mp->ackqlock); - return -1; -} - -static void capiminor_del_all_ack(struct capiminor *mp) -{ - struct ackqueue_entry *p, *tmp; - - list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) { - list_del(&p->list); - kfree(p); - mp->nack--; - } -} - - -/* -------- struct capiminor ---------------------------------------- */ - -static void capiminor_destroy(struct tty_port *port) -{ - struct capiminor *mp = container_of(port, struct capiminor, port); - - kfree_skb(mp->outskb); - skb_queue_purge(&mp->inqueue); - skb_queue_purge(&mp->outqueue); - capiminor_del_all_ack(mp); - kfree(mp); -} - -static const struct tty_port_operations capiminor_port_ops = { - .destruct = capiminor_destroy, -}; - -static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci) -{ - struct capiminor *mp; - struct device *dev; - unsigned int minor; - - mp = kzalloc_obj(*mp); - if (!mp) { - printk(KERN_ERR "capi: can't alloc capiminor\n"); - return NULL; - } - - mp->ap = ap; - mp->ncci = ncci; - INIT_LIST_HEAD(&mp->ackqueue); - spin_lock_init(&mp->ackqlock); - - skb_queue_head_init(&mp->inqueue); - skb_queue_head_init(&mp->outqueue); - spin_lock_init(&mp->outlock); - - tty_port_init(&mp->port); - mp->port.ops = &capiminor_port_ops; - - /* Allocate the least unused minor number. */ - spin_lock(&capiminors_lock); - for (minor = 0; minor < capi_ttyminors; minor++) - if (!capiminors[minor]) { - capiminors[minor] = mp; - break; - } - spin_unlock(&capiminors_lock); - - if (minor == capi_ttyminors) { - printk(KERN_NOTICE "capi: out of minors\n"); - goto err_out1; - } - - mp->minor = minor; - - dev = tty_port_register_device(&mp->port, capinc_tty_driver, minor, - NULL); - if (IS_ERR(dev)) - goto err_out2; - - return mp; - -err_out2: - spin_lock(&capiminors_lock); - capiminors[minor] = NULL; - spin_unlock(&capiminors_lock); - -err_out1: - tty_port_put(&mp->port); - return NULL; -} - -static struct capiminor *capiminor_get(unsigned int minor) -{ - struct capiminor *mp; - - spin_lock(&capiminors_lock); - mp = capiminors[minor]; - if (mp) - tty_port_get(&mp->port); - spin_unlock(&capiminors_lock); - - return mp; -} - -static inline void capiminor_put(struct capiminor *mp) -{ - tty_port_put(&mp->port); -} - -static void capiminor_free(struct capiminor *mp) -{ - tty_unregister_device(capinc_tty_driver, mp->minor); - - spin_lock(&capiminors_lock); - capiminors[mp->minor] = NULL; - spin_unlock(&capiminors_lock); - - capiminor_put(mp); -} - -/* -------- struct capincci ----------------------------------------- */ - -static void capincci_alloc_minor(struct capidev *cdev, struct capincci *np) -{ - if (cdev->userflags & CAPIFLAG_HIGHJACKING) - np->minorp = capiminor_alloc(&cdev->ap, np->ncci); -} - -static void capincci_free_minor(struct capincci *np) -{ - struct capiminor *mp = np->minorp; - - if (mp) { - tty_port_tty_vhangup(&mp->port); - capiminor_free(mp); - } -} - -static inline unsigned int capincci_minor_opencount(struct capincci *np) -{ - struct capiminor *mp = np->minorp; - unsigned int count = 0; - struct tty_struct *tty; - - if (mp) { - tty = tty_port_tty_get(&mp->port); - if (tty) { - count = tty->count; - tty_kref_put(tty); - } - } - return count; -} - -#else /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ - -static inline void -capincci_alloc_minor(struct capidev *cdev, struct capincci *np) { } -static inline void capincci_free_minor(struct capincci *np) { } - -#endif /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ - -static struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci) -{ - struct capincci *np; - - np = kzalloc_obj(*np); - if (!np) - return NULL; - np->ncci = ncci; - np->cdev = cdev; - - capincci_alloc_minor(cdev, np); - - list_add_tail(&np->list, &cdev->nccis); - - return np; -} - -static void capincci_free(struct capidev *cdev, u32 ncci) -{ - struct capincci *np, *tmp; - - list_for_each_entry_safe(np, tmp, &cdev->nccis, list) - if (ncci == 0xffffffff || np->ncci == ncci) { - capincci_free_minor(np); - list_del(&np->list); - kfree(np); - } -} - -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE -static struct capincci *capincci_find(struct capidev *cdev, u32 ncci) -{ - struct capincci *np; - - list_for_each_entry(np, &cdev->nccis, list) - if (np->ncci == ncci) - return np; - return NULL; -} - -/* -------- handle data queue --------------------------------------- */ - -static struct sk_buff * -gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb) -{ - struct sk_buff *nskb; - nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_KERNEL); - if (nskb) { - u16 datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 4 + 2); - unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN); - capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN); - capimsg_setu16(s, 2, mp->ap->applid); - capimsg_setu8 (s, 4, CAPI_DATA_B3); - capimsg_setu8 (s, 5, CAPI_RESP); - capimsg_setu16(s, 6, atomic_inc_return(&mp->msgid)); - capimsg_setu32(s, 8, mp->ncci); - capimsg_setu16(s, 12, datahandle); - } - return nskb; -} - -static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) -{ - unsigned int datalen = skb->len - CAPIMSG_LEN(skb->data); - struct tty_struct *tty; - struct sk_buff *nskb; - u16 errcode, datahandle; - struct tty_ldisc *ld; - int ret = -1; - - tty = tty_port_tty_get(&mp->port); - if (!tty) { - pr_debug("capi: currently no receiver\n"); - return -1; - } - - ld = tty_ldisc_ref(tty); - if (!ld) { - /* fatal error, do not requeue */ - ret = 0; - kfree_skb(skb); - goto deref_tty; - } - - if (ld->ops->receive_buf == NULL) { - pr_debug("capi: ldisc has no receive_buf function\n"); - /* fatal error, do not requeue */ - goto free_skb; - } - if (mp->ttyinstop) { - pr_debug("capi: recv tty throttled\n"); - goto deref_ldisc; - } - - if (tty->receive_room < datalen) { - pr_debug("capi: no room in tty\n"); - goto deref_ldisc; - } - - nskb = gen_data_b3_resp_for(mp, skb); - if (!nskb) { - printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); - goto deref_ldisc; - } - - datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4); - - errcode = capi20_put_message(mp->ap, nskb); - - if (errcode == CAPI_NOERROR) { - skb_pull(skb, CAPIMSG_LEN(skb->data)); - pr_debug("capi: DATA_B3_RESP %u len=%d => ldisc\n", - datahandle, skb->len); - ld->ops->receive_buf(tty, skb->data, NULL, skb->len); - } else { - printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", - errcode); - kfree_skb(nskb); - - if (errcode == CAPI_SENDQUEUEFULL) - goto deref_ldisc; - } - -free_skb: - ret = 0; - kfree_skb(skb); - -deref_ldisc: - tty_ldisc_deref(ld); - -deref_tty: - tty_kref_put(tty); - return ret; -} - -static void handle_minor_recv(struct capiminor *mp) -{ - struct sk_buff *skb; - - while ((skb = skb_dequeue(&mp->inqueue)) != NULL) - if (handle_recv_skb(mp, skb) < 0) { - skb_queue_head(&mp->inqueue, skb); - return; - } -} - -static void handle_minor_send(struct capiminor *mp) -{ - struct tty_struct *tty; - struct sk_buff *skb; - u16 len; - u16 errcode; - u16 datahandle; - - tty = tty_port_tty_get(&mp->port); - if (!tty) - return; - - if (mp->ttyoutstop) { - pr_debug("capi: send: tty stopped\n"); - tty_kref_put(tty); - return; - } - - while (1) { - spin_lock_bh(&mp->outlock); - skb = __skb_dequeue(&mp->outqueue); - if (!skb) { - spin_unlock_bh(&mp->outlock); - break; - } - len = (u16)skb->len; - mp->outbytes -= len; - spin_unlock_bh(&mp->outlock); - - datahandle = atomic_inc_return(&mp->datahandle); - skb_push(skb, CAPI_DATA_B3_REQ_LEN); - memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN); - capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN); - capimsg_setu16(skb->data, 2, mp->ap->applid); - capimsg_setu8 (skb->data, 4, CAPI_DATA_B3); - capimsg_setu8 (skb->data, 5, CAPI_REQ); - capimsg_setu16(skb->data, 6, atomic_inc_return(&mp->msgid)); - capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */ - capimsg_setu32(skb->data, 12, (u32)(long)skb->data);/* Data32 */ - capimsg_setu16(skb->data, 16, len); /* Data length */ - capimsg_setu16(skb->data, 18, datahandle); - capimsg_setu16(skb->data, 20, 0); /* Flags */ - - if (capiminor_add_ack(mp, datahandle) < 0) { - skb_pull(skb, CAPI_DATA_B3_REQ_LEN); - - spin_lock_bh(&mp->outlock); - __skb_queue_head(&mp->outqueue, skb); - mp->outbytes += len; - spin_unlock_bh(&mp->outlock); - - break; - } - errcode = capi20_put_message(mp->ap, skb); - if (errcode == CAPI_NOERROR) { - pr_debug("capi: DATA_B3_REQ %u len=%u\n", - datahandle, len); - continue; - } - capiminor_del_ack(mp, datahandle); - - if (errcode == CAPI_SENDQUEUEFULL) { - skb_pull(skb, CAPI_DATA_B3_REQ_LEN); - - spin_lock_bh(&mp->outlock); - __skb_queue_head(&mp->outqueue, skb); - mp->outbytes += len; - spin_unlock_bh(&mp->outlock); - - break; - } - - /* ups, drop packet */ - printk(KERN_ERR "capi: put_message = %x\n", errcode); - kfree_skb(skb); - } - tty_kref_put(tty); -} - -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ -/* -------- function called by lower level -------------------------- */ - -static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) -{ - struct capidev *cdev = ap->private; -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - struct capiminor *mp; - u16 datahandle; - struct capincci *np; -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - - mutex_lock(&cdev->lock); - - if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) { - u16 info = CAPIMSG_U16(skb->data, 12); // Info field - if ((info & 0xff00) == 0) - capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); - } - if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) - capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); - - if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) { - skb_queue_tail(&cdev->recvqueue, skb); - wake_up_interruptible(&cdev->recvwait); - goto unlock_out; - } - -#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE - skb_queue_tail(&cdev->recvqueue, skb); - wake_up_interruptible(&cdev->recvwait); - -#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - - np = capincci_find(cdev, CAPIMSG_CONTROL(skb->data)); - if (!np) { - printk(KERN_ERR "BUG: capi_signal: ncci not found\n"); - skb_queue_tail(&cdev->recvqueue, skb); - wake_up_interruptible(&cdev->recvwait); - goto unlock_out; - } - - mp = np->minorp; - if (!mp) { - skb_queue_tail(&cdev->recvqueue, skb); - wake_up_interruptible(&cdev->recvwait); - goto unlock_out; - } - if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) { - datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 4 + 2); - pr_debug("capi_signal: DATA_B3_IND %u len=%d\n", - datahandle, skb->len-CAPIMSG_LEN(skb->data)); - skb_queue_tail(&mp->inqueue, skb); - - handle_minor_recv(mp); - - } else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) { - - datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4); - pr_debug("capi_signal: DATA_B3_CONF %u 0x%x\n", - datahandle, - CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 2)); - kfree_skb(skb); - capiminor_del_ack(mp, datahandle); - tty_port_tty_wakeup(&mp->port); - handle_minor_send(mp); - - } else { - /* ups, let capi application handle it :-) */ - skb_queue_tail(&cdev->recvqueue, skb); - wake_up_interruptible(&cdev->recvwait); - } -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - -unlock_out: - mutex_unlock(&cdev->lock); -} - -/* -------- file_operations for capidev ----------------------------- */ - -static ssize_t -capi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - struct capidev *cdev = file->private_data; - struct sk_buff *skb; - size_t copied; - int err; - - if (!cdev->ap.applid) - return -ENODEV; - - skb = skb_dequeue(&cdev->recvqueue); - if (!skb) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - err = wait_event_interruptible(cdev->recvwait, - (skb = skb_dequeue(&cdev->recvqueue))); - if (err) - return err; - } - if (skb->len > count) { - skb_queue_head(&cdev->recvqueue, skb); - return -EMSGSIZE; - } - if (copy_to_user(buf, skb->data, skb->len)) { - skb_queue_head(&cdev->recvqueue, skb); - return -EFAULT; - } - copied = skb->len; - - kfree_skb(skb); - - return copied; -} - -static ssize_t -capi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - struct capidev *cdev = file->private_data; - struct sk_buff *skb; - u16 mlen; - - if (!cdev->ap.applid) - return -ENODEV; - - if (count < CAPIMSG_BASELEN) - return -EINVAL; - - skb = alloc_skb(count, GFP_USER); - if (!skb) - return -ENOMEM; - - if (copy_from_user(skb_put(skb, count), buf, count)) { - kfree_skb(skb); - return -EFAULT; - } - mlen = CAPIMSG_LEN(skb->data); - if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { - if (count < CAPI_DATA_B3_REQ_LEN || - (size_t)(mlen + CAPIMSG_DATALEN(skb->data)) != count) { - kfree_skb(skb); - return -EINVAL; - } - } else { - if (mlen != count) { - kfree_skb(skb); - return -EINVAL; - } - } - CAPIMSG_SETAPPID(skb->data, cdev->ap.applid); - - if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) { - if (count < CAPI_DISCONNECT_B3_RESP_LEN) { - kfree_skb(skb); - return -EINVAL; - } - mutex_lock(&cdev->lock); - capincci_free(cdev, CAPIMSG_NCCI(skb->data)); - mutex_unlock(&cdev->lock); - } - - cdev->errcode = capi20_put_message(&cdev->ap, skb); - - if (cdev->errcode) { - kfree_skb(skb); - return -EIO; - } - return count; -} - -static __poll_t -capi_poll(struct file *file, poll_table *wait) -{ - struct capidev *cdev = file->private_data; - __poll_t mask = 0; - - if (!cdev->ap.applid) - return EPOLLERR; - - poll_wait(file, &(cdev->recvwait), wait); - mask = EPOLLOUT | EPOLLWRNORM; - if (!skb_queue_empty_lockless(&cdev->recvqueue)) - mask |= EPOLLIN | EPOLLRDNORM; - return mask; -} - -static int -capi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct capidev *cdev = file->private_data; - capi_ioctl_struct data; - int retval = -EINVAL; - void __user *argp = (void __user *)arg; - - switch (cmd) { - case CAPI_REGISTER: - mutex_lock(&cdev->lock); - - if (cdev->ap.applid) { - retval = -EEXIST; - goto register_out; - } - if (copy_from_user(&cdev->ap.rparam, argp, - sizeof(struct capi_register_params))) { - retval = -EFAULT; - goto register_out; - } - cdev->ap.private = cdev; - cdev->ap.recv_message = capi_recv_message; - cdev->errcode = capi20_register(&cdev->ap); - retval = (int)cdev->ap.applid; - if (cdev->errcode) { - cdev->ap.applid = 0; - retval = -EIO; - } - -register_out: - mutex_unlock(&cdev->lock); - return retval; - - case CAPI_GET_VERSION: - if (copy_from_user(&data.contr, argp, - sizeof(data.contr))) - return -EFAULT; - cdev->errcode = capi20_get_version(data.contr, &data.version); - if (cdev->errcode) - return -EIO; - if (copy_to_user(argp, &data.version, - sizeof(data.version))) - return -EFAULT; - return 0; - - case CAPI_GET_SERIAL: - if (copy_from_user(&data.contr, argp, - sizeof(data.contr))) - return -EFAULT; - cdev->errcode = capi20_get_serial(data.contr, data.serial); - if (cdev->errcode) - return -EIO; - if (copy_to_user(argp, data.serial, - sizeof(data.serial))) - return -EFAULT; - return 0; - - case CAPI_GET_PROFILE: - if (copy_from_user(&data.contr, argp, - sizeof(data.contr))) - return -EFAULT; - - if (data.contr == 0) { - cdev->errcode = capi20_get_profile(data.contr, &data.profile); - if (cdev->errcode) - return -EIO; - - retval = copy_to_user(argp, - &data.profile.ncontroller, - sizeof(data.profile.ncontroller)); - - } else { - cdev->errcode = capi20_get_profile(data.contr, &data.profile); - if (cdev->errcode) - return -EIO; - - retval = copy_to_user(argp, &data.profile, - sizeof(data.profile)); - } - if (retval) - return -EFAULT; - return 0; - - case CAPI_GET_MANUFACTURER: - if (copy_from_user(&data.contr, argp, - sizeof(data.contr))) - return -EFAULT; - cdev->errcode = capi20_get_manufacturer(data.contr, data.manufacturer); - if (cdev->errcode) - return -EIO; - - if (copy_to_user(argp, data.manufacturer, - sizeof(data.manufacturer))) - return -EFAULT; - - return 0; - - case CAPI_GET_ERRCODE: - data.errcode = cdev->errcode; - cdev->errcode = CAPI_NOERROR; - if (arg) { - if (copy_to_user(argp, &data.errcode, - sizeof(data.errcode))) - return -EFAULT; - } - return data.errcode; - - case CAPI_INSTALLED: - if (capi20_isinstalled() == CAPI_NOERROR) - return 0; - return -ENXIO; - - case CAPI_MANUFACTURER_CMD: { - struct capi_manufacturer_cmd mcmd; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (copy_from_user(&mcmd, argp, sizeof(mcmd))) - return -EFAULT; - return capi20_manufacturer(mcmd.cmd, mcmd.data); - } - case CAPI_SET_FLAGS: - case CAPI_CLR_FLAGS: { - unsigned userflags; - - if (copy_from_user(&userflags, argp, sizeof(userflags))) - return -EFAULT; - - mutex_lock(&cdev->lock); - if (cmd == CAPI_SET_FLAGS) - cdev->userflags |= userflags; - else - cdev->userflags &= ~userflags; - mutex_unlock(&cdev->lock); - return 0; - } - case CAPI_GET_FLAGS: - if (copy_to_user(argp, &cdev->userflags, - sizeof(cdev->userflags))) - return -EFAULT; - return 0; - -#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE - case CAPI_NCCI_OPENCOUNT: - return 0; - -#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - case CAPI_NCCI_OPENCOUNT: { - struct capincci *nccip; - unsigned ncci; - int count = 0; - - if (copy_from_user(&ncci, argp, sizeof(ncci))) - return -EFAULT; - - mutex_lock(&cdev->lock); - nccip = capincci_find(cdev, (u32)ncci); - if (nccip) - count = capincci_minor_opencount(nccip); - mutex_unlock(&cdev->lock); - return count; - } - - case CAPI_NCCI_GETUNIT: { - struct capincci *nccip; - struct capiminor *mp; - unsigned ncci; - int unit = -ESRCH; - - if (copy_from_user(&ncci, argp, sizeof(ncci))) - return -EFAULT; - - mutex_lock(&cdev->lock); - nccip = capincci_find(cdev, (u32)ncci); - if (nccip) { - mp = nccip->minorp; - if (mp) - unit = mp->minor; - } - mutex_unlock(&cdev->lock); - return unit; - } -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - - default: - return -EINVAL; - } -} - -static long -capi_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int ret; - - mutex_lock(&capi_mutex); - ret = capi_ioctl(file, cmd, arg); - mutex_unlock(&capi_mutex); - - return ret; -} - -#ifdef CONFIG_COMPAT -static long -capi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int ret; - - if (cmd == CAPI_MANUFACTURER_CMD) { - struct { - compat_ulong_t cmd; - compat_uptr_t data; - } mcmd32; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (copy_from_user(&mcmd32, compat_ptr(arg), sizeof(mcmd32))) - return -EFAULT; - - mutex_lock(&capi_mutex); - ret = capi20_manufacturer(mcmd32.cmd, compat_ptr(mcmd32.data)); - mutex_unlock(&capi_mutex); - - return ret; - } - - return capi_unlocked_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static int capi_open(struct inode *inode, struct file *file) -{ - struct capidev *cdev; - - cdev = kzalloc_obj(*cdev); - if (!cdev) - return -ENOMEM; - - mutex_init(&cdev->lock); - skb_queue_head_init(&cdev->recvqueue); - init_waitqueue_head(&cdev->recvwait); - INIT_LIST_HEAD(&cdev->nccis); - file->private_data = cdev; - - mutex_lock(&capidev_list_lock); - list_add_tail(&cdev->list, &capidev_list); - mutex_unlock(&capidev_list_lock); - - return stream_open(inode, file); -} - -static int capi_release(struct inode *inode, struct file *file) -{ - struct capidev *cdev = file->private_data; - - mutex_lock(&capidev_list_lock); - list_del(&cdev->list); - mutex_unlock(&capidev_list_lock); - - if (cdev->ap.applid) - capi20_release(&cdev->ap); - skb_queue_purge(&cdev->recvqueue); - capincci_free(cdev, 0xffffffff); - - kfree(cdev); - return 0; -} - -static const struct file_operations capi_fops = -{ - .owner = THIS_MODULE, - .read = capi_read, - .write = capi_write, - .poll = capi_poll, - .unlocked_ioctl = capi_unlocked_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = capi_compat_ioctl, -#endif - .open = capi_open, - .release = capi_release, -}; - -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE -/* -------- tty_operations for capincci ----------------------------- */ - -static int -capinc_tty_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct capiminor *mp = capiminor_get(tty->index); - int ret = tty_standard_install(driver, tty); - - if (ret == 0) - tty->driver_data = mp; - else - capiminor_put(mp); - return ret; -} - -static void capinc_tty_cleanup(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - tty->driver_data = NULL; - capiminor_put(mp); -} - -static int capinc_tty_open(struct tty_struct *tty, struct file *filp) -{ - struct capiminor *mp = tty->driver_data; - int err; - - err = tty_port_open(&mp->port, tty, filp); - if (err) - return err; - - handle_minor_recv(mp); - return 0; -} - -static void capinc_tty_close(struct tty_struct *tty, struct file *filp) -{ - struct capiminor *mp = tty->driver_data; - - tty_port_close(&mp->port, tty, filp); -} - -static ssize_t capinc_tty_write(struct tty_struct *tty, const u8 *buf, - size_t count) -{ - struct capiminor *mp = tty->driver_data; - struct sk_buff *skb; - - pr_debug("capinc_tty_write(count=%zu)\n", count); - - spin_lock_bh(&mp->outlock); - skb = mp->outskb; - if (skb) { - mp->outskb = NULL; - __skb_queue_tail(&mp->outqueue, skb); - mp->outbytes += skb->len; - } - - skb = alloc_skb(CAPI_DATA_B3_REQ_LEN + count, GFP_ATOMIC); - if (!skb) { - printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n"); - spin_unlock_bh(&mp->outlock); - return -ENOMEM; - } - - skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); - skb_put_data(skb, buf, count); - - __skb_queue_tail(&mp->outqueue, skb); - mp->outbytes += skb->len; - spin_unlock_bh(&mp->outlock); - - handle_minor_send(mp); - - return count; -} - -static int capinc_tty_put_char(struct tty_struct *tty, u8 ch) -{ - struct capiminor *mp = tty->driver_data; - bool invoke_send = false; - struct sk_buff *skb; - int ret = 1; - - pr_debug("capinc_put_char(%u)\n", ch); - - spin_lock_bh(&mp->outlock); - skb = mp->outskb; - if (skb) { - if (skb_tailroom(skb) > 0) { - skb_put_u8(skb, ch); - goto unlock_out; - } - mp->outskb = NULL; - __skb_queue_tail(&mp->outqueue, skb); - mp->outbytes += skb->len; - invoke_send = true; - } - - skb = alloc_skb(CAPI_DATA_B3_REQ_LEN + CAPI_MAX_BLKSIZE, GFP_ATOMIC); - if (skb) { - skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); - skb_put_u8(skb, ch); - mp->outskb = skb; - } else { - printk(KERN_ERR "capinc_put_char: char %u lost\n", ch); - ret = 0; - } - -unlock_out: - spin_unlock_bh(&mp->outlock); - - if (invoke_send) - handle_minor_send(mp); - - return ret; -} - -static void capinc_tty_flush_chars(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - struct sk_buff *skb; - - spin_lock_bh(&mp->outlock); - skb = mp->outskb; - if (skb) { - mp->outskb = NULL; - __skb_queue_tail(&mp->outqueue, skb); - mp->outbytes += skb->len; - spin_unlock_bh(&mp->outlock); - - handle_minor_send(mp); - } else - spin_unlock_bh(&mp->outlock); - - handle_minor_recv(mp); -} - -static unsigned int capinc_tty_write_room(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - unsigned int room; - - room = CAPINC_MAX_SENDQUEUE-skb_queue_len(&mp->outqueue); - room *= CAPI_MAX_BLKSIZE; - pr_debug("capinc_tty_write_room = %u\n", room); - return room; -} - -static unsigned int capinc_tty_chars_in_buffer(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - - pr_debug("capinc_tty_chars_in_buffer = %d nack=%d sq=%d rq=%d\n", - mp->outbytes, mp->nack, - skb_queue_len(&mp->outqueue), - skb_queue_len(&mp->inqueue)); - return mp->outbytes; -} - -static void capinc_tty_throttle(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - mp->ttyinstop = 1; -} - -static void capinc_tty_unthrottle(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - - mp->ttyinstop = 0; - handle_minor_recv(mp); -} - -static void capinc_tty_stop(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - - mp->ttyoutstop = 1; -} - -static void capinc_tty_start(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - - mp->ttyoutstop = 0; - handle_minor_send(mp); -} - -static void capinc_tty_hangup(struct tty_struct *tty) -{ - struct capiminor *mp = tty->driver_data; - - tty_port_hangup(&mp->port); -} - -static void capinc_tty_send_xchar(struct tty_struct *tty, u8 ch) -{ - pr_debug("capinc_tty_send_xchar(%u)\n", ch); -} - -static const struct tty_operations capinc_ops = { - .open = capinc_tty_open, - .close = capinc_tty_close, - .write = capinc_tty_write, - .put_char = capinc_tty_put_char, - .flush_chars = capinc_tty_flush_chars, - .write_room = capinc_tty_write_room, - .chars_in_buffer = capinc_tty_chars_in_buffer, - .throttle = capinc_tty_throttle, - .unthrottle = capinc_tty_unthrottle, - .stop = capinc_tty_stop, - .start = capinc_tty_start, - .hangup = capinc_tty_hangup, - .send_xchar = capinc_tty_send_xchar, - .install = capinc_tty_install, - .cleanup = capinc_tty_cleanup, -}; - -static int __init capinc_tty_init(void) -{ - struct tty_driver *drv; - int err; - - if (capi_ttyminors > CAPINC_MAX_PORTS) - capi_ttyminors = CAPINC_MAX_PORTS; - if (capi_ttyminors <= 0) - capi_ttyminors = CAPINC_NR_PORTS; - - capiminors = kzalloc_objs(struct capiminor *, capi_ttyminors); - if (!capiminors) - return -ENOMEM; - - drv = tty_alloc_driver(capi_ttyminors, TTY_DRIVER_REAL_RAW | - TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_DYNAMIC_DEV); - if (IS_ERR(drv)) { - kfree(capiminors); - return PTR_ERR(drv); - } - drv->driver_name = "capi_nc"; - drv->name = "capi!"; - drv->major = 0; - drv->minor_start = 0; - drv->type = TTY_DRIVER_TYPE_SERIAL; - drv->subtype = SERIAL_TYPE_NORMAL; - drv->init_termios = tty_std_termios; - drv->init_termios.c_iflag = ICRNL; - drv->init_termios.c_oflag = OPOST | ONLCR; - drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - drv->init_termios.c_lflag = 0; - tty_set_operations(drv, &capinc_ops); - - err = tty_register_driver(drv); - if (err) { - tty_driver_kref_put(drv); - kfree(capiminors); - printk(KERN_ERR "Couldn't register capi_nc driver\n"); - return err; - } - capinc_tty_driver = drv; - return 0; -} - -static void __exit capinc_tty_exit(void) -{ - tty_unregister_driver(capinc_tty_driver); - tty_driver_kref_put(capinc_tty_driver); - kfree(capiminors); -} - -#else /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ - -static inline int capinc_tty_init(void) -{ - return 0; -} - -static inline void capinc_tty_exit(void) { } - -#endif /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ - -/* -------- /proc functions ----------------------------------------- */ - -/* - * /proc/capi/capi20: - * minor applid nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt - */ -static int __maybe_unused capi20_proc_show(struct seq_file *m, void *v) -{ - struct capidev *cdev; - struct list_head *l; - - mutex_lock(&capidev_list_lock); - list_for_each(l, &capidev_list) { - cdev = list_entry(l, struct capidev, list); - seq_printf(m, "0 %d %lu %lu %lu %lu\n", - cdev->ap.applid, - cdev->ap.nrecvctlpkt, - cdev->ap.nrecvdatapkt, - cdev->ap.nsentctlpkt, - cdev->ap.nsentdatapkt); - } - mutex_unlock(&capidev_list_lock); - return 0; -} - -/* - * /proc/capi/capi20ncci: - * applid ncci - */ -static int __maybe_unused capi20ncci_proc_show(struct seq_file *m, void *v) -{ - struct capidev *cdev; - struct capincci *np; - - mutex_lock(&capidev_list_lock); - list_for_each_entry(cdev, &capidev_list, list) { - mutex_lock(&cdev->lock); - list_for_each_entry(np, &cdev->nccis, list) - seq_printf(m, "%d 0x%x\n", cdev->ap.applid, np->ncci); - mutex_unlock(&cdev->lock); - } - mutex_unlock(&capidev_list_lock); - return 0; -} - -static void __init proc_init(void) -{ - proc_create_single("capi/capi20", 0, NULL, capi20_proc_show); - proc_create_single("capi/capi20ncci", 0, NULL, capi20ncci_proc_show); -} - -static void __exit proc_exit(void) -{ - remove_proc_entry("capi/capi20", NULL); - remove_proc_entry("capi/capi20ncci", NULL); -} - -/* -------- init function and module interface ---------------------- */ - - -static int __init capi_init(void) -{ - const char *compileinfo; - int major_ret; - int ret; - - ret = kcapi_init(); - if (ret) - return ret; - - major_ret = register_chrdev(capi_major, "capi20", &capi_fops); - if (major_ret < 0) { - printk(KERN_ERR "capi20: unable to get major %d\n", capi_major); - kcapi_exit(); - return major_ret; - } - - ret = class_register(&capi_class); - if (ret) { - unregister_chrdev(capi_major, "capi20"); - kcapi_exit(); - return ret; - } - - device_create(&capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi20"); - - if (capinc_tty_init() < 0) { - device_destroy(&capi_class, MKDEV(capi_major, 0)); - class_unregister(&capi_class); - unregister_chrdev(capi_major, "capi20"); - kcapi_exit(); - return -ENOMEM; - } - - proc_init(); - -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - compileinfo = " (middleware)"; -#else - compileinfo = " (no middleware)"; -#endif - printk(KERN_NOTICE "CAPI 2.0 started up with major %d%s\n", - capi_major, compileinfo); - - return 0; -} - -static void __exit capi_exit(void) -{ - proc_exit(); - - device_destroy(&capi_class, MKDEV(capi_major, 0)); - class_unregister(&capi_class); - unregister_chrdev(capi_major, "capi20"); - - capinc_tty_exit(); - - kcapi_exit(); -} - -module_init(capi_init); -module_exit(capi_exit); diff --git a/drivers/isdn/capi/capiutil.c b/drivers/isdn/capi/capiutil.c deleted file mode 100644 index eec9b36343b7..000000000000 --- a/drivers/isdn/capi/capiutil.c +++ /dev/null @@ -1,677 +0,0 @@ -/* $Id: capiutil.c,v 1.13.6.4 2001/09/23 22:24:33 kai Exp $ - * - * CAPI 2.0 convert capi message to capi message struct - * - * From CAPI 2.0 Development Kit AVM 1995 (msg.c) - * Rewritten for Linux 1996 by Carsten Paeth - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "kcapi.h" - -/* from CAPI2.0 DDK AVM Berlin GmbH */ - -typedef struct { - int typ; - size_t off; -} _cdef; - -#define _CBYTE 1 -#define _CWORD 2 -#define _CDWORD 3 -#define _CSTRUCT 4 -#define _CMSTRUCT 5 -#define _CEND 6 - -static _cdef cdef[] = -{ - /*00 */ - {_CEND}, - /*01 */ - {_CEND}, - /*02 */ - {_CEND}, - /*03 */ - {_CDWORD, offsetof(_cmsg, adr.adrController)}, - /*04 */ - {_CMSTRUCT, offsetof(_cmsg, AdditionalInfo)}, - /*05 */ - {_CSTRUCT, offsetof(_cmsg, B1configuration)}, - /*06 */ - {_CWORD, offsetof(_cmsg, B1protocol)}, - /*07 */ - {_CSTRUCT, offsetof(_cmsg, B2configuration)}, - /*08 */ - {_CWORD, offsetof(_cmsg, B2protocol)}, - /*09 */ - {_CSTRUCT, offsetof(_cmsg, B3configuration)}, - /*0a */ - {_CWORD, offsetof(_cmsg, B3protocol)}, - /*0b */ - {_CSTRUCT, offsetof(_cmsg, BC)}, - /*0c */ - {_CSTRUCT, offsetof(_cmsg, BChannelinformation)}, - /*0d */ - {_CMSTRUCT, offsetof(_cmsg, BProtocol)}, - /*0e */ - {_CSTRUCT, offsetof(_cmsg, CalledPartyNumber)}, - /*0f */ - {_CSTRUCT, offsetof(_cmsg, CalledPartySubaddress)}, - /*10 */ - {_CSTRUCT, offsetof(_cmsg, CallingPartyNumber)}, - /*11 */ - {_CSTRUCT, offsetof(_cmsg, CallingPartySubaddress)}, - /*12 */ - {_CDWORD, offsetof(_cmsg, CIPmask)}, - /*13 */ - {_CDWORD, offsetof(_cmsg, CIPmask2)}, - /*14 */ - {_CWORD, offsetof(_cmsg, CIPValue)}, - /*15 */ - {_CDWORD, offsetof(_cmsg, Class)}, - /*16 */ - {_CSTRUCT, offsetof(_cmsg, ConnectedNumber)}, - /*17 */ - {_CSTRUCT, offsetof(_cmsg, ConnectedSubaddress)}, - /*18 */ - {_CDWORD, offsetof(_cmsg, Data)}, - /*19 */ - {_CWORD, offsetof(_cmsg, DataHandle)}, - /*1a */ - {_CWORD, offsetof(_cmsg, DataLength)}, - /*1b */ - {_CSTRUCT, offsetof(_cmsg, FacilityConfirmationParameter)}, - /*1c */ - {_CSTRUCT, offsetof(_cmsg, Facilitydataarray)}, - /*1d */ - {_CSTRUCT, offsetof(_cmsg, FacilityIndicationParameter)}, - /*1e */ - {_CSTRUCT, offsetof(_cmsg, FacilityRequestParameter)}, - /*1f */ - {_CWORD, offsetof(_cmsg, FacilitySelector)}, - /*20 */ - {_CWORD, offsetof(_cmsg, Flags)}, - /*21 */ - {_CDWORD, offsetof(_cmsg, Function)}, - /*22 */ - {_CSTRUCT, offsetof(_cmsg, HLC)}, - /*23 */ - {_CWORD, offsetof(_cmsg, Info)}, - /*24 */ - {_CSTRUCT, offsetof(_cmsg, InfoElement)}, - /*25 */ - {_CDWORD, offsetof(_cmsg, InfoMask)}, - /*26 */ - {_CWORD, offsetof(_cmsg, InfoNumber)}, - /*27 */ - {_CSTRUCT, offsetof(_cmsg, Keypadfacility)}, - /*28 */ - {_CSTRUCT, offsetof(_cmsg, LLC)}, - /*29 */ - {_CSTRUCT, offsetof(_cmsg, ManuData)}, - /*2a */ - {_CDWORD, offsetof(_cmsg, ManuID)}, - /*2b */ - {_CSTRUCT, offsetof(_cmsg, NCPI)}, - /*2c */ - {_CWORD, offsetof(_cmsg, Reason)}, - /*2d */ - {_CWORD, offsetof(_cmsg, Reason_B3)}, - /*2e */ - {_CWORD, offsetof(_cmsg, Reject)}, - /*2f */ - {_CSTRUCT, offsetof(_cmsg, Useruserdata)} -}; - -static unsigned char *cpars[] = -{ - /* ALERT_REQ */ [0x01] = "\x03\x04\x0c\x27\x2f\x1c\x01\x01", - /* CONNECT_REQ */ [0x02] = "\x03\x14\x0e\x10\x0f\x11\x0d\x06\x08\x0a\x05\x07\x09\x01\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01", - /* DISCONNECT_REQ */ [0x04] = "\x03\x04\x0c\x27\x2f\x1c\x01\x01", - /* LISTEN_REQ */ [0x05] = "\x03\x25\x12\x13\x10\x11\x01", - /* INFO_REQ */ [0x08] = "\x03\x0e\x04\x0c\x27\x2f\x1c\x01\x01", - /* FACILITY_REQ */ [0x09] = "\x03\x1f\x1e\x01", - /* SELECT_B_PROTOCOL_REQ */ [0x0a] = "\x03\x0d\x06\x08\x0a\x05\x07\x09\x01\x01", - /* CONNECT_B3_REQ */ [0x0b] = "\x03\x2b\x01", - /* DISCONNECT_B3_REQ */ [0x0d] = "\x03\x2b\x01", - /* DATA_B3_REQ */ [0x0f] = "\x03\x18\x1a\x19\x20\x01", - /* RESET_B3_REQ */ [0x10] = "\x03\x2b\x01", - /* ALERT_CONF */ [0x13] = "\x03\x23\x01", - /* CONNECT_CONF */ [0x14] = "\x03\x23\x01", - /* DISCONNECT_CONF */ [0x16] = "\x03\x23\x01", - /* LISTEN_CONF */ [0x17] = "\x03\x23\x01", - /* MANUFACTURER_REQ */ [0x18] = "\x03\x2a\x15\x21\x29\x01", - /* INFO_CONF */ [0x1a] = "\x03\x23\x01", - /* FACILITY_CONF */ [0x1b] = "\x03\x23\x1f\x1b\x01", - /* SELECT_B_PROTOCOL_CONF */ [0x1c] = "\x03\x23\x01", - /* CONNECT_B3_CONF */ [0x1d] = "\x03\x23\x01", - /* DISCONNECT_B3_CONF */ [0x1f] = "\x03\x23\x01", - /* DATA_B3_CONF */ [0x21] = "\x03\x19\x23\x01", - /* RESET_B3_CONF */ [0x22] = "\x03\x23\x01", - /* CONNECT_IND */ [0x26] = "\x03\x14\x0e\x10\x0f\x11\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01", - /* CONNECT_ACTIVE_IND */ [0x27] = "\x03\x16\x17\x28\x01", - /* DISCONNECT_IND */ [0x28] = "\x03\x2c\x01", - /* MANUFACTURER_CONF */ [0x2a] = "\x03\x2a\x15\x21\x29\x01", - /* INFO_IND */ [0x2c] = "\x03\x26\x24\x01", - /* FACILITY_IND */ [0x2d] = "\x03\x1f\x1d\x01", - /* CONNECT_B3_IND */ [0x2f] = "\x03\x2b\x01", - /* CONNECT_B3_ACTIVE_IND */ [0x30] = "\x03\x2b\x01", - /* DISCONNECT_B3_IND */ [0x31] = "\x03\x2d\x2b\x01", - /* DATA_B3_IND */ [0x33] = "\x03\x18\x1a\x19\x20\x01", - /* RESET_B3_IND */ [0x34] = "\x03\x2b\x01", - /* CONNECT_B3_T90_ACTIVE_IND */ [0x35] = "\x03\x2b\x01", - /* CONNECT_RESP */ [0x38] = "\x03\x2e\x0d\x06\x08\x0a\x05\x07\x09\x01\x16\x17\x28\x04\x0c\x27\x2f\x1c\x01\x01", - /* CONNECT_ACTIVE_RESP */ [0x39] = "\x03\x01", - /* DISCONNECT_RESP */ [0x3a] = "\x03\x01", - /* MANUFACTURER_IND */ [0x3c] = "\x03\x2a\x15\x21\x29\x01", - /* INFO_RESP */ [0x3e] = "\x03\x01", - /* FACILITY_RESP */ [0x3f] = "\x03\x1f\x01", - /* CONNECT_B3_RESP */ [0x41] = "\x03\x2e\x2b\x01", - /* CONNECT_B3_ACTIVE_RESP */ [0x42] = "\x03\x01", - /* DISCONNECT_B3_RESP */ [0x43] = "\x03\x01", - /* DATA_B3_RESP */ [0x45] = "\x03\x19\x01", - /* RESET_B3_RESP */ [0x46] = "\x03\x01", - /* CONNECT_B3_T90_ACTIVE_RESP */ [0x47] = "\x03\x01", - /* MANUFACTURER_RESP */ [0x4e] = "\x03\x2a\x15\x21\x29\x01", -}; - -/*-------------------------------------------------------*/ - -#define byteTLcpy(x, y) *(u8 *)(x) = *(u8 *)(y); -#define wordTLcpy(x, y) *(u16 *)(x) = *(u16 *)(y); -#define dwordTLcpy(x, y) memcpy(x, y, 4); -#define structTLcpy(x, y, l) memcpy(x, y, l) -#define structTLcpyovl(x, y, l) memmove(x, y, l) - -#define byteTRcpy(x, y) *(u8 *)(y) = *(u8 *)(x); -#define wordTRcpy(x, y) *(u16 *)(y) = *(u16 *)(x); -#define dwordTRcpy(x, y) memcpy(y, x, 4); -#define structTRcpy(x, y, l) memcpy(y, x, l) -#define structTRcpyovl(x, y, l) memmove(y, x, l) - -/*-------------------------------------------------------*/ -static unsigned command_2_index(u8 c, u8 sc) -{ - if (c & 0x80) - c = 0x9 + (c & 0x0f); - else if (c == 0x41) - c = 0x9 + 0x1; - if (c > 0x18) - c = 0x00; - return (sc & 3) * (0x9 + 0x9) + c; -} - -/** - * capi_cmd2par() - find parameter string for CAPI 2.0 command/subcommand - * @cmd: command number - * @subcmd: subcommand number - * - * Return value: static string, NULL if command/subcommand unknown - */ - -static unsigned char *capi_cmd2par(u8 cmd, u8 subcmd) -{ - return cpars[command_2_index(cmd, subcmd)]; -} - -/*-------------------------------------------------------*/ -#define TYP (cdef[cmsg->par[cmsg->p]].typ) -#define OFF (((u8 *)cmsg) + cdef[cmsg->par[cmsg->p]].off) - -static void jumpcstruct(_cmsg *cmsg) -{ - unsigned layer; - for (cmsg->p++, layer = 1; layer;) { - /* $$$$$ assert (cmsg->p); */ - cmsg->p++; - switch (TYP) { - case _CMSTRUCT: - layer++; - break; - case _CEND: - layer--; - break; - } - } -} - -/*-------------------------------------------------------*/ - -static char *mnames[] = -{ - [0x01] = "ALERT_REQ", - [0x02] = "CONNECT_REQ", - [0x04] = "DISCONNECT_REQ", - [0x05] = "LISTEN_REQ", - [0x08] = "INFO_REQ", - [0x09] = "FACILITY_REQ", - [0x0a] = "SELECT_B_PROTOCOL_REQ", - [0x0b] = "CONNECT_B3_REQ", - [0x0d] = "DISCONNECT_B3_REQ", - [0x0f] = "DATA_B3_REQ", - [0x10] = "RESET_B3_REQ", - [0x13] = "ALERT_CONF", - [0x14] = "CONNECT_CONF", - [0x16] = "DISCONNECT_CONF", - [0x17] = "LISTEN_CONF", - [0x18] = "MANUFACTURER_REQ", - [0x1a] = "INFO_CONF", - [0x1b] = "FACILITY_CONF", - [0x1c] = "SELECT_B_PROTOCOL_CONF", - [0x1d] = "CONNECT_B3_CONF", - [0x1f] = "DISCONNECT_B3_CONF", - [0x21] = "DATA_B3_CONF", - [0x22] = "RESET_B3_CONF", - [0x26] = "CONNECT_IND", - [0x27] = "CONNECT_ACTIVE_IND", - [0x28] = "DISCONNECT_IND", - [0x2a] = "MANUFACTURER_CONF", - [0x2c] = "INFO_IND", - [0x2d] = "FACILITY_IND", - [0x2f] = "CONNECT_B3_IND", - [0x30] = "CONNECT_B3_ACTIVE_IND", - [0x31] = "DISCONNECT_B3_IND", - [0x33] = "DATA_B3_IND", - [0x34] = "RESET_B3_IND", - [0x35] = "CONNECT_B3_T90_ACTIVE_IND", - [0x38] = "CONNECT_RESP", - [0x39] = "CONNECT_ACTIVE_RESP", - [0x3a] = "DISCONNECT_RESP", - [0x3c] = "MANUFACTURER_IND", - [0x3e] = "INFO_RESP", - [0x3f] = "FACILITY_RESP", - [0x41] = "CONNECT_B3_RESP", - [0x42] = "CONNECT_B3_ACTIVE_RESP", - [0x43] = "DISCONNECT_B3_RESP", - [0x45] = "DATA_B3_RESP", - [0x46] = "RESET_B3_RESP", - [0x47] = "CONNECT_B3_T90_ACTIVE_RESP", - [0x4e] = "MANUFACTURER_RESP" -}; - -/** - * capi_cmd2str() - convert CAPI 2.0 command/subcommand number to name - * @cmd: command number - * @subcmd: subcommand number - * - * Return value: static string - */ - -char *capi_cmd2str(u8 cmd, u8 subcmd) -{ - char *result; - - result = mnames[command_2_index(cmd, subcmd)]; - if (result == NULL) - result = "INVALID_COMMAND"; - return result; -} - - -/*-------------------------------------------------------*/ - -#ifdef CONFIG_CAPI_TRACE - -/*-------------------------------------------------------*/ - -static char *pnames[] = -{ - /*00 */ NULL, - /*01 */ NULL, - /*02 */ NULL, - /*03 */ "Controller/PLCI/NCCI", - /*04 */ "AdditionalInfo", - /*05 */ "B1configuration", - /*06 */ "B1protocol", - /*07 */ "B2configuration", - /*08 */ "B2protocol", - /*09 */ "B3configuration", - /*0a */ "B3protocol", - /*0b */ "BC", - /*0c */ "BChannelinformation", - /*0d */ "BProtocol", - /*0e */ "CalledPartyNumber", - /*0f */ "CalledPartySubaddress", - /*10 */ "CallingPartyNumber", - /*11 */ "CallingPartySubaddress", - /*12 */ "CIPmask", - /*13 */ "CIPmask2", - /*14 */ "CIPValue", - /*15 */ "Class", - /*16 */ "ConnectedNumber", - /*17 */ "ConnectedSubaddress", - /*18 */ "Data32", - /*19 */ "DataHandle", - /*1a */ "DataLength", - /*1b */ "FacilityConfirmationParameter", - /*1c */ "Facilitydataarray", - /*1d */ "FacilityIndicationParameter", - /*1e */ "FacilityRequestParameter", - /*1f */ "FacilitySelector", - /*20 */ "Flags", - /*21 */ "Function", - /*22 */ "HLC", - /*23 */ "Info", - /*24 */ "InfoElement", - /*25 */ "InfoMask", - /*26 */ "InfoNumber", - /*27 */ "Keypadfacility", - /*28 */ "LLC", - /*29 */ "ManuData", - /*2a */ "ManuID", - /*2b */ "NCPI", - /*2c */ "Reason", - /*2d */ "Reason_B3", - /*2e */ "Reject", - /*2f */ "Useruserdata" -}; - -#include - -/*-------------------------------------------------------*/ -static _cdebbuf *bufprint(_cdebbuf *cdb, char *fmt, ...) -{ - va_list f; - size_t n, r; - - if (!cdb) - return NULL; - va_start(f, fmt); - r = cdb->size - cdb->pos; - n = vsnprintf(cdb->p, r, fmt, f); - va_end(f); - if (n >= r) { - /* truncated, need bigger buffer */ - size_t ns = 2 * cdb->size; - u_char *nb; - - while ((ns - cdb->pos) <= n) - ns *= 2; - nb = kmalloc(ns, GFP_ATOMIC); - if (!nb) { - cdebbuf_free(cdb); - return NULL; - } - memcpy(nb, cdb->buf, cdb->pos); - kfree(cdb->buf); - nb[cdb->pos] = 0; - cdb->buf = nb; - cdb->p = cdb->buf + cdb->pos; - cdb->size = ns; - va_start(f, fmt); - r = cdb->size - cdb->pos; - n = vsnprintf(cdb->p, r, fmt, f); - va_end(f); - } - cdb->p += n; - cdb->pos += n; - return cdb; -} - -static _cdebbuf *printstructlen(_cdebbuf *cdb, u8 *m, unsigned len) -{ - unsigned hex = 0; - - if (!cdb) - return NULL; - for (; len; len--, m++) - if (isalnum(*m) || *m == ' ') { - if (hex) - cdb = bufprint(cdb, ">"); - cdb = bufprint(cdb, "%c", *m); - hex = 0; - } else { - if (!hex) - cdb = bufprint(cdb, "<%02x", *m); - else - cdb = bufprint(cdb, " %02x", *m); - hex = 1; - } - if (hex) - cdb = bufprint(cdb, ">"); - return cdb; -} - -static _cdebbuf *printstruct(_cdebbuf *cdb, u8 *m) -{ - unsigned len; - - if (m[0] != 0xff) { - len = m[0]; - m += 1; - } else { - len = ((u16 *) (m + 1))[0]; - m += 3; - } - cdb = printstructlen(cdb, m, len); - return cdb; -} - -/*-------------------------------------------------------*/ -#define NAME (pnames[cmsg->par[cmsg->p]]) - -static _cdebbuf *protocol_message_2_pars(_cdebbuf *cdb, _cmsg *cmsg, int level) -{ - if (!cmsg->par) - return NULL; /* invalid command/subcommand */ - - for (; TYP != _CEND; cmsg->p++) { - int slen = 29 + 3 - level; - int i; - - if (!cdb) - return NULL; - cdb = bufprint(cdb, " "); - for (i = 0; i < level - 1; i++) - cdb = bufprint(cdb, " "); - - switch (TYP) { - case _CBYTE: - cdb = bufprint(cdb, "%-*s = 0x%x\n", slen, NAME, *(u8 *) (cmsg->m + cmsg->l)); - cmsg->l++; - break; - case _CWORD: - cdb = bufprint(cdb, "%-*s = 0x%x\n", slen, NAME, *(u16 *) (cmsg->m + cmsg->l)); - cmsg->l += 2; - break; - case _CDWORD: - cdb = bufprint(cdb, "%-*s = 0x%lx\n", slen, NAME, *(u32 *) (cmsg->m + cmsg->l)); - cmsg->l += 4; - break; - case _CSTRUCT: - cdb = bufprint(cdb, "%-*s = ", slen, NAME); - if (cmsg->m[cmsg->l] == '\0') - cdb = bufprint(cdb, "default"); - else - cdb = printstruct(cdb, cmsg->m + cmsg->l); - cdb = bufprint(cdb, "\n"); - if (cmsg->m[cmsg->l] != 0xff) - cmsg->l += 1 + cmsg->m[cmsg->l]; - else - cmsg->l += 3 + *(u16 *) (cmsg->m + cmsg->l + 1); - - break; - - case _CMSTRUCT: -/*----- Metastruktur 0 -----*/ - if (cmsg->m[cmsg->l] == '\0') { - cdb = bufprint(cdb, "%-*s = default\n", slen, NAME); - cmsg->l++; - jumpcstruct(cmsg); - } else { - char *name = NAME; - unsigned _l = cmsg->l; - cdb = bufprint(cdb, "%-*s\n", slen, name); - cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1; - cmsg->p++; - cdb = protocol_message_2_pars(cdb, cmsg, level + 1); - } - break; - } - } - return cdb; -} -/*-------------------------------------------------------*/ - -static _cdebbuf *g_debbuf; -static u_long g_debbuf_lock; -static _cmsg *g_cmsg; - -static _cdebbuf *cdebbuf_alloc(void) -{ - _cdebbuf *cdb; - - if (likely(!test_and_set_bit(1, &g_debbuf_lock))) { - cdb = g_debbuf; - goto init; - } else - cdb = kmalloc_obj(_cdebbuf, GFP_ATOMIC); - if (!cdb) - return NULL; - cdb->buf = kmalloc(CDEBUG_SIZE, GFP_ATOMIC); - if (!cdb->buf) { - kfree(cdb); - return NULL; - } - cdb->size = CDEBUG_SIZE; -init: - cdb->buf[0] = 0; - cdb->p = cdb->buf; - cdb->pos = 0; - return cdb; -} - -/** - * cdebbuf_free() - free CAPI debug buffer - * @cdb: buffer to free - */ - -void cdebbuf_free(_cdebbuf *cdb) -{ - if (likely(cdb == g_debbuf)) { - test_and_clear_bit(1, &g_debbuf_lock); - return; - } - if (likely(cdb)) - kfree(cdb->buf); - kfree(cdb); -} - - -/** - * capi_message2str() - format CAPI 2.0 message for printing - * @msg: CAPI 2.0 message - * - * Allocates a CAPI debug buffer and fills it with a printable representation - * of the CAPI 2.0 message in @msg. - * Return value: allocated debug buffer, NULL on error - * The returned buffer should be freed by a call to cdebbuf_free() after use. - */ - -_cdebbuf *capi_message2str(u8 *msg) -{ - _cdebbuf *cdb; - _cmsg *cmsg; - - cdb = cdebbuf_alloc(); - if (unlikely(!cdb)) - return NULL; - if (likely(cdb == g_debbuf)) - cmsg = g_cmsg; - else - cmsg = kmalloc_obj(_cmsg, GFP_ATOMIC); - if (unlikely(!cmsg)) { - cdebbuf_free(cdb); - return NULL; - } - cmsg->m = msg; - cmsg->l = 8; - cmsg->p = 0; - byteTRcpy(cmsg->m + 4, &cmsg->Command); - byteTRcpy(cmsg->m + 5, &cmsg->Subcommand); - cmsg->par = capi_cmd2par(cmsg->Command, cmsg->Subcommand); - - cdb = bufprint(cdb, "%-26s ID=%03d #0x%04x LEN=%04d\n", - capi_cmd2str(cmsg->Command, cmsg->Subcommand), - ((unsigned short *) msg)[1], - ((unsigned short *) msg)[3], - ((unsigned short *) msg)[0]); - - cdb = protocol_message_2_pars(cdb, cmsg, 1); - if (unlikely(cmsg != g_cmsg)) - kfree(cmsg); - return cdb; -} - -int __init cdebug_init(void) -{ - g_cmsg = kmalloc_obj(_cmsg); - if (!g_cmsg) - return -ENOMEM; - g_debbuf = kmalloc_obj(_cdebbuf); - if (!g_debbuf) { - kfree(g_cmsg); - return -ENOMEM; - } - g_debbuf->buf = kmalloc(CDEBUG_GSIZE, GFP_KERNEL); - if (!g_debbuf->buf) { - kfree(g_cmsg); - kfree(g_debbuf); - return -ENOMEM; - } - g_debbuf->size = CDEBUG_GSIZE; - g_debbuf->buf[0] = 0; - g_debbuf->p = g_debbuf->buf; - g_debbuf->pos = 0; - return 0; -} - -void cdebug_exit(void) -{ - if (g_debbuf) - kfree(g_debbuf->buf); - kfree(g_debbuf); - kfree(g_cmsg); -} - -#else /* !CONFIG_CAPI_TRACE */ - -static _cdebbuf g_debbuf = {"CONFIG_CAPI_TRACE not enabled", NULL, 0, 0}; - -_cdebbuf *capi_message2str(u8 *msg) -{ - return &g_debbuf; -} - -_cdebbuf *capi_cmsg2str(_cmsg *cmsg) -{ - return &g_debbuf; -} - -void cdebbuf_free(_cdebbuf *cdb) -{ -} - -int __init cdebug_init(void) -{ - return 0; -} - -void cdebug_exit(void) -{ -} - -#endif diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c deleted file mode 100644 index f9fa17d68095..000000000000 --- a/drivers/isdn/capi/kcapi.c +++ /dev/null @@ -1,933 +0,0 @@ -/* $Id: kcapi.c,v 1.1.2.8 2004/03/26 19:57:20 armin Exp $ - * - * Kernel CAPI 2.0 Module - * - * Copyright 1999 by Carsten Paeth - * Copyright 2002 by Kai Germaschewski - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include "kcapi.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int showcapimsgs; -static struct workqueue_struct *kcapi_wq; - -module_param(showcapimsgs, uint, 0); - -/* ------------------------------------------------------------- */ - -struct capictr_event { - struct work_struct work; - unsigned int type; - u32 controller; -}; - -/* ------------------------------------------------------------- */ - -static const struct capi_version driver_version = {2, 0, 1, 1 << 4}; -static char driver_serial[CAPI_SERIAL_LEN] = "0004711"; -static char capi_manufakturer[64] = "AVM Berlin"; - -#define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) - -struct capi_ctr *capi_controller[CAPI_MAXCONTR]; -DEFINE_MUTEX(capi_controller_lock); - -struct capi20_appl *capi_applications[CAPI_MAXAPPL]; - -static int ncontrollers; - -/* -------- controller ref counting -------------------------------------- */ - -static inline struct capi_ctr * -capi_ctr_get(struct capi_ctr *ctr) -{ - if (!try_module_get(ctr->owner)) - return NULL; - return ctr; -} - -static inline void -capi_ctr_put(struct capi_ctr *ctr) -{ - module_put(ctr->owner); -} - -/* ------------------------------------------------------------- */ - -static inline struct capi_ctr *get_capi_ctr_by_nr(u16 contr) -{ - if (contr < 1 || contr - 1 >= CAPI_MAXCONTR) - return NULL; - - return capi_controller[contr - 1]; -} - -static inline struct capi20_appl *__get_capi_appl_by_nr(u16 applid) -{ - lockdep_assert_held(&capi_controller_lock); - - if (applid < 1 || applid - 1 >= CAPI_MAXAPPL) - return NULL; - - return capi_applications[applid - 1]; -} - -static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid) -{ - if (applid < 1 || applid - 1 >= CAPI_MAXAPPL) - return NULL; - - return rcu_dereference(capi_applications[applid - 1]); -} - -/* -------- util functions ------------------------------------ */ - -static inline int capi_cmd_valid(u8 cmd) -{ - switch (cmd) { - case CAPI_ALERT: - case CAPI_CONNECT: - case CAPI_CONNECT_ACTIVE: - case CAPI_CONNECT_B3_ACTIVE: - case CAPI_CONNECT_B3: - case CAPI_CONNECT_B3_T90_ACTIVE: - case CAPI_DATA_B3: - case CAPI_DISCONNECT_B3: - case CAPI_DISCONNECT: - case CAPI_FACILITY: - case CAPI_INFO: - case CAPI_LISTEN: - case CAPI_MANUFACTURER: - case CAPI_RESET_B3: - case CAPI_SELECT_B_PROTOCOL: - return 1; - } - return 0; -} - -static inline int capi_subcmd_valid(u8 subcmd) -{ - switch (subcmd) { - case CAPI_REQ: - case CAPI_CONF: - case CAPI_IND: - case CAPI_RESP: - return 1; - } - return 0; -} - -/* ------------------------------------------------------------ */ - -static void -register_appl(struct capi_ctr *ctr, u16 applid, capi_register_params *rparam) -{ - ctr = capi_ctr_get(ctr); - - if (ctr) - ctr->register_appl(ctr, applid, rparam); - else - printk(KERN_WARNING "%s: cannot get controller resources\n", - __func__); -} - - -static void release_appl(struct capi_ctr *ctr, u16 applid) -{ - DBG("applid %#x", applid); - - ctr->release_appl(ctr, applid); - capi_ctr_put(ctr); -} - -static void notify_up(u32 contr) -{ - struct capi20_appl *ap; - struct capi_ctr *ctr; - u16 applid; - - mutex_lock(&capi_controller_lock); - - if (showcapimsgs & 1) - printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr); - - ctr = get_capi_ctr_by_nr(contr); - if (ctr) { - if (ctr->state == CAPI_CTR_RUNNING) - goto unlock_out; - - ctr->state = CAPI_CTR_RUNNING; - - for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { - ap = __get_capi_appl_by_nr(applid); - if (ap) - register_appl(ctr, applid, &ap->rparam); - } - } else - printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr); - -unlock_out: - mutex_unlock(&capi_controller_lock); -} - -static void ctr_down(struct capi_ctr *ctr, int new_state) -{ - struct capi20_appl *ap; - u16 applid; - - if (ctr->state == CAPI_CTR_DETECTED || ctr->state == CAPI_CTR_DETACHED) - return; - - ctr->state = new_state; - - memset(ctr->manu, 0, sizeof(ctr->manu)); - memset(&ctr->version, 0, sizeof(ctr->version)); - memset(&ctr->profile, 0, sizeof(ctr->profile)); - memset(ctr->serial, 0, sizeof(ctr->serial)); - - for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { - ap = __get_capi_appl_by_nr(applid); - if (ap) - capi_ctr_put(ctr); - } -} - -static void notify_down(u32 contr) -{ - struct capi_ctr *ctr; - - mutex_lock(&capi_controller_lock); - - if (showcapimsgs & 1) - printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr); - - ctr = get_capi_ctr_by_nr(contr); - if (ctr) - ctr_down(ctr, CAPI_CTR_DETECTED); - else - printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr); - - mutex_unlock(&capi_controller_lock); -} - -static void do_notify_work(struct work_struct *work) -{ - struct capictr_event *event = - container_of(work, struct capictr_event, work); - - switch (event->type) { - case CAPICTR_UP: - notify_up(event->controller); - break; - case CAPICTR_DOWN: - notify_down(event->controller); - break; - } - - kfree(event); -} - -static int notify_push(unsigned int event_type, u32 controller) -{ - struct capictr_event *event = kmalloc_obj(*event, GFP_ATOMIC); - - if (!event) - return -ENOMEM; - - INIT_WORK(&event->work, do_notify_work); - event->type = event_type; - event->controller = controller; - - queue_work(kcapi_wq, &event->work); - return 0; -} - -/* -------- Receiver ------------------------------------------ */ - -static void recv_handler(struct work_struct *work) -{ - struct sk_buff *skb; - struct capi20_appl *ap = - container_of(work, struct capi20_appl, recv_work); - - if ((!ap) || (ap->release_in_progress)) - return; - - mutex_lock(&ap->recv_mtx); - while ((skb = skb_dequeue(&ap->recv_queue))) { - if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND) - ap->nrecvdatapkt++; - else - ap->nrecvctlpkt++; - - ap->recv_message(ap, skb); - } - mutex_unlock(&ap->recv_mtx); -} - -/** - * capi_ctr_handle_message() - handle incoming CAPI message - * @ctr: controller descriptor structure. - * @appl: application ID. - * @skb: message. - * - * Called by hardware driver to pass a CAPI message to the application. - */ - -void capi_ctr_handle_message(struct capi_ctr *ctr, u16 appl, - struct sk_buff *skb) -{ - struct capi20_appl *ap; - int showctl = 0; - u8 cmd, subcmd; - _cdebbuf *cdb; - - if (ctr->state != CAPI_CTR_RUNNING) { - cdb = capi_message2str(skb->data); - if (cdb) { - printk(KERN_INFO "kcapi: controller [%03d] not active, got: %s", - ctr->cnr, cdb->buf); - cdebbuf_free(cdb); - } else - printk(KERN_INFO "kcapi: controller [%03d] not active, cannot trace\n", - ctr->cnr); - goto error; - } - - cmd = CAPIMSG_COMMAND(skb->data); - subcmd = CAPIMSG_SUBCOMMAND(skb->data); - if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) { - ctr->nrecvdatapkt++; - if (ctr->traceflag > 2) - showctl |= 2; - } else { - ctr->nrecvctlpkt++; - if (ctr->traceflag) - showctl |= 2; - } - showctl |= (ctr->traceflag & 1); - if (showctl & 2) { - if (showctl & 1) { - printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u\n", - ctr->cnr, CAPIMSG_APPID(skb->data), - capi_cmd2str(cmd, subcmd), - CAPIMSG_LEN(skb->data)); - } else { - cdb = capi_message2str(skb->data); - if (cdb) { - printk(KERN_DEBUG "kcapi: got [%03d] %s\n", - ctr->cnr, cdb->buf); - cdebbuf_free(cdb); - } else - printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u, cannot trace\n", - ctr->cnr, CAPIMSG_APPID(skb->data), - capi_cmd2str(cmd, subcmd), - CAPIMSG_LEN(skb->data)); - } - - } - - rcu_read_lock(); - ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data)); - if (!ap) { - rcu_read_unlock(); - cdb = capi_message2str(skb->data); - if (cdb) { - printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n", - CAPIMSG_APPID(skb->data), cdb->buf); - cdebbuf_free(cdb); - } else - printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s) cannot trace\n", - CAPIMSG_APPID(skb->data), - capi_cmd2str(cmd, subcmd)); - goto error; - } - skb_queue_tail(&ap->recv_queue, skb); - queue_work(kcapi_wq, &ap->recv_work); - rcu_read_unlock(); - - return; - -error: - kfree_skb(skb); -} - -EXPORT_SYMBOL(capi_ctr_handle_message); - -/** - * capi_ctr_ready() - signal CAPI controller ready - * @ctr: controller descriptor structure. - * - * Called by hardware driver to signal that the controller is up and running. - */ - -void capi_ctr_ready(struct capi_ctr *ctr) -{ - printk(KERN_NOTICE "kcapi: controller [%03d] \"%s\" ready.\n", - ctr->cnr, ctr->name); - - notify_push(CAPICTR_UP, ctr->cnr); -} - -EXPORT_SYMBOL(capi_ctr_ready); - -/** - * capi_ctr_down() - signal CAPI controller not ready - * @ctr: controller descriptor structure. - * - * Called by hardware driver to signal that the controller is down and - * unavailable for use. - */ - -void capi_ctr_down(struct capi_ctr *ctr) -{ - printk(KERN_NOTICE "kcapi: controller [%03d] down.\n", ctr->cnr); - - notify_push(CAPICTR_DOWN, ctr->cnr); -} - -EXPORT_SYMBOL(capi_ctr_down); - -/* ------------------------------------------------------------- */ - -/** - * attach_capi_ctr() - register CAPI controller - * @ctr: controller descriptor structure. - * - * Called by hardware driver to register a controller with the CAPI subsystem. - * Return value: 0 on success, error code < 0 on error - */ - -int attach_capi_ctr(struct capi_ctr *ctr) -{ - int i; - - mutex_lock(&capi_controller_lock); - - for (i = 0; i < CAPI_MAXCONTR; i++) { - if (!capi_controller[i]) - break; - } - if (i == CAPI_MAXCONTR) { - mutex_unlock(&capi_controller_lock); - printk(KERN_ERR "kcapi: out of controller slots\n"); - return -EBUSY; - } - capi_controller[i] = ctr; - - ctr->nrecvctlpkt = 0; - ctr->nrecvdatapkt = 0; - ctr->nsentctlpkt = 0; - ctr->nsentdatapkt = 0; - ctr->cnr = i + 1; - ctr->state = CAPI_CTR_DETECTED; - ctr->blocked = 0; - ctr->traceflag = showcapimsgs; - - sprintf(ctr->procfn, "capi/controllers/%d", ctr->cnr); - ctr->procent = proc_create_single_data(ctr->procfn, 0, NULL, - ctr->proc_show, ctr); - - ncontrollers++; - - mutex_unlock(&capi_controller_lock); - - printk(KERN_NOTICE "kcapi: controller [%03d]: %s attached\n", - ctr->cnr, ctr->name); - return 0; -} - -EXPORT_SYMBOL(attach_capi_ctr); - -/** - * detach_capi_ctr() - unregister CAPI controller - * @ctr: controller descriptor structure. - * - * Called by hardware driver to remove the registration of a controller - * with the CAPI subsystem. - * Return value: 0 on success, error code < 0 on error - */ - -int detach_capi_ctr(struct capi_ctr *ctr) -{ - int err = 0; - - mutex_lock(&capi_controller_lock); - - ctr_down(ctr, CAPI_CTR_DETACHED); - - if (ctr->cnr < 1 || ctr->cnr - 1 >= CAPI_MAXCONTR) { - err = -EINVAL; - goto unlock_out; - } - - if (capi_controller[ctr->cnr - 1] != ctr) { - err = -EINVAL; - goto unlock_out; - } - capi_controller[ctr->cnr - 1] = NULL; - ncontrollers--; - - if (ctr->procent) - remove_proc_entry(ctr->procfn, NULL); - - printk(KERN_NOTICE "kcapi: controller [%03d]: %s unregistered\n", - ctr->cnr, ctr->name); - -unlock_out: - mutex_unlock(&capi_controller_lock); - - return err; -} - -EXPORT_SYMBOL(detach_capi_ctr); - -/* ------------------------------------------------------------- */ -/* -------- CAPI2.0 Interface ---------------------------------- */ -/* ------------------------------------------------------------- */ - -/** - * capi20_isinstalled() - CAPI 2.0 operation CAPI_INSTALLED - * - * Return value: CAPI result code (CAPI_NOERROR if at least one ISDN controller - * is ready for use, CAPI_REGNOTINSTALLED otherwise) - */ - -u16 capi20_isinstalled(void) -{ - u16 ret = CAPI_REGNOTINSTALLED; - int i; - - mutex_lock(&capi_controller_lock); - - for (i = 0; i < CAPI_MAXCONTR; i++) - if (capi_controller[i] && - capi_controller[i]->state == CAPI_CTR_RUNNING) { - ret = CAPI_NOERROR; - break; - } - - mutex_unlock(&capi_controller_lock); - - return ret; -} - -/** - * capi20_register() - CAPI 2.0 operation CAPI_REGISTER - * @ap: CAPI application descriptor structure. - * - * Register an application's presence with CAPI. - * A unique application ID is assigned and stored in @ap->applid. - * After this function returns successfully, the message receive - * callback function @ap->recv_message() may be called at any time - * until capi20_release() has been called for the same @ap. - * Return value: CAPI result code - */ - -u16 capi20_register(struct capi20_appl *ap) -{ - int i; - u16 applid; - - DBG(""); - - if (ap->rparam.datablklen < 128) - return CAPI_LOGBLKSIZETOSMALL; - - ap->nrecvctlpkt = 0; - ap->nrecvdatapkt = 0; - ap->nsentctlpkt = 0; - ap->nsentdatapkt = 0; - mutex_init(&ap->recv_mtx); - skb_queue_head_init(&ap->recv_queue); - INIT_WORK(&ap->recv_work, recv_handler); - ap->release_in_progress = 0; - - mutex_lock(&capi_controller_lock); - - for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { - if (capi_applications[applid - 1] == NULL) - break; - } - if (applid > CAPI_MAXAPPL) { - mutex_unlock(&capi_controller_lock); - return CAPI_TOOMANYAPPLS; - } - - ap->applid = applid; - capi_applications[applid - 1] = ap; - - for (i = 0; i < CAPI_MAXCONTR; i++) { - if (!capi_controller[i] || - capi_controller[i]->state != CAPI_CTR_RUNNING) - continue; - register_appl(capi_controller[i], applid, &ap->rparam); - } - - mutex_unlock(&capi_controller_lock); - - if (showcapimsgs & 1) { - printk(KERN_DEBUG "kcapi: appl %d up\n", applid); - } - - return CAPI_NOERROR; -} - -/** - * capi20_release() - CAPI 2.0 operation CAPI_RELEASE - * @ap: CAPI application descriptor structure. - * - * Terminate an application's registration with CAPI. - * After this function returns successfully, the message receive - * callback function @ap->recv_message() will no longer be called. - * Return value: CAPI result code - */ - -u16 capi20_release(struct capi20_appl *ap) -{ - int i; - - DBG("applid %#x", ap->applid); - - mutex_lock(&capi_controller_lock); - - ap->release_in_progress = 1; - capi_applications[ap->applid - 1] = NULL; - - synchronize_rcu(); - - for (i = 0; i < CAPI_MAXCONTR; i++) { - if (!capi_controller[i] || - capi_controller[i]->state != CAPI_CTR_RUNNING) - continue; - release_appl(capi_controller[i], ap->applid); - } - - mutex_unlock(&capi_controller_lock); - - flush_workqueue(kcapi_wq); - skb_queue_purge(&ap->recv_queue); - - if (showcapimsgs & 1) { - printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid); - } - - return CAPI_NOERROR; -} - -/** - * capi20_put_message() - CAPI 2.0 operation CAPI_PUT_MESSAGE - * @ap: CAPI application descriptor structure. - * @skb: CAPI message. - * - * Transfer a single message to CAPI. - * Return value: CAPI result code - */ - -u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb) -{ - struct capi_ctr *ctr; - int showctl = 0; - u8 cmd, subcmd; - - DBG("applid %#x", ap->applid); - - if (ncontrollers == 0) - return CAPI_REGNOTINSTALLED; - if ((ap->applid == 0) || ap->release_in_progress) - return CAPI_ILLAPPNR; - if (skb->len < 12 - || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data)) - || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data))) - return CAPI_ILLCMDORSUBCMDORMSGTOSMALL; - - /* - * The controller reference is protected by the existence of the - * application passed to us. We assume that the caller properly - * synchronizes this service with capi20_release. - */ - ctr = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data)); - if (!ctr || ctr->state != CAPI_CTR_RUNNING) - return CAPI_REGNOTINSTALLED; - if (ctr->blocked) - return CAPI_SENDQUEUEFULL; - - cmd = CAPIMSG_COMMAND(skb->data); - subcmd = CAPIMSG_SUBCOMMAND(skb->data); - - if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) { - ctr->nsentdatapkt++; - ap->nsentdatapkt++; - if (ctr->traceflag > 2) - showctl |= 2; - } else { - ctr->nsentctlpkt++; - ap->nsentctlpkt++; - if (ctr->traceflag) - showctl |= 2; - } - showctl |= (ctr->traceflag & 1); - if (showctl & 2) { - if (showctl & 1) { - printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u\n", - CAPIMSG_CONTROLLER(skb->data), - CAPIMSG_APPID(skb->data), - capi_cmd2str(cmd, subcmd), - CAPIMSG_LEN(skb->data)); - } else { - _cdebbuf *cdb = capi_message2str(skb->data); - if (cdb) { - printk(KERN_DEBUG "kcapi: put [%03d] %s\n", - CAPIMSG_CONTROLLER(skb->data), - cdb->buf); - cdebbuf_free(cdb); - } else - printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u cannot trace\n", - CAPIMSG_CONTROLLER(skb->data), - CAPIMSG_APPID(skb->data), - capi_cmd2str(cmd, subcmd), - CAPIMSG_LEN(skb->data)); - } - } - return ctr->send_message(ctr, skb); -} - -/** - * capi20_get_manufacturer() - CAPI 2.0 operation CAPI_GET_MANUFACTURER - * @contr: controller number. - * @buf: result buffer (64 bytes). - * - * Retrieve information about the manufacturer of the specified ISDN controller - * or (for @contr == 0) the driver itself. - * Return value: CAPI result code - */ - -u16 capi20_get_manufacturer(u32 contr, u8 buf[CAPI_MANUFACTURER_LEN]) -{ - struct capi_ctr *ctr; - u16 ret; - - if (contr == 0) { - strscpy_pad(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); - return CAPI_NOERROR; - } - - mutex_lock(&capi_controller_lock); - - ctr = get_capi_ctr_by_nr(contr); - if (ctr && ctr->state == CAPI_CTR_RUNNING) { - strscpy_pad(buf, ctr->manu, CAPI_MANUFACTURER_LEN); - ret = CAPI_NOERROR; - } else - ret = CAPI_REGNOTINSTALLED; - - mutex_unlock(&capi_controller_lock); - return ret; -} - -/** - * capi20_get_version() - CAPI 2.0 operation CAPI_GET_VERSION - * @contr: controller number. - * @verp: result structure. - * - * Retrieve version information for the specified ISDN controller - * or (for @contr == 0) the driver itself. - * Return value: CAPI result code - */ - -u16 capi20_get_version(u32 contr, struct capi_version *verp) -{ - struct capi_ctr *ctr; - u16 ret; - - if (contr == 0) { - *verp = driver_version; - return CAPI_NOERROR; - } - - mutex_lock(&capi_controller_lock); - - ctr = get_capi_ctr_by_nr(contr); - if (ctr && ctr->state == CAPI_CTR_RUNNING) { - memcpy(verp, &ctr->version, sizeof(capi_version)); - ret = CAPI_NOERROR; - } else - ret = CAPI_REGNOTINSTALLED; - - mutex_unlock(&capi_controller_lock); - return ret; -} - -/** - * capi20_get_serial() - CAPI 2.0 operation CAPI_GET_SERIAL_NUMBER - * @contr: controller number. - * @serial: result buffer (8 bytes). - * - * Retrieve the serial number of the specified ISDN controller - * or (for @contr == 0) the driver itself. - * Return value: CAPI result code - */ - -u16 capi20_get_serial(u32 contr, u8 serial[CAPI_SERIAL_LEN]) -{ - struct capi_ctr *ctr; - u16 ret; - - if (contr == 0) { - strscpy(serial, driver_serial, CAPI_SERIAL_LEN); - return CAPI_NOERROR; - } - - mutex_lock(&capi_controller_lock); - - ctr = get_capi_ctr_by_nr(contr); - if (ctr && ctr->state == CAPI_CTR_RUNNING) { - strscpy(serial, ctr->serial, CAPI_SERIAL_LEN); - ret = CAPI_NOERROR; - } else - ret = CAPI_REGNOTINSTALLED; - - mutex_unlock(&capi_controller_lock); - return ret; -} - -/** - * capi20_get_profile() - CAPI 2.0 operation CAPI_GET_PROFILE - * @contr: controller number. - * @profp: result structure. - * - * Retrieve capability information for the specified ISDN controller - * or (for @contr == 0) the number of installed controllers. - * Return value: CAPI result code - */ - -u16 capi20_get_profile(u32 contr, struct capi_profile *profp) -{ - struct capi_ctr *ctr; - u16 ret; - - if (contr == 0) { - profp->ncontroller = ncontrollers; - return CAPI_NOERROR; - } - - mutex_lock(&capi_controller_lock); - - ctr = get_capi_ctr_by_nr(contr); - if (ctr && ctr->state == CAPI_CTR_RUNNING) { - memcpy(profp, &ctr->profile, sizeof(struct capi_profile)); - ret = CAPI_NOERROR; - } else - ret = CAPI_REGNOTINSTALLED; - - mutex_unlock(&capi_controller_lock); - return ret; -} - -/** - * capi20_manufacturer() - CAPI 2.0 operation CAPI_MANUFACTURER - * @cmd: command. - * @data: parameter. - * - * Perform manufacturer specific command. - * Return value: CAPI result code - */ - -int capi20_manufacturer(unsigned long cmd, void __user *data) -{ - struct capi_ctr *ctr; - int retval; - - switch (cmd) { - case KCAPI_CMD_TRACE: - { - kcapi_flagdef fdef; - - if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef))) - return -EFAULT; - - mutex_lock(&capi_controller_lock); - - ctr = get_capi_ctr_by_nr(fdef.contr); - if (ctr) { - ctr->traceflag = fdef.flag; - printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n", - ctr->cnr, ctr->traceflag); - retval = 0; - } else - retval = -ESRCH; - - mutex_unlock(&capi_controller_lock); - - return retval; - } - - default: - printk(KERN_ERR "kcapi: manufacturer command %lu unknown.\n", - cmd); - break; - - } - return -EINVAL; -} - -/* ------------------------------------------------------------- */ -/* -------- Init & Cleanup ------------------------------------- */ -/* ------------------------------------------------------------- */ - -/* - * init / exit functions - */ - -int __init kcapi_init(void) -{ - int err; - - kcapi_wq = alloc_workqueue("kcapi", WQ_PERCPU, 0); - if (!kcapi_wq) - return -ENOMEM; - - err = cdebug_init(); - if (err) { - destroy_workqueue(kcapi_wq); - return err; - } - - if (IS_ENABLED(CONFIG_PROC_FS)) - kcapi_proc_init(); - - return 0; -} - -void kcapi_exit(void) -{ - if (IS_ENABLED(CONFIG_PROC_FS)) - kcapi_proc_exit(); - - cdebug_exit(); - destroy_workqueue(kcapi_wq); -} diff --git a/drivers/isdn/capi/kcapi.h b/drivers/isdn/capi/kcapi.h deleted file mode 100644 index 479623e1db2a..000000000000 --- a/drivers/isdn/capi/kcapi.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Kernel CAPI 2.0 Module - * - * Copyright 1999 by Carsten Paeth - * Copyright 2002 by Kai Germaschewski - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - - -#include -#include -#include -#include - -#ifdef KCAPI_DEBUG -#define DBG(format, arg...) do { \ - printk(KERN_DEBUG "%s: " format "\n" , __func__ , ## arg); \ - } while (0) -#else -#define DBG(format, arg...) /* */ -#endif - -enum { - CAPI_CTR_DETACHED = 0, - CAPI_CTR_DETECTED = 1, - CAPI_CTR_LOADING = 2, - CAPI_CTR_RUNNING = 3, -}; - -extern struct capi_ctr *capi_controller[CAPI_MAXCONTR]; -extern struct mutex capi_controller_lock; - -extern struct capi20_appl *capi_applications[CAPI_MAXAPPL]; - -void kcapi_proc_init(void); -void kcapi_proc_exit(void); - -struct capi20_appl { - u16 applid; - capi_register_params rparam; - void (*recv_message)(struct capi20_appl *ap, struct sk_buff *skb); - void *private; - - /* internal to kernelcapi.o */ - unsigned long nrecvctlpkt; - unsigned long nrecvdatapkt; - unsigned long nsentctlpkt; - unsigned long nsentdatapkt; - struct mutex recv_mtx; - struct sk_buff_head recv_queue; - struct work_struct recv_work; - int release_in_progress; -}; - -u16 capi20_isinstalled(void); -u16 capi20_register(struct capi20_appl *ap); -u16 capi20_release(struct capi20_appl *ap); -u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb); -u16 capi20_get_manufacturer(u32 contr, u8 buf[CAPI_MANUFACTURER_LEN]); -u16 capi20_get_version(u32 contr, struct capi_version *verp); -u16 capi20_get_serial(u32 contr, u8 serial[CAPI_SERIAL_LEN]); -u16 capi20_get_profile(u32 contr, struct capi_profile *profp); -int capi20_manufacturer(unsigned long cmd, void __user *data); - -#define CAPICTR_UP 0 -#define CAPICTR_DOWN 1 - -int kcapi_init(void); -void kcapi_exit(void); - -/*----- basic-type definitions -----*/ - -typedef __u8 *_cstruct; - -typedef enum { - CAPI_COMPOSE, - CAPI_DEFAULT -} _cmstruct; - -/* - The _cmsg structure contains all possible CAPI 2.0 parameter. - All parameters are stored here first. The function CAPI_CMSG_2_MESSAGE - assembles the parameter and builds CAPI2.0 conform messages. - CAPI_MESSAGE_2_CMSG disassembles CAPI 2.0 messages and stores the - parameter in the _cmsg structure - */ - -typedef struct { - /* Header */ - __u16 ApplId; - __u8 Command; - __u8 Subcommand; - __u16 Messagenumber; - - /* Parameter */ - union { - __u32 adrController; - __u32 adrPLCI; - __u32 adrNCCI; - } adr; - - _cmstruct AdditionalInfo; - _cstruct B1configuration; - __u16 B1protocol; - _cstruct B2configuration; - __u16 B2protocol; - _cstruct B3configuration; - __u16 B3protocol; - _cstruct BC; - _cstruct BChannelinformation; - _cmstruct BProtocol; - _cstruct CalledPartyNumber; - _cstruct CalledPartySubaddress; - _cstruct CallingPartyNumber; - _cstruct CallingPartySubaddress; - __u32 CIPmask; - __u32 CIPmask2; - __u16 CIPValue; - __u32 Class; - _cstruct ConnectedNumber; - _cstruct ConnectedSubaddress; - __u32 Data; - __u16 DataHandle; - __u16 DataLength; - _cstruct FacilityConfirmationParameter; - _cstruct Facilitydataarray; - _cstruct FacilityIndicationParameter; - _cstruct FacilityRequestParameter; - __u16 FacilitySelector; - __u16 Flags; - __u32 Function; - _cstruct HLC; - __u16 Info; - _cstruct InfoElement; - __u32 InfoMask; - __u16 InfoNumber; - _cstruct Keypadfacility; - _cstruct LLC; - _cstruct ManuData; - __u32 ManuID; - _cstruct NCPI; - __u16 Reason; - __u16 Reason_B3; - __u16 Reject; - _cstruct Useruserdata; - - /* intern */ - unsigned l, p; - unsigned char *par; - __u8 *m; - - /* buffer to construct message */ - __u8 buf[180]; - -} _cmsg; - -/*-----------------------------------------------------------------------*/ - -/* - * Debugging / Tracing functions - */ - -char *capi_cmd2str(__u8 cmd, __u8 subcmd); - -typedef struct { - u_char *buf; - u_char *p; - size_t size; - size_t pos; -} _cdebbuf; - -#define CDEBUG_SIZE 1024 -#define CDEBUG_GSIZE 4096 - -void cdebbuf_free(_cdebbuf *cdb); -int cdebug_init(void); -void cdebug_exit(void); - -_cdebbuf *capi_message2str(__u8 *msg); diff --git a/drivers/isdn/capi/kcapi_proc.c b/drivers/isdn/capi/kcapi_proc.c deleted file mode 100644 index 77e951206809..000000000000 --- a/drivers/isdn/capi/kcapi_proc.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Kernel CAPI 2.0 Module - /proc/capi handling - * - * Copyright 1999 by Carsten Paeth - * Copyright 2002 by Kai Germaschewski - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - - -#include "kcapi.h" -#include -#include -#include -#include - -static char *state2str(unsigned short state) -{ - switch (state) { - case CAPI_CTR_DETECTED: return "detected"; - case CAPI_CTR_LOADING: return "loading"; - case CAPI_CTR_RUNNING: return "running"; - default: return "???"; - } -} - -// /proc/capi -// =========================================================================== - -// /proc/capi/controller: -// cnr driver cardstate name driverinfo -// /proc/capi/contrstats: -// cnr nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt -// --------------------------------------------------------------------------- - -static void *controller_start(struct seq_file *seq, loff_t *pos) - __acquires(capi_controller_lock) -{ - mutex_lock(&capi_controller_lock); - - if (*pos < CAPI_MAXCONTR) - return &capi_controller[*pos]; - - return NULL; -} - -static void *controller_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - if (*pos < CAPI_MAXCONTR) - return &capi_controller[*pos]; - - return NULL; -} - -static void controller_stop(struct seq_file *seq, void *v) - __releases(capi_controller_lock) -{ - mutex_unlock(&capi_controller_lock); -} - -static int controller_show(struct seq_file *seq, void *v) -{ - struct capi_ctr *ctr = *(struct capi_ctr **) v; - - if (!ctr) - return 0; - - seq_printf(seq, "%d %-10s %-8s %-16s %s\n", - ctr->cnr, ctr->driver_name, - state2str(ctr->state), - ctr->name, - ctr->procinfo ? ctr->procinfo(ctr) : ""); - - return 0; -} - -static int contrstats_show(struct seq_file *seq, void *v) -{ - struct capi_ctr *ctr = *(struct capi_ctr **) v; - - if (!ctr) - return 0; - - seq_printf(seq, "%d %lu %lu %lu %lu\n", - ctr->cnr, - ctr->nrecvctlpkt, - ctr->nrecvdatapkt, - ctr->nsentctlpkt, - ctr->nsentdatapkt); - - return 0; -} - -static const struct seq_operations seq_controller_ops = { - .start = controller_start, - .next = controller_next, - .stop = controller_stop, - .show = controller_show, -}; - -static const struct seq_operations seq_contrstats_ops = { - .start = controller_start, - .next = controller_next, - .stop = controller_stop, - .show = contrstats_show, -}; - -// /proc/capi/applications: -// applid l3cnt dblkcnt dblklen #ncci recvqueuelen -// /proc/capi/applstats: -// applid nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt -// --------------------------------------------------------------------------- - -static void *applications_start(struct seq_file *seq, loff_t *pos) - __acquires(capi_controller_lock) -{ - mutex_lock(&capi_controller_lock); - - if (*pos < CAPI_MAXAPPL) - return &capi_applications[*pos]; - - return NULL; -} - -static void * -applications_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - if (*pos < CAPI_MAXAPPL) - return &capi_applications[*pos]; - - return NULL; -} - -static void applications_stop(struct seq_file *seq, void *v) - __releases(capi_controller_lock) -{ - mutex_unlock(&capi_controller_lock); -} - -static int -applications_show(struct seq_file *seq, void *v) -{ - struct capi20_appl *ap = *(struct capi20_appl **) v; - - if (!ap) - return 0; - - seq_printf(seq, "%u %d %d %d\n", - ap->applid, - ap->rparam.level3cnt, - ap->rparam.datablkcnt, - ap->rparam.datablklen); - - return 0; -} - -static int -applstats_show(struct seq_file *seq, void *v) -{ - struct capi20_appl *ap = *(struct capi20_appl **) v; - - if (!ap) - return 0; - - seq_printf(seq, "%u %lu %lu %lu %lu\n", - ap->applid, - ap->nrecvctlpkt, - ap->nrecvdatapkt, - ap->nsentctlpkt, - ap->nsentdatapkt); - - return 0; -} - -static const struct seq_operations seq_applications_ops = { - .start = applications_start, - .next = applications_next, - .stop = applications_stop, - .show = applications_show, -}; - -static const struct seq_operations seq_applstats_ops = { - .start = applications_start, - .next = applications_next, - .stop = applications_stop, - .show = applstats_show, -}; - -// --------------------------------------------------------------------------- - -/* /proc/capi/drivers is always empty */ -static ssize_t empty_read(struct file *file, char __user *buf, - size_t size, loff_t *off) -{ - return 0; -} - -static const struct proc_ops empty_proc_ops = { - .proc_read = empty_read, - .proc_lseek = default_llseek, -}; - -// --------------------------------------------------------------------------- - -void __init -kcapi_proc_init(void) -{ - proc_mkdir("capi", NULL); - proc_mkdir("capi/controllers", NULL); - proc_create_seq("capi/controller", 0, NULL, &seq_controller_ops); - proc_create_seq("capi/contrstats", 0, NULL, &seq_contrstats_ops); - proc_create_seq("capi/applications", 0, NULL, &seq_applications_ops); - proc_create_seq("capi/applstats", 0, NULL, &seq_applstats_ops); - proc_create("capi/driver", 0, NULL, &empty_proc_ops); -} - -void -kcapi_proc_exit(void) -{ - remove_proc_entry("capi/driver", NULL); - remove_proc_entry("capi/controller", NULL); - remove_proc_entry("capi/contrstats", NULL); - remove_proc_entry("capi/applications", NULL); - remove_proc_entry("capi/applstats", NULL); - remove_proc_entry("capi/controllers", NULL); - remove_proc_entry("capi", NULL); -} diff --git a/drivers/isdn/hardware/Makefile b/drivers/isdn/hardware/Makefile deleted file mode 100644 index 96f9eb2e46ba..000000000000 --- a/drivers/isdn/hardware/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# Makefile for the CAPI hardware drivers - -# Object files in subdirectories - -obj-$(CONFIG_MISDN) += mISDN/ diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig deleted file mode 100644 index a35bff8a93f5..000000000000 --- a/drivers/isdn/hardware/mISDN/Kconfig +++ /dev/null @@ -1,98 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Hardware for mISDN -# -comment "mISDN hardware drivers" - -config MISDN_HFCPCI - tristate "Support for HFC PCI cards" - depends on MISDN - depends on PCI - help - Enable support for cards with Cologne Chip AG's - HFC PCI chip. - -config MISDN_HFCMULTI - tristate "Support for HFC multiport cards (HFC-4S/8S/E1)" - depends on (PCI || CPM1) && HAS_IOPORT - depends on MISDN - help - Enable support for cards with Cologne Chip AG's HFC multiport - chip. There are three types of chips that are quite similar, - but the interface is different: - * HFC-4S (4 S/T interfaces on one chip) - * HFC-8S (8 S/T interfaces on one chip) - * HFC-E1 (E1 interface for 2Mbit ISDN) - -config MISDN_HFCMULTI_8xx - bool "Support for XHFC embedded board in HFC multiport driver" - depends on MISDN - depends on MISDN_HFCMULTI - depends on CPM1 - default CPM1 - help - Enable support for the XHFC embedded solution from Speech Design. - -config MISDN_HFCUSB - tristate "Support for HFC-S USB based TAs" - depends on USB - help - Enable support for USB ISDN TAs with Cologne Chip AG's - HFC-S USB ISDN Controller - -config MISDN_AVMFRITZ - tristate "Support for AVM FRITZ!CARD PCI" - depends on MISDN - depends on PCI && HAS_IOPORT - select MISDN_IPAC - help - Enable support for AVMs FRITZ!CARD PCI cards - -config MISDN_SPEEDFAX - tristate "Support for Sedlbauer Speedfax+" - depends on MISDN - depends on PCI && HAS_IOPORT - select MISDN_IPAC - select MISDN_ISAR - help - Enable support for Sedlbauer Speedfax+. - -config MISDN_INFINEON - tristate "Support for cards with Infineon chipset" - depends on MISDN - depends on PCI && HAS_IOPORT - select MISDN_IPAC - help - Enable support for cards with ISAC + HSCX, IPAC or IPAC-SX - chip from Infineon (former manufacturer Siemens). - -config MISDN_W6692 - tristate "Support for cards with Winbond 6692" - depends on MISDN - depends on PCI && HAS_IOPORT - help - Enable support for Winbond 6692 PCI chip based cards. - -config MISDN_NETJET - tristate "Support for NETJet cards" - depends on MISDN - depends on PCI && HAS_IOPORT - depends on TTY - select MISDN_IPAC - select MISDN_HDLC - help - Enable support for Traverse Technologies NETJet PCI cards. - -config MISDN_HDLC - tristate - select CRC_CCITT - select BITREVERSE - -config MISDN_IPAC - tristate - depends on MISDN - -config MISDN_ISAR - tristate - depends on MISDN - diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile deleted file mode 100644 index 3f50f8c4753f..000000000000 --- a/drivers/isdn/hardware/mISDN/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the modular ISDN hardware drivers -# -# - -obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o -obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o -obj-$(CONFIG_MISDN_HFCUSB) += hfcsusb.o -obj-$(CONFIG_MISDN_AVMFRITZ) += avmfritz.o -obj-$(CONFIG_MISDN_SPEEDFAX) += speedfax.o -obj-$(CONFIG_MISDN_INFINEON) += mISDNinfineon.o -obj-$(CONFIG_MISDN_W6692) += w6692.o -obj-$(CONFIG_MISDN_NETJET) += netjet.o -# chip modules -obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o -obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o - -obj-$(CONFIG_MISDN_HDLC) += isdnhdlc.o diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c deleted file mode 100644 index 55e0b9efa194..000000000000 --- a/drivers/isdn/hardware/mISDN/avmfritz.c +++ /dev/null @@ -1,1164 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * avm_fritz.c low level stuff for AVM FRITZ!CARD PCI ISDN cards - * Thanks to AVM, Berlin for informations - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ -#include -#include -#include -#include -#include -#include -#include -#include "ipac.h" - - -#define AVMFRITZ_REV "2.3" - -static int AVM_cnt; -static int debug; - -enum { - AVM_FRITZ_PCI, - AVM_FRITZ_PCIV2, -}; - -#define HDLC_FIFO 0x0 -#define HDLC_STATUS 0x4 -#define CHIP_WINDOW 0x10 - -#define CHIP_INDEX 0x4 -#define AVM_HDLC_1 0x00 -#define AVM_HDLC_2 0x01 -#define AVM_ISAC_FIFO 0x02 -#define AVM_ISAC_REG_LOW 0x04 -#define AVM_ISAC_REG_HIGH 0x06 - -#define AVM_STATUS0_IRQ_ISAC 0x01 -#define AVM_STATUS0_IRQ_HDLC 0x02 -#define AVM_STATUS0_IRQ_TIMER 0x04 -#define AVM_STATUS0_IRQ_MASK 0x07 - -#define AVM_STATUS0_RESET 0x01 -#define AVM_STATUS0_DIS_TIMER 0x02 -#define AVM_STATUS0_RES_TIMER 0x04 -#define AVM_STATUS0_ENA_IRQ 0x08 -#define AVM_STATUS0_TESTBIT 0x10 - -#define AVM_STATUS1_INT_SEL 0x0f -#define AVM_STATUS1_ENA_IOM 0x80 - -#define HDLC_MODE_ITF_FLG 0x01 -#define HDLC_MODE_TRANS 0x02 -#define HDLC_MODE_CCR_7 0x04 -#define HDLC_MODE_CCR_16 0x08 -#define HDLC_FIFO_SIZE_128 0x20 -#define HDLC_MODE_TESTLOOP 0x80 - -#define HDLC_INT_XPR 0x80 -#define HDLC_INT_XDU 0x40 -#define HDLC_INT_RPR 0x20 -#define HDLC_INT_MASK 0xE0 - -#define HDLC_STAT_RME 0x01 -#define HDLC_STAT_RDO 0x10 -#define HDLC_STAT_CRCVFRRAB 0x0E -#define HDLC_STAT_CRCVFR 0x06 -#define HDLC_STAT_RML_MASK_V1 0x3f00 -#define HDLC_STAT_RML_MASK_V2 0x7f00 - -#define HDLC_CMD_XRS 0x80 -#define HDLC_CMD_XME 0x01 -#define HDLC_CMD_RRS 0x20 -#define HDLC_CMD_XML_MASK 0x3f00 - -#define HDLC_FIFO_SIZE_V1 32 -#define HDLC_FIFO_SIZE_V2 128 - -/* Fritz PCI v2.0 */ - -#define AVM_HDLC_FIFO_1 0x10 -#define AVM_HDLC_FIFO_2 0x18 - -#define AVM_HDLC_STATUS_1 0x14 -#define AVM_HDLC_STATUS_2 0x1c - -#define AVM_ISACX_INDEX 0x04 -#define AVM_ISACX_DATA 0x08 - -/* data struct */ -#define LOG_SIZE 63 - -struct hdlc_stat_reg { -#ifdef __BIG_ENDIAN - u8 fill; - u8 mode; - u8 xml; - u8 cmd; -#else - u8 cmd; - u8 xml; - u8 mode; - u8 fill; -#endif -} __attribute__((packed)); - -struct hdlc_hw { - union { - u32 ctrl; - struct hdlc_stat_reg sr; - } ctrl; - u32 stat; -}; - -struct fritzcard { - struct list_head list; - struct pci_dev *pdev; - char name[MISDN_MAX_IDLEN]; - u8 type; - u8 ctrlreg; - u16 irq; - u32 irqcnt; - u32 addr; - spinlock_t lock; /* hw lock */ - struct isac_hw isac; - struct hdlc_hw hdlc[2]; - struct bchannel bch[2]; - char log[LOG_SIZE + 1]; -}; - -static LIST_HEAD(Cards); -static DEFINE_RWLOCK(card_lock); /* protect Cards */ - -static void -_set_debug(struct fritzcard *card) -{ - card->isac.dch.debug = debug; - card->bch[0].debug = debug; - card->bch[1].debug = debug; -} - -static int -set_debug(const char *val, const struct kernel_param *kp) -{ - int ret; - struct fritzcard *card; - - ret = param_set_uint(val, kp); - if (!ret) { - read_lock(&card_lock); - list_for_each_entry(card, &Cards, list) - _set_debug(card); - read_unlock(&card_lock); - } - return ret; -} - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for AVM FRITZ!CARD PCI ISDN cards"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(AVMFRITZ_REV); -module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "avmfritz debug mask"); - -/* Interface functions */ - -static u8 -ReadISAC_V1(void *p, u8 offset) -{ - struct fritzcard *fc = p; - u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; - - outb(idx, fc->addr + CHIP_INDEX); - return inb(fc->addr + CHIP_WINDOW + (offset & 0xf)); -} - -static void -WriteISAC_V1(void *p, u8 offset, u8 value) -{ - struct fritzcard *fc = p; - u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; - - outb(idx, fc->addr + CHIP_INDEX); - outb(value, fc->addr + CHIP_WINDOW + (offset & 0xf)); -} - -static void -ReadFiFoISAC_V1(void *p, u8 off, u8 *data, int size) -{ - struct fritzcard *fc = p; - - outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); - insb(fc->addr + CHIP_WINDOW, data, size); -} - -static void -WriteFiFoISAC_V1(void *p, u8 off, u8 *data, int size) -{ - struct fritzcard *fc = p; - - outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); - outsb(fc->addr + CHIP_WINDOW, data, size); -} - -static u8 -ReadISAC_V2(void *p, u8 offset) -{ - struct fritzcard *fc = p; - - outl(offset, fc->addr + AVM_ISACX_INDEX); - return 0xff & inl(fc->addr + AVM_ISACX_DATA); -} - -static void -WriteISAC_V2(void *p, u8 offset, u8 value) -{ - struct fritzcard *fc = p; - - outl(offset, fc->addr + AVM_ISACX_INDEX); - outl(value, fc->addr + AVM_ISACX_DATA); -} - -static void -ReadFiFoISAC_V2(void *p, u8 off, u8 *data, int size) -{ - struct fritzcard *fc = p; - int i; - - outl(off, fc->addr + AVM_ISACX_INDEX); - for (i = 0; i < size; i++) - data[i] = 0xff & inl(fc->addr + AVM_ISACX_DATA); -} - -static void -WriteFiFoISAC_V2(void *p, u8 off, u8 *data, int size) -{ - struct fritzcard *fc = p; - int i; - - outl(off, fc->addr + AVM_ISACX_INDEX); - for (i = 0; i < size; i++) - outl(data[i], fc->addr + AVM_ISACX_DATA); -} - -static struct bchannel * -Sel_BCS(struct fritzcard *fc, u32 channel) -{ - if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) && - (fc->bch[0].nr & channel)) - return &fc->bch[0]; - else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) && - (fc->bch[1].nr & channel)) - return &fc->bch[1]; - else - return NULL; -} - -static inline void -__write_ctrl_pci(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { - u32 idx = channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1; - - outl(idx, fc->addr + CHIP_INDEX); - outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS); -} - -static inline void -__write_ctrl_pciv2(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { - outl(hdlc->ctrl.ctrl, fc->addr + (channel == 2 ? AVM_HDLC_STATUS_2 : - AVM_HDLC_STATUS_1)); -} - -static void -write_ctrl(struct bchannel *bch, int which) { - struct fritzcard *fc = bch->hw; - struct hdlc_hw *hdlc; - - hdlc = &fc->hdlc[(bch->nr - 1) & 1]; - pr_debug("%s: hdlc %c wr%x ctrl %x\n", fc->name, '@' + bch->nr, - which, hdlc->ctrl.ctrl); - switch (fc->type) { - case AVM_FRITZ_PCIV2: - __write_ctrl_pciv2(fc, hdlc, bch->nr); - break; - case AVM_FRITZ_PCI: - __write_ctrl_pci(fc, hdlc, bch->nr); - break; - } -} - - -static inline u32 -__read_status_pci(u_long addr, u32 channel) -{ - outl(channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX); - return inl(addr + CHIP_WINDOW + HDLC_STATUS); -} - -static inline u32 -__read_status_pciv2(u_long addr, u32 channel) -{ - return inl(addr + (channel == 2 ? AVM_HDLC_STATUS_2 : - AVM_HDLC_STATUS_1)); -} - - -static u32 -read_status(struct fritzcard *fc, u32 channel) -{ - switch (fc->type) { - case AVM_FRITZ_PCIV2: - return __read_status_pciv2(fc->addr, channel); - case AVM_FRITZ_PCI: - return __read_status_pci(fc->addr, channel); - } - /* dummy */ - return 0; -} - -static void -enable_hwirq(struct fritzcard *fc) -{ - fc->ctrlreg |= AVM_STATUS0_ENA_IRQ; - outb(fc->ctrlreg, fc->addr + 2); -} - -static void -disable_hwirq(struct fritzcard *fc) -{ - fc->ctrlreg &= ~AVM_STATUS0_ENA_IRQ; - outb(fc->ctrlreg, fc->addr + 2); -} - -static int -modehdlc(struct bchannel *bch, int protocol) -{ - struct fritzcard *fc = bch->hw; - struct hdlc_hw *hdlc; - u8 mode; - - hdlc = &fc->hdlc[(bch->nr - 1) & 1]; - pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name, - '@' + bch->nr, bch->state, protocol, bch->nr); - hdlc->ctrl.ctrl = 0; - mode = (fc->type == AVM_FRITZ_PCIV2) ? HDLC_FIFO_SIZE_128 : 0; - - switch (protocol) { - case -1: /* used for init */ - bch->state = -1; - fallthrough; - case ISDN_P_NONE: - if (bch->state == ISDN_P_NONE) - break; - hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; - hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS; - write_ctrl(bch, 5); - bch->state = ISDN_P_NONE; - test_and_clear_bit(FLG_HDLC, &bch->Flags); - test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); - break; - case ISDN_P_B_RAW: - bch->state = protocol; - hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; - hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS; - write_ctrl(bch, 5); - hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; - write_ctrl(bch, 1); - hdlc->ctrl.sr.cmd = 0; - test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); - break; - case ISDN_P_B_HDLC: - bch->state = protocol; - hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; - hdlc->ctrl.sr.mode = mode | HDLC_MODE_ITF_FLG; - write_ctrl(bch, 5); - hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; - write_ctrl(bch, 1); - hdlc->ctrl.sr.cmd = 0; - test_and_set_bit(FLG_HDLC, &bch->Flags); - break; - default: - pr_info("%s: protocol not known %x\n", fc->name, protocol); - return -ENOPROTOOPT; - } - return 0; -} - -static void -hdlc_empty_fifo(struct bchannel *bch, int count) -{ - u32 *ptr; - u8 *p; - u32 val, addr; - int cnt; - struct fritzcard *fc = bch->hw; - - pr_debug("%s: %s %d\n", fc->name, __func__, count); - if (test_bit(FLG_RX_OFF, &bch->Flags)) { - p = NULL; - bch->dropcnt += count; - } else { - cnt = bchannel_get_rxbuf(bch, count); - if (cnt < 0) { - pr_warn("%s.B%d: No bufferspace for %d bytes\n", - fc->name, bch->nr, count); - return; - } - p = skb_put(bch->rx_skb, count); - } - ptr = (u32 *)p; - if (fc->type == AVM_FRITZ_PCIV2) - addr = fc->addr + (bch->nr == 2 ? - AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); - else { - addr = fc->addr + CHIP_WINDOW; - outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr); - } - cnt = 0; - while (cnt < count) { - val = le32_to_cpu(inl(addr)); - if (p) { - put_unaligned(val, ptr); - ptr++; - } - cnt += 4; - } - if (p && (debug & DEBUG_HW_BFIFO)) { - snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ", - bch->nr, fc->name, count); - print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); - } -} - -static void -hdlc_fill_fifo(struct bchannel *bch) -{ - struct fritzcard *fc = bch->hw; - struct hdlc_hw *hdlc; - int count, fs, cnt = 0, idx; - bool fillempty = false; - u8 *p; - u32 *ptr, val, addr; - - idx = (bch->nr - 1) & 1; - hdlc = &fc->hdlc[idx]; - fs = (fc->type == AVM_FRITZ_PCIV2) ? - HDLC_FIFO_SIZE_V2 : HDLC_FIFO_SIZE_V1; - if (!bch->tx_skb) { - if (!test_bit(FLG_TX_EMPTY, &bch->Flags)) - return; - count = fs; - p = bch->fill; - fillempty = true; - } else { - count = bch->tx_skb->len - bch->tx_idx; - if (count <= 0) - return; - p = bch->tx_skb->data + bch->tx_idx; - } - hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME; - if (count > fs) { - count = fs; - } else { - if (test_bit(FLG_HDLC, &bch->Flags)) - hdlc->ctrl.sr.cmd |= HDLC_CMD_XME; - } - ptr = (u32 *)p; - if (!fillempty) { - pr_debug("%s.B%d: %d/%d/%d", fc->name, bch->nr, count, - bch->tx_idx, bch->tx_skb->len); - bch->tx_idx += count; - } else { - pr_debug("%s.B%d: fillempty %d\n", fc->name, bch->nr, count); - } - hdlc->ctrl.sr.xml = ((count == fs) ? 0 : count); - if (fc->type == AVM_FRITZ_PCIV2) { - __write_ctrl_pciv2(fc, hdlc, bch->nr); - addr = fc->addr + (bch->nr == 2 ? - AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); - } else { - __write_ctrl_pci(fc, hdlc, bch->nr); - addr = fc->addr + CHIP_WINDOW; - } - if (fillempty) { - while (cnt < count) { - /* all bytes the same - no worry about endian */ - outl(*ptr, addr); - cnt += 4; - } - } else { - while (cnt < count) { - val = get_unaligned(ptr); - outl(cpu_to_le32(val), addr); - ptr++; - cnt += 4; - } - } - if ((debug & DEBUG_HW_BFIFO) && !fillempty) { - snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ", - bch->nr, fc->name, count); - print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); - } -} - -static void -HDLC_irq_xpr(struct bchannel *bch) -{ - if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) { - hdlc_fill_fifo(bch); - } else { - dev_kfree_skb(bch->tx_skb); - if (get_next_bframe(bch)) { - hdlc_fill_fifo(bch); - test_and_clear_bit(FLG_TX_EMPTY, &bch->Flags); - } else if (test_bit(FLG_TX_EMPTY, &bch->Flags)) { - hdlc_fill_fifo(bch); - } - } -} - -static void -HDLC_irq(struct bchannel *bch, u32 stat) -{ - struct fritzcard *fc = bch->hw; - int len, fs; - u32 rmlMask; - struct hdlc_hw *hdlc; - - hdlc = &fc->hdlc[(bch->nr - 1) & 1]; - pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat); - if (fc->type == AVM_FRITZ_PCIV2) { - rmlMask = HDLC_STAT_RML_MASK_V2; - fs = HDLC_FIFO_SIZE_V2; - } else { - rmlMask = HDLC_STAT_RML_MASK_V1; - fs = HDLC_FIFO_SIZE_V1; - } - if (stat & HDLC_INT_RPR) { - if (stat & HDLC_STAT_RDO) { - pr_warn("%s: ch%d stat %x RDO\n", - fc->name, bch->nr, stat); - hdlc->ctrl.sr.xml = 0; - hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS; - write_ctrl(bch, 1); - hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS; - write_ctrl(bch, 1); - if (bch->rx_skb) - skb_trim(bch->rx_skb, 0); - } else { - len = (stat & rmlMask) >> 8; - if (!len) - len = fs; - hdlc_empty_fifo(bch, len); - if (!bch->rx_skb) - goto handle_tx; - if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { - recv_Bchannel(bch, 0, false); - } else if (stat & HDLC_STAT_RME) { - if ((stat & HDLC_STAT_CRCVFRRAB) == - HDLC_STAT_CRCVFR) { - recv_Bchannel(bch, 0, false); - } else { - pr_warn("%s: got invalid frame\n", - fc->name); - skb_trim(bch->rx_skb, 0); - } - } - } - } -handle_tx: - if (stat & HDLC_INT_XDU) { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame on HDLC - * in transparent mode we send the next data - */ - pr_warn("%s: ch%d stat %x XDU %s\n", fc->name, bch->nr, - stat, bch->tx_skb ? "tx_skb" : "no tx_skb"); - if (bch->tx_skb && bch->tx_skb->len) { - if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) - bch->tx_idx = 0; - } else if (test_bit(FLG_FILLEMPTY, &bch->Flags)) { - test_and_set_bit(FLG_TX_EMPTY, &bch->Flags); - } - hdlc->ctrl.sr.xml = 0; - hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS; - write_ctrl(bch, 1); - hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS; - HDLC_irq_xpr(bch); - return; - } else if (stat & HDLC_INT_XPR) - HDLC_irq_xpr(bch); -} - -static inline void -HDLC_irq_main(struct fritzcard *fc) -{ - u32 stat; - struct bchannel *bch; - - stat = read_status(fc, 1); - if (stat & HDLC_INT_MASK) { - bch = Sel_BCS(fc, 1); - if (bch) - HDLC_irq(bch, stat); - else - pr_debug("%s: spurious ch1 IRQ\n", fc->name); - } - stat = read_status(fc, 2); - if (stat & HDLC_INT_MASK) { - bch = Sel_BCS(fc, 2); - if (bch) - HDLC_irq(bch, stat); - else - pr_debug("%s: spurious ch2 IRQ\n", fc->name); - } -} - -static irqreturn_t -avm_fritz_interrupt(int intno, void *dev_id) -{ - struct fritzcard *fc = dev_id; - u8 val; - u8 sval; - - spin_lock(&fc->lock); - sval = inb(fc->addr + 2); - pr_debug("%s: irq stat0 %x\n", fc->name, sval); - if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) { - /* shared IRQ from other HW */ - spin_unlock(&fc->lock); - return IRQ_NONE; - } - fc->irqcnt++; - - if (!(sval & AVM_STATUS0_IRQ_ISAC)) { - val = ReadISAC_V1(fc, ISAC_ISTA); - mISDNisac_irq(&fc->isac, val); - } - if (!(sval & AVM_STATUS0_IRQ_HDLC)) - HDLC_irq_main(fc); - spin_unlock(&fc->lock); - return IRQ_HANDLED; -} - -static irqreturn_t -avm_fritzv2_interrupt(int intno, void *dev_id) -{ - struct fritzcard *fc = dev_id; - u8 val; - u8 sval; - - spin_lock(&fc->lock); - sval = inb(fc->addr + 2); - pr_debug("%s: irq stat0 %x\n", fc->name, sval); - if (!(sval & AVM_STATUS0_IRQ_MASK)) { - /* shared IRQ from other HW */ - spin_unlock(&fc->lock); - return IRQ_NONE; - } - fc->irqcnt++; - - if (sval & AVM_STATUS0_IRQ_HDLC) - HDLC_irq_main(fc); - if (sval & AVM_STATUS0_IRQ_ISAC) { - val = ReadISAC_V2(fc, ISACX_ISTA); - mISDNisac_irq(&fc->isac, val); - } - if (sval & AVM_STATUS0_IRQ_TIMER) { - pr_debug("%s: timer irq\n", fc->name); - outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2); - udelay(1); - outb(fc->ctrlreg, fc->addr + 2); - } - spin_unlock(&fc->lock); - return IRQ_HANDLED; -} - -static int -avm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct fritzcard *fc = bch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&fc->lock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - hdlc_fill_fifo(bch); - ret = 0; - } - spin_unlock_irqrestore(&fc->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(&fc->lock, flags); - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) - ret = modehdlc(bch, ch->protocol); - else - ret = 0; - spin_unlock_irqrestore(&fc->lock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - spin_lock_irqsave(&fc->lock, flags); - mISDN_clear_bchannel(bch); - modehdlc(bch, ISDN_P_NONE); - spin_unlock_irqrestore(&fc->lock, flags); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - ret = 0; - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static void -inithdlc(struct fritzcard *fc) -{ - modehdlc(&fc->bch[0], -1); - modehdlc(&fc->bch[1], -1); -} - -static void -clear_pending_hdlc_ints(struct fritzcard *fc) -{ - u32 val; - - val = read_status(fc, 1); - pr_debug("%s: HDLC 1 STA %x\n", fc->name, val); - val = read_status(fc, 2); - pr_debug("%s: HDLC 2 STA %x\n", fc->name, val); -} - -static void -reset_avm(struct fritzcard *fc) -{ - switch (fc->type) { - case AVM_FRITZ_PCI: - fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER; - break; - case AVM_FRITZ_PCIV2: - fc->ctrlreg = AVM_STATUS0_RESET; - break; - } - if (debug & DEBUG_HW) - pr_notice("%s: reset\n", fc->name); - disable_hwirq(fc); - mdelay(5); - switch (fc->type) { - case AVM_FRITZ_PCI: - fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER; - disable_hwirq(fc); - outb(AVM_STATUS1_ENA_IOM, fc->addr + 3); - break; - case AVM_FRITZ_PCIV2: - fc->ctrlreg = 0; - disable_hwirq(fc); - break; - } - mdelay(1); - if (debug & DEBUG_HW) - pr_notice("%s: S0/S1 %x/%x\n", fc->name, - inb(fc->addr + 2), inb(fc->addr + 3)); -} - -static int -init_card(struct fritzcard *fc) -{ - int ret, cnt = 3; - u_long flags; - - reset_avm(fc); /* disable IRQ */ - if (fc->type == AVM_FRITZ_PCIV2) - ret = request_irq(fc->irq, avm_fritzv2_interrupt, - IRQF_SHARED, fc->name, fc); - else - ret = request_irq(fc->irq, avm_fritz_interrupt, - IRQF_SHARED, fc->name, fc); - if (ret) { - pr_info("%s: couldn't get interrupt %d\n", - fc->name, fc->irq); - return ret; - } - while (cnt--) { - spin_lock_irqsave(&fc->lock, flags); - ret = fc->isac.init(&fc->isac); - if (ret) { - spin_unlock_irqrestore(&fc->lock, flags); - pr_info("%s: ISAC init failed with %d\n", - fc->name, ret); - break; - } - clear_pending_hdlc_ints(fc); - inithdlc(fc); - enable_hwirq(fc); - /* RESET Receiver and Transmitter */ - if (fc->type == AVM_FRITZ_PCIV2) { - WriteISAC_V2(fc, ISACX_MASK, 0); - WriteISAC_V2(fc, ISACX_CMDRD, 0x41); - } else { - WriteISAC_V1(fc, ISAC_MASK, 0); - WriteISAC_V1(fc, ISAC_CMDR, 0x41); - } - spin_unlock_irqrestore(&fc->lock, flags); - /* Timeout 10ms */ - msleep_interruptible(10); - if (debug & DEBUG_HW) - pr_notice("%s: IRQ %d count %d\n", fc->name, - fc->irq, fc->irqcnt); - if (!fc->irqcnt) { - pr_info("%s: IRQ(%d) getting no IRQs during init %d\n", - fc->name, fc->irq, 3 - cnt); - reset_avm(fc); - } else - return 0; - } - free_irq(fc->irq, fc); - return -EIO; -} - -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(bch, cq); -} - -static int -avm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct fritzcard *fc = bch->hw; - int ret = -EINVAL; - u_long flags; - - pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - cancel_work_sync(&bch->workq); - spin_lock_irqsave(&fc->lock, flags); - mISDN_clear_bchannel(bch); - modehdlc(bch, ISDN_P_NONE); - spin_unlock_irqrestore(&fc->lock, flags); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bch, arg); - break; - default: - pr_info("%s: %s unknown prim(%x)\n", fc->name, __func__, cmd); - } - return ret; -} - -static int -channel_ctrl(struct fritzcard *fc, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_LOOP: - /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ - if (cq->channel < 0 || cq->channel > 3) { - ret = -EINVAL; - break; - } - ret = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel); - break; - case MISDN_CTRL_L1_TIMER3: - ret = fc->isac.ctrl(&fc->isac, HW_TIMER3_VALUE, cq->p1); - break; - default: - pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -open_bchannel(struct fritzcard *fc, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - bch = &fc->bch[rq->adr.channel - 1]; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - return 0; -} - -/* - * device control function - */ -static int -avm_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct fritzcard *fc = dch->hw; - struct channel_req *rq; - int err = 0; - - pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if (rq->protocol == ISDN_P_TE_S0) - err = fc->isac.open(&fc->isac, rq); - else - err = open_bchannel(fc, rq); - if (err) - break; - if (!try_module_get(THIS_MODULE)) - pr_info("%s: cannot get module\n", fc->name); - break; - case CLOSE_CHANNEL: - pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id, - __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(fc, arg); - break; - default: - pr_debug("%s: %s unknown command %x\n", - fc->name, __func__, cmd); - return -EINVAL; - } - return err; -} - -static int -setup_fritz(struct fritzcard *fc) -{ - u32 val, ver; - - if (!request_region(fc->addr, 32, fc->name)) { - pr_info("%s: AVM config port %x-%x already in use\n", - fc->name, fc->addr, fc->addr + 31); - return -EIO; - } - switch (fc->type) { - case AVM_FRITZ_PCI: - val = inl(fc->addr); - outl(AVM_HDLC_1, fc->addr + CHIP_INDEX); - ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24; - if (debug & DEBUG_HW) { - pr_notice("%s: PCI stat %#x\n", fc->name, val); - pr_notice("%s: PCI Class %X Rev %d\n", fc->name, - val & 0xff, (val >> 8) & 0xff); - pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); - } - ASSIGN_FUNC(V1, ISAC, fc->isac); - fc->isac.type = IPAC_TYPE_ISAC; - break; - case AVM_FRITZ_PCIV2: - val = inl(fc->addr); - ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24; - if (debug & DEBUG_HW) { - pr_notice("%s: PCI V2 stat %#x\n", fc->name, val); - pr_notice("%s: PCI V2 Class %X Rev %d\n", fc->name, - val & 0xff, (val >> 8) & 0xff); - pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); - } - ASSIGN_FUNC(V2, ISAC, fc->isac); - fc->isac.type = IPAC_TYPE_ISACX; - break; - default: - release_region(fc->addr, 32); - pr_info("%s: AVM unknown type %d\n", fc->name, fc->type); - return -ENODEV; - } - pr_notice("%s: %s config irq:%d base:0x%X\n", fc->name, - (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!CARD PCI" : - "AVM Fritz!CARD PCIv2", fc->irq, fc->addr); - return 0; -} - -static void -release_card(struct fritzcard *card) -{ - u_long flags; - - disable_hwirq(card); - spin_lock_irqsave(&card->lock, flags); - modehdlc(&card->bch[0], ISDN_P_NONE); - modehdlc(&card->bch[1], ISDN_P_NONE); - spin_unlock_irqrestore(&card->lock, flags); - card->isac.release(&card->isac); - free_irq(card->irq, card); - mISDN_freebchannel(&card->bch[1]); - mISDN_freebchannel(&card->bch[0]); - mISDN_unregister_device(&card->isac.dch.dev); - release_region(card->addr, 32); - pci_disable_device(card->pdev); - pci_set_drvdata(card->pdev, NULL); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - kfree(card); - AVM_cnt--; -} - -static int -setup_instance(struct fritzcard *card) -{ - int i, err; - unsigned short minsize; - u_long flags; - - snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1); - write_lock_irqsave(&card_lock, flags); - list_add_tail(&card->list, &Cards); - write_unlock_irqrestore(&card_lock, flags); - - _set_debug(card); - card->isac.name = card->name; - spin_lock_init(&card->lock); - card->isac.hwlock = &card->lock; - mISDNisac_init(&card->isac, card); - - card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - card->isac.dch.dev.D.ctrl = avm_dctrl; - for (i = 0; i < 2; i++) { - card->bch[i].nr = i + 1; - set_channelmap(i + 1, card->isac.dch.dev.channelmap); - if (AVM_FRITZ_PCIV2 == card->type) - minsize = HDLC_FIFO_SIZE_V2; - else - minsize = HDLC_FIFO_SIZE_V1; - mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM, minsize); - card->bch[i].hw = card; - card->bch[i].ch.send = avm_l2l1B; - card->bch[i].ch.ctrl = avm_bctrl; - card->bch[i].ch.nr = i + 1; - list_add(&card->bch[i].ch.list, &card->isac.dch.dev.bchannels); - } - err = setup_fritz(card); - if (err) - goto error; - err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev, - card->name); - if (err) - goto error_reg; - err = init_card(card); - if (!err) { - AVM_cnt++; - pr_notice("AVM %d cards installed DEBUG\n", AVM_cnt); - return 0; - } - mISDN_unregister_device(&card->isac.dch.dev); -error_reg: - release_region(card->addr, 32); -error: - card->isac.release(&card->isac); - mISDN_freebchannel(&card->bch[1]); - mISDN_freebchannel(&card->bch[0]); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - kfree(card); - return err; -} - -static int -fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -ENOMEM; - struct fritzcard *card; - - card = kzalloc_obj(struct fritzcard); - if (!card) { - pr_info("No kmem for fritzcard\n"); - return err; - } - if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) - card->type = AVM_FRITZ_PCIV2; - else - card->type = AVM_FRITZ_PCI; - card->pdev = pdev; - err = pci_enable_device(pdev); - if (err) { - kfree(card); - return err; - } - - pr_notice("mISDN: found adapter %s at %s\n", - (char *) ent->driver_data, pci_name(pdev)); - - card->addr = pci_resource_start(pdev, 1); - card->irq = pdev->irq; - pci_set_drvdata(pdev, card); - err = setup_instance(card); - if (err) - pci_set_drvdata(pdev, NULL); - return err; -} - -static void -fritz_remove_pci(struct pci_dev *pdev) -{ - struct fritzcard *card = pci_get_drvdata(pdev); - - if (card) - release_card(card); - else - if (debug) - pr_info("%s: drvdata already removed\n", __func__); -} - -static const struct pci_device_id fcpci_ids[] = { - { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID, - 0, 0, (unsigned long) "Fritz!Card PCI"}, - { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID, - 0, 0, (unsigned long) "Fritz!Card PCI v2" }, - { } -}; -MODULE_DEVICE_TABLE(pci, fcpci_ids); - -static struct pci_driver fcpci_driver = { - .name = "fcpci", - .probe = fritzpci_probe, - .remove = fritz_remove_pci, - .id_table = fcpci_ids, -}; - -static int __init AVM_init(void) -{ - int err; - - pr_notice("AVM Fritz PCI driver Rev. %s\n", AVMFRITZ_REV); - err = pci_register_driver(&fcpci_driver); - return err; -} - -static void __exit AVM_cleanup(void) -{ - pci_unregister_driver(&fcpci_driver); -} - -module_init(AVM_init); -module_exit(AVM_cleanup); diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h deleted file mode 100644 index 5acf826d913c..000000000000 --- a/drivers/isdn/hardware/mISDN/hfc_multi.h +++ /dev/null @@ -1,1236 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * see notice in hfc_multi.c - */ - -#define DEBUG_HFCMULTI_FIFO 0x00010000 -#define DEBUG_HFCMULTI_CRC 0x00020000 -#define DEBUG_HFCMULTI_INIT 0x00040000 -#define DEBUG_HFCMULTI_PLXSD 0x00080000 -#define DEBUG_HFCMULTI_MODE 0x00100000 -#define DEBUG_HFCMULTI_MSG 0x00200000 -#define DEBUG_HFCMULTI_STATE 0x00400000 -#define DEBUG_HFCMULTI_FILL 0x00800000 -#define DEBUG_HFCMULTI_SYNC 0x01000000 -#define DEBUG_HFCMULTI_DTMF 0x02000000 -#define DEBUG_HFCMULTI_LOCK 0x80000000 - -#define PCI_ENA_REGIO 0x01 -#define PCI_ENA_MEMIO 0x02 - -#define XHFC_IRQ 4 /* SIU_IRQ2 */ -#define XHFC_MEMBASE 0xFE000000 -#define XHFC_MEMSIZE 0x00001000 -#define XHFC_OFFSET 0x00001000 -#define PA_XHFC_A0 0x0020 /* PA10 */ -#define PB_XHFC_IRQ1 0x00000100 /* PB23 */ -#define PB_XHFC_IRQ2 0x00000200 /* PB22 */ -#define PB_XHFC_IRQ3 0x00000400 /* PB21 */ -#define PB_XHFC_IRQ4 0x00000800 /* PB20 */ - -/* - * NOTE: some registers are assigned multiple times due to different modes - * also registers are assigned differen for HFC-4s/8s and HFC-E1 - */ - -/* - #define MAX_FRAME_SIZE 2048 -*/ - -struct hfc_chan { - struct dchannel *dch; /* link if channel is a D-channel */ - struct bchannel *bch; /* link if channel is a B-channel */ - int port; /* the interface port this */ - /* channel is associated with */ - int nt_timer; /* -1 if off, 0 if elapsed, >0 if running */ - int los, ais, slip_tx, slip_rx, rdi; /* current alarms */ - int jitter; - u_long cfg; /* port configuration */ - int sync; /* sync state (used by E1) */ - u_int protocol; /* current protocol */ - int slot_tx; /* current pcm slot */ - int bank_tx; /* current pcm bank */ - int slot_rx; - int bank_rx; - int conf; /* conference setting of TX slot */ - int txpending; /* if there is currently data in */ - /* the FIFO 0=no, 1=yes, 2=splloop */ - int Zfill; /* rx-fifo level on last hfcmulti_tx */ - int rx_off; /* set to turn fifo receive off */ - int coeff_count; /* curren coeff block */ - s32 *coeff; /* memory pointer to 8 coeff blocks */ -}; - - -struct hfcm_hw { - u_char r_ctrl; - u_char r_irq_ctrl; - u_char r_cirm; - u_char r_ram_sz; - u_char r_pcm_md0; - u_char r_irqmsk_misc; - u_char r_dtmf; - u_char r_st_sync; - u_char r_sci_msk; - u_char r_tx0, r_tx1; - u_char a_st_ctrl0[8]; - u_char r_bert_wd_md; - timer_t timer; -}; - - -/* for each stack these flags are used (cfg) */ -#define HFC_CFG_NONCAP_TX 1 /* S/T TX interface has less capacity */ -#define HFC_CFG_DIS_ECHANNEL 2 /* disable E-channel processing */ -#define HFC_CFG_REG_ECHANNEL 3 /* register E-channel */ -#define HFC_CFG_OPTICAL 4 /* the E1 interface is optical */ -#define HFC_CFG_REPORT_LOS 5 /* the card should report loss of signal */ -#define HFC_CFG_REPORT_AIS 6 /* the card should report alarm ind. sign. */ -#define HFC_CFG_REPORT_SLIP 7 /* the card should report bit slips */ -#define HFC_CFG_REPORT_RDI 8 /* the card should report remote alarm */ -#define HFC_CFG_DTMF 9 /* enable DTMF-detection */ -#define HFC_CFG_CRC4 10 /* disable CRC-4 Multiframe mode, */ -/* use double frame instead. */ - -#define HFC_TYPE_E1 1 /* controller is HFC-E1 */ -#define HFC_TYPE_4S 4 /* controller is HFC-4S */ -#define HFC_TYPE_8S 8 /* controller is HFC-8S */ -#define HFC_TYPE_XHFC 5 /* controller is XHFC */ - -#define HFC_CHIP_EXRAM_128 0 /* external ram 128k */ -#define HFC_CHIP_EXRAM_512 1 /* external ram 256k */ -#define HFC_CHIP_REVISION0 2 /* old fifo handling */ -#define HFC_CHIP_PCM_SLAVE 3 /* PCM is slave */ -#define HFC_CHIP_PCM_MASTER 4 /* PCM is master */ -#define HFC_CHIP_RX_SYNC 5 /* disable pll sync for pcm */ -#define HFC_CHIP_DTMF 6 /* DTMF decoding is enabled */ -#define HFC_CHIP_CONF 7 /* conference handling is enabled */ -#define HFC_CHIP_ULAW 8 /* ULAW mode */ -#define HFC_CHIP_CLOCK2 9 /* double clock mode */ -#define HFC_CHIP_E1CLOCK_GET 10 /* always get clock from E1 interface */ -#define HFC_CHIP_E1CLOCK_PUT 11 /* always put clock from E1 interface */ -#define HFC_CHIP_WATCHDOG 12 /* whether we should send signals */ -/* to the watchdog */ -#define HFC_CHIP_B410P 13 /* whether we have a b410p with echocan in */ -/* hw */ -#define HFC_CHIP_PLXSD 14 /* whether we have a Speech-Design PLX */ -#define HFC_CHIP_EMBSD 15 /* whether we have a SD Embedded board */ - -#define HFC_IO_MODE_PCIMEM 0x00 /* normal memory mapped IO */ -#define HFC_IO_MODE_REGIO 0x01 /* PCI io access */ -#define HFC_IO_MODE_PLXSD 0x02 /* access HFC via PLX9030 */ -#define HFC_IO_MODE_EMBSD 0x03 /* direct access */ - -/* table entry in the PCI devices list */ -struct hm_map { - char *vendor_name; - char *card_name; - int type; - int ports; - int clock2; - int leds; - int opticalsupport; - int dip_type; - int io_mode; - int irq; -}; - -struct hfc_multi { - struct list_head list; - struct hm_map *mtyp; - int id; - int pcm; /* id of pcm bus */ - int ctype; /* controller type */ - int ports; - - u_int irq; /* irq used by card */ - u_int irqcnt; - struct pci_dev *pci_dev; - int io_mode; /* selects mode */ -#ifdef HFC_REGISTER_DEBUG - void (*HFC_outb)(struct hfc_multi *hc, u_char reg, - u_char val, const char *function, int line); - void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, - u_char val, const char *function, int line); - u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg, - const char *function, int line); - u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg, - const char *function, int line); - u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg, - const char *function, int line); - u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg, - const char *function, int line); - void (*HFC_wait)(struct hfc_multi *hc, - const char *function, int line); - void (*HFC_wait_nodebug)(struct hfc_multi *hc, - const char *function, int line); -#else - void (*HFC_outb)(struct hfc_multi *hc, u_char reg, - u_char val); - void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, - u_char val); - u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg); - u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg); - u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg); - u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg); - void (*HFC_wait)(struct hfc_multi *hc); - void (*HFC_wait_nodebug)(struct hfc_multi *hc); -#endif - void (*read_fifo)(struct hfc_multi *hc, u_char *data, - int len); - void (*write_fifo)(struct hfc_multi *hc, u_char *data, - int len); - u_long pci_origmembase, plx_origmembase; - void __iomem *pci_membase; /* PCI memory */ - void __iomem *plx_membase; /* PLX memory */ - u_long xhfc_origmembase; - u_char *xhfc_membase; - u_long *xhfc_memaddr, *xhfc_memdata; -#ifdef CONFIG_MISDN_HFCMULTI_8xx - struct immap *immap; -#endif - u_long pb_irqmsk; /* Portbit mask to check the IRQ line */ - u_long pci_iobase; /* PCI IO */ - struct hfcm_hw hw; /* remember data of write-only-registers */ - - u_long chip; /* chip configuration */ - int masterclk; /* port that provides master clock -1=off */ - unsigned char silence;/* silence byte */ - unsigned char silence_data[128];/* silence block */ - int dtmf; /* flag that dtmf is currently in process */ - int Flen; /* F-buffer size */ - int Zlen; /* Z-buffer size (must be int for calculation)*/ - int max_trans; /* maximum transparent fifo fill */ - int Zmin; /* Z-buffer offset */ - int DTMFbase; /* base address of DTMF coefficients */ - - u_int slots; /* number of PCM slots */ - u_int leds; /* type of leds */ - u_long ledstate; /* save last state of leds */ - int opticalsupport; /* has the e1 board */ - /* an optical Interface */ - - u_int bmask[32]; /* bitmask of bchannels for port */ - u_char dnum[32]; /* array of used dchannel numbers for port */ - u_char created[32]; /* what port is created */ - u_int activity_tx; /* if there is data TX / RX */ - u_int activity_rx; /* bitmask according to port number */ - /* (will be cleared after */ - /* showing led-states) */ - u_int flash[8]; /* counter for flashing 8 leds on activity */ - - u_long wdcount; /* every 500 ms we need to */ - /* send the watchdog a signal */ - u_char wdbyte; /* watchdog toggle byte */ - int e1_state; /* keep track of last state */ - int e1_getclock; /* if sync is retrieved from interface */ - int syncronized; /* keep track of existing sync interface */ - int e1_resync; /* resync jobs */ - - spinlock_t lock; /* the lock */ - - struct mISDNclock *iclock; /* isdn clock support */ - int iclock_on; - - /* - * the channel index is counted from 0, regardless where the channel - * is located on the hfc-channel. - * the bch->channel is equvalent to the hfc-channel - */ - struct hfc_chan chan[32]; - signed char slot_owner[256]; /* owner channel of slot */ -}; - -/* PLX GPIOs */ -#define PLX_GPIO4_DIR_BIT 13 -#define PLX_GPIO4_BIT 14 -#define PLX_GPIO5_DIR_BIT 16 -#define PLX_GPIO5_BIT 17 -#define PLX_GPIO6_DIR_BIT 19 -#define PLX_GPIO6_BIT 20 -#define PLX_GPIO7_DIR_BIT 22 -#define PLX_GPIO7_BIT 23 -#define PLX_GPIO8_DIR_BIT 25 -#define PLX_GPIO8_BIT 26 - -#define PLX_GPIO4 (1 << PLX_GPIO4_BIT) -#define PLX_GPIO5 (1 << PLX_GPIO5_BIT) -#define PLX_GPIO6 (1 << PLX_GPIO6_BIT) -#define PLX_GPIO7 (1 << PLX_GPIO7_BIT) -#define PLX_GPIO8 (1 << PLX_GPIO8_BIT) - -#define PLX_GPIO4_DIR (1 << PLX_GPIO4_DIR_BIT) -#define PLX_GPIO5_DIR (1 << PLX_GPIO5_DIR_BIT) -#define PLX_GPIO6_DIR (1 << PLX_GPIO6_DIR_BIT) -#define PLX_GPIO7_DIR (1 << PLX_GPIO7_DIR_BIT) -#define PLX_GPIO8_DIR (1 << PLX_GPIO8_DIR_BIT) - -#define PLX_TERM_ON PLX_GPIO7 -#define PLX_SLAVE_EN_N PLX_GPIO5 -#define PLX_MASTER_EN PLX_GPIO6 -#define PLX_SYNC_O_EN PLX_GPIO4 -#define PLX_DSP_RES_N PLX_GPIO8 -/* GPIO4..8 Enable & Set to OUT, SLAVE_EN_N = 1 */ -#define PLX_GPIOC_INIT (PLX_GPIO4_DIR | PLX_GPIO5_DIR | PLX_GPIO6_DIR \ - | PLX_GPIO7_DIR | PLX_GPIO8_DIR | PLX_SLAVE_EN_N) - -/* PLX Interrupt Control/STATUS */ -#define PLX_INTCSR_LINTI1_ENABLE 0x01 -#define PLX_INTCSR_LINTI1_STATUS 0x04 -#define PLX_INTCSR_LINTI2_ENABLE 0x08 -#define PLX_INTCSR_LINTI2_STATUS 0x20 -#define PLX_INTCSR_PCIINT_ENABLE 0x40 - -/* PLX Registers */ -#define PLX_INTCSR 0x4c -#define PLX_CNTRL 0x50 -#define PLX_GPIOC 0x54 - - -/* - * REGISTER SETTING FOR HFC-4S/8S AND HFC-E1 - */ - -/* write only registers */ -#define R_CIRM 0x00 -#define R_CTRL 0x01 -#define R_BRG_PCM_CFG 0x02 -#define R_RAM_ADDR0 0x08 -#define R_RAM_ADDR1 0x09 -#define R_RAM_ADDR2 0x0A -#define R_FIRST_FIFO 0x0B -#define R_RAM_SZ 0x0C -#define R_FIFO_MD 0x0D -#define R_INC_RES_FIFO 0x0E -#define R_FSM_IDX 0x0F -#define R_FIFO 0x0F -#define R_SLOT 0x10 -#define R_IRQMSK_MISC 0x11 -#define R_SCI_MSK 0x12 -#define R_IRQ_CTRL 0x13 -#define R_PCM_MD0 0x14 -#define R_PCM_MD1 0x15 -#define R_PCM_MD2 0x15 -#define R_SH0H 0x15 -#define R_SH1H 0x15 -#define R_SH0L 0x15 -#define R_SH1L 0x15 -#define R_SL_SEL0 0x15 -#define R_SL_SEL1 0x15 -#define R_SL_SEL2 0x15 -#define R_SL_SEL3 0x15 -#define R_SL_SEL4 0x15 -#define R_SL_SEL5 0x15 -#define R_SL_SEL6 0x15 -#define R_SL_SEL7 0x15 -#define R_ST_SEL 0x16 -#define R_ST_SYNC 0x17 -#define R_CONF_EN 0x18 -#define R_TI_WD 0x1A -#define R_BERT_WD_MD 0x1B -#define R_DTMF 0x1C -#define R_DTMF_N 0x1D -#define R_E1_WR_STA 0x20 -#define R_E1_RD_STA 0x20 -#define R_LOS0 0x22 -#define R_LOS1 0x23 -#define R_RX0 0x24 -#define R_RX_FR0 0x25 -#define R_RX_FR1 0x26 -#define R_TX0 0x28 -#define R_TX1 0x29 -#define R_TX_FR0 0x2C - -#define R_TX_FR1 0x2D -#define R_TX_FR2 0x2E -#define R_JATT_ATT 0x2F /* undocumented */ -#define A_ST_RD_STATE 0x30 -#define A_ST_WR_STATE 0x30 -#define R_RX_OFF 0x30 -#define A_ST_CTRL0 0x31 -#define R_SYNC_OUT 0x31 -#define A_ST_CTRL1 0x32 -#define A_ST_CTRL2 0x33 -#define A_ST_SQ_WR 0x34 -#define R_TX_OFF 0x34 -#define R_SYNC_CTRL 0x35 -#define A_ST_CLK_DLY 0x37 -#define R_PWM0 0x38 -#define R_PWM1 0x39 -#define A_ST_B1_TX 0x3C -#define A_ST_B2_TX 0x3D -#define A_ST_D_TX 0x3E -#define R_GPIO_OUT0 0x40 -#define R_GPIO_OUT1 0x41 -#define R_GPIO_EN0 0x42 -#define R_GPIO_EN1 0x43 -#define R_GPIO_SEL 0x44 -#define R_BRG_CTRL 0x45 -#define R_PWM_MD 0x46 -#define R_BRG_MD 0x47 -#define R_BRG_TIM0 0x48 -#define R_BRG_TIM1 0x49 -#define R_BRG_TIM2 0x4A -#define R_BRG_TIM3 0x4B -#define R_BRG_TIM_SEL01 0x4C -#define R_BRG_TIM_SEL23 0x4D -#define R_BRG_TIM_SEL45 0x4E -#define R_BRG_TIM_SEL67 0x4F -#define A_SL_CFG 0xD0 -#define A_CONF 0xD1 -#define A_CH_MSK 0xF4 -#define A_CON_HDLC 0xFA -#define A_SUBCH_CFG 0xFB -#define A_CHANNEL 0xFC -#define A_FIFO_SEQ 0xFD -#define A_IRQ_MSK 0xFF - -/* read only registers */ -#define A_Z12 0x04 -#define A_Z1L 0x04 -#define A_Z1 0x04 -#define A_Z1H 0x05 -#define A_Z2L 0x06 -#define A_Z2 0x06 -#define A_Z2H 0x07 -#define A_F1 0x0C -#define A_F12 0x0C -#define A_F2 0x0D -#define R_IRQ_OVIEW 0x10 -#define R_IRQ_MISC 0x11 -#define R_IRQ_STATECH 0x12 -#define R_CONF_OFLOW 0x14 -#define R_RAM_USE 0x15 -#define R_CHIP_ID 0x16 -#define R_BERT_STA 0x17 -#define R_F0_CNTL 0x18 -#define R_F0_CNTH 0x19 -#define R_BERT_EC 0x1A -#define R_BERT_ECL 0x1A -#define R_BERT_ECH 0x1B -#define R_STATUS 0x1C -#define R_CHIP_RV 0x1F -#define R_STATE 0x20 -#define R_SYNC_STA 0x24 -#define R_RX_SL0_0 0x25 -#define R_RX_SL0_1 0x26 -#define R_RX_SL0_2 0x27 -#define R_JATT_DIR 0x2b /* undocumented */ -#define R_SLIP 0x2c -#define A_ST_RD_STA 0x30 -#define R_FAS_EC 0x30 -#define R_FAS_ECL 0x30 -#define R_FAS_ECH 0x31 -#define R_VIO_EC 0x32 -#define R_VIO_ECL 0x32 -#define R_VIO_ECH 0x33 -#define A_ST_SQ_RD 0x34 -#define R_CRC_EC 0x34 -#define R_CRC_ECL 0x34 -#define R_CRC_ECH 0x35 -#define R_E_EC 0x36 -#define R_E_ECL 0x36 -#define R_E_ECH 0x37 -#define R_SA6_SA13_EC 0x38 -#define R_SA6_SA13_ECL 0x38 -#define R_SA6_SA13_ECH 0x39 -#define R_SA6_SA23_EC 0x3A -#define R_SA6_SA23_ECL 0x3A -#define R_SA6_SA23_ECH 0x3B -#define A_ST_B1_RX 0x3C -#define A_ST_B2_RX 0x3D -#define A_ST_D_RX 0x3E -#define A_ST_E_RX 0x3F -#define R_GPIO_IN0 0x40 -#define R_GPIO_IN1 0x41 -#define R_GPI_IN0 0x44 -#define R_GPI_IN1 0x45 -#define R_GPI_IN2 0x46 -#define R_GPI_IN3 0x47 -#define R_INT_DATA 0x88 -#define R_IRQ_FIFO_BL0 0xC8 -#define R_IRQ_FIFO_BL1 0xC9 -#define R_IRQ_FIFO_BL2 0xCA -#define R_IRQ_FIFO_BL3 0xCB -#define R_IRQ_FIFO_BL4 0xCC -#define R_IRQ_FIFO_BL5 0xCD -#define R_IRQ_FIFO_BL6 0xCE -#define R_IRQ_FIFO_BL7 0xCF - -/* read and write registers */ -#define A_FIFO_DATA0 0x80 -#define A_FIFO_DATA1 0x80 -#define A_FIFO_DATA2 0x80 -#define A_FIFO_DATA0_NOINC 0x84 -#define A_FIFO_DATA1_NOINC 0x84 -#define A_FIFO_DATA2_NOINC 0x84 -#define R_RAM_DATA 0xC0 - - -/* - * BIT SETTING FOR HFC-4S/8S AND HFC-E1 - */ - -/* chapter 2: universal bus interface */ -/* R_CIRM */ -#define V_IRQ_SEL 0x01 -#define V_SRES 0x08 -#define V_HFCRES 0x10 -#define V_PCMRES 0x20 -#define V_STRES 0x40 -#define V_ETRES 0x40 -#define V_RLD_EPR 0x80 -/* R_CTRL */ -#define V_FIFO_LPRIO 0x02 -#define V_SLOW_RD 0x04 -#define V_EXT_RAM 0x08 -#define V_CLK_OFF 0x20 -#define V_ST_CLK 0x40 -/* R_RAM_ADDR0 */ -#define V_RAM_ADDR2 0x01 -#define V_ADDR_RES 0x40 -#define V_ADDR_INC 0x80 -/* R_RAM_SZ */ -#define V_RAM_SZ 0x01 -#define V_PWM0_16KHZ 0x10 -#define V_PWM1_16KHZ 0x20 -#define V_FZ_MD 0x80 -/* R_CHIP_ID */ -#define V_PNP_IRQ 0x01 -#define V_CHIP_ID 0x10 - -/* chapter 3: data flow */ -/* R_FIRST_FIFO */ -#define V_FIRST_FIRO_DIR 0x01 -#define V_FIRST_FIFO_NUM 0x02 -/* R_FIFO_MD */ -#define V_FIFO_MD 0x01 -#define V_CSM_MD 0x04 -#define V_FSM_MD 0x08 -#define V_FIFO_SZ 0x10 -/* R_FIFO */ -#define V_FIFO_DIR 0x01 -#define V_FIFO_NUM 0x02 -#define V_REV 0x80 -/* R_SLOT */ -#define V_SL_DIR 0x01 -#define V_SL_NUM 0x02 -/* A_SL_CFG */ -#define V_CH_DIR 0x01 -#define V_CH_SEL 0x02 -#define V_ROUTING 0x40 -/* A_CON_HDLC */ -#define V_IFF 0x01 -#define V_HDLC_TRP 0x02 -#define V_TRP_IRQ 0x04 -#define V_DATA_FLOW 0x20 -/* A_SUBCH_CFG */ -#define V_BIT_CNT 0x01 -#define V_START_BIT 0x08 -#define V_LOOP_FIFO 0x40 -#define V_INV_DATA 0x80 -/* A_CHANNEL */ -#define V_CH_DIR0 0x01 -#define V_CH_NUM0 0x02 -/* A_FIFO_SEQ */ -#define V_NEXT_FIFO_DIR 0x01 -#define V_NEXT_FIFO_NUM 0x02 -#define V_SEQ_END 0x40 - -/* chapter 4: FIFO handling and HDLC controller */ -/* R_INC_RES_FIFO */ -#define V_INC_F 0x01 -#define V_RES_F 0x02 -#define V_RES_LOST 0x04 - -/* chapter 5: S/T interface */ -/* R_SCI_MSK */ -#define V_SCI_MSK_ST0 0x01 -#define V_SCI_MSK_ST1 0x02 -#define V_SCI_MSK_ST2 0x04 -#define V_SCI_MSK_ST3 0x08 -#define V_SCI_MSK_ST4 0x10 -#define V_SCI_MSK_ST5 0x20 -#define V_SCI_MSK_ST6 0x40 -#define V_SCI_MSK_ST7 0x80 -/* R_ST_SEL */ -#define V_ST_SEL 0x01 -#define V_MULT_ST 0x08 -/* R_ST_SYNC */ -#define V_SYNC_SEL 0x01 -#define V_AUTO_SYNC 0x08 -/* A_ST_WR_STA */ -#define V_ST_SET_STA 0x01 -#define V_ST_LD_STA 0x10 -#define V_ST_ACT 0x20 -#define V_SET_G2_G3 0x80 -/* A_ST_CTRL0 */ -#define V_B1_EN 0x01 -#define V_B2_EN 0x02 -#define V_ST_MD 0x04 -#define V_D_PRIO 0x08 -#define V_SQ_EN 0x10 -#define V_96KHZ 0x20 -#define V_TX_LI 0x40 -#define V_ST_STOP 0x80 -/* A_ST_CTRL1 */ -#define V_G2_G3_EN 0x01 -#define V_D_HI 0x04 -#define V_E_IGNO 0x08 -#define V_E_LO 0x10 -#define V_B12_SWAP 0x80 -/* A_ST_CTRL2 */ -#define V_B1_RX_EN 0x01 -#define V_B2_RX_EN 0x02 -#define V_ST_TRIS 0x40 -/* A_ST_CLK_DLY */ -#define V_ST_CK_DLY 0x01 -#define V_ST_SMPL 0x10 -/* A_ST_D_TX */ -#define V_ST_D_TX 0x40 -/* R_IRQ_STATECH */ -#define V_SCI_ST0 0x01 -#define V_SCI_ST1 0x02 -#define V_SCI_ST2 0x04 -#define V_SCI_ST3 0x08 -#define V_SCI_ST4 0x10 -#define V_SCI_ST5 0x20 -#define V_SCI_ST6 0x40 -#define V_SCI_ST7 0x80 -/* A_ST_RD_STA */ -#define V_ST_STA 0x01 -#define V_FR_SYNC_ST 0x10 -#define V_TI2_EXP 0x20 -#define V_INFO0 0x40 -#define V_G2_G3 0x80 -/* A_ST_SQ_RD */ -#define V_ST_SQ 0x01 -#define V_MF_RX_RDY 0x10 -#define V_MF_TX_RDY 0x80 -/* A_ST_D_RX */ -#define V_ST_D_RX 0x40 -/* A_ST_E_RX */ -#define V_ST_E_RX 0x40 - -/* chapter 5: E1 interface */ -/* R_E1_WR_STA */ -/* R_E1_RD_STA */ -#define V_E1_SET_STA 0x01 -#define V_E1_LD_STA 0x10 -/* R_RX0 */ -#define V_RX_CODE 0x01 -#define V_RX_FBAUD 0x04 -#define V_RX_CMI 0x08 -#define V_RX_INV_CMI 0x10 -#define V_RX_INV_CLK 0x20 -#define V_RX_INV_DATA 0x40 -#define V_AIS_ITU 0x80 -/* R_RX_FR0 */ -#define V_NO_INSYNC 0x01 -#define V_AUTO_RESYNC 0x02 -#define V_AUTO_RECO 0x04 -#define V_SWORD_COND 0x08 -#define V_SYNC_LOSS 0x10 -#define V_XCRC_SYNC 0x20 -#define V_MF_RESYNC 0x40 -#define V_RESYNC 0x80 -/* R_RX_FR1 */ -#define V_RX_MF 0x01 -#define V_RX_MF_SYNC 0x02 -#define V_RX_SL0_RAM 0x04 -#define V_ERR_SIM 0x20 -#define V_RES_NMF 0x40 -/* R_TX0 */ -#define V_TX_CODE 0x01 -#define V_TX_FBAUD 0x04 -#define V_TX_CMI_CODE 0x08 -#define V_TX_INV_CMI_CODE 0x10 -#define V_TX_INV_CLK 0x20 -#define V_TX_INV_DATA 0x40 -#define V_OUT_EN 0x80 -/* R_TX1 */ -#define V_INV_CLK 0x01 -#define V_EXCHG_DATA_LI 0x02 -#define V_AIS_OUT 0x04 -#define V_ATX 0x20 -#define V_NTRI 0x40 -#define V_AUTO_ERR_RES 0x80 -/* R_TX_FR0 */ -#define V_TRP_FAS 0x01 -#define V_TRP_NFAS 0x02 -#define V_TRP_RAL 0x04 -#define V_TRP_SA 0x08 -/* R_TX_FR1 */ -#define V_TX_FAS 0x01 -#define V_TX_NFAS 0x02 -#define V_TX_RAL 0x04 -#define V_TX_SA 0x08 -/* R_TX_FR2 */ -#define V_TX_MF 0x01 -#define V_TRP_SL0 0x02 -#define V_TX_SL0_RAM 0x04 -#define V_TX_E 0x10 -#define V_NEG_E 0x20 -#define V_XS12_ON 0x40 -#define V_XS15_ON 0x80 -/* R_RX_OFF */ -#define V_RX_SZ 0x01 -#define V_RX_INIT 0x04 -/* R_SYNC_OUT */ -#define V_SYNC_E1_RX 0x01 -#define V_IPATS0 0x20 -#define V_IPATS1 0x40 -#define V_IPATS2 0x80 -/* R_TX_OFF */ -#define V_TX_SZ 0x01 -#define V_TX_INIT 0x04 -/* R_SYNC_CTRL */ -#define V_EXT_CLK_SYNC 0x01 -#define V_SYNC_OFFS 0x02 -#define V_PCM_SYNC 0x04 -#define V_NEG_CLK 0x08 -#define V_HCLK 0x10 -/* - #define V_JATT_AUTO_DEL 0x20 - #define V_JATT_AUTO 0x40 -*/ -#define V_JATT_OFF 0x80 -/* R_STATE */ -#define V_E1_STA 0x01 -#define V_ALT_FR_RX 0x40 -#define V_ALT_FR_TX 0x80 -/* R_SYNC_STA */ -#define V_RX_STA 0x01 -#define V_FR_SYNC_E1 0x04 -#define V_SIG_LOS 0x08 -#define V_MFA_STA 0x10 -#define V_AIS 0x40 -#define V_NO_MF_SYNC 0x80 -/* R_RX_SL0_0 */ -#define V_SI_FAS 0x01 -#define V_SI_NFAS 0x02 -#define V_A 0x04 -#define V_CRC_OK 0x08 -#define V_TX_E1 0x10 -#define V_TX_E2 0x20 -#define V_RX_E1 0x40 -#define V_RX_E2 0x80 -/* R_SLIP */ -#define V_SLIP_RX 0x01 -#define V_FOSLIP_RX 0x08 -#define V_SLIP_TX 0x10 -#define V_FOSLIP_TX 0x80 - -/* chapter 6: PCM interface */ -/* R_PCM_MD0 */ -#define V_PCM_MD 0x01 -#define V_C4_POL 0x02 -#define V_F0_NEG 0x04 -#define V_F0_LEN 0x08 -#define V_PCM_ADDR 0x10 -/* R_SL_SEL0 */ -#define V_SL_SEL0 0x01 -#define V_SH_SEL0 0x80 -/* R_SL_SEL1 */ -#define V_SL_SEL1 0x01 -#define V_SH_SEL1 0x80 -/* R_SL_SEL2 */ -#define V_SL_SEL2 0x01 -#define V_SH_SEL2 0x80 -/* R_SL_SEL3 */ -#define V_SL_SEL3 0x01 -#define V_SH_SEL3 0x80 -/* R_SL_SEL4 */ -#define V_SL_SEL4 0x01 -#define V_SH_SEL4 0x80 -/* R_SL_SEL5 */ -#define V_SL_SEL5 0x01 -#define V_SH_SEL5 0x80 -/* R_SL_SEL6 */ -#define V_SL_SEL6 0x01 -#define V_SH_SEL6 0x80 -/* R_SL_SEL7 */ -#define V_SL_SEL7 0x01 -#define V_SH_SEL7 0x80 -/* R_PCM_MD1 */ -#define V_ODEC_CON 0x01 -#define V_PLL_ADJ 0x04 -#define V_PCM_DR 0x10 -#define V_PCM_LOOP 0x40 -/* R_PCM_MD2 */ -#define V_SYNC_PLL 0x02 -#define V_SYNC_SRC 0x04 -#define V_SYNC_OUT 0x08 -#define V_ICR_FR_TIME 0x40 -#define V_EN_PLL 0x80 - -/* chapter 7: pulse width modulation */ -/* R_PWM_MD */ -#define V_EXT_IRQ_EN 0x08 -#define V_PWM0_MD 0x10 -#define V_PWM1_MD 0x40 - -/* chapter 8: multiparty audio conferences */ -/* R_CONF_EN */ -#define V_CONF_EN 0x01 -#define V_ULAW 0x80 -/* A_CONF */ -#define V_CONF_NUM 0x01 -#define V_NOISE_SUPPR 0x08 -#define V_ATT_LEV 0x20 -#define V_CONF_SL 0x80 -/* R_CONF_OFLOW */ -#define V_CONF_OFLOW0 0x01 -#define V_CONF_OFLOW1 0x02 -#define V_CONF_OFLOW2 0x04 -#define V_CONF_OFLOW3 0x08 -#define V_CONF_OFLOW4 0x10 -#define V_CONF_OFLOW5 0x20 -#define V_CONF_OFLOW6 0x40 -#define V_CONF_OFLOW7 0x80 - -/* chapter 9: DTMF contoller */ -/* R_DTMF0 */ -#define V_DTMF_EN 0x01 -#define V_HARM_SEL 0x02 -#define V_DTMF_RX_CH 0x04 -#define V_DTMF_STOP 0x08 -#define V_CHBL_SEL 0x10 -#define V_RST_DTMF 0x40 -#define V_ULAW_SEL 0x80 - -/* chapter 10: BERT */ -/* R_BERT_WD_MD */ -#define V_PAT_SEQ 0x01 -#define V_BERT_ERR 0x08 -#define V_AUTO_WD_RES 0x20 -#define V_WD_RES 0x80 -/* R_BERT_STA */ -#define V_BERT_SYNC_SRC 0x01 -#define V_BERT_SYNC 0x10 -#define V_BERT_INV_DATA 0x20 - -/* chapter 11: auxiliary interface */ -/* R_BRG_PCM_CFG */ -#define V_BRG_EN 0x01 -#define V_BRG_MD 0x02 -#define V_PCM_CLK 0x20 -#define V_ADDR_WRDLY 0x40 -/* R_BRG_CTRL */ -#define V_BRG_CS 0x01 -#define V_BRG_ADDR 0x08 -#define V_BRG_CS_SRC 0x80 -/* R_BRG_MD */ -#define V_BRG_MD0 0x01 -#define V_BRG_MD1 0x02 -#define V_BRG_MD2 0x04 -#define V_BRG_MD3 0x08 -#define V_BRG_MD4 0x10 -#define V_BRG_MD5 0x20 -#define V_BRG_MD6 0x40 -#define V_BRG_MD7 0x80 -/* R_BRG_TIM0 */ -#define V_BRG_TIM0_IDLE 0x01 -#define V_BRG_TIM0_CLK 0x10 -/* R_BRG_TIM1 */ -#define V_BRG_TIM1_IDLE 0x01 -#define V_BRG_TIM1_CLK 0x10 -/* R_BRG_TIM2 */ -#define V_BRG_TIM2_IDLE 0x01 -#define V_BRG_TIM2_CLK 0x10 -/* R_BRG_TIM3 */ -#define V_BRG_TIM3_IDLE 0x01 -#define V_BRG_TIM3_CLK 0x10 -/* R_BRG_TIM_SEL01 */ -#define V_BRG_WR_SEL0 0x01 -#define V_BRG_RD_SEL0 0x04 -#define V_BRG_WR_SEL1 0x10 -#define V_BRG_RD_SEL1 0x40 -/* R_BRG_TIM_SEL23 */ -#define V_BRG_WR_SEL2 0x01 -#define V_BRG_RD_SEL2 0x04 -#define V_BRG_WR_SEL3 0x10 -#define V_BRG_RD_SEL3 0x40 -/* R_BRG_TIM_SEL45 */ -#define V_BRG_WR_SEL4 0x01 -#define V_BRG_RD_SEL4 0x04 -#define V_BRG_WR_SEL5 0x10 -#define V_BRG_RD_SEL5 0x40 -/* R_BRG_TIM_SEL67 */ -#define V_BRG_WR_SEL6 0x01 -#define V_BRG_RD_SEL6 0x04 -#define V_BRG_WR_SEL7 0x10 -#define V_BRG_RD_SEL7 0x40 - -/* chapter 12: clock, reset, interrupt, timer and watchdog */ -/* R_IRQMSK_MISC */ -#define V_STA_IRQMSK 0x01 -#define V_TI_IRQMSK 0x02 -#define V_PROC_IRQMSK 0x04 -#define V_DTMF_IRQMSK 0x08 -#define V_IRQ1S_MSK 0x10 -#define V_SA6_IRQMSK 0x20 -#define V_RX_EOMF_MSK 0x40 -#define V_TX_EOMF_MSK 0x80 -/* R_IRQ_CTRL */ -#define V_FIFO_IRQ 0x01 -#define V_GLOB_IRQ_EN 0x08 -#define V_IRQ_POL 0x10 -/* R_TI_WD */ -#define V_EV_TS 0x01 -#define V_WD_TS 0x10 -/* A_IRQ_MSK */ -#define V_IRQ 0x01 -#define V_BERT_EN 0x02 -#define V_MIX_IRQ 0x04 -/* R_IRQ_OVIEW */ -#define V_IRQ_FIFO_BL0 0x01 -#define V_IRQ_FIFO_BL1 0x02 -#define V_IRQ_FIFO_BL2 0x04 -#define V_IRQ_FIFO_BL3 0x08 -#define V_IRQ_FIFO_BL4 0x10 -#define V_IRQ_FIFO_BL5 0x20 -#define V_IRQ_FIFO_BL6 0x40 -#define V_IRQ_FIFO_BL7 0x80 -/* R_IRQ_MISC */ -#define V_STA_IRQ 0x01 -#define V_TI_IRQ 0x02 -#define V_IRQ_PROC 0x04 -#define V_DTMF_IRQ 0x08 -#define V_IRQ1S 0x10 -#define V_SA6_IRQ 0x20 -#define V_RX_EOMF 0x40 -#define V_TX_EOMF 0x80 -/* R_STATUS */ -#define V_BUSY 0x01 -#define V_PROC 0x02 -#define V_DTMF_STA 0x04 -#define V_LOST_STA 0x08 -#define V_SYNC_IN 0x10 -#define V_EXT_IRQSTA 0x20 -#define V_MISC_IRQSTA 0x40 -#define V_FR_IRQSTA 0x80 -/* R_IRQ_FIFO_BL0 */ -#define V_IRQ_FIFO0_TX 0x01 -#define V_IRQ_FIFO0_RX 0x02 -#define V_IRQ_FIFO1_TX 0x04 -#define V_IRQ_FIFO1_RX 0x08 -#define V_IRQ_FIFO2_TX 0x10 -#define V_IRQ_FIFO2_RX 0x20 -#define V_IRQ_FIFO3_TX 0x40 -#define V_IRQ_FIFO3_RX 0x80 -/* R_IRQ_FIFO_BL1 */ -#define V_IRQ_FIFO4_TX 0x01 -#define V_IRQ_FIFO4_RX 0x02 -#define V_IRQ_FIFO5_TX 0x04 -#define V_IRQ_FIFO5_RX 0x08 -#define V_IRQ_FIFO6_TX 0x10 -#define V_IRQ_FIFO6_RX 0x20 -#define V_IRQ_FIFO7_TX 0x40 -#define V_IRQ_FIFO7_RX 0x80 -/* R_IRQ_FIFO_BL2 */ -#define V_IRQ_FIFO8_TX 0x01 -#define V_IRQ_FIFO8_RX 0x02 -#define V_IRQ_FIFO9_TX 0x04 -#define V_IRQ_FIFO9_RX 0x08 -#define V_IRQ_FIFO10_TX 0x10 -#define V_IRQ_FIFO10_RX 0x20 -#define V_IRQ_FIFO11_TX 0x40 -#define V_IRQ_FIFO11_RX 0x80 -/* R_IRQ_FIFO_BL3 */ -#define V_IRQ_FIFO12_TX 0x01 -#define V_IRQ_FIFO12_RX 0x02 -#define V_IRQ_FIFO13_TX 0x04 -#define V_IRQ_FIFO13_RX 0x08 -#define V_IRQ_FIFO14_TX 0x10 -#define V_IRQ_FIFO14_RX 0x20 -#define V_IRQ_FIFO15_TX 0x40 -#define V_IRQ_FIFO15_RX 0x80 -/* R_IRQ_FIFO_BL4 */ -#define V_IRQ_FIFO16_TX 0x01 -#define V_IRQ_FIFO16_RX 0x02 -#define V_IRQ_FIFO17_TX 0x04 -#define V_IRQ_FIFO17_RX 0x08 -#define V_IRQ_FIFO18_TX 0x10 -#define V_IRQ_FIFO18_RX 0x20 -#define V_IRQ_FIFO19_TX 0x40 -#define V_IRQ_FIFO19_RX 0x80 -/* R_IRQ_FIFO_BL5 */ -#define V_IRQ_FIFO20_TX 0x01 -#define V_IRQ_FIFO20_RX 0x02 -#define V_IRQ_FIFO21_TX 0x04 -#define V_IRQ_FIFO21_RX 0x08 -#define V_IRQ_FIFO22_TX 0x10 -#define V_IRQ_FIFO22_RX 0x20 -#define V_IRQ_FIFO23_TX 0x40 -#define V_IRQ_FIFO23_RX 0x80 -/* R_IRQ_FIFO_BL6 */ -#define V_IRQ_FIFO24_TX 0x01 -#define V_IRQ_FIFO24_RX 0x02 -#define V_IRQ_FIFO25_TX 0x04 -#define V_IRQ_FIFO25_RX 0x08 -#define V_IRQ_FIFO26_TX 0x10 -#define V_IRQ_FIFO26_RX 0x20 -#define V_IRQ_FIFO27_TX 0x40 -#define V_IRQ_FIFO27_RX 0x80 -/* R_IRQ_FIFO_BL7 */ -#define V_IRQ_FIFO28_TX 0x01 -#define V_IRQ_FIFO28_RX 0x02 -#define V_IRQ_FIFO29_TX 0x04 -#define V_IRQ_FIFO29_RX 0x08 -#define V_IRQ_FIFO30_TX 0x10 -#define V_IRQ_FIFO30_RX 0x20 -#define V_IRQ_FIFO31_TX 0x40 -#define V_IRQ_FIFO31_RX 0x80 - -/* chapter 13: general purpose I/O pins (GPIO) and input pins (GPI) */ -/* R_GPIO_OUT0 */ -#define V_GPIO_OUT0 0x01 -#define V_GPIO_OUT1 0x02 -#define V_GPIO_OUT2 0x04 -#define V_GPIO_OUT3 0x08 -#define V_GPIO_OUT4 0x10 -#define V_GPIO_OUT5 0x20 -#define V_GPIO_OUT6 0x40 -#define V_GPIO_OUT7 0x80 -/* R_GPIO_OUT1 */ -#define V_GPIO_OUT8 0x01 -#define V_GPIO_OUT9 0x02 -#define V_GPIO_OUT10 0x04 -#define V_GPIO_OUT11 0x08 -#define V_GPIO_OUT12 0x10 -#define V_GPIO_OUT13 0x20 -#define V_GPIO_OUT14 0x40 -#define V_GPIO_OUT15 0x80 -/* R_GPIO_EN0 */ -#define V_GPIO_EN0 0x01 -#define V_GPIO_EN1 0x02 -#define V_GPIO_EN2 0x04 -#define V_GPIO_EN3 0x08 -#define V_GPIO_EN4 0x10 -#define V_GPIO_EN5 0x20 -#define V_GPIO_EN6 0x40 -#define V_GPIO_EN7 0x80 -/* R_GPIO_EN1 */ -#define V_GPIO_EN8 0x01 -#define V_GPIO_EN9 0x02 -#define V_GPIO_EN10 0x04 -#define V_GPIO_EN11 0x08 -#define V_GPIO_EN12 0x10 -#define V_GPIO_EN13 0x20 -#define V_GPIO_EN14 0x40 -#define V_GPIO_EN15 0x80 -/* R_GPIO_SEL */ -#define V_GPIO_SEL0 0x01 -#define V_GPIO_SEL1 0x02 -#define V_GPIO_SEL2 0x04 -#define V_GPIO_SEL3 0x08 -#define V_GPIO_SEL4 0x10 -#define V_GPIO_SEL5 0x20 -#define V_GPIO_SEL6 0x40 -#define V_GPIO_SEL7 0x80 -/* R_GPIO_IN0 */ -#define V_GPIO_IN0 0x01 -#define V_GPIO_IN1 0x02 -#define V_GPIO_IN2 0x04 -#define V_GPIO_IN3 0x08 -#define V_GPIO_IN4 0x10 -#define V_GPIO_IN5 0x20 -#define V_GPIO_IN6 0x40 -#define V_GPIO_IN7 0x80 -/* R_GPIO_IN1 */ -#define V_GPIO_IN8 0x01 -#define V_GPIO_IN9 0x02 -#define V_GPIO_IN10 0x04 -#define V_GPIO_IN11 0x08 -#define V_GPIO_IN12 0x10 -#define V_GPIO_IN13 0x20 -#define V_GPIO_IN14 0x40 -#define V_GPIO_IN15 0x80 -/* R_GPI_IN0 */ -#define V_GPI_IN0 0x01 -#define V_GPI_IN1 0x02 -#define V_GPI_IN2 0x04 -#define V_GPI_IN3 0x08 -#define V_GPI_IN4 0x10 -#define V_GPI_IN5 0x20 -#define V_GPI_IN6 0x40 -#define V_GPI_IN7 0x80 -/* R_GPI_IN1 */ -#define V_GPI_IN8 0x01 -#define V_GPI_IN9 0x02 -#define V_GPI_IN10 0x04 -#define V_GPI_IN11 0x08 -#define V_GPI_IN12 0x10 -#define V_GPI_IN13 0x20 -#define V_GPI_IN14 0x40 -#define V_GPI_IN15 0x80 -/* R_GPI_IN2 */ -#define V_GPI_IN16 0x01 -#define V_GPI_IN17 0x02 -#define V_GPI_IN18 0x04 -#define V_GPI_IN19 0x08 -#define V_GPI_IN20 0x10 -#define V_GPI_IN21 0x20 -#define V_GPI_IN22 0x40 -#define V_GPI_IN23 0x80 -/* R_GPI_IN3 */ -#define V_GPI_IN24 0x01 -#define V_GPI_IN25 0x02 -#define V_GPI_IN26 0x04 -#define V_GPI_IN27 0x08 -#define V_GPI_IN28 0x10 -#define V_GPI_IN29 0x20 -#define V_GPI_IN30 0x40 -#define V_GPI_IN31 0x80 - -/* map of all registers, used for debugging */ - -#ifdef HFC_REGISTER_DEBUG -struct hfc_register_names { - char *name; - u_char reg; -} hfc_register_names[] = { - /* write registers */ - {"R_CIRM", 0x00}, - {"R_CTRL", 0x01}, - {"R_BRG_PCM_CFG ", 0x02}, - {"R_RAM_ADDR0", 0x08}, - {"R_RAM_ADDR1", 0x09}, - {"R_RAM_ADDR2", 0x0A}, - {"R_FIRST_FIFO", 0x0B}, - {"R_RAM_SZ", 0x0C}, - {"R_FIFO_MD", 0x0D}, - {"R_INC_RES_FIFO", 0x0E}, - {"R_FIFO / R_FSM_IDX", 0x0F}, - {"R_SLOT", 0x10}, - {"R_IRQMSK_MISC", 0x11}, - {"R_SCI_MSK", 0x12}, - {"R_IRQ_CTRL", 0x13}, - {"R_PCM_MD0", 0x14}, - {"R_0x15", 0x15}, - {"R_ST_SEL", 0x16}, - {"R_ST_SYNC", 0x17}, - {"R_CONF_EN", 0x18}, - {"R_TI_WD", 0x1A}, - {"R_BERT_WD_MD", 0x1B}, - {"R_DTMF", 0x1C}, - {"R_DTMF_N", 0x1D}, - {"R_E1_XX_STA", 0x20}, - {"R_LOS0", 0x22}, - {"R_LOS1", 0x23}, - {"R_RX0", 0x24}, - {"R_RX_FR0", 0x25}, - {"R_RX_FR1", 0x26}, - {"R_TX0", 0x28}, - {"R_TX1", 0x29}, - {"R_TX_FR0", 0x2C}, - {"R_TX_FR1", 0x2D}, - {"R_TX_FR2", 0x2E}, - {"R_JATT_ATT", 0x2F}, - {"A_ST_xx_STA/R_RX_OFF", 0x30}, - {"A_ST_CTRL0/R_SYNC_OUT", 0x31}, - {"A_ST_CTRL1", 0x32}, - {"A_ST_CTRL2", 0x33}, - {"A_ST_SQ_WR", 0x34}, - {"R_TX_OFF", 0x34}, - {"R_SYNC_CTRL", 0x35}, - {"A_ST_CLK_DLY", 0x37}, - {"R_PWM0", 0x38}, - {"R_PWM1", 0x39}, - {"A_ST_B1_TX", 0x3C}, - {"A_ST_B2_TX", 0x3D}, - {"A_ST_D_TX", 0x3E}, - {"R_GPIO_OUT0", 0x40}, - {"R_GPIO_OUT1", 0x41}, - {"R_GPIO_EN0", 0x42}, - {"R_GPIO_EN1", 0x43}, - {"R_GPIO_SEL", 0x44}, - {"R_BRG_CTRL", 0x45}, - {"R_PWM_MD", 0x46}, - {"R_BRG_MD", 0x47}, - {"R_BRG_TIM0", 0x48}, - {"R_BRG_TIM1", 0x49}, - {"R_BRG_TIM2", 0x4A}, - {"R_BRG_TIM3", 0x4B}, - {"R_BRG_TIM_SEL01", 0x4C}, - {"R_BRG_TIM_SEL23", 0x4D}, - {"R_BRG_TIM_SEL45", 0x4E}, - {"R_BRG_TIM_SEL67", 0x4F}, - {"A_FIFO_DATA0-2", 0x80}, - {"A_FIFO_DATA0-2_NOINC", 0x84}, - {"R_RAM_DATA", 0xC0}, - {"A_SL_CFG", 0xD0}, - {"A_CONF", 0xD1}, - {"A_CH_MSK", 0xF4}, - {"A_CON_HDLC", 0xFA}, - {"A_SUBCH_CFG", 0xFB}, - {"A_CHANNEL", 0xFC}, - {"A_FIFO_SEQ", 0xFD}, - {"A_IRQ_MSK", 0xFF}, - {NULL, 0}, - - /* read registers */ - {"A_Z1", 0x04}, - {"A_Z1H", 0x05}, - {"A_Z2", 0x06}, - {"A_Z2H", 0x07}, - {"A_F1", 0x0C}, - {"A_F2", 0x0D}, - {"R_IRQ_OVIEW", 0x10}, - {"R_IRQ_MISC", 0x11}, - {"R_IRQ_STATECH", 0x12}, - {"R_CONF_OFLOW", 0x14}, - {"R_RAM_USE", 0x15}, - {"R_CHIP_ID", 0x16}, - {"R_BERT_STA", 0x17}, - {"R_F0_CNTL", 0x18}, - {"R_F0_CNTH", 0x19}, - {"R_BERT_ECL", 0x1A}, - {"R_BERT_ECH", 0x1B}, - {"R_STATUS", 0x1C}, - {"R_CHIP_RV", 0x1F}, - {"R_STATE", 0x20}, - {"R_SYNC_STA", 0x24}, - {"R_RX_SL0_0", 0x25}, - {"R_RX_SL0_1", 0x26}, - {"R_RX_SL0_2", 0x27}, - {"R_JATT_DIR", 0x2b}, - {"R_SLIP", 0x2c}, - {"A_ST_RD_STA", 0x30}, - {"R_FAS_ECL", 0x30}, - {"R_FAS_ECH", 0x31}, - {"R_VIO_ECL", 0x32}, - {"R_VIO_ECH", 0x33}, - {"R_CRC_ECL / A_ST_SQ_RD", 0x34}, - {"R_CRC_ECH", 0x35}, - {"R_E_ECL", 0x36}, - {"R_E_ECH", 0x37}, - {"R_SA6_SA13_ECL", 0x38}, - {"R_SA6_SA13_ECH", 0x39}, - {"R_SA6_SA23_ECL", 0x3A}, - {"R_SA6_SA23_ECH", 0x3B}, - {"A_ST_B1_RX", 0x3C}, - {"A_ST_B2_RX", 0x3D}, - {"A_ST_D_RX", 0x3E}, - {"A_ST_E_RX", 0x3F}, - {"R_GPIO_IN0", 0x40}, - {"R_GPIO_IN1", 0x41}, - {"R_GPI_IN0", 0x44}, - {"R_GPI_IN1", 0x45}, - {"R_GPI_IN2", 0x46}, - {"R_GPI_IN3", 0x47}, - {"A_FIFO_DATA0-2", 0x80}, - {"A_FIFO_DATA0-2_NOINC", 0x84}, - {"R_INT_DATA", 0x88}, - {"R_RAM_DATA", 0xC0}, - {"R_IRQ_FIFO_BL0", 0xC8}, - {"R_IRQ_FIFO_BL1", 0xC9}, - {"R_IRQ_FIFO_BL2", 0xCA}, - {"R_IRQ_FIFO_BL3", 0xCB}, - {"R_IRQ_FIFO_BL4", 0xCC}, - {"R_IRQ_FIFO_BL5", 0xCD}, - {"R_IRQ_FIFO_BL6", 0xCE}, - {"R_IRQ_FIFO_BL7", 0xCF}, -}; -#endif /* HFC_REGISTER_DEBUG */ diff --git a/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h b/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h deleted file mode 100644 index 448ded8f9d24..000000000000 --- a/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h +++ /dev/null @@ -1,167 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * For License see notice in hfc_multi.c - * - * special IO and init functions for the embedded XHFC board - * from Speech Design - * - */ - -#include - -/* Change this to the value used by your board */ -#ifndef IMAP_ADDR -#define IMAP_ADDR 0xFFF00000 -#endif - -static void -#ifdef HFC_REGISTER_DEBUG -HFC_outb_embsd(struct hfc_multi *hc, u_char reg, u_char val, - const char *function, int line) -#else - HFC_outb_embsd(struct hfc_multi *hc, u_char reg, u_char val) -#endif -{ - hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; - writeb(reg, hc->xhfc_memaddr); - hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); - writeb(val, hc->xhfc_memdata); -} -static u_char -#ifdef HFC_REGISTER_DEBUG -HFC_inb_embsd(struct hfc_multi *hc, u_char reg, const char *function, int line) -#else - HFC_inb_embsd(struct hfc_multi *hc, u_char reg) -#endif -{ - hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; - writeb(reg, hc->xhfc_memaddr); - hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); - return readb(hc->xhfc_memdata); -} -static u_short -#ifdef HFC_REGISTER_DEBUG -HFC_inw_embsd(struct hfc_multi *hc, u_char reg, const char *function, int line) -#else - HFC_inw_embsd(struct hfc_multi *hc, u_char reg) -#endif -{ - hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; - writeb(reg, hc->xhfc_memaddr); - hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); - return readb(hc->xhfc_memdata); -} -static void -#ifdef HFC_REGISTER_DEBUG -HFC_wait_embsd(struct hfc_multi *hc, const char *function, int line) -#else - HFC_wait_embsd(struct hfc_multi *hc) -#endif -{ - hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; - writeb(R_STATUS, hc->xhfc_memaddr); - hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); - while (readb(hc->xhfc_memdata) & V_BUSY) - cpu_relax(); -} - -/* write fifo data (EMBSD) */ -void -write_fifo_embsd(struct hfc_multi *hc, u_char *data, int len) -{ - hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; - *hc->xhfc_memaddr = A_FIFO_DATA0; - hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); - while (len) { - *hc->xhfc_memdata = *data; - data++; - len--; - } -} - -/* read fifo data (EMBSD) */ -void -read_fifo_embsd(struct hfc_multi *hc, u_char *data, int len) -{ - hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; - *hc->xhfc_memaddr = A_FIFO_DATA0; - hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); - while (len) { - *data = (u_char)(*hc->xhfc_memdata); - data++; - len--; - } -} - -static int -setup_embedded(struct hfc_multi *hc, struct hm_map *m) -{ - printk(KERN_INFO - "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", - m->vendor_name, m->card_name, m->clock2 ? "double" : "normal"); - - hc->pci_dev = NULL; - if (m->clock2) - test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); - - hc->leds = m->leds; - hc->ledstate = 0xAFFEAFFE; - hc->opticalsupport = m->opticalsupport; - - hc->pci_iobase = 0; - hc->pci_membase = 0; - hc->xhfc_membase = NULL; - hc->xhfc_memaddr = NULL; - hc->xhfc_memdata = NULL; - - /* set memory access methods */ - if (m->io_mode) /* use mode from card config */ - hc->io_mode = m->io_mode; - switch (hc->io_mode) { - case HFC_IO_MODE_EMBSD: - test_and_set_bit(HFC_CHIP_EMBSD, &hc->chip); - hc->slots = 128; /* required */ - hc->HFC_outb = HFC_outb_embsd; - hc->HFC_inb = HFC_inb_embsd; - hc->HFC_inw = HFC_inw_embsd; - hc->HFC_wait = HFC_wait_embsd; - hc->read_fifo = read_fifo_embsd; - hc->write_fifo = write_fifo_embsd; - hc->xhfc_origmembase = XHFC_MEMBASE + XHFC_OFFSET * hc->id; - hc->xhfc_membase = (u_char *)ioremap(hc->xhfc_origmembase, - XHFC_MEMSIZE); - if (!hc->xhfc_membase) { - printk(KERN_WARNING - "HFC-multi: failed to remap xhfc address space. " - "(internal error)\n"); - return -EIO; - } - hc->xhfc_memaddr = (u_long *)(hc->xhfc_membase + 4); - hc->xhfc_memdata = (u_long *)(hc->xhfc_membase); - printk(KERN_INFO - "HFC-multi: xhfc_membase:%#lx xhfc_origmembase:%#lx " - "xhfc_memaddr:%#lx xhfc_memdata:%#lx\n", - (u_long)hc->xhfc_membase, hc->xhfc_origmembase, - (u_long)hc->xhfc_memaddr, (u_long)hc->xhfc_memdata); - break; - default: - printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); - return -EIO; - } - - /* Prepare the MPC8XX PortA 10 as output (address/data selector) */ - hc->immap = (struct immap *)(IMAP_ADDR); - hc->immap->im_ioport.iop_papar &= ~(PA_XHFC_A0); - hc->immap->im_ioport.iop_paodr &= ~(PA_XHFC_A0); - hc->immap->im_ioport.iop_padir |= PA_XHFC_A0; - - /* Prepare the MPC8xx PortB __X__ as input (ISDN__X__IRQ) */ - hc->pb_irqmsk = (PB_XHFC_IRQ1 << hc->id); - hc->immap->im_cpm.cp_pbpar &= ~(hc->pb_irqmsk); - hc->immap->im_cpm.cp_pbodr &= ~(hc->pb_irqmsk); - hc->immap->im_cpm.cp_pbdir &= ~(hc->pb_irqmsk); - - /* At this point the needed config is done */ - /* fifos are still not enabled */ - return 0; -} diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h deleted file mode 100644 index a0e4806c11fa..000000000000 --- a/drivers/isdn/hardware/mISDN/hfc_pci.h +++ /dev/null @@ -1,214 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * specific defines for CCD's HFC 2BDS0 PCI chips - * - * Author Werner Cornelius (werner@isdn4linux.de) - * - * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de) - */ - -/* - * thresholds for transparent B-channel mode - * change mask and threshold simultaneously - */ -#define HFCPCI_BTRANS_THRESHOLD 128 -#define HFCPCI_FILLEMPTY 64 -#define HFCPCI_BTRANS_THRESMASK 0x00 - -/* defines for PCI config */ -#define PCI_ENA_MEMIO 0x02 -#define PCI_ENA_MASTER 0x04 - -/* GCI/IOM bus monitor registers */ -#define HCFPCI_C_I 0x08 -#define HFCPCI_TRxR 0x0C -#define HFCPCI_MON1_D 0x28 -#define HFCPCI_MON2_D 0x2C - -/* GCI/IOM bus timeslot registers */ -#define HFCPCI_B1_SSL 0x80 -#define HFCPCI_B2_SSL 0x84 -#define HFCPCI_AUX1_SSL 0x88 -#define HFCPCI_AUX2_SSL 0x8C -#define HFCPCI_B1_RSL 0x90 -#define HFCPCI_B2_RSL 0x94 -#define HFCPCI_AUX1_RSL 0x98 -#define HFCPCI_AUX2_RSL 0x9C - -/* GCI/IOM bus data registers */ -#define HFCPCI_B1_D 0xA0 -#define HFCPCI_B2_D 0xA4 -#define HFCPCI_AUX1_D 0xA8 -#define HFCPCI_AUX2_D 0xAC - -/* GCI/IOM bus configuration registers */ -#define HFCPCI_MST_EMOD 0xB4 -#define HFCPCI_MST_MODE 0xB8 -#define HFCPCI_CONNECT 0xBC - - -/* Interrupt and status registers */ -#define HFCPCI_FIFO_EN 0x44 -#define HFCPCI_TRM 0x48 -#define HFCPCI_B_MODE 0x4C -#define HFCPCI_CHIP_ID 0x58 -#define HFCPCI_CIRM 0x60 -#define HFCPCI_CTMT 0x64 -#define HFCPCI_INT_M1 0x68 -#define HFCPCI_INT_M2 0x6C -#define HFCPCI_INT_S1 0x78 -#define HFCPCI_INT_S2 0x7C -#define HFCPCI_STATUS 0x70 - -/* S/T section registers */ -#define HFCPCI_STATES 0xC0 -#define HFCPCI_SCTRL 0xC4 -#define HFCPCI_SCTRL_E 0xC8 -#define HFCPCI_SCTRL_R 0xCC -#define HFCPCI_SQ 0xD0 -#define HFCPCI_CLKDEL 0xDC -#define HFCPCI_B1_REC 0xF0 -#define HFCPCI_B1_SEND 0xF0 -#define HFCPCI_B2_REC 0xF4 -#define HFCPCI_B2_SEND 0xF4 -#define HFCPCI_D_REC 0xF8 -#define HFCPCI_D_SEND 0xF8 -#define HFCPCI_E_REC 0xFC - - -/* bits in status register (READ) */ -#define HFCPCI_PCI_PROC 0x02 -#define HFCPCI_NBUSY 0x04 -#define HFCPCI_TIMER_ELAP 0x10 -#define HFCPCI_STATINT 0x20 -#define HFCPCI_FRAMEINT 0x40 -#define HFCPCI_ANYINT 0x80 - -/* bits in CTMT (Write) */ -#define HFCPCI_CLTIMER 0x80 -#define HFCPCI_TIM3_125 0x04 -#define HFCPCI_TIM25 0x10 -#define HFCPCI_TIM50 0x14 -#define HFCPCI_TIM400 0x18 -#define HFCPCI_TIM800 0x1C -#define HFCPCI_AUTO_TIMER 0x20 -#define HFCPCI_TRANSB2 0x02 -#define HFCPCI_TRANSB1 0x01 - -/* bits in CIRM (Write) */ -#define HFCPCI_AUX_MSK 0x07 -#define HFCPCI_RESET 0x08 -#define HFCPCI_B1_REV 0x40 -#define HFCPCI_B2_REV 0x80 - -/* bits in INT_M1 and INT_S1 */ -#define HFCPCI_INTS_B1TRANS 0x01 -#define HFCPCI_INTS_B2TRANS 0x02 -#define HFCPCI_INTS_DTRANS 0x04 -#define HFCPCI_INTS_B1REC 0x08 -#define HFCPCI_INTS_B2REC 0x10 -#define HFCPCI_INTS_DREC 0x20 -#define HFCPCI_INTS_L1STATE 0x40 -#define HFCPCI_INTS_TIMER 0x80 - -/* bits in INT_M2 */ -#define HFCPCI_PROC_TRANS 0x01 -#define HFCPCI_GCI_I_CHG 0x02 -#define HFCPCI_GCI_MON_REC 0x04 -#define HFCPCI_IRQ_ENABLE 0x08 -#define HFCPCI_PMESEL 0x80 - -/* bits in STATES */ -#define HFCPCI_STATE_MSK 0x0F -#define HFCPCI_LOAD_STATE 0x10 -#define HFCPCI_ACTIVATE 0x20 -#define HFCPCI_DO_ACTION 0x40 -#define HFCPCI_NT_G2_G3 0x80 - -/* bits in HFCD_MST_MODE */ -#define HFCPCI_MASTER 0x01 -#define HFCPCI_SLAVE 0x00 -#define HFCPCI_F0IO_POSITIV 0x02 -#define HFCPCI_F0_NEGATIV 0x04 -#define HFCPCI_F0_2C4 0x08 -/* remaining bits are for codecs control */ - -/* bits in HFCD_SCTRL */ -#define SCTRL_B1_ENA 0x01 -#define SCTRL_B2_ENA 0x02 -#define SCTRL_MODE_TE 0x00 -#define SCTRL_MODE_NT 0x04 -#define SCTRL_LOW_PRIO 0x08 -#define SCTRL_SQ_ENA 0x10 -#define SCTRL_TEST 0x20 -#define SCTRL_NONE_CAP 0x40 -#define SCTRL_PWR_DOWN 0x80 - -/* bits in SCTRL_E */ -#define HFCPCI_AUTO_AWAKE 0x01 -#define HFCPCI_DBIT_1 0x04 -#define HFCPCI_IGNORE_COL 0x08 -#define HFCPCI_CHG_B1_B2 0x80 - -/* bits in FIFO_EN register */ -#define HFCPCI_FIFOEN_B1 0x03 -#define HFCPCI_FIFOEN_B2 0x0C -#define HFCPCI_FIFOEN_DTX 0x10 -#define HFCPCI_FIFOEN_B1TX 0x01 -#define HFCPCI_FIFOEN_B1RX 0x02 -#define HFCPCI_FIFOEN_B2TX 0x04 -#define HFCPCI_FIFOEN_B2RX 0x08 - - -/* definitions of fifo memory area */ -#define MAX_D_FRAMES 15 -#define MAX_B_FRAMES 31 -#define B_SUB_VAL 0x200 -#define B_FIFO_SIZE (0x2000 - B_SUB_VAL) -#define D_FIFO_SIZE 512 -#define D_FREG_MASK 0xF - -struct zt { - __le16 z1; /* Z1 pointer 16 Bit */ - __le16 z2; /* Z2 pointer 16 Bit */ -}; - -struct dfifo { - u_char data[D_FIFO_SIZE]; /* FIFO data space */ - u_char fill1[0x20A0 - D_FIFO_SIZE]; /* reserved, do not use */ - u_char f1, f2; /* f pointers */ - u_char fill2[0x20C0 - 0x20A2]; /* reserved, do not use */ - /* mask index with D_FREG_MASK for access */ - struct zt za[MAX_D_FRAMES + 1]; - u_char fill3[0x4000 - 0x2100]; /* align 16K */ -}; - -struct bzfifo { - struct zt za[MAX_B_FRAMES + 1]; /* only range 0x0..0x1F allowed */ - u_char f1, f2; /* f pointers */ - u_char fill[0x2100 - 0x2082]; /* alignment */ -}; - - -union fifo_area { - struct { - struct dfifo d_tx; /* D-send channel */ - struct dfifo d_rx; /* D-receive channel */ - } d_chan; - struct { - u_char fill1[0x200]; - u_char txdat_b1[B_FIFO_SIZE]; - struct bzfifo txbz_b1; - struct bzfifo txbz_b2; - u_char txdat_b2[B_FIFO_SIZE]; - u_char fill2[D_FIFO_SIZE]; - u_char rxdat_b1[B_FIFO_SIZE]; - struct bzfifo rxbz_b1; - struct bzfifo rxbz_b2; - u_char rxdat_b2[B_FIFO_SIZE]; - } b_chans; - u_char fill[32768]; -}; - -#define Write_hfc(a, b, c) (writeb(c, (a->hw.pci_io) + b)) -#define Read_hfc(a, b) (readb((a->hw.pci_io) + b)) diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c deleted file mode 100644 index b3d28976b33a..000000000000 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ /dev/null @@ -1,5540 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * hfcmulti.c low level driver for hfc-4s/hfc-8s/hfc-e1 based cards - * - * Author Andreas Eversberg (jolly@eversberg.eu) - * ported to mqueue mechanism: - * Peter Sprenger (sprengermoving-bytes.de) - * - * inspired by existing hfc-pci driver: - * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) - * Copyright 2008 by Karsten Keil (kkeil@suse.de) - * Copyright 2008 by Andreas Eversberg (jolly@eversberg.eu) - * - * Thanks to Cologne Chip AG for this great controller! - */ - -/* - * module parameters: - * type: - * By default (0), the card is automatically detected. - * Or use the following combinations: - * Bit 0-7 = 0x00001 = HFC-E1 (1 port) - * or Bit 0-7 = 0x00004 = HFC-4S (4 ports) - * or Bit 0-7 = 0x00008 = HFC-8S (8 ports) - * Bit 8 = 0x00100 = uLaw (instead of aLaw) - * Bit 9 = 0x00200 = Disable DTMF detect on all B-channels via hardware - * Bit 10 = spare - * Bit 11 = 0x00800 = Force PCM bus into slave mode. (otherwise auto) - * or Bit 12 = 0x01000 = Force PCM bus into master mode. (otherwise auto) - * Bit 13 = spare - * Bit 14 = 0x04000 = Use external ram (128K) - * Bit 15 = 0x08000 = Use external ram (512K) - * Bit 16 = 0x10000 = Use 64 timeslots instead of 32 - * or Bit 17 = 0x20000 = Use 128 timeslots instead of anything else - * Bit 18 = spare - * Bit 19 = 0x80000 = Send the Watchdog a Signal (Dual E1 with Watchdog) - * (all other bits are reserved and shall be 0) - * example: 0x20204 one HFC-4S with dtmf detection and 128 timeslots on PCM - * bus (PCM master) - * - * port: (optional or required for all ports on all installed cards) - * HFC-4S/HFC-8S only bits: - * Bit 0 = 0x001 = Use master clock for this S/T interface - * (only once per chip). - * Bit 1 = 0x002 = transmitter line setup (non capacitive mode) - * Don't use this unless you know what you are doing! - * Bit 2 = 0x004 = Disable E-channel. (No E-channel processing) - * example: 0x0001,0x0000,0x0000,0x0000 one HFC-4S with master clock - * received from port 1 - * - * HFC-E1 only bits: - * Bit 0 = 0x0001 = interface: 0=copper, 1=optical - * Bit 1 = 0x0002 = reserved (later for 32 B-channels transparent mode) - * Bit 2 = 0x0004 = Report LOS - * Bit 3 = 0x0008 = Report AIS - * Bit 4 = 0x0010 = Report SLIP - * Bit 5 = 0x0020 = Report RDI - * Bit 8 = 0x0100 = Turn off CRC-4 Multiframe Mode, use double frame - * mode instead. - * Bit 9 = 0x0200 = Force get clock from interface, even in NT mode. - * or Bit 10 = 0x0400 = Force put clock to interface, even in TE mode. - * Bit 11 = 0x0800 = Use direct RX clock for PCM sync rather than PLL. - * (E1 only) - * Bit 12-13 = 0xX000 = elastic jitter buffer (1-3), Set both bits to 0 - * for default. - * (all other bits are reserved and shall be 0) - * - * debug: - * NOTE: only one debug value must be given for all cards - * enable debugging (see hfc_multi.h for debug options) - * - * poll: - * NOTE: only one poll value must be given for all cards - * Give the number of samples for each fifo process. - * By default 128 is used. Decrease to reduce delay, increase to - * reduce cpu load. If unsure, don't mess with it! - * Valid is 8, 16, 32, 64, 128, 256. - * - * pcm: - * NOTE: only one pcm value must be given for every card. - * The PCM bus id tells the mISDNdsp module about the connected PCM bus. - * By default (0), the PCM bus id is 100 for the card that is PCM master. - * If multiple cards are PCM master (because they are not interconnected), - * each card with PCM master will have increasing PCM id. - * All PCM buses with the same ID are expected to be connected and have - * common time slots slots. - * Only one chip of the PCM bus must be master, the others slave. - * -1 means no support of PCM bus not even. - * Omit this value, if all cards are interconnected or none is connected. - * If unsure, don't give this parameter. - * - * dmask and bmask: - * NOTE: One dmask value must be given for every HFC-E1 card. - * If omitted, the E1 card has D-channel on time slot 16, which is default. - * dmask is a 32 bit mask. The bit must be set for an alternate time slot. - * If multiple bits are set, multiple virtual card fragments are created. - * For each bit set, a bmask value must be given. Each bit on the bmask - * value stands for a B-channel. The bmask may not overlap with dmask or - * with other bmask values for that card. - * Example: dmask=0x00020002 bmask=0x0000fffc,0xfffc0000 - * This will create one fragment with D-channel on slot 1 with - * B-channels on slots 2..15, and a second fragment with D-channel - * on slot 17 with B-channels on slot 18..31. Slot 16 is unused. - * If bit 0 is set (dmask=0x00000001) the D-channel is on slot 0 and will - * not function. - * Example: dmask=0x00000001 bmask=0xfffffffe - * This will create a port with all 31 usable timeslots as - * B-channels. - * If no bits are set on bmask, no B-channel is created for that fragment. - * Example: dmask=0xfffffffe bmask=0,0,0,0.... (31 0-values for bmask) - * This will create 31 ports with one D-channel only. - * If you don't know how to use it, you don't need it! - * - * iomode: - * NOTE: only one mode value must be given for every card. - * -> See hfc_multi.h for HFC_IO_MODE_* values - * By default, the IO mode is pci memory IO (MEMIO). - * Some cards require specific IO mode, so it cannot be changed. - * It may be useful to set IO mode to register io (REGIO) to solve - * PCI bridge problems. - * If unsure, don't give this parameter. - * - * clockdelay_nt: - * NOTE: only one clockdelay_nt value must be given once for all cards. - * Give the value of the clock control register (A_ST_CLK_DLY) - * of the S/T interfaces in NT mode. - * This register is needed for the TBR3 certification, so don't change it. - * - * clockdelay_te: - * NOTE: only one clockdelay_te value must be given once - * Give the value of the clock control register (A_ST_CLK_DLY) - * of the S/T interfaces in TE mode. - * This register is needed for the TBR3 certification, so don't change it. - * - * clock: - * NOTE: only one clock value must be given once - * Selects interface with clock source for mISDN and applications. - * Set to card number starting with 1. Set to -1 to disable. - * By default, the first card is used as clock source. - * - * hwid: - * NOTE: only one hwid value must be given once - * Enable special embedded devices with XHFC controllers. - */ - -/* - * debug register access (never use this, it will flood your system log) - * #define HFC_REGISTER_DEBUG - */ - -#define HFC_MULTI_VERSION "2.03" - -#include -#include -#include -#include -#include -#include -#include - -/* - #define IRQCOUNT_DEBUG - #define IRQ_DEBUG -*/ - -#include "hfc_multi.h" -#ifdef ECHOPREP -#include "gaintab.h" -#endif - -#define MAX_CARDS 8 -#define MAX_PORTS (8 * MAX_CARDS) -#define MAX_FRAGS (32 * MAX_CARDS) - -static LIST_HEAD(HFClist); -static DEFINE_SPINLOCK(HFClock); /* global hfc list lock */ - -static void ph_state_change(struct dchannel *); - -static struct hfc_multi *syncmaster; -static int plxsd_master; /* if we have a master card (yet) */ -static DEFINE_SPINLOCK(plx_lock); /* may not acquire other lock inside */ - -#define TYP_E1 1 -#define TYP_4S 4 -#define TYP_8S 8 - -static int poll_timer = 6; /* default = 128 samples = 16ms */ -/* number of POLL_TIMER interrupts for G2 timeout (ca 1s) */ -static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30 }; -#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ -#define CLKDEL_NT 0x6c /* CLKDEL in NT mode - (0x60 MUST be included!) */ - -#define DIP_4S 0x1 /* DIP Switches for Beronet 1S/2S/4S cards */ -#define DIP_8S 0x2 /* DIP Switches for Beronet 8S+ cards */ -#define DIP_E1 0x3 /* DIP Switches for Beronet E1 cards */ - -/* - * module stuff - */ - -static uint type[MAX_CARDS]; -static int pcm[MAX_CARDS]; -static uint dmask[MAX_CARDS]; -static uint bmask[MAX_FRAGS]; -static uint iomode[MAX_CARDS]; -static uint port[MAX_PORTS]; -static uint debug; -static uint poll; -static int clock; -static uint timer; -static uint clockdelay_te = CLKDEL_TE; -static uint clockdelay_nt = CLKDEL_NT; -#define HWID_NONE 0 -#define HWID_MINIP4 1 -#define HWID_MINIP8 2 -#define HWID_MINIP16 3 -static uint hwid = HWID_NONE; - -static int HFC_cnt, E1_cnt, bmask_cnt, Port_cnt, PCM_cnt = 99; - -MODULE_AUTHOR("Andreas Eversberg"); -MODULE_DESCRIPTION("mISDN driver for hfc-4s/hfc-8s/hfc-e1 based cards"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(HFC_MULTI_VERSION); -module_param(debug, uint, S_IRUGO | S_IWUSR); -module_param(poll, uint, S_IRUGO | S_IWUSR); -module_param(clock, int, S_IRUGO | S_IWUSR); -module_param(timer, uint, S_IRUGO | S_IWUSR); -module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR); -module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR); -module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(pcm, int, NULL, S_IRUGO | S_IWUSR); -module_param_array(dmask, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(bmask, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); -module_param(hwid, uint, S_IRUGO | S_IWUSR); /* The hardware ID */ - -#ifdef HFC_REGISTER_DEBUG -#define HFC_outb(hc, reg, val) \ - (hc->HFC_outb(hc, reg, val, __func__, __LINE__)) -#define HFC_outb_nodebug(hc, reg, val) \ - (hc->HFC_outb_nodebug(hc, reg, val, __func__, __LINE__)) -#define HFC_inb(hc, reg) \ - (hc->HFC_inb(hc, reg, __func__, __LINE__)) -#define HFC_inb_nodebug(hc, reg) \ - (hc->HFC_inb_nodebug(hc, reg, __func__, __LINE__)) -#define HFC_inw(hc, reg) \ - (hc->HFC_inw(hc, reg, __func__, __LINE__)) -#define HFC_inw_nodebug(hc, reg) \ - (hc->HFC_inw_nodebug(hc, reg, __func__, __LINE__)) -#define HFC_wait(hc) \ - (hc->HFC_wait(hc, __func__, __LINE__)) -#define HFC_wait_nodebug(hc) \ - (hc->HFC_wait_nodebug(hc, __func__, __LINE__)) -#else -#define HFC_outb(hc, reg, val) (hc->HFC_outb(hc, reg, val)) -#define HFC_outb_nodebug(hc, reg, val) (hc->HFC_outb_nodebug(hc, reg, val)) -#define HFC_inb(hc, reg) (hc->HFC_inb(hc, reg)) -#define HFC_inb_nodebug(hc, reg) (hc->HFC_inb_nodebug(hc, reg)) -#define HFC_inw(hc, reg) (hc->HFC_inw(hc, reg)) -#define HFC_inw_nodebug(hc, reg) (hc->HFC_inw_nodebug(hc, reg)) -#define HFC_wait(hc) (hc->HFC_wait(hc)) -#define HFC_wait_nodebug(hc) (hc->HFC_wait_nodebug(hc)) -#endif - -#ifdef CONFIG_MISDN_HFCMULTI_8xx -#include "hfc_multi_8xx.h" -#endif - -/* HFC_IO_MODE_PCIMEM */ -static void -#ifdef HFC_REGISTER_DEBUG -HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val, - const char *function, int line) -#else - HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val) -#endif -{ - writeb(val, hc->pci_membase + reg); -} -static u_char -#ifdef HFC_REGISTER_DEBUG -HFC_inb_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) -#else - HFC_inb_pcimem(struct hfc_multi *hc, u_char reg) -#endif -{ - return readb(hc->pci_membase + reg); -} -static u_short -#ifdef HFC_REGISTER_DEBUG -HFC_inw_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) -#else - HFC_inw_pcimem(struct hfc_multi *hc, u_char reg) -#endif -{ - return readw(hc->pci_membase + reg); -} -static void -#ifdef HFC_REGISTER_DEBUG -HFC_wait_pcimem(struct hfc_multi *hc, const char *function, int line) -#else - HFC_wait_pcimem(struct hfc_multi *hc) -#endif -{ - while (readb(hc->pci_membase + R_STATUS) & V_BUSY) - cpu_relax(); -} - -/* HFC_IO_MODE_REGIO */ -static void -#ifdef HFC_REGISTER_DEBUG -HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val, - const char *function, int line) -#else - HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val) -#endif -{ - outb(reg, hc->pci_iobase + 4); - outb(val, hc->pci_iobase); -} -static u_char -#ifdef HFC_REGISTER_DEBUG -HFC_inb_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) -#else - HFC_inb_regio(struct hfc_multi *hc, u_char reg) -#endif -{ - outb(reg, hc->pci_iobase + 4); - return inb(hc->pci_iobase); -} -static u_short -#ifdef HFC_REGISTER_DEBUG -HFC_inw_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) -#else - HFC_inw_regio(struct hfc_multi *hc, u_char reg) -#endif -{ - outb(reg, hc->pci_iobase + 4); - return inw(hc->pci_iobase); -} -static void -#ifdef HFC_REGISTER_DEBUG -HFC_wait_regio(struct hfc_multi *hc, const char *function, int line) -#else - HFC_wait_regio(struct hfc_multi *hc) -#endif -{ - outb(R_STATUS, hc->pci_iobase + 4); - while (inb(hc->pci_iobase) & V_BUSY) - cpu_relax(); -} - -#ifdef HFC_REGISTER_DEBUG -static void -HFC_outb_debug(struct hfc_multi *hc, u_char reg, u_char val, - const char *function, int line) -{ - char regname[256] = "", bits[9] = "xxxxxxxx"; - int i; - - i = -1; - while (hfc_register_names[++i].name) { - if (hfc_register_names[i].reg == reg) - strcat(regname, hfc_register_names[i].name); - } - if (regname[0] == '\0') - strcpy(regname, "register"); - - bits[7] = '0' + (!!(val & 1)); - bits[6] = '0' + (!!(val & 2)); - bits[5] = '0' + (!!(val & 4)); - bits[4] = '0' + (!!(val & 8)); - bits[3] = '0' + (!!(val & 16)); - bits[2] = '0' + (!!(val & 32)); - bits[1] = '0' + (!!(val & 64)); - bits[0] = '0' + (!!(val & 128)); - printk(KERN_DEBUG - "HFC_outb(chip %d, %02x=%s, 0x%02x=%s); in %s() line %d\n", - hc->id, reg, regname, val, bits, function, line); - HFC_outb_nodebug(hc, reg, val); -} -static u_char -HFC_inb_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) -{ - char regname[256] = "", bits[9] = "xxxxxxxx"; - u_char val = HFC_inb_nodebug(hc, reg); - int i; - - i = 0; - while (hfc_register_names[i++].name) - ; - while (hfc_register_names[++i].name) { - if (hfc_register_names[i].reg == reg) - strcat(regname, hfc_register_names[i].name); - } - if (regname[0] == '\0') - strcpy(regname, "register"); - - bits[7] = '0' + (!!(val & 1)); - bits[6] = '0' + (!!(val & 2)); - bits[5] = '0' + (!!(val & 4)); - bits[4] = '0' + (!!(val & 8)); - bits[3] = '0' + (!!(val & 16)); - bits[2] = '0' + (!!(val & 32)); - bits[1] = '0' + (!!(val & 64)); - bits[0] = '0' + (!!(val & 128)); - printk(KERN_DEBUG - "HFC_inb(chip %d, %02x=%s) = 0x%02x=%s; in %s() line %d\n", - hc->id, reg, regname, val, bits, function, line); - return val; -} -static u_short -HFC_inw_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) -{ - char regname[256] = ""; - u_short val = HFC_inw_nodebug(hc, reg); - int i; - - i = 0; - while (hfc_register_names[i++].name) - ; - while (hfc_register_names[++i].name) { - if (hfc_register_names[i].reg == reg) - strcat(regname, hfc_register_names[i].name); - } - if (regname[0] == '\0') - strcpy(regname, "register"); - - printk(KERN_DEBUG - "HFC_inw(chip %d, %02x=%s) = 0x%04x; in %s() line %d\n", - hc->id, reg, regname, val, function, line); - return val; -} -static void -HFC_wait_debug(struct hfc_multi *hc, const char *function, int line) -{ - printk(KERN_DEBUG "HFC_wait(chip %d); in %s() line %d\n", - hc->id, function, line); - HFC_wait_nodebug(hc); -} -#endif - -/* write fifo data (REGIO) */ -static void -write_fifo_regio(struct hfc_multi *hc, u_char *data, int len) -{ - outb(A_FIFO_DATA0, (hc->pci_iobase) + 4); - while (len >> 2) { - outl(cpu_to_le32(*(u32 *)data), hc->pci_iobase); - data += 4; - len -= 4; - } - while (len >> 1) { - outw(cpu_to_le16(*(u16 *)data), hc->pci_iobase); - data += 2; - len -= 2; - } - while (len) { - outb(*data, hc->pci_iobase); - data++; - len--; - } -} -/* write fifo data (PCIMEM) */ -static void -write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) -{ - while (len >> 2) { - writel(cpu_to_le32(*(u32 *)data), - hc->pci_membase + A_FIFO_DATA0); - data += 4; - len -= 4; - } - while (len >> 1) { - writew(cpu_to_le16(*(u16 *)data), - hc->pci_membase + A_FIFO_DATA0); - data += 2; - len -= 2; - } - while (len) { - writeb(*data, hc->pci_membase + A_FIFO_DATA0); - data++; - len--; - } -} - -/* read fifo data (REGIO) */ -static void -read_fifo_regio(struct hfc_multi *hc, u_char *data, int len) -{ - outb(A_FIFO_DATA0, (hc->pci_iobase) + 4); - while (len >> 2) { - *(u32 *)data = le32_to_cpu(inl(hc->pci_iobase)); - data += 4; - len -= 4; - } - while (len >> 1) { - *(u16 *)data = le16_to_cpu(inw(hc->pci_iobase)); - data += 2; - len -= 2; - } - while (len) { - *data = inb(hc->pci_iobase); - data++; - len--; - } -} - -/* read fifo data (PCIMEM) */ -static void -read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) -{ - while (len >> 2) { - *(u32 *)data = - le32_to_cpu(readl(hc->pci_membase + A_FIFO_DATA0)); - data += 4; - len -= 4; - } - while (len >> 1) { - *(u16 *)data = - le16_to_cpu(readw(hc->pci_membase + A_FIFO_DATA0)); - data += 2; - len -= 2; - } - while (len) { - *data = readb(hc->pci_membase + A_FIFO_DATA0); - data++; - len--; - } -} - -static void -enable_hwirq(struct hfc_multi *hc) -{ - hc->hw.r_irq_ctrl |= V_GLOB_IRQ_EN; - HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); -} - -static void -disable_hwirq(struct hfc_multi *hc) -{ - hc->hw.r_irq_ctrl &= ~((u_char)V_GLOB_IRQ_EN); - HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); -} - -#define NUM_EC 2 -#define MAX_TDM_CHAN 32 - - -static inline void -enablepcibridge(struct hfc_multi *c) -{ - HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /* was _io before */ -} - -static inline void -disablepcibridge(struct hfc_multi *c) -{ - HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /* was _io before */ -} - -static inline unsigned char -readpcibridge(struct hfc_multi *hc, unsigned char address) -{ - unsigned short cipv; - unsigned char data; - - if (!hc->pci_iobase) - return 0; - - /* slow down a PCI read access by 1 PCI clock cycle */ - HFC_outb(hc, R_CTRL, 0x4); /*was _io before*/ - - if (address == 0) - cipv = 0x4000; - else - cipv = 0x5800; - - /* select local bridge port address by writing to CIP port */ - /* data = HFC_inb(c, cipv); * was _io before */ - outw(cipv, hc->pci_iobase + 4); - data = inb(hc->pci_iobase); - - /* restore R_CTRL for normal PCI read cycle speed */ - HFC_outb(hc, R_CTRL, 0x0); /* was _io before */ - - return data; -} - -static inline void -writepcibridge(struct hfc_multi *hc, unsigned char address, unsigned char data) -{ - unsigned short cipv; - unsigned int datav; - - if (!hc->pci_iobase) - return; - - if (address == 0) - cipv = 0x4000; - else - cipv = 0x5800; - - /* select local bridge port address by writing to CIP port */ - outw(cipv, hc->pci_iobase + 4); - /* define a 32 bit dword with 4 identical bytes for write sequence */ - datav = data | ((__u32) data << 8) | ((__u32) data << 16) | - ((__u32) data << 24); - - /* - * write this 32 bit dword to the bridge data port - * this will initiate a write sequence of up to 4 writes to the same - * address on the local bus interface the number of write accesses - * is undefined but >=1 and depends on the next PCI transaction - * during write sequence on the local bus - */ - outl(datav, hc->pci_iobase); -} - -static inline void -cpld_set_reg(struct hfc_multi *hc, unsigned char reg) -{ - /* Do data pin read low byte */ - HFC_outb(hc, R_GPIO_OUT1, reg); -} - -static inline void -cpld_write_reg(struct hfc_multi *hc, unsigned char reg, unsigned char val) -{ - cpld_set_reg(hc, reg); - - enablepcibridge(hc); - writepcibridge(hc, 1, val); - disablepcibridge(hc); - - return; -} - -static inline void -vpm_write_address(struct hfc_multi *hc, unsigned short addr) -{ - cpld_write_reg(hc, 0, 0xff & addr); - cpld_write_reg(hc, 1, 0x01 & (addr >> 8)); -} - -static inline unsigned char -vpm_in(struct hfc_multi *c, int which, unsigned short addr) -{ - unsigned char res; - - vpm_write_address(c, addr); - - if (!which) - cpld_set_reg(c, 2); - else - cpld_set_reg(c, 3); - - enablepcibridge(c); - res = readpcibridge(c, 1); - disablepcibridge(c); - - cpld_set_reg(c, 0); - - return res; -} - -static inline void -vpm_out(struct hfc_multi *c, int which, unsigned short addr, - unsigned char data) -{ - vpm_write_address(c, addr); - - enablepcibridge(c); - - if (!which) - cpld_set_reg(c, 2); - else - cpld_set_reg(c, 3); - - writepcibridge(c, 1, data); - - cpld_set_reg(c, 0); - - disablepcibridge(c); - - { - unsigned char regin; - regin = vpm_in(c, which, addr); - if (regin != data) - printk(KERN_DEBUG "Wrote 0x%x to register 0x%x but got back " - "0x%x\n", data, addr, regin); - } - -} - - -static void -vpm_init(struct hfc_multi *wc) -{ - unsigned char reg; - unsigned int mask; - unsigned int i, x, y; - unsigned int ver; - - for (x = 0; x < NUM_EC; x++) { - /* Setup GPIO's */ - if (!x) { - ver = vpm_in(wc, x, 0x1a0); - printk(KERN_DEBUG "VPM: Chip %d: ver %02x\n", x, ver); - } - - for (y = 0; y < 4; y++) { - vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ - vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ - vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ - } - - /* Setup TDM path - sets fsync and tdm_clk as inputs */ - reg = vpm_in(wc, x, 0x1a3); /* misc_con */ - vpm_out(wc, x, 0x1a3, reg & ~2); - - /* Setup Echo length (256 taps) */ - vpm_out(wc, x, 0x022, 1); - vpm_out(wc, x, 0x023, 0xff); - - /* Setup timeslots */ - vpm_out(wc, x, 0x02f, 0x00); - mask = 0x02020202 << (x * 4); - - /* Setup the tdm channel masks for all chips */ - for (i = 0; i < 4; i++) - vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff); - - /* Setup convergence rate */ - printk(KERN_DEBUG "VPM: A-law mode\n"); - reg = 0x00 | 0x10 | 0x01; - vpm_out(wc, x, 0x20, reg); - printk(KERN_DEBUG "VPM reg 0x20 is %x\n", reg); - /*vpm_out(wc, x, 0x20, (0x00 | 0x08 | 0x20 | 0x10)); */ - - vpm_out(wc, x, 0x24, 0x02); - reg = vpm_in(wc, x, 0x24); - printk(KERN_DEBUG "NLP Thresh is set to %d (0x%x)\n", reg, reg); - - /* Initialize echo cans */ - for (i = 0; i < MAX_TDM_CHAN; i++) { - if (mask & (0x00000001 << i)) - vpm_out(wc, x, i, 0x00); - } - - /* - * ARM arch at least disallows a udelay of - * more than 2ms... it gives a fake "__bad_udelay" - * reference at link-time. - * long delays in kernel code are pretty sucky anyway - * for now work around it using 5 x 2ms instead of 1 x 10ms - */ - - udelay(2000); - udelay(2000); - udelay(2000); - udelay(2000); - udelay(2000); - - /* Put in bypass mode */ - for (i = 0; i < MAX_TDM_CHAN; i++) { - if (mask & (0x00000001 << i)) - vpm_out(wc, x, i, 0x01); - } - - /* Enable bypass */ - for (i = 0; i < MAX_TDM_CHAN; i++) { - if (mask & (0x00000001 << i)) - vpm_out(wc, x, 0x78 + i, 0x01); - } - - } -} - -#ifdef UNUSED -static void -vpm_check(struct hfc_multi *hctmp) -{ - unsigned char gpi2; - - gpi2 = HFC_inb(hctmp, R_GPI_IN2); - - if ((gpi2 & 0x3) != 0x3) - printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2); -} -#endif /* UNUSED */ - - -/* - * Interface to enable/disable the HW Echocan - * - * these functions are called within a spin_lock_irqsave on - * the channel instance lock, so we are not disturbed by irqs - * - * we can later easily change the interface to make other - * things configurable, for now we configure the taps - * - */ - -static void -vpm_echocan_on(struct hfc_multi *hc, int ch, int taps) -{ - unsigned int timeslot; - unsigned int unit; - struct bchannel *bch = hc->chan[ch].bch; -#ifdef TXADJ - int txadj = -4; - struct sk_buff *skb; -#endif - if (hc->chan[ch].protocol != ISDN_P_B_RAW) - return; - - if (!bch) - return; - -#ifdef TXADJ - skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, - sizeof(int), &txadj, GFP_ATOMIC); - if (skb) - recv_Bchannel_skb(bch, skb); -#endif - - timeslot = ((ch / 4) * 8) + ((ch % 4) * 4) + 1; - unit = ch % 4; - - printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n", - taps, timeslot); - - vpm_out(hc, unit, timeslot, 0x7e); -} - -static void -vpm_echocan_off(struct hfc_multi *hc, int ch) -{ - unsigned int timeslot; - unsigned int unit; - struct bchannel *bch = hc->chan[ch].bch; -#ifdef TXADJ - int txadj = 0; - struct sk_buff *skb; -#endif - - if (hc->chan[ch].protocol != ISDN_P_B_RAW) - return; - - if (!bch) - return; - -#ifdef TXADJ - skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, - sizeof(int), &txadj, GFP_ATOMIC); - if (skb) - recv_Bchannel_skb(bch, skb); -#endif - - timeslot = ((ch / 4) * 8) + ((ch % 4) * 4) + 1; - unit = ch % 4; - - printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n", - timeslot); - /* FILLME */ - vpm_out(hc, unit, timeslot, 0x01); -} - - -/* - * Speech Design resync feature - * NOTE: This is called sometimes outside interrupt handler. - * We must lock irqsave, so no other interrupt (other card) will occur! - * Also multiple interrupts may nest, so must lock each access (lists, card)! - */ -static inline void -hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm) -{ - struct hfc_multi *hc, *next, *pcmmaster = NULL; - void __iomem *plx_acc_32; - u_int pv; - u_long flags; - - spin_lock_irqsave(&HFClock, flags); - spin_lock(&plx_lock); /* must be locked inside other locks */ - - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "%s: RESYNC(syncmaster=0x%p)\n", - __func__, syncmaster); - - /* select new master */ - if (newmaster) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "using provided controller\n"); - } else { - list_for_each_entry_safe(hc, next, &HFClist, list) { - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - if (hc->syncronized) { - newmaster = hc; - break; - } - } - } - } - - /* Disable sync of all cards */ - list_for_each_entry_safe(hc, next, &HFClist, list) { - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - pv &= ~PLX_SYNC_O_EN; - writel(pv, plx_acc_32); - if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { - pcmmaster = hc; - if (hc->ctype == HFC_TYPE_E1) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG - "Schedule SYNC_I\n"); - hc->e1_resync |= 1; /* get SYNC_I */ - } - } - } - } - - if (newmaster) { - hc = newmaster; - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "id=%d (0x%p) = synchronized with " - "interface.\n", hc->id, hc); - /* Enable new sync master */ - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - pv |= PLX_SYNC_O_EN; - writel(pv, plx_acc_32); - /* switch to jatt PLL, if not disabled by RX_SYNC */ - if (hc->ctype == HFC_TYPE_E1 - && !test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "Schedule jatt PLL\n"); - hc->e1_resync |= 2; /* switch to jatt */ - } - } else { - if (pcmmaster) { - hc = pcmmaster; - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG - "id=%d (0x%p) = PCM master synchronized " - "with QUARTZ\n", hc->id, hc); - if (hc->ctype == HFC_TYPE_E1) { - /* Use the crystal clock for the PCM - master card */ - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG - "Schedule QUARTZ for HFC-E1\n"); - hc->e1_resync |= 4; /* switch quartz */ - } else { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG - "QUARTZ is automatically " - "enabled by HFC-%dS\n", hc->ctype); - } - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - pv |= PLX_SYNC_O_EN; - writel(pv, plx_acc_32); - } else - if (!rm) - printk(KERN_ERR "%s no pcm master, this MUST " - "not happen!\n", __func__); - } - syncmaster = newmaster; - - spin_unlock(&plx_lock); - spin_unlock_irqrestore(&HFClock, flags); -} - -/* This must be called AND hc must be locked irqsave!!! */ -static inline void -plxsd_checksync(struct hfc_multi *hc, int rm) -{ - if (hc->syncronized) { - if (syncmaster == NULL) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "%s: GOT sync on card %d" - " (id=%d)\n", __func__, hc->id + 1, - hc->id); - hfcmulti_resync(hc, hc, rm); - } - } else { - if (syncmaster == hc) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "%s: LOST sync on card %d" - " (id=%d)\n", __func__, hc->id + 1, - hc->id); - hfcmulti_resync(hc, NULL, rm); - } - } -} - - -/* - * free hardware resources used by driver - */ -static void -release_io_hfcmulti(struct hfc_multi *hc) -{ - void __iomem *plx_acc_32; - u_int pv; - u_long plx_flags; - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: entered\n", __func__); - - /* soft reset also masks all interrupts */ - hc->hw.r_cirm |= V_SRES; - HFC_outb(hc, R_CIRM, hc->hw.r_cirm); - udelay(1000); - hc->hw.r_cirm &= ~V_SRES; - HFC_outb(hc, R_CIRM, hc->hw.r_cirm); - udelay(1000); /* instead of 'wait' that may cause locking */ - - /* release Speech Design card, if PLX was initialized */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && hc->plx_membase) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "%s: release PLXSD card %d\n", - __func__, hc->id + 1); - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - writel(PLX_GPIOC_INIT, plx_acc_32); - pv = readl(plx_acc_32); - /* Termination off */ - pv &= ~PLX_TERM_ON; - /* Disconnect the PCM */ - pv |= PLX_SLAVE_EN_N; - pv &= ~PLX_MASTER_EN; - pv &= ~PLX_SYNC_O_EN; - /* Put the DSP in Reset */ - pv &= ~PLX_DSP_RES_N; - writel(pv, plx_acc_32); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PCM off: PLX_GPIO=%x\n", - __func__, pv); - spin_unlock_irqrestore(&plx_lock, plx_flags); - } - - /* disable memory mapped ports / io ports */ - test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */ - if (hc->pci_dev) - pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0); - if (hc->pci_membase) - iounmap(hc->pci_membase); - if (hc->plx_membase) - iounmap(hc->plx_membase); - if (hc->pci_iobase) - release_region(hc->pci_iobase, 8); - if (hc->xhfc_membase) - iounmap((void *)hc->xhfc_membase); - - if (hc->pci_dev) { - pci_disable_device(hc->pci_dev); - pci_set_drvdata(hc->pci_dev, NULL); - } - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: done\n", __func__); -} - -/* - * function called to reset the HFC chip. A complete software reset of chip - * and fifos is done. All configuration of the chip is done. - */ - -static int -init_chip(struct hfc_multi *hc) -{ - u_long flags, val, val2 = 0, rev; - int i, err = 0; - u_char r_conf_en, rval; - void __iomem *plx_acc_32; - u_int pv; - u_long plx_flags, hfc_flags; - int plx_count; - struct hfc_multi *pos, *next, *plx_last_hc; - - spin_lock_irqsave(&hc->lock, flags); - /* reset all registers */ - memset(&hc->hw, 0, sizeof(struct hfcm_hw)); - - /* revision check */ - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: entered\n", __func__); - val = HFC_inb(hc, R_CHIP_ID); - if ((val >> 4) != 0x8 && (val >> 4) != 0xc && (val >> 4) != 0xe && - (val >> 1) != 0x31) { - printk(KERN_INFO "HFC_multi: unknown CHIP_ID:%x\n", (u_int)val); - err = -EIO; - goto out; - } - rev = HFC_inb(hc, R_CHIP_RV); - printk(KERN_INFO - "HFC_multi: detected HFC with chip ID=0x%lx revision=%ld%s\n", - val, rev, (rev == 0 && (hc->ctype != HFC_TYPE_XHFC)) ? - " (old FIFO handling)" : ""); - if (hc->ctype != HFC_TYPE_XHFC && rev == 0) { - test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip); - printk(KERN_WARNING - "HFC_multi: NOTE: Your chip is revision 0, " - "ask Cologne Chip for update. Newer chips " - "have a better FIFO handling. Old chips " - "still work but may have slightly lower " - "HDLC transmit performance.\n"); - } - if (rev > 1) { - printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't " - "consider chip revision = %ld. The chip / " - "bridge may not work.\n", rev); - } - - /* set s-ram size */ - hc->Flen = 0x10; - hc->Zmin = 0x80; - hc->Zlen = 384; - hc->DTMFbase = 0x1000; - if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: changing to 128K external RAM\n", - __func__); - hc->hw.r_ctrl |= V_EXT_RAM; - hc->hw.r_ram_sz = 1; - hc->Flen = 0x20; - hc->Zmin = 0xc0; - hc->Zlen = 1856; - hc->DTMFbase = 0x2000; - } - if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: changing to 512K external RAM\n", - __func__); - hc->hw.r_ctrl |= V_EXT_RAM; - hc->hw.r_ram_sz = 2; - hc->Flen = 0x20; - hc->Zmin = 0xc0; - hc->Zlen = 8000; - hc->DTMFbase = 0x2000; - } - if (hc->ctype == HFC_TYPE_XHFC) { - hc->Flen = 0x8; - hc->Zmin = 0x0; - hc->Zlen = 64; - hc->DTMFbase = 0x0; - } - hc->max_trans = poll << 1; - if (hc->max_trans > hc->Zlen) - hc->max_trans = hc->Zlen; - - /* Speech Design PLX bridge */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "%s: initializing PLXSD card %d\n", - __func__, hc->id + 1); - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - writel(PLX_GPIOC_INIT, plx_acc_32); - pv = readl(plx_acc_32); - /* The first and the last cards are terminating the PCM bus */ - pv |= PLX_TERM_ON; /* hc is currently the last */ - /* Disconnect the PCM */ - pv |= PLX_SLAVE_EN_N; - pv &= ~PLX_MASTER_EN; - pv &= ~PLX_SYNC_O_EN; - /* Put the DSP in Reset */ - pv &= ~PLX_DSP_RES_N; - writel(pv, plx_acc_32); - spin_unlock_irqrestore(&plx_lock, plx_flags); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: slave/term: PLX_GPIO=%x\n", - __func__, pv); - /* - * If we are the 3rd PLXSD card or higher, we must turn - * termination of last PLXSD card off. - */ - spin_lock_irqsave(&HFClock, hfc_flags); - plx_count = 0; - plx_last_hc = NULL; - list_for_each_entry_safe(pos, next, &HFClist, list) { - if (test_bit(HFC_CHIP_PLXSD, &pos->chip)) { - plx_count++; - if (pos != hc) - plx_last_hc = pos; - } - } - if (plx_count >= 3) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "%s: card %d is between, so " - "we disable termination\n", - __func__, plx_last_hc->id + 1); - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc_32 = plx_last_hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - pv &= ~PLX_TERM_ON; - writel(pv, plx_acc_32); - spin_unlock_irqrestore(&plx_lock, plx_flags); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: term off: PLX_GPIO=%x\n", - __func__, pv); - } - spin_unlock_irqrestore(&HFClock, hfc_flags); - hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */ - } - - if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) - hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */ - - /* we only want the real Z2 read-pointer for revision > 0 */ - if (!test_bit(HFC_CHIP_REVISION0, &hc->chip)) - hc->hw.r_ram_sz |= V_FZ_MD; - - /* select pcm mode */ - if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: setting PCM into slave mode\n", - __func__); - } else - if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) && !plxsd_master) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: setting PCM into master mode\n", - __func__); - hc->hw.r_pcm_md0 |= V_PCM_MD; - } else { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: performing PCM auto detect\n", - __func__); - } - - /* soft reset */ - HFC_outb(hc, R_CTRL, hc->hw.r_ctrl); - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, 0x0C /* R_FIFO_THRES */, - 0x11 /* 16 Bytes TX/RX */); - else - HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); - HFC_outb(hc, R_FIFO_MD, 0); - if (hc->ctype == HFC_TYPE_XHFC) - hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES; - else - hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES - | V_RLD_EPR; - HFC_outb(hc, R_CIRM, hc->hw.r_cirm); - udelay(100); - hc->hw.r_cirm = 0; - HFC_outb(hc, R_CIRM, hc->hw.r_cirm); - udelay(100); - if (hc->ctype != HFC_TYPE_XHFC) - HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); - - /* Speech Design PLX bridge pcm and sync mode */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - /* Connect PCM */ - if (hc->hw.r_pcm_md0 & V_PCM_MD) { - pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; - pv |= PLX_SYNC_O_EN; - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: master: PLX_GPIO=%x\n", - __func__, pv); - } else { - pv &= ~(PLX_MASTER_EN | PLX_SLAVE_EN_N); - pv &= ~PLX_SYNC_O_EN; - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: slave: PLX_GPIO=%x\n", - __func__, pv); - } - writel(pv, plx_acc_32); - spin_unlock_irqrestore(&plx_lock, plx_flags); - } - - /* PCM setup */ - HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90); - if (hc->slots == 32) - HFC_outb(hc, R_PCM_MD1, 0x00); - if (hc->slots == 64) - HFC_outb(hc, R_PCM_MD1, 0x10); - if (hc->slots == 128) - HFC_outb(hc, R_PCM_MD1, 0x20); - HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0); - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) - HFC_outb(hc, R_PCM_MD2, V_SYNC_SRC); /* sync via SYNC_I / O */ - else if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) - HFC_outb(hc, R_PCM_MD2, 0x10); /* V_C2O_EN */ - else - HFC_outb(hc, R_PCM_MD2, 0x00); /* sync from interface */ - HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); - for (i = 0; i < 256; i++) { - HFC_outb_nodebug(hc, R_SLOT, i); - HFC_outb_nodebug(hc, A_SL_CFG, 0); - if (hc->ctype != HFC_TYPE_XHFC) - HFC_outb_nodebug(hc, A_CONF, 0); - hc->slot_owner[i] = -1; - } - - /* set clock speed */ - if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: setting double clock\n", __func__); - HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); - } - - if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) - HFC_outb(hc, 0x02 /* R_CLK_CFG */, 0x40 /* V_CLKO_OFF */); - - /* B410P GPIO */ - if (test_bit(HFC_CHIP_B410P, &hc->chip)) { - printk(KERN_NOTICE "Setting GPIOs\n"); - HFC_outb(hc, R_GPIO_SEL, 0x30); - HFC_outb(hc, R_GPIO_EN1, 0x3); - udelay(1000); - printk(KERN_NOTICE "calling vpm_init\n"); - vpm_init(hc); - } - - /* check if R_F0_CNT counts (8 kHz frame count) */ - val = HFC_inb(hc, R_F0_CNTL); - val += HFC_inb(hc, R_F0_CNTH) << 8; - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "HFC_multi F0_CNT %ld after reset\n", val); - spin_unlock_irqrestore(&hc->lock, flags); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((HZ / 100) ? : 1); /* Timeout minimum 10ms */ - spin_lock_irqsave(&hc->lock, flags); - val2 = HFC_inb(hc, R_F0_CNTL); - val2 += HFC_inb(hc, R_F0_CNTH) << 8; - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "HFC_multi F0_CNT %ld after 10 ms (1st try)\n", - val2); - if (val2 >= val + 8) { /* 1 ms */ - /* it counts, so we keep the pcm mode */ - if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) - printk(KERN_INFO "controller is PCM bus MASTER\n"); - else - if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) - printk(KERN_INFO "controller is PCM bus SLAVE\n"); - else { - test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); - printk(KERN_INFO "controller is PCM bus SLAVE " - "(auto detected)\n"); - } - } else { - /* does not count */ - if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { - controller_fail: - printk(KERN_ERR "HFC_multi ERROR, getting no 125us " - "pulse. Seems that controller fails.\n"); - err = -EIO; - goto out; - } - if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { - printk(KERN_INFO "controller is PCM bus SLAVE " - "(ignoring missing PCM clock)\n"); - } else { - /* only one pcm master */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip) - && plxsd_master) { - printk(KERN_ERR "HFC_multi ERROR, no clock " - "on another Speech Design card found. " - "Please be sure to connect PCM cable.\n"); - err = -EIO; - goto out; - } - /* retry with master clock */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; - pv |= PLX_SYNC_O_EN; - writel(pv, plx_acc_32); - spin_unlock_irqrestore(&plx_lock, plx_flags); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: master: " - "PLX_GPIO=%x\n", __func__, pv); - } - hc->hw.r_pcm_md0 |= V_PCM_MD; - HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); - spin_unlock_irqrestore(&hc->lock, flags); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((HZ / 100) ?: 1); /* Timeout min. 10ms */ - spin_lock_irqsave(&hc->lock, flags); - val2 = HFC_inb(hc, R_F0_CNTL); - val2 += HFC_inb(hc, R_F0_CNTH) << 8; - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "HFC_multi F0_CNT %ld after " - "10 ms (2nd try)\n", val2); - if (val2 >= val + 8) { /* 1 ms */ - test_and_set_bit(HFC_CHIP_PCM_MASTER, - &hc->chip); - printk(KERN_INFO "controller is PCM bus MASTER " - "(auto detected)\n"); - } else - goto controller_fail; - } - } - - /* Release the DSP Reset */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) - plxsd_master = 1; - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc_32 = hc->plx_membase + PLX_GPIOC; - pv = readl(plx_acc_32); - pv |= PLX_DSP_RES_N; - writel(pv, plx_acc_32); - spin_unlock_irqrestore(&plx_lock, plx_flags); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: reset off: PLX_GPIO=%x\n", - __func__, pv); - } - - /* pcm id */ - if (hc->pcm) - printk(KERN_INFO "controller has given PCM BUS ID %d\n", - hc->pcm); - else { - if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) - || test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - PCM_cnt++; /* SD has proprietary bridging */ - } - hc->pcm = PCM_cnt; - printk(KERN_INFO "controller has PCM BUS ID %d " - "(auto selected)\n", hc->pcm); - } - - /* set up timer */ - HFC_outb(hc, R_TI_WD, poll_timer); - hc->hw.r_irqmsk_misc |= V_TI_IRQMSK; - - /* set E1 state machine IRQ */ - if (hc->ctype == HFC_TYPE_E1) - hc->hw.r_irqmsk_misc |= V_STA_IRQMSK; - - /* set DTMF detection */ - if (test_bit(HFC_CHIP_DTMF, &hc->chip)) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: enabling DTMF detection " - "for all B-channel\n", __func__); - hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP; - if (test_bit(HFC_CHIP_ULAW, &hc->chip)) - hc->hw.r_dtmf |= V_ULAW_SEL; - HFC_outb(hc, R_DTMF_N, 102 - 1); - hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK; - } - - /* conference engine */ - if (test_bit(HFC_CHIP_ULAW, &hc->chip)) - r_conf_en = V_CONF_EN | V_ULAW; - else - r_conf_en = V_CONF_EN; - if (hc->ctype != HFC_TYPE_XHFC) - HFC_outb(hc, R_CONF_EN, r_conf_en); - - /* setting leds */ - switch (hc->leds) { - case 1: /* HFC-E1 OEM */ - if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) - HFC_outb(hc, R_GPIO_SEL, 0x32); - else - HFC_outb(hc, R_GPIO_SEL, 0x30); - - HFC_outb(hc, R_GPIO_EN1, 0x0f); - HFC_outb(hc, R_GPIO_OUT1, 0x00); - - HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); - break; - - case 2: /* HFC-4S OEM */ - case 3: - HFC_outb(hc, R_GPIO_SEL, 0xf0); - HFC_outb(hc, R_GPIO_EN1, 0xff); - HFC_outb(hc, R_GPIO_OUT1, 0x00); - break; - } - - if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) { - hc->hw.r_st_sync = 0x10; /* V_AUTO_SYNCI */ - HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); - } - - /* set master clock */ - if (hc->masterclk >= 0) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: setting ST master clock " - "to port %d (0..%d)\n", - __func__, hc->masterclk, hc->ports - 1); - hc->hw.r_st_sync |= (hc->masterclk | V_AUTO_SYNC); - HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); - } - - - - /* setting misc irq */ - HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "r_irqmsk_misc.2: 0x%x\n", - hc->hw.r_irqmsk_misc); - - /* RAM access test */ - HFC_outb(hc, R_RAM_ADDR0, 0); - HFC_outb(hc, R_RAM_ADDR1, 0); - HFC_outb(hc, R_RAM_ADDR2, 0); - for (i = 0; i < 256; i++) { - HFC_outb_nodebug(hc, R_RAM_ADDR0, i); - HFC_outb_nodebug(hc, R_RAM_DATA, ((i * 3) & 0xff)); - } - for (i = 0; i < 256; i++) { - HFC_outb_nodebug(hc, R_RAM_ADDR0, i); - HFC_inb_nodebug(hc, R_RAM_DATA); - rval = HFC_inb_nodebug(hc, R_INT_DATA); - if (rval != ((i * 3) & 0xff)) { - printk(KERN_DEBUG - "addr:%x val:%x should:%x\n", i, rval, - (i * 3) & 0xff); - err++; - } - } - if (err) { - printk(KERN_DEBUG "aborting - %d RAM access errors\n", err); - err = -EIO; - goto out; - } - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: done\n", __func__); -out: - spin_unlock_irqrestore(&hc->lock, flags); - return err; -} - - -/* - * control the watchdog - */ -static void -hfcmulti_watchdog(struct hfc_multi *hc) -{ - hc->wdcount++; - - if (hc->wdcount > 10) { - hc->wdcount = 0; - hc->wdbyte = hc->wdbyte == V_GPIO_OUT2 ? - V_GPIO_OUT3 : V_GPIO_OUT2; - - /* printk("Sending Watchdog Kill %x\n",hc->wdbyte); */ - HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); - HFC_outb(hc, R_GPIO_OUT0, hc->wdbyte); - } -} - - - -/* - * output leds - */ -static void -hfcmulti_leds(struct hfc_multi *hc) -{ - unsigned long lled; - unsigned long leddw; - int i, state, active, leds; - struct dchannel *dch; - int led[4]; - - switch (hc->leds) { - case 1: /* HFC-E1 OEM */ - /* 2 red steady: LOS - * 1 red steady: L1 not active - * 2 green steady: L1 active - * 1st green flashing: activity on TX - * 2nd green flashing: activity on RX - */ - led[0] = 0; - led[1] = 0; - led[2] = 0; - led[3] = 0; - dch = hc->chan[hc->dnum[0]].dch; - if (dch) { - if (hc->chan[hc->dnum[0]].los) - led[1] = 1; - if (hc->e1_state != 1) { - led[0] = 1; - hc->flash[2] = 0; - hc->flash[3] = 0; - } else { - led[2] = 1; - led[3] = 1; - if (!hc->flash[2] && hc->activity_tx) - hc->flash[2] = poll; - if (!hc->flash[3] && hc->activity_rx) - hc->flash[3] = poll; - if (hc->flash[2] && hc->flash[2] < 1024) - led[2] = 0; - if (hc->flash[3] && hc->flash[3] < 1024) - led[3] = 0; - if (hc->flash[2] >= 2048) - hc->flash[2] = 0; - if (hc->flash[3] >= 2048) - hc->flash[3] = 0; - if (hc->flash[2]) - hc->flash[2] += poll; - if (hc->flash[3]) - hc->flash[3] += poll; - } - } - leds = (led[0] | (led[1]<<2) | (led[2]<<1) | (led[3]<<3))^0xF; - /* leds are inverted */ - if (leds != (int)hc->ledstate) { - HFC_outb_nodebug(hc, R_GPIO_OUT1, leds); - hc->ledstate = leds; - } - break; - - case 2: /* HFC-4S OEM */ - /* red steady: PH_DEACTIVATE - * green steady: PH_ACTIVATE - * green flashing: activity on TX - */ - for (i = 0; i < 4; i++) { - state = 0; - active = -1; - dch = hc->chan[(i << 2) | 2].dch; - if (dch) { - state = dch->state; - if (dch->dev.D.protocol == ISDN_P_NT_S0) - active = 3; - else - active = 7; - } - if (state) { - if (state == active) { - led[i] = 1; /* led green */ - hc->activity_tx |= hc->activity_rx; - if (!hc->flash[i] && - (hc->activity_tx & (1 << i))) - hc->flash[i] = poll; - if (hc->flash[i] && hc->flash[i] < 1024) - led[i] = 0; /* led off */ - if (hc->flash[i] >= 2048) - hc->flash[i] = 0; - if (hc->flash[i]) - hc->flash[i] += poll; - } else { - led[i] = 2; /* led red */ - hc->flash[i] = 0; - } - } else - led[i] = 0; /* led off */ - } - if (test_bit(HFC_CHIP_B410P, &hc->chip)) { - leds = 0; - for (i = 0; i < 4; i++) { - if (led[i] == 1) { - /*green*/ - leds |= (0x2 << (i * 2)); - } else if (led[i] == 2) { - /*red*/ - leds |= (0x1 << (i * 2)); - } - } - if (leds != (int)hc->ledstate) { - vpm_out(hc, 0, 0x1a8 + 3, leds); - hc->ledstate = leds; - } - } else { - leds = ((led[3] > 0) << 0) | ((led[1] > 0) << 1) | - ((led[0] > 0) << 2) | ((led[2] > 0) << 3) | - ((led[3] & 1) << 4) | ((led[1] & 1) << 5) | - ((led[0] & 1) << 6) | ((led[2] & 1) << 7); - if (leds != (int)hc->ledstate) { - HFC_outb_nodebug(hc, R_GPIO_EN1, leds & 0x0F); - HFC_outb_nodebug(hc, R_GPIO_OUT1, leds >> 4); - hc->ledstate = leds; - } - } - break; - - case 3: /* HFC 1S/2S Beronet */ - /* red steady: PH_DEACTIVATE - * green steady: PH_ACTIVATE - * green flashing: activity on TX - */ - for (i = 0; i < 2; i++) { - state = 0; - active = -1; - dch = hc->chan[(i << 2) | 2].dch; - if (dch) { - state = dch->state; - if (dch->dev.D.protocol == ISDN_P_NT_S0) - active = 3; - else - active = 7; - } - if (state) { - if (state == active) { - led[i] = 1; /* led green */ - hc->activity_tx |= hc->activity_rx; - if (!hc->flash[i] && - (hc->activity_tx & (1 << i))) - hc->flash[i] = poll; - if (hc->flash[i] < 1024) - led[i] = 0; /* led off */ - if (hc->flash[i] >= 2048) - hc->flash[i] = 0; - if (hc->flash[i]) - hc->flash[i] += poll; - } else { - led[i] = 2; /* led red */ - hc->flash[i] = 0; - } - } else - led[i] = 0; /* led off */ - } - leds = (led[0] > 0) | ((led[1] > 0) << 1) | ((led[0]&1) << 2) - | ((led[1]&1) << 3); - if (leds != (int)hc->ledstate) { - HFC_outb_nodebug(hc, R_GPIO_EN1, - ((led[0] > 0) << 2) | ((led[1] > 0) << 3)); - HFC_outb_nodebug(hc, R_GPIO_OUT1, - ((led[0] & 1) << 2) | ((led[1] & 1) << 3)); - hc->ledstate = leds; - } - break; - case 8: /* HFC 8S+ Beronet */ - /* off: PH_DEACTIVATE - * steady: PH_ACTIVATE - * flashing: activity on TX - */ - lled = 0xff; /* leds off */ - for (i = 0; i < 8; i++) { - state = 0; - active = -1; - dch = hc->chan[(i << 2) | 2].dch; - if (dch) { - state = dch->state; - if (dch->dev.D.protocol == ISDN_P_NT_S0) - active = 3; - else - active = 7; - } - if (state) { - if (state == active) { - lled &= ~(1 << i); /* led on */ - hc->activity_tx |= hc->activity_rx; - if (!hc->flash[i] && - (hc->activity_tx & (1 << i))) - hc->flash[i] = poll; - if (hc->flash[i] < 1024) - lled |= 1 << i; /* led off */ - if (hc->flash[i] >= 2048) - hc->flash[i] = 0; - if (hc->flash[i]) - hc->flash[i] += poll; - } else - hc->flash[i] = 0; - } - } - leddw = lled << 24 | lled << 16 | lled << 8 | lled; - if (leddw != hc->ledstate) { - /* HFC_outb(hc, R_BRG_PCM_CFG, 1); - HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); */ - /* was _io before */ - HFC_outb_nodebug(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); - outw(0x4000, hc->pci_iobase + 4); - outl(leddw, hc->pci_iobase); - HFC_outb_nodebug(hc, R_BRG_PCM_CFG, V_PCM_CLK); - hc->ledstate = leddw; - } - break; - } - hc->activity_tx = 0; - hc->activity_rx = 0; -} -/* - * read dtmf coefficients - */ - -static void -hfcmulti_dtmf(struct hfc_multi *hc) -{ - s32 *coeff; - u_int mantissa; - int co, ch; - struct bchannel *bch = NULL; - u8 exponent; - int dtmf = 0; - int addr; - u16 w_float; - struct sk_buff *skb; - struct mISDNhead *hh; - - if (debug & DEBUG_HFCMULTI_DTMF) - printk(KERN_DEBUG "%s: dtmf detection irq\n", __func__); - for (ch = 0; ch <= 31; ch++) { - /* only process enabled B-channels */ - bch = hc->chan[ch].bch; - if (!bch) - continue; - if (!hc->created[hc->chan[ch].port]) - continue; - if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) - continue; - if (debug & DEBUG_HFCMULTI_DTMF) - printk(KERN_DEBUG "%s: dtmf channel %d:", - __func__, ch); - coeff = &(hc->chan[ch].coeff[hc->chan[ch].coeff_count * 16]); - dtmf = 1; - for (co = 0; co < 8; co++) { - /* read W(n-1) coefficient */ - addr = hc->DTMFbase + ((co << 7) | (ch << 2)); - HFC_outb_nodebug(hc, R_RAM_ADDR0, addr); - HFC_outb_nodebug(hc, R_RAM_ADDR1, addr >> 8); - HFC_outb_nodebug(hc, R_RAM_ADDR2, (addr >> 16) - | V_ADDR_INC); - w_float = HFC_inb_nodebug(hc, R_RAM_DATA); - w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); - if (debug & DEBUG_HFCMULTI_DTMF) - printk(" %04x", w_float); - - /* decode float (see chip doc) */ - mantissa = w_float & 0x0fff; - if (w_float & 0x8000) - mantissa |= 0xfffff000; - exponent = (w_float >> 12) & 0x7; - if (exponent) { - mantissa ^= 0x1000; - mantissa <<= (exponent - 1); - } - - /* store coefficient */ - coeff[co << 1] = mantissa; - - /* read W(n) coefficient */ - w_float = HFC_inb_nodebug(hc, R_RAM_DATA); - w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); - if (debug & DEBUG_HFCMULTI_DTMF) - printk(" %04x", w_float); - - /* decode float (see chip doc) */ - mantissa = w_float & 0x0fff; - if (w_float & 0x8000) - mantissa |= 0xfffff000; - exponent = (w_float >> 12) & 0x7; - if (exponent) { - mantissa ^= 0x1000; - mantissa <<= (exponent - 1); - } - - /* store coefficient */ - coeff[(co << 1) | 1] = mantissa; - } - if (debug & DEBUG_HFCMULTI_DTMF) - printk(" DTMF ready %08x %08x %08x %08x " - "%08x %08x %08x %08x\n", - coeff[0], coeff[1], coeff[2], coeff[3], - coeff[4], coeff[5], coeff[6], coeff[7]); - hc->chan[ch].coeff_count++; - if (hc->chan[ch].coeff_count == 8) { - hc->chan[ch].coeff_count = 0; - skb = mI_alloc_skb(512, GFP_ATOMIC); - if (!skb) { - printk(KERN_DEBUG "%s: No memory for skb\n", - __func__); - continue; - } - hh = mISDN_HEAD_P(skb); - hh->prim = PH_CONTROL_IND; - hh->id = DTMF_HFC_COEF; - skb_put_data(skb, hc->chan[ch].coeff, 512); - recv_Bchannel_skb(bch, skb); - } - } - - /* restart DTMF processing */ - hc->dtmf = dtmf; - if (dtmf) - HFC_outb_nodebug(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); -} - - -/* - * fill fifo as much as possible - */ - -static void -hfcmulti_tx(struct hfc_multi *hc, int ch) -{ - int i, ii, temp, tmp_len, len = 0; - int Zspace, z1, z2; /* must be int for calculation */ - int Fspace, f1, f2; - u_char *d; - int *txpending, slot_tx; - struct bchannel *bch; - struct dchannel *dch; - struct sk_buff **sp = NULL; - int *idxp; - - bch = hc->chan[ch].bch; - dch = hc->chan[ch].dch; - if ((!dch) && (!bch)) - return; - - txpending = &hc->chan[ch].txpending; - slot_tx = hc->chan[ch].slot_tx; - if (dch) { - if (!test_bit(FLG_ACTIVE, &dch->Flags)) - return; - sp = &dch->tx_skb; - idxp = &dch->tx_idx; - } else { - if (!test_bit(FLG_ACTIVE, &bch->Flags)) - return; - sp = &bch->tx_skb; - idxp = &bch->tx_idx; - } - if (*sp) - len = (*sp)->len; - - if ((!len) && *txpending != 1) - return; /* no data */ - - if (test_bit(HFC_CHIP_B410P, &hc->chip) && - (hc->chan[ch].protocol == ISDN_P_B_RAW) && - (hc->chan[ch].slot_rx < 0) && - (hc->chan[ch].slot_tx < 0)) - HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1)); - else - HFC_outb_nodebug(hc, R_FIFO, ch << 1); - HFC_wait_nodebug(hc); - - if (*txpending == 2) { - /* reset fifo */ - HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait_nodebug(hc); - HFC_outb(hc, A_SUBCH_CFG, 0); - *txpending = 1; - } -next_frame: - if (dch || test_bit(FLG_HDLC, &bch->Flags)) { - f1 = HFC_inb_nodebug(hc, A_F1); - f2 = HFC_inb_nodebug(hc, A_F2); - while (f2 != (temp = HFC_inb_nodebug(hc, A_F2))) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG - "%s(card %d): reread f2 because %d!=%d\n", - __func__, hc->id + 1, temp, f2); - f2 = temp; /* repeat until F2 is equal */ - } - Fspace = f2 - f1 - 1; - if (Fspace < 0) - Fspace += hc->Flen; - /* - * Old FIFO handling doesn't give us the current Z2 read - * pointer, so we cannot send the next frame before the fifo - * is empty. It makes no difference except for a slightly - * lower performance. - */ - if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) { - if (f1 != f2) - Fspace = 0; - else - Fspace = 1; - } - /* one frame only for ST D-channels, to allow resending */ - if (hc->ctype != HFC_TYPE_E1 && dch) { - if (f1 != f2) - Fspace = 0; - } - /* F-counter full condition */ - if (Fspace == 0) - return; - } - z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; - z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; - while (z2 != (temp = (HFC_inw_nodebug(hc, A_Z2) - hc->Zmin))) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG "%s(card %d): reread z2 because " - "%d!=%d\n", __func__, hc->id + 1, temp, z2); - z2 = temp; /* repeat unti Z2 is equal */ - } - hc->chan[ch].Zfill = z1 - z2; - if (hc->chan[ch].Zfill < 0) - hc->chan[ch].Zfill += hc->Zlen; - Zspace = z2 - z1; - if (Zspace <= 0) - Zspace += hc->Zlen; - Zspace -= 4; /* keep not too full, so pointers will not overrun */ - /* fill transparent data only to maximum transparent load (minus 4) */ - if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) - Zspace = Zspace - hc->Zlen + hc->max_trans; - if (Zspace <= 0) /* no space of 4 bytes */ - return; - - /* if no data */ - if (!len) { - if (z1 == z2) { /* empty */ - /* if done with FIFO audio data during PCM connection */ - if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && - *txpending && slot_tx >= 0) { - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG - "%s: reconnecting PCM due to no " - "more FIFO data: channel %d " - "slot_tx %d\n", - __func__, ch, slot_tx); - /* connect slot */ - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, A_CON_HDLC, 0xc0 - | 0x07 << 2 | V_HDLC_TRP | V_IFF); - /* Enable FIFO, no interrupt */ - else - HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | - V_HDLC_TRP | V_IFF); - HFC_outb_nodebug(hc, R_FIFO, ch << 1 | 1); - HFC_wait_nodebug(hc); - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, A_CON_HDLC, 0xc0 - | 0x07 << 2 | V_HDLC_TRP | V_IFF); - /* Enable FIFO, no interrupt */ - else - HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | - V_HDLC_TRP | V_IFF); - HFC_outb_nodebug(hc, R_FIFO, ch << 1); - HFC_wait_nodebug(hc); - } - *txpending = 0; - } - return; /* no data */ - } - - /* "fill fifo if empty" feature */ - if (bch && test_bit(FLG_FILLEMPTY, &bch->Flags) - && !test_bit(FLG_HDLC, &bch->Flags) && z2 == z1) { - if (debug & DEBUG_HFCMULTI_FILL) - printk(KERN_DEBUG "%s: buffer empty, so we have " - "underrun\n", __func__); - /* fill buffer, to prevent future underrun */ - hc->write_fifo(hc, hc->silence_data, poll >> 1); - Zspace -= (poll >> 1); - } - - /* if audio data and connected slot */ - if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending) - && slot_tx >= 0) { - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG "%s: disconnecting PCM due to " - "FIFO data: channel %d slot_tx %d\n", - __func__, ch, slot_tx); - /* disconnect slot */ - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, A_CON_HDLC, 0x80 - | 0x07 << 2 | V_HDLC_TRP | V_IFF); - /* Enable FIFO, no interrupt */ - else - HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | - V_HDLC_TRP | V_IFF); - HFC_outb_nodebug(hc, R_FIFO, ch << 1 | 1); - HFC_wait_nodebug(hc); - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, A_CON_HDLC, 0x80 - | 0x07 << 2 | V_HDLC_TRP | V_IFF); - /* Enable FIFO, no interrupt */ - else - HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | - V_HDLC_TRP | V_IFF); - HFC_outb_nodebug(hc, R_FIFO, ch << 1); - HFC_wait_nodebug(hc); - } - *txpending = 1; - - /* show activity */ - if (dch) - hc->activity_tx |= 1 << hc->chan[ch].port; - - /* fill fifo to what we have left */ - ii = len; - if (dch || test_bit(FLG_HDLC, &bch->Flags)) - temp = 1; - else - temp = 0; - i = *idxp; - d = (*sp)->data + i; - if (ii - i > Zspace) - ii = Zspace + i; - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG "%s(card %d): fifo(%d) has %d bytes space " - "left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n", - __func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i, - temp ? "HDLC" : "TRANS"); - - /* Have to prep the audio data */ - hc->write_fifo(hc, d, ii - i); - hc->chan[ch].Zfill += ii - i; - *idxp = ii; - - /* if not all data has been written */ - if (ii != len) { - /* NOTE: fifo is started by the calling function */ - return; - } - - /* if all data has been written, terminate frame */ - if (dch || test_bit(FLG_HDLC, &bch->Flags)) { - /* increment f-counter */ - HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); - HFC_wait_nodebug(hc); - } - - tmp_len = (*sp)->len; - dev_kfree_skb(*sp); - /* check for next frame */ - if (bch && get_next_bframe(bch)) { - len = tmp_len; - goto next_frame; - } - if (dch && get_next_dframe(dch)) { - len = tmp_len; - goto next_frame; - } - - /* - * now we have no more data, so in case of transparent, - * we set the last byte in fifo to 'silence' in case we will get - * no more data at all. this prevents sending an undefined value. - */ - if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) - HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); -} - - -/* NOTE: only called if E1 card is in active state */ -static void -hfcmulti_rx(struct hfc_multi *hc, int ch) -{ - int temp; - int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */ - int f1 = 0, f2 = 0; /* = 0, to make GCC happy */ - int again = 0; - struct bchannel *bch; - struct dchannel *dch = NULL; - struct sk_buff *skb, **sp = NULL; - int maxlen; - - bch = hc->chan[ch].bch; - if (bch) { - if (!test_bit(FLG_ACTIVE, &bch->Flags)) - return; - } else if (hc->chan[ch].dch) { - dch = hc->chan[ch].dch; - if (!test_bit(FLG_ACTIVE, &dch->Flags)) - return; - } else { - return; - } -next_frame: - /* on first AND before getting next valid frame, R_FIFO must be written - to. */ - if (test_bit(HFC_CHIP_B410P, &hc->chip) && - (hc->chan[ch].protocol == ISDN_P_B_RAW) && - (hc->chan[ch].slot_rx < 0) && - (hc->chan[ch].slot_tx < 0)) - HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1) | 1); - else - HFC_outb_nodebug(hc, R_FIFO, (ch << 1) | 1); - HFC_wait_nodebug(hc); - - /* ignore if rx is off BUT change fifo (above) to start pending TX */ - if (hc->chan[ch].rx_off) { - if (bch) - bch->dropcnt += poll; /* not exact but fair enough */ - return; - } - - if (dch || test_bit(FLG_HDLC, &bch->Flags)) { - f1 = HFC_inb_nodebug(hc, A_F1); - while (f1 != (temp = HFC_inb_nodebug(hc, A_F1))) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG - "%s(card %d): reread f1 because %d!=%d\n", - __func__, hc->id + 1, temp, f1); - f1 = temp; /* repeat until F1 is equal */ - } - f2 = HFC_inb_nodebug(hc, A_F2); - } - z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; - while (z1 != (temp = (HFC_inw_nodebug(hc, A_Z1) - hc->Zmin))) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG "%s(card %d): reread z2 because " - "%d!=%d\n", __func__, hc->id + 1, temp, z2); - z1 = temp; /* repeat until Z1 is equal */ - } - z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; - Zsize = z1 - z2; - if ((dch || test_bit(FLG_HDLC, &bch->Flags)) && f1 != f2) - /* complete hdlc frame */ - Zsize++; - if (Zsize < 0) - Zsize += hc->Zlen; - /* if buffer is empty */ - if (Zsize <= 0) - return; - - if (bch) { - maxlen = bchannel_get_rxbuf(bch, Zsize); - if (maxlen < 0) { - pr_warn("card%d.B%d: No bufferspace for %d bytes\n", - hc->id + 1, bch->nr, Zsize); - return; - } - sp = &bch->rx_skb; - maxlen = bch->maxlen; - } else { /* Dchannel */ - sp = &dch->rx_skb; - maxlen = dch->maxlen + 3; - if (*sp == NULL) { - *sp = mI_alloc_skb(maxlen, GFP_ATOMIC); - if (*sp == NULL) { - pr_warn("card%d: No mem for dch rx_skb\n", - hc->id + 1); - return; - } - } - } - /* show activity */ - if (dch) - hc->activity_rx |= 1 << hc->chan[ch].port; - - /* empty fifo with what we have */ - if (dch || test_bit(FLG_HDLC, &bch->Flags)) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d " - "bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) " - "got=%d (again %d)\n", __func__, hc->id + 1, ch, - Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE", - f1, f2, Zsize + (*sp)->len, again); - /* HDLC */ - if ((Zsize + (*sp)->len) > maxlen) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG - "%s(card %d): hdlc-frame too large.\n", - __func__, hc->id + 1); - skb_trim(*sp, 0); - HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait_nodebug(hc); - return; - } - - hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); - - if (f1 != f2) { - /* increment Z2,F2-counter */ - HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); - HFC_wait_nodebug(hc); - /* check size */ - if ((*sp)->len < 4) { - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG - "%s(card %d): Frame below minimum " - "size\n", __func__, hc->id + 1); - skb_trim(*sp, 0); - goto next_frame; - } - /* there is at least one complete frame, check crc */ - if ((*sp)->data[(*sp)->len - 1]) { - if (debug & DEBUG_HFCMULTI_CRC) - printk(KERN_DEBUG - "%s: CRC-error\n", __func__); - skb_trim(*sp, 0); - goto next_frame; - } - skb_trim(*sp, (*sp)->len - 3); - if ((*sp)->len < MISDN_COPY_SIZE) { - skb = *sp; - *sp = mI_alloc_skb(skb->len, GFP_ATOMIC); - if (*sp) { - skb_put_data(*sp, skb->data, skb->len); - skb_trim(skb, 0); - } else { - printk(KERN_DEBUG "%s: No mem\n", - __func__); - *sp = skb; - skb = NULL; - } - } else { - skb = NULL; - } - if (debug & DEBUG_HFCMULTI_FIFO) { - printk(KERN_DEBUG "%s(card %d):", - __func__, hc->id + 1); - temp = 0; - while (temp < (*sp)->len) - printk(" %02x", (*sp)->data[temp++]); - printk("\n"); - } - if (dch) - recv_Dchannel(dch); - else - recv_Bchannel(bch, MISDN_ID_ANY, false); - *sp = skb; - again++; - goto next_frame; - } - /* there is an incomplete frame */ - } else { - /* transparent */ - hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); - if (debug & DEBUG_HFCMULTI_FIFO) - printk(KERN_DEBUG - "%s(card %d): fifo(%d) reading %d bytes " - "(z1=%04x, z2=%04x) TRANS\n", - __func__, hc->id + 1, ch, Zsize, z1, z2); - /* only bch is transparent */ - recv_Bchannel(bch, hc->chan[ch].Zfill, false); - } -} - - -/* - * Interrupt handler - */ -static void -signal_state_up(struct dchannel *dch, int info, char *msg) -{ - struct sk_buff *skb; - int id, data = info; - - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG "%s: %s\n", __func__, msg); - - id = TEI_SAPI | (GROUP_TEI << 8); /* manager address */ - - skb = _alloc_mISDN_skb(MPH_INFORMATION_IND, id, sizeof(data), &data, - GFP_ATOMIC); - if (!skb) - return; - recv_Dchannel_skb(dch, skb); -} - -static inline void -handle_timer_irq(struct hfc_multi *hc) -{ - int ch, temp; - struct dchannel *dch; - u_long flags; - - /* process queued resync jobs */ - if (hc->e1_resync) { - /* lock, so e1_resync gets not changed */ - spin_lock_irqsave(&HFClock, flags); - if (hc->e1_resync & 1) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "Enable SYNC_I\n"); - HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC); - /* disable JATT, if RX_SYNC is set */ - if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) - HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); - } - if (hc->e1_resync & 2) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG "Enable jatt PLL\n"); - HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); - } - if (hc->e1_resync & 4) { - if (debug & DEBUG_HFCMULTI_PLXSD) - printk(KERN_DEBUG - "Enable QUARTZ for HFC-E1\n"); - /* set jatt to quartz */ - HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC - | V_JATT_OFF); - /* switch to JATT, in case it is not already */ - HFC_outb(hc, R_SYNC_OUT, 0); - } - hc->e1_resync = 0; - spin_unlock_irqrestore(&HFClock, flags); - } - - if (hc->ctype != HFC_TYPE_E1 || hc->e1_state == 1) - for (ch = 0; ch <= 31; ch++) { - if (hc->created[hc->chan[ch].port]) { - hfcmulti_tx(hc, ch); - /* fifo is started when switching to rx-fifo */ - hfcmulti_rx(hc, ch); - if (hc->chan[ch].dch && - hc->chan[ch].nt_timer > -1) { - dch = hc->chan[ch].dch; - if (!(--hc->chan[ch].nt_timer)) { - schedule_event(dch, - FLG_PHCHANGE); - if (debug & - DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: nt_timer at " - "state %x\n", - __func__, - dch->state); - } - } - } - } - if (hc->ctype == HFC_TYPE_E1 && hc->created[0]) { - dch = hc->chan[hc->dnum[0]].dch; - /* LOS */ - temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS; - hc->chan[hc->dnum[0]].los = temp; - if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dnum[0]].cfg)) { - if (!temp && hc->chan[hc->dnum[0]].los) - signal_state_up(dch, L1_SIGNAL_LOS_ON, - "LOS detected"); - if (temp && !hc->chan[hc->dnum[0]].los) - signal_state_up(dch, L1_SIGNAL_LOS_OFF, - "LOS gone"); - } - if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dnum[0]].cfg)) { - /* AIS */ - temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS; - if (!temp && hc->chan[hc->dnum[0]].ais) - signal_state_up(dch, L1_SIGNAL_AIS_ON, - "AIS detected"); - if (temp && !hc->chan[hc->dnum[0]].ais) - signal_state_up(dch, L1_SIGNAL_AIS_OFF, - "AIS gone"); - hc->chan[hc->dnum[0]].ais = temp; - } - if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dnum[0]].cfg)) { - /* SLIP */ - temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX; - if (!temp && hc->chan[hc->dnum[0]].slip_rx) - signal_state_up(dch, L1_SIGNAL_SLIP_RX, - " bit SLIP detected RX"); - hc->chan[hc->dnum[0]].slip_rx = temp; - temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX; - if (!temp && hc->chan[hc->dnum[0]].slip_tx) - signal_state_up(dch, L1_SIGNAL_SLIP_TX, - " bit SLIP detected TX"); - hc->chan[hc->dnum[0]].slip_tx = temp; - } - if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dnum[0]].cfg)) { - /* RDI */ - temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A; - if (!temp && hc->chan[hc->dnum[0]].rdi) - signal_state_up(dch, L1_SIGNAL_RDI_ON, - "RDI detected"); - if (temp && !hc->chan[hc->dnum[0]].rdi) - signal_state_up(dch, L1_SIGNAL_RDI_OFF, - "RDI gone"); - hc->chan[hc->dnum[0]].rdi = temp; - } - temp = HFC_inb_nodebug(hc, R_JATT_DIR); - switch (hc->chan[hc->dnum[0]].sync) { - case 0: - if ((temp & 0x60) == 0x60) { - if (debug & DEBUG_HFCMULTI_SYNC) - printk(KERN_DEBUG - "%s: (id=%d) E1 now " - "in clock sync\n", - __func__, hc->id); - HFC_outb(hc, R_RX_OFF, - hc->chan[hc->dnum[0]].jitter | V_RX_INIT); - HFC_outb(hc, R_TX_OFF, - hc->chan[hc->dnum[0]].jitter | V_RX_INIT); - hc->chan[hc->dnum[0]].sync = 1; - goto check_framesync; - } - break; - case 1: - if ((temp & 0x60) != 0x60) { - if (debug & DEBUG_HFCMULTI_SYNC) - printk(KERN_DEBUG - "%s: (id=%d) E1 " - "lost clock sync\n", - __func__, hc->id); - hc->chan[hc->dnum[0]].sync = 0; - break; - } - check_framesync: - temp = HFC_inb_nodebug(hc, R_SYNC_STA); - if (temp == 0x27) { - if (debug & DEBUG_HFCMULTI_SYNC) - printk(KERN_DEBUG - "%s: (id=%d) E1 " - "now in frame sync\n", - __func__, hc->id); - hc->chan[hc->dnum[0]].sync = 2; - } - break; - case 2: - if ((temp & 0x60) != 0x60) { - if (debug & DEBUG_HFCMULTI_SYNC) - printk(KERN_DEBUG - "%s: (id=%d) E1 lost " - "clock & frame sync\n", - __func__, hc->id); - hc->chan[hc->dnum[0]].sync = 0; - break; - } - temp = HFC_inb_nodebug(hc, R_SYNC_STA); - if (temp != 0x27) { - if (debug & DEBUG_HFCMULTI_SYNC) - printk(KERN_DEBUG - "%s: (id=%d) E1 " - "lost frame sync\n", - __func__, hc->id); - hc->chan[hc->dnum[0]].sync = 1; - } - break; - } - } - - if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) - hfcmulti_watchdog(hc); - - if (hc->leds) - hfcmulti_leds(hc); -} - -static void -ph_state_irq(struct hfc_multi *hc, u_char r_irq_statech) -{ - struct dchannel *dch; - int ch; - int active; - u_char st_status, temp; - - /* state machine */ - for (ch = 0; ch <= 31; ch++) { - if (hc->chan[ch].dch) { - dch = hc->chan[ch].dch; - if (r_irq_statech & 1) { - HFC_outb_nodebug(hc, R_ST_SEL, - hc->chan[ch].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - /* undocumented: status changes during read */ - st_status = HFC_inb_nodebug(hc, A_ST_RD_STATE); - while (st_status != (temp = - HFC_inb_nodebug(hc, A_ST_RD_STATE))) { - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG "%s: reread " - "STATE because %d!=%d\n", - __func__, temp, - st_status); - st_status = temp; /* repeat */ - } - - /* Speech Design TE-sync indication */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && - dch->dev.D.protocol == ISDN_P_TE_S0) { - if (st_status & V_FR_SYNC_ST) - hc->syncronized |= - (1 << hc->chan[ch].port); - else - hc->syncronized &= - ~(1 << hc->chan[ch].port); - } - dch->state = st_status & 0x0f; - if (dch->dev.D.protocol == ISDN_P_NT_S0) - active = 3; - else - active = 7; - if (dch->state == active) { - HFC_outb_nodebug(hc, R_FIFO, - (ch << 1) | 1); - HFC_wait_nodebug(hc); - HFC_outb_nodebug(hc, - R_INC_RES_FIFO, V_RES_F); - HFC_wait_nodebug(hc); - dch->tx_idx = 0; - } - schedule_event(dch, FLG_PHCHANGE); - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: S/T newstate %x port %d\n", - __func__, dch->state, - hc->chan[ch].port); - } - r_irq_statech >>= 1; - } - } - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) - plxsd_checksync(hc, 0); -} - -static void -fifo_irq(struct hfc_multi *hc, int block) -{ - int ch, j; - struct dchannel *dch; - struct bchannel *bch; - u_char r_irq_fifo_bl; - - r_irq_fifo_bl = HFC_inb_nodebug(hc, R_IRQ_FIFO_BL0 + block); - j = 0; - while (j < 8) { - ch = (block << 2) + (j >> 1); - dch = hc->chan[ch].dch; - bch = hc->chan[ch].bch; - if (((!dch) && (!bch)) || (!hc->created[hc->chan[ch].port])) { - j += 2; - continue; - } - if (dch && (r_irq_fifo_bl & (1 << j)) && - test_bit(FLG_ACTIVE, &dch->Flags)) { - hfcmulti_tx(hc, ch); - /* start fifo */ - HFC_outb_nodebug(hc, R_FIFO, 0); - HFC_wait_nodebug(hc); - } - if (bch && (r_irq_fifo_bl & (1 << j)) && - test_bit(FLG_ACTIVE, &bch->Flags)) { - hfcmulti_tx(hc, ch); - /* start fifo */ - HFC_outb_nodebug(hc, R_FIFO, 0); - HFC_wait_nodebug(hc); - } - j++; - if (dch && (r_irq_fifo_bl & (1 << j)) && - test_bit(FLG_ACTIVE, &dch->Flags)) { - hfcmulti_rx(hc, ch); - } - if (bch && (r_irq_fifo_bl & (1 << j)) && - test_bit(FLG_ACTIVE, &bch->Flags)) { - hfcmulti_rx(hc, ch); - } - j++; - } -} - -#ifdef IRQ_DEBUG -int irqsem; -#endif -static irqreturn_t -hfcmulti_interrupt(int intno, void *dev_id) -{ -#ifdef IRQCOUNT_DEBUG - static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0, - iq5 = 0, iq6 = 0, iqcnt = 0; -#endif - struct hfc_multi *hc = dev_id; - struct dchannel *dch; - u_char r_irq_statech, status, r_irq_misc, r_irq_oview; - int i; - void __iomem *plx_acc; - u_short wval; - u_char e1_syncsta, temp, temp2; - u_long flags; - - if (!hc) { - printk(KERN_ERR "HFC-multi: Spurious interrupt!\n"); - return IRQ_NONE; - } - - spin_lock(&hc->lock); - -#ifdef IRQ_DEBUG - if (irqsem) - printk(KERN_ERR "irq for card %d during irq from " - "card %d, this is no bug.\n", hc->id + 1, irqsem); - irqsem = hc->id + 1; -#endif -#ifdef CONFIG_MISDN_HFCMULTI_8xx - if (hc->immap->im_cpm.cp_pbdat & hc->pb_irqmsk) - goto irq_notforus; -#endif - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - spin_lock_irqsave(&plx_lock, flags); - plx_acc = hc->plx_membase + PLX_INTCSR; - wval = readw(plx_acc); - spin_unlock_irqrestore(&plx_lock, flags); - if (!(wval & PLX_INTCSR_LINTI1_STATUS)) - goto irq_notforus; - } - - status = HFC_inb_nodebug(hc, R_STATUS); - r_irq_statech = HFC_inb_nodebug(hc, R_IRQ_STATECH); -#ifdef IRQCOUNT_DEBUG - if (r_irq_statech) - iq1++; - if (status & V_DTMF_STA) - iq2++; - if (status & V_LOST_STA) - iq3++; - if (status & V_EXT_IRQSTA) - iq4++; - if (status & V_MISC_IRQSTA) - iq5++; - if (status & V_FR_IRQSTA) - iq6++; - if (iqcnt++ > 5000) { - printk(KERN_ERR "iq1:%x iq2:%x iq3:%x iq4:%x iq5:%x iq6:%x\n", - iq1, iq2, iq3, iq4, iq5, iq6); - iqcnt = 0; - } -#endif - - if (!r_irq_statech && - !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | - V_MISC_IRQSTA | V_FR_IRQSTA))) { - /* irq is not for us */ - goto irq_notforus; - } - hc->irqcnt++; - if (r_irq_statech) { - if (hc->ctype != HFC_TYPE_E1) - ph_state_irq(hc, r_irq_statech); - } - if (status & V_LOST_STA) { - /* LOST IRQ */ - HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */ - } - if (status & V_MISC_IRQSTA) { - /* misc IRQ */ - r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC); - r_irq_misc &= hc->hw.r_irqmsk_misc; /* ignore disabled irqs */ - if (r_irq_misc & V_STA_IRQ) { - if (hc->ctype == HFC_TYPE_E1) { - /* state machine */ - dch = hc->chan[hc->dnum[0]].dch; - e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA); - if (test_bit(HFC_CHIP_PLXSD, &hc->chip) - && hc->e1_getclock) { - if (e1_syncsta & V_FR_SYNC_E1) - hc->syncronized = 1; - else - hc->syncronized = 0; - } - /* undocumented: status changes during read */ - temp = HFC_inb_nodebug(hc, R_E1_RD_STA); - while (temp != (temp2 = - HFC_inb_nodebug(hc, R_E1_RD_STA))) { - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG "%s: reread " - "STATE because %d!=%d\n", - __func__, temp, temp2); - temp = temp2; /* repeat */ - } - /* broadcast state change to all fragments */ - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: E1 (id=%d) newstate %x\n", - __func__, hc->id, temp & 0x7); - for (i = 0; i < hc->ports; i++) { - dch = hc->chan[hc->dnum[i]].dch; - dch->state = temp & 0x7; - schedule_event(dch, FLG_PHCHANGE); - } - - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) - plxsd_checksync(hc, 0); - } - } - if (r_irq_misc & V_TI_IRQ) { - if (hc->iclock_on) - mISDN_clock_update(hc->iclock, poll, NULL); - handle_timer_irq(hc); - } - - if (r_irq_misc & V_DTMF_IRQ) - hfcmulti_dtmf(hc); - - if (r_irq_misc & V_IRQ_PROC) { - static int irq_proc_cnt; - if (!irq_proc_cnt++) - printk(KERN_DEBUG "%s: got V_IRQ_PROC -" - " this should not happen\n", __func__); - } - - } - if (status & V_FR_IRQSTA) { - /* FIFO IRQ */ - r_irq_oview = HFC_inb_nodebug(hc, R_IRQ_OVIEW); - for (i = 0; i < 8; i++) { - if (r_irq_oview & (1 << i)) - fifo_irq(hc, i); - } - } - -#ifdef IRQ_DEBUG - irqsem = 0; -#endif - spin_unlock(&hc->lock); - return IRQ_HANDLED; - -irq_notforus: -#ifdef IRQ_DEBUG - irqsem = 0; -#endif - spin_unlock(&hc->lock); - return IRQ_NONE; -} - - -/* - * timer callback for D-chan busy resolution. Currently no function - */ - -static void -hfcmulti_dbusy_timer(struct timer_list *t) -{ -} - - -/* - * activate/deactivate hardware for selected channels and mode - * - * configure B-channel with the given protocol - * ch eqals to the HFC-channel (0-31) - * ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31 - * for S/T, 1-31 for E1) - * the hdlc interrupts will be set/unset - */ -static int -mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx, - int bank_tx, int slot_rx, int bank_rx) -{ - int flow_tx = 0, flow_rx = 0, routing = 0; - int oslot_tx, oslot_rx; - int conf; - - if (ch < 0 || ch > 31) - return -EINVAL; - oslot_tx = hc->chan[ch].slot_tx; - oslot_rx = hc->chan[ch].slot_rx; - conf = hc->chan[ch].conf; - - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG - "%s: card %d channel %d protocol %x slot old=%d new=%d " - "bank new=%d (TX) slot old=%d new=%d bank new=%d (RX)\n", - __func__, hc->id, ch, protocol, oslot_tx, slot_tx, - bank_tx, oslot_rx, slot_rx, bank_rx); - - if (oslot_tx >= 0 && slot_tx != oslot_tx) { - /* remove from slot */ - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG "%s: remove from slot %d (TX)\n", - __func__, oslot_tx); - if (hc->slot_owner[oslot_tx << 1] == ch) { - HFC_outb(hc, R_SLOT, oslot_tx << 1); - HFC_outb(hc, A_SL_CFG, 0); - if (hc->ctype != HFC_TYPE_XHFC) - HFC_outb(hc, A_CONF, 0); - hc->slot_owner[oslot_tx << 1] = -1; - } else { - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG - "%s: we are not owner of this tx slot " - "anymore, channel %d is.\n", - __func__, hc->slot_owner[oslot_tx << 1]); - } - } - - if (oslot_rx >= 0 && slot_rx != oslot_rx) { - /* remove from slot */ - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG - "%s: remove from slot %d (RX)\n", - __func__, oslot_rx); - if (hc->slot_owner[(oslot_rx << 1) | 1] == ch) { - HFC_outb(hc, R_SLOT, (oslot_rx << 1) | V_SL_DIR); - HFC_outb(hc, A_SL_CFG, 0); - hc->slot_owner[(oslot_rx << 1) | 1] = -1; - } else { - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG - "%s: we are not owner of this rx slot " - "anymore, channel %d is.\n", - __func__, - hc->slot_owner[(oslot_rx << 1) | 1]); - } - } - - if (slot_tx < 0) { - flow_tx = 0x80; /* FIFO->ST */ - /* disable pcm slot */ - hc->chan[ch].slot_tx = -1; - hc->chan[ch].bank_tx = 0; - } else { - /* set pcm slot */ - if (hc->chan[ch].txpending) - flow_tx = 0x80; /* FIFO->ST */ - else - flow_tx = 0xc0; /* PCM->ST */ - /* put on slot */ - routing = bank_tx ? 0xc0 : 0x80; - if (conf >= 0 || bank_tx > 1) - routing = 0x40; /* loop */ - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG "%s: put channel %d to slot %d bank" - " %d flow %02x routing %02x conf %d (TX)\n", - __func__, ch, slot_tx, bank_tx, - flow_tx, routing, conf); - HFC_outb(hc, R_SLOT, slot_tx << 1); - HFC_outb(hc, A_SL_CFG, (ch << 1) | routing); - if (hc->ctype != HFC_TYPE_XHFC) - HFC_outb(hc, A_CONF, - (conf < 0) ? 0 : (conf | V_CONF_SL)); - hc->slot_owner[slot_tx << 1] = ch; - hc->chan[ch].slot_tx = slot_tx; - hc->chan[ch].bank_tx = bank_tx; - } - if (slot_rx < 0) { - /* disable pcm slot */ - flow_rx = 0x80; /* ST->FIFO */ - hc->chan[ch].slot_rx = -1; - hc->chan[ch].bank_rx = 0; - } else { - /* set pcm slot */ - if (hc->chan[ch].txpending) - flow_rx = 0x80; /* ST->FIFO */ - else - flow_rx = 0xc0; /* ST->(FIFO,PCM) */ - /* put on slot */ - routing = bank_rx ? 0x80 : 0xc0; /* reversed */ - if (conf >= 0 || bank_rx > 1) - routing = 0x40; /* loop */ - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG "%s: put channel %d to slot %d bank" - " %d flow %02x routing %02x conf %d (RX)\n", - __func__, ch, slot_rx, bank_rx, - flow_rx, routing, conf); - HFC_outb(hc, R_SLOT, (slot_rx << 1) | V_SL_DIR); - HFC_outb(hc, A_SL_CFG, (ch << 1) | V_CH_DIR | routing); - hc->slot_owner[(slot_rx << 1) | 1] = ch; - hc->chan[ch].slot_rx = slot_rx; - hc->chan[ch].bank_rx = bank_rx; - } - - switch (protocol) { - case (ISDN_P_NONE): - /* disable TX fifo */ - HFC_outb(hc, R_FIFO, ch << 1); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_IFF); - HFC_outb(hc, A_SUBCH_CFG, 0); - HFC_outb(hc, A_IRQ_MSK, 0); - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - /* disable RX fifo */ - HFC_outb(hc, R_FIFO, (ch << 1) | 1); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00); - HFC_outb(hc, A_SUBCH_CFG, 0); - HFC_outb(hc, A_IRQ_MSK, 0); - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - if (hc->chan[ch].bch && hc->ctype != HFC_TYPE_E1) { - hc->hw.a_st_ctrl0[hc->chan[ch].port] &= - ((ch & 0x3) == 0) ? ~V_B1_EN : ~V_B2_EN; - HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_CTRL0, - hc->hw.a_st_ctrl0[hc->chan[ch].port]); - } - if (hc->chan[ch].bch) { - test_and_clear_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); - test_and_clear_bit(FLG_TRANSPARENT, - &hc->chan[ch].bch->Flags); - } - break; - case (ISDN_P_B_RAW): /* B-channel */ - - if (test_bit(HFC_CHIP_B410P, &hc->chip) && - (hc->chan[ch].slot_rx < 0) && - (hc->chan[ch].slot_tx < 0)) { - - printk(KERN_DEBUG - "Setting B-channel %d to echo cancelable " - "state on PCM slot %d\n", ch, - ((ch / 4) * 8) + ((ch % 4) * 4) + 1); - printk(KERN_DEBUG - "Enabling pass through for channel\n"); - vpm_out(hc, ch, ((ch / 4) * 8) + - ((ch % 4) * 4) + 1, 0x01); - /* rx path */ - /* S/T -> PCM */ - HFC_outb(hc, R_FIFO, (ch << 1)); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); - HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + - ((ch % 4) * 4) + 1) << 1); - HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1)); - - /* PCM -> FIFO */ - HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); - HFC_outb(hc, A_SUBCH_CFG, 0); - HFC_outb(hc, A_IRQ_MSK, 0); - if (hc->chan[ch].protocol != protocol) { - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - } - HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + - ((ch % 4) * 4) + 1) << 1) | 1); - HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1); - - /* tx path */ - /* PCM -> S/T */ - HFC_outb(hc, R_FIFO, (ch << 1) | 1); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); - HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + - ((ch % 4) * 4)) << 1) | 1); - HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1); - - /* FIFO -> PCM */ - HFC_outb(hc, R_FIFO, 0x20 | (ch << 1)); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); - HFC_outb(hc, A_SUBCH_CFG, 0); - HFC_outb(hc, A_IRQ_MSK, 0); - if (hc->chan[ch].protocol != protocol) { - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - } - /* tx silence */ - HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); - HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + - ((ch % 4) * 4)) << 1); - HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1)); - } else { - /* enable TX fifo */ - HFC_outb(hc, R_FIFO, ch << 1); - HFC_wait(hc); - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, A_CON_HDLC, flow_tx | 0x07 << 2 | - V_HDLC_TRP | V_IFF); - /* Enable FIFO, no interrupt */ - else - HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | - V_HDLC_TRP | V_IFF); - HFC_outb(hc, A_SUBCH_CFG, 0); - HFC_outb(hc, A_IRQ_MSK, 0); - if (hc->chan[ch].protocol != protocol) { - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - } - /* tx silence */ - HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); - /* enable RX fifo */ - HFC_outb(hc, R_FIFO, (ch << 1) | 1); - HFC_wait(hc); - if (hc->ctype == HFC_TYPE_XHFC) - HFC_outb(hc, A_CON_HDLC, flow_rx | 0x07 << 2 | - V_HDLC_TRP); - /* Enable FIFO, no interrupt*/ - else - HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | - V_HDLC_TRP); - HFC_outb(hc, A_SUBCH_CFG, 0); - HFC_outb(hc, A_IRQ_MSK, 0); - if (hc->chan[ch].protocol != protocol) { - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - } - } - if (hc->ctype != HFC_TYPE_E1) { - hc->hw.a_st_ctrl0[hc->chan[ch].port] |= - ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN; - HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_CTRL0, - hc->hw.a_st_ctrl0[hc->chan[ch].port]); - } - if (hc->chan[ch].bch) - test_and_set_bit(FLG_TRANSPARENT, - &hc->chan[ch].bch->Flags); - break; - case (ISDN_P_B_HDLC): /* B-channel */ - case (ISDN_P_TE_S0): /* D-channel */ - case (ISDN_P_NT_S0): - case (ISDN_P_TE_E1): - case (ISDN_P_NT_E1): - /* enable TX fifo */ - HFC_outb(hc, R_FIFO, ch << 1); - HFC_wait(hc); - if (hc->ctype == HFC_TYPE_E1 || hc->chan[ch].bch) { - /* E1 or B-channel */ - HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04); - HFC_outb(hc, A_SUBCH_CFG, 0); - } else { - /* D-Channel without HDLC fill flags */ - HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04 | V_IFF); - HFC_outb(hc, A_SUBCH_CFG, 2); - } - HFC_outb(hc, A_IRQ_MSK, V_IRQ); - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - /* enable RX fifo */ - HFC_outb(hc, R_FIFO, (ch << 1) | 1); - HFC_wait(hc); - HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04); - if (hc->ctype == HFC_TYPE_E1 || hc->chan[ch].bch) - HFC_outb(hc, A_SUBCH_CFG, 0); /* full 8 bits */ - else - HFC_outb(hc, A_SUBCH_CFG, 2); /* 2 bits dchannel */ - HFC_outb(hc, A_IRQ_MSK, V_IRQ); - HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait(hc); - if (hc->chan[ch].bch) { - test_and_set_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); - if (hc->ctype != HFC_TYPE_E1) { - hc->hw.a_st_ctrl0[hc->chan[ch].port] |= - ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN; - HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_CTRL0, - hc->hw.a_st_ctrl0[hc->chan[ch].port]); - } - } - break; - default: - printk(KERN_DEBUG "%s: protocol not known %x\n", - __func__, protocol); - hc->chan[ch].protocol = ISDN_P_NONE; - return -ENOPROTOOPT; - } - hc->chan[ch].protocol = protocol; - return 0; -} - - -/* - * connect/disconnect PCM - */ - -static void -hfcmulti_pcm(struct hfc_multi *hc, int ch, int slot_tx, int bank_tx, - int slot_rx, int bank_rx) -{ - if (slot_tx < 0 || slot_rx < 0 || bank_tx < 0 || bank_rx < 0) { - /* disable PCM */ - mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0, -1, 0); - return; - } - - /* enable pcm */ - mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot_tx, bank_tx, - slot_rx, bank_rx); -} - -/* - * set/disable conference - */ - -static void -hfcmulti_conf(struct hfc_multi *hc, int ch, int num) -{ - if (num >= 0 && num <= 7) - hc->chan[ch].conf = num; - else - hc->chan[ch].conf = -1; - mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot_tx, - hc->chan[ch].bank_tx, hc->chan[ch].slot_rx, - hc->chan[ch].bank_rx); -} - - -/* - * set/disable sample loop - */ - -/* NOTE: this function is experimental and therefore disabled */ - -/* - * Layer 1 callback function - */ -static int -hfcm_l1callback(struct dchannel *dch, u_int cmd) -{ - struct hfc_multi *hc = dch->hw; - struct sk_buff_head free_queue; - u_long flags; - - switch (cmd) { - case INFO3_P8: - case INFO3_P10: - break; - case HW_RESET_REQ: - /* start activation */ - spin_lock_irqsave(&hc->lock, flags); - if (hc->ctype == HFC_TYPE_E1) { - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: HW_RESET_REQ no BRI\n", - __func__); - } else { - HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* F3 */ - udelay(6); /* wait at least 5,21us */ - HFC_outb(hc, A_ST_WR_STATE, 3); - HFC_outb(hc, A_ST_WR_STATE, 3 | (V_ST_ACT * 3)); - /* activate */ - } - spin_unlock_irqrestore(&hc->lock, flags); - l1_event(dch->l1, HW_POWERUP_IND); - break; - case HW_DEACT_REQ: - __skb_queue_head_init(&free_queue); - /* start deactivation */ - spin_lock_irqsave(&hc->lock, flags); - if (hc->ctype == HFC_TYPE_E1) { - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: HW_DEACT_REQ no BRI\n", - __func__); - } else { - HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2); - /* deactivate */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - hc->syncronized &= - ~(1 << hc->chan[dch->slot].port); - plxsd_checksync(hc, 0); - } - } - skb_queue_splice_init(&dch->squeue, &free_queue); - if (dch->tx_skb) { - __skb_queue_tail(&free_queue, dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - __skb_queue_tail(&free_queue, dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); - spin_unlock_irqrestore(&hc->lock, flags); - __skb_queue_purge(&free_queue); - break; - case HW_POWERUP_REQ: - spin_lock_irqsave(&hc->lock, flags); - if (hc->ctype == HFC_TYPE_E1) { - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: HW_POWERUP_REQ no BRI\n", - __func__); - } else { - HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_WR_STATE, 3 | 0x10); /* activate */ - udelay(6); /* wait at least 5,21us */ - HFC_outb(hc, A_ST_WR_STATE, 3); /* activate */ - } - spin_unlock_irqrestore(&hc->lock, flags); - break; - case PH_ACTIVATE_IND: - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: unknown command %x\n", - __func__, cmd); - return -1; - } - return 0; -} - -/* - * Layer2 -> Layer 1 Transfer - */ - -static int -handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct hfc_multi *hc = dch->hw; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int ret = -EINVAL; - unsigned int id; - u_long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - if (skb->len < 1) - break; - spin_lock_irqsave(&hc->lock, flags); - ret = dchannel_senddata(dch, skb); - if (ret > 0) { /* direct TX */ - id = hh->id; /* skb can be freed */ - hfcmulti_tx(hc, dch->slot); - ret = 0; - /* start fifo */ - HFC_outb(hc, R_FIFO, 0); - HFC_wait(hc); - spin_unlock_irqrestore(&hc->lock, flags); - queue_ch_frame(ch, PH_DATA_CNF, id, NULL); - } else - spin_unlock_irqrestore(&hc->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - if (dch->dev.D.protocol != ISDN_P_TE_S0) { - spin_lock_irqsave(&hc->lock, flags); - ret = 0; - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: PH_ACTIVATE port %d (0..%d)\n", - __func__, hc->chan[dch->slot].port, - hc->ports - 1); - /* start activation */ - if (hc->ctype == HFC_TYPE_E1) { - ph_state_change(dch); - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: E1 report state %x \n", - __func__, dch->state); - } else { - HFC_outb(hc, R_ST_SEL, - hc->chan[dch->slot].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1); - /* G1 */ - udelay(6); /* wait at least 5,21us */ - HFC_outb(hc, A_ST_WR_STATE, 1); - HFC_outb(hc, A_ST_WR_STATE, 1 | - (V_ST_ACT * 3)); /* activate */ - dch->state = 1; - } - spin_unlock_irqrestore(&hc->lock, flags); - } else - ret = l1_event(dch->l1, hh->prim); - break; - case PH_DEACTIVATE_REQ: - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - if (dch->dev.D.protocol != ISDN_P_TE_S0) { - struct sk_buff_head free_queue; - - __skb_queue_head_init(&free_queue); - spin_lock_irqsave(&hc->lock, flags); - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: PH_DEACTIVATE port %d (0..%d)\n", - __func__, hc->chan[dch->slot].port, - hc->ports - 1); - /* start deactivation */ - if (hc->ctype == HFC_TYPE_E1) { - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: PH_DEACTIVATE no BRI\n", - __func__); - } else { - HFC_outb(hc, R_ST_SEL, - hc->chan[dch->slot].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2); - /* deactivate */ - dch->state = 1; - } - skb_queue_splice_init(&dch->squeue, &free_queue); - if (dch->tx_skb) { - __skb_queue_tail(&free_queue, dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - __skb_queue_tail(&free_queue, dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); -#ifdef FIXME - if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) - dchannel_sched_event(&hc->dch, D_CLEARBUSY); -#endif - ret = 0; - spin_unlock_irqrestore(&hc->lock, flags); - __skb_queue_purge(&free_queue); - } else - ret = l1_event(dch->l1, hh->prim); - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static void -deactivate_bchannel(struct bchannel *bch) -{ - struct hfc_multi *hc = bch->hw; - u_long flags; - - spin_lock_irqsave(&hc->lock, flags); - mISDN_clear_bchannel(bch); - hc->chan[bch->slot].coeff_count = 0; - hc->chan[bch->slot].rx_off = 0; - hc->chan[bch->slot].conf = -1; - mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0); - spin_unlock_irqrestore(&hc->lock, flags); -} - -static int -handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hfc_multi *hc = bch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - if (!skb->len) - break; - spin_lock_irqsave(&hc->lock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - hfcmulti_tx(hc, bch->slot); - ret = 0; - /* start fifo */ - HFC_outb_nodebug(hc, R_FIFO, 0); - HFC_wait_nodebug(hc); - } - spin_unlock_irqrestore(&hc->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", - __func__, bch->slot); - spin_lock_irqsave(&hc->lock, flags); - /* activate B-channel if not already activated */ - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { - hc->chan[bch->slot].txpending = 0; - ret = mode_hfcmulti(hc, bch->slot, - ch->protocol, - hc->chan[bch->slot].slot_tx, - hc->chan[bch->slot].bank_tx, - hc->chan[bch->slot].slot_rx, - hc->chan[bch->slot].bank_rx); - if (!ret) { - if (ch->protocol == ISDN_P_B_RAW && !hc->dtmf - && test_bit(HFC_CHIP_DTMF, &hc->chip)) { - /* start decoder */ - hc->dtmf = 1; - if (debug & DEBUG_HFCMULTI_DTMF) - printk(KERN_DEBUG - "%s: start dtmf decoder\n", - __func__); - HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | - V_RST_DTMF); - } - } - } else - ret = 0; - spin_unlock_irqrestore(&hc->lock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, NULL, - GFP_KERNEL); - break; - case PH_CONTROL_REQ: - spin_lock_irqsave(&hc->lock, flags); - switch (hh->id) { - case HFC_SPL_LOOP_ON: /* set sample loop */ - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: HFC_SPL_LOOP_ON (len = %d)\n", - __func__, skb->len); - ret = 0; - break; - case HFC_SPL_LOOP_OFF: /* set silence */ - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HFC_SPL_LOOP_OFF\n", - __func__); - ret = 0; - break; - default: - printk(KERN_ERR - "%s: unknown PH_CONTROL_REQ info %x\n", - __func__, hh->id); - ret = -EINVAL; - } - spin_unlock_irqrestore(&hc->lock, flags); - break; - case PH_DEACTIVATE_REQ: - deactivate_bchannel(bch); /* locked there */ - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL, - GFP_KERNEL); - ret = 0; - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -/* - * bchannel control function - */ -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - struct dsp_features *features = - (struct dsp_features *)(*((u_long *)&cq->p1)); - struct hfc_multi *hc = bch->hw; - int slot_tx; - int bank_tx; - int slot_rx; - int bank_rx; - int num; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - ret = mISDN_ctrl_bchannel(bch, cq); - cq->op |= MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP; - break; - case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */ - ret = mISDN_ctrl_bchannel(bch, cq); - hc->chan[bch->slot].rx_off = !!cq->p1; - if (!hc->chan[bch->slot].rx_off) { - /* reset fifo on rx on */ - HFC_outb_nodebug(hc, R_FIFO, (bch->slot << 1) | 1); - HFC_wait_nodebug(hc); - HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); - HFC_wait_nodebug(hc); - } - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n", - __func__, bch->nr, hc->chan[bch->slot].rx_off); - break; - case MISDN_CTRL_FILL_EMPTY: - ret = mISDN_ctrl_bchannel(bch, cq); - hc->silence = bch->fill[0]; - memset(hc->silence_data, hc->silence, sizeof(hc->silence_data)); - break; - case MISDN_CTRL_HW_FEATURES: /* fill features structure */ - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HW_FEATURE request\n", - __func__); - /* create confirm */ - features->hfc_id = hc->id; - if (test_bit(HFC_CHIP_DTMF, &hc->chip)) - features->hfc_dtmf = 1; - if (test_bit(HFC_CHIP_CONF, &hc->chip)) - features->hfc_conf = 1; - features->hfc_loops = 0; - if (test_bit(HFC_CHIP_B410P, &hc->chip)) { - features->hfc_echocanhw = 1; - } else { - features->pcm_id = hc->pcm; - features->pcm_slots = hc->slots; - features->pcm_banks = 2; - } - break; - case MISDN_CTRL_HFC_PCM_CONN: /* connect to pcm timeslot (0..N) */ - slot_tx = cq->p1 & 0xff; - bank_tx = cq->p1 >> 8; - slot_rx = cq->p2 & 0xff; - bank_rx = cq->p2 >> 8; - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG - "%s: HFC_PCM_CONN slot %d bank %d (TX) " - "slot %d bank %d (RX)\n", - __func__, slot_tx, bank_tx, - slot_rx, bank_rx); - if (slot_tx < hc->slots && bank_tx <= 2 && - slot_rx < hc->slots && bank_rx <= 2) - hfcmulti_pcm(hc, bch->slot, - slot_tx, bank_tx, slot_rx, bank_rx); - else { - printk(KERN_WARNING - "%s: HFC_PCM_CONN slot %d bank %d (TX) " - "slot %d bank %d (RX) out of range\n", - __func__, slot_tx, bank_tx, - slot_rx, bank_rx); - ret = -EINVAL; - } - break; - case MISDN_CTRL_HFC_PCM_DISC: /* release interface from pcm timeslot */ - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HFC_PCM_DISC\n", - __func__); - hfcmulti_pcm(hc, bch->slot, -1, 0, -1, 0); - break; - case MISDN_CTRL_HFC_CONF_JOIN: /* join conference (0..7) */ - num = cq->p1 & 0xff; - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HFC_CONF_JOIN conf %d\n", - __func__, num); - if (num <= 7) - hfcmulti_conf(hc, bch->slot, num); - else { - printk(KERN_WARNING - "%s: HW_CONF_JOIN conf %d out of range\n", - __func__, num); - ret = -EINVAL; - } - break; - case MISDN_CTRL_HFC_CONF_SPLIT: /* split conference */ - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HFC_CONF_SPLIT\n", __func__); - hfcmulti_conf(hc, bch->slot, -1); - break; - case MISDN_CTRL_HFC_ECHOCAN_ON: - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HFC_ECHOCAN_ON\n", __func__); - if (test_bit(HFC_CHIP_B410P, &hc->chip)) - vpm_echocan_on(hc, bch->slot, cq->p1); - else - ret = -EINVAL; - break; - - case MISDN_CTRL_HFC_ECHOCAN_OFF: - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: HFC_ECHOCAN_OFF\n", - __func__); - if (test_bit(HFC_CHIP_B410P, &hc->chip)) - vpm_echocan_off(hc, bch->slot); - else - ret = -EINVAL; - break; - default: - ret = mISDN_ctrl_bchannel(bch, cq); - break; - } - return ret; -} - -static int -hfcm_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hfc_multi *hc = bch->hw; - int err = -EINVAL; - u_long flags; - - if (bch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", - __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - deactivate_bchannel(bch); /* locked there */ - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - err = 0; - break; - case CONTROL_CHANNEL: - spin_lock_irqsave(&hc->lock, flags); - err = channel_bctrl(bch, arg); - spin_unlock_irqrestore(&hc->lock, flags); - break; - default: - printk(KERN_WARNING "%s: unknown prim(%x)\n", - __func__, cmd); - } - return err; -} - -/* - * handle D-channel events - * - * handle state change event - */ -static void -ph_state_change(struct dchannel *dch) -{ - struct hfc_multi *hc; - int ch, i; - - if (!dch) { - printk(KERN_WARNING "%s: ERROR given dch is NULL\n", __func__); - return; - } - hc = dch->hw; - ch = dch->slot; - - if (hc->ctype == HFC_TYPE_E1) { - if (dch->dev.D.protocol == ISDN_P_TE_E1) { - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: E1 TE (id=%d) newstate %x\n", - __func__, hc->id, dch->state); - } else { - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: E1 NT (id=%d) newstate %x\n", - __func__, hc->id, dch->state); - } - switch (dch->state) { - case (1): - if (hc->e1_state != 1) { - for (i = 1; i <= 31; i++) { - /* reset fifos on e1 activation */ - HFC_outb_nodebug(hc, R_FIFO, - (i << 1) | 1); - HFC_wait_nodebug(hc); - HFC_outb_nodebug(hc, R_INC_RES_FIFO, - V_RES_F); - HFC_wait_nodebug(hc); - } - } - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - break; - - default: - if (hc->e1_state != 1) - return; - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - } - hc->e1_state = dch->state; - } else { - if (dch->dev.D.protocol == ISDN_P_TE_S0) { - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG - "%s: S/T TE newstate %x\n", - __func__, dch->state); - switch (dch->state) { - case (0): - l1_event(dch->l1, HW_RESET_IND); - break; - case (3): - l1_event(dch->l1, HW_DEACT_IND); - break; - case (5): - case (8): - l1_event(dch->l1, ANYSIGNAL); - break; - case (6): - l1_event(dch->l1, INFO2); - break; - case (7): - l1_event(dch->l1, INFO4_P8); - break; - } - } else { - if (debug & DEBUG_HFCMULTI_STATE) - printk(KERN_DEBUG "%s: S/T NT newstate %x\n", - __func__, dch->state); - switch (dch->state) { - case (2): - if (hc->chan[ch].nt_timer == 0) { - hc->chan[ch].nt_timer = -1; - HFC_outb(hc, R_ST_SEL, - hc->chan[ch].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - HFC_outb(hc, A_ST_WR_STATE, 4 | - V_ST_LD_STA); /* G4 */ - udelay(6); /* wait at least 5,21us */ - HFC_outb(hc, A_ST_WR_STATE, 4); - dch->state = 4; - } else { - /* one extra count for the next event */ - hc->chan[ch].nt_timer = - nt_t1_count[poll_timer] + 1; - HFC_outb(hc, R_ST_SEL, - hc->chan[ch].port); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - /* allow G2 -> G3 transition */ - HFC_outb(hc, A_ST_WR_STATE, 2 | - V_SET_G2_G3); - } - break; - case (1): - hc->chan[ch].nt_timer = -1; - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - break; - case (4): - hc->chan[ch].nt_timer = -1; - break; - case (3): - hc->chan[ch].nt_timer = -1; - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - break; - } - } - } -} - -/* - * called for card mode init message - */ - -static void -hfcmulti_initmode(struct dchannel *dch) -{ - struct hfc_multi *hc = dch->hw; - u_char a_st_wr_state, r_e1_wr_sta; - int i, pt; - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: entered\n", __func__); - - i = dch->slot; - pt = hc->chan[i].port; - if (hc->ctype == HFC_TYPE_E1) { - /* E1 */ - hc->chan[hc->dnum[pt]].slot_tx = -1; - hc->chan[hc->dnum[pt]].slot_rx = -1; - hc->chan[hc->dnum[pt]].conf = -1; - if (hc->dnum[pt]) { - mode_hfcmulti(hc, dch->slot, dch->dev.D.protocol, - -1, 0, -1, 0); - timer_setup(&dch->timer, hfcmulti_dbusy_timer, 0); - } - for (i = 1; i <= 31; i++) { - if (!((1 << i) & hc->bmask[pt])) /* skip unused chan */ - continue; - hc->chan[i].slot_tx = -1; - hc->chan[i].slot_rx = -1; - hc->chan[i].conf = -1; - mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0); - } - } - if (hc->ctype == HFC_TYPE_E1 && pt == 0) { - /* E1, port 0 */ - dch = hc->chan[hc->dnum[0]].dch; - if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dnum[0]].cfg)) { - HFC_outb(hc, R_LOS0, 255); /* 2 ms */ - HFC_outb(hc, R_LOS1, 255); /* 512 ms */ - } - if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dnum[0]].cfg)) { - HFC_outb(hc, R_RX0, 0); - hc->hw.r_tx0 = 0 | V_OUT_EN; - } else { - HFC_outb(hc, R_RX0, 1); - hc->hw.r_tx0 = 1 | V_OUT_EN; - } - hc->hw.r_tx1 = V_ATX | V_NTRI; - HFC_outb(hc, R_TX0, hc->hw.r_tx0); - HFC_outb(hc, R_TX1, hc->hw.r_tx1); - HFC_outb(hc, R_TX_FR0, 0x00); - HFC_outb(hc, R_TX_FR1, 0xf8); - - if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dnum[0]].cfg)) - HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E); - - HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0); - - if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dnum[0]].cfg)) - HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC); - - if (dch->dev.D.protocol == ISDN_P_NT_E1) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: E1 port is NT-mode\n", - __func__); - r_e1_wr_sta = 0; /* G0 */ - hc->e1_getclock = 0; - } else { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: E1 port is TE-mode\n", - __func__); - r_e1_wr_sta = 0; /* F0 */ - hc->e1_getclock = 1; - } - if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) - HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); - else - HFC_outb(hc, R_SYNC_OUT, 0); - if (test_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip)) - hc->e1_getclock = 1; - if (test_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip)) - hc->e1_getclock = 0; - if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { - /* SLAVE (clock master) */ - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: E1 port is clock master " - "(clock from PCM)\n", __func__); - HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC); - } else { - if (hc->e1_getclock) { - /* MASTER (clock slave) */ - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: E1 port is clock slave " - "(clock to PCM)\n", __func__); - HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); - } else { - /* MASTER (clock master) */ - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: E1 port is " - "clock master " - "(clock from QUARTZ)\n", - __func__); - HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | - V_PCM_SYNC | V_JATT_OFF); - HFC_outb(hc, R_SYNC_OUT, 0); - } - } - HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */ - HFC_outb(hc, R_PWM_MD, V_PWM0_MD); - HFC_outb(hc, R_PWM0, 0x50); - HFC_outb(hc, R_PWM1, 0xff); - /* state machine setup */ - HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA); - udelay(6); /* wait at least 5,21us */ - HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta); - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - hc->syncronized = 0; - plxsd_checksync(hc, 0); - } - } - if (hc->ctype != HFC_TYPE_E1) { - /* ST */ - hc->chan[i].slot_tx = -1; - hc->chan[i].slot_rx = -1; - hc->chan[i].conf = -1; - mode_hfcmulti(hc, i, dch->dev.D.protocol, -1, 0, -1, 0); - timer_setup(&dch->timer, hfcmulti_dbusy_timer, 0); - hc->chan[i - 2].slot_tx = -1; - hc->chan[i - 2].slot_rx = -1; - hc->chan[i - 2].conf = -1; - mode_hfcmulti(hc, i - 2, ISDN_P_NONE, -1, 0, -1, 0); - hc->chan[i - 1].slot_tx = -1; - hc->chan[i - 1].slot_rx = -1; - hc->chan[i - 1].conf = -1; - mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0); - /* select interface */ - HFC_outb(hc, R_ST_SEL, pt); - /* undocumented: delay after R_ST_SEL */ - udelay(1); - if (dch->dev.D.protocol == ISDN_P_NT_S0) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: ST port %d is NT-mode\n", - __func__, pt); - /* clock delay */ - HFC_outb(hc, A_ST_CLK_DLY, clockdelay_nt); - a_st_wr_state = 1; /* G1 */ - hc->hw.a_st_ctrl0[pt] = V_ST_MD; - } else { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: ST port %d is TE-mode\n", - __func__, pt); - /* clock delay */ - HFC_outb(hc, A_ST_CLK_DLY, clockdelay_te); - a_st_wr_state = 2; /* F2 */ - hc->hw.a_st_ctrl0[pt] = 0; - } - if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg)) - hc->hw.a_st_ctrl0[pt] |= V_TX_LI; - if (hc->ctype == HFC_TYPE_XHFC) { - hc->hw.a_st_ctrl0[pt] |= 0x40 /* V_ST_PU_CTRL */; - HFC_outb(hc, 0x35 /* A_ST_CTRL3 */, - 0x7c << 1 /* V_ST_PULSE */); - } - /* line setup */ - HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[pt]); - /* disable E-channel */ - if ((dch->dev.D.protocol == ISDN_P_NT_S0) || - test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg)) - HFC_outb(hc, A_ST_CTRL1, V_E_IGNO); - else - HFC_outb(hc, A_ST_CTRL1, 0); - /* enable B-channel receive */ - HFC_outb(hc, A_ST_CTRL2, V_B1_RX_EN | V_B2_RX_EN); - /* state machine setup */ - HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA); - udelay(6); /* wait at least 5,21us */ - HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state); - hc->hw.r_sci_msk |= 1 << pt; - /* state machine interrupts */ - HFC_outb(hc, R_SCI_MSK, hc->hw.r_sci_msk); - /* unset sync on port */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - hc->syncronized &= - ~(1 << hc->chan[dch->slot].port); - plxsd_checksync(hc, 0); - } - } - if (debug & DEBUG_HFCMULTI_INIT) - printk("%s: done\n", __func__); -} - - -static int -open_dchannel(struct hfc_multi *hc, struct dchannel *dch, - struct channel_req *rq) -{ - int err = 0; - u_long flags; - - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, - dch->dev.id, __builtin_return_address(0)); - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - if ((dch->dev.D.protocol != ISDN_P_NONE) && - (dch->dev.D.protocol != rq->protocol)) { - if (debug & DEBUG_HFCMULTI_MODE) - printk(KERN_DEBUG "%s: change protocol %x to %x\n", - __func__, dch->dev.D.protocol, rq->protocol); - } - if ((dch->dev.D.protocol == ISDN_P_TE_S0) && - (rq->protocol != ISDN_P_TE_S0)) - l1_event(dch->l1, CLOSE_CHANNEL); - if (dch->dev.D.protocol != rq->protocol) { - if (rq->protocol == ISDN_P_TE_S0) { - err = create_l1(dch, hfcm_l1callback); - if (err) - return err; - } - dch->dev.D.protocol = rq->protocol; - spin_lock_irqsave(&hc->lock, flags); - hfcmulti_initmode(dch); - spin_unlock_irqrestore(&hc->lock, flags); - } - if (test_bit(FLG_ACTIVE, &dch->Flags)) - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - rq->ch = &dch->dev.D; - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", __func__); - return 0; -} - -static int -open_bchannel(struct hfc_multi *hc, struct dchannel *dch, - struct channel_req *rq) -{ - struct bchannel *bch; - int ch; - - if (!test_channelmap(rq->adr.channel, dch->dev.channelmap)) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - if (hc->ctype == HFC_TYPE_E1) - ch = rq->adr.channel; - else - ch = (rq->adr.channel - 1) + (dch->slot - 2); - bch = hc->chan[ch].bch; - if (!bch) { - printk(KERN_ERR "%s:internal error ch %d has no bch\n", - __func__, ch); - return -EINVAL; - } - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - hc->chan[ch].rx_off = 0; - rq->ch = &bch->ch; - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", __func__); - return 0; -} - -/* - * device control function - */ -static int -channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) -{ - struct hfc_multi *hc = dch->hw; - int ret = 0; - int wd_mode, wd_cnt; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_HFC_WD_INIT: /* init the watchdog */ - wd_cnt = cq->p1 & 0xf; - wd_mode = !!(cq->p1 >> 4); - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: MISDN_CTRL_HFC_WD_INIT mode %s" - ", counter 0x%x\n", __func__, - wd_mode ? "AUTO" : "MANUAL", wd_cnt); - /* set the watchdog timer */ - HFC_outb(hc, R_TI_WD, poll_timer | (wd_cnt << 4)); - hc->hw.r_bert_wd_md = (wd_mode ? V_AUTO_WD_RES : 0); - if (hc->ctype == HFC_TYPE_XHFC) - hc->hw.r_bert_wd_md |= 0x40 /* V_WD_EN */; - /* init the watchdog register and reset the counter */ - HFC_outb(hc, R_BERT_WD_MD, hc->hw.r_bert_wd_md | V_WD_RES); - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - /* enable the watchdog output for Speech-Design */ - HFC_outb(hc, R_GPIO_SEL, V_GPIO_SEL7); - HFC_outb(hc, R_GPIO_EN1, V_GPIO_EN15); - HFC_outb(hc, R_GPIO_OUT1, 0); - HFC_outb(hc, R_GPIO_OUT1, V_GPIO_OUT15); - } - break; - case MISDN_CTRL_HFC_WD_RESET: /* reset the watchdog counter */ - if (debug & DEBUG_HFCMULTI_MSG) - printk(KERN_DEBUG "%s: MISDN_CTRL_HFC_WD_RESET\n", - __func__); - HFC_outb(hc, R_BERT_WD_MD, hc->hw.r_bert_wd_md | V_WD_RES); - break; - case MISDN_CTRL_L1_TIMER3: - ret = l1_event(dch->l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); - break; - default: - printk(KERN_WARNING "%s: unknown Op %x\n", - __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct hfc_multi *hc = dch->hw; - struct channel_req *rq; - int err = 0; - u_long flags; - - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", - __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - switch (rq->protocol) { - case ISDN_P_TE_S0: - case ISDN_P_NT_S0: - if (hc->ctype == HFC_TYPE_E1) { - err = -EINVAL; - break; - } - err = open_dchannel(hc, dch, rq); /* locked there */ - break; - case ISDN_P_TE_E1: - case ISDN_P_NT_E1: - if (hc->ctype != HFC_TYPE_E1) { - err = -EINVAL; - break; - } - err = open_dchannel(hc, dch, rq); /* locked there */ - break; - default: - spin_lock_irqsave(&hc->lock, flags); - err = open_bchannel(hc, dch, rq); - spin_unlock_irqrestore(&hc->lock, flags); - } - break; - case CLOSE_CHANNEL: - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: dev(%d) close from %p\n", - __func__, dch->dev.id, - __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - spin_lock_irqsave(&hc->lock, flags); - err = channel_dctrl(dch, arg); - spin_unlock_irqrestore(&hc->lock, flags); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: unknown command %x\n", - __func__, cmd); - err = -EINVAL; - } - return err; -} - -static int -clockctl(void *priv, int enable) -{ - struct hfc_multi *hc = priv; - - hc->iclock_on = enable; - return 0; -} - -/* - * initialize the card - */ - -/* - * start timer irq, wait some time and check if we have interrupts. - * if not, reset chip and try again. - */ -static int -init_card(struct hfc_multi *hc) -{ - int err = -EIO; - u_long flags; - void __iomem *plx_acc; - u_long plx_flags; - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: entered\n", __func__); - - spin_lock_irqsave(&hc->lock, flags); - /* set interrupts but leave global interrupt disabled */ - hc->hw.r_irq_ctrl = V_FIFO_IRQ; - disable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - - if (request_irq(hc->irq, hfcmulti_interrupt, IRQF_SHARED, - "HFC-multi", hc)) { - printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n", - hc->irq); - hc->irq = 0; - return -EIO; - } - - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc = hc->plx_membase + PLX_INTCSR; - writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE), - plx_acc); /* enable PCI & LINT1 irq */ - spin_unlock_irqrestore(&plx_lock, plx_flags); - } - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: IRQ %d count %d\n", - __func__, hc->irq, hc->irqcnt); - err = init_chip(hc); - if (err) - goto error; - /* - * Finally enable IRQ output - * this is only allowed, if an IRQ routine is already - * established for this HFC, so don't do that earlier - */ - spin_lock_irqsave(&hc->lock, flags); - enable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - /* printk(KERN_DEBUG "no master irq set!!!\n"); */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ - /* turn IRQ off until chip is completely initialized */ - spin_lock_irqsave(&hc->lock, flags); - disable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: IRQ %d count %d\n", - __func__, hc->irq, hc->irqcnt); - if (hc->irqcnt) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: done\n", __func__); - - return 0; - } - if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { - printk(KERN_INFO "ignoring missing interrupts\n"); - return 0; - } - - printk(KERN_ERR "HFC PCI: IRQ(%d) getting no interrupts during init.\n", - hc->irq); - - err = -EIO; - -error: - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - spin_lock_irqsave(&plx_lock, plx_flags); - plx_acc = hc->plx_membase + PLX_INTCSR; - writew(0x00, plx_acc); /*disable IRQs*/ - spin_unlock_irqrestore(&plx_lock, plx_flags); - } - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: free irq %d\n", __func__, hc->irq); - if (hc->irq) { - free_irq(hc->irq, hc); - hc->irq = 0; - } - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: done (err=%d)\n", __func__, err); - return err; -} - -/* - * find pci device and set it up - */ - -static int -setup_pci(struct hfc_multi *hc, struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct hm_map *m = (struct hm_map *)ent->driver_data; - - printk(KERN_INFO - "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", - m->vendor_name, m->card_name, m->clock2 ? "double" : "normal"); - - hc->pci_dev = pdev; - if (m->clock2) - test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); - - if (ent->vendor == PCI_VENDOR_ID_DIGIUM && - ent->device == PCI_DEVICE_ID_DIGIUM_HFC4S) { - test_and_set_bit(HFC_CHIP_B410P, &hc->chip); - test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); - test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); - hc->slots = 32; - } - - if (hc->pci_dev->irq <= 0) { - printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n"); - return -EIO; - } - if (pci_enable_device(hc->pci_dev)) { - printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n"); - return -EIO; - } - hc->leds = m->leds; - hc->ledstate = 0xAFFEAFFE; - hc->opticalsupport = m->opticalsupport; - - hc->pci_iobase = 0; - hc->pci_membase = NULL; - hc->plx_membase = NULL; - - /* set memory access methods */ - if (m->io_mode) /* use mode from card config */ - hc->io_mode = m->io_mode; - switch (hc->io_mode) { - case HFC_IO_MODE_PLXSD: - test_and_set_bit(HFC_CHIP_PLXSD, &hc->chip); - hc->slots = 128; /* required */ - hc->HFC_outb = HFC_outb_pcimem; - hc->HFC_inb = HFC_inb_pcimem; - hc->HFC_inw = HFC_inw_pcimem; - hc->HFC_wait = HFC_wait_pcimem; - hc->read_fifo = read_fifo_pcimem; - hc->write_fifo = write_fifo_pcimem; - hc->plx_origmembase = hc->pci_dev->resource[0].start; - /* MEMBASE 1 is PLX PCI Bridge */ - - if (!hc->plx_origmembase) { - printk(KERN_WARNING - "HFC-multi: No IO-Memory for PCI PLX bridge found\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - hc->plx_membase = ioremap(hc->plx_origmembase, 0x80); - if (!hc->plx_membase) { - printk(KERN_WARNING - "HFC-multi: failed to remap plx address space. " - "(internal error)\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - printk(KERN_INFO - "HFC-multi: plx_membase:%#lx plx_origmembase:%#lx\n", - (u_long)hc->plx_membase, hc->plx_origmembase); - - hc->pci_origmembase = hc->pci_dev->resource[2].start; - /* MEMBASE 1 is PLX PCI Bridge */ - if (!hc->pci_origmembase) { - printk(KERN_WARNING - "HFC-multi: No IO-Memory for PCI card found\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - hc->pci_membase = ioremap(hc->pci_origmembase, 0x400); - if (!hc->pci_membase) { - printk(KERN_WARNING "HFC-multi: failed to remap io " - "address space. (internal error)\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - printk(KERN_INFO - "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d HZ %d " - "leds-type %d\n", - hc->id, (u_long)hc->pci_membase, hc->pci_origmembase, - hc->pci_dev->irq, HZ, hc->leds); - pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); - break; - case HFC_IO_MODE_PCIMEM: - hc->HFC_outb = HFC_outb_pcimem; - hc->HFC_inb = HFC_inb_pcimem; - hc->HFC_inw = HFC_inw_pcimem; - hc->HFC_wait = HFC_wait_pcimem; - hc->read_fifo = read_fifo_pcimem; - hc->write_fifo = write_fifo_pcimem; - hc->pci_origmembase = hc->pci_dev->resource[1].start; - if (!hc->pci_origmembase) { - printk(KERN_WARNING - "HFC-multi: No IO-Memory for PCI card found\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - hc->pci_membase = ioremap(hc->pci_origmembase, 256); - if (!hc->pci_membase) { - printk(KERN_WARNING - "HFC-multi: failed to remap io address space. " - "(internal error)\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - printk(KERN_INFO "card %d: defined at MEMBASE %#lx (%#lx) IRQ " - "%d HZ %d leds-type %d\n", hc->id, (u_long)hc->pci_membase, - hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds); - pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); - break; - case HFC_IO_MODE_REGIO: - hc->HFC_outb = HFC_outb_regio; - hc->HFC_inb = HFC_inb_regio; - hc->HFC_inw = HFC_inw_regio; - hc->HFC_wait = HFC_wait_regio; - hc->read_fifo = read_fifo_regio; - hc->write_fifo = write_fifo_regio; - hc->pci_iobase = (u_int) hc->pci_dev->resource[0].start; - if (!hc->pci_iobase) { - printk(KERN_WARNING - "HFC-multi: No IO for PCI card found\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - if (!request_region(hc->pci_iobase, 8, "hfcmulti")) { - printk(KERN_WARNING "HFC-multi: failed to request " - "address space at 0x%08lx (internal error)\n", - hc->pci_iobase); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - printk(KERN_INFO - "%s %s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n", - m->vendor_name, m->card_name, (u_int) hc->pci_iobase, - hc->pci_dev->irq, HZ, hc->leds); - pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_REGIO); - break; - default: - printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); - pci_disable_device(hc->pci_dev); - return -EIO; - } - - pci_set_drvdata(hc->pci_dev, hc); - - /* At this point the needed PCI config is done */ - /* fifos are still not enabled */ - return 0; -} - - -/* - * remove port - */ - -static void -release_port(struct hfc_multi *hc, struct dchannel *dch) -{ - int pt, ci, i = 0; - u_long flags; - struct bchannel *pb; - - ci = dch->slot; - pt = hc->chan[ci].port; - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: entered for port %d\n", - __func__, pt + 1); - - if (pt >= hc->ports) { - printk(KERN_WARNING "%s: ERROR port out of range (%d).\n", - __func__, pt + 1); - return; - } - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: releasing port=%d\n", - __func__, pt + 1); - - if (dch->dev.D.protocol == ISDN_P_TE_S0) - l1_event(dch->l1, CLOSE_CHANNEL); - - hc->chan[ci].dch = NULL; - - if (hc->created[pt]) { - hc->created[pt] = 0; - mISDN_unregister_device(&dch->dev); - } - - spin_lock_irqsave(&hc->lock, flags); - - if (dch->timer.function) { - timer_delete(&dch->timer); - dch->timer.function = NULL; - } - - if (hc->ctype == HFC_TYPE_E1) { /* E1 */ - /* remove sync */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - hc->syncronized = 0; - plxsd_checksync(hc, 1); - } - /* free channels */ - for (i = 0; i <= 31; i++) { - if (!((1 << i) & hc->bmask[pt])) /* skip unused chan */ - continue; - if (hc->chan[i].bch) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: free port %d channel %d\n", - __func__, hc->chan[i].port + 1, i); - pb = hc->chan[i].bch; - hc->chan[i].bch = NULL; - spin_unlock_irqrestore(&hc->lock, flags); - mISDN_freebchannel(pb); - kfree(pb); - kfree(hc->chan[i].coeff); - spin_lock_irqsave(&hc->lock, flags); - } - } - } else { - /* remove sync */ - if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { - hc->syncronized &= - ~(1 << hc->chan[ci].port); - plxsd_checksync(hc, 1); - } - /* free channels */ - if (hc->chan[ci - 2].bch) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: free port %d channel %d\n", - __func__, hc->chan[ci - 2].port + 1, - ci - 2); - pb = hc->chan[ci - 2].bch; - hc->chan[ci - 2].bch = NULL; - spin_unlock_irqrestore(&hc->lock, flags); - mISDN_freebchannel(pb); - kfree(pb); - kfree(hc->chan[ci - 2].coeff); - spin_lock_irqsave(&hc->lock, flags); - } - if (hc->chan[ci - 1].bch) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: free port %d channel %d\n", - __func__, hc->chan[ci - 1].port + 1, - ci - 1); - pb = hc->chan[ci - 1].bch; - hc->chan[ci - 1].bch = NULL; - spin_unlock_irqrestore(&hc->lock, flags); - mISDN_freebchannel(pb); - kfree(pb); - kfree(hc->chan[ci - 1].coeff); - spin_lock_irqsave(&hc->lock, flags); - } - } - - spin_unlock_irqrestore(&hc->lock, flags); - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: free port %d channel D(%d)\n", __func__, - pt+1, ci); - mISDN_freedchannel(dch); - kfree(dch); - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: done!\n", __func__); -} - -static void -release_card(struct hfc_multi *hc) -{ - u_long flags; - int ch; - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: release card (%d) entered\n", - __func__, hc->id); - - /* unregister clock source */ - if (hc->iclock) - mISDN_unregister_clock(hc->iclock); - - /* disable and free irq */ - spin_lock_irqsave(&hc->lock, flags); - disable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - udelay(1000); - if (hc->irq) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: free irq %d (hc=%p)\n", - __func__, hc->irq, hc); - free_irq(hc->irq, hc); - hc->irq = 0; - - } - - /* disable D-channels & B-channels */ - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: disable all channels (d and b)\n", - __func__); - for (ch = 0; ch <= 31; ch++) { - if (hc->chan[ch].dch) - release_port(hc, hc->chan[ch].dch); - } - - /* dimm leds */ - if (hc->leds) - hfcmulti_leds(hc); - - /* release hardware */ - release_io_hfcmulti(hc); - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: remove instance from list\n", - __func__); - list_del(&hc->list); - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: delete instance\n", __func__); - if (hc == syncmaster) - syncmaster = NULL; - kfree(hc); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: card successfully removed\n", - __func__); -} - -static void -init_e1_port_hw(struct hfc_multi *hc, struct hm_map *m) -{ - /* set optical line type */ - if (port[Port_cnt] & 0x001) { - if (!m->opticalsupport) { - printk(KERN_INFO - "This board has no optical " - "support\n"); - } else { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PORT set optical " - "interface: card(%d) " - "port(%d)\n", - __func__, - HFC_cnt + 1, 1); - test_and_set_bit(HFC_CFG_OPTICAL, - &hc->chan[hc->dnum[0]].cfg); - } - } - /* set LOS report */ - if (port[Port_cnt] & 0x004) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT set " - "LOS report: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CFG_REPORT_LOS, - &hc->chan[hc->dnum[0]].cfg); - } - /* set AIS report */ - if (port[Port_cnt] & 0x008) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT set " - "AIS report: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CFG_REPORT_AIS, - &hc->chan[hc->dnum[0]].cfg); - } - /* set SLIP report */ - if (port[Port_cnt] & 0x010) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PORT set SLIP report: " - "card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CFG_REPORT_SLIP, - &hc->chan[hc->dnum[0]].cfg); - } - /* set RDI report */ - if (port[Port_cnt] & 0x020) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PORT set RDI report: " - "card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CFG_REPORT_RDI, - &hc->chan[hc->dnum[0]].cfg); - } - /* set CRC-4 Mode */ - if (!(port[Port_cnt] & 0x100)) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT turn on CRC4 report:" - " card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CFG_CRC4, - &hc->chan[hc->dnum[0]].cfg); - } else { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT turn off CRC4" - " report: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - } - /* set forced clock */ - if (port[Port_cnt] & 0x0200) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT force getting clock from " - "E1: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip); - } else - if (port[Port_cnt] & 0x0400) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT force putting clock to " - "E1: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip); - } - /* set JATT PLL */ - if (port[Port_cnt] & 0x0800) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: PORT disable JATT PLL on " - "E1: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, 1); - test_and_set_bit(HFC_CHIP_RX_SYNC, &hc->chip); - } - /* set elastic jitter buffer */ - if (port[Port_cnt] & 0x3000) { - hc->chan[hc->dnum[0]].jitter = (port[Port_cnt]>>12) & 0x3; - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PORT set elastic " - "buffer to %d: card(%d) port(%d)\n", - __func__, hc->chan[hc->dnum[0]].jitter, - HFC_cnt + 1, 1); - } else - hc->chan[hc->dnum[0]].jitter = 2; /* default */ -} - -static int -init_e1_port(struct hfc_multi *hc, struct hm_map *m, int pt) -{ - struct dchannel *dch; - struct bchannel *bch; - int ch, ret = 0; - char name[MISDN_MAX_IDLEN]; - int bcount = 0; - - dch = kzalloc_obj(struct dchannel); - if (!dch) - return -ENOMEM; - dch->debug = debug; - mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); - dch->hw = hc; - dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1); - dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - dch->dev.D.send = handle_dmsg; - dch->dev.D.ctrl = hfcm_dctrl; - dch->slot = hc->dnum[pt]; - hc->chan[hc->dnum[pt]].dch = dch; - hc->chan[hc->dnum[pt]].port = pt; - hc->chan[hc->dnum[pt]].nt_timer = -1; - for (ch = 1; ch <= 31; ch++) { - if (!((1 << ch) & hc->bmask[pt])) /* skip unused channel */ - continue; - bch = kzalloc_obj(struct bchannel); - if (!bch) { - printk(KERN_ERR "%s: no memory for bchannel\n", - __func__); - ret = -ENOMEM; - goto free_chan; - } - hc->chan[ch].coeff = kzalloc(512, GFP_KERNEL); - if (!hc->chan[ch].coeff) { - printk(KERN_ERR "%s: no memory for coeffs\n", - __func__); - ret = -ENOMEM; - kfree(bch); - goto free_chan; - } - bch->nr = ch; - bch->slot = ch; - bch->debug = debug; - mISDN_initbchannel(bch, MAX_DATA_MEM, poll >> 1); - bch->hw = hc; - bch->ch.send = handle_bmsg; - bch->ch.ctrl = hfcm_bctrl; - bch->ch.nr = ch; - list_add(&bch->ch.list, &dch->dev.bchannels); - hc->chan[ch].bch = bch; - hc->chan[ch].port = pt; - set_channelmap(bch->nr, dch->dev.channelmap); - bcount++; - } - dch->dev.nrbchan = bcount; - if (pt == 0) - init_e1_port_hw(hc, m); - if (hc->ports > 1) - snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d-%d", - HFC_cnt + 1, pt+1); - else - snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); - ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); - if (ret) - goto free_chan; - hc->created[pt] = 1; - return ret; -free_chan: - release_port(hc, dch); - return ret; -} - -static int -init_multi_port(struct hfc_multi *hc, int pt) -{ - struct dchannel *dch; - struct bchannel *bch; - int ch, i, ret = 0; - char name[MISDN_MAX_IDLEN]; - - dch = kzalloc_obj(struct dchannel); - if (!dch) - return -ENOMEM; - dch->debug = debug; - mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); - dch->hw = hc; - dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); - dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - dch->dev.D.send = handle_dmsg; - dch->dev.D.ctrl = hfcm_dctrl; - dch->dev.nrbchan = 2; - i = pt << 2; - dch->slot = i + 2; - hc->chan[i + 2].dch = dch; - hc->chan[i + 2].port = pt; - hc->chan[i + 2].nt_timer = -1; - for (ch = 0; ch < dch->dev.nrbchan; ch++) { - bch = kzalloc_obj(struct bchannel); - if (!bch) { - printk(KERN_ERR "%s: no memory for bchannel\n", - __func__); - ret = -ENOMEM; - goto free_chan; - } - hc->chan[i + ch].coeff = kzalloc(512, GFP_KERNEL); - if (!hc->chan[i + ch].coeff) { - printk(KERN_ERR "%s: no memory for coeffs\n", - __func__); - ret = -ENOMEM; - kfree(bch); - goto free_chan; - } - bch->nr = ch + 1; - bch->slot = i + ch; - bch->debug = debug; - mISDN_initbchannel(bch, MAX_DATA_MEM, poll >> 1); - bch->hw = hc; - bch->ch.send = handle_bmsg; - bch->ch.ctrl = hfcm_bctrl; - bch->ch.nr = ch + 1; - list_add(&bch->ch.list, &dch->dev.bchannels); - hc->chan[i + ch].bch = bch; - hc->chan[i + ch].port = pt; - set_channelmap(bch->nr, dch->dev.channelmap); - } - /* set master clock */ - if (port[Port_cnt] & 0x001) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PROTOCOL set master clock: " - "card(%d) port(%d)\n", - __func__, HFC_cnt + 1, pt + 1); - if (dch->dev.D.protocol != ISDN_P_TE_S0) { - printk(KERN_ERR "Error: Master clock " - "for port(%d) of card(%d) is only" - " possible with TE-mode\n", - pt + 1, HFC_cnt + 1); - ret = -EINVAL; - goto free_chan; - } - if (hc->masterclk >= 0) { - printk(KERN_ERR "Error: Master clock " - "for port(%d) of card(%d) already " - "defined for port(%d)\n", - pt + 1, HFC_cnt + 1, hc->masterclk + 1); - ret = -EINVAL; - goto free_chan; - } - hc->masterclk = pt; - } - /* set transmitter line to non capacitive */ - if (port[Port_cnt] & 0x002) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PROTOCOL set non capacitive " - "transmitter: card(%d) port(%d)\n", - __func__, HFC_cnt + 1, pt + 1); - test_and_set_bit(HFC_CFG_NONCAP_TX, - &hc->chan[i + 2].cfg); - } - /* disable E-channel */ - if (port[Port_cnt] & 0x004) { - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: PROTOCOL disable E-channel: " - "card(%d) port(%d)\n", - __func__, HFC_cnt + 1, pt + 1); - test_and_set_bit(HFC_CFG_DIS_ECHANNEL, - &hc->chan[i + 2].cfg); - } - if (hc->ctype == HFC_TYPE_XHFC) { - snprintf(name, MISDN_MAX_IDLEN - 1, "xhfc.%d-%d", - HFC_cnt + 1, pt + 1); - ret = mISDN_register_device(&dch->dev, NULL, name); - } else { - snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d-%d", - hc->ctype, HFC_cnt + 1, pt + 1); - ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); - } - if (ret) - goto free_chan; - hc->created[pt] = 1; - return ret; -free_chan: - release_port(hc, dch); - return ret; -} - -static int -hfcmulti_init(struct hm_map *m, struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int ret_err = 0; - int pt; - struct hfc_multi *hc; - u_long flags; - u_char dips = 0, pmj = 0; /* dip settings, port mode Jumpers */ - int i, ch; - u_int maskcheck; - - if (HFC_cnt >= MAX_CARDS) { - printk(KERN_ERR "too many cards (max=%d).\n", - MAX_CARDS); - return -EINVAL; - } - if ((type[HFC_cnt] & 0xff) && (type[HFC_cnt] & 0xff) != m->type) { - printk(KERN_WARNING "HFC-MULTI: Card '%s:%s' type %d found but " - "type[%d] %d was supplied as module parameter\n", - m->vendor_name, m->card_name, m->type, HFC_cnt, - type[HFC_cnt] & 0xff); - printk(KERN_WARNING "HFC-MULTI: Load module without parameters " - "first, to see cards and their types."); - return -EINVAL; - } - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: Registering %s:%s chip type %d (0x%x)\n", - __func__, m->vendor_name, m->card_name, m->type, - type[HFC_cnt]); - - /* allocate card+fifo structure */ - hc = kzalloc_obj(struct hfc_multi); - if (!hc) { - printk(KERN_ERR "No kmem for HFC-Multi card\n"); - return -ENOMEM; - } - spin_lock_init(&hc->lock); - hc->mtyp = m; - hc->ctype = m->type; - hc->ports = m->ports; - hc->id = HFC_cnt; - hc->pcm = pcm[HFC_cnt]; - hc->io_mode = iomode[HFC_cnt]; - if (hc->ctype == HFC_TYPE_E1 && dmask[E1_cnt]) { - /* fragment card */ - pt = 0; - maskcheck = 0; - for (ch = 0; ch <= 31; ch++) { - if (!((1 << ch) & dmask[E1_cnt])) - continue; - hc->dnum[pt] = ch; - hc->bmask[pt] = bmask[bmask_cnt++]; - if ((maskcheck & hc->bmask[pt]) - || (dmask[E1_cnt] & hc->bmask[pt])) { - printk(KERN_INFO - "HFC-E1 #%d has overlapping B-channels on fragment #%d\n", - E1_cnt + 1, pt); - kfree(hc); - return -EINVAL; - } - maskcheck |= hc->bmask[pt]; - printk(KERN_INFO - "HFC-E1 #%d uses D-channel on slot %d and a B-channel map of 0x%08x\n", - E1_cnt + 1, ch, hc->bmask[pt]); - pt++; - } - hc->ports = pt; - } - if (hc->ctype == HFC_TYPE_E1 && !dmask[E1_cnt]) { - /* default card layout */ - hc->dnum[0] = 16; - hc->bmask[0] = 0xfffefffe; - hc->ports = 1; - } - - /* set chip specific features */ - hc->masterclk = -1; - if (type[HFC_cnt] & 0x100) { - test_and_set_bit(HFC_CHIP_ULAW, &hc->chip); - hc->silence = 0xff; /* ulaw silence */ - } else - hc->silence = 0x2a; /* alaw silence */ - if ((poll >> 1) > sizeof(hc->silence_data)) { - printk(KERN_ERR "HFCMULTI error: silence_data too small, " - "please fix\n"); - kfree(hc); - return -EINVAL; - } - for (i = 0; i < (poll >> 1); i++) - hc->silence_data[i] = hc->silence; - - if (hc->ctype != HFC_TYPE_XHFC) { - if (!(type[HFC_cnt] & 0x200)) - test_and_set_bit(HFC_CHIP_DTMF, &hc->chip); - test_and_set_bit(HFC_CHIP_CONF, &hc->chip); - } - - if (type[HFC_cnt] & 0x800) - test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); - if (type[HFC_cnt] & 0x1000) { - test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); - test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); - } - if (type[HFC_cnt] & 0x4000) - test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip); - if (type[HFC_cnt] & 0x8000) - test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip); - hc->slots = 32; - if (type[HFC_cnt] & 0x10000) - hc->slots = 64; - if (type[HFC_cnt] & 0x20000) - hc->slots = 128; - if (type[HFC_cnt] & 0x80000) { - test_and_set_bit(HFC_CHIP_WATCHDOG, &hc->chip); - hc->wdcount = 0; - hc->wdbyte = V_GPIO_OUT2; - printk(KERN_NOTICE "Watchdog enabled\n"); - } - - if (pdev && ent) - /* setup pci, hc->slots may change due to PLXSD */ - ret_err = setup_pci(hc, pdev, ent); - else -#ifdef CONFIG_MISDN_HFCMULTI_8xx - ret_err = setup_embedded(hc, m); -#else - { - printk(KERN_WARNING "Embedded IO Mode not selected\n"); - ret_err = -EIO; - } -#endif - if (ret_err) { - if (hc == syncmaster) - syncmaster = NULL; - kfree(hc); - return ret_err; - } - - hc->HFC_outb_nodebug = hc->HFC_outb; - hc->HFC_inb_nodebug = hc->HFC_inb; - hc->HFC_inw_nodebug = hc->HFC_inw; - hc->HFC_wait_nodebug = hc->HFC_wait; -#ifdef HFC_REGISTER_DEBUG - hc->HFC_outb = HFC_outb_debug; - hc->HFC_inb = HFC_inb_debug; - hc->HFC_inw = HFC_inw_debug; - hc->HFC_wait = HFC_wait_debug; -#endif - /* create channels */ - for (pt = 0; pt < hc->ports; pt++) { - if (Port_cnt >= MAX_PORTS) { - printk(KERN_ERR "too many ports (max=%d).\n", - MAX_PORTS); - ret_err = -EINVAL; - goto free_card; - } - if (hc->ctype == HFC_TYPE_E1) - ret_err = init_e1_port(hc, m, pt); - else - ret_err = init_multi_port(hc, pt); - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG - "%s: Registering D-channel, card(%d) port(%d) " - "result %d\n", - __func__, HFC_cnt + 1, pt + 1, ret_err); - - if (ret_err) { - while (pt) { /* release already registered ports */ - pt--; - if (hc->ctype == HFC_TYPE_E1) - release_port(hc, - hc->chan[hc->dnum[pt]].dch); - else - release_port(hc, - hc->chan[(pt << 2) + 2].dch); - } - goto free_card; - } - if (hc->ctype != HFC_TYPE_E1) - Port_cnt++; /* for each S0 port */ - } - if (hc->ctype == HFC_TYPE_E1) { - Port_cnt++; /* for each E1 port */ - E1_cnt++; - } - - /* disp switches */ - switch (m->dip_type) { - case DIP_4S: - /* - * Get DIP setting for beroNet 1S/2S/4S cards - * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) + - * GPI 19/23 (R_GPI_IN2)) - */ - dips = ((~HFC_inb(hc, R_GPIO_IN1) & 0xE0) >> 5) | - ((~HFC_inb(hc, R_GPI_IN2) & 0x80) >> 3) | - (~HFC_inb(hc, R_GPI_IN2) & 0x08); - - /* Port mode (TE/NT) jumpers */ - pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4) & 0xf); - - if (test_bit(HFC_CHIP_B410P, &hc->chip)) - pmj = ~pmj & 0xf; - - printk(KERN_INFO "%s: %s DIPs(0x%x) jumpers(0x%x)\n", - m->vendor_name, m->card_name, dips, pmj); - break; - case DIP_8S: - /* - * Get DIP Setting for beroNet 8S0+ cards - * Enable PCI auxbridge function - */ - HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); - /* prepare access to auxport */ - outw(0x4000, hc->pci_iobase + 4); - /* - * some dummy reads are required to - * read valid DIP switch data - */ - dips = inb(hc->pci_iobase); - dips = inb(hc->pci_iobase); - dips = inb(hc->pci_iobase); - dips = ~inb(hc->pci_iobase) & 0x3F; - outw(0x0, hc->pci_iobase + 4); - /* disable PCI auxbridge function */ - HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); - printk(KERN_INFO "%s: %s DIPs(0x%x)\n", - m->vendor_name, m->card_name, dips); - break; - case DIP_E1: - /* - * get DIP Setting for beroNet E1 cards - * DIP Setting: collect GPI 4/5/6/7 (R_GPI_IN0) - */ - dips = (~HFC_inb(hc, R_GPI_IN0) & 0xF0) >> 4; - printk(KERN_INFO "%s: %s DIPs(0x%x)\n", - m->vendor_name, m->card_name, dips); - break; - } - - /* add to list */ - spin_lock_irqsave(&HFClock, flags); - list_add_tail(&hc->list, &HFClist); - spin_unlock_irqrestore(&HFClock, flags); - - /* use as clock source */ - if (clock == HFC_cnt + 1) - hc->iclock = mISDN_register_clock("HFCMulti", 0, clockctl, hc); - - /* initialize hardware */ - hc->irq = (m->irq) ? : hc->pci_dev->irq; - ret_err = init_card(hc); - if (ret_err) { - printk(KERN_ERR "init card returns %d\n", ret_err); - release_card(hc); - return ret_err; - } - - /* start IRQ and return */ - spin_lock_irqsave(&hc->lock, flags); - enable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - return 0; - -free_card: - release_io_hfcmulti(hc); - if (hc == syncmaster) - syncmaster = NULL; - kfree(hc); - return ret_err; -} - -static void hfc_remove_pci(struct pci_dev *pdev) -{ - struct hfc_multi *card = pci_get_drvdata(pdev); - u_long flags; - - if (debug) - printk(KERN_INFO "removing hfc_multi card vendor:%x " - "device:%x subvendor:%x subdevice:%x\n", - pdev->vendor, pdev->device, - pdev->subsystem_vendor, pdev->subsystem_device); - - if (card) { - spin_lock_irqsave(&HFClock, flags); - release_card(card); - spin_unlock_irqrestore(&HFClock, flags); - } else { - if (debug) - printk(KERN_DEBUG "%s: drvdata already removed\n", - __func__); - } -} - -#define VENDOR_CCD "Cologne Chip AG" -#define VENDOR_BN "beroNet GmbH" -#define VENDOR_DIG "Digium Inc." -#define VENDOR_JH "Junghanns.NET GmbH" -#define VENDOR_PRIM "PrimuX" - -static const struct hm_map hfcm_map[] = { - /*0*/ {VENDOR_BN, "HFC-1S Card (mini PCI)", 4, 1, 1, 3, 0, DIP_4S, 0, 0}, - /*1*/ {VENDOR_BN, "HFC-2S Card", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, - /*2*/ {VENDOR_BN, "HFC-2S Card (mini PCI)", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, - /*3*/ {VENDOR_BN, "HFC-4S Card", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, - /*4*/ {VENDOR_BN, "HFC-4S Card (mini PCI)", 4, 4, 1, 2, 0, 0, 0, 0}, - /*5*/ {VENDOR_CCD, "HFC-4S Eval (old)", 4, 4, 0, 0, 0, 0, 0, 0}, - /*6*/ {VENDOR_CCD, "HFC-4S IOB4ST", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, - /*7*/ {VENDOR_CCD, "HFC-4S", 4, 4, 1, 2, 0, 0, 0, 0}, - /*8*/ {VENDOR_DIG, "HFC-4S Card", 4, 4, 0, 2, 0, 0, HFC_IO_MODE_REGIO, 0}, - /*9*/ {VENDOR_CCD, "HFC-4S Swyx 4xS0 SX2 QuadBri", 4, 4, 1, 2, 0, 0, 0, 0}, - /*10*/ {VENDOR_JH, "HFC-4S (junghanns 2.0)", 4, 4, 1, 2, 0, 0, 0, 0}, - /*11*/ {VENDOR_PRIM, "HFC-2S Primux Card", 4, 2, 0, 0, 0, 0, 0, 0}, - - /*12*/ {VENDOR_BN, "HFC-8S Card", 8, 8, 1, 0, 0, 0, 0, 0}, - /*13*/ {VENDOR_BN, "HFC-8S Card (+)", 8, 8, 1, 8, 0, DIP_8S, - HFC_IO_MODE_REGIO, 0}, - /*14*/ {VENDOR_CCD, "HFC-8S Eval (old)", 8, 8, 0, 0, 0, 0, 0, 0}, - /*15*/ {VENDOR_CCD, "HFC-8S IOB4ST Recording", 8, 8, 1, 0, 0, 0, 0, 0}, - - /*16*/ {VENDOR_CCD, "HFC-8S IOB8ST", 8, 8, 1, 0, 0, 0, 0, 0}, - /*17*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0, 0}, - /*18*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0, 0}, - - /*19*/ {VENDOR_BN, "HFC-E1 Card", 1, 1, 0, 1, 0, DIP_E1, 0, 0}, - /*20*/ {VENDOR_BN, "HFC-E1 Card (mini PCI)", 1, 1, 0, 1, 0, 0, 0, 0}, - /*21*/ {VENDOR_BN, "HFC-E1+ Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0, 0}, - /*22*/ {VENDOR_BN, "HFC-E1 Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0, 0}, - - /*23*/ {VENDOR_CCD, "HFC-E1 Eval (old)", 1, 1, 0, 0, 0, 0, 0, 0}, - /*24*/ {VENDOR_CCD, "HFC-E1 IOB1E1", 1, 1, 0, 1, 0, 0, 0, 0}, - /*25*/ {VENDOR_CCD, "HFC-E1", 1, 1, 0, 1, 0, 0, 0, 0}, - - /*26*/ {VENDOR_CCD, "HFC-4S Speech Design", 4, 4, 0, 0, 0, 0, - HFC_IO_MODE_PLXSD, 0}, - /*27*/ {VENDOR_CCD, "HFC-E1 Speech Design", 1, 1, 0, 0, 0, 0, - HFC_IO_MODE_PLXSD, 0}, - /*28*/ {VENDOR_CCD, "HFC-4S OpenVox", 4, 4, 1, 0, 0, 0, 0, 0}, - /*29*/ {VENDOR_CCD, "HFC-2S OpenVox", 4, 2, 1, 0, 0, 0, 0, 0}, - /*30*/ {VENDOR_CCD, "HFC-8S OpenVox", 8, 8, 1, 0, 0, 0, 0, 0}, - /*31*/ {VENDOR_CCD, "XHFC-4S Speech Design", 5, 4, 0, 0, 0, 0, - HFC_IO_MODE_EMBSD, XHFC_IRQ}, - /*32*/ {VENDOR_JH, "HFC-8S (junghanns)", 8, 8, 1, 0, 0, 0, 0, 0}, - /*33*/ {VENDOR_BN, "HFC-2S Beronet Card PCIe", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, - /*34*/ {VENDOR_BN, "HFC-4S Beronet Card PCIe", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, -}; - -#undef H -#define H(x) ((unsigned long)&hfcm_map[x]) -static const struct pci_device_id hfmultipci_ids[] = { - - /* Cards with HFC-4S Chip */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN1SM, 0, 0, H(0)}, /* BN1S mini PCI */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN2S, 0, 0, H(1)}, /* BN2S */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN2SM, 0, 0, H(2)}, /* BN2S mini PCI */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN4S, 0, 0, H(3)}, /* BN4S */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN4SM, 0, 0, H(4)}, /* BN4S mini PCI */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_DEVICE_ID_CCD_HFC4S, 0, 0, H(5)}, /* Old Eval */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_IOB4ST, 0, 0, H(6)}, /* IOB4ST */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_HFC4S, 0, 0, H(7)}, /* 4S */ - { PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, - PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, 0, 0, H(8)}, - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_SWYX4S, 0, 0, H(9)}, /* 4S Swyx */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_JH4S20, 0, 0, H(10)}, - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_PMX2S, 0, 0, H(11)}, /* Primux */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_OV4S, 0, 0, H(28)}, /* OpenVox 4 */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_OV2S, 0, 0, H(29)}, /* OpenVox 2 */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - 0xb761, 0, 0, H(33)}, /* BN2S PCIe */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, - 0xb762, 0, 0, H(34)}, /* BN4S PCIe */ - - /* Cards with HFC-8S Chip */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN8S, 0, 0, H(12)}, /* BN8S */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BN8SP, 0, 0, H(13)}, /* BN8S+ */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)}, /* IOB8ST Recording */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_IOB8ST_1, 0, 0, H(17)}, /* IOB8ST */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_HFC8S, 0, 0, H(18)}, /* 8S */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_OV8S, 0, 0, H(30)}, /* OpenVox 8 */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_JH8S, 0, 0, H(32)}, /* Junganns 8S */ - - - /* Cards with HFC-E1 Chip */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BNE1, 0, 0, H(19)}, /* BNE1 */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BNE1M, 0, 0, H(20)}, /* BNE1 mini PCI */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BNE1DP, 0, 0, H(21)}, /* BNE1 + (Dual) */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_BNE1D, 0, 0, H(22)}, /* BNE1 (Dual) */ - - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_DEVICE_ID_CCD_HFCE1, 0, 0, H(23)}, /* Old Eval */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_IOB1E1, 0, 0, H(24)}, /* IOB1E1 */ - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_HFCE1, 0, 0, H(25)}, /* E1 */ - - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_SPD4S, 0, 0, H(26)}, /* PLX PCI Bridge */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_SPDE1, 0, 0, H(27)}, /* PLX PCI Bridge */ - - { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_JHSE1, 0, 0, H(25)}, /* Junghanns E1 */ - - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_HFC4S), 0 }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_HFC8S), 0 }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_HFCE1), 0 }, - {0, } -}; -#undef H - -MODULE_DEVICE_TABLE(pci, hfmultipci_ids); - -static int -hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - struct hm_map *m = (struct hm_map *)ent->driver_data; - int ret; - - if (m == NULL && ent->vendor == PCI_VENDOR_ID_CCD && ( - ent->device == PCI_DEVICE_ID_CCD_HFC4S || - ent->device == PCI_DEVICE_ID_CCD_HFC8S || - ent->device == PCI_DEVICE_ID_CCD_HFCE1)) { - printk(KERN_ERR - "Unknown HFC multiport controller (vendor:%04x device:%04x " - "subvendor:%04x subdevice:%04x)\n", pdev->vendor, - pdev->device, pdev->subsystem_vendor, - pdev->subsystem_device); - printk(KERN_ERR - "Please contact the driver maintainer for support.\n"); - return -ENODEV; - } - ret = hfcmulti_init(m, pdev, ent); - if (ret) - return ret; - HFC_cnt++; - printk(KERN_INFO "%d devices registered\n", HFC_cnt); - return 0; -} - -static struct pci_driver hfcmultipci_driver = { - .name = "hfc_multi", - .probe = hfcmulti_probe, - .remove = hfc_remove_pci, - .id_table = hfmultipci_ids, -}; - -static void __exit -HFCmulti_cleanup(void) -{ - struct hfc_multi *card, *next; - - /* get rid of all devices of this driver */ - list_for_each_entry_safe(card, next, &HFClist, list) - release_card(card); - pci_unregister_driver(&hfcmultipci_driver); -} - -static int __init -HFCmulti_init(void) -{ - int err; - int i, xhfc = 0; - struct hm_map m; - - printk(KERN_INFO "mISDN: HFC-multi driver %s\n", HFC_MULTI_VERSION); - -#ifdef IRQ_DEBUG - printk(KERN_DEBUG "%s: IRQ_DEBUG IS ENABLED!\n", __func__); -#endif - - if (debug & DEBUG_HFCMULTI_INIT) - printk(KERN_DEBUG "%s: init entered\n", __func__); - - switch (poll) { - case 0: - poll_timer = 6; - poll = 128; - break; - case 8: - poll_timer = 2; - break; - case 16: - poll_timer = 3; - break; - case 32: - poll_timer = 4; - break; - case 64: - poll_timer = 5; - break; - case 128: - poll_timer = 6; - break; - case 256: - poll_timer = 7; - break; - default: - printk(KERN_ERR - "%s: Wrong poll value (%d).\n", __func__, poll); - err = -EINVAL; - return err; - - } - - if (!clock) - clock = 1; - - /* Register the embedded devices. - * This should be done before the PCI cards registration */ - switch (hwid) { - case HWID_MINIP4: - xhfc = 1; - m = hfcm_map[31]; - break; - case HWID_MINIP8: - xhfc = 2; - m = hfcm_map[31]; - break; - case HWID_MINIP16: - xhfc = 4; - m = hfcm_map[31]; - break; - default: - xhfc = 0; - } - - for (i = 0; i < xhfc; ++i) { - err = hfcmulti_init(&m, NULL, NULL); - if (err) { - printk(KERN_ERR "error registering embedded driver: " - "%x\n", err); - return err; - } - HFC_cnt++; - printk(KERN_INFO "%d devices registered\n", HFC_cnt); - } - - /* Register the PCI cards */ - err = pci_register_driver(&hfcmultipci_driver); - if (err < 0) { - printk(KERN_ERR "error registering pci driver: %x\n", err); - return err; - } - - return 0; -} - - -module_init(HFCmulti_init); -module_exit(HFCmulti_cleanup); diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c deleted file mode 100644 index 554a1c640321..000000000000 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ /dev/null @@ -1,2360 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * hfcpci.c low level driver for CCD's hfc-pci based cards - * - * Author Werner Cornelius (werner@isdn4linux.de) - * based on existing driver for CCD hfc ISA cards - * type approval valid for HFC-S PCI A based card - * - * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) - * Copyright 2008 by Karsten Keil - * - * Module options: - * - * debug: - * NOTE: only one poll value must be given for all cards - * See hfc_pci.h for debug flags. - * - * poll: - * NOTE: only one poll value must be given for all cards - * Give the number of samples for each fifo process. - * By default 128 is used. Decrease to reduce delay, increase to - * reduce cpu load. If unsure, don't mess with it! - * A value of 128 will use controller's interrupt. Other values will - * use kernel timer, because the controller will not allow lower values - * than 128. - * Also note that the value depends on the kernel timer frequency. - * If kernel uses a frequency of 1000 Hz, steps of 8 samples are possible. - * If the kernel uses 100 Hz, steps of 80 samples are possible. - * If the kernel uses 300 Hz, steps of about 26 samples are possible. - */ - -#include -#include -#include -#include -#include -#include - -#include "hfc_pci.h" - -static void hfcpci_softirq(struct timer_list *unused); -static const char *hfcpci_revision = "2.0"; - -static int HFC_cnt; -static uint debug; -static uint poll, tics; -static DEFINE_TIMER(hfc_tl, hfcpci_softirq); -static unsigned long hfc_jiffies; - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for CCD's hfc-pci based cards"); -MODULE_LICENSE("GPL"); -module_param(debug, uint, S_IRUGO | S_IWUSR); -module_param(poll, uint, S_IRUGO | S_IWUSR); - -enum { - HFC_CCD_2BD0, - HFC_CCD_B000, - HFC_CCD_B006, - HFC_CCD_B007, - HFC_CCD_B008, - HFC_CCD_B009, - HFC_CCD_B00A, - HFC_CCD_B00B, - HFC_CCD_B00C, - HFC_CCD_B100, - HFC_CCD_B700, - HFC_CCD_B701, - HFC_ASUS_0675, - HFC_BERKOM_A1T, - HFC_BERKOM_TCONCEPT, - HFC_ANIGMA_MC145575, - HFC_ZOLTRIX_2BD0, - HFC_DIGI_DF_M_IOM2_E, - HFC_DIGI_DF_M_E, - HFC_DIGI_DF_M_IOM2_A, - HFC_DIGI_DF_M_A, - HFC_ABOCOM_2BD1, - HFC_SITECOM_DC105V2, -}; - -struct hfcPCI_hw { - unsigned char cirm; - unsigned char ctmt; - unsigned char clkdel; - unsigned char states; - unsigned char conn; - unsigned char mst_m; - unsigned char int_m1; - unsigned char int_m2; - unsigned char sctrl; - unsigned char sctrl_r; - unsigned char sctrl_e; - unsigned char trm; - unsigned char fifo_en; - unsigned char bswapped; - unsigned char protocol; - int nt_timer; - unsigned char __iomem *pci_io; /* start of PCI IO memory */ - dma_addr_t dmahandle; - void *fifos; /* FIFO memory */ - int last_bfifo_cnt[2]; - /* marker saving last b-fifo frame count */ - struct timer_list timer; -}; - -#define HFC_CFG_MASTER 1 -#define HFC_CFG_SLAVE 2 -#define HFC_CFG_PCM 3 -#define HFC_CFG_2HFC 4 -#define HFC_CFG_SLAVEHFC 5 -#define HFC_CFG_NEG_F0 6 -#define HFC_CFG_SW_DD_DU 7 - -#define FLG_HFC_TIMER_T1 16 -#define FLG_HFC_TIMER_T3 17 - -#define NT_T1_COUNT 1120 /* number of 3.125ms interrupts (3.5s) */ -#define NT_T3_COUNT 31 /* number of 3.125ms interrupts (97 ms) */ -#define CLKDEL_TE 0x0e /* CLKDEL in TE mode */ -#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ - - -struct hfc_pci { - u_char subtype; - u_char chanlimit; - u_char initdone; - u_long cfg; - u_int irq; - u_int irqcnt; - struct pci_dev *pdev; - struct hfcPCI_hw hw; - spinlock_t lock; /* card lock */ - struct dchannel dch; - struct bchannel bch[2]; -}; - -/* Interface functions */ -static void -enable_hwirq(struct hfc_pci *hc) -{ - hc->hw.int_m2 |= HFCPCI_IRQ_ENABLE; - Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); -} - -static void -disable_hwirq(struct hfc_pci *hc) -{ - hc->hw.int_m2 &= ~((u_char)HFCPCI_IRQ_ENABLE); - Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); -} - -/* - * free hardware resources used by driver - */ -static void -release_io_hfcpci(struct hfc_pci *hc) -{ - /* disable memory mapped ports + busmaster */ - pci_write_config_word(hc->pdev, PCI_COMMAND, 0); - timer_delete(&hc->hw.timer); - dma_free_coherent(&hc->pdev->dev, 0x8000, hc->hw.fifos, - hc->hw.dmahandle); - iounmap(hc->hw.pci_io); -} - -/* - * set mode (NT or TE) - */ -static void -hfcpci_setmode(struct hfc_pci *hc) -{ - if (hc->hw.protocol == ISDN_P_NT_S0) { - hc->hw.clkdel = CLKDEL_NT; /* ST-Bit delay for NT-Mode */ - hc->hw.sctrl |= SCTRL_MODE_NT; /* NT-MODE */ - hc->hw.states = 1; /* G1 */ - } else { - hc->hw.clkdel = CLKDEL_TE; /* ST-Bit delay for TE-Mode */ - hc->hw.sctrl &= ~SCTRL_MODE_NT; /* TE-MODE */ - hc->hw.states = 2; /* F2 */ - } - Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel); - Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states); - udelay(10); - Write_hfc(hc, HFCPCI_STATES, hc->hw.states | 0x40); /* Deactivate */ - Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); -} - -/* - * function called to reset the HFC PCI chip. A complete software reset of chip - * and fifos is done. - */ -static void -reset_hfcpci(struct hfc_pci *hc) -{ - u_char val; - int cnt = 0; - - printk(KERN_DEBUG "reset_hfcpci: entered\n"); - val = Read_hfc(hc, HFCPCI_CHIP_ID); - printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val); - /* enable memory mapped ports, disable busmaster */ - pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO); - disable_hwirq(hc); - /* enable memory ports + busmaster */ - pci_write_config_word(hc->pdev, PCI_COMMAND, - PCI_ENA_MEMIO + PCI_ENA_MASTER); - val = Read_hfc(hc, HFCPCI_STATUS); - printk(KERN_DEBUG "HFC-PCI status(%x) before reset\n", val); - hc->hw.cirm = HFCPCI_RESET; /* Reset On */ - Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); - set_current_state(TASK_UNINTERRUPTIBLE); - mdelay(10); /* Timeout 10ms */ - hc->hw.cirm = 0; /* Reset Off */ - Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); - val = Read_hfc(hc, HFCPCI_STATUS); - printk(KERN_DEBUG "HFC-PCI status(%x) after reset\n", val); - while (cnt < 50000) { /* max 50000 us */ - udelay(5); - cnt += 5; - val = Read_hfc(hc, HFCPCI_STATUS); - if (!(val & 2)) - break; - } - printk(KERN_DEBUG "HFC-PCI status(%x) after %dus\n", val, cnt); - - hc->hw.fifo_en = 0x30; /* only D fifos enabled */ - - hc->hw.bswapped = 0; /* no exchange */ - hc->hw.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER; - hc->hw.trm = HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */ - hc->hw.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */ - hc->hw.sctrl_r = 0; - hc->hw.sctrl_e = HFCPCI_AUTO_AWAKE; /* S/T Auto awake */ - hc->hw.mst_m = 0; - if (test_bit(HFC_CFG_MASTER, &hc->cfg)) - hc->hw.mst_m |= HFCPCI_MASTER; /* HFC Master Mode */ - if (test_bit(HFC_CFG_NEG_F0, &hc->cfg)) - hc->hw.mst_m |= HFCPCI_F0_NEGATIV; - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); - Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); - Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); - Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); - - hc->hw.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | - HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - - /* Clear already pending ints */ - val = Read_hfc(hc, HFCPCI_INT_S1); - - /* set NT/TE mode */ - hfcpci_setmode(hc); - - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); - - /* - * Init GCI/IOM2 in master mode - * Slots 0 and 1 are set for B-chan 1 and 2 - * D- and monitor/CI channel are not enabled - * STIO1 is used as output for data, B1+B2 from ST->IOM+HFC - * STIO2 is used as data input, B1+B2 from IOM->ST - * ST B-channel send disabled -> continuous 1s - * The IOM slots are always enabled - */ - if (test_bit(HFC_CFG_PCM, &hc->cfg)) { - /* set data flow directions: connect B1,B2: HFC to/from PCM */ - hc->hw.conn = 0x09; - } else { - hc->hw.conn = 0x36; /* set data flow directions */ - if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { - Write_hfc(hc, HFCPCI_B1_SSL, 0xC0); - Write_hfc(hc, HFCPCI_B2_SSL, 0xC1); - Write_hfc(hc, HFCPCI_B1_RSL, 0xC0); - Write_hfc(hc, HFCPCI_B2_RSL, 0xC1); - } else { - Write_hfc(hc, HFCPCI_B1_SSL, 0x80); - Write_hfc(hc, HFCPCI_B2_SSL, 0x81); - Write_hfc(hc, HFCPCI_B1_RSL, 0x80); - Write_hfc(hc, HFCPCI_B2_RSL, 0x81); - } - } - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); - val = Read_hfc(hc, HFCPCI_INT_S2); -} - -/* - * Timer function called when kernel timer expires - */ -static void -hfcpci_Timer(struct timer_list *t) -{ - struct hfc_pci *hc = timer_container_of(hc, t, hw.timer); - hc->hw.timer.expires = jiffies + 75; - /* WD RESET */ -/* - * WriteReg(hc, HFCD_DATA, HFCD_CTMT, hc->hw.ctmt | 0x80); - * add_timer(&hc->hw.timer); - */ -} - - -/* - * select a b-channel entry matching and active - */ -static struct bchannel * -Sel_BCS(struct hfc_pci *hc, int channel) -{ - if (test_bit(FLG_ACTIVE, &hc->bch[0].Flags) && - (hc->bch[0].nr & channel)) - return &hc->bch[0]; - else if (test_bit(FLG_ACTIVE, &hc->bch[1].Flags) && - (hc->bch[1].nr & channel)) - return &hc->bch[1]; - else - return NULL; -} - -/* - * clear the desired B-channel rx fifo - */ -static void -hfcpci_clear_fifo_rx(struct hfc_pci *hc, int fifo) -{ - u_char fifo_state; - struct bzfifo *bzr; - - if (fifo) { - bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2; - fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX; - } else { - bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1; - fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1RX; - } - if (fifo_state) - hc->hw.fifo_en ^= fifo_state; - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); - hc->hw.last_bfifo_cnt[fifo] = 0; - bzr->f1 = MAX_B_FRAMES; - bzr->f2 = bzr->f1; /* init F pointers to remain constant */ - bzr->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); - bzr->za[MAX_B_FRAMES].z2 = cpu_to_le16( - le16_to_cpu(bzr->za[MAX_B_FRAMES].z1)); - if (fifo_state) - hc->hw.fifo_en |= fifo_state; - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); -} - -/* - * clear the desired B-channel tx fifo - */ -static void hfcpci_clear_fifo_tx(struct hfc_pci *hc, int fifo) -{ - u_char fifo_state; - struct bzfifo *bzt; - - if (fifo) { - bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; - fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX; - } else { - bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; - fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1TX; - } - if (fifo_state) - hc->hw.fifo_en ^= fifo_state; - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); - if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) " - "z1(%x) z2(%x) state(%x)\n", - fifo, bzt->f1, bzt->f2, - le16_to_cpu(bzt->za[MAX_B_FRAMES].z1), - le16_to_cpu(bzt->za[MAX_B_FRAMES].z2), - fifo_state); - bzt->f2 = MAX_B_FRAMES; - bzt->f1 = bzt->f2; /* init F pointers to remain constant */ - bzt->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); - bzt->za[MAX_B_FRAMES].z2 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 2); - if (fifo_state) - hc->hw.fifo_en |= fifo_state; - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); - if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG - "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)\n", - fifo, bzt->f1, bzt->f2, - le16_to_cpu(bzt->za[MAX_B_FRAMES].z1), - le16_to_cpu(bzt->za[MAX_B_FRAMES].z2)); -} - -/* - * read a complete B-frame out of the buffer - */ -static void -hfcpci_empty_bfifo(struct bchannel *bch, struct bzfifo *bz, - u_char *bdata, int count) -{ - u_char *ptr, *ptr1, new_f2; - int maxlen, new_z2; - struct zt *zp; - - if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO)) - printk(KERN_DEBUG "hfcpci_empty_fifo\n"); - zp = &bz->za[bz->f2]; /* point to Z-Regs */ - new_z2 = le16_to_cpu(zp->z2) + count; /* new position in fifo */ - if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z2 -= B_FIFO_SIZE; /* buffer wrap */ - new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; - if ((count > MAX_DATA_SIZE + 3) || (count < 4) || - (*(bdata + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) { - if (bch->debug & DEBUG_HW) - printk(KERN_DEBUG "hfcpci_empty_fifo: incoming packet " - "invalid length %d or crc\n", count); -#ifdef ERROR_STATISTIC - bch->err_inv++; -#endif - bz->za[new_f2].z2 = cpu_to_le16(new_z2); - bz->f2 = new_f2; /* next buffer */ - } else { - bch->rx_skb = mI_alloc_skb(count - 3, GFP_ATOMIC); - if (!bch->rx_skb) { - printk(KERN_WARNING "HFCPCI: receive out of memory\n"); - return; - } - count -= 3; - ptr = skb_put(bch->rx_skb, count); - - if (le16_to_cpu(zp->z2) + count <= B_FIFO_SIZE + B_SUB_VAL) - maxlen = count; /* complete transfer */ - else - maxlen = B_FIFO_SIZE + B_SUB_VAL - - le16_to_cpu(zp->z2); /* maximum */ - - ptr1 = bdata + (le16_to_cpu(zp->z2) - B_SUB_VAL); - /* start of data */ - memcpy(ptr, ptr1, maxlen); /* copy data */ - count -= maxlen; - - if (count) { /* rest remaining */ - ptr += maxlen; - ptr1 = bdata; /* start of buffer */ - memcpy(ptr, ptr1, count); /* rest */ - } - bz->za[new_f2].z2 = cpu_to_le16(new_z2); - bz->f2 = new_f2; /* next buffer */ - recv_Bchannel(bch, MISDN_ID_ANY, false); - } -} - -/* - * D-channel receive procedure - */ -static int -receive_dmsg(struct hfc_pci *hc) -{ - struct dchannel *dch = &hc->dch; - int maxlen; - int rcnt, total; - int count = 5; - u_char *ptr, *ptr1; - struct dfifo *df; - struct zt *zp; - - df = &((union fifo_area *)(hc->hw.fifos))->d_chan.d_rx; - while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) { - zp = &df->za[df->f2 & D_FREG_MASK]; - rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); - if (rcnt < 0) - rcnt += D_FIFO_SIZE; - rcnt++; - if (dch->debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG - "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)\n", - df->f1, df->f2, - le16_to_cpu(zp->z1), - le16_to_cpu(zp->z2), - rcnt); - - if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) || - (df->data[le16_to_cpu(zp->z1)])) { - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG - "empty_fifo hfcpci packet inv. len " - "%d or crc %d\n", - rcnt, - df->data[le16_to_cpu(zp->z1)]); -#ifdef ERROR_STATISTIC - cs->err_rx++; -#endif - df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | - (MAX_D_FRAMES + 1); /* next buffer */ - df->za[df->f2 & D_FREG_MASK].z2 = - cpu_to_le16((le16_to_cpu(zp->z2) + rcnt) & - (D_FIFO_SIZE - 1)); - } else { - dch->rx_skb = mI_alloc_skb(rcnt - 3, GFP_ATOMIC); - if (!dch->rx_skb) { - printk(KERN_WARNING - "HFC-PCI: D receive out of memory\n"); - break; - } - total = rcnt; - rcnt -= 3; - ptr = skb_put(dch->rx_skb, rcnt); - - if (le16_to_cpu(zp->z2) + rcnt <= D_FIFO_SIZE) - maxlen = rcnt; /* complete transfer */ - else - maxlen = D_FIFO_SIZE - le16_to_cpu(zp->z2); - /* maximum */ - - ptr1 = df->data + le16_to_cpu(zp->z2); - /* start of data */ - memcpy(ptr, ptr1, maxlen); /* copy data */ - rcnt -= maxlen; - - if (rcnt) { /* rest remaining */ - ptr += maxlen; - ptr1 = df->data; /* start of buffer */ - memcpy(ptr, ptr1, rcnt); /* rest */ - } - df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | - (MAX_D_FRAMES + 1); /* next buffer */ - df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16(( - le16_to_cpu(zp->z2) + total) & (D_FIFO_SIZE - 1)); - recv_Dchannel(dch); - } - } - return 1; -} - -/* - * check for transparent receive data and read max one 'poll' size if avail - */ -static void -hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *rxbz, - struct bzfifo *txbz, u_char *bdata) -{ - __le16 *z1r, *z2r, *z1t, *z2t; - int new_z2, fcnt_rx, fcnt_tx, maxlen; - u_char *ptr, *ptr1; - - z1r = &rxbz->za[MAX_B_FRAMES].z1; /* pointer to z reg */ - z2r = z1r + 1; - z1t = &txbz->za[MAX_B_FRAMES].z1; - z2t = z1t + 1; - - fcnt_rx = le16_to_cpu(*z1r) - le16_to_cpu(*z2r); - if (!fcnt_rx) - return; /* no data avail */ - - if (fcnt_rx <= 0) - fcnt_rx += B_FIFO_SIZE; /* bytes actually buffered */ - new_z2 = le16_to_cpu(*z2r) + fcnt_rx; /* new position in fifo */ - if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z2 -= B_FIFO_SIZE; /* buffer wrap */ - - fcnt_tx = le16_to_cpu(*z2t) - le16_to_cpu(*z1t); - if (fcnt_tx <= 0) - fcnt_tx += B_FIFO_SIZE; - /* fcnt_tx contains available bytes in tx-fifo */ - fcnt_tx = B_FIFO_SIZE - fcnt_tx; - /* remaining bytes to send (bytes in tx-fifo) */ - - if (test_bit(FLG_RX_OFF, &bch->Flags)) { - bch->dropcnt += fcnt_rx; - *z2r = cpu_to_le16(new_z2); - return; - } - maxlen = bchannel_get_rxbuf(bch, fcnt_rx); - if (maxlen < 0) { - pr_warn("B%d: No bufferspace for %d bytes\n", bch->nr, fcnt_rx); - } else { - ptr = skb_put(bch->rx_skb, fcnt_rx); - if (le16_to_cpu(*z2r) + fcnt_rx <= B_FIFO_SIZE + B_SUB_VAL) - maxlen = fcnt_rx; /* complete transfer */ - else - maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(*z2r); - /* maximum */ - - ptr1 = bdata + (le16_to_cpu(*z2r) - B_SUB_VAL); - /* start of data */ - memcpy(ptr, ptr1, maxlen); /* copy data */ - fcnt_rx -= maxlen; - - if (fcnt_rx) { /* rest remaining */ - ptr += maxlen; - ptr1 = bdata; /* start of buffer */ - memcpy(ptr, ptr1, fcnt_rx); /* rest */ - } - recv_Bchannel(bch, fcnt_tx, false); /* bch, id, !force */ - } - *z2r = cpu_to_le16(new_z2); /* new position */ -} - -/* - * B-channel main receive routine - */ -static void -main_rec_hfcpci(struct bchannel *bch) -{ - struct hfc_pci *hc = bch->hw; - int rcnt, real_fifo; - int receive = 0, count = 5; - struct bzfifo *txbz, *rxbz; - u_char *bdata; - struct zt *zp; - - if ((bch->nr & 2) && (!hc->hw.bswapped)) { - rxbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2; - txbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; - bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b2; - real_fifo = 1; - } else { - rxbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1; - txbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; - bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b1; - real_fifo = 0; - } -Begin: - count--; - if (rxbz->f1 != rxbz->f2) { - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG "hfcpci rec ch(%x) f1(%d) f2(%d)\n", - bch->nr, rxbz->f1, rxbz->f2); - zp = &rxbz->za[rxbz->f2]; - - rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); - if (rcnt < 0) - rcnt += B_FIFO_SIZE; - rcnt++; - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG - "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)\n", - bch->nr, le16_to_cpu(zp->z1), - le16_to_cpu(zp->z2), rcnt); - hfcpci_empty_bfifo(bch, rxbz, bdata, rcnt); - rcnt = rxbz->f1 - rxbz->f2; - if (rcnt < 0) - rcnt += MAX_B_FRAMES + 1; - if (hc->hw.last_bfifo_cnt[real_fifo] > rcnt + 1) { - rcnt = 0; - hfcpci_clear_fifo_rx(hc, real_fifo); - } - hc->hw.last_bfifo_cnt[real_fifo] = rcnt; - if (rcnt > 1) - receive = 1; - else - receive = 0; - } else if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { - hfcpci_empty_fifo_trans(bch, rxbz, txbz, bdata); - return; - } else - receive = 0; - if (count && receive) - goto Begin; - -} - -/* - * D-channel send routine - */ -static void -hfcpci_fill_dfifo(struct hfc_pci *hc) -{ - struct dchannel *dch = &hc->dch; - int fcnt; - int count, new_z1, maxlen; - struct dfifo *df; - u_char *src, *dst, new_f1; - - if ((dch->debug & DEBUG_HW_DCHANNEL) && !(dch->debug & DEBUG_HW_DFIFO)) - printk(KERN_DEBUG "%s\n", __func__); - - if (!dch->tx_skb) - return; - count = dch->tx_skb->len - dch->tx_idx; - if (count <= 0) - return; - df = &((union fifo_area *) (hc->hw.fifos))->d_chan.d_tx; - - if (dch->debug & DEBUG_HW_DFIFO) - printk(KERN_DEBUG "%s:f1(%d) f2(%d) z1(f1)(%x)\n", __func__, - df->f1, df->f2, - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1)); - fcnt = df->f1 - df->f2; /* frame count actually buffered */ - if (fcnt < 0) - fcnt += (MAX_D_FRAMES + 1); /* if wrap around */ - if (fcnt > (MAX_D_FRAMES - 1)) { - if (dch->debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG - "hfcpci_fill_Dfifo more as 14 frames\n"); -#ifdef ERROR_STATISTIC - cs->err_tx++; -#endif - return; - } - /* now determine free bytes in FIFO buffer */ - maxlen = le16_to_cpu(df->za[df->f2 & D_FREG_MASK].z2) - - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) - 1; - if (maxlen <= 0) - maxlen += D_FIFO_SIZE; /* count now contains available bytes */ - - if (dch->debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG "hfcpci_fill_Dfifo count(%d/%d)\n", - count, maxlen); - if (count > maxlen) { - if (dch->debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG "hfcpci_fill_Dfifo no fifo mem\n"); - return; - } - new_z1 = (le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) + count) & - (D_FIFO_SIZE - 1); - new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1); - src = dch->tx_skb->data + dch->tx_idx; /* source pointer */ - dst = df->data + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); - maxlen = D_FIFO_SIZE - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); - /* end fifo */ - if (maxlen > count) - maxlen = count; /* limit size */ - memcpy(dst, src, maxlen); /* first copy */ - - count -= maxlen; /* remaining bytes */ - if (count) { - dst = df->data; /* start of buffer */ - src += maxlen; /* new position */ - memcpy(dst, src, count); - } - df->za[new_f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); - /* for next buffer */ - df->za[df->f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); - /* new pos actual buffer */ - df->f1 = new_f1; /* next frame */ - dch->tx_idx = dch->tx_skb->len; -} - -/* - * B-channel send routine - */ -static void -hfcpci_fill_fifo(struct bchannel *bch) -{ - struct hfc_pci *hc = bch->hw; - int maxlen, fcnt; - int count, new_z1; - struct bzfifo *bz; - u_char *bdata; - u_char new_f1, *src, *dst; - __le16 *z1t, *z2t; - - if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO)) - printk(KERN_DEBUG "%s\n", __func__); - if ((!bch->tx_skb) || bch->tx_skb->len == 0) { - if (!test_bit(FLG_FILLEMPTY, &bch->Flags) && - !test_bit(FLG_TRANSPARENT, &bch->Flags)) - return; - count = HFCPCI_FILLEMPTY; - } else { - count = bch->tx_skb->len - bch->tx_idx; - } - if ((bch->nr & 2) && (!hc->hw.bswapped)) { - bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; - bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b2; - } else { - bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; - bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b1; - } - - if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { - z1t = &bz->za[MAX_B_FRAMES].z1; - z2t = z1t + 1; - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG "hfcpci_fill_fifo_trans ch(%x) " - "cnt(%d) z1(%x) z2(%x)\n", bch->nr, count, - le16_to_cpu(*z1t), le16_to_cpu(*z2t)); - fcnt = le16_to_cpu(*z2t) - le16_to_cpu(*z1t); - if (fcnt <= 0) - fcnt += B_FIFO_SIZE; - if (test_bit(FLG_FILLEMPTY, &bch->Flags)) { - /* fcnt contains available bytes in fifo */ - if (count > fcnt) - count = fcnt; - new_z1 = le16_to_cpu(*z1t) + count; - /* new buffer Position */ - if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z1 -= B_FIFO_SIZE; /* buffer wrap */ - dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); - maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); - /* end of fifo */ - if (bch->debug & DEBUG_HW_BFIFO) - printk(KERN_DEBUG "hfcpci_FFt fillempty " - "fcnt(%d) maxl(%d) nz1(%x) dst(%p)\n", - fcnt, maxlen, new_z1, dst); - if (maxlen > count) - maxlen = count; /* limit size */ - memset(dst, bch->fill[0], maxlen); /* first copy */ - count -= maxlen; /* remaining bytes */ - if (count) { - dst = bdata; /* start of buffer */ - memset(dst, bch->fill[0], count); - } - *z1t = cpu_to_le16(new_z1); /* now send data */ - return; - } - /* fcnt contains available bytes in fifo */ - fcnt = B_FIFO_SIZE - fcnt; - /* remaining bytes to send (bytes in fifo) */ - - next_t_frame: - count = bch->tx_skb->len - bch->tx_idx; - /* maximum fill shall be poll*2 */ - if (count > (poll << 1) - fcnt) - count = (poll << 1) - fcnt; - if (count <= 0) - return; - /* data is suitable for fifo */ - new_z1 = le16_to_cpu(*z1t) + count; - /* new buffer Position */ - if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z1 -= B_FIFO_SIZE; /* buffer wrap */ - src = bch->tx_skb->data + bch->tx_idx; - /* source pointer */ - dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); - maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); - /* end of fifo */ - if (bch->debug & DEBUG_HW_BFIFO) - printk(KERN_DEBUG "hfcpci_FFt fcnt(%d) " - "maxl(%d) nz1(%x) dst(%p)\n", - fcnt, maxlen, new_z1, dst); - fcnt += count; - bch->tx_idx += count; - if (maxlen > count) - maxlen = count; /* limit size */ - memcpy(dst, src, maxlen); /* first copy */ - count -= maxlen; /* remaining bytes */ - if (count) { - dst = bdata; /* start of buffer */ - src += maxlen; /* new position */ - memcpy(dst, src, count); - } - *z1t = cpu_to_le16(new_z1); /* now send data */ - if (bch->tx_idx < bch->tx_skb->len) - return; - dev_kfree_skb_any(bch->tx_skb); - if (get_next_bframe(bch)) - goto next_t_frame; - return; - } - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG - "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)\n", - __func__, bch->nr, bz->f1, bz->f2, - bz->za[bz->f1].z1); - fcnt = bz->f1 - bz->f2; /* frame count actually buffered */ - if (fcnt < 0) - fcnt += (MAX_B_FRAMES + 1); /* if wrap around */ - if (fcnt > (MAX_B_FRAMES - 1)) { - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG - "hfcpci_fill_Bfifo more as 14 frames\n"); - return; - } - /* now determine free bytes in FIFO buffer */ - maxlen = le16_to_cpu(bz->za[bz->f2].z2) - - le16_to_cpu(bz->za[bz->f1].z1) - 1; - if (maxlen <= 0) - maxlen += B_FIFO_SIZE; /* count now contains available bytes */ - - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG "hfcpci_fill_fifo ch(%x) count(%d/%d)\n", - bch->nr, count, maxlen); - - if (maxlen < count) { - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG "hfcpci_fill_fifo no fifo mem\n"); - return; - } - new_z1 = le16_to_cpu(bz->za[bz->f1].z1) + count; - /* new buffer Position */ - if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) - new_z1 -= B_FIFO_SIZE; /* buffer wrap */ - - new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES); - src = bch->tx_skb->data + bch->tx_idx; /* source pointer */ - dst = bdata + (le16_to_cpu(bz->za[bz->f1].z1) - B_SUB_VAL); - maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(bz->za[bz->f1].z1); - /* end fifo */ - if (maxlen > count) - maxlen = count; /* limit size */ - memcpy(dst, src, maxlen); /* first copy */ - - count -= maxlen; /* remaining bytes */ - if (count) { - dst = bdata; /* start of buffer */ - src += maxlen; /* new position */ - memcpy(dst, src, count); - } - bz->za[new_f1].z1 = cpu_to_le16(new_z1); /* for next buffer */ - bz->f1 = new_f1; /* next frame */ - dev_kfree_skb_any(bch->tx_skb); - get_next_bframe(bch); -} - - - -/* - * handle L1 state changes TE - */ - -static void -ph_state_te(struct dchannel *dch) -{ - if (dch->debug) - printk(KERN_DEBUG "%s: TE newstate %x\n", - __func__, dch->state); - switch (dch->state) { - case 0: - l1_event(dch->l1, HW_RESET_IND); - break; - case 3: - l1_event(dch->l1, HW_DEACT_IND); - break; - case 5: - case 8: - l1_event(dch->l1, ANYSIGNAL); - break; - case 6: - l1_event(dch->l1, INFO2); - break; - case 7: - l1_event(dch->l1, INFO4_P8); - break; - } -} - -/* - * handle L1 state changes NT - */ - -static void -handle_nt_timer3(struct dchannel *dch) { - struct hfc_pci *hc = dch->hw; - - test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); - hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - hc->hw.nt_timer = 0; - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - if (test_bit(HFC_CFG_MASTER, &hc->cfg)) - hc->hw.mst_m |= HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); -} - -static void -ph_state_nt(struct dchannel *dch) -{ - struct hfc_pci *hc = dch->hw; - - if (dch->debug) - printk(KERN_DEBUG "%s: NT newstate %x\n", - __func__, dch->state); - switch (dch->state) { - case 2: - if (hc->hw.nt_timer < 0) { - hc->hw.nt_timer = 0; - test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); - test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); - hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - /* Clear already pending ints */ - (void) Read_hfc(hc, HFCPCI_INT_S1); - Write_hfc(hc, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE); - udelay(10); - Write_hfc(hc, HFCPCI_STATES, 4); - dch->state = 4; - } else if (hc->hw.nt_timer == 0) { - hc->hw.int_m1 |= HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - hc->hw.nt_timer = NT_T1_COUNT; - hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER; - hc->hw.ctmt |= HFCPCI_TIM3_125; - Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | - HFCPCI_CLTIMER); - test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); - test_and_set_bit(FLG_HFC_TIMER_T1, &dch->Flags); - /* allow G2 -> G3 transition */ - Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); - } else { - Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); - } - break; - case 1: - hc->hw.nt_timer = 0; - test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); - test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); - hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - hc->hw.mst_m &= ~HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - break; - case 4: - hc->hw.nt_timer = 0; - test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); - test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); - hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - break; - case 3: - if (!test_and_set_bit(FLG_HFC_TIMER_T3, &dch->Flags)) { - if (!test_and_clear_bit(FLG_L2_ACTIVATED, - &dch->Flags)) { - handle_nt_timer3(dch); - break; - } - test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); - hc->hw.int_m1 |= HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - hc->hw.nt_timer = NT_T3_COUNT; - hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER; - hc->hw.ctmt |= HFCPCI_TIM3_125; - Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | - HFCPCI_CLTIMER); - } - break; - } -} - -static void -ph_state(struct dchannel *dch) -{ - struct hfc_pci *hc = dch->hw; - - if (hc->hw.protocol == ISDN_P_NT_S0) { - if (test_bit(FLG_HFC_TIMER_T3, &dch->Flags) && - hc->hw.nt_timer < 0) - handle_nt_timer3(dch); - else - ph_state_nt(dch); - } else - ph_state_te(dch); -} - -/* - * Layer 1 callback function - */ -static int -hfc_l1callback(struct dchannel *dch, u_int cmd) -{ - struct hfc_pci *hc = dch->hw; - - switch (cmd) { - case INFO3_P8: - case INFO3_P10: - if (test_bit(HFC_CFG_MASTER, &hc->cfg)) - hc->hw.mst_m |= HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - break; - case HW_RESET_REQ: - Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3); - /* HFC ST 3 */ - udelay(6); - Write_hfc(hc, HFCPCI_STATES, 3); /* HFC ST 2 */ - if (test_bit(HFC_CFG_MASTER, &hc->cfg)) - hc->hw.mst_m |= HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | - HFCPCI_DO_ACTION); - l1_event(dch->l1, HW_POWERUP_IND); - break; - case HW_DEACT_REQ: - hc->hw.mst_m &= ~HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - skb_queue_purge(&dch->squeue); - if (dch->tx_skb) { - dev_kfree_skb(dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - dev_kfree_skb(dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); - break; - case HW_POWERUP_REQ: - Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION); - break; - case PH_ACTIVATE_IND: - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: unknown command %x\n", - __func__, cmd); - return -1; - } - return 0; -} - -/* - * Interrupt handler - */ -static inline void -tx_birq(struct bchannel *bch) -{ - if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) - hfcpci_fill_fifo(bch); - else { - dev_kfree_skb_any(bch->tx_skb); - if (get_next_bframe(bch)) - hfcpci_fill_fifo(bch); - } -} - -static inline void -tx_dirq(struct dchannel *dch) -{ - if (dch->tx_skb && dch->tx_idx < dch->tx_skb->len) - hfcpci_fill_dfifo(dch->hw); - else { - dev_kfree_skb(dch->tx_skb); - if (get_next_dframe(dch)) - hfcpci_fill_dfifo(dch->hw); - } -} - -static irqreturn_t -hfcpci_int(int intno, void *dev_id) -{ - struct hfc_pci *hc = dev_id; - u_char exval; - struct bchannel *bch; - u_char val, stat; - - spin_lock(&hc->lock); - if (!(hc->hw.int_m2 & 0x08)) { - spin_unlock(&hc->lock); - return IRQ_NONE; /* not initialised */ - } - stat = Read_hfc(hc, HFCPCI_STATUS); - if (HFCPCI_ANYINT & stat) { - val = Read_hfc(hc, HFCPCI_INT_S1); - if (hc->dch.debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG - "HFC-PCI: stat(%02x) s1(%02x)\n", stat, val); - } else { - /* shared */ - spin_unlock(&hc->lock); - return IRQ_NONE; - } - hc->irqcnt++; - - if (hc->dch.debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG "HFC-PCI irq %x\n", val); - val &= hc->hw.int_m1; - if (val & 0x40) { /* state machine irq */ - exval = Read_hfc(hc, HFCPCI_STATES) & 0xf; - if (hc->dch.debug & DEBUG_HW_DCHANNEL) - printk(KERN_DEBUG "ph_state chg %d->%d\n", - hc->dch.state, exval); - hc->dch.state = exval; - schedule_event(&hc->dch, FLG_PHCHANGE); - val &= ~0x40; - } - if (val & 0x80) { /* timer irq */ - if (hc->hw.protocol == ISDN_P_NT_S0) { - if ((--hc->hw.nt_timer) < 0) - schedule_event(&hc->dch, FLG_PHCHANGE); - } - val &= ~0x80; - Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); - } - if (val & 0x08) { /* B1 rx */ - bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); - if (bch) - main_rec_hfcpci(bch); - else if (hc->dch.debug) - printk(KERN_DEBUG "hfcpci spurious 0x08 IRQ\n"); - } - if (val & 0x10) { /* B2 rx */ - bch = Sel_BCS(hc, 2); - if (bch) - main_rec_hfcpci(bch); - else if (hc->dch.debug) - printk(KERN_DEBUG "hfcpci spurious 0x10 IRQ\n"); - } - if (val & 0x01) { /* B1 tx */ - bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); - if (bch) - tx_birq(bch); - else if (hc->dch.debug) - printk(KERN_DEBUG "hfcpci spurious 0x01 IRQ\n"); - } - if (val & 0x02) { /* B2 tx */ - bch = Sel_BCS(hc, 2); - if (bch) - tx_birq(bch); - else if (hc->dch.debug) - printk(KERN_DEBUG "hfcpci spurious 0x02 IRQ\n"); - } - if (val & 0x20) /* D rx */ - receive_dmsg(hc); - if (val & 0x04) { /* D tx */ - if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags)) - timer_delete(&hc->dch.timer); - tx_dirq(&hc->dch); - } - spin_unlock(&hc->lock); - return IRQ_HANDLED; -} - -/* - * timer callback for D-chan busy resolution. Currently no function - */ -static void -hfcpci_dbusy_timer(struct timer_list *t) -{ -} - -/* - * activate/deactivate hardware for selected channels and mode - */ -static int -mode_hfcpci(struct bchannel *bch, int bc, int protocol) -{ - struct hfc_pci *hc = bch->hw; - int fifo2; - u_char rx_slot = 0, tx_slot = 0, pcm_mode; - - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG - "HFCPCI bchannel protocol %x-->%x ch %x-->%x\n", - bch->state, protocol, bch->nr, bc); - - fifo2 = bc; - pcm_mode = (bc >> 24) & 0xff; - if (pcm_mode) { /* PCM SLOT USE */ - if (!test_bit(HFC_CFG_PCM, &hc->cfg)) - printk(KERN_WARNING - "%s: pcm channel id without HFC_CFG_PCM\n", - __func__); - rx_slot = (bc >> 8) & 0xff; - tx_slot = (bc >> 16) & 0xff; - bc = bc & 0xff; - } else if (test_bit(HFC_CFG_PCM, &hc->cfg) && (protocol > ISDN_P_NONE)) - printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n", - __func__); - if (hc->chanlimit > 1) { - hc->hw.bswapped = 0; /* B1 and B2 normal mode */ - hc->hw.sctrl_e &= ~0x80; - } else { - if (bc & 2) { - if (protocol != ISDN_P_NONE) { - hc->hw.bswapped = 1; /* B1 and B2 exchanged */ - hc->hw.sctrl_e |= 0x80; - } else { - hc->hw.bswapped = 0; /* B1 and B2 normal mode */ - hc->hw.sctrl_e &= ~0x80; - } - fifo2 = 1; - } else { - hc->hw.bswapped = 0; /* B1 and B2 normal mode */ - hc->hw.sctrl_e &= ~0x80; - } - } - switch (protocol) { - case (-1): /* used for init */ - bch->state = -1; - bch->nr = bc; - fallthrough; - case (ISDN_P_NONE): - if (bch->state == ISDN_P_NONE) - return 0; - if (bc & 2) { - hc->hw.sctrl &= ~SCTRL_B2_ENA; - hc->hw.sctrl_r &= ~SCTRL_B2_ENA; - } else { - hc->hw.sctrl &= ~SCTRL_B1_ENA; - hc->hw.sctrl_r &= ~SCTRL_B1_ENA; - } - if (fifo2 & 2) { - hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2; - hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS | - HFCPCI_INTS_B2REC); - } else { - hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1; - hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS | - HFCPCI_INTS_B1REC); - } -#ifdef REVERSE_BITORDER - if (bch->nr & 2) - hc->hw.cirm &= 0x7f; - else - hc->hw.cirm &= 0xbf; -#endif - bch->state = ISDN_P_NONE; - bch->nr = bc; - test_and_clear_bit(FLG_HDLC, &bch->Flags); - test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); - break; - case (ISDN_P_B_RAW): - bch->state = protocol; - bch->nr = bc; - hfcpci_clear_fifo_rx(hc, (fifo2 & 2) ? 1 : 0); - hfcpci_clear_fifo_tx(hc, (fifo2 & 2) ? 1 : 0); - if (bc & 2) { - hc->hw.sctrl |= SCTRL_B2_ENA; - hc->hw.sctrl_r |= SCTRL_B2_ENA; -#ifdef REVERSE_BITORDER - hc->hw.cirm |= 0x80; -#endif - } else { - hc->hw.sctrl |= SCTRL_B1_ENA; - hc->hw.sctrl_r |= SCTRL_B1_ENA; -#ifdef REVERSE_BITORDER - hc->hw.cirm |= 0x40; -#endif - } - if (fifo2 & 2) { - hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; - if (!tics) - hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS | - HFCPCI_INTS_B2REC); - hc->hw.ctmt |= 2; - hc->hw.conn &= ~0x18; - } else { - hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; - if (!tics) - hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS | - HFCPCI_INTS_B1REC); - hc->hw.ctmt |= 1; - hc->hw.conn &= ~0x03; - } - test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); - break; - case (ISDN_P_B_HDLC): - bch->state = protocol; - bch->nr = bc; - hfcpci_clear_fifo_rx(hc, (fifo2 & 2) ? 1 : 0); - hfcpci_clear_fifo_tx(hc, (fifo2 & 2) ? 1 : 0); - if (bc & 2) { - hc->hw.sctrl |= SCTRL_B2_ENA; - hc->hw.sctrl_r |= SCTRL_B2_ENA; - } else { - hc->hw.sctrl |= SCTRL_B1_ENA; - hc->hw.sctrl_r |= SCTRL_B1_ENA; - } - if (fifo2 & 2) { - hc->hw.last_bfifo_cnt[1] = 0; - hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; - hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS | - HFCPCI_INTS_B2REC); - hc->hw.ctmt &= ~2; - hc->hw.conn &= ~0x18; - } else { - hc->hw.last_bfifo_cnt[0] = 0; - hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; - hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS | - HFCPCI_INTS_B1REC); - hc->hw.ctmt &= ~1; - hc->hw.conn &= ~0x03; - } - test_and_set_bit(FLG_HDLC, &bch->Flags); - break; - default: - printk(KERN_DEBUG "prot not known %x\n", protocol); - return -ENOPROTOOPT; - } - if (test_bit(HFC_CFG_PCM, &hc->cfg)) { - if ((protocol == ISDN_P_NONE) || - (protocol == -1)) { /* init case */ - rx_slot = 0; - tx_slot = 0; - } else { - if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { - rx_slot |= 0xC0; - tx_slot |= 0xC0; - } else { - rx_slot |= 0x80; - tx_slot |= 0x80; - } - } - if (bc & 2) { - hc->hw.conn &= 0xc7; - hc->hw.conn |= 0x08; - printk(KERN_DEBUG "%s: Write_hfc: B2_SSL 0x%x\n", - __func__, tx_slot); - printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n", - __func__, rx_slot); - Write_hfc(hc, HFCPCI_B2_SSL, tx_slot); - Write_hfc(hc, HFCPCI_B2_RSL, rx_slot); - } else { - hc->hw.conn &= 0xf8; - hc->hw.conn |= 0x01; - printk(KERN_DEBUG "%s: Write_hfc: B1_SSL 0x%x\n", - __func__, tx_slot); - printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n", - __func__, rx_slot); - Write_hfc(hc, HFCPCI_B1_SSL, tx_slot); - Write_hfc(hc, HFCPCI_B1_RSL, rx_slot); - } - } - Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); - Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); - Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); - Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); -#ifdef REVERSE_BITORDER - Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); -#endif - return 0; -} - -static int -set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan) -{ - struct hfc_pci *hc = bch->hw; - - if (bch->debug & DEBUG_HW_BCHANNEL) - printk(KERN_DEBUG - "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x\n", - bch->state, protocol, bch->nr, chan); - if (bch->nr != chan) { - printk(KERN_DEBUG - "HFCPCI rxtest wrong channel parameter %x/%x\n", - bch->nr, chan); - return -EINVAL; - } - switch (protocol) { - case (ISDN_P_B_RAW): - bch->state = protocol; - hfcpci_clear_fifo_rx(hc, (chan & 2) ? 1 : 0); - if (chan & 2) { - hc->hw.sctrl_r |= SCTRL_B2_ENA; - hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; - if (!tics) - hc->hw.int_m1 |= HFCPCI_INTS_B2REC; - hc->hw.ctmt |= 2; - hc->hw.conn &= ~0x18; -#ifdef REVERSE_BITORDER - hc->hw.cirm |= 0x80; -#endif - } else { - hc->hw.sctrl_r |= SCTRL_B1_ENA; - hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; - if (!tics) - hc->hw.int_m1 |= HFCPCI_INTS_B1REC; - hc->hw.ctmt |= 1; - hc->hw.conn &= ~0x03; -#ifdef REVERSE_BITORDER - hc->hw.cirm |= 0x40; -#endif - } - break; - case (ISDN_P_B_HDLC): - bch->state = protocol; - hfcpci_clear_fifo_rx(hc, (chan & 2) ? 1 : 0); - if (chan & 2) { - hc->hw.sctrl_r |= SCTRL_B2_ENA; - hc->hw.last_bfifo_cnt[1] = 0; - hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; - hc->hw.int_m1 |= HFCPCI_INTS_B2REC; - hc->hw.ctmt &= ~2; - hc->hw.conn &= ~0x18; - } else { - hc->hw.sctrl_r |= SCTRL_B1_ENA; - hc->hw.last_bfifo_cnt[0] = 0; - hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; - hc->hw.int_m1 |= HFCPCI_INTS_B1REC; - hc->hw.ctmt &= ~1; - hc->hw.conn &= ~0x03; - } - break; - default: - printk(KERN_DEBUG "prot not known %x\n", protocol); - return -ENOPROTOOPT; - } - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); - Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); - Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); -#ifdef REVERSE_BITORDER - Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); -#endif - return 0; -} - -static void -deactivate_bchannel(struct bchannel *bch) -{ - struct hfc_pci *hc = bch->hw; - u_long flags; - - spin_lock_irqsave(&hc->lock, flags); - mISDN_clear_bchannel(bch); - mode_hfcpci(bch, bch->nr, ISDN_P_NONE); - spin_unlock_irqrestore(&hc->lock, flags); -} - -/* - * Layer 1 B-channel hardware access - */ -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(bch, cq); -} -static int -hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hfc_pci *hc = bch->hw; - int ret = -EINVAL; - u_long flags; - - if (bch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg); - switch (cmd) { - case HW_TESTRX_RAW: - spin_lock_irqsave(&hc->lock, flags); - ret = set_hfcpci_rxtest(bch, ISDN_P_B_RAW, (int)(long)arg); - spin_unlock_irqrestore(&hc->lock, flags); - break; - case HW_TESTRX_HDLC: - spin_lock_irqsave(&hc->lock, flags); - ret = set_hfcpci_rxtest(bch, ISDN_P_B_HDLC, (int)(long)arg); - spin_unlock_irqrestore(&hc->lock, flags); - break; - case HW_TESTRX_OFF: - spin_lock_irqsave(&hc->lock, flags); - mode_hfcpci(bch, bch->nr, ISDN_P_NONE); - spin_unlock_irqrestore(&hc->lock, flags); - ret = 0; - break; - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - deactivate_bchannel(bch); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bch, arg); - break; - default: - printk(KERN_WARNING "%s: unknown prim(%x)\n", - __func__, cmd); - } - return ret; -} - -/* - * Layer2 -> Layer 1 Dchannel data - */ -static int -hfcpci_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct hfc_pci *hc = dch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned int id; - u_long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&hc->lock, flags); - ret = dchannel_senddata(dch, skb); - if (ret > 0) { /* direct TX */ - id = hh->id; /* skb can be freed */ - hfcpci_fill_dfifo(dch->hw); - ret = 0; - spin_unlock_irqrestore(&hc->lock, flags); - queue_ch_frame(ch, PH_DATA_CNF, id, NULL); - } else - spin_unlock_irqrestore(&hc->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(&hc->lock, flags); - if (hc->hw.protocol == ISDN_P_NT_S0) { - ret = 0; - if (test_bit(HFC_CFG_MASTER, &hc->cfg)) - hc->hw.mst_m |= HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - if (test_bit(FLG_ACTIVE, &dch->Flags)) { - spin_unlock_irqrestore(&hc->lock, flags); - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - break; - } - test_and_set_bit(FLG_L2_ACTIVATED, &dch->Flags); - Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | - HFCPCI_DO_ACTION | 1); - } else - ret = l1_event(dch->l1, hh->prim); - spin_unlock_irqrestore(&hc->lock, flags); - break; - case PH_DEACTIVATE_REQ: - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - spin_lock_irqsave(&hc->lock, flags); - if (hc->hw.protocol == ISDN_P_NT_S0) { - struct sk_buff_head free_queue; - - __skb_queue_head_init(&free_queue); - /* prepare deactivation */ - Write_hfc(hc, HFCPCI_STATES, 0x40); - skb_queue_splice_init(&dch->squeue, &free_queue); - if (dch->tx_skb) { - __skb_queue_tail(&free_queue, dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - __skb_queue_tail(&free_queue, dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); -#ifdef FIXME - if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) - dchannel_sched_event(&hc->dch, D_CLEARBUSY); -#endif - hc->hw.mst_m &= ~HFCPCI_MASTER; - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - ret = 0; - spin_unlock_irqrestore(&hc->lock, flags); - __skb_queue_purge(&free_queue); - } else { - ret = l1_event(dch->l1, hh->prim); - spin_unlock_irqrestore(&hc->lock, flags); - } - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -/* - * Layer2 -> Layer 1 Bchannel data - */ -static int -hfcpci_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hfc_pci *hc = bch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&hc->lock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - hfcpci_fill_fifo(bch); - ret = 0; - } - spin_unlock_irqrestore(&hc->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(&hc->lock, flags); - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) - ret = mode_hfcpci(bch, bch->nr, ch->protocol); - else - ret = 0; - spin_unlock_irqrestore(&hc->lock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - deactivate_bchannel(bch); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - ret = 0; - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -/* - * called for card init message - */ - -static void -inithfcpci(struct hfc_pci *hc) -{ - printk(KERN_DEBUG "inithfcpci: entered\n"); - timer_setup(&hc->dch.timer, hfcpci_dbusy_timer, 0); - hc->chanlimit = 2; - mode_hfcpci(&hc->bch[0], 1, -1); - mode_hfcpci(&hc->bch[1], 2, -1); -} - - -static int -init_card(struct hfc_pci *hc) -{ - int cnt = 3; - u_long flags; - - printk(KERN_DEBUG "init_card: entered\n"); - - - spin_lock_irqsave(&hc->lock, flags); - disable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - if (request_irq(hc->irq, hfcpci_int, IRQF_SHARED, "HFC PCI", hc)) { - printk(KERN_WARNING - "mISDN: couldn't get interrupt %d\n", hc->irq); - return -EIO; - } - spin_lock_irqsave(&hc->lock, flags); - reset_hfcpci(hc); - while (cnt) { - inithfcpci(hc); - /* - * Finally enable IRQ output - * this is only allowed, if an IRQ routine is already - * established for this HFC, so don't do that earlier - */ - enable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - /* Timeout 80ms */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((80 * HZ) / 1000); - printk(KERN_INFO "HFC PCI: IRQ %d count %d\n", - hc->irq, hc->irqcnt); - /* now switch timer interrupt off */ - spin_lock_irqsave(&hc->lock, flags); - hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - /* reinit mode reg */ - Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); - if (!hc->irqcnt) { - printk(KERN_WARNING - "HFC PCI: IRQ(%d) getting no interrupts " - "during init %d\n", hc->irq, 4 - cnt); - if (cnt == 1) - break; - else { - reset_hfcpci(hc); - cnt--; - } - } else { - spin_unlock_irqrestore(&hc->lock, flags); - hc->initdone = 1; - return 0; - } - } - disable_hwirq(hc); - spin_unlock_irqrestore(&hc->lock, flags); - free_irq(hc->irq, hc); - return -EIO; -} - -static int -channel_ctrl(struct hfc_pci *hc, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - u_char slot; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT | - MISDN_CTRL_DISCONNECT | MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_LOOP: - /* channel 0 disabled loop */ - if (cq->channel < 0 || cq->channel > 2) { - ret = -EINVAL; - break; - } - if (cq->channel & 1) { - if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) - slot = 0xC0; - else - slot = 0x80; - printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n", - __func__, slot); - Write_hfc(hc, HFCPCI_B1_SSL, slot); - Write_hfc(hc, HFCPCI_B1_RSL, slot); - hc->hw.conn = (hc->hw.conn & ~7) | 6; - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); - } - if (cq->channel & 2) { - if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) - slot = 0xC1; - else - slot = 0x81; - printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n", - __func__, slot); - Write_hfc(hc, HFCPCI_B2_SSL, slot); - Write_hfc(hc, HFCPCI_B2_RSL, slot); - hc->hw.conn = (hc->hw.conn & ~0x38) | 0x30; - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); - } - if (cq->channel & 3) - hc->hw.trm |= 0x80; /* enable IOM-loop */ - else { - hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09; - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); - hc->hw.trm &= 0x7f; /* disable IOM-loop */ - } - Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); - break; - case MISDN_CTRL_CONNECT: - if (cq->channel == cq->p1) { - ret = -EINVAL; - break; - } - if (cq->channel < 1 || cq->channel > 2 || - cq->p1 < 1 || cq->p1 > 2) { - ret = -EINVAL; - break; - } - if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) - slot = 0xC0; - else - slot = 0x80; - printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n", - __func__, slot); - Write_hfc(hc, HFCPCI_B1_SSL, slot); - Write_hfc(hc, HFCPCI_B2_RSL, slot); - if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) - slot = 0xC1; - else - slot = 0x81; - printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n", - __func__, slot); - Write_hfc(hc, HFCPCI_B2_SSL, slot); - Write_hfc(hc, HFCPCI_B1_RSL, slot); - hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x36; - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); - hc->hw.trm |= 0x80; - Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); - break; - case MISDN_CTRL_DISCONNECT: - hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09; - Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); - hc->hw.trm &= 0x7f; /* disable IOM-loop */ - break; - case MISDN_CTRL_L1_TIMER3: - ret = l1_event(hc->dch.l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); - break; - default: - printk(KERN_WARNING "%s: unknown Op %x\n", - __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch, - struct channel_req *rq) -{ - int err = 0; - - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, - hc->dch.dev.id, __builtin_return_address(0)); - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - if (rq->adr.channel == 1) { - /* TODO: E-Channel */ - return -EINVAL; - } - if (!hc->initdone) { - if (rq->protocol == ISDN_P_TE_S0) { - err = create_l1(&hc->dch, hfc_l1callback); - if (err) - return err; - } - hc->hw.protocol = rq->protocol; - ch->protocol = rq->protocol; - err = init_card(hc); - if (err) - return err; - } else { - if (rq->protocol != ch->protocol) { - if (hc->hw.protocol == ISDN_P_TE_S0) - l1_event(hc->dch.l1, CLOSE_CHANNEL); - if (rq->protocol == ISDN_P_TE_S0) { - err = create_l1(&hc->dch, hfc_l1callback); - if (err) - return err; - } - hc->hw.protocol = rq->protocol; - ch->protocol = rq->protocol; - hfcpci_setmode(hc); - } - } - - if (((ch->protocol == ISDN_P_NT_S0) && (hc->dch.state == 3)) || - ((ch->protocol == ISDN_P_TE_S0) && (hc->dch.state == 7))) { - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - } - rq->ch = ch; - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", __func__); - return 0; -} - -static int -open_bchannel(struct hfc_pci *hc, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - bch = &hc->bch[rq->adr.channel - 1]; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; /* TODO: E-channel */ - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", __func__); - return 0; -} - -/* - * device control function - */ -static int -hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct hfc_pci *hc = dch->hw; - struct channel_req *rq; - int err = 0; - - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", - __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if ((rq->protocol == ISDN_P_TE_S0) || - (rq->protocol == ISDN_P_NT_S0)) - err = open_dchannel(hc, ch, rq); - else - err = open_bchannel(hc, rq); - break; - case CLOSE_CHANNEL: - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: dev(%d) close from %p\n", - __func__, hc->dch.dev.id, - __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(hc, arg); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: unknown command %x\n", - __func__, cmd); - return -EINVAL; - } - return err; -} - -static int -setup_hw(struct hfc_pci *hc) -{ - void *buffer; - - printk(KERN_INFO "mISDN: HFC-PCI driver %s\n", hfcpci_revision); - hc->hw.cirm = 0; - hc->dch.state = 0; - pci_set_master(hc->pdev); - if (!hc->irq) { - printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n"); - return -EINVAL; - } - hc->hw.pci_io = - (char __iomem *)(unsigned long)hc->pdev->resource[1].start; - - if (!hc->hw.pci_io) { - printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); - return -ENOMEM; - } - /* Allocate memory for FIFOS */ - /* the memory needs to be on a 32k boundary within the first 4G */ - if (dma_set_mask(&hc->pdev->dev, 0xFFFF8000)) { - printk(KERN_WARNING - "HFC-PCI: No usable DMA configuration!\n"); - return -EIO; - } - buffer = dma_alloc_coherent(&hc->pdev->dev, 0x8000, &hc->hw.dmahandle, - GFP_KERNEL); - /* We silently assume the address is okay if nonzero */ - if (!buffer) { - printk(KERN_WARNING - "HFC-PCI: Error allocating memory for FIFO!\n"); - return -ENOMEM; - } - hc->hw.fifos = buffer; - pci_write_config_dword(hc->pdev, 0x80, hc->hw.dmahandle); - hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256); - if (unlikely(!hc->hw.pci_io)) { - printk(KERN_WARNING - "HFC-PCI: Error in ioremap for PCI!\n"); - dma_free_coherent(&hc->pdev->dev, 0x8000, hc->hw.fifos, - hc->hw.dmahandle); - return -ENOMEM; - } - - printk(KERN_INFO - "HFC-PCI: defined at mem %#lx fifo %p(%pad) IRQ %d HZ %d\n", - (u_long) hc->hw.pci_io, hc->hw.fifos, - &hc->hw.dmahandle, hc->irq, HZ); - - /* enable memory mapped ports, disable busmaster */ - pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO); - hc->hw.int_m2 = 0; - disable_hwirq(hc); - hc->hw.int_m1 = 0; - Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); - /* At this point the needed PCI config is done */ - /* fifos are still not enabled */ - timer_setup(&hc->hw.timer, hfcpci_Timer, 0); - /* default PCM master */ - test_and_set_bit(HFC_CFG_MASTER, &hc->cfg); - return 0; -} - -static void -release_card(struct hfc_pci *hc) { - u_long flags; - - spin_lock_irqsave(&hc->lock, flags); - hc->hw.int_m2 = 0; /* interrupt output off ! */ - disable_hwirq(hc); - mode_hfcpci(&hc->bch[0], 1, ISDN_P_NONE); - mode_hfcpci(&hc->bch[1], 2, ISDN_P_NONE); - if (hc->dch.timer.function != NULL) { - timer_delete(&hc->dch.timer); - hc->dch.timer.function = NULL; - } - spin_unlock_irqrestore(&hc->lock, flags); - if (hc->hw.protocol == ISDN_P_TE_S0) - l1_event(hc->dch.l1, CLOSE_CHANNEL); - if (hc->initdone) - free_irq(hc->irq, hc); - release_io_hfcpci(hc); /* must release after free_irq! */ - mISDN_unregister_device(&hc->dch.dev); - mISDN_freebchannel(&hc->bch[1]); - mISDN_freebchannel(&hc->bch[0]); - mISDN_freedchannel(&hc->dch); - pci_set_drvdata(hc->pdev, NULL); - kfree(hc); -} - -static int -setup_card(struct hfc_pci *card) -{ - int err = -EINVAL; - u_int i; - char name[MISDN_MAX_IDLEN]; - - card->dch.debug = debug; - spin_lock_init(&card->lock); - mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, ph_state); - card->dch.hw = card; - card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); - card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - card->dch.dev.D.send = hfcpci_l2l1D; - card->dch.dev.D.ctrl = hfc_dctrl; - card->dch.dev.nrbchan = 2; - for (i = 0; i < 2; i++) { - card->bch[i].nr = i + 1; - set_channelmap(i + 1, card->dch.dev.channelmap); - card->bch[i].debug = debug; - mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM, poll >> 1); - card->bch[i].hw = card; - card->bch[i].ch.send = hfcpci_l2l1B; - card->bch[i].ch.ctrl = hfc_bctrl; - card->bch[i].ch.nr = i + 1; - list_add(&card->bch[i].ch.list, &card->dch.dev.bchannels); - } - err = setup_hw(card); - if (err) - goto error; - snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1); - err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, name); - if (err) - goto error; - HFC_cnt++; - printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt); - return 0; -error: - mISDN_freebchannel(&card->bch[1]); - mISDN_freebchannel(&card->bch[0]); - mISDN_freedchannel(&card->dch); - kfree(card); - return err; -} - -/* private data in the PCI devices list */ -struct _hfc_map { - u_int subtype; - u_int flag; - char *name; -}; - -static const struct _hfc_map hfc_map[] = -{ - {HFC_CCD_2BD0, 0, "CCD/Billion/Asuscom 2BD0"}, - {HFC_CCD_B000, 0, "Billion B000"}, - {HFC_CCD_B006, 0, "Billion B006"}, - {HFC_CCD_B007, 0, "Billion B007"}, - {HFC_CCD_B008, 0, "Billion B008"}, - {HFC_CCD_B009, 0, "Billion B009"}, - {HFC_CCD_B00A, 0, "Billion B00A"}, - {HFC_CCD_B00B, 0, "Billion B00B"}, - {HFC_CCD_B00C, 0, "Billion B00C"}, - {HFC_CCD_B100, 0, "Seyeon B100"}, - {HFC_CCD_B700, 0, "Primux II S0 B700"}, - {HFC_CCD_B701, 0, "Primux II S0 NT B701"}, - {HFC_ABOCOM_2BD1, 0, "Abocom/Magitek 2BD1"}, - {HFC_ASUS_0675, 0, "Asuscom/Askey 675"}, - {HFC_BERKOM_TCONCEPT, 0, "German telekom T-Concept"}, - {HFC_BERKOM_A1T, 0, "German telekom A1T"}, - {HFC_ANIGMA_MC145575, 0, "Motorola MC145575"}, - {HFC_ZOLTRIX_2BD0, 0, "Zoltrix 2BD0"}, - {HFC_DIGI_DF_M_IOM2_E, 0, - "Digi International DataFire Micro V IOM2 (Europe)"}, - {HFC_DIGI_DF_M_E, 0, - "Digi International DataFire Micro V (Europe)"}, - {HFC_DIGI_DF_M_IOM2_A, 0, - "Digi International DataFire Micro V IOM2 (North America)"}, - {HFC_DIGI_DF_M_A, 0, - "Digi International DataFire Micro V (North America)"}, - {HFC_SITECOM_DC105V2, 0, "Sitecom Connectivity DC-105 ISDN TA"}, - {}, -}; - -static const struct pci_device_id hfc_ids[] = -{ - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_2BD0), - (unsigned long) &hfc_map[0] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B000), - (unsigned long) &hfc_map[1] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B006), - (unsigned long) &hfc_map[2] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B007), - (unsigned long) &hfc_map[3] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B008), - (unsigned long) &hfc_map[4] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B009), - (unsigned long) &hfc_map[5] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B00A), - (unsigned long) &hfc_map[6] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B00B), - (unsigned long) &hfc_map[7] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B00C), - (unsigned long) &hfc_map[8] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B100), - (unsigned long) &hfc_map[9] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B700), - (unsigned long) &hfc_map[10] }, - { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B701), - (unsigned long) &hfc_map[11] }, - { PCI_VDEVICE(ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1), - (unsigned long) &hfc_map[12] }, - { PCI_VDEVICE(ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675), - (unsigned long) &hfc_map[13] }, - { PCI_VDEVICE(BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT), - (unsigned long) &hfc_map[14] }, - { PCI_VDEVICE(BERKOM, PCI_DEVICE_ID_BERKOM_A1T), - (unsigned long) &hfc_map[15] }, - { PCI_VDEVICE(ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575), - (unsigned long) &hfc_map[16] }, - { PCI_VDEVICE(ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0), - (unsigned long) &hfc_map[17] }, - { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E), - (unsigned long) &hfc_map[18] }, - { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_E), - (unsigned long) &hfc_map[19] }, - { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A), - (unsigned long) &hfc_map[20] }, - { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_A), - (unsigned long) &hfc_map[21] }, - { PCI_VDEVICE(SITECOM, PCI_DEVICE_ID_SITECOM_DC105V2), - (unsigned long) &hfc_map[22] }, - {}, -}; - -static int -hfc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -ENOMEM; - struct hfc_pci *card; - struct _hfc_map *m = (struct _hfc_map *)ent->driver_data; - - card = kzalloc_obj(struct hfc_pci); - if (!card) { - printk(KERN_ERR "No kmem for HFC card\n"); - return err; - } - card->pdev = pdev; - card->subtype = m->subtype; - err = pci_enable_device(pdev); - if (err) { - kfree(card); - return err; - } - - printk(KERN_INFO "mISDN_hfcpci: found adapter %s at %s\n", - m->name, pci_name(pdev)); - - card->irq = pdev->irq; - pci_set_drvdata(pdev, card); - err = setup_card(card); - if (err) - pci_set_drvdata(pdev, NULL); - return err; -} - -static void -hfc_remove_pci(struct pci_dev *pdev) -{ - struct hfc_pci *card = pci_get_drvdata(pdev); - - if (card) - release_card(card); - else - if (debug) - printk(KERN_DEBUG "%s: drvdata already removed\n", - __func__); -} - - -static struct pci_driver hfc_driver = { - .name = "hfcpci", - .probe = hfc_probe, - .remove = hfc_remove_pci, - .id_table = hfc_ids, -}; - -static int -_hfcpci_softirq(struct device *dev, void *unused) -{ - struct hfc_pci *hc = dev_get_drvdata(dev); - struct bchannel *bch; - if (hc == NULL) - return 0; - - if (hc->hw.int_m2 & HFCPCI_IRQ_ENABLE) { - spin_lock_irq(&hc->lock); - bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); - if (bch && bch->state == ISDN_P_B_RAW) { /* B1 rx&tx */ - main_rec_hfcpci(bch); - tx_birq(bch); - } - bch = Sel_BCS(hc, hc->hw.bswapped ? 1 : 2); - if (bch && bch->state == ISDN_P_B_RAW) { /* B2 rx&tx */ - main_rec_hfcpci(bch); - tx_birq(bch); - } - spin_unlock_irq(&hc->lock); - } - return 0; -} - -static void -hfcpci_softirq(struct timer_list *unused) -{ - WARN_ON_ONCE(driver_for_each_device(&hfc_driver.driver, NULL, NULL, - _hfcpci_softirq) != 0); - - /* if next event would be in the past ... */ - if ((s32)(hfc_jiffies + tics - jiffies) <= 0) - hfc_jiffies = jiffies + 1; - else - hfc_jiffies += tics; - mod_timer(&hfc_tl, hfc_jiffies); -} - -static int __init -HFC_init(void) -{ - int err; - - if (!poll) - poll = HFCPCI_BTRANS_THRESHOLD; - - if (poll != HFCPCI_BTRANS_THRESHOLD) { - tics = (poll * HZ) / 8000; - if (tics < 1) - tics = 1; - poll = (tics * 8000) / HZ; - if (poll > 256 || poll < 8) { - printk(KERN_ERR "%s: Wrong poll value %d not in range " - "of 8..256.\n", __func__, poll); - err = -EINVAL; - return err; - } - } - if (poll != HFCPCI_BTRANS_THRESHOLD) { - printk(KERN_INFO "%s: Using alternative poll value of %d\n", - __func__, poll); - hfc_jiffies = jiffies + tics; - mod_timer(&hfc_tl, hfc_jiffies); - } else - tics = 0; /* indicate the use of controller's timer */ - - err = pci_register_driver(&hfc_driver); - if (err) { - if (timer_pending(&hfc_tl)) - timer_delete(&hfc_tl); - } - - return err; -} - -static void __exit -HFC_cleanup(void) -{ - timer_delete_sync(&hfc_tl); - - pci_unregister_driver(&hfc_driver); -} - -module_init(HFC_init); -module_exit(HFC_cleanup); - -MODULE_DEVICE_TABLE(pci, hfc_ids); diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c deleted file mode 100644 index 227babe83879..000000000000 --- a/drivers/isdn/hardware/mISDN/hfcsusb.c +++ /dev/null @@ -1,2157 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* hfcsusb.c - * mISDN driver for Colognechip HFC-S USB chip - * - * Copyright 2001 by Peter Sprenger (sprenger@moving-bytes.de) - * Copyright 2008 by Martin Bachem (info@bachem-it.com) - * - * module params - * debug=, default=0, with n=0xHHHHGGGG - * H - l1 driver flags described in hfcsusb.h - * G - common mISDN debug flags described at mISDNhw.h - * - * poll=, default 128 - * n : burst size of PH_DATA_IND at transparent rx data - * - * Revision: 0.3.3 (socket), 2008-11-05 - */ - -#include -#include -#include -#include -#include -#include "hfcsusb.h" - -static unsigned int debug; -static int poll = DEFAULT_TRANSP_BURST_SZ; - -static LIST_HEAD(HFClist); -static DEFINE_RWLOCK(HFClock); - - -MODULE_AUTHOR("Martin Bachem"); -MODULE_DESCRIPTION("mISDN driver for Colognechip HFC-S USB chip"); -MODULE_LICENSE("GPL"); -module_param(debug, uint, S_IRUGO | S_IWUSR); -module_param(poll, int, 0); - -static int hfcsusb_cnt; - -/* some function prototypes */ -static void hfcsusb_ph_command(struct hfcsusb *hw, u_char command); -static void release_hw(struct hfcsusb *hw); -static void reset_hfcsusb(struct hfcsusb *hw); -static void setPortMode(struct hfcsusb *hw); -static void hfcsusb_start_endpoint(struct hfcsusb *hw, int channel); -static void hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel); -static int hfcsusb_setup_bch(struct bchannel *bch, int protocol); -static void deactivate_bchannel(struct bchannel *bch); -static int hfcsusb_ph_info(struct hfcsusb *hw); - -/* start next background transfer for control channel */ -static void -ctrl_start_transfer(struct hfcsusb *hw) -{ - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - if (hw->ctrl_cnt) { - hw->ctrl_urb->pipe = hw->ctrl_out_pipe; - hw->ctrl_urb->setup_packet = (u_char *)&hw->ctrl_write; - hw->ctrl_urb->transfer_buffer = NULL; - hw->ctrl_urb->transfer_buffer_length = 0; - hw->ctrl_write.wIndex = - cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].hfcs_reg); - hw->ctrl_write.wValue = - cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].reg_val); - - usb_submit_urb(hw->ctrl_urb, GFP_ATOMIC); - } -} - -/* - * queue a control transfer request to write HFC-S USB - * chip register using CTRL resuest queue - */ -static int write_reg(struct hfcsusb *hw, __u8 reg, __u8 val) -{ - struct ctrl_buf *buf; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s reg(0x%02x) val(0x%02x)\n", - hw->name, __func__, reg, val); - - spin_lock(&hw->ctrl_lock); - if (hw->ctrl_cnt >= HFC_CTRL_BUFSIZE) { - spin_unlock(&hw->ctrl_lock); - return 1; - } - buf = &hw->ctrl_buff[hw->ctrl_in_idx]; - buf->hfcs_reg = reg; - buf->reg_val = val; - if (++hw->ctrl_in_idx >= HFC_CTRL_BUFSIZE) - hw->ctrl_in_idx = 0; - if (++hw->ctrl_cnt == 1) - ctrl_start_transfer(hw); - spin_unlock(&hw->ctrl_lock); - - return 0; -} - -/* control completion routine handling background control cmds */ -static void -ctrl_complete(struct urb *urb) -{ - struct hfcsusb *hw = (struct hfcsusb *) urb->context; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - urb->dev = hw->dev; - if (hw->ctrl_cnt) { - hw->ctrl_cnt--; /* decrement actual count */ - if (++hw->ctrl_out_idx >= HFC_CTRL_BUFSIZE) - hw->ctrl_out_idx = 0; /* pointer wrap */ - - ctrl_start_transfer(hw); /* start next transfer */ - } -} - -/* handle LED bits */ -static void -set_led_bit(struct hfcsusb *hw, signed short led_bits, int set_on) -{ - if (set_on) { - if (led_bits < 0) - hw->led_state &= ~abs(led_bits); - else - hw->led_state |= led_bits; - } else { - if (led_bits < 0) - hw->led_state |= abs(led_bits); - else - hw->led_state &= ~led_bits; - } -} - -/* handle LED requests */ -static void -handle_led(struct hfcsusb *hw, int event) -{ - struct hfcsusb_vdata *driver_info = (struct hfcsusb_vdata *) - hfcsusb_idtab[hw->vend_idx].driver_info; - __u8 tmpled; - - if (driver_info->led_scheme == LED_OFF) - return; - tmpled = hw->led_state; - - switch (event) { - case LED_POWER_ON: - set_led_bit(hw, driver_info->led_bits[0], 1); - set_led_bit(hw, driver_info->led_bits[1], 0); - set_led_bit(hw, driver_info->led_bits[2], 0); - set_led_bit(hw, driver_info->led_bits[3], 0); - break; - case LED_POWER_OFF: - set_led_bit(hw, driver_info->led_bits[0], 0); - set_led_bit(hw, driver_info->led_bits[1], 0); - set_led_bit(hw, driver_info->led_bits[2], 0); - set_led_bit(hw, driver_info->led_bits[3], 0); - break; - case LED_S0_ON: - set_led_bit(hw, driver_info->led_bits[1], 1); - break; - case LED_S0_OFF: - set_led_bit(hw, driver_info->led_bits[1], 0); - break; - case LED_B1_ON: - set_led_bit(hw, driver_info->led_bits[2], 1); - break; - case LED_B1_OFF: - set_led_bit(hw, driver_info->led_bits[2], 0); - break; - case LED_B2_ON: - set_led_bit(hw, driver_info->led_bits[3], 1); - break; - case LED_B2_OFF: - set_led_bit(hw, driver_info->led_bits[3], 0); - break; - } - - if (hw->led_state != tmpled) { - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s reg(0x%02x) val(x%02x)\n", - hw->name, __func__, - HFCUSB_P_DATA, hw->led_state); - - write_reg(hw, HFCUSB_P_DATA, hw->led_state); - } -} - -/* - * Layer2 -> Layer 1 Bchannel data - */ -static int -hfcusb_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hfcsusb *hw = bch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - u_long flags; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&hw->lock, flags); - ret = bchannel_senddata(bch, skb); - spin_unlock_irqrestore(&hw->lock, flags); - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s PH_DATA_REQ ret(%i)\n", - hw->name, __func__, ret); - if (ret > 0) - ret = 0; - return ret; - case PH_ACTIVATE_REQ: - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { - hfcsusb_start_endpoint(hw, bch->nr - 1); - ret = hfcsusb_setup_bch(bch, ch->protocol); - } else - ret = 0; - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - deactivate_bchannel(bch); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - ret = 0; - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -/* - * send full D/B channel status information - * as MPH_INFORMATION_IND - */ -static int -hfcsusb_ph_info(struct hfcsusb *hw) -{ - struct ph_info *phi; - struct dchannel *dch = &hw->dch; - int i; - - phi = kzalloc_flex(*phi, bch, dch->dev.nrbchan, GFP_ATOMIC); - if (!phi) - return -ENOMEM; - - phi->dch.ch.protocol = hw->protocol; - phi->dch.ch.Flags = dch->Flags; - phi->dch.state = dch->state; - phi->dch.num_bch = dch->dev.nrbchan; - for (i = 0; i < dch->dev.nrbchan; i++) { - phi->bch[i].protocol = hw->bch[i].ch.protocol; - phi->bch[i].Flags = hw->bch[i].Flags; - } - _queue_data(&dch->dev.D, MPH_INFORMATION_IND, MISDN_ID_ANY, - struct_size(phi, bch, dch->dev.nrbchan), phi, GFP_ATOMIC); - kfree(phi); - - return 0; -} - -/* - * Layer2 -> Layer 1 Dchannel data - */ -static int -hfcusb_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct mISDNhead *hh = mISDN_HEAD_P(skb); - struct hfcsusb *hw = dch->hw; - int ret = -EINVAL; - u_long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s: PH_DATA_REQ\n", - hw->name, __func__); - - spin_lock_irqsave(&hw->lock, flags); - ret = dchannel_senddata(dch, skb); - spin_unlock_irqrestore(&hw->lock, flags); - if (ret > 0) { - ret = 0; - queue_ch_frame(ch, PH_DATA_CNF, hh->id, NULL); - } - break; - - case PH_ACTIVATE_REQ: - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s: PH_ACTIVATE_REQ %s\n", - hw->name, __func__, - (hw->protocol == ISDN_P_NT_S0) ? "NT" : "TE"); - - if (hw->protocol == ISDN_P_NT_S0) { - ret = 0; - if (test_bit(FLG_ACTIVE, &dch->Flags)) { - _queue_data(&dch->dev.D, - PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_ATOMIC); - } else { - hfcsusb_ph_command(hw, - HFC_L1_ACTIVATE_NT); - test_and_set_bit(FLG_L2_ACTIVATED, - &dch->Flags); - } - } else { - hfcsusb_ph_command(hw, HFC_L1_ACTIVATE_TE); - ret = l1_event(dch->l1, hh->prim); - } - break; - - case PH_DEACTIVATE_REQ: - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s: PH_DEACTIVATE_REQ\n", - hw->name, __func__); - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - - if (hw->protocol == ISDN_P_NT_S0) { - struct sk_buff_head free_queue; - - __skb_queue_head_init(&free_queue); - hfcsusb_ph_command(hw, HFC_L1_DEACTIVATE_NT); - spin_lock_irqsave(&hw->lock, flags); - skb_queue_splice_init(&dch->squeue, &free_queue); - if (dch->tx_skb) { - __skb_queue_tail(&free_queue, dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - __skb_queue_tail(&free_queue, dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - spin_unlock_irqrestore(&hw->lock, flags); - __skb_queue_purge(&free_queue); -#ifdef FIXME - if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) - dchannel_sched_event(&hc->dch, D_CLEARBUSY); -#endif - ret = 0; - } else - ret = l1_event(dch->l1, hh->prim); - break; - case MPH_INFORMATION_REQ: - ret = hfcsusb_ph_info(hw); - break; - } - - return ret; -} - -/* - * Layer 1 callback function - */ -static int -hfc_l1callback(struct dchannel *dch, u_int cmd) -{ - struct hfcsusb *hw = dch->hw; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s cmd 0x%x\n", - hw->name, __func__, cmd); - - switch (cmd) { - case INFO3_P8: - case INFO3_P10: - case HW_RESET_REQ: - case HW_POWERUP_REQ: - break; - - case HW_DEACT_REQ: - skb_queue_purge(&dch->squeue); - if (dch->tx_skb) { - dev_kfree_skb(dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - dev_kfree_skb(dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - break; - case PH_ACTIVATE_IND: - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: unknown cmd %x\n", - hw->name, __func__, cmd); - return -1; - } - return hfcsusb_ph_info(hw); -} - -static int -open_dchannel(struct hfcsusb *hw, struct mISDNchannel *ch, - struct channel_req *rq) -{ - int err = 0; - - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: %s: dev(%d) open addr(%i) from %p\n", - hw->name, __func__, hw->dch.dev.id, rq->adr.channel, - __builtin_return_address(0)); - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - - test_and_clear_bit(FLG_ACTIVE, &hw->dch.Flags); - test_and_clear_bit(FLG_ACTIVE, &hw->ech.Flags); - hfcsusb_start_endpoint(hw, HFC_CHAN_D); - - /* E-Channel logging */ - if (rq->adr.channel == 1) { - if (hw->fifos[HFCUSB_PCM_RX].pipe) { - hfcsusb_start_endpoint(hw, HFC_CHAN_E); - set_bit(FLG_ACTIVE, &hw->ech.Flags); - _queue_data(&hw->ech.dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - } else - return -EINVAL; - } - - if (!hw->initdone) { - hw->protocol = rq->protocol; - if (rq->protocol == ISDN_P_TE_S0) { - err = create_l1(&hw->dch, hfc_l1callback); - if (err) - return err; - } - setPortMode(hw); - ch->protocol = rq->protocol; - hw->initdone = 1; - } else { - if (rq->protocol != ch->protocol) - return -EPROTONOSUPPORT; - } - - if (((ch->protocol == ISDN_P_NT_S0) && (hw->dch.state == 3)) || - ((ch->protocol == ISDN_P_TE_S0) && (hw->dch.state == 7))) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - rq->ch = ch; - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s: %s: cannot get module\n", - hw->name, __func__); - return 0; -} - -static int -open_bchannel(struct hfcsusb *hw, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s B%i\n", - hw->name, __func__, rq->adr.channel); - - bch = &hw->bch[rq->adr.channel - 1]; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s: %s:cannot get module\n", - hw->name, __func__); - return 0; -} - -static int -channel_ctrl(struct hfcsusb *hw, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s op(0x%x) channel(0x%x)\n", - hw->name, __func__, (cq->op), (cq->channel)); - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT | - MISDN_CTRL_DISCONNECT; - break; - default: - printk(KERN_WARNING "%s: %s: unknown Op %x\n", - hw->name, __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -/* - * device control function - */ -static int -hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct hfcsusb *hw = dch->hw; - struct channel_req *rq; - int err = 0; - - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: cmd:%x %p\n", - hw->name, __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if ((rq->protocol == ISDN_P_TE_S0) || - (rq->protocol == ISDN_P_NT_S0)) - err = open_dchannel(hw, ch, rq); - else - err = open_bchannel(hw, rq); - if (!err) - hw->open++; - break; - case CLOSE_CHANNEL: - hw->open--; - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG - "%s: %s: dev(%d) close from %p (open %d)\n", - hw->name, __func__, hw->dch.dev.id, - __builtin_return_address(0), hw->open); - if (!hw->open) { - hfcsusb_stop_endpoint(hw, HFC_CHAN_D); - if (hw->fifos[HFCUSB_PCM_RX].pipe) - hfcsusb_stop_endpoint(hw, HFC_CHAN_E); - handle_led(hw, LED_POWER_ON); - } - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(hw, arg); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: unknown command %x\n", - hw->name, __func__, cmd); - return -EINVAL; - } - return err; -} - -/* - * S0 TE state change event handler - */ -static void -ph_state_te(struct dchannel *dch) -{ - struct hfcsusb *hw = dch->hw; - - if (debug & DEBUG_HW) { - if (dch->state <= HFC_MAX_TE_LAYER1_STATE) - printk(KERN_DEBUG "%s: %s: %s\n", hw->name, __func__, - HFC_TE_LAYER1_STATES[dch->state]); - else - printk(KERN_DEBUG "%s: %s: TE F%d\n", - hw->name, __func__, dch->state); - } - - switch (dch->state) { - case 0: - l1_event(dch->l1, HW_RESET_IND); - break; - case 3: - l1_event(dch->l1, HW_DEACT_IND); - break; - case 5: - case 8: - l1_event(dch->l1, ANYSIGNAL); - break; - case 6: - l1_event(dch->l1, INFO2); - break; - case 7: - l1_event(dch->l1, INFO4_P8); - break; - } - if (dch->state == 7) - handle_led(hw, LED_S0_ON); - else - handle_led(hw, LED_S0_OFF); -} - -/* - * S0 NT state change event handler - */ -static void -ph_state_nt(struct dchannel *dch) -{ - struct hfcsusb *hw = dch->hw; - - if (debug & DEBUG_HW) { - if (dch->state <= HFC_MAX_NT_LAYER1_STATE) - printk(KERN_DEBUG "%s: %s: %s\n", - hw->name, __func__, - HFC_NT_LAYER1_STATES[dch->state]); - - else - printk(KERN_INFO DRIVER_NAME "%s: %s: NT G%d\n", - hw->name, __func__, dch->state); - } - - switch (dch->state) { - case (1): - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - hw->nt_timer = 0; - hw->timers &= ~NT_ACTIVATION_TIMER; - handle_led(hw, LED_S0_OFF); - break; - - case (2): - if (hw->nt_timer < 0) { - hw->nt_timer = 0; - hw->timers &= ~NT_ACTIVATION_TIMER; - hfcsusb_ph_command(dch->hw, HFC_L1_DEACTIVATE_NT); - } else { - hw->timers |= NT_ACTIVATION_TIMER; - hw->nt_timer = NT_T1_COUNT; - /* allow G2 -> G3 transition */ - write_reg(hw, HFCUSB_STATES, 2 | HFCUSB_NT_G2_G3); - } - break; - case (3): - hw->nt_timer = 0; - hw->timers &= ~NT_ACTIVATION_TIMER; - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - handle_led(hw, LED_S0_ON); - break; - case (4): - hw->nt_timer = 0; - hw->timers &= ~NT_ACTIVATION_TIMER; - break; - default: - break; - } - hfcsusb_ph_info(hw); -} - -static void -ph_state(struct dchannel *dch) -{ - struct hfcsusb *hw = dch->hw; - - if (hw->protocol == ISDN_P_NT_S0) - ph_state_nt(dch); - else if (hw->protocol == ISDN_P_TE_S0) - ph_state_te(dch); -} - -/* - * disable/enable BChannel for desired protocol - */ -static int -hfcsusb_setup_bch(struct bchannel *bch, int protocol) -{ - struct hfcsusb *hw = bch->hw; - __u8 conhdlc, sctrl, sctrl_r; - - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: protocol %x-->%x B%d\n", - hw->name, __func__, bch->state, protocol, - bch->nr); - - /* setup val for CON_HDLC */ - conhdlc = 0; - if (protocol > ISDN_P_NONE) - conhdlc = 8; /* enable FIFO */ - - switch (protocol) { - case (-1): /* used for init */ - bch->state = -1; - fallthrough; - case (ISDN_P_NONE): - if (bch->state == ISDN_P_NONE) - return 0; /* already in idle state */ - bch->state = ISDN_P_NONE; - clear_bit(FLG_HDLC, &bch->Flags); - clear_bit(FLG_TRANSPARENT, &bch->Flags); - break; - case (ISDN_P_B_RAW): - conhdlc |= 2; - bch->state = protocol; - set_bit(FLG_TRANSPARENT, &bch->Flags); - break; - case (ISDN_P_B_HDLC): - bch->state = protocol; - set_bit(FLG_HDLC, &bch->Flags); - break; - default: - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: prot not known %x\n", - hw->name, __func__, protocol); - return -ENOPROTOOPT; - } - - if (protocol >= ISDN_P_NONE) { - write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 0 : 2); - write_reg(hw, HFCUSB_CON_HDLC, conhdlc); - write_reg(hw, HFCUSB_INC_RES_F, 2); - write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 1 : 3); - write_reg(hw, HFCUSB_CON_HDLC, conhdlc); - write_reg(hw, HFCUSB_INC_RES_F, 2); - - sctrl = 0x40 + ((hw->protocol == ISDN_P_TE_S0) ? 0x00 : 0x04); - sctrl_r = 0x0; - if (test_bit(FLG_ACTIVE, &hw->bch[0].Flags)) { - sctrl |= 1; - sctrl_r |= 1; - } - if (test_bit(FLG_ACTIVE, &hw->bch[1].Flags)) { - sctrl |= 2; - sctrl_r |= 2; - } - write_reg(hw, HFCUSB_SCTRL, sctrl); - write_reg(hw, HFCUSB_SCTRL_R, sctrl_r); - - if (protocol > ISDN_P_NONE) - handle_led(hw, (bch->nr == 1) ? LED_B1_ON : LED_B2_ON); - else - handle_led(hw, (bch->nr == 1) ? LED_B1_OFF : - LED_B2_OFF); - } - return hfcsusb_ph_info(hw); -} - -static void -hfcsusb_ph_command(struct hfcsusb *hw, u_char command) -{ - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: %x\n", - hw->name, __func__, command); - - switch (command) { - case HFC_L1_ACTIVATE_TE: - /* force sending sending INFO1 */ - write_reg(hw, HFCUSB_STATES, 0x14); - /* start l1 activation */ - write_reg(hw, HFCUSB_STATES, 0x04); - break; - - case HFC_L1_FORCE_DEACTIVATE_TE: - write_reg(hw, HFCUSB_STATES, 0x10); - write_reg(hw, HFCUSB_STATES, 0x03); - break; - - case HFC_L1_ACTIVATE_NT: - if (hw->dch.state == 3) - _queue_data(&hw->dch.dev.D, PH_ACTIVATE_IND, - MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); - else - write_reg(hw, HFCUSB_STATES, HFCUSB_ACTIVATE | - HFCUSB_DO_ACTION | HFCUSB_NT_G2_G3); - break; - - case HFC_L1_DEACTIVATE_NT: - write_reg(hw, HFCUSB_STATES, - HFCUSB_DO_ACTION); - break; - } -} - -/* - * Layer 1 B-channel hardware access - */ -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(bch, cq); -} - -/* collect data from incoming interrupt or isochron USB data */ -static void -hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len, - int finish) -{ - struct hfcsusb *hw = fifo->hw; - struct sk_buff *rx_skb = NULL; - int maxlen = 0; - int fifon = fifo->fifonum; - int i; - int hdlc = 0; - unsigned long flags; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s: fifo(%i) len(%i) " - "dch(%p) bch(%p) ech(%p)\n", - hw->name, __func__, fifon, len, - fifo->dch, fifo->bch, fifo->ech); - - if (!len) - return; - - if ((!!fifo->dch + !!fifo->bch + !!fifo->ech) != 1) { - printk(KERN_DEBUG "%s: %s: undefined channel\n", - hw->name, __func__); - return; - } - - spin_lock_irqsave(&hw->lock, flags); - if (fifo->dch) { - rx_skb = fifo->dch->rx_skb; - maxlen = fifo->dch->maxlen; - hdlc = 1; - } - if (fifo->bch) { - if (test_bit(FLG_RX_OFF, &fifo->bch->Flags)) { - fifo->bch->dropcnt += len; - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - maxlen = bchannel_get_rxbuf(fifo->bch, len); - rx_skb = fifo->bch->rx_skb; - if (maxlen < 0) { - if (rx_skb) - skb_trim(rx_skb, 0); - pr_warn("%s.B%d: No bufferspace for %d bytes\n", - hw->name, fifo->bch->nr, len); - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - maxlen = fifo->bch->maxlen; - hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); - } - if (fifo->ech) { - rx_skb = fifo->ech->rx_skb; - maxlen = fifo->ech->maxlen; - hdlc = 1; - } - - if (fifo->dch || fifo->ech) { - if (!rx_skb) { - rx_skb = mI_alloc_skb(maxlen, GFP_ATOMIC); - if (rx_skb) { - if (fifo->dch) - fifo->dch->rx_skb = rx_skb; - if (fifo->ech) - fifo->ech->rx_skb = rx_skb; - skb_trim(rx_skb, 0); - } else { - printk(KERN_DEBUG "%s: %s: No mem for rx_skb\n", - hw->name, __func__); - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - } - /* D/E-Channel SKB range check */ - if ((rx_skb->len + len) >= MAX_DFRAME_LEN_L1) { - printk(KERN_DEBUG "%s: %s: sbk mem exceeded " - "for fifo(%d) HFCUSB_D_RX\n", - hw->name, __func__, fifon); - skb_trim(rx_skb, 0); - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - } - - skb_put_data(rx_skb, data, len); - - if (hdlc) { - /* we have a complete hdlc packet */ - if (finish) { - if ((rx_skb->len > 3) && - (!(rx_skb->data[rx_skb->len - 1]))) { - if (debug & DBG_HFC_FIFO_VERBOSE) { - printk(KERN_DEBUG "%s: %s: fifon(%i)" - " new RX len(%i): ", - hw->name, __func__, fifon, - rx_skb->len); - i = 0; - while (i < rx_skb->len) - printk("%02x ", - rx_skb->data[i++]); - printk("\n"); - } - - /* remove CRC & status */ - skb_trim(rx_skb, rx_skb->len - 3); - - if (fifo->dch) - recv_Dchannel(fifo->dch); - if (fifo->bch) - recv_Bchannel(fifo->bch, MISDN_ID_ANY, - 0); - if (fifo->ech) - recv_Echannel(fifo->ech, - &hw->dch); - } else { - if (debug & DBG_HFC_FIFO_VERBOSE) { - printk(KERN_DEBUG - "%s: CRC or minlen ERROR fifon(%i) " - "RX len(%i): ", - hw->name, fifon, rx_skb->len); - i = 0; - while (i < rx_skb->len) - printk("%02x ", - rx_skb->data[i++]); - printk("\n"); - } - skb_trim(rx_skb, 0); - } - } - } else { - /* deliver transparent data to layer2 */ - recv_Bchannel(fifo->bch, MISDN_ID_ANY, false); - } - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void -fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, - void *buf, int num_packets, int packet_size, int interval, - usb_complete_t complete, void *context) -{ - int k; - - usb_fill_bulk_urb(urb, dev, pipe, buf, packet_size * num_packets, - complete, context); - - urb->number_of_packets = num_packets; - urb->transfer_flags = URB_ISO_ASAP; - urb->actual_length = 0; - urb->interval = interval; - - for (k = 0; k < num_packets; k++) { - urb->iso_frame_desc[k].offset = packet_size * k; - urb->iso_frame_desc[k].length = packet_size; - urb->iso_frame_desc[k].actual_length = 0; - } -} - -/* receive completion routine for all ISO tx fifos */ -static void -rx_iso_complete(struct urb *urb) -{ - struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; - struct usb_fifo *fifo = context_iso_urb->owner_fifo; - struct hfcsusb *hw = fifo->hw; - int k, len, errcode, offset, num_isoc_packets, fifon, maxlen, - status, iso_status, i; - __u8 *buf; - static __u8 eof[8]; - __u8 s0_state; - unsigned long flags; - - fifon = fifo->fifonum; - status = urb->status; - - spin_lock_irqsave(&hw->lock, flags); - if (fifo->stop_gracefull) { - fifo->stop_gracefull = 0; - fifo->active = 0; - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - spin_unlock_irqrestore(&hw->lock, flags); - - /* - * ISO transfer only partially completed, - * look at individual frame status for details - */ - if (status == -EXDEV) { - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: with -EXDEV " - "urb->status %d, fifonum %d\n", - hw->name, __func__, status, fifon); - - /* clear status, so go on with ISO transfers */ - status = 0; - } - - s0_state = 0; - if (fifo->active && !status) { - num_isoc_packets = iso_packets[fifon]; - maxlen = fifo->usb_packet_maxlen; - - for (k = 0; k < num_isoc_packets; ++k) { - len = urb->iso_frame_desc[k].actual_length; - offset = urb->iso_frame_desc[k].offset; - buf = context_iso_urb->buffer + offset; - iso_status = urb->iso_frame_desc[k].status; - - if (iso_status && (debug & DBG_HFC_FIFO_VERBOSE)) { - printk(KERN_DEBUG "%s: %s: " - "ISO packet %i, status: %i\n", - hw->name, __func__, k, iso_status); - } - - /* USB data log for every D ISO in */ - if ((fifon == HFCUSB_D_RX) && - (debug & DBG_HFC_USB_VERBOSE)) { - printk(KERN_DEBUG - "%s: %s: %d (%d/%d) len(%d) ", - hw->name, __func__, urb->start_frame, - k, num_isoc_packets - 1, - len); - for (i = 0; i < len; i++) - printk("%x ", buf[i]); - printk("\n"); - } - - if (!iso_status) { - if (fifo->last_urblen != maxlen) { - /* - * save fifo fill-level threshold bits - * to use them later in TX ISO URB - * completions - */ - hw->threshold_mask = buf[1]; - - if (fifon == HFCUSB_D_RX) - s0_state = (buf[0] >> 4); - - eof[fifon] = buf[0] & 1; - if (len > 2) - hfcsusb_rx_frame(fifo, buf + 2, - len - 2, (len < maxlen) - ? eof[fifon] : 0); - } else - hfcsusb_rx_frame(fifo, buf, len, - (len < maxlen) ? - eof[fifon] : 0); - fifo->last_urblen = len; - } - } - - /* signal S0 layer1 state change */ - if ((s0_state) && (hw->initdone) && - (s0_state != hw->dch.state)) { - hw->dch.state = s0_state; - schedule_event(&hw->dch, FLG_PHCHANGE); - } - - fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe, - context_iso_urb->buffer, num_isoc_packets, - fifo->usb_packet_maxlen, fifo->intervall, - (usb_complete_t)rx_iso_complete, urb->context); - errcode = usb_submit_urb(urb, GFP_ATOMIC); - if (errcode < 0) { - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: error submitting " - "ISO URB: %d\n", - hw->name, __func__, errcode); - } - } else { - if (status && (debug & DBG_HFC_URB_INFO)) - printk(KERN_DEBUG "%s: %s: rx_iso_complete : " - "urb->status %d, fifonum %d\n", - hw->name, __func__, status, fifon); - } -} - -/* receive completion routine for all interrupt rx fifos */ -static void -rx_int_complete(struct urb *urb) -{ - int len, status, i; - __u8 *buf, maxlen, fifon; - struct usb_fifo *fifo = (struct usb_fifo *) urb->context; - struct hfcsusb *hw = fifo->hw; - static __u8 eof[8]; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - if (fifo->stop_gracefull) { - fifo->stop_gracefull = 0; - fifo->active = 0; - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - spin_unlock_irqrestore(&hw->lock, flags); - - fifon = fifo->fifonum; - if ((!fifo->active) || (urb->status)) { - if (debug & DBG_HFC_URB_ERROR) - printk(KERN_DEBUG - "%s: %s: RX-Fifo %i is going down (%i)\n", - hw->name, __func__, fifon, urb->status); - - fifo->urb->interval = 0; /* cancel automatic rescheduling */ - return; - } - len = urb->actual_length; - buf = fifo->buffer; - maxlen = fifo->usb_packet_maxlen; - - /* USB data log for every D INT in */ - if ((fifon == HFCUSB_D_RX) && (debug & DBG_HFC_USB_VERBOSE)) { - printk(KERN_DEBUG "%s: %s: D RX INT len(%d) ", - hw->name, __func__, len); - for (i = 0; i < len; i++) - printk("%02x ", buf[i]); - printk("\n"); - } - - if (fifo->last_urblen != fifo->usb_packet_maxlen) { - /* the threshold mask is in the 2nd status byte */ - hw->threshold_mask = buf[1]; - - /* signal S0 layer1 state change */ - if (hw->initdone && ((buf[0] >> 4) != hw->dch.state)) { - hw->dch.state = (buf[0] >> 4); - schedule_event(&hw->dch, FLG_PHCHANGE); - } - - eof[fifon] = buf[0] & 1; - /* if we have more than the 2 status bytes -> collect data */ - if (len > 2) - hfcsusb_rx_frame(fifo, buf + 2, - urb->actual_length - 2, - (len < maxlen) ? eof[fifon] : 0); - } else { - hfcsusb_rx_frame(fifo, buf, urb->actual_length, - (len < maxlen) ? eof[fifon] : 0); - } - fifo->last_urblen = urb->actual_length; - - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: error resubmitting USB\n", - hw->name, __func__); - } -} - -/* transmit completion routine for all ISO tx fifos */ -static void -tx_iso_complete(struct urb *urb) -{ - struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; - struct usb_fifo *fifo = context_iso_urb->owner_fifo; - struct hfcsusb *hw = fifo->hw; - struct sk_buff *tx_skb; - int k, tx_offset, num_isoc_packets, sink, remain, current_len, - errcode, hdlc, i; - int *tx_idx; - int frame_complete, fifon, status, fillempty = 0; - __u8 threshbit, *p; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - if (fifo->stop_gracefull) { - fifo->stop_gracefull = 0; - fifo->active = 0; - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - - if (fifo->dch) { - tx_skb = fifo->dch->tx_skb; - tx_idx = &fifo->dch->tx_idx; - hdlc = 1; - } else if (fifo->bch) { - tx_skb = fifo->bch->tx_skb; - tx_idx = &fifo->bch->tx_idx; - hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); - if (!tx_skb && !hdlc && - test_bit(FLG_FILLEMPTY, &fifo->bch->Flags)) - fillempty = 1; - } else { - printk(KERN_DEBUG "%s: %s: neither BCH nor DCH\n", - hw->name, __func__); - spin_unlock_irqrestore(&hw->lock, flags); - return; - } - - fifon = fifo->fifonum; - status = urb->status; - - tx_offset = 0; - - /* - * ISO transfer only partially completed, - * look at individual frame status for details - */ - if (status == -EXDEV) { - if (debug & DBG_HFC_URB_ERROR) - printk(KERN_DEBUG "%s: %s: " - "-EXDEV (%i) fifon (%d)\n", - hw->name, __func__, status, fifon); - - /* clear status, so go on with ISO transfers */ - status = 0; - } - - if (fifo->active && !status) { - /* is FifoFull-threshold set for our channel? */ - threshbit = (hw->threshold_mask & (1 << fifon)); - num_isoc_packets = iso_packets[fifon]; - - /* predict dataflow to avoid fifo overflow */ - if (fifon >= HFCUSB_D_TX) - sink = (threshbit) ? SINK_DMIN : SINK_DMAX; - else - sink = (threshbit) ? SINK_MIN : SINK_MAX; - fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe, - context_iso_urb->buffer, num_isoc_packets, - fifo->usb_packet_maxlen, fifo->intervall, - (usb_complete_t)tx_iso_complete, urb->context); - memset(context_iso_urb->buffer, 0, - sizeof(context_iso_urb->buffer)); - frame_complete = 0; - - for (k = 0; k < num_isoc_packets; ++k) { - /* analyze tx success of previous ISO packets */ - if (debug & DBG_HFC_URB_ERROR) { - errcode = urb->iso_frame_desc[k].status; - if (errcode) { - printk(KERN_DEBUG "%s: %s: " - "ISO packet %i, status: %i\n", - hw->name, __func__, k, errcode); - } - } - - /* Generate next ISO Packets */ - if (tx_skb) - remain = tx_skb->len - *tx_idx; - else if (fillempty) - remain = 15; /* > not complete */ - else - remain = 0; - - if (remain > 0) { - fifo->bit_line -= sink; - current_len = (0 - fifo->bit_line) / 8; - if (current_len > 14) - current_len = 14; - if (current_len < 0) - current_len = 0; - if (remain < current_len) - current_len = remain; - - /* how much bit do we put on the line? */ - fifo->bit_line += current_len * 8; - - context_iso_urb->buffer[tx_offset] = 0; - if (current_len == remain) { - if (hdlc) { - /* signal frame completion */ - context_iso_urb-> - buffer[tx_offset] = 1; - /* add 2 byte flags and 16bit - * CRC at end of ISDN frame */ - fifo->bit_line += 32; - } - frame_complete = 1; - } - - /* copy tx data to iso-urb buffer */ - p = context_iso_urb->buffer + tx_offset + 1; - if (fillempty) { - memset(p, fifo->bch->fill[0], - current_len); - } else { - memcpy(p, (tx_skb->data + *tx_idx), - current_len); - *tx_idx += current_len; - } - urb->iso_frame_desc[k].offset = tx_offset; - urb->iso_frame_desc[k].length = current_len + 1; - - /* USB data log for every D ISO out */ - if ((fifon == HFCUSB_D_RX) && !fillempty && - (debug & DBG_HFC_USB_VERBOSE)) { - printk(KERN_DEBUG - "%s: %s (%d/%d) offs(%d) len(%d) ", - hw->name, __func__, - k, num_isoc_packets - 1, - urb->iso_frame_desc[k].offset, - urb->iso_frame_desc[k].length); - - for (i = urb->iso_frame_desc[k].offset; - i < (urb->iso_frame_desc[k].offset - + urb->iso_frame_desc[k].length); - i++) - printk("%x ", - context_iso_urb->buffer[i]); - - printk(" skb->len(%i) tx-idx(%d)\n", - tx_skb->len, *tx_idx); - } - - tx_offset += (current_len + 1); - } else { - urb->iso_frame_desc[k].offset = tx_offset++; - urb->iso_frame_desc[k].length = 1; - /* we lower data margin every msec */ - fifo->bit_line -= sink; - if (fifo->bit_line < BITLINE_INF) - fifo->bit_line = BITLINE_INF; - } - - if (frame_complete) { - frame_complete = 0; - - if (debug & DBG_HFC_FIFO_VERBOSE) { - printk(KERN_DEBUG "%s: %s: " - "fifon(%i) new TX len(%i): ", - hw->name, __func__, - fifon, tx_skb->len); - i = 0; - while (i < tx_skb->len) - printk("%02x ", - tx_skb->data[i++]); - printk("\n"); - } - - dev_consume_skb_irq(tx_skb); - tx_skb = NULL; - if (fifo->dch && get_next_dframe(fifo->dch)) - tx_skb = fifo->dch->tx_skb; - else if (fifo->bch && - get_next_bframe(fifo->bch)) - tx_skb = fifo->bch->tx_skb; - } - } - errcode = usb_submit_urb(urb, GFP_ATOMIC); - if (errcode < 0) { - if (debug & DEBUG_HW) - printk(KERN_DEBUG - "%s: %s: error submitting ISO URB: %d \n", - hw->name, __func__, errcode); - } - - /* - * abuse DChannel tx iso completion to trigger NT mode state - * changes tx_iso_complete is assumed to be called every - * fifo->intervall (ms) - */ - if ((fifon == HFCUSB_D_TX) && (hw->protocol == ISDN_P_NT_S0) - && (hw->timers & NT_ACTIVATION_TIMER)) { - if ((--hw->nt_timer) < 0) - schedule_event(&hw->dch, FLG_PHCHANGE); - } - - } else { - if (status && (debug & DBG_HFC_URB_ERROR)) - printk(KERN_DEBUG "%s: %s: urb->status %s (%i)" - "fifonum=%d\n", - hw->name, __func__, - symbolic(urb_errlist, status), status, fifon); - } - spin_unlock_irqrestore(&hw->lock, flags); -} - -/* - * allocs urbs and start isoc transfer with two pending urbs to avoid - * gaps in the transfer chain - */ -static int -start_isoc_chain(struct usb_fifo *fifo, int num_packets_per_urb, - usb_complete_t complete, int packet_size) -{ - struct hfcsusb *hw = fifo->hw; - int i, k, errcode; - - if (debug) - printk(KERN_DEBUG "%s: %s: fifo %i\n", - hw->name, __func__, fifo->fifonum); - - /* allocate Memory for Iso out Urbs */ - for (i = 0; i < 2; i++) { - if (!(fifo->iso[i].urb)) { - fifo->iso[i].urb = - usb_alloc_urb(num_packets_per_urb, GFP_KERNEL); - if (!(fifo->iso[i].urb)) { - printk(KERN_DEBUG - "%s: %s: alloc urb for fifo %i failed", - hw->name, __func__, fifo->fifonum); - continue; - } - fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo; - fifo->iso[i].indx = i; - - /* Init the first iso */ - if (ISO_BUFFER_SIZE >= - (fifo->usb_packet_maxlen * - num_packets_per_urb)) { - fill_isoc_urb(fifo->iso[i].urb, - fifo->hw->dev, fifo->pipe, - fifo->iso[i].buffer, - num_packets_per_urb, - fifo->usb_packet_maxlen, - fifo->intervall, complete, - &fifo->iso[i]); - memset(fifo->iso[i].buffer, 0, - sizeof(fifo->iso[i].buffer)); - - for (k = 0; k < num_packets_per_urb; k++) { - fifo->iso[i].urb-> - iso_frame_desc[k].offset = - k * packet_size; - fifo->iso[i].urb-> - iso_frame_desc[k].length = - packet_size; - } - } else { - printk(KERN_DEBUG - "%s: %s: ISO Buffer size to small!\n", - hw->name, __func__); - } - } - fifo->bit_line = BITLINE_INF; - - errcode = usb_submit_urb(fifo->iso[i].urb, GFP_KERNEL); - fifo->active = (errcode >= 0) ? 1 : 0; - fifo->stop_gracefull = 0; - if (errcode < 0) { - printk(KERN_DEBUG "%s: %s: %s URB nr:%d\n", - hw->name, __func__, - symbolic(urb_errlist, errcode), i); - } - } - return fifo->active; -} - -static void -stop_iso_gracefull(struct usb_fifo *fifo) -{ - struct hfcsusb *hw = fifo->hw; - int i, timeout; - u_long flags; - - for (i = 0; i < 2; i++) { - spin_lock_irqsave(&hw->lock, flags); - if (debug) - printk(KERN_DEBUG "%s: %s for fifo %i.%i\n", - hw->name, __func__, fifo->fifonum, i); - fifo->stop_gracefull = 1; - spin_unlock_irqrestore(&hw->lock, flags); - } - - for (i = 0; i < 2; i++) { - timeout = 3; - while (fifo->stop_gracefull && timeout--) - schedule_timeout_interruptible((HZ / 1000) * 16); - if (debug && fifo->stop_gracefull) - printk(KERN_DEBUG "%s: ERROR %s for fifo %i.%i\n", - hw->name, __func__, fifo->fifonum, i); - } -} - -static void -stop_int_gracefull(struct usb_fifo *fifo) -{ - struct hfcsusb *hw = fifo->hw; - int timeout; - u_long flags; - - spin_lock_irqsave(&hw->lock, flags); - if (debug) - printk(KERN_DEBUG "%s: %s for fifo %i\n", - hw->name, __func__, fifo->fifonum); - fifo->stop_gracefull = 1; - spin_unlock_irqrestore(&hw->lock, flags); - - timeout = 3; - while (fifo->stop_gracefull && timeout--) - schedule_timeout_interruptible((HZ / 1000) * 3); - if (debug && fifo->stop_gracefull) - printk(KERN_DEBUG "%s: ERROR %s for fifo %i\n", - hw->name, __func__, fifo->fifonum); -} - -/* start the interrupt transfer for the given fifo */ -static void -start_int_fifo(struct usb_fifo *fifo) -{ - struct hfcsusb *hw = fifo->hw; - int errcode; - - if (debug) - printk(KERN_DEBUG "%s: %s: INT IN fifo:%d\n", - hw->name, __func__, fifo->fifonum); - - if (!fifo->urb) { - fifo->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!fifo->urb) - return; - } - usb_fill_int_urb(fifo->urb, fifo->hw->dev, fifo->pipe, - fifo->buffer, fifo->usb_packet_maxlen, - (usb_complete_t)rx_int_complete, fifo, fifo->intervall); - fifo->active = 1; - fifo->stop_gracefull = 0; - errcode = usb_submit_urb(fifo->urb, GFP_KERNEL); - if (errcode) { - printk(KERN_DEBUG "%s: %s: submit URB: status:%i\n", - hw->name, __func__, errcode); - fifo->active = 0; - } -} - -static void -setPortMode(struct hfcsusb *hw) -{ - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s %s\n", hw->name, __func__, - (hw->protocol == ISDN_P_TE_S0) ? "TE" : "NT"); - - if (hw->protocol == ISDN_P_TE_S0) { - write_reg(hw, HFCUSB_SCTRL, 0x40); - write_reg(hw, HFCUSB_SCTRL_E, 0x00); - write_reg(hw, HFCUSB_CLKDEL, CLKDEL_TE); - write_reg(hw, HFCUSB_STATES, 3 | 0x10); - write_reg(hw, HFCUSB_STATES, 3); - } else { - write_reg(hw, HFCUSB_SCTRL, 0x44); - write_reg(hw, HFCUSB_SCTRL_E, 0x09); - write_reg(hw, HFCUSB_CLKDEL, CLKDEL_NT); - write_reg(hw, HFCUSB_STATES, 1 | 0x10); - write_reg(hw, HFCUSB_STATES, 1); - } -} - -static void -reset_hfcsusb(struct hfcsusb *hw) -{ - struct usb_fifo *fifo; - int i; - - if (debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - /* do Chip reset */ - write_reg(hw, HFCUSB_CIRM, 8); - - /* aux = output, reset off */ - write_reg(hw, HFCUSB_CIRM, 0x10); - - /* set USB_SIZE to match the wMaxPacketSize for INT or BULK transfers */ - write_reg(hw, HFCUSB_USB_SIZE, (hw->packet_size / 8) | - ((hw->packet_size / 8) << 4)); - - /* set USB_SIZE_I to match the wMaxPacketSize for ISO transfers */ - write_reg(hw, HFCUSB_USB_SIZE_I, hw->iso_packet_size); - - /* enable PCM/GCI master mode */ - write_reg(hw, HFCUSB_MST_MODE1, 0); /* set default values */ - write_reg(hw, HFCUSB_MST_MODE0, 1); /* enable master mode */ - - /* init the fifos */ - write_reg(hw, HFCUSB_F_THRES, - (HFCUSB_TX_THRESHOLD / 8) | ((HFCUSB_RX_THRESHOLD / 8) << 4)); - - fifo = hw->fifos; - for (i = 0; i < HFCUSB_NUM_FIFOS; i++) { - write_reg(hw, HFCUSB_FIFO, i); /* select the desired fifo */ - fifo[i].max_size = - (i <= HFCUSB_B2_RX) ? MAX_BCH_SIZE : MAX_DFRAME_LEN; - fifo[i].last_urblen = 0; - - /* set 2 bit for D- & E-channel */ - write_reg(hw, HFCUSB_HDLC_PAR, ((i <= HFCUSB_B2_RX) ? 0 : 2)); - - /* enable all fifos */ - if (i == HFCUSB_D_TX) - write_reg(hw, HFCUSB_CON_HDLC, - (hw->protocol == ISDN_P_NT_S0) ? 0x08 : 0x09); - else - write_reg(hw, HFCUSB_CON_HDLC, 0x08); - write_reg(hw, HFCUSB_INC_RES_F, 2); /* reset the fifo */ - } - - write_reg(hw, HFCUSB_SCTRL_R, 0); /* disable both B receivers */ - handle_led(hw, LED_POWER_ON); -} - -/* start USB data pipes dependand on device's endpoint configuration */ -static void -hfcsusb_start_endpoint(struct hfcsusb *hw, int channel) -{ - /* quick check if endpoint already running */ - if ((channel == HFC_CHAN_D) && (hw->fifos[HFCUSB_D_RX].active)) - return; - if ((channel == HFC_CHAN_B1) && (hw->fifos[HFCUSB_B1_RX].active)) - return; - if ((channel == HFC_CHAN_B2) && (hw->fifos[HFCUSB_B2_RX].active)) - return; - if ((channel == HFC_CHAN_E) && (hw->fifos[HFCUSB_PCM_RX].active)) - return; - - /* start rx endpoints using USB INT IN method */ - if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO) - start_int_fifo(hw->fifos + channel * 2 + 1); - - /* start rx endpoints using USB ISO IN method */ - if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) { - switch (channel) { - case HFC_CHAN_D: - start_isoc_chain(hw->fifos + HFCUSB_D_RX, - ISOC_PACKETS_D, - (usb_complete_t)rx_iso_complete, - 16); - break; - case HFC_CHAN_E: - start_isoc_chain(hw->fifos + HFCUSB_PCM_RX, - ISOC_PACKETS_D, - (usb_complete_t)rx_iso_complete, - 16); - break; - case HFC_CHAN_B1: - start_isoc_chain(hw->fifos + HFCUSB_B1_RX, - ISOC_PACKETS_B, - (usb_complete_t)rx_iso_complete, - 16); - break; - case HFC_CHAN_B2: - start_isoc_chain(hw->fifos + HFCUSB_B2_RX, - ISOC_PACKETS_B, - (usb_complete_t)rx_iso_complete, - 16); - break; - } - } - - /* start tx endpoints using USB ISO OUT method */ - switch (channel) { - case HFC_CHAN_D: - start_isoc_chain(hw->fifos + HFCUSB_D_TX, - ISOC_PACKETS_B, - (usb_complete_t)tx_iso_complete, 1); - break; - case HFC_CHAN_B1: - start_isoc_chain(hw->fifos + HFCUSB_B1_TX, - ISOC_PACKETS_D, - (usb_complete_t)tx_iso_complete, 1); - break; - case HFC_CHAN_B2: - start_isoc_chain(hw->fifos + HFCUSB_B2_TX, - ISOC_PACKETS_B, - (usb_complete_t)tx_iso_complete, 1); - break; - } -} - -/* stop USB data pipes dependand on device's endpoint configuration */ -static void -hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel) -{ - /* quick check if endpoint currently running */ - if ((channel == HFC_CHAN_D) && (!hw->fifos[HFCUSB_D_RX].active)) - return; - if ((channel == HFC_CHAN_B1) && (!hw->fifos[HFCUSB_B1_RX].active)) - return; - if ((channel == HFC_CHAN_B2) && (!hw->fifos[HFCUSB_B2_RX].active)) - return; - if ((channel == HFC_CHAN_E) && (!hw->fifos[HFCUSB_PCM_RX].active)) - return; - - /* rx endpoints using USB INT IN method */ - if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO) - stop_int_gracefull(hw->fifos + channel * 2 + 1); - - /* rx endpoints using USB ISO IN method */ - if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) - stop_iso_gracefull(hw->fifos + channel * 2 + 1); - - /* tx endpoints using USB ISO OUT method */ - if (channel != HFC_CHAN_E) - stop_iso_gracefull(hw->fifos + channel * 2); -} - - -/* Hardware Initialization */ -static int -setup_hfcsusb(struct hfcsusb *hw) -{ - void *dmabuf = kmalloc_obj(u_char); - u_char b; - int ret; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - if (!dmabuf) - return -ENOMEM; - - ret = read_reg_atomic(hw, HFCUSB_CHIP_ID, dmabuf); - - memcpy(&b, dmabuf, sizeof(u_char)); - kfree(dmabuf); - - /* check the chip id */ - if (ret != 1) { - printk(KERN_DEBUG "%s: %s: cannot read chip id\n", - hw->name, __func__); - return 1; - } - if (b != HFCUSB_CHIPID) { - printk(KERN_DEBUG "%s: %s: Invalid chip id 0x%02x\n", - hw->name, __func__, b); - return 1; - } - - /* first set the needed config, interface and alternate */ - (void) usb_set_interface(hw->dev, hw->if_used, hw->alt_used); - - hw->led_state = 0; - - /* init the background machinery for control requests */ - hw->ctrl_read.bRequestType = 0xc0; - hw->ctrl_read.bRequest = 1; - hw->ctrl_read.wLength = cpu_to_le16(1); - hw->ctrl_write.bRequestType = 0x40; - hw->ctrl_write.bRequest = 0; - hw->ctrl_write.wLength = 0; - usb_fill_control_urb(hw->ctrl_urb, hw->dev, hw->ctrl_out_pipe, - (u_char *)&hw->ctrl_write, NULL, 0, - (usb_complete_t)ctrl_complete, hw); - - reset_hfcsusb(hw); - return 0; -} - -static void -release_hw(struct hfcsusb *hw) -{ - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - /* - * stop all endpoints gracefully - * TODO: mISDN_core should generate CLOSE_CHANNEL - * signals after calling mISDN_unregister_device() - */ - hfcsusb_stop_endpoint(hw, HFC_CHAN_D); - hfcsusb_stop_endpoint(hw, HFC_CHAN_B1); - hfcsusb_stop_endpoint(hw, HFC_CHAN_B2); - if (hw->fifos[HFCUSB_PCM_RX].pipe) - hfcsusb_stop_endpoint(hw, HFC_CHAN_E); - if (hw->protocol == ISDN_P_TE_S0) - l1_event(hw->dch.l1, CLOSE_CHANNEL); - - mISDN_unregister_device(&hw->dch.dev); - mISDN_freebchannel(&hw->bch[1]); - mISDN_freebchannel(&hw->bch[0]); - mISDN_freedchannel(&hw->dch); - - if (hw->ctrl_urb) { - usb_kill_urb(hw->ctrl_urb); - usb_free_urb(hw->ctrl_urb); - hw->ctrl_urb = NULL; - } - - if (hw->intf) - usb_set_intfdata(hw->intf, NULL); - list_del(&hw->list); - kfree(hw); - hw = NULL; -} - -static void -deactivate_bchannel(struct bchannel *bch) -{ - struct hfcsusb *hw = bch->hw; - u_long flags; - - if (bch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: %s: bch->nr(%i)\n", - hw->name, __func__, bch->nr); - - spin_lock_irqsave(&hw->lock, flags); - mISDN_clear_bchannel(bch); - spin_unlock_irqrestore(&hw->lock, flags); - hfcsusb_setup_bch(bch, ISDN_P_NONE); - hfcsusb_stop_endpoint(hw, bch->nr - 1); -} - -/* - * Layer 1 B-channel hardware access - */ -static int -hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - int ret = -EINVAL; - - if (bch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg); - - switch (cmd) { - case HW_TESTRX_RAW: - case HW_TESTRX_HDLC: - case HW_TESTRX_OFF: - ret = -EINVAL; - break; - - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - deactivate_bchannel(bch); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bch, arg); - break; - default: - printk(KERN_WARNING "%s: unknown prim(%x)\n", - __func__, cmd); - } - return ret; -} - -static int -setup_instance(struct hfcsusb *hw, struct device *parent) -{ - u_long flags; - int err, i; - - if (debug & DBG_HFC_CALL_TRACE) - printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); - - spin_lock_init(&hw->ctrl_lock); - spin_lock_init(&hw->lock); - - mISDN_initdchannel(&hw->dch, MAX_DFRAME_LEN_L1, ph_state); - hw->dch.debug = debug & 0xFFFF; - hw->dch.hw = hw; - hw->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); - hw->dch.dev.D.send = hfcusb_l2l1D; - hw->dch.dev.D.ctrl = hfc_dctrl; - - /* enable E-Channel logging */ - if (hw->fifos[HFCUSB_PCM_RX].pipe) - mISDN_initdchannel(&hw->ech, MAX_DFRAME_LEN_L1, NULL); - - hw->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - hw->dch.dev.nrbchan = 2; - for (i = 0; i < 2; i++) { - hw->bch[i].nr = i + 1; - set_channelmap(i + 1, hw->dch.dev.channelmap); - hw->bch[i].debug = debug; - mISDN_initbchannel(&hw->bch[i], MAX_DATA_MEM, poll >> 1); - hw->bch[i].hw = hw; - hw->bch[i].ch.send = hfcusb_l2l1B; - hw->bch[i].ch.ctrl = hfc_bctrl; - hw->bch[i].ch.nr = i + 1; - list_add(&hw->bch[i].ch.list, &hw->dch.dev.bchannels); - } - - hw->fifos[HFCUSB_B1_TX].bch = &hw->bch[0]; - hw->fifos[HFCUSB_B1_RX].bch = &hw->bch[0]; - hw->fifos[HFCUSB_B2_TX].bch = &hw->bch[1]; - hw->fifos[HFCUSB_B2_RX].bch = &hw->bch[1]; - hw->fifos[HFCUSB_D_TX].dch = &hw->dch; - hw->fifos[HFCUSB_D_RX].dch = &hw->dch; - hw->fifos[HFCUSB_PCM_RX].ech = &hw->ech; - hw->fifos[HFCUSB_PCM_TX].ech = &hw->ech; - - err = setup_hfcsusb(hw); - if (err) - goto out; - - snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s.%d", DRIVER_NAME, - hfcsusb_cnt + 1); - printk(KERN_INFO "%s: registered as '%s'\n", - DRIVER_NAME, hw->name); - - err = mISDN_register_device(&hw->dch.dev, parent, hw->name); - if (err) - goto out; - - hfcsusb_cnt++; - write_lock_irqsave(&HFClock, flags); - list_add_tail(&hw->list, &HFClist); - write_unlock_irqrestore(&HFClock, flags); - return 0; - -out: - mISDN_freebchannel(&hw->bch[1]); - mISDN_freebchannel(&hw->bch[0]); - mISDN_freedchannel(&hw->dch); - return err; -} - -static int -hfcsusb_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - int err; - struct hfcsusb *hw; - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_host_interface *iface = intf->cur_altsetting; - struct usb_host_interface *iface_used = NULL; - struct usb_host_endpoint *ep; - struct hfcsusb_vdata *driver_info; - int ifnum = iface->desc.bInterfaceNumber, i, idx, alt_idx, - probe_alt_setting, vend_idx, cfg_used, *vcf, attr, cfg_found, - ep_addr, cmptbl[16], small_match, iso_packet_size, packet_size, - alt_used = 0; - - vend_idx = 0xffff; - for (i = 0; hfcsusb_idtab[i].idVendor; i++) { - if ((le16_to_cpu(dev->descriptor.idVendor) - == hfcsusb_idtab[i].idVendor) && - (le16_to_cpu(dev->descriptor.idProduct) - == hfcsusb_idtab[i].idProduct)) { - vend_idx = i; - continue; - } - } - - printk(KERN_DEBUG - "%s: interface(%d) actalt(%d) minor(%d) vend_idx(%d)\n", - __func__, ifnum, iface->desc.bAlternateSetting, - intf->minor, vend_idx); - - if (vend_idx == 0xffff) { - printk(KERN_WARNING - "%s: no valid vendor found in USB descriptor\n", - __func__); - return -EIO; - } - /* if vendor and product ID is OK, start probing alternate settings */ - alt_idx = 0; - small_match = -1; - - /* default settings */ - iso_packet_size = 16; - packet_size = 64; - - while (alt_idx < intf->num_altsetting) { - iface = intf->altsetting + alt_idx; - probe_alt_setting = iface->desc.bAlternateSetting; - cfg_used = 0; - - while (validconf[cfg_used][0]) { - cfg_found = 1; - vcf = validconf[cfg_used]; - ep = iface->endpoint; - memcpy(cmptbl, vcf, 16 * sizeof(int)); - - /* check for all endpoints in this alternate setting */ - for (i = 0; i < iface->desc.bNumEndpoints; i++) { - ep_addr = ep->desc.bEndpointAddress; - - /* get endpoint base */ - idx = ((ep_addr & 0x7f) - 1) * 2; - if (idx > 15) - return -EIO; - - if (ep_addr & 0x80) - idx++; - attr = ep->desc.bmAttributes; - - if (cmptbl[idx] != EP_NOP) { - if (cmptbl[idx] == EP_NUL) - cfg_found = 0; - if (attr == USB_ENDPOINT_XFER_INT - && cmptbl[idx] == EP_INT) - cmptbl[idx] = EP_NUL; - if (attr == USB_ENDPOINT_XFER_BULK - && cmptbl[idx] == EP_BLK) - cmptbl[idx] = EP_NUL; - if (attr == USB_ENDPOINT_XFER_ISOC - && cmptbl[idx] == EP_ISO) - cmptbl[idx] = EP_NUL; - - if (attr == USB_ENDPOINT_XFER_INT && - ep->desc.bInterval < vcf[17]) { - cfg_found = 0; - } - } - ep++; - } - - for (i = 0; i < 16; i++) - if (cmptbl[i] != EP_NOP && cmptbl[i] != EP_NUL) - cfg_found = 0; - - if (cfg_found) { - if (small_match < cfg_used) { - small_match = cfg_used; - alt_used = probe_alt_setting; - iface_used = iface; - } - } - cfg_used++; - } - alt_idx++; - } /* (alt_idx < intf->num_altsetting) */ - - /* not found a valid USB Ta Endpoint config */ - if (small_match == -1) - return -EIO; - - iface = iface_used; - hw = kzalloc_obj(struct hfcsusb); - if (!hw) - return -ENOMEM; /* got no mem */ - snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s", DRIVER_NAME); - - ep = iface->endpoint; - vcf = validconf[small_match]; - - for (i = 0; i < iface->desc.bNumEndpoints; i++) { - struct usb_fifo *f; - - ep_addr = ep->desc.bEndpointAddress; - /* get endpoint base */ - idx = ((ep_addr & 0x7f) - 1) * 2; - if (ep_addr & 0x80) - idx++; - f = &hw->fifos[idx & 7]; - - /* init Endpoints */ - if (vcf[idx] == EP_NOP || vcf[idx] == EP_NUL) { - ep++; - continue; - } - switch (ep->desc.bmAttributes) { - case USB_ENDPOINT_XFER_INT: - f->pipe = usb_rcvintpipe(dev, - ep->desc.bEndpointAddress); - f->usb_transfer_mode = USB_INT; - packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); - break; - case USB_ENDPOINT_XFER_BULK: - if (ep_addr & 0x80) - f->pipe = usb_rcvbulkpipe(dev, - ep->desc.bEndpointAddress); - else - f->pipe = usb_sndbulkpipe(dev, - ep->desc.bEndpointAddress); - f->usb_transfer_mode = USB_BULK; - packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); - break; - case USB_ENDPOINT_XFER_ISOC: - if (ep_addr & 0x80) - f->pipe = usb_rcvisocpipe(dev, - ep->desc.bEndpointAddress); - else - f->pipe = usb_sndisocpipe(dev, - ep->desc.bEndpointAddress); - f->usb_transfer_mode = USB_ISOC; - iso_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); - break; - default: - f->pipe = 0; - } - - if (f->pipe) { - f->fifonum = idx & 7; - f->hw = hw; - f->usb_packet_maxlen = - le16_to_cpu(ep->desc.wMaxPacketSize); - f->intervall = ep->desc.bInterval; - } - ep++; - } - hw->dev = dev; /* save device */ - hw->if_used = ifnum; /* save used interface */ - hw->alt_used = alt_used; /* and alternate config */ - hw->ctrl_paksize = dev->descriptor.bMaxPacketSize0; /* control size */ - hw->cfg_used = vcf[16]; /* store used config */ - hw->vend_idx = vend_idx; /* store found vendor */ - hw->packet_size = packet_size; - hw->iso_packet_size = iso_packet_size; - - /* create the control pipes needed for register access */ - hw->ctrl_in_pipe = usb_rcvctrlpipe(hw->dev, 0); - hw->ctrl_out_pipe = usb_sndctrlpipe(hw->dev, 0); - - driver_info = (struct hfcsusb_vdata *) - hfcsusb_idtab[vend_idx].driver_info; - - hw->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!hw->ctrl_urb) { - pr_warn("%s: No memory for control urb\n", - driver_info->vend_name); - err = -ENOMEM; - goto err_free_hw; - } - - pr_info("%s: %s: detected \"%s\" (%s, if=%d alt=%d)\n", - hw->name, __func__, driver_info->vend_name, - conf_str[small_match], ifnum, alt_used); - - if (setup_instance(hw, dev->dev.parent)) { - err = -EIO; - goto err_free_urb; - } - - hw->intf = intf; - usb_set_intfdata(hw->intf, hw); - return 0; - -err_free_urb: - usb_free_urb(hw->ctrl_urb); -err_free_hw: - kfree(hw); - return err; -} - -/* function called when an active device is removed */ -static void -hfcsusb_disconnect(struct usb_interface *intf) -{ - struct hfcsusb *hw = usb_get_intfdata(intf); - struct hfcsusb *next; - int cnt = 0; - - printk(KERN_INFO "%s: device disconnected\n", hw->name); - - handle_led(hw, LED_POWER_OFF); - release_hw(hw); - - list_for_each_entry_safe(hw, next, &HFClist, list) - cnt++; - if (!cnt) - hfcsusb_cnt = 0; - - usb_set_intfdata(intf, NULL); -} - -static struct usb_driver hfcsusb_drv = { - .name = DRIVER_NAME, - .id_table = hfcsusb_idtab, - .probe = hfcsusb_probe, - .disconnect = hfcsusb_disconnect, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(hfcsusb_drv); diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.h b/drivers/isdn/hardware/mISDN/hfcsusb.h deleted file mode 100644 index 7e2bc5068019..000000000000 --- a/drivers/isdn/hardware/mISDN/hfcsusb.h +++ /dev/null @@ -1,425 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * hfcsusb.h, HFC-S USB mISDN driver - */ - -#ifndef __HFCSUSB_H__ -#define __HFCSUSB_H__ - - -#define DRIVER_NAME "HFC-S_USB" - -#define DBG_HFC_CALL_TRACE 0x00010000 -#define DBG_HFC_FIFO_VERBOSE 0x00020000 -#define DBG_HFC_USB_VERBOSE 0x00100000 -#define DBG_HFC_URB_INFO 0x00200000 -#define DBG_HFC_URB_ERROR 0x00400000 - -#define DEFAULT_TRANSP_BURST_SZ 128 - -#define HFC_CTRL_TIMEOUT 20 /* 5ms timeout writing/reading regs */ -#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ -#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ - -/* hfcsusb Layer1 commands */ -#define HFC_L1_ACTIVATE_TE 1 -#define HFC_L1_ACTIVATE_NT 2 -#define HFC_L1_DEACTIVATE_NT 3 -#define HFC_L1_FORCE_DEACTIVATE_TE 4 - -/* cmd FLAGS in HFCUSB_STATES register */ -#define HFCUSB_LOAD_STATE 0x10 -#define HFCUSB_ACTIVATE 0x20 -#define HFCUSB_DO_ACTION 0x40 -#define HFCUSB_NT_G2_G3 0x80 - -/* timers */ -#define NT_ACTIVATION_TIMER 0x01 /* enables NT mode activation Timer */ -#define NT_T1_COUNT 10 - -#define MAX_BCH_SIZE 2048 /* allowed B-channel packet size */ - -#define HFCUSB_RX_THRESHOLD 64 /* threshold for fifo report bit rx */ -#define HFCUSB_TX_THRESHOLD 96 /* threshold for fifo report bit tx */ - -#define HFCUSB_CHIP_ID 0x16 /* Chip ID register index */ -#define HFCUSB_CIRM 0x00 /* cirm register index */ -#define HFCUSB_USB_SIZE 0x07 /* int length register */ -#define HFCUSB_USB_SIZE_I 0x06 /* iso length register */ -#define HFCUSB_F_CROSS 0x0b /* bit order register */ -#define HFCUSB_CLKDEL 0x37 /* bit delay register */ -#define HFCUSB_CON_HDLC 0xfa /* channel connect register */ -#define HFCUSB_HDLC_PAR 0xfb -#define HFCUSB_SCTRL 0x31 /* S-bus control register (tx) */ -#define HFCUSB_SCTRL_E 0x32 /* same for E and special funcs */ -#define HFCUSB_SCTRL_R 0x33 /* S-bus control register (rx) */ -#define HFCUSB_F_THRES 0x0c /* threshold register */ -#define HFCUSB_FIFO 0x0f /* fifo select register */ -#define HFCUSB_F_USAGE 0x1a /* fifo usage register */ -#define HFCUSB_MST_MODE0 0x14 -#define HFCUSB_MST_MODE1 0x15 -#define HFCUSB_P_DATA 0x1f -#define HFCUSB_INC_RES_F 0x0e -#define HFCUSB_B1_SSL 0x20 -#define HFCUSB_B2_SSL 0x21 -#define HFCUSB_B1_RSL 0x24 -#define HFCUSB_B2_RSL 0x25 -#define HFCUSB_STATES 0x30 - - -#define HFCUSB_CHIPID 0x40 /* ID value of HFC-S USB */ - -/* fifo registers */ -#define HFCUSB_NUM_FIFOS 8 /* maximum number of fifos */ -#define HFCUSB_B1_TX 0 /* index for B1 transmit bulk/int */ -#define HFCUSB_B1_RX 1 /* index for B1 receive bulk/int */ -#define HFCUSB_B2_TX 2 -#define HFCUSB_B2_RX 3 -#define HFCUSB_D_TX 4 -#define HFCUSB_D_RX 5 -#define HFCUSB_PCM_TX 6 -#define HFCUSB_PCM_RX 7 - - -#define USB_INT 0 -#define USB_BULK 1 -#define USB_ISOC 2 - -#define ISOC_PACKETS_D 8 -#define ISOC_PACKETS_B 8 -#define ISO_BUFFER_SIZE 128 - -/* defines how much ISO packets are handled in one URB */ -static int iso_packets[8] = -{ ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, - ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D -}; - - -/* Fifo flow Control for TX ISO */ -#define SINK_MAX 68 -#define SINK_MIN 48 -#define SINK_DMIN 12 -#define SINK_DMAX 18 -#define BITLINE_INF (-96 * 8) - -/* HFC-S USB register access by Control-URSs */ -#define write_reg_atomic(a, b, c) \ - usb_control_msg((a)->dev, (a)->ctrl_out_pipe, 0, 0x40, (c), (b), \ - 0, 0, HFC_CTRL_TIMEOUT) -#define read_reg_atomic(a, b, c) \ - usb_control_msg((a)->dev, (a)->ctrl_in_pipe, 1, 0xC0, 0, (b), (c), \ - 1, HFC_CTRL_TIMEOUT) -#define HFC_CTRL_BUFSIZE 64 - -struct ctrl_buf { - __u8 hfcs_reg; /* register number */ - __u8 reg_val; /* value to be written (or read) */ -}; - -/* - * URB error codes - * Used to represent a list of values and their respective symbolic names - */ -struct hfcusb_symbolic_list { - const int num; - const char *name; -}; - -static struct hfcusb_symbolic_list urb_errlist[] = { - {-ENOMEM, "No memory for allocation of internal structures"}, - {-ENOSPC, "The host controller's bandwidth is already consumed"}, - {-ENOENT, "URB was canceled by unlink_urb"}, - {-EXDEV, "ISO transfer only partially completed"}, - {-EAGAIN, "Too match scheduled for the future"}, - {-ENXIO, "URB already queued"}, - {-EFBIG, "Too much ISO frames requested"}, - {-ENOSR, "Buffer error (overrun)"}, - {-EPIPE, "Specified endpoint is stalled (device not responding)"}, - {-EOVERFLOW, "Babble (bad cable?)"}, - {-EPROTO, "Bit-stuff error (bad cable?)"}, - {-EILSEQ, "CRC/Timeout"}, - {-ETIMEDOUT, "NAK (device does not respond)"}, - {-ESHUTDOWN, "Device unplugged"}, - {-1, NULL} -}; - -static inline const char * -symbolic(struct hfcusb_symbolic_list list[], const int num) -{ - int i; - for (i = 0; list[i].name != NULL; i++) - if (list[i].num == num) - return list[i].name; - return ""; -} - -/* USB descriptor need to contain one of the following EndPoint combination: */ -#define CNF_4INT3ISO 1 /* 4 INT IN, 3 ISO OUT */ -#define CNF_3INT3ISO 2 /* 3 INT IN, 3 ISO OUT */ -#define CNF_4ISO3ISO 3 /* 4 ISO IN, 3 ISO OUT */ -#define CNF_3ISO3ISO 4 /* 3 ISO IN, 3 ISO OUT */ - -#define EP_NUL 1 /* Endpoint at this position not allowed */ -#define EP_NOP 2 /* all type of endpoints allowed at this position */ -#define EP_ISO 3 /* Isochron endpoint mandatory at this position */ -#define EP_BLK 4 /* Bulk endpoint mandatory at this position */ -#define EP_INT 5 /* Interrupt endpoint mandatory at this position */ - -#define HFC_CHAN_B1 0 -#define HFC_CHAN_B2 1 -#define HFC_CHAN_D 2 -#define HFC_CHAN_E 3 - - -/* - * List of all supported endpoint configuration sets, used to find the - * best matching endpoint configuration within a device's USB descriptor. - * We need at least 3 RX endpoints, and 3 TX endpoints, either - * INT-in and ISO-out, or ISO-in and ISO-out) - * with 4 RX endpoints even E-Channel logging is possible - */ -static int -validconf[][19] = { - /* INT in, ISO out config */ - {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT, - EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, - CNF_4INT3ISO, 2, 1}, - {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL, - EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, - CNF_3INT3ISO, 2, 0}, - /* ISO in, ISO out config */ - {EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, - EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO, - CNF_4ISO3ISO, 2, 1}, - {EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, - EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL, - CNF_3ISO3ISO, 2, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* EOL element */ -}; - -/* string description of chosen config */ -static char *conf_str[] = { - "4 Interrupt IN + 3 Isochron OUT", - "3 Interrupt IN + 3 Isochron OUT", - "4 Isochron IN + 3 Isochron OUT", - "3 Isochron IN + 3 Isochron OUT" -}; - - -#define LED_OFF 0 /* no LED support */ -#define LED_SCHEME1 1 /* LED standard scheme */ -#define LED_SCHEME2 2 /* not used yet... */ - -#define LED_POWER_ON 1 -#define LED_POWER_OFF 2 -#define LED_S0_ON 3 -#define LED_S0_OFF 4 -#define LED_B1_ON 5 -#define LED_B1_OFF 6 -#define LED_B1_DATA 7 -#define LED_B2_ON 8 -#define LED_B2_OFF 9 -#define LED_B2_DATA 10 - -#define LED_NORMAL 0 /* LEDs are normal */ -#define LED_INVERTED 1 /* LEDs are inverted */ - -/* time in ms to perform a Flashing LED when B-Channel has traffic */ -#define LED_TIME 250 - - - -struct hfcsusb; -struct usb_fifo; - -/* structure defining input+output fifos (interrupt/bulk mode) */ -struct iso_urb { - struct urb *urb; - __u8 buffer[ISO_BUFFER_SIZE]; /* buffer rx/tx USB URB data */ - struct usb_fifo *owner_fifo; /* pointer to owner fifo */ - __u8 indx; /* Fifos's ISO double buffer 0 or 1 ? */ -#ifdef ISO_FRAME_START_DEBUG - int start_frames[ISO_FRAME_START_RING_COUNT]; - __u8 iso_frm_strt_pos; /* index in start_frame[] */ -#endif -}; - -struct usb_fifo { - int fifonum; /* fifo index attached to this structure */ - int active; /* fifo is currently active */ - struct hfcsusb *hw; /* pointer to main structure */ - int pipe; /* address of endpoint */ - __u8 usb_packet_maxlen; /* maximum length for usb transfer */ - unsigned int max_size; /* maximum size of receive/send packet */ - __u8 intervall; /* interrupt interval */ - struct urb *urb; /* transfer structure for usb routines */ - __u8 buffer[128]; /* buffer USB INT OUT URB data */ - int bit_line; /* how much bits are in the fifo? */ - - __u8 usb_transfer_mode; /* switched between ISO and INT */ - struct iso_urb iso[2]; /* two urbs to have one always - one pending */ - - struct dchannel *dch; /* link to hfcsusb_t->dch */ - struct bchannel *bch; /* link to hfcsusb_t->bch */ - struct dchannel *ech; /* link to hfcsusb_t->ech, TODO: E-CHANNEL */ - int last_urblen; /* remember length of last packet */ - __u8 stop_gracefull; /* stops URB retransmission */ -}; - -struct hfcsusb { - struct list_head list; - struct dchannel dch; - struct bchannel bch[2]; - struct dchannel ech; /* TODO : wait for struct echannel ;) */ - - struct usb_device *dev; /* our device */ - struct usb_interface *intf; /* used interface */ - int if_used; /* used interface number */ - int alt_used; /* used alternate config */ - int cfg_used; /* configuration index used */ - int vend_idx; /* index in hfcsusb_idtab */ - int packet_size; - int iso_packet_size; - struct usb_fifo fifos[HFCUSB_NUM_FIFOS]; - - /* control pipe background handling */ - struct ctrl_buf ctrl_buff[HFC_CTRL_BUFSIZE]; - int ctrl_in_idx, ctrl_out_idx, ctrl_cnt; - struct urb *ctrl_urb; - struct usb_ctrlrequest ctrl_write; - struct usb_ctrlrequest ctrl_read; - int ctrl_paksize; - int ctrl_in_pipe, ctrl_out_pipe; - spinlock_t ctrl_lock; /* lock for ctrl */ - spinlock_t lock; - - __u8 threshold_mask; - __u8 led_state; - - __u8 protocol; - int nt_timer; - int open; - __u8 timers; - __u8 initdone; - char name[MISDN_MAX_IDLEN]; -}; - -/* private vendor specific data */ -struct hfcsusb_vdata { - __u8 led_scheme; /* led display scheme */ - signed short led_bits[8]; /* array of 8 possible LED bitmask */ - char *vend_name; /* device name */ -}; - - -#define HFC_MAX_TE_LAYER1_STATE 8 -#define HFC_MAX_NT_LAYER1_STATE 4 - -static const char *HFC_TE_LAYER1_STATES[HFC_MAX_TE_LAYER1_STATE + 1] = { - "TE F0 - Reset", - "TE F1 - Reset", - "TE F2 - Sensing", - "TE F3 - Deactivated", - "TE F4 - Awaiting signal", - "TE F5 - Identifying input", - "TE F6 - Synchronized", - "TE F7 - Activated", - "TE F8 - Lost framing", -}; - -static const char *HFC_NT_LAYER1_STATES[HFC_MAX_NT_LAYER1_STATE + 1] = { - "NT G0 - Reset", - "NT G1 - Deactive", - "NT G2 - Pending activation", - "NT G3 - Active", - "NT G4 - Pending deactivation", -}; - -/* supported devices */ -static const struct usb_device_id hfcsusb_idtab[] = { - { - USB_DEVICE(0x0959, 0x2bd0), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_OFF, {4, 0, 2, 1}, - "ISDN USB TA (Cologne Chip HFC-S USB based)"}), - }, - { - USB_DEVICE(0x0675, 0x1688), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {1, 2, 0, 0}, - "DrayTek miniVigor 128 USB ISDN TA"}), - }, - { - USB_DEVICE(0x07b0, 0x0007), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x80, -64, -32, -16}, - "Billion tiny USB ISDN TA 128"}), - }, - { - USB_DEVICE(0x0742, 0x2008), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {4, 0, 2, 1}, - "Stollmann USB TA"}), - }, - { - USB_DEVICE(0x0742, 0x2009), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {4, 0, 2, 1}, - "Aceex USB ISDN TA"}), - }, - { - USB_DEVICE(0x0742, 0x200A), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {4, 0, 2, 1}, - "OEM USB ISDN TA"}), - }, - { - USB_DEVICE(0x08e3, 0x0301), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {2, 0, 1, 4}, - "Olitec USB RNIS"}), - }, - { - USB_DEVICE(0x07fa, 0x0846), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x80, -64, -32, -16}, - "Bewan Modem RNIS USB"}), - }, - { - USB_DEVICE(0x07fa, 0x0847), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x80, -64, -32, -16}, - "Djinn Numeris USB"}), - }, - { - USB_DEVICE(0x07b0, 0x0006), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x80, -64, -32, -16}, - "Twister ISDN TA"}), - }, - { - USB_DEVICE(0x071d, 0x1005), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x02, 0, 0x01, 0x04}, - "Eicon DIVA USB 4.0"}), - }, - { - USB_DEVICE(0x0586, 0x0102), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x88, -64, -32, -16}, - "ZyXEL OMNI.NET USB II"}), - }, - { - USB_DEVICE(0x1ae7, 0x0525), - .driver_info = (unsigned long) &((struct hfcsusb_vdata) - {LED_SCHEME1, {0x88, -64, -32, -16}, - "X-Tensions USB ISDN TA XC-525"}), - }, - { } -}; - -MODULE_DEVICE_TABLE(usb, hfcsusb_idtab); - -#endif /* __HFCSUSB_H__ */ diff --git a/drivers/isdn/hardware/mISDN/iohelper.h b/drivers/isdn/hardware/mISDN/iohelper.h deleted file mode 100644 index c81f7aba4b57..000000000000 --- a/drivers/isdn/hardware/mISDN/iohelper.h +++ /dev/null @@ -1,96 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * iohelper.h - * helper for define functions to access ISDN hardware - * supported are memory mapped IO - * indirect port IO (one port for address, one for data) - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ - -#ifndef _IOHELPER_H -#define _IOHELPER_H - -typedef u8 (read_reg_func)(void *hwp, u8 offset); -typedef void (write_reg_func)(void *hwp, u8 offset, u8 value); -typedef void (fifo_func)(void *hwp, u8 offset, u8 *datap, int size); - -struct _ioport { - u32 port; - u32 ale; -}; - -#define IOFUNC_IO(name, hws, ap) \ - static u8 Read##name##_IO(void *p, u8 off) { \ - struct hws *hw = p; \ - return inb(hw->ap.port + off); \ - } \ - static void Write##name##_IO(void *p, u8 off, u8 val) { \ - struct hws *hw = p; \ - outb(val, hw->ap.port + off); \ - } \ - static void ReadFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) { \ - struct hws *hw = p; \ - insb(hw->ap.port + off, dp, size); \ - } \ - static void WriteFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) { \ - struct hws *hw = p; \ - outsb(hw->ap.port + off, dp, size); \ - } - -#define IOFUNC_IND(name, hws, ap) \ - static u8 Read##name##_IND(void *p, u8 off) { \ - struct hws *hw = p; \ - outb(off, hw->ap.ale); \ - return inb(hw->ap.port); \ - } \ - static void Write##name##_IND(void *p, u8 off, u8 val) { \ - struct hws *hw = p; \ - outb(off, hw->ap.ale); \ - outb(val, hw->ap.port); \ - } \ - static void ReadFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) { \ - struct hws *hw = p; \ - outb(off, hw->ap.ale); \ - insb(hw->ap.port, dp, size); \ - } \ - static void WriteFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) { \ - struct hws *hw = p; \ - outb(off, hw->ap.ale); \ - outsb(hw->ap.port, dp, size); \ - } - -#define IOFUNC_MEMIO(name, hws, typ, adr) \ - static u8 Read##name##_MIO(void *p, u8 off) { \ - struct hws *hw = p; \ - return readb(((typ *)hw->adr) + off); \ - } \ - static void Write##name##_MIO(void *p, u8 off, u8 val) { \ - struct hws *hw = p; \ - writeb(val, ((typ *)hw->adr) + off); \ - } \ - static void ReadFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) { \ - struct hws *hw = p; \ - while (size--) \ - *dp++ = readb(((typ *)hw->adr) + off); \ - } \ - static void WriteFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) { \ - struct hws *hw = p; \ - while (size--) \ - writeb(*dp++, ((typ *)hw->adr) + off); \ - } - -#define ASSIGN_FUNC(typ, name, dest) do { \ - dest.read_reg = &Read##name##_##typ; \ - dest.write_reg = &Write##name##_##typ; \ - dest.read_fifo = &ReadFiFo##name##_##typ; \ - dest.write_fifo = &WriteFiFo##name##_##typ; \ - } while (0) -#define ASSIGN_FUNC_IPAC(typ, target) do { \ - ASSIGN_FUNC(typ, ISAC, target.isac); \ - ASSIGN_FUNC(typ, IPAC, target); \ - } while (0) - -#endif diff --git a/drivers/isdn/hardware/mISDN/ipac.h b/drivers/isdn/hardware/mISDN/ipac.h deleted file mode 100644 index 2f0c4978a7a5..000000000000 --- a/drivers/isdn/hardware/mISDN/ipac.h +++ /dev/null @@ -1,393 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * - * ipac.h Defines for the Infineon (former Siemens) ISDN - * chip series - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ - -#include "iohelper.h" - -struct isac_hw { - struct dchannel dch; - u32 type; - u32 off; /* offset to isac regs */ - char *name; - spinlock_t *hwlock; /* lock HW access */ - read_reg_func *read_reg; - write_reg_func *write_reg; - fifo_func *read_fifo; - fifo_func *write_fifo; - int (*monitor)(void *, u32, u8 *, int); - void (*release)(struct isac_hw *); - int (*init)(struct isac_hw *); - int (*ctrl)(struct isac_hw *, u32, u_long); - int (*open)(struct isac_hw *, struct channel_req *); - u8 *mon_tx; - u8 *mon_rx; - int mon_txp; - int mon_txc; - int mon_rxp; - struct arcofi_msg *arcofi_list; - struct timer_list arcofitimer; - wait_queue_head_t arcofi_wait; - u8 arcofi_bc; - u8 arcofi_state; - u8 mocr; - u8 adf2; - u8 state; -}; - -struct ipac_hw; - -struct hscx_hw { - struct bchannel bch; - struct ipac_hw *ip; - u8 fifo_size; - u8 off; /* offset to ICA or ICB */ - u8 slot; - char log[64]; -}; - -struct ipac_hw { - struct isac_hw isac; - struct hscx_hw hscx[2]; - char *name; - void *hw; - spinlock_t *hwlock; /* lock HW access */ - struct module *owner; - u32 type; - read_reg_func *read_reg; - write_reg_func *write_reg; - fifo_func *read_fifo; - fifo_func *write_fifo; - void (*release)(struct ipac_hw *); - int (*init)(struct ipac_hw *); - int (*ctrl)(struct ipac_hw *, u32, u_long); - u8 conf; -}; - -#define IPAC_TYPE_ISAC 0x0010 -#define IPAC_TYPE_IPAC 0x0020 -#define IPAC_TYPE_ISACX 0x0040 -#define IPAC_TYPE_IPACX 0x0080 -#define IPAC_TYPE_HSCX 0x0100 - -#define ISAC_USE_ARCOFI 0x1000 - -/* Monitor functions */ -#define MONITOR_RX_0 0x1000 -#define MONITOR_RX_1 0x1001 -#define MONITOR_TX_0 0x2000 -#define MONITOR_TX_1 0x2001 - -/* All registers original Siemens Spec */ -/* IPAC/ISAC registers */ -#define ISAC_ISTA 0x20 -#define ISAC_MASK 0x20 -#define ISAC_CMDR 0x21 -#define ISAC_STAR 0x21 -#define ISAC_MODE 0x22 -#define ISAC_TIMR 0x23 -#define ISAC_EXIR 0x24 -#define ISAC_RBCL 0x25 -#define ISAC_RSTA 0x27 -#define ISAC_RBCH 0x2A -#define ISAC_SPCR 0x30 -#define ISAC_CIR0 0x31 -#define ISAC_CIX0 0x31 -#define ISAC_MOR0 0x32 -#define ISAC_MOX0 0x32 -#define ISAC_CIR1 0x33 -#define ISAC_CIX1 0x33 -#define ISAC_MOR1 0x34 -#define ISAC_MOX1 0x34 -#define ISAC_STCR 0x37 -#define ISAC_ADF1 0x38 -#define ISAC_ADF2 0x39 -#define ISAC_MOCR 0x3a -#define ISAC_MOSR 0x3a -#define ISAC_SQRR 0x3b -#define ISAC_SQXR 0x3b - -#define ISAC_RBCH_XAC 0x80 - -#define IPAC_D_TIN2 0x01 - -/* IPAC/HSCX */ -#define IPAC_ISTAB 0x20 /* RD */ -#define IPAC_MASKB 0x20 /* WR */ -#define IPAC_STARB 0x21 /* RD */ -#define IPAC_CMDRB 0x21 /* WR */ -#define IPAC_MODEB 0x22 /* R/W */ -#define IPAC_EXIRB 0x24 /* RD */ -#define IPAC_RBCLB 0x25 /* RD */ -#define IPAC_RAH1 0x26 /* WR */ -#define IPAC_RAH2 0x27 /* WR */ -#define IPAC_RSTAB 0x27 /* RD */ -#define IPAC_RAL1 0x28 /* R/W */ -#define IPAC_RAL2 0x29 /* WR */ -#define IPAC_RHCRB 0x29 /* RD */ -#define IPAC_XBCL 0x2A /* WR */ -#define IPAC_CCR2 0x2C /* R/W */ -#define IPAC_RBCHB 0x2D /* RD */ -#define IPAC_XBCH 0x2D /* WR */ -#define HSCX_VSTR 0x2E /* RD */ -#define IPAC_RLCR 0x2E /* WR */ -#define IPAC_CCR1 0x2F /* R/W */ -#define IPAC_TSAX 0x30 /* WR */ -#define IPAC_TSAR 0x31 /* WR */ -#define IPAC_XCCR 0x32 /* WR */ -#define IPAC_RCCR 0x33 /* WR */ - -/* IPAC_ISTAB/IPAC_MASKB bits */ -#define IPAC_B_XPR 0x10 -#define IPAC_B_RPF 0x40 -#define IPAC_B_RME 0x80 -#define IPAC_B_ON 0x2F - -/* IPAC_EXIRB bits */ -#define IPAC_B_RFS 0x04 -#define IPAC_B_RFO 0x10 -#define IPAC_B_XDU 0x40 -#define IPAC_B_XMR 0x80 - -/* IPAC special registers */ -#define IPAC_CONF 0xC0 /* R/W */ -#define IPAC_ISTA 0xC1 /* RD */ -#define IPAC_MASK 0xC1 /* WR */ -#define IPAC_ID 0xC2 /* RD */ -#define IPAC_ACFG 0xC3 /* R/W */ -#define IPAC_AOE 0xC4 /* R/W */ -#define IPAC_ARX 0xC5 /* RD */ -#define IPAC_ATX 0xC5 /* WR */ -#define IPAC_PITA1 0xC6 /* R/W */ -#define IPAC_PITA2 0xC7 /* R/W */ -#define IPAC_POTA1 0xC8 /* R/W */ -#define IPAC_POTA2 0xC9 /* R/W */ -#define IPAC_PCFG 0xCA /* R/W */ -#define IPAC_SCFG 0xCB /* R/W */ -#define IPAC_TIMR2 0xCC /* R/W */ - -/* IPAC_ISTA/_MASK bits */ -#define IPAC__EXB 0x01 -#define IPAC__ICB 0x02 -#define IPAC__EXA 0x04 -#define IPAC__ICA 0x08 -#define IPAC__EXD 0x10 -#define IPAC__ICD 0x20 -#define IPAC__INT0 0x40 -#define IPAC__INT1 0x80 -#define IPAC__ON 0xC0 - -/* HSCX ISTA/MASK bits */ -#define HSCX__EXB 0x01 -#define HSCX__EXA 0x02 -#define HSCX__ICA 0x04 - -/* ISAC/ISACX/IPAC/IPACX L1 commands */ -#define ISAC_CMD_TIM 0x0 -#define ISAC_CMD_RS 0x1 -#define ISAC_CMD_SCZ 0x4 -#define ISAC_CMD_SSZ 0x2 -#define ISAC_CMD_AR8 0x8 -#define ISAC_CMD_AR10 0x9 -#define ISAC_CMD_ARL 0xA -#define ISAC_CMD_DUI 0xF - -/* ISAC/ISACX/IPAC/IPACX L1 indications */ -#define ISAC_IND_DR 0x0 -#define ISAC_IND_RS 0x1 -#define ISAC_IND_SD 0x2 -#define ISAC_IND_DIS 0x3 -#define ISAC_IND_RSY 0x4 -#define ISAC_IND_DR6 0x5 -#define ISAC_IND_EI 0x6 -#define ISAC_IND_PU 0x7 -#define ISAC_IND_ARD 0x8 -#define ISAC_IND_TI 0xA -#define ISAC_IND_ATI 0xB -#define ISAC_IND_AI8 0xC -#define ISAC_IND_AI10 0xD -#define ISAC_IND_DID 0xF - -/* the new ISACX / IPACX */ -/* D-channel registers */ -#define ISACX_RFIFOD 0x00 /* RD */ -#define ISACX_XFIFOD 0x00 /* WR */ -#define ISACX_ISTAD 0x20 /* RD */ -#define ISACX_MASKD 0x20 /* WR */ -#define ISACX_STARD 0x21 /* RD */ -#define ISACX_CMDRD 0x21 /* WR */ -#define ISACX_MODED 0x22 /* R/W */ -#define ISACX_EXMD1 0x23 /* R/W */ -#define ISACX_TIMR1 0x24 /* R/W */ -#define ISACX_SAP1 0x25 /* WR */ -#define ISACX_SAP2 0x26 /* WR */ -#define ISACX_RBCLD 0x26 /* RD */ -#define ISACX_RBCHD 0x27 /* RD */ -#define ISACX_TEI1 0x27 /* WR */ -#define ISACX_TEI2 0x28 /* WR */ -#define ISACX_RSTAD 0x28 /* RD */ -#define ISACX_TMD 0x29 /* R/W */ -#define ISACX_CIR0 0x2E /* RD */ -#define ISACX_CIX0 0x2E /* WR */ -#define ISACX_CIR1 0x2F /* RD */ -#define ISACX_CIX1 0x2F /* WR */ - -/* Transceiver registers */ -#define ISACX_TR_CONF0 0x30 /* R/W */ -#define ISACX_TR_CONF1 0x31 /* R/W */ -#define ISACX_TR_CONF2 0x32 /* R/W */ -#define ISACX_TR_STA 0x33 /* RD */ -#define ISACX_TR_CMD 0x34 /* R/W */ -#define ISACX_SQRR1 0x35 /* RD */ -#define ISACX_SQXR1 0x35 /* WR */ -#define ISACX_SQRR2 0x36 /* RD */ -#define ISACX_SQXR2 0x36 /* WR */ -#define ISACX_SQRR3 0x37 /* RD */ -#define ISACX_SQXR3 0x37 /* WR */ -#define ISACX_ISTATR 0x38 /* RD */ -#define ISACX_MASKTR 0x39 /* R/W */ -#define ISACX_TR_MODE 0x3A /* R/W */ -#define ISACX_ACFG1 0x3C /* R/W */ -#define ISACX_ACFG2 0x3D /* R/W */ -#define ISACX_AOE 0x3E /* R/W */ -#define ISACX_ARX 0x3F /* RD */ -#define ISACX_ATX 0x3F /* WR */ - -/* IOM: Timeslot, DPS, CDA */ -#define ISACX_CDA10 0x40 /* R/W */ -#define ISACX_CDA11 0x41 /* R/W */ -#define ISACX_CDA20 0x42 /* R/W */ -#define ISACX_CDA21 0x43 /* R/W */ -#define ISACX_CDA_TSDP10 0x44 /* R/W */ -#define ISACX_CDA_TSDP11 0x45 /* R/W */ -#define ISACX_CDA_TSDP20 0x46 /* R/W */ -#define ISACX_CDA_TSDP21 0x47 /* R/W */ -#define ISACX_BCHA_TSDP_BC1 0x48 /* R/W */ -#define ISACX_BCHA_TSDP_BC2 0x49 /* R/W */ -#define ISACX_BCHB_TSDP_BC1 0x4A /* R/W */ -#define ISACX_BCHB_TSDP_BC2 0x4B /* R/W */ -#define ISACX_TR_TSDP_BC1 0x4C /* R/W */ -#define ISACX_TR_TSDP_BC2 0x4D /* R/W */ -#define ISACX_CDA1_CR 0x4E /* R/W */ -#define ISACX_CDA2_CR 0x4F /* R/W */ - -/* IOM: Contol, Sync transfer, Monitor */ -#define ISACX_TR_CR 0x50 /* R/W */ -#define ISACX_TRC_CR 0x50 /* R/W */ -#define ISACX_BCHA_CR 0x51 /* R/W */ -#define ISACX_BCHB_CR 0x52 /* R/W */ -#define ISACX_DCI_CR 0x53 /* R/W */ -#define ISACX_DCIC_CR 0x53 /* R/W */ -#define ISACX_MON_CR 0x54 /* R/W */ -#define ISACX_SDS1_CR 0x55 /* R/W */ -#define ISACX_SDS2_CR 0x56 /* R/W */ -#define ISACX_IOM_CR 0x57 /* R/W */ -#define ISACX_STI 0x58 /* RD */ -#define ISACX_ASTI 0x58 /* WR */ -#define ISACX_MSTI 0x59 /* R/W */ -#define ISACX_SDS_CONF 0x5A /* R/W */ -#define ISACX_MCDA 0x5B /* RD */ -#define ISACX_MOR 0x5C /* RD */ -#define ISACX_MOX 0x5C /* WR */ -#define ISACX_MOSR 0x5D /* RD */ -#define ISACX_MOCR 0x5E /* R/W */ -#define ISACX_MSTA 0x5F /* RD */ -#define ISACX_MCONF 0x5F /* WR */ - -/* Interrupt and general registers */ -#define ISACX_ISTA 0x60 /* RD */ -#define ISACX_MASK 0x60 /* WR */ -#define ISACX_AUXI 0x61 /* RD */ -#define ISACX_AUXM 0x61 /* WR */ -#define ISACX_MODE1 0x62 /* R/W */ -#define ISACX_MODE2 0x63 /* R/W */ -#define ISACX_ID 0x64 /* RD */ -#define ISACX_SRES 0x64 /* WR */ -#define ISACX_TIMR2 0x65 /* R/W */ - -/* Register Bits */ -/* ISACX/IPACX _ISTAD (R) and _MASKD (W) */ -#define ISACX_D_XDU 0x04 -#define ISACX_D_XMR 0x08 -#define ISACX_D_XPR 0x10 -#define ISACX_D_RFO 0x20 -#define ISACX_D_RPF 0x40 -#define ISACX_D_RME 0x80 - -/* ISACX/IPACX _ISTA (R) and _MASK (W) */ -#define ISACX__ICD 0x01 -#define ISACX__MOS 0x02 -#define ISACX__TRAN 0x04 -#define ISACX__AUX 0x08 -#define ISACX__CIC 0x10 -#define ISACX__ST 0x20 -#define IPACX__ON 0x2C -#define IPACX__ICB 0x40 -#define IPACX__ICA 0x80 - -/* ISACX/IPACX _CMDRD (W) */ -#define ISACX_CMDRD_XRES 0x01 -#define ISACX_CMDRD_XME 0x02 -#define ISACX_CMDRD_XTF 0x08 -#define ISACX_CMDRD_STI 0x10 -#define ISACX_CMDRD_RRES 0x40 -#define ISACX_CMDRD_RMC 0x80 - -/* ISACX/IPACX _RSTAD (R) */ -#define ISACX_RSTAD_TA 0x01 -#define ISACX_RSTAD_CR 0x02 -#define ISACX_RSTAD_SA0 0x04 -#define ISACX_RSTAD_SA1 0x08 -#define ISACX_RSTAD_RAB 0x10 -#define ISACX_RSTAD_CRC 0x20 -#define ISACX_RSTAD_RDO 0x40 -#define ISACX_RSTAD_VFR 0x80 - -/* ISACX/IPACX _CIR0 (R) */ -#define ISACX_CIR0_BAS 0x01 -#define ISACX_CIR0_SG 0x08 -#define ISACX_CIR0_CIC1 0x08 -#define ISACX_CIR0_CIC0 0x08 - -/* B-channel registers */ -#define IPACX_OFF_ICA 0x70 -#define IPACX_OFF_ICB 0x80 - -/* ICA: IPACX_OFF_ICA + Reg ICB: IPACX_OFF_ICB + Reg */ - -#define IPACX_ISTAB 0x00 /* RD */ -#define IPACX_MASKB 0x00 /* WR */ -#define IPACX_STARB 0x01 /* RD */ -#define IPACX_CMDRB 0x01 /* WR */ -#define IPACX_MODEB 0x02 /* R/W */ -#define IPACX_EXMB 0x03 /* R/W */ -#define IPACX_RAH1 0x05 /* WR */ -#define IPACX_RAH2 0x06 /* WR */ -#define IPACX_RBCLB 0x06 /* RD */ -#define IPACX_RBCHB 0x07 /* RD */ -#define IPACX_RAL1 0x07 /* WR */ -#define IPACX_RAL2 0x08 /* WR */ -#define IPACX_RSTAB 0x08 /* RD */ -#define IPACX_TMB 0x09 /* R/W */ -#define IPACX_RFIFOB 0x0A /* RD */ -#define IPACX_XFIFOB 0x0A /* WR */ - -/* IPACX_ISTAB / IPACX_MASKB bits */ -#define IPACX_B_XDU 0x04 -#define IPACX_B_XPR 0x10 -#define IPACX_B_RFO 0x20 -#define IPACX_B_RPF 0x40 -#define IPACX_B_RME 0x80 - -#define IPACX_B_ON 0x0B - -extern int mISDNisac_init(struct isac_hw *, void *); -extern irqreturn_t mISDNisac_irq(struct isac_hw *, u8); -extern u32 mISDNipac_init(struct ipac_hw *, void *); -extern irqreturn_t mISDNipac_irq(struct ipac_hw *, int); diff --git a/drivers/isdn/hardware/mISDN/isar.h b/drivers/isdn/hardware/mISDN/isar.h deleted file mode 100644 index 36a9fa564b17..000000000000 --- a/drivers/isdn/hardware/mISDN/isar.h +++ /dev/null @@ -1,256 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * - * isar.h ISAR (Siemens PSB 7110) specific defines - * - * Author Karsten Keil (keil@isdn4linux.de) - * - * Copyright 2009 by Karsten Keil - */ - -#include "iohelper.h" - -struct isar_hw; - -struct isar_ch { - struct bchannel bch; - struct isar_hw *is; - struct timer_list ftimer; - u8 nr; - u8 dpath; - u8 mml; - u8 state; - u8 cmd; - u8 mod; - u8 newcmd; - u8 newmod; - u8 try_mod; - u8 conmsg[16]; -}; - -struct isar_hw { - struct isar_ch ch[2]; - void *hw; - spinlock_t *hwlock; /* lock HW access */ - char *name; - struct module *owner; - read_reg_func *read_reg; - write_reg_func *write_reg; - fifo_func *read_fifo; - fifo_func *write_fifo; - int (*ctrl)(void *, u32, u_long); - void (*release)(struct isar_hw *); - int (*init)(struct isar_hw *); - int (*open)(struct isar_hw *, struct channel_req *); - int (*firmware)(struct isar_hw *, const u8 *, int); - unsigned long Flags; - int version; - u8 bstat; - u8 iis; - u8 cmsb; - u8 clsb; - u8 buf[256]; - u8 log[256]; -}; - -#define ISAR_IRQMSK 0x04 -#define ISAR_IRQSTA 0x04 -#define ISAR_IRQBIT 0x75 -#define ISAR_CTRL_H 0x61 -#define ISAR_CTRL_L 0x60 -#define ISAR_IIS 0x58 -#define ISAR_IIA 0x58 -#define ISAR_HIS 0x50 -#define ISAR_HIA 0x50 -#define ISAR_MBOX 0x4c -#define ISAR_WADR 0x4a -#define ISAR_RADR 0x48 - -#define ISAR_HIS_VNR 0x14 -#define ISAR_HIS_DKEY 0x02 -#define ISAR_HIS_FIRM 0x1e -#define ISAR_HIS_STDSP 0x08 -#define ISAR_HIS_DIAG 0x05 -#define ISAR_HIS_P0CFG 0x3c -#define ISAR_HIS_P12CFG 0x24 -#define ISAR_HIS_SARTCFG 0x25 -#define ISAR_HIS_PUMPCFG 0x26 -#define ISAR_HIS_PUMPCTRL 0x2a -#define ISAR_HIS_IOM2CFG 0x27 -#define ISAR_HIS_IOM2REQ 0x07 -#define ISAR_HIS_IOM2CTRL 0x2b -#define ISAR_HIS_BSTREQ 0x0c -#define ISAR_HIS_PSTREQ 0x0e -#define ISAR_HIS_SDATA 0x20 -#define ISAR_HIS_DPS1 0x40 -#define ISAR_HIS_DPS2 0x80 -#define SET_DPS(x) ((x << 6) & 0xc0) - -#define ISAR_IIS_MSCMSD 0x3f -#define ISAR_IIS_VNR 0x15 -#define ISAR_IIS_DKEY 0x03 -#define ISAR_IIS_FIRM 0x1f -#define ISAR_IIS_STDSP 0x09 -#define ISAR_IIS_DIAG 0x25 -#define ISAR_IIS_GSTEV 0x00 -#define ISAR_IIS_BSTEV 0x28 -#define ISAR_IIS_BSTRSP 0x2c -#define ISAR_IIS_PSTRSP 0x2e -#define ISAR_IIS_PSTEV 0x2a -#define ISAR_IIS_IOM2RSP 0x27 -#define ISAR_IIS_RDATA 0x20 -#define ISAR_IIS_INVMSG 0x3f - -#define ISAR_CTRL_SWVER 0x10 -#define ISAR_CTRL_STST 0x40 - -#define ISAR_MSG_HWVER 0x20 - -#define ISAR_DP1_USE 1 -#define ISAR_DP2_USE 2 -#define ISAR_RATE_REQ 3 - -#define PMOD_DISABLE 0 -#define PMOD_FAX 1 -#define PMOD_DATAMODEM 2 -#define PMOD_HALFDUPLEX 3 -#define PMOD_V110 4 -#define PMOD_DTMF 5 -#define PMOD_DTMF_TRANS 6 -#define PMOD_BYPASS 7 - -#define PCTRL_ORIG 0x80 -#define PV32P2_V23R 0x40 -#define PV32P2_V22A 0x20 -#define PV32P2_V22B 0x10 -#define PV32P2_V22C 0x08 -#define PV32P2_V21 0x02 -#define PV32P2_BEL 0x01 - -/* LSB MSB in ISAR doc wrong !!! Arghhh */ -#define PV32P3_AMOD 0x80 -#define PV32P3_V32B 0x02 -#define PV32P3_V23B 0x01 -#define PV32P4_48 0x11 -#define PV32P5_48 0x05 -#define PV32P4_UT48 0x11 -#define PV32P5_UT48 0x0d -#define PV32P4_96 0x11 -#define PV32P5_96 0x03 -#define PV32P4_UT96 0x11 -#define PV32P5_UT96 0x0f -#define PV32P4_B96 0x91 -#define PV32P5_B96 0x0b -#define PV32P4_UTB96 0xd1 -#define PV32P5_UTB96 0x0f -#define PV32P4_120 0xb1 -#define PV32P5_120 0x09 -#define PV32P4_UT120 0xf1 -#define PV32P5_UT120 0x0f -#define PV32P4_144 0x99 -#define PV32P5_144 0x09 -#define PV32P4_UT144 0xf9 -#define PV32P5_UT144 0x0f -#define PV32P6_CTN 0x01 -#define PV32P6_ATN 0x02 - -#define PFAXP2_CTN 0x01 -#define PFAXP2_ATN 0x04 - -#define PSEV_10MS_TIMER 0x02 -#define PSEV_CON_ON 0x18 -#define PSEV_CON_OFF 0x19 -#define PSEV_V24_OFF 0x20 -#define PSEV_CTS_ON 0x21 -#define PSEV_CTS_OFF 0x22 -#define PSEV_DCD_ON 0x23 -#define PSEV_DCD_OFF 0x24 -#define PSEV_DSR_ON 0x25 -#define PSEV_DSR_OFF 0x26 -#define PSEV_REM_RET 0xcc -#define PSEV_REM_REN 0xcd -#define PSEV_GSTN_CLR 0xd4 - -#define PSEV_RSP_READY 0xbc -#define PSEV_LINE_TX_H 0xb3 -#define PSEV_LINE_TX_B 0xb2 -#define PSEV_LINE_RX_H 0xb1 -#define PSEV_LINE_RX_B 0xb0 -#define PSEV_RSP_CONN 0xb5 -#define PSEV_RSP_DISC 0xb7 -#define PSEV_RSP_FCERR 0xb9 -#define PSEV_RSP_SILDET 0xbe -#define PSEV_RSP_SILOFF 0xab -#define PSEV_FLAGS_DET 0xba - -#define PCTRL_CMD_TDTMF 0x5a - -#define PCTRL_CMD_FTH 0xa7 -#define PCTRL_CMD_FRH 0xa5 -#define PCTRL_CMD_FTM 0xa8 -#define PCTRL_CMD_FRM 0xa6 -#define PCTRL_CMD_SILON 0xac -#define PCTRL_CMD_CONT 0xa2 -#define PCTRL_CMD_ESC 0xa4 -#define PCTRL_CMD_SILOFF 0xab -#define PCTRL_CMD_HALT 0xa9 - -#define PCTRL_LOC_RET 0xcf -#define PCTRL_LOC_REN 0xce - -#define SMODE_DISABLE 0 -#define SMODE_V14 2 -#define SMODE_HDLC 3 -#define SMODE_BINARY 4 -#define SMODE_FSK_V14 5 - -#define SCTRL_HDMC_BOTH 0x00 -#define SCTRL_HDMC_DTX 0x80 -#define SCTRL_HDMC_DRX 0x40 -#define S_P1_OVSP 0x40 -#define S_P1_SNP 0x20 -#define S_P1_EOP 0x10 -#define S_P1_EDP 0x08 -#define S_P1_NSB 0x04 -#define S_P1_CHS_8 0x03 -#define S_P1_CHS_7 0x02 -#define S_P1_CHS_6 0x01 -#define S_P1_CHS_5 0x00 - -#define S_P2_BFT_DEF 0x10 - -#define IOM_CTRL_ENA 0x80 -#define IOM_CTRL_NOPCM 0x00 -#define IOM_CTRL_ALAW 0x02 -#define IOM_CTRL_ULAW 0x04 -#define IOM_CTRL_RCV 0x01 - -#define IOM_P1_TXD 0x10 - -#define HDLC_FED 0x40 -#define HDLC_FSD 0x20 -#define HDLC_FST 0x20 -#define HDLC_ERROR 0x1c -#define HDLC_ERR_FAD 0x10 -#define HDLC_ERR_RER 0x08 -#define HDLC_ERR_CER 0x04 -#define SART_NMD 0x01 - -#define BSTAT_RDM0 0x1 -#define BSTAT_RDM1 0x2 -#define BSTAT_RDM2 0x4 -#define BSTAT_RDM3 0x8 -#define BSTEV_TBO 0x1f -#define BSTEV_RBO 0x2f - -/* FAX State Machine */ -#define STFAX_NULL 0 -#define STFAX_READY 1 -#define STFAX_LINE 2 -#define STFAX_CONT 3 -#define STFAX_ACTIV 4 -#define STFAX_ESCAPE 5 -#define STFAX_SILDET 6 - -extern u32 mISDNisar_init(struct isar_hw *, void *); -extern void mISDNisar_irq(struct isar_hw *); diff --git a/drivers/isdn/hardware/mISDN/isdnhdlc.c b/drivers/isdn/hardware/mISDN/isdnhdlc.c deleted file mode 100644 index 985367e6711d..000000000000 --- a/drivers/isdn/hardware/mISDN/isdnhdlc.c +++ /dev/null @@ -1,617 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * isdnhdlc.c -- General purpose ISDN HDLC decoder. - * - * Copyright (C) - * 2009 Karsten Keil - * 2002 Wolfgang Mües - * 2001 Frode Isaksen - * 2001 Kai Germaschewski - */ - -#include -#include -#include -#include -#include "isdnhdlc.h" - -/*-------------------------------------------------------------------*/ - -MODULE_AUTHOR("Wolfgang Mües , " - "Frode Isaksen , " - "Kai Germaschewski "); -MODULE_DESCRIPTION("General purpose ISDN HDLC decoder"); -MODULE_LICENSE("GPL"); - -/*-------------------------------------------------------------------*/ - -enum { - HDLC_FAST_IDLE, HDLC_GET_FLAG_B0, HDLC_GETFLAG_B1A6, HDLC_GETFLAG_B7, - HDLC_GET_DATA, HDLC_FAST_FLAG -}; - -enum { - HDLC_SEND_DATA, HDLC_SEND_CRC1, HDLC_SEND_FAST_FLAG, - HDLC_SEND_FIRST_FLAG, HDLC_SEND_CRC2, HDLC_SEND_CLOSING_FLAG, - HDLC_SEND_IDLE1, HDLC_SEND_FAST_IDLE, HDLC_SENDFLAG_B0, - HDLC_SENDFLAG_B1A6, HDLC_SENDFLAG_B7, STOPPED, HDLC_SENDFLAG_ONE -}; - -void isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features) -{ - memset(hdlc, 0, sizeof(struct isdnhdlc_vars)); - hdlc->state = HDLC_GET_DATA; - if (features & HDLC_56KBIT) - hdlc->do_adapt56 = 1; - if (features & HDLC_BITREVERSE) - hdlc->do_bitreverse = 1; -} -EXPORT_SYMBOL(isdnhdlc_out_init); - -void isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features) -{ - memset(hdlc, 0, sizeof(struct isdnhdlc_vars)); - if (features & HDLC_DCHANNEL) { - hdlc->dchannel = 1; - hdlc->state = HDLC_SEND_FIRST_FLAG; - } else { - hdlc->dchannel = 0; - hdlc->state = HDLC_SEND_FAST_FLAG; - hdlc->ffvalue = 0x7e; - } - hdlc->cbin = 0x7e; - if (features & HDLC_56KBIT) { - hdlc->do_adapt56 = 1; - hdlc->state = HDLC_SENDFLAG_B0; - } else - hdlc->data_bits = 8; - if (features & HDLC_BITREVERSE) - hdlc->do_bitreverse = 1; -} -EXPORT_SYMBOL(isdnhdlc_rcv_init); - -static int -check_frame(struct isdnhdlc_vars *hdlc) -{ - int status; - - if (hdlc->dstpos < 2) /* too small - framing error */ - status = -HDLC_FRAMING_ERROR; - else if (hdlc->crc != 0xf0b8) /* crc error */ - status = -HDLC_CRC_ERROR; - else { - /* remove CRC */ - hdlc->dstpos -= 2; - /* good frame */ - status = hdlc->dstpos; - } - return status; -} - -/* - isdnhdlc_decode - decodes HDLC frames from a transparent bit stream. - - The source buffer is scanned for valid HDLC frames looking for - flags (01111110) to indicate the start of a frame. If the start of - the frame is found, the bit stuffing is removed (0 after 5 1's). - When a new flag is found, the complete frame has been received - and the CRC is checked. - If a valid frame is found, the function returns the frame length - excluding the CRC with the bit HDLC_END_OF_FRAME set. - If the beginning of a valid frame is found, the function returns - the length. - If a framing error is found (too many 1s and not a flag) the function - returns the length with the bit HDLC_FRAMING_ERROR set. - If a CRC error is found the function returns the length with the - bit HDLC_CRC_ERROR set. - If the frame length exceeds the destination buffer size, the function - returns the length with the bit HDLC_LENGTH_ERROR set. - - src - source buffer - slen - source buffer length - count - number of bytes removed (decoded) from the source buffer - dst _ destination buffer - dsize - destination buffer size - returns - number of decoded bytes in the destination buffer and status - flag. -*/ -int isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src, int slen, - int *count, u8 *dst, int dsize) -{ - int status = 0; - - static const unsigned char fast_flag[] = { - 0x00, 0x00, 0x00, 0x20, 0x30, 0x38, 0x3c, 0x3e, 0x3f - }; - - static const unsigned char fast_flag_value[] = { - 0x00, 0x7e, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3f - }; - - static const unsigned char fast_abort[] = { - 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff - }; - -#define handle_fast_flag(h) \ - do { \ - if (h->cbin == fast_flag[h->bit_shift]) { \ - h->ffvalue = fast_flag_value[h->bit_shift]; \ - h->state = HDLC_FAST_FLAG; \ - h->ffbit_shift = h->bit_shift; \ - h->bit_shift = 1; \ - } else { \ - h->state = HDLC_GET_DATA; \ - h->data_received = 0; \ - } \ - } while (0) - -#define handle_abort(h) \ - do { \ - h->shift_reg = fast_abort[h->ffbit_shift - 1]; \ - h->hdlc_bits1 = h->ffbit_shift - 2; \ - if (h->hdlc_bits1 < 0) \ - h->hdlc_bits1 = 0; \ - h->data_bits = h->ffbit_shift - 1; \ - h->state = HDLC_GET_DATA; \ - h->data_received = 0; \ - } while (0) - - *count = slen; - - while (slen > 0) { - if (hdlc->bit_shift == 0) { - /* the code is for bitreverse streams */ - if (hdlc->do_bitreverse == 0) - hdlc->cbin = bitrev8(*src++); - else - hdlc->cbin = *src++; - slen--; - hdlc->bit_shift = 8; - if (hdlc->do_adapt56) - hdlc->bit_shift--; - } - - switch (hdlc->state) { - case STOPPED: - return 0; - case HDLC_FAST_IDLE: - if (hdlc->cbin == 0xff) { - hdlc->bit_shift = 0; - break; - } - hdlc->state = HDLC_GET_FLAG_B0; - hdlc->hdlc_bits1 = 0; - hdlc->bit_shift = 8; - break; - case HDLC_GET_FLAG_B0: - if (!(hdlc->cbin & 0x80)) { - hdlc->state = HDLC_GETFLAG_B1A6; - hdlc->hdlc_bits1 = 0; - } else { - if ((!hdlc->do_adapt56) && - (++hdlc->hdlc_bits1 >= 8) && - (hdlc->bit_shift == 1)) - hdlc->state = HDLC_FAST_IDLE; - } - hdlc->cbin <<= 1; - hdlc->bit_shift--; - break; - case HDLC_GETFLAG_B1A6: - if (hdlc->cbin & 0x80) { - hdlc->hdlc_bits1++; - if (hdlc->hdlc_bits1 == 6) - hdlc->state = HDLC_GETFLAG_B7; - } else - hdlc->hdlc_bits1 = 0; - hdlc->cbin <<= 1; - hdlc->bit_shift--; - break; - case HDLC_GETFLAG_B7: - if (hdlc->cbin & 0x80) { - hdlc->state = HDLC_GET_FLAG_B0; - } else { - hdlc->state = HDLC_GET_DATA; - hdlc->crc = 0xffff; - hdlc->shift_reg = 0; - hdlc->hdlc_bits1 = 0; - hdlc->data_bits = 0; - hdlc->data_received = 0; - } - hdlc->cbin <<= 1; - hdlc->bit_shift--; - break; - case HDLC_GET_DATA: - if (hdlc->cbin & 0x80) { - hdlc->hdlc_bits1++; - switch (hdlc->hdlc_bits1) { - case 6: - break; - case 7: - if (hdlc->data_received) - /* bad frame */ - status = -HDLC_FRAMING_ERROR; - if (!hdlc->do_adapt56) { - if (hdlc->cbin == fast_abort - [hdlc->bit_shift + 1]) { - hdlc->state = - HDLC_FAST_IDLE; - hdlc->bit_shift = 1; - break; - } - } else - hdlc->state = HDLC_GET_FLAG_B0; - break; - default: - hdlc->shift_reg >>= 1; - hdlc->shift_reg |= 0x80; - hdlc->data_bits++; - break; - } - } else { - switch (hdlc->hdlc_bits1) { - case 5: - break; - case 6: - if (hdlc->data_received) - status = check_frame(hdlc); - hdlc->crc = 0xffff; - hdlc->shift_reg = 0; - hdlc->data_bits = 0; - if (!hdlc->do_adapt56) - handle_fast_flag(hdlc); - else { - hdlc->state = HDLC_GET_DATA; - hdlc->data_received = 0; - } - break; - default: - hdlc->shift_reg >>= 1; - hdlc->data_bits++; - break; - } - hdlc->hdlc_bits1 = 0; - } - if (status) { - hdlc->dstpos = 0; - *count -= slen; - hdlc->cbin <<= 1; - hdlc->bit_shift--; - return status; - } - if (hdlc->data_bits == 8) { - hdlc->data_bits = 0; - hdlc->data_received = 1; - hdlc->crc = crc_ccitt_byte(hdlc->crc, - hdlc->shift_reg); - - /* good byte received */ - if (hdlc->dstpos < dsize) - dst[hdlc->dstpos++] = hdlc->shift_reg; - else { - /* frame too long */ - status = -HDLC_LENGTH_ERROR; - hdlc->dstpos = 0; - } - } - hdlc->cbin <<= 1; - hdlc->bit_shift--; - break; - case HDLC_FAST_FLAG: - if (hdlc->cbin == hdlc->ffvalue) { - hdlc->bit_shift = 0; - break; - } else { - if (hdlc->cbin == 0xff) { - hdlc->state = HDLC_FAST_IDLE; - hdlc->bit_shift = 0; - } else if (hdlc->ffbit_shift == 8) { - hdlc->state = HDLC_GETFLAG_B7; - break; - } else - handle_abort(hdlc); - } - break; - default: - break; - } - } - *count -= slen; - return 0; -} -EXPORT_SYMBOL(isdnhdlc_decode); -/* - isdnhdlc_encode - encodes HDLC frames to a transparent bit stream. - - The bit stream starts with a beginning flag (01111110). After - that each byte is added to the bit stream with bit stuffing added - (0 after 5 1's). - When the last byte has been removed from the source buffer, the - CRC (2 bytes is added) and the frame terminates with the ending flag. - For the dchannel, the idle character (all 1's) is also added at the end. - If this function is called with empty source buffer (slen=0), flags or - idle character will be generated. - - src - source buffer - slen - source buffer length - count - number of bytes removed (encoded) from source buffer - dst _ destination buffer - dsize - destination buffer size - returns - number of encoded bytes in the destination buffer -*/ -int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src, u16 slen, - int *count, u8 *dst, int dsize) -{ - static const unsigned char xfast_flag_value[] = { - 0x7e, 0x3f, 0x9f, 0xcf, 0xe7, 0xf3, 0xf9, 0xfc, 0x7e - }; - - int len = 0; - - *count = slen; - - /* special handling for one byte frames */ - if ((slen == 1) && (hdlc->state == HDLC_SEND_FAST_FLAG)) - hdlc->state = HDLC_SENDFLAG_ONE; - while (dsize > 0) { - if (hdlc->bit_shift == 0) { - if (slen && !hdlc->do_closing) { - hdlc->shift_reg = *src++; - slen--; - if (slen == 0) - /* closing sequence, CRC + flag(s) */ - hdlc->do_closing = 1; - hdlc->bit_shift = 8; - } else { - if (hdlc->state == HDLC_SEND_DATA) { - if (hdlc->data_received) { - hdlc->state = HDLC_SEND_CRC1; - hdlc->crc ^= 0xffff; - hdlc->bit_shift = 8; - hdlc->shift_reg = - hdlc->crc & 0xff; - } else if (!hdlc->do_adapt56) - hdlc->state = - HDLC_SEND_FAST_FLAG; - else - hdlc->state = - HDLC_SENDFLAG_B0; - } - - } - } - - switch (hdlc->state) { - case STOPPED: - while (dsize--) - *dst++ = 0xff; - return dsize; - case HDLC_SEND_FAST_FLAG: - hdlc->do_closing = 0; - if (slen == 0) { - /* the code is for bitreverse streams */ - if (hdlc->do_bitreverse == 0) - *dst++ = bitrev8(hdlc->ffvalue); - else - *dst++ = hdlc->ffvalue; - len++; - dsize--; - break; - } - fallthrough; - case HDLC_SENDFLAG_ONE: - if (hdlc->bit_shift == 8) { - hdlc->cbin = hdlc->ffvalue >> - (8 - hdlc->data_bits); - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - hdlc->data_received = 1; - } - break; - case HDLC_SENDFLAG_B0: - hdlc->do_closing = 0; - hdlc->cbin <<= 1; - hdlc->data_bits++; - hdlc->hdlc_bits1 = 0; - hdlc->state = HDLC_SENDFLAG_B1A6; - break; - case HDLC_SENDFLAG_B1A6: - hdlc->cbin <<= 1; - hdlc->data_bits++; - hdlc->cbin++; - if (++hdlc->hdlc_bits1 == 6) - hdlc->state = HDLC_SENDFLAG_B7; - break; - case HDLC_SENDFLAG_B7: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (slen == 0) { - hdlc->state = HDLC_SENDFLAG_B0; - break; - } - if (hdlc->bit_shift == 8) { - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - hdlc->data_received = 1; - } - break; - case HDLC_SEND_FIRST_FLAG: - hdlc->data_received = 1; - if (hdlc->data_bits == 8) { - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - break; - } - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->shift_reg & 0x01) - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - if (hdlc->bit_shift == 0) { - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - } - break; - case HDLC_SEND_DATA: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->hdlc_bits1 == 5) { - hdlc->hdlc_bits1 = 0; - break; - } - if (hdlc->bit_shift == 8) - hdlc->crc = crc_ccitt_byte(hdlc->crc, - hdlc->shift_reg); - if (hdlc->shift_reg & 0x01) { - hdlc->hdlc_bits1++; - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } else { - hdlc->hdlc_bits1 = 0; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } - break; - case HDLC_SEND_CRC1: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->hdlc_bits1 == 5) { - hdlc->hdlc_bits1 = 0; - break; - } - if (hdlc->shift_reg & 0x01) { - hdlc->hdlc_bits1++; - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } else { - hdlc->hdlc_bits1 = 0; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } - if (hdlc->bit_shift == 0) { - hdlc->shift_reg = (hdlc->crc >> 8); - hdlc->state = HDLC_SEND_CRC2; - hdlc->bit_shift = 8; - } - break; - case HDLC_SEND_CRC2: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->hdlc_bits1 == 5) { - hdlc->hdlc_bits1 = 0; - break; - } - if (hdlc->shift_reg & 0x01) { - hdlc->hdlc_bits1++; - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } else { - hdlc->hdlc_bits1 = 0; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } - if (hdlc->bit_shift == 0) { - hdlc->shift_reg = 0x7e; - hdlc->state = HDLC_SEND_CLOSING_FLAG; - hdlc->bit_shift = 8; - } - break; - case HDLC_SEND_CLOSING_FLAG: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if (hdlc->hdlc_bits1 == 5) { - hdlc->hdlc_bits1 = 0; - break; - } - if (hdlc->shift_reg & 0x01) - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - if (hdlc->bit_shift == 0) { - hdlc->ffvalue = - xfast_flag_value[hdlc->data_bits]; - if (hdlc->dchannel) { - hdlc->ffvalue = 0x7e; - hdlc->state = HDLC_SEND_IDLE1; - hdlc->bit_shift = 8-hdlc->data_bits; - if (hdlc->bit_shift == 0) - hdlc->state = - HDLC_SEND_FAST_IDLE; - } else { - if (!hdlc->do_adapt56) { - hdlc->state = - HDLC_SEND_FAST_FLAG; - hdlc->data_received = 0; - } else { - hdlc->state = HDLC_SENDFLAG_B0; - hdlc->data_received = 0; - } - /* Finished this frame, send flags */ - if (dsize > 1) - dsize = 1; - } - } - break; - case HDLC_SEND_IDLE1: - hdlc->do_closing = 0; - hdlc->cbin <<= 1; - hdlc->cbin++; - hdlc->data_bits++; - hdlc->bit_shift--; - if (hdlc->bit_shift == 0) { - hdlc->state = HDLC_SEND_FAST_IDLE; - hdlc->bit_shift = 0; - } - break; - case HDLC_SEND_FAST_IDLE: - hdlc->do_closing = 0; - hdlc->cbin = 0xff; - hdlc->data_bits = 8; - if (hdlc->bit_shift == 8) { - hdlc->cbin = 0x7e; - hdlc->state = HDLC_SEND_FIRST_FLAG; - } else { - /* the code is for bitreverse streams */ - if (hdlc->do_bitreverse == 0) - *dst++ = bitrev8(hdlc->cbin); - else - *dst++ = hdlc->cbin; - hdlc->bit_shift = 0; - hdlc->data_bits = 0; - len++; - dsize = 0; - } - break; - default: - break; - } - if (hdlc->do_adapt56) { - if (hdlc->data_bits == 7) { - hdlc->cbin <<= 1; - hdlc->cbin++; - hdlc->data_bits++; - } - } - if (hdlc->data_bits == 8) { - /* the code is for bitreverse streams */ - if (hdlc->do_bitreverse == 0) - *dst++ = bitrev8(hdlc->cbin); - else - *dst++ = hdlc->cbin; - hdlc->data_bits = 0; - len++; - dsize--; - } - } - *count -= slen; - - return len; -} -EXPORT_SYMBOL(isdnhdlc_encode); diff --git a/drivers/isdn/hardware/mISDN/isdnhdlc.h b/drivers/isdn/hardware/mISDN/isdnhdlc.h deleted file mode 100644 index fe2c1279c139..000000000000 --- a/drivers/isdn/hardware/mISDN/isdnhdlc.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * hdlc.h -- General purpose ISDN HDLC decoder. - * - * Implementation of a HDLC decoder/encoder in software. - * Necessary because some ISDN devices don't have HDLC - * controllers. - * - * Copyright (C) - * 2009 Karsten Keil - * 2002 Wolfgang Mües - * 2001 Frode Isaksen - * 2001 Kai Germaschewski - */ - -#ifndef __ISDNHDLC_H__ -#define __ISDNHDLC_H__ - -struct isdnhdlc_vars { - int bit_shift; - int hdlc_bits1; - int data_bits; - int ffbit_shift; /* encoding only */ - int state; - int dstpos; - - u16 crc; - - u8 cbin; - u8 shift_reg; - u8 ffvalue; - - /* set if transferring data */ - u32 data_received:1; - /* set if D channel (send idle instead of flags) */ - u32 dchannel:1; - /* set if 56K adaptation */ - u32 do_adapt56:1; - /* set if in closing phase (need to send CRC + flag) */ - u32 do_closing:1; - /* set if data is bitreverse */ - u32 do_bitreverse:1; -}; - -/* Feature Flags */ -#define HDLC_56KBIT 0x01 -#define HDLC_DCHANNEL 0x02 -#define HDLC_BITREVERSE 0x04 - -/* - The return value from isdnhdlc_decode is - the frame length, 0 if no complete frame was decoded, - or a negative error number -*/ -#define HDLC_FRAMING_ERROR 1 -#define HDLC_CRC_ERROR 2 -#define HDLC_LENGTH_ERROR 3 - -extern void isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features); - -extern int isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src, - int slen, int *count, u8 *dst, int dsize); - -extern void isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features); - -extern int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src, - u16 slen, int *count, u8 *dst, int dsize); - -#endif /* __ISDNHDLC_H__ */ diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c deleted file mode 100644 index aaa639ad5526..000000000000 --- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c +++ /dev/null @@ -1,1168 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * mISDNinfineon.c - * Support for cards based on following Infineon ISDN chipsets - * - ISAC + HSCX - * - IPAC and IPAC-X - * - ISAC-SX + HSCX - * - * Supported cards: - * - Dialogic Diva 2.0 - * - Dialogic Diva 2.0U - * - Dialogic Diva 2.01 - * - Dialogic Diva 2.02 - * - Sedlbauer Speedwin - * - HST Saphir3 - * - Develo (former ELSA) Microlink PCI (Quickstep 1000) - * - Develo (former ELSA) Quickstep 3000 - * - Berkom Scitel BRIX Quadro - * - Dr.Neuhaus (Sagem) Niccy - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include -#include "ipac.h" - -#define INFINEON_REV "1.0" - -static int inf_cnt; -static u32 debug; -static u32 irqloops = 4; - -enum inf_types { - INF_NONE, - INF_DIVA20, - INF_DIVA20U, - INF_DIVA201, - INF_DIVA202, - INF_SPEEDWIN, - INF_SAPHIR3, - INF_QS1000, - INF_QS3000, - INF_NICCY, - INF_SCT_1, - INF_SCT_2, - INF_SCT_3, - INF_SCT_4, - INF_GAZEL_R685, - INF_GAZEL_R753 -}; - -enum addr_mode { - AM_NONE = 0, - AM_IO, - AM_MEMIO, - AM_IND_IO, -}; - -struct inf_cinfo { - enum inf_types typ; - const char *full; - const char *name; - enum addr_mode cfg_mode; - enum addr_mode addr_mode; - u8 cfg_bar; - u8 addr_bar; - void *irqfunc; -}; - -struct _ioaddr { - enum addr_mode mode; - union { - void __iomem *p; - struct _ioport io; - } a; -}; - -struct _iohandle { - enum addr_mode mode; - resource_size_t size; - resource_size_t start; - void __iomem *p; -}; - -struct inf_hw { - struct list_head list; - struct pci_dev *pdev; - const struct inf_cinfo *ci; - char name[MISDN_MAX_IDLEN]; - u32 irq; - u32 irqcnt; - struct _iohandle cfg; - struct _iohandle addr; - struct _ioaddr isac; - struct _ioaddr hscx; - spinlock_t lock; /* HW access lock */ - struct ipac_hw ipac; - struct inf_hw *sc[3]; /* slave cards */ -}; - - -#define PCI_SUBVENDOR_HST_SAPHIR3 0x52 -#define PCI_SUBVENDOR_SEDLBAUER_PCI 0x53 -#define PCI_SUB_ID_SEDLBAUER 0x01 - -static struct pci_device_id infineon_ids[] = { - { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA20), INF_DIVA20 }, - { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA20_U), INF_DIVA20U }, - { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA201), INF_DIVA201 }, - { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA202), INF_DIVA202 }, - { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, - PCI_SUBVENDOR_SEDLBAUER_PCI, PCI_SUB_ID_SEDLBAUER, 0, 0, - INF_SPEEDWIN }, - { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, - PCI_SUBVENDOR_HST_SAPHIR3, PCI_SUB_ID_SEDLBAUER, 0, 0, INF_SAPHIR3 }, - { PCI_VDEVICE(ELSA, PCI_DEVICE_ID_ELSA_MICROLINK), INF_QS1000 }, - { PCI_VDEVICE(ELSA, PCI_DEVICE_ID_ELSA_QS3000), INF_QS3000 }, - { PCI_VDEVICE(SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY), INF_NICCY }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO, 0, 0, - INF_SCT_1 }, - { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_R685), INF_GAZEL_R685 }, - { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_R753), INF_GAZEL_R753 }, - { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_DJINN_ITOO), INF_GAZEL_R753 }, - { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_OLITEC), INF_GAZEL_R753 }, - { } -}; -MODULE_DEVICE_TABLE(pci, infineon_ids); - -/* PCI interface specific defines */ -/* Diva 2.0/2.0U */ -#define DIVA_HSCX_PORT 0x00 -#define DIVA_HSCX_ALE 0x04 -#define DIVA_ISAC_PORT 0x08 -#define DIVA_ISAC_ALE 0x0C -#define DIVA_PCI_CTRL 0x10 - -/* DIVA_PCI_CTRL bits */ -#define DIVA_IRQ_BIT 0x01 -#define DIVA_RESET_BIT 0x08 -#define DIVA_EEPROM_CLK 0x40 -#define DIVA_LED_A 0x10 -#define DIVA_LED_B 0x20 -#define DIVA_IRQ_CLR 0x80 - -/* Diva 2.01/2.02 */ -/* Siemens PITA */ -#define PITA_ICR_REG 0x00 -#define PITA_INT0_STATUS 0x02 - -#define PITA_MISC_REG 0x1c -#define PITA_PARA_SOFTRESET 0x01000000 -#define PITA_SER_SOFTRESET 0x02000000 -#define PITA_PARA_MPX_MODE 0x04000000 -#define PITA_INT0_ENABLE 0x00020000 - -/* TIGER 100 Registers */ -#define TIGER_RESET_ADDR 0x00 -#define TIGER_EXTERN_RESET 0x01 -#define TIGER_AUX_CTRL 0x02 -#define TIGER_AUX_DATA 0x03 -#define TIGER_AUX_IRQMASK 0x05 -#define TIGER_AUX_STATUS 0x07 - -/* Tiger AUX BITs */ -#define TIGER_IOMASK 0xdd /* 1 and 5 are inputs */ -#define TIGER_IRQ_BIT 0x02 - -#define TIGER_IPAC_ALE 0xC0 -#define TIGER_IPAC_PORT 0xC8 - -/* ELSA (now Develo) PCI cards */ -#define ELSA_IRQ_ADDR 0x4c -#define ELSA_IRQ_MASK 0x04 -#define QS1000_IRQ_OFF 0x01 -#define QS3000_IRQ_OFF 0x03 -#define QS1000_IRQ_ON 0x41 -#define QS3000_IRQ_ON 0x43 - -/* Dr Neuhaus/Sagem Niccy */ -#define NICCY_ISAC_PORT 0x00 -#define NICCY_HSCX_PORT 0x01 -#define NICCY_ISAC_ALE 0x02 -#define NICCY_HSCX_ALE 0x03 - -#define NICCY_IRQ_CTRL_REG 0x38 -#define NICCY_IRQ_ENABLE 0x001f00 -#define NICCY_IRQ_DISABLE 0xff0000 -#define NICCY_IRQ_BIT 0x800000 - - -/* Scitel PLX */ -#define SCT_PLX_IRQ_ADDR 0x4c -#define SCT_PLX_RESET_ADDR 0x50 -#define SCT_PLX_IRQ_ENABLE 0x41 -#define SCT_PLX_RESET_BIT 0x04 - -/* Gazel */ -#define GAZEL_IPAC_DATA_PORT 0x04 -/* Gazel PLX */ -#define GAZEL_CNTRL 0x50 -#define GAZEL_RESET 0x04 -#define GAZEL_RESET_9050 0x40000000 -#define GAZEL_INCSR 0x4C -#define GAZEL_ISAC_EN 0x08 -#define GAZEL_INT_ISAC 0x20 -#define GAZEL_HSCX_EN 0x01 -#define GAZEL_INT_HSCX 0x04 -#define GAZEL_PCI_EN 0x40 -#define GAZEL_IPAC_EN 0x03 - - -static LIST_HEAD(Cards); -static DEFINE_RWLOCK(card_lock); /* protect Cards */ - -static void -_set_debug(struct inf_hw *card) -{ - card->ipac.isac.dch.debug = debug; - card->ipac.hscx[0].bch.debug = debug; - card->ipac.hscx[1].bch.debug = debug; -} - -static int -set_debug(const char *val, const struct kernel_param *kp) -{ - int ret; - struct inf_hw *card; - - ret = param_set_uint(val, kp); - if (!ret) { - read_lock(&card_lock); - list_for_each_entry(card, &Cards, list) - _set_debug(card); - read_unlock(&card_lock); - } - return ret; -} - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for cards based on Infineon ISDN chipsets"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(INFINEON_REV); -module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "infineon debug mask"); -module_param(irqloops, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(irqloops, "infineon maximal irqloops (default 4)"); - -/* Interface functions */ - -IOFUNC_IO(ISAC, inf_hw, isac.a.io) -IOFUNC_IO(IPAC, inf_hw, hscx.a.io) -IOFUNC_IND(ISAC, inf_hw, isac.a.io) -IOFUNC_IND(IPAC, inf_hw, hscx.a.io) -IOFUNC_MEMIO(ISAC, inf_hw, u32, isac.a.p) -IOFUNC_MEMIO(IPAC, inf_hw, u32, hscx.a.p) - -static irqreturn_t -diva_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - u8 val; - - spin_lock(&hw->lock); - val = inb((u32)hw->cfg.start + DIVA_PCI_CTRL); - if (!(val & DIVA_IRQ_BIT)) { /* for us or shared ? */ - spin_unlock(&hw->lock); - return IRQ_NONE; /* shared */ - } - hw->irqcnt++; - mISDNipac_irq(&hw->ipac, irqloops); - spin_unlock(&hw->lock); - return IRQ_HANDLED; -} - -static irqreturn_t -diva20x_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - u8 val; - - spin_lock(&hw->lock); - val = readb(hw->cfg.p); - if (!(val & PITA_INT0_STATUS)) { /* for us or shared ? */ - spin_unlock(&hw->lock); - return IRQ_NONE; /* shared */ - } - hw->irqcnt++; - mISDNipac_irq(&hw->ipac, irqloops); - writeb(PITA_INT0_STATUS, hw->cfg.p); /* ACK PITA INT0 */ - spin_unlock(&hw->lock); - return IRQ_HANDLED; -} - -static irqreturn_t -tiger_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - u8 val; - - spin_lock(&hw->lock); - val = inb((u32)hw->cfg.start + TIGER_AUX_STATUS); - if (val & TIGER_IRQ_BIT) { /* for us or shared ? */ - spin_unlock(&hw->lock); - return IRQ_NONE; /* shared */ - } - hw->irqcnt++; - mISDNipac_irq(&hw->ipac, irqloops); - spin_unlock(&hw->lock); - return IRQ_HANDLED; -} - -static irqreturn_t -elsa_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - u8 val; - - spin_lock(&hw->lock); - val = inb((u32)hw->cfg.start + ELSA_IRQ_ADDR); - if (!(val & ELSA_IRQ_MASK)) { - spin_unlock(&hw->lock); - return IRQ_NONE; /* shared */ - } - hw->irqcnt++; - mISDNipac_irq(&hw->ipac, irqloops); - spin_unlock(&hw->lock); - return IRQ_HANDLED; -} - -static irqreturn_t -niccy_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - u32 val; - - spin_lock(&hw->lock); - val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); - if (!(val & NICCY_IRQ_BIT)) { /* for us or shared ? */ - spin_unlock(&hw->lock); - return IRQ_NONE; /* shared */ - } - outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); - hw->irqcnt++; - mISDNipac_irq(&hw->ipac, irqloops); - spin_unlock(&hw->lock); - return IRQ_HANDLED; -} - -static irqreturn_t -gazel_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - irqreturn_t ret; - - spin_lock(&hw->lock); - ret = mISDNipac_irq(&hw->ipac, irqloops); - spin_unlock(&hw->lock); - return ret; -} - -static irqreturn_t -ipac_irq(int intno, void *dev_id) -{ - struct inf_hw *hw = dev_id; - u8 val; - - spin_lock(&hw->lock); - val = hw->ipac.read_reg(hw, IPAC_ISTA); - if (!(val & 0x3f)) { - spin_unlock(&hw->lock); - return IRQ_NONE; /* shared */ - } - hw->irqcnt++; - mISDNipac_irq(&hw->ipac, irqloops); - spin_unlock(&hw->lock); - return IRQ_HANDLED; -} - -static void -enable_hwirq(struct inf_hw *hw) -{ - u16 w; - u32 val; - - switch (hw->ci->typ) { - case INF_DIVA201: - case INF_DIVA202: - writel(PITA_INT0_ENABLE, hw->cfg.p); - break; - case INF_SPEEDWIN: - case INF_SAPHIR3: - outb(TIGER_IRQ_BIT, (u32)hw->cfg.start + TIGER_AUX_IRQMASK); - break; - case INF_QS1000: - outb(QS1000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR); - break; - case INF_QS3000: - outb(QS3000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR); - break; - case INF_NICCY: - val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); - val |= NICCY_IRQ_ENABLE; - outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); - break; - case INF_SCT_1: - w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); - w |= SCT_PLX_IRQ_ENABLE; - outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); - break; - case INF_GAZEL_R685: - outb(GAZEL_ISAC_EN + GAZEL_HSCX_EN + GAZEL_PCI_EN, - (u32)hw->cfg.start + GAZEL_INCSR); - break; - case INF_GAZEL_R753: - outb(GAZEL_IPAC_EN + GAZEL_PCI_EN, - (u32)hw->cfg.start + GAZEL_INCSR); - break; - default: - break; - } -} - -static void -disable_hwirq(struct inf_hw *hw) -{ - u16 w; - u32 val; - - switch (hw->ci->typ) { - case INF_DIVA201: - case INF_DIVA202: - writel(0, hw->cfg.p); - break; - case INF_SPEEDWIN: - case INF_SAPHIR3: - outb(0, (u32)hw->cfg.start + TIGER_AUX_IRQMASK); - break; - case INF_QS1000: - outb(QS1000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR); - break; - case INF_QS3000: - outb(QS3000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR); - break; - case INF_NICCY: - val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); - val &= NICCY_IRQ_DISABLE; - outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); - break; - case INF_SCT_1: - w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); - w &= (~SCT_PLX_IRQ_ENABLE); - outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); - break; - case INF_GAZEL_R685: - case INF_GAZEL_R753: - outb(0, (u32)hw->cfg.start + GAZEL_INCSR); - break; - default: - break; - } -} - -static void -ipac_chip_reset(struct inf_hw *hw) -{ - hw->ipac.write_reg(hw, IPAC_POTA2, 0x20); - mdelay(5); - hw->ipac.write_reg(hw, IPAC_POTA2, 0x00); - mdelay(5); - hw->ipac.write_reg(hw, IPAC_CONF, hw->ipac.conf); - hw->ipac.write_reg(hw, IPAC_MASK, 0xc0); -} - -static void -reset_inf(struct inf_hw *hw) -{ - u16 w; - u32 val; - - if (debug & DEBUG_HW) - pr_notice("%s: resetting card\n", hw->name); - switch (hw->ci->typ) { - case INF_DIVA20: - case INF_DIVA20U: - outb(0, (u32)hw->cfg.start + DIVA_PCI_CTRL); - mdelay(10); - outb(DIVA_RESET_BIT, (u32)hw->cfg.start + DIVA_PCI_CTRL); - mdelay(10); - /* Workaround PCI9060 */ - outb(9, (u32)hw->cfg.start + 0x69); - outb(DIVA_RESET_BIT | DIVA_LED_A, - (u32)hw->cfg.start + DIVA_PCI_CTRL); - break; - case INF_DIVA201: - writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE, - hw->cfg.p + PITA_MISC_REG); - mdelay(1); - writel(PITA_PARA_MPX_MODE, hw->cfg.p + PITA_MISC_REG); - mdelay(10); - break; - case INF_DIVA202: - writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE, - hw->cfg.p + PITA_MISC_REG); - mdelay(1); - writel(PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET, - hw->cfg.p + PITA_MISC_REG); - mdelay(10); - break; - case INF_SPEEDWIN: - case INF_SAPHIR3: - ipac_chip_reset(hw); - hw->ipac.write_reg(hw, IPAC_ACFG, 0xff); - hw->ipac.write_reg(hw, IPAC_AOE, 0x00); - hw->ipac.write_reg(hw, IPAC_PCFG, 0x12); - break; - case INF_QS1000: - case INF_QS3000: - ipac_chip_reset(hw); - hw->ipac.write_reg(hw, IPAC_ACFG, 0x00); - hw->ipac.write_reg(hw, IPAC_AOE, 0x3c); - hw->ipac.write_reg(hw, IPAC_ATX, 0xff); - break; - case INF_NICCY: - break; - case INF_SCT_1: - w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR); - w &= (~SCT_PLX_RESET_BIT); - outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR); - mdelay(10); - w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR); - w |= SCT_PLX_RESET_BIT; - outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR); - mdelay(10); - break; - case INF_GAZEL_R685: - val = inl((u32)hw->cfg.start + GAZEL_CNTRL); - val |= (GAZEL_RESET_9050 + GAZEL_RESET); - outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); - val &= ~(GAZEL_RESET_9050 + GAZEL_RESET); - mdelay(4); - outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); - mdelay(10); - hw->ipac.isac.adf2 = 0x87; - hw->ipac.hscx[0].slot = 0x1f; - hw->ipac.hscx[1].slot = 0x23; - break; - case INF_GAZEL_R753: - val = inl((u32)hw->cfg.start + GAZEL_CNTRL); - val |= (GAZEL_RESET_9050 + GAZEL_RESET); - outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); - val &= ~(GAZEL_RESET_9050 + GAZEL_RESET); - mdelay(4); - outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); - mdelay(10); - ipac_chip_reset(hw); - hw->ipac.write_reg(hw, IPAC_ACFG, 0xff); - hw->ipac.write_reg(hw, IPAC_AOE, 0x00); - hw->ipac.conf = 0x01; /* IOM off */ - break; - default: - return; - } - enable_hwirq(hw); -} - -static int -inf_ctrl(struct inf_hw *hw, u32 cmd, u_long arg) -{ - int ret = 0; - - switch (cmd) { - case HW_RESET_REQ: - reset_inf(hw); - break; - default: - pr_info("%s: %s unknown command %x %lx\n", - hw->name, __func__, cmd, arg); - ret = -EINVAL; - break; - } - return ret; -} - -static int -init_irq(struct inf_hw *hw) -{ - int ret, cnt = 3; - u_long flags; - - if (!hw->ci->irqfunc) - return -EINVAL; - ret = request_irq(hw->irq, hw->ci->irqfunc, IRQF_SHARED, hw->name, hw); - if (ret) { - pr_info("%s: couldn't get interrupt %d\n", hw->name, hw->irq); - return ret; - } - while (cnt--) { - spin_lock_irqsave(&hw->lock, flags); - reset_inf(hw); - ret = hw->ipac.init(&hw->ipac); - if (ret) { - spin_unlock_irqrestore(&hw->lock, flags); - pr_info("%s: ISAC init failed with %d\n", - hw->name, ret); - break; - } - spin_unlock_irqrestore(&hw->lock, flags); - msleep_interruptible(10); - if (debug & DEBUG_HW) - pr_notice("%s: IRQ %d count %d\n", hw->name, - hw->irq, hw->irqcnt); - if (!hw->irqcnt) { - pr_info("%s: IRQ(%d) got no requests during init %d\n", - hw->name, hw->irq, 3 - cnt); - } else - return 0; - } - free_irq(hw->irq, hw); - return -EIO; -} - -static void -release_io(struct inf_hw *hw) -{ - if (hw->cfg.mode) { - if (hw->cfg.mode == AM_MEMIO) { - release_mem_region(hw->cfg.start, hw->cfg.size); - if (hw->cfg.p) - iounmap(hw->cfg.p); - } else - release_region(hw->cfg.start, hw->cfg.size); - hw->cfg.mode = AM_NONE; - } - if (hw->addr.mode) { - if (hw->addr.mode == AM_MEMIO) { - release_mem_region(hw->addr.start, hw->addr.size); - if (hw->addr.p) - iounmap(hw->addr.p); - } else - release_region(hw->addr.start, hw->addr.size); - hw->addr.mode = AM_NONE; - } -} - -static int -setup_io(struct inf_hw *hw) -{ - int err = 0; - - if (hw->ci->cfg_mode) { - hw->cfg.start = pci_resource_start(hw->pdev, hw->ci->cfg_bar); - hw->cfg.size = pci_resource_len(hw->pdev, hw->ci->cfg_bar); - if (hw->ci->cfg_mode == AM_MEMIO) { - if (!request_mem_region(hw->cfg.start, hw->cfg.size, - hw->name)) - err = -EBUSY; - } else { - if (!request_region(hw->cfg.start, hw->cfg.size, - hw->name)) - err = -EBUSY; - } - if (err) { - pr_info("mISDN: %s config port %lx (%lu bytes)" - "already in use\n", hw->name, - (ulong)hw->cfg.start, (ulong)hw->cfg.size); - return err; - } - hw->cfg.mode = hw->ci->cfg_mode; - if (hw->ci->cfg_mode == AM_MEMIO) { - hw->cfg.p = ioremap(hw->cfg.start, hw->cfg.size); - if (!hw->cfg.p) - return -ENOMEM; - } - if (debug & DEBUG_HW) - pr_notice("%s: IO cfg %lx (%lu bytes) mode%d\n", - hw->name, (ulong)hw->cfg.start, - (ulong)hw->cfg.size, hw->ci->cfg_mode); - - } - if (hw->ci->addr_mode) { - hw->addr.start = pci_resource_start(hw->pdev, hw->ci->addr_bar); - hw->addr.size = pci_resource_len(hw->pdev, hw->ci->addr_bar); - if (hw->ci->addr_mode == AM_MEMIO) { - if (!request_mem_region(hw->addr.start, hw->addr.size, - hw->name)) - err = -EBUSY; - } else { - if (!request_region(hw->addr.start, hw->addr.size, - hw->name)) - err = -EBUSY; - } - if (err) { - pr_info("mISDN: %s address port %lx (%lu bytes)" - "already in use\n", hw->name, - (ulong)hw->addr.start, (ulong)hw->addr.size); - return err; - } - hw->addr.mode = hw->ci->addr_mode; - if (hw->ci->addr_mode == AM_MEMIO) { - hw->addr.p = ioremap(hw->addr.start, hw->addr.size); - if (!hw->addr.p) - return -ENOMEM; - } - if (debug & DEBUG_HW) - pr_notice("%s: IO addr %lx (%lu bytes) mode%d\n", - hw->name, (ulong)hw->addr.start, - (ulong)hw->addr.size, hw->ci->addr_mode); - - } - - switch (hw->ci->typ) { - case INF_DIVA20: - case INF_DIVA20U: - hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX; - hw->isac.mode = hw->cfg.mode; - hw->isac.a.io.ale = (u32)hw->cfg.start + DIVA_ISAC_ALE; - hw->isac.a.io.port = (u32)hw->cfg.start + DIVA_ISAC_PORT; - hw->hscx.mode = hw->cfg.mode; - hw->hscx.a.io.ale = (u32)hw->cfg.start + DIVA_HSCX_ALE; - hw->hscx.a.io.port = (u32)hw->cfg.start + DIVA_HSCX_PORT; - break; - case INF_DIVA201: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.mode = hw->addr.mode; - hw->isac.a.p = hw->addr.p; - hw->hscx.mode = hw->addr.mode; - hw->hscx.a.p = hw->addr.p; - break; - case INF_DIVA202: - hw->ipac.type = IPAC_TYPE_IPACX; - hw->isac.mode = hw->addr.mode; - hw->isac.a.p = hw->addr.p; - hw->hscx.mode = hw->addr.mode; - hw->hscx.a.p = hw->addr.p; - break; - case INF_SPEEDWIN: - case INF_SAPHIR3: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.mode = hw->cfg.mode; - hw->isac.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE; - hw->isac.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT; - hw->hscx.mode = hw->cfg.mode; - hw->hscx.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE; - hw->hscx.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT; - outb(0xff, (ulong)hw->cfg.start); - mdelay(1); - outb(0x00, (ulong)hw->cfg.start); - mdelay(1); - outb(TIGER_IOMASK, (ulong)hw->cfg.start + TIGER_AUX_CTRL); - break; - case INF_QS1000: - case INF_QS3000: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.a.io.ale = (u32)hw->addr.start; - hw->isac.a.io.port = (u32)hw->addr.start + 1; - hw->isac.mode = hw->addr.mode; - hw->hscx.a.io.ale = (u32)hw->addr.start; - hw->hscx.a.io.port = (u32)hw->addr.start + 1; - hw->hscx.mode = hw->addr.mode; - break; - case INF_NICCY: - hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX; - hw->isac.mode = hw->addr.mode; - hw->isac.a.io.ale = (u32)hw->addr.start + NICCY_ISAC_ALE; - hw->isac.a.io.port = (u32)hw->addr.start + NICCY_ISAC_PORT; - hw->hscx.mode = hw->addr.mode; - hw->hscx.a.io.ale = (u32)hw->addr.start + NICCY_HSCX_ALE; - hw->hscx.a.io.port = (u32)hw->addr.start + NICCY_HSCX_PORT; - break; - case INF_SCT_1: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.a.io.ale = (u32)hw->addr.start; - hw->isac.a.io.port = hw->isac.a.io.ale + 4; - hw->isac.mode = hw->addr.mode; - hw->hscx.a.io.ale = hw->isac.a.io.ale; - hw->hscx.a.io.port = hw->isac.a.io.port; - hw->hscx.mode = hw->addr.mode; - break; - case INF_SCT_2: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.a.io.ale = (u32)hw->addr.start + 0x08; - hw->isac.a.io.port = hw->isac.a.io.ale + 4; - hw->isac.mode = hw->addr.mode; - hw->hscx.a.io.ale = hw->isac.a.io.ale; - hw->hscx.a.io.port = hw->isac.a.io.port; - hw->hscx.mode = hw->addr.mode; - break; - case INF_SCT_3: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.a.io.ale = (u32)hw->addr.start + 0x10; - hw->isac.a.io.port = hw->isac.a.io.ale + 4; - hw->isac.mode = hw->addr.mode; - hw->hscx.a.io.ale = hw->isac.a.io.ale; - hw->hscx.a.io.port = hw->isac.a.io.port; - hw->hscx.mode = hw->addr.mode; - break; - case INF_SCT_4: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.a.io.ale = (u32)hw->addr.start + 0x20; - hw->isac.a.io.port = hw->isac.a.io.ale + 4; - hw->isac.mode = hw->addr.mode; - hw->hscx.a.io.ale = hw->isac.a.io.ale; - hw->hscx.a.io.port = hw->isac.a.io.port; - hw->hscx.mode = hw->addr.mode; - break; - case INF_GAZEL_R685: - hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX; - hw->ipac.isac.off = 0x80; - hw->isac.mode = hw->addr.mode; - hw->isac.a.io.port = (u32)hw->addr.start; - hw->hscx.mode = hw->addr.mode; - hw->hscx.a.io.port = hw->isac.a.io.port; - break; - case INF_GAZEL_R753: - hw->ipac.type = IPAC_TYPE_IPAC; - hw->ipac.isac.off = 0x80; - hw->isac.mode = hw->addr.mode; - hw->isac.a.io.ale = (u32)hw->addr.start; - hw->isac.a.io.port = (u32)hw->addr.start + GAZEL_IPAC_DATA_PORT; - hw->hscx.mode = hw->addr.mode; - hw->hscx.a.io.ale = hw->isac.a.io.ale; - hw->hscx.a.io.port = hw->isac.a.io.port; - break; - default: - return -EINVAL; - } - switch (hw->isac.mode) { - case AM_MEMIO: - ASSIGN_FUNC_IPAC(MIO, hw->ipac); - break; - case AM_IND_IO: - ASSIGN_FUNC_IPAC(IND, hw->ipac); - break; - case AM_IO: - ASSIGN_FUNC_IPAC(IO, hw->ipac); - break; - default: - return -EINVAL; - } - return 0; -} - -static void -release_card(struct inf_hw *card) { - ulong flags; - int i; - - spin_lock_irqsave(&card->lock, flags); - disable_hwirq(card); - spin_unlock_irqrestore(&card->lock, flags); - card->ipac.isac.release(&card->ipac.isac); - free_irq(card->irq, card); - mISDN_unregister_device(&card->ipac.isac.dch.dev); - release_io(card); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - switch (card->ci->typ) { - case INF_SCT_2: - case INF_SCT_3: - case INF_SCT_4: - break; - case INF_SCT_1: - for (i = 0; i < 3; i++) { - if (card->sc[i]) - release_card(card->sc[i]); - card->sc[i] = NULL; - } - fallthrough; - default: - pci_disable_device(card->pdev); - pci_set_drvdata(card->pdev, NULL); - break; - } - kfree(card); - inf_cnt--; -} - -static int -setup_instance(struct inf_hw *card) -{ - int err; - ulong flags; - - snprintf(card->name, MISDN_MAX_IDLEN - 1, "%s.%d", card->ci->name, - inf_cnt + 1); - write_lock_irqsave(&card_lock, flags); - list_add_tail(&card->list, &Cards); - write_unlock_irqrestore(&card_lock, flags); - - _set_debug(card); - card->ipac.isac.name = card->name; - card->ipac.name = card->name; - card->ipac.owner = THIS_MODULE; - spin_lock_init(&card->lock); - card->ipac.isac.hwlock = &card->lock; - card->ipac.hwlock = &card->lock; - card->ipac.ctrl = (void *)&inf_ctrl; - - err = setup_io(card); - if (err) - goto error_setup; - - card->ipac.isac.dch.dev.Bprotocols = - mISDNipac_init(&card->ipac, card); - - if (card->ipac.isac.dch.dev.Bprotocols == 0) - goto error_setup; - - err = mISDN_register_device(&card->ipac.isac.dch.dev, - &card->pdev->dev, card->name); - if (err) - goto error; - - err = init_irq(card); - if (!err) { - inf_cnt++; - pr_notice("Infineon %d cards installed\n", inf_cnt); - return 0; - } - mISDN_unregister_device(&card->ipac.isac.dch.dev); -error: - card->ipac.release(&card->ipac); -error_setup: - release_io(card); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - return err; -} - -static const struct inf_cinfo inf_card_info[] = { - { - INF_DIVA20, - "Dialogic Diva 2.0", - "diva20", - AM_IND_IO, AM_NONE, 2, 0, - &diva_irq - }, - { - INF_DIVA20U, - "Dialogic Diva 2.0U", - "diva20U", - AM_IND_IO, AM_NONE, 2, 0, - &diva_irq - }, - { - INF_DIVA201, - "Dialogic Diva 2.01", - "diva201", - AM_MEMIO, AM_MEMIO, 0, 1, - &diva20x_irq - }, - { - INF_DIVA202, - "Dialogic Diva 2.02", - "diva202", - AM_MEMIO, AM_MEMIO, 0, 1, - &diva20x_irq - }, - { - INF_SPEEDWIN, - "Sedlbauer SpeedWin PCI", - "speedwin", - AM_IND_IO, AM_NONE, 0, 0, - &tiger_irq - }, - { - INF_SAPHIR3, - "HST Saphir 3", - "saphir", - AM_IND_IO, AM_NONE, 0, 0, - &tiger_irq - }, - { - INF_QS1000, - "Develo Microlink PCI", - "qs1000", - AM_IO, AM_IND_IO, 1, 3, - &elsa_irq - }, - { - INF_QS3000, - "Develo QuickStep 3000", - "qs3000", - AM_IO, AM_IND_IO, 1, 3, - &elsa_irq - }, - { - INF_NICCY, - "Sagem NICCY", - "niccy", - AM_IO, AM_IND_IO, 0, 1, - &niccy_irq - }, - { - INF_SCT_1, - "SciTel Quadro", - "p1_scitel", - AM_IO, AM_IND_IO, 1, 5, - &ipac_irq - }, - { - INF_SCT_2, - "SciTel Quadro", - "p2_scitel", - AM_NONE, AM_IND_IO, 0, 4, - &ipac_irq - }, - { - INF_SCT_3, - "SciTel Quadro", - "p3_scitel", - AM_NONE, AM_IND_IO, 0, 3, - &ipac_irq - }, - { - INF_SCT_4, - "SciTel Quadro", - "p4_scitel", - AM_NONE, AM_IND_IO, 0, 2, - &ipac_irq - }, - { - INF_GAZEL_R685, - "Gazel R685", - "gazel685", - AM_IO, AM_IO, 1, 2, - &gazel_irq - }, - { - INF_GAZEL_R753, - "Gazel R753", - "gazel753", - AM_IO, AM_IND_IO, 1, 2, - &ipac_irq - }, - { - INF_NONE, - } -}; - -static const struct inf_cinfo * -get_card_info(enum inf_types typ) -{ - const struct inf_cinfo *ci = inf_card_info; - - while (ci->typ != INF_NONE) { - if (ci->typ == typ) - return ci; - ci++; - } - return NULL; -} - -static int -inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -ENOMEM; - struct inf_hw *card; - - card = kzalloc_obj(struct inf_hw); - if (!card) { - pr_info("No memory for Infineon ISDN card\n"); - return err; - } - card->pdev = pdev; - err = pci_enable_device(pdev); - if (err) { - kfree(card); - return err; - } - card->ci = get_card_info(ent->driver_data); - if (!card->ci) { - pr_info("mISDN: do not have information about adapter at %s\n", - pci_name(pdev)); - kfree(card); - pci_disable_device(pdev); - return -EINVAL; - } else - pr_notice("mISDN: found adapter %s at %s\n", - card->ci->full, pci_name(pdev)); - - card->irq = pdev->irq; - pci_set_drvdata(pdev, card); - err = setup_instance(card); - if (err) { - pci_disable_device(pdev); - kfree(card); - pci_set_drvdata(pdev, NULL); - } else if (ent->driver_data == INF_SCT_1) { - int i; - struct inf_hw *sc; - - for (i = 1; i < 4; i++) { - sc = kzalloc_obj(struct inf_hw); - if (!sc) { - release_card(card); - pci_disable_device(pdev); - return -ENOMEM; - } - sc->irq = card->irq; - sc->pdev = card->pdev; - sc->ci = card->ci + i; - err = setup_instance(sc); - if (err) { - pci_disable_device(pdev); - kfree(sc); - release_card(card); - break; - } else - card->sc[i - 1] = sc; - } - } - return err; -} - -static void -inf_remove(struct pci_dev *pdev) -{ - struct inf_hw *card = pci_get_drvdata(pdev); - - if (card) - release_card(card); - else - pr_debug("%s: drvdata already removed\n", __func__); -} - -static struct pci_driver infineon_driver = { - .name = "ISDN Infineon pci", - .probe = inf_probe, - .remove = inf_remove, - .id_table = infineon_ids, -}; - -static int __init -infineon_init(void) -{ - int err; - - pr_notice("Infineon ISDN Driver Rev. %s\n", INFINEON_REV); - err = pci_register_driver(&infineon_driver); - return err; -} - -static void __exit -infineon_cleanup(void) -{ - pci_unregister_driver(&infineon_driver); -} - -module_init(infineon_init); -module_exit(infineon_cleanup); diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c deleted file mode 100644 index a34ea6058960..000000000000 --- a/drivers/isdn/hardware/mISDN/mISDNipac.c +++ /dev/null @@ -1,1636 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * isac.c ISAC specific routines - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ - -#include -#include -#include -#include -#include "ipac.h" - - -#define DBUSY_TIMER_VALUE 80 -#define ARCOFI_USE 1 - -#define ISAC_REV "2.0" - -MODULE_AUTHOR("Karsten Keil"); -MODULE_VERSION(ISAC_REV); -MODULE_DESCRIPTION("mISDN driver for ISAC specific functions"); -MODULE_LICENSE("GPL v2"); - -#define ReadISAC(is, o) (is->read_reg(is->dch.hw, o + is->off)) -#define WriteISAC(is, o, v) (is->write_reg(is->dch.hw, o + is->off, v)) -#define ReadHSCX(h, o) (h->ip->read_reg(h->ip->hw, h->off + o)) -#define WriteHSCX(h, o, v) (h->ip->write_reg(h->ip->hw, h->off + o, v)) -#define ReadIPAC(ip, o) (ip->read_reg(ip->hw, o)) -#define WriteIPAC(ip, o, v) (ip->write_reg(ip->hw, o, v)) - -static inline void -ph_command(struct isac_hw *isac, u8 command) -{ - pr_debug("%s: ph_command %x\n", isac->name, command); - if (isac->type & IPAC_TYPE_ISACX) - WriteISAC(isac, ISACX_CIX0, (command << 4) | 0xE); - else - WriteISAC(isac, ISAC_CIX0, (command << 2) | 3); -} - -static void -isac_ph_state_change(struct isac_hw *isac) -{ - switch (isac->state) { - case (ISAC_IND_RS): - case (ISAC_IND_EI): - ph_command(isac, ISAC_CMD_DUI); - } - schedule_event(&isac->dch, FLG_PHCHANGE); -} - -static void -isac_ph_state_bh(struct dchannel *dch) -{ - struct isac_hw *isac = container_of(dch, struct isac_hw, dch); - - switch (isac->state) { - case ISAC_IND_RS: - case ISAC_IND_EI: - dch->state = 0; - l1_event(dch->l1, HW_RESET_IND); - break; - case ISAC_IND_DID: - dch->state = 3; - l1_event(dch->l1, HW_DEACT_CNF); - break; - case ISAC_IND_DR: - case ISAC_IND_DR6: - dch->state = 3; - l1_event(dch->l1, HW_DEACT_IND); - break; - case ISAC_IND_PU: - dch->state = 4; - l1_event(dch->l1, HW_POWERUP_IND); - break; - case ISAC_IND_RSY: - if (dch->state <= 5) { - dch->state = 5; - l1_event(dch->l1, ANYSIGNAL); - } else { - dch->state = 8; - l1_event(dch->l1, LOSTFRAMING); - } - break; - case ISAC_IND_ARD: - dch->state = 6; - l1_event(dch->l1, INFO2); - break; - case ISAC_IND_AI8: - dch->state = 7; - l1_event(dch->l1, INFO4_P8); - break; - case ISAC_IND_AI10: - dch->state = 7; - l1_event(dch->l1, INFO4_P10); - break; - } - pr_debug("%s: TE newstate %x\n", isac->name, dch->state); -} - -static void -isac_empty_fifo(struct isac_hw *isac, int count) -{ - u8 *ptr; - - pr_debug("%s: %s %d\n", isac->name, __func__, count); - - if (!isac->dch.rx_skb) { - isac->dch.rx_skb = mI_alloc_skb(isac->dch.maxlen, GFP_ATOMIC); - if (!isac->dch.rx_skb) { - pr_info("%s: D receive out of memory\n", isac->name); - WriteISAC(isac, ISAC_CMDR, 0x80); - return; - } - } - if ((isac->dch.rx_skb->len + count) >= isac->dch.maxlen) { - pr_debug("%s: %s overrun %d\n", isac->name, __func__, - isac->dch.rx_skb->len + count); - WriteISAC(isac, ISAC_CMDR, 0x80); - return; - } - ptr = skb_put(isac->dch.rx_skb, count); - isac->read_fifo(isac->dch.hw, isac->off, ptr, count); - WriteISAC(isac, ISAC_CMDR, 0x80); - if (isac->dch.debug & DEBUG_HW_DFIFO) { - char pfx[MISDN_MAX_IDLEN + 16]; - - snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-recv %s %d ", - isac->name, count); - print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count); - } -} - -static void -isac_fill_fifo(struct isac_hw *isac) -{ - int count, more; - u8 *ptr; - - if (!isac->dch.tx_skb) - return; - count = isac->dch.tx_skb->len - isac->dch.tx_idx; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - pr_debug("%s: %s %d\n", isac->name, __func__, count); - ptr = isac->dch.tx_skb->data + isac->dch.tx_idx; - isac->dch.tx_idx += count; - isac->write_fifo(isac->dch.hw, isac->off, ptr, count); - WriteISAC(isac, ISAC_CMDR, more ? 0x8 : 0xa); - if (test_and_set_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) { - pr_debug("%s: %s dbusytimer running\n", isac->name, __func__); - timer_delete(&isac->dch.timer); - } - isac->dch.timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); - add_timer(&isac->dch.timer); - if (isac->dch.debug & DEBUG_HW_DFIFO) { - char pfx[MISDN_MAX_IDLEN + 16]; - - snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-send %s %d ", - isac->name, count); - print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count); - } -} - -static void -isac_rme_irq(struct isac_hw *isac) -{ - u8 val, count; - - val = ReadISAC(isac, ISAC_RSTA); - if ((val & 0x70) != 0x20) { - if (val & 0x40) { - pr_debug("%s: ISAC RDO\n", isac->name); -#ifdef ERROR_STATISTIC - isac->dch.err_rx++; -#endif - } - if (!(val & 0x20)) { - pr_debug("%s: ISAC CRC error\n", isac->name); -#ifdef ERROR_STATISTIC - isac->dch.err_crc++; -#endif - } - WriteISAC(isac, ISAC_CMDR, 0x80); - dev_kfree_skb(isac->dch.rx_skb); - isac->dch.rx_skb = NULL; - } else { - count = ReadISAC(isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(isac, count); - recv_Dchannel(&isac->dch); - } -} - -static void -isac_xpr_irq(struct isac_hw *isac) -{ - if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) - timer_delete(&isac->dch.timer); - if (isac->dch.tx_skb && isac->dch.tx_idx < isac->dch.tx_skb->len) { - isac_fill_fifo(isac); - } else { - dev_kfree_skb(isac->dch.tx_skb); - if (get_next_dframe(&isac->dch)) - isac_fill_fifo(isac); - } -} - -static void -isac_retransmit(struct isac_hw *isac) -{ - if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) - timer_delete(&isac->dch.timer); - if (test_bit(FLG_TX_BUSY, &isac->dch.Flags)) { - /* Restart frame */ - isac->dch.tx_idx = 0; - isac_fill_fifo(isac); - } else if (isac->dch.tx_skb) { /* should not happen */ - pr_info("%s: tx_skb exist but not busy\n", isac->name); - test_and_set_bit(FLG_TX_BUSY, &isac->dch.Flags); - isac->dch.tx_idx = 0; - isac_fill_fifo(isac); - } else { - pr_info("%s: ISAC XDU no TX_BUSY\n", isac->name); - if (get_next_dframe(&isac->dch)) - isac_fill_fifo(isac); - } -} - -static void -isac_mos_irq(struct isac_hw *isac) -{ - u8 val; - int ret; - - val = ReadISAC(isac, ISAC_MOSR); - pr_debug("%s: ISAC MOSR %02x\n", isac->name, val); -#if ARCOFI_USE - if (val & 0x08) { - if (!isac->mon_rx) { - isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC); - if (!isac->mon_rx) { - pr_info("%s: ISAC MON RX out of memory!\n", - isac->name); - isac->mocr &= 0xf0; - isac->mocr |= 0x0a; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - goto afterMONR0; - } else - isac->mon_rxp = 0; - } - if (isac->mon_rxp >= MAX_MON_FRAME) { - isac->mocr &= 0xf0; - isac->mocr |= 0x0a; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - isac->mon_rxp = 0; - pr_debug("%s: ISAC MON RX overflow!\n", isac->name); - goto afterMONR0; - } - isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR0); - pr_debug("%s: ISAC MOR0 %02x\n", isac->name, - isac->mon_rx[isac->mon_rxp - 1]); - if (isac->mon_rxp == 1) { - isac->mocr |= 0x04; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - } - } -afterMONR0: - if (val & 0x80) { - if (!isac->mon_rx) { - isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC); - if (!isac->mon_rx) { - pr_info("%s: ISAC MON RX out of memory!\n", - isac->name); - isac->mocr &= 0x0f; - isac->mocr |= 0xa0; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - goto afterMONR1; - } else - isac->mon_rxp = 0; - } - if (isac->mon_rxp >= MAX_MON_FRAME) { - isac->mocr &= 0x0f; - isac->mocr |= 0xa0; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - isac->mon_rxp = 0; - pr_debug("%s: ISAC MON RX overflow!\n", isac->name); - goto afterMONR1; - } - isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR1); - pr_debug("%s: ISAC MOR1 %02x\n", isac->name, - isac->mon_rx[isac->mon_rxp - 1]); - isac->mocr |= 0x40; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - } -afterMONR1: - if (val & 0x04) { - isac->mocr &= 0xf0; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - isac->mocr |= 0x0a; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - if (isac->monitor) { - ret = isac->monitor(isac->dch.hw, MONITOR_RX_0, - isac->mon_rx, isac->mon_rxp); - if (ret) - kfree(isac->mon_rx); - } else { - pr_info("%s: MONITOR 0 received %d but no user\n", - isac->name, isac->mon_rxp); - kfree(isac->mon_rx); - } - isac->mon_rx = NULL; - isac->mon_rxp = 0; - } - if (val & 0x40) { - isac->mocr &= 0x0f; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - isac->mocr |= 0xa0; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - if (isac->monitor) { - ret = isac->monitor(isac->dch.hw, MONITOR_RX_1, - isac->mon_rx, isac->mon_rxp); - if (ret) - kfree(isac->mon_rx); - } else { - pr_info("%s: MONITOR 1 received %d but no user\n", - isac->name, isac->mon_rxp); - kfree(isac->mon_rx); - } - isac->mon_rx = NULL; - isac->mon_rxp = 0; - } - if (val & 0x02) { - if ((!isac->mon_tx) || (isac->mon_txc && - (isac->mon_txp >= isac->mon_txc) && !(val & 0x08))) { - isac->mocr &= 0xf0; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - isac->mocr |= 0x0a; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { - if (isac->monitor) - isac->monitor(isac->dch.hw, - MONITOR_TX_0, NULL, 0); - } - kfree(isac->mon_tx); - isac->mon_tx = NULL; - isac->mon_txc = 0; - isac->mon_txp = 0; - goto AfterMOX0; - } - if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { - if (isac->monitor) - isac->monitor(isac->dch.hw, - MONITOR_TX_0, NULL, 0); - kfree(isac->mon_tx); - isac->mon_tx = NULL; - isac->mon_txc = 0; - isac->mon_txp = 0; - goto AfterMOX0; - } - WriteISAC(isac, ISAC_MOX0, isac->mon_tx[isac->mon_txp++]); - pr_debug("%s: ISAC %02x -> MOX0\n", isac->name, - isac->mon_tx[isac->mon_txp - 1]); - } -AfterMOX0: - if (val & 0x20) { - if ((!isac->mon_tx) || (isac->mon_txc && - (isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) { - isac->mocr &= 0x0f; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - isac->mocr |= 0xa0; - WriteISAC(isac, ISAC_MOCR, isac->mocr); - if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { - if (isac->monitor) - isac->monitor(isac->dch.hw, - MONITOR_TX_1, NULL, 0); - } - kfree(isac->mon_tx); - isac->mon_tx = NULL; - isac->mon_txc = 0; - isac->mon_txp = 0; - goto AfterMOX1; - } - if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) { - if (isac->monitor) - isac->monitor(isac->dch.hw, - MONITOR_TX_1, NULL, 0); - kfree(isac->mon_tx); - isac->mon_tx = NULL; - isac->mon_txc = 0; - isac->mon_txp = 0; - goto AfterMOX1; - } - WriteISAC(isac, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]); - pr_debug("%s: ISAC %02x -> MOX1\n", isac->name, - isac->mon_tx[isac->mon_txp - 1]); - } -AfterMOX1: - val = 0; /* dummy to avoid warning */ -#endif -} - -static void -isac_cisq_irq(struct isac_hw *isac) { - u8 val; - - val = ReadISAC(isac, ISAC_CIR0); - pr_debug("%s: ISAC CIR0 %02X\n", isac->name, val); - if (val & 2) { - pr_debug("%s: ph_state change %x->%x\n", isac->name, - isac->state, (val >> 2) & 0xf); - isac->state = (val >> 2) & 0xf; - isac_ph_state_change(isac); - } - if (val & 1) { - val = ReadISAC(isac, ISAC_CIR1); - pr_debug("%s: ISAC CIR1 %02X\n", isac->name, val); - } -} - -static void -isacsx_cic_irq(struct isac_hw *isac) -{ - u8 val; - - val = ReadISAC(isac, ISACX_CIR0); - pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val); - if (val & ISACX_CIR0_CIC0) { - pr_debug("%s: ph_state change %x->%x\n", isac->name, - isac->state, val >> 4); - isac->state = val >> 4; - isac_ph_state_change(isac); - } -} - -static void -isacsx_rme_irq(struct isac_hw *isac) -{ - int count; - u8 val; - - val = ReadISAC(isac, ISACX_RSTAD); - if ((val & (ISACX_RSTAD_VFR | - ISACX_RSTAD_RDO | - ISACX_RSTAD_CRC | - ISACX_RSTAD_RAB)) - != (ISACX_RSTAD_VFR | ISACX_RSTAD_CRC)) { - pr_debug("%s: RSTAD %#x, dropped\n", isac->name, val); -#ifdef ERROR_STATISTIC - if (val & ISACX_RSTAD_CRC) - isac->dch.err_rx++; - else - isac->dch.err_crc++; -#endif - WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC); - dev_kfree_skb(isac->dch.rx_skb); - isac->dch.rx_skb = NULL; - } else { - count = ReadISAC(isac, ISACX_RBCLD) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(isac, count); - if (isac->dch.rx_skb) { - skb_trim(isac->dch.rx_skb, isac->dch.rx_skb->len - 1); - pr_debug("%s: dchannel received %d\n", isac->name, - isac->dch.rx_skb->len); - recv_Dchannel(&isac->dch); - } - } -} - -irqreturn_t -mISDNisac_irq(struct isac_hw *isac, u8 val) -{ - if (unlikely(!val)) - return IRQ_NONE; - pr_debug("%s: ISAC interrupt %02x\n", isac->name, val); - if (isac->type & IPAC_TYPE_ISACX) { - if (val & ISACX__CIC) - isacsx_cic_irq(isac); - if (val & ISACX__ICD) { - val = ReadISAC(isac, ISACX_ISTAD); - pr_debug("%s: ISTAD %02x\n", isac->name, val); - if (val & ISACX_D_XDU) { - pr_debug("%s: ISAC XDU\n", isac->name); -#ifdef ERROR_STATISTIC - isac->dch.err_tx++; -#endif - isac_retransmit(isac); - } - if (val & ISACX_D_XMR) { - pr_debug("%s: ISAC XMR\n", isac->name); -#ifdef ERROR_STATISTIC - isac->dch.err_tx++; -#endif - isac_retransmit(isac); - } - if (val & ISACX_D_XPR) - isac_xpr_irq(isac); - if (val & ISACX_D_RFO) { - pr_debug("%s: ISAC RFO\n", isac->name); - WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC); - } - if (val & ISACX_D_RME) - isacsx_rme_irq(isac); - if (val & ISACX_D_RPF) - isac_empty_fifo(isac, 0x20); - } - } else { - if (val & 0x80) /* RME */ - isac_rme_irq(isac); - if (val & 0x40) /* RPF */ - isac_empty_fifo(isac, 32); - if (val & 0x10) /* XPR */ - isac_xpr_irq(isac); - if (val & 0x04) /* CISQ */ - isac_cisq_irq(isac); - if (val & 0x20) /* RSC - never */ - pr_debug("%s: ISAC RSC interrupt\n", isac->name); - if (val & 0x02) /* SIN - never */ - pr_debug("%s: ISAC SIN interrupt\n", isac->name); - if (val & 0x01) { /* EXI */ - val = ReadISAC(isac, ISAC_EXIR); - pr_debug("%s: ISAC EXIR %02x\n", isac->name, val); - if (val & 0x80) /* XMR */ - pr_debug("%s: ISAC XMR\n", isac->name); - if (val & 0x40) { /* XDU */ - pr_debug("%s: ISAC XDU\n", isac->name); -#ifdef ERROR_STATISTIC - isac->dch.err_tx++; -#endif - isac_retransmit(isac); - } - if (val & 0x04) /* MOS */ - isac_mos_irq(isac); - } - } - return IRQ_HANDLED; -} -EXPORT_SYMBOL(mISDNisac_irq); - -static int -isac_l1hw(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct isac_hw *isac = container_of(dch, struct isac_hw, dch); - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - u32 id; - u_long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(isac->hwlock, flags); - ret = dchannel_senddata(dch, skb); - if (ret > 0) { /* direct TX */ - id = hh->id; /* skb can be freed */ - isac_fill_fifo(isac); - ret = 0; - spin_unlock_irqrestore(isac->hwlock, flags); - queue_ch_frame(ch, PH_DATA_CNF, id, NULL); - } else - spin_unlock_irqrestore(isac->hwlock, flags); - return ret; - case PH_ACTIVATE_REQ: - ret = l1_event(dch->l1, hh->prim); - break; - case PH_DEACTIVATE_REQ: - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - ret = l1_event(dch->l1, hh->prim); - break; - } - - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -isac_ctrl(struct isac_hw *isac, u32 cmd, unsigned long para) -{ - u8 tl = 0; - unsigned long flags; - int ret = 0; - - switch (cmd) { - case HW_TESTLOOP: - spin_lock_irqsave(isac->hwlock, flags); - if (!(isac->type & IPAC_TYPE_ISACX)) { - /* TODO: implement for IPAC_TYPE_ISACX */ - if (para & 1) /* B1 */ - tl |= 0x0c; - else if (para & 2) /* B2 */ - tl |= 0x3; - /* we only support IOM2 mode */ - WriteISAC(isac, ISAC_SPCR, tl); - if (tl) - WriteISAC(isac, ISAC_ADF1, 0x8); - else - WriteISAC(isac, ISAC_ADF1, 0x0); - } - spin_unlock_irqrestore(isac->hwlock, flags); - break; - case HW_TIMER3_VALUE: - ret = l1_event(isac->dch.l1, HW_TIMER3_VALUE | (para & 0xff)); - break; - default: - pr_debug("%s: %s unknown command %x %lx\n", isac->name, - __func__, cmd, para); - ret = -1; - } - return ret; -} - -static int -isac_l1cmd(struct dchannel *dch, u32 cmd) -{ - struct isac_hw *isac = container_of(dch, struct isac_hw, dch); - u_long flags; - - pr_debug("%s: cmd(%x) state(%02x)\n", isac->name, cmd, isac->state); - switch (cmd) { - case INFO3_P8: - spin_lock_irqsave(isac->hwlock, flags); - ph_command(isac, ISAC_CMD_AR8); - spin_unlock_irqrestore(isac->hwlock, flags); - break; - case INFO3_P10: - spin_lock_irqsave(isac->hwlock, flags); - ph_command(isac, ISAC_CMD_AR10); - spin_unlock_irqrestore(isac->hwlock, flags); - break; - case HW_RESET_REQ: - spin_lock_irqsave(isac->hwlock, flags); - if ((isac->state == ISAC_IND_EI) || - (isac->state == ISAC_IND_DR) || - (isac->state == ISAC_IND_DR6) || - (isac->state == ISAC_IND_RS)) - ph_command(isac, ISAC_CMD_TIM); - else - ph_command(isac, ISAC_CMD_RS); - spin_unlock_irqrestore(isac->hwlock, flags); - break; - case HW_DEACT_REQ: - skb_queue_purge(&dch->squeue); - if (dch->tx_skb) { - dev_kfree_skb(dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - dev_kfree_skb(dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); - break; - case HW_POWERUP_REQ: - spin_lock_irqsave(isac->hwlock, flags); - ph_command(isac, ISAC_CMD_TIM); - spin_unlock_irqrestore(isac->hwlock, flags); - break; - case PH_ACTIVATE_IND: - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - default: - pr_debug("%s: %s unknown command %x\n", isac->name, - __func__, cmd); - return -1; - } - return 0; -} - -static void -isac_release(struct isac_hw *isac) -{ - if (isac->type & IPAC_TYPE_ISACX) - WriteISAC(isac, ISACX_MASK, 0xff); - else if (isac->type != 0) - WriteISAC(isac, ISAC_MASK, 0xff); - if (isac->dch.timer.function != NULL) { - timer_delete(&isac->dch.timer); - isac->dch.timer.function = NULL; - } - kfree(isac->mon_rx); - isac->mon_rx = NULL; - kfree(isac->mon_tx); - isac->mon_tx = NULL; - if (isac->dch.l1) - l1_event(isac->dch.l1, CLOSE_CHANNEL); - mISDN_freedchannel(&isac->dch); -} - -static void -dbusy_timer_handler(struct timer_list *t) -{ - struct isac_hw *isac = timer_container_of(isac, t, dch.timer); - int rbch, star; - u_long flags; - - if (test_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) { - spin_lock_irqsave(isac->hwlock, flags); - rbch = ReadISAC(isac, ISAC_RBCH); - star = ReadISAC(isac, ISAC_STAR); - pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n", - isac->name, rbch, star); - if (rbch & ISAC_RBCH_XAC) /* D-Channel Busy */ - test_and_set_bit(FLG_L1_BUSY, &isac->dch.Flags); - else { - /* discard frame; reset transceiver */ - test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags); - if (isac->dch.tx_idx) - isac->dch.tx_idx = 0; - else - pr_info("%s: ISAC D-Channel Busy no tx_idx\n", - isac->name); - /* Transmitter reset */ - WriteISAC(isac, ISAC_CMDR, 0x01); - } - spin_unlock_irqrestore(isac->hwlock, flags); - } -} - -static int -open_dchannel_caller(struct isac_hw *isac, struct channel_req *rq, void *caller) -{ - pr_debug("%s: %s dev(%d) open from %p\n", isac->name, __func__, - isac->dch.dev.id, caller); - if (rq->protocol != ISDN_P_TE_S0) - return -EINVAL; - if (rq->adr.channel == 1) - /* E-Channel not supported */ - return -EINVAL; - rq->ch = &isac->dch.dev.D; - rq->ch->protocol = rq->protocol; - if (isac->dch.state == 7) - _queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - return 0; -} - -static int -open_dchannel(struct isac_hw *isac, struct channel_req *rq) -{ - return open_dchannel_caller(isac, rq, __builtin_return_address(0)); -} - -static const char *ISACVer[] = -{"2086/2186 V1.1", "2085 B1", "2085 B2", - "2085 V2.3"}; - -static int -isac_init(struct isac_hw *isac) -{ - u8 val; - int err = 0; - - if (!isac->dch.l1) { - err = create_l1(&isac->dch, isac_l1cmd); - if (err) - return err; - } - isac->mon_tx = NULL; - isac->mon_rx = NULL; - timer_setup(&isac->dch.timer, dbusy_timer_handler, 0); - isac->mocr = 0xaa; - if (isac->type & IPAC_TYPE_ISACX) { - /* Disable all IRQ */ - WriteISAC(isac, ISACX_MASK, 0xff); - val = ReadISAC(isac, ISACX_STARD); - pr_debug("%s: ISACX STARD %x\n", isac->name, val); - val = ReadISAC(isac, ISACX_ISTAD); - pr_debug("%s: ISACX ISTAD %x\n", isac->name, val); - val = ReadISAC(isac, ISACX_ISTA); - pr_debug("%s: ISACX ISTA %x\n", isac->name, val); - /* clear LDD */ - WriteISAC(isac, ISACX_TR_CONF0, 0x00); - /* enable transmitter */ - WriteISAC(isac, ISACX_TR_CONF2, 0x00); - /* transparent mode 0, RAC, stop/go */ - WriteISAC(isac, ISACX_MODED, 0xc9); - /* all HDLC IRQ unmasked */ - val = ReadISAC(isac, ISACX_ID); - if (isac->dch.debug & DEBUG_HW) - pr_notice("%s: ISACX Design ID %x\n", - isac->name, val & 0x3f); - val = ReadISAC(isac, ISACX_CIR0); - pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val); - isac->state = val >> 4; - isac_ph_state_change(isac); - ph_command(isac, ISAC_CMD_RS); - WriteISAC(isac, ISACX_MASK, IPACX__ON); - WriteISAC(isac, ISACX_MASKD, 0x00); - } else { /* old isac */ - WriteISAC(isac, ISAC_MASK, 0xff); - val = ReadISAC(isac, ISAC_STAR); - pr_debug("%s: ISAC STAR %x\n", isac->name, val); - val = ReadISAC(isac, ISAC_MODE); - pr_debug("%s: ISAC MODE %x\n", isac->name, val); - val = ReadISAC(isac, ISAC_ADF2); - pr_debug("%s: ISAC ADF2 %x\n", isac->name, val); - val = ReadISAC(isac, ISAC_ISTA); - pr_debug("%s: ISAC ISTA %x\n", isac->name, val); - if (val & 0x01) { - val = ReadISAC(isac, ISAC_EXIR); - pr_debug("%s: ISAC EXIR %x\n", isac->name, val); - } - val = ReadISAC(isac, ISAC_RBCH); - if (isac->dch.debug & DEBUG_HW) - pr_notice("%s: ISAC version (%x): %s\n", isac->name, - val, ISACVer[(val >> 5) & 3]); - isac->type |= ((val >> 5) & 3); - if (!isac->adf2) - isac->adf2 = 0x80; - if (!(isac->adf2 & 0x80)) { /* only IOM 2 Mode */ - pr_info("%s: only support IOM2 mode but adf2=%02x\n", - isac->name, isac->adf2); - isac_release(isac); - return -EINVAL; - } - WriteISAC(isac, ISAC_ADF2, isac->adf2); - WriteISAC(isac, ISAC_SQXR, 0x2f); - WriteISAC(isac, ISAC_SPCR, 0x00); - WriteISAC(isac, ISAC_STCR, 0x70); - WriteISAC(isac, ISAC_MODE, 0xc9); - WriteISAC(isac, ISAC_TIMR, 0x00); - WriteISAC(isac, ISAC_ADF1, 0x00); - val = ReadISAC(isac, ISAC_CIR0); - pr_debug("%s: ISAC CIR0 %x\n", isac->name, val); - isac->state = (val >> 2) & 0xf; - isac_ph_state_change(isac); - ph_command(isac, ISAC_CMD_RS); - WriteISAC(isac, ISAC_MASK, 0); - } - return err; -} - -int -mISDNisac_init(struct isac_hw *isac, void *hw) -{ - mISDN_initdchannel(&isac->dch, MAX_DFRAME_LEN_L1, isac_ph_state_bh); - isac->dch.hw = hw; - isac->dch.dev.D.send = isac_l1hw; - isac->init = isac_init; - isac->release = isac_release; - isac->ctrl = isac_ctrl; - isac->open = open_dchannel; - isac->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0); - isac->dch.dev.nrbchan = 2; - return 0; -} -EXPORT_SYMBOL(mISDNisac_init); - -static void -waitforCEC(struct hscx_hw *hx) -{ - u8 starb, to = 50; - - while (to) { - starb = ReadHSCX(hx, IPAC_STARB); - if (!(starb & 0x04)) - break; - udelay(1); - to--; - } - if (to < 50) - pr_debug("%s: B%1d CEC %d us\n", hx->ip->name, hx->bch.nr, - 50 - to); - if (!to) - pr_info("%s: B%1d CEC timeout\n", hx->ip->name, hx->bch.nr); -} - - -static void -waitforXFW(struct hscx_hw *hx) -{ - u8 starb, to = 50; - - while (to) { - starb = ReadHSCX(hx, IPAC_STARB); - if ((starb & 0x44) == 0x40) - break; - udelay(1); - to--; - } - if (to < 50) - pr_debug("%s: B%1d XFW %d us\n", hx->ip->name, hx->bch.nr, - 50 - to); - if (!to) - pr_info("%s: B%1d XFW timeout\n", hx->ip->name, hx->bch.nr); -} - -static void -hscx_cmdr(struct hscx_hw *hx, u8 cmd) -{ - if (hx->ip->type & IPAC_TYPE_IPACX) - WriteHSCX(hx, IPACX_CMDRB, cmd); - else { - waitforCEC(hx); - WriteHSCX(hx, IPAC_CMDRB, cmd); - } -} - -static void -hscx_empty_fifo(struct hscx_hw *hscx, u8 count) -{ - u8 *p; - int maxlen; - - pr_debug("%s: B%1d %d\n", hscx->ip->name, hscx->bch.nr, count); - if (test_bit(FLG_RX_OFF, &hscx->bch.Flags)) { - hscx->bch.dropcnt += count; - hscx_cmdr(hscx, 0x80); /* RMC */ - return; - } - maxlen = bchannel_get_rxbuf(&hscx->bch, count); - if (maxlen < 0) { - hscx_cmdr(hscx, 0x80); /* RMC */ - if (hscx->bch.rx_skb) - skb_trim(hscx->bch.rx_skb, 0); - pr_warn("%s.B%d: No bufferspace for %d bytes\n", - hscx->ip->name, hscx->bch.nr, count); - return; - } - p = skb_put(hscx->bch.rx_skb, count); - - if (hscx->ip->type & IPAC_TYPE_IPACX) - hscx->ip->read_fifo(hscx->ip->hw, - hscx->off + IPACX_RFIFOB, p, count); - else - hscx->ip->read_fifo(hscx->ip->hw, - hscx->off, p, count); - - hscx_cmdr(hscx, 0x80); /* RMC */ - - if (hscx->bch.debug & DEBUG_HW_BFIFO) { - snprintf(hscx->log, 64, "B%1d-recv %s %d ", - hscx->bch.nr, hscx->ip->name, count); - print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count); - } -} - -static void -hscx_fill_fifo(struct hscx_hw *hscx) -{ - int count, more; - u8 *p; - - if (!hscx->bch.tx_skb) { - if (!test_bit(FLG_TX_EMPTY, &hscx->bch.Flags)) - return; - count = hscx->fifo_size; - more = 1; - p = hscx->log; - memset(p, hscx->bch.fill[0], count); - } else { - count = hscx->bch.tx_skb->len - hscx->bch.tx_idx; - if (count <= 0) - return; - p = hscx->bch.tx_skb->data + hscx->bch.tx_idx; - - more = test_bit(FLG_TRANSPARENT, &hscx->bch.Flags) ? 1 : 0; - if (count > hscx->fifo_size) { - count = hscx->fifo_size; - more = 1; - } - pr_debug("%s: B%1d %d/%d/%d\n", hscx->ip->name, hscx->bch.nr, - count, hscx->bch.tx_idx, hscx->bch.tx_skb->len); - hscx->bch.tx_idx += count; - } - if (hscx->ip->type & IPAC_TYPE_IPACX) - hscx->ip->write_fifo(hscx->ip->hw, - hscx->off + IPACX_XFIFOB, p, count); - else { - waitforXFW(hscx); - hscx->ip->write_fifo(hscx->ip->hw, - hscx->off, p, count); - } - hscx_cmdr(hscx, more ? 0x08 : 0x0a); - - if (hscx->bch.tx_skb && (hscx->bch.debug & DEBUG_HW_BFIFO)) { - snprintf(hscx->log, 64, "B%1d-send %s %d ", - hscx->bch.nr, hscx->ip->name, count); - print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count); - } -} - -static void -hscx_xpr(struct hscx_hw *hx) -{ - if (hx->bch.tx_skb && hx->bch.tx_idx < hx->bch.tx_skb->len) { - hscx_fill_fifo(hx); - } else { - dev_kfree_skb(hx->bch.tx_skb); - if (get_next_bframe(&hx->bch)) { - hscx_fill_fifo(hx); - test_and_clear_bit(FLG_TX_EMPTY, &hx->bch.Flags); - } else if (test_bit(FLG_TX_EMPTY, &hx->bch.Flags)) { - hscx_fill_fifo(hx); - } - } -} - -static void -ipac_rme(struct hscx_hw *hx) -{ - int count; - u8 rstab; - - if (hx->ip->type & IPAC_TYPE_IPACX) - rstab = ReadHSCX(hx, IPACX_RSTAB); - else - rstab = ReadHSCX(hx, IPAC_RSTAB); - pr_debug("%s: B%1d RSTAB %02x\n", hx->ip->name, hx->bch.nr, rstab); - if ((rstab & 0xf0) != 0xa0) { - /* !(VFR && !RDO && CRC && !RAB) */ - if (!(rstab & 0x80)) { - if (hx->bch.debug & DEBUG_HW_BCHANNEL) - pr_notice("%s: B%1d invalid frame\n", - hx->ip->name, hx->bch.nr); - } - if (rstab & 0x40) { - if (hx->bch.debug & DEBUG_HW_BCHANNEL) - pr_notice("%s: B%1d RDO proto=%x\n", - hx->ip->name, hx->bch.nr, - hx->bch.state); - } - if (!(rstab & 0x20)) { - if (hx->bch.debug & DEBUG_HW_BCHANNEL) - pr_notice("%s: B%1d CRC error\n", - hx->ip->name, hx->bch.nr); - } - hscx_cmdr(hx, 0x80); /* Do RMC */ - return; - } - if (hx->ip->type & IPAC_TYPE_IPACX) - count = ReadHSCX(hx, IPACX_RBCLB); - else - count = ReadHSCX(hx, IPAC_RBCLB); - count &= (hx->fifo_size - 1); - if (count == 0) - count = hx->fifo_size; - hscx_empty_fifo(hx, count); - if (!hx->bch.rx_skb) - return; - if (hx->bch.rx_skb->len < 2) { - pr_debug("%s: B%1d frame too short %d\n", - hx->ip->name, hx->bch.nr, hx->bch.rx_skb->len); - skb_trim(hx->bch.rx_skb, 0); - } else { - skb_trim(hx->bch.rx_skb, hx->bch.rx_skb->len - 1); - recv_Bchannel(&hx->bch, 0, false); - } -} - -static void -ipac_irq(struct hscx_hw *hx, u8 ista) -{ - u8 istab, m, exirb = 0; - - if (hx->ip->type & IPAC_TYPE_IPACX) - istab = ReadHSCX(hx, IPACX_ISTAB); - else if (hx->ip->type & IPAC_TYPE_IPAC) { - istab = ReadHSCX(hx, IPAC_ISTAB); - m = (hx->bch.nr & 1) ? IPAC__EXA : IPAC__EXB; - if (m & ista) { - exirb = ReadHSCX(hx, IPAC_EXIRB); - pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name, - hx->bch.nr, exirb); - } - } else if (hx->bch.nr & 2) { /* HSCX B */ - if (ista & (HSCX__EXA | HSCX__ICA)) - ipac_irq(&hx->ip->hscx[0], ista); - if (ista & HSCX__EXB) { - exirb = ReadHSCX(hx, IPAC_EXIRB); - pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name, - hx->bch.nr, exirb); - } - istab = ista & 0xF8; - } else { /* HSCX A */ - istab = ReadHSCX(hx, IPAC_ISTAB); - if (ista & HSCX__EXA) { - exirb = ReadHSCX(hx, IPAC_EXIRB); - pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name, - hx->bch.nr, exirb); - } - istab = istab & 0xF8; - } - if (exirb & IPAC_B_XDU) - istab |= IPACX_B_XDU; - if (exirb & IPAC_B_RFO) - istab |= IPACX_B_RFO; - pr_debug("%s: B%1d ISTAB %02x\n", hx->ip->name, hx->bch.nr, istab); - - if (!test_bit(FLG_ACTIVE, &hx->bch.Flags)) - return; - - if (istab & IPACX_B_RME) - ipac_rme(hx); - - if (istab & IPACX_B_RPF) { - hscx_empty_fifo(hx, hx->fifo_size); - if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) - recv_Bchannel(&hx->bch, 0, false); - } - - if (istab & IPACX_B_RFO) { - pr_debug("%s: B%1d RFO error\n", hx->ip->name, hx->bch.nr); - hscx_cmdr(hx, 0x40); /* RRES */ - } - - if (istab & IPACX_B_XPR) - hscx_xpr(hx); - - if (istab & IPACX_B_XDU) { - if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) { - if (test_bit(FLG_FILLEMPTY, &hx->bch.Flags)) - test_and_set_bit(FLG_TX_EMPTY, &hx->bch.Flags); - hscx_xpr(hx); - return; - } - pr_debug("%s: B%1d XDU error at len %d\n", hx->ip->name, - hx->bch.nr, hx->bch.tx_idx); - hx->bch.tx_idx = 0; - hscx_cmdr(hx, 0x01); /* XRES */ - } -} - -irqreturn_t -mISDNipac_irq(struct ipac_hw *ipac, int maxloop) -{ - int cnt = maxloop + 1; - u8 ista, istad; - struct isac_hw *isac = &ipac->isac; - - if (ipac->type & IPAC_TYPE_IPACX) { - ista = ReadIPAC(ipac, ISACX_ISTA); - while (ista && --cnt) { - pr_debug("%s: ISTA %02x\n", ipac->name, ista); - if (ista & IPACX__ICA) - ipac_irq(&ipac->hscx[0], ista); - if (ista & IPACX__ICB) - ipac_irq(&ipac->hscx[1], ista); - if (ista & (ISACX__ICD | ISACX__CIC)) - mISDNisac_irq(&ipac->isac, ista); - ista = ReadIPAC(ipac, ISACX_ISTA); - } - } else if (ipac->type & IPAC_TYPE_IPAC) { - ista = ReadIPAC(ipac, IPAC_ISTA); - while (ista && --cnt) { - pr_debug("%s: ISTA %02x\n", ipac->name, ista); - if (ista & (IPAC__ICD | IPAC__EXD)) { - istad = ReadISAC(isac, ISAC_ISTA); - pr_debug("%s: ISTAD %02x\n", ipac->name, istad); - if (istad & IPAC_D_TIN2) - pr_debug("%s TIN2 irq\n", ipac->name); - if (ista & IPAC__EXD) - istad |= 1; /* ISAC EXI */ - mISDNisac_irq(isac, istad); - } - if (ista & (IPAC__ICA | IPAC__EXA)) - ipac_irq(&ipac->hscx[0], ista); - if (ista & (IPAC__ICB | IPAC__EXB)) - ipac_irq(&ipac->hscx[1], ista); - ista = ReadIPAC(ipac, IPAC_ISTA); - } - } else if (ipac->type & IPAC_TYPE_HSCX) { - while (--cnt) { - ista = ReadIPAC(ipac, IPAC_ISTAB + ipac->hscx[1].off); - pr_debug("%s: B2 ISTA %02x\n", ipac->name, ista); - if (ista) - ipac_irq(&ipac->hscx[1], ista); - istad = ReadISAC(isac, ISAC_ISTA); - pr_debug("%s: ISTAD %02x\n", ipac->name, istad); - if (istad) - mISDNisac_irq(isac, istad); - if (0 == (ista | istad)) - break; - } - } - if (cnt > maxloop) /* only for ISAC/HSCX without PCI IRQ test */ - return IRQ_NONE; - if (cnt < maxloop) - pr_debug("%s: %d irqloops cpu%d\n", ipac->name, - maxloop - cnt, smp_processor_id()); - if (maxloop && !cnt) - pr_notice("%s: %d IRQ LOOP cpu%d\n", ipac->name, - maxloop, smp_processor_id()); - return IRQ_HANDLED; -} -EXPORT_SYMBOL(mISDNipac_irq); - -static int -hscx_mode(struct hscx_hw *hscx, u32 bprotocol) -{ - pr_debug("%s: HSCX %c protocol %x-->%x ch %d\n", hscx->ip->name, - '@' + hscx->bch.nr, hscx->bch.state, bprotocol, hscx->bch.nr); - if (hscx->ip->type & IPAC_TYPE_IPACX) { - if (hscx->bch.nr & 1) { /* B1 and ICA */ - WriteIPAC(hscx->ip, ISACX_BCHA_TSDP_BC1, 0x80); - WriteIPAC(hscx->ip, ISACX_BCHA_CR, 0x88); - } else { /* B2 and ICB */ - WriteIPAC(hscx->ip, ISACX_BCHB_TSDP_BC1, 0x81); - WriteIPAC(hscx->ip, ISACX_BCHB_CR, 0x88); - } - switch (bprotocol) { - case ISDN_P_NONE: /* init */ - WriteHSCX(hscx, IPACX_MODEB, 0xC0); /* rec off */ - WriteHSCX(hscx, IPACX_EXMB, 0x30); /* std adj. */ - WriteHSCX(hscx, IPACX_MASKB, 0xFF); /* ints off */ - hscx_cmdr(hscx, 0x41); - test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags); - test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags); - break; - case ISDN_P_B_RAW: - WriteHSCX(hscx, IPACX_MODEB, 0x88); /* ex trans */ - WriteHSCX(hscx, IPACX_EXMB, 0x00); /* trans */ - hscx_cmdr(hscx, 0x41); - WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON); - test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags); - break; - case ISDN_P_B_HDLC: - WriteHSCX(hscx, IPACX_MODEB, 0xC0); /* trans */ - WriteHSCX(hscx, IPACX_EXMB, 0x00); /* hdlc,crc */ - hscx_cmdr(hscx, 0x41); - WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON); - test_and_set_bit(FLG_HDLC, &hscx->bch.Flags); - break; - default: - pr_info("%s: protocol not known %x\n", hscx->ip->name, - bprotocol); - return -ENOPROTOOPT; - } - } else if (hscx->ip->type & IPAC_TYPE_IPAC) { /* IPAC */ - WriteHSCX(hscx, IPAC_CCR1, 0x82); - WriteHSCX(hscx, IPAC_CCR2, 0x30); - WriteHSCX(hscx, IPAC_XCCR, 0x07); - WriteHSCX(hscx, IPAC_RCCR, 0x07); - WriteHSCX(hscx, IPAC_TSAX, hscx->slot); - WriteHSCX(hscx, IPAC_TSAR, hscx->slot); - switch (bprotocol) { - case ISDN_P_NONE: - WriteHSCX(hscx, IPAC_TSAX, 0x1F); - WriteHSCX(hscx, IPAC_TSAR, 0x1F); - WriteHSCX(hscx, IPAC_MODEB, 0x84); - WriteHSCX(hscx, IPAC_CCR1, 0x82); - WriteHSCX(hscx, IPAC_MASKB, 0xFF); /* ints off */ - test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags); - test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags); - break; - case ISDN_P_B_RAW: - WriteHSCX(hscx, IPAC_MODEB, 0xe4); /* ex trans */ - WriteHSCX(hscx, IPAC_CCR1, 0x82); - hscx_cmdr(hscx, 0x41); - WriteHSCX(hscx, IPAC_MASKB, 0); - test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags); - break; - case ISDN_P_B_HDLC: - WriteHSCX(hscx, IPAC_MODEB, 0x8c); - WriteHSCX(hscx, IPAC_CCR1, 0x8a); - hscx_cmdr(hscx, 0x41); - WriteHSCX(hscx, IPAC_MASKB, 0); - test_and_set_bit(FLG_HDLC, &hscx->bch.Flags); - break; - default: - pr_info("%s: protocol not known %x\n", hscx->ip->name, - bprotocol); - return -ENOPROTOOPT; - } - } else if (hscx->ip->type & IPAC_TYPE_HSCX) { /* HSCX */ - WriteHSCX(hscx, IPAC_CCR1, 0x85); - WriteHSCX(hscx, IPAC_CCR2, 0x30); - WriteHSCX(hscx, IPAC_XCCR, 0x07); - WriteHSCX(hscx, IPAC_RCCR, 0x07); - WriteHSCX(hscx, IPAC_TSAX, hscx->slot); - WriteHSCX(hscx, IPAC_TSAR, hscx->slot); - switch (bprotocol) { - case ISDN_P_NONE: - WriteHSCX(hscx, IPAC_TSAX, 0x1F); - WriteHSCX(hscx, IPAC_TSAR, 0x1F); - WriteHSCX(hscx, IPAC_MODEB, 0x84); - WriteHSCX(hscx, IPAC_CCR1, 0x85); - WriteHSCX(hscx, IPAC_MASKB, 0xFF); /* ints off */ - test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags); - test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags); - break; - case ISDN_P_B_RAW: - WriteHSCX(hscx, IPAC_MODEB, 0xe4); /* ex trans */ - WriteHSCX(hscx, IPAC_CCR1, 0x85); - hscx_cmdr(hscx, 0x41); - WriteHSCX(hscx, IPAC_MASKB, 0); - test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags); - break; - case ISDN_P_B_HDLC: - WriteHSCX(hscx, IPAC_MODEB, 0x8c); - WriteHSCX(hscx, IPAC_CCR1, 0x8d); - hscx_cmdr(hscx, 0x41); - WriteHSCX(hscx, IPAC_MASKB, 0); - test_and_set_bit(FLG_HDLC, &hscx->bch.Flags); - break; - default: - pr_info("%s: protocol not known %x\n", hscx->ip->name, - bprotocol); - return -ENOPROTOOPT; - } - } else - return -EINVAL; - hscx->bch.state = bprotocol; - return 0; -} - -static int -hscx_l2l1(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hscx_hw *hx = container_of(bch, struct hscx_hw, bch); - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(hx->ip->hwlock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - ret = 0; - hscx_fill_fifo(hx); - } - spin_unlock_irqrestore(hx->ip->hwlock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(hx->ip->hwlock, flags); - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) - ret = hscx_mode(hx, ch->protocol); - else - ret = 0; - spin_unlock_irqrestore(hx->ip->hwlock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - spin_lock_irqsave(hx->ip->hwlock, flags); - mISDN_clear_bchannel(bch); - hscx_mode(hx, ISDN_P_NONE); - spin_unlock_irqrestore(hx->ip->hwlock, flags); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - ret = 0; - break; - default: - pr_info("%s: %s unknown prim(%x,%x)\n", - hx->ip->name, __func__, hh->prim, hh->id); - ret = -EINVAL; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(bch, cq); -} - -static int -hscx_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct hscx_hw *hx = container_of(bch, struct hscx_hw, bch); - int ret = -EINVAL; - u_long flags; - - pr_debug("%s: %s cmd:%x %p\n", hx->ip->name, __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - cancel_work_sync(&bch->workq); - spin_lock_irqsave(hx->ip->hwlock, flags); - mISDN_clear_bchannel(bch); - hscx_mode(hx, ISDN_P_NONE); - spin_unlock_irqrestore(hx->ip->hwlock, flags); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(hx->ip->owner); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bch, arg); - break; - default: - pr_info("%s: %s unknown prim(%x)\n", - hx->ip->name, __func__, cmd); - } - return ret; -} - -static void -free_ipac(struct ipac_hw *ipac) -{ - isac_release(&ipac->isac); -} - -static const char *HSCXVer[] = -{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7", - "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"}; - - - -static void -hscx_init(struct hscx_hw *hx) -{ - u8 val; - - WriteHSCX(hx, IPAC_RAH2, 0xFF); - WriteHSCX(hx, IPAC_XBCH, 0x00); - WriteHSCX(hx, IPAC_RLCR, 0x00); - - if (hx->ip->type & IPAC_TYPE_HSCX) { - WriteHSCX(hx, IPAC_CCR1, 0x85); - val = ReadHSCX(hx, HSCX_VSTR); - pr_debug("%s: HSCX VSTR %02x\n", hx->ip->name, val); - if (hx->bch.debug & DEBUG_HW) - pr_notice("%s: HSCX version %s\n", hx->ip->name, - HSCXVer[val & 0x0f]); - } else - WriteHSCX(hx, IPAC_CCR1, 0x82); - WriteHSCX(hx, IPAC_CCR2, 0x30); - WriteHSCX(hx, IPAC_XCCR, 0x07); - WriteHSCX(hx, IPAC_RCCR, 0x07); -} - -static int -ipac_init(struct ipac_hw *ipac) -{ - u8 val; - - if (ipac->type & IPAC_TYPE_HSCX) { - hscx_init(&ipac->hscx[0]); - hscx_init(&ipac->hscx[1]); - val = ReadIPAC(ipac, IPAC_ID); - } else if (ipac->type & IPAC_TYPE_IPAC) { - hscx_init(&ipac->hscx[0]); - hscx_init(&ipac->hscx[1]); - WriteIPAC(ipac, IPAC_MASK, IPAC__ON); - val = ReadIPAC(ipac, IPAC_CONF); - /* conf is default 0, but can be overwritten by card setup */ - pr_debug("%s: IPAC CONF %02x/%02x\n", ipac->name, - val, ipac->conf); - WriteIPAC(ipac, IPAC_CONF, ipac->conf); - val = ReadIPAC(ipac, IPAC_ID); - if (ipac->hscx[0].bch.debug & DEBUG_HW) - pr_notice("%s: IPAC Design ID %02x\n", ipac->name, val); - } - /* nothing special for IPACX to do here */ - return isac_init(&ipac->isac); -} - -static int -open_bchannel(struct ipac_hw *ipac, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - bch = &ipac->hscx[rq->adr.channel - 1].bch; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - return 0; -} - -static int -channel_ctrl(struct ipac_hw *ipac, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_LOOP: - /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ - if (cq->channel < 0 || cq->channel > 3) { - ret = -EINVAL; - break; - } - ret = ipac->ctrl(ipac, HW_TESTLOOP, cq->channel); - break; - case MISDN_CTRL_L1_TIMER3: - ret = ipac->isac.ctrl(&ipac->isac, HW_TIMER3_VALUE, cq->p1); - break; - default: - pr_info("%s: unknown CTRL OP %x\n", ipac->name, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -ipac_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct isac_hw *isac = container_of(dch, struct isac_hw, dch); - struct ipac_hw *ipac = container_of(isac, struct ipac_hw, isac); - struct channel_req *rq; - int err = 0; - - pr_debug("%s: DCTRL: %x %p\n", ipac->name, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if (rq->protocol == ISDN_P_TE_S0) - err = open_dchannel_caller(isac, rq, __builtin_return_address(0)); - else - err = open_bchannel(ipac, rq); - if (err) - break; - if (!try_module_get(ipac->owner)) - pr_info("%s: cannot get module\n", ipac->name); - break; - case CLOSE_CHANNEL: - pr_debug("%s: dev(%d) close from %p\n", ipac->name, - dch->dev.id, __builtin_return_address(0)); - module_put(ipac->owner); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(ipac, arg); - break; - default: - pr_debug("%s: unknown DCTRL command %x\n", ipac->name, cmd); - return -EINVAL; - } - return err; -} - -u32 -mISDNipac_init(struct ipac_hw *ipac, void *hw) -{ - u32 ret; - u8 i; - - ipac->hw = hw; - if (ipac->isac.dch.debug & DEBUG_HW) - pr_notice("%s: ipac type %x\n", ipac->name, ipac->type); - if (ipac->type & IPAC_TYPE_HSCX) { - ipac->isac.type = IPAC_TYPE_ISAC; - ipac->hscx[0].off = 0; - ipac->hscx[1].off = 0x40; - ipac->hscx[0].fifo_size = 32; - ipac->hscx[1].fifo_size = 32; - } else if (ipac->type & IPAC_TYPE_IPAC) { - ipac->isac.type = IPAC_TYPE_IPAC | IPAC_TYPE_ISAC; - ipac->hscx[0].off = 0; - ipac->hscx[1].off = 0x40; - ipac->hscx[0].fifo_size = 64; - ipac->hscx[1].fifo_size = 64; - } else if (ipac->type & IPAC_TYPE_IPACX) { - ipac->isac.type = IPAC_TYPE_IPACX | IPAC_TYPE_ISACX; - ipac->hscx[0].off = IPACX_OFF_ICA; - ipac->hscx[1].off = IPACX_OFF_ICB; - ipac->hscx[0].fifo_size = 64; - ipac->hscx[1].fifo_size = 64; - } else - return 0; - - mISDNisac_init(&ipac->isac, hw); - - ipac->isac.dch.dev.D.ctrl = ipac_dctrl; - - for (i = 0; i < 2; i++) { - ipac->hscx[i].bch.nr = i + 1; - set_channelmap(i + 1, ipac->isac.dch.dev.channelmap); - list_add(&ipac->hscx[i].bch.ch.list, - &ipac->isac.dch.dev.bchannels); - mISDN_initbchannel(&ipac->hscx[i].bch, MAX_DATA_MEM, - ipac->hscx[i].fifo_size); - ipac->hscx[i].bch.ch.nr = i + 1; - ipac->hscx[i].bch.ch.send = &hscx_l2l1; - ipac->hscx[i].bch.ch.ctrl = hscx_bctrl; - ipac->hscx[i].bch.hw = hw; - ipac->hscx[i].ip = ipac; - /* default values for IOM time slots - * can be overwritten by card */ - ipac->hscx[i].slot = (i == 0) ? 0x2f : 0x03; - } - - ipac->init = ipac_init; - ipac->release = free_ipac; - - ret = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - return ret; -} -EXPORT_SYMBOL(mISDNipac_init); - -static int __init -isac_mod_init(void) -{ - pr_notice("mISDNipac module version %s\n", ISAC_REV); - return 0; -} - -static void __exit -isac_mod_cleanup(void) -{ - pr_notice("mISDNipac module unloaded\n"); -} -module_init(isac_mod_init); -module_exit(isac_mod_cleanup); diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c deleted file mode 100644 index dace91ba412b..000000000000 --- a/drivers/isdn/hardware/mISDN/mISDNisar.c +++ /dev/null @@ -1,1694 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * mISDNisar.c ISAR (Siemens PSB 7110) specific functions - * - * Author Karsten Keil (keil@isdn4linux.de) - * - * Copyright 2009 by Karsten Keil - */ - -/* define this to enable static debug messages, if you kernel supports - * dynamic debugging, you should use debugfs for this - */ -/* #define DEBUG */ - -#include -#include -#include -#include -#include -#include "isar.h" - -#define ISAR_REV "2.1" - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for ISAR (Siemens PSB 7110) specific functions"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(ISAR_REV); - -#define DEBUG_HW_FIRMWARE_FIFO 0x10000 - -static const u8 faxmodulation[] = {3, 24, 48, 72, 73, 74, 96, 97, 98, 121, - 122, 145, 146}; -#define FAXMODCNT 13 - -static void isar_setup(struct isar_hw *); - -static inline int -waitforHIA(struct isar_hw *isar, int timeout) -{ - int t = timeout; - u8 val = isar->read_reg(isar->hw, ISAR_HIA); - - while ((val & 1) && t) { - udelay(1); - t--; - val = isar->read_reg(isar->hw, ISAR_HIA); - } - pr_debug("%s: HIA after %dus\n", isar->name, timeout - t); - return timeout; -} - -/* - * send msg to ISAR mailbox - * if msg is NULL use isar->buf - */ -static int -send_mbox(struct isar_hw *isar, u8 his, u8 creg, u8 len, u8 *msg) -{ - if (!waitforHIA(isar, 1000)) - return 0; - pr_debug("send_mbox(%02x,%02x,%d)\n", his, creg, len); - isar->write_reg(isar->hw, ISAR_CTRL_H, creg); - isar->write_reg(isar->hw, ISAR_CTRL_L, len); - isar->write_reg(isar->hw, ISAR_WADR, 0); - if (!msg) - msg = isar->buf; - if (msg && len) { - isar->write_fifo(isar->hw, ISAR_MBOX, msg, len); - if (isar->ch[0].bch.debug & DEBUG_HW_BFIFO) { - int l = 0; - - while (l < (int)len) { - hex_dump_to_buffer(msg + l, len - l, 32, 1, - isar->log, 256, 1); - pr_debug("%s: %s %02x: %s\n", isar->name, - __func__, l, isar->log); - l += 32; - } - } - } - isar->write_reg(isar->hw, ISAR_HIS, his); - waitforHIA(isar, 1000); - return 1; -} - -/* - * receive message from ISAR mailbox - * if msg is NULL use isar->buf - */ -static void -rcv_mbox(struct isar_hw *isar, u8 *msg) -{ - if (!msg) - msg = isar->buf; - isar->write_reg(isar->hw, ISAR_RADR, 0); - if (msg && isar->clsb) { - isar->read_fifo(isar->hw, ISAR_MBOX, msg, isar->clsb); - if (isar->ch[0].bch.debug & DEBUG_HW_BFIFO) { - int l = 0; - - while (l < (int)isar->clsb) { - hex_dump_to_buffer(msg + l, isar->clsb - l, 32, - 1, isar->log, 256, 1); - pr_debug("%s: %s %02x: %s\n", isar->name, - __func__, l, isar->log); - l += 32; - } - } - } - isar->write_reg(isar->hw, ISAR_IIA, 0); -} - -static inline void -get_irq_infos(struct isar_hw *isar) -{ - isar->iis = isar->read_reg(isar->hw, ISAR_IIS); - isar->cmsb = isar->read_reg(isar->hw, ISAR_CTRL_H); - isar->clsb = isar->read_reg(isar->hw, ISAR_CTRL_L); - pr_debug("%s: rcv_mbox(%02x,%02x,%d)\n", isar->name, - isar->iis, isar->cmsb, isar->clsb); -} - -/* - * poll answer message from ISAR mailbox - * should be used only with ISAR IRQs disabled before DSP was started - * - */ -static int -poll_mbox(struct isar_hw *isar, int maxdelay) -{ - int t = maxdelay; - u8 irq; - - irq = isar->read_reg(isar->hw, ISAR_IRQBIT); - while (t && !(irq & ISAR_IRQSTA)) { - udelay(1); - t--; - } - if (t) { - get_irq_infos(isar); - rcv_mbox(isar, NULL); - } - pr_debug("%s: pulled %d bytes after %d us\n", - isar->name, isar->clsb, maxdelay - t); - return t; -} - -static int -ISARVersion(struct isar_hw *isar) -{ - int ver; - - /* disable ISAR IRQ */ - isar->write_reg(isar->hw, ISAR_IRQBIT, 0); - isar->buf[0] = ISAR_MSG_HWVER; - isar->buf[1] = 0; - isar->buf[2] = 1; - if (!send_mbox(isar, ISAR_HIS_VNR, 0, 3, NULL)) - return -1; - if (!poll_mbox(isar, 1000)) - return -2; - if (isar->iis == ISAR_IIS_VNR) { - if (isar->clsb == 1) { - ver = isar->buf[0] & 0xf; - return ver; - } - return -3; - } - return -4; -} - -static int -load_firmware(struct isar_hw *isar, const u8 *buf, int size) -{ - u32 saved_debug = isar->ch[0].bch.debug; - int ret, cnt; - u8 nom, noc; - u16 left, val, *sp = (u16 *)buf; - u8 *mp; - u_long flags; - - struct { - u16 sadr; - u16 len; - u16 d_key; - } blk_head; - - if (1 != isar->version) { - pr_err("%s: ISAR wrong version %d firmware download aborted\n", - isar->name, isar->version); - return -EINVAL; - } - if (!(saved_debug & DEBUG_HW_FIRMWARE_FIFO)) - isar->ch[0].bch.debug &= ~DEBUG_HW_BFIFO; - pr_debug("%s: load firmware %d words (%d bytes)\n", - isar->name, size / 2, size); - cnt = 0; - size /= 2; - /* disable ISAR IRQ */ - spin_lock_irqsave(isar->hwlock, flags); - isar->write_reg(isar->hw, ISAR_IRQBIT, 0); - spin_unlock_irqrestore(isar->hwlock, flags); - while (cnt < size) { - blk_head.sadr = le16_to_cpu(*sp++); - blk_head.len = le16_to_cpu(*sp++); - blk_head.d_key = le16_to_cpu(*sp++); - cnt += 3; - pr_debug("ISAR firmware block (%#x,%d,%#x)\n", - blk_head.sadr, blk_head.len, blk_head.d_key & 0xff); - left = blk_head.len; - if (cnt + left > size) { - pr_info("%s: firmware error have %d need %d words\n", - isar->name, size, cnt + left); - ret = -EINVAL; - goto reterrflg; - } - spin_lock_irqsave(isar->hwlock, flags); - if (!send_mbox(isar, ISAR_HIS_DKEY, blk_head.d_key & 0xff, - 0, NULL)) { - pr_info("ISAR send_mbox dkey failed\n"); - ret = -ETIME; - goto reterror; - } - if (!poll_mbox(isar, 1000)) { - pr_warn("ISAR poll_mbox dkey failed\n"); - ret = -ETIME; - goto reterror; - } - spin_unlock_irqrestore(isar->hwlock, flags); - if ((isar->iis != ISAR_IIS_DKEY) || isar->cmsb || isar->clsb) { - pr_info("ISAR wrong dkey response (%x,%x,%x)\n", - isar->iis, isar->cmsb, isar->clsb); - ret = 1; - goto reterrflg; - } - while (left > 0) { - if (left > 126) - noc = 126; - else - noc = left; - nom = (2 * noc) + 3; - mp = isar->buf; - /* the ISAR is big endian */ - *mp++ = blk_head.sadr >> 8; - *mp++ = blk_head.sadr & 0xFF; - left -= noc; - cnt += noc; - *mp++ = noc; - pr_debug("%s: load %3d words at %04x\n", isar->name, - noc, blk_head.sadr); - blk_head.sadr += noc; - while (noc) { - val = le16_to_cpu(*sp++); - *mp++ = val >> 8; - *mp++ = val & 0xFF; - noc--; - } - spin_lock_irqsave(isar->hwlock, flags); - if (!send_mbox(isar, ISAR_HIS_FIRM, 0, nom, NULL)) { - pr_info("ISAR send_mbox prog failed\n"); - ret = -ETIME; - goto reterror; - } - if (!poll_mbox(isar, 1000)) { - pr_info("ISAR poll_mbox prog failed\n"); - ret = -ETIME; - goto reterror; - } - spin_unlock_irqrestore(isar->hwlock, flags); - if ((isar->iis != ISAR_IIS_FIRM) || - isar->cmsb || isar->clsb) { - pr_info("ISAR wrong prog response (%x,%x,%x)\n", - isar->iis, isar->cmsb, isar->clsb); - ret = -EIO; - goto reterrflg; - } - } - pr_debug("%s: ISAR firmware block %d words loaded\n", - isar->name, blk_head.len); - } - isar->ch[0].bch.debug = saved_debug; - /* 10ms delay */ - cnt = 10; - while (cnt--) - mdelay(1); - isar->buf[0] = 0xff; - isar->buf[1] = 0xfe; - isar->bstat = 0; - spin_lock_irqsave(isar->hwlock, flags); - if (!send_mbox(isar, ISAR_HIS_STDSP, 0, 2, NULL)) { - pr_info("ISAR send_mbox start dsp failed\n"); - ret = -ETIME; - goto reterror; - } - if (!poll_mbox(isar, 1000)) { - pr_info("ISAR poll_mbox start dsp failed\n"); - ret = -ETIME; - goto reterror; - } - if ((isar->iis != ISAR_IIS_STDSP) || isar->cmsb || isar->clsb) { - pr_info("ISAR wrong start dsp response (%x,%x,%x)\n", - isar->iis, isar->cmsb, isar->clsb); - ret = -EIO; - goto reterror; - } else - pr_debug("%s: ISAR start dsp success\n", isar->name); - - /* NORMAL mode entered */ - /* Enable IRQs of ISAR */ - isar->write_reg(isar->hw, ISAR_IRQBIT, ISAR_IRQSTA); - spin_unlock_irqrestore(isar->hwlock, flags); - cnt = 1000; /* max 1s */ - while ((!isar->bstat) && cnt) { - mdelay(1); - cnt--; - } - if (!cnt) { - pr_info("ISAR no general status event received\n"); - ret = -ETIME; - goto reterrflg; - } else - pr_debug("%s: ISAR general status event %x\n", - isar->name, isar->bstat); - /* 10ms delay */ - cnt = 10; - while (cnt--) - mdelay(1); - isar->iis = 0; - spin_lock_irqsave(isar->hwlock, flags); - if (!send_mbox(isar, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) { - pr_info("ISAR send_mbox self tst failed\n"); - ret = -ETIME; - goto reterror; - } - spin_unlock_irqrestore(isar->hwlock, flags); - cnt = 10000; /* max 100 ms */ - while ((isar->iis != ISAR_IIS_DIAG) && cnt) { - udelay(10); - cnt--; - } - mdelay(1); - if (!cnt) { - pr_info("ISAR no self tst response\n"); - ret = -ETIME; - goto reterrflg; - } - if ((isar->cmsb == ISAR_CTRL_STST) && (isar->clsb == 1) - && (isar->buf[0] == 0)) - pr_debug("%s: ISAR selftest OK\n", isar->name); - else { - pr_info("ISAR selftest not OK %x/%x/%x\n", - isar->cmsb, isar->clsb, isar->buf[0]); - ret = -EIO; - goto reterrflg; - } - spin_lock_irqsave(isar->hwlock, flags); - isar->iis = 0; - if (!send_mbox(isar, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) { - pr_info("ISAR RQST SVN failed\n"); - ret = -ETIME; - goto reterror; - } - spin_unlock_irqrestore(isar->hwlock, flags); - cnt = 30000; /* max 300 ms */ - while ((isar->iis != ISAR_IIS_DIAG) && cnt) { - udelay(10); - cnt--; - } - mdelay(1); - if (!cnt) { - pr_info("ISAR no SVN response\n"); - ret = -ETIME; - goto reterrflg; - } else { - if ((isar->cmsb == ISAR_CTRL_SWVER) && (isar->clsb == 1)) { - pr_notice("%s: ISAR software version %#x\n", - isar->name, isar->buf[0]); - } else { - pr_info("%s: ISAR wrong swver response (%x,%x)" - " cnt(%d)\n", isar->name, isar->cmsb, - isar->clsb, cnt); - ret = -EIO; - goto reterrflg; - } - } - spin_lock_irqsave(isar->hwlock, flags); - isar_setup(isar); - spin_unlock_irqrestore(isar->hwlock, flags); - ret = 0; -reterrflg: - spin_lock_irqsave(isar->hwlock, flags); -reterror: - isar->ch[0].bch.debug = saved_debug; - if (ret) - /* disable ISAR IRQ */ - isar->write_reg(isar->hw, ISAR_IRQBIT, 0); - spin_unlock_irqrestore(isar->hwlock, flags); - return ret; -} - -static inline void -deliver_status(struct isar_ch *ch, int status) -{ - pr_debug("%s: HL->LL FAXIND %x\n", ch->is->name, status); - _queue_data(&ch->bch.ch, PH_CONTROL_IND, status, 0, NULL, GFP_ATOMIC); -} - -static inline void -isar_rcv_frame(struct isar_ch *ch) -{ - u8 *ptr; - int maxlen; - - if (!ch->is->clsb) { - pr_debug("%s; ISAR zero len frame\n", ch->is->name); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - return; - } - if (test_bit(FLG_RX_OFF, &ch->bch.Flags)) { - ch->bch.dropcnt += ch->is->clsb; - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - return; - } - switch (ch->bch.state) { - case ISDN_P_NONE: - pr_debug("%s: ISAR protocol 0 spurious IIS_RDATA %x/%x/%x\n", - ch->is->name, ch->is->iis, ch->is->cmsb, ch->is->clsb); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - case ISDN_P_B_RAW: - case ISDN_P_B_L2DTMF: - case ISDN_P_B_MODEM_ASYNC: - maxlen = bchannel_get_rxbuf(&ch->bch, ch->is->clsb); - if (maxlen < 0) { - pr_warn("%s.B%d: No bufferspace for %d bytes\n", - ch->is->name, ch->bch.nr, ch->is->clsb); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - } - rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb)); - recv_Bchannel(&ch->bch, 0, false); - break; - case ISDN_P_B_HDLC: - maxlen = bchannel_get_rxbuf(&ch->bch, ch->is->clsb); - if (maxlen < 0) { - pr_warn("%s.B%d: No bufferspace for %d bytes\n", - ch->is->name, ch->bch.nr, ch->is->clsb); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - } - if (ch->is->cmsb & HDLC_ERROR) { - pr_debug("%s: ISAR frame error %x len %d\n", - ch->is->name, ch->is->cmsb, ch->is->clsb); -#ifdef ERROR_STATISTIC - if (ch->is->cmsb & HDLC_ERR_RER) - ch->bch.err_inv++; - if (ch->is->cmsb & HDLC_ERR_CER) - ch->bch.err_crc++; -#endif - skb_trim(ch->bch.rx_skb, 0); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - } - if (ch->is->cmsb & HDLC_FSD) - skb_trim(ch->bch.rx_skb, 0); - ptr = skb_put(ch->bch.rx_skb, ch->is->clsb); - rcv_mbox(ch->is, ptr); - if (ch->is->cmsb & HDLC_FED) { - if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */ - pr_debug("%s: ISAR frame too short %d\n", - ch->is->name, ch->bch.rx_skb->len); - skb_trim(ch->bch.rx_skb, 0); - break; - } - skb_trim(ch->bch.rx_skb, ch->bch.rx_skb->len - 2); - recv_Bchannel(&ch->bch, 0, false); - } - break; - case ISDN_P_B_T30_FAX: - if (ch->state != STFAX_ACTIV) { - pr_debug("%s: isar_rcv_frame: not ACTIV\n", - ch->is->name); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - if (ch->bch.rx_skb) - skb_trim(ch->bch.rx_skb, 0); - break; - } - if (!ch->bch.rx_skb) { - ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen, - GFP_ATOMIC); - if (unlikely(!ch->bch.rx_skb)) { - pr_info("%s: B receive out of memory\n", - __func__); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - } - } - if (ch->cmd == PCTRL_CMD_FRM) { - rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb)); - pr_debug("%s: isar_rcv_frame: %d\n", - ch->is->name, ch->bch.rx_skb->len); - if (ch->is->cmsb & SART_NMD) { /* ABORT */ - pr_debug("%s: isar_rcv_frame: no more data\n", - ch->is->name); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - send_mbox(ch->is, SET_DPS(ch->dpath) | - ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, - 0, NULL); - ch->state = STFAX_ESCAPE; - /* set_skb_flag(skb, DF_NOMOREDATA); */ - } - recv_Bchannel(&ch->bch, 0, false); - if (ch->is->cmsb & SART_NMD) - deliver_status(ch, HW_MOD_NOCARR); - break; - } - if (ch->cmd != PCTRL_CMD_FRH) { - pr_debug("%s: isar_rcv_frame: unknown fax mode %x\n", - ch->is->name, ch->cmd); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - if (ch->bch.rx_skb) - skb_trim(ch->bch.rx_skb, 0); - break; - } - /* PCTRL_CMD_FRH */ - if ((ch->bch.rx_skb->len + ch->is->clsb) > - (ch->bch.maxlen + 2)) { - pr_info("%s: %s incoming packet too large\n", - ch->is->name, __func__); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - skb_trim(ch->bch.rx_skb, 0); - break; - } else if (ch->is->cmsb & HDLC_ERROR) { - pr_info("%s: ISAR frame error %x len %d\n", - ch->is->name, ch->is->cmsb, ch->is->clsb); - skb_trim(ch->bch.rx_skb, 0); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - } - if (ch->is->cmsb & HDLC_FSD) - skb_trim(ch->bch.rx_skb, 0); - ptr = skb_put(ch->bch.rx_skb, ch->is->clsb); - rcv_mbox(ch->is, ptr); - if (ch->is->cmsb & HDLC_FED) { - if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */ - pr_info("%s: ISAR frame too short %d\n", - ch->is->name, ch->bch.rx_skb->len); - skb_trim(ch->bch.rx_skb, 0); - break; - } - skb_trim(ch->bch.rx_skb, ch->bch.rx_skb->len - 2); - recv_Bchannel(&ch->bch, 0, false); - } - if (ch->is->cmsb & SART_NMD) { /* ABORT */ - pr_debug("%s: isar_rcv_frame: no more data\n", - ch->is->name); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - if (ch->bch.rx_skb) - skb_trim(ch->bch.rx_skb, 0); - send_mbox(ch->is, SET_DPS(ch->dpath) | - ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL); - ch->state = STFAX_ESCAPE; - deliver_status(ch, HW_MOD_NOCARR); - } - break; - default: - pr_info("isar_rcv_frame protocol (%x)error\n", ch->bch.state); - ch->is->write_reg(ch->is->hw, ISAR_IIA, 0); - break; - } -} - -static void -isar_fill_fifo(struct isar_ch *ch) -{ - int count; - u8 msb; - u8 *ptr; - - pr_debug("%s: ch%d tx_skb %d tx_idx %d\n", ch->is->name, ch->bch.nr, - ch->bch.tx_skb ? ch->bch.tx_skb->len : -1, ch->bch.tx_idx); - if (!(ch->is->bstat & - (ch->dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2))) - return; - if (!ch->bch.tx_skb) { - if (!test_bit(FLG_TX_EMPTY, &ch->bch.Flags) || - (ch->bch.state != ISDN_P_B_RAW)) - return; - count = ch->mml; - /* use the card buffer */ - memset(ch->is->buf, ch->bch.fill[0], count); - send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, - 0, count, ch->is->buf); - return; - } - count = ch->bch.tx_skb->len - ch->bch.tx_idx; - if (count <= 0) - return; - if (count > ch->mml) { - msb = 0; - count = ch->mml; - } else { - msb = HDLC_FED; - } - ptr = ch->bch.tx_skb->data + ch->bch.tx_idx; - if (!ch->bch.tx_idx) { - pr_debug("%s: frame start\n", ch->is->name); - if ((ch->bch.state == ISDN_P_B_T30_FAX) && - (ch->cmd == PCTRL_CMD_FTH)) { - if (count > 1) { - if ((ptr[0] == 0xff) && (ptr[1] == 0x13)) { - /* last frame */ - test_and_set_bit(FLG_LASTDATA, - &ch->bch.Flags); - pr_debug("%s: set LASTDATA\n", - ch->is->name); - if (msb == HDLC_FED) - test_and_set_bit(FLG_DLEETX, - &ch->bch.Flags); - } - } - } - msb |= HDLC_FST; - } - ch->bch.tx_idx += count; - switch (ch->bch.state) { - case ISDN_P_NONE: - pr_info("%s: wrong protocol 0\n", __func__); - break; - case ISDN_P_B_RAW: - case ISDN_P_B_L2DTMF: - case ISDN_P_B_MODEM_ASYNC: - send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, - 0, count, ptr); - break; - case ISDN_P_B_HDLC: - send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, - msb, count, ptr); - break; - case ISDN_P_B_T30_FAX: - if (ch->state != STFAX_ACTIV) - pr_debug("%s: not ACTIV\n", ch->is->name); - else if (ch->cmd == PCTRL_CMD_FTH) - send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, - msb, count, ptr); - else if (ch->cmd == PCTRL_CMD_FTM) - send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, - 0, count, ptr); - else - pr_debug("%s: not FTH/FTM\n", ch->is->name); - break; - default: - pr_info("%s: protocol(%x) error\n", - __func__, ch->bch.state); - break; - } -} - -static inline struct isar_ch * -sel_bch_isar(struct isar_hw *isar, u8 dpath) -{ - struct isar_ch *base = &isar->ch[0]; - - if ((!dpath) || (dpath > 2)) - return NULL; - if (base->dpath == dpath) - return base; - base++; - if (base->dpath == dpath) - return base; - return NULL; -} - -static void -send_next(struct isar_ch *ch) -{ - pr_debug("%s: %s ch%d tx_skb %d tx_idx %d\n", ch->is->name, __func__, - ch->bch.nr, ch->bch.tx_skb ? ch->bch.tx_skb->len : -1, - ch->bch.tx_idx); - if (ch->bch.state == ISDN_P_B_T30_FAX) { - if (ch->cmd == PCTRL_CMD_FTH) { - if (test_bit(FLG_LASTDATA, &ch->bch.Flags)) { - pr_debug("set NMD_DATA\n"); - test_and_set_bit(FLG_NMD_DATA, &ch->bch.Flags); - } - } else if (ch->cmd == PCTRL_CMD_FTM) { - if (test_bit(FLG_DLEETX, &ch->bch.Flags)) { - test_and_set_bit(FLG_LASTDATA, &ch->bch.Flags); - test_and_set_bit(FLG_NMD_DATA, &ch->bch.Flags); - } - } - } - dev_kfree_skb(ch->bch.tx_skb); - if (get_next_bframe(&ch->bch)) { - isar_fill_fifo(ch); - test_and_clear_bit(FLG_TX_EMPTY, &ch->bch.Flags); - } else if (test_bit(FLG_TX_EMPTY, &ch->bch.Flags)) { - isar_fill_fifo(ch); - } else { - if (test_and_clear_bit(FLG_DLEETX, &ch->bch.Flags)) { - if (test_and_clear_bit(FLG_LASTDATA, - &ch->bch.Flags)) { - if (test_and_clear_bit(FLG_NMD_DATA, - &ch->bch.Flags)) { - u8 zd = 0; - send_mbox(ch->is, SET_DPS(ch->dpath) | - ISAR_HIS_SDATA, 0x01, 1, &zd); - } - test_and_set_bit(FLG_LL_OK, &ch->bch.Flags); - } else { - deliver_status(ch, HW_MOD_CONNECT); - } - } else if (test_bit(FLG_FILLEMPTY, &ch->bch.Flags)) { - test_and_set_bit(FLG_TX_EMPTY, &ch->bch.Flags); - } - } -} - -static void -check_send(struct isar_hw *isar, u8 rdm) -{ - struct isar_ch *ch; - - pr_debug("%s: rdm %x\n", isar->name, rdm); - if (rdm & BSTAT_RDM1) { - ch = sel_bch_isar(isar, 1); - if (ch && test_bit(FLG_ACTIVE, &ch->bch.Flags)) { - if (ch->bch.tx_skb && (ch->bch.tx_skb->len > - ch->bch.tx_idx)) - isar_fill_fifo(ch); - else - send_next(ch); - } - } - if (rdm & BSTAT_RDM2) { - ch = sel_bch_isar(isar, 2); - if (ch && test_bit(FLG_ACTIVE, &ch->bch.Flags)) { - if (ch->bch.tx_skb && (ch->bch.tx_skb->len > - ch->bch.tx_idx)) - isar_fill_fifo(ch); - else - send_next(ch); - } - } -} - -static const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4", - "300", "600", "1200", "2400", "4800", "7200", - "9600nt", "9600t", "12000", "14400", "WRONG"}; -static const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21", - "Bell103", "V23", "Bell202", "V17", "V29", "V27ter"}; - -static void -isar_pump_status_rsp(struct isar_ch *ch) { - u8 ril = ch->is->buf[0]; - u8 rim; - - if (!test_and_clear_bit(ISAR_RATE_REQ, &ch->is->Flags)) - return; - if (ril > 14) { - pr_info("%s: wrong pstrsp ril=%d\n", ch->is->name, ril); - ril = 15; - } - switch (ch->is->buf[1]) { - case 0: - rim = 0; - break; - case 0x20: - rim = 2; - break; - case 0x40: - rim = 3; - break; - case 0x41: - rim = 4; - break; - case 0x51: - rim = 5; - break; - case 0x61: - rim = 6; - break; - case 0x71: - rim = 7; - break; - case 0x82: - rim = 8; - break; - case 0x92: - rim = 9; - break; - case 0xa2: - rim = 10; - break; - default: - rim = 1; - break; - } - sprintf(ch->conmsg, "%s %s", dmril[ril], dmrim[rim]); - pr_debug("%s: pump strsp %s\n", ch->is->name, ch->conmsg); -} - -static void -isar_pump_statev_modem(struct isar_ch *ch, u8 devt) { - u8 dps = SET_DPS(ch->dpath); - - switch (devt) { - case PSEV_10MS_TIMER: - pr_debug("%s: pump stev TIMER\n", ch->is->name); - break; - case PSEV_CON_ON: - pr_debug("%s: pump stev CONNECT\n", ch->is->name); - deliver_status(ch, HW_MOD_CONNECT); - break; - case PSEV_CON_OFF: - pr_debug("%s: pump stev NO CONNECT\n", ch->is->name); - send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); - deliver_status(ch, HW_MOD_NOCARR); - break; - case PSEV_V24_OFF: - pr_debug("%s: pump stev V24 OFF\n", ch->is->name); - break; - case PSEV_CTS_ON: - pr_debug("%s: pump stev CTS ON\n", ch->is->name); - break; - case PSEV_CTS_OFF: - pr_debug("%s pump stev CTS OFF\n", ch->is->name); - break; - case PSEV_DCD_ON: - pr_debug("%s: pump stev CARRIER ON\n", ch->is->name); - test_and_set_bit(ISAR_RATE_REQ, &ch->is->Flags); - send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); - break; - case PSEV_DCD_OFF: - pr_debug("%s: pump stev CARRIER OFF\n", ch->is->name); - break; - case PSEV_DSR_ON: - pr_debug("%s: pump stev DSR ON\n", ch->is->name); - break; - case PSEV_DSR_OFF: - pr_debug("%s: pump stev DSR_OFF\n", ch->is->name); - break; - case PSEV_REM_RET: - pr_debug("%s: pump stev REMOTE RETRAIN\n", ch->is->name); - break; - case PSEV_REM_REN: - pr_debug("%s: pump stev REMOTE RENEGOTIATE\n", ch->is->name); - break; - case PSEV_GSTN_CLR: - pr_debug("%s: pump stev GSTN CLEAR\n", ch->is->name); - break; - default: - pr_info("u%s: unknown pump stev %x\n", ch->is->name, devt); - break; - } -} - -static void -isar_pump_statev_fax(struct isar_ch *ch, u8 devt) { - u8 dps = SET_DPS(ch->dpath); - u8 p1; - - switch (devt) { - case PSEV_10MS_TIMER: - pr_debug("%s: pump stev TIMER\n", ch->is->name); - break; - case PSEV_RSP_READY: - pr_debug("%s: pump stev RSP_READY\n", ch->is->name); - ch->state = STFAX_READY; - deliver_status(ch, HW_MOD_READY); -#ifdef AUTOCON - if (test_bit(BC_FLG_ORIG, &ch->bch.Flags)) - isar_pump_cmd(bch, HW_MOD_FRH, 3); - else - isar_pump_cmd(bch, HW_MOD_FTH, 3); -#endif - break; - case PSEV_LINE_TX_H: - if (ch->state == STFAX_LINE) { - pr_debug("%s: pump stev LINE_TX_H\n", ch->is->name); - ch->state = STFAX_CONT; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - PCTRL_CMD_CONT, 0, NULL); - } else { - pr_debug("%s: pump stev LINE_TX_H wrong st %x\n", - ch->is->name, ch->state); - } - break; - case PSEV_LINE_RX_H: - if (ch->state == STFAX_LINE) { - pr_debug("%s: pump stev LINE_RX_H\n", ch->is->name); - ch->state = STFAX_CONT; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - PCTRL_CMD_CONT, 0, NULL); - } else { - pr_debug("%s: pump stev LINE_RX_H wrong st %x\n", - ch->is->name, ch->state); - } - break; - case PSEV_LINE_TX_B: - if (ch->state == STFAX_LINE) { - pr_debug("%s: pump stev LINE_TX_B\n", ch->is->name); - ch->state = STFAX_CONT; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - PCTRL_CMD_CONT, 0, NULL); - } else { - pr_debug("%s: pump stev LINE_TX_B wrong st %x\n", - ch->is->name, ch->state); - } - break; - case PSEV_LINE_RX_B: - if (ch->state == STFAX_LINE) { - pr_debug("%s: pump stev LINE_RX_B\n", ch->is->name); - ch->state = STFAX_CONT; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - PCTRL_CMD_CONT, 0, NULL); - } else { - pr_debug("%s: pump stev LINE_RX_B wrong st %x\n", - ch->is->name, ch->state); - } - break; - case PSEV_RSP_CONN: - if (ch->state == STFAX_CONT) { - pr_debug("%s: pump stev RSP_CONN\n", ch->is->name); - ch->state = STFAX_ACTIV; - test_and_set_bit(ISAR_RATE_REQ, &ch->is->Flags); - send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); - if (ch->cmd == PCTRL_CMD_FTH) { - int delay = (ch->mod == 3) ? 1000 : 200; - /* 1s (200 ms) Flags before data */ - if (test_and_set_bit(FLG_FTI_RUN, - &ch->bch.Flags)) - timer_delete(&ch->ftimer); - ch->ftimer.expires = - jiffies + ((delay * HZ) / 1000); - test_and_set_bit(FLG_LL_CONN, - &ch->bch.Flags); - add_timer(&ch->ftimer); - } else { - deliver_status(ch, HW_MOD_CONNECT); - } - } else { - pr_debug("%s: pump stev RSP_CONN wrong st %x\n", - ch->is->name, ch->state); - } - break; - case PSEV_FLAGS_DET: - pr_debug("%s: pump stev FLAGS_DET\n", ch->is->name); - break; - case PSEV_RSP_DISC: - pr_debug("%s: pump stev RSP_DISC state(%d)\n", - ch->is->name, ch->state); - if (ch->state == STFAX_ESCAPE) { - p1 = 5; - switch (ch->newcmd) { - case 0: - ch->state = STFAX_READY; - break; - case PCTRL_CMD_FTM: - p1 = 2; - fallthrough; - case PCTRL_CMD_FTH: - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - PCTRL_CMD_SILON, 1, &p1); - ch->state = STFAX_SILDET; - break; - case PCTRL_CMD_FRH: - case PCTRL_CMD_FRM: - ch->mod = ch->newmod; - p1 = ch->newmod; - ch->newmod = 0; - ch->cmd = ch->newcmd; - ch->newcmd = 0; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - ch->cmd, 1, &p1); - ch->state = STFAX_LINE; - ch->try_mod = 3; - break; - default: - pr_debug("%s: RSP_DISC unknown newcmd %x\n", - ch->is->name, ch->newcmd); - break; - } - } else if (ch->state == STFAX_ACTIV) { - if (test_and_clear_bit(FLG_LL_OK, &ch->bch.Flags)) - deliver_status(ch, HW_MOD_OK); - else if (ch->cmd == PCTRL_CMD_FRM) - deliver_status(ch, HW_MOD_NOCARR); - else - deliver_status(ch, HW_MOD_FCERROR); - ch->state = STFAX_READY; - } else if (ch->state != STFAX_SILDET) { - /* ignore in STFAX_SILDET */ - ch->state = STFAX_READY; - deliver_status(ch, HW_MOD_FCERROR); - } - break; - case PSEV_RSP_SILDET: - pr_debug("%s: pump stev RSP_SILDET\n", ch->is->name); - if (ch->state == STFAX_SILDET) { - ch->mod = ch->newmod; - p1 = ch->newmod; - ch->newmod = 0; - ch->cmd = ch->newcmd; - ch->newcmd = 0; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - ch->cmd, 1, &p1); - ch->state = STFAX_LINE; - ch->try_mod = 3; - } - break; - case PSEV_RSP_SILOFF: - pr_debug("%s: pump stev RSP_SILOFF\n", ch->is->name); - break; - case PSEV_RSP_FCERR: - if (ch->state == STFAX_LINE) { - pr_debug("%s: pump stev RSP_FCERR try %d\n", - ch->is->name, ch->try_mod); - if (ch->try_mod--) { - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, - ch->cmd, 1, &ch->mod); - break; - } - } - pr_debug("%s: pump stev RSP_FCERR\n", ch->is->name); - ch->state = STFAX_ESCAPE; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, - 0, NULL); - deliver_status(ch, HW_MOD_FCERROR); - break; - default: - break; - } -} - -void -mISDNisar_irq(struct isar_hw *isar) -{ - struct isar_ch *ch; - - get_irq_infos(isar); - switch (isar->iis & ISAR_IIS_MSCMSD) { - case ISAR_IIS_RDATA: - ch = sel_bch_isar(isar, isar->iis >> 6); - if (ch) - isar_rcv_frame(ch); - else { - pr_debug("%s: ISAR spurious IIS_RDATA %x/%x/%x\n", - isar->name, isar->iis, isar->cmsb, - isar->clsb); - isar->write_reg(isar->hw, ISAR_IIA, 0); - } - break; - case ISAR_IIS_GSTEV: - isar->write_reg(isar->hw, ISAR_IIA, 0); - isar->bstat |= isar->cmsb; - check_send(isar, isar->cmsb); - break; - case ISAR_IIS_BSTEV: -#ifdef ERROR_STATISTIC - ch = sel_bch_isar(isar, isar->iis >> 6); - if (ch) { - if (isar->cmsb == BSTEV_TBO) - ch->bch.err_tx++; - if (isar->cmsb == BSTEV_RBO) - ch->bch.err_rdo++; - } -#endif - pr_debug("%s: Buffer STEV dpath%d msb(%x)\n", - isar->name, isar->iis >> 6, isar->cmsb); - isar->write_reg(isar->hw, ISAR_IIA, 0); - break; - case ISAR_IIS_PSTEV: - ch = sel_bch_isar(isar, isar->iis >> 6); - if (ch) { - rcv_mbox(isar, NULL); - if (ch->bch.state == ISDN_P_B_MODEM_ASYNC) - isar_pump_statev_modem(ch, isar->cmsb); - else if (ch->bch.state == ISDN_P_B_T30_FAX) - isar_pump_statev_fax(ch, isar->cmsb); - else if (ch->bch.state == ISDN_P_B_RAW) { - int tt; - tt = isar->cmsb | 0x30; - if (tt == 0x3e) - tt = '*'; - else if (tt == 0x3f) - tt = '#'; - else if (tt > '9') - tt += 7; - tt |= DTMF_TONE_VAL; - _queue_data(&ch->bch.ch, PH_CONTROL_IND, - MISDN_ID_ANY, sizeof(tt), &tt, - GFP_ATOMIC); - } else - pr_debug("%s: ISAR IIS_PSTEV pm %d sta %x\n", - isar->name, ch->bch.state, - isar->cmsb); - } else { - pr_debug("%s: ISAR spurious IIS_PSTEV %x/%x/%x\n", - isar->name, isar->iis, isar->cmsb, - isar->clsb); - isar->write_reg(isar->hw, ISAR_IIA, 0); - } - break; - case ISAR_IIS_PSTRSP: - ch = sel_bch_isar(isar, isar->iis >> 6); - if (ch) { - rcv_mbox(isar, NULL); - isar_pump_status_rsp(ch); - } else { - pr_debug("%s: ISAR spurious IIS_PSTRSP %x/%x/%x\n", - isar->name, isar->iis, isar->cmsb, - isar->clsb); - isar->write_reg(isar->hw, ISAR_IIA, 0); - } - break; - case ISAR_IIS_DIAG: - case ISAR_IIS_BSTRSP: - case ISAR_IIS_IOM2RSP: - rcv_mbox(isar, NULL); - break; - case ISAR_IIS_INVMSG: - rcv_mbox(isar, NULL); - pr_debug("%s: invalid msg his:%x\n", isar->name, isar->cmsb); - break; - default: - rcv_mbox(isar, NULL); - pr_debug("%s: unhandled msg iis(%x) ctrl(%x/%x)\n", - isar->name, isar->iis, isar->cmsb, isar->clsb); - break; - } -} -EXPORT_SYMBOL(mISDNisar_irq); - -static void -ftimer_handler(struct timer_list *t) -{ - struct isar_ch *ch = timer_container_of(ch, t, ftimer); - - pr_debug("%s: ftimer flags %lx\n", ch->is->name, ch->bch.Flags); - test_and_clear_bit(FLG_FTI_RUN, &ch->bch.Flags); - if (test_and_clear_bit(FLG_LL_CONN, &ch->bch.Flags)) - deliver_status(ch, HW_MOD_CONNECT); -} - -static void -setup_pump(struct isar_ch *ch) { - u8 dps = SET_DPS(ch->dpath); - u8 ctrl, param[6]; - - switch (ch->bch.state) { - case ISDN_P_NONE: - case ISDN_P_B_RAW: - case ISDN_P_B_HDLC: - send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL); - break; - case ISDN_P_B_L2DTMF: - if (test_bit(FLG_DTMFSEND, &ch->bch.Flags)) { - param[0] = 5; /* TOA 5 db */ - send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, - PMOD_DTMF_TRANS, 1, param); - } else { - param[0] = 40; /* REL -46 dbm */ - send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, - PMOD_DTMF, 1, param); - } - fallthrough; - case ISDN_P_B_MODEM_ASYNC: - ctrl = PMOD_DATAMODEM; - if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) { - ctrl |= PCTRL_ORIG; - param[5] = PV32P6_CTN; - } else { - param[5] = PV32P6_ATN; - } - param[0] = 6; /* 6 db */ - param[1] = PV32P2_V23R | PV32P2_V22A | PV32P2_V22B | - PV32P2_V22C | PV32P2_V21 | PV32P2_BEL; - param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B; - param[3] = PV32P4_UT144; - param[4] = PV32P5_UT144; - send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param); - break; - case ISDN_P_B_T30_FAX: - ctrl = PMOD_FAX; - if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) { - ctrl |= PCTRL_ORIG; - param[1] = PFAXP2_CTN; - } else { - param[1] = PFAXP2_ATN; - } - param[0] = 6; /* 6 db */ - send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param); - ch->state = STFAX_NULL; - ch->newcmd = 0; - ch->newmod = 0; - test_and_set_bit(FLG_FTI_RUN, &ch->bch.Flags); - break; - } - udelay(1000); - send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); - udelay(1000); -} - -static void -setup_sart(struct isar_ch *ch) { - u8 dps = SET_DPS(ch->dpath); - u8 ctrl, param[2] = {0, 0}; - - switch (ch->bch.state) { - case ISDN_P_NONE: - send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, - 0, NULL); - break; - case ISDN_P_B_RAW: - case ISDN_P_B_L2DTMF: - send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, - 2, param); - break; - case ISDN_P_B_HDLC: - case ISDN_P_B_T30_FAX: - send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, - 1, param); - break; - case ISDN_P_B_MODEM_ASYNC: - ctrl = SMODE_V14 | SCTRL_HDMC_BOTH; - param[0] = S_P1_CHS_8; - param[1] = S_P2_BFT_DEF; - send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, ctrl, 2, param); - break; - } - udelay(1000); - send_mbox(ch->is, dps | ISAR_HIS_BSTREQ, 0, 0, NULL); - udelay(1000); -} - -static void -setup_iom2(struct isar_ch *ch) { - u8 dps = SET_DPS(ch->dpath); - u8 cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD, 0, 0, 0, 0}; - - if (ch->bch.nr == 2) { - msg[1] = 1; - msg[3] = 1; - } - switch (ch->bch.state) { - case ISDN_P_NONE: - cmsb = 0; - /* dummy slot */ - msg[1] = ch->dpath + 2; - msg[3] = ch->dpath + 2; - break; - case ISDN_P_B_RAW: - case ISDN_P_B_HDLC: - break; - case ISDN_P_B_MODEM_ASYNC: - case ISDN_P_B_T30_FAX: - cmsb |= IOM_CTRL_RCV; - fallthrough; - case ISDN_P_B_L2DTMF: - if (test_bit(FLG_DTMFSEND, &ch->bch.Flags)) - cmsb |= IOM_CTRL_RCV; - cmsb |= IOM_CTRL_ALAW; - break; - } - send_mbox(ch->is, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg); - udelay(1000); - send_mbox(ch->is, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL); - udelay(1000); -} - -static int -modeisar(struct isar_ch *ch, u32 bprotocol) -{ - /* Here we are selecting the best datapath for requested protocol */ - if (ch->bch.state == ISDN_P_NONE) { /* New Setup */ - switch (bprotocol) { - case ISDN_P_NONE: /* init */ - if (!ch->dpath) - /* no init for dpath 0 */ - return 0; - test_and_clear_bit(FLG_HDLC, &ch->bch.Flags); - test_and_clear_bit(FLG_TRANSPARENT, &ch->bch.Flags); - break; - case ISDN_P_B_RAW: - case ISDN_P_B_HDLC: - /* best is datapath 2 */ - if (!test_and_set_bit(ISAR_DP2_USE, &ch->is->Flags)) - ch->dpath = 2; - else if (!test_and_set_bit(ISAR_DP1_USE, - &ch->is->Flags)) - ch->dpath = 1; - else { - pr_info("modeisar both paths in use\n"); - return -EBUSY; - } - if (bprotocol == ISDN_P_B_HDLC) - test_and_set_bit(FLG_HDLC, &ch->bch.Flags); - else - test_and_set_bit(FLG_TRANSPARENT, - &ch->bch.Flags); - break; - case ISDN_P_B_MODEM_ASYNC: - case ISDN_P_B_T30_FAX: - case ISDN_P_B_L2DTMF: - /* only datapath 1 */ - if (!test_and_set_bit(ISAR_DP1_USE, &ch->is->Flags)) - ch->dpath = 1; - else { - pr_info("%s: ISAR modeisar analog functions" - "only with DP1\n", ch->is->name); - return -EBUSY; - } - break; - default: - pr_info("%s: protocol not known %x\n", ch->is->name, - bprotocol); - return -ENOPROTOOPT; - } - } - pr_debug("%s: ISAR ch%d dp%d protocol %x->%x\n", ch->is->name, - ch->bch.nr, ch->dpath, ch->bch.state, bprotocol); - ch->bch.state = bprotocol; - setup_pump(ch); - setup_iom2(ch); - setup_sart(ch); - if (ch->bch.state == ISDN_P_NONE) { - /* Clear resources */ - if (ch->dpath == 1) - test_and_clear_bit(ISAR_DP1_USE, &ch->is->Flags); - else if (ch->dpath == 2) - test_and_clear_bit(ISAR_DP2_USE, &ch->is->Flags); - ch->dpath = 0; - ch->is->ctrl(ch->is->hw, HW_DEACT_IND, ch->bch.nr); - } else - ch->is->ctrl(ch->is->hw, HW_ACTIVATE_IND, ch->bch.nr); - return 0; -} - -static void -isar_pump_cmd(struct isar_ch *ch, u32 cmd, u8 para) -{ - u8 dps = SET_DPS(ch->dpath); - u8 ctrl = 0, nom = 0, p1 = 0; - - pr_debug("%s: isar_pump_cmd %x/%x state(%x)\n", - ch->is->name, cmd, para, ch->bch.state); - switch (cmd) { - case HW_MOD_FTM: - if (ch->state == STFAX_READY) { - p1 = para; - ctrl = PCTRL_CMD_FTM; - nom = 1; - ch->state = STFAX_LINE; - ch->cmd = ctrl; - ch->mod = para; - ch->newmod = 0; - ch->newcmd = 0; - ch->try_mod = 3; - } else if ((ch->state == STFAX_ACTIV) && - (ch->cmd == PCTRL_CMD_FTM) && (ch->mod == para)) - deliver_status(ch, HW_MOD_CONNECT); - else { - ch->newmod = para; - ch->newcmd = PCTRL_CMD_FTM; - nom = 0; - ctrl = PCTRL_CMD_ESC; - ch->state = STFAX_ESCAPE; - } - break; - case HW_MOD_FTH: - if (ch->state == STFAX_READY) { - p1 = para; - ctrl = PCTRL_CMD_FTH; - nom = 1; - ch->state = STFAX_LINE; - ch->cmd = ctrl; - ch->mod = para; - ch->newmod = 0; - ch->newcmd = 0; - ch->try_mod = 3; - } else if ((ch->state == STFAX_ACTIV) && - (ch->cmd == PCTRL_CMD_FTH) && (ch->mod == para)) - deliver_status(ch, HW_MOD_CONNECT); - else { - ch->newmod = para; - ch->newcmd = PCTRL_CMD_FTH; - nom = 0; - ctrl = PCTRL_CMD_ESC; - ch->state = STFAX_ESCAPE; - } - break; - case HW_MOD_FRM: - if (ch->state == STFAX_READY) { - p1 = para; - ctrl = PCTRL_CMD_FRM; - nom = 1; - ch->state = STFAX_LINE; - ch->cmd = ctrl; - ch->mod = para; - ch->newmod = 0; - ch->newcmd = 0; - ch->try_mod = 3; - } else if ((ch->state == STFAX_ACTIV) && - (ch->cmd == PCTRL_CMD_FRM) && (ch->mod == para)) - deliver_status(ch, HW_MOD_CONNECT); - else { - ch->newmod = para; - ch->newcmd = PCTRL_CMD_FRM; - nom = 0; - ctrl = PCTRL_CMD_ESC; - ch->state = STFAX_ESCAPE; - } - break; - case HW_MOD_FRH: - if (ch->state == STFAX_READY) { - p1 = para; - ctrl = PCTRL_CMD_FRH; - nom = 1; - ch->state = STFAX_LINE; - ch->cmd = ctrl; - ch->mod = para; - ch->newmod = 0; - ch->newcmd = 0; - ch->try_mod = 3; - } else if ((ch->state == STFAX_ACTIV) && - (ch->cmd == PCTRL_CMD_FRH) && (ch->mod == para)) - deliver_status(ch, HW_MOD_CONNECT); - else { - ch->newmod = para; - ch->newcmd = PCTRL_CMD_FRH; - nom = 0; - ctrl = PCTRL_CMD_ESC; - ch->state = STFAX_ESCAPE; - } - break; - case PCTRL_CMD_TDTMF: - p1 = para; - nom = 1; - ctrl = PCTRL_CMD_TDTMF; - break; - } - if (ctrl) - send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1); -} - -static void -isar_setup(struct isar_hw *isar) -{ - u8 msg; - int i; - - /* Dpath 1, 2 */ - msg = 61; - for (i = 0; i < 2; i++) { - /* Buffer Config */ - send_mbox(isar, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) | - ISAR_HIS_P12CFG, 4, 1, &msg); - isar->ch[i].mml = msg; - isar->ch[i].bch.state = 0; - isar->ch[i].dpath = i + 1; - modeisar(&isar->ch[i], ISDN_P_NONE); - } -} - -static int -isar_l2l1(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct isar_ch *ich = container_of(bch, struct isar_ch, bch); - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - u32 id, *val; - u_long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(ich->is->hwlock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - ret = 0; - isar_fill_fifo(ich); - } - spin_unlock_irqrestore(ich->is->hwlock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(ich->is->hwlock, flags); - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) - ret = modeisar(ich, ch->protocol); - else - ret = 0; - spin_unlock_irqrestore(ich->is->hwlock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - spin_lock_irqsave(ich->is->hwlock, flags); - mISDN_clear_bchannel(bch); - modeisar(ich, ISDN_P_NONE); - spin_unlock_irqrestore(ich->is->hwlock, flags); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - ret = 0; - break; - case PH_CONTROL_REQ: - val = (u32 *)skb->data; - pr_debug("%s: PH_CONTROL | REQUEST %x/%x\n", ich->is->name, - hh->id, *val); - if ((hh->id == 0) && ((*val & ~DTMF_TONE_MASK) == - DTMF_TONE_VAL)) { - if (bch->state == ISDN_P_B_L2DTMF) { - char tt = *val & DTMF_TONE_MASK; - - if (tt == '*') - tt = 0x1e; - else if (tt == '#') - tt = 0x1f; - else if (tt > '9') - tt -= 7; - tt &= 0x1f; - spin_lock_irqsave(ich->is->hwlock, flags); - isar_pump_cmd(ich, PCTRL_CMD_TDTMF, tt); - spin_unlock_irqrestore(ich->is->hwlock, flags); - } else { - pr_info("%s: DTMF send wrong protocol %x\n", - __func__, bch->state); - return -EINVAL; - } - } else if ((hh->id == HW_MOD_FRM) || (hh->id == HW_MOD_FRH) || - (hh->id == HW_MOD_FTM) || (hh->id == HW_MOD_FTH)) { - for (id = 0; id < FAXMODCNT; id++) - if (faxmodulation[id] == *val) - break; - if ((FAXMODCNT > id) && - test_bit(FLG_INITIALIZED, &bch->Flags)) { - pr_debug("%s: isar: new mod\n", ich->is->name); - isar_pump_cmd(ich, hh->id, *val); - ret = 0; - } else { - pr_info("%s: wrong modulation\n", - ich->is->name); - ret = -EINVAL; - } - } else if (hh->id == HW_MOD_LASTDATA) - test_and_set_bit(FLG_DLEETX, &bch->Flags); - else { - pr_info("%s: unknown PH_CONTROL_REQ %x\n", - ich->is->name, hh->id); - ret = -EINVAL; - } - fallthrough; - default: - pr_info("%s: %s unknown prim(%x,%x)\n", - ich->is->name, __func__, hh->prim, hh->id); - ret = -EINVAL; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(bch, cq); -} - -static int -isar_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct isar_ch *ich = container_of(bch, struct isar_ch, bch); - int ret = -EINVAL; - u_long flags; - - pr_debug("%s: %s cmd:%x %p\n", ich->is->name, __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - cancel_work_sync(&bch->workq); - spin_lock_irqsave(ich->is->hwlock, flags); - mISDN_clear_bchannel(bch); - modeisar(ich, ISDN_P_NONE); - spin_unlock_irqrestore(ich->is->hwlock, flags); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(ich->is->owner); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bch, arg); - break; - default: - pr_info("%s: %s unknown prim(%x)\n", - ich->is->name, __func__, cmd); - } - return ret; -} - -static void -free_isar(struct isar_hw *isar) -{ - modeisar(&isar->ch[0], ISDN_P_NONE); - modeisar(&isar->ch[1], ISDN_P_NONE); - timer_delete(&isar->ch[0].ftimer); - timer_delete(&isar->ch[1].ftimer); - test_and_clear_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags); - test_and_clear_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags); -} - -static int -init_isar(struct isar_hw *isar) -{ - int cnt = 3; - - while (cnt--) { - isar->version = ISARVersion(isar); - if (isar->ch[0].bch.debug & DEBUG_HW) - pr_notice("%s: Testing version %d (%d time)\n", - isar->name, isar->version, 3 - cnt); - if (isar->version == 1) - break; - isar->ctrl(isar->hw, HW_RESET_REQ, 0); - } - if (isar->version != 1) - return -EINVAL; - timer_setup(&isar->ch[0].ftimer, ftimer_handler, 0); - test_and_set_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags); - timer_setup(&isar->ch[1].ftimer, ftimer_handler, 0); - test_and_set_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags); - return 0; -} - -static int -isar_open(struct isar_hw *isar, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - bch = &isar->ch[rq->adr.channel - 1].bch; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - return 0; -} - -u32 -mISDNisar_init(struct isar_hw *isar, void *hw) -{ - u32 ret, i; - - isar->hw = hw; - for (i = 0; i < 2; i++) { - isar->ch[i].bch.nr = i + 1; - mISDN_initbchannel(&isar->ch[i].bch, MAX_DATA_MEM, 32); - isar->ch[i].bch.ch.nr = i + 1; - isar->ch[i].bch.ch.send = &isar_l2l1; - isar->ch[i].bch.ch.ctrl = isar_bctrl; - isar->ch[i].bch.hw = hw; - isar->ch[i].is = isar; - } - - isar->init = &init_isar; - isar->release = &free_isar; - isar->firmware = &load_firmware; - isar->open = &isar_open; - - ret = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_L2DTMF & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_MODEM_ASYNC & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_T30_FAX & ISDN_P_B_MASK)); - - return ret; -} -EXPORT_SYMBOL(mISDNisar_init); - -static int __init isar_mod_init(void) -{ - pr_notice("mISDN: ISAR driver Rev. %s\n", ISAR_REV); - return 0; -} - -static void __exit isar_mod_cleanup(void) -{ - pr_notice("mISDN: ISAR module unloaded\n"); -} -module_init(isar_mod_init); -module_exit(isar_mod_cleanup); diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c deleted file mode 100644 index 8d740d8eacec..000000000000 --- a/drivers/isdn/hardware/mISDN/netjet.c +++ /dev/null @@ -1,1154 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * NETJet mISDN driver - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include -#include "ipac.h" -#include "iohelper.h" -#include "netjet.h" -#include "isdnhdlc.h" - -#define NETJET_REV "2.0" - -enum nj_types { - NETJET_S_TJ300, - NETJET_S_TJ320, - ENTERNOW__TJ320, -}; - -struct tiger_dma { - size_t size; - u32 *start; - int idx; - u32 dmastart; - u32 dmairq; - u32 dmaend; - u32 dmacur; -}; - -struct tiger_hw; - -struct tiger_ch { - struct bchannel bch; - struct tiger_hw *nj; - int idx; - int free; - int lastrx; - u16 rxstate; - u16 txstate; - struct isdnhdlc_vars hsend; - struct isdnhdlc_vars hrecv; - u8 *hsbuf; - u8 *hrbuf; -}; - -#define TX_INIT 0x0001 -#define TX_IDLE 0x0002 -#define TX_RUN 0x0004 -#define TX_UNDERRUN 0x0100 -#define RX_OVERRUN 0x0100 - -#define LOG_SIZE 64 - -struct tiger_hw { - struct list_head list; - struct pci_dev *pdev; - char name[MISDN_MAX_IDLEN]; - enum nj_types typ; - int irq; - u32 irqcnt; - u32 base; - size_t base_s; - dma_addr_t dma; - void *dma_p; - spinlock_t lock; /* lock HW */ - struct isac_hw isac; - struct tiger_dma send; - struct tiger_dma recv; - struct tiger_ch bc[2]; - u8 ctrlreg; - u8 dmactrl; - u8 auxd; - u8 last_is0; - u8 irqmask0; - char log[LOG_SIZE]; -}; - -static LIST_HEAD(Cards); -static DEFINE_RWLOCK(card_lock); /* protect Cards */ -static u32 debug; -static int nj_cnt; - -static void -_set_debug(struct tiger_hw *card) -{ - card->isac.dch.debug = debug; - card->bc[0].bch.debug = debug; - card->bc[1].bch.debug = debug; -} - -static int -set_debug(const char *val, const struct kernel_param *kp) -{ - int ret; - struct tiger_hw *card; - - ret = param_set_uint(val, kp); - if (!ret) { - read_lock(&card_lock); - list_for_each_entry(card, &Cards, list) - _set_debug(card); - read_unlock(&card_lock); - } - return ret; -} - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for NETJet cards"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(NETJET_REV); -module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Netjet debug mask"); - -static void -nj_disable_hwirq(struct tiger_hw *card) -{ - outb(0, card->base + NJ_IRQMASK0); - outb(0, card->base + NJ_IRQMASK1); -} - - -static u8 -ReadISAC_nj(void *p, u8 offset) -{ - struct tiger_hw *card = p; - u8 ret; - - card->auxd &= 0xfc; - card->auxd |= (offset >> 4) & 3; - outb(card->auxd, card->base + NJ_AUXDATA); - ret = inb(card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2)); - return ret; -} - -static void -WriteISAC_nj(void *p, u8 offset, u8 value) -{ - struct tiger_hw *card = p; - - card->auxd &= 0xfc; - card->auxd |= (offset >> 4) & 3; - outb(card->auxd, card->base + NJ_AUXDATA); - outb(value, card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2)); -} - -static void -ReadFiFoISAC_nj(void *p, u8 offset, u8 *data, int size) -{ - struct tiger_hw *card = p; - - card->auxd &= 0xfc; - outb(card->auxd, card->base + NJ_AUXDATA); - insb(card->base + NJ_ISAC_OFF, data, size); -} - -static void -WriteFiFoISAC_nj(void *p, u8 offset, u8 *data, int size) -{ - struct tiger_hw *card = p; - - card->auxd &= 0xfc; - outb(card->auxd, card->base + NJ_AUXDATA); - outsb(card->base + NJ_ISAC_OFF, data, size); -} - -static void -fill_mem(struct tiger_ch *bc, u32 idx, u32 cnt, u32 fill) -{ - struct tiger_hw *card = bc->bch.hw; - u32 mask = 0xff, val; - - pr_debug("%s: B%1d fill %02x len %d idx %d/%d\n", card->name, - bc->bch.nr, fill, cnt, idx, card->send.idx); - if (bc->bch.nr & 2) { - fill <<= 8; - mask <<= 8; - } - mask ^= 0xffffffff; - while (cnt--) { - val = card->send.start[idx]; - val &= mask; - val |= fill; - card->send.start[idx++] = val; - if (idx >= card->send.size) - idx = 0; - } -} - -static int -mode_tiger(struct tiger_ch *bc, u32 protocol) -{ - struct tiger_hw *card = bc->bch.hw; - - pr_debug("%s: B%1d protocol %x-->%x\n", card->name, - bc->bch.nr, bc->bch.state, protocol); - switch (protocol) { - case ISDN_P_NONE: - if (bc->bch.state == ISDN_P_NONE) - break; - fill_mem(bc, 0, card->send.size, 0xff); - bc->bch.state = protocol; - /* only stop dma and interrupts if both channels NULL */ - if ((card->bc[0].bch.state == ISDN_P_NONE) && - (card->bc[1].bch.state == ISDN_P_NONE)) { - card->dmactrl = 0; - outb(card->dmactrl, card->base + NJ_DMACTRL); - outb(0, card->base + NJ_IRQMASK0); - } - test_and_clear_bit(FLG_HDLC, &bc->bch.Flags); - test_and_clear_bit(FLG_TRANSPARENT, &bc->bch.Flags); - bc->txstate = 0; - bc->rxstate = 0; - bc->lastrx = -1; - break; - case ISDN_P_B_RAW: - test_and_set_bit(FLG_TRANSPARENT, &bc->bch.Flags); - bc->bch.state = protocol; - bc->idx = 0; - bc->free = card->send.size / 2; - bc->rxstate = 0; - bc->txstate = TX_INIT | TX_IDLE; - bc->lastrx = -1; - if (!card->dmactrl) { - card->dmactrl = 1; - outb(card->dmactrl, card->base + NJ_DMACTRL); - outb(0x0f, card->base + NJ_IRQMASK0); - } - break; - case ISDN_P_B_HDLC: - test_and_set_bit(FLG_HDLC, &bc->bch.Flags); - bc->bch.state = protocol; - bc->idx = 0; - bc->free = card->send.size / 2; - bc->rxstate = 0; - bc->txstate = TX_INIT | TX_IDLE; - isdnhdlc_rcv_init(&bc->hrecv, 0); - isdnhdlc_out_init(&bc->hsend, 0); - bc->lastrx = -1; - if (!card->dmactrl) { - card->dmactrl = 1; - outb(card->dmactrl, card->base + NJ_DMACTRL); - outb(0x0f, card->base + NJ_IRQMASK0); - } - break; - default: - pr_info("%s: %s protocol %x not handled\n", card->name, - __func__, protocol); - return -ENOPROTOOPT; - } - card->send.dmacur = inl(card->base + NJ_DMA_READ_ADR); - card->recv.dmacur = inl(card->base + NJ_DMA_WRITE_ADR); - card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2; - card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2; - pr_debug("%s: %s ctrl %x irq %02x/%02x idx %d/%d\n", - card->name, __func__, - inb(card->base + NJ_DMACTRL), - inb(card->base + NJ_IRQMASK0), - inb(card->base + NJ_IRQSTAT0), - card->send.idx, - card->recv.idx); - return 0; -} - -static void -nj_reset(struct tiger_hw *card) -{ - outb(0xff, card->base + NJ_CTRL); /* Reset On */ - mdelay(1); - - /* now edge triggered for TJ320 GE 13/07/00 */ - /* see comment in IRQ function */ - if (card->typ == NETJET_S_TJ320) /* TJ320 */ - card->ctrlreg = 0x40; /* Reset Off and status read clear */ - else - card->ctrlreg = 0x00; /* Reset Off and status read clear */ - outb(card->ctrlreg, card->base + NJ_CTRL); - mdelay(10); - - /* configure AUX pins (all output except ISAC IRQ pin) */ - card->auxd = 0; - card->dmactrl = 0; - outb(~NJ_ISACIRQ, card->base + NJ_AUXCTRL); - outb(NJ_ISACIRQ, card->base + NJ_IRQMASK1); - outb(card->auxd, card->base + NJ_AUXDATA); -} - -static int -inittiger(struct tiger_hw *card) -{ - int i; - - card->dma_p = dma_alloc_coherent(&card->pdev->dev, NJ_DMA_SIZE, - &card->dma, GFP_ATOMIC); - if (!card->dma_p) { - pr_info("%s: No DMA memory\n", card->name); - return -ENOMEM; - } - if ((u64)card->dma > 0xffffffff) { - pr_info("%s: DMA outside 32 bit\n", card->name); - return -ENOMEM; - } - for (i = 0; i < 2; i++) { - card->bc[i].hsbuf = kmalloc(NJ_DMA_TXSIZE, GFP_ATOMIC); - if (!card->bc[i].hsbuf) { - pr_info("%s: no B%d send buffer\n", card->name, i + 1); - return -ENOMEM; - } - card->bc[i].hrbuf = kmalloc(NJ_DMA_RXSIZE, GFP_ATOMIC); - if (!card->bc[i].hrbuf) { - pr_info("%s: no B%d recv buffer\n", card->name, i + 1); - return -ENOMEM; - } - } - memset(card->dma_p, 0xff, NJ_DMA_SIZE); - - card->send.start = card->dma_p; - card->send.dmastart = (u32)card->dma; - card->send.dmaend = card->send.dmastart + - (4 * (NJ_DMA_TXSIZE - 1)); - card->send.dmairq = card->send.dmastart + - (4 * ((NJ_DMA_TXSIZE / 2) - 1)); - card->send.size = NJ_DMA_TXSIZE; - - if (debug & DEBUG_HW) - pr_notice("%s: send buffer phy %#x - %#x - %#x virt %p" - " size %zu u32\n", card->name, - card->send.dmastart, card->send.dmairq, - card->send.dmaend, card->send.start, card->send.size); - - outl(card->send.dmastart, card->base + NJ_DMA_READ_START); - outl(card->send.dmairq, card->base + NJ_DMA_READ_IRQ); - outl(card->send.dmaend, card->base + NJ_DMA_READ_END); - - card->recv.start = card->dma_p + (NJ_DMA_SIZE / 2); - card->recv.dmastart = (u32)card->dma + (NJ_DMA_SIZE / 2); - card->recv.dmaend = card->recv.dmastart + - (4 * (NJ_DMA_RXSIZE - 1)); - card->recv.dmairq = card->recv.dmastart + - (4 * ((NJ_DMA_RXSIZE / 2) - 1)); - card->recv.size = NJ_DMA_RXSIZE; - - if (debug & DEBUG_HW) - pr_notice("%s: recv buffer phy %#x - %#x - %#x virt %p" - " size %zu u32\n", card->name, - card->recv.dmastart, card->recv.dmairq, - card->recv.dmaend, card->recv.start, card->recv.size); - - outl(card->recv.dmastart, card->base + NJ_DMA_WRITE_START); - outl(card->recv.dmairq, card->base + NJ_DMA_WRITE_IRQ); - outl(card->recv.dmaend, card->base + NJ_DMA_WRITE_END); - return 0; -} - -static void -read_dma(struct tiger_ch *bc, u32 idx, int cnt) -{ - struct tiger_hw *card = bc->bch.hw; - int i, stat; - u32 val; - u8 *p, *pn; - - if (bc->lastrx == idx) { - bc->rxstate |= RX_OVERRUN; - pr_info("%s: B%1d overrun at idx %d\n", card->name, - bc->bch.nr, idx); - } - bc->lastrx = idx; - if (test_bit(FLG_RX_OFF, &bc->bch.Flags)) { - bc->bch.dropcnt += cnt; - return; - } - stat = bchannel_get_rxbuf(&bc->bch, cnt); - /* only transparent use the count here, HDLC overun is detected later */ - if (stat == -ENOMEM) { - pr_warn("%s.B%d: No memory for %d bytes\n", - card->name, bc->bch.nr, cnt); - return; - } - if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) - p = skb_put(bc->bch.rx_skb, cnt); - else - p = bc->hrbuf; - - for (i = 0; i < cnt; i++) { - val = card->recv.start[idx++]; - if (bc->bch.nr & 2) - val >>= 8; - if (idx >= card->recv.size) - idx = 0; - p[i] = val & 0xff; - } - - if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) { - recv_Bchannel(&bc->bch, 0, false); - return; - } - - pn = bc->hrbuf; - while (cnt > 0) { - stat = isdnhdlc_decode(&bc->hrecv, pn, cnt, &i, - bc->bch.rx_skb->data, bc->bch.maxlen); - if (stat > 0) { /* valid frame received */ - p = skb_put(bc->bch.rx_skb, stat); - if (debug & DEBUG_HW_BFIFO) { - snprintf(card->log, LOG_SIZE, - "B%1d-recv %s %d ", bc->bch.nr, - card->name, stat); - print_hex_dump_bytes(card->log, - DUMP_PREFIX_OFFSET, p, - stat); - } - recv_Bchannel(&bc->bch, 0, false); - stat = bchannel_get_rxbuf(&bc->bch, bc->bch.maxlen); - if (stat < 0) { - pr_warn("%s.B%d: No memory for %d bytes\n", - card->name, bc->bch.nr, cnt); - return; - } - } else if (stat == -HDLC_CRC_ERROR) { - pr_info("%s: B%1d receive frame CRC error\n", - card->name, bc->bch.nr); - } else if (stat == -HDLC_FRAMING_ERROR) { - pr_info("%s: B%1d receive framing error\n", - card->name, bc->bch.nr); - } else if (stat == -HDLC_LENGTH_ERROR) { - pr_info("%s: B%1d receive frame too long (> %d)\n", - card->name, bc->bch.nr, bc->bch.maxlen); - } - pn += i; - cnt -= i; - } -} - -static void -recv_tiger(struct tiger_hw *card, u8 irq_stat) -{ - u32 idx; - int cnt = card->recv.size / 2; - - /* Note receive is via the WRITE DMA channel */ - card->last_is0 &= ~NJ_IRQM0_WR_MASK; - card->last_is0 |= (irq_stat & NJ_IRQM0_WR_MASK); - - if (irq_stat & NJ_IRQM0_WR_END) - idx = cnt - 1; - else - idx = card->recv.size - 1; - - if (test_bit(FLG_ACTIVE, &card->bc[0].bch.Flags)) - read_dma(&card->bc[0], idx, cnt); - if (test_bit(FLG_ACTIVE, &card->bc[1].bch.Flags)) - read_dma(&card->bc[1], idx, cnt); -} - -/* sync with current DMA address at start or after exception */ -static void -resync(struct tiger_ch *bc, struct tiger_hw *card) -{ - card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR); - card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2; - if (bc->free > card->send.size / 2) - bc->free = card->send.size / 2; - /* currently we simple sync to the next complete free area - * this hast the advantage that we have always maximum time to - * handle TX irq - */ - if (card->send.idx < ((card->send.size / 2) - 1)) - bc->idx = (card->recv.size / 2) - 1; - else - bc->idx = card->recv.size - 1; - bc->txstate = TX_RUN; - pr_debug("%s: %s B%1d free %d idx %d/%d\n", card->name, - __func__, bc->bch.nr, bc->free, bc->idx, card->send.idx); -} - -static int bc_next_frame(struct tiger_ch *); - -static void -fill_hdlc_flag(struct tiger_ch *bc) -{ - struct tiger_hw *card = bc->bch.hw; - int count, i; - u32 m, v; - u8 *p; - - if (bc->free == 0) - return; - pr_debug("%s: %s B%1d %d state %x idx %d/%d\n", card->name, - __func__, bc->bch.nr, bc->free, bc->txstate, - bc->idx, card->send.idx); - if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN)) - resync(bc, card); - count = isdnhdlc_encode(&bc->hsend, NULL, 0, &i, - bc->hsbuf, bc->free); - pr_debug("%s: B%1d hdlc encoded %d flags\n", card->name, - bc->bch.nr, count); - bc->free -= count; - p = bc->hsbuf; - m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff; - for (i = 0; i < count; i++) { - if (bc->idx >= card->send.size) - bc->idx = 0; - v = card->send.start[bc->idx]; - v &= m; - v |= (bc->bch.nr & 1) ? (u32)(p[i]) : ((u32)(p[i])) << 8; - card->send.start[bc->idx++] = v; - } - if (debug & DEBUG_HW_BFIFO) { - snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ", - bc->bch.nr, card->name, count); - print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count); - } -} - -static void -fill_dma(struct tiger_ch *bc) -{ - struct tiger_hw *card = bc->bch.hw; - int count, i, fillempty = 0; - u32 m, v, n = 0; - u8 *p; - - if (bc->free == 0) - return; - if (!bc->bch.tx_skb) { - if (!test_bit(FLG_TX_EMPTY, &bc->bch.Flags)) - return; - fillempty = 1; - count = card->send.size >> 1; - p = bc->bch.fill; - } else { - count = bc->bch.tx_skb->len - bc->bch.tx_idx; - if (count <= 0) - return; - pr_debug("%s: %s B%1d %d/%d/%d/%d state %x idx %d/%d\n", - card->name, __func__, bc->bch.nr, count, bc->free, - bc->bch.tx_idx, bc->bch.tx_skb->len, bc->txstate, - bc->idx, card->send.idx); - p = bc->bch.tx_skb->data + bc->bch.tx_idx; - } - if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN)) - resync(bc, card); - if (test_bit(FLG_HDLC, &bc->bch.Flags) && !fillempty) { - count = isdnhdlc_encode(&bc->hsend, p, count, &i, - bc->hsbuf, bc->free); - pr_debug("%s: B%1d hdlc encoded %d in %d\n", card->name, - bc->bch.nr, i, count); - bc->bch.tx_idx += i; - bc->free -= count; - p = bc->hsbuf; - } else { - if (count > bc->free) - count = bc->free; - if (!fillempty) - bc->bch.tx_idx += count; - bc->free -= count; - } - m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff; - if (fillempty) { - n = p[0]; - if (!(bc->bch.nr & 1)) - n <<= 8; - for (i = 0; i < count; i++) { - if (bc->idx >= card->send.size) - bc->idx = 0; - v = card->send.start[bc->idx]; - v &= m; - v |= n; - card->send.start[bc->idx++] = v; - } - } else { - for (i = 0; i < count; i++) { - if (bc->idx >= card->send.size) - bc->idx = 0; - v = card->send.start[bc->idx]; - v &= m; - n = p[i]; - v |= (bc->bch.nr & 1) ? n : n << 8; - card->send.start[bc->idx++] = v; - } - } - if (debug & DEBUG_HW_BFIFO) { - snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ", - bc->bch.nr, card->name, count); - print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count); - } - if (bc->free) - bc_next_frame(bc); -} - - -static int -bc_next_frame(struct tiger_ch *bc) -{ - int ret = 1; - - if (bc->bch.tx_skb && bc->bch.tx_idx < bc->bch.tx_skb->len) { - fill_dma(bc); - } else { - dev_kfree_skb(bc->bch.tx_skb); - if (get_next_bframe(&bc->bch)) { - fill_dma(bc); - test_and_clear_bit(FLG_TX_EMPTY, &bc->bch.Flags); - } else if (test_bit(FLG_TX_EMPTY, &bc->bch.Flags)) { - fill_dma(bc); - } else if (test_bit(FLG_FILLEMPTY, &bc->bch.Flags)) { - test_and_set_bit(FLG_TX_EMPTY, &bc->bch.Flags); - ret = 0; - } else { - ret = 0; - } - } - return ret; -} - -static void -send_tiger_bc(struct tiger_hw *card, struct tiger_ch *bc) -{ - int ret; - - bc->free += card->send.size / 2; - if (bc->free >= card->send.size) { - if (!(bc->txstate & (TX_UNDERRUN | TX_INIT))) { - pr_info("%s: B%1d TX underrun state %x\n", card->name, - bc->bch.nr, bc->txstate); - bc->txstate |= TX_UNDERRUN; - } - bc->free = card->send.size; - } - ret = bc_next_frame(bc); - if (!ret) { - if (test_bit(FLG_HDLC, &bc->bch.Flags)) { - fill_hdlc_flag(bc); - return; - } - pr_debug("%s: B%1d TX no data free %d idx %d/%d\n", card->name, - bc->bch.nr, bc->free, bc->idx, card->send.idx); - if (!(bc->txstate & (TX_IDLE | TX_INIT))) { - fill_mem(bc, bc->idx, bc->free, 0xff); - if (bc->free == card->send.size) - bc->txstate |= TX_IDLE; - } - } -} - -static void -send_tiger(struct tiger_hw *card, u8 irq_stat) -{ - int i; - - /* Note send is via the READ DMA channel */ - if ((irq_stat & card->last_is0) & NJ_IRQM0_RD_MASK) { - pr_info("%s: tiger warn write double dma %x/%x\n", - card->name, irq_stat, card->last_is0); - return; - } else { - card->last_is0 &= ~NJ_IRQM0_RD_MASK; - card->last_is0 |= (irq_stat & NJ_IRQM0_RD_MASK); - } - for (i = 0; i < 2; i++) { - if (test_bit(FLG_ACTIVE, &card->bc[i].bch.Flags)) - send_tiger_bc(card, &card->bc[i]); - } -} - -static irqreturn_t -nj_irq(int intno, void *dev_id) -{ - struct tiger_hw *card = dev_id; - u8 val, s1val, s0val; - - spin_lock(&card->lock); - s0val = inb(card->base | NJ_IRQSTAT0); - s1val = inb(card->base | NJ_IRQSTAT1); - if ((s1val & NJ_ISACIRQ) && (s0val == 0)) { - /* shared IRQ */ - spin_unlock(&card->lock); - return IRQ_NONE; - } - pr_debug("%s: IRQSTAT0 %02x IRQSTAT1 %02x\n", card->name, s0val, s1val); - card->irqcnt++; - if (!(s1val & NJ_ISACIRQ)) { - val = ReadISAC_nj(card, ISAC_ISTA); - if (val) - mISDNisac_irq(&card->isac, val); - } - - if (s0val) - /* write to clear */ - outb(s0val, card->base | NJ_IRQSTAT0); - else - goto end; - s1val = s0val; - /* set bits in sval to indicate which page is free */ - card->recv.dmacur = inl(card->base | NJ_DMA_WRITE_ADR); - card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2; - if (card->recv.dmacur < card->recv.dmairq) - s0val = 0x08; /* the 2nd write area is free */ - else - s0val = 0x04; /* the 1st write area is free */ - - card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR); - card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2; - if (card->send.dmacur < card->send.dmairq) - s0val |= 0x02; /* the 2nd read area is free */ - else - s0val |= 0x01; /* the 1st read area is free */ - - pr_debug("%s: DMA Status %02x/%02x/%02x %d/%d\n", card->name, - s1val, s0val, card->last_is0, - card->recv.idx, card->send.idx); - /* test if we have a DMA interrupt */ - if (s0val != card->last_is0) { - if ((s0val & NJ_IRQM0_RD_MASK) != - (card->last_is0 & NJ_IRQM0_RD_MASK)) - /* got a write dma int */ - send_tiger(card, s0val); - if ((s0val & NJ_IRQM0_WR_MASK) != - (card->last_is0 & NJ_IRQM0_WR_MASK)) - /* got a read dma int */ - recv_tiger(card, s0val); - } -end: - spin_unlock(&card->lock); - return IRQ_HANDLED; -} - -static int -nj_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) -{ - int ret = -EINVAL; - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch); - struct tiger_hw *card = bch->hw; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&card->lock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - fill_dma(bc); - ret = 0; - } - spin_unlock_irqrestore(&card->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(&card->lock, flags); - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) - ret = mode_tiger(bc, ch->protocol); - else - ret = 0; - spin_unlock_irqrestore(&card->lock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - spin_lock_irqsave(&card->lock, flags); - mISDN_clear_bchannel(bch); - mode_tiger(bc, ISDN_P_NONE); - spin_unlock_irqrestore(&card->lock, flags); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - ret = 0; - break; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -channel_bctrl(struct tiger_ch *bc, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(&bc->bch, cq); -} - -static int -nj_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch); - struct tiger_hw *card = bch->hw; - int ret = -EINVAL; - u_long flags; - - pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - cancel_work_sync(&bch->workq); - spin_lock_irqsave(&card->lock, flags); - mISDN_clear_bchannel(bch); - mode_tiger(bc, ISDN_P_NONE); - spin_unlock_irqrestore(&card->lock, flags); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bc, arg); - break; - default: - pr_info("%s: %s unknown prim(%x)\n", card->name, __func__, cmd); - } - return ret; -} - -static int -channel_ctrl(struct tiger_hw *card, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_LOOP: - /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ - if (cq->channel < 0 || cq->channel > 3) { - ret = -EINVAL; - break; - } - ret = card->isac.ctrl(&card->isac, HW_TESTLOOP, cq->channel); - break; - case MISDN_CTRL_L1_TIMER3: - ret = card->isac.ctrl(&card->isac, HW_TIMER3_VALUE, cq->p1); - break; - default: - pr_info("%s: %s unknown Op %x\n", card->name, __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -open_bchannel(struct tiger_hw *card, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - bch = &card->bc[rq->adr.channel - 1].bch; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - return 0; -} - -/* - * device control function - */ -static int -nj_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct tiger_hw *card = dch->hw; - struct channel_req *rq; - int err = 0; - - pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if (rq->protocol == ISDN_P_TE_S0) - err = card->isac.open(&card->isac, rq); - else - err = open_bchannel(card, rq); - if (err) - break; - if (!try_module_get(THIS_MODULE)) - pr_info("%s: cannot get module\n", card->name); - break; - case CLOSE_CHANNEL: - pr_debug("%s: dev(%d) close from %p\n", card->name, dch->dev.id, - __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(card, arg); - break; - default: - pr_debug("%s: %s unknown command %x\n", - card->name, __func__, cmd); - return -EINVAL; - } - return err; -} - -static int -nj_init_card(struct tiger_hw *card) -{ - u_long flags; - int ret; - - spin_lock_irqsave(&card->lock, flags); - nj_disable_hwirq(card); - spin_unlock_irqrestore(&card->lock, flags); - - card->irq = card->pdev->irq; - if (request_irq(card->irq, nj_irq, IRQF_SHARED, card->name, card)) { - pr_info("%s: couldn't get interrupt %d\n", - card->name, card->irq); - card->irq = -1; - return -EIO; - } - - spin_lock_irqsave(&card->lock, flags); - nj_reset(card); - ret = card->isac.init(&card->isac); - if (ret) - goto error; - ret = inittiger(card); - if (ret) - goto error; - mode_tiger(&card->bc[0], ISDN_P_NONE); - mode_tiger(&card->bc[1], ISDN_P_NONE); -error: - spin_unlock_irqrestore(&card->lock, flags); - return ret; -} - - -static void -nj_release(struct tiger_hw *card) -{ - u_long flags; - int i; - - if (card->base_s) { - spin_lock_irqsave(&card->lock, flags); - nj_disable_hwirq(card); - mode_tiger(&card->bc[0], ISDN_P_NONE); - mode_tiger(&card->bc[1], ISDN_P_NONE); - spin_unlock_irqrestore(&card->lock, flags); - card->isac.release(&card->isac); - release_region(card->base, card->base_s); - card->base_s = 0; - } - if (card->irq > 0) - free_irq(card->irq, card); - if (device_is_registered(&card->isac.dch.dev.dev)) - mISDN_unregister_device(&card->isac.dch.dev); - - for (i = 0; i < 2; i++) { - mISDN_freebchannel(&card->bc[i].bch); - kfree(card->bc[i].hsbuf); - kfree(card->bc[i].hrbuf); - } - if (card->dma_p) - dma_free_coherent(&card->pdev->dev, NJ_DMA_SIZE, card->dma_p, - card->dma); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - pci_disable_device(card->pdev); - pci_set_drvdata(card->pdev, NULL); - kfree(card); -} - - -static int -nj_setup(struct tiger_hw *card) -{ - card->base = pci_resource_start(card->pdev, 0); - card->base_s = pci_resource_len(card->pdev, 0); - if (!request_region(card->base, card->base_s, card->name)) { - pr_info("%s: NETjet config port %#x-%#x already in use\n", - card->name, card->base, - (u32)(card->base + card->base_s - 1)); - card->base_s = 0; - return -EIO; - } - ASSIGN_FUNC(nj, ISAC, card->isac); - return 0; -} - - -static int -setup_instance(struct tiger_hw *card) -{ - int i, err; - u_long flags; - - snprintf(card->name, MISDN_MAX_IDLEN - 1, "netjet.%d", nj_cnt + 1); - write_lock_irqsave(&card_lock, flags); - list_add_tail(&card->list, &Cards); - write_unlock_irqrestore(&card_lock, flags); - - _set_debug(card); - card->isac.name = card->name; - spin_lock_init(&card->lock); - card->isac.hwlock = &card->lock; - mISDNisac_init(&card->isac, card); - - card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - card->isac.dch.dev.D.ctrl = nj_dctrl; - for (i = 0; i < 2; i++) { - card->bc[i].bch.nr = i + 1; - set_channelmap(i + 1, card->isac.dch.dev.channelmap); - mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM, - NJ_DMA_RXSIZE >> 1); - card->bc[i].bch.hw = card; - card->bc[i].bch.ch.send = nj_l2l1B; - card->bc[i].bch.ch.ctrl = nj_bctrl; - card->bc[i].bch.ch.nr = i + 1; - list_add(&card->bc[i].bch.ch.list, - &card->isac.dch.dev.bchannels); - card->bc[i].bch.hw = card; - } - err = nj_setup(card); - if (err) - goto error; - err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev, - card->name); - if (err) - goto error; - err = nj_init_card(card); - if (!err) { - nj_cnt++; - pr_notice("Netjet %d cards installed\n", nj_cnt); - return 0; - } -error: - nj_release(card); - return err; -} - -static int -nj_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -ENOMEM; - int cfg; - struct tiger_hw *card; - - if (pdev->subsystem_vendor == 0x8086 && - pdev->subsystem_device == 0x0003) { - pr_notice("Netjet: Digium X100P/X101P not handled\n"); - return -ENODEV; - } - - if (pdev->subsystem_vendor == 0x55 && - pdev->subsystem_device == 0x02) { - pr_notice("Netjet: Enter!Now not handled yet\n"); - return -ENODEV; - } - - if (pdev->subsystem_vendor == 0xb100 && - pdev->subsystem_device == 0x0003) { - pr_notice("Netjet: Digium TDM400P not handled yet\n"); - return -ENODEV; - } - - card = kzalloc_obj(struct tiger_hw); - if (!card) { - pr_info("No kmem for Netjet\n"); - return err; - } - - card->pdev = pdev; - - err = pci_enable_device(pdev); - if (err) { - kfree(card); - return err; - } - - printk(KERN_INFO "nj_probe(mISDN): found adapter at %s\n", - pci_name(pdev)); - - pci_set_master(pdev); - - /* the TJ300 and TJ320 must be detected, the IRQ handling is different - * unfortunately the chips use the same device ID, but the TJ320 has - * the bit20 in status PCI cfg register set - */ - pci_read_config_dword(pdev, 0x04, &cfg); - if (cfg & 0x00100000) - card->typ = NETJET_S_TJ320; - else - card->typ = NETJET_S_TJ300; - - card->base = pci_resource_start(pdev, 0); - pci_set_drvdata(pdev, card); - err = setup_instance(card); - if (err) - pci_set_drvdata(pdev, NULL); - - return err; -} - - -static void nj_remove(struct pci_dev *pdev) -{ - struct tiger_hw *card = pci_get_drvdata(pdev); - - if (card) - nj_release(card); - else - pr_info("%s drvdata already removed\n", __func__); -} - -/* We cannot select cards with PCI_SUB... IDs, since here are cards with - * SUB IDs set to PCI_ANY_ID, so we need to match all and reject - * known other cards which not work with this driver - see probe function */ -static const struct pci_device_id nj_pci_ids[] = { - { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - { } -}; -MODULE_DEVICE_TABLE(pci, nj_pci_ids); - -static struct pci_driver nj_driver = { - .name = "netjet", - .probe = nj_probe, - .remove = nj_remove, - .id_table = nj_pci_ids, -}; - -static int __init nj_init(void) -{ - int err; - - pr_notice("Netjet PCI driver Rev. %s\n", NETJET_REV); - err = pci_register_driver(&nj_driver); - return err; -} - -static void __exit nj_cleanup(void) -{ - pci_unregister_driver(&nj_driver); -} - -module_init(nj_init); -module_exit(nj_cleanup); diff --git a/drivers/isdn/hardware/mISDN/netjet.h b/drivers/isdn/hardware/mISDN/netjet.h deleted file mode 100644 index b23ad9f6d4d0..000000000000 --- a/drivers/isdn/hardware/mISDN/netjet.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * NETjet common header file - * - * Author Karsten Keil - * based on work of Matt Henderson and Daniel Potts, - * Traverse Technologies P/L www.traverse.com.au - * - * Copyright 2009 by Karsten Keil - */ - -#define NJ_CTRL 0x00 -#define NJ_DMACTRL 0x01 -#define NJ_AUXCTRL 0x02 -#define NJ_AUXDATA 0x03 -#define NJ_IRQMASK0 0x04 -#define NJ_IRQMASK1 0x05 -#define NJ_IRQSTAT0 0x06 -#define NJ_IRQSTAT1 0x07 -#define NJ_DMA_READ_START 0x08 -#define NJ_DMA_READ_IRQ 0x0c -#define NJ_DMA_READ_END 0x10 -#define NJ_DMA_READ_ADR 0x14 -#define NJ_DMA_WRITE_START 0x18 -#define NJ_DMA_WRITE_IRQ 0x1c -#define NJ_DMA_WRITE_END 0x20 -#define NJ_DMA_WRITE_ADR 0x24 -#define NJ_PULSE_CNT 0x28 - -#define NJ_ISAC_OFF 0xc0 -#define NJ_ISACIRQ 0x10 - -#define NJ_IRQM0_RD_MASK 0x03 -#define NJ_IRQM0_RD_IRQ 0x01 -#define NJ_IRQM0_RD_END 0x02 -#define NJ_IRQM0_WR_MASK 0x0c -#define NJ_IRQM0_WR_IRQ 0x04 -#define NJ_IRQM0_WR_END 0x08 - -/* one page here is no need to be smaller */ -#define NJ_DMA_SIZE 4096 -/* 2 * 64 byte is a compromise between IRQ count and latency */ -#define NJ_DMA_RXSIZE 128 /* 2 * 64 */ -#define NJ_DMA_TXSIZE 128 /* 2 * 64 */ diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c deleted file mode 100644 index ab24c3c460e6..000000000000 --- a/drivers/isdn/hardware/mISDN/speedfax.c +++ /dev/null @@ -1,520 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * speedfax.c low level stuff for Sedlbauer Speedfax+ cards - * based on the ISAR DSP - * Thanks to Sedlbauer AG for informations and HW - * - * Author Karsten Keil - * - * Copyright 2009 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include -#include -#include "ipac.h" -#include "isar.h" - -#define SPEEDFAX_REV "2.0" - -#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID 0x51 -#define PCI_SUBVENDOR_SPEEDFAX_PCI 0x54 -#define PCI_SUB_ID_SEDLBAUER 0x01 - -#define SFAX_PCI_ADDR 0xc8 -#define SFAX_PCI_ISAC 0xd0 -#define SFAX_PCI_ISAR 0xe0 - -/* TIGER 100 Registers */ - -#define TIGER_RESET_ADDR 0x00 -#define TIGER_EXTERN_RESET_ON 0x01 -#define TIGER_EXTERN_RESET_OFF 0x00 -#define TIGER_AUX_CTRL 0x02 -#define TIGER_AUX_DATA 0x03 -#define TIGER_AUX_IRQMASK 0x05 -#define TIGER_AUX_STATUS 0x07 - -/* Tiger AUX BITs */ -#define SFAX_AUX_IOMASK 0xdd /* 1 and 5 are inputs */ -#define SFAX_ISAR_RESET_BIT_OFF 0x00 -#define SFAX_ISAR_RESET_BIT_ON 0x01 -#define SFAX_TIGER_IRQ_BIT 0x02 -#define SFAX_LED1_BIT 0x08 -#define SFAX_LED2_BIT 0x10 - -#define SFAX_PCI_RESET_ON (SFAX_ISAR_RESET_BIT_ON) -#define SFAX_PCI_RESET_OFF (SFAX_LED1_BIT | SFAX_LED2_BIT) - -static int sfax_cnt; -static u32 debug; -static u32 irqloops = 4; - -struct sfax_hw { - struct list_head list; - struct pci_dev *pdev; - char name[MISDN_MAX_IDLEN]; - u32 irq; - u32 irqcnt; - u32 cfg; - struct _ioport p_isac; - struct _ioport p_isar; - u8 aux_data; - spinlock_t lock; /* HW access lock */ - struct isac_hw isac; - struct isar_hw isar; -}; - -static LIST_HEAD(Cards); -static DEFINE_RWLOCK(card_lock); /* protect Cards */ - -static void -_set_debug(struct sfax_hw *card) -{ - card->isac.dch.debug = debug; - card->isar.ch[0].bch.debug = debug; - card->isar.ch[1].bch.debug = debug; -} - -static int -set_debug(const char *val, const struct kernel_param *kp) -{ - int ret; - struct sfax_hw *card; - - ret = param_set_uint(val, kp); - if (!ret) { - read_lock(&card_lock); - list_for_each_entry(card, &Cards, list) - _set_debug(card); - read_unlock(&card_lock); - } - return ret; -} - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for Sedlbauer Speedfax+ cards"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(SPEEDFAX_REV); -MODULE_FIRMWARE("isdn/ISAR.BIN"); -module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Speedfax debug mask"); -module_param(irqloops, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(irqloops, "Speedfax maximal irqloops (default 4)"); - -IOFUNC_IND(ISAC, sfax_hw, p_isac) -IOFUNC_IND(ISAR, sfax_hw, p_isar) - -static irqreturn_t -speedfax_irq(int intno, void *dev_id) -{ - struct sfax_hw *sf = dev_id; - u8 val; - int cnt = irqloops; - - spin_lock(&sf->lock); - val = inb(sf->cfg + TIGER_AUX_STATUS); - if (val & SFAX_TIGER_IRQ_BIT) { /* for us or shared ? */ - spin_unlock(&sf->lock); - return IRQ_NONE; /* shared */ - } - sf->irqcnt++; - val = ReadISAR_IND(sf, ISAR_IRQBIT); -Start_ISAR: - if (val & ISAR_IRQSTA) - mISDNisar_irq(&sf->isar); - val = ReadISAC_IND(sf, ISAC_ISTA); - if (val) - mISDNisac_irq(&sf->isac, val); - val = ReadISAR_IND(sf, ISAR_IRQBIT); - if ((val & ISAR_IRQSTA) && cnt--) - goto Start_ISAR; - if (cnt < irqloops) - pr_debug("%s: %d irqloops cpu%d\n", sf->name, - irqloops - cnt, smp_processor_id()); - if (irqloops && !cnt) - pr_notice("%s: %d IRQ LOOP cpu%d\n", sf->name, - irqloops, smp_processor_id()); - spin_unlock(&sf->lock); - return IRQ_HANDLED; -} - -static void -enable_hwirq(struct sfax_hw *sf) -{ - WriteISAC_IND(sf, ISAC_MASK, 0); - WriteISAR_IND(sf, ISAR_IRQBIT, ISAR_IRQMSK); - outb(SFAX_TIGER_IRQ_BIT, sf->cfg + TIGER_AUX_IRQMASK); -} - -static void -disable_hwirq(struct sfax_hw *sf) -{ - WriteISAC_IND(sf, ISAC_MASK, 0xFF); - WriteISAR_IND(sf, ISAR_IRQBIT, 0); - outb(0, sf->cfg + TIGER_AUX_IRQMASK); -} - -static void -reset_speedfax(struct sfax_hw *sf) -{ - - pr_debug("%s: resetting card\n", sf->name); - outb(TIGER_EXTERN_RESET_ON, sf->cfg + TIGER_RESET_ADDR); - outb(SFAX_PCI_RESET_ON, sf->cfg + TIGER_AUX_DATA); - mdelay(1); - outb(TIGER_EXTERN_RESET_OFF, sf->cfg + TIGER_RESET_ADDR); - sf->aux_data = SFAX_PCI_RESET_OFF; - outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA); - mdelay(1); -} - -static int -sfax_ctrl(struct sfax_hw *sf, u32 cmd, u_long arg) -{ - int ret = 0; - - switch (cmd) { - case HW_RESET_REQ: - reset_speedfax(sf); - break; - case HW_ACTIVATE_IND: - if (arg & 1) - sf->aux_data &= ~SFAX_LED1_BIT; - if (arg & 2) - sf->aux_data &= ~SFAX_LED2_BIT; - outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA); - break; - case HW_DEACT_IND: - if (arg & 1) - sf->aux_data |= SFAX_LED1_BIT; - if (arg & 2) - sf->aux_data |= SFAX_LED2_BIT; - outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA); - break; - default: - pr_info("%s: %s unknown command %x %lx\n", - sf->name, __func__, cmd, arg); - ret = -EINVAL; - break; - } - return ret; -} - -static int -channel_ctrl(struct sfax_hw *sf, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_LOOP: - /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ - if (cq->channel < 0 || cq->channel > 3) { - ret = -EINVAL; - break; - } - ret = sf->isac.ctrl(&sf->isac, HW_TESTLOOP, cq->channel); - break; - case MISDN_CTRL_L1_TIMER3: - ret = sf->isac.ctrl(&sf->isac, HW_TIMER3_VALUE, cq->p1); - break; - default: - pr_info("%s: unknown Op %x\n", sf->name, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -sfax_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct sfax_hw *sf = dch->hw; - struct channel_req *rq; - int err = 0; - - pr_debug("%s: cmd:%x %p\n", sf->name, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if (rq->protocol == ISDN_P_TE_S0) - err = sf->isac.open(&sf->isac, rq); - else - err = sf->isar.open(&sf->isar, rq); - if (err) - break; - if (!try_module_get(THIS_MODULE)) - pr_info("%s: cannot get module\n", sf->name); - break; - case CLOSE_CHANNEL: - pr_debug("%s: dev(%d) close from %p\n", sf->name, - dch->dev.id, __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(sf, arg); - break; - default: - pr_debug("%s: unknown command %x\n", sf->name, cmd); - return -EINVAL; - } - return err; -} - -static int -init_card(struct sfax_hw *sf) -{ - int ret, cnt = 3; - u_long flags; - - ret = request_irq(sf->irq, speedfax_irq, IRQF_SHARED, sf->name, sf); - if (ret) { - pr_info("%s: couldn't get interrupt %d\n", sf->name, sf->irq); - return ret; - } - while (cnt--) { - spin_lock_irqsave(&sf->lock, flags); - ret = sf->isac.init(&sf->isac); - if (ret) { - spin_unlock_irqrestore(&sf->lock, flags); - pr_info("%s: ISAC init failed with %d\n", - sf->name, ret); - break; - } - enable_hwirq(sf); - /* RESET Receiver and Transmitter */ - WriteISAC_IND(sf, ISAC_CMDR, 0x41); - spin_unlock_irqrestore(&sf->lock, flags); - msleep_interruptible(10); - if (debug & DEBUG_HW) - pr_notice("%s: IRQ %d count %d\n", sf->name, - sf->irq, sf->irqcnt); - if (!sf->irqcnt) { - pr_info("%s: IRQ(%d) got no requests during init %d\n", - sf->name, sf->irq, 3 - cnt); - } else - return 0; - } - free_irq(sf->irq, sf); - return -EIO; -} - - -static int -setup_speedfax(struct sfax_hw *sf) -{ - u_long flags; - - if (!request_region(sf->cfg, 256, sf->name)) { - pr_info("mISDN: %s config port %x-%x already in use\n", - sf->name, sf->cfg, sf->cfg + 255); - return -EIO; - } - outb(0xff, sf->cfg); - outb(0, sf->cfg); - outb(0xdd, sf->cfg + TIGER_AUX_CTRL); - outb(0, sf->cfg + TIGER_AUX_IRQMASK); - - sf->isac.type = IPAC_TYPE_ISAC; - sf->p_isac.ale = sf->cfg + SFAX_PCI_ADDR; - sf->p_isac.port = sf->cfg + SFAX_PCI_ISAC; - sf->p_isar.ale = sf->cfg + SFAX_PCI_ADDR; - sf->p_isar.port = sf->cfg + SFAX_PCI_ISAR; - ASSIGN_FUNC(IND, ISAC, sf->isac); - ASSIGN_FUNC(IND, ISAR, sf->isar); - spin_lock_irqsave(&sf->lock, flags); - reset_speedfax(sf); - disable_hwirq(sf); - spin_unlock_irqrestore(&sf->lock, flags); - return 0; -} - -static void -release_card(struct sfax_hw *card) { - u_long flags; - - spin_lock_irqsave(&card->lock, flags); - disable_hwirq(card); - spin_unlock_irqrestore(&card->lock, flags); - card->isac.release(&card->isac); - free_irq(card->irq, card); - card->isar.release(&card->isar); - mISDN_unregister_device(&card->isac.dch.dev); - release_region(card->cfg, 256); - pci_disable_device(card->pdev); - pci_set_drvdata(card->pdev, NULL); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - kfree(card); - sfax_cnt--; -} - -static int -setup_instance(struct sfax_hw *card) -{ - const struct firmware *firmware; - int i, err; - u_long flags; - - snprintf(card->name, MISDN_MAX_IDLEN - 1, "Speedfax.%d", sfax_cnt + 1); - write_lock_irqsave(&card_lock, flags); - list_add_tail(&card->list, &Cards); - write_unlock_irqrestore(&card_lock, flags); - _set_debug(card); - spin_lock_init(&card->lock); - card->isac.hwlock = &card->lock; - card->isar.hwlock = &card->lock; - card->isar.ctrl = (void *)&sfax_ctrl; - card->isac.name = card->name; - card->isar.name = card->name; - card->isar.owner = THIS_MODULE; - - err = request_firmware(&firmware, "isdn/ISAR.BIN", &card->pdev->dev); - if (err < 0) { - pr_info("%s: firmware request failed %d\n", - card->name, err); - goto error_fw; - } - if (debug & DEBUG_HW) - pr_notice("%s: got firmware %zu bytes\n", - card->name, firmware->size); - - mISDNisac_init(&card->isac, card); - - card->isac.dch.dev.D.ctrl = sfax_dctrl; - card->isac.dch.dev.Bprotocols = - mISDNisar_init(&card->isar, card); - for (i = 0; i < 2; i++) { - set_channelmap(i + 1, card->isac.dch.dev.channelmap); - list_add(&card->isar.ch[i].bch.ch.list, - &card->isac.dch.dev.bchannels); - } - - err = setup_speedfax(card); - if (err) - goto error_setup; - err = card->isar.init(&card->isar); - if (err) - goto error; - err = mISDN_register_device(&card->isac.dch.dev, - &card->pdev->dev, card->name); - if (err) - goto error; - err = init_card(card); - if (err) - goto error_init; - err = card->isar.firmware(&card->isar, firmware->data, firmware->size); - if (!err) { - release_firmware(firmware); - sfax_cnt++; - pr_notice("SpeedFax %d cards installed\n", sfax_cnt); - return 0; - } - disable_hwirq(card); - free_irq(card->irq, card); -error_init: - mISDN_unregister_device(&card->isac.dch.dev); -error: - release_region(card->cfg, 256); -error_setup: - card->isac.release(&card->isac); - card->isar.release(&card->isar); - release_firmware(firmware); -error_fw: - pci_disable_device(card->pdev); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - kfree(card); - return err; -} - -static int -sfaxpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -ENOMEM; - struct sfax_hw *card = kzalloc_obj(struct sfax_hw); - - if (!card) { - pr_info("No memory for Speedfax+ PCI\n"); - return err; - } - card->pdev = pdev; - err = pci_enable_device(pdev); - if (err) { - kfree(card); - return err; - } - - pr_notice("mISDN: Speedfax found adapter %s at %s\n", - (char *)ent->driver_data, pci_name(pdev)); - - card->cfg = pci_resource_start(pdev, 0); - card->irq = pdev->irq; - pci_set_drvdata(pdev, card); - err = setup_instance(card); - if (err) - pci_set_drvdata(pdev, NULL); - return err; -} - -static void -sfax_remove_pci(struct pci_dev *pdev) -{ - struct sfax_hw *card = pci_get_drvdata(pdev); - - if (card) - release_card(card); - else - pr_debug("%s: drvdata already removed\n", __func__); -} - -static struct pci_device_id sfaxpci_ids[] = { - { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, - PCI_SUBVENDOR_SPEEDFAX_PYRAMID, PCI_SUB_ID_SEDLBAUER, - 0, 0, (unsigned long) "Pyramid Speedfax + PCI" - }, - { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, - PCI_SUBVENDOR_SPEEDFAX_PCI, PCI_SUB_ID_SEDLBAUER, - 0, 0, (unsigned long) "Sedlbauer Speedfax + PCI" - }, - { } -}; -MODULE_DEVICE_TABLE(pci, sfaxpci_ids); - -static struct pci_driver sfaxpci_driver = { - .name = "speedfax+ pci", - .probe = sfaxpci_probe, - .remove = sfax_remove_pci, - .id_table = sfaxpci_ids, -}; - -static int __init -Speedfax_init(void) -{ - int err; - - pr_notice("Sedlbauer Speedfax+ Driver Rev. %s\n", - SPEEDFAX_REV); - err = pci_register_driver(&sfaxpci_driver); - return err; -} - -static void __exit -Speedfax_cleanup(void) -{ - pci_unregister_driver(&sfaxpci_driver); -} - -module_init(Speedfax_init); -module_exit(Speedfax_cleanup); diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c deleted file mode 100644 index a341470c042f..000000000000 --- a/drivers/isdn/hardware/mISDN/w6692.c +++ /dev/null @@ -1,1417 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * w6692.c mISDN driver for Winbond w6692 based cards - * - * Author Karsten Keil - * based on the w6692 I4L driver from Petr Novak - * - * Copyright 2009 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include -#include "w6692.h" - -#define W6692_REV "2.0" - -#define DBUSY_TIMER_VALUE 80 - -enum { - W6692_ASUS, - W6692_WINBOND, - W6692_USR -}; - -/* private data in the PCI devices list */ -struct w6692map { - u_int subtype; - char *name; -}; - -static const struct w6692map w6692_map[] = -{ - {W6692_ASUS, "Dynalink/AsusCom IS64PH"}, - {W6692_WINBOND, "Winbond W6692"}, - {W6692_USR, "USR W6692"} -}; - -#define PCI_DEVICE_ID_USR_6692 0x3409 - -struct w6692_ch { - struct bchannel bch; - u32 addr; - struct timer_list timer; - u8 b_mode; -}; - -struct w6692_hw { - struct list_head list; - struct pci_dev *pdev; - char name[MISDN_MAX_IDLEN]; - u32 irq; - u32 irqcnt; - u32 addr; - u32 fmask; /* feature mask - bit set per card nr */ - int subtype; - spinlock_t lock; /* hw lock */ - u8 imask; - u8 pctl; - u8 xaddr; - u8 xdata; - u8 state; - struct w6692_ch bc[2]; - struct dchannel dch; - char log[64]; -}; - -static LIST_HEAD(Cards); -static DEFINE_RWLOCK(card_lock); /* protect Cards */ - -static int w6692_cnt; -static int debug; -static u32 led; -static u32 pots; - -static void -_set_debug(struct w6692_hw *card) -{ - card->dch.debug = debug; - card->bc[0].bch.debug = debug; - card->bc[1].bch.debug = debug; -} - -static int -set_debug(const char *val, const struct kernel_param *kp) -{ - int ret; - struct w6692_hw *card; - - ret = param_set_uint(val, kp); - if (!ret) { - read_lock(&card_lock); - list_for_each_entry(card, &Cards, list) - _set_debug(card); - read_unlock(&card_lock); - } - return ret; -} - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("mISDN driver for Winbond w6692 based cards"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(W6692_REV); -module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "W6692 debug mask"); -module_param(led, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(led, "W6692 LED support bitmask (one bit per card)"); -module_param(pots, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(pots, "W6692 POTS support bitmask (one bit per card)"); - -static inline u8 -ReadW6692(struct w6692_hw *card, u8 offset) -{ - return inb(card->addr + offset); -} - -static inline void -WriteW6692(struct w6692_hw *card, u8 offset, u8 value) -{ - outb(value, card->addr + offset); -} - -static inline u8 -ReadW6692B(struct w6692_ch *bc, u8 offset) -{ - return inb(bc->addr + offset); -} - -static inline void -WriteW6692B(struct w6692_ch *bc, u8 offset, u8 value) -{ - outb(value, bc->addr + offset); -} - -static void -enable_hwirq(struct w6692_hw *card) -{ - WriteW6692(card, W_IMASK, card->imask); -} - -static void -disable_hwirq(struct w6692_hw *card) -{ - WriteW6692(card, W_IMASK, 0xff); -} - -static const char *W6692Ver[] = {"V00", "V01", "V10", "V11"}; - -static void -W6692Version(struct w6692_hw *card) -{ - int val; - - val = ReadW6692(card, W_D_RBCH); - pr_notice("%s: Winbond W6692 version: %s\n", card->name, - W6692Ver[(val >> 6) & 3]); -} - -static void -w6692_led_handler(struct w6692_hw *card, int on) -{ - if ((!(card->fmask & led)) || card->subtype == W6692_USR) - return; - if (on) { - card->xdata &= 0xfb; /* LED ON */ - WriteW6692(card, W_XDATA, card->xdata); - } else { - card->xdata |= 0x04; /* LED OFF */ - WriteW6692(card, W_XDATA, card->xdata); - } -} - -static void -ph_command(struct w6692_hw *card, u8 cmd) -{ - pr_debug("%s: ph_command %x\n", card->name, cmd); - WriteW6692(card, W_CIX, cmd); -} - -static void -W6692_new_ph(struct w6692_hw *card) -{ - if (card->state == W_L1CMD_RST) - ph_command(card, W_L1CMD_DRC); - schedule_event(&card->dch, FLG_PHCHANGE); -} - -static void -W6692_ph_bh(struct dchannel *dch) -{ - struct w6692_hw *card = dch->hw; - - switch (card->state) { - case W_L1CMD_RST: - dch->state = 0; - l1_event(dch->l1, HW_RESET_IND); - break; - case W_L1IND_CD: - dch->state = 3; - l1_event(dch->l1, HW_DEACT_CNF); - break; - case W_L1IND_DRD: - dch->state = 3; - l1_event(dch->l1, HW_DEACT_IND); - break; - case W_L1IND_CE: - dch->state = 4; - l1_event(dch->l1, HW_POWERUP_IND); - break; - case W_L1IND_LD: - if (dch->state <= 5) { - dch->state = 5; - l1_event(dch->l1, ANYSIGNAL); - } else { - dch->state = 8; - l1_event(dch->l1, LOSTFRAMING); - } - break; - case W_L1IND_ARD: - dch->state = 6; - l1_event(dch->l1, INFO2); - break; - case W_L1IND_AI8: - dch->state = 7; - l1_event(dch->l1, INFO4_P8); - break; - case W_L1IND_AI10: - dch->state = 7; - l1_event(dch->l1, INFO4_P10); - break; - default: - pr_debug("%s: TE unknown state %02x dch state %02x\n", - card->name, card->state, dch->state); - break; - } - pr_debug("%s: TE newstate %02x\n", card->name, dch->state); -} - -static void -W6692_empty_Dfifo(struct w6692_hw *card, int count) -{ - struct dchannel *dch = &card->dch; - u8 *ptr; - - pr_debug("%s: empty_Dfifo %d\n", card->name, count); - if (!dch->rx_skb) { - dch->rx_skb = mI_alloc_skb(card->dch.maxlen, GFP_ATOMIC); - if (!dch->rx_skb) { - pr_info("%s: D receive out of memory\n", card->name); - WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); - return; - } - } - if ((dch->rx_skb->len + count) >= dch->maxlen) { - pr_debug("%s: empty_Dfifo overrun %d\n", card->name, - dch->rx_skb->len + count); - WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); - return; - } - ptr = skb_put(dch->rx_skb, count); - insb(card->addr + W_D_RFIFO, ptr, count); - WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); - if (debug & DEBUG_HW_DFIFO) { - snprintf(card->log, 63, "D-recv %s %d ", - card->name, count); - print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); - } -} - -static void -W6692_fill_Dfifo(struct w6692_hw *card) -{ - struct dchannel *dch = &card->dch; - int count; - u8 *ptr; - u8 cmd = W_D_CMDR_XMS; - - pr_debug("%s: fill_Dfifo\n", card->name); - if (!dch->tx_skb) - return; - count = dch->tx_skb->len - dch->tx_idx; - if (count <= 0) - return; - if (count > W_D_FIFO_THRESH) - count = W_D_FIFO_THRESH; - else - cmd |= W_D_CMDR_XME; - ptr = dch->tx_skb->data + dch->tx_idx; - dch->tx_idx += count; - outsb(card->addr + W_D_XFIFO, ptr, count); - WriteW6692(card, W_D_CMDR, cmd); - if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) { - pr_debug("%s: fill_Dfifo dbusytimer running\n", card->name); - timer_delete(&dch->timer); - } - dch->timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000); - add_timer(&dch->timer); - if (debug & DEBUG_HW_DFIFO) { - snprintf(card->log, 63, "D-send %s %d ", - card->name, count); - print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); - } -} - -static void -d_retransmit(struct w6692_hw *card) -{ - struct dchannel *dch = &card->dch; - - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); -#ifdef FIXME - if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) - dchannel_sched_event(dch, D_CLEARBUSY); -#endif - if (test_bit(FLG_TX_BUSY, &dch->Flags)) { - /* Restart frame */ - dch->tx_idx = 0; - W6692_fill_Dfifo(card); - } else if (dch->tx_skb) { /* should not happen */ - pr_info("%s: %s without TX_BUSY\n", card->name, __func__); - test_and_set_bit(FLG_TX_BUSY, &dch->Flags); - dch->tx_idx = 0; - W6692_fill_Dfifo(card); - } else { - pr_info("%s: XDU no TX_BUSY\n", card->name); - if (get_next_dframe(dch)) - W6692_fill_Dfifo(card); - } -} - -static void -handle_rxD(struct w6692_hw *card) { - u8 stat; - int count; - - stat = ReadW6692(card, W_D_RSTA); - if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) { - if (stat & W_D_RSTA_RDOV) { - pr_debug("%s: D-channel RDOV\n", card->name); -#ifdef ERROR_STATISTIC - card->dch.err_rx++; -#endif - } - if (stat & W_D_RSTA_CRCE) { - pr_debug("%s: D-channel CRC error\n", card->name); -#ifdef ERROR_STATISTIC - card->dch.err_crc++; -#endif - } - if (stat & W_D_RSTA_RMB) { - pr_debug("%s: D-channel ABORT\n", card->name); -#ifdef ERROR_STATISTIC - card->dch.err_rx++; -#endif - } - dev_kfree_skb(card->dch.rx_skb); - card->dch.rx_skb = NULL; - WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST); - } else { - count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1); - if (count == 0) - count = W_D_FIFO_THRESH; - W6692_empty_Dfifo(card, count); - recv_Dchannel(&card->dch); - } -} - -static void -handle_txD(struct w6692_hw *card) { - if (test_and_clear_bit(FLG_BUSY_TIMER, &card->dch.Flags)) - timer_delete(&card->dch.timer); - if (card->dch.tx_skb && card->dch.tx_idx < card->dch.tx_skb->len) { - W6692_fill_Dfifo(card); - } else { - dev_kfree_skb(card->dch.tx_skb); - if (get_next_dframe(&card->dch)) - W6692_fill_Dfifo(card); - } -} - -static void -handle_statusD(struct w6692_hw *card) -{ - struct dchannel *dch = &card->dch; - u8 exval, v1, cir; - - exval = ReadW6692(card, W_D_EXIR); - - pr_debug("%s: D_EXIR %02x\n", card->name, exval); - if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) { - /* Transmit underrun/collision */ - pr_debug("%s: D-channel underrun/collision\n", card->name); -#ifdef ERROR_STATISTIC - dch->err_tx++; -#endif - d_retransmit(card); - } - if (exval & W_D_EXI_RDOV) { /* RDOV */ - pr_debug("%s: D-channel RDOV\n", card->name); - WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST); - } - if (exval & W_D_EXI_TIN2) /* TIN2 - never */ - pr_debug("%s: spurious TIN2 interrupt\n", card->name); - if (exval & W_D_EXI_MOC) { /* MOC - not supported */ - v1 = ReadW6692(card, W_MOSR); - pr_debug("%s: spurious MOC interrupt MOSR %02x\n", - card->name, v1); - } - if (exval & W_D_EXI_ISC) { /* ISC - Level1 change */ - cir = ReadW6692(card, W_CIR); - pr_debug("%s: ISC CIR %02X\n", card->name, cir); - if (cir & W_CIR_ICC) { - v1 = cir & W_CIR_COD_MASK; - pr_debug("%s: ph_state_change %x -> %x\n", card->name, - dch->state, v1); - card->state = v1; - if (card->fmask & led) { - switch (v1) { - case W_L1IND_AI8: - case W_L1IND_AI10: - w6692_led_handler(card, 1); - break; - default: - w6692_led_handler(card, 0); - break; - } - } - W6692_new_ph(card); - } - if (cir & W_CIR_SCC) { - v1 = ReadW6692(card, W_SQR); - pr_debug("%s: SCC SQR %02X\n", card->name, v1); - } - } - if (exval & W_D_EXI_WEXP) - pr_debug("%s: spurious WEXP interrupt!\n", card->name); - if (exval & W_D_EXI_TEXP) - pr_debug("%s: spurious TEXP interrupt!\n", card->name); -} - -static void -W6692_empty_Bfifo(struct w6692_ch *wch, int count) -{ - struct w6692_hw *card = wch->bch.hw; - u8 *ptr; - int maxlen; - - pr_debug("%s: empty_Bfifo %d\n", card->name, count); - if (unlikely(wch->bch.state == ISDN_P_NONE)) { - pr_debug("%s: empty_Bfifo ISDN_P_NONE\n", card->name); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); - if (wch->bch.rx_skb) - skb_trim(wch->bch.rx_skb, 0); - return; - } - if (test_bit(FLG_RX_OFF, &wch->bch.Flags)) { - wch->bch.dropcnt += count; - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); - return; - } - maxlen = bchannel_get_rxbuf(&wch->bch, count); - if (maxlen < 0) { - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); - if (wch->bch.rx_skb) - skb_trim(wch->bch.rx_skb, 0); - pr_warn("%s.B%d: No bufferspace for %d bytes\n", - card->name, wch->bch.nr, count); - return; - } - ptr = skb_put(wch->bch.rx_skb, count); - insb(wch->addr + W_B_RFIFO, ptr, count); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); - if (debug & DEBUG_HW_DFIFO) { - snprintf(card->log, 63, "B%1d-recv %s %d ", - wch->bch.nr, card->name, count); - print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); - } -} - -static void -W6692_fill_Bfifo(struct w6692_ch *wch) -{ - struct w6692_hw *card = wch->bch.hw; - int count, fillempty = 0; - u8 *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS; - - pr_debug("%s: fill Bfifo\n", card->name); - if (!wch->bch.tx_skb) { - if (!test_bit(FLG_TX_EMPTY, &wch->bch.Flags)) - return; - ptr = wch->bch.fill; - count = W_B_FIFO_THRESH; - fillempty = 1; - } else { - count = wch->bch.tx_skb->len - wch->bch.tx_idx; - if (count <= 0) - return; - ptr = wch->bch.tx_skb->data + wch->bch.tx_idx; - } - if (count > W_B_FIFO_THRESH) - count = W_B_FIFO_THRESH; - else if (test_bit(FLG_HDLC, &wch->bch.Flags)) - cmd |= W_B_CMDR_XME; - - pr_debug("%s: fill Bfifo%d/%d\n", card->name, - count, wch->bch.tx_idx); - wch->bch.tx_idx += count; - if (fillempty) { - while (count > 0) { - outsb(wch->addr + W_B_XFIFO, ptr, MISDN_BCH_FILL_SIZE); - count -= MISDN_BCH_FILL_SIZE; - } - } else { - outsb(wch->addr + W_B_XFIFO, ptr, count); - } - WriteW6692B(wch, W_B_CMDR, cmd); - if ((debug & DEBUG_HW_BFIFO) && !fillempty) { - snprintf(card->log, 63, "B%1d-send %s %d ", - wch->bch.nr, card->name, count); - print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count); - } -} - -#if 0 -static int -setvolume(struct w6692_ch *wch, int mic, struct sk_buff *skb) -{ - struct w6692_hw *card = wch->bch.hw; - u16 *vol = (u16 *)skb->data; - u8 val; - - if ((!(card->fmask & pots)) || - !test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) - return -ENODEV; - if (skb->len < 2) - return -EINVAL; - if (*vol > 7) - return -EINVAL; - val = *vol & 7; - val = 7 - val; - if (mic) { - val <<= 3; - card->xaddr &= 0xc7; - } else { - card->xaddr &= 0xf8; - } - card->xaddr |= val; - WriteW6692(card, W_XADDR, card->xaddr); - return 0; -} - -static int -enable_pots(struct w6692_ch *wch) -{ - struct w6692_hw *card = wch->bch.hw; - - if ((!(card->fmask & pots)) || - !test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) - return -ENODEV; - wch->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0; - WriteW6692B(wch, W_B_MODE, wch->b_mode); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); - card->pctl |= ((wch->bch.nr & 2) ? W_PCTL_PCX : 0); - WriteW6692(card, W_PCTL, card->pctl); - return 0; -} -#endif - -static int -disable_pots(struct w6692_ch *wch) -{ - struct w6692_hw *card = wch->bch.hw; - - if (!(card->fmask & pots)) - return -ENODEV; - wch->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0); - WriteW6692B(wch, W_B_MODE, wch->b_mode); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | - W_B_CMDR_XRST); - return 0; -} - -static int -w6692_mode(struct w6692_ch *wch, u32 pr) -{ - struct w6692_hw *card; - - card = wch->bch.hw; - pr_debug("%s: B%d protocol %x-->%x\n", card->name, - wch->bch.nr, wch->bch.state, pr); - switch (pr) { - case ISDN_P_NONE: - if ((card->fmask & pots) && (wch->b_mode & W_B_MODE_EPCM)) - disable_pots(wch); - wch->b_mode = 0; - mISDN_clear_bchannel(&wch->bch); - WriteW6692B(wch, W_B_MODE, wch->b_mode); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); - test_and_clear_bit(FLG_HDLC, &wch->bch.Flags); - test_and_clear_bit(FLG_TRANSPARENT, &wch->bch.Flags); - break; - case ISDN_P_B_RAW: - wch->b_mode = W_B_MODE_MMS; - WriteW6692B(wch, W_B_MODE, wch->b_mode); - WriteW6692B(wch, W_B_EXIM, 0); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | - W_B_CMDR_XRST); - test_and_set_bit(FLG_TRANSPARENT, &wch->bch.Flags); - break; - case ISDN_P_B_HDLC: - wch->b_mode = W_B_MODE_ITF; - WriteW6692B(wch, W_B_MODE, wch->b_mode); - WriteW6692B(wch, W_B_ADM1, 0xff); - WriteW6692B(wch, W_B_ADM2, 0xff); - WriteW6692B(wch, W_B_EXIM, 0); - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | - W_B_CMDR_XRST); - test_and_set_bit(FLG_HDLC, &wch->bch.Flags); - break; - default: - pr_info("%s: protocol %x not known\n", card->name, pr); - return -ENOPROTOOPT; - } - wch->bch.state = pr; - return 0; -} - -static void -send_next(struct w6692_ch *wch) -{ - if (wch->bch.tx_skb && wch->bch.tx_idx < wch->bch.tx_skb->len) { - W6692_fill_Bfifo(wch); - } else { - dev_kfree_skb(wch->bch.tx_skb); - if (get_next_bframe(&wch->bch)) { - W6692_fill_Bfifo(wch); - test_and_clear_bit(FLG_TX_EMPTY, &wch->bch.Flags); - } else if (test_bit(FLG_TX_EMPTY, &wch->bch.Flags)) { - W6692_fill_Bfifo(wch); - } - } -} - -static void -W6692B_interrupt(struct w6692_hw *card, int ch) -{ - struct w6692_ch *wch = &card->bc[ch]; - int count; - u8 stat, star = 0; - - stat = ReadW6692B(wch, W_B_EXIR); - pr_debug("%s: B%d EXIR %02x\n", card->name, wch->bch.nr, stat); - if (stat & W_B_EXI_RME) { - star = ReadW6692B(wch, W_B_STAR); - if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) { - if ((star & W_B_STAR_RDOV) && - test_bit(FLG_ACTIVE, &wch->bch.Flags)) { - pr_debug("%s: B%d RDOV proto=%x\n", card->name, - wch->bch.nr, wch->bch.state); -#ifdef ERROR_STATISTIC - wch->bch.err_rdo++; -#endif - } - if (test_bit(FLG_HDLC, &wch->bch.Flags)) { - if (star & W_B_STAR_CRCE) { - pr_debug("%s: B%d CRC error\n", - card->name, wch->bch.nr); -#ifdef ERROR_STATISTIC - wch->bch.err_crc++; -#endif - } - if (star & W_B_STAR_RMB) { - pr_debug("%s: B%d message abort\n", - card->name, wch->bch.nr); -#ifdef ERROR_STATISTIC - wch->bch.err_inv++; -#endif - } - } - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | - W_B_CMDR_RRST | W_B_CMDR_RACT); - if (wch->bch.rx_skb) - skb_trim(wch->bch.rx_skb, 0); - } else { - count = ReadW6692B(wch, W_B_RBCL) & - (W_B_FIFO_THRESH - 1); - if (count == 0) - count = W_B_FIFO_THRESH; - W6692_empty_Bfifo(wch, count); - recv_Bchannel(&wch->bch, 0, false); - } - } - if (stat & W_B_EXI_RMR) { - if (!(stat & W_B_EXI_RME)) - star = ReadW6692B(wch, W_B_STAR); - if (star & W_B_STAR_RDOV) { - pr_debug("%s: B%d RDOV proto=%x\n", card->name, - wch->bch.nr, wch->bch.state); -#ifdef ERROR_STATISTIC - wch->bch.err_rdo++; -#endif - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | - W_B_CMDR_RRST | W_B_CMDR_RACT); - } else { - W6692_empty_Bfifo(wch, W_B_FIFO_THRESH); - if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) - recv_Bchannel(&wch->bch, 0, false); - } - } - if (stat & W_B_EXI_RDOV) { - /* only if it is not handled yet */ - if (!(star & W_B_STAR_RDOV)) { - pr_debug("%s: B%d RDOV IRQ proto=%x\n", card->name, - wch->bch.nr, wch->bch.state); -#ifdef ERROR_STATISTIC - wch->bch.err_rdo++; -#endif - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | - W_B_CMDR_RRST | W_B_CMDR_RACT); - } - } - if (stat & W_B_EXI_XFR) { - if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) { - star = ReadW6692B(wch, W_B_STAR); - pr_debug("%s: B%d star %02x\n", card->name, - wch->bch.nr, star); - } - if (star & W_B_STAR_XDOW) { - pr_warn("%s: B%d XDOW proto=%x\n", card->name, - wch->bch.nr, wch->bch.state); -#ifdef ERROR_STATISTIC - wch->bch.err_xdu++; -#endif - WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST | - W_B_CMDR_RACT); - /* resend */ - if (wch->bch.tx_skb) { - if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) - wch->bch.tx_idx = 0; - } - } - send_next(wch); - if (star & W_B_STAR_XDOW) - return; /* handle XDOW only once */ - } - if (stat & W_B_EXI_XDUN) { - pr_warn("%s: B%d XDUN proto=%x\n", card->name, - wch->bch.nr, wch->bch.state); -#ifdef ERROR_STATISTIC - wch->bch.err_xdu++; -#endif - /* resend - no XRST needed */ - if (wch->bch.tx_skb) { - if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) - wch->bch.tx_idx = 0; - } else if (test_bit(FLG_FILLEMPTY, &wch->bch.Flags)) { - test_and_set_bit(FLG_TX_EMPTY, &wch->bch.Flags); - } - send_next(wch); - } -} - -static irqreturn_t -w6692_irq(int intno, void *dev_id) -{ - struct w6692_hw *card = dev_id; - u8 ista; - - spin_lock(&card->lock); - ista = ReadW6692(card, W_ISTA); - if ((ista | card->imask) == card->imask) { - /* possible a shared IRQ reqest */ - spin_unlock(&card->lock); - return IRQ_NONE; - } - card->irqcnt++; - pr_debug("%s: ista %02x\n", card->name, ista); - ista &= ~card->imask; - if (ista & W_INT_B1_EXI) - W6692B_interrupt(card, 0); - if (ista & W_INT_B2_EXI) - W6692B_interrupt(card, 1); - if (ista & W_INT_D_RME) - handle_rxD(card); - if (ista & W_INT_D_RMR) - W6692_empty_Dfifo(card, W_D_FIFO_THRESH); - if (ista & W_INT_D_XFR) - handle_txD(card); - if (ista & W_INT_D_EXI) - handle_statusD(card); - if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */ - pr_debug("%s: W6692 spurious XINT!\n", card->name); -/* End IRQ Handler */ - spin_unlock(&card->lock); - return IRQ_HANDLED; -} - -static void -dbusy_timer_handler(struct timer_list *t) -{ - struct dchannel *dch = timer_container_of(dch, t, timer); - struct w6692_hw *card = dch->hw; - int rbch, star; - u_long flags; - - if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) { - spin_lock_irqsave(&card->lock, flags); - rbch = ReadW6692(card, W_D_RBCH); - star = ReadW6692(card, W_D_STAR); - pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n", - card->name, rbch, star); - if (star & W_D_STAR_XBZ) /* D-Channel Busy */ - test_and_set_bit(FLG_L1_BUSY, &dch->Flags); - else { - /* discard frame; reset transceiver */ - test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags); - if (dch->tx_idx) - dch->tx_idx = 0; - else - pr_info("%s: W6692 D-Channel Busy no tx_idx\n", - card->name); - /* Transmitter reset */ - WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST); - } - spin_unlock_irqrestore(&card->lock, flags); - } -} - -static void initW6692(struct w6692_hw *card) -{ - u8 val; - - timer_setup(&card->dch.timer, dbusy_timer_handler, 0); - w6692_mode(&card->bc[0], ISDN_P_NONE); - w6692_mode(&card->bc[1], ISDN_P_NONE); - WriteW6692(card, W_D_CTL, 0x00); - disable_hwirq(card); - WriteW6692(card, W_D_SAM, 0xff); - WriteW6692(card, W_D_TAM, 0xff); - WriteW6692(card, W_D_MODE, W_D_MODE_RACT); - card->state = W_L1CMD_RST; - ph_command(card, W_L1CMD_RST); - ph_command(card, W_L1CMD_ECK); - /* enable all IRQ but extern */ - card->imask = 0x18; - WriteW6692(card, W_D_EXIM, 0x00); - WriteW6692B(&card->bc[0], W_B_EXIM, 0); - WriteW6692B(&card->bc[1], W_B_EXIM, 0); - /* Reset D-chan receiver and transmitter */ - WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST); - /* Reset B-chan receiver and transmitter */ - WriteW6692B(&card->bc[0], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); - WriteW6692B(&card->bc[1], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); - /* enable peripheral */ - if (card->subtype == W6692_USR) { - /* seems that USR implemented some power control features - * Pin 79 is connected to the oscilator circuit so we - * have to handle it here - */ - card->pctl = 0x80; - card->xdata = 0; - WriteW6692(card, W_PCTL, card->pctl); - WriteW6692(card, W_XDATA, card->xdata); - } else { - card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 | - W_PCTL_OE1 | W_PCTL_OE0; - card->xaddr = 0x00;/* all sw off */ - if (card->fmask & pots) - card->xdata |= 0x06; /* POWER UP/ LED OFF / ALAW */ - if (card->fmask & led) - card->xdata |= 0x04; /* LED OFF */ - if ((card->fmask & pots) || (card->fmask & led)) { - WriteW6692(card, W_PCTL, card->pctl); - WriteW6692(card, W_XADDR, card->xaddr); - WriteW6692(card, W_XDATA, card->xdata); - val = ReadW6692(card, W_XADDR); - if (debug & DEBUG_HW) - pr_notice("%s: W_XADDR=%02x\n", - card->name, val); - } - } -} - -static void -reset_w6692(struct w6692_hw *card) -{ - WriteW6692(card, W_D_CTL, W_D_CTL_SRST); - mdelay(10); - WriteW6692(card, W_D_CTL, 0); -} - -static int -init_card(struct w6692_hw *card) -{ - int cnt = 3; - u_long flags; - - spin_lock_irqsave(&card->lock, flags); - disable_hwirq(card); - spin_unlock_irqrestore(&card->lock, flags); - if (request_irq(card->irq, w6692_irq, IRQF_SHARED, card->name, card)) { - pr_info("%s: couldn't get interrupt %d\n", card->name, - card->irq); - return -EIO; - } - while (cnt--) { - spin_lock_irqsave(&card->lock, flags); - initW6692(card); - enable_hwirq(card); - spin_unlock_irqrestore(&card->lock, flags); - /* Timeout 10ms */ - msleep_interruptible(10); - if (debug & DEBUG_HW) - pr_notice("%s: IRQ %d count %d\n", card->name, - card->irq, card->irqcnt); - if (!card->irqcnt) { - pr_info("%s: IRQ(%d) getting no IRQs during init %d\n", - card->name, card->irq, 3 - cnt); - reset_w6692(card); - } else - return 0; - } - free_irq(card->irq, card); - return -EIO; -} - -static int -w6692_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch); - struct w6692_hw *card = bch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - unsigned long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&card->lock, flags); - ret = bchannel_senddata(bch, skb); - if (ret > 0) { /* direct TX */ - ret = 0; - W6692_fill_Bfifo(bc); - } - spin_unlock_irqrestore(&card->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - spin_lock_irqsave(&card->lock, flags); - if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) - ret = w6692_mode(bc, ch->protocol); - else - ret = 0; - spin_unlock_irqrestore(&card->lock, flags); - if (!ret) - _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - break; - case PH_DEACTIVATE_REQ: - spin_lock_irqsave(&card->lock, flags); - mISDN_clear_bchannel(bch); - w6692_mode(bc, ISDN_P_NONE); - spin_unlock_irqrestore(&card->lock, flags); - _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - ret = 0; - break; - default: - pr_info("%s: %s unknown prim(%x,%x)\n", - card->name, __func__, hh->prim, hh->id); - ret = -EINVAL; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - return mISDN_ctrl_bchannel(bch, cq); -} - -static int -open_bchannel(struct w6692_hw *card, struct channel_req *rq) -{ - struct bchannel *bch; - - if (rq->adr.channel == 0 || rq->adr.channel > 2) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - bch = &card->bc[rq->adr.channel - 1].bch; - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - return 0; -} - -static int -channel_ctrl(struct w6692_hw *card, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_L1_TIMER3; - break; - case MISDN_CTRL_L1_TIMER3: - ret = l1_event(card->dch.l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); - break; - default: - pr_info("%s: unknown CTRL OP %x\n", card->name, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -w6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch); - struct w6692_hw *card = bch->hw; - int ret = -EINVAL; - u_long flags; - - pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - cancel_work_sync(&bch->workq); - spin_lock_irqsave(&card->lock, flags); - mISDN_clear_bchannel(bch); - w6692_mode(bc, ISDN_P_NONE); - spin_unlock_irqrestore(&card->lock, flags); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - ret = 0; - break; - case CONTROL_CHANNEL: - ret = channel_bctrl(bch, arg); - break; - default: - pr_info("%s: %s unknown prim(%x)\n", - card->name, __func__, cmd); - } - return ret; -} - -static int -w6692_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - u32 id; - u_long flags; - - switch (hh->prim) { - case PH_DATA_REQ: - spin_lock_irqsave(&card->lock, flags); - ret = dchannel_senddata(dch, skb); - if (ret > 0) { /* direct TX */ - id = hh->id; /* skb can be freed */ - W6692_fill_Dfifo(card); - ret = 0; - spin_unlock_irqrestore(&card->lock, flags); - queue_ch_frame(ch, PH_DATA_CNF, id, NULL); - } else - spin_unlock_irqrestore(&card->lock, flags); - return ret; - case PH_ACTIVATE_REQ: - ret = l1_event(dch->l1, hh->prim); - break; - case PH_DEACTIVATE_REQ: - test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); - ret = l1_event(dch->l1, hh->prim); - break; - } - - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -w6692_l1callback(struct dchannel *dch, u32 cmd) -{ - struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); - u_long flags; - - pr_debug("%s: cmd(%x) state(%02x)\n", card->name, cmd, card->state); - switch (cmd) { - case INFO3_P8: - spin_lock_irqsave(&card->lock, flags); - ph_command(card, W_L1CMD_AR8); - spin_unlock_irqrestore(&card->lock, flags); - break; - case INFO3_P10: - spin_lock_irqsave(&card->lock, flags); - ph_command(card, W_L1CMD_AR10); - spin_unlock_irqrestore(&card->lock, flags); - break; - case HW_RESET_REQ: - spin_lock_irqsave(&card->lock, flags); - if (card->state != W_L1IND_DRD) - ph_command(card, W_L1CMD_RST); - ph_command(card, W_L1CMD_ECK); - spin_unlock_irqrestore(&card->lock, flags); - break; - case HW_DEACT_REQ: - skb_queue_purge(&dch->squeue); - if (dch->tx_skb) { - dev_kfree_skb(dch->tx_skb); - dch->tx_skb = NULL; - } - dch->tx_idx = 0; - if (dch->rx_skb) { - dev_kfree_skb(dch->rx_skb); - dch->rx_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) - timer_delete(&dch->timer); - break; - case HW_POWERUP_REQ: - spin_lock_irqsave(&card->lock, flags); - ph_command(card, W_L1CMD_ECK); - spin_unlock_irqrestore(&card->lock, flags); - break; - case PH_ACTIVATE_IND: - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); - break; - default: - pr_debug("%s: %s unknown command %x\n", card->name, - __func__, cmd); - return -1; - } - return 0; -} - -static int -open_dchannel(struct w6692_hw *card, struct channel_req *rq, void *caller) -{ - pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__, - card->dch.dev.id, caller); - if (rq->protocol != ISDN_P_TE_S0) - return -EINVAL; - if (rq->adr.channel == 1) - /* E-Channel not supported */ - return -EINVAL; - rq->ch = &card->dch.dev.D; - rq->ch->protocol = rq->protocol; - if (card->dch.state == 7) - _queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - return 0; -} - -static int -w6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct w6692_hw *card = container_of(dch, struct w6692_hw, dch); - struct channel_req *rq; - int err = 0; - - pr_debug("%s: DCTRL: %x %p\n", card->name, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - if (rq->protocol == ISDN_P_TE_S0) - err = open_dchannel(card, rq, __builtin_return_address(0)); - else - err = open_bchannel(card, rq); - if (err) - break; - if (!try_module_get(THIS_MODULE)) - pr_info("%s: cannot get module\n", card->name); - break; - case CLOSE_CHANNEL: - pr_debug("%s: dev(%d) close from %p\n", card->name, - dch->dev.id, __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_ctrl(card, arg); - break; - default: - pr_debug("%s: unknown DCTRL command %x\n", card->name, cmd); - return -EINVAL; - } - return err; -} - -static int -setup_w6692(struct w6692_hw *card) -{ - u32 val; - - if (!request_region(card->addr, 256, card->name)) { - pr_info("%s: config port %x-%x already in use\n", card->name, - card->addr, card->addr + 255); - return -EIO; - } - W6692Version(card); - card->bc[0].addr = card->addr; - card->bc[1].addr = card->addr + 0x40; - val = ReadW6692(card, W_ISTA); - if (debug & DEBUG_HW) - pr_notice("%s ISTA=%02x\n", card->name, val); - val = ReadW6692(card, W_IMASK); - if (debug & DEBUG_HW) - pr_notice("%s IMASK=%02x\n", card->name, val); - val = ReadW6692(card, W_D_EXIR); - if (debug & DEBUG_HW) - pr_notice("%s D_EXIR=%02x\n", card->name, val); - val = ReadW6692(card, W_D_EXIM); - if (debug & DEBUG_HW) - pr_notice("%s D_EXIM=%02x\n", card->name, val); - val = ReadW6692(card, W_D_RSTA); - if (debug & DEBUG_HW) - pr_notice("%s D_RSTA=%02x\n", card->name, val); - return 0; -} - -static void -release_card(struct w6692_hw *card) -{ - u_long flags; - - spin_lock_irqsave(&card->lock, flags); - disable_hwirq(card); - w6692_mode(&card->bc[0], ISDN_P_NONE); - w6692_mode(&card->bc[1], ISDN_P_NONE); - if ((card->fmask & led) || card->subtype == W6692_USR) { - card->xdata |= 0x04; /* LED OFF */ - WriteW6692(card, W_XDATA, card->xdata); - } - spin_unlock_irqrestore(&card->lock, flags); - free_irq(card->irq, card); - l1_event(card->dch.l1, CLOSE_CHANNEL); - mISDN_unregister_device(&card->dch.dev); - release_region(card->addr, 256); - mISDN_freebchannel(&card->bc[1].bch); - mISDN_freebchannel(&card->bc[0].bch); - mISDN_freedchannel(&card->dch); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - pci_disable_device(card->pdev); - pci_set_drvdata(card->pdev, NULL); - kfree(card); -} - -static int -setup_instance(struct w6692_hw *card) -{ - int i, err; - u_long flags; - - snprintf(card->name, MISDN_MAX_IDLEN - 1, "w6692.%d", w6692_cnt + 1); - write_lock_irqsave(&card_lock, flags); - list_add_tail(&card->list, &Cards); - write_unlock_irqrestore(&card_lock, flags); - card->fmask = (1 << w6692_cnt); - _set_debug(card); - spin_lock_init(&card->lock); - mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, W6692_ph_bh); - card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0); - card->dch.dev.D.send = w6692_l2l1D; - card->dch.dev.D.ctrl = w6692_dctrl; - card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - card->dch.hw = card; - card->dch.dev.nrbchan = 2; - for (i = 0; i < 2; i++) { - mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM, - W_B_FIFO_THRESH); - card->bc[i].bch.hw = card; - card->bc[i].bch.nr = i + 1; - card->bc[i].bch.ch.nr = i + 1; - card->bc[i].bch.ch.send = w6692_l2l1B; - card->bc[i].bch.ch.ctrl = w6692_bctrl; - set_channelmap(i + 1, card->dch.dev.channelmap); - list_add(&card->bc[i].bch.ch.list, &card->dch.dev.bchannels); - } - err = setup_w6692(card); - if (err) - goto error_setup; - err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, - card->name); - if (err) - goto error_reg; - err = init_card(card); - if (err) - goto error_init; - err = create_l1(&card->dch, w6692_l1callback); - if (!err) { - w6692_cnt++; - pr_notice("W6692 %d cards installed\n", w6692_cnt); - return 0; - } - - free_irq(card->irq, card); -error_init: - mISDN_unregister_device(&card->dch.dev); -error_reg: - release_region(card->addr, 256); -error_setup: - mISDN_freebchannel(&card->bc[1].bch); - mISDN_freebchannel(&card->bc[0].bch); - mISDN_freedchannel(&card->dch); - write_lock_irqsave(&card_lock, flags); - list_del(&card->list); - write_unlock_irqrestore(&card_lock, flags); - kfree(card); - return err; -} - -static int -w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = -ENOMEM; - struct w6692_hw *card; - struct w6692map *m = (struct w6692map *)ent->driver_data; - - card = kzalloc_obj(struct w6692_hw); - if (!card) { - pr_info("No kmem for w6692 card\n"); - return err; - } - card->pdev = pdev; - card->subtype = m->subtype; - err = pci_enable_device(pdev); - if (err) { - kfree(card); - return err; - } - - printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n", - m->name, pci_name(pdev)); - - card->addr = pci_resource_start(pdev, 1); - card->irq = pdev->irq; - pci_set_drvdata(pdev, card); - err = setup_instance(card); - if (err) - pci_set_drvdata(pdev, NULL); - return err; -} - -static void -w6692_remove_pci(struct pci_dev *pdev) -{ - struct w6692_hw *card = pci_get_drvdata(pdev); - - if (card) - release_card(card); - else - if (debug) - pr_notice("%s: drvdata already removed\n", __func__); -} - -static const struct pci_device_id w6692_ids[] = { - { PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[0]}, - { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, - PCI_VENDOR_ID_USR, PCI_DEVICE_ID_USR_6692, 0, 0, - (ulong)&w6692_map[2]}, - { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[1]}, - { } -}; -MODULE_DEVICE_TABLE(pci, w6692_ids); - -static struct pci_driver w6692_driver = { - .name = "w6692", - .probe = w6692_probe, - .remove = w6692_remove_pci, - .id_table = w6692_ids, -}; - -static int __init w6692_init(void) -{ - int err; - - pr_notice("Winbond W6692 PCI driver Rev. %s\n", W6692_REV); - - err = pci_register_driver(&w6692_driver); - return err; -} - -static void __exit w6692_cleanup(void) -{ - pci_unregister_driver(&w6692_driver); -} - -module_init(w6692_init); -module_exit(w6692_cleanup); diff --git a/drivers/isdn/hardware/mISDN/w6692.h b/drivers/isdn/hardware/mISDN/w6692.h deleted file mode 100644 index 45e1dc5d6c2d..000000000000 --- a/drivers/isdn/hardware/mISDN/w6692.h +++ /dev/null @@ -1,177 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Winbond W6692 specific defines - * - * Author Karsten Keil - * based on the w6692 I4L driver from Petr Novak - * - * Copyright 2009 by Karsten Keil - */ - -/* Specifications of W6692 registers */ - -#define W_D_RFIFO 0x00 /* R */ -#define W_D_XFIFO 0x04 /* W */ -#define W_D_CMDR 0x08 /* W */ -#define W_D_MODE 0x0c /* R/W */ -#define W_D_TIMR 0x10 /* R/W */ -#define W_ISTA 0x14 /* R_clr */ -#define W_IMASK 0x18 /* R/W */ -#define W_D_EXIR 0x1c /* R_clr */ -#define W_D_EXIM 0x20 /* R/W */ -#define W_D_STAR 0x24 /* R */ -#define W_D_RSTA 0x28 /* R */ -#define W_D_SAM 0x2c /* R/W */ -#define W_D_SAP1 0x30 /* R/W */ -#define W_D_SAP2 0x34 /* R/W */ -#define W_D_TAM 0x38 /* R/W */ -#define W_D_TEI1 0x3c /* R/W */ -#define W_D_TEI2 0x40 /* R/W */ -#define W_D_RBCH 0x44 /* R */ -#define W_D_RBCL 0x48 /* R */ -#define W_TIMR2 0x4c /* W */ -#define W_L1_RC 0x50 /* R/W */ -#define W_D_CTL 0x54 /* R/W */ -#define W_CIR 0x58 /* R */ -#define W_CIX 0x5c /* W */ -#define W_SQR 0x60 /* R */ -#define W_SQX 0x64 /* W */ -#define W_PCTL 0x68 /* R/W */ -#define W_MOR 0x6c /* R */ -#define W_MOX 0x70 /* R/W */ -#define W_MOSR 0x74 /* R_clr */ -#define W_MOCR 0x78 /* R/W */ -#define W_GCR 0x7c /* R/W */ - -#define W_B_RFIFO 0x80 /* R */ -#define W_B_XFIFO 0x84 /* W */ -#define W_B_CMDR 0x88 /* W */ -#define W_B_MODE 0x8c /* R/W */ -#define W_B_EXIR 0x90 /* R_clr */ -#define W_B_EXIM 0x94 /* R/W */ -#define W_B_STAR 0x98 /* R */ -#define W_B_ADM1 0x9c /* R/W */ -#define W_B_ADM2 0xa0 /* R/W */ -#define W_B_ADR1 0xa4 /* R/W */ -#define W_B_ADR2 0xa8 /* R/W */ -#define W_B_RBCL 0xac /* R */ -#define W_B_RBCH 0xb0 /* R */ - -#define W_XADDR 0xf4 /* R/W */ -#define W_XDATA 0xf8 /* R/W */ -#define W_EPCTL 0xfc /* W */ - -/* W6692 register bits */ - -#define W_D_CMDR_XRST 0x01 -#define W_D_CMDR_XME 0x02 -#define W_D_CMDR_XMS 0x08 -#define W_D_CMDR_STT 0x10 -#define W_D_CMDR_RRST 0x40 -#define W_D_CMDR_RACK 0x80 - -#define W_D_MODE_RLP 0x01 -#define W_D_MODE_DLP 0x02 -#define W_D_MODE_MFD 0x04 -#define W_D_MODE_TEE 0x08 -#define W_D_MODE_TMS 0x10 -#define W_D_MODE_RACT 0x40 -#define W_D_MODE_MMS 0x80 - -#define W_INT_B2_EXI 0x01 -#define W_INT_B1_EXI 0x02 -#define W_INT_D_EXI 0x04 -#define W_INT_XINT0 0x08 -#define W_INT_XINT1 0x10 -#define W_INT_D_XFR 0x20 -#define W_INT_D_RME 0x40 -#define W_INT_D_RMR 0x80 - -#define W_D_EXI_WEXP 0x01 -#define W_D_EXI_TEXP 0x02 -#define W_D_EXI_ISC 0x04 -#define W_D_EXI_MOC 0x08 -#define W_D_EXI_TIN2 0x10 -#define W_D_EXI_XCOL 0x20 -#define W_D_EXI_XDUN 0x40 -#define W_D_EXI_RDOV 0x80 - -#define W_D_STAR_DRDY 0x10 -#define W_D_STAR_XBZ 0x20 -#define W_D_STAR_XDOW 0x80 - -#define W_D_RSTA_RMB 0x10 -#define W_D_RSTA_CRCE 0x20 -#define W_D_RSTA_RDOV 0x40 - -#define W_D_CTL_SRST 0x20 - -#define W_CIR_SCC 0x80 -#define W_CIR_ICC 0x40 -#define W_CIR_COD_MASK 0x0f - -#define W_PCTL_PCX 0x01 -#define W_PCTL_XMODE 0x02 -#define W_PCTL_OE0 0x04 -#define W_PCTL_OE1 0x08 -#define W_PCTL_OE2 0x10 -#define W_PCTL_OE3 0x20 -#define W_PCTL_OE4 0x40 -#define W_PCTL_OE5 0x80 - -#define W_B_CMDR_XRST 0x01 -#define W_B_CMDR_XME 0x02 -#define W_B_CMDR_XMS 0x04 -#define W_B_CMDR_RACT 0x20 -#define W_B_CMDR_RRST 0x40 -#define W_B_CMDR_RACK 0x80 - -#define W_B_MODE_FTS0 0x01 -#define W_B_MODE_FTS1 0x02 -#define W_B_MODE_SW56 0x04 -#define W_B_MODE_BSW0 0x08 -#define W_B_MODE_BSW1 0x10 -#define W_B_MODE_EPCM 0x20 -#define W_B_MODE_ITF 0x40 -#define W_B_MODE_MMS 0x80 - -#define W_B_EXI_XDUN 0x01 -#define W_B_EXI_XFR 0x02 -#define W_B_EXI_RDOV 0x10 -#define W_B_EXI_RME 0x20 -#define W_B_EXI_RMR 0x40 - -#define W_B_STAR_XBZ 0x01 -#define W_B_STAR_XDOW 0x04 -#define W_B_STAR_RMB 0x10 -#define W_B_STAR_CRCE 0x20 -#define W_B_STAR_RDOV 0x40 - -#define W_B_RBCH_LOV 0x20 - -/* W6692 Layer1 commands */ - -#define W_L1CMD_ECK 0x00 -#define W_L1CMD_RST 0x01 -#define W_L1CMD_SCP 0x04 -#define W_L1CMD_SSP 0x02 -#define W_L1CMD_AR8 0x08 -#define W_L1CMD_AR10 0x09 -#define W_L1CMD_EAL 0x0a -#define W_L1CMD_DRC 0x0f - -/* W6692 Layer1 indications */ - -#define W_L1IND_CE 0x07 -#define W_L1IND_DRD 0x00 -#define W_L1IND_LD 0x04 -#define W_L1IND_ARD 0x08 -#define W_L1IND_TI 0x0a -#define W_L1IND_ATI 0x0b -#define W_L1IND_AI8 0x0c -#define W_L1IND_AI10 0x0d -#define W_L1IND_CD 0x0f - -/* FIFO thresholds */ -#define W_D_FIFO_THRESH 64 -#define W_B_FIFO_THRESH 64 diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig deleted file mode 100644 index c9a53c222472..000000000000 --- a/drivers/isdn/mISDN/Kconfig +++ /dev/null @@ -1,48 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# modularer ISDN driver -# - -menuconfig MISDN - tristate "Modular ISDN driver" - help - Enable support for the modular ISDN driver. - -if MISDN != n - -config MISDN_DSP - tristate "Digital Audio Processing of transparent data" - depends on MISDN - select BITREVERSE - help - Enable support for digital audio processing capability. - - This module may be used for special applications that require - cross connecting of bchannels, conferencing, dtmf decoding, - echo cancellation, tone generation, and Blowfish encryption and - decryption. It may use hardware features if available. - - E.g. it is required for PBX4Linux. Go to http://isdn.eversberg.eu - and get more information about this module and its usage. - - If unsure, say 'N'. - -config MISDN_L1OIP - tristate "ISDN over IP tunnel" - depends on MISDN - help - Enable support for ISDN over IP tunnel. - - It features: - - dynamic IP exchange, if one or both peers have dynamic IPs - - BRI (S0) and PRI (S2M) interface - - layer 1 control via network keepalive frames - - direct tunneling of physical interface via IP - - NOTE: This protocol is called 'Layer 1 over IP' and is not - compatible with ISDNoIP (Agfeo) or TDMoIP. Protocol description is - provided in the source code. - -source "drivers/isdn/hardware/mISDN/Kconfig" - -endif #MISDN diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile deleted file mode 100644 index f3b4b7fa85f8..000000000000 --- a/drivers/isdn/mISDN/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the modular ISDN driver -# - -obj-$(CONFIG_MISDN) += mISDN_core.o -obj-$(CONFIG_MISDN_DSP) += mISDN_dsp.o -obj-$(CONFIG_MISDN_L1OIP) += l1oip.o - -# multi objects - -mISDN_core-objs := core.o fsm.o socket.o clock.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o -mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o -l1oip-objs := l1oip_core.o l1oip_codec.o diff --git a/drivers/isdn/mISDN/clock.c b/drivers/isdn/mISDN/clock.c deleted file mode 100644 index 2668be9de20a..000000000000 --- a/drivers/isdn/mISDN/clock.c +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright 2008 by Andreas Eversberg - * - * Quick API description: - * - * A clock source registers using mISDN_register_clock: - * name = text string to name clock source - * priority = value to priorize clock sources (0 = default) - * ctl = callback function to enable/disable clock source - * priv = private pointer of clock source - * return = pointer to clock source structure; - * - * Note: Callback 'ctl' can be called before mISDN_register_clock returns! - * Also it can be called during mISDN_unregister_clock. - * - * A clock source calls mISDN_clock_update with given samples elapsed, if - * enabled. If function call is delayed, tv must be set with the timestamp - * of the actual event. - * - * A clock source unregisters using mISDN_unregister_clock. - * - * To get current clock, call mISDN_clock_get. The signed short value - * counts the number of samples since. Time since last clock event is added. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "core.h" - -static u_int *debug; -static LIST_HEAD(iclock_list); -static DEFINE_RWLOCK(iclock_lock); -static u16 iclock_count; /* counter of last clock */ -static ktime_t iclock_timestamp; /* time stamp of last clock */ -static int iclock_timestamp_valid; /* already received one timestamp */ -static struct mISDNclock *iclock_current; - -void -mISDN_init_clock(u_int *dp) -{ - debug = dp; - iclock_timestamp = ktime_get(); -} - -static void -select_iclock(void) -{ - struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL; - int pri = -128; - - list_for_each_entry(iclock, &iclock_list, list) { - if (iclock->pri > pri) { - pri = iclock->pri; - bestclock = iclock; - } - if (iclock_current == iclock) - lastclock = iclock; - } - if (lastclock && bestclock != lastclock) { - /* last used clock source still exists but changes, disable */ - if (*debug & DEBUG_CLOCK) - printk(KERN_DEBUG "Old clock source '%s' disable.\n", - lastclock->name); - lastclock->ctl(lastclock->priv, 0); - } - if (bestclock && bestclock != iclock_current) { - /* new clock source selected, enable */ - if (*debug & DEBUG_CLOCK) - printk(KERN_DEBUG "New clock source '%s' enable.\n", - bestclock->name); - bestclock->ctl(bestclock->priv, 1); - } - if (bestclock != iclock_current) { - /* no clock received yet */ - iclock_timestamp_valid = 0; - } - iclock_current = bestclock; -} - -struct mISDNclock -*mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv) -{ - u_long flags; - struct mISDNclock *iclock; - - if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) - printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri); - iclock = kzalloc_obj(struct mISDNclock, GFP_ATOMIC); - if (!iclock) { - printk(KERN_ERR "%s: No memory for clock entry.\n", __func__); - return NULL; - } - strscpy(iclock->name, name, sizeof(iclock->name)); - iclock->pri = pri; - iclock->priv = priv; - iclock->ctl = ctl; - write_lock_irqsave(&iclock_lock, flags); - list_add_tail(&iclock->list, &iclock_list); - select_iclock(); - write_unlock_irqrestore(&iclock_lock, flags); - return iclock; -} -EXPORT_SYMBOL(mISDN_register_clock); - -void -mISDN_unregister_clock(struct mISDNclock *iclock) -{ - u_long flags; - - if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) - printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name, - iclock->pri); - write_lock_irqsave(&iclock_lock, flags); - if (iclock_current == iclock) { - if (*debug & DEBUG_CLOCK) - printk(KERN_DEBUG - "Current clock source '%s' unregisters.\n", - iclock->name); - iclock->ctl(iclock->priv, 0); - } - list_del(&iclock->list); - select_iclock(); - write_unlock_irqrestore(&iclock_lock, flags); -} -EXPORT_SYMBOL(mISDN_unregister_clock); - -void -mISDN_clock_update(struct mISDNclock *iclock, int samples, ktime_t *timestamp) -{ - u_long flags; - ktime_t timestamp_now; - u16 delta; - - write_lock_irqsave(&iclock_lock, flags); - if (iclock_current != iclock) { - printk(KERN_ERR "%s: '%s' sends us clock updates, but we do " - "listen to '%s'. This is a bug!\n", __func__, - iclock->name, - iclock_current ? iclock_current->name : "nothing"); - iclock->ctl(iclock->priv, 0); - write_unlock_irqrestore(&iclock_lock, flags); - return; - } - if (iclock_timestamp_valid) { - /* increment sample counter by given samples */ - iclock_count += samples; - if (timestamp) { /* timestamp must be set, if function call is delayed */ - iclock_timestamp = *timestamp; - } else { - iclock_timestamp = ktime_get(); - } - } else { - /* calc elapsed time by system clock */ - if (timestamp) { /* timestamp must be set, if function call is delayed */ - timestamp_now = *timestamp; - } else { - timestamp_now = ktime_get(); - } - delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp), - (NSEC_PER_SEC / 8000)); - /* add elapsed time to counter and set new timestamp */ - iclock_count += delta; - iclock_timestamp = timestamp_now; - iclock_timestamp_valid = 1; - if (*debug & DEBUG_CLOCK) - printk("Received first clock from source '%s'.\n", - iclock_current ? iclock_current->name : "nothing"); - } - write_unlock_irqrestore(&iclock_lock, flags); -} -EXPORT_SYMBOL(mISDN_clock_update); - -unsigned short -mISDN_clock_get(void) -{ - u_long flags; - ktime_t timestamp_now; - u16 delta; - u16 count; - - read_lock_irqsave(&iclock_lock, flags); - /* calc elapsed time by system clock */ - timestamp_now = ktime_get(); - delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp), - (NSEC_PER_SEC / 8000)); - /* add elapsed time to counter */ - count = iclock_count + delta; - read_unlock_irqrestore(&iclock_lock, flags); - return count; -} -EXPORT_SYMBOL(mISDN_clock_get); diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c deleted file mode 100644 index 8ec2d4d4f135..000000000000 --- a/drivers/isdn/mISDN/core.c +++ /dev/null @@ -1,400 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include -#include "core.h" - -static u_int debug; - -MODULE_AUTHOR("Karsten Keil"); -MODULE_DESCRIPTION("Modular ISDN core driver"); -MODULE_LICENSE("GPL"); -module_param(debug, uint, S_IRUGO | S_IWUSR); - -static u64 device_ids; -#define MAX_DEVICE_ID 63 - -static LIST_HEAD(Bprotocols); -static DEFINE_RWLOCK(bp_lock); - -static void mISDN_dev_release(struct device *dev) -{ - /* nothing to do: the device is part of its parent's data structure */ -} - -static ssize_t id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return -ENODEV; - return sprintf(buf, "%d\n", mdev->id); -} -static DEVICE_ATTR_RO(id); - -static ssize_t nrbchan_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return -ENODEV; - return sprintf(buf, "%d\n", mdev->nrbchan); -} -static DEVICE_ATTR_RO(nrbchan); - -static ssize_t d_protocols_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return -ENODEV; - return sprintf(buf, "%d\n", mdev->Dprotocols); -} -static DEVICE_ATTR_RO(d_protocols); - -static ssize_t b_protocols_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return -ENODEV; - return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols()); -} -static DEVICE_ATTR_RO(b_protocols); - -static ssize_t protocol_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return -ENODEV; - return sprintf(buf, "%d\n", mdev->D.protocol); -} -static DEVICE_ATTR_RO(protocol); - -static ssize_t name_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - strcpy(buf, dev_name(dev)); - return strlen(buf); -} -static DEVICE_ATTR_RO(name); - -#if 0 /* hangs */ -static ssize_t name_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - int err = 0; - char *out = kmalloc(count + 1, GFP_KERNEL); - - if (!out) - return -ENOMEM; - - memcpy(out, buf, count); - if (count && out[count - 1] == '\n') - out[--count] = 0; - if (count) - err = device_rename(dev, out); - kfree(out); - - return (err < 0) ? err : count; -} -static DEVICE_ATTR_RW(name); -#endif - -static ssize_t channelmap_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - char *bp = buf; - int i; - - for (i = 0; i <= mdev->nrbchan; i++) - *bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0'; - - return bp - buf; -} -static DEVICE_ATTR_RO(channelmap); - -static struct attribute *mISDN_attrs[] = { - &dev_attr_id.attr, - &dev_attr_d_protocols.attr, - &dev_attr_b_protocols.attr, - &dev_attr_protocol.attr, - &dev_attr_channelmap.attr, - &dev_attr_nrbchan.attr, - &dev_attr_name.attr, - NULL, -}; -ATTRIBUTE_GROUPS(mISDN); - -static int mISDN_uevent(const struct device *dev, struct kobj_uevent_env *env) -{ - const struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return 0; - - if (add_uevent_var(env, "nchans=%d", mdev->nrbchan)) - return -ENOMEM; - - return 0; -} - -static struct class mISDN_class = { - .name = "mISDN", - .dev_uevent = mISDN_uevent, - .dev_groups = mISDN_groups, - .dev_release = mISDN_dev_release, -}; - -static int -_get_mdevice(struct device *dev, const void *id) -{ - struct mISDNdevice *mdev = dev_to_mISDN(dev); - - if (!mdev) - return 0; - if (mdev->id != *(const u_int *)id) - return 0; - return 1; -} - -struct mISDNdevice -*get_mdevice(u_int id) -{ - return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id, - _get_mdevice)); -} - -static int -_get_mdevice_count(struct device *dev, void *cnt) -{ - *(int *)cnt += 1; - return 0; -} - -int -get_mdevice_count(void) -{ - int cnt = 0; - - class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count); - return cnt; -} - -static int -get_free_devid(void) -{ - u_int i; - - for (i = 0; i <= MAX_DEVICE_ID; i++) - if (!test_and_set_bit(i, (u_long *)&device_ids)) - break; - if (i > MAX_DEVICE_ID) - return -EBUSY; - return i; -} - -int -mISDN_register_device(struct mISDNdevice *dev, - struct device *parent, char *name) -{ - int err; - - err = get_free_devid(); - if (err < 0) - return err; - dev->id = err; - - device_initialize(&dev->dev); - if (name && name[0]) - dev_set_name(&dev->dev, "%s", name); - else - dev_set_name(&dev->dev, "mISDN%d", dev->id); - if (debug & DEBUG_CORE) - printk(KERN_DEBUG "mISDN_register %s %d\n", - dev_name(&dev->dev), dev->id); - dev->dev.class = &mISDN_class; - - err = create_stack(dev); - if (err) - goto error1; - - dev->dev.platform_data = dev; - dev->dev.parent = parent; - dev_set_drvdata(&dev->dev, dev); - - err = device_add(&dev->dev); - if (err) - goto error3; - return 0; - -error3: - delete_stack(dev); -error1: - put_device(&dev->dev); - return err; - -} -EXPORT_SYMBOL(mISDN_register_device); - -void -mISDN_unregister_device(struct mISDNdevice *dev) { - if (debug & DEBUG_CORE) - printk(KERN_DEBUG "mISDN_unregister %s %d\n", - dev_name(&dev->dev), dev->id); - /* sysfs_remove_link(&dev->dev.kobj, "device"); */ - device_del(&dev->dev); - dev_set_drvdata(&dev->dev, NULL); - - test_and_clear_bit(dev->id, (u_long *)&device_ids); - delete_stack(dev); - put_device(&dev->dev); -} -EXPORT_SYMBOL(mISDN_unregister_device); - -u_int -get_all_Bprotocols(void) -{ - struct Bprotocol *bp; - u_int m = 0; - - read_lock(&bp_lock); - list_for_each_entry(bp, &Bprotocols, list) - m |= bp->Bprotocols; - read_unlock(&bp_lock); - return m; -} - -struct Bprotocol * -get_Bprotocol4mask(u_int m) -{ - struct Bprotocol *bp; - - read_lock(&bp_lock); - list_for_each_entry(bp, &Bprotocols, list) - if (bp->Bprotocols & m) { - read_unlock(&bp_lock); - return bp; - } - read_unlock(&bp_lock); - return NULL; -} - -int -mISDN_register_Bprotocol(struct Bprotocol *bp) -{ - u_long flags; - struct Bprotocol *old; - - if (debug & DEBUG_CORE) - printk(KERN_DEBUG "%s: %s/%x\n", __func__, - bp->name, bp->Bprotocols); - old = get_Bprotocol4mask(bp->Bprotocols); - if (old) { - printk(KERN_WARNING - "register duplicate protocol old %s/%x new %s/%x\n", - old->name, old->Bprotocols, bp->name, bp->Bprotocols); - return -EBUSY; - } - write_lock_irqsave(&bp_lock, flags); - list_add_tail(&bp->list, &Bprotocols); - write_unlock_irqrestore(&bp_lock, flags); - return 0; -} -EXPORT_SYMBOL(mISDN_register_Bprotocol); - -void -mISDN_unregister_Bprotocol(struct Bprotocol *bp) -{ - u_long flags; - - if (debug & DEBUG_CORE) - printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name, - bp->Bprotocols); - write_lock_irqsave(&bp_lock, flags); - list_del(&bp->list); - write_unlock_irqrestore(&bp_lock, flags); -} -EXPORT_SYMBOL(mISDN_unregister_Bprotocol); - -static const char *msg_no_channel = ""; -static const char *msg_no_stack = ""; -static const char *msg_no_stackdev = ""; - -const char *mISDNDevName4ch(struct mISDNchannel *ch) -{ - if (!ch) - return msg_no_channel; - if (!ch->st) - return msg_no_stack; - if (!ch->st->dev) - return msg_no_stackdev; - return dev_name(&ch->st->dev->dev); -}; -EXPORT_SYMBOL(mISDNDevName4ch); - -static int -mISDNInit(void) -{ - int err; - - printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n", - MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); - mISDN_init_clock(&debug); - mISDN_initstack(&debug); - err = class_register(&mISDN_class); - if (err) - goto error1; - err = mISDN_inittimer(&debug); - if (err) - goto error2; - err = Isdnl1_Init(&debug); - if (err) - goto error3; - err = Isdnl2_Init(&debug); - if (err) - goto error4; - err = misdn_sock_init(&debug); - if (err) - goto error5; - return 0; - -error5: - Isdnl2_cleanup(); -error4: - Isdnl1_cleanup(); -error3: - mISDN_timer_cleanup(); -error2: - class_unregister(&mISDN_class); -error1: - return err; -} - -static void mISDN_cleanup(void) -{ - misdn_sock_cleanup(); - Isdnl2_cleanup(); - Isdnl1_cleanup(); - mISDN_timer_cleanup(); - class_unregister(&mISDN_class); - - printk(KERN_DEBUG "mISDNcore unloaded\n"); -} - -module_init(mISDNInit); -module_exit(mISDN_cleanup); diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h deleted file mode 100644 index 5617c06de8e4..000000000000 --- a/drivers/isdn/mISDN/core.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright 2008 by Karsten Keil - */ - -#ifndef mISDN_CORE_H -#define mISDN_CORE_H - -extern struct mISDNdevice *get_mdevice(u_int); -extern int get_mdevice_count(void); - -/* stack status flag */ -#define mISDN_STACK_ACTION_MASK 0x0000ffff -#define mISDN_STACK_COMMAND_MASK 0x000f0000 -#define mISDN_STACK_STATUS_MASK 0xfff00000 -/* action bits 0-15 */ -#define mISDN_STACK_WORK 0 -#define mISDN_STACK_SETUP 1 -#define mISDN_STACK_CLEARING 2 -#define mISDN_STACK_RESTART 3 -#define mISDN_STACK_WAKEUP 4 -#define mISDN_STACK_ABORT 15 -/* command bits 16-19 */ -#define mISDN_STACK_STOPPED 16 -#define mISDN_STACK_INIT 17 -#define mISDN_STACK_THREADSTART 18 -/* status bits 20-31 */ -#define mISDN_STACK_BCHANNEL 20 -#define mISDN_STACK_ACTIVE 29 -#define mISDN_STACK_RUNNING 30 -#define mISDN_STACK_KILLED 31 - - -/* manager options */ -#define MGR_OPT_USER 24 -#define MGR_OPT_NETWORK 25 - -extern int connect_Bstack(struct mISDNdevice *, struct mISDNchannel *, - u_int, struct sockaddr_mISDN *); -extern int connect_layer1(struct mISDNdevice *, struct mISDNchannel *, - u_int, struct sockaddr_mISDN *); -extern int create_l2entity(struct mISDNdevice *, struct mISDNchannel *, - u_int, struct sockaddr_mISDN *); - -extern int create_stack(struct mISDNdevice *); -extern int create_teimanager(struct mISDNdevice *); -extern void delete_teimanager(struct mISDNchannel *); -extern void delete_channel(struct mISDNchannel *); -extern void delete_stack(struct mISDNdevice *); -extern void mISDN_initstack(u_int *); -extern int misdn_sock_init(u_int *); -extern void misdn_sock_cleanup(void); -extern void add_layer2(struct mISDNchannel *, struct mISDNstack *); -extern void __add_layer2(struct mISDNchannel *, struct mISDNstack *); - -extern u_int get_all_Bprotocols(void); -struct Bprotocol *get_Bprotocol4mask(u_int); - -extern int mISDN_inittimer(u_int *); -extern void mISDN_timer_cleanup(void); - -extern int Isdnl1_Init(u_int *); -extern void Isdnl1_cleanup(void); -extern int Isdnl2_Init(u_int *); -extern void Isdnl2_cleanup(void); - -extern void mISDN_init_clock(u_int *); - -#endif diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h deleted file mode 100644 index baf31258f5c9..000000000000 --- a/drivers/isdn/mISDN/dsp.h +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Audio support data for ISDN4Linux. - * - * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#define DEBUG_DSP_CTRL 0x0001 -#define DEBUG_DSP_CORE 0x0002 -#define DEBUG_DSP_DTMF 0x0004 -#define DEBUG_DSP_CMX 0x0010 -#define DEBUG_DSP_TONE 0x0020 -#define DEBUG_DSP_BLOWFISH 0x0040 -#define DEBUG_DSP_DELAY 0x0100 -#define DEBUG_DSP_CLOCK 0x0200 -#define DEBUG_DSP_DTMFCOEFF 0x8000 /* heavy output */ - -/* options may be: - * - * bit 0 = use ulaw instead of alaw - * bit 1 = enable hfc hardware acceleration for all channels - * - */ -#define DSP_OPT_ULAW (1 << 0) -#define DSP_OPT_NOHARDWARE (1 << 1) - -#include -#include - -#include "dsp_ecdis.h" - -extern int dsp_options; -extern int dsp_debug; -extern int dsp_poll; -extern int dsp_tics; -extern spinlock_t dsp_lock; -extern struct work_struct dsp_workq; -extern u32 dsp_poll_diff; /* calculated fix-comma corrected poll value */ - -/*************** - * audio stuff * - ***************/ - -extern s32 dsp_audio_alaw_to_s32[256]; -extern s32 dsp_audio_ulaw_to_s32[256]; -extern s32 *dsp_audio_law_to_s32; -extern u8 dsp_audio_s16_to_law[65536]; -extern u8 dsp_audio_alaw_to_ulaw[256]; -extern u8 dsp_audio_mix_law[65536]; -extern u8 dsp_audio_seven2law[128]; -extern u8 dsp_audio_law2seven[256]; -extern void dsp_audio_generate_law_tables(void); -extern void dsp_audio_generate_s2law_table(void); -extern void dsp_audio_generate_seven(void); -extern void dsp_audio_generate_mix_table(void); -extern void dsp_audio_generate_ulaw_samples(void); -extern void dsp_audio_generate_volume_changes(void); -extern u8 dsp_silence; - - -/************* - * cmx stuff * - *************/ - -#define MAX_POLL 256 /* maximum number of send-chunks */ - -#define CMX_BUFF_SIZE 0x8000 /* must be 2**n (0x1000 about 1/2 second) */ -#define CMX_BUFF_HALF 0x4000 /* CMX_BUFF_SIZE / 2 */ -#define CMX_BUFF_MASK 0x7fff /* CMX_BUFF_SIZE - 1 */ - -/* how many seconds will we check the lowest delay until the jitter buffer - is reduced by that delay */ -#define MAX_SECONDS_JITTER_CHECK 5 - -extern struct timer_list dsp_spl_tl; - -/* the datatype need to match jiffies datatype */ -extern unsigned long dsp_spl_jiffies; - -/* the structure of conferences: - * - * each conference has a unique number, given by user space. - * the conferences are linked in a chain. - * each conference has members linked in a chain. - * each dsplayer points to a member, each member points to a dsplayer. - */ - -/* all members within a conference (this is linked 1:1 with the dsp) */ -struct dsp; -struct dsp_conf_member { - struct list_head list; - struct dsp *dsp; -}; - -/* the list of all conferences */ -struct dsp_conf { - struct list_head list; - u32 id; - /* all cmx stacks with the same ID are - connected */ - struct list_head mlist; - int software; /* conf is processed by software */ - int hardware; /* conf is processed by hardware */ - /* note: if both unset, has only one member */ -}; - - -/************** - * DTMF stuff * - **************/ - -#define DSP_DTMF_NPOINTS 102 - -#define ECHOCAN_BUFF_SIZE 0x400 /* must be 2**n */ -#define ECHOCAN_BUFF_MASK 0x3ff /* -1 */ - -struct dsp_dtmf { - int enable; /* dtmf is enabled */ - int treshold; /* above this is dtmf (square of) */ - int software; /* dtmf uses software decoding */ - int hardware; /* dtmf uses hardware decoding */ - int size; /* number of bytes in buffer */ - signed short buffer[DSP_DTMF_NPOINTS]; - /* buffers one full dtmf frame */ - u8 lastwhat, lastdigit; - int count; - u8 digits[16]; /* dtmf result */ -}; - - -/****************** - * pipeline stuff * - ******************/ -struct dsp_pipeline { - rwlock_t lock; - struct list_head list; - int inuse; -}; - -/*************** - * tones stuff * - ***************/ - -struct dsp_tone { - int software; /* tones are generated by software */ - int hardware; /* tones are generated by hardware */ - int tone; - void *pattern; - int count; - int index; - struct timer_list tl; -}; - -/*************** - * echo stuff * - ***************/ - -struct dsp_echo { - int software; /* echo is generated by software */ - int hardware; /* echo is generated by hardware */ -}; - -/***************** - * general stuff * - *****************/ - -struct dsp { - struct list_head list; - struct mISDNchannel ch; - struct mISDNchannel *up; - unsigned char name[64]; - int b_active; - struct dsp_echo echo; - int rx_disabled; /* what the user wants */ - int rx_is_off; /* what the card is */ - int tx_mix; - struct dsp_tone tone; - struct dsp_dtmf dtmf; - int tx_volume, rx_volume; - - /* queue for sending frames */ - struct work_struct workq; - struct sk_buff_head sendq; - int hdlc; /* if mode is hdlc */ - int data_pending; /* currently an unconfirmed frame */ - - /* conference stuff */ - u32 conf_id; - struct dsp_conf *conf; - struct dsp_conf_member - *member; - - /* buffer stuff */ - int rx_W; /* current write pos for data without timestamp */ - int rx_R; /* current read pos for transmit clock */ - int rx_init; /* if set, pointers will be adjusted first */ - int tx_W; /* current write pos for transmit data */ - int tx_R; /* current read pos for transmit clock */ - int rx_delay[MAX_SECONDS_JITTER_CHECK]; - int tx_delay[MAX_SECONDS_JITTER_CHECK]; - u8 tx_buff[CMX_BUFF_SIZE]; - u8 rx_buff[CMX_BUFF_SIZE]; - int last_tx; /* if set, we transmitted last poll interval */ - int cmx_delay; /* initial delay of buffers, - or 0 for dynamic jitter buffer */ - int tx_dejitter; /* if set, dejitter tx buffer */ - int tx_data; /* enables tx-data of CMX to upper layer */ - - /* hardware stuff */ - struct dsp_features features; - int features_rx_off; /* set if rx_off is featured */ - int features_fill_empty; /* set if fill_empty is featured */ - int pcm_slot_rx; /* current PCM slot (or -1) */ - int pcm_bank_rx; - int pcm_slot_tx; - int pcm_bank_tx; - int hfc_conf; /* unique id of current conference (or -1) */ - - /* encryption stuff */ - int bf_enable; - u32 bf_p[18]; - u32 bf_s[1024]; - int bf_crypt_pos; - u8 bf_data_in[9]; - u8 bf_crypt_out[9]; - int bf_decrypt_in_pos; - int bf_decrypt_out_pos; - u8 bf_crypt_inring[16]; - u8 bf_data_out[9]; - int bf_sync; - - struct dsp_pipeline - pipeline; -}; - -/* functions */ - -extern void dsp_change_volume(struct sk_buff *skb, int volume); - -extern struct list_head dsp_ilist; -extern struct list_head conf_ilist; -extern void dsp_cmx_debug(struct dsp *dsp); -extern void dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp); -extern int dsp_cmx_conf(struct dsp *dsp, u32 conf_id); -extern void dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb); -extern void dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb); -extern void dsp_cmx_send(struct timer_list *arg); -extern void dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb); -extern int dsp_cmx_del_conf_member(struct dsp *dsp); -extern int dsp_cmx_del_conf(struct dsp_conf *conf); - -extern void dsp_dtmf_goertzel_init(struct dsp *dsp); -extern void dsp_dtmf_hardware(struct dsp *dsp); -extern u8 *dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, - int fmt); - -extern int dsp_tone(struct dsp *dsp, int tone); -extern void dsp_tone_copy(struct dsp *dsp, u8 *data, int len); -extern void dsp_tone_timeout(struct timer_list *t); - -extern void dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len); -extern void dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len); -extern int dsp_bf_init(struct dsp *dsp, const u8 *key, unsigned int keylen); -extern void dsp_bf_cleanup(struct dsp *dsp); - -extern int dsp_pipeline_module_init(void); -extern void dsp_pipeline_module_exit(void); -extern int dsp_pipeline_init(struct dsp_pipeline *pipeline); -extern void dsp_pipeline_destroy(struct dsp_pipeline *pipeline); -extern int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg); -extern void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, - int len); -extern void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, - int len, unsigned int txlen); diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c deleted file mode 100644 index bbef98e7a16e..000000000000 --- a/drivers/isdn/mISDN/dsp_audio.c +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Audio support data for mISDN_dsp. - * - * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) - * Rewritten by Peter - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include -#include -#include -#include "core.h" -#include "dsp.h" - -/* ulaw[unsigned char] -> signed 16-bit */ -s32 dsp_audio_ulaw_to_s32[256]; -/* alaw[unsigned char] -> signed 16-bit */ -s32 dsp_audio_alaw_to_s32[256]; - -s32 *dsp_audio_law_to_s32; -EXPORT_SYMBOL(dsp_audio_law_to_s32); - -/* signed 16-bit -> law */ -u8 dsp_audio_s16_to_law[65536]; -EXPORT_SYMBOL(dsp_audio_s16_to_law); - -/* alaw -> ulaw */ -u8 dsp_audio_alaw_to_ulaw[256]; -/* ulaw -> alaw */ -static u8 dsp_audio_ulaw_to_alaw[256]; -u8 dsp_silence; - - -/***************************************************** - * generate table for conversion of s16 to alaw/ulaw * - *****************************************************/ - -#define AMI_MASK 0x55 - -static inline unsigned char linear2alaw(short int linear) -{ - int mask; - int seg; - int pcm_val; - static int seg_end[8] = { - 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF - }; - - pcm_val = linear; - if (pcm_val >= 0) { - /* Sign (7th) bit = 1 */ - mask = AMI_MASK | 0x80; - } else { - /* Sign bit = 0 */ - mask = AMI_MASK; - pcm_val = -pcm_val; - } - - /* Convert the scaled magnitude to segment number. */ - for (seg = 0; seg < 8; seg++) { - if (pcm_val <= seg_end[seg]) - break; - } - /* Combine the sign, segment, and quantization bits. */ - return ((seg << 4) | - ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask; -} - - -static inline short int alaw2linear(unsigned char alaw) -{ - int i; - int seg; - - alaw ^= AMI_MASK; - i = ((alaw & 0x0F) << 4) + 8 /* rounding error */; - seg = (((int) alaw & 0x70) >> 4); - if (seg) - i = (i + 0x100) << (seg - 1); - return (short int) ((alaw & 0x80) ? i : -i); -} - -static inline short int ulaw2linear(unsigned char ulaw) -{ - short mu, e, f, y; - static short etab[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764}; - - mu = 255 - ulaw; - e = (mu & 0x70) / 16; - f = mu & 0x0f; - y = f * (1 << (e + 3)); - y += etab[e]; - if (mu & 0x80) - y = -y; - return y; -} - -#define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */ - -static unsigned char linear2ulaw(short sample) -{ - static int exp_lut[256] = { - 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; - int sign, exponent, mantissa; - unsigned char ulawbyte; - - /* Get the sample into sign-magnitude. */ - sign = (sample >> 8) & 0x80; /* set aside the sign */ - if (sign != 0) - sample = -sample; /* get magnitude */ - - /* Convert from 16 bit linear to ulaw. */ - sample = sample + BIAS; - exponent = exp_lut[(sample >> 7) & 0xFF]; - mantissa = (sample >> (exponent + 3)) & 0x0F; - ulawbyte = ~(sign | (exponent << 4) | mantissa); - - return ulawbyte; -} - -void dsp_audio_generate_law_tables(void) -{ - int i; - for (i = 0; i < 256; i++) - dsp_audio_alaw_to_s32[i] = alaw2linear(bitrev8((u8)i)); - - for (i = 0; i < 256; i++) - dsp_audio_ulaw_to_s32[i] = ulaw2linear(bitrev8((u8)i)); - - for (i = 0; i < 256; i++) { - dsp_audio_alaw_to_ulaw[i] = - linear2ulaw(dsp_audio_alaw_to_s32[i]); - dsp_audio_ulaw_to_alaw[i] = - linear2alaw(dsp_audio_ulaw_to_s32[i]); - } -} - -void -dsp_audio_generate_s2law_table(void) -{ - int i; - - if (dsp_options & DSP_OPT_ULAW) { - /* generating ulaw-table */ - for (i = -32768; i < 32768; i++) { - dsp_audio_s16_to_law[i & 0xffff] = - bitrev8(linear2ulaw(i)); - } - } else { - /* generating alaw-table */ - for (i = -32768; i < 32768; i++) { - dsp_audio_s16_to_law[i & 0xffff] = - bitrev8(linear2alaw(i)); - } - } -} - - -/* - * the seven bit sample is the number of every second alaw-sample ordered by - * aplitude. 0x00 is negative, 0x7f is positive amplitude. - */ -u8 dsp_audio_seven2law[128]; -u8 dsp_audio_law2seven[256]; - -/******************************************************************** - * generate table for conversion law from/to 7-bit alaw-like sample * - ********************************************************************/ - -void -dsp_audio_generate_seven(void) -{ - int i, j, k; - u8 spl; - u8 sorted_alaw[256]; - - /* generate alaw table, sorted by the linear value */ - for (i = 0; i < 256; i++) { - j = 0; - for (k = 0; k < 256; k++) { - if (dsp_audio_alaw_to_s32[k] - < dsp_audio_alaw_to_s32[i]) - j++; - } - sorted_alaw[j] = i; - } - - /* generate tabels */ - for (i = 0; i < 256; i++) { - /* spl is the source: the law-sample (converted to alaw) */ - spl = i; - if (dsp_options & DSP_OPT_ULAW) - spl = dsp_audio_ulaw_to_alaw[i]; - /* find the 7-bit-sample */ - for (j = 0; j < 256; j++) { - if (sorted_alaw[j] == spl) - break; - } - /* write 7-bit audio value */ - dsp_audio_law2seven[i] = j >> 1; - } - for (i = 0; i < 128; i++) { - spl = sorted_alaw[i << 1]; - if (dsp_options & DSP_OPT_ULAW) - spl = dsp_audio_alaw_to_ulaw[spl]; - dsp_audio_seven2law[i] = spl; - } -} - - -/* mix 2*law -> law */ -u8 dsp_audio_mix_law[65536]; - -/****************************************************** - * generate mix table to mix two law samples into one * - ******************************************************/ - -void -dsp_audio_generate_mix_table(void) -{ - int i, j; - s32 sample; - - i = 0; - while (i < 256) { - j = 0; - while (j < 256) { - sample = dsp_audio_law_to_s32[i]; - sample += dsp_audio_law_to_s32[j]; - if (sample > 32767) - sample = 32767; - if (sample < -32768) - sample = -32768; - dsp_audio_mix_law[(i << 8) | j] = - dsp_audio_s16_to_law[sample & 0xffff]; - j++; - } - i++; - } -} - - -/************************************* - * generate different volume changes * - *************************************/ - -static u8 dsp_audio_reduce8[256]; -static u8 dsp_audio_reduce7[256]; -static u8 dsp_audio_reduce6[256]; -static u8 dsp_audio_reduce5[256]; -static u8 dsp_audio_reduce4[256]; -static u8 dsp_audio_reduce3[256]; -static u8 dsp_audio_reduce2[256]; -static u8 dsp_audio_reduce1[256]; -static u8 dsp_audio_increase1[256]; -static u8 dsp_audio_increase2[256]; -static u8 dsp_audio_increase3[256]; -static u8 dsp_audio_increase4[256]; -static u8 dsp_audio_increase5[256]; -static u8 dsp_audio_increase6[256]; -static u8 dsp_audio_increase7[256]; -static u8 dsp_audio_increase8[256]; - -static u8 *dsp_audio_volume_change[16] = { - dsp_audio_reduce8, - dsp_audio_reduce7, - dsp_audio_reduce6, - dsp_audio_reduce5, - dsp_audio_reduce4, - dsp_audio_reduce3, - dsp_audio_reduce2, - dsp_audio_reduce1, - dsp_audio_increase1, - dsp_audio_increase2, - dsp_audio_increase3, - dsp_audio_increase4, - dsp_audio_increase5, - dsp_audio_increase6, - dsp_audio_increase7, - dsp_audio_increase8, -}; - -void -dsp_audio_generate_volume_changes(void) -{ - register s32 sample; - int i; - int num[] = { 110, 125, 150, 175, 200, 300, 400, 500 }; - int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 }; - - i = 0; - while (i < 256) { - dsp_audio_reduce8[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[7] / num[7]) & 0xffff]; - dsp_audio_reduce7[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff]; - dsp_audio_reduce6[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff]; - dsp_audio_reduce5[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff]; - dsp_audio_reduce4[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff]; - dsp_audio_reduce3[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff]; - dsp_audio_reduce2[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff]; - dsp_audio_reduce1[i] = dsp_audio_s16_to_law[ - (dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[0] / denum[0]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[1] / denum[1]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[2] / denum[2]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[3] / denum[3]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[4] / denum[4]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[5] / denum[5]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[6] / denum[6]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff]; - sample = dsp_audio_law_to_s32[i] * num[7] / denum[7]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - dsp_audio_increase8[i] = dsp_audio_s16_to_law[sample & 0xffff]; - - i++; - } -} - - -/************************************** - * change the volume of the given skb * - **************************************/ - -/* this is a helper function for changing volume of skb. the range may be - * -8 to 8, which is a shift to the power of 2. 0 == no volume, 3 == volume*8 - */ -void -dsp_change_volume(struct sk_buff *skb, int volume) -{ - u8 *volume_change; - int i, ii; - u8 *p; - int shift; - - if (volume == 0) - return; - - /* get correct conversion table */ - if (volume < 0) { - shift = volume + 8; - if (shift < 0) - shift = 0; - } else { - shift = volume + 7; - if (shift > 15) - shift = 15; - } - volume_change = dsp_audio_volume_change[shift]; - i = 0; - ii = skb->len; - p = skb->data; - /* change volume */ - while (i < ii) { - *p = volume_change[*p]; - p++; - i++; - } -} diff --git a/drivers/isdn/mISDN/dsp_biquad.h b/drivers/isdn/mISDN/dsp_biquad.h deleted file mode 100644 index f40d52a4c4ee..000000000000 --- a/drivers/isdn/mISDN/dsp_biquad.h +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * SpanDSP - a series of DSP components for telephony - * - * biquad.h - General telephony bi-quad section routines (currently this just - * handles canonic/type 2 form) - * - * Written by Steve Underwood - * - * Copyright (C) 2001 Steve Underwood - * - * All rights reserved. - */ - -struct biquad2_state { - int32_t gain; - int32_t a1; - int32_t a2; - int32_t b1; - int32_t b2; - - int32_t z1; - int32_t z2; -}; - -static inline void biquad2_init(struct biquad2_state *bq, - int32_t gain, int32_t a1, int32_t a2, int32_t b1, int32_t b2) -{ - bq->gain = gain; - bq->a1 = a1; - bq->a2 = a2; - bq->b1 = b1; - bq->b2 = b2; - - bq->z1 = 0; - bq->z2 = 0; -} - -static inline int16_t biquad2(struct biquad2_state *bq, int16_t sample) -{ - int32_t y; - int32_t z0; - - z0 = sample * bq->gain + bq->z1 * bq->a1 + bq->z2 * bq->a2; - y = z0 + bq->z1 * bq->b1 + bq->z2 * bq->b2; - - bq->z2 = bq->z1; - bq->z1 = z0 >> 15; - y >>= 15; - return y; -} diff --git a/drivers/isdn/mISDN/dsp_blowfish.c b/drivers/isdn/mISDN/dsp_blowfish.c deleted file mode 100644 index 0e77c282c862..000000000000 --- a/drivers/isdn/mISDN/dsp_blowfish.c +++ /dev/null @@ -1,667 +0,0 @@ -/* - * Blowfish encryption/decryption for mISDN_dsp. - * - * Copyright Andreas Eversberg (jolly@eversberg.eu) - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include "core.h" -#include "dsp.h" - -/* - * how to encode a sample stream to 64-bit blocks that will be encryped - * - * first of all, data is collected until a block of 9 samples are received. - * of course, a packet may have much more than 9 sample, but is may have - * not excacly the multiple of 9 samples. if there is a rest, the next - * received data will complete the block. - * - * the block is then converted to 9 uLAW samples without the least sigificant - * bit. the result is a 7-bit encoded sample. - * - * the samples will be reoganised to form 8 bytes of data: - * (5(6) means: encoded sample no. 5, bit 6) - * - * 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) 0(0) 1(6) - * 1(5) 1(4) 1(3) 1(2) 1(1) 1(0) 2(6) 2(5) - * 2(4) 2(3) 2(2) 2(1) 2(0) 3(6) 3(5) 3(4) - * 3(3) 3(2) 3(1) 3(0) 4(6) 4(5) 4(4) 4(3) - * 4(2) 4(1) 4(0) 5(6) 5(5) 5(4) 5(3) 5(2) - * 5(1) 5(0) 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) - * 6(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) - * 8(6) 8(5) 8(4) 8(3) 8(2) 8(1) 8(0) - * - * the missing bit 0 of the last byte is filled with some - * random noise, to fill all 8 bytes. - * - * the 8 bytes will be encrypted using blowfish. - * - * the result will be converted into 9 bytes. the bit 7 is used for - * checksumme (CS) for sync (0, 1) and for the last bit: - * (5(6) means: crypted byte 5, bit 6) - * - * 1 0(7) 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) - * 0 0(0) 1(7) 1(6) 1(5) 1(4) 1(3) 1(2) - * 0 1(1) 1(0) 2(7) 2(6) 2(5) 2(4) 2(3) - * 0 2(2) 2(1) 2(0) 3(7) 3(6) 3(5) 3(4) - * 0 3(3) 3(2) 3(1) 3(0) 4(7) 4(6) 4(5) - * CS 4(4) 4(3) 4(2) 4(1) 4(0) 5(7) 5(6) - * CS 5(5) 5(4) 5(3) 5(2) 5(1) 5(0) 6(7) - * CS 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) 6(0) - * 7(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) - * - * the checksum is used to detect transmission errors and frame drops. - * - * synchronisation of received block is done by shifting the upper bit of each - * byte (bit 7) to a shift register. if the rigister has the first five bits - * (10000), this is used to find the sync. only if sync has been found, the - * current block of 9 received bytes are decrypted. before that the check - * sum is calculated. if it is incorrect the block is dropped. - * this will avoid loud noise due to corrupt encrypted data. - * - * if the last block is corrupt, the current decoded block is repeated - * until a valid block has been received. - */ - -/* - * some blowfish parts are taken from the - * crypto-api for faster implementation - */ - -static const u32 bf_pbox[16 + 2] = { - 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, - 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, - 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, - 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, - 0x9216d5d9, 0x8979fb1b, -}; - -static const u32 bf_sbox[256 * 4] = { - 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, - 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, - 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, - 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, - 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, - 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, - 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, - 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, - 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, - 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, - 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, - 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, - 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, - 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, - 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, - 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, - 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, - 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, - 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, - 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, - 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, - 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, - 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, - 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, - 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, - 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, - 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, - 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, - 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, - 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, - 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, - 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, - 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, - 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, - 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, - 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, - 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, - 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, - 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, - 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, - 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, - 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, - 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, - 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, - 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, - 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, - 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, - 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, - 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, - 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, - 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, - 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, - 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, - 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, - 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, - 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, - 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, - 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, - 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, - 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, - 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, - 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, - 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, - 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, - 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, - 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, - 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, - 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, - 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, - 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, - 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, - 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, - 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, - 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, - 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, - 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, - 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, - 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, - 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, - 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, - 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, - 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, - 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, - 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, - 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, - 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, - 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, - 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, - 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, - 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, - 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, - 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, - 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, - 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, - 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, - 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, - 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, - 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, - 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, - 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, - 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, - 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, - 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, - 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, - 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, - 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, - 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, - 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, - 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, - 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, - 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, - 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, - 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, - 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, - 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, - 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, - 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, - 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, - 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, - 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, - 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, - 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, - 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, - 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, - 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, - 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, - 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, - 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, - 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, - 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, - 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, - 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, - 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, - 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, - 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, - 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, - 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, - 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, - 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, - 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, - 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, - 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, - 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, - 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, - 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, - 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, - 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, - 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, - 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, - 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, - 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, - 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, - 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, - 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, - 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, - 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, - 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, - 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, - 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, - 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, - 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, - 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, - 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, - 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, - 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, - 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, - 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, - 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, - 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, - 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, - 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, - 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, - 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, - 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, - 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, - 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, - 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, - 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, - 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, - 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, - 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, - 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, - 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, - 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, - 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, - 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, - 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, - 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, - 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, - 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, - 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, - 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, - 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, - 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, - 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, - 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, - 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, - 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, - 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, - 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, - 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, - 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, - 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, - 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, - 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, - 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, - 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, - 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, - 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, - 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, - 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, - 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, - 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, - 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, - 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, - 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, - 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, - 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, - 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, - 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, - 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, - 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, - 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, - 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, - 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, - 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, - 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, - 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, - 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, - 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, - 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, - 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, - 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, - 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, - 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, - 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, - 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, - 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, - 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, - 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, - 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, - 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, - 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, - 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, - 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, - 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, - 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, - 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, - 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, - 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, - 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, - 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, - 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, - 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, - 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, - 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, -}; - -/* - * Round loop unrolling macros, S is a pointer to a S-Box array - * organized in 4 unsigned longs at a row. - */ -#define GET32_3(x) (((x) & 0xff)) -#define GET32_2(x) (((x) >> (8)) & (0xff)) -#define GET32_1(x) (((x) >> (16)) & (0xff)) -#define GET32_0(x) (((x) >> (24)) & (0xff)) - -#define bf_F(x) (((S[GET32_0(x)] + S[256 + GET32_1(x)]) ^ \ - S[512 + GET32_2(x)]) + S[768 + GET32_3(x)]) - -#define EROUND(a, b, n) do { b ^= P[n]; a ^= bf_F(b); } while (0) -#define DROUND(a, b, n) do { a ^= bf_F(b); b ^= P[n]; } while (0) - - -/* - * encrypt isdn data frame - * every block with 9 samples is encrypted - */ -void -dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len) -{ - int i = 0, j = dsp->bf_crypt_pos; - u8 *bf_data_in = dsp->bf_data_in; - u8 *bf_crypt_out = dsp->bf_crypt_out; - u32 *P = dsp->bf_p; - u32 *S = dsp->bf_s; - u32 yl, yr; - u32 cs; - u8 nibble; - - while (i < len) { - /* collect a block of 9 samples */ - if (j < 9) { - bf_data_in[j] = *data; - *data++ = bf_crypt_out[j++]; - i++; - continue; - } - j = 0; - /* transcode 9 samples xlaw to 8 bytes */ - yl = dsp_audio_law2seven[bf_data_in[0]]; - yl = (yl << 7) | dsp_audio_law2seven[bf_data_in[1]]; - yl = (yl << 7) | dsp_audio_law2seven[bf_data_in[2]]; - yl = (yl << 7) | dsp_audio_law2seven[bf_data_in[3]]; - nibble = dsp_audio_law2seven[bf_data_in[4]]; - yr = nibble; - yl = (yl << 4) | (nibble >> 3); - yr = (yr << 7) | dsp_audio_law2seven[bf_data_in[5]]; - yr = (yr << 7) | dsp_audio_law2seven[bf_data_in[6]]; - yr = (yr << 7) | dsp_audio_law2seven[bf_data_in[7]]; - yr = (yr << 7) | dsp_audio_law2seven[bf_data_in[8]]; - yr = (yr << 1) | (bf_data_in[0] & 1); - - /* fill unused bit with random noise of audio input */ - /* encrypt */ - - EROUND(yr, yl, 0); - EROUND(yl, yr, 1); - EROUND(yr, yl, 2); - EROUND(yl, yr, 3); - EROUND(yr, yl, 4); - EROUND(yl, yr, 5); - EROUND(yr, yl, 6); - EROUND(yl, yr, 7); - EROUND(yr, yl, 8); - EROUND(yl, yr, 9); - EROUND(yr, yl, 10); - EROUND(yl, yr, 11); - EROUND(yr, yl, 12); - EROUND(yl, yr, 13); - EROUND(yr, yl, 14); - EROUND(yl, yr, 15); - yl ^= P[16]; - yr ^= P[17]; - - /* calculate 3-bit checksumme */ - cs = yl ^ (yl >> 3) ^ (yl >> 6) ^ (yl >> 9) ^ (yl >> 12) ^ (yl >> 15) - ^ (yl >> 18) ^ (yl >> 21) ^ (yl >> 24) ^ (yl >> 27) ^ (yl >> 30) - ^ (yr << 2) ^ (yr >> 1) ^ (yr >> 4) ^ (yr >> 7) ^ (yr >> 10) - ^ (yr >> 13) ^ (yr >> 16) ^ (yr >> 19) ^ (yr >> 22) ^ (yr >> 25) - ^ (yr >> 28) ^ (yr >> 31); - - /* - * transcode 8 crypted bytes to 9 data bytes with sync - * and checksum information - */ - bf_crypt_out[0] = (yl >> 25) | 0x80; - bf_crypt_out[1] = (yl >> 18) & 0x7f; - bf_crypt_out[2] = (yl >> 11) & 0x7f; - bf_crypt_out[3] = (yl >> 4) & 0x7f; - bf_crypt_out[4] = ((yl << 3) & 0x78) | ((yr >> 29) & 0x07); - bf_crypt_out[5] = ((yr >> 22) & 0x7f) | ((cs << 5) & 0x80); - bf_crypt_out[6] = ((yr >> 15) & 0x7f) | ((cs << 6) & 0x80); - bf_crypt_out[7] = ((yr >> 8) & 0x7f) | (cs << 7); - bf_crypt_out[8] = yr; - } - - /* write current count */ - dsp->bf_crypt_pos = j; - -} - - -/* - * decrypt isdn data frame - * every block with 9 bytes is decrypted - */ -void -dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len) -{ - int i = 0; - u8 j = dsp->bf_decrypt_in_pos; - u8 k = dsp->bf_decrypt_out_pos; - u8 *bf_crypt_inring = dsp->bf_crypt_inring; - u8 *bf_data_out = dsp->bf_data_out; - u16 sync = dsp->bf_sync; - u32 *P = dsp->bf_p; - u32 *S = dsp->bf_s; - u32 yl, yr; - u8 nibble; - u8 cs, cs0, cs1, cs2; - - while (i < len) { - /* - * shift upper bit and rotate data to buffer ring - * send current decrypted data - */ - sync = (sync << 1) | ((*data) >> 7); - bf_crypt_inring[j++ & 15] = *data; - *data++ = bf_data_out[k++]; - i++; - if (k == 9) - k = 0; /* repeat if no sync has been found */ - /* check if not in sync */ - if ((sync & 0x1f0) != 0x100) - continue; - j -= 9; - /* transcode receive data to 64 bit block of encrypted data */ - yl = bf_crypt_inring[j++ & 15]; - yl = (yl << 7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ - yl = (yl << 7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ - yl = (yl << 7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ - nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ - yr = nibble; - yl = (yl << 4) | (nibble >> 3); - cs2 = bf_crypt_inring[j++ & 15]; - yr = (yr << 7) | (cs2 & 0x7f); - cs1 = bf_crypt_inring[j++ & 15]; - yr = (yr << 7) | (cs1 & 0x7f); - cs0 = bf_crypt_inring[j++ & 15]; - yr = (yr << 7) | (cs0 & 0x7f); - yr = (yr << 8) | bf_crypt_inring[j++ & 15]; - - /* calculate 3-bit checksumme */ - cs = yl ^ (yl >> 3) ^ (yl >> 6) ^ (yl >> 9) ^ (yl >> 12) ^ (yl >> 15) - ^ (yl >> 18) ^ (yl >> 21) ^ (yl >> 24) ^ (yl >> 27) ^ (yl >> 30) - ^ (yr << 2) ^ (yr >> 1) ^ (yr >> 4) ^ (yr >> 7) ^ (yr >> 10) - ^ (yr >> 13) ^ (yr >> 16) ^ (yr >> 19) ^ (yr >> 22) ^ (yr >> 25) - ^ (yr >> 28) ^ (yr >> 31); - - /* check if frame is valid */ - if ((cs & 0x7) != (((cs2 >> 5) & 4) | ((cs1 >> 6) & 2) | (cs0 >> 7))) { - if (dsp_debug & DEBUG_DSP_BLOWFISH) - printk(KERN_DEBUG - "DSP BLOWFISH: received corrupt frame, " - "checksumme is not correct\n"); - continue; - } - - /* decrypt */ - yr ^= P[17]; - yl ^= P[16]; - DROUND(yl, yr, 15); - DROUND(yr, yl, 14); - DROUND(yl, yr, 13); - DROUND(yr, yl, 12); - DROUND(yl, yr, 11); - DROUND(yr, yl, 10); - DROUND(yl, yr, 9); - DROUND(yr, yl, 8); - DROUND(yl, yr, 7); - DROUND(yr, yl, 6); - DROUND(yl, yr, 5); - DROUND(yr, yl, 4); - DROUND(yl, yr, 3); - DROUND(yr, yl, 2); - DROUND(yl, yr, 1); - DROUND(yr, yl, 0); - - /* transcode 8 crypted bytes to 9 sample bytes */ - bf_data_out[0] = dsp_audio_seven2law[(yl >> 25) & 0x7f]; - bf_data_out[1] = dsp_audio_seven2law[(yl >> 18) & 0x7f]; - bf_data_out[2] = dsp_audio_seven2law[(yl >> 11) & 0x7f]; - bf_data_out[3] = dsp_audio_seven2law[(yl >> 4) & 0x7f]; - bf_data_out[4] = dsp_audio_seven2law[((yl << 3) & 0x78) | - ((yr >> 29) & 0x07)]; - - bf_data_out[5] = dsp_audio_seven2law[(yr >> 22) & 0x7f]; - bf_data_out[6] = dsp_audio_seven2law[(yr >> 15) & 0x7f]; - bf_data_out[7] = dsp_audio_seven2law[(yr >> 8) & 0x7f]; - bf_data_out[8] = dsp_audio_seven2law[(yr >> 1) & 0x7f]; - k = 0; /* start with new decoded frame */ - } - - /* write current count and sync */ - dsp->bf_decrypt_in_pos = j; - dsp->bf_decrypt_out_pos = k; - dsp->bf_sync = sync; -} - - -/* used to encrypt S and P boxes */ -static inline void -encrypt_block(const u32 *P, const u32 *S, u32 *dst, u32 *src) -{ - u32 yl = src[0]; - u32 yr = src[1]; - - EROUND(yr, yl, 0); - EROUND(yl, yr, 1); - EROUND(yr, yl, 2); - EROUND(yl, yr, 3); - EROUND(yr, yl, 4); - EROUND(yl, yr, 5); - EROUND(yr, yl, 6); - EROUND(yl, yr, 7); - EROUND(yr, yl, 8); - EROUND(yl, yr, 9); - EROUND(yr, yl, 10); - EROUND(yl, yr, 11); - EROUND(yr, yl, 12); - EROUND(yl, yr, 13); - EROUND(yr, yl, 14); - EROUND(yl, yr, 15); - - yl ^= P[16]; - yr ^= P[17]; - - dst[0] = yr; - dst[1] = yl; -} - -/* - * initialize the dsp for encryption and decryption using the same key - * Calculates the blowfish S and P boxes for encryption and decryption. - * The margin of keylen must be 4-56 bytes. - * returns 0 if ok. - */ -int -dsp_bf_init(struct dsp *dsp, const u8 *key, uint keylen) -{ - short i, j, count; - u32 data[2], temp; - u32 *P = (u32 *)dsp->bf_p; - u32 *S = (u32 *)dsp->bf_s; - - if (keylen < 4 || keylen > 56) - return 1; - - /* Set dsp states */ - i = 0; - while (i < 9) { - dsp->bf_crypt_out[i] = 0xff; - dsp->bf_data_out[i] = dsp_silence; - i++; - } - dsp->bf_crypt_pos = 0; - dsp->bf_decrypt_in_pos = 0; - dsp->bf_decrypt_out_pos = 0; - dsp->bf_sync = 0x1ff; - dsp->bf_enable = 1; - - /* Copy the initialization s-boxes */ - for (i = 0, count = 0; i < 256; i++) - for (j = 0; j < 4; j++, count++) - S[count] = bf_sbox[count]; - - /* Set the p-boxes */ - for (i = 0; i < 16 + 2; i++) - P[i] = bf_pbox[i]; - - /* Actual subkey generation */ - for (j = 0, i = 0; i < 16 + 2; i++) { - temp = (((u32)key[j] << 24) | - ((u32)key[(j + 1) % keylen] << 16) | - ((u32)key[(j + 2) % keylen] << 8) | - ((u32)key[(j + 3) % keylen])); - - P[i] = P[i] ^ temp; - j = (j + 4) % keylen; - } - - data[0] = 0x00000000; - data[1] = 0x00000000; - - for (i = 0; i < 16 + 2; i += 2) { - encrypt_block(P, S, data, data); - - P[i] = data[0]; - P[i + 1] = data[1]; - } - - for (i = 0; i < 4; i++) { - for (j = 0, count = i * 256; j < 256; j += 2, count += 2) { - encrypt_block(P, S, data, data); - - S[count] = data[0]; - S[count + 1] = data[1]; - } - } - - return 0; -} - - -/* - * turn encryption off - */ -void -dsp_bf_cleanup(struct dsp *dsp) -{ - dsp->bf_enable = 0; -} diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c deleted file mode 100644 index d5eb2349c414..000000000000 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ /dev/null @@ -1,1949 +0,0 @@ -/* - * Audio crossconnecting/conferrencing (hardware level). - * - * Copyright 2002 by Andreas Eversberg (jolly@eversberg.eu) - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -/* - * The process of adding and removing parties to/from a conference: - * - * There is a chain of struct dsp_conf which has one or more members in a chain - * of struct dsp_conf_member. - * - * After a party is added, the conference is checked for hardware capability. - * Also if a party is removed, the conference is checked again. - * - * There are 3 different solutions: -1 = software, 0 = hardware-crossconnect - * 1-n = hardware-conference. The n will give the conference number. - * - * Depending on the change after removal or insertion of a party, hardware - * commands are given. - * - * The current solution is stored within the struct dsp_conf entry. - */ - -/* - * HOW THE CMX WORKS: - * - * There are 3 types of interaction: One member is alone, in this case only - * data flow from upper to lower layer is done. - * Two members will also exchange their data so they are crossconnected. - * Three or more members will be added in a conference and will hear each - * other but will not receive their own speech (echo) if not enabled. - * - * Features of CMX are: - * - Crossconnecting or even conference, if more than two members are together. - * - Force mixing of transmit data with other crossconnect/conference members. - * - Echo generation to benchmark the delay of audio processing. - * - Use hardware to minimize cpu load, disable FIFO load and minimize delay. - * - Dejittering and clock generation. - * - * There are 2 buffers: - * - * - * RX-Buffer - * R W - * | | - * ----------------+-------------+------------------- - * - * The rx-buffer is a ring buffer used to store the received data for each - * individual member. This is only the case if data needs to be dejittered - * or in case of a conference where different clocks require reclocking. - * The transmit-clock (R) will read the buffer. - * If the clock overruns the write-pointer, we will have a buffer underrun. - * If the write pointer always has a certain distance from the transmit- - * clock, we will have a delay. The delay will dynamically be increased and - * reduced. - * - * - * TX-Buffer - * R W - * | | - * -----------------+--------+----------------------- - * - * The tx-buffer is a ring buffer to queue the transmit data from user space - * until it will be mixed or sent. There are two pointers, R and W. If the write - * pointer W would reach or overrun R, the buffer would overrun. In this case - * (some) data is dropped so that it will not overrun. - * Additionally a dynamic dejittering can be enabled. this allows data from - * user space that have jitter and different clock source. - * - * - * Clock: - * - * A Clock is not required, if the data source has exactly one clock. In this - * case the data source is forwarded to the destination. - * - * A Clock is required, because the data source - * - has multiple clocks. - * - has no usable clock due to jitter or packet loss (VoIP). - * In this case the system's clock is used. The clock resolution depends on - * the jiffy resolution. - * - * If a member joins a conference: - * - * - If a member joins, its rx_buff is set to silence and change read pointer - * to transmit clock. - * - * The procedure of received data from card is explained in cmx_receive. - * The procedure of received data from user space is explained in cmx_transmit. - * The procedure of transmit data to card is cmx_send. - * - * - * Interaction with other features: - * - * DTMF: - * DTMF decoding is done before the data is crossconnected. - * - * Volume change: - * Changing rx-volume is done before the data is crossconnected. The tx-volume - * must be changed whenever data is transmitted to the card by the cmx. - * - * Tones: - * If a tone is enabled, it will be processed whenever data is transmitted to - * the card. It will replace the tx-data from the user space. - * If tones are generated by hardware, this conference member is removed for - * this time. - * - * Disable rx-data: - * If cmx is realized in hardware, rx data will be disabled if requested by - * the upper layer. If dtmf decoding is done by software and enabled, rx data - * will not be disabled but blocked to the upper layer. - * - * HFC conference engine: - * If it is possible to realize all features using hardware, hardware will be - * used if not forbidden by control command. Disabling rx-data provides - * absolutely traffic free audio processing. (except for the quick 1-frame - * upload of a tone loop, only once for a new tone) - * - */ - -/* delay.h is required for hw_lock.h */ - -#include -#include -#include -#include -#include "core.h" -#include "dsp.h" -/* - * debugging of multi party conference, - * by using conference even with two members - */ - -/* #define CMX_CONF_DEBUG */ - -/*#define CMX_DEBUG * massive read/write pointer output */ -/*#define CMX_DELAY_DEBUG * gives rx-buffer delay overview */ -/*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */ - -/* - * debug cmx memory structure - */ -void -dsp_cmx_debug(struct dsp *dsp) -{ - struct dsp_conf *conf; - struct dsp_conf_member *member; - struct dsp *odsp; - - printk(KERN_DEBUG "-----Current DSP\n"); - list_for_each_entry(odsp, &dsp_ilist, list) { - printk(KERN_DEBUG "* %s hardecho=%d softecho=%d txmix=%d", - odsp->name, odsp->echo.hardware, odsp->echo.software, - odsp->tx_mix); - if (odsp->conf) - printk(" (Conf %d)", odsp->conf->id); - if (dsp == odsp) - printk(" *this*"); - printk("\n"); - } - printk(KERN_DEBUG "-----Current Conf:\n"); - list_for_each_entry(conf, &conf_ilist, list) { - printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf); - list_for_each_entry(member, &conf->mlist, list) { - printk(KERN_DEBUG - " - member = %s (slot_tx %d, bank_tx %d, " - "slot_rx %d, bank_rx %d hfc_conf %d " - "tx_data %d rx_is_off %d)%s\n", - member->dsp->name, member->dsp->pcm_slot_tx, - member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx, - member->dsp->pcm_bank_rx, member->dsp->hfc_conf, - member->dsp->tx_data, member->dsp->rx_is_off, - (member->dsp == dsp) ? " *this*" : ""); - } - } - printk(KERN_DEBUG "-----end\n"); -} - -/* - * search conference - */ -static struct dsp_conf * -dsp_cmx_search_conf(u32 id) -{ - struct dsp_conf *conf; - - if (!id) { - printk(KERN_WARNING "%s: conference ID is 0.\n", __func__); - return NULL; - } - - /* search conference */ - list_for_each_entry(conf, &conf_ilist, list) - if (conf->id == id) - return conf; - - return NULL; -} - - -/* - * add member to conference - */ -static int -dsp_cmx_add_conf_member(struct dsp *dsp, struct dsp_conf *conf) -{ - struct dsp_conf_member *member; - - if (!conf || !dsp) { - printk(KERN_WARNING "%s: conf or dsp is 0.\n", __func__); - return -EINVAL; - } - if (dsp->member) { - printk(KERN_WARNING "%s: dsp is already member in a conf.\n", - __func__); - return -EINVAL; - } - - if (dsp->conf) { - printk(KERN_WARNING "%s: dsp is already in a conf.\n", - __func__); - return -EINVAL; - } - - member = kzalloc_obj(struct dsp_conf_member, GFP_ATOMIC); - if (!member) { - printk(KERN_ERR "kzalloc struct dsp_conf_member failed\n"); - return -ENOMEM; - } - member->dsp = dsp; - /* clear rx buffer */ - memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); - dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */ - dsp->rx_W = 0; - dsp->rx_R = 0; - - list_add_tail(&member->list, &conf->mlist); - - dsp->conf = conf; - dsp->member = member; - - return 0; -} - - -/* - * del member from conference - */ -int -dsp_cmx_del_conf_member(struct dsp *dsp) -{ - struct dsp_conf_member *member; - - if (!dsp) { - printk(KERN_WARNING "%s: dsp is 0.\n", - __func__); - return -EINVAL; - } - - if (!dsp->conf) { - printk(KERN_WARNING "%s: dsp is not in a conf.\n", - __func__); - return -EINVAL; - } - - if (list_empty(&dsp->conf->mlist)) { - printk(KERN_WARNING "%s: dsp has linked an empty conf.\n", - __func__); - return -EINVAL; - } - - /* find us in conf */ - list_for_each_entry(member, &dsp->conf->mlist, list) { - if (member->dsp == dsp) { - list_del(&member->list); - dsp->conf = NULL; - dsp->member = NULL; - kfree(member); - return 0; - } - } - printk(KERN_WARNING - "%s: dsp is not present in its own conf_member list.\n", - __func__); - - return -EINVAL; -} - - -/* - * new conference - */ -static struct dsp_conf -*dsp_cmx_new_conf(u32 id) -{ - struct dsp_conf *conf; - - if (!id) { - printk(KERN_WARNING "%s: id is 0.\n", - __func__); - return NULL; - } - - conf = kzalloc_obj(struct dsp_conf, GFP_ATOMIC); - if (!conf) { - printk(KERN_ERR "kzalloc struct dsp_conf failed\n"); - return NULL; - } - INIT_LIST_HEAD(&conf->mlist); - conf->id = id; - - list_add_tail(&conf->list, &conf_ilist); - - return conf; -} - - -/* - * del conference - */ -int -dsp_cmx_del_conf(struct dsp_conf *conf) -{ - if (!conf) { - printk(KERN_WARNING "%s: conf is null.\n", - __func__); - return -EINVAL; - } - - if (!list_empty(&conf->mlist)) { - printk(KERN_WARNING "%s: conf not empty.\n", - __func__); - return -EINVAL; - } - list_del(&conf->list); - kfree(conf); - - return 0; -} - - -/* - * send HW message to hfc card - */ -static void -dsp_cmx_hw_message(struct dsp *dsp, u32 message, u32 param1, u32 param2, - u32 param3, u32 param4) -{ - struct mISDN_ctrl_req cq; - - memset(&cq, 0, sizeof(cq)); - cq.op = message; - cq.p1 = param1 | (param2 << 8); - cq.p2 = param3 | (param4 << 8); - if (dsp->ch.peer) - dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq); -} - - -/* - * do hardware update and set the software/hardware flag - * - * either a conference or a dsp instance can be given - * if only dsp instance is given, the instance is not associated with a conf - * and therefore removed. if a conference is given, the dsp is expected to - * be member of that conference. - */ -void -dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp) -{ - struct dsp_conf_member *member, *nextm; - struct dsp *finddsp; - int memb = 0, i, ii, i1, i2; - int freeunits[8]; - u_char freeslots[256]; - int same_hfc = -1, same_pcm = -1, current_conf = -1, - all_conf = 1, tx_data = 0; - - /* dsp gets updated (no conf) */ - if (!conf) { - if (!dsp) - return; - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "%s checking dsp %s\n", - __func__, dsp->name); - one_member: - /* remove HFC conference if enabled */ - if (dsp->hfc_conf >= 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s removing %s from HFC conf %d " - "because dsp is split\n", __func__, - dsp->name, dsp->hfc_conf); - dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_CONF_SPLIT, - 0, 0, 0, 0); - dsp->hfc_conf = -1; - } - /* process hw echo */ - if (dsp->features.pcm_banks < 1) - return; - if (!dsp->echo.software && !dsp->echo.hardware) { - /* NO ECHO: remove PCM slot if assigned */ - if (dsp->pcm_slot_tx >= 0 || dsp->pcm_slot_rx >= 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "%s removing %s from" - " PCM slot %d (TX) %d (RX) because" - " dsp is split (no echo)\n", - __func__, dsp->name, - dsp->pcm_slot_tx, dsp->pcm_slot_rx); - dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_DISC, - 0, 0, 0, 0); - dsp->pcm_slot_tx = -1; - dsp->pcm_bank_tx = -1; - dsp->pcm_slot_rx = -1; - dsp->pcm_bank_rx = -1; - } - return; - } - /* echo is enabled, find out if we use soft or hardware */ - dsp->echo.software = dsp->tx_data; - dsp->echo.hardware = 0; - /* ECHO: already echo */ - if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_rx < 0 && - dsp->pcm_bank_tx == 2 && dsp->pcm_bank_rx == 2) { - dsp->echo.hardware = 1; - return; - } - /* ECHO: if slot already assigned */ - if (dsp->pcm_slot_tx >= 0) { - dsp->pcm_slot_rx = dsp->pcm_slot_tx; - dsp->pcm_bank_tx = 2; /* 2 means loop */ - dsp->pcm_bank_rx = 2; - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s refresh %s for echo using slot %d\n", - __func__, dsp->name, - dsp->pcm_slot_tx); - dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, - dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); - dsp->echo.hardware = 1; - return; - } - /* ECHO: find slot */ - dsp->pcm_slot_tx = -1; - dsp->pcm_slot_rx = -1; - memset(freeslots, 1, sizeof(freeslots)); - list_for_each_entry(finddsp, &dsp_ilist, list) { - if (finddsp->features.pcm_id == dsp->features.pcm_id) { - if (finddsp->pcm_slot_rx >= 0 && - finddsp->pcm_slot_rx < sizeof(freeslots)) - freeslots[finddsp->pcm_slot_rx] = 0; - if (finddsp->pcm_slot_tx >= 0 && - finddsp->pcm_slot_tx < sizeof(freeslots)) - freeslots[finddsp->pcm_slot_tx] = 0; - } - } - i = 0; - ii = dsp->features.pcm_slots; - while (i < ii) { - if (freeslots[i]) - break; - i++; - } - if (i == ii) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s no slot available for echo\n", - __func__); - /* no more slots available */ - dsp->echo.software = 1; - return; - } - /* assign free slot */ - dsp->pcm_slot_tx = i; - dsp->pcm_slot_rx = i; - dsp->pcm_bank_tx = 2; /* loop */ - dsp->pcm_bank_rx = 2; - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s assign echo for %s using slot %d\n", - __func__, dsp->name, dsp->pcm_slot_tx); - dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, - dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); - dsp->echo.hardware = 1; - return; - } - - /* conf gets updated (all members) */ - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "%s checking conference %d\n", - __func__, conf->id); - - if (list_empty(&conf->mlist)) { - printk(KERN_ERR "%s: conference without members\n", - __func__); - return; - } - member = list_entry(conf->mlist.next, struct dsp_conf_member, list); - same_hfc = member->dsp->features.hfc_id; - same_pcm = member->dsp->features.pcm_id; - /* check all members in our conference */ - list_for_each_entry(member, &conf->mlist, list) { - /* check if member uses mixing */ - if (member->dsp->tx_mix) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "tx_mix is turned on\n", __func__, - member->dsp->name); - conf_software: - list_for_each_entry(member, &conf->mlist, list) { - dsp = member->dsp; - /* remove HFC conference if enabled */ - if (dsp->hfc_conf >= 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s removing %s from HFC " - "conf %d because not " - "possible with hardware\n", - __func__, - dsp->name, - dsp->hfc_conf); - dsp_cmx_hw_message(dsp, - MISDN_CTRL_HFC_CONF_SPLIT, - 0, 0, 0, 0); - dsp->hfc_conf = -1; - } - /* remove PCM slot if assigned */ - if (dsp->pcm_slot_tx >= 0 || - dsp->pcm_slot_rx >= 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "%s removing " - "%s from PCM slot %d (TX)" - " slot %d (RX) because not" - " possible with hardware\n", - __func__, - dsp->name, - dsp->pcm_slot_tx, - dsp->pcm_slot_rx); - dsp_cmx_hw_message(dsp, - MISDN_CTRL_HFC_PCM_DISC, - 0, 0, 0, 0); - dsp->pcm_slot_tx = -1; - dsp->pcm_bank_tx = -1; - dsp->pcm_slot_rx = -1; - dsp->pcm_bank_rx = -1; - } - } - conf->hardware = 0; - conf->software = 1; - return; - } - /* check if member has echo turned on */ - if (member->dsp->echo.hardware || member->dsp->echo.software) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "echo is turned on\n", __func__, - member->dsp->name); - goto conf_software; - } - /* check if member has tx_mix turned on */ - if (member->dsp->tx_mix) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "tx_mix is turned on\n", - __func__, member->dsp->name); - goto conf_software; - } - /* check if member changes volume at an not suppoted level */ - if (member->dsp->tx_volume) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "tx_volume is changed\n", - __func__, member->dsp->name); - goto conf_software; - } - if (member->dsp->rx_volume) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "rx_volume is changed\n", - __func__, member->dsp->name); - goto conf_software; - } - /* check if tx-data turned on */ - if (member->dsp->tx_data) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s tx_data is turned on\n", - __func__, member->dsp->name); - tx_data = 1; - } - /* check if pipeline exists */ - if (member->dsp->pipeline.inuse) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "pipeline exists\n", __func__, - member->dsp->name); - goto conf_software; - } - /* check if encryption is enabled */ - if (member->dsp->bf_enable) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "%s dsp %s cannot form a " - "conf, because encryption is enabled\n", - __func__, member->dsp->name); - goto conf_software; - } - /* check if member is on a card with PCM support */ - if (member->dsp->features.pcm_id < 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "dsp has no PCM bus\n", - __func__, member->dsp->name); - goto conf_software; - } - /* check if relations are on the same PCM bus */ - if (member->dsp->features.pcm_id != same_pcm) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s cannot form a conf, because " - "dsp is on a different PCM bus than the " - "first dsp\n", - __func__, member->dsp->name); - goto conf_software; - } - /* determine if members are on the same hfc chip */ - if (same_hfc != member->dsp->features.hfc_id) - same_hfc = -1; - /* if there are members already in a conference */ - if (current_conf < 0 && member->dsp->hfc_conf >= 0) - current_conf = member->dsp->hfc_conf; - /* if any member is not in a conference */ - if (member->dsp->hfc_conf < 0) - all_conf = 0; - - memb++; - } - - /* if no member, this is an error */ - if (memb < 1) - return; - - /* one member */ - if (memb == 1) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s conf %d cannot form a HW conference, " - "because dsp is alone\n", __func__, conf->id); - conf->hardware = 0; - conf->software = 0; - member = list_entry(conf->mlist.next, struct dsp_conf_member, - list); - dsp = member->dsp; - goto one_member; - } - - /* - * ok, now we are sure that all members are on the same pcm. - * now we will see if we have only two members, so we can do - * crossconnections, which don't have any limitations. - */ - - /* if we have only two members */ - if (memb == 2) { - member = list_entry(conf->mlist.next, struct dsp_conf_member, - list); - nextm = list_entry(member->list.next, struct dsp_conf_member, - list); - /* remove HFC conference if enabled */ - if (member->dsp->hfc_conf >= 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s removing %s from HFC conf %d because " - "two parties require only a PCM slot\n", - __func__, member->dsp->name, - member->dsp->hfc_conf); - dsp_cmx_hw_message(member->dsp, - MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); - member->dsp->hfc_conf = -1; - } - if (nextm->dsp->hfc_conf >= 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s removing %s from HFC conf %d because " - "two parties require only a PCM slot\n", - __func__, nextm->dsp->name, - nextm->dsp->hfc_conf); - dsp_cmx_hw_message(nextm->dsp, - MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); - nextm->dsp->hfc_conf = -1; - } - /* if members have two banks (and not on the same chip) */ - if (member->dsp->features.pcm_banks > 1 && - nextm->dsp->features.pcm_banks > 1 && - member->dsp->features.hfc_id != - nextm->dsp->features.hfc_id) { - /* if both members have same slots with crossed banks */ - if (member->dsp->pcm_slot_tx >= 0 && - member->dsp->pcm_slot_rx >= 0 && - nextm->dsp->pcm_slot_tx >= 0 && - nextm->dsp->pcm_slot_rx >= 0 && - nextm->dsp->pcm_slot_tx == - member->dsp->pcm_slot_rx && - nextm->dsp->pcm_slot_rx == - member->dsp->pcm_slot_tx && - nextm->dsp->pcm_slot_tx == - member->dsp->pcm_slot_tx && - member->dsp->pcm_bank_tx != - member->dsp->pcm_bank_rx && - nextm->dsp->pcm_bank_tx != - nextm->dsp->pcm_bank_rx) { - /* all members have same slot */ - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s & %s stay joined on " - "PCM slot %d bank %d (TX) bank %d " - "(RX) (on different chips)\n", - __func__, - member->dsp->name, - nextm->dsp->name, - member->dsp->pcm_slot_tx, - member->dsp->pcm_bank_tx, - member->dsp->pcm_bank_rx); - conf->hardware = 1; - conf->software = tx_data; - return; - } - /* find a new slot */ - memset(freeslots, 1, sizeof(freeslots)); - list_for_each_entry(dsp, &dsp_ilist, list) { - if (dsp != member->dsp && - dsp != nextm->dsp && - member->dsp->features.pcm_id == - dsp->features.pcm_id) { - if (dsp->pcm_slot_rx >= 0 && - dsp->pcm_slot_rx < - sizeof(freeslots)) - freeslots[dsp->pcm_slot_rx] = 0; - if (dsp->pcm_slot_tx >= 0 && - dsp->pcm_slot_tx < - sizeof(freeslots)) - freeslots[dsp->pcm_slot_tx] = 0; - } - } - i = 0; - ii = member->dsp->features.pcm_slots; - while (i < ii) { - if (freeslots[i]) - break; - i++; - } - if (i == ii) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s no slot available for " - "%s & %s\n", __func__, - member->dsp->name, - nextm->dsp->name); - /* no more slots available */ - goto conf_software; - } - /* assign free slot */ - member->dsp->pcm_slot_tx = i; - member->dsp->pcm_slot_rx = i; - nextm->dsp->pcm_slot_tx = i; - nextm->dsp->pcm_slot_rx = i; - member->dsp->pcm_bank_rx = 0; - member->dsp->pcm_bank_tx = 1; - nextm->dsp->pcm_bank_rx = 1; - nextm->dsp->pcm_bank_tx = 0; - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s adding %s & %s to new PCM slot %d " - "(TX and RX on different chips) because " - "both members have not same slots\n", - __func__, - member->dsp->name, - nextm->dsp->name, - member->dsp->pcm_slot_tx); - dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, - member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, - member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); - dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, - nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, - nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); - conf->hardware = 1; - conf->software = tx_data; - return; - /* if members have one bank (or on the same chip) */ - } else { - /* if both members have different crossed slots */ - if (member->dsp->pcm_slot_tx >= 0 && - member->dsp->pcm_slot_rx >= 0 && - nextm->dsp->pcm_slot_tx >= 0 && - nextm->dsp->pcm_slot_rx >= 0 && - nextm->dsp->pcm_slot_tx == - member->dsp->pcm_slot_rx && - nextm->dsp->pcm_slot_rx == - member->dsp->pcm_slot_tx && - member->dsp->pcm_slot_tx != - member->dsp->pcm_slot_rx && - member->dsp->pcm_bank_tx == 0 && - member->dsp->pcm_bank_rx == 0 && - nextm->dsp->pcm_bank_tx == 0 && - nextm->dsp->pcm_bank_rx == 0) { - /* all members have same slot */ - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s dsp %s & %s stay joined on PCM " - "slot %d (TX) %d (RX) on same chip " - "or one bank PCM)\n", __func__, - member->dsp->name, - nextm->dsp->name, - member->dsp->pcm_slot_tx, - member->dsp->pcm_slot_rx); - conf->hardware = 1; - conf->software = tx_data; - return; - } - /* find two new slot */ - memset(freeslots, 1, sizeof(freeslots)); - list_for_each_entry(dsp, &dsp_ilist, list) { - if (dsp != member->dsp && - dsp != nextm->dsp && - member->dsp->features.pcm_id == - dsp->features.pcm_id) { - if (dsp->pcm_slot_rx >= 0 && - dsp->pcm_slot_rx < - sizeof(freeslots)) - freeslots[dsp->pcm_slot_rx] = 0; - if (dsp->pcm_slot_tx >= 0 && - dsp->pcm_slot_tx < - sizeof(freeslots)) - freeslots[dsp->pcm_slot_tx] = 0; - } - } - i1 = 0; - ii = member->dsp->features.pcm_slots; - while (i1 < ii) { - if (freeslots[i1]) - break; - i1++; - } - if (i1 == ii) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s no slot available " - "for %s & %s\n", __func__, - member->dsp->name, - nextm->dsp->name); - /* no more slots available */ - goto conf_software; - } - i2 = i1 + 1; - while (i2 < ii) { - if (freeslots[i2]) - break; - i2++; - } - if (i2 == ii) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s no slot available " - "for %s & %s\n", - __func__, - member->dsp->name, - nextm->dsp->name); - /* no more slots available */ - goto conf_software; - } - /* assign free slots */ - member->dsp->pcm_slot_tx = i1; - member->dsp->pcm_slot_rx = i2; - nextm->dsp->pcm_slot_tx = i2; - nextm->dsp->pcm_slot_rx = i1; - member->dsp->pcm_bank_rx = 0; - member->dsp->pcm_bank_tx = 0; - nextm->dsp->pcm_bank_rx = 0; - nextm->dsp->pcm_bank_tx = 0; - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s adding %s & %s to new PCM slot %d " - "(TX) %d (RX) on same chip or one bank " - "PCM, because both members have not " - "crossed slots\n", __func__, - member->dsp->name, - nextm->dsp->name, - member->dsp->pcm_slot_tx, - member->dsp->pcm_slot_rx); - dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, - member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, - member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); - dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, - nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, - nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); - conf->hardware = 1; - conf->software = tx_data; - return; - } - } - - /* - * if we have more than two, we may check if we have a conference - * unit available on the chip. also all members must be on the same - */ - - /* if not the same HFC chip */ - if (same_hfc < 0) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s conference %d cannot be formed, because " - "members are on different chips or not " - "on HFC chip\n", - __func__, conf->id); - goto conf_software; - } - - /* for more than two members.. */ - - /* if all members already have the same conference */ - if (all_conf) { - conf->hardware = 1; - conf->software = tx_data; - return; - } - - /* - * if there is an existing conference, but not all members have joined - */ - if (current_conf >= 0) { - join_members: - list_for_each_entry(member, &conf->mlist, list) { - /* if no conference engine on our chip, change to - * software */ - if (!member->dsp->features.hfc_conf) - goto conf_software; - /* in case of hdlc, change to software */ - if (member->dsp->hdlc) - goto conf_software; - /* join to current conference */ - if (member->dsp->hfc_conf == current_conf) - continue; - /* get a free timeslot first */ - memset(freeslots, 1, sizeof(freeslots)); - list_for_each_entry(dsp, &dsp_ilist, list) { - /* - * not checking current member, because - * slot will be overwritten. - */ - if ( - dsp != member->dsp && - /* dsp must be on the same PCM */ - member->dsp->features.pcm_id == - dsp->features.pcm_id) { - /* dsp must be on a slot */ - if (dsp->pcm_slot_tx >= 0 && - dsp->pcm_slot_tx < - sizeof(freeslots)) - freeslots[dsp->pcm_slot_tx] = 0; - if (dsp->pcm_slot_rx >= 0 && - dsp->pcm_slot_rx < - sizeof(freeslots)) - freeslots[dsp->pcm_slot_rx] = 0; - } - } - i = 0; - ii = member->dsp->features.pcm_slots; - while (i < ii) { - if (freeslots[i]) - break; - i++; - } - if (i == ii) { - /* no more slots available */ - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s conference %d cannot be formed," - " because no slot free\n", - __func__, conf->id); - goto conf_software; - } - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s changing dsp %s to HW conference " - "%d slot %d\n", __func__, - member->dsp->name, current_conf, i); - /* assign free slot & set PCM & join conf */ - member->dsp->pcm_slot_tx = i; - member->dsp->pcm_slot_rx = i; - member->dsp->pcm_bank_tx = 2; /* loop */ - member->dsp->pcm_bank_rx = 2; - member->dsp->hfc_conf = current_conf; - dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, - i, 2, i, 2); - dsp_cmx_hw_message(member->dsp, - MISDN_CTRL_HFC_CONF_JOIN, current_conf, 0, 0, 0); - } - conf->hardware = 1; - conf->software = tx_data; - return; - } - - /* - * no member is in a conference yet, so we find a free one - */ - memset(freeunits, 1, sizeof(freeunits)); - list_for_each_entry(dsp, &dsp_ilist, list) { - /* dsp must be on the same chip */ - if (dsp->features.hfc_id == same_hfc && - /* dsp must have joined a HW conference */ - dsp->hfc_conf >= 0 && - /* slot must be within range */ - dsp->hfc_conf < 8) - freeunits[dsp->hfc_conf] = 0; - } - i = 0; - ii = 8; - while (i < ii) { - if (freeunits[i]) - break; - i++; - } - if (i == ii) { - /* no more conferences available */ - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "%s conference %d cannot be formed, because " - "no conference number free\n", - __func__, conf->id); - goto conf_software; - } - /* join all members */ - current_conf = i; - goto join_members; -} - - -/* - * conf_id != 0: join or change conference - * conf_id == 0: split from conference if not already - */ -int -dsp_cmx_conf(struct dsp *dsp, u32 conf_id) -{ - int err; - struct dsp_conf *conf; - struct dsp_conf_member *member; - - /* if conference doesn't change */ - if (dsp->conf_id == conf_id) - return 0; - - /* first remove us from current conf */ - if (dsp->conf_id) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "removing us from conference %d\n", - dsp->conf->id); - /* remove us from conf */ - conf = dsp->conf; - err = dsp_cmx_del_conf_member(dsp); - if (err) - return err; - dsp->conf_id = 0; - - /* update hardware */ - dsp_cmx_hardware(NULL, dsp); - - /* conf now empty? */ - if (list_empty(&conf->mlist)) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "conference is empty, so we remove it.\n"); - err = dsp_cmx_del_conf(conf); - if (err) - return err; - } else { - /* update members left on conf */ - dsp_cmx_hardware(conf, NULL); - } - } - - /* if split */ - if (!conf_id) - return 0; - - /* now add us to conf */ - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG "searching conference %d\n", - conf_id); - conf = dsp_cmx_search_conf(conf_id); - if (!conf) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "conference doesn't exist yet, creating.\n"); - /* the conference doesn't exist, so we create */ - conf = dsp_cmx_new_conf(conf_id); - if (!conf) - return -EINVAL; - } else if (!list_empty(&conf->mlist)) { - member = list_entry(conf->mlist.next, struct dsp_conf_member, - list); - if (dsp->hdlc && !member->dsp->hdlc) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "cannot join transparent conference.\n"); - return -EINVAL; - } - if (!dsp->hdlc && member->dsp->hdlc) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "cannot join hdlc conference.\n"); - return -EINVAL; - } - } - /* add conference member */ - err = dsp_cmx_add_conf_member(dsp, conf); - if (err) - return err; - dsp->conf_id = conf_id; - - /* if we are alone, we do nothing! */ - if (list_empty(&conf->mlist)) { - if (dsp_debug & DEBUG_DSP_CMX) - printk(KERN_DEBUG - "we are alone in this conference, so exit.\n"); - /* update hardware */ - dsp_cmx_hardware(NULL, dsp); - return 0; - } - - /* update members on conf */ - dsp_cmx_hardware(conf, NULL); - - return 0; -} - -#ifdef CMX_DELAY_DEBUG -int delaycount; -static void -showdelay(struct dsp *dsp, int samples, int delay) -{ - char bar[] = "--------------------------------------------------|"; - int sdelay; - - delaycount += samples; - if (delaycount < 8000) - return; - delaycount = 0; - - sdelay = delay * 50 / (dsp_poll << 2); - - printk(KERN_DEBUG "DELAY (%s) %3d >%s\n", dsp->name, delay, - sdelay > 50 ? "..." : bar + 50 - sdelay); -} -#endif - -/* - * audio data is received from card - */ -void -dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) -{ - u8 *d, *p; - int len = skb->len; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int w, i, ii; - - /* check if we have sompen */ - if (len < 1) - return; - - /* half of the buffer should be larger than maximum packet size */ - if (len >= CMX_BUFF_HALF) { - printk(KERN_ERR - "%s line %d: packet from card is too large (%d bytes). " - "please make card send smaller packets OR increase " - "CMX_BUFF_SIZE\n", __FILE__, __LINE__, len); - return; - } - - /* - * initialize pointers if not already - - * also add delay if requested by PH_SIGNAL - */ - if (dsp->rx_init) { - dsp->rx_init = 0; - if (dsp->features.unordered) { - dsp->rx_R = (hh->id & CMX_BUFF_MASK); - if (dsp->cmx_delay) - dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) - & CMX_BUFF_MASK; - else - dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1)) - & CMX_BUFF_MASK; - } else { - dsp->rx_R = 0; - if (dsp->cmx_delay) - dsp->rx_W = dsp->cmx_delay; - else - dsp->rx_W = dsp_poll >> 1; - } - } - /* if frame contains time code, write directly */ - if (dsp->features.unordered) { - dsp->rx_W = (hh->id & CMX_BUFF_MASK); - /* printk(KERN_DEBUG "%s %08x\n", dsp->name, hh->id); */ - } - /* - * if we underrun (or maybe overrun), - * we set our new read pointer, and write silence to buffer - */ - if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) { - if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG - "cmx_receive(dsp=%lx): UNDERRUN (or overrun the " - "maximum delay), adjusting read pointer! " - "(inst %s)\n", (u_long)dsp, dsp->name); - /* flush rx buffer and set delay to dsp_poll / 2 */ - if (dsp->features.unordered) { - dsp->rx_R = (hh->id & CMX_BUFF_MASK); - if (dsp->cmx_delay) - dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) - & CMX_BUFF_MASK; - else - dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1)) - & CMX_BUFF_MASK; - } else { - dsp->rx_R = 0; - if (dsp->cmx_delay) - dsp->rx_W = dsp->cmx_delay; - else - dsp->rx_W = dsp_poll >> 1; - } - memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); - } - /* if we have reached double delay, jump back to middle */ - if (dsp->cmx_delay) - if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >= - (dsp->cmx_delay << 1)) { - if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG - "cmx_receive(dsp=%lx): OVERRUN (because " - "twice the delay is reached), adjusting " - "read pointer! (inst %s)\n", - (u_long)dsp, dsp->name); - /* flush buffer */ - if (dsp->features.unordered) { - dsp->rx_R = (hh->id & CMX_BUFF_MASK); - dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) - & CMX_BUFF_MASK; - } else { - dsp->rx_R = 0; - dsp->rx_W = dsp->cmx_delay; - } - memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); - } - - /* show where to write */ -#ifdef CMX_DEBUG - printk(KERN_DEBUG - "cmx_receive(dsp=%lx): rx_R(dsp)=%05x rx_W(dsp)=%05x len=%d %s\n", - (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->name); -#endif - - /* write data into rx_buffer */ - p = skb->data; - d = dsp->rx_buff; - w = dsp->rx_W; - i = 0; - ii = len; - while (i < ii) { - d[w++ & CMX_BUFF_MASK] = *p++; - i++; - } - - /* increase write-pointer */ - dsp->rx_W = ((dsp->rx_W + len) & CMX_BUFF_MASK); -#ifdef CMX_DELAY_DEBUG - showdelay(dsp, len, (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK); -#endif -} - - -/* - * send (mixed) audio data to card and control jitter - */ -static void -dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members) -{ - struct dsp_conf *conf = dsp->conf; - struct dsp *member, *other; - register s32 sample; - u8 *d, *p, *q, *o_q; - struct sk_buff *nskb, *txskb; - int r, rr, t, tt, o_r, o_rr; - int preload = 0; - struct mISDNhead *hh, *thh; - int tx_data_only = 0; - - /* don't process if: */ - if (!dsp->b_active) { /* if not active */ - dsp->last_tx = 0; - return; - } - if (((dsp->conf && dsp->conf->hardware) || /* hardware conf */ - dsp->echo.hardware) && /* OR hardware echo */ - dsp->tx_R == dsp->tx_W && /* AND no tx-data */ - !(dsp->tone.tone && dsp->tone.software)) { /* AND not soft tones */ - if (!dsp->tx_data) { /* no tx_data for user space required */ - dsp->last_tx = 0; - return; - } - if (dsp->conf && dsp->conf->software && dsp->conf->hardware) - tx_data_only = 1; - if (dsp->echo.software && dsp->echo.hardware) - tx_data_only = 1; - } - -#ifdef CMX_DEBUG - printk(KERN_DEBUG - "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n", - members, dsp->name, conf, dsp->rx_R, dsp->rx_W); -#endif - - /* preload if we have delay set */ - if (dsp->cmx_delay && !dsp->last_tx) { - preload = len; - if (preload < 128) - preload = 128; - } - - /* PREPARE RESULT */ - nskb = mI_alloc_skb(len + preload, GFP_ATOMIC); - if (!nskb) { - printk(KERN_ERR - "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n", - len + preload); - return; - } - hh = mISDN_HEAD_P(nskb); - hh->prim = PH_DATA_REQ; - hh->id = 0; - dsp->last_tx = 1; - - /* set pointers, indexes and stuff */ - member = dsp; - p = dsp->tx_buff; /* transmit data */ - q = dsp->rx_buff; /* received data */ - d = skb_put(nskb, preload + len); /* result */ - t = dsp->tx_R; /* tx-pointers */ - tt = dsp->tx_W; - r = dsp->rx_R; /* rx-pointers */ - rr = (r + len) & CMX_BUFF_MASK; - - /* preload with silence, if required */ - if (preload) { - memset(d, dsp_silence, preload); - d += preload; - } - - /* PROCESS TONES/TX-DATA ONLY */ - if (dsp->tone.tone && dsp->tone.software) { - /* -> copy tone */ - dsp_tone_copy(dsp, d, len); - dsp->tx_R = 0; /* clear tx buffer */ - dsp->tx_W = 0; - goto send_packet; - } - /* if we have tx-data but do not use mixing */ - if (!dsp->tx_mix && t != tt) { - /* -> send tx-data and continue when not enough */ -#ifdef CMX_TX_DEBUG - sprintf(debugbuf, "TX sending (%04x-%04x)%p: ", t, tt, p); -#endif - while (r != rr && t != tt) { -#ifdef CMX_TX_DEBUG - if (strlen(debugbuf) < 48) - sprintf(debugbuf + strlen(debugbuf), " %02x", - p[t]); -#endif - *d++ = p[t]; /* write tx_buff */ - t = (t + 1) & CMX_BUFF_MASK; - r = (r + 1) & CMX_BUFF_MASK; - } - if (r == rr) { - dsp->tx_R = t; -#ifdef CMX_TX_DEBUG - printk(KERN_DEBUG "%s\n", debugbuf); -#endif - goto send_packet; - } - } -#ifdef CMX_TX_DEBUG - printk(KERN_DEBUG "%s\n", debugbuf); -#endif - - /* PROCESS DATA (one member / no conf) */ - if (!conf || members <= 1) { - /* -> if echo is NOT enabled */ - if (!dsp->echo.software) { - /* -> send tx-data if available or use 0-volume */ - while (r != rr && t != tt) { - *d++ = p[t]; /* write tx_buff */ - t = (t + 1) & CMX_BUFF_MASK; - r = (r + 1) & CMX_BUFF_MASK; - } - if (r != rr) { - if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG "%s: RX empty\n", - __func__); - memset(d, dsp_silence, (rr - r) & CMX_BUFF_MASK); - } - /* -> if echo is enabled */ - } else { - /* - * -> mix tx-data with echo if available, - * or use echo only - */ - while (r != rr && t != tt) { - *d++ = dsp_audio_mix_law[(p[t] << 8) | q[r]]; - t = (t + 1) & CMX_BUFF_MASK; - r = (r + 1) & CMX_BUFF_MASK; - } - while (r != rr) { - *d++ = q[r]; /* echo */ - r = (r + 1) & CMX_BUFF_MASK; - } - } - dsp->tx_R = t; - goto send_packet; - } - /* PROCESS DATA (two members) */ -#ifdef CMX_CONF_DEBUG - if (0) { -#else - if (members == 2) { -#endif - /* "other" becomes other party */ - other = (list_entry(conf->mlist.next, - struct dsp_conf_member, list))->dsp; - if (other == member) - other = (list_entry(conf->mlist.prev, - struct dsp_conf_member, list))->dsp; - o_q = other->rx_buff; /* received data */ - o_rr = (other->rx_R + len) & CMX_BUFF_MASK; - /* end of rx-pointer */ - o_r = (o_rr - rr + r) & CMX_BUFF_MASK; - /* start rx-pointer at current read position*/ - /* -> if echo is NOT enabled */ - if (!dsp->echo.software) { - /* - * -> copy other member's rx-data, - * if tx-data is available, mix - */ - while (o_r != o_rr && t != tt) { - *d++ = dsp_audio_mix_law[(p[t] << 8) | o_q[o_r]]; - t = (t + 1) & CMX_BUFF_MASK; - o_r = (o_r + 1) & CMX_BUFF_MASK; - } - while (o_r != o_rr) { - *d++ = o_q[o_r]; - o_r = (o_r + 1) & CMX_BUFF_MASK; - } - /* -> if echo is enabled */ - } else { - /* - * -> mix other member's rx-data with echo, - * if tx-data is available, mix - */ - while (r != rr && t != tt) { - sample = dsp_audio_law_to_s32[p[t]] + - dsp_audio_law_to_s32[q[r]] + - dsp_audio_law_to_s32[o_q[o_r]]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - *d++ = dsp_audio_s16_to_law[sample & 0xffff]; - /* tx-data + rx_data + echo */ - t = (t + 1) & CMX_BUFF_MASK; - r = (r + 1) & CMX_BUFF_MASK; - o_r = (o_r + 1) & CMX_BUFF_MASK; - } - while (r != rr) { - *d++ = dsp_audio_mix_law[(q[r] << 8) | o_q[o_r]]; - r = (r + 1) & CMX_BUFF_MASK; - o_r = (o_r + 1) & CMX_BUFF_MASK; - } - } - dsp->tx_R = t; - goto send_packet; - } - /* PROCESS DATA (three or more members) */ - /* -> if echo is NOT enabled */ - if (!dsp->echo.software) { - /* - * -> subtract rx-data from conf-data, - * if tx-data is available, mix - */ - while (r != rr && t != tt) { - sample = dsp_audio_law_to_s32[p[t]] + *c++ - - dsp_audio_law_to_s32[q[r]]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - *d++ = dsp_audio_s16_to_law[sample & 0xffff]; - /* conf-rx+tx */ - r = (r + 1) & CMX_BUFF_MASK; - t = (t + 1) & CMX_BUFF_MASK; - } - while (r != rr) { - sample = *c++ - dsp_audio_law_to_s32[q[r]]; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - *d++ = dsp_audio_s16_to_law[sample & 0xffff]; - /* conf-rx */ - r = (r + 1) & CMX_BUFF_MASK; - } - /* -> if echo is enabled */ - } else { - /* - * -> encode conf-data, if tx-data - * is available, mix - */ - while (r != rr && t != tt) { - sample = dsp_audio_law_to_s32[p[t]] + *c++; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - *d++ = dsp_audio_s16_to_law[sample & 0xffff]; - /* conf(echo)+tx */ - t = (t + 1) & CMX_BUFF_MASK; - r = (r + 1) & CMX_BUFF_MASK; - } - while (r != rr) { - sample = *c++; - if (sample < -32768) - sample = -32768; - else if (sample > 32767) - sample = 32767; - *d++ = dsp_audio_s16_to_law[sample & 0xffff]; - /* conf(echo) */ - r = (r + 1) & CMX_BUFF_MASK; - } - } - dsp->tx_R = t; - goto send_packet; - -send_packet: - /* - * send tx-data if enabled - don't filter, - * because we want what we send, not what we filtered - */ - if (dsp->tx_data) { - if (tx_data_only) { - hh->prim = DL_DATA_REQ; - hh->id = 0; - /* queue and trigger */ - skb_queue_tail(&dsp->sendq, nskb); - schedule_work(&dsp->workq); - /* exit because only tx_data is used */ - return; - } else { - txskb = mI_alloc_skb(len, GFP_ATOMIC); - if (!txskb) { - printk(KERN_ERR - "FATAL ERROR in mISDN_dsp.o: " - "cannot alloc %d bytes\n", len); - } else { - thh = mISDN_HEAD_P(txskb); - thh->prim = DL_DATA_REQ; - thh->id = 0; - skb_put_data(txskb, nskb->data + preload, len); - /* queue (trigger later) */ - skb_queue_tail(&dsp->sendq, txskb); - } - } - } - - /* send data only to card, if we don't just calculated tx_data */ - /* adjust volume */ - if (dsp->tx_volume) - dsp_change_volume(nskb, dsp->tx_volume); - /* pipeline */ - if (dsp->pipeline.inuse) - dsp_pipeline_process_tx(&dsp->pipeline, nskb->data, - nskb->len); - /* crypt */ - if (dsp->bf_enable) - dsp_bf_encrypt(dsp, nskb->data, nskb->len); - /* queue and trigger */ - skb_queue_tail(&dsp->sendq, nskb); - schedule_work(&dsp->workq); -} - -static u32 jittercount; /* counter for jitter check */ -struct timer_list dsp_spl_tl; -unsigned long dsp_spl_jiffies; /* calculate the next time to fire */ -static u16 dsp_count; /* last sample count */ -static int dsp_count_valid; /* if we have last sample count */ - -void -dsp_cmx_send(struct timer_list *arg) -{ - struct dsp_conf *conf; - struct dsp_conf_member *member; - struct dsp *dsp; - int mustmix, members; - static s32 mixbuffer[MAX_POLL + 100]; - s32 *c; - u8 *p, *q; - int r, rr; - int jittercheck = 0, delay, i; - u_long flags; - u16 length, count; - - /* lock */ - spin_lock_irqsave(&dsp_lock, flags); - - if (!dsp_count_valid) { - dsp_count = mISDN_clock_get(); - length = dsp_poll; - dsp_count_valid = 1; - } else { - count = mISDN_clock_get(); - length = count - dsp_count; - dsp_count = count; - } - if (length > MAX_POLL + 100) - length = MAX_POLL + 100; - /* printk(KERN_DEBUG "len=%d dsp_count=0x%x\n", length, dsp_count); */ - - /* - * check if jitter needs to be checked (this is every second) - */ - jittercount += length; - if (jittercount >= 8000) { - jittercount -= 8000; - jittercheck = 1; - } - - /* loop all members that do not require conference mixing */ - list_for_each_entry(dsp, &dsp_ilist, list) { - if (dsp->hdlc) - continue; - conf = dsp->conf; - mustmix = 0; - members = 0; - if (conf) { - members = list_count_nodes(&conf->mlist); -#ifdef CMX_CONF_DEBUG - if (conf->software && members > 1) -#else - if (conf->software && members > 2) -#endif - mustmix = 1; - } - - /* transmission required */ - if (!mustmix) { - dsp_cmx_send_member(dsp, length, mixbuffer, members); - - /* - * unused mixbuffer is given to prevent a - * potential null-pointer-bug - */ - } - } - - /* loop all members that require conference mixing */ - list_for_each_entry(conf, &conf_ilist, list) { - /* count members and check hardware */ - members = list_count_nodes(&conf->mlist); -#ifdef CMX_CONF_DEBUG - if (conf->software && members > 1) { -#else - if (conf->software && members > 2) { -#endif - /* check for hdlc conf */ - member = list_entry(conf->mlist.next, - struct dsp_conf_member, list); - if (member->dsp->hdlc) - continue; - /* mix all data */ - memset(mixbuffer, 0, length * sizeof(s32)); - list_for_each_entry(member, &conf->mlist, list) { - dsp = member->dsp; - /* get range of data to mix */ - c = mixbuffer; - q = dsp->rx_buff; - r = dsp->rx_R; - rr = (r + length) & CMX_BUFF_MASK; - /* add member's data */ - while (r != rr) { - *c++ += dsp_audio_law_to_s32[q[r]]; - r = (r + 1) & CMX_BUFF_MASK; - } - } - - /* process each member */ - list_for_each_entry(member, &conf->mlist, list) { - /* transmission */ - dsp_cmx_send_member(member->dsp, length, - mixbuffer, members); - } - } - } - - /* delete rx-data, increment buffers, change pointers */ - list_for_each_entry(dsp, &dsp_ilist, list) { - if (dsp->hdlc) - continue; - p = dsp->rx_buff; - q = dsp->tx_buff; - r = dsp->rx_R; - /* move receive pointer when receiving */ - if (!dsp->rx_is_off) { - rr = (r + length) & CMX_BUFF_MASK; - /* delete rx-data */ - while (r != rr) { - p[r] = dsp_silence; - r = (r + 1) & CMX_BUFF_MASK; - } - /* increment rx-buffer pointer */ - dsp->rx_R = r; /* write incremented read pointer */ - } - - /* check current rx_delay */ - delay = (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK; - if (delay >= CMX_BUFF_HALF) - delay = 0; /* will be the delay before next write */ - /* check for lower delay */ - if (delay < dsp->rx_delay[0]) - dsp->rx_delay[0] = delay; - /* check current tx_delay */ - delay = (dsp->tx_W-dsp->tx_R) & CMX_BUFF_MASK; - if (delay >= CMX_BUFF_HALF) - delay = 0; /* will be the delay before next write */ - /* check for lower delay */ - if (delay < dsp->tx_delay[0]) - dsp->tx_delay[0] = delay; - if (jittercheck) { - /* find the lowest of all rx_delays */ - delay = dsp->rx_delay[0]; - i = 1; - while (i < MAX_SECONDS_JITTER_CHECK) { - if (delay > dsp->rx_delay[i]) - delay = dsp->rx_delay[i]; - i++; - } - /* - * remove rx_delay only if we have delay AND we - * have not preset cmx_delay AND - * the delay is greater dsp_poll - */ - if (delay > dsp_poll && !dsp->cmx_delay) { - if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG - "%s lowest rx_delay of %d bytes for" - " dsp %s are now removed.\n", - __func__, delay, - dsp->name); - r = dsp->rx_R; - rr = (r + delay - (dsp_poll >> 1)) - & CMX_BUFF_MASK; - /* delete rx-data */ - while (r != rr) { - p[r] = dsp_silence; - r = (r + 1) & CMX_BUFF_MASK; - } - /* increment rx-buffer pointer */ - dsp->rx_R = r; - /* write incremented read pointer */ - } - /* find the lowest of all tx_delays */ - delay = dsp->tx_delay[0]; - i = 1; - while (i < MAX_SECONDS_JITTER_CHECK) { - if (delay > dsp->tx_delay[i]) - delay = dsp->tx_delay[i]; - i++; - } - /* - * remove delay only if we have delay AND we - * have enabled tx_dejitter - */ - if (delay > dsp_poll && dsp->tx_dejitter) { - if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG - "%s lowest tx_delay of %d bytes for" - " dsp %s are now removed.\n", - __func__, delay, - dsp->name); - r = dsp->tx_R; - rr = (r + delay - (dsp_poll >> 1)) - & CMX_BUFF_MASK; - /* delete tx-data */ - while (r != rr) { - q[r] = dsp_silence; - r = (r + 1) & CMX_BUFF_MASK; - } - /* increment rx-buffer pointer */ - dsp->tx_R = r; - /* write incremented read pointer */ - } - /* scroll up delays */ - i = MAX_SECONDS_JITTER_CHECK - 1; - while (i) { - dsp->rx_delay[i] = dsp->rx_delay[i - 1]; - dsp->tx_delay[i] = dsp->tx_delay[i - 1]; - i--; - } - dsp->tx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ - dsp->rx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ - } - } - - /* if next event would be in the past ... */ - if ((s32)(dsp_spl_jiffies + dsp_tics-jiffies) <= 0) - dsp_spl_jiffies = jiffies + 1; - else - dsp_spl_jiffies += dsp_tics; - - dsp_spl_tl.expires = dsp_spl_jiffies; - add_timer(&dsp_spl_tl); - - /* unlock */ - spin_unlock_irqrestore(&dsp_lock, flags); -} - -/* - * audio data is transmitted from upper layer to the dsp - */ -void -dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb) -{ - u_int w, ww; - u8 *d, *p; - int space; /* todo: , l = skb->len; */ -#ifdef CMX_TX_DEBUG - char debugbuf[256] = ""; -#endif - - /* check if there is enough space, and then copy */ - w = dsp->tx_W; - ww = dsp->tx_R; - p = dsp->tx_buff; - d = skb->data; - space = (ww - w - 1) & CMX_BUFF_MASK; - /* write-pointer should not overrun nor reach read pointer */ - if (space < skb->len) { - /* write to the space we have left */ - ww = (ww - 1) & CMX_BUFF_MASK; /* end one byte prior tx_R */ - if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG "%s: TX overflow space=%d skb->len=" - "%d, w=0x%04x, ww=0x%04x\n", __func__, space, - skb->len, w, ww); - } else - /* write until all byte are copied */ - ww = (w + skb->len) & CMX_BUFF_MASK; - dsp->tx_W = ww; - /* show current buffer */ -#ifdef CMX_DEBUG - printk(KERN_DEBUG - "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", - (u_long)dsp, (ww - w) & CMX_BUFF_MASK, w, ww, dsp->name); -#endif - - /* copy transmit data to tx-buffer */ -#ifdef CMX_TX_DEBUG - sprintf(debugbuf, "TX getting (%04x-%04x)%p: ", w, ww, p); -#endif - while (w != ww) { -#ifdef CMX_TX_DEBUG - if (strlen(debugbuf) < 48) - sprintf(debugbuf + strlen(debugbuf), " %02x", *d); -#endif - p[w] = *d++; - w = (w + 1) & CMX_BUFF_MASK; - } -#ifdef CMX_TX_DEBUG - printk(KERN_DEBUG "%s\n", debugbuf); -#endif - -} - -/* - * hdlc data is received from card and sent to all members. - */ -void -dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb) -{ - struct sk_buff *nskb = NULL; - struct dsp_conf_member *member; - struct mISDNhead *hh; - - /* not if not active */ - if (!dsp->b_active) - return; - - /* check if we have sompen */ - if (skb->len < 1) - return; - - /* no conf */ - if (!dsp->conf) { - /* in case of software echo */ - if (dsp->echo.software) { - nskb = skb_clone(skb, GFP_ATOMIC); - if (nskb) { - hh = mISDN_HEAD_P(nskb); - hh->prim = PH_DATA_REQ; - hh->id = 0; - skb_queue_tail(&dsp->sendq, nskb); - schedule_work(&dsp->workq); - } - } - return; - } - /* in case of hardware conference */ - if (dsp->conf->hardware) - return; - list_for_each_entry(member, &dsp->conf->mlist, list) { - if (dsp->echo.software || member->dsp != dsp) { - nskb = skb_clone(skb, GFP_ATOMIC); - if (nskb) { - hh = mISDN_HEAD_P(nskb); - hh->prim = PH_DATA_REQ; - hh->id = 0; - skb_queue_tail(&member->dsp->sendq, nskb); - schedule_work(&member->dsp->workq); - } - } - } -} diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c deleted file mode 100644 index d0aa415a6b09..000000000000 --- a/drivers/isdn/mISDN/dsp_core.c +++ /dev/null @@ -1,1227 +0,0 @@ -/* - * Author Andreas Eversberg (jolly@eversberg.eu) - * Based on source code structure by - * Karsten Keil (keil@isdn4linux.de) - * - * This file is (c) under GNU PUBLIC LICENSE - * - * Thanks to Karsten Keil (great drivers) - * Cologne Chip (great chips) - * - * This module does: - * Real-time tone generation - * DTMF detection - * Real-time cross-connection and conferrence - * Compensate jitter due to system load and hardware fault. - * All features are done in kernel space and will be realized - * using hardware, if available and supported by chip set. - * Blowfish encryption/decryption - */ - -/* STRUCTURE: - * - * The dsp module provides layer 2 for b-channels (64kbit). It provides - * transparent audio forwarding with special digital signal processing: - * - * - (1) generation of tones - * - (2) detection of dtmf tones - * - (3) crossconnecting and conferences (clocking) - * - (4) echo generation for delay test - * - (5) volume control - * - (6) disable receive data - * - (7) pipeline - * - (8) encryption/decryption - * - * Look: - * TX RX - * ------upper layer------ - * | ^ - * | |(6) - * v | - * +-----+-------------+-----+ - * |(3)(4) | - * | CMX | - * | | - * | +-------------+ - * | | ^ - * | | | - * |+---------+| +----+----+ - * ||(1) || |(2) | - * || || | | - * || Tones || | DTMF | - * || || | | - * || || | | - * |+----+----+| +----+----+ - * +-----+-----+ ^ - * | | - * v | - * +----+----+ +----+----+ - * |(5) | |(5) | - * | | | | - * |TX Volume| |RX Volume| - * | | | | - * | | | | - * +----+----+ +----+----+ - * | ^ - * | | - * v | - * +----+-------------+----+ - * |(7) | - * | | - * | Pipeline Processing | - * | | - * | | - * +----+-------------+----+ - * | ^ - * | | - * v | - * +----+----+ +----+----+ - * |(8) | |(8) | - * | | | | - * | Encrypt | | Decrypt | - * | | | | - * | | | | - * +----+----+ +----+----+ - * | ^ - * | | - * v | - * ------card layer------ - * TX RX - * - * Above you can see the logical data flow. If software is used to do the - * process, it is actually the real data flow. If hardware is used, data - * may not flow, but hardware commands to the card, to provide the data flow - * as shown. - * - * NOTE: The channel must be activated in order to make dsp work, even if - * no data flow to the upper layer is intended. Activation can be done - * after and before controlling the setting using PH_CONTROL requests. - * - * DTMF: Will be detected by hardware if possible. It is done before CMX - * processing. - * - * Tones: Will be generated via software if endless looped audio fifos are - * not supported by hardware. Tones will override all data from CMX. - * It is not required to join a conference to use tones at any time. - * - * CMX: Is transparent when not used. When it is used, it will do - * crossconnections and conferences via software if not possible through - * hardware. If hardware capability is available, hardware is used. - * - * Echo: Is generated by CMX and is used to check performance of hard and - * software CMX. - * - * The CMX has special functions for conferences with one, two and more - * members. It will allow different types of data flow. Receive and transmit - * data to/form upper layer may be switched on/off individually without losing - * features of CMX, Tones and DTMF. - * - * Echo Cancellation: Sometimes we like to cancel echo from the interface. - * Note that a VoIP call may not have echo caused by the IP phone. The echo - * is generated by the telephone line connected to it. Because the delay - * is high, it becomes an echo. RESULT: Echo Cachelation is required if - * both echo AND delay is applied to an interface. - * Remember that software CMX always generates a more or less delay. - * - * If all used features can be realized in hardware, and if transmit and/or - * receive data ist disabled, the card may not send/receive any data at all. - * Not receiving is useful if only announcements are played. Not sending is - * useful if an answering machine records audio. Not sending and receiving is - * useful during most states of the call. If supported by hardware, tones - * will be played without cpu load. Small PBXs and NT-Mode applications will - * not need expensive hardware when processing calls. - * - * - * LOCKING: - * - * When data is received from upper or lower layer (card), the complete dsp - * module is locked by a global lock. This lock MUST lock irq, because it - * must lock timer events by DSP poll timer. - * When data is ready to be transmitted down, the data is queued and sent - * outside lock and timer event. - * PH_CONTROL must not change any settings, join or split conference members - * during process of data. - * - * HDLC: - * - * It works quite the same as transparent, except that HDLC data is forwarded - * to all other conference members if no hardware bridging is possible. - * Send data will be writte to sendq. Sendq will be sent if confirm is received. - * Conference cannot join, if one member is not hdlc. - * - */ - -#include -#include -#include -#include -#include -#include -#include "core.h" -#include "dsp.h" - -static const char *mISDN_dsp_revision = "2.0"; - -static int debug; -static int options; -static int poll; -static int dtmfthreshold = 100; - -MODULE_AUTHOR("Andreas Eversberg"); -module_param(debug, uint, S_IRUGO | S_IWUSR); -module_param(options, uint, S_IRUGO | S_IWUSR); -module_param(poll, uint, S_IRUGO | S_IWUSR); -module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR); -MODULE_DESCRIPTION("mISDN driver for Digital Audio Processing of transparent data"); -MODULE_LICENSE("GPL"); - -/*int spinnest = 0;*/ - -DEFINE_SPINLOCK(dsp_lock); /* global dsp lock */ -LIST_HEAD(dsp_ilist); -LIST_HEAD(conf_ilist); -int dsp_debug; -int dsp_options; -int dsp_poll, dsp_tics; - -/* check if rx may be turned off or must be turned on */ -static void -dsp_rx_off_member(struct dsp *dsp) -{ - struct mISDN_ctrl_req cq; - int rx_off = 1; - - memset(&cq, 0, sizeof(cq)); - - if (!dsp->features_rx_off) - return; - - /* not disabled */ - if (!dsp->rx_disabled) - rx_off = 0; - /* software dtmf */ - else if (dsp->dtmf.software) - rx_off = 0; - /* echo in software */ - else if (dsp->echo.software) - rx_off = 0; - /* bridge in software */ - else if (dsp->conf && dsp->conf->software) - rx_off = 0; - /* data is not required by user space and not required - * for echo dtmf detection, soft-echo, soft-bridging */ - - if (rx_off == dsp->rx_is_off) - return; - - if (!dsp->ch.peer) { - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: no peer, no rx_off\n", - __func__); - return; - } - cq.op = MISDN_CTRL_RX_OFF; - cq.p1 = rx_off; - if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) { - printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n", - __func__); - return; - } - dsp->rx_is_off = rx_off; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: %s set rx_off = %d\n", - __func__, dsp->name, rx_off); -} -static void -dsp_rx_off(struct dsp *dsp) -{ - struct dsp_conf_member *member; - - if (dsp_options & DSP_OPT_NOHARDWARE) - return; - - /* no conf */ - if (!dsp->conf) { - dsp_rx_off_member(dsp); - return; - } - /* check all members in conf */ - list_for_each_entry(member, &dsp->conf->mlist, list) { - dsp_rx_off_member(member->dsp); - } -} - -/* enable "fill empty" feature */ -static void -dsp_fill_empty(struct dsp *dsp) -{ - struct mISDN_ctrl_req cq; - - memset(&cq, 0, sizeof(cq)); - - if (!dsp->ch.peer) { - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: no peer, no fill_empty\n", - __func__); - return; - } - cq.op = MISDN_CTRL_FILL_EMPTY; - cq.p1 = 1; - cq.p2 = dsp_silence; - if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) { - printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", - __func__); - return; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: %s set fill_empty = 1\n", - __func__, dsp->name); -} - -static int -dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) -{ - struct sk_buff *nskb; - int ret = 0; - int cont; - u8 *data; - int len; - - if (skb->len < sizeof(int)) { - printk(KERN_ERR "%s: PH_CONTROL message too short\n", __func__); - return -EINVAL; - } - cont = *((int *)skb->data); - len = skb->len - sizeof(int); - data = skb->data + sizeof(int); - - switch (cont) { - case DTMF_TONE_START: /* turn on DTMF */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: start dtmf\n", __func__); - if (len == sizeof(int)) { - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_NOTICE "changing DTMF Threshold " - "to %d\n", *((int *)data)); - dsp->dtmf.treshold = (*(int *)data) * 10000; - } - dsp->dtmf.enable = 1; - /* init goertzel */ - dsp_dtmf_goertzel_init(dsp); - - /* check dtmf hardware */ - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - break; - case DTMF_TONE_STOP: /* turn off DTMF */ - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: stop dtmf\n", __func__); - dsp->dtmf.enable = 0; - dsp->dtmf.hardware = 0; - dsp->dtmf.software = 0; - break; - case DSP_CONF_JOIN: /* join / update conference */ - if (len < sizeof(int)) { - ret = -EINVAL; - break; - } - if (*((u32 *)data) == 0) - goto conf_split; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: join conference %d\n", - __func__, *((u32 *)data)); - ret = dsp_cmx_conf(dsp, *((u32 *)data)); - /* dsp_cmx_hardware will also be called here */ - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_CONF_SPLIT: /* remove from conference */ - conf_split: - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: release conference\n", __func__); - ret = dsp_cmx_conf(dsp, 0); - /* dsp_cmx_hardware will also be called here */ - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - dsp_rx_off(dsp); - break; - case DSP_TONE_PATT_ON: /* play tone */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (len < sizeof(int)) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: turn tone 0x%x on\n", - __func__, *((int *)skb->data)); - ret = dsp_tone(dsp, *((int *)data)); - if (!ret) { - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - } - if (!dsp->tone.tone) - goto tone_off; - break; - case DSP_TONE_PATT_OFF: /* stop tone */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: turn tone off\n", __func__); - dsp_tone(dsp, 0); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - /* reset tx buffers (user space data) */ - tone_off: - dsp->rx_W = 0; - dsp->rx_R = 0; - break; - case DSP_VOL_CHANGE_TX: /* change volume */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (len < sizeof(int)) { - ret = -EINVAL; - break; - } - dsp->tx_volume = *((int *)data); - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: change tx vol to %d\n", - __func__, dsp->tx_volume); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - break; - case DSP_VOL_CHANGE_RX: /* change volume */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (len < sizeof(int)) { - ret = -EINVAL; - break; - } - dsp->rx_volume = *((int *)data); - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: change rx vol to %d\n", - __func__, dsp->tx_volume); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - break; - case DSP_ECHO_ON: /* enable echo */ - dsp->echo.software = 1; /* soft echo */ - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: enable cmx-echo\n", __func__); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_ECHO_OFF: /* disable echo */ - dsp->echo.software = 0; - dsp->echo.hardware = 0; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: disable cmx-echo\n", __func__); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_RECEIVE_ON: /* enable receive to user space */ - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: enable receive to user " - "space\n", __func__); - dsp->rx_disabled = 0; - dsp_rx_off(dsp); - break; - case DSP_RECEIVE_OFF: /* disable receive to user space */ - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: disable receive to " - "user space\n", __func__); - dsp->rx_disabled = 1; - dsp_rx_off(dsp); - break; - case DSP_MIX_ON: /* enable mixing of tx data */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: enable mixing of " - "tx-data with conf members\n", __func__); - dsp->tx_mix = 1; - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_MIX_OFF: /* disable mixing of tx data */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: disable mixing of " - "tx-data with conf members\n", __func__); - dsp->tx_mix = 0; - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_TXDATA_ON: /* enable txdata */ - dsp->tx_data = 1; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: enable tx-data\n", __func__); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_TXDATA_OFF: /* disable txdata */ - dsp->tx_data = 0; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: disable tx-data\n", __func__); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - if (dsp_debug & DEBUG_DSP_CMX) - dsp_cmx_debug(dsp); - break; - case DSP_DELAY: /* use delay algorithm instead of dynamic - jitter algorithm */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (len < sizeof(int)) { - ret = -EINVAL; - break; - } - dsp->cmx_delay = (*((int *)data)) << 3; - /* milliseconds to samples */ - if (dsp->cmx_delay >= (CMX_BUFF_HALF >> 1)) - /* clip to half of maximum usable buffer - (half of half buffer) */ - dsp->cmx_delay = (CMX_BUFF_HALF >> 1) - 1; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: use delay algorithm to " - "compensate jitter (%d samples)\n", - __func__, dsp->cmx_delay); - break; - case DSP_JITTER: /* use dynamic jitter algorithm instead of - delay algorithm */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - dsp->cmx_delay = 0; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: use jitter algorithm to " - "compensate jitter\n", __func__); - break; - case DSP_TX_DEJITTER: /* use dynamic jitter algorithm for tx-buffer */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - dsp->tx_dejitter = 1; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: use dejitter on TX " - "buffer\n", __func__); - break; - case DSP_TX_DEJ_OFF: /* use tx-buffer without dejittering*/ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - dsp->tx_dejitter = 0; - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: use TX buffer without " - "dejittering\n", __func__); - break; - case DSP_PIPELINE_CFG: - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (len > 0 && ((char *)data)[len - 1]) { - printk(KERN_DEBUG "%s: pipeline config string " - "is not NULL terminated!\n", __func__); - ret = -EINVAL; - } else { - dsp->pipeline.inuse = 1; - dsp_cmx_hardware(dsp->conf, dsp); - ret = dsp_pipeline_build(&dsp->pipeline, - len > 0 ? data : NULL); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - } - break; - case DSP_BF_ENABLE_KEY: /* turn blowfish on */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (len < 4 || len > 56) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: turn blowfish on (key " - "not shown)\n", __func__); - ret = dsp_bf_init(dsp, (u8 *)data, len); - /* set new cont */ - if (!ret) - cont = DSP_BF_ACCEPT; - else - cont = DSP_BF_REJECT; - /* send indication if it worked to set it */ - nskb = _alloc_mISDN_skb(PH_CONTROL_IND, MISDN_ID_ANY, - sizeof(int), &cont, GFP_ATOMIC); - if (nskb) { - if (dsp->up) { - if (dsp->up->send(dsp->up, nskb)) - dev_kfree_skb(nskb); - } else - dev_kfree_skb(nskb); - } - if (!ret) { - dsp_cmx_hardware(dsp->conf, dsp); - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - } - break; - case DSP_BF_DISABLE: /* turn blowfish off */ - if (dsp->hdlc) { - ret = -EINVAL; - break; - } - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: turn blowfish off\n", __func__); - dsp_bf_cleanup(dsp); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - break; - default: - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: ctrl req %x unhandled\n", - __func__, cont); - ret = -EINVAL; - } - return ret; -} - -static void -get_features(struct mISDNchannel *ch) -{ - struct dsp *dsp = container_of(ch, struct dsp, ch); - struct mISDN_ctrl_req cq; - - if (!ch->peer) { - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: no peer, no features\n", - __func__); - return; - } - memset(&cq, 0, sizeof(cq)); - cq.op = MISDN_CTRL_GETOP; - if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq) < 0) { - printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", - __func__); - return; - } - if (cq.op & MISDN_CTRL_RX_OFF) - dsp->features_rx_off = 1; - if (cq.op & MISDN_CTRL_FILL_EMPTY) - dsp->features_fill_empty = 1; - if (dsp_options & DSP_OPT_NOHARDWARE) - return; - if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) { - cq.op = MISDN_CTRL_HW_FEATURES; - *((u_long *)&cq.p1) = (u_long)&dsp->features; - if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq)) { - printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n", - __func__); - } - } else - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: features not supported for %s\n", - __func__, dsp->name); -} - -static int -dsp_function(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct dsp *dsp = container_of(ch, struct dsp, ch); - struct mISDNhead *hh; - int ret = 0; - u8 *digits = NULL; - u_long flags; - - hh = mISDN_HEAD_P(skb); - switch (hh->prim) { - /* FROM DOWN */ - case (PH_DATA_CNF): - dsp->data_pending = 0; - /* trigger next hdlc frame, if any */ - if (dsp->hdlc) { - spin_lock_irqsave(&dsp_lock, flags); - if (dsp->b_active) - schedule_work(&dsp->workq); - spin_unlock_irqrestore(&dsp_lock, flags); - } - break; - case (PH_DATA_IND): - case (DL_DATA_IND): - if (skb->len < 1) { - ret = -EINVAL; - break; - } - if (dsp->rx_is_off) { - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: rx-data during rx_off" - " for %s\n", - __func__, dsp->name); - } - if (dsp->hdlc) { - /* hdlc */ - spin_lock_irqsave(&dsp_lock, flags); - dsp_cmx_hdlc(dsp, skb); - spin_unlock_irqrestore(&dsp_lock, flags); - if (dsp->rx_disabled) { - /* if receive is not allowed */ - break; - } - hh->prim = DL_DATA_IND; - if (dsp->up) - return dsp->up->send(dsp->up, skb); - break; - } - - spin_lock_irqsave(&dsp_lock, flags); - - /* decrypt if enabled */ - if (dsp->bf_enable) - dsp_bf_decrypt(dsp, skb->data, skb->len); - /* pipeline */ - if (dsp->pipeline.inuse) - dsp_pipeline_process_rx(&dsp->pipeline, skb->data, - skb->len, hh->id); - /* change volume if requested */ - if (dsp->rx_volume) - dsp_change_volume(skb, dsp->rx_volume); - /* check if dtmf soft decoding is turned on */ - if (dsp->dtmf.software) { - digits = dsp_dtmf_goertzel_decode(dsp, skb->data, - skb->len, (dsp_options & DSP_OPT_ULAW) ? 1 : 0); - } - /* we need to process receive data if software */ - if (dsp->conf && dsp->conf->software) { - /* process data from card at cmx */ - dsp_cmx_receive(dsp, skb); - } - - spin_unlock_irqrestore(&dsp_lock, flags); - - /* send dtmf result, if any */ - if (digits) { - while (*digits) { - int k; - struct sk_buff *nskb; - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "%s: digit" - "(%c) to layer %s\n", - __func__, *digits, dsp->name); - k = *digits | DTMF_TONE_VAL; - nskb = _alloc_mISDN_skb(PH_CONTROL_IND, - MISDN_ID_ANY, sizeof(int), &k, - GFP_ATOMIC); - if (nskb) { - if (dsp->up) { - if (dsp->up->send( - dsp->up, nskb)) - dev_kfree_skb(nskb); - } else - dev_kfree_skb(nskb); - } - digits++; - } - } - if (dsp->rx_disabled) { - /* if receive is not allowed */ - break; - } - hh->prim = DL_DATA_IND; - if (dsp->up) - return dsp->up->send(dsp->up, skb); - break; - case (PH_CONTROL_IND): - if (dsp_debug & DEBUG_DSP_DTMFCOEFF) - printk(KERN_DEBUG "%s: PH_CONTROL INDICATION " - "received: %x (len %d) %s\n", __func__, - hh->id, skb->len, dsp->name); - switch (hh->id) { - case (DTMF_HFC_COEF): /* getting coefficients */ - if (!dsp->dtmf.hardware) { - if (dsp_debug & DEBUG_DSP_DTMFCOEFF) - printk(KERN_DEBUG "%s: ignoring DTMF " - "coefficients from HFC\n", - __func__); - break; - } - digits = dsp_dtmf_goertzel_decode(dsp, skb->data, - skb->len, 2); - while (*digits) { - int k; - struct sk_buff *nskb; - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "%s: digit" - "(%c) to layer %s\n", - __func__, *digits, dsp->name); - k = *digits | DTMF_TONE_VAL; - nskb = _alloc_mISDN_skb(PH_CONTROL_IND, - MISDN_ID_ANY, sizeof(int), &k, - GFP_ATOMIC); - if (nskb) { - if (dsp->up) { - if (dsp->up->send( - dsp->up, nskb)) - dev_kfree_skb(nskb); - } else - dev_kfree_skb(nskb); - } - digits++; - } - break; - case (HFC_VOL_CHANGE_TX): /* change volume */ - if (skb->len != sizeof(int)) { - ret = -EINVAL; - break; - } - spin_lock_irqsave(&dsp_lock, flags); - dsp->tx_volume = *((int *)skb->data); - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: change tx volume to " - "%d\n", __func__, dsp->tx_volume); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - spin_unlock_irqrestore(&dsp_lock, flags); - break; - default: - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: ctrl ind %x unhandled " - "%s\n", __func__, hh->id, dsp->name); - ret = -EINVAL; - } - break; - case (PH_ACTIVATE_IND): - case (PH_ACTIVATE_CNF): - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: b_channel is now active %s\n", - __func__, dsp->name); - /* bchannel now active */ - spin_lock_irqsave(&dsp_lock, flags); - dsp->b_active = 1; - dsp->data_pending = 0; - dsp->rx_init = 1; - /* rx_W and rx_R will be adjusted on first frame */ - dsp->rx_W = 0; - dsp->rx_R = 0; - memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff)); - dsp_cmx_hardware(dsp->conf, dsp); - dsp_dtmf_hardware(dsp); - dsp_rx_off(dsp); - spin_unlock_irqrestore(&dsp_lock, flags); - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: done with activation, sending " - "confirm to user space. %s\n", __func__, - dsp->name); - /* send activation to upper layer */ - hh->prim = DL_ESTABLISH_CNF; - if (dsp->up) - return dsp->up->send(dsp->up, skb); - break; - case (PH_DEACTIVATE_IND): - case (PH_DEACTIVATE_CNF): - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: b_channel is now inactive %s\n", - __func__, dsp->name); - /* bchannel now inactive */ - spin_lock_irqsave(&dsp_lock, flags); - dsp->b_active = 0; - dsp->data_pending = 0; - dsp_cmx_hardware(dsp->conf, dsp); - dsp_rx_off(dsp); - spin_unlock_irqrestore(&dsp_lock, flags); - hh->prim = DL_RELEASE_CNF; - if (dsp->up) - return dsp->up->send(dsp->up, skb); - break; - /* FROM UP */ - case (DL_DATA_REQ): - case (PH_DATA_REQ): - if (skb->len < 1) { - ret = -EINVAL; - break; - } - if (dsp->hdlc) { - /* hdlc */ - if (!dsp->b_active) { - ret = -EIO; - break; - } - hh->prim = PH_DATA_REQ; - spin_lock_irqsave(&dsp_lock, flags); - skb_queue_tail(&dsp->sendq, skb); - schedule_work(&dsp->workq); - spin_unlock_irqrestore(&dsp_lock, flags); - return 0; - } - /* send data to tx-buffer (if no tone is played) */ - if (!dsp->tone.tone) { - spin_lock_irqsave(&dsp_lock, flags); - dsp_cmx_transmit(dsp, skb); - spin_unlock_irqrestore(&dsp_lock, flags); - } - break; - case (PH_CONTROL_REQ): - spin_lock_irqsave(&dsp_lock, flags); - ret = dsp_control_req(dsp, hh, skb); - spin_unlock_irqrestore(&dsp_lock, flags); - break; - case (DL_ESTABLISH_REQ): - case (PH_ACTIVATE_REQ): - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: activating b_channel %s\n", - __func__, dsp->name); - if (dsp->dtmf.hardware || dsp->dtmf.software) - dsp_dtmf_goertzel_init(dsp); - get_features(ch); - /* enable fill_empty feature */ - if (dsp->features_fill_empty) - dsp_fill_empty(dsp); - /* send ph_activate */ - hh->prim = PH_ACTIVATE_REQ; - if (ch->peer) - return ch->recv(ch->peer, skb); - break; - case (DL_RELEASE_REQ): - case (PH_DEACTIVATE_REQ): - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: releasing b_channel %s\n", - __func__, dsp->name); - spin_lock_irqsave(&dsp_lock, flags); - dsp->tone.tone = 0; - dsp->tone.hardware = 0; - dsp->tone.software = 0; - if (timer_pending(&dsp->tone.tl)) - timer_delete(&dsp->tone.tl); - if (dsp->conf) - dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be - called here */ - skb_queue_purge(&dsp->sendq); - spin_unlock_irqrestore(&dsp_lock, flags); - hh->prim = PH_DEACTIVATE_REQ; - if (ch->peer) - return ch->recv(ch->peer, skb); - break; - default: - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: msg %x unhandled %s\n", - __func__, hh->prim, dsp->name); - ret = -EINVAL; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -dsp_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct dsp *dsp = container_of(ch, struct dsp, ch); - u_long flags; - - if (debug & DEBUG_DSP_CTRL) - printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd); - - switch (cmd) { - case OPEN_CHANNEL: - break; - case CLOSE_CHANNEL: - if (dsp->ch.peer) - dsp->ch.peer->ctrl(dsp->ch.peer, CLOSE_CHANNEL, NULL); - - /* wait until workqueue has finished, - * must lock here, or we may hit send-process currently - * queueing. */ - spin_lock_irqsave(&dsp_lock, flags); - dsp->b_active = 0; - spin_unlock_irqrestore(&dsp_lock, flags); - /* MUST not be locked, because it waits until queue is done. */ - cancel_work_sync(&dsp->workq); - spin_lock_irqsave(&dsp_lock, flags); - if (timer_pending(&dsp->tone.tl)) - timer_delete(&dsp->tone.tl); - skb_queue_purge(&dsp->sendq); - if (dsp_debug & DEBUG_DSP_CTRL) - printk(KERN_DEBUG "%s: releasing member %s\n", - __func__, dsp->name); - dsp->b_active = 0; - dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be called - here */ - dsp_pipeline_destroy(&dsp->pipeline); - - if (dsp_debug & DEBUG_DSP_CTRL) - printk(KERN_DEBUG "%s: remove & destroy object %s\n", - __func__, dsp->name); - list_del(&dsp->list); - spin_unlock_irqrestore(&dsp_lock, flags); - - if (dsp_debug & DEBUG_DSP_CTRL) - printk(KERN_DEBUG "%s: dsp instance released\n", - __func__); - vfree(dsp); - module_put(THIS_MODULE); - break; - } - return 0; -} - -static void -dsp_send_bh(struct work_struct *work) -{ - struct dsp *dsp = container_of(work, struct dsp, workq); - struct sk_buff *skb; - struct mISDNhead *hh; - - if (dsp->hdlc && dsp->data_pending) - return; /* wait until data has been acknowledged */ - - /* send queued data */ - while ((skb = skb_dequeue(&dsp->sendq))) { - /* in locked date, we must have still data in queue */ - if (dsp->data_pending) { - if (dsp_debug & DEBUG_DSP_CORE) - printk(KERN_DEBUG "%s: fifo full %s, this is " - "no bug!\n", __func__, dsp->name); - /* flush transparent data, if not acked */ - dev_kfree_skb(skb); - continue; - } - hh = mISDN_HEAD_P(skb); - if (hh->prim == DL_DATA_REQ) { - /* send packet up */ - if (dsp->up) { - if (dsp->up->send(dsp->up, skb)) - dev_kfree_skb(skb); - } else - dev_kfree_skb(skb); - } else { - /* send packet down */ - if (dsp->ch.peer) { - dsp->data_pending = 1; - if (dsp->ch.recv(dsp->ch.peer, skb)) { - dev_kfree_skb(skb); - dsp->data_pending = 0; - } - } else - dev_kfree_skb(skb); - } - } -} - -static int -dspcreate(struct channel_req *crq) -{ - struct dsp *ndsp; - u_long flags; - - if (crq->protocol != ISDN_P_B_L2DSP - && crq->protocol != ISDN_P_B_L2DSPHDLC) - return -EPROTONOSUPPORT; - ndsp = vzalloc(sizeof(struct dsp)); - if (!ndsp) { - printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__); - return -ENOMEM; - } - if (dsp_debug & DEBUG_DSP_CTRL) - printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__); - - /* default enabled */ - INIT_WORK(&ndsp->workq, (void *)dsp_send_bh); - skb_queue_head_init(&ndsp->sendq); - ndsp->ch.send = dsp_function; - ndsp->ch.ctrl = dsp_ctrl; - ndsp->up = crq->ch; - crq->ch = &ndsp->ch; - if (crq->protocol == ISDN_P_B_L2DSP) { - crq->protocol = ISDN_P_B_RAW; - ndsp->hdlc = 0; - } else { - crq->protocol = ISDN_P_B_HDLC; - ndsp->hdlc = 1; - } - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", - __func__); - - sprintf(ndsp->name, "DSP_C%x(0x%p)", - ndsp->up->st->dev->id + 1, ndsp); - /* set frame size to start */ - ndsp->features.hfc_id = -1; /* current PCM id */ - ndsp->features.pcm_id = -1; /* current PCM id */ - ndsp->pcm_slot_rx = -1; /* current CPM slot */ - ndsp->pcm_slot_tx = -1; - ndsp->pcm_bank_rx = -1; - ndsp->pcm_bank_tx = -1; - ndsp->hfc_conf = -1; /* current conference number */ - /* set tone timer */ - timer_setup(&ndsp->tone.tl, dsp_tone_timeout, 0); - - if (dtmfthreshold < 20 || dtmfthreshold > 500) - dtmfthreshold = 200; - ndsp->dtmf.treshold = dtmfthreshold * 10000; - - /* init pipeline append to list */ - spin_lock_irqsave(&dsp_lock, flags); - dsp_pipeline_init(&ndsp->pipeline); - list_add_tail(&ndsp->list, &dsp_ilist); - spin_unlock_irqrestore(&dsp_lock, flags); - - return 0; -} - - -static struct Bprotocol DSP = { - .Bprotocols = (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK)) - | (1 << (ISDN_P_B_L2DSPHDLC & ISDN_P_B_MASK)), - .name = "dsp", - .create = dspcreate -}; - -static int __init dsp_init(void) -{ - int err; - int tics; - - printk(KERN_INFO "DSP module %s\n", mISDN_dsp_revision); - - dsp_options = options; - dsp_debug = debug; - - /* set packet size */ - dsp_poll = poll; - if (dsp_poll) { - if (dsp_poll > MAX_POLL) { - printk(KERN_ERR "%s: Wrong poll value (%d), use %d " - "maximum.\n", __func__, poll, MAX_POLL); - err = -EINVAL; - return err; - } - if (dsp_poll < 8) { - printk(KERN_ERR "%s: Wrong poll value (%d), use 8 " - "minimum.\n", __func__, dsp_poll); - err = -EINVAL; - return err; - } - dsp_tics = poll * HZ / 8000; - if (dsp_tics * 8000 != poll * HZ) { - printk(KERN_INFO "mISDN_dsp: Cannot clock every %d " - "samples (0,125 ms). It is not a multiple of " - "%d HZ.\n", poll, HZ); - err = -EINVAL; - return err; - } - } else { - poll = 8; - while (poll <= MAX_POLL) { - tics = (poll * HZ) / 8000; - if (tics * 8000 == poll * HZ) { - dsp_tics = tics; - dsp_poll = poll; - if (poll >= 64) - break; - } - poll++; - } - } - if (dsp_poll == 0) { - printk(KERN_INFO "mISDN_dsp: There is no multiple of kernel " - "clock that equals exactly the duration of 8-256 " - "samples. (Choose kernel clock speed like 100, 250, " - "300, 1000)\n"); - err = -EINVAL; - return err; - } - printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals " - "%d jiffies.\n", dsp_poll, dsp_tics); - - /* init conversion tables */ - dsp_audio_generate_law_tables(); - dsp_silence = (dsp_options & DSP_OPT_ULAW) ? 0xff : 0x2a; - dsp_audio_law_to_s32 = (dsp_options & DSP_OPT_ULAW) ? - dsp_audio_ulaw_to_s32 : dsp_audio_alaw_to_s32; - dsp_audio_generate_s2law_table(); - dsp_audio_generate_seven(); - dsp_audio_generate_mix_table(); - if (dsp_options & DSP_OPT_ULAW) - dsp_audio_generate_ulaw_samples(); - dsp_audio_generate_volume_changes(); - - err = dsp_pipeline_module_init(); - if (err) { - printk(KERN_ERR "mISDN_dsp: Can't initialize pipeline, " - "error(%d)\n", err); - return err; - } - - err = mISDN_register_Bprotocol(&DSP); - if (err) { - printk(KERN_ERR "Can't register %s error(%d)\n", DSP.name, err); - return err; - } - - /* set sample timer */ - timer_setup(&dsp_spl_tl, dsp_cmx_send, 0); - dsp_spl_tl.expires = jiffies + dsp_tics; - dsp_spl_jiffies = dsp_spl_tl.expires; - add_timer(&dsp_spl_tl); - - return 0; -} - - -static void __exit dsp_cleanup(void) -{ - mISDN_unregister_Bprotocol(&DSP); - - timer_delete_sync(&dsp_spl_tl); - - if (!list_empty(&dsp_ilist)) { - printk(KERN_ERR "mISDN_dsp: Audio DSP object inst list not " - "empty.\n"); - } - if (!list_empty(&conf_ilist)) { - printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not " - "all memory freed.\n"); - } - - dsp_pipeline_module_exit(); -} - -module_init(dsp_init); -module_exit(dsp_cleanup); diff --git a/drivers/isdn/mISDN/dsp_dtmf.c b/drivers/isdn/mISDN/dsp_dtmf.c deleted file mode 100644 index 642f30be5ce2..000000000000 --- a/drivers/isdn/mISDN/dsp_dtmf.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * DTMF decoder. - * - * Copyright by Andreas Eversberg (jolly@eversberg.eu) - * based on different decoders such as ISDN4Linux - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include "core.h" -#include "dsp.h" - -#define NCOEFF 8 /* number of frequencies to be analyzed */ - -/* For DTMF recognition: - * 2 * cos(2 * PI * k / N) precalculated for all k - */ -static u64 cos2pik[NCOEFF] = -{ - /* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */ - 55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630 -}; - -/* digit matrix */ -static char dtmf_matrix[4][4] = -{ - {'1', '2', '3', 'A'}, - {'4', '5', '6', 'B'}, - {'7', '8', '9', 'C'}, - {'*', '0', '#', 'D'} -}; - -/* dtmf detection using goertzel algorithm - * init function - */ -void dsp_dtmf_goertzel_init(struct dsp *dsp) -{ - dsp->dtmf.size = 0; - dsp->dtmf.lastwhat = '\0'; - dsp->dtmf.lastdigit = '\0'; - dsp->dtmf.count = 0; -} - -/* check for hardware or software features - */ -void dsp_dtmf_hardware(struct dsp *dsp) -{ - int hardware = 1; - - if (!dsp->dtmf.enable) - return; - - if (!dsp->features.hfc_dtmf) - hardware = 0; - - /* check for volume change */ - if (dsp->tx_volume) { - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " - "because tx_volume is changed\n", - __func__, dsp->name); - hardware = 0; - } - if (dsp->rx_volume) { - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " - "because rx_volume is changed\n", - __func__, dsp->name); - hardware = 0; - } - /* check if encryption is enabled */ - if (dsp->bf_enable) { - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " - "because encryption is enabled\n", - __func__, dsp->name); - hardware = 0; - } - /* check if pipeline exists */ - if (dsp->pipeline.inuse) { - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " - "because pipeline exists.\n", - __func__, dsp->name); - hardware = 0; - } - - dsp->dtmf.hardware = hardware; - dsp->dtmf.software = !hardware; -} - - -/************************************************************* - * calculate the coefficients of the given sample and decode * - *************************************************************/ - -/* the given sample is decoded. if the sample is not long enough for a - * complete frame, the decoding is finished and continued with the next - * call of this function. - * - * the algorithm is very good for detection with a minimum of errors. i - * tested it allot. it even works with very short tones (40ms). the only - * disadvantage is, that it doesn't work good with different volumes of both - * tones. this will happen, if accoustically coupled dialers are used. - * it sometimes detects tones during speech, which is normal for decoders. - * use sequences to given commands during calls. - * - * dtmf - points to a structure of the current dtmf state - * spl and len - the sample - * fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder - */ - -u8 -*dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, int fmt) -{ - u8 what; - int size; - signed short *buf; - s32 sk, sk1, sk2; - int k, n, i; - s32 *hfccoeff; - s32 result[NCOEFF], tresh, treshl; - int lowgroup, highgroup; - s64 cos2pik_; - - dsp->dtmf.digits[0] = '\0'; - - /* Note: The function will loop until the buffer has not enough samples - * left to decode a full frame. - */ -again: - /* convert samples */ - size = dsp->dtmf.size; - buf = dsp->dtmf.buffer; - switch (fmt) { - case 0: /* alaw */ - case 1: /* ulaw */ - while (size < DSP_DTMF_NPOINTS && len) { - buf[size++] = dsp_audio_law_to_s32[*data++]; - len--; - } - break; - - case 2: /* HFC coefficients */ - default: - if (len < 64) { - if (len > 0) - printk(KERN_ERR "%s: coefficients have invalid " - "size. (is=%d < must=%d)\n", - __func__, len, 64); - return dsp->dtmf.digits; - } - hfccoeff = (s32 *)data; - for (k = 0; k < NCOEFF; k++) { - sk2 = (*hfccoeff++) >> 4; - sk = (*hfccoeff++) >> 4; - if (sk > 32767 || sk < -32767 || sk2 > 32767 - || sk2 < -32767) - printk(KERN_WARNING - "DTMF-Detection overflow\n"); - /* compute |X(k)|**2 */ - result[k] = - (sk * sk) - - (((cos2pik[k] * sk) >> 15) * sk2) + - (sk2 * sk2); - } - data += 64; - len -= 64; - goto coefficients; - break; - } - dsp->dtmf.size = size; - - if (size < DSP_DTMF_NPOINTS) - return dsp->dtmf.digits; - - dsp->dtmf.size = 0; - - /* now we have a full buffer of signed long samples - we do goertzel */ - for (k = 0; k < NCOEFF; k++) { - sk = 0; - sk1 = 0; - sk2 = 0; - buf = dsp->dtmf.buffer; - cos2pik_ = cos2pik[k]; - for (n = 0; n < DSP_DTMF_NPOINTS; n++) { - sk = ((cos2pik_ * sk1) >> 15) - sk2 + (*buf++); - sk2 = sk1; - sk1 = sk; - } - sk >>= 8; - sk2 >>= 8; - if (sk > 32767 || sk < -32767 || sk2 > 32767 || sk2 < -32767) - printk(KERN_WARNING "DTMF-Detection overflow\n"); - /* compute |X(k)|**2 */ - result[k] = - (sk * sk) - - (((cos2pik[k] * sk) >> 15) * sk2) + - (sk2 * sk2); - } - - /* our (squared) coefficients have been calculated, we need to process - * them. - */ -coefficients: - tresh = 0; - for (i = 0; i < NCOEFF; i++) { - if (result[i] < 0) - result[i] = 0; - if (result[i] > dsp->dtmf.treshold) { - if (result[i] > tresh) - tresh = result[i]; - } - } - - if (tresh == 0) { - what = 0; - goto storedigit; - } - - if (dsp_debug & DEBUG_DSP_DTMFCOEFF) { - s32 tresh_100 = tresh/100; - - if (tresh_100 == 0) { - tresh_100 = 1; - printk(KERN_DEBUG - "tresh(%d) too small set tresh/100 to 1\n", - tresh); - } - printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d" - " tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n", - result[0] / 10000, result[1] / 10000, result[2] / 10000, - result[3] / 10000, result[4] / 10000, result[5] / 10000, - result[6] / 10000, result[7] / 10000, tresh / 10000, - result[0] / (tresh_100), result[1] / (tresh_100), - result[2] / (tresh_100), result[3] / (tresh_100), - result[4] / (tresh_100), result[5] / (tresh_100), - result[6] / (tresh_100), result[7] / (tresh_100)); - } - - /* calc digit (lowgroup/highgroup) */ - lowgroup = -1; - highgroup = -1; - treshl = tresh >> 3; /* tones which are not on, must be below 9 dB */ - tresh = tresh >> 2; /* touchtones must match within 6 dB */ - for (i = 0; i < NCOEFF; i++) { - if (result[i] < treshl) - continue; /* ignore */ - if (result[i] < tresh) { - lowgroup = -1; - highgroup = -1; - break; /* noise in between */ - } - /* good level found. This is allowed only one time per group */ - if (i < NCOEFF / 2) { - /* lowgroup */ - if (lowgroup >= 0) { - /* Bad. Another tone found. */ - lowgroup = -1; - break; - } else - lowgroup = i; - } else { - /* higroup */ - if (highgroup >= 0) { - /* Bad. Another tone found. */ - highgroup = -1; - break; - } else - highgroup = i - (NCOEFF / 2); - } - } - - /* get digit or null */ - what = 0; - if (lowgroup >= 0 && highgroup >= 0) - what = dtmf_matrix[lowgroup][highgroup]; - -storedigit: - if (what && (dsp_debug & DEBUG_DSP_DTMF)) - printk(KERN_DEBUG "DTMF what: %c\n", what); - - if (dsp->dtmf.lastwhat != what) - dsp->dtmf.count = 0; - - /* the tone (or no tone) must remain 3 times without change */ - if (dsp->dtmf.count == 2) { - if (dsp->dtmf.lastdigit != what) { - dsp->dtmf.lastdigit = what; - if (what) { - if (dsp_debug & DEBUG_DSP_DTMF) - printk(KERN_DEBUG "DTMF digit: %c\n", - what); - if ((strlen(dsp->dtmf.digits) + 1) - < sizeof(dsp->dtmf.digits)) { - dsp->dtmf.digits[strlen( - dsp->dtmf.digits) + 1] = '\0'; - dsp->dtmf.digits[strlen( - dsp->dtmf.digits)] = what; - } - } - } - } else - dsp->dtmf.count++; - - dsp->dtmf.lastwhat = what; - - goto again; -} diff --git a/drivers/isdn/mISDN/dsp_ecdis.h b/drivers/isdn/mISDN/dsp_ecdis.h deleted file mode 100644 index 4bcdf321875d..000000000000 --- a/drivers/isdn/mISDN/dsp_ecdis.h +++ /dev/null @@ -1,96 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * SpanDSP - a series of DSP components for telephony - * - * ec_disable_detector.h - A detector which should eventually meet the - * G.164/G.165 requirements for detecting the - * 2100Hz echo cancellor disable tone. - * - * Written by Steve Underwood - * - * Copyright (C) 2001 Steve Underwood - * - * All rights reserved. - */ - -#include "dsp_biquad.h" - -struct ec_disable_detector_state { - struct biquad2_state notch; - int notch_level; - int channel_level; - int tone_present; - int tone_cycle_duration; - int good_cycles; - int hit; -}; - - -#define FALSE 0 -#define TRUE (!FALSE) - -static inline void -echo_can_disable_detector_init(struct ec_disable_detector_state *det) -{ - /* Elliptic notch */ - /* This is actually centred at 2095Hz, but gets the balance we want, due - to the asymmetric walls of the notch */ - biquad2_init(&det->notch, - (int32_t)(-0.7600000 * 32768.0), - (int32_t)(-0.1183852 * 32768.0), - (int32_t)(-0.5104039 * 32768.0), - (int32_t)(0.1567596 * 32768.0), - (int32_t)(1.0000000 * 32768.0)); - - det->channel_level = 0; - det->notch_level = 0; - det->tone_present = FALSE; - det->tone_cycle_duration = 0; - det->good_cycles = 0; - det->hit = 0; -} -/*- End of function --------------------------------------------------------*/ - -static inline int -echo_can_disable_detector_update(struct ec_disable_detector_state *det, - int16_t amp) -{ - int16_t notched; - - notched = biquad2(&det->notch, amp); - /* Estimate the overall energy in the channel, and the energy in - the notch (i.e. overall channel energy - tone energy => noise). - Use abs instead of multiply for speed (is it really faster?). - Damp the overall energy a little more for a stable result. - Damp the notch energy a little less, so we don't damp out the - blip every time the phase reverses */ - det->channel_level += ((abs(amp) - det->channel_level) >> 5); - det->notch_level += ((abs(notched) - det->notch_level) >> 4); - if (det->channel_level > 280) { - /* There is adequate energy in the channel. - Is it mostly at 2100Hz? */ - if (det->notch_level * 6 < det->channel_level) { - /* The notch says yes, so we have the tone. */ - if (!det->tone_present) { - /* Do we get a kick every 450+-25ms? */ - if (det->tone_cycle_duration >= 425 * 8 - && det->tone_cycle_duration <= 475 * 8) { - det->good_cycles++; - if (det->good_cycles > 2) - det->hit = TRUE; - } - det->tone_cycle_duration = 0; - } - det->tone_present = TRUE; - } else - det->tone_present = FALSE; - det->tone_cycle_duration++; - } else { - det->tone_present = FALSE; - det->tone_cycle_duration = 0; - det->good_cycles = 0; - } - return det->hit; -} -/*- End of function --------------------------------------------------------*/ -/*- End of file ------------------------------------------------------------*/ diff --git a/drivers/isdn/mISDN/dsp_hwec.c b/drivers/isdn/mISDN/dsp_hwec.c deleted file mode 100644 index 0cd216e28f00..000000000000 --- a/drivers/isdn/mISDN/dsp_hwec.c +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * dsp_hwec.c: - * builtin mISDN dsp pipeline element for enabling the hw echocanceller - * - * Copyright (C) 2007, Nadi Sarrar - * - * Nadi Sarrar - */ - -#include -#include -#include -#include -#include "core.h" -#include "dsp.h" -#include "dsp_hwec.h" - -static struct mISDN_dsp_element_arg args[] = { - { "deftaps", "128", "Set the number of taps of cancellation." }, -}; - -static struct mISDN_dsp_element dsp_hwec_p = { - .name = "hwec", - .new = NULL, - .free = NULL, - .process_tx = NULL, - .process_rx = NULL, - .num_args = ARRAY_SIZE(args), - .args = args, -}; -struct mISDN_dsp_element *dsp_hwec = &dsp_hwec_p; - -void dsp_hwec_enable(struct dsp *dsp, const char *arg) -{ - int deftaps = 128, - len; - struct mISDN_ctrl_req cq; - - if (!dsp) { - printk(KERN_ERR "%s: failed to enable hwec: dsp is NULL\n", - __func__); - return; - } - - if (!arg) - goto _do; - - len = strlen(arg); - if (!len) - goto _do; - - { - char *dup, *next, *tok, *name, *val; - int tmp; - - dup = next = kstrdup(arg, GFP_ATOMIC); - if (!dup) - return; - - while ((tok = strsep(&next, ","))) { - if (!strlen(tok)) - continue; - name = strsep(&tok, "="); - val = tok; - - if (!val) - continue; - - if (!strcmp(name, "deftaps")) { - if (sscanf(val, "%d", &tmp) == 1) - deftaps = tmp; - } - } - - kfree(dup); - } - -_do: - printk(KERN_DEBUG "%s: enabling hwec with deftaps=%d\n", - __func__, deftaps); - memset(&cq, 0, sizeof(cq)); - cq.op = MISDN_CTRL_HFC_ECHOCAN_ON; - cq.p1 = deftaps; - if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) { - printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", - __func__); - return; - } -} - -void dsp_hwec_disable(struct dsp *dsp) -{ - struct mISDN_ctrl_req cq; - - if (!dsp) { - printk(KERN_ERR "%s: failed to disable hwec: dsp is NULL\n", - __func__); - return; - } - - printk(KERN_DEBUG "%s: disabling hwec\n", __func__); - memset(&cq, 0, sizeof(cq)); - cq.op = MISDN_CTRL_HFC_ECHOCAN_OFF; - if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) { - printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", - __func__); - return; - } -} - -int dsp_hwec_init(void) -{ - mISDN_dsp_element_register(dsp_hwec); - - return 0; -} - -void dsp_hwec_exit(void) -{ - mISDN_dsp_element_unregister(dsp_hwec); -} diff --git a/drivers/isdn/mISDN/dsp_hwec.h b/drivers/isdn/mISDN/dsp_hwec.h deleted file mode 100644 index c9cb0ea249da..000000000000 --- a/drivers/isdn/mISDN/dsp_hwec.h +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * dsp_hwec.h - */ - -extern struct mISDN_dsp_element *dsp_hwec; -extern void dsp_hwec_enable(struct dsp *dsp, const char *arg); -extern void dsp_hwec_disable(struct dsp *dsp); -extern int dsp_hwec_init(void); -extern void dsp_hwec_exit(void); diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c deleted file mode 100644 index 55693dc7206b..000000000000 --- a/drivers/isdn/mISDN/dsp_pipeline.c +++ /dev/null @@ -1,300 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * dsp_pipeline.c: pipelined audio processing - * - * Copyright (C) 2007, Nadi Sarrar - * - * Nadi Sarrar - */ - -#include -#include -#include -#include -#include -#include -#include -#include "dsp.h" -#include "dsp_hwec.h" - -struct dsp_pipeline_entry { - struct mISDN_dsp_element *elem; - void *p; - struct list_head list; -}; -struct dsp_element_entry { - struct mISDN_dsp_element *elem; - struct device dev; - struct list_head list; -}; - -static LIST_HEAD(dsp_elements); - -/* sysfs */ -static const struct class elements_class = { - .name = "dsp_pipeline", -}; - -static ssize_t -attr_show_args(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct mISDN_dsp_element *elem = dev_get_drvdata(dev); - int i; - char *p = buf; - - *buf = 0; - for (i = 0; i < elem->num_args; i++) - p += sprintf(p, "Name: %s\n%s%s%sDescription: %s\n\n", - elem->args[i].name, - elem->args[i].def ? "Default: " : "", - elem->args[i].def ? elem->args[i].def : "", - elem->args[i].def ? "\n" : "", - elem->args[i].desc); - - return p - buf; -} - -static struct device_attribute element_attributes[] = { - __ATTR(args, 0444, attr_show_args, NULL), -}; - -static void -mISDN_dsp_dev_release(struct device *dev) -{ - struct dsp_element_entry *entry = - container_of(dev, struct dsp_element_entry, dev); - list_del(&entry->list); - kfree(entry); -} - -int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) -{ - struct dsp_element_entry *entry; - int ret, i; - - if (!elem) - return -EINVAL; - - entry = kzalloc_obj(struct dsp_element_entry, GFP_ATOMIC); - if (!entry) - return -ENOMEM; - - INIT_LIST_HEAD(&entry->list); - entry->elem = elem; - - entry->dev.class = &elements_class; - entry->dev.release = mISDN_dsp_dev_release; - dev_set_drvdata(&entry->dev, elem); - dev_set_name(&entry->dev, "%s", elem->name); - ret = device_register(&entry->dev); - if (ret) { - printk(KERN_ERR "%s: failed to register %s\n", - __func__, elem->name); - goto err1; - } - list_add_tail(&entry->list, &dsp_elements); - - for (i = 0; i < ARRAY_SIZE(element_attributes); ++i) { - ret = device_create_file(&entry->dev, - &element_attributes[i]); - if (ret) { - printk(KERN_ERR "%s: failed to create device file\n", - __func__); - goto err2; - } - } - - return 0; - -err2: - device_unregister(&entry->dev); - return ret; -err1: - put_device(&entry->dev); - return ret; -} -EXPORT_SYMBOL(mISDN_dsp_element_register); - -void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem) -{ - struct dsp_element_entry *entry, *n; - - if (!elem) - return; - - list_for_each_entry_safe(entry, n, &dsp_elements, list) - if (entry->elem == elem) { - device_unregister(&entry->dev); - return; - } - printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name); -} -EXPORT_SYMBOL(mISDN_dsp_element_unregister); - -int dsp_pipeline_module_init(void) -{ - int err; - - err = class_register(&elements_class); - if (err) - return err; - - dsp_hwec_init(); - - return 0; -} - -void dsp_pipeline_module_exit(void) -{ - struct dsp_element_entry *entry, *n; - - dsp_hwec_exit(); - - class_unregister(&elements_class); - - list_for_each_entry_safe(entry, n, &dsp_elements, list) { - list_del(&entry->list); - printk(KERN_WARNING "%s: element was still registered: %s\n", - __func__, entry->elem->name); - kfree(entry); - } -} - -int dsp_pipeline_init(struct dsp_pipeline *pipeline) -{ - if (!pipeline) - return -EINVAL; - - INIT_LIST_HEAD(&pipeline->list); - - return 0; -} - -static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline) -{ - struct dsp_pipeline_entry *entry, *n; - - list_for_each_entry_safe(entry, n, &pipeline->list, list) { - list_del(&entry->list); - if (entry->elem == dsp_hwec) - dsp_hwec_disable(container_of(pipeline, struct dsp, - pipeline)); - else - entry->elem->free(entry->p); - kfree(entry); - } -} - -void dsp_pipeline_destroy(struct dsp_pipeline *pipeline) -{ - - if (!pipeline) - return; - - _dsp_pipeline_destroy(pipeline); -} - -int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) -{ - int found = 0; - char *dup, *next, *tok, *name, *args; - struct dsp_element_entry *entry, *n; - struct dsp_pipeline_entry *pipeline_entry; - struct mISDN_dsp_element *elem; - - if (!pipeline) - return -EINVAL; - - if (!list_empty(&pipeline->list)) - _dsp_pipeline_destroy(pipeline); - - dup = next = kstrdup(cfg, GFP_ATOMIC); - if (!dup) - return 0; - while ((tok = strsep(&next, "|"))) { - if (!strlen(tok)) - continue; - name = strsep(&tok, "("); - args = strsep(&tok, ")"); - if (args && !*args) - args = NULL; - - list_for_each_entry_safe(entry, n, &dsp_elements, list) - if (!strcmp(entry->elem->name, name)) { - elem = entry->elem; - - pipeline_entry = kmalloc_obj(struct dsp_pipeline_entry, - GFP_ATOMIC); - if (!pipeline_entry) { - printk(KERN_ERR "%s: failed to add " - "entry to pipeline: %s (out of " - "memory)\n", __func__, elem->name); - goto _out; - } - pipeline_entry->elem = elem; - - if (elem == dsp_hwec) { - /* This is a hack to make the hwec - available as a pipeline module */ - dsp_hwec_enable(container_of(pipeline, - struct dsp, pipeline), args); - list_add_tail(&pipeline_entry->list, - &pipeline->list); - } else { - pipeline_entry->p = elem->new(args); - if (pipeline_entry->p) { - list_add_tail(&pipeline_entry-> - list, &pipeline->list); - } else { - printk(KERN_ERR "%s: failed " - "to add entry to pipeline: " - "%s (new() returned NULL)\n", - __func__, elem->name); - kfree(pipeline_entry); - } - } - found = 1; - break; - } - - if (found) - found = 0; - else - printk(KERN_ERR "%s: element not found, skipping: " - "%s\n", __func__, name); - } - -_out: - if (!list_empty(&pipeline->list)) - pipeline->inuse = 1; - else - pipeline->inuse = 0; - - kfree(dup); - return 0; -} - -void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len) -{ - struct dsp_pipeline_entry *entry; - - if (!pipeline) - return; - - list_for_each_entry(entry, &pipeline->list, list) - if (entry->elem->process_tx) - entry->elem->process_tx(entry->p, data, len); -} - -void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len, - unsigned int txlen) -{ - struct dsp_pipeline_entry *entry; - - if (!pipeline) - return; - - list_for_each_entry_reverse(entry, &pipeline->list, list) - if (entry->elem->process_rx) - entry->elem->process_rx(entry->p, data, len, txlen); -} diff --git a/drivers/isdn/mISDN/dsp_tones.c b/drivers/isdn/mISDN/dsp_tones.c deleted file mode 100644 index fa7813ae8d97..000000000000 --- a/drivers/isdn/mISDN/dsp_tones.c +++ /dev/null @@ -1,550 +0,0 @@ -/* - * Audio support data for ISDN4Linux. - * - * Copyright Andreas Eversberg (jolly@eversberg.eu) - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include -#include "core.h" -#include "dsp.h" - - -#define DATA_S sample_silence -#define SIZE_S (&sizeof_silence) -#define DATA_GA sample_german_all -#define SIZE_GA (&sizeof_german_all) -#define DATA_GO sample_german_old -#define SIZE_GO (&sizeof_german_old) -#define DATA_DT sample_american_dialtone -#define SIZE_DT (&sizeof_american_dialtone) -#define DATA_RI sample_american_ringing -#define SIZE_RI (&sizeof_american_ringing) -#define DATA_BU sample_american_busy -#define SIZE_BU (&sizeof_american_busy) -#define DATA_S1 sample_special1 -#define SIZE_S1 (&sizeof_special1) -#define DATA_S2 sample_special2 -#define SIZE_S2 (&sizeof_special2) -#define DATA_S3 sample_special3 -#define SIZE_S3 (&sizeof_special3) - -/***************/ -/* tones loops */ -/***************/ - -/* all tones are alaw encoded */ -/* the last sample+1 is in phase with the first sample. the error is low */ - -static u8 sample_german_all[] = { - 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, - 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, - 0xdc, 0xfc, 0x6c, - 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, - 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, - 0xdc, 0xfc, 0x6c, - 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, - 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, - 0xdc, 0xfc, 0x6c, - 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, - 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, - 0xdc, 0xfc, 0x6c, -}; -static u32 sizeof_german_all = sizeof(sample_german_all); - -static u8 sample_german_old[] = { - 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, - 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, - 0x8c, - 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, - 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, - 0x8c, - 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, - 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, - 0x8c, - 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, - 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, - 0x8c, -}; -static u32 sizeof_german_old = sizeof(sample_german_old); - -static u8 sample_american_dialtone[] = { - 0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c, - 0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d, - 0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0, - 0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67, - 0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67, - 0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef, - 0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8, - 0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61, - 0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e, - 0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30, - 0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d, - 0x6d, 0x91, 0x19, -}; -static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone); - -static u8 sample_american_ringing[] = { - 0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90, - 0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed, - 0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c, - 0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d, - 0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec, - 0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11, - 0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00, - 0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39, - 0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6, - 0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3, - 0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b, - 0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f, - 0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56, - 0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59, - 0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30, - 0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d, - 0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c, - 0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd, - 0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc, - 0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d, - 0x4d, 0xbd, 0x0d, 0xad, 0xe1, -}; -static u32 sizeof_american_ringing = sizeof(sample_american_ringing); - -static u8 sample_american_busy[] = { - 0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66, - 0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96, - 0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57, - 0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f, - 0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40, - 0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d, - 0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c, - 0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d, - 0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40, - 0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7, - 0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a, - 0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7, - 0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40, - 0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d, - 0x4d, 0x4d, 0x6d, 0x01, -}; -static u32 sizeof_american_busy = sizeof(sample_american_busy); - -static u8 sample_special1[] = { - 0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d, - 0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd, - 0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd, - 0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd, - 0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed, - 0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41, - 0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7, - 0x6d, 0xbd, 0x2d, -}; -static u32 sizeof_special1 = sizeof(sample_special1); - -static u8 sample_special2[] = { - 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, - 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, - 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, - 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, - 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, - 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, - 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, - 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, - 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, - 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, -}; -static u32 sizeof_special2 = sizeof(sample_special2); - -static u8 sample_special3[] = { - 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, - 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, - 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, - 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, - 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, - 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, - 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, - 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, - 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, - 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, -}; -static u32 sizeof_special3 = sizeof(sample_special3); - -static u8 sample_silence[] = { - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, -}; -static u32 sizeof_silence = sizeof(sample_silence); - -struct tones_samples { - u32 *len; - u8 *data; -}; -static struct -tones_samples samples[] = { - {&sizeof_german_all, sample_german_all}, - {&sizeof_german_old, sample_german_old}, - {&sizeof_american_dialtone, sample_american_dialtone}, - {&sizeof_american_ringing, sample_american_ringing}, - {&sizeof_american_busy, sample_american_busy}, - {&sizeof_special1, sample_special1}, - {&sizeof_special2, sample_special2}, - {&sizeof_special3, sample_special3}, - {NULL, NULL}, -}; - -/*********************************** - * generate ulaw from alaw samples * - ***********************************/ - -void -dsp_audio_generate_ulaw_samples(void) -{ - int i, j; - - i = 0; - while (samples[i].len) { - j = 0; - while (j < (*samples[i].len)) { - samples[i].data[j] = - dsp_audio_alaw_to_ulaw[samples[i].data[j]]; - j++; - } - i++; - } -} - - -/**************************** - * tone sequence definition * - ****************************/ - -static struct pattern { - int tone; - u8 *data[10]; - u32 *siz[10]; - u32 seq[10]; -} pattern[] = { - {TONE_GERMAN_DIALTONE, - {DATA_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_OLDDIALTONE, - {DATA_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_AMERICAN_DIALTONE, - {DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_DIALPBX, - {DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, - NULL}, - {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, - NULL}, - {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, - - {TONE_GERMAN_OLDDIALPBX, - {DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, - NULL}, - {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, - NULL}, - {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, - - {TONE_AMERICAN_DIALPBX, - {DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, NULL, NULL, NULL, - NULL}, - {SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, NULL, NULL, NULL, - NULL}, - {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, - - {TONE_GERMAN_RINGING, - {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_OLDRINGING, - {DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_AMERICAN_RINGING, - {DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_RINGPBX, - {DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_OLDRINGPBX, - {DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, - - {TONE_AMERICAN_RINGPBX, - {DATA_RI, DATA_S, DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_BUSY, - {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_OLDBUSY, - {DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_AMERICAN_BUSY, - {DATA_BU, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_BU, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_HANGUP, - {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_OLDHANGUP, - {DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_AMERICAN_HANGUP, - {DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_SPECIAL_INFO, - {DATA_S1, DATA_S2, DATA_S3, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_GASSENBESETZT, - {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} }, - - {TONE_GERMAN_AUFSCHALTTON, - {DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL}, - {1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} }, - - {0, - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, -}; - -/****************** - * copy tone data * - ******************/ - -/* an sk_buff is generated from the number of samples needed. - * the count will be changed and may begin from 0 each pattern period. - * the clue is to precalculate the pointers and legths to use only one - * memcpy per function call, or two memcpy if the tone sequence changes. - * - * pattern - the type of the pattern - * count - the sample from the beginning of the pattern (phase) - * len - the number of bytes - * - * return - the sk_buff with the sample - * - * if tones has finished (e.g. knocking tone), dsp->tones is turned off - */ -void dsp_tone_copy(struct dsp *dsp, u8 *data, int len) -{ - int index, count, start, num; - struct pattern *pat; - struct dsp_tone *tone = &dsp->tone; - - /* if we have no tone, we copy silence */ - if (!tone->tone) { - memset(data, dsp_silence, len); - return; - } - - /* process pattern */ - pat = (struct pattern *)tone->pattern; - /* points to the current pattern */ - index = tone->index; /* gives current sequence index */ - count = tone->count; /* gives current sample */ - - /* copy sample */ - while (len) { - /* find sample to start with */ - while (42) { - /* wrap around */ - if (!pat->seq[index]) { - count = 0; - index = 0; - } - /* check if we are currently playing this tone */ - if (count < pat->seq[index]) - break; - if (dsp_debug & DEBUG_DSP_TONE) - printk(KERN_DEBUG "%s: reaching next sequence " - "(index=%d)\n", __func__, index); - count -= pat->seq[index]; - index++; - } - /* calculate start and number of samples */ - start = count % (*(pat->siz[index])); - num = len; - if (num + count > pat->seq[index]) - num = pat->seq[index] - count; - if (num + start > (*(pat->siz[index]))) - num = (*(pat->siz[index])) - start; - /* copy memory */ - memcpy(data, pat->data[index] + start, num); - /* reduce length */ - data += num; - count += num; - len -= num; - } - tone->index = index; - tone->count = count; - - /* return sk_buff */ - return; -} - - -/******************************* - * send HW message to hfc card * - *******************************/ - -static void -dsp_tone_hw_message(struct dsp *dsp, u8 *sample, int len) -{ - struct sk_buff *nskb; - - /* unlocking is not required, because we don't expect a response */ - nskb = _alloc_mISDN_skb(PH_CONTROL_REQ, - (len) ? HFC_SPL_LOOP_ON : HFC_SPL_LOOP_OFF, len, sample, - GFP_ATOMIC); - if (nskb) { - if (dsp->ch.peer) { - if (dsp->ch.recv(dsp->ch.peer, nskb)) - dev_kfree_skb(nskb); - } else - dev_kfree_skb(nskb); - } -} - - -/***************** - * timer expires * - *****************/ -void -dsp_tone_timeout(struct timer_list *t) -{ - struct dsp *dsp = timer_container_of(dsp, t, tone.tl); - struct dsp_tone *tone = &dsp->tone; - struct pattern *pat = (struct pattern *)tone->pattern; - int index = tone->index; - - if (!tone->tone) - return; - - index++; - if (!pat->seq[index]) - index = 0; - tone->index = index; - - /* set next tone */ - if (pat->data[index] == DATA_S) - dsp_tone_hw_message(dsp, NULL, 0); - else - dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index])); - /* set timer */ - tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000; - add_timer(&tone->tl); -} - - -/******************** - * set/release tone * - ********************/ - -/* - * tones are relaized by streaming or by special loop commands if supported - * by hardware. when hardware is used, the patterns will be controlled by - * timers. - */ -int -dsp_tone(struct dsp *dsp, int tone) -{ - struct pattern *pat; - int i; - struct dsp_tone *tonet = &dsp->tone; - - tonet->software = 0; - tonet->hardware = 0; - - /* we turn off the tone */ - if (!tone) { - if (dsp->features.hfc_loops && timer_pending(&tonet->tl)) - timer_delete(&tonet->tl); - if (dsp->features.hfc_loops) - dsp_tone_hw_message(dsp, NULL, 0); - tonet->tone = 0; - return 0; - } - - pat = NULL; - i = 0; - while (pattern[i].tone) { - if (pattern[i].tone == tone) { - pat = &pattern[i]; - break; - } - i++; - } - if (!pat) { - printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone); - return -EINVAL; - } - if (dsp_debug & DEBUG_DSP_TONE) - printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n", - __func__, tone, 0); - tonet->tone = tone; - tonet->pattern = pat; - tonet->index = 0; - tonet->count = 0; - - if (dsp->features.hfc_loops) { - tonet->hardware = 1; - /* set first tone */ - dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0])); - /* set timer */ - if (timer_pending(&tonet->tl)) - timer_delete(&tonet->tl); - tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000; - add_timer(&tonet->tl); - } else { - tonet->software = 1; - } - - return 0; -} diff --git a/drivers/isdn/mISDN/fsm.c b/drivers/isdn/mISDN/fsm.c deleted file mode 100644 index 825b686496d2..000000000000 --- a/drivers/isdn/mISDN/fsm.c +++ /dev/null @@ -1,176 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * finite state machine implementation - * - * Author Karsten Keil - * - * Thanks to Jan den Ouden - * Fritz Elfert - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include -#include -#include "fsm.h" - -#define FSM_TIMER_DEBUG 0 - -int -mISDN_FsmNew(struct Fsm *fsm, - struct FsmNode *fnlist, int fncount) -{ - int i; - - fsm->jumpmatrix = - kzalloc(array3_size(sizeof(FSMFNPTR), fsm->state_count, - fsm->event_count), - GFP_KERNEL); - if (fsm->jumpmatrix == NULL) - return -ENOMEM; - - for (i = 0; i < fncount; i++) - if ((fnlist[i].state >= fsm->state_count) || - (fnlist[i].event >= fsm->event_count)) { - printk(KERN_ERR - "mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n", - i, (long)fnlist[i].state, (long)fsm->state_count, - (long)fnlist[i].event, (long)fsm->event_count); - } else - fsm->jumpmatrix[fsm->state_count * fnlist[i].event + - fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; - return 0; -} -EXPORT_SYMBOL(mISDN_FsmNew); - -void -mISDN_FsmFree(struct Fsm *fsm) -{ - kfree((void *) fsm->jumpmatrix); -} -EXPORT_SYMBOL(mISDN_FsmFree); - -int -mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg) -{ - FSMFNPTR r; - - if ((fi->state >= fi->fsm->state_count) || - (event >= fi->fsm->event_count)) { - printk(KERN_ERR - "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n", - (long)fi->state, (long)fi->fsm->state_count, event, - (long)fi->fsm->event_count); - return 1; - } - r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; - if (r) { - if (fi->debug) - fi->printdebug(fi, "State %s Event %s", - fi->fsm->strState[fi->state], - fi->fsm->strEvent[event]); - r(fi, event, arg); - return 0; - } else { - if (fi->debug) - fi->printdebug(fi, "State %s Event %s no action", - fi->fsm->strState[fi->state], - fi->fsm->strEvent[event]); - return 1; - } -} -EXPORT_SYMBOL(mISDN_FsmEvent); - -void -mISDN_FsmChangeState(struct FsmInst *fi, int newstate) -{ - fi->state = newstate; - if (fi->debug) - fi->printdebug(fi, "ChangeState %s", - fi->fsm->strState[newstate]); -} -EXPORT_SYMBOL(mISDN_FsmChangeState); - -static void -FsmExpireTimer(struct timer_list *t) -{ - struct FsmTimer *ft = timer_container_of(ft, t, tl); -#if FSM_TIMER_DEBUG - if (ft->fi->debug) - ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft); -#endif - mISDN_FsmEvent(ft->fi, ft->event, ft->arg); -} - -void -mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) -{ - ft->fi = fi; -#if FSM_TIMER_DEBUG - if (ft->fi->debug) - ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft); -#endif - timer_setup(&ft->tl, FsmExpireTimer, 0); -} -EXPORT_SYMBOL(mISDN_FsmInitTimer); - -void -mISDN_FsmDelTimer(struct FsmTimer *ft, int where) -{ -#if FSM_TIMER_DEBUG - if (ft->fi->debug) - ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d", - (long) ft, where); -#endif - timer_delete(&ft->tl); -} -EXPORT_SYMBOL(mISDN_FsmDelTimer); - -int -mISDN_FsmAddTimer(struct FsmTimer *ft, - int millisec, int event, void *arg, int where) -{ - -#if FSM_TIMER_DEBUG - if (ft->fi->debug) - ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d", - (long) ft, millisec, where); -#endif - - if (timer_pending(&ft->tl)) { - if (ft->fi->debug) { - printk(KERN_WARNING - "mISDN_FsmAddTimer: timer already active!\n"); - ft->fi->printdebug(ft->fi, - "mISDN_FsmAddTimer already active!"); - } - return -1; - } - ft->event = event; - ft->arg = arg; - ft->tl.expires = jiffies + (millisec * HZ) / 1000; - add_timer(&ft->tl); - return 0; -} -EXPORT_SYMBOL(mISDN_FsmAddTimer); - -void -mISDN_FsmRestartTimer(struct FsmTimer *ft, - int millisec, int event, void *arg, int where) -{ - -#if FSM_TIMER_DEBUG - if (ft->fi->debug) - ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d", - (long) ft, millisec, where); -#endif - - if (timer_pending(&ft->tl)) - timer_delete(&ft->tl); - ft->event = event; - ft->arg = arg; - ft->tl.expires = jiffies + (millisec * HZ) / 1000; - add_timer(&ft->tl); -} -EXPORT_SYMBOL(mISDN_FsmRestartTimer); diff --git a/drivers/isdn/mISDN/fsm.h b/drivers/isdn/mISDN/fsm.h deleted file mode 100644 index 211554b997f8..000000000000 --- a/drivers/isdn/mISDN/fsm.h +++ /dev/null @@ -1,58 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * - * Author Karsten Keil - * - * Thanks to Jan den Ouden - * Fritz Elfert - * Copyright 2008 by Karsten Keil - */ - -#ifndef _MISDN_FSM_H -#define _MISDN_FSM_H - -#include - -/* Statemachine */ - -struct FsmInst; - -typedef void (*FSMFNPTR)(struct FsmInst *, int, void *); - -struct Fsm { - FSMFNPTR *jumpmatrix; - int state_count, event_count; - char **strEvent, **strState; -}; - -struct FsmInst { - struct Fsm *fsm; - int state; - int debug; - void *userdata; - int userint; - void (*printdebug) (struct FsmInst *, char *, ...); -}; - -struct FsmNode { - int state, event; - void (*routine) (struct FsmInst *, int, void *); -}; - -struct FsmTimer { - struct FsmInst *fi; - struct timer_list tl; - int event; - void *arg; -}; - -extern int mISDN_FsmNew(struct Fsm *, struct FsmNode *, int); -extern void mISDN_FsmFree(struct Fsm *); -extern int mISDN_FsmEvent(struct FsmInst *, int , void *); -extern void mISDN_FsmChangeState(struct FsmInst *, int); -extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *); -extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int); -extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int); -extern void mISDN_FsmDelTimer(struct FsmTimer *, int); - -#endif diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c deleted file mode 100644 index 8c93af06ed02..000000000000 --- a/drivers/isdn/mISDN/hwchannel.c +++ /dev/null @@ -1,516 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include - -static void -dchannel_bh(struct work_struct *ws) -{ - struct dchannel *dch = container_of(ws, struct dchannel, workq); - struct sk_buff *skb; - int err; - - if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) { - while ((skb = skb_dequeue(&dch->rqueue))) { - if (likely(dch->dev.D.peer)) { - err = dch->dev.D.recv(dch->dev.D.peer, skb); - if (err) - dev_kfree_skb(skb); - } else - dev_kfree_skb(skb); - } - } - if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) { - if (dch->phfunc) - dch->phfunc(dch); - } -} - -static void -bchannel_bh(struct work_struct *ws) -{ - struct bchannel *bch = container_of(ws, struct bchannel, workq); - struct sk_buff *skb; - int err; - - if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) { - while ((skb = skb_dequeue(&bch->rqueue))) { - bch->rcount--; - if (likely(bch->ch.peer)) { - err = bch->ch.recv(bch->ch.peer, skb); - if (err) - dev_kfree_skb(skb); - } else - dev_kfree_skb(skb); - } - } -} - -int -mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf) -{ - test_and_set_bit(FLG_HDLC, &ch->Flags); - ch->maxlen = maxlen; - ch->hw = NULL; - ch->rx_skb = NULL; - ch->tx_skb = NULL; - ch->tx_idx = 0; - ch->phfunc = phf; - skb_queue_head_init(&ch->squeue); - skb_queue_head_init(&ch->rqueue); - INIT_LIST_HEAD(&ch->dev.bchannels); - INIT_WORK(&ch->workq, dchannel_bh); - return 0; -} -EXPORT_SYMBOL(mISDN_initdchannel); - -int -mISDN_initbchannel(struct bchannel *ch, unsigned short maxlen, - unsigned short minlen) -{ - ch->Flags = 0; - ch->minlen = minlen; - ch->next_minlen = minlen; - ch->init_minlen = minlen; - ch->maxlen = maxlen; - ch->next_maxlen = maxlen; - ch->init_maxlen = maxlen; - ch->hw = NULL; - ch->rx_skb = NULL; - ch->tx_skb = NULL; - ch->tx_idx = 0; - skb_queue_head_init(&ch->rqueue); - ch->rcount = 0; - ch->next_skb = NULL; - INIT_WORK(&ch->workq, bchannel_bh); - return 0; -} -EXPORT_SYMBOL(mISDN_initbchannel); - -int -mISDN_freedchannel(struct dchannel *ch) -{ - if (ch->tx_skb) { - dev_kfree_skb(ch->tx_skb); - ch->tx_skb = NULL; - } - if (ch->rx_skb) { - dev_kfree_skb(ch->rx_skb); - ch->rx_skb = NULL; - } - skb_queue_purge(&ch->squeue); - skb_queue_purge(&ch->rqueue); - flush_work(&ch->workq); - return 0; -} -EXPORT_SYMBOL(mISDN_freedchannel); - -void -mISDN_clear_bchannel(struct bchannel *ch) -{ - if (ch->tx_skb) { - dev_kfree_skb(ch->tx_skb); - ch->tx_skb = NULL; - } - ch->tx_idx = 0; - if (ch->rx_skb) { - dev_kfree_skb(ch->rx_skb); - ch->rx_skb = NULL; - } - if (ch->next_skb) { - dev_kfree_skb(ch->next_skb); - ch->next_skb = NULL; - } - test_and_clear_bit(FLG_TX_BUSY, &ch->Flags); - test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); - test_and_clear_bit(FLG_ACTIVE, &ch->Flags); - test_and_clear_bit(FLG_FILLEMPTY, &ch->Flags); - test_and_clear_bit(FLG_TX_EMPTY, &ch->Flags); - test_and_clear_bit(FLG_RX_OFF, &ch->Flags); - ch->dropcnt = 0; - ch->minlen = ch->init_minlen; - ch->next_minlen = ch->init_minlen; - ch->maxlen = ch->init_maxlen; - ch->next_maxlen = ch->init_maxlen; - skb_queue_purge(&ch->rqueue); - ch->rcount = 0; -} -EXPORT_SYMBOL(mISDN_clear_bchannel); - -void -mISDN_freebchannel(struct bchannel *ch) -{ - cancel_work_sync(&ch->workq); - mISDN_clear_bchannel(ch); -} -EXPORT_SYMBOL(mISDN_freebchannel); - -int -mISDN_ctrl_bchannel(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_RX_BUFFER | MISDN_CTRL_FILL_EMPTY | - MISDN_CTRL_RX_OFF; - break; - case MISDN_CTRL_FILL_EMPTY: - if (cq->p1) { - memset(bch->fill, cq->p2 & 0xff, MISDN_BCH_FILL_SIZE); - test_and_set_bit(FLG_FILLEMPTY, &bch->Flags); - } else { - test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); - } - break; - case MISDN_CTRL_RX_OFF: - /* read back dropped byte count */ - cq->p2 = bch->dropcnt; - if (cq->p1) - test_and_set_bit(FLG_RX_OFF, &bch->Flags); - else - test_and_clear_bit(FLG_RX_OFF, &bch->Flags); - bch->dropcnt = 0; - break; - case MISDN_CTRL_RX_BUFFER: - if (cq->p2 > MISDN_CTRL_RX_SIZE_IGNORE) - bch->next_maxlen = cq->p2; - if (cq->p1 > MISDN_CTRL_RX_SIZE_IGNORE) - bch->next_minlen = cq->p1; - /* we return the old values */ - cq->p1 = bch->minlen; - cq->p2 = bch->maxlen; - break; - default: - pr_info("mISDN unhandled control %x operation\n", cq->op); - ret = -EINVAL; - break; - } - return ret; -} -EXPORT_SYMBOL(mISDN_ctrl_bchannel); - -static inline u_int -get_sapi_tei(u_char *p) -{ - u_int sapi, tei; - - sapi = *p >> 2; - tei = p[1] >> 1; - return sapi | (tei << 8); -} - -void -recv_Dchannel(struct dchannel *dch) -{ - struct mISDNhead *hh; - - if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */ - dev_kfree_skb(dch->rx_skb); - dch->rx_skb = NULL; - return; - } - hh = mISDN_HEAD_P(dch->rx_skb); - hh->prim = PH_DATA_IND; - hh->id = get_sapi_tei(dch->rx_skb->data); - skb_queue_tail(&dch->rqueue, dch->rx_skb); - dch->rx_skb = NULL; - schedule_event(dch, FLG_RECVQUEUE); -} -EXPORT_SYMBOL(recv_Dchannel); - -void -recv_Echannel(struct dchannel *ech, struct dchannel *dch) -{ - struct mISDNhead *hh; - - if (ech->rx_skb->len < 2) { /* at least 2 for sapi / tei */ - dev_kfree_skb(ech->rx_skb); - ech->rx_skb = NULL; - return; - } - hh = mISDN_HEAD_P(ech->rx_skb); - hh->prim = PH_DATA_E_IND; - hh->id = get_sapi_tei(ech->rx_skb->data); - skb_queue_tail(&dch->rqueue, ech->rx_skb); - ech->rx_skb = NULL; - schedule_event(dch, FLG_RECVQUEUE); -} -EXPORT_SYMBOL(recv_Echannel); - -void -recv_Bchannel(struct bchannel *bch, unsigned int id, bool force) -{ - struct mISDNhead *hh; - - /* if allocation did fail upper functions still may call us */ - if (unlikely(!bch->rx_skb)) - return; - if (unlikely(!bch->rx_skb->len)) { - /* we have no data to send - this may happen after recovery - * from overflow or too small allocation. - * We need to free the buffer here */ - dev_kfree_skb(bch->rx_skb); - bch->rx_skb = NULL; - } else { - if (test_bit(FLG_TRANSPARENT, &bch->Flags) && - (bch->rx_skb->len < bch->minlen) && !force) - return; - hh = mISDN_HEAD_P(bch->rx_skb); - hh->prim = PH_DATA_IND; - hh->id = id; - if (bch->rcount >= 64) { - printk(KERN_WARNING - "B%d receive queue overflow - flushing!\n", - bch->nr); - skb_queue_purge(&bch->rqueue); - } - bch->rcount++; - skb_queue_tail(&bch->rqueue, bch->rx_skb); - bch->rx_skb = NULL; - schedule_event(bch, FLG_RECVQUEUE); - } -} -EXPORT_SYMBOL(recv_Bchannel); - -void -recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb) -{ - skb_queue_tail(&dch->rqueue, skb); - schedule_event(dch, FLG_RECVQUEUE); -} -EXPORT_SYMBOL(recv_Dchannel_skb); - -void -recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb) -{ - if (bch->rcount >= 64) { - printk(KERN_WARNING "B-channel %p receive queue overflow, " - "flushing!\n", bch); - skb_queue_purge(&bch->rqueue); - bch->rcount = 0; - } - bch->rcount++; - skb_queue_tail(&bch->rqueue, skb); - schedule_event(bch, FLG_RECVQUEUE); -} -EXPORT_SYMBOL(recv_Bchannel_skb); - -static void -confirm_Dsend(struct dchannel *dch) -{ - struct sk_buff *skb; - - skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb), - 0, NULL, GFP_ATOMIC); - if (!skb) { - printk(KERN_ERR "%s: no skb id %x\n", __func__, - mISDN_HEAD_ID(dch->tx_skb)); - return; - } - skb_queue_tail(&dch->rqueue, skb); - schedule_event(dch, FLG_RECVQUEUE); -} - -int -get_next_dframe(struct dchannel *dch) -{ - dch->tx_idx = 0; - dch->tx_skb = skb_dequeue(&dch->squeue); - if (dch->tx_skb) { - confirm_Dsend(dch); - return 1; - } - dch->tx_skb = NULL; - test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); - return 0; -} -EXPORT_SYMBOL(get_next_dframe); - -static void -confirm_Bsend(struct bchannel *bch) -{ - struct sk_buff *skb; - - if (bch->rcount >= 64) { - printk(KERN_WARNING "B-channel %p receive queue overflow, " - "flushing!\n", bch); - skb_queue_purge(&bch->rqueue); - bch->rcount = 0; - } - skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb), - 0, NULL, GFP_ATOMIC); - if (!skb) { - printk(KERN_ERR "%s: no skb id %x\n", __func__, - mISDN_HEAD_ID(bch->tx_skb)); - return; - } - bch->rcount++; - skb_queue_tail(&bch->rqueue, skb); - schedule_event(bch, FLG_RECVQUEUE); -} - -int -get_next_bframe(struct bchannel *bch) -{ - bch->tx_idx = 0; - if (test_bit(FLG_TX_NEXT, &bch->Flags)) { - bch->tx_skb = bch->next_skb; - if (bch->tx_skb) { - bch->next_skb = NULL; - test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); - /* confirm imediately to allow next data */ - confirm_Bsend(bch); - return 1; - } else { - test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); - printk(KERN_WARNING "B TX_NEXT without skb\n"); - } - } - bch->tx_skb = NULL; - test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); - return 0; -} -EXPORT_SYMBOL(get_next_bframe); - -void -queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb) -{ - struct mISDNhead *hh; - - if (!skb) { - _queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC); - } else { - if (ch->peer) { - hh = mISDN_HEAD_P(skb); - hh->prim = pr; - hh->id = id; - if (!ch->recv(ch->peer, skb)) - return; - } - dev_kfree_skb(skb); - } -} -EXPORT_SYMBOL(queue_ch_frame); - -int -dchannel_senddata(struct dchannel *ch, struct sk_buff *skb) -{ - /* check oversize */ - if (skb->len <= 0) { - printk(KERN_WARNING "%s: skb too small\n", __func__); - return -EINVAL; - } - if (skb->len > ch->maxlen) { - printk(KERN_WARNING "%s: skb too large(%d/%d)\n", - __func__, skb->len, ch->maxlen); - return -EINVAL; - } - /* HW lock must be obtained */ - if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { - skb_queue_tail(&ch->squeue, skb); - return 0; - } else { - /* write to fifo */ - ch->tx_skb = skb; - ch->tx_idx = 0; - return 1; - } -} -EXPORT_SYMBOL(dchannel_senddata); - -int -bchannel_senddata(struct bchannel *ch, struct sk_buff *skb) -{ - - /* check oversize */ - if (skb->len <= 0) { - printk(KERN_WARNING "%s: skb too small\n", __func__); - return -EINVAL; - } - if (skb->len > ch->maxlen) { - printk(KERN_WARNING "%s: skb too large(%d/%d)\n", - __func__, skb->len, ch->maxlen); - return -EINVAL; - } - /* HW lock must be obtained */ - /* check for pending next_skb */ - if (ch->next_skb) { - printk(KERN_WARNING - "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", - __func__, skb->len, ch->next_skb->len); - return -EBUSY; - } - if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { - test_and_set_bit(FLG_TX_NEXT, &ch->Flags); - ch->next_skb = skb; - return 0; - } else { - /* write to fifo */ - ch->tx_skb = skb; - ch->tx_idx = 0; - confirm_Bsend(ch); - return 1; - } -} -EXPORT_SYMBOL(bchannel_senddata); - -/* The function allocates a new receive skb on demand with a size for the - * requirements of the current protocol. It returns the tailroom of the - * receive skb or an error. - */ -int -bchannel_get_rxbuf(struct bchannel *bch, int reqlen) -{ - int len; - - if (bch->rx_skb) { - len = skb_tailroom(bch->rx_skb); - if (len < reqlen) { - pr_warn("B%d no space for %d (only %d) bytes\n", - bch->nr, reqlen, len); - if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { - /* send what we have now and try a new buffer */ - recv_Bchannel(bch, 0, true); - } else { - /* on HDLC we have to drop too big frames */ - return -EMSGSIZE; - } - } else { - return len; - } - } - /* update current min/max length first */ - if (unlikely(bch->maxlen != bch->next_maxlen)) - bch->maxlen = bch->next_maxlen; - if (unlikely(bch->minlen != bch->next_minlen)) - bch->minlen = bch->next_minlen; - if (unlikely(reqlen > bch->maxlen)) - return -EMSGSIZE; - if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { - if (reqlen >= bch->minlen) { - len = reqlen; - } else { - len = 2 * bch->minlen; - if (len > bch->maxlen) - len = bch->maxlen; - } - } else { - /* with HDLC we do not know the length yet */ - len = bch->maxlen; - } - bch->rx_skb = mI_alloc_skb(len, GFP_ATOMIC); - if (!bch->rx_skb) { - pr_warn("B%d receive no memory for %d bytes\n", bch->nr, len); - len = -ENOMEM; - } - return len; -} -EXPORT_SYMBOL(bchannel_get_rxbuf); diff --git a/drivers/isdn/mISDN/l1oip.h b/drivers/isdn/mISDN/l1oip.h deleted file mode 100644 index 48133d022812..000000000000 --- a/drivers/isdn/mISDN/l1oip.h +++ /dev/null @@ -1,92 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * see notice in l1oip.c - */ - -/* debugging */ -#define DEBUG_L1OIP_INIT 0x00010000 -#define DEBUG_L1OIP_SOCKET 0x00020000 -#define DEBUG_L1OIP_MGR 0x00040000 -#define DEBUG_L1OIP_MSG 0x00080000 - -/* enable to disorder received bchannels by sequence 2143658798... */ -/* - #define REORDER_DEBUG -*/ - -/* frames */ -#define L1OIP_MAX_LEN 2048 /* max packet size form l2 */ -#define L1OIP_MAX_PERFRAME 1400 /* max data size in one frame */ - - -/* timers */ -#define L1OIP_KEEPALIVE 15 -#define L1OIP_TIMEOUT 65 - - -/* socket */ -#define L1OIP_DEFAULTPORT 931 - - -/* channel structure */ -struct l1oip_chan { - struct dchannel *dch; - struct bchannel *bch; - u32 tx_counter; /* counts xmit bytes/packets */ - u32 rx_counter; /* counts recv bytes/packets */ - u32 codecstate; /* used by codec to save data */ -#ifdef REORDER_DEBUG - int disorder_flag; - struct sk_buff *disorder_skb; - u32 disorder_cnt; -#endif -}; - - -/* card structure */ -struct l1oip { - struct list_head list; - - /* card */ - int registered; /* if registered with mISDN */ - char name[MISDN_MAX_IDLEN]; - int idx; /* card index */ - int pri; /* 1=pri, 0=bri */ - int d_idx; /* current dchannel number */ - int b_num; /* number of bchannels */ - u32 id; /* id of connection */ - int ondemand; /* if transmis. is on demand */ - int bundle; /* bundle channels in one frm */ - int codec; /* codec to use for transmis. */ - int limit; /* limit number of bchannels */ - bool shutdown; /* if card is released */ - - /* timer */ - struct timer_list keep_tl; - struct timer_list timeout_tl; - int timeout_on; - struct work_struct workq; - - /* socket */ - struct socket *socket; /* if set, socket is created */ - struct completion socket_complete;/* completion of sock thread */ - struct task_struct *socket_thread; - spinlock_t socket_lock; /* access sock outside thread */ - u32 remoteip; /* if all set, ip is assigned */ - u16 localport; /* must always be set */ - u16 remoteport; /* must always be set */ - struct sockaddr_in sin_local; /* local socket name */ - struct sockaddr_in sin_remote; /* remote socket name */ - struct msghdr sendmsg; /* ip message to send */ - struct kvec sendiov; /* iov for message */ - - /* frame */ - struct l1oip_chan chan[128]; /* channel instances */ -}; - -extern int l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state); -extern int l1oip_4bit_to_law(u8 *data, int len, u8 *result); -extern int l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result); -extern int l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result); -extern void l1oip_4bit_free(void); -extern int l1oip_4bit_alloc(int ulaw); diff --git a/drivers/isdn/mISDN/l1oip_codec.c b/drivers/isdn/mISDN/l1oip_codec.c deleted file mode 100644 index 1059234fbc67..000000000000 --- a/drivers/isdn/mISDN/l1oip_codec.c +++ /dev/null @@ -1,358 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - - * l1oip_codec.c generic codec using lookup table - * -> conversion from a-Law to u-Law - * -> conversion from u-Law to a-Law - * -> compression by reducing the number of sample resolution to 4 - * - * NOTE: It is not compatible with any standard codec like ADPCM. - * - * Author Andreas Eversberg (jolly@eversberg.eu) - * - - */ - -/* - - How the codec works: - -------------------- - - The volume is increased to increase the dynamic range of the audio signal. - Each sample is converted to a-LAW with only 16 steps of level resolution. - A pair of two samples are stored in one byte. - - The first byte is stored in the upper bits, the second byte is stored in the - lower bits. - - To speed up compression and decompression, two lookup tables are formed: - - - 16 bits index for two samples (law encoded) with 8 bit compressed result. - - 8 bits index for one compressed data with 16 bits decompressed result. - - NOTE: The bytes are handled as they are law-encoded. - -*/ - -#include -#include -#include -#include "core.h" -#include "l1oip.h" - -/* definitions of codec. don't use calculations, code may run slower. */ - -static u8 *table_com; -static u16 *table_dec; - - -/* alaw -> ulaw */ -static u8 alaw_to_ulaw[256] = -{ - 0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49, - 0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57, - 0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41, - 0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f, - 0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d, - 0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b, - 0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45, - 0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53, - 0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47, - 0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55, - 0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f, - 0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e, - 0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b, - 0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59, - 0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43, - 0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51, - 0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a, - 0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58, - 0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42, - 0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50, - 0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e, - 0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c, - 0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46, - 0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54, - 0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48, - 0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56, - 0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40, - 0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f, - 0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c, - 0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a, - 0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44, - 0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52 -}; - -/* ulaw -> alaw */ -static u8 ulaw_to_alaw[256] = -{ - 0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35, - 0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25, - 0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d, - 0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d, - 0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31, - 0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21, - 0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9, - 0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9, - 0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47, - 0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf, - 0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f, - 0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33, - 0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23, - 0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b, - 0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b, - 0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b, - 0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34, - 0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24, - 0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c, - 0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c, - 0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30, - 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20, - 0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8, - 0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8, - 0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46, - 0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde, - 0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e, - 0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32, - 0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22, - 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a, - 0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a, - 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a -}; - -/* alaw -> 4bit compression */ -static u8 alaw_to_4bit[256] = { - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0d, 0x02, - 0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x01, 0x0a, 0x05, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, - 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, - 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, -}; - -/* 4bit -> alaw decompression */ -static u8 _4bit_to_alaw[16] = { - 0x5d, 0x51, 0xd9, 0xd7, 0x5f, 0x53, 0xa3, 0x4b, - 0x2a, 0x3a, 0x22, 0x2e, 0x26, 0x56, 0x20, 0x2c, -}; - -/* ulaw -> 4bit compression */ -static u8 ulaw_to_4bit[256] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, -}; - -/* 4bit -> ulaw decompression */ -static u8 _4bit_to_ulaw[16] = { - 0x11, 0x21, 0x31, 0x40, 0x4e, 0x5c, 0x68, 0x71, - 0xfe, 0xef, 0xe7, 0xdb, 0xcd, 0xbf, 0xaf, 0x9f, -}; - - -/* - * Compresses data to the result buffer - * The result size must be at least half of the input buffer. - * The number of samples also must be even! - */ -int -l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state) -{ - int ii, i = 0, o = 0; - - if (!len) - return 0; - - /* send saved byte and first input byte */ - if (*state) { - *result++ = table_com[(((*state) << 8) & 0xff00) | (*data++)]; - len--; - o++; - } - - ii = len >> 1; - - while (i < ii) { - *result++ = table_com[(data[0]<<8) | (data[1])]; - data += 2; - i++; - o++; - } - - /* if len has an odd number, we save byte for next call */ - if (len & 1) - *state = 0x100 + *data; - else - *state = 0; - - return o; -} - -/* Decompress data to the result buffer - * The result size must be the number of sample in packet. (2 * input data) - * The number of samples in the result are even! - */ -int -l1oip_4bit_to_law(u8 *data, int len, u8 *result) -{ - int i = 0; - u16 r; - - while (i < len) { - r = table_dec[*data++]; - *result++ = r >> 8; - *result++ = r; - i++; - } - - return len << 1; -} - - -/* - * law conversion - */ -int -l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result) -{ - int i = 0; - - while (i < len) { - *result++ = alaw_to_ulaw[*data++]; - i++; - } - - return len; -} - -int -l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result) -{ - int i = 0; - - while (i < len) { - *result++ = ulaw_to_alaw[*data++]; - i++; - } - - return len; -} - - -/* - * generate/free compression and decompression table - */ -void -l1oip_4bit_free(void) -{ - vfree(table_dec); - vfree(table_com); - table_com = NULL; - table_dec = NULL; -} - -int -l1oip_4bit_alloc(int ulaw) -{ - int i1, i2, c, sample; - - /* in case, it is called again */ - if (table_dec) - return 0; - - /* alloc conversion tables */ - table_com = vzalloc(65536); - table_dec = vzalloc(512); - if (!table_com || !table_dec) { - l1oip_4bit_free(); - return -ENOMEM; - } - /* generate compression table */ - i1 = 0; - while (i1 < 256) { - if (ulaw) - c = ulaw_to_4bit[i1]; - else - c = alaw_to_4bit[i1]; - i2 = 0; - while (i2 < 256) { - table_com[(i1 << 8) | i2] |= (c << 4); - table_com[(i2 << 8) | i1] |= c; - i2++; - } - i1++; - } - - /* generate decompression table */ - i1 = 0; - while (i1 < 16) { - if (ulaw) - sample = _4bit_to_ulaw[i1]; - else - sample = _4bit_to_alaw[i1]; - i2 = 0; - while (i2 < 16) { - table_dec[(i1 << 4) | i2] |= (sample << 8); - table_dec[(i2 << 4) | i1] |= sample; - i2++; - } - i1++; - } - - return 0; -} diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c deleted file mode 100644 index 6866a0d6b382..000000000000 --- a/drivers/isdn/mISDN/l1oip_core.c +++ /dev/null @@ -1,1505 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - - * l1oip.c low level driver for tunneling layer 1 over IP - * - * NOTE: It is not compatible with TDMoIP nor "ISDN over IP". - * - * Author Andreas Eversberg (jolly@eversberg.eu) - */ - -/* module parameters: - * type: - Value 1 = BRI - Value 2 = PRI - Value 3 = BRI (multi channel frame, not supported yet) - Value 4 = PRI (multi channel frame, not supported yet) - A multi channel frame reduces overhead to a single frame for all - b-channels, but increases delay. - (NOTE: Multi channel frames are not implemented yet.) - - * codec: - Value 0 = transparent (default) - Value 1 = transfer ALAW - Value 2 = transfer ULAW - Value 3 = transfer generic 4 bit compression. - - * ulaw: - 0 = we use a-Law (default) - 1 = we use u-Law - - * limit: - limitation of B-channels to control bandwidth (1...126) - BRI: 1 or 2 - PRI: 1-30, 31-126 (126, because dchannel ist not counted here) - Also limited ressources are used for stack, resulting in less channels. - It is possible to have more channels than 30 in PRI mode, this must - be supported by the application. - - * ip: - byte representation of remote ip address (127.0.0.1 -> 127,0,0,1) - If not given or four 0, no remote address is set. - For multiple interfaces, concat ip addresses. (127,0,0,1,127,0,0,1) - - * port: - port number (local interface) - If not given or 0, port 931 is used for fist instance, 932 for next... - For multiple interfaces, different ports must be given. - - * remoteport: - port number (remote interface) - If not given or 0, remote port equals local port - For multiple interfaces on equal sites, different ports must be given. - - * ondemand: - 0 = fixed (always transmit packets, even when remote side timed out) - 1 = on demand (only transmit packets, when remote side is detected) - the default is 0 - NOTE: ID must also be set for on demand. - - * id: - optional value to identify frames. This value must be equal on both - peers and should be random. If omitted or 0, no ID is transmitted. - - * debug: - NOTE: only one debug value must be given for all cards - enable debugging (see l1oip.h for debug options) - - - Special mISDN controls: - - op = MISDN_CTRL_SETPEER* - p1 = bytes 0-3 : remote IP address in network order (left element first) - p2 = bytes 1-2 : remote port in network order (high byte first) - optional: - p2 = bytes 3-4 : local port in network order (high byte first) - - op = MISDN_CTRL_UNSETPEER* - - * Use l1oipctrl for comfortable setting or removing ip address. - (Layer 1 Over IP CTRL) - - - L1oIP-Protocol - -------------- - - Frame Header: - - 7 6 5 4 3 2 1 0 - +---------------+ - |Ver|T|I|Coding | - +---------------+ - | ID byte 3 * | - +---------------+ - | ID byte 2 * | - +---------------+ - | ID byte 1 * | - +---------------+ - | ID byte 0 * | - +---------------+ - |M| Channel | - +---------------+ - | Length * | - +---------------+ - | Time Base MSB | - +---------------+ - | Time Base LSB | - +---------------+ - | Data.... | - - ... - - | | - +---------------+ - |M| Channel | - +---------------+ - | Length * | - +---------------+ - | Time Base MSB | - +---------------+ - | Time Base LSB | - +---------------+ - | Data.... | - - ... - - - * Only included in some cases. - - - Ver = Version - If version is missmatch, the frame must be ignored. - - - T = Type of interface - Must be 0 for S0 or 1 for E1. - - - I = Id present - If bit is set, four ID bytes are included in frame. - - - ID = Connection ID - Additional ID to prevent Denial of Service attacs. Also it prevents hijacking - connections with dynamic IP. The ID should be random and must not be 0. - - - Coding = Type of codec - Must be 0 for no transcoding. Also for D-channel and other HDLC frames. - 1 and 2 are reserved for explicitly use of a-LAW or u-LAW codec. - 3 is used for generic table compressor. - - - M = More channels to come. If this flag is 1, the following byte contains - the length of the channel data. After the data block, the next channel will - be defined. The flag for the last channel block (or if only one channel is - transmitted), must be 0 and no length is given. - - - Channel = Channel number - 0 reserved - 1-3 channel data for S0 (3 is D-channel) - 1-31 channel data for E1 (16 is D-channel) - 32-127 channel data for extended E1 (16 is D-channel) - - - The length is used if the M-flag is 1. It is used to find the next channel - inside frame. - NOTE: A value of 0 equals 256 bytes of data. - -> For larger data blocks, a single frame must be used. - -> For larger streams, a single frame or multiple blocks with same channel ID - must be used. - - - Time Base = Timestamp of first sample in frame - The "Time Base" is used to rearange packets and to detect packet loss. - The 16 bits are sent in network order (MSB first) and count 1/8000 th of a - second. This causes a wrap around each 8,192 seconds. There is no requirement - for the initial "Time Base", but 0 should be used for the first packet. - In case of HDLC data, this timestamp counts the packet or byte number. - - - Two Timers: - - After initialisation, a timer of 15 seconds is started. Whenever a packet is - transmitted, the timer is reset to 15 seconds again. If the timer expires, an - empty packet is transmitted. This keep the connection alive. - - When a valid packet is received, a timer 65 seconds is started. The interface - become ACTIVE. If the timer expires, the interface becomes INACTIVE. - - - Dynamic IP handling: - - To allow dynamic IP, the ID must be non 0. In this case, any packet with the - correct port number and ID will be accepted. If the remote side changes its IP - the new IP is used for all transmitted packets until it changes again. - - - On Demand: - - If the ondemand parameter is given, the remote IP is set to 0 on timeout. - This will stop keepalive traffic to remote. If the remote is online again, - traffic will continue to the remote address. This is useful for road warriors. - This feature only works with ID set, otherwhise it is highly unsecure. - - - Socket and Thread - ----------------- - - The complete socket opening and closing is done by a thread. - When the thread opened a socket, the hc->socket descriptor is set. Whenever a - packet shall be sent to the socket, the hc->socket must be checked whether not - NULL. To prevent change in socket descriptor, the hc->socket_lock must be used. - To change the socket, a recall of l1oip_socket_open() will safely kill the - socket process and create a new one. - -*/ - -#define L1OIP_VERSION 0 /* 0...3 */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "core.h" -#include "l1oip.h" - -static const char *l1oip_revision = "2.00"; - -static int l1oip_cnt; -static DEFINE_SPINLOCK(l1oip_lock); -static LIST_HEAD(l1oip_ilist); - -#define MAX_CARDS 16 -static u_int type[MAX_CARDS]; -static u_int codec[MAX_CARDS]; -static u_int ip[MAX_CARDS * 4]; -static u_int port[MAX_CARDS]; -static u_int remoteport[MAX_CARDS]; -static u_int ondemand[MAX_CARDS]; -static u_int limit[MAX_CARDS]; -static u_int id[MAX_CARDS]; -static int debug; -static int ulaw; - -MODULE_AUTHOR("Andreas Eversberg"); -MODULE_DESCRIPTION("mISDN driver for tunneling layer 1 over IP"); -MODULE_LICENSE("GPL"); -module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(codec, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(ip, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(remoteport, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(ondemand, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(limit, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(id, uint, NULL, S_IRUGO | S_IWUSR); -module_param(ulaw, uint, S_IRUGO | S_IWUSR); -module_param(debug, uint, S_IRUGO | S_IWUSR); - -/* - * send a frame via socket, if open and restart timer - */ -static int -l1oip_socket_send(struct l1oip *hc, u8 localcodec, u8 channel, u32 chanmask, - u16 timebase, u8 *buf, int len) -{ - u8 *p; - u8 frame[MAX_DFRAME_LEN_L1 + 32]; - struct socket *socket = NULL; - - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: sending data to socket (len = %d)\n", - __func__, len); - - p = frame; - - /* restart timer */ - if (time_before(hc->keep_tl.expires, jiffies + 5 * HZ) && !hc->shutdown) - mod_timer(&hc->keep_tl, jiffies + L1OIP_KEEPALIVE * HZ); - else - hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE * HZ; - - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: resetting timer\n", __func__); - - /* drop if we have no remote ip or port */ - if (!hc->sin_remote.sin_addr.s_addr || !hc->sin_remote.sin_port) { - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: dropping frame, because remote " - "IP is not set.\n", __func__); - return len; - } - - /* assemble frame */ - *p++ = (L1OIP_VERSION << 6) /* version and coding */ - | (hc->pri ? 0x20 : 0x00) /* type */ - | (hc->id ? 0x10 : 0x00) /* id */ - | localcodec; - if (hc->id) { - *p++ = hc->id >> 24; /* id */ - *p++ = hc->id >> 16; - *p++ = hc->id >> 8; - *p++ = hc->id; - } - *p++ = 0x00 + channel; /* m-flag, channel */ - *p++ = timebase >> 8; /* time base */ - *p++ = timebase; - - if (buf && len) { /* add data to frame */ - if (localcodec == 1 && ulaw) - l1oip_ulaw_to_alaw(buf, len, p); - else if (localcodec == 2 && !ulaw) - l1oip_alaw_to_ulaw(buf, len, p); - else if (localcodec == 3) - len = l1oip_law_to_4bit(buf, len, p, - &hc->chan[channel].codecstate); - else - memcpy(p, buf, len); - } - len += p - frame; - - /* check for socket in safe condition */ - spin_lock(&hc->socket_lock); - if (!hc->socket) { - spin_unlock(&hc->socket_lock); - return 0; - } - /* seize socket */ - socket = hc->socket; - hc->socket = NULL; - spin_unlock(&hc->socket_lock); - /* send packet */ - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: sending packet to socket (len " - "= %d)\n", __func__, len); - hc->sendiov.iov_base = frame; - hc->sendiov.iov_len = len; - len = kernel_sendmsg(socket, &hc->sendmsg, &hc->sendiov, 1, len); - /* give socket back */ - hc->socket = socket; /* no locking required */ - - return len; -} - - -/* - * receive channel data from socket - */ -static void -l1oip_socket_recv(struct l1oip *hc, u8 remotecodec, u8 channel, u16 timebase, - u8 *buf, int len) -{ - struct sk_buff *nskb; - struct bchannel *bch; - struct dchannel *dch; - u8 *p; - u32 rx_counter; - - if (len == 0) { - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: received empty keepalive data, " - "ignoring\n", __func__); - return; - } - - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: received data, sending to mISDN (%d)\n", - __func__, len); - - if (channel < 1 || channel > 127) { - printk(KERN_WARNING "%s: packet error - channel %d out of " - "range\n", __func__, channel); - return; - } - dch = hc->chan[channel].dch; - bch = hc->chan[channel].bch; - if (!dch && !bch) { - printk(KERN_WARNING "%s: packet error - channel %d not in " - "stack\n", __func__, channel); - return; - } - - /* prepare message */ - nskb = mI_alloc_skb((remotecodec == 3) ? (len << 1) : len, GFP_ATOMIC); - if (!nskb) { - printk(KERN_ERR "%s: No mem for skb.\n", __func__); - return; - } - p = skb_put(nskb, (remotecodec == 3) ? (len << 1) : len); - - if (remotecodec == 1 && ulaw) - l1oip_alaw_to_ulaw(buf, len, p); - else if (remotecodec == 2 && !ulaw) - l1oip_ulaw_to_alaw(buf, len, p); - else if (remotecodec == 3) - len = l1oip_4bit_to_law(buf, len, p); - else - memcpy(p, buf, len); - - /* send message up */ - if (dch && len >= 2) { - dch->rx_skb = nskb; - recv_Dchannel(dch); - } - if (bch) { - /* expand 16 bit sequence number to 32 bit sequence number */ - rx_counter = hc->chan[channel].rx_counter; - if (((s16)(timebase - rx_counter)) >= 0) { - /* time has changed forward */ - if (timebase >= (rx_counter & 0xffff)) - rx_counter = - (rx_counter & 0xffff0000) | timebase; - else - rx_counter = ((rx_counter & 0xffff0000) + 0x10000) - | timebase; - } else { - /* time has changed backwards */ - if (timebase < (rx_counter & 0xffff)) - rx_counter = - (rx_counter & 0xffff0000) | timebase; - else - rx_counter = ((rx_counter & 0xffff0000) - 0x10000) - | timebase; - } - hc->chan[channel].rx_counter = rx_counter; - -#ifdef REORDER_DEBUG - if (hc->chan[channel].disorder_flag) { - swap(hc->chan[channel].disorder_skb, nskb); - swap(hc->chan[channel].disorder_cnt, rx_counter); - } - hc->chan[channel].disorder_flag ^= 1; - if (nskb) -#endif - queue_ch_frame(&bch->ch, PH_DATA_IND, rx_counter, nskb); - } -} - - -/* - * parse frame and extract channel data - */ -static void -l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len) -{ - u32 packet_id; - u8 channel; - u8 remotecodec; - u16 timebase; - int m, mlen; - int len_start = len; /* initial frame length */ - struct dchannel *dch = hc->chan[hc->d_idx].dch; - - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: received frame, parsing... (%d)\n", - __func__, len); - - /* check length */ - if (len < 1 + 1 + 2) { - printk(KERN_WARNING "%s: packet error - length %d below " - "4 bytes\n", __func__, len); - return; - } - - /* check version */ - if (((*buf) >> 6) != L1OIP_VERSION) { - printk(KERN_WARNING "%s: packet error - unknown version %d\n", - __func__, buf[0]>>6); - return; - } - - /* check type */ - if (((*buf) & 0x20) && !hc->pri) { - printk(KERN_WARNING "%s: packet error - received E1 packet " - "on S0 interface\n", __func__); - return; - } - if (!((*buf) & 0x20) && hc->pri) { - printk(KERN_WARNING "%s: packet error - received S0 packet " - "on E1 interface\n", __func__); - return; - } - - /* get id flag */ - packet_id = (*buf >> 4) & 1; - - /* check coding */ - remotecodec = (*buf) & 0x0f; - if (remotecodec > 3) { - printk(KERN_WARNING "%s: packet error - remotecodec %d " - "unsupported\n", __func__, remotecodec); - return; - } - buf++; - len--; - - /* check packet_id */ - if (packet_id) { - if (!hc->id) { - printk(KERN_WARNING "%s: packet error - packet has id " - "0x%x, but we have not\n", __func__, packet_id); - return; - } - if (len < 4) { - printk(KERN_WARNING "%s: packet error - packet too " - "short for ID value\n", __func__); - return; - } - packet_id = (*buf++) << 24; - packet_id += (*buf++) << 16; - packet_id += (*buf++) << 8; - packet_id += (*buf++); - len -= 4; - - if (packet_id != hc->id) { - printk(KERN_WARNING "%s: packet error - ID mismatch, " - "got 0x%x, we 0x%x\n", - __func__, packet_id, hc->id); - return; - } - } else { - if (hc->id) { - printk(KERN_WARNING "%s: packet error - packet has no " - "ID, but we have\n", __func__); - return; - } - } - -multiframe: - if (len < 1) { - printk(KERN_WARNING "%s: packet error - packet too short, " - "channel expected at position %d.\n", - __func__, len-len_start + 1); - return; - } - - /* get channel and multiframe flag */ - channel = *buf & 0x7f; - m = *buf >> 7; - buf++; - len--; - - /* check length on multiframe */ - if (m) { - if (len < 1) { - printk(KERN_WARNING "%s: packet error - packet too " - "short, length expected at position %d.\n", - __func__, len_start - len - 1); - return; - } - - mlen = *buf++; - len--; - if (mlen == 0) - mlen = 256; - if (len < mlen + 3) { - printk(KERN_WARNING "%s: packet error - length %d at " - "position %d exceeds total length %d.\n", - __func__, mlen, len_start-len - 1, len_start); - return; - } - if (len == mlen + 3) { - printk(KERN_WARNING "%s: packet error - length %d at " - "position %d will not allow additional " - "packet.\n", - __func__, mlen, len_start-len + 1); - return; - } - } else - mlen = len - 2; /* single frame, subtract timebase */ - - if (len < 2) { - printk(KERN_WARNING "%s: packet error - packet too short, time " - "base expected at position %d.\n", - __func__, len-len_start + 1); - return; - } - - /* get time base */ - timebase = (*buf++) << 8; - timebase |= (*buf++); - len -= 2; - - /* if inactive, we send up a PH_ACTIVATE and activate */ - if (!test_bit(FLG_ACTIVE, &dch->Flags)) { - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: interface become active due to " - "received packet\n", __func__); - test_and_set_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_ATOMIC); - } - - /* distribute packet */ - l1oip_socket_recv(hc, remotecodec, channel, timebase, buf, mlen); - buf += mlen; - len -= mlen; - - /* multiframe */ - if (m) - goto multiframe; - - /* restart timer */ - if ((time_before(hc->timeout_tl.expires, jiffies + 5 * HZ) || - !hc->timeout_on) && - !hc->shutdown) { - hc->timeout_on = 1; - mod_timer(&hc->timeout_tl, jiffies + L1OIP_TIMEOUT * HZ); - } else /* only adjust timer */ - hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT * HZ; - - /* if ip or source port changes */ - if ((hc->sin_remote.sin_addr.s_addr != sin->sin_addr.s_addr) - || (hc->sin_remote.sin_port != sin->sin_port)) { - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: remote address changes from " - "0x%08x to 0x%08x (port %d to %d)\n", __func__, - ntohl(hc->sin_remote.sin_addr.s_addr), - ntohl(sin->sin_addr.s_addr), - ntohs(hc->sin_remote.sin_port), - ntohs(sin->sin_port)); - hc->sin_remote.sin_addr.s_addr = sin->sin_addr.s_addr; - hc->sin_remote.sin_port = sin->sin_port; - } -} - - -/* - * socket stuff - */ -static int -l1oip_socket_thread(void *data) -{ - struct l1oip *hc = (struct l1oip *)data; - int ret = 0; - struct sockaddr_in sin_rx; - struct kvec iov; - struct msghdr msg = {.msg_name = &sin_rx, - .msg_namelen = sizeof(sin_rx)}; - unsigned char *recvbuf; - size_t recvbuf_size = 1500; - int recvlen; - struct socket *socket = NULL; - DECLARE_COMPLETION_ONSTACK(wait); - - /* allocate buffer memory */ - recvbuf = kmalloc(recvbuf_size, GFP_KERNEL); - if (!recvbuf) { - printk(KERN_ERR "%s: Failed to alloc recvbuf.\n", __func__); - ret = -ENOMEM; - goto fail; - } - - iov.iov_base = recvbuf; - iov.iov_len = recvbuf_size; - - /* make daemon */ - allow_signal(SIGTERM); - - /* create socket */ - if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &socket)) { - printk(KERN_ERR "%s: Failed to create socket.\n", __func__); - ret = -EIO; - goto fail; - } - - /* set incoming address */ - hc->sin_local.sin_family = AF_INET; - hc->sin_local.sin_addr.s_addr = INADDR_ANY; - hc->sin_local.sin_port = htons((unsigned short)hc->localport); - - /* set outgoing address */ - hc->sin_remote.sin_family = AF_INET; - hc->sin_remote.sin_addr.s_addr = htonl(hc->remoteip); - hc->sin_remote.sin_port = htons((unsigned short)hc->remoteport); - - /* bind to incoming port */ - if (socket->ops->bind(socket, (struct sockaddr_unsized *)&hc->sin_local, - sizeof(hc->sin_local))) { - printk(KERN_ERR "%s: Failed to bind socket to port %d.\n", - __func__, hc->localport); - ret = -EINVAL; - goto fail; - } - - /* check sk */ - if (socket->sk == NULL) { - printk(KERN_ERR "%s: socket->sk == NULL\n", __func__); - ret = -EIO; - goto fail; - } - - /* build send message */ - hc->sendmsg.msg_name = &hc->sin_remote; - hc->sendmsg.msg_namelen = sizeof(hc->sin_remote); - hc->sendmsg.msg_control = NULL; - hc->sendmsg.msg_controllen = 0; - - /* give away socket */ - spin_lock(&hc->socket_lock); - hc->socket = socket; - spin_unlock(&hc->socket_lock); - - /* read loop */ - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: socket created and open\n", - __func__); - while (!signal_pending(current)) { - iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, recvbuf_size); - recvlen = sock_recvmsg(socket, &msg, 0); - if (recvlen > 0) { - l1oip_socket_parse(hc, &sin_rx, recvbuf, recvlen); - } else { - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_WARNING - "%s: broken pipe on socket\n", __func__); - } - } - - /* get socket back, check first if in use, maybe by send function */ - spin_lock(&hc->socket_lock); - /* if hc->socket is NULL, it is in use until it is given back */ - while (!hc->socket) { - spin_unlock(&hc->socket_lock); - schedule_timeout(HZ / 10); - spin_lock(&hc->socket_lock); - } - hc->socket = NULL; - spin_unlock(&hc->socket_lock); - - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: socket thread terminating\n", - __func__); - -fail: - /* free recvbuf */ - kfree(recvbuf); - - /* close socket */ - if (socket) - sock_release(socket); - - /* if we got killed, signal completion */ - complete(&hc->socket_complete); - hc->socket_thread = NULL; /* show termination of thread */ - - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: socket thread terminated\n", - __func__); - return ret; -} - -static void -l1oip_socket_close(struct l1oip *hc) -{ - struct dchannel *dch = hc->chan[hc->d_idx].dch; - - /* kill thread */ - if (hc->socket_thread) { - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: socket thread exists, " - "killing...\n", __func__); - send_sig(SIGTERM, hc->socket_thread, 0); - wait_for_completion(&hc->socket_complete); - } - - /* if active, we send up a PH_DEACTIVATE and deactivate */ - if (test_bit(FLG_ACTIVE, &dch->Flags)) { - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: interface become deactivated " - "due to timeout\n", __func__); - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_ATOMIC); - } -} - -static int -l1oip_socket_open(struct l1oip *hc) -{ - /* in case of reopen, we need to close first */ - l1oip_socket_close(hc); - - init_completion(&hc->socket_complete); - - /* create receive process */ - hc->socket_thread = kthread_run(l1oip_socket_thread, hc, "l1oip_%s", - hc->name); - if (IS_ERR(hc->socket_thread)) { - int err = PTR_ERR(hc->socket_thread); - printk(KERN_ERR "%s: Failed (%d) to create socket process.\n", - __func__, err); - hc->socket_thread = NULL; - sock_release(hc->socket); - return err; - } - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: socket thread created\n", __func__); - - return 0; -} - - -static void -l1oip_send_bh(struct work_struct *work) -{ - struct l1oip *hc = container_of(work, struct l1oip, workq); - - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: keepalive timer expired, sending empty " - "frame on dchannel\n", __func__); - - /* send an empty l1oip frame at D-channel */ - l1oip_socket_send(hc, 0, hc->d_idx, 0, 0, NULL, 0); -} - - -/* - * timer stuff - */ -static void -l1oip_keepalive(struct timer_list *t) -{ - struct l1oip *hc = timer_container_of(hc, t, keep_tl); - - schedule_work(&hc->workq); -} - -static void -l1oip_timeout(struct timer_list *t) -{ - struct l1oip *hc = timer_container_of(hc, t, - timeout_tl); - struct dchannel *dch = hc->chan[hc->d_idx].dch; - - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: timeout timer expired, turn layer one " - "down.\n", __func__); - - hc->timeout_on = 0; /* state that timer must be initialized next time */ - - /* if timeout, we send up a PH_DEACTIVATE and deactivate */ - if (test_bit(FLG_ACTIVE, &dch->Flags)) { - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: interface become deactivated " - "due to timeout\n", __func__); - test_and_clear_bit(FLG_ACTIVE, &dch->Flags); - _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, - NULL, GFP_ATOMIC); - } - - /* if we have ondemand set, we remove ip address */ - if (hc->ondemand) { - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: on demand causes ip address to " - "be removed\n", __func__); - hc->sin_remote.sin_addr.s_addr = 0; - } -} - - -/* - * message handling - */ -static int -handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct l1oip *hc = dch->hw; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int ret = -EINVAL; - int l, ll; - unsigned char *p; - - switch (hh->prim) { - case PH_DATA_REQ: - if (skb->len < 1) { - printk(KERN_WARNING "%s: skb too small\n", - __func__); - break; - } - if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) { - printk(KERN_WARNING "%s: skb too large\n", - __func__); - break; - } - /* send frame */ - p = skb->data; - l = skb->len; - while (l) { - /* - * This is technically bounded by L1OIP_MAX_PERFRAME but - * MAX_DFRAME_LEN_L1 < L1OIP_MAX_PERFRAME - */ - ll = (l < MAX_DFRAME_LEN_L1) ? l : MAX_DFRAME_LEN_L1; - l1oip_socket_send(hc, 0, dch->slot, 0, - hc->chan[dch->slot].tx_counter++, p, ll); - p += ll; - l -= ll; - } - skb_trim(skb, 0); - queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb); - return 0; - case PH_ACTIVATE_REQ: - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n" - , __func__, dch->slot, hc->b_num + 1); - skb_trim(skb, 0); - if (test_bit(FLG_ACTIVE, &dch->Flags)) - queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb); - else - queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb); - return 0; - case PH_DEACTIVATE_REQ: - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d " - "(1..%d)\n", __func__, dch->slot, - hc->b_num + 1); - skb_trim(skb, 0); - if (test_bit(FLG_ACTIVE, &dch->Flags)) - queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb); - else - queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb); - return 0; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - struct l1oip *hc = dch->hw; - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER - | MISDN_CTRL_GETPEER; - break; - case MISDN_CTRL_SETPEER: - hc->remoteip = (u32)cq->p1; - hc->remoteport = cq->p2 & 0xffff; - hc->localport = cq->p2 >> 16; - if (!hc->remoteport) - hc->remoteport = hc->localport; - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: got new ip address from user " - "space.\n", __func__); - l1oip_socket_open(hc); - break; - case MISDN_CTRL_UNSETPEER: - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: removing ip address.\n", - __func__); - hc->remoteip = 0; - l1oip_socket_open(hc); - break; - case MISDN_CTRL_GETPEER: - if (debug & DEBUG_L1OIP_SOCKET) - printk(KERN_DEBUG "%s: getting ip address.\n", - __func__); - cq->p1 = hc->remoteip; - cq->p2 = hc->remoteport | (hc->localport << 16); - break; - default: - printk(KERN_WARNING "%s: unknown Op %x\n", - __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -open_dchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq) -{ - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, - dch->dev.id, __builtin_return_address(0)); - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - if ((dch->dev.D.protocol != ISDN_P_NONE) && - (dch->dev.D.protocol != rq->protocol)) { - if (debug & DEBUG_HW_OPEN) - printk(KERN_WARNING "%s: change protocol %x to %x\n", - __func__, dch->dev.D.protocol, rq->protocol); - } - if (dch->dev.D.protocol != rq->protocol) - dch->dev.D.protocol = rq->protocol; - - if (test_bit(FLG_ACTIVE, &dch->Flags)) { - _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, - 0, NULL, GFP_KERNEL); - } - rq->ch = &dch->dev.D; - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", __func__); - return 0; -} - -static int -open_bchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq) -{ - struct bchannel *bch; - int ch; - - if (!test_channelmap(rq->adr.channel, dch->dev.channelmap)) - return -EINVAL; - if (rq->protocol == ISDN_P_NONE) - return -EINVAL; - ch = rq->adr.channel; /* BRI: 1=B1 2=B2 PRI: 1..15,17.. */ - bch = hc->chan[ch].bch; - if (!bch) { - printk(KERN_ERR "%s:internal error ch %d has no bch\n", - __func__, ch); - return -EINVAL; - } - if (test_and_set_bit(FLG_OPEN, &bch->Flags)) - return -EBUSY; /* b-channel can be only open once */ - bch->ch.protocol = rq->protocol; - rq->ch = &bch->ch; - if (!try_module_get(THIS_MODULE)) - printk(KERN_WARNING "%s:cannot get module\n", __func__); - return 0; -} - -static int -l1oip_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); - struct dchannel *dch = container_of(dev, struct dchannel, dev); - struct l1oip *hc = dch->hw; - struct channel_req *rq; - int err = 0; - - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", - __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - rq = arg; - switch (rq->protocol) { - case ISDN_P_TE_S0: - case ISDN_P_NT_S0: - if (hc->pri) { - err = -EINVAL; - break; - } - err = open_dchannel(hc, dch, rq); - break; - case ISDN_P_TE_E1: - case ISDN_P_NT_E1: - if (!hc->pri) { - err = -EINVAL; - break; - } - err = open_dchannel(hc, dch, rq); - break; - default: - err = open_bchannel(hc, dch, rq); - } - break; - case CLOSE_CHANNEL: - if (debug & DEBUG_HW_OPEN) - printk(KERN_DEBUG "%s: dev(%d) close from %p\n", - __func__, dch->dev.id, - __builtin_return_address(0)); - module_put(THIS_MODULE); - break; - case CONTROL_CHANNEL: - err = channel_dctrl(dch, arg); - break; - default: - if (dch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: unknown command %x\n", - __func__, cmd); - err = -EINVAL; - } - return err; -} - -static int -handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - struct l1oip *hc = bch->hw; - int ret = -EINVAL; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int l, ll; - unsigned char *p; - - switch (hh->prim) { - case PH_DATA_REQ: - if (skb->len <= 0) { - printk(KERN_WARNING "%s: skb too small\n", - __func__); - break; - } - if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) { - printk(KERN_WARNING "%s: skb too large\n", - __func__); - break; - } - /* check for AIS / ulaw-silence */ - l = skb->len; - if (!memchr_inv(skb->data, 0xff, l)) { - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: got AIS, not sending, " - "but counting\n", __func__); - hc->chan[bch->slot].tx_counter += l; - skb_trim(skb, 0); - queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb); - return 0; - } - /* check for silence */ - l = skb->len; - if (!memchr_inv(skb->data, 0x2a, l)) { - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: got silence, not sending" - ", but counting\n", __func__); - hc->chan[bch->slot].tx_counter += l; - skb_trim(skb, 0); - queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb); - return 0; - } - - /* send frame */ - p = skb->data; - l = skb->len; - while (l) { - /* - * This is technically bounded by L1OIP_MAX_PERFRAME but - * MAX_DFRAME_LEN_L1 < L1OIP_MAX_PERFRAME - */ - ll = (l < MAX_DFRAME_LEN_L1) ? l : MAX_DFRAME_LEN_L1; - l1oip_socket_send(hc, hc->codec, bch->slot, 0, - hc->chan[bch->slot].tx_counter, p, ll); - hc->chan[bch->slot].tx_counter += ll; - p += ll; - l -= ll; - } - skb_trim(skb, 0); - queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb); - return 0; - case PH_ACTIVATE_REQ: - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n" - , __func__, bch->slot, hc->b_num + 1); - hc->chan[bch->slot].codecstate = 0; - test_and_set_bit(FLG_ACTIVE, &bch->Flags); - skb_trim(skb, 0); - queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb); - return 0; - case PH_DEACTIVATE_REQ: - if (debug & (DEBUG_L1OIP_MSG | DEBUG_L1OIP_SOCKET)) - printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d " - "(1..%d)\n", __func__, bch->slot, - hc->b_num + 1); - test_and_clear_bit(FLG_ACTIVE, &bch->Flags); - skb_trim(skb, 0); - queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb); - return 0; - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) -{ - int ret = 0; - struct dsp_features *features = - (struct dsp_features *)(*((u_long *)&cq->p1)); - - switch (cq->op) { - case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_HW_FEATURES_OP; - break; - case MISDN_CTRL_HW_FEATURES: /* fill features structure */ - if (debug & DEBUG_L1OIP_MSG) - printk(KERN_DEBUG "%s: HW_FEATURE request\n", - __func__); - /* create confirm */ - features->unclocked = 1; - features->unordered = 1; - break; - default: - printk(KERN_WARNING "%s: unknown Op %x\n", - __func__, cq->op); - ret = -EINVAL; - break; - } - return ret; -} - -static int -l1oip_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct bchannel *bch = container_of(ch, struct bchannel, ch); - int err = -EINVAL; - - if (bch->debug & DEBUG_HW) - printk(KERN_DEBUG "%s: cmd:%x %p\n", - __func__, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - test_and_clear_bit(FLG_OPEN, &bch->Flags); - test_and_clear_bit(FLG_ACTIVE, &bch->Flags); - ch->protocol = ISDN_P_NONE; - ch->peer = NULL; - module_put(THIS_MODULE); - err = 0; - break; - case CONTROL_CHANNEL: - err = channel_bctrl(bch, arg); - break; - default: - printk(KERN_WARNING "%s: unknown prim(%x)\n", - __func__, cmd); - } - return err; -} - - -/* - * cleanup module and stack - */ -static void -release_card(struct l1oip *hc) -{ - int ch; - - hc->shutdown = true; - - timer_shutdown_sync(&hc->keep_tl); - timer_shutdown_sync(&hc->timeout_tl); - - cancel_work_sync(&hc->workq); - - if (hc->socket_thread) - l1oip_socket_close(hc); - - if (hc->registered && hc->chan[hc->d_idx].dch) - mISDN_unregister_device(&hc->chan[hc->d_idx].dch->dev); - for (ch = 0; ch < 128; ch++) { - if (hc->chan[ch].dch) { - mISDN_freedchannel(hc->chan[ch].dch); - kfree(hc->chan[ch].dch); - } - if (hc->chan[ch].bch) { - mISDN_freebchannel(hc->chan[ch].bch); - kfree(hc->chan[ch].bch); -#ifdef REORDER_DEBUG - dev_kfree_skb(hc->chan[ch].disorder_skb); -#endif - } - } - - spin_lock(&l1oip_lock); - list_del(&hc->list); - spin_unlock(&l1oip_lock); - - kfree(hc); -} - -static void -l1oip_cleanup(void) -{ - struct l1oip *hc, *next; - - list_for_each_entry_safe(hc, next, &l1oip_ilist, list) - release_card(hc); - - l1oip_4bit_free(); -} - - -/* - * module and stack init - */ -static int -init_card(struct l1oip *hc, int pri, int bundle) -{ - struct dchannel *dch; - struct bchannel *bch; - int ret; - int i, ch; - - spin_lock_init(&hc->socket_lock); - hc->idx = l1oip_cnt; - hc->pri = pri; - hc->d_idx = pri ? 16 : 3; - hc->b_num = pri ? 30 : 2; - hc->bundle = bundle; - if (hc->pri) - sprintf(hc->name, "l1oip-e1.%d", l1oip_cnt + 1); - else - sprintf(hc->name, "l1oip-s0.%d", l1oip_cnt + 1); - - switch (codec[l1oip_cnt]) { - case 0: /* as is */ - case 1: /* alaw */ - case 2: /* ulaw */ - case 3: /* 4bit */ - break; - default: - printk(KERN_ERR "Codec(%d) not supported.\n", - codec[l1oip_cnt]); - return -EINVAL; - } - hc->codec = codec[l1oip_cnt]; - if (debug & DEBUG_L1OIP_INIT) - printk(KERN_DEBUG "%s: using codec %d\n", - __func__, hc->codec); - - if (id[l1oip_cnt] == 0) { - printk(KERN_WARNING "Warning: No 'id' value given or " - "0, this is highly unsecure. Please use 32 " - "bit random number 0x...\n"); - } - hc->id = id[l1oip_cnt]; - if (debug & DEBUG_L1OIP_INIT) - printk(KERN_DEBUG "%s: using id 0x%x\n", __func__, hc->id); - - hc->ondemand = ondemand[l1oip_cnt]; - if (hc->ondemand && !hc->id) { - printk(KERN_ERR "%s: ondemand option only allowed in " - "conjunction with non 0 ID\n", __func__); - return -EINVAL; - } - - if (limit[l1oip_cnt]) - hc->b_num = limit[l1oip_cnt]; - if (!pri && hc->b_num > 2) { - printk(KERN_ERR "Maximum limit for BRI interface is 2 " - "channels.\n"); - return -EINVAL; - } - if (pri && hc->b_num > 126) { - printk(KERN_ERR "Maximum limit for PRI interface is 126 " - "channels.\n"); - return -EINVAL; - } - if (pri && hc->b_num > 30) { - printk(KERN_WARNING "Maximum limit for BRI interface is 30 " - "channels.\n"); - printk(KERN_WARNING "Your selection of %d channels must be " - "supported by application.\n", hc->limit); - } - - hc->remoteip = ip[l1oip_cnt << 2] << 24 - | ip[(l1oip_cnt << 2) + 1] << 16 - | ip[(l1oip_cnt << 2) + 2] << 8 - | ip[(l1oip_cnt << 2) + 3]; - hc->localport = port[l1oip_cnt]?:(L1OIP_DEFAULTPORT + l1oip_cnt); - if (remoteport[l1oip_cnt]) - hc->remoteport = remoteport[l1oip_cnt]; - else - hc->remoteport = hc->localport; - if (debug & DEBUG_L1OIP_INIT) - printk(KERN_DEBUG "%s: using local port %d remote ip " - "%d.%d.%d.%d port %d ondemand %d\n", __func__, - hc->localport, hc->remoteip >> 24, - (hc->remoteip >> 16) & 0xff, - (hc->remoteip >> 8) & 0xff, hc->remoteip & 0xff, - hc->remoteport, hc->ondemand); - - dch = kzalloc_obj(struct dchannel); - if (!dch) - return -ENOMEM; - dch->debug = debug; - mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, NULL); - dch->hw = hc; - if (pri) - dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1); - else - dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); - dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | - (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); - dch->dev.D.send = handle_dmsg; - dch->dev.D.ctrl = l1oip_dctrl; - dch->dev.nrbchan = hc->b_num; - dch->slot = hc->d_idx; - hc->chan[hc->d_idx].dch = dch; - i = 1; - for (ch = 0; ch < dch->dev.nrbchan; ch++) { - if (ch == 15) - i++; - bch = kzalloc_obj(struct bchannel); - if (!bch) { - printk(KERN_ERR "%s: no memory for bchannel\n", - __func__); - return -ENOMEM; - } - bch->nr = i + ch; - bch->slot = i + ch; - bch->debug = debug; - mISDN_initbchannel(bch, MAX_DATA_MEM, 0); - bch->hw = hc; - bch->ch.send = handle_bmsg; - bch->ch.ctrl = l1oip_bctrl; - bch->ch.nr = i + ch; - list_add(&bch->ch.list, &dch->dev.bchannels); - hc->chan[i + ch].bch = bch; - set_channelmap(bch->nr, dch->dev.channelmap); - } - /* TODO: create a parent device for this driver */ - ret = mISDN_register_device(&dch->dev, NULL, hc->name); - if (ret) - return ret; - hc->registered = 1; - - if (debug & DEBUG_L1OIP_INIT) - printk(KERN_DEBUG "%s: Setting up network card(%d)\n", - __func__, l1oip_cnt + 1); - ret = l1oip_socket_open(hc); - if (ret) - return ret; - - timer_setup(&hc->keep_tl, l1oip_keepalive, 0); - hc->keep_tl.expires = jiffies + 2 * HZ; /* two seconds first time */ - add_timer(&hc->keep_tl); - - timer_setup(&hc->timeout_tl, l1oip_timeout, 0); - hc->timeout_on = 0; /* state that we have timer off */ - - return 0; -} - -static int __init -l1oip_init(void) -{ - int pri, bundle; - struct l1oip *hc; - int ret; - - printk(KERN_INFO "mISDN: Layer-1-over-IP driver Rev. %s\n", - l1oip_revision); - - if (l1oip_4bit_alloc(ulaw)) - return -ENOMEM; - - l1oip_cnt = 0; - while (l1oip_cnt < MAX_CARDS && type[l1oip_cnt]) { - switch (type[l1oip_cnt] & 0xff) { - case 1: - pri = 0; - bundle = 0; - break; - case 2: - pri = 1; - bundle = 0; - break; - case 3: - pri = 0; - bundle = 1; - break; - case 4: - pri = 1; - bundle = 1; - break; - default: - printk(KERN_ERR "Card type(%d) not supported.\n", - type[l1oip_cnt] & 0xff); - l1oip_cleanup(); - return -EINVAL; - } - - if (debug & DEBUG_L1OIP_INIT) - printk(KERN_DEBUG "%s: interface %d is %s with %s.\n", - __func__, l1oip_cnt, pri ? "PRI" : "BRI", - bundle ? "bundled IP packet for all B-channels" : - "separate IP packets for every B-channel"); - - hc = kzalloc_obj(struct l1oip, GFP_ATOMIC); - if (!hc) { - printk(KERN_ERR "No kmem for L1-over-IP driver.\n"); - l1oip_cleanup(); - return -ENOMEM; - } - INIT_WORK(&hc->workq, (void *)l1oip_send_bh); - - spin_lock(&l1oip_lock); - list_add_tail(&hc->list, &l1oip_ilist); - spin_unlock(&l1oip_lock); - - ret = init_card(hc, pri, bundle); - if (ret) { - l1oip_cleanup(); - return ret; - } - - l1oip_cnt++; - } - printk(KERN_INFO "%d virtual devices registered\n", l1oip_cnt); - return 0; -} - -module_init(l1oip_init); -module_exit(l1oip_cleanup); diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c deleted file mode 100644 index 3fbc170acf9a..000000000000 --- a/drivers/isdn/mISDN/layer1.c +++ /dev/null @@ -1,415 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ - - -#include -#include -#include -#include "core.h" -#include "layer1.h" -#include "fsm.h" - -static u_int *debug; - -struct layer1 { - u_long Flags; - struct FsmInst l1m; - struct FsmTimer timer3; - struct FsmTimer timerX; - int delay; - int t3_value; - struct dchannel *dch; - dchannel_l1callback *dcb; -}; - -#define TIMER3_DEFAULT_VALUE 7000 - -static -struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL}; - -enum { - ST_L1_F2, - ST_L1_F3, - ST_L1_F4, - ST_L1_F5, - ST_L1_F6, - ST_L1_F7, - ST_L1_F8, -}; - -#define L1S_STATE_COUNT (ST_L1_F8 + 1) - -static char *strL1SState[] = -{ - "ST_L1_F2", - "ST_L1_F3", - "ST_L1_F4", - "ST_L1_F5", - "ST_L1_F6", - "ST_L1_F7", - "ST_L1_F8", -}; - -enum { - EV_PH_ACTIVATE, - EV_PH_DEACTIVATE, - EV_RESET_IND, - EV_DEACT_CNF, - EV_DEACT_IND, - EV_POWER_UP, - EV_ANYSIG_IND, - EV_INFO2_IND, - EV_INFO4_IND, - EV_TIMER_DEACT, - EV_TIMER_ACT, - EV_TIMER3, -}; - -#define L1_EVENT_COUNT (EV_TIMER3 + 1) - -static char *strL1Event[] = -{ - "EV_PH_ACTIVATE", - "EV_PH_DEACTIVATE", - "EV_RESET_IND", - "EV_DEACT_CNF", - "EV_DEACT_IND", - "EV_POWER_UP", - "EV_ANYSIG_IND", - "EV_INFO2_IND", - "EV_INFO4_IND", - "EV_TIMER_DEACT", - "EV_TIMER_ACT", - "EV_TIMER3", -}; - -static void -l1m_debug(struct FsmInst *fi, char *fmt, ...) -{ - struct layer1 *l1 = fi->userdata; - struct va_format vaf; - va_list va; - - va_start(va, fmt); - - vaf.fmt = fmt; - vaf.va = &va; - - printk(KERN_DEBUG "%s: %pV\n", dev_name(&l1->dch->dev.dev), &vaf); - - va_end(va); -} - -static void -l1_reset(struct FsmInst *fi, int event, void *arg) -{ - mISDN_FsmChangeState(fi, ST_L1_F3); -} - -static void -l1_deact_cnf(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - mISDN_FsmChangeState(fi, ST_L1_F3); - if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) - l1->dcb(l1->dch, HW_POWERUP_REQ); -} - -static void -l1_deact_req_s(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - mISDN_FsmChangeState(fi, ST_L1_F3); - mISDN_FsmRestartTimer(&l1->timerX, 550, EV_TIMER_DEACT, NULL, 2); - test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags); -} - -static void -l1_power_up_s(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) { - mISDN_FsmChangeState(fi, ST_L1_F4); - l1->dcb(l1->dch, INFO3_P8); - } else - mISDN_FsmChangeState(fi, ST_L1_F3); -} - -static void -l1_go_F5(struct FsmInst *fi, int event, void *arg) -{ - mISDN_FsmChangeState(fi, ST_L1_F5); -} - -static void -l1_go_F8(struct FsmInst *fi, int event, void *arg) -{ - mISDN_FsmChangeState(fi, ST_L1_F8); -} - -static void -l1_info2_ind(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - mISDN_FsmChangeState(fi, ST_L1_F6); - l1->dcb(l1->dch, INFO3_P8); -} - -static void -l1_info4_ind(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - mISDN_FsmChangeState(fi, ST_L1_F7); - l1->dcb(l1->dch, INFO3_P8); - if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags)) - mISDN_FsmDelTimer(&l1->timerX, 4); - if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) { - if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags)) - mISDN_FsmDelTimer(&l1->timer3, 3); - mISDN_FsmRestartTimer(&l1->timerX, 110, EV_TIMER_ACT, NULL, 2); - test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags); - } -} - -static void -l1_timer3(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags); - if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) { - if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) - l1->dcb(l1->dch, HW_D_NOBLOCKED); - l1->dcb(l1->dch, PH_DEACTIVATE_IND); - } - if (l1->l1m.state != ST_L1_F6) { - mISDN_FsmChangeState(fi, ST_L1_F3); - /* do not force anything here, we need send INFO 0 */ - } -} - -static void -l1_timer_act(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags); - test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags); - l1->dcb(l1->dch, PH_ACTIVATE_IND); -} - -static void -l1_timer_deact(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags); - test_and_clear_bit(FLG_L1_ACTIVATED, &l1->Flags); - if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) - l1->dcb(l1->dch, HW_D_NOBLOCKED); - l1->dcb(l1->dch, PH_DEACTIVATE_IND); - l1->dcb(l1->dch, HW_DEACT_REQ); -} - -static void -l1_activate_s(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - mISDN_FsmRestartTimer(&l1->timer3, l1->t3_value, EV_TIMER3, NULL, 2); - test_and_set_bit(FLG_L1_T3RUN, &l1->Flags); - /* Tell HW to send INFO 1 */ - l1->dcb(l1->dch, HW_RESET_REQ); -} - -static void -l1_activate_no(struct FsmInst *fi, int event, void *arg) -{ - struct layer1 *l1 = fi->userdata; - - if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) && - (!test_bit(FLG_L1_T3RUN, &l1->Flags))) { - test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags); - if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) - l1->dcb(l1->dch, HW_D_NOBLOCKED); - l1->dcb(l1->dch, PH_DEACTIVATE_IND); - } -} - -static struct FsmNode L1SFnList[] = -{ - {ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s}, - {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no}, - {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no}, - {ST_L1_F3, EV_RESET_IND, l1_reset}, - {ST_L1_F4, EV_RESET_IND, l1_reset}, - {ST_L1_F5, EV_RESET_IND, l1_reset}, - {ST_L1_F6, EV_RESET_IND, l1_reset}, - {ST_L1_F7, EV_RESET_IND, l1_reset}, - {ST_L1_F8, EV_RESET_IND, l1_reset}, - {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, - {ST_L1_F6, EV_DEACT_IND, l1_deact_req_s}, - {ST_L1_F7, EV_DEACT_IND, l1_deact_req_s}, - {ST_L1_F8, EV_DEACT_IND, l1_deact_req_s}, - {ST_L1_F3, EV_POWER_UP, l1_power_up_s}, - {ST_L1_F4, EV_ANYSIG_IND, l1_go_F5}, - {ST_L1_F6, EV_ANYSIG_IND, l1_go_F8}, - {ST_L1_F7, EV_ANYSIG_IND, l1_go_F8}, - {ST_L1_F3, EV_INFO2_IND, l1_info2_ind}, - {ST_L1_F4, EV_INFO2_IND, l1_info2_ind}, - {ST_L1_F5, EV_INFO2_IND, l1_info2_ind}, - {ST_L1_F7, EV_INFO2_IND, l1_info2_ind}, - {ST_L1_F8, EV_INFO2_IND, l1_info2_ind}, - {ST_L1_F3, EV_INFO4_IND, l1_info4_ind}, - {ST_L1_F4, EV_INFO4_IND, l1_info4_ind}, - {ST_L1_F5, EV_INFO4_IND, l1_info4_ind}, - {ST_L1_F6, EV_INFO4_IND, l1_info4_ind}, - {ST_L1_F8, EV_INFO4_IND, l1_info4_ind}, - {ST_L1_F3, EV_TIMER3, l1_timer3}, - {ST_L1_F4, EV_TIMER3, l1_timer3}, - {ST_L1_F5, EV_TIMER3, l1_timer3}, - {ST_L1_F6, EV_TIMER3, l1_timer3}, - {ST_L1_F8, EV_TIMER3, l1_timer3}, - {ST_L1_F7, EV_TIMER_ACT, l1_timer_act}, - {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact}, - {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact}, - {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact}, - {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact}, - {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact}, - {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, -}; - -static void -release_l1(struct layer1 *l1) { - mISDN_FsmDelTimer(&l1->timerX, 0); - mISDN_FsmDelTimer(&l1->timer3, 0); - if (l1->dch) - l1->dch->l1 = NULL; - module_put(THIS_MODULE); - kfree(l1); -} - -int -l1_event(struct layer1 *l1, u_int event) -{ - int err = 0; - - if (!l1) - return -EINVAL; - switch (event) { - case HW_RESET_IND: - mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL); - break; - case HW_DEACT_IND: - mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL); - break; - case HW_POWERUP_IND: - mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL); - break; - case HW_DEACT_CNF: - mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL); - break; - case ANYSIGNAL: - mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); - break; - case LOSTFRAMING: - mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); - break; - case INFO2: - mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL); - break; - case INFO4_P8: - mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); - break; - case INFO4_P10: - mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); - break; - case PH_ACTIVATE_REQ: - if (test_bit(FLG_L1_ACTIVATED, &l1->Flags)) - l1->dcb(l1->dch, PH_ACTIVATE_IND); - else { - test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags); - mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL); - } - break; - case CLOSE_CHANNEL: - release_l1(l1); - break; - default: - if ((event & ~HW_TIMER3_VMASK) == HW_TIMER3_VALUE) { - int val = event & HW_TIMER3_VMASK; - - if (val < 5) - val = 5; - if (val > 30) - val = 30; - l1->t3_value = val; - break; - } - if (*debug & DEBUG_L1) - printk(KERN_DEBUG "%s %x unhandled\n", - __func__, event); - err = -EINVAL; - } - return err; -} -EXPORT_SYMBOL(l1_event); - -int -create_l1(struct dchannel *dch, dchannel_l1callback *dcb) { - struct layer1 *nl1; - - nl1 = kzalloc_obj(struct layer1, GFP_ATOMIC); - if (!nl1) { - printk(KERN_ERR "kmalloc struct layer1 failed\n"); - return -ENOMEM; - } - nl1->l1m.fsm = &l1fsm_s; - nl1->l1m.state = ST_L1_F3; - nl1->Flags = 0; - nl1->t3_value = TIMER3_DEFAULT_VALUE; - nl1->l1m.debug = *debug & DEBUG_L1_FSM; - nl1->l1m.userdata = nl1; - nl1->l1m.userint = 0; - nl1->l1m.printdebug = l1m_debug; - nl1->dch = dch; - nl1->dcb = dcb; - mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer3); - mISDN_FsmInitTimer(&nl1->l1m, &nl1->timerX); - __module_get(THIS_MODULE); - dch->l1 = nl1; - return 0; -} -EXPORT_SYMBOL(create_l1); - -int -Isdnl1_Init(u_int *deb) -{ - debug = deb; - l1fsm_s.state_count = L1S_STATE_COUNT; - l1fsm_s.event_count = L1_EVENT_COUNT; - l1fsm_s.strEvent = strL1Event; - l1fsm_s.strState = strL1SState; - return mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList)); -} - -void -Isdnl1_cleanup(void) -{ - mISDN_FsmFree(&l1fsm_s); -} diff --git a/drivers/isdn/mISDN/layer1.h b/drivers/isdn/mISDN/layer1.h deleted file mode 100644 index f03e86450daf..000000000000 --- a/drivers/isdn/mISDN/layer1.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * - * Layer 1 defines - * - * Copyright 2008 by Karsten Keil - */ - -#define FLG_L1_ACTIVATING 1 -#define FLG_L1_ACTIVATED 2 -#define FLG_L1_DEACTTIMER 3 -#define FLG_L1_ACTTIMER 4 -#define FLG_L1_T3RUN 5 -#define FLG_L1_PULL_REQ 6 -#define FLG_L1_UINT 7 -#define FLG_L1_DBLOCKED 8 diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c deleted file mode 100644 index b75869c9f78f..000000000000 --- a/drivers/isdn/mISDN/layer2.c +++ /dev/null @@ -1,2266 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include "core.h" -#include "fsm.h" -#include "layer2.h" - -static u_int *debug; - -static -struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL}; - -static char *strL2State[] = -{ - "ST_L2_1", - "ST_L2_2", - "ST_L2_3", - "ST_L2_4", - "ST_L2_5", - "ST_L2_6", - "ST_L2_7", - "ST_L2_8", -}; - -enum { - EV_L2_UI, - EV_L2_SABME, - EV_L2_DISC, - EV_L2_DM, - EV_L2_UA, - EV_L2_FRMR, - EV_L2_SUPER, - EV_L2_I, - EV_L2_DL_DATA, - EV_L2_ACK_PULL, - EV_L2_DL_UNITDATA, - EV_L2_DL_ESTABLISH_REQ, - EV_L2_DL_RELEASE_REQ, - EV_L2_MDL_ASSIGN, - EV_L2_MDL_REMOVE, - EV_L2_MDL_ERROR, - EV_L1_DEACTIVATE, - EV_L2_T200, - EV_L2_T203, - EV_L2_T200I, - EV_L2_T203I, - EV_L2_SET_OWN_BUSY, - EV_L2_CLEAR_OWN_BUSY, - EV_L2_FRAME_ERROR, -}; - -#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR + 1) - -static char *strL2Event[] = -{ - "EV_L2_UI", - "EV_L2_SABME", - "EV_L2_DISC", - "EV_L2_DM", - "EV_L2_UA", - "EV_L2_FRMR", - "EV_L2_SUPER", - "EV_L2_I", - "EV_L2_DL_DATA", - "EV_L2_ACK_PULL", - "EV_L2_DL_UNITDATA", - "EV_L2_DL_ESTABLISH_REQ", - "EV_L2_DL_RELEASE_REQ", - "EV_L2_MDL_ASSIGN", - "EV_L2_MDL_REMOVE", - "EV_L2_MDL_ERROR", - "EV_L1_DEACTIVATE", - "EV_L2_T200", - "EV_L2_T203", - "EV_L2_T200I", - "EV_L2_T203I", - "EV_L2_SET_OWN_BUSY", - "EV_L2_CLEAR_OWN_BUSY", - "EV_L2_FRAME_ERROR", -}; - -static void -l2m_debug(struct FsmInst *fi, char *fmt, ...) -{ - struct layer2 *l2 = fi->userdata; - struct va_format vaf; - va_list va; - - if (!(*debug & DEBUG_L2_FSM)) - return; - - va_start(va, fmt); - - vaf.fmt = fmt; - vaf.va = &va; - - printk(KERN_DEBUG "%s l2 (sapi %d tei %d): %pV\n", - mISDNDevName4ch(&l2->ch), l2->sapi, l2->tei, &vaf); - - va_end(va); -} - -inline u_int -l2headersize(struct layer2 *l2, int ui) -{ - return ((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) + - (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1); -} - -inline u_int -l2addrsize(struct layer2 *l2) -{ - return test_bit(FLG_LAPD, &l2->flag) ? 2 : 1; -} - -static u_int -l2_newid(struct layer2 *l2) -{ - u_int id; - - id = l2->next_id++; - if (id == 0x7fff) - l2->next_id = 1; - id <<= 16; - id |= l2->tei << 8; - id |= l2->sapi; - return id; -} - -static void -l2up(struct layer2 *l2, u_int prim, struct sk_buff *skb) -{ - int err; - - if (!l2->up) - return; - mISDN_HEAD_PRIM(skb) = prim; - mISDN_HEAD_ID(skb) = (l2->ch.nr << 16) | l2->ch.addr; - err = l2->up->send(l2->up, skb); - if (err) { - printk(KERN_WARNING "%s: dev %s err=%d\n", __func__, - mISDNDevName4ch(&l2->ch), err); - dev_kfree_skb(skb); - } -} - -static void -l2up_create(struct layer2 *l2, u_int prim, int len, void *arg) -{ - struct sk_buff *skb; - struct mISDNhead *hh; - int err; - - if (!l2->up) - return; - skb = mI_alloc_skb(len, GFP_ATOMIC); - if (!skb) - return; - hh = mISDN_HEAD_P(skb); - hh->prim = prim; - hh->id = (l2->ch.nr << 16) | l2->ch.addr; - if (len) - skb_put_data(skb, arg, len); - err = l2->up->send(l2->up, skb); - if (err) { - printk(KERN_WARNING "%s: dev %s err=%d\n", __func__, - mISDNDevName4ch(&l2->ch), err); - dev_kfree_skb(skb); - } -} - -static int -l2down_skb(struct layer2 *l2, struct sk_buff *skb) { - int ret; - - ret = l2->ch.recv(l2->ch.peer, skb); - if (ret && (*debug & DEBUG_L2_RECV)) - printk(KERN_DEBUG "l2down_skb: dev %s ret(%d)\n", - mISDNDevName4ch(&l2->ch), ret); - return ret; -} - -static int -l2down_raw(struct layer2 *l2, struct sk_buff *skb) -{ - struct mISDNhead *hh = mISDN_HEAD_P(skb); - - if (hh->prim == PH_DATA_REQ) { - if (test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) { - skb_queue_tail(&l2->down_queue, skb); - return 0; - } - l2->down_id = mISDN_HEAD_ID(skb); - } - return l2down_skb(l2, skb); -} - -static int -l2down(struct layer2 *l2, u_int prim, u_int id, struct sk_buff *skb) -{ - struct mISDNhead *hh = mISDN_HEAD_P(skb); - - hh->prim = prim; - hh->id = id; - return l2down_raw(l2, skb); -} - -static int -l2down_create(struct layer2 *l2, u_int prim, u_int id, int len, void *arg) -{ - struct sk_buff *skb; - int err; - struct mISDNhead *hh; - - skb = mI_alloc_skb(len, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - hh = mISDN_HEAD_P(skb); - hh->prim = prim; - hh->id = id; - if (len) - skb_put_data(skb, arg, len); - err = l2down_raw(l2, skb); - if (err) - dev_kfree_skb(skb); - return err; -} - -static int -ph_data_confirm(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) { - struct sk_buff *nskb = skb; - int ret = -EAGAIN; - - if (test_bit(FLG_L1_NOTREADY, &l2->flag)) { - if (hh->id == l2->down_id) { - nskb = skb_dequeue(&l2->down_queue); - if (nskb) { - l2->down_id = mISDN_HEAD_ID(nskb); - if (l2down_skb(l2, nskb)) { - dev_kfree_skb(nskb); - l2->down_id = MISDN_ID_NONE; - } - } else - l2->down_id = MISDN_ID_NONE; - if (ret) { - dev_kfree_skb(skb); - ret = 0; - } - if (l2->down_id == MISDN_ID_NONE) { - test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); - mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); - } - } - } - if (!test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) { - nskb = skb_dequeue(&l2->down_queue); - if (nskb) { - l2->down_id = mISDN_HEAD_ID(nskb); - if (l2down_skb(l2, nskb)) { - dev_kfree_skb(nskb); - l2->down_id = MISDN_ID_NONE; - test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); - } - } else - test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); - } - return ret; -} - -static void -l2_timeout(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb; - struct mISDNhead *hh; - - skb = mI_alloc_skb(0, GFP_ATOMIC); - if (!skb) { - printk(KERN_WARNING "%s: L2(%d,%d) nr:%x timer %s no skb\n", - mISDNDevName4ch(&l2->ch), l2->sapi, l2->tei, - l2->ch.nr, event == EV_L2_T200 ? "T200" : "T203"); - return; - } - hh = mISDN_HEAD_P(skb); - hh->prim = event == EV_L2_T200 ? DL_TIMER200_IND : DL_TIMER203_IND; - hh->id = l2->ch.nr; - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s: L2(%d,%d) nr:%x timer %s expired\n", - mISDNDevName4ch(&l2->ch), l2->sapi, l2->tei, - l2->ch.nr, event == EV_L2_T200 ? "T200" : "T203"); - if (l2->ch.st) - l2->ch.st->own.recv(&l2->ch.st->own, skb); -} - -static int -l2mgr(struct layer2 *l2, u_int prim, void *arg) { - long c = (long)arg; - - printk(KERN_WARNING "l2mgr: dev %s addr:%x prim %x %c\n", - mISDNDevName4ch(&l2->ch), l2->id, prim, (char)c); - if (test_bit(FLG_LAPD, &l2->flag) && - !test_bit(FLG_FIXED_TEI, &l2->flag)) { - switch (c) { - case 'C': - case 'D': - case 'G': - case 'H': - l2_tei(l2, prim, (u_long)arg); - break; - } - } - return 0; -} - -static void -set_peer_busy(struct layer2 *l2) { - test_and_set_bit(FLG_PEER_BUSY, &l2->flag); - if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue)) - test_and_set_bit(FLG_L2BLOCK, &l2->flag); -} - -static void -clear_peer_busy(struct layer2 *l2) { - if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag)) - test_and_clear_bit(FLG_L2BLOCK, &l2->flag); -} - -static void -InitWin(struct layer2 *l2) -{ - int i; - - for (i = 0; i < MAX_WINDOW; i++) - l2->windowar[i] = NULL; -} - -static int -freewin(struct layer2 *l2) -{ - int i, cnt = 0; - - for (i = 0; i < MAX_WINDOW; i++) { - if (l2->windowar[i]) { - cnt++; - dev_kfree_skb(l2->windowar[i]); - l2->windowar[i] = NULL; - } - } - return cnt; -} - -static void -ReleaseWin(struct layer2 *l2) -{ - int cnt = freewin(l2); - - if (cnt) - printk(KERN_WARNING - "isdnl2 freed %d skbuffs in release\n", cnt); -} - -inline unsigned int -cansend(struct layer2 *l2) -{ - unsigned int p1; - - if (test_bit(FLG_MOD128, &l2->flag)) - p1 = (l2->vs - l2->va) % 128; - else - p1 = (l2->vs - l2->va) % 8; - return (p1 < l2->window) && !test_bit(FLG_PEER_BUSY, &l2->flag); -} - -inline void -clear_exception(struct layer2 *l2) -{ - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); - test_and_clear_bit(FLG_REJEXC, &l2->flag); - test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); - clear_peer_busy(l2); -} - -static int -sethdraddr(struct layer2 *l2, u_char *header, int rsp) -{ - u_char *ptr = header; - int crbit = rsp; - - if (test_bit(FLG_LAPD, &l2->flag)) { - if (test_bit(FLG_LAPD_NET, &l2->flag)) - crbit = !crbit; - *ptr++ = (l2->sapi << 2) | (crbit ? 2 : 0); - *ptr++ = (l2->tei << 1) | 1; - return 2; - } else { - if (test_bit(FLG_ORIG, &l2->flag)) - crbit = !crbit; - if (crbit) - *ptr++ = l2->addr.B; - else - *ptr++ = l2->addr.A; - return 1; - } -} - -static inline void -enqueue_super(struct layer2 *l2, struct sk_buff *skb) -{ - if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb)) - dev_kfree_skb(skb); -} - -static inline void -enqueue_ui(struct layer2 *l2, struct sk_buff *skb) -{ - if (l2->tm) - l2_tei(l2, MDL_STATUS_UI_IND, 0); - if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb)) - dev_kfree_skb(skb); -} - -inline int -IsUI(u_char *data) -{ - return (data[0] & 0xef) == UI; -} - -inline int -IsUA(u_char *data) -{ - return (data[0] & 0xef) == UA; -} - -inline int -IsDM(u_char *data) -{ - return (data[0] & 0xef) == DM; -} - -inline int -IsDISC(u_char *data) -{ - return (data[0] & 0xef) == DISC; -} - -inline int -IsRR(u_char *data, struct layer2 *l2) -{ - if (test_bit(FLG_MOD128, &l2->flag)) - return data[0] == RR; - else - return (data[0] & 0xf) == 1; -} - -inline int -IsSFrame(u_char *data, struct layer2 *l2) -{ - register u_char d = *data; - - if (!test_bit(FLG_MOD128, &l2->flag)) - d &= 0xf; - return ((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c); -} - -inline int -IsSABME(u_char *data, struct layer2 *l2) -{ - u_char d = data[0] & ~0x10; - - return test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM; -} - -inline int -IsREJ(u_char *data, struct layer2 *l2) -{ - return test_bit(FLG_MOD128, &l2->flag) ? - data[0] == REJ : (data[0] & 0xf) == REJ; -} - -inline int -IsFRMR(u_char *data) -{ - return (data[0] & 0xef) == FRMR; -} - -inline int -IsRNR(u_char *data, struct layer2 *l2) -{ - return test_bit(FLG_MOD128, &l2->flag) ? - data[0] == RNR : (data[0] & 0xf) == RNR; -} - -static int -iframe_error(struct layer2 *l2, struct sk_buff *skb) -{ - u_int i; - int rsp = *skb->data & 0x2; - - i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1); - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; - if (rsp) - return 'L'; - if (skb->len < i) - return 'N'; - if ((skb->len - i) > l2->maxlen) - return 'O'; - return 0; -} - -static int -super_error(struct layer2 *l2, struct sk_buff *skb) -{ - if (skb->len != l2addrsize(l2) + - (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1)) - return 'N'; - return 0; -} - -static int -unnum_error(struct layer2 *l2, struct sk_buff *skb, int wantrsp) -{ - int rsp = (*skb->data & 0x2) >> 1; - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; - if (rsp != wantrsp) - return 'L'; - if (skb->len != l2addrsize(l2) + 1) - return 'N'; - return 0; -} - -static int -UI_error(struct layer2 *l2, struct sk_buff *skb) -{ - int rsp = *skb->data & 0x2; - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; - if (rsp) - return 'L'; - if (skb->len > l2->maxlen + l2addrsize(l2) + 1) - return 'O'; - return 0; -} - -static int -FRMR_error(struct layer2 *l2, struct sk_buff *skb) -{ - u_int headers = l2addrsize(l2) + 1; - u_char *datap = skb->data + headers; - int rsp = *skb->data & 0x2; - - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; - if (!rsp) - return 'L'; - if (test_bit(FLG_MOD128, &l2->flag)) { - if (skb->len < headers + 5) - return 'N'; - else if (*debug & DEBUG_L2) - l2m_debug(&l2->l2m, - "FRMR information %2x %2x %2x %2x %2x", - datap[0], datap[1], datap[2], datap[3], datap[4]); - } else { - if (skb->len < headers + 3) - return 'N'; - else if (*debug & DEBUG_L2) - l2m_debug(&l2->l2m, - "FRMR information %2x %2x %2x", - datap[0], datap[1], datap[2]); - } - return 0; -} - -static unsigned int -legalnr(struct layer2 *l2, unsigned int nr) -{ - if (test_bit(FLG_MOD128, &l2->flag)) - return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128); - else - return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8); -} - -static void -setva(struct layer2 *l2, unsigned int nr) -{ - struct sk_buff *skb; - - while (l2->va != nr) { - l2->va++; - if (test_bit(FLG_MOD128, &l2->flag)) - l2->va %= 128; - else - l2->va %= 8; - if (l2->windowar[l2->sow]) { - skb_trim(l2->windowar[l2->sow], 0); - skb_queue_tail(&l2->tmp_queue, l2->windowar[l2->sow]); - l2->windowar[l2->sow] = NULL; - } - l2->sow = (l2->sow + 1) % l2->window; - } - skb = skb_dequeue(&l2->tmp_queue); - while (skb) { - dev_kfree_skb(skb); - skb = skb_dequeue(&l2->tmp_queue); - } -} - -static void -send_uframe(struct layer2 *l2, struct sk_buff *skb, u_char cmd, u_char cr) -{ - u_char tmp[MAX_L2HEADER_LEN]; - int i; - - i = sethdraddr(l2, tmp, cr); - tmp[i++] = cmd; - if (skb) - skb_trim(skb, 0); - else { - skb = mI_alloc_skb(i, GFP_ATOMIC); - if (!skb) { - printk(KERN_WARNING "%s: can't alloc skbuff in %s\n", - mISDNDevName4ch(&l2->ch), __func__); - return; - } - } - skb_put_data(skb, tmp, i); - enqueue_super(l2, skb); -} - - -inline u_char -get_PollFlag(struct layer2 *l2, struct sk_buff *skb) -{ - return skb->data[l2addrsize(l2)] & 0x10; -} - -inline u_char -get_PollFlagFree(struct layer2 *l2, struct sk_buff *skb) -{ - u_char PF; - - PF = get_PollFlag(l2, skb); - dev_kfree_skb(skb); - return PF; -} - -inline void -start_t200(struct layer2 *l2, int i) -{ - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); - test_and_set_bit(FLG_T200_RUN, &l2->flag); -} - -inline void -restart_t200(struct layer2 *l2, int i) -{ - mISDN_FsmRestartTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); - test_and_set_bit(FLG_T200_RUN, &l2->flag); -} - -inline void -stop_t200(struct layer2 *l2, int i) -{ - if (test_and_clear_bit(FLG_T200_RUN, &l2->flag)) - mISDN_FsmDelTimer(&l2->t200, i); -} - -inline void -st5_dl_release_l2l3(struct layer2 *l2) -{ - int pr; - - if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) - pr = DL_RELEASE_CNF; - else - pr = DL_RELEASE_IND; - l2up_create(l2, pr, 0, NULL); -} - -inline void -lapb_dl_release_l2l3(struct layer2 *l2, int f) -{ - if (test_bit(FLG_LAPB, &l2->flag)) - l2down_create(l2, PH_DEACTIVATE_REQ, l2_newid(l2), 0, NULL); - l2up_create(l2, f, 0, NULL); -} - -static void -establishlink(struct FsmInst *fi) -{ - struct layer2 *l2 = fi->userdata; - u_char cmd; - - clear_exception(l2); - l2->rc = 0; - cmd = (test_bit(FLG_MOD128, &l2->flag) ? SABME : SABM) | 0x10; - send_uframe(l2, NULL, cmd, CMD); - mISDN_FsmDelTimer(&l2->t203, 1); - restart_t200(l2, 1); - test_and_clear_bit(FLG_PEND_REL, &l2->flag); - freewin(l2); - mISDN_FsmChangeState(fi, ST_L2_5); -} - -static void -l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - if (get_PollFlagFree(l2, skb)) - l2mgr(l2, MDL_ERROR_IND, (void *) 'C'); - else - l2mgr(l2, MDL_ERROR_IND, (void *) 'D'); - -} - -static void -l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - if (get_PollFlagFree(l2, skb)) - l2mgr(l2, MDL_ERROR_IND, (void *) 'B'); - else { - l2mgr(l2, MDL_ERROR_IND, (void *) 'E'); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &l2->flag); - } -} - -static void -l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - if (get_PollFlagFree(l2, skb)) - l2mgr(l2, MDL_ERROR_IND, (void *) 'B'); - else - l2mgr(l2, MDL_ERROR_IND, (void *) 'E'); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &l2->flag); -} - -static void -l2_go_st3(struct FsmInst *fi, int event, void *arg) -{ - dev_kfree_skb((struct sk_buff *)arg); - mISDN_FsmChangeState(fi, ST_L2_3); -} - -static void -l2_mdl_assign(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - mISDN_FsmChangeState(fi, ST_L2_3); - dev_kfree_skb((struct sk_buff *)arg); - l2_tei(l2, MDL_ASSIGN_IND, 0); -} - -static void -l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_tail(&l2->ui_queue, skb); - mISDN_FsmChangeState(fi, ST_L2_2); - l2_tei(l2, MDL_ASSIGN_IND, 0); -} - -static void -l2_queue_ui(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_tail(&l2->ui_queue, skb); -} - -static void -tx_ui(struct layer2 *l2) -{ - struct sk_buff *skb; - u_char header[MAX_L2HEADER_LEN]; - int i; - - i = sethdraddr(l2, header, CMD); - if (test_bit(FLG_LAPD_NET, &l2->flag)) - header[1] = 0xff; /* tei 127 */ - header[i++] = UI; - while ((skb = skb_dequeue(&l2->ui_queue))) { - memcpy(skb_push(skb, i), header, i); - enqueue_ui(l2, skb); - } -} - -static void -l2_send_ui(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_tail(&l2->ui_queue, skb); - tx_ui(l2); -} - -static void -l2_got_ui(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_pull(skb, l2headersize(l2, 1)); -/* - * in states 1-3 for broadcast - */ - - if (l2->tm) - l2_tei(l2, MDL_STATUS_UI_IND, 0); - l2up(l2, DL_UNITDATA_IND, skb); -} - -static void -l2_establish(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - establishlink(fi); - test_and_set_bit(FLG_L3_INIT, &l2->flag); - dev_kfree_skb(skb); -} - -static void -l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->i_queue); - test_and_set_bit(FLG_L3_INIT, &l2->flag); - test_and_clear_bit(FLG_PEND_REL, &l2->flag); - dev_kfree_skb(skb); -} - -static void -l2_l3_reestablish(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->i_queue); - establishlink(fi); - test_and_set_bit(FLG_L3_INIT, &l2->flag); - dev_kfree_skb(skb); -} - -static void -l2_release(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_trim(skb, 0); - l2up(l2, DL_RELEASE_CNF, skb); -} - -static void -l2_pend_rel(struct FsmInst *fi, int event, void *arg) -{ - struct sk_buff *skb = arg; - struct layer2 *l2 = fi->userdata; - - test_and_set_bit(FLG_PEND_REL, &l2->flag); - dev_kfree_skb(skb); -} - -static void -l2_disconnect(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_purge(&l2->i_queue); - freewin(l2); - mISDN_FsmChangeState(fi, ST_L2_6); - l2->rc = 0; - send_uframe(l2, NULL, DISC | 0x10, CMD); - mISDN_FsmDelTimer(&l2->t203, 1); - restart_t200(l2, 2); - dev_kfree_skb(skb); -} - -static void -l2_start_multi(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - l2->vs = 0; - l2->va = 0; - l2->vr = 0; - l2->sow = 0; - clear_exception(l2); - send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP); - mISDN_FsmChangeState(fi, ST_L2_7); - mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); - skb_trim(skb, 0); - l2up(l2, DL_ESTABLISH_IND, skb); - if (l2->tm) - l2_tei(l2, MDL_STATUS_UP_IND, 0); -} - -static void -l2_send_UA(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); -} - -static void -l2_send_DM(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - send_uframe(l2, skb, DM | get_PollFlag(l2, skb), RSP); -} - -static void -l2_restart_multi(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - int est = 0; - - send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); - - l2mgr(l2, MDL_ERROR_IND, (void *) 'F'); - - if (l2->vs != l2->va) { - skb_queue_purge(&l2->i_queue); - est = 1; - } - - clear_exception(l2); - l2->vs = 0; - l2->va = 0; - l2->vr = 0; - l2->sow = 0; - mISDN_FsmChangeState(fi, ST_L2_7); - stop_t200(l2, 3); - mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); - - if (est) - l2up_create(l2, DL_ESTABLISH_IND, 0, NULL); -/* mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST, - * MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_ESTABLISHED, - * 0, NULL, 0); - */ - if (skb_queue_len(&l2->i_queue) && cansend(l2)) - mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); -} - -static void -l2_stop_multi(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - mISDN_FsmChangeState(fi, ST_L2_4); - mISDN_FsmDelTimer(&l2->t203, 3); - stop_t200(l2, 4); - - send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); - skb_queue_purge(&l2->i_queue); - freewin(l2); - lapb_dl_release_l2l3(l2, DL_RELEASE_IND); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); -} - -static void -l2_connected(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - int pr = -1; - - if (!get_PollFlag(l2, skb)) { - l2_mdl_error_ua(fi, event, arg); - return; - } - dev_kfree_skb(skb); - if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) - l2_disconnect(fi, event, NULL); - if (test_and_clear_bit(FLG_L3_INIT, &l2->flag)) { - pr = DL_ESTABLISH_CNF; - } else if (l2->vs != l2->va) { - skb_queue_purge(&l2->i_queue); - pr = DL_ESTABLISH_IND; - } - stop_t200(l2, 5); - l2->vr = 0; - l2->vs = 0; - l2->va = 0; - l2->sow = 0; - mISDN_FsmChangeState(fi, ST_L2_7); - mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 4); - if (pr != -1) - l2up_create(l2, pr, 0, NULL); - - if (skb_queue_len(&l2->i_queue) && cansend(l2)) - mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); - - if (l2->tm) - l2_tei(l2, MDL_STATUS_UP_IND, 0); -} - -static void -l2_released(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (!get_PollFlag(l2, skb)) { - l2_mdl_error_ua(fi, event, arg); - return; - } - dev_kfree_skb(skb); - stop_t200(l2, 6); - lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); - mISDN_FsmChangeState(fi, ST_L2_4); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); -} - -static void -l2_reestablish(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (!get_PollFlagFree(l2, skb)) { - establishlink(fi); - test_and_set_bit(FLG_L3_INIT, &l2->flag); - } -} - -static void -l2_st5_dm_release(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (get_PollFlagFree(l2, skb)) { - stop_t200(l2, 7); - if (!test_bit(FLG_L3_INIT, &l2->flag)) - skb_queue_purge(&l2->i_queue); - if (test_bit(FLG_LAPB, &l2->flag)) - l2down_create(l2, PH_DEACTIVATE_REQ, - l2_newid(l2), 0, NULL); - st5_dl_release_l2l3(l2); - mISDN_FsmChangeState(fi, ST_L2_4); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); - } -} - -static void -l2_st6_dm_release(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (get_PollFlagFree(l2, skb)) { - stop_t200(l2, 8); - lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); - mISDN_FsmChangeState(fi, ST_L2_4); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); - } -} - -static void -enquiry_cr(struct layer2 *l2, u_char typ, u_char cr, u_char pf) -{ - struct sk_buff *skb; - u_char tmp[MAX_L2HEADER_LEN]; - int i; - - i = sethdraddr(l2, tmp, cr); - if (test_bit(FLG_MOD128, &l2->flag)) { - tmp[i++] = typ; - tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0); - } else - tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0); - skb = mI_alloc_skb(i, GFP_ATOMIC); - if (!skb) { - printk(KERN_WARNING "%s: isdnl2 can't alloc sbbuff in %s\n", - mISDNDevName4ch(&l2->ch), __func__); - return; - } - skb_put_data(skb, tmp, i); - enqueue_super(l2, skb); -} - -inline void -enquiry_response(struct layer2 *l2) -{ - if (test_bit(FLG_OWN_BUSY, &l2->flag)) - enquiry_cr(l2, RNR, RSP, 1); - else - enquiry_cr(l2, RR, RSP, 1); - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); -} - -inline void -transmit_enquiry(struct layer2 *l2) -{ - if (test_bit(FLG_OWN_BUSY, &l2->flag)) - enquiry_cr(l2, RNR, CMD, 1); - else - enquiry_cr(l2, RR, CMD, 1); - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); - start_t200(l2, 9); -} - - -static void -nrerrorrecovery(struct FsmInst *fi) -{ - struct layer2 *l2 = fi->userdata; - - l2mgr(l2, MDL_ERROR_IND, (void *) 'J'); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &l2->flag); -} - -static void -invoke_retransmission(struct layer2 *l2, unsigned int nr) -{ - u_int p1; - - if (l2->vs != nr) { - while (l2->vs != nr) { - (l2->vs)--; - if (test_bit(FLG_MOD128, &l2->flag)) { - l2->vs %= 128; - p1 = (l2->vs - l2->va) % 128; - } else { - l2->vs %= 8; - p1 = (l2->vs - l2->va) % 8; - } - p1 = (p1 + l2->sow) % l2->window; - if (l2->windowar[p1]) - skb_queue_head(&l2->i_queue, l2->windowar[p1]); - else - printk(KERN_WARNING - "%s: windowar[%d] is NULL\n", - mISDNDevName4ch(&l2->ch), p1); - l2->windowar[p1] = NULL; - } - mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); - } -} - -static void -l2_st7_got_super(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - int PollFlag, rsp, typ = RR; - unsigned int nr; - - rsp = *skb->data & 0x2; - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; - - skb_pull(skb, l2addrsize(l2)); - if (IsRNR(skb->data, l2)) { - set_peer_busy(l2); - typ = RNR; - } else - clear_peer_busy(l2); - if (IsREJ(skb->data, l2)) - typ = REJ; - - if (test_bit(FLG_MOD128, &l2->flag)) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - nr = skb->data[1] >> 1; - } else { - PollFlag = (skb->data[0] & 0x10); - nr = (skb->data[0] >> 5) & 0x7; - } - dev_kfree_skb(skb); - - if (PollFlag) { - if (rsp) - l2mgr(l2, MDL_ERROR_IND, (void *) 'A'); - else - enquiry_response(l2); - } - if (legalnr(l2, nr)) { - if (typ == REJ) { - setva(l2, nr); - invoke_retransmission(l2, nr); - stop_t200(l2, 10); - if (mISDN_FsmAddTimer(&l2->t203, l2->T203, - EV_L2_T203, NULL, 6)) - l2m_debug(&l2->l2m, "Restart T203 ST7 REJ"); - } else if ((nr == l2->vs) && (typ == RR)) { - setva(l2, nr); - stop_t200(l2, 11); - mISDN_FsmRestartTimer(&l2->t203, l2->T203, - EV_L2_T203, NULL, 7); - } else if ((l2->va != nr) || (typ == RNR)) { - setva(l2, nr); - if (typ != RR) - mISDN_FsmDelTimer(&l2->t203, 9); - restart_t200(l2, 12); - } - if (skb_queue_len(&l2->i_queue) && (typ == RR)) - mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); - } else - nrerrorrecovery(fi); -} - -static void -l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (!test_bit(FLG_L3_INIT, &l2->flag)) - skb_queue_tail(&l2->i_queue, skb); - else - dev_kfree_skb(skb); -} - -static void -l2_feed_i_pull(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_tail(&l2->i_queue, skb); - mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); -} - -static void -l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_tail(&l2->i_queue, skb); -} - -static void -l2_got_iframe(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - int PollFlag, i; - u_int ns, nr; - - i = l2addrsize(l2); - if (test_bit(FLG_MOD128, &l2->flag)) { - PollFlag = ((skb->data[i + 1] & 0x1) == 0x1); - ns = skb->data[i] >> 1; - nr = (skb->data[i + 1] >> 1) & 0x7f; - } else { - PollFlag = (skb->data[i] & 0x10); - ns = (skb->data[i] >> 1) & 0x7; - nr = (skb->data[i] >> 5) & 0x7; - } - if (test_bit(FLG_OWN_BUSY, &l2->flag)) { - dev_kfree_skb(skb); - if (PollFlag) - enquiry_response(l2); - } else { - if (l2->vr == ns) { - l2->vr++; - if (test_bit(FLG_MOD128, &l2->flag)) - l2->vr %= 128; - else - l2->vr %= 8; - test_and_clear_bit(FLG_REJEXC, &l2->flag); - if (PollFlag) - enquiry_response(l2); - else - test_and_set_bit(FLG_ACK_PEND, &l2->flag); - skb_pull(skb, l2headersize(l2, 0)); - l2up(l2, DL_DATA_IND, skb); - } else { - /* n(s)!=v(r) */ - dev_kfree_skb(skb); - if (test_and_set_bit(FLG_REJEXC, &l2->flag)) { - if (PollFlag) - enquiry_response(l2); - } else { - enquiry_cr(l2, REJ, RSP, PollFlag); - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); - } - } - } - if (legalnr(l2, nr)) { - if (!test_bit(FLG_PEER_BUSY, &l2->flag) && - (fi->state == ST_L2_7)) { - if (nr == l2->vs) { - stop_t200(l2, 13); - mISDN_FsmRestartTimer(&l2->t203, l2->T203, - EV_L2_T203, NULL, 7); - } else if (nr != l2->va) - restart_t200(l2, 14); - } - setva(l2, nr); - } else { - nrerrorrecovery(fi); - return; - } - if (skb_queue_len(&l2->i_queue) && (fi->state == ST_L2_7)) - mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); - if (test_and_clear_bit(FLG_ACK_PEND, &l2->flag)) - enquiry_cr(l2, RR, RSP, 0); -} - -static void -l2_got_tei(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - u_int info; - - l2->tei = (signed char)(long)arg; - set_channel_address(&l2->ch, l2->sapi, l2->tei); - info = DL_INFO_L2_CONNECT; - l2up_create(l2, DL_INFORMATION_IND, sizeof(info), &info); - if (fi->state == ST_L2_3) { - establishlink(fi); - test_and_set_bit(FLG_L3_INIT, &l2->flag); - } else - mISDN_FsmChangeState(fi, ST_L2_4); - if (skb_queue_len(&l2->ui_queue)) - tx_ui(l2); -} - -static void -l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - if (test_bit(FLG_LAPD, &l2->flag) && - test_bit(FLG_DCHAN_BUSY, &l2->flag)) { - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); - } else if (l2->rc == l2->N200) { - mISDN_FsmChangeState(fi, ST_L2_4); - test_and_clear_bit(FLG_T200_RUN, &l2->flag); - skb_queue_purge(&l2->i_queue); - l2mgr(l2, MDL_ERROR_IND, (void *) 'G'); - if (test_bit(FLG_LAPB, &l2->flag)) - l2down_create(l2, PH_DEACTIVATE_REQ, - l2_newid(l2), 0, NULL); - st5_dl_release_l2l3(l2); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); - } else { - l2->rc++; - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); - send_uframe(l2, NULL, (test_bit(FLG_MOD128, &l2->flag) ? - SABME : SABM) | 0x10, CMD); - } -} - -static void -l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - if (test_bit(FLG_LAPD, &l2->flag) && - test_bit(FLG_DCHAN_BUSY, &l2->flag)) { - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); - } else if (l2->rc == l2->N200) { - mISDN_FsmChangeState(fi, ST_L2_4); - test_and_clear_bit(FLG_T200_RUN, &l2->flag); - l2mgr(l2, MDL_ERROR_IND, (void *) 'H'); - lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); - } else { - l2->rc++; - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, - NULL, 9); - send_uframe(l2, NULL, DISC | 0x10, CMD); - } -} - -static void -l2_st7_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - if (test_bit(FLG_LAPD, &l2->flag) && - test_bit(FLG_DCHAN_BUSY, &l2->flag)) { - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); - return; - } - test_and_clear_bit(FLG_T200_RUN, &l2->flag); - l2->rc = 0; - mISDN_FsmChangeState(fi, ST_L2_8); - transmit_enquiry(l2); - l2->rc++; -} - -static void -l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - if (test_bit(FLG_LAPD, &l2->flag) && - test_bit(FLG_DCHAN_BUSY, &l2->flag)) { - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); - return; - } - test_and_clear_bit(FLG_T200_RUN, &l2->flag); - if (l2->rc == l2->N200) { - l2mgr(l2, MDL_ERROR_IND, (void *) 'I'); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &l2->flag); - } else { - transmit_enquiry(l2); - l2->rc++; - } -} - -static void -l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - if (test_bit(FLG_LAPD, &l2->flag) && - test_bit(FLG_DCHAN_BUSY, &l2->flag)) { - mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 9); - return; - } - mISDN_FsmChangeState(fi, ST_L2_8); - transmit_enquiry(l2); - l2->rc = 0; -} - -static void -l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb, *nskb; - u_char header[MAX_L2HEADER_LEN]; - u_int i, p1; - - if (!cansend(l2)) - return; - - skb = skb_dequeue(&l2->i_queue); - if (!skb) - return; - i = sethdraddr(l2, header, CMD); - if (test_bit(FLG_MOD128, &l2->flag)) { - header[i++] = l2->vs << 1; - header[i++] = l2->vr << 1; - } else - header[i++] = (l2->vr << 5) | (l2->vs << 1); - nskb = skb_realloc_headroom(skb, i); - if (!nskb) { - printk(KERN_WARNING "%s: no headroom(%d) copy for IFrame\n", - mISDNDevName4ch(&l2->ch), i); - skb_queue_head(&l2->i_queue, skb); - return; - } - if (test_bit(FLG_MOD128, &l2->flag)) { - p1 = (l2->vs - l2->va) % 128; - l2->vs = (l2->vs + 1) % 128; - } else { - p1 = (l2->vs - l2->va) % 8; - l2->vs = (l2->vs + 1) % 8; - } - p1 = (p1 + l2->sow) % l2->window; - if (l2->windowar[p1]) { - printk(KERN_WARNING "%s: l2 try overwrite ack queue entry %d\n", - mISDNDevName4ch(&l2->ch), p1); - dev_kfree_skb(l2->windowar[p1]); - } - l2->windowar[p1] = skb; - memcpy(skb_push(nskb, i), header, i); - l2down(l2, PH_DATA_REQ, l2_newid(l2), nskb); - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); - if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) { - mISDN_FsmDelTimer(&l2->t203, 13); - mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 11); - } -} - -static void -l2_st8_got_super(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - int PollFlag, rsp, rnr = 0; - unsigned int nr; - - rsp = *skb->data & 0x2; - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; - - skb_pull(skb, l2addrsize(l2)); - - if (IsRNR(skb->data, l2)) { - set_peer_busy(l2); - rnr = 1; - } else - clear_peer_busy(l2); - - if (test_bit(FLG_MOD128, &l2->flag)) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - nr = skb->data[1] >> 1; - } else { - PollFlag = (skb->data[0] & 0x10); - nr = (skb->data[0] >> 5) & 0x7; - } - dev_kfree_skb(skb); - if (rsp && PollFlag) { - if (legalnr(l2, nr)) { - if (rnr) { - restart_t200(l2, 15); - } else { - stop_t200(l2, 16); - mISDN_FsmAddTimer(&l2->t203, l2->T203, - EV_L2_T203, NULL, 5); - setva(l2, nr); - } - invoke_retransmission(l2, nr); - mISDN_FsmChangeState(fi, ST_L2_7); - if (skb_queue_len(&l2->i_queue) && cansend(l2)) - mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); - } else - nrerrorrecovery(fi); - } else { - if (!rsp && PollFlag) - enquiry_response(l2); - if (legalnr(l2, nr)) - setva(l2, nr); - else - nrerrorrecovery(fi); - } -} - -static void -l2_got_FRMR(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_pull(skb, l2addrsize(l2) + 1); - - if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ - (IsUA(skb->data) && (fi->state == ST_L2_7))) { - l2mgr(l2, MDL_ERROR_IND, (void *) 'K'); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &l2->flag); - } - dev_kfree_skb(skb); -} - -static void -l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->ui_queue); - l2->tei = GROUP_TEI; - mISDN_FsmChangeState(fi, ST_L2_1); -} - -static void -l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->ui_queue); - l2->tei = GROUP_TEI; - l2up_create(l2, DL_RELEASE_IND, 0, NULL); - mISDN_FsmChangeState(fi, ST_L2_1); -} - -static void -l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->i_queue); - skb_queue_purge(&l2->ui_queue); - freewin(l2); - l2->tei = GROUP_TEI; - stop_t200(l2, 17); - st5_dl_release_l2l3(l2); - mISDN_FsmChangeState(fi, ST_L2_1); -} - -static void -l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->ui_queue); - l2->tei = GROUP_TEI; - stop_t200(l2, 18); - l2up_create(l2, DL_RELEASE_IND, 0, NULL); - mISDN_FsmChangeState(fi, ST_L2_1); -} - -static void -l2_tei_remove(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - skb_queue_purge(&l2->i_queue); - skb_queue_purge(&l2->ui_queue); - freewin(l2); - l2->tei = GROUP_TEI; - stop_t200(l2, 17); - mISDN_FsmDelTimer(&l2->t203, 19); - l2up_create(l2, DL_RELEASE_IND, 0, NULL); -/* mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST, - * MGR_SHORTSTATUS_IND, SSTATUS_L2_RELEASED, - * 0, NULL, 0); - */ - mISDN_FsmChangeState(fi, ST_L2_1); -} - -static void -l2_st14_persistent_da(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_purge(&l2->i_queue); - skb_queue_purge(&l2->ui_queue); - if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) - l2up(l2, DL_RELEASE_IND, skb); - else - dev_kfree_skb(skb); -} - -static void -l2_st5_persistent_da(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_purge(&l2->i_queue); - skb_queue_purge(&l2->ui_queue); - freewin(l2); - stop_t200(l2, 19); - st5_dl_release_l2l3(l2); - mISDN_FsmChangeState(fi, ST_L2_4); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); - dev_kfree_skb(skb); -} - -static void -l2_st6_persistent_da(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_purge(&l2->ui_queue); - stop_t200(l2, 20); - l2up(l2, DL_RELEASE_CNF, skb); - mISDN_FsmChangeState(fi, ST_L2_4); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); -} - -static void -l2_persistent_da(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - skb_queue_purge(&l2->i_queue); - skb_queue_purge(&l2->ui_queue); - freewin(l2); - stop_t200(l2, 19); - mISDN_FsmDelTimer(&l2->t203, 19); - l2up(l2, DL_RELEASE_IND, skb); - mISDN_FsmChangeState(fi, ST_L2_4); - if (l2->tm) - l2_tei(l2, MDL_STATUS_DOWN_IND, 0); -} - -static void -l2_set_own_busy(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (!test_and_set_bit(FLG_OWN_BUSY, &l2->flag)) { - enquiry_cr(l2, RNR, RSP, 0); - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); - } - dev_kfree_skb(skb); -} - -static void -l2_clear_own_busy(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - struct sk_buff *skb = arg; - - if (!test_and_clear_bit(FLG_OWN_BUSY, &l2->flag)) { - enquiry_cr(l2, RR, RSP, 0); - test_and_clear_bit(FLG_ACK_PEND, &l2->flag); - } - dev_kfree_skb(skb); -} - -static void -l2_frame_error(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - l2mgr(l2, MDL_ERROR_IND, arg); -} - -static void -l2_frame_error_reest(struct FsmInst *fi, int event, void *arg) -{ - struct layer2 *l2 = fi->userdata; - - l2mgr(l2, MDL_ERROR_IND, arg); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &l2->flag); -} - -static struct FsmNode L2FnList[] = -{ - {ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign}, - {ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3}, - {ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish}, - {ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3}, - {ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, - {ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, - {ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release}, - {ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel}, - {ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect}, - {ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect}, - {ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest}, - {ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull}, - {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, - {ST_L2_1, EV_L2_DL_UNITDATA, l2_queue_ui_assign}, - {ST_L2_2, EV_L2_DL_UNITDATA, l2_queue_ui}, - {ST_L2_3, EV_L2_DL_UNITDATA, l2_queue_ui}, - {ST_L2_4, EV_L2_DL_UNITDATA, l2_send_ui}, - {ST_L2_5, EV_L2_DL_UNITDATA, l2_send_ui}, - {ST_L2_6, EV_L2_DL_UNITDATA, l2_send_ui}, - {ST_L2_7, EV_L2_DL_UNITDATA, l2_send_ui}, - {ST_L2_8, EV_L2_DL_UNITDATA, l2_send_ui}, - {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, - {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, - {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, - {ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove}, - {ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove}, - {ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove}, - {ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove}, - {ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove}, - {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_4, EV_L2_SABME, l2_start_multi}, - {ST_L2_5, EV_L2_SABME, l2_send_UA}, - {ST_L2_6, EV_L2_SABME, l2_send_DM}, - {ST_L2_7, EV_L2_SABME, l2_restart_multi}, - {ST_L2_8, EV_L2_SABME, l2_restart_multi}, - {ST_L2_4, EV_L2_DISC, l2_send_DM}, - {ST_L2_5, EV_L2_DISC, l2_send_DM}, - {ST_L2_6, EV_L2_DISC, l2_send_UA}, - {ST_L2_7, EV_L2_DISC, l2_stop_multi}, - {ST_L2_8, EV_L2_DISC, l2_stop_multi}, - {ST_L2_4, EV_L2_UA, l2_mdl_error_ua}, - {ST_L2_5, EV_L2_UA, l2_connected}, - {ST_L2_6, EV_L2_UA, l2_released}, - {ST_L2_7, EV_L2_UA, l2_mdl_error_ua}, - {ST_L2_8, EV_L2_UA, l2_mdl_error_ua}, - {ST_L2_4, EV_L2_DM, l2_reestablish}, - {ST_L2_5, EV_L2_DM, l2_st5_dm_release}, - {ST_L2_6, EV_L2_DM, l2_st6_dm_release}, - {ST_L2_7, EV_L2_DM, l2_mdl_error_dm}, - {ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm}, - {ST_L2_1, EV_L2_UI, l2_got_ui}, - {ST_L2_2, EV_L2_UI, l2_got_ui}, - {ST_L2_3, EV_L2_UI, l2_got_ui}, - {ST_L2_4, EV_L2_UI, l2_got_ui}, - {ST_L2_5, EV_L2_UI, l2_got_ui}, - {ST_L2_6, EV_L2_UI, l2_got_ui}, - {ST_L2_7, EV_L2_UI, l2_got_ui}, - {ST_L2_8, EV_L2_UI, l2_got_ui}, - {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, - {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, - {ST_L2_7, EV_L2_SUPER, l2_st7_got_super}, - {ST_L2_8, EV_L2_SUPER, l2_st8_got_super}, - {ST_L2_7, EV_L2_I, l2_got_iframe}, - {ST_L2_8, EV_L2_I, l2_got_iframe}, - {ST_L2_5, EV_L2_T200, l2_timeout}, - {ST_L2_6, EV_L2_T200, l2_timeout}, - {ST_L2_7, EV_L2_T200, l2_timeout}, - {ST_L2_8, EV_L2_T200, l2_timeout}, - {ST_L2_7, EV_L2_T203, l2_timeout}, - {ST_L2_5, EV_L2_T200I, l2_st5_tout_200}, - {ST_L2_6, EV_L2_T200I, l2_st6_tout_200}, - {ST_L2_7, EV_L2_T200I, l2_st7_tout_200}, - {ST_L2_8, EV_L2_T200I, l2_st8_tout_200}, - {ST_L2_7, EV_L2_T203I, l2_st7_tout_203}, - {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, - {ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, - {ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, - {ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, - {ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, - {ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error}, - {ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error}, - {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error}, - {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest}, - {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest}, - {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistent_da}, - {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove}, - {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove}, - {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistent_da}, - {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistent_da}, - {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistent_da}, - {ST_L2_7, EV_L1_DEACTIVATE, l2_persistent_da}, - {ST_L2_8, EV_L1_DEACTIVATE, l2_persistent_da}, -}; - -static int -ph_data_indication(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) -{ - u_char *datap = skb->data; - int ret = -EINVAL; - int psapi, ptei; - u_int l; - int c = 0; - - l = l2addrsize(l2); - if (skb->len <= l) { - mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N'); - return ret; - } - if (test_bit(FLG_LAPD, &l2->flag)) { /* Maybe not needed */ - psapi = *datap++; - ptei = *datap++; - if ((psapi & 1) || !(ptei & 1)) { - printk(KERN_WARNING - "%s l2 D-channel frame wrong EA0/EA1\n", - mISDNDevName4ch(&l2->ch)); - return ret; - } - psapi >>= 2; - ptei >>= 1; - if (psapi != l2->sapi) { - /* not our business */ - if (*debug & DEBUG_L2) - printk(KERN_DEBUG "%s: sapi %d/%d mismatch\n", - mISDNDevName4ch(&l2->ch), psapi, - l2->sapi); - dev_kfree_skb(skb); - return 0; - } - if ((ptei != l2->tei) && (ptei != GROUP_TEI)) { - /* not our business */ - if (*debug & DEBUG_L2) - printk(KERN_DEBUG "%s: tei %d/%d mismatch\n", - mISDNDevName4ch(&l2->ch), ptei, l2->tei); - dev_kfree_skb(skb); - return 0; - } - } else - datap += l; - if (!(*datap & 1)) { /* I-Frame */ - c = iframe_error(l2, skb); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_I, skb); - } else if (IsSFrame(datap, l2)) { /* S-Frame */ - c = super_error(l2, skb); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SUPER, skb); - } else if (IsUI(datap)) { - c = UI_error(l2, skb); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb); - } else if (IsSABME(datap, l2)) { - c = unnum_error(l2, skb, CMD); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb); - } else if (IsUA(datap)) { - c = unnum_error(l2, skb, RSP); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UA, skb); - } else if (IsDISC(datap)) { - c = unnum_error(l2, skb, CMD); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DISC, skb); - } else if (IsDM(datap)) { - c = unnum_error(l2, skb, RSP); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DM, skb); - } else if (IsFRMR(datap)) { - c = FRMR_error(l2, skb); - if (!c) - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_FRMR, skb); - } else - c = 'L'; - if (c) { - printk(KERN_WARNING "%s:l2 D-channel frame error %c\n", - mISDNDevName4ch(&l2->ch), c); - mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *)(long)c); - } - return ret; -} - -static int -l2_send(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct layer2 *l2 = container_of(ch, struct layer2, ch); - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int ret = -EINVAL; - - if (*debug & DEBUG_L2_RECV) - printk(KERN_DEBUG "%s: %s prim(%x) id(%x) sapi(%d) tei(%d)\n", - __func__, mISDNDevName4ch(&l2->ch), hh->prim, hh->id, - l2->sapi, l2->tei); - if (hh->prim == DL_INTERN_MSG) { - struct mISDNhead *chh = hh + 1; /* saved copy */ - - *hh = *chh; - if (*debug & DEBUG_L2_RECV) - printk(KERN_DEBUG "%s: prim(%x) id(%x) internal msg\n", - mISDNDevName4ch(&l2->ch), hh->prim, hh->id); - } - switch (hh->prim) { - case PH_DATA_IND: - ret = ph_data_indication(l2, hh, skb); - break; - case PH_DATA_CNF: - ret = ph_data_confirm(l2, hh, skb); - break; - case PH_ACTIVATE_IND: - test_and_set_bit(FLG_L1_ACTIV, &l2->flag); - l2up_create(l2, MPH_ACTIVATE_IND, 0, NULL); - if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) - ret = mISDN_FsmEvent(&l2->l2m, - EV_L2_DL_ESTABLISH_REQ, skb); - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(FLG_L1_ACTIV, &l2->flag); - l2up_create(l2, MPH_DEACTIVATE_IND, 0, NULL); - ret = mISDN_FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, skb); - break; - case MPH_INFORMATION_IND: - if (!l2->up) - break; - ret = l2->up->send(l2->up, skb); - break; - case DL_DATA_REQ: - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_DATA, skb); - break; - case DL_UNITDATA_REQ: - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, skb); - break; - case DL_ESTABLISH_REQ: - if (test_bit(FLG_LAPB, &l2->flag)) - test_and_set_bit(FLG_ORIG, &l2->flag); - if (test_bit(FLG_L1_ACTIV, &l2->flag)) { - if (test_bit(FLG_LAPD, &l2->flag) || - test_bit(FLG_ORIG, &l2->flag)) - ret = mISDN_FsmEvent(&l2->l2m, - EV_L2_DL_ESTABLISH_REQ, skb); - } else { - if (test_bit(FLG_LAPD, &l2->flag) || - test_bit(FLG_ORIG, &l2->flag)) { - test_and_set_bit(FLG_ESTAB_PEND, - &l2->flag); - } - ret = l2down(l2, PH_ACTIVATE_REQ, l2_newid(l2), - skb); - } - break; - case DL_RELEASE_REQ: - if (test_bit(FLG_LAPB, &l2->flag)) - l2down_create(l2, PH_DEACTIVATE_REQ, - l2_newid(l2), 0, NULL); - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ, - skb); - break; - case DL_TIMER200_IND: - mISDN_FsmEvent(&l2->l2m, EV_L2_T200I, NULL); - break; - case DL_TIMER203_IND: - mISDN_FsmEvent(&l2->l2m, EV_L2_T203I, NULL); - break; - default: - if (*debug & DEBUG_L2) - l2m_debug(&l2->l2m, "l2 unknown pr %04x", - hh->prim); - } - if (ret) { - dev_kfree_skb(skb); - ret = 0; - } - return ret; -} - -int -tei_l2(struct layer2 *l2, u_int cmd, u_long arg) -{ - int ret = -EINVAL; - - if (*debug & DEBUG_L2_TEI) - printk(KERN_DEBUG "%s: cmd(%x) in %s\n", - mISDNDevName4ch(&l2->ch), cmd, __func__); - switch (cmd) { - case (MDL_ASSIGN_REQ): - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, (void *)arg); - break; - case (MDL_REMOVE_REQ): - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, NULL); - break; - case (MDL_ERROR_IND): - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL); - break; - case (MDL_ERROR_RSP): - /* ETS 300-125 5.3.2.1 Test: TC13010 */ - printk(KERN_NOTICE "%s: MDL_ERROR|REQ (tei_l2)\n", - mISDNDevName4ch(&l2->ch)); - ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL); - break; - } - return ret; -} - -static void -release_l2(struct layer2 *l2) -{ - mISDN_FsmDelTimer(&l2->t200, 21); - mISDN_FsmDelTimer(&l2->t203, 16); - skb_queue_purge(&l2->i_queue); - skb_queue_purge(&l2->ui_queue); - skb_queue_purge(&l2->down_queue); - ReleaseWin(l2); - if (test_bit(FLG_LAPD, &l2->flag)) { - TEIrelease(l2); - if (l2->ch.st) - l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, - CLOSE_CHANNEL, NULL); - } - kfree(l2); -} - -static int -l2_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct layer2 *l2 = container_of(ch, struct layer2, ch); - u_int info; - - if (*debug & DEBUG_L2_CTRL) - printk(KERN_DEBUG "%s: %s cmd(%x)\n", - mISDNDevName4ch(ch), __func__, cmd); - - switch (cmd) { - case OPEN_CHANNEL: - if (test_bit(FLG_LAPD, &l2->flag)) { - set_channel_address(&l2->ch, l2->sapi, l2->tei); - info = DL_INFO_L2_CONNECT; - l2up_create(l2, DL_INFORMATION_IND, - sizeof(info), &info); - } - break; - case CLOSE_CHANNEL: - if (l2->ch.peer) - l2->ch.peer->ctrl(l2->ch.peer, CLOSE_CHANNEL, NULL); - release_l2(l2); - break; - } - return 0; -} - -struct layer2 * -create_l2(struct mISDNchannel *ch, u_int protocol, u_long options, int tei, - int sapi) -{ - struct layer2 *l2; - struct channel_req rq; - - l2 = kzalloc_obj(struct layer2); - if (!l2) { - printk(KERN_ERR "kzalloc layer2 failed\n"); - return NULL; - } - l2->next_id = 1; - l2->down_id = MISDN_ID_NONE; - l2->up = ch; - l2->ch.st = ch->st; - l2->ch.send = l2_send; - l2->ch.ctrl = l2_ctrl; - switch (protocol) { - case ISDN_P_LAPD_NT: - test_and_set_bit(FLG_LAPD, &l2->flag); - test_and_set_bit(FLG_LAPD_NET, &l2->flag); - test_and_set_bit(FLG_MOD128, &l2->flag); - l2->sapi = sapi; - l2->maxlen = MAX_DFRAME_LEN; - if (test_bit(OPTION_L2_PMX, &options)) - l2->window = 7; - else - l2->window = 1; - if (test_bit(OPTION_L2_PTP, &options)) - test_and_set_bit(FLG_PTP, &l2->flag); - if (test_bit(OPTION_L2_FIXEDTEI, &options)) - test_and_set_bit(FLG_FIXED_TEI, &l2->flag); - l2->tei = tei; - l2->T200 = 1000; - l2->N200 = 3; - l2->T203 = 10000; - if (test_bit(OPTION_L2_PMX, &options)) - rq.protocol = ISDN_P_NT_E1; - else - rq.protocol = ISDN_P_NT_S0; - rq.adr.channel = 0; - l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq); - break; - case ISDN_P_LAPD_TE: - test_and_set_bit(FLG_LAPD, &l2->flag); - test_and_set_bit(FLG_MOD128, &l2->flag); - test_and_set_bit(FLG_ORIG, &l2->flag); - l2->sapi = sapi; - l2->maxlen = MAX_DFRAME_LEN; - if (test_bit(OPTION_L2_PMX, &options)) - l2->window = 7; - else - l2->window = 1; - if (test_bit(OPTION_L2_PTP, &options)) - test_and_set_bit(FLG_PTP, &l2->flag); - if (test_bit(OPTION_L2_FIXEDTEI, &options)) - test_and_set_bit(FLG_FIXED_TEI, &l2->flag); - l2->tei = tei; - l2->T200 = 1000; - l2->N200 = 3; - l2->T203 = 10000; - if (test_bit(OPTION_L2_PMX, &options)) - rq.protocol = ISDN_P_TE_E1; - else - rq.protocol = ISDN_P_TE_S0; - rq.adr.channel = 0; - l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq); - break; - case ISDN_P_B_X75SLP: - test_and_set_bit(FLG_LAPB, &l2->flag); - l2->window = 7; - l2->maxlen = MAX_DATA_SIZE; - l2->T200 = 1000; - l2->N200 = 4; - l2->T203 = 5000; - l2->addr.A = 3; - l2->addr.B = 1; - break; - default: - printk(KERN_ERR "layer2 create failed prt %x\n", - protocol); - kfree(l2); - return NULL; - } - skb_queue_head_init(&l2->i_queue); - skb_queue_head_init(&l2->ui_queue); - skb_queue_head_init(&l2->down_queue); - skb_queue_head_init(&l2->tmp_queue); - InitWin(l2); - l2->l2m.fsm = &l2fsm; - if (test_bit(FLG_LAPB, &l2->flag) || - test_bit(FLG_FIXED_TEI, &l2->flag) || - test_bit(FLG_LAPD_NET, &l2->flag)) - l2->l2m.state = ST_L2_4; - else - l2->l2m.state = ST_L2_1; - l2->l2m.debug = *debug; - l2->l2m.userdata = l2; - l2->l2m.userint = 0; - l2->l2m.printdebug = l2m_debug; - - mISDN_FsmInitTimer(&l2->l2m, &l2->t200); - mISDN_FsmInitTimer(&l2->l2m, &l2->t203); - return l2; -} - -static int -x75create(struct channel_req *crq) -{ - struct layer2 *l2; - - if (crq->protocol != ISDN_P_B_X75SLP) - return -EPROTONOSUPPORT; - l2 = create_l2(crq->ch, crq->protocol, 0, 0, 0); - if (!l2) - return -ENOMEM; - crq->ch = &l2->ch; - crq->protocol = ISDN_P_B_HDLC; - return 0; -} - -static struct Bprotocol X75SLP = { - .Bprotocols = (1 << (ISDN_P_B_X75SLP & ISDN_P_B_MASK)), - .name = "X75SLP", - .create = x75create -}; - -int -Isdnl2_Init(u_int *deb) -{ - int res; - debug = deb; - mISDN_register_Bprotocol(&X75SLP); - l2fsm.state_count = L2_STATE_COUNT; - l2fsm.event_count = L2_EVENT_COUNT; - l2fsm.strEvent = strL2Event; - l2fsm.strState = strL2State; - res = mISDN_FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList)); - if (res) - goto error; - res = TEIInit(deb); - if (res) - goto error_fsm; - return 0; - -error_fsm: - mISDN_FsmFree(&l2fsm); -error: - mISDN_unregister_Bprotocol(&X75SLP); - return res; -} - -void -Isdnl2_cleanup(void) -{ - mISDN_unregister_Bprotocol(&X75SLP); - TEIFree(); - mISDN_FsmFree(&l2fsm); -} diff --git a/drivers/isdn/mISDN/layer2.h b/drivers/isdn/mISDN/layer2.h deleted file mode 100644 index c466fd94aa02..000000000000 --- a/drivers/isdn/mISDN/layer2.h +++ /dev/null @@ -1,131 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Layer 2 defines - * - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include "fsm.h" - -#define MAX_WINDOW 8 - -struct manager { - struct mISDNchannel ch; - struct mISDNchannel bcast; - u_long options; - struct list_head layer2; - rwlock_t lock; - struct FsmInst deact; - struct FsmTimer datimer; - struct sk_buff_head sendq; - struct mISDNchannel *up; - u_int nextid; - u_int lastid; -}; - -struct teimgr { - int ri; - int rcnt; - struct FsmInst tei_m; - struct FsmTimer timer; - int tval, nval; - struct layer2 *l2; - struct manager *mgr; -}; - -struct laddr { - u_char A; - u_char B; -}; - -struct layer2 { - struct list_head list; - struct mISDNchannel ch; - u_long flag; - int id; - struct mISDNchannel *up; - signed char sapi; - signed char tei; - struct laddr addr; - u_int maxlen; - struct teimgr *tm; - u_int vs, va, vr; - int rc; - u_int window; - u_int sow; - struct FsmInst l2m; - struct FsmTimer t200, t203; - int T200, N200, T203; - u_int next_id; - u_int down_id; - struct sk_buff *windowar[MAX_WINDOW]; - struct sk_buff_head i_queue; - struct sk_buff_head ui_queue; - struct sk_buff_head down_queue; - struct sk_buff_head tmp_queue; -}; - -enum { - ST_L2_1, - ST_L2_2, - ST_L2_3, - ST_L2_4, - ST_L2_5, - ST_L2_6, - ST_L2_7, - ST_L2_8, -}; - -#define L2_STATE_COUNT (ST_L2_8 + 1) - -extern struct layer2 *create_l2(struct mISDNchannel *, u_int, - u_long, int, int); -extern int tei_l2(struct layer2 *, u_int, u_long arg); - - -/* from tei.c */ -extern int l2_tei(struct layer2 *, u_int, u_long arg); -extern void TEIrelease(struct layer2 *); -extern int TEIInit(u_int *); -extern void TEIFree(void); - -#define MAX_L2HEADER_LEN 4 - -#define RR 0x01 -#define RNR 0x05 -#define REJ 0x09 -#define SABME 0x6f -#define SABM 0x2f -#define DM 0x0f -#define UI 0x03 -#define DISC 0x43 -#define UA 0x63 -#define FRMR 0x87 -#define XID 0xaf - -#define CMD 0 -#define RSP 1 - -#define LC_FLUSH_WAIT 1 - -#define FLG_LAPB 0 -#define FLG_LAPD 1 -#define FLG_ORIG 2 -#define FLG_MOD128 3 -#define FLG_PEND_REL 4 -#define FLG_L3_INIT 5 -#define FLG_T200_RUN 6 -#define FLG_ACK_PEND 7 -#define FLG_REJEXC 8 -#define FLG_OWN_BUSY 9 -#define FLG_PEER_BUSY 10 -#define FLG_DCHAN_BUSY 11 -#define FLG_L1_ACTIV 12 -#define FLG_ESTAB_PEND 13 -#define FLG_PTP 14 -#define FLG_FIXED_TEI 15 -#define FLG_L2BLOCK 16 -#define FLG_L1_NOTREADY 17 -#define FLG_LAPD_NET 18 diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c deleted file mode 100644 index 77b900db1cac..000000000000 --- a/drivers/isdn/mISDN/socket.c +++ /dev/null @@ -1,825 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include -#include "core.h" - -static u_int *debug; - -static struct proto mISDN_proto = { - .name = "misdn", - .owner = THIS_MODULE, - .obj_size = sizeof(struct mISDN_sock) -}; - -#define _pms(sk) ((struct mISDN_sock *)sk) - -static struct mISDN_sock_list data_sockets = { - .lock = __RW_LOCK_UNLOCKED(data_sockets.lock) -}; - -static struct mISDN_sock_list base_sockets = { - .lock = __RW_LOCK_UNLOCKED(base_sockets.lock) -}; - -#define L2_HEADER_LEN 4 - -static inline struct sk_buff * -_l2_alloc_skb(unsigned int len, gfp_t gfp_mask) -{ - struct sk_buff *skb; - - skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask); - if (likely(skb)) - skb_reserve(skb, L2_HEADER_LEN); - return skb; -} - -static void -mISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk) -{ - write_lock_bh(&l->lock); - sk_add_node(sk, &l->head); - write_unlock_bh(&l->lock); -} - -static void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk) -{ - write_lock_bh(&l->lock); - sk_del_node_init(sk); - write_unlock_bh(&l->lock); -} - -static int -mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct mISDN_sock *msk; - int err; - - msk = container_of(ch, struct mISDN_sock, ch); - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb); - if (msk->sk.sk_state == MISDN_CLOSED) - return -EUNATCH; - __net_timestamp(skb); - err = sock_queue_rcv_skb(&msk->sk, skb); - if (err) - printk(KERN_WARNING "%s: error %d\n", __func__, err); - return err; -} - -static int -mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct mISDN_sock *msk; - - msk = container_of(ch, struct mISDN_sock, ch); - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg); - switch (cmd) { - case CLOSE_CHANNEL: - msk->sk.sk_state = MISDN_CLOSED; - break; - } - return 0; -} - -static inline void -mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) -{ - struct __kernel_old_timeval tv; - - if (_pms(sk)->cmask & MISDN_TIME_STAMP) { - skb_get_timestamp(skb, &tv); - put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv); - } -} - -static int -mISDN_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, - int flags) -{ - struct sk_buff *skb; - struct sock *sk = sock->sk; - - int copied, err; - - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n", - __func__, (int)len, flags, _pms(sk)->ch.nr, - sk->sk_protocol); - if (flags & (MSG_OOB)) - return -EOPNOTSUPP; - - if (sk->sk_state == MISDN_CLOSED) - return 0; - - skb = skb_recv_datagram(sk, flags, &err); - if (!skb) - return err; - - if (msg->msg_name) { - DECLARE_SOCKADDR(struct sockaddr_mISDN *, maddr, msg->msg_name); - - maddr->family = AF_ISDN; - maddr->dev = _pms(sk)->dev->id; - if ((sk->sk_protocol == ISDN_P_LAPD_TE) || - (sk->sk_protocol == ISDN_P_LAPD_NT)) { - maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff; - maddr->tei = (mISDN_HEAD_ID(skb) >> 8) & 0xff; - maddr->sapi = mISDN_HEAD_ID(skb) & 0xff; - } else { - maddr->channel = _pms(sk)->ch.nr; - maddr->sapi = _pms(sk)->ch.addr & 0xFF; - maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xFF; - } - msg->msg_namelen = sizeof(*maddr); - } - - copied = skb->len + MISDN_HEADER_LEN; - if (len < copied) { - if (flags & MSG_PEEK) - refcount_dec(&skb->users); - else - skb_queue_head(&sk->sk_receive_queue, skb); - return -ENOSPC; - } - memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb), - MISDN_HEADER_LEN); - - err = skb_copy_datagram_msg(skb, 0, msg, copied); - - mISDN_sock_cmsg(sk, msg, skb); - - skb_free_datagram(sk, skb); - - return err ? : copied; -} - -static int -mISDN_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct sk_buff *skb; - int err = -ENOMEM; - - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n", - __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr, - sk->sk_protocol); - - if (msg->msg_flags & MSG_OOB) - return -EOPNOTSUPP; - - if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_NOSIGNAL | MSG_ERRQUEUE)) - return -EINVAL; - - if (len < MISDN_HEADER_LEN) - return -EINVAL; - - if (sk->sk_state != MISDN_BOUND) - return -EBADFD; - - lock_sock(sk); - - skb = _l2_alloc_skb(len, GFP_KERNEL); - if (!skb) - goto done; - - if (memcpy_from_msg(skb_put(skb, len), msg, len)) { - err = -EFAULT; - goto done; - } - - memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN); - skb_pull(skb, MISDN_HEADER_LEN); - - if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) { - /* if we have a address, we use it */ - DECLARE_SOCKADDR(struct sockaddr_mISDN *, maddr, msg->msg_name); - mISDN_HEAD_ID(skb) = maddr->channel; - } else { /* use default for L2 messages */ - if ((sk->sk_protocol == ISDN_P_LAPD_TE) || - (sk->sk_protocol == ISDN_P_LAPD_NT)) - mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr; - } - - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s: ID:%x\n", - __func__, mISDN_HEAD_ID(skb)); - - err = -ENODEV; - if (!_pms(sk)->ch.peer) - goto done; - err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb); - if (err) - goto done; - else { - skb = NULL; - err = len; - } - -done: - kfree_skb(skb); - release_sock(sk); - return err; -} - -static int -data_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); - if (!sk) - return 0; - switch (sk->sk_protocol) { - case ISDN_P_TE_S0: - case ISDN_P_NT_S0: - case ISDN_P_TE_E1: - case ISDN_P_NT_E1: - if (sk->sk_state == MISDN_BOUND) - delete_channel(&_pms(sk)->ch); - else - mISDN_sock_unlink(&data_sockets, sk); - break; - case ISDN_P_LAPD_TE: - case ISDN_P_LAPD_NT: - case ISDN_P_B_RAW: - case ISDN_P_B_HDLC: - case ISDN_P_B_X75SLP: - case ISDN_P_B_L2DTMF: - case ISDN_P_B_L2DSP: - case ISDN_P_B_L2DSPHDLC: - delete_channel(&_pms(sk)->ch); - mISDN_sock_unlink(&data_sockets, sk); - break; - } - - lock_sock(sk); - - sock_orphan(sk); - skb_queue_purge(&sk->sk_receive_queue); - - release_sock(sk); - sock_put(sk); - - return 0; -} - -static int -data_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p) -{ - struct mISDN_ctrl_req cq; - int err = -EINVAL, val[2]; - struct mISDNchannel *bchan, *next; - - lock_sock(sk); - if (!_pms(sk)->dev) { - err = -ENODEV; - goto done; - } - switch (cmd) { - case IMCTRLREQ: - if (copy_from_user(&cq, p, sizeof(cq))) { - err = -EFAULT; - break; - } - if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) { - list_for_each_entry_safe(bchan, next, - &_pms(sk)->dev->bchannels, list) { - if (bchan->nr == cq.channel) { - err = bchan->ctrl(bchan, - CONTROL_CHANNEL, &cq); - break; - } - } - } else - err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D, - CONTROL_CHANNEL, &cq); - if (err) - break; - if (copy_to_user(p, &cq, sizeof(cq))) - err = -EFAULT; - break; - case IMCLEAR_L2: - if (sk->sk_protocol != ISDN_P_LAPD_NT) { - err = -EINVAL; - break; - } - val[0] = cmd; - if (get_user(val[1], (int __user *)p)) { - err = -EFAULT; - break; - } - err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr, - CONTROL_CHANNEL, val); - break; - case IMHOLD_L1: - if (sk->sk_protocol != ISDN_P_LAPD_NT - && sk->sk_protocol != ISDN_P_LAPD_TE) { - err = -EINVAL; - break; - } - val[0] = cmd; - if (get_user(val[1], (int __user *)p)) { - err = -EFAULT; - break; - } - err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr, - CONTROL_CHANNEL, val); - break; - default: - err = -EINVAL; - break; - } -done: - release_sock(sk); - return err; -} - -static int -data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - int err = 0, id; - struct sock *sk = sock->sk; - struct mISDNdevice *dev; - struct mISDNversion ver; - - switch (cmd) { - case IMGETVERSION: - ver.major = MISDN_MAJOR_VERSION; - ver.minor = MISDN_MINOR_VERSION; - ver.release = MISDN_RELEASE; - if (copy_to_user((void __user *)arg, &ver, sizeof(ver))) - err = -EFAULT; - break; - case IMGETCOUNT: - id = get_mdevice_count(); - if (put_user(id, (int __user *)arg)) - err = -EFAULT; - break; - case IMGETDEVINFO: - if (get_user(id, (int __user *)arg)) { - err = -EFAULT; - break; - } - dev = get_mdevice(id); - if (dev) { - struct mISDN_devinfo di; - - memset(&di, 0, sizeof(di)); - di.id = dev->id; - di.Dprotocols = dev->Dprotocols; - di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); - di.protocol = dev->D.protocol; - memcpy(di.channelmap, dev->channelmap, - sizeof(di.channelmap)); - di.nrbchan = dev->nrbchan; - strscpy(di.name, dev_name(&dev->dev), sizeof(di.name)); - if (copy_to_user((void __user *)arg, &di, sizeof(di))) - err = -EFAULT; - } else - err = -ENODEV; - break; - default: - if (sk->sk_state == MISDN_BOUND) - err = data_sock_ioctl_bound(sk, cmd, - (void __user *)arg); - else - err = -ENOTCONN; - } - return err; -} - -static int data_sock_setsockopt(struct socket *sock, int level, int optname, - sockptr_t optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - int err = 0, opt = 0; - - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s(%p, %d, %x, optval, %d)\n", __func__, sock, - level, optname, optlen); - - lock_sock(sk); - - switch (optname) { - case MISDN_TIME_STAMP: - err = copy_safe_from_sockptr(&opt, sizeof(opt), - optval, optlen); - if (err) - break; - - if (opt) - _pms(sk)->cmask |= MISDN_TIME_STAMP; - else - _pms(sk)->cmask &= ~MISDN_TIME_STAMP; - break; - default: - err = -ENOPROTOOPT; - break; - } - release_sock(sk); - return err; -} - -static int data_sock_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - int len, opt; - - if (get_user(len, optlen)) - return -EFAULT; - - if (len != sizeof(char)) - return -EINVAL; - - switch (optname) { - case MISDN_TIME_STAMP: - if (_pms(sk)->cmask & MISDN_TIME_STAMP) - opt = 1; - else - opt = 0; - - if (put_user(opt, optval)) - return -EFAULT; - break; - default: - return -ENOPROTOOPT; - } - - return 0; -} - -static int -data_sock_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr_len) -{ - struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; - struct sock *sk = sock->sk; - struct sock *csk; - int err = 0; - - if (*debug & DEBUG_SOCKET) - printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); - if (addr_len != sizeof(struct sockaddr_mISDN)) - return -EINVAL; - if (!maddr || maddr->family != AF_ISDN) - return -EINVAL; - - lock_sock(sk); - - if (_pms(sk)->dev) { - err = -EALREADY; - goto done; - } - _pms(sk)->dev = get_mdevice(maddr->dev); - if (!_pms(sk)->dev) { - err = -ENODEV; - goto done; - } - - if (sk->sk_protocol < ISDN_P_B_START) { - read_lock_bh(&data_sockets.lock); - sk_for_each(csk, &data_sockets.head) { - if (sk == csk) - continue; - if (_pms(csk)->dev != _pms(sk)->dev) - continue; - if (csk->sk_protocol >= ISDN_P_B_START) - continue; - if (IS_ISDN_P_TE(csk->sk_protocol) - == IS_ISDN_P_TE(sk->sk_protocol)) - continue; - read_unlock_bh(&data_sockets.lock); - err = -EBUSY; - goto done; - } - read_unlock_bh(&data_sockets.lock); - } - - _pms(sk)->ch.send = mISDN_send; - _pms(sk)->ch.ctrl = mISDN_ctrl; - - switch (sk->sk_protocol) { - case ISDN_P_TE_S0: - case ISDN_P_NT_S0: - case ISDN_P_TE_E1: - case ISDN_P_NT_E1: - mISDN_sock_unlink(&data_sockets, sk); - err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch, - sk->sk_protocol, maddr); - if (err) - mISDN_sock_link(&data_sockets, sk); - break; - case ISDN_P_LAPD_TE: - case ISDN_P_LAPD_NT: - err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch, - sk->sk_protocol, maddr); - break; - case ISDN_P_B_RAW: - case ISDN_P_B_HDLC: - case ISDN_P_B_X75SLP: - case ISDN_P_B_L2DTMF: - case ISDN_P_B_L2DSP: - case ISDN_P_B_L2DSPHDLC: - err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch, - sk->sk_protocol, maddr); - break; - default: - err = -EPROTONOSUPPORT; - } - if (err) - goto done; - sk->sk_state = MISDN_BOUND; - _pms(sk)->ch.protocol = sk->sk_protocol; - -done: - release_sock(sk); - return err; -} - -static int -data_sock_getname(struct socket *sock, struct sockaddr *addr, - int peer) -{ - struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; - struct sock *sk = sock->sk; - - if (!_pms(sk)->dev) - return -EBADFD; - - lock_sock(sk); - - maddr->family = AF_ISDN; - maddr->dev = _pms(sk)->dev->id; - maddr->channel = _pms(sk)->ch.nr; - maddr->sapi = _pms(sk)->ch.addr & 0xff; - maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff; - release_sock(sk); - return sizeof(*maddr); -} - -static const struct proto_ops data_sock_ops = { - .family = PF_ISDN, - .owner = THIS_MODULE, - .release = data_sock_release, - .ioctl = data_sock_ioctl, - .bind = data_sock_bind, - .getname = data_sock_getname, - .sendmsg = mISDN_sock_sendmsg, - .recvmsg = mISDN_sock_recvmsg, - .poll = datagram_poll, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = data_sock_setsockopt, - .getsockopt = data_sock_getsockopt, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - -static int -data_sock_create(struct net *net, struct socket *sock, int protocol, int kern) -{ - struct sock *sk; - - if (sock->type != SOCK_DGRAM) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto, kern); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - - sock->ops = &data_sock_ops; - sock->state = SS_UNCONNECTED; - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = protocol; - sk->sk_state = MISDN_OPEN; - mISDN_sock_link(&data_sockets, sk); - - return 0; -} - -static int -base_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); - if (!sk) - return 0; - - mISDN_sock_unlink(&base_sockets, sk); - sock_orphan(sk); - sock_put(sk); - - return 0; -} - -static int -base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - int err = 0, id; - struct mISDNdevice *dev; - struct mISDNversion ver; - - switch (cmd) { - case IMGETVERSION: - ver.major = MISDN_MAJOR_VERSION; - ver.minor = MISDN_MINOR_VERSION; - ver.release = MISDN_RELEASE; - if (copy_to_user((void __user *)arg, &ver, sizeof(ver))) - err = -EFAULT; - break; - case IMGETCOUNT: - id = get_mdevice_count(); - if (put_user(id, (int __user *)arg)) - err = -EFAULT; - break; - case IMGETDEVINFO: - if (get_user(id, (int __user *)arg)) { - err = -EFAULT; - break; - } - dev = get_mdevice(id); - if (dev) { - struct mISDN_devinfo di; - - memset(&di, 0, sizeof(di)); - di.id = dev->id; - di.Dprotocols = dev->Dprotocols; - di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); - di.protocol = dev->D.protocol; - memcpy(di.channelmap, dev->channelmap, - sizeof(di.channelmap)); - di.nrbchan = dev->nrbchan; - strscpy(di.name, dev_name(&dev->dev), sizeof(di.name)); - if (copy_to_user((void __user *)arg, &di, sizeof(di))) - err = -EFAULT; - } else - err = -ENODEV; - break; - case IMSETDEVNAME: - { - struct mISDN_devrename dn; - if (copy_from_user(&dn, (void __user *)arg, - sizeof(dn))) { - err = -EFAULT; - break; - } - dn.name[sizeof(dn.name) - 1] = '\0'; - dev = get_mdevice(dn.id); - if (dev) - err = device_rename(&dev->dev, dn.name); - else - err = -ENODEV; - } - break; - default: - err = -EINVAL; - } - return err; -} - -static int -base_sock_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr_len) -{ - struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; - struct sock *sk = sock->sk; - int err = 0; - - if (addr_len < sizeof(struct sockaddr_mISDN)) - return -EINVAL; - - if (!maddr || maddr->family != AF_ISDN) - return -EINVAL; - - lock_sock(sk); - - if (_pms(sk)->dev) { - err = -EALREADY; - goto done; - } - - _pms(sk)->dev = get_mdevice(maddr->dev); - if (!_pms(sk)->dev) { - err = -ENODEV; - goto done; - } - sk->sk_state = MISDN_BOUND; - -done: - release_sock(sk); - return err; -} - -static const struct proto_ops base_sock_ops = { - .family = PF_ISDN, - .owner = THIS_MODULE, - .release = base_sock_release, - .ioctl = base_sock_ioctl, - .bind = base_sock_bind, - .getname = sock_no_getname, - .sendmsg = sock_no_sendmsg, - .recvmsg = sock_no_recvmsg, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - - -static int -base_sock_create(struct net *net, struct socket *sock, int protocol, int kern) -{ - struct sock *sk; - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - if (!capable(CAP_NET_RAW)) - return -EPERM; - - sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto, kern); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - sock->ops = &base_sock_ops; - sock->state = SS_UNCONNECTED; - sock_reset_flag(sk, SOCK_ZAPPED); - sk->sk_protocol = protocol; - sk->sk_state = MISDN_OPEN; - mISDN_sock_link(&base_sockets, sk); - - return 0; -} - -static int -mISDN_sock_create(struct net *net, struct socket *sock, int proto, int kern) -{ - int err = -EPROTONOSUPPORT; - - switch (proto) { - case ISDN_P_BASE: - err = base_sock_create(net, sock, proto, kern); - break; - case ISDN_P_TE_S0: - case ISDN_P_NT_S0: - case ISDN_P_TE_E1: - case ISDN_P_NT_E1: - case ISDN_P_LAPD_TE: - case ISDN_P_LAPD_NT: - case ISDN_P_B_RAW: - case ISDN_P_B_HDLC: - case ISDN_P_B_X75SLP: - case ISDN_P_B_L2DTMF: - case ISDN_P_B_L2DSP: - case ISDN_P_B_L2DSPHDLC: - err = data_sock_create(net, sock, proto, kern); - break; - default: - return err; - } - - return err; -} - -static const struct net_proto_family mISDN_sock_family_ops = { - .owner = THIS_MODULE, - .family = PF_ISDN, - .create = mISDN_sock_create, -}; - -int -misdn_sock_init(u_int *deb) -{ - int err; - - debug = deb; - err = sock_register(&mISDN_sock_family_ops); - if (err) - printk(KERN_ERR "%s: error(%d)\n", __func__, err); - return err; -} - -void -misdn_sock_cleanup(void) -{ - sock_unregister(PF_ISDN); -} diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c deleted file mode 100644 index 4e96684af0aa..000000000000 --- a/drivers/isdn/mISDN/stack.c +++ /dev/null @@ -1,654 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include - -#include "core.h" - -static u_int *debug; - -static inline void -_queue_message(struct mISDNstack *st, struct sk_buff *skb) -{ - struct mISDNhead *hh = mISDN_HEAD_P(skb); - - if (*debug & DEBUG_QUEUE_FUNC) - printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n", - __func__, hh->prim, hh->id, skb); - skb_queue_tail(&st->msgq, skb); - if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) { - test_and_set_bit(mISDN_STACK_WORK, &st->status); - wake_up_interruptible(&st->workq); - } -} - -static int -mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb) -{ - _queue_message(ch->st, skb); - return 0; -} - -static struct mISDNchannel * -get_channel4id(struct mISDNstack *st, u_int id) -{ - struct mISDNchannel *ch; - - mutex_lock(&st->lmutex); - list_for_each_entry(ch, &st->layer2, list) { - if (id == ch->nr) - goto unlock; - } - ch = NULL; -unlock: - mutex_unlock(&st->lmutex); - return ch; -} - -static void -send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb) -{ - struct sock *sk; - struct sk_buff *cskb = NULL; - - read_lock(&sl->lock); - sk_for_each(sk, &sl->head) { - if (sk->sk_state != MISDN_BOUND) - continue; - if (!cskb) - cskb = skb_copy(skb, GFP_ATOMIC); - if (!cskb) { - printk(KERN_WARNING "%s no skb\n", __func__); - break; - } - if (!sock_queue_rcv_skb(sk, cskb)) - cskb = NULL; - } - read_unlock(&sl->lock); - dev_kfree_skb(cskb); -} - -static void -send_layer2(struct mISDNstack *st, struct sk_buff *skb) -{ - struct sk_buff *cskb; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - struct mISDNchannel *ch; - int ret; - - if (!st) - return; - mutex_lock(&st->lmutex); - if ((hh->id & MISDN_ID_ADDR_MASK) == MISDN_ID_ANY) { /* L2 for all */ - list_for_each_entry(ch, &st->layer2, list) { - if (list_is_last(&ch->list, &st->layer2)) { - cskb = skb; - skb = NULL; - } else { - cskb = skb_copy(skb, GFP_KERNEL); - } - if (cskb) { - ret = ch->send(ch, cskb); - if (ret) { - if (*debug & DEBUG_SEND_ERR) - printk(KERN_DEBUG - "%s ch%d prim(%x) addr(%x)" - " err %d\n", - __func__, ch->nr, - hh->prim, ch->addr, ret); - dev_kfree_skb(cskb); - } - } else { - printk(KERN_WARNING "%s ch%d addr %x no mem\n", - __func__, ch->nr, ch->addr); - goto out; - } - } - } else { - list_for_each_entry(ch, &st->layer2, list) { - if ((hh->id & MISDN_ID_ADDR_MASK) == ch->addr) { - ret = ch->send(ch, skb); - if (!ret) - skb = NULL; - goto out; - } - } - ret = st->dev->teimgr->ctrl(st->dev->teimgr, CHECK_DATA, skb); - if (!ret) - skb = NULL; - else if (*debug & DEBUG_SEND_ERR) - printk(KERN_DEBUG - "%s mgr prim(%x) err %d\n", - __func__, hh->prim, ret); - } -out: - mutex_unlock(&st->lmutex); - dev_kfree_skb(skb); -} - -static inline int -send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb) -{ - struct mISDNhead *hh = mISDN_HEAD_P(skb); - struct mISDNchannel *ch; - int lm; - - lm = hh->prim & MISDN_LAYERMASK; - if (*debug & DEBUG_QUEUE_FUNC) - printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n", - __func__, hh->prim, hh->id, skb); - if (lm == 0x1) { - if (!hlist_empty(&st->l1sock.head)) { - __net_timestamp(skb); - send_socklist(&st->l1sock, skb); - } - return st->layer1->send(st->layer1, skb); - } else if (lm == 0x2) { - if (!hlist_empty(&st->l1sock.head)) - send_socklist(&st->l1sock, skb); - send_layer2(st, skb); - return 0; - } else if (lm == 0x4) { - ch = get_channel4id(st, hh->id); - if (ch) - return ch->send(ch, skb); - else - printk(KERN_WARNING - "%s: dev(%s) prim(%x) id(%x) no channel\n", - __func__, dev_name(&st->dev->dev), hh->prim, - hh->id); - } else if (lm == 0x8) { - WARN_ON(lm == 0x8); - ch = get_channel4id(st, hh->id); - if (ch) - return ch->send(ch, skb); - else - printk(KERN_WARNING - "%s: dev(%s) prim(%x) id(%x) no channel\n", - __func__, dev_name(&st->dev->dev), hh->prim, - hh->id); - } else { - /* broadcast not handled yet */ - printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n", - __func__, dev_name(&st->dev->dev), hh->prim); - } - return -ESRCH; -} - -static void -do_clear_stack(struct mISDNstack *st) -{ -} - -static int -mISDNStackd(void *data) -{ - struct mISDNstack *st = data; -#ifdef MISDN_MSG_STATS - u64 utime, stime; -#endif - int err = 0; - - sigfillset(¤t->blocked); - if (*debug & DEBUG_MSG_THREAD) - printk(KERN_DEBUG "mISDNStackd %s started\n", - dev_name(&st->dev->dev)); - - if (st->notify != NULL) { - complete(st->notify); - st->notify = NULL; - } - - for (;;) { - struct sk_buff *skb; - - if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) { - test_and_clear_bit(mISDN_STACK_WORK, &st->status); - test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); - } else - test_and_set_bit(mISDN_STACK_RUNNING, &st->status); - while (test_bit(mISDN_STACK_WORK, &st->status)) { - skb = skb_dequeue(&st->msgq); - if (!skb) { - test_and_clear_bit(mISDN_STACK_WORK, - &st->status); - /* test if a race happens */ - skb = skb_dequeue(&st->msgq); - if (!skb) - continue; - test_and_set_bit(mISDN_STACK_WORK, - &st->status); - } -#ifdef MISDN_MSG_STATS - st->msg_cnt++; -#endif - err = send_msg_to_layer(st, skb); - if (unlikely(err)) { - if (*debug & DEBUG_SEND_ERR) - printk(KERN_DEBUG - "%s: %s prim(%x) id(%x) " - "send call(%d)\n", - __func__, dev_name(&st->dev->dev), - mISDN_HEAD_PRIM(skb), - mISDN_HEAD_ID(skb), err); - dev_kfree_skb(skb); - continue; - } - if (unlikely(test_bit(mISDN_STACK_STOPPED, - &st->status))) { - test_and_clear_bit(mISDN_STACK_WORK, - &st->status); - test_and_clear_bit(mISDN_STACK_RUNNING, - &st->status); - break; - } - } - if (test_bit(mISDN_STACK_CLEARING, &st->status)) { - test_and_set_bit(mISDN_STACK_STOPPED, &st->status); - test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); - do_clear_stack(st); - test_and_clear_bit(mISDN_STACK_CLEARING, &st->status); - test_and_set_bit(mISDN_STACK_RESTART, &st->status); - } - if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) { - test_and_clear_bit(mISDN_STACK_STOPPED, &st->status); - test_and_set_bit(mISDN_STACK_RUNNING, &st->status); - if (!skb_queue_empty(&st->msgq)) - test_and_set_bit(mISDN_STACK_WORK, - &st->status); - } - if (test_bit(mISDN_STACK_ABORT, &st->status)) - break; - if (st->notify != NULL) { - complete(st->notify); - st->notify = NULL; - } -#ifdef MISDN_MSG_STATS - st->sleep_cnt++; -#endif - test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); - wait_event_interruptible(st->workq, (st->status & - mISDN_STACK_ACTION_MASK)); - if (*debug & DEBUG_MSG_THREAD) - printk(KERN_DEBUG "%s: %s wake status %08lx\n", - __func__, dev_name(&st->dev->dev), st->status); - test_and_set_bit(mISDN_STACK_ACTIVE, &st->status); - - test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status); - - if (test_bit(mISDN_STACK_STOPPED, &st->status)) { - test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); -#ifdef MISDN_MSG_STATS - st->stopped_cnt++; -#endif - } - } -#ifdef MISDN_MSG_STATS - printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d " - "msg %d sleep %d stopped\n", - dev_name(&st->dev->dev), st->msg_cnt, st->sleep_cnt, - st->stopped_cnt); - task_cputime(st->thread, &utime, &stime); - printk(KERN_DEBUG - "mISDNStackd daemon for %s utime(%llu) stime(%llu)\n", - dev_name(&st->dev->dev), utime, stime); - printk(KERN_DEBUG - "mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n", - dev_name(&st->dev->dev), st->thread->nvcsw, st->thread->nivcsw); - printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n", - dev_name(&st->dev->dev)); -#endif - test_and_set_bit(mISDN_STACK_KILLED, &st->status); - test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); - test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); - test_and_clear_bit(mISDN_STACK_ABORT, &st->status); - skb_queue_purge(&st->msgq); - st->thread = NULL; - if (st->notify != NULL) { - complete(st->notify); - st->notify = NULL; - } - return 0; -} - -static int -l1_receive(struct mISDNchannel *ch, struct sk_buff *skb) -{ - if (!ch->st) - return -ENODEV; - __net_timestamp(skb); - _queue_message(ch->st, skb); - return 0; -} - -void -set_channel_address(struct mISDNchannel *ch, u_int sapi, u_int tei) -{ - ch->addr = sapi | (tei << 8); -} - -void -__add_layer2(struct mISDNchannel *ch, struct mISDNstack *st) -{ - list_add_tail(&ch->list, &st->layer2); -} - -void -add_layer2(struct mISDNchannel *ch, struct mISDNstack *st) -{ - mutex_lock(&st->lmutex); - __add_layer2(ch, st); - mutex_unlock(&st->lmutex); -} - -static int -st_own_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - if (!ch->st || !ch->st->layer1) - return -EINVAL; - return ch->st->layer1->ctrl(ch->st->layer1, cmd, arg); -} - -int -create_stack(struct mISDNdevice *dev) -{ - struct mISDNstack *newst; - int err; - DECLARE_COMPLETION_ONSTACK(done); - - newst = kzalloc_obj(struct mISDNstack); - if (!newst) { - printk(KERN_ERR "kmalloc mISDN_stack failed\n"); - return -ENOMEM; - } - newst->dev = dev; - INIT_LIST_HEAD(&newst->layer2); - INIT_HLIST_HEAD(&newst->l1sock.head); - rwlock_init(&newst->l1sock.lock); - init_waitqueue_head(&newst->workq); - skb_queue_head_init(&newst->msgq); - mutex_init(&newst->lmutex); - dev->D.st = newst; - err = create_teimanager(dev); - if (err) { - printk(KERN_ERR "kmalloc teimanager failed\n"); - kfree(newst); - return err; - } - dev->teimgr->peer = &newst->own; - dev->teimgr->recv = mISDN_queue_message; - dev->teimgr->st = newst; - newst->layer1 = &dev->D; - dev->D.recv = l1_receive; - dev->D.peer = &newst->own; - newst->own.st = newst; - newst->own.ctrl = st_own_ctrl; - newst->own.send = mISDN_queue_message; - newst->own.recv = mISDN_queue_message; - if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: st(%s)\n", __func__, - dev_name(&newst->dev->dev)); - newst->notify = &done; - newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s", - dev_name(&newst->dev->dev)); - if (IS_ERR(newst->thread)) { - err = PTR_ERR(newst->thread); - printk(KERN_ERR - "mISDN:cannot create kernel thread for %s (%d)\n", - dev_name(&newst->dev->dev), err); - delete_teimanager(dev->teimgr); - kfree(newst); - } else - wait_for_completion(&done); - return err; -} - -int -connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch, - u_int protocol, struct sockaddr_mISDN *adr) -{ - struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch); - struct channel_req rq; - int err; - - - if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev_name(&dev->dev), protocol, adr->dev, - adr->channel, adr->sapi, adr->tei); - switch (protocol) { - case ISDN_P_NT_S0: - case ISDN_P_NT_E1: - case ISDN_P_TE_S0: - case ISDN_P_TE_E1: - ch->recv = mISDN_queue_message; - ch->peer = &dev->D.st->own; - ch->st = dev->D.st; - rq.protocol = protocol; - rq.adr.channel = adr->channel; - err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); - printk(KERN_DEBUG "%s: ret %d (dev %d)\n", __func__, err, - dev->id); - if (err) - return err; - write_lock_bh(&dev->D.st->l1sock.lock); - sk_add_node(&msk->sk, &dev->D.st->l1sock.head); - write_unlock_bh(&dev->D.st->l1sock.lock); - break; - default: - return -ENOPROTOOPT; - } - return 0; -} - -int -connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch, - u_int protocol, struct sockaddr_mISDN *adr) -{ - struct channel_req rq, rq2; - int pmask, err; - struct Bprotocol *bp; - - if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev_name(&dev->dev), protocol, - adr->dev, adr->channel, adr->sapi, - adr->tei); - ch->st = dev->D.st; - pmask = 1 << (protocol & ISDN_P_B_MASK); - if (pmask & dev->Bprotocols) { - rq.protocol = protocol; - rq.adr = *adr; - err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); - if (err) - return err; - ch->recv = rq.ch->send; - ch->peer = rq.ch; - rq.ch->recv = ch->send; - rq.ch->peer = ch; - rq.ch->st = dev->D.st; - } else { - bp = get_Bprotocol4mask(pmask); - if (!bp) - return -ENOPROTOOPT; - rq2.protocol = protocol; - rq2.adr = *adr; - rq2.ch = ch; - err = bp->create(&rq2); - if (err) - return err; - ch->recv = rq2.ch->send; - ch->peer = rq2.ch; - rq2.ch->st = dev->D.st; - rq.protocol = rq2.protocol; - rq.adr = *adr; - err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); - if (err) { - rq2.ch->ctrl(rq2.ch, CLOSE_CHANNEL, NULL); - return err; - } - rq2.ch->recv = rq.ch->send; - rq2.ch->peer = rq.ch; - rq.ch->recv = rq2.ch->send; - rq.ch->peer = rq2.ch; - rq.ch->st = dev->D.st; - } - ch->protocol = protocol; - ch->nr = rq.ch->nr; - return 0; -} - -int -create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch, - u_int protocol, struct sockaddr_mISDN *adr) -{ - struct channel_req rq; - int err; - - if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev_name(&dev->dev), protocol, - adr->dev, adr->channel, adr->sapi, - adr->tei); - rq.protocol = ISDN_P_TE_S0; - if (dev->Dprotocols & (1 << ISDN_P_TE_E1)) - rq.protocol = ISDN_P_TE_E1; - switch (protocol) { - case ISDN_P_LAPD_NT: - rq.protocol = ISDN_P_NT_S0; - if (dev->Dprotocols & (1 << ISDN_P_NT_E1)) - rq.protocol = ISDN_P_NT_E1; - fallthrough; - case ISDN_P_LAPD_TE: - ch->recv = mISDN_queue_message; - ch->peer = &dev->D.st->own; - ch->st = dev->D.st; - rq.adr.channel = 0; - err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); - printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err); - if (err) - break; - rq.protocol = protocol; - rq.adr = *adr; - rq.ch = ch; - err = dev->teimgr->ctrl(dev->teimgr, OPEN_CHANNEL, &rq); - printk(KERN_DEBUG "%s: ret 2 %d\n", __func__, err); - if (!err) { - if ((protocol == ISDN_P_LAPD_NT) && !rq.ch) - break; - add_layer2(rq.ch, dev->D.st); - rq.ch->recv = mISDN_queue_message; - rq.ch->peer = &dev->D.st->own; - rq.ch->ctrl(rq.ch, OPEN_CHANNEL, NULL); /* can't fail */ - } - break; - default: - err = -EPROTONOSUPPORT; - } - return err; -} - -void -delete_channel(struct mISDNchannel *ch) -{ - struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch); - struct mISDNchannel *pch; - - if (!ch->st) { - printk(KERN_WARNING "%s: no stack\n", __func__); - return; - } - if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__, - dev_name(&ch->st->dev->dev), ch->protocol); - if (ch->protocol >= ISDN_P_B_START) { - if (ch->peer) { - ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL); - ch->peer = NULL; - } - return; - } - switch (ch->protocol) { - case ISDN_P_NT_S0: - case ISDN_P_TE_S0: - case ISDN_P_NT_E1: - case ISDN_P_TE_E1: - write_lock_bh(&ch->st->l1sock.lock); - sk_del_node_init(&msk->sk); - write_unlock_bh(&ch->st->l1sock.lock); - ch->st->dev->D.ctrl(&ch->st->dev->D, CLOSE_CHANNEL, NULL); - break; - case ISDN_P_LAPD_TE: - pch = get_channel4id(ch->st, ch->nr); - if (pch) { - mutex_lock(&ch->st->lmutex); - list_del(&pch->list); - mutex_unlock(&ch->st->lmutex); - pch->ctrl(pch, CLOSE_CHANNEL, NULL); - pch = ch->st->dev->teimgr; - pch->ctrl(pch, CLOSE_CHANNEL, NULL); - } else - printk(KERN_WARNING "%s: no l2 channel\n", - __func__); - break; - case ISDN_P_LAPD_NT: - pch = ch->st->dev->teimgr; - if (pch) { - pch->ctrl(pch, CLOSE_CHANNEL, NULL); - } else - printk(KERN_WARNING "%s: no l2 channel\n", - __func__); - break; - default: - break; - } - return; -} - -void -delete_stack(struct mISDNdevice *dev) -{ - struct mISDNstack *st = dev->D.st; - DECLARE_COMPLETION_ONSTACK(done); - - if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: st(%s)\n", __func__, - dev_name(&st->dev->dev)); - if (dev->teimgr) - delete_teimanager(dev->teimgr); - if (st->thread) { - if (st->notify) { - printk(KERN_WARNING "%s: notifier in use\n", - __func__); - complete(st->notify); - } - st->notify = &done; - test_and_set_bit(mISDN_STACK_ABORT, &st->status); - test_and_set_bit(mISDN_STACK_WAKEUP, &st->status); - wake_up_interruptible(&st->workq); - wait_for_completion(&done); - } - if (!list_empty(&st->layer2)) - printk(KERN_WARNING "%s: layer2 list not empty\n", - __func__); - if (!hlist_empty(&st->l1sock.head)) - printk(KERN_WARNING "%s: layer1 list not empty\n", - __func__); - kfree(st); -} - -void -mISDN_initstack(u_int *dp) -{ - debug = dp; -} diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c deleted file mode 100644 index 2bad3083be90..000000000000 --- a/drivers/isdn/mISDN/tei.c +++ /dev/null @@ -1,1416 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ -#include "layer2.h" -#include -#include -#include "core.h" - -#define ID_REQUEST 1 -#define ID_ASSIGNED 2 -#define ID_DENIED 3 -#define ID_CHK_REQ 4 -#define ID_CHK_RES 5 -#define ID_REMOVE 6 -#define ID_VERIFY 7 - -#define TEI_ENTITY_ID 0xf - -#define MGR_PH_ACTIVE 16 -#define MGR_PH_NOTREADY 17 - -#define DATIMER_VAL 10000 - -static u_int *debug; - -static struct Fsm deactfsm = {NULL, 0, 0, NULL, NULL}; -static struct Fsm teifsmu = {NULL, 0, 0, NULL, NULL}; -static struct Fsm teifsmn = {NULL, 0, 0, NULL, NULL}; - -enum { - ST_L1_DEACT, - ST_L1_DEACT_PENDING, - ST_L1_ACTIV, -}; -#define DEACT_STATE_COUNT (ST_L1_ACTIV + 1) - -static char *strDeactState[] = -{ - "ST_L1_DEACT", - "ST_L1_DEACT_PENDING", - "ST_L1_ACTIV", -}; - -enum { - EV_ACTIVATE, - EV_ACTIVATE_IND, - EV_DEACTIVATE, - EV_DEACTIVATE_IND, - EV_UI, - EV_DATIMER, -}; - -#define DEACT_EVENT_COUNT (EV_DATIMER + 1) - -static char *strDeactEvent[] = -{ - "EV_ACTIVATE", - "EV_ACTIVATE_IND", - "EV_DEACTIVATE", - "EV_DEACTIVATE_IND", - "EV_UI", - "EV_DATIMER", -}; - -static void -da_debug(struct FsmInst *fi, char *fmt, ...) -{ - struct manager *mgr = fi->userdata; - struct va_format vaf; - va_list va; - - if (!(*debug & DEBUG_L2_TEIFSM)) - return; - - va_start(va, fmt); - - vaf.fmt = fmt; - vaf.va = &va; - - printk(KERN_DEBUG "mgr(%d): %pV\n", mgr->ch.st->dev->id, &vaf); - - va_end(va); -} - -static void -da_activate(struct FsmInst *fi, int event, void *arg) -{ - struct manager *mgr = fi->userdata; - - if (fi->state == ST_L1_DEACT_PENDING) - mISDN_FsmDelTimer(&mgr->datimer, 1); - mISDN_FsmChangeState(fi, ST_L1_ACTIV); -} - -static void -da_deactivate_ind(struct FsmInst *fi, int event, void *arg) -{ - mISDN_FsmChangeState(fi, ST_L1_DEACT); -} - -static void -da_deactivate(struct FsmInst *fi, int event, void *arg) -{ - struct manager *mgr = fi->userdata; - struct layer2 *l2; - u_long flags; - - read_lock_irqsave(&mgr->lock, flags); - list_for_each_entry(l2, &mgr->layer2, list) { - if (l2->l2m.state > ST_L2_4) { - /* have still activ TEI */ - read_unlock_irqrestore(&mgr->lock, flags); - return; - } - } - read_unlock_irqrestore(&mgr->lock, flags); - /* All TEI are inactiv */ - if (!test_bit(OPTION_L1_HOLD, &mgr->options)) { - mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, - NULL, 1); - mISDN_FsmChangeState(fi, ST_L1_DEACT_PENDING); - } -} - -static void -da_ui(struct FsmInst *fi, int event, void *arg) -{ - struct manager *mgr = fi->userdata; - - /* restart da timer */ - if (!test_bit(OPTION_L1_HOLD, &mgr->options)) { - mISDN_FsmDelTimer(&mgr->datimer, 2); - mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, - NULL, 2); - } -} - -static void -da_timer(struct FsmInst *fi, int event, void *arg) -{ - struct manager *mgr = fi->userdata; - struct layer2 *l2; - u_long flags; - - /* check again */ - read_lock_irqsave(&mgr->lock, flags); - list_for_each_entry(l2, &mgr->layer2, list) { - if (l2->l2m.state > ST_L2_4) { - /* have still activ TEI */ - read_unlock_irqrestore(&mgr->lock, flags); - mISDN_FsmChangeState(fi, ST_L1_ACTIV); - return; - } - } - read_unlock_irqrestore(&mgr->lock, flags); - /* All TEI are inactiv */ - mISDN_FsmChangeState(fi, ST_L1_DEACT); - _queue_data(&mgr->ch, PH_DEACTIVATE_REQ, MISDN_ID_ANY, 0, NULL, - GFP_ATOMIC); -} - -static struct FsmNode DeactFnList[] = -{ - {ST_L1_DEACT, EV_ACTIVATE_IND, da_activate}, - {ST_L1_ACTIV, EV_DEACTIVATE_IND, da_deactivate_ind}, - {ST_L1_ACTIV, EV_DEACTIVATE, da_deactivate}, - {ST_L1_DEACT_PENDING, EV_ACTIVATE, da_activate}, - {ST_L1_DEACT_PENDING, EV_UI, da_ui}, - {ST_L1_DEACT_PENDING, EV_DATIMER, da_timer}, -}; - -enum { - ST_TEI_NOP, - ST_TEI_IDREQ, - ST_TEI_IDVERIFY, -}; - -#define TEI_STATE_COUNT (ST_TEI_IDVERIFY + 1) - -static char *strTeiState[] = -{ - "ST_TEI_NOP", - "ST_TEI_IDREQ", - "ST_TEI_IDVERIFY", -}; - -enum { - EV_IDREQ, - EV_ASSIGN, - EV_ASSIGN_REQ, - EV_DENIED, - EV_CHKREQ, - EV_CHKRESP, - EV_REMOVE, - EV_VERIFY, - EV_TIMER, -}; - -#define TEI_EVENT_COUNT (EV_TIMER + 1) - -static char *strTeiEvent[] = -{ - "EV_IDREQ", - "EV_ASSIGN", - "EV_ASSIGN_REQ", - "EV_DENIED", - "EV_CHKREQ", - "EV_CHKRESP", - "EV_REMOVE", - "EV_VERIFY", - "EV_TIMER", -}; - -static void -tei_debug(struct FsmInst *fi, char *fmt, ...) -{ - struct teimgr *tm = fi->userdata; - struct va_format vaf; - va_list va; - - if (!(*debug & DEBUG_L2_TEIFSM)) - return; - - va_start(va, fmt); - - vaf.fmt = fmt; - vaf.va = &va; - - printk(KERN_DEBUG "sapi(%d) tei(%d): %pV\n", - tm->l2->sapi, tm->l2->tei, &vaf); - - va_end(va); -} - - - -static int -get_free_id(struct manager *mgr) -{ - DECLARE_BITMAP(ids, 64) = { [0 ... BITS_TO_LONGS(64) - 1] = 0 }; - int i; - struct layer2 *l2; - - list_for_each_entry(l2, &mgr->layer2, list) { - if (l2->ch.nr > 63) { - printk(KERN_WARNING - "%s: more as 63 layer2 for one device\n", - __func__); - return -EBUSY; - } - __set_bit(l2->ch.nr, ids); - } - i = find_next_zero_bit(ids, 64, 1); - if (i < 64) - return i; - printk(KERN_WARNING "%s: more as 63 layer2 for one device\n", - __func__); - return -EBUSY; -} - -static int -get_free_tei(struct manager *mgr) -{ - DECLARE_BITMAP(ids, 64) = { [0 ... BITS_TO_LONGS(64) - 1] = 0 }; - int i; - struct layer2 *l2; - - list_for_each_entry(l2, &mgr->layer2, list) { - if (l2->ch.nr == 0) - continue; - if ((l2->ch.addr & 0xff) != 0) - continue; - i = l2->ch.addr >> 8; - if (i < 64) - continue; - i -= 64; - - __set_bit(i, ids); - } - i = find_first_zero_bit(ids, 64); - if (i < 64) - return i + 64; - printk(KERN_WARNING "%s: more as 63 dynamic tei for one device\n", - __func__); - return -1; -} - -static void -teiup_create(struct manager *mgr, u_int prim, int len, void *arg) -{ - struct sk_buff *skb; - struct mISDNhead *hh; - int err; - - skb = mI_alloc_skb(len, GFP_ATOMIC); - if (!skb) - return; - hh = mISDN_HEAD_P(skb); - hh->prim = prim; - hh->id = (mgr->ch.nr << 16) | mgr->ch.addr; - if (len) - skb_put_data(skb, arg, len); - err = mgr->up->send(mgr->up, skb); - if (err) { - printk(KERN_WARNING "%s: err=%d\n", __func__, err); - dev_kfree_skb(skb); - } -} - -static u_int -new_id(struct manager *mgr) -{ - u_int id; - - id = mgr->nextid++; - if (id == 0x7fff) - mgr->nextid = 1; - id <<= 16; - id |= GROUP_TEI << 8; - id |= TEI_SAPI; - return id; -} - -static void -do_send(struct manager *mgr) -{ - if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) - return; - - if (!test_and_set_bit(MGR_PH_NOTREADY, &mgr->options)) { - struct sk_buff *skb = skb_dequeue(&mgr->sendq); - - if (!skb) { - test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); - return; - } - mgr->lastid = mISDN_HEAD_ID(skb); - mISDN_FsmEvent(&mgr->deact, EV_UI, NULL); - if (mgr->ch.recv(mgr->ch.peer, skb)) { - dev_kfree_skb(skb); - test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); - mgr->lastid = MISDN_ID_NONE; - } - } -} - -static void -do_ack(struct manager *mgr, u_int id) -{ - if (test_bit(MGR_PH_NOTREADY, &mgr->options)) { - if (id == mgr->lastid) { - if (test_bit(MGR_PH_ACTIVE, &mgr->options)) { - struct sk_buff *skb; - - skb = skb_dequeue(&mgr->sendq); - if (skb) { - mgr->lastid = mISDN_HEAD_ID(skb); - if (!mgr->ch.recv(mgr->ch.peer, skb)) - return; - dev_kfree_skb(skb); - } - } - mgr->lastid = MISDN_ID_NONE; - test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); - } - } -} - -static void -mgr_send_down(struct manager *mgr, struct sk_buff *skb) -{ - skb_queue_tail(&mgr->sendq, skb); - if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) { - _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - } else { - do_send(mgr); - } -} - -static int -dl_unit_data(struct manager *mgr, struct sk_buff *skb) -{ - if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) /* only net send UI */ - return -EINVAL; - if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) - _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0, - NULL, GFP_KERNEL); - skb_push(skb, 3); - skb->data[0] = 0x02; /* SAPI 0 C/R = 1 */ - skb->data[1] = 0xff; /* TEI 127 */ - skb->data[2] = UI; /* UI frame */ - mISDN_HEAD_PRIM(skb) = PH_DATA_REQ; - mISDN_HEAD_ID(skb) = new_id(mgr); - skb_queue_tail(&mgr->sendq, skb); - do_send(mgr); - return 0; -} - -static unsigned int -random_ri(void) -{ - u16 x; - - get_random_bytes(&x, sizeof(x)); - return x; -} - -static struct layer2 * -findtei(struct manager *mgr, int tei) -{ - struct layer2 *l2; - u_long flags; - - read_lock_irqsave(&mgr->lock, flags); - list_for_each_entry(l2, &mgr->layer2, list) { - if ((l2->sapi == 0) && (l2->tei > 0) && - (l2->tei != GROUP_TEI) && (l2->tei == tei)) - goto done; - } - l2 = NULL; -done: - read_unlock_irqrestore(&mgr->lock, flags); - return l2; -} - -static void -put_tei_msg(struct manager *mgr, u_char m_id, unsigned int ri, int tei) -{ - struct sk_buff *skb; - u_char bp[8]; - - bp[0] = (TEI_SAPI << 2); - if (test_bit(MGR_OPT_NETWORK, &mgr->options)) - bp[0] |= 2; /* CR:=1 for net command */ - bp[1] = (GROUP_TEI << 1) | 0x1; - bp[2] = UI; - bp[3] = TEI_ENTITY_ID; - bp[4] = ri >> 8; - bp[5] = ri & 0xff; - bp[6] = m_id; - bp[7] = ((tei << 1) & 0xff) | 1; - skb = _alloc_mISDN_skb(PH_DATA_REQ, new_id(mgr), 8, bp, GFP_ATOMIC); - if (!skb) { - printk(KERN_WARNING "%s: no skb for tei msg\n", __func__); - return; - } - mgr_send_down(mgr, skb); -} - -static void -tei_id_request(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - - if (tm->l2->tei != GROUP_TEI) { - tm->tei_m.printdebug(&tm->tei_m, - "assign request for already assigned tei %d", - tm->l2->tei); - return; - } - tm->ri = random_ri(); - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(&tm->tei_m, - "assign request ri %d", tm->ri); - put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI); - mISDN_FsmChangeState(fi, ST_TEI_IDREQ); - mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 1); - tm->nval = 3; -} - -static void -tei_id_assign(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - struct layer2 *l2; - u_char *dp = arg; - int ri, tei; - - ri = ((unsigned int) *dp++ << 8); - ri += *dp++; - dp++; - tei = *dp >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "identity assign ri %d tei %d", - ri, tei); - l2 = findtei(tm->mgr, tei); - if (l2) { /* same tei is in use */ - if (ri != l2->tm->ri) { - tm->tei_m.printdebug(fi, - "possible duplicate assignment tei %d", tei); - tei_l2(l2, MDL_ERROR_RSP, 0); - } - } else if (ri == tm->ri) { - mISDN_FsmDelTimer(&tm->timer, 1); - mISDN_FsmChangeState(fi, ST_TEI_NOP); - tei_l2(tm->l2, MDL_ASSIGN_REQ, tei); - } -} - -static void -tei_id_test_dup(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - struct layer2 *l2; - u_char *dp = arg; - int tei, ri; - - ri = ((unsigned int) *dp++ << 8); - ri += *dp++; - dp++; - tei = *dp >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "foreign identity assign ri %d tei %d", - ri, tei); - l2 = findtei(tm->mgr, tei); - if (l2) { /* same tei is in use */ - if (ri != l2->tm->ri) { /* and it wasn't our request */ - tm->tei_m.printdebug(fi, - "possible duplicate assignment tei %d", tei); - mISDN_FsmEvent(&l2->tm->tei_m, EV_VERIFY, NULL); - } - } -} - -static void -tei_id_denied(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - u_char *dp = arg; - int ri, tei; - - ri = ((unsigned int) *dp++ << 8); - ri += *dp++; - dp++; - tei = *dp >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "identity denied ri %d tei %d", - ri, tei); -} - -static void -tei_id_chk_req(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - u_char *dp = arg; - int tei; - - tei = *(dp + 3) >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "identity check req tei %d", tei); - if ((tm->l2->tei != GROUP_TEI) && ((tei == GROUP_TEI) || - (tei == tm->l2->tei))) { - mISDN_FsmDelTimer(&tm->timer, 4); - mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); - put_tei_msg(tm->mgr, ID_CHK_RES, random_ri(), tm->l2->tei); - } -} - -static void -tei_id_remove(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - u_char *dp = arg; - int tei; - - tei = *(dp + 3) >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "identity remove tei %d", tei); - if ((tm->l2->tei != GROUP_TEI) && - ((tei == GROUP_TEI) || (tei == tm->l2->tei))) { - mISDN_FsmDelTimer(&tm->timer, 5); - mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); - tei_l2(tm->l2, MDL_REMOVE_REQ, 0); - } -} - -static void -tei_id_verify(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "id verify request for tei %d", - tm->l2->tei); - put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei); - mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); - mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2); - tm->nval = 2; -} - -static void -tei_id_req_tout(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - - if (--tm->nval) { - tm->ri = random_ri(); - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "assign req(%d) ri %d", - 4 - tm->nval, tm->ri); - put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI); - mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 3); - } else { - tm->tei_m.printdebug(fi, "assign req failed"); - tei_l2(tm->l2, MDL_ERROR_RSP, 0); - mISDN_FsmChangeState(fi, ST_TEI_NOP); - } -} - -static void -tei_id_ver_tout(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - - if (--tm->nval) { - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, - "id verify req(%d) for tei %d", - 3 - tm->nval, tm->l2->tei); - put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei); - mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4); - } else { - tm->tei_m.printdebug(fi, "verify req for tei %d failed", - tm->l2->tei); - tei_l2(tm->l2, MDL_REMOVE_REQ, 0); - mISDN_FsmChangeState(fi, ST_TEI_NOP); - } -} - -static struct FsmNode TeiFnListUser[] = -{ - {ST_TEI_NOP, EV_IDREQ, tei_id_request}, - {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup}, - {ST_TEI_NOP, EV_VERIFY, tei_id_verify}, - {ST_TEI_NOP, EV_REMOVE, tei_id_remove}, - {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req}, - {ST_TEI_IDREQ, EV_TIMER, tei_id_req_tout}, - {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign}, - {ST_TEI_IDREQ, EV_DENIED, tei_id_denied}, - {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout}, - {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove}, - {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req}, -}; - -static void -tei_l2remove(struct layer2 *l2) -{ - put_tei_msg(l2->tm->mgr, ID_REMOVE, 0, l2->tei); - tei_l2(l2, MDL_REMOVE_REQ, 0); - list_del(&l2->ch.list); - l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); -} - -static void -tei_assign_req(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - u_char *dp = arg; - - if (tm->l2->tei == GROUP_TEI) { - tm->tei_m.printdebug(&tm->tei_m, - "net tei assign request without tei"); - return; - } - tm->ri = ((unsigned int) *dp++ << 8); - tm->ri += *dp++; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(&tm->tei_m, - "net assign request ri %d teim %d", tm->ri, *dp); - put_tei_msg(tm->mgr, ID_ASSIGNED, tm->ri, tm->l2->tei); - mISDN_FsmChangeState(fi, ST_TEI_NOP); -} - -static void -tei_id_chk_req_net(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "id check request for tei %d", - tm->l2->tei); - tm->rcnt = 0; - put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei); - mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); - mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2); - tm->nval = 2; -} - -static void -tei_id_chk_resp(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - u_char *dp = arg; - int tei; - - tei = dp[3] >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "identity check resp tei %d", tei); - if (tei == tm->l2->tei) - tm->rcnt++; -} - -static void -tei_id_verify_net(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - u_char *dp = arg; - int tei; - - tei = dp[3] >> 1; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, "identity verify req tei %d/%d", - tei, tm->l2->tei); - if (tei == tm->l2->tei) - tei_id_chk_req_net(fi, event, arg); -} - -static void -tei_id_ver_tout_net(struct FsmInst *fi, int event, void *arg) -{ - struct teimgr *tm = fi->userdata; - - if (tm->rcnt == 1) { - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, - "check req for tei %d successful\n", tm->l2->tei); - mISDN_FsmChangeState(fi, ST_TEI_NOP); - } else if (tm->rcnt > 1) { - /* duplicate assignment; remove */ - tei_l2remove(tm->l2); - } else if (--tm->nval) { - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(fi, - "id check req(%d) for tei %d", - 3 - tm->nval, tm->l2->tei); - put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei); - mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4); - } else { - tm->tei_m.printdebug(fi, "check req for tei %d failed", - tm->l2->tei); - mISDN_FsmChangeState(fi, ST_TEI_NOP); - tei_l2remove(tm->l2); - } -} - -static struct FsmNode TeiFnListNet[] = -{ - {ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req}, - {ST_TEI_NOP, EV_VERIFY, tei_id_verify_net}, - {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req_net}, - {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout_net}, - {ST_TEI_IDVERIFY, EV_CHKRESP, tei_id_chk_resp}, -}; - -static void -tei_ph_data_ind(struct teimgr *tm, u_int mt, u_char *dp, int len) -{ - if (test_bit(FLG_FIXED_TEI, &tm->l2->flag)) - return; - if (*debug & DEBUG_L2_TEI) - tm->tei_m.printdebug(&tm->tei_m, "tei handler mt %x", mt); - if (mt == ID_ASSIGNED) - mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN, dp); - else if (mt == ID_DENIED) - mISDN_FsmEvent(&tm->tei_m, EV_DENIED, dp); - else if (mt == ID_CHK_REQ) - mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, dp); - else if (mt == ID_REMOVE) - mISDN_FsmEvent(&tm->tei_m, EV_REMOVE, dp); - else if (mt == ID_VERIFY) - mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, dp); - else if (mt == ID_CHK_RES) - mISDN_FsmEvent(&tm->tei_m, EV_CHKRESP, dp); -} - -static struct layer2 * -create_new_tei(struct manager *mgr, int tei, int sapi) -{ - unsigned long opt = 0; - unsigned long flags; - int id; - struct layer2 *l2; - struct channel_req rq; - - if (!mgr->up) - return NULL; - if ((tei >= 0) && (tei < 64)) - test_and_set_bit(OPTION_L2_FIXEDTEI, &opt); - if (mgr->ch.st->dev->Dprotocols & ((1 << ISDN_P_TE_E1) | - (1 << ISDN_P_NT_E1))) { - test_and_set_bit(OPTION_L2_PMX, &opt); - rq.protocol = ISDN_P_NT_E1; - } else { - rq.protocol = ISDN_P_NT_S0; - } - l2 = create_l2(mgr->up, ISDN_P_LAPD_NT, opt, tei, sapi); - if (!l2) { - printk(KERN_WARNING "%s:no memory for layer2\n", __func__); - return NULL; - } - l2->tm = kzalloc_obj(struct teimgr); - if (!l2->tm) { - kfree(l2); - printk(KERN_WARNING "%s:no memory for teimgr\n", __func__); - return NULL; - } - l2->tm->mgr = mgr; - l2->tm->l2 = l2; - l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM; - l2->tm->tei_m.userdata = l2->tm; - l2->tm->tei_m.printdebug = tei_debug; - l2->tm->tei_m.fsm = &teifsmn; - l2->tm->tei_m.state = ST_TEI_NOP; - l2->tm->tval = 2000; /* T202 2 sec */ - mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer); - write_lock_irqsave(&mgr->lock, flags); - id = get_free_id(mgr); - list_add_tail(&l2->list, &mgr->layer2); - write_unlock_irqrestore(&mgr->lock, flags); - if (id < 0) { - l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); - printk(KERN_WARNING "%s:no free id\n", __func__); - return NULL; - } else { - l2->ch.nr = id; - __add_layer2(&l2->ch, mgr->ch.st); - l2->ch.recv = mgr->ch.recv; - l2->ch.peer = mgr->ch.peer; - l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL); - /* We need open here L1 for the manager as well (refcounting) */ - rq.adr.dev = mgr->ch.st->dev->id; - id = mgr->ch.st->own.ctrl(&mgr->ch.st->own, OPEN_CHANNEL, &rq); - if (id < 0) { - printk(KERN_WARNING "%s: cannot open L1\n", __func__); - l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); - l2 = NULL; - } - } - return l2; -} - -static void -new_tei_req(struct manager *mgr, u_char *dp) -{ - int tei, ri; - struct layer2 *l2; - - ri = dp[0] << 8; - ri += dp[1]; - if (!mgr->up) - goto denied; - if (!(dp[3] & 1)) /* Extension bit != 1 */ - goto denied; - if (dp[3] != 0xff) - tei = dp[3] >> 1; /* 3GPP TS 08.56 6.1.11.2 */ - else - tei = get_free_tei(mgr); - if (tei < 0) { - printk(KERN_WARNING "%s:No free tei\n", __func__); - goto denied; - } - l2 = create_new_tei(mgr, tei, CTRL_SAPI); - if (!l2) - goto denied; - else - mISDN_FsmEvent(&l2->tm->tei_m, EV_ASSIGN_REQ, dp); - return; -denied: - put_tei_msg(mgr, ID_DENIED, ri, GROUP_TEI); -} - -static int -ph_data_ind(struct manager *mgr, struct sk_buff *skb) -{ - int ret = -EINVAL; - struct layer2 *l2, *nl2; - u_char mt; - - if (skb->len < 8) { - if (*debug & DEBUG_L2_TEI) - printk(KERN_DEBUG "%s: short mgr frame %d/8\n", - __func__, skb->len); - goto done; - } - - if ((skb->data[0] >> 2) != TEI_SAPI) /* not for us */ - goto done; - if (skb->data[0] & 1) /* EA0 formal error */ - goto done; - if (!(skb->data[1] & 1)) /* EA1 formal error */ - goto done; - if ((skb->data[1] >> 1) != GROUP_TEI) /* not for us */ - goto done; - if ((skb->data[2] & 0xef) != UI) /* not UI */ - goto done; - if (skb->data[3] != TEI_ENTITY_ID) /* not tei entity */ - goto done; - mt = skb->data[6]; - switch (mt) { - case ID_REQUEST: - case ID_CHK_RES: - case ID_VERIFY: - if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) - goto done; - break; - case ID_ASSIGNED: - case ID_DENIED: - case ID_CHK_REQ: - case ID_REMOVE: - if (test_bit(MGR_OPT_NETWORK, &mgr->options)) - goto done; - break; - default: - goto done; - } - ret = 0; - if (mt == ID_REQUEST) { - new_tei_req(mgr, &skb->data[4]); - goto done; - } - list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { - tei_ph_data_ind(l2->tm, mt, &skb->data[4], skb->len - 4); - } -done: - return ret; -} - -int -l2_tei(struct layer2 *l2, u_int cmd, u_long arg) -{ - struct teimgr *tm = l2->tm; - - if (test_bit(FLG_FIXED_TEI, &l2->flag)) - return 0; - if (*debug & DEBUG_L2_TEI) - printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd); - switch (cmd) { - case MDL_ASSIGN_IND: - mISDN_FsmEvent(&tm->tei_m, EV_IDREQ, NULL); - break; - case MDL_ERROR_IND: - if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) - mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, &l2->tei); - if (test_bit(MGR_OPT_USER, &tm->mgr->options)) - mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, NULL); - break; - case MDL_STATUS_UP_IND: - if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) - mISDN_FsmEvent(&tm->mgr->deact, EV_ACTIVATE, NULL); - break; - case MDL_STATUS_DOWN_IND: - if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) - mISDN_FsmEvent(&tm->mgr->deact, EV_DEACTIVATE, NULL); - break; - case MDL_STATUS_UI_IND: - if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) - mISDN_FsmEvent(&tm->mgr->deact, EV_UI, NULL); - break; - } - return 0; -} - -void -TEIrelease(struct layer2 *l2) -{ - struct teimgr *tm = l2->tm; - u_long flags; - - mISDN_FsmDelTimer(&tm->timer, 1); - write_lock_irqsave(&tm->mgr->lock, flags); - list_del(&l2->list); - write_unlock_irqrestore(&tm->mgr->lock, flags); - l2->tm = NULL; - kfree(tm); -} - -static int -create_teimgr(struct manager *mgr, struct channel_req *crq) -{ - struct layer2 *l2; - unsigned long opt = 0; - unsigned long flags; - int id; - struct channel_req l1rq; - - if (*debug & DEBUG_L2_TEI) - printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev_name(&mgr->ch.st->dev->dev), - crq->protocol, crq->adr.dev, crq->adr.channel, - crq->adr.sapi, crq->adr.tei); - if (crq->adr.tei > GROUP_TEI) - return -EINVAL; - if (crq->adr.tei < 64) - test_and_set_bit(OPTION_L2_FIXEDTEI, &opt); - if (crq->adr.tei == 0) - test_and_set_bit(OPTION_L2_PTP, &opt); - if (test_bit(MGR_OPT_NETWORK, &mgr->options)) { - if (crq->protocol == ISDN_P_LAPD_TE) - return -EPROTONOSUPPORT; - if ((crq->adr.tei != 0) && (crq->adr.tei != 127)) - return -EINVAL; - if (mgr->up) { - printk(KERN_WARNING - "%s: only one network manager is allowed\n", - __func__); - return -EBUSY; - } - } else if (test_bit(MGR_OPT_USER, &mgr->options)) { - if (crq->protocol == ISDN_P_LAPD_NT) - return -EPROTONOSUPPORT; - if ((crq->adr.tei >= 64) && (crq->adr.tei < GROUP_TEI)) - return -EINVAL; /* dyn tei */ - } else { - if (crq->protocol == ISDN_P_LAPD_NT) - test_and_set_bit(MGR_OPT_NETWORK, &mgr->options); - if (crq->protocol == ISDN_P_LAPD_TE) - test_and_set_bit(MGR_OPT_USER, &mgr->options); - } - l1rq.adr = crq->adr; - if (mgr->ch.st->dev->Dprotocols - & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1))) - test_and_set_bit(OPTION_L2_PMX, &opt); - if ((crq->protocol == ISDN_P_LAPD_NT) && (crq->adr.tei == 127)) { - mgr->up = crq->ch; - id = DL_INFO_L2_CONNECT; - teiup_create(mgr, DL_INFORMATION_IND, sizeof(id), &id); - if (test_bit(MGR_PH_ACTIVE, &mgr->options)) - teiup_create(mgr, PH_ACTIVATE_IND, 0, NULL); - crq->ch = NULL; - if (!list_empty(&mgr->layer2)) { - read_lock_irqsave(&mgr->lock, flags); - list_for_each_entry(l2, &mgr->layer2, list) { - l2->up = mgr->up; - l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL); - } - read_unlock_irqrestore(&mgr->lock, flags); - } - return 0; - } - l2 = create_l2(crq->ch, crq->protocol, opt, - crq->adr.tei, crq->adr.sapi); - if (!l2) - return -ENOMEM; - l2->tm = kzalloc_obj(struct teimgr); - if (!l2->tm) { - kfree(l2); - printk(KERN_ERR "kmalloc teimgr failed\n"); - return -ENOMEM; - } - l2->tm->mgr = mgr; - l2->tm->l2 = l2; - l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM; - l2->tm->tei_m.userdata = l2->tm; - l2->tm->tei_m.printdebug = tei_debug; - if (crq->protocol == ISDN_P_LAPD_TE) { - l2->tm->tei_m.fsm = &teifsmu; - l2->tm->tei_m.state = ST_TEI_NOP; - l2->tm->tval = 1000; /* T201 1 sec */ - if (test_bit(OPTION_L2_PMX, &opt)) - l1rq.protocol = ISDN_P_TE_E1; - else - l1rq.protocol = ISDN_P_TE_S0; - } else { - l2->tm->tei_m.fsm = &teifsmn; - l2->tm->tei_m.state = ST_TEI_NOP; - l2->tm->tval = 2000; /* T202 2 sec */ - if (test_bit(OPTION_L2_PMX, &opt)) - l1rq.protocol = ISDN_P_NT_E1; - else - l1rq.protocol = ISDN_P_NT_S0; - } - mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer); - write_lock_irqsave(&mgr->lock, flags); - id = get_free_id(mgr); - list_add_tail(&l2->list, &mgr->layer2); - write_unlock_irqrestore(&mgr->lock, flags); - if (id >= 0) { - l2->ch.nr = id; - l2->up->nr = id; - crq->ch = &l2->ch; - /* We need open here L1 for the manager as well (refcounting) */ - id = mgr->ch.st->own.ctrl(&mgr->ch.st->own, OPEN_CHANNEL, - &l1rq); - } - if (id < 0) - l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); - return id; -} - -static int -mgr_send(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct manager *mgr; - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int ret = -EINVAL; - - mgr = container_of(ch, struct manager, ch); - if (*debug & DEBUG_L2_RECV) - printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", - __func__, hh->prim, hh->id); - switch (hh->prim) { - case PH_DATA_IND: - mISDN_FsmEvent(&mgr->deact, EV_UI, NULL); - ret = ph_data_ind(mgr, skb); - break; - case PH_DATA_CNF: - do_ack(mgr, hh->id); - ret = 0; - break; - case PH_ACTIVATE_IND: - test_and_set_bit(MGR_PH_ACTIVE, &mgr->options); - if (mgr->up) - teiup_create(mgr, PH_ACTIVATE_IND, 0, NULL); - mISDN_FsmEvent(&mgr->deact, EV_ACTIVATE_IND, NULL); - do_send(mgr); - ret = 0; - break; - case PH_DEACTIVATE_IND: - test_and_clear_bit(MGR_PH_ACTIVE, &mgr->options); - if (mgr->up) - teiup_create(mgr, PH_DEACTIVATE_IND, 0, NULL); - mISDN_FsmEvent(&mgr->deact, EV_DEACTIVATE_IND, NULL); - ret = 0; - break; - case DL_UNITDATA_REQ: - return dl_unit_data(mgr, skb); - } - if (!ret) - dev_kfree_skb(skb); - return ret; -} - -static int -free_teimanager(struct manager *mgr) -{ - struct layer2 *l2, *nl2; - - test_and_clear_bit(OPTION_L1_HOLD, &mgr->options); - if (test_bit(MGR_OPT_NETWORK, &mgr->options)) { - /* not locked lock is taken in release tei */ - mgr->up = NULL; - if (test_bit(OPTION_L2_CLEANUP, &mgr->options)) { - list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { - put_tei_msg(mgr, ID_REMOVE, 0, l2->tei); - mutex_lock(&mgr->ch.st->lmutex); - list_del(&l2->ch.list); - mutex_unlock(&mgr->ch.st->lmutex); - l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); - } - test_and_clear_bit(MGR_OPT_NETWORK, &mgr->options); - } else { - list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { - l2->up = NULL; - } - } - } - if (test_bit(MGR_OPT_USER, &mgr->options)) { - if (list_empty(&mgr->layer2)) - test_and_clear_bit(MGR_OPT_USER, &mgr->options); - } - mgr->ch.st->dev->D.ctrl(&mgr->ch.st->dev->D, CLOSE_CHANNEL, NULL); - return 0; -} - -static int -ctrl_teimanager(struct manager *mgr, void *arg) -{ - /* currently we only have one option */ - unsigned int *val = (unsigned int *)arg; - - switch (val[0]) { - case IMCLEAR_L2: - if (val[1]) - test_and_set_bit(OPTION_L2_CLEANUP, &mgr->options); - else - test_and_clear_bit(OPTION_L2_CLEANUP, &mgr->options); - break; - case IMHOLD_L1: - if (val[1]) - test_and_set_bit(OPTION_L1_HOLD, &mgr->options); - else - test_and_clear_bit(OPTION_L1_HOLD, &mgr->options); - break; - default: - return -EINVAL; - } - return 0; -} - -/* This function does create a L2 for fixed TEI in NT Mode */ -static int -check_data(struct manager *mgr, struct sk_buff *skb) -{ - struct mISDNhead *hh = mISDN_HEAD_P(skb); - int ret, tei, sapi; - struct layer2 *l2; - - if (*debug & DEBUG_L2_CTRL) - printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", - __func__, hh->prim, hh->id); - if (test_bit(MGR_OPT_USER, &mgr->options)) - return -ENOTCONN; - if (hh->prim != PH_DATA_IND) - return -ENOTCONN; - if (skb->len != 3) - return -ENOTCONN; - if (skb->data[0] & 3) /* EA0 and CR must be 0 */ - return -EINVAL; - sapi = skb->data[0] >> 2; - if (!(skb->data[1] & 1)) /* invalid EA1 */ - return -EINVAL; - tei = skb->data[1] >> 1; - if (tei > 63) /* not a fixed tei */ - return -ENOTCONN; - if ((skb->data[2] & ~0x10) != SABME) - return -ENOTCONN; - /* We got a SABME for a fixed TEI */ - if (*debug & DEBUG_L2_CTRL) - printk(KERN_DEBUG "%s: SABME sapi(%d) tei(%d)\n", - __func__, sapi, tei); - l2 = create_new_tei(mgr, tei, sapi); - if (!l2) { - if (*debug & DEBUG_L2_CTRL) - printk(KERN_DEBUG "%s: failed to create new tei\n", - __func__); - return -ENOMEM; - } - ret = l2->ch.send(&l2->ch, skb); - return ret; -} - -void -delete_teimanager(struct mISDNchannel *ch) -{ - struct manager *mgr; - struct layer2 *l2, *nl2; - - mgr = container_of(ch, struct manager, ch); - /* not locked lock is taken in release tei */ - list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { - mutex_lock(&mgr->ch.st->lmutex); - list_del(&l2->ch.list); - mutex_unlock(&mgr->ch.st->lmutex); - l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); - } - list_del(&mgr->ch.list); - list_del(&mgr->bcast.list); - skb_queue_purge(&mgr->sendq); - kfree(mgr); -} - -static int -mgr_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - struct manager *mgr; - int ret = -EINVAL; - - mgr = container_of(ch, struct manager, ch); - if (*debug & DEBUG_L2_CTRL) - printk(KERN_DEBUG "%s(%x, %p)\n", __func__, cmd, arg); - switch (cmd) { - case OPEN_CHANNEL: - ret = create_teimgr(mgr, arg); - break; - case CLOSE_CHANNEL: - ret = free_teimanager(mgr); - break; - case CONTROL_CHANNEL: - ret = ctrl_teimanager(mgr, arg); - break; - case CHECK_DATA: - ret = check_data(mgr, arg); - break; - } - return ret; -} - -static int -mgr_bcast(struct mISDNchannel *ch, struct sk_buff *skb) -{ - struct manager *mgr = container_of(ch, struct manager, bcast); - struct mISDNhead *hhc, *hh = mISDN_HEAD_P(skb); - struct sk_buff *cskb = NULL; - struct layer2 *l2; - u_long flags; - int ret; - - read_lock_irqsave(&mgr->lock, flags); - list_for_each_entry(l2, &mgr->layer2, list) { - if ((hh->id & MISDN_ID_SAPI_MASK) == - (l2->ch.addr & MISDN_ID_SAPI_MASK)) { - if (list_is_last(&l2->list, &mgr->layer2)) { - cskb = skb; - skb = NULL; - } else { - if (!cskb) - cskb = skb_copy(skb, GFP_ATOMIC); - } - if (cskb) { - hhc = mISDN_HEAD_P(cskb); - /* save original header behind normal header */ - hhc++; - *hhc = *hh; - hhc--; - hhc->prim = DL_INTERN_MSG; - hhc->id = l2->ch.nr; - ret = ch->st->own.recv(&ch->st->own, cskb); - if (ret) { - if (*debug & DEBUG_SEND_ERR) - printk(KERN_DEBUG - "%s ch%d prim(%x) addr(%x)" - " err %d\n", - __func__, l2->ch.nr, - hh->prim, l2->ch.addr, ret); - } else - cskb = NULL; - } else { - printk(KERN_WARNING "%s ch%d addr %x no mem\n", - __func__, ch->nr, ch->addr); - goto out; - } - } - } -out: - read_unlock_irqrestore(&mgr->lock, flags); - dev_kfree_skb(cskb); - dev_kfree_skb(skb); - return 0; -} - -static int -mgr_bcast_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) -{ - - return -EINVAL; -} - -int -create_teimanager(struct mISDNdevice *dev) -{ - struct manager *mgr; - - mgr = kzalloc_obj(struct manager); - if (!mgr) - return -ENOMEM; - INIT_LIST_HEAD(&mgr->layer2); - rwlock_init(&mgr->lock); - skb_queue_head_init(&mgr->sendq); - mgr->nextid = 1; - mgr->lastid = MISDN_ID_NONE; - mgr->ch.send = mgr_send; - mgr->ch.ctrl = mgr_ctrl; - mgr->ch.st = dev->D.st; - set_channel_address(&mgr->ch, TEI_SAPI, GROUP_TEI); - add_layer2(&mgr->ch, dev->D.st); - mgr->bcast.send = mgr_bcast; - mgr->bcast.ctrl = mgr_bcast_ctrl; - mgr->bcast.st = dev->D.st; - set_channel_address(&mgr->bcast, 0, GROUP_TEI); - add_layer2(&mgr->bcast, dev->D.st); - mgr->deact.debug = *debug & DEBUG_MANAGER; - mgr->deact.userdata = mgr; - mgr->deact.printdebug = da_debug; - mgr->deact.fsm = &deactfsm; - mgr->deact.state = ST_L1_DEACT; - mISDN_FsmInitTimer(&mgr->deact, &mgr->datimer); - dev->teimgr = &mgr->ch; - return 0; -} - -int TEIInit(u_int *deb) -{ - int res; - debug = deb; - teifsmu.state_count = TEI_STATE_COUNT; - teifsmu.event_count = TEI_EVENT_COUNT; - teifsmu.strEvent = strTeiEvent; - teifsmu.strState = strTeiState; - res = mISDN_FsmNew(&teifsmu, TeiFnListUser, ARRAY_SIZE(TeiFnListUser)); - if (res) - goto error; - teifsmn.state_count = TEI_STATE_COUNT; - teifsmn.event_count = TEI_EVENT_COUNT; - teifsmn.strEvent = strTeiEvent; - teifsmn.strState = strTeiState; - res = mISDN_FsmNew(&teifsmn, TeiFnListNet, ARRAY_SIZE(TeiFnListNet)); - if (res) - goto error_smn; - deactfsm.state_count = DEACT_STATE_COUNT; - deactfsm.event_count = DEACT_EVENT_COUNT; - deactfsm.strEvent = strDeactEvent; - deactfsm.strState = strDeactState; - res = mISDN_FsmNew(&deactfsm, DeactFnList, ARRAY_SIZE(DeactFnList)); - if (res) - goto error_deact; - return 0; - -error_deact: - mISDN_FsmFree(&teifsmn); -error_smn: - mISDN_FsmFree(&teifsmu); -error: - return res; -} - -void TEIFree(void) -{ - mISDN_FsmFree(&teifsmu); - mISDN_FsmFree(&teifsmn); - mISDN_FsmFree(&deactfsm); -} diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c deleted file mode 100644 index a18845755633..000000000000 --- a/drivers/isdn/mISDN/timerdev.c +++ /dev/null @@ -1,295 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * general timer device for using in ISDN stacks - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "core.h" - -static DEFINE_MUTEX(mISDN_mutex); -static u_int *debug; - - -struct mISDNtimerdev { - int next_id; - struct list_head pending; - struct list_head expired; - wait_queue_head_t wait; - u_int work; - spinlock_t lock; /* protect lists */ -}; - -struct mISDNtimer { - struct list_head list; - struct mISDNtimerdev *dev; - struct timer_list tl; - int id; -}; - -static int -mISDN_open(struct inode *ino, struct file *filep) -{ - struct mISDNtimerdev *dev; - - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); - dev = kmalloc_obj(struct mISDNtimerdev); - if (!dev) - return -ENOMEM; - dev->next_id = 1; - INIT_LIST_HEAD(&dev->pending); - INIT_LIST_HEAD(&dev->expired); - spin_lock_init(&dev->lock); - dev->work = 0; - init_waitqueue_head(&dev->wait); - filep->private_data = dev; - return nonseekable_open(ino, filep); -} - -static int -mISDN_close(struct inode *ino, struct file *filep) -{ - struct mISDNtimerdev *dev = filep->private_data; - struct list_head *list = &dev->pending; - struct mISDNtimer *timer, *next; - - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); - - spin_lock_irq(&dev->lock); - while (!list_empty(list)) { - timer = list_first_entry(list, struct mISDNtimer, list); - spin_unlock_irq(&dev->lock); - timer_shutdown_sync(&timer->tl); - spin_lock_irq(&dev->lock); - /* it might have been moved to ->expired */ - list_del(&timer->list); - kfree(timer); - } - spin_unlock_irq(&dev->lock); - - list_for_each_entry_safe(timer, next, &dev->expired, list) { - kfree(timer); - } - kfree(dev); - return 0; -} - -static ssize_t -mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off) -{ - struct mISDNtimerdev *dev = filep->private_data; - struct list_head *list = &dev->expired; - struct mISDNtimer *timer; - int ret = 0; - - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__, - filep, buf, (int)count, off); - - if (count < sizeof(int)) - return -ENOSPC; - - spin_lock_irq(&dev->lock); - while (list_empty(list) && (dev->work == 0)) { - spin_unlock_irq(&dev->lock); - if (filep->f_flags & O_NONBLOCK) - return -EAGAIN; - wait_event_interruptible(dev->wait, (READ_ONCE(dev->work) || - !list_empty(list))); - if (signal_pending(current)) - return -ERESTARTSYS; - spin_lock_irq(&dev->lock); - } - if (dev->work) - WRITE_ONCE(dev->work, 0); - if (!list_empty(list)) { - timer = list_first_entry(list, struct mISDNtimer, list); - list_del(&timer->list); - spin_unlock_irq(&dev->lock); - if (put_user(timer->id, (int __user *)buf)) - ret = -EFAULT; - else - ret = sizeof(int); - kfree(timer); - } else { - spin_unlock_irq(&dev->lock); - } - return ret; -} - -static __poll_t -mISDN_poll(struct file *filep, poll_table *wait) -{ - struct mISDNtimerdev *dev = filep->private_data; - __poll_t mask = EPOLLERR; - - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait); - if (dev) { - u32 work; - - poll_wait(filep, &dev->wait, wait); - mask = 0; - work = READ_ONCE(dev->work); - if (work || !list_empty(&dev->expired)) - mask |= (EPOLLIN | EPOLLRDNORM); - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__, - work, list_empty(&dev->expired)); - } - return mask; -} - -static void -dev_expire_timer(struct timer_list *t) -{ - struct mISDNtimer *timer = timer_container_of(timer, t, tl); - u_long flags; - - spin_lock_irqsave(&timer->dev->lock, flags); - if (timer->id >= 0) - list_move_tail(&timer->list, &timer->dev->expired); - wake_up_interruptible(&timer->dev->wait); - spin_unlock_irqrestore(&timer->dev->lock, flags); -} - -static int -misdn_add_timer(struct mISDNtimerdev *dev, int timeout) -{ - int id; - struct mISDNtimer *timer; - - if (!timeout) { - WRITE_ONCE(dev->work, 1); - wake_up_interruptible(&dev->wait); - id = 0; - } else { - timer = kzalloc_obj(struct mISDNtimer); - if (!timer) - return -ENOMEM; - timer->dev = dev; - timer_setup(&timer->tl, dev_expire_timer, 0); - spin_lock_irq(&dev->lock); - id = timer->id = dev->next_id++; - if (dev->next_id < 0) - dev->next_id = 1; - list_add_tail(&timer->list, &dev->pending); - timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000); - add_timer(&timer->tl); - spin_unlock_irq(&dev->lock); - } - return id; -} - -static int -misdn_del_timer(struct mISDNtimerdev *dev, int id) -{ - struct mISDNtimer *timer; - - spin_lock_irq(&dev->lock); - list_for_each_entry(timer, &dev->pending, list) { - if (timer->id == id) { - list_del_init(&timer->list); - timer->id = -1; - spin_unlock_irq(&dev->lock); - timer_shutdown_sync(&timer->tl); - kfree(timer); - return id; - } - } - spin_unlock_irq(&dev->lock); - return 0; -} - -static long -mISDN_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - struct mISDNtimerdev *dev = filep->private_data; - int id, tout, ret = 0; - - - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__, - filep, cmd, arg); - mutex_lock(&mISDN_mutex); - switch (cmd) { - case IMADDTIMER: - if (get_user(tout, (int __user *)arg)) { - ret = -EFAULT; - break; - } - id = misdn_add_timer(dev, tout); - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s add %d id %d\n", __func__, - tout, id); - if (id < 0) { - ret = id; - break; - } - if (put_user(id, (int __user *)arg)) - ret = -EFAULT; - break; - case IMDELTIMER: - if (get_user(id, (int __user *)arg)) { - ret = -EFAULT; - break; - } - if (*debug & DEBUG_TIMER) - printk(KERN_DEBUG "%s del id %d\n", __func__, id); - id = misdn_del_timer(dev, id); - if (put_user(id, (int __user *)arg)) - ret = -EFAULT; - break; - default: - ret = -EINVAL; - } - mutex_unlock(&mISDN_mutex); - return ret; -} - -static const struct file_operations mISDN_fops = { - .owner = THIS_MODULE, - .read = mISDN_read, - .poll = mISDN_poll, - .unlocked_ioctl = mISDN_ioctl, - .open = mISDN_open, - .release = mISDN_close, -}; - -static struct miscdevice mISDNtimer = { - .minor = MISC_DYNAMIC_MINOR, - .name = "mISDNtimer", - .fops = &mISDN_fops, -}; - -int -mISDN_inittimer(u_int *deb) -{ - int err; - - debug = deb; - err = misc_register(&mISDNtimer); - if (err) - printk(KERN_WARNING "mISDN: Could not register timer device\n"); - return err; -} - -void mISDN_timer_cleanup(void) -{ - misc_deregister(&mISDNtimer); -} diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 597d7a79c988..f4a0a3c8c870 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -765,7 +765,6 @@ config LEDS_NETXBIG tristate "LED support for Big Network series LEDs" depends on LEDS_CLASS depends on MACH_KIRKWOOD || COMPILE_TEST - depends on OF_GPIO default MACH_KIRKWOOD help This option enables support for LEDs found on the LaCie 2Big diff --git a/drivers/leds/blink/leds-lgm-sso.c b/drivers/leds/blink/leds-lgm-sso.c index 8923d2df4704..0f27c68e2741 100644 --- a/drivers/leds/blink/leds-lgm-sso.c +++ b/drivers/leds/blink/leds-lgm-sso.c @@ -25,7 +25,7 @@ #define LED_BLINK_H8_0 0x0 #define LED_BLINK_H8_1 0x4 #define GET_FREQ_OFFSET(pin, src) (((pin) * 6) + ((src) * 2)) -#define GET_SRC_OFFSET(pinc) (((pin) * 6) + 4) +#define GET_SRC_OFFSET(pin) (((pin) * 6) + 4) #define DUTY_CYCLE(x) (0x8 + ((x) * 4)) #define SSO_CON0 0x2B0 @@ -808,8 +808,6 @@ static int intel_sso_led_probe(struct platform_device *pdev) priv->fpid_clkrate = clk_get_rate(priv->clocks[1].clk); - priv->mmap = syscon_node_to_regmap(dev->of_node); - priv->mmap = syscon_node_to_regmap(dev->of_node); if (IS_ERR(priv->mmap)) { dev_err(dev, "Failed to map iomem!\n"); diff --git a/drivers/leds/flash/leds-ktd2692.c b/drivers/leds/flash/leds-ktd2692.c index 0f16eefcfe4c..22fbfccd4873 100644 --- a/drivers/leds/flash/leds-ktd2692.c +++ b/drivers/leds/flash/leds-ktd2692.c @@ -59,7 +59,7 @@ struct ktd2692_led_config_data { enum led_brightness max_brightness; }; -const struct expresswire_timing ktd2692_timing = { +static const struct expresswire_timing ktd2692_timing = { .poweroff_us = 700, .data_start_us = 10, .end_of_data_low_us = 10, diff --git a/drivers/leds/led-class-multicolor.c b/drivers/leds/led-class-multicolor.c index fd66d2bdeace..6b671f3f9c61 100644 --- a/drivers/leds/led-class-multicolor.c +++ b/drivers/leds/led-class-multicolor.c @@ -34,14 +34,14 @@ static ssize_t multi_intensity_store(struct device *dev, struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev); int nrchars, offset = 0; - int intensity_value[LED_COLOR_ID_MAX]; + unsigned int intensity_value[LED_COLOR_ID_MAX]; int i; ssize_t ret; mutex_lock(&led_cdev->led_access); for (i = 0; i < mcled_cdev->num_colors; i++) { - ret = sscanf(buf + offset, "%i%n", + ret = sscanf(buf + offset, "%u%n", &intensity_value[i], &nrchars); if (ret != 1) { ret = -EINVAL; diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index d34a19453560..9e14ae588f78 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -273,7 +273,7 @@ static struct led_classdev *of_led_get(struct device_node *np, int index, if (!led_node) return ERR_PTR(-ENOENT); - led_dev = class_find_device_by_of_node(&leds_class, led_node); + led_dev = class_find_device_by_fwnode(&leds_class, of_fwnode_handle(led_node)); of_node_put(led_node); return led_module_get(led_dev); @@ -421,6 +421,9 @@ EXPORT_SYMBOL_GPL(led_add_lookup); */ void led_remove_lookup(struct led_lookup_data *led_lookup) { + if (!led_lookup) + return; + mutex_lock(&leds_lookup_lock); list_del(&led_lookup->list); mutex_unlock(&leds_lookup_lock); diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 59473f286b31..385e78af1dac 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -85,7 +85,7 @@ static void led_timer_function(struct timer_list *t) if (!brightness) { /* Time to switch the LED on. */ if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, - &led_cdev->work_flags)) + &led_cdev->work_flags)) brightness = led_cdev->new_blink_brightness; else brightness = led_cdev->blink_brightness; @@ -217,10 +217,9 @@ static void led_set_software_blink(struct led_classdev *led_cdev, mod_timer(&led_cdev->blink_timer, jiffies + 1); } - static void led_blink_setup(struct led_classdev *led_cdev, - unsigned long *delay_on, - unsigned long *delay_off) + unsigned long *delay_on, + unsigned long *delay_off) { if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) && led_cdev->blink_set && @@ -262,7 +261,7 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev, int invert) { if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) && - timer_pending(&led_cdev->blink_timer)) + timer_pending(&led_cdev->blink_timer)) return; set_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags); @@ -347,9 +346,9 @@ void led_set_brightness_nopm(struct led_classdev *led_cdev, unsigned int value) /* Ensure delayed_set_value is seen before work_flags modification */ smp_mb__before_atomic(); - if (value) + if (value) { set_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags); - else { + } else { clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags); clear_bit(LED_SET_BLINK, &led_cdev->work_flags); set_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags); @@ -499,7 +498,6 @@ static void led_parse_fwnode_props(struct device *dev, props->color_present = true; } - if (!fwnode_property_present(fwnode, "function")) return; @@ -581,8 +579,12 @@ int led_compose_name(struct device *dev, struct led_init_data *init_data, } else if (is_of_node(fwnode)) { n = snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s", to_of_node(fwnode)->name); - } else + } else if (is_software_node(fwnode)) { + n = snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s", + fwnode_get_name(fwnode)); + } else { return -EINVAL; + } if (n >= LED_MAX_NAME_SIZE) return -E2BIG; diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c index 61629d5d6703..36246267b096 100644 --- a/drivers/leds/leds-lm3642.c +++ b/drivers/leds/leds-lm3642.c @@ -3,15 +3,16 @@ * Simple driver for Texas Instruments LM3642 LED Flash driver chip * Copyright (C) 2012 Texas Instruments */ -#include +#include #include +#include #include #include -#include -#include -#include -#include +#include #include +#include +#include +#include #define REG_FILT_TIME (0x0) #define REG_IVFM_MODE (0x1) @@ -202,10 +203,9 @@ static int lm3642_torch_brightness_set(struct led_classdev *cdev, container_of(cdev, struct lm3642_chip_data, cdev_torch); int ret; - mutex_lock(&chip->lock); + guard(mutex)(&chip->lock); chip->br_torch = brightness; ret = lm3642_control(chip, chip->br_torch, MODES_TORCH); - mutex_unlock(&chip->lock); return ret; } @@ -249,10 +249,9 @@ static int lm3642_strobe_brightness_set(struct led_classdev *cdev, container_of(cdev, struct lm3642_chip_data, cdev_flash); int ret; - mutex_lock(&chip->lock); + guard(mutex)(&chip->lock); chip->br_flash = brightness; ret = lm3642_control(chip, chip->br_flash, MODES_FLASH); - mutex_unlock(&chip->lock); return ret; } @@ -264,10 +263,9 @@ static int lm3642_indicator_brightness_set(struct led_classdev *cdev, container_of(cdev, struct lm3642_chip_data, cdev_indicator); int ret; - mutex_lock(&chip->lock); + guard(mutex)(&chip->lock); chip->br_indicator = brightness; ret = lm3642_control(chip, chip->br_indicator, MODES_INDIC); - mutex_unlock(&chip->lock); return ret; } diff --git a/drivers/leds/leds-lp5569.c b/drivers/leds/leds-lp5569.c index 786f2aa35319..a252ba6c455d 100644 --- a/drivers/leds/leds-lp5569.c +++ b/drivers/leds/leds-lp5569.c @@ -410,12 +410,12 @@ static ssize_t lp5569_selftest(struct device *dev, /* Test LED Open */ pos = lp5569_led_open_test(led, buf); if (pos < 0) - return sprintf(buf, "FAIL\n"); + return sysfs_emit(buf, "FAIL\n"); /* Test LED Shorted */ pos += lp5569_led_short_test(led, buf); if (pos < 0) - return sprintf(buf, "FAIL\n"); + return sysfs_emit(buf, "FAIL\n"); for (i = 0; i < chip->pdata->num_channels; i++) { /* Restore current */ diff --git a/drivers/leds/leds-lp8860.c b/drivers/leds/leds-lp8860.c index 0962c00c215a..7a436861c4b7 100644 --- a/drivers/leds/leds-lp8860.c +++ b/drivers/leds/leds-lp8860.c @@ -89,16 +89,24 @@ * @client: Pointer to the I2C client * @led_dev: led class device pointer * @regmap: Devices register map - * @eeprom_regmap: EEPROM register map */ struct lp8860_led { struct mutex lock; struct i2c_client *client; struct led_classdev led_dev; struct regmap *regmap; - struct regmap *eeprom_regmap; }; +static bool program_eeprom; +module_param(program_eeprom, bool, 0644); +MODULE_PARM_DESC(program_eeprom, "Program the configuration EEPROM on device startup"); + +/* + * EEPROM bits are intended to be set/programmed before normal operation only + * once during silicon production, but can be reprogrammed for evaluation purposes + * up to 1000 cycles. To program this EEPROM using this driver, update the below + * table and set the module param "program_eeprom" to 1 + */ static const struct reg_sequence lp8860_eeprom_disp_regs[] = { { LP8860_EEPROM_REG_0, 0xed }, { LP8860_EEPROM_REG_1, 0xdf }, @@ -127,32 +135,6 @@ static const struct reg_sequence lp8860_eeprom_disp_regs[] = { { LP8860_EEPROM_REG_24, 0x3E }, }; -static int lp8860_unlock_eeprom(struct lp8860_led *led) -{ - int ret; - - guard(mutex)(&led->lock); - - ret = regmap_write(led->regmap, LP8860_EEPROM_UNLOCK, LP8860_EEPROM_CODE_1); - if (ret) { - dev_err(&led->client->dev, "EEPROM Unlock failed\n"); - return ret; - } - - ret = regmap_write(led->regmap, LP8860_EEPROM_UNLOCK, LP8860_EEPROM_CODE_2); - if (ret) { - dev_err(&led->client->dev, "EEPROM Unlock failed\n"); - return ret; - } - ret = regmap_write(led->regmap, LP8860_EEPROM_UNLOCK, LP8860_EEPROM_CODE_3); - if (ret) { - dev_err(&led->client->dev, "EEPROM Unlock failed\n"); - return ret; - } - - return ret; -} - static int lp8860_fault_check(struct lp8860_led *led) { int ret, fault; @@ -211,61 +193,72 @@ static int lp8860_brightness_set(struct led_classdev *led_cdev, return 0; } -static int lp8860_init(struct lp8860_led *led) +static int lp8860_program_eeprom(struct lp8860_led *led) { - unsigned int read_buf; int ret, reg_count; + guard(mutex)(&led->lock); + ret = lp8860_fault_check(led); - if (ret) - goto out; - - ret = regmap_read(led->regmap, LP8860_STATUS, &read_buf); - if (ret) - goto out; - - ret = lp8860_unlock_eeprom(led); if (ret) { - dev_err(&led->client->dev, "Failed unlocking EEPROM\n"); - goto out; + dev_err(&led->client->dev, "Cannot read/clear faults\n"); + return ret; + } + + ret = regmap_write(led->regmap, LP8860_EEPROM_UNLOCK, LP8860_EEPROM_CODE_1); + if (ret) { + dev_err(&led->client->dev, "EEPROM Unlock failed\n"); + return ret; + } + + ret = regmap_write(led->regmap, LP8860_EEPROM_UNLOCK, LP8860_EEPROM_CODE_2); + if (ret) { + dev_err(&led->client->dev, "EEPROM Unlock failed\n"); + return ret; + } + ret = regmap_write(led->regmap, LP8860_EEPROM_UNLOCK, LP8860_EEPROM_CODE_3); + if (ret) { + dev_err(&led->client->dev, "EEPROM Unlock failed\n"); + return ret; } reg_count = ARRAY_SIZE(lp8860_eeprom_disp_regs); - ret = regmap_multi_reg_write(led->eeprom_regmap, lp8860_eeprom_disp_regs, reg_count); + ret = regmap_multi_reg_write(led->regmap, lp8860_eeprom_disp_regs, reg_count); if (ret) { dev_err(&led->client->dev, "Failed writing EEPROM\n"); - goto out; + return ret; } ret = regmap_write(led->regmap, LP8860_EEPROM_UNLOCK, LP8860_LOCK_EEPROM); if (ret) - goto out; + return ret; ret = regmap_write(led->regmap, LP8860_EEPROM_CNTRL, LP8860_PROGRAM_EEPROM); if (ret) { dev_err(&led->client->dev, "Failed programming EEPROM\n"); - goto out; + return ret; } - return ret; - -out: - return ret; + return 0; } +static const struct regmap_range lp8860_reg_ranges[] = { + regmap_reg_range(LP8860_DISP_CL1_BRT_MSB, LP8860_EEPROM_UNLOCK), + regmap_reg_range(LP8860_EEPROM_REG_0, LP8860_EEPROM_REG_24), +}; + +static const struct regmap_access_table lp8860_reg_table = { + .yes_ranges = lp8860_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(lp8860_reg_ranges), +}; + static const struct regmap_config lp8860_regmap_config = { .reg_bits = 8, .val_bits = 8, - - .max_register = LP8860_EEPROM_UNLOCK, -}; - -static const struct regmap_config lp8860_eeprom_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - + .rd_table = &lp8860_reg_table, + .wr_table = &lp8860_reg_table, .max_register = LP8860_EEPROM_REG_24, }; @@ -319,18 +312,12 @@ static int lp8860_probe(struct i2c_client *client) return ret; } - led->eeprom_regmap = devm_regmap_init_i2c(client, &lp8860_eeprom_regmap_config); - if (IS_ERR(led->eeprom_regmap)) { - ret = PTR_ERR(led->eeprom_regmap); - dev_err(&client->dev, "Failed to allocate register map: %d\n", - ret); - return ret; + if (program_eeprom) { + ret = lp8860_program_eeprom(led); + if (ret) + return ret; } - ret = lp8860_init(led); - if (ret) - return ret; - init_data.fwnode = of_fwnode_handle(child_node); init_data.devicename = LP8860_NAME; init_data.default_label = ":display_cluster"; diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index 016bf468e094..f6061c47f863 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -1273,7 +1273,12 @@ static int lpg_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, return ret; if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) { - refclk = lpg_clk_rates_hi_res[FIELD_GET(PWM_CLK_SELECT_HI_RES_MASK, val)]; + unsigned int clk_idx = FIELD_GET(PWM_CLK_SELECT_HI_RES_MASK, val); + + if (clk_idx >= ARRAY_SIZE(lpg_clk_rates_hi_res)) + return -EINVAL; + + refclk = lpg_clk_rates_hi_res[clk_idx]; resolution = lpg_pwm_resolution_hi_res[FIELD_GET(PWM_SIZE_HI_RES_MASK, val)]; } else { refclk = lpg_clk_rates[FIELD_GET(PWM_CLK_SELECT_MASK, val)]; diff --git a/drivers/leds/trigger/ledtrig-tty.c b/drivers/leds/trigger/ledtrig-tty.c index 8eb6286b33ac..3725571144d9 100644 --- a/drivers/leds/trigger/ledtrig-tty.c +++ b/drivers/leds/trigger/ledtrig-tty.c @@ -220,7 +220,7 @@ static void ledtrig_tty_work(struct work_struct *work) goto out; tty = tty_kopen_shared(devno); - if (IS_ERR(tty) || !tty) + if (IS_ERR_OR_NULL(tty)) /* What to do? retry or abort */ goto out; diff --git a/drivers/mailbox/cix-mailbox.c b/drivers/mailbox/cix-mailbox.c index 443620e8ae37..43c76cdab24a 100644 --- a/drivers/mailbox/cix-mailbox.c +++ b/drivers/mailbox/cix-mailbox.c @@ -12,8 +12,6 @@ #include #include -#include "mailbox.h" - /* * The maximum transmission size is 32 words or 128 bytes. */ @@ -405,7 +403,7 @@ static int cix_mbox_startup(struct mbox_chan *chan) int index = cp->index, ret; u32 val; - ret = request_irq(priv->irq, cix_mbox_isr, 0, + ret = request_irq(priv->irq, cix_mbox_isr, IRQF_NO_SUSPEND, dev_name(priv->dev), chan); if (ret) { dev_err(priv->dev, "Unable to acquire IRQ %d\n", priv->irq); @@ -415,7 +413,7 @@ static int cix_mbox_startup(struct mbox_chan *chan) switch (cp->type) { case CIX_MBOX_TYPE_DB: /* Overwrite txdone_method for DB channel */ - chan->txdone_method = TXDONE_BY_ACK; + chan->txdone_method = MBOX_TXDONE_BY_ACK; fallthrough; case CIX_MBOX_TYPE_REG: if (priv->dir == CIX_MBOX_TX) { diff --git a/drivers/mailbox/exynos-mailbox.c b/drivers/mailbox/exynos-mailbox.c index 5f2d3b81c1db..d2355b128ba4 100644 --- a/drivers/mailbox/exynos-mailbox.c +++ b/drivers/mailbox/exynos-mailbox.c @@ -99,7 +99,6 @@ static int exynos_mbox_probe(struct platform_device *pdev) struct mbox_controller *mbox; struct mbox_chan *chans; struct clk *pclk; - int i; exynos_mbox = devm_kzalloc(dev, sizeof(*exynos_mbox), GFP_KERNEL); if (!exynos_mbox) @@ -129,9 +128,6 @@ static int exynos_mbox_probe(struct platform_device *pdev) mbox->ops = &exynos_mbox_chan_ops; mbox->of_xlate = exynos_mbox_of_xlate; - for (i = 0; i < EXYNOS_MBOX_CHAN_COUNT; i++) - chans[i].mbox = mbox; - exynos_mbox->mbox = mbox; platform_set_drvdata(pdev, exynos_mbox); diff --git a/drivers/mailbox/hi3660-mailbox.c b/drivers/mailbox/hi3660-mailbox.c index 17c29e960fbf..9b727a2b54a5 100644 --- a/drivers/mailbox/hi3660-mailbox.c +++ b/drivers/mailbox/hi3660-mailbox.c @@ -15,8 +15,6 @@ #include #include -#include "mailbox.h" - #define MBOX_CHAN_MAX 32 #define MBOX_RX 0x0 diff --git a/drivers/mailbox/hi6220-mailbox.c b/drivers/mailbox/hi6220-mailbox.c index f77741ce42e7..69d15b6283e9 100644 --- a/drivers/mailbox/hi6220-mailbox.c +++ b/drivers/mailbox/hi6220-mailbox.c @@ -79,12 +79,12 @@ struct hi6220_mbox { /* region for mailbox */ void __iomem *base; - unsigned int chan_num; - struct hi6220_mbox_chan *mchan; - void *irq_map_chan[MBOX_CHAN_MAX]; struct mbox_chan *chan; struct mbox_controller controller; + + unsigned int chan_num; + struct hi6220_mbox_chan mchan[] __counted_by(chan_num); }; static void mbox_set_state(struct hi6220_mbox *mbox, @@ -267,16 +267,12 @@ static int hi6220_mbox_probe(struct platform_device *pdev) struct hi6220_mbox *mbox; int i, err; - mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + mbox = devm_kzalloc(dev, struct_size(mbox, mchan, MBOX_CHAN_MAX), GFP_KERNEL); if (!mbox) return -ENOMEM; - mbox->dev = dev; mbox->chan_num = MBOX_CHAN_MAX; - mbox->mchan = devm_kcalloc(dev, - mbox->chan_num, sizeof(*mbox->mchan), GFP_KERNEL); - if (!mbox->mchan) - return -ENOMEM; + mbox->dev = dev; mbox->chan = devm_kcalloc(dev, mbox->chan_num, sizeof(*mbox->chan), GFP_KERNEL); diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 003f9236c35e..246a9a9e3952 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -23,8 +23,6 @@ #include #include -#include "mailbox.h" - #define IMX_MU_CHANS 24 /* TX0/RX0/RXDB[0-3] */ #define IMX_MU_SCU_CHANS 6 @@ -734,7 +732,7 @@ static struct mbox_chan * imx_mu_xlate(struct mbox_controller *mbox, p_chan = &mbox->chans[chan]; if (type == IMX_MU_TYPE_TXDB_V2) - p_chan->txdone_method = TXDONE_BY_ACK; + p_chan->txdone_method = MBOX_TXDONE_BY_ACK; return p_chan; } diff --git a/drivers/mailbox/mailbox-sti.c b/drivers/mailbox/mailbox-sti.c index b4b5bdd503cf..b6c9ecbbc8ec 100644 --- a/drivers/mailbox/mailbox-sti.c +++ b/drivers/mailbox/mailbox-sti.c @@ -21,8 +21,6 @@ #include #include -#include "mailbox.h" - #define STI_MBOX_INST_MAX 4 /* RAM saving: Max supported instances */ #define STI_MBOX_CHAN_MAX 20 /* RAM saving: Max supported channels */ diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index 3a28ab5c42e5..7b6ef033e77a 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -28,8 +28,6 @@ #define MBOX_HEXDUMP_MAX_LEN (MBOX_HEXDUMP_LINE_LEN * \ (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE)) -static bool mbox_data_ready; - struct mbox_test_device { struct device *dev; void __iomem *tx_mmio; @@ -42,6 +40,7 @@ struct mbox_test_device { spinlock_t lock; struct mutex mutex; wait_queue_head_t waitq; + bool data_ready; struct fasync_struct *async_queue; struct dentry *root_debugfs_dir; }; @@ -162,7 +161,7 @@ static bool mbox_test_message_data_ready(struct mbox_test_device *tdev) unsigned long flags; spin_lock_irqsave(&tdev->lock, flags); - data_ready = mbox_data_ready; + data_ready = tdev->data_ready; spin_unlock_irqrestore(&tdev->lock, flags); return data_ready; @@ -227,7 +226,7 @@ static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf, *(touser + l) = '\0'; memset(tdev->rx_buffer, 0, MBOX_MAX_MSG_LEN); - mbox_data_ready = false; + tdev->data_ready = false; spin_unlock_irqrestore(&tdev->lock, flags); @@ -297,7 +296,7 @@ static void mbox_test_receive_message(struct mbox_client *client, void *message) message, MBOX_MAX_MSG_LEN); memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN); } - mbox_data_ready = true; + tdev->data_ready = true; spin_unlock_irqrestore(&tdev->lock, flags); wake_up_interruptible(&tdev->waitq); @@ -336,7 +335,7 @@ mbox_test_request_channel(struct platform_device *pdev, const char *name) client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL); if (!client) - return ERR_PTR(-ENOMEM); + return NULL; client->dev = &pdev->dev; client->rx_callback = mbox_test_receive_message; @@ -355,67 +354,80 @@ mbox_test_request_channel(struct platform_device *pdev, const char *name) return channel; } +static void __iomem *mbox_test_ioremap(struct platform_device *pdev, unsigned int res_num) +{ + struct resource *res; + void __iomem *mmio; + + res = platform_get_resource(pdev, IORESOURCE_MEM, res_num); + if (!res) + return NULL; + + mmio = devm_ioremap_resource(&pdev->dev, res); + if (PTR_ERR(mmio) == -EBUSY) { + dev_info(&pdev->dev, "trying workaround with plain ioremap\n"); + return devm_ioremap(&pdev->dev, res->start, resource_size(res)); + } + + return IS_ERR(mmio) ? NULL : mmio; +} + static int mbox_test_probe(struct platform_device *pdev) { struct mbox_test_device *tdev; - struct resource *res; - resource_size_t size; int ret; tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL); if (!tdev) return -ENOMEM; + tdev->dev = &pdev->dev; + spin_lock_init(&tdev->lock); + mutex_init(&tdev->mutex); + init_waitqueue_head(&tdev->waitq); + platform_set_drvdata(pdev, tdev); + /* It's okay for MMIO to be NULL */ - tdev->tx_mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (PTR_ERR(tdev->tx_mmio) == -EBUSY) { - /* if reserved area in SRAM, try just ioremap */ - size = resource_size(res); - tdev->tx_mmio = devm_ioremap(&pdev->dev, res->start, size); - } else if (IS_ERR(tdev->tx_mmio)) { - tdev->tx_mmio = NULL; - } + tdev->tx_mmio = mbox_test_ioremap(pdev, 0); /* If specified, second reg entry is Rx MMIO */ - tdev->rx_mmio = devm_platform_get_and_ioremap_resource(pdev, 1, &res); - if (PTR_ERR(tdev->rx_mmio) == -EBUSY) { - size = resource_size(res); - tdev->rx_mmio = devm_ioremap(&pdev->dev, res->start, size); - } else if (IS_ERR(tdev->rx_mmio)) { + tdev->rx_mmio = mbox_test_ioremap(pdev, 1); + if (!tdev->rx_mmio) tdev->rx_mmio = tdev->tx_mmio; - } tdev->tx_channel = mbox_test_request_channel(pdev, "tx"); tdev->rx_channel = mbox_test_request_channel(pdev, "rx"); - if (IS_ERR_OR_NULL(tdev->tx_channel) && IS_ERR_OR_NULL(tdev->rx_channel)) + if (!tdev->tx_channel && !tdev->rx_channel) return -EPROBE_DEFER; /* If Rx is not specified but has Rx MMIO, then Rx = Tx */ if (!tdev->rx_channel && (tdev->rx_mmio != tdev->tx_mmio)) tdev->rx_channel = tdev->tx_channel; - tdev->dev = &pdev->dev; - platform_set_drvdata(pdev, tdev); - - spin_lock_init(&tdev->lock); - mutex_init(&tdev->mutex); - if (tdev->rx_channel) { tdev->rx_buffer = devm_kzalloc(&pdev->dev, MBOX_MAX_MSG_LEN, GFP_KERNEL); - if (!tdev->rx_buffer) - return -ENOMEM; + if (!tdev->rx_buffer) { + ret = -ENOMEM; + goto err_free_chans; + } } ret = mbox_test_add_debugfs(pdev, tdev); if (ret) - return ret; + goto err_free_chans; - init_waitqueue_head(&tdev->waitq); dev_info(&pdev->dev, "Successfully registered\n"); return 0; + +err_free_chans: + if (tdev->tx_channel) + mbox_free_channel(tdev->tx_channel); + if (tdev->rx_channel && tdev->rx_channel != tdev->tx_channel) + mbox_free_channel(tdev->rx_channel); + return ret; } static void mbox_test_remove(struct platform_device *pdev) @@ -426,7 +438,7 @@ static void mbox_test_remove(struct platform_device *pdev) if (tdev->tx_channel) mbox_free_channel(tdev->tx_channel); - if (tdev->rx_channel) + if (tdev->rx_channel && tdev->rx_channel != tdev->tx_channel) mbox_free_channel(tdev->rx_channel); } diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index 617ba505691d..bbc9fd75a95f 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -18,8 +18,6 @@ #include #include -#include "mailbox.h" - static LIST_HEAD(mbox_cons); static DEFINE_MUTEX(con_mutex); @@ -52,7 +50,7 @@ static void msg_submit(struct mbox_chan *chan) int err = -EBUSY; scoped_guard(spinlock_irqsave, &chan->lock) { - if (!chan->msg_count || chan->active_req) + if (!chan->msg_count || chan->active_req != MBOX_NO_MSG) break; count = chan->msg_count; @@ -74,7 +72,7 @@ static void msg_submit(struct mbox_chan *chan) } } - if (!err && (chan->txdone_method & TXDONE_BY_POLL)) { + if (!err && (chan->txdone_method & MBOX_TXDONE_BY_POLL)) { /* kick start the timer immediately to avoid delays */ scoped_guard(spinlock_irqsave, &chan->mbox->poll_hrt_lock) hrtimer_start(&chan->mbox->poll_hrt, 0, HRTIMER_MODE_REL); @@ -87,13 +85,13 @@ static void tx_tick(struct mbox_chan *chan, int r) scoped_guard(spinlock_irqsave, &chan->lock) { mssg = chan->active_req; - chan->active_req = NULL; + chan->active_req = MBOX_NO_MSG; } /* Submit next message */ msg_submit(chan); - if (!mssg) + if (mssg == MBOX_NO_MSG) return; /* Notify the client */ @@ -114,7 +112,7 @@ static enum hrtimer_restart txdone_hrtimer(struct hrtimer *hrtimer) for (i = 0; i < mbox->num_chans; i++) { struct mbox_chan *chan = &mbox->chans[i]; - if (chan->active_req && chan->cl) { + if (chan->active_req != MBOX_NO_MSG && chan->cl) { txdone = chan->mbox->ops->last_tx_done(chan); if (txdone) tx_tick(chan, 0); @@ -164,7 +162,7 @@ EXPORT_SYMBOL_GPL(mbox_chan_received_data); */ void mbox_chan_txdone(struct mbox_chan *chan, int r) { - if (unlikely(!(chan->txdone_method & TXDONE_BY_IRQ))) { + if (unlikely(!(chan->txdone_method & MBOX_TXDONE_BY_IRQ))) { dev_err(chan->mbox->dev, "Controller can't run the TX ticker\n"); return; @@ -185,7 +183,7 @@ EXPORT_SYMBOL_GPL(mbox_chan_txdone); */ void mbox_client_txdone(struct mbox_chan *chan, int r) { - if (unlikely(!(chan->txdone_method & TXDONE_BY_ACK))) { + if (unlikely(!(chan->txdone_method & MBOX_TXDONE_BY_ACK))) { dev_err(chan->mbox->dev, "Client can't run the TX ticker\n"); return; } @@ -218,6 +216,29 @@ bool mbox_client_peek_data(struct mbox_chan *chan) } EXPORT_SYMBOL_GPL(mbox_client_peek_data); +/** + * mbox_chan_tx_slots_available - Query the number of available TX queue slots. + * @chan: Mailbox channel to query. + * + * Clients may call this to check how many messages can be queued via + * mbox_send_message() before the channel's TX queue is full. This helps + * clients avoid the -ENOBUFS error without needing to increase + * MBOX_TX_QUEUE_LEN. + * This can be called from atomic context. + * + * Return: Number of available slots in the channel's TX queue. + */ +unsigned int mbox_chan_tx_slots_available(struct mbox_chan *chan) +{ + unsigned int ret; + + guard(spinlock_irqsave)(&chan->lock); + ret = MBOX_TX_QUEUE_LEN - chan->msg_count; + + return ret; +} +EXPORT_SYMBOL_GPL(mbox_chan_tx_slots_available); + /** * mbox_send_message - For client to submit a message to be * sent to the remote. @@ -246,7 +267,7 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg) { int t; - if (!chan || !chan->cl) + if (!chan || !chan->cl || mssg == MBOX_NO_MSG) return -EINVAL; t = add_to_rbuf(chan, mssg); @@ -319,12 +340,12 @@ static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl) scoped_guard(spinlock_irqsave, &chan->lock) { chan->msg_free = 0; chan->msg_count = 0; - chan->active_req = NULL; + chan->active_req = MBOX_NO_MSG; chan->cl = cl; init_completion(&chan->tx_complete); - if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone) - chan->txdone_method = TXDONE_BY_ACK; + if (chan->txdone_method == MBOX_TXDONE_BY_POLL && cl->knows_txdone) + chan->txdone_method = MBOX_TXDONE_BY_ACK; } if (chan->mbox->ops->startup) { @@ -341,7 +362,7 @@ static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl) } /** - * mbox_bind_client - Request a mailbox channel. + * mbox_bind_client - Bind client to a mailbox channel. * @chan: The mailbox channel to bind the client to. * @cl: Identity of the client requesting the channel. * @@ -477,9 +498,9 @@ void mbox_free_channel(struct mbox_chan *chan) /* The queued TX requests are simply aborted, no callbacks are made */ scoped_guard(spinlock_irqsave, &chan->lock) { chan->cl = NULL; - chan->active_req = NULL; - if (chan->txdone_method == TXDONE_BY_ACK) - chan->txdone_method = TXDONE_BY_POLL; + chan->active_req = MBOX_NO_MSG; + if (chan->txdone_method == MBOX_TXDONE_BY_ACK) + chan->txdone_method = MBOX_TXDONE_BY_POLL; } module_put(chan->mbox->dev->driver->owner); @@ -505,18 +526,17 @@ int mbox_controller_register(struct mbox_controller *mbox) { int i, txdone; - /* Sanity check */ - if (!mbox || !mbox->dev || !mbox->ops || !mbox->num_chans) + if (!mbox || !mbox->dev || !mbox->ops || !mbox->chans || !mbox->num_chans) return -EINVAL; if (mbox->txdone_irq) - txdone = TXDONE_BY_IRQ; + txdone = MBOX_TXDONE_BY_IRQ; else if (mbox->txdone_poll) - txdone = TXDONE_BY_POLL; + txdone = MBOX_TXDONE_BY_POLL; else /* It has to be ACK then */ - txdone = TXDONE_BY_ACK; + txdone = MBOX_TXDONE_BY_ACK; - if (txdone == TXDONE_BY_POLL) { + if (txdone == MBOX_TXDONE_BY_POLL) { if (!mbox->ops->last_tx_done) { dev_err(mbox->dev, "last_tx_done method is absent\n"); @@ -532,6 +552,7 @@ int mbox_controller_register(struct mbox_controller *mbox) chan->cl = NULL; chan->mbox = mbox; + chan->active_req = MBOX_NO_MSG; chan->txdone_method = txdone; spin_lock_init(&chan->lock); } diff --git a/drivers/mailbox/mailbox.h b/drivers/mailbox/mailbox.h deleted file mode 100644 index e1ec4efab693..000000000000 --- a/drivers/mailbox/mailbox.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#ifndef __MAILBOX_H -#define __MAILBOX_H - -#include - -#define TXDONE_BY_IRQ BIT(0) /* controller has remote RTR irq */ -#define TXDONE_BY_POLL BIT(1) /* controller can read status of last TX */ -#define TXDONE_BY_ACK BIT(2) /* S/W ACK received by Client ticks the TX */ - -#endif /* __MAILBOX_H */ diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index d7c6b38888a3..e523c84b4808 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -493,14 +493,14 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data) if (curr_pa == end_pa - CMDQ_INST_SIZE || curr_pa == end_pa) { /* set to this task directly */ - writel(task->pa_base >> cmdq->pdata->shift, - thread->base + CMDQ_THR_CURR_ADDR); + gce_addr = cmdq_convert_gce_addr(task->pa_base, cmdq->pdata); + writel(gce_addr, thread->base + CMDQ_THR_CURR_ADDR); } else { cmdq_task_insert_into_thread(task); smp_mb(); /* modify jump before enable thread */ } - writel((task->pa_base + pkt->cmd_buf_size) >> cmdq->pdata->shift, - thread->base + CMDQ_THR_END_ADDR); + gce_addr = cmdq_convert_gce_addr(task->pa_base + pkt->cmd_buf_size, cmdq->pdata); + writel(gce_addr, thread->base + CMDQ_THR_END_ADDR); cmdq_thread_resume(thread); } list_move_tail(&task->list_entry, &thread->task_busy_list); @@ -728,7 +728,7 @@ static int cmdq_probe(struct platform_device *pdev) cmdq->mbox.ops = &cmdq_mbox_chan_ops; cmdq->mbox.of_xlate = cmdq_xlate; - /* make use of TXDONE_BY_ACK */ + /* make use of MBOX_TXDONE_BY_ACK */ cmdq->mbox.txdone_irq = false; cmdq->mbox.txdone_poll = false; diff --git a/drivers/mailbox/mtk-vcp-mailbox.c b/drivers/mailbox/mtk-vcp-mailbox.c index cedad575528f..1b291b8ea15a 100644 --- a/drivers/mailbox/mtk-vcp-mailbox.c +++ b/drivers/mailbox/mtk-vcp-mailbox.c @@ -50,7 +50,7 @@ static struct mbox_chan *mtk_vcp_mbox_xlate(struct mbox_controller *mbox, const struct of_phandle_args *sp) { if (sp->args_count) - return NULL; + return ERR_PTR(-EINVAL); return &mbox->chans[0]; } diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index d9f100c18895..535ca8020877 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -22,8 +22,6 @@ #include #include -#include "mailbox.h" - #define MAILBOX_REVISION 0x000 #define MAILBOX_MESSAGE(m) (0x040 + 4 * (m)) #define MAILBOX_FIFOSTATUS(m) (0x080 + 4 * (m)) @@ -240,7 +238,7 @@ static int omap_mbox_startup(struct omap_mbox *mbox) } if (mbox->send_no_irq) - mbox->chan->txdone_method = TXDONE_BY_ACK; + mbox->chan->txdone_method = MBOX_TXDONE_BY_ACK; omap_mbox_enable_irq(mbox, IRQ_RX); diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index 22e70af1ae5d..636879ae1db7 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -59,8 +59,6 @@ #include #include -#include "mailbox.h" - #define MBOX_IRQ_NAME "pcc-mbox" /** diff --git a/drivers/mailbox/rockchip-mailbox.c b/drivers/mailbox/rockchip-mailbox.c index 4d966cb2ed03..a1a7dee64356 100644 --- a/drivers/mailbox/rockchip-mailbox.c +++ b/drivers/mailbox/rockchip-mailbox.c @@ -46,7 +46,7 @@ struct rockchip_mbox { /* The maximum size of buf for each channel */ u32 buf_size; - struct rockchip_mbox_chan *chans; + struct rockchip_mbox_chan chans[]; }; static int rockchip_mbox_send_data(struct mbox_chan *chan, void *data) @@ -173,15 +173,10 @@ static int rockchip_mbox_probe(struct platform_device *pdev) drv_data = (const struct rockchip_mbox_data *) device_get_match_data(&pdev->dev); - mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL); + mb = devm_kzalloc(&pdev->dev, struct_size(mb, chans, drv_data->num_chans), GFP_KERNEL); if (!mb) return -ENOMEM; - mb->chans = devm_kcalloc(&pdev->dev, drv_data->num_chans, - sizeof(*mb->chans), GFP_KERNEL); - if (!mb->chans) - return -ENOMEM; - mb->mbox.chans = devm_kcalloc(&pdev->dev, drv_data->num_chans, sizeof(*mb->mbox.chans), GFP_KERNEL); if (!mb->mbox.chans) diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c index ed9a0bb2bcd8..500fa77c7d53 100644 --- a/drivers/mailbox/tegra-hsp.c +++ b/drivers/mailbox/tegra-hsp.c @@ -16,8 +16,6 @@ #include -#include "mailbox.h" - #define HSP_INT_IE(x) (0x100 + ((x) * 4)) #define HSP_INT_IV 0x300 #define HSP_INT_IR 0x304 @@ -497,7 +495,7 @@ static int tegra_hsp_mailbox_flush(struct mbox_chan *chan, mbox_chan_txdone(chan, 0); /* Wait until channel is empty */ - if (chan->active_req != NULL) + if (chan->active_req != MBOX_NO_MSG) continue; return 0; @@ -516,7 +514,7 @@ static int tegra_hsp_mailbox_startup(struct mbox_chan *chan) struct tegra_hsp *hsp = mb->channel.hsp; unsigned long flags; - chan->txdone_method = TXDONE_BY_IRQ; + chan->txdone_method = MBOX_TXDONE_BY_IRQ; /* * Shared mailboxes start out as consumers by default. FULL and EMPTY diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c index 83378c033c72..028b9ca8ce52 100644 --- a/drivers/md/md-bitmap.c +++ b/drivers/md/md-bitmap.c @@ -216,6 +216,7 @@ struct bitmap { }; static struct workqueue_struct *md_bitmap_wq; +static struct attribute_group md_bitmap_internal_group; static int __bitmap_resize(struct bitmap *bitmap, sector_t blocks, int chunksize, bool init); @@ -2580,6 +2581,30 @@ static int bitmap_resize(struct mddev *mddev, sector_t blocks, int chunksize) return __bitmap_resize(bitmap, blocks, chunksize, false); } +static bool bitmap_none_enabled(void *data, bool flush) +{ + return false; +} + +static int bitmap_none_create(struct mddev *mddev) +{ + return 0; +} + +static int bitmap_none_load(struct mddev *mddev) +{ + return 0; +} + +static void bitmap_none_destroy(struct mddev *mddev) +{ +} + +static int bitmap_none_get_stats(void *data, struct md_bitmap_stats *stats) +{ + return -ENOENT; +} + static ssize_t location_show(struct mddev *mddev, char *page) { @@ -2618,7 +2643,11 @@ location_store(struct mddev *mddev, const char *buf, size_t len) goto out; } - bitmap_destroy(mddev); + sysfs_unmerge_group(&mddev->kobj, &md_bitmap_internal_group); + md_bitmap_destroy_nosysfs(mddev); + mddev->bitmap_id = ID_BITMAP_NONE; + if (!mddev_set_bitmap_ops_nosysfs(mddev)) + goto none_err; mddev->bitmap_info.offset = 0; if (mddev->bitmap_info.file) { struct file *f = mddev->bitmap_info.file; @@ -2654,16 +2683,25 @@ location_store(struct mddev *mddev, const char *buf, size_t len) } mddev->bitmap_info.offset = offset; - rv = bitmap_create(mddev); - if (rv) - goto out; + md_bitmap_destroy_nosysfs(mddev); + mddev->bitmap_id = ID_BITMAP; + if (!mddev_set_bitmap_ops_nosysfs(mddev)) + goto bitmap_err; - rv = bitmap_load(mddev); + rv = md_bitmap_create_nosysfs(mddev); + if (rv) + goto create_err; + + rv = mddev->bitmap_ops->load(mddev); if (rv) { mddev->bitmap_info.offset = 0; - bitmap_destroy(mddev); - goto out; + goto load_err; } + + rv = sysfs_merge_group(&mddev->kobj, + &md_bitmap_internal_group); + if (rv) + goto merge_err; } } if (!mddev->external) { @@ -2679,6 +2717,22 @@ out: if (rv) return rv; return len; + +merge_err: + mddev->bitmap_info.offset = 0; +load_err: + md_bitmap_destroy_nosysfs(mddev); +create_err: + mddev->bitmap_info.offset = 0; + mddev->bitmap_id = ID_BITMAP_NONE; + if (!mddev_set_bitmap_ops_nosysfs(mddev)) + rv = -ENOENT; + goto out; +bitmap_err: + rv = -ENOENT; +none_err: + mddev->bitmap_info.offset = 0; + goto out; } static struct md_sysfs_entry bitmap_location = @@ -2955,8 +3009,12 @@ static struct md_sysfs_entry max_backlog_used = __ATTR(max_backlog_used, S_IRUGO | S_IWUSR, behind_writes_used_show, behind_writes_used_reset); -static struct attribute *md_bitmap_attrs[] = { +static struct attribute *md_bitmap_common_attrs[] = { &bitmap_location.attr, + NULL +}; + +static struct attribute *md_bitmap_internal_attrs[] = { &bitmap_space.attr, &bitmap_timeout.attr, &bitmap_backlog.attr, @@ -2967,9 +3025,41 @@ static struct attribute *md_bitmap_attrs[] = { NULL }; -static struct attribute_group md_bitmap_group = { +static struct attribute_group md_bitmap_common_group = { .name = "bitmap", - .attrs = md_bitmap_attrs, + .attrs = md_bitmap_common_attrs, +}; + +static struct attribute_group md_bitmap_internal_group = { + .name = "bitmap", + .attrs = md_bitmap_internal_attrs, +}; + +static const struct attribute_group *bitmap_groups[] = { + &md_bitmap_common_group, + &md_bitmap_internal_group, + NULL, +}; + +static const struct attribute_group *bitmap_none_groups[] = { + &md_bitmap_common_group, + NULL, +}; + +static struct bitmap_operations bitmap_none_ops = { + .head = { + .type = MD_BITMAP, + .id = ID_BITMAP_NONE, + .name = "none", + }, + + .enabled = bitmap_none_enabled, + .create = bitmap_none_create, + .load = bitmap_none_load, + .destroy = bitmap_none_destroy, + .get_stats = bitmap_none_get_stats, + + .groups = bitmap_none_groups, }; static struct bitmap_operations bitmap_ops = { @@ -3013,21 +3103,38 @@ static struct bitmap_operations bitmap_ops = { .set_pages = bitmap_set_pages, .free = md_bitmap_free, - .group = &md_bitmap_group, + .groups = bitmap_groups, }; int md_bitmap_init(void) { + int err; + md_bitmap_wq = alloc_workqueue("md_bitmap", WQ_MEM_RECLAIM | WQ_UNBOUND, 0); if (!md_bitmap_wq) return -ENOMEM; - return register_md_submodule(&bitmap_ops.head); + err = register_md_submodule(&bitmap_none_ops.head); + if (err) + goto err_wq; + + err = register_md_submodule(&bitmap_ops.head); + if (err) + goto err_none; + + return 0; + +err_none: + unregister_md_submodule(&bitmap_none_ops.head); +err_wq: + destroy_workqueue(md_bitmap_wq); + return err; } void md_bitmap_exit(void) { - destroy_workqueue(md_bitmap_wq); unregister_md_submodule(&bitmap_ops.head); + unregister_md_submodule(&bitmap_none_ops.head); + destroy_workqueue(md_bitmap_wq); } diff --git a/drivers/md/md-bitmap.h b/drivers/md/md-bitmap.h index b42a28fa83a0..214f623c7e79 100644 --- a/drivers/md/md-bitmap.h +++ b/drivers/md/md-bitmap.h @@ -125,7 +125,7 @@ struct bitmap_operations { void (*set_pages)(void *data, unsigned long pages); void (*free)(void *data); - struct attribute_group *group; + const struct attribute_group **groups; }; /* the bitmap API */ diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 9e7e6b1a6f15..1adc5b117821 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -1738,6 +1738,11 @@ static struct attribute_group md_llbitmap_group = { .attrs = md_llbitmap_attrs, }; +static const struct attribute_group *md_llbitmap_groups[] = { + &md_llbitmap_group, + NULL, +}; + static struct bitmap_operations llbitmap_ops = { .head = { .type = MD_BITMAP, @@ -1774,7 +1779,7 @@ static struct bitmap_operations llbitmap_ops = { .dirty_bits = llbitmap_dirty_bits, .write_all = llbitmap_write_all, - .group = &md_llbitmap_group, + .groups = md_llbitmap_groups, }; int md_llbitmap_init(void) diff --git a/drivers/md/md.c b/drivers/md/md.c index 5fb5ae8368ba..8b568eee8743 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -396,27 +396,19 @@ bool md_handle_request(struct mddev *mddev, struct bio *bio) { check_suspended: if (is_suspended(mddev, bio)) { - DEFINE_WAIT(__wait); /* Bail out if REQ_NOWAIT is set for the bio */ if (bio->bi_opf & REQ_NOWAIT) { bio_wouldblock_error(bio); return true; } - for (;;) { - prepare_to_wait(&mddev->sb_wait, &__wait, - TASK_UNINTERRUPTIBLE); - if (!is_suspended(mddev, bio)) - break; - schedule(); - } - finish_wait(&mddev->sb_wait, &__wait); + wait_event(mddev->sb_wait, !is_suspended(mddev, bio)); } if (!percpu_ref_tryget_live(&mddev->active_io)) goto check_suspended; if (!mddev->pers->make_request(mddev, bio)) { percpu_ref_put(&mddev->active_io); - if (!mddev->gendisk && mddev->pers->prepare_suspend) + if (mddev_is_dm(mddev) && mddev->pers->prepare_suspend) return false; goto check_suspended; } @@ -687,13 +679,38 @@ static void active_io_release(struct percpu_ref *ref) static void no_op(struct percpu_ref *r) {} -static bool mddev_set_bitmap_ops(struct mddev *mddev) +static void md_bitmap_sysfs_add(struct mddev *mddev) +{ + if (sysfs_update_groups(&mddev->kobj, mddev->bitmap_ops->groups)) + pr_warn("md: cannot register extra bitmap attributes for %s\n", + mdname(mddev)); + else + /* + * Inform user with KOBJ_CHANGE about new bitmap + * attributes. + */ + kobject_uevent(&mddev->kobj, KOBJ_CHANGE); +} + +static void md_bitmap_sysfs_del(struct mddev *mddev) +{ + int nr_groups = 0; + + for (nr_groups = 0; mddev->bitmap_ops->groups[nr_groups]; nr_groups++) + ; + + while (--nr_groups >= 1) + sysfs_unmerge_group(&mddev->kobj, + mddev->bitmap_ops->groups[nr_groups]); + sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->groups[0]); +} + +bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev) { - struct bitmap_operations *old = mddev->bitmap_ops; struct md_submodule_head *head; - if (mddev->bitmap_id == ID_BITMAP_NONE || - (old && old->head.id == mddev->bitmap_id)) + if (mddev->bitmap_ops && + mddev->bitmap_ops->head.id == mddev->bitmap_id) return true; xa_lock(&md_submodule); @@ -711,18 +728,6 @@ static bool mddev_set_bitmap_ops(struct mddev *mddev) mddev->bitmap_ops = (void *)head; xa_unlock(&md_submodule); - - if (!mddev_is_dm(mddev) && mddev->bitmap_ops->group) { - if (sysfs_create_group(&mddev->kobj, mddev->bitmap_ops->group)) - pr_warn("md: cannot register extra bitmap attributes for %s\n", - mdname(mddev)); - else - /* - * Inform user with KOBJ_CHANGE about new bitmap - * attributes. - */ - kobject_uevent(&mddev->kobj, KOBJ_CHANGE); - } return true; err: @@ -730,15 +735,6 @@ err: return false; } -static void mddev_clear_bitmap_ops(struct mddev *mddev) -{ - if (!mddev_is_dm(mddev) && mddev->bitmap_ops && - mddev->bitmap_ops->group) - sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->group); - - mddev->bitmap_ops = NULL; -} - int mddev_init(struct mddev *mddev) { int err = 0; @@ -4279,7 +4275,7 @@ bitmap_type_show(struct mddev *mddev, char *page) xa_lock(&md_submodule); xa_for_each(&md_submodule, i, head) { - if (head->type != MD_BITMAP) + if (head->type != MD_BITMAP || head->id == ID_BITMAP_NONE) continue; if (mddev->bitmap_id == head->id) @@ -6059,10 +6055,7 @@ static struct attribute *md_default_attrs[] = { &md_logical_block_size.attr, NULL, }; - -static const struct attribute_group md_default_group = { - .attrs = md_default_attrs, -}; +ATTRIBUTE_GROUPS(md_default); static struct attribute *md_redundancy_attrs[] = { &md_scan_mode.attr, @@ -6087,11 +6080,6 @@ static const struct attribute_group md_redundancy_group = { .attrs = md_redundancy_attrs, }; -static const struct attribute_group *md_attr_groups[] = { - &md_default_group, - NULL, -}; - static ssize_t md_attr_show(struct kobject *kobj, struct attribute *attr, char *page) { @@ -6174,7 +6162,7 @@ static const struct sysfs_ops md_sysfs_ops = { static const struct kobj_type md_ktype = { .release = md_kobj_release, .sysfs_ops = &md_sysfs_ops, - .default_groups = md_attr_groups, + .default_groups = md_default_groups, }; int mdp_major = 0; @@ -6539,7 +6527,7 @@ out: return id; } -static int md_bitmap_create(struct mddev *mddev) +int md_bitmap_create_nosysfs(struct mddev *mddev) { enum md_submodule_id orig_id = mddev->bitmap_id; enum md_submodule_id sb_id; @@ -6548,8 +6536,10 @@ static int md_bitmap_create(struct mddev *mddev) if (mddev->bitmap_id == ID_BITMAP_NONE) return -EINVAL; - if (!mddev_set_bitmap_ops(mddev)) + if (!mddev_set_bitmap_ops_nosysfs(mddev)) { + mddev->bitmap_id = orig_id; return -ENOENT; + } err = mddev->bitmap_ops->create(mddev); if (!err) @@ -6560,37 +6550,72 @@ static int md_bitmap_create(struct mddev *mddev) * doesn't match, and mdadm is not the latest version to set * bitmap_type, set bitmap_ops based on the disk version. */ - mddev_clear_bitmap_ops(mddev); + mddev->bitmap_ops = NULL; sb_id = md_bitmap_get_id_from_sb(mddev); - if (sb_id == ID_BITMAP_NONE || sb_id == orig_id) + if (sb_id == ID_BITMAP_NONE || sb_id == orig_id) { + mddev->bitmap_id = orig_id; return err; + } pr_info("md: %s: bitmap version mismatch, switching from %d to %d\n", mdname(mddev), orig_id, sb_id); mddev->bitmap_id = sb_id; - if (!mddev_set_bitmap_ops(mddev)) { + if (!mddev_set_bitmap_ops_nosysfs(mddev)) { mddev->bitmap_id = orig_id; return -ENOENT; } err = mddev->bitmap_ops->create(mddev); if (err) { - mddev_clear_bitmap_ops(mddev); + mddev->bitmap_ops = NULL; mddev->bitmap_id = orig_id; } return err; } -static void md_bitmap_destroy(struct mddev *mddev) +static int md_bitmap_create(struct mddev *mddev) +{ + int err; + + err = md_bitmap_create_nosysfs(mddev); + if (err) + return err; + + if (!mddev_is_dm(mddev) && mddev->bitmap_ops->groups) + md_bitmap_sysfs_add(mddev); + + return 0; +} + +void md_bitmap_destroy_nosysfs(struct mddev *mddev) { if (!md_bitmap_registered(mddev)) return; mddev->bitmap_ops->destroy(mddev); - mddev_clear_bitmap_ops(mddev); + mddev->bitmap_ops = NULL; +} + +static void md_bitmap_destroy(struct mddev *mddev) +{ + if (!mddev_is_dm(mddev) && mddev->bitmap_ops && + mddev->bitmap_ops->groups) + md_bitmap_sysfs_del(mddev); + + md_bitmap_destroy_nosysfs(mddev); +} + +static void md_bitmap_set_none(struct mddev *mddev) +{ + mddev->bitmap_id = ID_BITMAP_NONE; + if (!mddev_set_bitmap_ops_nosysfs(mddev)) + return; + + if (!mddev_is_dm(mddev) && mddev->bitmap_ops->groups) + md_bitmap_sysfs_add(mddev); } int md_run(struct mddev *mddev) @@ -6713,7 +6738,7 @@ int md_run(struct mddev *mddev) } /* dm-raid expect sync_thread to be frozen until resume */ - if (mddev->gendisk) + if (!mddev_is_dm(mddev)) mddev->recovery = 0; /* may be over-ridden by personality */ @@ -6802,6 +6827,10 @@ int md_run(struct mddev *mddev) if (mddev->sb_flags) md_update_sb(mddev, 0); + if (IS_ENABLED(CONFIG_MD_BITMAP) && !mddev->bitmap_info.file && + !mddev->bitmap_info.offset) + md_bitmap_set_none(mddev); + md_new_event(); return 0; @@ -7747,7 +7776,8 @@ static int set_bitmap_file(struct mddev *mddev, int fd) { int err = 0; - if (!md_bitmap_registered(mddev)) + if (!md_bitmap_registered(mddev) || + mddev->bitmap_id == ID_BITMAP_NONE) return -EINVAL; if (mddev->pers) { @@ -7812,10 +7842,12 @@ static int set_bitmap_file(struct mddev *mddev, int fd) if (err) { md_bitmap_destroy(mddev); + md_bitmap_set_none(mddev); fd = -1; } } else if (fd < 0) { md_bitmap_destroy(mddev); + md_bitmap_set_none(mddev); } } @@ -8122,12 +8154,16 @@ static int update_array_info(struct mddev *mddev, mdu_array_info_t *info) mddev->bitmap_info.default_offset; mddev->bitmap_info.space = mddev->bitmap_info.default_space; + mddev->bitmap_id = ID_BITMAP; rv = md_bitmap_create(mddev); if (!rv) rv = mddev->bitmap_ops->load(mddev); - if (rv) + if (rv) { md_bitmap_destroy(mddev); + mddev->bitmap_info.offset = 0; + md_bitmap_set_none(mddev); + } } else { struct md_bitmap_stats stats; @@ -8155,6 +8191,7 @@ static int update_array_info(struct mddev *mddev, mdu_array_info_t *info) } md_bitmap_destroy(mddev); mddev->bitmap_info.offset = 0; + md_bitmap_set_none(mddev); } } md_update_sb(mddev, 1); @@ -9341,9 +9378,11 @@ static void md_bitmap_end(struct mddev *mddev, struct md_io_clone *md_io_clone) static void md_end_clone_io(struct bio *bio) { - struct md_io_clone *md_io_clone = bio->bi_private; + struct md_io_clone *md_io_clone = container_of(bio, struct md_io_clone, + bio_clone); struct bio *orig_bio = md_io_clone->orig_bio; struct mddev *mddev = md_io_clone->mddev; + struct completion *reshape_completion = bio->bi_private; if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false)) md_bitmap_end(mddev, md_io_clone); @@ -9355,7 +9394,10 @@ static void md_end_clone_io(struct bio *bio) bio_end_io_acct(orig_bio, md_io_clone->start_time); bio_put(bio); - bio_endio(orig_bio); + if (unlikely(reshape_completion)) + complete(reshape_completion); + else + bio_endio(orig_bio); percpu_ref_put(&mddev->active_io); } @@ -9380,7 +9422,7 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio) } clone->bi_end_io = md_end_clone_io; - clone->bi_private = md_io_clone; + clone->bi_private = NULL; *bio = clone; } @@ -9391,26 +9433,6 @@ void md_account_bio(struct mddev *mddev, struct bio **bio) } EXPORT_SYMBOL_GPL(md_account_bio); -void md_free_cloned_bio(struct bio *bio) -{ - struct md_io_clone *md_io_clone = bio->bi_private; - struct bio *orig_bio = md_io_clone->orig_bio; - struct mddev *mddev = md_io_clone->mddev; - - if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false)) - md_bitmap_end(mddev, md_io_clone); - - if (bio->bi_status && !orig_bio->bi_status) - orig_bio->bi_status = bio->bi_status; - - if (md_io_clone->start_time) - bio_end_io_acct(orig_bio, md_io_clone->start_time); - - bio_put(bio); - percpu_ref_put(&mddev->active_io); -} -EXPORT_SYMBOL_GPL(md_free_cloned_bio); - /* md_allow_write(mddev) * Calling this ensures that the array is marked 'active' so that writes * may proceed without blocking. It is important to call this before diff --git a/drivers/md/md.h b/drivers/md/md.h index d6f5482e2479..52c378086046 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -920,7 +920,6 @@ extern void md_finish_reshape(struct mddev *mddev); void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev, struct bio *bio, sector_t start, sector_t size); void md_account_bio(struct mddev *mddev, struct bio **bio); -void md_free_cloned_bio(struct bio *bio); extern bool __must_check md_flush_request(struct mddev *mddev, struct bio *bio); void md_write_metadata(struct mddev *mddev, struct md_rdev *rdev, @@ -935,6 +934,9 @@ extern void md_allow_write(struct mddev *mddev); extern void md_wait_for_blocked_rdev(struct md_rdev *rdev, struct mddev *mddev); extern void md_set_array_sectors(struct mddev *mddev, sector_t array_sectors); extern int md_check_no_bitmap(struct mddev *mddev); +bool mddev_set_bitmap_ops_nosysfs(struct mddev *mddev); +int md_bitmap_create_nosysfs(struct mddev *mddev); +void md_bitmap_destroy_nosysfs(struct mddev *mddev); extern int md_integrity_register(struct mddev *mddev); extern int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale); @@ -1015,7 +1017,7 @@ static inline int mddev_suspend_and_lock(struct mddev *mddev) static inline void mddev_suspend_and_lock_nointr(struct mddev *mddev) { mddev_suspend(mddev, false); - mutex_lock(&mddev->reconfig_mutex); + mddev_lock_nointr(mddev); } static inline void mddev_unlock_and_resume(struct mddev *mddev) diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c index 942cd47eb52d..aeec5b9a1dd5 100644 --- a/drivers/md/persistent-data/dm-btree-remove.c +++ b/drivers/md/persistent-data/dm-btree-remove.c @@ -490,12 +490,20 @@ static int rebalance_children(struct shadow_spine *s, if (le32_to_cpu(n->header.nr_entries) == 1) { struct dm_block *child; + int is_shared; dm_block_t b = value64(n, 0); + r = dm_tm_block_is_shared(info->tm, b, &is_shared); + if (r) + return r; + r = dm_tm_read_lock(info->tm, b, &btree_node_validator, &child); if (r) return r; + if (is_shared) + inc_children(info->tm, dm_block_data(child), vt); + memcpy(n, dm_block_data(child), dm_bm_block_size(dm_tm_get_bm(info->tm))); diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c index c33099925f23..56a56a4da4f8 100644 --- a/drivers/md/raid1-10.c +++ b/drivers/md/raid1-10.c @@ -293,8 +293,13 @@ static inline bool raid1_should_read_first(struct mddev *mddev, * bio with REQ_RAHEAD or REQ_NOWAIT can fail at anytime, before such IO is * submitted to the underlying disks, hence don't record badblocks or retry * in this case. + * + * BLK_STS_INVAL means the bio was not valid for the underlying device. This + * is a user error, not a device failure, so retrying or recording bad blocks + * would be wrong. */ static inline bool raid1_should_handle_error(struct bio *bio) { - return !(bio->bi_opf & (REQ_RAHEAD | REQ_NOWAIT)); + return !(bio->bi_opf & (REQ_RAHEAD | REQ_NOWAIT)) && + bio->bi_status != BLK_STS_INVAL; } diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index ba91f7e61920..64d970e2ef50 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1510,21 +1510,14 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, mddev->cluster_ops->area_resyncing(mddev, WRITE, bio->bi_iter.bi_sector, bio_end_sector(bio))) { - DEFINE_WAIT(w); if (bio->bi_opf & REQ_NOWAIT) { bio_wouldblock_error(bio); return; } - for (;;) { - prepare_to_wait(&conf->wait_barrier, - &w, TASK_IDLE); - if (!mddev->cluster_ops->area_resyncing(mddev, WRITE, - bio->bi_iter.bi_sector, - bio_end_sector(bio))) - break; - schedule(); - } - finish_wait(&conf->wait_barrier, &w); + wait_event_idle(conf->wait_barrier, + !mddev->cluster_ops->area_resyncing(mddev, WRITE, + bio->bi_iter.bi_sector, + bio_end_sector(bio))); } /* diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 4901ebe45c87..39085e7dd6d2 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -3791,6 +3791,8 @@ static int setup_geo(struct geom *geo, struct mddev *mddev, enum geo_type new) nc = layout & 255; fc = (layout >> 8) & 255; fo = layout & (1<<16); + if (!nc || !fc) + return -1; geo->raid_disks = disks; geo->near_copies = nc; geo->far_copies = fc; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 6e79829c5acb..0d76e82f4506 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -6217,7 +6217,12 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi) mempool_free(ctx, conf->ctx_pool); if (res == STRIPE_WAIT_RESHAPE) { - md_free_cloned_bio(bi); + DECLARE_COMPLETION_ONSTACK(done); + WRITE_ONCE(bi->bi_private, &done); + + bio_endio(bi); + + wait_for_completion(&done); return false; } diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c index fae3e1ad2df7..67f3001153ae 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-vpu.c @@ -163,7 +163,7 @@ void mdp_vpu_unregister(struct mdp_dev *mdp) } static int mdp_vpu_sendmsg(struct mdp_vpu_dev *vpu, enum scp_ipi_id id, - void *buf, unsigned int len) + const void *buf, unsigned int len) { struct mdp_dev *mdp = vpu_to_mdp(vpu); unsigned int t = MDP_VPU_MESSAGE_TIMEOUT; diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen3.c b/drivers/media/platform/qcom/camss/camss-csid-gen3.c index 664245cf6eb0..bd059243790e 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen3.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen3.c @@ -48,9 +48,9 @@ #define IS_CSID_690(csid) ((csid->camss->res->version == CAMSS_8775P) \ || (csid->camss->res->version == CAMSS_8300)) #define CSID_BUF_DONE_IRQ_STATUS 0x8C -#define BUF_DONE_IRQ_STATUS_RDI_OFFSET (csid_is_lite(csid) ?\ - 1 : (IS_CSID_690(csid) ?\ - 13 : 14)) +#define BUF_DONE_IRQ_STATUS_RDI_OFFSET (csid_is_lite(csid) ? \ + ((IS_CSID_690(csid) ? 0 : 1)) : \ + ((IS_CSID_690(csid) ? 13 : 14))) #define CSID_BUF_DONE_IRQ_MASK 0x90 #define CSID_BUF_DONE_IRQ_CLEAR 0x94 #define CSID_BUF_DONE_IRQ_SET 0x98 diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 62623393f414..78a1b568dbae 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -558,12 +558,16 @@ static int csiphy_init_formats(struct v4l2_subdev *sd, return csiphy_set_format(sd, fh ? fh->state : NULL, &format); } -static bool csiphy_match_clock_name(const char *clock_name, const char *format, - int index) +static bool __printf(2, 3) +csiphy_match_clock_name(const char *clock_name, const char *format, ...) { char name[16]; /* csiphyXXX_timer\0 */ + va_list args; + + va_start(args, format); + vsnprintf(name, sizeof(name), format, args); + va_end(args); - snprintf(name, sizeof(name), format, index); return !strcmp(clock_name, name); } diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 00b87fd9afbd..9335636d7c4d 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -3598,12 +3598,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID2 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite0" }, .interrupt = { "csid_lite0" }, @@ -3617,12 +3615,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID3 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite1" }, .interrupt = { "csid_lite1" }, @@ -3636,12 +3632,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID4 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite2" }, .interrupt = { "csid_lite2" }, @@ -3655,12 +3649,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID5 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite3" }, .interrupt = { "csid_lite3" }, @@ -3674,12 +3666,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID6 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite4" }, .interrupt = { "csid_lite4" }, @@ -3752,15 +3742,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE2 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite0" }, .interrupt = { "vfe_lite0" }, @@ -3775,15 +3767,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE3 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite1" }, .interrupt = { "vfe_lite1" }, @@ -3798,15 +3792,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE4 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite2" }, .interrupt = { "vfe_lite2" }, @@ -3821,15 +3817,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE5 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite3" }, .interrupt = { "vfe_lite3" }, @@ -3844,15 +3842,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE6 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite4" }, .interrupt = { "vfe_lite4" }, diff --git a/drivers/media/platform/qcom/iris/Kconfig b/drivers/media/platform/qcom/iris/Kconfig index 3c803a05305a..5498f48362d1 100644 --- a/drivers/media/platform/qcom/iris/Kconfig +++ b/drivers/media/platform/qcom/iris/Kconfig @@ -3,7 +3,7 @@ config VIDEO_QCOM_IRIS depends on VIDEO_DEV depends on ARCH_QCOM || COMPILE_TEST select V4L2_MEM2MEM_DEV - select QCOM_MDT_LOADER if ARCH_QCOM + select QCOM_MDT_LOADER select QCOM_SCM select VIDEOBUF2_DMA_CONTIG help diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index 9151f43bc6b9..1d53c7414b75 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -582,10 +582,12 @@ static int iris_release_internal_buffers(struct iris_inst *inst, continue; if (!(buffer->attr & BUF_ATTR_QUEUED)) continue; - ret = hfi_ops->session_release_buf(inst, buffer); - if (ret) - return ret; buffer->attr |= BUF_ATTR_PENDING_RELEASE; + ret = hfi_ops->session_release_buf(inst, buffer); + if (ret) { + buffer->attr &= ~BUF_ATTR_PENDING_RELEASE; + return ret; + } } return 0; diff --git a/drivers/media/platform/qcom/iris/iris_core.c b/drivers/media/platform/qcom/iris/iris_core.c index 8406c48d635b..dbaac01eb15a 100644 --- a/drivers/media/platform/qcom/iris/iris_core.c +++ b/drivers/media/platform/qcom/iris/iris_core.c @@ -75,6 +75,10 @@ int iris_core_init(struct iris_core *core) if (ret) goto error_unload_fw; + ret = iris_vpu_switch_to_hwmode(core); + if (ret) + goto error_unload_fw; + ret = iris_hfi_core_init(core); if (ret) goto error_unload_fw; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.c b/drivers/media/platform/qcom/iris/iris_hfi_common.c index 92112eb16c11..621c66593d88 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.c @@ -159,6 +159,10 @@ int iris_hfi_pm_resume(struct iris_core *core) if (ret) goto err_suspend_hw; + ret = iris_vpu_switch_to_hwmode(core); + if (ret) + goto err_suspend_hw; + ret = ops->sys_interframe_powercollapse(core); if (ret) goto err_suspend_hw; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_queue.c b/drivers/media/platform/qcom/iris/iris_hfi_queue.c index b3ed06297953..bf6db23b53e2 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_queue.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_queue.c @@ -263,7 +263,7 @@ int iris_hfi_queues_init(struct iris_core *core) GFP_KERNEL, DMA_ATTR_WRITE_COMBINE); if (!core->sfr_vaddr) { dev_err(core->dev, "sfr alloc and map failed\n"); - dma_free_attrs(core->dev, sizeof(*q_tbl_hdr), core->iface_q_table_vaddr, + dma_free_attrs(core->dev, queue_size, core->iface_q_table_vaddr, core->iface_q_table_daddr, DMA_ATTR_WRITE_COMBINE); return -ENOMEM; } diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index 719217399a30..99d544e2af4f 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -61,12 +61,6 @@ int iris_vdec_inst_init(struct iris_inst *inst) return iris_ctrls_init(inst); } -void iris_vdec_inst_deinit(struct iris_inst *inst) -{ - kfree(inst->fmt_dst); - kfree(inst->fmt_src); -} - static const struct iris_fmt iris_vdec_formats_cap[] = { [IRIS_FMT_NV12] = { .pixfmt = V4L2_PIX_FMT_NV12, diff --git a/drivers/media/platform/qcom/iris/iris_vdec.h b/drivers/media/platform/qcom/iris/iris_vdec.h index ec1ce55d1375..5123d2a340e1 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.h +++ b/drivers/media/platform/qcom/iris/iris_vdec.h @@ -9,7 +9,6 @@ struct iris_inst; int iris_vdec_inst_init(struct iris_inst *inst); -void iris_vdec_inst_deinit(struct iris_inst *inst); int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f); int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f); int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f); diff --git a/drivers/media/platform/qcom/iris/iris_venc.c b/drivers/media/platform/qcom/iris/iris_venc.c index aa27b22704eb..4d886769d958 100644 --- a/drivers/media/platform/qcom/iris/iris_venc.c +++ b/drivers/media/platform/qcom/iris/iris_venc.c @@ -79,12 +79,6 @@ int iris_venc_inst_init(struct iris_inst *inst) return iris_ctrls_init(inst); } -void iris_venc_inst_deinit(struct iris_inst *inst) -{ - kfree(inst->fmt_dst); - kfree(inst->fmt_src); -} - static const struct iris_fmt iris_venc_formats_cap[] = { [IRIS_FMT_H264] = { .pixfmt = V4L2_PIX_FMT_H264, diff --git a/drivers/media/platform/qcom/iris/iris_venc.h b/drivers/media/platform/qcom/iris/iris_venc.h index c4db7433da53..00c1716b2747 100644 --- a/drivers/media/platform/qcom/iris/iris_venc.h +++ b/drivers/media/platform/qcom/iris/iris_venc.h @@ -9,7 +9,6 @@ struct iris_inst; int iris_venc_inst_init(struct iris_inst *inst); -void iris_venc_inst_deinit(struct iris_inst *inst); int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f); int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f); int iris_venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f); diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index bd38d84c9cc7..5eb1786b0737 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -289,10 +289,6 @@ int iris_close(struct file *filp) v4l2_m2m_ctx_release(inst->m2m_ctx); v4l2_m2m_release(inst->m2m_dev); mutex_lock(&inst->lock); - if (inst->domain == DECODER) - iris_vdec_inst_deinit(inst); - else if (inst->domain == ENCODER) - iris_venc_inst_deinit(inst); iris_session_close(inst); iris_inst_change_state(inst, IRIS_INST_DEINIT); iris_v4l2_fh_deinit(inst, filp); @@ -304,6 +300,8 @@ int iris_close(struct file *filp) mutex_unlock(&inst->lock); mutex_destroy(&inst->ctx_q_lock); mutex_destroy(&inst->lock); + kfree(inst->fmt_src); + kfree(inst->fmt_dst); kfree(inst); return 0; diff --git a/drivers/media/platform/qcom/iris/iris_vpu2.c b/drivers/media/platform/qcom/iris/iris_vpu2.c index 9c103a2e4e4e..01ef40f38957 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_vpu2.c @@ -44,4 +44,5 @@ const struct vpu_ops iris_vpu2_ops = { .power_off_controller = iris_vpu_power_off_controller, .power_on_controller = iris_vpu_power_on_controller, .calc_freq = iris_vpu2_calc_freq, + .set_hwmode = iris_vpu_set_hwmode, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu3x.c b/drivers/media/platform/qcom/iris/iris_vpu3x.c index fe4423b951b1..3dad47be78b5 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu3x.c +++ b/drivers/media/platform/qcom/iris/iris_vpu3x.c @@ -234,14 +234,8 @@ static int iris_vpu35_power_on_hw(struct iris_core *core) if (ret) goto err_disable_hw_free_clk; - ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], true); - if (ret) - goto err_disable_hw_clk; - return 0; -err_disable_hw_clk: - iris_disable_unprepare_clock(core, IRIS_HW_CLK); err_disable_hw_free_clk: iris_disable_unprepare_clock(core, IRIS_HW_FREERUN_CLK); err_disable_axi_clk: @@ -266,6 +260,7 @@ const struct vpu_ops iris_vpu3_ops = { .power_off_controller = iris_vpu_power_off_controller, .power_on_controller = iris_vpu_power_on_controller, .calc_freq = iris_vpu3x_vpu4x_calculate_frequency, + .set_hwmode = iris_vpu_set_hwmode, }; const struct vpu_ops iris_vpu33_ops = { @@ -274,6 +269,7 @@ const struct vpu_ops iris_vpu33_ops = { .power_off_controller = iris_vpu33_power_off_controller, .power_on_controller = iris_vpu_power_on_controller, .calc_freq = iris_vpu3x_vpu4x_calculate_frequency, + .set_hwmode = iris_vpu_set_hwmode, }; const struct vpu_ops iris_vpu35_ops = { @@ -283,4 +279,5 @@ const struct vpu_ops iris_vpu35_ops = { .power_on_controller = iris_vpu35_vpu4x_power_on_controller, .program_bootup_registers = iris_vpu35_vpu4x_program_bootup_registers, .calc_freq = iris_vpu3x_vpu4x_calculate_frequency, + .set_hwmode = iris_vpu_set_hwmode, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu4x.c b/drivers/media/platform/qcom/iris/iris_vpu4x.c index a8db02ce5c5e..02e100a4045f 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu4x.c +++ b/drivers/media/platform/qcom/iris/iris_vpu4x.c @@ -252,21 +252,10 @@ static int iris_vpu4x_power_on_hardware(struct iris_core *core) ret = iris_vpu4x_power_on_apv(core); if (ret) goto disable_hw_clocks; - - iris_vpu4x_ahb_sync_reset_apv(core); } - iris_vpu4x_ahb_sync_reset_hardware(core); - - ret = iris_vpu4x_genpd_set_hwmode(core, true, efuse_value); - if (ret) - goto disable_apv_power_domain; - return 0; -disable_apv_power_domain: - if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) - iris_vpu4x_power_off_apv(core); disable_hw_clocks: iris_vpu4x_disable_hardware_clocks(core, efuse_value); disable_vpp1_power_domain: @@ -359,6 +348,18 @@ disable_clocks_and_power: iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); } +static int iris_vpu4x_set_hwmode(struct iris_core *core) +{ + u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR); + + if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) + iris_vpu4x_ahb_sync_reset_apv(core); + + iris_vpu4x_ahb_sync_reset_hardware(core); + + return iris_vpu4x_genpd_set_hwmode(core, true, efuse_value); +} + const struct vpu_ops iris_vpu4x_ops = { .power_off_hw = iris_vpu4x_power_off_hardware, .power_on_hw = iris_vpu4x_power_on_hardware, @@ -366,4 +367,5 @@ const struct vpu_ops iris_vpu4x_ops = { .power_on_controller = iris_vpu35_vpu4x_power_on_controller, .program_bootup_registers = iris_vpu35_vpu4x_program_bootup_registers, .calc_freq = iris_vpu3x_vpu4x_calculate_frequency, + .set_hwmode = iris_vpu4x_set_hwmode, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h index 12640eb5ed8c..8c0d6b7b5de8 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h @@ -67,7 +67,7 @@ struct iris_inst; #define SIZE_DOLBY_RPU_METADATA (41 * 1024) #define H264_CABAC_HDR_RATIO_HD_TOT 1 #define H264_CABAC_RES_RATIO_HD_TOT 3 -#define H265D_MAX_SLICE 1200 +#define H265D_MAX_SLICE 3600 #define SIZE_H265D_HW_PIC_T SIZE_H264D_HW_PIC_T #define H265_CABAC_HDR_RATIO_HD_TOT 2 #define H265_CABAC_RES_RATIO_HD_TOT 2 diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index 548e5f1727fd..69e6126dc4d9 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -292,14 +292,8 @@ int iris_vpu_power_on_hw(struct iris_core *core) if (ret && ret != -ENOENT) goto err_disable_hw_clock; - ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], true); - if (ret) - goto err_disable_hw_ahb_clock; - return 0; -err_disable_hw_ahb_clock: - iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK); err_disable_hw_clock: iris_disable_unprepare_clock(core, IRIS_HW_CLK); err_disable_power: @@ -308,6 +302,16 @@ err_disable_power: return ret; } +int iris_vpu_set_hwmode(struct iris_core *core) +{ + return dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], true); +} + +int iris_vpu_switch_to_hwmode(struct iris_core *core) +{ + return core->iris_platform_data->vpu_ops->set_hwmode(core); +} + int iris_vpu35_vpu4x_power_off_controller(struct iris_core *core) { u32 clk_rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h index f6dffc613b82..dee3b1349c5e 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.h +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h @@ -21,6 +21,7 @@ struct vpu_ops { int (*power_on_controller)(struct iris_core *core); void (*program_bootup_registers)(struct iris_core *core); u64 (*calc_freq)(struct iris_inst *inst, size_t data_size); + int (*set_hwmode)(struct iris_core *core); }; int iris_vpu_boot_firmware(struct iris_core *core); @@ -30,6 +31,8 @@ int iris_vpu_watchdog(struct iris_core *core, u32 intr_status); int iris_vpu_prepare_pc(struct iris_core *core); int iris_vpu_power_on_controller(struct iris_core *core); int iris_vpu_power_on_hw(struct iris_core *core); +int iris_vpu_set_hwmode(struct iris_core *core); +int iris_vpu_switch_to_hwmode(struct iris_core *core); int iris_vpu_power_on(struct iris_core *core); int iris_vpu_power_off_controller(struct iris_core *core); void iris_vpu_power_off_hw(struct iris_core *core); diff --git a/drivers/media/platform/qcom/venus/Kconfig b/drivers/media/platform/qcom/venus/Kconfig index ffb731ecd48c..63ee8c78dc6d 100644 --- a/drivers/media/platform/qcom/venus/Kconfig +++ b/drivers/media/platform/qcom/venus/Kconfig @@ -4,7 +4,7 @@ config VIDEO_QCOM_VENUS depends on VIDEO_DEV && QCOM_SMEM depends on (ARCH_QCOM && ARM64 && IOMMU_API) || COMPILE_TEST select OF_DYNAMIC if ARCH_QCOM - select QCOM_MDT_LOADER if ARCH_QCOM + select QCOM_MDT_LOADER select QCOM_SCM select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 79df0d22e218..e5527020ff33 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -64,17 +64,6 @@ config BRCMSTB_MEMC controller and specifically control the Self Refresh Power Down (SRPD) inactivity timeout. -config BT1_L2_CTL - bool "Baikal-T1 CM2 L2-RAM Cache Control Block" - depends on MIPS_BAIKAL_T1 || COMPILE_TEST - select MFD_SYSCON - help - Baikal-T1 CPU is based on the MIPS P5600 Warrior IP-core. The CPU - resides Coherency Manager v2 with embedded 1MB L2-cache. It's - possible to tune the L2 cache performance up by setting the data, - tags and way-select latencies of RAM access. This driver provides a - dt properties-based and sysfs interface for it. - config TI_AEMIF tristate "Texas Instruments AEMIF driver" depends on ARCH_DAVINCI || ARCH_KEYSTONE || COMPILE_TEST diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index c1959661bf63..3ee883c8759a 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_ARM_PL172_MPMC) += pl172.o obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o obj-$(CONFIG_BRCMSTB_DPFE) += brcmstb_dpfe.o obj-$(CONFIG_BRCMSTB_MEMC) += brcmstb_memc.o -obj-$(CONFIG_BT1_L2_CTL) += bt1-l2-ctl.o obj-$(CONFIG_TI_AEMIF) += ti-aemif.o obj-$(CONFIG_TI_EMIF) += emif.o obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o diff --git a/drivers/memory/brcmstb_memc.c b/drivers/memory/brcmstb_memc.c index ba73470b1b13..c28fe9093616 100644 --- a/drivers/memory/brcmstb_memc.c +++ b/drivers/memory/brcmstb_memc.c @@ -14,6 +14,7 @@ #define REG_MEMC_CNTRLR_CONFIG 0x00 #define CNTRLR_CONFIG_LPDDR4_SHIFT 5 +#define CNTRLR_CONFIG_LPDDR5_SHIFT 6 #define CNTRLR_CONFIG_MASK 0xf #define REG_MEMC_SRPD_CFG_21 0x20 #define REG_MEMC_SRPD_CFG_20 0x34 @@ -34,14 +35,15 @@ struct brcmstb_memc { u32 srpd_offset; }; -static int brcmstb_memc_uses_lpddr4(struct brcmstb_memc *memc) +static int brcmstb_memc_uses_lpddr45(struct brcmstb_memc *memc) { void __iomem *config = memc->ddr_ctrl + REG_MEMC_CNTRLR_CONFIG; u32 reg; reg = readl_relaxed(config) & CNTRLR_CONFIG_MASK; - return reg == CNTRLR_CONFIG_LPDDR4_SHIFT; + return reg == CNTRLR_CONFIG_LPDDR4_SHIFT || + reg == CNTRLR_CONFIG_LPDDR5_SHIFT; } static int brcmstb_memc_srpd_config(struct brcmstb_memc *memc, @@ -95,7 +97,7 @@ static ssize_t srpd_store(struct device *dev, struct device_attribute *attr, * dynamic tuning process will also get affected by the inactivity * timeout, thus making it non functional. */ - if (brcmstb_memc_uses_lpddr4(memc)) + if (brcmstb_memc_uses_lpddr45(memc)) return -EOPNOTSUPP; ret = kstrtouint(buf, 10, &val); diff --git a/drivers/memory/bt1-l2-ctl.c b/drivers/memory/bt1-l2-ctl.c deleted file mode 100644 index 0fd96abc172a..000000000000 --- a/drivers/memory/bt1-l2-ctl.c +++ /dev/null @@ -1,323 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC - * - * Authors: - * Serge Semin - * - * Baikal-T1 CM2 L2-cache Control Block driver. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define L2_CTL_REG 0x028 -#define L2_CTL_DATA_STALL_FLD 0 -#define L2_CTL_DATA_STALL_MASK GENMASK(1, L2_CTL_DATA_STALL_FLD) -#define L2_CTL_TAG_STALL_FLD 2 -#define L2_CTL_TAG_STALL_MASK GENMASK(3, L2_CTL_TAG_STALL_FLD) -#define L2_CTL_WS_STALL_FLD 4 -#define L2_CTL_WS_STALL_MASK GENMASK(5, L2_CTL_WS_STALL_FLD) -#define L2_CTL_SET_CLKRATIO BIT(13) -#define L2_CTL_CLKRATIO_LOCK BIT(31) - -#define L2_CTL_STALL_MIN 0 -#define L2_CTL_STALL_MAX 3 -#define L2_CTL_STALL_SET_DELAY_US 1 -#define L2_CTL_STALL_SET_TOUT_US 1000 - -/* - * struct l2_ctl - Baikal-T1 L2 Control block private data. - * @dev: Pointer to the device structure. - * @sys_regs: Baikal-T1 System Controller registers map. - */ -struct l2_ctl { - struct device *dev; - - struct regmap *sys_regs; -}; - -/* - * enum l2_ctl_stall - Baikal-T1 L2-cache-RAM stall identifier. - * @L2_WSSTALL: Way-select latency. - * @L2_TAGSTALL: Tag latency. - * @L2_DATASTALL: Data latency. - */ -enum l2_ctl_stall { - L2_WS_STALL, - L2_TAG_STALL, - L2_DATA_STALL -}; - -/* - * struct l2_ctl_device_attribute - Baikal-T1 L2-cache device attribute. - * @dev_attr: Actual sysfs device attribute. - * @id: L2-cache stall field identifier. - */ -struct l2_ctl_device_attribute { - struct device_attribute dev_attr; - enum l2_ctl_stall id; -}; - -#define to_l2_ctl_dev_attr(_dev_attr) \ - container_of(_dev_attr, struct l2_ctl_device_attribute, dev_attr) - -#define L2_CTL_ATTR_RW(_name, _prefix, _id) \ - struct l2_ctl_device_attribute l2_ctl_attr_##_name = \ - { __ATTR(_name, 0644, _prefix##_show, _prefix##_store), _id } - -static int l2_ctl_get_latency(struct l2_ctl *l2, enum l2_ctl_stall id, u32 *val) -{ - u32 data = 0; - int ret; - - ret = regmap_read(l2->sys_regs, L2_CTL_REG, &data); - if (ret) - return ret; - - switch (id) { - case L2_WS_STALL: - *val = FIELD_GET(L2_CTL_WS_STALL_MASK, data); - break; - case L2_TAG_STALL: - *val = FIELD_GET(L2_CTL_TAG_STALL_MASK, data); - break; - case L2_DATA_STALL: - *val = FIELD_GET(L2_CTL_DATA_STALL_MASK, data); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int l2_ctl_set_latency(struct l2_ctl *l2, enum l2_ctl_stall id, u32 val) -{ - u32 mask = 0, data = 0; - int ret; - - val = clamp_val(val, L2_CTL_STALL_MIN, L2_CTL_STALL_MAX); - - switch (id) { - case L2_WS_STALL: - data = FIELD_PREP(L2_CTL_WS_STALL_MASK, val); - mask = L2_CTL_WS_STALL_MASK; - break; - case L2_TAG_STALL: - data = FIELD_PREP(L2_CTL_TAG_STALL_MASK, val); - mask = L2_CTL_TAG_STALL_MASK; - break; - case L2_DATA_STALL: - data = FIELD_PREP(L2_CTL_DATA_STALL_MASK, val); - mask = L2_CTL_DATA_STALL_MASK; - break; - default: - return -EINVAL; - } - - data |= L2_CTL_SET_CLKRATIO; - mask |= L2_CTL_SET_CLKRATIO; - - ret = regmap_update_bits(l2->sys_regs, L2_CTL_REG, mask, data); - if (ret) - return ret; - - return regmap_read_poll_timeout(l2->sys_regs, L2_CTL_REG, data, - data & L2_CTL_CLKRATIO_LOCK, - L2_CTL_STALL_SET_DELAY_US, - L2_CTL_STALL_SET_TOUT_US); -} - -static void l2_ctl_clear_data(void *data) -{ - struct l2_ctl *l2 = data; - struct platform_device *pdev = to_platform_device(l2->dev); - - platform_set_drvdata(pdev, NULL); -} - -static struct l2_ctl *l2_ctl_create_data(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct l2_ctl *l2; - int ret; - - l2 = devm_kzalloc(dev, sizeof(*l2), GFP_KERNEL); - if (!l2) - return ERR_PTR(-ENOMEM); - - ret = devm_add_action(dev, l2_ctl_clear_data, l2); - if (ret) { - dev_err(dev, "Can't add L2 CTL data clear action\n"); - return ERR_PTR(ret); - } - - l2->dev = dev; - platform_set_drvdata(pdev, l2); - - return l2; -} - -static int l2_ctl_find_sys_regs(struct l2_ctl *l2) -{ - l2->sys_regs = syscon_node_to_regmap(l2->dev->of_node->parent); - if (IS_ERR(l2->sys_regs)) { - dev_err(l2->dev, "Couldn't get L2 CTL register map\n"); - return PTR_ERR(l2->sys_regs); - } - - return 0; -} - -static int l2_ctl_of_parse_property(struct l2_ctl *l2, enum l2_ctl_stall id, - const char *propname) -{ - int ret = 0; - u32 data; - - if (!of_property_read_u32(l2->dev->of_node, propname, &data)) { - ret = l2_ctl_set_latency(l2, id, data); - if (ret) - dev_err(l2->dev, "Invalid value of '%s'\n", propname); - } - - return ret; -} - -static int l2_ctl_of_parse(struct l2_ctl *l2) -{ - int ret; - - ret = l2_ctl_of_parse_property(l2, L2_WS_STALL, "baikal,l2-ws-latency"); - if (ret) - return ret; - - ret = l2_ctl_of_parse_property(l2, L2_TAG_STALL, "baikal,l2-tag-latency"); - if (ret) - return ret; - - return l2_ctl_of_parse_property(l2, L2_DATA_STALL, - "baikal,l2-data-latency"); -} - -static ssize_t l2_ctl_latency_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct l2_ctl_device_attribute *devattr = to_l2_ctl_dev_attr(attr); - struct l2_ctl *l2 = dev_get_drvdata(dev); - u32 data; - int ret; - - ret = l2_ctl_get_latency(l2, devattr->id, &data); - if (ret) - return ret; - - return sysfs_emit(buf, "%u\n", data); -} - -static ssize_t l2_ctl_latency_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct l2_ctl_device_attribute *devattr = to_l2_ctl_dev_attr(attr); - struct l2_ctl *l2 = dev_get_drvdata(dev); - u32 data; - int ret; - - if (kstrtouint(buf, 0, &data) < 0) - return -EINVAL; - - ret = l2_ctl_set_latency(l2, devattr->id, data); - if (ret) - return ret; - - return count; -} - -static L2_CTL_ATTR_RW(l2_ws_latency, l2_ctl_latency, L2_WS_STALL); -static L2_CTL_ATTR_RW(l2_tag_latency, l2_ctl_latency, L2_TAG_STALL); -static L2_CTL_ATTR_RW(l2_data_latency, l2_ctl_latency, L2_DATA_STALL); - -static struct attribute *l2_ctl_sysfs_attrs[] = { - &l2_ctl_attr_l2_ws_latency.dev_attr.attr, - &l2_ctl_attr_l2_tag_latency.dev_attr.attr, - &l2_ctl_attr_l2_data_latency.dev_attr.attr, - NULL -}; -ATTRIBUTE_GROUPS(l2_ctl_sysfs); - -static void l2_ctl_remove_sysfs(void *data) -{ - struct l2_ctl *l2 = data; - - device_remove_groups(l2->dev, l2_ctl_sysfs_groups); -} - -static int l2_ctl_init_sysfs(struct l2_ctl *l2) -{ - int ret; - - ret = device_add_groups(l2->dev, l2_ctl_sysfs_groups); - if (ret) { - dev_err(l2->dev, "Failed to create L2 CTL sysfs nodes\n"); - return ret; - } - - ret = devm_add_action_or_reset(l2->dev, l2_ctl_remove_sysfs, l2); - if (ret) - dev_err(l2->dev, "Can't add L2 CTL sysfs remove action\n"); - - return ret; -} - -static int l2_ctl_probe(struct platform_device *pdev) -{ - struct l2_ctl *l2; - int ret; - - l2 = l2_ctl_create_data(pdev); - if (IS_ERR(l2)) - return PTR_ERR(l2); - - ret = l2_ctl_find_sys_regs(l2); - if (ret) - return ret; - - ret = l2_ctl_of_parse(l2); - if (ret) - return ret; - - ret = l2_ctl_init_sysfs(l2); - if (ret) - return ret; - - return 0; -} - -static const struct of_device_id l2_ctl_of_match[] = { - { .compatible = "baikal,bt1-l2-ctl" }, - { } -}; -MODULE_DEVICE_TABLE(of, l2_ctl_of_match); - -static struct platform_driver l2_ctl_driver = { - .probe = l2_ctl_probe, - .driver = { - .name = "bt1-l2-ctl", - .of_match_table = l2_ctl_of_match - } -}; -module_platform_driver(l2_ctl_driver); - -MODULE_AUTHOR("Serge Semin "); -MODULE_DESCRIPTION("Baikal-T1 L2-cache driver"); diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 58ccc1c02e90..0fb568456164 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -1005,11 +1005,9 @@ static int rpcif_probe(struct platform_device *pdev) return PTR_ERR(rpc->base); rpc->info = of_device_get_match_data(dev); rpc->regmap = devm_regmap_init(dev, NULL, rpc, rpc->info->regmap_config); - if (IS_ERR(rpc->regmap)) { - dev_err(dev, "failed to init regmap for rpcif, error %ld\n", - PTR_ERR(rpc->regmap)); - return PTR_ERR(rpc->regmap); - } + if (IS_ERR(rpc->regmap)) + return dev_err_probe(dev, PTR_ERR(rpc->regmap), + "failed to init regmap for rpcif\n"); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirmap"); rpc->dirmap = devm_ioremap_resource(dev, res); diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 6edb210287dc..d620660da331 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2014-2025 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved. */ #include @@ -56,6 +56,23 @@ static const struct of_device_id tegra_mc_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_mc_of_match); +const struct tegra_mc_regs tegra20_mc_regs = { + .cfg_channel_enable = 0xdf8, + .err_status = 0x08, + .err_add = 0x0c, + .err_add_hi = 0x11fc, + .err_vpr_status = 0x654, + .err_vpr_add = 0x658, + .err_sec_status = 0x67c, + .err_sec_add = 0x680, + .err_mts_status = 0x9b0, + .err_mts_add = 0x9b4, + .err_gen_co_status = 0xc00, + .err_gen_co_add = 0xc04, + .err_route_status = 0x9c0, + .err_route_add = 0x9c4, +}; + static void tegra_mc_devm_action_put_device(void *data) { struct tegra_mc *mc = data; @@ -381,12 +398,16 @@ unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc) } EXPORT_SYMBOL_GPL(tegra_mc_get_emem_device_count); +const irq_handler_t tegra30_mc_irq_handlers[] = { + tegra30_mc_handle_irq +}; + #if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \ defined(CONFIG_ARCH_TEGRA_114_SOC) || \ defined(CONFIG_ARCH_TEGRA_124_SOC) || \ defined(CONFIG_ARCH_TEGRA_132_SOC) || \ defined(CONFIG_ARCH_TEGRA_210_SOC) -static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) +static void tegra_mc_setup_latency_allowance(struct tegra_mc *mc) { unsigned long long tick; unsigned int i; @@ -414,8 +435,6 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) /* latch new values */ mc_writel(mc, MC_TIMING_UPDATE, MC_TIMING_CONTROL); - - return 0; } static int load_one_timing(struct tegra_mc *mc, @@ -509,32 +528,24 @@ int tegra30_mc_probe(struct tegra_mc *mc) int err; mc->clk = devm_clk_get_optional(mc->dev, "mc"); - if (IS_ERR(mc->clk)) { - dev_err(mc->dev, "failed to get MC clock: %ld\n", PTR_ERR(mc->clk)); - return PTR_ERR(mc->clk); - } + if (IS_ERR(mc->clk)) + return dev_err_probe(mc->dev, PTR_ERR(mc->clk), + "failed to get MC clock\n"); /* ensure that debug features are disabled */ mc_writel(mc, 0x00000000, MC_TIMING_CONTROL_DBG); - err = tegra_mc_setup_latency_allowance(mc); - if (err < 0) { - dev_err(mc->dev, "failed to setup latency allowance: %d\n", err); - return err; - } + tegra_mc_setup_latency_allowance(mc); err = tegra_mc_setup_timings(mc); - if (err < 0) { - dev_err(mc->dev, "failed to setup timings: %d\n", err); - return err; - } + if (err < 0) + return dev_err_probe(mc->dev, err, "failed to setup timings\n"); return 0; } const struct tegra_mc_ops tegra30_mc_ops = { .probe = tegra30_mc_probe, - .handle_irq = tegra30_mc_handle_irq, }; #endif @@ -575,9 +586,9 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data) } /* mask all interrupts to avoid flooding */ - status = mc_ch_readl(mc, channel, MC_INTSTATUS) & mc->soc->intmask; + status = mc_ch_readl(mc, channel, MC_INTSTATUS) & mc->soc->intmasks[0].mask; } else { - status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask; + status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmasks[0].mask; } if (!status) @@ -600,37 +611,37 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data) switch (intmask) { case MC_INT_DECERR_VPR: - status_reg = MC_ERR_VPR_STATUS; - addr_reg = MC_ERR_VPR_ADR; + status_reg = mc->soc->regs->err_vpr_status; + addr_reg = mc->soc->regs->err_vpr_add; break; case MC_INT_SECERR_SEC: - status_reg = MC_ERR_SEC_STATUS; - addr_reg = MC_ERR_SEC_ADR; + status_reg = mc->soc->regs->err_sec_status; + addr_reg = mc->soc->regs->err_sec_add; break; case MC_INT_DECERR_MTS: - status_reg = MC_ERR_MTS_STATUS; - addr_reg = MC_ERR_MTS_ADR; + status_reg = mc->soc->regs->err_mts_status; + addr_reg = mc->soc->regs->err_mts_add; break; case MC_INT_DECERR_GENERALIZED_CARVEOUT: - status_reg = MC_ERR_GENERALIZED_CARVEOUT_STATUS; - addr_reg = MC_ERR_GENERALIZED_CARVEOUT_ADR; + status_reg = mc->soc->regs->err_gen_co_status; + addr_reg = mc->soc->regs->err_gen_co_add; break; case MC_INT_DECERR_ROUTE_SANITY: - status_reg = MC_ERR_ROUTE_SANITY_STATUS; - addr_reg = MC_ERR_ROUTE_SANITY_ADR; + status_reg = mc->soc->regs->err_route_status; + addr_reg = mc->soc->regs->err_route_add; break; default: - status_reg = MC_ERR_STATUS; - addr_reg = MC_ERR_ADR; + status_reg = mc->soc->regs->err_status; + addr_reg = mc->soc->regs->err_add; #ifdef CONFIG_PHYS_ADDR_T_64BIT if (mc->soc->has_addr_hi_reg) - addr_hi_reg = MC_ERR_ADR_HI; + addr_hi_reg = mc->soc->regs->err_add_hi; #endif break; } @@ -647,9 +658,12 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data) addr = mc_ch_readl(mc, channel, addr_hi_reg); else addr = mc_readl(mc, addr_hi_reg); - } else { + } else if (mc->soc->mc_addr_hi_mask) { addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) & - MC_ERR_STATUS_ADR_HI_MASK); + mc->soc->mc_addr_hi_mask); + } else { + dev_err_ratelimited(mc->dev, "Unable to determine high address!"); + return IRQ_NONE; } addr <<= 32; } @@ -674,11 +688,11 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data) } } - type = (value & MC_ERR_STATUS_TYPE_MASK) >> + type = (value & mc->soc->mc_err_status_type_mask) >> MC_ERR_STATUS_TYPE_SHIFT; - desc = tegra_mc_error_names[type]; + desc = tegra20_mc_error_names[type]; - switch (value & MC_ERR_STATUS_TYPE_MASK) { + switch (value & mc->soc->mc_err_status_type_mask) { case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE: perm[0] = ' '; perm[1] = '['; @@ -744,9 +758,10 @@ const char *const tegra_mc_status_names[32] = { [16] = "MTS carveout violation", [17] = "Generalized carveout violation", [20] = "Route Sanity error", + [21] = "GIC_MSI error", }; -const char *const tegra_mc_error_names[8] = { +const char *const tegra20_mc_error_names[8] = { [2] = "EMEM decode error", [3] = "TrustZone violation", [4] = "Carveout violation", @@ -883,7 +898,7 @@ static void tegra_mc_num_channel_enabled(struct tegra_mc *mc) unsigned int i; u32 value; - value = mc_ch_readl(mc, 0, MC_EMEM_ADR_CFG_CHANNEL_ENABLE); + value = mc_ch_readl(mc, 0, mc->soc->regs->cfg_channel_enable); if (value <= 0) { mc->num_channels = mc->soc->num_channels; return; @@ -935,25 +950,32 @@ static int tegra_mc_probe(struct platform_device *pdev) tegra_mc_num_channel_enabled(mc); - if (mc->soc->ops && mc->soc->ops->handle_irq) { - mc->irq = platform_get_irq(pdev, 0); - if (mc->irq < 0) - return mc->irq; + if (mc->soc->handle_irq) { + unsigned int i; WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n"); - if (mc->soc->num_channels) - mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmask, - MC_INTMASK); - else - mc_writel(mc, mc->soc->intmask, MC_INTMASK); + for (i = 0; i < mc->soc->num_interrupts; i++) { + int irq; - err = devm_request_irq(&pdev->dev, mc->irq, mc->soc->ops->handle_irq, 0, - dev_name(&pdev->dev), mc); - if (err < 0) { - dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq, - err); - return err; + irq = platform_get_irq(pdev, i); + if (irq < 0) + return irq; + + err = devm_request_irq(&pdev->dev, irq, mc->soc->handle_irq[i], 0, + dev_name(&pdev->dev), mc); + if (err < 0) { + dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", irq, err); + return err; + } + } + + for (i = 0; i < mc->soc->num_intmasks; i++) { + if (mc->soc->num_channels) + mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmasks[i].mask, + mc->soc->intmasks[i].reg); + else + mc_writel(mc, mc->soc->intmasks[i].mask, mc->soc->intmasks[i].reg); } } @@ -971,8 +993,7 @@ static int tegra_mc_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) && mc->soc->smmu) { mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc); if (IS_ERR(mc->smmu)) { - dev_err(&pdev->dev, "failed to probe SMMU: %ld\n", - PTR_ERR(mc->smmu)); + dev_err(&pdev->dev, "failed to probe SMMU: %pe\n", mc->smmu); mc->smmu = NULL; } } diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index 1d97cf4d3a94..649b54369263 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2014-2025 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved. */ #ifndef MEMORY_TEGRA_MC_H @@ -13,15 +13,36 @@ #include #define MC_INTSTATUS 0x00 +/* Bit field of MC_INTSTATUS register */ +#define MC_INT_DECERR_EMEM BIT(6) +#define MC_INT_INVALID_GART_PAGE BIT(7) +#define MC_INT_SECURITY_VIOLATION BIT(8) +#define MC_INT_ARBITRATION_EMEM BIT(9) +#define MC_INT_INVALID_SMMU_PAGE BIT(10) +#define MC_INT_INVALID_APB_ASID_UPDATE BIT(11) +#define MC_INT_DECERR_VPR BIT(12) +#define MC_INT_SECERR_SEC BIT(13) +#define MC_INT_DECERR_MTS BIT(16) +#define MC_INT_DECERR_GENERALIZED_CARVEOUT BIT(17) +#define MC_INT_DECERR_ROUTE_SANITY BIT(20) +#define MC_INT_DECERR_ROUTE_SANITY_GIC_MSI BIT(21) + #define MC_INTMASK 0x04 -#define MC_ERR_STATUS 0x08 -#define MC_ERR_ADR 0x0c #define MC_GART_ERROR_REQ 0x30 #define MC_EMEM_ADR_CFG 0x54 +#define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0) + #define MC_DECERR_EMEM_OTHERS_STATUS 0x58 #define MC_SECURITY_VIOLATION_STATUS 0x74 #define MC_EMEM_ARB_CFG 0x90 +#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) ((x) & 0x1ff) +#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff + #define MC_EMEM_ARB_OUTSTANDING_REQ 0x94 +#define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE BIT(30) +#define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE BIT(31) +#define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK 0x1ff + #define MC_EMEM_ARB_TIMING_RCD 0x98 #define MC_EMEM_ARB_TIMING_RP 0x9c #define MC_EMEM_ARB_TIMING_RC 0xa0 @@ -41,60 +62,97 @@ #define MC_EMEM_ARB_MISC1 0xdc #define MC_EMEM_ARB_RING1_THROTTLE 0xe0 #define MC_EMEM_ARB_OVERRIDE 0xe8 +#define MC_EMEM_ARB_OVERRIDE_EACK_MASK 0x3 + #define MC_TIMING_CONTROL_DBG 0xf8 #define MC_TIMING_CONTROL 0xfc -#define MC_ERR_VPR_STATUS 0x654 -#define MC_ERR_VPR_ADR 0x658 -#define MC_ERR_SEC_STATUS 0x67c -#define MC_ERR_SEC_ADR 0x680 -#define MC_ERR_MTS_STATUS 0x9b0 -#define MC_ERR_MTS_ADR 0x9b4 -#define MC_ERR_ROUTE_SANITY_STATUS 0x9c0 -#define MC_ERR_ROUTE_SANITY_ADR 0x9c4 -#define MC_ERR_GENERALIZED_CARVEOUT_STATUS 0xc00 -#define MC_ERR_GENERALIZED_CARVEOUT_ADR 0xc04 -#define MC_EMEM_ADR_CFG_CHANNEL_ENABLE 0xdf8 -#define MC_GLOBAL_INTSTATUS 0xf24 -#define MC_ERR_ADR_HI 0x11fc +#define MC_TIMING_UPDATE BIT(0) -#define MC_INT_DECERR_ROUTE_SANITY BIT(20) -#define MC_INT_DECERR_GENERALIZED_CARVEOUT BIT(17) -#define MC_INT_DECERR_MTS BIT(16) -#define MC_INT_SECERR_SEC BIT(13) -#define MC_INT_DECERR_VPR BIT(12) -#define MC_INT_INVALID_APB_ASID_UPDATE BIT(11) -#define MC_INT_INVALID_SMMU_PAGE BIT(10) -#define MC_INT_ARBITRATION_EMEM BIT(9) -#define MC_INT_SECURITY_VIOLATION BIT(8) -#define MC_INT_INVALID_GART_PAGE BIT(7) -#define MC_INT_DECERR_EMEM BIT(6) +#define MC_GLOBAL_INTSTATUS 0xf24 + +/* Bit field of MC_ERR_STATUS_0 register */ +#define MC_ERR_STATUS_RW BIT(16) +#define MC_ERR_STATUS_SECURITY BIT(17) +#define MC_ERR_STATUS_NONSECURE BIT(25) +#define MC_ERR_STATUS_WRITABLE BIT(26) +#define MC_ERR_STATUS_READABLE BIT(27) + +#define MC_ERR_STATUS_GSC_ADR_HI_MASK 0xffff +#define MC_ERR_STATUS_GSC_ADR_HI_SHIFT 16 +#define MC_ERR_STATUS_RT_ADR_HI_SHIFT 15 #define MC_ERR_STATUS_TYPE_SHIFT 28 #define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (0x6 << 28) -#define MC_ERR_STATUS_TYPE_MASK (0x7 << 28) -#define MC_ERR_STATUS_READABLE BIT(27) -#define MC_ERR_STATUS_WRITABLE BIT(26) -#define MC_ERR_STATUS_NONSECURE BIT(25) +#define MC_ERR_STATUS_RT_TYPE_MASK (0xf << 28) +#define MC_ERR_STATUS_RT_TYPE_SHIFT 28 + #define MC_ERR_STATUS_ADR_HI_SHIFT 20 -#define MC_ERR_STATUS_ADR_HI_MASK 0x3 -#define MC_ERR_STATUS_SECURITY BIT(17) -#define MC_ERR_STATUS_RW BIT(16) - -#define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0) - -#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) ((x) & 0x1ff) -#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff - -#define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK 0x1ff -#define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE BIT(30) -#define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE BIT(31) - -#define MC_EMEM_ARB_OVERRIDE_EACK_MASK 0x3 - -#define MC_TIMING_UPDATE BIT(0) #define MC_BROADCAST_CHANNEL ~0 +/* Tegra264 specific registers */ + +/* Registers for MSS HUB */ +#define MSS_HUB_GLOBAL_INTSTATUS_0 0x6000 +#define MSS_HUBC_INTR BIT(0) +#define MSS_HUB_GLOBAL_MASK 0x7F00 +#define MSS_HUB_GLOBAL_SHIFT 8 + +#define MSS_HUB_HUBC_INTSTATUS_0 0x6008 +#define MSS_HUB_INTRSTATUS_0 0x600c +#define MSS_HUB_HUBC_INTMASK_0 0x6010 +#define MSS_HUB_HUBC_SCRUB_DONE_INTMASK BIT(0) + +#define MSS_HUB_HUBC_INTPRIORITY_0 0x6014 +#define MSS_HUB_INTRMASK_0 0x6018 +#define MSS_HUB_COALESCER_ERR_INTMASK BIT(0) +#define MSS_HUB_SMMU_BYPASS_ALLOW_ERR_INTMASK BIT(1) +#define MSS_HUB_ILLEGAL_TBUGRP_ID_INTMASK BIT(2) +#define MSS_HUB_MSI_ERR_INTMASK BIT(3) +#define MSS_HUB_POISON_RSP_INTMASK BIT(4) +#define MSS_HUB_RESTRICTED_ACCESS_ERR_INTMASK BIT(5) +#define MSS_HUB_RESERVED_PA_ERR_INTMASK BIT(6) + +#define MSS_HUB_INTRPRIORITY_0 0x601c +#define MSS_HUB_SMMU_BYPASS_ALLOW_ERR_STATUS_0 0x6020 +#define MSS_HUB_MSI_ERR_STATUS_0 0x6024 +#define MSS_HUB_POISON_RSP_STATUS_0 0x6028 +#define MSS_HUB_COALESCE_ERR_STATUS_0 0x60e0 +#define MSS_HUB_COALESCE_ERR_ADR_HI_0 0x60e4 +#define MSS_HUB_COALESCE_ERR_ADR_0 0x60e8 +#define MSS_HUB_RESTRICTED_ACCESS_ERR_STATUS_0 0x638c +#define MSS_HUB_RESERVED_PA_ERR_STATUS_0 0x6390 +#define MSS_HUB_ILLEGAL_TBUGRP_ID_ERR_STATUS_0 0x63b0 + +/* Registers for channels */ +#define MC_CH_INTSTATUS_0 0x82d4 +#define MC_CH_INTMASK_0 0x82d8 +#define WCAM_ERR_INTMASK BIT(19) + +#define MC_ERR_GENERALIZED_CARVEOUT_STATUS_1_0 0xbc74 + +/* Registers for MCF */ +#define MCF_COMMON_INTSTATUS0_0_0 0xce04 +#define MCF_INTSTATUS_0 0xce2c +#define MCF_INTMASK_0 0xce30 +#define MCF_INTPRIORITY_0 0xce34 + +/* Registers for SBS */ +#define MSS_SBS_INTSTATUS_0 0xec08 +#define MSS_SBS_INTMASK_0 0xec0c +#define MSS_SBS_FILL_FIFO_ISO_OVERFLOW_INTMASK BIT(0) +#define MSS_SBS_FILL_FIFO_SISO_OVERFLOW_INTMASK BIT(1) +#define MSS_SBS_FILL_FIFO_NISO_OVERFLOW_INTMASK BIT(2) + +/* Bit field of MC_ERR_ROUTE_SANITY_STATUS_0 register */ +#define MC_ERR_ROUTE_SANITY_RW BIT(12) +#define MC_ERR_ROUTE_SANITY_SEC BIT(13) + +#define ERR_GENERALIZED_APERTURE_ID_SHIFT 0 +#define ERR_GENERALIZED_APERTURE_ID_MASK 0x1F +#define ERR_GENERALIZED_CARVEOUT_APERTURE_ID_SHIFT 5 +#define ERR_GENERALIZED_CARVEOUT_APERTURE_ID_MASK 0x1F + static inline u32 tegra_mc_scale_percents(u64 val, unsigned int percents) { val = val * percents; @@ -203,8 +261,9 @@ extern const struct tegra_mc_ops tegra186_mc_ops; #endif irqreturn_t tegra30_mc_handle_irq(int irq, void *data); +extern const irq_handler_t tegra30_mc_irq_handlers[1]; extern const char * const tegra_mc_status_names[32]; -extern const char * const tegra_mc_error_names[8]; +extern const char * const tegra20_mc_error_names[8]; /* * These IDs are for internal use of Tegra ICC drivers. The ID numbers are diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c index 41350570c815..02dd4e26288a 100644 --- a/drivers/memory/tegra/tegra114.c +++ b/drivers/memory/tegra/tegra114.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved. */ #include @@ -1101,6 +1101,14 @@ static const struct tegra_mc_reset tegra114_mc_resets[] = { TEGRA114_MC_RESET(VI, 0x200, 0x204, 17), }; +static const struct tegra_mc_intmask tegra114_mc_intmasks[] = { + { + .reg = MC_INTMASK, + .mask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | + MC_INT_DECERR_EMEM, + }, +}; + const struct tegra_mc_soc tegra114_mc_soc = { .clients = tegra114_mc_clients, .num_clients = ARRAY_SIZE(tegra114_mc_clients), @@ -1108,10 +1116,14 @@ const struct tegra_mc_soc tegra114_mc_soc = { .atom_size = 32, .client_id_mask = 0x7f, .smmu = &tegra114_smmu_soc, - .intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | - MC_INT_DECERR_EMEM, + .intmasks = tegra114_mc_intmasks, + .num_intmasks = ARRAY_SIZE(tegra114_mc_intmasks), .reset_ops = &tegra_mc_reset_ops_common, .resets = tegra114_mc_resets, .num_resets = ARRAY_SIZE(tegra114_mc_resets), .ops = &tegra30_mc_ops, + .regs = &tegra20_mc_regs, + .handle_irq = tegra30_mc_irq_handlers, + .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers), + .mc_err_status_type_mask = (0x7 << 28), }; diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c index ff26815e51f1..5cfbc169c5f9 100644 --- a/drivers/memory/tegra/tegra124-emc.c +++ b/drivers/memory/tegra/tegra124-emc.c @@ -608,7 +608,7 @@ static int tegra124_emc_prepare_timing_change(struct tegra_emc *emc, if ((last->emc_mode_1 & 0x1) == (timing->emc_mode_1 & 0x1)) dll_change = DLL_CHANGE_NONE; - else if (timing->emc_mode_1 & 0x1) + else if (!(timing->emc_mode_1 & 0x1)) dll_change = DLL_CHANGE_ON; else dll_change = DLL_CHANGE_OFF; diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c index 991d4f7bc070..df87c5038625 100644 --- a/drivers/memory/tegra/tegra124.c +++ b/drivers/memory/tegra/tegra124.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved. */ #include @@ -1258,6 +1258,15 @@ static const struct tegra_smmu_soc tegra124_smmu_soc = { .num_asids = 128, }; +static const struct tegra_mc_intmask tegra124_mc_intmasks[] = { + { + .reg = MC_INTMASK, + .mask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | + MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | + MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + }, +}; + const struct tegra_mc_soc tegra124_mc_soc = { .clients = tegra124_mc_clients, .num_clients = ARRAY_SIZE(tegra124_mc_clients), @@ -1267,14 +1276,18 @@ const struct tegra_mc_soc tegra124_mc_soc = { .smmu = &tegra124_smmu_soc, .emem_regs = tegra124_mc_emem_regs, .num_emem_regs = ARRAY_SIZE(tegra124_mc_emem_regs), - .intmask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | - MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | - MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + .intmasks = tegra124_mc_intmasks, + .num_intmasks = ARRAY_SIZE(tegra124_mc_intmasks), .reset_ops = &tegra_mc_reset_ops_common, .resets = tegra124_mc_resets, .num_resets = ARRAY_SIZE(tegra124_mc_resets), .icc_ops = &tegra124_mc_icc_ops, .ops = &tegra30_mc_ops, + .regs = &tegra20_mc_regs, + .handle_irq = tegra30_mc_irq_handlers, + .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers), + .mc_addr_hi_mask = 0x3, + .mc_err_status_type_mask = (0x7 << 28), }; #endif /* CONFIG_ARCH_TEGRA_124_SOC */ @@ -1292,6 +1305,15 @@ static const struct tegra_smmu_soc tegra132_smmu_soc = { .num_asids = 128, }; +static const struct tegra_mc_intmask tegra132_mc_intmasks[] = { + { + .reg = MC_INTMASK, + .mask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | + MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | + MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + }, +}; + const struct tegra_mc_soc tegra132_mc_soc = { .clients = tegra124_mc_clients, .num_clients = ARRAY_SIZE(tegra124_mc_clients), @@ -1299,13 +1321,17 @@ const struct tegra_mc_soc tegra132_mc_soc = { .atom_size = 32, .client_id_mask = 0x7f, .smmu = &tegra132_smmu_soc, - .intmask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | - MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | - MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + .intmasks = tegra132_mc_intmasks, + .num_intmasks = ARRAY_SIZE(tegra132_mc_intmasks), .reset_ops = &tegra_mc_reset_ops_common, .resets = tegra124_mc_resets, .num_resets = ARRAY_SIZE(tegra124_mc_resets), .icc_ops = &tegra124_mc_icc_ops, .ops = &tegra30_mc_ops, + .regs = &tegra20_mc_regs, + .handle_irq = tegra30_mc_irq_handlers, + .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers), + .mc_addr_hi_mask = 0x3, + .mc_err_status_type_mask = (0x7 << 28), }; #endif /* CONFIG_ARCH_TEGRA_132_SOC */ diff --git a/drivers/memory/tegra/tegra186-emc.c b/drivers/memory/tegra/tegra186-emc.c index dfddceecdd1a..03ebab6fbe68 100644 --- a/drivers/memory/tegra/tegra186-emc.c +++ b/drivers/memory/tegra/tegra186-emc.c @@ -22,6 +22,7 @@ struct tegra186_emc { struct tegra_bpmp *bpmp; struct device *dev; struct clk *clk; + struct clk *clk_dbb; struct tegra186_emc_dvfs *dvfs; unsigned int num_dvfs; @@ -328,6 +329,13 @@ static int tegra186_emc_probe(struct platform_device *pdev) goto put_bpmp; } + emc->clk_dbb = devm_clk_get_optional_enabled(&pdev->dev, "dbb"); + if (IS_ERR(emc->clk_dbb)) { + err = dev_err_probe(&pdev->dev, PTR_ERR(emc->clk_dbb), + "failed to get DBB clock\n"); + goto put_bpmp; + } + platform_set_drvdata(pdev, emc); emc->dev = &pdev->dev; diff --git a/drivers/memory/tegra/tegra186.c b/drivers/memory/tegra/tegra186.c index aee11457bf8e..91d56165605f 100644 --- a/drivers/memory/tegra/tegra186.c +++ b/drivers/memory/tegra/tegra186.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2017-2025 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2017-2026 NVIDIA CORPORATION. All rights reserved. */ #include @@ -174,7 +174,6 @@ const struct tegra_mc_ops tegra186_mc_ops = { .remove = tegra186_mc_remove, .resume = tegra186_mc_resume, .probe_device = tegra186_mc_probe_device, - .handle_irq = tegra30_mc_handle_irq, }; #if defined(CONFIG_ARCH_TEGRA_186_SOC) @@ -902,17 +901,30 @@ static const struct tegra_mc_client tegra186_mc_clients[] = { }, }; +static const struct tegra_mc_intmask tegra186_mc_intmasks[] = { + { + .reg = MC_INTMASK, + .mask = MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS | + MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | + MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + }, +}; + const struct tegra_mc_soc tegra186_mc_soc = { .num_clients = ARRAY_SIZE(tegra186_mc_clients), .clients = tegra186_mc_clients, .num_address_bits = 40, .num_channels = 4, .client_id_mask = 0xff, - .intmask = MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS | - MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | - MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + .intmasks = tegra186_mc_intmasks, + .num_intmasks = ARRAY_SIZE(tegra186_mc_intmasks), .ops = &tegra186_mc_ops, .ch_intmask = 0x0000000f, .global_intstatus_channel_shift = 0, + .regs = &tegra20_mc_regs, + .handle_irq = tegra30_mc_irq_handlers, + .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers), + .mc_addr_hi_mask = 0x3, + .mc_err_status_type_mask = (0x7 << 28), }; #endif diff --git a/drivers/memory/tegra/tegra194.c b/drivers/memory/tegra/tegra194.c index 26035ac3a1eb..a8cc57690696 100644 --- a/drivers/memory/tegra/tegra194.c +++ b/drivers/memory/tegra/tegra194.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2017-2021 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2017-2026 NVIDIA CORPORATION. All rights reserved. */ #include @@ -1343,19 +1343,31 @@ static const struct tegra_mc_client tegra194_mc_clients[] = { }, }; +static const struct tegra_mc_intmask tegra194_mc_intmasks[] = { + { + .reg = MC_INTMASK, + .mask = MC_INT_DECERR_ROUTE_SANITY | MC_INT_DECERR_GENERALIZED_CARVEOUT | + MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | + MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + }, +}; + const struct tegra_mc_soc tegra194_mc_soc = { .num_clients = ARRAY_SIZE(tegra194_mc_clients), .clients = tegra194_mc_clients, .num_address_bits = 40, .num_channels = 16, .client_id_mask = 0xff, - .intmask = MC_INT_DECERR_ROUTE_SANITY | - MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS | - MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | - MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + .intmasks = tegra194_mc_intmasks, + .num_intmasks = ARRAY_SIZE(tegra194_mc_intmasks), .has_addr_hi_reg = true, .ops = &tegra186_mc_ops, .icc_ops = &tegra_mc_icc_ops, .ch_intmask = 0x00000f00, .global_intstatus_channel_shift = 8, + .regs = &tegra20_mc_regs, + .handle_irq = tegra30_mc_irq_handlers, + .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers), + .mc_addr_hi_mask = 0x3, + .mc_err_status_type_mask = (0x7 << 28), }; diff --git a/drivers/memory/tegra/tegra20.c b/drivers/memory/tegra/tegra20.c index 4748113bfe9d..27dd6886f86e 100644 --- a/drivers/memory/tegra/tegra20.c +++ b/drivers/memory/tegra/tegra20.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2012-2026 NVIDIA CORPORATION. All rights reserved. */ #include @@ -695,7 +695,7 @@ static irqreturn_t tegra20_mc_handle_irq(int irq, void *data) unsigned int bit; /* mask all interrupts to avoid flooding */ - status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask; + status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmasks[0].mask; if (!status) return IRQ_NONE; @@ -713,7 +713,7 @@ static irqreturn_t tegra20_mc_handle_irq(int irq, void *data) value = mc_readl(mc, reg); id = value & mc->soc->client_id_mask; - desc = tegra_mc_error_names[2]; + desc = tegra20_mc_error_names[2]; if (value & BIT(31)) direction = "write"; @@ -724,7 +724,7 @@ static irqreturn_t tegra20_mc_handle_irq(int irq, void *data) value = mc_readl(mc, reg); id = (value >> 1) & mc->soc->client_id_mask; - desc = tegra_mc_error_names[2]; + desc = tegra20_mc_error_names[2]; if (value & BIT(0)) direction = "write"; @@ -736,7 +736,7 @@ static irqreturn_t tegra20_mc_handle_irq(int irq, void *data) id = value & mc->soc->client_id_mask; type = (value & BIT(30)) ? 4 : 3; - desc = tegra_mc_error_names[type]; + desc = tegra20_mc_error_names[type]; secure = "secure "; if (value & BIT(31)) @@ -761,9 +761,20 @@ static irqreturn_t tegra20_mc_handle_irq(int irq, void *data) return IRQ_HANDLED; } +static const irq_handler_t tegra20_mc_irq_handlers[] = { + tegra20_mc_handle_irq +}; + static const struct tegra_mc_ops tegra20_mc_ops = { .probe = tegra20_mc_probe, - .handle_irq = tegra20_mc_handle_irq, +}; + +static const struct tegra_mc_intmask tegra20_mc_intmasks[] = { + { + .reg = MC_INTMASK, + .mask = MC_INT_SECURITY_VIOLATION | MC_INT_INVALID_GART_PAGE | + MC_INT_DECERR_EMEM, + }, }; const struct tegra_mc_soc tegra20_mc_soc = { @@ -771,11 +782,15 @@ const struct tegra_mc_soc tegra20_mc_soc = { .num_clients = ARRAY_SIZE(tegra20_mc_clients), .num_address_bits = 32, .client_id_mask = 0x3f, - .intmask = MC_INT_SECURITY_VIOLATION | MC_INT_INVALID_GART_PAGE | - MC_INT_DECERR_EMEM, + .intmasks = tegra20_mc_intmasks, + .num_intmasks = ARRAY_SIZE(tegra20_mc_intmasks), .reset_ops = &tegra20_mc_reset_ops, .resets = tegra20_mc_resets, .num_resets = ARRAY_SIZE(tegra20_mc_resets), .icc_ops = &tegra20_mc_icc_ops, .ops = &tegra20_mc_ops, + .regs = &tegra20_mc_regs, + .handle_irq = tegra20_mc_irq_handlers, + .num_interrupts = ARRAY_SIZE(tegra20_mc_irq_handlers), + .mc_err_status_type_mask = (0x7 << 28), }; diff --git a/drivers/memory/tegra/tegra210-emc-table.c b/drivers/memory/tegra/tegra210-emc-table.c index 34a8785d2861..4b3c478b2743 100644 --- a/drivers/memory/tegra/tegra210-emc-table.c +++ b/drivers/memory/tegra/tegra210-emc-table.c @@ -70,19 +70,20 @@ static void tegra210_emc_table_device_release(struct reserved_mem *rmem, memunmap(timings); } -static const struct reserved_mem_ops tegra210_emc_table_ops = { - .device_init = tegra210_emc_table_device_init, - .device_release = tegra210_emc_table_device_release, -}; - -static int tegra210_emc_table_init(struct reserved_mem *rmem) +static int tegra210_emc_table_init(unsigned long node, + struct reserved_mem *rmem) { pr_debug("Tegra210 EMC table at %pa, size %lu bytes\n", &rmem->base, (unsigned long)rmem->size); - rmem->ops = &tegra210_emc_table_ops; - return 0; } + +static const struct reserved_mem_ops tegra210_emc_table_ops = { + .node_init = tegra210_emc_table_init, + .device_init = tegra210_emc_table_device_init, + .device_release = tegra210_emc_table_device_release, +}; + RESERVEDMEM_OF_DECLARE(tegra210_emc_table, "nvidia,tegra210-emc-table", - tegra210_emc_table_init); + &tegra210_emc_table_ops); diff --git a/drivers/memory/tegra/tegra210.c b/drivers/memory/tegra/tegra210.c index 3c2949c16fde..f58f3ef6f681 100644 --- a/drivers/memory/tegra/tegra210.c +++ b/drivers/memory/tegra/tegra210.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2015 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2015-2026 NVIDIA CORPORATION. All rights reserved. */ #include @@ -1273,6 +1273,15 @@ static const struct tegra_mc_reset tegra210_mc_resets[] = { TEGRA210_MC_RESET(TSECB, 0x970, 0x974, 13), }; +static const struct tegra_mc_intmask tegra210_mc_intmasks[] = { + { + .reg = MC_INTMASK, + .mask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | + MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | + MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + }, +}; + const struct tegra_mc_soc tegra210_mc_soc = { .clients = tegra210_mc_clients, .num_clients = ARRAY_SIZE(tegra210_mc_clients), @@ -1280,11 +1289,15 @@ const struct tegra_mc_soc tegra210_mc_soc = { .atom_size = 64, .client_id_mask = 0xff, .smmu = &tegra210_smmu_soc, - .intmask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | - MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | - MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + .intmasks = tegra210_mc_intmasks, + .num_intmasks = ARRAY_SIZE(tegra210_mc_intmasks), .reset_ops = &tegra_mc_reset_ops_common, .resets = tegra210_mc_resets, .num_resets = ARRAY_SIZE(tegra210_mc_resets), .ops = &tegra30_mc_ops, + .regs = &tegra20_mc_regs, + .handle_irq = tegra30_mc_irq_handlers, + .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers), + .mc_addr_hi_mask = 0x3, + .mc_err_status_type_mask = (0x7 << 28), }; diff --git a/drivers/memory/tegra/tegra234.c b/drivers/memory/tegra/tegra234.c index 5f57cea48b62..87b22038a5fb 100644 --- a/drivers/memory/tegra/tegra234.c +++ b/drivers/memory/tegra/tegra234.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2022-2023, NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2022-2026, NVIDIA CORPORATION. All rights reserved. */ #include @@ -1132,16 +1132,23 @@ static const struct tegra_mc_icc_ops tegra234_mc_icc_ops = { .set = tegra234_mc_icc_set, }; +static const struct tegra_mc_intmask tegra234_mc_intmasks[] = { + { + .reg = MC_INTMASK, + .mask = MC_INT_DECERR_ROUTE_SANITY | MC_INT_DECERR_GENERALIZED_CARVEOUT | + MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | + MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + }, +}; + const struct tegra_mc_soc tegra234_mc_soc = { .num_clients = ARRAY_SIZE(tegra234_mc_clients), .clients = tegra234_mc_clients, .num_address_bits = 40, .num_channels = 16, .client_id_mask = 0x1ff, - .intmask = MC_INT_DECERR_ROUTE_SANITY | - MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS | - MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | - MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + .intmasks = tegra234_mc_intmasks, + .num_intmasks = ARRAY_SIZE(tegra234_mc_intmasks), .has_addr_hi_reg = true, .ops = &tegra186_mc_ops, .icc_ops = &tegra234_mc_icc_ops, @@ -1152,4 +1159,9 @@ const struct tegra_mc_soc tegra234_mc_soc = { * supported. */ .num_carveouts = 32, + .regs = &tegra20_mc_regs, + .handle_irq = tegra30_mc_irq_handlers, + .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers), + .mc_addr_hi_mask = 0x3, + .mc_err_status_type_mask = (0x7 << 28), }; diff --git a/drivers/memory/tegra/tegra264.c b/drivers/memory/tegra/tegra264.c index 5203e6c11372..e43ef14da1ee 100644 --- a/drivers/memory/tegra/tegra264.c +++ b/drivers/memory/tegra/tegra264.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2025, NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2025-2026, NVIDIA CORPORATION. All rights reserved. */ #include @@ -188,6 +188,41 @@ static const struct tegra_mc_client tegra264_mc_clients[] = { }, }; +static const char *const tegra264_hub_error_names[32] = { + [0] = "coalescer error", + [1] = "SMMU BYPASS ALLOW error", + [2] = "Illegal tbugrp_id error", + [3] = "Malformed MSI request error", + [4] = "Read response with poison bit error", + [5] = "Restricted access violation error", + [6] = "Reserved PA error", +}; + +static const char *const tegra264_mc_error_names[4] = { + [1] = "EMEM decode error", + [2] = "TrustZone violation", + [3] = "Carveout violation", +}; + +static const char *const tegra264_rt_error_names[16] = { + [1] = "DECERR_PARTIAL_POPULATED", + [2] = "DECERR_SMMU_BYPASS", + [3] = "DECERR_INVALID_MMIO", + [4] = "DECERR_INVALID_GIC_MSI", + [5] = "DECERR_ATOMIC_SYSRAM", + [9] = "DECERR_REMOTE_REQ_PRE_BOOT", + [10] = "DECERR_ISO_OVER_C2C", + [11] = "DECERR_UNSUPPORTED_SBS_OPCODE", + [12] = "DECERR_SBS_REQ_OVER_SISO_LL", +}; + +/* + * MC instance aperture mapping for hubc registers + */ +static const int mc_hubc_aperture_number[5] = { + 7, 8, 9, 10, 11 +}; + /* * tegra264_mc_icc_set() - Pass MC client info to the BPMP-FW * @src: ICC node for Memory Controller's (MC) Client @@ -283,6 +318,312 @@ static int tegra264_mc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *pea return 0; } +static void mcf_log_fault(struct tegra_mc *mc, u32 channel, unsigned long mcf_ch_intstatus) +{ + unsigned int bit; + + for_each_set_bit(bit, &mcf_ch_intstatus, 32) { + const char *client = "unknown", *desc = "NA"; + u32 status_reg, status1_reg = 0, addr_reg, addr_hi_reg = 0, err_type_mask = 0; + u32 value, client_id, i, addr_hi_shift = 0, addr_hi_mask = 0, status1; + u32 mc_rw_bit = MC_ERR_STATUS_RW, mc_sec_bit = MC_ERR_STATUS_SECURITY; + phys_addr_t addr = 0; + u8 type; + + switch (BIT(bit)) { + case MC_INT_DECERR_EMEM: + case MC_INT_SECURITY_VIOLATION: + status_reg = mc->soc->regs->err_status; + addr_reg = mc->soc->regs->err_add; + addr_hi_reg = mc->soc->regs->err_add_hi; + err_type_mask = mc->soc->mc_err_status_type_mask; + break; + + case MC_INT_DECERR_VPR: + status_reg = mc->soc->regs->err_vpr_status; + addr_reg = mc->soc->regs->err_vpr_add; + addr_hi_shift = MC_ERR_STATUS_ADR_HI_SHIFT; + addr_hi_mask = mc->soc->mc_addr_hi_mask; + break; + + case MC_INT_SECERR_SEC: + status_reg = mc->soc->regs->err_sec_status; + addr_reg = mc->soc->regs->err_sec_add; + addr_hi_shift = MC_ERR_STATUS_ADR_HI_SHIFT; + addr_hi_mask = mc->soc->mc_addr_hi_mask; + break; + + case MC_INT_DECERR_MTS: + status_reg = mc->soc->regs->err_mts_status; + addr_reg = mc->soc->regs->err_mts_add; + addr_hi_shift = MC_ERR_STATUS_ADR_HI_SHIFT; + addr_hi_mask = mc->soc->mc_addr_hi_mask; + break; + + case MC_INT_DECERR_GENERALIZED_CARVEOUT: + status_reg = mc->soc->regs->err_gen_co_status; + status1_reg = MC_ERR_GENERALIZED_CARVEOUT_STATUS_1_0; + addr_reg = mc->soc->regs->err_gen_co_add; + addr_hi_shift = MC_ERR_STATUS_GSC_ADR_HI_SHIFT; + addr_hi_mask = MC_ERR_STATUS_GSC_ADR_HI_MASK; + break; + + case MC_INT_DECERR_ROUTE_SANITY: + case MC_INT_DECERR_ROUTE_SANITY_GIC_MSI: + status_reg = mc->soc->regs->err_route_status; + addr_reg = mc->soc->regs->err_route_add; + addr_hi_shift = MC_ERR_STATUS_RT_ADR_HI_SHIFT; + addr_hi_mask = mc->soc->mc_addr_hi_mask; + mc_sec_bit = MC_ERR_ROUTE_SANITY_SEC; + mc_rw_bit = MC_ERR_ROUTE_SANITY_RW; + err_type_mask = MC_ERR_STATUS_RT_TYPE_MASK; + break; + + default: + dev_err_ratelimited(mc->dev, "Incorrect MC interrupt mask\n"); + return; + } + + value = mc_ch_readl(mc, channel, status_reg); + if (addr_hi_reg) { + addr = mc_ch_readl(mc, channel, addr_hi_reg); + } else { + if (!status1_reg) { + addr = ((value >> addr_hi_shift) & addr_hi_mask); + } else { + status1 = mc_ch_readl(mc, channel, status1_reg); + addr = ((status1 >> addr_hi_shift) & addr_hi_mask); + } + } + + addr <<= 32; + addr |= mc_ch_readl(mc, channel, addr_reg); + + client_id = value & mc->soc->client_id_mask; + for (i = 0; i < mc->soc->num_clients; i++) { + if (mc->soc->clients[i].id == client_id) { + client = mc->soc->clients[i].name; + break; + } + } + + if (err_type_mask == MC_ERR_STATUS_RT_TYPE_MASK) { + type = (value & err_type_mask) >> + MC_ERR_STATUS_RT_TYPE_SHIFT; + desc = tegra264_rt_error_names[type]; + } else if (err_type_mask) { + type = (value & err_type_mask) >> + MC_ERR_STATUS_TYPE_SHIFT; + desc = tegra264_mc_error_names[type]; + } + + dev_err_ratelimited(mc->dev, "%s: %s %s @%pa: %s (%s)\n", + client, value & mc_sec_bit ? "secure" : "non-secure", + value & mc_rw_bit ? "write" : "read", &addr, + tegra_mc_status_names[bit] ?: "unknown", desc); + if (status1_reg) + dev_err_ratelimited(mc->dev, "gsc_apr_id=%u gsc_co_apr_id=%u\n", + ((status1 >> ERR_GENERALIZED_APERTURE_ID_SHIFT) + & ERR_GENERALIZED_APERTURE_ID_MASK), + ((status1 >> ERR_GENERALIZED_CARVEOUT_APERTURE_ID_SHIFT) + & ERR_GENERALIZED_CARVEOUT_APERTURE_ID_MASK)); + } + + /* clear interrupts */ + mc_ch_writel(mc, channel, mcf_ch_intstatus, MCF_INTSTATUS_0); +} + +static irqreturn_t handle_mcf_irq(int irq, void *data) +{ + struct tegra_mc *mc = data; + unsigned long common_intstat, intstatus; + u32 slice; + + /* Read MCF_COMMON_INTSTATUS0_0_0 from MCB block */ + common_intstat = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, MCF_COMMON_INTSTATUS0_0_0); + if (common_intstat == 0) { + dev_warn(mc->dev, "No interrupt in MCF\n"); + return IRQ_NONE; + } + + for_each_set_bit(slice, &common_intstat, 32) { + /* Find out the slice number on which interrupt occurred */ + if (slice > 4) { + dev_err(mc->dev, "Slice index out of bounds: %u\n", slice); + return IRQ_NONE; + } + + intstatus = mc_ch_readl(mc, slice, MCF_INTSTATUS_0); + if (intstatus != 0) + mcf_log_fault(mc, slice, intstatus); + } + + return IRQ_HANDLED; +} + +static void hub_log_fault(struct tegra_mc *mc, u32 hub, unsigned long hub_intstat) +{ + unsigned int bit; + + for_each_set_bit(bit, &hub_intstat, 32) { + const char *client = "unknown"; + u32 client_id, status_reg, value, i; + phys_addr_t addr = 0; + + switch (BIT(bit)) { + case MSS_HUB_COALESCER_ERR_INTMASK: + status_reg = MSS_HUB_COALESCE_ERR_STATUS_0; + addr = mc_ch_readl(mc, hub, MSS_HUB_COALESCE_ERR_ADR_HI_0); + addr <<= 32; + addr |= mc_ch_readl(mc, hub, MSS_HUB_COALESCE_ERR_ADR_0); + break; + + case MSS_HUB_SMMU_BYPASS_ALLOW_ERR_INTMASK: + status_reg = MSS_HUB_SMMU_BYPASS_ALLOW_ERR_STATUS_0; + break; + + case MSS_HUB_ILLEGAL_TBUGRP_ID_INTMASK: + status_reg = MSS_HUB_ILLEGAL_TBUGRP_ID_ERR_STATUS_0; + break; + + case MSS_HUB_MSI_ERR_INTMASK: + status_reg = MSS_HUB_MSI_ERR_STATUS_0; + break; + + case MSS_HUB_POISON_RSP_INTMASK: + status_reg = MSS_HUB_POISON_RSP_STATUS_0; + break; + + case MSS_HUB_RESTRICTED_ACCESS_ERR_INTMASK: + status_reg = MSS_HUB_RESTRICTED_ACCESS_ERR_STATUS_0; + break; + + case MSS_HUB_RESERVED_PA_ERR_INTMASK: + status_reg = MSS_HUB_RESERVED_PA_ERR_STATUS_0; + break; + + default: + dev_err_ratelimited(mc->dev, "Incorrect HUB interrupt mask\n"); + return; + } + + value = mc_ch_readl(mc, hub, status_reg); + + client_id = value & mc->soc->client_id_mask; + for (i = 0; i < mc->soc->num_clients; i++) { + if (mc->soc->clients[i].id == client_id) { + client = mc->soc->clients[i].name; + break; + } + } + + dev_err_ratelimited(mc->dev, "%s: @%pa: %s status: 0x%x\n", + client, &addr, tegra264_hub_error_names[bit] ?: "unknown", + value); + } + + /* clear interrupts */ + mc_ch_writel(mc, hub, hub_intstat, MSS_HUB_INTRSTATUS_0); +} + +static irqreturn_t handle_hub_irq(int irq, void *data, int mc_hubc_aperture_number) +{ + struct tegra_mc *mc = data; + u32 global_intstat; + unsigned long hub_interrupt, intstat, hub; + + /* Read MSS_HUB_GLOBAL_INTSTATUS_0 from mc_hubc_aperture_number block */ + global_intstat = mc_ch_readl(mc, mc_hubc_aperture_number, MSS_HUB_GLOBAL_INTSTATUS_0); + if (global_intstat == 0) { + dev_warn(mc->dev, "No interrupt in HUB/HUBC\n"); + return IRQ_NONE; + } + + /* Handle interrupt from hubc */ + if (global_intstat & MSS_HUBC_INTR) { + /* Read MSS_HUB_HUBC_INTSTATUS_0 from block mc_hubc_aperture_number */ + intstat = mc_ch_readl(mc, mc_hubc_aperture_number, MSS_HUB_HUBC_INTSTATUS_0); + if (intstat != 0) { + dev_err_ratelimited(mc->dev, "Scrubber operation status: 0x%lx\n", + intstat); + /* Clear hubc interrupt */ + mc_ch_writel(mc, mc_hubc_aperture_number, intstat, + MSS_HUB_HUBC_INTSTATUS_0); + } + } + + hub_interrupt = (global_intstat & MSS_HUB_GLOBAL_MASK) >> MSS_HUB_GLOBAL_SHIFT; + /* Handle interrupt from hub */ + for_each_set_bit(hub, &hub_interrupt, 32) { + /* Read MSS_HUB_INTRSTATUS_0 from block MCi */ + intstat = mc_ch_readl(mc, hub, MSS_HUB_INTRSTATUS_0); + if (intstat != 0) + hub_log_fault(mc, hub, intstat); + } + + /* Clear global interrupt status register */ + mc_ch_writel(mc, mc_hubc_aperture_number, global_intstat, MSS_HUB_GLOBAL_INTSTATUS_0); + return IRQ_HANDLED; +} + +static irqreturn_t handle_disp_hub_irq(int irq, void *data) +{ + return handle_hub_irq(irq, data, mc_hubc_aperture_number[0]); +} + +static irqreturn_t handle_system_hub_irq(int irq, void *data) +{ + return handle_hub_irq(irq, data, mc_hubc_aperture_number[1]); +} + +static irqreturn_t handle_vision_hub_irq(int irq, void *data) +{ + return handle_hub_irq(irq, data, mc_hubc_aperture_number[2]); +} + +static irqreturn_t handle_uphy_hub_irq(int irq, void *data) +{ + return handle_hub_irq(irq, data, mc_hubc_aperture_number[3]); +} + +static irqreturn_t handle_top_hub_irq(int irq, void *data) +{ + return handle_hub_irq(irq, data, mc_hubc_aperture_number[4]); +} + +static irqreturn_t handle_generic_irq(struct tegra_mc *mc, unsigned long intstat_reg) +{ + u32 intstat, i; + + /* Iterate over all MC blocks to read INTSTATUS */ + for (i = 0; i < mc->num_channels; i++) { + intstat = mc_ch_readl(mc, i, intstat_reg); + if (intstat) { + dev_err_ratelimited(mc->dev, "channel: %i status: 0x%x\n", i, intstat); + /* Clear interrupt */ + mc_ch_writel(mc, i, intstat, intstat_reg); + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t handle_sbs_irq(int irq, void *data) +{ + return handle_generic_irq((struct tegra_mc *)data, MSS_SBS_INTSTATUS_0); +} + +static irqreturn_t handle_channel_irq(int irq, void *data) +{ + return handle_generic_irq((struct tegra_mc *)data, MC_CH_INTSTATUS_0); +} + +static const irq_handler_t tegra264_mc_irq_handlers[8] = { + handle_mcf_irq, handle_disp_hub_irq, handle_vision_hub_irq, + handle_system_hub_irq, handle_uphy_hub_irq, handle_top_hub_irq, + handle_sbs_irq, handle_channel_irq +}; + static const struct tegra_mc_icc_ops tegra264_mc_icc_ops = { .xlate = tegra_mc_icc_xlate, .aggregate = tegra264_mc_icc_aggregate, @@ -290,16 +631,80 @@ static const struct tegra_mc_icc_ops tegra264_mc_icc_ops = { .set = tegra264_mc_icc_set, }; +static const struct tegra_mc_regs tegra264_mc_regs = { + .cfg_channel_enable = 0x8870, + .err_status = 0xbc00, + .err_add = 0xbc04, + .err_add_hi = 0xbc08, + .err_vpr_status = 0xbc20, + .err_vpr_add = 0xbc24, + .err_sec_status = 0xbc3c, + .err_sec_add = 0xbc40, + .err_mts_status = 0xbc5c, + .err_mts_add = 0xbc60, + .err_gen_co_status = 0xbc78, + .err_gen_co_add = 0xbc7c, + .err_route_status = 0xbc64, + .err_route_add = 0xbc68, +}; + +static const struct tegra_mc_intmask tegra264_mc_intmasks[] = { + { + .reg = MCF_INTMASK_0, + .mask = MC_INT_DECERR_ROUTE_SANITY_GIC_MSI | MC_INT_DECERR_ROUTE_SANITY | + MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS | + MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | MC_INT_SECURITY_VIOLATION | + MC_INT_DECERR_EMEM, + }, + { + .reg = MCF_INTPRIORITY_0, + .mask = MC_INT_DECERR_ROUTE_SANITY_GIC_MSI | MC_INT_DECERR_ROUTE_SANITY | + MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS | + MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | MC_INT_SECURITY_VIOLATION | + MC_INT_DECERR_EMEM, + }, + { + .reg = MSS_HUB_INTRMASK_0, + .mask = MSS_HUB_COALESCER_ERR_INTMASK | MSS_HUB_SMMU_BYPASS_ALLOW_ERR_INTMASK | + MSS_HUB_ILLEGAL_TBUGRP_ID_INTMASK | MSS_HUB_MSI_ERR_INTMASK | + MSS_HUB_POISON_RSP_INTMASK | MSS_HUB_RESTRICTED_ACCESS_ERR_INTMASK | + MSS_HUB_RESERVED_PA_ERR_INTMASK, + }, + { + .reg = MSS_HUB_INTRPRIORITY_0, + .mask = MSS_HUB_COALESCER_ERR_INTMASK | MSS_HUB_SMMU_BYPASS_ALLOW_ERR_INTMASK | + MSS_HUB_ILLEGAL_TBUGRP_ID_INTMASK | MSS_HUB_MSI_ERR_INTMASK | + MSS_HUB_POISON_RSP_INTMASK | MSS_HUB_RESTRICTED_ACCESS_ERR_INTMASK | + MSS_HUB_RESERVED_PA_ERR_INTMASK, + }, + { + .reg = MSS_HUB_HUBC_INTMASK_0, + .mask = MSS_HUB_HUBC_SCRUB_DONE_INTMASK, + }, + { + .reg = MSS_HUB_HUBC_INTPRIORITY_0, + .mask = MSS_HUB_HUBC_SCRUB_DONE_INTMASK, + }, + { + .reg = MSS_SBS_INTMASK_0, + .mask = MSS_SBS_FILL_FIFO_ISO_OVERFLOW_INTMASK | + MSS_SBS_FILL_FIFO_SISO_OVERFLOW_INTMASK | + MSS_SBS_FILL_FIFO_NISO_OVERFLOW_INTMASK, + }, + { + .reg = MC_CH_INTMASK_0, + .mask = WCAM_ERR_INTMASK, + } +}; + const struct tegra_mc_soc tegra264_mc_soc = { .num_clients = ARRAY_SIZE(tegra264_mc_clients), .clients = tegra264_mc_clients, .num_address_bits = 40, .num_channels = 16, .client_id_mask = 0x1ff, - .intmask = MC_INT_DECERR_ROUTE_SANITY | - MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS | - MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | - MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, + .intmasks = tegra264_mc_intmasks, + .num_intmasks = ARRAY_SIZE(tegra264_mc_intmasks), .has_addr_hi_reg = true, .ops = &tegra186_mc_ops, .icc_ops = &tegra264_mc_icc_ops, @@ -310,4 +715,9 @@ const struct tegra_mc_soc tegra264_mc_soc = { * supported. */ .num_carveouts = 32, + .mc_addr_hi_mask = 0xff, + .mc_err_status_type_mask = (0x3 << 28), + .regs = &tegra264_mc_regs, + .handle_irq = tegra264_mc_irq_handlers, + .num_interrupts = ARRAY_SIZE(tegra264_mc_irq_handlers), }; diff --git a/drivers/memory/tegra/tegra30-emc.c b/drivers/memory/tegra/tegra30-emc.c index 606106dd2b32..5812c8cd6ce4 100644 --- a/drivers/memory/tegra/tegra30-emc.c +++ b/drivers/memory/tegra/tegra30-emc.c @@ -554,14 +554,14 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate) emc->emc_cfg = readl_relaxed(emc->regs + EMC_CFG); emc_dbg = readl_relaxed(emc->regs + EMC_DBG); - if (emc->dll_on == !!(timing->emc_mode_1 & 0x1)) + if (emc->dll_on == !(timing->emc_mode_1 & 0x1)) dll_change = DLL_CHANGE_NONE; - else if (timing->emc_mode_1 & 0x1) + else if (!(timing->emc_mode_1 & 0x1)) dll_change = DLL_CHANGE_ON; else dll_change = DLL_CHANGE_OFF; - emc->dll_on = !!(timing->emc_mode_1 & 0x1); + emc->dll_on = !(timing->emc_mode_1 & 0x1); if (timing->data[80] && !readl_relaxed(emc->regs + EMC_ZCAL_INTERVAL)) emc->zcal_long = true; diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c index a6bcde4b92c0..8389e3af0121 100644 --- a/drivers/memory/tegra/tegra30.c +++ b/drivers/memory/tegra/tegra30.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved. */ #include @@ -1384,6 +1384,14 @@ static const struct tegra_mc_icc_ops tegra30_mc_icc_ops = { .set = tegra30_mc_icc_set, }; +static const struct tegra_mc_intmask tegra30_mc_intmasks[] = { + { + .reg = MC_INTMASK, + .mask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | + MC_INT_DECERR_EMEM, + }, +}; + const struct tegra_mc_soc tegra30_mc_soc = { .clients = tegra30_mc_clients, .num_clients = ARRAY_SIZE(tegra30_mc_clients), @@ -1393,11 +1401,15 @@ const struct tegra_mc_soc tegra30_mc_soc = { .smmu = &tegra30_smmu_soc, .emem_regs = tegra30_mc_emem_regs, .num_emem_regs = ARRAY_SIZE(tegra30_mc_emem_regs), - .intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | - MC_INT_DECERR_EMEM, + .intmasks = tegra30_mc_intmasks, + .num_intmasks = ARRAY_SIZE(tegra30_mc_intmasks), .reset_ops = &tegra_mc_reset_ops_common, .resets = tegra30_mc_resets, .num_resets = ARRAY_SIZE(tegra30_mc_resets), .icc_ops = &tegra30_mc_icc_ops, .ops = &tegra30_mc_ops, + .regs = &tegra20_mc_regs, + .handle_irq = tegra30_mc_irq_handlers, + .num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers), + .mc_err_status_type_mask = (0x7 << 28), }; diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c index c3f3d39bf584..0b541c0d3b1b 100644 --- a/drivers/mfd/atmel-hlcdc.c +++ b/drivers/mfd/atmel-hlcdc.c @@ -108,10 +108,19 @@ static int atmel_hlcdc_probe(struct platform_device *pdev) return PTR_ERR(hlcdc->periph_clk); } + /* + * Retrieve one of the primary clocks required for LCD operation: + * prefer sys_clk (for RGB/MIPI), and fall back to lvds_pll_clk + * (for LVDS) if needed. + */ hlcdc->sys_clk = devm_clk_get(dev, "sys_clk"); if (IS_ERR(hlcdc->sys_clk)) { - dev_err(dev, "failed to get system clock\n"); - return PTR_ERR(hlcdc->sys_clk); + hlcdc->sys_clk = NULL; + hlcdc->lvds_pll_clk = devm_clk_get(dev, "lvds_pll_clk"); + if (IS_ERR(hlcdc->lvds_pll_clk)) { + dev_err(dev, "Failed to obtain both the LCDC (generic) and LVDS PLL clocks\n"); + return PTR_ERR(hlcdc->lvds_pll_clk); + } } hlcdc->slow_clk = devm_clk_get(dev, "slow_clk"); diff --git a/drivers/mfd/bcm2835-pm.c b/drivers/mfd/bcm2835-pm.c index 8bed59816e82..9e8e3dcf4bce 100644 --- a/drivers/mfd/bcm2835-pm.c +++ b/drivers/mfd/bcm2835-pm.c @@ -81,6 +81,7 @@ static int bcm2835_pm_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pm); pm->dev = dev; + pm->soc = (uintptr_t)device_get_match_data(dev); ret = bcm2835_pm_get_pdata(pdev, pm); if (ret) @@ -97,7 +98,7 @@ static int bcm2835_pm_probe(struct platform_device *pdev) * bcm2835-pm binding as the key for whether we can reference * the full PM register range and support power domains. */ - if (pm->asb) + if (pm->asb || pm->soc == BCM2835_PM_SOC_BCM2712) return devm_mfd_add_devices(dev, -1, bcm2835_power_devs, ARRAY_SIZE(bcm2835_power_devs), NULL, 0, NULL); @@ -106,9 +107,9 @@ static int bcm2835_pm_probe(struct platform_device *pdev) static const struct of_device_id bcm2835_pm_of_match[] = { { .compatible = "brcm,bcm2835-pm-wdt", }, - { .compatible = "brcm,bcm2835-pm", }, - { .compatible = "brcm,bcm2711-pm", }, - { .compatible = "brcm,bcm2712-pm", }, + { .compatible = "brcm,bcm2835-pm", .data = (void *)BCM2835_PM_SOC_BCM2835 }, + { .compatible = "brcm,bcm2711-pm", .data = (void *)BCM2835_PM_SOC_BCM2711 }, + { .compatible = "brcm,bcm2712-pm", .data = (void *)BCM2835_PM_SOC_BCM2712 }, {}, }; MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match); diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c index d12510e391c8..0b1e7fd7f5d7 100644 --- a/drivers/mfd/dln2.c +++ b/drivers/mfd/dln2.c @@ -424,8 +424,8 @@ static void free_rx_slot(struct dln2_dev *dln2, u16 handle, int slot) } static int _dln2_transfer(struct dln2_dev *dln2, u16 handle, u16 cmd, - const void *obuf, unsigned obuf_len, - void *ibuf, unsigned *ibuf_len) + const void *obuf, unsigned int obuf_len, + void *ibuf, unsigned int *ibuf_len) { int ret = 0; int rx_slot; @@ -511,8 +511,8 @@ out_decr: } int dln2_transfer(struct platform_device *pdev, u16 cmd, - const void *obuf, unsigned obuf_len, - void *ibuf, unsigned *ibuf_len) + const void *obuf, unsigned int obuf_len, + void *ibuf, unsigned int *ibuf_len) { struct dln2_platform_data *dln2_pdata; struct dln2_dev *dln2; @@ -583,10 +583,8 @@ static void dln2_free_rx_urbs(struct dln2_dev *dln2) { int i; - for (i = 0; i < DLN2_MAX_URBS; i++) { + for (i = 0; i < DLN2_MAX_URBS; i++) usb_free_urb(dln2->rx_urb[i]); - kfree(dln2->rx_buf[i]); - } } static void dln2_stop_rx_urbs(struct dln2_dev *dln2) @@ -600,8 +598,6 @@ static void dln2_stop_rx_urbs(struct dln2_dev *dln2) static void dln2_free(struct dln2_dev *dln2) { dln2_free_rx_urbs(dln2); - usb_put_dev(dln2->usb_dev); - kfree(dln2); } static int dln2_setup_rx_urbs(struct dln2_dev *dln2, @@ -609,9 +605,10 @@ static int dln2_setup_rx_urbs(struct dln2_dev *dln2, { int i; const int rx_max_size = DLN2_RX_BUF_SIZE; + struct device *dev = &dln2->interface->dev; for (i = 0; i < DLN2_MAX_URBS; i++) { - dln2->rx_buf[i] = kmalloc(rx_max_size, GFP_KERNEL); + dln2->rx_buf[i] = devm_kmalloc(dev, rx_max_size, GFP_KERNEL); if (!dln2->rx_buf[i]) return -ENOMEM; @@ -778,13 +775,13 @@ static int dln2_probe(struct usb_interface *interface, if (ret) return ret; - dln2 = kzalloc_obj(*dln2); + dln2 = devm_kzalloc(dev, sizeof(*dln2), GFP_KERNEL); if (!dln2) return -ENOMEM; dln2->ep_out = epout->bEndpointAddress; dln2->ep_in = epin->bEndpointAddress; - dln2->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + dln2->usb_dev = interface_to_usbdev(interface); dln2->interface = interface; usb_set_intfdata(interface, dln2); init_waitqueue_head(&dln2->disconnect_wq); diff --git a/drivers/mfd/ene-kb3930.c b/drivers/mfd/ene-kb3930.c index 9460a67acb0b..086e0758d4cc 100644 --- a/drivers/mfd/ene-kb3930.c +++ b/drivers/mfd/ene-kb3930.c @@ -157,7 +157,7 @@ static int kb3930_probe(struct i2c_client *client) if (ret) return ret; - if (of_property_read_bool(np, "system-power-controller")) { + if (of_device_is_system_power_controller(np)) { ddata->off_gpios = devm_gpiod_get_array_optional(dev, "off", GPIOD_IN); if (IS_ERR(ddata->off_gpios)) diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c index 24ca140d6a48..9a685ff8cd15 100644 --- a/drivers/mfd/ezx-pcap.c +++ b/drivers/mfd/ezx-pcap.c @@ -375,8 +375,6 @@ static void ezx_pcap_remove(struct spi_device *spi) /* cleanup irqchip */ for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) irq_set_chip_and_handler(i, NULL, NULL); - - destroy_workqueue(pcap->workqueue); } static int ezx_pcap_probe(struct spi_device *spi) @@ -384,17 +382,15 @@ static int ezx_pcap_probe(struct spi_device *spi) struct pcap_platform_data *pdata = dev_get_platdata(&spi->dev); struct pcap_chip *pcap; int i, adc_irq; - int ret = -ENODEV; + int ret; /* platform data is required */ if (!pdata) - goto ret; + return -ENODEV; pcap = devm_kzalloc(&spi->dev, sizeof(*pcap), GFP_KERNEL); - if (!pcap) { - ret = -ENOMEM; - goto ret; - } + if (!pcap) + return -ENOMEM; spin_lock_init(&pcap->io_lock); spin_lock_init(&pcap->adc_lock); @@ -407,18 +403,15 @@ static int ezx_pcap_probe(struct spi_device *spi) spi->mode = SPI_MODE_0 | (pdata->config & PCAP_CS_AH ? SPI_CS_HIGH : 0); ret = spi_setup(spi); if (ret) - goto ret; + return ret; pcap->spi = spi; /* setup irq */ pcap->irq_base = pdata->irq_base; - pcap->workqueue = create_singlethread_workqueue("pcapd"); - if (!pcap->workqueue) { - ret = -ENOMEM; - dev_err(&spi->dev, "can't create pcap thread\n"); - goto ret; - } + pcap->workqueue = devm_alloc_ordered_workqueue(&spi->dev, "pcapd", 0); + if (!pcap->workqueue) + return -ENOMEM; /* redirect interrupts to AP, except adcdone2 */ if (!(pdata->config & PCAP_SECOND_PORT)) @@ -468,9 +461,7 @@ remove_subdevs: free_irqchip: for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) irq_set_chip_and_handler(i, NULL, NULL); -/* destroy_workqueue: */ - destroy_workqueue(pcap->workqueue); -ret: + return ret; } diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index 713a5bfb1a3c..a9452ac92fb2 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -633,6 +633,19 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0xa879), (kernel_ulong_t)&ehl_i2c_info }, { PCI_VDEVICE(INTEL, 0xa87a), (kernel_ulong_t)&ehl_i2c_info }, { PCI_VDEVICE(INTEL, 0xa87b), (kernel_ulong_t)&ehl_i2c_info }, + /* NVL-H */ + { PCI_VDEVICE(INTEL, 0xd325), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xd326), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xd327), (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xd330), (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xd347), (kernel_ulong_t)&tgl_spi_info }, + { PCI_VDEVICE(INTEL, 0xd350), (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xd351), (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xd352), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0xd378), (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xd379), (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xd37a), (kernel_ulong_t)&ehl_i2c_info }, + { PCI_VDEVICE(INTEL, 0xd37b), (kernel_ulong_t)&ehl_i2c_info }, /* PTL-H */ { PCI_VDEVICE(INTEL, 0xe325), (kernel_ulong_t)&bxt_uart_info }, { PCI_VDEVICE(INTEL, 0xe326), (kernel_ulong_t)&bxt_uart_info }, diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 4b7d0cb9340f..5a3d79f339dd 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -125,11 +126,17 @@ static struct mfd_cell lpc_ich_wdt_cell = { .ignore_resource_conflicts = true, }; +const struct software_node lpc_ich_gpio_swnode = { + .name = "gpio_ich", +}; +EXPORT_SYMBOL_NS(lpc_ich_gpio_swnode, "LPC_ICH"); + static struct mfd_cell lpc_ich_gpio_cell = { .name = "gpio_ich", .num_resources = ARRAY_SIZE(gpio_ich_res), .resources = gpio_ich_res, .ignore_resource_conflicts = true, + .swnode = &lpc_ich_gpio_swnode, }; #define INTEL_GPIO_RESOURCE_SIZE 0x1000 diff --git a/drivers/mfd/macsmc.c b/drivers/mfd/macsmc.c index 1b7e7b3e785f..358feec2d088 100644 --- a/drivers/mfd/macsmc.c +++ b/drivers/mfd/macsmc.c @@ -46,6 +46,7 @@ static const struct mfd_cell apple_smc_devs[] = { MFD_CELL_NAME("macsmc-input"), + MFD_CELL_NAME("macsmc-power"), MFD_CELL_OF("macsmc-gpio", NULL, NULL, 0, 0, "apple,smc-gpio"), MFD_CELL_OF("macsmc-hwmon", NULL, NULL, 0, 0, "apple,smc-hwmon"), MFD_CELL_OF("macsmc-reboot", NULL, NULL, 0, 0, "apple,smc-reboot"), diff --git a/drivers/mfd/max77705.c b/drivers/mfd/max77705.c index e1a9bfd65856..e98c76d6b699 100644 --- a/drivers/mfd/max77705.c +++ b/drivers/mfd/max77705.c @@ -157,7 +157,7 @@ static int max77705_resume(struct device *dev) return 0; } -DEFINE_SIMPLE_DEV_PM_OPS(max77705_pm_ops, max77705_suspend, max77705_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(max77705_pm_ops, max77705_suspend, max77705_resume); static const struct of_device_id max77705_i2c_of_match[] = { { .compatible = "maxim,max77705" }, diff --git a/drivers/mfd/max77759.c b/drivers/mfd/max77759.c index a7efe233ec8c..9fa6027a92c4 100644 --- a/drivers/mfd/max77759.c +++ b/drivers/mfd/max77759.c @@ -201,8 +201,24 @@ static const struct regmap_config max77759_regmap_config_charger = { * - SYSUVLO_INT * - FSHIP_NOT_RD * - CHGR_INT: charger - * - CHG_INT - * - CHG_INT2 + * - INT1 + * - AICL + * - CHGIN + * - WCIN + * - CHG + * - BAT + * - INLIM + * - THM2 + * - BYP + * - INT2 + * - INSEL + * - SYS_UVLO1 + * - SYS_UVLO2 + * - BAT_OILO + * - CHG_STA_CC + * - CHG_STA_CV + * - CHG_STA_TO + * - CHG_STA_DONE */ enum { MAX77759_INT_MAXQ, @@ -228,8 +244,22 @@ enum { }; enum { - MAX77759_CHARGER_INT_1, - MAX77759_CHARGER_INT_2, + MAX77759_CHGR_INT1_AICL, + MAX77759_CHGR_INT1_CHGIN, + MAX77759_CHGR_INT1_WCIN, + MAX77759_CHGR_INT1_CHG, + MAX77759_CHGR_INT1_BAT, + MAX77759_CHGR_INT1_INLIM, + MAX77759_CHGR_INT1_THM2, + MAX77759_CHGR_INT1_BYP, + MAX77759_CHGR_INT2_INSEL, + MAX77759_CHGR_INT2_SYS_UVLO1, + MAX77759_CHGR_INT2_SYS_UVLO2, + MAX77759_CHGR_INT2_BAT_OILO, + MAX77759_CHGR_INT2_CHG_STA_CC, + MAX77759_CHGR_INT2_CHG_STA_CV, + MAX77759_CHGR_INT2_CHG_STA_TO, + MAX77759_CHGR_INT2_CHG_STA_DONE, }; static const struct regmap_irq max77759_pmic_irqs[] = { @@ -256,8 +286,38 @@ static const struct regmap_irq max77759_topsys_irqs[] = { }; static const struct regmap_irq max77759_chgr_irqs[] = { - REGMAP_IRQ_REG(MAX77759_CHARGER_INT_1, 0, GENMASK(7, 0)), - REGMAP_IRQ_REG(MAX77759_CHARGER_INT_2, 1, GENMASK(7, 0)), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_AICL, 0, + MAX77759_CHGR_REG_CHG_INT_AICL), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_CHGIN, 0, + MAX77759_CHGR_REG_CHG_INT_CHGIN), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_WCIN, 0, + MAX77759_CHGR_REG_CHG_INT_WCIN), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_CHG, 0, + MAX77759_CHGR_REG_CHG_INT_CHG), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_BAT, 0, + MAX77759_CHGR_REG_CHG_INT_BAT), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_INLIM, 0, + MAX77759_CHGR_REG_CHG_INT_INLIM), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_THM2, 0, + MAX77759_CHGR_REG_CHG_INT_THM2), + REGMAP_IRQ_REG(MAX77759_CHGR_INT1_BYP, 0, + MAX77759_CHGR_REG_CHG_INT_BYP), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_INSEL, 1, + MAX77759_CHGR_REG_CHG_INT2_INSEL), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_SYS_UVLO1, 1, + MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO1), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_SYS_UVLO2, 1, + MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO2), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_BAT_OILO, 1, + MAX77759_CHGR_REG_CHG_INT2_BAT_OILO), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_CC, 1, + MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CC), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_CV, 1, + MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CV), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_TO, 1, + MAX77759_CHGR_REG_CHG_INT2_CHG_STA_TO), + REGMAP_IRQ_REG(MAX77759_CHGR_INT2_CHG_STA_DONE, 1, + MAX77759_CHGR_REG_CHG_INT2_CHG_STA_DONE), }; static const struct regmap_irq_chip max77759_pmic_irq_chip = { @@ -297,11 +357,12 @@ static const struct regmap_irq_chip max77759_topsys_irq_chip = { .num_irqs = ARRAY_SIZE(max77759_topsys_irqs), }; -static const struct regmap_irq_chip max77759_chrg_irq_chip = { +static const struct regmap_irq_chip max77759_chgr_irq_chip = { .name = "max77759-chgr", .domain_suffix = "CHGR", .status_base = MAX77759_CHGR_REG_CHG_INT, .mask_base = MAX77759_CHGR_REG_CHG_INT_MASK, + .ack_base = MAX77759_CHGR_REG_CHG_INT, .num_regs = 2, .irqs = max77759_chgr_irqs, .num_irqs = ARRAY_SIZE(max77759_chgr_irqs), @@ -325,8 +386,22 @@ static const struct resource max77759_gpio_resources[] = { }; static const struct resource max77759_charger_resources[] = { - DEFINE_RES_IRQ_NAMED(MAX77759_CHARGER_INT_1, "INT1"), - DEFINE_RES_IRQ_NAMED(MAX77759_CHARGER_INT_2, "INT2"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_AICL, "AICL"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_CHGIN, "CHGIN"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_WCIN, "WCIN"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_CHG, "CHG"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_BAT, "BAT"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_INLIM, "INLIM"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_THM2, "THM2"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT1_BYP, "BYP"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_INSEL, "INSEL"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_SYS_UVLO1, "SYS_UVLO1"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_SYS_UVLO2, "SYS_UVLO2"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_BAT_OILO, "BAT_OILO"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_CC, "CHG_STA_CC"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_CV, "CHG_STA_CV"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_TO, "CHG_STA_TO"), + DEFINE_RES_IRQ_NAMED(MAX77759_CHGR_INT2_CHG_STA_DONE, "CHG_STA_DONE"), }; static const struct mfd_cell max77759_cells[] = { @@ -567,7 +642,7 @@ static int max77759_add_chained_charger(struct i2c_client *client, max77759->regmap_charger, MAX77759_INT_CHGR, parent, - &max77759_chrg_irq_chip, + &max77759_chgr_irq_chip, &irq_chip_data); if (ret) return ret; diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 920797b806ce..786eab3b2d03 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -377,7 +377,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, if (snprintf(buf, sizeof(buf), format, name) > sizeof(buf)) return -E2BIG; - cell.name = kmemdup(buf, strlen(buf) + 1, GFP_KERNEL); + cell.name = devm_kmemdup(mc13xxx->dev, buf, strlen(buf) + 1, GFP_KERNEL); if (!cell.name) return -ENOMEM; diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 6be58eb5a746..7aa32b90cf1e 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -88,7 +88,17 @@ static void mfd_acpi_add_device(const struct mfd_cell *cell, } } - device_set_node(&pdev->dev, acpi_fwnode_handle(adev ?: parent)); + /* + * NOTE: The fwnode design doesn't allow proper stacking/sharing. This + * should eventually turn into a device fwnode API call that will allow + * prepending to a list of fwnodes (with ACPI taking precedence). + * + * set_primary_fwnode() is used here, instead of device_set_node(), as + * device_set_node() will overwrite the existing fwnode, which may be an + * OF node that was populated earlier. To support a use case where ACPI + * and OF is used in conjunction, we call set_primary_fwnode() instead. + */ + set_primary_fwnode(&pdev->dev, acpi_fwnode_handle(adev ?: parent)); } #else static inline void mfd_acpi_add_device(const struct mfd_cell *cell, diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index 3e58d0764c7e..1bdacda9a933 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -297,7 +297,7 @@ static const struct chip_data mt6323_core = { static const struct chip_data mt6328_core = { .cid_addr = MT6328_HWCID, - .cid_shift = 0, + .cid_shift = 8, .cells = mt6328_devs, .cell_size = ARRAY_SIZE(mt6328_devs), .irq_init = mt6397_irq_init, @@ -313,7 +313,7 @@ static const struct chip_data mt6357_core = { static const struct chip_data mt6331_mt6332_core = { .cid_addr = MT6331_HWCID, - .cid_shift = 0, + .cid_shift = 8, .cells = mt6331_mt6332_devs, .cell_size = ARRAY_SIZE(mt6331_mt6332_devs), .irq_init = mt6397_irq_init, diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c index e54152a03510..a79f354bf5cb 100644 --- a/drivers/mfd/rohm-bd71828.c +++ b/drivers/mfd/rohm-bd71828.c @@ -41,6 +41,7 @@ static struct gpio_keys_button button = { .code = KEY_POWER, .gpio = -1, .type = EV_KEY, + .wakeup = 1, }; static const struct gpio_keys_platform_data bd71828_powerkey_data = { diff --git a/drivers/mfd/sec-acpm.c b/drivers/mfd/sec-acpm.c index 537ea65685bf..0e23b9d9f7ee 100644 --- a/drivers/mfd/sec-acpm.c +++ b/drivers/mfd/sec-acpm.c @@ -367,7 +367,7 @@ static const struct regmap_config s2mpg11_regmap_config_meter = { }; struct sec_pmic_acpm_shared_bus_context { - const struct acpm_handle *acpm; + struct acpm_handle *acpm; unsigned int acpm_chan_id; u8 speedy_channel; }; @@ -390,7 +390,7 @@ static int sec_pmic_acpm_bus_write(void *context, const void *data, size_t count) { struct sec_pmic_acpm_bus_context *ctx = context; - const struct acpm_handle *acpm = ctx->shared->acpm; + struct acpm_handle *acpm = ctx->shared->acpm; const struct acpm_pmic_ops *pmic_ops = &acpm->ops.pmic_ops; size_t val_count = count - BITS_TO_BYTES(ACPM_ADDR_BITS); const u8 *d = data; @@ -410,7 +410,7 @@ static int sec_pmic_acpm_bus_read(void *context, const void *reg_buf, size_t reg void *val_buf, size_t val_size) { struct sec_pmic_acpm_bus_context *ctx = context; - const struct acpm_handle *acpm = ctx->shared->acpm; + struct acpm_handle *acpm = ctx->shared->acpm; const struct acpm_pmic_ops *pmic_ops = &acpm->ops.pmic_ops; const u8 *r = reg_buf; u8 reg; @@ -429,7 +429,7 @@ static int sec_pmic_acpm_bus_reg_update_bits(void *context, unsigned int reg, un unsigned int val) { struct sec_pmic_acpm_bus_context *ctx = context; - const struct acpm_handle *acpm = ctx->shared->acpm; + struct acpm_handle *acpm = ctx->shared->acpm; const struct acpm_pmic_ops *pmic_ops = &acpm->ops.pmic_ops; return pmic_ops->update_reg(acpm, ctx->shared->acpm_chan_id, ctx->type, reg & 0xff, @@ -480,7 +480,7 @@ static int sec_pmic_acpm_probe(struct platform_device *pdev) struct regmap *regmap_common, *regmap_pmic, *regmap; const struct sec_pmic_acpm_platform_data *pdata; struct sec_pmic_acpm_shared_bus_context *shared_ctx; - const struct acpm_handle *acpm; + struct acpm_handle *acpm; struct device *dev = &pdev->dev; int ret, irq; diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c index 081827bc0596..7c677b0344c6 100644 --- a/drivers/mfd/stpmic1.c +++ b/drivers/mfd/stpmic1.c @@ -16,6 +16,8 @@ #include +#define STPMIC1_MAX_RETRIES 2 + #define STPMIC1_MAIN_IRQ 0 static const struct regmap_range stpmic1_readable_ranges[] = { @@ -121,9 +123,23 @@ static const struct regmap_irq_chip stpmic1_regmap_irq_chip = { static int stpmic1_power_off(struct sys_off_data *data) { struct stpmic1 *ddata = data->cb_data; + int ret; - regmap_update_bits(ddata->regmap, MAIN_CR, - SOFTWARE_SWITCH_OFF, SOFTWARE_SWITCH_OFF); + /* + * Attempt to shut down again, in case the first attempt failed. + * The STPMIC1 might get confused and the first regmap_update_bits() + * returns with -ETIMEDOUT / -110 . If that or similar transient + * failure occurs, try to shut down again. If the second attempt + * fails, there is some bigger problem, report it to user. + */ + for (int retries = 0; retries < STPMIC1_MAX_RETRIES; retries++) { + ret = regmap_update_bits(ddata->regmap, MAIN_CR, SOFTWARE_SWITCH_OFF, + SOFTWARE_SWITCH_OFF); + if (!ret) + return NOTIFY_DONE; + } + + dev_err(ddata->dev, "Failed to access PMIC I2C bus (%d)\n", ret); return NOTIFY_DONE; } diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c index f964bcfa0796..888737b8e7be 100644 --- a/drivers/mfd/viperboard.c +++ b/drivers/mfd/viperboard.c @@ -59,7 +59,7 @@ static int vprbrd_probe(struct usb_interface *interface, mutex_init(&vb->lock); - vb->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + vb->usb_dev = interface_to_usbdev(interface); /* save our data pointer in this interface device */ usb_set_intfdata(interface, vb); @@ -96,10 +96,8 @@ static int vprbrd_probe(struct usb_interface *interface, return 0; error: - if (vb) { - usb_put_dev(vb->usb_dev); + if (vb) kfree(vb); - } return ret; } @@ -110,7 +108,6 @@ static void vprbrd_disconnect(struct usb_interface *interface) mfd_remove_devices(&interface->dev); usb_set_intfdata(interface, NULL); - usb_put_dev(vb->usb_dev); kfree(vb); dev_dbg(&interface->dev, "disconnected\n"); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 5cc79d1517af..00683bf06258 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -116,7 +116,8 @@ config RPMB config TI_FPC202 tristate "TI FPC202 Dual Port Controller" depends on I2C - select GPIOLIB + depends on GPIOLIB + depends on LEDS_CLASS select I2C_ATR help If you say yes here you get support for the Texas Instruments FPC202 diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c index c3a58912d6db..d4238ebad3c6 100644 --- a/drivers/misc/amd-sbi/rmi-core.c +++ b/drivers/misc/amd-sbi/rmi-core.c @@ -48,7 +48,7 @@ /* CPUID MCAMSR mask & index */ #define CPUID_MCA_THRD_INDEX 32 #define CPUID_MCA_FUNC_MASK GENMASK(31, 0) -#define CPUID_EXT_FUNC_INDEX 56 +#define CPUID_EXT_FUNC_INDEX 48 /* input for bulk write to CPUID protocol */ struct cpu_msr_indata { @@ -214,6 +214,7 @@ static int rmi_cpuid_read(struct sbrmi_data *data, goto exit_unlock; break; case 0x21: + case 0x31: ret = rmi_cpuid_input_ext(data, msg, thread); if (ret) goto exit_unlock; @@ -327,6 +328,7 @@ static int rmi_mca_msr_read(struct sbrmi_data *data, goto exit_unlock; break; case 0x21: + case 0x31: ret = rmi_mcamsr_input_ext(data, msg, thread); if (ret) goto exit_unlock; diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c index f0cc99000b69..37e5ea83bf97 100644 --- a/drivers/misc/amd-sbi/rmi-i2c.c +++ b/drivers/misc/amd-sbi/rmi-i2c.c @@ -170,6 +170,16 @@ static int sbrmi_i3c_probe(struct i3c_device *i3cdev) struct regmap *regmap; int rev, ret; + /* + * AMD OOB devices are distinguished by their Instance ID. + * For SBRMI, the Instance ID is 1. Since the device ID match + * does not account for the Instance ID, the following check + * ensures that only the SBRMI device is probed, excluding + * other OOB devices. + */ + if (I3C_PID_INSTANCE_ID(i3cdev->desc->info.pid) != 1) + return -ENXIO; + regmap = devm_regmap_init_i3c(i3cdev, &sbrmi_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); @@ -212,6 +222,10 @@ static void sbrmi_i3c_remove(struct i3c_device *i3cdev) static const struct i3c_device_id sbrmi_i3c_id[] = { /* PID for AMD SBRMI device */ I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x2, NULL), + I3C_DEVICE_EXTRA_INFO(0x0, 0x0, 0x118, NULL), /* Socket:0, Venice */ + I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice */ + I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice */ + I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice */ {} }; MODULE_DEVICE_TABLE(i3c, sbrmi_i3c_id); diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c index f68a8957b98f..dfdfa9ba4747 100644 --- a/drivers/misc/ibmasm/ibmasmfs.c +++ b/drivers/misc/ibmasm/ibmasmfs.c @@ -303,6 +303,8 @@ static ssize_t command_file_write(struct file *file, const char __user *ubuff, s return -EINVAL; if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE) return 0; + if (count < sizeof(struct dot_command_header)) + return -EINVAL; if (*offset != 0) return 0; @@ -319,6 +321,11 @@ static ssize_t command_file_write(struct file *file, const char __user *ubuff, s return -EFAULT; } + if (count < get_dot_command_size(cmd->buffer)) { + command_put(cmd); + return -EINVAL; + } + spin_lock_irqsave(&command_data->sp->lock, flags); if (command_data->command) { spin_unlock_irqrestore(&command_data->sp->lock, flags); diff --git a/drivers/misc/ibmasm/lowlevel.c b/drivers/misc/ibmasm/lowlevel.c index 6922dc6c10db..5313230f36ad 100644 --- a/drivers/misc/ibmasm/lowlevel.c +++ b/drivers/misc/ibmasm/lowlevel.c @@ -19,17 +19,21 @@ static struct i2o_header header = I2O_HEADER_TEMPLATE; int ibmasm_send_i2o_message(struct service_processor *sp) { u32 mfa; - unsigned int command_size; + size_t command_size; struct i2o_message *message; struct command *command = sp->current_command; + command_size = get_dot_command_size(command->buffer); + if (command_size > command->buffer_size) + return 1; + if (command_size > I2O_COMMAND_SIZE) + command_size = I2O_COMMAND_SIZE; + mfa = get_mfa_inbound(sp->base_address); if (!mfa) return 1; - command_size = get_dot_command_size(command->buffer); - header.message_size = outgoing_message_size(command_size); - + header.message_size = outgoing_message_size((unsigned int)command_size); message = get_i2o_message(sp->base_address, mfa); memcpy_toio(&message->header, &header, sizeof(struct i2o_header)); diff --git a/drivers/misc/ibmasm/remote.c b/drivers/misc/ibmasm/remote.c index ec816d3b38cb..521531738c9a 100644 --- a/drivers/misc/ibmasm/remote.c +++ b/drivers/misc/ibmasm/remote.c @@ -177,6 +177,11 @@ void ibmasm_handle_mouse_interrupt(struct service_processor *sp) writer = get_queue_writer(sp); while (reader != writer) { + if (reader >= REMOTE_QUEUE_SIZE || writer >= REMOTE_QUEUE_SIZE) { + set_queue_reader(sp, 0); + break; + } + memcpy_fromio(&input, get_queue_entry(sp, reader), sizeof(struct remote_input)); diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index 094fb1dde0fe..2d1bc5b4d11b 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -59,6 +59,17 @@ config INTEL_MEI_GSC tasks such as graphics card firmware update and security tasks. +config INTEL_MEI_CSC + tristate "Intel MEI CSC embedded device" + depends on INTEL_MEI_ME + help + Intel PCI driver for the chassis controller embedded in Intel graphics devices. + + An MEI device here called CSC can be embedded in discrete + Intel graphics devices, to support a range of chassis + tasks such as graphics card firmware update and security + tasks. + config INTEL_MEI_VSC_HW tristate "Intel visual sensing controller device transport driver" depends on ACPI && SPI diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index a203ed766b33..9a6aa335921e 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -21,6 +21,9 @@ mei-me-objs += hw-me.o obj-$(CONFIG_INTEL_MEI_GSC) += mei-gsc.o mei-gsc-objs := gsc-me.o +obj-$(CONFIG_INTEL_MEI_CSC) += mei-csc.o +mei-csc-objs := pci-csc.o + obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o mei-txe-objs := pci-txe.o mei-txe-objs += hw-txe.o diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index e6a1d3534663..bea7a47d216e 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -303,9 +303,9 @@ static void mei_wd(struct mei_cl_device *cldev) { struct pci_dev *pdev = to_pci_dev(cldev->dev.parent); - if (pdev->device == MEI_DEV_ID_WPT_LP || - pdev->device == MEI_DEV_ID_SPT || - pdev->device == MEI_DEV_ID_SPT_H) + if (pdev->device == PCI_DEVICE_ID_INTEL_MEI_WPT_LP || + pdev->device == PCI_DEVICE_ID_INTEL_MEI_SPT || + pdev->device == PCI_DEVICE_ID_INTEL_MEI_SPT_H) cldev->me_cl->props.protocol_version = 0x2; cldev->do_match = 1; diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index f739dbcdb04c..fcde082eb5e3 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -601,6 +601,19 @@ void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data) } EXPORT_SYMBOL_GPL(mei_cldev_set_drvdata); +/** + * mei_cldev_uuid - return uuid of the underlying me client + * + * @cldev: mei client device + * + * Return: me client uuid + */ +const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev) +{ + return mei_me_cl_uuid(cldev->me_cl); +} +EXPORT_SYMBOL_GPL(mei_cldev_uuid); + /** * mei_cldev_ver - return protocol version of the underlying me client * diff --git a/drivers/misc/mei/gsc-me.c b/drivers/misc/mei/gsc-me.c index 93cba090ea08..73d5beeb9c34 100644 --- a/drivers/misc/mei/gsc-me.c +++ b/drivers/misc/mei/gsc-me.c @@ -23,11 +23,12 @@ #define MEI_GSC_RPM_TIMEOUT 500 -static int mei_gsc_read_hfs(const struct mei_device *dev, int where, u32 *val) +static int mei_gsc_read_hfs(const struct mei_device *dev, int where, const char *name, u32 *val) { struct mei_me_hw *hw = to_me_hw(dev); *val = ioread32(hw->mem_addr + where + 0xC00); + trace_mei_reg_read(&dev->dev, name, where, *val); return 0; } diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index fa30899a5fa2..f145e8e36cb3 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -9,120 +9,123 @@ /* * MEI device IDs */ -#define MEI_DEV_ID_82946GZ 0x2974 /* 82946GZ/GL */ -#define MEI_DEV_ID_82G35 0x2984 /* 82G35 Express */ -#define MEI_DEV_ID_82Q965 0x2994 /* 82Q963/Q965 */ -#define MEI_DEV_ID_82G965 0x29A4 /* 82P965/G965 */ +#define PCI_DEVICE_ID_INTEL_MEI_82946GZ 0x2974 /* 82946GZ/GL */ +#define PCI_DEVICE_ID_INTEL_MEI_82G35 0x2984 /* 82G35 Express */ +#define PCI_DEVICE_ID_INTEL_MEI_82Q965 0x2994 /* 82Q963/Q965 */ +#define PCI_DEVICE_ID_INTEL_MEI_82G965 0x29A4 /* 82P965/G965 */ -#define MEI_DEV_ID_82GM965 0x2A04 /* Mobile PM965/GM965 */ -#define MEI_DEV_ID_82GME965 0x2A14 /* Mobile GME965/GLE960 */ +#define PCI_DEVICE_ID_INTEL_MEI_82GM965 0x2A04 /* Mobile PM965/GM965 */ +#define PCI_DEVICE_ID_INTEL_MEI_82GME965 0x2A14 /* Mobile GME965/GLE960 */ -#define MEI_DEV_ID_ICH9_82Q35 0x29B4 /* 82Q35 Express */ -#define MEI_DEV_ID_ICH9_82G33 0x29C4 /* 82G33/G31/P35/P31 Express */ -#define MEI_DEV_ID_ICH9_82Q33 0x29D4 /* 82Q33 Express */ -#define MEI_DEV_ID_ICH9_82X38 0x29E4 /* 82X38/X48 Express */ -#define MEI_DEV_ID_ICH9_3200 0x29F4 /* 3200/3210 Server */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_82Q35 0x29B4 /* 82Q35 Express */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_82G33 0x29C4 /* 82G33/G31/P35/P31 Express */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_82Q33 0x29D4 /* 82Q33 Express */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_82X38 0x29E4 /* 82X38/X48 Express */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_3200 0x29F4 /* 3200/3210 Server */ -#define MEI_DEV_ID_ICH9_6 0x28B4 /* Bearlake */ -#define MEI_DEV_ID_ICH9_7 0x28C4 /* Bearlake */ -#define MEI_DEV_ID_ICH9_8 0x28D4 /* Bearlake */ -#define MEI_DEV_ID_ICH9_9 0x28E4 /* Bearlake */ -#define MEI_DEV_ID_ICH9_10 0x28F4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_6 0x28B4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_7 0x28C4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_8 0x28D4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_9 0x28E4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9_10 0x28F4 /* Bearlake */ -#define MEI_DEV_ID_ICH9M_1 0x2A44 /* Cantiga */ -#define MEI_DEV_ID_ICH9M_2 0x2A54 /* Cantiga */ -#define MEI_DEV_ID_ICH9M_3 0x2A64 /* Cantiga */ -#define MEI_DEV_ID_ICH9M_4 0x2A74 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9M_1 0x2A44 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9M_2 0x2A54 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9M_3 0x2A64 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH9M_4 0x2A74 /* Cantiga */ -#define MEI_DEV_ID_ICH10_1 0x2E04 /* Eaglelake */ -#define MEI_DEV_ID_ICH10_2 0x2E14 /* Eaglelake */ -#define MEI_DEV_ID_ICH10_3 0x2E24 /* Eaglelake */ -#define MEI_DEV_ID_ICH10_4 0x2E34 /* Eaglelake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH10_1 0x2E04 /* Eaglelake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH10_2 0x2E14 /* Eaglelake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH10_3 0x2E24 /* Eaglelake */ +#define PCI_DEVICE_ID_INTEL_MEI_ICH10_4 0x2E34 /* Eaglelake */ -#define MEI_DEV_ID_IBXPK_1 0x3B64 /* Calpella */ -#define MEI_DEV_ID_IBXPK_2 0x3B65 /* Calpella */ +#define PCI_DEVICE_ID_INTEL_MEI_IBXPK_1 0x3B64 /* Calpella */ +#define PCI_DEVICE_ID_INTEL_MEI_IBXPK_2 0x3B65 /* Calpella */ -#define MEI_DEV_ID_CPT_1 0x1C3A /* Couger Point */ -#define MEI_DEV_ID_PBG_1 0x1D3A /* C600/X79 Patsburg */ +#define PCI_DEVICE_ID_INTEL_MEI_CPT_1 0x1C3A /* Couger Point */ +#define PCI_DEVICE_ID_INTEL_MEI_PBG_1 0x1D3A /* C600/X79 Patsburg */ -#define MEI_DEV_ID_PPT_1 0x1E3A /* Panther Point */ -#define MEI_DEV_ID_PPT_2 0x1CBA /* Panther Point */ -#define MEI_DEV_ID_PPT_3 0x1DBA /* Panther Point */ +#define PCI_DEVICE_ID_INTEL_MEI_PPT_1 0x1E3A /* Panther Point */ +#define PCI_DEVICE_ID_INTEL_MEI_PPT_2 0x1CBA /* Panther Point */ +#define PCI_DEVICE_ID_INTEL_MEI_PPT_3 0x1DBA /* Panther Point */ -#define MEI_DEV_ID_LPT_H 0x8C3A /* Lynx Point H */ -#define MEI_DEV_ID_LPT_W 0x8D3A /* Lynx Point - Wellsburg */ -#define MEI_DEV_ID_LPT_LP 0x9C3A /* Lynx Point LP */ -#define MEI_DEV_ID_LPT_HR 0x8CBA /* Lynx Point H Refresh */ +#define PCI_DEVICE_ID_INTEL_MEI_LPT_H 0x8C3A /* Lynx Point H */ +#define PCI_DEVICE_ID_INTEL_MEI_LPT_W 0x8D3A /* Lynx Point - Wellsburg */ +#define PCI_DEVICE_ID_INTEL_MEI_LPT_LP 0x9C3A /* Lynx Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_LPT_HR 0x8CBA /* Lynx Point H Refresh */ -#define MEI_DEV_ID_WPT_LP 0x9CBA /* Wildcat Point LP */ -#define MEI_DEV_ID_WPT_LP_2 0x9CBB /* Wildcat Point LP 2 */ +#define PCI_DEVICE_ID_INTEL_MEI_WPT_LP 0x9CBA /* Wildcat Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_WPT_LP_2 0x9CBB /* Wildcat Point LP 2 */ -#define MEI_DEV_ID_SPT 0x9D3A /* Sunrise Point */ -#define MEI_DEV_ID_SPT_2 0x9D3B /* Sunrise Point 2 */ -#define MEI_DEV_ID_SPT_3 0x9D3E /* Sunrise Point 3 (iToutch) */ -#define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */ -#define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */ +#define PCI_DEVICE_ID_INTEL_MEI_SPT 0x9D3A /* Sunrise Point */ +#define PCI_DEVICE_ID_INTEL_MEI_SPT_2 0x9D3B /* Sunrise Point 2 */ +#define PCI_DEVICE_ID_INTEL_MEI_SPT_3 0x9D3E /* Sunrise Point 3 (iToutch) */ +#define PCI_DEVICE_ID_INTEL_MEI_SPT_H 0xA13A /* Sunrise Point H */ +#define PCI_DEVICE_ID_INTEL_MEI_SPT_H_2 0xA13B /* Sunrise Point H 2 */ -#define MEI_DEV_ID_LBG 0xA1BA /* Lewisburg (SPT) */ +#define PCI_DEVICE_ID_INTEL_MEI_LBG 0xA1BA /* Lewisburg (SPT) */ -#define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */ -#define MEI_DEV_ID_APL_I 0x5A9A /* Apollo Lake I */ +#define PCI_DEVICE_ID_INTEL_MEI_BXT_M 0x1A9A /* Broxton M */ +#define PCI_DEVICE_ID_INTEL_MEI_APL_I 0x5A9A /* Apollo Lake I */ -#define MEI_DEV_ID_DNV_IE 0x19E5 /* Denverton IE */ +#define PCI_DEVICE_ID_INTEL_MEI_DNV_IE 0x19E5 /* Denverton IE */ -#define MEI_DEV_ID_GLK 0x319A /* Gemini Lake */ +#define PCI_DEVICE_ID_INTEL_MEI_GLK 0x319A /* Gemini Lake */ -#define MEI_DEV_ID_KBP 0xA2BA /* Kaby Point */ -#define MEI_DEV_ID_KBP_2 0xA2BB /* Kaby Point 2 */ -#define MEI_DEV_ID_KBP_3 0xA2BE /* Kaby Point 3 (iTouch) */ +#define PCI_DEVICE_ID_INTEL_MEI_KBP 0xA2BA /* Kaby Point */ +#define PCI_DEVICE_ID_INTEL_MEI_KBP_2 0xA2BB /* Kaby Point 2 */ +#define PCI_DEVICE_ID_INTEL_MEI_KBP_3 0xA2BE /* Kaby Point 3 (iTouch) */ -#define MEI_DEV_ID_CNP_LP 0x9DE0 /* Cannon Point LP */ -#define MEI_DEV_ID_CNP_LP_3 0x9DE4 /* Cannon Point LP 3 (iTouch) */ -#define MEI_DEV_ID_CNP_H 0xA360 /* Cannon Point H */ -#define MEI_DEV_ID_CNP_H_3 0xA364 /* Cannon Point H 3 (iTouch) */ +#define PCI_DEVICE_ID_INTEL_MEI_CNP_LP 0x9DE0 /* Cannon Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_CNP_LP_3 0x9DE4 /* Cannon Point LP 3 (iTouch) */ +#define PCI_DEVICE_ID_INTEL_MEI_CNP_H 0xA360 /* Cannon Point H */ +#define PCI_DEVICE_ID_INTEL_MEI_CNP_H_3 0xA364 /* Cannon Point H 3 (iTouch) */ -#define MEI_DEV_ID_CMP_LP 0x02e0 /* Comet Point LP */ -#define MEI_DEV_ID_CMP_LP_3 0x02e4 /* Comet Point LP 3 (iTouch) */ +#define PCI_DEVICE_ID_INTEL_MEI_CMP_LP 0x02e0 /* Comet Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_CMP_LP_3 0x02e4 /* Comet Point LP 3 (iTouch) */ -#define MEI_DEV_ID_CMP_V 0xA3BA /* Comet Point Lake V */ +#define PCI_DEVICE_ID_INTEL_MEI_CMP_V 0xA3BA /* Comet Point Lake V */ -#define MEI_DEV_ID_CMP_H 0x06e0 /* Comet Lake H */ -#define MEI_DEV_ID_CMP_H_3 0x06e4 /* Comet Lake H 3 (iTouch) */ +#define PCI_DEVICE_ID_INTEL_MEI_CMP_H 0x06e0 /* Comet Lake H */ +#define PCI_DEVICE_ID_INTEL_MEI_CMP_H_3 0x06e4 /* Comet Lake H 3 (iTouch) */ -#define MEI_DEV_ID_CDF 0x18D3 /* Cedar Fork */ +#define PCI_DEVICE_ID_INTEL_MEI_CDF 0x18D3 /* Cedar Fork */ -#define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */ -#define MEI_DEV_ID_ICP_N 0x38E0 /* Ice Lake Point N */ +#define PCI_DEVICE_ID_INTEL_MEI_ICP_LP 0x34E0 /* Ice Lake Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_ICP_N 0x38E0 /* Ice Lake Point N */ -#define MEI_DEV_ID_JSP_N 0x4DE0 /* Jasper Lake Point N */ +#define PCI_DEVICE_ID_INTEL_MEI_JSP_N 0x4DE0 /* Jasper Lake Point N */ -#define MEI_DEV_ID_TGP_LP 0xA0E0 /* Tiger Lake Point LP */ -#define MEI_DEV_ID_TGP_H 0x43E0 /* Tiger Lake Point H */ +#define PCI_DEVICE_ID_INTEL_MEI_TGP_LP 0xA0E0 /* Tiger Lake Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_TGP_H 0x43E0 /* Tiger Lake Point H */ -#define MEI_DEV_ID_MCC 0x4B70 /* Mule Creek Canyon (EHL) */ -#define MEI_DEV_ID_MCC_4 0x4B75 /* Mule Creek Canyon 4 (EHL) */ +#define PCI_DEVICE_ID_INTEL_MEI_MCC 0x4B70 /* Mule Creek Canyon (EHL) */ +#define PCI_DEVICE_ID_INTEL_MEI_MCC_4 0x4B75 /* Mule Creek Canyon 4 (EHL) */ -#define MEI_DEV_ID_EBG 0x1BE0 /* Emmitsburg WS */ +#define PCI_DEVICE_ID_INTEL_MEI_EBG 0x1BE0 /* Emmitsburg WS */ -#define MEI_DEV_ID_ADP_S 0x7AE8 /* Alder Lake Point S */ -#define MEI_DEV_ID_ADP_LP 0x7A60 /* Alder Lake Point LP */ -#define MEI_DEV_ID_ADP_P 0x51E0 /* Alder Lake Point P */ -#define MEI_DEV_ID_ADP_N 0x54E0 /* Alder Lake Point N */ +#define PCI_DEVICE_ID_INTEL_MEI_ADP_S 0x7AE8 /* Alder Lake Point S */ +#define PCI_DEVICE_ID_INTEL_MEI_ADP_LP 0x7A60 /* Alder Lake Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_ADP_P 0x51E0 /* Alder Lake Point P */ +#define PCI_DEVICE_ID_INTEL_MEI_ADP_N 0x54E0 /* Alder Lake Point N */ -#define MEI_DEV_ID_RPL_S 0x7A68 /* Raptor Lake Point S */ +#define PCI_DEVICE_ID_INTEL_MEI_RPL_S 0x7A68 /* Raptor Lake Point S */ -#define MEI_DEV_ID_MTL_M 0x7E70 /* Meteor Lake Point M */ -#define MEI_DEV_ID_ARL_S 0x7F68 /* Arrow Lake Point S */ -#define MEI_DEV_ID_ARL_H 0x7770 /* Arrow Lake Point H */ +#define PCI_DEVICE_ID_INTEL_MEI_MTL_M 0x7E70 /* Meteor Lake Point M */ +#define PCI_DEVICE_ID_INTEL_MEI_ARL_S 0x7F68 /* Arrow Lake Point S */ +#define PCI_DEVICE_ID_INTEL_MEI_ARL_H 0x7770 /* Arrow Lake Point H */ -#define MEI_DEV_ID_LNL_M 0xA870 /* Lunar Lake Point M */ +#define PCI_DEVICE_ID_INTEL_MEI_LNL_M 0xA870 /* Lunar Lake Point M */ -#define MEI_DEV_ID_PTL_H 0xE370 /* Panther Lake H */ -#define MEI_DEV_ID_PTL_P 0xE470 /* Panther Lake P */ +#define PCI_DEVICE_ID_INTEL_MEI_PTL_H 0xE370 /* Panther Lake H */ +#define PCI_DEVICE_ID_INTEL_MEI_PTL_P 0xE470 /* Panther Lake P */ -#define MEI_DEV_ID_WCL_P 0x4D70 /* Wildcat Lake P */ +#define PCI_DEVICE_ID_INTEL_MEI_WCL_P 0x4D70 /* Wildcat Lake P */ -#define MEI_DEV_ID_NVL_S 0x6E68 /* Nova Lake Point S */ +#define PCI_DEVICE_ID_INTEL_MEI_NVL_S 0x6E68 /* Nova Lake Point S */ +#define PCI_DEVICE_ID_INTEL_MEI_NVL_H 0xD370 /* Nova Lake Point H */ + +#define PCI_DEVICE_ID_INTEL_MEI_CRI 0x6766 /* Crescent Island */ /* * MEI HW Section @@ -134,6 +137,7 @@ # define PCI_CFG_HFS_1_OPMODE_MSK 0xf0000 /* OP MODE Mask: SPS <= 4.0 */ # define PCI_CFG_HFS_1_OPMODE_SPS 0xf0000 /* SPS SKU : SPS <= 4.0 */ #define PCI_CFG_HFS_2 0x48 +# define PCI_CFG_HFS_2_D3_BLOCK BIT(7) # define PCI_CFG_HFS_2_PM_CMOFF_TO_CMX_ERROR 0x1000000 /* CMoff->CMx wake after an error */ # define PCI_CFG_HFS_2_PM_CM_RESET_ERROR 0x5000000 /* CME reset due to exception */ # define PCI_CFG_HFS_2_PM_EVENT_MASK 0xf000000 diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 1e4a41ac428f..e286763b52ec 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -215,11 +215,8 @@ static int mei_me_fw_status(struct mei_device *dev, fw_status->count = fw_src->count; for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { - ret = hw->read_fws(dev, fw_src->status[i], + ret = hw->read_fws(dev, fw_src->status[i], "PCI_CFG_HFS_X", &fw_status->status[i]); - trace_mei_pci_cfg_read(&dev->dev, "PCI_CFG_HFS_X", - fw_src->status[i], - fw_status->status[i]); if (ret) return ret; } @@ -227,6 +224,15 @@ static int mei_me_fw_status(struct mei_device *dev, return 0; } +static bool mei_csc_pg_blocked(struct mei_device *dev) +{ + struct mei_me_hw *hw = to_me_hw(dev); + u32 reg = 0; + + hw->read_fws(dev, PCI_CFG_HFS_2, "PCI_CFG_HFS_2", ®); + return (reg & PCI_CFG_HFS_2_D3_BLOCK) == PCI_CFG_HFS_2_D3_BLOCK; +} + /** * mei_me_hw_config - configure hw dependent settings * @@ -250,8 +256,7 @@ static int mei_me_hw_config(struct mei_device *dev) hw->hbuf_depth = (hcsr & H_CBD) >> 24; reg = 0; - hw->read_fws(dev, PCI_CFG_HFS_1, ®); - trace_mei_pci_cfg_read(&dev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg); + hw->read_fws(dev, PCI_CFG_HFS_1, "PCI_CFG_HFS_1", ®); hw->d0i3_supported = ((reg & PCI_CFG_HFS_1_D0I3_MSK) == PCI_CFG_HFS_1_D0I3_MSK); @@ -446,8 +451,7 @@ static void mei_gsc_pxp_check(struct mei_device *dev) if (!kind_is_gsc(dev) && !kind_is_gscfi(dev)) return; - hw->read_fws(dev, PCI_CFG_HFS_5, &fwsts5); - trace_mei_pci_cfg_read(&dev->dev, "PCI_CFG_HFS_5", PCI_CFG_HFS_5, fwsts5); + hw->read_fws(dev, PCI_CFG_HFS_5, "PCI_CFG_HFS_5", &fwsts5); if ((fwsts5 & GSC_CFG_HFS_5_BOOT_TYPE_MSK) == GSC_CFG_HFS_5_BOOT_TYPE_PXP) { if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_DEFAULT) @@ -1211,6 +1215,7 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) return ret; } else { hw->pg_state = MEI_PG_OFF; + dev->pg_blocked = mei_csc_pg_blocked(dev); } } @@ -1299,6 +1304,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *) dev_id; struct list_head cmpl_list; + bool pg_blocked; s32 slots; u32 hcsr; int rets = 0; @@ -1350,6 +1356,14 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) } goto end; } + + pg_blocked = mei_csc_pg_blocked(dev); + if (pg_blocked && !dev->pg_blocked) /* PG block requested */ + pm_request_resume(&dev->dev); + else if (!pg_blocked && dev->pg_blocked) /* PG block lifted */ + pm_request_autosuspend(&dev->dev); + dev->pg_blocked = pg_blocked; + /* check slots available for reading */ slots = mei_count_full_read_slots(dev); while (slots > 0) { @@ -1504,10 +1518,11 @@ static bool mei_me_fw_type_nm(const struct pci_dev *pdev) { u32 reg; unsigned int devfn; + int ret; devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); - pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_2, ®); - trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_2", PCI_CFG_HFS_2, reg); + ret = pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_2, ®); + trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_2", PCI_CFG_HFS_2, reg, ret); /* make sure that bit 9 (NM) is up and bit 10 (DM) is down */ return (reg & 0x600) == 0x200; } @@ -1530,10 +1545,11 @@ static bool mei_me_fw_type_sps_4(const struct pci_dev *pdev) { u32 reg; unsigned int devfn; + int ret; devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); - pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_1, ®); - trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg); + ret = pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_1, ®); + trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg, ret); return (reg & PCI_CFG_HFS_1_OPMODE_MSK) == PCI_CFG_HFS_1_OPMODE_SPS; } @@ -1555,10 +1571,11 @@ static bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev) u32 reg; u32 fw_type; unsigned int devfn; + int ret; devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); - pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_3, ®); - trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_3", PCI_CFG_HFS_3, reg); + ret = pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_3, ®); + trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_3", PCI_CFG_HFS_3, reg, ret); fw_type = (reg & PCI_CFG_HFS_3_FW_SKU_MSK); dev_dbg(&pdev->dev, "fw type is %d\n", fw_type); @@ -1722,6 +1739,13 @@ static const struct mei_cfg mei_me_gscfi_cfg = { MEI_CFG_FW_VER_SUPP, }; +/* Chassis System Controller Firmware Interface */ +static const struct mei_cfg mei_me_csc_cfg = { + MEI_CFG_TYPE_GSCFI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, +}; + /* * mei_cfg_list - A list of platform platform specific configurations. * Note: has to be synchronized with enum mei_cfg_idx. @@ -1744,6 +1768,7 @@ static const struct mei_cfg *const mei_cfg_list[] = { [MEI_ME_PCH15_SPS_CFG] = &mei_me_pch15_sps_cfg, [MEI_ME_GSC_CFG] = &mei_me_gsc_cfg, [MEI_ME_GSCFI_CFG] = &mei_me_gscfi_cfg, + [MEI_ME_CSC_CFG] = &mei_me_csc_cfg, }; const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx) diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 204b92af6c47..8da8662a9d61 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -33,12 +33,6 @@ struct mei_cfg { u32 hw_trc_supported:1; }; - -#define MEI_PCI_DEVICE(dev, cfg) \ - .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ - .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \ - .driver_data = (kernel_ulong_t)(cfg), - #define MEI_ME_RPM_TIMEOUT 500 /* ms */ /** @@ -62,7 +56,7 @@ struct mei_me_hw { enum mei_pg_state pg_state; bool d0i3_supported; u8 hbuf_depth; - int (*read_fws)(const struct mei_device *dev, int where, u32 *val); + int (*read_fws)(const struct mei_device *dev, int where, const char *name, u32 *val); /* polling */ struct task_struct *polling_thread; wait_queue_head_t wait_active; @@ -110,6 +104,7 @@ static inline bool mei_me_hw_use_polling(const struct mei_me_hw *hw) * SPS firmware exclusion. * @MEI_ME_GSC_CFG: Graphics System Controller * @MEI_ME_GSCFI_CFG: Graphics System Controller Firmware Interface + * @MEI_ME_CSC_CFG: Chassis System Controller Firmware Interface * @MEI_ME_NUM_CFG: Upper Sentinel. */ enum mei_cfg_idx { @@ -130,6 +125,7 @@ enum mei_cfg_idx { MEI_ME_PCH15_SPS_CFG, MEI_ME_GSC_CFG, MEI_ME_GSCFI_CFG, + MEI_ME_CSC_CFG, MEI_ME_NUM_CFG, }; diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index e4688c391027..a83de653c603 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -651,9 +651,9 @@ static int mei_txe_fw_status(struct mei_device *dev, &fw_status->status[i]); trace_mei_pci_cfg_read(&dev->dev, "PCI_CFG_HSF_X", fw_src->status[i], - fw_status->status[i]); + fw_status->status[i], ret); if (ret) - return ret; + return pcibios_err_to_errno(ret); } return 0; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index b789c4d9c709..766f119f7ed0 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -344,13 +344,15 @@ EXPORT_SYMBOL_GPL(mei_stop); bool mei_write_is_idle(struct mei_device *dev) { bool idle = (dev->dev_state == MEI_DEV_ENABLED && + !dev->pg_blocked && list_empty(&dev->ctrl_wr_list) && list_empty(&dev->write_list) && list_empty(&dev->write_waiting_list)); - dev_dbg(&dev->dev, "write pg: is idle[%d] state=%s ctrl=%01d write=%01d wwait=%01d\n", + dev_dbg(&dev->dev, "write pg: is idle[%d] state=%s blocked=%d ctrl=%d write=%d wwait=%d\n", idle, mei_dev_state_str(dev->dev_state), + dev->pg_blocked, list_empty(&dev->ctrl_wr_list), list_empty(&dev->write_list), list_empty(&dev->write_waiting_list)); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 6f26d5160788..54f70f513482 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -4,6 +4,7 @@ * Intel Management Engine Interface (Intel MEI) Linux driver */ +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -982,14 +984,22 @@ static DEVICE_ATTR_RO(trc); static ssize_t fw_status_show(struct device *device, struct device_attribute *attr, char *buf) { - struct mei_device *dev = dev_get_drvdata(device); + struct mei_device *mdev = dev_get_drvdata(device); struct mei_fw_status fw_status; int err, i; ssize_t cnt = 0; - mutex_lock(&dev->device_lock); - err = mei_fw_status(dev, &fw_status); - mutex_unlock(&dev->device_lock); + if (mdev->read_fws_need_resume) { + err = pm_runtime_resume_and_get(mdev->parent); + if (err) { + dev_err(device, "read fw_status resume error = %d\n", err); + return err; + } + } + scoped_guard(mutex, &mdev->device_lock) + err = mei_fw_status(mdev, &fw_status); + if (mdev->read_fws_need_resume) + pm_runtime_put_autosuspend(mdev->parent); if (err) { dev_err(device, "read fw_status error = %d\n", err); return err; diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h index 24fa321d88bd..fa5224e5353a 100644 --- a/drivers/misc/mei/mei-trace.h +++ b/drivers/misc/mei/mei-trace.h @@ -55,22 +55,24 @@ TRACE_EVENT(mei_reg_write, ); TRACE_EVENT(mei_pci_cfg_read, - TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val), - TP_ARGS(dev, reg, offs, val), + TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val, int ret), + TP_ARGS(dev, reg, offs, val, ret), TP_STRUCT__entry( __string(dev, dev_name(dev)) __string(reg, reg) __field(u32, offs) __field(u32, val) + __field(int, ret) ), TP_fast_assign( __assign_str(dev); __assign_str(reg); __entry->offs = offs; __entry->val = val; + __entry->ret = ret; ), - TP_printk("[%s] pci cfg read %s:[%#x] = %#x", - __get_str(dev), __get_str(reg), __entry->offs, __entry->val) + TP_printk("[%s] pci cfg read %s:[%#x] = %#x, ret = %d", + __get_str(dev), __get_str(reg), __entry->offs, __entry->val, __entry->ret) ); #endif /* _MEI_TRACE_H_ */ diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 0bf8d552c3ea..d8634a726990 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -490,6 +490,8 @@ struct mei_dev_timeouts { * @timer_work : MEI timer delayed work (timeouts) * * @recvd_hw_ready : hw ready message received flag + * @pg_blocked : low power mode is not allowed + * @read_fws_need_resume: the FW status handler needs HW woken from sleep * * @wait_hw_ready : wait queue for receive HW ready message form FW * @wait_pg : wait queue for receive PG message from FW @@ -575,6 +577,9 @@ struct mei_device { struct delayed_work timer_work; bool recvd_hw_ready; + bool pg_blocked; + bool read_fws_need_resume; + /* * waiting queue for receive message from FW */ diff --git a/drivers/misc/mei/mei_lb.c b/drivers/misc/mei/mei_lb.c index 78717ee8ac9a..f6a258c2b838 100644 --- a/drivers/misc/mei/mei_lb.c +++ b/drivers/misc/mei/mei_lb.c @@ -59,12 +59,17 @@ * 5. Status is returned back to the host via MEI. */ +/* Late Binding version 1 */ + #define INTEL_LB_CMD 0x12 #define INTEL_LB_RSP (INTEL_LB_CMD | 0x80) #define INTEL_LB_SEND_TIMEOUT_MSEC 3000 #define INTEL_LB_RECV_TIMEOUT_MSEC 3000 +#define MEI_GUID_MKHI UUID_LE(0xe2c2afa2, 0x3817, 0x4d19, \ + 0x9d, 0x95, 0x6, 0xb1, 0x6b, 0x58, 0x8a, 0x5d) + /** * struct mei_lb_req - Late Binding request structure * @header: MKHI message header (see struct mkhi_msg_hdr) @@ -97,8 +102,74 @@ struct mei_lb_rsp { __le32 status; } __packed; -static bool mei_lb_check_response(const struct device *dev, ssize_t bytes, - struct mei_lb_rsp *rsp) +/* Late Binding version 2 */ + +#define MEI_LB2_CMD 0x01 + +#define MEI_LB2_HDR_FLAG_RSP 0x01 + +#define MEI_GUID_LB UUID_LE(0x4ed87243, 0x3980, 0x4d8e, \ + 0xb1, 0xf9, 0x6f, 0xb7, 0xc0, 0x14, 0x8c, 0x4d) + +/** + * struct mei_lb2_header - Late Binding2 header + * @command_id: + * @flags: Flags for transport layer (e.g. MEI_LB2_HDR_FLAG_RSP) + * @reserved: Reserved for future use by authentication firmware, must be set to 0 + */ +struct mei_lb2_header { + __le32 command_id; + u8 flags; + u8 reserved[3]; +}; + +/** + * struct mei_lb2_rsp_header - Late Binding2 response header + * @header: Common command header + * @status: Status returned by authentication firmware (see &enum intel_lb_status) + */ +struct mei_lb2_rsp_header { + struct mei_lb2_header header; + __le32 status; +}; + +#define MEI_LB2_FLAG_FST_CHUNK 0x02 +#define MEI_LB2_FLAG_LST_CHUNK 0x04 + +/** + * struct mei_lb2_req - Late Binding2 request + * @header: Common command header + * @type: Type of the Late Binding payload (see &enum intel_lb_type) + * @flags: Flags to be passed to the authentication firmware (MEI_LB2_FLAG_*) + * @reserved: Reserved for future use by authentication firmware, must be set to 0 + * @total_payload_size: Size of whole Late Binding package in bytes + * @payload_size: Size of the payload chunk in bytes + * @payload: Data chunk to be sent to the authentication firmware + */ +struct mei_lb2_req { + struct mei_lb2_header header; + __le32 type; + __le32 flags; + __le32 reserved; + __le32 total_payload_size; + __le32 payload_size; + u8 payload[] __counted_by(payload_size); +}; + +/** + * struct mei_lb2_rsp - Late Binding2 response + * @rheader: Common response header + * @type: Type of the Late Binding payload (see &enum intel_lb_type) + * @reserved: Reserved for future use by authentication firmware, must be set to 0 + */ +struct mei_lb2_rsp { + struct mei_lb2_rsp_header rheader; + __le32 type; + __le32 reserved[2]; +}; + +static bool mei_lb_check_response_v1(const struct device *dev, ssize_t bytes, + struct mei_lb_rsp *rsp) { /* * Received message size may be smaller than the full message size when @@ -134,24 +205,15 @@ static bool mei_lb_check_response(const struct device *dev, ssize_t bytes, return true; } -static int mei_lb_push_payload(struct device *dev, u32 type, u32 flags, - const void *payload, size_t payload_size) +static int mei_lb_push_payload_v1(struct device *dev, struct mei_cl_device *cldev, + u32 type, u32 flags, const void *payload, size_t payload_size) { - struct mei_cl_device *cldev; struct mei_lb_req *req = NULL; struct mei_lb_rsp rsp; size_t req_size; ssize_t bytes; int ret; - cldev = to_mei_cl_device(dev); - - ret = mei_cldev_enable(cldev); - if (ret) { - dev_dbg(dev, "Failed to enable firmware client. %d\n", ret); - return ret; - } - req_size = struct_size(req, payload, payload_size); if (req_size > mei_cldev_mtu(cldev)) { dev_err(dev, "Payload is too big: %zu\n", payload_size); @@ -190,7 +252,7 @@ static int mei_lb_push_payload(struct device *dev, u32 type, u32 flags, ret = bytes; goto end; } - if (!mei_lb_check_response(dev, bytes, &rsp)) { + if (!mei_lb_check_response_v1(dev, bytes, &rsp)) { dev_err(dev, "Bad response from the firmware. header: %02x %02x %02x %02x\n", rsp.header.group_id, rsp.header.command, rsp.header.reserved, rsp.header.result); @@ -201,11 +263,130 @@ static int mei_lb_push_payload(struct device *dev, u32 type, u32 flags, dev_dbg(dev, "status = %u\n", le32_to_cpu(rsp.status)); ret = (int)le32_to_cpu(rsp.status); end: - mei_cldev_disable(cldev); kfree(req); return ret; } +static int mei_lb_check_response_v2(const struct device *dev, ssize_t bytes, + struct mei_lb2_rsp *rsp) +{ + /* + * Received message size may be smaller than the full message size when + * reply contains only header with status field set to the error code. + * Check the header size and content first to output exact error, if needed, + * and then process to the whole message. + */ + if (bytes < sizeof(rsp->rheader)) { + dev_err(dev, "Received less than header size from the firmware: %zd < %zu\n", + bytes, sizeof(rsp->rheader)); + return -ENOMSG; + } + if (rsp->rheader.header.command_id != MEI_LB2_CMD) { + dev_err(dev, "Mismatch command: 0x%x instead of 0x%x\n", + rsp->rheader.header.command_id, MEI_LB2_CMD); + return -EPROTO; + } + if (!(rsp->rheader.header.flags & MEI_LB2_HDR_FLAG_RSP)) { + dev_err(dev, "Not a response: 0x%x\n", rsp->rheader.header.flags); + return -EBADMSG; + } + if (rsp->rheader.status) { + dev_err(dev, "Error in result: 0x%x\n", rsp->rheader.status); + return (int)le32_to_cpu(rsp->rheader.status); + } + if (bytes < sizeof(*rsp)) { + dev_err(dev, "Received less than message size from the firmware: %zd < %zu\n", + bytes, sizeof(*rsp)); + return -ENODATA; + } + + return 0; +} + +static int mei_lb_push_payload_v2(struct device *dev, struct mei_cl_device *cldev, + u32 type, u32 flags, const void *payload, size_t payload_size) +{ + u32 first_chunk, last_chunk; + struct mei_lb2_rsp rsp; + size_t sent_data = 0; + size_t chunk_size; + size_t req_size; + ssize_t bytes; + int ret; + + struct mei_lb2_req *req __free(kfree) = kzalloc(mei_cldev_mtu(cldev), GFP_KERNEL); + if (!req) + return -ENOMEM; + + first_chunk = MEI_LB2_FLAG_FST_CHUNK; + last_chunk = 0; + do { + chunk_size = min(payload_size - sent_data, mei_cldev_mtu(cldev) - sizeof(*req)); + + req_size = struct_size(req, payload, chunk_size); + if (sent_data + chunk_size == payload_size) + last_chunk = MEI_LB2_FLAG_LST_CHUNK; + + req->header.command_id = MEI_LB2_CMD; + req->type = cpu_to_le32(type); + req->flags = cpu_to_le32(flags | first_chunk | last_chunk); + req->reserved = 0; + req->total_payload_size = cpu_to_le32(payload_size); + req->payload_size = cpu_to_le32(chunk_size); + memcpy(req->payload, payload + sent_data, chunk_size); + + dev_dbg(dev, "Sending %zu bytes from offset %zu of %zu%s%s\n", + chunk_size, sent_data, payload_size, + first_chunk ? " first" : "", last_chunk ? " last" : ""); + + bytes = mei_cldev_send_timeout(cldev, (u8 *)req, req_size, + INTEL_LB_SEND_TIMEOUT_MSEC); + if (bytes < 0) { + dev_err(dev, "Failed to send late binding request to firmware. %zd\n", + bytes); + return bytes; + } + + bytes = mei_cldev_recv_timeout(cldev, (u8 *)&rsp, sizeof(rsp), + INTEL_LB_RECV_TIMEOUT_MSEC); + if (bytes < 0) { + dev_err(dev, "Failed to receive late binding reply from firmware. %zd\n", + bytes); + return bytes; + } + ret = mei_lb_check_response_v2(dev, bytes, &rsp); + if (ret) + return ret; + + /* prepare for the next chunk */ + sent_data += chunk_size; + first_chunk = 0; + } while (!last_chunk); + + return 0; +} + +static int mei_lb_push_payload(struct device *dev, u32 type, u32 flags, + const void *payload, size_t payload_size) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + int ret; + + ret = mei_cldev_enable(cldev); + if (ret) { + dev_dbg(dev, "Failed to enable firmware client. %d\n", ret); + return ret; + } + + if (memcmp(&MEI_GUID_LB, mei_cldev_uuid(cldev), sizeof(uuid_le)) == 0) + ret = mei_lb_push_payload_v2(dev, cldev, type, flags, payload, payload_size); + else + ret = mei_lb_push_payload_v1(dev, cldev, type, flags, payload, payload_size); + + mei_cldev_disable(cldev); + return ret; +} + static const struct intel_lb_component_ops mei_lb_ops = { .push_payload = mei_lb_push_payload, }; @@ -229,11 +410,16 @@ static int mei_lb_component_match(struct device *dev, int subcomponent, void *data) { /* - * This function checks if requester is Intel %PCI_CLASS_DISPLAY_VGA or - * %PCI_CLASS_DISPLAY_OTHER device, and checks if the requester is the - * grand parent of mei_if i.e. late bind MEI device + * This function checks if requester is Intel vendor, + * determines if MEI is standalone PCI device or the auxiliary one + * and checks the following: + * 0) PCI parent: (e.g. /sys/class/mei/mei0/device -> ../../../0000:15:00.0) + * the requester and MEI device has the same grand parent + * 1) Auxiliary parent: (e.g. /sys/class/mei/mei1/device -> ../../../xe.mei-gscfi.768) + * the requester is the parent of MEI device */ struct device *base = data; + struct device *basep = dev; struct pci_dev *pdev; if (!dev) @@ -247,20 +433,30 @@ static int mei_lb_component_match(struct device *dev, int subcomponent, if (pdev->vendor != PCI_VENDOR_ID_INTEL) return 0; - if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8) && - pdev->class != (PCI_CLASS_DISPLAY_OTHER << 8)) - return 0; - if (subcomponent != INTEL_COMPONENT_LB) return 0; base = base->parent; - if (!base) /* mei device */ + if (!base) /* MEI device */ return 0; - base = base->parent; /* pci device */ + if (dev_is_pci(base)) { + /* case 0) PCI parent */ + base = base->parent; /* bridge 1 */ + if (!base) + return 0; + base = base->parent; /* bridge 2 */ - return !!base && dev == base; + basep = basep->parent; /* bridge 1 */ + if (!basep) + return 0; + basep = basep->parent; /* bridge 2 */ + } else { + /* case 1) Auxiliary parent */ + base = base->parent; /* PCI device */ + } + + return !!base && !!basep && base == basep; } static int mei_lb_probe(struct mei_cl_device *cldev, @@ -288,11 +484,9 @@ static void mei_lb_remove(struct mei_cl_device *cldev) component_master_del(&cldev->dev, &mei_lb_component_master_ops); } -#define MEI_GUID_MKHI UUID_LE(0xe2c2afa2, 0x3817, 0x4d19, \ - 0x9d, 0x95, 0x6, 0xb1, 0x6b, 0x58, 0x8a, 0x5d) - static const struct mei_cl_device_id mei_lb_tbl[] = { - { .uuid = MEI_GUID_MKHI, .version = MEI_CL_VERSION_ANY }, + { .uuid = MEI_GUID_MKHI, .version = 1 }, + { .uuid = MEI_GUID_LB, .version = MEI_CL_VERSION_ANY }, { } }; MODULE_DEVICE_TABLE(mei, mei_lb_tbl); diff --git a/drivers/misc/mei/pci-csc.c b/drivers/misc/mei/pci-csc.c new file mode 100644 index 000000000000..70792bf9b3c0 --- /dev/null +++ b/drivers/misc/mei/pci-csc.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025-2026, Intel Corporation. All rights reserved. + * Intel Management Engine Interface (Intel MEI) Linux driver + * for CSC platforms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client.h" +#include "hw-me-regs.h" +#include "hw-me.h" +#include "mei_dev.h" +#include "mei-trace.h" + +#define MEI_CSC_HECI2_OFFSET 0x1000 + +static int mei_csc_read_fws(const struct mei_device *mdev, int where, const char *name, u32 *val) +{ + struct mei_me_hw *hw = to_me_hw(mdev); + + *val = ioread32(hw->mem_addr + where + 0xC00); + trace_mei_reg_read(&mdev->dev, name, where, *val); + return 0; +} + +static int mei_csc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + const struct mei_cfg *cfg; + char __iomem *registers; + struct mei_device *mdev; + struct mei_me_hw *hw; + int err; + + cfg = mei_me_get_cfg(ent->driver_data); + if (!cfg) + return -ENODEV; + + err = pcim_enable_device(pdev); + if (err) + return dev_err_probe(dev, err, "Failed to enable PCI device.\n"); + + pci_set_master(pdev); + + registers = pcim_iomap_region(pdev, 0, KBUILD_MODNAME); + if (IS_ERR(registers)) + return dev_err_probe(dev, PTR_ERR(registers), "Failed to get PCI region.\n"); + + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (err) + return dev_err_probe(dev, err, "No usable DMA configuration.\n"); + + /* allocates and initializes the mei dev structure */ + mdev = mei_me_dev_init(dev, cfg, false); + if (!mdev) + return -ENOMEM; + + mdev->read_fws_need_resume = true; + + hw = to_me_hw(mdev); + + /* + * Both HECI1 and HECI2 are on this device, but only HECI2 is supported. + */ + hw->mem_addr = registers + MEI_CSC_HECI2_OFFSET; + hw->read_fws = mei_csc_read_fws; + + /* + * mei_register() assumes ownership of mdev. + * No need to release it explicitly in error path. + */ + err = mei_register(mdev, dev); + if (err) + return err; + + pci_set_drvdata(pdev, mdev); + + err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX | PCI_IRQ_MSI); + if (err < 0) { + dev_err_probe(dev, err, "Failed to allocate IRQ.\n"); + goto err_mei_unreg; + } + + hw->irq = pci_irq_vector(pdev, 0); + + /* request and enable interrupt */ + err = request_threaded_irq(hw->irq, + mei_me_irq_quick_handler, mei_me_irq_thread_handler, + IRQF_SHARED | IRQF_ONESHOT, KBUILD_MODNAME, mdev); + if (err) + goto err_free_irq_vectors; + + /* + * Continue to char device setup in spite of firmware handshake failure. + * In order to provide access to the firmware status registers to the user + * space via sysfs. The firmware status registers required to understand + * firmware error state and possible recovery flow. + */ + if (mei_start(mdev)) + dev_warn(dev, "Failed to initialize HECI hardware.\n"); + + pm_runtime_set_autosuspend_delay(dev, MEI_ME_RPM_TIMEOUT); + pm_runtime_use_autosuspend(dev); + + /* + * MEI requires to resume from runtime suspend mode + * in order to perform link reset flow upon system suspend. + */ + dev_pm_set_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE); + + pm_runtime_allow(dev); + pm_runtime_put_noidle(dev); + + return 0; + +err_free_irq_vectors: + pci_free_irq_vectors(pdev); +err_mei_unreg: + mei_deregister(mdev); + return err; +} + +static void mei_csc_shutdown(struct pci_dev *pdev) +{ + struct mei_device *mdev = pci_get_drvdata(pdev); + struct mei_me_hw *hw = to_me_hw(mdev); + + pm_runtime_get_noresume(&pdev->dev); + + mei_stop(mdev); + + mei_disable_interrupts(mdev); + free_irq(hw->irq, mdev); + pci_free_irq_vectors(pdev); +} + +static void mei_csc_remove(struct pci_dev *pdev) +{ + struct mei_device *mdev = pci_get_drvdata(pdev); + + mei_csc_shutdown(pdev); + + mei_deregister(mdev); +} + +static int mei_csc_pci_prepare(struct device *dev) +{ + pm_runtime_resume(dev); + return 0; +} + +static int mei_csc_pci_suspend(struct device *dev) +{ + struct mei_device *mdev = dev_get_drvdata(dev); + + mei_stop(mdev); + + mei_disable_interrupts(mdev); + + return 0; +} + +static int mei_csc_pci_resume(struct device *dev) +{ + struct mei_device *mdev = dev_get_drvdata(dev); + int err; + + err = mei_restart(mdev); + if (err) + return err; + + /* Start timer if stopped in suspend */ + schedule_delayed_work(&mdev->timer_work, HZ); + + return 0; +} + +static void mei_csc_pci_complete(struct device *dev) +{ + pm_runtime_suspend(dev); +} + +static int mei_csc_pm_runtime_idle(struct device *dev) +{ + struct mei_device *mdev = dev_get_drvdata(dev); + + return mei_write_is_idle(mdev) ? 0 : -EBUSY; +} + +static int mei_csc_pm_runtime_suspend(struct device *dev) +{ + struct mei_device *mdev = dev_get_drvdata(dev); + struct mei_me_hw *hw = to_me_hw(mdev); + + guard(mutex)(&mdev->device_lock); + + if (!mei_write_is_idle(mdev)) + return -EAGAIN; + + hw->pg_state = MEI_PG_ON; + return 0; +} + +static int mei_csc_pm_runtime_resume(struct device *dev) +{ + struct mei_device *mdev = dev_get_drvdata(dev); + struct mei_me_hw *hw = to_me_hw(mdev); + irqreturn_t irq_ret; + + scoped_guard(mutex, &mdev->device_lock) + hw->pg_state = MEI_PG_OFF; + + /* Process all queues that wait for resume */ + irq_ret = mei_me_irq_thread_handler(1, mdev); + if (irq_ret != IRQ_HANDLED) + dev_err(dev, "thread handler fail %d\n", irq_ret); + + return 0; +} + +static const struct dev_pm_ops mei_csc_pm_ops = { + .prepare = pm_sleep_ptr(mei_csc_pci_prepare), + .complete = pm_sleep_ptr(mei_csc_pci_complete), + SYSTEM_SLEEP_PM_OPS(mei_csc_pci_suspend, mei_csc_pci_resume) + RUNTIME_PM_OPS(mei_csc_pm_runtime_suspend, + mei_csc_pm_runtime_resume, mei_csc_pm_runtime_idle) +}; + +static const struct pci_device_id mei_csc_pci_tbl[] = { + { PCI_DEVICE_DATA(INTEL, MEI_CRI, MEI_ME_CSC_CFG) }, + {} +}; +MODULE_DEVICE_TABLE(pci, mei_csc_pci_tbl); + +static struct pci_driver mei_csc_driver = { + .name = KBUILD_MODNAME, + .id_table = mei_csc_pci_tbl, + .probe = mei_csc_probe, + .remove = mei_csc_remove, + .shutdown = mei_csc_shutdown, + .driver = { + .pm = &mei_csc_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + } +}; +module_pci_driver(mei_csc_driver); + +MODULE_DESCRIPTION("Intel(R) Management Engine Interface for discrete graphics (CSC)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 2a6e569558b9..9efeafa8f1ca 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -23,113 +23,115 @@ #include "client.h" #include "hw-me-regs.h" #include "hw-me.h" +#include "mei-trace.h" /* mei_pci_tbl - PCI Device ID Table */ static const struct pci_device_id mei_me_pci_tbl[] = { - {MEI_PCI_DEVICE(MEI_DEV_ID_82946GZ, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82G35, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82Q965, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82G965, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82GM965, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82GME965, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q35, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82G33, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q33, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82X38, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_3200, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_82946GZ, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_82G35, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_82Q965, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_82G965, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_82GM965, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_82GME965, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q35, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82G33, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82Q33, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_82X38, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_3200, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_6, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_7, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_8, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_9, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_10, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_1, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_2, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_3, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_4, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_6, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_7, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_8, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_9, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9_10, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_1, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_2, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_3, MEI_ME_ICH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH9M_4, MEI_ME_ICH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_1, MEI_ME_ICH10_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_2, MEI_ME_ICH10_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_3, MEI_ME_ICH10_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_4, MEI_ME_ICH10_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH10_1, MEI_ME_ICH10_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH10_2, MEI_ME_ICH10_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH10_3, MEI_ME_ICH10_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICH10_4, MEI_ME_ICH10_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, MEI_ME_PCH6_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, MEI_ME_PCH6_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CPT_1, MEI_ME_PCH_CPT_PBG_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PBG_1, MEI_ME_PCH_CPT_PBG_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, MEI_ME_PCH7_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, MEI_ME_PCH7_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, MEI_ME_PCH7_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_H, MEI_ME_PCH8_SPS_4_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_W, MEI_ME_PCH8_SPS_4_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, MEI_ME_PCH8_SPS_4_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP_2, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_IBXPK_1, MEI_ME_PCH6_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_IBXPK_2, MEI_ME_PCH6_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CPT_1, MEI_ME_PCH_CPT_PBG_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_PBG_1, MEI_ME_PCH_CPT_PBG_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_PPT_1, MEI_ME_PCH7_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_PPT_2, MEI_ME_PCH7_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_PPT_3, MEI_ME_PCH7_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_LPT_H, MEI_ME_PCH8_SPS_4_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_LPT_W, MEI_ME_PCH8_SPS_4_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_LPT_LP, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_LPT_HR, MEI_ME_PCH8_SPS_4_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_WPT_LP, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_WPT_LP_2, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, MEI_ME_PCH8_SPS_4_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, MEI_ME_PCH12_SPS_4_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_SPT, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_SPT_2, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_SPT_3, MEI_ME_PCH8_ITOUCH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_SPT_H, MEI_ME_PCH8_SPS_4_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_LBG, MEI_ME_PCH12_SPS_4_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_BXT_M, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_APL_I, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_DNV_IE, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_DNV_IE, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_GLK, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_GLK, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_KBP, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_3, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_KBP, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_KBP_2, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_KBP_3, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H, MEI_ME_PCH12_SPS_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CNP_LP, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CNP_H, MEI_ME_PCH12_SPS_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_V, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CMP_LP, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CMP_V, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CMP_H, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_N, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_H, MEI_ME_PCH15_SPS_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_JSP_N, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_JSP_N, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_MCC, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_MCC_4, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_MCC, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_MCC_4, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CDF, MEI_ME_PCH8_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_CDF, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_EBG, MEI_ME_PCH15_SPS_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_EBG, MEI_ME_PCH15_SPS_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_S, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_LP, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_P, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_N, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ADP_S, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ADP_LP, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ADP_P, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ADP_N, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_RPL_S, MEI_ME_PCH15_SPS_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_RPL_S, MEI_ME_PCH15_SPS_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_MTL_M, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ARL_S, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ARL_H, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_MTL_M, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ARL_S, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ARL_H, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LNL_M, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_LNL_M, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PTL_H, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PTL_P, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_PTL_H, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_PTL_P, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_WCL_P, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_WCL_P, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_NVL_S, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_NVL_S, MEI_ME_PCH15_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_NVL_H, MEI_ME_PCH15_CFG)}, /* required last entry */ {0, } @@ -145,11 +147,14 @@ static inline void mei_me_set_pm_domain(struct mei_device *dev) {} static inline void mei_me_unset_pm_domain(struct mei_device *dev) {} #endif /* CONFIG_PM */ -static int mei_me_read_fws(const struct mei_device *dev, int where, u32 *val) +static int mei_me_read_fws(const struct mei_device *dev, int where, const char *name, u32 *val) { struct pci_dev *pdev = to_pci_dev(dev->parent); + int ret; - return pci_read_config_dword(pdev, where, val); + ret = pci_read_config_dword(pdev, where, val); + trace_mei_pci_cfg_read(&dev->dev, name, where, *val, ret); + return pcibios_err_to_errno(ret); } /** diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c index 8eb2b5ac9850..2aac83ec4a39 100644 --- a/drivers/misc/ti_fpc202.c +++ b/drivers/misc/ti_fpc202.c @@ -7,12 +7,17 @@ */ #include +#include #include #include #include #include #include +#include +#include #include +#include +#include #define FPC202_NUM_PORTS 2 #define FPC202_ALIASES_PER_PORT 2 @@ -34,18 +39,55 @@ * ... * 19: P1_S1_OUT_B * + * Ports with optional LED control: + * + * 20: P0_S0_OUT_C (P0_S0_LED1) + * ... + * 23: P1_S1_OUT_C (P1_S1_LED1) + * 24: P0_S0_OUT_D (P0_S0_LED2 + * ... + * 27: P1_S1_OUT_D (P1_S1_LED2) + * */ -#define FPC202_GPIO_COUNT 20 +#define FPC202_GPIO_COUNT 28 #define FPC202_GPIO_P0_S0_IN_B 4 #define FPC202_GPIO_P0_S0_OUT_A 12 +#define FPC202_GPIO_P0_S0_OUT_C 20 +#define FPC202_GPIO_P0_S0_OUT_D 24 #define FPC202_REG_IN_A_INT 0x6 #define FPC202_REG_IN_C_IN_B 0x7 #define FPC202_REG_OUT_A_OUT_B 0x8 +#define FPC202_REG_OUT_C_OUT_D 0x9 #define FPC202_REG_OUT_A_OUT_B_VAL 0xa +#define FPC202_LED_COUNT 8 + +/* There are four LED GPIO mode registers which manage two GPIOs each. */ +#define FPC202_REG_LED_MODE(offset) (0x1a + 0x20 * ((offset) % 4)) + +/* LED1 GPIOs (*_OUT_C) are configured in bits 1:0, LED2 GPIOs (*_OUT_D) in bits 3:2. */ +#define FPC202_LED_MODE_SHIFT(offset) ((offset) < FPC202_GPIO_P0_S0_OUT_D ? 0 : 2) +#define FPC202_LED_MODE_MASK(offset) (GENMASK(1, 0) << FPC202_LED_MODE_SHIFT(offset)) + +/* There is one PWM control register for each GPIO LED */ +#define FPC202_REG_LED_PWM(offset) \ + (((offset) < FPC202_GPIO_P0_S0_OUT_D ? 0x14 : 0x15) + 0x20 * ((offset) % 4)) + +/* There are two blink delay registers (on/off time) for each GPIO LED */ +#define FPC202_REG_LED_BLINK_ON(offset) \ + (((offset) < FPC202_GPIO_P0_S0_OUT_D ? 0x16 : 0x18) + 0x20 * ((offset) % 4)) +#define FPC202_REG_LED_BLINK_OFF(offset) (FPC202_REG_LED_BLINK_ON(offset) + 1) + +/* The actual hardware precision is 2.5ms but since the LED API doesn't handle sub-millisecond + * timesteps this is rounded up to 5ms + */ +#define FPC202_LED_BLINK_PRECISION 5UL + +#define FPC202_LED_MAX_BRIGHTNESS 255 + #define FPC202_REG_MOD_DEV(port, dev) (0xb4 + ((port) * 4) + (dev)) #define FPC202_REG_AUX_DEV(port, dev) (0xb6 + ((port) * 4) + (dev)) @@ -59,15 +101,34 @@ /* Even aliases are assigned to device 0 and odd aliases to device 1 */ #define fpc202_dev_num_from_alias(alias) ((alias) % 2) +enum fpc202_led_mode { + FPC202_LED_MODE_OFF = 0, + FPC202_LED_MODE_ON = 1, + FPC202_LED_MODE_PWM = 2, + FPC202_LED_MODE_BLINK = 3, +}; + +struct fpc202_led { + int offset; + struct led_classdev led_cdev; + struct fpc202_priv *priv; + struct gpio_desc *gpio; + enum fpc202_led_mode mode; +}; + struct fpc202_priv { struct i2c_client *client; struct i2c_atr *atr; struct gpio_desc *en_gpio; struct gpio_chip gpio; + struct fpc202_led leds[FPC202_LED_COUNT]; /* Lock REG_MOD/AUX_DEV and addr_caches during attach/detach */ struct mutex reg_dev_lock; + /* Lock LED mode select register during accesses */ + struct mutex led_mode_lock; + /* Cached device addresses for both ports and their devices */ u8 addr_caches[2][2]; @@ -97,6 +158,11 @@ static int fpc202_gpio_get_dir(int offset) return offset < FPC202_GPIO_P0_S0_OUT_A ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; } +static int fpc202_gpio_has_led_caps(int offset) +{ + return offset >= FPC202_GPIO_P0_S0_OUT_C; +} + static int fpc202_read(struct fpc202_priv *priv, u8 reg) { int val; @@ -118,6 +184,37 @@ static void fpc202_set_enable(struct fpc202_priv *priv, int enable) gpiod_set_value(priv->en_gpio, enable); } +static int fpc202_led_mode_write(struct fpc202_priv *priv, + int offset, + enum fpc202_led_mode mode) +{ + u8 val, reg = FPC202_REG_LED_MODE(offset); + int ret; + + guard(mutex)(&priv->led_mode_lock); + + ret = fpc202_read(priv, reg); + if (ret < 0) { + dev_err(&priv->client->dev, "failed to read LED mode %d! err %d\n", + offset, ret); + return ret; + } + + val = (u8)ret & ~FPC202_LED_MODE_MASK(offset); + val |= mode << FPC202_LED_MODE_SHIFT(offset); + + return fpc202_write(priv, reg, val); +} + +static int fpc202_led_mode_set(struct fpc202_led *led, enum fpc202_led_mode mode) +{ + struct fpc202_priv *priv = led->priv; + + led->mode = mode; + + return fpc202_led_mode_write(priv, led->offset, mode); +} + static int fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { @@ -125,6 +222,16 @@ static int fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, int ret; u8 val; + if (fpc202_gpio_has_led_caps(offset)) { + ret = fpc202_led_mode_write(priv, offset, + value ? FPC202_LED_MODE_ON : FPC202_LED_MODE_OFF); + if (ret < 0) + dev_err(&priv->client->dev, "Failed to set GPIO %d LED mode! err %d\n", + offset, ret); + + return ret; + } + ret = fpc202_read(priv, FPC202_REG_OUT_A_OUT_B_VAL); if (ret < 0) { dev_err(&priv->client->dev, "Failed to set GPIO %d value! err %d\n", offset, ret); @@ -153,9 +260,11 @@ static int fpc202_gpio_get(struct gpio_chip *chip, unsigned int offset) } else if (offset < FPC202_GPIO_P0_S0_OUT_A) { reg = FPC202_REG_IN_C_IN_B; bit = BIT(offset - FPC202_GPIO_P0_S0_IN_B); - } else { + } else if (!fpc202_gpio_has_led_caps(offset)) { reg = FPC202_REG_OUT_A_OUT_B_VAL; bit = BIT(offset - FPC202_GPIO_P0_S0_OUT_A); + } else { + return -EOPNOTSUPP; } ret = fpc202_read(priv, reg); @@ -177,21 +286,29 @@ static int fpc202_gpio_direction_output(struct gpio_chip *chip, unsigned int off int value) { struct fpc202_priv *priv = gpiochip_get_data(chip); + u8 reg, val, bit; int ret; - u8 val; if (fpc202_gpio_get_dir(offset) == GPIO_LINE_DIRECTION_IN) return -EINVAL; fpc202_gpio_set(chip, offset, value); - ret = fpc202_read(priv, FPC202_REG_OUT_A_OUT_B); + if (fpc202_gpio_has_led_caps(offset)) { + reg = FPC202_REG_OUT_C_OUT_D; + bit = BIT(offset - FPC202_GPIO_P0_S0_OUT_C); + } else { + reg = FPC202_REG_OUT_A_OUT_B; + bit = BIT(offset - FPC202_GPIO_P0_S0_OUT_A); + } + + ret = fpc202_read(priv, reg); if (ret < 0) return ret; - val = (u8)ret | BIT(offset - FPC202_GPIO_P0_S0_OUT_A); + val = (u8)ret | bit; - return fpc202_write(priv, FPC202_REG_OUT_A_OUT_B, val); + return fpc202_write(priv, reg, val); } /* @@ -243,23 +360,15 @@ static void fpc202_detach_addr(struct i2c_atr *atr, u32 chan_id, u16 addr) { struct fpc202_priv *priv = i2c_atr_get_driver_data(atr); - int dev_num, reg_mod, val; + int dev_num, val; for (dev_num = 0; dev_num < 2; dev_num++) { - reg_mod = FPC202_REG_MOD_DEV(chan_id, dev_num); - mutex_lock(&priv->reg_dev_lock); val = priv->addr_caches[chan_id][dev_num]; mutex_unlock(&priv->reg_dev_lock); - if (val < 0) { - dev_err(&priv->client->dev, "failed to read register 0x%x while detaching address 0x%02x\n", - reg_mod, addr); - return; - } - if (val == (addr & 0x7f)) { fpc202_write_dev_addr(priv, chan_id, dev_num, FPC202_REG_DEV_INVALID); return; @@ -272,6 +381,183 @@ static const struct i2c_atr_ops fpc202_atr_ops = { .detach_addr = fpc202_detach_addr, }; +static struct fpc202_led *fpc202_cdev_to_led(struct led_classdev *cdev) +{ + return container_of(cdev, struct fpc202_led, led_cdev); +} + +static struct fpc202_led *fpc202_led_get(struct fpc202_priv *priv, int offset) +{ + return &priv->leds[offset - FPC202_GPIO_P0_S0_OUT_C]; +} + +static int fpc202_led_blink_set(struct led_classdev *cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct fpc202_led *led = fpc202_cdev_to_led(cdev); + struct fpc202_priv *priv = led->priv; + unsigned long val; + int ret; + + if (*delay_on == 0 && *delay_off == 0) { + *delay_on = 250; + *delay_off = 250; + } else { + if (*delay_on % FPC202_LED_BLINK_PRECISION) + *delay_on = roundup(*delay_on, FPC202_LED_BLINK_PRECISION); + + if (*delay_off % FPC202_LED_BLINK_PRECISION) + *delay_off = roundup(*delay_off, FPC202_LED_BLINK_PRECISION); + } + + /* Multiply the duration by two, since the actual precision is 2.5ms not 5ms*/ + val = 2 * (*delay_on / FPC202_LED_BLINK_PRECISION); + if (val > 255) { + val = 255; + *delay_on = (val / 2) * FPC202_LED_BLINK_PRECISION; + } + + ret = fpc202_write(priv, FPC202_REG_LED_BLINK_ON(led->offset), val); + if (ret) { + dev_err(&priv->client->dev, + "Failed to set blink on duration for LED %d, err %d\n", + led->offset, ret); + return ret; + } + + val = 2 * (*delay_off / FPC202_LED_BLINK_PRECISION); + if (val > 255) { + val = 255; + *delay_off = (val / 2) * FPC202_LED_BLINK_PRECISION; + } + + ret = fpc202_write(priv, FPC202_REG_LED_BLINK_OFF(led->offset), val); + if (ret) { + dev_err(&priv->client->dev, + "Failed to set blink off duration for LED %d, err %d\n", + led->offset, ret); + return ret; + } + + return fpc202_led_mode_set(led, FPC202_LED_MODE_BLINK); +} + +static enum led_brightness fpc202_led_brightness_get(struct led_classdev *cdev) +{ + struct fpc202_led *led = fpc202_cdev_to_led(cdev); + + if (led->mode == FPC202_LED_MODE_OFF) + return LED_OFF; + + return LED_ON; +} + +static int fpc202_led_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct fpc202_led *led = fpc202_cdev_to_led(cdev); + struct fpc202_priv *priv = led->priv; + int ret; + + if (!brightness) + return fpc202_led_mode_set(led, FPC202_LED_MODE_OFF); + + if (led->mode != FPC202_LED_MODE_BLINK) { + if (brightness == FPC202_LED_MAX_BRIGHTNESS) + return fpc202_led_mode_set(led, FPC202_LED_MODE_ON); + + ret = fpc202_led_mode_set(led, FPC202_LED_MODE_PWM); + if (ret) { + dev_err(&priv->client->dev, "Failed to set LED %d mode, err %d\n", + led->offset, ret); + return ret; + } + } + + return fpc202_write(priv, FPC202_REG_LED_PWM(led->offset), brightness); +} + +static int fpc202_register_led(struct fpc202_priv *priv, int offset, + struct device_node *led_handle) +{ + struct fpc202_led *led = fpc202_led_get(priv, offset); + struct device *dev = &priv->client->dev; + struct led_init_data init_data = { }; + int ret = 0; + + led->priv = priv; + led->offset = offset; + led->led_cdev.max_brightness = FPC202_LED_MAX_BRIGHTNESS; + led->led_cdev.brightness_set_blocking = fpc202_led_brightness_set; + led->led_cdev.brightness_get = fpc202_led_brightness_get; + led->led_cdev.blink_set = fpc202_led_blink_set; + + init_data.fwnode = of_fwnode_handle(led_handle); + init_data.default_label = NULL; + init_data.devicename = NULL; + init_data.devname_mandatory = false; + + ret = fpc202_led_mode_set(led, FPC202_LED_MODE_OFF); + if (ret) { + dev_err(dev, "Failed to set LED %d mode, err %d\n", offset, ret); + return ret; + } + + ret = devm_led_classdev_register_ext(dev, &led->led_cdev, &init_data); + if (ret) { + dev_err(dev, "Failed to register LED %d cdev, err %d\n", offset, ret); + return ret; + } + + /* Claim corresponding GPIO line so that it cannot be interfered with */ + led->gpio = gpiochip_request_own_desc(&priv->gpio, offset, led->led_cdev.name, + GPIO_ACTIVE_HIGH, GPIOD_ASIS); + if (IS_ERR(led->gpio)) { + ret = PTR_ERR(led->gpio); + dev_err(dev, "Failed to register LED %d cdev, err %d\n", offset, ret); + } + + return ret; +} + +static int fpc202_register_leds(struct fpc202_priv *priv) +{ + struct device *dev = &priv->client->dev; + int offset, ret = 0; + + if (!devres_open_group(dev, fpc202_register_leds, GFP_KERNEL)) + return -ENOMEM; + + for_each_child_of_node_scoped(dev->of_node, led_handle) { + ret = of_property_read_u32(led_handle, "reg", &offset); + if (ret) { + dev_err(dev, "Failed to read 'reg' property of child node, err %d\n", ret); + return ret; + } + + if (offset < FPC202_GPIO_P0_S0_OUT_C || offset > FPC202_GPIO_COUNT) + continue; + + ret = fpc202_register_led(priv, offset, led_handle); + if (ret) { + dev_err(dev, "Failed to register LED %d, err %d\n", offset, + ret); + goto free_own_gpios; + } + } + + devres_close_group(dev, fpc202_register_leds); + + return 0; + +free_own_gpios: + for (offset = 0; offset < FPC202_LED_COUNT; offset++) + if (priv->leds[offset].gpio) + gpiochip_free_own_desc(priv->leds[offset].gpio); + return ret; +} + static int fpc202_probe_port(struct fpc202_priv *priv, struct device_node *i2c_handle, int port_id) { u16 aliases[FPC202_ALIASES_PER_PORT] = { }; @@ -310,13 +596,14 @@ static int fpc202_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct fpc202_priv *priv; - int ret, port_id; + int ret, port_id, led_id; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; mutex_init(&priv->reg_dev_lock); + mutex_init(&priv->led_mode_lock); priv->client = client; i2c_set_clientdata(client, priv); @@ -354,6 +641,12 @@ static int fpc202_probe(struct i2c_client *client) i2c_atr_set_driver_data(priv->atr, priv); + ret = fpc202_register_leds(priv); + if (ret) { + dev_err(dev, "Failed to register LEDs, err %d\n", ret); + goto delete_atr; + } + bitmap_zero(priv->probed_ports, FPC202_NUM_PORTS); for_each_child_of_node_scoped(dev->of_node, i2c_handle) { @@ -366,11 +659,8 @@ static int fpc202_probe(struct i2c_client *client) goto unregister_chans; } - if (port_id > FPC202_NUM_PORTS) { - dev_err(dev, "port ID %d is out of range!\n", port_id); - ret = -EINVAL; - goto unregister_chans; - } + if (port_id >= FPC202_NUM_PORTS) + continue; ret = fpc202_probe_port(priv, i2c_handle, port_id); if (ret) { @@ -385,11 +675,18 @@ unregister_chans: for_each_set_bit(port_id, priv->probed_ports, FPC202_NUM_PORTS) fpc202_remove_port(priv, port_id); + for (led_id = 0; led_id < FPC202_LED_COUNT; led_id++) + if (priv->leds[led_id].gpio) + gpiochip_free_own_desc(priv->leds[led_id].gpio); + + devres_release_group(&client->dev, fpc202_register_leds); +delete_atr: i2c_atr_delete(priv->atr); disable_gpio: fpc202_set_enable(priv, 0); gpiochip_remove(&priv->gpio); destroy_mutex: + mutex_destroy(&priv->led_mode_lock); mutex_destroy(&priv->reg_dev_lock); out: return ret; @@ -398,11 +695,19 @@ out: static void fpc202_remove(struct i2c_client *client) { struct fpc202_priv *priv = i2c_get_clientdata(client); - int port_id; + int port_id, led_id; for_each_set_bit(port_id, priv->probed_ports, FPC202_NUM_PORTS) fpc202_remove_port(priv, port_id); + for (led_id = 0; led_id < FPC202_LED_COUNT; led_id++) + if (priv->leds[led_id].gpio) + gpiochip_free_own_desc(priv->leds[led_id].gpio); + + /* Release led devices early so that blink handlers don't trigger. */ + devres_release_group(&client->dev, fpc202_register_leds); + + mutex_destroy(&priv->led_mode_lock); mutex_destroy(&priv->reg_dev_lock); i2c_atr_delete(priv->atr); diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index 872aad355dbe..b777bc3fdde2 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -2532,7 +2532,7 @@ static bool qp_wait_for_ready_queue(struct vmci_qp *qpair) * VMCI_ERROR_QUEUEPAIR_NOSPACE if no space was available to enqueue * data, VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the * queue (as defined by the queue size), VMCI_ERROR_INVALID_ARGS, if - * an error occured when accessing the buffer, + * an error occurred when accessing the buffer, * VMCI_ERROR_QUEUEPAIR_NOTATTACHED, if the queue pair pages aren't * available. Otherwise, the number of bytes written to the queue is * returned. Updates the tail pointer of the produce queue. @@ -2598,7 +2598,7 @@ static ssize_t qp_enqueue_locked(struct vmci_queue *produce_q, * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue. * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue * (as defined by the queue size). - * VMCI_ERROR_INVALID_ARGS, if an error occured when accessing the buffer. + * VMCI_ERROR_INVALID_ARGS, if an error occurred when accessing the buffer. * Otherwise the number of bytes dequeued is returned. * Side effects: * Updates the head pointer of the consume queue. diff --git a/drivers/most/most_cdev.c b/drivers/most/most_cdev.c index b31dc824466f..5df508d8d60a 100644 --- a/drivers/most/most_cdev.c +++ b/drivers/most/most_cdev.c @@ -19,11 +19,14 @@ #define CHRDEV_REGION_SIZE 50 +static const struct class most_cdev_class = { + .name = "most_cdev" +}; + static struct cdev_component { dev_t devno; struct ida minor_id; unsigned int major; - struct class *class; struct most_component cc; } comp; @@ -91,7 +94,7 @@ static void destroy_cdev(struct comp_channel *c) { unsigned long flags; - device_destroy(comp.class, c->devno); + device_destroy(&most_cdev_class, c->devno); cdev_del(&c->cdev); spin_lock_irqsave(&ch_list_lock, flags); list_del(&c->list); @@ -455,7 +458,7 @@ static int comp_probe(struct most_interface *iface, int channel_id, spin_lock_irqsave(&ch_list_lock, cl_flags); list_add_tail(&c->list, &channel_list); spin_unlock_irqrestore(&ch_list_lock, cl_flags); - c->dev = device_create(comp.class, NULL, c->devno, NULL, "%s", name); + c->dev = device_create(&most_cdev_class, NULL, c->devno, NULL, "%s", name); if (IS_ERR(c->dev)) { retval = PTR_ERR(c->dev); @@ -487,13 +490,14 @@ static struct cdev_component comp = { }, }; + static int __init most_cdev_init(void) { int err; - comp.class = class_create("most_cdev"); - if (IS_ERR(comp.class)) - return PTR_ERR(comp.class); + err = class_register(&most_cdev_class); + if (err) + return err; ida_init(&comp.minor_id); @@ -515,7 +519,7 @@ free_cdev: unregister_chrdev_region(comp.devno, CHRDEV_REGION_SIZE); dest_ida: ida_destroy(&comp.minor_id); - class_destroy(comp.class); + class_unregister(&most_cdev_class); return err; } @@ -532,7 +536,7 @@ static void __exit most_cdev_exit(void) } unregister_chrdev_region(comp.devno, CHRDEV_REGION_SIZE); ida_destroy(&comp.minor_id); - class_destroy(comp.class); + class_unregister(&most_cdev_class); } module_init(most_cdev_init); diff --git a/drivers/most/most_usb.c b/drivers/most/most_usb.c index d2c0875727a3..6437733afee0 100644 --- a/drivers/most/most_usb.c +++ b/drivers/most/most_usb.c @@ -1009,13 +1009,11 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id) goto err_free_conf; mdev->iface.channel_vector = mdev->cap; - mdev->ep_address = - kcalloc(num_endpoints, sizeof(*mdev->ep_address), GFP_KERNEL); + mdev->ep_address = kzalloc_objs(*mdev->ep_address, num_endpoints); if (!mdev->ep_address) goto err_free_cap; - mdev->busy_urbs = - kzalloc_objs(*mdev->busy_urbs, num_endpoints); + mdev->busy_urbs = kzalloc_objs(*mdev->busy_urbs, num_endpoints); if (!mdev->busy_urbs) goto err_free_ep_address; diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 796a2eccbef0..0421c6208de7 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -206,6 +206,15 @@ config MTD_PARTITIONED_MASTER the parent of the partition device be the master device, rather than what lies behind the master. +config MTD_VIRT_CONCAT + bool "Virtual concatenated MTD devices" + depends on MTD_PARTITIONED_MASTER + help + The driver enables the creation of virtual MTD device by + concatenating multiple physical MTD devices into a single + entity. This allows for the creation of partitions larger than + the individual physical chips, extending across chip boundaries. + source "drivers/mtd/chips/Kconfig" source "drivers/mtd/maps/Kconfig" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 593d0593a038..7b6dd53e8150 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -6,6 +6,7 @@ # Core functionality. obj-$(CONFIG_MTD) += mtd.o mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o +mtd-$(CONFIG_MTD_VIRT_CONCAT) += mtd_virt_concat.o obj-y += parsers/ diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 33050a2a80f7..603fd0efc2ea 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -2049,7 +2049,6 @@ err_probe: static void docg3_release(struct platform_device *pdev) { struct docg3_cascade *cascade = platform_get_drvdata(pdev); - struct docg3 *docg3 = cascade->floors[0]->priv; int floor; doc_unregister_sysfs(pdev, cascade); @@ -2057,7 +2056,7 @@ static void docg3_release(struct platform_device *pdev) if (cascade->floors[floor]) doc_release_device(cascade->floors[floor]); - bch_free(docg3->cascade->bch); + bch_free(cascade->bch); } #ifdef CONFIG_OF diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 8a8b19874e23..99d5ff9a1fbe 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -75,17 +75,6 @@ config MTD_PHYSMAP_OF physically into the CPU's memory. The mapping description here is taken from OF device tree. -config MTD_PHYSMAP_BT1_ROM - bool "Baikal-T1 Boot ROMs OF-based physical memory map handling" - depends on MTD_PHYSMAP_OF - depends on MIPS_BAIKAL_T1 || COMPILE_TEST - select MTD_COMPLEX_MAPPINGS - select MULTIPLEXER - select MUX_MMIO - help - This provides some extra DT physmap parsing for the Baikal-T1 - platforms, some detection and setting up ROMs-specific accessors. - config MTD_PHYSMAP_VERSATILE bool "ARM Versatile OF-based physical memory map handling" depends on MTD_PHYSMAP_OF diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 019f1e92cc41..51af1d2ebd52 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o physmap-y := physmap-core.o -physmap-$(CONFIG_MTD_PHYSMAP_BT1_ROM) += physmap-bt1-rom.o physmap-$(CONFIG_MTD_PHYSMAP_VERSATILE) += physmap-versatile.o physmap-$(CONFIG_MTD_PHYSMAP_GEMINI) += physmap-gemini.o physmap-$(CONFIG_MTD_PHYSMAP_IXP4XX) += physmap-ixp4xx.o diff --git a/drivers/mtd/maps/physmap-bt1-rom.c b/drivers/mtd/maps/physmap-bt1-rom.c deleted file mode 100644 index 60dccc48f99e..000000000000 --- a/drivers/mtd/maps/physmap-bt1-rom.c +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC - * - * Authors: - * Serge Semin - * - * Baikal-T1 Physically Mapped Internal ROM driver - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "physmap-bt1-rom.h" - -/* - * Baikal-T1 SoC ROMs are only accessible by the dword-aligned instructions. - * We have to take this into account when implementing the data read-methods. - * Note there is no need in bothering with endianness, since both Baikal-T1 - * CPU and MMIO are LE. - */ -static map_word __xipram bt1_rom_map_read(struct map_info *map, - unsigned long ofs) -{ - void __iomem *src = map->virt + ofs; - unsigned int shift; - map_word ret; - u32 data; - - /* Read data within offset dword. */ - shift = (uintptr_t)src & 0x3; - data = readl_relaxed(src - shift); - if (!shift) { - ret.x[0] = data; - return ret; - } - ret.x[0] = data >> (shift * BITS_PER_BYTE); - - /* Read data from the next dword. */ - shift = 4 - shift; - if (ofs + shift >= map->size) - return ret; - - data = readl_relaxed(src + shift); - ret.x[0] |= data << (shift * BITS_PER_BYTE); - - return ret; -} - -static void __xipram bt1_rom_map_copy_from(struct map_info *map, - void *to, unsigned long from, - ssize_t len) -{ - void __iomem *src = map->virt + from; - unsigned int shift, chunk; - u32 data; - - if (len <= 0 || from >= map->size) - return; - - /* Make sure we don't go over the map limit. */ - len = min_t(ssize_t, map->size - from, len); - - /* - * Since requested data size can be pretty big we have to implement - * the copy procedure as optimal as possible. That's why it's split - * up into the next three stages: unaligned head, aligned body, - * unaligned tail. - */ - shift = (uintptr_t)src & 0x3; - if (shift) { - chunk = min_t(ssize_t, 4 - shift, len); - data = readl_relaxed(src - shift); - memcpy(to, (char *)&data + shift, chunk); - src += chunk; - to += chunk; - len -= chunk; - } - - while (len >= 4) { - data = readl_relaxed(src); - memcpy(to, &data, 4); - src += 4; - to += 4; - len -= 4; - } - - if (len) { - data = readl_relaxed(src); - memcpy(to, &data, len); - } -} - -int of_flash_probe_bt1_rom(struct platform_device *pdev, - struct device_node *np, - struct map_info *map) -{ - struct device *dev = &pdev->dev; - - /* It's supposed to be read-only MTD. */ - if (!of_device_is_compatible(np, "mtd-rom")) { - dev_info(dev, "No mtd-rom compatible string\n"); - return 0; - } - - /* Multiplatform guard. */ - if (!of_device_is_compatible(np, "baikal,bt1-int-rom")) - return 0; - - /* Sanity check the device parameters retrieved from DTB. */ - if (map->bankwidth != 4) - dev_warn(dev, "Bank width is supposed to be 32 bits wide\n"); - - map->read = bt1_rom_map_read; - map->copy_from = bt1_rom_map_copy_from; - - return 0; -} diff --git a/drivers/mtd/maps/physmap-bt1-rom.h b/drivers/mtd/maps/physmap-bt1-rom.h deleted file mode 100644 index 6782899598a4..000000000000 --- a/drivers/mtd/maps/physmap-bt1-rom.h +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#include -#include - -#ifdef CONFIG_MTD_PHYSMAP_BT1_ROM -int of_flash_probe_bt1_rom(struct platform_device *pdev, - struct device_node *np, - struct map_info *map); -#else -static inline -int of_flash_probe_bt1_rom(struct platform_device *pdev, - struct device_node *np, - struct map_info *map) -{ - return 0; -} -#endif diff --git a/drivers/mtd/maps/physmap-core.c b/drivers/mtd/maps/physmap-core.c index 0dcc25b7ff98..dcda7685fc99 100644 --- a/drivers/mtd/maps/physmap-core.c +++ b/drivers/mtd/maps/physmap-core.c @@ -42,7 +42,6 @@ #include #include -#include "physmap-bt1-rom.h" #include "physmap-gemini.h" #include "physmap-ixp4xx.h" #include "physmap-versatile.h" @@ -365,10 +364,6 @@ static int physmap_flash_of_init(struct platform_device *dev) info->maps[i].bankwidth = bankwidth; info->maps[i].device_node = dp; - err = of_flash_probe_bt1_rom(dev, dp, &info->maps[i]); - if (err) - return err; - err = of_flash_probe_gemini(dev, dp, &info->maps[i]); if (err) return err; diff --git a/drivers/mtd/maps/physmap-gemini.c b/drivers/mtd/maps/physmap-gemini.c index 9d3b4bf84a1a..1c34b4ef77ea 100644 --- a/drivers/mtd/maps/physmap-gemini.c +++ b/drivers/mtd/maps/physmap-gemini.c @@ -181,7 +181,7 @@ int of_flash_probe_gemini(struct platform_device *pdev, dev_err(dev, "no enabled pin control state\n"); gf->disabled_state = pinctrl_lookup_state(gf->p, "disabled"); - if (IS_ERR(gf->enabled_state)) { + if (IS_ERR(gf->disabled_state)) { dev_err(dev, "no disabled pin control state\n"); } else { ret = pinctrl_select_state(gf->p, gf->disabled_state); diff --git a/drivers/mtd/mtd_virt_concat.c b/drivers/mtd/mtd_virt_concat.c new file mode 100644 index 000000000000..37075ead0f33 --- /dev/null +++ b/drivers/mtd/mtd_virt_concat.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Virtual concat MTD device driver + * + * Copyright (C) 2018 Bernhard Frauendienst + * Author: Bernhard Frauendienst + */ + +#include +#include +#include "mtdcore.h" +#include +#include +#include +#include +#include + +#define CONCAT_PROP "part-concat-next" +#define CONCAT_POSTFIX "concat" +#define MIN_DEV_PER_CONCAT 1 + +static LIST_HEAD(concat_node_list); + +/** + * struct mtd_virt_concat_node - components of a concatenation + * @head: List handle + * @count: Number of nodes + * @nodes: Pointer to the nodes (partitions) to concatenate + * @concat: Concatenation container + */ +struct mtd_virt_concat_node { + struct list_head head; + unsigned int count; + struct mtd_concat *concat; + struct device_node *nodes[] __counted_by(count); +}; + +/** + * mtd_is_part_concat - Check if the device is already part + * of a concatenated device + * @dev: pointer to 'device_node' + * + * Return: true if the device is already part of a concatenation, + * false otherwise. + */ +static bool mtd_is_part_concat(struct device_node *dev) +{ + struct mtd_virt_concat_node *item; + int idx; + + list_for_each_entry(item, &concat_node_list, head) { + for (idx = 0; idx < item->count; idx++) { + if (item->nodes[idx] == dev) + return true; + } + } + return false; +} + +static void mtd_virt_concat_put_mtd_devices(struct mtd_concat *concat) +{ + int i; + + for (i = 0; i < concat->num_subdev; i++) + put_mtd_device(concat->subdev[i]); +} + +void mtd_virt_concat_destroy_joins(void) +{ + struct mtd_virt_concat_node *item, *tmp; + struct mtd_info *mtd; + + list_for_each_entry_safe(item, tmp, &concat_node_list, head) { + mtd = &item->concat->mtd; + if (item->concat) { + mtd_device_unregister(mtd); + kfree(mtd->name); + mtd_concat_destroy(mtd); + mtd_virt_concat_put_mtd_devices(item->concat); + } + } +} + +/** + * mtd_virt_concat_destroy - Destroy the concat that includes the mtd object + * @mtd: pointer to 'mtd_info' + * + * Return: 0 on success, -error otherwise. + */ +int mtd_virt_concat_destroy(struct mtd_info *mtd) +{ + struct mtd_info *child, *master = mtd_get_master(mtd); + struct mtd_virt_concat_node *item, *tmp; + struct mtd_concat *concat; + int idx, ret = 0; + bool is_mtd_found; + + list_for_each_entry_safe(item, tmp, &concat_node_list, head) { + is_mtd_found = false; + + /* Find the concat item that hold the mtd device */ + for (idx = 0; idx < item->count; idx++) { + if (item->nodes[idx] == mtd->dev.of_node) { + is_mtd_found = true; + break; + } + } + if (!is_mtd_found) + continue; + concat = item->concat; + + /* + * Since this concatenated device is being removed, retrieve + * all MTD devices that are part of it and register them + * individually. + */ + for (idx = 0; idx < concat->num_subdev; idx++) { + child = concat->subdev[idx]; + if (child->dev.of_node != mtd->dev.of_node) { + ret = add_mtd_device(child); + if (ret) + goto out; + } + } + /* Destroy the concat */ + if (concat->mtd.name) { + del_mtd_device(&concat->mtd); + kfree(concat->mtd.name); + mtd_concat_destroy(&concat->mtd); + mtd_virt_concat_put_mtd_devices(item->concat); + } + + for (idx = 0; idx < item->count; idx++) + of_node_put(item->nodes[idx]); + + kfree(item); + } + return 0; +out: + mutex_lock(&master->master.partitions_lock); + list_del(&child->part.node); + mutex_unlock(&master->master.partitions_lock); + kfree(mtd->name); + kfree(mtd); + + return ret; +} + +/** + * mtd_virt_concat_create_item - Create a concat item + * @parts: pointer to 'device_node' + * @count: number of mtd devices that make up + * the concatenated device. + * + * Return: 0 on success, -error otherwise. + */ +static int mtd_virt_concat_create_item(struct device_node *parts, + unsigned int count) +{ + struct mtd_virt_concat_node *item; + struct mtd_concat *concat; + int i; + + for (i = 0; i < (count - 1); i++) { + if (mtd_is_part_concat(of_parse_phandle(parts, CONCAT_PROP, i))) + return 0; + } + + item = kzalloc_flex(*item, nodes, count, GFP_KERNEL); + if (!item) + return -ENOMEM; + + item->count = count; + + /* + * The partition in which "part-concat-next" property + * is defined is the first device in the list of concat + * devices. + */ + item->nodes[0] = parts; + + for (i = 1; i < count; i++) + item->nodes[i] = of_parse_phandle(parts, CONCAT_PROP, (i - 1)); + + concat = kzalloc_flex(*concat, subdev, count, GFP_KERNEL); + if (!concat) { + kfree(item); + return -ENOMEM; + } + + item->concat = concat; + + list_add_tail(&item->head, &concat_node_list); + + return 0; +} + +void mtd_virt_concat_destroy_items(void) +{ + struct mtd_virt_concat_node *item, *temp; + int i; + + list_for_each_entry_safe(item, temp, &concat_node_list, head) { + for (i = 0; i < item->count; i++) + of_node_put(item->nodes[i]); + + kfree(item); + } +} + +/** + * mtd_virt_concat_add - Add a mtd device to the concat list + * @mtd: pointer to 'mtd_info' + * + * Return: true on success, false otherwise. + */ +bool mtd_virt_concat_add(struct mtd_info *mtd) +{ + struct mtd_virt_concat_node *item; + struct mtd_concat *concat; + int idx; + + list_for_each_entry(item, &concat_node_list, head) { + concat = item->concat; + for (idx = 0; idx < item->count; idx++) { + if (item->nodes[idx] == mtd->dev.of_node) { + concat->subdev[concat->num_subdev++] = mtd; + return true; + } + } + } + return false; +} + +/** + * mtd_virt_concat_node_create - List all the concatenations found in DT + * + * Return: 0 on success, -error otherwise. + */ +int mtd_virt_concat_node_create(void) +{ + struct device_node *parts = NULL; + int ret = 0, count = 0; + + /* List all the concatenations found in DT */ + do { + parts = of_find_node_with_property(parts, CONCAT_PROP); + if (!of_device_is_available(parts)) + continue; + + if (mtd_is_part_concat(parts)) + continue; + + count = of_count_phandle_with_args(parts, CONCAT_PROP, NULL); + if (count < MIN_DEV_PER_CONCAT) + continue; + + /* + * The partition in which "part-concat-next" property is defined + * is also part of the concat device, so increament count by 1. + */ + count++; + + ret = mtd_virt_concat_create_item(parts, count); + if (ret) { + of_node_put(parts); + goto destroy_items; + } + } while (parts); + + return ret; + +destroy_items: + mtd_virt_concat_destroy_items(); + + return ret; +} + +/** + * mtd_virt_concat_create_join - Create and register the concatenated + * MTD device. + * + * Return: 0 on success, -error otherwise. + */ +int mtd_virt_concat_create_join(void) +{ + struct mtd_virt_concat_node *item; + struct mtd_concat *concat; + struct mtd_info *mtd; + ssize_t name_sz; + int ret, idx; + char *name; + + list_for_each_entry(item, &concat_node_list, head) { + concat = item->concat; + /* + * Check if item->count != concat->num_subdev, it indicates + * that the MTD information for all devices included in the + * concatenation are not handy, concat MTD device can't be + * created hence switch to next concat device. + */ + if (item->count != concat->num_subdev) { + continue; + } else { + /* Calculate the legth of the name of the virtual device */ + for (idx = 0, name_sz = 0; idx < concat->num_subdev; idx++) + name_sz += (strlen(concat->subdev[idx]->name) + 1); + name_sz += strlen(CONCAT_POSTFIX); + name = kmalloc(name_sz + 1, GFP_KERNEL); + if (!name) { + mtd_virt_concat_put_mtd_devices(concat); + return -ENOMEM; + } + + ret = 0; + for (idx = 0; idx < concat->num_subdev; idx++) { + ret += sprintf((name + ret), "%s-", + concat->subdev[idx]->name); + } + sprintf((name + ret), CONCAT_POSTFIX); + + if (concat->mtd.name) { + ret = memcmp(concat->mtd.name, name, name_sz); + if (ret == 0) + continue; + } + mtd = mtd_concat_create(concat->subdev, concat->num_subdev, name); + if (!mtd) { + kfree(name); + return -ENXIO; + } + concat->mtd = *mtd; + /* Arbitrary set the first device as parent */ + concat->mtd.dev.parent = concat->subdev[0]->dev.parent; + concat->mtd.dev = concat->subdev[0]->dev; + + /* Add the mtd device */ + ret = add_mtd_device(&concat->mtd); + if (ret) + goto destroy_concat; + } + } + + return 0; + +destroy_concat: + mtd_concat_destroy(mtd); + + return ret; +} diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 9eb5d919d9ba..c97167d51fe2 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -20,18 +20,6 @@ #include -/* - * Our storage structure: - * Subdev points to an array of pointers to struct mtd_info objects - * which is allocated along with this structure - * - */ -struct mtd_concat { - struct mtd_info mtd; - int num_subdev; - struct mtd_info **subdev; -}; - /* * how to calculate the size required for the above structure, * including the pointer array subdev points to: @@ -639,7 +627,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c const char *name) { /* name for the new device */ int i; - size_t size; struct mtd_concat *concat; struct mtd_info *subdev_master = NULL; uint32_t max_erasesize, curr_erasesize; @@ -652,15 +639,13 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c printk(KERN_NOTICE "into device \"%s\"\n", name); /* allocate the device structure */ - size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); - concat = kzalloc(size, GFP_KERNEL); + concat = kzalloc_flex(*concat, subdev, num_devs, GFP_KERNEL); if (!concat) { printk ("memory allocation error while creating concatenated device \"%s\"\n", name); return NULL; } - concat->subdev = (struct mtd_info **) (concat + 1); /* * Set up the new "super" device's MTD object structure, check for diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 64808493b4f5..576537774628 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -34,6 +34,7 @@ #include #include +#include #include "mtdcore.h" @@ -1120,6 +1121,12 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, goto out; } + if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) { + ret = mtd_virt_concat_node_create(); + if (ret < 0) + goto out; + } + /* Prefer parsed partitions over driver-provided fallback */ ret = parse_mtd_partitions(mtd, types, parser_data); if (ret == -EPROBE_DEFER) @@ -1137,6 +1144,11 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, if (ret) goto out; + if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) { + ret = mtd_virt_concat_create_join(); + if (ret < 0) + goto out; + } /* * FIXME: some drivers unfortunately call this function more than once. * So we have to check if we've already assigned the reboot notifier. @@ -1186,6 +1198,11 @@ int mtd_device_unregister(struct mtd_info *master) nvmem_unregister(master->otp_user_nvmem); nvmem_unregister(master->otp_factory_nvmem); + if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) { + err = mtd_virt_concat_destroy(master); + if (err) + return err; + } err = del_mtd_partitions(master); if (err) return err; @@ -2621,6 +2638,10 @@ err_reg: static void __exit cleanup_mtd(void) { + if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) { + mtd_virt_concat_destroy_joins(); + mtd_virt_concat_destroy_items(); + } debugfs_remove_recursive(dfs_dir_mtd); cleanup_mtdchar(); if (proc_mtd) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index e016cfbc7224..795a94e6b482 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "mtdcore.h" @@ -409,6 +410,11 @@ int add_mtd_partitions(struct mtd_info *parent, goto err_del_partitions; } + if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) { + if (mtd_virt_concat_add(child)) + continue; + } + mutex_lock(&master->master.partitions_lock); list_add_tail(&child->part.node, &parent->partitions); mutex_unlock(&master->master.partitions_lock); diff --git a/drivers/mtd/nand/ecc-realtek.c b/drivers/mtd/nand/ecc-realtek.c index 0046da37ea3e..7d003fd72027 100644 --- a/drivers/mtd/nand/ecc-realtek.c +++ b/drivers/mtd/nand/ecc-realtek.c @@ -17,10 +17,12 @@ * - BCH12 : Generate 20 ECC bytes from 512 data bytes plus 6 free bytes * * It can run for arbitrary NAND flash chips with different block and OOB sizes. Currently there - * are only two known devices in the wild that have NAND flash and make use of this ECC engine - * (Linksys LGS328C & LGS352C). To keep compatibility with vendor firmware, new modes can only - * be added when new data layouts have been analyzed. For now allow BCH6 on flash with 2048 byte - * blocks and 64 bytes oob. + * are a few known devices in the wild that make use of this ECC engine + * (Linksys LGS328C, LGS352C & Netlink HG323DAC). To keep compatibility with vendor firmware, + * new modes can only be added when new data layouts have been analyzed. For now allow BCH6 on + * flash with 2048 byte blocks and at least 64 bytes oob. Some vendors make use of + * 128 bytes OOB NAND chips (e.g. Macronix MX35LF1G24AD) but only use BCH6 and thus the first + * 64 bytes of the OOB area. In this case the engine leaves any extra bytes unused. * * This driver aligns with kernel ECC naming conventions. Neverthless a short notice on the * Realtek naming conventions for the different structures in the OOB area. @@ -39,7 +41,7 @@ */ #define RTL_ECC_ALLOWED_PAGE_SIZE 2048 -#define RTL_ECC_ALLOWED_OOB_SIZE 64 +#define RTL_ECC_ALLOWED_MIN_OOB_SIZE 64 #define RTL_ECC_ALLOWED_STRENGTH 6 #define RTL_ECC_BLOCK_SIZE 512 @@ -310,10 +312,10 @@ static int rtl_ecc_check_support(struct nand_device *nand) struct mtd_info *mtd = nanddev_to_mtd(nand); struct device *dev = nand->ecc.engine->dev; - if (mtd->oobsize != RTL_ECC_ALLOWED_OOB_SIZE || + if (mtd->oobsize < RTL_ECC_ALLOWED_MIN_OOB_SIZE || mtd->writesize != RTL_ECC_ALLOWED_PAGE_SIZE) { - dev_err(dev, "only flash geometry data=%d, oob=%d supported\n", - RTL_ECC_ALLOWED_PAGE_SIZE, RTL_ECC_ALLOWED_OOB_SIZE); + dev_err(dev, "only flash geometry data=%d, oob>=%d supported\n", + RTL_ECC_ALLOWED_PAGE_SIZE, RTL_ECC_ALLOWED_MIN_OOB_SIZE); return -EINVAL; } diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c index 65a36d5de742..c4018bc59670 100644 --- a/drivers/mtd/nand/raw/cafe_nand.c +++ b/drivers/mtd/nand/raw/cafe_nand.c @@ -837,9 +837,10 @@ static const struct pci_device_id cafe_nand_tbl[] = { MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); -static int cafe_nand_resume(struct pci_dev *pdev) +static int cafe_nand_resume(struct device *dev) { uint32_t ctrl; + struct pci_dev *pdev = to_pci_dev(dev); struct mtd_info *mtd = pci_get_drvdata(pdev); struct nand_chip *chip = mtd_to_nand(mtd); struct cafe_priv *cafe = nand_get_controller_data(chip); @@ -877,12 +878,14 @@ static int cafe_nand_resume(struct pci_dev *pdev) return 0; } +static DEFINE_SIMPLE_DEV_PM_OPS(cafe_nand_ops, NULL, cafe_nand_resume); + static struct pci_driver cafe_nand_pci_driver = { .name = "CAFÉ NAND", .id_table = cafe_nand_tbl, .probe = cafe_nand_probe, .remove = cafe_nand_remove, - .resume = cafe_nand_resume, + .driver.pm = &cafe_nand_ops, }; module_pci_driver(cafe_nand_pci_driver); diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index dd88b22a91bd..fad0334f759d 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -7,6 +7,7 @@ * Author: Dipen Dudhat */ +#include #include #include #include @@ -863,7 +864,14 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* Fill in fsl_ifc_mtd structure */ mtd->dev.parent = priv->dev; - nand_set_flash_node(chip, priv->dev->of_node); + + struct device_node *np __free(device_node) = + of_get_next_child_with_prefix(priv->dev->of_node, NULL, "nand"); + + if (np) + nand_set_flash_node(chip, np); + else + nand_set_flash_node(chip, priv->dev->of_node); /* fill in nand_chip structure */ /* set up function call table */ diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 51f595fbc834..c1f766cb225a 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -5,6 +5,7 @@ * Copyright (C) 2010-2015 Freescale Semiconductor, Inc. * Copyright (C) 2008 Embedded Alley Solutions, Inc. */ +#include #include #include #include @@ -2688,7 +2689,15 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */ nand_set_controller_data(chip, this); - nand_set_flash_node(chip, this->pdev->dev.of_node); + + struct device_node *np __free(device_node) = + of_get_next_child_with_prefix(this->pdev->dev.of_node, NULL, "nand"); + + if (np) + nand_set_flash_node(chip, np); + else + nand_set_flash_node(chip, this->pdev->dev.of_node); + chip->legacy.block_markbad = gpmi_block_markbad; chip->badblock_pattern = &gpmi_bbt_descr; chip->options |= NAND_NO_SUBPAGE_WRITE; diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index 8c56b685bf91..4d8b92e7e672 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -4,6 +4,7 @@ * Copyright 2008 Sascha Hauer, kernel@pengutronix.de */ +#include #include #include #include @@ -1714,7 +1715,14 @@ static int mxcnd_probe(struct platform_device *pdev) this->legacy.chip_delay = 5; nand_set_controller_data(this, host); - nand_set_flash_node(this, pdev->dev.of_node); + + struct device_node *np __free(device_node) = + of_get_next_child_with_prefix(pdev->dev.of_node, NULL, "nand"); + + if (np) + nand_set_flash_node(this, np); + else + nand_set_flash_node(this, pdev->dev.of_node); host->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index dfd8361bdd36..d6d3e17ab407 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "internals.h" @@ -4704,16 +4705,16 @@ static void nand_resume(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); - mutex_lock(&chip->lock); - if (chip->suspended) { - if (chip->ops.resume) - chip->ops.resume(chip); - chip->suspended = 0; - } else { - pr_err("%s called for a chip which is not in suspended state\n", - __func__); + scoped_guard(mutex, &chip->lock) { + if (chip->suspended) { + if (chip->ops.resume) + chip->ops.resume(chip); + chip->suspended = 0; + } else { + pr_err("%s called for a chip which is not in suspended state\n", + __func__); + } } - mutex_unlock(&chip->lock); wake_up_all(&chip->resume_wq); } diff --git a/drivers/mtd/nand/raw/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c index 0bcd455328ef..fe31551bcf5f 100644 --- a/drivers/mtd/nand/raw/plat_nand.c +++ b/drivers/mtd/nand/raw/plat_nand.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -17,6 +18,7 @@ struct plat_nand_data { struct nand_controller controller; struct nand_chip chip; void __iomem *io_base; + struct gpio_desc *ready_gpio; }; static int plat_nand_attach_chip(struct nand_chip *chip) @@ -32,6 +34,14 @@ static const struct nand_controller_ops plat_nand_ops = { .attach_chip = plat_nand_attach_chip, }; +/* Resources and device for NAND */ +static int plat_nand_gpio_dev_ready(struct nand_chip *chip) +{ + struct plat_nand_data *data = nand_get_controller_data(chip); + + return gpiod_get_value(data->ready_gpio); +} + /* * Probe for the NAND device. */ @@ -41,6 +51,7 @@ static int plat_nand_probe(struct platform_device *pdev) struct plat_nand_data *data; struct mtd_info *mtd; const char **part_types; + struct nand_chip *chip; int err = 0; if (!pdata) { @@ -59,9 +70,17 @@ static int plat_nand_probe(struct platform_device *pdev) if (!data) return -ENOMEM; + data->ready_gpio = devm_gpiod_get_optional(&pdev->dev, "ready", + GPIOD_IN); + if (IS_ERR(data->ready_gpio)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->ready_gpio), + "could not get READY GPIO\n"); + data->controller.ops = &plat_nand_ops; nand_controller_init(&data->controller); data->chip.controller = &data->controller; + chip = &data->chip; + nand_set_controller_data(chip, data); data->io_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->io_base)) @@ -74,7 +93,10 @@ static int plat_nand_probe(struct platform_device *pdev) data->chip.legacy.IO_ADDR_R = data->io_base; data->chip.legacy.IO_ADDR_W = data->io_base; data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl; - data->chip.legacy.dev_ready = pdata->ctrl.dev_ready; + if (data->ready_gpio) + data->chip.legacy.dev_ready = plat_nand_gpio_dev_ready; + else + data->chip.legacy.dev_ready = pdata->ctrl.dev_ready; data->chip.legacy.select_chip = pdata->ctrl.select_chip; data->chip.legacy.write_buf = pdata->ctrl.write_buf; data->chip.legacy.read_buf = pdata->ctrl.read_buf; diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index e66adfcca7cd..02647565c8ba 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -209,9 +209,8 @@ /* * On A10/A23, this is the size of the NDFC User Data Register, containing the - * mandatory user data bytes following the ECC for each ECC step. + * mandatory user data bytes preceding the ECC for each ECC step. * Thus, for each ECC step, we need the ECC bytes + USER_DATA_SZ. - * Those bits are currently unsused, and kept as default value 0xffffffff. * * On H6/H616, this size became configurable, from 0 bytes to 32, via the * USER_DATA_LEN registers. @@ -249,6 +248,7 @@ struct sunxi_nand_hw_ecc { * @timing_ctl: TIMING_CTL register value for this NAND chip * @nsels: number of CS lines required by the NAND chip * @sels: array of CS lines descriptions + * @user_data_bytes: array of user data lengths for all ECC steps */ struct sunxi_nand_chip { struct list_head node; @@ -257,6 +257,7 @@ struct sunxi_nand_chip { unsigned long clk_rate; u32 timing_cfg; u32 timing_ctl; + u8 *user_data_bytes; int nsels; struct sunxi_nand_chip_sel sels[] __counted_by(nsels); }; @@ -272,9 +273,11 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) * * @has_mdma: Use mbus dma mode, otherwise general dma * through MBUS on A23/A33 needs extra configuration. - * @has_ecc_block_512: If the ECC can handle 512B or only 1024B chuncks + * @has_ecc_block_512: If the ECC can handle 512B or only 1024B chunks * @has_ecc_clk: If the controller needs an ECC clock. * @has_mbus_clk: If the controller needs a mbus clock. + * @legacy_max_strength:If the maximize strength function was off by 2 bytes + * NB: this should not be used in new controllers * @reg_io_data: I/O data register * @reg_ecc_err_cnt: ECC error counter register * @reg_user_data: User data register @@ -292,7 +295,7 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) * @nstrengths: Size of @ecc_strengths * @max_ecc_steps: Maximum supported steps for ECC, this is also the * number of user data registers - * @user_data_len_tab: Table of lenghts supported by USER_DATA_LEN register + * @user_data_len_tab: Table of lengths supported by USER_DATA_LEN register * The table index is the value to set in NFC_USER_DATA_LEN * registers, and the corresponding value is the number of * bytes to write @@ -304,6 +307,7 @@ struct sunxi_nfc_caps { bool has_ecc_block_512; bool has_ecc_clk; bool has_mbus_clk; + bool legacy_max_strength; unsigned int reg_io_data; unsigned int reg_ecc_err_cnt; unsigned int reg_user_data; @@ -820,12 +824,50 @@ static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); } -static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob, - int step, bool bbm, int page) +static u8 sunxi_nfc_user_data_sz(struct sunxi_nand_chip *sunxi_nand, int step) { - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + if (!sunxi_nand->user_data_bytes) + return USER_DATA_SZ; - sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(nfc, step)), oob); + return sunxi_nand->user_data_bytes[step]; +} + +static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob, + int step, bool bbm, int page, + unsigned int user_data_sz) +{ + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + u32 user_data; + + if (!nfc->caps->reg_user_data_len) { + /* + * For A10, the user data for step n is in the nth + * REG_USER_DATA + */ + user_data = readl(nfc->regs + NFC_REG_USER_DATA(nfc, step)); + sunxi_nfc_user_data_to_buf(user_data, oob); + } else { + /* + * For H6 NAND controller, the user data for all steps is + * contained in 32 user data registers, but not at a specific + * offset for each step, they are just concatenated. + */ + unsigned int user_data_off = 0; + unsigned int reg_off; + u8 *ptr = oob; + unsigned int i; + + for (i = 0; i < step; i++) + user_data_off += sunxi_nfc_user_data_sz(sunxi_nand, i); + + user_data_off /= 4; + for (i = 0; i < user_data_sz / 4; i++, ptr += 4) { + reg_off = NFC_REG_USER_DATA(nfc, user_data_off + i); + user_data = readl(nfc->regs + reg_off); + sunxi_nfc_user_data_to_buf(user_data, ptr); + } + } /* De-randomize the Bad Block Marker. */ if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) @@ -884,17 +926,46 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand, bool bbm, int page) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - u8 user_data[USER_DATA_SZ]; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step); + u8 *user_data = NULL; /* Randomize the Bad Block Marker. */ if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { - memcpy(user_data, oob, sizeof(user_data)); + user_data = kmalloc(user_data_sz, GFP_KERNEL); + memcpy(user_data, oob, user_data_sz); sunxi_nfc_randomize_bbm(nand, page, user_data); oob = user_data; } - writel(sunxi_nfc_buf_to_user_data(oob), - nfc->regs + NFC_REG_USER_DATA(nfc, step)); + if (!nfc->caps->reg_user_data_len) { + /* + * For A10, the user data for step n is in the nth + * REG_USER_DATA + */ + writel(sunxi_nfc_buf_to_user_data(oob), + nfc->regs + NFC_REG_USER_DATA(nfc, step)); + } else { + /* + * For H6 NAND controller, the user data for all steps is + * contained in 32 user data registers, but not at a specific + * offset for each step, they are just concatenated. + */ + unsigned int user_data_off = 0; + const u8 *ptr = oob; + unsigned int i; + + for (i = 0; i < step; i++) + user_data_off += sunxi_nfc_user_data_sz(sunxi_nand, i); + + user_data_off /= 4; + for (i = 0; i < user_data_sz / 4; i++, ptr += 4) { + writel(sunxi_nfc_buf_to_user_data(ptr), + nfc->regs + NFC_REG_USER_DATA(nfc, user_data_off + i)); + } + } + + kfree(user_data); } static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand, @@ -915,6 +986,8 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob, bool *erased) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step); struct nand_ecc_ctrl *ecc = &nand->ecc; u32 tmp; @@ -937,7 +1010,7 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob, memset(data, pattern, ecc->size); if (oob) - memset(oob, pattern, ecc->bytes + USER_DATA_SZ); + memset(oob, pattern, ecc->bytes + user_data_sz); return 0; } @@ -952,14 +1025,19 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, u8 *oob, int oob_off, int *cur_off, unsigned int *max_bitflips, - bool bbm, bool oob_required, int page) + int step, bool oob_required, int page) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step); struct nand_ecc_ctrl *ecc = &nand->ecc; int raw_mode = 0; u32 pattern_found; + bool bbm = !step; bool erased; int ret; + /* From the controller point of view, we are at step 0 */ + const int nfc_step = 0; if (*cur_off != data_off) nand_change_read_column_op(nand, data_off, NULL, 0, false); @@ -973,8 +1051,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, if (ret) return ret; - sunxi_nfc_reset_user_data_len(nfc); - sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); + sunxi_nfc_set_user_data_len(nfc, user_data_sz, nfc_step); sunxi_nfc_randomizer_config(nand, page, false); sunxi_nfc_randomizer_enable(nand); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, @@ -985,15 +1062,14 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, if (ret) return ret; - *cur_off = oob_off + ecc->bytes + USER_DATA_SZ; + *cur_off = oob_off + ecc->bytes + user_data_sz; pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found); pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found); - ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, 0, - readl(nfc->regs + NFC_REG_ECC_ST), - pattern_found, - &erased); + ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, + nfc_step, readl(nfc->regs + NFC_REG_ECC_ST), + pattern_found, &erased); if (erased) return 1; @@ -1010,10 +1086,10 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, ecc->size); nand_change_read_column_op(nand, oob_off, oob, - ecc->bytes + USER_DATA_SZ, false); + ecc->bytes + user_data_sz, false); ret = nand_check_erased_ecc_chunk(data, ecc->size, oob, - ecc->bytes + USER_DATA_SZ, + ecc->bytes + user_data_sz, NULL, 0, ecc->strength); if (ret >= 0) raw_mode = 1; @@ -1023,11 +1099,11 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, if (oob_required) { nand_change_read_column_op(nand, oob_off, NULL, 0, false); - sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + USER_DATA_SZ, + sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + user_data_sz, true, page); - sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, 0, - bbm, page); + sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, nfc_step, + bbm, page, user_data_sz); } } @@ -1036,21 +1112,50 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, return raw_mode; } +/* + * Returns the offset of the OOB for each step. + * (it includes the user data before the ECC data.) + */ +static int sunxi_get_oob_offset(struct sunxi_nand_chip *sunxi_nand, + struct nand_ecc_ctrl *ecc, int step) +{ + int ecc_off = step * ecc->bytes; + int i; + + for (i = 0; i < step; i++) + ecc_off += sunxi_nfc_user_data_sz(sunxi_nand, i); + + return ecc_off; +} + +/* + * Returns the offset of the ECC for each step. + * So, it's the same as sunxi_get_oob_offset(), + * but it skips the next user data. + */ +static int sunxi_get_ecc_offset(struct sunxi_nand_chip *sunxi_nand, + struct nand_ecc_ctrl *ecc, int step) +{ + return sunxi_get_oob_offset(sunxi_nand, ecc, step) + + sunxi_nfc_user_data_sz(sunxi_nand, step); +} + static void sunxi_nfc_hw_ecc_read_extra_oob(struct nand_chip *nand, u8 *oob, int *cur_off, bool randomize, int page) { + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct mtd_info *mtd = nand_to_mtd(nand); struct nand_ecc_ctrl *ecc = &nand->ecc; - int offset = ((ecc->bytes + 4) * ecc->steps); + int offset = sunxi_get_oob_offset(sunxi_nand, ecc, ecc->steps); int len = mtd->oobsize - offset; if (len <= 0) return; - if (!cur_off || *cur_off != offset) - nand_change_read_column_op(nand, mtd->writesize, NULL, 0, - false); + if (!cur_off || *cur_off != (offset + mtd->writesize)) + nand_change_read_column_op(nand, mtd->writesize + offset, + NULL, 0, false); if (!randomize) sunxi_nfc_read_buf(nand, oob + offset, len); @@ -1067,6 +1172,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf int nchunks) { bool randomized = nand->options & NAND_NEED_SCRAMBLING; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct mtd_info *mtd = nand_to_mtd(nand); struct nand_ecc_ctrl *ecc = &nand->ecc; @@ -1086,7 +1192,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf sunxi_nfc_hw_ecc_enable(nand); sunxi_nfc_reset_user_data_len(nfc); - sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); + for (i = 0; i < nchunks; i++) + sunxi_nfc_set_user_data_len(nfc, sunxi_nfc_user_data_sz(sunxi_nand, i), i); sunxi_nfc_randomizer_config(nand, page, false); sunxi_nfc_randomizer_enable(nand); @@ -1121,7 +1228,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf for (i = 0; i < nchunks; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + USER_DATA_SZ); + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, i); + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); u8 *data = buf + data_off; u8 *oob = nand->oob_poi + oob_off; bool erased; @@ -1139,10 +1247,10 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf /* TODO: use DMA to retrieve OOB */ nand_change_read_column_op(nand, mtd->writesize + oob_off, - oob, ecc->bytes + USER_DATA_SZ, false); + oob, ecc->bytes + user_data_sz, false); - sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i, - !i, page); + sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i, !i, + page, user_data_sz); } if (erased) @@ -1154,7 +1262,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf if (status & NFC_ECC_ERR_MSK(nfc)) { for (i = 0; i < nchunks; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + USER_DATA_SZ); + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, i); + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); u8 *data = buf + data_off; u8 *oob = nand->oob_poi + oob_off; @@ -1174,10 +1283,10 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf /* TODO: use DMA to retrieve OOB */ nand_change_read_column_op(nand, mtd->writesize + oob_off, - oob, ecc->bytes + USER_DATA_SZ, false); + oob, ecc->bytes + user_data_sz, false); ret = nand_check_erased_ecc_chunk(data, ecc->size, oob, - ecc->bytes + USER_DATA_SZ, + ecc->bytes + user_data_sz, NULL, 0, ecc->strength); if (ret >= 0) @@ -1198,12 +1307,17 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand, const u8 *data, int data_off, const u8 *oob, int oob_off, - int *cur_off, bool bbm, + int *cur_off, int step, int page) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step); struct nand_ecc_ctrl *ecc = &nand->ecc; + bool bbm = !step; int ret; + /* From the controller point of view, we are at step 0 */ + const int nfc_step = 0; if (data_off != *cur_off) nand_change_write_column_op(nand, data_off, NULL, 0, false); @@ -1219,9 +1333,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand, sunxi_nfc_randomizer_config(nand, page, false); sunxi_nfc_randomizer_enable(nand); - sunxi_nfc_reset_user_data_len(nfc); - sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); - sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page); + sunxi_nfc_set_user_data_len(nfc, user_data_sz, nfc_step); + sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, nfc_step, bbm, page); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | NFC_ECC_OP, @@ -1232,7 +1345,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand, if (ret) return ret; - *cur_off = oob_off + ecc->bytes + USER_DATA_SZ; + *cur_off = oob_off + ecc->bytes + user_data_sz; return 0; } @@ -1242,8 +1355,9 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand, int page) { struct mtd_info *mtd = nand_to_mtd(nand); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct nand_ecc_ctrl *ecc = &nand->ecc; - int offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps); + int offset = sunxi_get_oob_offset(sunxi_nand, ecc, ecc->steps); int len = mtd->oobsize - offset; if (len <= 0) @@ -1262,6 +1376,8 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand, static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf, int oob_required, int page) { + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct mtd_info *mtd = nand_to_mtd(nand); struct nand_ecc_ctrl *ecc = &nand->ecc; unsigned int max_bitflips = 0; @@ -1274,16 +1390,17 @@ static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf, sunxi_nfc_hw_ecc_enable(nand); + sunxi_nfc_reset_user_data_len(nfc); for (i = 0; i < ecc->steps; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + USER_DATA_SZ); + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); u8 *data = buf + data_off; u8 *oob = nand->oob_poi + oob_off; ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off, oob, oob_off + mtd->writesize, &cur_off, &max_bitflips, - !i, oob_required, page); + i, oob_required, page); if (ret < 0) return ret; else if (ret) @@ -1321,6 +1438,8 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand, u32 data_offs, u32 readlen, u8 *bufpoi, int page) { + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct mtd_info *mtd = nand_to_mtd(nand); struct nand_ecc_ctrl *ecc = &nand->ecc; int ret, i, cur_off = 0; @@ -1332,17 +1451,18 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand, sunxi_nfc_hw_ecc_enable(nand); + sunxi_nfc_reset_user_data_len(nfc); for (i = data_offs / ecc->size; i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + USER_DATA_SZ); + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); u8 *data = bufpoi + data_off; u8 *oob = nand->oob_poi + oob_off; ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off, oob, oob_off + mtd->writesize, - &cur_off, &max_bitflips, !i, + &cur_off, &max_bitflips, i, false, page); if (ret < 0) return ret; @@ -1377,6 +1497,8 @@ static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand, const uint8_t *buf, int oob_required, int page) { + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct mtd_info *mtd = nand_to_mtd(nand); struct nand_ecc_ctrl *ecc = &nand->ecc; int ret, i, cur_off = 0; @@ -1387,15 +1509,16 @@ static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand, sunxi_nfc_hw_ecc_enable(nand); + sunxi_nfc_reset_user_data_len(nfc); for (i = 0; i < ecc->steps; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + USER_DATA_SZ); + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); const u8 *data = buf + data_off; const u8 *oob = nand->oob_poi + oob_off; ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob, oob_off + mtd->writesize, - &cur_off, !i, page); + &cur_off, i, page); if (ret) return ret; } @@ -1414,6 +1537,8 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand, const u8 *buf, int oob_required, int page) { + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct mtd_info *mtd = nand_to_mtd(nand); struct nand_ecc_ctrl *ecc = &nand->ecc; int ret, i, cur_off = 0; @@ -1424,16 +1549,17 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand, sunxi_nfc_hw_ecc_enable(nand); + sunxi_nfc_reset_user_data_len(nfc); for (i = data_offs / ecc->size; i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + USER_DATA_SZ); + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); const u8 *data = buf + data_off; const u8 *oob = nand->oob_poi + oob_off; ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob, oob_off + mtd->writesize, - &cur_off, !i, page); + &cur_off, i, page); if (ret) return ret; } @@ -1449,6 +1575,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand, int page) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct nand_ecc_ctrl *ecc = &nand->ecc; struct scatterlist sg; u32 wait; @@ -1467,10 +1594,12 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand, sunxi_nfc_reset_user_data_len(nfc); for (i = 0; i < ecc->steps; i++) { - const u8 *oob = nand->oob_poi + (i * (ecc->bytes + USER_DATA_SZ)); + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, i); + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); + const u8 *oob = nand->oob_poi + oob_off; sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page); - sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, i); + sunxi_nfc_set_user_data_len(nfc, user_data_sz, i); } nand_prog_page_begin_op(nand, page, 0, NULL, 0); @@ -1734,11 +1863,12 @@ static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section, { struct nand_chip *nand = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &nand->ecc; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); if (section >= ecc->steps) return -ERANGE; - oobregion->offset = section * (ecc->bytes + USER_DATA_SZ) + 4; + oobregion->offset = sunxi_get_ecc_offset(sunxi_nand, ecc, section); oobregion->length = ecc->bytes; return 0; @@ -1749,35 +1879,30 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, { struct nand_chip *nand = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &nand->ecc; - - if (section > ecc->steps) - return -ERANGE; - - /* - * The first 2 bytes are used for BB markers, hence we - * only have 2 bytes available in the first user data - * section. - */ - if (!section && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { - oobregion->offset = 2; - oobregion->length = 2; - - return 0; - } + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, section); /* * The controller does not provide access to OOB bytes * past the end of the ECC data. */ - if (section == ecc->steps && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) + if (section >= ecc->steps) return -ERANGE; - oobregion->offset = section * (ecc->bytes + USER_DATA_SZ); + /* + * The first 2 bytes are used for BB markers, hence we + * only have user_data_sz - 2 bytes available in the first user data + * section. + */ + if (section == 0) { + oobregion->offset = 2; + oobregion->length = user_data_sz - 2; - if (section < ecc->steps) - oobregion->length = USER_DATA_SZ; - else - oobregion->length = mtd->oobsize - oobregion->offset; + return 0; + } + + oobregion->offset = sunxi_get_ecc_offset(sunxi_nand, ecc, section); + oobregion->length = user_data_sz; return 0; } @@ -1787,6 +1912,43 @@ static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = { .free = sunxi_nand_ooblayout_free, }; +static void sunxi_nand_detach_chip(struct nand_chip *nand) +{ + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + devm_kfree(nfc->dev, sunxi_nand->user_data_bytes); + sunxi_nand->user_data_bytes = NULL; +} + +static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize, + int ecc_bytes, int nsectors) +{ + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + const struct sunxi_nfc_caps *c = nfc->caps; + int remaining_bytes = oobsize - (ecc_bytes * nsectors); + int i, step; + + sunxi_nand->user_data_bytes = devm_kzalloc(nfc->dev, nsectors, + GFP_KERNEL); + if (!sunxi_nand->user_data_bytes) + return -ENOMEM; + + for (step = 0; (step < nsectors) && (remaining_bytes > 0); step++) { + for (i = 0; i < c->nuser_data_tab; i++) { + if (c->user_data_len_tab[i] > remaining_bytes) + break; + sunxi_nand->user_data_bytes[step] = c->user_data_len_tab[i]; + } + remaining_bytes -= sunxi_nand->user_data_bytes[step]; + if (sunxi_nand->user_data_bytes[step] == 0) + break; + } + + return 0; +} + static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, struct nand_ecc_ctrl *ecc, struct device_node *np) @@ -1796,20 +1958,50 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, const u8 *strengths = nfc->caps->ecc_strengths; struct mtd_info *mtd = nand_to_mtd(nand); struct nand_device *nanddev = mtd_to_nanddev(mtd); + int total_user_data_sz = 0; int nsectors; + int ecc_mode; int i; if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { - int bytes; + int bytes = mtd->oobsize; ecc->size = 1024; nsectors = mtd->writesize / ecc->size; - /* Reserve 2 bytes for the BBM */ - bytes = (mtd->oobsize - 2) / nsectors; + if (!nfc->caps->reg_user_data_len) { + /* + * If there's a fixed user data length, subtract it before + * computing the max ECC strength + */ - /* 4 non-ECC bytes are added before each ECC bytes section */ - bytes -= USER_DATA_SZ; + for (i = 0; i < nsectors; i++) + total_user_data_sz += sunxi_nfc_user_data_sz(sunxi_nand, i); + + /* + * The 2 BBM bytes should not be removed from the grand total, + * because they are part of the USER_DATA_SZ. + * But we can't modify that for older platform since it may + * result in a stronger ECC at the end, and break the + * compatibility. + */ + if (nfc->caps->legacy_max_strength) + bytes -= 2; + + bytes -= total_user_data_sz; + } else { + /* + * remove at least the BBM size before computing the + * max ECC + */ + bytes -= 2; + } + + /* + * Once all user data has been subtracted, the rest can be used + * for ECC bytes + */ + bytes /= nsectors; /* and bytes has to be even. */ if (bytes % 2) @@ -1838,18 +2030,18 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, } /* Add ECC info retrieval from DT */ - for (i = 0; i < nfc->caps->nstrengths; i++) { - if (ecc->strength <= strengths[i]) { + for (ecc_mode = 0; ecc_mode < nfc->caps->nstrengths; ecc_mode++) { + if (ecc->strength <= strengths[ecc_mode]) { /* * Update ecc->strength value with the actual strength * that will be used by the ECC engine. */ - ecc->strength = strengths[i]; + ecc->strength = strengths[ecc_mode]; break; } } - if (i >= nfc->caps->nstrengths) { + if (ecc_mode >= nfc->caps->nstrengths) { dev_err(nfc->dev, "unsupported strength\n"); return -ENOTSUPP; } @@ -1862,7 +2054,19 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, nsectors = mtd->writesize / ecc->size; - if (mtd->oobsize < ((ecc->bytes + USER_DATA_SZ) * nsectors)) + /* + * The rationale for variable data length is to prioritize maximum ECC + * strength, and then use the remaining space for user data. + */ + if (nfc->caps->reg_user_data_len) + sunxi_nfc_maximize_user_data(nand, mtd->oobsize, ecc->bytes, + nsectors); + + if (total_user_data_sz == 0) + for (i = 0; i < nsectors; i++) + total_user_data_sz += sunxi_nfc_user_data_sz(sunxi_nand, i); + + if (mtd->oobsize < (ecc->bytes * nsectors + total_user_data_sz)) return -EINVAL; ecc->read_oob = sunxi_nfc_hw_ecc_read_oob; @@ -1885,7 +2089,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, ecc->read_oob_raw = nand_read_oob_std; ecc->write_oob_raw = nand_write_oob_std; - sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(nfc, i) | NFC_ECC_EXCEPTION | + sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(nfc, ecc_mode) | NFC_ECC_EXCEPTION | NFC_ECC_PIPELINE | NFC_ECC_EN; if (ecc->size == 512) { @@ -2092,6 +2296,7 @@ static int sunxi_nfc_exec_op(struct nand_chip *nand, static const struct nand_controller_ops sunxi_nand_controller_ops = { .attach_chip = sunxi_nand_attach_chip, + .detach_chip = sunxi_nand_detach_chip, .setup_interface = sunxi_nfc_setup_interface, .exec_op = sunxi_nfc_exec_op, }; @@ -2373,6 +2578,7 @@ static const u8 sunxi_user_data_len_h6[] = { static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .has_ecc_block_512 = true, + .legacy_max_strength = true, .reg_io_data = NFC_REG_A10_IO_DATA, .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, .reg_user_data = NFC_REG_A10_USER_DATA, @@ -2394,6 +2600,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { .has_mdma = true, .has_ecc_block_512 = true, + .legacy_max_strength = true, .reg_io_data = NFC_REG_A23_IO_DATA, .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, .reg_user_data = NFC_REG_A10_USER_DATA, diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 8aa3753aaaa1..0b076790bd9d 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -100,6 +100,17 @@ spinand_fill_page_read_op(struct spinand_device *spinand, u64 addr) return op; } +static struct spi_mem_op +spinand_fill_page_read_packed_op(struct spinand_device *spinand, u64 addr) +{ + struct spi_mem_op op = spinand->op_templates->page_read; + + op.cmd.opcode |= addr >> 16; + op.addr.val = addr & 0xFFFF; + + return op; +} + struct spi_mem_op spinand_fill_prog_exec_op(struct spinand_device *spinand, u64 addr) { @@ -453,7 +464,10 @@ static int spinand_load_page_op(struct spinand_device *spinand, { struct nand_device *nand = spinand_to_nand(spinand); unsigned int row = nanddev_pos_to_row(nand, &req->pos); - struct spi_mem_op op = SPINAND_OP(spinand, page_read, row); + bool packed = spinand->flags & SPINAND_ODTR_PACKED_PAGE_READ; + struct spi_mem_op op = packed ? + SPINAND_OP(spinand, page_read_packed, row) : + SPINAND_OP(spinand, page_read, row); return spi_mem_exec_op(spinand->spimem, &op); } @@ -1489,9 +1503,13 @@ static int spinand_init_odtr_instruction_set(struct spinand_device *spinand) if (!spi_mem_supports_op(spinand->spimem, &tmpl->blk_erase)) return -EOPNOTSUPP; - tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_8D_8D_0_OP(0); - if (!spi_mem_supports_op(spinand->spimem, &tmpl->page_read)) + if (spinand->flags & SPINAND_ODTR_PACKED_PAGE_READ) + tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_PACKED_8D_8D_0_OP(0); + else + tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_8D_8D_0_OP(0); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->page_read)) { return -EOPNOTSUPP; + } tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_8D_8D_0_OP(0); if (!spi_mem_supports_op(spinand->spimem, &tmpl->prog_exec)) diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 6dfd0dcc8ee7..7cc0f0091430 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -99,7 +99,7 @@ static SPINAND_OP_VARIANTS(update_cache_variants, #define SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, buf) \ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x81, 8), \ - SPI_MEM_DTR_OP_ADDR(4, reg, 8), \ + SPI_MEM_DTR_OP_ADDR(4, reg << 8, 8), \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_DTR_OP_DATA_OUT(2, buf, 8)) @@ -337,16 +337,19 @@ static int w25n0xjw_hs_cfg(struct spinand_device *spinand, if (iface != SSDR) return -EOPNOTSUPP; + /* + * SDR dual and quad I/O operations over 104MHz require the HS bit to + * enable a few more dummy cycles. + */ op = spinand->op_templates->read_cache; if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) hs = false; - else if (op->cmd.buswidth == 1 && op->addr.buswidth == 1 && - op->dummy.buswidth == 1 && op->data.buswidth == 1) + else if (op->cmd.buswidth != 1 || op->addr.buswidth == 1) + hs = false; + else if (op->max_freq && op->max_freq <= 104 * HZ_PER_MHZ) hs = false; - else if (!op->max_freq) - hs = true; else - hs = false; + hs = true; ret = spinand_read_reg_op(spinand, W25N0XJW_SR4, &sr4); if (ret) @@ -485,7 +488,7 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants, &write_cache_variants, &update_cache_variants), - 0, + SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&w25n01jw_ooblayout, NULL), SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)), SPINAND_INFO("W25N01KV", /* 3.3V */ @@ -515,7 +518,7 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants, &write_cache_octal_variants, &update_cache_octal_variants), - 0, + SPINAND_ODTR_PACKED_PAGE_READ, SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), @@ -526,7 +529,7 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants, &write_cache_octal_variants, &update_cache_octal_variants), - 0, + SPINAND_ODTR_PACKED_PAGE_READ, SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), @@ -549,7 +552,7 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants, &write_cache_variants, &update_cache_variants), - 0, + SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)), SPINAND_INFO("W25N02KV", /* 3.3V */ diff --git a/drivers/mtd/parsers/cmdlinepart.c b/drivers/mtd/parsers/cmdlinepart.c index 504e5fa2b45b..4caf1b3804f2 100644 --- a/drivers/mtd/parsers/cmdlinepart.c +++ b/drivers/mtd/parsers/cmdlinepart.c @@ -50,9 +50,9 @@ struct cmdline_mtd_partition { struct cmdline_mtd_partition *next; - char *mtd_id; int num_parts; struct mtd_partition *parts; + char mtd_id[]; }; /* mtdpart_setup() parses into here */ @@ -289,7 +289,6 @@ static int mtdpart_setup_real(char *s) /* enter results */ this_mtd->parts = parts; this_mtd->num_parts = num_parts; - this_mtd->mtd_id = (char*)(this_mtd + 1); strscpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1); /* link into chain */ diff --git a/drivers/mtd/parsers/ofpart_core.c b/drivers/mtd/parsers/ofpart_core.c index 0029bda165bd..262c4221d23f 100644 --- a/drivers/mtd/parsers/ofpart_core.c +++ b/drivers/mtd/parsers/ofpart_core.c @@ -75,7 +75,7 @@ static int parse_fixed_partitions(struct mtd_info *master, dedicated = false; } } else { /* Partition */ - ofpart_node = mtd_node; + ofpart_node = of_node_get(mtd_node); } of_id = of_match_node(parse_ofpart_match_table, ofpart_node); @@ -195,11 +195,11 @@ static int parse_fixed_partitions(struct mtd_info *master, ofpart_fail: pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n", master->name, pp, mtd_node); + of_node_put(pp); ret = -EINVAL; ofpart_none: if (dedicated) of_node_put(ofpart_node); - of_node_put(pp); kfree(parts); return ret; } diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 1eee519c01e5..5dd0b3cb5250 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2393,7 +2393,7 @@ static int spi_nor_spimem_check_readop(struct spi_nor *nor, /* convert the dummy cycles to the number of bytes */ op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) * op.dummy.buswidth / 8; - if (spi_nor_protocol_is_dtr(nor->read_proto)) + if (spi_nor_protocol_is_dtr(read->proto)) op.dummy.nbytes *= 2; return spi_nor_spimem_check_read_pp_op(nor, &op); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 16b382d4f04f..e838c40a2589 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -413,7 +413,7 @@ struct spi_nor_flash_parameter { * number of dummy cycles in read register ops. * @smpt_map_id: called after map ID in SMPT table has been determined for the * case the map ID is wrong and needs to be fixed. - * @post_sfdp: called after SFDP has been parsed (is also called for SPI NORs + * @post_sfdp: called after SFDP has been parsed (is not called for SPI NORs * that do not support RDSFDP). Typically used to tweak various * parameters that could not be extracted by other means (i.e. * when information provided by the SFDP/flash_info tables are diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index fa6956144d2e..14ba1680c315 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include #include #include @@ -92,7 +93,8 @@ static int spi_nor_params_show(struct seq_file *s, void *data) seq_printf(s, "address nbytes\t%u\n", nor->addr_nbytes); seq_puts(s, "flags\t\t"); - spi_nor_print_flags(s, nor->flags, snor_f_names, sizeof(snor_f_names)); + spi_nor_print_flags(s, nor->flags, snor_f_names, + ARRAY_SIZE(snor_f_names)); seq_puts(s, "\n"); seq_puts(s, "\nopcodes\n"); diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index 88033384a71e..c75b0a1cd567 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -167,6 +167,16 @@ static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor) 0, 20, SPINOR_OP_MT_DTR_RD, SNOR_PROTO_8_8_8_DTR); + /* + * Some batches of mt35xu512aba do not contain the OCT DTR command + * information, but do support OCT DTR mode. Add the settings for + * SNOR_CMD_PP_8_8_8_DTR here. This also makes sure the flash can switch + * to OCT DTR mode. + */ + nor->params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR; + spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_8_8_8_DTR], + SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR); + nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; nor->params->rdsr_dummy = 8; nor->params->rdsr_addr_nbytes = 0; @@ -185,7 +195,7 @@ static const struct spi_nor_fixups mt35xu512aba_fixups = { .post_sfdp = mt35xu512aba_post_sfdp_fixup, }; -static const struct spi_nor_fixups mt35xu01gbba_fixups = { +static const struct spi_nor_fixups mt35_two_die_fixups = { .post_sfdp = mt35xu512aba_post_sfdp_fixup, .late_init = micron_st_nor_two_die_late_init, }; @@ -202,25 +212,16 @@ static const struct flash_info micron_nor_parts[] = { .id = SNOR_ID(0x2c, 0x5b, 0x1b), .mfr_flags = USE_FSR, .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE, - .fixups = &mt35xu01gbba_fixups, + .fixups = &mt35_two_die_fixups, }, { - /* - * The MT35XU02GCBA flash device does not support chip erase, - * according to its datasheet. It supports die erase, which - * means the current driver implementation will likely need to - * be converted to use die erase. Furthermore, similar to the - * MT35XU01GBBA, the SPI_NOR_IO_MODE_EN_VOLATILE flag probably - * needs to be enabled. - * - * TODO: Fix these and test on real hardware. - */ .id = SNOR_ID(0x2c, 0x5b, 0x1c), .name = "mt35xu02g", .sector_size = SZ_128K, .size = SZ_256M, .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ, .mfr_flags = USE_FSR, - .fixup_flags = SPI_NOR_4B_OPCODES, + .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE, + .fixups = &mt35_two_die_fixups, }, }; diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c index 175211fe6a5e..db02c14ba16f 100644 --- a/drivers/mtd/spi-nor/sst.c +++ b/drivers/mtd/spi-nor/sst.c @@ -203,6 +203,8 @@ static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len, /* Start write from odd address. */ if (to % 2) { + bool needs_write_enable = (len > 1); + /* write one byte. */ ret = sst_nor_write_data(nor, to, 1, buf); if (ret < 0) @@ -210,6 +212,17 @@ static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len, to++; actual++; + + /* + * Byte program clears the write enable latch. If more + * data needs to be written using the AAI sequence, + * re-enable writes. + */ + if (needs_write_enable) { + ret = spi_nor_write_enable(nor); + if (ret) + goto out; + } } /* Write out most of the data here. */ diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 9b07f83aeac7..e67a81dbb6bf 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -28,8 +28,10 @@ static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor) { if (nor->flags & SNOR_F_HAS_SR_TB_BIT6) return SR_TB_BIT6; - else + else if (nor->flags & SNOR_F_HAS_SR_TB) return SR_TB_BIT5; + else + return 0; } static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor) diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index fb855fe44733..eaa547d36aad 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -274,6 +274,7 @@ static const struct flash_info winbond_nor_parts[] = { .id = SNOR_ID(0xef, 0x60, 0x19), .name = "w25q256jw", .size = SZ_32M, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { .id = SNOR_ID(0xef, 0x60, 0x20), @@ -295,6 +296,7 @@ static const struct flash_info winbond_nor_parts[] = { .id = SNOR_ID(0xef, 0x70, 0x17), .name = "w25q64jvm", .size = SZ_8M, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K, }, { .id = SNOR_ID(0xef, 0x70, 0x18), @@ -337,7 +339,7 @@ static const struct flash_info winbond_nor_parts[] = { .id = SNOR_ID(0xef, 0x80, 0x19), .name = "w25q256jwm", .size = SZ_32M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { .id = SNOR_ID(0xef, 0x80, 0x20), diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c index 0611ef28bb69..b61e590f2ac9 100644 --- a/drivers/mux/mmio.c +++ b/drivers/mux/mmio.c @@ -100,12 +100,14 @@ static int mux_mmio_probe(struct platform_device *pdev) mux_mmio = mux_chip_priv(mux_chip); - mux_mmio->fields = devm_kmalloc(dev, num_fields * sizeof(*mux_mmio->fields), GFP_KERNEL); + mux_mmio->fields = devm_kcalloc(dev, num_fields, sizeof(*mux_mmio->fields), + GFP_KERNEL); if (!mux_mmio->fields) return -ENOMEM; - mux_mmio->hardware_states = devm_kmalloc(dev, num_fields * - sizeof(*mux_mmio->hardware_states), GFP_KERNEL); + mux_mmio->hardware_states = devm_kcalloc(dev, num_fields, + sizeof(*mux_mmio->hardware_states), + GFP_KERNEL); if (!mux_mmio->hardware_states) return -ENOMEM; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index edaab759dc50..8ec98f6dfef9 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -503,8 +503,6 @@ source "drivers/net/arcnet/Kconfig" source "drivers/atm/Kconfig" -source "drivers/net/caif/Kconfig" - source "drivers/net/dsa/Kconfig" source "drivers/net/ethernet/Kconfig" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 5b01215f6829..b87a741fc952 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -48,14 +48,12 @@ obj-$(CONFIG_MHI_NET) += mhi_net.o # Networking Drivers # obj-$(CONFIG_ARCNET) += arcnet/ -obj-$(CONFIG_CAIF) += caif/ obj-$(CONFIG_CAN) += can/ ifdef CONFIG_NET_DSA obj-y += dsa/ endif obj-$(CONFIG_ETHERNET) += ethernet/ obj-$(CONFIG_FDDI) += fddi/ -obj-$(CONFIG_HAMRADIO) += hamradio/ obj-$(CONFIG_QCOM_IPA) += ipa/ obj-$(CONFIG_PLIP) += plip/ obj-$(CONFIG_PPP) += ppp/ diff --git a/drivers/net/Space.c b/drivers/net/Space.c index c01e2c2f7d6c..305f0a712a64 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -200,24 +200,9 @@ static int __init probe_list2(int unit, struct devprobe2 *p, int autoprobe) * look for EISA/PCI cards in addition to ISA cards). */ static struct devprobe2 isa_probes[] __initdata = { -#ifdef CONFIG_3C515 - {tc515_probe, 0}, -#endif -#ifdef CONFIG_ULTRA - {ultra_probe, 0}, -#endif -#ifdef CONFIG_WD80x3 - {wd_probe, 0}, -#endif #if defined(CONFIG_NE2000) /* ISA (use ne2k-pci for PCI cards) */ {ne_probe, 0}, #endif -#ifdef CONFIG_LANCE /* ISA/VLB (use pcnet32 for PCI cards) */ - {lance_probe, 0}, -#endif -#ifdef CONFIG_SMC9194 - {smc_init, 0}, -#endif #ifdef CONFIG_CS89x0_ISA {cs89x0_probe, 0}, #endif diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c index 0df3208783ad..da5866ba0699 100644 --- a/drivers/net/bareudp.c +++ b/drivers/net/bareudp.c @@ -529,6 +529,9 @@ static int bareudp_fill_metadata_dst(struct net_device *dev, struct in6_addr saddr; struct socket *sock = rcu_dereference(bareudp->sock); + if (!sock) + return -ESHUTDOWN; + dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sock, 0, &saddr, &info->key, sport, bareudp->port, info->key.tos, diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index af7f74cfdc08..f0aa7d2f2171 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -1029,6 +1029,7 @@ static void ad_cond_set_peer_notif(struct port *port) static void ad_mux_machine(struct port *port, bool *update_slave_arr) { struct bonding *bond = __get_bond_by_port(port); + struct aggregator *aggregator; mux_states_t last_state; /* keep current State Machine state to compare later if it was @@ -1036,6 +1037,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) */ last_state = port->sm_mux_state; + aggregator = rcu_dereference(port->aggregator); if (port->sm_vars & AD_PORT_BEGIN) { port->sm_mux_state = AD_MUX_DETACHED; } else { @@ -1055,7 +1057,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) * cycle to update ready variable, we check * READY_N and update READY here */ - __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); + __set_agg_ports_ready(aggregator, __agg_ports_are_ready(aggregator)); port->sm_mux_state = AD_MUX_DETACHED; break; } @@ -1070,7 +1072,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) * update ready variable, we check READY_N and update * READY here */ - __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); + __set_agg_ports_ready(aggregator, __agg_ports_are_ready(aggregator)); /* if the wait_while_timer expired, and the port is * in READY state, move to ATTACHED state @@ -1086,7 +1088,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) if ((port->sm_vars & AD_PORT_SELECTED) && (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) && !__check_agg_selection_timer(port)) { - if (port->aggregator->is_active) { + if (aggregator->is_active) { int state = AD_MUX_COLLECTING_DISTRIBUTING; if (!bond->params.coupled_control) @@ -1102,9 +1104,9 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) * cycle to update ready variable, we check * READY_N and update READY here */ - __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); + __set_agg_ports_ready(aggregator, __agg_ports_are_ready(aggregator)); port->sm_mux_state = AD_MUX_DETACHED; - } else if (port->aggregator->is_active) { + } else if (aggregator->is_active) { port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION; } @@ -1115,7 +1117,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) * sure that a collecting distributing * port in an active aggregator is enabled */ - if (port->aggregator->is_active && + if (aggregator->is_active && !__port_is_collecting_distributing(port)) { __enable_port(port); *update_slave_arr = true; @@ -1134,7 +1136,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) */ struct slave *slave = port->slave; - if (port->aggregator->is_active && + if (aggregator->is_active && bond_is_slave_rx_disabled(slave)) { ad_enable_collecting(port); *update_slave_arr = true; @@ -1154,8 +1156,8 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) * sure that a collecting distributing * port in an active aggregator is enabled */ - if (port->aggregator && - port->aggregator->is_active && + if (aggregator && + aggregator->is_active && !__port_is_collecting_distributing(port)) { __enable_port(port); *update_slave_arr = true; @@ -1187,7 +1189,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) port->sm_mux_timer_counter = __ad_timer_to_ticks(AD_WAIT_WHILE_TIMER, 0); break; case AD_MUX_ATTACHED: - if (port->aggregator->is_active) + if (aggregator->is_active) port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION; else @@ -1561,9 +1563,9 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) bond = __get_bond_by_port(port); /* if the port is connected to other aggregator, detach it */ - if (port->aggregator) { + temp_aggregator = rcu_dereference(port->aggregator); + if (temp_aggregator) { /* detach the port from its former aggregator */ - temp_aggregator = port->aggregator; for (curr_port = temp_aggregator->lag_ports; curr_port; last_port = curr_port, curr_port = curr_port->next_port_in_aggregator) { @@ -1586,7 +1588,7 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) /* clear the port's relations to this * aggregator */ - port->aggregator = NULL; + RCU_INIT_POINTER(port->aggregator, NULL); port->next_port_in_aggregator = NULL; port->actor_port_aggregator_identifier = 0; @@ -1609,7 +1611,7 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) port->slave->bond->dev->name, port->slave->dev->name, port->actor_port_number, - port->aggregator->aggregator_identifier); + temp_aggregator->aggregator_identifier); } } /* search on all aggregators for a suitable aggregator for this port */ @@ -1633,15 +1635,15 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) ) ) { /* attach to the founded aggregator */ - port->aggregator = aggregator; + rcu_assign_pointer(port->aggregator, aggregator); port->actor_port_aggregator_identifier = - port->aggregator->aggregator_identifier; + aggregator->aggregator_identifier; port->next_port_in_aggregator = aggregator->lag_ports; - port->aggregator->num_of_ports++; + aggregator->num_of_ports++; aggregator->lag_ports = port; slave_dbg(bond->dev, slave->dev, "Port %d joined LAG %d (existing LAG)\n", port->actor_port_number, - port->aggregator->aggregator_identifier); + aggregator->aggregator_identifier); /* mark this port as selected */ port->sm_vars |= AD_PORT_SELECTED; @@ -1656,39 +1658,40 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) if (!found) { if (free_aggregator) { /* assign port a new aggregator */ - port->aggregator = free_aggregator; port->actor_port_aggregator_identifier = - port->aggregator->aggregator_identifier; + free_aggregator->aggregator_identifier; /* update the new aggregator's parameters * if port was responsed from the end-user */ if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS) /* if port is full duplex */ - port->aggregator->is_individual = false; + free_aggregator->is_individual = false; else - port->aggregator->is_individual = true; + free_aggregator->is_individual = true; - port->aggregator->actor_admin_aggregator_key = + free_aggregator->actor_admin_aggregator_key = port->actor_admin_port_key; - port->aggregator->actor_oper_aggregator_key = + free_aggregator->actor_oper_aggregator_key = port->actor_oper_port_key; - port->aggregator->partner_system = + free_aggregator->partner_system = port->partner_oper.system; - port->aggregator->partner_system_priority = + free_aggregator->partner_system_priority = port->partner_oper.system_priority; - port->aggregator->partner_oper_aggregator_key = port->partner_oper.key; - port->aggregator->receive_state = 1; - port->aggregator->transmit_state = 1; - port->aggregator->lag_ports = port; - port->aggregator->num_of_ports++; + free_aggregator->partner_oper_aggregator_key = port->partner_oper.key; + free_aggregator->receive_state = 1; + free_aggregator->transmit_state = 1; + free_aggregator->lag_ports = port; + free_aggregator->num_of_ports++; + + rcu_assign_pointer(port->aggregator, free_aggregator); /* mark this port as selected */ port->sm_vars |= AD_PORT_SELECTED; slave_dbg(bond->dev, port->slave->dev, "Port %d joined LAG %d (new LAG)\n", port->actor_port_number, - port->aggregator->aggregator_identifier); + free_aggregator->aggregator_identifier); } else { slave_err(bond->dev, port->slave->dev, "Port %d did not find a suitable aggregator\n", @@ -1700,13 +1703,12 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) * in all aggregator's ports, else set ready=FALSE in all * aggregator's ports */ - __set_agg_ports_ready(port->aggregator, - __agg_ports_are_ready(port->aggregator)); + aggregator = rcu_dereference(port->aggregator); + __set_agg_ports_ready(aggregator, __agg_ports_are_ready(aggregator)); - aggregator = __get_first_agg(port); - ad_agg_selection_logic(aggregator, update_slave_arr); + ad_agg_selection_logic(__get_first_agg(port), update_slave_arr); - if (!port->aggregator->is_active) + if (!aggregator->is_active) port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION; } @@ -2075,13 +2077,15 @@ static void ad_initialize_port(struct port *port, const struct bond_params *bond */ static void ad_enable_collecting(struct port *port) { - if (port->aggregator->is_active) { + struct aggregator *aggregator = rcu_dereference(port->aggregator); + + if (aggregator->is_active) { struct slave *slave = port->slave; slave_dbg(slave->bond->dev, slave->dev, "Enabling collecting on port %d (LAG %d)\n", port->actor_port_number, - port->aggregator->aggregator_identifier); + aggregator->aggregator_identifier); __enable_collecting_port(port); } } @@ -2093,11 +2097,13 @@ static void ad_enable_collecting(struct port *port) */ static void ad_disable_distributing(struct port *port, bool *update_slave_arr) { - if (port->aggregator && __agg_has_partner(port->aggregator)) { + struct aggregator *aggregator = rcu_dereference(port->aggregator); + + if (aggregator && __agg_has_partner(aggregator)) { slave_dbg(port->slave->bond->dev, port->slave->dev, "Disabling distributing on port %d (LAG %d)\n", port->actor_port_number, - port->aggregator->aggregator_identifier); + aggregator->aggregator_identifier); __disable_distributing_port(port); /* Slave array needs an update */ *update_slave_arr = true; @@ -2114,11 +2120,13 @@ static void ad_disable_distributing(struct port *port, bool *update_slave_arr) static void ad_enable_collecting_distributing(struct port *port, bool *update_slave_arr) { - if (port->aggregator->is_active) { + struct aggregator *aggregator = rcu_dereference(port->aggregator); + + if (aggregator->is_active) { slave_dbg(port->slave->bond->dev, port->slave->dev, "Enabling port %d (LAG %d)\n", port->actor_port_number, - port->aggregator->aggregator_identifier); + aggregator->aggregator_identifier); __enable_port(port); /* Slave array needs update */ *update_slave_arr = true; @@ -2135,11 +2143,13 @@ static void ad_enable_collecting_distributing(struct port *port, static void ad_disable_collecting_distributing(struct port *port, bool *update_slave_arr) { - if (port->aggregator && __agg_has_partner(port->aggregator)) { + struct aggregator *aggregator = rcu_dereference(port->aggregator); + + if (aggregator && __agg_has_partner(aggregator)) { slave_dbg(port->slave->bond->dev, port->slave->dev, "Disabling port %d (LAG %d)\n", port->actor_port_number, - port->aggregator->aggregator_identifier); + aggregator->aggregator_identifier); __disable_port(port); /* Slave array needs an update */ *update_slave_arr = true; @@ -2379,7 +2389,7 @@ void bond_3ad_unbind_slave(struct slave *slave) */ for (temp_port = aggregator->lag_ports; temp_port; temp_port = temp_port->next_port_in_aggregator) { - temp_port->aggregator = new_aggregator; + rcu_assign_pointer(temp_port->aggregator, new_aggregator); temp_port->actor_port_aggregator_identifier = new_aggregator->aggregator_identifier; } @@ -2848,15 +2858,16 @@ out: int __bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info) { - struct aggregator *aggregator = NULL; + struct aggregator *aggregator = NULL, *tmp; struct list_head *iter; struct slave *slave; struct port *port; bond_for_each_slave_rcu(bond, slave, iter) { port = &(SLAVE_AD_INFO(slave)->port); - if (port->aggregator && port->aggregator->is_active) { - aggregator = port->aggregator; + tmp = rcu_dereference(port->aggregator); + if (tmp && tmp->is_active) { + aggregator = tmp; break; } } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index c7baa5c4bf40..af82a3df2c5d 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1433,7 +1433,7 @@ static void bond_poll_controller(struct net_device *bond_dev) if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct aggregator *agg = - SLAVE_AD_INFO(slave)->port.aggregator; + rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator); if (agg && agg->aggregator_identifier != ad_info.aggregator_id) @@ -5179,15 +5179,16 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave) spin_unlock_bh(&bond->mode_lock); agg_id = ad_info.aggregator_id; } + rcu_read_lock(); bond_for_each_slave(bond, slave, iter) { if (skipslave == slave) continue; all_slaves->arr[all_slaves->count++] = slave; if (BOND_MODE(bond) == BOND_MODE_8023AD) { - struct aggregator *agg; + const struct aggregator *agg; - agg = SLAVE_AD_INFO(slave)->port.aggregator; + agg = rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator); if (!agg || agg->aggregator_identifier != agg_id) continue; } @@ -5199,6 +5200,7 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave) usable_slaves->arr[usable_slaves->count++] = slave; } + rcu_read_unlock(); bond_set_slave_arr(bond, usable_slaves, all_slaves); return ret; diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index ea1a80e658ae..c7d3e0602c83 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -66,27 +66,29 @@ static int bond_fill_slave_info(struct sk_buff *skb, const struct port *ad_port; ad_port = &SLAVE_AD_INFO(slave)->port; - agg = SLAVE_AD_INFO(slave)->port.aggregator; + rcu_read_lock(); + agg = rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator); if (agg) { if (nla_put_u16(skb, IFLA_BOND_SLAVE_AD_AGGREGATOR_ID, agg->aggregator_identifier)) - goto nla_put_failure; + goto nla_put_failure_rcu; if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE, ad_port->actor_oper_port_state)) - goto nla_put_failure; + goto nla_put_failure_rcu; if (nla_put_u16(skb, IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, ad_port->partner_oper.port_state)) - goto nla_put_failure; + goto nla_put_failure_rcu; if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_ACTOR_STATE, ad_port->sm_churn_actor_state)) - goto nla_put_failure; + goto nla_put_failure_rcu; if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_PARTNER_STATE, ad_port->sm_churn_partner_state)) - goto nla_put_failure; + goto nla_put_failure_rcu; } + rcu_read_unlock(); if (nla_put_u16(skb, IFLA_BOND_SLAVE_ACTOR_PORT_PRIO, SLAVE_AD_INFO(slave)->port_priority)) @@ -95,6 +97,8 @@ static int bond_fill_slave_info(struct sk_buff *skb, return 0; +nla_put_failure_rcu: + rcu_read_unlock(); nla_put_failure: return -EMSGSIZE; } diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index e34f80305191..3714aab1a3d9 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -188,6 +188,7 @@ static void bond_info_show_master(struct seq_file *seq) } } +/* Note: runs under rcu_read_lock() */ static void bond_info_show_slave(struct seq_file *seq, const struct slave *slave) { @@ -214,7 +215,7 @@ static void bond_info_show_slave(struct seq_file *seq, if (BOND_MODE(bond) == BOND_MODE_8023AD) { const struct port *port = &SLAVE_AD_INFO(slave)->port; - const struct aggregator *agg = port->aggregator; + const struct aggregator *agg = rcu_dereference(port->aggregator); if (agg) { seq_printf(seq, "Aggregator ID: %d\n", diff --git a/drivers/net/bonding/bond_sysfs_slave.c b/drivers/net/bonding/bond_sysfs_slave.c index 36d0e8440b5b..fc6fe7181789 100644 --- a/drivers/net/bonding/bond_sysfs_slave.c +++ b/drivers/net/bonding/bond_sysfs_slave.c @@ -62,10 +62,15 @@ static ssize_t ad_aggregator_id_show(struct slave *slave, char *buf) const struct aggregator *agg; if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) { - agg = SLAVE_AD_INFO(slave)->port.aggregator; - if (agg) - return sysfs_emit(buf, "%d\n", - agg->aggregator_identifier); + rcu_read_lock(); + agg = rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator); + if (agg) { + ssize_t res = sysfs_emit(buf, "%d\n", + agg->aggregator_identifier); + rcu_read_unlock(); + return res; + } + rcu_read_unlock(); } return sysfs_emit(buf, "N/A\n"); @@ -78,7 +83,7 @@ static ssize_t ad_actor_oper_port_state_show(struct slave *slave, char *buf) if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) { ad_port = &SLAVE_AD_INFO(slave)->port; - if (ad_port->aggregator) + if (rcu_access_pointer(ad_port->aggregator)) return sysfs_emit(buf, "%u\n", ad_port->actor_oper_port_state); } @@ -93,7 +98,7 @@ static ssize_t ad_partner_oper_port_state_show(struct slave *slave, char *buf) if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) { ad_port = &SLAVE_AD_INFO(slave)->port; - if (ad_port->aggregator) + if (rcu_access_pointer(ad_port->aggregator)) return sysfs_emit(buf, "%u\n", ad_port->partner_oper.port_state); } diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig deleted file mode 100644 index 709660cb38f8..000000000000 --- a/drivers/net/caif/Kconfig +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# CAIF physical drivers -# - -menuconfig CAIF_DRIVERS - bool "CAIF transport drivers" - depends on CAIF - help - Enable this to see CAIF physical drivers. - -if CAIF_DRIVERS - -config CAIF_TTY - tristate "CAIF TTY transport driver" - depends on CAIF && TTY - default n - help - The CAIF TTY transport driver is a Line Discipline (ldisc) - identified as N_CAIF. When this ldisc is opened from user space - it will redirect the TTY's traffic into the CAIF stack. - -config CAIF_VIRTIO - tristate "CAIF virtio transport driver" - depends on CAIF && HAS_DMA - select VHOST_RING - select VIRTIO - select GENERIC_ALLOCATOR - default n - help - The CAIF driver for CAIF over Virtio. - -endif # CAIF_DRIVERS diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile deleted file mode 100644 index 97f664f8016c..000000000000 --- a/drivers/net/caif/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -ccflags-$(CONFIG_CAIF_DEBUG) := -DDEBUG - -# Serial interface -obj-$(CONFIG_CAIF_TTY) += caif_serial.o - -# Virtio interface -obj-$(CONFIG_CAIF_VIRTIO) += caif_virtio.o diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c deleted file mode 100644 index 1873d8287bb9..000000000000 --- a/drivers/net/caif/caif_serial.c +++ /dev/null @@ -1,443 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Sjur Brendeland"); -MODULE_DESCRIPTION("CAIF serial device TTY line discipline"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_LDISC(N_CAIF); - -#define SEND_QUEUE_LOW 10 -#define SEND_QUEUE_HIGH 100 -#define CAIF_SENDING 1 /* Bit 1 = 0x02*/ -#define CAIF_FLOW_OFF_SENT 4 /* Bit 4 = 0x10 */ -#define MAX_WRITE_CHUNK 4096 -#define ON 1 -#define OFF 0 -#define CAIF_MAX_MTU 4096 - -static DEFINE_SPINLOCK(ser_lock); -static LIST_HEAD(ser_list); -static LIST_HEAD(ser_release_list); - -static bool ser_loop; -module_param(ser_loop, bool, 0444); -MODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode."); - -static bool ser_use_stx = true; -module_param(ser_use_stx, bool, 0444); -MODULE_PARM_DESC(ser_use_stx, "STX enabled or not."); - -static bool ser_use_fcs = true; - -module_param(ser_use_fcs, bool, 0444); -MODULE_PARM_DESC(ser_use_fcs, "FCS enabled or not."); - -static int ser_write_chunk = MAX_WRITE_CHUNK; -module_param(ser_write_chunk, int, 0444); - -MODULE_PARM_DESC(ser_write_chunk, "Maximum size of data written to UART."); - -static struct dentry *debugfsdir; - -static int caif_net_open(struct net_device *dev); -static int caif_net_close(struct net_device *dev); - -struct ser_device { - struct caif_dev_common common; - struct list_head node; - struct net_device *dev; - struct sk_buff_head head; - struct tty_struct *tty; - bool tx_started; - unsigned long state; -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_tty_dir; - struct debugfs_blob_wrapper tx_blob; - struct debugfs_blob_wrapper rx_blob; - u8 rx_data[128]; - u8 tx_data[128]; - u8 tty_status; - -#endif -}; - -static void caifdev_setup(struct net_device *dev); -static void ldisc_tx_wakeup(struct tty_struct *tty); -#ifdef CONFIG_DEBUG_FS -static inline void update_tty_status(struct ser_device *ser) -{ - ser->tty_status = - ser->tty->flow.stopped << 5 | - ser->tty->flow.tco_stopped << 3 | - ser->tty->ctrl.packet << 2; -} -static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) -{ - ser->debugfs_tty_dir = debugfs_create_dir(tty->name, debugfsdir); - - debugfs_create_blob("last_tx_msg", 0400, ser->debugfs_tty_dir, - &ser->tx_blob); - - debugfs_create_blob("last_rx_msg", 0400, ser->debugfs_tty_dir, - &ser->rx_blob); - - debugfs_create_xul("ser_state", 0400, ser->debugfs_tty_dir, - &ser->state); - - debugfs_create_x8("tty_status", 0400, ser->debugfs_tty_dir, - &ser->tty_status); - - ser->tx_blob.data = ser->tx_data; - ser->tx_blob.size = 0; - ser->rx_blob.data = ser->rx_data; - ser->rx_blob.size = 0; -} - -static inline void debugfs_deinit(struct ser_device *ser) -{ - debugfs_remove_recursive(ser->debugfs_tty_dir); -} - -static inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size) -{ - if (size > sizeof(ser->rx_data)) - size = sizeof(ser->rx_data); - memcpy(ser->rx_data, data, size); - ser->rx_blob.data = ser->rx_data; - ser->rx_blob.size = size; -} -#else -static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) -{ -} - -static inline void debugfs_deinit(struct ser_device *ser) -{ -} - -static inline void update_tty_status(struct ser_device *ser) -{ -} - -static inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size) -{ -} -#endif - -static void ldisc_receive(struct tty_struct *tty, const u8 *data, - const u8 *flags, size_t count) -{ - struct sk_buff *skb = NULL; - struct ser_device *ser; - int ret; - - ser = tty->disc_data; - - /* - * NOTE: flags may contain information about break or overrun. - * This is not yet handled. - */ - - - /* - * Workaround for garbage at start of transmission, - * only enable if STX handling is not enabled. - */ - if (!ser->common.use_stx && !ser->tx_started) { - dev_info(&ser->dev->dev, - "Bytes received before initial transmission -" - "bytes discarded.\n"); - return; - } - - BUG_ON(ser->dev == NULL); - - /* Get a suitable caif packet and copy in data. */ - skb = netdev_alloc_skb(ser->dev, count+1); - if (skb == NULL) - return; - skb_put_data(skb, data, count); - - skb->protocol = htons(ETH_P_CAIF); - skb_reset_mac_header(skb); - debugfs_rx(ser, data, count); - /* Push received packet up the stack. */ - ret = netif_rx(skb); - if (!ret) { - ser->dev->stats.rx_packets++; - ser->dev->stats.rx_bytes += count; - } else - ++ser->dev->stats.rx_dropped; - update_tty_status(ser); -} - -static int handle_tx(struct ser_device *ser) -{ - struct tty_struct *tty; - struct sk_buff *skb; - int tty_wr, len, room; - - tty = ser->tty; - ser->tx_started = true; - - /* Enter critical section */ - if (test_and_set_bit(CAIF_SENDING, &ser->state)) - return 0; - - /* skb_peek is safe because handle_tx is called after skb_queue_tail */ - while ((skb = skb_peek(&ser->head)) != NULL) { - - /* Make sure you don't write too much */ - len = skb->len; - room = tty_write_room(tty); - if (!room) - break; - if (room > ser_write_chunk) - room = ser_write_chunk; - if (len > room) - len = room; - - /* Write to tty or loopback */ - if (!ser_loop) { - tty_wr = tty->ops->write(tty, skb->data, len); - update_tty_status(ser); - } else { - tty_wr = len; - ldisc_receive(tty, skb->data, NULL, len); - } - ser->dev->stats.tx_packets++; - ser->dev->stats.tx_bytes += tty_wr; - - /* Error on TTY ?! */ - if (tty_wr < 0) - goto error; - /* Reduce buffer written, and discard if empty */ - skb_pull(skb, tty_wr); - if (skb->len == 0) { - struct sk_buff *tmp = skb_dequeue(&ser->head); - WARN_ON(tmp != skb); - dev_consume_skb_any(skb); - } - } - /* Send flow off if queue is empty */ - if (ser->head.qlen <= SEND_QUEUE_LOW && - test_and_clear_bit(CAIF_FLOW_OFF_SENT, &ser->state) && - ser->common.flowctrl != NULL) - ser->common.flowctrl(ser->dev, ON); - clear_bit(CAIF_SENDING, &ser->state); - return 0; -error: - clear_bit(CAIF_SENDING, &ser->state); - return tty_wr; -} - -static netdev_tx_t caif_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct ser_device *ser; - - ser = netdev_priv(dev); - - /* Send flow off once, on high water mark */ - if (ser->head.qlen > SEND_QUEUE_HIGH && - !test_and_set_bit(CAIF_FLOW_OFF_SENT, &ser->state) && - ser->common.flowctrl != NULL) - - ser->common.flowctrl(ser->dev, OFF); - - skb_queue_tail(&ser->head, skb); - return handle_tx(ser); -} - - -static void ldisc_tx_wakeup(struct tty_struct *tty) -{ - struct ser_device *ser; - - ser = tty->disc_data; - BUG_ON(ser == NULL); - WARN_ON(ser->tty != tty); - handle_tx(ser); -} - - -static void ser_release(struct work_struct *work) -{ - struct list_head list; - struct ser_device *ser, *tmp; - struct tty_struct *tty; - - spin_lock(&ser_lock); - list_replace_init(&ser_release_list, &list); - spin_unlock(&ser_lock); - - if (!list_empty(&list)) { - rtnl_lock(); - list_for_each_entry_safe(ser, tmp, &list, node) { - tty = ser->tty; - dev_close(ser->dev); - unregister_netdevice(ser->dev); - debugfs_deinit(ser); - tty_kref_put(tty->link); - tty_kref_put(tty); - } - rtnl_unlock(); - } -} - -static DECLARE_WORK(ser_release_work, ser_release); - -static int ldisc_open(struct tty_struct *tty) -{ - struct ser_device *ser; - struct net_device *dev; - char name[64]; - int result; - - /* No write no play */ - if (tty->ops->write == NULL) - return -EOPNOTSUPP; - if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_TTY_CONFIG)) - return -EPERM; - - /* release devices to avoid name collision */ - ser_release(NULL); - - result = snprintf(name, sizeof(name), "cf%s", tty->name); - if (result >= IFNAMSIZ) - return -EINVAL; - dev = alloc_netdev(sizeof(*ser), name, NET_NAME_UNKNOWN, - caifdev_setup); - if (!dev) - return -ENOMEM; - - ser = netdev_priv(dev); - ser->tty = tty_kref_get(tty); - tty_kref_get(tty->link); - ser->dev = dev; - debugfs_init(ser, tty); - tty->receive_room = 4096; - tty->disc_data = ser; - set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - rtnl_lock(); - result = register_netdevice(dev); - if (result) { - tty_kref_put(tty->link); - tty_kref_put(tty); - rtnl_unlock(); - free_netdev(dev); - return -ENODEV; - } - - spin_lock(&ser_lock); - list_add(&ser->node, &ser_list); - spin_unlock(&ser_lock); - rtnl_unlock(); - netif_stop_queue(dev); - update_tty_status(ser); - return 0; -} - -static void ldisc_close(struct tty_struct *tty) -{ - struct ser_device *ser = tty->disc_data; - - spin_lock(&ser_lock); - list_move(&ser->node, &ser_release_list); - spin_unlock(&ser_lock); - schedule_work(&ser_release_work); -} - -/* The line discipline structure. */ -static struct tty_ldisc_ops caif_ldisc = { - .owner = THIS_MODULE, - .num = N_CAIF, - .name = "n_caif", - .open = ldisc_open, - .close = ldisc_close, - .receive_buf = ldisc_receive, - .write_wakeup = ldisc_tx_wakeup -}; - -static const struct net_device_ops netdev_ops = { - .ndo_open = caif_net_open, - .ndo_stop = caif_net_close, - .ndo_start_xmit = caif_xmit -}; - -static void caifdev_setup(struct net_device *dev) -{ - struct ser_device *serdev = netdev_priv(dev); - - dev->features = 0; - dev->netdev_ops = &netdev_ops; - dev->type = ARPHRD_CAIF; - dev->flags = IFF_POINTOPOINT | IFF_NOARP; - dev->mtu = CAIF_MAX_MTU; - dev->priv_flags |= IFF_NO_QUEUE; - dev->needs_free_netdev = true; - skb_queue_head_init(&serdev->head); - serdev->common.link_select = CAIF_LINK_LOW_LATENCY; - serdev->common.use_frag = true; - serdev->common.use_stx = ser_use_stx; - serdev->common.use_fcs = ser_use_fcs; - serdev->dev = dev; -} - - -static int caif_net_open(struct net_device *dev) -{ - netif_wake_queue(dev); - return 0; -} - -static int caif_net_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - -static int __init caif_ser_init(void) -{ - int ret; - - ret = tty_register_ldisc(&caif_ldisc); - if (ret < 0) - pr_err("cannot register CAIF ldisc=%d err=%d\n", N_CAIF, ret); - - debugfsdir = debugfs_create_dir("caif_serial", NULL); - return ret; -} - -static void __exit caif_ser_exit(void) -{ - spin_lock(&ser_lock); - list_splice(&ser_list, &ser_release_list); - spin_unlock(&ser_lock); - ser_release(NULL); - cancel_work_sync(&ser_release_work); - tty_unregister_ldisc(&caif_ldisc); - debugfs_remove_recursive(debugfsdir); -} - -module_init(caif_ser_init); -module_exit(caif_ser_exit); diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c deleted file mode 100644 index 8ac1a4b8e055..000000000000 --- a/drivers/net/caif/caif_virtio.c +++ /dev/null @@ -1,791 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2013 - * Authors: Vicram Arv - * Dmitry Tarnyagin - * Sjur Brendeland - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Vicram Arv"); -MODULE_AUTHOR("Sjur Brendeland"); -MODULE_DESCRIPTION("Virtio CAIF Driver"); - -/* NAPI schedule quota */ -#define CFV_DEFAULT_QUOTA 32 - -/* Defaults used if virtio config space is unavailable */ -#define CFV_DEF_MTU_SIZE 4096 -#define CFV_DEF_HEADROOM 32 -#define CFV_DEF_TAILROOM 32 - -/* Required IP header alignment */ -#define IP_HDR_ALIGN 4 - -/* struct cfv_napi_contxt - NAPI context info - * @riov: IOV holding data read from the ring. Note that riov may - * still hold data when cfv_rx_poll() returns. - * @head: Last descriptor ID we received from vringh_getdesc_kern. - * We use this to put descriptor back on the used ring. USHRT_MAX is - * used to indicate invalid head-id. - */ -struct cfv_napi_context { - struct vringh_kiov riov; - unsigned short head; -}; - -/* struct cfv_stats - statistics for debugfs - * @rx_napi_complete: Number of NAPI completions (RX) - * @rx_napi_resched: Number of calls where the full quota was used (RX) - * @rx_nomem: Number of SKB alloc failures (RX) - * @rx_kicks: Number of RX kicks - * @tx_full_ring: Number times TX ring was full - * @tx_no_mem: Number of times TX went out of memory - * @tx_flow_on: Number of flow on (TX) - * @tx_kicks: Number of TX kicks - */ -struct cfv_stats { - u32 rx_napi_complete; - u32 rx_napi_resched; - u32 rx_nomem; - u32 rx_kicks; - u32 tx_full_ring; - u32 tx_no_mem; - u32 tx_flow_on; - u32 tx_kicks; -}; - -/* struct cfv_info - Caif Virtio control structure - * @cfdev: caif common header - * @vdev: Associated virtio device - * @vr_rx: rx/downlink host vring - * @vq_tx: tx/uplink virtqueue - * @ndev: CAIF link layer device - * @watermark_tx: indicates number of free descriptors we need - * to reopen the tx-queues after overload. - * @tx_lock: protects vq_tx from concurrent use - * @tx_release_tasklet: Tasklet for freeing consumed TX buffers - * @napi: Napi context used in cfv_rx_poll() - * @ctx: Context data used in cfv_rx_poll() - * @tx_hr: transmit headroom - * @rx_hr: receive headroom - * @tx_tr: transmit tail room - * @rx_tr: receive tail room - * @mtu: transmit max size - * @mru: receive max size - * @allocsz: size of dma memory reserved for TX buffers - * @alloc_addr: virtual address to dma memory for TX buffers - * @alloc_dma: dma address to dma memory for TX buffers - * @genpool: Gen Pool used for allocating TX buffers - * @reserved_mem: Pointer to memory reserve allocated from genpool - * @reserved_size: Size of memory reserve allocated from genpool - * @stats: Statistics exposed in sysfs - * @debugfs: Debugfs dentry for statistic counters - */ -struct cfv_info { - struct caif_dev_common cfdev; - struct virtio_device *vdev; - struct vringh *vr_rx; - struct virtqueue *vq_tx; - struct net_device *ndev; - unsigned int watermark_tx; - /* Protect access to vq_tx */ - spinlock_t tx_lock; - struct tasklet_struct tx_release_tasklet; - struct napi_struct napi; - struct cfv_napi_context ctx; - u16 tx_hr; - u16 rx_hr; - u16 tx_tr; - u16 rx_tr; - u32 mtu; - u32 mru; - size_t allocsz; - void *alloc_addr; - dma_addr_t alloc_dma; - struct gen_pool *genpool; - unsigned long reserved_mem; - size_t reserved_size; - struct cfv_stats stats; - struct dentry *debugfs; -}; - -/* struct buf_info - maintains transmit buffer data handle - * @size: size of transmit buffer - * @dma_handle: handle to allocated dma device memory area - * @vaddr: virtual address mapping to allocated memory area - */ -struct buf_info { - size_t size; - u8 *vaddr; -}; - -/* Called from virtio device, in IRQ context */ -static void cfv_release_cb(struct virtqueue *vq_tx) -{ - struct cfv_info *cfv = vq_tx->vdev->priv; - - ++cfv->stats.tx_kicks; - tasklet_schedule(&cfv->tx_release_tasklet); -} - -static void free_buf_info(struct cfv_info *cfv, struct buf_info *buf_info) -{ - if (!buf_info) - return; - gen_pool_free(cfv->genpool, (unsigned long) buf_info->vaddr, - buf_info->size); - kfree(buf_info); -} - -/* This is invoked whenever the remote processor completed processing - * a TX msg we just sent, and the buffer is put back to the used ring. - */ -static void cfv_release_used_buf(struct virtqueue *vq_tx) -{ - struct cfv_info *cfv = vq_tx->vdev->priv; - unsigned long flags; - - BUG_ON(vq_tx != cfv->vq_tx); - - for (;;) { - unsigned int len; - struct buf_info *buf_info; - - /* Get used buffer from used ring to recycle used descriptors */ - spin_lock_irqsave(&cfv->tx_lock, flags); - buf_info = virtqueue_get_buf(vq_tx, &len); - spin_unlock_irqrestore(&cfv->tx_lock, flags); - - /* Stop looping if there are no more buffers to free */ - if (!buf_info) - break; - - free_buf_info(cfv, buf_info); - - /* watermark_tx indicates if we previously stopped the tx - * queues. If we have enough free stots in the virtio ring, - * re-establish memory reserved and open up tx queues. - */ - if (cfv->vq_tx->num_free <= cfv->watermark_tx) - continue; - - /* Re-establish memory reserve */ - if (cfv->reserved_mem == 0 && cfv->genpool) - cfv->reserved_mem = - gen_pool_alloc(cfv->genpool, - cfv->reserved_size); - - /* Open up the tx queues */ - if (cfv->reserved_mem) { - cfv->watermark_tx = - virtqueue_get_vring_size(cfv->vq_tx); - netif_tx_wake_all_queues(cfv->ndev); - /* Buffers are recycled in cfv_netdev_tx, so - * disable notifications when queues are opened. - */ - virtqueue_disable_cb(cfv->vq_tx); - ++cfv->stats.tx_flow_on; - } else { - /* if no memory reserve, wait for more free slots */ - WARN_ON(cfv->watermark_tx > - virtqueue_get_vring_size(cfv->vq_tx)); - cfv->watermark_tx += - virtqueue_get_vring_size(cfv->vq_tx) / 4; - } - } -} - -/* Allocate a SKB and copy packet data to it */ -static struct sk_buff *cfv_alloc_and_copy_skb(int *err, - struct cfv_info *cfv, - u8 *frm, u32 frm_len) -{ - struct sk_buff *skb; - u32 cfpkt_len, pad_len; - - *err = 0; - /* Verify that packet size with down-link header and mtu size */ - if (frm_len > cfv->mru || frm_len <= cfv->rx_hr + cfv->rx_tr) { - netdev_err(cfv->ndev, - "Invalid frmlen:%u mtu:%u hr:%d tr:%d\n", - frm_len, cfv->mru, cfv->rx_hr, - cfv->rx_tr); - *err = -EPROTO; - return NULL; - } - - cfpkt_len = frm_len - (cfv->rx_hr + cfv->rx_tr); - pad_len = (unsigned long)(frm + cfv->rx_hr) & (IP_HDR_ALIGN - 1); - - skb = netdev_alloc_skb(cfv->ndev, frm_len + pad_len); - if (!skb) { - *err = -ENOMEM; - return NULL; - } - - skb_reserve(skb, cfv->rx_hr + pad_len); - - skb_put_data(skb, frm + cfv->rx_hr, cfpkt_len); - return skb; -} - -/* Get packets from the host vring */ -static int cfv_rx_poll(struct napi_struct *napi, int quota) -{ - struct cfv_info *cfv = container_of(napi, struct cfv_info, napi); - int rxcnt = 0; - int err = 0; - void *buf; - struct sk_buff *skb; - struct vringh_kiov *riov = &cfv->ctx.riov; - unsigned int skb_len; - - do { - skb = NULL; - - /* Put the previous iovec back on the used ring and - * fetch a new iovec if we have processed all elements. - */ - if (riov->i == riov->used) { - if (cfv->ctx.head != USHRT_MAX) { - vringh_complete_kern(cfv->vr_rx, - cfv->ctx.head, - 0); - cfv->ctx.head = USHRT_MAX; - } - - err = vringh_getdesc_kern( - cfv->vr_rx, - riov, - NULL, - &cfv->ctx.head, - GFP_ATOMIC); - - if (err <= 0) - goto exit; - } - - buf = phys_to_virt((unsigned long) riov->iov[riov->i].iov_base); - /* TODO: Add check on valid buffer address */ - - skb = cfv_alloc_and_copy_skb(&err, cfv, buf, - riov->iov[riov->i].iov_len); - if (unlikely(err)) - goto exit; - - /* Push received packet up the stack. */ - skb_len = skb->len; - skb->protocol = htons(ETH_P_CAIF); - skb_reset_mac_header(skb); - skb->dev = cfv->ndev; - err = netif_receive_skb(skb); - if (unlikely(err)) { - ++cfv->ndev->stats.rx_dropped; - } else { - ++cfv->ndev->stats.rx_packets; - cfv->ndev->stats.rx_bytes += skb_len; - } - - ++riov->i; - ++rxcnt; - } while (rxcnt < quota); - - ++cfv->stats.rx_napi_resched; - goto out; - -exit: - switch (err) { - case 0: - ++cfv->stats.rx_napi_complete; - - /* Really out of packets? (stolen from virtio_net)*/ - napi_complete(napi); - if (unlikely(!vringh_notify_enable_kern(cfv->vr_rx)) && - napi_schedule_prep(napi)) { - vringh_notify_disable_kern(cfv->vr_rx); - __napi_schedule(napi); - } - break; - - case -ENOMEM: - ++cfv->stats.rx_nomem; - dev_kfree_skb(skb); - /* Stop NAPI poll on OOM, we hope to be polled later */ - napi_complete(napi); - vringh_notify_enable_kern(cfv->vr_rx); - break; - - default: - /* We're doomed, any modem fault is fatal */ - netdev_warn(cfv->ndev, "Bad ring, disable device\n"); - cfv->ndev->stats.rx_dropped = riov->used - riov->i; - napi_complete(napi); - vringh_notify_disable_kern(cfv->vr_rx); - netif_carrier_off(cfv->ndev); - break; - } -out: - if (rxcnt && vringh_need_notify_kern(cfv->vr_rx) > 0) - vringh_notify(cfv->vr_rx); - return rxcnt; -} - -static void cfv_recv(struct virtio_device *vdev, struct vringh *vr_rx) -{ - struct cfv_info *cfv = vdev->priv; - - ++cfv->stats.rx_kicks; - vringh_notify_disable_kern(cfv->vr_rx); - napi_schedule(&cfv->napi); -} - -static void cfv_destroy_genpool(struct cfv_info *cfv) -{ - if (cfv->alloc_addr) - dma_free_coherent(cfv->vdev->dev.parent->parent, - cfv->allocsz, cfv->alloc_addr, - cfv->alloc_dma); - - if (!cfv->genpool) - return; - gen_pool_free(cfv->genpool, cfv->reserved_mem, - cfv->reserved_size); - gen_pool_destroy(cfv->genpool); - cfv->genpool = NULL; -} - -static int cfv_create_genpool(struct cfv_info *cfv) -{ - int err; - - /* dma_alloc can only allocate whole pages, and we need a more - * fine graned allocation so we use genpool. We ask for space needed - * by IP and a full ring. If the dma allcoation fails we retry with a - * smaller allocation size. - */ - err = -ENOMEM; - cfv->allocsz = (virtqueue_get_vring_size(cfv->vq_tx) * - (ETH_DATA_LEN + cfv->tx_hr + cfv->tx_tr) * 11)/10; - if (cfv->allocsz <= (num_possible_cpus() + 1) * cfv->ndev->mtu) - return -EINVAL; - - for (;;) { - if (cfv->allocsz <= num_possible_cpus() * cfv->ndev->mtu) { - netdev_info(cfv->ndev, "Not enough device memory\n"); - return -ENOMEM; - } - - cfv->alloc_addr = dma_alloc_coherent( - cfv->vdev->dev.parent->parent, - cfv->allocsz, &cfv->alloc_dma, - GFP_ATOMIC); - if (cfv->alloc_addr) - break; - - cfv->allocsz = (cfv->allocsz * 3) >> 2; - } - - netdev_dbg(cfv->ndev, "Allocated %zd bytes from dma-memory\n", - cfv->allocsz); - - /* Allocate on 128 bytes boundaries (1 << 7)*/ - cfv->genpool = gen_pool_create(7, -1); - if (!cfv->genpool) - goto err; - - err = gen_pool_add_virt(cfv->genpool, (unsigned long)cfv->alloc_addr, - (phys_addr_t)virt_to_phys(cfv->alloc_addr), - cfv->allocsz, -1); - if (err) - goto err; - - /* Reserve some memory for low memory situations. If we hit the roof - * in the memory pool, we stop TX flow and release the reserve. - */ - cfv->reserved_size = num_possible_cpus() * cfv->ndev->mtu; - cfv->reserved_mem = gen_pool_alloc(cfv->genpool, - cfv->reserved_size); - if (!cfv->reserved_mem) { - err = -ENOMEM; - goto err; - } - - cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx); - return 0; -err: - cfv_destroy_genpool(cfv); - return err; -} - -/* Enable the CAIF interface and allocate the memory-pool */ -static int cfv_netdev_open(struct net_device *netdev) -{ - struct cfv_info *cfv = netdev_priv(netdev); - - if (cfv_create_genpool(cfv)) - return -ENOMEM; - - netif_carrier_on(netdev); - napi_enable(&cfv->napi); - - /* Schedule NAPI to read any pending packets */ - napi_schedule(&cfv->napi); - return 0; -} - -/* Disable the CAIF interface and free the memory-pool */ -static int cfv_netdev_close(struct net_device *netdev) -{ - struct cfv_info *cfv = netdev_priv(netdev); - unsigned long flags; - struct buf_info *buf_info; - - /* Disable interrupts, queues and NAPI polling */ - netif_carrier_off(netdev); - virtqueue_disable_cb(cfv->vq_tx); - vringh_notify_disable_kern(cfv->vr_rx); - napi_disable(&cfv->napi); - - /* Release any TX buffers on both used and available rings */ - cfv_release_used_buf(cfv->vq_tx); - spin_lock_irqsave(&cfv->tx_lock, flags); - while ((buf_info = virtqueue_detach_unused_buf(cfv->vq_tx))) - free_buf_info(cfv, buf_info); - spin_unlock_irqrestore(&cfv->tx_lock, flags); - - /* Release all dma allocated memory and destroy the pool */ - cfv_destroy_genpool(cfv); - return 0; -} - -/* Allocate a buffer in dma-memory and copy skb to it */ -static struct buf_info *cfv_alloc_and_copy_to_shm(struct cfv_info *cfv, - struct sk_buff *skb, - struct scatterlist *sg) -{ - struct caif_payload_info *info = (void *)&skb->cb; - struct buf_info *buf_info = NULL; - u8 pad_len, hdr_ofs; - - if (!cfv->genpool) - goto err; - - if (unlikely(cfv->tx_hr + skb->len + cfv->tx_tr > cfv->mtu)) { - netdev_warn(cfv->ndev, "Invalid packet len (%d > %d)\n", - cfv->tx_hr + skb->len + cfv->tx_tr, cfv->mtu); - goto err; - } - - buf_info = kmalloc_obj(struct buf_info, GFP_ATOMIC); - if (unlikely(!buf_info)) - goto err; - - /* Make the IP header aligned in the buffer */ - hdr_ofs = cfv->tx_hr + info->hdr_len; - pad_len = hdr_ofs & (IP_HDR_ALIGN - 1); - buf_info->size = cfv->tx_hr + skb->len + cfv->tx_tr + pad_len; - - /* allocate dma memory buffer */ - buf_info->vaddr = (void *)gen_pool_alloc(cfv->genpool, buf_info->size); - if (unlikely(!buf_info->vaddr)) - goto err; - - /* copy skbuf contents to send buffer */ - skb_copy_bits(skb, 0, buf_info->vaddr + cfv->tx_hr + pad_len, skb->len); - sg_init_one(sg, buf_info->vaddr + pad_len, - skb->len + cfv->tx_hr + cfv->rx_hr); - - return buf_info; -err: - kfree(buf_info); - return NULL; -} - -/* Put the CAIF packet on the virtio ring and kick the receiver */ -static netdev_tx_t cfv_netdev_tx(struct sk_buff *skb, struct net_device *netdev) -{ - struct cfv_info *cfv = netdev_priv(netdev); - struct buf_info *buf_info; - struct scatterlist sg; - unsigned long flags; - bool flow_off = false; - int ret; - - /* garbage collect released buffers */ - cfv_release_used_buf(cfv->vq_tx); - spin_lock_irqsave(&cfv->tx_lock, flags); - - /* Flow-off check takes into account number of cpus to make sure - * virtqueue will not be overfilled in any possible smp conditions. - * - * Flow-on is triggered when sufficient buffers are freed - */ - if (unlikely(cfv->vq_tx->num_free <= num_present_cpus())) { - flow_off = true; - cfv->stats.tx_full_ring++; - } - - /* If we run out of memory, we release the memory reserve and retry - * allocation. - */ - buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg); - if (unlikely(!buf_info)) { - cfv->stats.tx_no_mem++; - flow_off = true; - - if (cfv->reserved_mem && cfv->genpool) { - gen_pool_free(cfv->genpool, cfv->reserved_mem, - cfv->reserved_size); - cfv->reserved_mem = 0; - buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg); - } - } - - if (unlikely(flow_off)) { - /* Turn flow on when a 1/4 of the descriptors are released */ - cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx) / 4; - /* Enable notifications of recycled TX buffers */ - virtqueue_enable_cb(cfv->vq_tx); - netif_tx_stop_all_queues(netdev); - } - - if (unlikely(!buf_info)) { - /* If the memory reserve does it's job, this shouldn't happen */ - netdev_warn(cfv->ndev, "Out of gen_pool memory\n"); - goto err; - } - - ret = virtqueue_add_outbuf(cfv->vq_tx, &sg, 1, buf_info, GFP_ATOMIC); - if (unlikely((ret < 0))) { - /* If flow control works, this shouldn't happen */ - netdev_warn(cfv->ndev, "Failed adding buffer to TX vring:%d\n", - ret); - goto err; - } - - /* update netdev statistics */ - cfv->ndev->stats.tx_packets++; - cfv->ndev->stats.tx_bytes += skb->len; - spin_unlock_irqrestore(&cfv->tx_lock, flags); - - /* tell the remote processor it has a pending message to read */ - virtqueue_kick(cfv->vq_tx); - - dev_kfree_skb(skb); - return NETDEV_TX_OK; -err: - spin_unlock_irqrestore(&cfv->tx_lock, flags); - cfv->ndev->stats.tx_dropped++; - free_buf_info(cfv, buf_info); - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -static void cfv_tx_release_tasklet(struct tasklet_struct *t) -{ - struct cfv_info *cfv = from_tasklet(cfv, t, tx_release_tasklet); - cfv_release_used_buf(cfv->vq_tx); -} - -static const struct net_device_ops cfv_netdev_ops = { - .ndo_open = cfv_netdev_open, - .ndo_stop = cfv_netdev_close, - .ndo_start_xmit = cfv_netdev_tx, -}; - -static void cfv_netdev_setup(struct net_device *netdev) -{ - netdev->netdev_ops = &cfv_netdev_ops; - netdev->type = ARPHRD_CAIF; - netdev->tx_queue_len = 100; - netdev->flags = IFF_POINTOPOINT | IFF_NOARP; - netdev->mtu = CFV_DEF_MTU_SIZE; - netdev->needs_free_netdev = true; -} - -/* Create debugfs counters for the device */ -static inline void debugfs_init(struct cfv_info *cfv) -{ - cfv->debugfs = debugfs_create_dir(netdev_name(cfv->ndev), NULL); - - debugfs_create_u32("rx-napi-complete", 0400, cfv->debugfs, - &cfv->stats.rx_napi_complete); - debugfs_create_u32("rx-napi-resched", 0400, cfv->debugfs, - &cfv->stats.rx_napi_resched); - debugfs_create_u32("rx-nomem", 0400, cfv->debugfs, - &cfv->stats.rx_nomem); - debugfs_create_u32("rx-kicks", 0400, cfv->debugfs, - &cfv->stats.rx_kicks); - debugfs_create_u32("tx-full-ring", 0400, cfv->debugfs, - &cfv->stats.tx_full_ring); - debugfs_create_u32("tx-no-mem", 0400, cfv->debugfs, - &cfv->stats.tx_no_mem); - debugfs_create_u32("tx-kicks", 0400, cfv->debugfs, - &cfv->stats.tx_kicks); - debugfs_create_u32("tx-flow-on", 0400, cfv->debugfs, - &cfv->stats.tx_flow_on); -} - -/* Setup CAIF for the a virtio device */ -static int cfv_probe(struct virtio_device *vdev) -{ - vrh_callback_t *vrh_cbs = cfv_recv; - const char *cfv_netdev_name = "cfvrt"; - struct net_device *netdev; - struct cfv_info *cfv; - int err; - - netdev = alloc_netdev(sizeof(struct cfv_info), cfv_netdev_name, - NET_NAME_UNKNOWN, cfv_netdev_setup); - if (!netdev) - return -ENOMEM; - - cfv = netdev_priv(netdev); - cfv->vdev = vdev; - cfv->ndev = netdev; - - spin_lock_init(&cfv->tx_lock); - - /* Get the RX virtio ring. This is a "host side vring". */ - err = -ENODEV; - if (!vdev->vringh_config || !vdev->vringh_config->find_vrhs) - goto err; - - err = vdev->vringh_config->find_vrhs(vdev, 1, &cfv->vr_rx, &vrh_cbs); - if (err) - goto err; - - /* Get the TX virtio ring. This is a "guest side vring". */ - cfv->vq_tx = virtio_find_single_vq(vdev, cfv_release_cb, "output"); - if (IS_ERR(cfv->vq_tx)) { - err = PTR_ERR(cfv->vq_tx); - goto err; - } - - /* Get the CAIF configuration from virtio config space, if available */ - if (vdev->config->get) { - virtio_cread(vdev, struct virtio_caif_transf_config, headroom, - &cfv->tx_hr); - virtio_cread(vdev, struct virtio_caif_transf_config, headroom, - &cfv->rx_hr); - virtio_cread(vdev, struct virtio_caif_transf_config, tailroom, - &cfv->tx_tr); - virtio_cread(vdev, struct virtio_caif_transf_config, tailroom, - &cfv->rx_tr); - virtio_cread(vdev, struct virtio_caif_transf_config, mtu, - &cfv->mtu); - virtio_cread(vdev, struct virtio_caif_transf_config, mtu, - &cfv->mru); - } else { - cfv->tx_hr = CFV_DEF_HEADROOM; - cfv->rx_hr = CFV_DEF_HEADROOM; - cfv->tx_tr = CFV_DEF_TAILROOM; - cfv->rx_tr = CFV_DEF_TAILROOM; - cfv->mtu = CFV_DEF_MTU_SIZE; - cfv->mru = CFV_DEF_MTU_SIZE; - } - - netdev->needed_headroom = cfv->tx_hr; - netdev->needed_tailroom = cfv->tx_tr; - - /* Disable buffer release interrupts unless we have stopped TX queues */ - virtqueue_disable_cb(cfv->vq_tx); - - netdev->mtu = cfv->mtu - cfv->tx_tr; - vdev->priv = cfv; - - /* Initialize NAPI poll context data */ - vringh_kiov_init(&cfv->ctx.riov, NULL, 0); - cfv->ctx.head = USHRT_MAX; - netif_napi_add_weight(netdev, &cfv->napi, cfv_rx_poll, - CFV_DEFAULT_QUOTA); - - tasklet_setup(&cfv->tx_release_tasklet, cfv_tx_release_tasklet); - - /* Carrier is off until netdevice is opened */ - netif_carrier_off(netdev); - - /* serialize netdev register + virtio_device_ready() with ndo_open() */ - rtnl_lock(); - - /* register Netdev */ - err = register_netdevice(netdev); - if (err) { - rtnl_unlock(); - dev_err(&vdev->dev, "Unable to register netdev (%d)\n", err); - goto err; - } - - virtio_device_ready(vdev); - - rtnl_unlock(); - - debugfs_init(cfv); - - return 0; -err: - netdev_warn(cfv->ndev, "CAIF Virtio probe failed:%d\n", err); - - if (cfv->vr_rx) - vdev->vringh_config->del_vrhs(cfv->vdev); - if (cfv->vq_tx) - vdev->config->del_vqs(cfv->vdev); - free_netdev(netdev); - return err; -} - -static void cfv_remove(struct virtio_device *vdev) -{ - struct cfv_info *cfv = vdev->priv; - - rtnl_lock(); - dev_close(cfv->ndev); - rtnl_unlock(); - - tasklet_kill(&cfv->tx_release_tasklet); - debugfs_remove_recursive(cfv->debugfs); - - vringh_kiov_cleanup(&cfv->ctx.riov); - virtio_reset_device(vdev); - vdev->vringh_config->del_vrhs(cfv->vdev); - cfv->vr_rx = NULL; - vdev->config->del_vqs(cfv->vdev); - unregister_netdev(cfv->ndev); -} - -static struct virtio_device_id id_table[] = { - { VIRTIO_ID_CAIF, VIRTIO_DEV_ANY_ID }, - { 0 }, -}; - -static unsigned int features[] = { -}; - -static struct virtio_driver caif_virtio_driver = { - .feature_table = features, - .feature_table_size = ARRAY_SIZE(features), - .driver.name = KBUILD_MODNAME, - .id_table = id_table, - .probe = cfv_probe, - .remove = cfv_remove, -}; - -module_virtio_driver(caif_virtio_driver); -MODULE_DEVICE_TABLE(virtio, id_table); diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index b9423389c2ef..44d670904ad8 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -25,6 +25,9 @@ #include "mt7530.h" +#define MT7530_STATS_POLL_INTERVAL (1 * HZ) +#define MT7530_STATS_RATE_LIMIT (HZ / 10) + static struct mt753x_pcs *pcs_to_mt753x_pcs(struct phylink_pcs *pcs) { return container_of(pcs, struct mt753x_pcs, pcs); @@ -906,10 +909,9 @@ static void mt7530_get_rmon_stats(struct dsa_switch *ds, int port, *ranges = mt7530_rmon_ranges; } -static void mt7530_get_stats64(struct dsa_switch *ds, int port, - struct rtnl_link_stats64 *storage) +static void mt7530_read_port_stats64(struct mt7530_priv *priv, int port, + struct rtnl_link_stats64 *storage) { - struct mt7530_priv *priv = ds->priv; uint64_t data; /* MIB counter doesn't provide a FramesTransmittedOK but instead @@ -951,6 +953,54 @@ static void mt7530_get_stats64(struct dsa_switch *ds, int port, &storage->rx_crc_errors); } +static void mt7530_stats_refresh(struct mt7530_priv *priv) +{ + struct rtnl_link_stats64 stats = {}; + struct dsa_port *dp; + int port; + + dsa_switch_for_each_user_port(dp, priv->ds) { + port = dp->index; + + mt7530_read_port_stats64(priv, port, &stats); + + spin_lock_bh(&priv->stats_lock); + priv->ports[port].stats = stats; + priv->stats_last = jiffies; + spin_unlock_bh(&priv->stats_lock); + } +} + +static void mt7530_stats_poll(struct work_struct *work) +{ + struct mt7530_priv *priv = container_of(work, struct mt7530_priv, + stats_work.work); + + mt7530_stats_refresh(priv); + schedule_delayed_work(&priv->stats_work, + MT7530_STATS_POLL_INTERVAL); +} + +static void mt7530_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *storage) +{ + struct mt7530_priv *priv = ds->priv; + bool refresh; + + if (priv->bus) { + spin_lock_bh(&priv->stats_lock); + *storage = priv->ports[port].stats; + refresh = time_after(jiffies, priv->stats_last + + MT7530_STATS_RATE_LIMIT); + spin_unlock_bh(&priv->stats_lock); + if (refresh) + mod_delayed_work(system_percpu_wq, + &priv->stats_work, 0); + } else { + mt7530_read_port_stats64(priv, port, storage); + } +} + static void mt7530_get_eth_ctrl_stats(struct dsa_switch *ds, int port, struct ethtool_eth_ctrl_stats *ctrl_stats) { @@ -3137,9 +3187,24 @@ mt753x_setup(struct dsa_switch *ds) if (ret && priv->irq_domain) mt7530_free_mdio_irq(priv); + if (!ret && priv->bus) { + mt7530_stats_refresh(priv); + schedule_delayed_work(&priv->stats_work, + MT7530_STATS_POLL_INTERVAL); + } + return ret; } +static void +mt753x_teardown(struct dsa_switch *ds) +{ + struct mt7530_priv *priv = ds->priv; + + if (priv->bus) + cancel_delayed_work_sync(&priv->stats_work); +} + static int mt753x_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e) { @@ -3257,6 +3322,7 @@ static int mt7988_setup(struct dsa_switch *ds) static const struct dsa_switch_ops mt7530_switch_ops = { .get_tag_protocol = mtk_get_tag_protocol, .setup = mt753x_setup, + .teardown = mt753x_teardown, .preferred_default_local_cpu_port = mt753x_preferred_default_local_cpu_port, .get_strings = mt7530_get_strings, .get_ethtool_stats = mt7530_get_ethtool_stats, @@ -3395,6 +3461,9 @@ mt7530_probe_common(struct mt7530_priv *priv) priv->ds->ops = &mt7530_switch_ops; priv->ds->phylink_mac_ops = &mt753x_phylink_mac_ops; mutex_init(&priv->reg_mutex); + spin_lock_init(&priv->stats_lock); + INIT_DELAYED_WORK(&priv->stats_work, mt7530_stats_poll); + dev_set_drvdata(dev, priv); return 0; diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 3e0090bed298..dd33b0df3419 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -796,6 +796,7 @@ struct mt7530_fdb { * @pvid: The VLAN specified is to be considered a PVID at ingress. Any * untagged frames will be assigned to the related VLAN. * @sgmii_pcs: Pointer to PCS instance for SerDes ports + * @stats: Cached port statistics for MDIO-connected switches */ struct mt7530_port { bool enable; @@ -803,6 +804,7 @@ struct mt7530_port { u32 pm; u16 pvid; struct phylink_pcs *sgmii_pcs; + struct rtnl_link_stats64 stats; }; /* Port 5 mode definitions of the MT7530 switch */ @@ -875,6 +877,9 @@ struct mt753x_info { * @create_sgmii: Pointer to function creating SGMII PCS instance(s) * @active_cpu_ports: Holding the active CPU ports * @mdiodev: The pointer to the MDIO device structure + * @stats_lock: Protects cached per-port stats from concurrent access + * @stats_work: Delayed work for polling MIB counters on MDIO switches + * @stats_last: Jiffies timestamp of last MIB counter poll */ struct mt7530_priv { struct device *dev; @@ -900,6 +905,9 @@ struct mt7530_priv { int (*create_sgmii)(struct mt7530_priv *priv); u8 active_cpu_ports; struct mdio_device *mdiodev; + spinlock_t stats_lock; /* protects cached stats counters */ + struct delayed_work stats_work; + unsigned long stats_last; }; struct mt7530_hw_vlan_entry { diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index 31fa94dac627..c35cef01ec26 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -216,7 +216,7 @@ (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \ 0x0) #define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \ - (0xF << (((_extint) % 2))) + (0xF << (((_extint) % 2) * 4)) #define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(_extint) \ (((_extint) % 2) * 4) diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index d6bdad4baadd..f8a4eb365c3d 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -47,7 +47,9 @@ static int numdummies = 1; /* fake multicast ability */ -static void set_multicast_list(struct net_device *dev) +static void set_multicast_list(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { } @@ -87,7 +89,7 @@ static const struct net_device_ops dummy_netdev_ops = { .ndo_init = dummy_dev_init, .ndo_start_xmit = dummy_xmit, .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = set_multicast_list, + .ndo_set_rx_mode_async = set_multicast_list, .ndo_set_mac_address = eth_mac_addr, .ndo_get_stats64 = dummy_get_stats64, .ndo_change_carrier = dummy_change_carrier, diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c deleted file mode 100644 index fb68339e1511..000000000000 --- a/drivers/net/ethernet/3com/3c509.c +++ /dev/null @@ -1,1448 +0,0 @@ -/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */ -/* - Written 1993-2000 by Donald Becker. - - Copyright 1994-2000 by Donald Becker. - Copyright 1993 United States Government as represented by the - Director, National Security Agency. This software may be used and - distributed according to the terms of the GNU General Public License, - incorporated herein by reference. - - This driver is for the 3Com EtherLinkIII series. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - Known limitations: - Because of the way 3c509 ISA detection works it's difficult to predict - a priori which of several ISA-mode cards will be detected first. - - This driver does not use predictive interrupt mode, resulting in higher - packet latency but lower overhead. If interrupts are disabled for an - unusually long time it could also result in missed packets, but in - practice this rarely happens. - - - FIXES: - Alan Cox: Removed the 'Unexpected interrupt' bug. - Michael Meskes: Upgraded to Donald Becker's version 1.07. - Alan Cox: Increased the eeprom delay. Regardless of - what the docs say some people definitely - get problems with lower (but in card spec) - delays - v1.10 4/21/97 Fixed module code so that multiple cards may be detected, - other cleanups. -djb - Andrea Arcangeli: Upgraded to Donald Becker's version 1.12. - Rick Payne: Fixed SMP race condition - v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb - v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb - v1.15 1/31/98 Faster recovery for Tx errors. -djb - v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb - v1.18 12Mar2001 Andrew Morton - - Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz) - - Reviewed against 1.18 from scyld.com - v1.18a 17Nov2001 Jeff Garzik - - ethtool support - v1.18b 1Mar2002 Zwane Mwaikambo - - Power Management support - v1.18c 1Mar2002 David Ruggiero - - Full duplex support - v1.19 16Oct2002 Zwane Mwaikambo - - Additional ethtool features - v1.19a 28Oct2002 Davud Ruggiero - - Increase *read_eeprom udelay to workaround oops with 2 cards. - v1.19b 08Nov2002 Marc Zyngier - - Introduce driver model for EISA cards. - v1.20 04Feb2008 Ondrej Zary - - convert to isa_driver and pnp_driver and some cleanups -*/ - -#define DRV_NAME "3c509" - -/* A few values that may be tweaked. */ - -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (400*HZ/1000) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for udelay() */ -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifdef EL3_DEBUG -static int el3_debug = EL3_DEBUG; -#else -static int el3_debug = 2; -#endif - -/* Used to do a global count of all the cards in the system. Must be - * a global variable so that the eisa probe routines can increment - * it */ -static int el3_cards = 0; -#define EL3_MAX_CARDS 8 - -/* To minimize the size of the driver source I only define operating - constants if they are used several times. You'll need the manual - anyway if you want to understand driver details. */ -/* Offsets from base I/O address. */ -#define EL3_DATA 0x00 -#define EL3_CMD 0x0e -#define EL3_STATUS 0x0e -#define EEPROM_READ 0x80 - -#define EL3_IO_EXTENT 16 - -#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) - - -/* The top five bits written to EL3_CMD are a command, the lower - 11 bits are the parameter, if applicable. */ -enum c509cmd { - TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, - RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, - TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, - FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, - SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, - SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11, - StatsDisable = 22<<11, StopCoax = 23<<11, PowerUp = 27<<11, - PowerDown = 28<<11, PowerAuto = 29<<11}; - -enum c509status { - IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, - TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, - IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, }; - -/* The SetRxFilter command accepts the following classes: */ -enum RxFilter { - RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 }; - -/* Register window 1 offsets, the window used in normal operation. */ -#define TX_FIFO 0x00 -#define RX_FIFO 0x00 -#define RX_STATUS 0x08 -#define TX_STATUS 0x0B -#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */ - -#define WN0_CONF_CTRL 0x04 /* Window 0: Configuration control register */ -#define WN0_ADDR_CONF 0x06 /* Window 0: Address configuration register */ -#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */ -#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */ -#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ -#define WN4_NETDIAG 0x06 /* Window 4: Net diagnostic */ -#define FD_ENABLE 0x8000 /* Enable full-duplex ("external loopback") */ - -/* - * Must be a power of two (we use a binary and in the - * circular queue) - */ -#define SKB_QUEUE_SIZE 64 - -enum el3_cardtype { EL3_ISA, EL3_PNP, EL3_EISA }; - -struct el3_private { - spinlock_t lock; - /* skb send-queue */ - int head, size; - struct sk_buff *queue[SKB_QUEUE_SIZE]; - enum el3_cardtype type; -}; -static int id_port; -static int current_tag; -static struct net_device *el3_devs[EL3_MAX_CARDS]; - -/* Parameters that may be passed into the module. */ -static int debug = -1; -static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1}; -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 10; -#ifdef CONFIG_PNP -static int nopnp; -#endif - -static int el3_common_init(struct net_device *dev); -static void el3_common_remove(struct net_device *dev); -static ushort id_read_eeprom(int index); -static ushort read_eeprom(int ioaddr, int index); -static int el3_open(struct net_device *dev); -static netdev_tx_t el3_start_xmit(struct sk_buff *skb, struct net_device *dev); -static irqreturn_t el3_interrupt(int irq, void *dev_id); -static void update_stats(struct net_device *dev); -static struct net_device_stats *el3_get_stats(struct net_device *dev); -static int el3_rx(struct net_device *dev); -static int el3_close(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static void el3_tx_timeout (struct net_device *dev, unsigned int txqueue); -static void el3_down(struct net_device *dev); -static void el3_up(struct net_device *dev); -static const struct ethtool_ops ethtool_ops; -#ifdef CONFIG_PM -static int el3_suspend(struct device *, pm_message_t); -static int el3_resume(struct device *); -#else -#define el3_suspend NULL -#define el3_resume NULL -#endif - - -/* generic device remove for all device types */ -static int el3_device_remove (struct device *device); -#ifdef CONFIG_NET_POLL_CONTROLLER -static void el3_poll_controller(struct net_device *dev); -#endif - -/* Return 0 on success, 1 on error, 2 when found already detected PnP card */ -static int el3_isa_id_sequence(__be16 *phys_addr) -{ - short lrs_state = 0xff; - int i; - - /* ISA boards are detected by sending the ID sequence to the - ID_PORT. We find cards past the first by setting the 'current_tag' - on cards as they are found. Cards with their tag set will not - respond to subsequent ID sequences. */ - - outb(0x00, id_port); - outb(0x00, id_port); - for (i = 0; i < 255; i++) { - outb(lrs_state, id_port); - lrs_state <<= 1; - lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state; - } - /* For the first probe, clear all board's tag registers. */ - if (current_tag == 0) - outb(0xd0, id_port); - else /* Otherwise kill off already-found boards. */ - outb(0xd8, id_port); - if (id_read_eeprom(7) != 0x6d50) - return 1; - /* Read in EEPROM data, which does contention-select. - Only the lowest address board will stay "on-line". - 3Com got the byte order backwards. */ - for (i = 0; i < 3; i++) - phys_addr[i] = htons(id_read_eeprom(i)); -#ifdef CONFIG_PNP - if (!nopnp) { - /* The ISA PnP 3c509 cards respond to the ID sequence too. - This check is needed in order not to register them twice. */ - for (i = 0; i < el3_cards; i++) { - struct el3_private *lp = netdev_priv(el3_devs[i]); - if (lp->type == EL3_PNP && - ether_addr_equal((u8 *)phys_addr, el3_devs[i]->dev_addr)) { - if (el3_debug > 3) - pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n", - phys_addr[0] & 0xff, phys_addr[0] >> 8, - phys_addr[1] & 0xff, phys_addr[1] >> 8, - phys_addr[2] & 0xff, phys_addr[2] >> 8); - /* Set the adaptor tag so that the next card can be found. */ - outb(0xd0 + ++current_tag, id_port); - return 2; - } - } - } -#endif /* CONFIG_PNP */ - return 0; - -} - -static void el3_dev_fill(struct net_device *dev, __be16 *phys_addr, int ioaddr, - int irq, int if_port, enum el3_cardtype type) -{ - struct el3_private *lp = netdev_priv(dev); - - eth_hw_addr_set(dev, (u8 *)phys_addr); - dev->base_addr = ioaddr; - dev->irq = irq; - dev->if_port = if_port; - lp->type = type; -} - -static int el3_isa_match(struct device *pdev, unsigned int ndev) -{ - struct net_device *dev; - int ioaddr, isa_irq, if_port, err; - unsigned int iobase; - __be16 phys_addr[3]; - - while ((err = el3_isa_id_sequence(phys_addr)) == 2) - ; /* Skip to next card when PnP card found */ - if (err == 1) - return 0; - - iobase = id_read_eeprom(8); - if_port = iobase >> 14; - ioaddr = 0x200 + ((iobase & 0x1f) << 4); - if (irq[el3_cards] > 1 && irq[el3_cards] < 16) - isa_irq = irq[el3_cards]; - else - isa_irq = id_read_eeprom(9) >> 12; - - dev = alloc_etherdev(sizeof(struct el3_private)); - if (!dev) - return -ENOMEM; - - SET_NETDEV_DEV(dev, pdev); - - if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-isa")) { - free_netdev(dev); - return 0; - } - - /* Set the adaptor tag so that the next card can be found. */ - outb(0xd0 + ++current_tag, id_port); - - /* Activate the adaptor at the EEPROM location. */ - outb((ioaddr >> 4) | 0xe0, id_port); - - EL3WINDOW(0); - if (inw(ioaddr) != 0x6d50) { - free_netdev(dev); - return 0; - } - - /* Free the interrupt so that some other card can use it. */ - outw(0x0f00, ioaddr + WN0_IRQ); - - el3_dev_fill(dev, phys_addr, ioaddr, isa_irq, if_port, EL3_ISA); - dev_set_drvdata(pdev, dev); - if (el3_common_init(dev)) { - free_netdev(dev); - return 0; - } - - el3_devs[el3_cards++] = dev; - return 1; -} - -static void el3_isa_remove(struct device *pdev, - unsigned int ndev) -{ - el3_device_remove(pdev); - dev_set_drvdata(pdev, NULL); -} - -#ifdef CONFIG_PM -static int el3_isa_suspend(struct device *dev, unsigned int n, - pm_message_t state) -{ - current_tag = 0; - return el3_suspend(dev, state); -} - -static int el3_isa_resume(struct device *dev, unsigned int n) -{ - struct net_device *ndev = dev_get_drvdata(dev); - int ioaddr = ndev->base_addr, err; - __be16 phys_addr[3]; - - while ((err = el3_isa_id_sequence(phys_addr)) == 2) - ; /* Skip to next card when PnP card found */ - if (err == 1) - return 0; - /* Set the adaptor tag so that the next card can be found. */ - outb(0xd0 + ++current_tag, id_port); - /* Enable the card */ - outb((ioaddr >> 4) | 0xe0, id_port); - EL3WINDOW(0); - if (inw(ioaddr) != 0x6d50) - return 1; - /* Free the interrupt so that some other card can use it. */ - outw(0x0f00, ioaddr + WN0_IRQ); - return el3_resume(dev); -} -#endif - -static struct isa_driver el3_isa_driver = { - .match = el3_isa_match, - .remove = el3_isa_remove, -#ifdef CONFIG_PM - .suspend = el3_isa_suspend, - .resume = el3_isa_resume, -#endif - .driver = { - .name = "3c509" - }, -}; -static int isa_registered; - -#ifdef CONFIG_PNP -static const struct pnp_device_id el3_pnp_ids[] = { - { .id = "TCM5090" }, /* 3Com Etherlink III (TP) */ - { .id = "TCM5091" }, /* 3Com Etherlink III */ - { .id = "TCM5094" }, /* 3Com Etherlink III (combo) */ - { .id = "TCM5095" }, /* 3Com Etherlink III (TPO) */ - { .id = "TCM5098" }, /* 3Com Etherlink III (TPC) */ - { .id = "PNP80f7" }, /* 3Com Etherlink III compatible */ - { .id = "PNP80f8" }, /* 3Com Etherlink III compatible */ - { .id = "" } -}; -MODULE_DEVICE_TABLE(pnp, el3_pnp_ids); - -static int el3_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id) -{ - short i; - int ioaddr, irq, if_port; - __be16 phys_addr[3]; - struct net_device *dev = NULL; - int err; - - ioaddr = pnp_port_start(pdev, 0); - if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-pnp")) - return -EBUSY; - irq = pnp_irq(pdev, 0); - EL3WINDOW(0); - for (i = 0; i < 3; i++) - phys_addr[i] = htons(read_eeprom(ioaddr, i)); - if_port = read_eeprom(ioaddr, 8) >> 14; - dev = alloc_etherdev(sizeof(struct el3_private)); - if (!dev) { - release_region(ioaddr, EL3_IO_EXTENT); - return -ENOMEM; - } - SET_NETDEV_DEV(dev, &pdev->dev); - - el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_PNP); - pnp_set_drvdata(pdev, dev); - err = el3_common_init(dev); - - if (err) { - pnp_set_drvdata(pdev, NULL); - free_netdev(dev); - return err; - } - - el3_devs[el3_cards++] = dev; - return 0; -} - -static void el3_pnp_remove(struct pnp_dev *pdev) -{ - el3_common_remove(pnp_get_drvdata(pdev)); - pnp_set_drvdata(pdev, NULL); -} - -#ifdef CONFIG_PM -static int el3_pnp_suspend(struct pnp_dev *pdev, pm_message_t state) -{ - return el3_suspend(&pdev->dev, state); -} - -static int el3_pnp_resume(struct pnp_dev *pdev) -{ - return el3_resume(&pdev->dev); -} -#endif - -static struct pnp_driver el3_pnp_driver = { - .name = "3c509", - .id_table = el3_pnp_ids, - .probe = el3_pnp_probe, - .remove = el3_pnp_remove, -#ifdef CONFIG_PM - .suspend = el3_pnp_suspend, - .resume = el3_pnp_resume, -#endif -}; -static int pnp_registered; -#endif /* CONFIG_PNP */ - -#ifdef CONFIG_EISA -static const struct eisa_device_id el3_eisa_ids[] = { - { "TCM5090" }, - { "TCM5091" }, - { "TCM5092" }, - { "TCM5093" }, - { "TCM5094" }, - { "TCM5095" }, - { "TCM5098" }, - { "" } -}; -MODULE_DEVICE_TABLE(eisa, el3_eisa_ids); - -static int el3_eisa_probe (struct device *device); - -static struct eisa_driver el3_eisa_driver = { - .id_table = el3_eisa_ids, - .driver = { - .name = "3c579", - .probe = el3_eisa_probe, - .remove = el3_device_remove, - .suspend = el3_suspend, - .resume = el3_resume, - } -}; -static int eisa_registered; -#endif - -static const struct net_device_ops netdev_ops = { - .ndo_open = el3_open, - .ndo_stop = el3_close, - .ndo_start_xmit = el3_start_xmit, - .ndo_get_stats = el3_get_stats, - .ndo_set_rx_mode = set_multicast_list, - .ndo_tx_timeout = el3_tx_timeout, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = el3_poll_controller, -#endif -}; - -static int el3_common_init(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - int err; - static const char * const if_names[] = { - "10baseT", "AUI", "undefined", "BNC" - }; - - spin_lock_init(&lp->lock); - - if (dev->mem_start & 0x05) { /* xcvr codes 1/3/4/12 */ - dev->if_port = (dev->mem_start & 0x0f); - } else { /* xcvr codes 0/8 */ - /* use eeprom value, but save user's full-duplex selection */ - dev->if_port |= (dev->mem_start & 0x08); - } - - /* The EL3-specific entries in the device structure. */ - dev->netdev_ops = &netdev_ops; - dev->watchdog_timeo = TX_TIMEOUT; - dev->ethtool_ops = ðtool_ops; - - err = register_netdev(dev); - if (err) { - pr_err("Failed to register 3c5x9 at %#3.3lx, IRQ %d.\n", - dev->base_addr, dev->irq); - release_region(dev->base_addr, EL3_IO_EXTENT); - return err; - } - - pr_info("%s: 3c5x9 found at %#3.3lx, %s port, address %pM, IRQ %d.\n", - dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)], - dev->dev_addr, dev->irq); - - return 0; - -} - -static void el3_common_remove (struct net_device *dev) -{ - unregister_netdev (dev); - release_region(dev->base_addr, EL3_IO_EXTENT); - free_netdev (dev); -} - -#ifdef CONFIG_EISA -static int el3_eisa_probe(struct device *device) -{ - short i; - int ioaddr, irq, if_port; - __be16 phys_addr[3]; - struct net_device *dev = NULL; - struct eisa_device *edev; - int err; - - /* Yeepee, The driver framework is calling us ! */ - edev = to_eisa_device (device); - ioaddr = edev->base_addr; - - if (!request_region(ioaddr, EL3_IO_EXTENT, "3c579-eisa")) - return -EBUSY; - - /* Change the register set to the configuration window 0. */ - outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD); - - irq = inw(ioaddr + WN0_IRQ) >> 12; - if_port = inw(ioaddr + 6)>>14; - for (i = 0; i < 3; i++) - phys_addr[i] = htons(read_eeprom(ioaddr, i)); - - /* Restore the "Product ID" to the EEPROM read register. */ - read_eeprom(ioaddr, 3); - - dev = alloc_etherdev(sizeof (struct el3_private)); - if (dev == NULL) { - release_region(ioaddr, EL3_IO_EXTENT); - return -ENOMEM; - } - - SET_NETDEV_DEV(dev, device); - - el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_EISA); - eisa_set_drvdata (edev, dev); - err = el3_common_init(dev); - - if (err) { - eisa_set_drvdata (edev, NULL); - free_netdev(dev); - return err; - } - - el3_devs[el3_cards++] = dev; - return 0; -} -#endif - -/* This remove works for all device types. - * - * The net dev must be stored in the driver data field */ -static int el3_device_remove(struct device *device) -{ - struct net_device *dev; - - dev = dev_get_drvdata(device); - - el3_common_remove (dev); - return 0; -} - -/* Read a word from the EEPROM using the regular EEPROM access register. - Assume that we are in register window zero. - */ -static ushort read_eeprom(int ioaddr, int index) -{ - outw(EEPROM_READ + index, ioaddr + 10); - /* Pause for at least 162 us. for the read to take place. - Some chips seem to require much longer */ - mdelay(2); - return inw(ioaddr + 12); -} - -/* Read a word from the EEPROM when in the ISA ID probe state. */ -static ushort id_read_eeprom(int index) -{ - int bit, word = 0; - - /* Issue read command, and pause for at least 162 us. for it to complete. - Assume extra-fast 16Mhz bus. */ - outb(EEPROM_READ + index, id_port); - - /* Pause for at least 162 us. for the read to take place. */ - /* Some chips seem to require much longer */ - mdelay(4); - - for (bit = 15; bit >= 0; bit--) - word = (word << 1) + (inb(id_port) & 0x01); - - if (el3_debug > 3) - pr_debug(" 3c509 EEPROM word %d %#4.4x.\n", index, word); - - return word; -} - - -static int -el3_open(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - int i; - - outw(TxReset, ioaddr + EL3_CMD); - outw(RxReset, ioaddr + EL3_CMD); - outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); - - i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev); - if (i) - return i; - - EL3WINDOW(0); - if (el3_debug > 3) - pr_debug("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name, - dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS)); - - el3_up(dev); - - if (el3_debug > 3) - pr_debug("%s: Opened 3c509 IRQ %d status %4.4x.\n", - dev->name, dev->irq, inw(ioaddr + EL3_STATUS)); - - return 0; -} - -static void -el3_tx_timeout (struct net_device *dev, unsigned int txqueue) -{ - int ioaddr = dev->base_addr; - - /* Transmitter timeout, serious problems. */ - pr_warn("%s: transmit timed out, Tx_status %2.2x status %4.4x Tx FIFO room %d\n", - dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS), - inw(ioaddr + TX_FREE)); - dev->stats.tx_errors++; - netif_trans_update(dev); /* prevent tx timeout */ - /* Issue TX_RESET and TX_START commands. */ - outw(TxReset, ioaddr + EL3_CMD); - outw(TxEnable, ioaddr + EL3_CMD); - netif_wake_queue(dev); -} - - -static netdev_tx_t -el3_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - int ioaddr = dev->base_addr; - unsigned long flags; - - netif_stop_queue (dev); - - dev->stats.tx_bytes += skb->len; - - if (el3_debug > 4) { - pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n", - dev->name, skb->len, inw(ioaddr + EL3_STATUS)); - } - /* - * We lock the driver against other processors. Note - * we don't need to lock versus the IRQ as we suspended - * that. This means that we lose the ability to take - * an RX during a TX upload. That sucks a bit with SMP - * on an original 3c509 (2K buffer) - * - * Using disable_irq stops us crapping on other - * time sensitive devices. - */ - - spin_lock_irqsave(&lp->lock, flags); - - /* Put out the doubleword header... */ - outw(skb->len, ioaddr + TX_FIFO); - outw(0x00, ioaddr + TX_FIFO); - /* ... and the packet rounded to a doubleword. */ - outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); - - if (inw(ioaddr + TX_FREE) > 1536) - netif_start_queue(dev); - else - /* Interrupt us when the FIFO has room for max-sized packet. */ - outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); - - spin_unlock_irqrestore(&lp->lock, flags); - - dev_consume_skb_any (skb); - - /* Clear the Tx status stack. */ - { - short tx_status; - int i = 4; - - while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { - if (tx_status & 0x38) dev->stats.tx_aborted_errors++; - if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); - if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); - outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ - } - } - return NETDEV_TX_OK; -} - -/* The EL3 interrupt handler. */ -static irqreturn_t -el3_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct el3_private *lp; - int ioaddr, status; - int i = max_interrupt_work; - - lp = netdev_priv(dev); - spin_lock(&lp->lock); - - ioaddr = dev->base_addr; - - if (el3_debug > 4) { - status = inw(ioaddr + EL3_STATUS); - pr_debug("%s: interrupt, status %4.4x.\n", dev->name, status); - } - - while ((status = inw(ioaddr + EL3_STATUS)) & - (IntLatch | RxComplete | StatsFull)) { - - if (status & RxComplete) - el3_rx(dev); - - if (status & TxAvailable) { - if (el3_debug > 5) - pr_debug(" TX room bit was handled.\n"); - /* There's room in the FIFO for a full-sized packet. */ - outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); - netif_wake_queue (dev); - } - if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) { - /* Handle all uncommon interrupts. */ - if (status & StatsFull) /* Empty statistics. */ - update_stats(dev); - if (status & RxEarly) { /* Rx early is unused. */ - el3_rx(dev); - outw(AckIntr | RxEarly, ioaddr + EL3_CMD); - } - if (status & TxComplete) { /* Really Tx error. */ - short tx_status; - int i = 4; - - while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { - if (tx_status & 0x38) dev->stats.tx_aborted_errors++; - if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); - if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); - outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ - } - } - if (status & AdapterFailure) { - /* Adapter failure requires Rx reset and reinit. */ - outw(RxReset, ioaddr + EL3_CMD); - /* Set the Rx filter to the current state. */ - outw(SetRxFilter | RxStation | RxBroadcast - | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0) - | (dev->flags & IFF_PROMISC ? RxProm : 0), - ioaddr + EL3_CMD); - outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ - outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); - } - } - - if (--i < 0) { - pr_err("%s: Infinite loop in interrupt, status %4.4x.\n", - dev->name, status); - /* Clear all interrupts. */ - outw(AckIntr | 0xFF, ioaddr + EL3_CMD); - break; - } - /* Acknowledge the IRQ. */ - outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */ - } - - if (el3_debug > 4) { - pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, - inw(ioaddr + EL3_STATUS)); - } - spin_unlock(&lp->lock); - return IRQ_HANDLED; -} - - -#ifdef CONFIG_NET_POLL_CONTROLLER -/* - * Polling receive - used by netconsole and other diagnostic tools - * to allow network i/o with interrupts disabled. - */ -static void el3_poll_controller(struct net_device *dev) -{ - disable_irq(dev->irq); - el3_interrupt(dev->irq, dev); - enable_irq(dev->irq); -} -#endif - -static struct net_device_stats * -el3_get_stats(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - unsigned long flags; - - /* - * This is fast enough not to bother with disable IRQ - * stuff. - */ - - spin_lock_irqsave(&lp->lock, flags); - update_stats(dev); - spin_unlock_irqrestore(&lp->lock, flags); - return &dev->stats; -} - -/* Update statistics. We change to register window 6, so this should be run - single-threaded if the device is active. This is expected to be a rare - operation, and it's simpler for the rest of the driver to assume that - window 1 is always valid rather than use a special window-state variable. - */ -static void update_stats(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - - if (el3_debug > 5) - pr_debug(" Updating the statistics.\n"); - /* Turn off statistics updates while reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - /* Switch to the stats window, and read everything. */ - EL3WINDOW(6); - dev->stats.tx_carrier_errors += inb(ioaddr + 0); - dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); - /* Multiple collisions. */ inb(ioaddr + 2); - dev->stats.collisions += inb(ioaddr + 3); - dev->stats.tx_window_errors += inb(ioaddr + 4); - dev->stats.rx_fifo_errors += inb(ioaddr + 5); - dev->stats.tx_packets += inb(ioaddr + 6); - /* Rx packets */ inb(ioaddr + 7); - /* Tx deferrals */ inb(ioaddr + 8); - inw(ioaddr + 10); /* Total Rx and Tx octets. */ - inw(ioaddr + 12); - - /* Back to window 1, and turn statistics back on. */ - EL3WINDOW(1); - outw(StatsEnable, ioaddr + EL3_CMD); -} - -static int -el3_rx(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - short rx_status; - - if (el3_debug > 5) - pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", - inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); - while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) { - if (rx_status & 0x4000) { /* Error, update stats. */ - short error = rx_status & 0x3800; - - outw(RxDiscard, ioaddr + EL3_CMD); - dev->stats.rx_errors++; - switch (error) { - case 0x0000: dev->stats.rx_over_errors++; break; - case 0x0800: dev->stats.rx_length_errors++; break; - case 0x1000: dev->stats.rx_frame_errors++; break; - case 0x1800: dev->stats.rx_length_errors++; break; - case 0x2000: dev->stats.rx_frame_errors++; break; - case 0x2800: dev->stats.rx_crc_errors++; break; - } - } else { - short pkt_len = rx_status & 0x7ff; - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, pkt_len + 5); - if (el3_debug > 4) - pr_debug("Receiving packet size %d status %4.4x.\n", - pkt_len, rx_status); - if (skb != NULL) { - skb_reserve(skb, 2); /* Align IP on 16 byte */ - - /* 'skb->data' points to the start of sk_buff data area. */ - insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len), - (pkt_len + 3) >> 2); - - outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ - skb->protocol = eth_type_trans(skb,dev); - netif_rx(skb); - dev->stats.rx_bytes += pkt_len; - dev->stats.rx_packets++; - continue; - } - outw(RxDiscard, ioaddr + EL3_CMD); - dev->stats.rx_dropped++; - if (el3_debug) - pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", - dev->name, pkt_len); - } - inw(ioaddr + EL3_STATUS); /* Delay. */ - while (inw(ioaddr + EL3_STATUS) & 0x1000) - pr_debug(" Waiting for 3c509 to discard packet, status %x.\n", - inw(ioaddr + EL3_STATUS) ); - } - - return 0; -} - -/* - * Set or clear the multicast filter for this adaptor. - */ -static void -set_multicast_list(struct net_device *dev) -{ - unsigned long flags; - struct el3_private *lp = netdev_priv(dev); - int ioaddr = dev->base_addr; - int mc_count = netdev_mc_count(dev); - - if (el3_debug > 1) { - static int old; - if (old != mc_count) { - old = mc_count; - pr_debug("%s: Setting Rx mode to %d addresses.\n", - dev->name, mc_count); - } - } - spin_lock_irqsave(&lp->lock, flags); - if (dev->flags&IFF_PROMISC) { - outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, - ioaddr + EL3_CMD); - } - else if (mc_count || (dev->flags&IFF_ALLMULTI)) { - outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD); - } - else - outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); - spin_unlock_irqrestore(&lp->lock, flags); -} - -static int -el3_close(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - struct el3_private *lp = netdev_priv(dev); - - if (el3_debug > 2) - pr_debug("%s: Shutting down ethercard.\n", dev->name); - - el3_down(dev); - - free_irq(dev->irq, dev); - /* Switching back to window 0 disables the IRQ. */ - EL3WINDOW(0); - if (lp->type != EL3_EISA) { - /* But we explicitly zero the IRQ line select anyway. Don't do - * it on EISA cards, it prevents the module from getting an - * IRQ after unload+reload... */ - outw(0x0f00, ioaddr + WN0_IRQ); - } - - return 0; -} - -static int -el3_link_ok(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - u16 tmp; - - EL3WINDOW(4); - tmp = inw(ioaddr + WN4_MEDIA); - EL3WINDOW(1); - return tmp & (1<<11); -} - -static void -el3_netdev_get_ecmd(struct net_device *dev, struct ethtool_link_ksettings *cmd) -{ - u16 tmp; - int ioaddr = dev->base_addr; - u32 supported; - - EL3WINDOW(0); - /* obtain current transceiver via WN4_MEDIA? */ - tmp = inw(ioaddr + WN0_ADDR_CONF); - switch (tmp >> 14) { - case 0: - cmd->base.port = PORT_TP; - break; - case 1: - cmd->base.port = PORT_AUI; - break; - case 3: - cmd->base.port = PORT_BNC; - break; - default: - break; - } - - cmd->base.duplex = DUPLEX_HALF; - supported = 0; - tmp = inw(ioaddr + WN0_CONF_CTRL); - if (tmp & (1<<13)) - supported |= SUPPORTED_AUI; - if (tmp & (1<<12)) - supported |= SUPPORTED_BNC; - if (tmp & (1<<9)) { - supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full; /* hmm... */ - EL3WINDOW(4); - tmp = inw(ioaddr + WN4_NETDIAG); - if (tmp & FD_ENABLE) - cmd->base.duplex = DUPLEX_FULL; - } - - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, - supported); - cmd->base.speed = SPEED_10; - EL3WINDOW(1); -} - -static int -el3_netdev_set_ecmd(struct net_device *dev, - const struct ethtool_link_ksettings *cmd) -{ - u16 tmp; - int ioaddr = dev->base_addr; - - if (cmd->base.speed != SPEED_10) - return -EINVAL; - if ((cmd->base.duplex != DUPLEX_HALF) && - (cmd->base.duplex != DUPLEX_FULL)) - return -EINVAL; - - /* change XCVR type */ - EL3WINDOW(0); - tmp = inw(ioaddr + WN0_ADDR_CONF); - switch (cmd->base.port) { - case PORT_TP: - tmp &= ~(3<<14); - dev->if_port = 0; - break; - case PORT_AUI: - tmp |= (1<<14); - dev->if_port = 1; - break; - case PORT_BNC: - tmp |= (3<<14); - dev->if_port = 3; - break; - default: - return -EINVAL; - } - - outw(tmp, ioaddr + WN0_ADDR_CONF); - if (dev->if_port == 3) { - /* fire up the DC-DC convertor if BNC gets enabled */ - tmp = inw(ioaddr + WN0_ADDR_CONF); - if (tmp & (3 << 14)) { - outw(StartCoax, ioaddr + EL3_CMD); - udelay(800); - } else - return -EIO; - } - - EL3WINDOW(4); - tmp = inw(ioaddr + WN4_NETDIAG); - if (cmd->base.duplex == DUPLEX_FULL) - tmp |= FD_ENABLE; - else - tmp &= ~FD_ENABLE; - outw(tmp, ioaddr + WN4_NETDIAG); - EL3WINDOW(1); - - return 0; -} - -static void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); -} - -static int el3_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings *cmd) -{ - struct el3_private *lp = netdev_priv(dev); - - spin_lock_irq(&lp->lock); - el3_netdev_get_ecmd(dev, cmd); - spin_unlock_irq(&lp->lock); - return 0; -} - -static int el3_set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings *cmd) -{ - struct el3_private *lp = netdev_priv(dev); - int ret; - - spin_lock_irq(&lp->lock); - ret = el3_netdev_set_ecmd(dev, cmd); - spin_unlock_irq(&lp->lock); - return ret; -} - -static u32 el3_get_link(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - u32 ret; - - spin_lock_irq(&lp->lock); - ret = el3_link_ok(dev); - spin_unlock_irq(&lp->lock); - return ret; -} - -static u32 el3_get_msglevel(struct net_device *dev) -{ - return el3_debug; -} - -static void el3_set_msglevel(struct net_device *dev, u32 v) -{ - el3_debug = v; -} - -static const struct ethtool_ops ethtool_ops = { - .get_drvinfo = el3_get_drvinfo, - .get_link = el3_get_link, - .get_msglevel = el3_get_msglevel, - .set_msglevel = el3_set_msglevel, - .get_link_ksettings = el3_get_link_ksettings, - .set_link_ksettings = el3_set_link_ksettings, -}; - -static void -el3_down(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - - netif_stop_queue(dev); - - /* Turn off statistics ASAP. We update lp->stats below. */ - outw(StatsDisable, ioaddr + EL3_CMD); - - /* Disable the receiver and transmitter. */ - outw(RxDisable, ioaddr + EL3_CMD); - outw(TxDisable, ioaddr + EL3_CMD); - - if (dev->if_port == 3) - /* Turn off thinnet power. Green! */ - outw(StopCoax, ioaddr + EL3_CMD); - else if (dev->if_port == 0) { - /* Disable link beat and jabber, if_port may change here next open(). */ - EL3WINDOW(4); - outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA); - } - - outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); - - update_stats(dev); -} - -static void -el3_up(struct net_device *dev) -{ - int i, sw_info, net_diag; - int ioaddr = dev->base_addr; - - /* Activating the board required and does no harm otherwise */ - outw(0x0001, ioaddr + 4); - - /* Set the IRQ line. */ - outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ); - - /* Set the station address in window 2 each time opened. */ - EL3WINDOW(2); - - for (i = 0; i < 6; i++) - outb(dev->dev_addr[i], ioaddr + i); - - if ((dev->if_port & 0x03) == 3) /* BNC interface */ - /* Start the thinnet transceiver. We should really wait 50ms...*/ - outw(StartCoax, ioaddr + EL3_CMD); - else if ((dev->if_port & 0x03) == 0) { /* 10baseT interface */ - /* Combine secondary sw_info word (the adapter level) and primary - sw_info word (duplex setting plus other useless bits) */ - EL3WINDOW(0); - sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) | - (read_eeprom(ioaddr, 0x0d) & 0xBff0); - - EL3WINDOW(4); - net_diag = inw(ioaddr + WN4_NETDIAG); - net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */ - pr_info("%s: ", dev->name); - switch (dev->if_port & 0x0c) { - case 12: - /* force full-duplex mode if 3c5x9b */ - if (sw_info & 0x000f) { - pr_cont("Forcing 3c5x9b full-duplex mode"); - break; - } - fallthrough; - case 8: - /* set full-duplex mode based on eeprom config setting */ - if ((sw_info & 0x000f) && (sw_info & 0x8000)) { - pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)"); - break; - } - fallthrough; - default: - /* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */ - pr_cont("Setting 3c5x9/3c5x9B half-duplex mode"); - net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */ - } - - outw(net_diag, ioaddr + WN4_NETDIAG); - pr_cont(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info); - if (el3_debug > 3) - pr_debug("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag); - /* Enable link beat and jabber check. */ - outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA); - } - - /* Switch to the stats window, and clear all stats by reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - EL3WINDOW(6); - for (i = 0; i < 9; i++) - inb(ioaddr + i); - inw(ioaddr + 10); - inw(ioaddr + 12); - - /* Switch to register set 1 for normal use. */ - EL3WINDOW(1); - - /* Accept b-case and phys addr only. */ - outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); - outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ - - outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ - outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ - /* Allow status bits to be seen. */ - outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); - /* Ack all pending events, and set active indicator mask. */ - outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, - ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull, - ioaddr + EL3_CMD); - - netif_start_queue(dev); -} - -/* Power Management support functions */ -#ifdef CONFIG_PM - -static int -el3_suspend(struct device *pdev, pm_message_t state) -{ - unsigned long flags; - struct net_device *dev; - struct el3_private *lp; - int ioaddr; - - dev = dev_get_drvdata(pdev); - lp = netdev_priv(dev); - ioaddr = dev->base_addr; - - spin_lock_irqsave(&lp->lock, flags); - - if (netif_running(dev)) - netif_device_detach(dev); - - el3_down(dev); - outw(PowerDown, ioaddr + EL3_CMD); - - spin_unlock_irqrestore(&lp->lock, flags); - return 0; -} - -static int -el3_resume(struct device *pdev) -{ - unsigned long flags; - struct net_device *dev; - struct el3_private *lp; - int ioaddr; - - dev = dev_get_drvdata(pdev); - lp = netdev_priv(dev); - ioaddr = dev->base_addr; - - spin_lock_irqsave(&lp->lock, flags); - - outw(PowerUp, ioaddr + EL3_CMD); - EL3WINDOW(0); - el3_up(dev); - - if (netif_running(dev)) - netif_device_attach(dev); - - spin_unlock_irqrestore(&lp->lock, flags); - return 0; -} - -#endif /* CONFIG_PM */ - -module_param(debug,int, 0); -module_param_hw_array(irq, int, irq, NULL, 0); -module_param(max_interrupt_work, int, 0); -MODULE_PARM_DESC(debug, "debug level (0-6)"); -MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); -MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt"); -#ifdef CONFIG_PNP -module_param(nopnp, int, 0); -MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)"); -#endif /* CONFIG_PNP */ -MODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B, 3c529, 3c579) ethernet driver"); -MODULE_LICENSE("GPL"); - -static int __init el3_init_module(void) -{ - int ret = 0; - - if (debug >= 0) - el3_debug = debug; - -#ifdef CONFIG_PNP - if (!nopnp) { - ret = pnp_register_driver(&el3_pnp_driver); - if (!ret) - pnp_registered = 1; - } -#endif - /* Select an open I/O location at 0x1*0 to do ISA contention select. */ - /* Start with 0x110 to avoid some sound cards.*/ - for (id_port = 0x110 ; id_port < 0x200; id_port += 0x10) { - if (!request_region(id_port, 1, "3c509-control")) - continue; - outb(0x00, id_port); - outb(0xff, id_port); - if (inb(id_port) & 0x01) - break; - else - release_region(id_port, 1); - } - if (id_port >= 0x200) { - id_port = 0; - pr_err("No I/O port available for 3c509 activation.\n"); - } else { - ret = isa_register_driver(&el3_isa_driver, EL3_MAX_CARDS); - if (!ret) - isa_registered = 1; - } -#ifdef CONFIG_EISA - ret = eisa_driver_register(&el3_eisa_driver); - if (!ret) - eisa_registered = 1; -#endif - -#ifdef CONFIG_PNP - if (pnp_registered) - ret = 0; -#endif - if (isa_registered) - ret = 0; -#ifdef CONFIG_EISA - if (eisa_registered) - ret = 0; -#endif - return ret; -} - -static void __exit el3_cleanup_module(void) -{ -#ifdef CONFIG_PNP - if (pnp_registered) - pnp_unregister_driver(&el3_pnp_driver); -#endif - if (isa_registered) - isa_unregister_driver(&el3_isa_driver); - if (id_port) - release_region(id_port, 1); -#ifdef CONFIG_EISA - if (eisa_registered) - eisa_driver_unregister(&el3_eisa_driver); -#endif -} - -module_init (el3_init_module); -module_exit (el3_cleanup_module); diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c deleted file mode 100644 index 2227c83a4862..000000000000 --- a/drivers/net/ethernet/3com/3c515.c +++ /dev/null @@ -1,1566 +0,0 @@ -/* - Written 1997-1998 by Donald Becker. - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - This driver is for the 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - - 2000/2/2- Added support for kernel-level ISAPnP - by Stephen Frost and Alessandro Zummo - Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox. - - 2001/11/17 - Added ethtool support (jgarzik) - - 2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk) - -*/ - -#define DRV_NAME "3c515" - -#define CORKSCREW 1 - -/* "Knobs" that adjust features and parameters. */ -/* Set the copy breakpoint for the copy-only-tiny-frames scheme. - Setting to > 1512 effectively disables this feature. */ -static int rx_copybreak = 200; - -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 20; - -/* Enable the automatic media selection code -- usually set. */ -#define AUTOMEDIA 1 - -/* Allow the use of fragment bus master transfers instead of only - programmed-I/O for Vortex cards. Full-bus-master transfers are always - enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined, - the feature may be turned on using 'options'. */ -#define VORTEX_BUS_MASTER - -/* A few values that may be tweaked. */ -/* Keep the ring sizes a power of two for efficiency. */ -#define TX_RING_SIZE 16 -#define RX_RING_SIZE 16 -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#define NEW_MULTICAST -#include - -#define MAX_UNITS 8 - -MODULE_AUTHOR("Donald Becker "); -MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver"); -MODULE_LICENSE("GPL"); - -/* "Knobs" for adjusting internal parameters. */ -/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */ -#define DRIVER_DEBUG 1 -/* Some values here only for performance evaluation and path-coverage - debugging. */ -static int rx_nocopy, rx_copy, queued_packet; - -/* Number of times to check to see if the Tx FIFO has space, used in some - limited cases. */ -#define WAIT_TX_AVAIL 200 - -/* Operational parameter that usually are not changed. */ -#define TX_TIMEOUT ((4*HZ)/10) /* Time in jiffies before concluding Tx hung */ - -/* The size here is somewhat misleading: the Corkscrew also uses the ISA - aliased registers at +0x400. - */ -#define CORKSCREW_TOTAL_SIZE 0x20 - -#ifdef DRIVER_DEBUG -static int corkscrew_debug = DRIVER_DEBUG; -#else -static int corkscrew_debug = 1; -#endif - -#define CORKSCREW_ID 10 - -/* - Theory of Operation - -I. Board Compatibility - -This device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL, -3Com's ISA bus adapter for Fast Ethernet. Due to the unique I/O port layout, -it's not practical to integrate this driver with the other EtherLink drivers. - -II. Board-specific settings - -The Corkscrew has an EEPROM for configuration, but no special settings are -needed for Linux. - -III. Driver operation - -The 3c515 series use an interface that's very similar to the 3c900 "Boomerang" -PCI cards, with the bus master interface extensively modified to work with -the ISA bus. - -The card is capable of full-bus-master transfers with separate -lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, -DEC Tulip and Intel Speedo3. - -This driver uses a "RX_COPYBREAK" scheme rather than a fixed intermediate -receive buffer. This scheme allocates full-sized skbuffs as receive -buffers. The value RX_COPYBREAK is used as the copying breakpoint: it is -chosen to trade-off the memory wasted by passing the full-sized skbuff to -the queue layer for all frames vs. the copying cost of copying a frame to a -correctly-sized skbuff. - - -IIIC. Synchronization -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the netif -layer. The other thread is the interrupt handler, which is single -threaded by the hardware and other software. - -IV. Notes - -Thanks to Terry Murphy of 3Com for providing documentation and a development -board. - -The names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com -project names. I use these names to eliminate confusion -- 3Com product -numbers and names are very similar and often confused. - -The new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes! -This driver only supports ethernet frames because of the recent MTU limit -of 1.5K, but the changes to support 4.5K are minimal. -*/ - -/* Operational definitions. - These are not used by other compilation units and thus are not - exported in a ".h" file. - - First the windows. There are eight register windows, with the command - and status registers available in each. - */ -#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) -#define EL3_CMD 0x0e -#define EL3_STATUS 0x0e - -/* The top five bits written to EL3_CMD are a command, the lower - 11 bits are the parameter, if applicable. - Note that 11 parameters bits was fine for ethernet, but the new chips - can handle FDDI length frames (~4500 octets) and now parameters count - 32-bit 'Dwords' rather than octets. */ - -enum corkscrew_cmd { - TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11, - RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11, - UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2, - DownUnstall = (6 << 11) + 3, RxDiscard = 8 << 11, TxEnable = 9 << 11, - TxDisable = 10 << 11, TxReset = 11 << 11, FakeIntr = 12 << 11, - AckIntr = 13 << 11, SetIntrEnb = 14 << 11, SetStatusEnb = 15 << 11, - SetRxFilter = 16 << 11, SetRxThreshold = 17 << 11, - SetTxThreshold = 18 << 11, SetTxStart = 19 << 11, StartDMAUp = 20 << 11, - StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11, - StatsDisable = 22 << 11, StopCoax = 23 << 11, -}; - -/* The SetRxFilter command accepts the following classes: */ -enum RxFilter { - RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 -}; - -/* Bits in the general status register. */ -enum corkscrew_status { - IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, - TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, - IntReq = 0x0040, StatsFull = 0x0080, - DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10, - DMAInProgress = 1 << 11, /* DMA controller is still busy. */ - CmdInProgress = 1 << 12, /* EL3_CMD is still busy. */ -}; - -/* Register window 1 offsets, the window used in normal operation. - On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */ -enum Window1 { - TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14, - RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B, - TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */ -}; -enum Window0 { - Wn0IRQ = 0x08, -#if defined(CORKSCREW) - Wn0EepromCmd = 0x200A, /* Corkscrew EEPROM command register. */ - Wn0EepromData = 0x200C, /* Corkscrew EEPROM results register. */ -#else - Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */ - Wn0EepromData = 12, /* Window 0: EEPROM results register. */ -#endif -}; -enum Win0_EEPROM_bits { - EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0, - EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */ - EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */ -}; - -/* EEPROM locations. */ -enum eeprom_offset { - PhysAddr01 = 0, PhysAddr23 = 1, PhysAddr45 = 2, ModelID = 3, - EtherLink3ID = 7, -}; - -enum Window3 { /* Window 3: MAC/config bits. */ - Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8, -}; -enum wn3_config { - Ram_size = 7, - Ram_width = 8, - Ram_speed = 0x30, - Rom_size = 0xc0, - Ram_split_shift = 16, - Ram_split = 3 << Ram_split_shift, - Xcvr_shift = 20, - Xcvr = 7 << Xcvr_shift, - Autoselect = 0x1000000, -}; - -enum Window4 { - Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */ -}; -enum Win4_Media_bits { - Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */ - Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */ - Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */ - Media_LnkBeat = 0x0800, -}; -enum Window7 { /* Window 7: Bus Master control. */ - Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12, -}; - -/* Boomerang-style bus master control registers. Note ISA aliases! */ -enum MasterCtrl { - PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen = - 0x40c, - TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418, -}; - -/* The Rx and Tx descriptor lists. - Caution Alpha hackers: these types are 32 bits! Note also the 8 byte - alignment contraint on tx_ring[] and rx_ring[]. */ -struct boom_rx_desc { - u32 next; - s32 status; - u32 addr; - s32 length; -}; - -/* Values for the Rx status entry. */ -enum rx_desc_status { - RxDComplete = 0x00008000, RxDError = 0x4000, - /* See boomerang_rx() for actual error bits */ -}; - -struct boom_tx_desc { - u32 next; - s32 status; - u32 addr; - s32 length; -}; - -struct corkscrew_private { - const char *product_name; - struct list_head list; - struct net_device *our_dev; - /* The Rx and Tx rings are here to keep them quad-word-aligned. */ - struct boom_rx_desc rx_ring[RX_RING_SIZE]; - struct boom_tx_desc tx_ring[TX_RING_SIZE]; - /* The addresses of transmit- and receive-in-place skbuffs. */ - struct sk_buff *rx_skbuff[RX_RING_SIZE]; - struct sk_buff *tx_skbuff[TX_RING_SIZE]; - unsigned int cur_rx, cur_tx; /* The next free ring entry */ - unsigned int dirty_rx, dirty_tx;/* The ring entries to be free()ed. */ - struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */ - struct timer_list timer; /* Media selection timer. */ - int capabilities ; /* Adapter capabilities word. */ - int options; /* User-settable misc. driver options. */ - int last_rx_packets; /* For media autoselection. */ - unsigned int available_media:8, /* From Wn3_Options */ - media_override:3, /* Passed-in media type. */ - default_media:3, /* Read from the EEPROM. */ - full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */ - full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */ - tx_full:1; - spinlock_t lock; - struct device *dev; -}; - -/* The action to take with a media selection timer tick. - Note that we deviate from the 3Com order by checking 10base2 before AUI. - */ -enum xcvr_types { - XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx, - XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8, -}; - -static struct media_table { - char *name; - unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */ - mask:8, /* The transceiver-present bit in Wn3_Config. */ - next:8; /* The media type to try next. */ - short wait; /* Time before we check media status. */ -} media_tbl[] = { - { "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10 }, - { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10}, - { "undefined", 0, 0x80, XCVR_10baseT, 10000}, - { "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10}, - { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14 * HZ) / 10}, - { "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10}, - { "MII", 0, 0x40, XCVR_10baseT, 3 * HZ}, - { "undefined", 0, 0x01, XCVR_10baseT, 10000}, - { "Default", 0, 0xFF, XCVR_10baseT, 10000}, -}; - -#ifdef __ISAPNP__ -static struct isapnp_device_id corkscrew_isapnp_adapters[] = { - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5051), - (long) "3Com Fast EtherLink ISA" }, - { } /* terminate list */ -}; - -MODULE_DEVICE_TABLE(isapnp, corkscrew_isapnp_adapters); - -static int nopnp; -#endif /* __ISAPNP__ */ - -static struct net_device *corkscrew_scan(int unit); -static int corkscrew_setup(struct net_device *dev, int ioaddr, - struct pnp_dev *idev, int card_number); -static int corkscrew_open(struct net_device *dev); -static void corkscrew_timer(struct timer_list *t); -static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static int corkscrew_rx(struct net_device *dev); -static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue); -static int boomerang_rx(struct net_device *dev); -static irqreturn_t corkscrew_interrupt(int irq, void *dev_id); -static int corkscrew_close(struct net_device *dev); -static void update_stats(int addr, struct net_device *dev); -static struct net_device_stats *corkscrew_get_stats(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static const struct ethtool_ops netdev_ethtool_ops; - - -/* - Unfortunately maximizing the shared code between the integrated and - module version of the driver results in a complicated set of initialization - procedures. - init_module() -- modules / tc59x_init() -- built-in - The wrappers for corkscrew_scan() - corkscrew_scan() The common routine that scans for PCI and EISA cards - corkscrew_found_device() Allocate a device structure when we find a card. - Different versions exist for modules and built-in. - corkscrew_probe1() Fill in the device structure -- this is separated - so that the modules code can put it in dev->init. -*/ -/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ -/* Note: this is the only limit on the number of cards supported!! */ -static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, }; - -#ifdef MODULE -static int debug = -1; - -module_param(debug, int, 0); -module_param_array(options, int, NULL, 0); -module_param(rx_copybreak, int, 0); -module_param(max_interrupt_work, int, 0); -MODULE_PARM_DESC(debug, "3c515 debug level (0-6)"); -MODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering"); -MODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames"); -MODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt"); - -/* A list of all installed Vortex devices, for removing the driver module. */ -/* we will need locking (and refcounting) if we ever use it for more */ -static LIST_HEAD(root_corkscrew_dev); - -static int corkscrew_init_module(void) -{ - int found = 0; - if (debug >= 0) - corkscrew_debug = debug; - while (corkscrew_scan(-1)) - found++; - return found ? 0 : -ENODEV; -} -module_init(corkscrew_init_module); - -#else -struct net_device *tc515_probe(int unit) -{ - struct net_device *dev = corkscrew_scan(unit); - - if (!dev) - return ERR_PTR(-ENODEV); - - return dev; -} -#endif /* not MODULE */ - -static int check_device(unsigned ioaddr) -{ - int timer; - - if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515")) - return 0; - /* Check the resource configuration for a matching ioaddr. */ - if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) { - release_region(ioaddr, CORKSCREW_TOTAL_SIZE); - return 0; - } - /* Verify by reading the device ID from the EEPROM. */ - outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd); - /* Pause for at least 162 us. for the read to take place. */ - for (timer = 4; timer >= 0; timer--) { - udelay(162); - if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) - break; - } - if (inw(ioaddr + Wn0EepromData) != 0x6d50) { - release_region(ioaddr, CORKSCREW_TOTAL_SIZE); - return 0; - } - return 1; -} - -static void cleanup_card(struct net_device *dev) -{ - struct corkscrew_private *vp = netdev_priv(dev); - list_del_init(&vp->list); - if (dev->dma) - free_dma(dev->dma); - outw(TotalReset, dev->base_addr + EL3_CMD); - release_region(dev->base_addr, CORKSCREW_TOTAL_SIZE); - if (vp->dev) - pnp_device_detach(to_pnp_dev(vp->dev)); -} - -static struct net_device *corkscrew_scan(int unit) -{ - struct net_device *dev; - static int cards_found = 0; - static int ioaddr; - int err; -#ifdef __ISAPNP__ - short i; - static int pnp_cards; -#endif - - dev = alloc_etherdev(sizeof(struct corkscrew_private)); - if (!dev) - return ERR_PTR(-ENOMEM); - - if (unit >= 0) { - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - } - -#ifdef __ISAPNP__ - if(nopnp == 1) - goto no_pnp; - for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) { - struct pnp_dev *idev = NULL; - int irq; - while((idev = pnp_find_dev(NULL, - corkscrew_isapnp_adapters[i].vendor, - corkscrew_isapnp_adapters[i].function, - idev))) { - - if (pnp_device_attach(idev) < 0) - continue; - if (pnp_activate_dev(idev) < 0) { - pr_warn("pnp activate failed (out of resources?)\n"); - pnp_device_detach(idev); - continue; - } - if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) { - pnp_device_detach(idev); - continue; - } - ioaddr = pnp_port_start(idev, 0); - irq = pnp_irq(idev, 0); - if (!check_device(ioaddr)) { - pnp_device_detach(idev); - continue; - } - if(corkscrew_debug) - pr_debug("ISAPNP reports %s at i/o 0x%x, irq %d\n", - (char*) corkscrew_isapnp_adapters[i].driver_data, ioaddr, irq); - pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n", - inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); - /* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */ - SET_NETDEV_DEV(dev, &idev->dev); - pnp_cards++; - err = corkscrew_setup(dev, ioaddr, idev, cards_found++); - if (!err) - return dev; - cleanup_card(dev); - } - } -no_pnp: -#endif /* __ISAPNP__ */ - - /* Check all locations on the ISA bus -- evil! */ - for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) { - if (!check_device(ioaddr)) - continue; - - pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n", - inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); - err = corkscrew_setup(dev, ioaddr, NULL, cards_found++); - if (!err) - return dev; - cleanup_card(dev); - } - free_netdev(dev); - return NULL; -} - - -static const struct net_device_ops netdev_ops = { - .ndo_open = corkscrew_open, - .ndo_stop = corkscrew_close, - .ndo_start_xmit = corkscrew_start_xmit, - .ndo_tx_timeout = corkscrew_timeout, - .ndo_get_stats = corkscrew_get_stats, - .ndo_set_rx_mode = set_rx_mode, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - - -static int corkscrew_setup(struct net_device *dev, int ioaddr, - struct pnp_dev *idev, int card_number) -{ - struct corkscrew_private *vp = netdev_priv(dev); - unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ - __be16 addr[ETH_ALEN / 2]; - int i; - int irq; - -#ifdef __ISAPNP__ - if (idev) { - irq = pnp_irq(idev, 0); - vp->dev = &idev->dev; - } else { - irq = inw(ioaddr + 0x2002) & 15; - } -#else - irq = inw(ioaddr + 0x2002) & 15; -#endif - - dev->base_addr = ioaddr; - dev->irq = irq; - dev->dma = inw(ioaddr + 0x2000) & 7; - vp->product_name = "3c515"; - vp->options = dev->mem_start; - vp->our_dev = dev; - - if (!vp->options) { - if (card_number >= MAX_UNITS) - vp->options = -1; - else - vp->options = options[card_number]; - } - - if (vp->options >= 0) { - vp->media_override = vp->options & 7; - if (vp->media_override == 2) - vp->media_override = 0; - vp->full_duplex = (vp->options & 8) ? 1 : 0; - vp->bus_master = (vp->options & 16) ? 1 : 0; - } else { - vp->media_override = 7; - vp->full_duplex = 0; - vp->bus_master = 0; - } -#ifdef MODULE - list_add(&vp->list, &root_corkscrew_dev); -#endif - - pr_info("%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr); - - spin_lock_init(&vp->lock); - - timer_setup(&vp->timer, corkscrew_timer, 0); - - /* Read the station address from the EEPROM. */ - EL3WINDOW(0); - for (i = 0; i < 0x18; i++) { - int timer; - outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd); - /* Pause for at least 162 us. for the read to take place. */ - for (timer = 4; timer >= 0; timer--) { - udelay(162); - if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) - break; - } - eeprom[i] = inw(ioaddr + Wn0EepromData); - checksum ^= eeprom[i]; - if (i < 3) - addr[i] = htons(eeprom[i]); - } - eth_hw_addr_set(dev, (u8 *)addr); - checksum = (checksum ^ (checksum >> 8)) & 0xff; - if (checksum != 0x00) - pr_cont(" ***INVALID CHECKSUM %4.4x*** ", checksum); - pr_cont(" %pM", dev->dev_addr); - if (eeprom[16] == 0x11c7) { /* Corkscrew */ - if (request_dma(dev->dma, "3c515")) { - pr_cont(", DMA %d allocation failed", dev->dma); - dev->dma = 0; - } else - pr_cont(", DMA %d", dev->dma); - } - pr_cont(", IRQ %d\n", dev->irq); - /* Tell them about an invalid IRQ. */ - if (corkscrew_debug && (dev->irq <= 0 || dev->irq > 15)) - pr_warn(" *** Warning: this IRQ is unlikely to work! ***\n"); - - { - static const char * const ram_split[] = { - "5:3", "3:1", "1:1", "3:5" - }; - __u32 config; - EL3WINDOW(3); - vp->available_media = inw(ioaddr + Wn3_Options); - config = inl(ioaddr + Wn3_Config); - if (corkscrew_debug > 1) - pr_info(" Internal config register is %4.4x, transceivers %#x.\n", - config, inw(ioaddr + Wn3_Options)); - pr_info(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n", - 8 << config & Ram_size, - config & Ram_width ? "word" : "byte", - ram_split[(config & Ram_split) >> Ram_split_shift], - config & Autoselect ? "autoselect/" : "", - media_tbl[(config & Xcvr) >> Xcvr_shift].name); - vp->default_media = (config & Xcvr) >> Xcvr_shift; - vp->autoselect = config & Autoselect ? 1 : 0; - dev->if_port = vp->default_media; - } - if (vp->media_override != 7) { - pr_info(" Media override to transceiver type %d (%s).\n", - vp->media_override, - media_tbl[vp->media_override].name); - dev->if_port = vp->media_override; - } - - vp->capabilities = eeprom[16]; - vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0; - /* Rx is broken at 10mbps, so we always disable it. */ - /* vp->full_bus_master_rx = 0; */ - vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0; - - /* The 3c51x-specific entries in the device structure. */ - dev->netdev_ops = &netdev_ops; - dev->watchdog_timeo = (400 * HZ) / 1000; - dev->ethtool_ops = &netdev_ethtool_ops; - - return register_netdev(dev); -} - - -static int corkscrew_open(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - struct corkscrew_private *vp = netdev_priv(dev); - bool armtimer = false; - __u32 config; - int i; - - /* Before initializing select the active media port. */ - EL3WINDOW(3); - if (vp->full_duplex) - outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */ - config = inl(ioaddr + Wn3_Config); - - if (vp->media_override != 7) { - if (corkscrew_debug > 1) - pr_info("%s: Media override to transceiver %d (%s).\n", - dev->name, vp->media_override, - media_tbl[vp->media_override].name); - dev->if_port = vp->media_override; - } else if (vp->autoselect) { - /* Find first available media type, starting with 100baseTx. */ - dev->if_port = 4; - while (!(vp->available_media & media_tbl[dev->if_port].mask)) - dev->if_port = media_tbl[dev->if_port].next; - - if (corkscrew_debug > 1) - pr_debug("%s: Initial media type %s.\n", - dev->name, media_tbl[dev->if_port].name); - armtimer = true; - } else - dev->if_port = vp->default_media; - - config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift); - outl(config, ioaddr + Wn3_Config); - - if (corkscrew_debug > 1) { - pr_debug("%s: corkscrew_open() InternalConfig %8.8x.\n", - dev->name, config); - } - - outw(TxReset, ioaddr + EL3_CMD); - for (i = 20; i >= 0; i--) - if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - - outw(RxReset, ioaddr + EL3_CMD); - /* Wait a few ticks for the RxReset command to complete. */ - for (i = 20; i >= 0; i--) - if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - - outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); - - /* Use the now-standard shared IRQ implementation. */ - if (vp->capabilities == 0x11c7) { - /* Corkscrew: Cannot share ISA resources. */ - if (dev->irq == 0 || - dev->dma == 0 || - request_irq(dev->irq, corkscrew_interrupt, 0, - vp->product_name, dev)) - return -EAGAIN; - enable_dma(dev->dma); - set_dma_mode(dev->dma, DMA_MODE_CASCADE); - } else if (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED, - vp->product_name, dev)) { - return -EAGAIN; - } - - if (armtimer) - mod_timer(&vp->timer, jiffies + media_tbl[dev->if_port].wait); - - if (corkscrew_debug > 1) { - EL3WINDOW(4); - pr_debug("%s: corkscrew_open() irq %d media status %4.4x.\n", - dev->name, dev->irq, inw(ioaddr + Wn4_Media)); - } - - /* Set the station address and mask in window 2 each time opened. */ - EL3WINDOW(2); - for (i = 0; i < 6; i++) - outb(dev->dev_addr[i], ioaddr + i); - for (; i < 12; i += 2) - outw(0, ioaddr + i); - - if (dev->if_port == 3) - /* Start the thinnet transceiver. We should really wait 50ms... */ - outw(StartCoax, ioaddr + EL3_CMD); - EL3WINDOW(4); - outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) | - media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); - - /* Switch to the stats window, and clear all stats by reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - EL3WINDOW(6); - for (i = 0; i < 10; i++) - inb(ioaddr + i); - inw(ioaddr + 10); - inw(ioaddr + 12); - /* New: On the Vortex we must also clear the BadSSD counter. */ - EL3WINDOW(4); - inb(ioaddr + 12); - /* ..and on the Boomerang we enable the extra statistics bits. */ - outw(0x0040, ioaddr + Wn4_NetDiag); - - /* Switch to register set 7 for normal use. */ - EL3WINDOW(7); - - if (vp->full_bus_master_rx) { /* Boomerang bus master. */ - vp->cur_rx = vp->dirty_rx = 0; - if (corkscrew_debug > 2) - pr_debug("%s: Filling in the Rx ring.\n", dev->name); - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb; - if (i < (RX_RING_SIZE - 1)) - vp->rx_ring[i].next = - isa_virt_to_bus(&vp->rx_ring[i + 1]); - else - vp->rx_ring[i].next = 0; - vp->rx_ring[i].status = 0; /* Clear complete bit. */ - vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000; - skb = netdev_alloc_skb(dev, PKT_BUF_SZ); - vp->rx_skbuff[i] = skb; - if (skb == NULL) - break; /* Bad news! */ - skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - vp->rx_ring[i].addr = isa_virt_to_bus(skb->data); - } - if (i != 0) - vp->rx_ring[i - 1].next = - isa_virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */ - outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr); - } - if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */ - vp->cur_tx = vp->dirty_tx = 0; - outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold); /* Room for a packet. */ - /* Clear the Tx ring. */ - for (i = 0; i < TX_RING_SIZE; i++) - vp->tx_skbuff[i] = NULL; - outl(0, ioaddr + DownListPtr); - } - /* Set receiver mode: presumably accept b-case and phys addr only. */ - set_rx_mode(dev); - outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ - - netif_start_queue(dev); - - outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ - outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ - /* Allow status bits to be seen. */ - outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull | - (vp->full_bus_master_tx ? DownComplete : TxAvailable) | - (vp->full_bus_master_rx ? UpComplete : RxComplete) | - (vp->bus_master ? DMADone : 0), ioaddr + EL3_CMD); - /* Ack all pending events, and set active indicator mask. */ - outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, - ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull - | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete, - ioaddr + EL3_CMD); - - return 0; -} - -static void corkscrew_timer(struct timer_list *t) -{ -#ifdef AUTOMEDIA - struct corkscrew_private *vp = timer_container_of(vp, t, timer); - struct net_device *dev = vp->our_dev; - int ioaddr = dev->base_addr; - unsigned long flags; - int ok = 0; - - if (corkscrew_debug > 1) - pr_debug("%s: Media selection timer tick happened, %s.\n", - dev->name, media_tbl[dev->if_port].name); - - spin_lock_irqsave(&vp->lock, flags); - - { - int old_window = inw(ioaddr + EL3_CMD) >> 13; - int media_status; - EL3WINDOW(4); - media_status = inw(ioaddr + Wn4_Media); - switch (dev->if_port) { - case 0: - case 4: - case 5: /* 10baseT, 100baseTX, 100baseFX */ - if (media_status & Media_LnkBeat) { - ok = 1; - if (corkscrew_debug > 1) - pr_debug("%s: Media %s has link beat, %x.\n", - dev->name, - media_tbl[dev->if_port].name, - media_status); - } else if (corkscrew_debug > 1) - pr_debug("%s: Media %s is has no link beat, %x.\n", - dev->name, - media_tbl[dev->if_port].name, - media_status); - - break; - default: /* Other media types handled by Tx timeouts. */ - if (corkscrew_debug > 1) - pr_debug("%s: Media %s is has no indication, %x.\n", - dev->name, - media_tbl[dev->if_port].name, - media_status); - ok = 1; - } - if (!ok) { - __u32 config; - - do { - dev->if_port = - media_tbl[dev->if_port].next; - } - while (!(vp->available_media & media_tbl[dev->if_port].mask)); - - if (dev->if_port == 8) { /* Go back to default. */ - dev->if_port = vp->default_media; - if (corkscrew_debug > 1) - pr_debug("%s: Media selection failing, using default %s port.\n", - dev->name, - media_tbl[dev->if_port].name); - } else { - if (corkscrew_debug > 1) - pr_debug("%s: Media selection failed, now trying %s port.\n", - dev->name, - media_tbl[dev->if_port].name); - vp->timer.expires = jiffies + media_tbl[dev->if_port].wait; - add_timer(&vp->timer); - } - outw((media_status & ~(Media_10TP | Media_SQE)) | - media_tbl[dev->if_port].media_bits, - ioaddr + Wn4_Media); - - EL3WINDOW(3); - config = inl(ioaddr + Wn3_Config); - config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift); - outl(config, ioaddr + Wn3_Config); - - outw(dev->if_port == 3 ? StartCoax : StopCoax, - ioaddr + EL3_CMD); - } - EL3WINDOW(old_window); - } - - spin_unlock_irqrestore(&vp->lock, flags); - if (corkscrew_debug > 1) - pr_debug("%s: Media selection timer finished, %s.\n", - dev->name, media_tbl[dev->if_port].name); - -#endif /* AUTOMEDIA */ -} - -static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue) -{ - int i; - struct corkscrew_private *vp = netdev_priv(dev); - int ioaddr = dev->base_addr; - - pr_warn("%s: transmit timed out, tx_status %2.2x status %4.4x\n", - dev->name, inb(ioaddr + TxStatus), - inw(ioaddr + EL3_STATUS)); - /* Slight code bloat to be user friendly. */ - if ((inb(ioaddr + TxStatus) & 0x88) == 0x88) - pr_warn("%s: Transmitter encountered 16 collisions -- network cable problem?\n", - dev->name); -#ifndef final_version - pr_debug(" Flags; bus-master %d, full %d; dirty %d current %d.\n", - vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, - vp->cur_tx); - pr_debug(" Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr), - &vp->tx_ring[0]); - for (i = 0; i < TX_RING_SIZE; i++) { - pr_debug(" %d: %p length %8.8x status %8.8x\n", i, - &vp->tx_ring[i], - vp->tx_ring[i].length, vp->tx_ring[i].status); - } -#endif - /* Issue TX_RESET and TX_START commands. */ - outw(TxReset, ioaddr + EL3_CMD); - for (i = 20; i >= 0; i--) - if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - outw(TxEnable, ioaddr + EL3_CMD); - netif_trans_update(dev); /* prevent tx timeout */ - dev->stats.tx_errors++; - dev->stats.tx_dropped++; - netif_wake_queue(dev); -} - -static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct corkscrew_private *vp = netdev_priv(dev); - int ioaddr = dev->base_addr; - - /* Block a timer-based transmit from overlapping. */ - - netif_stop_queue(dev); - - if (vp->full_bus_master_tx) { /* BOOMERANG bus-master */ - /* Calculate the next Tx descriptor entry. */ - int entry = vp->cur_tx % TX_RING_SIZE; - struct boom_tx_desc *prev_entry; - unsigned long flags; - int i; - - if (vp->tx_full) /* No room to transmit with */ - return NETDEV_TX_BUSY; - if (vp->cur_tx != 0) - prev_entry = &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE]; - else - prev_entry = NULL; - if (corkscrew_debug > 3) - pr_debug("%s: Trying to send a packet, Tx index %d.\n", - dev->name, vp->cur_tx); - /* vp->tx_full = 1; */ - vp->tx_skbuff[entry] = skb; - vp->tx_ring[entry].next = 0; - vp->tx_ring[entry].addr = isa_virt_to_bus(skb->data); - vp->tx_ring[entry].length = skb->len | 0x80000000; - vp->tx_ring[entry].status = skb->len | 0x80000000; - - spin_lock_irqsave(&vp->lock, flags); - outw(DownStall, ioaddr + EL3_CMD); - /* Wait for the stall to complete. */ - for (i = 20; i >= 0; i--) - if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) - break; - if (prev_entry) - prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]); - if (inl(ioaddr + DownListPtr) == 0) { - outl(isa_virt_to_bus(&vp->tx_ring[entry]), - ioaddr + DownListPtr); - queued_packet++; - } - outw(DownUnstall, ioaddr + EL3_CMD); - spin_unlock_irqrestore(&vp->lock, flags); - - vp->cur_tx++; - if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1) - vp->tx_full = 1; - else { /* Clear previous interrupt enable. */ - if (prev_entry) - prev_entry->status &= ~0x80000000; - netif_wake_queue(dev); - } - return NETDEV_TX_OK; - } - /* Put out the doubleword header... */ - outl(skb->len, ioaddr + TX_FIFO); - dev->stats.tx_bytes += skb->len; -#ifdef VORTEX_BUS_MASTER - if (vp->bus_master) { - /* Set the bus-master controller to transfer the packet. */ - outl(isa_virt_to_bus(skb->data), ioaddr + Wn7_MasterAddr); - outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen); - vp->tx_skb = skb; - outw(StartDMADown, ioaddr + EL3_CMD); - /* queue will be woken at the DMADone interrupt. */ - } else { - /* ... and the packet rounded to a doubleword. */ - outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); - dev_kfree_skb(skb); - if (inw(ioaddr + TxFree) > 1536) { - netif_wake_queue(dev); - } else - /* Interrupt us when the FIFO has room for max-sized packet. */ - outw(SetTxThreshold + (1536 >> 2), - ioaddr + EL3_CMD); - } -#else - /* ... and the packet rounded to a doubleword. */ - outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); - dev_kfree_skb(skb); - if (inw(ioaddr + TxFree) > 1536) { - netif_wake_queue(dev); - } else - /* Interrupt us when the FIFO has room for max-sized packet. */ - outw(SetTxThreshold + (1536 >> 2), ioaddr + EL3_CMD); -#endif /* bus master */ - - - /* Clear the Tx status stack. */ - { - short tx_status; - int i = 4; - - while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) { - if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */ - if (corkscrew_debug > 2) - pr_debug("%s: Tx error, status %2.2x.\n", - dev->name, tx_status); - if (tx_status & 0x04) - dev->stats.tx_fifo_errors++; - if (tx_status & 0x38) - dev->stats.tx_aborted_errors++; - if (tx_status & 0x30) { - int j; - outw(TxReset, ioaddr + EL3_CMD); - for (j = 20; j >= 0; j--) - if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - } - outw(TxEnable, ioaddr + EL3_CMD); - } - outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */ - } - } - return NETDEV_TX_OK; -} - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ - -static irqreturn_t corkscrew_interrupt(int irq, void *dev_id) -{ - /* Use the now-standard shared IRQ implementation. */ - struct net_device *dev = dev_id; - struct corkscrew_private *lp = netdev_priv(dev); - int ioaddr, status; - int latency; - int i = max_interrupt_work; - - ioaddr = dev->base_addr; - latency = inb(ioaddr + Timer); - - spin_lock(&lp->lock); - - status = inw(ioaddr + EL3_STATUS); - - if (corkscrew_debug > 4) - pr_debug("%s: interrupt, status %4.4x, timer %d.\n", - dev->name, status, latency); - if ((status & 0xE000) != 0xE000) { - static int donedidthis; - /* Some interrupt controllers store a bogus interrupt from boot-time. - Ignore a single early interrupt, but don't hang the machine for - other interrupt problems. */ - if (donedidthis++ > 100) { - pr_err("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n", - dev->name, status, netif_running(dev)); - free_irq(dev->irq, dev); - dev->irq = -1; - } - } - - do { - if (corkscrew_debug > 5) - pr_debug("%s: In interrupt loop, status %4.4x.\n", - dev->name, status); - if (status & RxComplete) - corkscrew_rx(dev); - - if (status & TxAvailable) { - if (corkscrew_debug > 5) - pr_debug(" TX room bit was handled.\n"); - /* There's room in the FIFO for a full-sized packet. */ - outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); - netif_wake_queue(dev); - } - if (status & DownComplete) { - unsigned int dirty_tx = lp->dirty_tx; - - while (lp->cur_tx - dirty_tx > 0) { - int entry = dirty_tx % TX_RING_SIZE; - if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry])) - break; /* It still hasn't been processed. */ - if (lp->tx_skbuff[entry]) { - dev_consume_skb_irq(lp->tx_skbuff[entry]); - lp->tx_skbuff[entry] = NULL; - } - dirty_tx++; - } - lp->dirty_tx = dirty_tx; - outw(AckIntr | DownComplete, ioaddr + EL3_CMD); - if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) { - lp->tx_full = 0; - netif_wake_queue(dev); - } - } -#ifdef VORTEX_BUS_MASTER - if (status & DMADone) { - outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ - dev_consume_skb_irq(lp->tx_skb); /* Release the transferred buffer */ - netif_wake_queue(dev); - } -#endif - if (status & UpComplete) { - boomerang_rx(dev); - outw(AckIntr | UpComplete, ioaddr + EL3_CMD); - } - if (status & (AdapterFailure | RxEarly | StatsFull)) { - /* Handle all uncommon interrupts at once. */ - if (status & RxEarly) { /* Rx early is unused. */ - corkscrew_rx(dev); - outw(AckIntr | RxEarly, ioaddr + EL3_CMD); - } - if (status & StatsFull) { /* Empty statistics. */ - static int DoneDidThat; - if (corkscrew_debug > 4) - pr_debug("%s: Updating stats.\n", dev->name); - update_stats(ioaddr, dev); - /* DEBUG HACK: Disable statistics as an interrupt source. */ - /* This occurs when we have the wrong media type! */ - if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) { - int win, reg; - pr_notice("%s: Updating stats failed, disabling stats as an interrupt source.\n", - dev->name); - for (win = 0; win < 8; win++) { - EL3WINDOW(win); - pr_notice("Vortex window %d:", win); - for (reg = 0; reg < 16; reg++) - pr_cont(" %2.2x", inb(ioaddr + reg)); - pr_cont("\n"); - } - EL3WINDOW(7); - outw(SetIntrEnb | TxAvailable | - RxComplete | AdapterFailure | - UpComplete | DownComplete | - TxComplete, ioaddr + EL3_CMD); - DoneDidThat++; - } - } - if (status & AdapterFailure) { - /* Adapter failure requires Rx reset and reinit. */ - outw(RxReset, ioaddr + EL3_CMD); - /* Set the Rx filter to the current state. */ - set_rx_mode(dev); - outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ - outw(AckIntr | AdapterFailure, - ioaddr + EL3_CMD); - } - } - - if (--i < 0) { - pr_err("%s: Too much work in interrupt, status %4.4x. Disabling functions (%4.4x).\n", - dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); - /* Disable all pending interrupts. */ - outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); - outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); - break; - } - /* Acknowledge the IRQ. */ - outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); - - } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); - - spin_unlock(&lp->lock); - - if (corkscrew_debug > 4) - pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, status); - return IRQ_HANDLED; -} - -static int corkscrew_rx(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - int i; - short rx_status; - - if (corkscrew_debug > 5) - pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", - inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus)); - while ((rx_status = inw(ioaddr + RxStatus)) > 0) { - if (rx_status & 0x4000) { /* Error, update stats. */ - unsigned char rx_error = inb(ioaddr + RxErrors); - if (corkscrew_debug > 2) - pr_debug(" Rx error: status %2.2x.\n", - rx_error); - dev->stats.rx_errors++; - if (rx_error & 0x01) - dev->stats.rx_over_errors++; - if (rx_error & 0x02) - dev->stats.rx_length_errors++; - if (rx_error & 0x04) - dev->stats.rx_frame_errors++; - if (rx_error & 0x08) - dev->stats.rx_crc_errors++; - if (rx_error & 0x10) - dev->stats.rx_length_errors++; - } else { - /* The packet length: up to 4.5K!. */ - short pkt_len = rx_status & 0x1fff; - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, pkt_len + 5 + 2); - if (corkscrew_debug > 4) - pr_debug("Receiving packet size %d status %4.4x.\n", - pkt_len, rx_status); - if (skb != NULL) { - skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - /* 'skb_put()' points to the start of sk_buff data area. */ - insl(ioaddr + RX_FIFO, - skb_put(skb, pkt_len), - (pkt_len + 3) >> 2); - outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - /* Wait a limited time to go to next packet. */ - for (i = 200; i >= 0; i--) - if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - continue; - } else if (corkscrew_debug) - pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); - } - outw(RxDiscard, ioaddr + EL3_CMD); - dev->stats.rx_dropped++; - /* Wait a limited time to skip this packet. */ - for (i = 200; i >= 0; i--) - if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - } - return 0; -} - -static int boomerang_rx(struct net_device *dev) -{ - struct corkscrew_private *vp = netdev_priv(dev); - int entry = vp->cur_rx % RX_RING_SIZE; - int ioaddr = dev->base_addr; - int rx_status; - - if (corkscrew_debug > 5) - pr_debug(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n", - inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus)); - while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) { - if (rx_status & RxDError) { /* Error, update stats. */ - unsigned char rx_error = rx_status >> 16; - if (corkscrew_debug > 2) - pr_debug(" Rx error: status %2.2x.\n", - rx_error); - dev->stats.rx_errors++; - if (rx_error & 0x01) - dev->stats.rx_over_errors++; - if (rx_error & 0x02) - dev->stats.rx_length_errors++; - if (rx_error & 0x04) - dev->stats.rx_frame_errors++; - if (rx_error & 0x08) - dev->stats.rx_crc_errors++; - if (rx_error & 0x10) - dev->stats.rx_length_errors++; - } else { - /* The packet length: up to 4.5K!. */ - short pkt_len = rx_status & 0x1fff; - struct sk_buff *skb; - - dev->stats.rx_bytes += pkt_len; - if (corkscrew_debug > 4) - pr_debug("Receiving packet size %d status %4.4x.\n", - pkt_len, rx_status); - - /* Check if the packet is long enough to just accept without - copying to a properly sized skbuff. */ - if (pkt_len < rx_copybreak && - (skb = netdev_alloc_skb(dev, pkt_len + 4)) != NULL) { - skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - /* 'skb_put()' points to the start of sk_buff data area. */ - skb_put_data(skb, - isa_bus_to_virt(vp->rx_ring[entry].addr), - pkt_len); - rx_copy++; - } else { - void *temp; - /* Pass up the skbuff already on the Rx ring. */ - skb = vp->rx_skbuff[entry]; - vp->rx_skbuff[entry] = NULL; - temp = skb_put(skb, pkt_len); - /* Remove this checking code for final release. */ - if (isa_bus_to_virt(vp->rx_ring[entry].addr) != temp) - pr_warn("%s: Warning -- the skbuff addresses do not match in boomerang_rx: %p vs. %p / %p\n", - dev->name, - isa_bus_to_virt(vp->rx_ring[entry].addr), - skb->head, temp); - rx_nocopy++; - } - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - } - entry = (++vp->cur_rx) % RX_RING_SIZE; - } - /* Refill the Rx ring buffers. */ - for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) { - struct sk_buff *skb; - entry = vp->dirty_rx % RX_RING_SIZE; - if (vp->rx_skbuff[entry] == NULL) { - skb = netdev_alloc_skb(dev, PKT_BUF_SZ); - if (skb == NULL) - break; /* Bad news! */ - skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - vp->rx_ring[entry].addr = isa_virt_to_bus(skb->data); - vp->rx_skbuff[entry] = skb; - } - vp->rx_ring[entry].status = 0; /* Clear complete bit. */ - } - return 0; -} - -static int corkscrew_close(struct net_device *dev) -{ - struct corkscrew_private *vp = netdev_priv(dev); - int ioaddr = dev->base_addr; - int i; - - netif_stop_queue(dev); - - if (corkscrew_debug > 1) { - pr_debug("%s: corkscrew_close() status %4.4x, Tx status %2.2x.\n", - dev->name, inw(ioaddr + EL3_STATUS), - inb(ioaddr + TxStatus)); - pr_debug("%s: corkscrew close stats: rx_nocopy %d rx_copy %d tx_queued %d.\n", - dev->name, rx_nocopy, rx_copy, queued_packet); - } - - timer_delete_sync(&vp->timer); - - /* Turn off statistics ASAP. We update lp->stats below. */ - outw(StatsDisable, ioaddr + EL3_CMD); - - /* Disable the receiver and transmitter. */ - outw(RxDisable, ioaddr + EL3_CMD); - outw(TxDisable, ioaddr + EL3_CMD); - - if (dev->if_port == XCVR_10base2) - /* Turn off thinnet power. Green! */ - outw(StopCoax, ioaddr + EL3_CMD); - - free_irq(dev->irq, dev); - - outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); - - update_stats(ioaddr, dev); - if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */ - outl(0, ioaddr + UpListPtr); - for (i = 0; i < RX_RING_SIZE; i++) - if (vp->rx_skbuff[i]) { - dev_kfree_skb(vp->rx_skbuff[i]); - vp->rx_skbuff[i] = NULL; - } - } - if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */ - outl(0, ioaddr + DownListPtr); - for (i = 0; i < TX_RING_SIZE; i++) - if (vp->tx_skbuff[i]) { - dev_kfree_skb(vp->tx_skbuff[i]); - vp->tx_skbuff[i] = NULL; - } - } - - return 0; -} - -static struct net_device_stats *corkscrew_get_stats(struct net_device *dev) -{ - struct corkscrew_private *vp = netdev_priv(dev); - unsigned long flags; - - if (netif_running(dev)) { - spin_lock_irqsave(&vp->lock, flags); - update_stats(dev->base_addr, dev); - spin_unlock_irqrestore(&vp->lock, flags); - } - return &dev->stats; -} - -/* Update statistics. - Unlike with the EL3 we need not worry about interrupts changing - the window setting from underneath us, but we must still guard - against a race condition with a StatsUpdate interrupt updating the - table. This is done by checking that the ASM (!) code generated uses - atomic updates with '+='. - */ -static void update_stats(int ioaddr, struct net_device *dev) -{ - /* Unlike the 3c5x9 we need not turn off stats updates while reading. */ - /* Switch to the stats window, and read everything. */ - EL3WINDOW(6); - dev->stats.tx_carrier_errors += inb(ioaddr + 0); - dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); - /* Multiple collisions. */ inb(ioaddr + 2); - dev->stats.collisions += inb(ioaddr + 3); - dev->stats.tx_window_errors += inb(ioaddr + 4); - dev->stats.rx_fifo_errors += inb(ioaddr + 5); - dev->stats.tx_packets += inb(ioaddr + 6); - dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4; - /* Rx packets */ inb(ioaddr + 7); - /* Must read to clear */ - /* Tx deferrals */ inb(ioaddr + 8); - /* Don't bother with register 9, an extension of registers 6&7. - If we do use the 6&7 values the atomic update assumption above - is invalid. */ - inw(ioaddr + 10); /* Total Rx and Tx octets. */ - inw(ioaddr + 12); - /* New: On the Vortex we must also clear the BadSSD counter. */ - EL3WINDOW(4); - inb(ioaddr + 12); - - /* We change back to window 7 (not 1) with the Vortex. */ - EL3WINDOW(7); -} - -/* This new version of set_rx_mode() supports v1.4 kernels. - The Vortex chip has no documented multicast filter, so the only - multicast setting is to receive all multicast frames. At least - the chip has a very clean way to set the mode, unlike many others. */ -static void set_rx_mode(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - unsigned short new_mode; - - if (dev->flags & IFF_PROMISC) { - if (corkscrew_debug > 3) - pr_debug("%s: Setting promiscuous mode.\n", - dev->name); - new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm; - } else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) { - new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast; - } else - new_mode = SetRxFilter | RxStation | RxBroadcast; - - outw(new_mode, ioaddr + EL3_CMD); -} - -static void netdev_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx", - dev->base_addr); -} - -static u32 netdev_get_msglevel(struct net_device *dev) -{ - return corkscrew_debug; -} - -static void netdev_set_msglevel(struct net_device *dev, u32 level) -{ - corkscrew_debug = level; -} - -static const struct ethtool_ops netdev_ethtool_ops = { - .get_drvinfo = netdev_get_drvinfo, - .get_msglevel = netdev_get_msglevel, - .set_msglevel = netdev_set_msglevel, -}; - -#ifdef MODULE -static void __exit corkscrew_exit_module(void) -{ - while (!list_empty(&root_corkscrew_dev)) { - struct net_device *dev; - struct corkscrew_private *vp; - - vp = list_entry(root_corkscrew_dev.next, - struct corkscrew_private, list); - dev = vp->our_dev; - unregister_netdev(dev); - cleanup_card(dev); - free_netdev(dev); - } -} -module_exit(corkscrew_exit_module); -#endif /* MODULE */ diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c deleted file mode 100644 index 1f2070497a75..000000000000 --- a/drivers/net/ethernet/3com/3c574_cs.c +++ /dev/null @@ -1,1164 +0,0 @@ -/* 3c574.c: A PCMCIA ethernet driver for the 3com 3c574 "RoadRunner". - - Written 1993-1998 by - Donald Becker, becker@scyld.com, (driver core) and - David Hinds, dahinds@users.sourceforge.net (from his PC card code). - Locking fixes (C) Copyright 2003 Red Hat Inc - - This software may be used and distributed according to the terms of - the GNU General Public License, incorporated herein by reference. - - This driver derives from Donald Becker's 3c509 core, which has the - following copyright: - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - - -*/ - -/* - Theory of Operation - -I. Board Compatibility - -This device driver is designed for the 3Com 3c574 PC card Fast Ethernet -Adapter. - -II. Board-specific settings - -None -- PC cards are autoconfigured. - -III. Driver operation - -The 3c574 uses a Boomerang-style interface, without the bus-master capability. -See the Boomerang driver and documentation for most details. - -IV. Notes and chip documentation. - -Two added registers are used to enhance PIO performance, RunnerRdCtrl and -RunnerWrCtrl. These are 11 bit down-counters that are preloaded with the -count of word (16 bits) reads or writes the driver is about to do to the Rx -or Tx FIFO. The chip is then able to hide the internal-PCI-bus to PC-card -translation latency by buffering the I/O operations with an 8 word FIFO. -Note: No other chip accesses are permitted when this buffer is used. - -A second enhancement is that both attribute and common memory space -0x0800-0x0fff can translated to the PIO FIFO. Thus memory operations (faster -with *some* PCcard bridges) may be used instead of I/O operations. -This is enabled by setting the 0x10 bit in the PCMCIA LAN COR. - -Some slow PC card bridges work better if they never see a WAIT signal. -This is configured by setting the 0x20 bit in the PCMCIA LAN COR. -Only do this after testing that it is reliable and improves performance. - -The upper five bits of RunnerRdCtrl are used to window into PCcard -configuration space registers. Window 0 is the regular Boomerang/Odie -register set, 1-5 are various PC card control registers, and 16-31 are -the (reversed!) CIS table. - -A final note: writing the InternalConfig register in window 3 with an -invalid ramWidth is Very Bad. - -V. References - -http://www.scyld.com/expert/NWay.html -http://www.national.com/opf/DP/DP83840A.html - -Thanks to Terry Murphy of 3Com for providing development information for -earlier 3Com products. - -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -/*====================================================================*/ - -/* Module parameters */ - -MODULE_AUTHOR("David Hinds "); -MODULE_DESCRIPTION("3Com 3c574 series PCMCIA ethernet driver"); -MODULE_LICENSE("GPL"); - -#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) - -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -INT_MODULE_PARM(max_interrupt_work, 32); - -/* Force full duplex modes? */ -INT_MODULE_PARM(full_duplex, 0); - -/* Autodetect link polarity reversal? */ -INT_MODULE_PARM(auto_polarity, 1); - - -/*====================================================================*/ - -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT ((800*HZ)/1000) - -/* To minimize the size of the driver source and make the driver more - readable not all constants are symbolically defined. - You'll need the manual if you want to understand driver details anyway. */ -/* Offsets from base I/O address. */ -#define EL3_DATA 0x00 -#define EL3_CMD 0x0e -#define EL3_STATUS 0x0e - -#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) - -/* The top five bits written to EL3_CMD are a command, the lower - 11 bits are the parameter, if applicable. */ -enum el3_cmds { - TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, - RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, - TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, - FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, - SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, - SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11, - StatsDisable = 22<<11, StopCoax = 23<<11, -}; - -enum elxl_status { - IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, - TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, - IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 }; - -/* The SetRxFilter command accepts the following classes: */ -enum RxFilter { - RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 -}; - -enum Window0 { - Wn0EepromCmd = 10, Wn0EepromData = 12, /* EEPROM command/address, data. */ - IntrStatus=0x0E, /* Valid in all windows. */ -}; -/* These assumes the larger EEPROM. */ -enum Win0_EEPROM_cmds { - EEPROM_Read = 0x200, EEPROM_WRITE = 0x100, EEPROM_ERASE = 0x300, - EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */ - EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */ -}; - -/* Register window 1 offsets, the window used in normal operation. - On the "Odie" this window is always mapped at offsets 0x10-0x1f. - Except for TxFree, which is overlapped by RunnerWrCtrl. */ -enum Window1 { - TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14, - RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B, - TxFree = 0x0C, /* Remaining free bytes in Tx buffer. */ - RunnerRdCtrl = 0x16, RunnerWrCtrl = 0x1c, -}; - -enum Window3 { /* Window 3: MAC/config bits. */ - Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8, -}; -enum wn3_config { - Ram_size = 7, - Ram_width = 8, - Ram_speed = 0x30, - Rom_size = 0xc0, - Ram_split_shift = 16, - Ram_split = 3 << Ram_split_shift, - Xcvr_shift = 20, - Xcvr = 7 << Xcvr_shift, - Autoselect = 0x1000000, -}; - -enum Window4 { /* Window 4: Xcvr/media bits. */ - Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10, -}; - -#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ - -struct el3_private { - struct pcmcia_device *p_dev; - u16 advertising, partner; /* NWay media advertisement */ - unsigned char phys; /* MII device address */ - unsigned int autoselect:1, default_media:3; /* Read from the EEPROM/Wn3_Config. */ - /* for transceiver monitoring */ - struct timer_list media; - unsigned short media_status; - unsigned short fast_poll; - unsigned long last_irq; - spinlock_t window_lock; /* Guards the Window selection */ -}; - -/* Set iff a MII transceiver on any interface requires mdio preamble. - This only set with the original DP83840 on older 3c905 boards, so the extra - code size of a per-interface flag is not worthwhile. */ -static char mii_preamble_required = 0; - -/* Index of functions. */ - -static int tc574_config(struct pcmcia_device *link); -static void tc574_release(struct pcmcia_device *link); - -static void mdio_sync(unsigned int ioaddr, int bits); -static int mdio_read(unsigned int ioaddr, int phy_id, int location); -static void mdio_write(unsigned int ioaddr, int phy_id, int location, - int value); -static unsigned short read_eeprom(unsigned int ioaddr, int index); -static void tc574_wait_for_completion(struct net_device *dev, int cmd); - -static void tc574_reset(struct net_device *dev); -static void media_check(struct timer_list *t); -static int el3_open(struct net_device *dev); -static netdev_tx_t el3_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t el3_interrupt(int irq, void *dev_id); -static void update_stats(struct net_device *dev); -static struct net_device_stats *el3_get_stats(struct net_device *dev); -static int el3_rx(struct net_device *dev, int worklimit); -static int el3_close(struct net_device *dev); -static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue); -static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void set_rx_mode(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); - -static void tc574_detach(struct pcmcia_device *p_dev); - -/* - tc574_attach() creates an "instance" of the driver, allocating - local data structures for one device. The device is registered - with Card Services. -*/ -static const struct net_device_ops el3_netdev_ops = { - .ndo_open = el3_open, - .ndo_stop = el3_close, - .ndo_start_xmit = el3_start_xmit, - .ndo_tx_timeout = el3_tx_timeout, - .ndo_get_stats = el3_get_stats, - .ndo_eth_ioctl = el3_ioctl, - .ndo_set_rx_mode = set_multicast_list, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int tc574_probe(struct pcmcia_device *link) -{ - struct el3_private *lp; - struct net_device *dev; - - dev_dbg(&link->dev, "3c574_attach()\n"); - - /* Create the PC card device object. */ - dev = alloc_etherdev(sizeof(struct el3_private)); - if (!dev) - return -ENOMEM; - lp = netdev_priv(dev); - link->priv = dev; - lp->p_dev = link; - - spin_lock_init(&lp->window_lock); - link->resource[0]->end = 32; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; - link->config_flags |= CONF_ENABLE_IRQ; - link->config_index = 1; - - dev->netdev_ops = &el3_netdev_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - return tc574_config(link); -} - -static void tc574_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - dev_dbg(&link->dev, "3c574_detach()\n"); - - unregister_netdev(dev); - - tc574_release(link); - - free_netdev(dev); -} /* tc574_detach */ - -static const char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; - -static int tc574_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct el3_private *lp = netdev_priv(dev); - int ret, i, j; - __be16 addr[ETH_ALEN / 2]; - unsigned int ioaddr; - char *cardname; - __u32 config; - u8 *buf; - size_t len; - - dev_dbg(&link->dev, "3c574_config()\n"); - - link->io_lines = 16; - - for (i = j = 0; j < 0x400; j += 0x20) { - link->resource[0]->start = j ^ 0x300; - i = pcmcia_request_io(link); - if (i == 0) - break; - } - if (i != 0) - goto failed; - - ret = pcmcia_request_irq(link, el3_interrupt); - if (ret) - goto failed; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - dev->irq = link->irq; - dev->base_addr = link->resource[0]->start; - - ioaddr = dev->base_addr; - - /* The 3c574 normally uses an EEPROM for configuration info, including - the hardware address. The future products may include a modem chip - and put the address in the CIS. */ - - len = pcmcia_get_tuple(link, 0x88, &buf); - if (buf && len >= 6) { - for (i = 0; i < 3; i++) - addr[i] = htons(le16_to_cpu(buf[i * 2])); - kfree(buf); - } else { - kfree(buf); /* 0 < len < 6 */ - EL3WINDOW(0); - for (i = 0; i < 3; i++) - addr[i] = htons(read_eeprom(ioaddr, i + 10)); - if (addr[0] == htons(0x6060)) { - pr_notice("IO port conflict at 0x%03lx-0x%03lx\n", - dev->base_addr, dev->base_addr+15); - goto failed; - } - } - eth_hw_addr_set(dev, (u8 *)addr); - if (link->prod_id[1]) - cardname = link->prod_id[1]; - else - cardname = "3Com 3c574"; - - { - u_char mcr; - outw(2<<11, ioaddr + RunnerRdCtrl); - mcr = inb(ioaddr + 2); - outw(0<<11, ioaddr + RunnerRdCtrl); - pr_info(" ASIC rev %d,", mcr>>3); - EL3WINDOW(3); - config = inl(ioaddr + Wn3_Config); - lp->default_media = (config & Xcvr) >> Xcvr_shift; - lp->autoselect = config & Autoselect ? 1 : 0; - } - - timer_setup(&lp->media, media_check, 0); - - { - int phy; - - /* Roadrunner only: Turn on the MII transceiver */ - outw(0x8040, ioaddr + Wn3_Options); - mdelay(1); - outw(0xc040, ioaddr + Wn3_Options); - tc574_wait_for_completion(dev, TxReset); - tc574_wait_for_completion(dev, RxReset); - mdelay(1); - outw(0x8040, ioaddr + Wn3_Options); - - EL3WINDOW(4); - for (phy = 1; phy <= 32; phy++) { - int mii_status; - mdio_sync(ioaddr, 32); - mii_status = mdio_read(ioaddr, phy & 0x1f, 1); - if (mii_status != 0xffff) { - lp->phys = phy & 0x1f; - dev_dbg(&link->dev, " MII transceiver at " - "index %d, status %x.\n", - phy, mii_status); - if ((mii_status & 0x0040) == 0) - mii_preamble_required = 1; - break; - } - } - if (phy > 32) { - pr_notice(" No MII transceivers found!\n"); - goto failed; - } - i = mdio_read(ioaddr, lp->phys, 16) | 0x40; - mdio_write(ioaddr, lp->phys, 16, i); - lp->advertising = mdio_read(ioaddr, lp->phys, 4); - if (full_duplex) { - /* Only advertise the FD media types. */ - lp->advertising &= ~0x02a0; - mdio_write(ioaddr, lp->phys, 4, lp->advertising); - } - } - - SET_NETDEV_DEV(dev, &link->dev); - - if (register_netdev(dev) != 0) { - pr_notice("register_netdev() failed\n"); - goto failed; - } - - netdev_info(dev, "%s at io %#3lx, irq %d, hw_addr %pM\n", - cardname, dev->base_addr, dev->irq, dev->dev_addr); - netdev_info(dev, " %dK FIFO split %s Rx:Tx, %sMII interface.\n", - 8 << (config & Ram_size), - ram_split[(config & Ram_split) >> Ram_split_shift], - config & Autoselect ? "autoselect " : ""); - - return 0; - -failed: - tc574_release(link); - return -ENODEV; - -} /* tc574_config */ - -static void tc574_release(struct pcmcia_device *link) -{ - pcmcia_disable_device(link); -} - -static int tc574_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int tc574_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) { - tc574_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - -static void dump_status(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - EL3WINDOW(1); - netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x, tx free %04x\n", - inw(ioaddr+EL3_STATUS), - inw(ioaddr+RxStatus), inb(ioaddr+TxStatus), - inw(ioaddr+TxFree)); - EL3WINDOW(4); - netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n", - inw(ioaddr+0x04), inw(ioaddr+0x06), - inw(ioaddr+0x08), inw(ioaddr+0x0a)); - EL3WINDOW(1); -} - -/* - Use this for commands that may take time to finish -*/ -static void tc574_wait_for_completion(struct net_device *dev, int cmd) -{ - int i = 1500; - outw(cmd, dev->base_addr + EL3_CMD); - while (--i > 0) - if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break; - if (i == 0) - netdev_notice(dev, "command 0x%04x did not complete!\n", cmd); -} - -/* Read a word from the EEPROM using the regular EEPROM access register. - Assume that we are in register window zero. - */ -static unsigned short read_eeprom(unsigned int ioaddr, int index) -{ - int timer; - outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd); - /* Pause for at least 162 usec for the read to take place. */ - for (timer = 1620; timer >= 0; timer--) { - if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0) - break; - } - return inw(ioaddr + Wn0EepromData); -} - -/* MII transceiver control section. - Read and write the MII registers using software-generated serial - MDIO protocol. See the MII specifications or DP83840A data sheet - for details. - The maxium data clock rate is 2.5 Mhz. The timing is easily met by the - slow PC card interface. */ - -#define MDIO_SHIFT_CLK 0x01 -#define MDIO_DIR_WRITE 0x04 -#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE) -#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE) -#define MDIO_DATA_READ 0x02 -#define MDIO_ENB_IN 0x00 - -/* Generate the preamble required for initial synchronization and - a few older transceivers. */ -static void mdio_sync(unsigned int ioaddr, int bits) -{ - unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt; - - /* Establish sync by sending at least 32 logic ones. */ - while (-- bits >= 0) { - outw(MDIO_DATA_WRITE1, mdio_addr); - outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - } -} - -static int mdio_read(unsigned int ioaddr, int phy_id, int location) -{ - int i; - int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; - unsigned int retval = 0; - unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt; - - if (mii_preamble_required) - mdio_sync(ioaddr, 32); - - /* Shift the read command bits out. */ - for (i = 14; i >= 0; i--) { - int dataval = (read_cmd&(1< 0; i--) { - outw(MDIO_ENB_IN, mdio_addr); - retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); - outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - } - return (retval>>1) & 0xffff; -} - -static void mdio_write(unsigned int ioaddr, int phy_id, int location, int value) -{ - int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; - unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt; - int i; - - if (mii_preamble_required) - mdio_sync(ioaddr, 32); - - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = (write_cmd&(1<= 0; i--) { - outw(MDIO_ENB_IN, mdio_addr); - outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - } -} - -/* Reset and restore all of the 3c574 registers. */ -static void tc574_reset(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - int i; - unsigned int ioaddr = dev->base_addr; - unsigned long flags; - - tc574_wait_for_completion(dev, TotalReset|0x10); - - spin_lock_irqsave(&lp->window_lock, flags); - /* Clear any transactions in progress. */ - outw(0, ioaddr + RunnerWrCtrl); - outw(0, ioaddr + RunnerRdCtrl); - - /* Set the station address and mask. */ - EL3WINDOW(2); - for (i = 0; i < 6; i++) - outb(dev->dev_addr[i], ioaddr + i); - for (; i < 12; i+=2) - outw(0, ioaddr + i); - - /* Reset config options */ - EL3WINDOW(3); - outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); - outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b, - ioaddr + Wn3_Config); - /* Roadrunner only: Turn on the MII transceiver. */ - outw(0x8040, ioaddr + Wn3_Options); - mdelay(1); - outw(0xc040, ioaddr + Wn3_Options); - EL3WINDOW(1); - spin_unlock_irqrestore(&lp->window_lock, flags); - - tc574_wait_for_completion(dev, TxReset); - tc574_wait_for_completion(dev, RxReset); - mdelay(1); - spin_lock_irqsave(&lp->window_lock, flags); - EL3WINDOW(3); - outw(0x8040, ioaddr + Wn3_Options); - - /* Switch to the stats window, and clear all stats by reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - EL3WINDOW(6); - for (i = 0; i < 10; i++) - inb(ioaddr + i); - inw(ioaddr + 10); - inw(ioaddr + 12); - EL3WINDOW(4); - inb(ioaddr + 12); - inb(ioaddr + 13); - - /* .. enable any extra statistics bits.. */ - outw(0x0040, ioaddr + Wn4_NetDiag); - - EL3WINDOW(1); - spin_unlock_irqrestore(&lp->window_lock, flags); - - /* .. re-sync MII and re-fill what NWay is advertising. */ - mdio_sync(ioaddr, 32); - mdio_write(ioaddr, lp->phys, 4, lp->advertising); - if (!auto_polarity) { - /* works for TDK 78Q2120 series MII's */ - i = mdio_read(ioaddr, lp->phys, 16) | 0x20; - mdio_write(ioaddr, lp->phys, 16, i); - } - - spin_lock_irqsave(&lp->window_lock, flags); - /* Switch to register set 1 for normal use, just for TxFree. */ - set_rx_mode(dev); - spin_unlock_irqrestore(&lp->window_lock, flags); - outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ - outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ - outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ - /* Allow status bits to be seen. */ - outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); - /* Ack all pending events, and set active indicator mask. */ - outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, - ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull - | AdapterFailure | RxEarly, ioaddr + EL3_CMD); -} - -static int el3_open(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - if (!pcmcia_dev_present(link)) - return -ENODEV; - - link->open++; - netif_start_queue(dev); - - tc574_reset(dev); - lp->media.expires = jiffies + HZ; - add_timer(&lp->media); - - dev_dbg(&link->dev, "%s: opened, status %4.4x.\n", - dev->name, inw(dev->base_addr + EL3_STATUS)); - - return 0; -} - -static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - unsigned int ioaddr = dev->base_addr; - - netdev_notice(dev, "Transmit timed out!\n"); - dump_status(dev); - dev->stats.tx_errors++; - netif_trans_update(dev); /* prevent tx timeout */ - /* Issue TX_RESET and TX_START commands. */ - tc574_wait_for_completion(dev, TxReset); - outw(TxEnable, ioaddr + EL3_CMD); - netif_wake_queue(dev); -} - -static void pop_tx_status(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - int i; - - /* Clear the Tx status stack. */ - for (i = 32; i > 0; i--) { - u_char tx_status = inb(ioaddr + TxStatus); - if (!(tx_status & 0x84)) - break; - /* reset transmitter on jabber error or underrun */ - if (tx_status & 0x30) - tc574_wait_for_completion(dev, TxReset); - if (tx_status & 0x38) { - pr_debug("%s: transmit error: status 0x%02x\n", - dev->name, tx_status); - outw(TxEnable, ioaddr + EL3_CMD); - dev->stats.tx_aborted_errors++; - } - outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */ - } -} - -static netdev_tx_t el3_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - struct el3_private *lp = netdev_priv(dev); - unsigned long flags; - - pr_debug("%s: el3_start_xmit(length = %ld) called, " - "status %4.4x.\n", dev->name, (long)skb->len, - inw(ioaddr + EL3_STATUS)); - - spin_lock_irqsave(&lp->window_lock, flags); - - dev->stats.tx_bytes += skb->len; - - /* Put out the doubleword header... */ - outw(skb->len, ioaddr + TX_FIFO); - outw(0, ioaddr + TX_FIFO); - /* ... and the packet rounded to a doubleword. */ - outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2); - - /* TxFree appears only in Window 1, not offset 0x1c. */ - if (inw(ioaddr + TxFree) <= 1536) { - netif_stop_queue(dev); - /* Interrupt us when the FIFO has room for max-sized packet. - The threshold is in units of dwords. */ - outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); - } - - pop_tx_status(dev); - spin_unlock_irqrestore(&lp->window_lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -/* The EL3 interrupt handler. */ -static irqreturn_t el3_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *) dev_id; - struct el3_private *lp = netdev_priv(dev); - unsigned int ioaddr; - unsigned status; - int work_budget = max_interrupt_work; - int handled = 0; - - if (!netif_device_present(dev)) - return IRQ_NONE; - ioaddr = dev->base_addr; - - pr_debug("%s: interrupt, status %4.4x.\n", - dev->name, inw(ioaddr + EL3_STATUS)); - - spin_lock(&lp->window_lock); - - while ((status = inw(ioaddr + EL3_STATUS)) & - (IntLatch | RxComplete | RxEarly | StatsFull)) { - if (!netif_device_present(dev) || - ((status & 0xe000) != 0x2000)) { - pr_debug("%s: Interrupt from dead card\n", dev->name); - break; - } - - handled = 1; - - if (status & RxComplete) - work_budget = el3_rx(dev, work_budget); - - if (status & TxAvailable) { - pr_debug(" TX room bit was handled.\n"); - /* There's room in the FIFO for a full-sized packet. */ - outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); - netif_wake_queue(dev); - } - - if (status & TxComplete) - pop_tx_status(dev); - - if (status & (AdapterFailure | RxEarly | StatsFull)) { - /* Handle all uncommon interrupts. */ - if (status & StatsFull) - update_stats(dev); - if (status & RxEarly) { - work_budget = el3_rx(dev, work_budget); - outw(AckIntr | RxEarly, ioaddr + EL3_CMD); - } - if (status & AdapterFailure) { - u16 fifo_diag; - EL3WINDOW(4); - fifo_diag = inw(ioaddr + Wn4_FIFODiag); - EL3WINDOW(1); - netdev_notice(dev, "adapter failure, FIFO diagnostic register %04x\n", - fifo_diag); - if (fifo_diag & 0x0400) { - /* Tx overrun */ - tc574_wait_for_completion(dev, TxReset); - outw(TxEnable, ioaddr + EL3_CMD); - } - if (fifo_diag & 0x2000) { - /* Rx underrun */ - tc574_wait_for_completion(dev, RxReset); - set_rx_mode(dev); - outw(RxEnable, ioaddr + EL3_CMD); - } - outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); - } - } - - if (--work_budget < 0) { - pr_debug("%s: Too much work in interrupt, " - "status %4.4x.\n", dev->name, status); - /* Clear all interrupts */ - outw(AckIntr | 0xFF, ioaddr + EL3_CMD); - break; - } - /* Acknowledge the IRQ. */ - outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); - } - - pr_debug("%s: exiting interrupt, status %4.4x.\n", - dev->name, inw(ioaddr + EL3_STATUS)); - - spin_unlock(&lp->window_lock); - return IRQ_RETVAL(handled); -} - -/* - This timer serves two purposes: to check for missed interrupts - (and as a last resort, poll the NIC for events), and to monitor - the MII, reporting changes in cable status. -*/ -static void media_check(struct timer_list *t) -{ - struct el3_private *lp = timer_container_of(lp, t, media); - struct net_device *dev = lp->p_dev->priv; - unsigned int ioaddr = dev->base_addr; - unsigned long flags; - unsigned short /* cable, */ media, partner; - - if (!netif_device_present(dev)) - goto reschedule; - - /* Check for pending interrupt with expired latency timer: with - this, we can limp along even if the interrupt is blocked */ - if ((inw(ioaddr + EL3_STATUS) & IntLatch) && (inb(ioaddr + Timer) == 0xff)) { - if (!lp->fast_poll) - netdev_info(dev, "interrupt(s) dropped!\n"); - - local_irq_save(flags); - el3_interrupt(dev->irq, dev); - local_irq_restore(flags); - - lp->fast_poll = HZ; - } - if (lp->fast_poll) { - lp->fast_poll--; - lp->media.expires = jiffies + 2*HZ/100; - add_timer(&lp->media); - return; - } - - spin_lock_irqsave(&lp->window_lock, flags); - EL3WINDOW(4); - media = mdio_read(ioaddr, lp->phys, 1); - partner = mdio_read(ioaddr, lp->phys, 5); - EL3WINDOW(1); - - if (media != lp->media_status) { - if ((media ^ lp->media_status) & 0x0004) - netdev_info(dev, "%s link beat\n", - (lp->media_status & 0x0004) ? "lost" : "found"); - if ((media ^ lp->media_status) & 0x0020) { - lp->partner = 0; - if (lp->media_status & 0x0020) { - netdev_info(dev, "autonegotiation restarted\n"); - } else if (partner) { - partner &= lp->advertising; - lp->partner = partner; - netdev_info(dev, "autonegotiation complete: " - "%dbaseT-%cD selected\n", - (partner & 0x0180) ? 100 : 10, - (partner & 0x0140) ? 'F' : 'H'); - } else { - netdev_info(dev, "link partner did not autonegotiate\n"); - } - - EL3WINDOW(3); - outb((partner & 0x0140 ? 0x20 : 0) | - (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); - EL3WINDOW(1); - - } - if (media & 0x0010) - netdev_info(dev, "remote fault detected\n"); - if (media & 0x0002) - netdev_info(dev, "jabber detected\n"); - lp->media_status = media; - } - spin_unlock_irqrestore(&lp->window_lock, flags); - -reschedule: - lp->media.expires = jiffies + HZ; - add_timer(&lp->media); -} - -static struct net_device_stats *el3_get_stats(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - - if (netif_device_present(dev)) { - unsigned long flags; - spin_lock_irqsave(&lp->window_lock, flags); - update_stats(dev); - spin_unlock_irqrestore(&lp->window_lock, flags); - } - return &dev->stats; -} - -/* Update statistics. - Surprisingly this need not be run single-threaded, but it effectively is. - The counters clear when read, so the adds must merely be atomic. - */ -static void update_stats(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - u8 up; - - pr_debug("%s: updating the statistics.\n", dev->name); - - if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */ - return; - - /* Unlike the 3c509 we need not turn off stats updates while reading. */ - /* Switch to the stats window, and read everything. */ - EL3WINDOW(6); - dev->stats.tx_carrier_errors += inb(ioaddr + 0); - dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); - /* Multiple collisions. */ inb(ioaddr + 2); - dev->stats.collisions += inb(ioaddr + 3); - dev->stats.tx_window_errors += inb(ioaddr + 4); - dev->stats.rx_fifo_errors += inb(ioaddr + 5); - dev->stats.tx_packets += inb(ioaddr + 6); - up = inb(ioaddr + 9); - dev->stats.tx_packets += (up&0x30) << 4; - /* Rx packets */ inb(ioaddr + 7); - /* Tx deferrals */ inb(ioaddr + 8); - /* rx */ inw(ioaddr + 10); - /* tx */ inw(ioaddr + 12); - - EL3WINDOW(4); - /* BadSSD */ inb(ioaddr + 12); - up = inb(ioaddr + 13); - - EL3WINDOW(1); -} - -static int el3_rx(struct net_device *dev, int worklimit) -{ - unsigned int ioaddr = dev->base_addr; - short rx_status; - - pr_debug("%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n", - dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); - while (!((rx_status = inw(ioaddr + RxStatus)) & 0x8000) && - worklimit > 0) { - worklimit--; - if (rx_status & 0x4000) { /* Error, update stats. */ - short error = rx_status & 0x3800; - dev->stats.rx_errors++; - switch (error) { - case 0x0000: dev->stats.rx_over_errors++; break; - case 0x0800: dev->stats.rx_length_errors++; break; - case 0x1000: dev->stats.rx_frame_errors++; break; - case 0x1800: dev->stats.rx_length_errors++; break; - case 0x2000: dev->stats.rx_frame_errors++; break; - case 0x2800: dev->stats.rx_crc_errors++; break; - } - } else { - short pkt_len = rx_status & 0x7ff; - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, pkt_len + 5); - - pr_debug(" Receiving packet size %d status %4.4x.\n", - pkt_len, rx_status); - if (skb != NULL) { - skb_reserve(skb, 2); - insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), - ((pkt_len+3)>>2)); - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } else { - pr_debug("%s: couldn't allocate a sk_buff of" - " size %d.\n", dev->name, pkt_len); - dev->stats.rx_dropped++; - } - } - tc574_wait_for_completion(dev, RxDiscard); - } - - return worklimit; -} - -/* Provide ioctl() calls to examine the MII xcvr state. */ -static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct el3_private *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - struct mii_ioctl_data *data = if_mii(rq); - int phy = lp->phys & 0x1f; - - pr_debug("%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n", - dev->name, rq->ifr_ifrn.ifrn_name, cmd, - data->phy_id, data->reg_num, data->val_in, data->val_out); - - switch(cmd) { - case SIOCGMIIPHY: /* Get the address of the PHY in use. */ - data->phy_id = phy; - fallthrough; - case SIOCGMIIREG: /* Read the specified MII register. */ - { - int saved_window; - unsigned long flags; - - spin_lock_irqsave(&lp->window_lock, flags); - saved_window = inw(ioaddr + EL3_CMD) >> 13; - EL3WINDOW(4); - data->val_out = mdio_read(ioaddr, data->phy_id & 0x1f, - data->reg_num & 0x1f); - EL3WINDOW(saved_window); - spin_unlock_irqrestore(&lp->window_lock, flags); - return 0; - } - case SIOCSMIIREG: /* Write the specified MII register */ - { - int saved_window; - unsigned long flags; - - spin_lock_irqsave(&lp->window_lock, flags); - saved_window = inw(ioaddr + EL3_CMD) >> 13; - EL3WINDOW(4); - mdio_write(ioaddr, data->phy_id & 0x1f, - data->reg_num & 0x1f, data->val_in); - EL3WINDOW(saved_window); - spin_unlock_irqrestore(&lp->window_lock, flags); - return 0; - } - default: - return -EOPNOTSUPP; - } -} - -/* The Odie chip has a 64 bin multicast filter, but the bit layout is not - documented. Until it is we revert to receiving all multicast frames when - any multicast reception is desired. - Note: My other drivers emit a log message whenever promiscuous mode is - entered to help detect password sniffers. This is less desirable on - typical PC card machines, so we omit the message. - */ - -static void set_rx_mode(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - - if (dev->flags & IFF_PROMISC) - outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, - ioaddr + EL3_CMD); - else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI)) - outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD); - else - outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); -} - -static void set_multicast_list(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - unsigned long flags; - - spin_lock_irqsave(&lp->window_lock, flags); - set_rx_mode(dev); - spin_unlock_irqrestore(&lp->window_lock, flags); -} - -static int el3_close(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - struct el3_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name); - - if (pcmcia_dev_present(link)) { - unsigned long flags; - - /* Turn off statistics ASAP. We update lp->stats below. */ - outw(StatsDisable, ioaddr + EL3_CMD); - - /* Disable the receiver and transmitter. */ - outw(RxDisable, ioaddr + EL3_CMD); - outw(TxDisable, ioaddr + EL3_CMD); - - /* Note: Switching to window 0 may disable the IRQ. */ - EL3WINDOW(0); - spin_lock_irqsave(&lp->window_lock, flags); - update_stats(dev); - spin_unlock_irqrestore(&lp->window_lock, flags); - - /* force interrupts off */ - outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); - } - - link->open--; - netif_stop_queue(dev); - timer_delete_sync(&lp->media); - - return 0; -} - -static const struct pcmcia_device_id tc574_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0574), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0556, "cis/3CCFEM556.cis"), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, tc574_ids); - -static struct pcmcia_driver tc574_driver = { - .owner = THIS_MODULE, - .name = "3c574_cs", - .probe = tc574_probe, - .remove = tc574_detach, - .id_table = tc574_ids, - .suspend = tc574_suspend, - .resume = tc574_resume, -}; -module_pcmcia_driver(tc574_driver); diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c deleted file mode 100644 index ea49be43b8c3..000000000000 --- a/drivers/net/ethernet/3com/3c589_cs.c +++ /dev/null @@ -1,974 +0,0 @@ -/* ====================================================================== - * - * A PCMCIA ethernet driver for the 3com 3c589 card. - * - * Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net - * - * 3c589_cs.c 1.162 2001/10/13 00:08:50 - * - * The network driver code is based on Donald Becker's 3c589 code: - * - * Written 1994 by Donald Becker. - * Copyright 1993 United States Government as represented by the - * Director, National Security Agency. This software may be used and - * distributed according to the terms of the GNU General Public License, - * incorporated herein by reference. - * Donald Becker may be reached at becker@scyld.com - * - * Updated for 2.5.x by Alan Cox - * - * ====================================================================== - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define DRV_NAME "3c589_cs" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -/* To minimize the size of the driver source I only define operating - * constants if they are used several times. You'll need the manual - * if you want to understand driver details. - */ - -/* Offsets from base I/O address. */ -#define EL3_DATA 0x00 -#define EL3_TIMER 0x0a -#define EL3_CMD 0x0e -#define EL3_STATUS 0x0e - -#define EEPROM_READ 0x0080 -#define EEPROM_BUSY 0x8000 - -#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) - -/* The top five bits written to EL3_CMD are a command, the lower - * 11 bits are the parameter, if applicable. - */ - -enum c509cmd { - TotalReset = 0<<11, - SelectWindow = 1<<11, - StartCoax = 2<<11, - RxDisable = 3<<11, - RxEnable = 4<<11, - RxReset = 5<<11, - RxDiscard = 8<<11, - TxEnable = 9<<11, - TxDisable = 10<<11, - TxReset = 11<<11, - FakeIntr = 12<<11, - AckIntr = 13<<11, - SetIntrEnb = 14<<11, - SetStatusEnb = 15<<11, - SetRxFilter = 16<<11, - SetRxThreshold = 17<<11, - SetTxThreshold = 18<<11, - SetTxStart = 19<<11, - StatsEnable = 21<<11, - StatsDisable = 22<<11, - StopCoax = 23<<11 -}; - -enum c509status { - IntLatch = 0x0001, - AdapterFailure = 0x0002, - TxComplete = 0x0004, - TxAvailable = 0x0008, - RxComplete = 0x0010, - RxEarly = 0x0020, - IntReq = 0x0040, - StatsFull = 0x0080, - CmdBusy = 0x1000 -}; - -/* The SetRxFilter command accepts the following classes: */ -enum RxFilter { - RxStation = 1, - RxMulticast = 2, - RxBroadcast = 4, - RxProm = 8 -}; - -/* Register window 1 offsets, the window used in normal operation. */ -#define TX_FIFO 0x00 -#define RX_FIFO 0x00 -#define RX_STATUS 0x08 -#define TX_STATUS 0x0B -#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */ - -#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */ -#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */ -#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ -#define MEDIA_LED 0x0001 /* Enable link light on 3C589E cards. */ - -/* Time in jiffies before concluding Tx hung */ -#define TX_TIMEOUT ((400*HZ)/1000) - -struct el3_private { - struct pcmcia_device *p_dev; - /* For transceiver monitoring */ - struct timer_list media; - u16 media_status; - u16 fast_poll; - unsigned long last_irq; - spinlock_t lock; -}; - -static const char *if_names[] = { "auto", "10baseT", "10base2", "AUI" }; - -/*====================================================================*/ - -/* Module parameters */ - -MODULE_AUTHOR("David Hinds "); -MODULE_DESCRIPTION("3Com 3c589 series PCMCIA ethernet driver"); -MODULE_LICENSE("GPL"); - -#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) - -/* Special hook for setting if_port when module is loaded */ -INT_MODULE_PARM(if_port, 0); - - -/*====================================================================*/ - -static int tc589_config(struct pcmcia_device *link); -static void tc589_release(struct pcmcia_device *link); - -static u16 read_eeprom(unsigned int ioaddr, int index); -static void tc589_reset(struct net_device *dev); -static void media_check(struct timer_list *t); -static int el3_config(struct net_device *dev, struct ifmap *map); -static int el3_open(struct net_device *dev); -static netdev_tx_t el3_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t el3_interrupt(int irq, void *dev_id); -static void update_stats(struct net_device *dev); -static struct net_device_stats *el3_get_stats(struct net_device *dev); -static int el3_rx(struct net_device *dev); -static int el3_close(struct net_device *dev); -static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue); -static void set_rx_mode(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static const struct ethtool_ops netdev_ethtool_ops; - -static void tc589_detach(struct pcmcia_device *p_dev); - -static const struct net_device_ops el3_netdev_ops = { - .ndo_open = el3_open, - .ndo_stop = el3_close, - .ndo_start_xmit = el3_start_xmit, - .ndo_tx_timeout = el3_tx_timeout, - .ndo_set_config = el3_config, - .ndo_get_stats = el3_get_stats, - .ndo_set_rx_mode = set_multicast_list, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int tc589_probe(struct pcmcia_device *link) -{ - struct el3_private *lp; - struct net_device *dev; - int ret; - - dev_dbg(&link->dev, "3c589_attach()\n"); - - /* Create new ethernet device */ - dev = alloc_etherdev(sizeof(struct el3_private)); - if (!dev) - return -ENOMEM; - lp = netdev_priv(dev); - link->priv = dev; - lp->p_dev = link; - - spin_lock_init(&lp->lock); - link->resource[0]->end = 16; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; - - link->config_flags |= CONF_ENABLE_IRQ; - link->config_index = 1; - - dev->netdev_ops = &el3_netdev_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - dev->ethtool_ops = &netdev_ethtool_ops; - - ret = tc589_config(link); - if (ret) - goto err_free_netdev; - - return 0; - -err_free_netdev: - free_netdev(dev); - return ret; -} - -static void tc589_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - dev_dbg(&link->dev, "3c589_detach\n"); - - unregister_netdev(dev); - - tc589_release(link); - - free_netdev(dev); -} /* tc589_detach */ - -static int tc589_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - int ret, i, j, multi = 0, fifo; - __be16 addr[ETH_ALEN / 2]; - unsigned int ioaddr; - static const char * const ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; - u8 *buf; - size_t len; - - dev_dbg(&link->dev, "3c589_config\n"); - - /* Is this a 3c562? */ - if (link->manf_id != MANFID_3COM) - dev_info(&link->dev, "hmmm, is this really a 3Com card??\n"); - multi = (link->card_id == PRODID_3COM_3C562); - - link->io_lines = 16; - - /* For the 3c562, the base address must be xx00-xx7f */ - for (i = j = 0; j < 0x400; j += 0x10) { - if (multi && (j & 0x80)) - continue; - link->resource[0]->start = j ^ 0x300; - i = pcmcia_request_io(link); - if (i == 0) - break; - } - if (i != 0) - goto failed; - - ret = pcmcia_request_irq(link, el3_interrupt); - if (ret) - goto failed; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - dev->irq = link->irq; - dev->base_addr = link->resource[0]->start; - ioaddr = dev->base_addr; - EL3WINDOW(0); - - /* The 3c589 has an extra EEPROM for configuration info, including - * the hardware address. The 3c562 puts the address in the CIS. - */ - len = pcmcia_get_tuple(link, 0x88, &buf); - if (buf && len >= 6) { - for (i = 0; i < 3; i++) - addr[i] = htons(le16_to_cpu(buf[i*2])); - kfree(buf); - } else { - kfree(buf); /* 0 < len < 6 */ - for (i = 0; i < 3; i++) - addr[i] = htons(read_eeprom(ioaddr, i)); - if (addr[0] == htons(0x6060)) { - dev_err(&link->dev, "IO port conflict at 0x%03lx-0x%03lx\n", - dev->base_addr, dev->base_addr+15); - goto failed; - } - } - eth_hw_addr_set(dev, (u8 *)addr); - - /* The address and resource configuration register aren't loaded from - * the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version. - */ - - outw(0x3f00, ioaddr + 8); - fifo = inl(ioaddr); - - /* The if_port symbol can be set when the module is loaded */ - if ((if_port >= 0) && (if_port <= 3)) - dev->if_port = if_port; - else - dev_err(&link->dev, "invalid if_port requested\n"); - - SET_NETDEV_DEV(dev, &link->dev); - - if (register_netdev(dev) != 0) { - dev_err(&link->dev, "register_netdev() failed\n"); - goto failed; - } - - netdev_info(dev, "3Com 3c%s, io %#3lx, irq %d, hw_addr %pM\n", - (multi ? "562" : "589"), dev->base_addr, dev->irq, - dev->dev_addr); - netdev_info(dev, " %dK FIFO split %s Rx:Tx, %s xcvr\n", - (fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3], - if_names[dev->if_port]); - return 0; - -failed: - tc589_release(link); - return -ENODEV; -} /* tc589_config */ - -static void tc589_release(struct pcmcia_device *link) -{ - pcmcia_disable_device(link); -} - -static int tc589_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int tc589_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) { - tc589_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - -/*====================================================================*/ - -/* Use this for commands that may take time to finish */ - -static void tc589_wait_for_completion(struct net_device *dev, int cmd) -{ - int i = 100; - outw(cmd, dev->base_addr + EL3_CMD); - while (--i > 0) - if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) - break; - if (i == 0) - netdev_warn(dev, "command 0x%04x did not complete!\n", cmd); -} - -/* Read a word from the EEPROM using the regular EEPROM access register. - * Assume that we are in register window zero. - */ - -static u16 read_eeprom(unsigned int ioaddr, int index) -{ - int i; - outw(EEPROM_READ + index, ioaddr + 10); - /* Reading the eeprom takes 162 us */ - for (i = 1620; i >= 0; i--) - if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0) - break; - return inw(ioaddr + 12); -} - -/* Set transceiver type, perhaps to something other than what the user - * specified in dev->if_port. - */ - -static void tc589_set_xcvr(struct net_device *dev, int if_port) -{ - struct el3_private *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - - EL3WINDOW(0); - switch (if_port) { - case 0: - case 1: - outw(0, ioaddr + 6); - break; - case 2: - outw(3<<14, ioaddr + 6); - break; - case 3: - outw(1<<14, ioaddr + 6); - break; - } - /* On PCMCIA, this just turns on the LED */ - outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD); - /* 10baseT interface, enable link beat and jabber check. */ - EL3WINDOW(4); - outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA); - EL3WINDOW(1); - if (if_port == 2) - lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000); - else - lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800); -} - -static void dump_status(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - EL3WINDOW(1); - netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x tx free %04x\n", - inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS), - inb(ioaddr+TX_STATUS), inw(ioaddr+TX_FREE)); - EL3WINDOW(4); - netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n", - inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08), - inw(ioaddr+0x0a)); - EL3WINDOW(1); -} - -/* Reset and restore all of the 3c589 registers. */ -static void tc589_reset(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - int i; - - EL3WINDOW(0); - outw(0x0001, ioaddr + 4); /* Activate board. */ - outw(0x3f00, ioaddr + 8); /* Set the IRQ line. */ - - /* Set the station address in window 2. */ - EL3WINDOW(2); - for (i = 0; i < 6; i++) - outb(dev->dev_addr[i], ioaddr + i); - - tc589_set_xcvr(dev, dev->if_port); - - /* Switch to the stats window, and clear all stats by reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - EL3WINDOW(6); - for (i = 0; i < 9; i++) - inb(ioaddr+i); - inw(ioaddr + 10); - inw(ioaddr + 12); - - /* Switch to register set 1 for normal use. */ - EL3WINDOW(1); - - set_rx_mode(dev); - outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ - outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ - outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ - /* Allow status bits to be seen. */ - outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); - /* Ack all pending events, and set active indicator mask. */ - outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, - ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull - | AdapterFailure, ioaddr + EL3_CMD); -} - -static void netdev_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - snprintf(info->bus_info, sizeof(info->bus_info), - "PCMCIA 0x%lx", dev->base_addr); -} - -static const struct ethtool_ops netdev_ethtool_ops = { - .get_drvinfo = netdev_get_drvinfo, -}; - -static int el3_config(struct net_device *dev, struct ifmap *map) -{ - if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { - if (map->port <= 3) { - WRITE_ONCE(dev->if_port, map->port); - netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]); - tc589_set_xcvr(dev, dev->if_port); - } else { - return -EINVAL; - } - } - return 0; -} - -static int el3_open(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - if (!pcmcia_dev_present(link)) - return -ENODEV; - - link->open++; - netif_start_queue(dev); - - tc589_reset(dev); - timer_setup(&lp->media, media_check, 0); - mod_timer(&lp->media, jiffies + HZ); - - dev_dbg(&link->dev, "%s: opened, status %4.4x.\n", - dev->name, inw(dev->base_addr + EL3_STATUS)); - - return 0; -} - -static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - unsigned int ioaddr = dev->base_addr; - - netdev_warn(dev, "Transmit timed out!\n"); - dump_status(dev); - dev->stats.tx_errors++; - netif_trans_update(dev); /* prevent tx timeout */ - /* Issue TX_RESET and TX_START commands. */ - tc589_wait_for_completion(dev, TxReset); - outw(TxEnable, ioaddr + EL3_CMD); - netif_wake_queue(dev); -} - -static void pop_tx_status(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - int i; - - /* Clear the Tx status stack. */ - for (i = 32; i > 0; i--) { - u_char tx_status = inb(ioaddr + TX_STATUS); - if (!(tx_status & 0x84)) - break; - /* reset transmitter on jabber error or underrun */ - if (tx_status & 0x30) - tc589_wait_for_completion(dev, TxReset); - if (tx_status & 0x38) { - netdev_dbg(dev, "transmit error: status 0x%02x\n", tx_status); - outw(TxEnable, ioaddr + EL3_CMD); - dev->stats.tx_aborted_errors++; - } - outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ - } -} - -static netdev_tx_t el3_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - struct el3_private *priv = netdev_priv(dev); - unsigned long flags; - - netdev_dbg(dev, "el3_start_xmit(length = %ld) called, status %4.4x.\n", - (long)skb->len, inw(ioaddr + EL3_STATUS)); - - spin_lock_irqsave(&priv->lock, flags); - - dev->stats.tx_bytes += skb->len; - - /* Put out the doubleword header... */ - outw(skb->len, ioaddr + TX_FIFO); - outw(0x00, ioaddr + TX_FIFO); - /* ... and the packet rounded to a doubleword. */ - outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); - - if (inw(ioaddr + TX_FREE) <= 1536) { - netif_stop_queue(dev); - /* Interrupt us when the FIFO has room for max-sized packet. */ - outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); - } - - pop_tx_status(dev); - spin_unlock_irqrestore(&priv->lock, flags); - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/* The EL3 interrupt handler. */ -static irqreturn_t el3_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *) dev_id; - struct el3_private *lp = netdev_priv(dev); - unsigned int ioaddr; - __u16 status; - int i = 0, handled = 1; - - if (!netif_device_present(dev)) - return IRQ_NONE; - - ioaddr = dev->base_addr; - - netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS)); - - spin_lock(&lp->lock); - while ((status = inw(ioaddr + EL3_STATUS)) & - (IntLatch | RxComplete | StatsFull)) { - if ((status & 0xe000) != 0x2000) { - netdev_dbg(dev, "interrupt from dead card\n"); - handled = 0; - break; - } - if (status & RxComplete) - el3_rx(dev); - if (status & TxAvailable) { - netdev_dbg(dev, " TX room bit was handled.\n"); - /* There's room in the FIFO for a full-sized packet. */ - outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); - netif_wake_queue(dev); - } - if (status & TxComplete) - pop_tx_status(dev); - if (status & (AdapterFailure | RxEarly | StatsFull)) { - /* Handle all uncommon interrupts. */ - if (status & StatsFull) /* Empty statistics. */ - update_stats(dev); - if (status & RxEarly) { - /* Rx early is unused. */ - el3_rx(dev); - outw(AckIntr | RxEarly, ioaddr + EL3_CMD); - } - if (status & AdapterFailure) { - u16 fifo_diag; - EL3WINDOW(4); - fifo_diag = inw(ioaddr + 4); - EL3WINDOW(1); - netdev_warn(dev, "adapter failure, FIFO diagnostic register %04x.\n", - fifo_diag); - if (fifo_diag & 0x0400) { - /* Tx overrun */ - tc589_wait_for_completion(dev, TxReset); - outw(TxEnable, ioaddr + EL3_CMD); - } - if (fifo_diag & 0x2000) { - /* Rx underrun */ - tc589_wait_for_completion(dev, RxReset); - set_rx_mode(dev); - outw(RxEnable, ioaddr + EL3_CMD); - } - outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); - } - } - if (++i > 10) { - netdev_err(dev, "infinite loop in interrupt, status %4.4x.\n", - status); - /* Clear all interrupts */ - outw(AckIntr | 0xFF, ioaddr + EL3_CMD); - break; - } - /* Acknowledge the IRQ. */ - outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); - } - lp->last_irq = jiffies; - spin_unlock(&lp->lock); - netdev_dbg(dev, "exiting interrupt, status %4.4x.\n", - inw(ioaddr + EL3_STATUS)); - return IRQ_RETVAL(handled); -} - -static void media_check(struct timer_list *t) -{ - struct el3_private *lp = timer_container_of(lp, t, media); - struct net_device *dev = lp->p_dev->priv; - unsigned int ioaddr = dev->base_addr; - u16 media, errs; - unsigned long flags; - - if (!netif_device_present(dev)) - goto reschedule; - - /* Check for pending interrupt with expired latency timer: with - * this, we can limp along even if the interrupt is blocked - */ - if ((inw(ioaddr + EL3_STATUS) & IntLatch) && - (inb(ioaddr + EL3_TIMER) == 0xff)) { - if (!lp->fast_poll) - netdev_warn(dev, "interrupt(s) dropped!\n"); - - local_irq_save(flags); - el3_interrupt(dev->irq, dev); - local_irq_restore(flags); - - lp->fast_poll = HZ; - } - if (lp->fast_poll) { - lp->fast_poll--; - lp->media.expires = jiffies + HZ/100; - add_timer(&lp->media); - return; - } - - /* lp->lock guards the EL3 window. Window should always be 1 except - * when the lock is held - */ - - spin_lock_irqsave(&lp->lock, flags); - EL3WINDOW(4); - media = inw(ioaddr+WN4_MEDIA) & 0xc810; - - /* Ignore collisions unless we've had no irq's recently */ - if (time_before(jiffies, lp->last_irq + HZ)) { - media &= ~0x0010; - } else { - /* Try harder to detect carrier errors */ - EL3WINDOW(6); - outw(StatsDisable, ioaddr + EL3_CMD); - errs = inb(ioaddr + 0); - outw(StatsEnable, ioaddr + EL3_CMD); - dev->stats.tx_carrier_errors += errs; - if (errs || (lp->media_status & 0x0010)) - media |= 0x0010; - } - - if (media != lp->media_status) { - if ((media & lp->media_status & 0x8000) && - ((lp->media_status ^ media) & 0x0800)) - netdev_info(dev, "%s link beat\n", - (lp->media_status & 0x0800 ? "lost" : "found")); - else if ((media & lp->media_status & 0x4000) && - ((lp->media_status ^ media) & 0x0010)) - netdev_info(dev, "coax cable %s\n", - (lp->media_status & 0x0010 ? "ok" : "problem")); - if (dev->if_port == 0) { - if (media & 0x8000) { - if (media & 0x0800) - netdev_info(dev, "flipped to 10baseT\n"); - else - tc589_set_xcvr(dev, 2); - } else if (media & 0x4000) { - if (media & 0x0010) - tc589_set_xcvr(dev, 1); - else - netdev_info(dev, "flipped to 10base2\n"); - } - } - lp->media_status = media; - } - - EL3WINDOW(1); - spin_unlock_irqrestore(&lp->lock, flags); - -reschedule: - lp->media.expires = jiffies + HZ; - add_timer(&lp->media); -} - -static struct net_device_stats *el3_get_stats(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - unsigned long flags; - struct pcmcia_device *link = lp->p_dev; - - if (pcmcia_dev_present(link)) { - spin_lock_irqsave(&lp->lock, flags); - update_stats(dev); - spin_unlock_irqrestore(&lp->lock, flags); - } - return &dev->stats; -} - -/* Update statistics. We change to register window 6, so this should be run -* single-threaded if the device is active. This is expected to be a rare -* operation, and it's simpler for the rest of the driver to assume that -* window 1 is always valid rather than use a special window-state variable. -* -* Caller must hold the lock for this -*/ - -static void update_stats(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - - netdev_dbg(dev, "updating the statistics.\n"); - /* Turn off statistics updates while reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - /* Switch to the stats window, and read everything. */ - EL3WINDOW(6); - dev->stats.tx_carrier_errors += inb(ioaddr + 0); - dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); - /* Multiple collisions. */ - inb(ioaddr + 2); - dev->stats.collisions += inb(ioaddr + 3); - dev->stats.tx_window_errors += inb(ioaddr + 4); - dev->stats.rx_fifo_errors += inb(ioaddr + 5); - dev->stats.tx_packets += inb(ioaddr + 6); - /* Rx packets */ - inb(ioaddr + 7); - /* Tx deferrals */ - inb(ioaddr + 8); - /* Rx octets */ - inw(ioaddr + 10); - /* Tx octets */ - inw(ioaddr + 12); - - /* Back to window 1, and turn statistics back on. */ - EL3WINDOW(1); - outw(StatsEnable, ioaddr + EL3_CMD); -} - -static int el3_rx(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - int worklimit = 32; - short rx_status; - - netdev_dbg(dev, "in rx_packet(), status %4.4x, rx_status %4.4x.\n", - inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); - while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) && - worklimit > 0) { - worklimit--; - if (rx_status & 0x4000) { /* Error, update stats. */ - short error = rx_status & 0x3800; - dev->stats.rx_errors++; - switch (error) { - case 0x0000: - dev->stats.rx_over_errors++; - break; - case 0x0800: - dev->stats.rx_length_errors++; - break; - case 0x1000: - dev->stats.rx_frame_errors++; - break; - case 0x1800: - dev->stats.rx_length_errors++; - break; - case 0x2000: - dev->stats.rx_frame_errors++; - break; - case 0x2800: - dev->stats.rx_crc_errors++; - break; - } - } else { - short pkt_len = rx_status & 0x7ff; - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, pkt_len + 5); - - netdev_dbg(dev, " Receiving packet size %d status %4.4x.\n", - pkt_len, rx_status); - if (skb != NULL) { - skb_reserve(skb, 2); - insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), - (pkt_len+3)>>2); - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } else { - netdev_dbg(dev, "couldn't allocate a sk_buff of size %d.\n", - pkt_len); - dev->stats.rx_dropped++; - } - } - /* Pop the top of the Rx FIFO */ - tc589_wait_for_completion(dev, RxDiscard); - } - if (worklimit == 0) - netdev_warn(dev, "too much work in el3_rx!\n"); - return 0; -} - -static void set_rx_mode(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - u16 opts = SetRxFilter | RxStation | RxBroadcast; - - if (dev->flags & IFF_PROMISC) - opts |= RxMulticast | RxProm; - else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI)) - opts |= RxMulticast; - outw(opts, ioaddr + EL3_CMD); -} - -static void set_multicast_list(struct net_device *dev) -{ - struct el3_private *priv = netdev_priv(dev); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - set_rx_mode(dev); - spin_unlock_irqrestore(&priv->lock, flags); -} - -static int el3_close(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - unsigned int ioaddr = dev->base_addr; - - dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name); - - if (pcmcia_dev_present(link)) { - /* Turn off statistics ASAP. We update dev->stats below. */ - outw(StatsDisable, ioaddr + EL3_CMD); - - /* Disable the receiver and transmitter. */ - outw(RxDisable, ioaddr + EL3_CMD); - outw(TxDisable, ioaddr + EL3_CMD); - - if (dev->if_port == 2) - /* Turn off thinnet power. Green! */ - outw(StopCoax, ioaddr + EL3_CMD); - else if (dev->if_port == 1) { - /* Disable link beat and jabber */ - EL3WINDOW(4); - outw(0, ioaddr + WN4_MEDIA); - } - - /* Switching back to window 0 disables the IRQ. */ - EL3WINDOW(0); - /* But we explicitly zero the IRQ line select anyway. */ - outw(0x0f00, ioaddr + WN0_IRQ); - - /* Check if the card still exists */ - if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000) - update_stats(dev); - } - - link->open--; - netif_stop_queue(dev); - timer_delete_sync(&lp->media); - - return 0; -} - -static const struct pcmcia_device_id tc589_ids[] = { - PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0101, 0x0562), - PCMCIA_MFC_DEVICE_PROD_ID1(0, "Motorola MARQUIS", 0xf03e4e77), - PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0589), - PCMCIA_DEVICE_PROD_ID12("Farallon", "ENet", 0x58d93fc4, 0x992c2202), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0035, "cis/3CXEM556.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x003d, "cis/3CXEM556.cis"), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, tc589_ids); - -static struct pcmcia_driver tc589_driver = { - .owner = THIS_MODULE, - .name = "3c589_cs", - .probe = tc589_probe, - .remove = tc589_detach, - .id_table = tc589_ids, - .suspend = tc589_suspend, - .resume = tc589_resume, -}; -module_pcmcia_driver(tc589_driver); diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig index 1fbab79e2be4..399cb6c56198 100644 --- a/drivers/net/ethernet/3com/Kconfig +++ b/drivers/net/ethernet/3com/Kconfig @@ -17,51 +17,6 @@ config NET_VENDOR_3COM if NET_VENDOR_3COM -config EL3 - tristate "3c509/3c579 \"EtherLink III\" support" - depends on (ISA || EISA) - help - If you have a network (Ethernet) card belonging to the 3Com - EtherLinkIII series, say Y here. - - If your card is not working you may need to use the DOS - setup disk to disable Plug & Play mode, and to select the default - media type. - - To compile this driver as a module, choose M here. The module - will be called 3c509. - -config 3C515 - tristate "3c515 ISA \"Fast EtherLink\"" - depends on ISA && ISA_DMA_API && !PPC32 - select NETDEV_LEGACY_INIT - help - If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet - network card, say Y here. - - To compile this driver as a module, choose M here. The module - will be called 3c515. - -config PCMCIA_3C574 - tristate "3Com 3c574 PCMCIA support" - depends on PCMCIA && HAS_IOPORT - help - Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA - (PC-card) Fast Ethernet card to your computer. - - To compile this driver as a module, choose M here: the module will be - called 3c574_cs. If unsure, say N. - -config PCMCIA_3C589 - tristate "3Com 3c589 PCMCIA support" - depends on PCMCIA && HAS_IOPORT - help - Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA - (PC-card) Ethernet card to your computer. - - To compile this driver as a module, choose M here: the module will be - called 3c589_cs. If unsure, say N. - config VORTEX tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support" depends on (PCI || EISA) && HAS_IOPORT_MAP diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile index f8b73babc510..5c4d07f1d456 100644 --- a/drivers/net/ethernet/3com/Makefile +++ b/drivers/net/ethernet/3com/Makefile @@ -3,9 +3,5 @@ # Makefile for the 3Com Ethernet device drivers # -obj-$(CONFIG_EL3) += 3c509.o -obj-$(CONFIG_3C515) += 3c515.o -obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o -obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o obj-$(CONFIG_VORTEX) += 3c59x.o obj-$(CONFIG_TYPHOON) += typhoon.o diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig index 345f250781c6..5d12a595ab19 100644 --- a/drivers/net/ethernet/8390/Kconfig +++ b/drivers/net/ethernet/8390/Kconfig @@ -17,18 +17,6 @@ config NET_VENDOR_8390 if NET_VENDOR_8390 -config PCMCIA_AXNET - tristate "Asix AX88190 PCMCIA support" - depends on PCMCIA && HAS_IOPORT - help - Say Y here if you intend to attach an Asix AX88190-based PCMCIA - (PC-card) Fast Ethernet card to your computer. These cards are - nearly NE2000 compatible but need a separate driver due to a few - misfeatures. - - To compile this driver as a module, choose M here: the module will be - called axnet_cs. If unsure, say N. - config AX88796 tristate "ASIX AX88796 NE2000 clone support" if !ZORRO depends on (ARM || MIPS || SUPERH || ZORRO || COMPILE_TEST) @@ -167,35 +155,6 @@ config STNIC If unsure, say N. -config ULTRA - tristate "SMC Ultra support" - depends on ISA - select NETDEV_LEGACY_INIT - select CRC32 - help - If you have a network (Ethernet) card of this type, say Y here. - - Important: There have been many reports that, with some motherboards - mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible, - such as some BusLogic models) causes corruption problems with many - operating systems. The Linux smc-ultra driver has a work-around for - this but keep it in mind if you have such a SCSI card and have - problems. - - To compile this driver as a module, choose M here. The module - will be called smc-ultra. - -config WD80x3 - tristate "WD80*3 support" - depends on ISA - select NETDEV_LEGACY_INIT - select CRC32 - help - If you have a network (Ethernet) card of this type, say Y here. - - To compile this driver as a module, choose M here. The module - will be called wd. - config ZORRO8390 tristate "Zorro NS8390-based Ethernet support" depends on ZORRO diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile index 85c83c566ec6..bca5babdadc7 100644 --- a/drivers/net/ethernet/8390/Makefile +++ b/drivers/net/ethernet/8390/Makefile @@ -11,10 +11,7 @@ obj-$(CONFIG_HYDRA) += hydra.o obj-$(CONFIG_MCF8390) += mcf8390.o obj-$(CONFIG_NE2000) += ne.o 8390p.o obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o -obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o 8390.o obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o obj-$(CONFIG_STNIC) += stnic.o 8390.o -obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o -obj-$(CONFIG_WD80x3) += wd.o 8390.o obj-$(CONFIG_XSURF100) += xsurf100.o obj-$(CONFIG_ZORRO8390) += zorro8390.o diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c deleted file mode 100644 index 7c8213011b5c..000000000000 --- a/drivers/net/ethernet/8390/axnet_cs.c +++ /dev/null @@ -1,1707 +0,0 @@ -// SPDX-License-Identifier: GPL-1.0+ - -/*====================================================================== - - A PCMCIA ethernet driver for Asix AX88190-based cards - - The Asix AX88190 is a NS8390-derived chipset with a few nasty - idiosyncracies that make it very inconvenient to support with a - standard 8390 driver. This driver is based on pcnet_cs, with the - tweaked 8390 code grafted on the end. Much of what I did was to - clean up and update a similar driver supplied by Asix, which was - adapted by William Lee, william@asix.com.tw. - - Copyright (C) 2001 David A. Hinds -- dahinds@users.sourceforge.net - - axnet_cs.c 1.28 2002/06/29 06:27:37 - - The network driver code is based on Donald Becker's NE2000 code: - - Written 1992,1993 by Donald Becker. - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - Donald Becker may be reached at becker@scyld.com - -======================================================================*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "8390.h" - -#include -#include -#include -#include - -#include -#include -#include - -#define AXNET_CMD 0x00 -#define AXNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */ -#define AXNET_RESET 0x1f /* Issue a read to reset, a write to clear. */ -#define AXNET_MII_EEP 0x14 /* Offset of MII access port */ -#define AXNET_TEST 0x15 /* Offset of TEST Register port */ -#define AXNET_GPIO 0x17 /* Offset of General Purpose Register Port */ - -#define AXNET_START_PG 0x40 /* First page of TX buffer */ -#define AXNET_STOP_PG 0x80 /* Last page +1 of RX ring */ - -#define AXNET_RDC_TIMEOUT 0x02 /* Max wait in jiffies for Tx RDC */ - -#define IS_AX88190 0x0001 -#define IS_AX88790 0x0002 - -/*====================================================================*/ - -/* Module parameters */ - -MODULE_AUTHOR("David Hinds "); -MODULE_DESCRIPTION("Asix AX88190 PCMCIA ethernet driver"); -MODULE_LICENSE("GPL"); - - -/*====================================================================*/ - -static int axnet_config(struct pcmcia_device *link); -static void axnet_release(struct pcmcia_device *link); -static int axnet_open(struct net_device *dev); -static int axnet_close(struct net_device *dev); -static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static netdev_tx_t axnet_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static struct net_device_stats *get_stats(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static void axnet_tx_timeout(struct net_device *dev, unsigned int txqueue); -static irqreturn_t ei_irq_wrapper(int irq, void *dev_id); -static void ei_watchdog(struct timer_list *t); -static void axnet_reset_8390(struct net_device *dev); - -static int mdio_read(unsigned int addr, int phy_id, int loc); -static void mdio_write(unsigned int addr, int phy_id, int loc, int value); - -static void get_8390_hdr(struct net_device *, - struct e8390_pkt_hdr *, int); -static void block_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset); -static void block_output(struct net_device *dev, int count, - const u_char *buf, const int start_page); - -static void axnet_detach(struct pcmcia_device *p_dev); - -static void AX88190_init(struct net_device *dev, int startp); -static int ax_open(struct net_device *dev); -static int ax_close(struct net_device *dev); -static irqreturn_t ax_interrupt(int irq, void *dev_id); - -/*====================================================================*/ - -struct axnet_dev { - struct pcmcia_device *p_dev; - caddr_t base; - struct timer_list watchdog; - int stale, fast_poll; - u_short link_status; - u_char duplex_flag; - int phy_id; - int flags; - int active_low; -}; - -static inline struct axnet_dev *PRIV(struct net_device *dev) -{ - void *p = (char *)netdev_priv(dev) + sizeof(struct ei_device); - return p; -} - -static const struct net_device_ops axnet_netdev_ops = { - .ndo_open = axnet_open, - .ndo_stop = axnet_close, - .ndo_eth_ioctl = axnet_ioctl, - .ndo_start_xmit = axnet_start_xmit, - .ndo_tx_timeout = axnet_tx_timeout, - .ndo_get_stats = get_stats, - .ndo_set_rx_mode = set_multicast_list, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int axnet_probe(struct pcmcia_device *link) -{ - struct axnet_dev *info; - struct net_device *dev; - struct ei_device *ei_local; - - dev_dbg(&link->dev, "axnet_attach()\n"); - - dev = alloc_etherdev(sizeof(struct ei_device) + sizeof(struct axnet_dev)); - if (!dev) - return -ENOMEM; - - ei_local = netdev_priv(dev); - spin_lock_init(&ei_local->page_lock); - - info = PRIV(dev); - info->p_dev = link; - link->priv = dev; - link->config_flags |= CONF_ENABLE_IRQ; - - dev->netdev_ops = &axnet_netdev_ops; - - dev->watchdog_timeo = TX_TIMEOUT; - - return axnet_config(link); -} /* axnet_attach */ - -static void axnet_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - dev_dbg(&link->dev, "axnet_detach(0x%p)\n", link); - - unregister_netdev(dev); - - axnet_release(link); - - free_netdev(dev); -} /* axnet_detach */ - -/*====================================================================== - - This probes for a card's hardware address by reading the PROM. - -======================================================================*/ - -static int get_prom(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - unsigned int ioaddr = dev->base_addr; - u8 addr[ETH_ALEN]; - int i, j; - - /* This is based on drivers/net/ethernet/8390/ne.c */ - struct { - u_char value, offset; - } program_seq[] = { - {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ - {0x01, EN0_DCFG}, /* Set word-wide access. */ - {0x00, EN0_RCNTLO}, /* Clear the count regs. */ - {0x00, EN0_RCNTHI}, - {0x00, EN0_IMR}, /* Mask completion irq. */ - {0xFF, EN0_ISR}, - {E8390_RXOFF|0x40, EN0_RXCR}, /* 0x60 Set to monitor */ - {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ - {0x10, EN0_RCNTLO}, - {0x00, EN0_RCNTHI}, - {0x00, EN0_RSARLO}, /* DMA starting at 0x0400. */ - {0x04, EN0_RSARHI}, - {E8390_RREAD+E8390_START, E8390_CMD}, - }; - - /* Not much of a test, but the alternatives are messy */ - if (link->config_base != 0x03c0) - return 0; - - axnet_reset_8390(dev); - mdelay(10); - - for (i = 0; i < ARRAY_SIZE(program_seq); i++) - outb_p(program_seq[i].value, ioaddr + program_seq[i].offset); - - for (i = 0; i < 6; i += 2) { - j = inw(ioaddr + AXNET_DATAPORT); - addr[i] = j & 0xff; - addr[i+1] = j >> 8; - } - eth_hw_addr_set(dev, addr); - - return 1; -} /* get_prom */ - -static int try_io_port(struct pcmcia_device *link) -{ - int j, ret; - link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; - if (link->resource[0]->end == 32) { - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - /* for master/slave multifunction cards */ - if (link->resource[1]->end > 0) - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - } else { - /* This should be two 16-port windows */ - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16; - } - if (link->resource[0]->start == 0) { - for (j = 0; j < 0x400; j += 0x20) { - link->resource[0]->start = j ^ 0x300; - link->resource[1]->start = (j ^ 0x300) + 0x10; - link->io_lines = 16; - ret = pcmcia_request_io(link); - if (ret == 0) - return ret; - } - return ret; - } else { - return pcmcia_request_io(link); - } -} - -static int axnet_configcheck(struct pcmcia_device *p_dev, void *priv_data) -{ - if (p_dev->config_index == 0) - return -EINVAL; - - p_dev->config_index = 0x05; - if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32) - return -ENODEV; - - return try_io_port(p_dev); -} - -static int axnet_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct axnet_dev *info = PRIV(dev); - int i, j, j2, ret; - - dev_dbg(&link->dev, "axnet_config(0x%p)\n", link); - - /* don't trust the CIS on this; Linksys got it wrong */ - link->config_regs = 0x63; - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - ret = pcmcia_loop_config(link, axnet_configcheck, NULL); - if (ret != 0) - goto failed; - - if (!link->irq) - goto failed; - - if (resource_size(link->resource[1]) == 8) - link->config_flags |= CONF_ENABLE_SPKR; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - dev->irq = link->irq; - dev->base_addr = link->resource[0]->start; - - if (!get_prom(link)) { - pr_notice("this is not an AX88190 card!\n"); - pr_notice("use pcnet_cs instead.\n"); - goto failed; - } - - ei_status.name = "AX88190"; - ei_status.word16 = 1; - ei_status.tx_start_page = AXNET_START_PG; - ei_status.rx_start_page = AXNET_START_PG + TX_PAGES; - ei_status.stop_page = AXNET_STOP_PG; - ei_status.reset_8390 = axnet_reset_8390; - ei_status.get_8390_hdr = get_8390_hdr; - ei_status.block_input = block_input; - ei_status.block_output = block_output; - - if (inb(dev->base_addr + AXNET_TEST) != 0) - info->flags |= IS_AX88790; - else - info->flags |= IS_AX88190; - - if (info->flags & IS_AX88790) - outb(0x10, dev->base_addr + AXNET_GPIO); /* select Internal PHY */ - - info->active_low = 0; - - for (i = 0; i < 32; i++) { - j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1); - j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2); - if (j == j2) continue; - if ((j != 0) && (j != 0xffff)) break; - } - - if (i == 32) { - /* Maybe PHY is in power down mode. (PPD_SET = 1) - Bit 2 of CCSR is active low. */ - pcmcia_write_config_byte(link, CISREG_CCSR, 0x04); - for (i = 0; i < 32; i++) { - j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1); - j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2); - if (j == j2) continue; - if ((j != 0) && (j != 0xffff)) { - info->active_low = 1; - break; - } - } - } - - info->phy_id = (i < 32) ? i : -1; - SET_NETDEV_DEV(dev, &link->dev); - - if (register_netdev(dev) != 0) { - pr_notice("register_netdev() failed\n"); - goto failed; - } - - netdev_info(dev, "Asix AX88%d90: io %#3lx, irq %d, hw_addr %pM\n", - ((info->flags & IS_AX88790) ? 7 : 1), - dev->base_addr, dev->irq, dev->dev_addr); - if (info->phy_id != -1) { - netdev_dbg(dev, " MII transceiver at index %d, status %x\n", - info->phy_id, j); - } else { - netdev_notice(dev, " No MII transceivers found!\n"); - } - return 0; - -failed: - axnet_release(link); - return -ENODEV; -} /* axnet_config */ - -static void axnet_release(struct pcmcia_device *link) -{ - pcmcia_disable_device(link); -} - -static int axnet_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int axnet_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct axnet_dev *info = PRIV(dev); - - if (link->open) { - if (info->active_low == 1) - pcmcia_write_config_byte(link, CISREG_CCSR, 0x04); - - axnet_reset_8390(dev); - AX88190_init(dev, 1); - netif_device_attach(dev); - } - - return 0; -} - - -/*====================================================================== - - MII interface support - -======================================================================*/ - -#define MDIO_SHIFT_CLK 0x01 -#define MDIO_DATA_WRITE0 0x00 -#define MDIO_DATA_WRITE1 0x08 -#define MDIO_DATA_READ 0x04 -#define MDIO_MASK 0x0f -#define MDIO_ENB_IN 0x02 - -static void mdio_sync(unsigned int addr) -{ - int bits; - for (bits = 0; bits < 32; bits++) { - outb_p(MDIO_DATA_WRITE1, addr); - outb_p(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr); - } -} - -static int mdio_read(unsigned int addr, int phy_id, int loc) -{ - u_int cmd = (0xf6<<10)|(phy_id<<5)|loc; - int i, retval = 0; - - mdio_sync(addr); - for (i = 14; i >= 0; i--) { - int dat = (cmd&(1< 0; i--) { - outb_p(MDIO_ENB_IN, addr); - retval = (retval << 1) | ((inb_p(addr) & MDIO_DATA_READ) != 0); - outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr); - } - return (retval>>1) & 0xffff; -} - -static void mdio_write(unsigned int addr, int phy_id, int loc, int value) -{ - u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value; - int i; - - mdio_sync(addr); - for (i = 31; i >= 0; i--) { - int dat = (cmd&(1<= 0; i--) { - outb_p(MDIO_ENB_IN, addr); - outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr); - } -} - -/*====================================================================*/ - -static int axnet_open(struct net_device *dev) -{ - int ret; - struct axnet_dev *info = PRIV(dev); - struct pcmcia_device *link = info->p_dev; - unsigned int nic_base = dev->base_addr; - - dev_dbg(&link->dev, "axnet_open('%s')\n", dev->name); - - if (!pcmcia_dev_present(link)) - return -ENODEV; - - outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */ - ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, "axnet_cs", dev); - if (ret) - return ret; - - link->open++; - - info->link_status = 0x00; - timer_setup(&info->watchdog, ei_watchdog, 0); - mod_timer(&info->watchdog, jiffies + HZ); - - return ax_open(dev); -} /* axnet_open */ - -/*====================================================================*/ - -static int axnet_close(struct net_device *dev) -{ - struct axnet_dev *info = PRIV(dev); - struct pcmcia_device *link = info->p_dev; - - dev_dbg(&link->dev, "axnet_close('%s')\n", dev->name); - - ax_close(dev); - free_irq(dev->irq, dev); - - link->open--; - netif_stop_queue(dev); - timer_delete_sync(&info->watchdog); - - return 0; -} /* axnet_close */ - -/*====================================================================== - - Hard reset the card. This used to pause for the same period that - a 8390 reset command required, but that shouldn't be necessary. - -======================================================================*/ - -static void axnet_reset_8390(struct net_device *dev) -{ - unsigned int nic_base = dev->base_addr; - int i; - - ei_status.txing = ei_status.dmaing = 0; - - outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD); - - outb(inb(nic_base + AXNET_RESET), nic_base + AXNET_RESET); - - for (i = 0; i < 100; i++) { - if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0) - break; - udelay(100); - } - outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */ - - if (i == 100) - netdev_err(dev, "axnet_reset_8390() did not complete\n"); - -} /* axnet_reset_8390 */ - -/*====================================================================*/ - -static irqreturn_t ei_irq_wrapper(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - PRIV(dev)->stale = 0; - return ax_interrupt(irq, dev_id); -} - -static void ei_watchdog(struct timer_list *t) -{ - struct axnet_dev *info = timer_container_of(info, t, watchdog); - struct net_device *dev = info->p_dev->priv; - unsigned int nic_base = dev->base_addr; - unsigned int mii_addr = nic_base + AXNET_MII_EEP; - u_short link; - - if (!netif_device_present(dev)) goto reschedule; - - /* Check for pending interrupt with expired latency timer: with - this, we can limp along even if the interrupt is blocked */ - if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) { - if (!info->fast_poll) - netdev_info(dev, "interrupt(s) dropped!\n"); - ei_irq_wrapper(dev->irq, dev); - info->fast_poll = HZ; - } - if (info->fast_poll) { - info->fast_poll--; - info->watchdog.expires = jiffies + 1; - add_timer(&info->watchdog); - return; - } - - if (info->phy_id < 0) - goto reschedule; - link = mdio_read(mii_addr, info->phy_id, 1); - if (!link || (link == 0xffff)) { - netdev_info(dev, "MII is missing!\n"); - info->phy_id = -1; - goto reschedule; - } - - link &= 0x0004; - if (link != info->link_status) { - u_short p = mdio_read(mii_addr, info->phy_id, 5); - netdev_info(dev, "%s link beat\n", link ? "found" : "lost"); - if (link) { - info->duplex_flag = (p & 0x0140) ? 0x80 : 0x00; - if (p) - netdev_info(dev, "autonegotiation complete: %dbaseT-%cD selected\n", - (p & 0x0180) ? 100 : 10, (p & 0x0140) ? 'F' : 'H'); - else - netdev_info(dev, "link partner did not autonegotiate\n"); - AX88190_init(dev, 1); - } - info->link_status = link; - } - -reschedule: - info->watchdog.expires = jiffies + HZ; - add_timer(&info->watchdog); -} - -/*====================================================================*/ - -static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct axnet_dev *info = PRIV(dev); - struct mii_ioctl_data *data = if_mii(rq); - unsigned int mii_addr = dev->base_addr + AXNET_MII_EEP; - switch (cmd) { - case SIOCGMIIPHY: - data->phy_id = info->phy_id; - fallthrough; - case SIOCGMIIREG: /* Read MII PHY register. */ - data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f); - return 0; - case SIOCSMIIREG: /* Write MII PHY register. */ - mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in); - return 0; - } - return -EOPNOTSUPP; -} - -/*====================================================================*/ - -static void get_8390_hdr(struct net_device *dev, - struct e8390_pkt_hdr *hdr, - int ring_page) -{ - unsigned int nic_base = dev->base_addr; - - outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */ - outb_p(ring_page, nic_base + EN0_RSARHI); - outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD); - - insw(nic_base + AXNET_DATAPORT, hdr, - sizeof(struct e8390_pkt_hdr)>>1); - /* Fix for big endian systems */ - hdr->count = le16_to_cpu(hdr->count); - -} - -/*====================================================================*/ - -static void block_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset) -{ - unsigned int nic_base = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - char *buf = skb->data; - - if ((netif_msg_rx_status(ei_local)) && (count != 4)) - netdev_dbg(dev, "[bi=%d]\n", count+4); - outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO); - outb_p(ring_offset >> 8, nic_base + EN0_RSARHI); - outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD); - - insw(nic_base + AXNET_DATAPORT,buf,count>>1); - if (count & 0x01) { - buf[count-1] = inb(nic_base + AXNET_DATAPORT); - } -} - -/*====================================================================*/ - -static void block_output(struct net_device *dev, int count, - const u_char *buf, const int start_page) -{ - unsigned int nic_base = dev->base_addr; - - pr_debug("%s: [bo=%d]\n", dev->name, count); - - /* Round the count up for word writes. Do we need to do this? - What effect will an odd byte count have on the 8390? - I should check someday. */ - if (count & 0x01) - count++; - - outb_p(0x00, nic_base + EN0_RSARLO); - outb_p(start_page, nic_base + EN0_RSARHI); - outb_p(E8390_RWRITE+E8390_START, nic_base + AXNET_CMD); - outsw(nic_base + AXNET_DATAPORT, buf, count>>1); -} - -static const struct pcmcia_device_id axnet_ids[] = { - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x016c, 0x0081), - PCMCIA_DEVICE_MANF_CARD(0x018a, 0x0301), - PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x2328), - PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0301), - PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0303), - PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0309), - PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1106), - PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), - PCMCIA_DEVICE_MANF_CARD(0x021b, 0x0202), - PCMCIA_DEVICE_MANF_CARD(0xffff, 0x1090), - PCMCIA_DEVICE_PROD_ID12("AmbiCom,Inc.", "Fast Ethernet PC Card(AMB8110)", 0x49b020a7, 0x119cc9fc), - PCMCIA_DEVICE_PROD_ID124("Fast Ethernet", "16-bit PC Card", "AX88190", 0xb4be14e3, 0x9a12eb6a, 0xab9be5ef), - PCMCIA_DEVICE_PROD_ID12("ASIX", "AX88190", 0x0959823b, 0xab9be5ef), - PCMCIA_DEVICE_PROD_ID12("Billionton", "LNA-100B", 0x552ab682, 0xbc3b87e1), - PCMCIA_DEVICE_PROD_ID12("CHEETAH ETHERCARD", "EN2228", 0x00fa7bc8, 0x00e990cc), - PCMCIA_DEVICE_PROD_ID12("CNet", "CNF301", 0xbc477dde, 0x78c5f40b), - PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXD", 0x5261440f, 0x436768c5), - PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEtherII PCC-TXD", 0x5261440f, 0x730df72e), - PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXM", 0x5261440f, 0x3abbd061), - PCMCIA_DEVICE_PROD_ID12("Dynalink", "L100C16", 0x55632fd5, 0x66bc2a90), - PCMCIA_DEVICE_PROD_ID12("IO DATA", "ETXPCM", 0x547e66dc, 0x233adac2), - PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V3)", 0x0733cc81, 0x232019a8), - PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC3-TX", 0x481e0094, 0xf91af609), - PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA411", 0x9aa79dc3, 0x40fad875), - PCMCIA_DEVICE_PROD_ID12("PCMCIA", "100BASE", 0x281f1c5d, 0x7c2add04), - PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEtherCard", 0x281f1c5d, 0x7ef26116), - PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FEP501", 0x281f1c5d, 0x2e272058), - PCMCIA_DEVICE_PROD_ID14("Network Everywhere", "AX88190", 0x820a67b6, 0xab9be5ef), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, axnet_ids); - -static struct pcmcia_driver axnet_cs_driver = { - .owner = THIS_MODULE, - .name = "axnet_cs", - .probe = axnet_probe, - .remove = axnet_detach, - .id_table = axnet_ids, - .suspend = axnet_suspend, - .resume = axnet_resume, -}; -module_pcmcia_driver(axnet_cs_driver); - -/*====================================================================*/ - -/* 8390.c: A general NS8390 ethernet driver core for linux. */ -/* - Written 1992-94 by Donald Becker. - - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - This is the chip-specific code for many 8390-based ethernet adaptors. - This is not a complete driver, it must be combined with board-specific - code such as ne.c, wd.c, 3c503.c, etc. - - Seeing how at least eight drivers use this code, (not counting the - PCMCIA ones either) it is easy to break some card by what seems like - a simple innocent change. Please contact me or Donald if you think - you have found something that needs changing. -- PG - - Changelog: - - Paul Gortmaker : remove set_bit lock, other cleanups. - Paul Gortmaker : add ei_get_8390_hdr() so we can pass skb's to - ei_block_input() for eth_io_copy_and_sum(). - Paul Gortmaker : exchange static int ei_pingpong for a #define, - also add better Tx error handling. - Paul Gortmaker : rewrite Rx overrun handling as per NS specs. - Alexey Kuznetsov : use the 8390's six bit hash multicast filter. - Paul Gortmaker : tweak ANK's above multicast changes a bit. - Paul Gortmaker : update packet statistics for v2.1.x - Alan Cox : support arbitrary stupid port mappings on the - 68K Macintosh. Support >16bit I/O spaces - Paul Gortmaker : add kmod support for auto-loading of the 8390 - module by all drivers that require it. - Alan Cox : Spinlocking work, added 'BUG_83C690' - Paul Gortmaker : Separate out Tx timeout code from Tx path. - - Sources: - The National Semiconductor LAN Databook, and the 3Com 3c503 databook. - - */ - -#include -#include -#include -#include -#include - -#define BUG_83C690 - -/* These are the operational function interfaces to board-specific - routines. - void reset_8390(struct net_device *dev) - Resets the board associated with DEV, including a hardware reset of - the 8390. This is only called when there is a transmit timeout, and - it is always followed by 8390_init(). - void block_output(struct net_device *dev, int count, const unsigned char *buf, - int start_page) - Write the COUNT bytes of BUF to the packet buffer at START_PAGE. The - "page" value uses the 8390's 256-byte pages. - void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page) - Read the 4 byte, page aligned 8390 header. *If* there is a - subsequent read, it will be of the rest of the packet. - void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) - Read COUNT bytes from the packet buffer into the skb data area. Start - reading from RING_OFFSET, the address as the 8390 sees it. This will always - follow the read of the 8390 header. -*/ -#define ei_reset_8390 (ei_local->reset_8390) -#define ei_block_output (ei_local->block_output) -#define ei_block_input (ei_local->block_input) -#define ei_get_8390_hdr (ei_local->get_8390_hdr) - -/* Index to functions. */ -static void ei_tx_intr(struct net_device *dev); -static void ei_tx_err(struct net_device *dev); -static void ei_receive(struct net_device *dev); -static void ei_rx_overrun(struct net_device *dev); - -/* Routines generic to NS8390-based boards. */ -static void NS8390_trigger_send(struct net_device *dev, unsigned int length, - int start_page); -static void do_set_multicast_list(struct net_device *dev); - -/* - * SMP and the 8390 setup. - * - * The 8390 isn't exactly designed to be multithreaded on RX/TX. There is - * a page register that controls bank and packet buffer access. We guard - * this with ei_local->page_lock. Nobody should assume or set the page other - * than zero when the lock is not held. Lock holders must restore page 0 - * before unlocking. Even pure readers must take the lock to protect in - * page 0. - * - * To make life difficult the chip can also be very slow. We therefore can't - * just use spinlocks. For the longer lockups we disable the irq the device - * sits on and hold the lock. We must hold the lock because there is a dual - * processor case other than interrupts (get stats/set multicast list in - * parallel with each other and transmit). - * - * Note: in theory we can just disable the irq on the card _but_ there is - * a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs" - * enter lock, take the queued irq. So we waddle instead of flying. - * - * Finally by special arrangement for the purpose of being generally - * annoying the transmit function is called bh atomic. That places - * restrictions on the user context callers as disable_irq won't save - * them. - */ - -/** - * ax_open - Open/initialize the board. - * @dev: network device to initialize - * - * This routine goes all-out, setting everything - * up anew at each open, even though many of these registers should only - * need to be set once at boot. - */ -static int ax_open(struct net_device *dev) -{ - unsigned long flags; - struct ei_device *ei_local = netdev_priv(dev); - - /* - * Grab the page lock so we own the register set, then call - * the init function. - */ - - spin_lock_irqsave(&ei_local->page_lock, flags); - AX88190_init(dev, 1); - /* Set the flag before we drop the lock, That way the IRQ arrives - after its set and we get no silly warnings */ - netif_start_queue(dev); - spin_unlock_irqrestore(&ei_local->page_lock, flags); - ei_local->irqlock = 0; - return 0; -} - -#define dev_lock(dev) (((struct ei_device *)netdev_priv(dev))->page_lock) - -/** - * ax_close - shut down network device - * @dev: network device to close - * - * Opposite of ax_open(). Only used when "ifconfig down" is done. - */ -static int ax_close(struct net_device *dev) -{ - unsigned long flags; - - /* - * Hold the page lock during close - */ - - spin_lock_irqsave(&dev_lock(dev), flags); - AX88190_init(dev, 0); - spin_unlock_irqrestore(&dev_lock(dev), flags); - netif_stop_queue(dev); - return 0; -} - -/** - * axnet_tx_timeout - handle transmit time out condition - * @dev: network device which has apparently fallen asleep - * @txqueue: unused - * - * Called by kernel when device never acknowledges a transmit has - * completed (or failed) - i.e. never posted a Tx related interrupt. - */ - -static void axnet_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - long e8390_base = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - int txsr, isr, tickssofar = jiffies - dev_trans_start(dev); - unsigned long flags; - - dev->stats.tx_errors++; - - spin_lock_irqsave(&ei_local->page_lock, flags); - txsr = inb(e8390_base+EN0_TSR); - isr = inb(e8390_base+EN0_ISR); - spin_unlock_irqrestore(&ei_local->page_lock, flags); - - netdev_dbg(dev, "Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n", - (txsr & ENTSR_ABT) ? "excess collisions." : - (isr) ? "lost interrupt?" : "cable problem?", - txsr, isr, tickssofar); - - if (!isr && !dev->stats.tx_packets) - { - /* The 8390 probably hasn't gotten on the cable yet. */ - ei_local->interface_num ^= 1; /* Try a different xcvr. */ - } - - /* Ugly but a reset can be slow, yet must be protected */ - - spin_lock_irqsave(&ei_local->page_lock, flags); - - /* Try to restart the card. Perhaps the user has fixed something. */ - ei_reset_8390(dev); - AX88190_init(dev, 1); - - spin_unlock_irqrestore(&ei_local->page_lock, flags); - netif_wake_queue(dev); -} - -/** - * axnet_start_xmit - begin packet transmission - * @skb: packet to be sent - * @dev: network device to which packet is sent - * - * Sends a packet to an 8390 network device. - */ - -static netdev_tx_t axnet_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - long e8390_base = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - int length, send_length, output_page; - unsigned long flags; - u8 packet[ETH_ZLEN]; - - netif_stop_queue(dev); - - length = skb->len; - - /* Mask interrupts from the ethercard. - SMP: We have to grab the lock here otherwise the IRQ handler - on another CPU can flip window and race the IRQ mask set. We end - up trashing the mcast filter not disabling irqs if we don't lock */ - - spin_lock_irqsave(&ei_local->page_lock, flags); - outb_p(0x00, e8390_base + EN0_IMR); - - /* - * Slow phase with lock held. - */ - - ei_local->irqlock = 1; - - send_length = max(length, ETH_ZLEN); - - /* - * We have two Tx slots available for use. Find the first free - * slot, and then perform some sanity checks. With two Tx bufs, - * you get very close to transmitting back-to-back packets. With - * only one Tx buf, the transmitter sits idle while you reload the - * card, leaving a substantial gap between each transmitted packet. - */ - - if (ei_local->tx1 == 0) - { - output_page = ei_local->tx_start_page; - ei_local->tx1 = send_length; - if ((netif_msg_tx_queued(ei_local)) && - ei_local->tx2 > 0) - netdev_dbg(dev, - "idle transmitter tx2=%d, lasttx=%d, txing=%d\n", - ei_local->tx2, ei_local->lasttx, - ei_local->txing); - } - else if (ei_local->tx2 == 0) - { - output_page = ei_local->tx_start_page + TX_PAGES/2; - ei_local->tx2 = send_length; - if ((netif_msg_tx_queued(ei_local)) && - ei_local->tx1 > 0) - netdev_dbg(dev, - "idle transmitter, tx1=%d, lasttx=%d, txing=%d\n", - ei_local->tx1, ei_local->lasttx, - ei_local->txing); - } - else - { /* We should never get here. */ - netif_dbg(ei_local, tx_err, dev, - "No Tx buffers free! tx1=%d tx2=%d last=%d\n", - ei_local->tx1, ei_local->tx2, - ei_local->lasttx); - ei_local->irqlock = 0; - netif_stop_queue(dev); - outb_p(ENISR_ALL, e8390_base + EN0_IMR); - spin_unlock_irqrestore(&ei_local->page_lock, flags); - dev->stats.tx_errors++; - return NETDEV_TX_BUSY; - } - - /* - * Okay, now upload the packet and trigger a send if the transmitter - * isn't already sending. If it is busy, the interrupt handler will - * trigger the send later, upon receiving a Tx done interrupt. - */ - - if (length == skb->len) - ei_block_output(dev, length, skb->data, output_page); - else { - memset(packet, 0, ETH_ZLEN); - skb_copy_from_linear_data(skb, packet, skb->len); - ei_block_output(dev, length, packet, output_page); - } - - if (! ei_local->txing) - { - ei_local->txing = 1; - NS8390_trigger_send(dev, send_length, output_page); - netif_trans_update(dev); - if (output_page == ei_local->tx_start_page) - { - ei_local->tx1 = -1; - ei_local->lasttx = -1; - } - else - { - ei_local->tx2 = -1; - ei_local->lasttx = -2; - } - } - else ei_local->txqueue++; - - if (ei_local->tx1 && ei_local->tx2) - netif_stop_queue(dev); - else - netif_start_queue(dev); - - /* Turn 8390 interrupts back on. */ - ei_local->irqlock = 0; - outb_p(ENISR_ALL, e8390_base + EN0_IMR); - - spin_unlock_irqrestore(&ei_local->page_lock, flags); - - dev_kfree_skb (skb); - dev->stats.tx_bytes += send_length; - - return NETDEV_TX_OK; -} - -/** - * ax_interrupt - handle the interrupts from an 8390 - * @irq: interrupt number - * @dev_id: a pointer to the net_device - * - * Handle the ether interface interrupts. We pull packets from - * the 8390 via the card specific functions and fire them at the networking - * stack. We also handle transmit completions and wake the transmit path if - * necessary. We also update the counters and do other housekeeping as - * needed. - */ - -static irqreturn_t ax_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - long e8390_base; - int interrupts, nr_serviced = 0, i; - struct ei_device *ei_local; - int handled = 0; - unsigned long flags; - - e8390_base = dev->base_addr; - ei_local = netdev_priv(dev); - - /* - * Protect the irq test too. - */ - - spin_lock_irqsave(&ei_local->page_lock, flags); - - if (ei_local->irqlock) { -#if 1 /* This might just be an interrupt for a PCI device sharing this line */ - const char *msg; - /* The "irqlock" check is only for testing. */ - if (ei_local->irqlock) - msg = "Interrupted while interrupts are masked!"; - else - msg = "Reentering the interrupt handler!"; - netdev_info(dev, "%s, isr=%#2x imr=%#2x\n", - msg, - inb_p(e8390_base + EN0_ISR), - inb_p(e8390_base + EN0_IMR)); -#endif - spin_unlock_irqrestore(&ei_local->page_lock, flags); - return IRQ_NONE; - } - - netif_dbg(ei_local, intr, dev, "interrupt(isr=%#2.2x)\n", - inb_p(e8390_base + EN0_ISR)); - - outb_p(0x00, e8390_base + EN0_ISR); - ei_local->irqlock = 1; - - /* !!Assumption!! -- we stay in page 0. Don't break this. */ - while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 && - ++nr_serviced < MAX_SERVICE) - { - if (!netif_running(dev) || (interrupts == 0xff)) { - netif_warn(ei_local, intr, dev, - "interrupt from stopped card\n"); - outb_p(interrupts, e8390_base + EN0_ISR); - interrupts = 0; - break; - } - handled = 1; - - /* AX88190 bug fix. */ - outb_p(interrupts, e8390_base + EN0_ISR); - for (i = 0; i < 10; i++) { - if (!(inb(e8390_base + EN0_ISR) & interrupts)) - break; - outb_p(0, e8390_base + EN0_ISR); - outb_p(interrupts, e8390_base + EN0_ISR); - } - if (interrupts & ENISR_OVER) - ei_rx_overrun(dev); - else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) - { - /* Got a good (?) packet. */ - ei_receive(dev); - } - /* Push the next to-transmit packet through. */ - if (interrupts & ENISR_TX) - ei_tx_intr(dev); - else if (interrupts & ENISR_TX_ERR) - ei_tx_err(dev); - - if (interrupts & ENISR_COUNTERS) - { - dev->stats.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0); - dev->stats.rx_crc_errors += inb_p(e8390_base + EN0_COUNTER1); - dev->stats.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2); - } - } - - if (interrupts && (netif_msg_intr(ei_local))) - { - handled = 1; - if (nr_serviced >= MAX_SERVICE) - { - /* 0xFF is valid for a card removal */ - if (interrupts != 0xFF) - netdev_warn(dev, - "Too much work at interrupt, status %#2.2x\n", - interrupts); - outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */ - } else { - netdev_warn(dev, "unknown interrupt %#2x\n", - interrupts); - outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */ - } - } - - /* Turn 8390 interrupts back on. */ - ei_local->irqlock = 0; - outb_p(ENISR_ALL, e8390_base + EN0_IMR); - - spin_unlock_irqrestore(&ei_local->page_lock, flags); - return IRQ_RETVAL(handled); -} - -/** - * ei_tx_err - handle transmitter error - * @dev: network device which threw the exception - * - * A transmitter error has happened. Most likely excess collisions (which - * is a fairly normal condition). If the error is one where the Tx will - * have been aborted, we try and send another one right away, instead of - * letting the failed packet sit and collect dust in the Tx buffer. This - * is a much better solution as it avoids kernel based Tx timeouts, and - * an unnecessary card reset. - * - * Called with lock held. - */ - -static void ei_tx_err(struct net_device *dev) -{ - long e8390_base = dev->base_addr; - unsigned char txsr = inb_p(e8390_base+EN0_TSR); - unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU); - -#ifdef VERBOSE_ERROR_DUMP - netdev_dbg(dev, "transmitter error (%#2x):", txsr); - if (txsr & ENTSR_ABT) - pr_cont(" excess-collisions"); - if (txsr & ENTSR_ND) - pr_cont(" non-deferral"); - if (txsr & ENTSR_CRS) - pr_cont(" lost-carrier"); - if (txsr & ENTSR_FU) - pr_cont(" FIFO-underrun"); - if (txsr & ENTSR_CDH) - pr_cont(" lost-heartbeat"); - pr_cont("\n"); -#endif - - if (tx_was_aborted) - ei_tx_intr(dev); - else - { - dev->stats.tx_errors++; - if (txsr & ENTSR_CRS) dev->stats.tx_carrier_errors++; - if (txsr & ENTSR_CDH) dev->stats.tx_heartbeat_errors++; - if (txsr & ENTSR_OWC) dev->stats.tx_window_errors++; - } -} - -/** - * ei_tx_intr - transmit interrupt handler - * @dev: network device for which tx intr is handled - * - * We have finished a transmit: check for errors and then trigger the next - * packet to be sent. Called with lock held. - */ - -static void ei_tx_intr(struct net_device *dev) -{ - long e8390_base = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - int status = inb(e8390_base + EN0_TSR); - - /* - * There are two Tx buffers, see which one finished, and trigger - * the send of another one if it exists. - */ - ei_local->txqueue--; - - if (ei_local->tx1 < 0) - { - if (ei_local->lasttx != 1 && ei_local->lasttx != -1) - netdev_err(dev, "%s: bogus last_tx_buffer %d, tx1=%d\n", - ei_local->name, ei_local->lasttx, - ei_local->tx1); - ei_local->tx1 = 0; - if (ei_local->tx2 > 0) - { - ei_local->txing = 1; - NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6); - netif_trans_update(dev); - ei_local->tx2 = -1; - ei_local->lasttx = 2; - } else { - ei_local->lasttx = 20; - ei_local->txing = 0; - } - } - else if (ei_local->tx2 < 0) - { - if (ei_local->lasttx != 2 && ei_local->lasttx != -2) - netdev_err(dev, "%s: bogus last_tx_buffer %d, tx2=%d\n", - ei_local->name, ei_local->lasttx, - ei_local->tx2); - ei_local->tx2 = 0; - if (ei_local->tx1 > 0) - { - ei_local->txing = 1; - NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page); - netif_trans_update(dev); - ei_local->tx1 = -1; - ei_local->lasttx = 1; - } else { - ei_local->lasttx = 10; - ei_local->txing = 0; - } - } -// else -// netdev_warn(dev, "unexpected TX-done interrupt, lasttx=%d\n", -// ei_local->lasttx); - - /* Minimize Tx latency: update the statistics after we restart TXing. */ - if (status & ENTSR_COL) - dev->stats.collisions++; - if (status & ENTSR_PTX) - dev->stats.tx_packets++; - else - { - dev->stats.tx_errors++; - if (status & ENTSR_ABT) - { - dev->stats.tx_aborted_errors++; - dev->stats.collisions += 16; - } - if (status & ENTSR_CRS) - dev->stats.tx_carrier_errors++; - if (status & ENTSR_FU) - dev->stats.tx_fifo_errors++; - if (status & ENTSR_CDH) - dev->stats.tx_heartbeat_errors++; - if (status & ENTSR_OWC) - dev->stats.tx_window_errors++; - } - netif_wake_queue(dev); -} - -/** - * ei_receive - receive some packets - * @dev: network device with which receive will be run - * - * We have a good packet(s), get it/them out of the buffers. - * Called with lock held. - */ - -static void ei_receive(struct net_device *dev) -{ - long e8390_base = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - unsigned char rxing_page, this_frame, next_frame; - unsigned short current_offset; - int rx_pkt_count = 0; - struct e8390_pkt_hdr rx_frame; - - while (++rx_pkt_count < 10) - { - int pkt_len, pkt_stat; - - /* Get the rx page (incoming packet pointer). */ - rxing_page = inb_p(e8390_base + EN1_CURPAG -1); - - /* Remove one frame from the ring. Boundary is always a page behind. */ - this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1; - if (this_frame >= ei_local->stop_page) - this_frame = ei_local->rx_start_page; - - /* Someday we'll omit the previous, iff we never get this message. - (There is at least one clone claimed to have a problem.) - - Keep quiet if it looks like a card removal. One problem here - is that some clones crash in roughly the same way. - */ - if ((netif_msg_rx_err(ei_local)) && - this_frame != ei_local->current_page && - (this_frame != 0x0 || rxing_page != 0xFF)) - netdev_err(dev, "mismatched read page pointers %2x vs %2x\n", - this_frame, ei_local->current_page); - - if (this_frame == rxing_page) /* Read all the frames? */ - break; /* Done for now */ - - current_offset = this_frame << 8; - ei_get_8390_hdr(dev, &rx_frame, this_frame); - - pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr); - pkt_stat = rx_frame.status; - - next_frame = this_frame + 1 + ((pkt_len+4)>>8); - - if (pkt_len < 60 || pkt_len > 1518) - { - netif_err(ei_local, rx_err, dev, - "bogus packet size: %d, status=%#2x nxpg=%#2x\n", - rx_frame.count, rx_frame.status, - rx_frame.next); - dev->stats.rx_errors++; - dev->stats.rx_length_errors++; - } - else if ((pkt_stat & 0x0F) == ENRSR_RXOK) - { - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, pkt_len + 2); - if (skb == NULL) - { - netif_err(ei_local, rx_err, dev, - "Couldn't allocate a sk_buff of size %d\n", - pkt_len); - dev->stats.rx_dropped++; - break; - } - else - { - skb_reserve(skb,2); /* IP headers on 16 byte boundaries */ - skb_put(skb, pkt_len); /* Make room */ - ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame)); - skb->protocol=eth_type_trans(skb,dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - if (pkt_stat & ENRSR_PHY) - dev->stats.multicast++; - } - } - else - { - netif_err(ei_local, rx_err, dev, - "bogus packet: status=%#2x nxpg=%#2x size=%d\n", - rx_frame.status, rx_frame.next, - rx_frame.count); - dev->stats.rx_errors++; - /* NB: The NIC counts CRC, frame and missed errors. */ - if (pkt_stat & ENRSR_FO) - dev->stats.rx_fifo_errors++; - } - next_frame = rx_frame.next; - - /* This _should_ never happen: it's here for avoiding bad clones. */ - if (next_frame >= ei_local->stop_page) { - netdev_info(dev, "next frame inconsistency, %#2x\n", - next_frame); - next_frame = ei_local->rx_start_page; - } - ei_local->current_page = next_frame; - outb_p(next_frame-1, e8390_base+EN0_BOUNDARY); - } -} - -/** - * ei_rx_overrun - handle receiver overrun - * @dev: network device which threw exception - * - * We have a receiver overrun: we have to kick the 8390 to get it started - * again. Problem is that you have to kick it exactly as NS prescribes in - * the updated datasheets, or "the NIC may act in an unpredictable manner." - * This includes causing "the NIC to defer indefinitely when it is stopped - * on a busy network." Ugh. - * Called with lock held. Don't call this with the interrupts off or your - * computer will hate you - it takes 10ms or so. - */ - -static void ei_rx_overrun(struct net_device *dev) -{ - struct axnet_dev *info = PRIV(dev); - long e8390_base = dev->base_addr; - unsigned char was_txing, must_resend = 0; - struct ei_device *ei_local = netdev_priv(dev); - - /* - * Record whether a Tx was in progress and then issue the - * stop command. - */ - was_txing = inb_p(e8390_base+E8390_CMD) & E8390_TRANS; - outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); - - netif_dbg(ei_local, rx_err, dev, "Receiver overrun\n"); - dev->stats.rx_over_errors++; - - /* - * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total. - * We wait at least 2ms. - */ - - mdelay(2); - - /* - * Reset RBCR[01] back to zero as per magic incantation. - */ - outb_p(0x00, e8390_base+EN0_RCNTLO); - outb_p(0x00, e8390_base+EN0_RCNTHI); - - /* - * See if any Tx was interrupted or not. According to NS, this - * step is vital, and skipping it will cause no end of havoc. - */ - - if (was_txing) - { - unsigned char tx_completed = inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR); - if (!tx_completed) - must_resend = 1; - } - - /* - * Have to enter loopback mode and then restart the NIC before - * you are allowed to slurp packets up off the ring. - */ - outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); - outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD); - - /* - * Clear the Rx ring of all the debris, and ack the interrupt. - */ - ei_receive(dev); - - /* - * Leave loopback mode, and resend any packet that got stopped. - */ - outb_p(E8390_TXCONFIG | info->duplex_flag, e8390_base + EN0_TXCR); - if (must_resend) - outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD); -} - -/* - * Collect the stats. This is called unlocked and from several contexts. - */ - -static struct net_device_stats *get_stats(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - unsigned long flags; - - /* If the card is stopped, just return the present stats. */ - if (!netif_running(dev)) - return &dev->stats; - - spin_lock_irqsave(&ei_local->page_lock,flags); - /* Read the counter registers, assuming we are in page 0. */ - dev->stats.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0); - dev->stats.rx_crc_errors += inb_p(ioaddr + EN0_COUNTER1); - dev->stats.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2); - spin_unlock_irqrestore(&ei_local->page_lock, flags); - - return &dev->stats; -} - -/* - * Form the 64 bit 8390 multicast table from the linked list of addresses - * associated with this dev structure. - */ - -static inline void make_mc_bits(u8 *bits, struct net_device *dev) -{ - struct netdev_hw_addr *ha; - u32 crc; - - netdev_for_each_mc_addr(ha, dev) { - crc = ether_crc(ETH_ALEN, ha->addr); - /* - * The 8390 uses the 6 most significant bits of the - * CRC to index the multicast table. - */ - bits[crc>>29] |= (1<<((crc>>26)&7)); - } -} - -/** - * do_set_multicast_list - set/clear multicast filter - * @dev: net device for which multicast filter is adjusted - * - * Set or clear the multicast filter for this adaptor. - * Must be called with lock held. - */ - -static void do_set_multicast_list(struct net_device *dev) -{ - long e8390_base = dev->base_addr; - int i; - struct ei_device *ei_local = netdev_priv(dev); - - if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) { - memset(ei_local->mcfilter, 0, 8); - if (!netdev_mc_empty(dev)) - make_mc_bits(ei_local->mcfilter, dev); - } else { - /* set to accept-all */ - memset(ei_local->mcfilter, 0xFF, 8); - } - - outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD); - for(i = 0; i < 8; i++) - { - outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i)); - } - outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD); - - if(dev->flags&IFF_PROMISC) - outb_p(E8390_RXCONFIG | 0x58, e8390_base + EN0_RXCR); - else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev)) - outb_p(E8390_RXCONFIG | 0x48, e8390_base + EN0_RXCR); - else - outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR); - - outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD); -} - -/* - * Called without lock held. This is invoked from user context and may - * be parallel to just about everything else. Its also fairly quick and - * not called too often. Must protect against both bh and irq users - */ - -static void set_multicast_list(struct net_device *dev) -{ - unsigned long flags; - - spin_lock_irqsave(&dev_lock(dev), flags); - do_set_multicast_list(dev); - spin_unlock_irqrestore(&dev_lock(dev), flags); -} - -/* This page of functions should be 8390 generic */ -/* Follow National Semi's recommendations for initializing the "NIC". */ - -/** - * AX88190_init - initialize 8390 hardware - * @dev: network device to initialize - * @startp: boolean. non-zero value to initiate chip processing - * - * Must be called with lock held. - */ - -static void AX88190_init(struct net_device *dev, int startp) -{ - struct axnet_dev *info = PRIV(dev); - long e8390_base = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - int i; - int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48; - - if(sizeof(struct e8390_pkt_hdr)!=4) - panic("8390.c: header struct mispacked\n"); - /* Follow National Semi's recommendations for initing the DP83902. */ - outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */ - outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */ - /* Clear the remote byte count registers. */ - outb_p(0x00, e8390_base + EN0_RCNTLO); - outb_p(0x00, e8390_base + EN0_RCNTHI); - /* Set to monitor and loopback mode -- this is vital!. */ - outb_p(E8390_RXOFF|0x40, e8390_base + EN0_RXCR); /* 0x60 */ - outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */ - /* Set the transmit page and receive ring. */ - outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR); - ei_local->tx1 = ei_local->tx2 = 0; - outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG); - outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/ - ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */ - outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG); - /* Clear the pending interrupts and mask. */ - outb_p(0xFF, e8390_base + EN0_ISR); - outb_p(0x00, e8390_base + EN0_IMR); - - /* Copy the station address into the DS8390 registers. */ - - outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */ - for(i = 0; i < 6; i++) - { - outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i)); - if(inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i]) - netdev_err(dev, "Hw. address read/write mismap %d\n", i); - } - - outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG); - outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); - - netif_start_queue(dev); - ei_local->tx1 = ei_local->tx2 = 0; - ei_local->txing = 0; - - if (info->flags & IS_AX88790) /* select Internal PHY */ - outb(0x10, e8390_base + AXNET_GPIO); - - if (startp) - { - outb_p(0xff, e8390_base + EN0_ISR); - outb_p(ENISR_ALL, e8390_base + EN0_IMR); - outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD); - outb_p(E8390_TXCONFIG | info->duplex_flag, - e8390_base + EN0_TXCR); /* xmit on. */ - /* 3c503 TechMan says rxconfig only after the NIC is started. */ - outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR); /* rx on, */ - do_set_multicast_list(dev); /* (re)load the mcast table */ - } -} - -/* Trigger a transmit start, assuming the length is valid. - Always called with the page lock held */ - -static void NS8390_trigger_send(struct net_device *dev, unsigned int length, - int start_page) -{ - long e8390_base = dev->base_addr; - struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev); - - if (inb_p(e8390_base) & E8390_TRANS) - { - netdev_warn(dev, "trigger_send() called with the transmitter busy\n"); - return; - } - outb_p(length & 0xff, e8390_base + EN0_TCNTLO); - outb_p(length >> 8, e8390_base + EN0_TCNTHI); - outb_p(start_page, e8390_base + EN0_TPSR); - outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD); -} diff --git a/drivers/net/ethernet/8390/smc-ultra.c b/drivers/net/ethernet/8390/smc-ultra.c deleted file mode 100644 index 22ca804b2e95..000000000000 --- a/drivers/net/ethernet/8390/smc-ultra.c +++ /dev/null @@ -1,630 +0,0 @@ -// SPDX-License-Identifier: GPL-1.0+ -/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */ -/* - This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards. - - Written 1993-1998 by Donald Becker. - - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - This driver uses the cards in the 8390-compatible mode. - Most of the run-time complexity is handled by the generic code in - 8390.c. The code in this file is responsible for - - ultra_probe() Detecting and initializing the card. - ultra_probe1() - ultra_probe_isapnp() - - ultra_open() The card-specific details of starting, stopping - ultra_reset_8390() and resetting the 8390 NIC core. - ultra_close() - - ultra_block_input() Routines for reading and writing blocks of - ultra_block_output() packet buffer memory. - ultra_pio_input() - ultra_pio_output() - - This driver enables the shared memory only when doing the actual data - transfers to avoid a bug in early version of the card that corrupted - data transferred by a AHA1542. - - This driver now supports the programmed-I/O (PIO) data transfer mode of - the EtherEZ. It does not use the non-8390-compatible "Altego" mode. - That support (if available) is in smc-ez.c. - - Changelog: - - Paul Gortmaker : multiple card support for module users. - Donald Becker : 4/17/96 PIO support, minor potential problems avoided. - Donald Becker : 6/6/96 correctly set auto-wrap bit. - Alexander Sotirov : 1/20/01 Added support for ISAPnP cards - - Note about the ISA PnP support: - - This driver can not autoprobe for more than one SMC EtherEZ PnP card. - You have to configure the second card manually through the /proc/isapnp - interface and then load the module with an explicit io=0x___ option. -*/ - -static const char version[] = - "smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "8390.h" - -#define DRV_NAME "smc-ultra" - -/* A zero-terminated list of I/O addresses to be probed. */ -static unsigned int ultra_portlist[] __initdata = -{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0}; - -static int ultra_probe1(struct net_device *dev, int ioaddr); - -#ifdef __ISAPNP__ -static int ultra_probe_isapnp(struct net_device *dev); -#endif - -static int ultra_open(struct net_device *dev); -static void ultra_reset_8390(struct net_device *dev); -static void ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, - int ring_page); -static void ultra_block_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset); -static void ultra_block_output(struct net_device *dev, int count, - const unsigned char *buf, const int start_page); -static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, - int ring_page); -static void ultra_pio_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset); -static void ultra_pio_output(struct net_device *dev, int count, - const unsigned char *buf, const int start_page); -static int ultra_close_card(struct net_device *dev); - -#ifdef __ISAPNP__ -static struct isapnp_device_id ultra_device_ids[] __initdata = { - { ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416), - ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416), - (long) "SMC EtherEZ (8416)" }, - { } /* terminate list */ -}; - -MODULE_DEVICE_TABLE(isapnp, ultra_device_ids); -#endif - -static u32 ultra_msg_enable; - -#define START_PG 0x00 /* First page of TX buffer */ - -#define ULTRA_CMDREG 0 /* Offset to ASIC command register. */ -#define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */ -#define ULTRA_MEMENB 0x40 /* Enable the shared memory. */ -#define IOPD 0x02 /* I/O Pipe Data (16 bits), PIO operation. */ -#define IOPA 0x07 /* I/O Pipe Address for PIO operation. */ -#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */ -#define ULTRA_IO_EXTENT 32 -#define EN0_ERWCNT 0x08 /* Early receive warning count. */ - -#ifdef CONFIG_NET_POLL_CONTROLLER -static void ultra_poll(struct net_device *dev) -{ - disable_irq(dev->irq); - ei_interrupt(dev->irq, dev); - enable_irq(dev->irq); -} -#endif -/* Probe for the Ultra. This looks like a 8013 with the station - address PROM at I/O ports +8 to +13, with a checksum - following. -*/ - -static int __init do_ultra_probe(struct net_device *dev) -{ - int i; - int base_addr = dev->base_addr; - int irq = dev->irq; - - if (base_addr > 0x1ff) /* Check a single specified location. */ - return ultra_probe1(dev, base_addr); - else if (base_addr != 0) /* Don't probe at all. */ - return -ENXIO; - -#ifdef __ISAPNP__ - /* Look for any installed ISAPnP cards */ - if (isapnp_present() && (ultra_probe_isapnp(dev) == 0)) - return 0; -#endif - - for (i = 0; ultra_portlist[i]; i++) { - dev->irq = irq; - if (ultra_probe1(dev, ultra_portlist[i]) == 0) - return 0; - } - - return -ENODEV; -} - -#ifndef MODULE -struct net_device * __init ultra_probe(int unit) -{ - struct net_device *dev = alloc_ei_netdev(); - int err; - - if (!dev) - return ERR_PTR(-ENOMEM); - - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - - err = do_ultra_probe(dev); - if (err) - goto out; - return dev; -out: - free_netdev(dev); - return ERR_PTR(err); -} -#endif - -static const struct net_device_ops ultra_netdev_ops = { - .ndo_open = ultra_open, - .ndo_stop = ultra_close_card, - - .ndo_start_xmit = ei_start_xmit, - .ndo_tx_timeout = ei_tx_timeout, - .ndo_get_stats = ei_get_stats, - .ndo_set_rx_mode = ei_set_multicast_list, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = ultra_poll, -#endif -}; - -static int __init ultra_probe1(struct net_device *dev, int ioaddr) -{ - int i, retval; - int checksum = 0; - u8 macaddr[ETH_ALEN]; - const char *model_name; - unsigned char eeprom_irq = 0; - static unsigned version_printed; - /* Values from various config regs. */ - unsigned char num_pages, irqreg, addr, piomode; - unsigned char idreg = inb(ioaddr + 7); - unsigned char reg4 = inb(ioaddr + 4) & 0x7f; - struct ei_device *ei_local = netdev_priv(dev); - - if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME)) - return -EBUSY; - - /* Check the ID nibble. */ - if ((idreg & 0xF0) != 0x20 /* SMC Ultra */ - && (idreg & 0xF0) != 0x40) { /* SMC EtherEZ */ - retval = -ENODEV; - goto out; - } - - /* Select the station address register set. */ - outb(reg4, ioaddr + 4); - - for (i = 0; i < 8; i++) - checksum += inb(ioaddr + 8 + i); - if ((checksum & 0xff) != 0xFF) { - retval = -ENODEV; - goto out; - } - - if ((ultra_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0)) - netdev_info(dev, version); - - model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ"; - - for (i = 0; i < 6; i++) - macaddr[i] = inb(ioaddr + 8 + i); - eth_hw_addr_set(dev, macaddr); - - netdev_info(dev, "%s at %#3x, %pM", model_name, - ioaddr, dev->dev_addr); - - /* Switch from the station address to the alternate register set and - read the useful registers there. */ - outb(0x80 | reg4, ioaddr + 4); - - /* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */ - outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c); - piomode = inb(ioaddr + 0x8); - addr = inb(ioaddr + 0xb); - irqreg = inb(ioaddr + 0xd); - - /* Switch back to the station address register set so that the MS-DOS driver - can find the card after a warm boot. */ - outb(reg4, ioaddr + 4); - - if (dev->irq < 2) { - unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15}; - int irq; - - /* The IRQ bits are split. */ - irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)]; - - if (irq == 0) { - pr_cont(", failed to detect IRQ line.\n"); - retval = -EAGAIN; - goto out; - } - dev->irq = irq; - eeprom_irq = 1; - } - - /* The 8390 isn't at the base address, so fake the offset */ - dev->base_addr = ioaddr+ULTRA_NIC_OFFSET; - - { - static const int addr_tbl[4] = { - 0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000 - }; - static const short num_pages_tbl[4] = { - 0x20, 0x40, 0x80, 0xff - }; - - dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ; - num_pages = num_pages_tbl[(addr >> 4) & 3]; - } - - ei_status.name = model_name; - ei_status.word16 = 1; - ei_status.tx_start_page = START_PG; - ei_status.rx_start_page = START_PG + TX_PAGES; - ei_status.stop_page = num_pages; - - ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG)*256); - if (!ei_status.mem) { - pr_cont(", failed to ioremap.\n"); - retval = -ENOMEM; - goto out; - } - - dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG)*256; - - if (piomode) { - pr_cont(", %s IRQ %d programmed-I/O mode.\n", - eeprom_irq ? "EEPROM" : "assigned ", dev->irq); - ei_status.block_input = &ultra_pio_input; - ei_status.block_output = &ultra_pio_output; - ei_status.get_8390_hdr = &ultra_pio_get_hdr; - } else { - pr_cont(", %s IRQ %d memory %#lx-%#lx.\n", - eeprom_irq ? "" : "assigned ", dev->irq, dev->mem_start, - dev->mem_end-1); - ei_status.block_input = &ultra_block_input; - ei_status.block_output = &ultra_block_output; - ei_status.get_8390_hdr = &ultra_get_8390_hdr; - } - ei_status.reset_8390 = &ultra_reset_8390; - - dev->netdev_ops = &ultra_netdev_ops; - NS8390_init(dev, 0); - ei_local->msg_enable = ultra_msg_enable; - - retval = register_netdev(dev); - if (retval) - goto out; - return 0; -out: - release_region(ioaddr, ULTRA_IO_EXTENT); - return retval; -} - -#ifdef __ISAPNP__ -static int __init ultra_probe_isapnp(struct net_device *dev) -{ - int i; - - for (i = 0; ultra_device_ids[i].vendor != 0; i++) { - struct pnp_dev *idev = NULL; - - while ((idev = pnp_find_dev(NULL, - ultra_device_ids[i].vendor, - ultra_device_ids[i].function, - idev))) { - /* Avoid already found cards from previous calls */ - if (pnp_device_attach(idev) < 0) - continue; - if (pnp_activate_dev(idev) < 0) { - __again: - pnp_device_detach(idev); - continue; - } - /* if no io and irq, search for next */ - if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) - goto __again; - /* found it */ - dev->base_addr = pnp_port_start(idev, 0); - dev->irq = pnp_irq(idev, 0); - netdev_info(dev, - "smc-ultra.c: ISAPnP reports %s at i/o %#lx, irq %d.\n", - (char *) ultra_device_ids[i].driver_data, - dev->base_addr, dev->irq); - if (ultra_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */ - netdev_err(dev, - "smc-ultra.c: Probe of ISAPnP card at %#lx failed.\n", - dev->base_addr); - pnp_device_detach(idev); - return -ENXIO; - } - ei_status.priv = (unsigned long)idev; - break; - } - if (!idev) - continue; - return 0; - } - - return -ENODEV; -} -#endif - -static int -ultra_open(struct net_device *dev) -{ - int retval; - int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ - unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40, - 0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, }; - - retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); - if (retval) - return retval; - - outb(0x00, ioaddr); /* Disable shared memory for safety. */ - outb(0x80, ioaddr + 5); - /* Set the IRQ line. */ - outb(inb(ioaddr + 4) | 0x80, ioaddr + 4); - outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13); - outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4); - - if (ei_status.block_input == &ultra_pio_input) { - outb(0x11, ioaddr + 6); /* Enable interrupts and PIO. */ - outb(0x01, ioaddr + 0x19); /* Enable ring read auto-wrap. */ - } else - outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */ - /* Set the early receive warning level in window 0 high enough not - to receive ERW interrupts. */ - outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); - outb(0xff, dev->base_addr + EN0_ERWCNT); - ei_open(dev); - return 0; -} - -static void -ultra_reset_8390(struct net_device *dev) -{ - int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */ - struct ei_device *ei_local = netdev_priv(dev); - - outb(ULTRA_RESET, cmd_port); - netif_dbg(ei_local, hw, dev, "resetting Ultra, t=%ld...\n", jiffies); - ei_status.txing = 0; - - outb(0x00, cmd_port); /* Disable shared memory for safety. */ - outb(0x80, cmd_port + 5); - if (ei_status.block_input == &ultra_pio_input) - outb(0x11, cmd_port + 6); /* Enable interrupts and PIO. */ - else - outb(0x01, cmd_port + 6); /* Enable interrupts and memory. */ - - netif_dbg(ei_local, hw, dev, "reset done\n"); -} - -/* Grab the 8390 specific header. Similar to the block_input routine, but - we don't need to be concerned with ring wrap as the header will be at - the start of a page, so we optimize accordingly. */ - -static void -ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) -{ - void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG)<<8); - - outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem on */ -#ifdef __BIG_ENDIAN - /* Officially this is what we are doing, but the readl() is faster */ - /* unfortunately it isn't endian aware of the struct */ - memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); - hdr->count = le16_to_cpu(hdr->count); -#else - ((unsigned int*)hdr)[0] = readl(hdr_start); -#endif - outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */ -} - -/* Block input and output are easy on shared memory ethercards, the only - complication is when the ring buffer wraps. */ - -static void -ultra_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) -{ - void __iomem *xfer_start = ei_status.mem + ring_offset - (START_PG<<8); - - /* Enable shared memory. */ - outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); - - if (ring_offset + count > ei_status.stop_page*256) { - /* We must wrap the input move. */ - int semi_count = ei_status.stop_page*256 - ring_offset; - memcpy_fromio(skb->data, xfer_start, semi_count); - count -= semi_count; - memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count); - } else { - memcpy_fromio(skb->data, xfer_start, count); - } - - outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */ -} - -static void -ultra_block_output(struct net_device *dev, int count, const unsigned char *buf, - int start_page) -{ - void __iomem *shmem = ei_status.mem + ((start_page - START_PG)<<8); - - /* Enable shared memory. */ - outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); - - memcpy_toio(shmem, buf, count); - - outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */ -} - -/* The identical operations for programmed I/O cards. - The PIO model is trivial to use: the 16 bit start address is written - byte-sequentially to IOPA, with no intervening I/O operations, and the - data is read or written to the IOPD data port. - The only potential complication is that the address register is shared - and must be always be rewritten between each read/write direction change. - This is no problem for us, as the 8390 code ensures that we are single - threaded. */ -static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, - int ring_page) -{ - int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ - outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */ - outb(ring_page, ioaddr + IOPA); - insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1); -} - -static void ultra_pio_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset) -{ - int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ - char *buf = skb->data; - - /* For now set the address again, although it should already be correct. */ - outb(ring_offset, ioaddr + IOPA); /* Set the address, LSB first. */ - outb(ring_offset >> 8, ioaddr + IOPA); - /* We know skbuffs are padded to at least word alignment. */ - insw(ioaddr + IOPD, buf, (count+1)>>1); -} -static void ultra_pio_output(struct net_device *dev, int count, - const unsigned char *buf, const int start_page) -{ - int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ - outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */ - outb(start_page, ioaddr + IOPA); - /* An extra odd byte is OK here as well. */ - outsw(ioaddr + IOPD, buf, (count+1)>>1); -} - -static int -ultra_close_card(struct net_device *dev) -{ - int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */ - struct ei_device *ei_local = netdev_priv(dev); - - netif_stop_queue(dev); - - netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n"); - - outb(0x00, ioaddr + 6); /* Disable interrupts. */ - free_irq(dev->irq, dev); - - NS8390_init(dev, 0); - - /* We should someday disable shared memory and change to 8-bit mode - "just in case"... */ - - return 0; -} - - -#ifdef MODULE -#define MAX_ULTRA_CARDS 4 /* Max number of Ultra cards per module */ -static struct net_device *dev_ultra[MAX_ULTRA_CARDS]; -static int io[MAX_ULTRA_CARDS]; -static int irq[MAX_ULTRA_CARDS]; - -module_param_hw_array(io, int, ioport, NULL, 0); -module_param_hw_array(irq, int, irq, NULL, 0); -module_param_named(msg_enable, ultra_msg_enable, uint, 0444); -MODULE_PARM_DESC(io, "I/O base address(es)"); -MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); -MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); -MODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver"); -MODULE_LICENSE("GPL"); - -/* This is set up so that only a single autoprobe takes place per call. -ISA device autoprobes on a running machine are not recommended. */ -static int __init ultra_init_module(void) -{ - struct net_device *dev; - int this_dev, found = 0; - - for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { - if (io[this_dev] == 0) { - if (this_dev != 0) break; /* only autoprobe 1st one */ - printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n"); - } - dev = alloc_ei_netdev(); - if (!dev) - break; - dev->irq = irq[this_dev]; - dev->base_addr = io[this_dev]; - if (do_ultra_probe(dev) == 0) { - dev_ultra[found++] = dev; - continue; - } - free_netdev(dev); - printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]); - break; - } - if (found) - return 0; - return -ENXIO; -} -module_init(ultra_init_module); - -static void cleanup_card(struct net_device *dev) -{ - /* NB: ultra_close_card() does free_irq */ -#ifdef __ISAPNP__ - struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv; - if (idev) - pnp_device_detach(idev); -#endif - release_region(dev->base_addr - ULTRA_NIC_OFFSET, ULTRA_IO_EXTENT); - iounmap(ei_status.mem); -} - -static void __exit ultra_cleanup_module(void) -{ - int this_dev; - - for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { - struct net_device *dev = dev_ultra[this_dev]; - if (dev) { - unregister_netdev(dev); - cleanup_card(dev); - free_netdev(dev); - } - } -} -module_exit(ultra_cleanup_module); -#endif /* MODULE */ diff --git a/drivers/net/ethernet/8390/wd.c b/drivers/net/ethernet/8390/wd.c deleted file mode 100644 index ffd639477dfc..000000000000 --- a/drivers/net/ethernet/8390/wd.c +++ /dev/null @@ -1,575 +0,0 @@ -// SPDX-License-Identifier: GPL-1.0+ -/* wd.c: A WD80x3 ethernet driver for linux. */ -/* - Written 1993-94 by Donald Becker. - - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - This is a driver for WD8003 and WD8013 "compatible" ethercards. - - Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013. - - Changelog: - - Paul Gortmaker : multiple card support for module users, support - for non-standard memory sizes. - - -*/ - -static const char version[] = - "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "8390.h" - -#define DRV_NAME "wd" - -/* A zero-terminated list of I/O addresses to be probed. */ -static unsigned int wd_portlist[] __initdata = -{0x300, 0x280, 0x380, 0x240, 0}; - -static int wd_probe1(struct net_device *dev, int ioaddr); - -static int wd_open(struct net_device *dev); -static void wd_reset_8390(struct net_device *dev); -static void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, - int ring_page); -static void wd_block_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset); -static void wd_block_output(struct net_device *dev, int count, - const unsigned char *buf, int start_page); -static int wd_close(struct net_device *dev); - -static u32 wd_msg_enable; - -#define WD_START_PG 0x00 /* First page of TX buffer */ -#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */ -#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */ - -#define WD_CMDREG 0 /* Offset to ASIC command register. */ -#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */ -#define WD_MEMENB 0x40 /* Enable the shared memory. */ -#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */ -#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */ -#define NIC16 0x40 /* Enable 16 bit access from the 8390. */ -#define WD_NIC_OFFSET 16 /* Offset to the 8390 from the base_addr. */ -#define WD_IO_EXTENT 32 - - -/* Probe for the WD8003 and WD8013. These cards have the station - address PROM at I/O ports +8 to +13, with a checksum - following. A Soundblaster can have the same checksum as an WDethercard, - so we have an extra exclusionary check for it. - - The wd_probe1() routine initializes the card and fills the - station address field. */ - -static int __init do_wd_probe(struct net_device *dev) -{ - int i; - struct resource *r; - int base_addr = dev->base_addr; - int irq = dev->irq; - int mem_start = dev->mem_start; - int mem_end = dev->mem_end; - - if (base_addr > 0x1ff) { /* Check a user specified location. */ - r = request_region(base_addr, WD_IO_EXTENT, "wd-probe"); - if ( r == NULL) - return -EBUSY; - i = wd_probe1(dev, base_addr); - if (i != 0) - release_region(base_addr, WD_IO_EXTENT); - else - r->name = dev->name; - return i; - } - else if (base_addr != 0) /* Don't probe at all. */ - return -ENXIO; - - for (i = 0; wd_portlist[i]; i++) { - int ioaddr = wd_portlist[i]; - r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe"); - if (r == NULL) - continue; - if (wd_probe1(dev, ioaddr) == 0) { - r->name = dev->name; - return 0; - } - release_region(ioaddr, WD_IO_EXTENT); - dev->irq = irq; - dev->mem_start = mem_start; - dev->mem_end = mem_end; - } - - return -ENODEV; -} - -#ifndef MODULE -struct net_device * __init wd_probe(int unit) -{ - struct net_device *dev = alloc_ei_netdev(); - int err; - - if (!dev) - return ERR_PTR(-ENOMEM); - - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - - err = do_wd_probe(dev); - if (err) - goto out; - return dev; -out: - free_netdev(dev); - return ERR_PTR(err); -} -#endif - -static const struct net_device_ops wd_netdev_ops = { - .ndo_open = wd_open, - .ndo_stop = wd_close, - .ndo_start_xmit = ei_start_xmit, - .ndo_tx_timeout = ei_tx_timeout, - .ndo_get_stats = ei_get_stats, - .ndo_set_rx_mode = ei_set_multicast_list, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = ei_poll, -#endif -}; - -static int __init wd_probe1(struct net_device *dev, int ioaddr) -{ - int i; - int err; - int checksum = 0; - int ancient = 0; /* An old card without config registers. */ - int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */ - u8 addr[ETH_ALEN]; - const char *model_name; - static unsigned version_printed; - struct ei_device *ei_local = netdev_priv(dev); - - for (i = 0; i < 8; i++) - checksum += inb(ioaddr + 8 + i); - if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */ - || inb(ioaddr + 9) == 0xff - || (checksum & 0xff) != 0xFF) - return -ENODEV; - - /* Check for semi-valid mem_start/end values if supplied. */ - if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) { - netdev_warn(dev, - "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n"); - dev->mem_start = 0; - dev->mem_end = 0; - } - - if ((wd_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0)) - netdev_info(dev, version); - - for (i = 0; i < 6; i++) - addr[i] = inb(ioaddr + 8 + i); - eth_hw_addr_set(dev, addr); - - netdev_info(dev, "WD80x3 at %#3x, %pM", ioaddr, dev->dev_addr); - - /* The following PureData probe code was contributed by - Mike Jagdis . Puredata does software - configuration differently from others so we have to check for them. - This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card. - */ - if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') { - unsigned char reg5 = inb(ioaddr+5); - - switch (inb(ioaddr+2)) { - case 0x03: word16 = 0; model_name = "PDI8023-8"; break; - case 0x05: word16 = 0; model_name = "PDUC8023"; break; - case 0x0a: word16 = 1; model_name = "PDI8023-16"; break; - /* Either 0x01 (dumb) or they've released a new version. */ - default: word16 = 0; model_name = "PDI8023"; break; - } - dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12; - dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1; - } else { /* End of PureData probe */ - /* This method of checking for a 16-bit board is borrowed from the - we.c driver. A simpler method is just to look in ASIC reg. 0x03. - I'm comparing the two method in alpha test to make certain they - return the same result. */ - /* Check for the old 8 bit board - it has register 0/8 aliasing. - Do NOT check i>=6 here -- it hangs the old 8003 boards! */ - for (i = 0; i < 6; i++) - if (inb(ioaddr+i) != inb(ioaddr+8+i)) - break; - if (i >= 6) { - ancient = 1; - model_name = "WD8003-old"; - word16 = 0; - } else { - int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */ - outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */ - if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */ - && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */ - int asic_reg5 = inb(ioaddr+WD_CMDREG5); - /* Magic to set ASIC to word-wide mode. */ - outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5); - outb(tmp, ioaddr+1); - model_name = "WD8013"; - word16 = 1; /* We have a 16bit board here! */ - } else { - model_name = "WD8003"; - word16 = 0; - } - outb(tmp, ioaddr+1); /* Restore original reg1 value. */ - } -#ifndef final_version - if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01)) - pr_cont("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).", - word16 ? 16 : 8, - (inb(ioaddr+1) & 0x01) ? 16 : 8); -#endif - } - -#if defined(WD_SHMEM) && WD_SHMEM > 0x80000 - /* Allow a compile-time override. */ - dev->mem_start = WD_SHMEM; -#else - if (dev->mem_start == 0) { - /* Sanity and old 8003 check */ - int reg0 = inb(ioaddr); - if (reg0 == 0xff || reg0 == 0) { - /* Future plan: this could check a few likely locations first. */ - dev->mem_start = 0xd0000; - pr_cont(" assigning address %#lx", dev->mem_start); - } else { - int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f; - /* Some boards don't have the register 5 -- it returns 0xff. */ - if (high_addr_bits == 0x1f || word16 == 0) - high_addr_bits = 0x01; - dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19); - } - } -#endif - - /* The 8390 isn't at the base address -- the ASIC regs are there! */ - dev->base_addr = ioaddr+WD_NIC_OFFSET; - - if (dev->irq < 2) { - static const int irqmap[] = {9, 3, 5, 7, 10, 11, 15, 4}; - int reg1 = inb(ioaddr+1); - int reg4 = inb(ioaddr+4); - if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */ - short nic_addr = ioaddr+WD_NIC_OFFSET; - unsigned long irq_mask; - - /* We have an old-style ethercard that doesn't report its IRQ - line. Do autoirq to find the IRQ line. Note that this IS NOT - a reliable way to trigger an interrupt. */ - outb_p(E8390_NODMA + E8390_STOP, nic_addr); - outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */ - - irq_mask = probe_irq_on(); - outb_p(0xff, nic_addr + EN0_IMR); /* Enable all interrupts. */ - outb_p(0x00, nic_addr + EN0_RCNTLO); - outb_p(0x00, nic_addr + EN0_RCNTHI); - outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */ - mdelay(20); - dev->irq = probe_irq_off(irq_mask); - - outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */ - - if (wd_msg_enable & NETIF_MSG_PROBE) - pr_cont(" autoirq is %d", dev->irq); - if (dev->irq < 2) - dev->irq = word16 ? 10 : 5; - } else - dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)]; - } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */ - dev->irq = 9; - - /* Snarf the interrupt now. There's no point in waiting since we cannot - share and the board will usually be enabled. */ - i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev); - if (i) { - pr_cont(" unable to get IRQ %d.\n", dev->irq); - return i; - } - - /* OK, were are certain this is going to work. Setup the device. */ - ei_status.name = model_name; - ei_status.word16 = word16; - ei_status.tx_start_page = WD_START_PG; - ei_status.rx_start_page = WD_START_PG + TX_PAGES; - - /* Don't map in the shared memory until the board is actually opened. */ - - /* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */ - if (dev->mem_end != 0) { - ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; - ei_status.priv = dev->mem_end - dev->mem_start; - } else { - ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG; - dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256; - ei_status.priv = (ei_status.stop_page - WD_START_PG)*256; - } - - ei_status.mem = ioremap(dev->mem_start, ei_status.priv); - if (!ei_status.mem) { - free_irq(dev->irq, dev); - return -ENOMEM; - } - - pr_cont(" %s, IRQ %d, shared memory at %#lx-%#lx.\n", - model_name, dev->irq, dev->mem_start, dev->mem_end-1); - - ei_status.reset_8390 = wd_reset_8390; - ei_status.block_input = wd_block_input; - ei_status.block_output = wd_block_output; - ei_status.get_8390_hdr = wd_get_8390_hdr; - - dev->netdev_ops = &wd_netdev_ops; - NS8390_init(dev, 0); - ei_local->msg_enable = wd_msg_enable; - -#if 1 - /* Enable interrupt generation on softconfig cards -- M.U */ - /* .. but possibly potentially unsafe - Donald */ - if (inb(ioaddr+14) & 0x20) - outb(inb(ioaddr+4)|0x80, ioaddr+4); -#endif - - err = register_netdev(dev); - if (err) { - free_irq(dev->irq, dev); - iounmap(ei_status.mem); - } - return err; -} - -static int -wd_open(struct net_device *dev) -{ - int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - - /* Map in the shared memory. Always set register 0 last to remain - compatible with very old boards. */ - ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB; - ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16; - - if (ei_status.word16) - outb(ei_status.reg5, ioaddr+WD_CMDREG5); - outb(ei_status.reg0, ioaddr); /* WD_CMDREG */ - - return ei_open(dev); -} - -static void -wd_reset_8390(struct net_device *dev) -{ - int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - struct ei_device *ei_local = netdev_priv(dev); - - outb(WD_RESET, wd_cmd_port); - netif_dbg(ei_local, hw, dev, "resetting the WD80x3 t=%lu...\n", - jiffies); - ei_status.txing = 0; - - /* Set up the ASIC registers, just in case something changed them. */ - outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); - if (ei_status.word16) - outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5); - - netif_dbg(ei_local, hw, dev, "reset done\n"); -} - -/* Grab the 8390 specific header. Similar to the block_input routine, but - we don't need to be concerned with ring wrap as the header will be at - the start of a page, so we optimize accordingly. */ - -static void -wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) -{ - - int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8); - - /* We'll always get a 4 byte header read followed by a packet read, so - we enable 16 bit mode before the header, and disable after the body. */ - if (ei_status.word16) - outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); - -#ifdef __BIG_ENDIAN - /* Officially this is what we are doing, but the readl() is faster */ - /* unfortunately it isn't endian aware of the struct */ - memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); - hdr->count = le16_to_cpu(hdr->count); -#else - ((unsigned int*)hdr)[0] = readl(hdr_start); -#endif -} - -/* Block input and output are easy on shared memory ethercards, and trivial - on the Western digital card where there is no choice of how to do it. - The only complications are that the ring buffer wraps, and need to map - switch between 8- and 16-bit modes. */ - -static void -wd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) -{ - int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - unsigned long offset = ring_offset - (WD_START_PG<<8); - void __iomem *xfer_start = ei_status.mem + offset; - - if (offset + count > ei_status.priv) { - /* We must wrap the input move. */ - int semi_count = ei_status.priv - offset; - memcpy_fromio(skb->data, xfer_start, semi_count); - count -= semi_count; - memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count); - } else { - /* Packet is in one chunk -- we can copy + cksum. */ - memcpy_fromio(skb->data, xfer_start, count); - } - - /* Turn off 16 bit access so that reboot works. ISA brain-damage */ - if (ei_status.word16) - outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); -} - -static void -wd_block_output(struct net_device *dev, int count, const unsigned char *buf, - int start_page) -{ - int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8); - - - if (ei_status.word16) { - /* Turn on and off 16 bit access so that reboot works. */ - outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); - memcpy_toio(shmem, buf, count); - outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); - } else - memcpy_toio(shmem, buf, count); -} - - -static int -wd_close(struct net_device *dev) -{ - int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - struct ei_device *ei_local = netdev_priv(dev); - - netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n"); - ei_close(dev); - - /* Change from 16-bit to 8-bit shared memory so reboot works. */ - if (ei_status.word16) - outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 ); - - /* And disable the shared memory. */ - outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg); - - return 0; -} - - -#ifdef MODULE -#define MAX_WD_CARDS 4 /* Max number of wd cards per module */ -static struct net_device *dev_wd[MAX_WD_CARDS]; -static int io[MAX_WD_CARDS]; -static int irq[MAX_WD_CARDS]; -static int mem[MAX_WD_CARDS]; -static int mem_end[MAX_WD_CARDS]; /* for non std. mem size */ - -module_param_hw_array(io, int, ioport, NULL, 0); -module_param_hw_array(irq, int, irq, NULL, 0); -module_param_hw_array(mem, int, iomem, NULL, 0); -module_param_hw_array(mem_end, int, iomem, NULL, 0); -module_param_named(msg_enable, wd_msg_enable, uint, 0444); -MODULE_PARM_DESC(io, "I/O base address(es)"); -MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)"); -MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)"); -MODULE_PARM_DESC(mem_end, "memory end address(es)"); -MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); -MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver"); -MODULE_LICENSE("GPL"); - -/* This is set up so that only a single autoprobe takes place per call. -ISA device autoprobes on a running machine are not recommended. */ - -static int __init wd_init_module(void) -{ - struct net_device *dev; - int this_dev, found = 0; - - for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { - if (io[this_dev] == 0) { - if (this_dev != 0) break; /* only autoprobe 1st one */ - printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n"); - } - dev = alloc_ei_netdev(); - if (!dev) - break; - dev->irq = irq[this_dev]; - dev->base_addr = io[this_dev]; - dev->mem_start = mem[this_dev]; - dev->mem_end = mem_end[this_dev]; - if (do_wd_probe(dev) == 0) { - dev_wd[found++] = dev; - continue; - } - free_netdev(dev); - printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]); - break; - } - if (found) - return 0; - return -ENXIO; -} -module_init(wd_init_module); - -static void cleanup_card(struct net_device *dev) -{ - free_irq(dev->irq, dev); - release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT); - iounmap(ei_status.mem); -} - -static void __exit wd_cleanup_module(void) -{ - int this_dev; - - for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { - struct net_device *dev = dev_wd[this_dev]; - if (dev) { - unregister_netdev(dev); - cleanup_card(dev); - free_netdev(dev); - } - } -} -module_exit(wd_cleanup_module); -#endif /* MODULE */ diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index bdc29d143160..b8f70e2a1763 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -61,7 +61,6 @@ source "drivers/net/ethernet/engleder/Kconfig" source "drivers/net/ethernet/ezchip/Kconfig" source "drivers/net/ethernet/faraday/Kconfig" source "drivers/net/ethernet/freescale/Kconfig" -source "drivers/net/ethernet/fujitsu/Kconfig" source "drivers/net/ethernet/fungible/Kconfig" source "drivers/net/ethernet/google/Kconfig" source "drivers/net/ethernet/hisilicon/Kconfig" @@ -157,7 +156,6 @@ config OA_TC6 To know the implementation details, refer documentation in . -source "drivers/net/ethernet/packetengines/Kconfig" source "drivers/net/ethernet/pasemi/Kconfig" source "drivers/net/ethernet/pensando/Kconfig" source "drivers/net/ethernet/qlogic/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 6bffb60ba644..57344fec6ce0 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -40,7 +40,6 @@ obj-$(CONFIG_NET_VENDOR_ENGLEDER) += engleder/ obj-$(CONFIG_NET_VENDOR_EZCHIP) += ezchip/ obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/ obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/ -obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/ obj-$(CONFIG_NET_VENDOR_FUNGIBLE) += fungible/ obj-$(CONFIG_NET_VENDOR_GOOGLE) += google/ obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/ @@ -73,7 +72,6 @@ obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/ obj-$(CONFIG_LPC_ENET) += nxp/ obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/ obj-$(CONFIG_ETHOC) += ethoc.o -obj-$(CONFIG_NET_VENDOR_PACKET_ENGINES) += packetengines/ obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/ obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/ obj-$(CONFIG_NET_VENDOR_QUALCOMM) += qualcomm/ diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index e1ab15f1ee7d..d0c0c0ec8a80 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -745,14 +745,18 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q, dma_addr_t dma_addr; q->buf_size = PAGE_SIZE / 2; - q->ndesc = ndesc; q->qdma = qdma; - q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), + q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry), GFP_KERNEL); if (!q->entry) return -ENOMEM; + q->desc = dmam_alloc_coherent(eth->dev, ndesc * sizeof(*q->desc), + &dma_addr, GFP_KERNEL); + if (!q->desc) + return -ENOMEM; + q->page_pool = page_pool_create(&pp_params); if (IS_ERR(q->page_pool)) { int err = PTR_ERR(q->page_pool); @@ -761,11 +765,7 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q, return err; } - q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc), - &dma_addr, GFP_KERNEL); - if (!q->desc) - return -ENOMEM; - + q->ndesc = ndesc; netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll); airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr); @@ -843,6 +843,32 @@ static int airoha_qdma_init_rx(struct airoha_qdma *qdma) return 0; } +static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q) +{ + struct airoha_qdma *qdma = q->qdma; + struct airoha_eth *eth = qdma->eth; + int i, qid = q - &qdma->q_tx[0]; + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + int j; + + if (!port) + continue; + + if (port->qdma != qdma) + continue; + + for (j = 0; j < port->dev->num_tx_queues; j++) { + if (airoha_qdma_get_txq(qdma, j) != qid) + continue; + + netif_wake_subqueue(port->dev, j); + } + } + q->txq_stopped = false; +} + static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) { struct airoha_tx_irq_queue *irq_q; @@ -914,17 +940,25 @@ static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) q->queued--; if (skb) { - u16 queue = skb_get_queue_mapping(skb); struct netdev_queue *txq; - txq = netdev_get_tx_queue(skb->dev, queue); + txq = skb_get_tx_queue(skb->dev, skb); netdev_tx_completed_queue(txq, 1, skb->len); - if (netif_tx_queue_stopped(txq) && - q->ndesc - q->queued >= q->free_thr) - netif_tx_wake_queue(txq); - dev_kfree_skb_any(skb); } + + if (q->txq_stopped && q->ndesc - q->queued >= q->free_thr) { + /* Since multiple net_device TX queues can share the + * same hw QDMA TX queue, there is no guarantee we have + * inflight packets queued in hw belonging to a + * net_device TX queue stopped in the xmit path. + * In order to avoid any potential net_device TX queue + * stall, we need to wake all the net_device TX queues + * feeding the same hw QDMA TX queue. + */ + airoha_qdma_wake_netdev_txqs(q); + } + unlock: spin_unlock_bh(&q->lock); } @@ -954,27 +988,27 @@ static int airoha_qdma_init_tx_queue(struct airoha_queue *q, dma_addr_t dma_addr; spin_lock_init(&q->lock); - q->ndesc = size; q->qdma = qdma; q->free_thr = 1 + MAX_SKB_FRAGS; INIT_LIST_HEAD(&q->tx_list); - q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), + q->entry = devm_kzalloc(eth->dev, size * sizeof(*q->entry), GFP_KERNEL); if (!q->entry) return -ENOMEM; - q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc), + q->desc = dmam_alloc_coherent(eth->dev, size * sizeof(*q->desc), &dma_addr, GFP_KERNEL); if (!q->desc) return -ENOMEM; - for (i = 0; i < q->ndesc; i++) { + for (i = 0; i < size; i++) { u32 val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1); list_add_tail(&q->entry[i].list, &q->tx_list); WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val)); } + q->ndesc = size; /* xmit ring drop default setting */ airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(qid), @@ -996,8 +1030,6 @@ static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q, struct airoha_eth *eth = qdma->eth; dma_addr_t dma_addr; - netif_napi_add_tx(eth->napi_dev, &irq_q->napi, - airoha_qdma_tx_napi_poll); irq_q->q = dmam_alloc_coherent(eth->dev, size * sizeof(u32), &dma_addr, GFP_KERNEL); if (!irq_q->q) @@ -1007,6 +1039,9 @@ static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q, irq_q->size = size; irq_q->qdma = qdma; + netif_napi_add_tx(eth->napi_dev, &irq_q->napi, + airoha_qdma_tx_napi_poll); + airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr); airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK, FIELD_PREP(TX_IRQ_DEPTH_MASK, size)); @@ -1039,12 +1074,15 @@ static int airoha_qdma_init_tx(struct airoha_qdma *qdma) static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q) { - struct airoha_eth *eth = q->qdma->eth; - int i; + struct airoha_qdma *qdma = q->qdma; + struct airoha_eth *eth = qdma->eth; + int i, qid = q - &qdma->q_tx[0]; + u16 index = 0; spin_lock_bh(&q->lock); for (i = 0; i < q->ndesc; i++) { struct airoha_queue_entry *e = &q->entry[i]; + struct airoha_qdma_desc *desc = &q->desc[i]; if (!e->dma_addr) continue; @@ -1055,8 +1093,33 @@ static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q) e->dma_addr = 0; e->skb = NULL; list_add_tail(&e->list, &q->tx_list); + + /* Reset DMA descriptor */ + WRITE_ONCE(desc->ctrl, 0); + WRITE_ONCE(desc->addr, 0); + WRITE_ONCE(desc->data, 0); + WRITE_ONCE(desc->msg0, 0); + WRITE_ONCE(desc->msg1, 0); + WRITE_ONCE(desc->msg2, 0); + q->queued--; } + + if (!list_empty(&q->tx_list)) { + struct airoha_queue_entry *e; + + e = list_first_entry(&q->tx_list, struct airoha_queue_entry, + list); + index = e - q->entry; + } + /* Set TX_DMA_IDX to TX_CPU_IDX to notify the hw the QDMA TX ring is + * empty. + */ + airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, + FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); + airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK, + FIELD_PREP(TX_RING_DMA_IDX_MASK, index)); + spin_unlock_bh(&q->lock); } @@ -1398,8 +1461,12 @@ static void airoha_qdma_cleanup(struct airoha_qdma *qdma) } } - for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) + for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { + if (!qdma->q_tx_irq[i].size) + continue; + netif_napi_del(&qdma->q_tx_irq[i].napi); + } for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { if (!qdma->q_tx[i].ndesc) @@ -1680,14 +1747,11 @@ static int airoha_dev_stop(struct net_device *dev) { struct airoha_gdm_port *port = netdev_priv(dev); struct airoha_qdma *qdma = port->qdma; - int i, err; + int i; netif_tx_disable(dev); - err = airoha_set_vip_for_gdm_port(port, false); - if (err) - return err; - - for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) + airoha_set_vip_for_gdm_port(port, false); + for (i = 0; i < dev->num_tx_queues; i++) netdev_tx_reset_subqueue(dev, i); airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), @@ -1727,7 +1791,7 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port) { struct airoha_eth *eth = port->qdma->eth; u32 val, pse_port, chan; - int src_port; + int i, src_port; /* Forward the traffic to the proper GDM port */ pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 @@ -1769,6 +1833,9 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port) SP_CPORT_MASK(val), __field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2)); + for (i = 0; i < eth->soc->num_ppe; i++) + airoha_ppe_set_cpu_port(port, i, AIROHA_GDM2_IDX); + if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) { u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq); @@ -1807,7 +1874,8 @@ static int airoha_dev_init(struct net_device *dev) } for (i = 0; i < eth->soc->num_ppe; i++) - airoha_ppe_set_cpu_port(port, i); + airoha_ppe_set_cpu_port(port, i, + airoha_get_fe_port(port)); return 0; } @@ -1936,12 +2004,12 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, struct netdev_queue *txq; struct airoha_queue *q; LIST_HEAD(tx_list); + int i = 0, qid; void *data; - int i, qid; u16 index; u8 fport; - qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx); + qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb)); tag = airoha_get_dsa_tag(skb, dev); msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK, @@ -1978,12 +2046,13 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, spin_lock_bh(&q->lock); - txq = netdev_get_tx_queue(dev, qid); + txq = skb_get_tx_queue(dev, skb); nr_frags = 1 + skb_shinfo(skb)->nr_frags; if (q->queued + nr_frags >= q->ndesc) { /* not enough space in the queue */ netif_tx_stop_queue(txq); + q->txq_stopped = true; spin_unlock_bh(&q->lock); return NETDEV_TX_BUSY; } @@ -1995,7 +2064,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, list); index = e - q->entry; - for (i = 0; i < nr_frags; i++) { + while (true) { struct airoha_qdma_desc *desc = &q->desc[index]; skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; dma_addr_t addr; @@ -2007,7 +2076,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, goto error_unmap; list_move_tail(&e->list, &tx_list); - e->skb = i ? NULL : skb; + e->skb = i == nr_frags - 1 ? skb : NULL; e->dma_addr = addr; e->dma_len = len; @@ -2026,6 +2095,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, WRITE_ONCE(desc->msg1, cpu_to_le32(msg1)); WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff)); + if (++i == nr_frags) + break; + data = skb_frag_address(frag); len = skb_frag_size(frag); } @@ -2033,28 +2105,27 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, skb_tx_timestamp(skb); netdev_tx_sent_queue(txq, skb->len); + if (q->ndesc - q->queued < q->free_thr) { + netif_tx_stop_queue(txq); + q->txq_stopped = true; + } if (netif_xmit_stopped(txq) || !netdev_xmit_more()) airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); - if (q->ndesc - q->queued < q->free_thr) - netif_tx_stop_queue(txq); - spin_unlock_bh(&q->lock); return NETDEV_TX_OK; error_unmap: - while (!list_empty(&tx_list)) { - e = list_first_entry(&tx_list, struct airoha_queue_entry, - list); + list_for_each_entry(e, &tx_list, list) { dma_unmap_single(dev->dev.parent, e->dma_addr, e->dma_len, DMA_TO_DEVICE); e->dma_addr = 0; - list_move_tail(&e->list, &q->tx_list); } + list_splice(&tx_list, &q->tx_list); spin_unlock_bh(&q->lock); error: diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index 95e557638617..4fad3acc3ccf 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -193,6 +193,7 @@ struct airoha_queue { int ndesc; int free_thr; int buf_size; + bool txq_stopped; struct napi_struct napi; struct page_pool *page_pool; @@ -630,6 +631,11 @@ u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val); #define airoha_qdma_clear(qdma, offset, val) \ airoha_rmw((qdma)->regs, (offset), (val), 0) +static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid) +{ + return qid % ARRAY_SIZE(qdma->q_tx); +} + static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port) { /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. @@ -653,7 +659,8 @@ int airoha_get_fe_port(struct airoha_gdm_port *port); bool airoha_is_valid_gdm_port(struct airoha_eth *eth, struct airoha_gdm_port *port); -void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id); +void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, + u8 fport); bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index); void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, u16 hash, bool rx_wlan); diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 03115c1c1063..5c9dff6bccd1 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -85,10 +85,9 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe) return FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, timestamp); } -void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id) +void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, u8 fport) { struct airoha_qdma *qdma = port->qdma; - u8 fport = airoha_get_fe_port(port); struct airoha_eth *eth = qdma->eth; u8 qdma_id = qdma - ð->qdma[0]; u32 fe_cpu_port; @@ -182,7 +181,8 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe) if (!port) continue; - airoha_ppe_set_cpu_port(port, i); + airoha_ppe_set_cpu_port(port, i, + airoha_get_fe_port(port)); } } } @@ -1356,6 +1356,29 @@ static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth) return npu; } +static int airoha_ppe_wait_for_npu_init(struct airoha_eth *eth) +{ + int err; + u32 val; + + /* PPE_FLOW_CFG default register value is 0. Since we reset FE + * during the device probe we can just check the configured value + * is not 0 here. + */ + err = read_poll_timeout(airoha_fe_rr, val, val, USEC_PER_MSEC, + 100 * USEC_PER_MSEC, false, eth, + REG_PPE_PPE_FLOW_CFG(0)); + if (err) + return err; + + if (airoha_ppe_is_enabled(eth, 1)) + err = read_poll_timeout(airoha_fe_rr, val, val, USEC_PER_MSEC, + 100 * USEC_PER_MSEC, false, eth, + REG_PPE_PPE_FLOW_CFG(1)); + + return err; +} + static int airoha_ppe_offload_setup(struct airoha_eth *eth) { struct airoha_npu *npu = airoha_ppe_npu_get(eth); @@ -1369,6 +1392,11 @@ static int airoha_ppe_offload_setup(struct airoha_eth *eth) if (err) goto error_npu_put; + /* Wait for NPU PPE configuration to complete */ + err = airoha_ppe_wait_for_npu_init(eth); + if (err) + goto error_npu_put; + ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe); if (ppe_num_stats_entries > 0) { err = npu->ops.ppe_init_stats(npu, ppe->foe_stats_dma, diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c index e67b592e5697..8c86789d867a 100644 --- a/drivers/net/ethernet/amazon/ena/ena_com.c +++ b/drivers/net/ethernet/amazon/ena/ena_com.c @@ -1782,20 +1782,23 @@ void ena_com_phc_destroy(struct ena_com_dev *ena_dev) int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp) { - volatile struct ena_admin_phc_resp *resp = ena_dev->phc.virt_addr; const ktime_t zero_system_time = ktime_set(0, 0); struct ena_com_phc_info *phc = &ena_dev->phc; + volatile struct ena_admin_phc_resp *resp; ktime_t expire_time; ktime_t block_time; unsigned long flags = 0; int ret = 0; + spin_lock_irqsave(&phc->lock, flags); + if (!phc->active) { + spin_unlock_irqrestore(&phc->lock, flags); netdev_err(ena_dev->net_device, "PHC feature is not active in the device\n"); return -EOPNOTSUPP; } - spin_lock_irqsave(&phc->lock, flags); + resp = ena_dev->phc.virt_addr; /* Check if PHC is in blocked state */ if (unlikely(ktime_compare(phc->system_time, zero_system_time))) { diff --git a/drivers/net/ethernet/amazon/ena/ena_phc.c b/drivers/net/ethernet/amazon/ena/ena_phc.c index 7867e893fd15..c2a3ff1ef645 100644 --- a/drivers/net/ethernet/amazon/ena/ena_phc.c +++ b/drivers/net/ethernet/amazon/ena/ena_phc.c @@ -46,9 +46,12 @@ static int ena_phc_gettimex64(struct ptp_clock_info *clock_info, spin_unlock_irqrestore(&phc_info->lock, flags); + if (rc) + return rc; + *ts = ns_to_timespec64(timestamp_nsec); - return rc; + return 0; } static int ena_phc_settime64(struct ptp_clock_info *clock_info, diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index 45e8d698781c..e35991141a1a 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -43,17 +43,6 @@ config AMD8111_ETH To compile this driver as a module, choose M here. The module will be called amd8111e. -config LANCE - tristate "AMD LANCE and PCnet (AT1500 and NE2100) support" - depends on ISA && ISA_DMA_API && !ARM && !PPC32 - select NETDEV_LEGACY_INIT - help - If you have a network (Ethernet) card of this type, say Y here. - Some LinkSys cards are of this type. - - To compile this driver as a module, choose M here: the module - will be called lance. This is recommended. - config PCNET32 tristate "AMD PCnet32 PCI support" depends on PCI && HAS_IOPORT @@ -120,16 +109,6 @@ config MVME147_NET driver for this chip in your kernel. To compile this driver as a module, choose M here. -config PCMCIA_NMCLAN - tristate "New Media PCMCIA support" - depends on PCMCIA && HAS_IOPORT - help - Say Y here if you intend to attach a New Media Ethernet or LiveWire - PCMCIA (PC-card) Ethernet card to your computer. - - To compile this driver as a module, choose M here: the module will be - called nmclan_cs. If unsure, say N. - config SUN3LANCE tristate "Sun3/Sun3x on-board LANCE support" depends on (SUN3 || SUN3X) diff --git a/drivers/net/ethernet/amd/Makefile b/drivers/net/ethernet/amd/Makefile index 2dcfb84731e1..e485fae235a7 100644 --- a/drivers/net/ethernet/amd/Makefile +++ b/drivers/net/ethernet/amd/Makefile @@ -9,10 +9,8 @@ obj-$(CONFIG_ARIADNE) += ariadne.o obj-$(CONFIG_ATARILANCE) += atarilance.o obj-$(CONFIG_DECLANCE) += declance.o obj-$(CONFIG_HPLANCE) += hplance.o 7990.o -obj-$(CONFIG_LANCE) += lance.o obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o -obj-$(CONFIG_PCMCIA_NMCLAN) += nmclan_cs.o obj-$(CONFIG_PCNET32) += pcnet32.o obj-$(CONFIG_SUN3LANCE) += sun3lance.o obj-$(CONFIG_SUNLANCE) += sunlance.o diff --git a/drivers/net/ethernet/amd/lance.c b/drivers/net/ethernet/amd/lance.c deleted file mode 100644 index 98afd8cb0efb..000000000000 --- a/drivers/net/ethernet/amd/lance.c +++ /dev/null @@ -1,1317 +0,0 @@ -/* lance.c: An AMD LANCE/PCnet ethernet driver for Linux. */ -/* - Written/copyright 1993-1998 by Donald Becker. - - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - This driver is for the Allied Telesis AT1500 and HP J2405A, and should work - with most other LANCE-based bus-master (NE2100/NE2500) ethercards. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - Andrey V. Savochkin: - - alignment problem with 1.3.* kernel and some minor changes. - Thomas Bogendoerfer (tsbogend@bigbug.franken.de): - - added support for Linux/Alpha, but removed most of it, because - it worked only for the PCI chip. - - added hook for the 32bit lance driver - - added PCnetPCI II (79C970A) to chip table - Paul Gortmaker (gpg109@rsphy1.anu.edu.au): - - hopefully fix above so Linux/Alpha can use ISA cards too. - 8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb - v1.12 10/27/97 Module support -djb - v1.14 2/3/98 Module support modified, made PCI support optional -djb - v1.15 5/27/99 Fixed bug in the cleanup_module(). dev->priv was freed - before unregister_netdev() which caused NULL pointer - reference later in the chain (in rtnetlink_fill_ifinfo()) - -- Mika Kuoppala - - Forward ported v1.14 to 2.1.129, merged the PCI and misc changes from - the 2.1 version of the old driver - Alan Cox - - Get rid of check_region, check kmalloc return in lance_probe1 - Arnaldo Carvalho de Melo - 11/01/2001 - - Reworked detection, added support for Racal InterLan EtherBlaster cards - Vesselin Kostadinov - 22/4/2004 -*/ - -static const char version[] = "lance.c:v1.16 2006/11/09 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n"; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static unsigned int lance_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0}; -static int lance_probe1(struct net_device *dev, int ioaddr, int irq, int options); -static int __init do_lance_probe(struct net_device *dev); - - -static struct card { - char id_offset14; - char id_offset15; -} cards[] = { - { //"normal" - .id_offset14 = 0x57, - .id_offset15 = 0x57, - }, - { //NI6510EB - .id_offset14 = 0x52, - .id_offset15 = 0x44, - }, - { //Racal InterLan EtherBlaster - .id_offset14 = 0x52, - .id_offset15 = 0x49, - }, -}; -#define NUM_CARDS 3 - -#ifdef LANCE_DEBUG -static int lance_debug = LANCE_DEBUG; -#else -static int lance_debug = 1; -#endif - -/* - Theory of Operation - -I. Board Compatibility - -This device driver is designed for the AMD 79C960, the "PCnet-ISA -single-chip ethernet controller for ISA". This chip is used in a wide -variety of boards from vendors such as Allied Telesis, HP, Kingston, -and Boca. This driver is also intended to work with older AMD 7990 -designs, such as the NE1500 and NE2100, and newer 79C961. For convenience, -I use the name LANCE to refer to all of the AMD chips, even though it properly -refers only to the original 7990. - -II. Board-specific settings - -The driver is designed to work the boards that use the faster -bus-master mode, rather than in shared memory mode. (Only older designs -have on-board buffer memory needed to support the slower shared memory mode.) - -Most ISA boards have jumpered settings for the I/O base, IRQ line, and DMA -channel. This driver probes the likely base addresses: -{0x300, 0x320, 0x340, 0x360}. -After the board is found it generates a DMA-timeout interrupt and uses -autoIRQ to find the IRQ line. The DMA channel can be set with the low bits -of the otherwise-unused dev->mem_start value (aka PARAM1). If unset it is -probed for by enabling each free DMA channel in turn and checking if -initialization succeeds. - -The HP-J2405A board is an exception: with this board it is easy to read the -EEPROM-set values for the base, IRQ, and DMA. (Of course you must already -_know_ the base address -- that field is for writing the EEPROM.) - -III. Driver operation - -IIIa. Ring buffers -The LANCE uses ring buffers of Tx and Rx descriptors. Each entry describes -the base and length of the data buffer, along with status bits. The length -of these buffers is set by LANCE_LOG_{RX,TX}_BUFFERS, which is log_2() of -the buffer length (rather than being directly the buffer length) for -implementation ease. The current values are 2 (Tx) and 4 (Rx), which leads to -ring sizes of 4 (Tx) and 16 (Rx). Increasing the number of ring entries -needlessly uses extra space and reduces the chance that an upper layer will -be able to reorder queued Tx packets based on priority. Decreasing the number -of entries makes it more difficult to achieve back-to-back packet transmission -and increases the chance that Rx ring will overflow. (Consider the worst case -of receiving back-to-back minimum-sized packets.) - -The LANCE has the capability to "chain" both Rx and Tx buffers, but this driver -statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to -avoid the administrative overhead. For the Rx side this avoids dynamically -allocating full-sized buffers "just in case", at the expense of a -memory-to-memory data copy for each packet received. For most systems this -is a good tradeoff: the Rx buffer will always be in low memory, the copy -is inexpensive, and it primes the cache for later packet processing. For Tx -the buffers are only used when needed as low-memory bounce buffers. - -IIIB. 16M memory limitations. -For the ISA bus master mode all structures used directly by the LANCE, -the initialization block, Rx and Tx rings, and data buffers, must be -accessible from the ISA bus, i.e. in the lower 16M of real memory. -This is a problem for current Linux kernels on >16M machines. The network -devices are initialized after memory initialization, and the kernel doles out -memory from the top of memory downward. The current solution is to have a -special network initialization routine that's called before memory -initialization; this will eventually be generalized for all network devices. -As mentioned before, low-memory "bounce-buffers" are used when needed. - -IIIC. Synchronization -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the -dev->tbusy flag. The other thread is the interrupt handler, which is single -threaded by the hardware and other software. - -The send packet thread has partial control over the Tx ring and 'dev->tbusy' -flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next -queue slot is empty, it clears the tbusy flag when finished otherwise it sets -the 'lp->tx_full' flag. - -The interrupt handler has exclusive control over the Rx ring and records stats -from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so -we can't avoid the interrupt overhead by having the Tx routine reap the Tx -stats.) After reaping the stats, it marks the queue entry as empty by setting -the 'base' to zero. Iff the 'lp->tx_full' flag is set, it clears both the -tx_full and tbusy flags. - -*/ - -/* Set the number of Tx and Rx buffers, using Log_2(# buffers). - Reasonable default values are 16 Tx buffers, and 16 Rx buffers. - That translates to 4 and 4 (16 == 2^^4). - This is a compile-time option for efficiency. - */ -#ifndef LANCE_LOG_TX_BUFFERS -#define LANCE_LOG_TX_BUFFERS 4 -#define LANCE_LOG_RX_BUFFERS 4 -#endif - -#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS)) -#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) -#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29) - -#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS)) -#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) -#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29) - -#define PKT_BUF_SZ 1544 - -/* Offsets from base I/O address. */ -#define LANCE_DATA 0x10 -#define LANCE_ADDR 0x12 -#define LANCE_RESET 0x14 -#define LANCE_BUS_IF 0x16 -#define LANCE_TOTAL_SIZE 0x18 - -#define TX_TIMEOUT (HZ/5) - -/* The LANCE Rx and Tx ring descriptors. */ -struct lance_rx_head { - s32 base; - s16 buf_length; /* This length is 2s complement (negative)! */ - s16 msg_length; /* This length is "normal". */ -}; - -struct lance_tx_head { - s32 base; - s16 length; /* Length is 2s complement (negative)! */ - s16 misc; -}; - -/* The LANCE initialization block, described in databook. */ -struct lance_init_block { - u16 mode; /* Pre-set mode (reg. 15) */ - u8 phys_addr[6]; /* Physical ethernet address */ - u32 filter[2]; /* Multicast filter (unused). */ - /* Receive and transmit ring base, along with extra bits. */ - u32 rx_ring; /* Tx and Rx ring base pointers */ - u32 tx_ring; -}; - -struct lance_private { - /* The Tx and Rx ring entries must be aligned on 8-byte boundaries. */ - struct lance_rx_head rx_ring[RX_RING_SIZE]; - struct lance_tx_head tx_ring[TX_RING_SIZE]; - struct lance_init_block init_block; - const char *name; - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff* tx_skbuff[TX_RING_SIZE]; - /* The addresses of receive-in-place skbuffs. */ - struct sk_buff* rx_skbuff[RX_RING_SIZE]; - unsigned long rx_buffs; /* Address of Rx and Tx buffers. */ - /* Tx low-memory "bounce buffer" address. */ - char (*tx_bounce_buffs)[PKT_BUF_SZ]; - int cur_rx, cur_tx; /* The next free ring entry */ - int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ - int dma; - unsigned char chip_version; /* See lance_chip_type. */ - spinlock_t devlock; -}; - -#define LANCE_MUST_PAD 0x00000001 -#define LANCE_ENABLE_AUTOSELECT 0x00000002 -#define LANCE_MUST_REINIT_RING 0x00000004 -#define LANCE_MUST_UNRESET 0x00000008 -#define LANCE_HAS_MISSED_FRAME 0x00000010 - -/* A mapping from the chip ID number to the part number and features. - These are from the datasheets -- in real life the '970 version - reportedly has the same ID as the '965. */ -static struct lance_chip_type { - int id_number; - const char *name; - int flags; -} chip_table[] = { - {0x0000, "LANCE 7990", /* Ancient lance chip. */ - LANCE_MUST_PAD + LANCE_MUST_UNRESET}, - {0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */ - LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + - LANCE_HAS_MISSED_FRAME}, - {0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */ - LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + - LANCE_HAS_MISSED_FRAME}, - {0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */ - LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + - LANCE_HAS_MISSED_FRAME}, - /* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call - it the PCnet32. */ - {0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */ - LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + - LANCE_HAS_MISSED_FRAME}, - {0x2621, "PCnet/PCI-II 79C970A", /* 79C970A PCInetPCI II. */ - LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + - LANCE_HAS_MISSED_FRAME}, - {0x0, "PCnet (unknown)", - LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + - LANCE_HAS_MISSED_FRAME}, -}; - -enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, PCNET_PCI_II=5, LANCE_UNKNOWN=6}; - - -/* Non-zero if lance_probe1() needs to allocate low-memory bounce buffers. - Assume yes until we know the memory size. */ -static unsigned char lance_need_isa_bounce_buffers = 1; - -static int lance_open(struct net_device *dev); -static void lance_init_ring(struct net_device *dev, gfp_t mode); -static netdev_tx_t lance_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static int lance_rx(struct net_device *dev); -static irqreturn_t lance_interrupt(int irq, void *dev_id); -static int lance_close(struct net_device *dev); -static struct net_device_stats *lance_get_stats(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static void lance_tx_timeout (struct net_device *dev, unsigned int txqueue); - - - -#ifdef MODULE -#define MAX_CARDS 8 /* Max number of interfaces (cards) per module */ - -static struct net_device *dev_lance[MAX_CARDS]; -static int io[MAX_CARDS]; -static int dma[MAX_CARDS]; -static int irq[MAX_CARDS]; - -module_param_hw_array(io, int, ioport, NULL, 0); -module_param_hw_array(dma, int, dma, NULL, 0); -module_param_hw_array(irq, int, irq, NULL, 0); -module_param(lance_debug, int, 0); -MODULE_PARM_DESC(io, "LANCE/PCnet I/O base address(es),required"); -MODULE_PARM_DESC(dma, "LANCE/PCnet ISA DMA channel (ignored for some devices)"); -MODULE_PARM_DESC(irq, "LANCE/PCnet IRQ number (ignored for some devices)"); -MODULE_PARM_DESC(lance_debug, "LANCE/PCnet debug level (0-7)"); - -static int __init lance_init_module(void) -{ - struct net_device *dev; - int this_dev, found = 0; - - for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) { - if (io[this_dev] == 0) { - if (this_dev != 0) /* only complain once */ - break; - printk(KERN_NOTICE "lance.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n"); - return -EPERM; - } - dev = alloc_etherdev(0); - if (!dev) - break; - dev->irq = irq[this_dev]; - dev->base_addr = io[this_dev]; - dev->dma = dma[this_dev]; - if (do_lance_probe(dev) == 0) { - dev_lance[found++] = dev; - continue; - } - free_netdev(dev); - break; - } - if (found != 0) - return 0; - return -ENXIO; -} -module_init(lance_init_module); - -static void cleanup_card(struct net_device *dev) -{ - struct lance_private *lp = dev->ml_priv; - if (dev->dma != 4) - free_dma(dev->dma); - release_region(dev->base_addr, LANCE_TOTAL_SIZE); - kfree(lp->tx_bounce_buffs); - kfree((void*)lp->rx_buffs); - kfree(lp); -} - -static void __exit lance_cleanup_module(void) -{ - int this_dev; - - for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) { - struct net_device *dev = dev_lance[this_dev]; - if (dev) { - unregister_netdev(dev); - cleanup_card(dev); - free_netdev(dev); - } - } -} -module_exit(lance_cleanup_module); -#endif /* MODULE */ -MODULE_DESCRIPTION("AMD LANCE/PCnet Ethernet driver"); -MODULE_LICENSE("GPL"); - - -/* Starting in v2.1.*, the LANCE/PCnet probe is now similar to the other - board probes now that kmalloc() can allocate ISA DMA-able regions. - This also allows the LANCE driver to be used as a module. - */ -static int __init do_lance_probe(struct net_device *dev) -{ - unsigned int *port; - int result; - - if (high_memory <= phys_to_virt(16*1024*1024)) - lance_need_isa_bounce_buffers = 0; - - for (port = lance_portlist; *port; port++) { - int ioaddr = *port; - struct resource *r = request_region(ioaddr, LANCE_TOTAL_SIZE, - "lance-probe"); - - if (r) { - /* Detect the card with minimal I/O reads */ - char offset14 = inb(ioaddr + 14); - int card; - for (card = 0; card < NUM_CARDS; ++card) - if (cards[card].id_offset14 == offset14) - break; - if (card < NUM_CARDS) {/*yes, the first byte matches*/ - char offset15 = inb(ioaddr + 15); - for (card = 0; card < NUM_CARDS; ++card) - if ((cards[card].id_offset14 == offset14) && - (cards[card].id_offset15 == offset15)) - break; - } - if (card < NUM_CARDS) { /*Signature OK*/ - result = lance_probe1(dev, ioaddr, 0, 0); - if (!result) { - struct lance_private *lp = dev->ml_priv; - int ver = lp->chip_version; - - r->name = chip_table[ver].name; - return 0; - } - } - release_region(ioaddr, LANCE_TOTAL_SIZE); - } - } - return -ENODEV; -} - -#ifndef MODULE -struct net_device * __init lance_probe(int unit) -{ - struct net_device *dev = alloc_etherdev(0); - int err; - - if (!dev) - return ERR_PTR(-ENODEV); - - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - - err = do_lance_probe(dev); - if (err) - goto out; - return dev; -out: - free_netdev(dev); - return ERR_PTR(err); -} -#endif - -static const struct net_device_ops lance_netdev_ops = { - .ndo_open = lance_open, - .ndo_start_xmit = lance_start_xmit, - .ndo_stop = lance_close, - .ndo_get_stats = lance_get_stats, - .ndo_set_rx_mode = set_multicast_list, - .ndo_tx_timeout = lance_tx_timeout, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options) -{ - struct lance_private *lp; - unsigned long dma_channels; /* Mark spuriously-busy DMA channels */ - int i, reset_val, lance_version; - const char *chipname; - /* Flags for specific chips or boards. */ - unsigned char hpJ2405A = 0; /* HP ISA adaptor */ - int hp_builtin = 0; /* HP on-board ethernet. */ - static int did_version; /* Already printed version info. */ - unsigned long flags; - int err = -ENOMEM; - void __iomem *bios; - u8 addr[ETH_ALEN]; - - /* First we look for special cases. - Check for HP's on-board ethernet by looking for 'HP' in the BIOS. - There are two HP versions, check the BIOS for the configuration port. - This method provided by L. Julliard, Laurent_Julliard@grenoble.hp.com. - */ - bios = ioremap(0xf00f0, 0x14); - if (!bios) - return -ENOMEM; - if (readw(bios + 0x12) == 0x5048) { - static const short ioaddr_table[] = { 0x300, 0x320, 0x340, 0x360}; - int hp_port = (readl(bios + 1) & 1) ? 0x499 : 0x99; - /* We can have boards other than the built-in! Verify this is on-board. */ - if ((inb(hp_port) & 0xc0) == 0x80 && - ioaddr_table[inb(hp_port) & 3] == ioaddr) - hp_builtin = hp_port; - } - iounmap(bios); - /* We also recognize the HP Vectra on-board here, but check below. */ - hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00 && - inb(ioaddr+2) == 0x09); - - /* Reset the LANCE. */ - reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */ - - /* The Un-Reset needed is only needed for the real NE2100, and will - confuse the HP board. */ - if (!hpJ2405A) - outw(reset_val, ioaddr+LANCE_RESET); - - outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */ - if (inw(ioaddr+LANCE_DATA) != 0x0004) - return -ENODEV; - - /* Get the version of the chip. */ - outw(88, ioaddr+LANCE_ADDR); - if (inw(ioaddr+LANCE_ADDR) != 88) { - lance_version = 0; - } else { /* Good, it's a newer chip. */ - int chip_version = inw(ioaddr+LANCE_DATA); - outw(89, ioaddr+LANCE_ADDR); - chip_version |= inw(ioaddr+LANCE_DATA) << 16; - if (lance_debug > 2) - printk(" LANCE chip version is %#x.\n", chip_version); - if ((chip_version & 0xfff) != 0x003) - return -ENODEV; - chip_version = (chip_version >> 12) & 0xffff; - for (lance_version = 1; chip_table[lance_version].id_number; lance_version++) { - if (chip_table[lance_version].id_number == chip_version) - break; - } - } - - /* We can't allocate private data from alloc_etherdev() because it must - a ISA DMA-able region. */ - chipname = chip_table[lance_version].name; - printk("%s: %s at %#3x, ", dev->name, chipname, ioaddr); - - /* There is a 16 byte station address PROM at the base address. - The first six bytes are the station address. */ - for (i = 0; i < 6; i++) - addr[i] = inb(ioaddr + i); - eth_hw_addr_set(dev, addr); - printk("%pM", dev->dev_addr); - - dev->base_addr = ioaddr; - /* Make certain the data structures used by the LANCE are aligned and DMAble. */ - - lp = kzalloc_obj(*lp, GFP_DMA | GFP_KERNEL); - if (!lp) - return -ENOMEM; - if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp); - dev->ml_priv = lp; - lp->name = chipname; - lp->rx_buffs = (unsigned long)kmalloc_array(RX_RING_SIZE, PKT_BUF_SZ, - GFP_DMA | GFP_KERNEL); - if (!lp->rx_buffs) - goto out_lp; - if (lance_need_isa_bounce_buffers) { - lp->tx_bounce_buffs = kmalloc_array(TX_RING_SIZE, PKT_BUF_SZ, - GFP_DMA | GFP_KERNEL); - if (!lp->tx_bounce_buffs) - goto out_rx; - } else - lp->tx_bounce_buffs = NULL; - - lp->chip_version = lance_version; - spin_lock_init(&lp->devlock); - - lp->init_block.mode = 0x0003; /* Disable Rx and Tx. */ - for (i = 0; i < 6; i++) - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - lp->init_block.filter[0] = 0x00000000; - lp->init_block.filter[1] = 0x00000000; - lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS; - lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS; - - outw(0x0001, ioaddr+LANCE_ADDR); - inw(ioaddr+LANCE_ADDR); - outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA); - outw(0x0002, ioaddr+LANCE_ADDR); - inw(ioaddr+LANCE_ADDR); - outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA); - outw(0x0000, ioaddr+LANCE_ADDR); - inw(ioaddr+LANCE_ADDR); - - if (irq) { /* Set iff PCI card. */ - dev->dma = 4; /* Native bus-master, no DMA channel needed. */ - dev->irq = irq; - } else if (hp_builtin) { - static const char dma_tbl[4] = {3, 5, 6, 0}; - static const char irq_tbl[4] = {3, 4, 5, 9}; - unsigned char port_val = inb(hp_builtin); - dev->dma = dma_tbl[(port_val >> 4) & 3]; - dev->irq = irq_tbl[(port_val >> 2) & 3]; - printk(" HP Vectra IRQ %d DMA %d.\n", dev->irq, dev->dma); - } else if (hpJ2405A) { - static const char dma_tbl[4] = {3, 5, 6, 7}; - static const char irq_tbl[8] = {3, 4, 5, 9, 10, 11, 12, 15}; - short reset_val = inw(ioaddr+LANCE_RESET); - dev->dma = dma_tbl[(reset_val >> 2) & 3]; - dev->irq = irq_tbl[(reset_val >> 4) & 7]; - printk(" HP J2405A IRQ %d DMA %d.\n", dev->irq, dev->dma); - } else if (lance_version == PCNET_ISAP) { /* The plug-n-play version. */ - short bus_info; - outw(8, ioaddr+LANCE_ADDR); - bus_info = inw(ioaddr+LANCE_BUS_IF); - dev->dma = bus_info & 0x07; - dev->irq = (bus_info >> 4) & 0x0F; - } else { - /* The DMA channel may be passed in PARAM1. */ - if (dev->mem_start & 0x07) - dev->dma = dev->mem_start & 0x07; - } - - if (dev->dma == 0) { - /* Read the DMA channel status register, so that we can avoid - stuck DMA channels in the DMA detection below. */ - dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) | - (inb(DMA2_STAT_REG) & 0xf0); - } - err = -ENODEV; - if (dev->irq >= 2) - printk(" assigned IRQ %d", dev->irq); - else if (lance_version != 0) { /* 7990 boards need DMA detection first. */ - unsigned long irq_mask; - - /* To auto-IRQ we enable the initialization-done and DMA error - interrupts. For ISA boards we get a DMA error, but VLB and PCI - boards will work. */ - irq_mask = probe_irq_on(); - - /* Trigger an initialization just for the interrupt. */ - outw(0x0041, ioaddr+LANCE_DATA); - - mdelay(20); - dev->irq = probe_irq_off(irq_mask); - if (dev->irq) - printk(", probed IRQ %d", dev->irq); - else { - printk(", failed to detect IRQ line.\n"); - goto out_tx; - } - - /* Check for the initialization done bit, 0x0100, which means - that we don't need a DMA channel. */ - if (inw(ioaddr+LANCE_DATA) & 0x0100) - dev->dma = 4; - } - - if (dev->dma == 4) { - printk(", no DMA needed.\n"); - } else if (dev->dma) { - if (request_dma(dev->dma, chipname)) { - printk("DMA %d allocation failed.\n", dev->dma); - goto out_tx; - } else - printk(", assigned DMA %d.\n", dev->dma); - } else { /* OK, we have to auto-DMA. */ - for (i = 0; i < 4; i++) { - static const char dmas[] = { 5, 6, 7, 3 }; - int dma = dmas[i]; - int boguscnt; - - /* Don't enable a permanently busy DMA channel, or the machine - will hang. */ - if (test_bit(dma, &dma_channels)) - continue; - outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */ - if (request_dma(dma, chipname)) - continue; - - flags=claim_dma_lock(); - set_dma_mode(dma, DMA_MODE_CASCADE); - enable_dma(dma); - release_dma_lock(flags); - - /* Trigger an initialization. */ - outw(0x0001, ioaddr+LANCE_DATA); - for (boguscnt = 100; boguscnt > 0; --boguscnt) - if (inw(ioaddr+LANCE_DATA) & 0x0900) - break; - if (inw(ioaddr+LANCE_DATA) & 0x0100) { - dev->dma = dma; - printk(", DMA %d.\n", dev->dma); - break; - } else { - flags=claim_dma_lock(); - disable_dma(dma); - release_dma_lock(flags); - free_dma(dma); - } - } - if (i == 4) { /* Failure: bail. */ - printk("DMA detection failed.\n"); - goto out_tx; - } - } - - if (lance_version == 0 && dev->irq == 0) { - /* We may auto-IRQ now that we have a DMA channel. */ - /* Trigger an initialization just for the interrupt. */ - unsigned long irq_mask; - - irq_mask = probe_irq_on(); - outw(0x0041, ioaddr+LANCE_DATA); - - mdelay(40); - dev->irq = probe_irq_off(irq_mask); - if (dev->irq == 0) { - printk(" Failed to detect the 7990 IRQ line.\n"); - goto out_dma; - } - printk(" Auto-IRQ detected IRQ%d.\n", dev->irq); - } - - if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) { - /* Turn on auto-select of media (10baseT or BNC) so that the user - can watch the LEDs even if the board isn't opened. */ - outw(0x0002, ioaddr+LANCE_ADDR); - /* Don't touch 10base2 power bit. */ - outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF); - } - - if (lance_debug > 0 && did_version++ == 0) - printk(version); - - /* The LANCE-specific entries in the device structure. */ - dev->netdev_ops = &lance_netdev_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - err = register_netdev(dev); - if (err) - goto out_dma; - return 0; -out_dma: - if (dev->dma != 4) - free_dma(dev->dma); -out_tx: - kfree(lp->tx_bounce_buffs); -out_rx: - kfree((void*)lp->rx_buffs); -out_lp: - kfree(lp); - return err; -} - - -static int -lance_open(struct net_device *dev) -{ - struct lance_private *lp = dev->ml_priv; - int ioaddr = dev->base_addr; - int i; - - if (dev->irq == 0 || - request_irq(dev->irq, lance_interrupt, 0, dev->name, dev)) { - return -EAGAIN; - } - - /* We used to allocate DMA here, but that was silly. - DMA lines can't be shared! We now permanently allocate them. */ - - /* Reset the LANCE */ - inw(ioaddr+LANCE_RESET); - - /* The DMA controller is used as a no-operation slave, "cascade mode". */ - if (dev->dma != 4) { - unsigned long flags=claim_dma_lock(); - enable_dma(dev->dma); - set_dma_mode(dev->dma, DMA_MODE_CASCADE); - release_dma_lock(flags); - } - - /* Un-Reset the LANCE, needed only for the NE2100. */ - if (chip_table[lp->chip_version].flags & LANCE_MUST_UNRESET) - outw(0, ioaddr+LANCE_RESET); - - if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) { - /* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */ - outw(0x0002, ioaddr+LANCE_ADDR); - /* Only touch autoselect bit. */ - outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF); - } - - if (lance_debug > 1) - printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n", - dev->name, dev->irq, dev->dma, - (u32) isa_virt_to_bus(lp->tx_ring), - (u32) isa_virt_to_bus(lp->rx_ring), - (u32) isa_virt_to_bus(&lp->init_block)); - - lance_init_ring(dev, GFP_KERNEL); - /* Re-initialize the LANCE, and start it when done. */ - outw(0x0001, ioaddr+LANCE_ADDR); - outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA); - outw(0x0002, ioaddr+LANCE_ADDR); - outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA); - - outw(0x0004, ioaddr+LANCE_ADDR); - outw(0x0915, ioaddr+LANCE_DATA); - - outw(0x0000, ioaddr+LANCE_ADDR); - outw(0x0001, ioaddr+LANCE_DATA); - - netif_start_queue (dev); - - i = 0; - while (i++ < 100) - if (inw(ioaddr+LANCE_DATA) & 0x0100) - break; - /* - * We used to clear the InitDone bit, 0x0100, here but Mark Stockton - * reports that doing so triggers a bug in the '974. - */ - outw(0x0042, ioaddr+LANCE_DATA); - - if (lance_debug > 2) - printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n", - dev->name, i, (u32) isa_virt_to_bus(&lp->init_block), inw(ioaddr+LANCE_DATA)); - - return 0; /* Always succeed */ -} - -/* The LANCE has been halted for one reason or another (busmaster memory - arbitration error, Tx FIFO underflow, driver stopped it to reconfigure, - etc.). Modern LANCE variants always reload their ring-buffer - configuration when restarted, so we must reinitialize our ring - context before restarting. As part of this reinitialization, - find all packets still on the Tx ring and pretend that they had been - sent (in effect, drop the packets on the floor) - the higher-level - protocols will time out and retransmit. It'd be better to shuffle - these skbs to a temp list and then actually re-Tx them after - restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com -*/ - -static void -lance_purge_ring(struct net_device *dev) -{ - struct lance_private *lp = dev->ml_priv; - int i; - - /* Free all the skbuffs in the Rx and Tx queues. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = lp->rx_skbuff[i]; - lp->rx_skbuff[i] = NULL; - lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */ - if (skb) - dev_kfree_skb_any(skb); - } - for (i = 0; i < TX_RING_SIZE; i++) { - if (lp->tx_skbuff[i]) { - dev_kfree_skb_any(lp->tx_skbuff[i]); - lp->tx_skbuff[i] = NULL; - } - } -} - - -/* Initialize the LANCE Rx and Tx rings. */ -static void -lance_init_ring(struct net_device *dev, gfp_t gfp) -{ - struct lance_private *lp = dev->ml_priv; - int i; - - lp->cur_rx = lp->cur_tx = 0; - lp->dirty_rx = lp->dirty_tx = 0; - - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb; - void *rx_buff; - - skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | gfp); - lp->rx_skbuff[i] = skb; - if (skb) - rx_buff = skb->data; - else - rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp); - if (!rx_buff) - lp->rx_ring[i].base = 0; - else - lp->rx_ring[i].base = (u32)isa_virt_to_bus(rx_buff) | 0x80000000; - lp->rx_ring[i].buf_length = -PKT_BUF_SZ; - } - /* The Tx buffer address is filled in as needed, but we do need to clear - the upper ownership bit. */ - for (i = 0; i < TX_RING_SIZE; i++) { - lp->tx_skbuff[i] = NULL; - lp->tx_ring[i].base = 0; - } - - lp->init_block.mode = 0x0000; - for (i = 0; i < 6; i++) - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - lp->init_block.filter[0] = 0x00000000; - lp->init_block.filter[1] = 0x00000000; - lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS; - lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS; -} - -static void -lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit) -{ - struct lance_private *lp = dev->ml_priv; - - if (must_reinit || - (chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) { - lance_purge_ring(dev); - lance_init_ring(dev, GFP_ATOMIC); - } - outw(0x0000, dev->base_addr + LANCE_ADDR); - outw(csr0_bits, dev->base_addr + LANCE_DATA); -} - - -static void lance_tx_timeout (struct net_device *dev, unsigned int txqueue) -{ - struct lance_private *lp = (struct lance_private *) dev->ml_priv; - int ioaddr = dev->base_addr; - - outw (0, ioaddr + LANCE_ADDR); - printk ("%s: transmit timed out, status %4.4x, resetting.\n", - dev->name, inw (ioaddr + LANCE_DATA)); - outw (0x0004, ioaddr + LANCE_DATA); - dev->stats.tx_errors++; -#ifndef final_version - if (lance_debug > 3) { - int i; - printk (" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.", - lp->dirty_tx, lp->cur_tx, netif_queue_stopped(dev) ? " (full)" : "", - lp->cur_rx); - for (i = 0; i < RX_RING_SIZE; i++) - printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ", - lp->rx_ring[i].base, -lp->rx_ring[i].buf_length, - lp->rx_ring[i].msg_length); - for (i = 0; i < TX_RING_SIZE; i++) - printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ", - lp->tx_ring[i].base, -lp->tx_ring[i].length, - lp->tx_ring[i].misc); - printk ("\n"); - } -#endif - lance_restart (dev, 0x0043, 1); - - netif_trans_update(dev); /* prevent tx timeout */ - netif_wake_queue (dev); -} - - -static netdev_tx_t lance_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct lance_private *lp = dev->ml_priv; - int ioaddr = dev->base_addr; - int entry; - unsigned long flags; - - spin_lock_irqsave(&lp->devlock, flags); - - if (lance_debug > 3) { - outw(0x0000, ioaddr+LANCE_ADDR); - printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name, - inw(ioaddr+LANCE_DATA)); - outw(0x0000, ioaddr+LANCE_DATA); - } - - /* Fill in a Tx ring entry */ - - /* Mask to ring buffer boundary. */ - entry = lp->cur_tx & TX_RING_MOD_MASK; - - /* Caution: the write order is important here, set the base address - with the "ownership" bits last. */ - - /* The old LANCE chips doesn't automatically pad buffers to min. size. */ - if (chip_table[lp->chip_version].flags & LANCE_MUST_PAD) { - if (skb->len < ETH_ZLEN) { - if (skb_padto(skb, ETH_ZLEN)) - goto out; - lp->tx_ring[entry].length = -ETH_ZLEN; - } - else - lp->tx_ring[entry].length = -skb->len; - } else - lp->tx_ring[entry].length = -skb->len; - - lp->tx_ring[entry].misc = 0x0000; - - dev->stats.tx_bytes += skb->len; - - /* If any part of this buffer is >16M we must copy it to a low-memory - buffer. */ - if ((u32)isa_virt_to_bus(skb->data) + skb->len > 0x01000000) { - if (lance_debug > 5) - printk("%s: bouncing a high-memory packet (%#x).\n", - dev->name, (u32)isa_virt_to_bus(skb->data)); - skb_copy_from_linear_data(skb, &lp->tx_bounce_buffs[entry], skb->len); - lp->tx_ring[entry].base = - ((u32)isa_virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000; - dev_consume_skb_irq(skb); - } else { - lp->tx_skbuff[entry] = skb; - lp->tx_ring[entry].base = ((u32)isa_virt_to_bus(skb->data) & 0xffffff) | 0x83000000; - } - lp->cur_tx++; - - /* Trigger an immediate send poll. */ - outw(0x0000, ioaddr+LANCE_ADDR); - outw(0x0048, ioaddr+LANCE_DATA); - - if ((lp->cur_tx - lp->dirty_tx) >= TX_RING_SIZE) - netif_stop_queue(dev); - -out: - spin_unlock_irqrestore(&lp->devlock, flags); - return NETDEV_TX_OK; -} - -/* The LANCE interrupt handler. */ -static irqreturn_t lance_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct lance_private *lp; - int csr0, ioaddr, boguscnt=10; - int must_restart; - - ioaddr = dev->base_addr; - lp = dev->ml_priv; - - spin_lock (&lp->devlock); - - outw(0x00, dev->base_addr + LANCE_ADDR); - while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600 && - --boguscnt >= 0) { - /* Acknowledge all of the current interrupt sources ASAP. */ - outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA); - - must_restart = 0; - - if (lance_debug > 5) - printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n", - dev->name, csr0, inw(dev->base_addr + LANCE_DATA)); - - if (csr0 & 0x0400) /* Rx interrupt */ - lance_rx(dev); - - if (csr0 & 0x0200) { /* Tx-done interrupt */ - int dirty_tx = lp->dirty_tx; - - while (dirty_tx < lp->cur_tx) { - int entry = dirty_tx & TX_RING_MOD_MASK; - int status = lp->tx_ring[entry].base; - - if (status < 0) - break; /* It still hasn't been Txed */ - - lp->tx_ring[entry].base = 0; - - if (status & 0x40000000) { - /* There was an major error, log it. */ - int err_status = lp->tx_ring[entry].misc; - dev->stats.tx_errors++; - if (err_status & 0x0400) - dev->stats.tx_aborted_errors++; - if (err_status & 0x0800) - dev->stats.tx_carrier_errors++; - if (err_status & 0x1000) - dev->stats.tx_window_errors++; - if (err_status & 0x4000) { - /* Ackk! On FIFO errors the Tx unit is turned off! */ - dev->stats.tx_fifo_errors++; - /* Remove this verbosity later! */ - printk("%s: Tx FIFO error! Status %4.4x.\n", - dev->name, csr0); - /* Restart the chip. */ - must_restart = 1; - } - } else { - if (status & 0x18000000) - dev->stats.collisions++; - dev->stats.tx_packets++; - } - - /* We must free the original skb if it's not a data-only copy - in the bounce buffer. */ - if (lp->tx_skbuff[entry]) { - dev_consume_skb_irq(lp->tx_skbuff[entry]); - lp->tx_skbuff[entry] = NULL; - } - dirty_tx++; - } - -#ifndef final_version - if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { - printk("out-of-sync dirty pointer, %d vs. %d, full=%s.\n", - dirty_tx, lp->cur_tx, - netif_queue_stopped(dev) ? "yes" : "no"); - dirty_tx += TX_RING_SIZE; - } -#endif - - /* if the ring is no longer full, accept more packets */ - if (netif_queue_stopped(dev) && - dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) - netif_wake_queue (dev); - - lp->dirty_tx = dirty_tx; - } - - /* Log misc errors. */ - if (csr0 & 0x4000) - dev->stats.tx_errors++; /* Tx babble. */ - if (csr0 & 0x1000) - dev->stats.rx_errors++; /* Missed a Rx frame. */ - if (csr0 & 0x0800) { - printk("%s: Bus master arbitration failure, status %4.4x.\n", - dev->name, csr0); - /* Restart the chip. */ - must_restart = 1; - } - - if (must_restart) { - /* stop the chip to clear the error condition, then restart */ - outw(0x0000, dev->base_addr + LANCE_ADDR); - outw(0x0004, dev->base_addr + LANCE_DATA); - lance_restart(dev, 0x0002, 0); - } - } - - /* Clear any other interrupt, and set interrupt enable. */ - outw(0x0000, dev->base_addr + LANCE_ADDR); - outw(0x7940, dev->base_addr + LANCE_DATA); - - if (lance_debug > 4) - printk("%s: exiting interrupt, csr%d=%#4.4x.\n", - dev->name, inw(ioaddr + LANCE_ADDR), - inw(dev->base_addr + LANCE_DATA)); - - spin_unlock (&lp->devlock); - return IRQ_HANDLED; -} - -static int -lance_rx(struct net_device *dev) -{ - struct lance_private *lp = dev->ml_priv; - int entry = lp->cur_rx & RX_RING_MOD_MASK; - int i; - - /* If we own the next entry, it's a new packet. Send it up. */ - while (lp->rx_ring[entry].base >= 0) { - int status = lp->rx_ring[entry].base >> 24; - - if (status != 0x03) { /* There was an error. */ - /* There is a tricky error noted by John Murphy, - to Russ Nelson: Even with full-sized - buffers it's possible for a jabber packet to use two - buffers, with only the last correctly noting the error. */ - if (status & 0x01) /* Only count a general error at the */ - dev->stats.rx_errors++; /* end of a packet.*/ - if (status & 0x20) - dev->stats.rx_frame_errors++; - if (status & 0x10) - dev->stats.rx_over_errors++; - if (status & 0x08) - dev->stats.rx_crc_errors++; - if (status & 0x04) - dev->stats.rx_fifo_errors++; - lp->rx_ring[entry].base &= 0x03ffffff; - } - else - { - /* Malloc up new buffer, compatible with net3. */ - short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4; - struct sk_buff *skb; - - if(pkt_len<60) - { - printk("%s: Runt packet!\n",dev->name); - dev->stats.rx_errors++; - } - else - { - skb = dev_alloc_skb(pkt_len+2); - if (!skb) - { - printk("%s: Memory squeeze, deferring packet.\n", dev->name); - for (i=0; i < RX_RING_SIZE; i++) - if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0) - break; - - if (i > RX_RING_SIZE -2) - { - dev->stats.rx_dropped++; - lp->rx_ring[entry].base |= 0x80000000; - lp->cur_rx++; - } - break; - } - skb_reserve(skb,2); /* 16 byte align */ - skb_put(skb,pkt_len); /* Make room */ - skb_copy_to_linear_data(skb, - (unsigned char *)isa_bus_to_virt((lp->rx_ring[entry].base & 0x00ffffff)), - pkt_len); - skb->protocol=eth_type_trans(skb,dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } - } - /* The docs say that the buffer length isn't touched, but Andrew Boyd - of QNX reports that some revs of the 79C965 clear it. */ - lp->rx_ring[entry].buf_length = -PKT_BUF_SZ; - lp->rx_ring[entry].base |= 0x80000000; - entry = (++lp->cur_rx) & RX_RING_MOD_MASK; - } - - /* We should check that at least two ring entries are free. If not, - we should free one and mark stats->rx_dropped++. */ - - return 0; -} - -static int -lance_close(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - struct lance_private *lp = dev->ml_priv; - - netif_stop_queue (dev); - - if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) { - outw(112, ioaddr+LANCE_ADDR); - dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); - } - outw(0, ioaddr+LANCE_ADDR); - - if (lance_debug > 1) - printk("%s: Shutting down ethercard, status was %2.2x.\n", - dev->name, inw(ioaddr+LANCE_DATA)); - - /* We stop the LANCE here -- it occasionally polls - memory if we don't. */ - outw(0x0004, ioaddr+LANCE_DATA); - - if (dev->dma != 4) - { - unsigned long flags=claim_dma_lock(); - disable_dma(dev->dma); - release_dma_lock(flags); - } - free_irq(dev->irq, dev); - - lance_purge_ring(dev); - - return 0; -} - -static struct net_device_stats *lance_get_stats(struct net_device *dev) -{ - struct lance_private *lp = dev->ml_priv; - - if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) { - short ioaddr = dev->base_addr; - short saved_addr; - unsigned long flags; - - spin_lock_irqsave(&lp->devlock, flags); - saved_addr = inw(ioaddr+LANCE_ADDR); - outw(112, ioaddr+LANCE_ADDR); - dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); - outw(saved_addr, ioaddr+LANCE_ADDR); - spin_unlock_irqrestore(&lp->devlock, flags); - } - - return &dev->stats; -} - -/* Set or clear the multicast filter for this adaptor. - */ - -static void set_multicast_list(struct net_device *dev) -{ - short ioaddr = dev->base_addr; - - outw(0, ioaddr+LANCE_ADDR); - outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance. */ - - if (dev->flags&IFF_PROMISC) { - outw(15, ioaddr+LANCE_ADDR); - outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */ - } else { - short multicast_table[4]; - int i; - int num_addrs=netdev_mc_count(dev); - if(dev->flags&IFF_ALLMULTI) - num_addrs=1; - /* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */ - memset(multicast_table, (num_addrs == 0) ? 0 : -1, sizeof(multicast_table)); - for (i = 0; i < 4; i++) { - outw(8 + i, ioaddr+LANCE_ADDR); - outw(multicast_table[i], ioaddr+LANCE_DATA); - } - outw(15, ioaddr+LANCE_ADDR); - outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */ - } - - lance_restart(dev, 0x0142, 0); /* Resume normal operation */ - -} - diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c deleted file mode 100644 index 37054a670407..000000000000 --- a/drivers/net/ethernet/amd/nmclan_cs.c +++ /dev/null @@ -1,1508 +0,0 @@ -/* ---------------------------------------------------------------------------- -Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN. - nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao - - The Ethernet LAN uses the Advanced Micro Devices (AMD) Am79C940 Media - Access Controller for Ethernet (MACE). It is essentially the Am2150 - PCMCIA Ethernet card contained in the Am2150 Demo Kit. - -Written by Roger C. Pao - Copyright 1995 Roger C. Pao - Linux 2.5 cleanups Copyright Red Hat 2003 - - This software may be used and distributed according to the terms of - the GNU General Public License. - -Ported to Linux 1.3.* network driver environment by - Matti Aarnio - -References - - Am2150 Technical Reference Manual, Revision 1.0, August 17, 1993 - Am79C940 (MACE) Data Sheet, 1994 - Am79C90 (C-LANCE) Data Sheet, 1994 - Linux PCMCIA Programmer's Guide v1.17 - /usr/src/linux/net/inet/dev.c, Linux kernel 1.2.8 - - Eric Mears, New Media Corporation - Tom Pollard, New Media Corporation - Dean Siasoyco, New Media Corporation - Ken Lesniak, Silicon Graphics, Inc. - Donald Becker - David Hinds - - The Linux client driver is based on the 3c589_cs.c client driver by - David Hinds. - - The Linux network driver outline is based on the 3c589_cs.c driver, - the 8390.c driver, and the example skeleton.c kernel code, which are - by Donald Becker. - - The Am2150 network driver hardware interface code is based on the - OS/9000 driver for the New Media Ethernet LAN by Eric Mears. - - Special thanks for testing and help in debugging this driver goes - to Ken Lesniak. - -------------------------------------------------------------------------------- -Driver Notes and Issues -------------------------------------------------------------------------------- - -1. Developed on a Dell 320SLi - PCMCIA Card Services 2.6.2 - Linux dell 1.2.10 #1 Thu Jun 29 20:23:41 PDT 1995 i386 - -2. rc.pcmcia may require loading pcmcia_core with io_speed=300: - 'insmod pcmcia_core.o io_speed=300'. - This will avoid problems with fast systems which causes rx_framecnt - to return random values. - -3. If hot extraction does not work for you, use 'ifconfig eth0 down' - before extraction. - -4. There is a bad slow-down problem in this driver. - -5. Future: Multicast processing. In the meantime, do _not_ compile your - kernel with multicast ip enabled. - -------------------------------------------------------------------------------- -History -------------------------------------------------------------------------------- -Log: nmclan_cs.c,v - * 2.5.75-ac1 2003/07/11 Alan Cox - * Fixed hang on card eject as we probe it - * Cleaned up to use new style locking. - * - * Revision 0.16 1995/07/01 06:42:17 rpao - * Bug fix: nmclan_reset() called CardServices incorrectly. - * - * Revision 0.15 1995/05/24 08:09:47 rpao - * Re-implement MULTI_TX dev->tbusy handling. - * - * Revision 0.14 1995/05/23 03:19:30 rpao - * Added, in nmclan_config(), "tuple.Attributes = 0;". - * Modified MACE ID check to ignore chip revision level. - * Avoid tx_free_frames race condition between _start_xmit and _interrupt. - * - * Revision 0.13 1995/05/18 05:56:34 rpao - * Statistics changes. - * Bug fix: nmclan_reset did not enable TX and RX: call restore_multicast_list. - * Bug fix: mace_interrupt checks ~MACE_IMR_DEFAULT. Fixes driver lockup. - * - * Revision 0.12 1995/05/14 00:12:23 rpao - * Statistics overhaul. - * - -95/05/13 rpao V0.10a - Bug fix: MACE statistics counters used wrong I/O ports. - Bug fix: mace_interrupt() needed to allow statistics to be - processed without RX or TX interrupts pending. -95/05/11 rpao V0.10 - Multiple transmit request processing. - Modified statistics to use MACE counters where possible. -95/05/10 rpao V0.09 Bug fix: Must use IO_DATA_PATH_WIDTH_AUTO. - *Released -95/05/10 rpao V0.08 - Bug fix: Make all non-exported functions private by using - static keyword. - Bug fix: Test IntrCnt _before_ reading MACE_IR. -95/05/10 rpao V0.07 Statistics. -95/05/09 rpao V0.06 Fix rx_framecnt problem by addition of PCIC wait states. - ----------------------------------------------------------------------------- */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define DRV_NAME "nmclan_cs" - -/* ---------------------------------------------------------------------------- -Conditional Compilation Options ----------------------------------------------------------------------------- */ - -#define MULTI_TX 0 -#define RESET_ON_TIMEOUT 1 -#define TX_INTERRUPTABLE 1 -#define RESET_XILINX 0 - -/* ---------------------------------------------------------------------------- -Include Files ----------------------------------------------------------------------------- */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -/* ---------------------------------------------------------------------------- -Defines ----------------------------------------------------------------------------- */ - -#define MACE_LADRF_LEN 8 - /* 8 bytes in Logical Address Filter */ - -/* Loop Control Defines */ -#define MACE_MAX_IR_ITERATIONS 10 -#define MACE_MAX_RX_ITERATIONS 12 - /* - TBD: Dean brought this up, and I assumed the hardware would - handle it: - - If MACE_MAX_RX_ITERATIONS is > 1, rx_framecnt may still be - non-zero when the isr exits. We may not get another interrupt - to process the remaining packets for some time. - */ - -/* -The Am2150 has a Xilinx XC3042 field programmable gate array (FPGA) -which manages the interface between the MACE and the PCMCIA bus. It -also includes buffer management for the 32K x 8 SRAM to control up to -four transmit and 12 receive frames at a time. -*/ -#define AM2150_MAX_TX_FRAMES 4 -#define AM2150_MAX_RX_FRAMES 12 - -/* Am2150 Ethernet Card I/O Mapping */ -#define AM2150_RCV 0x00 -#define AM2150_XMT 0x04 -#define AM2150_XMT_SKIP 0x09 -#define AM2150_RCV_NEXT 0x0A -#define AM2150_RCV_FRAME_COUNT 0x0B -#define AM2150_MACE_BANK 0x0C -#define AM2150_MACE_BASE 0x10 - -/* MACE Registers */ -#define MACE_RCVFIFO 0 -#define MACE_XMTFIFO 1 -#define MACE_XMTFC 2 -#define MACE_XMTFS 3 -#define MACE_XMTRC 4 -#define MACE_RCVFC 5 -#define MACE_RCVFS 6 -#define MACE_FIFOFC 7 -#define MACE_IR 8 -#define MACE_IMR 9 -#define MACE_PR 10 -#define MACE_BIUCC 11 -#define MACE_FIFOCC 12 -#define MACE_MACCC 13 -#define MACE_PLSCC 14 -#define MACE_PHYCC 15 -#define MACE_CHIPIDL 16 -#define MACE_CHIPIDH 17 -#define MACE_IAC 18 -/* Reserved */ -#define MACE_LADRF 20 -#define MACE_PADR 21 -/* Reserved */ -/* Reserved */ -#define MACE_MPC 24 -/* Reserved */ -#define MACE_RNTPC 26 -#define MACE_RCVCC 27 -/* Reserved */ -#define MACE_UTR 29 -#define MACE_RTR1 30 -#define MACE_RTR2 31 - -/* MACE Bit Masks */ -#define MACE_XMTRC_EXDEF 0x80 -#define MACE_XMTRC_XMTRC 0x0F - -#define MACE_XMTFS_XMTSV 0x80 -#define MACE_XMTFS_UFLO 0x40 -#define MACE_XMTFS_LCOL 0x20 -#define MACE_XMTFS_MORE 0x10 -#define MACE_XMTFS_ONE 0x08 -#define MACE_XMTFS_DEFER 0x04 -#define MACE_XMTFS_LCAR 0x02 -#define MACE_XMTFS_RTRY 0x01 - -#define MACE_RCVFS_RCVSTS 0xF000 -#define MACE_RCVFS_OFLO 0x8000 -#define MACE_RCVFS_CLSN 0x4000 -#define MACE_RCVFS_FRAM 0x2000 -#define MACE_RCVFS_FCS 0x1000 - -#define MACE_FIFOFC_RCVFC 0xF0 -#define MACE_FIFOFC_XMTFC 0x0F - -#define MACE_IR_JAB 0x80 -#define MACE_IR_BABL 0x40 -#define MACE_IR_CERR 0x20 -#define MACE_IR_RCVCCO 0x10 -#define MACE_IR_RNTPCO 0x08 -#define MACE_IR_MPCO 0x04 -#define MACE_IR_RCVINT 0x02 -#define MACE_IR_XMTINT 0x01 - -#define MACE_MACCC_PROM 0x80 -#define MACE_MACCC_DXMT2PD 0x40 -#define MACE_MACCC_EMBA 0x20 -#define MACE_MACCC_RESERVED 0x10 -#define MACE_MACCC_DRCVPA 0x08 -#define MACE_MACCC_DRCVBC 0x04 -#define MACE_MACCC_ENXMT 0x02 -#define MACE_MACCC_ENRCV 0x01 - -#define MACE_PHYCC_LNKFL 0x80 -#define MACE_PHYCC_DLNKTST 0x40 -#define MACE_PHYCC_REVPOL 0x20 -#define MACE_PHYCC_DAPC 0x10 -#define MACE_PHYCC_LRT 0x08 -#define MACE_PHYCC_ASEL 0x04 -#define MACE_PHYCC_RWAKE 0x02 -#define MACE_PHYCC_AWAKE 0x01 - -#define MACE_IAC_ADDRCHG 0x80 -#define MACE_IAC_PHYADDR 0x04 -#define MACE_IAC_LOGADDR 0x02 - -#define MACE_UTR_RTRE 0x80 -#define MACE_UTR_RTRD 0x40 -#define MACE_UTR_RPA 0x20 -#define MACE_UTR_FCOLL 0x10 -#define MACE_UTR_RCVFCSE 0x08 -#define MACE_UTR_LOOP_INCL_MENDEC 0x06 -#define MACE_UTR_LOOP_NO_MENDEC 0x04 -#define MACE_UTR_LOOP_EXTERNAL 0x02 -#define MACE_UTR_LOOP_NONE 0x00 -#define MACE_UTR_RESERVED 0x01 - -/* Switch MACE register bank (only 0 and 1 are valid) */ -#define MACEBANK(win_num) outb((win_num), ioaddr + AM2150_MACE_BANK) - -#define MACE_IMR_DEFAULT \ - (0xFF - \ - ( \ - MACE_IR_CERR | \ - MACE_IR_RCVCCO | \ - MACE_IR_RNTPCO | \ - MACE_IR_MPCO | \ - MACE_IR_RCVINT | \ - MACE_IR_XMTINT \ - ) \ - ) -#undef MACE_IMR_DEFAULT -#define MACE_IMR_DEFAULT 0x00 /* New statistics handling: grab everything */ - -#define TX_TIMEOUT ((400*HZ)/1000) - -/* ---------------------------------------------------------------------------- -Type Definitions ----------------------------------------------------------------------------- */ - -typedef struct _mace_statistics { - /* MACE_XMTFS */ - int xmtsv; - int uflo; - int lcol; - int more; - int one; - int defer; - int lcar; - int rtry; - - /* MACE_XMTRC */ - int exdef; - int xmtrc; - - /* RFS1--Receive Status (RCVSTS) */ - int oflo; - int clsn; - int fram; - int fcs; - - /* RFS2--Runt Packet Count (RNTPC) */ - int rfs_rntpc; - - /* RFS3--Receive Collision Count (RCVCC) */ - int rfs_rcvcc; - - /* MACE_IR */ - int jab; - int babl; - int cerr; - int rcvcco; - int rntpco; - int mpco; - - /* MACE_MPC */ - int mpc; - - /* MACE_RNTPC */ - int rntpc; - - /* MACE_RCVCC */ - int rcvcc; -} mace_statistics; - -typedef struct _mace_private { - struct pcmcia_device *p_dev; - mace_statistics mace_stats; /* MACE chip statistics counters */ - - /* restore_multicast_list() state variables */ - int multicast_ladrf[MACE_LADRF_LEN]; /* Logical address filter */ - int multicast_num_addrs; - - char tx_free_frames; /* Number of free transmit frame buffers */ - char tx_irq_disabled; /* MACE TX interrupt disabled */ - - spinlock_t bank_lock; /* Must be held if you step off bank 0 */ -} mace_private; - -/* ---------------------------------------------------------------------------- -Private Global Variables ----------------------------------------------------------------------------- */ - -static const char *if_names[]={ - "Auto", "10baseT", "BNC", -}; - -/* ---------------------------------------------------------------------------- -Parameters - These are the parameters that can be set during loading with - 'insmod'. ----------------------------------------------------------------------------- */ - -MODULE_DESCRIPTION("New Media PCMCIA ethernet driver"); -MODULE_LICENSE("GPL"); - -#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) - -/* 0=auto, 1=10baseT, 2 = 10base2, default=auto */ -INT_MODULE_PARM(if_port, 0); - - -/* ---------------------------------------------------------------------------- -Function Prototypes ----------------------------------------------------------------------------- */ - -static int nmclan_config(struct pcmcia_device *link); -static void nmclan_release(struct pcmcia_device *link); - -static void nmclan_reset(struct net_device *dev); -static int mace_config(struct net_device *dev, struct ifmap *map); -static int mace_open(struct net_device *dev); -static int mace_close(struct net_device *dev); -static netdev_tx_t mace_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue); -static irqreturn_t mace_interrupt(int irq, void *dev_id); -static struct net_device_stats *mace_get_stats(struct net_device *dev); -static int mace_rx(struct net_device *dev, unsigned char RxCnt); -static void restore_multicast_list(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static const struct ethtool_ops netdev_ethtool_ops; - - -static void nmclan_detach(struct pcmcia_device *p_dev); - -static const struct net_device_ops mace_netdev_ops = { - .ndo_open = mace_open, - .ndo_stop = mace_close, - .ndo_start_xmit = mace_start_xmit, - .ndo_tx_timeout = mace_tx_timeout, - .ndo_set_config = mace_config, - .ndo_get_stats = mace_get_stats, - .ndo_set_rx_mode = set_multicast_list, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int nmclan_probe(struct pcmcia_device *link) -{ - mace_private *lp; - struct net_device *dev; - - dev_dbg(&link->dev, "nmclan_attach()\n"); - - /* Create new ethernet device */ - dev = alloc_etherdev(sizeof(mace_private)); - if (!dev) - return -ENOMEM; - lp = netdev_priv(dev); - lp->p_dev = link; - link->priv = dev; - - spin_lock_init(&lp->bank_lock); - link->resource[0]->end = 32; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->config_flags |= CONF_ENABLE_IRQ; - link->config_index = 1; - link->config_regs = PRESENT_OPTION; - - lp->tx_free_frames=AM2150_MAX_TX_FRAMES; - - dev->netdev_ops = &mace_netdev_ops; - dev->ethtool_ops = &netdev_ethtool_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - return nmclan_config(link); -} /* nmclan_attach */ - -static void nmclan_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - dev_dbg(&link->dev, "nmclan_detach\n"); - - unregister_netdev(dev); - - nmclan_release(link); - - free_netdev(dev); -} /* nmclan_detach */ - -/* ---------------------------------------------------------------------------- -mace_read - Reads a MACE register. This is bank independent; however, the - caller must ensure that this call is not interruptable. We are - assuming that during normal operation, the MACE is always in - bank 0. ----------------------------------------------------------------------------- */ -static int mace_read(mace_private *lp, unsigned int ioaddr, int reg) -{ - int data = 0xFF; - unsigned long flags; - - switch (reg >> 4) { - case 0: /* register 0-15 */ - data = inb(ioaddr + AM2150_MACE_BASE + reg); - break; - case 1: /* register 16-31 */ - spin_lock_irqsave(&lp->bank_lock, flags); - MACEBANK(1); - data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); - MACEBANK(0); - spin_unlock_irqrestore(&lp->bank_lock, flags); - break; - } - return data & 0xFF; -} /* mace_read */ - -/* ---------------------------------------------------------------------------- -mace_write - Writes to a MACE register. This is bank independent; however, - the caller must ensure that this call is not interruptable. We - are assuming that during normal operation, the MACE is always in - bank 0. ----------------------------------------------------------------------------- */ -static void mace_write(mace_private *lp, unsigned int ioaddr, int reg, - int data) -{ - unsigned long flags; - - switch (reg >> 4) { - case 0: /* register 0-15 */ - outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg); - break; - case 1: /* register 16-31 */ - spin_lock_irqsave(&lp->bank_lock, flags); - MACEBANK(1); - outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); - MACEBANK(0); - spin_unlock_irqrestore(&lp->bank_lock, flags); - break; - } -} /* mace_write */ - -/* ---------------------------------------------------------------------------- -mace_init - Resets the MACE chip. ----------------------------------------------------------------------------- */ -static int mace_init(mace_private *lp, unsigned int ioaddr, - const char *enet_addr) -{ - int i; - int ct = 0; - - /* MACE Software reset */ - mace_write(lp, ioaddr, MACE_BIUCC, 1); - while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) { - /* Wait for reset bit to be cleared automatically after <= 200ns */; - if(++ct > 500) - { - pr_err("reset failed, card removed?\n"); - return -1; - } - udelay(1); - } - mace_write(lp, ioaddr, MACE_BIUCC, 0); - - /* The Am2150 requires that the MACE FIFOs operate in burst mode. */ - mace_write(lp, ioaddr, MACE_FIFOCC, 0x0F); - - mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */ - mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */ - - /* - * Bit 2-1 PORTSEL[1-0] Port Select. - * 00 AUI/10Base-2 - * 01 10Base-T - * 10 DAI Port (reserved in Am2150) - * 11 GPSI - * For this card, only the first two are valid. - * So, PLSCC should be set to - * 0x00 for 10Base-2 - * 0x02 for 10Base-T - * Or just set ASEL in PHYCC below! - */ - switch (if_port) { - case 1: - mace_write(lp, ioaddr, MACE_PLSCC, 0x02); - break; - case 2: - mace_write(lp, ioaddr, MACE_PLSCC, 0x00); - break; - default: - mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4); - /* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden, - and the MACE device will automatically select the operating media - interface port. */ - break; - } - - mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR); - /* Poll ADDRCHG bit */ - ct = 0; - while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) - { - if(++ ct > 500) - { - pr_err("ADDRCHG timeout, card removed?\n"); - return -1; - } - } - /* Set PADR register */ - for (i = 0; i < ETH_ALEN; i++) - mace_write(lp, ioaddr, MACE_PADR, enet_addr[i]); - - /* MAC Configuration Control Register should be written last */ - /* Let set_multicast_list set this. */ - /* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */ - mace_write(lp, ioaddr, MACE_MACCC, 0x00); - return 0; -} /* mace_init */ - -static int nmclan_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - mace_private *lp = netdev_priv(dev); - u8 *buf; - size_t len; - int i, ret; - unsigned int ioaddr; - - dev_dbg(&link->dev, "nmclan_config\n"); - - link->io_lines = 5; - ret = pcmcia_request_io(link); - if (ret) - goto failed; - ret = pcmcia_request_irq(link, mace_interrupt); - if (ret) - goto failed; - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - dev->irq = link->irq; - dev->base_addr = link->resource[0]->start; - - ioaddr = dev->base_addr; - - /* Read the ethernet address from the CIS. */ - len = pcmcia_get_tuple(link, 0x80, &buf); - if (!buf || len < ETH_ALEN) { - kfree(buf); - goto failed; - } - eth_hw_addr_set(dev, buf); - kfree(buf); - - /* Verify configuration by reading the MACE ID. */ - { - char sig[2]; - - sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL); - sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH); - if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) { - dev_dbg(&link->dev, "nmclan_cs configured: mace id=%x %x\n", - sig[0], sig[1]); - } else { - pr_notice("mace id not found: %x %x should be 0x40 0x?9\n", - sig[0], sig[1]); - goto failed; - } - } - - if(mace_init(lp, ioaddr, dev->dev_addr) == -1) - goto failed; - - /* The if_port symbol can be set when the module is loaded */ - if (if_port <= 2) - dev->if_port = if_port; - else - pr_notice("invalid if_port requested\n"); - - SET_NETDEV_DEV(dev, &link->dev); - - i = register_netdev(dev); - if (i != 0) { - pr_notice("register_netdev() failed\n"); - goto failed; - } - - netdev_info(dev, "nmclan: port %#3lx, irq %d, %s port, hw_addr %pM\n", - dev->base_addr, dev->irq, if_names[dev->if_port], dev->dev_addr); - return 0; - -failed: - nmclan_release(link); - return -ENODEV; -} /* nmclan_config */ - -static void nmclan_release(struct pcmcia_device *link) -{ - dev_dbg(&link->dev, "nmclan_release\n"); - pcmcia_disable_device(link); -} - -static int nmclan_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int nmclan_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) { - nmclan_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - - -/* ---------------------------------------------------------------------------- -nmclan_reset - Reset and restore all of the Xilinx and MACE registers. ----------------------------------------------------------------------------- */ -static void nmclan_reset(struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - -#if RESET_XILINX - struct pcmcia_device *link = &lp->link; - u8 OrigCorValue; - - /* Save original COR value */ - pcmcia_read_config_byte(link, CISREG_COR, &OrigCorValue); - - /* Reset Xilinx */ - dev_dbg(&link->dev, "nmclan_reset: OrigCorValue=0x%x, resetting...\n", - OrigCorValue); - pcmcia_write_config_byte(link, CISREG_COR, COR_SOFT_RESET); - /* Need to wait for 20 ms for PCMCIA to finish reset. */ - - /* Restore original COR configuration index */ - pcmcia_write_config_byte(link, CISREG_COR, - (COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK))); - /* Xilinx is now completely reset along with the MACE chip. */ - lp->tx_free_frames=AM2150_MAX_TX_FRAMES; - -#endif /* #if RESET_XILINX */ - - /* Xilinx is now completely reset along with the MACE chip. */ - lp->tx_free_frames=AM2150_MAX_TX_FRAMES; - - /* Reinitialize the MACE chip for operation. */ - mace_init(lp, dev->base_addr, dev->dev_addr); - mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT); - - /* Restore the multicast list and enable TX and RX. */ - restore_multicast_list(dev); -} /* nmclan_reset */ - -/* ---------------------------------------------------------------------------- -mace_config - [Someone tell me what this is supposed to do? Is if_port a defined - standard? If so, there should be defines to indicate 1=10Base-T, - 2=10Base-2, etc. including limited automatic detection.] ----------------------------------------------------------------------------- */ -static int mace_config(struct net_device *dev, struct ifmap *map) -{ - if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { - if (map->port <= 2) { - WRITE_ONCE(dev->if_port, map->port); - netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]); - } else - return -EINVAL; - } - return 0; -} /* mace_config */ - -/* ---------------------------------------------------------------------------- -mace_open - Open device driver. ----------------------------------------------------------------------------- */ -static int mace_open(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - mace_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - if (!pcmcia_dev_present(link)) - return -ENODEV; - - link->open++; - - MACEBANK(0); - - netif_start_queue(dev); - nmclan_reset(dev); - - return 0; /* Always succeed */ -} /* mace_open */ - -/* ---------------------------------------------------------------------------- -mace_close - Closes device driver. ----------------------------------------------------------------------------- */ -static int mace_close(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - mace_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name); - - /* Mask off all interrupts from the MACE chip. */ - outb(0xFF, ioaddr + AM2150_MACE_BASE + MACE_IMR); - - link->open--; - netif_stop_queue(dev); - - return 0; -} /* mace_close */ - -static void netdev_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - snprintf(info->bus_info, sizeof(info->bus_info), - "PCMCIA 0x%lx", dev->base_addr); -} - -static const struct ethtool_ops netdev_ethtool_ops = { - .get_drvinfo = netdev_get_drvinfo, -}; - -/* ---------------------------------------------------------------------------- -mace_start_xmit - This routine begins the packet transmit function. When completed, - it will generate a transmit interrupt. - - According to /usr/src/linux/net/inet/dev.c, if _start_xmit - returns 0, the "packet is now solely the responsibility of the - driver." If _start_xmit returns non-zero, the "transmission - failed, put skb back into a list." ----------------------------------------------------------------------------- */ - -static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - mace_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - netdev_notice(dev, "transmit timed out -- "); -#if RESET_ON_TIMEOUT - pr_cont("resetting card\n"); - pcmcia_reset_card(link->socket); -#else /* #if RESET_ON_TIMEOUT */ - pr_cont("NOT resetting card\n"); -#endif /* #if RESET_ON_TIMEOUT */ - netif_trans_update(dev); /* prevent tx timeout */ - netif_wake_queue(dev); -} - -static netdev_tx_t mace_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - - netif_stop_queue(dev); - - pr_debug("%s: mace_start_xmit(length = %ld) called.\n", - dev->name, (long)skb->len); - -#if (!TX_INTERRUPTABLE) - /* Disable MACE TX interrupts. */ - outb(MACE_IMR_DEFAULT | MACE_IR_XMTINT, - ioaddr + AM2150_MACE_BASE + MACE_IMR); - lp->tx_irq_disabled=1; -#endif /* #if (!TX_INTERRUPTABLE) */ - - { - /* This block must not be interrupted by another transmit request! - mace_tx_timeout will take care of timer-based retransmissions from - the upper layers. The interrupt handler is guaranteed never to - service a transmit interrupt while we are in here. - */ - - dev->stats.tx_bytes += skb->len; - lp->tx_free_frames--; - - /* WARNING: Write the _exact_ number of bytes written in the header! */ - /* Put out the word header [must be an outw()] . . . */ - outw(skb->len, ioaddr + AM2150_XMT); - /* . . . and the packet [may be any combination of outw() and outb()] */ - outsw(ioaddr + AM2150_XMT, skb->data, skb->len >> 1); - if (skb->len & 1) { - /* Odd byte transfer */ - outb(skb->data[skb->len-1], ioaddr + AM2150_XMT); - } - -#if MULTI_TX - if (lp->tx_free_frames > 0) - netif_start_queue(dev); -#endif /* #if MULTI_TX */ - } - -#if (!TX_INTERRUPTABLE) - /* Re-enable MACE TX interrupts. */ - lp->tx_irq_disabled=0; - outb(MACE_IMR_DEFAULT, ioaddr + AM2150_MACE_BASE + MACE_IMR); -#endif /* #if (!TX_INTERRUPTABLE) */ - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} /* mace_start_xmit */ - -/* ---------------------------------------------------------------------------- -mace_interrupt - The interrupt handler. ----------------------------------------------------------------------------- */ -static irqreturn_t mace_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *) dev_id; - mace_private *lp = netdev_priv(dev); - unsigned int ioaddr; - int status; - int IntrCnt = MACE_MAX_IR_ITERATIONS; - - if (!dev) { - pr_debug("mace_interrupt(): irq 0x%X for unknown device.\n", - irq); - return IRQ_NONE; - } - - ioaddr = dev->base_addr; - - if (lp->tx_irq_disabled) { - const char *msg; - if (lp->tx_irq_disabled) - msg = "Interrupt with tx_irq_disabled"; - else - msg = "Re-entering the interrupt handler"; - netdev_notice(dev, "%s [isr=%02X, imr=%02X]\n", - msg, - inb(ioaddr + AM2150_MACE_BASE + MACE_IR), - inb(ioaddr + AM2150_MACE_BASE + MACE_IMR)); - /* WARNING: MACE_IR has been read! */ - return IRQ_NONE; - } - - if (!netif_device_present(dev)) { - netdev_dbg(dev, "interrupt from dead card\n"); - return IRQ_NONE; - } - - do { - /* WARNING: MACE_IR is a READ/CLEAR port! */ - status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR); - if (!(status & ~MACE_IMR_DEFAULT) && IntrCnt == MACE_MAX_IR_ITERATIONS) - return IRQ_NONE; - - pr_debug("mace_interrupt: irq 0x%X status 0x%X.\n", irq, status); - - if (status & MACE_IR_RCVINT) { - mace_rx(dev, MACE_MAX_RX_ITERATIONS); - } - - if (status & MACE_IR_XMTINT) { - unsigned char fifofc; - unsigned char xmtrc; - unsigned char xmtfs; - - fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC); - if ((fifofc & MACE_FIFOFC_XMTFC)==0) { - dev->stats.tx_errors++; - outb(0xFF, ioaddr + AM2150_XMT_SKIP); - } - - /* Transmit Retry Count (XMTRC, reg 4) */ - xmtrc = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTRC); - if (xmtrc & MACE_XMTRC_EXDEF) lp->mace_stats.exdef++; - lp->mace_stats.xmtrc += (xmtrc & MACE_XMTRC_XMTRC); - - if ( - (xmtfs = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTFS)) & - MACE_XMTFS_XMTSV /* Transmit Status Valid */ - ) { - lp->mace_stats.xmtsv++; - - if (xmtfs & ~MACE_XMTFS_XMTSV) { - if (xmtfs & MACE_XMTFS_UFLO) { - /* Underflow. Indicates that the Transmit FIFO emptied before - the end of frame was reached. */ - lp->mace_stats.uflo++; - } - if (xmtfs & MACE_XMTFS_LCOL) { - /* Late Collision */ - lp->mace_stats.lcol++; - } - if (xmtfs & MACE_XMTFS_MORE) { - /* MORE than one retry was needed */ - lp->mace_stats.more++; - } - if (xmtfs & MACE_XMTFS_ONE) { - /* Exactly ONE retry occurred */ - lp->mace_stats.one++; - } - if (xmtfs & MACE_XMTFS_DEFER) { - /* Transmission was defered */ - lp->mace_stats.defer++; - } - if (xmtfs & MACE_XMTFS_LCAR) { - /* Loss of carrier */ - lp->mace_stats.lcar++; - } - if (xmtfs & MACE_XMTFS_RTRY) { - /* Retry error: transmit aborted after 16 attempts */ - lp->mace_stats.rtry++; - } - } /* if (xmtfs & ~MACE_XMTFS_XMTSV) */ - - } /* if (xmtfs & MACE_XMTFS_XMTSV) */ - - dev->stats.tx_packets++; - lp->tx_free_frames++; - netif_wake_queue(dev); - } /* if (status & MACE_IR_XMTINT) */ - - if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) { - if (status & MACE_IR_JAB) { - /* Jabber Error. Excessive transmit duration (20-150ms). */ - lp->mace_stats.jab++; - } - if (status & MACE_IR_BABL) { - /* Babble Error. >1518 bytes transmitted. */ - lp->mace_stats.babl++; - } - if (status & MACE_IR_CERR) { - /* Collision Error. CERR indicates the absence of the - Signal Quality Error Test message after a packet - transmission. */ - lp->mace_stats.cerr++; - } - if (status & MACE_IR_RCVCCO) { - /* Receive Collision Count Overflow; */ - lp->mace_stats.rcvcco++; - } - if (status & MACE_IR_RNTPCO) { - /* Runt Packet Count Overflow */ - lp->mace_stats.rntpco++; - } - if (status & MACE_IR_MPCO) { - /* Missed Packet Count Overflow */ - lp->mace_stats.mpco++; - } - } /* if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) */ - - } while ((status & ~MACE_IMR_DEFAULT) && (--IntrCnt)); - - return IRQ_HANDLED; -} /* mace_interrupt */ - -/* ---------------------------------------------------------------------------- -mace_rx - Receives packets. ----------------------------------------------------------------------------- */ -static int mace_rx(struct net_device *dev, unsigned char RxCnt) -{ - mace_private *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - unsigned char rx_framecnt; - unsigned short rx_status; - - while ( - ((rx_framecnt = inb(ioaddr + AM2150_RCV_FRAME_COUNT)) > 0) && - (rx_framecnt <= 12) && /* rx_framecnt==0xFF if card is extracted. */ - (RxCnt--) - ) { - rx_status = inw(ioaddr + AM2150_RCV); - - pr_debug("%s: in mace_rx(), framecnt 0x%X, rx_status" - " 0x%X.\n", dev->name, rx_framecnt, rx_status); - - if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */ - dev->stats.rx_errors++; - if (rx_status & MACE_RCVFS_OFLO) { - lp->mace_stats.oflo++; - } - if (rx_status & MACE_RCVFS_CLSN) { - lp->mace_stats.clsn++; - } - if (rx_status & MACE_RCVFS_FRAM) { - lp->mace_stats.fram++; - } - if (rx_status & MACE_RCVFS_FCS) { - lp->mace_stats.fcs++; - } - } else { - short pkt_len = (rx_status & ~MACE_RCVFS_RCVSTS) - 4; - /* Auto Strip is off, always subtract 4 */ - struct sk_buff *skb; - - lp->mace_stats.rfs_rntpc += inb(ioaddr + AM2150_RCV); - /* runt packet count */ - lp->mace_stats.rfs_rcvcc += inb(ioaddr + AM2150_RCV); - /* rcv collision count */ - - pr_debug(" receiving packet size 0x%X rx_status" - " 0x%X.\n", pkt_len, rx_status); - - skb = netdev_alloc_skb(dev, pkt_len + 2); - - if (skb) { - skb_reserve(skb, 2); - insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1); - if (pkt_len & 1) - *(skb_tail_pointer(skb) - 1) = inb(ioaddr + AM2150_RCV); - skb->protocol = eth_type_trans(skb, dev); - - netif_rx(skb); /* Send the packet to the upper (protocol) layers. */ - - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */ - continue; - } else { - pr_debug("%s: couldn't allocate a sk_buff of size" - " %d.\n", dev->name, pkt_len); - dev->stats.rx_dropped++; - } - } - outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */ - } /* while */ - - return 0; -} /* mace_rx */ - -/* ---------------------------------------------------------------------------- -pr_linux_stats ----------------------------------------------------------------------------- */ -static void pr_linux_stats(struct net_device_stats *pstats) -{ - pr_debug("pr_linux_stats\n"); - pr_debug(" rx_packets=%-7ld tx_packets=%ld\n", - (long)pstats->rx_packets, (long)pstats->tx_packets); - pr_debug(" rx_errors=%-7ld tx_errors=%ld\n", - (long)pstats->rx_errors, (long)pstats->tx_errors); - pr_debug(" rx_dropped=%-7ld tx_dropped=%ld\n", - (long)pstats->rx_dropped, (long)pstats->tx_dropped); - pr_debug(" multicast=%-7ld collisions=%ld\n", - (long)pstats->multicast, (long)pstats->collisions); - - pr_debug(" rx_length_errors=%-7ld rx_over_errors=%ld\n", - (long)pstats->rx_length_errors, (long)pstats->rx_over_errors); - pr_debug(" rx_crc_errors=%-7ld rx_frame_errors=%ld\n", - (long)pstats->rx_crc_errors, (long)pstats->rx_frame_errors); - pr_debug(" rx_fifo_errors=%-7ld rx_missed_errors=%ld\n", - (long)pstats->rx_fifo_errors, (long)pstats->rx_missed_errors); - - pr_debug(" tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n", - (long)pstats->tx_aborted_errors, (long)pstats->tx_carrier_errors); - pr_debug(" tx_fifo_errors=%-7ld tx_heartbeat_errors=%ld\n", - (long)pstats->tx_fifo_errors, (long)pstats->tx_heartbeat_errors); - pr_debug(" tx_window_errors=%ld\n", - (long)pstats->tx_window_errors); -} /* pr_linux_stats */ - -/* ---------------------------------------------------------------------------- -pr_mace_stats ----------------------------------------------------------------------------- */ -static void pr_mace_stats(mace_statistics *pstats) -{ - pr_debug("pr_mace_stats\n"); - - pr_debug(" xmtsv=%-7d uflo=%d\n", - pstats->xmtsv, pstats->uflo); - pr_debug(" lcol=%-7d more=%d\n", - pstats->lcol, pstats->more); - pr_debug(" one=%-7d defer=%d\n", - pstats->one, pstats->defer); - pr_debug(" lcar=%-7d rtry=%d\n", - pstats->lcar, pstats->rtry); - - /* MACE_XMTRC */ - pr_debug(" exdef=%-7d xmtrc=%d\n", - pstats->exdef, pstats->xmtrc); - - /* RFS1--Receive Status (RCVSTS) */ - pr_debug(" oflo=%-7d clsn=%d\n", - pstats->oflo, pstats->clsn); - pr_debug(" fram=%-7d fcs=%d\n", - pstats->fram, pstats->fcs); - - /* RFS2--Runt Packet Count (RNTPC) */ - /* RFS3--Receive Collision Count (RCVCC) */ - pr_debug(" rfs_rntpc=%-7d rfs_rcvcc=%d\n", - pstats->rfs_rntpc, pstats->rfs_rcvcc); - - /* MACE_IR */ - pr_debug(" jab=%-7d babl=%d\n", - pstats->jab, pstats->babl); - pr_debug(" cerr=%-7d rcvcco=%d\n", - pstats->cerr, pstats->rcvcco); - pr_debug(" rntpco=%-7d mpco=%d\n", - pstats->rntpco, pstats->mpco); - - /* MACE_MPC */ - pr_debug(" mpc=%d\n", pstats->mpc); - - /* MACE_RNTPC */ - pr_debug(" rntpc=%d\n", pstats->rntpc); - - /* MACE_RCVCC */ - pr_debug(" rcvcc=%d\n", pstats->rcvcc); - -} /* pr_mace_stats */ - -/* ---------------------------------------------------------------------------- -update_stats - Update statistics. We change to register window 1, so this - should be run single-threaded if the device is active. This is - expected to be a rare operation, and it's simpler for the rest - of the driver to assume that window 0 is always valid rather - than use a special window-state variable. - - oflo & uflo should _never_ occur since it would mean the Xilinx - was not able to transfer data between the MACE FIFO and the - card's SRAM fast enough. If this happens, something is - seriously wrong with the hardware. ----------------------------------------------------------------------------- */ -static void update_stats(unsigned int ioaddr, struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - - lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC); - lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC); - lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC); - /* At this point, mace_stats is fully updated for this call. - We may now update the netdev stats. */ - - /* The MACE has no equivalent for netdev stats field which are commented - out. */ - - /* dev->stats.multicast; */ - dev->stats.collisions = - lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc; - /* Collision: The MACE may retry sending a packet 15 times - before giving up. The retry count is in XMTRC. - Does each retry constitute a collision? - If so, why doesn't the RCVCC record these collisions? */ - - /* detailed rx_errors: */ - dev->stats.rx_length_errors = - lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc; - /* dev->stats.rx_over_errors */ - dev->stats.rx_crc_errors = lp->mace_stats.fcs; - dev->stats.rx_frame_errors = lp->mace_stats.fram; - dev->stats.rx_fifo_errors = lp->mace_stats.oflo; - dev->stats.rx_missed_errors = - lp->mace_stats.mpco * 256 + lp->mace_stats.mpc; - - /* detailed tx_errors */ - dev->stats.tx_aborted_errors = lp->mace_stats.rtry; - dev->stats.tx_carrier_errors = lp->mace_stats.lcar; - /* LCAR usually results from bad cabling. */ - dev->stats.tx_fifo_errors = lp->mace_stats.uflo; - dev->stats.tx_heartbeat_errors = lp->mace_stats.cerr; - /* dev->stats.tx_window_errors; */ -} /* update_stats */ - -/* ---------------------------------------------------------------------------- -mace_get_stats - Gathers ethernet statistics from the MACE chip. ----------------------------------------------------------------------------- */ -static struct net_device_stats *mace_get_stats(struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - - update_stats(dev->base_addr, dev); - - pr_debug("%s: updating the statistics.\n", dev->name); - pr_linux_stats(&dev->stats); - pr_mace_stats(&lp->mace_stats); - - return &dev->stats; -} /* net_device_stats */ - -/* ---------------------------------------------------------------------------- -updateCRC - Modified from Am79C90 data sheet. ----------------------------------------------------------------------------- */ - -#ifdef BROKEN_MULTICAST - -static void updateCRC(int *CRC, int bit) -{ - static const int poly[]={ - 1,1,1,0, 1,1,0,1, - 1,0,1,1, 1,0,0,0, - 1,0,0,0, 0,0,1,1, - 0,0,1,0, 0,0,0,0 - }; /* CRC polynomial. poly[n] = coefficient of the x**n term of the - CRC generator polynomial. */ - - int j; - - /* shift CRC and control bit (CRC[32]) */ - for (j = 32; j > 0; j--) - CRC[j] = CRC[j-1]; - CRC[0] = 0; - - /* If bit XOR(control bit) = 1, set CRC = CRC XOR polynomial. */ - if (bit ^ CRC[32]) - for (j = 0; j < 32; j++) - CRC[j] ^= poly[j]; -} /* updateCRC */ - -/* ---------------------------------------------------------------------------- -BuildLAF - Build logical address filter. - Modified from Am79C90 data sheet. - -Input - ladrf: logical address filter (contents initialized to 0) - adr: ethernet address ----------------------------------------------------------------------------- */ -static void BuildLAF(int *ladrf, int *adr) -{ - int CRC[33]={1}; /* CRC register, 1 word/bit + extra control bit */ - - int i, byte; /* temporary array indices */ - int hashcode; /* the output object */ - - CRC[32]=0; - - for (byte = 0; byte < 6; byte++) - for (i = 0; i < 8; i++) - updateCRC(CRC, (adr[byte] >> i) & 1); - - hashcode = 0; - for (i = 0; i < 6; i++) - hashcode = (hashcode << 1) + CRC[i]; - - byte = hashcode >> 3; - ladrf[byte] |= (1 << (hashcode & 7)); - -#ifdef PCMCIA_DEBUG - if (0) - printk(KERN_DEBUG " adr =%pM\n", adr); - printk(KERN_DEBUG " hashcode = %d(decimal), ladrf[0:63] =", hashcode); - for (i = 0; i < 8; i++) - pr_cont(" %02X", ladrf[i]); - pr_cont("\n"); -#endif -} /* BuildLAF */ - -/* ---------------------------------------------------------------------------- -restore_multicast_list - Restores the multicast filter for MACE chip to the last - set_multicast_list() call. - -Input - multicast_num_addrs - multicast_ladrf[] ----------------------------------------------------------------------------- */ -static void restore_multicast_list(struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - int num_addrs = lp->multicast_num_addrs; - int *ladrf = lp->multicast_ladrf; - unsigned int ioaddr = dev->base_addr; - int i; - - pr_debug("%s: restoring Rx mode to %d addresses.\n", - dev->name, num_addrs); - - if (num_addrs > 0) { - - pr_debug("Attempt to restore multicast list detected.\n"); - - mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR); - /* Poll ADDRCHG bit */ - while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) - ; - /* Set LADRF register */ - for (i = 0; i < MACE_LADRF_LEN; i++) - mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]); - - mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL); - mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); - - } else if (num_addrs < 0) { - - /* Promiscuous mode: receive all packets */ - mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(lp, ioaddr, MACE_MACCC, - MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV - ); - - } else { - - /* Normal mode */ - mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); - - } -} /* restore_multicast_list */ - -/* ---------------------------------------------------------------------------- -set_multicast_list - Set or clear the multicast filter for this adaptor. - -Input - num_addrs == -1 Promiscuous mode, receive all packets - num_addrs == 0 Normal mode, clear multicast list - num_addrs > 0 Multicast mode, receive normal and MC packets, and do - best-effort filtering. -Output - multicast_num_addrs - multicast_ladrf[] ----------------------------------------------------------------------------- */ - -static void set_multicast_list(struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - int adr[ETH_ALEN] = {0}; /* Ethernet address */ - struct netdev_hw_addr *ha; - -#ifdef PCMCIA_DEBUG - { - static int old; - if (netdev_mc_count(dev) != old) { - old = netdev_mc_count(dev); - pr_debug("%s: setting Rx mode to %d addresses.\n", - dev->name, old); - } - } -#endif - - /* Set multicast_num_addrs. */ - lp->multicast_num_addrs = netdev_mc_count(dev); - - /* Set multicast_ladrf. */ - if (num_addrs > 0) { - /* Calculate multicast logical address filter */ - memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN); - netdev_for_each_mc_addr(ha, dev) { - memcpy(adr, ha->addr, ETH_ALEN); - BuildLAF(lp->multicast_ladrf, adr); - } - } - - restore_multicast_list(dev); - -} /* set_multicast_list */ - -#endif /* BROKEN_MULTICAST */ - -static void restore_multicast_list(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - mace_private *lp = netdev_priv(dev); - - pr_debug("%s: restoring Rx mode to %d addresses.\n", dev->name, - lp->multicast_num_addrs); - - if (dev->flags & IFF_PROMISC) { - /* Promiscuous mode: receive all packets */ - mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(lp, ioaddr, MACE_MACCC, - MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV - ); - } else { - /* Normal mode */ - mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); - } -} /* restore_multicast_list */ - -static void set_multicast_list(struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - -#ifdef PCMCIA_DEBUG - { - static int old; - if (netdev_mc_count(dev) != old) { - old = netdev_mc_count(dev); - pr_debug("%s: setting Rx mode to %d addresses.\n", - dev->name, old); - } - } -#endif - - lp->multicast_num_addrs = netdev_mc_count(dev); - restore_multicast_list(dev); - -} /* set_multicast_list */ - -static const struct pcmcia_device_id nmclan_ids[] = { - PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "Ethernet", 0x085a850b, 0x00b2e941), - PCMCIA_DEVICE_PROD_ID12("Portable Add-ons", "Ethernet+", 0xebf1d60, 0xad673aaf), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, nmclan_ids); - -static struct pcmcia_driver nmclan_cs_driver = { - .owner = THIS_MODULE, - .name = "nmclan_cs", - .probe = nmclan_probe, - .remove = nmclan_detach, - .id_table = nmclan_ids, - .suspend = nmclan_suspend, - .resume = nmclan_resume, -}; -module_pcmcia_driver(nmclan_cs_driver); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 60b7e53206d1..3d3b09010d48 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -135,11 +135,11 @@ */ #define XGBE_TSTAMP_SSINC 20 #define XGBE_TSTAMP_SNSINC 0 -#define XGBE_PTP_ACT_CLK_FREQ 500000000 +#define XGBE_PTP_ACT_CLK_FREQ (NSEC_PER_SEC / XGBE_TSTAMP_SSINC) #define XGBE_V2_TSTAMP_SSINC 0xA #define XGBE_V2_TSTAMP_SNSINC 0 -#define XGBE_V2_PTP_ACT_CLK_FREQ 1000000000 +#define XGBE_V2_PTP_ACT_CLK_FREQ (NSEC_PER_SEC / XGBE_V2_TSTAMP_SSINC) /* Define maximum supported values */ #define XGBE_MAX_PPS_OUT 4 diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index b854b6b42d77..2926e1e59941 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -910,7 +910,9 @@ static int xgene_mdiobus_register(struct xgene_enet_pdata *pdata, return -ENXIO; } - return of_mdiobus_register(mdio, mdio_np); + ret = of_mdiobus_register(mdio, mdio_np); + of_node_put(mdio_np); + return ret; } /* Mask out all PHYs from auto probing. */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c index e9e38af680c3..39e1b606a75a 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c @@ -371,7 +371,7 @@ static void aq_pci_shutdown(struct pci_dev *pdev) pci_disable_device(pdev); if (system_state == SYSTEM_POWER_OFF) { - pci_wake_from_d3(pdev, false); + pci_wake_from_d3(pdev, self->aq_hw->aq_nic_cfg->wol); pci_set_power_state(pdev, PCI_D3hot); } } diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_core.c b/drivers/net/ethernet/broadcom/bnge/bnge_core.c index 1c14c5fe8d61..68b74eb2c3a2 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_core.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_core.c @@ -74,6 +74,13 @@ static int bnge_func_qcaps(struct bnge_dev *bd) return rc; } + return 0; +} + +static int bnge_func_qrcaps_qcfg(struct bnge_dev *bd) +{ + int rc; + rc = bnge_hwrm_func_resc_qcaps(bd); if (rc) { dev_err(bd->dev, "query resc caps failure rc: %d\n", rc); @@ -133,23 +140,28 @@ static int bnge_fw_register_dev(struct bnge_dev *bd) bnge_hwrm_fw_set_time(bd); - rc = bnge_hwrm_func_drv_rgtr(bd); + /* Get the resources and configuration from firmware */ + rc = bnge_func_qcaps(bd); if (rc) { - dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc); + dev_err(bd->dev, "Failed querying caps rc: %d\n", rc); return rc; } rc = bnge_alloc_ctx_mem(bd); if (rc) { dev_err(bd->dev, "Failed to allocate ctx mem rc: %d\n", rc); - goto err_func_unrgtr; + goto err_free_ctx_mem; } - /* Get the resources and configuration from firmware */ - rc = bnge_func_qcaps(bd); + rc = bnge_hwrm_func_drv_rgtr(bd); if (rc) { - dev_err(bd->dev, "Failed initial configuration rc: %d\n", rc); - rc = -ENODEV; + dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc); + goto err_free_ctx_mem; + } + + rc = bnge_func_qrcaps_qcfg(bd); + if (rc) { + dev_err(bd->dev, "Failed querying resources rc: %d\n", rc); goto err_func_unrgtr; } @@ -158,7 +170,9 @@ static int bnge_fw_register_dev(struct bnge_dev *bd) return 0; err_func_unrgtr: - bnge_fw_unregister_dev(bd); + bnge_hwrm_func_drv_unrgtr(bd); +err_free_ctx_mem: + bnge_free_ctx_mem(bd); return rc; } diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_rmem.c b/drivers/net/ethernet/broadcom/bnge/bnge_rmem.c index 94f15e08a88c..b066ee887a09 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_rmem.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_rmem.c @@ -324,7 +324,6 @@ int bnge_alloc_ctx_mem(struct bnge_dev *bd) u32 l2_qps, qp1_qps, max_qps; u32 ena, entries_sp, entries; u32 srqs, max_srqs, min; - u32 num_mr, num_ah; u32 extra_srqs = 0; u32 extra_qps = 0; u32 fast_qpmd_qps; @@ -390,21 +389,6 @@ int bnge_alloc_ctx_mem(struct bnge_dev *bd) if (!bnge_is_roce_en(bd)) goto skip_rdma; - ctxm = &ctx->ctx_arr[BNGE_CTX_MRAV]; - /* 128K extra is needed to accommodate static AH context - * allocation by f/w. - */ - num_mr = min_t(u32, ctxm->max_entries / 2, 1024 * 256); - num_ah = min_t(u32, num_mr, 1024 * 128); - ctxm->split_entry_cnt = BNGE_CTX_MRAV_AV_SPLIT_ENTRY + 1; - if (!ctxm->mrav_av_entries || ctxm->mrav_av_entries > num_ah) - ctxm->mrav_av_entries = num_ah; - - rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, num_mr + num_ah, 2); - if (rc) - return rc; - ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_MRAV; - ctxm = &ctx->ctx_arr[BNGE_CTX_TIM]; rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, l2_qps + qp1_qps + extra_qps, 1); if (rc) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 2715632115a5..008c34cff7b4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -59,10 +59,10 @@ #include #include #include +#include #include "bnxt.h" #include "bnxt_hwrm.h" -#include "bnxt_ulp.h" #include "bnxt_sriov.h" #include "bnxt_ethtool.h" #include "bnxt_dcb.h" @@ -3825,7 +3825,10 @@ static int bnxt_alloc_tpa_info(struct bnxt *bp) if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) { if (!bp->max_tpa_v2) return 0; - bp->max_tpa = max_t(u16, bp->max_tpa_v2, MAX_TPA_P5); + bp->max_tpa = min_t(u16, bp->max_tpa_v2, MAX_TPA_P5); + /* Older P5 FW sets max_tpa_v2 low by mistake except NPAR */ + if (bp->max_tpa <= 32 && BNXT_CHIP_P5(bp) && !BNXT_NPAR(bp)) + bp->max_tpa = MAX_TPA_P5; } for (i = 0; i < bp->rx_nr_rings; i++) { @@ -6985,7 +6988,8 @@ vnic_mru: #endif if ((bp->flags & BNXT_FLAG_STRIP_VLAN) || def_vlan) req->flags |= cpu_to_le32(VNIC_CFG_REQ_FLAGS_VLAN_STRIP_MODE); - if (vnic->vnic_id == BNXT_VNIC_DEFAULT && bnxt_ulp_registered(bp->edev)) + if (vnic->vnic_id == BNXT_VNIC_DEFAULT && + bnxt_ulp_registered(bp->edev[BNXT_AUXDEV_RDMA])) req->flags |= cpu_to_le32(bnxt_get_roce_vnic_mode(bp)); return hwrm_req_send(bp, req); @@ -8120,6 +8124,7 @@ static int bnxt_get_avail_msix(struct bnxt *bp, int num); static int __bnxt_reserve_rings(struct bnxt *bp) { + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; struct bnxt_hw_rings hwr = {0}; int rx_rings, old_rx_rings, rc; int cp = bp->cp_nr_rings; @@ -8130,7 +8135,7 @@ static int __bnxt_reserve_rings(struct bnxt *bp) if (!bnxt_need_reserve_rings(bp)) return 0; - if (BNXT_NEW_RM(bp) && !bnxt_ulp_registered(bp->edev)) { + if (BNXT_NEW_RM(bp) && !bnxt_ulp_registered(edev)) { ulp_msix = bnxt_get_avail_msix(bp, bp->ulp_num_msix_want); if (!ulp_msix) bnxt_set_ulp_stat_ctxs(bp, 0); @@ -8183,8 +8188,7 @@ static int __bnxt_reserve_rings(struct bnxt *bp) } rx_rings = min_t(int, rx_rings, hwr.grp); hwr.cp = min_t(int, hwr.cp, bp->cp_nr_rings); - if (bnxt_ulp_registered(bp->edev) && - hwr.stat > bnxt_get_ulp_stat_ctxs(bp)) + if (bnxt_ulp_registered(edev) && hwr.stat > bnxt_get_ulp_stat_ctxs(bp)) hwr.stat -= bnxt_get_ulp_stat_ctxs(bp); hwr.cp = min_t(int, hwr.cp, hwr.stat); rc = bnxt_trim_rings(bp, &rx_rings, &hwr.tx, hwr.cp, sh); @@ -8227,7 +8231,7 @@ static int __bnxt_reserve_rings(struct bnxt *bp) !netif_is_rxfh_configured(bp->dev)) bnxt_set_dflt_rss_indir_tbl(bp, NULL); - if (!bnxt_ulp_registered(bp->edev) && BNXT_NEW_RM(bp)) { + if (!bnxt_ulp_registered(edev) && BNXT_NEW_RM(bp)) { int resv_msix, resv_ctx, ulp_ctxs; struct bnxt_hw_resc *hw_resc; @@ -11131,8 +11135,9 @@ static int bnxt_setup_nitroa0_vnic(struct bnxt *bp) return rc; } -static int bnxt_cfg_rx_mode(struct bnxt *); -static bool bnxt_mc_list_updated(struct bnxt *, u32 *); +static int bnxt_cfg_rx_mode(struct bnxt *, struct netdev_hw_addr_list *, bool); +static bool bnxt_mc_list_updated(struct bnxt *, u32 *, + const struct netdev_hw_addr_list *); static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init) { @@ -11222,11 +11227,11 @@ static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init) } else if (bp->dev->flags & IFF_MULTICAST) { u32 mask = 0; - bnxt_mc_list_updated(bp, &mask); + bnxt_mc_list_updated(bp, &mask, &bp->dev->mc); vnic->rx_mask |= mask; } - rc = bnxt_cfg_rx_mode(bp); + rc = bnxt_cfg_rx_mode(bp, &bp->dev->uc, true); if (rc) goto err_out; @@ -11584,6 +11589,7 @@ static void bnxt_clear_int_mode(struct bnxt *bp) int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init) { + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; bool irq_cleared = false; bool irq_change = false; int tcs = bp->num_tc; @@ -11593,7 +11599,7 @@ int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init) if (!bnxt_need_reserve_rings(bp)) return 0; - if (BNXT_NEW_RM(bp) && !bnxt_ulp_registered(bp->edev)) { + if (BNXT_NEW_RM(bp) && !bnxt_ulp_registered(edev)) { int ulp_msix = bnxt_get_avail_msix(bp, bp->ulp_num_msix_want); if (ulp_msix > bp->ulp_num_msix_want) @@ -13620,17 +13626,17 @@ void bnxt_get_ring_drv_stats(struct bnxt *bp, bnxt_get_one_ring_drv_stats(bp, stats, &bp->bnapi[i]->cp_ring); } -static bool bnxt_mc_list_updated(struct bnxt *bp, u32 *rx_mask) +static bool bnxt_mc_list_updated(struct bnxt *bp, u32 *rx_mask, + const struct netdev_hw_addr_list *mc) { struct bnxt_vnic_info *vnic = &bp->vnic_info[BNXT_VNIC_DEFAULT]; - struct net_device *dev = bp->dev; struct netdev_hw_addr *ha; u8 *haddr; int mc_count = 0; bool update = false; int off = 0; - netdev_for_each_mc_addr(ha, dev) { + netdev_hw_addr_list_for_each(ha, mc) { if (mc_count >= BNXT_MAX_MC_ADDRS) { *rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; vnic->mc_list_count = 0; @@ -13654,17 +13660,17 @@ static bool bnxt_mc_list_updated(struct bnxt *bp, u32 *rx_mask) return update; } -static bool bnxt_uc_list_updated(struct bnxt *bp) +static bool bnxt_uc_list_updated(struct bnxt *bp, + const struct netdev_hw_addr_list *uc) { - struct net_device *dev = bp->dev; struct bnxt_vnic_info *vnic = &bp->vnic_info[BNXT_VNIC_DEFAULT]; struct netdev_hw_addr *ha; int off = 0; - if (netdev_uc_count(dev) != (vnic->uc_filter_count - 1)) + if (netdev_hw_addr_list_count(uc) != (vnic->uc_filter_count - 1)) return true; - netdev_for_each_uc_addr(ha, dev) { + netdev_hw_addr_list_for_each(ha, uc) { if (!ether_addr_equal(ha->addr, vnic->uc_list + off)) return true; @@ -13673,7 +13679,9 @@ static bool bnxt_uc_list_updated(struct bnxt *bp) return false; } -static void bnxt_set_rx_mode(struct net_device *dev) +static void bnxt_set_rx_mode(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct bnxt *bp = netdev_priv(dev); struct bnxt_vnic_info *vnic; @@ -13694,7 +13702,7 @@ static void bnxt_set_rx_mode(struct net_device *dev) if (dev->flags & IFF_PROMISC) mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; - uc_update = bnxt_uc_list_updated(bp); + uc_update = bnxt_uc_list_updated(bp, uc); if (dev->flags & IFF_BROADCAST) mask |= CFA_L2_SET_RX_MASK_REQ_MASK_BCAST; @@ -13702,27 +13710,23 @@ static void bnxt_set_rx_mode(struct net_device *dev) mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; vnic->mc_list_count = 0; } else if (dev->flags & IFF_MULTICAST) { - mc_update = bnxt_mc_list_updated(bp, &mask); + mc_update = bnxt_mc_list_updated(bp, &mask, mc); } if (mask != vnic->rx_mask || uc_update || mc_update) { vnic->rx_mask = mask; - bnxt_queue_sp_work(bp, BNXT_RX_MASK_SP_EVENT); + bnxt_cfg_rx_mode(bp, uc, uc_update); } } -static int bnxt_cfg_rx_mode(struct bnxt *bp) +static int bnxt_cfg_rx_mode(struct bnxt *bp, struct netdev_hw_addr_list *uc, + bool uc_update) { struct net_device *dev = bp->dev; struct bnxt_vnic_info *vnic = &bp->vnic_info[BNXT_VNIC_DEFAULT]; struct netdev_hw_addr *ha; int i, off = 0, rc; - bool uc_update; - - netif_addr_lock_bh(dev); - uc_update = bnxt_uc_list_updated(bp); - netif_addr_unlock_bh(dev); if (!uc_update) goto skip_uc; @@ -13737,10 +13741,10 @@ static int bnxt_cfg_rx_mode(struct bnxt *bp) vnic->uc_filter_count = 1; netif_addr_lock_bh(dev); - if (netdev_uc_count(dev) > (BNXT_MAX_UC_ADDRS - 1)) { + if (netdev_hw_addr_list_count(uc) > (BNXT_MAX_UC_ADDRS - 1)) { vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; } else { - netdev_for_each_uc_addr(ha, dev) { + netdev_hw_addr_list_for_each(ha, uc) { memcpy(vnic->uc_list + off, ha->addr, ETH_ALEN); off += ETH_ALEN; vnic->uc_filter_count++; @@ -14700,12 +14704,13 @@ static void bnxt_fw_echo_reply(struct bnxt *bp) static void bnxt_ulp_restart(struct bnxt *bp) { bnxt_ulp_stop(bp); - bnxt_ulp_start(bp, 0); + bnxt_ulp_start(bp); } static void bnxt_sp_task(struct work_struct *work) { struct bnxt *bp = container_of(work, struct bnxt, sp_task); + struct net_device *dev = bp->dev; set_bit(BNXT_STATE_IN_SP_TASK, &bp->state); smp_mb__after_atomic(); @@ -14719,9 +14724,6 @@ static void bnxt_sp_task(struct work_struct *work) bnxt_reenable_sriov(bp); } - if (test_and_clear_bit(BNXT_RX_MASK_SP_EVENT, &bp->sp_event)) - bnxt_cfg_rx_mode(bp); - if (test_and_clear_bit(BNXT_RX_NTP_FLTR_SP_EVENT, &bp->sp_event)) bnxt_cfg_ntp_filters(bp); if (test_and_clear_bit(BNXT_HWRM_EXEC_FWD_REQ_SP_EVENT, &bp->sp_event)) @@ -14786,6 +14788,13 @@ static void bnxt_sp_task(struct work_struct *work) /* These functions below will clear BNXT_STATE_IN_SP_TASK. They * must be the last functions to be called before exiting. */ + if (test_and_clear_bit(BNXT_RX_MASK_SP_EVENT, &bp->sp_event)) { + bnxt_lock_sp(bp); + if (test_bit(BNXT_STATE_OPEN, &bp->state)) + bnxt_cfg_rx_mode(bp, &dev->uc, true); + bnxt_unlock_sp(bp); + } + if (test_and_clear_bit(BNXT_RESET_TASK_SP_EVENT, &bp->sp_event)) bnxt_reset(bp, false); @@ -14857,7 +14866,7 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, hwr.cp_p5 = hwr.tx + rx; rc = bnxt_hwrm_check_rings(bp, &hwr); if (!rc && pci_msix_can_alloc_dyn(bp->pdev)) { - if (!bnxt_ulp_registered(bp->edev)) { + if (!bnxt_ulp_registered(bp->edev[BNXT_AUXDEV_RDMA])) { hwr.cp += bnxt_get_ulp_msix_num(bp); hwr.cp = min_t(int, hwr.cp, bnxt_get_max_func_irqs(bp)); } @@ -15377,7 +15386,7 @@ static void bnxt_fw_reset_task(struct work_struct *work) bnxt_dl_health_fw_status_update(bp, true); } netdev_unlock(bp->dev); - bnxt_ulp_start(bp, 0); + bnxt_ulp_start(bp); bnxt_reenable_sriov(bp); netdev_lock(bp->dev); bnxt_vf_reps_alloc(bp); @@ -15399,7 +15408,8 @@ fw_reset_abort: bnxt_fw_reset_abort(bp, rc); netdev_unlock(bp->dev); ulp_start: - bnxt_ulp_start(bp, rc); + if (!rc) + bnxt_ulp_start(bp); } static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev) @@ -15986,7 +15996,7 @@ static const struct net_device_ops bnxt_netdev_ops = { .ndo_start_xmit = bnxt_start_xmit, .ndo_stop = bnxt_close, .ndo_get_stats64 = bnxt_get_stats64, - .ndo_set_rx_mode = bnxt_set_rx_mode, + .ndo_set_rx_mode_async = bnxt_set_rx_mode, .ndo_eth_ioctl = bnxt_ioctl, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = bnxt_change_mac_addr, @@ -16444,12 +16454,13 @@ static void bnxt_remove_one(struct pci_dev *pdev) if (BNXT_PF(bp)) __bnxt_sriov_disable(bp); - bnxt_rdma_aux_device_del(bp); + bnxt_aux_devices_del(bp); unregister_netdev(dev); bnxt_ptp_clear(bp); - bnxt_rdma_aux_device_uninit(bp); + bnxt_aux_devices_uninit(bp); + bnxt_auxdev_id_free(bp, bp->auxdev_id); bnxt_free_l2_filters(bp, true); bnxt_free_ntp_fltrs(bp, true); @@ -17053,7 +17064,9 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) bnxt_set_tpa_flags(bp); bnxt_init_ring_params(bp); bnxt_set_ring_params(bp); - bnxt_rdma_aux_device_init(bp); + mutex_init(&bp->auxdev_lock); + if (!bnxt_auxdev_id_alloc(bp)) + bnxt_aux_devices_init(bp); rc = bnxt_set_dflt_rings(bp, true); if (rc) { if (BNXT_VF(bp) && rc == -ENODEV) { @@ -17118,7 +17131,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) bnxt_dl_fw_reporters_create(bp); - bnxt_rdma_aux_device_add(bp); + bnxt_aux_devices_add(bp); bnxt_print_device_info(bp); @@ -17126,7 +17139,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; init_err_cleanup: - bnxt_rdma_aux_device_uninit(bp); + bnxt_aux_devices_uninit(bp); + bnxt_auxdev_id_free(bp, bp->auxdev_id); bnxt_dl_unregister(bp); init_err_dl: bnxt_shutdown_tc(bp); @@ -17260,9 +17274,10 @@ static int bnxt_resume(struct device *device) resume_exit: netdev_unlock(bp->dev); - bnxt_ulp_start(bp, rc); - if (!rc) + if (!rc) { + bnxt_ulp_start(bp); bnxt_reenable_sriov(bp); + } return rc; } @@ -17348,9 +17363,14 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev) netdev_info(bp->dev, "PCI Slot Reset\n"); - if (!(bp->flags & BNXT_FLAG_CHIP_P5_PLUS) && - test_bit(BNXT_STATE_PCI_CHANNEL_IO_FROZEN, &bp->state)) - msleep(900); + if (test_bit(BNXT_STATE_PCI_CHANNEL_IO_FROZEN, &bp->state)) { + /* After DPC, the chip should return CRS when the vendor ID + * config register is read until it is ready. On all chips, + * this is not happening reliably so add a 5-second delay as a + * workaround. + */ + msleep(5000); + } netdev_lock(netdev); @@ -17442,9 +17462,10 @@ static void bnxt_io_resume(struct pci_dev *pdev) netif_device_attach(netdev); netdev_unlock(netdev); - bnxt_ulp_start(bp, err); - if (!err) + if (!err) { + bnxt_ulp_start(bp); bnxt_reenable_sriov(bp); + } } static const struct pci_error_handlers bnxt_err_handler = { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index fe50576ae525..61c847b36b9f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -26,12 +26,12 @@ #include #include #include -#include #include #include #include #include #include +#include #ifdef CONFIG_TEE_BNXT_FW #include #endif @@ -2100,12 +2100,6 @@ struct bnxt_fw_health { #define BNXT_FW_IF_RETRY 10 #define BNXT_FW_SLOT_RESET_RETRY 4 -struct bnxt_aux_priv { - struct auxiliary_device aux_dev; - struct bnxt_en_dev *edev; - int id; -}; - enum board_idx { BCM57301, BCM57302, @@ -2365,8 +2359,8 @@ struct bnxt { #define BNXT_CHIP_P5_AND_MINUS(bp) \ (BNXT_CHIP_P3(bp) || BNXT_CHIP_P4(bp) || BNXT_CHIP_P5(bp)) - struct bnxt_aux_priv *aux_priv; - struct bnxt_en_dev *edev; + struct bnxt_aux_priv *aux_priv[__BNXT_AUXDEV_MAX]; + struct bnxt_en_dev *edev[__BNXT_AUXDEV_MAX]; struct bnxt_napi **bnapi; @@ -2778,6 +2772,13 @@ struct bnxt { struct bnxt_ctx_pg_info *fw_crash_mem; u32 fw_crash_len; struct bnxt_bs_trace_info bs_trace[BNXT_TRACE_MAX]; + int auxdev_id; + /* synchronize validity checks of available aux devices */ + struct mutex auxdev_lock; + u8 auxdev_state[__BNXT_AUXDEV_MAX]; +#define BNXT_ADEV_STATE_NONE 0 +#define BNXT_ADEV_STATE_INIT 1 +#define BNXT_ADEV_STATE_ADD 2 }; #define BNXT_NUM_RX_RING_STATS 8 diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index 15de802bbac4..835f2b413931 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -13,12 +13,12 @@ #include #include #include +#include #include "bnxt.h" #include "bnxt_hwrm.h" #include "bnxt_vfr.h" #include "bnxt_devlink.h" #include "bnxt_ethtool.h" -#include "bnxt_ulp.h" #include "bnxt_ptp.h" #include "bnxt_coredump.h" #include "bnxt_nvm_defs.h" @@ -440,13 +440,13 @@ static int bnxt_dl_reload_down(struct devlink *dl, bool netns_change, "reload is unsupported while VFs are allocated or being configured"); netdev_unlock(bp->dev); rtnl_unlock(); - bnxt_ulp_start(bp, 0); + bnxt_ulp_start(bp); return -EOPNOTSUPP; } if (bp->dev->reg_state == NETREG_UNREGISTERED) { netdev_unlock(bp->dev); rtnl_unlock(); - bnxt_ulp_start(bp, 0); + bnxt_ulp_start(bp); return -ENODEV; } if (netif_running(bp->dev)) @@ -578,8 +578,8 @@ static int bnxt_dl_reload_up(struct devlink *dl, enum devlink_reload_action acti } netdev_unlock(bp->dev); rtnl_unlock(); - if (action == DEVLINK_RELOAD_ACTION_DRIVER_REINIT) - bnxt_ulp_start(bp, rc); + if (!rc && action == DEVLINK_RELOAD_ACTION_DRIVER_REINIT) + bnxt_ulp_start(bp); return rc; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 9ded88196bb4..9b14134d62d2 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -27,9 +27,9 @@ #include #include #include +#include #include "bnxt.h" #include "bnxt_hwrm.h" -#include "bnxt_ulp.h" #include "bnxt_xdp.h" #include "bnxt_ptp.h" #include "bnxt_ethtool.h" @@ -5245,7 +5245,7 @@ static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, memset(buf, 0, sizeof(u64) * bp->num_tests); if (etest->flags & ETH_TEST_FL_OFFLINE && - bnxt_ulp_registered(bp->edev)) { + bnxt_ulp_registered(bp->edev[BNXT_AUXDEV_RDMA])) { etest->flags |= ETH_TEST_FL_FAILED; netdev_warn(dev, "Offline tests cannot be run with RoCE driver loaded\n"); return; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c index 53f336db4fcc..5d41dc1bc782 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c @@ -419,31 +419,13 @@ void bnxt_ptp_reapply_pps(struct bnxt *bp) } } -static int bnxt_get_target_cycles(struct bnxt_ptp_cfg *ptp, u64 target_ns, - u64 *cycles_delta) -{ - u64 cycles_now; - u64 nsec_now, nsec_delta; - int rc; - - rc = bnxt_refclk_read(ptp->bp, NULL, &cycles_now); - if (rc) - return rc; - - nsec_now = bnxt_timecounter_cyc2time(ptp, cycles_now); - - nsec_delta = target_ns - nsec_now; - *cycles_delta = div64_u64(nsec_delta << ptp->cc.shift, ptp->cc.mult); - return 0; -} - static int bnxt_ptp_perout_cfg(struct bnxt_ptp_cfg *ptp, struct ptp_clock_request *rq) { struct hwrm_func_ptp_cfg_input *req; struct bnxt *bp = ptp->bp; struct timespec64 ts; - u64 target_ns, delta; + u64 target_ns; u16 enables; int rc; @@ -451,10 +433,6 @@ static int bnxt_ptp_perout_cfg(struct bnxt_ptp_cfg *ptp, ts.tv_nsec = rq->perout.start.nsec; target_ns = timespec64_to_ns(&ts); - rc = bnxt_get_target_cycles(ptp, target_ns, &delta); - if (rc) - return rc; - rc = hwrm_req_init(bp, req, HWRM_FUNC_PTP_CFG); if (rc) return rc; @@ -468,7 +446,10 @@ static int bnxt_ptp_perout_cfg(struct bnxt_ptp_cfg *ptp, req->ptp_freq_adj_dll_phase = 0; req->ptp_freq_adj_ext_period = cpu_to_le32(NSEC_PER_SEC); req->ptp_freq_adj_ext_up = 0; - req->ptp_freq_adj_ext_phase_lower = cpu_to_le32(delta); + req->ptp_freq_adj_ext_phase_lower = + cpu_to_le32(lower_32_bits(target_ns)); + req->ptp_freq_adj_ext_phase_upper = + cpu_to_le32(upper_32_bits(target_ns)); return hwrm_req_send(bp, req); } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index 7f9829287c49..edcc002e4ca3 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -17,9 +17,9 @@ #include #include #include +#include #include "bnxt.h" #include "bnxt_hwrm.h" -#include "bnxt_ulp.h" #include "bnxt_sriov.h" #include "bnxt_vfr.h" #include "bnxt_ethtool.h" diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c index e1e82a72cf1b..5c751933da6a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c @@ -22,16 +22,41 @@ #include #include #include +#include #include "bnxt.h" #include "bnxt_hwrm.h" -#include "bnxt_ulp.h" static DEFINE_IDA(bnxt_aux_dev_ids); +struct bnxt_aux_device { + const char *name; +}; + +static void bnxt_auxdev_set_state(struct bnxt *bp, int idx, int state) +{ + bp->auxdev_state[idx] = state; +} + +static bool bnxt_auxdev_is_init(struct bnxt *bp, int idx) +{ + return (bp->auxdev_state[idx] == BNXT_ADEV_STATE_INIT); +} + +static bool bnxt_auxdev_is_active(struct bnxt *bp, int idx) +{ + return (bp->auxdev_state[idx] == BNXT_ADEV_STATE_ADD); +} + +static struct bnxt_aux_device bnxt_aux_devices[__BNXT_AUXDEV_MAX] = {{ + .name = "rdma", +}, { + .name = "fwctl", +}}; + static void bnxt_fill_msix_vecs(struct bnxt *bp, struct bnxt_msix_entry *ent) { - struct bnxt_en_dev *edev = bp->edev; + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; int num_msix, i; if (!edev->ulp_tbl->msix_requested) { @@ -51,61 +76,75 @@ static void bnxt_fill_msix_vecs(struct bnxt *bp, struct bnxt_msix_entry *ent) int bnxt_get_ulp_msix_num(struct bnxt *bp) { - if (bp->edev) - return bp->edev->ulp_num_msix_vec; + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; + + if (edev) + return edev->ulp_num_msix_vec; return 0; } void bnxt_set_ulp_msix_num(struct bnxt *bp, int num) { - if (bp->edev) - bp->edev->ulp_num_msix_vec = num; + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; + + if (edev) + edev->ulp_num_msix_vec = num; } int bnxt_get_ulp_msix_num_in_use(struct bnxt *bp) { - if (bnxt_ulp_registered(bp->edev)) - return bp->edev->ulp_num_msix_vec; + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; + + if (bnxt_ulp_registered(edev)) + return edev->ulp_num_msix_vec; return 0; } int bnxt_get_ulp_stat_ctxs(struct bnxt *bp) { - if (bp->edev) - return bp->edev->ulp_num_ctxs; + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; + + if (edev) + return edev->ulp_num_ctxs; return 0; } void bnxt_set_ulp_stat_ctxs(struct bnxt *bp, int num_ulp_ctx) { - if (bp->edev) - bp->edev->ulp_num_ctxs = num_ulp_ctx; + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; + + if (edev) + edev->ulp_num_ctxs = num_ulp_ctx; } int bnxt_get_ulp_stat_ctxs_in_use(struct bnxt *bp) { - if (bnxt_ulp_registered(bp->edev)) - return bp->edev->ulp_num_ctxs; + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; + + if (bnxt_ulp_registered(edev)) + return edev->ulp_num_ctxs; return 0; } void bnxt_set_dflt_ulp_stat_ctxs(struct bnxt *bp) { - if (bp->edev) { - bp->edev->ulp_num_ctxs = BNXT_MIN_ROCE_STAT_CTXS; + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; + + if (edev) { + edev->ulp_num_ctxs = BNXT_MIN_ROCE_STAT_CTXS; /* Reserve one additional stat_ctx for PF0 (except * on 1-port NICs) as it also creates one stat_ctx * for PF1 in case of RoCE bonding. */ if (BNXT_PF(bp) && !bp->pf.port_id && bp->port_count > 1) - bp->edev->ulp_num_ctxs++; + edev->ulp_num_ctxs++; /* Reserve one additional stat_ctx when the device is capable * of supporting port mirroring on RDMA device. */ if (BNXT_MIRROR_ON_ROCE_CAP(bp)) - bp->edev->ulp_num_ctxs++; + edev->ulp_num_ctxs++; } } @@ -136,12 +175,18 @@ int bnxt_register_dev(struct bnxt_en_dev *edev, ulp->handle = handle; rcu_assign_pointer(ulp->ulp_ops, ulp_ops); - if (test_bit(BNXT_STATE_OPEN, &bp->state)) - bnxt_hwrm_vnic_cfg(bp, &bp->vnic_info[BNXT_VNIC_DEFAULT]); + if (test_bit(BNXT_STATE_OPEN, &bp->state)) { + rc = bnxt_hwrm_vnic_cfg(bp, &bp->vnic_info[BNXT_VNIC_DEFAULT]); + if (rc) { + netdev_err(dev, "Failed to configure dual VNIC mode\n"); + RCU_INIT_POINTER(ulp->ulp_ops, NULL); + goto exit; + } + } edev->ulp_tbl->msix_requested = bnxt_get_ulp_msix_num(bp); - bnxt_fill_msix_vecs(bp, bp->edev->msix_entries); + bnxt_fill_msix_vecs(bp, edev->msix_entries); exit: mutex_unlock(&edev->en_dev_lock); netdev_unlock(dev); @@ -227,20 +272,32 @@ EXPORT_SYMBOL(bnxt_send_msg); void bnxt_ulp_stop(struct bnxt *bp) { - struct bnxt_aux_priv *aux_priv = bp->aux_priv; - struct bnxt_en_dev *edev = bp->edev; + int i; - if (!edev) - return; - - mutex_lock(&edev->en_dev_lock); - if (!bnxt_ulp_registered(edev) || - (edev->flags & BNXT_EN_FLAG_ULP_STOPPED)) - goto ulp_stop_exit; - - edev->flags |= BNXT_EN_FLAG_ULP_STOPPED; - if (aux_priv) { + mutex_lock(&bp->auxdev_lock); + for (i = 0; i < __BNXT_AUXDEV_MAX; i++) { + struct bnxt_aux_priv *aux_priv; struct auxiliary_device *adev; + struct bnxt_en_dev *edev; + + if (!bnxt_auxdev_is_active(bp, i)) + continue; + + aux_priv = bp->aux_priv[i]; + edev = bp->edev[i]; + mutex_lock(&edev->en_dev_lock); + if (i == BNXT_AUXDEV_FWCTL) { + edev->flags |= BNXT_EN_FLAG_ULP_STOPPED; + mutex_unlock(&edev->en_dev_lock); + continue; + } + if (!bnxt_ulp_registered(edev) || + (edev->flags & BNXT_EN_FLAG_ULP_STOPPED)) { + mutex_unlock(&edev->en_dev_lock); + continue; + } + + edev->flags |= BNXT_EN_FLAG_ULP_STOPPED; adev = &aux_priv->aux_dev; if (adev->dev.driver) { @@ -251,29 +308,35 @@ void bnxt_ulp_stop(struct bnxt *bp) edev->en_state = bp->state; adrv->suspend(adev, pm); } + mutex_unlock(&edev->en_dev_lock); } -ulp_stop_exit: - mutex_unlock(&edev->en_dev_lock); + mutex_unlock(&bp->auxdev_lock); } -void bnxt_ulp_start(struct bnxt *bp, int err) +void bnxt_ulp_start(struct bnxt *bp) { - struct bnxt_aux_priv *aux_priv = bp->aux_priv; - struct bnxt_en_dev *edev = bp->edev; + int i; - if (!edev || err) - return; - - mutex_lock(&edev->en_dev_lock); - if (!bnxt_ulp_registered(edev) || - !(edev->flags & BNXT_EN_FLAG_ULP_STOPPED)) - goto ulp_start_exit; - - if (edev->ulp_tbl->msix_requested) - bnxt_fill_msix_vecs(bp, edev->msix_entries); - - if (aux_priv) { + mutex_lock(&bp->auxdev_lock); + for (i = 0; i < __BNXT_AUXDEV_MAX; i++) { + struct bnxt_aux_priv *aux_priv; struct auxiliary_device *adev; + struct bnxt_en_dev *edev; + + if (!bnxt_auxdev_is_active(bp, i)) + continue; + + aux_priv = bp->aux_priv[i]; + edev = bp->edev[i]; + mutex_lock(&edev->en_dev_lock); + if (i == BNXT_AUXDEV_FWCTL || !bnxt_ulp_registered(edev) || + !(edev->flags & BNXT_EN_FLAG_ULP_STOPPED)) { + goto clear_flag_continue; + } + + if (edev->ulp_tbl->msix_requested) + bnxt_fill_msix_vecs(bp, edev->msix_entries); + adev = &aux_priv->aux_dev; if (adev->dev.driver) { @@ -283,22 +346,23 @@ void bnxt_ulp_start(struct bnxt *bp, int err) edev->en_state = bp->state; adrv->resume(adev); } +clear_flag_continue: + edev->flags &= ~BNXT_EN_FLAG_ULP_STOPPED; + mutex_unlock(&edev->en_dev_lock); } -ulp_start_exit: - edev->flags &= ~BNXT_EN_FLAG_ULP_STOPPED; - mutex_unlock(&edev->en_dev_lock); + mutex_unlock(&bp->auxdev_lock); } void bnxt_ulp_irq_stop(struct bnxt *bp) { - struct bnxt_en_dev *edev = bp->edev; + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; struct bnxt_ulp_ops *ops; bool reset = false; if (!edev) return; - if (bnxt_ulp_registered(bp->edev)) { + if (bnxt_ulp_registered(edev)) { struct bnxt_ulp *ulp = edev->ulp_tbl; if (!ulp->msix_requested) @@ -315,13 +379,13 @@ void bnxt_ulp_irq_stop(struct bnxt *bp) void bnxt_ulp_irq_restart(struct bnxt *bp, int err) { - struct bnxt_en_dev *edev = bp->edev; + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; struct bnxt_ulp_ops *ops; if (!edev) return; - if (bnxt_ulp_registered(bp->edev)) { + if (bnxt_ulp_registered(edev)) { struct bnxt_ulp *ulp = edev->ulp_tbl; struct bnxt_msix_entry *ent = NULL; @@ -346,7 +410,7 @@ void bnxt_ulp_irq_restart(struct bnxt *bp, int err) void bnxt_ulp_async_events(struct bnxt *bp, struct hwrm_async_event_cmpl *cmpl) { u16 event_id = le16_to_cpu(cmpl->event_id); - struct bnxt_en_dev *edev = bp->edev; + struct bnxt_en_dev *edev = bp->edev[BNXT_AUXDEV_RDMA]; struct bnxt_ulp_ops *ops; struct bnxt_ulp *ulp; @@ -387,18 +451,21 @@ void bnxt_register_async_events(struct bnxt_en_dev *edev, } EXPORT_SYMBOL(bnxt_register_async_events); -void bnxt_rdma_aux_device_uninit(struct bnxt *bp) +void bnxt_aux_devices_uninit(struct bnxt *bp) { struct bnxt_aux_priv *aux_priv; struct auxiliary_device *adev; + int idx; - /* Skip if no auxiliary device init was done. */ - if (!bp->aux_priv) - return; - - aux_priv = bp->aux_priv; - adev = &aux_priv->aux_dev; - auxiliary_device_uninit(adev); + mutex_lock(&bp->auxdev_lock); + for (idx = 0; idx < __BNXT_AUXDEV_MAX; idx++) { + if (bnxt_auxdev_is_init(bp, idx)) { + aux_priv = bp->aux_priv[idx]; + adev = &aux_priv->aux_dev; + auxiliary_device_uninit(adev); + } + } + mutex_unlock(&bp->auxdev_lock); } static void bnxt_aux_dev_release(struct device *dev) @@ -407,20 +474,25 @@ static void bnxt_aux_dev_release(struct device *dev) container_of(dev, struct bnxt_aux_priv, aux_dev.dev); struct bnxt *bp = netdev_priv(aux_priv->edev->net); - ida_free(&bnxt_aux_dev_ids, aux_priv->id); kfree(aux_priv->edev->ulp_tbl); - bp->edev = NULL; + bp->edev[aux_priv->id] = NULL; kfree(aux_priv->edev); + bp->aux_priv[aux_priv->id] = NULL; kfree(aux_priv); - bp->aux_priv = NULL; } -void bnxt_rdma_aux_device_del(struct bnxt *bp) +void bnxt_aux_devices_del(struct bnxt *bp) { - if (!bp->edev) - return; + int idx; - auxiliary_device_delete(&bp->aux_priv->aux_dev); + mutex_lock(&bp->auxdev_lock); + for (idx = 0; idx < __BNXT_AUXDEV_MAX; idx++) { + if (bnxt_auxdev_is_active(bp, idx)) { + auxiliary_device_delete(&bp->aux_priv[idx]->aux_dev); + bnxt_auxdev_set_state(bp, idx, BNXT_ADEV_STATE_INIT); + } + } + mutex_unlock(&bp->auxdev_lock); } static void bnxt_set_edev_info(struct bnxt_en_dev *edev, struct bnxt *bp) @@ -450,83 +522,106 @@ static void bnxt_set_edev_info(struct bnxt_en_dev *edev, struct bnxt *bp) edev->bar0 = bp->bar0; } -void bnxt_rdma_aux_device_add(struct bnxt *bp) +void bnxt_aux_devices_add(struct bnxt *bp) { struct auxiliary_device *aux_dev; - int rc; + int rc, idx; - if (!bp->edev) - return; - - aux_dev = &bp->aux_priv->aux_dev; - rc = auxiliary_device_add(aux_dev); - if (rc) { - netdev_warn(bp->dev, "Failed to add auxiliary device for ROCE\n"); - auxiliary_device_uninit(aux_dev); - bp->flags &= ~BNXT_FLAG_ROCE_CAP; + mutex_lock(&bp->auxdev_lock); + for (idx = 0; idx < __BNXT_AUXDEV_MAX; idx++) { + if (bnxt_auxdev_is_init(bp, idx)) { + aux_dev = &bp->aux_priv[idx]->aux_dev; + rc = auxiliary_device_add(aux_dev); + if (rc) { + netdev_warn(bp->dev, "Failed to add auxiliary device for auxdev type %d\n", + idx); + auxiliary_device_uninit(aux_dev); + if (idx == BNXT_AUXDEV_RDMA) + bp->flags &= ~BNXT_FLAG_ROCE_CAP; + continue; + } + bnxt_auxdev_set_state(bp, idx, BNXT_ADEV_STATE_ADD); + } } + mutex_unlock(&bp->auxdev_lock); } -void bnxt_rdma_aux_device_init(struct bnxt *bp) +void bnxt_aux_devices_init(struct bnxt *bp) { struct auxiliary_device *aux_dev; struct bnxt_aux_priv *aux_priv; struct bnxt_en_dev *edev; struct bnxt_ulp *ulp; - int rc; + int rc, idx; - if (!(bp->flags & BNXT_FLAG_ROCE_CAP)) - return; + mutex_lock(&bp->auxdev_lock); + for (idx = 0; idx < __BNXT_AUXDEV_MAX; idx++) { + bnxt_auxdev_set_state(bp, idx, BNXT_ADEV_STATE_NONE); - aux_priv = kzalloc_obj(*bp->aux_priv); - if (!aux_priv) - goto exit; + if (idx == BNXT_AUXDEV_RDMA && + !(bp->flags & BNXT_FLAG_ROCE_CAP)) + continue; - aux_priv->id = ida_alloc(&bnxt_aux_dev_ids, GFP_KERNEL); - if (aux_priv->id < 0) { - netdev_warn(bp->dev, - "ida alloc failed for ROCE auxiliary device\n"); - kfree(aux_priv); - goto exit; - } + aux_priv = kzalloc_obj(*aux_priv); + if (!aux_priv) + goto next_auxdev; - aux_dev = &aux_priv->aux_dev; - aux_dev->id = aux_priv->id; - aux_dev->name = "rdma"; - aux_dev->dev.parent = &bp->pdev->dev; - aux_dev->dev.release = bnxt_aux_dev_release; + aux_dev = &aux_priv->aux_dev; + aux_dev->id = bp->auxdev_id; + aux_dev->name = bnxt_aux_devices[idx].name; + aux_dev->dev.parent = &bp->pdev->dev; + aux_dev->dev.release = bnxt_aux_dev_release; - rc = auxiliary_device_init(aux_dev); - if (rc) { - ida_free(&bnxt_aux_dev_ids, aux_priv->id); - kfree(aux_priv); - goto exit; - } - bp->aux_priv = aux_priv; + rc = auxiliary_device_init(aux_dev); + if (rc) { + kfree(aux_priv); + goto next_auxdev; + } + bp->aux_priv[idx] = aux_priv; - /* From this point, all cleanup will happen via the .release callback & - * any error unwinding will need to include a call to - * auxiliary_device_uninit. - */ - edev = kzalloc_obj(*edev); - if (!edev) - goto aux_dev_uninit; + /* From this point, all cleanup will happen via the .release + * callback & any error unwinding will need to include a call + * to auxiliary_device_uninit. + */ + edev = kzalloc_obj(*edev); + if (!edev) + goto aux_dev_uninit; - aux_priv->edev = edev; + aux_priv->edev = edev; + bnxt_set_edev_info(edev, bp); - ulp = kzalloc_obj(*ulp); - if (!ulp) - goto aux_dev_uninit; + ulp = kzalloc_obj(*ulp); + if (!ulp) + goto aux_dev_uninit; - edev->ulp_tbl = ulp; - bp->edev = edev; - bnxt_set_edev_info(edev, bp); - bp->ulp_num_msix_want = bnxt_set_dflt_ulp_msix(bp); - - return; + edev->ulp_tbl = ulp; + bp->edev[idx] = edev; + if (idx == BNXT_AUXDEV_RDMA) + bp->ulp_num_msix_want = bnxt_set_dflt_ulp_msix(bp); + aux_priv->id = idx; + bnxt_auxdev_set_state(bp, idx, BNXT_ADEV_STATE_INIT); + continue; aux_dev_uninit: - auxiliary_device_uninit(aux_dev); -exit: - bp->flags &= ~BNXT_FLAG_ROCE_CAP; + auxiliary_device_uninit(aux_dev); +next_auxdev: + if (idx == BNXT_AUXDEV_RDMA) + bp->flags &= ~BNXT_FLAG_ROCE_CAP; + } + mutex_unlock(&bp->auxdev_lock); +} + +int bnxt_auxdev_id_alloc(struct bnxt *bp) +{ + bp->auxdev_id = ida_alloc(&bnxt_aux_dev_ids, GFP_KERNEL); + if (bp->auxdev_id < 0) + return bp->auxdev_id; + + return 0; +} + +void bnxt_auxdev_id_free(struct bnxt *bp, int id) +{ + if (bp->auxdev_id >= 0) + ida_free(&bnxt_aux_dev_ids, id); } diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c index fa5857923db4..b4bfd6c174e7 100644 --- a/drivers/net/ethernet/cirrus/cs89x0.c +++ b/drivers/net/ethernet/cirrus/cs89x0.c @@ -1271,7 +1271,6 @@ static const struct net_device_ops net_ops = { static void __init reset_chip(struct net_device *dev) { -#if !defined(CONFIG_MACH_MX31ADS) struct net_local *lp = netdev_priv(dev); unsigned long reset_start_time; @@ -1298,7 +1297,6 @@ static void __init reset_chip(struct net_device *dev) while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 && time_before(jiffies, reset_start_time + 2)) ; -#endif /* !CONFIG_MACH_MX31ADS */ } /* This is the real probe routine. diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 4824232f4890..4c762229ce42 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -122,6 +122,9 @@ struct gemini_ethernet_port { struct napi_struct napi; struct hrtimer rx_coalesce_timer; unsigned int rx_coalesce_nsecs; + struct sk_buff *rx_skb; + unsigned int rx_frag_nr; + unsigned int freeq_refill; struct gmac_txq txq[TX_QUEUE_NUM]; unsigned int txq_order; @@ -1442,10 +1445,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) unsigned short m = (1 << port->rxq_order) - 1; struct gemini_ethernet *geth = port->geth; void __iomem *ptr_reg = port->rxq_rwptr; + unsigned int frag_nr = port->rx_frag_nr; + struct sk_buff *skb = port->rx_skb; unsigned int frame_len, frag_len; struct gmac_rxdesc *rx = NULL; struct gmac_queue_page *gpage; - static struct sk_buff *skb; union gmac_rxdesc_0 word0; union gmac_rxdesc_1 word1; union gmac_rxdesc_3 word3; @@ -1455,7 +1459,6 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) unsigned short r, w; union dma_rwptr rw; dma_addr_t mapping; - int frag_nr = 0; spin_lock_irqsave(&geth->irq_lock, flags); rw.bits32 = readl(ptr_reg); @@ -1491,6 +1494,12 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE); if (!gpage) { dev_err(geth->dev, "could not find mapping\n"); + port->stats.rx_dropped++; + if (skb) { + napi_free_frags(&port->napi); + skb = NULL; + frag_nr = 0; + } continue; } page = gpage->page; @@ -1499,6 +1508,8 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) if (skb) { napi_free_frags(&port->napi); port->stats.rx_dropped++; + skb = NULL; + frag_nr = 0; } skb = gmac_skb_if_good_frame(port, word0, frame_len); @@ -1533,6 +1544,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) if (word3.bits32 & EOF_BIT) { napi_gro_frags(&port->napi); skb = NULL; + frag_nr = 0; --budget; } continue; @@ -1541,6 +1553,7 @@ err_drop: if (skb) { napi_free_frags(&port->napi); skb = NULL; + frag_nr = 0; } if (mapping) @@ -1549,6 +1562,8 @@ err_drop: port->stats.rx_dropped++; } + port->rx_skb = skb; + port->rx_frag_nr = frag_nr; writew(r, ptr_reg); return budget; } @@ -1876,6 +1891,8 @@ static int gmac_stop(struct net_device *netdev) gmac_disable_tx_rx(netdev); gmac_stop_dma(port); napi_disable(&port->napi); + port->rx_skb = NULL; + port->rx_frag_nr = 0; gmac_enable_irq(netdev, 0); gmac_cleanup_rxq(netdev); diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h index e663bb5e614e..e691144e8756 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.h +++ b/drivers/net/ethernet/freescale/enetc/enetc.h @@ -330,6 +330,7 @@ struct enetc_si { struct workqueue_struct *workqueue; struct work_struct rx_mode_task; struct dentry *debugfs_root; + struct enetc_msg_swbd msg; /* Only valid for VSI */ }; #define ENETC_SI_ALIGN 32 diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c index 6c4b374bcb0e..df8e95cc47d0 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c @@ -17,11 +17,36 @@ static void enetc_msg_vsi_write_msg(struct enetc_hw *hw, enetc_wr(hw, ENETC_VSIMSGSNDAR0, val); } +static void enetc_msg_dma_free(struct device *dev, struct enetc_msg_swbd *msg) +{ + if (msg->vaddr) { + dma_free_coherent(dev, msg->size, msg->vaddr, msg->dma); + msg->vaddr = NULL; + } +} + static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg) { + struct device *dev = &si->pdev->dev; int timeout = 100; u32 vsimsgsr; + /* The VSI mailbox may be busy if last message was not yet processed + * by PSI. So need to check the mailbox status before sending. + */ + vsimsgsr = enetc_rd(&si->hw, ENETC_VSIMSGSR); + if (vsimsgsr & ENETC_VSIMSGSR_MB) { + /* It is safe to free the DMA buffer here, the caller does + * not access the DMA buffer if enetc_msg_vsi_send() fails. + */ + enetc_msg_dma_free(dev, msg); + dev_err(dev, "VSI mailbox is busy\n"); + return -EIO; + } + + /* Free the DMA buffer of the last message */ + enetc_msg_dma_free(dev, &si->msg); + si->msg = *msg; enetc_msg_vsi_write_msg(&si->hw, msg); do { @@ -32,12 +57,15 @@ static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg) usleep_range(1000, 2000); } while (--timeout); - if (!timeout) + if (!timeout) { + dev_err(dev, "VSI mailbox timeout\n"); + return -ETIMEDOUT; + } /* check for message delivery error */ if (vsimsgsr & ENETC_VSIMSGSR_MS) { - dev_err(&si->pdev->dev, "VSI command execute error: %d\n", + dev_err(dev, "VSI command execute error: %d\n", ENETC_SIMSGSR_GET_MC(vsimsgsr)); return -EIO; } @@ -50,7 +78,6 @@ static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv, { struct enetc_msg_cmd_set_primary_mac *cmd; struct enetc_msg_swbd msg; - int err; msg.size = ALIGN(sizeof(struct enetc_msg_cmd_set_primary_mac), 64); msg.vaddr = dma_alloc_coherent(priv->dev, msg.size, &msg.dma, @@ -67,11 +94,7 @@ static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv, memcpy(&cmd->mac, saddr, sizeof(struct sockaddr)); /* send the command and wait */ - err = enetc_msg_vsi_send(priv->si, &msg); - - dma_free_coherent(priv->dev, msg.size, msg.vaddr, msg.dma); - - return err; + return enetc_msg_vsi_send(priv->si, &msg); } static int enetc_vf_set_mac_addr(struct net_device *ndev, void *addr) @@ -259,6 +282,7 @@ static void enetc_vf_remove(struct pci_dev *pdev) { struct enetc_si *si = pci_get_drvdata(pdev); struct enetc_ndev_priv *priv; + struct enetc_msg_swbd msg; priv = netdev_priv(si->ndev); unregister_netdev(si->ndev); @@ -270,7 +294,9 @@ static void enetc_vf_remove(struct pci_dev *pdev) free_netdev(si->ndev); + msg = si->msg; enetc_pci_remove(pdev); + enetc_msg_dma_free(&pdev->dev, &msg); } static const struct pci_device_id enetc_vf_id_table[] = { diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c index 703752995e93..c94a928622fd 100644 --- a/drivers/net/ethernet/freescale/enetc/ntmp.c +++ b/drivers/net/ethernet/freescale/enetc/ntmp.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "ntmp_private.h" @@ -42,6 +43,12 @@ int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev, if (!cbdr->addr_base) return -ENOMEM; + cbdr->swcbd = vcalloc(cbd_num, sizeof(struct netc_swcbd)); + if (!cbdr->swcbd) { + dma_free_coherent(dev, size, cbdr->addr_base, cbdr->dma_base); + return -ENOMEM; + } + cbdr->dma_size = size; cbdr->bd_num = cbd_num; cbdr->regs = *regs; @@ -52,10 +59,10 @@ int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev, cbdr->addr_base_align = PTR_ALIGN(cbdr->addr_base, NTMP_BASE_ADDR_ALIGN); - spin_lock_init(&cbdr->ring_lock); + mutex_init(&cbdr->ring_lock); cbdr->next_to_use = netc_read(cbdr->regs.pir); - cbdr->next_to_clean = netc_read(cbdr->regs.cir); + cbdr->next_to_clean = netc_read(cbdr->regs.cir) & NETC_CBDRCIR_INDEX; /* Step 1: Configure the base address of the Control BD Ring */ netc_write(cbdr->regs.bar0, lower_32_bits(cbdr->dma_base_align)); @@ -71,10 +78,24 @@ int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev, } EXPORT_SYMBOL_GPL(ntmp_init_cbdr); +static void ntmp_free_data_mem(struct device *dev, struct netc_swcbd *swcbd) +{ + if (unlikely(!swcbd->buf)) + return; + + dma_free_coherent(dev, swcbd->size + NTMP_DATA_ADDR_ALIGN, + swcbd->buf, swcbd->dma); +} + void ntmp_free_cbdr(struct netc_cbdr *cbdr) { /* Disable the Control BD Ring */ netc_write(cbdr->regs.mr, 0); + + for (int i = 0; i < cbdr->bd_num; i++) + ntmp_free_data_mem(cbdr->dev, &cbdr->swcbd[i]); + + vfree(cbdr->swcbd); dma_free_coherent(cbdr->dev, cbdr->dma_size, cbdr->addr_base, cbdr->dma_base); memset(cbdr, 0, sizeof(*cbdr)); @@ -94,40 +115,59 @@ static union netc_cbd *ntmp_get_cbd(struct netc_cbdr *cbdr, int index) static void ntmp_clean_cbdr(struct netc_cbdr *cbdr) { - union netc_cbd *cbd; - int i; + int i = cbdr->next_to_clean; - i = cbdr->next_to_clean; - while (netc_read(cbdr->regs.cir) != i) { - cbd = ntmp_get_cbd(cbdr, i); + while ((netc_read(cbdr->regs.cir) & NETC_CBDRCIR_INDEX) != i) { + union netc_cbd *cbd = ntmp_get_cbd(cbdr, i); + struct netc_swcbd *swcbd = &cbdr->swcbd[i]; + + ntmp_free_data_mem(cbdr->dev, swcbd); + memset(swcbd, 0, sizeof(*swcbd)); memset(cbd, 0, sizeof(*cbd)); i = (i + 1) % cbdr->bd_num; } + dma_wmb(); cbdr->next_to_clean = i; } -static int netc_xmit_ntmp_cmd(struct ntmp_user *user, union netc_cbd *cbd) +static void ntmp_select_and_lock_cbdr(struct ntmp_user *user, + struct netc_cbdr **cbdr) +{ + /* Currently only ENETC is supported, and it has only one command + * BD ring. + */ + *cbdr = &user->ring[0]; + + mutex_lock(&(*cbdr)->ring_lock); +} + +static void ntmp_unlock_cbdr(struct netc_cbdr *cbdr) +{ + mutex_unlock(&cbdr->ring_lock); +} + +static int netc_xmit_ntmp_cmd(struct netc_cbdr *cbdr, union netc_cbd *cbd, + struct netc_swcbd *swcbd) { union netc_cbd *cur_cbd; - struct netc_cbdr *cbdr; - int i, err; + int i, err, used_bds; u16 status; u32 val; - /* Currently only i.MX95 ENETC is supported, and it only has one - * command BD ring - */ - cbdr = &user->ring[0]; - - spin_lock_bh(&cbdr->ring_lock); - - if (unlikely(!ntmp_get_free_cbd_num(cbdr))) + used_bds = cbdr->bd_num - ntmp_get_free_cbd_num(cbdr); + if (unlikely(used_bds >= NETC_CBDR_CLEAN_WORK)) { ntmp_clean_cbdr(cbdr); + if (unlikely(!ntmp_get_free_cbd_num(cbdr))) { + ntmp_free_data_mem(cbdr->dev, swcbd); + return -EBUSY; + } + } i = cbdr->next_to_use; cur_cbd = ntmp_get_cbd(cbdr, i); *cur_cbd = *cbd; + cbdr->swcbd[i] = *swcbd; dma_wmb(); /* Update producer index of both software and hardware */ @@ -135,11 +175,17 @@ static int netc_xmit_ntmp_cmd(struct ntmp_user *user, union netc_cbd *cbd) cbdr->next_to_use = i; netc_write(cbdr->regs.pir, i); - err = read_poll_timeout_atomic(netc_read, val, val == i, - NETC_CBDR_DELAY_US, NETC_CBDR_TIMEOUT, - true, cbdr->regs.cir); + err = read_poll_timeout(netc_read, val, + (val & NETC_CBDRCIR_INDEX) == i, + NETC_CBDR_DELAY_US, NETC_CBDR_TIMEOUT, + true, cbdr->regs.cir); if (unlikely(err)) - goto cbdr_unlock; + return err; + + if (unlikely(val & NETC_CBDRCIR_SBE)) { + dev_err(cbdr->dev, "Command BD system bus error\n"); + return -EIO; + } dma_rmb(); /* Get the writeback command BD, because the caller may need @@ -150,38 +196,27 @@ static int netc_xmit_ntmp_cmd(struct ntmp_user *user, union netc_cbd *cbd) /* Check the writeback error status */ status = le16_to_cpu(cbd->resp_hdr.error_rr) & NTMP_RESP_ERROR; if (unlikely(status)) { - err = -EIO; - dev_err(user->dev, "Command BD error: 0x%04x\n", status); + dev_err(cbdr->dev, "Command BD error: 0x%04x\n", status); + return -EIO; } - ntmp_clean_cbdr(cbdr); - dma_wmb(); - -cbdr_unlock: - spin_unlock_bh(&cbdr->ring_lock); - - return err; -} - -static int ntmp_alloc_data_mem(struct ntmp_dma_buf *data, void **buf_align) -{ - void *buf; - - buf = dma_alloc_coherent(data->dev, data->size + NTMP_DATA_ADDR_ALIGN, - &data->dma, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - data->buf = buf; - *buf_align = PTR_ALIGN(buf, NTMP_DATA_ADDR_ALIGN); - return 0; } -static void ntmp_free_data_mem(struct ntmp_dma_buf *data) +static int ntmp_alloc_data_mem(struct device *dev, struct netc_swcbd *swcbd, + void **buf_align) { - dma_free_coherent(data->dev, data->size + NTMP_DATA_ADDR_ALIGN, - data->buf, data->dma); + void *buf; + + buf = dma_alloc_coherent(dev, swcbd->size + NTMP_DATA_ADDR_ALIGN, + &swcbd->dma, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + swcbd->buf = buf; + *buf_align = PTR_ALIGN(buf, NTMP_DATA_ADDR_ALIGN); + + return 0; } static void ntmp_fill_request_hdr(union netc_cbd *cbd, dma_addr_t dma, @@ -234,37 +269,39 @@ static int ntmp_delete_entry_by_id(struct ntmp_user *user, int tbl_id, u8 tbl_ver, u32 entry_id, u32 req_len, u32 resp_len) { - struct ntmp_dma_buf data = { - .dev = user->dev, + struct netc_swcbd swcbd = { .size = max(req_len, resp_len), }; struct ntmp_req_by_eid *req; + struct netc_cbdr *cbdr; union netc_cbd cbd; int err; - err = ntmp_alloc_data_mem(&data, (void **)&req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); if (err) return err; ntmp_fill_crd_eid(req, tbl_ver, 0, 0, entry_id); - ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(req_len, resp_len), + ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(req_len, resp_len), tbl_id, NTMP_CMD_DELETE, NTMP_AM_ENTRY_ID); - err = netc_xmit_ntmp_cmd(user, &cbd); + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); if (err) dev_err(user->dev, "Failed to delete entry 0x%x of %s, err: %pe", entry_id, ntmp_table_name(tbl_id), ERR_PTR(err)); - - ntmp_free_data_mem(&data); + ntmp_unlock_cbdr(cbdr); return err; } -static int ntmp_query_entry_by_id(struct ntmp_user *user, int tbl_id, - u32 len, struct ntmp_req_by_eid *req, - dma_addr_t dma, bool compare_eid) +static int ntmp_query_entry_by_id(struct netc_cbdr *cbdr, int tbl_id, + struct ntmp_req_by_eid *req, + struct netc_swcbd *swcbd, + bool compare_eid) { + u32 len = NTMP_LEN(sizeof(*req), swcbd->size); struct ntmp_cmn_resp_query *resp; int cmd = NTMP_CMD_QUERY; union netc_cbd cbd; @@ -276,10 +313,11 @@ static int ntmp_query_entry_by_id(struct ntmp_user *user, int tbl_id, cmd = NTMP_CMD_QU; /* Request header */ - ntmp_fill_request_hdr(&cbd, dma, len, tbl_id, cmd, NTMP_AM_ENTRY_ID); - err = netc_xmit_ntmp_cmd(user, &cbd); + ntmp_fill_request_hdr(&cbd, swcbd->dma, len, tbl_id, cmd, + NTMP_AM_ENTRY_ID); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, swcbd); if (err) { - dev_err(user->dev, + dev_err(cbdr->dev, "Failed to query entry 0x%x of %s, err: %pe\n", entry_id, ntmp_table_name(tbl_id), ERR_PTR(err)); return err; @@ -293,7 +331,7 @@ static int ntmp_query_entry_by_id(struct ntmp_user *user, int tbl_id, resp = (struct ntmp_cmn_resp_query *)req; if (unlikely(le32_to_cpu(resp->entry_id) != entry_id)) { - dev_err(user->dev, + dev_err(cbdr->dev, "%s: query EID 0x%x doesn't match response EID 0x%x\n", ntmp_table_name(tbl_id), entry_id, le32_to_cpu(resp->entry_id)); return -EIO; @@ -305,15 +343,15 @@ static int ntmp_query_entry_by_id(struct ntmp_user *user, int tbl_id, int ntmp_maft_add_entry(struct ntmp_user *user, u32 entry_id, struct maft_entry_data *maft) { - struct ntmp_dma_buf data = { - .dev = user->dev, + struct netc_swcbd swcbd = { .size = sizeof(struct maft_req_add), }; struct maft_req_add *req; + struct netc_cbdr *cbdr; union netc_cbd cbd; int err; - err = ntmp_alloc_data_mem(&data, (void **)&req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); if (err) return err; @@ -322,14 +360,15 @@ int ntmp_maft_add_entry(struct ntmp_user *user, u32 entry_id, req->keye = maft->keye; req->cfge = maft->cfge; - ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(data.size, 0), + ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0), NTMP_MAFT_ID, NTMP_CMD_ADD, NTMP_AM_ENTRY_ID); - err = netc_xmit_ntmp_cmd(user, &cbd); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); if (err) dev_err(user->dev, "Failed to add MAFT entry 0x%x, err: %pe\n", entry_id, ERR_PTR(err)); - - ntmp_free_data_mem(&data); + ntmp_unlock_cbdr(cbdr); return err; } @@ -338,31 +377,31 @@ EXPORT_SYMBOL_GPL(ntmp_maft_add_entry); int ntmp_maft_query_entry(struct ntmp_user *user, u32 entry_id, struct maft_entry_data *maft) { - struct ntmp_dma_buf data = { - .dev = user->dev, + struct netc_swcbd swcbd = { .size = sizeof(struct maft_resp_query), }; struct maft_resp_query *resp; struct ntmp_req_by_eid *req; + struct netc_cbdr *cbdr; int err; - err = ntmp_alloc_data_mem(&data, (void **)&req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); if (err) return err; ntmp_fill_crd_eid(req, user->tbl.maft_ver, 0, 0, entry_id); - err = ntmp_query_entry_by_id(user, NTMP_MAFT_ID, - NTMP_LEN(sizeof(*req), data.size), - req, data.dma, true); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = ntmp_query_entry_by_id(cbdr, NTMP_MAFT_ID, req, &swcbd, true); if (err) - goto end; + goto unlock_cbdr; resp = (struct maft_resp_query *)req; maft->keye = resp->keye; maft->cfge = resp->cfge; -end: - ntmp_free_data_mem(&data); +unlock_cbdr: + ntmp_unlock_cbdr(cbdr); return err; } @@ -378,8 +417,9 @@ EXPORT_SYMBOL_GPL(ntmp_maft_delete_entry); int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table, int count) { - struct ntmp_dma_buf data = {.dev = user->dev}; struct rsst_req_update *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; union netc_cbd cbd; int err, i; @@ -387,8 +427,8 @@ int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table, /* HW only takes in a full 64 entry table */ return -EINVAL; - data.size = struct_size(req, groups, count); - err = ntmp_alloc_data_mem(&data, (void **)&req); + swcbd.size = struct_size(req, groups, count); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); if (err) return err; @@ -398,15 +438,15 @@ int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table, for (i = 0; i < count; i++) req->groups[i] = (u8)(table[i]); - ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(data.size, 0), + ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0), NTMP_RSST_ID, NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID); - err = netc_xmit_ntmp_cmd(user, &cbd); + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); if (err) dev_err(user->dev, "Failed to update RSST entry, err: %pe\n", ERR_PTR(err)); - - ntmp_free_data_mem(&data); + ntmp_unlock_cbdr(cbdr); return err; } @@ -414,8 +454,9 @@ EXPORT_SYMBOL_GPL(ntmp_rsst_update_entry); int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count) { - struct ntmp_dma_buf data = {.dev = user->dev}; struct ntmp_req_by_eid *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; union netc_cbd cbd; int err, i; u8 *group; @@ -424,21 +465,23 @@ int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count) /* HW only takes in a full 64 entry table */ return -EINVAL; - data.size = NTMP_ENTRY_ID_SIZE + RSST_STSE_DATA_SIZE(count) + - RSST_CFGE_DATA_SIZE(count); - err = ntmp_alloc_data_mem(&data, (void **)&req); + swcbd.size = NTMP_ENTRY_ID_SIZE + RSST_STSE_DATA_SIZE(count) + + RSST_CFGE_DATA_SIZE(count); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); if (err) return err; /* Set the request data buffer */ ntmp_fill_crd_eid(req, user->tbl.rsst_ver, 0, 0, 0); - ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(sizeof(*req), data.size), + ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(sizeof(*req), swcbd.size), NTMP_RSST_ID, NTMP_CMD_QUERY, NTMP_AM_ENTRY_ID); - err = netc_xmit_ntmp_cmd(user, &cbd); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); if (err) { dev_err(user->dev, "Failed to query RSST entry, err: %pe\n", ERR_PTR(err)); - goto end; + goto unlock_cbdr; } group = (u8 *)req; @@ -446,8 +489,8 @@ int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count) for (i = 0; i < count; i++) table[i] = group[i]; -end: - ntmp_free_data_mem(&data); +unlock_cbdr: + ntmp_unlock_cbdr(cbdr); return err; } diff --git a/drivers/net/ethernet/freescale/enetc/ntmp_private.h b/drivers/net/ethernet/freescale/enetc/ntmp_private.h index 34394e40fddd..f8dff3ba2c28 100644 --- a/drivers/net/ethernet/freescale/enetc/ntmp_private.h +++ b/drivers/net/ethernet/freescale/enetc/ntmp_private.h @@ -12,6 +12,9 @@ #define NTMP_EID_REQ_LEN 8 #define NETC_CBDR_BD_NUM 256 +#define NETC_CBDRCIR_INDEX GENMASK(9, 0) +#define NETC_CBDRCIR_SBE BIT(31) +#define NETC_CBDR_CLEAN_WORK 16 union netc_cbd { struct { @@ -54,13 +57,6 @@ union netc_cbd { } resp_hdr; /* NTMP Response Message Header Format */ }; -struct ntmp_dma_buf { - struct device *dev; - size_t size; - void *buf; - dma_addr_t dma; -}; - struct ntmp_cmn_req_data { __le16 update_act; u8 dbg_opt; diff --git a/drivers/net/ethernet/fujitsu/Kconfig b/drivers/net/ethernet/fujitsu/Kconfig deleted file mode 100644 index 06a28bce5d27..000000000000 --- a/drivers/net/ethernet/fujitsu/Kconfig +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Fujitsu Network device configuration -# - -config NET_VENDOR_FUJITSU - bool "Fujitsu devices" - default y - depends on PCMCIA - help - If you have a network (Ethernet) card belonging to this class, say Y. - - Note that the answer to this question doesn't directly affect the - the questions about Fujitsu cards. If you say Y, you will be asked for - your specific card in the following questions. - -if NET_VENDOR_FUJITSU - -config PCMCIA_FMVJ18X - tristate "Fujitsu FMV-J18x PCMCIA support" - depends on PCMCIA && HAS_IOPORT - select CRC32 - help - Say Y here if you intend to attach a Fujitsu FMV-J18x or compatible - PCMCIA (PC-card) Ethernet card to your computer. - - To compile this driver as a module, choose M here: the module will be - called fmvj18x_cs. If unsure, say N. - -endif # NET_VENDOR_FUJITSU diff --git a/drivers/net/ethernet/fujitsu/Makefile b/drivers/net/ethernet/fujitsu/Makefile deleted file mode 100644 index 74feebbf4572..000000000000 --- a/drivers/net/ethernet/fujitsu/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Fujitsu network device drivers. -# - -obj-$(CONFIG_PCMCIA_FMVJ18X) += fmvj18x_cs.o diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c deleted file mode 100644 index 4859493471db..000000000000 --- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c +++ /dev/null @@ -1,1176 +0,0 @@ -/*====================================================================== - fmvj18x_cs.c 2.8 2002/03/23 - - A fmvj18x (and its compatibles) PCMCIA client driver - - Contributed by Shingo Fujimoto, shingo@flab.fujitsu.co.jp - - TDK LAK-CD021 and CONTEC C-NET(PC)C support added by - Nobuhiro Katayama, kata-n@po.iijnet.or.jp - - The PCMCIA client code is based on code written by David Hinds. - Network code is based on the "FMV-18x driver" by Yutaka TAMIYA - but is actually largely Donald Becker's AT1700 driver, which - carries the following attribution: - - Written 1993-94 by Donald Becker. - - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - -======================================================================*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define DRV_NAME "fmvj18x_cs" -#define DRV_VERSION "2.9" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -/*====================================================================*/ - -/* Module parameters */ - -MODULE_DESCRIPTION("fmvj18x and compatible PCMCIA ethernet driver"); -MODULE_LICENSE("GPL"); - -#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) - -/* SRAM configuration */ -/* 0:4KB*2 TX buffer else:8KB*2 TX buffer */ -INT_MODULE_PARM(sram_config, 0); - - -/*====================================================================*/ -/* - PCMCIA event handlers - */ -static int fmvj18x_config(struct pcmcia_device *link); -static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id); -static int fmvj18x_setup_mfc(struct pcmcia_device *link); -static void fmvj18x_release(struct pcmcia_device *link); -static void fmvj18x_detach(struct pcmcia_device *p_dev); - -/* - LAN controller(MBH86960A) specific routines - */ -static int fjn_config(struct net_device *dev, struct ifmap *map); -static int fjn_open(struct net_device *dev); -static int fjn_close(struct net_device *dev); -static netdev_tx_t fjn_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t fjn_interrupt(int irq, void *dev_id); -static void fjn_rx(struct net_device *dev); -static void fjn_reset(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static void fjn_tx_timeout(struct net_device *dev, unsigned int txqueue); -static const struct ethtool_ops netdev_ethtool_ops; - -/* - card type - */ -enum cardtype { MBH10302, MBH10304, TDK, CONTEC, LA501, UNGERMANN, - XXX10304, NEC, KME -}; - -/* - driver specific data structure -*/ -struct local_info { - struct pcmcia_device *p_dev; - long open_time; - uint tx_started:1; - uint tx_queue; - u_short tx_queue_len; - enum cardtype cardtype; - u_short sent; - u_char __iomem *base; -}; - -#define MC_FILTERBREAK 64 - -/*====================================================================*/ -/* - ioport offset from the base address - */ -#define TX_STATUS 0 /* transmit status register */ -#define RX_STATUS 1 /* receive status register */ -#define TX_INTR 2 /* transmit interrupt mask register */ -#define RX_INTR 3 /* receive interrupt mask register */ -#define TX_MODE 4 /* transmit mode register */ -#define RX_MODE 5 /* receive mode register */ -#define CONFIG_0 6 /* configuration register 0 */ -#define CONFIG_1 7 /* configuration register 1 */ - -#define NODE_ID 8 /* node ID register (bank 0) */ -#define MAR_ADR 8 /* multicast address registers (bank 1) */ - -#define DATAPORT 8 /* buffer mem port registers (bank 2) */ -#define TX_START 10 /* transmit start register */ -#define COL_CTRL 11 /* 16 collision control register */ -#define BMPR12 12 /* reserved */ -#define BMPR13 13 /* reserved */ -#define RX_SKIP 14 /* skip received packet register */ - -#define LAN_CTRL 16 /* LAN card control register */ - -#define MAC_ID 0x1a /* hardware address */ -#define UNGERMANN_MAC_ID 0x18 /* UNGERMANN-BASS hardware address */ - -/* - control bits - */ -#define ENA_TMT_OK 0x80 -#define ENA_TMT_REC 0x20 -#define ENA_COL 0x04 -#define ENA_16_COL 0x02 -#define ENA_TBUS_ERR 0x01 - -#define ENA_PKT_RDY 0x80 -#define ENA_BUS_ERR 0x40 -#define ENA_LEN_ERR 0x08 -#define ENA_ALG_ERR 0x04 -#define ENA_CRC_ERR 0x02 -#define ENA_OVR_FLO 0x01 - -/* flags */ -#define F_TMT_RDY 0x80 /* can accept new packet */ -#define F_NET_BSY 0x40 /* carrier is detected */ -#define F_TMT_OK 0x20 /* send packet successfully */ -#define F_SRT_PKT 0x10 /* short packet error */ -#define F_COL_ERR 0x04 /* collision error */ -#define F_16_COL 0x02 /* 16 collision error */ -#define F_TBUS_ERR 0x01 /* bus read error */ - -#define F_PKT_RDY 0x80 /* packet(s) in buffer */ -#define F_BUS_ERR 0x40 /* bus read error */ -#define F_LEN_ERR 0x08 /* short packet */ -#define F_ALG_ERR 0x04 /* frame error */ -#define F_CRC_ERR 0x02 /* CRC error */ -#define F_OVR_FLO 0x01 /* overflow error */ - -#define F_BUF_EMP 0x40 /* receive buffer is empty */ - -#define F_SKP_PKT 0x05 /* drop packet in buffer */ - -/* default bitmaps */ -#define D_TX_INTR ( ENA_TMT_OK ) -#define D_RX_INTR ( ENA_PKT_RDY | ENA_LEN_ERR \ - | ENA_ALG_ERR | ENA_CRC_ERR | ENA_OVR_FLO ) -#define TX_STAT_M ( F_TMT_RDY ) -#define RX_STAT_M ( F_PKT_RDY | F_LEN_ERR \ - | F_ALG_ERR | F_CRC_ERR | F_OVR_FLO ) - -/* commands */ -#define D_TX_MODE 0x06 /* no tests, detect carrier */ -#define ID_MATCHED 0x02 /* (RX_MODE) */ -#define RECV_ALL 0x03 /* (RX_MODE) */ -#define CONFIG0_DFL 0x5a /* 16bit bus, 4K x 2 Tx queues */ -#define CONFIG0_DFL_1 0x5e /* 16bit bus, 8K x 2 Tx queues */ -#define CONFIG0_RST 0xda /* Data Link Controller off (CONFIG_0) */ -#define CONFIG0_RST_1 0xde /* Data Link Controller off (CONFIG_0) */ -#define BANK_0 0xa0 /* bank 0 (CONFIG_1) */ -#define BANK_1 0xa4 /* bank 1 (CONFIG_1) */ -#define BANK_2 0xa8 /* bank 2 (CONFIG_1) */ -#define CHIP_OFF 0x80 /* contrl chip power off (CONFIG_1) */ -#define DO_TX 0x80 /* do transmit packet */ -#define SEND_PKT 0x81 /* send a packet */ -#define AUTO_MODE 0x07 /* Auto skip packet on 16 col detected */ -#define MANU_MODE 0x03 /* Stop and skip packet on 16 col */ -#define TDK_AUTO_MODE 0x47 /* Auto skip packet on 16 col detected */ -#define TDK_MANU_MODE 0x43 /* Stop and skip packet on 16 col */ -#define INTR_OFF 0x0d /* LAN controller ignores interrupts */ -#define INTR_ON 0x1d /* LAN controller will catch interrupts */ - -#define TX_TIMEOUT ((400*HZ)/1000) - -#define BANK_0U 0x20 /* bank 0 (CONFIG_1) */ -#define BANK_1U 0x24 /* bank 1 (CONFIG_1) */ -#define BANK_2U 0x28 /* bank 2 (CONFIG_1) */ - -static const struct net_device_ops fjn_netdev_ops = { - .ndo_open = fjn_open, - .ndo_stop = fjn_close, - .ndo_start_xmit = fjn_start_xmit, - .ndo_tx_timeout = fjn_tx_timeout, - .ndo_set_config = fjn_config, - .ndo_set_rx_mode = set_rx_mode, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int fmvj18x_probe(struct pcmcia_device *link) -{ - struct local_info *lp; - struct net_device *dev; - - dev_dbg(&link->dev, "fmvj18x_attach()\n"); - - /* Make up a FMVJ18x specific data structure */ - dev = alloc_etherdev(sizeof(struct local_info)); - if (!dev) - return -ENOMEM; - lp = netdev_priv(dev); - link->priv = dev; - lp->p_dev = link; - lp->base = NULL; - - /* The io structure describes IO port mapping */ - link->resource[0]->end = 32; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - - /* General socket configuration */ - link->config_flags |= CONF_ENABLE_IRQ; - - dev->netdev_ops = &fjn_netdev_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - dev->ethtool_ops = &netdev_ethtool_ops; - - return fmvj18x_config(link); -} /* fmvj18x_attach */ - -/*====================================================================*/ - -static void fmvj18x_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - dev_dbg(&link->dev, "fmvj18x_detach\n"); - - unregister_netdev(dev); - - fmvj18x_release(link); - - free_netdev(dev); -} /* fmvj18x_detach */ - -/*====================================================================*/ - -static int mfc_try_io_port(struct pcmcia_device *link) -{ - int i, ret; - static const unsigned int serial_base[5] = - { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; - - for (i = 0; i < 5; i++) { - link->resource[1]->start = serial_base[i]; - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - if (link->resource[1]->start == 0) { - link->resource[1]->end = 0; - pr_notice("out of resource for serial\n"); - } - ret = pcmcia_request_io(link); - if (ret == 0) - return ret; - } - return ret; -} - -static int ungermann_try_io_port(struct pcmcia_device *link) -{ - int ret; - unsigned int ioaddr; - /* - Ungermann-Bass Access/CARD accepts 0x300,0x320,0x340,0x360 - 0x380,0x3c0 only for ioport. - */ - for (ioaddr = 0x300; ioaddr < 0x3e0; ioaddr += 0x20) { - link->resource[0]->start = ioaddr; - ret = pcmcia_request_io(link); - if (ret == 0) { - /* calculate ConfigIndex value */ - link->config_index = - ((link->resource[0]->start & 0x0f0) >> 3) | 0x22; - return ret; - } - } - return ret; /* RequestIO failed */ -} - -static int fmvj18x_ioprobe(struct pcmcia_device *p_dev, void *priv_data) -{ - return 0; /* strange, but that's what the code did already before... */ -} - -static int fmvj18x_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct local_info *lp = netdev_priv(dev); - int i, ret; - unsigned int ioaddr; - enum cardtype cardtype; - char *card_name = "unknown"; - u8 *buf; - size_t len; - u_char buggybuf[32]; - u8 addr[ETH_ALEN]; - - dev_dbg(&link->dev, "fmvj18x_config\n"); - - link->io_lines = 5; - - len = pcmcia_get_tuple(link, CISTPL_FUNCE, &buf); - kfree(buf); - - if (len) { - /* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */ - ret = pcmcia_loop_config(link, fmvj18x_ioprobe, NULL); - if (ret != 0) - goto failed; - - switch (link->manf_id) { - case MANFID_TDK: - cardtype = TDK; - if (link->card_id == PRODID_TDK_GN3410 || - link->card_id == PRODID_TDK_NP9610 || - link->card_id == PRODID_TDK_MN3200) { - /* MultiFunction Card */ - link->config_base = 0x800; - link->config_index = 0x47; - link->resource[1]->end = 8; - } - break; - case MANFID_NEC: - cardtype = NEC; /* MultiFunction Card */ - link->config_base = 0x800; - link->config_index = 0x47; - link->resource[1]->end = 8; - break; - case MANFID_KME: - cardtype = KME; /* MultiFunction Card */ - link->config_base = 0x800; - link->config_index = 0x47; - link->resource[1]->end = 8; - break; - case MANFID_CONTEC: - cardtype = CONTEC; - break; - case MANFID_FUJITSU: - if (link->config_base == 0x0fe0) - cardtype = MBH10302; - else if (link->card_id == PRODID_FUJITSU_MBH10302) - /* RATOC REX-5588/9822/4886's PRODID are 0004(=MBH10302), - but these are MBH10304 based card. */ - cardtype = MBH10304; - else if (link->card_id == PRODID_FUJITSU_MBH10304) - cardtype = MBH10304; - else - cardtype = LA501; - break; - default: - cardtype = MBH10304; - } - } else { - /* old type card */ - switch (link->manf_id) { - case MANFID_FUJITSU: - if (link->card_id == PRODID_FUJITSU_MBH10304) { - cardtype = XXX10304; /* MBH10304 with buggy CIS */ - link->config_index = 0x20; - } else { - cardtype = MBH10302; /* NextCom NC5310, etc. */ - link->config_index = 1; - } - break; - case MANFID_UNGERMANN: - cardtype = UNGERMANN; - break; - default: - cardtype = MBH10302; - link->config_index = 1; - } - } - - if (link->resource[1]->end != 0) { - ret = mfc_try_io_port(link); - if (ret != 0) goto failed; - } else if (cardtype == UNGERMANN) { - ret = ungermann_try_io_port(link); - if (ret != 0) goto failed; - } else { - ret = pcmcia_request_io(link); - if (ret) - goto failed; - } - ret = pcmcia_request_irq(link, fjn_interrupt); - if (ret) - goto failed; - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - dev->irq = link->irq; - dev->base_addr = link->resource[0]->start; - - if (resource_size(link->resource[1]) != 0) { - ret = fmvj18x_setup_mfc(link); - if (ret != 0) goto failed; - } - - ioaddr = dev->base_addr; - - /* Reset controller */ - if (sram_config == 0) - outb(CONFIG0_RST, ioaddr + CONFIG_0); - else - outb(CONFIG0_RST_1, ioaddr + CONFIG_0); - - /* Power On chip and select bank 0 */ - if (cardtype == MBH10302) - outb(BANK_0, ioaddr + CONFIG_1); - else - outb(BANK_0U, ioaddr + CONFIG_1); - - /* Set hardware address */ - switch (cardtype) { - case MBH10304: - case TDK: - case LA501: - case CONTEC: - case NEC: - case KME: - if (cardtype == MBH10304) { - card_name = "FMV-J182"; - - len = pcmcia_get_tuple(link, CISTPL_FUNCE, &buf); - if (len < 11) { - kfree(buf); - goto failed; - } - /* Read MACID from CIS */ - eth_hw_addr_set(dev, &buf[5]); - kfree(buf); - } else { - if (pcmcia_get_mac_from_cis(link, dev)) - goto failed; - if( cardtype == TDK ) { - card_name = "TDK LAK-CD021"; - } else if( cardtype == LA501 ) { - card_name = "LA501"; - } else if( cardtype == NEC ) { - card_name = "PK-UG-J001"; - } else if( cardtype == KME ) { - card_name = "Panasonic"; - } else { - card_name = "C-NET(PC)C"; - } - } - break; - case UNGERMANN: - /* Read MACID from register */ - for (i = 0; i < 6; i++) - addr[i] = inb(ioaddr + UNGERMANN_MAC_ID + i); - eth_hw_addr_set(dev, addr); - card_name = "Access/CARD"; - break; - case XXX10304: - /* Read MACID from Buggy CIS */ - if (fmvj18x_get_hwinfo(link, buggybuf) == -1) { - pr_notice("unable to read hardware net address\n"); - goto failed; - } - eth_hw_addr_set(dev, buggybuf); - card_name = "FMV-J182"; - break; - case MBH10302: - default: - /* Read MACID from register */ - for (i = 0; i < 6; i++) - addr[i] = inb(ioaddr + MAC_ID + i); - eth_hw_addr_set(dev, addr); - card_name = "FMV-J181"; - break; - } - - lp->cardtype = cardtype; - SET_NETDEV_DEV(dev, &link->dev); - - if (register_netdev(dev) != 0) { - pr_notice("register_netdev() failed\n"); - goto failed; - } - - /* print current configuration */ - netdev_info(dev, "%s, sram %s, port %#3lx, irq %d, hw_addr %pM\n", - card_name, sram_config == 0 ? "4K TX*2" : "8K TX*2", - dev->base_addr, dev->irq, dev->dev_addr); - - return 0; - -failed: - fmvj18x_release(link); - return -ENODEV; -} /* fmvj18x_config */ -/*====================================================================*/ - -static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id) -{ - u_char __iomem *base; - int i, j; - - /* Allocate a small memory window */ - link->resource[2]->flags |= WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; - link->resource[2]->start = 0; link->resource[2]->end = 0; - i = pcmcia_request_window(link, link->resource[2], 0); - if (i != 0) - return -1; - - base = ioremap(link->resource[2]->start, resource_size(link->resource[2])); - if (!base) { - pcmcia_release_window(link, link->resource[2]); - return -1; - } - - pcmcia_map_mem_page(link, link->resource[2], 0); - - /* - * MBH10304 CISTPL_FUNCE_LAN_NODE_ID format - * 22 0d xx xx xx 04 06 yy yy yy yy yy yy ff - * 'xx' is garbage. - * 'yy' is MAC address. - */ - for (i = 0; i < 0x200; i++) { - if (readb(base+i*2) == 0x22) { - if (readb(base+(i-1)*2) == 0xff && - readb(base+(i+5)*2) == 0x04 && - readb(base+(i+6)*2) == 0x06 && - readb(base+(i+13)*2) == 0xff) - break; - } - } - - if (i != 0x200) { - for (j = 0 ; j < 6; j++,i++) { - node_id[j] = readb(base+(i+7)*2); - } - } - - iounmap(base); - j = pcmcia_release_window(link, link->resource[2]); - return (i != 0x200) ? 0 : -1; - -} /* fmvj18x_get_hwinfo */ -/*====================================================================*/ - -static int fmvj18x_setup_mfc(struct pcmcia_device *link) -{ - int i; - struct net_device *dev = link->priv; - unsigned int ioaddr; - struct local_info *lp = netdev_priv(dev); - - /* Allocate a small memory window */ - link->resource[3]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; - link->resource[3]->start = link->resource[3]->end = 0; - i = pcmcia_request_window(link, link->resource[3], 0); - if (i != 0) - return -1; - - lp->base = ioremap(link->resource[3]->start, - resource_size(link->resource[3])); - if (lp->base == NULL) { - netdev_notice(dev, "ioremap failed\n"); - return -1; - } - - i = pcmcia_map_mem_page(link, link->resource[3], 0); - if (i != 0) { - iounmap(lp->base); - lp->base = NULL; - return -1; - } - - ioaddr = dev->base_addr; - writeb(0x47, lp->base+0x800); /* Config Option Register of LAN */ - writeb(0x0, lp->base+0x802); /* Config and Status Register */ - - writeb(ioaddr & 0xff, lp->base+0x80a); /* I/O Base(Low) of LAN */ - writeb((ioaddr >> 8) & 0xff, lp->base+0x80c); /* I/O Base(High) of LAN */ - - writeb(0x45, lp->base+0x820); /* Config Option Register of Modem */ - writeb(0x8, lp->base+0x822); /* Config and Status Register */ - - return 0; - -} -/*====================================================================*/ - -static void fmvj18x_release(struct pcmcia_device *link) -{ - - struct net_device *dev = link->priv; - struct local_info *lp = netdev_priv(dev); - u_char __iomem *tmp; - - dev_dbg(&link->dev, "fmvj18x_release\n"); - - if (lp->base != NULL) { - tmp = lp->base; - lp->base = NULL; /* set NULL before iounmap */ - iounmap(tmp); - } - - pcmcia_disable_device(link); - -} - -static int fmvj18x_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int fmvj18x_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) { - fjn_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - -/*====================================================================*/ - -static const struct pcmcia_device_id fmvj18x_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x0004, 0x0004), - PCMCIA_DEVICE_PROD_ID12("EAGLE Technology", "NE200 ETHERNET LAN MBH10302 04", 0x528c88c4, 0x74f91e59), - PCMCIA_DEVICE_PROD_ID12("Eiger Labs,Inc", "EPX-10BT PC Card Ethernet 10BT", 0x53af556e, 0x877f9922), - PCMCIA_DEVICE_PROD_ID12("Eiger labs,Inc.", "EPX-10BT PC Card Ethernet 10BT", 0xf47e6c66, 0x877f9922), - PCMCIA_DEVICE_PROD_ID12("FUJITSU", "LAN Card(FMV-J182)", 0x6ee5a3d8, 0x5baf31db), - PCMCIA_DEVICE_PROD_ID12("FUJITSU", "MBH10308", 0x6ee5a3d8, 0x3f04875e), - PCMCIA_DEVICE_PROD_ID12("FUJITSU TOWA", "LA501", 0xb8451188, 0x12939ba2), - PCMCIA_DEVICE_PROD_ID12("HITACHI", "HT-4840-11", 0xf4f43949, 0x773910f4), - PCMCIA_DEVICE_PROD_ID12("NextComK.K.", "NC5310B Ver1.0 ", 0x8cef4d3a, 0x075fc7b6), - PCMCIA_DEVICE_PROD_ID12("NextComK.K.", "NC5310 Ver1.0 ", 0x8cef4d3a, 0xbccf43e6), - PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "10BASE_T CARD R280", 0x85c10e17, 0xd9413666), - PCMCIA_DEVICE_PROD_ID12("TDK", "LAC-CD02x", 0x1eae9475, 0x8fa0ee70), - PCMCIA_DEVICE_PROD_ID12("TDK", "LAC-CF010", 0x1eae9475, 0x7683bc9a), - PCMCIA_DEVICE_PROD_ID1("CONTEC Co.,Ltd.", 0x58d8fee2), - PCMCIA_DEVICE_PROD_ID1("PCMCIA LAN MBH10304 ES", 0x2599f454), - PCMCIA_DEVICE_PROD_ID1("PCMCIA MBH10302", 0x8f4005da), - PCMCIA_DEVICE_PROD_ID1("UBKK,V2.0", 0x90888080), - PCMCIA_PFC_DEVICE_PROD_ID12(0, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed), - PCMCIA_PFC_DEVICE_PROD_ID12(0, "NEC", "PK-UG-J001" ,0x18df0ba0 ,0x831b1064), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0d0a), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0e0a), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0e01), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0a05), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0b05), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x1101), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, fmvj18x_ids); - -static struct pcmcia_driver fmvj18x_cs_driver = { - .owner = THIS_MODULE, - .name = "fmvj18x_cs", - .probe = fmvj18x_probe, - .remove = fmvj18x_detach, - .id_table = fmvj18x_ids, - .suspend = fmvj18x_suspend, - .resume = fmvj18x_resume, -}; -module_pcmcia_driver(fmvj18x_cs_driver); - -/*====================================================================*/ - -static irqreturn_t fjn_interrupt(int dummy, void *dev_id) -{ - struct net_device *dev = dev_id; - struct local_info *lp = netdev_priv(dev); - unsigned int ioaddr; - unsigned short tx_stat, rx_stat; - - ioaddr = dev->base_addr; - - /* avoid multiple interrupts */ - outw(0x0000, ioaddr + TX_INTR); - - /* wait for a while */ - udelay(1); - - /* get status */ - tx_stat = inb(ioaddr + TX_STATUS); - rx_stat = inb(ioaddr + RX_STATUS); - - /* clear status */ - outb(tx_stat, ioaddr + TX_STATUS); - outb(rx_stat, ioaddr + RX_STATUS); - - pr_debug("%s: interrupt, rx_status %02x.\n", dev->name, rx_stat); - pr_debug(" tx_status %02x.\n", tx_stat); - - if (rx_stat || (inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) { - /* there is packet(s) in rx buffer */ - fjn_rx(dev); - } - if (tx_stat & F_TMT_RDY) { - dev->stats.tx_packets += lp->sent ; - lp->sent = 0 ; - if (lp->tx_queue) { - outb(DO_TX | lp->tx_queue, ioaddr + TX_START); - lp->sent = lp->tx_queue ; - lp->tx_queue = 0; - lp->tx_queue_len = 0; - netif_trans_update(dev); - } else { - lp->tx_started = 0; - } - netif_wake_queue(dev); - } - pr_debug("%s: exiting interrupt,\n", dev->name); - pr_debug(" tx_status %02x, rx_status %02x.\n", tx_stat, rx_stat); - - outb(D_TX_INTR, ioaddr + TX_INTR); - outb(D_RX_INTR, ioaddr + RX_INTR); - - if (lp->base != NULL) { - /* Ack interrupt for multifunction card */ - writeb(0x01, lp->base+0x802); - writeb(0x09, lp->base+0x822); - } - - return IRQ_HANDLED; - -} /* fjn_interrupt */ - -/*====================================================================*/ - -static void fjn_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - struct local_info *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - - netdev_notice(dev, "transmit timed out with status %04x, %s?\n", - htons(inw(ioaddr + TX_STATUS)), - inb(ioaddr + TX_STATUS) & F_TMT_RDY - ? "IRQ conflict" : "network cable problem"); - netdev_notice(dev, "timeout registers: %04x %04x %04x " - "%04x %04x %04x %04x %04x.\n", - htons(inw(ioaddr + 0)), htons(inw(ioaddr + 2)), - htons(inw(ioaddr + 4)), htons(inw(ioaddr + 6)), - htons(inw(ioaddr + 8)), htons(inw(ioaddr + 10)), - htons(inw(ioaddr + 12)), htons(inw(ioaddr + 14))); - dev->stats.tx_errors++; - /* ToDo: We should try to restart the adaptor... */ - local_irq_disable(); - fjn_reset(dev); - - lp->tx_started = 0; - lp->tx_queue = 0; - lp->tx_queue_len = 0; - lp->sent = 0; - lp->open_time = jiffies; - local_irq_enable(); - netif_wake_queue(dev); -} - -static netdev_tx_t fjn_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct local_info *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - short length = skb->len; - - if (length < ETH_ZLEN) - { - if (skb_padto(skb, ETH_ZLEN)) - return NETDEV_TX_OK; - length = ETH_ZLEN; - } - - netif_stop_queue(dev); - - { - unsigned char *buf = skb->data; - - if (length > ETH_FRAME_LEN) { - netdev_notice(dev, "Attempting to send a large packet (%d bytes)\n", - length); - return NETDEV_TX_BUSY; - } - - netdev_dbg(dev, "Transmitting a packet of length %lu\n", - (unsigned long)skb->len); - dev->stats.tx_bytes += skb->len; - - /* Disable both interrupts. */ - outw(0x0000, ioaddr + TX_INTR); - - /* wait for a while */ - udelay(1); - - outw(length, ioaddr + DATAPORT); - outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); - - lp->tx_queue++; - lp->tx_queue_len += ((length+3) & ~1); - - if (lp->tx_started == 0) { - /* If the Tx is idle, always trigger a transmit. */ - outb(DO_TX | lp->tx_queue, ioaddr + TX_START); - lp->sent = lp->tx_queue ; - lp->tx_queue = 0; - lp->tx_queue_len = 0; - lp->tx_started = 1; - netif_start_queue(dev); - } else { - if( sram_config == 0 ) { - if (lp->tx_queue_len < (4096 - (ETH_FRAME_LEN +2)) ) - /* Yes, there is room for one more packet. */ - netif_start_queue(dev); - } else { - if (lp->tx_queue_len < (8192 - (ETH_FRAME_LEN +2)) && - lp->tx_queue < 127 ) - /* Yes, there is room for one more packet. */ - netif_start_queue(dev); - } - } - - /* Re-enable interrupts */ - outb(D_TX_INTR, ioaddr + TX_INTR); - outb(D_RX_INTR, ioaddr + RX_INTR); - } - dev_kfree_skb (skb); - - return NETDEV_TX_OK; -} /* fjn_start_xmit */ - -/*====================================================================*/ - -static void fjn_reset(struct net_device *dev) -{ - struct local_info *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - int i; - - netdev_dbg(dev, "fjn_reset() called\n"); - - /* Reset controller */ - if( sram_config == 0 ) - outb(CONFIG0_RST, ioaddr + CONFIG_0); - else - outb(CONFIG0_RST_1, ioaddr + CONFIG_0); - - /* Power On chip and select bank 0 */ - if (lp->cardtype == MBH10302) - outb(BANK_0, ioaddr + CONFIG_1); - else - outb(BANK_0U, ioaddr + CONFIG_1); - - /* Set Tx modes */ - outb(D_TX_MODE, ioaddr + TX_MODE); - /* set Rx modes */ - outb(ID_MATCHED, ioaddr + RX_MODE); - - /* Set hardware address */ - for (i = 0; i < 6; i++) - outb(dev->dev_addr[i], ioaddr + NODE_ID + i); - - /* (re)initialize the multicast table */ - set_rx_mode(dev); - - /* Switch to bank 2 (runtime mode) */ - if (lp->cardtype == MBH10302) - outb(BANK_2, ioaddr + CONFIG_1); - else - outb(BANK_2U, ioaddr + CONFIG_1); - - /* set 16col ctrl bits */ - if( lp->cardtype == TDK || lp->cardtype == CONTEC) - outb(TDK_AUTO_MODE, ioaddr + COL_CTRL); - else - outb(AUTO_MODE, ioaddr + COL_CTRL); - - /* clear Reserved Regs */ - outb(0x00, ioaddr + BMPR12); - outb(0x00, ioaddr + BMPR13); - - /* reset Skip packet reg. */ - outb(0x01, ioaddr + RX_SKIP); - - /* Enable Tx and Rx */ - if( sram_config == 0 ) - outb(CONFIG0_DFL, ioaddr + CONFIG_0); - else - outb(CONFIG0_DFL_1, ioaddr + CONFIG_0); - - /* Init receive pointer ? */ - inw(ioaddr + DATAPORT); - inw(ioaddr + DATAPORT); - - /* Clear all status */ - outb(0xff, ioaddr + TX_STATUS); - outb(0xff, ioaddr + RX_STATUS); - - if (lp->cardtype == MBH10302) - outb(INTR_OFF, ioaddr + LAN_CTRL); - - /* Turn on Rx interrupts */ - outb(D_TX_INTR, ioaddr + TX_INTR); - outb(D_RX_INTR, ioaddr + RX_INTR); - - /* Turn on interrupts from LAN card controller */ - if (lp->cardtype == MBH10302) - outb(INTR_ON, ioaddr + LAN_CTRL); -} /* fjn_reset */ - -/*====================================================================*/ - -static void fjn_rx(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - int boguscount = 10; /* 5 -> 10: by agy 19940922 */ - - pr_debug("%s: in rx_packet(), rx_status %02x.\n", - dev->name, inb(ioaddr + RX_STATUS)); - - while ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) { - u_short status = inw(ioaddr + DATAPORT); - - netdev_dbg(dev, "Rxing packet mode %02x status %04x.\n", - inb(ioaddr + RX_MODE), status); -#ifndef final_version - if (status == 0) { - outb(F_SKP_PKT, ioaddr + RX_SKIP); - break; - } -#endif - if ((status & 0xF0) != 0x20) { /* There was an error. */ - dev->stats.rx_errors++; - if (status & F_LEN_ERR) dev->stats.rx_length_errors++; - if (status & F_ALG_ERR) dev->stats.rx_frame_errors++; - if (status & F_CRC_ERR) dev->stats.rx_crc_errors++; - if (status & F_OVR_FLO) dev->stats.rx_over_errors++; - } else { - u_short pkt_len = inw(ioaddr + DATAPORT); - /* Malloc up new buffer. */ - struct sk_buff *skb; - - if (pkt_len > 1550) { - netdev_notice(dev, "The FMV-18x claimed a very large packet, size %d\n", - pkt_len); - outb(F_SKP_PKT, ioaddr + RX_SKIP); - dev->stats.rx_errors++; - break; - } - skb = netdev_alloc_skb(dev, pkt_len + 2); - if (skb == NULL) { - outb(F_SKP_PKT, ioaddr + RX_SKIP); - dev->stats.rx_dropped++; - break; - } - - skb_reserve(skb, 2); - insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), - (pkt_len + 1) >> 1); - skb->protocol = eth_type_trans(skb, dev); - - { - int i; - pr_debug("%s: Rxed packet of length %d: ", - dev->name, pkt_len); - for (i = 0; i < 14; i++) - pr_debug(" %02x", skb->data[i]); - pr_debug(".\n"); - } - - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } - if (--boguscount <= 0) - break; - } - - /* If any worth-while packets have been received, dev_rint() - has done a netif_wake_queue() for us and will work on them - when we get to the bottom-half routine. */ -/* - if (lp->cardtype != TDK) { - int i; - for (i = 0; i < 20; i++) { - if ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == F_BUF_EMP) - break; - (void)inw(ioaddr + DATAPORT); /+ dummy status read +/ - outb(F_SKP_PKT, ioaddr + RX_SKIP); - } - - if (i > 0) - pr_debug("%s: Exint Rx packet with mode %02x after " - "%d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i); - } -*/ -} /* fjn_rx */ - -/*====================================================================*/ - -static void netdev_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - strscpy(info->version, DRV_VERSION, sizeof(info->version)); - snprintf(info->bus_info, sizeof(info->bus_info), - "PCMCIA 0x%lx", dev->base_addr); -} - -static const struct ethtool_ops netdev_ethtool_ops = { - .get_drvinfo = netdev_get_drvinfo, -}; - -static int fjn_config(struct net_device *dev, struct ifmap *map){ - return 0; -} - -static int fjn_open(struct net_device *dev) -{ - struct local_info *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - pr_debug("fjn_open('%s').\n", dev->name); - - if (!pcmcia_dev_present(link)) - return -ENODEV; - - link->open++; - - fjn_reset(dev); - - lp->tx_started = 0; - lp->tx_queue = 0; - lp->tx_queue_len = 0; - lp->open_time = jiffies; - netif_start_queue(dev); - - return 0; -} /* fjn_open */ - -/*====================================================================*/ - -static int fjn_close(struct net_device *dev) -{ - struct local_info *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - unsigned int ioaddr = dev->base_addr; - - pr_debug("fjn_close('%s').\n", dev->name); - - lp->open_time = 0; - netif_stop_queue(dev); - - /* Set configuration register 0 to disable Tx and Rx. */ - if( sram_config == 0 ) - outb(CONFIG0_RST ,ioaddr + CONFIG_0); - else - outb(CONFIG0_RST_1 ,ioaddr + CONFIG_0); - - /* Update the statistics -- ToDo. */ - - /* Power-down the chip. Green, green, green! */ - outb(CHIP_OFF ,ioaddr + CONFIG_1); - - /* Set the ethernet adaptor disable IRQ */ - if (lp->cardtype == MBH10302) - outb(INTR_OFF, ioaddr + LAN_CTRL); - - link->open--; - - return 0; -} /* fjn_close */ - -/*====================================================================*/ - -/* - Set the multicast/promiscuous mode for this adaptor. -*/ - -static void set_rx_mode(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - u_char mc_filter[8]; /* Multicast hash filter */ - u_long flags; - int i; - - int saved_bank; - int saved_config_0 = inb(ioaddr + CONFIG_0); - - local_irq_save(flags); - - /* Disable Tx and Rx */ - if (sram_config == 0) - outb(CONFIG0_RST, ioaddr + CONFIG_0); - else - outb(CONFIG0_RST_1, ioaddr + CONFIG_0); - - if (dev->flags & IFF_PROMISC) { - memset(mc_filter, 0xff, sizeof(mc_filter)); - outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */ - } else if (netdev_mc_count(dev) > MC_FILTERBREAK || - (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter perfectly -- accept all multicasts. */ - memset(mc_filter, 0xff, sizeof(mc_filter)); - outb(2, ioaddr + RX_MODE); /* Use normal mode. */ - } else if (netdev_mc_empty(dev)) { - memset(mc_filter, 0x00, sizeof(mc_filter)); - outb(1, ioaddr + RX_MODE); /* Ignore almost all multicasts. */ - } else { - struct netdev_hw_addr *ha; - - memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(ha, dev) { - unsigned int bit = ether_crc_le(ETH_ALEN, ha->addr) >> 26; - mc_filter[bit >> 3] |= (1 << (bit & 7)); - } - outb(2, ioaddr + RX_MODE); /* Use normal mode. */ - } - - /* Switch to bank 1 and set the multicast table. */ - saved_bank = inb(ioaddr + CONFIG_1); - outb(0xe4, ioaddr + CONFIG_1); - - for (i = 0; i < 8; i++) - outb(mc_filter[i], ioaddr + MAR_ADR + i); - outb(saved_bank, ioaddr + CONFIG_1); - - outb(saved_config_0, ioaddr + CONFIG_0); - - local_irq_restore(flags); -} diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 58cc3147afe2..73e051d26b9d 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1756,6 +1756,27 @@ static int ibmveth_set_mac_addr(struct net_device *dev, void *p) return 0; } +static netdev_features_t ibmveth_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + /* Some physical adapters do not support segmentation offload with + * MSS < 224. Disable GSO for such packets to avoid adapter freeze. + * Note: Single-segment packets (gso_segs == 1) don't need this check + * as they bypass the LSO path and are transmitted without segmentation. + */ + if (skb_is_gso(skb)) { + if (skb_shinfo(skb)->gso_size < IBMVETH_MIN_LSO_MSS) { + netdev_warn_once(dev, + "MSS %u too small for LSO, disabling GSO\n", + skb_shinfo(skb)->gso_size); + features &= ~NETIF_F_GSO_MASK; + } + } + + return vlan_features_check(skb, features); +} + static const struct net_device_ops ibmveth_netdev_ops = { .ndo_open = ibmveth_open, .ndo_stop = ibmveth_close, @@ -1767,6 +1788,7 @@ static const struct net_device_ops ibmveth_netdev_ops = { .ndo_set_features = ibmveth_set_features, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = ibmveth_set_mac_addr, + .ndo_features_check = ibmveth_features_check, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ibmveth_poll_controller, #endif diff --git a/drivers/net/ethernet/ibm/ibmveth.h b/drivers/net/ethernet/ibm/ibmveth.h index 068f99df133e..d87713668ed3 100644 --- a/drivers/net/ethernet/ibm/ibmveth.h +++ b/drivers/net/ethernet/ibm/ibmveth.h @@ -37,6 +37,7 @@ #define IBMVETH_ILLAN_IPV4_TCP_CSUM 0x0000000000000002UL #define IBMVETH_ILLAN_ACTIVE_TRUNK 0x0000000000000001UL +#define IBMVETH_MIN_LSO_MSS 224 /* Minimum MSS for LSO */ /* hcall macros */ #define h_register_logical_lan(ua, buflst, rxq, fltlst, mac) \ plpar_hcall_norets(H_REGISTER_LOGICAL_LAN, ua, buflst, rxq, fltlst, mac) diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 9befdacd6730..7ce0cc8ab8f4 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -7706,6 +7706,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err_register: if (!(adapter->flags & FLAG_HAS_AMT)) e1000e_release_hw_control(adapter); + e1000e_ptp_remove(adapter); err_eeprom: if (hw->phy.ops.check_reset_block && !hw->phy.ops.check_reset_block(hw)) e1000_phy_hw_reset(&adapter->hw); diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index dcb50c2e1aa2..83e780919ac9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -1318,6 +1318,7 @@ void i40e_ptp_restore_hw_time(struct i40e_pf *pf); void i40e_ptp_init(struct i40e_pf *pf); void i40e_ptp_stop(struct i40e_pf *pf); int i40e_ptp_alloc_pins(struct i40e_pf *pf); +void i40e_ptp_free_pins(struct i40e_pf *pf); int i40e_update_adq_vsi_queues(struct i40e_vsi *vsi, int vsi_offset); int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi); int i40e_get_partition_bw_setting(struct i40e_pf *pf); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 926d001b2150..6d4f9218dc68 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -13783,7 +13783,6 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) netdev->neigh_priv_len = sizeof(u32) * 4; netdev->priv_flags |= IFF_UNICAST_FLT; - netdev->priv_flags |= IFF_SUPP_NOFCS; /* Setup netdev TC information */ i40e_vsi_config_netdev_tc(vsi, vsi->tc_config.enabled_tc); @@ -16109,9 +16108,11 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Unwind what we've done if something failed in the setup */ err_vsis: set_bit(__I40E_DOWN, pf->state); + i40e_ptp_stop(pf); i40e_clear_interrupt_scheme(pf); kfree(pf->vsi); err_switch_setup: + i40e_ptp_free_pins(pf); i40e_reset_interrupt_capability(pf); timer_shutdown_sync(&pf->service_timer); err_mac_addr: diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 404a716db8da..7d07c389bb23 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -940,12 +940,13 @@ int i40e_ptp_hwtstamp_get(struct net_device *netdev, * * Release memory allocated for PTP pins. **/ -static void i40e_ptp_free_pins(struct i40e_pf *pf) +void i40e_ptp_free_pins(struct i40e_pf *pf) { if (i40e_is_ptp_pin_dev(&pf->hw)) { kfree(pf->ptp_pins); kfree(pf->ptp_caps.pin_config); pf->ptp_pins = NULL; + pf->ptp_caps.pin_config = NULL; } } diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h index e9fb0a0919e3..050f8241ef5e 100644 --- a/drivers/net/ethernet/intel/iavf/iavf.h +++ b/drivers/net/ethernet/intel/iavf/iavf.h @@ -158,11 +158,10 @@ struct iavf_vlan { enum iavf_vlan_state_t { IAVF_VLAN_INVALID, IAVF_VLAN_ADD, /* filter needs to be added */ - IAVF_VLAN_IS_NEW, /* filter is new, wait for PF answer */ - IAVF_VLAN_ACTIVE, /* filter is accepted by PF */ - IAVF_VLAN_DISABLE, /* filter needs to be deleted by PF, then marked INACTIVE */ - IAVF_VLAN_INACTIVE, /* filter is inactive, we are in IFF_DOWN */ - IAVF_VLAN_REMOVE, /* filter needs to be removed from list */ + IAVF_VLAN_ADDING, /* ADD sent to PF, waiting for response */ + IAVF_VLAN_ACTIVE, /* PF confirmed, filter is in HW */ + IAVF_VLAN_REMOVE, /* filter queued for DEL from PF */ + IAVF_VLAN_REMOVING, /* DEL sent to PF, waiting for response */ }; struct iavf_vlan_filter { diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c index dad001abc908..d2914c511e1e 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_main.c +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c @@ -757,10 +757,10 @@ iavf_vlan_filter *iavf_add_vlan(struct iavf_adapter *adapter, adapter->num_vlan_filters++; iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_ADD_VLAN_FILTER); } else if (f->state == IAVF_VLAN_REMOVE) { - /* Re-add the filter since we cannot tell whether the - * pending delete has already been processed by the PF. - * A duplicate add is harmless. - */ + /* DEL not yet sent to PF, cancel it */ + f->state = IAVF_VLAN_ACTIVE; + } else if (f->state == IAVF_VLAN_REMOVING) { + /* DEL already sent to PF, re-add after completion */ f->state = IAVF_VLAN_ADD; iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_ADD_VLAN_FILTER); @@ -791,37 +791,19 @@ static void iavf_del_vlan(struct iavf_adapter *adapter, struct iavf_vlan vlan) list_del(&f->list); kfree(f); adapter->num_vlan_filters--; - } else { + } else if (f->state != IAVF_VLAN_REMOVING) { f->state = IAVF_VLAN_REMOVE; iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_DEL_VLAN_FILTER); } + /* If REMOVING, DEL is already sent to PF; completion + * handler will free the filter when PF confirms. + */ } spin_unlock_bh(&adapter->mac_vlan_list_lock); } -/** - * iavf_restore_filters - * @adapter: board private structure - * - * Restore existing non MAC filters when VF netdev comes back up - **/ -static void iavf_restore_filters(struct iavf_adapter *adapter) -{ - struct iavf_vlan_filter *f; - - /* re-add all VLAN filters */ - spin_lock_bh(&adapter->mac_vlan_list_lock); - - list_for_each_entry(f, &adapter->vlan_filter_list, list) { - if (f->state == IAVF_VLAN_INACTIVE) - f->state = IAVF_VLAN_ADD; - } - - spin_unlock_bh(&adapter->mac_vlan_list_lock); - adapter->aq_required |= IAVF_FLAG_AQ_ADD_VLAN_FILTER; -} /** * iavf_get_num_vlans_added - get number of VLANs added @@ -1150,14 +1132,18 @@ bool iavf_promiscuous_mode_changed(struct iavf_adapter *adapter) /** * iavf_set_rx_mode - NDO callback to set the netdev filters * @netdev: network interface device structure + * @uc: snapshot of uc address list + * @mc: snapshot of mc address list **/ -static void iavf_set_rx_mode(struct net_device *netdev) +static void iavf_set_rx_mode(struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct iavf_adapter *adapter = netdev_priv(netdev); spin_lock_bh(&adapter->mac_vlan_list_lock); - __dev_uc_sync(netdev, iavf_addr_sync, iavf_addr_unsync); - __dev_mc_sync(netdev, iavf_addr_sync, iavf_addr_unsync); + __hw_addr_sync_dev(uc, netdev, iavf_addr_sync, iavf_addr_unsync); + __hw_addr_sync_dev(mc, netdev, iavf_addr_sync, iavf_addr_unsync); spin_unlock_bh(&adapter->mac_vlan_list_lock); spin_lock_bh(&adapter->current_netdev_promisc_flags_lock); @@ -1210,7 +1196,9 @@ static void iavf_configure(struct iavf_adapter *adapter) struct net_device *netdev = adapter->netdev; int i; - iavf_set_rx_mode(netdev); + netif_addr_lock_bh(netdev); + iavf_set_rx_mode(netdev, &netdev->uc, &netdev->mc); + netif_addr_unlock_bh(netdev); iavf_configure_tx(adapter); iavf_configure_rx(adapter); @@ -1240,13 +1228,12 @@ static void iavf_up_complete(struct iavf_adapter *adapter) } /** - * iavf_clear_mac_vlan_filters - Remove mac and vlan filters not sent to PF - * yet and mark other to be removed. + * iavf_clear_mac_filters - Remove MAC filters not sent to PF yet and mark + * others to be removed. * @adapter: board private structure **/ -static void iavf_clear_mac_vlan_filters(struct iavf_adapter *adapter) +static void iavf_clear_mac_filters(struct iavf_adapter *adapter) { - struct iavf_vlan_filter *vlf, *vlftmp; struct iavf_mac_filter *f, *ftmp; spin_lock_bh(&adapter->mac_vlan_list_lock); @@ -1265,11 +1252,6 @@ static void iavf_clear_mac_vlan_filters(struct iavf_adapter *adapter) } } - /* disable all VLAN filters */ - list_for_each_entry_safe(vlf, vlftmp, &adapter->vlan_filter_list, - list) - vlf->state = IAVF_VLAN_DISABLE; - spin_unlock_bh(&adapter->mac_vlan_list_lock); } @@ -1365,7 +1347,7 @@ void iavf_down(struct iavf_adapter *adapter) iavf_napi_disable_all(adapter); iavf_irq_disable(adapter); - iavf_clear_mac_vlan_filters(adapter); + iavf_clear_mac_filters(adapter); iavf_clear_cloud_filters(adapter); iavf_clear_fdir_filters(adapter); iavf_clear_adv_rss_conf(adapter); @@ -1382,8 +1364,6 @@ void iavf_down(struct iavf_adapter *adapter) */ if (!list_empty(&adapter->mac_filter_list)) adapter->aq_required |= IAVF_FLAG_AQ_DEL_MAC_FILTER; - if (!list_empty(&adapter->vlan_filter_list)) - adapter->aq_required |= IAVF_FLAG_AQ_DEL_VLAN_FILTER; if (!list_empty(&adapter->cloud_filter_list)) adapter->aq_required |= IAVF_FLAG_AQ_DEL_CLOUD_FILTER; if (!list_empty(&adapter->fdir_list_head)) @@ -4488,8 +4468,6 @@ static int iavf_open(struct net_device *netdev) iavf_add_filter(adapter, adapter->hw.mac.addr); spin_unlock_bh(&adapter->mac_vlan_list_lock); - /* Restore filters that were removed with IFF_DOWN */ - iavf_restore_filters(adapter); iavf_restore_fdir_filters(adapter); iavf_configure(adapter); @@ -5153,7 +5131,7 @@ static const struct net_device_ops iavf_netdev_ops = { .ndo_open = iavf_open, .ndo_stop = iavf_close, .ndo_start_xmit = iavf_xmit_frame, - .ndo_set_rx_mode = iavf_set_rx_mode, + .ndo_set_rx_mode_async = iavf_set_rx_mode, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = iavf_set_mac, .ndo_change_mtu = iavf_change_mtu, diff --git a/drivers/net/ethernet/intel/iavf/iavf_type.h b/drivers/net/ethernet/intel/iavf/iavf_type.h index 1d8cf29cb65a..5bb1de1cfd33 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_type.h +++ b/drivers/net/ethernet/intel/iavf/iavf_type.h @@ -277,7 +277,7 @@ struct iavf_rx_desc { /* L2 Tag 2 Presence */ #define IAVF_RXD_LEGACY_L2TAG2P_M BIT(0) /* Stripped S-TAG VLAN from the receive packet */ -#define IAVF_RXD_LEGACY_L2TAG2_M GENMASK_ULL(63, 32) +#define IAVF_RXD_LEGACY_L2TAG2_M GENMASK_ULL(63, 48) /* Stripped S-TAG VLAN from the receive packet */ #define IAVF_RXD_FLEX_L2TAG2_2_M GENMASK_ULL(63, 48) /* The packet is a UDP tunneled packet */ diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c index a52c100dcbc5..4f2defd2331b 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c +++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c @@ -746,7 +746,7 @@ static void iavf_vlan_add_reject(struct iavf_adapter *adapter) spin_lock_bh(&adapter->mac_vlan_list_lock); list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) { - if (f->state == IAVF_VLAN_IS_NEW) { + if (f->state == IAVF_VLAN_ADDING) { list_del(&f->list); kfree(f); adapter->num_vlan_filters--; @@ -812,7 +812,7 @@ void iavf_add_vlans(struct iavf_adapter *adapter) if (f->state == IAVF_VLAN_ADD) { vvfl->vlan_id[i] = f->vlan.vid; i++; - f->state = IAVF_VLAN_IS_NEW; + f->state = IAVF_VLAN_ADDING; if (i == count) break; } @@ -874,7 +874,7 @@ void iavf_add_vlans(struct iavf_adapter *adapter) vlan->tpid = f->vlan.tpid; i++; - f->state = IAVF_VLAN_IS_NEW; + f->state = IAVF_VLAN_ADDING; } } @@ -911,22 +911,12 @@ void iavf_del_vlans(struct iavf_adapter *adapter) spin_lock_bh(&adapter->mac_vlan_list_lock); list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) { - /* since VLAN capabilities are not allowed, we dont want to send - * a VLAN delete request because it will most likely fail and - * create unnecessary errors/noise, so just free the VLAN - * filters marked for removal to enable bailing out before - * sending a virtchnl message - */ if (f->state == IAVF_VLAN_REMOVE && !VLAN_FILTERING_ALLOWED(adapter)) { list_del(&f->list); kfree(f); adapter->num_vlan_filters--; - } else if (f->state == IAVF_VLAN_DISABLE && - !VLAN_FILTERING_ALLOWED(adapter)) { - f->state = IAVF_VLAN_INACTIVE; - } else if (f->state == IAVF_VLAN_REMOVE || - f->state == IAVF_VLAN_DISABLE) { + } else if (f->state == IAVF_VLAN_REMOVE) { count++; } } @@ -958,18 +948,10 @@ void iavf_del_vlans(struct iavf_adapter *adapter) vvfl->vsi_id = adapter->vsi_res->vsi_id; vvfl->num_elements = count; - list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) { - if (f->state == IAVF_VLAN_DISABLE) { + list_for_each_entry(f, &adapter->vlan_filter_list, list) { + if (f->state == IAVF_VLAN_REMOVE) { vvfl->vlan_id[i] = f->vlan.vid; - f->state = IAVF_VLAN_INACTIVE; - i++; - if (i == count) - break; - } else if (f->state == IAVF_VLAN_REMOVE) { - vvfl->vlan_id[i] = f->vlan.vid; - list_del(&f->list); - kfree(f); - adapter->num_vlan_filters--; + f->state = IAVF_VLAN_REMOVING; i++; if (i == count) break; @@ -1006,9 +988,8 @@ void iavf_del_vlans(struct iavf_adapter *adapter) vvfl_v2->vport_id = adapter->vsi_res->vsi_id; vvfl_v2->num_elements = count; - list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) { - if (f->state == IAVF_VLAN_DISABLE || - f->state == IAVF_VLAN_REMOVE) { + list_for_each_entry(f, &adapter->vlan_filter_list, list) { + if (f->state == IAVF_VLAN_REMOVE) { struct virtchnl_vlan_supported_caps *filtering_support = &adapter->vlan_v2_caps.filtering.filtering_support; struct virtchnl_vlan *vlan; @@ -1022,13 +1003,7 @@ void iavf_del_vlans(struct iavf_adapter *adapter) vlan->tci = f->vlan.vid; vlan->tpid = f->vlan.tpid; - if (f->state == IAVF_VLAN_DISABLE) { - f->state = IAVF_VLAN_INACTIVE; - } else { - list_del(&f->list); - kfree(f); - adapter->num_vlan_filters--; - } + f->state = IAVF_VLAN_REMOVING; i++; if (i == count) break; @@ -2391,10 +2366,6 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter, ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr); wake_up(&adapter->vc_waitqueue); break; - case VIRTCHNL_OP_DEL_VLAN: - dev_err(&adapter->pdev->dev, "Failed to delete VLAN filter, error %s\n", - iavf_stat_str(&adapter->hw, v_retval)); - break; case VIRTCHNL_OP_DEL_ETH_ADDR: dev_err(&adapter->pdev->dev, "Failed to delete MAC filter, error %s\n", iavf_stat_str(&adapter->hw, v_retval)); @@ -2905,17 +2876,42 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter, spin_unlock_bh(&adapter->adv_rss_lock); } break; + case VIRTCHNL_OP_ADD_VLAN: case VIRTCHNL_OP_ADD_VLAN_V2: { struct iavf_vlan_filter *f; + if (v_retval) + break; + spin_lock_bh(&adapter->mac_vlan_list_lock); list_for_each_entry(f, &adapter->vlan_filter_list, list) { - if (f->state == IAVF_VLAN_IS_NEW) + if (f->state == IAVF_VLAN_ADDING) f->state = IAVF_VLAN_ACTIVE; } spin_unlock_bh(&adapter->mac_vlan_list_lock); } break; + case VIRTCHNL_OP_DEL_VLAN: + case VIRTCHNL_OP_DEL_VLAN_V2: { + struct iavf_vlan_filter *f, *ftmp; + + spin_lock_bh(&adapter->mac_vlan_list_lock); + list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, + list) { + if (f->state == IAVF_VLAN_REMOVING) { + if (v_retval) { + /* PF rejected DEL, keep filter */ + f->state = IAVF_VLAN_ACTIVE; + } else { + list_del(&f->list); + kfree(f); + adapter->num_vlan_filters--; + } + } + } + spin_unlock_bh(&adapter->mac_vlan_list_lock); + } + break; case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING: /* PF enabled vlan strip on this VF. * Update netdev->features if needed to be in sync with ethtool. diff --git a/drivers/net/ethernet/intel/ice/devlink/devlink.c b/drivers/net/ethernet/intel/ice/devlink/devlink.c index 6144cee8034d..641d6e289d5c 100644 --- a/drivers/net/ethernet/intel/ice/devlink/devlink.c +++ b/drivers/net/ethernet/intel/ice/devlink/devlink.c @@ -1245,6 +1245,8 @@ static int ice_devlink_reinit_up(struct ice_pf *pf) return err; } + ice_init_dev_hw(pf); + /* load MSI-X values */ ice_set_min_max_msix(pf); diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index eb3a48330cc1..725b130dd3a2 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -753,7 +753,7 @@ static inline bool ice_is_xdp_ena_vsi(struct ice_vsi *vsi) static inline void ice_set_ring_xdp(struct ice_tx_ring *ring) { - ring->flags |= ICE_TX_FLAGS_RING_XDP; + set_bit(ICE_TX_RING_FLAGS_XDP, ring->flags); } /** @@ -778,7 +778,7 @@ static inline bool ice_is_txtime_ena(const struct ice_tx_ring *ring) */ static inline bool ice_is_txtime_cfg(const struct ice_tx_ring *ring) { - return !!(ring->flags & ICE_TX_FLAGS_TXTIME); + return test_bit(ICE_TX_RING_FLAGS_TXTIME, ring->flags); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h index 859e9c66f3e7..3cbb1b0582e3 100644 --- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h +++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h @@ -1252,7 +1252,7 @@ struct ice_aqc_get_link_status_data { #define ICE_AQ_LINK_PWR_QSFP_CLASS_3 2 #define ICE_AQ_LINK_PWR_QSFP_CLASS_4 3 __le16 link_speed; -#define ICE_AQ_LINK_SPEED_M 0x7FF +#define ICE_AQ_LINK_SPEED_M GENMASK(11, 0) #define ICE_AQ_LINK_SPEED_10MB BIT(0) #define ICE_AQ_LINK_SPEED_100MB BIT(1) #define ICE_AQ_LINK_SPEED_1000MB BIT(2) diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index ce11fea122d0..b617a6bff891 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -1126,8 +1126,6 @@ int ice_init_hw(struct ice_hw *hw) if (status) goto err_unroll_fltr_mgmt_struct; - ice_init_dev_hw(hw->back); - mutex_init(&hw->tnl_lock); ice_init_chk_recipe_reuse_support(hw); diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c index bd77f1c001ee..0bc6dd375687 100644 --- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c @@ -537,14 +537,14 @@ void ice_dcb_rebuild(struct ice_pf *pf) struct ice_dcbx_cfg *err_cfg; int ret; + mutex_lock(&pf->tc_mutex); + ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); if (ret) { dev_err(dev, "Query Port ETS failed\n"); goto dcb_error; } - mutex_lock(&pf->tc_mutex); - if (!pf->hw.port_info->qos_cfg.is_sw_lldp) ice_cfg_etsrec_defaults(pf->hw.port_info); @@ -943,7 +943,7 @@ ice_tx_prepare_vlan_flags_dcb(struct ice_tx_ring *tx_ring, /* if this is not already set it means a VLAN 0 + priority needs * to be offloaded */ - if (tx_ring->flags & ICE_TX_FLAGS_RING_VLAN_L2TAG2) + if (test_bit(ICE_TX_RING_FLAGS_VLAN_L2TAG2, tx_ring->flags)) first->tx_flags |= ICE_TX_FLAGS_HW_OUTER_SINGLE_VLAN; else first->tx_flags |= ICE_TX_FLAGS_HW_VLAN; diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 62f75701d652..892bc7c2e28b 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -1154,6 +1154,32 @@ ice_dpll_input_state_get(const struct dpll_pin *pin, void *pin_priv, extack, ICE_DPLL_PIN_TYPE_INPUT); } +/** + * ice_dpll_sw_pin_notify_peer - notify the paired SW pin after a state change + * @d: pointer to dplls struct + * @changed: the SW pin that was explicitly changed (already notified by dpll core) + * + * SMA and U.FL pins share physical signal paths in pairs (SMA1/U.FL1 and + * SMA2/U.FL2). When one pin's routing changes via the PCA9575 GPIO + * expander, the paired pin's state may also change. Send a change + * notification for the peer pin so userspace consumers monitoring the + * peer via dpll netlink learn about the update. + * + * Context: Called from dpll_pin_ops callbacks after pf->dplls.lock is + * released. Uses __dpll_pin_change_ntf() because dpll_lock is + * still held by the dpll netlink layer. + */ +static void ice_dpll_sw_pin_notify_peer(struct ice_dplls *d, + struct ice_dpll_pin *changed) +{ + struct ice_dpll_pin *peer; + + peer = (changed >= d->sma && changed < d->sma + ICE_DPLL_PIN_SW_NUM) ? + &d->ufl[changed->idx] : &d->sma[changed->idx]; + if (peer->pin) + __dpll_pin_change_ntf(peer->pin); +} + /** * ice_dpll_sma_direction_set - set direction of SMA pin * @p: pointer to a pin @@ -1171,6 +1197,8 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p, enum dpll_pin_direction direction, struct netlink_ext_ack *extack) { + struct ice_dplls *d = &p->pf->dplls; + struct ice_dpll_pin *peer; u8 data; int ret; @@ -1189,8 +1217,9 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p, case ICE_DPLL_PIN_SW_2_IDX: if (direction == DPLL_PIN_DIRECTION_INPUT) { data &= ~ICE_SMA2_DIR_EN; + data |= ICE_SMA2_UFL2_RX_DIS; } else { - data &= ~ICE_SMA2_TX_EN; + data &= ~(ICE_SMA2_TX_EN | ICE_SMA2_UFL2_RX_DIS); data |= ICE_SMA2_DIR_EN; } break; @@ -1202,6 +1231,34 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p, ret = ice_dpll_pin_state_update(p->pf, p, ICE_DPLL_PIN_TYPE_SOFTWARE, extack); + if (ret) + return ret; + + /* When a direction change activates the paired U.FL pin, enable + * its backing CGU pin so the pin reports as connected. Without + * this the U.FL routing is correct but the CGU pin stays disabled + * and userspace sees the pin as disconnected. Do not disable the + * backing pin when U.FL becomes inactive because the SMA pin may + * still be using it. + */ + peer = &d->ufl[p->idx]; + if (peer->active) { + struct ice_dpll_pin *target; + enum ice_dpll_pin_type type; + + if (peer->output) { + target = peer->output; + type = ICE_DPLL_PIN_TYPE_OUTPUT; + } else { + target = peer->input; + type = ICE_DPLL_PIN_TYPE_INPUT; + } + ret = ice_dpll_pin_enable(&p->pf->hw, target, + d->eec.dpll_idx, type, extack); + if (!ret) + ret = ice_dpll_pin_state_update(p->pf, target, + type, extack); + } return ret; } @@ -1253,6 +1310,14 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv, data &= ~ICE_SMA1_MASK; enable = true; } else if (state == DPLL_PIN_STATE_DISCONNECTED) { + /* Skip if U.FL1 is not active, setting TX_EN + * while DIR_EN is set would also deactivate + * the paired SMA1 output. + */ + if (data & (ICE_SMA1_DIR_EN | ICE_SMA1_TX_EN)) { + ret = 0; + goto unlock; + } data |= ICE_SMA1_TX_EN; enable = false; } else { @@ -1267,6 +1332,15 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv, data &= ~ICE_SMA2_UFL2_RX_DIS; enable = true; } else if (state == DPLL_PIN_STATE_DISCONNECTED) { + /* Skip if U.FL2 is not active, setting + * UFL2_RX_DIS could also disable the paired + * SMA2 input. + */ + if (!(data & ICE_SMA2_DIR_EN) || + (data & ICE_SMA2_UFL2_RX_DIS)) { + ret = 0; + goto unlock; + } data |= ICE_SMA2_UFL2_RX_DIS; enable = false; } else { @@ -1296,6 +1370,8 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv, unlock: mutex_unlock(&pf->dplls.lock); + if (!ret) + ice_dpll_sw_pin_notify_peer(&pf->dplls, p); return ret; } @@ -1414,6 +1490,8 @@ ice_dpll_sma_pin_state_set(const struct dpll_pin *pin, void *pin_priv, unlock: mutex_unlock(&pf->dplls.lock); + if (!ret) + ice_dpll_sw_pin_notify_peer(&pf->dplls, sma); return ret; } @@ -1609,6 +1687,8 @@ ice_dpll_pin_sma_direction_set(const struct dpll_pin *pin, void *pin_priv, mutex_lock(&pf->dplls.lock); ret = ice_dpll_sma_direction_set(p, direction, extack); mutex_unlock(&pf->dplls.lock); + if (!ret) + ice_dpll_sw_pin_notify_peer(&pf->dplls, p); return ret; } @@ -1915,7 +1995,10 @@ ice_dpll_phase_offset_get(const struct dpll_pin *pin, void *pin_priv, d->active_input == p->input->pin)) *phase_offset = d->phase_offset * ICE_DPLL_PHASE_OFFSET_FACTOR; else if (d->phase_offset_monitor_period) - *phase_offset = p->phase_offset * ICE_DPLL_PHASE_OFFSET_FACTOR; + *phase_offset = (p->input && + p->direction == DPLL_PIN_DIRECTION_INPUT ? + p->input->phase_offset : + p->phase_offset) * ICE_DPLL_PHASE_OFFSET_FACTOR; else *phase_offset = 0; mutex_unlock(&pf->dplls.lock); @@ -2440,6 +2523,8 @@ ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv, if (hw_idx < 0) goto unlock; hw_idx -= pf->dplls.base_rclk_idx; + if (hw_idx >= ICE_DPLL_RCLK_NUM_MAX) + goto unlock; if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) || (!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) { @@ -2503,6 +2588,9 @@ ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv, hw_idx = ice_dpll_pin_get_parent_idx(p, parent_pin); if (hw_idx < 0) goto unlock; + hw_idx -= pf->dplls.base_rclk_idx; + if (hw_idx >= ICE_DPLL_RCLK_NUM_MAX) + goto unlock; ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT, extack); @@ -2609,6 +2697,27 @@ static u64 ice_generate_clock_id(struct ice_pf *pf) return pci_get_dsn(pf->pdev); } +/** + * ice_dpll_pin_ntf - notify pin change including any SW pin wrappers + * @dplls: pointer to dplls struct + * @pin: the dpll_pin that changed + * + * Send a change notification for @pin and for any registered SMA/U.FL pin + * whose backing CGU input matches @pin. + */ +static void ice_dpll_pin_ntf(struct ice_dplls *dplls, struct dpll_pin *pin) +{ + dpll_pin_change_ntf(pin); + for (int i = 0; i < ICE_DPLL_PIN_SW_NUM; i++) { + if (dplls->sma[i].pin && dplls->sma[i].input && + dplls->sma[i].input->pin == pin) + dpll_pin_change_ntf(dplls->sma[i].pin); + if (dplls->ufl[i].pin && dplls->ufl[i].input && + dplls->ufl[i].input->pin == pin) + dpll_pin_change_ntf(dplls->ufl[i].pin); + } +} + /** * ice_dpll_notify_changes - notify dpll subsystem about changes * @d: pointer do dpll @@ -2617,6 +2726,7 @@ static u64 ice_generate_clock_id(struct ice_pf *pf) */ static void ice_dpll_notify_changes(struct ice_dpll *d) { + struct ice_dplls *dplls = &d->pf->dplls; bool pin_notified = false; if (d->prev_dpll_state != d->dpll_state) { @@ -2625,17 +2735,17 @@ static void ice_dpll_notify_changes(struct ice_dpll *d) } if (d->prev_input != d->active_input) { if (d->prev_input) - dpll_pin_change_ntf(d->prev_input); + ice_dpll_pin_ntf(dplls, d->prev_input); d->prev_input = d->active_input; if (d->active_input) { - dpll_pin_change_ntf(d->active_input); + ice_dpll_pin_ntf(dplls, d->active_input); pin_notified = true; } } if (d->prev_phase_offset != d->phase_offset) { d->prev_phase_offset = d->phase_offset; if (!pin_notified && d->active_input) - dpll_pin_change_ntf(d->active_input); + ice_dpll_pin_ntf(dplls, d->active_input); } } @@ -2664,6 +2774,7 @@ static bool ice_dpll_is_pps_phase_monitor(struct ice_pf *pf) /** * ice_dpll_pins_notify_mask - notify dpll subsystem about bulk pin changes + * @dplls: pointer to dplls struct * @pins: array of ice_dpll_pin pointers registered within dpll subsystem * @pin_num: number of pins * @phase_offset_ntf_mask: bitmask of pin indexes to notify @@ -2673,15 +2784,14 @@ static bool ice_dpll_is_pps_phase_monitor(struct ice_pf *pf) * * Context: Must be called while pf->dplls.lock is released. */ -static void ice_dpll_pins_notify_mask(struct ice_dpll_pin *pins, +static void ice_dpll_pins_notify_mask(struct ice_dplls *dplls, + struct ice_dpll_pin *pins, u8 pin_num, u32 phase_offset_ntf_mask) { - int i = 0; - - for (i = 0; i < pin_num; i++) - if (phase_offset_ntf_mask & (1 << i)) - dpll_pin_change_ntf(pins[i].pin); + for (int i = 0; i < pin_num; i++) + if (phase_offset_ntf_mask & BIT(i)) + ice_dpll_pin_ntf(dplls, pins[i].pin); } /** @@ -2857,7 +2967,7 @@ static void ice_dpll_periodic_work(struct kthread_work *work) ice_dpll_notify_changes(de); ice_dpll_notify_changes(dp); if (phase_offset_ntf) - ice_dpll_pins_notify_mask(d->inputs, d->num_inputs, + ice_dpll_pins_notify_mask(d, d->inputs, d->num_inputs, phase_offset_ntf); resched: @@ -4014,6 +4124,7 @@ static int ice_dpll_init_info_sw_pins(struct ice_pf *pf) struct ice_dpll_pin *pin; u32 phase_adj_max, caps; int i, ret; + u8 data; if (pf->hw.device_id == ICE_DEV_ID_E810C_QSFP) input_idx_offset = ICE_E810_RCLK_PINS_NUM; @@ -4073,6 +4184,22 @@ static int ice_dpll_init_info_sw_pins(struct ice_pf *pf) } ice_dpll_phase_range_set(&pin->prop.phase_range, phase_adj_max); } + + /* Initialize the SMA control register to a known-good default state. + * Without this write the PCA9575 GPIO expander retains its power-on + * default (all outputs high) which makes all SW pins appear inactive. + * Set SMA1 and SMA2 as active inputs, disable U.FL1 output and + * U.FL2 input. + */ + ret = ice_read_sma_ctrl(&pf->hw, &data); + if (ret) + return ret; + data &= ~ICE_ALL_SMA_MASK; + data |= ICE_SMA1_TX_EN | ICE_SMA2_TX_EN | ICE_SMA2_UFL2_RX_DIS; + ret = ice_write_sma_ctrl(&pf->hw, data); + if (ret) + return ret; + ret = ice_dpll_pin_state_update(pf, pin, ICE_DPLL_PIN_TYPE_SOFTWARE, NULL); if (ret) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h index ae42cdea0ee1..8678575359b9 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.h +++ b/drivers/net/ethernet/intel/ice/ice_dpll.h @@ -8,6 +8,22 @@ #define ICE_DPLL_RCLK_NUM_MAX 4 +#define ICE_CGU_R10 0x28 +#define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5) +#define ICE_CGU_R10_SYNCE_CLKODIV_M1 GENMASK(13, 9) +#define ICE_CGU_R10_SYNCE_CLKODIV_LOAD BIT(14) +#define ICE_CGU_R10_SYNCE_DCK_RST BIT(15) +#define ICE_CGU_R10_SYNCE_ETHCLKO_SEL GENMASK(18, 16) +#define ICE_CGU_R10_SYNCE_ETHDIV_M1 GENMASK(23, 19) +#define ICE_CGU_R10_SYNCE_ETHDIV_LOAD BIT(24) +#define ICE_CGU_R10_SYNCE_DCK2_RST BIT(25) +#define ICE_CGU_R10_SYNCE_S_REF_CLK GENMASK(31, 27) + +#define ICE_CGU_R11 0x2C +#define ICE_CGU_R11_SYNCE_S_BYP_CLK GENMASK(6, 1) + +#define ICE_CGU_BYPASS_MUX_OFFSET_E825C 3 + /** * enum ice_dpll_pin_sw - enumerate ice software pin indices: * @ICE_DPLL_PIN_SW_1_IDX: index of first SW pin @@ -157,19 +173,3 @@ static inline void ice_dpll_deinit(struct ice_pf *pf) { } #endif #endif - -#define ICE_CGU_R10 0x28 -#define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5) -#define ICE_CGU_R10_SYNCE_CLKODIV_M1 GENMASK(13, 9) -#define ICE_CGU_R10_SYNCE_CLKODIV_LOAD BIT(14) -#define ICE_CGU_R10_SYNCE_DCK_RST BIT(15) -#define ICE_CGU_R10_SYNCE_ETHCLKO_SEL GENMASK(18, 16) -#define ICE_CGU_R10_SYNCE_ETHDIV_M1 GENMASK(23, 19) -#define ICE_CGU_R10_SYNCE_ETHDIV_LOAD BIT(24) -#define ICE_CGU_R10_SYNCE_DCK2_RST BIT(25) -#define ICE_CGU_R10_SYNCE_S_REF_CLK GENMASK(31, 27) - -#define ICE_CGU_R11 0x2C -#define ICE_CGU_R11_SYNCE_S_BYP_CLK GENMASK(6, 1) - -#define ICE_CGU_BYPASS_MUX_OFFSET_E825C 3 diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index e6a20af6f63d..f28416a707d7 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -3290,6 +3290,7 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, tx_rings[i].desc = NULL; tx_rings[i].tx_buf = NULL; tx_rings[i].tstamp_ring = NULL; + clear_bit(ICE_TX_RING_FLAGS_TXTIME, tx_rings[i].flags); tx_rings[i].tx_tstamps = &pf->ptp.port.tx; err = ice_setup_tx_ring(&tx_rings[i]); if (err) { diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index 689c6025ea82..837b71b7b2b7 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -1412,9 +1412,9 @@ static int ice_vsi_alloc_rings(struct ice_vsi *vsi) ring->count = vsi->num_tx_desc; ring->txq_teid = ICE_INVAL_TEID; if (dvm_ena) - ring->flags |= ICE_TX_FLAGS_RING_VLAN_L2TAG2; + set_bit(ICE_TX_RING_FLAGS_VLAN_L2TAG2, ring->flags); else - ring->flags |= ICE_TX_FLAGS_RING_VLAN_L2TAG1; + set_bit(ICE_TX_RING_FLAGS_VLAN_L2TAG1, ring->flags); WRITE_ONCE(vsi->tx_rings[i], ring); } diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index f93ee3b62391..c52c465280f7 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -1922,82 +1922,6 @@ static void ice_handle_mdd_event(struct ice_pf *pf) ice_print_vfs_mdd_events(pf); } -/** - * ice_force_phys_link_state - Force the physical link state - * @vsi: VSI to force the physical link state to up/down - * @link_up: true/false indicates to set the physical link to up/down - * - * Force the physical link state by getting the current PHY capabilities from - * hardware and setting the PHY config based on the determined capabilities. If - * link changes a link event will be triggered because both the Enable Automatic - * Link Update and LESM Enable bits are set when setting the PHY capabilities. - * - * Returns 0 on success, negative on failure - */ -static int ice_force_phys_link_state(struct ice_vsi *vsi, bool link_up) -{ - struct ice_aqc_get_phy_caps_data *pcaps; - struct ice_aqc_set_phy_cfg_data *cfg; - struct ice_port_info *pi; - struct device *dev; - int retcode; - - if (!vsi || !vsi->port_info || !vsi->back) - return -EINVAL; - if (vsi->type != ICE_VSI_PF) - return 0; - - dev = ice_pf_to_dev(vsi->back); - - pi = vsi->port_info; - - pcaps = kzalloc_obj(*pcaps); - if (!pcaps) - return -ENOMEM; - - retcode = ice_aq_get_phy_caps(pi, false, ICE_AQC_REPORT_ACTIVE_CFG, pcaps, - NULL); - if (retcode) { - dev_err(dev, "Failed to get phy capabilities, VSI %d error %d\n", - vsi->vsi_num, retcode); - retcode = -EIO; - goto out; - } - - /* No change in link */ - if (link_up == !!(pcaps->caps & ICE_AQC_PHY_EN_LINK) && - link_up == !!(pi->phy.link_info.link_info & ICE_AQ_LINK_UP)) - goto out; - - /* Use the current user PHY configuration. The current user PHY - * configuration is initialized during probe from PHY capabilities - * software mode, and updated on set PHY configuration. - */ - cfg = kmemdup(&pi->phy.curr_user_phy_cfg, sizeof(*cfg), GFP_KERNEL); - if (!cfg) { - retcode = -ENOMEM; - goto out; - } - - cfg->caps |= ICE_AQ_PHY_ENA_AUTO_LINK_UPDT; - if (link_up) - cfg->caps |= ICE_AQ_PHY_ENA_LINK; - else - cfg->caps &= ~ICE_AQ_PHY_ENA_LINK; - - retcode = ice_aq_set_phy_cfg(&vsi->back->hw, pi, cfg, NULL); - if (retcode) { - dev_err(dev, "Failed to set phy config, VSI %d error %d\n", - vsi->vsi_num, retcode); - retcode = -EIO; - } - - kfree(cfg); -out: - kfree(pcaps); - return retcode; -} - /** * ice_init_nvm_phy_type - Initialize the NVM PHY type * @pi: port info structure @@ -2066,7 +1990,7 @@ static void ice_init_link_dflt_override(struct ice_port_info *pi) * first time media is available. The ICE_LINK_DEFAULT_OVERRIDE_PENDING state * is used to indicate that the user PHY cfg default override is initialized * and the PHY has not been configured with the default override settings. The - * state is set here, and cleared in ice_configure_phy the first time the PHY is + * state is set here, and cleared in ice_phy_cfg the first time the PHY is * configured. * * This function should be called only if the FW doesn't support default @@ -2172,14 +2096,18 @@ err_out: } /** - * ice_configure_phy - configure PHY + * ice_phy_cfg - configure PHY * @vsi: VSI of PHY + * @link_en: true/false indicates to set link to enable/disable * * Set the PHY configuration. If the current PHY configuration is the same as - * the curr_user_phy_cfg, then do nothing to avoid link flap. Otherwise - * configure the based get PHY capabilities for topology with media. + * the curr_user_phy_cfg and link_en hasn't changed, then do nothing to avoid + * link flap. Otherwise configure the PHY based get PHY capabilities for + * topology with media and link_en. + * + * Return: 0 on success, negative on failure */ -static int ice_configure_phy(struct ice_vsi *vsi) +static int ice_phy_cfg(struct ice_vsi *vsi, bool link_en) { struct device *dev = ice_pf_to_dev(vsi->back); struct ice_port_info *pi = vsi->port_info; @@ -2199,9 +2127,6 @@ static int ice_configure_phy(struct ice_vsi *vsi) phy->link_info.topo_media_conflict == ICE_AQ_LINK_TOPO_UNSUPP_MEDIA) return -EPERM; - if (test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, pf->flags)) - return ice_force_phys_link_state(vsi, true); - pcaps = kzalloc_obj(*pcaps); if (!pcaps) return -ENOMEM; @@ -2215,10 +2140,8 @@ static int ice_configure_phy(struct ice_vsi *vsi) goto done; } - /* If PHY enable link is configured and configuration has not changed, - * there's nothing to do - */ - if (pcaps->caps & ICE_AQC_PHY_EN_LINK && + /* Configuration has not changed. There's nothing to do. */ + if (link_en == !!(pcaps->caps & ICE_AQC_PHY_EN_LINK) && ice_phy_caps_equals_cfg(pcaps, &phy->curr_user_phy_cfg)) goto done; @@ -2282,8 +2205,12 @@ static int ice_configure_phy(struct ice_vsi *vsi) */ ice_cfg_phy_fc(pi, cfg, phy->curr_user_fc_req); - /* Enable link and link update */ - cfg->caps |= ICE_AQ_PHY_ENA_AUTO_LINK_UPDT | ICE_AQ_PHY_ENA_LINK; + /* Enable/Disable link and link update */ + cfg->caps |= ICE_AQ_PHY_ENA_AUTO_LINK_UPDT; + if (link_en) + cfg->caps |= ICE_AQ_PHY_ENA_LINK; + else + cfg->caps &= ~ICE_AQ_PHY_ENA_LINK; err = ice_aq_set_phy_cfg(&pf->hw, pi, cfg, NULL); if (err) @@ -2336,7 +2263,7 @@ static void ice_check_media_subtask(struct ice_pf *pf) test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, vsi->back->flags)) return; - err = ice_configure_phy(vsi); + err = ice_phy_cfg(vsi, true); if (!err) clear_bit(ICE_FLAG_NO_MEDIA, pf->flags); @@ -4892,9 +4819,15 @@ static int ice_init_link(struct ice_pf *pf) if (!test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, pf->flags)) { struct ice_vsi *vsi = ice_get_main_vsi(pf); + struct ice_link_default_override_tlv *ldo; + bool link_en; + + ldo = &pf->link_dflt_override; + link_en = !(ldo->options & + ICE_LINK_OVERRIDE_AUTO_LINK_DIS); if (vsi) - ice_configure_phy(vsi); + ice_phy_cfg(vsi, link_en); } } else { set_bit(ICE_FLAG_NO_MEDIA, pf->flags); @@ -5312,6 +5245,8 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) return err; } + ice_init_dev_hw(pf); + adapter = ice_adapter_get(pdev); if (IS_ERR(adapter)) { err = PTR_ERR(adapter); @@ -8111,7 +8046,7 @@ int ice_set_rss_hfunc(struct ice_vsi *vsi, u8 hfunc) ctx->info.q_opt_rss |= FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M, hfunc); ctx->info.q_opt_tc = vsi->info.q_opt_tc; - ctx->info.q_opt_flags = vsi->info.q_opt_rss; + ctx->info.q_opt_flags = vsi->info.q_opt_flags; err = ice_update_vsi(hw, vsi->idx, ctx, NULL); if (err) { @@ -9707,7 +9642,7 @@ int ice_open_internal(struct net_device *netdev) } } - err = ice_configure_phy(vsi); + err = ice_phy_cfg(vsi, true); if (err) { netdev_err(netdev, "Failed to set physical link up, error %d\n", err); @@ -9748,7 +9683,7 @@ int ice_stop(struct net_device *netdev) } if (test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, vsi->back->flags)) { - int link_err = ice_force_phys_link_state(vsi, false); + int link_err = ice_phy_cfg(vsi, false); if (link_err) { if (link_err == -ENOMEDIUM) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 6cb0cf7a9891..36df742c326c 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -2710,7 +2710,7 @@ static bool ice_any_port_has_timestamps(struct ice_pf *pf) bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf) { struct ice_hw *hw = &pf->hw; - unsigned int i; + int ret; /* Check software indicator */ switch (pf->ptp.tx_interrupt_mode) { @@ -2731,16 +2731,19 @@ bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf) } /* Check hardware indicator */ - for (i = 0; i < ICE_GET_QUAD_NUM(hw->ptp.num_lports); i++) { - u64 tstamp_ready = 0; - int err; - - err = ice_get_phy_tx_tstamp_ready(&pf->hw, i, &tstamp_ready); - if (err || tstamp_ready) - return true; + ret = ice_check_phy_tx_tstamp_ready(hw); + if (ret < 0) { + dev_dbg(ice_pf_to_dev(pf), "Unable to read PHY Tx timestamp ready bitmap, err %d\n", + ret); + /* Stop triggering IRQs if we're unable to read PHY */ + return false; } - return false; + /* ice_check_phy_tx_tstamp_ready() returns 1 if there are timestamps + * available, 0 if there are no waiting timestamps, and a negative + * value if there was an error (which we checked for above). + */ + return ret > 0; } /** @@ -2824,8 +2827,7 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct ice_pf *pf) { struct device *dev = ice_pf_to_dev(pf); struct ice_hw *hw = &pf->hw; - bool trigger_oicr = false; - unsigned int i; + int ret; if (!pf->ptp.port.tx.has_ready_bitmap) return; @@ -2833,21 +2835,11 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct ice_pf *pf) if (!ice_pf_src_tmr_owned(pf)) return; - for (i = 0; i < ICE_GET_QUAD_NUM(hw->ptp.num_lports); i++) { - u64 tstamp_ready; - int err; - - err = ice_get_phy_tx_tstamp_ready(&pf->hw, i, &tstamp_ready); - if (!err && tstamp_ready) { - trigger_oicr = true; - break; - } - } - - if (trigger_oicr) { - /* Trigger a software interrupt, to ensure this data - * gets processed. - */ + ret = ice_check_phy_tx_tstamp_ready(hw); + if (ret < 0) { + dev_dbg(dev, "PTP periodic task unable to read PHY timestamp ready bitmap, err %d\n", + ret); + } else if (ret) { dev_dbg(dev, "PTP periodic task detected waiting timestamps. Triggering Tx timestamp interrupt now.\n"); wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M); diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_consts.h b/drivers/net/ethernet/intel/ice/ice_ptp_consts.h index 19dddd9b53dd..4d298c27bfb2 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_consts.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_consts.h @@ -78,14 +78,14 @@ struct ice_eth56g_mac_reg_cfg eth56g_mac_cfg[NUM_ICE_ETH56G_LNK_SPD] = { .blktime = 0x666, /* 3.2 */ .tx_offset = { .serdes = 0x234c, /* 17.6484848 */ - .no_fec = 0x8e80, /* 71.25 */ + .no_fec = 0x93d9, /* 73 */ .fc = 0xb4a4, /* 90.32 */ .sfd = 0x4a4, /* 2.32 */ .onestep = 0x4ccd /* 38.4 */ }, .rx_offset = { .serdes = 0xffffeb27, /* -10.42424 */ - .no_fec = 0xffffcccd, /* -25.6 */ + .no_fec = 0xffffc7b6, /* -28 */ .fc = 0xfffc557b, /* -469.26 */ .sfd = 0x4a4, /* 2.32 */ .bs_ds = 0x32 /* 0.0969697 */ @@ -118,17 +118,17 @@ struct ice_eth56g_mac_reg_cfg eth56g_mac_cfg[NUM_ICE_ETH56G_LNK_SPD] = { .mktime = 0x147b, /* 10.24, only if RS-FEC enabled */ .tx_offset = { .serdes = 0xe1e, /* 7.0593939 */ - .no_fec = 0x3857, /* 28.17 */ + .no_fec = 0x4266, /* 33 */ .fc = 0x48c3, /* 36.38 */ - .rs = 0x8100, /* 64.5 */ + .rs = 0x8a00, /* 69 */ .sfd = 0x1dc, /* 0.93 */ .onestep = 0x1eb8 /* 15.36 */ }, .rx_offset = { .serdes = 0xfffff7a9, /* -4.1697 */ - .no_fec = 0xffffe71a, /* -12.45 */ + .no_fec = 0xffffe700, /* -12 */ .fc = 0xfffe894d, /* -187.35 */ - .rs = 0xfffff8cd, /* -3.6 */ + .rs = 0xfffff8cc, /* -3 */ .sfd = 0x1dc, /* 0.93 */ .bs_ds = 0x14 /* 0.0387879, RS-FEC 0 */ } diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 61c0a0d93ea8..24fb7a3e14d6 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -377,6 +377,31 @@ static void ice_ptp_cfg_sync_delay(const struct ice_hw *hw, u32 delay) * The following functions operate on devices with the ETH 56G PHY. */ +/** + * ice_ptp_init_phc_e825c - Perform E825C specific PHC initialization + * @hw: pointer to HW struct + * + * Perform E825C-specific PTP hardware clock initialization steps. + * + * Return: 0 on success, or a negative error value on failure. + */ +static int ice_ptp_init_phc_e825c(struct ice_hw *hw) +{ + int err; + + /* Soft reset all ports, to ensure everything is at a clean state */ + for (int port = 0; port < hw->ptp.num_lports; port++) { + err = ice_ptp_phy_soft_reset_eth56g(hw, port); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to soft reset port %d, err %d\n", + port, err); + return err; + } + } + + return 0; +} + /** * ice_ptp_get_dest_dev_e825 - get destination PHY for given port number * @hw: pointer to the HW struct @@ -1847,6 +1872,8 @@ static int ice_phy_cfg_mac_eth56g(struct ice_hw *hw, u8 port) * @ena: enable or disable interrupt * @threshold: interrupt threshold * + * The threshold cannot be 0 while the interrupt is enabled. + * * Configure TX timestamp interrupt for the specified port * * Return: @@ -1858,19 +1885,45 @@ int ice_phy_cfg_intr_eth56g(struct ice_hw *hw, u8 port, bool ena, u8 threshold) int err; u32 val; + if (ena && !threshold) + return -EINVAL; + err = ice_read_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, &val); if (err) return err; + val &= ~PHY_TS_INT_CONFIG_ENA_M; if (ena) { - val |= PHY_TS_INT_CONFIG_ENA_M; val &= ~PHY_TS_INT_CONFIG_THRESHOLD_M; val |= FIELD_PREP(PHY_TS_INT_CONFIG_THRESHOLD_M, threshold); - } else { - val &= ~PHY_TS_INT_CONFIG_ENA_M; + err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, + val); + if (err) { + ice_debug(hw, ICE_DBG_PTP, + "Failed to update 'threshold' PHY_REG_TS_INT_CONFIG port=%u ena=%u threshold=%u\n", + port, !!ena, threshold); + return err; + } + val |= PHY_TS_INT_CONFIG_ENA_M; } - return ice_write_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, val); + err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, val); + if (err) { + ice_debug(hw, ICE_DBG_PTP, + "Failed to update 'ena' PHY_REG_TS_INT_CONFIG port=%u ena=%u threshold=%u\n", + port, !!ena, threshold); + return err; + } + + err = ice_read_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, &val); + if (err) { + ice_debug(hw, ICE_DBG_PTP, + "Failed to read PHY_REG_TS_INT_CONFIG port=%u ena=%u threshold=%u\n", + port, !!ena, threshold); + return err; + } + + return 0; } /** @@ -2115,6 +2168,35 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port) return 0; } +/** + * ice_check_phy_tx_tstamp_ready_eth56g - Check Tx memory status for all ports + * @hw: pointer to the HW struct + * + * Check the PHY_REG_TX_MEMORY_STATUS for all ports. A set bit indicates + * a waiting timestamp. + * + * Return: 1 if any port has at least one timestamp ready bit set, + * 0 otherwise, and a negative error code if unable to read the bitmap. + */ +static int ice_check_phy_tx_tstamp_ready_eth56g(struct ice_hw *hw) +{ + int port; + + for (port = 0; port < hw->ptp.num_lports; port++) { + u64 tstamp_ready; + int err; + + err = ice_get_phy_tx_tstamp_ready(hw, port, &tstamp_ready); + if (err) + return err; + + if (tstamp_ready) + return 1; + } + + return 0; +} + /** * ice_ptp_read_tx_hwtstamp_status_eth56g - Get TX timestamp status * @hw: pointer to the HW struct @@ -2137,13 +2219,19 @@ int ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32 *ts_status) *ts_status = 0; for (phy = 0; phy < params->num_phys; phy++) { + u8 port; int err; - err = ice_read_phy_eth56g(hw, phy, PHY_PTP_INT_STATUS, &status); + /* ice_read_phy_eth56g expects a port index, so use the first + * port of the PHY + */ + port = phy * hw->ptp.ports_per_phy; + + err = ice_read_phy_eth56g(hw, port, PHY_PTP_INT_STATUS, &status); if (err) return err; - *ts_status |= (status & mask) << (phy * hw->ptp.ports_per_phy); + *ts_status |= (status & mask) << port; } ice_debug(hw, ICE_DBG_PTP, "PHY interrupt err: %x\n", *ts_status); @@ -2151,6 +2239,69 @@ int ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32 *ts_status) return 0; } +/** + * ice_ptp_phy_soft_reset_eth56g - Perform a PHY soft reset on ETH56G + * @hw: pointer to the HW structure + * @port: PHY port number + * + * Trigger a soft reset of the ETH56G PHY by toggling the soft reset + * bit in the PHY global register. The reset sequence consists of: + * 1. Clearing the soft reset bit + * 2. Asserting the soft reset bit + * 3. Clearing the soft reset bit again + * + * Short delays are inserted between each step to allow the hardware + * to settle. This provides a controlled way to reinitialize the PHY + * without requiring a full device reset. + * + * Return: 0 on success, or a negative error code on failure when + * reading or writing the PHY register. + */ +int ice_ptp_phy_soft_reset_eth56g(struct ice_hw *hw, u8 port) +{ + u32 global_val; + int err; + + err = ice_read_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, &global_val); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to read PHY_REG_GLOBAL for port %d, err %d\n", + port, err); + return err; + } + + global_val &= ~PHY_REG_GLOBAL_SOFT_RESET_M; + ice_debug(hw, ICE_DBG_PTP, "Clearing soft reset bit for port %d, val: 0x%x\n", + port, global_val); + err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, global_val); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to write PHY_REG_GLOBAL for port %d, err %d\n", + port, err); + return err; + } + + usleep_range(5000, 6000); + + global_val |= PHY_REG_GLOBAL_SOFT_RESET_M; + ice_debug(hw, ICE_DBG_PTP, "Set soft reset bit for port %d, val: 0x%x\n", + port, global_val); + err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, global_val); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to write PHY_REG_GLOBAL for port %d, err %d\n", + port, err); + return err; + } + usleep_range(5000, 6000); + + global_val &= ~PHY_REG_GLOBAL_SOFT_RESET_M; + ice_debug(hw, ICE_DBG_PTP, "Clear soft reset bit for port %d, val: 0x%x\n", + port, global_val); + err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, global_val); + if (err) + ice_debug(hw, ICE_DBG_PTP, "Failed to write PHY_REG_GLOBAL for port %d, err %d\n", + port, err); + return err; +} + /** * ice_get_phy_tx_tstamp_ready_eth56g - Read the Tx memory status register * @hw: pointer to the HW struct @@ -4202,6 +4353,35 @@ ice_get_phy_tx_tstamp_ready_e82x(struct ice_hw *hw, u8 quad, u64 *tstamp_ready) return 0; } +/** + * ice_check_phy_tx_tstamp_ready_e82x - Check Tx memory status for all quads + * @hw: pointer to the HW struct + * + * Check the Q_REG_TX_MEMORY_STATUS for all quads. A set bit indicates + * a waiting timestamp. + * + * Return: 1 if any quad has at least one timestamp ready bit set, + * 0 otherwise, and a negative error value if unable to read the bitmap. + */ +static int ice_check_phy_tx_tstamp_ready_e82x(struct ice_hw *hw) +{ + int quad; + + for (quad = 0; quad < ICE_GET_QUAD_NUM(hw->ptp.num_lports); quad++) { + u64 tstamp_ready; + int err; + + err = ice_get_phy_tx_tstamp_ready(hw, quad, &tstamp_ready); + if (err) + return err; + + if (tstamp_ready) + return 1; + } + + return 0; +} + /** * ice_phy_cfg_intr_e82x - Configure TX timestamp interrupt * @hw: pointer to the HW struct @@ -4755,6 +4935,23 @@ ice_get_phy_tx_tstamp_ready_e810(struct ice_hw *hw, u8 port, u64 *tstamp_ready) return 0; } +/** + * ice_check_phy_tx_tstamp_ready_e810 - Check Tx memory status register + * @hw: pointer to the HW struct + * + * The E810 devices do not have a Tx memory status register. Note this is + * intentionally different behavior from ice_get_phy_tx_tstamp_ready_e810 + * which always says that all bits are ready. This function is called in cases + * where code will trigger interrupts if timestamps are waiting, and should + * not be called for E810 hardware. + * + * Return: 0. + */ +static int ice_check_phy_tx_tstamp_ready_e810(struct ice_hw *hw) +{ + return 0; +} + /* E810 SMA functions * * The following functions operate specifically on E810 hardware and are used @@ -5009,6 +5206,21 @@ static void ice_get_phy_tx_tstamp_ready_e830(const struct ice_hw *hw, u8 port, *tstamp_ready |= rd32(hw, E830_PRTMAC_TS_TX_MEM_VALID_L); } +/** + * ice_check_phy_tx_tstamp_ready_e830 - Check Tx memory status register + * @hw: pointer to the HW struct + * + * Return: 1 if the device has waiting timestamps, 0 otherwise. + */ +static int ice_check_phy_tx_tstamp_ready_e830(struct ice_hw *hw) +{ + u64 tstamp_ready; + + ice_get_phy_tx_tstamp_ready_e830(hw, 0, &tstamp_ready); + + return !!tstamp_ready; +} + /** * ice_ptp_init_phy_e830 - initialize PHY parameters * @ptp: pointer to the PTP HW struct @@ -5381,8 +5593,8 @@ int ice_ptp_write_incval_locked(struct ice_hw *hw, u64 incval) */ int ice_ptp_adj_clock(struct ice_hw *hw, s32 adj) { + int err = 0; u8 tmr_idx; - int err; tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; @@ -5399,8 +5611,8 @@ int ice_ptp_adj_clock(struct ice_hw *hw, s32 adj) err = ice_ptp_prep_phy_adj_e810(hw, adj); break; case ICE_MAC_E830: - /* E830 sync PHYs automatically after setting GLTSYN_SHADJ */ - return 0; + /* E830 sync PHYs automatically after setting cmd register */ + break; case ICE_MAC_GENERIC: err = ice_ptp_prep_phy_adj_e82x(hw, adj); break; @@ -5564,7 +5776,7 @@ int ice_ptp_init_phc(struct ice_hw *hw) case ICE_MAC_GENERIC: return ice_ptp_init_phc_e82x(hw); case ICE_MAC_GENERIC_3K_E825: - return 0; + return ice_ptp_init_phc_e825c(hw); default: return -EOPNOTSUPP; } @@ -5601,6 +5813,33 @@ int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready) } } +/** + * ice_check_phy_tx_tstamp_ready - Check PHY Tx timestamp memory status + * @hw: pointer to the HW struct + * + * Check the PHY for Tx timestamp memory status on all ports. If you need to + * see individual timestamp status for each index, use + * ice_get_phy_tx_tstamp_ready() instead. + * + * Return: 1 if any port has timestamps available, 0 if there are no timestamps + * available, and a negative error code on failure. + */ +int ice_check_phy_tx_tstamp_ready(struct ice_hw *hw) +{ + switch (hw->mac_type) { + case ICE_MAC_E810: + return ice_check_phy_tx_tstamp_ready_e810(hw); + case ICE_MAC_E830: + return ice_check_phy_tx_tstamp_ready_e830(hw); + case ICE_MAC_GENERIC: + return ice_check_phy_tx_tstamp_ready_e82x(hw); + case ICE_MAC_GENERIC_3K_E825: + return ice_check_phy_tx_tstamp_ready_eth56g(hw); + default: + return -EOPNOTSUPP; + } +} + /** * ice_cgu_get_pin_desc_e823 - get pin description array * @hw: pointer to the hw struct diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h index 9bfd3e79c580..1c9e77dbc770 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h @@ -300,6 +300,7 @@ void ice_ptp_reset_ts_memory(struct ice_hw *hw); int ice_ptp_init_phc(struct ice_hw *hw); void ice_ptp_init_hw(struct ice_hw *hw); int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready); +int ice_check_phy_tx_tstamp_ready(struct ice_hw *hw); int ice_ptp_one_port_cmd(struct ice_hw *hw, u8 configured_port, enum ice_ptp_tmr_cmd configured_cmd); @@ -374,6 +375,7 @@ int ice_stop_phy_timer_eth56g(struct ice_hw *hw, u8 port, bool soft_reset); int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port); int ice_phy_cfg_intr_eth56g(struct ice_hw *hw, u8 port, bool ena, u8 threshold); int ice_phy_cfg_ptp_1step_eth56g(struct ice_hw *hw, u8 port); +int ice_ptp_phy_soft_reset_eth56g(struct ice_hw *hw, u8 port); #define ICE_ETH56G_NOMINAL_INCVAL 0x140000000ULL #define ICE_ETH56G_NOMINAL_PCS_REF_TUS 0x100000000ULL @@ -676,6 +678,9 @@ static inline u64 ice_get_base_incval(struct ice_hw *hw) #define ICE_P0_GNSS_PRSNT_N BIT(4) /* ETH56G PHY register addresses */ +#define PHY_REG_GLOBAL 0x0 +#define PHY_REG_GLOBAL_SOFT_RESET_M BIT(11) + /* Timestamp PHY incval registers */ #define PHY_REG_TIMETUS_L 0x8 #define PHY_REG_TIMETUS_U 0xC diff --git a/drivers/net/ethernet/intel/ice/ice_sf_eth.c b/drivers/net/ethernet/intel/ice/ice_sf_eth.c index 2cf04bc6edce..a730aa368c92 100644 --- a/drivers/net/ethernet/intel/ice/ice_sf_eth.c +++ b/drivers/net/ethernet/intel/ice/ice_sf_eth.c @@ -305,6 +305,8 @@ ice_sf_eth_activate(struct ice_dynamic_port *dyn_port, aux_dev_uninit: auxiliary_device_uninit(&sf_dev->adev); + return err; + sf_dev_free: kfree(sf_dev); xa_erase: diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index a2cd4cf37734..4ca1a0602307 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -190,9 +190,10 @@ void ice_free_tstamp_ring(struct ice_tx_ring *tx_ring) void ice_free_tx_tstamp_ring(struct ice_tx_ring *tx_ring) { ice_free_tstamp_ring(tx_ring); + clear_bit(ICE_TX_RING_FLAGS_TXTIME, tx_ring->flags); + smp_wmb(); /* order flag clear before pointer NULL */ kfree_rcu(tx_ring->tstamp_ring, rcu); - tx_ring->tstamp_ring = NULL; - tx_ring->flags &= ~ICE_TX_FLAGS_TXTIME; + WRITE_ONCE(tx_ring->tstamp_ring, NULL); } /** @@ -405,7 +406,7 @@ static int ice_alloc_tstamp_ring(struct ice_tx_ring *tx_ring) tx_ring->tstamp_ring = tstamp_ring; tstamp_ring->desc = NULL; tstamp_ring->count = ice_calc_ts_ring_count(tx_ring); - tx_ring->flags |= ICE_TX_FLAGS_TXTIME; + set_bit(ICE_TX_RING_FLAGS_TXTIME, tx_ring->flags); return 0; } @@ -1521,13 +1522,20 @@ ice_tx_map(struct ice_tx_ring *tx_ring, struct ice_tx_buf *first, return; if (ice_is_txtime_cfg(tx_ring)) { - struct ice_tstamp_ring *tstamp_ring = tx_ring->tstamp_ring; - u32 tstamp_count = tstamp_ring->count; - u32 j = tstamp_ring->next_to_use; + struct ice_tstamp_ring *tstamp_ring; + u32 tstamp_count, j; struct ice_ts_desc *ts_desc; struct timespec64 ts; u32 tstamp; + smp_rmb(); /* order flag read before pointer read */ + tstamp_ring = READ_ONCE(tx_ring->tstamp_ring); + if (unlikely(!tstamp_ring)) + goto ring_kick; + + tstamp_count = tstamp_ring->count; + j = tstamp_ring->next_to_use; + ts = ktime_to_timespec64(first->skb->tstamp); tstamp = ts.tv_nsec >> ICE_TXTIME_CTX_RESOLUTION_128NS; @@ -1555,6 +1563,7 @@ ice_tx_map(struct ice_tx_ring *tx_ring, struct ice_tx_buf *first, tstamp_ring->next_to_use = j; writel_relaxed(j, tstamp_ring->tail); } else { +ring_kick: writel_relaxed(i, tx_ring->tail); } return; @@ -1814,7 +1823,7 @@ ice_tx_prepare_vlan_flags(struct ice_tx_ring *tx_ring, struct ice_tx_buf *first) */ if (skb_vlan_tag_present(skb)) { first->vid = skb_vlan_tag_get(skb); - if (tx_ring->flags & ICE_TX_FLAGS_RING_VLAN_L2TAG2) + if (test_bit(ICE_TX_RING_FLAGS_VLAN_L2TAG2, tx_ring->flags)) first->tx_flags |= ICE_TX_FLAGS_HW_OUTER_SINGLE_VLAN; else first->tx_flags |= ICE_TX_FLAGS_HW_VLAN; @@ -2158,6 +2167,9 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) ice_trace(xmit_frame_ring, tx_ring, skb); + /* record the location of the first descriptor for this packet */ + first = &tx_ring->tx_buf[tx_ring->next_to_use]; + count = ice_xmit_desc_count(skb); if (ice_chk_linearize(skb, count)) { if (__skb_linearize(skb)) @@ -2183,8 +2195,6 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) offload.tx_ring = tx_ring; - /* record the location of the first descriptor for this packet */ - first = &tx_ring->tx_buf[tx_ring->next_to_use]; first->skb = skb; first->type = ICE_TX_BUF_SKB; first->bytecount = max_t(unsigned int, skb->len, ETH_ZLEN); @@ -2249,6 +2259,7 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) out_drop: ice_trace(xmit_frame_ring_drop, tx_ring, skb); dev_kfree_skb_any(skb); + first->type = ICE_TX_BUF_EMPTY; return NETDEV_TX_OK; } diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.h b/drivers/net/ethernet/intel/ice/ice_txrx.h index b6547e1b7c42..5e517f219379 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.h +++ b/drivers/net/ethernet/intel/ice/ice_txrx.h @@ -212,6 +212,14 @@ enum ice_rx_dtype { ICE_RX_DTYPE_SPLIT_ALWAYS = 2, }; +enum ice_tx_ring_flags { + ICE_TX_RING_FLAGS_XDP, + ICE_TX_RING_FLAGS_VLAN_L2TAG1, + ICE_TX_RING_FLAGS_VLAN_L2TAG2, + ICE_TX_RING_FLAGS_TXTIME, + ICE_TX_RING_FLAGS_NBITS, +}; + struct ice_pkt_ctx { u64 cached_phctime; __be16 vlan_proto; @@ -352,11 +360,7 @@ struct ice_tx_ring { u16 count; /* Number of descriptors */ u16 q_index; /* Queue number of ring */ - u8 flags; -#define ICE_TX_FLAGS_RING_XDP BIT(0) -#define ICE_TX_FLAGS_RING_VLAN_L2TAG1 BIT(1) -#define ICE_TX_FLAGS_RING_VLAN_L2TAG2 BIT(2) -#define ICE_TX_FLAGS_TXTIME BIT(3) + DECLARE_BITMAP(flags, ICE_TX_RING_FLAGS_NBITS); struct xsk_buff_pool *xsk_pool; @@ -398,7 +402,7 @@ static inline bool ice_ring_ch_enabled(struct ice_tx_ring *ring) static inline bool ice_ring_is_xdp(struct ice_tx_ring *ring) { - return !!(ring->flags & ICE_TX_FLAGS_RING_XDP); + return test_bit(ICE_TX_RING_FLAGS_XDP, ring->flags); } enum ice_container_type { diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 772f6b07340d..b1f46707dcc0 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -804,7 +804,12 @@ void ice_reset_all_vfs(struct ice_pf *pf) ice_vf_ctrl_invalidate_vsi(vf); ice_vf_pre_vsi_rebuild(vf); - ice_vf_rebuild_vsi(vf); + if (ice_vf_rebuild_vsi(vf)) { + dev_err(dev, "VF %u VSI rebuild failed, leaving VF disabled\n", + vf->vf_id); + mutex_unlock(&vf->cfg_lock); + continue; + } ice_vf_post_vsi_rebuild(vf); ice_eswitch_attach_vf(pf, vf); diff --git a/drivers/net/ethernet/intel/idpf/idpf_idc.c b/drivers/net/ethernet/intel/idpf/idpf_idc.c index 7e4f4ac92653..b7d6b08fc89e 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_idc.c +++ b/drivers/net/ethernet/intel/idpf/idpf_idc.c @@ -90,7 +90,10 @@ static int idpf_plug_vport_aux_dev(struct iidc_rdma_core_dev_info *cdev_info, return 0; err_aux_dev_add: + ida_free(&idpf_idc_ida, adev->id); + vdev_info->adev = NULL; auxiliary_device_uninit(adev); + return ret; err_aux_dev_init: ida_free(&idpf_idc_ida, adev->id); err_ida_alloc: @@ -228,7 +231,10 @@ static int idpf_plug_core_aux_dev(struct iidc_rdma_core_dev_info *cdev_info) return 0; err_aux_dev_add: + ida_free(&idpf_idc_ida, adev->id); + cdev_info->adev = NULL; auxiliary_device_uninit(adev); + return ret; err_aux_dev_init: ida_free(&idpf_idc_ida, adev->id); err_ida_alloc: diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c index eec91c4f0a75..4a51d2727547 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c +++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c @@ -952,6 +952,8 @@ int idpf_ptp_init(struct idpf_adapter *adapter) goto free_ptp; } + spin_lock_init(&adapter->ptp->read_dev_clk_lock); + err = idpf_ptp_create_clock(adapter); if (err) goto free_ptp; @@ -977,8 +979,6 @@ int idpf_ptp_init(struct idpf_adapter *adapter) goto remove_clock; } - spin_lock_init(&adapter->ptp->read_dev_clk_lock); - pci_dbg(adapter->pdev, "PTP init successful\n"); return 0; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c index 3debf2fae1a4..6f13296303cb 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c @@ -249,34 +249,21 @@ DEFINE_SHOW_ATTRIBUTE(npc_defrag); int npc_cn20k_debugfs_init(struct rvu *rvu) { struct npc_priv_t *npc_priv = npc_priv_get(); - struct dentry *npc_dentry; - npc_dentry = debugfs_create_file("mcam_layout", 0444, rvu->rvu_dbg.npc, - npc_priv, &npc_mcam_layout_fops); + debugfs_create_file("mcam_layout", 0444, rvu->rvu_dbg.npc, + npc_priv, &npc_mcam_layout_fops); - if (!npc_dentry) - return -EFAULT; + debugfs_create_file("mcam_default", 0444, rvu->rvu_dbg.npc, + rvu, &npc_mcam_default_fops); - npc_dentry = debugfs_create_file("mcam_default", 0444, rvu->rvu_dbg.npc, - rvu, &npc_mcam_default_fops); + debugfs_create_file("vidx2idx", 0444, rvu->rvu_dbg.npc, + npc_priv, &npc_vidx2idx_map_fops); - if (!npc_dentry) - return -EFAULT; + debugfs_create_file("idx2vidx", 0444, rvu->rvu_dbg.npc, + npc_priv, &npc_idx2vidx_map_fops); - npc_dentry = debugfs_create_file("vidx2idx", 0444, rvu->rvu_dbg.npc, - npc_priv, &npc_vidx2idx_map_fops); - if (!npc_dentry) - return -EFAULT; - - npc_dentry = debugfs_create_file("idx2vidx", 0444, rvu->rvu_dbg.npc, - npc_priv, &npc_idx2vidx_map_fops); - if (!npc_dentry) - return -EFAULT; - - npc_dentry = debugfs_create_file("defrag", 0444, rvu->rvu_dbg.npc, - npc_priv, &npc_defrag_fops); - if (!npc_dentry) - return -EFAULT; + debugfs_create_file("defrag", 0444, rvu->rvu_dbg.npc, + npc_priv, &npc_defrag_fops); return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c index 7291fdb89b03..6b3f453fd500 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c @@ -798,7 +798,7 @@ program_mkex_extr: iounmap(mkex_prfl_addr); } -void +int npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, int index, bool enable) { @@ -808,7 +808,12 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, u64 cfg, hw_prio; u8 kw_type; - npc_mcam_idx_2_key_type(rvu, index, &kw_type); + if (index < 0 || index >= mcam->total_entries) + return -EINVAL; + + if (npc_mcam_idx_2_key_type(rvu, index, &kw_type)) + return -EINVAL; + if (kw_type == NPC_MCAM_KEY_X2) { cfg = rvu_read64(rvu, blkaddr, NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, @@ -819,7 +824,7 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, rvu_write64(rvu, blkaddr, NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), cfg); - return; + return 0; } /* For NPC_CN20K_MCAM_KEY_X4 keys, both the banks @@ -836,10 +841,12 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), cfg); } + + return 0; } -void -npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int bank, int index) +static void +npc_clear_x2_entry(struct rvu *rvu, int blkaddr, int bank, int index) { rvu_write64(rvu, blkaddr, NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index, bank, 1), @@ -873,6 +880,33 @@ npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int bank, int index) NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(index, bank), 0); } +int +npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int mcam_idx) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + int bank = npc_get_bank(mcam, mcam_idx); + u8 kw_type; + int index; + + if (npc_mcam_idx_2_key_type(rvu, mcam_idx, &kw_type)) + return -EINVAL; + + index = mcam_idx & (mcam->banksize - 1); + + if (kw_type == NPC_MCAM_KEY_X2) { + npc_clear_x2_entry(rvu, blkaddr, bank, index); + return 0; + } + + /* For NPC_MCAM_KEY_X4 keys, both the banks + * need to be programmed with the same value. + */ + for (bank = 0; bank < mcam->banks_per_entry; bank++) + npc_clear_x2_entry(rvu, blkaddr, bank, index); + + return 0; +} + static void npc_cn20k_get_keyword(struct cn20k_mcam_entry *entry, int idx, u64 *cam0, u64 *cam1) { @@ -1014,48 +1048,27 @@ static void npc_cn20k_config_kw_x4(struct rvu *rvu, struct npc_mcam *mcam, kw, req_kw_type); } -static void -npc_cn20k_set_mcam_bank_cfg(struct rvu *rvu, int blkaddr, int mcam_idx, - int bank, u8 kw_type, bool enable, u8 hw_prio) -{ - struct npc_mcam *mcam = &rvu->hw->mcam; - u64 bank_cfg; - - bank_cfg = (u64)hw_prio << 24; - if (enable) - bank_cfg |= 0x1; - - if (kw_type == NPC_MCAM_KEY_X2) { - rvu_write64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), - bank_cfg); - return; - } - - /* For NPC_MCAM_KEY_X4 keys, both the banks - * need to be programmed with the same value. - */ - for (bank = 0; bank < mcam->banks_per_entry; bank++) { - rvu_write64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), - bank_cfg); - } -} - -void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index, - u8 intf, struct cn20k_mcam_entry *entry, - bool enable, u8 hw_prio, u8 req_kw_type) +int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index, + u8 intf, struct cn20k_mcam_entry *entry, + bool enable, u8 hw_prio, u8 req_kw_type) { struct npc_mcam *mcam = &rvu->hw->mcam; int mcam_idx = index % mcam->banksize; int bank = index / mcam->banksize; + u64 bank_cfg = (u64)hw_prio << 24; int kw = 0; u8 kw_type; + if (index < 0 || index >= mcam->total_entries) + return -EINVAL; + + if (npc_mcam_idx_2_key_type(rvu, index, &kw_type)) + return -EINVAL; + /* Disable before mcam entry update */ - npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, false); + if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, false)) + return -EINVAL; - npc_mcam_idx_2_key_type(rvu, index, &kw_type); /* CAM1 takes the comparison value and * CAM0 specifies match for a bit in key being '0' or '1' or 'dontcare'. * CAM1 = 0 & CAM0 = 1 => match if key = 0 @@ -1064,7 +1077,7 @@ void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index, */ if (kw_type == NPC_MCAM_KEY_X2) { /* Clear mcam entry to avoid writes being suppressed by NPC */ - npc_cn20k_clear_mcam_entry(rvu, blkaddr, bank, mcam_idx); + npc_clear_x2_entry(rvu, blkaddr, bank, mcam_idx); npc_cn20k_config_kw_x2(rvu, mcam, blkaddr, mcam_idx, intf, entry, bank, kw_type, kw, req_kw_type); @@ -1085,44 +1098,55 @@ void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index, NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, bank, 1), entry->vtag_action); - goto set_cfg; + + /* Set HW priority */ + rvu_write64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), + bank_cfg); + + } else { + /* Clear mcam entry to avoid writes being suppressed by NPC */ + npc_clear_x2_entry(rvu, blkaddr, 0, mcam_idx); + npc_clear_x2_entry(rvu, blkaddr, 1, mcam_idx); + + npc_cn20k_config_kw_x4(rvu, mcam, blkaddr, + mcam_idx, intf, entry, + kw_type, req_kw_type); + for (bank = 0; bank < mcam->banks_per_entry; bank++) { + /* Set 'action' */ + rvu_write64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, + bank, 0), + entry->action); + + /* Set TAG 'action' */ + rvu_write64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, + bank, 1), + entry->vtag_action); + + /* Set 'action2' for inline receive */ + rvu_write64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, + bank, 2), + entry->action2); + + /* Set HW priority */ + rvu_write64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), + bank_cfg); + } } - /* Clear mcam entry to avoid writes being suppressed by NPC */ - npc_cn20k_clear_mcam_entry(rvu, blkaddr, 0, mcam_idx); - npc_cn20k_clear_mcam_entry(rvu, blkaddr, 1, mcam_idx); - - npc_cn20k_config_kw_x4(rvu, mcam, blkaddr, - mcam_idx, intf, entry, - kw_type, req_kw_type); - for (bank = 0; bank < mcam->banks_per_entry; bank++) { - /* Set 'action' */ - rvu_write64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, - bank, 0), - entry->action); - - /* Set TAG 'action' */ - rvu_write64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, - bank, 1), - entry->vtag_action); - - /* Set 'action2' for inline receive */ - rvu_write64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, - bank, 2), - entry->action2); - } - -set_cfg: /* TODO: */ /* PF installing VF rule */ - npc_cn20k_set_mcam_bank_cfg(rvu, blkaddr, mcam_idx, bank, - kw_type, enable, hw_prio); + if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable)) + return -EINVAL; + + return 0; } -void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest) +int npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest) { struct npc_mcam *mcam = &rvu->hw->mcam; u64 cfg, sreg, dreg, soff, doff; @@ -1130,12 +1154,20 @@ void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest) int bank, i, sb, db; int dbank, sbank; + if (src >= mcam->total_entries || dest >= mcam->total_entries) + return -EINVAL; + dbank = npc_get_bank(mcam, dest); sbank = npc_get_bank(mcam, src); - npc_mcam_idx_2_key_type(rvu, src, &src_kwtype); - npc_mcam_idx_2_key_type(rvu, dest, &dest_kwtype); + + if (npc_mcam_idx_2_key_type(rvu, src, &src_kwtype)) + return -EINVAL; + + if (npc_mcam_idx_2_key_type(rvu, dest, &dest_kwtype)) + return -EINVAL; + if (src_kwtype != dest_kwtype) - return; + return -EINVAL; src &= (mcam->banksize - 1); dest &= (mcam->banksize - 1); @@ -1170,6 +1202,8 @@ void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest) if (src_kwtype == NPC_MCAM_KEY_X2) break; } + + return 0; } static void npc_cn20k_fill_entryword(struct cn20k_mcam_entry *entry, int idx, @@ -1179,20 +1213,36 @@ static void npc_cn20k_fill_entryword(struct cn20k_mcam_entry *entry, int idx, entry->kw_mask[idx] = cam1 ^ cam0; } -void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index, - struct cn20k_mcam_entry *entry, - u8 *intf, u8 *ena, u8 *hw_prio) +int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index, + struct cn20k_mcam_entry *entry, + u8 *intf, u8 *ena, u8 *hw_prio) { struct npc_mcam *mcam = &rvu->hw->mcam; u64 cam0, cam1, bank_cfg, cfg; int kw = 0, bank; u8 kw_type; - npc_mcam_idx_2_key_type(rvu, index, &kw_type); + if (index >= mcam->total_entries) + return -EINVAL; + + if (npc_mcam_idx_2_key_type(rvu, index, &kw_type)) + return -EINVAL; bank = npc_get_bank(mcam, index); index &= (mcam->banksize - 1); + cfg = rvu_read64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 0)); + entry->action = cfg; + + cfg = rvu_read64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 1)); + entry->vtag_action = cfg; + + cfg = rvu_read64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 2)); + entry->action2 = cfg; + cfg = rvu_read64(rvu, blkaddr, NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index, bank, 1)) & 3; @@ -1242,7 +1292,7 @@ void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index, bank, 0)); npc_cn20k_fill_entryword(entry, kw + 3, cam0, cam1); - goto read_action; + return 0; } for (bank = 0; bank < mcam->banks_per_entry; bank++, kw = kw + 4) { @@ -1287,17 +1337,7 @@ void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index, npc_cn20k_fill_entryword(entry, kw + 3, cam0, cam1); } -read_action: - /* 'action' is set to same value for both bank '0' and '1'. - * Hence, reading bank '0' should be enough. - */ - cfg = rvu_read64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, 0, 0)); - entry->action = cfg; - - cfg = rvu_read64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, 0, 1)); - entry->vtag_action = cfg; + return 0; } int rvu_mbox_handler_npc_cn20k_mcam_write_entry(struct rvu *rvu, @@ -1335,11 +1375,10 @@ int rvu_mbox_handler_npc_cn20k_mcam_write_entry(struct rvu *rvu, if (is_pffunc_af(req->hdr.pcifunc)) nix_intf = req->intf; - npc_cn20k_config_mcam_entry(rvu, blkaddr, req->entry, nix_intf, - &req->entry_data, req->enable_entry, - req->hw_prio, req->req_kw_type); + rc = npc_cn20k_config_mcam_entry(rvu, blkaddr, req->entry, nix_intf, + &req->entry_data, req->enable_entry, + req->hw_prio, req->req_kw_type); - rc = 0; exit: mutex_unlock(&mcam->lock); return rc; @@ -1361,11 +1400,13 @@ int rvu_mbox_handler_npc_cn20k_mcam_read_entry(struct rvu *rvu, mutex_lock(&mcam->lock); rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry); - if (!rc) - npc_cn20k_read_mcam_entry(rvu, blkaddr, req->entry, - &rsp->entry_data, &rsp->intf, - &rsp->enable, &rsp->hw_prio); + if (rc) + goto fail; + rc = npc_cn20k_read_mcam_entry(rvu, blkaddr, req->entry, + &rsp->entry_data, &rsp->intf, + &rsp->enable, &rsp->hw_prio); +fail: mutex_unlock(&mcam->lock); return rc; } @@ -1375,11 +1416,13 @@ int rvu_mbox_handler_npc_cn20k_mcam_alloc_and_write_entry(struct rvu *rvu, struct npc_mcam_alloc_and_write_entry_rsp *rsp) { struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); + struct npc_mcam_free_entry_req free_req = { 0 }; struct npc_mcam_alloc_entry_req entry_req; struct npc_mcam_alloc_entry_rsp entry_rsp; struct npc_mcam *mcam = &rvu->hw->mcam; u16 entry = NPC_MCAM_ENTRY_INVALID; - int blkaddr, rc; + struct msg_rsp free_rsp; + int blkaddr, rc, err; u8 nix_intf; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); @@ -1415,12 +1458,23 @@ int rvu_mbox_handler_npc_cn20k_mcam_alloc_and_write_entry(struct rvu *rvu, else nix_intf = pfvf->nix_rx_intf; - npc_cn20k_config_mcam_entry(rvu, blkaddr, entry, nix_intf, - &req->entry_data, req->enable_entry, - req->hw_prio, req->req_kw_type); + rc = npc_cn20k_config_mcam_entry(rvu, blkaddr, entry, nix_intf, + &req->entry_data, req->enable_entry, + req->hw_prio, req->req_kw_type); mutex_unlock(&mcam->lock); + if (rc) { + free_req.hdr.pcifunc = req->hdr.pcifunc; + free_req.entry = entry_rsp.entry; + err = rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, &free_rsp); + if (err) + dev_err(rvu->dev, + "%s: Error to free mcam idx %u\n", + __func__, entry_rsp.entry); + return rc; + } + rsp->entry = entry_rsp.entry; return 0; } @@ -1480,9 +1534,9 @@ int rvu_mbox_handler_npc_cn20k_read_base_steer_rule(struct rvu *rvu, read_entry: /* Read the mcam entry */ - npc_cn20k_read_mcam_entry(rvu, blkaddr, index, - &rsp->entry, &intf, - &enable, &hw_prio); + rc = npc_cn20k_read_mcam_entry(rvu, blkaddr, index, + &rsp->entry, &intf, + &enable, &hw_prio); mutex_unlock(&mcam->lock); out: return rc; @@ -2305,6 +2359,7 @@ err2: __npc_subbank_mark_free(rvu, sb); err1: kfree(save); + *alloc_cnt = 0; return rc; } @@ -3482,7 +3537,7 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu, { int alloc_cnt1, alloc_cnt2; struct npc_subbank *sb; - int rc, sb_off, i; + int rc, sb_off, i, err; bool deleted; sb = &npc_priv.sb[f->idx]; @@ -3496,6 +3551,7 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu, NPC_MCAM_LOWER_PRIO, false, cnt, save, cnt, true, &alloc_cnt1); + if (alloc_cnt1 < cnt) { rc = __npc_subbank_alloc(rvu, sb, NPC_MCAM_KEY_X2, sb->b1b, @@ -3511,15 +3567,17 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu, dev_err(rvu->dev, "%s: Failed to alloc cnt=%u alloc_cnt1=%u alloc_cnt2=%u\n", __func__, cnt, alloc_cnt1, alloc_cnt2); + rc = -ENOSPC; goto fail_free_alloc; } + return 0; fail_free_alloc: for (i = 0; i < alloc_cnt1 + alloc_cnt2; i++) { - rc = npc_mcam_idx_2_subbank_idx(rvu, save[i], - &sb, &sb_off); - if (rc) { + err = npc_mcam_idx_2_subbank_idx(rvu, save[i], + &sb, &sb_off); + if (err) { dev_err(rvu->dev, "%s: Error to find subbank for mcam idx=%u\n", __func__, save[i]); @@ -3565,9 +3623,10 @@ int npc_defrag_move_vdx_to_free(struct rvu *rvu, struct npc_defrag_node *v, int cnt, u16 *save) { + u16 new_midx, old_midx, vidx, target_pf; struct npc_mcam *mcam = &rvu->hw->mcam; + struct rvu_npc_mcam_rule *rule, *tmp; int i, vidx_cnt, rc, sb_off; - u16 new_midx, old_midx, vidx; struct npc_subbank *sb; bool deleted; u16 pcifunc; @@ -3607,9 +3666,30 @@ int npc_defrag_move_vdx_to_free(struct rvu *rvu, NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(midx, bank)); - npc_cn20k_enable_mcam_entry(rvu, blkaddr, old_midx, false); - npc_cn20k_copy_mcam_entry(rvu, blkaddr, old_midx, new_midx); - npc_cn20k_enable_mcam_entry(rvu, blkaddr, new_midx, true); + /* If bug happened during copy/enable mcam, then there is a bug in allocation + * algorithm itself. There is no point in rewinding and returning, as it + * will face further issue. Return error after printing error + */ + if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, old_midx, false)) { + dev_err(rvu->dev, + "%s: Error happened while disabling old_mid=%u\n", + __func__, old_midx); + return -EFAULT; + } + + if (npc_cn20k_copy_mcam_entry(rvu, blkaddr, old_midx, new_midx)) { + dev_err(rvu->dev, + "%s: Error happened while copying old_midx=%u new_midx=%u\n", + __func__, old_midx, new_midx); + return -EFAULT; + } + + if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, new_midx, true)) { + dev_err(rvu->dev, + "%s: Error happened while enabling new_mid=%u\n", + __func__, new_midx); + return -EFAULT; + } midx = new_midx % mcam->banksize; bank = new_midx / mcam->banksize; @@ -3665,8 +3745,21 @@ int npc_defrag_move_vdx_to_free(struct rvu *rvu, mcam->entry2pfvf_map[new_midx] = pcifunc; /* Counter is not preserved */ mcam->entry2cntr_map[new_midx] = new_midx; + target_pf = mcam->entry2target_pffunc[old_midx]; + mcam->entry2target_pffunc[new_midx] = target_pf; + mcam->entry2target_pffunc[old_midx] = NPC_MCAM_INVALID_MAP; + npc_mcam_set_bit(mcam, new_midx); + /* Note: list order is not functionally required for mcam_rules */ + list_for_each_entry_safe(rule, tmp, &mcam->mcam_rules, list) { + if (rule->entry != old_midx) + continue; + + rule->entry = new_midx; + break; + } + /* Mark as invalid */ v->vidx[vidx_cnt - i - 1] = -1; save[cnt - i - 1] = -1; @@ -3935,6 +4028,13 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, void *val; int i, j; + for (i = 0; i < ARRAY_SIZE(ptr); i++) { + if (!ptr[i]) + continue; + + *ptr[i] = USHRT_MAX; + } + if (!npc_priv.init_done) return 0; @@ -3950,7 +4050,6 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, npc_dft_rule_name[NPC_DFT_RULE_PROMISC_ID], pcifunc); - *ptr[0] = USHRT_MAX; return -ESRCH; } @@ -3970,7 +4069,6 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, npc_dft_rule_name[NPC_DFT_RULE_UCAST_ID], pcifunc); - *ptr[3] = USHRT_MAX; return -ESRCH; } @@ -3990,7 +4088,6 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, __func__, npc_dft_rule_name[i], pcifunc); - *ptr[j] = USHRT_MAX; continue; } @@ -4085,7 +4182,7 @@ int rvu_mbox_handler_npc_get_dft_rl_idxs(struct rvu *rvu, struct msg_req *req, return 0; } -static bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc) +bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc) { return is_pf_cgxmapped(rvu, rvu_get_pf(rvu->pdev, pcifunc)) || is_lbk_vf(rvu, pcifunc); @@ -4093,11 +4190,11 @@ static bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc) void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc) { - struct npc_mcam_free_entry_req free_req = { 0 }; + struct npc_mcam *mcam = &rvu->hw->mcam; + u16 ptr[4] = {[0 ... 3] = USHRT_MAX}; + struct rvu_npc_mcam_rule *rule, *tmp; unsigned long index; - struct msg_rsp rsp; - u16 ptr[4]; - int rc, i; + int blkaddr, rc, i; void *map; if (!npc_priv.init_done) @@ -4155,14 +4252,43 @@ void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc) } free_rules: + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) + return; + for (int i = 0; i < 4; i++) { + if (ptr[i] == USHRT_MAX) + continue; - free_req.hdr.pcifunc = pcifunc; - free_req.all = 1; - rc = rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, &rsp); - if (rc) - dev_err(rvu->dev, - "%s: Error deleting default entries (pcifunc=%#x\n", - __func__, pcifunc); + mutex_lock(&mcam->lock); + npc_mcam_clear_bit(mcam, ptr[i]); + mcam->entry2pfvf_map[ptr[i]] = NPC_MCAM_INVALID_MAP; + npc_cn20k_enable_mcam_entry(rvu, blkaddr, ptr[i], false); + mcam->entry2target_pffunc[ptr[i]] = 0x0; + mutex_unlock(&mcam->lock); + + rc = npc_cn20k_idx_free(rvu, &ptr[i], 1); + if (rc) { + /* Non recoverable error. Let us WARN and return. Keep system alive to + * enable debugging + */ + WARN(1, "%s Error deleting default entries (pcifunc=%#x) mcam_idx=%u\n", + __func__, pcifunc, ptr[i]); + return; + } + } + + mutex_lock(&mcam->lock); + list_for_each_entry_safe(rule, tmp, &mcam->mcam_rules, list) { + for (int i = 0; i < 4; i++) { + if (ptr[i] != rule->entry) + continue; + + list_del(&rule->list); + kfree(rule); + break; + } + } + mutex_unlock(&mcam->lock); } int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h index 815d0b257a7e..3d5eb952cc07 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h @@ -320,21 +320,21 @@ void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc); int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, u16 *mcast, u16 *promisc, u16 *ucast); -void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index, - u8 intf, struct cn20k_mcam_entry *entry, - bool enable, u8 hw_prio, u8 req_kw_type); -void npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, - int index, bool enable); -void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, - u16 src, u16 dest); -void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index, - struct cn20k_mcam_entry *entry, u8 *intf, - u8 *ena, u8 *hw_prio); -void npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, - int bank, int index); +int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index, + u8 intf, struct cn20k_mcam_entry *entry, + bool enable, u8 hw_prio, u8 req_kw_type); +int npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, + int index, bool enable); +int npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, + u16 src, u16 dest); +int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index, + struct cn20k_mcam_entry *entry, u8 *intf, + u8 *ena, u8 *hw_prio); +int npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int index); int npc_mcam_idx_2_key_type(struct rvu *rvu, u16 mcam_idx, u8 *key_type); u16 npc_cn20k_vidx2idx(u16 index); u16 npc_cn20k_idx2vidx(u16 idx); int npc_cn20k_defrag(struct rvu *rvu); +bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc); #endif /* NPC_CN20K_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index ef5b081162eb..f977734ae712 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -3577,6 +3577,9 @@ static int nix_update_mce_rule(struct rvu *rvu, u16 pcifunc, mcam_index = npc_get_nixlf_mcam_index(mcam, pcifunc & ~RVU_PFVF_FUNC_MASK, nixlf, type); + if (mcam_index < 0) + return -EINVAL; + err = nix_update_mce_list(rvu, pcifunc, mce_list, mce_idx, mcam_index, add); return err; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index c2ca5ed1d028..3c814d157ab9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -163,14 +163,35 @@ int npc_get_nixlf_mcam_index(struct npc_mcam *mcam, if (rc) return -EFAULT; + if (is_lbk_vf(rvu, pcifunc)) { + if (promisc == USHRT_MAX) + return -EINVAL; + return promisc; + } + + if (is_cgx_vf(rvu, pcifunc)) { + if (ucast == USHRT_MAX) + return -EINVAL; + + return ucast; + } + switch (type) { case NIXLF_BCAST_ENTRY: + if (bcast == USHRT_MAX) + return -EINVAL; return bcast; case NIXLF_ALLMULTI_ENTRY: + if (mcast == USHRT_MAX) + return -EINVAL; return mcast; case NIXLF_PROMISC_ENTRY: + if (promisc == USHRT_MAX) + return -EINVAL; return promisc; case NIXLF_UCAST_ENTRY: + if (ucast == USHRT_MAX) + return -EINVAL; return ucast; default: return -EINVAL; @@ -238,10 +259,10 @@ void npc_enable_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, int actbank = bank; if (is_cn20k(rvu->pdev)) { - if (index < 0 || index >= mcam->banksize * mcam->banks) - return; - - return npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable); + if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable)) + dev_err(rvu->dev, "Error to %s mcam %u entry\n", + enable ? "enable" : "disable", index); + return; } index &= (mcam->banksize - 1); @@ -258,6 +279,13 @@ static void npc_clear_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, int bank = npc_get_bank(mcam, index); int actbank = bank; + if (is_cn20k(rvu->pdev)) { + if (npc_cn20k_clear_mcam_entry(rvu, blkaddr, index)) + dev_err(rvu->dev, "%s Failed to clear mcam %u\n", + __func__, index); + return; + } + index &= (mcam->banksize - 1); for (; bank < (actbank + mcam->banks_per_entry); bank++) { rvu_write64(rvu, blkaddr, @@ -424,6 +452,15 @@ static u64 npc_get_default_entry_action(struct rvu *rvu, struct npc_mcam *mcam, index = npc_get_nixlf_mcam_index(mcam, pf_func, nixlf, NIXLF_UCAST_ENTRY); + + if (index < 0) { + dev_err(rvu->dev, + "%s: failed to get ucast entry pcifunc:0x%x\n", + __func__, pf_func); + /* Action 0 is drop */ + return 0; + } + bank = npc_get_bank(mcam, index); index &= (mcam->banksize - 1); @@ -589,8 +626,8 @@ void npc_read_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, NPC_AF_MCAMEX_BANKX_CFG(src, sbank)) & 1; } -static void npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, - int blkaddr, u16 src, u16 dest) +static int npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, + int blkaddr, u16 src, u16 dest) { int dbank = npc_get_bank(mcam, dest); int sbank = npc_get_bank(mcam, src); @@ -630,6 +667,7 @@ static void npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, NPC_AF_MCAMEX_BANKX_CFG(src, sbank)); rvu_write64(rvu, blkaddr, NPC_AF_MCAMEX_BANKX_CFG(dest, dbank), cfg); + return 0; } u64 npc_get_mcam_action(struct rvu *rvu, struct npc_mcam *mcam, @@ -689,6 +727,12 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc, index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get ucast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } /* Don't change the action if entry is already enabled * Otherwise RSS action may get overwritten. @@ -744,16 +788,38 @@ void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc, index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_PROMISC_ENTRY); + /* In cn20k, default indexes are installed only for CGX mapped + * and lbk interfaces + */ if (is_cgx_vf(rvu, pcifunc)) index = npc_get_nixlf_mcam_index(mcam, pcifunc & ~RVU_PFVF_FUNC_MASK, nixlf, NIXLF_PROMISC_ENTRY); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get promisc entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } + /* If the corresponding PF's ucast action is RSS, * use the same action for promisc also + * Please note that for lbk(s) "index" and "ucast_idx" + * will be same. */ - ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc, - nixlf, NIXLF_UCAST_ENTRY); + if (is_lbk_vf(rvu, pcifunc)) + ucast_idx = index; + else + ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc, + nixlf, NIXLF_UCAST_ENTRY); + if (ucast_idx < 0) { + dev_err(rvu->dev, + "%s: Error to get ucast/promisc entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } + if (is_mcam_entry_enabled(rvu, mcam, blkaddr, ucast_idx)) *(u64 *)&action = npc_get_mcam_action(rvu, mcam, blkaddr, ucast_idx); @@ -827,6 +893,14 @@ void rvu_npc_enable_promisc_entry(struct rvu *rvu, u16 pcifunc, index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_PROMISC_ENTRY); + + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get promisc entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } + npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable); } @@ -867,6 +941,12 @@ void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc, index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_BCAST_ENTRY); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get bcast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } if (!hw->cap.nix_rx_multicast) { /* Early silicon doesn't support pkt replication, @@ -931,12 +1011,25 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_ALLMULTI_ENTRY); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get mcast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } /* If the corresponding PF's ucast action is RSS, * use the same action for multicast entry also */ ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); + if (ucast_idx < 0) { + dev_err(rvu->dev, + "%s: Error to get ucast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } + if (is_mcam_entry_enabled(rvu, mcam, blkaddr, ucast_idx)) *(u64 *)&action = npc_get_mcam_action(rvu, mcam, blkaddr, ucast_idx); @@ -1001,6 +1094,13 @@ void rvu_npc_enable_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_ALLMULTI_ENTRY); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get mcast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } + npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable); } @@ -1113,8 +1213,12 @@ void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf, index = mcam_index; } - if (index >= mcam->total_entries) + if (index < 0 || index >= mcam->total_entries) { + dev_err(rvu->dev, + "%s: Invalid mcam index, pcifunc=%#x\n", + __func__, pcifunc); return; + } bank = npc_get_bank(mcam, index); index &= (mcam->banksize - 1); @@ -1158,16 +1262,18 @@ void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf, /* If PF's promiscuous entry is enabled, * Set RSS action for that entry as well */ - npc_update_rx_action_with_alg_idx(rvu, action, pfvf, index, - blkaddr, alg_idx); + if (index >= 0) + npc_update_rx_action_with_alg_idx(rvu, action, pfvf, index, + blkaddr, alg_idx); index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_ALLMULTI_ENTRY); /* If PF's allmulti entry is enabled, * Set RSS action for that entry as well */ - npc_update_rx_action_with_alg_idx(rvu, action, pfvf, index, - blkaddr, alg_idx); + if (index >= 0) + npc_update_rx_action_with_alg_idx(rvu, action, pfvf, index, + blkaddr, alg_idx); } } @@ -1180,12 +1286,22 @@ void npc_enadis_default_mce_entry(struct rvu *rvu, u16 pcifunc, int index, blkaddr, mce_idx; struct rvu_pfvf *pfvf; + /* multicast pkt replication is not enabled for AF's VFs & SDP links */ + if (is_lbk_vf(rvu, pcifunc) || is_sdp_pfvf(rvu, pcifunc)) + return; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); if (blkaddr < 0) return; index = npc_get_nixlf_mcam_index(mcam, pcifunc & ~RVU_PFVF_FUNC_MASK, nixlf, type); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get entry for pcifunc=%#x, type=%u\n", + __func__, pcifunc, type); + return; + } /* disable MCAM entry when packet replication is not supported by hw */ if (!hw->cap.nix_rx_multicast && !is_vf(pcifunc)) { @@ -1214,6 +1330,10 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc, struct npc_mcam *mcam = &rvu->hw->mcam; int index, blkaddr; + /* only CGX or LBK interfaces have default entries */ + if (is_cn20k(rvu->pdev) && !npc_is_cgx_or_lbk(rvu, pcifunc)) + return; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); if (blkaddr < 0) return; @@ -1223,6 +1343,12 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc, pfvf->nix_rx_intf)) { index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get ucast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable); } @@ -2504,33 +2630,58 @@ void npc_mcam_clear_bit(struct npc_mcam *mcam, u16 index) static void npc_mcam_free_all_entries(struct rvu *rvu, struct npc_mcam *mcam, int blkaddr, u16 pcifunc) { + u16 dft_idxs[NPC_DFT_RULE_MAX_ID] = {[0 ... NPC_DFT_RULE_MAX_ID - 1] = USHRT_MAX}; + bool cn20k_dft_rl; u16 index, cntr; int rc; + npc_cn20k_dft_rules_idx_get(rvu, pcifunc, + &dft_idxs[NPC_DFT_RULE_BCAST_ID], + &dft_idxs[NPC_DFT_RULE_MCAST_ID], + &dft_idxs[NPC_DFT_RULE_PROMISC_ID], + &dft_idxs[NPC_DFT_RULE_UCAST_ID]); + /* Scan all MCAM entries and free the ones mapped to 'pcifunc' */ for (index = 0; index < mcam->bmap_entries; index++) { - if (mcam->entry2pfvf_map[index] == pcifunc) { + if (mcam->entry2pfvf_map[index] != pcifunc) + continue; + + cn20k_dft_rl = false; + + if (is_cn20k(rvu->pdev)) { + if (dft_idxs[NPC_DFT_RULE_BCAST_ID] == index || + dft_idxs[NPC_DFT_RULE_MCAST_ID] == index || + dft_idxs[NPC_DFT_RULE_PROMISC_ID] == index || + dft_idxs[NPC_DFT_RULE_UCAST_ID] == index) { + cn20k_dft_rl = true; + } + } + + /* Disable the entry */ + npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false); + + if (!cn20k_dft_rl) { mcam->entry2pfvf_map[index] = NPC_MCAM_INVALID_MAP; /* Free the entry in bitmap */ npc_mcam_clear_bit(mcam, index); - /* Disable the entry */ - npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false); - - /* Update entry2counter mapping */ - cntr = mcam->entry2cntr_map[index]; - if (cntr != NPC_MCAM_INVALID_MAP) - npc_unmap_mcam_entry_and_cntr(rvu, mcam, - blkaddr, index, - cntr); mcam->entry2target_pffunc[index] = 0x0; - if (is_cn20k(rvu->pdev)) { - rc = npc_cn20k_idx_free(rvu, &index, 1); - if (rc) - dev_err(rvu->dev, - "Failed to free mcam idx=%u pcifunc=%#x\n", - index, pcifunc); - } } + + /* Update entry2counter mapping */ + cntr = mcam->entry2cntr_map[index]; + if (cntr != NPC_MCAM_INVALID_MAP) + npc_unmap_mcam_entry_and_cntr(rvu, mcam, + blkaddr, index, + cntr); + + if (!is_cn20k(rvu->pdev) || cn20k_dft_rl) + continue; + + rc = npc_cn20k_idx_free(rvu, &index, 1); + if (rc) + dev_err(rvu->dev, + "Failed to free mcam idx=%u pcifunc=%#x\n", + index, pcifunc); } } @@ -3266,7 +3417,10 @@ int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu, npc_enable_mcam_entry(rvu, mcam, blkaddr, new_entry, false); /* Copy rule from old entry to new entry */ - npc_copy_mcam_entry(rvu, mcam, blkaddr, old_entry, new_entry); + if (npc_copy_mcam_entry(rvu, mcam, blkaddr, old_entry, new_entry)) { + rc = NPC_MCAM_INVALID_REQ; + break; + } /* Copy counter mapping, if any */ cntr = mcam->entry2cntr_map[old_entry]; @@ -3284,7 +3438,8 @@ int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu, /* If shift has failed then report the failed index */ if (index != req->shift_count) { - rc = NPC_MCAM_PERM_DENIED; + if (!rc) + rc = NPC_MCAM_PERM_DENIED; rsp->failed_entry_idx = index; } @@ -3851,6 +4006,12 @@ int rvu_mbox_handler_npc_read_base_steer_rule(struct rvu *rvu, /* Read the default ucast entry if there is no pkt steering rule */ index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); + if (index < 0) { + mutex_unlock(&mcam->lock); + rc = NIX_AF_ERR_AF_LF_INVALID; + goto out; + } + read_entry: /* Read the mcam entry */ npc_read_mcam_entry(rvu, mcam, blkaddr, index, &rsp->entry, &intf, @@ -3924,6 +4085,12 @@ void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf) ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); + if (ucast_idx < 0) { + dev_err(rvu->dev, + "%s: Error to get ucast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } npc_enable_mcam_entry(rvu, mcam, blkaddr, ucast_idx, false); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index b45798d9fdab..6ae9cdcb608b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -1444,7 +1444,7 @@ static int npc_install_flow(struct rvu *rvu, int blkaddr, u16 target, struct msg_rsp write_rsp; struct mcam_entry *entry; bool new = false; - u16 entry_index; + int entry_index; int err; installed_features = req->features; @@ -1477,6 +1477,14 @@ static int npc_install_flow(struct rvu *rvu, int blkaddr, u16 target, if (req->default_rule) { entry_index = npc_get_nixlf_mcam_index(mcam, target, nixlf, NIXLF_UCAST_ENTRY); + + if (entry_index < 0) { + dev_err(rvu->dev, + "%s: Error to get ucast entry for target=%#x\n", + __func__, target); + return -EINVAL; + } + enable = is_mcam_entry_enabled(rvu, mcam, blkaddr, entry_index); } @@ -1980,13 +1988,15 @@ static int npc_update_dmac_value(struct rvu *rvu, int npcblkaddr, ether_addr_copy(rule->packet.dmac, pfvf->mac_addr); - if (is_cn20k(rvu->pdev)) - npc_cn20k_read_mcam_entry(rvu, npcblkaddr, rule->entry, - cn20k_entry, &intf, - &enable, &hw_prio); - else + if (is_cn20k(rvu->pdev)) { + if (npc_cn20k_read_mcam_entry(rvu, npcblkaddr, rule->entry, + cn20k_entry, &intf, + &enable, &hw_prio)) + return -EINVAL; + } else { npc_read_mcam_entry(rvu, mcam, npcblkaddr, rule->entry, entry, &intf, &enable); + } npc_update_entry(rvu, NPC_DMAC, &mdata, ether_addr_to_u64(pfvf->mac_addr), 0, @@ -2038,8 +2048,12 @@ void npc_mcam_enable_flows(struct rvu *rvu, u16 target) continue; } - if (rule->vfvlan_cfg) - npc_update_dmac_value(rvu, blkaddr, rule, pfvf); + if (rule->vfvlan_cfg) { + if (npc_update_dmac_value(rvu, blkaddr, rule, pfvf)) + dev_err(rvu->dev, + "Update dmac failed for %u, target=%#x\n", + rule->entry, target); + } if (rule->rx_action.op == NIX_RX_ACTION_DEFAULT) { if (!def_ucast_rule) diff --git a/drivers/net/ethernet/mellanox/mlx4/srq.c b/drivers/net/ethernet/mellanox/mlx4/srq.c index dd890f5d7b72..8711689120f3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/srq.c +++ b/drivers/net/ethernet/mellanox/mlx4/srq.c @@ -44,13 +44,14 @@ void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type) { struct mlx4_srq_table *srq_table = &mlx4_priv(dev)->srq_table; struct mlx4_srq *srq; + unsigned long flags; - rcu_read_lock(); + spin_lock_irqsave(&srq_table->lock, flags); srq = radix_tree_lookup(&srq_table->tree, srqn & (dev->caps.num_srqs - 1)); - rcu_read_unlock(); - if (srq) - refcount_inc(&srq->refcount); - else { + if (!srq || !refcount_inc_not_zero(&srq->refcount)) + srq = NULL; + spin_unlock_irqrestore(&srq_table->lock, flags); + if (!srq) { mlx4_warn(dev, "Async event for bogus SRQ %08x\n", srqn); return; } @@ -203,8 +204,8 @@ int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, u32 cqn, u16 xrcd, if (err) goto err_radix; - refcount_set(&srq->refcount, 1); init_completion(&srq->free); + refcount_set_release(&srq->refcount, 1); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h index c3408b3f7010..091b80a67189 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h @@ -201,7 +201,10 @@ int mlx5e_add_vlan_trap(struct mlx5e_flow_steering *fs, int trap_id, int tir_nu void mlx5e_remove_vlan_trap(struct mlx5e_flow_steering *fs); int mlx5e_add_mac_trap(struct mlx5e_flow_steering *fs, int trap_id, int tir_num); void mlx5e_remove_mac_trap(struct mlx5e_flow_steering *fs); -void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs, struct net_device *netdev); +void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs, + struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc); int mlx5e_fs_vlan_rx_add_vid(struct mlx5e_flow_steering *fs, struct net_device *netdev, __be16 proto, u16 vid); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c index 6a50b6dec0fa..d9adb993e64d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c @@ -1070,29 +1070,37 @@ static struct psp_dev_ops mlx5_psp_ops = { void mlx5e_psp_unregister(struct mlx5e_priv *priv) { - if (!priv->psp || !priv->psp->psp) + struct mlx5e_psp *psp = priv->psp; + + if (!psp || !psp->psp) return; - psp_dev_unregister(priv->psp->psp); + psp_dev_unregister(psp->psp); + psp->psp = NULL; } void mlx5e_psp_register(struct mlx5e_priv *priv) { + struct mlx5e_psp *psp = priv->psp; + struct psp_dev *psd; + /* FW Caps missing */ if (!priv->psp) return; - priv->psp->caps.assoc_drv_spc = sizeof(u32); - priv->psp->caps.versions = 1 << PSP_VERSION_HDR0_AES_GCM_128; + psp->caps.assoc_drv_spc = sizeof(u32); + psp->caps.versions = 1 << PSP_VERSION_HDR0_AES_GCM_128; if (MLX5_CAP_PSP(priv->mdev, psp_crypto_esp_aes_gcm_256_encrypt) && MLX5_CAP_PSP(priv->mdev, psp_crypto_esp_aes_gcm_256_decrypt)) - priv->psp->caps.versions |= 1 << PSP_VERSION_HDR0_AES_GCM_256; + psp->caps.versions |= 1 << PSP_VERSION_HDR0_AES_GCM_256; - priv->psp->psp = psp_dev_create(priv->netdev, &mlx5_psp_ops, - &priv->psp->caps, NULL); - if (IS_ERR(priv->psp->psp)) + psd = psp_dev_create(priv->netdev, &mlx5_psp_ops, &psp->caps, NULL); + if (IS_ERR(psd)) { mlx5_core_err(priv->mdev, "PSP failed to register due to %pe\n", - priv->psp->psp); + psd); + return; + } + psp->psp = psd; } int mlx5e_psp_init(struct mlx5e_priv *priv) @@ -1131,22 +1139,18 @@ int mlx5e_psp_init(struct mlx5e_priv *priv) if (!psp) return -ENOMEM; - priv->psp = psp; fs = mlx5e_accel_psp_fs_init(priv); if (IS_ERR(fs)) { err = PTR_ERR(fs); - goto out_err; + kfree(psp); + return err; } psp->fs = fs; + priv->psp = psp; mlx5_core_dbg(priv->mdev, "PSP attached to netdevice\n"); return 0; - -out_err: - priv->psp = NULL; - kfree(psp); - return err; } void mlx5e_psp_cleanup(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index fdfe9d1cfe21..12492c4a5d41 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -609,20 +609,26 @@ static void mlx5e_execute_l2_action(struct mlx5e_flow_steering *fs, } static void mlx5e_sync_netdev_addr(struct mlx5e_flow_steering *fs, - struct net_device *netdev) + struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct netdev_hw_addr *ha; - netif_addr_lock_bh(netdev); + if (!uc || !mc) { + netif_addr_lock_bh(netdev); + mlx5e_sync_netdev_addr(fs, netdev, &netdev->uc, &netdev->mc); + netif_addr_unlock_bh(netdev); + return; + } mlx5e_add_l2_to_hash(fs->l2.netdev_uc, netdev->dev_addr); - netdev_for_each_uc_addr(ha, netdev) + + netdev_hw_addr_list_for_each(ha, uc) mlx5e_add_l2_to_hash(fs->l2.netdev_uc, ha->addr); - netdev_for_each_mc_addr(ha, netdev) + netdev_hw_addr_list_for_each(ha, mc) mlx5e_add_l2_to_hash(fs->l2.netdev_mc, ha->addr); - - netif_addr_unlock_bh(netdev); } static void mlx5e_fill_addr_array(struct mlx5e_flow_steering *fs, int list_type, @@ -724,7 +730,9 @@ static void mlx5e_apply_netdev_addr(struct mlx5e_flow_steering *fs) } static void mlx5e_handle_netdev_addr(struct mlx5e_flow_steering *fs, - struct net_device *netdev) + struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct mlx5e_l2_hash_node *hn; struct hlist_node *tmp; @@ -736,7 +744,7 @@ static void mlx5e_handle_netdev_addr(struct mlx5e_flow_steering *fs, hn->action = MLX5E_ACTION_DEL; if (fs->state_destroy) - mlx5e_sync_netdev_addr(fs, netdev); + mlx5e_sync_netdev_addr(fs, netdev, uc, mc); mlx5e_apply_netdev_addr(fs); } @@ -820,13 +828,15 @@ static void mlx5e_destroy_promisc_table(struct mlx5e_flow_steering *fs) } void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs, - struct net_device *netdev) + struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5e_l2_table *ea = &fs->l2; if (mlx5e_is_uplink_rep(priv)) { - mlx5e_handle_netdev_addr(fs, netdev); + mlx5e_handle_netdev_addr(fs, netdev, uc, mc); goto update_vport_context; } @@ -856,7 +866,7 @@ void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs, if (enable_broadcast) mlx5e_add_l2_flow_rule(fs, &ea->broadcast, MLX5E_FULLMATCH); - mlx5e_handle_netdev_addr(fs, netdev); + mlx5e_handle_netdev_addr(fs, netdev, uc, mc); if (disable_broadcast) mlx5e_del_l2_flow_rule(fs, &ea->broadcast); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 6c4eeb88588c..8f2b3abe0092 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -4145,11 +4145,13 @@ static void mlx5e_nic_set_rx_mode(struct mlx5e_priv *priv) queue_work(priv->wq, &priv->set_rx_mode_work); } -static void mlx5e_set_rx_mode(struct net_device *dev) +static void mlx5e_set_rx_mode(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct mlx5e_priv *priv = netdev_priv(dev); - mlx5e_nic_set_rx_mode(priv); + mlx5e_fs_set_rx_mode_work(priv->fs, dev, uc, mc); } static int mlx5e_set_mac(struct net_device *netdev, void *addr) @@ -5324,7 +5326,7 @@ const struct net_device_ops mlx5e_netdev_ops = { .ndo_setup_tc = mlx5e_setup_tc, .ndo_select_queue = mlx5e_select_queue, .ndo_get_stats64 = mlx5e_get_stats, - .ndo_set_rx_mode = mlx5e_set_rx_mode, + .ndo_set_rx_mode_async = mlx5e_set_rx_mode, .ndo_set_mac_address = mlx5e_set_mac, .ndo_vlan_rx_add_vid = mlx5e_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = mlx5e_vlan_rx_kill_vid, @@ -6021,7 +6023,6 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev, if (take_rtnl) rtnl_lock(); - mlx5e_psp_register(priv); /* update XDP supported features */ mlx5e_set_xdp_feature(priv); @@ -6034,7 +6035,6 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev, static void mlx5e_nic_cleanup(struct mlx5e_priv *priv) { mlx5e_health_destroy_reporters(priv); - mlx5e_psp_unregister(priv); mlx5e_ktls_cleanup(priv); mlx5e_psp_cleanup(priv); mlx5e_fs_cleanup(priv->fs); @@ -6158,6 +6158,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) mlx5e_fs_init_l2_addr(priv->fs, netdev); mlx5e_ipsec_init(priv); + mlx5e_psp_register(priv); err = mlx5e_macsec_init(priv); if (err) @@ -6228,6 +6229,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv) mlx5_lag_remove_netdev(mdev, priv->netdev); mlx5_vxlan_reset_to_default(mdev->vxlan); mlx5e_macsec_cleanup(priv); + mlx5e_psp_unregister(priv); mlx5e_ipsec_cleanup(priv); } @@ -6309,8 +6311,11 @@ void mlx5e_set_rx_mode_work(struct work_struct *work) { struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv, set_rx_mode_work); + struct net_device *dev = priv->netdev; - return mlx5e_fs_set_rx_mode_work(priv->fs, priv->netdev); + netdev_lock_ops(dev); + mlx5e_fs_set_rx_mode_work(priv->fs, dev, NULL, NULL); + netdev_unlock_ops(dev); } /* mlx5e generic netdev management API (move to en_common.c) */ @@ -6769,9 +6774,11 @@ static int mlx5e_resume(struct auxiliary_device *adev) return err; actual_adev = mlx5_sd_get_adev(mdev, adev, edev->idx); - if (actual_adev) - return _mlx5e_resume(actual_adev); - return 0; + if (actual_adev) { + err = _mlx5e_resume(actual_adev); + mlx5_sd_put_adev(actual_adev, adev); + } + return err; } static int _mlx5e_suspend(struct auxiliary_device *adev, bool pre_netdev_reg) @@ -6810,6 +6817,8 @@ static int mlx5e_suspend(struct auxiliary_device *adev, pm_message_t state) err = _mlx5e_suspend(actual_adev, false); mlx5_sd_cleanup(mdev); + if (actual_adev) + mlx5_sd_put_adev(actual_adev, adev); return err; } @@ -6907,9 +6916,19 @@ static int mlx5e_probe(struct auxiliary_device *adev, return err; actual_adev = mlx5_sd_get_adev(mdev, adev, edev->idx); - if (actual_adev) - return _mlx5e_probe(actual_adev); + if (actual_adev) { + err = _mlx5e_probe(actual_adev); + if (err) + goto sd_cleanup; + mlx5_sd_put_adev(actual_adev, adev); + } return 0; + +sd_cleanup: + mlx5_sd_cleanup(mdev); + if (actual_adev) + mlx5_sd_put_adev(actual_adev, adev); + return err; } static void _mlx5e_remove(struct auxiliary_device *adev) @@ -6961,6 +6980,8 @@ static void mlx5e_remove(struct auxiliary_device *adev) _mlx5e_remove(actual_adev); mlx5_sd_cleanup(mdev); + if (actual_adev) + mlx5_sd_put_adev(actual_adev, adev); } static const struct auxiliary_device_id mlx5e_id_table[] = { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c index 762c783156b4..6e199161b008 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c @@ -18,6 +18,7 @@ struct mlx5_sd { u8 host_buses; struct mlx5_devcom_comp_dev *devcom; struct dentry *dfs; + u8 state; bool primary; union { struct { /* primary */ @@ -31,6 +32,11 @@ struct mlx5_sd { }; }; +enum mlx5_sd_state { + MLX5_SD_STATE_DOWN = 0, + MLX5_SD_STATE_UP, +}; + static int mlx5_sd_get_host_buses(struct mlx5_core_dev *dev) { struct mlx5_sd *sd = mlx5_get_sd(dev); @@ -270,9 +276,6 @@ static void sd_unregister(struct mlx5_core_dev *dev) { struct mlx5_sd *sd = mlx5_get_sd(dev); - mlx5_devcom_comp_lock(sd->devcom); - mlx5_devcom_comp_set_ready(sd->devcom, false); - mlx5_devcom_comp_unlock(sd->devcom); mlx5_devcom_unregister_component(sd->devcom); } @@ -426,6 +429,7 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) struct mlx5_core_dev *primary, *pos, *to; struct mlx5_sd *sd = mlx5_get_sd(dev); u8 alias_key[ACCESS_KEY_LEN]; + struct mlx5_sd *primary_sd; int err, i; err = sd_init(dev); @@ -440,10 +444,17 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) if (err) goto err_sd_cleanup; + mlx5_devcom_comp_lock(sd->devcom); if (!mlx5_devcom_comp_is_ready(sd->devcom)) - return 0; + goto out; primary = mlx5_sd_get_primary(dev); + if (!primary) + goto out; + + primary_sd = mlx5_get_sd(primary); + if (primary_sd->state != MLX5_SD_STATE_DOWN) + goto out; for (i = 0; i < ACCESS_KEY_LEN; i++) alias_key[i] = get_random_u8(); @@ -452,9 +463,13 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) if (err) goto err_sd_unregister; - sd->dfs = debugfs_create_dir("multi-pf", mlx5_debugfs_get_dev_root(primary)); - debugfs_create_x32("group_id", 0400, sd->dfs, &sd->group_id); - debugfs_create_file("primary", 0400, sd->dfs, primary, &dev_fops); + primary_sd->dfs = + debugfs_create_dir("multi-pf", + mlx5_debugfs_get_dev_root(primary)); + debugfs_create_x32("group_id", 0400, primary_sd->dfs, + &primary_sd->group_id); + debugfs_create_file("primary", 0400, primary_sd->dfs, primary, + &dev_fops); mlx5_sd_for_each_secondary(i, primary, pos) { char name[32]; @@ -464,7 +479,8 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) goto err_unset_secondaries; snprintf(name, sizeof(name), "secondary_%d", i - 1); - debugfs_create_file(name, 0400, sd->dfs, pos, &dev_fops); + debugfs_create_file(name, 0400, primary_sd->dfs, pos, + &dev_fops); } @@ -472,6 +488,9 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) sd->group_id, mlx5_devcom_comp_get_size(sd->devcom)); sd_print_group(primary); + primary_sd->state = MLX5_SD_STATE_UP; +out: + mlx5_devcom_comp_unlock(sd->devcom); return 0; err_unset_secondaries: @@ -479,8 +498,18 @@ err_unset_secondaries: mlx5_sd_for_each_secondary_to(i, primary, to, pos) sd_cmd_unset_secondary(pos); sd_cmd_unset_primary(primary); - debugfs_remove_recursive(sd->dfs); + debugfs_remove_recursive(primary_sd->dfs); + primary_sd->dfs = NULL; err_sd_unregister: + mlx5_sd_for_each_secondary(i, primary, pos) { + struct mlx5_sd *peer_sd = mlx5_get_sd(pos); + + primary_sd->secondaries[i - 1] = NULL; + peer_sd->primary_dev = NULL; + } + primary_sd->primary = false; + mlx5_devcom_comp_set_ready(sd->devcom, false); + mlx5_devcom_comp_unlock(sd->devcom); sd_unregister(dev); err_sd_cleanup: sd_cleanup(dev); @@ -491,42 +520,97 @@ void mlx5_sd_cleanup(struct mlx5_core_dev *dev) { struct mlx5_sd *sd = mlx5_get_sd(dev); struct mlx5_core_dev *primary, *pos; + struct mlx5_sd *primary_sd; int i; if (!sd) return; + mlx5_devcom_comp_lock(sd->devcom); if (!mlx5_devcom_comp_is_ready(sd->devcom)) - goto out; + goto out_unlock; primary = mlx5_sd_get_primary(dev); + if (!primary) + goto out_ready_false; + + primary_sd = mlx5_get_sd(primary); + if (primary_sd->state != MLX5_SD_STATE_UP) + goto out_clear_peers; + mlx5_sd_for_each_secondary(i, primary, pos) sd_cmd_unset_secondary(pos); sd_cmd_unset_primary(primary); - debugfs_remove_recursive(sd->dfs); + debugfs_remove_recursive(primary_sd->dfs); + primary_sd->dfs = NULL; sd_info(primary, "group id %#x, uncombined\n", sd->group_id); -out: + primary_sd->state = MLX5_SD_STATE_DOWN; +out_clear_peers: + mlx5_sd_for_each_secondary(i, primary, pos) { + struct mlx5_sd *peer_sd = mlx5_get_sd(pos); + + primary_sd->secondaries[i - 1] = NULL; + peer_sd->primary_dev = NULL; + } + primary_sd->primary = false; +out_ready_false: + mlx5_devcom_comp_set_ready(sd->devcom, false); +out_unlock: + mlx5_devcom_comp_unlock(sd->devcom); sd_unregister(dev); sd_cleanup(dev); } +/* Lock order: + * primary: actual_adev_lock -> SD devcom comp lock + * secondary: SD devcom comp lock -> (drop) -> actual_adev_lock + * The two locks are never held together, so no ABBA. + */ struct auxiliary_device *mlx5_sd_get_adev(struct mlx5_core_dev *dev, struct auxiliary_device *adev, int idx) { struct mlx5_sd *sd = mlx5_get_sd(dev); struct mlx5_core_dev *primary; + struct mlx5_adev *primary_adev; if (!sd) return adev; - if (!mlx5_devcom_comp_is_ready(sd->devcom)) + mlx5_devcom_comp_lock(sd->devcom); + if (!mlx5_devcom_comp_is_ready(sd->devcom)) { + mlx5_devcom_comp_unlock(sd->devcom); return NULL; + } primary = mlx5_sd_get_primary(dev); - if (dev == primary) + if (!primary || dev == primary) { + mlx5_devcom_comp_unlock(sd->devcom); return adev; + } - return &primary->priv.adev[idx]->adev; + primary_adev = primary->priv.adev[idx]; + get_device(&primary_adev->adev.dev); + mlx5_devcom_comp_unlock(sd->devcom); + + device_lock(&primary_adev->adev.dev); + /* Primary may have completed remove between dropping devcom and + * acquiring device_lock; recheck. + */ + if (!mlx5_devcom_comp_is_ready(sd->devcom)) { + device_unlock(&primary_adev->adev.dev); + put_device(&primary_adev->adev.dev); + return NULL; + } + return &primary_adev->adev; +} + +void mlx5_sd_put_adev(struct auxiliary_device *actual_adev, + struct auxiliary_device *adev) +{ + if (actual_adev != adev) { + device_unlock(&actual_adev->dev); + put_device(&actual_adev->dev); + } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h index 137efaf9aabc..9bfd5b9756b5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h @@ -15,6 +15,8 @@ struct mlx5_core_dev *mlx5_sd_ch_ix_get_dev(struct mlx5_core_dev *primary, int c struct auxiliary_device *mlx5_sd_get_adev(struct mlx5_core_dev *dev, struct auxiliary_device *adev, int idx); +void mlx5_sd_put_adev(struct auxiliary_device *actual_adev, + struct auxiliary_device *adev); int mlx5_sd_init(struct mlx5_core_dev *dev); void mlx5_sd_cleanup(struct mlx5_core_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 8761b9deed1c..74827e8ca125 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -111,74 +111,9 @@ static struct mlx5_profile profile[] = { }, [2] = { - .mask = MLX5_PROF_MASK_QP_SIZE | - MLX5_PROF_MASK_MR_CACHE, + .mask = MLX5_PROF_MASK_QP_SIZE, .log_max_qp = LOG_MAX_SUPPORTED_QPS, .num_cmd_caches = MLX5_NUM_COMMAND_CACHES, - .mr_cache[0] = { - .size = 500, - .limit = 250 - }, - .mr_cache[1] = { - .size = 500, - .limit = 250 - }, - .mr_cache[2] = { - .size = 500, - .limit = 250 - }, - .mr_cache[3] = { - .size = 500, - .limit = 250 - }, - .mr_cache[4] = { - .size = 500, - .limit = 250 - }, - .mr_cache[5] = { - .size = 500, - .limit = 250 - }, - .mr_cache[6] = { - .size = 500, - .limit = 250 - }, - .mr_cache[7] = { - .size = 500, - .limit = 250 - }, - .mr_cache[8] = { - .size = 500, - .limit = 250 - }, - .mr_cache[9] = { - .size = 500, - .limit = 250 - }, - .mr_cache[10] = { - .size = 500, - .limit = 250 - }, - .mr_cache[11] = { - .size = 500, - .limit = 250 - }, - .mr_cache[12] = { - .size = 64, - .limit = 32 - }, - .mr_cache[13] = { - .size = 32, - .limit = 16 - }, - .mr_cache[14] = { - .size = 16, - .limit = 8 - }, - .mr_cache[15] = { - .size = 8, - .limit = 4 - }, }, [3] = { .mask = MLX5_PROF_MASK_QP_SIZE, @@ -1914,7 +1849,7 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) err = mlx5_notifiers_init(dev); if (err) - goto err_hca_caps; + goto err_notifiers_init; /* The conjunction of sw_vhca_id with sw_owner_id will be a global * unique id per function which uses mlx5_core. @@ -1930,6 +1865,8 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) return 0; +err_notifiers_init: + mlx5_hca_caps_free(dev); err_hca_caps: mlx5_adev_cleanup(dev); err_adev_init: diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c index b4b396ca9bce..4dea2bb58d2f 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c @@ -183,7 +183,9 @@ static int fbnic_mc_unsync(struct net_device *netdev, const unsigned char *addr) return ret; } -void __fbnic_set_rx_mode(struct fbnic_dev *fbd) +void __fbnic_set_rx_mode(struct fbnic_dev *fbd, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { bool uc_promisc = false, mc_promisc = false; struct net_device *netdev = fbd->netdev; @@ -213,10 +215,10 @@ void __fbnic_set_rx_mode(struct fbnic_dev *fbd) } /* Synchronize unicast and multicast address lists */ - err = __dev_uc_sync(netdev, fbnic_uc_sync, fbnic_uc_unsync); + err = __hw_addr_sync_dev(uc, netdev, fbnic_uc_sync, fbnic_uc_unsync); if (err == -ENOSPC) uc_promisc = true; - err = __dev_mc_sync(netdev, fbnic_mc_sync, fbnic_mc_unsync); + err = __hw_addr_sync_dev(mc, netdev, fbnic_mc_sync, fbnic_mc_unsync); if (err == -ENOSPC) mc_promisc = true; @@ -238,18 +240,21 @@ void __fbnic_set_rx_mode(struct fbnic_dev *fbd) fbnic_write_tce_tcam(fbd); } -static void fbnic_set_rx_mode(struct net_device *netdev) +static void fbnic_set_rx_mode(struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct fbnic_net *fbn = netdev_priv(netdev); struct fbnic_dev *fbd = fbn->fbd; /* No need to update the hardware if we are not running */ if (netif_running(netdev)) - __fbnic_set_rx_mode(fbd); + __fbnic_set_rx_mode(fbd, uc, mc); } static int fbnic_set_mac(struct net_device *netdev, void *p) { + struct fbnic_net *fbn = netdev_priv(netdev); struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data)) @@ -257,7 +262,8 @@ static int fbnic_set_mac(struct net_device *netdev, void *p) eth_hw_addr_set(netdev, addr->sa_data); - fbnic_set_rx_mode(netdev); + if (netif_running(netdev)) + __fbnic_set_rx_mode(fbn->fbd, &netdev->uc, &netdev->mc); return 0; } @@ -551,7 +557,7 @@ static const struct net_device_ops fbnic_netdev_ops = { .ndo_features_check = fbnic_features_check, .ndo_set_mac_address = fbnic_set_mac, .ndo_change_mtu = fbnic_change_mtu, - .ndo_set_rx_mode = fbnic_set_rx_mode, + .ndo_set_rx_mode_async = fbnic_set_rx_mode, .ndo_get_stats64 = fbnic_get_stats64, .ndo_bpf = fbnic_bpf, .ndo_hwtstamp_get = fbnic_hwtstamp_get, @@ -820,7 +826,8 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd) netif_tx_stop_all_queues(netdev); if (fbnic_phylink_create(netdev)) { - fbnic_netdev_free(fbd); + free_netdev(netdev); + fbd->netdev = NULL; return NULL; } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h index 9129a658f8fa..eded20b0e9e4 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h @@ -97,7 +97,9 @@ void fbnic_time_init(struct fbnic_net *fbn); int fbnic_time_start(struct fbnic_net *fbn); void fbnic_time_stop(struct fbnic_net *fbn); -void __fbnic_set_rx_mode(struct fbnic_dev *fbd); +void __fbnic_set_rx_mode(struct fbnic_dev *fbd, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc); void fbnic_clear_rx_mode(struct fbnic_dev *fbd); void fbnic_phylink_get_pauseparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c index b7c0b7349d00..7e85b480203c 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c @@ -135,7 +135,7 @@ void fbnic_up(struct fbnic_net *fbn) fbnic_rss_reinit_hw(fbn->fbd, fbn); - __fbnic_set_rx_mode(fbn->fbd); + __fbnic_set_rx_mode(fbn->fbd, &fbn->netdev->uc, &fbn->netdev->mc); /* Enable Tx/Rx processing */ fbnic_napi_enable(fbn); @@ -180,7 +180,7 @@ static int fbnic_fw_config_after_crash(struct fbnic_dev *fbd) } fbnic_rpc_reset_valid_entries(fbd); - __fbnic_set_rx_mode(fbd); + __fbnic_set_rx_mode(fbd, &fbd->netdev->uc, &fbd->netdev->mc); return 0; } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c b/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c index 42a186db43ea..fe95b6f69646 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c @@ -244,7 +244,7 @@ void fbnic_bmc_rpc_check(struct fbnic_dev *fbd) if (fbd->fw_cap.need_bmc_tcam_reinit) { fbnic_bmc_rpc_init(fbd); - __fbnic_set_rx_mode(fbd); + __fbnic_set_rx_mode(fbd, &fbd->netdev->uc, &fbd->netdev->mc); fbd->fw_cap.need_bmc_tcam_reinit = false; } diff --git a/drivers/net/ethernet/micrel/ks8851.h b/drivers/net/ethernet/micrel/ks8851.h index 31f75b4a67fd..b795a3a60571 100644 --- a/drivers/net/ethernet/micrel/ks8851.h +++ b/drivers/net/ethernet/micrel/ks8851.h @@ -408,10 +408,8 @@ struct ks8851_net { struct gpio_desc *gpio; struct mii_bus *mii_bus; - void (*lock)(struct ks8851_net *ks, - unsigned long *flags); - void (*unlock)(struct ks8851_net *ks, - unsigned long *flags); + void (*lock)(struct ks8851_net *ks); + void (*unlock)(struct ks8851_net *ks); unsigned int (*rdreg16)(struct ks8851_net *ks, unsigned int reg); void (*wrreg16)(struct ks8851_net *ks, diff --git a/drivers/net/ethernet/micrel/ks8851_common.c b/drivers/net/ethernet/micrel/ks8851_common.c index 8048770958d6..4afbb40bc0e4 100644 --- a/drivers/net/ethernet/micrel/ks8851_common.c +++ b/drivers/net/ethernet/micrel/ks8851_common.c @@ -28,25 +28,23 @@ /** * ks8851_lock - register access lock * @ks: The chip state - * @flags: Spinlock flags * * Claim chip register access lock */ -static void ks8851_lock(struct ks8851_net *ks, unsigned long *flags) +static void ks8851_lock(struct ks8851_net *ks) { - ks->lock(ks, flags); + ks->lock(ks); } /** * ks8851_unlock - register access unlock * @ks: The chip state - * @flags: Spinlock flags * * Release chip register access lock */ -static void ks8851_unlock(struct ks8851_net *ks, unsigned long *flags) +static void ks8851_unlock(struct ks8851_net *ks) { - ks->unlock(ks, flags); + ks->unlock(ks); } /** @@ -129,11 +127,10 @@ static void ks8851_set_powermode(struct ks8851_net *ks, unsigned pwrmode) static int ks8851_write_mac_addr(struct net_device *dev) { struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; u16 val; int i; - ks8851_lock(ks, &flags); + ks8851_lock(ks); /* * Wake up chip in case it was powered off when stopped; otherwise, @@ -149,7 +146,7 @@ static int ks8851_write_mac_addr(struct net_device *dev) if (!netif_running(dev)) ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); return 0; } @@ -163,12 +160,11 @@ static int ks8851_write_mac_addr(struct net_device *dev) static void ks8851_read_mac_addr(struct net_device *dev) { struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; u8 addr[ETH_ALEN]; u16 reg; int i; - ks8851_lock(ks, &flags); + ks8851_lock(ks); for (i = 0; i < ETH_ALEN; i += 2) { reg = ks8851_rdreg16(ks, KS_MAR(i)); @@ -177,7 +173,7 @@ static void ks8851_read_mac_addr(struct net_device *dev) } eth_hw_addr_set(dev, addr); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); } /** @@ -312,11 +308,10 @@ static irqreturn_t ks8851_irq(int irq, void *_ks) { struct ks8851_net *ks = _ks; struct sk_buff_head rxq; - unsigned long flags; unsigned int status; struct sk_buff *skb; - ks8851_lock(ks, &flags); + ks8851_lock(ks); status = ks8851_rdreg16(ks, KS_ISR); ks8851_wrreg16(ks, KS_ISR, status); @@ -373,14 +368,17 @@ static irqreturn_t ks8851_irq(int irq, void *_ks) ks8851_wrreg16(ks, KS_RXCR1, rxc->rxcr1); } - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); if (status & IRQ_LCI) mii_check_link(&ks->mii); - if (status & IRQ_RXI) + if (status & IRQ_RXI) { + local_bh_disable(); while ((skb = __skb_dequeue(&rxq))) netif_rx(skb); + local_bh_enable(); + } return IRQ_HANDLED; } @@ -405,7 +403,6 @@ static void ks8851_flush_tx_work(struct ks8851_net *ks) static int ks8851_net_open(struct net_device *dev) { struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; int ret; ret = request_threaded_irq(dev->irq, NULL, ks8851_irq, @@ -418,7 +415,7 @@ static int ks8851_net_open(struct net_device *dev) /* lock the card, even if we may not actually be doing anything * else at the moment */ - ks8851_lock(ks, &flags); + ks8851_lock(ks); netif_dbg(ks, ifup, ks->netdev, "opening\n"); @@ -471,7 +468,7 @@ static int ks8851_net_open(struct net_device *dev) netif_dbg(ks, ifup, ks->netdev, "network device up\n"); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); mii_check_link(&ks->mii); return 0; } @@ -487,23 +484,22 @@ static int ks8851_net_open(struct net_device *dev) static int ks8851_net_stop(struct net_device *dev) { struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; netif_info(ks, ifdown, dev, "shutting down\n"); netif_stop_queue(dev); - ks8851_lock(ks, &flags); + ks8851_lock(ks); /* turn off the IRQs and ack any outstanding */ ks8851_wrreg16(ks, KS_IER, 0x0000); ks8851_wrreg16(ks, KS_ISR, 0xffff); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); /* stop any outstanding work */ ks8851_flush_tx_work(ks); flush_work(&ks->rxctrl_work); - ks8851_lock(ks, &flags); + ks8851_lock(ks); /* shutdown RX process */ ks8851_wrreg16(ks, KS_RXCR1, 0x0000); @@ -512,7 +508,7 @@ static int ks8851_net_stop(struct net_device *dev) /* set powermode to soft power down to save power */ ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); /* ensure any queued tx buffers are dumped */ while (!skb_queue_empty(&ks->txq)) { @@ -566,14 +562,13 @@ static netdev_tx_t ks8851_start_xmit(struct sk_buff *skb, static void ks8851_rxctrl_work(struct work_struct *work) { struct ks8851_net *ks = container_of(work, struct ks8851_net, rxctrl_work); - unsigned long flags; - ks8851_lock(ks, &flags); + ks8851_lock(ks); /* need to shutdown RXQ before modifying filter parameters */ ks8851_wrreg16(ks, KS_RXCR1, 0x00); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); } static void ks8851_set_rx_mode(struct net_device *dev) @@ -780,7 +775,6 @@ static int ks8851_set_eeprom(struct net_device *dev, { struct ks8851_net *ks = netdev_priv(dev); int offset = ee->offset; - unsigned long flags; int len = ee->len; u16 tmp; @@ -794,7 +788,7 @@ static int ks8851_set_eeprom(struct net_device *dev, if (!(ks->rc_ccr & CCR_EEPROM)) return -ENOENT; - ks8851_lock(ks, &flags); + ks8851_lock(ks); ks8851_eeprom_claim(ks); @@ -817,7 +811,7 @@ static int ks8851_set_eeprom(struct net_device *dev, eeprom_93cx6_wren(&ks->eeprom, false); ks8851_eeprom_release(ks); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); return 0; } @@ -827,7 +821,6 @@ static int ks8851_get_eeprom(struct net_device *dev, { struct ks8851_net *ks = netdev_priv(dev); int offset = ee->offset; - unsigned long flags; int len = ee->len; /* must be 2 byte aligned */ @@ -837,7 +830,7 @@ static int ks8851_get_eeprom(struct net_device *dev, if (!(ks->rc_ccr & CCR_EEPROM)) return -ENOENT; - ks8851_lock(ks, &flags); + ks8851_lock(ks); ks8851_eeprom_claim(ks); @@ -845,7 +838,7 @@ static int ks8851_get_eeprom(struct net_device *dev, eeprom_93cx6_multiread(&ks->eeprom, offset/2, (__le16 *)data, len/2); ks8851_eeprom_release(ks); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); return 0; } @@ -904,7 +897,6 @@ static int ks8851_phy_reg(int reg) static int ks8851_phy_read_common(struct net_device *dev, int phy_addr, int reg) { struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; int result; int ksreg; @@ -912,9 +904,9 @@ static int ks8851_phy_read_common(struct net_device *dev, int phy_addr, int reg) if (ksreg < 0) return ksreg; - ks8851_lock(ks, &flags); + ks8851_lock(ks); result = ks8851_rdreg16(ks, ksreg); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); return result; } @@ -949,14 +941,13 @@ static void ks8851_phy_write(struct net_device *dev, int phy, int reg, int value) { struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; int ksreg; ksreg = ks8851_phy_reg(reg); if (ksreg >= 0) { - ks8851_lock(ks, &flags); + ks8851_lock(ks); ks8851_wrreg16(ks, ksreg, value); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); } } diff --git a/drivers/net/ethernet/micrel/ks8851_par.c b/drivers/net/ethernet/micrel/ks8851_par.c index 78695be2570b..9f1c33f6ddec 100644 --- a/drivers/net/ethernet/micrel/ks8851_par.c +++ b/drivers/net/ethernet/micrel/ks8851_par.c @@ -55,29 +55,27 @@ struct ks8851_net_par { /** * ks8851_lock_par - register access lock * @ks: The chip state - * @flags: Spinlock flags * * Claim chip register access lock */ -static void ks8851_lock_par(struct ks8851_net *ks, unsigned long *flags) +static void ks8851_lock_par(struct ks8851_net *ks) { struct ks8851_net_par *ksp = to_ks8851_par(ks); - spin_lock_irqsave(&ksp->lock, *flags); + spin_lock_bh(&ksp->lock); } /** * ks8851_unlock_par - register access unlock * @ks: The chip state - * @flags: Spinlock flags * * Release chip register access lock */ -static void ks8851_unlock_par(struct ks8851_net *ks, unsigned long *flags) +static void ks8851_unlock_par(struct ks8851_net *ks) { struct ks8851_net_par *ksp = to_ks8851_par(ks); - spin_unlock_irqrestore(&ksp->lock, *flags); + spin_unlock_bh(&ksp->lock); } /** @@ -233,7 +231,6 @@ static netdev_tx_t ks8851_start_xmit_par(struct sk_buff *skb, { struct ks8851_net *ks = netdev_priv(dev); netdev_tx_t ret = NETDEV_TX_OK; - unsigned long flags; unsigned int txqcr; u16 txmir; int err; @@ -241,7 +238,7 @@ static netdev_tx_t ks8851_start_xmit_par(struct sk_buff *skb, netif_dbg(ks, tx_queued, ks->netdev, "%s: skb %p, %d@%p\n", __func__, skb, skb->len, skb->data); - ks8851_lock_par(ks, &flags); + ks8851_lock_par(ks); txmir = ks8851_rdreg16_par(ks, KS_TXMIR) & 0x1fff; @@ -262,7 +259,7 @@ static netdev_tx_t ks8851_start_xmit_par(struct sk_buff *skb, ret = NETDEV_TX_BUSY; } - ks8851_unlock_par(ks, &flags); + ks8851_unlock_par(ks); return ret; } diff --git a/drivers/net/ethernet/micrel/ks8851_spi.c b/drivers/net/ethernet/micrel/ks8851_spi.c index a161ae45743a..b9e68520278d 100644 --- a/drivers/net/ethernet/micrel/ks8851_spi.c +++ b/drivers/net/ethernet/micrel/ks8851_spi.c @@ -71,11 +71,10 @@ struct ks8851_net_spi { /** * ks8851_lock_spi - register access lock * @ks: The chip state - * @flags: Spinlock flags * * Claim chip register access lock */ -static void ks8851_lock_spi(struct ks8851_net *ks, unsigned long *flags) +static void ks8851_lock_spi(struct ks8851_net *ks) { struct ks8851_net_spi *kss = to_ks8851_spi(ks); @@ -85,11 +84,10 @@ static void ks8851_lock_spi(struct ks8851_net *ks, unsigned long *flags) /** * ks8851_unlock_spi - register access unlock * @ks: The chip state - * @flags: Spinlock flags * * Release chip register access lock */ -static void ks8851_unlock_spi(struct ks8851_net *ks, unsigned long *flags) +static void ks8851_unlock_spi(struct ks8851_net *ks) { struct ks8851_net_spi *kss = to_ks8851_spi(ks); @@ -309,7 +307,6 @@ static void ks8851_tx_work(struct work_struct *work) struct ks8851_net_spi *kss; unsigned short tx_space; struct ks8851_net *ks; - unsigned long flags; struct sk_buff *txb; bool last; @@ -317,7 +314,7 @@ static void ks8851_tx_work(struct work_struct *work) ks = &kss->ks8851; last = skb_queue_empty(&ks->txq); - ks8851_lock_spi(ks, &flags); + ks8851_lock_spi(ks); while (!last) { txb = skb_dequeue(&ks->txq); @@ -343,7 +340,7 @@ static void ks8851_tx_work(struct work_struct *work) ks->tx_space = tx_space; spin_unlock_bh(&ks->statelock); - ks8851_unlock_spi(ks, &flags); + ks8851_unlock_spi(ks); } /** diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 47752d3fde0b..1179a6e127c5 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -749,11 +749,10 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x) for (p = 0; p < lan966x->num_phys_ports; p++) { port = lan966x->ports[p]; - if (!port) + if (!port || !port->dev) continue; - if (port->dev) - unregister_netdev(port->dev); + unregister_netdev(port->dev); lan966x_xdp_port_deinit(port); if (lan966x->fdma && lan966x->fdma_ndev == port->dev) @@ -873,6 +872,9 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, err = register_netdev(dev); if (err) { dev_err(lan966x->dev, "register_netdev failed\n"); + phylink_destroy(phylink); + port->phylink = NULL; + port->dev = NULL; return err; } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index 6a745bb71b5c..eb57b86fbe22 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -31,11 +31,11 @@ enum spx5_target_chiptype { SPX5_TARGET_CT_7552 = 0x7552, /* SparX-5-128 Enterprise */ SPX5_TARGET_CT_7556 = 0x7556, /* SparX-5-160 Enterprise */ SPX5_TARGET_CT_7558 = 0x7558, /* SparX-5-200 Enterprise */ - SPX5_TARGET_CT_7546TSN = 0x47546, /* SparX-5-64i Industrial */ - SPX5_TARGET_CT_7549TSN = 0x47549, /* SparX-5-90i Industrial */ - SPX5_TARGET_CT_7552TSN = 0x47552, /* SparX-5-128i Industrial */ - SPX5_TARGET_CT_7556TSN = 0x47556, /* SparX-5-160i Industrial */ - SPX5_TARGET_CT_7558TSN = 0x47558, /* SparX-5-200i Industrial */ + SPX5_TARGET_CT_7546TSN = 0x0546, /* SparX-5-64i Industrial */ + SPX5_TARGET_CT_7549TSN = 0x0549, /* SparX-5-90i Industrial */ + SPX5_TARGET_CT_7552TSN = 0x0552, /* SparX-5-128i Industrial */ + SPX5_TARGET_CT_7556TSN = 0x0556, /* SparX-5-160i Industrial */ + SPX5_TARGET_CT_7558TSN = 0x0558, /* SparX-5-200i Industrial */ SPX5_TARGET_CT_LAN9694 = 0x9694, /* lan969x-40 */ SPX5_TARGET_CT_LAN9691VAO = 0x9691, /* lan969x-40-VAO */ SPX5_TARGET_CT_LAN9694TSN = 0x9695, /* lan969x-40-TSN */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c index 04bc8fffaf96..62c49893de3c 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c @@ -1128,7 +1128,8 @@ int sparx5_port_init(struct sparx5 *sparx5, DEV2G5_PCS1G_SD_CFG(port->portno)); if (conf->portmode == PHY_INTERFACE_MODE_QSGMII || - conf->portmode == PHY_INTERFACE_MODE_SGMII) { + conf->portmode == PHY_INTERFACE_MODE_SGMII || + conf->portmode == PHY_INTERFACE_MODE_1000BASEX) { err = sparx5_serdes_set(sparx5, port, conf); if (err) return err; diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index 098fbda0d128..d8e816882f02 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -43,8 +43,9 @@ static u64 mana_gd_r64(struct gdma_context *g, u64 offset) static int mana_gd_init_pf_regs(struct pci_dev *pdev) { struct gdma_context *gc = pci_get_drvdata(pdev); - void __iomem *sriov_base_va; + u64 remaining_barsize; u64 sriov_base_off; + u64 sriov_shm_off; gc->db_page_size = mana_gd_r32(gc, GDMA_PF_REG_DB_PAGE_SIZE) & 0xFFFF; @@ -73,10 +74,28 @@ static int mana_gd_init_pf_regs(struct pci_dev *pdev) gc->phys_db_page_base = gc->bar0_pa + gc->db_page_off; sriov_base_off = mana_gd_r64(gc, GDMA_SRIOV_REG_CFG_BASE_OFF); + if (sriov_base_off >= gc->bar0_size || + gc->bar0_size - sriov_base_off < + GDMA_PF_REG_SHM_OFF + sizeof(u64) || + !IS_ALIGNED(sriov_base_off, sizeof(u64))) { + dev_err(gc->dev, + "SRIOV base offset 0x%llx out of range or unaligned (BAR0 size 0x%llx)\n", + sriov_base_off, (u64)gc->bar0_size); + return -EPROTO; + } - sriov_base_va = gc->bar0_va + sriov_base_off; - gc->shm_base = sriov_base_va + - mana_gd_r64(gc, sriov_base_off + GDMA_PF_REG_SHM_OFF); + remaining_barsize = gc->bar0_size - sriov_base_off; + sriov_shm_off = mana_gd_r64(gc, sriov_base_off + GDMA_PF_REG_SHM_OFF); + if (sriov_shm_off >= remaining_barsize || + remaining_barsize - sriov_shm_off < SMC_APERTURE_SIZE || + !IS_ALIGNED(sriov_shm_off, sizeof(u32))) { + dev_err(gc->dev, + "SRIOV SHM offset 0x%llx out of range or unaligned (BAR0 size 0x%llx)\n", + sriov_shm_off, (u64)gc->bar0_size); + return -EPROTO; + } + + gc->shm_base = gc->bar0_va + sriov_base_off + sriov_shm_off; return 0; } @@ -84,6 +103,7 @@ static int mana_gd_init_pf_regs(struct pci_dev *pdev) static int mana_gd_init_vf_regs(struct pci_dev *pdev) { struct gdma_context *gc = pci_get_drvdata(pdev); + u64 shm_off; gc->db_page_size = mana_gd_r32(gc, GDMA_REG_DB_PAGE_SIZE) & 0xFFFF; @@ -111,7 +131,17 @@ static int mana_gd_init_vf_regs(struct pci_dev *pdev) gc->db_page_base = gc->bar0_va + gc->db_page_off; gc->phys_db_page_base = gc->bar0_pa + gc->db_page_off; - gc->shm_base = gc->bar0_va + mana_gd_r64(gc, GDMA_REG_SHM_OFFSET); + shm_off = mana_gd_r64(gc, GDMA_REG_SHM_OFFSET); + if (shm_off >= gc->bar0_size || + gc->bar0_size - shm_off < SMC_APERTURE_SIZE || + !IS_ALIGNED(shm_off, sizeof(u32))) { + dev_err(gc->dev, + "SHM offset 0x%llx out of range or unaligned (BAR0 size 0x%llx)\n", + shm_off, (u64)gc->bar0_size); + return -EPROTO; + } + + gc->shm_base = gc->bar0_va + shm_off; return 0; } diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 6302432b9bf6..9afc786b297a 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2520,9 +2520,12 @@ static void mana_destroy_rxq(struct mana_port_context *apc, napi_disable_locked(napi); netif_napi_del_locked(napi); } - xdp_rxq_info_unreg(&rxq->xdp_rxq); - mana_destroy_wq_obj(apc, GDMA_RQ, rxq->rxobj); + if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) + xdp_rxq_info_unreg(&rxq->xdp_rxq); + + if (rxq->rxobj != INVALID_MANA_HANDLE) + mana_destroy_wq_obj(apc, GDMA_RQ, rxq->rxobj); mana_deinit_cq(apc, &rxq->rx_cq); @@ -2796,9 +2799,6 @@ out: mana_destroy_rxq(apc, rxq, false); - if (cq) - mana_deinit_cq(apc, cq); - return NULL; } @@ -2926,6 +2926,13 @@ static void mana_rss_table_init(struct mana_port_context *apc) ethtool_rxfh_indir_default(i, apc->num_queues); } +int mana_disable_vport_rx(struct mana_port_context *apc) +{ + return mana_cfg_vport_steering(apc, TRI_STATE_FALSE, false, false, + false); +} +EXPORT_SYMBOL_NS(mana_disable_vport_rx, "NET_MANA"); + int mana_config_rss(struct mana_port_context *apc, enum TRI_STATE rx, bool update_hash, bool update_tab) { @@ -3312,10 +3319,12 @@ static int mana_dealloc_queues(struct net_device *ndev) */ apc->rss_state = TRI_STATE_FALSE; - err = mana_config_rss(apc, TRI_STATE_FALSE, false, false); + err = mana_disable_vport_rx(apc); if (err && mana_en_need_log(apc, err)) netdev_err(ndev, "Failed to disable vPort: %d\n", err); + mana_fence_rqs(apc); + /* Even in err case, still need to cleanup the vPort */ mana_destroy_vport(apc); @@ -3631,8 +3640,12 @@ int mana_probe(struct gdma_dev *gd, bool resuming) ac->gdma_dev = gd; gd->driver_data = ac; + + INIT_WORK(&ac->link_change_work, mana_link_state_handle); } + INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler); + err = mana_create_eq(ac); if (err) { dev_err(dev, "Failed to create EQs: %d\n", err); @@ -3648,8 +3661,6 @@ int mana_probe(struct gdma_dev *gd, bool resuming) if (!resuming) { ac->num_ports = num_ports; - - INIT_WORK(&ac->link_change_work, mana_link_state_handle); } else { if (ac->num_ports != num_ports) { dev_err(dev, "The number of vPorts changed: %d->%d\n", @@ -3678,10 +3689,9 @@ int mana_probe(struct gdma_dev *gd, bool resuming) if (!resuming) { for (i = 0; i < ac->num_ports; i++) { err = mana_probe_port(ac, i, &ac->ports[i]); - /* we log the port for which the probe failed and stop - * probes for subsequent ports. - * Note that we keep running ports, for which the probes - * were successful, unless add_adev fails too + /* Log the port for which the probe failed, stop probing + * subsequent ports, and skip add_adev. + * mana_remove() will clean up already-probed ports. */ if (err) { dev_err(dev, "Probe Failed for port %d\n", i); @@ -3695,10 +3705,9 @@ int mana_probe(struct gdma_dev *gd, bool resuming) enable_work(&apc->queue_reset_work); err = mana_attach(ac->ports[i]); rtnl_unlock(); - /* we log the port for which the attach failed and stop - * attach for subsequent ports - * Note that we keep running ports, for which the attach - * were successful, unless add_adev fails too + /* Log the port for which the attach failed, stop + * attaching subsequent ports, and skip add_adev. + * mana_remove() will clean up already-attached ports. */ if (err) { dev_err(dev, "Attach Failed for port %d\n", i); @@ -3707,9 +3716,9 @@ int mana_probe(struct gdma_dev *gd, bool resuming) } } - err = add_adev(gd, "eth"); + if (!err) + err = add_adev(gd, "eth"); - INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler); schedule_delayed_work(&ac->gf_stats_work, MANA_GF_STATS_PERIOD); out: @@ -3730,11 +3739,16 @@ void mana_remove(struct gdma_dev *gd, bool suspending) struct gdma_context *gc = gd->gdma_context; struct mana_context *ac = gd->driver_data; struct mana_port_context *apc; - struct device *dev = gc->dev; + struct device *dev; struct net_device *ndev; int err; int i; + if (!gc || !ac) + return; + + dev = gc->dev; + disable_work_sync(&ac->link_change_work); cancel_delayed_work_sync(&ac->gf_stats_work); @@ -3747,7 +3761,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending) if (!ndev) { if (i == 0) dev_err(dev, "No net device to remove\n"); - goto out; + break; } apc = netdev_priv(ndev); @@ -3778,7 +3792,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending) } mana_destroy_eq(ac); -out: + if (ac->per_port_queue_reset_wq) { destroy_workqueue(ac->per_port_queue_reset_wq); ac->per_port_queue_reset_wq = NULL; diff --git a/drivers/net/ethernet/microsoft/mana/shm_channel.c b/drivers/net/ethernet/microsoft/mana/shm_channel.c index 0f1679ebad96..d21b5db06e50 100644 --- a/drivers/net/ethernet/microsoft/mana/shm_channel.c +++ b/drivers/net/ethernet/microsoft/mana/shm_channel.c @@ -61,11 +61,6 @@ union smc_proto_hdr { }; }; /* HW DATA */ -#define SMC_APERTURE_BITS 256 -#define SMC_BASIC_UNIT (sizeof(u32)) -#define SMC_APERTURE_DWORDS (SMC_APERTURE_BITS / (SMC_BASIC_UNIT * 8)) -#define SMC_LAST_DWORD (SMC_APERTURE_DWORDS - 1) - static int mana_smc_poll_register(void __iomem *base, bool reset) { void __iomem *ptr = base + SMC_LAST_DWORD * SMC_BASIC_UNIT; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_target.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_target.c index 79470f198a62..9cf19446657c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_target.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_target.c @@ -435,12 +435,17 @@ static int nfp_encode_basic_qdr(u64 addr, int dest_island, int cpp_tgt, /* Full Island ID and channel bits overlap? */ ret = nfp_decode_basic(addr, &v, cpp_tgt, mode, addr40, isld1, isld0); - if (ret) + if (ret) { + pr_warn("%s: decode dest_island failed: %d\n", __func__, ret); return ret; + } /* The current address won't go where expected? */ - if (dest_island != -1 && dest_island != v) + if (dest_island != -1 && dest_island != v) { + pr_warn("%s: dest_island mismatch: current (%d) != decoded (%d)\n", + __func__, dest_island, v); return -EINVAL; + } /* If dest_island was -1, we don't care where it goes. */ return 0; @@ -493,7 +498,7 @@ static int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt, * the address but we can verify if the existing * contents will point to a valid island. */ - return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island, + return nfp_encode_basic_qdr(*addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); iid_lsb = addr40 ? 34 : 26; @@ -504,7 +509,7 @@ static int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt, return 0; case 1: if (cpp_tgt == NFP_CPP_TARGET_QDR && !addr40) - return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island, + return nfp_encode_basic_qdr(*addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); idx_lsb = addr40 ? 39 : 31; @@ -530,7 +535,7 @@ static int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt, * be set before hand and with them select an island. * So we need to confirm that it's at least plausible. */ - return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island, + return nfp_encode_basic_qdr(*addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); /* Make sure we compare against isldN values @@ -551,7 +556,7 @@ static int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt, * iid<1> = addr<30> = channel<0> * channel<1> = addr<31> = Index */ - return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island, + return nfp_encode_basic_qdr(*addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); isld[0] &= ~3; diff --git a/drivers/net/ethernet/packetengines/Kconfig b/drivers/net/ethernet/packetengines/Kconfig deleted file mode 100644 index de91331dcb7d..000000000000 --- a/drivers/net/ethernet/packetengines/Kconfig +++ /dev/null @@ -1,44 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Packet Engines device configuration -# - -config NET_VENDOR_PACKET_ENGINES - bool "Packet Engines devices" - default y - depends on PCI - help - If you have a network (Ethernet) card belonging to this class, say Y. - - Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about Packet Engines devices. If you say Y, you will - be asked for your specific card in the following questions. - -if NET_VENDOR_PACKET_ENGINES - -config HAMACHI - tristate "Packet Engines Hamachi GNIC-II support" - depends on PCI - select MII - help - If you have a Gigabit Ethernet card of this type, say Y here. - - To compile this driver as a module, choose M here. The module will be - called hamachi. - -config YELLOWFIN - tristate "Packet Engines Yellowfin Gigabit-NIC support" - depends on PCI - select CRC32 - help - Say Y here if you have a Packet Engines G-NIC PCI Gigabit Ethernet - adapter or the SYM53C885 Ethernet controller. The Gigabit adapter is - used by the Beowulf Linux cluster project. See - for more - information about this driver in particular and Beowulf in general. - - To compile this driver as a module, choose M here: the module - will be called yellowfin. This is recommended. - -endif # NET_VENDOR_PACKET_ENGINES diff --git a/drivers/net/ethernet/packetengines/Makefile b/drivers/net/ethernet/packetengines/Makefile deleted file mode 100644 index cf054b796d11..000000000000 --- a/drivers/net/ethernet/packetengines/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Packet Engines network device drivers. -# - -obj-$(CONFIG_HAMACHI) += hamachi.o -obj-$(CONFIG_YELLOWFIN) += yellowfin.o diff --git a/drivers/net/ethernet/packetengines/hamachi.c b/drivers/net/ethernet/packetengines/hamachi.c deleted file mode 100644 index b0de7e9f12a5..000000000000 --- a/drivers/net/ethernet/packetengines/hamachi.c +++ /dev/null @@ -1,1967 +0,0 @@ -/* hamachi.c: A Packet Engines GNIC-II Gigabit Ethernet driver for Linux. */ -/* - Written 1998-2000 by Donald Becker. - Updates 2000 by Keith Underwood. - - This software may be used and distributed according to the terms of - the GNU General Public License (GPL), incorporated herein by reference. - Drivers based on or derived from this code fall under the GPL and must - retain the authorship, copyright and license notice. This file is not - a complete program and may only be used when the entire operating - system is licensed under the GPL. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - This driver is for the Packet Engines GNIC-II PCI Gigabit Ethernet - adapter. - - Support and updates available at - http://www.scyld.com/network/hamachi.html - [link no longer provides useful info -jgarzik] - or - http://www.parl.clemson.edu/~keithu/hamachi.html - -*/ - -#define DRV_NAME "hamachi" -#define DRV_VERSION "2.1" -#define DRV_RELDATE "Sept 11, 2006" - - -/* A few user-configurable values. */ - -static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ -#define final_version -#define hamachi_debug debug -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 40; -static int mtu; -/* Default values selected by testing on a dual processor PIII-450 */ -/* These six interrupt control parameters may be set directly when loading the - * module, or through the rx_params and tx_params variables - */ -static int max_rx_latency = 0x11; -static int max_rx_gap = 0x05; -static int min_rx_pkt = 0x18; -static int max_tx_latency = 0x00; -static int max_tx_gap = 0x00; -static int min_tx_pkt = 0x30; - -/* Set the copy breakpoint for the copy-only-tiny-frames scheme. - -Setting to > 1518 causes all frames to be copied - -Setting to 0 disables copies -*/ -static int rx_copybreak; - -/* An override for the hardware detection of bus width. - Set to 1 to force 32 bit PCI bus detection. Set to 4 to force 64 bit. - Add 2 to disable parity detection. -*/ -static int force32; - - -/* Used to pass the media type, etc. - These exist for driver interoperability. - No media types are currently defined. - - The lower 4 bits are reserved for the media type. - - The next three bits may be set to one of the following: - 0x00000000 : Autodetect PCI bus - 0x00000010 : Force 32 bit PCI bus - 0x00000020 : Disable parity detection - 0x00000040 : Force 64 bit PCI bus - Default is autodetect - - The next bit can be used to force half-duplex. This is a bad - idea since no known implementations implement half-duplex, and, - in general, half-duplex for gigabit ethernet is a bad idea. - 0x00000080 : Force half-duplex - Default is full-duplex. - - In the original driver, the ninth bit could be used to force - full-duplex. Maintain that for compatibility - 0x00000200 : Force full-duplex -*/ -#define MAX_UNITS 8 /* More are supported, limit only on options */ -static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -/* The Hamachi chipset supports 3 parameters each for Rx and Tx - * interruput management. Parameters will be loaded as specified into - * the TxIntControl and RxIntControl registers. - * - * The registers are arranged as follows: - * 23 - 16 15 - 8 7 - 0 - * _________________________________ - * | min_pkt | max_gap | max_latency | - * --------------------------------- - * min_pkt : The minimum number of packets processed between - * interrupts. - * max_gap : The maximum inter-packet gap in units of 8.192 us - * max_latency : The absolute time between interrupts in units of 8.192 us - * - */ -static int rx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int tx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; - -/* Operational parameters that are set at compile time. */ - -/* Keep the ring sizes a power of two for compile efficiency. - The compiler will convert '%'<2^N> into a bit mask. - Making the Tx ring too large decreases the effectiveness of channel - bonding and packet priority. - There are no ill effects from too-large receive rings, except for - excessive memory usage */ -/* Empirically it appears that the Tx ring needs to be a little bigger - for these Gbit adapters or you get into an overrun condition really - easily. Also, things appear to work a bit better in back-to-back - configurations if the Rx ring is 8 times the size of the Tx ring -*/ -#define TX_RING_SIZE 64 -#define RX_RING_SIZE 512 -#define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct hamachi_desc) -#define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct hamachi_desc) - -/* - * Enable netdev_ioctl. Added interrupt coalescing parameter adjustment. - * 2/19/99 Pete Wyckoff - */ - -/* play with 64-bit addrlen; seems to be a teensy bit slower --pw */ -/* #define ADDRLEN 64 */ - -/* - * RX_CHECKSUM turns on card-generated receive checksum generation for - * TCP and UDP packets. Otherwise the upper layers do the calculation. - * 3/10/1999 Pete Wyckoff - */ -#define RX_CHECKSUM - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (5*HZ) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include /* Processor type for cache alignment. */ -#include -#include -#include - -static const char version[] = -KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Written by Donald Becker\n" -" Some modifications by Eric kasten \n" -" Further modifications by Keith Underwood \n"; - - -/* IP_MF appears to be only defined in , however, - we need it for hardware checksumming support. FYI... some of - the definitions in conflict/duplicate those in - other linux headers causing many compiler warnings. -*/ -#ifndef IP_MF - #define IP_MF 0x2000 /* IP more frags from */ -#endif - -/* Define IP_OFFSET to be IPOPT_OFFSET */ -#ifndef IP_OFFSET - #ifdef IPOPT_OFFSET - #define IP_OFFSET IPOPT_OFFSET - #else - #define IP_OFFSET 2 - #endif -#endif - -#define RUN_AT(x) (jiffies + (x)) - -#ifndef ADDRLEN -#define ADDRLEN 32 -#endif - -/* Condensed bus+endian portability operations. */ -#if ADDRLEN == 64 -#define cpu_to_leXX(addr) cpu_to_le64(addr) -#define leXX_to_cpu(addr) le64_to_cpu(addr) -#else -#define cpu_to_leXX(addr) cpu_to_le32(addr) -#define leXX_to_cpu(addr) le32_to_cpu(addr) -#endif - - -/* - Theory of Operation - -I. Board Compatibility - -This device driver is designed for the Packet Engines "Hamachi" -Gigabit Ethernet chip. The only PCA currently supported is the GNIC-II 64-bit -66Mhz PCI card. - -II. Board-specific settings - -No jumpers exist on the board. The chip supports software correction of -various motherboard wiring errors, however this driver does not support -that feature. - -III. Driver operation - -IIIa. Ring buffers - -The Hamachi uses a typical descriptor based bus-master architecture. -The descriptor list is similar to that used by the Digital Tulip. -This driver uses two statically allocated fixed-size descriptor lists -formed into rings by a branch from the final descriptor to the beginning of -the list. The ring sizes are set at compile time by RX/TX_RING_SIZE. - -This driver uses a zero-copy receive and transmit scheme similar my other -network drivers. -The driver allocates full frame size skbuffs for the Rx ring buffers at -open() time and passes the skb->data field to the Hamachi as receive data -buffers. When an incoming frame is less than RX_COPYBREAK bytes long, -a fresh skbuff is allocated and the frame is copied to the new skbuff. -When the incoming frame is larger, the skbuff is passed directly up the -protocol stack and replaced by a newly allocated skbuff. - -The RX_COPYBREAK value is chosen to trade-off the memory wasted by -using a full-sized skbuff for small frames vs. the copying costs of larger -frames. Gigabit cards are typically used on generously configured machines -and the underfilled buffers have negligible impact compared to the benefit of -a single allocation size, so the default value of zero results in never -copying packets. - -IIIb/c. Transmit/Receive Structure - -The Rx and Tx descriptor structure are straight-forward, with no historical -baggage that must be explained. Unlike the awkward DBDMA structure, there -are no unused fields or option bits that had only one allowable setting. - -Two details should be noted about the descriptors: The chip supports both 32 -bit and 64 bit address structures, and the length field is overwritten on -the receive descriptors. The descriptor length is set in the control word -for each channel. The development driver uses 32 bit addresses only, however -64 bit addresses may be enabled for 64 bit architectures e.g. the Alpha. - -IIId. Synchronization - -This driver is very similar to my other network drivers. -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the -dev->tbusy flag. The other thread is the interrupt handler, which is single -threaded by the hardware and other software. - -The send packet thread has partial control over the Tx ring and 'dev->tbusy' -flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next -queue slot is empty, it clears the tbusy flag when finished otherwise it sets -the 'hmp->tx_full' flag. - -The interrupt handler has exclusive control over the Rx ring and records stats -from the Tx ring. After reaping the stats, it marks the Tx queue entry as -empty by incrementing the dirty_tx mark. Iff the 'hmp->tx_full' flag is set, it -clears both the tx_full and tbusy flags. - -IV. Notes - -Thanks to Kim Stearns of Packet Engines for providing a pair of GNIC-II boards. - -IVb. References - -Hamachi Engineering Design Specification, 5/15/97 -(Note: This version was marked "Confidential".) - -IVc. Errata - -None noted. - -V. Recent Changes - -01/15/1999 EPK Enlargement of the TX and RX ring sizes. This appears - to help avoid some stall conditions -- this needs further research. - -01/15/1999 EPK Creation of the hamachi_tx function. This function cleans - the Tx ring and is called from hamachi_start_xmit (this used to be - called from hamachi_interrupt but it tends to delay execution of the - interrupt handler and thus reduce bandwidth by reducing the latency - between hamachi_rx()'s). Notably, some modification has been made so - that the cleaning loop checks only to make sure that the DescOwn bit - isn't set in the status flag since the card is not required - to set the entire flag to zero after processing. - -01/15/1999 EPK In the hamachi_start_tx function, the Tx ring full flag is - checked before attempting to add a buffer to the ring. If the ring is full - an attempt is made to free any dirty buffers and thus find space for - the new buffer or the function returns non-zero which should case the - scheduler to reschedule the buffer later. - -01/15/1999 EPK Some adjustments were made to the chip initialization. - End-to-end flow control should now be fully active and the interrupt - algorithm vars have been changed. These could probably use further tuning. - -01/15/1999 EPK Added the max_{rx,tx}_latency options. These are used to - set the rx and tx latencies for the Hamachi interrupts. If you're having - problems with network stalls, try setting these to higher values. - Valid values are 0x00 through 0xff. - -01/15/1999 EPK In general, the overall bandwidth has increased and - latencies are better (sometimes by a factor of 2). Stalls are rare at - this point, however there still appears to be a bug somewhere between the - hardware and driver. TCP checksum errors under load also appear to be - eliminated at this point. - -01/18/1999 EPK Ensured that the DescEndRing bit was being set on both the - Rx and Tx rings. This appears to have been affecting whether a particular - peer-to-peer connection would hang under high load. I believe the Rx - rings was typically getting set correctly, but the Tx ring wasn't getting - the DescEndRing bit set during initialization. ??? Does this mean the - hamachi card is using the DescEndRing in processing even if a particular - slot isn't in use -- hypothetically, the card might be searching the - entire Tx ring for slots with the DescOwn bit set and then processing - them. If the DescEndRing bit isn't set, then it might just wander off - through memory until it hits a chunk of data with that bit set - and then looping back. - -02/09/1999 EPK Added Michel Mueller's TxDMA Interrupt and Tx-timeout - problem (TxCmd and RxCmd need only to be set when idle or stopped. - -02/09/1999 EPK Added code to check/reset dev->tbusy in hamachi_interrupt. - (Michel Mueller pointed out the ``permanently busy'' potential - problem here). - -02/22/1999 EPK Added Pete Wyckoff's ioctl to control the Tx/Rx latencies. - -02/23/1999 EPK Verified that the interrupt status field bits for Tx were - incorrectly defined and corrected (as per Michel Mueller). - -02/23/1999 EPK Corrected the Tx full check to check that at least 4 slots - were available before resetting the tbusy and tx_full flags - (as per Michel Mueller). - -03/11/1999 EPK Added Pete Wyckoff's hardware checksumming support. - -12/31/1999 KDU Cleaned up assorted things and added Don's code to force -32 bit. - -02/20/2000 KDU Some of the control was just plain odd. Cleaned up the -hamachi_start_xmit() and hamachi_interrupt() code. There is still some -re-structuring I would like to do. - -03/01/2000 KDU Experimenting with a WIDE range of interrupt mitigation -parameters on a dual P3-450 setup yielded the new default interrupt -mitigation parameters. Tx should interrupt VERY infrequently due to -Eric's scheme. Rx should be more often... - -03/13/2000 KDU Added a patch to make the Rx Checksum code interact -nicely with non-linux machines. - -03/13/2000 KDU Experimented with some of the configuration values: - - -It seems that enabling PCI performance commands for descriptors - (changing RxDMACtrl and TxDMACtrl lower nibble from 5 to D) has minimal - performance impact for any of my tests. (ttcp, netpipe, netperf) I will - leave them that way until I hear further feedback. - - -Increasing the PCI_LATENCY_TIMER to 130 - (2 + (burst size of 128 * (0 wait states + 1))) seems to slightly - degrade performance. Leaving default at 64 pending further information. - -03/14/2000 KDU Further tuning: - - -adjusted boguscnt in hamachi_rx() to depend on interrupt - mitigation parameters chosen. - - -Selected a set of interrupt parameters based on some extensive testing. - These may change with more testing. - -TO DO: - --Consider borrowing from the acenic driver code to check PCI_COMMAND for -PCI_COMMAND_INVALIDATE. Set maximum burst size to cache line size in -that case. - --fix the reset procedure. It doesn't quite work. -*/ - -/* A few values that may be tweaked. */ -/* Size of each temporary Rx buffer, calculated as: - * 1518 bytes (ethernet packet) + 2 bytes (to get 8 byte alignment for - * the card) + 8 bytes of status info + 8 bytes for the Rx Checksum - */ -#define PKT_BUF_SZ 1536 - -/* For now, this is going to be set to the maximum size of an ethernet - * packet. Eventually, we may want to make it a variable that is - * related to the MTU - */ -#define MAX_FRAME_SIZE 1518 - -/* The rest of these values should never change. */ - -static void hamachi_timer(struct timer_list *t); - -enum capability_flags {CanHaveMII=1, }; -static const struct chip_info { - u16 vendor_id, device_id, device_id_mask, pad; - const char *name; - void (*media_timer)(struct timer_list *t); - int flags; -} chip_tbl[] = { - {0x1318, 0x0911, 0xffff, 0, "Hamachi GNIC-II", hamachi_timer, 0}, - {0,}, -}; - -/* Offsets to the Hamachi registers. Various sizes. */ -enum hamachi_offsets { - TxDMACtrl=0x00, TxCmd=0x04, TxStatus=0x06, TxPtr=0x08, TxCurPtr=0x10, - RxDMACtrl=0x20, RxCmd=0x24, RxStatus=0x26, RxPtr=0x28, RxCurPtr=0x30, - PCIClkMeas=0x060, MiscStatus=0x066, ChipRev=0x68, ChipReset=0x06B, - LEDCtrl=0x06C, VirtualJumpers=0x06D, GPIO=0x6E, - TxChecksum=0x074, RxChecksum=0x076, - TxIntrCtrl=0x078, RxIntrCtrl=0x07C, - InterruptEnable=0x080, InterruptClear=0x084, IntrStatus=0x088, - EventStatus=0x08C, - MACCnfg=0x0A0, FrameGap0=0x0A2, FrameGap1=0x0A4, - /* See enum MII_offsets below. */ - MACCnfg2=0x0B0, RxDepth=0x0B8, FlowCtrl=0x0BC, MaxFrameSize=0x0CE, - AddrMode=0x0D0, StationAddr=0x0D2, - /* Gigabit AutoNegotiation. */ - ANCtrl=0x0E0, ANStatus=0x0E2, ANXchngCtrl=0x0E4, ANAdvertise=0x0E8, - ANLinkPartnerAbility=0x0EA, - EECmdStatus=0x0F0, EEData=0x0F1, EEAddr=0x0F2, - FIFOcfg=0x0F8, -}; - -/* Offsets to the MII-mode registers. */ -enum MII_offsets { - MII_Cmd=0xA6, MII_Addr=0xA8, MII_Wr_Data=0xAA, MII_Rd_Data=0xAC, - MII_Status=0xAE, -}; - -/* Bits in the interrupt status/mask registers. */ -enum intr_status_bits { - IntrRxDone=0x01, IntrRxPCIFault=0x02, IntrRxPCIErr=0x04, - IntrTxDone=0x100, IntrTxPCIFault=0x200, IntrTxPCIErr=0x400, - LinkChange=0x10000, NegotiationChange=0x20000, StatsMax=0x40000, }; - -/* The Hamachi Rx and Tx buffer descriptors. */ -struct hamachi_desc { - __le32 status_n_length; -#if ADDRLEN == 64 - u32 pad; - __le64 addr; -#else - __le32 addr; -#endif -}; - -/* Bits in hamachi_desc.status_n_length */ -enum desc_status_bits { - DescOwn=0x80000000, DescEndPacket=0x40000000, DescEndRing=0x20000000, - DescIntr=0x10000000, -}; - -#define PRIV_ALIGN 15 /* Required alignment mask */ -#define MII_CNT 4 -struct hamachi_private { - /* Descriptor rings first for alignment. Tx requires a second descriptor - for status. */ - struct hamachi_desc *rx_ring; - struct hamachi_desc *tx_ring; - struct sk_buff* rx_skbuff[RX_RING_SIZE]; - struct sk_buff* tx_skbuff[TX_RING_SIZE]; - dma_addr_t tx_ring_dma; - dma_addr_t rx_ring_dma; - struct timer_list timer; /* Media selection timer. */ - /* Frequently used and paired value: keep adjacent for cache effect. */ - spinlock_t lock; - int chip_id; - unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ - unsigned int cur_tx, dirty_tx; - unsigned int rx_buf_sz; /* Based on MTU+slack. */ - unsigned int tx_full:1; /* The Tx queue is full. */ - unsigned int duplex_lock:1; - unsigned int default_port:4; /* Last dev->if_port value. */ - /* MII transceiver section. */ - int mii_cnt; /* MII device addresses. */ - struct mii_if_info mii_if; /* MII lib hooks/info */ - unsigned char phys[MII_CNT]; /* MII device addresses, only first one used. */ - u32 rx_int_var, tx_int_var; /* interrupt control variables */ - u32 option; /* Hold on to a copy of the options */ - struct pci_dev *pci_dev; - void __iomem *base; -}; - -MODULE_AUTHOR("Donald Becker , Eric Kasten , Keith Underwood "); -MODULE_DESCRIPTION("Packet Engines 'Hamachi' GNIC-II Gigabit Ethernet driver"); -MODULE_LICENSE("GPL"); - -module_param(max_interrupt_work, int, 0); -module_param(mtu, int, 0); -module_param(debug, int, 0); -module_param(min_rx_pkt, int, 0); -module_param(max_rx_gap, int, 0); -module_param(max_rx_latency, int, 0); -module_param(min_tx_pkt, int, 0); -module_param(max_tx_gap, int, 0); -module_param(max_tx_latency, int, 0); -module_param(rx_copybreak, int, 0); -module_param_array(rx_params, int, NULL, 0); -module_param_array(tx_params, int, NULL, 0); -module_param_array(options, int, NULL, 0); -module_param_array(full_duplex, int, NULL, 0); -module_param(force32, int, 0); -MODULE_PARM_DESC(max_interrupt_work, "GNIC-II maximum events handled per interrupt"); -MODULE_PARM_DESC(mtu, "GNIC-II MTU (all boards)"); -MODULE_PARM_DESC(debug, "GNIC-II debug level (0-7)"); -MODULE_PARM_DESC(min_rx_pkt, "GNIC-II minimum Rx packets processed between interrupts"); -MODULE_PARM_DESC(max_rx_gap, "GNIC-II maximum Rx inter-packet gap in 8.192 microsecond units"); -MODULE_PARM_DESC(max_rx_latency, "GNIC-II time between Rx interrupts in 8.192 microsecond units"); -MODULE_PARM_DESC(min_tx_pkt, "GNIC-II minimum Tx packets processed between interrupts"); -MODULE_PARM_DESC(max_tx_gap, "GNIC-II maximum Tx inter-packet gap in 8.192 microsecond units"); -MODULE_PARM_DESC(max_tx_latency, "GNIC-II time between Tx interrupts in 8.192 microsecond units"); -MODULE_PARM_DESC(rx_copybreak, "GNIC-II copy breakpoint for copy-only-tiny-frames"); -MODULE_PARM_DESC(rx_params, "GNIC-II min_rx_pkt+max_rx_gap+max_rx_latency"); -MODULE_PARM_DESC(tx_params, "GNIC-II min_tx_pkt+max_tx_gap+max_tx_latency"); -MODULE_PARM_DESC(options, "GNIC-II Bits 0-3: media type, bits 4-6: as force32, bit 7: half duplex, bit 9 full duplex"); -MODULE_PARM_DESC(full_duplex, "GNIC-II full duplex setting(s) (1)"); -MODULE_PARM_DESC(force32, "GNIC-II: Bit 0: 32 bit PCI, bit 1: disable parity, bit 2: 64 bit PCI (all boards)"); - -static int read_eeprom(void __iomem *ioaddr, int location); -static int mdio_read(struct net_device *dev, int phy_id, int location); -static void mdio_write(struct net_device *dev, int phy_id, int location, int value); -static int hamachi_open(struct net_device *dev); -static int hamachi_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static int hamachi_siocdevprivate(struct net_device *dev, struct ifreq *rq, - void __user *data, int cmd); -static void hamachi_timer(struct timer_list *t); -static void hamachi_tx_timeout(struct net_device *dev, unsigned int txqueue); -static void hamachi_init_ring(struct net_device *dev); -static netdev_tx_t hamachi_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t hamachi_interrupt(int irq, void *dev_instance); -static int hamachi_rx(struct net_device *dev); -static inline int hamachi_tx(struct net_device *dev); -static void hamachi_error(struct net_device *dev, int intr_status); -static int hamachi_close(struct net_device *dev); -static struct net_device_stats *hamachi_get_stats(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static const struct ethtool_ops ethtool_ops; -static const struct ethtool_ops ethtool_ops_no_mii; - -static const struct net_device_ops hamachi_netdev_ops = { - .ndo_open = hamachi_open, - .ndo_stop = hamachi_close, - .ndo_start_xmit = hamachi_start_xmit, - .ndo_get_stats = hamachi_get_stats, - .ndo_set_rx_mode = set_rx_mode, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, - .ndo_tx_timeout = hamachi_tx_timeout, - .ndo_eth_ioctl = hamachi_ioctl, - .ndo_siocdevprivate = hamachi_siocdevprivate, -}; - - -static int hamachi_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct hamachi_private *hmp; - int option, i, rx_int_var, tx_int_var, boguscnt; - int chip_id = ent->driver_data; - int irq; - void __iomem *ioaddr; - unsigned long base; - static int card_idx; - struct net_device *dev; - void *ring_space; - dma_addr_t ring_dma; - int ret = -ENOMEM; - u8 addr[ETH_ALEN]; - -/* when built into the kernel, we only print version if device is found */ -#ifndef MODULE - static int printed_version; - if (!printed_version++) - printk(version); -#endif - - if (pci_enable_device(pdev)) { - ret = -EIO; - goto err_out; - } - - base = pci_resource_start(pdev, 0); -#ifdef __alpha__ /* Really "64 bit addrs" */ - base |= (pci_resource_start(pdev, 1) << 32); -#endif - - pci_set_master(pdev); - - i = pci_request_regions(pdev, DRV_NAME); - if (i) - return i; - - irq = pdev->irq; - ioaddr = ioremap(base, 0x400); - if (!ioaddr) - goto err_out_release; - - dev = alloc_etherdev(sizeof(struct hamachi_private)); - if (!dev) - goto err_out_iounmap; - - SET_NETDEV_DEV(dev, &pdev->dev); - - for (i = 0; i < 6; i++) - addr[i] = read_eeprom(ioaddr, 4 + i); - eth_hw_addr_set(dev, addr); - -#if ! defined(final_version) - if (hamachi_debug > 4) - for (i = 0; i < 0x10; i++) - printk("%2.2x%s", - read_eeprom(ioaddr, i), i % 16 != 15 ? " " : "\n"); -#endif - - hmp = netdev_priv(dev); - spin_lock_init(&hmp->lock); - - hmp->mii_if.dev = dev; - hmp->mii_if.mdio_read = mdio_read; - hmp->mii_if.mdio_write = mdio_write; - hmp->mii_if.phy_id_mask = 0x1f; - hmp->mii_if.reg_num_mask = 0x1f; - - ring_space = dma_alloc_coherent(&pdev->dev, TX_TOTAL_SIZE, &ring_dma, - GFP_KERNEL); - if (!ring_space) - goto err_out_cleardev; - hmp->tx_ring = ring_space; - hmp->tx_ring_dma = ring_dma; - - ring_space = dma_alloc_coherent(&pdev->dev, RX_TOTAL_SIZE, &ring_dma, - GFP_KERNEL); - if (!ring_space) - goto err_out_unmap_tx; - hmp->rx_ring = ring_space; - hmp->rx_ring_dma = ring_dma; - - /* Check for options being passed in */ - option = card_idx < MAX_UNITS ? options[card_idx] : 0; - if (dev->mem_start) - option = dev->mem_start; - - /* If the bus size is misidentified, do the following. */ - force32 = force32 ? force32 : - ((option >= 0) ? ((option & 0x00000070) >> 4) : 0 ); - if (force32) - writeb(force32, ioaddr + VirtualJumpers); - - /* Hmmm, do we really need to reset the chip???. */ - writeb(0x01, ioaddr + ChipReset); - - /* After a reset, the clock speed measurement of the PCI bus will not - * be valid for a moment. Wait for a little while until it is. If - * it takes more than 10ms, forget it. - */ - udelay(10); - i = readb(ioaddr + PCIClkMeas); - for (boguscnt = 0; (!(i & 0x080)) && boguscnt < 1000; boguscnt++){ - udelay(10); - i = readb(ioaddr + PCIClkMeas); - } - - hmp->base = ioaddr; - pci_set_drvdata(pdev, dev); - - hmp->chip_id = chip_id; - hmp->pci_dev = pdev; - - /* The lower four bits are the media type. */ - if (option > 0) { - hmp->option = option; - if (option & 0x200) - hmp->mii_if.full_duplex = 1; - else if (option & 0x080) - hmp->mii_if.full_duplex = 0; - hmp->default_port = option & 15; - if (hmp->default_port) - hmp->mii_if.force_media = 1; - } - if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) - hmp->mii_if.full_duplex = 1; - - /* lock the duplex mode if someone specified a value */ - if (hmp->mii_if.full_duplex || (option & 0x080)) - hmp->duplex_lock = 1; - - /* Set interrupt tuning parameters */ - max_rx_latency = max_rx_latency & 0x00ff; - max_rx_gap = max_rx_gap & 0x00ff; - min_rx_pkt = min_rx_pkt & 0x00ff; - max_tx_latency = max_tx_latency & 0x00ff; - max_tx_gap = max_tx_gap & 0x00ff; - min_tx_pkt = min_tx_pkt & 0x00ff; - - rx_int_var = card_idx < MAX_UNITS ? rx_params[card_idx] : -1; - tx_int_var = card_idx < MAX_UNITS ? tx_params[card_idx] : -1; - hmp->rx_int_var = rx_int_var >= 0 ? rx_int_var : - (min_rx_pkt << 16 | max_rx_gap << 8 | max_rx_latency); - hmp->tx_int_var = tx_int_var >= 0 ? tx_int_var : - (min_tx_pkt << 16 | max_tx_gap << 8 | max_tx_latency); - - - /* The Hamachi-specific entries in the device structure. */ - dev->netdev_ops = &hamachi_netdev_ops; - dev->ethtool_ops = (chip_tbl[hmp->chip_id].flags & CanHaveMII) ? - ðtool_ops : ðtool_ops_no_mii; - dev->watchdog_timeo = TX_TIMEOUT; - if (mtu) - dev->mtu = mtu; - - i = register_netdev(dev); - if (i) { - ret = i; - goto err_out_unmap_rx; - } - - printk(KERN_INFO "%s: %s type %x at %p, %pM, IRQ %d.\n", - dev->name, chip_tbl[chip_id].name, readl(ioaddr + ChipRev), - ioaddr, dev->dev_addr, irq); - i = readb(ioaddr + PCIClkMeas); - printk(KERN_INFO "%s: %d-bit %d Mhz PCI bus (%d), Virtual Jumpers " - "%2.2x, LPA %4.4x.\n", - dev->name, readw(ioaddr + MiscStatus) & 1 ? 64 : 32, - i ? 2000/(i&0x7f) : 0, i&0x7f, (int)readb(ioaddr + VirtualJumpers), - readw(ioaddr + ANLinkPartnerAbility)); - - if (chip_tbl[hmp->chip_id].flags & CanHaveMII) { - int phy, phy_idx = 0; - for (phy = 0; phy < 32 && phy_idx < MII_CNT; phy++) { - int mii_status = mdio_read(dev, phy, MII_BMSR); - if (mii_status != 0xffff && - mii_status != 0x0000) { - hmp->phys[phy_idx++] = phy; - hmp->mii_if.advertising = mdio_read(dev, phy, MII_ADVERTISE); - printk(KERN_INFO "%s: MII PHY found at address %d, status " - "0x%4.4x advertising %4.4x.\n", - dev->name, phy, mii_status, hmp->mii_if.advertising); - } - } - hmp->mii_cnt = phy_idx; - if (hmp->mii_cnt > 0) - hmp->mii_if.phy_id = hmp->phys[0]; - else - memset(&hmp->mii_if, 0, sizeof(hmp->mii_if)); - } - /* Configure gigabit autonegotiation. */ - writew(0x0400, ioaddr + ANXchngCtrl); /* Enable legacy links. */ - writew(0x08e0, ioaddr + ANAdvertise); /* Set our advertise word. */ - writew(0x1000, ioaddr + ANCtrl); /* Enable negotiation */ - - card_idx++; - return 0; - -err_out_unmap_rx: - dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, hmp->rx_ring, - hmp->rx_ring_dma); -err_out_unmap_tx: - dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, hmp->tx_ring, - hmp->tx_ring_dma); -err_out_cleardev: - free_netdev (dev); -err_out_iounmap: - iounmap(ioaddr); -err_out_release: - pci_release_regions(pdev); -err_out: - return ret; -} - -static int read_eeprom(void __iomem *ioaddr, int location) -{ - int bogus_cnt = 1000; - - /* We should check busy first - per docs -KDU */ - while ((readb(ioaddr + EECmdStatus) & 0x40) && --bogus_cnt > 0); - writew(location, ioaddr + EEAddr); - writeb(0x02, ioaddr + EECmdStatus); - bogus_cnt = 1000; - while ((readb(ioaddr + EECmdStatus) & 0x40) && --bogus_cnt > 0); - if (hamachi_debug > 5) - printk(" EEPROM status is %2.2x after %d ticks.\n", - (int)readb(ioaddr + EECmdStatus), 1000- bogus_cnt); - return readb(ioaddr + EEData); -} - -/* MII Managemen Data I/O accesses. - These routines assume the MDIO controller is idle, and do not exit until - the command is finished. */ - -static int mdio_read(struct net_device *dev, int phy_id, int location) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - int i; - - /* We should check busy first - per docs -KDU */ - for (i = 10000; i >= 0; i--) - if ((readw(ioaddr + MII_Status) & 1) == 0) - break; - writew((phy_id<<8) + location, ioaddr + MII_Addr); - writew(0x0001, ioaddr + MII_Cmd); - for (i = 10000; i >= 0; i--) - if ((readw(ioaddr + MII_Status) & 1) == 0) - break; - return readw(ioaddr + MII_Rd_Data); -} - -static void mdio_write(struct net_device *dev, int phy_id, int location, int value) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - int i; - - /* We should check busy first - per docs -KDU */ - for (i = 10000; i >= 0; i--) - if ((readw(ioaddr + MII_Status) & 1) == 0) - break; - writew((phy_id<<8) + location, ioaddr + MII_Addr); - writew(value, ioaddr + MII_Wr_Data); - - /* Wait for the command to finish. */ - for (i = 10000; i >= 0; i--) - if ((readw(ioaddr + MII_Status) & 1) == 0) - break; -} - - -static int hamachi_open(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - int i; - u32 rx_int_var, tx_int_var; - u16 fifo_info; - - i = request_irq(hmp->pci_dev->irq, hamachi_interrupt, IRQF_SHARED, - dev->name, dev); - if (i) - return i; - - hamachi_init_ring(dev); - -#if ADDRLEN == 64 - /* writellll anyone ? */ - writel(hmp->rx_ring_dma, ioaddr + RxPtr); - writel(hmp->rx_ring_dma >> 32, ioaddr + RxPtr + 4); - writel(hmp->tx_ring_dma, ioaddr + TxPtr); - writel(hmp->tx_ring_dma >> 32, ioaddr + TxPtr + 4); -#else - writel(hmp->rx_ring_dma, ioaddr + RxPtr); - writel(hmp->tx_ring_dma, ioaddr + TxPtr); -#endif - - /* TODO: It would make sense to organize this as words since the card - * documentation does. -KDU - */ - for (i = 0; i < 6; i++) - writeb(dev->dev_addr[i], ioaddr + StationAddr + i); - - /* Initialize other registers: with so many this eventually this will - converted to an offset/value list. */ - - /* Configure the FIFO */ - fifo_info = (readw(ioaddr + GPIO) & 0x00C0) >> 6; - switch (fifo_info){ - case 0 : - /* No FIFO */ - writew(0x0000, ioaddr + FIFOcfg); - break; - case 1 : - /* Configure the FIFO for 512K external, 16K used for Tx. */ - writew(0x0028, ioaddr + FIFOcfg); - break; - case 2 : - /* Configure the FIFO for 1024 external, 32K used for Tx. */ - writew(0x004C, ioaddr + FIFOcfg); - break; - case 3 : - /* Configure the FIFO for 2048 external, 32K used for Tx. */ - writew(0x006C, ioaddr + FIFOcfg); - break; - default : - printk(KERN_WARNING "%s: Unsupported external memory config!\n", - dev->name); - /* Default to no FIFO */ - writew(0x0000, ioaddr + FIFOcfg); - break; - } - - if (dev->if_port == 0) - dev->if_port = hmp->default_port; - - - /* Setting the Rx mode will start the Rx process. */ - /* If someone didn't choose a duplex, default to full-duplex */ - if (hmp->duplex_lock != 1) - hmp->mii_if.full_duplex = 1; - - /* always 1, takes no more time to do it */ - writew(0x0001, ioaddr + RxChecksum); - writew(0x0000, ioaddr + TxChecksum); - writew(0x8000, ioaddr + MACCnfg); /* Soft reset the MAC */ - writew(0x215F, ioaddr + MACCnfg); - writew(0x000C, ioaddr + FrameGap0); - /* WHAT?!?!? Why isn't this documented somewhere? -KDU */ - writew(0x1018, ioaddr + FrameGap1); - /* Why do we enable receives/transmits here? -KDU */ - writew(0x0780, ioaddr + MACCnfg2); /* Upper 16 bits control LEDs. */ - /* Enable automatic generation of flow control frames, period 0xffff. */ - writel(0x0030FFFF, ioaddr + FlowCtrl); - writew(MAX_FRAME_SIZE, ioaddr + MaxFrameSize); /* dev->mtu+14 ??? */ - - /* Enable legacy links. */ - writew(0x0400, ioaddr + ANXchngCtrl); /* Enable legacy links. */ - /* Initial Link LED to blinking red. */ - writeb(0x03, ioaddr + LEDCtrl); - - /* Configure interrupt mitigation. This has a great effect on - performance, so systems tuning should start here!. */ - - rx_int_var = hmp->rx_int_var; - tx_int_var = hmp->tx_int_var; - - if (hamachi_debug > 1) { - printk("max_tx_latency: %d, max_tx_gap: %d, min_tx_pkt: %d\n", - tx_int_var & 0x00ff, (tx_int_var & 0x00ff00) >> 8, - (tx_int_var & 0x00ff0000) >> 16); - printk("max_rx_latency: %d, max_rx_gap: %d, min_rx_pkt: %d\n", - rx_int_var & 0x00ff, (rx_int_var & 0x00ff00) >> 8, - (rx_int_var & 0x00ff0000) >> 16); - printk("rx_int_var: %x, tx_int_var: %x\n", rx_int_var, tx_int_var); - } - - writel(tx_int_var, ioaddr + TxIntrCtrl); - writel(rx_int_var, ioaddr + RxIntrCtrl); - - set_rx_mode(dev); - - netif_start_queue(dev); - - /* Enable interrupts by setting the interrupt mask. */ - writel(0x80878787, ioaddr + InterruptEnable); - writew(0x0000, ioaddr + EventStatus); /* Clear non-interrupting events */ - - /* Configure and start the DMA channels. */ - /* Burst sizes are in the low three bits: size = 4<<(val&7) */ -#if ADDRLEN == 64 - writew(0x005D, ioaddr + RxDMACtrl); /* 128 dword bursts */ - writew(0x005D, ioaddr + TxDMACtrl); -#else - writew(0x001D, ioaddr + RxDMACtrl); - writew(0x001D, ioaddr + TxDMACtrl); -#endif - writew(0x0001, ioaddr + RxCmd); - - if (hamachi_debug > 2) { - printk(KERN_DEBUG "%s: Done hamachi_open(), status: Rx %x Tx %x.\n", - dev->name, readw(ioaddr + RxStatus), readw(ioaddr + TxStatus)); - } - /* Set the timer to check for link beat. */ - timer_setup(&hmp->timer, hamachi_timer, 0); - hmp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ - add_timer(&hmp->timer); - - return 0; -} - -static inline int hamachi_tx(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - - /* Update the dirty pointer until we find an entry that is - still owned by the card */ - for (; hmp->cur_tx - hmp->dirty_tx > 0; hmp->dirty_tx++) { - int entry = hmp->dirty_tx % TX_RING_SIZE; - struct sk_buff *skb; - - if (hmp->tx_ring[entry].status_n_length & cpu_to_le32(DescOwn)) - break; - /* Free the original skb. */ - skb = hmp->tx_skbuff[entry]; - if (skb) { - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->tx_ring[entry].addr), - skb->len, DMA_TO_DEVICE); - dev_kfree_skb(skb); - hmp->tx_skbuff[entry] = NULL; - } - hmp->tx_ring[entry].status_n_length = 0; - if (entry >= TX_RING_SIZE-1) - hmp->tx_ring[TX_RING_SIZE-1].status_n_length |= - cpu_to_le32(DescEndRing); - dev->stats.tx_packets++; - } - - return 0; -} - -static void hamachi_timer(struct timer_list *t) -{ - struct hamachi_private *hmp = timer_container_of(hmp, t, timer); - struct net_device *dev = hmp->mii_if.dev; - void __iomem *ioaddr = hmp->base; - int next_tick = 10*HZ; - - if (hamachi_debug > 2) { - printk(KERN_INFO "%s: Hamachi Autonegotiation status %4.4x, LPA " - "%4.4x.\n", dev->name, readw(ioaddr + ANStatus), - readw(ioaddr + ANLinkPartnerAbility)); - printk(KERN_INFO "%s: Autonegotiation regs %4.4x %4.4x %4.4x " - "%4.4x %4.4x %4.4x.\n", dev->name, - readw(ioaddr + 0x0e0), - readw(ioaddr + 0x0e2), - readw(ioaddr + 0x0e4), - readw(ioaddr + 0x0e6), - readw(ioaddr + 0x0e8), - readw(ioaddr + 0x0eA)); - } - /* We could do something here... nah. */ - hmp->timer.expires = RUN_AT(next_tick); - add_timer(&hmp->timer); -} - -static void hamachi_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - int i; - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - - printk(KERN_WARNING "%s: Hamachi transmit timed out, status %8.8x," - " resetting...\n", dev->name, (int)readw(ioaddr + TxStatus)); - - { - printk(KERN_DEBUG " Rx ring %p: ", hmp->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - printk(KERN_CONT " %8.8x", - le32_to_cpu(hmp->rx_ring[i].status_n_length)); - printk(KERN_CONT "\n"); - printk(KERN_DEBUG" Tx ring %p: ", hmp->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(KERN_CONT " %4.4x", - le32_to_cpu(hmp->tx_ring[i].status_n_length)); - printk(KERN_CONT "\n"); - } - - /* Reinit the hardware and make sure the Rx and Tx processes - are up and running. - */ - dev->if_port = 0; - /* The right way to do Reset. -KDU - * -Clear OWN bit in all Rx/Tx descriptors - * -Wait 50 uS for channels to go idle - * -Turn off MAC receiver - * -Issue Reset - */ - - for (i = 0; i < RX_RING_SIZE; i++) - hmp->rx_ring[i].status_n_length &= cpu_to_le32(~DescOwn); - - /* Presume that all packets in the Tx queue are gone if we have to - * re-init the hardware. - */ - for (i = 0; i < TX_RING_SIZE; i++){ - struct sk_buff *skb; - - if (i >= TX_RING_SIZE - 1) - hmp->tx_ring[i].status_n_length = - cpu_to_le32(DescEndRing) | - (hmp->tx_ring[i].status_n_length & - cpu_to_le32(0x0000ffff)); - else - hmp->tx_ring[i].status_n_length &= cpu_to_le32(0x0000ffff); - skb = hmp->tx_skbuff[i]; - if (skb){ - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->tx_ring[i].addr), - skb->len, DMA_TO_DEVICE); - dev_kfree_skb(skb); - hmp->tx_skbuff[i] = NULL; - } - } - - udelay(60); /* Sleep 60 us just for safety sake */ - writew(0x0002, ioaddr + RxCmd); /* STOP Rx */ - - writeb(0x01, ioaddr + ChipReset); /* Reinit the hardware */ - - hmp->tx_full = 0; - hmp->cur_rx = hmp->cur_tx = 0; - hmp->dirty_rx = hmp->dirty_tx = 0; - /* Rx packets are also presumed lost; however, we need to make sure a - * ring of buffers is in tact. -KDU - */ - for (i = 0; i < RX_RING_SIZE; i++){ - struct sk_buff *skb = hmp->rx_skbuff[i]; - - if (skb){ - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->rx_ring[i].addr), - hmp->rx_buf_sz, DMA_FROM_DEVICE); - dev_kfree_skb(skb); - hmp->rx_skbuff[i] = NULL; - } - } - /* Fill in the Rx buffers. Handle allocation failure gracefully. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb; - - skb = netdev_alloc_skb_ip_align(dev, hmp->rx_buf_sz); - hmp->rx_skbuff[i] = skb; - if (skb == NULL) - break; - - hmp->rx_ring[i].addr = cpu_to_leXX(dma_map_single(&hmp->pci_dev->dev, - skb->data, - hmp->rx_buf_sz, - DMA_FROM_DEVICE)); - hmp->rx_ring[i].status_n_length = cpu_to_le32(DescOwn | - DescEndPacket | DescIntr | (hmp->rx_buf_sz - 2)); - } - hmp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); - /* Mark the last entry as wrapping the ring. */ - hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing); - - /* Trigger an immediate transmit demand. */ - netif_trans_update(dev); /* prevent tx timeout */ - dev->stats.tx_errors++; - - /* Restart the chip's Tx/Rx processes . */ - writew(0x0002, ioaddr + TxCmd); /* STOP Tx */ - writew(0x0001, ioaddr + TxCmd); /* START Tx */ - writew(0x0001, ioaddr + RxCmd); /* START Rx */ - - netif_wake_queue(dev); -} - - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void hamachi_init_ring(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - int i; - - hmp->tx_full = 0; - hmp->cur_rx = hmp->cur_tx = 0; - hmp->dirty_rx = hmp->dirty_tx = 0; - - /* +26 gets the maximum ethernet encapsulation, +7 & ~7 because the - * card needs room to do 8 byte alignment, +2 so we can reserve - * the first 2 bytes, and +16 gets room for the status word from the - * card. -KDU - */ - hmp->rx_buf_sz = (dev->mtu <= 1492 ? PKT_BUF_SZ : - (((dev->mtu+26+7) & ~7) + 16)); - - /* Initialize all Rx descriptors. */ - for (i = 0; i < RX_RING_SIZE; i++) { - hmp->rx_ring[i].status_n_length = 0; - hmp->rx_skbuff[i] = NULL; - } - /* Fill in the Rx buffers. Handle allocation failure gracefully. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = netdev_alloc_skb(dev, hmp->rx_buf_sz + 2); - hmp->rx_skbuff[i] = skb; - if (skb == NULL) - break; - skb_reserve(skb, 2); /* 16 byte align the IP header. */ - hmp->rx_ring[i].addr = cpu_to_leXX(dma_map_single(&hmp->pci_dev->dev, - skb->data, - hmp->rx_buf_sz, - DMA_FROM_DEVICE)); - /* -2 because it doesn't REALLY have that first 2 bytes -KDU */ - hmp->rx_ring[i].status_n_length = cpu_to_le32(DescOwn | - DescEndPacket | DescIntr | (hmp->rx_buf_sz -2)); - } - hmp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); - hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing); - - for (i = 0; i < TX_RING_SIZE; i++) { - hmp->tx_skbuff[i] = NULL; - hmp->tx_ring[i].status_n_length = 0; - } - /* Mark the last entry of the ring */ - hmp->tx_ring[TX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing); -} - - -static netdev_tx_t hamachi_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - unsigned entry; - u16 status; - - /* Ok, now make sure that the queue has space before trying to - add another skbuff. if we return non-zero the scheduler - should interpret this as a queue full and requeue the buffer - for later. - */ - if (hmp->tx_full) { - /* We should NEVER reach this point -KDU */ - printk(KERN_WARNING "%s: Hamachi transmit queue full at slot %d.\n",dev->name, hmp->cur_tx); - - /* Wake the potentially-idle transmit channel. */ - /* If we don't need to read status, DON'T -KDU */ - status=readw(hmp->base + TxStatus); - if( !(status & 0x0001) || (status & 0x0002)) - writew(0x0001, hmp->base + TxCmd); - return NETDEV_TX_BUSY; - } - - /* Caution: the write order is important here, set the field - with the "ownership" bits last. */ - - /* Calculate the next Tx descriptor entry. */ - entry = hmp->cur_tx % TX_RING_SIZE; - - hmp->tx_skbuff[entry] = skb; - - hmp->tx_ring[entry].addr = cpu_to_leXX(dma_map_single(&hmp->pci_dev->dev, - skb->data, - skb->len, - DMA_TO_DEVICE)); - - /* Hmmmm, could probably put a DescIntr on these, but the way - the driver is currently coded makes Tx interrupts unnecessary - since the clearing of the Tx ring is handled by the start_xmit - routine. This organization helps mitigate the interrupts a - bit and probably renders the max_tx_latency param useless. - - Update: Putting a DescIntr bit on all of the descriptors and - mitigating interrupt frequency with the tx_min_pkt parameter. -KDU - */ - if (entry >= TX_RING_SIZE-1) /* Wrap ring */ - hmp->tx_ring[entry].status_n_length = cpu_to_le32(DescOwn | - DescEndPacket | DescEndRing | DescIntr | skb->len); - else - hmp->tx_ring[entry].status_n_length = cpu_to_le32(DescOwn | - DescEndPacket | DescIntr | skb->len); - hmp->cur_tx++; - - /* Non-x86 Todo: explicitly flush cache lines here. */ - - /* Wake the potentially-idle transmit channel. */ - /* If we don't need to read status, DON'T -KDU */ - status=readw(hmp->base + TxStatus); - if( !(status & 0x0001) || (status & 0x0002)) - writew(0x0001, hmp->base + TxCmd); - - /* Immediately before returning, let's clear as many entries as we can. */ - hamachi_tx(dev); - - /* We should kick the bottom half here, since we are not accepting - * interrupts with every packet. i.e. realize that Gigabit ethernet - * can transmit faster than ordinary machines can load packets; - * hence, any packet that got put off because we were in the transmit - * routine should IMMEDIATELY get a chance to be re-queued. -KDU - */ - if ((hmp->cur_tx - hmp->dirty_tx) < (TX_RING_SIZE - 4)) - netif_wake_queue(dev); /* Typical path */ - else { - hmp->tx_full = 1; - netif_stop_queue(dev); - } - - if (hamachi_debug > 4) { - printk(KERN_DEBUG "%s: Hamachi transmit frame #%d queued in slot %d.\n", - dev->name, hmp->cur_tx, entry); - } - return NETDEV_TX_OK; -} - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -static irqreturn_t hamachi_interrupt(int irq, void *dev_instance) -{ - struct net_device *dev = dev_instance; - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - long boguscnt = max_interrupt_work; - int handled = 0; - -#ifndef final_version /* Can never occur. */ - if (dev == NULL) { - printk (KERN_ERR "hamachi_interrupt(): irq %d for unknown device.\n", irq); - return IRQ_NONE; - } -#endif - - spin_lock(&hmp->lock); - - do { - u32 intr_status = readl(ioaddr + InterruptClear); - - if (hamachi_debug > 4) - printk(KERN_DEBUG "%s: Hamachi interrupt, status %4.4x.\n", - dev->name, intr_status); - - if (intr_status == 0) - break; - - handled = 1; - - if (intr_status & IntrRxDone) - hamachi_rx(dev); - - if (intr_status & IntrTxDone){ - /* This code should RARELY need to execute. After all, this is - * a gigabit link, it should consume packets as fast as we put - * them in AND we clear the Tx ring in hamachi_start_xmit(). - */ - if (hmp->tx_full){ - for (; hmp->cur_tx - hmp->dirty_tx > 0; hmp->dirty_tx++){ - int entry = hmp->dirty_tx % TX_RING_SIZE; - struct sk_buff *skb; - - if (hmp->tx_ring[entry].status_n_length & cpu_to_le32(DescOwn)) - break; - skb = hmp->tx_skbuff[entry]; - /* Free the original skb. */ - if (skb){ - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->tx_ring[entry].addr), - skb->len, - DMA_TO_DEVICE); - dev_consume_skb_irq(skb); - hmp->tx_skbuff[entry] = NULL; - } - hmp->tx_ring[entry].status_n_length = 0; - if (entry >= TX_RING_SIZE-1) - hmp->tx_ring[TX_RING_SIZE-1].status_n_length |= - cpu_to_le32(DescEndRing); - dev->stats.tx_packets++; - } - if (hmp->cur_tx - hmp->dirty_tx < TX_RING_SIZE - 4){ - /* The ring is no longer full */ - hmp->tx_full = 0; - netif_wake_queue(dev); - } - } else { - netif_wake_queue(dev); - } - } - - - /* Abnormal error summary/uncommon events handlers. */ - if (intr_status & - (IntrTxPCIFault | IntrTxPCIErr | IntrRxPCIFault | IntrRxPCIErr | - LinkChange | NegotiationChange | StatsMax)) - hamachi_error(dev, intr_status); - - if (--boguscnt < 0) { - printk(KERN_WARNING "%s: Too much work at interrupt, status=0x%4.4x.\n", - dev->name, intr_status); - break; - } - } while (1); - - if (hamachi_debug > 3) - printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", - dev->name, readl(ioaddr + IntrStatus)); - -#ifndef final_version - /* Code that should never be run! Perhaps remove after testing.. */ - { - static int stopit = 10; - if (dev->start == 0 && --stopit < 0) { - printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n", - dev->name); - free_irq(irq, dev); - } - } -#endif - - spin_unlock(&hmp->lock); - return IRQ_RETVAL(handled); -} - -/* This routine is logically part of the interrupt handler, but separated - for clarity and better register allocation. */ -static int hamachi_rx(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - int entry = hmp->cur_rx % RX_RING_SIZE; - int boguscnt = (hmp->dirty_rx + RX_RING_SIZE) - hmp->cur_rx; - - if (hamachi_debug > 4) { - printk(KERN_DEBUG " In hamachi_rx(), entry %d status %4.4x.\n", - entry, hmp->rx_ring[entry].status_n_length); - } - - /* If EOP is set on the next entry, it's a new packet. Send it up. */ - while (1) { - struct hamachi_desc *desc = &(hmp->rx_ring[entry]); - u32 desc_status = le32_to_cpu(desc->status_n_length); - u16 data_size = desc_status; /* Implicit truncate */ - u8 *buf_addr; - s32 frame_status; - - if (desc_status & DescOwn) - break; - dma_sync_single_for_cpu(&hmp->pci_dev->dev, - leXX_to_cpu(desc->addr), - hmp->rx_buf_sz, DMA_FROM_DEVICE); - buf_addr = (u8 *) hmp->rx_skbuff[entry]->data; - frame_status = get_unaligned_le32(&(buf_addr[data_size - 12])); - if (hamachi_debug > 4) - printk(KERN_DEBUG " hamachi_rx() status was %8.8x.\n", - frame_status); - if (--boguscnt < 0) - break; - if ( ! (desc_status & DescEndPacket)) { - printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " - "multiple buffers, entry %#x length %d status %4.4x!\n", - dev->name, hmp->cur_rx, data_size, desc_status); - printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n", - dev->name, desc, &hmp->rx_ring[hmp->cur_rx % RX_RING_SIZE]); - printk(KERN_WARNING "%s: Oversized Ethernet frame -- next status %x/%x last status %x.\n", - dev->name, - le32_to_cpu(hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length) & 0xffff0000, - le32_to_cpu(hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length) & 0x0000ffff, - le32_to_cpu(hmp->rx_ring[(hmp->cur_rx-1) % RX_RING_SIZE].status_n_length)); - dev->stats.rx_length_errors++; - } /* else Omit for prototype errata??? */ - if (frame_status & 0x00380000) { - /* There was an error. */ - if (hamachi_debug > 2) - printk(KERN_DEBUG " hamachi_rx() Rx error was %8.8x.\n", - frame_status); - dev->stats.rx_errors++; - if (frame_status & 0x00600000) - dev->stats.rx_length_errors++; - if (frame_status & 0x00080000) - dev->stats.rx_frame_errors++; - if (frame_status & 0x00100000) - dev->stats.rx_crc_errors++; - if (frame_status < 0) - dev->stats.rx_dropped++; - } else { - struct sk_buff *skb; - /* Omit CRC */ - u16 pkt_len = (frame_status & 0x07ff) - 4; -#ifdef RX_CHECKSUM - u32 pfck = *(u32 *) &buf_addr[data_size - 8]; -#endif - - -#ifndef final_version - if (hamachi_debug > 4) - printk(KERN_DEBUG " hamachi_rx() normal Rx pkt length %d" - " of %d, bogus_cnt %d.\n", - pkt_len, data_size, boguscnt); - if (hamachi_debug > 5) - printk(KERN_DEBUG"%s: rx status %8.8x %8.8x %8.8x %8.8x %8.8x.\n", - dev->name, - *(s32*)&(buf_addr[data_size - 20]), - *(s32*)&(buf_addr[data_size - 16]), - *(s32*)&(buf_addr[data_size - 12]), - *(s32*)&(buf_addr[data_size - 8]), - *(s32*)&(buf_addr[data_size - 4])); -#endif - /* Check if the packet is long enough to accept without copying - to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak && - (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) { -#ifdef RX_CHECKSUM - printk(KERN_ERR "%s: rx_copybreak non-zero " - "not good with RX_CHECKSUM\n", dev->name); -#endif - skb_reserve(skb, 2); /* 16 byte align the IP header */ - dma_sync_single_for_cpu(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->rx_ring[entry].addr), - hmp->rx_buf_sz, - DMA_FROM_DEVICE); - /* Call copy + cksum if available. */ -#if 1 || USE_IP_COPYSUM - skb_copy_to_linear_data(skb, - hmp->rx_skbuff[entry]->data, pkt_len); - skb_put(skb, pkt_len); -#else - skb_put_data(skb, hmp->rx_ring_dma - + entry*sizeof(*desc), pkt_len); -#endif - dma_sync_single_for_device(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->rx_ring[entry].addr), - hmp->rx_buf_sz, - DMA_FROM_DEVICE); - } else { - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->rx_ring[entry].addr), - hmp->rx_buf_sz, - DMA_FROM_DEVICE); - skb_put(skb = hmp->rx_skbuff[entry], pkt_len); - hmp->rx_skbuff[entry] = NULL; - } - skb->protocol = eth_type_trans(skb, dev); - - -#ifdef RX_CHECKSUM - /* TCP or UDP on ipv4, DIX encoding */ - if (pfck>>24 == 0x91 || pfck>>24 == 0x51) { - struct iphdr *ih = (struct iphdr *) skb->data; - /* Check that IP packet is at least 46 bytes, otherwise, - * there may be pad bytes included in the hardware checksum. - * This wouldn't happen if everyone padded with 0. - */ - if (ntohs(ih->tot_len) >= 46){ - /* don't worry about frags */ - if (!(ih->frag_off & cpu_to_be16(IP_MF|IP_OFFSET))) { - u32 inv = *(u32 *) &buf_addr[data_size - 16]; - u32 *p = (u32 *) &buf_addr[data_size - 20]; - register u32 crc, p_r, p_r1; - - if (inv & 4) { - inv &= ~4; - --p; - } - p_r = *p; - p_r1 = *(p-1); - switch (inv) { - case 0: - crc = (p_r & 0xffff) + (p_r >> 16); - break; - case 1: - crc = (p_r >> 16) + (p_r & 0xffff) - + (p_r1 >> 16 & 0xff00); - break; - case 2: - crc = p_r + (p_r1 >> 16); - break; - case 3: - crc = p_r + (p_r1 & 0xff00) + (p_r1 >> 16); - break; - default: /*NOTREACHED*/ crc = 0; - } - if (crc & 0xffff0000) { - crc &= 0xffff; - ++crc; - } - /* tcp/udp will add in pseudo */ - skb->csum = ntohs(pfck & 0xffff); - if (skb->csum > crc) - skb->csum -= crc; - else - skb->csum += (~crc & 0xffff); - /* - * could do the pseudo myself and return - * CHECKSUM_UNNECESSARY - */ - skb->ip_summed = CHECKSUM_COMPLETE; - } - } - } -#endif /* RX_CHECKSUM */ - - netif_rx(skb); - dev->stats.rx_packets++; - } - entry = (++hmp->cur_rx) % RX_RING_SIZE; - } - - /* Refill the Rx ring buffers. */ - for (; hmp->cur_rx - hmp->dirty_rx > 0; hmp->dirty_rx++) { - struct hamachi_desc *desc; - - entry = hmp->dirty_rx % RX_RING_SIZE; - desc = &(hmp->rx_ring[entry]); - if (hmp->rx_skbuff[entry] == NULL) { - struct sk_buff *skb = netdev_alloc_skb(dev, hmp->rx_buf_sz + 2); - - hmp->rx_skbuff[entry] = skb; - if (skb == NULL) - break; /* Better luck next round. */ - skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - desc->addr = cpu_to_leXX(dma_map_single(&hmp->pci_dev->dev, - skb->data, - hmp->rx_buf_sz, - DMA_FROM_DEVICE)); - } - desc->status_n_length = cpu_to_le32(hmp->rx_buf_sz); - if (entry >= RX_RING_SIZE-1) - desc->status_n_length |= cpu_to_le32(DescOwn | - DescEndPacket | DescEndRing | DescIntr); - else - desc->status_n_length |= cpu_to_le32(DescOwn | - DescEndPacket | DescIntr); - } - - /* Restart Rx engine if stopped. */ - /* If we don't need to check status, don't. -KDU */ - if (readw(hmp->base + RxStatus) & 0x0002) - writew(0x0001, hmp->base + RxCmd); - - return 0; -} - -/* This is more properly named "uncommon interrupt events", as it covers more - than just errors. */ -static void hamachi_error(struct net_device *dev, int intr_status) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - - if (intr_status & (LinkChange|NegotiationChange)) { - if (hamachi_debug > 1) - printk(KERN_INFO "%s: Link changed: AutoNegotiation Ctrl" - " %4.4x, Status %4.4x %4.4x Intr status %4.4x.\n", - dev->name, readw(ioaddr + 0x0E0), readw(ioaddr + 0x0E2), - readw(ioaddr + ANLinkPartnerAbility), - readl(ioaddr + IntrStatus)); - if (readw(ioaddr + ANStatus) & 0x20) - writeb(0x01, ioaddr + LEDCtrl); - else - writeb(0x03, ioaddr + LEDCtrl); - } - if (intr_status & StatsMax) { - hamachi_get_stats(dev); - /* Read the overflow bits to clear. */ - readl(ioaddr + 0x370); - readl(ioaddr + 0x3F0); - } - if ((intr_status & ~(LinkChange|StatsMax|NegotiationChange|IntrRxDone|IntrTxDone)) && - hamachi_debug) - printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", - dev->name, intr_status); - /* Hmmmmm, it's not clear how to recover from PCI faults. */ - if (intr_status & (IntrTxPCIErr | IntrTxPCIFault)) - dev->stats.tx_fifo_errors++; - if (intr_status & (IntrRxPCIErr | IntrRxPCIFault)) - dev->stats.rx_fifo_errors++; -} - -static int hamachi_close(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - struct sk_buff *skb; - int i; - - netif_stop_queue(dev); - - if (hamachi_debug > 1) { - printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %4.4x Rx %4.4x Int %2.2x.\n", - dev->name, readw(ioaddr + TxStatus), - readw(ioaddr + RxStatus), readl(ioaddr + IntrStatus)); - printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n", - dev->name, hmp->cur_tx, hmp->dirty_tx, hmp->cur_rx, hmp->dirty_rx); - } - - /* Disable interrupts by clearing the interrupt mask. */ - writel(0x0000, ioaddr + InterruptEnable); - - /* Stop the chip's Tx and Rx processes. */ - writel(2, ioaddr + RxCmd); - writew(2, ioaddr + TxCmd); - -#ifdef __i386__ - if (hamachi_debug > 2) { - printk(KERN_DEBUG " Tx ring at %8.8x:\n", - (int)hmp->tx_ring_dma); - for (i = 0; i < TX_RING_SIZE; i++) - printk(KERN_DEBUG " %c #%d desc. %8.8x %8.8x.\n", - readl(ioaddr + TxCurPtr) == (long)&hmp->tx_ring[i] ? '>' : ' ', - i, hmp->tx_ring[i].status_n_length, hmp->tx_ring[i].addr); - printk(KERN_DEBUG " Rx ring %8.8x:\n", - (int)hmp->rx_ring_dma); - for (i = 0; i < RX_RING_SIZE; i++) { - printk(KERN_DEBUG " %c #%d desc. %4.4x %8.8x\n", - readl(ioaddr + RxCurPtr) == (long)&hmp->rx_ring[i] ? '>' : ' ', - i, hmp->rx_ring[i].status_n_length, hmp->rx_ring[i].addr); - if (hamachi_debug > 6) { - if (*(u8*)hmp->rx_skbuff[i]->data != 0x69) { - u16 *addr = (u16 *) - hmp->rx_skbuff[i]->data; - int j; - printk(KERN_DEBUG "Addr: "); - for (j = 0; j < 0x50; j++) - printk(" %4.4x", addr[j]); - printk("\n"); - } - } - } - } -#endif /* __i386__ debugging only */ - - free_irq(hmp->pci_dev->irq, dev); - - timer_delete_sync(&hmp->timer); - - /* Free all the skbuffs in the Rx queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { - skb = hmp->rx_skbuff[i]; - hmp->rx_ring[i].status_n_length = 0; - if (skb) { - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->rx_ring[i].addr), - hmp->rx_buf_sz, DMA_FROM_DEVICE); - dev_kfree_skb(skb); - hmp->rx_skbuff[i] = NULL; - } - hmp->rx_ring[i].addr = cpu_to_leXX(0xBADF00D0); /* An invalid address. */ - } - for (i = 0; i < TX_RING_SIZE; i++) { - skb = hmp->tx_skbuff[i]; - if (skb) { - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->tx_ring[i].addr), - skb->len, DMA_TO_DEVICE); - dev_kfree_skb(skb); - hmp->tx_skbuff[i] = NULL; - } - } - - writeb(0x00, ioaddr + LEDCtrl); - - return 0; -} - -static struct net_device_stats *hamachi_get_stats(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - - /* We should lock this segment of code for SMP eventually, although - the vulnerability window is very small and statistics are - non-critical. */ - /* Ok, what goes here? This appears to be stuck at 21 packets - according to ifconfig. It does get incremented in hamachi_tx(), - so I think I'll comment it out here and see if better things - happen. - */ - /* dev->stats.tx_packets = readl(ioaddr + 0x000); */ - - /* Total Uni+Brd+Multi */ - dev->stats.rx_bytes = readl(ioaddr + 0x330); - /* Total Uni+Brd+Multi */ - dev->stats.tx_bytes = readl(ioaddr + 0x3B0); - /* Multicast Rx */ - dev->stats.multicast = readl(ioaddr + 0x320); - - /* Over+Undersized */ - dev->stats.rx_length_errors = readl(ioaddr + 0x368); - /* Jabber */ - dev->stats.rx_over_errors = readl(ioaddr + 0x35C); - /* Jabber */ - dev->stats.rx_crc_errors = readl(ioaddr + 0x360); - /* Symbol Errs */ - dev->stats.rx_frame_errors = readl(ioaddr + 0x364); - /* Dropped */ - dev->stats.rx_missed_errors = readl(ioaddr + 0x36C); - - return &dev->stats; -} - -static void set_rx_mode(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - writew(0x000F, ioaddr + AddrMode); - } else if ((netdev_mc_count(dev) > 63) || (dev->flags & IFF_ALLMULTI)) { - /* Too many to match, or accept all multicasts. */ - writew(0x000B, ioaddr + AddrMode); - } else if (!netdev_mc_empty(dev)) { /* Must use the CAM filter. */ - struct netdev_hw_addr *ha; - int i = 0; - - netdev_for_each_mc_addr(ha, dev) { - writel(*(u32 *)(ha->addr), ioaddr + 0x100 + i*8); - writel(0x20000 | (*(u16 *)&ha->addr[4]), - ioaddr + 0x104 + i*8); - i++; - } - /* Clear remaining entries. */ - for (; i < 64; i++) - writel(0, ioaddr + 0x104 + i*8); - writew(0x0003, ioaddr + AddrMode); - } else { /* Normal, unicast/broadcast-only mode. */ - writew(0x0001, ioaddr + AddrMode); - } -} - -static int check_if_running(struct net_device *dev) -{ - if (!netif_running(dev)) - return -EINVAL; - return 0; -} - -static void hamachi_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - struct hamachi_private *np = netdev_priv(dev); - - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - strscpy(info->version, DRV_VERSION, sizeof(info->version)); - strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); -} - -static int hamachi_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings *cmd) -{ - struct hamachi_private *np = netdev_priv(dev); - spin_lock_irq(&np->lock); - mii_ethtool_get_link_ksettings(&np->mii_if, cmd); - spin_unlock_irq(&np->lock); - return 0; -} - -static int hamachi_set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings *cmd) -{ - struct hamachi_private *np = netdev_priv(dev); - int res; - spin_lock_irq(&np->lock); - res = mii_ethtool_set_link_ksettings(&np->mii_if, cmd); - spin_unlock_irq(&np->lock); - return res; -} - -static int hamachi_nway_reset(struct net_device *dev) -{ - struct hamachi_private *np = netdev_priv(dev); - return mii_nway_restart(&np->mii_if); -} - -static u32 hamachi_get_link(struct net_device *dev) -{ - struct hamachi_private *np = netdev_priv(dev); - return mii_link_ok(&np->mii_if); -} - -static const struct ethtool_ops ethtool_ops = { - .begin = check_if_running, - .get_drvinfo = hamachi_get_drvinfo, - .nway_reset = hamachi_nway_reset, - .get_link = hamachi_get_link, - .get_link_ksettings = hamachi_get_link_ksettings, - .set_link_ksettings = hamachi_set_link_ksettings, -}; - -static const struct ethtool_ops ethtool_ops_no_mii = { - .begin = check_if_running, - .get_drvinfo = hamachi_get_drvinfo, -}; - -/* private ioctl: set rx,tx intr params */ -static int hamachi_siocdevprivate(struct net_device *dev, struct ifreq *rq, - void __user *data, int cmd) -{ - struct hamachi_private *np = netdev_priv(dev); - u32 *d = (u32 *)&rq->ifr_ifru; - - if (!netif_running(dev)) - return -EINVAL; - - if (cmd != SIOCDEVPRIVATE + 3) - return -EOPNOTSUPP; - - /* Should add this check here or an ordinary user can do nasty - * things. -KDU - * - * TODO: Shut down the Rx and Tx engines while doing this. - */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - writel(d[0], np->base + TxIntrCtrl); - writel(d[1], np->base + RxIntrCtrl); - printk(KERN_NOTICE "%s: tx %08x, rx %08x intr\n", dev->name, - (u32)readl(np->base + TxIntrCtrl), - (u32)readl(np->base + RxIntrCtrl)); - - return 0; -} - -static int hamachi_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct hamachi_private *np = netdev_priv(dev); - struct mii_ioctl_data *data = if_mii(rq); - int rc; - - if (!netif_running(dev)) - return -EINVAL; - - spin_lock_irq(&np->lock); - rc = generic_mii_ioctl(&np->mii_if, data, cmd, NULL); - spin_unlock_irq(&np->lock); - - return rc; -} - - -static void hamachi_remove_one(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - - if (dev) { - struct hamachi_private *hmp = netdev_priv(dev); - - dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, hmp->rx_ring, - hmp->rx_ring_dma); - dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, hmp->tx_ring, - hmp->tx_ring_dma); - unregister_netdev(dev); - iounmap(hmp->base); - free_netdev(dev); - pci_release_regions(pdev); - } -} - -static const struct pci_device_id hamachi_pci_tbl[] = { - { 0x1318, 0x0911, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, hamachi_pci_tbl); - -static struct pci_driver hamachi_driver = { - .name = DRV_NAME, - .id_table = hamachi_pci_tbl, - .probe = hamachi_init_one, - .remove = hamachi_remove_one, -}; - -static int __init hamachi_init (void) -{ -/* when a module, this is printed whether or not devices are found in probe */ -#ifdef MODULE - printk(version); -#endif - return pci_register_driver(&hamachi_driver); -} - -static void __exit hamachi_exit (void) -{ - pci_unregister_driver(&hamachi_driver); -} - - -module_init(hamachi_init); -module_exit(hamachi_exit); diff --git a/drivers/net/ethernet/packetengines/yellowfin.c b/drivers/net/ethernet/packetengines/yellowfin.c deleted file mode 100644 index 1e25ac13a7d8..000000000000 --- a/drivers/net/ethernet/packetengines/yellowfin.c +++ /dev/null @@ -1,1438 +0,0 @@ -/* yellowfin.c: A Packet Engines G-NIC ethernet driver for linux. */ -/* - Written 1997-2001 by Donald Becker. - - This software may be used and distributed according to the terms of - the GNU General Public License (GPL), incorporated herein by reference. - Drivers based on or derived from this code fall under the GPL and must - retain the authorship, copyright and license notice. This file is not - a complete program and may only be used when the entire operating - system is licensed under the GPL. - - This driver is for the Packet Engines G-NIC PCI Gigabit Ethernet adapter. - It also supports the Symbios Logic version of the same chip core. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - Support and updates available at - http://www.scyld.com/network/yellowfin.html - [link no longer provides useful info -jgarzik] - -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define DRV_NAME "yellowfin" -#define DRV_VERSION "2.1" -#define DRV_RELDATE "Sep 11, 2006" - -/* The user-configurable values. - These may be modified when a driver module is loaded.*/ - -static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 20; -static int mtu; -#ifdef YF_PROTOTYPE /* Support for prototype hardware errata. */ -/* System-wide count of bogus-rx frames. */ -static int bogus_rx; -static int dma_ctrl = 0x004A0263; /* Constrained by errata */ -static int fifo_cfg = 0x0020; /* Bypass external Tx FIFO. */ -#elif defined(YF_NEW) /* A future perfect board :->. */ -static int dma_ctrl = 0x00CAC277; /* Override when loading module! */ -static int fifo_cfg = 0x0028; -#else -static const int dma_ctrl = 0x004A0263; /* Constrained by errata */ -static const int fifo_cfg = 0x0020; /* Bypass external Tx FIFO. */ -#endif - -/* Set the copy breakpoint for the copy-only-tiny-frames scheme. - Setting to > 1514 effectively disables this feature. */ -static int rx_copybreak; - -/* Used to pass the media type, etc. - No media types are currently defined. These exist for driver - interoperability. -*/ -#define MAX_UNITS 8 /* More are supported, limit only on options */ -static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; - -/* Do ugly workaround for GX server chipset errata. */ -static int gx_fix; - -/* Operational parameters that are set at compile time. */ - -/* Keep the ring sizes a power of two for efficiency. - Making the Tx ring too long decreases the effectiveness of channel - bonding and packet priority. - There are no ill effects from too-large receive rings. */ -#define TX_RING_SIZE 16 -#define TX_QUEUE_SIZE 12 /* Must be > 4 && <= TX_RING_SIZE */ -#define RX_RING_SIZE 64 -#define STATUS_TOTAL_SIZE TX_RING_SIZE*sizeof(struct tx_status_words) -#define TX_TOTAL_SIZE 2*TX_RING_SIZE*sizeof(struct yellowfin_desc) -#define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct yellowfin_desc) - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (2*HZ) -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ - -#define yellowfin_debug debug - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* Processor type for cache alignment. */ -#include -#include - -/* These identify the driver base version and may not be removed. */ -static const char version[] = - KERN_INFO DRV_NAME ".c:v1.05 1/09/2001 Written by Donald Becker \n" - " (unofficial 2.4.x port, " DRV_VERSION ", " DRV_RELDATE ")\n"; - -MODULE_AUTHOR("Donald Becker "); -MODULE_DESCRIPTION("Packet Engines Yellowfin G-NIC Gigabit Ethernet driver"); -MODULE_LICENSE("GPL"); - -module_param(max_interrupt_work, int, 0); -module_param(mtu, int, 0); -module_param(debug, int, 0); -module_param(rx_copybreak, int, 0); -module_param_array(options, int, NULL, 0); -module_param_array(full_duplex, int, NULL, 0); -module_param(gx_fix, int, 0); -MODULE_PARM_DESC(max_interrupt_work, "G-NIC maximum events handled per interrupt"); -MODULE_PARM_DESC(mtu, "G-NIC MTU (all boards)"); -MODULE_PARM_DESC(debug, "G-NIC debug level (0-7)"); -MODULE_PARM_DESC(rx_copybreak, "G-NIC copy breakpoint for copy-only-tiny-frames"); -MODULE_PARM_DESC(options, "G-NIC: Bits 0-3: media type, bit 17: full duplex"); -MODULE_PARM_DESC(full_duplex, "G-NIC full duplex setting(s) (1)"); -MODULE_PARM_DESC(gx_fix, "G-NIC: enable GX server chipset bug workaround (0-1)"); - -/* - Theory of Operation - -I. Board Compatibility - -This device driver is designed for the Packet Engines "Yellowfin" Gigabit -Ethernet adapter. The G-NIC 64-bit PCI card is supported, as well as the -Symbios 53C885E dual function chip. - -II. Board-specific settings - -PCI bus devices are configured by the system at boot time, so no jumpers -need to be set on the board. The system BIOS preferably should assign the -PCI INTA signal to an otherwise unused system IRQ line. -Note: Kernel versions earlier than 1.3.73 do not support shared PCI -interrupt lines. - -III. Driver operation - -IIIa. Ring buffers - -The Yellowfin uses the Descriptor Based DMA Architecture specified by Apple. -This is a descriptor list scheme similar to that used by the EEPro100 and -Tulip. This driver uses two statically allocated fixed-size descriptor lists -formed into rings by a branch from the final descriptor to the beginning of -the list. The ring sizes are set at compile time by RX/TX_RING_SIZE. - -The driver allocates full frame size skbuffs for the Rx ring buffers at -open() time and passes the skb->data field to the Yellowfin as receive data -buffers. When an incoming frame is less than RX_COPYBREAK bytes long, -a fresh skbuff is allocated and the frame is copied to the new skbuff. -When the incoming frame is larger, the skbuff is passed directly up the -protocol stack and replaced by a newly allocated skbuff. - -The RX_COPYBREAK value is chosen to trade-off the memory wasted by -using a full-sized skbuff for small frames vs. the copying costs of larger -frames. For small frames the copying cost is negligible (esp. considering -that we are pre-loading the cache with immediately useful header -information). For large frames the copying cost is non-trivial, and the -larger copy might flush the cache of useful data. - -IIIC. Synchronization - -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the -dev->tbusy flag. The other thread is the interrupt handler, which is single -threaded by the hardware and other software. - -The send packet thread has partial control over the Tx ring and 'dev->tbusy' -flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next -queue slot is empty, it clears the tbusy flag when finished otherwise it sets -the 'yp->tx_full' flag. - -The interrupt handler has exclusive control over the Rx ring and records stats -from the Tx ring. After reaping the stats, it marks the Tx queue entry as -empty by incrementing the dirty_tx mark. Iff the 'yp->tx_full' flag is set, it -clears both the tx_full and tbusy flags. - -IV. Notes - -Thanks to Kim Stearns of Packet Engines for providing a pair of G-NIC boards. -Thanks to Bruce Faust of Digitalscape for providing both their SYM53C885 board -and an AlphaStation to verify the Alpha port! - -IVb. References - -Yellowfin Engineering Design Specification, 4/23/97 Preliminary/Confidential -Symbios SYM53C885 PCI-SCSI/Fast Ethernet Multifunction Controller Preliminary - Data Manual v3.0 -http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html -http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html - -IVc. Errata - -See Packet Engines confidential appendix (prototype chips only). -*/ - - - -enum capability_flags { - HasMII=1, FullTxStatus=2, IsGigabit=4, HasMulticastBug=8, FullRxStatus=16, - HasMACAddrBug=32, /* Only on early revs. */ - DontUseEeprom=64, /* Don't read the MAC from the EEPROm. */ -}; - -/* The PCI I/O space extent. */ -enum { - YELLOWFIN_SIZE = 0x100, -}; - -struct pci_id_info { - const char *name; - struct match_info { - int pci, pci_mask, subsystem, subsystem_mask; - int revision, revision_mask; /* Only 8 bits. */ - } id; - int drv_flags; /* Driver use, intended as capability flags. */ -}; - -static const struct pci_id_info pci_id_tbl[] = { - {"Yellowfin G-NIC Gigabit Ethernet", { 0x07021000, 0xffffffff}, - FullTxStatus | IsGigabit | HasMulticastBug | HasMACAddrBug | DontUseEeprom}, - {"Symbios SYM83C885", { 0x07011000, 0xffffffff}, - HasMII | DontUseEeprom }, - { } -}; - -static const struct pci_device_id yellowfin_pci_tbl[] = { - { 0x1000, 0x0702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0x1000, 0x0701, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, - { } -}; -MODULE_DEVICE_TABLE (pci, yellowfin_pci_tbl); - - -/* Offsets to the Yellowfin registers. Various sizes and alignments. */ -enum yellowfin_offsets { - TxCtrl=0x00, TxStatus=0x04, TxPtr=0x0C, - TxIntrSel=0x10, TxBranchSel=0x14, TxWaitSel=0x18, - RxCtrl=0x40, RxStatus=0x44, RxPtr=0x4C, - RxIntrSel=0x50, RxBranchSel=0x54, RxWaitSel=0x58, - EventStatus=0x80, IntrEnb=0x82, IntrClear=0x84, IntrStatus=0x86, - ChipRev=0x8C, DMACtrl=0x90, TxThreshold=0x94, - Cnfg=0xA0, FrameGap0=0xA2, FrameGap1=0xA4, - MII_Cmd=0xA6, MII_Addr=0xA8, MII_Wr_Data=0xAA, MII_Rd_Data=0xAC, - MII_Status=0xAE, - RxDepth=0xB8, FlowCtrl=0xBC, - AddrMode=0xD0, StnAddr=0xD2, HashTbl=0xD8, FIFOcfg=0xF8, - EEStatus=0xF0, EECtrl=0xF1, EEAddr=0xF2, EERead=0xF3, EEWrite=0xF4, - EEFeature=0xF5, -}; - -/* The Yellowfin Rx and Tx buffer descriptors. - Elements are written as 32 bit for endian portability. */ -struct yellowfin_desc { - __le32 dbdma_cmd; - __le32 addr; - __le32 branch_addr; - __le32 result_status; -}; - -struct tx_status_words { -#ifdef __BIG_ENDIAN - u16 tx_errs; - u16 tx_cnt; - u16 paused; - u16 total_tx_cnt; -#else /* Little endian chips. */ - u16 tx_cnt; - u16 tx_errs; - u16 total_tx_cnt; - u16 paused; -#endif /* __BIG_ENDIAN */ -}; - -/* Bits in yellowfin_desc.cmd */ -enum desc_cmd_bits { - CMD_TX_PKT=0x10000000, CMD_RX_BUF=0x20000000, CMD_TXSTATUS=0x30000000, - CMD_NOP=0x60000000, CMD_STOP=0x70000000, - BRANCH_ALWAYS=0x0C0000, INTR_ALWAYS=0x300000, WAIT_ALWAYS=0x030000, - BRANCH_IFTRUE=0x040000, -}; - -/* Bits in yellowfin_desc.status */ -enum desc_status_bits { RX_EOP=0x0040, }; - -/* Bits in the interrupt status/mask registers. */ -enum intr_status_bits { - IntrRxDone=0x01, IntrRxInvalid=0x02, IntrRxPCIFault=0x04,IntrRxPCIErr=0x08, - IntrTxDone=0x10, IntrTxInvalid=0x20, IntrTxPCIFault=0x40,IntrTxPCIErr=0x80, - IntrEarlyRx=0x100, IntrWakeup=0x200, }; - -#define PRIV_ALIGN 31 /* Required alignment mask */ -#define MII_CNT 4 -struct yellowfin_private { - /* Descriptor rings first for alignment. - Tx requires a second descriptor for status. */ - struct yellowfin_desc *rx_ring; - struct yellowfin_desc *tx_ring; - struct sk_buff* rx_skbuff[RX_RING_SIZE]; - struct sk_buff* tx_skbuff[TX_RING_SIZE]; - dma_addr_t rx_ring_dma; - dma_addr_t tx_ring_dma; - - struct tx_status_words *tx_status; - dma_addr_t tx_status_dma; - - struct timer_list timer; /* Media selection timer. */ - /* Frequently used and paired value: keep adjacent for cache effect. */ - int chip_id, drv_flags; - struct pci_dev *pci_dev; - unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ - unsigned int rx_buf_sz; /* Based on MTU+slack. */ - struct tx_status_words *tx_tail_desc; - unsigned int cur_tx, dirty_tx; - int tx_threshold; - unsigned int tx_full:1; /* The Tx queue is full. */ - unsigned int full_duplex:1; /* Full-duplex operation requested. */ - unsigned int duplex_lock:1; - unsigned int medialock:1; /* Do not sense media. */ - unsigned int default_port:4; /* Last dev->if_port value. */ - /* MII transceiver section. */ - int mii_cnt; /* MII device addresses. */ - u16 advertising; /* NWay media advertisement */ - unsigned char phys[MII_CNT]; /* MII device addresses, only first one used */ - spinlock_t lock; - void __iomem *base; -}; - -static int read_eeprom(void __iomem *ioaddr, int location); -static int mdio_read(void __iomem *ioaddr, int phy_id, int location); -static void mdio_write(void __iomem *ioaddr, int phy_id, int location, int value); -static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static int yellowfin_open(struct net_device *dev); -static void yellowfin_timer(struct timer_list *t); -static void yellowfin_tx_timeout(struct net_device *dev, unsigned int txqueue); -static int yellowfin_init_ring(struct net_device *dev); -static netdev_tx_t yellowfin_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance); -static int yellowfin_rx(struct net_device *dev); -static void yellowfin_error(struct net_device *dev, int intr_status); -static int yellowfin_close(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static const struct ethtool_ops ethtool_ops; - -static const struct net_device_ops netdev_ops = { - .ndo_open = yellowfin_open, - .ndo_stop = yellowfin_close, - .ndo_start_xmit = yellowfin_start_xmit, - .ndo_set_rx_mode = set_rx_mode, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, - .ndo_eth_ioctl = netdev_ioctl, - .ndo_tx_timeout = yellowfin_tx_timeout, -}; - -static int yellowfin_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct net_device *dev; - struct yellowfin_private *np; - int irq; - int chip_idx = ent->driver_data; - static int find_cnt; - void __iomem *ioaddr; - int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0; - int drv_flags = pci_id_tbl[chip_idx].drv_flags; - void *ring_space; - dma_addr_t ring_dma; -#ifdef USE_IO_OPS - int bar = 0; -#else - int bar = 1; -#endif - u8 addr[ETH_ALEN]; - -/* when built into the kernel, we only print version if device is found */ -#ifndef MODULE - static int printed_version; - if (!printed_version++) - printk(version); -#endif - - i = pci_enable_device(pdev); - if (i) return i; - - dev = alloc_etherdev(sizeof(*np)); - if (!dev) - return -ENOMEM; - - SET_NETDEV_DEV(dev, &pdev->dev); - - np = netdev_priv(dev); - - if (pci_request_regions(pdev, DRV_NAME)) - goto err_out_free_netdev; - - pci_set_master (pdev); - - ioaddr = pci_iomap(pdev, bar, YELLOWFIN_SIZE); - if (!ioaddr) - goto err_out_free_res; - - irq = pdev->irq; - - if (drv_flags & DontUseEeprom) - for (i = 0; i < 6; i++) - addr[i] = ioread8(ioaddr + StnAddr + i); - else { - int ee_offset = (read_eeprom(ioaddr, 6) == 0xff ? 0x100 : 0); - for (i = 0; i < 6; i++) - addr[i] = read_eeprom(ioaddr, ee_offset + i); - } - eth_hw_addr_set(dev, addr); - - /* Reset the chip. */ - iowrite32(0x80000000, ioaddr + DMACtrl); - - pci_set_drvdata(pdev, dev); - spin_lock_init(&np->lock); - - np->pci_dev = pdev; - np->chip_id = chip_idx; - np->drv_flags = drv_flags; - np->base = ioaddr; - - ring_space = dma_alloc_coherent(&pdev->dev, TX_TOTAL_SIZE, &ring_dma, - GFP_KERNEL); - if (!ring_space) - goto err_out_cleardev; - np->tx_ring = ring_space; - np->tx_ring_dma = ring_dma; - - ring_space = dma_alloc_coherent(&pdev->dev, RX_TOTAL_SIZE, &ring_dma, - GFP_KERNEL); - if (!ring_space) - goto err_out_unmap_tx; - np->rx_ring = ring_space; - np->rx_ring_dma = ring_dma; - - ring_space = dma_alloc_coherent(&pdev->dev, STATUS_TOTAL_SIZE, - &ring_dma, GFP_KERNEL); - if (!ring_space) - goto err_out_unmap_rx; - np->tx_status = ring_space; - np->tx_status_dma = ring_dma; - - if (dev->mem_start) - option = dev->mem_start; - - /* The lower four bits are the media type. */ - if (option > 0) { - if (option & 0x200) - np->full_duplex = 1; - np->default_port = option & 15; - if (np->default_port) - np->medialock = 1; - } - if (find_cnt < MAX_UNITS && full_duplex[find_cnt] > 0) - np->full_duplex = 1; - - if (np->full_duplex) - np->duplex_lock = 1; - - /* The Yellowfin-specific entries in the device structure. */ - dev->netdev_ops = &netdev_ops; - dev->ethtool_ops = ðtool_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - if (mtu) - dev->mtu = mtu; - - i = register_netdev(dev); - if (i) - goto err_out_unmap_status; - - netdev_info(dev, "%s type %8x at %p, %pM, IRQ %d\n", - pci_id_tbl[chip_idx].name, - ioread32(ioaddr + ChipRev), ioaddr, - dev->dev_addr, irq); - - if (np->drv_flags & HasMII) { - int phy, phy_idx = 0; - for (phy = 0; phy < 32 && phy_idx < MII_CNT; phy++) { - int mii_status = mdio_read(ioaddr, phy, 1); - if (mii_status != 0xffff && mii_status != 0x0000) { - np->phys[phy_idx++] = phy; - np->advertising = mdio_read(ioaddr, phy, 4); - netdev_info(dev, "MII PHY found at address %d, status 0x%04x advertising %04x\n", - phy, mii_status, np->advertising); - } - } - np->mii_cnt = phy_idx; - } - - find_cnt++; - - return 0; - -err_out_unmap_status: - dma_free_coherent(&pdev->dev, STATUS_TOTAL_SIZE, np->tx_status, - np->tx_status_dma); -err_out_unmap_rx: - dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring, - np->rx_ring_dma); -err_out_unmap_tx: - dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring, - np->tx_ring_dma); -err_out_cleardev: - pci_iounmap(pdev, ioaddr); -err_out_free_res: - pci_release_regions(pdev); -err_out_free_netdev: - free_netdev (dev); - return -ENODEV; -} - -static int read_eeprom(void __iomem *ioaddr, int location) -{ - int bogus_cnt = 10000; /* Typical 33Mhz: 1050 ticks */ - - iowrite8(location, ioaddr + EEAddr); - iowrite8(0x30 | ((location >> 8) & 7), ioaddr + EECtrl); - while ((ioread8(ioaddr + EEStatus) & 0x80) && --bogus_cnt > 0) - ; - return ioread8(ioaddr + EERead); -} - -/* MII Managemen Data I/O accesses. - These routines assume the MDIO controller is idle, and do not exit until - the command is finished. */ - -static int mdio_read(void __iomem *ioaddr, int phy_id, int location) -{ - int i; - - iowrite16((phy_id<<8) + location, ioaddr + MII_Addr); - iowrite16(1, ioaddr + MII_Cmd); - for (i = 10000; i >= 0; i--) - if ((ioread16(ioaddr + MII_Status) & 1) == 0) - break; - return ioread16(ioaddr + MII_Rd_Data); -} - -static void mdio_write(void __iomem *ioaddr, int phy_id, int location, int value) -{ - int i; - - iowrite16((phy_id<<8) + location, ioaddr + MII_Addr); - iowrite16(value, ioaddr + MII_Wr_Data); - - /* Wait for the command to finish. */ - for (i = 10000; i >= 0; i--) - if ((ioread16(ioaddr + MII_Status) & 1) == 0) - break; -} - - -static int yellowfin_open(struct net_device *dev) -{ - struct yellowfin_private *yp = netdev_priv(dev); - const int irq = yp->pci_dev->irq; - void __iomem *ioaddr = yp->base; - int i, rc; - - /* Reset the chip. */ - iowrite32(0x80000000, ioaddr + DMACtrl); - - rc = request_irq(irq, yellowfin_interrupt, IRQF_SHARED, dev->name, dev); - if (rc) - return rc; - - rc = yellowfin_init_ring(dev); - if (rc < 0) - goto err_free_irq; - - iowrite32(yp->rx_ring_dma, ioaddr + RxPtr); - iowrite32(yp->tx_ring_dma, ioaddr + TxPtr); - - for (i = 0; i < 6; i++) - iowrite8(dev->dev_addr[i], ioaddr + StnAddr + i); - - /* Set up various condition 'select' registers. - There are no options here. */ - iowrite32(0x00800080, ioaddr + TxIntrSel); /* Interrupt on Tx abort */ - iowrite32(0x00800080, ioaddr + TxBranchSel); /* Branch on Tx abort */ - iowrite32(0x00400040, ioaddr + TxWaitSel); /* Wait on Tx status */ - iowrite32(0x00400040, ioaddr + RxIntrSel); /* Interrupt on Rx done */ - iowrite32(0x00400040, ioaddr + RxBranchSel); /* Branch on Rx error */ - iowrite32(0x00400040, ioaddr + RxWaitSel); /* Wait on Rx done */ - - /* Initialize other registers: with so many this eventually this will - converted to an offset/value list. */ - iowrite32(dma_ctrl, ioaddr + DMACtrl); - iowrite16(fifo_cfg, ioaddr + FIFOcfg); - /* Enable automatic generation of flow control frames, period 0xffff. */ - iowrite32(0x0030FFFF, ioaddr + FlowCtrl); - - yp->tx_threshold = 32; - iowrite32(yp->tx_threshold, ioaddr + TxThreshold); - - if (dev->if_port == 0) - dev->if_port = yp->default_port; - - netif_start_queue(dev); - - /* Setting the Rx mode will start the Rx process. */ - if (yp->drv_flags & IsGigabit) { - /* We are always in full-duplex mode with gigabit! */ - yp->full_duplex = 1; - iowrite16(0x01CF, ioaddr + Cnfg); - } else { - iowrite16(0x0018, ioaddr + FrameGap0); /* 0060/4060 for non-MII 10baseT */ - iowrite16(0x1018, ioaddr + FrameGap1); - iowrite16(0x101C | (yp->full_duplex ? 2 : 0), ioaddr + Cnfg); - } - set_rx_mode(dev); - - /* Enable interrupts by setting the interrupt mask. */ - iowrite16(0x81ff, ioaddr + IntrEnb); /* See enum intr_status_bits */ - iowrite16(0x0000, ioaddr + EventStatus); /* Clear non-interrupting events */ - iowrite32(0x80008000, ioaddr + RxCtrl); /* Start Rx and Tx channels. */ - iowrite32(0x80008000, ioaddr + TxCtrl); - - if (yellowfin_debug > 2) { - netdev_printk(KERN_DEBUG, dev, "Done %s()\n", __func__); - } - - /* Set the timer to check for link beat. */ - timer_setup(&yp->timer, yellowfin_timer, 0); - yp->timer.expires = jiffies + 3*HZ; - add_timer(&yp->timer); -out: - return rc; - -err_free_irq: - free_irq(irq, dev); - goto out; -} - -static void yellowfin_timer(struct timer_list *t) -{ - struct yellowfin_private *yp = timer_container_of(yp, t, timer); - struct net_device *dev = pci_get_drvdata(yp->pci_dev); - void __iomem *ioaddr = yp->base; - int next_tick = 60*HZ; - - if (yellowfin_debug > 3) { - netdev_printk(KERN_DEBUG, dev, "Yellowfin timer tick, status %08x\n", - ioread16(ioaddr + IntrStatus)); - } - - if (yp->mii_cnt) { - int bmsr = mdio_read(ioaddr, yp->phys[0], MII_BMSR); - int lpa = mdio_read(ioaddr, yp->phys[0], MII_LPA); - int negotiated = lpa & yp->advertising; - if (yellowfin_debug > 1) - netdev_printk(KERN_DEBUG, dev, "MII #%d status register is %04x, link partner capability %04x\n", - yp->phys[0], bmsr, lpa); - - yp->full_duplex = mii_duplex(yp->duplex_lock, negotiated); - - iowrite16(0x101C | (yp->full_duplex ? 2 : 0), ioaddr + Cnfg); - - if (bmsr & BMSR_LSTATUS) - next_tick = 60*HZ; - else - next_tick = 3*HZ; - } - - yp->timer.expires = jiffies + next_tick; - add_timer(&yp->timer); -} - -static void yellowfin_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - struct yellowfin_private *yp = netdev_priv(dev); - void __iomem *ioaddr = yp->base; - - netdev_warn(dev, "Yellowfin transmit timed out at %d/%d Tx status %04x, Rx status %04x, resetting...\n", - yp->cur_tx, yp->dirty_tx, - ioread32(ioaddr + TxStatus), - ioread32(ioaddr + RxStatus)); - - /* Note: these should be KERN_DEBUG. */ - if (yellowfin_debug) { - int i; - pr_warn(" Rx ring %p: ", yp->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - pr_cont(" %08x", yp->rx_ring[i].result_status); - pr_cont("\n"); - pr_warn(" Tx ring %p: ", yp->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - pr_cont(" %04x /%08x", - yp->tx_status[i].tx_errs, - yp->tx_ring[i].result_status); - pr_cont("\n"); - } - - /* If the hardware is found to hang regularly, we will update the code - to reinitialize the chip here. */ - dev->if_port = 0; - - /* Wake the potentially-idle transmit channel. */ - iowrite32(0x10001000, yp->base + TxCtrl); - if (yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE) - netif_wake_queue (dev); /* Typical path */ - - netif_trans_update(dev); /* prevent tx timeout */ - dev->stats.tx_errors++; -} - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static int yellowfin_init_ring(struct net_device *dev) -{ - struct yellowfin_private *yp = netdev_priv(dev); - int i, j; - - yp->tx_full = 0; - yp->cur_rx = yp->cur_tx = 0; - yp->dirty_tx = 0; - - yp->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); - - for (i = 0; i < RX_RING_SIZE; i++) { - yp->rx_ring[i].dbdma_cmd = - cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | yp->rx_buf_sz); - yp->rx_ring[i].branch_addr = cpu_to_le32(yp->rx_ring_dma + - ((i+1)%RX_RING_SIZE)*sizeof(struct yellowfin_desc)); - } - - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = netdev_alloc_skb(dev, yp->rx_buf_sz + 2); - yp->rx_skbuff[i] = skb; - if (skb == NULL) - break; - skb_reserve(skb, 2); /* 16 byte align the IP header. */ - yp->rx_ring[i].addr = cpu_to_le32(dma_map_single(&yp->pci_dev->dev, - skb->data, - yp->rx_buf_sz, - DMA_FROM_DEVICE)); - } - if (i != RX_RING_SIZE) { - for (j = 0; j < i; j++) - dev_kfree_skb(yp->rx_skbuff[j]); - return -ENOMEM; - } - yp->rx_ring[i-1].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); - -#define NO_TXSTATS -#ifdef NO_TXSTATS - /* In this mode the Tx ring needs only a single descriptor. */ - for (i = 0; i < TX_RING_SIZE; i++) { - yp->tx_skbuff[i] = NULL; - yp->tx_ring[i].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->tx_ring[i].branch_addr = cpu_to_le32(yp->tx_ring_dma + - ((i+1)%TX_RING_SIZE)*sizeof(struct yellowfin_desc)); - } - /* Wrap ring */ - yp->tx_ring[--i].dbdma_cmd = cpu_to_le32(CMD_STOP | BRANCH_ALWAYS); -#else -{ - /* Tx ring needs a pair of descriptors, the second for the status. */ - for (i = 0; i < TX_RING_SIZE; i++) { - j = 2*i; - yp->tx_skbuff[i] = 0; - /* Branch on Tx error. */ - yp->tx_ring[j].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->tx_ring[j].branch_addr = cpu_to_le32(yp->tx_ring_dma + - (j+1)*sizeof(struct yellowfin_desc)); - j++; - if (yp->flags & FullTxStatus) { - yp->tx_ring[j].dbdma_cmd = - cpu_to_le32(CMD_TXSTATUS | sizeof(*yp->tx_status)); - yp->tx_ring[j].request_cnt = sizeof(*yp->tx_status); - yp->tx_ring[j].addr = cpu_to_le32(yp->tx_status_dma + - i*sizeof(struct tx_status_words)); - } else { - /* Symbios chips write only tx_errs word. */ - yp->tx_ring[j].dbdma_cmd = - cpu_to_le32(CMD_TXSTATUS | INTR_ALWAYS | 2); - yp->tx_ring[j].request_cnt = 2; - /* Om pade ummmmm... */ - yp->tx_ring[j].addr = cpu_to_le32(yp->tx_status_dma + - i*sizeof(struct tx_status_words) + - &(yp->tx_status[0].tx_errs) - - &(yp->tx_status[0])); - } - yp->tx_ring[j].branch_addr = cpu_to_le32(yp->tx_ring_dma + - ((j+1)%(2*TX_RING_SIZE))*sizeof(struct yellowfin_desc)); - } - /* Wrap ring */ - yp->tx_ring[++j].dbdma_cmd |= cpu_to_le32(BRANCH_ALWAYS | INTR_ALWAYS); -} -#endif - yp->tx_tail_desc = &yp->tx_status[0]; - return 0; -} - -static netdev_tx_t yellowfin_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct yellowfin_private *yp = netdev_priv(dev); - unsigned entry; - int len = skb->len; - - netif_stop_queue (dev); - - /* Note: Ordering is important here, set the field with the - "ownership" bit last, and only then increment cur_tx. */ - - /* Calculate the next Tx descriptor entry. */ - entry = yp->cur_tx % TX_RING_SIZE; - - if (gx_fix) { /* Note: only works for paddable protocols e.g. IP. */ - int cacheline_end = ((unsigned long)skb->data + skb->len) % 32; - /* Fix GX chipset errata. */ - if (cacheline_end > 24 || cacheline_end == 0) { - len = skb->len + 32 - cacheline_end + 1; - if (skb_padto(skb, len)) { - yp->tx_skbuff[entry] = NULL; - netif_wake_queue(dev); - return NETDEV_TX_OK; - } - } - } - yp->tx_skbuff[entry] = skb; - -#ifdef NO_TXSTATS - yp->tx_ring[entry].addr = cpu_to_le32(dma_map_single(&yp->pci_dev->dev, - skb->data, - len, DMA_TO_DEVICE)); - yp->tx_ring[entry].result_status = 0; - if (entry >= TX_RING_SIZE-1) { - /* New stop command. */ - yp->tx_ring[0].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->tx_ring[TX_RING_SIZE-1].dbdma_cmd = - cpu_to_le32(CMD_TX_PKT|BRANCH_ALWAYS | len); - } else { - yp->tx_ring[entry+1].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->tx_ring[entry].dbdma_cmd = - cpu_to_le32(CMD_TX_PKT | BRANCH_IFTRUE | len); - } - yp->cur_tx++; -#else - yp->tx_ring[entry<<1].request_cnt = len; - yp->tx_ring[entry<<1].addr = cpu_to_le32(dma_map_single(&yp->pci_dev->dev, - skb->data, - len, DMA_TO_DEVICE)); - /* The input_last (status-write) command is constant, but we must - rewrite the subsequent 'stop' command. */ - - yp->cur_tx++; - { - unsigned next_entry = yp->cur_tx % TX_RING_SIZE; - yp->tx_ring[next_entry<<1].dbdma_cmd = cpu_to_le32(CMD_STOP); - } - /* Final step -- overwrite the old 'stop' command. */ - - yp->tx_ring[entry<<1].dbdma_cmd = - cpu_to_le32( ((entry % 6) == 0 ? CMD_TX_PKT|INTR_ALWAYS|BRANCH_IFTRUE : - CMD_TX_PKT | BRANCH_IFTRUE) | len); -#endif - - /* Non-x86 Todo: explicitly flush cache lines here. */ - - /* Wake the potentially-idle transmit channel. */ - iowrite32(0x10001000, yp->base + TxCtrl); - - if (yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE) - netif_start_queue (dev); /* Typical path */ - else - yp->tx_full = 1; - - if (yellowfin_debug > 4) { - netdev_printk(KERN_DEBUG, dev, "Yellowfin transmit frame #%d queued in slot %d\n", - yp->cur_tx, entry); - } - return NETDEV_TX_OK; -} - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance) -{ - struct net_device *dev = dev_instance; - struct yellowfin_private *yp; - void __iomem *ioaddr; - int boguscnt = max_interrupt_work; - unsigned int handled = 0; - - yp = netdev_priv(dev); - ioaddr = yp->base; - - spin_lock (&yp->lock); - - do { - u16 intr_status = ioread16(ioaddr + IntrClear); - - if (yellowfin_debug > 4) - netdev_printk(KERN_DEBUG, dev, "Yellowfin interrupt, status %04x\n", - intr_status); - - if (intr_status == 0) - break; - handled = 1; - - if (intr_status & (IntrRxDone | IntrEarlyRx)) { - yellowfin_rx(dev); - iowrite32(0x10001000, ioaddr + RxCtrl); /* Wake Rx engine. */ - } - -#ifdef NO_TXSTATS - for (; yp->cur_tx - yp->dirty_tx > 0; yp->dirty_tx++) { - int entry = yp->dirty_tx % TX_RING_SIZE; - struct sk_buff *skb; - - if (yp->tx_ring[entry].result_status == 0) - break; - skb = yp->tx_skbuff[entry]; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - /* Free the original skb. */ - dma_unmap_single(&yp->pci_dev->dev, - le32_to_cpu(yp->tx_ring[entry].addr), - skb->len, DMA_TO_DEVICE); - dev_consume_skb_irq(skb); - yp->tx_skbuff[entry] = NULL; - } - if (yp->tx_full && - yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE - 4) { - /* The ring is no longer full, clear tbusy. */ - yp->tx_full = 0; - netif_wake_queue(dev); - } -#else - if ((intr_status & IntrTxDone) || (yp->tx_tail_desc->tx_errs)) { - unsigned dirty_tx = yp->dirty_tx; - - for (dirty_tx = yp->dirty_tx; yp->cur_tx - dirty_tx > 0; - dirty_tx++) { - /* Todo: optimize this. */ - int entry = dirty_tx % TX_RING_SIZE; - u16 tx_errs = yp->tx_status[entry].tx_errs; - struct sk_buff *skb; - -#ifndef final_version - if (yellowfin_debug > 5) - netdev_printk(KERN_DEBUG, dev, "Tx queue %d check, Tx status %04x %04x %04x %04x\n", - entry, - yp->tx_status[entry].tx_cnt, - yp->tx_status[entry].tx_errs, - yp->tx_status[entry].total_tx_cnt, - yp->tx_status[entry].paused); -#endif - if (tx_errs == 0) - break; /* It still hasn't been Txed */ - skb = yp->tx_skbuff[entry]; - if (tx_errs & 0xF810) { - /* There was an major error, log it. */ -#ifndef final_version - if (yellowfin_debug > 1) - netdev_printk(KERN_DEBUG, dev, "Transmit error, Tx status %04x\n", - tx_errs); -#endif - dev->stats.tx_errors++; - if (tx_errs & 0xF800) dev->stats.tx_aborted_errors++; - if (tx_errs & 0x0800) dev->stats.tx_carrier_errors++; - if (tx_errs & 0x2000) dev->stats.tx_window_errors++; - if (tx_errs & 0x8000) dev->stats.tx_fifo_errors++; - } else { -#ifndef final_version - if (yellowfin_debug > 4) - netdev_printk(KERN_DEBUG, dev, "Normal transmit, Tx status %04x\n", - tx_errs); -#endif - dev->stats.tx_bytes += skb->len; - dev->stats.collisions += tx_errs & 15; - dev->stats.tx_packets++; - } - /* Free the original skb. */ - dma_unmap_single(&yp->pci_dev->dev, - yp->tx_ring[entry << 1].addr, - skb->len, DMA_TO_DEVICE); - dev_consume_skb_irq(skb); - yp->tx_skbuff[entry] = 0; - /* Mark status as empty. */ - yp->tx_status[entry].tx_errs = 0; - } - -#ifndef final_version - if (yp->cur_tx - dirty_tx > TX_RING_SIZE) { - netdev_err(dev, "Out-of-sync dirty pointer, %d vs. %d, full=%d\n", - dirty_tx, yp->cur_tx, yp->tx_full); - dirty_tx += TX_RING_SIZE; - } -#endif - - if (yp->tx_full && - yp->cur_tx - dirty_tx < TX_QUEUE_SIZE - 2) { - /* The ring is no longer full, clear tbusy. */ - yp->tx_full = 0; - netif_wake_queue(dev); - } - - yp->dirty_tx = dirty_tx; - yp->tx_tail_desc = &yp->tx_status[dirty_tx % TX_RING_SIZE]; - } -#endif - - /* Log errors and other uncommon events. */ - if (intr_status & 0x2ee) /* Abnormal error summary. */ - yellowfin_error(dev, intr_status); - - if (--boguscnt < 0) { - netdev_warn(dev, "Too much work at interrupt, status=%#04x\n", - intr_status); - break; - } - } while (1); - - if (yellowfin_debug > 3) - netdev_printk(KERN_DEBUG, dev, "exiting interrupt, status=%#04x\n", - ioread16(ioaddr + IntrStatus)); - - spin_unlock (&yp->lock); - return IRQ_RETVAL(handled); -} - -/* This routine is logically part of the interrupt handler, but separated - for clarity and better register allocation. */ -static int yellowfin_rx(struct net_device *dev) -{ - struct yellowfin_private *yp = netdev_priv(dev); - int entry = yp->cur_rx % RX_RING_SIZE; - int boguscnt = yp->dirty_rx + RX_RING_SIZE - yp->cur_rx; - - if (yellowfin_debug > 4) { - printk(KERN_DEBUG " In yellowfin_rx(), entry %d status %08x\n", - entry, yp->rx_ring[entry].result_status); - printk(KERN_DEBUG " #%d desc. %08x %08x %08x\n", - entry, yp->rx_ring[entry].dbdma_cmd, yp->rx_ring[entry].addr, - yp->rx_ring[entry].result_status); - } - - /* If EOP is set on the next entry, it's a new packet. Send it up. */ - while (1) { - struct yellowfin_desc *desc = &yp->rx_ring[entry]; - struct sk_buff *rx_skb = yp->rx_skbuff[entry]; - s16 frame_status; - u16 desc_status; - int data_size, __maybe_unused yf_size; - u8 *buf_addr; - - if(!desc->result_status) - break; - dma_sync_single_for_cpu(&yp->pci_dev->dev, - le32_to_cpu(desc->addr), - yp->rx_buf_sz, DMA_FROM_DEVICE); - desc_status = le32_to_cpu(desc->result_status) >> 16; - buf_addr = rx_skb->data; - data_size = (le32_to_cpu(desc->dbdma_cmd) - - le32_to_cpu(desc->result_status)) & 0xffff; - frame_status = get_unaligned_le16(&(buf_addr[data_size - 2])); - if (yellowfin_debug > 4) - printk(KERN_DEBUG " %s() status was %04x\n", - __func__, frame_status); - if (--boguscnt < 0) - break; - - yf_size = sizeof(struct yellowfin_desc); - - if ( ! (desc_status & RX_EOP)) { - if (data_size != 0) - netdev_warn(dev, "Oversized Ethernet frame spanned multiple buffers, status %04x, data_size %d!\n", - desc_status, data_size); - dev->stats.rx_length_errors++; - } else if ((yp->drv_flags & IsGigabit) && (frame_status & 0x0038)) { - /* There was a error. */ - if (yellowfin_debug > 3) - printk(KERN_DEBUG " %s() Rx error was %04x\n", - __func__, frame_status); - dev->stats.rx_errors++; - if (frame_status & 0x0060) dev->stats.rx_length_errors++; - if (frame_status & 0x0008) dev->stats.rx_frame_errors++; - if (frame_status & 0x0010) dev->stats.rx_crc_errors++; - if (frame_status < 0) dev->stats.rx_dropped++; - } else if ( !(yp->drv_flags & IsGigabit) && - ((buf_addr[data_size-1] & 0x85) || buf_addr[data_size-2] & 0xC0)) { - u8 status1 = buf_addr[data_size-2]; - u8 status2 = buf_addr[data_size-1]; - dev->stats.rx_errors++; - if (status1 & 0xC0) dev->stats.rx_length_errors++; - if (status2 & 0x03) dev->stats.rx_frame_errors++; - if (status2 & 0x04) dev->stats.rx_crc_errors++; - if (status2 & 0x80) dev->stats.rx_dropped++; -#ifdef YF_PROTOTYPE /* Support for prototype hardware errata. */ - } else if ((yp->flags & HasMACAddrBug) && - !ether_addr_equal(le32_to_cpu(yp->rx_ring_dma + - entry * yf_size), - dev->dev_addr) && - !ether_addr_equal(le32_to_cpu(yp->rx_ring_dma + - entry * yf_size), - "\377\377\377\377\377\377")) { - if (bogus_rx++ == 0) - netdev_warn(dev, "Bad frame to %pM\n", - buf_addr); -#endif - } else { - struct sk_buff *skb; - int pkt_len = data_size - - (yp->chip_id ? 7 : 8 + buf_addr[data_size - 8]); - /* To verify: Yellowfin Length should omit the CRC! */ - -#ifndef final_version - if (yellowfin_debug > 4) - printk(KERN_DEBUG " %s() normal Rx pkt length %d of %d, bogus_cnt %d\n", - __func__, pkt_len, data_size, boguscnt); -#endif - /* Check if the packet is long enough to just pass up the skbuff - without copying to a properly sized skbuff. */ - if (pkt_len > rx_copybreak) { - skb_put(skb = rx_skb, pkt_len); - dma_unmap_single(&yp->pci_dev->dev, - le32_to_cpu(yp->rx_ring[entry].addr), - yp->rx_buf_sz, - DMA_FROM_DEVICE); - yp->rx_skbuff[entry] = NULL; - } else { - skb = netdev_alloc_skb(dev, pkt_len + 2); - if (skb == NULL) - break; - skb_reserve(skb, 2); /* 16 byte align the IP header */ - skb_copy_to_linear_data(skb, rx_skb->data, pkt_len); - skb_put(skb, pkt_len); - dma_sync_single_for_device(&yp->pci_dev->dev, - le32_to_cpu(desc->addr), - yp->rx_buf_sz, - DMA_FROM_DEVICE); - } - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } - entry = (++yp->cur_rx) % RX_RING_SIZE; - } - - /* Refill the Rx ring buffers. */ - for (; yp->cur_rx - yp->dirty_rx > 0; yp->dirty_rx++) { - entry = yp->dirty_rx % RX_RING_SIZE; - if (yp->rx_skbuff[entry] == NULL) { - struct sk_buff *skb = netdev_alloc_skb(dev, yp->rx_buf_sz + 2); - if (skb == NULL) - break; /* Better luck next round. */ - yp->rx_skbuff[entry] = skb; - skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - yp->rx_ring[entry].addr = cpu_to_le32(dma_map_single(&yp->pci_dev->dev, - skb->data, - yp->rx_buf_sz, - DMA_FROM_DEVICE)); - } - yp->rx_ring[entry].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->rx_ring[entry].result_status = 0; /* Clear complete bit. */ - if (entry != 0) - yp->rx_ring[entry - 1].dbdma_cmd = - cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | yp->rx_buf_sz); - else - yp->rx_ring[RX_RING_SIZE - 1].dbdma_cmd = - cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | BRANCH_ALWAYS - | yp->rx_buf_sz); - } - - return 0; -} - -static void yellowfin_error(struct net_device *dev, int intr_status) -{ - netdev_err(dev, "Something Wicked happened! %04x\n", intr_status); - /* Hmmmmm, it's not clear what to do here. */ - if (intr_status & (IntrTxPCIErr | IntrTxPCIFault)) - dev->stats.tx_errors++; - if (intr_status & (IntrRxPCIErr | IntrRxPCIFault)) - dev->stats.rx_errors++; -} - -static int yellowfin_close(struct net_device *dev) -{ - struct yellowfin_private *yp = netdev_priv(dev); - void __iomem *ioaddr = yp->base; - int i; - - netif_stop_queue (dev); - - if (yellowfin_debug > 1) { - netdev_printk(KERN_DEBUG, dev, "Shutting down ethercard, status was Tx %04x Rx %04x Int %02x\n", - ioread16(ioaddr + TxStatus), - ioread16(ioaddr + RxStatus), - ioread16(ioaddr + IntrStatus)); - netdev_printk(KERN_DEBUG, dev, "Queue pointers were Tx %d / %d, Rx %d / %d\n", - yp->cur_tx, yp->dirty_tx, - yp->cur_rx, yp->dirty_rx); - } - - /* Disable interrupts by clearing the interrupt mask. */ - iowrite16(0x0000, ioaddr + IntrEnb); - - /* Stop the chip's Tx and Rx processes. */ - iowrite32(0x80000000, ioaddr + RxCtrl); - iowrite32(0x80000000, ioaddr + TxCtrl); - - timer_delete(&yp->timer); - -#if defined(__i386__) - if (yellowfin_debug > 2) { - printk(KERN_DEBUG " Tx ring at %08llx:\n", - (unsigned long long)yp->tx_ring_dma); - for (i = 0; i < TX_RING_SIZE*2; i++) - printk(KERN_DEBUG " %c #%d desc. %08x %08x %08x %08x\n", - ioread32(ioaddr + TxPtr) == (long)&yp->tx_ring[i] ? '>' : ' ', - i, yp->tx_ring[i].dbdma_cmd, yp->tx_ring[i].addr, - yp->tx_ring[i].branch_addr, yp->tx_ring[i].result_status); - printk(KERN_DEBUG " Tx status %p:\n", yp->tx_status); - for (i = 0; i < TX_RING_SIZE; i++) - printk(KERN_DEBUG " #%d status %04x %04x %04x %04x\n", - i, yp->tx_status[i].tx_cnt, yp->tx_status[i].tx_errs, - yp->tx_status[i].total_tx_cnt, yp->tx_status[i].paused); - - printk(KERN_DEBUG " Rx ring %08llx:\n", - (unsigned long long)yp->rx_ring_dma); - for (i = 0; i < RX_RING_SIZE; i++) { - printk(KERN_DEBUG " %c #%d desc. %08x %08x %08x\n", - ioread32(ioaddr + RxPtr) == (long)&yp->rx_ring[i] ? '>' : ' ', - i, yp->rx_ring[i].dbdma_cmd, yp->rx_ring[i].addr, - yp->rx_ring[i].result_status); - if (yellowfin_debug > 6) { - if (get_unaligned((u8*)yp->rx_ring[i].addr) != 0x69) { - int j; - - printk(KERN_DEBUG); - for (j = 0; j < 0x50; j++) - pr_cont(" %04x", - get_unaligned(((u16*)yp->rx_ring[i].addr) + j)); - pr_cont("\n"); - } - } - } - } -#endif /* __i386__ debugging only */ - - free_irq(yp->pci_dev->irq, dev); - - /* Free all the skbuffs in the Rx queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { - yp->rx_ring[i].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->rx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */ - if (yp->rx_skbuff[i]) { - dev_kfree_skb(yp->rx_skbuff[i]); - } - yp->rx_skbuff[i] = NULL; - } - for (i = 0; i < TX_RING_SIZE; i++) { - dev_kfree_skb(yp->tx_skbuff[i]); - yp->tx_skbuff[i] = NULL; - } - -#ifdef YF_PROTOTYPE /* Support for prototype hardware errata. */ - if (yellowfin_debug > 0) { - netdev_printk(KERN_DEBUG, dev, "Received %d frames that we should not have\n", - bogus_rx); - } -#endif - - return 0; -} - -/* Set or clear the multicast filter for this adaptor. */ - -static void set_rx_mode(struct net_device *dev) -{ - struct yellowfin_private *yp = netdev_priv(dev); - void __iomem *ioaddr = yp->base; - u16 cfg_value = ioread16(ioaddr + Cnfg); - - /* Stop the Rx process to change any value. */ - iowrite16(cfg_value & ~0x1000, ioaddr + Cnfg); - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - iowrite16(0x000F, ioaddr + AddrMode); - } else if ((netdev_mc_count(dev) > 64) || - (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter well, or accept all multicasts. */ - iowrite16(0x000B, ioaddr + AddrMode); - } else if (!netdev_mc_empty(dev)) { /* Must use the multicast hash table. */ - struct netdev_hw_addr *ha; - u16 hash_table[4]; - int i; - - memset(hash_table, 0, sizeof(hash_table)); - netdev_for_each_mc_addr(ha, dev) { - unsigned int bit; - - /* Due to a bug in the early chip versions, multiple filter - slots must be set for each address. */ - if (yp->drv_flags & HasMulticastBug) { - bit = (ether_crc_le(3, ha->addr) >> 3) & 0x3f; - hash_table[bit >> 4] |= (1 << bit); - bit = (ether_crc_le(4, ha->addr) >> 3) & 0x3f; - hash_table[bit >> 4] |= (1 << bit); - bit = (ether_crc_le(5, ha->addr) >> 3) & 0x3f; - hash_table[bit >> 4] |= (1 << bit); - } - bit = (ether_crc_le(6, ha->addr) >> 3) & 0x3f; - hash_table[bit >> 4] |= (1 << bit); - } - /* Copy the hash table to the chip. */ - for (i = 0; i < 4; i++) - iowrite16(hash_table[i], ioaddr + HashTbl + i*2); - iowrite16(0x0003, ioaddr + AddrMode); - } else { /* Normal, unicast/broadcast-only mode. */ - iowrite16(0x0001, ioaddr + AddrMode); - } - /* Restart the Rx process. */ - iowrite16(cfg_value | 0x1000, ioaddr + Cnfg); -} - -static void yellowfin_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - struct yellowfin_private *np = netdev_priv(dev); - - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - strscpy(info->version, DRV_VERSION, sizeof(info->version)); - strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); -} - -static const struct ethtool_ops ethtool_ops = { - .get_drvinfo = yellowfin_get_drvinfo -}; - -static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct yellowfin_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->base; - struct mii_ioctl_data *data = if_mii(rq); - - switch(cmd) { - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - data->phy_id = np->phys[0] & 0x1f; - fallthrough; - - case SIOCGMIIREG: /* Read MII PHY register. */ - data->val_out = mdio_read(ioaddr, data->phy_id & 0x1f, data->reg_num & 0x1f); - return 0; - - case SIOCSMIIREG: /* Write MII PHY register. */ - if (data->phy_id == np->phys[0]) { - u16 value = data->val_in; - switch (data->reg_num) { - case 0: - /* Check for autonegotiation on or reset. */ - np->medialock = (value & 0x9000) ? 0 : 1; - if (np->medialock) - np->full_duplex = (value & 0x0100) ? 1 : 0; - break; - case 4: np->advertising = value; break; - } - /* Perhaps check_duplex(dev), depending on chip semantics. */ - } - mdio_write(ioaddr, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in); - return 0; - default: - return -EOPNOTSUPP; - } -} - - -static void yellowfin_remove_one(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct yellowfin_private *np; - - BUG_ON(!dev); - np = netdev_priv(dev); - - dma_free_coherent(&pdev->dev, STATUS_TOTAL_SIZE, np->tx_status, - np->tx_status_dma); - dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring, - np->rx_ring_dma); - dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring, - np->tx_ring_dma); - unregister_netdev (dev); - - pci_iounmap(pdev, np->base); - - pci_release_regions (pdev); - - free_netdev (dev); -} - - -static struct pci_driver yellowfin_driver = { - .name = DRV_NAME, - .id_table = yellowfin_pci_tbl, - .probe = yellowfin_init_one, - .remove = yellowfin_remove_one, -}; - - -static int __init yellowfin_init (void) -{ -/* when a module, this is printed whether or not devices are found in probe */ -#ifdef MODULE - printk(version); -#endif - return pci_register_driver(&yellowfin_driver); -} - - -static void __exit yellowfin_cleanup (void) -{ - pci_unregister_driver (&yellowfin_driver); -} - - -module_init(yellowfin_init); -module_exit(yellowfin_cleanup); diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 42c6dcfb1f0f..dd75c47758e1 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -5103,6 +5103,13 @@ static int qed_init_wfq_param(struct qed_hwfn *p_hwfn, return -EINVAL; } + /* All vports are already or become configured, nothing to distribute */ + if (non_requested_count == 0) { + p_hwfn->qm_info.wfq_data[vport_id].min_speed = req_rate; + p_hwfn->qm_info.wfq_data[vport_id].configured = true; + return 0; + } + total_left_rate = min_pf_rate - total_req_min_rate; left_rate_per_vp = total_left_rate / non_requested_count; diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 1dbfadb2a881..5f88733094d0 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -1108,9 +1108,12 @@ static int ravb_stop_dma(struct net_device *ndev) /* Request for transmission suspension */ ravb_modify(ndev, CCC, CCC_DTSR, CCC_DTSR); - error = ravb_wait(ndev, CSR, CSR_DTS, CSR_DTS); - if (error) - netdev_err(ndev, "failed to stop AXI BUS\n"); + /* Access to URAM will not be suspended if WoL is enabled. */ + if (!priv->wol_enabled) { + error = ravb_wait(ndev, CSR, CSR_DTS, CSR_DTS); + if (error) + netdev_err(ndev, "failed to stop AXI BUS\n"); + } /* Stop AVB-DMAC process */ return ravb_set_opmode(ndev, CCC_OPC_CONFIG); diff --git a/drivers/net/ethernet/renesas/rtsn.c b/drivers/net/ethernet/renesas/rtsn.c index 03a2669f0518..ee8381b60b8d 100644 --- a/drivers/net/ethernet/renesas/rtsn.c +++ b/drivers/net/ethernet/renesas/rtsn.c @@ -797,11 +797,11 @@ static int rtsn_mdio_alloc(struct rtsn_private *priv) /* Enter config mode before registering the MDIO bus */ ret = rtsn_reset(priv); if (ret) - goto out_free_bus; + goto out_put_node; ret = rtsn_change_mode(priv, OCR_OPC_CONFIG); if (ret) - goto out_free_bus; + goto out_put_node; rtsn_modify(priv, MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK, MPIC_PSMCS_DEFAULT | MPIC_PSMHT_DEFAULT); @@ -824,6 +824,8 @@ static int rtsn_mdio_alloc(struct rtsn_private *priv) return 0; +out_put_node: + of_node_put(mdio_node); out_free_bus: mdiobus_free(mii); return ret; diff --git a/drivers/net/ethernet/sfc/efx_devlink.c b/drivers/net/ethernet/sfc/efx_devlink.c index d842c60dfc10..e5c6f81af48b 100644 --- a/drivers/net/ethernet/sfc/efx_devlink.c +++ b/drivers/net/ethernet/sfc/efx_devlink.c @@ -531,7 +531,7 @@ static int efx_devlink_info_running_versions(struct efx_nic *efx, if (rc || outlength < MC_CMD_GET_VERSION_OUT_LEN) { netif_err(efx, drv, efx->net_dev, "mcdi MC_CMD_GET_VERSION failed\n"); - return rc; + return rc ?: -EIO; } /* Handle previous output */ diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig index 13ce9086a9ca..66bca803b19c 100644 --- a/drivers/net/ethernet/smsc/Kconfig +++ b/drivers/net/ethernet/smsc/Kconfig @@ -19,21 +19,6 @@ config NET_VENDOR_SMSC if NET_VENDOR_SMSC -config SMC9194 - tristate "SMC 9194 support" - depends on ISA - select CRC32 - select NETDEV_LEGACY_INIT - help - This is support for the SMC9xxx based Ethernet cards. Choose this - option if you have a DELL laptop with the docking station, or - another SMC9192/9194 based chipset. Say Y if you want it compiled - into the kernel, and read the file - . - - To compile this driver as a module, choose M here. The module - will be called smc9194. - config SMC91X tristate "SMC 91C9x/91C1xxx support" select CRC32 @@ -52,18 +37,6 @@ config SMC91X The module will be called smc91x. If you want to compile it as a module, say M here and read . -config PCMCIA_SMC91C92 - tristate "SMC 91Cxx PCMCIA support" - depends on PCMCIA && HAS_IOPORT - select CRC32 - select MII - help - Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA - (PC-card) Ethernet or Fast Ethernet card to your computer. - - To compile this driver as a module, choose M here: the module will be - called smc91c92_cs. If unsure, say N. - config EPIC100 tristate "SMC EtherPower II" depends on PCI diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile index 1501fa364c13..ab6f03f7ba17 100644 --- a/drivers/net/ethernet/smsc/Makefile +++ b/drivers/net/ethernet/smsc/Makefile @@ -3,9 +3,7 @@ # Makefile for the SMSC network device drivers. # -obj-$(CONFIG_SMC9194) += smc9194.o obj-$(CONFIG_SMC91X) += smc91x.o -obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o obj-$(CONFIG_EPIC100) += epic100.o obj-$(CONFIG_SMSC9420) += smsc9420.o obj-$(CONFIG_SMSC911X) += smsc911x.o diff --git a/drivers/net/ethernet/smsc/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c deleted file mode 100644 index e2e7b1c68563..000000000000 --- a/drivers/net/ethernet/smsc/smc9194.c +++ /dev/null @@ -1,1535 +0,0 @@ -/*------------------------------------------------------------------------ - . smc9194.c - . This is a driver for SMC's 9000 series of Ethernet cards. - . - . Copyright (C) 1996 by Erik Stahlman - . This software may be used and distributed according to the terms - . of the GNU General Public License, incorporated herein by reference. - . - . "Features" of the SMC chip: - . 4608 byte packet memory. ( for the 91C92. Others have more ) - . EEPROM for configuration - . AUI/TP selection ( mine has 10Base2/10BaseT select ) - . - . Arguments: - . io = for the base address - . irq = for the IRQ - . ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 ) - . - . author: - . Erik Stahlman ( erik@vt.edu ) - . contributors: - . Arnaldo Carvalho de Melo - . - . Hardware multicast code from Peter Cammaert ( pc@denkart.be ) - . - . Sources: - . o SMC databook - . o skeleton.c by Donald Becker ( becker@scyld.com ) - . o ( a LOT of advice from Becker as well ) - . - . History: - . 12/07/95 Erik Stahlman written, got receive/xmit handled - . 01/03/96 Erik Stahlman worked out some bugs, actually usable!!! :-) - . 01/06/96 Erik Stahlman cleaned up some, better testing, etc - . 01/29/96 Erik Stahlman fixed autoirq, added multicast - . 02/01/96 Erik Stahlman 1. disabled all interrupts in smc_reset - . 2. got rid of post-decrementing bug -- UGH. - . 02/13/96 Erik Stahlman Tried to fix autoirq failure. Added more - . descriptive error messages. - . 02/15/96 Erik Stahlman Fixed typo that caused detection failure - . 02/23/96 Erik Stahlman Modified it to fit into kernel tree - . Added support to change hardware address - . Cleared stats on opens - . 02/26/96 Erik Stahlman Trial support for Kernel 1.2.13 - . Kludge for automatic IRQ detection - . 03/04/96 Erik Stahlman Fixed kernel 1.3.70 + - . Fixed bug reported by Gardner Buchanan in - . smc_enable, with outw instead of outb - . 03/06/96 Erik Stahlman Added hardware multicast from Peter Cammaert - . 04/14/00 Heiko Pruessing (SMA Regelsysteme) Fixed bug in chip memory - . allocation - . 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet - . 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ" - . 11/08/01 Matt Domsch Use common crc32 function - ----------------------------------------------------------------------------*/ - -static const char version[] = - "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)"; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "smc9194.h" - -#define DRV_NAME "smc9194" - -/*------------------------------------------------------------------------ - . - . Configuration options, for the experienced user to change. - . - -------------------------------------------------------------------------*/ - -/* - . Do you want to use 32 bit xfers? This should work on all chips, as - . the chipset is designed to accommodate them. -*/ -#ifdef __sh__ -#undef USE_32_BIT -#else -#define USE_32_BIT 1 -#endif - -/* - .the SMC9194 can be at any of the following port addresses. To change, - .for a slightly different card, you can add it to the array. Keep in - .mind that the array must end in zero. -*/ - -struct devlist { - unsigned int port; - unsigned int irq; -}; - -static struct devlist smc_devlist[] __initdata = { - {.port = 0x200, .irq = 0}, - {.port = 0x220, .irq = 0}, - {.port = 0x240, .irq = 0}, - {.port = 0x260, .irq = 0}, - {.port = 0x280, .irq = 0}, - {.port = 0x2A0, .irq = 0}, - {.port = 0x2C0, .irq = 0}, - {.port = 0x2E0, .irq = 0}, - {.port = 0x300, .irq = 0}, - {.port = 0x320, .irq = 0}, - {.port = 0x340, .irq = 0}, - {.port = 0x360, .irq = 0}, - {.port = 0x380, .irq = 0}, - {.port = 0x3A0, .irq = 0}, - {.port = 0x3C0, .irq = 0}, - {.port = 0x3E0, .irq = 0}, - {.port = 0, .irq = 0}, -}; -/* - . Wait time for memory to be free. This probably shouldn't be - . tuned that much, as waiting for this means nothing else happens - . in the system -*/ -#define MEMORY_WAIT_TIME 16 - -/* - . DEBUGGING LEVELS - . - . 0 for normal operation - . 1 for slightly more details - . >2 for various levels of increasingly useless information - . 2 for interrupt tracking, status flags - . 3 for packet dumps, etc. -*/ -#define SMC_DEBUG 0 - -#if (SMC_DEBUG > 2 ) -#define PRINTK3(x) printk x -#else -#define PRINTK3(x) -#endif - -#if SMC_DEBUG > 1 -#define PRINTK2(x) printk x -#else -#define PRINTK2(x) -#endif - -#ifdef SMC_DEBUG -#define PRINTK(x) printk x -#else -#define PRINTK(x) -#endif - - -/*------------------------------------------------------------------------ - . - . The internal workings of the driver. If you are changing anything - . here with the SMC stuff, you should have the datasheet and known - . what you are doing. - . - -------------------------------------------------------------------------*/ -#define CARDNAME "SMC9194" - - -/* store this information for the driver.. */ -struct smc_local { - /* - If I have to wait until memory is available to send - a packet, I will store the skbuff here, until I get the - desired memory. Then, I'll send it out and free it. - */ - struct sk_buff * saved_skb; - - /* - . This keeps track of how many packets that I have - . sent out. When an TX_EMPTY interrupt comes, I know - . that all of these have been sent. - */ - int packets_waiting; -}; - - -/*----------------------------------------------------------------- - . - . The driver can be entered at any of the following entry points. - . - .------------------------------------------------------------------ */ - -/* - . This is called by register_netdev(). It is responsible for - . checking the portlist for the SMC9000 series chipset. If it finds - . one, then it will initialize the device, find the hardware information, - . and sets up the appropriate device parameters. - . NOTE: Interrupts are *OFF* when this procedure is called. - . - . NB:This shouldn't be static since it is referred to externally. -*/ -struct net_device *smc_init(int unit); - -/* - . The kernel calls this function when someone wants to use the device, - . typically 'ifconfig ethX up'. -*/ -static int smc_open(struct net_device *dev); - -/* - . Our watchdog timed out. Called by the networking layer -*/ -static void smc_timeout(struct net_device *dev, unsigned int txqueue); - -/* - . This is called by the kernel in response to 'ifconfig ethX down'. It - . is responsible for cleaning up everything that the open routine - . does, and maybe putting the card into a powerdown state. -*/ -static int smc_close(struct net_device *dev); - -/* - . Finally, a call to set promiscuous mode ( for TCPDUMP and related - . programs ) and multicast modes. -*/ -static void smc_set_multicast_list(struct net_device *dev); - - -/*--------------------------------------------------------------- - . - . Interrupt level calls.. - . - ----------------------------------------------------------------*/ - -/* - . Handles the actual interrupt -*/ -static irqreturn_t smc_interrupt(int irq, void *); -/* - . This is a separate procedure to handle the receipt of a packet, to - . leave the interrupt code looking slightly cleaner -*/ -static inline void smc_rcv( struct net_device *dev ); -/* - . This handles a TX interrupt, which is only called when an error - . relating to a packet is sent. -*/ -static inline void smc_tx( struct net_device * dev ); - -/* - ------------------------------------------------------------ - . - . Internal routines - . - ------------------------------------------------------------ -*/ - -/* - . Test if a given location contains a chip, trying to cause as - . little damage as possible if it's not a SMC chip. -*/ -static int smc_probe(struct net_device *dev, int ioaddr); - -/* - . A rather simple routine to print out a packet for debugging purposes. -*/ -#if SMC_DEBUG > 2 -static void print_packet( byte *, int ); -#endif - -#define tx_done(dev) 1 - -/* this is called to actually send the packet to the chip */ -static void smc_hardware_send_packet( struct net_device * dev ); - -/* Since I am not sure if I will have enough room in the chip's ram - . to store the packet, I call this routine, which either sends it - . now, or generates an interrupt when the card is ready for the - . packet */ -static netdev_tx_t smc_wait_to_send_packet( struct sk_buff * skb, - struct net_device *dev ); - -/* this does a soft reset on the device */ -static void smc_reset( int ioaddr ); - -/* Enable Interrupts, Receive, and Transmit */ -static void smc_enable( int ioaddr ); - -/* this puts the device in an inactive state */ -static void smc_shutdown( int ioaddr ); - -/* This routine will find the IRQ of the driver if one is not - . specified in the input to the device. */ -static int smc_findirq( int ioaddr ); - -/* - . Function: smc_reset( int ioaddr ) - . Purpose: - . This sets the SMC91xx chip to its normal state, hopefully from whatever - . mess that any other DOS driver has put it in. - . - . Maybe I should reset more registers to defaults in here? SOFTRESET should - . do that for me. - . - . Method: - . 1. send a SOFT RESET - . 2. wait for it to finish - . 3. enable autorelease mode - . 4. reset the memory management unit - . 5. clear all interrupts - . -*/ -static void smc_reset( int ioaddr ) -{ - /* This resets the registers mostly to defaults, but doesn't - affect EEPROM. That seems unnecessary */ - SMC_SELECT_BANK( 0 ); - outw( RCR_SOFTRESET, ioaddr + RCR ); - - /* this should pause enough for the chip to be happy */ - SMC_DELAY( ); - - /* Set the transmit and receive configuration registers to - default values */ - outw( RCR_CLEAR, ioaddr + RCR ); - outw( TCR_CLEAR, ioaddr + TCR ); - - /* set the control register to automatically - release successfully transmitted packets, to make the best - use out of our limited memory */ - SMC_SELECT_BANK( 1 ); - outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL ); - - /* Reset the MMU */ - SMC_SELECT_BANK( 2 ); - outw( MC_RESET, ioaddr + MMU_CMD ); - - /* Note: It doesn't seem that waiting for the MMU busy is needed here, - but this is a place where future chipsets _COULD_ break. Be wary - of issuing another MMU command right after this */ - - outb( 0, ioaddr + INT_MASK ); -} - -/* - . Function: smc_enable - . Purpose: let the chip talk to the outside work - . Method: - . 1. Enable the transmitter - . 2. Enable the receiver - . 3. Enable interrupts -*/ -static void smc_enable( int ioaddr ) -{ - SMC_SELECT_BANK( 0 ); - /* see the header file for options in TCR/RCR NORMAL*/ - outw( TCR_NORMAL, ioaddr + TCR ); - outw( RCR_NORMAL, ioaddr + RCR ); - - /* now, enable interrupts */ - SMC_SELECT_BANK( 2 ); - outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK ); -} - -/* - . Function: smc_shutdown - . Purpose: closes down the SMC91xxx chip. - . Method: - . 1. zero the interrupt mask - . 2. clear the enable receive flag - . 3. clear the enable xmit flags - . - . TODO: - . (1) maybe utilize power down mode. - . Why not yet? Because while the chip will go into power down mode, - . the manual says that it will wake up in response to any I/O requests - . in the register space. Empirical results do not show this working. -*/ -static void smc_shutdown( int ioaddr ) -{ - /* no more interrupts for me */ - SMC_SELECT_BANK( 2 ); - outb( 0, ioaddr + INT_MASK ); - - /* and tell the card to stay away from that nasty outside world */ - SMC_SELECT_BANK( 0 ); - outb( RCR_CLEAR, ioaddr + RCR ); - outb( TCR_CLEAR, ioaddr + TCR ); -#if 0 - /* finally, shut the chip down */ - SMC_SELECT_BANK( 1 ); - outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL ); -#endif -} - - -/* - . Function: smc_setmulticast( int ioaddr, struct net_device *dev ) - . Purpose: - . This sets the internal hardware table to filter out unwanted multicast - . packets before they take up memory. - . - . The SMC chip uses a hash table where the high 6 bits of the CRC of - . address are the offset into the table. If that bit is 1, then the - . multicast packet is accepted. Otherwise, it's dropped silently. - . - . To use the 6 bits as an offset into the table, the high 3 bits are the - . number of the 8 bit register, while the low 3 bits are the bit within - . that register. - . - . This routine is based very heavily on the one provided by Peter Cammaert. -*/ - - -static void smc_setmulticast(int ioaddr, struct net_device *dev) -{ - int i; - unsigned char multicast_table[ 8 ]; - struct netdev_hw_addr *ha; - /* table for flipping the order of 3 bits */ - unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; - - /* start with a table of all zeros: reject all */ - memset( multicast_table, 0, sizeof( multicast_table ) ); - - netdev_for_each_mc_addr(ha, dev) { - int position; - - /* only use the low order bits */ - position = ether_crc_le(6, ha->addr) & 0x3f; - - /* do some messy swapping to put the bit in the right spot */ - multicast_table[invert3[position&7]] |= - (1<>3)&7]); - - } - /* now, the table can be loaded into the chipset */ - SMC_SELECT_BANK( 3 ); - - for ( i = 0; i < 8 ; i++ ) { - outb( multicast_table[i], ioaddr + MULTICAST1 + i ); - } -} - -/* - . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * ) - . Purpose: - . Attempt to allocate memory for a packet, if chip-memory is not - . available, then tell the card to generate an interrupt when it - . is available. - . - . Algorithm: - . - . o if the saved_skb is not currently null, then drop this packet - . on the floor. This should never happen, because of TBUSY. - . o if the saved_skb is null, then replace it with the current packet, - . o See if I can sending it now. - . o (NO): Enable interrupts and let the interrupt handler deal with it. - . o (YES):Send it now. -*/ -static netdev_tx_t smc_wait_to_send_packet(struct sk_buff *skb, - struct net_device *dev) -{ - struct smc_local *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - word length; - unsigned short numPages; - word time_out; - - netif_stop_queue(dev); - /* Well, I want to send the packet.. but I don't know - if I can send it right now... */ - - if ( lp->saved_skb) { - /* THIS SHOULD NEVER HAPPEN. */ - dev->stats.tx_aborted_errors++; - printk(CARDNAME": Bad Craziness - sent packet while busy.\n" ); - return NETDEV_TX_BUSY; - } - lp->saved_skb = skb; - - length = skb->len; - - if (length < ETH_ZLEN) { - if (skb_padto(skb, ETH_ZLEN)) { - netif_wake_queue(dev); - return NETDEV_TX_OK; - } - length = ETH_ZLEN; - } - - /* - ** The MMU wants the number of pages to be the number of 256 bytes - ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) - ** - ** Pkt size for allocating is data length +6 (for additional status words, - ** length and ctl!) If odd size last byte is included in this header. - */ - numPages = ((length & 0xfffe) + 6) / 256; - - if (numPages > 7 ) { - printk(CARDNAME": Far too big packet error.\n"); - /* freeing the packet is a good thing here... but should - . any packets of this size get down here? */ - dev_kfree_skb (skb); - lp->saved_skb = NULL; - /* this IS an error, but, i don't want the skb saved */ - netif_wake_queue(dev); - return NETDEV_TX_OK; - } - /* either way, a packet is waiting now */ - lp->packets_waiting++; - - /* now, try to allocate the memory */ - SMC_SELECT_BANK( 2 ); - outw( MC_ALLOC | numPages, ioaddr + MMU_CMD ); - /* - . Performance Hack - . - . wait a short amount of time.. if I can send a packet now, I send - . it now. Otherwise, I enable an interrupt and wait for one to be - . available. - . - . I could have handled this a slightly different way, by checking to - . see if any memory was available in the FREE MEMORY register. However, - . either way, I need to generate an allocation, and the allocation works - . no matter what, so I saw no point in checking free memory. - */ - time_out = MEMORY_WAIT_TIME; - do { - word status; - - status = inb( ioaddr + INTERRUPT ); - if ( status & IM_ALLOC_INT ) { - /* acknowledge the interrupt */ - outb( IM_ALLOC_INT, ioaddr + INTERRUPT ); - break; - } - } while ( -- time_out ); - - if ( !time_out ) { - /* oh well, wait until the chip finds memory later */ - SMC_ENABLE_INT( IM_ALLOC_INT ); - PRINTK2((CARDNAME": memory allocation deferred.\n")); - /* it's deferred, but I'll handle it later */ - return NETDEV_TX_OK; - } - /* or YES! I can send the packet now.. */ - smc_hardware_send_packet(dev); - netif_wake_queue(dev); - return NETDEV_TX_OK; -} - -/* - . Function: smc_hardware_send_packet(struct net_device * ) - . Purpose: - . This sends the actual packet to the SMC9xxx chip. - . - . Algorithm: - . First, see if a saved_skb is available. - . ( this should NOT be called if there is no 'saved_skb' - . Now, find the packet number that the chip allocated - . Point the data pointers at it in memory - . Set the length word in the chip's memory - . Dump the packet to chip memory - . Check if a last byte is needed ( odd length packet ) - . if so, set the control flag right - . Tell the card to send it - . Enable the transmit interrupt, so I know if it failed - . Free the kernel data if I actually sent it. -*/ -static void smc_hardware_send_packet( struct net_device * dev ) -{ - struct smc_local *lp = netdev_priv(dev); - byte packet_no; - struct sk_buff * skb = lp->saved_skb; - word length; - unsigned int ioaddr; - byte * buf; - - ioaddr = dev->base_addr; - - if ( !skb ) { - PRINTK((CARDNAME": In XMIT with no packet to send\n")); - return; - } - length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - buf = skb->data; - - /* If I get here, I _know_ there is a packet slot waiting for me */ - packet_no = inb( ioaddr + PNR_ARR + 1 ); - if ( packet_no & 0x80 ) { - /* or isn't there? BAD CHIP! */ - netdev_dbg(dev, CARDNAME": Memory allocation failed.\n"); - dev_kfree_skb_any(skb); - lp->saved_skb = NULL; - netif_wake_queue(dev); - return; - } - - /* we have a packet address, so tell the card to use it */ - outb( packet_no, ioaddr + PNR_ARR ); - - /* point to the beginning of the packet */ - outw( PTR_AUTOINC , ioaddr + POINTER ); - - PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length)); -#if SMC_DEBUG > 2 - print_packet( buf, length ); -#endif - - /* send the packet length ( +6 for status, length and ctl byte ) - and the status word ( set to zeros ) */ -#ifdef USE_32_BIT - outl( (length +6 ) << 16 , ioaddr + DATA_1 ); -#else - outw( 0, ioaddr + DATA_1 ); - /* send the packet length ( +6 for status words, length, and ctl*/ - outb( (length+6) & 0xFF,ioaddr + DATA_1 ); - outb( (length+6) >> 8 , ioaddr + DATA_1 ); -#endif - - /* send the actual data - . I _think_ it's faster to send the longs first, and then - . mop up by sending the last word. It depends heavily - . on alignment, at least on the 486. Maybe it would be - . a good idea to check which is optimal? But that could take - . almost as much time as is saved? - */ -#ifdef USE_32_BIT - if ( length & 0x2 ) { - outsl(ioaddr + DATA_1, buf, length >> 2 ); - outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1); - } - else - outsl(ioaddr + DATA_1, buf, length >> 2 ); -#else - outsw(ioaddr + DATA_1 , buf, (length ) >> 1); -#endif - /* Send the last byte, if there is one. */ - - if ( (length & 1) == 0 ) { - outw( 0, ioaddr + DATA_1 ); - } else { - outb( buf[length -1 ], ioaddr + DATA_1 ); - outb( 0x20, ioaddr + DATA_1); - } - - /* enable the interrupts */ - SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) ); - - /* and let the chipset deal with it */ - outw( MC_ENQUEUE , ioaddr + MMU_CMD ); - - PRINTK2((CARDNAME": Sent packet of length %d\n", length)); - - lp->saved_skb = NULL; - dev_kfree_skb_any (skb); - - netif_trans_update(dev); - - /* we can send another packet */ - netif_wake_queue(dev); -} - -/*------------------------------------------------------------------------- - | - | smc_init(int unit) - | Input parameters: - | dev->base_addr == 0, try to find all possible locations - | dev->base_addr == 1, return failure code - | dev->base_addr == 2, always allocate space, and return success - | dev->base_addr == this is the address to check - | - | Output: - | pointer to net_device or ERR_PTR(error) - | - --------------------------------------------------------------------------- -*/ -static int io; -static int irq; -static int ifport; - -struct net_device * __init smc_init(int unit) -{ - struct net_device *dev = alloc_etherdev(sizeof(struct smc_local)); - struct devlist *smcdev = smc_devlist; - int err = 0; - - if (!dev) - return ERR_PTR(-ENODEV); - - if (unit >= 0) { - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - io = dev->base_addr; - irq = dev->irq; - } - - if (io > 0x1ff) { /* Check a single specified location. */ - err = smc_probe(dev, io); - } else if (io != 0) { /* Don't probe at all. */ - err = -ENXIO; - } else { - for (;smcdev->port; smcdev++) { - if (smc_probe(dev, smcdev->port) == 0) - break; - } - if (!smcdev->port) - err = -ENODEV; - } - if (err) - goto out; - err = register_netdev(dev); - if (err) - goto out1; - return dev; -out1: - free_irq(dev->irq, dev); - release_region(dev->base_addr, SMC_IO_EXTENT); -out: - free_netdev(dev); - return ERR_PTR(err); -} - -/*---------------------------------------------------------------------- - . smc_findirq - . - . This routine has a simple purpose -- make the SMC chip generate an - . interrupt, so an auto-detect routine can detect it, and find the IRQ, - ------------------------------------------------------------------------ -*/ -static int __init smc_findirq(int ioaddr) -{ -#ifndef NO_AUTOPROBE - int timeout = 20; - unsigned long cookie; - - - cookie = probe_irq_on(); - - /* - * What I try to do here is trigger an ALLOC_INT. This is done - * by allocating a small chunk of memory, which will give an interrupt - * when done. - */ - - - SMC_SELECT_BANK(2); - /* enable ALLOCation interrupts ONLY */ - outb( IM_ALLOC_INT, ioaddr + INT_MASK ); - - /* - . Allocate 512 bytes of memory. Note that the chip was just - . reset so all the memory is available - */ - outw( MC_ALLOC | 1, ioaddr + MMU_CMD ); - - /* - . Wait until positive that the interrupt has been generated - */ - while ( timeout ) { - byte int_status; - - int_status = inb( ioaddr + INTERRUPT ); - - if ( int_status & IM_ALLOC_INT ) - break; /* got the interrupt */ - timeout--; - } - /* there is really nothing that I can do here if timeout fails, - as probe_irq_off will return a 0 anyway, which is what I - want in this case. Plus, the clean up is needed in both - cases. */ - - /* DELAY HERE! - On a fast machine, the status might change before the interrupt - is given to the processor. This means that the interrupt was - never detected, and probe_irq_off fails to report anything. - This should fix probe_irq_* problems. - */ - SMC_DELAY(); - SMC_DELAY(); - - /* and disable all interrupts again */ - outb( 0, ioaddr + INT_MASK ); - - /* and return what I found */ - return probe_irq_off(cookie); -#else /* NO_AUTOPROBE */ - struct devlist *smcdev; - for (smcdev = smc_devlist; smcdev->port; smcdev++) { - if (smcdev->port == ioaddr) - return smcdev->irq; - } - return 0; -#endif -} - -static const struct net_device_ops smc_netdev_ops = { - .ndo_open = smc_open, - .ndo_stop = smc_close, - .ndo_start_xmit = smc_wait_to_send_packet, - .ndo_tx_timeout = smc_timeout, - .ndo_set_rx_mode = smc_set_multicast_list, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -/*---------------------------------------------------------------------- - . Function: smc_probe( int ioaddr ) - . - . Purpose: - . Tests to see if a given ioaddr points to an SMC9xxx chip. - . Returns a 0 on success - . - . Algorithm: - . (1) see if the high byte of BANK_SELECT is 0x33 - . (2) compare the ioaddr with the base register's address - . (3) see if I recognize the chip ID in the appropriate register - . - .--------------------------------------------------------------------- - */ - -/*--------------------------------------------------------------- - . Here I do typical initialization tasks. - . - . o Initialize the structure if needed - . o print out my vanity message if not done so already - . o print out what type of hardware is detected - . o print out the ethernet address - . o find the IRQ - . o set up my private data - . o configure the dev structure with my subroutines - . o actually GRAB the irq. - . o GRAB the region - .----------------------------------------------------------------- -*/ -static int __init smc_probe(struct net_device *dev, int ioaddr) -{ - int i, memory, retval; - unsigned int bank; - - const char *version_string; - const char *if_string; - - /* registers */ - word revision_register; - word base_address_register; - word configuration_register; - word memory_info_register; - word memory_cfg_register; - u8 addr[ETH_ALEN]; - - /* Grab the region so that no one else tries to probe our ioports. */ - if (!request_region(ioaddr, SMC_IO_EXTENT, DRV_NAME)) - return -EBUSY; - - dev->irq = irq; - dev->if_port = ifport; - - /* First, see if the high byte is 0x33 */ - bank = inw( ioaddr + BANK_SELECT ); - if ( (bank & 0xFF00) != 0x3300 ) { - retval = -ENODEV; - goto err_out; - } - /* The above MIGHT indicate a device, but I need to write to further - test this. */ - outw( 0x0, ioaddr + BANK_SELECT ); - bank = inw( ioaddr + BANK_SELECT ); - if ( (bank & 0xFF00 ) != 0x3300 ) { - retval = -ENODEV; - goto err_out; - } - /* well, we've already written once, so hopefully another time won't - hurt. This time, I need to switch the bank register to bank 1, - so I can access the base address register */ - SMC_SELECT_BANK(1); - base_address_register = inw( ioaddr + BASE ); - if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) ) { - printk(CARDNAME ": IOADDR %x doesn't match configuration (%x). " - "Probably not a SMC chip\n", - ioaddr, base_address_register >> 3 & 0x3E0 ); - /* well, the base address register didn't match. Must not have - been a SMC chip after all. */ - retval = -ENODEV; - goto err_out; - } - - /* check if the revision register is something that I recognize. - These might need to be added to later, as future revisions - could be added. */ - SMC_SELECT_BANK(3); - revision_register = inw( ioaddr + REVISION ); - if ( !chip_ids[ ( revision_register >> 4 ) & 0xF ] ) { - /* I don't recognize this chip, so... */ - printk(CARDNAME ": IO %x: Unrecognized revision register:" - " %x, Contact author.\n", ioaddr, revision_register); - - retval = -ENODEV; - goto err_out; - } - - /* at this point I'll assume that the chip is an SMC9xxx. - It might be prudent to check a listing of MAC addresses - against the hardware address, or do some other tests. */ - - pr_info_once("%s\n", version); - - /* fill in some of the fields */ - dev->base_addr = ioaddr; - - /* - . Get the MAC address ( bank 1, regs 4 - 9 ) - */ - SMC_SELECT_BANK( 1 ); - for ( i = 0; i < 6; i += 2 ) { - word address; - - address = inw( ioaddr + ADDR0 + i ); - addr[i + 1] = address >> 8; - addr[i] = address & 0xFF; - } - eth_hw_addr_set(dev, addr); - - /* get the memory information */ - - SMC_SELECT_BANK( 0 ); - memory_info_register = inw( ioaddr + MIR ); - memory_cfg_register = inw( ioaddr + MCR ); - memory = ( memory_cfg_register >> 9 ) & 0x7; /* multiplier */ - memory *= 256 * ( memory_info_register & 0xFF ); - - /* - Now, I want to find out more about the chip. This is sort of - redundant, but it's cleaner to have it in both, rather than having - one VERY long probe procedure. - */ - SMC_SELECT_BANK(3); - revision_register = inw( ioaddr + REVISION ); - version_string = chip_ids[ ( revision_register >> 4 ) & 0xF ]; - if ( !version_string ) { - /* I shouldn't get here because this call was done before.... */ - retval = -ENODEV; - goto err_out; - } - - /* is it using AUI or 10BaseT ? */ - if ( dev->if_port == 0 ) { - SMC_SELECT_BANK(1); - configuration_register = inw( ioaddr + CONFIG ); - if ( configuration_register & CFG_AUI_SELECT ) - dev->if_port = 2; - else - dev->if_port = 1; - } - if_string = interfaces[ dev->if_port - 1 ]; - - /* now, reset the chip, and put it into a known state */ - smc_reset( ioaddr ); - - /* - . If dev->irq is 0, then the device has to be banged on to see - . what the IRQ is. - . - . This banging doesn't always detect the IRQ, for unknown reasons. - . a workaround is to reset the chip and try again. - . - . Interestingly, the DOS packet driver *SETS* the IRQ on the card to - . be what is requested on the command line. I don't do that, mostly - . because the card that I have uses a non-standard method of accessing - . the IRQs, and because this _should_ work in most configurations. - . - . Specifying an IRQ is done with the assumption that the user knows - . what (s)he is doing. No checking is done!!!! - . - */ - if ( dev->irq < 2 ) { - int trials; - - trials = 3; - while ( trials-- ) { - dev->irq = smc_findirq( ioaddr ); - if ( dev->irq ) - break; - /* kick the card and try again */ - smc_reset( ioaddr ); - } - } - if (dev->irq == 0 ) { - printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n"); - retval = -ENODEV; - goto err_out; - } - - /* now, print out the card info, in a short format.. */ - - netdev_info(dev, "%s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", - version_string, revision_register & 0xF, ioaddr, dev->irq, - if_string, memory); - /* - . Print the Ethernet address - */ - netdev_info(dev, "ADDR: %pM\n", dev->dev_addr); - - /* Grab the IRQ */ - retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev); - if (retval) { - netdev_warn(dev, "%s: unable to get IRQ %d (irqval=%d).\n", - DRV_NAME, dev->irq, retval); - goto err_out; - } - - dev->netdev_ops = &smc_netdev_ops; - dev->watchdog_timeo = HZ/20; - - return 0; - -err_out: - release_region(ioaddr, SMC_IO_EXTENT); - return retval; -} - -#if SMC_DEBUG > 2 -static void print_packet( byte * buf, int length ) -{ -#if 0 - print_hex_dump_debug(DRV_NAME, DUMP_PREFIX_OFFSET, 16, 1, - buf, length, true); -#endif -} -#endif - - -/* - * Open and Initialize the board - * - * Set up everything, reset the card, etc .. - * - */ -static int smc_open(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - - int i; /* used to set hw ethernet address */ - - /* clear out all the junk that was put here before... */ - memset(netdev_priv(dev), 0, sizeof(struct smc_local)); - - /* reset the hardware */ - - smc_reset( ioaddr ); - smc_enable( ioaddr ); - - /* Select which interface to use */ - - SMC_SELECT_BANK( 1 ); - if ( dev->if_port == 1 ) { - outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT, - ioaddr + CONFIG ); - } - else if ( dev->if_port == 2 ) { - outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT, - ioaddr + CONFIG ); - } - - /* - According to Becker, I have to set the hardware address - at this point, because the (l)user can set it with an - ioctl. Easily done... - */ - SMC_SELECT_BANK( 1 ); - for ( i = 0; i < 6; i += 2 ) { - word address; - - address = dev->dev_addr[ i + 1 ] << 8 ; - address |= dev->dev_addr[ i ]; - outw( address, ioaddr + ADDR0 + i ); - } - - netif_start_queue(dev); - return 0; -} - -/*-------------------------------------------------------- - . Called by the kernel to send a packet out into the void - . of the net. This routine is largely based on - . skeleton.c, from Becker. - .-------------------------------------------------------- -*/ - -static void smc_timeout(struct net_device *dev, unsigned int txqueue) -{ - /* If we get here, some higher level has decided we are broken. - There should really be a "kick me" function call instead. */ - netdev_warn(dev, CARDNAME": transmit timed out, %s?\n", - tx_done(dev) ? "IRQ conflict" : "network cable problem"); - /* "kick" the adaptor */ - smc_reset( dev->base_addr ); - smc_enable( dev->base_addr ); - netif_trans_update(dev); /* prevent tx timeout */ - /* clear anything saved */ - ((struct smc_local *)netdev_priv(dev))->saved_skb = NULL; - netif_wake_queue(dev); -} - -/*------------------------------------------------------------- - . - . smc_rcv - receive a packet from the card - . - . There is ( at least ) a packet waiting to be read from - . chip-memory. - . - . o Read the status - . o If an error, record it - . o otherwise, read in the packet - -------------------------------------------------------------- -*/ -static void smc_rcv(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - int packet_number; - word status; - word packet_length; - - /* assume bank 2 */ - - packet_number = inw( ioaddr + FIFO_PORTS ); - - if ( packet_number & FP_RXEMPTY ) { - /* we got called , but nothing was on the FIFO */ - PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO.\n")); - /* don't need to restore anything */ - return; - } - - /* start reading from the start of the packet */ - outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER ); - - /* First two words are status and packet_length */ - status = inw( ioaddr + DATA_1 ); - packet_length = inw( ioaddr + DATA_1 ); - - packet_length &= 0x07ff; /* mask off top bits */ - - PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length )); - /* - . the packet length contains 3 extra words : - . status, length, and an extra word with an odd byte . - */ - packet_length -= 6; - - if ( !(status & RS_ERRORS ) ){ - /* do stuff to make a new packet */ - struct sk_buff * skb; - byte * data; - - /* read one extra byte */ - if ( status & RS_ODDFRAME ) - packet_length++; - - /* set multicast stats */ - if ( status & RS_MULTICAST ) - dev->stats.multicast++; - - skb = netdev_alloc_skb(dev, packet_length + 5); - if ( skb == NULL ) { - dev->stats.rx_dropped++; - goto done; - } - - /* - ! This should work without alignment, but it could be - ! in the worse case - */ - - skb_reserve( skb, 2 ); /* 16 bit alignment */ - - data = skb_put( skb, packet_length); - -#ifdef USE_32_BIT - /* QUESTION: Like in the TX routine, do I want - to send the DWORDs or the bytes first, or some - mixture. A mixture might improve already slow PIO - performance */ - PRINTK3((" Reading %d dwords (and %d bytes)\n", - packet_length >> 2, packet_length & 3 )); - insl(ioaddr + DATA_1 , data, packet_length >> 2 ); - /* read the left over bytes */ - insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC), - packet_length & 0x3 ); -#else - PRINTK3((" Reading %d words and %d byte(s)\n", - (packet_length >> 1 ), packet_length & 1 )); - insw(ioaddr + DATA_1 , data, packet_length >> 1); - if ( packet_length & 1 ) { - data += packet_length & ~1; - *(data++) = inb( ioaddr + DATA_1 ); - } -#endif -#if SMC_DEBUG > 2 - print_packet( data, packet_length ); -#endif - - skb->protocol = eth_type_trans(skb, dev ); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += packet_length; - } else { - /* error ... */ - dev->stats.rx_errors++; - - if ( status & RS_ALGNERR ) dev->stats.rx_frame_errors++; - if ( status & (RS_TOOSHORT | RS_TOOLONG ) ) - dev->stats.rx_length_errors++; - if ( status & RS_BADCRC) dev->stats.rx_crc_errors++; - } - -done: - /* error or good, tell the card to get rid of this packet */ - outw( MC_RELEASE, ioaddr + MMU_CMD ); -} - - -/************************************************************************* - . smc_tx - . - . Purpose: Handle a transmit error message. This will only be called - . when an error, because of the AUTO_RELEASE mode. - . - . Algorithm: - . Save pointer and packet no - . Get the packet no from the top of the queue - . check if it's valid ( if not, is this an error??? ) - . read the status word - . record the error - . ( resend? Not really, since we don't want old packets around ) - . Restore saved values - ************************************************************************/ -static void smc_tx( struct net_device * dev ) -{ - int ioaddr = dev->base_addr; - struct smc_local *lp = netdev_priv(dev); - byte saved_packet; - byte packet_no; - word tx_status; - - - /* assume bank 2 */ - - saved_packet = inb( ioaddr + PNR_ARR ); - packet_no = inw( ioaddr + FIFO_PORTS ); - packet_no &= 0x7F; - - /* select this as the packet to read from */ - outb( packet_no, ioaddr + PNR_ARR ); - - /* read the first word from this packet */ - outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER ); - - tx_status = inw( ioaddr + DATA_1 ); - PRINTK3((CARDNAME": TX DONE STATUS: %4x\n", tx_status)); - - dev->stats.tx_errors++; - if ( tx_status & TS_LOSTCAR ) dev->stats.tx_carrier_errors++; - if ( tx_status & TS_LATCOL ) { - netdev_dbg(dev, CARDNAME": Late collision occurred on last xmit.\n"); - dev->stats.tx_window_errors++; - } -#if 0 - if ( tx_status & TS_16COL ) { ... } -#endif - - if ( tx_status & TS_SUCCESS ) { - netdev_info(dev, CARDNAME": Successful packet caused interrupt\n"); - } - /* re-enable transmit */ - SMC_SELECT_BANK( 0 ); - outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR ); - - /* kill the packet */ - SMC_SELECT_BANK( 2 ); - outw( MC_FREEPKT, ioaddr + MMU_CMD ); - - /* one less packet waiting for me */ - lp->packets_waiting--; - - outb( saved_packet, ioaddr + PNR_ARR ); -} - -/*-------------------------------------------------------------------- - . - . This is the main routine of the driver, to handle the device when - . it needs some attention. - . - . So: - . first, save state of the chipset - . branch off into routines to handle each case, and acknowledge - . each to the interrupt register - . and finally restore state. - . - ---------------------------------------------------------------------*/ - -static irqreturn_t smc_interrupt(int irq, void * dev_id) -{ - struct net_device *dev = dev_id; - int ioaddr = dev->base_addr; - struct smc_local *lp = netdev_priv(dev); - - byte status; - word card_stats; - byte mask; - int timeout; - /* state registers */ - word saved_bank; - word saved_pointer; - int handled = 0; - - - PRINTK3((CARDNAME": SMC interrupt started\n")); - - saved_bank = inw( ioaddr + BANK_SELECT ); - - SMC_SELECT_BANK(2); - saved_pointer = inw( ioaddr + POINTER ); - - mask = inb( ioaddr + INT_MASK ); - /* clear all interrupts */ - outb( 0, ioaddr + INT_MASK ); - - - /* set a timeout value, so I don't stay here forever */ - timeout = 4; - - PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x\n", mask)); - do { - /* read the status flag, and mask it */ - status = inb( ioaddr + INTERRUPT ) & mask; - if (!status ) - break; - - handled = 1; - - PRINTK3((KERN_WARNING CARDNAME - ": Handling interrupt status %x\n", status)); - - if (status & IM_RCV_INT) { - /* Got a packet(s). */ - PRINTK2((KERN_WARNING CARDNAME - ": Receive Interrupt\n")); - smc_rcv(dev); - } else if (status & IM_TX_INT ) { - PRINTK2((KERN_WARNING CARDNAME - ": TX ERROR handled\n")); - smc_tx(dev); - outb(IM_TX_INT, ioaddr + INTERRUPT ); - } else if (status & IM_TX_EMPTY_INT ) { - /* update stats */ - SMC_SELECT_BANK( 0 ); - card_stats = inw( ioaddr + COUNTER ); - /* single collisions */ - dev->stats.collisions += card_stats & 0xF; - card_stats >>= 4; - /* multiple collisions */ - dev->stats.collisions += card_stats & 0xF; - - /* these are for when linux supports these statistics */ - - SMC_SELECT_BANK( 2 ); - PRINTK2((KERN_WARNING CARDNAME - ": TX_BUFFER_EMPTY handled\n")); - outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT ); - mask &= ~IM_TX_EMPTY_INT; - dev->stats.tx_packets += lp->packets_waiting; - lp->packets_waiting = 0; - - } else if (status & IM_ALLOC_INT ) { - PRINTK2((KERN_DEBUG CARDNAME - ": Allocation interrupt\n")); - /* clear this interrupt so it doesn't happen again */ - mask &= ~IM_ALLOC_INT; - - smc_hardware_send_packet( dev ); - - /* enable xmit interrupts based on this */ - mask |= ( IM_TX_EMPTY_INT | IM_TX_INT ); - - /* and let the card send more packets to me */ - netif_wake_queue(dev); - - PRINTK2((CARDNAME": Handoff done successfully.\n")); - } else if (status & IM_RX_OVRN_INT ) { - dev->stats.rx_errors++; - dev->stats.rx_fifo_errors++; - outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT ); - } else if (status & IM_EPH_INT ) { - PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT\n")); - } else if (status & IM_ERCV_INT ) { - PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT\n")); - outb( IM_ERCV_INT, ioaddr + INTERRUPT ); - } - } while ( timeout -- ); - - - /* restore state register */ - SMC_SELECT_BANK( 2 ); - outb( mask, ioaddr + INT_MASK ); - - PRINTK3((KERN_WARNING CARDNAME ": MASK is now %x\n", mask)); - outw( saved_pointer, ioaddr + POINTER ); - - SMC_SELECT_BANK( saved_bank ); - - PRINTK3((CARDNAME ": Interrupt done\n")); - return IRQ_RETVAL(handled); -} - - -/*---------------------------------------------------- - . smc_close - . - . this makes the board clean up everything that it can - . and not talk to the outside world. Caused by - . an 'ifconfig ethX down' - . - -----------------------------------------------------*/ -static int smc_close(struct net_device *dev) -{ - netif_stop_queue(dev); - /* clear everything */ - smc_shutdown( dev->base_addr ); - - /* Update the statistics here. */ - return 0; -} - -/*----------------------------------------------------------- - . smc_set_multicast_list - . - . This routine will, depending on the values passed to it, - . either make it accept multicast packets, go into - . promiscuous mode ( for TCPDUMP and cousins ) or accept - . a select set of multicast packets -*/ -static void smc_set_multicast_list(struct net_device *dev) -{ - short ioaddr = dev->base_addr; - - SMC_SELECT_BANK(0); - if ( dev->flags & IFF_PROMISC ) - outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR ); - -/* BUG? I never disable promiscuous mode if multicasting was turned on. - Now, I turn off promiscuous mode, but I don't do anything to multicasting - when promiscuous mode is turned on. -*/ - - /* Here, I am setting this to accept all multicast packets. - I don't need to zero the multicast table, because the flag is - checked before the table is - */ - else if (dev->flags & IFF_ALLMULTI) - outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR ); - - /* We just get all multicast packets even if we only want them - . from one source. This will be changed at some future - . point. */ - else if (!netdev_mc_empty(dev)) { - /* support hardware multicasting */ - - /* be sure I get rid of flags I might have set */ - outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL), - ioaddr + RCR ); - /* NOTE: this has to set the bank, so make sure it is the - last thing called. The bank is set to zero at the top */ - smc_setmulticast(ioaddr, dev); - } - else { - outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL), - ioaddr + RCR ); - - /* - since I'm disabling all multicast entirely, I need to - clear the multicast list - */ - SMC_SELECT_BANK( 3 ); - outw( 0, ioaddr + MULTICAST1 ); - outw( 0, ioaddr + MULTICAST2 ); - outw( 0, ioaddr + MULTICAST3 ); - outw( 0, ioaddr + MULTICAST4 ); - } -} - -#ifdef MODULE - -static struct net_device *devSMC9194; -MODULE_DESCRIPTION("SMC 9194 Ethernet driver"); -MODULE_LICENSE("GPL"); - -module_param_hw(io, int, ioport, 0); -module_param_hw(irq, int, irq, 0); -module_param(ifport, int, 0); -MODULE_PARM_DESC(io, "SMC 99194 I/O base address"); -MODULE_PARM_DESC(irq, "SMC 99194 IRQ number"); -MODULE_PARM_DESC(ifport, "SMC 99194 interface port (0-default, 1-TP, 2-AUI)"); - -static int __init smc_init_module(void) -{ - if (io == 0) - printk(KERN_WARNING - CARDNAME": You shouldn't use auto-probing with insmod!\n" ); - - /* copy the parameters from insmod into the device structure */ - devSMC9194 = smc_init(-1); - return PTR_ERR_OR_ZERO(devSMC9194); -} -module_init(smc_init_module); - -static void __exit smc_cleanup_module(void) -{ - unregister_netdev(devSMC9194); - free_irq(devSMC9194->irq, devSMC9194); - release_region(devSMC9194->base_addr, SMC_IO_EXTENT); - free_netdev(devSMC9194); -} -module_exit(smc_cleanup_module); - -#endif /* MODULE */ diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c deleted file mode 100644 index cc0c75694351..000000000000 --- a/drivers/net/ethernet/smsc/smc91c92_cs.c +++ /dev/null @@ -1,2059 +0,0 @@ -/*====================================================================== - - A PCMCIA ethernet driver for SMC91c92-based cards. - - This driver supports Megahertz PCMCIA ethernet cards; and - Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem - multifunction cards. - - Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net - - smc91c92_cs.c 1.122 2002/10/25 06:26:39 - - This driver contains code written by Donald Becker - (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au), - David Hinds (dahinds@users.sourceforge.net), and Erik Stahlman - (erik@vt.edu). Donald wrote the SMC 91c92 code using parts of - Erik's SMC 91c94 driver. Rowan wrote a similar driver, and I've - incorporated some parts of his driver here. I (Dave) wrote most - of the PCMCIA glue code, and the Ositech support code. Kelly - Stephens (kstephen@holli.com) added support for the Motorola - Mariner, with help from Allen Brost. - - This software may be used and distributed according to the terms of - the GNU General Public License, incorporated herein by reference. - -======================================================================*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -/*====================================================================*/ - -static const char *if_names[] = { "auto", "10baseT", "10base2"}; - -/* Firmware name */ -#define FIRMWARE_NAME "ositech/Xilinx7OD.bin" - -/* Module parameters */ - -MODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(FIRMWARE_NAME); - -#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) - -/* - Transceiver/media type. - 0 = auto - 1 = 10baseT (and autoselect if #define AUTOSELECT), - 2 = AUI/10base2, -*/ -INT_MODULE_PARM(if_port, 0); - - -#define DRV_NAME "smc91c92_cs" -#define DRV_VERSION "1.123" - -/*====================================================================*/ - -/* Operational parameter that usually are not changed. */ - -/* Time in jiffies before concluding Tx hung */ -#define TX_TIMEOUT ((400*HZ)/1000) - -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -#define INTR_WORK 4 - -/* Times to check the check the chip before concluding that it doesn't - currently have room for another Tx packet. */ -#define MEMORY_WAIT_TIME 8 - -struct smc_private { - struct pcmcia_device *p_dev; - spinlock_t lock; - u_short manfid; - u_short cardid; - - struct sk_buff *saved_skb; - int packets_waiting; - void __iomem *base; - u_short cfg; - struct timer_list media; - int watchdog, tx_err; - u_short media_status; - u_short fast_poll; - u_short link_status; - struct mii_if_info mii_if; - int duplex; - int rx_ovrn; - unsigned long last_rx; -}; - -/* Special definitions for Megahertz multifunction cards */ -#define MEGAHERTZ_ISR 0x0380 - -/* Special function registers for Motorola Mariner */ -#define MOT_LAN 0x0000 -#define MOT_UART 0x0020 -#define MOT_EEPROM 0x20 - -#define MOT_NORMAL \ -(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA) - -/* Special function registers for Ositech cards */ -#define OSITECH_AUI_CTL 0x0c -#define OSITECH_PWRDOWN 0x0d -#define OSITECH_RESET 0x0e -#define OSITECH_ISR 0x0f -#define OSITECH_AUI_PWR 0x0c -#define OSITECH_RESET_ISR 0x0e - -#define OSI_AUI_PWR 0x40 -#define OSI_LAN_PWRDOWN 0x02 -#define OSI_MODEM_PWRDOWN 0x01 -#define OSI_LAN_RESET 0x02 -#define OSI_MODEM_RESET 0x01 - -/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */ -#define BANK_SELECT 14 /* Window select register. */ -#define SMC_SELECT_BANK(x) { outw(x, ioaddr + BANK_SELECT); } - -/* Bank 0 registers. */ -#define TCR 0 /* transmit control register */ -#define TCR_CLEAR 0 /* do NOTHING */ -#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */ -#define TCR_PAD_EN 0x0080 /* pads short packets to 64 bytes */ -#define TCR_MONCSN 0x0400 /* Monitor Carrier. */ -#define TCR_FDUPLX 0x0800 /* Full duplex mode. */ -#define TCR_NORMAL TCR_ENABLE | TCR_PAD_EN - -#define EPH 2 /* Ethernet Protocol Handler report. */ -#define EPH_TX_SUC 0x0001 -#define EPH_SNGLCOL 0x0002 -#define EPH_MULCOL 0x0004 -#define EPH_LTX_MULT 0x0008 -#define EPH_16COL 0x0010 -#define EPH_SQET 0x0020 -#define EPH_LTX_BRD 0x0040 -#define EPH_TX_DEFR 0x0080 -#define EPH_LAT_COL 0x0200 -#define EPH_LOST_CAR 0x0400 -#define EPH_EXC_DEF 0x0800 -#define EPH_CTR_ROL 0x1000 -#define EPH_RX_OVRN 0x2000 -#define EPH_LINK_OK 0x4000 -#define EPH_TX_UNRN 0x8000 -#define MEMINFO 8 /* Memory Information Register */ -#define MEMCFG 10 /* Memory Configuration Register */ - -/* Bank 1 registers. */ -#define CONFIG 0 -#define CFG_MII_SELECT 0x8000 /* 91C100 only */ -#define CFG_NO_WAIT 0x1000 -#define CFG_FULL_STEP 0x0400 -#define CFG_SET_SQLCH 0x0200 -#define CFG_AUI_SELECT 0x0100 -#define CFG_16BIT 0x0080 -#define CFG_DIS_LINK 0x0040 -#define CFG_STATIC 0x0030 -#define CFG_IRQ_SEL_1 0x0004 -#define CFG_IRQ_SEL_0 0x0002 -#define BASE_ADDR 2 -#define ADDR0 4 -#define GENERAL 10 -#define CONTROL 12 -#define CTL_STORE 0x0001 -#define CTL_RELOAD 0x0002 -#define CTL_EE_SELECT 0x0004 -#define CTL_TE_ENABLE 0x0020 -#define CTL_CR_ENABLE 0x0040 -#define CTL_LE_ENABLE 0x0080 -#define CTL_AUTO_RELEASE 0x0800 -#define CTL_POWERDOWN 0x2000 - -/* Bank 2 registers. */ -#define MMU_CMD 0 -#define MC_ALLOC 0x20 /* or with number of 256 byte packets */ -#define MC_RESET 0x40 -#define MC_RELEASE 0x80 /* remove and release the current rx packet */ -#define MC_FREEPKT 0xA0 /* Release packet in PNR register */ -#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */ -#define PNR_ARR 2 -#define FIFO_PORTS 4 -#define FP_RXEMPTY 0x8000 -#define POINTER 6 -#define PTR_AUTO_INC 0x0040 -#define PTR_READ 0x2000 -#define PTR_AUTOINC 0x4000 -#define PTR_RCV 0x8000 -#define DATA_1 8 -#define INTERRUPT 12 -#define IM_RCV_INT 0x1 -#define IM_TX_INT 0x2 -#define IM_TX_EMPTY_INT 0x4 -#define IM_ALLOC_INT 0x8 -#define IM_RX_OVRN_INT 0x10 -#define IM_EPH_INT 0x20 - -#define RCR 4 -enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002, - RxEnable = 0x0100, RxStripCRC = 0x0200}; -#define RCR_SOFTRESET 0x8000 /* resets the chip */ -#define RCR_STRIP_CRC 0x200 /* strips CRC */ -#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */ -#define RCR_ALMUL 0x4 /* receive all multicast packets */ -#define RCR_PROMISC 0x2 /* enable promiscuous mode */ - -/* the normal settings for the RCR register : */ -#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE) -#define RCR_CLEAR 0x0 /* set it to a base state */ -#define COUNTER 6 - -/* BANK 3 -- not the same values as in smc9194! */ -#define MULTICAST0 0 -#define MULTICAST2 2 -#define MULTICAST4 4 -#define MULTICAST6 6 -#define MGMT 8 -#define REVISION 0x0a - -/* Transmit status bits. */ -#define TS_SUCCESS 0x0001 -#define TS_16COL 0x0010 -#define TS_LATCOL 0x0200 -#define TS_LOSTCAR 0x0400 - -/* Receive status bits. */ -#define RS_ALGNERR 0x8000 -#define RS_BADCRC 0x2000 -#define RS_ODDFRAME 0x1000 -#define RS_TOOLONG 0x0800 -#define RS_TOOSHORT 0x0400 -#define RS_MULTICAST 0x0001 -#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) - -#define set_bits(v, p) outw(inw(p)|(v), (p)) -#define mask_bits(v, p) outw(inw(p)&(v), (p)) - -/*====================================================================*/ - -static void smc91c92_detach(struct pcmcia_device *p_dev); -static int smc91c92_config(struct pcmcia_device *link); -static void smc91c92_release(struct pcmcia_device *link); - -static int smc_open(struct net_device *dev); -static int smc_close(struct net_device *dev); -static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void smc_tx_timeout(struct net_device *dev, unsigned int txqueue); -static netdev_tx_t smc_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t smc_interrupt(int irq, void *dev_id); -static void smc_rx(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static int s9k_config(struct net_device *dev, struct ifmap *map); -static void smc_set_xcvr(struct net_device *dev, int if_port); -static void smc_reset(struct net_device *dev); -static void media_check(struct timer_list *t); -static void mdio_sync(unsigned int addr); -static int mdio_read(struct net_device *dev, int phy_id, int loc); -static void mdio_write(struct net_device *dev, int phy_id, int loc, int value); -static int smc_link_ok(struct net_device *dev); -static const struct ethtool_ops ethtool_ops; - -static const struct net_device_ops smc_netdev_ops = { - .ndo_open = smc_open, - .ndo_stop = smc_close, - .ndo_start_xmit = smc_start_xmit, - .ndo_tx_timeout = smc_tx_timeout, - .ndo_set_config = s9k_config, - .ndo_set_rx_mode = set_rx_mode, - .ndo_eth_ioctl = smc_ioctl, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int smc91c92_probe(struct pcmcia_device *link) -{ - struct smc_private *smc; - struct net_device *dev; - - dev_dbg(&link->dev, "smc91c92_attach()\n"); - - /* Create new ethernet device */ - dev = alloc_etherdev(sizeof(struct smc_private)); - if (!dev) - return -ENOMEM; - smc = netdev_priv(dev); - smc->p_dev = link; - link->priv = dev; - - spin_lock_init(&smc->lock); - - /* The SMC91c92-specific entries in the device structure. */ - dev->netdev_ops = &smc_netdev_ops; - dev->ethtool_ops = ðtool_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - smc->mii_if.dev = dev; - smc->mii_if.mdio_read = mdio_read; - smc->mii_if.mdio_write = mdio_write; - smc->mii_if.phy_id_mask = 0x1f; - smc->mii_if.reg_num_mask = 0x1f; - - return smc91c92_config(link); -} /* smc91c92_attach */ - -static void smc91c92_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - dev_dbg(&link->dev, "smc91c92_detach\n"); - - unregister_netdev(dev); - - smc91c92_release(link); - - free_netdev(dev); -} /* smc91c92_detach */ - -/*====================================================================*/ - -static int cvt_ascii_address(struct net_device *dev, char *s) -{ - u8 mac[ETH_ALEN]; - int i, j, da, c; - - if (strlen(s) != 12) - return -1; - for (i = 0; i < 6; i++) { - da = 0; - for (j = 0; j < 2; j++) { - c = *s++; - da <<= 4; - da += ((c >= '0') && (c <= '9')) ? - (c - '0') : ((c & 0x0f) + 9); - } - mac[i] = da; - } - eth_hw_addr_set(dev, mac); - return 0; -} - -/*==================================================================== - - Configuration stuff for Megahertz cards - - mhz_3288_power() is used to power up a 3288's ethernet chip. - mhz_mfc_config() handles socket setup for multifunction (1144 - and 3288) cards. mhz_setup() gets a card's hardware ethernet - address. - -======================================================================*/ - -static int mhz_3288_power(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - u_char tmp; - - /* Read the ISR twice... */ - readb(smc->base+MEGAHERTZ_ISR); - udelay(5); - readb(smc->base+MEGAHERTZ_ISR); - - /* Pause 200ms... */ - mdelay(200); - - /* Now read and write the COR... */ - tmp = readb(smc->base + link->config_base + CISREG_COR); - udelay(5); - writeb(tmp, smc->base + link->config_base + CISREG_COR); - - return 0; -} - -static int mhz_mfc_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - int k; - p_dev->io_lines = 16; - p_dev->resource[1]->start = p_dev->resource[0]->start; - p_dev->resource[1]->end = 8; - p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[0]->end = 16; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - for (k = 0; k < 0x400; k += 0x10) { - if (k & 0x80) - continue; - p_dev->resource[0]->start = k ^ 0x300; - if (!pcmcia_request_io(p_dev)) - return 0; - } - return -ENODEV; -} - -static int mhz_mfc_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - unsigned int offset; - int i; - - link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ | - CONF_AUTO_SET_IO; - - /* The Megahertz combo cards have modem-like CIS entries, so - we have to explicitly try a bunch of port combinations. */ - if (pcmcia_loop_config(link, mhz_mfc_config_check, NULL)) - return -ENODEV; - - dev->base_addr = link->resource[0]->start; - - /* Allocate a memory window, for accessing the ISR */ - link->resource[2]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; - link->resource[2]->start = link->resource[2]->end = 0; - i = pcmcia_request_window(link, link->resource[2], 0); - if (i != 0) - return -ENODEV; - - smc->base = ioremap(link->resource[2]->start, - resource_size(link->resource[2])); - offset = (smc->manfid == MANFID_MOTOROLA) ? link->config_base : 0; - i = pcmcia_map_mem_page(link, link->resource[2], offset); - if ((i == 0) && - (smc->manfid == MANFID_MEGAHERTZ) && - (smc->cardid == PRODID_MEGAHERTZ_EM3288)) - mhz_3288_power(link); - - return 0; -} - -static int pcmcia_get_versmac(struct pcmcia_device *p_dev, - tuple_t *tuple, - void *priv) -{ - struct net_device *dev = priv; - cisparse_t parse; - u8 *buf; - - if (pcmcia_parse_tuple(tuple, &parse)) - return -EINVAL; - - buf = parse.version_1.str + parse.version_1.ofs[3]; - - if ((parse.version_1.ns > 3) && (cvt_ascii_address(dev, buf) == 0)) - return 0; - - return -EINVAL; -}; - -static int mhz_setup(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - size_t len; - u8 *buf; - int rc; - - /* Read the station address from the CIS. It is stored as the last - (fourth) string in the Version 1 Version/ID tuple. */ - if ((link->prod_id[3]) && - (cvt_ascii_address(dev, link->prod_id[3]) == 0)) - return 0; - - /* Workarounds for broken cards start here. */ - /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */ - if (!pcmcia_loop_tuple(link, CISTPL_VERS_1, pcmcia_get_versmac, dev)) - return 0; - - /* Another possibility: for the EM3288, in a special tuple */ - rc = -1; - len = pcmcia_get_tuple(link, 0x81, &buf); - if (buf && len >= 13) { - buf[12] = '\0'; - if (cvt_ascii_address(dev, buf) == 0) - rc = 0; - } - kfree(buf); - - return rc; -}; - -/*====================================================================== - - Configuration stuff for the Motorola Mariner - - mot_config() writes directly to the Mariner configuration - registers because the CIS is just bogus. - -======================================================================*/ - -static void mot_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - unsigned int iouart = link->resource[1]->start; - - /* Set UART base address and force map with COR bit 1 */ - writeb(iouart & 0xff, smc->base + MOT_UART + CISREG_IOBASE_0); - writeb((iouart >> 8) & 0xff, smc->base + MOT_UART + CISREG_IOBASE_1); - writeb(MOT_NORMAL, smc->base + MOT_UART + CISREG_COR); - - /* Set SMC base address and force map with COR bit 1 */ - writeb(ioaddr & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_0); - writeb((ioaddr >> 8) & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_1); - writeb(MOT_NORMAL, smc->base + MOT_LAN + CISREG_COR); - - /* Wait for things to settle down */ - mdelay(100); -} - -static int mot_setup(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - unsigned int ioaddr = dev->base_addr; - int i, wait, loop; - u8 mac[ETH_ALEN]; - u_int addr; - - /* Read Ethernet address from Serial EEPROM */ - - for (i = 0; i < 3; i++) { - SMC_SELECT_BANK(2); - outw(MOT_EEPROM + i, ioaddr + POINTER); - SMC_SELECT_BANK(1); - outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL); - - for (loop = wait = 0; loop < 200; loop++) { - udelay(10); - wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL)); - if (wait == 0) break; - } - - if (wait) - return -1; - - addr = inw(ioaddr + GENERAL); - mac[2*i] = addr & 0xff; - mac[2*i+1] = (addr >> 8) & 0xff; - } - eth_hw_addr_set(dev, mac); - - return 0; -} - -/*====================================================================*/ - -static int smc_configcheck(struct pcmcia_device *p_dev, void *priv_data) -{ - p_dev->resource[0]->end = 16; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - - return pcmcia_request_io(p_dev); -} - -static int smc_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - int i; - - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - - i = pcmcia_loop_config(link, smc_configcheck, NULL); - if (!i) - dev->base_addr = link->resource[0]->start; - - return i; -} - - -static int smc_setup(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - /* Check for a LAN function extension tuple */ - if (!pcmcia_get_mac_from_cis(link, dev)) - return 0; - - /* Try the third string in the Version 1 Version/ID tuple. */ - if (link->prod_id[2]) { - if (cvt_ascii_address(dev, link->prod_id[2]) == 0) - return 0; - } - return -1; -} - -/*====================================================================*/ - -static int osi_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - static const unsigned int com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; - int i, j; - - link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ; - link->resource[0]->end = 64; - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - link->resource[1]->end = 8; - - /* Enable Hard Decode, LAN, Modem */ - link->io_lines = 16; - link->config_index = 0x23; - - for (i = j = 0; j < 4; j++) { - link->resource[1]->start = com[j]; - i = pcmcia_request_io(link); - if (i == 0) - break; - } - if (i != 0) { - /* Fallback: turn off hard decode */ - link->config_index = 0x03; - link->resource[1]->end = 0; - i = pcmcia_request_io(link); - } - dev->base_addr = link->resource[0]->start + 0x10; - return i; -} - -static int osi_load_firmware(struct pcmcia_device *link) -{ - const struct firmware *fw; - int i, err; - - err = request_firmware(&fw, FIRMWARE_NAME, &link->dev); - if (err) { - pr_err("Failed to load firmware \"%s\"\n", FIRMWARE_NAME); - return err; - } - - /* Download the Seven of Diamonds firmware */ - for (i = 0; i < fw->size; i++) { - outb(fw->data[i], link->resource[0]->start + 2); - udelay(50); - } - release_firmware(fw); - return err; -} - -static int pcmcia_osi_mac(struct pcmcia_device *p_dev, - tuple_t *tuple, - void *priv) -{ - struct net_device *dev = priv; - - if (tuple->TupleDataLen < 8) - return -EINVAL; - if (tuple->TupleData[0] != 0x04) - return -EINVAL; - - eth_hw_addr_set(dev, &tuple->TupleData[2]); - return 0; -}; - - -static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid) -{ - struct net_device *dev = link->priv; - int rc; - - /* Read the station address from tuple 0x90, subtuple 0x04 */ - if (pcmcia_loop_tuple(link, 0x90, pcmcia_osi_mac, dev)) - return -1; - - if (((manfid == MANFID_OSITECH) && - (cardid == PRODID_OSITECH_SEVEN)) || - ((manfid == MANFID_PSION) && - (cardid == PRODID_PSION_NET100))) { - rc = osi_load_firmware(link); - if (rc) - return rc; - } else if (manfid == MANFID_OSITECH) { - /* Make sure both functions are powered up */ - set_bits(0x300, link->resource[0]->start + OSITECH_AUI_PWR); - /* Now, turn on the interrupt for both card functions */ - set_bits(0x300, link->resource[0]->start + OSITECH_RESET_ISR); - dev_dbg(&link->dev, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n", - inw(link->resource[0]->start + OSITECH_AUI_PWR), - inw(link->resource[0]->start + OSITECH_RESET_ISR)); - } - return 0; -} - -static int smc91c92_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int smc91c92_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - int i; - - if ((smc->manfid == MANFID_MEGAHERTZ) && - (smc->cardid == PRODID_MEGAHERTZ_EM3288)) - mhz_3288_power(link); - if (smc->manfid == MANFID_MOTOROLA) - mot_config(link); - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) { - /* Power up the card and enable interrupts */ - set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR); - set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR); - } - if (((smc->manfid == MANFID_OSITECH) && - (smc->cardid == PRODID_OSITECH_SEVEN)) || - ((smc->manfid == MANFID_PSION) && - (smc->cardid == PRODID_PSION_NET100))) { - i = osi_load_firmware(link); - if (i) { - netdev_err(dev, "Failed to load firmware\n"); - return i; - } - } - if (link->open) { - smc_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - - -/*====================================================================== - - This verifies that the chip is some SMC91cXX variant, and returns - the revision code if successful. Otherwise, it returns -ENODEV. - -======================================================================*/ - -static int check_sig(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - unsigned int ioaddr = dev->base_addr; - int width; - u_short s; - - SMC_SELECT_BANK(1); - if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) { - /* Try powering up the chip */ - outw(0, ioaddr + CONTROL); - mdelay(55); - } - - /* Try setting bus width */ - width = (link->resource[0]->flags == IO_DATA_PATH_WIDTH_AUTO); - s = inb(ioaddr + CONFIG); - if (width) - s |= CFG_16BIT; - else - s &= ~CFG_16BIT; - outb(s, ioaddr + CONFIG); - - /* Check Base Address Register to make sure bus width is OK */ - s = inw(ioaddr + BASE_ADDR); - if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) && - ((s >> 8) != (s & 0xff))) { - SMC_SELECT_BANK(3); - s = inw(ioaddr + REVISION); - return s & 0xff; - } - - if (width) { - netdev_info(dev, "using 8-bit IO window\n"); - - smc91c92_suspend(link); - pcmcia_fixup_iowidth(link); - smc91c92_resume(link); - return check_sig(link); - } - return -ENODEV; -} - -static int smc91c92_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - char *name; - int i, rev, j = 0; - unsigned int ioaddr; - u_long mir; - - dev_dbg(&link->dev, "smc91c92_config\n"); - - smc->manfid = link->manf_id; - smc->cardid = link->card_id; - - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) { - i = osi_config(link); - } else if ((smc->manfid == MANFID_MOTOROLA) || - ((smc->manfid == MANFID_MEGAHERTZ) && - ((smc->cardid == PRODID_MEGAHERTZ_VARIOUS) || - (smc->cardid == PRODID_MEGAHERTZ_EM3288)))) { - i = mhz_mfc_config(link); - } else { - i = smc_config(link); - } - if (i) - goto config_failed; - - i = pcmcia_request_irq(link, smc_interrupt); - if (i) - goto config_failed; - i = pcmcia_enable_device(link); - if (i) - goto config_failed; - - if (smc->manfid == MANFID_MOTOROLA) - mot_config(link); - - dev->irq = link->irq; - - if ((if_port >= 0) && (if_port <= 2)) - dev->if_port = if_port; - else - dev_notice(&link->dev, "invalid if_port requested\n"); - - switch (smc->manfid) { - case MANFID_OSITECH: - case MANFID_PSION: - i = osi_setup(link, smc->manfid, smc->cardid); break; - case MANFID_SMC: - case MANFID_NEW_MEDIA: - i = smc_setup(link); break; - case 0x128: /* For broken Megahertz cards */ - case MANFID_MEGAHERTZ: - i = mhz_setup(link); break; - case MANFID_MOTOROLA: - default: /* get the hw address from EEPROM */ - i = mot_setup(link); break; - } - - if (i != 0) { - dev_notice(&link->dev, "Unable to find hardware address.\n"); - goto config_failed; - } - - smc->duplex = 0; - smc->rx_ovrn = 0; - - rev = check_sig(link); - name = "???"; - if (rev > 0) - switch (rev >> 4) { - case 3: name = "92"; break; - case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break; - case 5: name = "95"; break; - case 7: name = "100"; break; - case 8: name = "100-FD"; break; - case 9: name = "110"; break; - } - - ioaddr = dev->base_addr; - if (rev > 0) { - u_long mcr; - SMC_SELECT_BANK(0); - mir = inw(ioaddr + MEMINFO) & 0xff; - if (mir == 0xff) mir++; - /* Get scale factor for memory size */ - mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200; - mir *= 128 * (1<<((mcr >> 9) & 7)); - SMC_SELECT_BANK(1); - smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT; - smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC; - if (smc->manfid == MANFID_OSITECH) - smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0; - if ((rev >> 4) >= 7) - smc->cfg |= CFG_MII_SELECT; - } else - mir = 0; - - if (smc->cfg & CFG_MII_SELECT) { - SMC_SELECT_BANK(3); - - for (i = 0; i < 32; i++) { - j = mdio_read(dev, i, 1); - if ((j != 0) && (j != 0xffff)) break; - } - smc->mii_if.phy_id = (i < 32) ? i : -1; - - SMC_SELECT_BANK(0); - } - - SET_NETDEV_DEV(dev, &link->dev); - - if (register_netdev(dev) != 0) { - dev_err(&link->dev, "register_netdev() failed\n"); - goto config_undo; - } - - netdev_info(dev, "smc91c%s rev %d: io %#3lx, irq %d, hw_addr %pM\n", - name, (rev & 0x0f), dev->base_addr, dev->irq, dev->dev_addr); - - if (rev > 0) { - if (mir & 0x3ff) - netdev_info(dev, " %lu byte", mir); - else - netdev_info(dev, " %lu kb", mir>>10); - pr_cont(" buffer, %s xcvr\n", - (smc->cfg & CFG_MII_SELECT) ? "MII" : if_names[dev->if_port]); - } - - if (smc->cfg & CFG_MII_SELECT) { - if (smc->mii_if.phy_id != -1) { - netdev_dbg(dev, " MII transceiver at index %d, status %x\n", - smc->mii_if.phy_id, j); - } else { - netdev_notice(dev, " No MII transceivers found!\n"); - } - } - return 0; - -config_undo: - unregister_netdev(dev); -config_failed: - smc91c92_release(link); - free_netdev(dev); - return -ENODEV; -} /* smc91c92_config */ - -static void smc91c92_release(struct pcmcia_device *link) -{ - dev_dbg(&link->dev, "smc91c92_release\n"); - if (link->resource[2]->end) { - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - iounmap(smc->base); - } - pcmcia_disable_device(link); -} - -/*====================================================================== - - MII interface support for SMC91cXX based cards -======================================================================*/ - -#define MDIO_SHIFT_CLK 0x04 -#define MDIO_DATA_OUT 0x01 -#define MDIO_DIR_WRITE 0x08 -#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE) -#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT) -#define MDIO_DATA_READ 0x02 - -static void mdio_sync(unsigned int addr) -{ - int bits; - for (bits = 0; bits < 32; bits++) { - outb(MDIO_DATA_WRITE1, addr); - outb(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr); - } -} - -static int mdio_read(struct net_device *dev, int phy_id, int loc) -{ - unsigned int addr = dev->base_addr + MGMT; - u_int cmd = (0x06<<10)|(phy_id<<5)|loc; - int i, retval = 0; - - mdio_sync(addr); - for (i = 13; i >= 0; i--) { - int dat = (cmd&(1< 0; i--) { - outb(0, addr); - retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0); - outb(MDIO_SHIFT_CLK, addr); - } - return (retval>>1) & 0xffff; -} - -static void mdio_write(struct net_device *dev, int phy_id, int loc, int value) -{ - unsigned int addr = dev->base_addr + MGMT; - u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value; - int i; - - mdio_sync(addr); - for (i = 31; i >= 0; i--) { - int dat = (cmd&(1<= 0; i--) { - outb(0, addr); - outb(MDIO_SHIFT_CLK, addr); - } -} - -/*====================================================================== - - The driver core code, most of which should be common with a - non-PCMCIA implementation. - -======================================================================*/ - -#ifdef PCMCIA_DEBUG -static void smc_dump(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - u_short i, w, save; - save = inw(ioaddr + BANK_SELECT); - for (w = 0; w < 4; w++) { - SMC_SELECT_BANK(w); - netdev_dbg(dev, "bank %d: ", w); - for (i = 0; i < 14; i += 2) - pr_cont(" %04x", inw(ioaddr + i)); - pr_cont("\n"); - } - outw(save, ioaddr + BANK_SELECT); -} -#endif - -static int smc_open(struct net_device *dev) -{ - struct smc_private *smc = netdev_priv(dev); - struct pcmcia_device *link = smc->p_dev; - - dev_dbg(&link->dev, "%s: smc_open(%p), ID/Window %4.4x.\n", - dev->name, dev, inw(dev->base_addr + BANK_SELECT)); -#ifdef PCMCIA_DEBUG - smc_dump(dev); -#endif - - /* Check that the PCMCIA card is still here. */ - if (!pcmcia_dev_present(link)) - return -ENODEV; - /* Physical device present signature. */ - if (check_sig(link) < 0) { - netdev_info(dev, "Yikes! Bad chip signature!\n"); - return -ENODEV; - } - link->open++; - - netif_start_queue(dev); - smc->saved_skb = NULL; - smc->packets_waiting = 0; - - smc_reset(dev); - timer_setup(&smc->media, media_check, 0); - mod_timer(&smc->media, jiffies + HZ); - - return 0; -} /* smc_open */ - -/*====================================================================*/ - -static int smc_close(struct net_device *dev) -{ - struct smc_private *smc = netdev_priv(dev); - struct pcmcia_device *link = smc->p_dev; - unsigned int ioaddr = dev->base_addr; - - dev_dbg(&link->dev, "%s: smc_close(), status %4.4x.\n", - dev->name, inw(ioaddr + BANK_SELECT)); - - netif_stop_queue(dev); - - /* Shut off all interrupts, and turn off the Tx and Rx sections. - Don't bother to check for chip present. */ - SMC_SELECT_BANK(2); /* Nominally paranoia, but do no assume... */ - outw(0, ioaddr + INTERRUPT); - SMC_SELECT_BANK(0); - mask_bits(0xff00, ioaddr + RCR); - mask_bits(0xff00, ioaddr + TCR); - - /* Put the chip into power-down mode. */ - SMC_SELECT_BANK(1); - outw(CTL_POWERDOWN, ioaddr + CONTROL ); - - link->open--; - timer_delete_sync(&smc->media); - - return 0; -} /* smc_close */ - -/*====================================================================== - - Transfer a packet to the hardware and trigger the packet send. - This may be called at either from either the Tx queue code - or the interrupt handler. - -======================================================================*/ - -static void smc_hardware_send_packet(struct net_device * dev) -{ - struct smc_private *smc = netdev_priv(dev); - struct sk_buff *skb = smc->saved_skb; - unsigned int ioaddr = dev->base_addr; - u_char packet_no; - - if (!skb) { - netdev_err(dev, "In XMIT with no packet to send\n"); - return; - } - - /* There should be a packet slot waiting. */ - packet_no = inw(ioaddr + PNR_ARR) >> 8; - if (packet_no & 0x80) { - /* If not, there is a hardware problem! Likely an ejected card. */ - netdev_warn(dev, "hardware Tx buffer allocation failed, status %#2.2x\n", - packet_no); - dev_kfree_skb_irq(skb); - smc->saved_skb = NULL; - netif_start_queue(dev); - return; - } - - dev->stats.tx_bytes += skb->len; - /* The card should use the just-allocated buffer. */ - outw(packet_no, ioaddr + PNR_ARR); - /* point to the beginning of the packet */ - outw(PTR_AUTOINC , ioaddr + POINTER); - - /* Send the packet length (+6 for status, length and ctl byte) - and the status word (set to zeros). */ - { - u_char *buf = skb->data; - u_int length = skb->len; /* The chip will pad to ethernet min. */ - - netdev_dbg(dev, "Trying to xmit packet of length %d\n", length); - - /* send the packet length: +6 for status word, length, and ctl */ - outw(0, ioaddr + DATA_1); - outw(length + 6, ioaddr + DATA_1); - outsw(ioaddr + DATA_1, buf, length >> 1); - - /* The odd last byte, if there is one, goes in the control word. */ - outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1); - } - - /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */ - outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) | - (inw(ioaddr + INTERRUPT) & 0xff00), - ioaddr + INTERRUPT); - - /* The chip does the rest of the work. */ - outw(MC_ENQUEUE , ioaddr + MMU_CMD); - - smc->saved_skb = NULL; - dev_kfree_skb_irq(skb); - netif_trans_update(dev); - netif_start_queue(dev); -} - -/*====================================================================*/ - -static void smc_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - - netdev_notice(dev, "transmit timed out, Tx_status %2.2x status %4.4x.\n", - inw(ioaddr)&0xff, inw(ioaddr + 2)); - dev->stats.tx_errors++; - smc_reset(dev); - netif_trans_update(dev); /* prevent tx timeout */ - smc->saved_skb = NULL; - netif_wake_queue(dev); -} - -static netdev_tx_t smc_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - u_short num_pages; - short time_out, ir; - unsigned long flags; - - netif_stop_queue(dev); - - netdev_dbg(dev, "smc_start_xmit(length = %d) called, status %04x\n", - skb->len, inw(ioaddr + 2)); - - if (smc->saved_skb) { - /* THIS SHOULD NEVER HAPPEN. */ - dev->stats.tx_aborted_errors++; - netdev_dbg(dev, "Internal error -- sent packet while busy\n"); - return NETDEV_TX_BUSY; - } - smc->saved_skb = skb; - - num_pages = skb->len >> 8; - - if (num_pages > 7) { - netdev_err(dev, "Far too big packet error: %d pages\n", num_pages); - dev_kfree_skb (skb); - smc->saved_skb = NULL; - dev->stats.tx_dropped++; - return NETDEV_TX_OK; /* Do not re-queue this packet. */ - } - /* A packet is now waiting. */ - smc->packets_waiting++; - - spin_lock_irqsave(&smc->lock, flags); - SMC_SELECT_BANK(2); /* Paranoia, we should always be in window 2 */ - - /* need MC_RESET to keep the memory consistent. errata? */ - if (smc->rx_ovrn) { - outw(MC_RESET, ioaddr + MMU_CMD); - smc->rx_ovrn = 0; - } - - /* Allocate the memory; send the packet now if we win. */ - outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD); - for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) { - ir = inw(ioaddr+INTERRUPT); - if (ir & IM_ALLOC_INT) { - /* Acknowledge the interrupt, send the packet. */ - outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT); - smc_hardware_send_packet(dev); /* Send the packet now.. */ - spin_unlock_irqrestore(&smc->lock, flags); - return NETDEV_TX_OK; - } - } - - /* Otherwise defer until the Tx-space-allocated interrupt. */ - netdev_dbg(dev, "memory allocation deferred.\n"); - outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT); - spin_unlock_irqrestore(&smc->lock, flags); - - return NETDEV_TX_OK; -} - -/*====================================================================== - - Handle a Tx anomalous event. Entered while in Window 2. - -======================================================================*/ - -static void smc_tx_err(struct net_device * dev) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - int saved_packet = inw(ioaddr + PNR_ARR) & 0xff; - int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f; - int tx_status; - - /* select this as the packet to read from */ - outw(packet_no, ioaddr + PNR_ARR); - - /* read the first word from this packet */ - outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER); - - tx_status = inw(ioaddr + DATA_1); - - dev->stats.tx_errors++; - if (tx_status & TS_LOSTCAR) dev->stats.tx_carrier_errors++; - if (tx_status & TS_LATCOL) dev->stats.tx_window_errors++; - if (tx_status & TS_16COL) { - dev->stats.tx_aborted_errors++; - smc->tx_err++; - } - - if (tx_status & TS_SUCCESS) { - netdev_notice(dev, "Successful packet caused error interrupt?\n"); - } - /* re-enable transmit */ - SMC_SELECT_BANK(0); - outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR); - SMC_SELECT_BANK(2); - - outw(MC_FREEPKT, ioaddr + MMU_CMD); /* Free the packet memory. */ - - /* one less packet waiting for me */ - smc->packets_waiting--; - - outw(saved_packet, ioaddr + PNR_ARR); -} - -/*====================================================================*/ - -static void smc_eph_irq(struct net_device *dev) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - u_short card_stats, ephs; - - SMC_SELECT_BANK(0); - ephs = inw(ioaddr + EPH); - netdev_dbg(dev, "Ethernet protocol handler interrupt, status %4.4x.\n", - ephs); - /* Could be a counter roll-over warning: update stats. */ - card_stats = inw(ioaddr + COUNTER); - /* single collisions */ - dev->stats.collisions += card_stats & 0xF; - card_stats >>= 4; - /* multiple collisions */ - dev->stats.collisions += card_stats & 0xF; -#if 0 /* These are for when linux supports these statistics */ - card_stats >>= 4; /* deferred */ - card_stats >>= 4; /* excess deferred */ -#endif - /* If we had a transmit error we must re-enable the transmitter. */ - outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR); - - /* Clear a link error interrupt. */ - SMC_SELECT_BANK(1); - outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL); - outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, - ioaddr + CONTROL); - SMC_SELECT_BANK(2); -} - -/*====================================================================*/ - -static irqreturn_t smc_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr; - u_short saved_bank, saved_pointer, mask, status; - unsigned int handled = 1; - char bogus_cnt = INTR_WORK; /* Work we are willing to do. */ - - if (!netif_device_present(dev)) - return IRQ_NONE; - - ioaddr = dev->base_addr; - - netdev_dbg(dev, "SMC91c92 interrupt %d at %#x.\n", - irq, ioaddr); - - spin_lock(&smc->lock); - smc->watchdog = 0; - saved_bank = inw(ioaddr + BANK_SELECT); - if ((saved_bank & 0xff00) != 0x3300) { - /* The device does not exist -- the card could be off-line, or - maybe it has been ejected. */ - netdev_dbg(dev, "SMC91c92 interrupt %d for non-existent/ejected device.\n", - irq); - handled = 0; - goto irq_done; - } - - SMC_SELECT_BANK(2); - saved_pointer = inw(ioaddr + POINTER); - mask = inw(ioaddr + INTERRUPT) >> 8; - /* clear all interrupts */ - outw(0, ioaddr + INTERRUPT); - - do { /* read the status flag, and mask it */ - status = inw(ioaddr + INTERRUPT) & 0xff; - netdev_dbg(dev, "Status is %#2.2x (mask %#2.2x).\n", - status, mask); - if ((status & mask) == 0) { - if (bogus_cnt == INTR_WORK) - handled = 0; - break; - } - if (status & IM_RCV_INT) { - /* Got a packet(s). */ - smc_rx(dev); - } - if (status & IM_TX_INT) { - smc_tx_err(dev); - outw(IM_TX_INT, ioaddr + INTERRUPT); - } - status &= mask; - if (status & IM_TX_EMPTY_INT) { - outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT); - mask &= ~IM_TX_EMPTY_INT; - dev->stats.tx_packets += smc->packets_waiting; - smc->packets_waiting = 0; - } - if (status & IM_ALLOC_INT) { - /* Clear this interrupt so it doesn't happen again */ - mask &= ~IM_ALLOC_INT; - - smc_hardware_send_packet(dev); - - /* enable xmit interrupts based on this */ - mask |= (IM_TX_EMPTY_INT | IM_TX_INT); - - /* and let the card send more packets to me */ - netif_wake_queue(dev); - } - if (status & IM_RX_OVRN_INT) { - dev->stats.rx_errors++; - dev->stats.rx_fifo_errors++; - if (smc->duplex) - smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */ - outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT); - } - if (status & IM_EPH_INT) - smc_eph_irq(dev); - } while (--bogus_cnt); - - netdev_dbg(dev, " Restoring saved registers mask %2.2x bank %4.4x pointer %4.4x.\n", - mask, saved_bank, saved_pointer); - - /* restore state register */ - outw((mask<<8), ioaddr + INTERRUPT); - outw(saved_pointer, ioaddr + POINTER); - SMC_SELECT_BANK(saved_bank); - - netdev_dbg(dev, "Exiting interrupt IRQ%d.\n", irq); - -irq_done: - - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) { - /* Retrigger interrupt if needed */ - mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR); - set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR); - } - if (smc->manfid == MANFID_MOTOROLA) { - u_char cor; - cor = readb(smc->base + MOT_UART + CISREG_COR); - writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_UART + CISREG_COR); - writeb(cor, smc->base + MOT_UART + CISREG_COR); - cor = readb(smc->base + MOT_LAN + CISREG_COR); - writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_LAN + CISREG_COR); - writeb(cor, smc->base + MOT_LAN + CISREG_COR); - } - - if ((smc->base != NULL) && /* Megahertz MFC's */ - (smc->manfid == MANFID_MEGAHERTZ) && - (smc->cardid == PRODID_MEGAHERTZ_EM3288)) { - - u_char tmp; - tmp = readb(smc->base+MEGAHERTZ_ISR); - tmp = readb(smc->base+MEGAHERTZ_ISR); - - /* Retrigger interrupt if needed */ - writeb(tmp, smc->base + MEGAHERTZ_ISR); - writeb(tmp, smc->base + MEGAHERTZ_ISR); - } - - spin_unlock(&smc->lock); - return IRQ_RETVAL(handled); -} - -/*====================================================================*/ - -static void smc_rx(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - int rx_status; - int packet_length; /* Caution: not frame length, rather words - to transfer from the chip. */ - - /* Assertion: we are in Window 2. */ - - if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) { - netdev_err(dev, "smc_rx() with nothing on Rx FIFO\n"); - return; - } - - /* Reset the read pointer, and read the status and packet length. */ - outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER); - rx_status = inw(ioaddr + DATA_1); - packet_length = inw(ioaddr + DATA_1) & 0x07ff; - - netdev_dbg(dev, "Receive status %4.4x length %d.\n", - rx_status, packet_length); - - if (!(rx_status & RS_ERRORS)) { - /* do stuff to make a new packet */ - struct sk_buff *skb; - struct smc_private *smc = netdev_priv(dev); - - /* Note: packet_length adds 5 or 6 extra bytes here! */ - skb = netdev_alloc_skb(dev, packet_length+2); - - if (skb == NULL) { - netdev_dbg(dev, "Low memory, packet dropped.\n"); - dev->stats.rx_dropped++; - outw(MC_RELEASE, ioaddr + MMU_CMD); - return; - } - - packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6); - skb_reserve(skb, 2); - insw(ioaddr+DATA_1, skb_put(skb, packet_length), - (packet_length+1)>>1); - skb->protocol = eth_type_trans(skb, dev); - - netif_rx(skb); - smc->last_rx = jiffies; - dev->stats.rx_packets++; - dev->stats.rx_bytes += packet_length; - if (rx_status & RS_MULTICAST) - dev->stats.multicast++; - } else { - /* error ... */ - dev->stats.rx_errors++; - - if (rx_status & RS_ALGNERR) dev->stats.rx_frame_errors++; - if (rx_status & (RS_TOOSHORT | RS_TOOLONG)) - dev->stats.rx_length_errors++; - if (rx_status & RS_BADCRC) dev->stats.rx_crc_errors++; - } - /* Let the MMU free the memory of this packet. */ - outw(MC_RELEASE, ioaddr + MMU_CMD); -} - -/*====================================================================== - - Set the receive mode. - - This routine is used by both the protocol level to notify us of - promiscuous/multicast mode changes, and by the open/reset code to - initialize the Rx registers. We always set the multicast list and - leave the receiver running. - -======================================================================*/ - -static void set_rx_mode(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - struct smc_private *smc = netdev_priv(dev); - unsigned char multicast_table[8]; - unsigned long flags; - u_short rx_cfg_setting; - int i; - - memset(multicast_table, 0, sizeof(multicast_table)); - - if (dev->flags & IFF_PROMISC) { - rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti; - } else if (dev->flags & IFF_ALLMULTI) - rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti; - else { - if (!netdev_mc_empty(dev)) { - struct netdev_hw_addr *ha; - - netdev_for_each_mc_addr(ha, dev) { - u_int position = ether_crc(6, ha->addr); - multicast_table[position >> 29] |= 1 << ((position >> 26) & 7); - } - } - rx_cfg_setting = RxStripCRC | RxEnable; - } - - /* Load MC table and Rx setting into the chip without interrupts. */ - spin_lock_irqsave(&smc->lock, flags); - SMC_SELECT_BANK(3); - for (i = 0; i < 8; i++) - outb(multicast_table[i], ioaddr + MULTICAST0 + i); - SMC_SELECT_BANK(0); - outw(rx_cfg_setting, ioaddr + RCR); - SMC_SELECT_BANK(2); - spin_unlock_irqrestore(&smc->lock, flags); -} - -/*====================================================================== - - Senses when a card's config changes. Here, it's coax or TP. - -======================================================================*/ - -static int s9k_config(struct net_device *dev, struct ifmap *map) -{ - struct smc_private *smc = netdev_priv(dev); - if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { - if (smc->cfg & CFG_MII_SELECT) - return -EOPNOTSUPP; - else if (map->port > 2) - return -EINVAL; - WRITE_ONCE(dev->if_port, map->port); - netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]); - smc_reset(dev); - } - return 0; -} - -/*====================================================================== - - Reset the chip, reloading every register that might be corrupted. - -======================================================================*/ - -/* - Set transceiver type, perhaps to something other than what the user - specified in dev->if_port. -*/ -static void smc_set_xcvr(struct net_device *dev, int if_port) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - u_short saved_bank; - - saved_bank = inw(ioaddr + BANK_SELECT); - SMC_SELECT_BANK(1); - if (if_port == 2) { - outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG); - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) - set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR); - smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002); - } else { - outw(smc->cfg, ioaddr + CONFIG); - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) - mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR); - smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001); - } - SMC_SELECT_BANK(saved_bank); -} - -static void smc_reset(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - struct smc_private *smc = netdev_priv(dev); - int i; - - netdev_dbg(dev, "smc91c92 reset called.\n"); - - /* The first interaction must be a write to bring the chip out - of sleep mode. */ - SMC_SELECT_BANK(0); - /* Reset the chip. */ - outw(RCR_SOFTRESET, ioaddr + RCR); - udelay(10); - - /* Clear the transmit and receive configuration registers. */ - outw(RCR_CLEAR, ioaddr + RCR); - outw(TCR_CLEAR, ioaddr + TCR); - - /* Set the Window 1 control, configuration and station addr registers. - No point in writing the I/O base register ;-> */ - SMC_SELECT_BANK(1); - /* Automatically release successfully transmitted packets, - Accept link errors, counter and Tx error interrupts. */ - outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, - ioaddr + CONTROL); - smc_set_xcvr(dev, dev->if_port); - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) - outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) | - (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00), - ioaddr - 0x10 + OSITECH_AUI_PWR); - - /* Fill in the physical address. The databook is wrong about the order! */ - for (i = 0; i < 6; i += 2) - outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i], - ioaddr + ADDR0 + i); - - /* Reset the MMU */ - SMC_SELECT_BANK(2); - outw(MC_RESET, ioaddr + MMU_CMD); - outw(0, ioaddr + INTERRUPT); - - /* Re-enable the chip. */ - SMC_SELECT_BANK(0); - outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) | - TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR); - set_rx_mode(dev); - - if (smc->cfg & CFG_MII_SELECT) { - SMC_SELECT_BANK(3); - - /* Reset MII */ - mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000); - - /* Advertise 100F, 100H, 10F, 10H */ - mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1); - - /* Restart MII autonegotiation */ - mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000); - mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200); - } - - /* Enable interrupts. */ - SMC_SELECT_BANK(2); - outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8, - ioaddr + INTERRUPT); -} - -/*====================================================================== - - Media selection timer routine - -======================================================================*/ - -static void media_check(struct timer_list *t) -{ - struct smc_private *smc = timer_container_of(smc, t, media); - struct net_device *dev = smc->mii_if.dev; - unsigned int ioaddr = dev->base_addr; - u_short i, media, saved_bank; - u_short link; - unsigned long flags; - - spin_lock_irqsave(&smc->lock, flags); - - saved_bank = inw(ioaddr + BANK_SELECT); - - if (!netif_device_present(dev)) - goto reschedule; - - SMC_SELECT_BANK(2); - - /* need MC_RESET to keep the memory consistent. errata? */ - if (smc->rx_ovrn) { - outw(MC_RESET, ioaddr + MMU_CMD); - smc->rx_ovrn = 0; - } - i = inw(ioaddr + INTERRUPT); - SMC_SELECT_BANK(0); - media = inw(ioaddr + EPH) & EPH_LINK_OK; - SMC_SELECT_BANK(1); - media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1; - - SMC_SELECT_BANK(saved_bank); - spin_unlock_irqrestore(&smc->lock, flags); - - /* Check for pending interrupt with watchdog flag set: with - this, we can limp along even if the interrupt is blocked */ - if (smc->watchdog++ && ((i>>8) & i)) { - if (!smc->fast_poll) - netdev_info(dev, "interrupt(s) dropped!\n"); - local_irq_save(flags); - smc_interrupt(dev->irq, dev); - local_irq_restore(flags); - smc->fast_poll = HZ; - } - if (smc->fast_poll) { - smc->fast_poll--; - smc->media.expires = jiffies + HZ/100; - add_timer(&smc->media); - return; - } - - spin_lock_irqsave(&smc->lock, flags); - - saved_bank = inw(ioaddr + BANK_SELECT); - - if (smc->cfg & CFG_MII_SELECT) { - if (smc->mii_if.phy_id < 0) - goto reschedule; - - SMC_SELECT_BANK(3); - link = mdio_read(dev, smc->mii_if.phy_id, 1); - if (!link || (link == 0xffff)) { - netdev_info(dev, "MII is missing!\n"); - smc->mii_if.phy_id = -1; - goto reschedule; - } - - link &= 0x0004; - if (link != smc->link_status) { - u_short p = mdio_read(dev, smc->mii_if.phy_id, 5); - netdev_info(dev, "%s link beat\n", link ? "found" : "lost"); - smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40)) - ? TCR_FDUPLX : 0); - if (link) { - netdev_info(dev, "autonegotiation complete: " - "%dbaseT-%cD selected\n", - (p & 0x0180) ? 100 : 10, smc->duplex ? 'F' : 'H'); - } - SMC_SELECT_BANK(0); - outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR); - smc->link_status = link; - } - goto reschedule; - } - - /* Ignore collisions unless we've had no rx's recently */ - if (time_after(jiffies, smc->last_rx + HZ)) { - if (smc->tx_err || (smc->media_status & EPH_16COL)) - media |= EPH_16COL; - } - smc->tx_err = 0; - - if (media != smc->media_status) { - if ((media & smc->media_status & 1) && - ((smc->media_status ^ media) & EPH_LINK_OK)) - netdev_info(dev, "%s link beat\n", - smc->media_status & EPH_LINK_OK ? "lost" : "found"); - else if ((media & smc->media_status & 2) && - ((smc->media_status ^ media) & EPH_16COL)) - netdev_info(dev, "coax cable %s\n", - media & EPH_16COL ? "problem" : "ok"); - if (dev->if_port == 0) { - if (media & 1) { - if (media & EPH_LINK_OK) - netdev_info(dev, "flipped to 10baseT\n"); - else - smc_set_xcvr(dev, 2); - } else { - if (media & EPH_16COL) - smc_set_xcvr(dev, 1); - else - netdev_info(dev, "flipped to 10base2\n"); - } - } - smc->media_status = media; - } - -reschedule: - smc->media.expires = jiffies + HZ; - add_timer(&smc->media); - SMC_SELECT_BANK(saved_bank); - spin_unlock_irqrestore(&smc->lock, flags); -} - -static int smc_link_ok(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - struct smc_private *smc = netdev_priv(dev); - - if (smc->cfg & CFG_MII_SELECT) { - return mii_link_ok(&smc->mii_if); - } else { - SMC_SELECT_BANK(0); - return inw(ioaddr + EPH) & EPH_LINK_OK; - } -} - -static void smc_netdev_get_ecmd(struct net_device *dev, - struct ethtool_link_ksettings *ecmd) -{ - u16 tmp; - unsigned int ioaddr = dev->base_addr; - u32 supported; - - supported = (SUPPORTED_TP | SUPPORTED_AUI | - SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full); - - SMC_SELECT_BANK(1); - tmp = inw(ioaddr + CONFIG); - ecmd->base.port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP; - ecmd->base.speed = SPEED_10; - ecmd->base.phy_address = ioaddr + MGMT; - - SMC_SELECT_BANK(0); - tmp = inw(ioaddr + TCR); - ecmd->base.duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF; - - ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported, - supported); -} - -static int smc_netdev_set_ecmd(struct net_device *dev, - const struct ethtool_link_ksettings *ecmd) -{ - u16 tmp; - unsigned int ioaddr = dev->base_addr; - - if (ecmd->base.speed != SPEED_10) - return -EINVAL; - if (ecmd->base.duplex != DUPLEX_HALF && - ecmd->base.duplex != DUPLEX_FULL) - return -EINVAL; - if (ecmd->base.port != PORT_TP && ecmd->base.port != PORT_AUI) - return -EINVAL; - - if (ecmd->base.port == PORT_AUI) - smc_set_xcvr(dev, 1); - else - smc_set_xcvr(dev, 0); - - SMC_SELECT_BANK(0); - tmp = inw(ioaddr + TCR); - if (ecmd->base.duplex == DUPLEX_FULL) - tmp |= TCR_FDUPLX; - else - tmp &= ~TCR_FDUPLX; - outw(tmp, ioaddr + TCR); - - return 0; -} - -static int check_if_running(struct net_device *dev) -{ - if (!netif_running(dev)) - return -EINVAL; - return 0; -} - -static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - strscpy(info->version, DRV_VERSION, sizeof(info->version)); -} - -static int smc_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings *ecmd) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - u16 saved_bank = inw(ioaddr + BANK_SELECT); - unsigned long flags; - - spin_lock_irqsave(&smc->lock, flags); - SMC_SELECT_BANK(3); - if (smc->cfg & CFG_MII_SELECT) - mii_ethtool_get_link_ksettings(&smc->mii_if, ecmd); - else - smc_netdev_get_ecmd(dev, ecmd); - SMC_SELECT_BANK(saved_bank); - spin_unlock_irqrestore(&smc->lock, flags); - return 0; -} - -static int smc_set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings *ecmd) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - u16 saved_bank = inw(ioaddr + BANK_SELECT); - int ret; - unsigned long flags; - - spin_lock_irqsave(&smc->lock, flags); - SMC_SELECT_BANK(3); - if (smc->cfg & CFG_MII_SELECT) - ret = mii_ethtool_set_link_ksettings(&smc->mii_if, ecmd); - else - ret = smc_netdev_set_ecmd(dev, ecmd); - SMC_SELECT_BANK(saved_bank); - spin_unlock_irqrestore(&smc->lock, flags); - return ret; -} - -static u32 smc_get_link(struct net_device *dev) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - u16 saved_bank = inw(ioaddr + BANK_SELECT); - u32 ret; - unsigned long flags; - - spin_lock_irqsave(&smc->lock, flags); - SMC_SELECT_BANK(3); - ret = smc_link_ok(dev); - SMC_SELECT_BANK(saved_bank); - spin_unlock_irqrestore(&smc->lock, flags); - return ret; -} - -static int smc_nway_reset(struct net_device *dev) -{ - struct smc_private *smc = netdev_priv(dev); - if (smc->cfg & CFG_MII_SELECT) { - unsigned int ioaddr = dev->base_addr; - u16 saved_bank = inw(ioaddr + BANK_SELECT); - int res; - - SMC_SELECT_BANK(3); - res = mii_nway_restart(&smc->mii_if); - SMC_SELECT_BANK(saved_bank); - - return res; - } else - return -EOPNOTSUPP; -} - -static const struct ethtool_ops ethtool_ops = { - .begin = check_if_running, - .get_drvinfo = smc_get_drvinfo, - .get_link = smc_get_link, - .nway_reset = smc_nway_reset, - .get_link_ksettings = smc_get_link_ksettings, - .set_link_ksettings = smc_set_link_ksettings, -}; - -static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct smc_private *smc = netdev_priv(dev); - struct mii_ioctl_data *mii = if_mii(rq); - int rc = 0; - u16 saved_bank; - unsigned int ioaddr = dev->base_addr; - unsigned long flags; - - if (!netif_running(dev)) - return -EINVAL; - - spin_lock_irqsave(&smc->lock, flags); - saved_bank = inw(ioaddr + BANK_SELECT); - SMC_SELECT_BANK(3); - rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL); - SMC_SELECT_BANK(saved_bank); - spin_unlock_irqrestore(&smc->lock, flags); - return rc; -} - -static const struct pcmcia_device_id smc91c92_ids[] = { - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0109, 0x0501), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0140, 0x000a), - PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), - PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), - PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), - PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef), - PCMCIA_PFC_DEVICE_PROD_ID12(0, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), - PCMCIA_PFC_DEVICE_PROD_ID12(0, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e), - PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9), - PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed), - PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x016c, 0x0020), - PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0023), - PCMCIA_DEVICE_PROD_ID123("BASICS by New Media Corporation", "Ethernet", "SMC91C94", 0x23c78a9d, 0x00b2e941, 0xcef397fb), - PCMCIA_DEVICE_PROD_ID12("ARGOSY", "Fast Ethernet PCCard", 0x78f308dc, 0xdcea68bc), - PCMCIA_DEVICE_PROD_ID12("dit Co., Ltd.", "PC Card-10/100BTX", 0xe59365c8, 0x6a2161d1), - PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L100C", 0x6a26d1cf, 0xc16ce9c5), - PCMCIA_DEVICE_PROD_ID12("Farallon", "Farallon Enet", 0x58d93fc4, 0x244734e9), - PCMCIA_DEVICE_PROD_ID12("Megahertz", "CC10BT/2", 0x33234748, 0x3c95b953), - PCMCIA_DEVICE_PROD_ID12("MELCO/SMC", "LPC-TX", 0xa2cd8e6d, 0x42da662a), - PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Four of Diamonds Ethernet", 0xc2f80cd, 0xb3466314), - PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Seven of Diamonds Ethernet", 0xc2f80cd, 0x194b650a), - PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast Ethernet PCCard", 0x281f1c5d, 0xdcea68bc), - PCMCIA_DEVICE_PROD_ID12("Psion", "10Mb Ethernet", 0x4ef00b21, 0x844be9e9), - PCMCIA_DEVICE_PROD_ID12("SMC", "EtherEZ Ethernet 8020", 0xc4f8b18b, 0x4a0eeb2d), - /* These conflict with other cards! */ - /* PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0100), */ - /* PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), */ - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, smc91c92_ids); - -static struct pcmcia_driver smc91c92_cs_driver = { - .owner = THIS_MODULE, - .name = "smc91c92_cs", - .probe = smc91c92_probe, - .remove = smc91c92_detach, - .id_table = smc91c92_ids, - .suspend = smc91c92_suspend, - .resume = smc91c92_resume, -}; -module_pcmcia_driver(smc91c92_cs_driver); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c index e2240b68ad98..2ab6ecac6422 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c @@ -100,6 +100,8 @@ static int nvt_gmac_probe(struct platform_device *pdev) if (!priv) return dev_err_probe(dev, -ENOMEM, "Failed to allocate private data\n"); + priv->dev = dev; + priv->regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, "nuvoton,sys", 1, &priv->macid); if (IS_ERR(priv->regmap)) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 01a983001ab4..3591755ea30b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1410,8 +1410,6 @@ static int stmmac_phylink_setup(struct stmmac_priv *priv) priv->tx_lpi_clk_stop = priv->plat->flags & STMMAC_FLAG_EN_TX_LPI_CLOCKGATING; - config->default_an_inband = priv->plat->default_an_inband; - /* Get the PHY interface modes (at the PHY end of the link) that * are supported by the platform. */ @@ -1419,6 +1417,8 @@ static int stmmac_phylink_setup(struct stmmac_priv *priv) priv->plat->get_interfaces(priv, priv->plat->bsp_priv, config->supported_interfaces); + config->default_an_inband = priv->plat->default_an_inband; + /* Set the platform/firmware specified interface mode if the * supported interfaces have not already been provided using * phy_interface as a last resort. @@ -5549,9 +5549,12 @@ read_again: break; /* Prefetch the next RX descriptor */ - rx_q->cur_rx = STMMAC_NEXT_ENTRY(rx_q->cur_rx, - priv->dma_conf.dma_rx_size); - next_entry = rx_q->cur_rx; + next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx, + priv->dma_conf.dma_rx_size); + if (unlikely(next_entry == rx_q->dirty_rx)) + break; + + rx_q->cur_rx = next_entry; np = stmmac_get_rx_desc(priv, rx_q, next_entry); @@ -5686,7 +5689,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) dma_dir = page_pool_get_dma_dir(rx_q->page_pool); bufsz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE; - limit = min(priv->dma_conf.dma_rx_size - 1, (unsigned int)limit); if (netif_msg_rx_status(priv)) { void *rx_head = stmmac_get_rx_desc(priv, rx_q, 0); @@ -5733,9 +5735,12 @@ read_again: if (unlikely(status & dma_own)) break; - rx_q->cur_rx = STMMAC_NEXT_ENTRY(rx_q->cur_rx, - priv->dma_conf.dma_rx_size); - next_entry = rx_q->cur_rx; + next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx, + priv->dma_conf.dma_rx_size); + if (unlikely(next_entry == rx_q->dirty_rx)) + break; + + rx_q->cur_rx = next_entry; np = stmmac_get_rx_desc(priv, rx_q, next_entry); diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/ethernet/ti/icssm/icssm_prueth.c index 53bbd9290904..b7e94244355a 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c @@ -1825,6 +1825,7 @@ static int icssm_prueth_probe(struct platform_device *pdev) dev_err(dev, "%pOF error reading port_id %d\n", eth_node, ret); of_node_put(eth_node); + of_node_put(eth_ports_node); return ret; } diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index d3772d01e00b..2451f6b20b11 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -2480,8 +2480,11 @@ int wx_sw_init(struct wx *wx) wx->oem_svid = pdev->subsystem_vendor; wx->oem_ssid = pdev->subsystem_device; wx->bus.device = PCI_SLOT(pdev->devfn); - wx->bus.func = FIELD_GET(WX_CFG_PORT_ST_LANID, - rd32(wx, WX_CFG_PORT_ST)); + if (pdev->is_virtfn) + wx->bus.func = PCI_FUNC(pdev->devfn); + else + wx->bus.func = FIELD_GET(WX_CFG_PORT_ST_LANID, + rd32(wx, WX_CFG_PORT_ST)); if (wx->oem_svid == PCI_VENDOR_ID_WANGXUN || pdev->is_virtfn) { diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c index 29cdbed2e5ec..94ff8f5f0b4c 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c @@ -99,8 +99,8 @@ int wx_request_msix_irqs_vf(struct wx *wx) } } - err = request_threaded_irq(wx->msix_entry->vector, wx_msix_misc_vf, - NULL, IRQF_ONESHOT, netdev->name, wx); + err = request_irq(wx->msix_entry->vector, wx_msix_misc_vf, + 0, netdev->name, wx); if (err) { wx_err(wx, "request_irq for msix_other failed: %d\n", err); goto free_queue_irqs; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index ec32a5f422f2..8b7c3753bb6a 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -864,7 +864,8 @@ static int txgbe_probe(struct pci_dev *pdev, "0x%08x", etrack_id); } - if (etrack_id < 0x20010) + if (wx->mac.type == wx_mac_sp && + ((etrack_id & 0xfffff) < 0x20010)) dev_warn(&pdev->dev, "Please upgrade the firmware to 0x20010 or above.\n"); err = txgbe_test_hostif(wx); diff --git a/drivers/net/fddi/defza.c b/drivers/net/fddi/defza.c index 064fa484f797..9bfecc87d6b2 100644 --- a/drivers/net/fddi/defza.c +++ b/drivers/net/fddi/defza.c @@ -984,7 +984,7 @@ static irqreturn_t fza_interrupt(int irq, void *dev_id) case FZA_STATE_UNINITIALIZED: netif_carrier_off(dev); - timer_delete_sync(&fp->reset_timer); + timer_delete_sync_try(&fp->reset_timer); fp->ring_cmd_index = 0; fp->ring_uns_index = 0; fp->ring_rmc_tx_index = 0; @@ -1018,7 +1018,9 @@ static irqreturn_t fza_interrupt(int irq, void *dev_id) fp->queue_active = 0; netif_stop_queue(dev); pr_debug("%s: queue stopped\n", fp->name); - timer_delete_sync(&fp->reset_timer); + + spin_lock(&fp->lock); + timer_delete(&fp->reset_timer); pr_warn("%s: halted, reason: %x\n", fp->name, FZA_STATUS_GET_HALT(status)); fza_regs_dump(fp); @@ -1027,6 +1029,8 @@ static irqreturn_t fza_interrupt(int irq, void *dev_id) fp->timer_state = 0; fp->reset_timer.expires = jiffies + 45 * HZ; add_timer(&fp->reset_timer); + spin_unlock(&fp->lock); + break; default: @@ -1046,7 +1050,9 @@ static irqreturn_t fza_interrupt(int irq, void *dev_id) static void fza_reset_timer(struct timer_list *t) { struct fza_private *fp = timer_container_of(fp, t, reset_timer); + unsigned long flags; + spin_lock_irqsave(&fp->lock, flags); if (!fp->timer_state) { pr_err("%s: RESET timed out!\n", fp->name); pr_info("%s: trying harder...\n", fp->name); @@ -1069,6 +1075,7 @@ static void fza_reset_timer(struct timer_list *t) fp->reset_timer.expires = jiffies + 45 * HZ; } add_timer(&fp->reset_timer); + spin_unlock_irqrestore(&fp->lock, flags); } static int fza_set_mac_address(struct net_device *dev, void *addr) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 70b9e58b9b78..5150f2e4f66b 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -2400,6 +2400,7 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) return -ENODEV; } + local_bh_disable(); udp_tunnel_xmit_skb(rt, sk, skb_to_send, fl4.saddr, fl4.daddr, inet_dscp_to_dsfield(fl4.flowi4_dscp), @@ -2409,6 +2410,7 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) !net_eq(sock_net(sk), dev_net(gtp->dev)), false, 0); + local_bh_enable(); return 0; } diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c deleted file mode 100644 index c8b2dc5c1bec..000000000000 --- a/drivers/net/hamradio/6pack.c +++ /dev/null @@ -1,912 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * 6pack.c This module implements the 6pack protocol for kernel-based - * devices like TTY. It interfaces between a raw TTY and the - * kernel's AX.25 protocol layers. - * - * Authors: Andreas Könsgen - * Ralf Baechle DL5RB - * - * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by - * - * Laurence Culhane, - * Fred N. van Kempen, - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* sixpack priority commands */ -#define SIXP_SEOF 0x40 /* start and end of a 6pack frame */ -#define SIXP_TX_URUN 0x48 /* transmit overrun */ -#define SIXP_RX_ORUN 0x50 /* receive overrun */ -#define SIXP_RX_BUF_OVL 0x58 /* receive buffer overflow */ - -#define SIXP_CHKSUM 0xFF /* valid checksum of a 6pack frame */ - -/* masks to get certain bits out of the status bytes sent by the TNC */ - -#define SIXP_CMD_MASK 0xC0 -#define SIXP_CHN_MASK 0x07 -#define SIXP_PRIO_CMD_MASK 0x80 -#define SIXP_STD_CMD_MASK 0x40 -#define SIXP_PRIO_DATA_MASK 0x38 -#define SIXP_TX_MASK 0x20 -#define SIXP_RX_MASK 0x10 -#define SIXP_RX_DCD_MASK 0x18 -#define SIXP_LEDS_ON 0x78 -#define SIXP_LEDS_OFF 0x60 -#define SIXP_CON 0x08 -#define SIXP_STA 0x10 - -#define SIXP_FOUND_TNC 0xe9 -#define SIXP_CON_ON 0x68 -#define SIXP_DCD_MASK 0x08 -#define SIXP_DAMA_OFF 0 - -/* default level 2 parameters */ -#define SIXP_TXDELAY 25 /* 250 ms */ -#define SIXP_PERSIST 50 /* in 256ths */ -#define SIXP_SLOTTIME 10 /* 100 ms */ -#define SIXP_INIT_RESYNC_TIMEOUT (3*HZ/2) /* in 1 s */ -#define SIXP_RESYNC_TIMEOUT 5*HZ /* in 1 s */ - -/* 6pack configuration. */ -#define SIXP_NRUNIT 31 /* MAX number of 6pack channels */ -#define SIXP_MTU 256 /* Default MTU */ - -enum sixpack_flags { - SIXPF_ERROR, /* Parity, etc. error */ -}; - -struct sixpack { - /* Various fields. */ - struct tty_struct *tty; /* ptr to TTY structure */ - struct net_device *dev; /* easy for intr handling */ - - /* These are pointers to the malloc()ed frame buffers. */ - int rcount; /* received chars counter */ - unsigned char *xbuff; /* transmitter buffer */ - unsigned char *xhead; /* next byte to XMIT */ - int xleft; /* bytes left in XMIT queue */ - - u8 raw_buf[4]; - u8 cooked_buf[400]; - - unsigned int rx_count; - unsigned int rx_count_cooked; - spinlock_t rxlock; - - unsigned long flags; /* Flag values/ mode etc */ - unsigned char mode; /* 6pack mode */ - - /* 6pack stuff */ - unsigned char tx_delay; - unsigned char persistence; - unsigned char slottime; - unsigned char duplex; - unsigned char led_state; - u8 status; - u8 status1; - unsigned char status2; - unsigned char tx_enable; - unsigned char tnc_state; - - struct timer_list tx_t; - struct timer_list resync_t; - spinlock_t lock; -}; - -#define AX25_6PACK_HEADER_LEN 0 - -static void sixpack_decode(struct sixpack *, const u8 *, size_t); -static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char); - -/* - * Perform the persistence/slottime algorithm for CSMA access. If the - * persistence check was successful, write the data to the serial driver. - * Note that in case of DAMA operation, the data is not sent here. - */ - -static void sp_xmit_on_air(struct timer_list *t) -{ - struct sixpack *sp = timer_container_of(sp, t, tx_t); - int actual, when = sp->slottime; - static unsigned char random; - - random = random * 17 + 41; - - if (((sp->status1 & SIXP_DCD_MASK) == 0) && (random < sp->persistence)) { - sp->led_state = 0x70; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->tx_enable = 1; - actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2); - sp->xleft -= actual; - sp->xhead += actual; - sp->led_state = 0x60; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->status2 = 0; - } else - mod_timer(&sp->tx_t, jiffies + ((when + 1) * HZ) / 100); -} - -/* ----> 6pack timer interrupt handler and friends. <---- */ - -/* Encapsulate one AX.25 frame and stuff into a TTY queue. */ -static void sp_encaps(struct sixpack *sp, unsigned char *icp, int len) -{ - unsigned char *msg, *p = icp; - int actual, count; - - if (len > AX25_MTU + 73) { - msg = "oversized transmit packet!"; - goto out_drop; - } - - if (p[0] > 5) { - msg = "invalid KISS command"; - goto out_drop; - } - - if ((p[0] != 0) && (len > 2)) { - msg = "KISS control packet too long"; - goto out_drop; - } - - if ((p[0] == 0) && (len < 15)) { - msg = "bad AX.25 packet to transmit"; - goto out_drop; - } - - count = encode_sixpack(p, sp->xbuff, len, sp->tx_delay); - set_bit(TTY_DO_WRITE_WAKEUP, &sp->tty->flags); - - switch (p[0]) { - case 1: sp->tx_delay = p[1]; - return; - case 2: sp->persistence = p[1]; - return; - case 3: sp->slottime = p[1]; - return; - case 4: /* ignored */ - return; - case 5: sp->duplex = p[1]; - return; - } - - if (p[0] != 0) - return; - - /* - * In case of fullduplex or DAMA operation, we don't take care about the - * state of the DCD or of any timers, as the determination of the - * correct time to send is the job of the AX.25 layer. We send - * immediately after data has arrived. - */ - if (sp->duplex == 1) { - sp->led_state = 0x70; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->tx_enable = 1; - actual = sp->tty->ops->write(sp->tty, sp->xbuff, count); - sp->xleft = count - actual; - sp->xhead = sp->xbuff + actual; - sp->led_state = 0x60; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - } else { - sp->xleft = count; - sp->xhead = sp->xbuff; - sp->status2 = count; - sp_xmit_on_air(&sp->tx_t); - } - - return; - -out_drop: - sp->dev->stats.tx_dropped++; - netif_start_queue(sp->dev); - if (net_ratelimit()) - printk(KERN_DEBUG "%s: %s - dropped.\n", sp->dev->name, msg); -} - -/* Encapsulate an IP datagram and kick it into a TTY queue. */ - -static netdev_tx_t sp_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct sixpack *sp = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - spin_lock_bh(&sp->lock); - /* We were not busy, so we are now... :-) */ - netif_stop_queue(dev); - dev->stats.tx_bytes += skb->len; - sp_encaps(sp, skb->data, skb->len); - spin_unlock_bh(&sp->lock); - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -static int sp_open_dev(struct net_device *dev) -{ - struct sixpack *sp = netdev_priv(dev); - - if (sp->tty == NULL) - return -ENODEV; - return 0; -} - -/* Close the low-level part of the 6pack channel. */ -static int sp_close(struct net_device *dev) -{ - struct sixpack *sp = netdev_priv(dev); - - spin_lock_bh(&sp->lock); - if (sp->tty) { - /* TTY discipline is running. */ - clear_bit(TTY_DO_WRITE_WAKEUP, &sp->tty->flags); - } - netif_stop_queue(dev); - spin_unlock_bh(&sp->lock); - - return 0; -} - -static int sp_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr_ax25 *sa = addr; - - netif_tx_lock_bh(dev); - netif_addr_lock(dev); - __dev_addr_set(dev, &sa->sax25_call, AX25_ADDR_LEN); - netif_addr_unlock(dev); - netif_tx_unlock_bh(dev); - - return 0; -} - -static const struct net_device_ops sp_netdev_ops = { - .ndo_open = sp_open_dev, - .ndo_stop = sp_close, - .ndo_start_xmit = sp_xmit, - .ndo_set_mac_address = sp_set_mac_address, -}; - -static void sp_setup(struct net_device *dev) -{ - /* Finish setting up the DEVICE info. */ - dev->netdev_ops = &sp_netdev_ops; - dev->mtu = SIXP_MTU; - dev->hard_header_len = AX25_MAX_HEADER_LEN; - dev->header_ops = &ax25_header_ops; - - dev->addr_len = AX25_ADDR_LEN; - dev->type = ARPHRD_AX25; - dev->tx_queue_len = 10; - - /* Only activated in AX.25 mode */ - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); - - dev->flags = 0; -} - -/* Send one completely decapsulated IP datagram to the IP layer. */ - -/* - * This is the routine that sends the received data to the kernel AX.25. - * 'cmd' is the KISS command. For AX.25 data, it is zero. - */ - -static void sp_bump(struct sixpack *sp, char cmd) -{ - struct sk_buff *skb; - int count; - u8 *ptr; - - count = sp->rcount + 1; - - sp->dev->stats.rx_bytes += count; - - if ((skb = dev_alloc_skb(count + 1)) == NULL) - goto out_mem; - - ptr = skb_put(skb, count + 1); - *ptr++ = cmd; /* KISS command */ - - memcpy(ptr, sp->cooked_buf + 1, count); - skb->protocol = ax25_type_trans(skb, sp->dev); - netif_rx(skb); - sp->dev->stats.rx_packets++; - - return; - -out_mem: - sp->dev->stats.rx_dropped++; -} - - -/* ----------------------------------------------------------------------- */ - -/* - * Called by the TTY driver when there's room for more data. If we have - * more packets to send, we send them here. - */ -static void sixpack_write_wakeup(struct tty_struct *tty) -{ - struct sixpack *sp = tty->disc_data; - int actual; - - if (!sp) - return; - if (sp->xleft <= 0) { - /* Now serial buffer is almost free & we can start - * transmission of another packet */ - sp->dev->stats.tx_packets++; - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - sp->tx_enable = 0; - netif_wake_queue(sp->dev); - return; - } - - if (sp->tx_enable) { - actual = tty->ops->write(tty, sp->xhead, sp->xleft); - sp->xleft -= actual; - sp->xhead += actual; - } -} - -/* ----------------------------------------------------------------------- */ - -/* - * Handle the 'receiver data ready' interrupt. - * This function is called by the tty module in the kernel when - * a block of 6pack data has been received, which can now be decapsulated - * and sent on to some IP layer for further processing. - */ -static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp, - const u8 *fp, size_t count) -{ - struct sixpack *sp; - - if (!count) - return; - - sp = tty->disc_data; - if (!sp) - return; - - /* Read the characters out of the buffer */ - while (count--) { - if (fp && *fp++) { - if (!test_and_set_bit(SIXPF_ERROR, &sp->flags)) - sp->dev->stats.rx_errors++; - cp++; - continue; - } - sixpack_decode(sp, cp, 1); - cp++; - } - - tty_unthrottle(tty); -} - -/* - * Try to resync the TNC. Called by the resync timer defined in - * decode_prio_command - */ - -#define TNC_UNINITIALIZED 0 -#define TNC_UNSYNC_STARTUP 1 -#define TNC_UNSYNCED 2 -#define TNC_IN_SYNC 3 - -static void __tnc_set_sync_state(struct sixpack *sp, int new_tnc_state) -{ - char *msg; - - switch (new_tnc_state) { - default: /* gcc oh piece-o-crap ... */ - case TNC_UNSYNC_STARTUP: - msg = "Synchronizing with TNC"; - break; - case TNC_UNSYNCED: - msg = "Lost synchronization with TNC\n"; - break; - case TNC_IN_SYNC: - msg = "Found TNC"; - break; - } - - sp->tnc_state = new_tnc_state; - printk(KERN_INFO "%s: %s\n", sp->dev->name, msg); -} - -static inline void tnc_set_sync_state(struct sixpack *sp, int new_tnc_state) -{ - int old_tnc_state = sp->tnc_state; - - if (old_tnc_state != new_tnc_state) - __tnc_set_sync_state(sp, new_tnc_state); -} - -static void resync_tnc(struct timer_list *t) -{ - struct sixpack *sp = timer_container_of(sp, t, resync_t); - static char resync_cmd = 0xe8; - - /* clear any data that might have been received */ - - sp->rx_count = 0; - sp->rx_count_cooked = 0; - - /* reset state machine */ - - sp->status = 1; - sp->status1 = 1; - sp->status2 = 0; - - /* resync the TNC */ - - sp->led_state = 0x60; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->tty->ops->write(sp->tty, &resync_cmd, 1); - - - /* Start resync timer again -- the TNC might be still absent */ - mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT); -} - -static inline int tnc_init(struct sixpack *sp) -{ - unsigned char inbyte = 0xe8; - - tnc_set_sync_state(sp, TNC_UNSYNC_STARTUP); - - sp->tty->ops->write(sp->tty, &inbyte, 1); - - mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT); - - return 0; -} - -/* - * Open the high-level part of the 6pack channel. - * This function is called by the TTY module when the - * 6pack line discipline is called for. Because we are - * sure the tty line exists, we only have to link it to - * a free 6pcack channel... - */ -static int sixpack_open(struct tty_struct *tty) -{ - char *xbuff = NULL; - struct net_device *dev; - struct sixpack *sp; - unsigned long len; - int err = 0; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (tty->ops->write == NULL) - return -EOPNOTSUPP; - - dev = alloc_netdev(sizeof(struct sixpack), "sp%d", NET_NAME_UNKNOWN, - sp_setup); - if (!dev) { - err = -ENOMEM; - goto out; - } - - sp = netdev_priv(dev); - sp->dev = dev; - - spin_lock_init(&sp->lock); - spin_lock_init(&sp->rxlock); - - /* !!! length of the buffers. MTU is IP MTU, not PACLEN! */ - - len = dev->mtu * 2; - - xbuff = kmalloc(len + 4, GFP_KERNEL); - if (xbuff == NULL) { - err = -ENOBUFS; - goto out_free; - } - - spin_lock_bh(&sp->lock); - - sp->tty = tty; - - sp->xbuff = xbuff; - - sp->rcount = 0; - sp->rx_count = 0; - sp->rx_count_cooked = 0; - sp->xleft = 0; - - sp->flags = 0; /* Clear ESCAPE & ERROR flags */ - - sp->duplex = 0; - sp->tx_delay = SIXP_TXDELAY; - sp->persistence = SIXP_PERSIST; - sp->slottime = SIXP_SLOTTIME; - sp->led_state = 0x60; - sp->status = 1; - sp->status1 = 1; - sp->status2 = 0; - sp->tx_enable = 0; - - netif_start_queue(dev); - - timer_setup(&sp->tx_t, sp_xmit_on_air, 0); - - timer_setup(&sp->resync_t, resync_tnc, 0); - - spin_unlock_bh(&sp->lock); - - /* Done. We have linked the TTY line to a channel. */ - tty->disc_data = sp; - tty->receive_room = 65536; - - /* Now we're ready to register. */ - err = register_netdev(dev); - if (err) - goto out_free; - - tnc_init(sp); - - return 0; - -out_free: - kfree(xbuff); - - free_netdev(dev); - -out: - return err; -} - - -/* - * Close down a 6pack channel. - * This means flushing out any pending queues, and then restoring the - * TTY line discipline to what it was before it got hooked to 6pack - * (which usually is TTY again). - */ -static void sixpack_close(struct tty_struct *tty) -{ - struct sixpack *sp; - - sp = tty->disc_data; - if (!sp) - return; - - tty->disc_data = NULL; - - /* We must stop the queue to avoid potentially scribbling - * on the free buffers. The sp->dead completion is not sufficient - * to protect us from sp->xbuff access. - */ - netif_stop_queue(sp->dev); - - unregister_netdev(sp->dev); - - timer_delete_sync(&sp->tx_t); - timer_delete_sync(&sp->resync_t); - - /* Free all 6pack frame buffers after unreg. */ - kfree(sp->xbuff); - - free_netdev(sp->dev); -} - -/* Perform I/O control on an active 6pack channel. */ -static int sixpack_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - struct sixpack *sp = tty->disc_data; - struct net_device *dev; - unsigned int tmp, err; - - if (!sp) - return -ENXIO; - dev = sp->dev; - - switch(cmd) { - case SIOCGIFNAME: - err = copy_to_user((void __user *) arg, dev->name, - strlen(dev->name) + 1) ? -EFAULT : 0; - break; - - case SIOCGIFENCAP: - err = put_user(0, (int __user *) arg); - break; - - case SIOCSIFENCAP: - if (get_user(tmp, (int __user *) arg)) { - err = -EFAULT; - break; - } - - sp->mode = tmp; - dev->addr_len = AX25_ADDR_LEN; - dev->hard_header_len = AX25_KISS_HEADER_LEN + - AX25_MAX_HEADER_LEN + 3; - dev->type = ARPHRD_AX25; - - err = 0; - break; - - case SIOCSIFHWADDR: { - char addr[AX25_ADDR_LEN]; - - if (copy_from_user(&addr, - (void __user *)arg, AX25_ADDR_LEN)) { - err = -EFAULT; - break; - } - - netif_tx_lock_bh(dev); - __dev_addr_set(dev, &addr, AX25_ADDR_LEN); - netif_tx_unlock_bh(dev); - err = 0; - break; - } - default: - err = tty_mode_ioctl(tty, cmd, arg); - } - - return err; -} - -static struct tty_ldisc_ops sp_ldisc = { - .owner = THIS_MODULE, - .num = N_6PACK, - .name = "6pack", - .open = sixpack_open, - .close = sixpack_close, - .ioctl = sixpack_ioctl, - .receive_buf = sixpack_receive_buf, - .write_wakeup = sixpack_write_wakeup, -}; - -/* Initialize 6pack control device -- register 6pack line discipline */ - -static int __init sixpack_init_driver(void) -{ - int status; - - /* Register the provided line protocol discipline */ - status = tty_register_ldisc(&sp_ldisc); - if (status) - pr_err("6pack: can't register line discipline (err = %d)\n", status); - - return status; -} - -static void __exit sixpack_exit_driver(void) -{ - tty_unregister_ldisc(&sp_ldisc); -} - -/* encode an AX.25 packet into 6pack */ - -static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, - int length, unsigned char tx_delay) -{ - int count = 0; - unsigned char checksum = 0, buf[400]; - int raw_count = 0; - - tx_buf_raw[raw_count++] = SIXP_PRIO_CMD_MASK | SIXP_TX_MASK; - tx_buf_raw[raw_count++] = SIXP_SEOF; - - buf[0] = tx_delay; - for (count = 1; count < length; count++) - buf[count] = tx_buf[count]; - - for (count = 0; count < length; count++) - checksum += buf[count]; - buf[length] = (unsigned char) 0xff - checksum; - - for (count = 0; count <= length; count++) { - if ((count % 3) == 0) { - tx_buf_raw[raw_count++] = (buf[count] & 0x3f); - tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x30); - } else if ((count % 3) == 1) { - tx_buf_raw[raw_count++] |= (buf[count] & 0x0f); - tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x3c); - } else { - tx_buf_raw[raw_count++] |= (buf[count] & 0x03); - tx_buf_raw[raw_count++] = (buf[count] >> 2); - } - } - if ((length % 3) != 2) - raw_count++; - tx_buf_raw[raw_count++] = SIXP_SEOF; - return raw_count; -} - -/* decode 4 sixpack-encoded bytes into 3 data bytes */ - -static void decode_data(struct sixpack *sp, u8 inbyte) -{ - u8 *buf; - - if (sp->rx_count != 3) { - sp->raw_buf[sp->rx_count++] = inbyte; - - return; - } - - if (sp->rx_count_cooked + 2 >= sizeof(sp->cooked_buf)) { - pr_err("6pack: cooked buffer overrun, data loss\n"); - sp->rx_count = 0; - return; - } - - buf = sp->raw_buf; - sp->cooked_buf[sp->rx_count_cooked++] = - buf[0] | ((buf[1] << 2) & 0xc0); - sp->cooked_buf[sp->rx_count_cooked++] = - (buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0); - sp->cooked_buf[sp->rx_count_cooked++] = - (buf[2] & 0x03) | (inbyte << 2); - sp->rx_count = 0; -} - -/* identify and execute a 6pack priority command byte */ - -static void decode_prio_command(struct sixpack *sp, u8 cmd) -{ - ssize_t actual; - - if ((cmd & SIXP_PRIO_DATA_MASK) != 0) { /* idle ? */ - - /* RX and DCD flags can only be set in the same prio command, - if the DCD flag has been set without the RX flag in the previous - prio command. If DCD has not been set before, something in the - transmission has gone wrong. In this case, RX and DCD are - cleared in order to prevent the decode_data routine from - reading further data that might be corrupt. */ - - if (((sp->status & SIXP_DCD_MASK) == 0) && - ((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) { - if (sp->status != 1) - printk(KERN_DEBUG "6pack: protocol violation\n"); - else - sp->status = 0; - cmd &= ~SIXP_RX_DCD_MASK; - } - sp->status = cmd & SIXP_PRIO_DATA_MASK; - } else { /* output watchdog char if idle */ - if ((sp->status2 != 0) && (sp->duplex == 1)) { - sp->led_state = 0x70; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->tx_enable = 1; - actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2); - sp->xleft -= actual; - sp->xhead += actual; - sp->led_state = 0x60; - sp->status2 = 0; - - } - } - - /* needed to trigger the TNC watchdog */ - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - - /* if the state byte has been received, the TNC is present, - so the resync timer can be reset. */ - - if (sp->tnc_state == TNC_IN_SYNC) - mod_timer(&sp->resync_t, jiffies + SIXP_INIT_RESYNC_TIMEOUT); - - sp->status1 = cmd & SIXP_PRIO_DATA_MASK; -} - -/* identify and execute a standard 6pack command byte */ - -static void decode_std_command(struct sixpack *sp, u8 cmd) -{ - u8 checksum = 0, rest = 0; - short i; - - switch (cmd & SIXP_CMD_MASK) { /* normal command */ - case SIXP_SEOF: - if ((sp->rx_count == 0) && (sp->rx_count_cooked == 0)) { - if ((sp->status & SIXP_RX_DCD_MASK) == - SIXP_RX_DCD_MASK) { - sp->led_state = 0x68; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - } - } else { - sp->led_state = 0x60; - /* fill trailing bytes with zeroes */ - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - spin_lock_bh(&sp->rxlock); - rest = sp->rx_count; - if (rest != 0) - for (i = rest; i <= 3; i++) - decode_data(sp, 0); - if (rest == 2) - sp->rx_count_cooked -= 2; - else if (rest == 3) - sp->rx_count_cooked -= 1; - for (i = 0; i < sp->rx_count_cooked; i++) - checksum += sp->cooked_buf[i]; - if (checksum != SIXP_CHKSUM) { - printk(KERN_DEBUG "6pack: bad checksum %2.2x\n", checksum); - } else { - sp->rcount = sp->rx_count_cooked-2; - sp_bump(sp, 0); - } - sp->rx_count_cooked = 0; - spin_unlock_bh(&sp->rxlock); - } - break; - case SIXP_TX_URUN: printk(KERN_DEBUG "6pack: TX underrun\n"); - break; - case SIXP_RX_ORUN: printk(KERN_DEBUG "6pack: RX overrun\n"); - break; - case SIXP_RX_BUF_OVL: - printk(KERN_DEBUG "6pack: RX buffer overflow\n"); - } -} - -/* decode a 6pack packet */ - -static void -sixpack_decode(struct sixpack *sp, const u8 *pre_rbuff, size_t count) -{ - size_t count1; - u8 inbyte; - - for (count1 = 0; count1 < count; count1++) { - inbyte = pre_rbuff[count1]; - if (inbyte == SIXP_FOUND_TNC) { - tnc_set_sync_state(sp, TNC_IN_SYNC); - timer_delete(&sp->resync_t); - } - if ((inbyte & SIXP_PRIO_CMD_MASK) != 0) - decode_prio_command(sp, inbyte); - else if ((inbyte & SIXP_STD_CMD_MASK) != 0) - decode_std_command(sp, inbyte); - else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK) { - spin_lock_bh(&sp->rxlock); - decode_data(sp, inbyte); - spin_unlock_bh(&sp->rxlock); - } - } -} - -MODULE_AUTHOR("Ralf Baechle DO1GRB "); -MODULE_DESCRIPTION("6pack driver for AX.25"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_LDISC(N_6PACK); - -module_init(sixpack_init_driver); -module_exit(sixpack_exit_driver); diff --git a/drivers/net/hamradio/Kconfig b/drivers/net/hamradio/Kconfig deleted file mode 100644 index 36a9aade9f33..000000000000 --- a/drivers/net/hamradio/Kconfig +++ /dev/null @@ -1,162 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config MKISS - tristate "Serial port KISS driver" - depends on AX25 && TTY - select CRC16 - help - KISS is a protocol used for the exchange of data between a computer - and a Terminal Node Controller (a small embedded system commonly - used for networking over AX.25 amateur radio connections; it - connects the computer's serial port with the radio's microphone - input and speaker output). - - Although KISS is less advanced than the 6pack protocol, it has - the advantage that it is already supported by most modern TNCs - without the need for a firmware upgrade. - - To compile this driver as a module, choose M here: the module - will be called mkiss. - -config 6PACK - tristate "Serial port 6PACK driver" - depends on AX25 && TTY - help - 6pack is a transmission protocol for the data exchange between your - PC and your TNC (the Terminal Node Controller acts as a kind of - modem connecting your computer's serial port to your radio's - microphone input and speaker output). This protocol can be used as - an alternative to KISS for networking over AX.25 amateur radio - connections, but it has some extended functionality. - - Note that this driver is still experimental and might cause - problems. For details about the features and the usage of the - driver, read . - - To compile this driver as a module, choose M here: the module - will be called 6pack. - -config BPQETHER - tristate "BPQ Ethernet driver" - depends on AX25 - help - AX.25 is the protocol used for computer communication over amateur - radio. If you say Y here, you will be able to send and receive AX.25 - traffic over Ethernet (also called "BPQ AX.25"), which could be - useful if some other computer on your local network has a direct - amateur radio connection. - -config SCC - tristate "Z8530 SCC driver" - depends on ISA && AX25 - help - These cards are used to connect your Linux box to an amateur radio - in order to communicate with other computers. If you want to use - this, read - - and the AX25-HOWTO, available from - . Also make sure to say Y - to "Amateur Radio AX.25 Level 2" support. - - To compile this driver as a module, choose M here: the module - will be called scc. - -config SCC_DELAY - bool "additional delay for PA0HZP OptoSCC compatible boards" - depends on SCC - help - Say Y here if you experience problems with the SCC driver not - working properly; please read - - for details. - - If unsure, say N. - -config SCC_TRXECHO - bool "support for TRX that feedback the tx signal to rx" - depends on SCC - help - Some transmitters feed the transmitted signal back to the receive - line. Say Y here to foil this by explicitly disabling the receiver - during data transmission. - - If in doubt, say Y. - -config BAYCOM_SER_FDX - tristate "BAYCOM ser12 fullduplex driver for AX.25" - depends on AX25 && HAS_IOPORT - select CRC_CCITT - help - This is one of two drivers for Baycom style simple amateur radio - modems that connect to a serial interface. The driver supports the - ser12 design in full-duplex mode. In addition, it allows the - baudrate to be set between 300 and 4800 baud (however not all modems - support all baudrates). This is the preferred driver. The next - driver, "BAYCOM ser12 half-duplex driver for AX.25" is the old - driver and still provided in case this driver does not work with - your serial interface chip. To configure the driver, use the sethdlc - utility available in the standard ax25 utilities package. - For more information on the modems, see - . - - To compile this driver as a module, choose M here: the module - will be called baycom_ser_fdx. This is recommended. - -config BAYCOM_SER_HDX - tristate "BAYCOM ser12 halfduplex driver for AX.25" - depends on AX25 && HAS_IOPORT - select CRC_CCITT - help - This is one of two drivers for Baycom style simple amateur radio - modems that connect to a serial interface. The driver supports the - ser12 design in half-duplex mode. This is the old driver. It is - still provided in case your serial interface chip does not work with - the full-duplex driver. This driver is deprecated. To configure - the driver, use the sethdlc utility available in the standard ax25 - utilities package. For more information on the modems, see - . - - To compile this driver as a module, choose M here: the module - will be called baycom_ser_hdx. This is recommended. - -config BAYCOM_PAR - tristate "BAYCOM picpar and par96 driver for AX.25" - depends on PARPORT && AX25 - select CRC_CCITT - help - This is a driver for Baycom style simple amateur radio modems that - connect to a parallel interface. The driver supports the picpar and - par96 designs. To configure the driver, use the sethdlc utility - available in the standard ax25 utilities package. - For more information on the modems, see - . - - To compile this driver as a module, choose M here: the module - will be called baycom_par. This is recommended. - -config BAYCOM_EPP - tristate "BAYCOM epp driver for AX.25" - depends on PARPORT && AX25 && !64BIT - select CRC_CCITT - help - This is a driver for Baycom style simple amateur radio modems that - connect to a parallel interface. The driver supports the EPP - designs. To configure the driver, use the sethdlc utility available - in the standard ax25 utilities package. - For more information on the modems, see - . - - To compile this driver as a module, choose M here: the module - will be called baycom_epp. This is recommended. - -config YAM - tristate "YAM driver for AX.25" - depends on AX25 && HAS_IOPORT - help - The YAM is a modem for packet radio which connects to the serial - port and includes some of the functions of a Terminal Node - Controller. If you have one of those, say Y here. - - To compile this driver as a module, choose M here: the module - will be called yam. - - diff --git a/drivers/net/hamradio/Makefile b/drivers/net/hamradio/Makefile deleted file mode 100644 index 25fc400369ba..000000000000 --- a/drivers/net/hamradio/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the Linux AX.25 and HFMODEM device drivers. -# -# -# 19971130 Moved the amateur radio related network drivers from -# drivers/net/ to drivers/hamradio for easier maintenance. -# Joerg Reuter DL1BKE -# -# 20000806 Rewritten to use lists instead of if-statements. -# Christoph Hellwig -# - -obj-$(CONFIG_SCC) += scc.o -obj-$(CONFIG_MKISS) += mkiss.o -obj-$(CONFIG_6PACK) += 6pack.o -obj-$(CONFIG_YAM) += yam.o -obj-$(CONFIG_BPQETHER) += bpqether.o -obj-$(CONFIG_BAYCOM_SER_FDX) += baycom_ser_fdx.o hdlcdrv.o -obj-$(CONFIG_BAYCOM_SER_HDX) += baycom_ser_hdx.o hdlcdrv.o -obj-$(CONFIG_BAYCOM_PAR) += baycom_par.o hdlcdrv.o -obj-$(CONFIG_BAYCOM_EPP) += baycom_epp.o hdlcdrv.o diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c deleted file mode 100644 index 5fda7a0fcce0..000000000000 --- a/drivers/net/hamradio/baycom_epp.c +++ /dev/null @@ -1,1316 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * baycom_epp.c -- baycom epp radio modem driver. - * - * Copyright (C) 1998-2000 - * Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * History: - * 0.1 xx.xx.1998 Initial version by Matthias Welwarsky (dg2fef) - * 0.2 21.04.1998 Massive rework by Thomas Sailer - * Integrated FPGA EPP modem configuration routines - * 0.3 11.05.1998 Took FPGA config out and moved it into a separate program - * 0.4 26.07.1999 Adapted to new lowlevel parport driver interface - * 0.5 03.08.1999 adapt to Linus' new __setup/__initcall - * removed some pre-2.2 kernel compatibility cruft - * 0.6 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts - * 0.7 12.02.2000 adapted to softnet driver interface - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG -#define BAYCOM_MAGIC 19730510 - -/* --------------------------------------------------------------------- */ - -static const char paranoia_str[] = KERN_ERR - "baycom_epp: bad magic number for hdlcdrv_state struct in routine %s\n"; - -static const char bc_drvname[] = "baycom_epp"; -static const char bc_drvinfo[] = KERN_INFO "baycom_epp: (C) 1998-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_epp: version 0.7\n"; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device *baycom_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -/* EPP status register */ -#define EPP_DCDBIT 0x80 -#define EPP_PTTBIT 0x08 -#define EPP_NREF 0x01 -#define EPP_NRAEF 0x02 -#define EPP_NRHF 0x04 -#define EPP_NTHF 0x20 -#define EPP_NTAEF 0x10 -#define EPP_NTEF EPP_PTTBIT - -/* EPP control register */ -#define EPP_TX_FIFO_ENABLE 0x10 -#define EPP_RX_FIFO_ENABLE 0x08 -#define EPP_MODEM_ENABLE 0x20 -#define EPP_LEDS 0xC0 -#define EPP_IRQ_ENABLE 0x10 - -/* LPT registers */ -#define LPTREG_ECONTROL 0x402 -#define LPTREG_CONFIGB 0x401 -#define LPTREG_CONFIGA 0x400 -#define LPTREG_EPPDATA 0x004 -#define LPTREG_EPPADDR 0x003 -#define LPTREG_CONTROL 0x002 -#define LPTREG_STATUS 0x001 -#define LPTREG_DATA 0x000 - -/* LPT control register */ -#define LPTCTRL_PROGRAM 0x04 /* 0 to reprogram */ -#define LPTCTRL_WRITE 0x01 -#define LPTCTRL_ADDRSTB 0x08 -#define LPTCTRL_DATASTB 0x02 -#define LPTCTRL_INTEN 0x10 - -/* LPT status register */ -#define LPTSTAT_SHIFT_NINTR 6 -#define LPTSTAT_WAIT 0x80 -#define LPTSTAT_NINTR (1<0;len--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff]; - crc ^= 0xffff; - *buffer++ = crc; - *buffer++ = crc >> 8; -} -#endif - -/*---------------------------------------------------------------------------*/ - -static inline int check_crc_ccitt(const unsigned char *buf, int cnt) -{ - return (crc_ccitt(0xffff, buf, cnt) & 0xffff) == 0xf0b8; -} - -/*---------------------------------------------------------------------------*/ - -static inline int calc_crc_ccitt(const unsigned char *buf, int cnt) -{ - return (crc_ccitt(0xffff, buf, cnt) ^ 0xffff) & 0xffff; -} - -/* ---------------------------------------------------------------------- */ - -#define tenms_to_flags(bc,tenms) ((tenms * bc->bitrate) / 800) - -/* --------------------------------------------------------------------- */ - -static inline void baycom_int_freq(struct baycom_state *bc) -{ -#ifdef BAYCOM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -#endif /* BAYCOM_DEBUG */ -} - -/* ---------------------------------------------------------------------- */ -/* - * eppconfig_path should be setable via /proc/sys. - */ - -static char const eppconfig_path[] = "/usr/sbin/eppfpga"; - -static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL }; - -/* eppconfig: called during ifconfig up to configure the modem */ -static int eppconfig(struct baycom_state *bc) -{ - char modearg[256]; - char portarg[16]; - char *argv[] = { - (char *)eppconfig_path, - "-s", - "-p", portarg, - "-m", modearg, - NULL }; - - /* set up arguments */ - sprintf(modearg, "%sclk,%smodem,fclk=%d,bps=%d,divider=%d%s,extstat", - bc->cfg.intclk ? "int" : "ext", - bc->cfg.extmodem ? "ext" : "int", bc->cfg.fclk, bc->cfg.bps, - (bc->cfg.fclk + 8 * bc->cfg.bps) / (16 * bc->cfg.bps), - bc->cfg.loopback ? ",loopback" : ""); - sprintf(portarg, "%ld", bc->pdev->port->base); - printk(KERN_DEBUG "%s: %s -s -p %s -m %s\n", bc_drvname, eppconfig_path, portarg, modearg); - - return call_usermodehelper(eppconfig_path, argv, envp, UMH_WAIT_PROC); -} - -/* ---------------------------------------------------------------------- */ - -static inline void do_kiss_params(struct baycom_state *bc, - unsigned char *data, unsigned long len) -{ - -#ifdef KISS_VERBOSE -#define PKP(a,b) printk(KERN_INFO "baycomm_epp: channel params: " a "\n", b) -#else /* KISS_VERBOSE */ -#define PKP(a,b) -#endif /* KISS_VERBOSE */ - - if (len < 2) - return; - switch(data[0]) { - case PARAM_TXDELAY: - bc->ch_params.tx_delay = data[1]; - PKP("TX delay = %ums", 10 * bc->ch_params.tx_delay); - break; - case PARAM_PERSIST: - bc->ch_params.ppersist = data[1]; - PKP("p persistence = %u", bc->ch_params.ppersist); - break; - case PARAM_SLOTTIME: - bc->ch_params.slottime = data[1]; - PKP("slot time = %ums", bc->ch_params.slottime); - break; - case PARAM_TXTAIL: - bc->ch_params.tx_tail = data[1]; - PKP("TX tail = %ums", bc->ch_params.tx_tail); - break; - case PARAM_FULLDUP: - bc->ch_params.fulldup = !!data[1]; - PKP("%s duplex", bc->ch_params.fulldup ? "full" : "half"); - break; - default: - break; - } -#undef PKP -} - -/* --------------------------------------------------------------------- */ - -static void encode_hdlc(struct baycom_state *bc) -{ - struct sk_buff *skb; - unsigned char *wp, *bp; - int pkt_len; - unsigned bitstream, notbitstream, bitbuf, numbit, crc; - unsigned char crcarr[2]; - int j; - - if (bc->hdlctx.bufcnt > 0) - return; - skb = bc->skb; - if (!skb) - return; - bc->skb = NULL; - pkt_len = skb->len-1; /* strip KISS byte */ - wp = bc->hdlctx.buf; - bp = skb->data+1; - crc = calc_crc_ccitt(bp, pkt_len); - crcarr[0] = crc; - crcarr[1] = crc >> 8; - *wp++ = 0x7e; - bitstream = bitbuf = numbit = 0; - while (pkt_len > -2) { - bitstream >>= 8; - bitstream |= ((unsigned int)*bp) << 8; - bitbuf |= ((unsigned int)*bp) << numbit; - notbitstream = ~bitstream; - bp++; - pkt_len--; - if (!pkt_len) - bp = crcarr; - for (j = 0; j < 8; j++) - if (unlikely(!(notbitstream & (0x1f0 << j)))) { - bitstream &= ~(0x100 << j); - bitbuf = (bitbuf & (((2 << j) << numbit) - 1)) | - ((bitbuf & ~(((2 << j) << numbit) - 1)) << 1); - numbit++; - notbitstream = ~bitstream; - } - numbit += 8; - while (numbit >= 8) { - *wp++ = bitbuf; - bitbuf >>= 8; - numbit -= 8; - } - } - bitbuf |= 0x7e7e << numbit; - numbit += 16; - while (numbit >= 8) { - *wp++ = bitbuf; - bitbuf >>= 8; - numbit -= 8; - } - bc->hdlctx.bufptr = bc->hdlctx.buf; - bc->hdlctx.bufcnt = wp - bc->hdlctx.buf; - dev_kfree_skb(skb); - bc->dev->stats.tx_packets++; -} - -/* ---------------------------------------------------------------------- */ - -static int transmit(struct baycom_state *bc, int cnt, unsigned char stat) -{ - struct parport *pp = bc->pdev->port; - unsigned char tmp[128]; - int i, j; - - if (bc->hdlctx.state == tx_tail && !(stat & EPP_PTTBIT)) - bc->hdlctx.state = tx_idle; - if (bc->hdlctx.state == tx_idle && bc->hdlctx.calibrate <= 0) { - if (bc->hdlctx.bufcnt <= 0) - encode_hdlc(bc); - if (bc->hdlctx.bufcnt <= 0) - return 0; - if (!bc->ch_params.fulldup) { - if (!(stat & EPP_DCDBIT)) { - bc->hdlctx.slotcnt = bc->ch_params.slottime; - return 0; - } - if ((--bc->hdlctx.slotcnt) > 0) - return 0; - bc->hdlctx.slotcnt = bc->ch_params.slottime; - if (get_random_u8() > bc->ch_params.ppersist) - return 0; - } - } - if (bc->hdlctx.state == tx_idle && bc->hdlctx.bufcnt > 0) { - bc->hdlctx.state = tx_keyup; - bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_delay); - bc->ptt_keyed++; - } - while (cnt > 0) { - switch (bc->hdlctx.state) { - case tx_keyup: - i = min_t(int, cnt, bc->hdlctx.flags); - cnt -= i; - bc->hdlctx.flags -= i; - if (bc->hdlctx.flags <= 0) - bc->hdlctx.state = tx_data; - memset(tmp, 0x7e, sizeof(tmp)); - while (i > 0) { - j = (i > sizeof(tmp)) ? sizeof(tmp) : i; - if (j != pp->ops->epp_write_data(pp, tmp, j, 0)) - return -1; - i -= j; - } - break; - - case tx_data: - if (bc->hdlctx.bufcnt <= 0) { - encode_hdlc(bc); - if (bc->hdlctx.bufcnt <= 0) { - bc->hdlctx.state = tx_tail; - bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_tail); - break; - } - } - i = min_t(int, cnt, bc->hdlctx.bufcnt); - bc->hdlctx.bufcnt -= i; - cnt -= i; - if (i != pp->ops->epp_write_data(pp, bc->hdlctx.bufptr, i, 0)) - return -1; - bc->hdlctx.bufptr += i; - break; - - case tx_tail: - encode_hdlc(bc); - if (bc->hdlctx.bufcnt > 0) { - bc->hdlctx.state = tx_data; - break; - } - i = min_t(int, cnt, bc->hdlctx.flags); - if (i) { - cnt -= i; - bc->hdlctx.flags -= i; - memset(tmp, 0x7e, sizeof(tmp)); - while (i > 0) { - j = (i > sizeof(tmp)) ? sizeof(tmp) : i; - if (j != pp->ops->epp_write_data(pp, tmp, j, 0)) - return -1; - i -= j; - } - break; - } - fallthrough; - - default: - if (bc->hdlctx.calibrate <= 0) - return 0; - i = min_t(int, cnt, bc->hdlctx.calibrate); - cnt -= i; - bc->hdlctx.calibrate -= i; - memset(tmp, 0, sizeof(tmp)); - while (i > 0) { - j = (i > sizeof(tmp)) ? sizeof(tmp) : i; - if (j != pp->ops->epp_write_data(pp, tmp, j, 0)) - return -1; - i -= j; - } - break; - } - } - return 0; -} - -/* ---------------------------------------------------------------------- */ - -static void do_rxpacket(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct sk_buff *skb; - unsigned char *cp; - unsigned pktlen; - - if (bc->hdlcrx.bufcnt < 4) - return; - if (!check_crc_ccitt(bc->hdlcrx.buf, bc->hdlcrx.bufcnt)) - return; - pktlen = bc->hdlcrx.bufcnt-2+1; /* KISS kludge */ - if (!(skb = dev_alloc_skb(pktlen))) { - printk("%s: memory squeeze, dropping packet\n", dev->name); - dev->stats.rx_dropped++; - return; - } - cp = skb_put(skb, pktlen); - *cp++ = 0; /* KISS kludge */ - memcpy(cp, bc->hdlcrx.buf, pktlen - 1); - skb->protocol = ax25_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; -} - -static int receive(struct net_device *dev, int cnt) -{ - struct baycom_state *bc = netdev_priv(dev); - struct parport *pp = bc->pdev->port; - unsigned int bitbuf, notbitstream, bitstream, numbits, state; - unsigned char tmp[128]; - unsigned char *cp; - int cnt2, ret = 0; - int j; - - numbits = bc->hdlcrx.numbits; - state = bc->hdlcrx.state; - bitstream = bc->hdlcrx.bitstream; - bitbuf = bc->hdlcrx.bitbuf; - while (cnt > 0) { - cnt2 = (cnt > sizeof(tmp)) ? sizeof(tmp) : cnt; - cnt -= cnt2; - if (cnt2 != pp->ops->epp_read_data(pp, tmp, cnt2, 0)) { - ret = -1; - break; - } - cp = tmp; - for (; cnt2 > 0; cnt2--, cp++) { - bitstream >>= 8; - bitstream |= (*cp) << 8; - bitbuf >>= 8; - bitbuf |= (*cp) << 8; - numbits += 8; - notbitstream = ~bitstream; - for (j = 0; j < 8; j++) { - - /* flag or abort */ - if (unlikely(!(notbitstream & (0x0fc << j)))) { - - /* abort received */ - if (!(notbitstream & (0x1fc << j))) - state = 0; - - /* flag received */ - else if ((bitstream & (0x1fe << j)) == (0x0fc << j)) { - if (state) - do_rxpacket(dev); - bc->hdlcrx.bufcnt = 0; - bc->hdlcrx.bufptr = bc->hdlcrx.buf; - state = 1; - numbits = 7-j; - } - } - - /* stuffed bit */ - else if (unlikely((bitstream & (0x1f8 << j)) == (0xf8 << j))) { - numbits--; - bitbuf = (bitbuf & ((~0xff) << j)) | ((bitbuf & ~((~0xff) << j)) << 1); - } - } - while (state && numbits >= 8) { - if (bc->hdlcrx.bufcnt >= TXBUFFER_SIZE) { - state = 0; - } else { - *(bc->hdlcrx.bufptr)++ = bitbuf >> (16-numbits); - bc->hdlcrx.bufcnt++; - numbits -= 8; - } - } - } - } - bc->hdlcrx.numbits = numbits; - bc->hdlcrx.state = state; - bc->hdlcrx.bitstream = bitstream; - bc->hdlcrx.bitbuf = bitbuf; - return ret; -} - -/* --------------------------------------------------------------------- */ - -#define GETTICK(x) \ -({ \ - x = (unsigned int)get_cycles(); \ -}) - -static void epp_bh(struct work_struct *work) -{ - struct net_device *dev; - struct baycom_state *bc; - struct parport *pp; - unsigned char stat; - unsigned char tmp[2]; - unsigned int time1 = 0, time2 = 0, time3 = 0; - int cnt, cnt2; - - bc = container_of(work, struct baycom_state, run_work.work); - dev = bc->dev; - if (!bc->work_running) - return; - baycom_int_freq(bc); - pp = bc->pdev->port; - /* update status */ - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - bc->stat = stat; - bc->debug_vals.last_pllcorr = stat; - GETTICK(time1); - if (bc->modem == EPP_FPGAEXTSTATUS) { - /* get input count */ - tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|1; - if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) - goto epptimeout; - if (pp->ops->epp_read_addr(pp, tmp, 2, 0) != 2) - goto epptimeout; - cnt = tmp[0] | (tmp[1] << 8); - cnt &= 0x7fff; - /* get output count */ - tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|2; - if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) - goto epptimeout; - if (pp->ops->epp_read_addr(pp, tmp, 2, 0) != 2) - goto epptimeout; - cnt2 = tmp[0] | (tmp[1] << 8); - cnt2 = 16384 - (cnt2 & 0x7fff); - /* return to normal */ - tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE; - if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) - goto epptimeout; - if (transmit(bc, cnt2, stat)) - goto epptimeout; - GETTICK(time2); - if (receive(dev, cnt)) - goto epptimeout; - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - bc->stat = stat; - } else { - /* try to tx */ - switch (stat & (EPP_NTAEF|EPP_NTHF)) { - case EPP_NTHF: - cnt = 2048 - 256; - break; - - case EPP_NTAEF: - cnt = 2048 - 1793; - break; - - case 0: - cnt = 0; - break; - - default: - cnt = 2048 - 1025; - break; - } - if (transmit(bc, cnt, stat)) - goto epptimeout; - GETTICK(time2); - /* do receiver */ - while ((stat & (EPP_NRAEF|EPP_NRHF)) != EPP_NRHF) { - switch (stat & (EPP_NRAEF|EPP_NRHF)) { - case EPP_NRAEF: - cnt = 1025; - break; - - case 0: - cnt = 1793; - break; - - default: - cnt = 256; - break; - } - if (receive(dev, cnt)) - goto epptimeout; - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - } - cnt = 0; - if (bc->bitrate < 50000) - cnt = 256; - else if (bc->bitrate < 100000) - cnt = 128; - while (cnt > 0 && stat & EPP_NREF) { - if (receive(dev, 1)) - goto epptimeout; - cnt--; - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - } - } - GETTICK(time3); -#ifdef BAYCOM_DEBUG - bc->debug_vals.mod_cycles = time2 - time1; - bc->debug_vals.demod_cycles = time3 - time2; -#endif /* BAYCOM_DEBUG */ - schedule_delayed_work(&bc->run_work, 1); - if (!bc->skb) - netif_wake_queue(dev); - return; - epptimeout: - printk(KERN_ERR "%s: EPP timeout!\n", bc_drvname); -} - -/* ---------------------------------------------------------------------- */ -/* - * ===================== network driver interface ========================= - */ - -static netdev_tx_t baycom_send_packet(struct sk_buff *skb, struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - if (skb->data[0] != 0) { - do_kiss_params(bc, skb->data, skb->len); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - if (bc->skb) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - /* strip KISS byte */ - if (skb->len >= HDLCDRV_MAXFLEN+1 || skb->len < 3) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - netif_stop_queue(dev); - bc->skb = skb; - return NETDEV_TX_OK; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *)addr; - - /* addr is an AX.25 shifted ASCII mac address */ - dev_addr_set(dev, sa->sa_data); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void epp_wakeup(void *handle) -{ - struct net_device *dev = (struct net_device *)handle; - struct baycom_state *bc = netdev_priv(dev); - - printk(KERN_DEBUG "baycom_epp: %s: why am I being woken up?\n", dev->name); - if (!parport_claim(bc->pdev)) - printk(KERN_DEBUG "baycom_epp: %s: I'm broken.\n", dev->name); -} - -/* --------------------------------------------------------------------- */ - -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ - -static int epp_open(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct parport *pp = parport_find_base(dev->base_addr); - unsigned int i, j; - unsigned char tmp[128]; - unsigned char stat; - unsigned long tstart; - struct pardev_cb par_cb; - - if (!pp) { - printk(KERN_ERR "%s: parport at 0x%lx unknown\n", bc_drvname, dev->base_addr); - return -ENXIO; - } -#if 0 - if (pp->irq < 0) { - printk(KERN_ERR "%s: parport at 0x%lx has no irq\n", bc_drvname, pp->base); - parport_put_port(pp); - return -ENXIO; - } -#endif - if ((~pp->modes) & (PARPORT_MODE_TRISTATE | PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT)) { - printk(KERN_ERR "%s: parport at 0x%lx cannot be used\n", - bc_drvname, pp->base); - parport_put_port(pp); - return -EIO; - } - memset(&bc->modem, 0, sizeof(bc->modem)); - memset(&par_cb, 0, sizeof(par_cb)); - par_cb.wakeup = epp_wakeup; - par_cb.private = (void *)dev; - par_cb.flags = PARPORT_DEV_EXCL; - for (i = 0; i < NR_PORTS; i++) - if (baycom_device[i] == dev) - break; - - if (i == NR_PORTS) { - pr_err("%s: no device found\n", bc_drvname); - parport_put_port(pp); - return -ENODEV; - } - - bc->pdev = parport_register_dev_model(pp, dev->name, &par_cb, i); - parport_put_port(pp); - if (!bc->pdev) { - printk(KERN_ERR "%s: cannot register parport at 0x%lx\n", bc_drvname, pp->base); - return -ENXIO; - } - if (parport_claim(bc->pdev)) { - printk(KERN_ERR "%s: parport at 0x%lx busy\n", bc_drvname, pp->base); - parport_unregister_device(bc->pdev); - return -EBUSY; - } - dev->irq = /*pp->irq*/ 0; - INIT_DELAYED_WORK(&bc->run_work, epp_bh); - bc->work_running = 1; - bc->modem = EPP_CONVENTIONAL; - if (eppconfig(bc)) - printk(KERN_INFO "%s: no FPGA detected, assuming conventional EPP modem\n", bc_drvname); - else - bc->modem = /*EPP_FPGA*/ EPP_FPGAEXTSTATUS; - parport_write_control(pp, LPTCTRL_PROGRAM); /* prepare EPP mode; we aren't using interrupts */ - /* reset the modem */ - tmp[0] = 0; - tmp[1] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE; - if (pp->ops->epp_write_addr(pp, tmp, 2, 0) != 2) - goto epptimeout; - /* autoprobe baud rate */ - tstart = jiffies; - i = 0; - while (time_before(jiffies, tstart + HZ/3)) { - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - if ((stat & (EPP_NRAEF|EPP_NRHF)) == EPP_NRHF) { - schedule(); - continue; - } - if (pp->ops->epp_read_data(pp, tmp, 128, 0) != 128) - goto epptimeout; - if (pp->ops->epp_read_data(pp, tmp, 128, 0) != 128) - goto epptimeout; - i += 256; - } - for (j = 0; j < 256; j++) { - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - if (!(stat & EPP_NREF)) - break; - if (pp->ops->epp_read_data(pp, tmp, 1, 0) != 1) - goto epptimeout; - i++; - } - tstart = jiffies - tstart; - bc->bitrate = i * (8 * HZ) / tstart; - j = 1; - i = bc->bitrate >> 3; - while (j < 7 && i > 150) { - j++; - i >>= 1; - } - printk(KERN_INFO "%s: autoprobed bitrate: %d int divider: %d int rate: %d\n", - bc_drvname, bc->bitrate, j, bc->bitrate >> (j+2)); - tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE/*|j*/; - if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) - goto epptimeout; - /* - * initialise hdlc variables - */ - bc->hdlcrx.state = 0; - bc->hdlcrx.numbits = 0; - bc->hdlctx.state = tx_idle; - bc->hdlctx.bufcnt = 0; - bc->hdlctx.slotcnt = bc->ch_params.slottime; - bc->hdlctx.calibrate = 0; - /* start the bottom half stuff */ - schedule_delayed_work(&bc->run_work, 1); - netif_start_queue(dev); - return 0; - - epptimeout: - printk(KERN_ERR "%s: epp timeout during bitrate probe\n", bc_drvname); - parport_write_control(pp, 0); /* reset the adapter */ - parport_release(bc->pdev); - parport_unregister_device(bc->pdev); - return -EIO; -} - -/* --------------------------------------------------------------------- */ - -static int epp_close(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct parport *pp = bc->pdev->port; - unsigned char tmp[1]; - - bc->work_running = 0; - cancel_delayed_work_sync(&bc->run_work); - bc->stat = EPP_DCDBIT; - tmp[0] = 0; - pp->ops->epp_write_addr(pp, tmp, 1, 0); - parport_write_control(pp, 0); /* reset the adapter */ - parport_release(bc->pdev); - parport_unregister_device(bc->pdev); - dev_kfree_skb(bc->skb); - bc->skb = NULL; - printk(KERN_INFO "%s: close epp at iobase 0x%lx irq %u\n", - bc_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_setmode(struct baycom_state *bc, const char *modestr) -{ - const char *cp; - - if (strstr(modestr,"intclk")) - bc->cfg.intclk = 1; - if (strstr(modestr,"extclk")) - bc->cfg.intclk = 0; - if (strstr(modestr,"intmodem")) - bc->cfg.extmodem = 0; - if (strstr(modestr,"extmodem")) - bc->cfg.extmodem = 1; - if (strstr(modestr,"loopback")) - bc->cfg.loopback = 1; - if (strstr(modestr, "noloopback")) - bc->cfg.loopback = 0; - if ((cp = strstr(modestr,"fclk="))) { - bc->cfg.fclk = simple_strtoul(cp+5, NULL, 0); - if (bc->cfg.fclk < 1000000) - bc->cfg.fclk = 1000000; - if (bc->cfg.fclk > 25000000) - bc->cfg.fclk = 25000000; - } - if ((cp = strstr(modestr,"bps="))) { - bc->cfg.bps = simple_strtoul(cp+4, NULL, 0); - if (bc->cfg.bps < 1000) - bc->cfg.bps = 1000; - if (bc->cfg.bps > 1500000) - bc->cfg.bps = 1500000; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_siocdevprivate(struct net_device *dev, struct ifreq *ifr, - void __user *data, int cmd) -{ - struct baycom_state *bc = netdev_priv(dev); - struct hdlcdrv_ioctl hi; - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (copy_from_user(&hi, data, sizeof(hi))) - return -EFAULT; - switch (hi.cmd) { - default: - return -ENOIOCTLCMD; - - case HDLCDRVCTL_GETCHANNELPAR: - hi.data.cp.tx_delay = bc->ch_params.tx_delay; - hi.data.cp.tx_tail = bc->ch_params.tx_tail; - hi.data.cp.slottime = bc->ch_params.slottime; - hi.data.cp.ppersist = bc->ch_params.ppersist; - hi.data.cp.fulldup = bc->ch_params.fulldup; - break; - - case HDLCDRVCTL_SETCHANNELPAR: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - bc->ch_params.tx_delay = hi.data.cp.tx_delay; - bc->ch_params.tx_tail = hi.data.cp.tx_tail; - bc->ch_params.slottime = hi.data.cp.slottime; - bc->ch_params.ppersist = hi.data.cp.ppersist; - bc->ch_params.fulldup = hi.data.cp.fulldup; - bc->hdlctx.slotcnt = 1; - return 0; - - case HDLCDRVCTL_GETMODEMPAR: - hi.data.mp.iobase = dev->base_addr; - hi.data.mp.irq = dev->irq; - hi.data.mp.dma = dev->dma; - hi.data.mp.dma2 = 0; - hi.data.mp.seriobase = 0; - hi.data.mp.pariobase = 0; - hi.data.mp.midiiobase = 0; - break; - - case HDLCDRVCTL_SETMODEMPAR: - if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev)) - return -EACCES; - dev->base_addr = hi.data.mp.iobase; - dev->irq = /*hi.data.mp.irq*/0; - dev->dma = /*hi.data.mp.dma*/0; - return 0; - - case HDLCDRVCTL_GETSTAT: - hi.data.cs.ptt = !!(bc->stat & EPP_PTTBIT); - hi.data.cs.dcd = !(bc->stat & EPP_DCDBIT); - hi.data.cs.ptt_keyed = bc->ptt_keyed; - hi.data.cs.tx_packets = dev->stats.tx_packets; - hi.data.cs.tx_errors = dev->stats.tx_errors; - hi.data.cs.rx_packets = dev->stats.rx_packets; - hi.data.cs.rx_errors = dev->stats.rx_errors; - break; - - case HDLCDRVCTL_OLDGETSTAT: - hi.data.ocs.ptt = !!(bc->stat & EPP_PTTBIT); - hi.data.ocs.dcd = !(bc->stat & EPP_DCDBIT); - hi.data.ocs.ptt_keyed = bc->ptt_keyed; - break; - - case HDLCDRVCTL_CALIBRATE: - if (!capable(CAP_SYS_RAWIO)) - return -EACCES; - bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8; - return 0; - - case HDLCDRVCTL_DRIVERNAME: - strscpy_pad(hi.data.drivername, "baycom_epp"); - break; - - case HDLCDRVCTL_GETMODE: - sprintf(hi.data.modename, "%sclk,%smodem,fclk=%d,bps=%d%s", - bc->cfg.intclk ? "int" : "ext", - bc->cfg.extmodem ? "ext" : "int", bc->cfg.fclk, bc->cfg.bps, - bc->cfg.loopback ? ",loopback" : ""); - break; - - case HDLCDRVCTL_SETMODE: - if (!capable(CAP_NET_ADMIN) || netif_running(dev)) - return -EACCES; - hi.data.modename[sizeof(hi.data.modename)-1] = '\0'; - return baycom_setmode(bc, hi.data.modename); - - case HDLCDRVCTL_MODELIST: - strscpy_pad(hi.data.modename, "intclk,extclk,intmodem,extmodem,divider=x"); - break; - - case HDLCDRVCTL_MODEMPARMASK: - return HDLCDRV_PARMASK_IOBASE; - - } - if (copy_to_user(data, &hi, sizeof(hi))) - return -EFAULT; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static const struct net_device_ops baycom_netdev_ops = { - .ndo_open = epp_open, - .ndo_stop = epp_close, - .ndo_siocdevprivate = baycom_siocdevprivate, - .ndo_start_xmit = baycom_send_packet, - .ndo_set_mac_address = baycom_set_mac_address, -}; - -/* - * Check for a network adaptor of this type, and return '0' if one exists. - * If dev->base_addr == 0, probe all likely locations. - * If dev->base_addr == 1, always return failure. - * If dev->base_addr == 2, allocate space for the device and return success - * (detachable devices only). - */ -static void baycom_probe(struct net_device *dev) -{ - const struct hdlcdrv_channel_params dflt_ch_params = { - 20, 2, 10, 40, 0 - }; - struct baycom_state *bc; - - /* - * not a real probe! only initialize data structures - */ - bc = netdev_priv(dev); - /* - * initialize the baycom_state struct - */ - bc->ch_params = dflt_ch_params; - bc->ptt_keyed = 0; - - /* - * initialize the device struct - */ - - /* Fill in the fields of the device structure */ - bc->skb = NULL; - - dev->netdev_ops = &baycom_netdev_ops; - dev->header_ops = &ax25_header_ops; - - dev->type = ARPHRD_AX25; /* AF_AX25 device */ - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */ - dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */ - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&null_ax25_address); - dev->tx_queue_len = 16; - - /* New style flags */ - dev->flags = 0; -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { "", }; -static int iobase[NR_PORTS] = { 0x378, }; - -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode, "baycom operating mode"); -module_param_hw_array(iobase, int, ioport, NULL, 0); -MODULE_PARM_DESC(iobase, "baycom io base address"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom epp amateur radio modem driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -static int baycom_epp_par_probe(struct pardevice *par_dev) -{ - struct device_driver *drv = par_dev->dev.driver; - int len = strlen(drv->name); - - if (strncmp(par_dev->name, drv->name, len)) - return -ENODEV; - - return 0; -} - -static struct parport_driver baycom_epp_par_driver = { - .name = "bce", - .probe = baycom_epp_par_probe, -}; - -static void __init baycom_epp_dev_setup(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - - /* - * initialize part of the baycom_state struct - */ - bc->dev = dev; - bc->magic = BAYCOM_MAGIC; - bc->cfg.fclk = 19666600; - bc->cfg.bps = 9600; - /* - * initialize part of the device struct - */ - baycom_probe(dev); -} - -static int __init init_baycomepp(void) -{ - int i, found = 0, ret; - char set_hw = 1; - - printk(bc_drvinfo); - - ret = parport_register_driver(&baycom_epp_par_driver); - if (ret) - return ret; - - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev; - - dev = alloc_netdev(sizeof(struct baycom_state), "bce%d", - NET_NAME_UNKNOWN, baycom_epp_dev_setup); - - if (!dev) { - printk(KERN_WARNING "bce%d : out of memory\n", i); - return found ? 0 : -ENOMEM; - } - - sprintf(dev->name, "bce%d", i); - dev->base_addr = iobase[i]; - - if (!mode[i]) - set_hw = 0; - if (!set_hw) - iobase[i] = 0; - - if (register_netdev(dev)) { - printk(KERN_WARNING "%s: cannot register net device %s\n", bc_drvname, dev->name); - free_netdev(dev); - break; - } - if (set_hw && baycom_setmode(netdev_priv(dev), mode[i])) - set_hw = 0; - baycom_device[i] = dev; - found++; - } - - if (found == 0) { - parport_unregister_driver(&baycom_epp_par_driver); - return -ENXIO; - } - - return 0; -} - -static void __exit cleanup_baycomepp(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = baycom_device[i]; - - if (dev) { - struct baycom_state *bc = netdev_priv(dev); - if (bc->magic == BAYCOM_MAGIC) { - unregister_netdev(dev); - free_netdev(dev); - } else - printk(paranoia_str, "cleanup_module"); - } - } - parport_unregister_driver(&baycom_epp_par_driver); -} - -module_init(init_baycomepp); -module_exit(cleanup_baycomepp); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: baycom_epp=io,mode - * mode: fpga config options - */ - -static int __init baycom_epp_setup(char *str) -{ - static unsigned __initdata nr_dev = 0; - int ints[2]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 2, ints); - if (ints[0] < 1) - return 0; - mode[nr_dev] = str; - iobase[nr_dev] = ints[1]; - nr_dev++; - return 1; -} - -__setup("baycom_epp=", baycom_epp_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c deleted file mode 100644 index f03797103c6a..000000000000 --- a/drivers/net/hamradio/baycom_par.c +++ /dev/null @@ -1,598 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * baycom_par.c -- baycom par96 and picpar radio modem driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * Supported modems - * - * par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. - * The modem does all the filtering and regenerates the receiver clock. - * Data is transferred from and to the PC via a shift register. - * The shift register is filled with 16 bits and an interrupt is - * signalled. The PC then empties the shift register in a burst. This - * modem connects to the parallel port, hence the name. The modem - * leaves the implementation of the HDLC protocol and the scrambler - * polynomial to the PC. This modem is no longer available (at least - * from Baycom) and has been replaced by the PICPAR modem (see below). - * You may however still build one from the schematics published in - * cq-DL :-). - * - * picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The - * modem is protocol compatible to par96, but uses only three low - * power ICs and can therefore be fed from the parallel port and - * does not require an additional power supply. It features - * built in DCD circuitry. The driver should therefore be configured - * for hardware DCD. - * - * Command line options (insmod command line) - * - * mode driver mode string. Valid choices are par96 and picpar. - * iobase base address of the port; common values are 0x378, 0x278, 0x3bc - * - * History: - * 0.1 26.06.1996 Adapted from baycom.c and made network driver interface - * 18.10.1996 Changed to new user space access routines (copy_{to,from}_user) - * 0.3 26.04.1997 init code/data tagged - * 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints) - * 0.5 11.11.1997 split into separate files for ser12/par96 - * 0.6 03.08.1999 adapt to Linus' new __setup/__initcall - * removed some pre-2.2 kernel compatibility cruft - * 0.7 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts - * 0.8 12.02.2000 adapted to softnet driver interface - * removed direct parport access, uses parport driver methods - * 0.9 03.07.2000 fix interface name handling - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG - -/* - * modem options; bit mask - */ -#define BAYCOM_OPTIONS_SOFTDCD 1 - -/* --------------------------------------------------------------------- */ - -static const char bc_drvname[] = "baycom_par"; -static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_par: version 0.9\n"; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device *baycom_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -#define PAR96_BURSTBITS 16 -#define PAR96_BURST 4 -#define PAR96_PTT 2 -#define PAR96_TXBIT 1 -#define PAR96_ACK 0x40 -#define PAR96_RXBIT 0x20 -#define PAR96_DCD 0x10 -#define PAR97_POWER 0xf8 - -/* ---------------------------------------------------------------------- */ -/* - * Information that need to be kept for each board. - */ - -struct baycom_state { - struct hdlcdrv_state hdrv; - - struct pardevice *pdev; - unsigned int options; - - struct modem_state { - short arb_divider; - unsigned char flags; - unsigned int shreg; - struct modem_state_par96 { - int dcd_count; - unsigned int dcd_shreg; - unsigned long descram; - unsigned long scram; - } par96; - } modem; - -#ifdef BAYCOM_DEBUG - struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - int cur_pllcorr; - int last_pllcorr; - } debug_vals; -#endif /* BAYCOM_DEBUG */ -}; - -/* --------------------------------------------------------------------- */ - -static inline void baycom_int_freq(struct baycom_state *bc) -{ -#ifdef BAYCOM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -#endif /* BAYCOM_DEBUG */ -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== PAR96 specific routines ========================= - */ - -#define PAR96_DESCRAM_TAP1 0x20000 -#define PAR96_DESCRAM_TAP2 0x01000 -#define PAR96_DESCRAM_TAP3 0x00001 - -#define PAR96_DESCRAM_TAPSH1 17 -#define PAR96_DESCRAM_TAPSH2 12 -#define PAR96_DESCRAM_TAPSH3 0 - -#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */ -#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */ - -/* --------------------------------------------------------------------- */ - -static inline void par96_tx(struct net_device *dev, struct baycom_state *bc) -{ - int i; - unsigned int data = hdlcdrv_getbits(&bc->hdrv); - struct parport *pp = bc->pdev->port; - - for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) { - unsigned char val = PAR97_POWER; - bc->modem.par96.scram = ((bc->modem.par96.scram << 1) | - (bc->modem.par96.scram & 1)); - if (!(data & 1)) - bc->modem.par96.scram ^= 1; - if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1)) - bc->modem.par96.scram ^= - (PAR96_SCRAM_TAPN << 1); - if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2)) - val |= PAR96_TXBIT; - pp->ops->write_data(pp, val); - pp->ops->write_data(pp, val | PAR96_BURST); - } -} - -/* --------------------------------------------------------------------- */ - -static inline void par96_rx(struct net_device *dev, struct baycom_state *bc) -{ - int i; - unsigned int data, mask, mask2, descx; - struct parport *pp = bc->pdev->port; - - /* - * do receiver; differential decode and descramble on the fly - */ - for(data = i = 0; i < PAR96_BURSTBITS; i++) { - bc->modem.par96.descram = (bc->modem.par96.descram << 1); - if (pp->ops->read_status(pp) & PAR96_RXBIT) - bc->modem.par96.descram |= 1; - descx = bc->modem.par96.descram ^ - (bc->modem.par96.descram >> 1); - /* now the diff decoded data is inverted in descram */ - pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT); - descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^ - (descx >> PAR96_DESCRAM_TAPSH2)); - data >>= 1; - if (!(descx & 1)) - data |= 0x8000; - pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT | PAR96_BURST); - } - hdlcdrv_putbits(&bc->hdrv, data); - /* - * do DCD algorithm - */ - if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { - bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16) - | (data << 16); - /* search for flags and set the dcd counter appropriately */ - for(mask = 0x1fe00, mask2 = 0xfc00, i = 0; - i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) - if ((bc->modem.par96.dcd_shreg & mask) == mask2) - bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4; - /* check for abort/noise sequences */ - for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0; - i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) - if (((bc->modem.par96.dcd_shreg & mask) == mask2) && - (bc->modem.par96.dcd_count >= 0)) - bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10; - /* decrement and set the dcd variable */ - if (bc->modem.par96.dcd_count >= 0) - bc->modem.par96.dcd_count -= 2; - hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0); - } else { - hdlcdrv_setdcd(&bc->hdrv, !!(pp->ops->read_status(pp) & PAR96_DCD)); - } -} - -/* --------------------------------------------------------------------- */ - -static void par96_interrupt(void *dev_id) -{ - struct net_device *dev = dev_id; - struct baycom_state *bc = netdev_priv(dev); - - baycom_int_freq(bc); - /* - * check if transmitter active - */ - if (hdlcdrv_ptt(&bc->hdrv)) - par96_tx(dev, bc); - else { - par96_rx(dev, bc); - if (--bc->modem.arb_divider <= 0) { - bc->modem.arb_divider = 6; - local_irq_enable(); - hdlcdrv_arbitrate(dev, &bc->hdrv); - } - } - local_irq_enable(); - hdlcdrv_transmitter(dev, &bc->hdrv); - hdlcdrv_receiver(dev, &bc->hdrv); - local_irq_disable(); -} - -/* --------------------------------------------------------------------- */ - -static void par96_wakeup(void *handle) -{ - struct net_device *dev = (struct net_device *)handle; - struct baycom_state *bc = netdev_priv(dev); - - printk(KERN_DEBUG "baycom_par: %s: why am I being woken up?\n", dev->name); - if (!parport_claim(bc->pdev)) - printk(KERN_DEBUG "baycom_par: %s: I'm broken.\n", dev->name); -} - -/* --------------------------------------------------------------------- */ - -static int par96_open(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct pardev_cb par_cb; - struct parport *pp; - int i; - - if (!dev || !bc) - return -ENXIO; - pp = parport_find_base(dev->base_addr); - if (!pp) { - printk(KERN_ERR "baycom_par: parport at 0x%lx unknown\n", dev->base_addr); - return -ENXIO; - } - if (pp->irq < 0) { - printk(KERN_ERR "baycom_par: parport at 0x%lx has no irq\n", pp->base); - parport_put_port(pp); - return -ENXIO; - } - if ((~pp->modes) & (PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT)) { - printk(KERN_ERR "baycom_par: parport at 0x%lx cannot be used\n", pp->base); - parport_put_port(pp); - return -ENXIO; - } - memset(&bc->modem, 0, sizeof(bc->modem)); - bc->hdrv.par.bitrate = 9600; - memset(&par_cb, 0, sizeof(par_cb)); - par_cb.wakeup = par96_wakeup; - par_cb.irq_func = par96_interrupt; - par_cb.private = (void *)dev; - par_cb.flags = PARPORT_DEV_EXCL; - for (i = 0; i < NR_PORTS; i++) - if (baycom_device[i] == dev) - break; - - if (i == NR_PORTS) { - pr_err("%s: no device found\n", bc_drvname); - parport_put_port(pp); - return -ENODEV; - } - bc->pdev = parport_register_dev_model(pp, dev->name, &par_cb, i); - parport_put_port(pp); - if (!bc->pdev) { - printk(KERN_ERR "baycom_par: cannot register parport at 0x%lx\n", dev->base_addr); - return -ENXIO; - } - if (parport_claim(bc->pdev)) { - printk(KERN_ERR "baycom_par: parport at 0x%lx busy\n", pp->base); - parport_unregister_device(bc->pdev); - return -EBUSY; - } - pp = bc->pdev->port; - dev->irq = pp->irq; - pp->ops->data_forward(pp); - bc->hdrv.par.bitrate = 9600; - pp->ops->write_data(pp, PAR96_PTT | PAR97_POWER); /* switch off PTT */ - pp->ops->enable_irq(pp); - printk(KERN_INFO "%s: par96 at iobase 0x%lx irq %u options 0x%x\n", - bc_drvname, dev->base_addr, dev->irq, bc->options); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int par96_close(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct parport *pp; - - if (!dev || !bc) - return -EINVAL; - pp = bc->pdev->port; - /* disable interrupt */ - pp->ops->disable_irq(pp); - /* switch off PTT */ - pp->ops->write_data(pp, PAR96_PTT | PAR97_POWER); - parport_release(bc->pdev); - parport_unregister_device(bc->pdev); - printk(KERN_INFO "%s: close par96 at iobase 0x%lx irq %u\n", - bc_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== hdlcdrv driver interface ========================= - */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd); - -/* --------------------------------------------------------------------- */ - -static const struct hdlcdrv_ops par96_ops = { - .drvname = bc_drvname, - .drvinfo = bc_drvinfo, - .open = par96_open, - .close = par96_close, - .ioctl = baycom_ioctl -}; - -/* --------------------------------------------------------------------- */ - -static int baycom_setmode(struct baycom_state *bc, const char *modestr) -{ - if (!strncmp(modestr, "picpar", 6)) - bc->options = 0; - else if (!strncmp(modestr, "par96", 5)) - bc->options = BAYCOM_OPTIONS_SOFTDCD; - else - bc->options = !!strchr(modestr, '*'); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct baycom_state *bc; - struct baycom_ioctl bi; - - if (!dev) - return -EINVAL; - - bc = netdev_priv(dev); - BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC); - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - switch (hi->cmd) { - default: - break; - - case HDLCDRVCTL_GETMODE: - strscpy(hi->data.modename, bc->options ? "par96" : "picpar"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !capable(CAP_NET_ADMIN)) - return -EACCES; - hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; - return baycom_setmode(bc, hi->data.modename); - - case HDLCDRVCTL_MODELIST: - strscpy(hi->data.modename, "par96,picpar"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_MODEMPARMASK: - return HDLCDRV_PARMASK_IOBASE; - - } - - if (copy_from_user(&bi, data, sizeof(bi))) - return -EFAULT; - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - -#ifdef BAYCOM_DEBUG - case BAYCOMCTL_GETDEBUG: - bi.data.dbg.debug1 = bc->hdrv.ptt_keyed; - bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; - bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; - break; -#endif /* BAYCOM_DEBUG */ - - } - if (copy_to_user(data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { "picpar", }; -static int iobase[NR_PORTS] = { 0x378, }; - -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode, "baycom operating mode; eg. par96 or picpar"); -module_param_hw_array(iobase, int, ioport, NULL, 0); -MODULE_PARM_DESC(iobase, "baycom io base address"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom par96 and picpar amateur radio modem driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -static int baycom_par_probe(struct pardevice *par_dev) -{ - struct device_driver *drv = par_dev->dev.driver; - int len = strlen(drv->name); - - if (strncmp(par_dev->name, drv->name, len)) - return -ENODEV; - - return 0; -} - -static struct parport_driver baycom_par_driver = { - .name = "bcp", - .probe = baycom_par_probe, -}; - -static int __init init_baycompar(void) -{ - int i, found = 0, ret; - char set_hw = 1; - - printk(bc_drvinfo); - - ret = parport_register_driver(&baycom_par_driver); - if (ret) - return ret; - - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev; - struct baycom_state *bc; - char ifname[IFNAMSIZ]; - - sprintf(ifname, "bcp%d", i); - - if (!mode[i]) - set_hw = 0; - if (!set_hw) - iobase[i] = 0; - - dev = hdlcdrv_register(&par96_ops, - sizeof(struct baycom_state), - ifname, iobase[i], 0, 0); - if (IS_ERR(dev)) - break; - - bc = netdev_priv(dev); - if (set_hw && baycom_setmode(bc, mode[i])) - set_hw = 0; - found++; - baycom_device[i] = dev; - } - - if (!found) { - parport_unregister_driver(&baycom_par_driver); - return -ENXIO; - } - return 0; -} - -static void __exit cleanup_baycompar(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = baycom_device[i]; - - if (dev) - hdlcdrv_unregister(dev); - } - parport_unregister_driver(&baycom_par_driver); -} - -module_init(init_baycompar); -module_exit(cleanup_baycompar); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: baycom_par=io,mode - * mode: par96,picpar - */ - -static int __init baycom_par_setup(char *str) -{ - static unsigned nr_dev; - int ints[2]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 2, ints); - if (ints[0] < 1) - return 0; - mode[nr_dev] = str; - iobase[nr_dev] = ints[1]; - nr_dev++; - return 1; -} - -__setup("baycom_par=", baycom_par_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c deleted file mode 100644 index ee5bd3c12040..000000000000 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ /dev/null @@ -1,678 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * baycom_ser_fdx.c -- baycom ser12 fullduplex radio modem driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * Supported modems - * - * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only - * of a modulator/demodulator chip, usually a TI TCM3105. The computer - * is responsible for regenerating the receiver bit clock, as well as - * for handling the HDLC protocol. The modem connects to a serial port, - * hence the name. Since the serial port is not used as an async serial - * port, the kernel driver for serial ports cannot be used, and this - * driver only supports standard serial hardware (8250, 16450, 16550A) - * - * This modem usually draws its supply current out of the otherwise unused - * TXD pin of the serial port. Thus a contiguous stream of 0x00-bytes - * is transmitted to achieve a positive supply voltage. - * - * hsk: This is a 4800 baud FSK modem, designed for TNC use. It works fine - * in 'baycom-mode' :-) In contrast to the TCM3105 modem, power is - * externally supplied. So there's no need to provide the 0x00-byte-stream - * when receiving or idle, which drastically reduces interrupt load. - * - * Command line options (insmod command line) - * - * mode ser# hardware DCD - * ser#* software DCD - * ser#+ hardware DCD, inverted signal at DCD pin - * '#' denotes the baud rate / 100, eg. ser12* is '1200 baud, soft DCD' - * iobase base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8 - * baud baud rate (between 300 and 4800) - * irq interrupt line of the port; common values are 4,3 - * - * History: - * 0.1 26.06.1996 Adapted from baycom.c and made network driver interface - * 18.10.1996 Changed to new user space access routines (copy_{to,from}_user) - * 0.3 26.04.1997 init code/data tagged - * 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints) - * 0.5 11.11.1997 ser12/par96 split into separate files - * 0.6 24.01.1998 Thorsten Kranzkowski, dl8bcu and Thomas Sailer: - * reduced interrupt load in transmit case - * reworked receiver - * 0.7 03.08.1999 adapt to Linus' new __setup/__initcall - * 0.8 10.08.1999 use module_init/module_exit - * 0.9 12.02.2000 adapted to softnet driver interface - * 0.10 03.07.2000 fix interface name handling - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG - -/* --------------------------------------------------------------------- */ - -static const char bc_drvname[] = "baycom_ser_fdx"; -static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_ser_fdx: version 0.10\n"; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device *baycom_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -#define RBR(iobase) (iobase+0) -#define THR(iobase) (iobase+0) -#define IER(iobase) (iobase+1) -#define IIR(iobase) (iobase+2) -#define FCR(iobase) (iobase+2) -#define LCR(iobase) (iobase+3) -#define MCR(iobase) (iobase+4) -#define LSR(iobase) (iobase+5) -#define MSR(iobase) (iobase+6) -#define SCR(iobase) (iobase+7) -#define DLL(iobase) (iobase+0) -#define DLM(iobase) (iobase+1) - -#define SER12_EXTENT 8 - -/* ---------------------------------------------------------------------- */ -/* - * Information that need to be kept for each board. - */ - -struct baycom_state { - struct hdlcdrv_state hdrv; - - unsigned int baud, baud_us, baud_arbdiv, baud_uartdiv, baud_dcdtimeout; - int opt_dcd; - - struct modem_state { - unsigned char flags; - unsigned char ptt; - unsigned int shreg; - struct modem_state_ser12 { - unsigned char tx_bit; - unsigned char last_rxbit; - int dcd_sum0, dcd_sum1, dcd_sum2; - int dcd_time; - unsigned int pll_time; - unsigned int txshreg; - } ser12; - } modem; - -#ifdef BAYCOM_DEBUG - struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - int cur_pllcorr; - int last_pllcorr; - } debug_vals; -#endif /* BAYCOM_DEBUG */ -}; - -/* --------------------------------------------------------------------- */ - -static inline void baycom_int_freq(struct baycom_state *bc) -{ -#ifdef BAYCOM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -#endif /* BAYCOM_DEBUG */ -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== SER12 specific routines ========================= - */ - -/* --------------------------------------------------------------------- */ - -static inline void ser12_set_divisor(struct net_device *dev, - unsigned int divisor) -{ - outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */ - outb(divisor, DLL(dev->base_addr)); - outb(divisor >> 8, DLM(dev->base_addr)); - outb(0x01, LCR(dev->base_addr)); /* word length = 6 */ - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(dev->base_addr)); - /* - * it is important not to set the divider while transmitting; - * this reportedly makes some UARTs generating interrupts - * in the hundredthousands per second region - * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno) - */ -} - -static __inline__ void ser12_rx(struct net_device *dev, struct baycom_state *bc, struct timespec64 *ts, unsigned char curs) -{ - int timediff; - int bdus8 = bc->baud_us >> 3; - int bdus4 = bc->baud_us >> 2; - int bdus2 = bc->baud_us >> 1; - - timediff = 1000000 + ts->tv_nsec / NSEC_PER_USEC - - bc->modem.ser12.pll_time; - while (timediff >= 500000) - timediff -= 1000000; - while (timediff >= bdus2) { - timediff -= bc->baud_us; - bc->modem.ser12.pll_time += bc->baud_us; - bc->modem.ser12.dcd_time--; - /* first check if there is room to add a bit */ - if (bc->modem.shreg & 1) { - hdlcdrv_putbits(&bc->hdrv, (bc->modem.shreg >> 1) ^ 0xffff); - bc->modem.shreg = 0x10000; - } - /* add a one bit */ - bc->modem.shreg >>= 1; - } - if (bc->modem.ser12.dcd_time <= 0) { - if (!bc->opt_dcd) - hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + - bc->modem.ser12.dcd_sum1 + - bc->modem.ser12.dcd_sum2) < 0); - bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; - bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; - bc->modem.ser12.dcd_sum0 = 2; /* slight bias */ - bc->modem.ser12.dcd_time += 120; - } - if (bc->modem.ser12.last_rxbit != curs) { - bc->modem.ser12.last_rxbit = curs; - bc->modem.shreg |= 0x10000; - /* adjust the PLL */ - if (timediff > 0) - bc->modem.ser12.pll_time += bdus8; - else - bc->modem.ser12.pll_time += 1000000 - bdus8; - /* update DCD */ - if (abs(timediff) > bdus4) - bc->modem.ser12.dcd_sum0 += 4; - else - bc->modem.ser12.dcd_sum0--; -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr = timediff; -#endif /* BAYCOM_DEBUG */ - } - while (bc->modem.ser12.pll_time >= 1000000) - bc->modem.ser12.pll_time -= 1000000; -} - -/* --------------------------------------------------------------------- */ - -static irqreturn_t ser12_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct baycom_state *bc = netdev_priv(dev); - struct timespec64 ts; - unsigned char iir, msr; - unsigned int txcount = 0; - - if (!bc || bc->hdrv.magic != HDLCDRV_MAGIC) - return IRQ_NONE; - /* fast way out for shared irq */ - if ((iir = inb(IIR(dev->base_addr))) & 1) - return IRQ_NONE; - /* get current time */ - ktime_get_ts64(&ts); - msr = inb(MSR(dev->base_addr)); - /* delta DCD */ - if ((msr & 8) && bc->opt_dcd) - hdlcdrv_setdcd(&bc->hdrv, !((msr ^ bc->opt_dcd) & 0x80)); - do { - switch (iir & 6) { - case 6: - inb(LSR(dev->base_addr)); - break; - - case 4: - inb(RBR(dev->base_addr)); - break; - - case 2: - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(dev->base_addr)); - baycom_int_freq(bc); - txcount++; - /* - * first output the last bit (!) then call HDLC transmitter, - * since this may take quite long - */ - if (bc->modem.ptt) - outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr)); - else - outb(0x0d, MCR(dev->base_addr)); /* transmitter off */ - break; - - default: - msr = inb(MSR(dev->base_addr)); - /* delta DCD */ - if ((msr & 8) && bc->opt_dcd) - hdlcdrv_setdcd(&bc->hdrv, !((msr ^ bc->opt_dcd) & 0x80)); - break; - } - iir = inb(IIR(dev->base_addr)); - } while (!(iir & 1)); - ser12_rx(dev, bc, &ts, msr & 0x10); /* CTS */ - if (bc->modem.ptt && txcount) { - if (bc->modem.ser12.txshreg <= 1) { - bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv); - if (!hdlcdrv_ptt(&bc->hdrv)) { - ser12_set_divisor(dev, 115200/100/8); - bc->modem.ptt = 0; - goto end_transmit; - } - } - bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1)); - bc->modem.ser12.txshreg >>= 1; - } - end_transmit: - local_irq_enable(); - if (!bc->modem.ptt && txcount) { - hdlcdrv_arbitrate(dev, &bc->hdrv); - if (hdlcdrv_ptt(&bc->hdrv)) { - ser12_set_divisor(dev, bc->baud_uartdiv); - bc->modem.ser12.txshreg = 1; - bc->modem.ptt = 1; - } - } - hdlcdrv_transmitter(dev, &bc->hdrv); - hdlcdrv_receiver(dev, &bc->hdrv); - local_irq_disable(); - return IRQ_HANDLED; -} - -/* --------------------------------------------------------------------- */ - -enum uart { c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A}; -static const char *uart_str[] = { - "unknown", "8250", "16450", "16550", "16550A" -}; - -static enum uart ser12_check_uart(unsigned int iobase) -{ - unsigned char b1,b2,b3; - enum uart u; - enum uart uart_tab[] = - { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; - - b1 = inb(MCR(iobase)); - outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ - b2 = inb(MSR(iobase)); - outb(0x1a, MCR(iobase)); - b3 = inb(MSR(iobase)) & 0xf0; - outb(b1, MCR(iobase)); /* restore old values */ - outb(b2, MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(RBR(iobase)); - inb(RBR(iobase)); - outb(0x01, FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, SCR(iobase)); - b1 = inb(SCR(iobase)); - outb(0xa5, SCR(iobase)); - b2 = inb(SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_open(struct net_device *dev) -{ - const unsigned int nr_irqs = irq_get_nr_irqs(); - struct baycom_state *bc = netdev_priv(dev); - enum uart u; - - if (!dev || !bc) - return -ENXIO; - if (!dev->base_addr || dev->base_addr > 0xffff-SER12_EXTENT || - dev->irq < 2 || dev->irq > nr_irqs) { - printk(KERN_INFO "baycom_ser_fdx: invalid portnumber (max %u) " - "or irq (2 <= irq <= %d)\n", - 0xffff-SER12_EXTENT, nr_irqs); - return -ENXIO; - } - if (bc->baud < 300 || bc->baud > 4800) { - printk(KERN_INFO "baycom_ser_fdx: invalid baudrate " - "(300...4800)\n"); - return -EINVAL; - } - if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx")) { - printk(KERN_WARNING "BAYCOM_SER_FSX: I/O port 0x%04lx busy\n", - dev->base_addr); - return -EACCES; - } - memset(&bc->modem, 0, sizeof(bc->modem)); - bc->hdrv.par.bitrate = bc->baud; - bc->baud_us = 1000000/bc->baud; - bc->baud_uartdiv = (115200/8)/bc->baud; - if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown){ - release_region(dev->base_addr, SER12_EXTENT); - return -EIO; - } - outb(0, FCR(dev->base_addr)); /* disable FIFOs */ - outb(0x0d, MCR(dev->base_addr)); - outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED, - "baycom_ser_fdx", dev)) { - release_region(dev->base_addr, SER12_EXTENT); - return -EBUSY; - } - /* - * set the SIO to 6 Bits/character; during receive, - * the baud rate is set to produce 100 ints/sec - * to feed the channel arbitration process, - * during transmit to baud ints/sec to run - * the transmitter - */ - ser12_set_divisor(dev, 115200/100/8); - /* - * enable transmitter empty interrupt and modem status interrupt - */ - outb(0x0a, IER(dev->base_addr)); - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(dev->base_addr)); - hdlcdrv_setdcd(&bc->hdrv, 0); - printk(KERN_INFO "%s: ser_fdx at iobase 0x%lx irq %u baud %u uart %s\n", - bc_drvname, dev->base_addr, dev->irq, bc->baud, uart_str[u]); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_close(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - - if (!dev || !bc) - return -EINVAL; - /* - * disable interrupts - */ - outb(0, IER(dev->base_addr)); - outb(1, MCR(dev->base_addr)); - free_irq(dev->irq, dev); - release_region(dev->base_addr, SER12_EXTENT); - printk(KERN_INFO "%s: close ser_fdx at iobase 0x%lx irq %u\n", - bc_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== hdlcdrv driver interface ========================= - */ - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd); - -/* --------------------------------------------------------------------- */ - -static const struct hdlcdrv_ops ser12_ops = { - .drvname = bc_drvname, - .drvinfo = bc_drvinfo, - .open = ser12_open, - .close = ser12_close, - .ioctl = baycom_ioctl, -}; - -/* --------------------------------------------------------------------- */ - -static int baycom_setmode(struct baycom_state *bc, const char *modestr) -{ - unsigned int baud; - - if (!strncmp(modestr, "ser", 3)) { - baud = simple_strtoul(modestr+3, NULL, 10); - if (baud >= 3 && baud <= 48) - bc->baud = baud*100; - } - if (strchr(modestr, '*')) - bc->opt_dcd = 0; - else if (strchr(modestr, '+')) - bc->opt_dcd = -1; - else - bc->opt_dcd = 1; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct baycom_state *bc; - struct baycom_ioctl bi; - - if (!dev) - return -EINVAL; - - bc = netdev_priv(dev); - BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC); - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - switch (hi->cmd) { - default: - break; - - case HDLCDRVCTL_GETMODE: - sprintf(hi->data.modename, "ser%u", bc->baud / 100); - if (bc->opt_dcd <= 0) - strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : "+"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !capable(CAP_NET_ADMIN)) - return -EACCES; - hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; - return baycom_setmode(bc, hi->data.modename); - - case HDLCDRVCTL_MODELIST: - strscpy(hi->data.modename, "ser12,ser3,ser24"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_MODEMPARMASK: - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ; - - } - - if (copy_from_user(&bi, data, sizeof(bi))) - return -EFAULT; - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - -#ifdef BAYCOM_DEBUG - case BAYCOMCTL_GETDEBUG: - bi.data.dbg.debug1 = bc->hdrv.ptt_keyed; - bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; - bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; - break; -#endif /* BAYCOM_DEBUG */ - - } - if (copy_to_user(data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { "ser12*", }; -static int iobase[NR_PORTS] = { 0x3f8, }; -static int irq[NR_PORTS] = { 4, }; -static int baud[NR_PORTS] = { [0 ... NR_PORTS-1] = 1200 }; - -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD"); -module_param_hw_array(iobase, int, ioport, NULL, 0); -MODULE_PARM_DESC(iobase, "baycom io base address"); -module_param_hw_array(irq, int, irq, NULL, 0); -MODULE_PARM_DESC(irq, "baycom irq number"); -module_param_array(baud, int, NULL, 0); -MODULE_PARM_DESC(baud, "baycom baud rate (300 to 4800)"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom ser12 full duplex amateur radio modem driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -static int __init init_baycomserfdx(void) -{ - int i, found = 0; - char set_hw = 1; - - printk(bc_drvinfo); - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev; - struct baycom_state *bc; - char ifname[IFNAMSIZ]; - - sprintf(ifname, "bcsf%d", i); - - if (!mode[i]) - set_hw = 0; - if (!set_hw) - iobase[i] = irq[i] = 0; - - dev = hdlcdrv_register(&ser12_ops, - sizeof(struct baycom_state), - ifname, iobase[i], irq[i], 0); - if (IS_ERR(dev)) - break; - - bc = netdev_priv(dev); - if (set_hw && baycom_setmode(bc, mode[i])) - set_hw = 0; - bc->baud = baud[i]; - found++; - baycom_device[i] = dev; - } - - if (!found) - return -ENXIO; - return 0; -} - -static void __exit cleanup_baycomserfdx(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = baycom_device[i]; - if (dev) - hdlcdrv_unregister(dev); - } -} - -module_init(init_baycomserfdx); -module_exit(cleanup_baycomserfdx); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: baycom_ser_fdx=io,irq,mode - * mode: ser# hardware DCD - * ser#* software DCD - * ser#+ hardware DCD, inverted signal at DCD pin - * '#' denotes the baud rate / 100, eg. ser12* is '1200 baud, soft DCD' - */ - -static int __init baycom_ser_fdx_setup(char *str) -{ - static unsigned nr_dev; - int ints[4]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 4, ints); - if (ints[0] < 2) - return 0; - mode[nr_dev] = str; - iobase[nr_dev] = ints[1]; - irq[nr_dev] = ints[2]; - if (ints[0] >= 3) - baud[nr_dev] = ints[3]; - nr_dev++; - return 1; -} - -__setup("baycom_ser_fdx=", baycom_ser_fdx_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c deleted file mode 100644 index 05bdad214799..000000000000 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ /dev/null @@ -1,727 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * baycom_ser_hdx.c -- baycom ser12 halfduplex radio modem driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * Supported modems - * - * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only - * of a modulator/demodulator chip, usually a TI TCM3105. The computer - * is responsible for regenerating the receiver bit clock, as well as - * for handling the HDLC protocol. The modem connects to a serial port, - * hence the name. Since the serial port is not used as an async serial - * port, the kernel driver for serial ports cannot be used, and this - * driver only supports standard serial hardware (8250, 16450, 16550A) - * - * Command line options (insmod command line) - * - * mode ser12 hardware DCD - * ser12* software DCD - * ser12@ hardware/software DCD, i.e. no explicit DCD signal but hardware - * mutes audio input to the modem - * ser12+ hardware DCD, inverted signal at DCD pin - * iobase base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8 - * irq interrupt line of the port; common values are 4,3 - * - * History: - * 0.1 26.06.1996 Adapted from baycom.c and made network driver interface - * 18.10.1996 Changed to new user space access routines (copy_{to,from}_user) - * 0.3 26.04.1997 init code/data tagged - * 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints) - * 0.5 11.11.1997 ser12/par96 split into separate files - * 0.6 14.04.1998 cleanups - * 0.7 03.08.1999 adapt to Linus' new __setup/__initcall - * 0.8 10.08.1999 use module_init/module_exit - * 0.9 12.02.2000 adapted to softnet driver interface - * 0.10 03.07.2000 fix interface name handling - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG - -/* --------------------------------------------------------------------- */ - -static const char bc_drvname[] = "baycom_ser_hdx"; -static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_ser_hdx: version 0.10\n"; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device *baycom_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -#define RBR(iobase) (iobase+0) -#define THR(iobase) (iobase+0) -#define IER(iobase) (iobase+1) -#define IIR(iobase) (iobase+2) -#define FCR(iobase) (iobase+2) -#define LCR(iobase) (iobase+3) -#define MCR(iobase) (iobase+4) -#define LSR(iobase) (iobase+5) -#define MSR(iobase) (iobase+6) -#define SCR(iobase) (iobase+7) -#define DLL(iobase) (iobase+0) -#define DLM(iobase) (iobase+1) - -#define SER12_EXTENT 8 - -/* ---------------------------------------------------------------------- */ -/* - * Information that need to be kept for each board. - */ - -struct baycom_state { - struct hdlcdrv_state hdrv; - - int opt_dcd; - - struct modem_state { - short arb_divider; - unsigned char flags; - unsigned int shreg; - struct modem_state_ser12 { - unsigned char tx_bit; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned char last_sample; - unsigned char last_rxbit; - unsigned int dcd_shreg; - unsigned int dcd_time; - unsigned int bit_pll; - unsigned char interm_sample; - } ser12; - } modem; - -#ifdef BAYCOM_DEBUG - struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - int cur_pllcorr; - int last_pllcorr; - } debug_vals; -#endif /* BAYCOM_DEBUG */ -}; - -/* --------------------------------------------------------------------- */ - -static inline void baycom_int_freq(struct baycom_state *bc) -{ -#ifdef BAYCOM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -#endif /* BAYCOM_DEBUG */ -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== SER12 specific routines ========================= - */ - -static inline void ser12_set_divisor(struct net_device *dev, - unsigned char divisor) -{ - outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */ - outb(divisor, DLL(dev->base_addr)); - outb(0, DLM(dev->base_addr)); - outb(0x01, LCR(dev->base_addr)); /* word length = 6 */ - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(dev->base_addr)); - /* - * it is important not to set the divider while transmitting; - * this reportedly makes some UARTs generating interrupts - * in the hundredthousands per second region - * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno) - */ -} - -/* --------------------------------------------------------------------- */ - -/* - * must call the TX arbitrator every 10ms - */ -#define SER12_ARB_DIVIDER(bc) (bc->opt_dcd ? 24 : 36) - -#define SER12_DCD_INTERVAL(bc) (bc->opt_dcd ? 12 : 240) - -static inline void ser12_tx(struct net_device *dev, struct baycom_state *bc) -{ - /* one interrupt per channel bit */ - ser12_set_divisor(dev, 12); - /* - * first output the last bit (!) then call HDLC transmitter, - * since this may take quite long - */ - outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr)); - if (bc->modem.shreg <= 1) - bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv); - bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ - (bc->modem.shreg & 1)); - bc->modem.shreg >>= 1; -} - -/* --------------------------------------------------------------------- */ - -static inline void ser12_rx(struct net_device *dev, struct baycom_state *bc) -{ - unsigned char cur_s; - /* - * do demodulator - */ - cur_s = inb(MSR(dev->base_addr)) & 0x10; /* the CTS line */ - hdlcdrv_channelbit(&bc->hdrv, cur_s); - bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) | - (cur_s != bc->modem.ser12.last_sample); - bc->modem.ser12.last_sample = cur_s; - if(bc->modem.ser12.dcd_shreg & 1) { - if (!bc->opt_dcd) { - unsigned int dcdspos, dcdsneg; - - dcdspos = dcdsneg = 0; - dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1); - if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe)) - dcdspos += 2; - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1); - - bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg; - } else - bc->modem.ser12.dcd_sum0--; - } - if(!bc->modem.ser12.dcd_time) { - hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + - bc->modem.ser12.dcd_sum1 + - bc->modem.ser12.dcd_sum2) < 0); - bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; - bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; - /* offset to ensure DCD off on silent input */ - bc->modem.ser12.dcd_sum0 = 2; - bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc); - } - bc->modem.ser12.dcd_time--; - if (!bc->opt_dcd) { - /* - * PLL code for the improved software DCD algorithm - */ - if (bc->modem.ser12.interm_sample) { - /* - * intermediate sample; set timing correction to normal - */ - ser12_set_divisor(dev, 4); - } else { - /* - * do PLL correction and call HDLC receiver - */ - switch (bc->modem.ser12.dcd_shreg & 7) { - case 1: /* transition too late */ - ser12_set_divisor(dev, 5); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr++; -#endif /* BAYCOM_DEBUG */ - break; - case 4: /* transition too early */ - ser12_set_divisor(dev, 3); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr--; -#endif /* BAYCOM_DEBUG */ - break; - default: - ser12_set_divisor(dev, 4); - break; - } - bc->modem.shreg >>= 1; - if (bc->modem.ser12.last_sample == - bc->modem.ser12.last_rxbit) - bc->modem.shreg |= 0x10000; - bc->modem.ser12.last_rxbit = - bc->modem.ser12.last_sample; - } - if (++bc->modem.ser12.interm_sample >= 3) - bc->modem.ser12.interm_sample = 0; - /* - * DCD stuff - */ - if (bc->modem.ser12.dcd_shreg & 1) { - unsigned int dcdspos, dcdsneg; - - dcdspos = dcdsneg = 0; - dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1); - dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe)) - << 1; - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1); - - bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg; - } - } else { - /* - * PLL algorithm for the hardware squelch DCD algorithm - */ - if (bc->modem.ser12.interm_sample) { - /* - * intermediate sample; set timing correction to normal - */ - ser12_set_divisor(dev, 6); - } else { - /* - * do PLL correction and call HDLC receiver - */ - switch (bc->modem.ser12.dcd_shreg & 3) { - case 1: /* transition too late */ - ser12_set_divisor(dev, 7); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr++; -#endif /* BAYCOM_DEBUG */ - break; - case 2: /* transition too early */ - ser12_set_divisor(dev, 5); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr--; -#endif /* BAYCOM_DEBUG */ - break; - default: - ser12_set_divisor(dev, 6); - break; - } - bc->modem.shreg >>= 1; - if (bc->modem.ser12.last_sample == - bc->modem.ser12.last_rxbit) - bc->modem.shreg |= 0x10000; - bc->modem.ser12.last_rxbit = - bc->modem.ser12.last_sample; - } - bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample; - /* - * DCD stuff - */ - bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1); - } - outb(0x0d, MCR(dev->base_addr)); /* transmitter off */ - if (bc->modem.shreg & 1) { - hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1); - bc->modem.shreg = 0x10000; - } - if(!bc->modem.ser12.dcd_time) { - if (bc->opt_dcd & 1) - hdlcdrv_setdcd(&bc->hdrv, !((inb(MSR(dev->base_addr)) ^ bc->opt_dcd) & 0x80)); - else - hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + - bc->modem.ser12.dcd_sum1 + - bc->modem.ser12.dcd_sum2) < 0); - bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; - bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; - /* offset to ensure DCD off on silent input */ - bc->modem.ser12.dcd_sum0 = 2; - bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc); - } - bc->modem.ser12.dcd_time--; -} - -/* --------------------------------------------------------------------- */ - -static irqreturn_t ser12_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct baycom_state *bc = netdev_priv(dev); - unsigned char iir; - - if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) - return IRQ_NONE; - /* fast way out */ - if ((iir = inb(IIR(dev->base_addr))) & 1) - return IRQ_NONE; - baycom_int_freq(bc); - do { - switch (iir & 6) { - case 6: - inb(LSR(dev->base_addr)); - break; - - case 4: - inb(RBR(dev->base_addr)); - break; - - case 2: - /* - * check if transmitter active - */ - if (hdlcdrv_ptt(&bc->hdrv)) - ser12_tx(dev, bc); - else { - ser12_rx(dev, bc); - bc->modem.arb_divider--; - } - outb(0x00, THR(dev->base_addr)); - break; - - default: - inb(MSR(dev->base_addr)); - break; - } - iir = inb(IIR(dev->base_addr)); - } while (!(iir & 1)); - if (bc->modem.arb_divider <= 0) { - bc->modem.arb_divider = SER12_ARB_DIVIDER(bc); - local_irq_enable(); - hdlcdrv_arbitrate(dev, &bc->hdrv); - } - local_irq_enable(); - hdlcdrv_transmitter(dev, &bc->hdrv); - hdlcdrv_receiver(dev, &bc->hdrv); - local_irq_disable(); - return IRQ_HANDLED; -} - -/* --------------------------------------------------------------------- */ - -enum uart { c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A}; -static const char *uart_str[] = { - "unknown", "8250", "16450", "16550", "16550A" -}; - -static enum uart ser12_check_uart(unsigned int iobase) -{ - unsigned char b1,b2,b3; - enum uart u; - enum uart uart_tab[] = - { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; - - b1 = inb(MCR(iobase)); - outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ - b2 = inb(MSR(iobase)); - outb(0x1a, MCR(iobase)); - b3 = inb(MSR(iobase)) & 0xf0; - outb(b1, MCR(iobase)); /* restore old values */ - outb(b2, MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(RBR(iobase)); - inb(RBR(iobase)); - outb(0x01, FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, SCR(iobase)); - b1 = inb(SCR(iobase)); - outb(0xa5, SCR(iobase)); - b2 = inb(SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_open(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - enum uart u; - - if (!dev || !bc) - return -ENXIO; - if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT || - dev->irq < 2 || dev->irq > 15) - return -ENXIO; - if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12")) - return -EACCES; - memset(&bc->modem, 0, sizeof(bc->modem)); - bc->hdrv.par.bitrate = 1200; - if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown) { - release_region(dev->base_addr, SER12_EXTENT); - return -EIO; - } - outb(0, FCR(dev->base_addr)); /* disable FIFOs */ - outb(0x0d, MCR(dev->base_addr)); - outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED, - "baycom_ser12", dev)) { - release_region(dev->base_addr, SER12_EXTENT); - return -EBUSY; - } - /* - * enable transmitter empty interrupt - */ - outb(2, IER(dev->base_addr)); - /* - * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that - * we get exactly (hopefully) 2 or 3 interrupts per radio symbol, - * depending on the usage of the software DCD routine - */ - ser12_set_divisor(dev, bc->opt_dcd ? 6 : 4); - printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u uart %s\n", - bc_drvname, dev->base_addr, dev->irq, uart_str[u]); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_close(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - - if (!dev || !bc) - return -EINVAL; - /* - * disable interrupts - */ - outb(0, IER(dev->base_addr)); - outb(1, MCR(dev->base_addr)); - free_irq(dev->irq, dev); - release_region(dev->base_addr, SER12_EXTENT); - printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n", - bc_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== hdlcdrv driver interface ========================= - */ - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd); - -/* --------------------------------------------------------------------- */ - -static const struct hdlcdrv_ops ser12_ops = { - .drvname = bc_drvname, - .drvinfo = bc_drvinfo, - .open = ser12_open, - .close = ser12_close, - .ioctl = baycom_ioctl, -}; - -/* --------------------------------------------------------------------- */ - -static int baycom_setmode(struct baycom_state *bc, const char *modestr) -{ - if (strchr(modestr, '*')) - bc->opt_dcd = 0; - else if (strchr(modestr, '+')) - bc->opt_dcd = -1; - else if (strchr(modestr, '@')) - bc->opt_dcd = -2; - else - bc->opt_dcd = 1; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct baycom_state *bc; - struct baycom_ioctl bi; - - if (!dev) - return -EINVAL; - - bc = netdev_priv(dev); - BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC); - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - switch (hi->cmd) { - default: - break; - - case HDLCDRVCTL_GETMODE: - strscpy(hi->data.modename, "ser12"); - if (bc->opt_dcd <= 0) - strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : (bc->opt_dcd == -2) ? "@" : "+"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !capable(CAP_NET_ADMIN)) - return -EACCES; - hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; - return baycom_setmode(bc, hi->data.modename); - - case HDLCDRVCTL_MODELIST: - strscpy(hi->data.modename, "ser12"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_MODEMPARMASK: - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ; - - } - - if (copy_from_user(&bi, data, sizeof(bi))) - return -EFAULT; - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - -#ifdef BAYCOM_DEBUG - case BAYCOMCTL_GETDEBUG: - bi.data.dbg.debug1 = bc->hdrv.ptt_keyed; - bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; - bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; - break; -#endif /* BAYCOM_DEBUG */ - - } - if (copy_to_user(data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { "ser12*", }; -static int iobase[NR_PORTS] = { 0x3f8, }; -static int irq[NR_PORTS] = { 4, }; - -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD"); -module_param_hw_array(iobase, int, ioport, NULL, 0); -MODULE_PARM_DESC(iobase, "baycom io base address"); -module_param_hw_array(irq, int, irq, NULL, 0); -MODULE_PARM_DESC(irq, "baycom irq number"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -static int __init init_baycomserhdx(void) -{ - int i, found = 0; - char set_hw = 1; - - printk(bc_drvinfo); - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev; - struct baycom_state *bc; - char ifname[IFNAMSIZ]; - - sprintf(ifname, "bcsh%d", i); - - if (!mode[i]) - set_hw = 0; - if (!set_hw) - iobase[i] = irq[i] = 0; - - dev = hdlcdrv_register(&ser12_ops, - sizeof(struct baycom_state), - ifname, iobase[i], irq[i], 0); - if (IS_ERR(dev)) - break; - - bc = netdev_priv(dev); - if (set_hw && baycom_setmode(bc, mode[i])) - set_hw = 0; - found++; - baycom_device[i] = dev; - } - - if (!found) - return -ENXIO; - return 0; -} - -static void __exit cleanup_baycomserhdx(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = baycom_device[i]; - - if (dev) - hdlcdrv_unregister(dev); - } -} - -module_init(init_baycomserhdx); -module_exit(cleanup_baycomserhdx); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: baycom_ser_hdx=io,irq,mode - * mode: ser12 hardware DCD - * ser12* software DCD - * ser12@ hardware/software DCD, i.e. no explicit DCD signal but hardware - * mutes audio input to the modem - * ser12+ hardware DCD, inverted signal at DCD pin - */ - -static int __init baycom_ser_hdx_setup(char *str) -{ - static unsigned nr_dev; - int ints[3]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 3, ints); - if (ints[0] < 2) - return 0; - mode[nr_dev] = str; - iobase[nr_dev] = ints[1]; - irq[nr_dev] = ints[2]; - nr_dev++; - return 1; -} - -__setup("baycom_ser_hdx=", baycom_ser_hdx_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c deleted file mode 100644 index 214fd1f819a1..000000000000 --- a/drivers/net/hamradio/bpqether.c +++ /dev/null @@ -1,593 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * G8BPQ compatible "AX.25 via ethernet" driver release 004 - * - * This code REQUIRES 2.0.0 or higher/ NET3.029 - * - * This is a "pseudo" network driver to allow AX.25 over Ethernet - * using G8BPQ encapsulation. It has been extracted from the protocol - * implementation because - * - * - things got unreadable within the protocol stack - * - to cure the protocol stack from "feature-ism" - * - a protocol implementation shouldn't need to know on - * which hardware it is running - * - user-level programs like the AX.25 utilities shouldn't - * need to know about the hardware. - * - IP over ethernet encapsulated AX.25 was impossible - * - rxecho.c did not work - * - to have room for extensions - * - it just deserves to "live" as an own driver - * - * This driver can use any ethernet destination address, and can be - * limited to accept frames from one dedicated ethernet card only. - * - * Note that the driver sets up the BPQ devices automagically on - * startup or (if started before the "insmod" of an ethernet device) - * on "ifconfig up". It hopefully will remove the BPQ on "rmmod"ing - * the ethernet device (in fact: as soon as another ethernet or bpq - * device gets "ifconfig"ured). - * - * I have heard that several people are thinking of experiments - * with highspeed packet radio using existing ethernet cards. - * Well, this driver is prepared for this purpose, just add - * your tx key control and a txdelay / tailtime algorithm, - * probably some buffering, and /voila/... - * - * History - * BPQ 001 Joerg(DL1BKE) Extracted BPQ code from AX.25 - * protocol stack and added my own - * yet existing patches - * BPQ 002 Joerg(DL1BKE) Scan network device list on - * startup. - * BPQ 003 Joerg(DL1BKE) Ethernet destination address - * and accepted source address - * can be configured by an ioctl() - * call. - * Fixed to match Linux networking - * changes - 2.1.15. - * BPQ 004 Joerg(DL1BKE) Fixed to not lock up on ifconfig. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -static const char banner[] __initconst = KERN_INFO \ - "AX.25: bpqether driver version 004\n"; - -static int bpq_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); -static int bpq_device_event(struct notifier_block *, unsigned long, void *); - -static struct packet_type bpq_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_BPQ), - .func = bpq_rcv, -}; - -static struct notifier_block bpq_dev_notifier = { - .notifier_call = bpq_device_event, -}; - - -struct bpqdev { - struct list_head bpq_list; /* list of bpq devices chain */ - struct net_device *ethdev; /* link to ethernet device */ - struct net_device *axdev; /* bpq device (bpq#) */ - char dest_addr[6]; /* ether destination address */ - char acpt_addr[6]; /* accept ether frames from this address only */ -}; - -static LIST_HEAD(bpq_devices); - -/* ------------------------------------------------------------------------ */ - - -/* - * Get the ethernet device for a BPQ device - */ -static inline struct net_device *bpq_get_ether_dev(struct net_device *dev) -{ - struct bpqdev *bpq = netdev_priv(dev); - - return bpq ? bpq->ethdev : NULL; -} - -/* - * Get the BPQ device for the ethernet device - */ -static inline struct net_device *bpq_get_ax25_dev(struct net_device *dev) -{ - struct bpqdev *bpq; - - list_for_each_entry_rcu(bpq, &bpq_devices, bpq_list, - lockdep_rtnl_is_held()) { - if (bpq->ethdev == dev) - return bpq->axdev; - } - return NULL; -} - -static inline int dev_is_ethdev(struct net_device *dev) -{ - return dev->type == ARPHRD_ETHER && !netdev_need_ops_lock(dev); -} - -/* ------------------------------------------------------------------------ */ - - -/* - * Receive an AX.25 frame via an ethernet interface. - */ -static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev) -{ - int len; - char * ptr; - struct ethhdr *eth; - struct bpqdev *bpq; - - if (!net_eq(dev_net(dev), &init_net)) - goto drop; - - if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) - return NET_RX_DROP; - - if (!pskb_may_pull(skb, sizeof(struct ethhdr))) - goto drop; - - rcu_read_lock(); - dev = bpq_get_ax25_dev(dev); - - if (dev == NULL || !netif_running(dev)) - goto drop_unlock; - - /* - * if we want to accept frames from just one ethernet device - * we check the source address of the sender. - */ - - bpq = netdev_priv(dev); - - eth = eth_hdr(skb); - - if (!(bpq->acpt_addr[0] & 0x01) && - !ether_addr_equal(eth->h_source, bpq->acpt_addr)) - goto drop_unlock; - - if (skb_cow(skb, sizeof(struct ethhdr))) - goto drop_unlock; - - len = skb->data[0] + skb->data[1] * 256 - 5; - - if (len < 0 || len > skb->len - 2) - goto drop_unlock; - - skb_pull(skb, 2); /* Remove the length bytes */ - skb_trim(skb, len); /* Set the length of the data */ - - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; - - ptr = skb_push(skb, 1); - *ptr = 0; - - skb->protocol = ax25_type_trans(skb, dev); - netif_rx(skb); -unlock: - - rcu_read_unlock(); - - return 0; -drop_unlock: - kfree_skb(skb); - goto unlock; - -drop: - kfree_skb(skb); - return 0; -} - -/* - * Send an AX.25 frame via an ethernet interface - */ -static netdev_tx_t bpq_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned char *ptr; - struct bpqdev *bpq; - struct net_device *orig_dev; - int size; - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - /* - * Just to be *really* sure not to send anything if the interface - * is down, the ethernet device may have gone. - */ - if (!netif_running(dev)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } - - skb_pull(skb, 1); /* Drop KISS byte */ - size = skb->len; - - /* - * We're about to mess with the skb which may still shared with the - * generic networking code so unshare and ensure it's got enough - * space for the BPQ headers. - */ - if (skb_cow(skb, AX25_BPQ_HEADER_LEN)) { - if (net_ratelimit()) - pr_err("bpqether: out of memory\n"); - kfree_skb(skb); - - return NETDEV_TX_OK; - } - - ptr = skb_push(skb, 2); /* Make space for length */ - - *ptr++ = (size + 5) % 256; - *ptr++ = (size + 5) / 256; - - bpq = netdev_priv(dev); - - orig_dev = dev; - if ((dev = bpq_get_ether_dev(dev)) == NULL) { - orig_dev->stats.tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; - } - - skb->protocol = ax25_type_trans(skb, dev); - skb_reset_network_header(skb); - dev_hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); - dev->stats.tx_packets++; - dev->stats.tx_bytes+=skb->len; - - dev_queue_xmit(skb); - netif_wake_queue(dev); - return NETDEV_TX_OK; -} - -/* - * Set AX.25 callsign - */ -static int bpq_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *)addr; - - dev_addr_set(dev, sa->sa_data); - - return 0; -} - -/* Ioctl commands - * - * SIOCSBPQETHOPT reserved for enhancements - * SIOCSBPQETHADDR set the destination and accepted - * source ethernet address (broadcast - * or multicast: accept all) - */ -static int bpq_siocdevprivate(struct net_device *dev, struct ifreq *ifr, - void __user *data, int cmd) -{ - struct bpq_ethaddr __user *ethaddr = data; - struct bpqdev *bpq = netdev_priv(dev); - struct bpq_req req; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch (cmd) { - case SIOCSBPQETHOPT: - if (copy_from_user(&req, data, sizeof(struct bpq_req))) - return -EFAULT; - switch (req.cmd) { - case SIOCGBPQETHPARAM: - case SIOCSBPQETHPARAM: - default: - return -EINVAL; - } - - break; - - case SIOCSBPQETHADDR: - if (copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN)) - return -EFAULT; - if (copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN)) - return -EFAULT; - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* - * open/close a device - */ -static int bpq_open(struct net_device *dev) -{ - netif_start_queue(dev); - return 0; -} - -static int bpq_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - - -/* ------------------------------------------------------------------------ */ - -#ifdef CONFIG_PROC_FS -/* - * Proc filesystem - */ -static void *bpq_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(RCU) -{ - int i = 1; - struct bpqdev *bpqdev; - - rcu_read_lock(); - - if (*pos == 0) - return SEQ_START_TOKEN; - - list_for_each_entry_rcu(bpqdev, &bpq_devices, bpq_list) { - if (i == *pos) - return bpqdev; - } - return NULL; -} - -static void *bpq_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct list_head *p; - struct bpqdev *bpqdev = v; - - ++*pos; - - if (v == SEQ_START_TOKEN) - p = rcu_dereference(list_next_rcu(&bpq_devices)); - else - p = rcu_dereference(list_next_rcu(&bpqdev->bpq_list)); - - return (p == &bpq_devices) ? NULL - : list_entry(p, struct bpqdev, bpq_list); -} - -static void bpq_seq_stop(struct seq_file *seq, void *v) - __releases(RCU) -{ - rcu_read_unlock(); -} - - -static int bpq_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "dev ether destination accept from\n"); - else { - const struct bpqdev *bpqdev = v; - - seq_printf(seq, "%-5s %-10s %pM ", - bpqdev->axdev->name, bpqdev->ethdev->name, - bpqdev->dest_addr); - - if (is_multicast_ether_addr(bpqdev->acpt_addr)) - seq_printf(seq, "*\n"); - else - seq_printf(seq, "%pM\n", bpqdev->acpt_addr); - - } - return 0; -} - -static const struct seq_operations bpq_seqops = { - .start = bpq_seq_start, - .next = bpq_seq_next, - .stop = bpq_seq_stop, - .show = bpq_seq_show, -}; -#endif -/* ------------------------------------------------------------------------ */ - -static const struct net_device_ops bpq_netdev_ops = { - .ndo_open = bpq_open, - .ndo_stop = bpq_close, - .ndo_start_xmit = bpq_xmit, - .ndo_set_mac_address = bpq_set_mac_address, - .ndo_siocdevprivate = bpq_siocdevprivate, -}; - -static void bpq_setup(struct net_device *dev) -{ - netdev_lockdep_set_classes(dev); - - dev->netdev_ops = &bpq_netdev_ops; - dev->needs_free_netdev = true; - - dev->flags = 0; - dev->lltx = true; /* Allow recursion */ - -#if IS_ENABLED(CONFIG_AX25) - dev->header_ops = &ax25_header_ops; -#endif - - dev->type = ARPHRD_AX25; - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; - dev->addr_len = AX25_ADDR_LEN; - - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); -} - -/* - * Setup a new device. - */ -static int bpq_new_device(struct net_device *edev) -{ - int err; - struct net_device *ndev; - struct bpqdev *bpq; - - ndev = alloc_netdev(sizeof(struct bpqdev), "bpq%d", NET_NAME_UNKNOWN, - bpq_setup); - if (!ndev) - return -ENOMEM; - - - bpq = netdev_priv(ndev); - dev_hold(edev); - bpq->ethdev = edev; - bpq->axdev = ndev; - - eth_broadcast_addr(bpq->dest_addr); - eth_broadcast_addr(bpq->acpt_addr); - - err = register_netdevice(ndev); - if (err) - goto error; - - /* List protected by RTNL */ - list_add_rcu(&bpq->bpq_list, &bpq_devices); - return 0; - - error: - dev_put(edev); - free_netdev(ndev); - return err; - -} - -static void bpq_free_device(struct net_device *ndev) -{ - struct bpqdev *bpq = netdev_priv(ndev); - - dev_put(bpq->ethdev); - list_del_rcu(&bpq->bpq_list); - - unregister_netdevice(ndev); -} - -/* - * Handle device status changes. - */ -static int bpq_device_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (!dev_is_ethdev(dev) && !bpq_get_ax25_dev(dev)) - return NOTIFY_DONE; - - switch (event) { - case NETDEV_UP: /* new ethernet device -> new BPQ interface */ - if (bpq_get_ax25_dev(dev) == NULL) - bpq_new_device(dev); - break; - - case NETDEV_DOWN: /* ethernet device closed -> close BPQ interface */ - if ((dev = bpq_get_ax25_dev(dev)) != NULL) - dev_close(dev); - break; - - case NETDEV_UNREGISTER: /* ethernet device removed -> free BPQ interface */ - if ((dev = bpq_get_ax25_dev(dev)) != NULL) - bpq_free_device(dev); - break; - default: - break; - } - - return NOTIFY_DONE; -} - - -/* ------------------------------------------------------------------------ */ - -/* - * Initialize driver. To be called from af_ax25 if not compiled as a - * module - */ -static int __init bpq_init_driver(void) -{ -#ifdef CONFIG_PROC_FS - if (!proc_create_seq("bpqether", 0444, init_net.proc_net, &bpq_seqops)) { - printk(KERN_ERR - "bpq: cannot create /proc/net/bpqether entry.\n"); - return -ENOENT; - } -#endif /* CONFIG_PROC_FS */ - - dev_add_pack(&bpq_packet_type); - - register_netdevice_notifier(&bpq_dev_notifier); - - printk(banner); - - return 0; -} - -static void __exit bpq_cleanup_driver(void) -{ - struct bpqdev *bpq; - - dev_remove_pack(&bpq_packet_type); - - unregister_netdevice_notifier(&bpq_dev_notifier); - - remove_proc_entry("bpqether", init_net.proc_net); - - rtnl_lock(); - while (!list_empty(&bpq_devices)) { - bpq = list_entry(bpq_devices.next, struct bpqdev, bpq_list); - bpq_free_device(bpq->axdev); - } - rtnl_unlock(); -} - -MODULE_AUTHOR("Joerg Reuter DL1BKE "); -MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet"); -MODULE_LICENSE("GPL"); -module_init(bpq_init_driver); -module_exit(bpq_cleanup_driver); diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c deleted file mode 100644 index 3b88e465d08f..000000000000 --- a/drivers/net/hamradio/hdlcdrv.c +++ /dev/null @@ -1,747 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * hdlcdrv.c -- HDLC packet radio network driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * The driver was derived from Donald Beckers skeleton.c - * Written 1993-94 by Donald Becker. - * - * History: - * 0.1 21.09.1996 Started - * 18.10.1996 Changed to new user space access routines - * (copy_{to,from}_user) - * 0.2 21.11.1996 various small changes - * 0.3 03.03.1997 fixed (hopefully) IP not working with ax.25 as a module - * 0.4 16.04.1997 init code/data tagged - * 0.5 30.07.1997 made HDLC buffers bigger (solves a problem with the - * soundmodem driver) - * 0.6 05.04.1998 add spinlocks - * 0.7 03.08.1999 removed some old compatibility cruft - * 0.8 12.02.2000 adapted to softnet driver interface - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -/* --------------------------------------------------------------------- */ - -#define KISS_VERBOSE - -/* --------------------------------------------------------------------- */ - -#define PARAM_TXDELAY 1 -#define PARAM_PERSIST 2 -#define PARAM_SLOTTIME 3 -#define PARAM_TXTAIL 4 -#define PARAM_FULLDUP 5 -#define PARAM_HARDWARE 6 -#define PARAM_RETURN 255 - -/* --------------------------------------------------------------------- */ -/* - * the CRC routines are stolen from WAMPES - * by Dieter Deyke - */ - - -/*---------------------------------------------------------------------------*/ - -static inline void append_crc_ccitt(unsigned char *buffer, int len) -{ - unsigned int crc = crc_ccitt(0xffff, buffer, len) ^ 0xffff; - buffer += len; - *buffer++ = crc; - *buffer++ = crc >> 8; -} - -/*---------------------------------------------------------------------------*/ - -static inline int check_crc_ccitt(const unsigned char *buf, int cnt) -{ - return (crc_ccitt(0xffff, buf, cnt) & 0xffff) == 0xf0b8; -} - -/*---------------------------------------------------------------------------*/ - -#if 0 -static int calc_crc_ccitt(const unsigned char *buf, int cnt) -{ - unsigned int crc = 0xffff; - - for (; cnt > 0; cnt--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff]; - crc ^= 0xffff; - return crc & 0xffff; -} -#endif - -/* ---------------------------------------------------------------------- */ - -#define tenms_to_2flags(s,tenms) ((tenms * s->par.bitrate) / 100 / 16) - -/* ---------------------------------------------------------------------- */ -/* - * The HDLC routines - */ - -static int hdlc_rx_add_bytes(struct hdlcdrv_state *s, unsigned int bits, - int num) -{ - int added = 0; - - while (s->hdlcrx.rx_state && num >= 8) { - if (s->hdlcrx.len >= sizeof(s->hdlcrx.buffer)) { - s->hdlcrx.rx_state = 0; - return 0; - } - *s->hdlcrx.bp++ = bits >> (32-num); - s->hdlcrx.len++; - num -= 8; - added += 8; - } - return added; -} - -static void hdlc_rx_flag(struct net_device *dev, struct hdlcdrv_state *s) -{ - struct sk_buff *skb; - int pkt_len; - unsigned char *cp; - - if (s->hdlcrx.len < 4) - return; - if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len)) - return; - pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */ - if (!(skb = dev_alloc_skb(pkt_len))) { - printk("%s: memory squeeze, dropping packet\n", dev->name); - dev->stats.rx_dropped++; - return; - } - cp = skb_put(skb, pkt_len); - *cp++ = 0; /* KISS kludge */ - memcpy(cp, s->hdlcrx.buffer, pkt_len - 1); - skb->protocol = ax25_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; -} - -void hdlcdrv_receiver(struct net_device *dev, struct hdlcdrv_state *s) -{ - int i; - unsigned int mask1, mask2, mask3, mask4, mask5, mask6, word; - - if (!s || s->magic != HDLCDRV_MAGIC) - return; - if (test_and_set_bit(0, &s->hdlcrx.in_hdlc_rx)) - return; - - while (!hdlcdrv_hbuf_empty(&s->hdlcrx.hbuf)) { - word = hdlcdrv_hbuf_get(&s->hdlcrx.hbuf); - -#ifdef HDLCDRV_DEBUG - hdlcdrv_add_bitbuffer_word(&s->bitbuf_hdlc, word); -#endif /* HDLCDRV_DEBUG */ - s->hdlcrx.bitstream >>= 16; - s->hdlcrx.bitstream |= word << 16; - s->hdlcrx.bitbuf >>= 16; - s->hdlcrx.bitbuf |= word << 16; - s->hdlcrx.numbits += 16; - for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00, - mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; - i >= 0; - i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, - mask5 <<= 1, mask6 = (mask6 << 1) | 1) { - if ((s->hdlcrx.bitstream & mask1) == mask1) - s->hdlcrx.rx_state = 0; /* abort received */ - else if ((s->hdlcrx.bitstream & mask2) == mask3) { - /* flag received */ - if (s->hdlcrx.rx_state) { - hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf - << (8+i), - s->hdlcrx.numbits - -8-i); - hdlc_rx_flag(dev, s); - } - s->hdlcrx.len = 0; - s->hdlcrx.bp = s->hdlcrx.buffer; - s->hdlcrx.rx_state = 1; - s->hdlcrx.numbits = i; - } else if ((s->hdlcrx.bitstream & mask4) == mask5) { - /* stuffed bit */ - s->hdlcrx.numbits--; - s->hdlcrx.bitbuf = (s->hdlcrx.bitbuf & (~mask6)) | - ((s->hdlcrx.bitbuf & mask6) << 1); - } - } - s->hdlcrx.numbits -= hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf, - s->hdlcrx.numbits); - } - clear_bit(0, &s->hdlcrx.in_hdlc_rx); -} - -/* ---------------------------------------------------------------------- */ - -static inline void do_kiss_params(struct hdlcdrv_state *s, - unsigned char *data, unsigned long len) -{ - -#ifdef KISS_VERBOSE -#define PKP(a,b) printk(KERN_INFO "hdlcdrv.c: channel params: " a "\n", b) -#else /* KISS_VERBOSE */ -#define PKP(a,b) -#endif /* KISS_VERBOSE */ - - if (len < 2) - return; - switch(data[0]) { - case PARAM_TXDELAY: - s->ch_params.tx_delay = data[1]; - PKP("TX delay = %ums", 10 * s->ch_params.tx_delay); - break; - case PARAM_PERSIST: - s->ch_params.ppersist = data[1]; - PKP("p persistence = %u", s->ch_params.ppersist); - break; - case PARAM_SLOTTIME: - s->ch_params.slottime = data[1]; - PKP("slot time = %ums", s->ch_params.slottime); - break; - case PARAM_TXTAIL: - s->ch_params.tx_tail = data[1]; - PKP("TX tail = %ums", s->ch_params.tx_tail); - break; - case PARAM_FULLDUP: - s->ch_params.fulldup = !!data[1]; - PKP("%s duplex", s->ch_params.fulldup ? "full" : "half"); - break; - default: - break; - } -#undef PKP -} - -/* ---------------------------------------------------------------------- */ - -void hdlcdrv_transmitter(struct net_device *dev, struct hdlcdrv_state *s) -{ - unsigned int mask1, mask2, mask3; - int i; - struct sk_buff *skb; - int pkt_len; - - if (!s || s->magic != HDLCDRV_MAGIC) - return; - if (test_and_set_bit(0, &s->hdlctx.in_hdlc_tx)) - return; - for (;;) { - if (s->hdlctx.numbits >= 16) { - if (hdlcdrv_hbuf_full(&s->hdlctx.hbuf)) { - clear_bit(0, &s->hdlctx.in_hdlc_tx); - return; - } - hdlcdrv_hbuf_put(&s->hdlctx.hbuf, s->hdlctx.bitbuf); - s->hdlctx.bitbuf >>= 16; - s->hdlctx.numbits -= 16; - } - switch (s->hdlctx.tx_state) { - default: - clear_bit(0, &s->hdlctx.in_hdlc_tx); - return; - case 0: - case 1: - if (s->hdlctx.numflags) { - s->hdlctx.numflags--; - s->hdlctx.bitbuf |= - 0x7e7e << s->hdlctx.numbits; - s->hdlctx.numbits += 16; - break; - } - if (s->hdlctx.tx_state == 1) { - clear_bit(0, &s->hdlctx.in_hdlc_tx); - return; - } - if (!(skb = s->skb)) { - int flgs = tenms_to_2flags(s, s->ch_params.tx_tail); - if (flgs < 2) - flgs = 2; - s->hdlctx.tx_state = 1; - s->hdlctx.numflags = flgs; - break; - } - s->skb = NULL; - netif_wake_queue(dev); - pkt_len = skb->len-1; /* strip KISS byte */ - if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) { - s->hdlctx.tx_state = 0; - s->hdlctx.numflags = 1; - dev_kfree_skb_irq(skb); - break; - } - skb_copy_from_linear_data_offset(skb, 1, - s->hdlctx.buffer, - pkt_len); - dev_kfree_skb_irq(skb); - s->hdlctx.bp = s->hdlctx.buffer; - append_crc_ccitt(s->hdlctx.buffer, pkt_len); - s->hdlctx.len = pkt_len+2; /* the appended CRC */ - s->hdlctx.tx_state = 2; - s->hdlctx.bitstream = 0; - dev->stats.tx_packets++; - break; - case 2: - if (!s->hdlctx.len) { - s->hdlctx.tx_state = 0; - s->hdlctx.numflags = 1; - break; - } - s->hdlctx.len--; - s->hdlctx.bitbuf |= *s->hdlctx.bp << - s->hdlctx.numbits; - s->hdlctx.bitstream >>= 8; - s->hdlctx.bitstream |= (*s->hdlctx.bp++) << 16; - mask1 = 0x1f000; - mask2 = 0x10000; - mask3 = 0xffffffff >> (31-s->hdlctx.numbits); - s->hdlctx.numbits += 8; - for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, - mask3 = (mask3 << 1) | 1) { - if ((s->hdlctx.bitstream & mask1) != mask1) - continue; - s->hdlctx.bitstream &= ~mask2; - s->hdlctx.bitbuf = - (s->hdlctx.bitbuf & mask3) | - ((s->hdlctx.bitbuf & - (~mask3)) << 1); - s->hdlctx.numbits++; - mask3 = (mask3 << 1) | 1; - } - break; - } - } -} - -/* ---------------------------------------------------------------------- */ - -static void start_tx(struct net_device *dev, struct hdlcdrv_state *s) -{ - s->hdlctx.tx_state = 0; - s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay); - s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0; - hdlcdrv_transmitter(dev, s); - s->hdlctx.ptt = 1; - s->ptt_keyed++; -} - -/* ---------------------------------------------------------------------- */ - -void hdlcdrv_arbitrate(struct net_device *dev, struct hdlcdrv_state *s) -{ - if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt || !s->skb) - return; - if (s->ch_params.fulldup) { - start_tx(dev, s); - return; - } - if (s->hdlcrx.dcd) { - s->hdlctx.slotcnt = s->ch_params.slottime; - return; - } - if ((--s->hdlctx.slotcnt) > 0) - return; - s->hdlctx.slotcnt = s->ch_params.slottime; - if (get_random_u8() > s->ch_params.ppersist) - return; - start_tx(dev, s); -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== network driver interface ========================= - */ - -static netdev_tx_t hdlcdrv_send_packet(struct sk_buff *skb, - struct net_device *dev) -{ - struct hdlcdrv_state *sm = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - if (skb->data[0] != 0) { - do_kiss_params(sm, skb->data, skb->len); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - if (sm->skb) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - netif_stop_queue(dev); - sm->skb = skb; - return NETDEV_TX_OK; -} - -/* --------------------------------------------------------------------- */ - -static int hdlcdrv_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *)addr; - - /* addr is an AX.25 shifted ASCII mac address */ - dev_addr_set(dev, sa->sa_data); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ - -static int hdlcdrv_open(struct net_device *dev) -{ - struct hdlcdrv_state *s = netdev_priv(dev); - int i; - - if (!s->ops || !s->ops->open) - return -ENODEV; - - /* - * initialise some variables - */ - s->opened = 1; - s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0; - s->hdlcrx.in_hdlc_rx = 0; - s->hdlcrx.rx_state = 0; - - s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0; - s->hdlctx.in_hdlc_tx = 0; - s->hdlctx.tx_state = 1; - s->hdlctx.numflags = 0; - s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0; - s->hdlctx.ptt = 0; - s->hdlctx.slotcnt = s->ch_params.slottime; - s->hdlctx.calibrate = 0; - - i = s->ops->open(dev); - if (i) - return i; - netif_start_queue(dev); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * The inverse routine to hdlcdrv_open(). - */ - -static int hdlcdrv_close(struct net_device *dev) -{ - struct hdlcdrv_state *s = netdev_priv(dev); - int i = 0; - - netif_stop_queue(dev); - - if (s->ops && s->ops->close) - i = s->ops->close(dev); - dev_kfree_skb(s->skb); - s->skb = NULL; - s->opened = 0; - return i; -} - -/* --------------------------------------------------------------------- */ - -static int hdlcdrv_siocdevprivate(struct net_device *dev, struct ifreq *ifr, - void __user *data, int cmd) -{ - struct hdlcdrv_state *s = netdev_priv(dev); - struct hdlcdrv_ioctl bi; - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (in_compat_syscall()) /* to be implemented */ - return -ENOIOCTLCMD; - - if (copy_from_user(&bi, data, sizeof(bi))) - return -EFAULT; - - switch (bi.cmd) { - default: - if (s->ops && s->ops->ioctl) - return s->ops->ioctl(dev, data, &bi, cmd); - return -ENOIOCTLCMD; - - case HDLCDRVCTL_GETCHANNELPAR: - bi.data.cp.tx_delay = s->ch_params.tx_delay; - bi.data.cp.tx_tail = s->ch_params.tx_tail; - bi.data.cp.slottime = s->ch_params.slottime; - bi.data.cp.ppersist = s->ch_params.ppersist; - bi.data.cp.fulldup = s->ch_params.fulldup; - break; - - case HDLCDRVCTL_SETCHANNELPAR: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - s->ch_params.tx_delay = bi.data.cp.tx_delay; - s->ch_params.tx_tail = bi.data.cp.tx_tail; - s->ch_params.slottime = bi.data.cp.slottime; - s->ch_params.ppersist = bi.data.cp.ppersist; - s->ch_params.fulldup = bi.data.cp.fulldup; - s->hdlctx.slotcnt = 1; - return 0; - - case HDLCDRVCTL_GETMODEMPAR: - bi.data.mp.iobase = dev->base_addr; - bi.data.mp.irq = dev->irq; - bi.data.mp.dma = dev->dma; - bi.data.mp.dma2 = s->ptt_out.dma2; - bi.data.mp.seriobase = s->ptt_out.seriobase; - bi.data.mp.pariobase = s->ptt_out.pariobase; - bi.data.mp.midiiobase = s->ptt_out.midiiobase; - break; - - case HDLCDRVCTL_SETMODEMPAR: - if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev)) - return -EACCES; - dev->base_addr = bi.data.mp.iobase; - dev->irq = bi.data.mp.irq; - dev->dma = bi.data.mp.dma; - s->ptt_out.dma2 = bi.data.mp.dma2; - s->ptt_out.seriobase = bi.data.mp.seriobase; - s->ptt_out.pariobase = bi.data.mp.pariobase; - s->ptt_out.midiiobase = bi.data.mp.midiiobase; - return 0; - - case HDLCDRVCTL_GETSTAT: - bi.data.cs.ptt = hdlcdrv_ptt(s); - bi.data.cs.dcd = s->hdlcrx.dcd; - bi.data.cs.ptt_keyed = s->ptt_keyed; - bi.data.cs.tx_packets = dev->stats.tx_packets; - bi.data.cs.tx_errors = dev->stats.tx_errors; - bi.data.cs.rx_packets = dev->stats.rx_packets; - bi.data.cs.rx_errors = dev->stats.rx_errors; - break; - - case HDLCDRVCTL_OLDGETSTAT: - bi.data.ocs.ptt = hdlcdrv_ptt(s); - bi.data.ocs.dcd = s->hdlcrx.dcd; - bi.data.ocs.ptt_keyed = s->ptt_keyed; - break; - - case HDLCDRVCTL_CALIBRATE: - if(!capable(CAP_SYS_RAWIO)) - return -EPERM; - if (s->par.bitrate <= 0) - return -EINVAL; - if (bi.data.calibrate > INT_MAX / s->par.bitrate) - return -EINVAL; - s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16; - return 0; - - case HDLCDRVCTL_GETSAMPLES: -#ifndef HDLCDRV_DEBUG - return -EPERM; -#else /* HDLCDRV_DEBUG */ - if (s->bitbuf_channel.rd == s->bitbuf_channel.wr) - return -EAGAIN; - bi.data.bits = - s->bitbuf_channel.buffer[s->bitbuf_channel.rd]; - s->bitbuf_channel.rd = (s->bitbuf_channel.rd+1) % - sizeof(s->bitbuf_channel.buffer); - break; -#endif /* HDLCDRV_DEBUG */ - - case HDLCDRVCTL_GETBITS: -#ifndef HDLCDRV_DEBUG - return -EPERM; -#else /* HDLCDRV_DEBUG */ - if (s->bitbuf_hdlc.rd == s->bitbuf_hdlc.wr) - return -EAGAIN; - bi.data.bits = - s->bitbuf_hdlc.buffer[s->bitbuf_hdlc.rd]; - s->bitbuf_hdlc.rd = (s->bitbuf_hdlc.rd+1) % - sizeof(s->bitbuf_hdlc.buffer); - break; -#endif /* HDLCDRV_DEBUG */ - - case HDLCDRVCTL_DRIVERNAME: - if (s->ops && s->ops->drvname) { - strscpy(bi.data.drivername, s->ops->drvname, - sizeof(bi.data.drivername)); - break; - } - bi.data.drivername[0] = '\0'; - break; - - } - if (copy_to_user(data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -static const struct net_device_ops hdlcdrv_netdev = { - .ndo_open = hdlcdrv_open, - .ndo_stop = hdlcdrv_close, - .ndo_start_xmit = hdlcdrv_send_packet, - .ndo_siocdevprivate = hdlcdrv_siocdevprivate, - .ndo_set_mac_address = hdlcdrv_set_mac_address, -}; - -/* - * Initialize fields in hdlcdrv - */ -static void hdlcdrv_setup(struct net_device *dev) -{ - static const struct hdlcdrv_channel_params dflt_ch_params = { - 20, 2, 10, 40, 0 - }; - struct hdlcdrv_state *s = netdev_priv(dev); - - /* - * initialize the hdlcdrv_state struct - */ - s->ch_params = dflt_ch_params; - s->ptt_keyed = 0; - - spin_lock_init(&s->hdlcrx.hbuf.lock); - s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0; - s->hdlcrx.in_hdlc_rx = 0; - s->hdlcrx.rx_state = 0; - - spin_lock_init(&s->hdlctx.hbuf.lock); - s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0; - s->hdlctx.in_hdlc_tx = 0; - s->hdlctx.tx_state = 1; - s->hdlctx.numflags = 0; - s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0; - s->hdlctx.ptt = 0; - s->hdlctx.slotcnt = s->ch_params.slottime; - s->hdlctx.calibrate = 0; - -#ifdef HDLCDRV_DEBUG - s->bitbuf_channel.rd = s->bitbuf_channel.wr = 0; - s->bitbuf_channel.shreg = 0x80; - - s->bitbuf_hdlc.rd = s->bitbuf_hdlc.wr = 0; - s->bitbuf_hdlc.shreg = 0x80; -#endif /* HDLCDRV_DEBUG */ - - - /* Fill in the fields of the device structure */ - - s->skb = NULL; - - dev->netdev_ops = &hdlcdrv_netdev; - dev->header_ops = &ax25_header_ops; - - dev->type = ARPHRD_AX25; /* AF_AX25 device */ - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */ - dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */ - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); - dev->tx_queue_len = 16; -} - -/* --------------------------------------------------------------------- */ -struct net_device *hdlcdrv_register(const struct hdlcdrv_ops *ops, - unsigned int privsize, const char *ifname, - unsigned int baseaddr, unsigned int irq, - unsigned int dma) -{ - struct net_device *dev; - struct hdlcdrv_state *s; - int err; - - if (privsize < sizeof(struct hdlcdrv_state)) - privsize = sizeof(struct hdlcdrv_state); - - dev = alloc_netdev(privsize, ifname, NET_NAME_UNKNOWN, hdlcdrv_setup); - if (!dev) - return ERR_PTR(-ENOMEM); - - /* - * initialize part of the hdlcdrv_state struct - */ - s = netdev_priv(dev); - s->magic = HDLCDRV_MAGIC; - s->ops = ops; - dev->base_addr = baseaddr; - dev->irq = irq; - dev->dma = dma; - - err = register_netdev(dev); - if (err < 0) { - printk(KERN_WARNING "hdlcdrv: cannot register net " - "device %s\n", dev->name); - free_netdev(dev); - dev = ERR_PTR(err); - } - return dev; -} - -/* --------------------------------------------------------------------- */ - -void hdlcdrv_unregister(struct net_device *dev) -{ - struct hdlcdrv_state *s = netdev_priv(dev); - - BUG_ON(s->magic != HDLCDRV_MAGIC); - - if (s->opened && s->ops->close) - s->ops->close(dev); - unregister_netdev(dev); - - free_netdev(dev); -} - -/* --------------------------------------------------------------------- */ - -EXPORT_SYMBOL(hdlcdrv_receiver); -EXPORT_SYMBOL(hdlcdrv_transmitter); -EXPORT_SYMBOL(hdlcdrv_arbitrate); -EXPORT_SYMBOL(hdlcdrv_register); -EXPORT_SYMBOL(hdlcdrv_unregister); - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c deleted file mode 100644 index 5f38a002bd9e..000000000000 --- a/drivers/net/hamradio/mkiss.c +++ /dev/null @@ -1,980 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Copyright (C) Hans Alblas PE1AYX - * Copyright (C) 2004, 05 Ralf Baechle DL5RB - * Copyright (C) 2004, 05 Thomas Osterried DL9SAU - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define AX_MTU 236 - -/* some arch define END as assembly function ending, just undef it */ -#undef END -/* SLIP/KISS protocol characters. */ -#define END 0300 /* indicates end of frame */ -#define ESC 0333 /* indicates byte stuffing */ -#define ESC_END 0334 /* ESC ESC_END means END 'data' */ -#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ - -struct mkiss { - struct tty_struct *tty; /* ptr to TTY structure */ - struct net_device *dev; /* easy for intr handling */ - - /* These are pointers to the malloc()ed frame buffers. */ - spinlock_t buflock;/* lock for rbuf and xbuf */ - unsigned char *rbuff; /* receiver buffer */ - int rcount; /* received chars counter */ - unsigned char *xbuff; /* transmitter buffer */ - unsigned char *xhead; /* pointer to next byte to XMIT */ - int xleft; /* bytes left in XMIT queue */ - - /* Detailed SLIP statistics. */ - int mtu; /* Our mtu (to spot changes!) */ - int buffsize; /* Max buffers sizes */ - - unsigned long flags; /* Flag values/ mode etc */ - /* long req'd: used by set_bit --RR */ -#define AXF_INUSE 0 /* Channel in use */ -#define AXF_ESCAPE 1 /* ESC received */ -#define AXF_ERROR 2 /* Parity, etc. error */ -#define AXF_KEEPTEST 3 /* Keepalive test flag */ -#define AXF_OUTWAIT 4 /* is outpacket was flag */ - - int mode; - int crcmode; /* MW: for FlexNet, SMACK etc. */ - int crcauto; /* CRC auto mode */ - -#define CRC_MODE_NONE 0 -#define CRC_MODE_FLEX 1 -#define CRC_MODE_SMACK 2 -#define CRC_MODE_FLEX_TEST 3 -#define CRC_MODE_SMACK_TEST 4 - - refcount_t refcnt; - struct completion dead; -}; - -/*---------------------------------------------------------------------------*/ - -static const unsigned short crc_flex_table[] = { - 0x0f87, 0x1e0e, 0x2c95, 0x3d1c, 0x49a3, 0x582a, 0x6ab1, 0x7b38, - 0x83cf, 0x9246, 0xa0dd, 0xb154, 0xc5eb, 0xd462, 0xe6f9, 0xf770, - 0x1f06, 0x0e8f, 0x3c14, 0x2d9d, 0x5922, 0x48ab, 0x7a30, 0x6bb9, - 0x934e, 0x82c7, 0xb05c, 0xa1d5, 0xd56a, 0xc4e3, 0xf678, 0xe7f1, - 0x2e85, 0x3f0c, 0x0d97, 0x1c1e, 0x68a1, 0x7928, 0x4bb3, 0x5a3a, - 0xa2cd, 0xb344, 0x81df, 0x9056, 0xe4e9, 0xf560, 0xc7fb, 0xd672, - 0x3e04, 0x2f8d, 0x1d16, 0x0c9f, 0x7820, 0x69a9, 0x5b32, 0x4abb, - 0xb24c, 0xa3c5, 0x915e, 0x80d7, 0xf468, 0xe5e1, 0xd77a, 0xc6f3, - 0x4d83, 0x5c0a, 0x6e91, 0x7f18, 0x0ba7, 0x1a2e, 0x28b5, 0x393c, - 0xc1cb, 0xd042, 0xe2d9, 0xf350, 0x87ef, 0x9666, 0xa4fd, 0xb574, - 0x5d02, 0x4c8b, 0x7e10, 0x6f99, 0x1b26, 0x0aaf, 0x3834, 0x29bd, - 0xd14a, 0xc0c3, 0xf258, 0xe3d1, 0x976e, 0x86e7, 0xb47c, 0xa5f5, - 0x6c81, 0x7d08, 0x4f93, 0x5e1a, 0x2aa5, 0x3b2c, 0x09b7, 0x183e, - 0xe0c9, 0xf140, 0xc3db, 0xd252, 0xa6ed, 0xb764, 0x85ff, 0x9476, - 0x7c00, 0x6d89, 0x5f12, 0x4e9b, 0x3a24, 0x2bad, 0x1936, 0x08bf, - 0xf048, 0xe1c1, 0xd35a, 0xc2d3, 0xb66c, 0xa7e5, 0x957e, 0x84f7, - 0x8b8f, 0x9a06, 0xa89d, 0xb914, 0xcdab, 0xdc22, 0xeeb9, 0xff30, - 0x07c7, 0x164e, 0x24d5, 0x355c, 0x41e3, 0x506a, 0x62f1, 0x7378, - 0x9b0e, 0x8a87, 0xb81c, 0xa995, 0xdd2a, 0xcca3, 0xfe38, 0xefb1, - 0x1746, 0x06cf, 0x3454, 0x25dd, 0x5162, 0x40eb, 0x7270, 0x63f9, - 0xaa8d, 0xbb04, 0x899f, 0x9816, 0xeca9, 0xfd20, 0xcfbb, 0xde32, - 0x26c5, 0x374c, 0x05d7, 0x145e, 0x60e1, 0x7168, 0x43f3, 0x527a, - 0xba0c, 0xab85, 0x991e, 0x8897, 0xfc28, 0xeda1, 0xdf3a, 0xceb3, - 0x3644, 0x27cd, 0x1556, 0x04df, 0x7060, 0x61e9, 0x5372, 0x42fb, - 0xc98b, 0xd802, 0xea99, 0xfb10, 0x8faf, 0x9e26, 0xacbd, 0xbd34, - 0x45c3, 0x544a, 0x66d1, 0x7758, 0x03e7, 0x126e, 0x20f5, 0x317c, - 0xd90a, 0xc883, 0xfa18, 0xeb91, 0x9f2e, 0x8ea7, 0xbc3c, 0xadb5, - 0x5542, 0x44cb, 0x7650, 0x67d9, 0x1366, 0x02ef, 0x3074, 0x21fd, - 0xe889, 0xf900, 0xcb9b, 0xda12, 0xaead, 0xbf24, 0x8dbf, 0x9c36, - 0x64c1, 0x7548, 0x47d3, 0x565a, 0x22e5, 0x336c, 0x01f7, 0x107e, - 0xf808, 0xe981, 0xdb1a, 0xca93, 0xbe2c, 0xafa5, 0x9d3e, 0x8cb7, - 0x7440, 0x65c9, 0x5752, 0x46db, 0x3264, 0x23ed, 0x1176, 0x00ff -}; - -static unsigned short calc_crc_flex(unsigned char *cp, int size) -{ - unsigned short crc = 0xffff; - - while (size--) - crc = (crc << 8) ^ crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; - - return crc; -} - -static int check_crc_flex(unsigned char *cp, int size) -{ - unsigned short crc = 0xffff; - - if (size < 3) - return -1; - - while (size--) - crc = (crc << 8) ^ crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; - - if ((crc & 0xffff) != 0x7070) - return -1; - - return 0; -} - -static int check_crc_16(unsigned char *cp, int size) -{ - unsigned short crc = 0x0000; - - if (size < 3) - return -1; - - crc = crc16(0, cp, size); - - if (crc != 0x0000) - return -1; - - return 0; -} - -/* - * Standard encapsulation - */ - -static int kiss_esc(unsigned char *s, unsigned char *d, int len) -{ - unsigned char *ptr = d; - unsigned char c; - - /* - * Send an initial END character to flush out any data that may have - * accumulated in the receiver due to line noise. - */ - - *ptr++ = END; - - while (len-- > 0) { - switch (c = *s++) { - case END: - *ptr++ = ESC; - *ptr++ = ESC_END; - break; - case ESC: - *ptr++ = ESC; - *ptr++ = ESC_ESC; - break; - default: - *ptr++ = c; - break; - } - } - - *ptr++ = END; - - return ptr - d; -} - -/* - * MW: - * OK its ugly, but tell me a better solution without copying the - * packet to a temporary buffer :-) - */ -static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, - int len) -{ - unsigned char *ptr = d; - unsigned char c=0; - - *ptr++ = END; - while (len > 0) { - if (len > 2) - c = *s++; - else if (len > 1) - c = crc >> 8; - else - c = crc & 0xff; - - len--; - - switch (c) { - case END: - *ptr++ = ESC; - *ptr++ = ESC_END; - break; - case ESC: - *ptr++ = ESC; - *ptr++ = ESC_ESC; - break; - default: - *ptr++ = c; - break; - } - } - *ptr++ = END; - - return ptr - d; -} - -/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */ -static void ax_bump(struct mkiss *ax) -{ - struct sk_buff *skb; - int count; - - spin_lock_bh(&ax->buflock); - if (ax->rbuff[0] > 0x0f) { - if (ax->rbuff[0] & 0x80) { - if (check_crc_16(ax->rbuff, ax->rcount) < 0) { - ax->dev->stats.rx_errors++; - spin_unlock_bh(&ax->buflock); - - return; - } - if (ax->crcmode != CRC_MODE_SMACK && ax->crcauto) { - printk(KERN_INFO - "mkiss: %s: Switching to crc-smack\n", - ax->dev->name); - ax->crcmode = CRC_MODE_SMACK; - } - ax->rcount -= 2; - *ax->rbuff &= ~0x80; - } else if (ax->rbuff[0] & 0x20) { - if (check_crc_flex(ax->rbuff, ax->rcount) < 0) { - ax->dev->stats.rx_errors++; - spin_unlock_bh(&ax->buflock); - return; - } - if (ax->crcmode != CRC_MODE_FLEX && ax->crcauto) { - printk(KERN_INFO - "mkiss: %s: Switching to crc-flexnet\n", - ax->dev->name); - ax->crcmode = CRC_MODE_FLEX; - } - ax->rcount -= 2; - - /* - * dl9sau bugfix: the trailling two bytes flexnet crc - * will not be passed to the kernel. thus we have to - * correct the kissparm signature, because it indicates - * a crc but there's none - */ - *ax->rbuff &= ~0x20; - } - } - - count = ax->rcount; - - if ((skb = dev_alloc_skb(count)) == NULL) { - printk(KERN_ERR "mkiss: %s: memory squeeze, dropping packet.\n", - ax->dev->name); - ax->dev->stats.rx_dropped++; - spin_unlock_bh(&ax->buflock); - return; - } - - skb_put_data(skb, ax->rbuff, count); - skb->protocol = ax25_type_trans(skb, ax->dev); - netif_rx(skb); - ax->dev->stats.rx_packets++; - ax->dev->stats.rx_bytes += count; - spin_unlock_bh(&ax->buflock); -} - -static void kiss_unesc(struct mkiss *ax, unsigned char s) -{ - switch (s) { - case END: - /* drop keeptest bit = VSV */ - if (test_bit(AXF_KEEPTEST, &ax->flags)) - clear_bit(AXF_KEEPTEST, &ax->flags); - - if (!test_and_clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2)) - ax_bump(ax); - - clear_bit(AXF_ESCAPE, &ax->flags); - ax->rcount = 0; - return; - - case ESC: - set_bit(AXF_ESCAPE, &ax->flags); - return; - case ESC_ESC: - if (test_and_clear_bit(AXF_ESCAPE, &ax->flags)) - s = ESC; - break; - case ESC_END: - if (test_and_clear_bit(AXF_ESCAPE, &ax->flags)) - s = END; - break; - } - - spin_lock_bh(&ax->buflock); - if (!test_bit(AXF_ERROR, &ax->flags)) { - if (ax->rcount < ax->buffsize) { - ax->rbuff[ax->rcount++] = s; - spin_unlock_bh(&ax->buflock); - return; - } - - ax->dev->stats.rx_over_errors++; - set_bit(AXF_ERROR, &ax->flags); - } - spin_unlock_bh(&ax->buflock); -} - -static int ax_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr_ax25 *sa = addr; - - netif_tx_lock_bh(dev); - netif_addr_lock(dev); - __dev_addr_set(dev, &sa->sax25_call, AX25_ADDR_LEN); - netif_addr_unlock(dev); - netif_tx_unlock_bh(dev); - - return 0; -} - -/*---------------------------------------------------------------------------*/ - -static void ax_changedmtu(struct mkiss *ax) -{ - struct net_device *dev = ax->dev; - unsigned char *xbuff, *rbuff, *oxbuff, *orbuff; - int len; - - len = dev->mtu * 2; - - /* - * allow for arrival of larger UDP packets, even if we say not to - * also fixes a bug in which SunOS sends 512-byte packets even with - * an MSS of 128 - */ - if (len < 576 * 2) - len = 576 * 2; - - xbuff = kmalloc(len + 4, GFP_ATOMIC); - rbuff = kmalloc(len + 4, GFP_ATOMIC); - - if (xbuff == NULL || rbuff == NULL) { - printk(KERN_ERR "mkiss: %s: unable to grow ax25 buffers, " - "MTU change cancelled.\n", - ax->dev->name); - dev->mtu = ax->mtu; - kfree(xbuff); - kfree(rbuff); - return; - } - - spin_lock_bh(&ax->buflock); - - oxbuff = ax->xbuff; - ax->xbuff = xbuff; - orbuff = ax->rbuff; - ax->rbuff = rbuff; - - if (ax->xleft) { - if (ax->xleft <= len) { - memcpy(ax->xbuff, ax->xhead, ax->xleft); - } else { - ax->xleft = 0; - dev->stats.tx_dropped++; - } - } - - ax->xhead = ax->xbuff; - - if (ax->rcount) { - if (ax->rcount <= len) { - memcpy(ax->rbuff, orbuff, ax->rcount); - } else { - ax->rcount = 0; - dev->stats.rx_over_errors++; - set_bit(AXF_ERROR, &ax->flags); - } - } - - ax->mtu = dev->mtu + 73; - ax->buffsize = len; - - spin_unlock_bh(&ax->buflock); - - kfree(oxbuff); - kfree(orbuff); -} - -/* Encapsulate one AX.25 packet and stuff into a TTY queue. */ -static void ax_encaps(struct net_device *dev, unsigned char *icp, int len) -{ - struct mkiss *ax = netdev_priv(dev); - unsigned char *p; - int actual, count; - - if (ax->mtu != ax->dev->mtu + 73) /* Someone has been ifconfigging */ - ax_changedmtu(ax); - - if (len > ax->mtu) { /* Sigh, shouldn't occur BUT ... */ - printk(KERN_ERR "mkiss: %s: truncating oversized transmit packet!\n", ax->dev->name); - dev->stats.tx_dropped++; - netif_start_queue(dev); - return; - } - - p = icp; - - spin_lock_bh(&ax->buflock); - if ((*p & 0x0f) != 0) { - /* Configuration Command (kissparms(1). - * Protocol spec says: never append CRC. - * This fixes a very old bug in the linux - * kiss driver. -- dl9sau */ - switch (*p & 0xff) { - case 0x85: - /* command from userspace especially for us, - * not for delivery to the tnc */ - if (len > 1) { - int cmd = (p[1] & 0xff); - switch(cmd) { - case 3: - ax->crcmode = CRC_MODE_SMACK; - break; - case 2: - ax->crcmode = CRC_MODE_FLEX; - break; - case 1: - ax->crcmode = CRC_MODE_NONE; - break; - case 0: - default: - ax->crcmode = CRC_MODE_SMACK_TEST; - cmd = 0; - } - ax->crcauto = (cmd ? 0 : 1); - printk(KERN_INFO "mkiss: %s: crc mode set to %d\n", - ax->dev->name, cmd); - } - spin_unlock_bh(&ax->buflock); - netif_start_queue(dev); - - return; - default: - count = kiss_esc(p, ax->xbuff, len); - } - } else { - unsigned short crc; - switch (ax->crcmode) { - case CRC_MODE_SMACK_TEST: - ax->crcmode = CRC_MODE_FLEX_TEST; - printk(KERN_INFO "mkiss: %s: Trying crc-smack\n", ax->dev->name); - fallthrough; - case CRC_MODE_SMACK: - *p |= 0x80; - crc = swab16(crc16(0, p, len)); - count = kiss_esc_crc(p, ax->xbuff, crc, len+2); - break; - case CRC_MODE_FLEX_TEST: - ax->crcmode = CRC_MODE_NONE; - printk(KERN_INFO "mkiss: %s: Trying crc-flexnet\n", ax->dev->name); - fallthrough; - case CRC_MODE_FLEX: - *p |= 0x20; - crc = calc_crc_flex(p, len); - count = kiss_esc_crc(p, ax->xbuff, crc, len+2); - break; - - default: - count = kiss_esc(p, ax->xbuff, len); - } - } - spin_unlock_bh(&ax->buflock); - - set_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); - actual = ax->tty->ops->write(ax->tty, ax->xbuff, count); - dev->stats.tx_packets++; - dev->stats.tx_bytes += actual; - - netif_trans_update(ax->dev); - ax->xleft = count - actual; - ax->xhead = ax->xbuff + actual; -} - -/* Encapsulate an AX.25 packet and kick it into a TTY queue. */ -static netdev_tx_t ax_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct mkiss *ax = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - if (!netif_running(dev)) { - printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name); - return NETDEV_TX_BUSY; - } - - if (netif_queue_stopped(dev)) { - /* - * May be we must check transmitter timeout here ? - * 14 Oct 1994 Dmitry Gorodchanin. - */ - if (time_before(jiffies, dev_trans_start(dev) + 20 * HZ)) { - /* 20 sec timeout not reached */ - return NETDEV_TX_BUSY; - } - - printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name, - (tty_chars_in_buffer(ax->tty) || ax->xleft) ? - "bad line quality" : "driver error"); - - ax->xleft = 0; - clear_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); - netif_start_queue(dev); - } - - /* We were not busy, so we are now... :-) */ - netif_stop_queue(dev); - ax_encaps(dev, skb->data, skb->len); - kfree_skb(skb); - - return NETDEV_TX_OK; -} - -static int ax_open_dev(struct net_device *dev) -{ - struct mkiss *ax = netdev_priv(dev); - - if (ax->tty == NULL) - return -ENODEV; - - return 0; -} - -/* Open the low-level part of the AX25 channel. Easy! */ -static int ax_open(struct net_device *dev) -{ - struct mkiss *ax = netdev_priv(dev); - unsigned long len; - - if (ax->tty == NULL) - return -ENODEV; - - /* - * Allocate the frame buffers: - * - * rbuff Receive buffer. - * xbuff Transmit buffer. - */ - len = dev->mtu * 2; - - /* - * allow for arrival of larger UDP packets, even if we say not to - * also fixes a bug in which SunOS sends 512-byte packets even with - * an MSS of 128 - */ - if (len < 576 * 2) - len = 576 * 2; - - if ((ax->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) - goto norbuff; - - if ((ax->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) - goto noxbuff; - - ax->mtu = dev->mtu + 73; - ax->buffsize = len; - ax->rcount = 0; - ax->xleft = 0; - - ax->flags &= (1 << AXF_INUSE); /* Clear ESCAPE & ERROR flags */ - - spin_lock_init(&ax->buflock); - - return 0; - -noxbuff: - kfree(ax->rbuff); - -norbuff: - return -ENOMEM; -} - - -/* Close the low-level part of the AX25 channel. Easy! */ -static int ax_close(struct net_device *dev) -{ - struct mkiss *ax = netdev_priv(dev); - - if (ax->tty) - clear_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); - - netif_stop_queue(dev); - - return 0; -} - -static const struct net_device_ops ax_netdev_ops = { - .ndo_open = ax_open_dev, - .ndo_stop = ax_close, - .ndo_start_xmit = ax_xmit, - .ndo_set_mac_address = ax_set_mac_address, -}; - -static void ax_setup(struct net_device *dev) -{ - /* Finish setting up the DEVICE info. */ - dev->mtu = AX_MTU; - dev->hard_header_len = AX25_MAX_HEADER_LEN; - dev->addr_len = AX25_ADDR_LEN; - dev->type = ARPHRD_AX25; - dev->tx_queue_len = 10; - dev->header_ops = &ax25_header_ops; - dev->netdev_ops = &ax_netdev_ops; - - - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); - - dev->flags = IFF_BROADCAST | IFF_MULTICAST; -} - -/* - * We have a potential race on dereferencing tty->disc_data, because the tty - * layer provides no locking at all - thus one cpu could be running - * sixpack_receive_buf while another calls sixpack_close, which zeroes - * tty->disc_data and frees the memory that sixpack_receive_buf is using. The - * best way to fix this is to use a rwlock in the tty struct, but for now we - * use a single global rwlock for all ttys in ppp line discipline. - */ -static DEFINE_RWLOCK(disc_data_lock); - -static struct mkiss *mkiss_get(struct tty_struct *tty) -{ - struct mkiss *ax; - - read_lock(&disc_data_lock); - ax = tty->disc_data; - if (ax) - refcount_inc(&ax->refcnt); - read_unlock(&disc_data_lock); - - return ax; -} - -static void mkiss_put(struct mkiss *ax) -{ - if (refcount_dec_and_test(&ax->refcnt)) - complete(&ax->dead); -} - -static int crc_force = 0; /* Can be overridden with insmod */ - -static int mkiss_open(struct tty_struct *tty) -{ - struct net_device *dev; - struct mkiss *ax; - int err; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (tty->ops->write == NULL) - return -EOPNOTSUPP; - - dev = alloc_netdev(sizeof(struct mkiss), "ax%d", NET_NAME_UNKNOWN, - ax_setup); - if (!dev) { - err = -ENOMEM; - goto out; - } - - ax = netdev_priv(dev); - ax->dev = dev; - - spin_lock_init(&ax->buflock); - refcount_set(&ax->refcnt, 1); - init_completion(&ax->dead); - - ax->tty = tty; - tty->disc_data = ax; - tty->receive_room = 65535; - - tty_driver_flush_buffer(tty); - - /* Restore default settings */ - dev->type = ARPHRD_AX25; - - /* Perform the low-level AX25 initialization. */ - err = ax_open(ax->dev); - if (err) - goto out_free_netdev; - - err = register_netdev(dev); - if (err) - goto out_free_buffers; - - /* after register_netdev() - because else printk smashes the kernel */ - switch (crc_force) { - case 3: - ax->crcmode = CRC_MODE_SMACK; - printk(KERN_INFO "mkiss: %s: crc mode smack forced.\n", - ax->dev->name); - break; - case 2: - ax->crcmode = CRC_MODE_FLEX; - printk(KERN_INFO "mkiss: %s: crc mode flexnet forced.\n", - ax->dev->name); - break; - case 1: - ax->crcmode = CRC_MODE_NONE; - printk(KERN_INFO "mkiss: %s: crc mode disabled.\n", - ax->dev->name); - break; - case 0: - default: - crc_force = 0; - printk(KERN_INFO "mkiss: %s: crc mode is auto.\n", - ax->dev->name); - ax->crcmode = CRC_MODE_SMACK_TEST; - } - ax->crcauto = (crc_force ? 0 : 1); - - netif_start_queue(dev); - - /* Done. We have linked the TTY line to a channel. */ - return 0; - -out_free_buffers: - kfree(ax->rbuff); - kfree(ax->xbuff); - -out_free_netdev: - free_netdev(dev); - -out: - return err; -} - -static void mkiss_close(struct tty_struct *tty) -{ - struct mkiss *ax; - - write_lock_irq(&disc_data_lock); - ax = tty->disc_data; - tty->disc_data = NULL; - write_unlock_irq(&disc_data_lock); - - if (!ax) - return; - - /* - * We have now ensured that nobody can start using ap from now on, but - * we have to wait for all existing users to finish. - */ - if (!refcount_dec_and_test(&ax->refcnt)) - wait_for_completion(&ax->dead); - /* - * Halt the transmit queue so that a new transmit cannot scribble - * on our buffers - */ - netif_stop_queue(ax->dev); - - unregister_netdev(ax->dev); - - /* Free all AX25 frame buffers after unreg. */ - kfree(ax->rbuff); - kfree(ax->xbuff); - - ax->tty = NULL; - - free_netdev(ax->dev); -} - -/* Perform I/O control on an active ax25 channel. */ -static int mkiss_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - struct mkiss *ax = mkiss_get(tty); - struct net_device *dev; - unsigned int tmp, err; - - /* First make sure we're connected. */ - if (ax == NULL) - return -ENXIO; - dev = ax->dev; - - switch (cmd) { - case SIOCGIFNAME: - err = copy_to_user((void __user *) arg, ax->dev->name, - strlen(ax->dev->name) + 1) ? -EFAULT : 0; - break; - - case SIOCGIFENCAP: - err = put_user(4, (int __user *) arg); - break; - - case SIOCSIFENCAP: - if (get_user(tmp, (int __user *) arg)) { - err = -EFAULT; - break; - } - - ax->mode = tmp; - dev->addr_len = AX25_ADDR_LEN; - dev->hard_header_len = AX25_KISS_HEADER_LEN + - AX25_MAX_HEADER_LEN + 3; - dev->type = ARPHRD_AX25; - - err = 0; - break; - - case SIOCSIFHWADDR: { - char addr[AX25_ADDR_LEN]; - - if (copy_from_user(&addr, - (void __user *) arg, AX25_ADDR_LEN)) { - err = -EFAULT; - break; - } - - netif_tx_lock_bh(dev); - __dev_addr_set(dev, addr, AX25_ADDR_LEN); - netif_tx_unlock_bh(dev); - - err = 0; - break; - } - default: - err = -ENOIOCTLCMD; - } - - mkiss_put(ax); - - return err; -} - -/* - * Handle the 'receiver data ready' interrupt. - * This function is called by the 'tty_io' module in the kernel when - * a block of data has been received, which can now be decapsulated - * and sent on to the AX.25 layer for further processing. - */ -static void mkiss_receive_buf(struct tty_struct *tty, const u8 *cp, - const u8 *fp, size_t count) -{ - struct mkiss *ax = mkiss_get(tty); - - if (!ax) - return; - - /* - * Argh! mtu change time! - costs us the packet part received - * at the change - */ - if (ax->mtu != ax->dev->mtu + 73) - ax_changedmtu(ax); - - /* Read the characters out of the buffer */ - while (count--) { - if (fp != NULL && *fp++) { - if (!test_and_set_bit(AXF_ERROR, &ax->flags)) - ax->dev->stats.rx_errors++; - cp++; - continue; - } - - kiss_unesc(ax, *cp++); - } - - mkiss_put(ax); - tty_unthrottle(tty); -} - -/* - * Called by the driver when there's room for more data. If we have - * more packets to send, we send them here. - */ -static void mkiss_write_wakeup(struct tty_struct *tty) -{ - struct mkiss *ax = mkiss_get(tty); - int actual; - - if (!ax) - return; - - if (ax->xleft <= 0) { - /* Now serial buffer is almost free & we can start - * transmission of another packet - */ - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - - netif_wake_queue(ax->dev); - goto out; - } - - actual = tty->ops->write(tty, ax->xhead, ax->xleft); - ax->xleft -= actual; - ax->xhead += actual; - -out: - mkiss_put(ax); -} - -static struct tty_ldisc_ops ax_ldisc = { - .owner = THIS_MODULE, - .num = N_AX25, - .name = "mkiss", - .open = mkiss_open, - .close = mkiss_close, - .ioctl = mkiss_ioctl, - .receive_buf = mkiss_receive_buf, - .write_wakeup = mkiss_write_wakeup -}; - -static const char banner[] __initconst = KERN_INFO \ - "mkiss: AX.25 Multikiss, Hans Albas PE1AYX\n"; -static const char msg_regfail[] __initconst = KERN_ERR \ - "mkiss: can't register line discipline (err = %d)\n"; - -static int __init mkiss_init_driver(void) -{ - int status; - - printk(banner); - - status = tty_register_ldisc(&ax_ldisc); - if (status != 0) - printk(msg_regfail, status); - - return status; -} - -static void __exit mkiss_exit_driver(void) -{ - tty_unregister_ldisc(&ax_ldisc); -} - -MODULE_AUTHOR("Ralf Baechle DL5RB "); -MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs"); -module_param(crc_force, int, 0); -MODULE_PARM_DESC(crc_force, "crc [0 = auto | 1 = none | 2 = flexnet | 3 = smack]"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_LDISC(N_AX25); - -module_init(mkiss_init_driver); -module_exit(mkiss_exit_driver); diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c deleted file mode 100644 index 8569db4a7140..000000000000 --- a/drivers/net/hamradio/scc.c +++ /dev/null @@ -1,2179 +0,0 @@ -#define RCS_ID "$Id: scc.c,v 1.75 1998/11/04 15:15:01 jreuter Exp jreuter $" - -#define VERSION "3.0" - -/* - * Please use z8530drv-utils-3.0 with this version. - * ------------------ - * - * You can find a subset of the documentation in - * Documentation/networking/device_drivers/hamradio/z8530drv.rst. - */ - -/* - ******************************************************************** - * SCC.C - Linux driver for Z8530 based HDLC cards for AX.25 * - ******************************************************************** - - - ******************************************************************** - - Copyright (c) 1993, 2000 Joerg Reuter DL1BKE - - portions (c) 1993 Guido ten Dolle PE1NNZ - - ******************************************************************** - - The driver and the programs in the archive are UNDER CONSTRUCTION. - The code is likely to fail, and so your kernel could --- even - a whole network. - - This driver is intended for Amateur Radio use. If you are running it - for commercial purposes, please drop me a note. I am nosy... - - ...BUT: - - ! You m u s t recognize the appropriate legislations of your country ! - ! before you connect a radio to the SCC board and start to transmit or ! - ! receive. The GPL allows you to use the d r i v e r, NOT the RADIO! ! - - For non-Amateur-Radio use please note that you might need a special - allowance/licence from the designer of the SCC Board and/or the - MODEM. - - This program is free software; you can redistribute it and/or modify - it under the terms of the (modified) GNU General Public License - delivered with the Linux kernel source. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should find a copy of the GNU General Public License in - /usr/src/linux/COPYING; - - ******************************************************************** - - - Incomplete history of z8530drv: - ------------------------------- - - 1994-09-13 started to write the driver, rescued most of my own - code (and Hans Alblas' memory buffer pool concept) from - an earlier project "sccdrv" which was initiated by - Guido ten Dolle. Not much of the old driver survived, - though. The first version I put my hands on was sccdrv1.3 - from August 1993. The memory buffer pool concept - appeared in an unauthorized sccdrv version (1.5) from - August 1994. - - 1995-01-31 changed copyright notice to GPL without limitations. - - . - . - . - - 1996-10-05 New semester, new driver... - - * KISS TNC emulator removed (TTY driver) - * Source moved to drivers/net/ - * Includes Z8530 defines from drivers/net/z8530.h - * Uses sk_buffer memory management - * Reduced overhead of /proc/net/z8530drv output - * Streamlined quite a lot things - * Invents brand new bugs... ;-) - - The move to version number 3.0 reflects theses changes. - You can use 'kissbridge' if you need a KISS TNC emulator. - - 1996-12-13 Fixed for Linux networking changes. (G4KLX) - 1997-01-08 Fixed the remaining problems. - 1997-04-02 Hopefully fixed the problems with the new *_timer() - routines, added calibration code. - 1997-10-12 Made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO - 1998-01-29 Small fix to avoid lock-up on initialization - 1998-09-29 Fixed the "grouping" bugs, tx_inhibit works again, - using dev->tx_queue_len now instead of MAXQUEUE now. - 1998-10-21 Postponed the spinlock changes, would need a lot of - testing I currently don't have the time to. Softdcd doesn't - work. - 1998-11-04 Softdcd does not work correctly in DPLL mode, in fact it - never did. The DPLL locks on noise, the SYNC unit sees - flags that aren't... Restarting the DPLL does not help - either, it resynchronizes too slow and the first received - frame gets lost. - 2000-02-13 Fixed for new network driver interface changes, still - does TX timeouts itself since it uses its own queue - scheme. - - Thanks to all who contributed to this driver with ideas and bug - reports! - - NB -- if you find errors, change something, please let me know - first before you distribute it... And please don't touch - the version number. Just replace my callsign in - "v3.0.dl1bke" with your own. Just to avoid confusion... - - If you want to add your modification to the linux distribution - please (!) contact me first. - - New versions of the driver will be announced on the linux-hams - mailing list on vger.kernel.org. To subscribe send an e-mail - to majordomo@vger.kernel.org with the following line in - the body of the mail: - - subscribe linux-hams - - The content of the "Subject" field will be ignored. - - vy 73, - Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ABH.#BAY.DEU.EU - Internet: jreuter@yaina.de - www : http://yaina.de/jreuter -*/ - -/* ----------------------------------------------------------------------- */ - -#undef SCC_LDELAY /* slow it even a bit more down */ -#undef SCC_DONT_CHECK /* don't look if the SCCs you specified are available */ - -#define SCC_MAXCHIPS 4 /* number of max. supported chips */ -#define SCC_BUFSIZE 384 /* must not exceed 4096 */ -#undef SCC_DEBUG - -#define SCC_DEFAULT_CLOCK 4915200 - /* default pclock if nothing is specified */ - -/* ----------------------------------------------------------------------- */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include "z8530.h" - -static const char banner[] __initconst = KERN_INFO \ - "AX.25: Z8530 SCC driver version "VERSION".dl1bke\n"; - -static void t_dwait(struct timer_list *t); -static void t_txdelay(struct timer_list *t); -static void t_tail(struct timer_list *t); -static void t_busy(struct timer_list *); -static void t_maxkeyup(struct timer_list *); -static void t_idle(struct timer_list *t); -static void scc_tx_done(struct scc_channel *); -static void scc_start_tx_timer(struct scc_channel *, - void (*)(struct timer_list *), unsigned long); -static void scc_start_maxkeyup(struct scc_channel *); -static void scc_start_defer(struct scc_channel *); - -static void z8530_init(void); - -static void init_channel(struct scc_channel *scc); -static void scc_key_trx (struct scc_channel *scc, char tx); -static void scc_init_timer(struct scc_channel *scc); - -static int scc_net_alloc(const char *name, struct scc_channel *scc); -static void scc_net_setup(struct net_device *dev); -static int scc_net_open(struct net_device *dev); -static int scc_net_close(struct net_device *dev); -static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb); -static netdev_tx_t scc_net_tx(struct sk_buff *skb, - struct net_device *dev); -static int scc_net_siocdevprivate(struct net_device *dev, struct ifreq *ifr, - void __user *data, int cmd); -static int scc_net_set_mac_address(struct net_device *dev, void *addr); -static struct net_device_stats * scc_net_get_stats(struct net_device *dev); - -static unsigned char SCC_DriverName[] = "scc"; - -static struct irqflags { unsigned char used : 1; } Ivec[NR_IRQS]; - -static struct scc_channel SCC_Info[2 * SCC_MAXCHIPS]; /* information per channel */ - -static struct scc_ctrl { - io_port chan_A; - io_port chan_B; - int irq; -} SCC_ctrl[SCC_MAXCHIPS+1]; - -static unsigned char Driver_Initialized; -static int Nchips; -static io_port Vector_Latch; - - -/* ******************************************************************** */ -/* * Port Access Functions * */ -/* ******************************************************************** */ - -/* These provide interrupt save 2-step access to the Z8530 registers */ - -static DEFINE_SPINLOCK(iolock); /* Guards paired accesses */ - -static inline unsigned char InReg(io_port port, unsigned char reg) -{ - unsigned long flags; - unsigned char r; - - spin_lock_irqsave(&iolock, flags); -#ifdef SCC_LDELAY - Outb(port, reg); - udelay(SCC_LDELAY); - r=Inb(port); - udelay(SCC_LDELAY); -#else - Outb(port, reg); - r=Inb(port); -#endif - spin_unlock_irqrestore(&iolock, flags); - return r; -} - -static inline void OutReg(io_port port, unsigned char reg, unsigned char val) -{ - unsigned long flags; - - spin_lock_irqsave(&iolock, flags); -#ifdef SCC_LDELAY - Outb(port, reg); udelay(SCC_LDELAY); - Outb(port, val); udelay(SCC_LDELAY); -#else - Outb(port, reg); - Outb(port, val); -#endif - spin_unlock_irqrestore(&iolock, flags); -} - -static inline void wr(struct scc_channel *scc, unsigned char reg, - unsigned char val) -{ - OutReg(scc->ctrl, reg, (scc->wreg[reg] = val)); -} - -static inline void or(struct scc_channel *scc, unsigned char reg, unsigned char val) -{ - OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val)); -} - -static inline void cl(struct scc_channel *scc, unsigned char reg, unsigned char val) -{ - OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val)); -} - -/* ******************************************************************** */ -/* * Some useful macros * */ -/* ******************************************************************** */ - -static inline void scc_discard_buffers(struct scc_channel *scc) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - if (scc->tx_buff != NULL) - { - dev_kfree_skb_irq(scc->tx_buff); - scc->tx_buff = NULL; - } - - while (!skb_queue_empty(&scc->tx_queue)) - dev_kfree_skb_irq(skb_dequeue(&scc->tx_queue)); - - spin_unlock_irqrestore(&scc->lock, flags); -} - - - -/* ******************************************************************** */ -/* * Interrupt Service Routines * */ -/* ******************************************************************** */ - - -/* ----> subroutines for the interrupt handlers <---- */ - -static inline void scc_notify(struct scc_channel *scc, int event) -{ - struct sk_buff *skb; - char *bp; - - if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA) - return; - - skb = dev_alloc_skb(2); - if (skb != NULL) - { - bp = skb_put(skb, 2); - *bp++ = PARAM_HWEVENT; - *bp++ = event; - scc_net_rx(scc, skb); - } else - scc->stat.nospace++; -} - -static inline void flush_rx_FIFO(struct scc_channel *scc) -{ - int k; - - for (k=0; k<3; k++) - Inb(scc->data); - - if(scc->rx_buff != NULL) /* did we receive something? */ - { - scc->stat.rxerrs++; /* then count it as an error */ - dev_kfree_skb_irq(scc->rx_buff); - scc->rx_buff = NULL; - } -} - -static void start_hunt(struct scc_channel *scc) -{ - if ((scc->modem.clocksrc != CLK_EXTERNAL)) - OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ - or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ -} - -/* ----> four different interrupt handlers for Tx, Rx, changing of */ -/* DCD/CTS and Rx/Tx errors */ - -/* Transmitter interrupt handler */ -static inline void scc_txint(struct scc_channel *scc) -{ - struct sk_buff *skb; - - scc->stat.txints++; - skb = scc->tx_buff; - - /* send first octet */ - - if (skb == NULL) - { - skb = skb_dequeue(&scc->tx_queue); - scc->tx_buff = skb; - netif_wake_queue(scc->dev); - - if (skb == NULL) - { - scc_tx_done(scc); - Outb(scc->ctrl, RES_Tx_P); - return; - } - - if (skb->len == 0) /* Paranoia... */ - { - dev_kfree_skb_irq(skb); - scc->tx_buff = NULL; - scc_tx_done(scc); - Outb(scc->ctrl, RES_Tx_P); - return; - } - - scc->stat.tx_state = TXS_ACTIVE; - - OutReg(scc->ctrl, R0, RES_Tx_CRC); - /* reset CRC generator */ - or(scc,R10,ABUNDER); /* re-install underrun protection */ - Outb(scc->data,*skb->data); /* send byte */ - skb_pull(skb, 1); - - if (!scc->enhanced) /* reset EOM latch */ - Outb(scc->ctrl,RES_EOM_L); - return; - } - - /* End Of Frame... */ - - if (skb->len == 0) - { - Outb(scc->ctrl, RES_Tx_P); /* reset pending int */ - cl(scc, R10, ABUNDER); /* send CRC */ - dev_kfree_skb_irq(skb); - scc->tx_buff = NULL; - scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */ - return; - } - - /* send octet */ - - Outb(scc->data,*skb->data); - skb_pull(skb, 1); -} - - -/* External/Status interrupt handler */ -static inline void scc_exint(struct scc_channel *scc) -{ - unsigned char status,changes,chg_and_stat; - - scc->stat.exints++; - - status = InReg(scc->ctrl,R0); - changes = status ^ scc->status; - chg_and_stat = changes & status; - - /* ABORT: generated whenever DCD drops while receiving */ - - if (chg_and_stat & BRK_ABRT) /* Received an ABORT */ - flush_rx_FIFO(scc); - - /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */ - - if ((changes & SYNC_HUNT) && scc->kiss.softdcd) - { - if (status & SYNC_HUNT) - { - scc->dcd = 0; - flush_rx_FIFO(scc); - if ((scc->modem.clocksrc != CLK_EXTERNAL)) - OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ - } else { - scc->dcd = 1; - } - - scc_notify(scc, scc->dcd? HWEV_DCD_OFF:HWEV_DCD_ON); - } - - /* DCD: on = start to receive packet, off = ABORT condition */ - /* (a successfully received packet generates a special condition int) */ - - if((changes & DCD) && !scc->kiss.softdcd) /* DCD input changed state */ - { - if(status & DCD) /* DCD is now ON */ - { - start_hunt(scc); - scc->dcd = 1; - } else { /* DCD is now OFF */ - cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */ - flush_rx_FIFO(scc); - scc->dcd = 0; - } - - scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF); - } - -#ifdef notdef - /* CTS: use external TxDelay (what's that good for?!) - * Anyway: If we _could_ use it (BayCom USCC uses CTS for - * own purposes) we _should_ use the "autoenable" feature - * of the Z8530 and not this interrupt... - */ - - if (chg_and_stat & CTS) /* CTS is now ON */ - { - if (scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */ - scc_start_tx_timer(scc, t_txdelay, 0); - } -#endif - - if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM)) - { - scc->stat.tx_under++; /* oops, an underrun! count 'em */ - Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */ - - if (scc->tx_buff != NULL) - { - dev_kfree_skb_irq(scc->tx_buff); - scc->tx_buff = NULL; - } - - or(scc,R10,ABUNDER); - scc_start_tx_timer(scc, t_txdelay, 0); /* restart transmission */ - } - - scc->status = status; - Outb(scc->ctrl,RES_EXT_INT); -} - - -/* Receiver interrupt handler */ -static inline void scc_rxint(struct scc_channel *scc) -{ - struct sk_buff *skb; - - scc->stat.rxints++; - - if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - Inb(scc->data); /* discard char */ - or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ - return; - } - - skb = scc->rx_buff; - - if (skb == NULL) - { - skb = dev_alloc_skb(scc->stat.bufsize); - if (skb == NULL) - { - scc->dev_stat.rx_dropped++; - scc->stat.nospace++; - Inb(scc->data); - or(scc, R3, ENT_HM); - return; - } - - scc->rx_buff = skb; - skb_put_u8(skb, 0); /* KISS data */ - } - - if (skb->len >= scc->stat.bufsize) - { -#ifdef notdef - printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n"); -#endif - dev_kfree_skb_irq(skb); - scc->rx_buff = NULL; - Inb(scc->data); - or(scc, R3, ENT_HM); - return; - } - - skb_put_u8(skb, Inb(scc->data)); -} - - -/* Receive Special Condition interrupt handler */ -static inline void scc_spint(struct scc_channel *scc) -{ - unsigned char status; - struct sk_buff *skb; - - scc->stat.spints++; - - status = InReg(scc->ctrl,R1); /* read receiver status */ - - Inb(scc->data); /* throw away Rx byte */ - skb = scc->rx_buff; - - if(status & Rx_OVR) /* receiver overrun */ - { - scc->stat.rx_over++; /* count them */ - or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ - - if (skb != NULL) - dev_kfree_skb_irq(skb); - scc->rx_buff = skb = NULL; - } - - if(status & END_FR && skb != NULL) /* end of frame */ - { - /* CRC okay, frame ends on 8 bit boundary and received something ? */ - - if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0) - { - /* ignore last received byte (first of the CRC bytes) */ - skb_trim(skb, skb->len-1); - scc_net_rx(scc, skb); - scc->rx_buff = NULL; - scc->stat.rxframes++; - } else { /* a bad frame */ - dev_kfree_skb_irq(skb); - scc->rx_buff = NULL; - scc->stat.rxerrs++; - } - } - - Outb(scc->ctrl,ERR_RES); -} - - -/* ----> interrupt service routine for the Z8530 <---- */ - -static void scc_isr_dispatch(struct scc_channel *scc, int vector) -{ - spin_lock(&scc->lock); - switch (vector & VECTOR_MASK) - { - case TXINT: scc_txint(scc); break; - case EXINT: scc_exint(scc); break; - case RXINT: scc_rxint(scc); break; - case SPINT: scc_spint(scc); break; - } - spin_unlock(&scc->lock); -} - -/* If the card has a latch for the interrupt vector (like the PA0HZP card) - use it to get the number of the chip that generated the int. - If not: poll all defined chips. - */ - -#define SCC_IRQTIMEOUT 30000 - -static irqreturn_t scc_isr(int irq, void *dev_id) -{ - int chip_irq = (long) dev_id; - unsigned char vector; - struct scc_channel *scc; - struct scc_ctrl *ctrl; - int k; - - if (Vector_Latch) - { - for(k=0; k < SCC_IRQTIMEOUT; k++) - { - Outb(Vector_Latch, 0); /* Generate INTACK */ - - /* Read the vector */ - if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; - if (vector & 0x01) break; - - scc=&SCC_Info[vector >> 3 ^ 0x01]; - if (!scc->dev) break; - - scc_isr_dispatch(scc, vector); - - OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */ - } - - if (k == SCC_IRQTIMEOUT) - printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n"); - - return IRQ_HANDLED; - } - - /* Find the SCC generating the interrupt by polling all attached SCCs - * reading RR3A (the interrupt pending register) - */ - - ctrl = SCC_ctrl; - while (ctrl->chan_A) - { - if (ctrl->irq != chip_irq) - { - ctrl++; - continue; - } - - scc = NULL; - for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++) - { - vector=InReg(ctrl->chan_B,R2); /* Read the vector */ - if (vector & 0x01) break; - - scc = &SCC_Info[vector >> 3 ^ 0x01]; - if (!scc->dev) break; - - scc_isr_dispatch(scc, vector); - } - - if (k == SCC_IRQTIMEOUT) - { - printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n"); - break; - } - - /* This looks weird and it is. At least the BayCom USCC doesn't - * use the Interrupt Daisy Chain, thus we'll have to start - * all over again to be sure not to miss an interrupt from - * (any of) the other chip(s)... - * Honestly, the situation *is* braindamaged... - */ - - if (scc != NULL) - { - OutReg(scc->ctrl,R0,RES_H_IUS); - ctrl = SCC_ctrl; - } else - ctrl++; - } - return IRQ_HANDLED; -} - - - -/* ******************************************************************** */ -/* * Init Channel */ -/* ******************************************************************** */ - - -/* ----> set SCC channel speed <---- */ - -static inline void set_brg(struct scc_channel *scc, unsigned int tc) -{ - cl(scc,R14,BRENABL); /* disable baudrate generator */ - wr(scc,R12,tc & 255); /* brg rate LOW */ - wr(scc,R13,tc >> 8); /* brg rate HIGH */ - or(scc,R14,BRENABL); /* enable baudrate generator */ -} - -static inline void set_speed(struct scc_channel *scc) -{ - unsigned long flags; - spin_lock_irqsave(&scc->lock, flags); - - if (scc->modem.speed > 0) /* paranoia... */ - set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2); - - spin_unlock_irqrestore(&scc->lock, flags); -} - - -/* ----> initialize a SCC channel <---- */ - -static inline void init_brg(struct scc_channel *scc) -{ - wr(scc, R14, BRSRC); /* BRG source = PCLK */ - OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ - OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */ -} - -/* - * Initialization according to the Z8530 manual (SGS-Thomson's version): - * - * 1. Modes and constants - * - * WR9 11000000 chip reset - * WR4 XXXXXXXX Tx/Rx control, async or sync mode - * WR1 0XX00X00 select W/REQ (optional) - * WR2 XXXXXXXX program interrupt vector - * WR3 XXXXXXX0 select Rx control - * WR5 XXXX0XXX select Tx control - * WR6 XXXXXXXX sync character - * WR7 XXXXXXXX sync character - * WR9 000X0XXX select interrupt control - * WR10 XXXXXXXX miscellaneous control (optional) - * WR11 XXXXXXXX clock control - * WR12 XXXXXXXX time constant lower byte (optional) - * WR13 XXXXXXXX time constant upper byte (optional) - * WR14 XXXXXXX0 miscellaneous control - * WR14 XXXSSSSS commands (optional) - * - * 2. Enables - * - * WR14 000SSSS1 baud rate enable - * WR3 SSSSSSS1 Rx enable - * WR5 SSSS1SSS Tx enable - * WR0 10000000 reset Tx CRG (optional) - * WR1 XSS00S00 DMA enable (optional) - * - * 3. Interrupt status - * - * WR15 XXXXXXXX enable external/status - * WR0 00010000 reset external status - * WR0 00010000 reset external status twice - * WR1 SSSXXSXX enable Rx, Tx and Ext/status - * WR9 000SXSSS enable master interrupt enable - * - * 1 = set to one, 0 = reset to zero - * X = user defined, S = same as previous init - * - * - * Note that the implementation differs in some points from above scheme. - * - */ - -static void init_channel(struct scc_channel *scc) -{ - timer_delete(&scc->tx_t); - timer_delete(&scc->tx_wdog); - - disable_irq(scc->irq); - - wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */ - wr(scc,R1,0); /* no W/REQ operation */ - wr(scc,R3,Rx8|RxCRC_ENAB); /* RX 8 bits/char, CRC, disabled */ - wr(scc,R5,Tx8|DTR|TxCRC_ENAB); /* TX 8 bits/char, disabled, DTR */ - wr(scc,R6,0); /* SDLC address zero (not used) */ - wr(scc,R7,FLAG); /* SDLC flag value */ - wr(scc,R9,VIS); /* vector includes status */ - wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */ - wr(scc,R14, 0); - - -/* set clock sources: - - CLK_DPLL: normal halfduplex operation - - RxClk: use DPLL - TxClk: use DPLL - TRxC mode DPLL output - - CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem) - - BayCom: others: - - TxClk = pin RTxC TxClk = pin TRxC - RxClk = pin TRxC RxClk = pin RTxC - - - CLK_DIVIDER: - RxClk = use DPLL - TxClk = pin RTxC - - BayCom: others: - pin TRxC = DPLL pin TRxC = BRG - (RxClk * 1) (RxClk * 32) -*/ - - - switch(scc->modem.clocksrc) - { - case CLK_DPLL: - wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); - init_brg(scc); - break; - - case CLK_DIVIDER: - wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI); - init_brg(scc); - break; - - case CLK_EXTERNAL: - wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP); - OutReg(scc->ctrl, R14, DISDPLL); - break; - - } - - set_speed(scc); /* set baudrate */ - - if(scc->enhanced) - { - or(scc,R15,SHDLCE|FIFOE); /* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */ - wr(scc,R7,AUTOEOM); - } - - if(scc->kiss.softdcd || (InReg(scc->ctrl,R0) & DCD)) - /* DCD is now ON */ - { - start_hunt(scc); - } - - /* enable ABORT, DCD & SYNC/HUNT interrupts */ - - wr(scc,R15, BRKIE|TxUIE|(scc->kiss.softdcd? SYNCIE:DCDIE)); - - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */ - - or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */ - - scc->status = InReg(scc->ctrl,R0); /* read initial status */ - - or(scc,R9,MIE); /* master interrupt enable */ - - scc_init_timer(scc); - - enable_irq(scc->irq); -} - - - - -/* ******************************************************************** */ -/* * SCC timer functions * */ -/* ******************************************************************** */ - - -/* ----> scc_key_trx sets the time constant for the baudrate - generator and keys the transmitter <---- */ - -static void scc_key_trx(struct scc_channel *scc, char tx) -{ - unsigned int time_const; - - if (scc->brand & PRIMUS) - Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0)); - - if (scc->modem.speed < 300) - scc->modem.speed = 1200; - - time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2; - - disable_irq(scc->irq); - - if (tx) - { - or(scc, R1, TxINT_ENAB); /* t_maxkeyup may have reset these */ - or(scc, R15, TxUIE); - } - - if (scc->modem.clocksrc == CLK_DPLL) - { /* force simplex operation */ - if (tx) - { -#ifdef CONFIG_SCC_TRXECHO - cl(scc, R3, RxENABLE|ENT_HM); /* switch off receiver */ - cl(scc, R15, DCDIE|SYNCIE); /* No DCD changes, please */ -#endif - set_brg(scc, time_const); /* reprogram baudrate generator */ - - /* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */ - wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR); - - /* By popular demand: tx_inhibit */ - if (scc->kiss.tx_inhibit) - { - or(scc,R5, TxENAB); - scc->wreg[R5] |= RTS; - } else { - or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */ - } - } else { - cl(scc,R5,RTS|TxENAB); - - set_brg(scc, time_const); /* reprogram baudrate generator */ - - /* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */ - wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); - -#ifndef CONFIG_SCC_TRXECHO - if (scc->kiss.softdcd) -#endif - { - or(scc,R15, scc->kiss.softdcd? SYNCIE:DCDIE); - start_hunt(scc); - } - } - } else { - if (tx) - { -#ifdef CONFIG_SCC_TRXECHO - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - cl(scc, R3, RxENABLE); - cl(scc, R15, DCDIE|SYNCIE); - } -#endif - - if (scc->kiss.tx_inhibit) - { - or(scc,R5, TxENAB); - scc->wreg[R5] |= RTS; - } else { - or(scc,R5,RTS|TxENAB); /* enable tx */ - } - } else { - cl(scc,R5,RTS|TxENAB); /* disable tx */ - - if ((scc->kiss.fulldup == KISS_DUPLEX_HALF) && -#ifndef CONFIG_SCC_TRXECHO - scc->kiss.softdcd) -#else - 1) -#endif - { - or(scc, R15, scc->kiss.softdcd? SYNCIE:DCDIE); - start_hunt(scc); - } - } - } - - enable_irq(scc->irq); -} - - -/* ----> SCC timer interrupt handler and friends. <---- */ - -static void __scc_start_tx_timer(struct scc_channel *scc, - void (*handler)(struct timer_list *t), - unsigned long when) -{ - timer_delete(&scc->tx_t); - - if (when == 0) - { - handler(&scc->tx_t); - } else - if (when != TIMER_OFF) - { - scc->tx_t.function = handler; - scc->tx_t.expires = jiffies + (when*HZ)/100; - add_timer(&scc->tx_t); - } -} - -static void scc_start_tx_timer(struct scc_channel *scc, - void (*handler)(struct timer_list *t), - unsigned long when) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - __scc_start_tx_timer(scc, handler, when); - spin_unlock_irqrestore(&scc->lock, flags); -} - -static void scc_start_defer(struct scc_channel *scc) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - timer_delete(&scc->tx_wdog); - - if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF) - { - scc->tx_wdog.function = t_busy; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer; - add_timer(&scc->tx_wdog); - } - spin_unlock_irqrestore(&scc->lock, flags); -} - -static void scc_start_maxkeyup(struct scc_channel *scc) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - timer_delete(&scc->tx_wdog); - - if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF) - { - scc->tx_wdog.function = t_maxkeyup; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup; - add_timer(&scc->tx_wdog); - } - spin_unlock_irqrestore(&scc->lock, flags); -} - -/* - * This is called from scc_txint() when there are no more frames to send. - * Not exactly a timer function, but it is a close friend of the family... - */ - -static void scc_tx_done(struct scc_channel *scc) -{ - /* - * trx remains keyed in fulldup mode 2 until t_idle expires. - */ - - switch (scc->kiss.fulldup) - { - case KISS_DUPLEX_LINK: - scc->stat.tx_state = TXS_IDLE2; - if (scc->kiss.idletime != TIMER_OFF) - scc_start_tx_timer(scc, t_idle, - scc->kiss.idletime*100); - break; - case KISS_DUPLEX_OPTIMA: - scc_notify(scc, HWEV_ALL_SENT); - break; - default: - scc->stat.tx_state = TXS_BUSY; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); - } - - netif_wake_queue(scc->dev); -} - - -static unsigned char Rand = 17; - -static inline int is_grouped(struct scc_channel *scc) -{ - int k; - struct scc_channel *scc2; - unsigned char grp1, grp2; - - grp1 = scc->kiss.group; - - for (k = 0; k < (Nchips * 2); k++) - { - scc2 = &SCC_Info[k]; - grp2 = scc2->kiss.group; - - if (scc2 == scc || !(scc2->dev && grp2)) - continue; - - if ((grp1 & 0x3f) == (grp2 & 0x3f)) - { - if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) ) - return 1; - - if ( (grp1 & RXGROUP) && scc2->dcd ) - return 1; - } - } - return 0; -} - -/* DWAIT and SLOTTIME expired - * - * fulldup == 0: DCD is active or Rand > P-persistence: start t_busy timer - * else key trx and start txdelay - * fulldup == 1: key trx and start txdelay - * fulldup == 2: mintime expired, reset status or key trx and start txdelay - */ - -static void t_dwait(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_t); - - if (scc->stat.tx_state == TXS_WAIT) /* maxkeyup or idle timeout */ - { - if (skb_queue_empty(&scc->tx_queue)) { /* nothing to send */ - scc->stat.tx_state = TXS_IDLE; - netif_wake_queue(scc->dev); /* t_maxkeyup locked it. */ - return; - } - - scc->stat.tx_state = TXS_BUSY; - } - - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - Rand = Rand * 17 + 31; - - if (scc->dcd || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) ) - { - scc_start_defer(scc); - scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime); - return ; - } - } - - if ( !(scc->wreg[R5] & RTS) ) - { - scc_key_trx(scc, TX_ON); - scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); - } else { - scc_start_tx_timer(scc, t_txdelay, 0); - } -} - - -/* TXDELAY expired - * - * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog. - */ - -static void t_txdelay(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_t); - - scc_start_maxkeyup(scc); - - if (scc->tx_buff == NULL) - { - disable_irq(scc->irq); - scc_txint(scc); - enable_irq(scc->irq); - } -} - - -/* TAILTIME expired - * - * switch off transmitter. If we were stopped by Maxkeyup restart - * transmission after 'mintime' seconds - */ - -static void t_tail(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_t); - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - timer_delete(&scc->tx_wdog); - scc_key_trx(scc, TX_OFF); - spin_unlock_irqrestore(&scc->lock, flags); - - if (scc->stat.tx_state == TXS_TIMEOUT) /* we had a timeout? */ - { - scc->stat.tx_state = TXS_WAIT; - scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); - return; - } - - scc->stat.tx_state = TXS_IDLE; - netif_wake_queue(scc->dev); -} - - -/* BUSY timeout - * - * throw away send buffers if DCD remains active too long. - */ - -static void t_busy(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_wdog); - - timer_delete(&scc->tx_t); - netif_stop_queue(scc->dev); /* don't pile on the wabbit! */ - - scc_discard_buffers(scc); - scc->stat.txerrs++; - scc->stat.tx_state = TXS_IDLE; - - netif_wake_queue(scc->dev); -} - -/* MAXKEYUP timeout - * - * this is our watchdog. - */ - -static void t_maxkeyup(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_wdog); - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - /* - * let things settle down before we start to - * accept new data. - */ - - netif_stop_queue(scc->dev); - scc_discard_buffers(scc); - - timer_delete(&scc->tx_t); - - cl(scc, R1, TxINT_ENAB); /* force an ABORT, but don't */ - cl(scc, R15, TxUIE); /* count it. */ - OutReg(scc->ctrl, R0, RES_Tx_P); - - spin_unlock_irqrestore(&scc->lock, flags); - - scc->stat.txerrs++; - scc->stat.tx_state = TXS_TIMEOUT; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); -} - -/* IDLE timeout - * - * in fulldup mode 2 it keys down the transmitter after 'idle' seconds - * of inactivity. We will not restart transmission before 'mintime' - * expires. - */ - -static void t_idle(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_t); - - timer_delete(&scc->tx_wdog); - - scc_key_trx(scc, TX_OFF); - if(scc->kiss.mintime) - scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); - scc->stat.tx_state = TXS_WAIT; -} - -static void scc_init_timer(struct scc_channel *scc) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - scc->stat.tx_state = TXS_IDLE; - spin_unlock_irqrestore(&scc->lock, flags); -} - - -/* ******************************************************************** */ -/* * Set/get L1 parameters * */ -/* ******************************************************************** */ - - -/* - * this will set the "hardware" parameters through KISS commands or ioctl() - */ - -#define CAST(x) (unsigned long)(x) - -static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg) -{ - switch (cmd) - { - case PARAM_TXDELAY: scc->kiss.txdelay=arg; break; - case PARAM_PERSIST: scc->kiss.persist=arg; break; - case PARAM_SLOTTIME: scc->kiss.slottime=arg; break; - case PARAM_TXTAIL: scc->kiss.tailtime=arg; break; - case PARAM_FULLDUP: scc->kiss.fulldup=arg; break; - case PARAM_DTR: break; /* does someone need this? */ - case PARAM_GROUP: scc->kiss.group=arg; break; - case PARAM_IDLE: scc->kiss.idletime=arg; break; - case PARAM_MIN: scc->kiss.mintime=arg; break; - case PARAM_MAXKEY: scc->kiss.maxkeyup=arg; break; - case PARAM_WAIT: scc->kiss.waittime=arg; break; - case PARAM_MAXDEFER: scc->kiss.maxdefer=arg; break; - case PARAM_TX: scc->kiss.tx_inhibit=arg; break; - - case PARAM_SOFTDCD: - scc->kiss.softdcd=arg; - if (arg) - { - or(scc, R15, SYNCIE); - cl(scc, R15, DCDIE); - start_hunt(scc); - } else { - or(scc, R15, DCDIE); - cl(scc, R15, SYNCIE); - } - break; - - case PARAM_SPEED: - if (arg < 256) - scc->modem.speed=arg*100; - else - scc->modem.speed=arg; - - if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */ - set_speed(scc); - break; - - case PARAM_RTS: - if ( !(scc->wreg[R5] & RTS) ) - { - if (arg != TX_OFF) { - scc_key_trx(scc, TX_ON); - scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); - } - } else { - if (arg == TX_OFF) - { - scc->stat.tx_state = TXS_BUSY; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); - } - } - break; - - case PARAM_HWEVENT: - scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF); - break; - - default: return -EINVAL; - } - - return 0; -} - - - -static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd) -{ - switch (cmd) - { - case PARAM_TXDELAY: return CAST(scc->kiss.txdelay); - case PARAM_PERSIST: return CAST(scc->kiss.persist); - case PARAM_SLOTTIME: return CAST(scc->kiss.slottime); - case PARAM_TXTAIL: return CAST(scc->kiss.tailtime); - case PARAM_FULLDUP: return CAST(scc->kiss.fulldup); - case PARAM_SOFTDCD: return CAST(scc->kiss.softdcd); - case PARAM_DTR: return CAST((scc->wreg[R5] & DTR)? 1:0); - case PARAM_RTS: return CAST((scc->wreg[R5] & RTS)? 1:0); - case PARAM_SPEED: return CAST(scc->modem.speed); - case PARAM_GROUP: return CAST(scc->kiss.group); - case PARAM_IDLE: return CAST(scc->kiss.idletime); - case PARAM_MIN: return CAST(scc->kiss.mintime); - case PARAM_MAXKEY: return CAST(scc->kiss.maxkeyup); - case PARAM_WAIT: return CAST(scc->kiss.waittime); - case PARAM_MAXDEFER: return CAST(scc->kiss.maxdefer); - case PARAM_TX: return CAST(scc->kiss.tx_inhibit); - default: return NO_SUCH_PARAM; - } - -} - -#undef CAST - -/* ******************************************************************* */ -/* * Send calibration pattern * */ -/* ******************************************************************* */ - -static void scc_stop_calibrate(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_wdog); - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - timer_delete(&scc->tx_wdog); - scc_key_trx(scc, TX_OFF); - wr(scc, R6, 0); - wr(scc, R7, FLAG); - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); - - netif_wake_queue(scc->dev); - spin_unlock_irqrestore(&scc->lock, flags); -} - - -static void -scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - netif_stop_queue(scc->dev); - scc_discard_buffers(scc); - - timer_delete(&scc->tx_wdog); - - scc->tx_wdog.function = scc_stop_calibrate; - scc->tx_wdog.expires = jiffies + HZ*duration; - add_timer(&scc->tx_wdog); - - /* This doesn't seem to work. Why not? */ - wr(scc, R6, 0); - wr(scc, R7, pattern); - - /* - * Don't know if this works. - * Damn, where is my Z8530 programming manual...? - */ - - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); - - scc_key_trx(scc, TX_ON); - spin_unlock_irqrestore(&scc->lock, flags); -} - -/* ******************************************************************* */ -/* * Init channel structures, special HW, etc... * */ -/* ******************************************************************* */ - -/* - * Reset the Z8530s and setup special hardware - */ - -static void z8530_init(void) -{ - const unsigned int nr_irqs = irq_get_nr_irqs(); - struct scc_channel *scc; - int chip, k; - unsigned long flags; - char *flag; - - - printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2); - - flag=" "; - for (k = 0; k < nr_irqs; k++) - if (Ivec[k].used) - { - printk("%s%d", flag, k); - flag=","; - } - printk("\n"); - - - /* reset and pre-init all chips in the system */ - for (chip = 0; chip < Nchips; chip++) - { - scc=&SCC_Info[2*chip]; - if (!scc->ctrl) continue; - - /* Special SCC cards */ - - if(scc->brand & EAGLE) /* this is an EAGLE card */ - Outb(scc->special,0x08); /* enable interrupt on the board */ - - if(scc->brand & (PC100 | PRIMUS)) /* this is a PC100/PRIMUS card */ - Outb(scc->special,scc->option); /* set the MODEM mode (0x22) */ - - - /* Reset and pre-init Z8530 */ - - spin_lock_irqsave(&scc->lock, flags); - - Outb(scc->ctrl, 0); - OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */ - udelay(100); /* give it 'a bit' more time than required */ - wr(scc, R2, chip*16); /* interrupt vector */ - wr(scc, R9, VIS); /* vector includes status */ - spin_unlock_irqrestore(&scc->lock, flags); - } - - - Driver_Initialized = 1; -} - -/* - * Allocate device structure, err, instance, and register driver - */ - -static int scc_net_alloc(const char *name, struct scc_channel *scc) -{ - int err; - struct net_device *dev; - - dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, scc_net_setup); - if (!dev) - return -ENOMEM; - - dev->ml_priv = scc; - scc->dev = dev; - spin_lock_init(&scc->lock); - timer_setup(&scc->tx_t, NULL, 0); - timer_setup(&scc->tx_wdog, NULL, 0); - - err = register_netdevice(dev); - if (err) { - printk(KERN_ERR "%s: can't register network device (%d)\n", - name, err); - free_netdev(dev); - scc->dev = NULL; - return err; - } - - return 0; -} - - - -/* ******************************************************************** */ -/* * Network driver methods * */ -/* ******************************************************************** */ - -static const struct net_device_ops scc_netdev_ops = { - .ndo_open = scc_net_open, - .ndo_stop = scc_net_close, - .ndo_start_xmit = scc_net_tx, - .ndo_set_mac_address = scc_net_set_mac_address, - .ndo_get_stats = scc_net_get_stats, - .ndo_siocdevprivate = scc_net_siocdevprivate, -}; - -/* ----> Initialize device <----- */ - -static void scc_net_setup(struct net_device *dev) -{ - dev->tx_queue_len = 16; /* should be enough... */ - - dev->netdev_ops = &scc_netdev_ops; - dev->header_ops = &ax25_header_ops; - - dev->flags = 0; - - dev->type = ARPHRD_AX25; - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; - dev->addr_len = AX25_ADDR_LEN; - - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); -} - -/* ----> open network device <---- */ - -static int scc_net_open(struct net_device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - - if (!scc->init) - return -EINVAL; - - scc->tx_buff = NULL; - skb_queue_head_init(&scc->tx_queue); - - init_channel(scc); - - netif_start_queue(dev); - return 0; -} - -/* ----> close network device <---- */ - -static int scc_net_close(struct net_device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - unsigned long flags; - - netif_stop_queue(dev); - - spin_lock_irqsave(&scc->lock, flags); - Outb(scc->ctrl,0); /* Make sure pointer is written */ - wr(scc,R1,0); /* disable interrupts */ - wr(scc,R3,0); - spin_unlock_irqrestore(&scc->lock, flags); - - timer_delete_sync(&scc->tx_t); - timer_delete_sync(&scc->tx_wdog); - - scc_discard_buffers(scc); - - return 0; -} - -/* ----> receive frame, called from scc_rxint() <---- */ - -static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) -{ - if (skb->len == 0) { - dev_kfree_skb_irq(skb); - return; - } - - scc->dev_stat.rx_packets++; - scc->dev_stat.rx_bytes += skb->len; - - skb->protocol = ax25_type_trans(skb, scc->dev); - - netif_rx(skb); -} - -/* ----> transmit frame <---- */ - -static netdev_tx_t scc_net_tx(struct sk_buff *skb, struct net_device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - unsigned long flags; - char kisscmd; - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - if (skb->len > scc->stat.bufsize || skb->len < 2) { - scc->dev_stat.tx_dropped++; /* bogus frame */ - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - scc->dev_stat.tx_packets++; - scc->dev_stat.tx_bytes += skb->len; - scc->stat.txframes++; - - kisscmd = *skb->data & 0x1f; - skb_pull(skb, 1); - - if (kisscmd) { - scc_set_param(scc, kisscmd, *skb->data); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - spin_lock_irqsave(&scc->lock, flags); - - if (skb_queue_len(&scc->tx_queue) > scc->dev->tx_queue_len) { - struct sk_buff *skb_del; - skb_del = skb_dequeue(&scc->tx_queue); - dev_kfree_skb_irq(skb_del); - } - skb_queue_tail(&scc->tx_queue, skb); - netif_trans_update(dev); - - - /* - * Start transmission if the trx state is idle or - * t_idle hasn't expired yet. Use dwait/persistence/slottime - * algorithm for normal halfduplex operation. - */ - - if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) { - scc->stat.tx_state = TXS_BUSY; - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - __scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime); - else - __scc_start_tx_timer(scc, t_dwait, 0); - } - spin_unlock_irqrestore(&scc->lock, flags); - return NETDEV_TX_OK; -} - -/* ----> ioctl functions <---- */ - -/* - * SIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg - * SIOCSCCINI - initialize driver arg: --- - * SIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg - * SIOCSCCSMEM - set memory arg: (struct scc_mem_config *) arg - * SIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg - * SIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg - * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg - * SIOCSCCCAL - send calib. pattern arg: (struct scc_calibrate *) arg - */ - -static int scc_net_siocdevprivate(struct net_device *dev, - struct ifreq *ifr, void __user *arg, int cmd) -{ - struct scc_kiss_cmd kiss_cmd; - struct scc_mem_config memcfg; - struct scc_hw_config hwcfg; - struct scc_calibrate cal; - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - int chan; - unsigned char device_name[IFNAMSIZ]; - - if (!Driver_Initialized) - { - if (cmd == SIOCSCCCFG) - { - int found = 1; - - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (in_compat_syscall()) - return -EOPNOTSUPP; - - if (!arg) return -EFAULT; - - if (Nchips >= SCC_MAXCHIPS) - return -EINVAL; - - if (copy_from_user(&hwcfg, arg, sizeof(hwcfg))) - return -EFAULT; - - if (hwcfg.irq == 2) hwcfg.irq = 9; - - if (hwcfg.irq < 0 || hwcfg.irq >= irq_get_nr_irqs()) - return -EINVAL; - - if (!Ivec[hwcfg.irq].used && hwcfg.irq) - { - if (request_irq(hwcfg.irq, scc_isr, - 0, "AX.25 SCC", - (void *)(long) hwcfg.irq)) - printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); - else - Ivec[hwcfg.irq].used = 1; - } - - if (hwcfg.vector_latch && !Vector_Latch) { - if (!request_region(hwcfg.vector_latch, 1, "scc vector latch")) - printk(KERN_WARNING "z8530drv: warning, cannot reserve vector latch port 0x%lx\n, disabled.", hwcfg.vector_latch); - else - Vector_Latch = hwcfg.vector_latch; - } - - if (hwcfg.clock == 0) - hwcfg.clock = SCC_DEFAULT_CLOCK; - -#ifndef SCC_DONT_CHECK - - if(request_region(hwcfg.ctrl_a, 1, "scc-probe")) - { - disable_irq(hwcfg.irq); - Outb(hwcfg.ctrl_a, 0); - OutReg(hwcfg.ctrl_a, R9, FHWRES); - udelay(100); - OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */ - udelay(5); - - if (InReg(hwcfg.ctrl_a,R13) != 0x55) - found = 0; - enable_irq(hwcfg.irq); - release_region(hwcfg.ctrl_a, 1); - } - else - found = 0; -#endif - - if (found) - { - SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a; - SCC_Info[2*Nchips ].data = hwcfg.data_a; - SCC_Info[2*Nchips ].irq = hwcfg.irq; - SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b; - SCC_Info[2*Nchips+1].data = hwcfg.data_b; - SCC_Info[2*Nchips+1].irq = hwcfg.irq; - - SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a; - SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b; - SCC_ctrl[Nchips].irq = hwcfg.irq; - } - - - for (chan = 0; chan < 2; chan++) - { - sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan); - - SCC_Info[2*Nchips+chan].special = hwcfg.special; - SCC_Info[2*Nchips+chan].clock = hwcfg.clock; - SCC_Info[2*Nchips+chan].brand = hwcfg.brand; - SCC_Info[2*Nchips+chan].option = hwcfg.option; - SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc; - -#ifdef SCC_DONT_CHECK - printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n", - device_name, - SCC_Info[2*Nchips+chan].data, - SCC_Info[2*Nchips+chan].ctrl); - -#else - printk(KERN_INFO "%s: data port = 0x%3.3lx control port = 0x%3.3lx -- %s\n", - device_name, - chan? hwcfg.data_b : hwcfg.data_a, - chan? hwcfg.ctrl_b : hwcfg.ctrl_a, - found? "found" : "missing"); -#endif - - if (found) - { - request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl"); - request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data"); - if (Nchips+chan != 0 && - scc_net_alloc(device_name, - &SCC_Info[2*Nchips+chan])) - return -EINVAL; - } - } - - if (found) Nchips++; - - return 0; - } - - if (cmd == SIOCSCCINI) - { - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - - if (Nchips == 0) - return -EINVAL; - - z8530_init(); - return 0; - } - - return -EINVAL; /* confuse the user */ - } - - if (!scc->init) - { - if (cmd == SIOCSCCCHANINI) - { - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (!arg) return -EINVAL; - - scc->stat.bufsize = SCC_BUFSIZE; - - if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem))) - return -EINVAL; - - /* default KISS Params */ - - if (scc->modem.speed < 4800) - { - scc->kiss.txdelay = 36; /* 360 ms */ - scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 16; /* 160 ms */ - scc->kiss.tailtime = 4; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50; /* 500 ms */ - scc->kiss.maxkeyup = 10; /* 10 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.softdcd = 0; /* hardware dcd */ - } else { - scc->kiss.txdelay = 10; /* 100 ms */ - scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 8; /* 160 ms */ - scc->kiss.tailtime = 1; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50; /* 500 ms */ - scc->kiss.maxkeyup = 7; /* 7 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.softdcd = 0; /* hardware dcd */ - } - - scc->tx_buff = NULL; - skb_queue_head_init(&scc->tx_queue); - scc->init = 1; - - return 0; - } - - return -EINVAL; - } - - switch(cmd) - { - case SIOCSCCRESERVED: - return -ENOIOCTLCMD; - - case SIOCSCCSMEM: - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg))) - return -EINVAL; - if (memcfg.bufsize < 16) - return -EINVAL; - scc->stat.bufsize = memcfg.bufsize; - return 0; - - case SIOCSCCGSTAT: - if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat))) - return -EINVAL; - return 0; - - case SIOCSCCGKISS: - if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) - return -EINVAL; - kiss_cmd.param = scc_get_param(scc, kiss_cmd.command); - if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd))) - return -EINVAL; - return 0; - - case SIOCSCCSKISS: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) - return -EINVAL; - return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param); - - case SIOCSCCCAL: - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0) - return -EINVAL; - - scc_start_calibrate(scc, cal.time, cal.pattern); - return 0; - - default: - return -ENOIOCTLCMD; - - } - - return -EINVAL; -} - -/* ----> set interface callsign <---- */ - -static int scc_net_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *) addr; - dev_addr_set(dev, sa->sa_data); - return 0; -} - -/* ----> get statistics <---- */ - -static struct net_device_stats *scc_net_get_stats(struct net_device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - - scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over; - scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under; - scc->dev_stat.rx_fifo_errors = scc->stat.rx_over; - scc->dev_stat.tx_fifo_errors = scc->stat.tx_under; - - return &scc->dev_stat; -} - -/* ******************************************************************** */ -/* * dump statistics to /proc/net/z8530drv * */ -/* ******************************************************************** */ - -#ifdef CONFIG_PROC_FS - -static inline struct scc_channel *scc_net_seq_idx(loff_t pos) -{ - int k; - - for (k = 0; k < Nchips*2; ++k) { - if (!SCC_Info[k].init) - continue; - if (pos-- == 0) - return &SCC_Info[k]; - } - return NULL; -} - -static void *scc_net_seq_start(struct seq_file *seq, loff_t *pos) -{ - return *pos ? scc_net_seq_idx(*pos - 1) : SEQ_START_TOKEN; - -} - -static void *scc_net_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - unsigned k; - struct scc_channel *scc = v; - ++*pos; - - for (k = (v == SEQ_START_TOKEN) ? 0 : (scc - SCC_Info)+1; - k < Nchips*2; ++k) { - if (SCC_Info[k].init) - return &SCC_Info[k]; - } - return NULL; -} - -static void scc_net_seq_stop(struct seq_file *seq, void *v) -{ -} - -static int scc_net_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) { - seq_puts(seq, "z8530drv-"VERSION"\n"); - } else if (!Driver_Initialized) { - seq_puts(seq, "not initialized\n"); - } else if (!Nchips) { - seq_puts(seq, "chips missing\n"); - } else { - const struct scc_channel *scc = v; - const struct scc_stat *stat = &scc->stat; - const struct scc_kiss *kiss = &scc->kiss; - - - /* dev data ctrl irq clock brand enh vector special option - * baud nrz clocksrc softdcd bufsize - * rxints txints exints spints - * rcvd rxerrs over / xmit txerrs under / nospace bufsize - * txd pers slot tail ful wait min maxk idl defr txof grp - * W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## - * R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ## - */ - - seq_printf(seq, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n", - scc->dev->name, - scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand, - scc->enhanced, Vector_Latch, scc->special, - scc->option); - seq_printf(seq, "\t%lu %d %d %d %d\n", - scc->modem.speed, scc->modem.nrz, - scc->modem.clocksrc, kiss->softdcd, - stat->bufsize); - seq_printf(seq, "\t%lu %lu %lu %lu\n", - stat->rxints, stat->txints, stat->exints, stat->spints); - seq_printf(seq, "\t%lu %lu %d / %lu %lu %d / %d %d\n", - stat->rxframes, stat->rxerrs, stat->rx_over, - stat->txframes, stat->txerrs, stat->tx_under, - stat->nospace, stat->tx_state); - -#define K(x) kiss->x - seq_printf(seq, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n", - K(txdelay), K(persist), K(slottime), K(tailtime), - K(fulldup), K(waittime), K(mintime), K(maxkeyup), - K(idletime), K(maxdefer), K(tx_inhibit), K(group)); -#undef K -#ifdef SCC_DEBUG - { - int reg; - - seq_printf(seq, "\tW "); - for (reg = 0; reg < 16; reg++) - seq_printf(seq, "%2.2x ", scc->wreg[reg]); - seq_printf(seq, "\n"); - - seq_printf(seq, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1)); - for (reg = 3; reg < 8; reg++) - seq_printf(seq, "%2.2x ", InReg(scc->ctrl, reg)); - seq_printf(seq, "XX "); - for (reg = 9; reg < 16; reg++) - seq_printf(seq, "%2.2x ", InReg(scc->ctrl, reg)); - seq_printf(seq, "\n"); - } -#endif - seq_putc(seq, '\n'); - } - - return 0; -} - -static const struct seq_operations scc_net_seq_ops = { - .start = scc_net_seq_start, - .next = scc_net_seq_next, - .stop = scc_net_seq_stop, - .show = scc_net_seq_show, -}; -#endif /* CONFIG_PROC_FS */ - - -/* ******************************************************************** */ -/* * Init SCC driver * */ -/* ******************************************************************** */ - -static int __init scc_init_driver (void) -{ - char devname[IFNAMSIZ]; - - printk(banner); - - sprintf(devname,"%s0", SCC_DriverName); - - rtnl_lock(); - if (scc_net_alloc(devname, SCC_Info)) { - rtnl_unlock(); - printk(KERN_ERR "z8530drv: cannot initialize module\n"); - return -EIO; - } - rtnl_unlock(); - - proc_create_seq("z8530drv", 0, init_net.proc_net, &scc_net_seq_ops); - - return 0; -} - -static void __exit scc_cleanup_driver(void) -{ - const unsigned int nr_irqs = irq_get_nr_irqs(); - io_port ctrl; - int k; - struct scc_channel *scc; - struct net_device *dev; - - if (Nchips == 0 && (dev = SCC_Info[0].dev)) - { - unregister_netdev(dev); - free_netdev(dev); - } - - /* Guard against chip prattle */ - local_irq_disable(); - - for (k = 0; k < Nchips; k++) - if ( (ctrl = SCC_ctrl[k].chan_A) ) - { - Outb(ctrl, 0); - OutReg(ctrl,R9,FHWRES); /* force hardware reset */ - udelay(50); - } - - /* To unload the port must be closed so no real IRQ pending */ - for (k = 0; k < nr_irqs ; k++) - if (Ivec[k].used) free_irq(k, NULL); - - local_irq_enable(); - - /* Now clean up */ - for (k = 0; k < Nchips*2; k++) - { - scc = &SCC_Info[k]; - if (scc->ctrl) - { - release_region(scc->ctrl, 1); - release_region(scc->data, 1); - } - if (scc->dev) - { - unregister_netdev(scc->dev); - free_netdev(scc->dev); - } - } - - - if (Vector_Latch) - release_region(Vector_Latch, 1); - - remove_proc_entry("z8530drv", init_net.proc_net); -} - -MODULE_AUTHOR("Joerg Reuter "); -MODULE_DESCRIPTION("AX.25 Device Driver for Z8530 based HDLC cards"); -MODULE_LICENSE("GPL"); -module_init(scc_init_driver); -module_exit(scc_cleanup_driver); diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c deleted file mode 100644 index 4106f0301ab4..000000000000 --- a/drivers/net/hamradio/yam.c +++ /dev/null @@ -1,1191 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * yam.c -- YAM radio modem driver. - * - * Copyright (C) 1998 Frederic Rible F1OAT (frible@teaser.fr) - * Adapted from baycom.c driver written by Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * History: - * 0.0 F1OAT 06.06.98 Begin of work with baycom.c source code V 0.3 - * 0.1 F1OAT 07.06.98 Add timer polling routine for channel arbitration - * 0.2 F6FBB 08.06.98 Added delay after FPGA programming - * 0.3 F6FBB 29.07.98 Delayed PTT implementation for dupmode=2 - * 0.4 F6FBB 30.07.98 Added TxTail, Slottime and Persistence - * 0.5 F6FBB 01.08.98 Shared IRQs, /proc/net and network statistics - * 0.6 F6FBB 25.08.98 Added 1200Bds format - * 0.7 F6FBB 12.09.98 Added to the kernel configuration - * 0.8 F6FBB 14.10.98 Fixed slottime/persistence timing bug - * OK1ZIA 2.09.01 Fixed "kfree_skb on hard IRQ" - * using dev_kfree_skb_any(). (important in 2.4 kernel) - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -/* --------------------------------------------------------------------- */ - -static const char yam_drvname[] = "yam"; -static const char yam_drvinfo[] __initconst = KERN_INFO \ - "YAM driver version 0.8 by F1OAT/F6FBB\n"; - -/* --------------------------------------------------------------------- */ - -#define FIRMWARE_9600 "yam/9600.bin" -#define FIRMWARE_1200 "yam/1200.bin" - -#define YAM_9600 1 -#define YAM_1200 2 - -#define NR_PORTS 4 -#define YAM_MAGIC 0xF10A7654 - -/* Transmitter states */ - -#define TX_OFF 0 -#define TX_HEAD 1 -#define TX_DATA 2 -#define TX_CRC1 3 -#define TX_CRC2 4 -#define TX_TAIL 5 - -#define YAM_MAX_FRAME 1024 - -#define DEFAULT_BITRATE 9600 /* bps */ -#define DEFAULT_HOLDD 10 /* sec */ -#define DEFAULT_TXD 300 /* ms */ -#define DEFAULT_TXTAIL 10 /* ms */ -#define DEFAULT_SLOT 100 /* ms */ -#define DEFAULT_PERS 64 /* 0->255 */ - -struct yam_port { - int magic; - int bitrate; - int baudrate; - int iobase; - int irq; - int dupmode; - - struct net_device *dev; - - int nb_rxint; - int nb_mdint; - - /* Parameters section */ - - int txd; /* tx delay */ - int holdd; /* duplex ptt delay */ - int txtail; /* txtail delay */ - int slot; /* slottime */ - int pers; /* persistence */ - - /* Tx section */ - - int tx_state; - int tx_count; - int slotcnt; - unsigned char tx_buf[YAM_MAX_FRAME]; - int tx_len; - int tx_crcl, tx_crch; - struct sk_buff_head send_queue; /* Packets awaiting transmission */ - - /* Rx section */ - - int dcd; - unsigned char rx_buf[YAM_MAX_FRAME]; - int rx_len; - int rx_crcl, rx_crch; -}; - -struct yam_mcs { - unsigned char bits[YAM_FPGA_SIZE]; - int bitrate; - struct yam_mcs *next; -}; - -static struct net_device *yam_devs[NR_PORTS]; - -static struct yam_mcs *yam_data; - -static DEFINE_TIMER(yam_timer, NULL); - -/* --------------------------------------------------------------------- */ - -#define RBR(iobase) (iobase+0) -#define THR(iobase) (iobase+0) -#define IER(iobase) (iobase+1) -#define IIR(iobase) (iobase+2) -#define FCR(iobase) (iobase+2) -#define LCR(iobase) (iobase+3) -#define MCR(iobase) (iobase+4) -#define LSR(iobase) (iobase+5) -#define MSR(iobase) (iobase+6) -#define SCR(iobase) (iobase+7) -#define DLL(iobase) (iobase+0) -#define DLM(iobase) (iobase+1) - -#define YAM_EXTENT 8 - -/* Interrupt Identification Register Bit Masks */ -#define IIR_NOPEND 1 -#define IIR_MSR 0 -#define IIR_TX 2 -#define IIR_RX 4 -#define IIR_LSR 6 -#define IIR_TIMEOUT 12 /* Fifo mode only */ - -#define IIR_MASK 0x0F - -/* Interrupt Enable Register Bit Masks */ -#define IER_RX 1 /* enable rx interrupt */ -#define IER_TX 2 /* enable tx interrupt */ -#define IER_LSR 4 /* enable line status interrupts */ -#define IER_MSR 8 /* enable modem status interrupts */ - -/* Modem Control Register Bit Masks */ -#define MCR_DTR 0x01 /* DTR output */ -#define MCR_RTS 0x02 /* RTS output */ -#define MCR_OUT1 0x04 /* OUT1 output (not accessible in RS232) */ -#define MCR_OUT2 0x08 /* Master Interrupt enable (must be set on PCs) */ -#define MCR_LOOP 0x10 /* Loopback enable */ - -/* Modem Status Register Bit Masks */ -#define MSR_DCTS 0x01 /* Delta CTS input */ -#define MSR_DDSR 0x02 /* Delta DSR */ -#define MSR_DRIN 0x04 /* Delta RI */ -#define MSR_DDCD 0x08 /* Delta DCD */ -#define MSR_CTS 0x10 /* CTS input */ -#define MSR_DSR 0x20 /* DSR input */ -#define MSR_RING 0x40 /* RI input */ -#define MSR_DCD 0x80 /* DCD input */ - -/* line status register bit mask */ -#define LSR_RXC 0x01 -#define LSR_OE 0x02 -#define LSR_PE 0x04 -#define LSR_FE 0x08 -#define LSR_BREAK 0x10 -#define LSR_THRE 0x20 -#define LSR_TSRE 0x40 - -/* Line Control Register Bit Masks */ -#define LCR_DLAB 0x80 -#define LCR_BREAK 0x40 -#define LCR_PZERO 0x28 -#define LCR_PEVEN 0x18 -#define LCR_PODD 0x08 -#define LCR_STOP1 0x00 -#define LCR_STOP2 0x04 -#define LCR_BIT5 0x00 -#define LCR_BIT6 0x02 -#define LCR_BIT7 0x01 -#define LCR_BIT8 0x03 - -/* YAM Modem <-> UART Port mapping */ - -#define TX_RDY MSR_DCTS /* transmitter ready to send */ -#define RX_DCD MSR_DCD /* carrier detect */ -#define RX_FLAG MSR_RING /* hdlc flag received */ -#define FPGA_DONE MSR_DSR /* FPGA is configured */ -#define PTT_ON (MCR_RTS|MCR_OUT2) /* activate PTT */ -#define PTT_OFF (MCR_DTR|MCR_OUT2) /* release PTT */ - -#define ENABLE_RXINT IER_RX /* enable uart rx interrupt during rx */ -#define ENABLE_TXINT IER_MSR /* enable uart ms interrupt during tx */ -#define ENABLE_RTXINT (IER_RX|IER_MSR) /* full duplex operations */ - - -/************************************************************************* -* CRC Tables -************************************************************************/ - -static const unsigned char chktabl[256] = -{0x00, 0x89, 0x12, 0x9b, 0x24, 0xad, 0x36, 0xbf, 0x48, 0xc1, 0x5a, 0xd3, 0x6c, 0xe5, 0x7e, - 0xf7, 0x81, 0x08, 0x93, 0x1a, 0xa5, 0x2c, 0xb7, 0x3e, 0xc9, 0x40, 0xdb, 0x52, 0xed, 0x64, - 0xff, 0x76, 0x02, 0x8b, 0x10, 0x99, 0x26, 0xaf, 0x34, 0xbd, 0x4a, 0xc3, 0x58, 0xd1, 0x6e, - 0xe7, 0x7c, 0xf5, 0x83, 0x0a, 0x91, 0x18, 0xa7, 0x2e, 0xb5, 0x3c, 0xcb, 0x42, 0xd9, 0x50, - 0xef, 0x66, 0xfd, 0x74, 0x04, 0x8d, 0x16, 0x9f, 0x20, 0xa9, 0x32, 0xbb, 0x4c, 0xc5, 0x5e, - 0xd7, 0x68, 0xe1, 0x7a, 0xf3, 0x85, 0x0c, 0x97, 0x1e, 0xa1, 0x28, 0xb3, 0x3a, 0xcd, 0x44, - 0xdf, 0x56, 0xe9, 0x60, 0xfb, 0x72, 0x06, 0x8f, 0x14, 0x9d, 0x22, 0xab, 0x30, 0xb9, 0x4e, - 0xc7, 0x5c, 0xd5, 0x6a, 0xe3, 0x78, 0xf1, 0x87, 0x0e, 0x95, 0x1c, 0xa3, 0x2a, 0xb1, 0x38, - 0xcf, 0x46, 0xdd, 0x54, 0xeb, 0x62, 0xf9, 0x70, 0x08, 0x81, 0x1a, 0x93, 0x2c, 0xa5, 0x3e, - 0xb7, 0x40, 0xc9, 0x52, 0xdb, 0x64, 0xed, 0x76, 0xff, 0x89, 0x00, 0x9b, 0x12, 0xad, 0x24, - 0xbf, 0x36, 0xc1, 0x48, 0xd3, 0x5a, 0xe5, 0x6c, 0xf7, 0x7e, 0x0a, 0x83, 0x18, 0x91, 0x2e, - 0xa7, 0x3c, 0xb5, 0x42, 0xcb, 0x50, 0xd9, 0x66, 0xef, 0x74, 0xfd, 0x8b, 0x02, 0x99, 0x10, - 0xaf, 0x26, 0xbd, 0x34, 0xc3, 0x4a, 0xd1, 0x58, 0xe7, 0x6e, 0xf5, 0x7c, 0x0c, 0x85, 0x1e, - 0x97, 0x28, 0xa1, 0x3a, 0xb3, 0x44, 0xcd, 0x56, 0xdf, 0x60, 0xe9, 0x72, 0xfb, 0x8d, 0x04, - 0x9f, 0x16, 0xa9, 0x20, 0xbb, 0x32, 0xc5, 0x4c, 0xd7, 0x5e, 0xe1, 0x68, 0xf3, 0x7a, 0x0e, - 0x87, 0x1c, 0x95, 0x2a, 0xa3, 0x38, 0xb1, 0x46, 0xcf, 0x54, 0xdd, 0x62, 0xeb, 0x70, 0xf9, - 0x8f, 0x06, 0x9d, 0x14, 0xab, 0x22, 0xb9, 0x30, 0xc7, 0x4e, 0xd5, 0x5c, 0xe3, 0x6a, 0xf1, - 0x78}; -static const unsigned char chktabh[256] = -{0x00, 0x11, 0x23, 0x32, 0x46, 0x57, 0x65, 0x74, 0x8c, 0x9d, 0xaf, 0xbe, 0xca, 0xdb, 0xe9, - 0xf8, 0x10, 0x01, 0x33, 0x22, 0x56, 0x47, 0x75, 0x64, 0x9c, 0x8d, 0xbf, 0xae, 0xda, 0xcb, - 0xf9, 0xe8, 0x21, 0x30, 0x02, 0x13, 0x67, 0x76, 0x44, 0x55, 0xad, 0xbc, 0x8e, 0x9f, 0xeb, - 0xfa, 0xc8, 0xd9, 0x31, 0x20, 0x12, 0x03, 0x77, 0x66, 0x54, 0x45, 0xbd, 0xac, 0x9e, 0x8f, - 0xfb, 0xea, 0xd8, 0xc9, 0x42, 0x53, 0x61, 0x70, 0x04, 0x15, 0x27, 0x36, 0xce, 0xdf, 0xed, - 0xfc, 0x88, 0x99, 0xab, 0xba, 0x52, 0x43, 0x71, 0x60, 0x14, 0x05, 0x37, 0x26, 0xde, 0xcf, - 0xfd, 0xec, 0x98, 0x89, 0xbb, 0xaa, 0x63, 0x72, 0x40, 0x51, 0x25, 0x34, 0x06, 0x17, 0xef, - 0xfe, 0xcc, 0xdd, 0xa9, 0xb8, 0x8a, 0x9b, 0x73, 0x62, 0x50, 0x41, 0x35, 0x24, 0x16, 0x07, - 0xff, 0xee, 0xdc, 0xcd, 0xb9, 0xa8, 0x9a, 0x8b, 0x84, 0x95, 0xa7, 0xb6, 0xc2, 0xd3, 0xe1, - 0xf0, 0x08, 0x19, 0x2b, 0x3a, 0x4e, 0x5f, 0x6d, 0x7c, 0x94, 0x85, 0xb7, 0xa6, 0xd2, 0xc3, - 0xf1, 0xe0, 0x18, 0x09, 0x3b, 0x2a, 0x5e, 0x4f, 0x7d, 0x6c, 0xa5, 0xb4, 0x86, 0x97, 0xe3, - 0xf2, 0xc0, 0xd1, 0x29, 0x38, 0x0a, 0x1b, 0x6f, 0x7e, 0x4c, 0x5d, 0xb5, 0xa4, 0x96, 0x87, - 0xf3, 0xe2, 0xd0, 0xc1, 0x39, 0x28, 0x1a, 0x0b, 0x7f, 0x6e, 0x5c, 0x4d, 0xc6, 0xd7, 0xe5, - 0xf4, 0x80, 0x91, 0xa3, 0xb2, 0x4a, 0x5b, 0x69, 0x78, 0x0c, 0x1d, 0x2f, 0x3e, 0xd6, 0xc7, - 0xf5, 0xe4, 0x90, 0x81, 0xb3, 0xa2, 0x5a, 0x4b, 0x79, 0x68, 0x1c, 0x0d, 0x3f, 0x2e, 0xe7, - 0xf6, 0xc4, 0xd5, 0xa1, 0xb0, 0x82, 0x93, 0x6b, 0x7a, 0x48, 0x59, 0x2d, 0x3c, 0x0e, 0x1f, - 0xf7, 0xe6, 0xd4, 0xc5, 0xb1, 0xa0, 0x92, 0x83, 0x7b, 0x6a, 0x58, 0x49, 0x3d, 0x2c, 0x1e, - 0x0f}; - -/************************************************************************* -* FPGA functions -************************************************************************/ - -static void delay(int ms) -{ - unsigned long timeout = jiffies + ((ms * HZ) / 1000); - while (time_before(jiffies, timeout)) - cpu_relax(); -} - -/* - * reset FPGA - */ - -static void fpga_reset(int iobase) -{ - outb(0, IER(iobase)); - outb(LCR_DLAB | LCR_BIT5, LCR(iobase)); - outb(1, DLL(iobase)); - outb(0, DLM(iobase)); - - outb(LCR_BIT5, LCR(iobase)); - inb(LSR(iobase)); - inb(MSR(iobase)); - /* turn off FPGA supply voltage */ - outb(MCR_OUT1 | MCR_OUT2, MCR(iobase)); - delay(100); - /* turn on FPGA supply voltage again */ - outb(MCR_DTR | MCR_RTS | MCR_OUT1 | MCR_OUT2, MCR(iobase)); - delay(100); -} - -/* - * send one byte to FPGA - */ - -static int fpga_write(int iobase, unsigned char wrd) -{ - unsigned char bit; - int k; - unsigned long timeout = jiffies + HZ / 10; - - for (k = 0; k < 8; k++) { - bit = (wrd & 0x80) ? (MCR_RTS | MCR_DTR) : MCR_DTR; - outb(bit | MCR_OUT1 | MCR_OUT2, MCR(iobase)); - wrd <<= 1; - outb(0xfc, THR(iobase)); - while ((inb(LSR(iobase)) & LSR_TSRE) == 0) - if (time_after(jiffies, timeout)) - return -1; - } - - return 0; -} - -/* - * predef should be 0 for loading user defined mcs - * predef should be YAM_1200 for loading predef 1200 mcs - * predef should be YAM_9600 for loading predef 9600 mcs - */ -static unsigned char *add_mcs(unsigned char *bits, int bitrate, - unsigned int predef) -{ - const char *fw_name[2] = {FIRMWARE_9600, FIRMWARE_1200}; - const struct firmware *fw; - struct platform_device *pdev; - struct yam_mcs *p; - int err; - - switch (predef) { - case 0: - fw = NULL; - break; - case YAM_1200: - case YAM_9600: - predef--; - pdev = platform_device_register_simple("yam", 0, NULL, 0); - if (IS_ERR(pdev)) { - printk(KERN_ERR "yam: Failed to register firmware\n"); - return NULL; - } - err = request_firmware(&fw, fw_name[predef], &pdev->dev); - platform_device_unregister(pdev); - if (err) { - printk(KERN_ERR "Failed to load firmware \"%s\"\n", - fw_name[predef]); - return NULL; - } - if (fw->size != YAM_FPGA_SIZE) { - printk(KERN_ERR "Bogus length %zu in firmware \"%s\"\n", - fw->size, fw_name[predef]); - release_firmware(fw); - return NULL; - } - bits = (unsigned char *)fw->data; - break; - default: - printk(KERN_ERR "yam: Invalid predef number %u\n", predef); - return NULL; - } - - /* If it already exists, replace the bit data */ - p = yam_data; - while (p) { - if (p->bitrate == bitrate) { - memcpy(p->bits, bits, YAM_FPGA_SIZE); - goto out; - } - p = p->next; - } - - /* Allocate a new mcs */ - if ((p = kmalloc_obj(struct yam_mcs)) == NULL) { - release_firmware(fw); - return NULL; - } - memcpy(p->bits, bits, YAM_FPGA_SIZE); - p->bitrate = bitrate; - p->next = yam_data; - yam_data = p; - out: - release_firmware(fw); - return p->bits; -} - -static unsigned char *get_mcs(int bitrate) -{ - struct yam_mcs *p; - - p = yam_data; - while (p) { - if (p->bitrate == bitrate) - return p->bits; - p = p->next; - } - - /* Load predefined mcs data */ - switch (bitrate) { - case 1200: - /* setting predef as YAM_1200 for loading predef 1200 mcs */ - return add_mcs(NULL, bitrate, YAM_1200); - default: - /* setting predef as YAM_9600 for loading predef 9600 mcs */ - return add_mcs(NULL, bitrate, YAM_9600); - } -} - -/* - * download bitstream to FPGA - * data is contained in bits[] array in yam1200.h resp. yam9600.h - */ - -static int fpga_download(int iobase, int bitrate) -{ - int i, rc; - unsigned char *pbits; - - pbits = get_mcs(bitrate); - if (pbits == NULL) - return -1; - - fpga_reset(iobase); - for (i = 0; i < YAM_FPGA_SIZE; i++) { - if (fpga_write(iobase, pbits[i])) { - printk(KERN_ERR "yam: error in write cycle\n"); - return -1; /* write... */ - } - } - - fpga_write(iobase, 0xFF); - rc = inb(MSR(iobase)); /* check DONE signal */ - - /* Needed for some hardwares */ - delay(50); - - return (rc & MSR_DSR) ? 0 : -1; -} - - -/************************************************************************ -* Serial port init -************************************************************************/ - -static void yam_set_uart(struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - int divisor = 115200 / yp->baudrate; - - outb(0, IER(dev->base_addr)); - outb(LCR_DLAB | LCR_BIT8, LCR(dev->base_addr)); - outb(divisor, DLL(dev->base_addr)); - outb(0, DLM(dev->base_addr)); - outb(LCR_BIT8, LCR(dev->base_addr)); - outb(PTT_OFF, MCR(dev->base_addr)); - outb(0x00, FCR(dev->base_addr)); - - /* Flush pending irq */ - - inb(RBR(dev->base_addr)); - inb(MSR(dev->base_addr)); - - /* Enable rx irq */ - - outb(ENABLE_RTXINT, IER(dev->base_addr)); -} - - -/* --------------------------------------------------------------------- */ - -enum uart { - c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A -}; - -static const char *uart_str[] = -{"unknown", "8250", "16450", "16550", "16550A"}; - -static enum uart yam_check_uart(unsigned int iobase) -{ - unsigned char b1, b2, b3; - enum uart u; - enum uart uart_tab[] = - {c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A}; - - b1 = inb(MCR(iobase)); - outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ - b2 = inb(MSR(iobase)); - outb(0x1a, MCR(iobase)); - b3 = inb(MSR(iobase)) & 0xf0; - outb(b1, MCR(iobase)); /* restore old values */ - outb(b2, MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(RBR(iobase)); - inb(RBR(iobase)); - outb(0x01, FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, SCR(iobase)); - b1 = inb(SCR(iobase)); - outb(0xa5, SCR(iobase)); - b2 = inb(SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/****************************************************************************** -* Rx Section -******************************************************************************/ -static inline void yam_rx_flag(struct net_device *dev, struct yam_port *yp) -{ - if (yp->dcd && yp->rx_len >= 3 && yp->rx_len < YAM_MAX_FRAME) { - int pkt_len = yp->rx_len - 2 + 1; /* -CRC + kiss */ - struct sk_buff *skb; - - if ((yp->rx_crch & yp->rx_crcl) != 0xFF) { - /* Bad crc */ - } else { - if (!(skb = dev_alloc_skb(pkt_len))) { - printk(KERN_WARNING "%s: memory squeeze, dropping packet\n", dev->name); - ++dev->stats.rx_dropped; - } else { - unsigned char *cp; - cp = skb_put(skb, pkt_len); - *cp++ = 0; /* KISS kludge */ - memcpy(cp, yp->rx_buf, pkt_len - 1); - skb->protocol = ax25_type_trans(skb, dev); - netif_rx(skb); - ++dev->stats.rx_packets; - } - } - } - yp->rx_len = 0; - yp->rx_crcl = 0x21; - yp->rx_crch = 0xf3; -} - -static inline void yam_rx_byte(struct net_device *dev, struct yam_port *yp, unsigned char rxb) -{ - if (yp->rx_len < YAM_MAX_FRAME) { - unsigned char c = yp->rx_crcl; - yp->rx_crcl = (chktabl[c] ^ yp->rx_crch); - yp->rx_crch = (chktabh[c] ^ rxb); - yp->rx_buf[yp->rx_len++] = rxb; - } -} - -/******************************************************************************** -* TX Section -********************************************************************************/ - -static void ptt_on(struct net_device *dev) -{ - outb(PTT_ON, MCR(dev->base_addr)); -} - -static void ptt_off(struct net_device *dev) -{ - outb(PTT_OFF, MCR(dev->base_addr)); -} - -static netdev_tx_t yam_send_packet(struct sk_buff *skb, - struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - skb_queue_tail(&yp->send_queue, skb); - netif_trans_update(dev); - return NETDEV_TX_OK; -} - -static void yam_start_tx(struct net_device *dev, struct yam_port *yp) -{ - if ((yp->tx_state == TX_TAIL) || (yp->txd == 0)) - yp->tx_count = 1; - else - yp->tx_count = (yp->bitrate * yp->txd) / 8000; - yp->tx_state = TX_HEAD; - ptt_on(dev); -} - -static void yam_arbitrate(struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - - if (yp->magic != YAM_MAGIC || yp->tx_state != TX_OFF || - skb_queue_empty(&yp->send_queue)) - return; - /* tx_state is TX_OFF and there is data to send */ - - if (yp->dupmode) { - /* Full duplex mode, don't wait */ - yam_start_tx(dev, yp); - return; - } - if (yp->dcd) { - /* DCD on, wait slotime ... */ - yp->slotcnt = yp->slot / 10; - return; - } - /* Is slottime passed ? */ - if ((--yp->slotcnt) > 0) - return; - - yp->slotcnt = yp->slot / 10; - - /* is random > persist ? */ - if (get_random_u8() > yp->pers) - return; - - yam_start_tx(dev, yp); -} - -static void yam_dotimer(struct timer_list *unused) -{ - int i; - - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev = yam_devs[i]; - if (dev && netif_running(dev)) - yam_arbitrate(dev); - } - yam_timer.expires = jiffies + HZ / 100; - add_timer(&yam_timer); -} - -static void yam_tx_byte(struct net_device *dev, struct yam_port *yp) -{ - struct sk_buff *skb; - unsigned char b, temp; - - switch (yp->tx_state) { - case TX_OFF: - break; - case TX_HEAD: - if (--yp->tx_count <= 0) { - if (!(skb = skb_dequeue(&yp->send_queue))) { - ptt_off(dev); - yp->tx_state = TX_OFF; - break; - } - yp->tx_state = TX_DATA; - if (skb->data[0] != 0) { -/* do_kiss_params(s, skb->data, skb->len); */ - dev_kfree_skb_any(skb); - break; - } - yp->tx_len = skb->len - 1; /* strip KISS byte */ - if (yp->tx_len >= YAM_MAX_FRAME || yp->tx_len < 2) { - dev_kfree_skb_any(skb); - break; - } - skb_copy_from_linear_data_offset(skb, 1, - yp->tx_buf, - yp->tx_len); - dev_kfree_skb_any(skb); - yp->tx_count = 0; - yp->tx_crcl = 0x21; - yp->tx_crch = 0xf3; - yp->tx_state = TX_DATA; - } - break; - case TX_DATA: - b = yp->tx_buf[yp->tx_count++]; - outb(b, THR(dev->base_addr)); - temp = yp->tx_crcl; - yp->tx_crcl = chktabl[temp] ^ yp->tx_crch; - yp->tx_crch = chktabh[temp] ^ b; - if (yp->tx_count >= yp->tx_len) { - yp->tx_state = TX_CRC1; - } - break; - case TX_CRC1: - yp->tx_crch = chktabl[yp->tx_crcl] ^ yp->tx_crch; - yp->tx_crcl = chktabh[yp->tx_crcl] ^ chktabl[yp->tx_crch] ^ 0xff; - outb(yp->tx_crcl, THR(dev->base_addr)); - yp->tx_state = TX_CRC2; - break; - case TX_CRC2: - outb(chktabh[yp->tx_crch] ^ 0xFF, THR(dev->base_addr)); - if (skb_queue_empty(&yp->send_queue)) { - yp->tx_count = (yp->bitrate * yp->txtail) / 8000; - if (yp->dupmode == 2) - yp->tx_count += (yp->bitrate * yp->holdd) / 8; - if (yp->tx_count == 0) - yp->tx_count = 1; - yp->tx_state = TX_TAIL; - } else { - yp->tx_count = 1; - yp->tx_state = TX_HEAD; - } - ++dev->stats.tx_packets; - break; - case TX_TAIL: - if (--yp->tx_count <= 0) { - yp->tx_state = TX_OFF; - ptt_off(dev); - } - break; - } -} - -/*********************************************************************************** -* ISR routine -************************************************************************************/ - -static irqreturn_t yam_interrupt(int irq, void *dev_id) -{ - struct net_device *dev; - struct yam_port *yp; - unsigned char iir; - int counter = 100; - int i; - int handled = 0; - - for (i = 0; i < NR_PORTS; i++) { - dev = yam_devs[i]; - yp = netdev_priv(dev); - - if (!netif_running(dev)) - continue; - - while ((iir = IIR_MASK & inb(IIR(dev->base_addr))) != IIR_NOPEND) { - unsigned char msr = inb(MSR(dev->base_addr)); - unsigned char lsr = inb(LSR(dev->base_addr)); - unsigned char rxb; - - handled = 1; - - if (lsr & LSR_OE) - ++dev->stats.rx_fifo_errors; - - yp->dcd = (msr & RX_DCD) ? 1 : 0; - - if (--counter <= 0) { - printk(KERN_ERR "%s: too many irq iir=%d\n", - dev->name, iir); - goto out; - } - if (msr & TX_RDY) { - ++yp->nb_mdint; - yam_tx_byte(dev, yp); - } - if (lsr & LSR_RXC) { - ++yp->nb_rxint; - rxb = inb(RBR(dev->base_addr)); - if (msr & RX_FLAG) - yam_rx_flag(dev, yp); - else - yam_rx_byte(dev, yp, rxb); - } - } - } -out: - return IRQ_RETVAL(handled); -} - -#ifdef CONFIG_PROC_FS - -static void *yam_seq_start(struct seq_file *seq, loff_t *pos) -{ - return (*pos < NR_PORTS) ? yam_devs[*pos] : NULL; -} - -static void *yam_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - return (*pos < NR_PORTS) ? yam_devs[*pos] : NULL; -} - -static void yam_seq_stop(struct seq_file *seq, void *v) -{ -} - -static int yam_seq_show(struct seq_file *seq, void *v) -{ - struct net_device *dev = v; - const struct yam_port *yp = netdev_priv(dev); - - seq_printf(seq, "Device %s\n", dev->name); - seq_printf(seq, " Up %d\n", netif_running(dev)); - seq_printf(seq, " Speed %u\n", yp->bitrate); - seq_printf(seq, " IoBase 0x%x\n", yp->iobase); - seq_printf(seq, " BaudRate %u\n", yp->baudrate); - seq_printf(seq, " IRQ %u\n", yp->irq); - seq_printf(seq, " TxState %u\n", yp->tx_state); - seq_printf(seq, " Duplex %u\n", yp->dupmode); - seq_printf(seq, " HoldDly %u\n", yp->holdd); - seq_printf(seq, " TxDelay %u\n", yp->txd); - seq_printf(seq, " TxTail %u\n", yp->txtail); - seq_printf(seq, " SlotTime %u\n", yp->slot); - seq_printf(seq, " Persist %u\n", yp->pers); - seq_printf(seq, " TxFrames %lu\n", dev->stats.tx_packets); - seq_printf(seq, " RxFrames %lu\n", dev->stats.rx_packets); - seq_printf(seq, " TxInt %u\n", yp->nb_mdint); - seq_printf(seq, " RxInt %u\n", yp->nb_rxint); - seq_printf(seq, " RxOver %lu\n", dev->stats.rx_fifo_errors); - seq_printf(seq, "\n"); - return 0; -} - -static const struct seq_operations yam_seqops = { - .start = yam_seq_start, - .next = yam_seq_next, - .stop = yam_seq_stop, - .show = yam_seq_show, -}; -#endif - - -/* --------------------------------------------------------------------- */ - -static int yam_open(struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - enum uart u; - int i; - int ret=0; - - printk(KERN_INFO "Trying %s at iobase 0x%lx irq %u\n", dev->name, dev->base_addr, dev->irq); - - if (!yp->bitrate) - return -ENXIO; - if (!dev->base_addr || dev->base_addr > 0x1000 - YAM_EXTENT || - dev->irq < 2 || dev->irq > 15) { - return -ENXIO; - } - if (!request_region(dev->base_addr, YAM_EXTENT, dev->name)) - { - printk(KERN_ERR "%s: cannot 0x%lx busy\n", dev->name, dev->base_addr); - return -EACCES; - } - if ((u = yam_check_uart(dev->base_addr)) == c_uart_unknown) { - printk(KERN_ERR "%s: cannot find uart type\n", dev->name); - ret = -EIO; - goto out_release_base; - } - if (fpga_download(dev->base_addr, yp->bitrate)) { - printk(KERN_ERR "%s: cannot init FPGA\n", dev->name); - ret = -EIO; - goto out_release_base; - } - outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, yam_interrupt, IRQF_SHARED, dev->name, dev)) { - printk(KERN_ERR "%s: irq %d busy\n", dev->name, dev->irq); - ret = -EBUSY; - goto out_release_base; - } - - yam_set_uart(dev); - - netif_start_queue(dev); - - yp->slotcnt = yp->slot / 10; - - /* Reset overruns for all ports - FPGA programming makes overruns */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *yam_dev = yam_devs[i]; - - inb(LSR(yam_dev->base_addr)); - yam_dev->stats.rx_fifo_errors = 0; - } - - printk(KERN_INFO "%s at iobase 0x%lx irq %u uart %s\n", dev->name, dev->base_addr, dev->irq, - uart_str[u]); - return 0; - -out_release_base: - release_region(dev->base_addr, YAM_EXTENT); - return ret; -} - -/* --------------------------------------------------------------------- */ - -static int yam_close(struct net_device *dev) -{ - struct sk_buff *skb; - struct yam_port *yp = netdev_priv(dev); - - if (!dev) - return -EINVAL; - - /* - * disable interrupts - */ - outb(0, IER(dev->base_addr)); - outb(1, MCR(dev->base_addr)); - /* Remove IRQ handler if last */ - free_irq(dev->irq,dev); - release_region(dev->base_addr, YAM_EXTENT); - netif_stop_queue(dev); - while ((skb = skb_dequeue(&yp->send_queue))) - dev_kfree_skb(skb); - - printk(KERN_INFO "%s: close yam at iobase 0x%lx irq %u\n", - yam_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int yam_siocdevprivate(struct net_device *dev, struct ifreq *ifr, void __user *data, int cmd) -{ - struct yam_port *yp = netdev_priv(dev); - struct yamdrv_ioctl_cfg yi; - struct yamdrv_ioctl_mcs *ym; - int ioctl_cmd; - - if (copy_from_user(&ioctl_cmd, data, sizeof(int))) - return -EFAULT; - - if (yp->magic != YAM_MAGIC) - return -EINVAL; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (cmd != SIOCDEVPRIVATE) - return -EINVAL; - - switch (ioctl_cmd) { - - case SIOCYAMRESERVED: - return -EINVAL; /* unused */ - - case SIOCYAMSMCS: - if (netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - ym = memdup_user(data, sizeof(struct yamdrv_ioctl_mcs)); - if (IS_ERR(ym)) - return PTR_ERR(ym); - if (ym->cmd != SIOCYAMSMCS || ym->bitrate > YAM_MAXBITRATE) { - kfree(ym); - return -EINVAL; - } - /* setting predef as 0 for loading userdefined mcs data */ - add_mcs(ym->bits, ym->bitrate, 0); - kfree(ym); - break; - - case SIOCYAMSCFG: - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - if (copy_from_user(&yi, data, sizeof(struct yamdrv_ioctl_cfg))) - return -EFAULT; - - if (yi.cmd != SIOCYAMSCFG) - return -EINVAL; - if ((yi.cfg.mask & YAM_IOBASE) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - if ((yi.cfg.mask & YAM_IRQ) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - if ((yi.cfg.mask & YAM_BITRATE) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - if ((yi.cfg.mask & YAM_BAUDRATE) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - - if (yi.cfg.mask & YAM_IOBASE) { - yp->iobase = yi.cfg.iobase; - dev->base_addr = yi.cfg.iobase; - } - if (yi.cfg.mask & YAM_IRQ) { - if (yi.cfg.irq > 15) - return -EINVAL; - yp->irq = yi.cfg.irq; - dev->irq = yi.cfg.irq; - } - if (yi.cfg.mask & YAM_BITRATE) { - if (yi.cfg.bitrate > YAM_MAXBITRATE) - return -EINVAL; - yp->bitrate = yi.cfg.bitrate; - } - if (yi.cfg.mask & YAM_BAUDRATE) { - if (yi.cfg.baudrate > YAM_MAXBAUDRATE) - return -EINVAL; - yp->baudrate = yi.cfg.baudrate; - } - if (yi.cfg.mask & YAM_MODE) { - if (yi.cfg.mode > YAM_MAXMODE) - return -EINVAL; - yp->dupmode = yi.cfg.mode; - } - if (yi.cfg.mask & YAM_HOLDDLY) { - if (yi.cfg.holddly > YAM_MAXHOLDDLY) - return -EINVAL; - yp->holdd = yi.cfg.holddly; - } - if (yi.cfg.mask & YAM_TXDELAY) { - if (yi.cfg.txdelay > YAM_MAXTXDELAY) - return -EINVAL; - yp->txd = yi.cfg.txdelay; - } - if (yi.cfg.mask & YAM_TXTAIL) { - if (yi.cfg.txtail > YAM_MAXTXTAIL) - return -EINVAL; - yp->txtail = yi.cfg.txtail; - } - if (yi.cfg.mask & YAM_PERSIST) { - if (yi.cfg.persist > YAM_MAXPERSIST) - return -EINVAL; - yp->pers = yi.cfg.persist; - } - if (yi.cfg.mask & YAM_SLOTTIME) { - if (yi.cfg.slottime > YAM_MAXSLOTTIME) - return -EINVAL; - yp->slot = yi.cfg.slottime; - yp->slotcnt = yp->slot / 10; - } - break; - - case SIOCYAMGCFG: - memset(&yi, 0, sizeof(yi)); - yi.cfg.mask = 0xffffffff; - yi.cfg.iobase = yp->iobase; - yi.cfg.irq = yp->irq; - yi.cfg.bitrate = yp->bitrate; - yi.cfg.baudrate = yp->baudrate; - yi.cfg.mode = yp->dupmode; - yi.cfg.txdelay = yp->txd; - yi.cfg.holddly = yp->holdd; - yi.cfg.txtail = yp->txtail; - yi.cfg.persist = yp->pers; - yi.cfg.slottime = yp->slot; - if (copy_to_user(data, &yi, sizeof(struct yamdrv_ioctl_cfg))) - return -EFAULT; - break; - - default: - return -EINVAL; - - } - - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int yam_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *) addr; - - /* addr is an AX.25 shifted ASCII mac address */ - dev_addr_set(dev, sa->sa_data); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static const struct net_device_ops yam_netdev_ops = { - .ndo_open = yam_open, - .ndo_stop = yam_close, - .ndo_start_xmit = yam_send_packet, - .ndo_siocdevprivate = yam_siocdevprivate, - .ndo_set_mac_address = yam_set_mac_address, -}; - -static void yam_setup(struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - - yp->magic = YAM_MAGIC; - yp->bitrate = DEFAULT_BITRATE; - yp->baudrate = DEFAULT_BITRATE * 2; - yp->iobase = 0; - yp->irq = 0; - yp->dupmode = 0; - yp->holdd = DEFAULT_HOLDD; - yp->txd = DEFAULT_TXD; - yp->txtail = DEFAULT_TXTAIL; - yp->slot = DEFAULT_SLOT; - yp->pers = DEFAULT_PERS; - yp->dev = dev; - - dev->base_addr = yp->iobase; - dev->irq = yp->irq; - - skb_queue_head_init(&yp->send_queue); - - dev->netdev_ops = &yam_netdev_ops; - dev->header_ops = &ax25_header_ops; - - dev->type = ARPHRD_AX25; - dev->hard_header_len = AX25_MAX_HEADER_LEN; - dev->mtu = AX25_MTU; - dev->addr_len = AX25_ADDR_LEN; - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); -} - -static int __init yam_init_driver(void) -{ - struct net_device *dev; - int i, err; - char name[IFNAMSIZ]; - - printk(yam_drvinfo); - - for (i = 0; i < NR_PORTS; i++) { - sprintf(name, "yam%d", i); - - dev = alloc_netdev(sizeof(struct yam_port), name, - NET_NAME_UNKNOWN, yam_setup); - if (!dev) { - pr_err("yam: cannot allocate net device\n"); - err = -ENOMEM; - goto error; - } - - err = register_netdev(dev); - if (err) { - printk(KERN_WARNING "yam: cannot register net device %s\n", dev->name); - free_netdev(dev); - goto error; - } - yam_devs[i] = dev; - - } - - timer_setup(&yam_timer, yam_dotimer, 0); - yam_timer.expires = jiffies + HZ / 100; - add_timer(&yam_timer); - - proc_create_seq("yam", 0444, init_net.proc_net, &yam_seqops); - return 0; - error: - while (--i >= 0) { - unregister_netdev(yam_devs[i]); - free_netdev(yam_devs[i]); - } - return err; -} - -/* --------------------------------------------------------------------- */ - -static void __exit yam_cleanup_driver(void) -{ - struct yam_mcs *p; - int i; - - timer_delete_sync(&yam_timer); - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev = yam_devs[i]; - if (dev) { - unregister_netdev(dev); - free_netdev(dev); - } - } - - while (yam_data) { - p = yam_data; - yam_data = yam_data->next; - kfree(p); - } - - remove_proc_entry("yam", init_net.proc_net); -} - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR("Frederic Rible F1OAT frible@teaser.fr"); -MODULE_DESCRIPTION("Yam amateur radio modem driver"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(FIRMWARE_1200); -MODULE_FIRMWARE(FIRMWARE_9600); - -module_init(yam_init_driver); -module_exit(yam_cleanup_driver); - -/* --------------------------------------------------------------------- */ - diff --git a/drivers/net/hamradio/z8530.h b/drivers/net/hamradio/z8530.h deleted file mode 100644 index 1655901d713b..000000000000 --- a/drivers/net/hamradio/z8530.h +++ /dev/null @@ -1,246 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/* 8530 Serial Communications Controller Register definitions */ -#define FLAG 0x7e - -/* Write Register 0 */ -#define R0 0 /* Register selects */ -#define R1 1 -#define R2 2 -#define R3 3 -#define R4 4 -#define R5 5 -#define R6 6 -#define R7 7 -#define R8 8 -#define R9 9 -#define R10 10 -#define R11 11 -#define R12 12 -#define R13 13 -#define R14 14 -#define R15 15 - -#define NULLCODE 0 /* Null Code */ -#define POINT_HIGH 0x8 /* Select upper half of registers */ -#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ -#define SEND_ABORT 0x18 /* HDLC Abort */ -#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ -#define RES_Tx_P 0x28 /* Reset TxINT Pending */ -#define ERR_RES 0x30 /* Error Reset */ -#define RES_H_IUS 0x38 /* Reset highest IUS */ - -#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ -#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ -#define RES_EOM_L 0xC0 /* Reset EOM latch */ - -/* Write Register 1 */ - -#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ -#define TxINT_ENAB 0x2 /* Tx Int Enable */ -#define PAR_SPEC 0x4 /* Parity is special condition */ - -#define RxINT_DISAB 0 /* Rx Int Disable */ -#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ -#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ -#define INT_ERR_Rx 0x18 /* Int on error only */ - -#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ -#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ -#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ - -/* Write Register #2 (Interrupt Vector) */ - -/* Write Register 3 */ - -#define RxENABLE 0x1 /* Rx Enable */ -#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ -#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ -#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ -#define ENT_HM 0x10 /* Enter Hunt Mode */ -#define AUTO_ENAB 0x20 /* Auto Enables */ -#define Rx5 0x0 /* Rx 5 Bits/Character */ -#define Rx7 0x40 /* Rx 7 Bits/Character */ -#define Rx6 0x80 /* Rx 6 Bits/Character */ -#define Rx8 0xc0 /* Rx 8 Bits/Character */ - -/* Write Register 4 */ - -#define PAR_ENA 0x1 /* Parity Enable */ -#define PAR_EVEN 0x2 /* Parity Even/Odd* */ - -#define SYNC_ENAB 0 /* Sync Modes Enable */ -#define SB1 0x4 /* 1 stop bit/char */ -#define SB15 0x8 /* 1.5 stop bits/char */ -#define SB2 0xc /* 2 stop bits/char */ - -#define MONSYNC 0 /* 8 Bit Sync character */ -#define BISYNC 0x10 /* 16 bit sync character */ -#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ -#define EXTSYNC 0x30 /* External Sync Mode */ - -#define X1CLK 0x0 /* x1 clock mode */ -#define X16CLK 0x40 /* x16 clock mode */ -#define X32CLK 0x80 /* x32 clock mode */ -#define X64CLK 0xC0 /* x64 clock mode */ - -/* Write Register 5 */ - -#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ -#define RTS 0x2 /* RTS */ -#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ -#define TxENAB 0x8 /* Tx Enable */ -#define SND_BRK 0x10 /* Send Break */ -#define Tx5 0x0 /* Tx 5 bits (or less)/character */ -#define Tx7 0x20 /* Tx 7 bits/character */ -#define Tx6 0x40 /* Tx 6 bits/character */ -#define Tx8 0x60 /* Tx 8 bits/character */ -#define DTR 0x80 /* DTR */ - -/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ - -/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ - -/* Write Register 8 (transmit buffer) */ - -/* Write Register 9 (Master interrupt control) */ -#define VIS 1 /* Vector Includes Status */ -#define NV 2 /* No Vector */ -#define DLC 4 /* Disable Lower Chain */ -#define MIE 8 /* Master Interrupt Enable */ -#define STATHI 0x10 /* Status high */ -#define NORESET 0 /* No reset on write to R9 */ -#define CHRB 0x40 /* Reset channel B */ -#define CHRA 0x80 /* Reset channel A */ -#define FHWRES 0xc0 /* Force hardware reset */ - -/* Write Register 10 (misc control bits) */ -#define BIT6 1 /* 6 bit/8bit sync */ -#define LOOPMODE 2 /* SDLC Loop mode */ -#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ -#define MARKIDLE 8 /* Mark/flag on idle */ -#define GAOP 0x10 /* Go active on poll */ -#define NRZ 0 /* NRZ mode */ -#define NRZI 0x20 /* NRZI mode */ -#define FM1 0x40 /* FM1 (transition = 1) */ -#define FM0 0x60 /* FM0 (transition = 0) */ -#define CRCPS 0x80 /* CRC Preset I/O */ - -/* Write Register 11 (Clock Mode control) */ -#define TRxCXT 0 /* TRxC = Xtal output */ -#define TRxCTC 1 /* TRxC = Transmit clock */ -#define TRxCBR 2 /* TRxC = BR Generator Output */ -#define TRxCDP 3 /* TRxC = DPLL output */ -#define TRxCOI 4 /* TRxC O/I */ -#define TCRTxCP 0 /* Transmit clock = RTxC pin */ -#define TCTRxCP 8 /* Transmit clock = TRxC pin */ -#define TCBR 0x10 /* Transmit clock = BR Generator output */ -#define TCDPLL 0x18 /* Transmit clock = DPLL output */ -#define RCRTxCP 0 /* Receive clock = RTxC pin */ -#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ -#define RCBR 0x40 /* Receive clock = BR Generator output */ -#define RCDPLL 0x60 /* Receive clock = DPLL output */ -#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ - -/* Write Register 12 (lower byte of baud rate generator time constant) */ - -/* Write Register 13 (upper byte of baud rate generator time constant) */ - -/* Write Register 14 (Misc control bits) */ -#define BRENABL 1 /* Baud rate generator enable */ -#define BRSRC 2 /* Baud rate generator source */ -#define DTRREQ 4 /* DTR/Request function */ -#define AUTOECHO 8 /* Auto Echo */ -#define LOOPBAK 0x10 /* Local loopback */ -#define SEARCH 0x20 /* Enter search mode */ -#define RMC 0x40 /* Reset missing clock */ -#define DISDPLL 0x60 /* Disable DPLL */ -#define SSBR 0x80 /* Set DPLL source = BR generator */ -#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ -#define SFMM 0xc0 /* Set FM mode */ -#define SNRZI 0xe0 /* Set NRZI mode */ - -/* Write Register 15 (external/status interrupt control) */ -#define ZCIE 2 /* Zero count IE */ -#define DCDIE 8 /* DCD IE */ -#define SYNCIE 0x10 /* Sync/hunt IE */ -#define CTSIE 0x20 /* CTS IE */ -#define TxUIE 0x40 /* Tx Underrun/EOM IE */ -#define BRKIE 0x80 /* Break/Abort IE */ - - -/* Read Register 0 */ -#define Rx_CH_AV 0x1 /* Rx Character Available */ -#define ZCOUNT 0x2 /* Zero count */ -#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ -#define DCD 0x8 /* DCD */ -#define SYNC_HUNT 0x10 /* Sync/hunt */ -#define CTS 0x20 /* CTS */ -#define TxEOM 0x40 /* Tx underrun */ -#define BRK_ABRT 0x80 /* Break/Abort */ - -/* Read Register 1 */ -#define ALL_SNT 0x1 /* All sent */ -/* Residue Data for 8 Rx bits/char programmed */ -#define RES3 0x8 /* 0/3 */ -#define RES4 0x4 /* 0/4 */ -#define RES5 0xc /* 0/5 */ -#define RES6 0x2 /* 0/6 */ -#define RES7 0xa /* 0/7 */ -#define RES8 0x6 /* 0/8 */ -#define RES18 0xe /* 1/8 */ -#define RES28 0x0 /* 2/8 */ -/* Special Rx Condition Interrupts */ -#define PAR_ERR 0x10 /* Parity error */ -#define Rx_OVR 0x20 /* Rx Overrun Error */ -#define CRC_ERR 0x40 /* CRC/Framing Error */ -#define END_FR 0x80 /* End of Frame (SDLC) */ - -/* Read Register 2 (channel b only) - Interrupt vector */ - -/* Read Register 3 (interrupt pending register) ch a only */ -#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ -#define CHBTxIP 0x2 /* Channel B Tx IP */ -#define CHBRxIP 0x4 /* Channel B Rx IP */ -#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ -#define CHATxIP 0x10 /* Channel A Tx IP */ -#define CHARxIP 0x20 /* Channel A Rx IP */ - -/* Read Register 8 (receive data register) */ - -/* Read Register 10 (misc status bits) */ -#define ONLOOP 2 /* On loop */ -#define LOOPSEND 0x10 /* Loop sending */ -#define CLK2MIS 0x40 /* Two clocks missing */ -#define CLK1MIS 0x80 /* One clock missing */ - -/* Read Register 12 (lower byte of baud rate generator constant) */ - -/* Read Register 13 (upper byte of baud rate generator constant) */ - -/* Read Register 15 (value of WR 15) */ - -/* Z85C30/Z85230 Enhanced SCC register definitions */ - -/* Write Register 7' (SDLC/HDLC Programmable Enhancements) */ -#define AUTOTXF 0x01 /* Auto Tx Flag */ -#define AUTOEOM 0x02 /* Auto EOM Latch Reset */ -#define AUTORTS 0x04 /* Auto RTS */ -#define TXDNRZI 0x08 /* TxD Pulled High in SDLC NRZI mode */ -#define RXFIFOH 0x08 /* Z85230: Int on RX FIFO half full */ -#define FASTDTR 0x10 /* Fast DTR/REQ Mode */ -#define CRCCBCR 0x20 /* CRC Check Bytes Completely Received */ -#define TXFIFOE 0x20 /* Z85230: Int on TX FIFO completely empty */ -#define EXTRDEN 0x40 /* Extended Read Enabled */ - -/* Write Register 15 (external/status interrupt control) */ -#define SHDLCE 1 /* SDLC/HDLC Enhancements Enable */ -#define FIFOE 4 /* FIFO Enable */ - -/* Read Register 6 (frame status FIFO) */ -#define BCLSB 0xff /* LSB of 14 bits count */ - -/* Read Register 7 (frame status FIFO) */ -#define BCMSB 0x3f /* MSB of 14 bits count */ -#define FDA 0x40 /* FIFO Data Available Status */ -#define FOS 0x80 /* FIFO Overflow Status */ diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 6147ee8b1d78..f904f4d16b45 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -26,6 +26,8 @@ #include +static struct workqueue_struct *macsec_wq; + /* SecTAG length = macsec_eth_header without the optional SCI */ #define MACSEC_TAG_LEN 6 @@ -174,9 +176,10 @@ static void macsec_rxsc_put(struct macsec_rx_sc *sc) call_rcu(&sc->rcu_head, free_rx_sc_rcu); } -static void free_rxsa(struct rcu_head *head) +static void free_rxsa_work(struct work_struct *work) { - struct macsec_rx_sa *sa = container_of(head, struct macsec_rx_sa, rcu); + struct macsec_rx_sa *sa = + container_of(to_rcu_work(work), struct macsec_rx_sa, destroy_work); crypto_free_aead(sa->key.tfm); free_percpu(sa->stats); @@ -186,7 +189,7 @@ static void free_rxsa(struct rcu_head *head) static void macsec_rxsa_put(struct macsec_rx_sa *sa) { if (refcount_dec_and_test(&sa->refcnt)) - call_rcu(&sa->rcu, free_rxsa); + queue_rcu_work(macsec_wq, &sa->destroy_work); } static struct macsec_tx_sa *macsec_txsa_get(struct macsec_tx_sa __rcu *ptr) @@ -202,9 +205,10 @@ static struct macsec_tx_sa *macsec_txsa_get(struct macsec_tx_sa __rcu *ptr) return sa; } -static void free_txsa(struct rcu_head *head) +static void free_txsa_work(struct work_struct *work) { - struct macsec_tx_sa *sa = container_of(head, struct macsec_tx_sa, rcu); + struct macsec_tx_sa *sa = + container_of(to_rcu_work(work), struct macsec_tx_sa, destroy_work); crypto_free_aead(sa->key.tfm); free_percpu(sa->stats); @@ -214,7 +218,7 @@ static void free_txsa(struct rcu_head *head) static void macsec_txsa_put(struct macsec_tx_sa *sa) { if (refcount_dec_and_test(&sa->refcnt)) - call_rcu(&sa->rcu, free_txsa); + queue_rcu_work(macsec_wq, &sa->destroy_work); } static struct macsec_cb *macsec_skb_cb(struct sk_buff *skb) @@ -1407,6 +1411,7 @@ static int init_rx_sa(struct macsec_rx_sa *rx_sa, char *sak, int key_len, rx_sa->next_pn = 1; refcount_set(&rx_sa->refcnt, 1); spin_lock_init(&rx_sa->lock); + INIT_RCU_WORK(&rx_sa->destroy_work, free_rxsa_work); return 0; } @@ -1506,6 +1511,7 @@ static int init_tx_sa(struct macsec_tx_sa *tx_sa, char *sak, int key_len, tx_sa->active = false; refcount_set(&tx_sa->refcnt, 1); spin_lock_init(&tx_sa->lock); + INIT_RCU_WORK(&tx_sa->destroy_work, free_txsa_work); return 0; } @@ -4505,25 +4511,35 @@ static int __init macsec_init(void) { int err; + macsec_wq = alloc_workqueue("macsec", WQ_UNBOUND, 0); + if (!macsec_wq) + return -ENOMEM; + pr_info("MACsec IEEE 802.1AE\n"); err = register_netdevice_notifier(&macsec_notifier); if (err) - return err; + goto err_destroy_wq; err = rtnl_link_register(&macsec_link_ops); if (err) - goto notifier; + goto err_notifier; err = genl_register_family(&macsec_fam); if (err) - goto rtnl; + goto err_rtnl; return 0; -rtnl: +err_rtnl: rtnl_link_unregister(&macsec_link_ops); -notifier: +err_notifier: unregister_netdevice_notifier(&macsec_notifier); +err_destroy_wq: + /* Precautionary, mirrors macsec_exit() to stay safe if work + * ever becomes queueable before this point in the future. + */ + rcu_barrier(); + destroy_workqueue(macsec_wq); return err; } @@ -4533,6 +4549,7 @@ static void __exit macsec_exit(void) rtnl_link_unregister(&macsec_link_ops); unregister_netdevice_notifier(&macsec_notifier); rcu_barrier(); + destroy_workqueue(macsec_wq); } module_init(macsec_init); diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 9f90c598649d..c40fa331836b 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -1689,6 +1689,7 @@ static size_t macvlan_get_size(const struct net_device *dev) + macvlan_get_size_mac(vlan) /* IFLA_MACVLAN_MACADDR */ + nla_total_size(4) /* IFLA_MACVLAN_BC_QUEUE_LEN */ + nla_total_size(4) /* IFLA_MACVLAN_BC_QUEUE_LEN_USED */ + + nla_total_size(4) /* IFLA_MACVLAN_BC_CUTOFF */ ); } diff --git a/drivers/net/mctp/mctp-i2c.c b/drivers/net/mctp/mctp-i2c.c index 15fe4d1163c1..ee2913758e54 100644 --- a/drivers/net/mctp/mctp-i2c.c +++ b/drivers/net/mctp/mctp-i2c.c @@ -496,8 +496,6 @@ static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb) u8 *pecp; int rc; - fs = mctp_i2c_get_tx_flow_state(midev, skb); - hdr = (void *)skb_mac_header(skb); /* Sanity check that packet contents matches skb length, * and can't exceed MCTP_I2C_BUFSZ @@ -509,6 +507,8 @@ static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb) return; } + fs = mctp_i2c_get_tx_flow_state(midev, skb); + if (skb_tailroom(skb) >= 1) { /* Linear case with space, we can just append the PEC */ skb_put(skb, 1); diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig index 516b0d05e16e..c71132f33f84 100644 --- a/drivers/net/mdio/Kconfig +++ b/drivers/net/mdio/Kconfig @@ -147,6 +147,7 @@ config MDIO_OCTEON config MDIO_PIC64HPSC tristate "PIC64-HPSC/HX MDIO interface support" + depends on ARCH_MICROCHIP || COMPILE_TEST depends on HAS_IOMEM && OF_MDIO help This driver supports the MDIO interface found on the PIC64-HPSC/HX diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c index d0361aaf25ef..3f7d31033bae 100644 --- a/drivers/net/net_failover.c +++ b/drivers/net/net_failover.c @@ -502,7 +502,7 @@ static int net_failover_slave_register(struct net_device *slave_dev, /* Align MTU of slave with failover dev */ orig_mtu = slave_dev->mtu; - err = dev_set_mtu(slave_dev, failover_dev->mtu); + err = netif_set_mtu(slave_dev, failover_dev->mtu); if (err) { netdev_err(failover_dev, "unable to change mtu of %s to %u register failed\n", slave_dev->name, failover_dev->mtu); @@ -512,11 +512,11 @@ static int net_failover_slave_register(struct net_device *slave_dev, dev_hold(slave_dev); if (netif_running(failover_dev)) { - err = dev_open(slave_dev, NULL); + err = netif_open(slave_dev, NULL); if (err && (err != -EBUSY)) { netdev_err(failover_dev, "Opening slave %s failed err:%d\n", slave_dev->name, err); - goto err_dev_open; + goto err_netif_open; } } @@ -562,10 +562,10 @@ static int net_failover_slave_register(struct net_device *slave_dev, err_vlan_add: dev_uc_unsync(slave_dev, failover_dev); dev_mc_unsync(slave_dev, failover_dev); - dev_close(slave_dev); -err_dev_open: + netif_close(slave_dev); +err_netif_open: dev_put(slave_dev); - dev_set_mtu(slave_dev, orig_mtu); + netif_set_mtu(slave_dev, orig_mtu); done: return err; } diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 3c9acd6e49e8..57dd6821a8aa 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -497,6 +497,8 @@ static void trim_newline(char *s, size_t maxlen) size_t len; len = strnlen(s, maxlen); + if (!len) + return; if (s[len - 1] == '\n') s[len - 1] = '\0'; } @@ -750,7 +752,7 @@ static ssize_t enabled_store(struct config_item *item, unregister_netcons_consoles(); } - ret = strnlen(buf, count); + ret = count; /* Deferred cleanup */ netconsole_process_cleanups(); out_unlock: @@ -779,7 +781,7 @@ static ssize_t release_store(struct config_item *item, const char *buf, nt->release = release; - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -805,7 +807,7 @@ static ssize_t extended_store(struct config_item *item, const char *buf, goto out_unlock; nt->extended = extended; - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -815,6 +817,13 @@ static ssize_t dev_name_store(struct config_item *item, const char *buf, size_t count) { struct netconsole_target *nt = to_target(item); + size_t len = count; + + /* Account for a trailing newline appended by tools like echo */ + if (len && buf[len - 1] == '\n') + len--; + if (len >= IFNAMSIZ) + return -ENAMETOOLONG; dynamic_netconsole_mutex_lock(); if (nt->state == STATE_ENABLED) { @@ -828,7 +837,7 @@ static ssize_t dev_name_store(struct config_item *item, const char *buf, trim_newline(nt->np.dev_name, IFNAMSIZ); dynamic_netconsole_mutex_unlock(); - return strnlen(buf, count); + return count; } static ssize_t local_port_store(struct config_item *item, const char *buf, @@ -847,7 +856,7 @@ static ssize_t local_port_store(struct config_item *item, const char *buf, ret = kstrtou16(buf, 10, &nt->np.local_port); if (ret < 0) goto out_unlock; - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -869,7 +878,7 @@ static ssize_t remote_port_store(struct config_item *item, ret = kstrtou16(buf, 10, &nt->np.remote_port); if (ret < 0) goto out_unlock; - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -894,7 +903,7 @@ static ssize_t local_ip_store(struct config_item *item, const char *buf, goto out_unlock; nt->np.ipv6 = !!ipv6; - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -919,7 +928,7 @@ static ssize_t remote_ip_store(struct config_item *item, const char *buf, goto out_unlock; nt->np.ipv6 = !!ipv6; - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -955,7 +964,7 @@ static ssize_t remote_mac_store(struct config_item *item, const char *buf, goto out_unlock; memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN); - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -1070,26 +1079,30 @@ static ssize_t userdatum_value_store(struct config_item *item, const char *buf, size_t count) { struct userdatum *udm = to_userdatum(item); + char old_value[MAX_EXTRADATA_VALUE_LEN]; struct netconsole_target *nt; struct userdata *ud; ssize_t ret; - if (count > MAX_EXTRADATA_VALUE_LEN) + if (count >= MAX_EXTRADATA_VALUE_LEN) return -EMSGSIZE; mutex_lock(&netconsole_subsys.su_mutex); dynamic_netconsole_mutex_lock(); - - ret = strscpy(udm->value, buf, sizeof(udm->value)); - if (ret < 0) - goto out_unlock; + /* Snapshot for rollback if update_userdata() fails below */ + strscpy(old_value, udm->value, sizeof(old_value)); + /* count is bounded above, so strscpy() cannot truncate here */ + strscpy(udm->value, buf, sizeof(udm->value)); trim_newline(udm->value, sizeof(udm->value)); ud = to_userdata(item->ci_parent); nt = userdata_to_target(ud); ret = update_userdata(nt); - if (ret < 0) + if (ret < 0) { + /* Restore the previous value so it matches the live payload */ + strscpy(udm->value, old_value, sizeof(udm->value)); goto out_unlock; + } ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); @@ -1131,7 +1144,7 @@ static ssize_t sysdata_msgid_enabled_store(struct config_item *item, disable_sysdata_feature(nt, SYSDATA_MSGID); unlock_ok: - ret = strnlen(buf, count); + ret = count; dynamic_netconsole_mutex_unlock(); mutex_unlock(&netconsole_subsys.su_mutex); return ret; @@ -1160,7 +1173,7 @@ static ssize_t sysdata_release_enabled_store(struct config_item *item, disable_sysdata_feature(nt, SYSDATA_RELEASE); unlock_ok: - ret = strnlen(buf, count); + ret = count; dynamic_netconsole_mutex_unlock(); mutex_unlock(&netconsole_subsys.su_mutex); return ret; @@ -1189,7 +1202,7 @@ static ssize_t sysdata_taskname_enabled_store(struct config_item *item, disable_sysdata_feature(nt, SYSDATA_TASKNAME); unlock_ok: - ret = strnlen(buf, count); + ret = count; dynamic_netconsole_mutex_unlock(); mutex_unlock(&netconsole_subsys.su_mutex); return ret; @@ -1223,7 +1236,7 @@ static ssize_t sysdata_cpu_nr_enabled_store(struct config_item *item, disable_sysdata_feature(nt, SYSDATA_CPU_NR); unlock_ok: - ret = strnlen(buf, count); + ret = count; dynamic_netconsole_mutex_unlock(); mutex_unlock(&netconsole_subsys.su_mutex); return ret; diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index 1e06e781c835..f00fc2f9ebde 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -829,7 +829,7 @@ static struct sk_buff *nsim_dev_trap_skb_build(void) skb->protocol = htons(ETH_P_IP); skb_set_network_header(skb, skb->len); - iph = skb_put(skb, sizeof(struct iphdr)); + iph = skb_put_zero(skb, sizeof(struct iphdr)); iph->protocol = IPPROTO_UDP; iph->saddr = in_aton("192.0.2.1"); iph->daddr = in_aton("198.51.100.1"); diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index e1541ca76715..a750768912b5 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -185,7 +185,9 @@ out_drop_cnt: return NETDEV_TX_OK; } -static void nsim_set_rx_mode(struct net_device *dev) +static void nsim_set_rx_mode(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { } @@ -623,7 +625,7 @@ static const struct net_shaper_ops nsim_shaper_ops = { static const struct net_device_ops nsim_netdev_ops = { .ndo_start_xmit = nsim_start_xmit, - .ndo_set_rx_mode = nsim_set_rx_mode, + .ndo_set_rx_mode_async = nsim_set_rx_mode, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = nsim_change_mtu, @@ -648,7 +650,7 @@ static const struct net_device_ops nsim_netdev_ops = { static const struct net_device_ops nsim_vf_netdev_ops = { .ndo_start_xmit = nsim_start_xmit, - .ndo_set_rx_mode = nsim_set_rx_mode, + .ndo_set_rx_mode_async = nsim_set_rx_mode, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = nsim_change_mtu, @@ -1180,7 +1182,8 @@ void nsim_destroy(struct netdevsim *ns) unregister_netdevice_notifier_dev_net(ns->netdev, &ns->nb, &ns->nn); - nsim_psp_uninit(ns); + if (nsim_dev_port_is_pf(ns->nsim_dev_port)) + nsim_psp_uninit(ns); rtnl_lock(); peer = rtnl_dereference(ns->peer); diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 7e129dddbbe7..d909c4160ea1 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -120,7 +120,9 @@ struct netdevsim { u64_stats_t tx_packets; u64_stats_t tx_bytes; struct u64_stats_sync syncp; - struct psp_dev *dev; + struct psp_dev __rcu *dev; + struct dentry *rereg; + struct mutex rereg_lock; u32 spi; u32 assoc_cnt; } psp; diff --git a/drivers/net/netdevsim/psp.c b/drivers/net/netdevsim/psp.c index 0b4d717253b0..6936ecb8173e 100644 --- a/drivers/net/netdevsim/psp.c +++ b/drivers/net/netdevsim/psp.c @@ -19,6 +19,7 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, struct netdevsim *peer_ns, struct skb_ext **psp_ext) { enum skb_drop_reason rc = 0; + struct psp_dev *peer_psd; struct psp_assoc *pas; struct net *net; void **ptr; @@ -48,7 +49,8 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, } /* Now pretend we just received this frame */ - if (peer_ns->psp.dev->config.versions & (1 << pas->version)) { + peer_psd = rcu_dereference(peer_ns->psp.dev); + if (peer_psd && peer_psd->config.versions & (1 << pas->version)) { bool strip_icv = false; u8 generation; @@ -61,8 +63,7 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, skb_ext_reset(skb); skb->mac_len = ETH_HLEN; - if (psp_dev_rcv(skb, peer_ns->psp.dev->id, generation, - strip_icv)) { + if (psp_dev_rcv(skb, peer_psd->id, generation, strip_icv)) { rc = SKB_DROP_REASON_PSP_OUTPUT; goto out_unlock; } @@ -209,11 +210,26 @@ static struct psp_dev_caps nsim_psp_caps = { .assoc_drv_spc = sizeof(void *), }; +static void __nsim_psp_uninit(struct netdevsim *ns, bool teardown) +{ + struct psp_dev *psd; + + psd = rcu_dereference_protected(ns->psp.dev, + teardown || + lockdep_is_held(&ns->psp.rereg_lock)); + if (psd) { + rcu_assign_pointer(ns->psp.dev, NULL); + synchronize_rcu(); + psp_dev_unregister(psd); + } + WARN_ON(ns->psp.assoc_cnt); +} + void nsim_psp_uninit(struct netdevsim *ns) { - if (!IS_ERR(ns->psp.dev)) - psp_dev_unregister(ns->psp.dev); - WARN_ON(ns->psp.assoc_cnt); + debugfs_remove(ns->psp.rereg); + mutex_destroy(&ns->psp.rereg_lock); + __nsim_psp_uninit(ns, true); } static ssize_t @@ -221,14 +237,23 @@ nsim_psp_rereg_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) { struct netdevsim *ns = file->private_data; - int err; + struct psp_dev *psd; + ssize_t ret; - nsim_psp_uninit(ns); + mutex_lock(&ns->psp.rereg_lock); + __nsim_psp_uninit(ns, false); - ns->psp.dev = psp_dev_create(ns->netdev, &nsim_psp_ops, - &nsim_psp_caps, ns); - err = PTR_ERR_OR_ZERO(ns->psp.dev); - return err ?: count; + psd = psp_dev_create(ns->netdev, &nsim_psp_ops, &nsim_psp_caps, ns); + if (IS_ERR(psd)) { + ret = PTR_ERR(psd); + goto out; + } + + rcu_assign_pointer(ns->psp.dev, psd); + ret = count; +out: + mutex_unlock(&ns->psp.rereg_lock); + return ret; } static const struct file_operations nsim_psp_rereg_fops = { @@ -241,14 +266,16 @@ static const struct file_operations nsim_psp_rereg_fops = { int nsim_psp_init(struct netdevsim *ns) { struct dentry *ddir = ns->nsim_dev_port->ddir; - int err; + struct psp_dev *psd; - ns->psp.dev = psp_dev_create(ns->netdev, &nsim_psp_ops, - &nsim_psp_caps, ns); - err = PTR_ERR_OR_ZERO(ns->psp.dev); - if (err) - return err; + psd = psp_dev_create(ns->netdev, &nsim_psp_ops, &nsim_psp_caps, ns); + if (IS_ERR(psd)) + return PTR_ERR(psd); - debugfs_create_file("psp_rereg", 0200, ddir, ns, &nsim_psp_rereg_fops); + rcu_assign_pointer(ns->psp.dev, psd); + + mutex_init(&ns->psp.rereg_lock); + ns->psp.rereg = debugfs_create_file("psp_rereg", 0200, ddir, ns, + &nsim_psp_rereg_fops); return 0; } diff --git a/drivers/net/netkit.c b/drivers/net/netkit.c index 7b56a7ad7a49..5e2eecc3165d 100644 --- a/drivers/net/netkit.c +++ b/drivers/net/netkit.c @@ -186,7 +186,9 @@ static int netkit_get_iflink(const struct net_device *dev) return iflink; } -static void netkit_set_multicast(struct net_device *dev) +static void netkit_set_multicast(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { /* Nothing to do, we receive whatever gets pushed to us! */ } @@ -330,7 +332,7 @@ static const struct net_device_ops netkit_netdev_ops = { .ndo_open = netkit_open, .ndo_stop = netkit_close, .ndo_start_xmit = netkit_xmit, - .ndo_set_rx_mode = netkit_set_multicast, + .ndo_set_rx_mode_async = netkit_set_multicast, .ndo_set_rx_headroom = netkit_set_headroom, .ndo_set_mac_address = netkit_set_macaddr, .ndo_get_iflink = netkit_get_iflink, diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index db43a1f8a07a..22c555dd962e 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -85,17 +85,24 @@ static void ovpn_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb) skb_scrub_packet(skb, true); /* network header reset in ovpn_decrypt_post() */ + skb_reset_mac_header(skb); skb_reset_transport_header(skb); skb_reset_inner_headers(skb); /* cause packet to be "received" by the interface */ pkt_len = skb->len; + /* we may get here in process context in case of TCP connections, + * therefore we have to disable BHs to ensure gro_cells_receive() + * and dev_dstats_rx_add() do not get corrupted or enter deadlock + */ + local_bh_disable(); ret = gro_cells_receive(&peer->ovpn->gro_cells, skb); if (likely(ret == NET_RX_SUCCESS)) { /* update RX stats with the size of decrypted packet */ ovpn_peer_stats_increment_rx(&peer->vpn_stats, pkt_len); dev_dstats_rx_add(peer->ovpn->dev, pkt_len); } + local_bh_enable(); } void ovpn_decrypt_post(void *data, int ret) diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index 5198d66dbbc0..b64beade8dd9 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -563,6 +563,15 @@ void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, } EXPORT_SYMBOL_GPL(bcm_phy_get_stats); +void bcm_phy_update_stats_shadow(struct phy_device *phydev, u64 *shadow) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) + bcm_phy_get_stat(phydev, shadow, i); +} +EXPORT_SYMBOL_GPL(bcm_phy_update_stats_shadow); + void bcm_phy_r_rc_cal_reset(struct phy_device *phydev) { /* Reset R_CAL/RC_CAL Engine */ diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h index bceddbc860eb..bba94ce96195 100644 --- a/drivers/net/phy/bcm-phy-lib.h +++ b/drivers/net/phy/bcm-phy-lib.h @@ -85,6 +85,7 @@ int bcm_phy_get_sset_count(struct phy_device *phydev); void bcm_phy_get_strings(struct phy_device *phydev, u8 *data); void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, struct ethtool_stats *stats, u64 *data); +void bcm_phy_update_stats_shadow(struct phy_device *phydev, u64 *shadow); void bcm_phy_r_rc_cal_reset(struct phy_device *phydev); int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev); int bcm_phy_enable_jumbo(struct phy_device *phydev); diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 00e8fa14aa77..71a163f62c0e 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -807,6 +807,17 @@ static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev, bcm_phy_get_stats(phydev, priv->stats, stats, data); } +static int bcm7xxx_28nm_suspend(struct phy_device *phydev) +{ + struct bcm7xxx_phy_priv *priv = phydev->priv; + + mutex_lock(&phydev->lock); + bcm_phy_update_stats_shadow(phydev, priv->stats); + mutex_unlock(&phydev->lock); + + return genphy_suspend(phydev); +} + static int bcm7xxx_28nm_probe(struct phy_device *phydev) { struct bcm7xxx_phy_priv *priv; @@ -849,6 +860,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev) .flags = PHY_IS_INTERNAL, \ .config_init = bcm7xxx_28nm_config_init, \ .resume = bcm7xxx_28nm_resume, \ + .suspend = bcm7xxx_28nm_suspend, \ .get_tunable = bcm7xxx_28nm_get_tunable, \ .set_tunable = bcm7xxx_28nm_set_tunable, \ .get_sset_count = bcm_phy_get_sset_count, \ @@ -866,6 +878,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev) .flags = PHY_IS_INTERNAL, \ .config_init = bcm7xxx_28nm_ephy_config_init, \ .resume = bcm7xxx_28nm_ephy_resume, \ + .suspend = bcm7xxx_28nm_suspend, \ .get_sset_count = bcm_phy_get_sset_count, \ .get_strings = bcm_phy_get_strings, \ .get_stats = bcm7xxx_28nm_get_phy_stats, \ @@ -902,6 +915,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev) .config_aneg = genphy_config_aneg, \ .read_status = genphy_read_status, \ .resume = bcm7xxx_16nm_ephy_resume, \ + .suspend = bcm7xxx_28nm_suspend, \ } static struct phy_driver bcm7xxx_driver[] = { diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index bf0c6a04481e..d1a4edb34ad2 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -592,8 +592,13 @@ static int bcm54xx_set_wakeup_irq(struct phy_device *phydev, bool state) static int bcm54xx_suspend(struct phy_device *phydev) { + struct bcm54xx_phy_priv *priv = phydev->priv; int ret = 0; + mutex_lock(&phydev->lock); + bcm_phy_update_stats_shadow(phydev, priv->stats); + mutex_unlock(&phydev->lock); + bcm54xx_ptp_stop(phydev); /* Acknowledge any Wake-on-LAN interrupt prior to suspend */ diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c index 1f381d7b13ff..96a7d255f50f 100644 --- a/drivers/net/phy/dp83869.c +++ b/drivers/net/phy/dp83869.c @@ -31,6 +31,7 @@ #define DP83869_RGMIICTL 0x0032 #define DP83869_STRAP_STS1 0x006e #define DP83869_RGMIIDCTL 0x0086 +#define DP83869_ANA_PLL_PROG_PI 0x00c6 #define DP83869_RXFCFG 0x0134 #define DP83869_RXFPMD1 0x0136 #define DP83869_RXFPMD2 0x0137 @@ -826,12 +827,22 @@ static int dp83869_config_init(struct phy_device *phydev) dp83869_config_port_mirroring(phydev); /* Clock output selection if muxing property is set */ - if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK) + if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK) { + /* + * Table 7-121 in datasheet says we have to set register 0xc6 + * to value 0x10 before CLK_O_SEL can be modified. + */ + ret = phy_write_mmd(phydev, DP83869_DEVADDR, + DP83869_ANA_PLL_PROG_PI, 0x10); + if (ret) + return ret; + ret = phy_modify_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG, DP83869_IO_MUX_CFG_CLK_O_SEL_MASK, dp83869->clk_output_sel << DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT); + } if (phy_interface_is_rgmii(phydev)) { ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL, diff --git a/drivers/net/phy/dp83tc811.c b/drivers/net/phy/dp83tc811.c index e480c2a07450..252fb12b3e68 100644 --- a/drivers/net/phy/dp83tc811.c +++ b/drivers/net/phy/dp83tc811.c @@ -393,6 +393,7 @@ static struct phy_driver dp83811_driver[] = { .config_init = dp83811_config_init, .config_aneg = dp83811_config_aneg, .soft_reset = dp83811_phy_reset, + .get_features = genphy_c45_pma_read_ext_abilities, .get_wol = dp83811_get_wol, .set_wol = dp83811_set_wol, .config_intr = dp83811_config_intr, diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 2aa1dedd21b8..e211a523c258 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -4548,6 +4548,13 @@ static int lan8814_config_init(struct phy_device *phydev) struct kszphy_priv *lan8814 = phydev->priv; int ret; + if (phy_package_init_once(phydev)) + /* Reset the PHY */ + lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, + LAN8814_QSGMII_SOFT_RESET, + LAN8814_QSGMII_SOFT_RESET_BIT, + LAN8814_QSGMII_SOFT_RESET_BIT); + /* Based on the interface type select how the advertise ability is * encoded, to set as SGMII or as USGMII. */ @@ -4655,13 +4662,7 @@ static int lan8814_probe(struct phy_device *phydev) priv->is_ptp_available = err == LAN8814_REV_LAN8814 || err == LAN8814_REV_LAN8818; - if (phy_package_init_once(phydev)) { - /* Reset the PHY */ - lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, - LAN8814_QSGMII_SOFT_RESET, - LAN8814_QSGMII_SOFT_RESET_BIT, - LAN8814_QSGMII_SOFT_RESET_BIT); - + if (phy_package_probe_once(phydev)) { err = lan8814_release_coma_mode(phydev); if (err) return err; diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index b0d3bc49c685..57c68efa5ff8 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -2245,7 +2245,7 @@ ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) */ static void __ppp_decompress_proto(struct sk_buff *skb) { - if (skb->data[0] & 0x01) + if (ppp_skb_is_compressed_proto(skb)) *(u8 *)skb_push(skb, 1) = 0x00; } diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index d546a7af0d54..bdd61c504a1c 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -393,7 +393,7 @@ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev, if (skb_mac_header_len(skb) < ETH_HLEN) goto drop; - if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr))) + if (!pskb_may_pull(skb, PPPOE_SES_HLEN)) goto drop; ph = pppoe_hdr(skb); @@ -403,6 +403,12 @@ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev, if (skb->len < len) goto drop; + /* skb->data points to the PPP protocol header after skb_pull_rcsum. + * Drop PFC frames. + */ + if (ppp_skb_is_compressed_proto(skb)) + goto drop; + if (pskb_trim_rcsum(skb, len)) goto drop; diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c index f6b94ac7a68a..87aa4f4e9724 100644 --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c @@ -1170,6 +1170,7 @@ struct pse_irq { struct pse_controller_dev *pcdev; struct pse_irq_desc desc; unsigned long *notifs; + unsigned long *notifs_mask; }; /** @@ -1247,7 +1248,6 @@ static int pse_set_config_isr(struct pse_controller_dev *pcdev, int id, static irqreturn_t pse_isr(int irq, void *data) { struct pse_controller_dev *pcdev; - unsigned long notifs_mask = 0; struct pse_irq_desc *desc; struct pse_irq *h = data; int ret, i; @@ -1257,14 +1257,15 @@ static irqreturn_t pse_isr(int irq, void *data) /* Clear notifs mask */ memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs)); + bitmap_zero(h->notifs_mask, pcdev->nr_lines); mutex_lock(&pcdev->lock); - ret = desc->map_event(irq, pcdev, h->notifs, ¬ifs_mask); - if (ret || !notifs_mask) { + ret = desc->map_event(irq, pcdev, h->notifs, h->notifs_mask); + if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) { mutex_unlock(&pcdev->lock); return IRQ_NONE; } - for_each_set_bit(i, ¬ifs_mask, pcdev->nr_lines) { + for_each_set_bit(i, h->notifs_mask, pcdev->nr_lines) { unsigned long notifs, rnotifs; struct pse_ntf ntf = {}; @@ -1340,6 +1341,10 @@ int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq, if (!h->notifs) return -ENOMEM; + h->notifs_mask = devm_bitmap_zalloc(dev, pcdev->nr_lines, GFP_KERNEL); + if (!h->notifs_mask) + return -ENOMEM; + ret = devm_request_threaded_irq(dev, irq, NULL, pse_isr, IRQF_ONESHOT | irq_flags, irq_name, h); diff --git a/drivers/net/slip/slhc.c b/drivers/net/slip/slhc.c index e3c785da3eef..1a9b27d5e256 100644 --- a/drivers/net/slip/slhc.c +++ b/drivers/net/slip/slhc.c @@ -80,9 +80,9 @@ #include static unsigned char *encode(unsigned char *cp, unsigned short n); -static long decode(unsigned char **cpp); +static long decode(unsigned char **cpp, const unsigned char *end); static unsigned char * put16(unsigned char *cp, unsigned short x); -static unsigned short pull16(unsigned char **cpp); +static long pull16(unsigned char **cpp, const unsigned char *end); /* Allocate compression data structure * slots must be in range 0 to 255 (zero meaning no compression) @@ -190,30 +190,34 @@ encode(unsigned char *cp, unsigned short n) return cp; } -/* Pull a 16-bit integer in host order from buffer in network byte order */ -static unsigned short -pull16(unsigned char **cpp) +/* Pull a 16-bit integer in host order from buffer in network byte order. + * Returns -1 if the buffer is exhausted, otherwise the 16-bit value. + */ +static long +pull16(unsigned char **cpp, const unsigned char *end) { - short rval; + long rval; + if (*cpp + 2 > end) + return -1; rval = *(*cpp)++; rval <<= 8; rval |= *(*cpp)++; return rval; } -/* Decode a number */ +/* Decode a number. Returns -1 if the buffer is exhausted. */ static long -decode(unsigned char **cpp) +decode(unsigned char **cpp, const unsigned char *end) { int x; + if (*cpp >= end) + return -1; x = *(*cpp)++; - if(x == 0){ - return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */ - } else { - return x & 0xff; /* -1 if PULLCHAR returned error */ - } + if (x == 0) + return pull16(cpp, end); + return x & 0xff; } /* @@ -499,6 +503,7 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) struct cstate *cs; int len, hdrlen; unsigned char *cp = icp; + const unsigned char *end = icp + isize; /* We've got a compressed packet; read the change byte */ comp->sls_i_compressed++; @@ -506,6 +511,8 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) comp->sls_i_error++; return 0; } + if (!comp->rstate) + goto bad; changes = *cp++; if(changes & NEW_C){ /* Make sure the state index is in range, then grab the state. @@ -534,6 +541,8 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) thp = &cs->cs_tcp; ip = &cs->cs_ip; + if (cp + 2 > end) + goto bad; thp->check = *(__sum16 *)cp; cp += 2; @@ -564,26 +573,26 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) default: if(changes & NEW_U){ thp->urg = 1; - if((x = decode(&cp)) == -1) { + if((x = decode(&cp, end)) == -1) { goto bad; } thp->urg_ptr = htons(x); } else thp->urg = 0; if(changes & NEW_W){ - if((x = decode(&cp)) == -1) { + if((x = decode(&cp, end)) == -1) { goto bad; } thp->window = htons( ntohs(thp->window) + x); } if(changes & NEW_A){ - if((x = decode(&cp)) == -1) { + if((x = decode(&cp, end)) == -1) { goto bad; } thp->ack_seq = htonl( ntohl(thp->ack_seq) + x); } if(changes & NEW_S){ - if((x = decode(&cp)) == -1) { + if((x = decode(&cp, end)) == -1) { goto bad; } thp->seq = htonl( ntohl(thp->seq) + x); @@ -591,7 +600,7 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) break; } if(changes & NEW_I){ - if((x = decode(&cp)) == -1) { + if((x = decode(&cp, end)) == -1) { goto bad; } ip->id = htons (ntohs (ip->id) + x); @@ -649,6 +658,10 @@ slhc_remember(struct slcompress *comp, unsigned char *icp, int isize) struct cstate *cs; unsigned int ihl; + if (!comp->rstate) { + comp->sls_i_error++; + return slhc_toss(comp); + } /* The packet is shorter than a legal IP header. * Also make sure isize is positive. */ diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index df0bcfedddbc..293ef80c4e30 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -756,6 +756,7 @@ static void ax88772_mac_link_down(struct phylink_config *config, struct usbnet *dev = netdev_priv(to_net_dev(config->dev)); asix_write_medium_mode(dev, 0, 0); + usbnet_link_change(dev, false, false); } static void ax88772_mac_link_up(struct phylink_config *config, @@ -786,6 +787,7 @@ static void ax88772_mac_link_up(struct phylink_config *config, m |= AX_MEDIUM_RFC; asix_write_medium_mode(dev, m, 0); + usbnet_link_change(dev, true, false); } static const struct phylink_mac_ops ax88772_phylink_mac_ops = { diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index bb9929727eb9..0223a172851e 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -2012,6 +2012,14 @@ static const struct usb_device_id cdc_devs[] = { .driver_info = (unsigned long)&apple_private_interface_info, }, + /* Mac */ + { USB_DEVICE_INTERFACE_NUMBER(0x05ac, 0x1905, 0), + .driver_info = (unsigned long)&apple_private_interface_info, + }, + { USB_DEVICE_INTERFACE_NUMBER(0x05ac, 0x1905, 2), + .driver_info = (unsigned long)&apple_private_interface_info, + }, + /* Ericsson MBM devices like F5521gw */ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_VENDOR, diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 7337bf1b7d6a..1ace1d2398c9 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -10138,6 +10138,7 @@ static const struct usb_device_id rtl8152_table[] = { { USB_DEVICE(VENDOR_ID_DELL, 0xb097) }, { USB_DEVICE(VENDOR_ID_ASUS, 0x1976) }, { USB_DEVICE(VENDOR_ID_TRENDNET, 0xe02b) }, + { USB_DEVICE(VENDOR_ID_TRENDNET, 0xe02c) }, {} }; diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index 4cda0643afb6..c880c95c41a5 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -683,6 +683,7 @@ static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb, struct net_device *netdev) { rtl8150_t *dev = netdev_priv(netdev); + unsigned int skb_len; int count, res; /* pad the frame and ensure terminating USB packet, datasheet 9.2.3 */ @@ -694,6 +695,8 @@ static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } + skb_len = skb->len; + netif_stop_queue(netdev); dev->tx_skb = skb; usb_fill_bulk_urb(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), @@ -707,9 +710,16 @@ static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb, netdev->stats.tx_errors++; netif_start_queue(netdev); } + /* + * The URB was not submitted, so write_bulk_callback() will + * never run to free dev->tx_skb. Drop the skb here and + * clear tx_skb to avoid leaving a stale pointer. + */ + dev->tx_skb = NULL; + dev_kfree_skb_any(skb); } else { netdev->stats.tx_packets++; - netdev->stats.tx_bytes += skb->len; + netdev->stats.tx_bytes += skb_len; netif_trans_update(netdev); } diff --git a/drivers/net/veth.c b/drivers/net/veth.c index e35df717e65e..0cfb19b760dd 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -972,7 +972,8 @@ static int veth_poll(struct napi_struct *napi, int budget) /* NAPI functions as RCU section */ peer_dev = rcu_dereference_check(priv->peer, rcu_read_lock_bh_held()); - peer_txq = peer_dev ? netdev_get_tx_queue(peer_dev, queue_idx) : NULL; + peer_txq = (peer_dev && queue_idx < peer_dev->real_num_tx_queues) ? + netdev_get_tx_queue(peer_dev, queue_idx) : NULL; xdp_set_return_frame_no_direct(); done = veth_xdp_rcv(rq, budget, &bq, &stats); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index bfb566fecb92..f4adcfee7a80 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -3759,6 +3759,12 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs) queue_pairs); return -EINVAL; } + + /* Keep max_tx_vq in sync so that a later RSS command does not + * revert queue_pairs to a stale value. + */ + if (vi->has_rss) + vi->rss_trailer.max_tx_vq = cpu_to_le16(queue_pairs); succ: vi->curr_queue_pairs = queue_pairs; if (dev->flags & IFF_UP) { diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 2cf2dbd1c12f..46209917ae4d 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -1034,6 +1034,7 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev, err: port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE; + synchronize_net(); return ret; } @@ -1053,10 +1054,16 @@ static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev, } /* inverse of do_vrf_add_slave */ -static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev) +static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev, + bool needs_sync) { netdev_upper_dev_unlink(port_dev, dev); port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE; + /* Make sure that concurrent RCU readers that identified the device + * as a VRF port see a VRF master or no master at all. + */ + if (needs_sync) + synchronize_net(); cycle_netdev(port_dev, NULL); @@ -1065,7 +1072,7 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev) static int vrf_del_slave(struct net_device *dev, struct net_device *port_dev) { - return do_vrf_del_slave(dev, port_dev); + return do_vrf_del_slave(dev, port_dev, true); } static void vrf_dev_uninit(struct net_device *dev) @@ -1619,7 +1626,7 @@ static void vrf_dellink(struct net_device *dev, struct list_head *head) struct list_head *iter; netdev_for_each_lower_dev(dev, port_dev, iter) - vrf_del_slave(dev, port_dev); + do_vrf_del_slave(dev, port_dev, false); vrf_map_unregister_dev(dev); @@ -1751,7 +1758,7 @@ static int vrf_device_event(struct notifier_block *unused, goto out; vrf_dev = netdev_master_upper_dev_get(dev); - vrf_del_slave(vrf_dev, dev); + do_vrf_del_slave(vrf_dev, dev, false); } out: return NOTIFY_DONE; diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c index 3bd57527b1be..809f21fb93f5 100644 --- a/drivers/net/wan/fsl_ucc_hdlc.c +++ b/drivers/net/wan/fsl_ucc_hdlc.c @@ -740,6 +740,8 @@ static int uhdlc_open(struct net_device *dev) static void uhdlc_memclean(struct ucc_hdlc_private *priv) { + int i; + qe_muram_free(ioread16be(&priv->ucc_pram->riptr)); qe_muram_free(ioread16be(&priv->ucc_pram->tiptr)); @@ -770,14 +772,14 @@ static void uhdlc_memclean(struct ucc_hdlc_private *priv) kfree(priv->rx_skbuff); priv->rx_skbuff = NULL; + for (i = 0; i < TX_BD_RING_LEN; i++) { + dev_kfree_skb(priv->tx_skbuff[i]); + priv->tx_skbuff[i] = NULL; + } + kfree(priv->tx_skbuff); priv->tx_skbuff = NULL; - if (priv->uf_regs) { - iounmap(priv->uf_regs); - priv->uf_regs = NULL; - } - if (priv->uccf) { ucc_fast_free(priv->uccf); priv->uccf = NULL; @@ -1255,12 +1257,12 @@ static void ucc_hdlc_remove(struct platform_device *pdev) uhdlc_memclean(priv); - if (priv->utdm->si_regs) { + if (priv->utdm && priv->utdm->si_regs) { iounmap(priv->utdm->si_regs); priv->utdm->si_regs = NULL; } - if (priv->utdm->siram) { + if (priv->utdm && priv->utdm->siram) { iounmap(priv->utdm->siram); priv->utdm->siram = NULL; } diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index 876aed765833..efb9f022d8c6 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -46,6 +46,7 @@ config ATH10K_SNOC depends on ARCH_QCOM || COMPILE_TEST depends on QCOM_SMEM depends on QCOM_RPROC_COMMON || QCOM_RPROC_COMMON=n + select POWER_SEQUENCING select QCOM_SCM select QCOM_QMI_HELPERS help diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 2519e2400d58..980a12fb2c6e 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -1838,10 +1838,22 @@ static struct ath12k_hw_group *ath12k_core_hw_group_alloc(struct ath12k_base *ab return ag; } +static void ath12k_core_free_wsi_info(struct ath12k_hw_group *ag) +{ + int i; + + for (i = 0; i < ag->num_devices; i++) { + of_node_put(ag->wsi_node[i]); + ag->wsi_node[i] = NULL; + } + ag->num_devices = 0; +} + static void ath12k_core_hw_group_free(struct ath12k_hw_group *ag) { mutex_lock(&ath12k_hw_group_mutex); + ath12k_core_free_wsi_info(ag); list_del(&ag->list); kfree(ag); @@ -1867,52 +1879,59 @@ static struct ath12k_hw_group *ath12k_core_hw_group_find_by_dt(struct ath12k_bas static int ath12k_core_get_wsi_info(struct ath12k_hw_group *ag, struct ath12k_base *ab) { - struct device_node *wsi_dev = ab->dev->of_node, *next_wsi_dev; - struct device_node *tx_endpoint, *next_rx_endpoint; - int device_count = 0; + struct device_node *next_wsi_dev; + int device_count = 0, ret = 0; + struct device_node *wsi_dev; - next_wsi_dev = wsi_dev; - - if (!next_wsi_dev) + wsi_dev = of_node_get(ab->dev->of_node); + if (!wsi_dev) return -ENODEV; do { - ag->wsi_node[device_count] = next_wsi_dev; + if (device_count >= ATH12K_MAX_DEVICES) { + ath12k_warn(ab, "device count in DT %d is more than limit %d\n", + device_count, ATH12K_MAX_DEVICES); + ret = -EINVAL; + break; + } - tx_endpoint = of_graph_get_endpoint_by_regs(next_wsi_dev, 0, -1); + ag->wsi_node[device_count++] = of_node_get(wsi_dev); + + struct device_node *tx_endpoint __free(device_node) = + of_graph_get_endpoint_by_regs(wsi_dev, 0, -1); if (!tx_endpoint) { - of_node_put(next_wsi_dev); - return -ENODEV; + ret = -ENODEV; + break; } - next_rx_endpoint = of_graph_get_remote_endpoint(tx_endpoint); + struct device_node *next_rx_endpoint __free(device_node) = + of_graph_get_remote_endpoint(tx_endpoint); if (!next_rx_endpoint) { - of_node_put(next_wsi_dev); - of_node_put(tx_endpoint); - return -ENODEV; + ret = -ENODEV; + break; } - of_node_put(tx_endpoint); - of_node_put(next_wsi_dev); - next_wsi_dev = of_graph_get_port_parent(next_rx_endpoint); if (!next_wsi_dev) { - of_node_put(next_rx_endpoint); - return -ENODEV; + ret = -ENODEV; + break; } - of_node_put(next_rx_endpoint); + of_node_put(wsi_dev); + wsi_dev = next_wsi_dev; + } while (ab->dev->of_node != wsi_dev); - device_count++; - if (device_count > ATH12K_MAX_DEVICES) { - ath12k_warn(ab, "device count in DT %d is more than limit %d\n", - device_count, ATH12K_MAX_DEVICES); - of_node_put(next_wsi_dev); - return -EINVAL; + if (ret) { + while (--device_count >= 0) { + of_node_put(ag->wsi_node[device_count]); + ag->wsi_node[device_count] = NULL; } - } while (wsi_dev != next_wsi_dev); - of_node_put(next_wsi_dev); + of_node_put(wsi_dev); + return ret; + } + + of_node_put(wsi_dev); ag->num_devices = device_count; return 0; @@ -1983,9 +2002,9 @@ static struct ath12k_hw_group *ath12k_core_hw_group_assign(struct ath12k_base *a ath12k_core_get_wsi_index(ag, ab)) { ath12k_dbg(ab, ATH12K_DBG_BOOT, "unable to get wsi info from dt, grouping single device"); + ath12k_core_free_wsi_info(ag); ag->id = ATH12K_INVALID_GROUP_ID; ag->num_devices = 1; - memset(ag->wsi_node, 0, sizeof(ag->wsi_node)); wsi->index = 0; } diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 250459facff3..b108ccd0f637 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -565,6 +565,9 @@ static int ath12k_dp_prepare_reo_update_elem(struct ath12k_dp *dp, lockdep_assert_held(&dp->dp_lock); + if (!peer->primary_link) + return 0; + elem = kzalloc_obj(*elem, GFP_ATOMIC); if (!elem) return -ENOMEM; @@ -1337,7 +1340,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc bool is_mcbc = rxcb->is_mcbc; bool is_eapol = rxcb->is_eapol; - peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rx_info->peer_id); + peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id); pubsta = peer ? peer->sta : NULL; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index fbdfe6424fd7..df2334f3bad6 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -788,7 +788,7 @@ struct ath12k_link_vif *ath12k_mac_get_arvif(struct ath12k *ar, u32 vdev_id) /* To use the arvif returned, caller must have held rcu read lock. */ - WARN_ON(!rcu_read_lock_any_held()); + lockdep_assert_in_rcu_read_lock(); arvif_iter.vdev_id = vdev_id; arvif_iter.ar = ar; diff --git a/drivers/net/wireless/ath/ath12k/p2p.c b/drivers/net/wireless/ath/ath12k/p2p.c index 59589748f1a8..19ebcd1d8eb2 100644 --- a/drivers/net/wireless/ath/ath12k/p2p.c +++ b/drivers/net/wireless/ath/ath12k/p2p.c @@ -123,7 +123,7 @@ static void ath12k_p2p_noa_update_vdev_iter(void *data, u8 *mac, struct ath12k_p2p_noa_arg *arg = data; struct ath12k_link_vif *arvif; - WARN_ON(!rcu_read_lock_any_held()); + lockdep_assert_in_rcu_read_lock(); arvif = &ahvif->deflink; if (!arvif->is_created || arvif->ar != arg->ar || arvif->vdev_id != arg->vdev_id) return; diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 65a05a9520ff..b5e904a55aea 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -9778,7 +9778,7 @@ static void ath12k_wmi_rssi_dbm_conversion_params_info_event(struct ath12k_base *ab, struct sk_buff *skb) { - struct ath12k_wmi_rssi_dbm_conv_info_arg rssi_info; + struct ath12k_wmi_rssi_dbm_conv_info_arg rssi_info = {}; struct ath12k *ar; s32 noise_floor; u32 pdev_id; @@ -10251,7 +10251,7 @@ int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, struct wmi_hw_data_filter_a { struct wmi_hw_data_filter_cmd *cmd; struct sk_buff *skb; - int len; + int ret, len; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10275,7 +10275,13 @@ int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, struct wmi_hw_data_filter_a "wmi hw data filter enable %d filter_bitmap 0x%x\n", arg->enable, arg->hw_filter_bitmap); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_HW_DATA_FILTER_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) @@ -10283,6 +10289,7 @@ int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) struct wmi_wow_host_wakeup_cmd *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10295,14 +10302,20 @@ int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow host wakeup ind\n"); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_enable(struct ath12k *ar) { struct wmi_wow_enable_cmd *cmd; struct sk_buff *skb; - int len; + int ret, len; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10317,7 +10330,13 @@ int ath12k_wmi_wow_enable(struct ath12k *ar) cmd->pause_iface_config = cpu_to_le32(WOW_IFACE_PAUSE_ENABLED); ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow enable\n"); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, @@ -10327,6 +10346,7 @@ int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, struct wmi_wow_add_del_event_cmd *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10343,7 +10363,13 @@ int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow add wakeup event %s enable %d vdev_id %d\n", wow_wakeup_event(event), enable, vdev_id); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, @@ -10356,6 +10382,7 @@ int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, struct sk_buff *skb; void *ptr; size_t len; + int ret; len = sizeof(*cmd) + sizeof(*tlv) + /* array struct */ @@ -10435,7 +10462,13 @@ int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, ath12k_dbg_dump(ar->ab, ATH12K_DBG_WMI, NULL, "wow bitmask: ", bitmap->bitmaskbuf, pattern_len); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_ADD_WAKE_PATTERN_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id) @@ -10443,6 +10476,7 @@ int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id) struct wmi_wow_del_pattern_cmd *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10459,7 +10493,13 @@ int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id) ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow del pattern vdev_id %d pattern_id %d\n", vdev_id, pattern_id); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_DEL_WAKE_PATTERN_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } static struct sk_buff * @@ -10595,6 +10635,7 @@ int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, struct wmi_pno_scan_req_arg *pno_scan) { struct sk_buff *skb; + int ret; if (pno_scan->enable) skb = ath12k_wmi_op_gen_config_pno_start(ar, vdev_id, pno_scan); @@ -10604,7 +10645,13 @@ int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, if (IS_ERR_OR_NULL(skb)) return -ENOMEM; - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } static void ath12k_wmi_fill_ns_offload(struct ath12k *ar, @@ -10717,6 +10764,7 @@ int ath12k_wmi_arp_ns_offload(struct ath12k *ar, void *buf_ptr; size_t len; u8 ns_cnt, ns_ext_tuples = 0; + int ret; ns_cnt = offload->ipv6_count; @@ -10752,7 +10800,13 @@ int ath12k_wmi_arp_ns_offload(struct ath12k *ar, if (ns_ext_tuples) ath12k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 1); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_SET_ARP_NS_OFFLOAD_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, @@ -10762,7 +10816,7 @@ int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, struct wmi_gtk_rekey_offload_cmd *cmd; struct sk_buff *skb; __le64 replay_ctr; - int len; + int ret, len; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10789,7 +10843,13 @@ int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "offload gtk rekey vdev: %d %d\n", arvif->vdev_id, enable); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID offload\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, @@ -10797,7 +10857,7 @@ int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, { struct wmi_gtk_rekey_offload_cmd *cmd; struct sk_buff *skb; - int len; + int ret, len; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10811,7 +10871,13 @@ int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "get gtk rekey vdev_id: %d\n", arvif->vdev_id); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID getinfo\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_sta_keepalive(struct ath12k *ar, @@ -10822,6 +10888,7 @@ int ath12k_wmi_sta_keepalive(struct ath12k *ar, struct wmi_sta_keepalive_cmd *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd) + sizeof(*arp); skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); @@ -10849,7 +10916,13 @@ int ath12k_wmi_sta_keepalive(struct ath12k *ar, "wmi sta keepalive vdev %d enabled %d method %d interval %d\n", arg->vdev_id, arg->enabled, arg->method, arg->interval); - return ath12k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID); + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_STA_KEEPALIVE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params) diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 05c9c07591fc..6ca31d4ea437 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1738,7 +1738,8 @@ ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb, } info->status.rates[ts->ts_final_idx].count = ts->ts_final_retry; - info->status.rates[ts->ts_final_idx + 1].idx = -1; + if (ts->ts_final_idx + 1 < IEEE80211_TX_MAX_RATES) + info->status.rates[ts->ts_final_idx + 1].idx = -1; if (unlikely(ts->ts_status)) { ah->stats.ack_fail++; diff --git a/drivers/net/wireless/broadcom/b43/xmit.c b/drivers/net/wireless/broadcom/b43/xmit.c index 7651b1bdb592..f0b082596637 100644 --- a/drivers/net/wireless/broadcom/b43/xmit.c +++ b/drivers/net/wireless/broadcom/b43/xmit.c @@ -702,7 +702,8 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) * key index, but the ucode passed it slightly different. */ keyidx = b43_kidx_to_raw(dev, keyidx); - B43_WARN_ON(keyidx >= ARRAY_SIZE(dev->key)); + if (B43_WARN_ON(keyidx >= ARRAY_SIZE(dev->key))) + goto drop; if (dev->key[keyidx].algorithm != B43_SEC_ALGO_NONE) { wlhdr_len = ieee80211_hdrlen(fctl); diff --git a/drivers/net/wireless/broadcom/b43legacy/xmit.c b/drivers/net/wireless/broadcom/b43legacy/xmit.c index efd63f4ce74f..ee199d4eaf03 100644 --- a/drivers/net/wireless/broadcom/b43legacy/xmit.c +++ b/drivers/net/wireless/broadcom/b43legacy/xmit.c @@ -476,7 +476,8 @@ void b43legacy_rx(struct b43legacy_wldev *dev, * key index, but the ucode passed it slightly different. */ keyidx = b43legacy_kidx_to_raw(dev, keyidx); - B43legacy_WARN_ON(keyidx >= dev->max_nr_keys); + if (B43legacy_WARN_ON(keyidx >= dev->max_nr_keys)) + goto drop; if (dev->key[keyidx].algorithm != B43legacy_SEC_ALGO_NONE) { /* Remove PROTECTED flag to mark it as decrypted. */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 30f6fcb68632..8fb595733b9c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -2476,8 +2476,9 @@ static void brcmf_sdio_bus_stop(struct device *dev) brcmf_dbg(TRACE, "Enter\n"); if (bus->watchdog_tsk) { + get_task_struct(bus->watchdog_tsk); send_sig(SIGTERM, bus->watchdog_tsk, 1); - kthread_stop(bus->watchdog_tsk); + kthread_stop_put(bus->watchdog_tsk); bus->watchdog_tsk = NULL; } @@ -4567,8 +4568,9 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) if (bus) { /* Stop watchdog task */ if (bus->watchdog_tsk) { + get_task_struct(bus->watchdog_tsk); send_sig(SIGTERM, bus->watchdog_tsk, 1); - kthread_stop(bus->watchdog_tsk); + kthread_stop_put(bus->watchdog_tsk); bus->watchdog_tsk = NULL; } diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c index 4fae0e335136..5cc0c5cac257 100644 --- a/drivers/net/wireless/marvell/libertas/if_usb.c +++ b/drivers/net/wireless/marvell/libertas/if_usb.c @@ -310,6 +310,7 @@ static void if_usb_disconnect(struct usb_interface *intf) struct lbs_private *priv = cardp->priv; cardp->surprise_removed = 1; + wake_up(&cardp->fw_wq); if (priv) { lbs_stop_card(priv); @@ -633,9 +634,10 @@ static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, unsigned long flags; u8 i; - if (recvlength > LBS_CMD_BUFFER_SIZE) { + if (recvlength < MESSAGE_HEADER_LEN || + recvlength > LBS_CMD_BUFFER_SIZE) { lbs_deb_usbd(&cardp->udev->dev, - "The receive buffer is too large\n"); + "The receive buffer is invalid: %d\n", recvlength); kfree_skb(skb); return; } diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 591602beeec6..3cdf9ded876d 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); diff --git a/drivers/net/wireless/st/cw1200/pm.c b/drivers/net/wireless/st/cw1200/pm.c index 84eb15d729c7..120f0379f81d 100644 --- a/drivers/net/wireless/st/cw1200/pm.c +++ b/drivers/net/wireless/st/cw1200/pm.c @@ -264,14 +264,12 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) wiphy_err(priv->hw->wiphy, "PM request failed: %d. WoW is disabled.\n", ret); cw1200_wow_resume(hw); - mutex_unlock(&priv->conf_mutex); return -EBUSY; } /* Force resume if event is coming from the device. */ if (atomic_read(&priv->bh_rx)) { cw1200_wow_resume(hw); - mutex_unlock(&priv->conf_mutex); return -EAGAIN; } diff --git a/drivers/net/wwan/t7xx/t7xx_modem_ops.c b/drivers/net/wwan/t7xx/t7xx_modem_ops.c index 7968e208dd37..adb29d30c63f 100644 --- a/drivers/net/wwan/t7xx/t7xx_modem_ops.c +++ b/drivers/net/wwan/t7xx/t7xx_modem_ops.c @@ -457,8 +457,20 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf offset = sizeof(struct feature_query); for (i = 0; i < FEATURE_COUNT && offset < data_length; i++) { + size_t remaining = data_length - offset; + size_t feat_data_len, feat_total; + + if (remaining < sizeof(*rt_feature)) + break; + rt_feature = data + offset; - offset += sizeof(*rt_feature) + le32_to_cpu(rt_feature->data_len); + feat_data_len = le32_to_cpu(rt_feature->data_len); + + if (feat_data_len > remaining - sizeof(*rt_feature)) + break; + + feat_total = sizeof(*rt_feature) + feat_data_len; + offset += feat_total; ft_spt_cfg = FIELD_GET(FEATURE_MSK, core->feature_set[i]); if (ft_spt_cfg != MTK_FEATURE_MUST_BE_SUPPORTED) @@ -468,8 +480,10 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf if (ft_spt_st != MTK_FEATURE_MUST_BE_SUPPORTED) return -EINVAL; - if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM) - t7xx_port_enum_msg_handler(ctl->md, rt_feature->data); + if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM) { + t7xx_port_enum_msg_handler(ctl->md, rt_feature->data, + feat_data_len); + } } return 0; diff --git a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c index ae632ef96698..f869e4ed9ee9 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c +++ b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c @@ -117,6 +117,7 @@ static int fsm_ee_message_handler(struct t7xx_port *port, struct t7xx_fsm_ctl *c * t7xx_port_enum_msg_handler() - Parse the port enumeration message to create/remove nodes. * @md: Modem context. * @msg: Message. + * @msg_len: Length of @msg in bytes. * * Used to control create/remove device node. * @@ -124,12 +125,18 @@ static int fsm_ee_message_handler(struct t7xx_port *port, struct t7xx_fsm_ctl *c * * 0 - Success. * * -EFAULT - Message check failure. */ -int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg) +int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg, size_t msg_len) { struct device *dev = &md->t7xx_dev->pdev->dev; unsigned int version, port_count, i; struct port_msg *port_msg = msg; + if (msg_len < sizeof(*port_msg)) { + dev_err(dev, "Port enum msg too short for header: need %zu, have %zu\n", + sizeof(*port_msg), msg_len); + return -EINVAL; + } + version = FIELD_GET(PORT_MSG_VERSION, le32_to_cpu(port_msg->info)); if (version != PORT_ENUM_VER || le32_to_cpu(port_msg->head_pattern) != PORT_ENUM_HEAD_PATTERN || @@ -141,6 +148,13 @@ int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg) } port_count = FIELD_GET(PORT_MSG_PRT_CNT, le32_to_cpu(port_msg->info)); + + if (msg_len < struct_size(port_msg, data, port_count)) { + dev_err(dev, "Port enum msg too short: need %zu, have %zu\n", + struct_size(port_msg, data, port_count), msg_len); + return -EINVAL; + } + for (i = 0; i < port_count; i++) { u32 port_info = le32_to_cpu(port_msg->data[i]); unsigned int ch_id; @@ -191,7 +205,7 @@ static int control_msg_handler(struct t7xx_port *port, struct sk_buff *skb) case CTL_ID_PORT_ENUM: skb_pull(skb, sizeof(*ctrl_msg_h)); - ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data); + ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data, skb->len); if (!ret) ret = port_ctl_send_msg_to_md(port, CTL_ID_PORT_ENUM, 0); else diff --git a/drivers/net/wwan/t7xx/t7xx_port_proxy.h b/drivers/net/wwan/t7xx/t7xx_port_proxy.h index f0918b36e899..7c3190bf0fcf 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_proxy.h +++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.h @@ -103,7 +103,7 @@ void t7xx_port_proxy_reset(struct port_proxy *port_prox); void t7xx_port_proxy_uninit(struct port_proxy *port_prox); int t7xx_port_proxy_init(struct t7xx_modem *md); void t7xx_port_proxy_md_status_notify(struct port_proxy *port_prox, unsigned int state); -int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg); +int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg, size_t msg_len); int t7xx_port_proxy_chl_enable_disable(struct port_proxy *port_prox, unsigned int ch_id, bool en_flag); void t7xx_port_proxy_set_cfg(struct t7xx_modem *md, enum port_cfg_id cfg_id); diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c index d17c701c7888..08c27bb438b5 100644 --- a/drivers/nfc/trf7970a.c +++ b/drivers/nfc/trf7970a.c @@ -317,6 +317,7 @@ #define TRF7970A_RSSI_OSC_STATUS_RSSI_MASK (BIT(2) | BIT(1) | BIT(0)) #define TRF7970A_RSSI_OSC_STATUS_RSSI_X_MASK (BIT(5) | BIT(4) | BIT(3)) #define TRF7970A_RSSI_OSC_STATUS_RSSI_OSC_OK BIT(6) +#define TRF7970A_RSSI_OSC_STATUS_RSSI_NOISE_LEVEL 1 #define TRF7970A_SPECIAL_FCN_REG1_COL_7_6 BIT(0) #define TRF7970A_SPECIAL_FCN_REG1_14_ANTICOLL BIT(1) @@ -1300,7 +1301,7 @@ static int trf7970a_is_rf_field(struct trf7970a *trf, bool *is_rf_field) if (ret) return ret; - if (rssi & TRF7970A_RSSI_OSC_STATUS_RSSI_MASK) + if ((rssi & TRF7970A_RSSI_OSC_STATUS_RSSI_MASK) > TRF7970A_RSSI_OSC_STATUS_RSSI_NOISE_LEVEL) *is_rf_field = true; else *is_rf_field = false; diff --git a/drivers/nvme/common/auth.c b/drivers/nvme/common/auth.c index 2d325fb93083..77f1d22512f8 100644 --- a/drivers/nvme/common/auth.c +++ b/drivers/nvme/common/auth.c @@ -351,18 +351,29 @@ struct nvme_dhchap_key *nvme_auth_transform_key( } EXPORT_SYMBOL_GPL(nvme_auth_transform_key); +/** + * nvme_auth_augmented_challenge() - Compute the augmented DH-HMAC-CHAP challenge + * @hmac_id: Hash algorithm identifier + * @skey: Session key + * @skey_len: Length of @skey + * @challenge: Challenge value + * @aug: Output buffer for the augmented challenge + * @hlen: Hash output length (length of @challenge and @aug) + * + * NVMe base specification 8.3.5.5.4: The augmented challenge is computed + * applying the HMAC function using the hash function H() selected by the + * HashID parameter ... with the hash of the ephemeral DH key ... as HMAC key + * to the challenge C (i.e., Ca = HMAC(H(g^xy mod p), C)). + * + * As the session key skey is already H(g^xy mod p) per section 8.3.5.5.9, use + * it directly as the HMAC key without additional hashing. + * + * Return: 0 on success, negative errno on failure. + */ int nvme_auth_augmented_challenge(u8 hmac_id, const u8 *skey, size_t skey_len, const u8 *challenge, u8 *aug, size_t hlen) { - u8 hashed_key[NVME_AUTH_MAX_DIGEST_SIZE]; - int ret; - - ret = nvme_auth_hash(hmac_id, skey, skey_len, hashed_key); - if (ret) - return ret; - ret = nvme_auth_hmac(hmac_id, hashed_key, hlen, challenge, hlen, aug); - memzero_explicit(hashed_key, sizeof(hashed_key)); - return ret; + return nvme_auth_hmac(hmac_id, skey, skey_len, challenge, hlen, aug); } EXPORT_SYMBOL_GPL(nvme_auth_augmented_challenge); @@ -403,33 +414,76 @@ int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm, } EXPORT_SYMBOL_GPL(nvme_auth_gen_pubkey); -int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm, - const u8 *ctrl_key, size_t ctrl_key_len, - u8 *sess_key, size_t sess_key_len) +/** + * nvme_auth_gen_session_key() - Generate an ephemeral session key + * @dh_tfm: Diffie-Hellman transform with local private key already set + * @public_key: Peer's public key + * @public_key_len: Length of @public_key + * @sess_key: Output buffer for the session key + * @sess_key_len: Size of @sess_key buffer + * @hash_id: Hash algorithm identifier + * + * NVMe base specification 8.3.5.5.9: The session key Ks shall be computed from + * the ephemeral DH key (i.e., g^xy mod p) ... by applying the hash function + * H() selected by the HashID parameter ... (i.e., Ks = H(g^xy mod p)). + * + * Return: 0 on success, negative errno on failure. + */ +int nvme_auth_gen_session_key(struct crypto_kpp *dh_tfm, + const u8 *public_key, size_t public_key_len, + u8 *sess_key, size_t sess_key_len, u8 hash_id) { struct kpp_request *req; struct crypto_wait wait; struct scatterlist src, dst; + u8 *dh_secret; + size_t dh_secret_len, hash_len; int ret; - req = kpp_request_alloc(dh_tfm, GFP_KERNEL); - if (!req) + hash_len = nvme_auth_hmac_hash_len(hash_id); + if (!hash_len) { + pr_warn("%s: invalid hash algorithm %d\n", __func__, hash_id); + return -EINVAL; + } + + if (sess_key_len != hash_len) { + pr_warn("%s: sess_key buffer missized (%zu != %zu)\n", + __func__, sess_key_len, hash_len); + return -EINVAL; + } + + dh_secret_len = crypto_kpp_maxsize(dh_tfm); + dh_secret = kzalloc(dh_secret_len, GFP_KERNEL); + if (!dh_secret) return -ENOMEM; + req = kpp_request_alloc(dh_tfm, GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto out_free_secret; + } + crypto_init_wait(&wait); - sg_init_one(&src, ctrl_key, ctrl_key_len); - kpp_request_set_input(req, &src, ctrl_key_len); - sg_init_one(&dst, sess_key, sess_key_len); - kpp_request_set_output(req, &dst, sess_key_len); + sg_init_one(&src, public_key, public_key_len); + kpp_request_set_input(req, &src, public_key_len); + sg_init_one(&dst, dh_secret, dh_secret_len); + kpp_request_set_output(req, &dst, dh_secret_len); kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait); ret = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait); - kpp_request_free(req); + + if (ret) + goto out_free_secret; + + ret = nvme_auth_hash(hash_id, dh_secret, dh_secret_len, sess_key); + +out_free_secret: + kfree_sensitive(dh_secret); return ret; } -EXPORT_SYMBOL_GPL(nvme_auth_gen_shared_secret); +EXPORT_SYMBOL_GPL(nvme_auth_gen_session_key); int nvme_auth_parse_key(const char *secret, struct nvme_dhchap_key **ret_key) { diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index ed61b97fde59..c692fc73babf 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -1009,6 +1009,7 @@ static void apple_nvme_init_queue(struct apple_nvme_queue *q) unsigned int depth = apple_nvme_queue_depth(q); struct apple_nvme *anv = queue_to_apple_nvme(q); + q->sq_tail = 0; q->cq_head = 0; q->cq_phase = 1; if (anv->hw->has_lsq_nvmmu) @@ -1267,11 +1268,7 @@ static int apple_nvme_get_address(struct nvme_ctrl *ctrl, char *buf, int size) static void apple_nvme_free_ctrl(struct nvme_ctrl *ctrl) { - struct apple_nvme *anv = ctrl_to_apple_nvme(ctrl); - - if (anv->ctrl.admin_q) - blk_put_queue(anv->ctrl.admin_q); - put_device(anv->dev); + put_device(ctrl->dev); } static const struct nvme_ctrl_ops nvme_ctrl_ops = { diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c index bbedbe181c8a..16de4499a8e7 100644 --- a/drivers/nvme/host/auth.c +++ b/drivers/nvme/host/auth.c @@ -535,11 +535,12 @@ static int nvme_auth_dhchap_setup_ctrl_response(struct nvme_ctrl *ctrl, put_unaligned_le16(chap->transaction, buf); nvme_auth_hmac_update(&hmac, buf, 2); - memset(buf, 0, 4); + *buf = chap->sc_c; nvme_auth_hmac_update(&hmac, buf, 1); nvme_auth_hmac_update(&hmac, "Controller", 10); nvme_auth_hmac_update(&hmac, ctrl->opts->subsysnqn, strlen(ctrl->opts->subsysnqn)); + memset(buf, 0, 4); nvme_auth_hmac_update(&hmac, buf, 1); nvme_auth_hmac_update(&hmac, ctrl->opts->host->nqn, strlen(ctrl->opts->host->nqn)); @@ -587,7 +588,7 @@ static int nvme_auth_dhchap_exponential(struct nvme_ctrl *ctrl, } gen_sesskey: - chap->sess_key_len = chap->host_key_len; + chap->sess_key_len = chap->hash_len; chap->sess_key = kmalloc(chap->sess_key_len, GFP_KERNEL); if (!chap->sess_key) { chap->sess_key_len = 0; @@ -595,16 +596,17 @@ gen_sesskey: return -ENOMEM; } - ret = nvme_auth_gen_shared_secret(chap->dh_tfm, - chap->ctrl_key, chap->ctrl_key_len, - chap->sess_key, chap->sess_key_len); + ret = nvme_auth_gen_session_key(chap->dh_tfm, + chap->ctrl_key, chap->ctrl_key_len, + chap->sess_key, chap->sess_key_len, + chap->hash_id); if (ret) { dev_dbg(ctrl->device, - "failed to generate shared secret, error %d\n", ret); + "failed to generate session key, error %d\n", ret); chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; return ret; } - dev_dbg(ctrl->device, "shared secret %*ph\n", + dev_dbg(ctrl->device, "session key %*ph\n", (int)chap->sess_key_len, chap->sess_key); return 0; } diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 1e33af94c24b..c3032d6ad6b1 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -454,11 +454,10 @@ void nvme_end_req(struct request *req) blk_mq_end_request(req, status); } -void nvme_complete_rq(struct request *req) +static void __nvme_complete_rq(struct request *req) { struct nvme_ctrl *ctrl = nvme_req(req)->ctrl; - trace_nvme_complete_rq(req); nvme_cleanup_cmd(req); /* @@ -493,6 +492,12 @@ void nvme_complete_rq(struct request *req) return; } } + +void nvme_complete_rq(struct request *req) +{ + trace_nvme_complete_rq(req); + __nvme_complete_rq(req); +} EXPORT_SYMBOL_GPL(nvme_complete_rq); void nvme_complete_batch_req(struct request *req) @@ -513,7 +518,7 @@ blk_status_t nvme_host_path_error(struct request *req) { nvme_req(req)->status = NVME_SC_HOST_PATH_ERROR; blk_mq_set_request_complete(req); - nvme_complete_rq(req); + __nvme_complete_rq(req); return BLK_STS_OK; } EXPORT_SYMBOL_GPL(nvme_host_path_error); @@ -3044,7 +3049,7 @@ static const struct nvme_core_quirk_entry core_quirks[] = { * * The device is left in a state where it is also not possible * to use "nvme set-feature" to disable APST, but booting with - * nvme_core.default_ps_max_latency=0 works. + * nvme_core.default_ps_max_latency_us=0 works. */ .vid = 0x1e0f, .mn = "KCD6XVUL6T40", @@ -3744,6 +3749,10 @@ int nvme_init_ctrl_finish(struct nvme_ctrl *ctrl, bool was_suspended) ret = nvme_hwmon_init(ctrl); if (ret == -EINTR) return ret; + + if (!nvme_ctrl_sgl_supported(ctrl)) + dev_info(ctrl->device, + "passthrough uses implicit buffer lengths\n"); } clear_bit(NVME_CTRL_DIRTY_CAPABILITY, &ctrl->flags); @@ -4083,7 +4092,8 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info) mutex_unlock(&ctrl->subsys->lock); #ifdef CONFIG_NVME_MULTIPATH - cancel_delayed_work(&head->remove_work); + if (cancel_delayed_work(&head->remove_work)) + module_put(THIS_MODULE); #endif return 0; @@ -5035,8 +5045,8 @@ void nvme_start_ctrl(struct nvme_ctrl *ctrl) nvme_mpath_update(ctrl); } - nvme_change_uevent(ctrl, "NVME_EVENT=connected"); set_bit(NVME_CTRL_STARTED_ONCE, &ctrl->flags); + nvme_change_uevent(ctrl, "NVME_EVENT=connected"); } EXPORT_SYMBOL_GPL(nvme_start_ctrl); diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index e1bb4707183c..e4f4528fe2a2 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -3968,3 +3968,4 @@ module_exit(nvme_fc_exit_module); MODULE_DESCRIPTION("NVMe host FC transport driver"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("nvme-fc"); diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c index 9597a87cf05d..08889b20e5d8 100644 --- a/drivers/nvme/host/ioctl.c +++ b/drivers/nvme/host/ioctl.c @@ -120,21 +120,11 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, struct nvme_ns *ns = q->queuedata; struct block_device *bdev = ns ? ns->disk->part0 : NULL; bool supports_metadata = bdev && blk_get_integrity(bdev->bd_disk); - struct nvme_ctrl *ctrl = nvme_req(req)->ctrl; bool has_metadata = meta_buffer && meta_len; - struct bio *bio = NULL; int ret; - if (!nvme_ctrl_sgl_supported(ctrl)) - dev_warn_once(ctrl->device, "using unchecked data buffer\n"); - if (has_metadata) { - if (!supports_metadata) - return -EINVAL; - - if (!nvme_ctrl_meta_sgl_supported(ctrl)) - dev_warn_once(ctrl->device, - "using unchecked metadata buffer\n"); - } + if (has_metadata && !supports_metadata) + return -EINVAL; if (iter) ret = blk_rq_map_user_iov(q, req, NULL, iter, GFP_KERNEL); @@ -154,8 +144,8 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, return ret; out_unmap: - if (bio) - blk_rq_unmap_user(bio); + if (req->bio) + blk_rq_unmap_user(req->bio); return ret; } diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index ba00f0b72b85..263161cb8ac0 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -231,16 +231,12 @@ bool nvme_mpath_clear_current_path(struct nvme_ns *ns) bool changed = false; int node; - if (!head) - goto out; - for_each_node(node) { if (ns == rcu_access_pointer(head->current_path[node])) { rcu_assign_pointer(head->current_path[node], NULL); changed = true; } } -out: return changed; } diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index db5fc9bf6627..139a10cd687f 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2241,6 +2241,7 @@ release_cq: static const struct blk_mq_ops nvme_mq_admin_ops = { .queue_rq = nvme_queue_rq, .complete = nvme_pci_complete_rq, + .commit_rqs = nvme_commit_rqs, .init_hctx = nvme_admin_init_hctx, .init_request = nvme_pci_init_request, .timeout = nvme_timeout, @@ -2532,11 +2533,13 @@ static void nvme_free_host_mem_multi(struct nvme_dev *dev) static void nvme_free_host_mem(struct nvme_dev *dev) { - if (dev->hmb_sgt) + if (dev->hmb_sgt) { dma_free_noncontiguous(dev->dev, dev->host_mem_size, dev->hmb_sgt, DMA_BIDIRECTIONAL); - else + dev->hmb_sgt = NULL; + } else { nvme_free_host_mem_multi(dev); + } dma_free_coherent(dev->dev, dev->host_mem_descs_size, dev->host_mem_descs, dev->host_mem_descs_dma); @@ -4104,6 +4107,8 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, { PCI_DEVICE(0x1c5f, 0x0540), /* Memblaze Pblaze4 adapter */ .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, + { PCI_DEVICE(0x1c5f, 0x0555), /* Memblaze Pblaze5 adapter */ + .driver_data = NVME_QUIRK_NO_NS_DESC_LIST, }, { PCI_DEVICE(0x144d, 0xa821), /* Samsung PM1725 */ .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, { PCI_DEVICE(0x144d, 0xa822), /* Samsung PM1725a */ diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index 57111139e84f..f77c960f7632 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -2189,6 +2189,13 @@ out_fail: nvme_rdma_reconnect_or_remove(ctrl, ret); } +static bool nvme_rdma_supports_pci_p2pdma(struct nvme_ctrl *ctrl) +{ + struct nvme_rdma_ctrl *r_ctrl = to_rdma_ctrl(ctrl); + + return ib_dma_pci_p2p_dma_supported(r_ctrl->device->dev); +} + static const struct nvme_ctrl_ops nvme_rdma_ctrl_ops = { .name = "rdma", .module = THIS_MODULE, @@ -2203,6 +2210,7 @@ static const struct nvme_ctrl_ops nvme_rdma_ctrl_ops = { .get_address = nvmf_get_address, .stop_ctrl = nvme_rdma_stop_ctrl, .get_virt_boundary = nvme_get_virt_boundary, + .supports_pci_p2pdma = nvme_rdma_supports_pci_p2pdma, }; /* @@ -2432,3 +2440,4 @@ module_exit(nvme_rdma_cleanup_module); MODULE_DESCRIPTION("NVMe host RDMA transport driver"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("nvme-rdma"); diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c index 7bf2e972126b..e59758616f27 100644 --- a/drivers/nvme/host/sysfs.c +++ b/drivers/nvme/host/sysfs.c @@ -883,10 +883,26 @@ static ssize_t tls_keyring_show(struct device *dev, } static DEVICE_ATTR_RO(tls_keyring); +static ssize_t tls_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + const char *mode; + + if (ctrl->opts->tls) + mode = "tls"; + else + mode = "concat"; + + return sysfs_emit(buf, "%s\n", mode); +} +static DEVICE_ATTR_RO(tls_mode); + static struct attribute *nvme_tls_attrs[] = { &dev_attr_tls_key.attr, &dev_attr_tls_configured_key.attr, &dev_attr_tls_keyring.attr, + &dev_attr_tls_mode.attr, NULL, }; @@ -908,6 +924,9 @@ static umode_t nvme_tls_attrs_are_visible(struct kobject *kobj, if (a == &dev_attr_tls_keyring.attr && !ctrl->opts->keyring) return 0; + if (a == &dev_attr_tls_mode.attr && + !ctrl->opts->tls && !ctrl->opts->concat) + return 0; return a->mode; } diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 243dab830dc8..15d36d6a728e 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -1438,18 +1438,32 @@ static void nvme_tcp_free_queue(struct nvme_ctrl *nctrl, int qid) { struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl); struct nvme_tcp_queue *queue = &ctrl->queues[qid]; - unsigned int noreclaim_flag; + unsigned int noio_flag; if (!test_and_clear_bit(NVME_TCP_Q_ALLOCATED, &queue->flags)) return; page_frag_cache_drain(&queue->pf_cache); - noreclaim_flag = memalloc_noreclaim_save(); - /* ->sock will be released by fput() */ - fput(queue->sock->file); + /** + * Prevent memory reclaim from triggering block I/O during socket + * teardown. The socket release path fput -> tcp_close -> + * tcp_disconnect -> tcp_send_active_reset may allocate memory, and + * allowing reclaim to issue I/O could deadlock if we're being called + * from block device teardown (e.g., del_gendisk -> elevator cleanup) + * which holds locks that the I/O completion path needs. + */ + noio_flag = memalloc_noio_save(); + + /** + * Release the socket synchronously. During reset in + * nvme_reset_ctrl_work(), queue teardown is immediately followed by + * re-allocation. fput() defers socket cleanup to delayed_fput_work + * in workqueue context, which can race with new queue setup. + */ + __fput_sync(queue->sock->file); queue->sock = NULL; - memalloc_noreclaim_restore(noreclaim_flag); + memalloc_noio_restore(noio_flag); kfree(queue->pdu); mutex_destroy(&queue->send_mutex); @@ -1901,8 +1915,8 @@ err_init_connect: err_rcv_pdu: kfree(queue->pdu); err_sock: - /* ->sock will be released by fput() */ - fput(queue->sock->file); + /* Use sync variant - see nvme_tcp_free_queue() for explanation */ + __fput_sync(queue->sock->file); queue->sock = NULL; err_destroy_mutex: mutex_destroy(&queue->send_mutex); @@ -3071,3 +3085,4 @@ module_exit(nvme_tcp_cleanup_module); MODULE_DESCRIPTION("NVMe host TCP transport driver"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("nvme-tcp"); diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig index 4904097dfd49..69bde270115e 100644 --- a/drivers/nvme/target/Kconfig +++ b/drivers/nvme/target/Kconfig @@ -117,6 +117,15 @@ config NVME_TARGET_AUTH If unsure, say N. +config NVME_TARGET_AUTH_DEBUG + bool "NVMe over Fabrics In-band Authentication debug messages" + depends on NVME_TARGET_AUTH + help + This enables additional debug messages including the generated + DH-HMAC-CHAP secrets to help debugging authentication failures. + + If unsure, say N. + config NVME_TARGET_PCI_EPF tristate "NVMe PCI Endpoint Function target support" depends on NVME_TARGET && PCI_ENDPOINT diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c index e4fd1caadfb0..01b799e92ae6 100644 --- a/drivers/nvme/target/admin-cmd.c +++ b/drivers/nvme/target/admin-cmd.c @@ -687,12 +687,8 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req) id->cmic = NVME_CTRL_CMIC_MULTI_PORT | NVME_CTRL_CMIC_MULTI_CTRL | NVME_CTRL_CMIC_ANA; - /* Limit MDTS according to transport capability */ - if (ctrl->ops->get_mdts) - id->mdts = ctrl->ops->get_mdts(ctrl); - else - id->mdts = 0; - + /* Limit MDTS according to port config or transport capability */ + id->mdts = nvmet_ctrl_mdts(req); id->cntlid = cpu_to_le16(ctrl->cntlid); id->ver = cpu_to_le32(ctrl->subsys->ver); diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c index b34610e2f19d..edb9627d97b0 100644 --- a/drivers/nvme/target/auth.c +++ b/drivers/nvme/target/auth.c @@ -144,7 +144,6 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, bool reset) goto out_unlock; list_for_each_entry(p, &ctrl->subsys->hosts, entry) { - pr_debug("check %s\n", nvmet_host_name(p->host)); if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn)) continue; host = p->host; @@ -189,11 +188,12 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, bool reset) ctrl->host_key = NULL; goto out_free_hash; } +#ifdef CONFIG_NVME_TARGET_AUTH_DEBUG pr_debug("%s: using hash %s key %*ph\n", __func__, ctrl->host_key->hash > 0 ? nvme_auth_hmac_name(ctrl->host_key->hash) : "none", (int)ctrl->host_key->len, ctrl->host_key->key); - +#endif nvme_auth_free_key(ctrl->ctrl_key); if (!host->dhchap_ctrl_secret) { ctrl->ctrl_key = NULL; @@ -207,11 +207,12 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, bool reset) ctrl->ctrl_key = NULL; goto out_free_hash; } +#ifdef CONFIG_NVME_TARGET_AUTH_DEBUG pr_debug("%s: using ctrl hash %s key %*ph\n", __func__, ctrl->ctrl_key->hash > 0 ? nvme_auth_hmac_name(ctrl->ctrl_key->hash) : "none", (int)ctrl->ctrl_key->len, ctrl->ctrl_key->key); - +#endif out_free_hash: if (ret) { if (ctrl->host_key) { @@ -229,9 +230,6 @@ out_unlock: void nvmet_auth_sq_free(struct nvmet_sq *sq) { cancel_delayed_work(&sq->auth_expired_work); -#ifdef CONFIG_NVME_TARGET_TCP_TLS - sq->tls_key = NULL; -#endif kfree(sq->dhchap_c1); sq->dhchap_c1 = NULL; kfree(sq->dhchap_c2); @@ -320,7 +318,6 @@ int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response, if (ret) goto out_free_challenge; } - pr_debug("ctrl %d qid %d host response seq %u transaction %d\n", ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1, req->sq->dhchap_tid); @@ -402,11 +399,12 @@ int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response, put_unaligned_le16(req->sq->dhchap_tid, buf); nvme_auth_hmac_update(&hmac, buf, 2); - memset(buf, 0, 4); + *buf = req->sq->sc_c; nvme_auth_hmac_update(&hmac, buf, 1); nvme_auth_hmac_update(&hmac, "Controller", 10); nvme_auth_hmac_update(&hmac, ctrl->subsys->subsysnqn, strlen(ctrl->subsys->subsysnqn)); + memset(buf, 0, 4); nvme_auth_hmac_update(&hmac, buf, 1); nvme_auth_hmac_update(&hmac, ctrl->hostnqn, strlen(ctrl->hostnqn)); nvme_auth_hmac_final(&hmac, response); @@ -436,8 +434,10 @@ int nvmet_auth_ctrl_exponential(struct nvmet_req *req, ret = -EINVAL; } else { memcpy(buf, ctrl->dh_key, buf_size); +#ifdef CONFIG_NVME_TARGET_AUTH_DEBUG pr_debug("%s: ctrl %d public key %*ph\n", __func__, ctrl->cntlid, (int)buf_size, buf); +#endif } return ret; @@ -449,21 +449,23 @@ int nvmet_auth_ctrl_sesskey(struct nvmet_req *req, struct nvmet_ctrl *ctrl = req->sq->ctrl; int ret; - req->sq->dhchap_skey_len = ctrl->dh_keysize; + req->sq->dhchap_skey_len = nvme_auth_hmac_hash_len(ctrl->shash_id); req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len, GFP_KERNEL); if (!req->sq->dhchap_skey) return -ENOMEM; - ret = nvme_auth_gen_shared_secret(ctrl->dh_tfm, - pkey, pkey_size, - req->sq->dhchap_skey, - req->sq->dhchap_skey_len); + ret = nvme_auth_gen_session_key(ctrl->dh_tfm, + pkey, pkey_size, + req->sq->dhchap_skey, + req->sq->dhchap_skey_len, + ctrl->shash_id); if (ret) - pr_debug("failed to compute shared secret, err %d\n", ret); + pr_debug("failed to compute session key, err %d\n", ret); +#ifdef CONFIG_NVME_TARGET_AUTH_DEBUG else - pr_debug("%s: shared secret %*ph\n", __func__, + pr_debug("%s: session key %*ph\n", __func__, (int)req->sq->dhchap_skey_len, req->sq->dhchap_skey); - +#endif return ret; } diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c index 463348c7f097..b88f897f06e2 100644 --- a/drivers/nvme/target/configfs.c +++ b/drivers/nvme/target/configfs.c @@ -301,6 +301,31 @@ static ssize_t nvmet_param_max_queue_size_store(struct config_item *item, CONFIGFS_ATTR(nvmet_, param_max_queue_size); +static ssize_t nvmet_param_mdts_show(struct config_item *item, char *page) +{ + struct nvmet_port *port = to_nvmet_port(item); + + return snprintf(page, PAGE_SIZE, "%d\n", port->mdts); +} + +static ssize_t nvmet_param_mdts_store(struct config_item *item, + const char *page, size_t count) +{ + struct nvmet_port *port = to_nvmet_port(item); + int ret; + + if (nvmet_is_port_enabled(port, __func__)) + return -EACCES; + ret = kstrtoint(page, 0, &port->mdts); + if (ret) { + pr_err("Invalid value '%s' for mdts\n", page); + return -EINVAL; + } + return count; +} + +CONFIGFS_ATTR(nvmet_, param_mdts); + #ifdef CONFIG_BLK_DEV_INTEGRITY static ssize_t nvmet_param_pi_enable_show(struct config_item *item, char *page) @@ -1995,6 +2020,7 @@ static struct configfs_attribute *nvmet_port_attrs[] = { &nvmet_attr_addr_tsas, &nvmet_attr_param_inline_data_size, &nvmet_attr_param_max_queue_size, + &nvmet_attr_param_mdts, #ifdef CONFIG_BLK_DEV_INTEGRITY &nvmet_attr_param_pi_enable, #endif @@ -2053,6 +2079,7 @@ static struct config_group *nvmet_ports_make(struct config_group *group, INIT_LIST_HEAD(&port->referrals); port->inline_data_size = -1; /* < 0 == let the transport choose */ port->max_queue_size = -1; /* < 0 == let the transport choose */ + port->mdts = -1; /* < 0 == let the transport choose */ port->disc_addr.trtype = NVMF_TRTYPE_MAX; port->disc_addr.portid = cpu_to_le16(portid); diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index 45f686175fea..62dd59b9aa4f 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -370,6 +370,14 @@ int nvmet_enable_port(struct nvmet_port *port) NVMET_MIN_QUEUE_SIZE, NVMET_MAX_QUEUE_SIZE); + /* + * If the transport didn't set the mdts properly, then clamp it to the + * target limits. Also set default values in case the transport didn't + * set it at all. + */ + if (port->mdts < 0 || port->mdts > NVMET_MAX_MDTS) + port->mdts = 0; + port->enabled = true; port->tr_ops = ops; return 0; @@ -1743,7 +1751,7 @@ static void nvmet_ctrl_free(struct kref *ref) nvmet_stop_keep_alive_timer(ctrl); - flush_work(&ctrl->async_event_work); + cancel_work_sync(&ctrl->async_event_work); cancel_work_sync(&ctrl->fatal_err_work); nvmet_destroy_auth(ctrl); diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index b9ab80c7a694..f1e613e7c63e 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -395,10 +395,9 @@ done: goto complete; } /* Final states, clear up variables */ - if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) { - nvmet_auth_sq_free(req->sq); + nvmet_auth_sq_free(req->sq); + if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) nvmet_ctrl_fatal_error(ctrl); - } complete: nvmet_req_complete(req, status); @@ -574,7 +573,9 @@ void nvmet_execute_auth_receive(struct nvmet_req *req) status = nvmet_copy_to_sgl(req, 0, d, al); kfree(d); done: - if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) { + if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2) + nvmet_auth_sq_free(req->sq); + else if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) { nvmet_auth_sq_free(req->sq); nvmet_ctrl_fatal_error(ctrl); } diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index 50070cfb782a..3305a88684ec 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -214,6 +214,7 @@ struct nvmet_port { bool enabled; int inline_data_size; int max_queue_size; + int mdts; const struct nvmet_fabrics_ops *tr_ops; bool pi_enable; }; @@ -673,6 +674,7 @@ void nvmet_add_async_event(struct nvmet_ctrl *ctrl, u8 event_type, #define NVMET_MAX_QUEUE_SIZE 1024 #define NVMET_NR_QUEUES 128 #define NVMET_MAX_CMD(ctrl) (NVME_CAP_MQES(ctrl->cap) + 1) +#define NVMET_MAX_MDTS 255 /* * Nice round number that makes a list of nsids fit into a page. @@ -761,6 +763,17 @@ static inline bool nvmet_is_pci_ctrl(struct nvmet_ctrl *ctrl) return ctrl->port->disc_addr.trtype == NVMF_TRTYPE_PCI; } +/* Limit MDTS according to port config or transport capability */ +static inline u8 nvmet_ctrl_mdts(struct nvmet_req *req) +{ + struct nvmet_ctrl *ctrl = req->sq->ctrl; + u8 mdts = req->port->mdts; + + if (!ctrl->ops->get_mdts) + return mdts; + return min_not_zero(ctrl->ops->get_mdts(ctrl), mdts); +} + #ifdef CONFIG_NVME_TARGET_PASSTHRU void nvmet_passthru_subsys_free(struct nvmet_subsys *subsys); int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys); diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 4b8b02341ddc..20f150d17a96 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -349,9 +349,7 @@ static void nvmet_tcp_free_cmd_buffers(struct nvmet_tcp_cmd *cmd) cmd->req.sg = NULL; } -static void nvmet_tcp_fatal_error(struct nvmet_tcp_queue *queue); - -static void nvmet_tcp_build_pdu_iovec(struct nvmet_tcp_cmd *cmd) +static int nvmet_tcp_build_pdu_iovec(struct nvmet_tcp_cmd *cmd) { struct bio_vec *iov = cmd->iov; struct scatterlist *sg; @@ -364,22 +362,19 @@ static void nvmet_tcp_build_pdu_iovec(struct nvmet_tcp_cmd *cmd) offset = cmd->rbytes_done; cmd->sg_idx = offset / PAGE_SIZE; sg_offset = offset % PAGE_SIZE; - if (!cmd->req.sg_cnt || cmd->sg_idx >= cmd->req.sg_cnt) { - nvmet_tcp_fatal_error(cmd->queue); - return; - } + if (!cmd->req.sg_cnt || cmd->sg_idx >= cmd->req.sg_cnt) + return -EPROTO; + sg = &cmd->req.sg[cmd->sg_idx]; sg_remaining = cmd->req.sg_cnt - cmd->sg_idx; while (length) { - if (!sg_remaining) { - nvmet_tcp_fatal_error(cmd->queue); - return; - } - if (!sg->length || sg->length <= sg_offset) { - nvmet_tcp_fatal_error(cmd->queue); - return; - } + if (!sg_remaining) + return -EPROTO; + + if (!sg->length || sg->length <= sg_offset) + return -EPROTO; + u32 iov_len = min_t(u32, length, sg->length - sg_offset); bvec_set_page(iov, sg_page(sg), iov_len, @@ -394,24 +389,29 @@ static void nvmet_tcp_build_pdu_iovec(struct nvmet_tcp_cmd *cmd) iov_iter_bvec(&cmd->recv_msg.msg_iter, ITER_DEST, cmd->iov, nr_pages, cmd->pdu_len); -} - -static void nvmet_tcp_fatal_error(struct nvmet_tcp_queue *queue) -{ - queue->rcv_state = NVMET_TCP_RECV_ERR; - if (queue->nvme_sq.ctrl) - nvmet_ctrl_fatal_error(queue->nvme_sq.ctrl); - else - kernel_sock_shutdown(queue->sock, SHUT_RDWR); + return 0; } static void nvmet_tcp_socket_error(struct nvmet_tcp_queue *queue, int status) { + /* + * Keep rcv_state at RECV_ERR even for the internal -ESHUTDOWN path. + * nvmet_tcp_handle_icreq() can return -ESHUTDOWN after the ICReq has + * already been consumed and queue teardown has started. + * + * If nvmet_tcp_data_ready() or nvmet_tcp_write_space() queues + * nvmet_tcp_io_work() again before nvmet_tcp_release_queue_work() + * cancels it, the queue must not keep that old receive state. + * Otherwise the next nvmet_tcp_io_work() run can reach + * nvmet_tcp_done_recv_pdu() and try to handle the same ICReq again. + * + * That is why queue->rcv_state needs to be updated before we return. + */ queue->rcv_state = NVMET_TCP_RECV_ERR; - if (status == -EPIPE || status == -ECONNRESET) + if (status == -EPIPE || status == -ECONNRESET || !queue->nvme_sq.ctrl) kernel_sock_shutdown(queue->sock, SHUT_RDWR); else - nvmet_tcp_fatal_error(queue); + nvmet_ctrl_fatal_error(queue->nvme_sq.ctrl); } static int nvmet_tcp_map_data(struct nvmet_tcp_cmd *cmd) @@ -887,7 +887,6 @@ static int nvmet_tcp_handle_icreq(struct nvmet_tcp_queue *queue) if (le32_to_cpu(icreq->hdr.plen) != sizeof(struct nvme_tcp_icreq_pdu)) { pr_err("bad nvme-tcp pdu length (%d)\n", le32_to_cpu(icreq->hdr.plen)); - nvmet_tcp_fatal_error(queue); return -EPROTO; } @@ -922,16 +921,29 @@ static int nvmet_tcp_handle_icreq(struct nvmet_tcp_queue *queue) iov.iov_len = sizeof(*icresp); ret = kernel_sendmsg(queue->sock, &msg, &iov, 1, iov.iov_len); if (ret < 0) { + spin_lock_bh(&queue->state_lock); + if (queue->state == NVMET_TCP_Q_DISCONNECTING) { + spin_unlock_bh(&queue->state_lock); + return -ESHUTDOWN; + } queue->state = NVMET_TCP_Q_FAILED; + spin_unlock_bh(&queue->state_lock); return ret; /* queue removal will cleanup */ } + spin_lock_bh(&queue->state_lock); + if (queue->state == NVMET_TCP_Q_DISCONNECTING) { + spin_unlock_bh(&queue->state_lock); + /* Tell nvmet_tcp_socket_error() teardown is in progress. */ + return -ESHUTDOWN; + } queue->state = NVMET_TCP_Q_LIVE; + spin_unlock_bh(&queue->state_lock); nvmet_prepare_receive_pdu(queue); return 0; } -static void nvmet_tcp_handle_req_failure(struct nvmet_tcp_queue *queue, +static int nvmet_tcp_handle_req_failure(struct nvmet_tcp_queue *queue, struct nvmet_tcp_cmd *cmd, struct nvmet_req *req) { size_t data_len = le32_to_cpu(req->cmd->common.dptr.sgl.length); @@ -947,19 +959,22 @@ static void nvmet_tcp_handle_req_failure(struct nvmet_tcp_queue *queue, if (!nvme_is_write(cmd->req.cmd) || !data_len || data_len > cmd->req.port->inline_data_size) { nvmet_prepare_receive_pdu(queue); - return; + return 0; } ret = nvmet_tcp_map_data(cmd); if (unlikely(ret)) { pr_err("queue %d: failed to map data\n", queue->idx); - nvmet_tcp_fatal_error(queue); - return; + return -EPROTO; } queue->rcv_state = NVMET_TCP_RECV_DATA; - nvmet_tcp_build_pdu_iovec(cmd); cmd->flags |= NVMET_TCP_F_INIT_FAILED; + ret = nvmet_tcp_build_pdu_iovec(cmd); + if (unlikely(ret)) + pr_err("queue %d: failed to build PDU iovec\n", queue->idx); + + return ret; } static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue) @@ -1011,7 +1026,10 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue) goto err_proto; } cmd->pdu_recv = 0; - nvmet_tcp_build_pdu_iovec(cmd); + if (unlikely(nvmet_tcp_build_pdu_iovec(cmd))) { + pr_err("queue %d: failed to build PDU iovec\n", queue->idx); + goto err_proto; + } queue->cmd = cmd; queue->rcv_state = NVMET_TCP_RECV_DATA; @@ -1019,7 +1037,6 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue) err_proto: /* FIXME: use proper transport errors */ - nvmet_tcp_fatal_error(queue); return -EPROTO; } @@ -1034,7 +1051,6 @@ static int nvmet_tcp_done_recv_pdu(struct nvmet_tcp_queue *queue) if (hdr->type != nvme_tcp_icreq) { pr_err("unexpected pdu type (%d) before icreq\n", hdr->type); - nvmet_tcp_fatal_error(queue); return -EPROTO; } return nvmet_tcp_handle_icreq(queue); @@ -1043,7 +1059,6 @@ static int nvmet_tcp_done_recv_pdu(struct nvmet_tcp_queue *queue) if (unlikely(hdr->type == nvme_tcp_icreq)) { pr_err("queue %d: received icreq pdu in state %d\n", queue->idx, queue->state); - nvmet_tcp_fatal_error(queue); return -EPROTO; } @@ -1060,7 +1075,6 @@ static int nvmet_tcp_done_recv_pdu(struct nvmet_tcp_queue *queue) pr_err("queue %d: out of commands (%d) send_list_len: %d, opcode: %d", queue->idx, queue->nr_cmds, queue->send_list_len, nvme_cmd->common.opcode); - nvmet_tcp_fatal_error(queue); return -ENOMEM; } @@ -1074,17 +1088,16 @@ static int nvmet_tcp_done_recv_pdu(struct nvmet_tcp_queue *queue) le32_to_cpu(req->cmd->common.dptr.sgl.length), le16_to_cpu(req->cqe->status)); - nvmet_tcp_handle_req_failure(queue, queue->cmd, req); - return 0; + return nvmet_tcp_handle_req_failure(queue, queue->cmd, req); } ret = nvmet_tcp_map_data(queue->cmd); if (unlikely(ret)) { pr_err("queue %d: failed to map data\n", queue->idx); if (nvmet_tcp_has_inline_data(queue->cmd)) - nvmet_tcp_fatal_error(queue); - else - nvmet_req_complete(req, ret); + return -EPROTO; + + nvmet_req_complete(req, ret); ret = -EAGAIN; goto out; } @@ -1092,8 +1105,11 @@ static int nvmet_tcp_done_recv_pdu(struct nvmet_tcp_queue *queue) if (nvmet_tcp_need_data_in(queue->cmd)) { if (nvmet_tcp_has_inline_data(queue->cmd)) { queue->rcv_state = NVMET_TCP_RECV_DATA; - nvmet_tcp_build_pdu_iovec(queue->cmd); - return 0; + ret = nvmet_tcp_build_pdu_iovec(queue->cmd); + if (unlikely(ret)) + pr_err("queue %d: failed to build PDU iovec\n", + queue->idx); + return ret; } /* send back R2T */ nvmet_tcp_queue_response(&queue->cmd->req); @@ -1204,7 +1220,6 @@ recv: if (unlikely(!nvmet_tcp_pdu_valid(hdr->type))) { pr_err("unexpected pdu type %d\n", hdr->type); - nvmet_tcp_fatal_error(queue); return -EIO; } @@ -1218,16 +1233,12 @@ recv: } if (queue->hdr_digest && - nvmet_tcp_verify_hdgst(queue, &queue->pdu, hdr->hlen)) { - nvmet_tcp_fatal_error(queue); /* fatal */ + nvmet_tcp_verify_hdgst(queue, &queue->pdu, hdr->hlen)) return -EPROTO; - } if (queue->data_digest && - nvmet_tcp_check_ddgst(queue, &queue->pdu)) { - nvmet_tcp_fatal_error(queue); /* fatal */ + nvmet_tcp_check_ddgst(queue, &queue->pdu)) return -EPROTO; - } return nvmet_tcp_done_recv_pdu(queue); } @@ -1310,9 +1321,11 @@ static int nvmet_tcp_try_recv_ddgst(struct nvmet_tcp_queue *queue) queue->idx, cmd->req.cmd->common.command_id, queue->pdu.cmd.hdr.type, le32_to_cpu(cmd->recv_ddgst), le32_to_cpu(cmd->exp_ddgst)); - nvmet_req_uninit(&cmd->req); + if (!(cmd->flags & NVMET_TCP_F_INIT_FAILED)) { + cmd->req.cqe->status = NVME_SC_CMD_SEQ_ERROR; + nvmet_req_uninit(&cmd->req); + } nvmet_tcp_free_cmd_buffers(cmd); - nvmet_tcp_fatal_error(queue); ret = -EPROTO; goto out; } diff --git a/drivers/nvme/target/zns.c b/drivers/nvme/target/zns.c index aeaf73b54c3a..f00921931eb6 100644 --- a/drivers/nvme/target/zns.c +++ b/drivers/nvme/target/zns.c @@ -69,7 +69,6 @@ bool nvmet_bdev_zns_enable(struct nvmet_ns *ns) void nvmet_execute_identify_ctrl_zns(struct nvmet_req *req) { u8 zasl = req->sq->ctrl->subsys->zasl; - struct nvmet_ctrl *ctrl = req->sq->ctrl; struct nvme_id_ctrl_zns *id; u16 status; @@ -79,10 +78,7 @@ void nvmet_execute_identify_ctrl_zns(struct nvmet_req *req) goto out; } - if (ctrl->ops->get_mdts) - id->zasl = min_t(u8, ctrl->ops->get_mdts(ctrl), zasl); - else - id->zasl = zasl; + id->zasl = min_not_zero(nvmet_ctrl_mdts(req), zasl); status = nvmet_copy_to_sgl(req, 0, id, sizeof(*id)); diff --git a/drivers/nvmem/qnap-mcu-eeprom.c b/drivers/nvmem/qnap-mcu-eeprom.c index 0b919895b3b2..07bdaa2a33fa 100644 --- a/drivers/nvmem/qnap-mcu-eeprom.c +++ b/drivers/nvmem/qnap-mcu-eeprom.c @@ -86,10 +86,10 @@ static int qnap_mcu_eeprom_probe(struct platform_device *pdev) nvcfg.read_only = true; nvcfg.root_only = false; nvcfg.reg_read = qnap_mcu_eeprom_read; - nvcfg.size = QNAP_MCU_EEPROM_SIZE, - nvcfg.word_size = 1, - nvcfg.stride = 1, - nvcfg.priv = mcu, + nvcfg.size = QNAP_MCU_EEPROM_SIZE; + nvcfg.word_size = 1; + nvcfg.stride = 1; + nvcfg.priv = mcu; ndev = devm_nvmem_register(&pdev->dev, &nvcfg); if (IS_ERR(ndev)) diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c index d88f12c53242..0ec78b5e19e7 100644 --- a/drivers/nvmem/rockchip-otp.c +++ b/drivers/nvmem/rockchip-otp.c @@ -27,6 +27,7 @@ #define OTPC_USER_CTRL 0x0100 #define OTPC_USER_ADDR 0x0104 #define OTPC_USER_ENABLE 0x0108 +#define OTPC_USER_QP 0x0120 #define OTPC_USER_Q 0x0124 #define OTPC_INT_STATUS 0x0304 #define OTPC_SBPI_CMD0_OFFSET 0x1000 @@ -59,7 +60,6 @@ #define RK3588_OTPC_AUTO_EN 0x08 #define RK3588_OTPC_INT_ST 0x84 #define RK3588_OTPC_DOUT0 0x20 -#define RK3588_NBYTES 4 #define RK3588_BURST_NUM 1 #define RK3588_BURST_SHIFT 8 #define RK3588_ADDR_SHIFT 16 @@ -69,6 +69,7 @@ struct rockchip_data { int size; int read_offset; + int word_size; const char * const *clks; int num_clks; nvmem_reg_read_t reg_read; @@ -184,49 +185,81 @@ read_end: return ret; } -static int rk3588_otp_read(void *context, unsigned int offset, - void *val, size_t bytes) +static int rk3568_otp_read(void *context, unsigned int offset, void *val, + size_t count) { struct rockchip_otp *otp = context; - unsigned int addr_start, addr_end, addr_len; - int ret, i = 0; - u32 data; - u8 *buf; + u16 *buf = val; + u32 otp_qp; + int ret; - addr_start = round_down(offset, RK3588_NBYTES) / RK3588_NBYTES; - addr_end = round_up(offset + bytes, RK3588_NBYTES) / RK3588_NBYTES; - addr_len = addr_end - addr_start; - addr_start += otp->data->read_offset / RK3588_NBYTES; + ret = rockchip_otp_reset(otp); + if (ret) { + dev_err(otp->dev, "failed to reset otp phy\n"); + return ret; + } - buf = kzalloc(array_size(addr_len, RK3588_NBYTES), GFP_KERNEL); - if (!buf) - return -ENOMEM; + ret = rockchip_otp_ecc_enable(otp, true); + if (ret) { + dev_err(otp->dev, "rockchip_otp_ecc_enable err\n"); + return ret; + } - while (addr_len--) { - writel((addr_start << RK3588_ADDR_SHIFT) | + writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); + udelay(5); + + while (count--) { + writel(offset++ | OTPC_USER_ADDR_MASK, + otp->base + OTPC_USER_ADDR); + writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, + otp->base + OTPC_USER_ENABLE); + + ret = rockchip_otp_wait_status(otp, OTPC_INT_STATUS, + OTPC_USER_DONE); + if (ret) { + dev_err(otp->dev, "timeout during read setup\n"); + goto read_end; + } + + otp_qp = readl(otp->base + OTPC_USER_QP); + if (((otp_qp & 0xc0) == 0xc0) || (otp_qp & 0x20)) { + ret = -EIO; + dev_err(otp->dev, "ecc check error during read setup\n"); + goto read_end; + } + + *buf++ = readl(otp->base + OTPC_USER_Q); + } + +read_end: + writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); + + return ret; +} + +static int rk3588_otp_read(void *context, unsigned int offset, + void *val, size_t count) +{ + struct rockchip_otp *otp = context; + u32 *buf = val; + int ret; + + while (count--) { + writel((offset++ << RK3588_ADDR_SHIFT) | (RK3588_BURST_NUM << RK3588_BURST_SHIFT), otp->base + RK3588_OTPC_AUTO_CTRL); writel(RK3588_AUTO_EN, otp->base + RK3588_OTPC_AUTO_EN); ret = rockchip_otp_wait_status(otp, RK3588_OTPC_INT_ST, RK3588_RD_DONE); - if (ret < 0) { + if (ret) { dev_err(otp->dev, "timeout during read setup\n"); - goto read_end; + return ret; } - data = readl(otp->base + RK3588_OTPC_DOUT0); - memcpy(&buf[i], &data, RK3588_NBYTES); - - i += RK3588_NBYTES; - addr_start++; + *buf++ = readl(otp->base + RK3588_OTPC_DOUT0); } - memcpy(val, buf + offset % RK3588_NBYTES, bytes); - -read_end: - kfree(buf); - return ret; } @@ -234,7 +267,7 @@ static int rockchip_otp_read(void *context, unsigned int offset, void *val, size_t bytes) { struct rockchip_otp *otp = context; - int ret; + int ret, word_size; if (!otp->data || !otp->data->reg_read) return -EINVAL; @@ -245,8 +278,34 @@ static int rockchip_otp_read(void *context, unsigned int offset, return ret; } - ret = otp->data->reg_read(context, offset, val, bytes); + offset += otp->data->read_offset; + word_size = otp->data->word_size; + if (word_size > 1) { + unsigned int addr_start, addr_end; + size_t count; + u8 *buf; + + addr_start = offset / word_size; + addr_end = DIV_ROUND_UP(offset + bytes, word_size); + count = addr_end - addr_start; + + buf = kzalloc(array_size(count, word_size), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto err; + } + + ret = otp->data->reg_read(context, addr_start, buf, count); + if (!ret) + memcpy(val, buf + (offset % word_size), bytes); + + kfree(buf); + } else { + ret = otp->data->reg_read(context, offset, val, bytes); + } + +err: clk_bulk_disable_unprepare(otp->data->num_clks, otp->clks); return ret; @@ -259,7 +318,7 @@ static struct nvmem_config otp_config = { .type = NVMEM_TYPE_OTP, .read_only = true, .stride = 1, - .word_size = 1, + .word_size = sizeof(u8), .reg_read = rockchip_otp_read, }; @@ -274,9 +333,34 @@ static const struct rockchip_data px30_data = { .reg_read = px30_otp_read, }; +static const char * const rk3528_otp_clocks[] = { + "otp", "apb_pclk", "sbpi", +}; + +static const struct rockchip_data rk3528_data = { + .size = 0x80, + .word_size = sizeof(u16), + .clks = rk3528_otp_clocks, + .num_clks = ARRAY_SIZE(rk3528_otp_clocks), + .reg_read = rk3568_otp_read, +}; + +static const char * const rk3568_otp_clocks[] = { + "otp", "apb_pclk", "phy", "sbpi", +}; + +static const struct rockchip_data rk3568_data = { + .size = 0x80, + .word_size = sizeof(u16), + .clks = rk3568_otp_clocks, + .num_clks = ARRAY_SIZE(rk3568_otp_clocks), + .reg_read = rk3568_otp_read, +}; + static const struct rockchip_data rk3576_data = { .size = 0x100, .read_offset = 0x700, + .word_size = sizeof(u32), .clks = px30_otp_clocks, .num_clks = ARRAY_SIZE(px30_otp_clocks), .reg_read = rk3588_otp_read, @@ -289,6 +373,7 @@ static const char * const rk3588_otp_clocks[] = { static const struct rockchip_data rk3588_data = { .size = 0x400, .read_offset = 0xc00, + .word_size = sizeof(u32), .clks = rk3588_otp_clocks, .num_clks = ARRAY_SIZE(rk3588_otp_clocks), .reg_read = rk3588_otp_read, @@ -303,6 +388,18 @@ static const struct of_device_id rockchip_otp_match[] = { .compatible = "rockchip,rk3308-otp", .data = &px30_data, }, + { + .compatible = "rockchip,rk3528-otp", + .data = &rk3528_data, + }, + { + .compatible = "rockchip,rk3562-otp", + .data = &rk3568_data, + }, + { + .compatible = "rockchip,rk3568-otp", + .data = &rk3568_data, + }, { .compatible = "rockchip,rk3576-otp", .data = &rk3576_data, diff --git a/drivers/of/base.c b/drivers/of/base.c index 180dbce65b98..a650c91897cc 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -463,13 +463,12 @@ int of_machine_read_model(const char **model) EXPORT_SYMBOL_GPL(of_machine_read_model); /** - * of_machine_device_match - Test root of device tree against a of_device_id array + * of_machine_get_match - Test root of device tree against an of_device_id array * @matches: NULL terminated array of of_device_id match structures to search in * - * Returns true if the root node has any of the given compatible values in its - * compatible property. + * Returns matched entry or NULL */ -bool of_machine_device_match(const struct of_device_id *matches) +const struct of_device_id *of_machine_get_match(const struct of_device_id *matches) { struct device_node *root; const struct of_device_id *match = NULL; @@ -480,9 +479,9 @@ bool of_machine_device_match(const struct of_device_id *matches) of_node_put(root); } - return match != NULL; + return match; } -EXPORT_SYMBOL(of_machine_device_match); +EXPORT_SYMBOL(of_machine_get_match); /** * of_machine_get_match_data - Tell if root of device tree has a matching of_match structure @@ -493,15 +492,8 @@ EXPORT_SYMBOL(of_machine_device_match); const void *of_machine_get_match_data(const struct of_device_id *matches) { const struct of_device_id *match; - struct device_node *root; - - root = of_find_node_by_path("/"); - if (!root) - return NULL; - - match = of_match_node(matches, root); - of_node_put(root); + match = of_machine_get_match(matches); if (!match) return NULL; diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 331646d667b9..82f7327c59ea 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -68,7 +68,7 @@ void __init of_fdt_limit_memory(int limit) bool of_fdt_device_is_available(const void *blob, unsigned long node) { - const char *status = fdt_getprop(blob, node, "status", NULL); + const char *status = fdt_stringlist_get(blob, node, "status", 0, NULL); if (!status) return true; @@ -677,22 +677,15 @@ void __init of_flat_dt_read_addr_size(const __be32 *prop, int entry_index, * specific compatible values. */ static int of_fdt_is_compatible(const void *blob, - unsigned long node, const char *compat) + unsigned long node, const char *compat) { const char *cp; - int cplen; - unsigned long l, score = 0; + int idx = 0, score = 0; - cp = fdt_getprop(blob, node, "compatible", &cplen); - if (cp == NULL) - return 0; - while (cplen > 0) { + while ((cp = fdt_stringlist_get(blob, node, "compatible", idx++, NULL))) { score++; if (of_compat_cmp(cp, compat, strlen(compat)) == 0) return score; - l = strlen(cp) + 1; - cp += l; - cplen -= l; } return 0; @@ -741,9 +734,10 @@ const char * __init of_flat_dt_get_machine_name(void) const char *name; unsigned long dt_root = of_get_flat_dt_root(); - name = of_get_flat_dt_prop(dt_root, "model", NULL); + name = fdt_stringlist_get(initial_boot_params, dt_root, "model", 0, NULL); if (!name) - name = of_get_flat_dt_prop(dt_root, "compatible", NULL); + name = fdt_stringlist_get(initial_boot_params, dt_root, + "compatible", 0, NULL); return name; } @@ -775,19 +769,14 @@ const void * __init of_flat_dt_match_machine(const void *default_match, } if (!best_data) { const char *prop; - int size; + int idx = 0, size; pr_err("\n unrecognized device tree list:\n[ "); - prop = of_get_flat_dt_prop(dt_root, "compatible", &size); - if (prop) { - while (size > 0) { - printk("'%s' ", prop); - size -= strlen(prop) + 1; - prop += strlen(prop) + 1; - } - } - printk("]\n\n"); + while ((prop = fdt_stringlist_get(initial_boot_params, dt_root, + "compatible", idx++, &size))) + pr_err("'%s' ", prop); + pr_err("]\n\n"); return NULL; } @@ -866,6 +855,26 @@ static void __init early_init_dt_check_for_elfcorehdr(unsigned long node) elfcorehdr_addr, elfcorehdr_size); } +static void __init early_init_dt_check_for_dmcryptkeys(unsigned long node) +{ + const char *prop_name = "linux,dmcryptkeys"; + const __be32 *prop; + + if (!IS_ENABLED(CONFIG_CRASH_DM_CRYPT)) + return; + + pr_debug("Looking for dmcryptkeys property... "); + + prop = of_get_flat_dt_prop(node, prop_name, NULL); + if (!prop) + return; + + dm_crypt_keys_addr = dt_mem_next_cell(dt_root_addr_cells, &prop); + + /* Property only accessible to crash dump kernel */ + fdt_delprop(initial_boot_params, node, prop_name); +} + static unsigned long chosen_node_offset = -FDT_ERR_NOTFOUND; /* @@ -954,9 +963,9 @@ int __init early_init_dt_scan_chosen_stdout(void) if (offset < 0) return -ENOENT; - p = fdt_getprop(fdt, offset, "stdout-path", &l); + p = fdt_stringlist_get(fdt, offset, "stdout-path", 0, &l); if (!p) - p = fdt_getprop(fdt, offset, "linux,stdout-path", &l); + p = fdt_stringlist_get(fdt, offset, "linux,stdout-path", 0, &l); if (!p || !l) return -ENOENT; @@ -1032,7 +1041,8 @@ int __init early_init_dt_scan_memory(void) const void *fdt = initial_boot_params; fdt_for_each_subnode(node, fdt, 0) { - const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const char *type = fdt_stringlist_get(fdt, node, + "device_type", 0, NULL); const __be32 *reg; int i, l; bool hotpluggable; @@ -1097,6 +1107,7 @@ int __init early_init_dt_scan_chosen(char *cmdline) early_init_dt_check_for_initrd(node); early_init_dt_check_for_elfcorehdr(node); + early_init_dt_check_for_dmcryptkeys(node); rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l); if (rng_seed && l > 0) { @@ -1274,7 +1285,7 @@ void __init unflatten_device_tree(void) void *fdt = initial_boot_params; /* Save the statically-placed regions in the reserved_mem array */ - fdt_scan_reserved_mem_reg_nodes(); + fdt_scan_reserved_mem_late(); /* Populate an empty root node when bootloader doesn't provide one */ if (!fdt) { diff --git a/drivers/of/kexec.c b/drivers/of/kexec.c index c4cf3552c018..b6837e299e7f 100644 --- a/drivers/of/kexec.c +++ b/drivers/of/kexec.c @@ -175,7 +175,7 @@ int __init ima_free_kexec_buffer(void) if (ret) return ret; - memblock_free_late(addr, size); + memblock_phys_free(addr, size); return 0; } #endif @@ -423,6 +423,25 @@ void *of_kexec_alloc_and_setup_fdt(const struct kimage *image, if (ret) goto out; + if (image->dm_crypt_keys_addr != 0) { + ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, + "linux,dmcryptkeys", + image->dm_crypt_keys_addr, + image->dm_crypt_keys_sz); + + if (ret) + goto out; + + /* + * Avoid dmcryptkeys from being stomped on in kdump kernel by + * setting up memory reserve map. + */ + ret = fdt_add_mem_rsv(fdt, image->dm_crypt_keys_addr, + image->dm_crypt_keys_sz); + if (ret) + goto out; + } + #ifdef CONFIG_CRASH_DUMP /* add linux,usable-memory-range */ ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index df0bb00349e0..0ae16da066e2 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -186,7 +186,7 @@ static inline struct device_node *__of_get_dma_parent(const struct device_node * #endif int fdt_scan_reserved_mem(void); -void __init fdt_scan_reserved_mem_reg_nodes(void); +void __init fdt_scan_reserved_mem_late(void); bool of_fdt_device_is_available(const void *blob, unsigned long node); diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 1fd28f805610..8d5777cb5d1b 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -24,8 +24,6 @@ #include #include #include -#include -#include #include "of_private.h" @@ -104,30 +102,12 @@ static void __init alloc_reserved_mem_array(void) reserved_mem = new_array; } -static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem); -/* - * fdt_reserved_mem_save_node() - save fdt node for second pass initialization - */ -static void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname, - phys_addr_t base, phys_addr_t size) -{ - struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; - - if (reserved_mem_count == total_reserved_mem_cnt) { - pr_err("not enough space for all defined regions.\n"); - return; - } - - rmem->fdt_node = node; - rmem->name = uname; - rmem->base = base; - rmem->size = size; - - /* Call the region specific initialization function */ - fdt_init_reserved_mem_node(rmem); - - reserved_mem_count++; -} +static void fdt_init_reserved_mem_node(unsigned long node, const char *uname, + phys_addr_t base, phys_addr_t size); +static int fdt_validate_reserved_mem_node(unsigned long node, + phys_addr_t *align); +static int fdt_fixup_reserved_mem_node(unsigned long node, + phys_addr_t base, phys_addr_t size); static int __init early_init_dt_reserve_memory(phys_addr_t base, phys_addr_t size, bool nomap) @@ -154,21 +134,19 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, const char *uname) { phys_addr_t base, size; - int i, len; + int i, len, err; const __be32 *prop; - bool nomap, default_cma; + bool nomap; prop = of_flat_dt_get_addr_size_prop(node, "reg", &len); if (!prop) return -ENOENT; nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; - default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL); - if (default_cma && cma_skip_dt_default_reserved_mem()) { - pr_err("Skipping dt linux,cma-default for \"cma=\" kernel param.\n"); - return -EINVAL; - } + err = fdt_validate_reserved_mem_node(node, NULL); + if (err && err != -ENODEV) + return err; for (i = 0; i < len; i++) { u64 b, s; @@ -179,10 +157,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, size = s; if (size && early_init_dt_reserve_memory(base, size, nomap) == 0) { - /* Architecture specific contiguous memory fixup. */ - if (of_flat_dt_is_compatible(node, "shared-dma-pool") && - of_get_flat_dt_prop(node, "reusable", NULL)) - dma_contiguous_early_fixup(base, size); + fdt_fixup_reserved_mem_node(node, base, size); pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n", uname, &base, (unsigned long)(size / SZ_1M)); } else { @@ -216,19 +191,66 @@ static int __init __reserved_mem_check_root(unsigned long node) return 0; } -static void __init __rmem_check_for_overlap(void); +static int __init __rmem_cmp(const void *a, const void *b) +{ + const struct reserved_mem *ra = a, *rb = b; + + if (ra->base < rb->base) + return -1; + + if (ra->base > rb->base) + return 1; + + /* + * Put the dynamic allocations (address == 0, size == 0) before static + * allocations at address 0x0 so that overlap detection works + * correctly. + */ + if (ra->size < rb->size) + return -1; + if (ra->size > rb->size) + return 1; + + return 0; +} + +static void __init __rmem_check_for_overlap(void) +{ + int i; + + if (reserved_mem_count < 2) + return; + + sort(reserved_mem, reserved_mem_count, sizeof(reserved_mem[0]), + __rmem_cmp, NULL); + for (i = 0; i < reserved_mem_count - 1; i++) { + struct reserved_mem *this, *next; + + this = &reserved_mem[i]; + next = &reserved_mem[i + 1]; + + if (this->base + this->size > next->base) { + phys_addr_t this_end, next_end; + + this_end = this->base + this->size; + next_end = next->base + next->size; + pr_err("OVERLAP DETECTED!\n%s (%pa--%pa) overlaps with %s (%pa--%pa)\n", + this->name, &this->base, &this_end, + next->name, &next->base, &next_end); + } + } +} /** - * fdt_scan_reserved_mem_reg_nodes() - Store info for the "reg" defined - * reserved memory regions. + * fdt_scan_reserved_mem_late() - Scan FDT and initialize remaining reserved + * memory regions. * - * This function is used to scan through the DT and store the - * information for the reserved memory regions that are defined using - * the "reg" property. The region node number, name, base address, and - * size are all stored in the reserved_mem array by calling the - * fdt_reserved_mem_save_node() function. + * This function is used to scan again through the DT and initialize the + * "static" reserved memory regions, that are defined using the "reg" + * property. Each such region is then initialized with its specific init + * function and stored in the global reserved_mem array. */ -void __init fdt_scan_reserved_mem_reg_nodes(void) +void __init fdt_scan_reserved_mem_late(void) { const void *fdt = initial_boot_params; phys_addr_t base, size; @@ -253,23 +275,25 @@ void __init fdt_scan_reserved_mem_reg_nodes(void) fdt_for_each_subnode(child, fdt, node) { const char *uname; - bool default_cma = of_get_flat_dt_prop(child, "linux,cma-default", NULL); u64 b, s; + int ret; if (!of_fdt_device_is_available(fdt, child)) continue; - if (default_cma && cma_skip_dt_default_reserved_mem()) - continue; if (!of_flat_dt_get_addr_size(child, "reg", &b, &s)) continue; + ret = fdt_validate_reserved_mem_node(child, NULL); + if (ret && ret != -ENODEV) + continue; + base = b; size = s; if (size) { uname = fdt_get_name(fdt, child, NULL); - fdt_reserved_mem_save_node(child, uname, base, size); + fdt_init_reserved_mem_node(child, uname, base, size); } } @@ -280,7 +304,14 @@ void __init fdt_scan_reserved_mem_reg_nodes(void) static int __init __reserved_mem_alloc_size(unsigned long node, const char *uname); /* - * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory + * fdt_scan_reserved_mem() - reserve and allocate memory occupied by + * reserved memory regions. + * + * This function is used to scan through the FDT and mark memory occupied + * by all static (defined by the "reg" property) reserved memory regions. + * Then memory for all dynamic regions (defined by size & alignment) is + * allocated, a region specific init function is called and region information + * is stored in the reserved_mem array. */ int __init fdt_scan_reserved_mem(void) { @@ -397,7 +428,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam phys_addr_t base = 0, align = 0, size; int i, len; const __be32 *prop; - bool nomap, default_cma; + bool nomap; int ret; prop = of_get_flat_dt_prop(node, "size", &len); @@ -421,19 +452,10 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam } nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; - default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL); - if (default_cma && cma_skip_dt_default_reserved_mem()) { - pr_err("Skipping dt linux,cma-default for \"cma=\" kernel param.\n"); - return -EINVAL; - } - - /* Need adjust the alignment to satisfy the CMA requirement */ - if (IS_ENABLED(CONFIG_CMA) - && of_flat_dt_is_compatible(node, "shared-dma-pool") - && of_get_flat_dt_prop(node, "reusable", NULL) - && !nomap) - align = max_t(phys_addr_t, align, CMA_MIN_ALIGNMENT_BYTES); + ret = fdt_validate_reserved_mem_node(node, &align); + if (ret && ret != -ENODEV) + return ret; prop = of_flat_dt_get_addr_size_prop(node, "alloc-ranges", &len); if (prop) { @@ -468,121 +490,151 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam uname, (unsigned long)(size / SZ_1M)); return -ENOMEM; } - /* Architecture specific contiguous memory fixup. */ - if (of_flat_dt_is_compatible(node, "shared-dma-pool") && - of_get_flat_dt_prop(node, "reusable", NULL)) - dma_contiguous_early_fixup(base, size); - /* Save region in the reserved_mem array */ - fdt_reserved_mem_save_node(node, uname, base, size); + + fdt_fixup_reserved_mem_node(node, base, size); + fdt_init_reserved_mem_node(node, uname, base, size); + return 0; } +extern const struct of_device_id __reservedmem_of_table[]; static const struct of_device_id __rmem_of_table_sentinel __used __section("__reservedmem_of_table_end"); -/* - * __reserved_mem_init_node() - call region specific reserved memory init code +/** + * fdt_fixup_reserved_mem_node() - call fixup function for a reserved memory node + * @node: FDT node to fixup + * @base: base address of the reserved memory region + * @size: size of the reserved memory region + * + * This function iterates through the reserved memory drivers and calls + * the node_fixup callback for the compatible entry matching the node. + * + * Return: 0 on success, -ENODEV if no compatible match found */ -static int __init __reserved_mem_init_node(struct reserved_mem *rmem) +static int __init fdt_fixup_reserved_mem_node(unsigned long node, + phys_addr_t base, phys_addr_t size) { - extern const struct of_device_id __reservedmem_of_table[]; const struct of_device_id *i; - int ret = -ENOENT; + int ret = -ENODEV; - for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) { - reservedmem_of_init_fn initfn = i->data; - const char *compat = i->compatible; + for (i = __reservedmem_of_table; ret == -ENODEV && + i < &__rmem_of_table_sentinel; i++) { + const struct reserved_mem_ops *ops = i->data; - if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) + if (!of_flat_dt_is_compatible(node, i->compatible)) continue; - ret = initfn(rmem); + if (ops->node_fixup) + ret = ops->node_fixup(node, base, size); + } + return ret; +} + +/** + * fdt_validate_reserved_mem_node() - validate a reserved memory node + * @node: FDT node to validate + * @align: pointer to store the validated alignment (may be modified by callback) + * + * This function iterates through the reserved memory drivers and calls + * the node_validate callback for the compatible entry matching the node. + * + * Return: 0 on success, -ENODEV if no compatible match found + */ +static int __init fdt_validate_reserved_mem_node(unsigned long node, phys_addr_t *align) +{ + const struct of_device_id *i; + int ret = -ENODEV; + + for (i = __reservedmem_of_table; ret == -ENODEV && + i < &__rmem_of_table_sentinel; i++) { + const struct reserved_mem_ops *ops = i->data; + + if (!of_flat_dt_is_compatible(node, i->compatible)) + continue; + + if (ops->node_validate) + ret = ops->node_validate(node, align); + } + return ret; +} + +/** + * __reserved_mem_init_node() - initialize a reserved memory region + * @rmem: reserved_mem structure to initialize + * @node: FDT node describing the reserved memory region + * + * This function iterates through the reserved memory drivers and calls the + * node_init callback for the compatible entry matching the node. On success, + * the operations pointer is stored in the reserved_mem structure. + * + * Return: 0 on success, -ENODEV if no compatible match found + */ +static int __init __reserved_mem_init_node(struct reserved_mem *rmem, + unsigned long node) +{ + const struct of_device_id *i; + int ret = -ENODEV; + + for (i = __reservedmem_of_table; ret == -ENODEV && + i < &__rmem_of_table_sentinel; i++) { + const struct reserved_mem_ops *ops = i->data; + const char *compat = i->compatible; + + if (!of_flat_dt_is_compatible(node, compat)) + continue; + + ret = ops->node_init(node, rmem); if (ret == 0) { + rmem->ops = ops; pr_info("initialized node %s, compatible id %s\n", rmem->name, compat); - break; + return ret; } } return ret; } -static int __init __rmem_cmp(const void *a, const void *b) -{ - const struct reserved_mem *ra = a, *rb = b; - - if (ra->base < rb->base) - return -1; - - if (ra->base > rb->base) - return 1; - - /* - * Put the dynamic allocations (address == 0, size == 0) before static - * allocations at address 0x0 so that overlap detection works - * correctly. - */ - if (ra->size < rb->size) - return -1; - if (ra->size > rb->size) - return 1; - - if (ra->fdt_node < rb->fdt_node) - return -1; - if (ra->fdt_node > rb->fdt_node) - return 1; - - return 0; -} - -static void __init __rmem_check_for_overlap(void) -{ - int i; - - if (reserved_mem_count < 2) - return; - - sort(reserved_mem, reserved_mem_count, sizeof(reserved_mem[0]), - __rmem_cmp, NULL); - for (i = 0; i < reserved_mem_count - 1; i++) { - struct reserved_mem *this, *next; - - this = &reserved_mem[i]; - next = &reserved_mem[i + 1]; - - if (this->base + this->size > next->base) { - phys_addr_t this_end, next_end; - - this_end = this->base + this->size; - next_end = next->base + next->size; - pr_err("OVERLAP DETECTED!\n%s (%pa--%pa) overlaps with %s (%pa--%pa)\n", - this->name, &this->base, &this_end, - next->name, &next->base, &next_end); - } - } -} - /** * fdt_init_reserved_mem_node() - Initialize a reserved memory region - * @rmem: reserved_mem struct of the memory region to be initialized. + * @node: fdt node of the initialized region + * @uname: name of the reserved memory node + * @base: base address of the reserved memory region + * @size: size of the reserved memory region * - * This function is used to call the region specific initialization - * function for a reserved memory region. + * This function calls the region-specific initialization function for a + * reserved memory region and saves all region-specific data to the + * reserved_mem array to allow of_reserved_mem_lookup() to find it. */ -static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem) +static void __init fdt_init_reserved_mem_node(unsigned long node, const char *uname, + phys_addr_t base, phys_addr_t size) { - unsigned long node = rmem->fdt_node; int err = 0; bool nomap; + struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; + + if (reserved_mem_count == total_reserved_mem_cnt) { + pr_err("not enough space for all defined regions.\n"); + return; + } + + rmem->name = uname; + rmem->base = base; + rmem->size = size; + nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; - err = __reserved_mem_init_node(rmem); - if (err != 0 && err != -ENOENT) { + err = __reserved_mem_init_node(rmem, node); + if (err != 0 && err != -ENODEV) { pr_info("node %s compatible matching fail\n", rmem->name); + rmem->name = NULL; + if (nomap) memblock_clear_nomap(rmem->base, rmem->size); else memblock_phys_free(rmem->base, rmem->size); + return; } else { phys_addr_t end = rmem->base + rmem->size - 1; bool reusable = @@ -594,6 +646,8 @@ static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem) reusable ? "reusable" : "non-reusable", rmem->name ? rmem->name : "unknown"); } + + reserved_mem_count++; } struct rmem_assigned_device { diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 7eeaf8e27b5b..a42224f9d1a8 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -500,7 +500,7 @@ static const struct of_device_id reserved_mem_matches[] = { static int __init of_platform_default_populate_init(void) { - struct device_node *node; + struct device_node *node, *reserved; device_links_supplier_sync_state_pause(); @@ -563,8 +563,14 @@ static int __init of_platform_default_populate_init(void) * platform_devices for every node in /reserved-memory with a * "compatible", */ - for_each_matching_node(node, reserved_mem_matches) - of_platform_device_create(node, NULL, NULL); + reserved = of_find_node_by_path("/reserved-memory"); + if (reserved) { + for_each_child_of_node(reserved, node) { + if (of_match_node(reserved_mem_matches, node)) + of_platform_device_create(node, NULL, NULL); + } + of_node_put(reserved); + } node = of_find_node_by_path("/firmware"); if (node) { diff --git a/drivers/of/property.c b/drivers/of/property.c index 50d95d512bf5..136946f8b746 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -88,7 +88,7 @@ EXPORT_SYMBOL(of_graph_is_present); * Search for a property in a device node and count the number of elements of * size elem_size in it. * - * Return: The number of elements on sucess, -EINVAL if the property does not + * Return: The number of elements on success, -EINVAL if the property does not * exist or its length does not match a multiple of elem_size and -ENODATA if * the property does not have a value. */ @@ -1620,12 +1620,36 @@ static int of_fwnode_irq_get(const struct fwnode_handle *fwnode, return of_irq_get(to_of_node(fwnode), index); } +static int match_property_by_path(const char *node_path, const char *prop_name, + const char *value) +{ + struct device_node *np __free(device_node) = of_find_node_by_path(node_path); + + return of_property_match_string(np, prop_name, value); +} + +static bool of_is_fwnode_add_links_supported(void) +{ + static int is_supported = -1; + + if (!IS_ENABLED(CONFIG_X86)) + return true; + + if (is_supported != -1) + return !!is_supported; + + is_supported = !((match_property_by_path("/soc", "compatible", "intel,ce4100-cp") >= 0) || + (match_property_by_path("/", "architecture", "OLPC") >= 0)); + + return !!is_supported; +} + static int of_fwnode_add_links(struct fwnode_handle *fwnode) { const struct property *p; struct device_node *con_np = to_of_node(fwnode); - if (IS_ENABLED(CONFIG_X86)) + if (!of_is_fwnode_add_links_supported()) return 0; if (!con_np) diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 2940295843e6..4078569a0f96 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -896,8 +896,6 @@ static void __init of_unittest_changeset(void) unittest(!of_changeset_apply(&chgset), "apply failed\n"); - of_node_put(nchangeset); - /* Make sure node names are constructed correctly */ unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")), "'%pOF' not added\n", n21); @@ -919,6 +917,7 @@ static void __init of_unittest_changeset(void) if (!ret) unittest(strcmp(propstr, "hello") == 0, "original value not in updated property after revert"); + of_node_put(nchangeset); of_changeset_destroy(&chgset); of_node_put(n1); @@ -4318,7 +4317,6 @@ static int testdrv_probe(struct pci_dev *pdev, const struct pci_device_id *id) size = info->dtbo_end - info->dtbo_begin; ret = of_overlay_fdt_apply(info->dtbo_begin, size, &ovcs_id, dn); - of_node_put(dn); if (ret) return ret; diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c index d5b95e63c24e..dc1b6d11f9ab 100644 --- a/drivers/parisc/ccio-dma.c +++ b/drivers/parisc/ccio-dma.c @@ -503,8 +503,8 @@ typedef unsigned long space_t; /* -** Use direction (ie PCI_DMA_TODEVICE) to pick hint. -** ccio_alloc_consistent() depends on this to get SAFE_DMA +** Use direction (ie DMA_TO_DEVICE) to pick hint. +** ccio_alloc() depends on this to get SAFE_DMA ** when it passes in BIDIRECTIONAL flag. */ static u32 hint_lookup[] = { @@ -865,8 +865,8 @@ ccio_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag, * ccio_free - Free a consistent DMA mapping. * @dev: The PCI device. * @size: The length of the DMA region. - * @cpu_addr: The cpu address returned from the ccio_alloc_consistent. - * @dma_handle: The device address returned from the ccio_alloc_consistent. + * @cpu_addr: The cpu address returned from ccio_alloc(). + * @dma_handle: The device address returned from ccio_alloc(). * @attrs: attributes * * This function implements the pci_free_consistent function. diff --git a/drivers/parisc/lasi.c b/drivers/parisc/lasi.c index ef6125d83878..a5b80cd5cc37 100644 --- a/drivers/parisc/lasi.c +++ b/drivers/parisc/lasi.c @@ -193,8 +193,7 @@ static int __init lasi_init_chip(struct parisc_device *dev) ret = request_irq(lasi->gsc_irq.irq, gsc_asic_intr, 0, "lasi", lasi); if (ret < 0) { - kfree(lasi); - return ret; + goto err_free; } /* enable IRQ's for devices below LASI */ @@ -203,8 +202,7 @@ static int __init lasi_init_chip(struct parisc_device *dev) /* Done init'ing, register this driver */ ret = gsc_common_setup(dev, lasi); if (ret) { - kfree(lasi); - return ret; + goto err_irq; } gsc_fixup_irqs(dev, lasi, lasi_choose_irq); @@ -214,6 +212,12 @@ static int __init lasi_init_chip(struct parisc_device *dev) SYS_OFF_PRIO_DEFAULT, lasi_power_off, lasi); return ret; + +err_irq: + free_irq(lasi->gsc_irq.irq, lasi); +err_free: + kfree(lasi); + return ret; } static struct parisc_device_id lasi_tbl[] __initdata = { diff --git a/drivers/parport/TODO-parport b/drivers/parport/TODO-parport index 089b14ee446f..661a5ab91c0a 100644 --- a/drivers/parport/TODO-parport +++ b/drivers/parport/TODO-parport @@ -13,7 +13,7 @@ Things to be done. bits when they have something to say. We should read out and deal with (maybe just log) whatever the printer wants to tell the world. -3. Support more hardware (eg m68k, Sun bpp). +3. Support more hardware (eg m68k). 4. A better PLIP (make use of bidirectional/ECP/EPP ports). diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 686349e09cd3..2247709ef6d6 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -187,7 +187,7 @@ config VMD config PCI_LOONGSON bool "LOONGSON PCIe controller" - depends on MACH_LOONGSON64 || COMPILE_TEST + depends on MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST depends on OF || ACPI depends on PCI_QUIRKS default MACH_LOONGSON64 diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 49c0a2d51162..cfc8fa403dad 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -4172,7 +4172,7 @@ static int __init init_hv_pci_drv(void) if (!hv_is_hyperv_initialized()) return -ENODEV; - if (hv_root_partition() && !hv_nested) + if (!hv_vmbus_exists()) return -ENODEV; ret = hv_pci_irqchip_init(); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index d10ece0889f0..e3f59001785a 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -179,6 +179,11 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv, return NULL; } +static void _pci_free_device(struct device *dev) +{ + kfree(to_pci_dev(dev)); +} + /** * new_id_store - sysfs frontend to pci_add_dynid() * @driver: target device driver @@ -214,11 +219,13 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf, pdev->subsystem_vendor = subvendor; pdev->subsystem_device = subdevice; pdev->class = class; + pdev->dev.release = _pci_free_device; + device_initialize(&pdev->dev); if (pci_match_device(pdrv, pdev)) retval = -EEXIST; - kfree(pdev); + put_device(&pdev->dev); if (retval) return retval; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8f7cfcc00090..d34266651ad0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5607,13 +5607,14 @@ static int pci_try_reset_bus(struct pci_bus *bus) * reset for affected devices * * This function will first try to reset the slots on this bus if the method is - * available. If slot reset fails or is not available, this will fall back to a + * available. If slot reset is not available, this will fall back to a * secondary bus reset. */ static int pci_reset_bridge(struct pci_dev *bridge, bool restore) { struct pci_bus *bus = bridge->subordinate; struct pci_slot *slot; + int ret = 0; if (!bus) return -ENOTTY; @@ -5627,19 +5628,17 @@ static int pci_reset_bridge(struct pci_dev *bridge, bool restore) goto bus_reset; list_for_each_entry(slot, &bus->slots, list) { - int ret; - if (restore) ret = pci_try_reset_slot(slot); else ret = pci_slot_reset(slot, PCI_RESET_DO_RESET); if (ret) - goto bus_reset; + break; } mutex_unlock(&pci_slot_mutex); - return 0; + return ret; bus_reset: mutex_unlock(&pci_slot_mutex); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 757a296eae41..caaed1a01dc0 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1296,9 +1296,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_vt /* * CardBus controllers have a legacy base address that enables them to - * respond as i82365 pcmcia controllers. We don't want them to do this - * even if the Linux CardBus driver is not loaded, because the Linux i82365 - * driver does not (and should not) handle CardBus. + * respond as i82365 PCMCIA controllers. We don't want them to do this. */ static void quirk_cardbus_legacy(struct pci_dev *dev) { diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index fbc05cda96ee..991d3ed543f5 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -102,6 +102,7 @@ static void pci_std_update_resource(struct pci_dev *dev, int resno) } pci_write_config_dword(dev, reg, new); + dev->saved_config_space[reg / 4] = new; pci_read_config_dword(dev, reg, &check); if ((new ^ check) & mask) { @@ -112,6 +113,7 @@ static void pci_std_update_resource(struct pci_dev *dev, int resno) if (res->flags & IORESOURCE_MEM_64) { new = region.start >> 16 >> 16; pci_write_config_dword(dev, reg + 4, new); + dev->saved_config_space[(reg + 4) / 4] = new; pci_read_config_dword(dev, reg + 4, &check); if (check != new) { pci_err(dev, "%s: error updating (high %#010x != %#010x)\n", diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index e0b7fb43423c..6d5cd37bfb1e 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -106,7 +106,18 @@ static struct attribute *pci_slot_default_attrs[] = { &pci_slot_attr_cur_speed.attr, NULL, }; -ATTRIBUTE_GROUPS(pci_slot_default); + +static const struct attribute_group pci_slot_default_group = { + .attrs = pci_slot_default_attrs, +}; + +static const struct attribute_group *pci_slot_default_groups[] = { + &pci_slot_default_group, +#ifdef ARCH_PCI_SLOT_GROUPS + ARCH_PCI_SLOT_GROUPS, +#endif + NULL, +}; static const struct kobj_type pci_slot_ktype = { .sysfs_ops = &pci_slot_sysfs_ops, diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 660a95805524..d1d25aa296ad 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -119,36 +119,6 @@ config PD6729 This provides support for the Cirrus PD6729 PCI-to-PCMCIA bridge device, found in some older laptops and PCMCIA card readers. -config I82092 - tristate "i82092 compatible bridge support" - depends on PCMCIA && PCI && HAS_IOPORT - select PCCARD_NONSTATIC - help - This provides support for the Intel I82092AA PCI-to-PCMCIA bridge device, - found in some older laptops and more commonly in evaluation boards for the - chip. - -config I82365 - tristate "i82365 compatible bridge support" - depends on PCMCIA && ISA - select PCCARD_NONSTATIC - help - Say Y here to include support for ISA-bus PCMCIA host bridges that - are register compatible with the Intel i82365. These are found on - older laptops and ISA-bus card readers for desktop systems. A - "bridge" is the hardware inside your computer that PCMCIA cards are - plugged into. If unsure, say N. - -config TCIC - tristate "Databook TCIC host bridge support" - depends on PCMCIA && ISA - select PCCARD_NONSTATIC - help - Say Y here to include support for the Databook TCIC family of PCMCIA - host bridges. These are only found on a handful of old systems. - "Bridge" is the name used for the hardware inside your computer that - PCMCIA cards are plugged into. If unsure, say N. - config PCMCIA_ALCHEMY_DEVBOARD tristate "Alchemy Db/Pb1xxx PCMCIA socket services" depends on MIPS_DB1XXX && PCMCIA diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index d16a0317ce43..ee2f204cbc21 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -20,9 +20,6 @@ obj-$(CONFIG_PCCARD) += pcmcia_rsrc.o obj-$(CONFIG_YENTA) += yenta_socket.o obj-$(CONFIG_PD6729) += pd6729.o -obj-$(CONFIG_I82365) += i82365.o -obj-$(CONFIG_I82092) += i82092.o -obj-$(CONFIG_TCIC) += tcic.o obj-$(CONFIG_PCMCIA_SOC_COMMON) += soc_common.o obj-$(CONFIG_PCMCIA_SA11XX_BASE) += sa11xx_base.o obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c deleted file mode 100644 index a947ffb2df55..000000000000 --- a/drivers/pcmcia/i82092.c +++ /dev/null @@ -1,679 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Driver for Intel I82092AA PCI-PCMCIA bridge. - * - * (C) 2001 Red Hat, Inc. - * - * Author: Arjan Van De Ven - * Loosly based on i82365.c from the pcmcia-cs package - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "i82092aa.h" -#include "i82365.h" - -MODULE_DESCRIPTION("Driver for Intel I82092AA PCI-PCMCIA bridge"); -MODULE_LICENSE("GPL"); - -/* PCI core routines */ -static const struct pci_device_id i82092aa_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82092AA_0) }, - { } -}; -MODULE_DEVICE_TABLE(pci, i82092aa_pci_ids); - -static struct pci_driver i82092aa_pci_driver = { - .name = "i82092aa", - .id_table = i82092aa_pci_ids, - .probe = i82092aa_pci_probe, - .remove = i82092aa_pci_remove, -}; - - -/* the pccard structure and its functions */ -static struct pccard_operations i82092aa_operations = { - .init = i82092aa_init, - .get_status = i82092aa_get_status, - .set_socket = i82092aa_set_socket, - .set_io_map = i82092aa_set_io_map, - .set_mem_map = i82092aa_set_mem_map, -}; - -/* The card can do up to 4 sockets, allocate a structure for each of them */ - -struct socket_info { - int number; - int card_state; - /* 0 = no socket, - * 1 = empty socket, - * 2 = card but not initialized, - * 3 = operational card - */ - unsigned int io_base; /* base io address of the socket */ - - struct pcmcia_socket socket; - struct pci_dev *dev; /* The PCI device for the socket */ -}; - -#define MAX_SOCKETS 4 -static struct socket_info sockets[MAX_SOCKETS]; -static int socket_count; /* shortcut */ - - -static int i82092aa_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id) -{ - unsigned char configbyte; - int i, ret; - - ret = pci_enable_device(dev); - if (ret) - return ret; - - /* PCI Configuration Control */ - pci_read_config_byte(dev, 0x40, &configbyte); - - switch (configbyte&6) { - case 0: - socket_count = 2; - break; - case 2: - socket_count = 1; - break; - case 4: - case 6: - socket_count = 4; - break; - - default: - dev_err(&dev->dev, - "Oops, you did something we didn't think of.\n"); - ret = -EIO; - goto err_out_disable; - } - dev_info(&dev->dev, "configured as a %d socket device.\n", - socket_count); - - if (!request_region(pci_resource_start(dev, 0), 2, "i82092aa")) { - ret = -EBUSY; - goto err_out_disable; - } - - for (i = 0; i < socket_count; i++) { - sockets[i].card_state = 1; /* 1 = present but empty */ - sockets[i].io_base = pci_resource_start(dev, 0); - sockets[i].dev = dev; - sockets[i].socket.features |= SS_CAP_PCCARD; - sockets[i].socket.map_size = 0x1000; - sockets[i].socket.irq_mask = 0; - sockets[i].socket.pci_irq = dev->irq; - sockets[i].socket.cb_dev = dev; - sockets[i].socket.owner = THIS_MODULE; - - sockets[i].number = i; - - if (card_present(i)) { - sockets[i].card_state = 3; - dev_dbg(&dev->dev, "slot %i is occupied\n", i); - } else { - dev_dbg(&dev->dev, "slot %i is vacant\n", i); - } - } - - /* Now, specifiy that all interrupts are to be done as PCI interrupts - * bitmask, one bit per event, 1 = PCI interrupt, 0 = ISA interrupt - */ - configbyte = 0xFF; - - /* PCI Interrupt Routing Register */ - pci_write_config_byte(dev, 0x50, configbyte); - - /* Register the interrupt handler */ - dev_dbg(&dev->dev, "Requesting interrupt %i\n", dev->irq); - ret = request_irq(dev->irq, i82092aa_interrupt, IRQF_SHARED, - "i82092aa", i82092aa_interrupt); - if (ret) { - dev_err(&dev->dev, "Failed to register IRQ %d, aborting\n", - dev->irq); - goto err_out_free_res; - } - - for (i = 0; i < socket_count; i++) { - sockets[i].socket.dev.parent = &dev->dev; - sockets[i].socket.ops = &i82092aa_operations; - sockets[i].socket.resource_ops = &pccard_nonstatic_ops; - ret = pcmcia_register_socket(&sockets[i].socket); - if (ret) - goto err_out_free_sockets; - } - - return 0; - -err_out_free_sockets: - if (i) { - for (i--; i >= 0; i--) - pcmcia_unregister_socket(&sockets[i].socket); - } - free_irq(dev->irq, i82092aa_interrupt); -err_out_free_res: - release_region(pci_resource_start(dev, 0), 2); -err_out_disable: - pci_disable_device(dev); - return ret; -} - -static void i82092aa_pci_remove(struct pci_dev *dev) -{ - int i; - - free_irq(dev->irq, i82092aa_interrupt); - - for (i = 0; i < socket_count; i++) - pcmcia_unregister_socket(&sockets[i].socket); -} - -static DEFINE_SPINLOCK(port_lock); - -/* basic value read/write functions */ - -static unsigned char indirect_read(int socket, unsigned short reg) -{ - unsigned short int port; - unsigned char val; - unsigned long flags; - - spin_lock_irqsave(&port_lock, flags); - reg += socket * 0x40; - port = sockets[socket].io_base; - outb(reg, port); - val = inb(port+1); - spin_unlock_irqrestore(&port_lock, flags); - return val; -} - -static void indirect_write(int socket, unsigned short reg, unsigned char value) -{ - unsigned short int port; - unsigned long flags; - - spin_lock_irqsave(&port_lock, flags); - reg = reg + socket * 0x40; - port = sockets[socket].io_base; - outb(reg, port); - outb(value, port+1); - spin_unlock_irqrestore(&port_lock, flags); -} - -static void indirect_setbit(int socket, unsigned short reg, unsigned char mask) -{ - unsigned short int port; - unsigned char val; - unsigned long flags; - - spin_lock_irqsave(&port_lock, flags); - reg = reg + socket * 0x40; - port = sockets[socket].io_base; - outb(reg, port); - val = inb(port+1); - val |= mask; - outb(reg, port); - outb(val, port+1); - spin_unlock_irqrestore(&port_lock, flags); -} - - -static void indirect_resetbit(int socket, - unsigned short reg, unsigned char mask) -{ - unsigned short int port; - unsigned char val; - unsigned long flags; - - spin_lock_irqsave(&port_lock, flags); - reg = reg + socket * 0x40; - port = sockets[socket].io_base; - outb(reg, port); - val = inb(port+1); - val &= ~mask; - outb(reg, port); - outb(val, port+1); - spin_unlock_irqrestore(&port_lock, flags); -} - -static void indirect_write16(int socket, - unsigned short reg, unsigned short value) -{ - unsigned short int port; - unsigned char val; - unsigned long flags; - - spin_lock_irqsave(&port_lock, flags); - reg = reg + socket * 0x40; - port = sockets[socket].io_base; - - outb(reg, port); - val = value & 255; - outb(val, port+1); - - reg++; - - outb(reg, port); - val = value>>8; - outb(val, port+1); - spin_unlock_irqrestore(&port_lock, flags); -} - -/* simple helper functions */ -/* External clock time, in nanoseconds. 120 ns = 8.33 MHz */ -static int cycle_time = 120; - -static int to_cycles(int ns) -{ - if (cycle_time != 0) - return ns/cycle_time; - else - return 0; -} - - -/* Interrupt handler functionality */ - -static irqreturn_t i82092aa_interrupt(int irq, void *dev) -{ - int i; - int loopcount = 0; - int handled = 0; - - unsigned int events, active = 0; - - while (1) { - loopcount++; - if (loopcount > 20) { - pr_err("i82092aa: infinite eventloop in interrupt\n"); - break; - } - - active = 0; - - for (i = 0; i < socket_count; i++) { - int csc; - - /* Inactive socket, should not happen */ - if (sockets[i].card_state == 0) - continue; - - /* card status change register */ - csc = indirect_read(i, I365_CSC); - - if (csc == 0) /* no events on this socket */ - continue; - handled = 1; - events = 0; - - if (csc & I365_CSC_DETECT) { - events |= SS_DETECT; - dev_info(&sockets[i].dev->dev, - "Card detected in socket %i!\n", i); - } - - if (indirect_read(i, I365_INTCTL) & I365_PC_IOCARD) { - /* For IO/CARDS, bit 0 means "read the card" */ - if (csc & I365_CSC_STSCHG) - events |= SS_STSCHG; - } else { - /* Check for battery/ready events */ - if (csc & I365_CSC_BVD1) - events |= SS_BATDEAD; - if (csc & I365_CSC_BVD2) - events |= SS_BATWARN; - if (csc & I365_CSC_READY) - events |= SS_READY; - } - - if (events) - pcmcia_parse_events(&sockets[i].socket, events); - active |= events; - } - - if (active == 0) /* no more events to handle */ - break; - } - return IRQ_RETVAL(handled); -} - - - -/* socket functions */ - -static int card_present(int socketno) -{ - unsigned int val; - - if ((socketno < 0) || (socketno >= MAX_SOCKETS)) - return 0; - if (sockets[socketno].io_base == 0) - return 0; - - - val = indirect_read(socketno, 1); /* Interface status register */ - if ((val&12) == 12) - return 1; - - return 0; -} - -static void set_bridge_state(int sock) -{ - indirect_write(sock, I365_GBLCTL, 0x00); - indirect_write(sock, I365_GENCTL, 0x00); - - indirect_setbit(sock, I365_INTCTL, 0x08); -} - - -static int i82092aa_init(struct pcmcia_socket *sock) -{ - int i; - struct resource res = { .start = 0, .end = 0x0fff }; - pccard_io_map io = { 0, 0, 0, 0, 1 }; - pccard_mem_map mem = { .res = &res, }; - - for (i = 0; i < 2; i++) { - io.map = i; - i82092aa_set_io_map(sock, &io); - } - for (i = 0; i < 5; i++) { - mem.map = i; - i82092aa_set_mem_map(sock, &mem); - } - - return 0; -} - -static int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value) -{ - unsigned int sock = container_of(socket, - struct socket_info, socket)->number; - unsigned int status; - - /* Interface Status Register */ - status = indirect_read(sock, I365_STATUS); - - *value = 0; - - if ((status & I365_CS_DETECT) == I365_CS_DETECT) - *value |= SS_DETECT; - - /* IO cards have a different meaning of bits 0,1 */ - /* Also notice the inverse-logic on the bits */ - if (indirect_read(sock, I365_INTCTL) & I365_PC_IOCARD) { - /* IO card */ - if (!(status & I365_CS_STSCHG)) - *value |= SS_STSCHG; - } else { /* non I/O card */ - if (!(status & I365_CS_BVD1)) - *value |= SS_BATDEAD; - if (!(status & I365_CS_BVD2)) - *value |= SS_BATWARN; - } - - if (status & I365_CS_WRPROT) - (*value) |= SS_WRPROT; /* card is write protected */ - - if (status & I365_CS_READY) - (*value) |= SS_READY; /* card is not busy */ - - if (status & I365_CS_POWERON) - (*value) |= SS_POWERON; /* power is applied to the card */ - - return 0; -} - - -static int i82092aa_set_socket(struct pcmcia_socket *socket, - socket_state_t *state) -{ - struct socket_info *sock_info = container_of(socket, struct socket_info, - socket); - unsigned int sock = sock_info->number; - unsigned char reg; - - /* First, set the global controller options */ - - set_bridge_state(sock); - - /* Values for the IGENC register */ - - reg = 0; - - /* The reset bit has "inverse" logic */ - if (!(state->flags & SS_RESET)) - reg = reg | I365_PC_RESET; - if (state->flags & SS_IOCARD) - reg = reg | I365_PC_IOCARD; - - /* IGENC, Interrupt and General Control Register */ - indirect_write(sock, I365_INTCTL, reg); - - /* Power registers */ - - reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */ - - if (state->flags & SS_PWR_AUTO) { - dev_info(&sock_info->dev->dev, "Auto power\n"); - reg |= I365_PWR_AUTO; /* automatic power mngmnt */ - } - if (state->flags & SS_OUTPUT_ENA) { - dev_info(&sock_info->dev->dev, "Power Enabled\n"); - reg |= I365_PWR_OUT; /* enable power */ - } - - switch (state->Vcc) { - case 0: - break; - case 50: - dev_info(&sock_info->dev->dev, - "setting voltage to Vcc to 5V on socket %i\n", - sock); - reg |= I365_VCC_5V; - break; - default: - dev_err(&sock_info->dev->dev, - "%s called with invalid VCC power value: %i", - __func__, state->Vcc); - return -EINVAL; - } - - switch (state->Vpp) { - case 0: - dev_info(&sock_info->dev->dev, - "not setting Vpp on socket %i\n", sock); - break; - case 50: - dev_info(&sock_info->dev->dev, - "setting Vpp to 5.0 for socket %i\n", sock); - reg |= I365_VPP1_5V | I365_VPP2_5V; - break; - case 120: - dev_info(&sock_info->dev->dev, "setting Vpp to 12.0\n"); - reg |= I365_VPP1_12V | I365_VPP2_12V; - break; - default: - dev_err(&sock_info->dev->dev, - "%s called with invalid VPP power value: %i", - __func__, state->Vcc); - return -EINVAL; - } - - if (reg != indirect_read(sock, I365_POWER)) /* only write if changed */ - indirect_write(sock, I365_POWER, reg); - - /* Enable specific interrupt events */ - - reg = 0x00; - if (state->csc_mask & SS_DETECT) - reg |= I365_CSC_DETECT; - if (state->flags & SS_IOCARD) { - if (state->csc_mask & SS_STSCHG) - reg |= I365_CSC_STSCHG; - } else { - if (state->csc_mask & SS_BATDEAD) - reg |= I365_CSC_BVD1; - if (state->csc_mask & SS_BATWARN) - reg |= I365_CSC_BVD2; - if (state->csc_mask & SS_READY) - reg |= I365_CSC_READY; - - } - - /* now write the value and clear the (probably bogus) pending stuff - * by doing a dummy read - */ - - indirect_write(sock, I365_CSCINT, reg); - (void)indirect_read(sock, I365_CSC); - - return 0; -} - -static int i82092aa_set_io_map(struct pcmcia_socket *socket, - struct pccard_io_map *io) -{ - struct socket_info *sock_info = container_of(socket, struct socket_info, - socket); - unsigned int sock = sock_info->number; - unsigned char map, ioctl; - - map = io->map; - - /* Check error conditions */ - if (map > 1) - return -EINVAL; - - if ((io->start > 0xffff) || (io->stop > 0xffff) - || (io->stop < io->start)) - return -EINVAL; - - /* Turn off the window before changing anything */ - if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_IO(map)) - indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_IO(map)); - - /* write the new values */ - indirect_write16(sock, I365_IO(map)+I365_W_START, io->start); - indirect_write16(sock, I365_IO(map)+I365_W_STOP, io->stop); - - ioctl = indirect_read(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map); - - if (io->flags & (MAP_16BIT|MAP_AUTOSZ)) - ioctl |= I365_IOCTL_16BIT(map); - - indirect_write(sock, I365_IOCTL, ioctl); - - /* Turn the window back on if needed */ - if (io->flags & MAP_ACTIVE) - indirect_setbit(sock, I365_ADDRWIN, I365_ENA_IO(map)); - - return 0; -} - -static int i82092aa_set_mem_map(struct pcmcia_socket *socket, - struct pccard_mem_map *mem) -{ - struct socket_info *sock_info = container_of(socket, struct socket_info, - socket); - unsigned int sock = sock_info->number; - struct pci_bus_region region; - unsigned short base, i; - unsigned char map; - - pcibios_resource_to_bus(sock_info->dev->bus, ®ion, mem->res); - - map = mem->map; - if (map > 4) - return -EINVAL; - - if ((mem->card_start > 0x3ffffff) || (region.start > region.end) || - (mem->speed > 1000)) { - dev_err(&sock_info->dev->dev, - "invalid mem map for socket %i: %llx to %llx with a start of %x\n", - sock, - (unsigned long long)region.start, - (unsigned long long)region.end, - mem->card_start); - return -EINVAL; - } - - /* Turn off the window before changing anything */ - if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_MEM(map)) - indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_MEM(map)); - - /* write the start address */ - base = I365_MEM(map); - i = (region.start >> 12) & 0x0fff; - if (mem->flags & MAP_16BIT) - i |= I365_MEM_16BIT; - if (mem->flags & MAP_0WS) - i |= I365_MEM_0WS; - indirect_write16(sock, base+I365_W_START, i); - - /* write the stop address */ - - i = (region.end >> 12) & 0x0fff; - switch (to_cycles(mem->speed)) { - case 0: - break; - case 1: - i |= I365_MEM_WS0; - break; - case 2: - i |= I365_MEM_WS1; - break; - default: - i |= I365_MEM_WS1 | I365_MEM_WS0; - break; - } - - indirect_write16(sock, base+I365_W_STOP, i); - - /* card start */ - - i = ((mem->card_start - region.start) >> 12) & 0x3fff; - if (mem->flags & MAP_WRPROT) - i |= I365_MEM_WRPROT; - if (mem->flags & MAP_ATTRIB) - i |= I365_MEM_REG; - indirect_write16(sock, base+I365_W_OFF, i); - - /* Enable the window if necessary */ - if (mem->flags & MAP_ACTIVE) - indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map)); - - return 0; -} - -static int __init i82092aa_module_init(void) -{ - return pci_register_driver(&i82092aa_pci_driver); -} - -static void __exit i82092aa_module_exit(void) -{ - pci_unregister_driver(&i82092aa_pci_driver); - if (sockets[0].io_base > 0) - release_region(sockets[0].io_base, 2); -} - -module_init(i82092aa_module_init); -module_exit(i82092aa_module_exit); - diff --git a/drivers/pcmcia/i82092aa.h b/drivers/pcmcia/i82092aa.h deleted file mode 100644 index 0f851acab7e5..000000000000 --- a/drivers/pcmcia/i82092aa.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _INCLUDE_GUARD_i82092aa_H_ -#define _INCLUDE_GUARD_i82092aa_H_ - -#include - -/* prototypes */ - -static int i82092aa_pci_probe(struct pci_dev *dev, const struct pci_device_id *id); -static void i82092aa_pci_remove(struct pci_dev *dev); -static int card_present(int socketno); -static irqreturn_t i82092aa_interrupt(int irq, void *dev); - - - - -static int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value); -static int i82092aa_set_socket(struct pcmcia_socket *socket, socket_state_t *state); -static int i82092aa_set_io_map(struct pcmcia_socket *socket, struct pccard_io_map *io); -static int i82092aa_set_mem_map(struct pcmcia_socket *socket, struct pccard_mem_map *mem); -static int i82092aa_init(struct pcmcia_socket *socket); - -#endif - diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c deleted file mode 100644 index 1e464b951ed2..000000000000 --- a/drivers/pcmcia/i82365.c +++ /dev/null @@ -1,1347 +0,0 @@ -/*====================================================================== - - Device driver for Intel 82365 and compatible PC Card controllers. - - i82365.c 1.265 1999/11/10 18:36:21 - - The contents of this file are subject to the Mozilla Public - License Version 1.1 (the "License"); you may not use this file - except in compliance with the License. You may obtain a copy of - the License at http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS - IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - implied. See the License for the specific language governing - rights and limitations under the License. - - The initial developer of the original code is David A. Hinds - . Portions created by David A. Hinds - are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - - Alternatively, the contents of this file may be used under the - terms of the GNU General Public License version 2 (the "GPL"), in which - case the provisions of the GPL are applicable instead of the - above. If you wish to allow the use of your version of this file - only under the terms of the GPL and not to allow others to use - your version of this file under the MPL, indicate your decision - by deleting the provisions above and replace them with the notice - and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this - file under either the MPL or the GPL. - -======================================================================*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -/* ISA-bus controllers */ -#include "i82365.h" -#include "cirrus.h" -#include "vg468.h" -#include "ricoh.h" - - -static irqreturn_t i365_count_irq(int, void *); -static inline int _check_irq(int irq, int flags) -{ - if (request_irq(irq, i365_count_irq, flags, "x", i365_count_irq) != 0) - return -1; - free_irq(irq, i365_count_irq); - return 0; -} - -/*====================================================================*/ - -/* Parameters that can be set with 'insmod' */ - -/* Default base address for i82365sl and other ISA chips */ -static unsigned long i365_base = 0x3e0; -/* Should we probe at 0x3e2 for an extra ISA controller? */ -static int extra_sockets = 0; -/* Specify a socket number to ignore */ -static int ignore = -1; -/* Bit map or list of interrupts to choose from */ -static u_int irq_mask = 0xffff; -static int irq_list[16]; -static unsigned int irq_list_count; -/* The card status change interrupt -- 0 means autoselect */ -static int cs_irq = 0; - -/* Probe for safe interrupts? */ -static int do_scan = 1; -/* Poll status interval -- 0 means default to interrupt */ -static int poll_interval = 0; -/* External clock time, in nanoseconds. 120 ns = 8.33 MHz */ -static int cycle_time = 120; - -/* Cirrus options */ -static int has_dma = -1; -static int has_led = -1; -static int has_ring = -1; -static int dynamic_mode = 0; -static int freq_bypass = -1; -static int setup_time = -1; -static int cmd_time = -1; -static int recov_time = -1; - -/* Vadem options */ -static int async_clock = -1; -static int cable_mode = -1; -static int wakeup = 0; - -module_param_hw(i365_base, ulong, ioport, 0444); -module_param(ignore, int, 0444); -module_param(extra_sockets, int, 0444); -module_param_hw(irq_mask, int, other, 0444); -module_param_hw_array(irq_list, int, irq, &irq_list_count, 0444); -module_param_hw(cs_irq, int, irq, 0444); -module_param(async_clock, int, 0444); -module_param(cable_mode, int, 0444); -module_param(wakeup, int, 0444); - -module_param(do_scan, int, 0444); -module_param(poll_interval, int, 0444); -module_param(cycle_time, int, 0444); -module_param(has_dma, int, 0444); -module_param(has_led, int, 0444); -module_param(has_ring, int, 0444); -module_param(dynamic_mode, int, 0444); -module_param(freq_bypass, int, 0444); -module_param(setup_time, int, 0444); -module_param(cmd_time, int, 0444); -module_param(recov_time, int, 0444); - -/*====================================================================*/ - -struct cirrus_state { - u_char misc1, misc2; - u_char timer[6]; -}; - -struct vg46x_state { - u_char ctl, ema; -}; - -struct i82365_socket { - u_short type, flags; - struct pcmcia_socket socket; - unsigned int number; - unsigned int ioaddr; - u_short psock; - u_char cs_irq, intr; - union { - struct cirrus_state cirrus; - struct vg46x_state vg46x; - } state; -}; - -/* Where we keep track of our sockets... */ -static int sockets = 0; -static struct i82365_socket socket[8] = { - { 0, }, /* ... */ -}; - -/* Default ISA interrupt mask */ -#define I365_MASK 0xdeb8 /* irq 15,14,12,11,10,9,7,5,4,3 */ - -static int grab_irq; -static DEFINE_SPINLOCK(isa_lock); -#define ISA_LOCK(n, f) spin_lock_irqsave(&isa_lock, f) -#define ISA_UNLOCK(n, f) spin_unlock_irqrestore(&isa_lock, f) - -static struct timer_list poll_timer; - -/*====================================================================*/ - -/* These definitions must match the pcic table! */ -enum pcic_id { - IS_I82365A, IS_I82365B, IS_I82365DF, - IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469, - IS_PD6710, IS_PD672X, IS_VT83C469, -}; - -/* Flags for classifying groups of controllers */ -#define IS_VADEM 0x0001 -#define IS_CIRRUS 0x0002 -#define IS_VIA 0x0010 -#define IS_UNKNOWN 0x0400 -#define IS_VG_PWR 0x0800 -#define IS_DF_PWR 0x1000 -#define IS_REGISTERED 0x2000 -#define IS_ALIVE 0x8000 - -struct pcic { - char *name; - u_short flags; -}; - -static struct pcic pcic[] = { - { "Intel i82365sl A step", 0 }, - { "Intel i82365sl B step", 0 }, - { "Intel i82365sl DF", IS_DF_PWR }, - { "IBM Clone", 0 }, - { "Ricoh RF5C296/396", 0 }, - { "VLSI 82C146", 0 }, - { "Vadem VG-468", IS_VADEM }, - { "Vadem VG-469", IS_VADEM|IS_VG_PWR }, - { "Cirrus PD6710", IS_CIRRUS }, - { "Cirrus PD672x", IS_CIRRUS }, - { "VIA VT83C469", IS_CIRRUS|IS_VIA }, -}; - -#define PCIC_COUNT ARRAY_SIZE(pcic) - -/*====================================================================*/ - -static DEFINE_SPINLOCK(bus_lock); - -static u_char i365_get(u_short sock, u_short reg) -{ - unsigned long flags; - spin_lock_irqsave(&bus_lock,flags); - { - unsigned int port = socket[sock].ioaddr; - u_char val; - reg = I365_REG(socket[sock].psock, reg); - outb(reg, port); val = inb(port+1); - spin_unlock_irqrestore(&bus_lock,flags); - return val; - } -} - -static void i365_set(u_short sock, u_short reg, u_char data) -{ - unsigned long flags; - spin_lock_irqsave(&bus_lock,flags); - { - unsigned int port = socket[sock].ioaddr; - u_char val = I365_REG(socket[sock].psock, reg); - outb(val, port); outb(data, port+1); - spin_unlock_irqrestore(&bus_lock,flags); - } -} - -static void i365_bset(u_short sock, u_short reg, u_char mask) -{ - u_char d = i365_get(sock, reg); - d |= mask; - i365_set(sock, reg, d); -} - -static void i365_bclr(u_short sock, u_short reg, u_char mask) -{ - u_char d = i365_get(sock, reg); - d &= ~mask; - i365_set(sock, reg, d); -} - -static void i365_bflip(u_short sock, u_short reg, u_char mask, int b) -{ - u_char d = i365_get(sock, reg); - if (b) - d |= mask; - else - d &= ~mask; - i365_set(sock, reg, d); -} - -static u_short i365_get_pair(u_short sock, u_short reg) -{ - u_short a, b; - a = i365_get(sock, reg); - b = i365_get(sock, reg+1); - return (a + (b<<8)); -} - -static void i365_set_pair(u_short sock, u_short reg, u_short data) -{ - i365_set(sock, reg, data & 0xff); - i365_set(sock, reg+1, data >> 8); -} - -/*====================================================================== - - Code to save and restore global state information for Cirrus - PD67xx controllers, and to set and report global configuration - options. - - The VIA controllers also use these routines, as they are mostly - Cirrus lookalikes, without the timing registers. - -======================================================================*/ - -#define flip(v,b,f) (v = ((f)<0) ? v : ((f) ? ((v)|(b)) : ((v)&(~b)))) - -static void cirrus_get_state(u_short s) -{ - int i; - struct cirrus_state *p = &socket[s].state.cirrus; - p->misc1 = i365_get(s, PD67_MISC_CTL_1); - p->misc1 &= (PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA); - p->misc2 = i365_get(s, PD67_MISC_CTL_2); - for (i = 0; i < 6; i++) - p->timer[i] = i365_get(s, PD67_TIME_SETUP(0)+i); -} - -static void cirrus_set_state(u_short s) -{ - int i; - u_char misc; - struct cirrus_state *p = &socket[s].state.cirrus; - - misc = i365_get(s, PD67_MISC_CTL_2); - i365_set(s, PD67_MISC_CTL_2, p->misc2); - if (misc & PD67_MC2_SUSPEND) mdelay(50); - misc = i365_get(s, PD67_MISC_CTL_1); - misc &= ~(PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA); - i365_set(s, PD67_MISC_CTL_1, misc | p->misc1); - for (i = 0; i < 6; i++) - i365_set(s, PD67_TIME_SETUP(0)+i, p->timer[i]); -} - -static u_int __init cirrus_set_opts(u_short s, char *buf) -{ - struct i82365_socket *t = &socket[s]; - struct cirrus_state *p = &socket[s].state.cirrus; - u_int mask = 0xffff; - - if (has_ring == -1) has_ring = 1; - flip(p->misc2, PD67_MC2_IRQ15_RI, has_ring); - flip(p->misc2, PD67_MC2_DYNAMIC_MODE, dynamic_mode); - flip(p->misc2, PD67_MC2_FREQ_BYPASS, freq_bypass); - if (p->misc2 & PD67_MC2_IRQ15_RI) - strcat(buf, " [ring]"); - if (p->misc2 & PD67_MC2_DYNAMIC_MODE) - strcat(buf, " [dyn mode]"); - if (p->misc2 & PD67_MC2_FREQ_BYPASS) - strcat(buf, " [freq bypass]"); - if (p->misc1 & PD67_MC1_INPACK_ENA) - strcat(buf, " [inpack]"); - if (p->misc2 & PD67_MC2_IRQ15_RI) - mask &= ~0x8000; - if (has_led > 0) { - strcat(buf, " [led]"); - mask &= ~0x1000; - } - if (has_dma > 0) { - strcat(buf, " [dma]"); - mask &= ~0x0600; - } - if (!(t->flags & IS_VIA)) { - if (setup_time >= 0) - p->timer[0] = p->timer[3] = setup_time; - if (cmd_time > 0) { - p->timer[1] = cmd_time; - p->timer[4] = cmd_time*2+4; - } - if (p->timer[1] == 0) { - p->timer[1] = 6; p->timer[4] = 16; - if (p->timer[0] == 0) - p->timer[0] = p->timer[3] = 1; - } - if (recov_time >= 0) - p->timer[2] = p->timer[5] = recov_time; - buf += strlen(buf); - sprintf(buf, " [%d/%d/%d] [%d/%d/%d]", p->timer[0], p->timer[1], - p->timer[2], p->timer[3], p->timer[4], p->timer[5]); - } - return mask; -} - -/*====================================================================== - - Code to save and restore global state information for Vadem VG468 - and VG469 controllers, and to set and report global configuration - options. - -======================================================================*/ - -static void vg46x_get_state(u_short s) -{ - struct vg46x_state *p = &socket[s].state.vg46x; - p->ctl = i365_get(s, VG468_CTL); - if (socket[s].type == IS_VG469) - p->ema = i365_get(s, VG469_EXT_MODE); -} - -static void vg46x_set_state(u_short s) -{ - struct vg46x_state *p = &socket[s].state.vg46x; - i365_set(s, VG468_CTL, p->ctl); - if (socket[s].type == IS_VG469) - i365_set(s, VG469_EXT_MODE, p->ema); -} - -static u_int __init vg46x_set_opts(u_short s, char *buf) -{ - struct vg46x_state *p = &socket[s].state.vg46x; - - flip(p->ctl, VG468_CTL_ASYNC, async_clock); - flip(p->ema, VG469_MODE_CABLE, cable_mode); - if (p->ctl & VG468_CTL_ASYNC) - strcat(buf, " [async]"); - if (p->ctl & VG468_CTL_INPACK) - strcat(buf, " [inpack]"); - if (socket[s].type == IS_VG469) { - u_char vsel = i365_get(s, VG469_VSELECT); - if (vsel & VG469_VSEL_EXT_STAT) { - strcat(buf, " [ext mode]"); - if (vsel & VG469_VSEL_EXT_BUS) - strcat(buf, " [isa buf]"); - } - if (p->ema & VG469_MODE_CABLE) - strcat(buf, " [cable]"); - if (p->ema & VG469_MODE_COMPAT) - strcat(buf, " [c step]"); - } - return 0xffff; -} - -/*====================================================================== - - Generic routines to get and set controller options - -======================================================================*/ - -static void get_bridge_state(u_short s) -{ - struct i82365_socket *t = &socket[s]; - if (t->flags & IS_CIRRUS) - cirrus_get_state(s); - else if (t->flags & IS_VADEM) - vg46x_get_state(s); -} - -static void set_bridge_state(u_short s) -{ - struct i82365_socket *t = &socket[s]; - if (t->flags & IS_CIRRUS) - cirrus_set_state(s); - else { - i365_set(s, I365_GBLCTL, 0x00); - i365_set(s, I365_GENCTL, 0x00); - } - i365_bflip(s, I365_INTCTL, I365_INTR_ENA, t->intr); - if (t->flags & IS_VADEM) - vg46x_set_state(s); -} - -static u_int __init set_bridge_opts(u_short s, u_short ns) -{ - u_short i; - u_int m = 0xffff; - char buf[128]; - - for (i = s; i < s+ns; i++) { - if (socket[i].flags & IS_ALIVE) { - printk(KERN_INFO " host opts [%d]: already alive!\n", i); - continue; - } - buf[0] = '\0'; - get_bridge_state(i); - if (socket[i].flags & IS_CIRRUS) - m = cirrus_set_opts(i, buf); - else if (socket[i].flags & IS_VADEM) - m = vg46x_set_opts(i, buf); - set_bridge_state(i); - printk(KERN_INFO " host opts [%d]:%s\n", i, - (*buf) ? buf : " none"); - } - return m; -} - -/*====================================================================== - - Interrupt testing code, for ISA and PCI interrupts - -======================================================================*/ - -static volatile u_int irq_hits; -static u_short irq_sock; - -static irqreturn_t i365_count_irq(int irq, void *dev) -{ - i365_get(irq_sock, I365_CSC); - irq_hits++; - pr_debug("i82365: -> hit on irq %d\n", irq); - return IRQ_HANDLED; -} - -static u_int __init test_irq(u_short sock, int irq) -{ - pr_debug("i82365: testing ISA irq %d\n", irq); - if (request_irq(irq, i365_count_irq, IRQF_PROBE_SHARED, "scan", - i365_count_irq) != 0) - return 1; - irq_hits = 0; irq_sock = sock; - msleep(10); - if (irq_hits) { - free_irq(irq, i365_count_irq); - pr_debug("i82365: spurious hit!\n"); - return 1; - } - - /* Generate one interrupt */ - i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (irq << 4)); - i365_bset(sock, I365_GENCTL, I365_CTL_SW_IRQ); - udelay(1000); - - free_irq(irq, i365_count_irq); - - /* mask all interrupts */ - i365_set(sock, I365_CSCINT, 0); - pr_debug("i82365: hits = %d\n", irq_hits); - - return (irq_hits != 1); -} - -static u_int __init isa_scan(u_short sock, u_int mask0) -{ - u_int mask1 = 0; - int i; - -#ifdef __alpha__ -#define PIC 0x4d0 - /* Don't probe level-triggered interrupts -- reserved for PCI */ - mask0 &= ~(inb(PIC) | (inb(PIC+1) << 8)); -#endif - - if (do_scan) { - set_bridge_state(sock); - i365_set(sock, I365_CSCINT, 0); - for (i = 0; i < 16; i++) - if ((mask0 & (1 << i)) && (test_irq(sock, i) == 0)) - mask1 |= (1 << i); - for (i = 0; i < 16; i++) - if ((mask1 & (1 << i)) && (test_irq(sock, i) != 0)) - mask1 ^= (1 << i); - } - - printk(KERN_INFO " ISA irqs ("); - if (mask1) { - printk("scanned"); - } else { - /* Fallback: just find interrupts that aren't in use */ - for (i = 0; i < 16; i++) - if ((mask0 & (1 << i)) && (_check_irq(i, IRQF_PROBE_SHARED) == 0)) - mask1 |= (1 << i); - printk("default"); - /* If scan failed, default to polled status */ - if (!cs_irq && (poll_interval == 0)) poll_interval = HZ; - } - printk(") = "); - - for (i = 0; i < 16; i++) - if (mask1 & (1<= 4) ? IS_VG469 : IS_VG468; - } - - /* Check for Ricoh chips */ - val = i365_get(sockets, RF5C_CHIP_ID); - if ((val == RF5C_CHIP_RF5C296) || (val == RF5C_CHIP_RF5C396)) - type = IS_RF5Cx96; - - /* Check for Cirrus CL-PD67xx chips */ - i365_set(sockets, PD67_CHIP_INFO, 0); - val = i365_get(sockets, PD67_CHIP_INFO); - if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) { - val = i365_get(sockets, PD67_CHIP_INFO); - if ((val & PD67_INFO_CHIP_ID) == 0) { - type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710; - i365_set(sockets, PD67_EXT_INDEX, 0xe5); - if (i365_get(sockets, PD67_EXT_INDEX) != 0xe5) - type = IS_VT83C469; - } - } - return type; -} /* identify */ - -/*====================================================================== - - See if a card is present, powered up, in IO mode, and already - bound to a (non PC Card) Linux driver. We leave these alone. - - We make an exception for cards that seem to be serial devices. - -======================================================================*/ - -static int __init is_alive(u_short sock) -{ - u_char stat; - unsigned int start, stop; - - stat = i365_get(sock, I365_STATUS); - start = i365_get_pair(sock, I365_IO(0)+I365_W_START); - stop = i365_get_pair(sock, I365_IO(0)+I365_W_STOP); - if ((stat & I365_CS_DETECT) && (stat & I365_CS_POWERON) && - (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) && - (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(0)) && - ((start & 0xfeef) != 0x02e8)) { - if (!request_region(start, stop-start+1, "i82365")) - return 1; - release_region(start, stop-start+1); - } - - return 0; -} - -/*====================================================================*/ - -static void __init add_socket(unsigned int port, int psock, int type) -{ - socket[sockets].ioaddr = port; - socket[sockets].psock = psock; - socket[sockets].type = type; - socket[sockets].flags = pcic[type].flags; - if (is_alive(sockets)) - socket[sockets].flags |= IS_ALIVE; - sockets++; -} - -static void __init add_pcic(int ns, int type) -{ - u_int mask = 0, i, base; - int isa_irq = 0; - struct i82365_socket *t = &socket[sockets-ns]; - - base = sockets-ns; - if (base == 0) printk("\n"); - printk(KERN_INFO " %s", pcic[type].name); - printk(" ISA-to-PCMCIA at port %#x ofs 0x%02x", - t->ioaddr, t->psock*0x40); - printk(", %d socket%s\n", ns, ((ns > 1) ? "s" : "")); - - /* Set host options, build basic interrupt mask */ - if (irq_list_count == 0) - mask = irq_mask; - else - for (i = mask = 0; i < irq_list_count; i++) - mask |= (1< 0; cs_irq--) - if ((cs_mask & (1 << cs_irq)) && - (_check_irq(cs_irq, IRQF_PROBE_SHARED) == 0)) - break; - if (cs_irq) { - grab_irq = 1; - isa_irq = cs_irq; - printk(" status change on irq %d\n", cs_irq); - } - } - - if (!isa_irq) { - if (poll_interval == 0) - poll_interval = HZ; - printk(" polling interval = %d ms\n", - poll_interval * 1000 / HZ); - - } - - /* Update socket interrupt information, capabilities */ - for (i = 0; i < ns; i++) { - t[i].socket.features |= SS_CAP_PCCARD; - t[i].socket.map_size = 0x1000; - t[i].socket.irq_mask = mask; - t[i].cs_irq = isa_irq; - } - -} /* add_pcic */ - -/*====================================================================*/ - -#ifdef CONFIG_PNP -static struct isapnp_device_id id_table[] __initdata = { - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'), - ISAPNP_FUNCTION(0x0e00), (unsigned long) "Intel 82365-Compatible" }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'), - ISAPNP_FUNCTION(0x0e01), (unsigned long) "Cirrus Logic CL-PD6720" }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'), - ISAPNP_FUNCTION(0x0e02), (unsigned long) "VLSI VL82C146" }, - { 0 } -}; -MODULE_DEVICE_TABLE(isapnp, id_table); - -static struct pnp_dev *i82365_pnpdev; -#endif - -static void __init isa_probe(void) -{ - int i, j, sock, k, ns, id; - unsigned int port; -#ifdef CONFIG_PNP - struct isapnp_device_id *devid; - struct pnp_dev *dev; - - for (devid = id_table; devid->vendor; devid++) { - if ((dev = pnp_find_dev(NULL, devid->vendor, devid->function, NULL))) { - - if (pnp_device_attach(dev) < 0) - continue; - - if (pnp_activate_dev(dev) < 0) { - printk("activate failed\n"); - pnp_device_detach(dev); - break; - } - - if (!pnp_port_valid(dev, 0)) { - printk("invalid resources ?\n"); - pnp_device_detach(dev); - break; - } - i365_base = pnp_port_start(dev, 0); - i82365_pnpdev = dev; - break; - } - } -#endif - - if (!request_region(i365_base, 2, "i82365")) { - if (sockets == 0) - printk("port conflict at %#lx\n", i365_base); - return; - } - - id = identify(i365_base, 0); - if ((id == IS_I82365DF) && (identify(i365_base, 1) != id)) { - for (i = 0; i < 4; i++) { - if (i == ignore) continue; - port = i365_base + ((i & 1) << 2) + ((i & 2) << 1); - sock = (i & 1) << 1; - if (identify(port, sock) == IS_I82365DF) { - add_socket(port, sock, IS_VLSI); - add_pcic(1, IS_VLSI); - } - } - } else { - for (i = 0; i < 8; i += 2) { - if (sockets && !extra_sockets && (i == 4)) - break; - port = i365_base + 2*(i>>2); - sock = (i & 3); - id = identify(port, sock); - if (id < 0) continue; - - for (j = ns = 0; j < 2; j++) { - /* Does the socket exist? */ - if ((ignore == i+j) || (identify(port, sock+j) < 0)) - continue; - /* Check for bad socket decode */ - for (k = 0; k <= sockets; k++) - i365_set(k, I365_MEM(0)+I365_W_OFF, k); - for (k = 0; k <= sockets; k++) - if (i365_get(k, I365_MEM(0)+I365_W_OFF) != k) - break; - if (k <= sockets) break; - add_socket(port, sock+j, id); ns++; - } - if (ns != 0) add_pcic(ns, id); - } - } -} - -/*====================================================================*/ - -static irqreturn_t pcic_interrupt(int irq, void *dev) -{ - int i, j, csc; - u_int events, active; - u_long flags = 0; - int handled = 0; - - pr_debug("pcic_interrupt(%d)\n", irq); - - for (j = 0; j < 20; j++) { - active = 0; - for (i = 0; i < sockets; i++) { - if (socket[i].cs_irq != irq) - continue; - handled = 1; - ISA_LOCK(i, flags); - csc = i365_get(i, I365_CSC); - if ((csc == 0) || (i365_get(i, I365_IDENT) & 0x70)) { - ISA_UNLOCK(i, flags); - continue; - } - events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0; - - if (i365_get(i, I365_INTCTL) & I365_PC_IOCARD) - events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0; - else { - events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0; - events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0; - events |= (csc & I365_CSC_READY) ? SS_READY : 0; - } - ISA_UNLOCK(i, flags); - pr_debug("socket %d event 0x%02x\n", i, events); - - if (events) - pcmcia_parse_events(&socket[i].socket, events); - - active |= events; - } - if (!active) break; - } - if (j == 20) - printk(KERN_NOTICE "i82365: infinite loop in interrupt handler\n"); - - pr_debug("pcic_interrupt done\n"); - return IRQ_RETVAL(handled); -} /* pcic_interrupt */ - -static void pcic_interrupt_wrapper(struct timer_list *unused) -{ - pcic_interrupt(0, NULL); - poll_timer.expires = jiffies + poll_interval; - add_timer(&poll_timer); -} - -/*====================================================================*/ - -static int i365_get_status(u_short sock, u_int *value) -{ - u_int status; - - status = i365_get(sock, I365_STATUS); - *value = ((status & I365_CS_DETECT) == I365_CS_DETECT) - ? SS_DETECT : 0; - - if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) - *value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG; - else { - *value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD; - *value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN; - } - *value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0; - *value |= (status & I365_CS_READY) ? SS_READY : 0; - *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0; - - if (socket[sock].type == IS_VG469) { - status = i365_get(sock, VG469_VSENSE); - if (socket[sock].psock & 1) { - *value |= (status & VG469_VSENSE_B_VS1) ? 0 : SS_3VCARD; - *value |= (status & VG469_VSENSE_B_VS2) ? 0 : SS_XVCARD; - } else { - *value |= (status & VG469_VSENSE_A_VS1) ? 0 : SS_3VCARD; - *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD; - } - } - - pr_debug("GetStatus(%d) = %#4.4x\n", sock, *value); - return 0; -} /* i365_get_status */ - -/*====================================================================*/ - -static int i365_set_socket(u_short sock, socket_state_t *state) -{ - struct i82365_socket *t = &socket[sock]; - u_char reg; - - pr_debug("SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " - "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags, - state->Vcc, state->Vpp, state->io_irq, state->csc_mask); - - /* First set global controller options */ - set_bridge_state(sock); - - /* IO card, RESET flag, IO interrupt */ - reg = t->intr; - reg |= state->io_irq; - reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET; - reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0; - i365_set(sock, I365_INTCTL, reg); - - reg = I365_PWR_NORESET; - if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO; - if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT; - - if (t->flags & IS_CIRRUS) { - if (state->Vpp != 0) { - if (state->Vpp == 120) - reg |= I365_VPP1_12V; - else if (state->Vpp == state->Vcc) - reg |= I365_VPP1_5V; - else return -EINVAL; - } - if (state->Vcc != 0) { - reg |= I365_VCC_5V; - if (state->Vcc == 33) - i365_bset(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); - else if (state->Vcc == 50) - i365_bclr(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); - else return -EINVAL; - } - } else if (t->flags & IS_VG_PWR) { - if (state->Vpp != 0) { - if (state->Vpp == 120) - reg |= I365_VPP1_12V; - else if (state->Vpp == state->Vcc) - reg |= I365_VPP1_5V; - else return -EINVAL; - } - if (state->Vcc != 0) { - reg |= I365_VCC_5V; - if (state->Vcc == 33) - i365_bset(sock, VG469_VSELECT, VG469_VSEL_VCC); - else if (state->Vcc == 50) - i365_bclr(sock, VG469_VSELECT, VG469_VSEL_VCC); - else return -EINVAL; - } - } else if (t->flags & IS_DF_PWR) { - switch (state->Vcc) { - case 0: break; - case 33: reg |= I365_VCC_3V; break; - case 50: reg |= I365_VCC_5V; break; - default: return -EINVAL; - } - switch (state->Vpp) { - case 0: break; - case 50: reg |= I365_VPP1_5V; break; - case 120: reg |= I365_VPP1_12V; break; - default: return -EINVAL; - } - } else { - switch (state->Vcc) { - case 0: break; - case 50: reg |= I365_VCC_5V; break; - default: return -EINVAL; - } - switch (state->Vpp) { - case 0: break; - case 50: reg |= I365_VPP1_5V | I365_VPP2_5V; break; - case 120: reg |= I365_VPP1_12V | I365_VPP2_12V; break; - default: return -EINVAL; - } - } - - if (reg != i365_get(sock, I365_POWER)) - i365_set(sock, I365_POWER, reg); - - /* Chipset-specific functions */ - if (t->flags & IS_CIRRUS) { - /* Speaker control */ - i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA, - state->flags & SS_SPKR_ENA); - } - - /* Card status change interrupt mask */ - reg = t->cs_irq << 4; - if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT; - if (state->flags & SS_IOCARD) { - if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG; - } else { - if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1; - if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2; - if (state->csc_mask & SS_READY) reg |= I365_CSC_READY; - } - i365_set(sock, I365_CSCINT, reg); - i365_get(sock, I365_CSC); - - return 0; -} /* i365_set_socket */ - -/*====================================================================*/ - -static int i365_set_io_map(u_short sock, struct pccard_io_map *io) -{ - u_char map, ioctl; - - pr_debug("SetIOMap(%d, %d, %#2.2x, %d ns, " - "%#llx-%#llx)\n", sock, io->map, io->flags, io->speed, - (unsigned long long)io->start, (unsigned long long)io->stop); - map = io->map; - if ((map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) || - (io->stop < io->start)) return -EINVAL; - /* Turn off the window before changing anything */ - if (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(map)) - i365_bclr(sock, I365_ADDRWIN, I365_ENA_IO(map)); - i365_set_pair(sock, I365_IO(map)+I365_W_START, io->start); - i365_set_pair(sock, I365_IO(map)+I365_W_STOP, io->stop); - ioctl = i365_get(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map); - if (io->speed) ioctl |= I365_IOCTL_WAIT(map); - if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map); - if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map); - if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map); - i365_set(sock, I365_IOCTL, ioctl); - /* Turn on the window if necessary */ - if (io->flags & MAP_ACTIVE) - i365_bset(sock, I365_ADDRWIN, I365_ENA_IO(map)); - return 0; -} /* i365_set_io_map */ - -/*====================================================================*/ - -static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem) -{ - u_short base, i; - u_char map; - - pr_debug("SetMemMap(%d, %d, %#2.2x, %d ns, %#llx-%#llx, " - "%#x)\n", sock, mem->map, mem->flags, mem->speed, - (unsigned long long)mem->res->start, - (unsigned long long)mem->res->end, mem->card_start); - - map = mem->map; - if ((map > 4) || (mem->card_start > 0x3ffffff) || - (mem->res->start > mem->res->end) || (mem->speed > 1000)) - return -EINVAL; - if ((mem->res->start > 0xffffff) || (mem->res->end > 0xffffff)) - return -EINVAL; - - /* Turn off the window before changing anything */ - if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map)) - i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map)); - - base = I365_MEM(map); - i = (mem->res->start >> 12) & 0x0fff; - if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT; - if (mem->flags & MAP_0WS) i |= I365_MEM_0WS; - i365_set_pair(sock, base+I365_W_START, i); - - i = (mem->res->end >> 12) & 0x0fff; - switch (to_cycles(mem->speed)) { - case 0: break; - case 1: i |= I365_MEM_WS0; break; - case 2: i |= I365_MEM_WS1; break; - default: i |= I365_MEM_WS1 | I365_MEM_WS0; break; - } - i365_set_pair(sock, base+I365_W_STOP, i); - - i = ((mem->card_start - mem->res->start) >> 12) & 0x3fff; - if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT; - if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG; - i365_set_pair(sock, base+I365_W_OFF, i); - - /* Turn on the window if necessary */ - if (mem->flags & MAP_ACTIVE) - i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map)); - return 0; -} /* i365_set_mem_map */ - -#if 0 /* driver model ordering issue */ -/*====================================================================== - - Routines for accessing socket information and register dumps via - /sys/class/pcmcia_socket/... - -======================================================================*/ - -static ssize_t show_info(struct class_device *class_dev, char *buf) -{ - struct i82365_socket *s = container_of(class_dev, struct i82365_socket, socket.dev); - return sprintf(buf, "type: %s\npsock: %d\n", - pcic[s->type].name, s->psock); -} - -static ssize_t show_exca(struct class_device *class_dev, char *buf) -{ - struct i82365_socket *s = container_of(class_dev, struct i82365_socket, socket.dev); - unsigned short sock; - int i; - ssize_t ret = 0; - unsigned long flags = 0; - - sock = s->number; - - ISA_LOCK(sock, flags); - for (i = 0; i < 0x40; i += 4) { - ret += sprintf(buf, "%02x %02x %02x %02x%s", - i365_get(sock,i), i365_get(sock,i+1), - i365_get(sock,i+2), i365_get(sock,i+3), - ((i % 16) == 12) ? "\n" : " "); - buf += ret; - } - ISA_UNLOCK(sock, flags); - - return ret; -} - -static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL); -static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL); -#endif - -/*====================================================================*/ - -/* this is horribly ugly... proper locking needs to be done here at - * some time... */ -#define LOCKED(x) do { \ - int retval; \ - unsigned long flags; \ - spin_lock_irqsave(&isa_lock, flags); \ - retval = x; \ - spin_unlock_irqrestore(&isa_lock, flags); \ - return retval; \ -} while (0) - - -static int pcic_get_status(struct pcmcia_socket *s, u_int *value) -{ - unsigned int sock = container_of(s, struct i82365_socket, socket)->number; - - if (socket[sock].flags & IS_ALIVE) { - *value = 0; - return -EINVAL; - } - - LOCKED(i365_get_status(sock, value)); -} - -static int pcic_set_socket(struct pcmcia_socket *s, socket_state_t *state) -{ - unsigned int sock = container_of(s, struct i82365_socket, socket)->number; - - if (socket[sock].flags & IS_ALIVE) - return -EINVAL; - - LOCKED(i365_set_socket(sock, state)); -} - -static int pcic_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) -{ - unsigned int sock = container_of(s, struct i82365_socket, socket)->number; - if (socket[sock].flags & IS_ALIVE) - return -EINVAL; - - LOCKED(i365_set_io_map(sock, io)); -} - -static int pcic_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem) -{ - unsigned int sock = container_of(s, struct i82365_socket, socket)->number; - if (socket[sock].flags & IS_ALIVE) - return -EINVAL; - - LOCKED(i365_set_mem_map(sock, mem)); -} - -static int pcic_init(struct pcmcia_socket *s) -{ - int i; - struct resource res = { .start = 0, .end = 0x1000 }; - pccard_io_map io = { 0, 0, 0, 0, 1 }; - pccard_mem_map mem = { .res = &res, }; - - for (i = 0; i < 2; i++) { - io.map = i; - pcic_set_io_map(s, &io); - } - for (i = 0; i < 5; i++) { - mem.map = i; - pcic_set_mem_map(s, &mem); - } - return 0; -} - - -static struct pccard_operations pcic_operations = { - .init = pcic_init, - .get_status = pcic_get_status, - .set_socket = pcic_set_socket, - .set_io_map = pcic_set_io_map, - .set_mem_map = pcic_set_mem_map, -}; - -/*====================================================================*/ - -static struct platform_driver i82365_driver = { - .driver = { - .name = "i82365", - }, -}; - -static struct platform_device *i82365_device; - -static int __init init_i82365(void) -{ - int i, ret; - - ret = platform_driver_register(&i82365_driver); - if (ret) - goto err_out; - - i82365_device = platform_device_alloc("i82365", 0); - if (i82365_device) { - ret = platform_device_add(i82365_device); - if (ret) - platform_device_put(i82365_device); - } else - ret = -ENOMEM; - - if (ret) - goto err_driver_unregister; - - printk(KERN_INFO "Intel ISA PCIC probe: "); - sockets = 0; - - isa_probe(); - - if (sockets == 0) { - printk("not found.\n"); - ret = -ENODEV; - goto err_dev_unregister; - } - - /* Set up interrupt handler(s) */ - if (grab_irq != 0) - ret = request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt); - - if (ret) - goto err_socket_release; - - /* register sockets with the pcmcia core */ - for (i = 0; i < sockets; i++) { - socket[i].socket.dev.parent = &i82365_device->dev; - socket[i].socket.ops = &pcic_operations; - socket[i].socket.resource_ops = &pccard_nonstatic_ops; - socket[i].socket.owner = THIS_MODULE; - socket[i].number = i; - ret = pcmcia_register_socket(&socket[i].socket); - if (!ret) - socket[i].flags |= IS_REGISTERED; - } - - /* Finally, schedule a polling interrupt */ - if (poll_interval != 0) { - timer_setup(&poll_timer, pcic_interrupt_wrapper, 0); - poll_timer.expires = jiffies + poll_interval; - add_timer(&poll_timer); - } - - return 0; -err_socket_release: - for (i = 0; i < sockets; i++) { - /* Turn off all interrupt sources! */ - i365_set(i, I365_CSCINT, 0); - release_region(socket[i].ioaddr, 2); - } -err_dev_unregister: - platform_device_unregister(i82365_device); - release_region(i365_base, 2); -#ifdef CONFIG_PNP - if (i82365_pnpdev) - pnp_disable_dev(i82365_pnpdev); -#endif -err_driver_unregister: - platform_driver_unregister(&i82365_driver); -err_out: - return ret; -} /* init_i82365 */ - -static void __exit exit_i82365(void) -{ - int i; - - for (i = 0; i < sockets; i++) { - if (socket[i].flags & IS_REGISTERED) - pcmcia_unregister_socket(&socket[i].socket); - } - platform_device_unregister(i82365_device); - if (poll_interval != 0) - timer_delete_sync(&poll_timer); - if (grab_irq != 0) - free_irq(cs_irq, pcic_interrupt); - for (i = 0; i < sockets; i++) { - /* Turn off all interrupt sources! */ - i365_set(i, I365_CSCINT, 0); - release_region(socket[i].ioaddr, 2); - } - release_region(i365_base, 2); -#ifdef CONFIG_PNP - if (i82365_pnpdev) - pnp_disable_dev(i82365_pnpdev); -#endif - platform_driver_unregister(&i82365_driver); -} /* exit_i82365 */ - -module_init(init_i82365); -module_exit(exit_i82365); -MODULE_DESCRIPTION("Driver for Intel 82365 and compatible PC Card controllers"); -MODULE_LICENSE("Dual MPL/GPL"); -/*====================================================================*/ diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 949e69921fe9..95190f814a51 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -187,7 +187,7 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base, int any; u_char *b, hole, most; - dev_info(&s->dev, "cs: IO port probe %#x-%#x:", base, base+num-1); + pr_info("%s: cs: IO port probe %#x-%#x:", dev_name(&s->dev), base, base+num-1); /* First, what does a floating port look like? */ b = kzalloc(256, GFP_KERNEL); @@ -409,8 +409,8 @@ static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num, struct socket_data *s_data = s->resource_data; u_long i, j, bad, fail, step; - dev_info(&s->dev, "cs: memory probe 0x%06lx-0x%06lx:", - base, base+num-1); + pr_info("%s: cs: memory probe 0x%06lx-0x%06lx:", + dev_name(&s->dev), base, base+num-1); bad = fail = 0; step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff); /* don't allow too large steps */ @@ -936,7 +936,7 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long static int nonstatic_autoadd_resources(struct pcmcia_socket *s) { struct resource *res; - int i, done = 0; + int done = 0; if (!s->cb_dev || !s->cb_dev->bus) return -ENODEV; @@ -963,10 +963,10 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s) if (s->cb_dev->bus->number == 0) return -EINVAL; - for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { + for (unsigned int i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { res = s->cb_dev->bus->resource[i]; #else - pci_bus_for_each_resource(s->cb_dev->bus, res, i) { + pci_bus_for_each_resource(s->cb_dev->bus, res) { #endif if (!res) continue; diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c deleted file mode 100644 index 060aed0edc65..000000000000 --- a/drivers/pcmcia/tcic.c +++ /dev/null @@ -1,805 +0,0 @@ -/*====================================================================== - - Device driver for Databook TCIC-2 PCMCIA controller - - tcic.c 1.111 2000/02/15 04:13:12 - - The contents of this file are subject to the Mozilla Public - License Version 1.1 (the "License"); you may not use this file - except in compliance with the License. You may obtain a copy of - the License at http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS - IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - implied. See the License for the specific language governing - rights and limitations under the License. - - The initial developer of the original code is David A. Hinds - . Portions created by David A. Hinds - are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - - Alternatively, the contents of this file may be used under the - terms of the GNU General Public License version 2 (the "GPL"), in which - case the provisions of the GPL are applicable instead of the - above. If you wish to allow the use of your version of this file - only under the terms of the GPL and not to allow others to use - your version of this file under the MPL, indicate your decision - by deleting the provisions above and replace them with the notice - and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this - file under either the MPL or the GPL. - -======================================================================*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include "tcic.h" - -MODULE_AUTHOR("David Hinds "); -MODULE_DESCRIPTION("Databook TCIC-2 PCMCIA socket driver"); -MODULE_LICENSE("Dual MPL/GPL"); - -/*====================================================================*/ - -/* Parameters that can be set with 'insmod' */ - -/* The base port address of the TCIC-2 chip */ -static unsigned long tcic_base = TCIC_BASE; - -/* Specify a socket number to ignore */ -static int ignore = -1; - -/* Probe for safe interrupts? */ -static int do_scan = 1; - -/* Bit map of interrupts to choose from */ -static u_int irq_mask = 0xffff; -static int irq_list[16]; -static unsigned int irq_list_count; - -/* The card status change interrupt -- 0 means autoselect */ -static int cs_irq; - -/* Poll status interval -- 0 means default to interrupt */ -static int poll_interval; - -/* Delay for card status double-checking */ -static int poll_quick = HZ/20; - -/* CCLK external clock time, in nanoseconds. 70 ns = 14.31818 MHz */ -static int cycle_time = 70; - -module_param_hw(tcic_base, ulong, ioport, 0444); -module_param(ignore, int, 0444); -module_param(do_scan, int, 0444); -module_param_hw(irq_mask, int, other, 0444); -module_param_hw_array(irq_list, int, irq, &irq_list_count, 0444); -module_param_hw(cs_irq, int, irq, 0444); -module_param(poll_interval, int, 0444); -module_param(poll_quick, int, 0444); -module_param(cycle_time, int, 0444); - -/*====================================================================*/ - -static irqreturn_t tcic_interrupt(int irq, void *dev); -static void tcic_timer(struct timer_list *unused); -static struct pccard_operations tcic_operations; - -struct tcic_socket { - u_short psock; - u_char last_sstat; - u_char id; - struct pcmcia_socket socket; -}; - -static struct timer_list poll_timer; -static int tcic_timer_pending; - -static int sockets; -static struct tcic_socket socket_table[2]; - -/*====================================================================*/ - -/* Trick when selecting interrupts: the TCIC sktirq pin is supposed - to map to irq 11, but is coded as 0 or 1 in the irq registers. */ -#define TCIC_IRQ(x) ((x) ? (((x) == 11) ? 1 : (x)) : 15) - -#ifdef DEBUG_X -static u_char tcic_getb(u_char reg) -{ - u_char val = inb(tcic_base+reg); - printk(KERN_DEBUG "tcic_getb(%#lx) = %#x\n", tcic_base+reg, val); - return val; -} - -static u_short tcic_getw(u_char reg) -{ - u_short val = inw(tcic_base+reg); - printk(KERN_DEBUG "tcic_getw(%#lx) = %#x\n", tcic_base+reg, val); - return val; -} - -static void tcic_setb(u_char reg, u_char data) -{ - printk(KERN_DEBUG "tcic_setb(%#lx, %#x)\n", tcic_base+reg, data); - outb(data, tcic_base+reg); -} - -static void tcic_setw(u_char reg, u_short data) -{ - printk(KERN_DEBUG "tcic_setw(%#lx, %#x)\n", tcic_base+reg, data); - outw(data, tcic_base+reg); -} -#else -#define tcic_getb(reg) inb(tcic_base+reg) -#define tcic_getw(reg) inw(tcic_base+reg) -#define tcic_setb(reg, data) outb(data, tcic_base+reg) -#define tcic_setw(reg, data) outw(data, tcic_base+reg) -#endif - -static void tcic_setl(u_char reg, u_int data) -{ -#ifdef DEBUG_X - printk(KERN_DEBUG "tcic_setl(%#x, %#lx)\n", tcic_base+reg, data); -#endif - outw(data & 0xffff, tcic_base+reg); - outw(data >> 16, tcic_base+reg+2); -} - -static void tcic_aux_setb(u_short reg, u_char data) -{ - u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; - tcic_setb(TCIC_MODE, mode); - tcic_setb(TCIC_AUX, data); -} - -static u_short tcic_aux_getw(u_short reg) -{ - u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; - tcic_setb(TCIC_MODE, mode); - return tcic_getw(TCIC_AUX); -} - -static void tcic_aux_setw(u_short reg, u_short data) -{ - u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; - tcic_setb(TCIC_MODE, mode); - tcic_setw(TCIC_AUX, data); -} - -/*====================================================================*/ - -/* Time conversion functions */ - -static int to_cycles(int ns) -{ - if (ns < 14) - return 0; - else - return 2*(ns-14)/cycle_time; -} - -/*====================================================================*/ - -static volatile u_int irq_hits; - -static irqreturn_t __init tcic_irq_count(int irq, void *dev) -{ - irq_hits++; - return IRQ_HANDLED; -} - -static u_int __init try_irq(int irq) -{ - u_short cfg; - - irq_hits = 0; - if (request_irq(irq, tcic_irq_count, 0, "irq scan", tcic_irq_count) != 0) - return -1; - mdelay(10); - if (irq_hits) { - free_irq(irq, tcic_irq_count); - return -1; - } - - /* Generate one interrupt */ - cfg = TCIC_SYSCFG_AUTOBUSY | 0x0a00; - tcic_aux_setw(TCIC_AUX_SYSCFG, cfg | TCIC_IRQ(irq)); - tcic_setb(TCIC_IENA, TCIC_IENA_ERR | TCIC_IENA_CFG_HIGH); - tcic_setb(TCIC_ICSR, TCIC_ICSR_ERR | TCIC_ICSR_JAM); - - udelay(1000); - free_irq(irq, tcic_irq_count); - - /* Turn off interrupts */ - tcic_setb(TCIC_IENA, TCIC_IENA_CFG_OFF); - while (tcic_getb(TCIC_ICSR)) - tcic_setb(TCIC_ICSR, TCIC_ICSR_JAM); - tcic_aux_setw(TCIC_AUX_SYSCFG, cfg); - - return (irq_hits != 1); -} - -static u_int __init irq_scan(u_int mask0) -{ - u_int mask1; - int i; - -#ifdef __alpha__ -#define PIC 0x4d0 - /* Don't probe level-triggered interrupts -- reserved for PCI */ - int level_mask = inb_p(PIC) | (inb_p(PIC+1) << 8); - if (level_mask) - mask0 &= ~level_mask; -#endif - - mask1 = 0; - if (do_scan) { - for (i = 0; i < 16; i++) - if ((mask0 & (1 << i)) && (try_irq(i) == 0)) - mask1 |= (1 << i); - for (i = 0; i < 16; i++) - if ((mask1 & (1 << i)) && (try_irq(i) != 0)) { - mask1 ^= (1 << i); - } - } - - if (mask1) { - printk("scanned"); - } else { - /* Fallback: just find interrupts that aren't in use */ - for (i = 0; i < 16; i++) - if ((mask0 & (1 << i)) && - (request_irq(i, tcic_irq_count, 0, "x", tcic_irq_count) == 0)) { - mask1 |= (1 << i); - free_irq(i, tcic_irq_count); - } - printk("default"); - } - - printk(") = "); - for (i = 0; i < 16; i++) - if (mask1 & (1<> TCIC_ILOCKTEST_ID_SH; - tcic_aux_setw(TCIC_AUX_TEST, 0); - return id; -} - -/*====================================================================*/ - -static struct platform_driver tcic_driver = { - .driver = { - .name = "tcic-pcmcia", - }, -}; - -static struct platform_device tcic_device = { - .name = "tcic-pcmcia", - .id = 0, -}; - - -static int __init init_tcic(void) -{ - int i, sock, ret = 0; - u_int mask, scan; - - if (platform_driver_register(&tcic_driver)) - return -1; - - printk(KERN_INFO "Databook TCIC-2 PCMCIA probe: "); - sock = 0; - - if (!request_region(tcic_base, 16, "tcic-2")) { - printk("could not allocate ports,\n "); - platform_driver_unregister(&tcic_driver); - return -ENODEV; - } - else { - tcic_setw(TCIC_ADDR, 0); - if (tcic_getw(TCIC_ADDR) == 0) { - tcic_setw(TCIC_ADDR, 0xc3a5); - if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2; - } - if (sock == 0) { - /* See if resetting the controller does any good */ - tcic_setb(TCIC_SCTRL, TCIC_SCTRL_RESET); - tcic_setb(TCIC_SCTRL, 0); - tcic_setw(TCIC_ADDR, 0); - if (tcic_getw(TCIC_ADDR) == 0) { - tcic_setw(TCIC_ADDR, 0xc3a5); - if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2; - } - } - } - if (sock == 0) { - printk("not found.\n"); - release_region(tcic_base, 16); - platform_driver_unregister(&tcic_driver); - return -ENODEV; - } - - sockets = 0; - for (i = 0; i < sock; i++) { - if ((i == ignore) || is_active(i)) continue; - socket_table[sockets].psock = i; - socket_table[sockets].id = get_tcic_id(); - - socket_table[sockets].socket.owner = THIS_MODULE; - /* only 16-bit cards, memory windows must be size-aligned */ - /* No PCI or CardBus support */ - socket_table[sockets].socket.features = SS_CAP_PCCARD | SS_CAP_MEM_ALIGN; - /* irq 14, 11, 10, 7, 6, 5, 4, 3 */ - socket_table[sockets].socket.irq_mask = 0x4cf8; - /* 4K minimum window size */ - socket_table[sockets].socket.map_size = 0x1000; - sockets++; - } - - switch (socket_table[0].id) { - case TCIC_ID_DB86082: - printk("DB86082"); break; - case TCIC_ID_DB86082A: - printk("DB86082A"); break; - case TCIC_ID_DB86084: - printk("DB86084"); break; - case TCIC_ID_DB86084A: - printk("DB86084A"); break; - case TCIC_ID_DB86072: - printk("DB86072"); break; - case TCIC_ID_DB86184: - printk("DB86184"); break; - case TCIC_ID_DB86082B: - printk("DB86082B"); break; - default: - printk("Unknown ID 0x%02x", socket_table[0].id); - } - - /* Set up polling */ - timer_setup(&poll_timer, tcic_timer, 0); - - /* Build interrupt mask */ - printk(KERN_CONT ", %d sockets\n", sockets); - printk(KERN_INFO " irq list ("); - if (irq_list_count == 0) - mask = irq_mask; - else - for (i = mask = 0; i < irq_list_count; i++) - mask |= (1< 0; i--) - if ((cs_mask & (1 << i)) && - (request_irq(i, tcic_interrupt, 0, "tcic", - tcic_interrupt) == 0)) - break; - cs_irq = i; - if (cs_irq == 0) poll_interval = HZ; - } - - if (socket_table[0].socket.irq_mask & (1 << 11)) - printk("sktirq is irq 11, "); - if (cs_irq != 0) - printk("status change on irq %d\n", cs_irq); - else - printk("polled status, interval = %d ms\n", - poll_interval * 1000 / HZ); - - for (i = 0; i < sockets; i++) { - tcic_setw(TCIC_ADDR+2, socket_table[i].psock << TCIC_SS_SHFT); - socket_table[i].last_sstat = tcic_getb(TCIC_SSTAT); - } - - /* jump start interrupt handler, if needed */ - tcic_interrupt(0, NULL); - - platform_device_register(&tcic_device); - - for (i = 0; i < sockets; i++) { - socket_table[i].socket.ops = &tcic_operations; - socket_table[i].socket.resource_ops = &pccard_nonstatic_ops; - socket_table[i].socket.dev.parent = &tcic_device.dev; - ret = pcmcia_register_socket(&socket_table[i].socket); - if (ret && i) - pcmcia_unregister_socket(&socket_table[0].socket); - } - - return ret; - - return 0; - -} /* init_tcic */ - -/*====================================================================*/ - -static void __exit exit_tcic(void) -{ - int i; - - timer_delete_sync(&poll_timer); - if (cs_irq != 0) { - tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00); - free_irq(cs_irq, tcic_interrupt); - } - release_region(tcic_base, 16); - - for (i = 0; i < sockets; i++) { - pcmcia_unregister_socket(&socket_table[i].socket); - } - - platform_device_unregister(&tcic_device); - platform_driver_unregister(&tcic_driver); -} /* exit_tcic */ - -/*====================================================================*/ - -static irqreturn_t tcic_interrupt(int irq, void *dev) -{ - int i, quick = 0; - u_char latch, sstat; - u_short psock; - u_int events; - static volatile int active = 0; - - if (active) { - printk(KERN_NOTICE "tcic: reentered interrupt handler!\n"); - return IRQ_NONE; - } else - active = 1; - - pr_debug("tcic_interrupt()\n"); - - for (i = 0; i < sockets; i++) { - psock = socket_table[i].psock; - tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT) - | TCIC_ADDR_INDREG | TCIC_SCF1(psock)); - sstat = tcic_getb(TCIC_SSTAT); - latch = sstat ^ socket_table[psock].last_sstat; - socket_table[i].last_sstat = sstat; - if (tcic_getb(TCIC_ICSR) & TCIC_ICSR_CDCHG) { - tcic_setb(TCIC_ICSR, TCIC_ICSR_CLEAR); - quick = 1; - } - if (latch == 0) - continue; - events = (latch & TCIC_SSTAT_CD) ? SS_DETECT : 0; - events |= (latch & TCIC_SSTAT_WP) ? SS_WRPROT : 0; - if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) { - events |= (latch & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0; - } else { - events |= (latch & TCIC_SSTAT_RDY) ? SS_READY : 0; - events |= (latch & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0; - events |= (latch & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0; - } - if (events) { - pcmcia_parse_events(&socket_table[i].socket, events); - } - } - - /* Schedule next poll, if needed */ - if (((cs_irq == 0) || quick) && (!tcic_timer_pending)) { - poll_timer.expires = jiffies + (quick ? poll_quick : poll_interval); - add_timer(&poll_timer); - tcic_timer_pending = 1; - } - active = 0; - - pr_debug("interrupt done\n"); - return IRQ_HANDLED; -} /* tcic_interrupt */ - -static void tcic_timer(struct timer_list *unused) -{ - pr_debug("tcic_timer()\n"); - tcic_timer_pending = 0; - tcic_interrupt(0, NULL); -} /* tcic_timer */ - -/*====================================================================*/ - -static int tcic_get_status(struct pcmcia_socket *sock, u_int *value) -{ - u_short psock = container_of(sock, struct tcic_socket, socket)->psock; - u_char reg; - - tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT) - | TCIC_ADDR_INDREG | TCIC_SCF1(psock)); - reg = tcic_getb(TCIC_SSTAT); - *value = (reg & TCIC_SSTAT_CD) ? SS_DETECT : 0; - *value |= (reg & TCIC_SSTAT_WP) ? SS_WRPROT : 0; - if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) { - *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0; - } else { - *value |= (reg & TCIC_SSTAT_RDY) ? SS_READY : 0; - *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0; - *value |= (reg & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0; - } - reg = tcic_getb(TCIC_PWR); - if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock))) - *value |= SS_POWERON; - dev_dbg(&sock->dev, "GetStatus(%d) = %#2.2x\n", psock, *value); - return 0; -} /* tcic_get_status */ - -/*====================================================================*/ - -static int tcic_set_socket(struct pcmcia_socket *sock, socket_state_t *state) -{ - u_short psock = container_of(sock, struct tcic_socket, socket)->psock; - u_char reg; - u_short scf1, scf2; - - dev_dbg(&sock->dev, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " - "io_irq %d, csc_mask %#2.2x)\n", psock, state->flags, - state->Vcc, state->Vpp, state->io_irq, state->csc_mask); - tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG); - - reg = tcic_getb(TCIC_PWR); - reg &= ~(TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock)); - - if (state->Vcc == 50) { - switch (state->Vpp) { - case 0: reg |= TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock); break; - case 50: reg |= TCIC_PWR_VCC(psock); break; - case 120: reg |= TCIC_PWR_VPP(psock); break; - default: return -EINVAL; - } - } else if (state->Vcc != 0) - return -EINVAL; - - if (reg != tcic_getb(TCIC_PWR)) - tcic_setb(TCIC_PWR, reg); - - reg = TCIC_ILOCK_HOLD_CCLK | TCIC_ILOCK_CWAIT; - if (state->flags & SS_OUTPUT_ENA) { - tcic_setb(TCIC_SCTRL, TCIC_SCTRL_ENA); - reg |= TCIC_ILOCK_CRESENA; - } else - tcic_setb(TCIC_SCTRL, 0); - if (state->flags & SS_RESET) - reg |= TCIC_ILOCK_CRESET; - tcic_aux_setb(TCIC_AUX_ILOCK, reg); - - tcic_setw(TCIC_ADDR, TCIC_SCF1(psock)); - scf1 = TCIC_SCF1_FINPACK; - scf1 |= TCIC_IRQ(state->io_irq); - if (state->flags & SS_IOCARD) { - scf1 |= TCIC_SCF1_IOSTS; - if (state->flags & SS_SPKR_ENA) - scf1 |= TCIC_SCF1_SPKR; - if (state->flags & SS_DMA_MODE) - scf1 |= TCIC_SCF1_DREQ2 << TCIC_SCF1_DMA_SHIFT; - } - tcic_setw(TCIC_DATA, scf1); - - /* Some general setup stuff, and configure status interrupt */ - reg = TCIC_WAIT_ASYNC | TCIC_WAIT_SENSE | to_cycles(250); - tcic_aux_setb(TCIC_AUX_WCTL, reg); - tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00| - TCIC_IRQ(cs_irq)); - - /* Card status change interrupt mask */ - tcic_setw(TCIC_ADDR, TCIC_SCF2(psock)); - scf2 = TCIC_SCF2_MALL; - if (state->csc_mask & SS_DETECT) scf2 &= ~TCIC_SCF2_MCD; - if (state->flags & SS_IOCARD) { - if (state->csc_mask & SS_STSCHG) reg &= ~TCIC_SCF2_MLBAT1; - } else { - if (state->csc_mask & SS_BATDEAD) reg &= ~TCIC_SCF2_MLBAT1; - if (state->csc_mask & SS_BATWARN) reg &= ~TCIC_SCF2_MLBAT2; - if (state->csc_mask & SS_READY) reg &= ~TCIC_SCF2_MRDY; - } - tcic_setw(TCIC_DATA, scf2); - /* For the ISA bus, the irq should be active-high totem-pole */ - tcic_setb(TCIC_IENA, TCIC_IENA_CDCHG | TCIC_IENA_CFG_HIGH); - - return 0; -} /* tcic_set_socket */ - -/*====================================================================*/ - -static int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) -{ - u_short psock = container_of(sock, struct tcic_socket, socket)->psock; - u_int addr; - u_short base, len, ioctl; - - dev_dbg(&sock->dev, "SetIOMap(%d, %d, %#2.2x, %d ns, " - "%#llx-%#llx)\n", psock, io->map, io->flags, io->speed, - (unsigned long long)io->start, (unsigned long long)io->stop); - if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) || - (io->stop < io->start)) return -EINVAL; - tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT)); - addr = TCIC_IWIN(psock, io->map); - - base = io->start; len = io->stop - io->start; - /* Check to see that len+1 is power of two, etc */ - if ((len & (len+1)) || (base & len)) return -EINVAL; - base |= (len+1)>>1; - tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X); - tcic_setw(TCIC_DATA, base); - - ioctl = (psock << TCIC_ICTL_SS_SHFT); - ioctl |= (len == 0) ? TCIC_ICTL_TINY : 0; - ioctl |= (io->flags & MAP_ACTIVE) ? TCIC_ICTL_ENA : 0; - ioctl |= to_cycles(io->speed) & TCIC_ICTL_WSCNT_MASK; - if (!(io->flags & MAP_AUTOSZ)) { - ioctl |= TCIC_ICTL_QUIET; - ioctl |= (io->flags & MAP_16BIT) ? TCIC_ICTL_BW_16 : TCIC_ICTL_BW_8; - } - tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X); - tcic_setw(TCIC_DATA, ioctl); - - return 0; -} /* tcic_set_io_map */ - -/*====================================================================*/ - -static int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem) -{ - u_short psock = container_of(sock, struct tcic_socket, socket)->psock; - u_short addr, ctl; - u_long base, len, mmap; - - dev_dbg(&sock->dev, "SetMemMap(%d, %d, %#2.2x, %d ns, " - "%#llx-%#llx, %#x)\n", psock, mem->map, mem->flags, - mem->speed, (unsigned long long)mem->res->start, - (unsigned long long)mem->res->end, mem->card_start); - if ((mem->map > 3) || (mem->card_start > 0x3ffffff) || - (mem->res->start > 0xffffff) || (mem->res->end > 0xffffff) || - (mem->res->start > mem->res->end) || (mem->speed > 1000)) - return -EINVAL; - tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT)); - addr = TCIC_MWIN(psock, mem->map); - - base = mem->res->start; len = mem->res->end - mem->res->start; - if ((len & (len+1)) || (base & len)) return -EINVAL; - if (len == 0x0fff) - base = (base >> TCIC_MBASE_HA_SHFT) | TCIC_MBASE_4K_BIT; - else - base = (base | (len+1)>>1) >> TCIC_MBASE_HA_SHFT; - tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X); - tcic_setw(TCIC_DATA, base); - - mmap = mem->card_start - mem->res->start; - mmap = (mmap >> TCIC_MMAP_CA_SHFT) & TCIC_MMAP_CA_MASK; - if (mem->flags & MAP_ATTRIB) mmap |= TCIC_MMAP_REG; - tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X); - tcic_setw(TCIC_DATA, mmap); - - ctl = TCIC_MCTL_QUIET | (psock << TCIC_MCTL_SS_SHFT); - ctl |= to_cycles(mem->speed) & TCIC_MCTL_WSCNT_MASK; - ctl |= (mem->flags & MAP_16BIT) ? 0 : TCIC_MCTL_B8; - ctl |= (mem->flags & MAP_WRPROT) ? TCIC_MCTL_WP : 0; - ctl |= (mem->flags & MAP_ACTIVE) ? TCIC_MCTL_ENA : 0; - tcic_setw(TCIC_ADDR, addr + TCIC_MCTL_X); - tcic_setw(TCIC_DATA, ctl); - - return 0; -} /* tcic_set_mem_map */ - -/*====================================================================*/ - -static int tcic_init(struct pcmcia_socket *s) -{ - int i; - struct resource res = { .start = 0, .end = 0x1000 }; - pccard_io_map io = { 0, 0, 0, 0, 1 }; - pccard_mem_map mem = { .res = &res, }; - - for (i = 0; i < 2; i++) { - io.map = i; - tcic_set_io_map(s, &io); - } - for (i = 0; i < 5; i++) { - mem.map = i; - tcic_set_mem_map(s, &mem); - } - return 0; -} - -static struct pccard_operations tcic_operations = { - .init = tcic_init, - .get_status = tcic_get_status, - .set_socket = tcic_set_socket, - .set_io_map = tcic_set_io_map, - .set_mem_map = tcic_set_mem_map, -}; - -/*====================================================================*/ - -module_init(init_tcic); -module_exit(exit_tcic); diff --git a/drivers/pcmcia/tcic.h b/drivers/pcmcia/tcic.h deleted file mode 100644 index 2c0b8f65ad6c..000000000000 --- a/drivers/pcmcia/tcic.h +++ /dev/null @@ -1,266 +0,0 @@ -/* - * tcic.h 1.13 1999/10/25 20:03:34 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License - * at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and - * limitations under the License. - * - * The initial developer of the original code is David A. Hinds - * . Portions created by David A. Hinds - * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License version 2 (the "GPL"), in which - * case the provisions of the GPL are applicable instead of the - * above. If you wish to allow the use of your version of this file - * only under the terms of the GPL and not to allow others to use - * your version of this file under the MPL, indicate your decision by - * deleting the provisions above and replace them with the notice and - * other provisions required by the GPL. If you do not delete the - * provisions above, a recipient may use your version of this file - * under either the MPL or the GPL. - */ - -#ifndef _LINUX_TCIC_H -#define _LINUX_TCIC_H - -#define TCIC_BASE 0x240 - -/* offsets of registers from TCIC_BASE */ -#define TCIC_DATA 0x00 -#define TCIC_ADDR 0x02 -#define TCIC_SCTRL 0x06 -#define TCIC_SSTAT 0x07 -#define TCIC_MODE 0x08 -#define TCIC_PWR 0x09 -#define TCIC_EDC 0x0A -#define TCIC_ICSR 0x0C -#define TCIC_IENA 0x0D -#define TCIC_AUX 0x0E - -#define TCIC_SS_SHFT 12 -#define TCIC_SS_MASK 0x7000 - -/* Flags for TCIC_ADDR */ -#define TCIC_ADR2_REG 0x8000 -#define TCIC_ADR2_INDREG 0x0800 - -#define TCIC_ADDR_REG 0x80000000 -#define TCIC_ADDR_SS_SHFT (TCIC_SS_SHFT+16) -#define TCIC_ADDR_SS_MASK (TCIC_SS_MASK<<16) -#define TCIC_ADDR_INDREG 0x08000000 -#define TCIC_ADDR_IO 0x04000000 -#define TCIC_ADDR_MASK 0x03ffffff - -/* Flags for TCIC_SCTRL */ -#define TCIC_SCTRL_ENA 0x01 -#define TCIC_SCTRL_INCMODE 0x18 -#define TCIC_SCTRL_INCMODE_HOLD 0x00 -#define TCIC_SCTRL_INCMODE_WORD 0x08 -#define TCIC_SCTRL_INCMODE_REG 0x10 -#define TCIC_SCTRL_INCMODE_AUTO 0x18 -#define TCIC_SCTRL_EDCSUM 0x20 -#define TCIC_SCTRL_RESET 0x80 - -/* Flags for TCIC_SSTAT */ -#define TCIC_SSTAT_6US 0x01 -#define TCIC_SSTAT_10US 0x02 -#define TCIC_SSTAT_PROGTIME 0x04 -#define TCIC_SSTAT_LBAT1 0x08 -#define TCIC_SSTAT_LBAT2 0x10 -#define TCIC_SSTAT_RDY 0x20 /* Inverted */ -#define TCIC_SSTAT_WP 0x40 -#define TCIC_SSTAT_CD 0x80 /* Card detect */ - -/* Flags for TCIC_MODE */ -#define TCIC_MODE_PGMMASK 0x1f -#define TCIC_MODE_NORMAL 0x00 -#define TCIC_MODE_PGMWR 0x01 -#define TCIC_MODE_PGMRD 0x02 -#define TCIC_MODE_PGMCE 0x04 -#define TCIC_MODE_PGMDBW 0x08 -#define TCIC_MODE_PGMWORD 0x10 -#define TCIC_MODE_AUXSEL_MASK 0xe0 - -/* Registers accessed through TCIC_AUX, by setting TCIC_MODE */ -#define TCIC_AUX_TCTL (0<<5) -#define TCIC_AUX_PCTL (1<<5) -#define TCIC_AUX_WCTL (2<<5) -#define TCIC_AUX_EXTERN (3<<5) -#define TCIC_AUX_PDATA (4<<5) -#define TCIC_AUX_SYSCFG (5<<5) -#define TCIC_AUX_ILOCK (6<<5) -#define TCIC_AUX_TEST (7<<5) - -/* Flags for TCIC_PWR */ -#define TCIC_PWR_VCC(sock) (0x01<<(sock)) -#define TCIC_PWR_VCC_MASK 0x03 -#define TCIC_PWR_VPP(sock) (0x08<<(sock)) -#define TCIC_PWR_VPP_MASK 0x18 -#define TCIC_PWR_CLIMENA 0x40 -#define TCIC_PWR_CLIMSTAT 0x80 - -/* Flags for TCIC_ICSR */ -#define TCIC_ICSR_CLEAR 0x01 -#define TCIC_ICSR_SET 0x02 -#define TCIC_ICSR_JAM (TCIC_ICSR_CLEAR|TCIC_ICSR_SET) -#define TCIC_ICSR_STOPCPU 0x04 -#define TCIC_ICSR_ILOCK 0x08 -#define TCIC_ICSR_PROGTIME 0x10 -#define TCIC_ICSR_ERR 0x20 -#define TCIC_ICSR_CDCHG 0x40 -#define TCIC_ICSR_IOCHK 0x80 - -/* Flags for TCIC_IENA */ -#define TCIC_IENA_CFG_MASK 0x03 -#define TCIC_IENA_CFG_OFF 0x00 /* disabled */ -#define TCIC_IENA_CFG_OD 0x01 /* active low, open drain */ -#define TCIC_IENA_CFG_LOW 0x02 /* active low, totem pole */ -#define TCIC_IENA_CFG_HIGH 0x03 /* active high, totem pole */ -#define TCIC_IENA_ILOCK 0x08 -#define TCIC_IENA_PROGTIME 0x10 -#define TCIC_IENA_ERR 0x20 /* overcurrent or iochk */ -#define TCIC_IENA_CDCHG 0x40 - -/* Flags for TCIC_AUX_WCTL */ -#define TCIC_WAIT_COUNT_MASK 0x001f -#define TCIC_WAIT_ASYNC 0x0020 -#define TCIC_WAIT_SENSE 0x0040 -#define TCIC_WAIT_SRC 0x0080 -#define TCIC_WCTL_WR 0x0100 -#define TCIC_WCTL_RD 0x0200 -#define TCIC_WCTL_CE 0x0400 -#define TCIC_WCTL_LLBAT1 0x0800 -#define TCIC_WCTL_LLBAT2 0x1000 -#define TCIC_WCTL_LRDY 0x2000 -#define TCIC_WCTL_LWP 0x4000 -#define TCIC_WCTL_LCD 0x8000 - -/* Flags for TCIC_AUX_SYSCFG */ -#define TCIC_SYSCFG_IRQ_MASK 0x000f -#define TCIC_SYSCFG_MCSFULL 0x0010 -#define TCIC_SYSCFG_IO1723 0x0020 -#define TCIC_SYSCFG_MCSXB 0x0040 -#define TCIC_SYSCFG_ICSXB 0x0080 -#define TCIC_SYSCFG_NOPDN 0x0100 -#define TCIC_SYSCFG_MPSEL_SHFT 9 -#define TCIC_SYSCFG_MPSEL_MASK 0x0e00 -#define TCIC_SYSCFG_MPSENSE 0x2000 -#define TCIC_SYSCFG_AUTOBUSY 0x4000 -#define TCIC_SYSCFG_ACC 0x8000 - -#define TCIC_ILOCK_OUT 0x01 -#define TCIC_ILOCK_SENSE 0x02 -#define TCIC_ILOCK_CRESET 0x04 -#define TCIC_ILOCK_CRESENA 0x08 -#define TCIC_ILOCK_CWAIT 0x10 -#define TCIC_ILOCK_CWAITSNS 0x20 -#define TCIC_ILOCK_HOLD_MASK 0xc0 -#define TCIC_ILOCK_HOLD_CCLK 0xc0 - -#define TCIC_ILOCKTEST_ID_SH 8 -#define TCIC_ILOCKTEST_ID_MASK 0x7f00 -#define TCIC_ILOCKTEST_MCIC_1 0x8000 - -#define TCIC_ID_DB86082 0x02 -#define TCIC_ID_DB86082A 0x03 -#define TCIC_ID_DB86084 0x04 -#define TCIC_ID_DB86084A 0x08 -#define TCIC_ID_DB86072 0x15 -#define TCIC_ID_DB86184 0x14 -#define TCIC_ID_DB86082B 0x17 - -#define TCIC_TEST_DIAG 0x8000 - -/* - * Indirectly addressed registers - */ - -#define TCIC_SCF1(sock) ((sock)<<3) -#define TCIC_SCF2(sock) (((sock)<<3)+2) - -/* Flags for SCF1 */ -#define TCIC_SCF1_IRQ_MASK 0x000f -#define TCIC_SCF1_IRQ_OFF 0x0000 -#define TCIC_SCF1_IRQOC 0x0010 -#define TCIC_SCF1_PCVT 0x0020 -#define TCIC_SCF1_IRDY 0x0040 -#define TCIC_SCF1_ATA 0x0080 -#define TCIC_SCF1_DMA_SHIFT 8 -#define TCIC_SCF1_DMA_MASK 0x0700 -#define TCIC_SCF1_DMA_OFF 0 -#define TCIC_SCF1_DREQ2 2 -#define TCIC_SCF1_IOSTS 0x0800 -#define TCIC_SCF1_SPKR 0x1000 -#define TCIC_SCF1_FINPACK 0x2000 -#define TCIC_SCF1_DELWR 0x4000 -#define TCIC_SCF1_HD7IDE 0x8000 - -/* Flags for SCF2 */ -#define TCIC_SCF2_RI 0x0001 -#define TCIC_SCF2_IDBR 0x0002 -#define TCIC_SCF2_MDBR 0x0004 -#define TCIC_SCF2_MLBAT1 0x0008 -#define TCIC_SCF2_MLBAT2 0x0010 -#define TCIC_SCF2_MRDY 0x0020 -#define TCIC_SCF2_MWP 0x0040 -#define TCIC_SCF2_MCD 0x0080 -#define TCIC_SCF2_MALL 0x00f8 - -/* Indirect addresses for memory window registers */ -#define TCIC_MWIN(sock,map) (0x100+(((map)+((sock)<<2))<<3)) -#define TCIC_MBASE_X 2 -#define TCIC_MMAP_X 4 -#define TCIC_MCTL_X 6 - -#define TCIC_MBASE_4K_BIT 0x4000 -#define TCIC_MBASE_HA_SHFT 12 -#define TCIC_MBASE_HA_MASK 0x0fff - -#define TCIC_MMAP_REG 0x8000 -#define TCIC_MMAP_CA_SHFT 12 -#define TCIC_MMAP_CA_MASK 0x3fff - -#define TCIC_MCTL_WSCNT_MASK 0x001f -#define TCIC_MCTL_WCLK 0x0020 -#define TCIC_MCTL_WCLK_CCLK 0x0000 -#define TCIC_MCTL_WCLK_BCLK 0x0020 -#define TCIC_MCTL_QUIET 0x0040 -#define TCIC_MCTL_WP 0x0080 -#define TCIC_MCTL_ACC 0x0100 -#define TCIC_MCTL_KE 0x0200 -#define TCIC_MCTL_EDC 0x0400 -#define TCIC_MCTL_B8 0x0800 -#define TCIC_MCTL_SS_SHFT TCIC_SS_SHFT -#define TCIC_MCTL_SS_MASK TCIC_SS_MASK -#define TCIC_MCTL_ENA 0x8000 - -/* Indirect addresses for I/O window registers */ -#define TCIC_IWIN(sock,map) (0x200+(((map)+((sock)<<1))<<2)) -#define TCIC_IBASE_X 0 -#define TCIC_ICTL_X 2 - -#define TCIC_ICTL_WSCNT_MASK TCIC_MCTL_WSCNT_MASK -#define TCIC_ICTL_QUIET TCIC_MCTL_QUIET -#define TCIC_ICTL_1K 0x0080 -#define TCIC_ICTL_PASS16 0x0100 -#define TCIC_ICTL_ACC TCIC_MCTL_ACC -#define TCIC_ICTL_TINY 0x0200 -#define TCIC_ICTL_B16 0x0400 -#define TCIC_ICTL_B8 TCIC_MCTL_B8 -#define TCIC_ICTL_BW_MASK (TCIC_ICTL_B16|TCIC_ICTL_B8) -#define TCIC_ICTL_BW_DYN 0 -#define TCIC_ICTL_BW_8 TCIC_ICTL_B8 -#define TCIC_ICTL_BW_16 TCIC_ICTL_B16 -#define TCIC_ICTL_BW_ATA (TCIC_ICTL_B16|TCIC_ICTL_B8) -#define TCIC_ICTL_SS_SHFT TCIC_SS_SHFT -#define TCIC_ICTL_SS_MASK TCIC_SS_MASK -#define TCIC_ICTL_ENA TCIC_MCTL_ENA - -#endif /* _LINUX_TCIC_H */ diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 54dc1fee57aa..08c22257b085 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -674,9 +674,8 @@ static int yenta_search_res(struct yenta_socket *socket, struct resource *res, u32 min) { struct resource *root; - int i; - pci_bus_for_each_resource(socket->dev->bus, root, i) { + pci_bus_for_each_resource(socket->dev->bus, root) { if (!root) continue; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 1875d5b784f6..227b9a4c612e 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -46,6 +46,26 @@ config GENERIC_PHY_MIPI_DPHY Provides a number of helpers a core functions for MIPI D-PHY drivers to us. +config PHY_AIROHA_PCIE + tristate "Airoha PCIe-PHY Driver" + depends on ARCH_AIROHA || COMPILE_TEST + depends on OF + select GENERIC_PHY + help + Say Y here to add support for Airoha PCIe PHY driver. + This driver create the basic PHY instance and provides initialize + callback for PCIe GEN3 port. + +config PHY_CAN_TRANSCEIVER + tristate "CAN transceiver PHY" + select GENERIC_PHY + select MULTIPLEXER + help + This option enables support for CAN transceivers as a PHY. This + driver provides function for putting the transceivers in various + functional modes using gpios and sets the attribute max link + rate, for CAN drivers. + config PHY_GOOGLE_USB tristate "Google Tensor SoC USB PHY driver" select GENERIC_PHY @@ -57,6 +77,18 @@ config PHY_GOOGLE_USB both of which are integrated with the DWC3 USB DRD controller. This driver currently supports USB high-speed. +config USB_LGM_PHY + tristate "INTEL Lightning Mountain USB PHY Driver" + depends on USB_SUPPORT + depends on X86 || COMPILE_TEST + select USB_PHY + select REGULATOR + select REGULATOR_FIXED_VOLTAGE + help + Enable this to support Intel DWC3 PHY USB phy. This driver provides + interface to interact with USB GEN-II and USB 3.x PHY that is part + of the Intel network SOC. + config PHY_LPC18XX_USB_OTG tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver" depends on OF && (ARCH_LPC18XX || COMPILE_TEST) @@ -68,6 +100,17 @@ config PHY_LPC18XX_USB_OTG This driver is need for USB0 support on LPC18xx/43xx and takes care of enabling and clock setup. +config PHY_NXP_PTN3222 + tristate "NXP PTN3222 1-port eUSB2 to USB2 redriver" + depends on I2C + depends on OF + select GENERIC_PHY + help + Enable this to support NXP PTN3222 1-port eUSB2 to USB2 Redriver. + This redriver performs translation between eUSB2 and USB2 signalling + schemes. It supports all three USB 2.0 data rates: Low Speed, Full + Speed and High Speed. + config PHY_PISTACHIO_USB tristate "IMG Pistachio USB2.0 PHY driver" depends on MIPS || COMPILE_TEST @@ -90,69 +133,17 @@ config PHY_XGENE help This option enables support for APM X-Gene SoC multi-purpose PHY. -config USB_LGM_PHY - tristate "INTEL Lightning Mountain USB PHY Driver" - depends on USB_SUPPORT - depends on X86 || COMPILE_TEST - select USB_PHY - select REGULATOR - select REGULATOR_FIXED_VOLTAGE - help - Enable this to support Intel DWC3 PHY USB phy. This driver provides - interface to interact with USB GEN-II and USB 3.x PHY that is part - of the Intel network SOC. - -config PHY_CAN_TRANSCEIVER - tristate "CAN transceiver PHY" - select GENERIC_PHY - select MULTIPLEXER - help - This option enables support for CAN transceivers as a PHY. This - driver provides function for putting the transceivers in various - functional modes using gpios and sets the attribute max link - rate, for CAN drivers. - -config PHY_AIROHA_PCIE - tristate "Airoha PCIe-PHY Driver" - depends on ARCH_AIROHA || COMPILE_TEST - depends on OF - select GENERIC_PHY - help - Say Y here to add support for Airoha PCIe PHY driver. - This driver create the basic PHY instance and provides initialize - callback for PCIe GEN3 port. - -config PHY_NXP_PTN3222 - tristate "NXP PTN3222 1-port eUSB2 to USB2 redriver" - depends on I2C - depends on OF - select GENERIC_PHY - help - Enable this to support NXP PTN3222 1-port eUSB2 to USB2 Redriver. - This redriver performs translation between eUSB2 and USB2 signalling - schemes. It supports all three USB 2.0 data rates: Low Speed, Full - Speed and High Speed. - -config PHY_SPACEMIT_K1_PCIE - tristate "PCIe and combo PHY driver for the SpacemiT K1 SoC" - depends on ARCH_SPACEMIT || COMPILE_TEST - depends on COMMON_CLK - depends on HAS_IOMEM - depends on OF - select GENERIC_PHY - default ARCH_SPACEMIT - help - Enable support for the PCIe and USB 3 combo PHY and two - PCIe-only PHYs used in the SpacemiT K1 SoC. - source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" source "drivers/phy/apple/Kconfig" source "drivers/phy/broadcom/Kconfig" source "drivers/phy/cadence/Kconfig" +source "drivers/phy/canaan/Kconfig" +source "drivers/phy/eswin/Kconfig" source "drivers/phy/freescale/Kconfig" source "drivers/phy/hisilicon/Kconfig" source "drivers/phy/ingenic/Kconfig" +source "drivers/phy/intel/Kconfig" source "drivers/phy/lantiq/Kconfig" source "drivers/phy/marvell/Kconfig" source "drivers/phy/mediatek/Kconfig" @@ -174,7 +165,6 @@ source "drivers/phy/starfive/Kconfig" source "drivers/phy/sunplus/Kconfig" source "drivers/phy/tegra/Kconfig" source "drivers/phy/ti/Kconfig" -source "drivers/phy/intel/Kconfig" source "drivers/phy/xilinx/Kconfig" endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index a648c2e02a83..f49d83f00a3d 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -7,21 +7,23 @@ obj-$(CONFIG_PHY_COMMON_PROPS) += phy-common-props.o obj-$(CONFIG_PHY_COMMON_PROPS_TEST) += phy-common-props-test.o obj-$(CONFIG_GENERIC_PHY) += phy-core.o obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o +obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o obj-$(CONFIG_PHY_CAN_TRANSCEIVER) += phy-can-transceiver.o obj-$(CONFIG_PHY_GOOGLE_USB) += phy-google-usb.o +obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o -obj-$(CONFIG_PHY_XGENE) += phy-xgene.o +obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o obj-$(CONFIG_PHY_SNPS_EUSB2) += phy-snps-eusb2.o -obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o -obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o -obj-$(CONFIG_PHY_NXP_PTN3222) += phy-nxp-ptn3222.o -obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE) += phy-spacemit-k1-pcie.o +obj-$(CONFIG_PHY_XGENE) += phy-xgene.o + obj-$(CONFIG_GENERIC_PHY) += allwinner/ \ amlogic/ \ apple/ \ broadcom/ \ cadence/ \ + canaan/ \ + eswin/ \ freescale/ \ hisilicon/ \ ingenic/ \ diff --git a/drivers/phy/apple/atc.c b/drivers/phy/apple/atc.c index dc867f368b68..e9d106f135c5 100644 --- a/drivers/phy/apple/atc.c +++ b/drivers/phy/apple/atc.c @@ -2023,7 +2023,7 @@ static int atcphy_dwc3_reset_deassert(struct reset_controller_dev *rcdev, unsign return 0; } -const struct reset_control_ops atcphy_dwc3_reset_ops = { +static const struct reset_control_ops atcphy_dwc3_reset_ops = { .assert = atcphy_dwc3_reset_assert, .deassert = atcphy_dwc3_reset_deassert, }; @@ -2202,14 +2202,16 @@ static int atcphy_map_resources(struct platform_device *pdev, struct apple_atcph { "pipehandler", &atcphy->regs.pipehandler, NULL }, }; struct resource *res; + void __iomem *addr; for (int i = 0; i < ARRAY_SIZE(resources); i++) { res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resources[i].name); - *resources[i].addr = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(resources[i].addr)) - return dev_err_probe(atcphy->dev, PTR_ERR(resources[i].addr), + addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(addr)) + return dev_err_probe(atcphy->dev, PTR_ERR(addr), "Unable to map %s regs", resources[i].name); + *resources[i].addr = addr; if (resources[i].res) *resources[i].res = res; } diff --git a/drivers/phy/canaan/Kconfig b/drivers/phy/canaan/Kconfig new file mode 100644 index 000000000000..1ff8831846d5 --- /dev/null +++ b/drivers/phy/canaan/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Phy drivers for Canaan platforms +# +config PHY_CANAAN_USB + tristate "Canaan USB2 PHY Driver" + depends on (ARCH_CANAAN || COMPILE_TEST) && OF + select GENERIC_PHY + help + Enable this driver to support the USB 2.0 PHY controller + on Canaan K230 RISC-V SoCs. This PHY controller + provides physical layer functionality for USB 2.0 devices. + If you have a Canaan K230 board and need USB 2.0 support, + say Y or M here. diff --git a/drivers/phy/canaan/Makefile b/drivers/phy/canaan/Makefile new file mode 100644 index 000000000000..d73857ba284e --- /dev/null +++ b/drivers/phy/canaan/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_CANAAN_USB) += phy-k230-usb.o diff --git a/drivers/phy/canaan/phy-k230-usb.c b/drivers/phy/canaan/phy-k230-usb.c new file mode 100644 index 000000000000..52dad35fc6cf --- /dev/null +++ b/drivers/phy/canaan/phy-k230-usb.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Canaan usb PHY driver + * + * Copyright (C) 2026 Jiayu Du + */ + +#include +#include +#include +#include +#include + +#define MAX_PHYS 2 + +/* Register offsets within the HiSysConfig system controller */ +#define K230_USB0_TEST_REG_BASE 0x70 +#define K230_USB0_CTL_REG_BASE 0xb0 +#define K230_USB1_TEST_REG_BASE 0x90 +#define K230_USB1_CTL_REG_BASE 0xb8 + +/* Relative offsets within each PHY's control/test block */ +#define CTL0_OFFSET 0x00 +#define CTL1_OFFSET 0x04 +#define TEST_CTL3_OFFSET 0x0c + +/* Bit definitions for TEST_CTL3 */ +#define USB_IDPULLUP0 BIT(4) +#define USB_DMPULLDOWN0 BIT(8) +#define USB_DPPULLDOWN0 BIT(9) + +/* USB control register 0 in HiSysConfig system controller */ +/* PLL Integral Path Tune */ +#define USB_CTL0_PLLITUNE_MASK GENMASK(23, 22) + +/* PLL Proportional Path Tune */ +#define USB_CTL0_PLLPTUNE_MASK GENMASK(21, 18) + +/* PLL Bandwidth Adjustment */ +#define USB_CTL0_PLLBTUNE_MASK GENMASK(17, 17) + +/* VReg18 Bypass Control */ +#define USB_CTL0_VREGBYPASS_MASK GENMASK(16, 16) + +/* Retention Mode Enable */ +#define USB_CTL0_RETENABLEN_MASK GENMASK(15, 15) + +/* Reserved Request Input */ +#define USB_CTL0_RESREQIN_MASK GENMASK(14, 14) + +/* External VBUS Valid Select */ +#define USB_CTL0_VBUSVLDEXTSEL0_MASK GENMASK(13, 13) + +/* OTG Block Disable Control */ +#define USB_CTL0_OTGDISABLE0_MASK GENMASK(12, 12) + +/* Drive VBUS Enable */ +#define USB_CTL0_DRVVBUS0_MASK GENMASK(11, 11) + +/* Autoresume Mode Enable */ +#define USB_CTL0_AUTORSMENB0_MASK GENMASK(10, 10) + +/* HS Transceiver Asynchronous Control */ +#define USB_CTL0_HSXCVREXTCTL0_MASK GENMASK(9, 9) + +/* USB 1.1 Transmit Data */ +#define USB_CTL0_FSDATAEXT0_MASK GENMASK(8, 8) + +/* USB 1.1 SE0 Generation */ +#define USB_CTL0_FSSE0EXT0_MASK GENMASK(7, 7) + +/* USB 1.1 Data Enable */ +#define USB_CTL0_TXENABLEN0_MASK GENMASK(6, 6) + +/* Disconnect Threshold */ +#define USB_CTL0_COMPDISTUNE0_MASK GENMASK(5, 3) + +/* Squelch Threshold */ +#define USB_CTL0_SQRXTUNE0_MASK GENMASK(2, 0) + +/* USB control register 1 in HiSysConfig system controller */ +/* Data Detect Voltage */ +#define USB_CTL1_VDATREFTUNE0_MASK GENMASK(23, 22) + +/* VBUS Valid Threshold */ +#define USB_CTL1_OTGTUNE0_MASK GENMASK(21, 19) + +/* Transmitter High-Speed Crossover */ +#define USB_CTL1_TXHSXVTUNE0_MASK GENMASK(18, 17) + +/* FS/LS Source Impedance */ +#define USB_CTL1_TXFSLSTUNE0_MASK GENMASK(16, 13) + +/* HS DC Voltage Level */ +#define USB_CTL1_TXVREFTUNE0_MASK GENMASK(12, 9) + +/* HS Transmitter Rise/Fall Time */ +#define USB_CTL1_TXRISETUNE0_MASK GENMASK(8, 7) + +/* USB Source Impedance */ +#define USB_CTL1_TXRESTUNE0_MASK GENMASK(6, 5) + +/* HS Transmitter Pre-Emphasis Current Control */ +#define USB_CTL1_TXPREEMPAMPTUNE0_MASK GENMASK(4, 3) + +/* HS Transmitter Pre-Emphasis Duration Control */ +#define USB_CTL1_TXPREEMPPULSETUNE0_MASK GENMASK(2, 2) + +/* charging detection */ +#define USB_CTL1_CHRGSRCPUENB0_MASK GENMASK(1, 0) + +#define K230_PHY_CTL0_VAL \ +( \ + FIELD_PREP(USB_CTL0_PLLITUNE_MASK, 0x0) | \ + FIELD_PREP(USB_CTL0_PLLPTUNE_MASK, 0xc) | \ + FIELD_PREP(USB_CTL0_PLLBTUNE_MASK, 0x1) | \ + FIELD_PREP(USB_CTL0_VREGBYPASS_MASK, 0x1) | \ + FIELD_PREP(USB_CTL0_RETENABLEN_MASK, 0x1) | \ + FIELD_PREP(USB_CTL0_RESREQIN_MASK, 0x0) | \ + FIELD_PREP(USB_CTL0_VBUSVLDEXTSEL0_MASK, 0x0) | \ + FIELD_PREP(USB_CTL0_OTGDISABLE0_MASK, 0x0) | \ + FIELD_PREP(USB_CTL0_DRVVBUS0_MASK, 0x1) | \ + FIELD_PREP(USB_CTL0_AUTORSMENB0_MASK, 0x0) | \ + FIELD_PREP(USB_CTL0_HSXCVREXTCTL0_MASK, 0x0) | \ + FIELD_PREP(USB_CTL0_FSDATAEXT0_MASK, 0x0) | \ + FIELD_PREP(USB_CTL0_FSSE0EXT0_MASK, 0x0) | \ + FIELD_PREP(USB_CTL0_TXENABLEN0_MASK, 0x0) | \ + FIELD_PREP(USB_CTL0_COMPDISTUNE0_MASK, 0x3) | \ + FIELD_PREP(USB_CTL0_SQRXTUNE0_MASK, 0x3) \ +) + +#define K230_PHY_CTL1_VAL \ +( \ + FIELD_PREP(USB_CTL1_VDATREFTUNE0_MASK, 0x1) | \ + FIELD_PREP(USB_CTL1_OTGTUNE0_MASK, 0x3) | \ + FIELD_PREP(USB_CTL1_TXHSXVTUNE0_MASK, 0x3) | \ + FIELD_PREP(USB_CTL1_TXFSLSTUNE0_MASK, 0x3) | \ + FIELD_PREP(USB_CTL1_TXVREFTUNE0_MASK, 0x3) | \ + FIELD_PREP(USB_CTL1_TXRISETUNE0_MASK, 0x1) | \ + FIELD_PREP(USB_CTL1_TXRESTUNE0_MASK, 0x1) | \ + FIELD_PREP(USB_CTL1_TXPREEMPAMPTUNE0_MASK, 0x0) | \ + FIELD_PREP(USB_CTL1_TXPREEMPPULSETUNE0_MASK, 0x0) | \ + FIELD_PREP(USB_CTL1_CHRGSRCPUENB0_MASK, 0x0) \ +) + +struct k230_usb_phy_instance { + struct k230_usb_phy_global *global; + struct phy *phy; + u32 test_offset; + u32 ctl_offset; + int index; +}; + +struct k230_usb_phy_global { + struct k230_usb_phy_instance phys[MAX_PHYS]; + void __iomem *base; +}; + +static int k230_usb_phy_power_on(struct phy *phy) +{ + struct k230_usb_phy_instance *inst = phy_get_drvdata(phy); + struct k230_usb_phy_global *global = inst->global; + void __iomem *base = global->base; + u32 val; + + /* Apply recommended settings */ + writel(K230_PHY_CTL0_VAL, base + inst->ctl_offset + CTL0_OFFSET); + writel(K230_PHY_CTL1_VAL, base + inst->ctl_offset + CTL1_OFFSET); + + /* Configure test register (pull-ups/pull-downs) */ + val = readl(base + inst->test_offset + TEST_CTL3_OFFSET); + val |= USB_IDPULLUP0; + + if (inst->index == 1) + val |= (USB_DMPULLDOWN0 | USB_DPPULLDOWN0); + else + val &= ~(USB_DMPULLDOWN0 | USB_DPPULLDOWN0); + + writel(val, base + inst->test_offset + TEST_CTL3_OFFSET); + + return 0; +} + +static int k230_usb_phy_power_off(struct phy *phy) +{ + struct k230_usb_phy_instance *inst = phy_get_drvdata(phy); + struct k230_usb_phy_global *global = inst->global; + void __iomem *base = global->base; + u32 val; + + val = readl(base + inst->test_offset + TEST_CTL3_OFFSET); + val &= ~(USB_DMPULLDOWN0 | USB_DPPULLDOWN0); + writel(val, base + inst->test_offset + TEST_CTL3_OFFSET); + + return 0; +} + +static const struct phy_ops k230_usb_phy_ops = { + .power_on = k230_usb_phy_power_on, + .power_off = k230_usb_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct phy *k230_usb_phy_xlate(struct device *dev, + const struct of_phandle_args *args) +{ + struct k230_usb_phy_global *global = dev_get_drvdata(dev); + unsigned int idx = args->args[0]; + + if (idx >= MAX_PHYS) + return ERR_PTR(-EINVAL); + + return global->phys[idx].phy; +} + +static int k230_usb_phy_probe(struct platform_device *pdev) +{ + struct k230_usb_phy_global *global; + struct device *dev = &pdev->dev; + struct phy_provider *provider; + int i; + + global = devm_kzalloc(dev, sizeof(*global), GFP_KERNEL); + if (!global) + return -ENOMEM; + dev_set_drvdata(dev, global); + + global->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(global->base)) + return dev_err_probe(dev, PTR_ERR(global->base), + "failed to map registers\n"); + + static const struct { + u32 test_offset; + u32 ctl_offset; + } phy_reg_info[MAX_PHYS] = { + [0] = { K230_USB0_TEST_REG_BASE, K230_USB0_CTL_REG_BASE }, + [1] = { K230_USB1_TEST_REG_BASE, K230_USB1_CTL_REG_BASE }, + }; + + for (i = 0; i < MAX_PHYS; i++) { + struct k230_usb_phy_instance *inst = &global->phys[i]; + struct phy *phy; + + inst->global = global; + inst->index = i; + inst->test_offset = phy_reg_info[i].test_offset; + inst->ctl_offset = phy_reg_info[i].ctl_offset; + + phy = devm_phy_create(dev, NULL, &k230_usb_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create phy%d\n", i); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, inst); + inst->phy = phy; + } + + provider = devm_of_phy_provider_register(dev, k230_usb_phy_xlate); + if (IS_ERR(provider)) + return PTR_ERR(provider); + + return 0; +} + +static const struct of_device_id k230_usb_phy_of_match[] = { + { .compatible = "canaan,k230-usb-phy" }, + {} +}; +MODULE_DEVICE_TABLE(of, k230_usb_phy_of_match); + +static struct platform_driver k230_usb_phy_driver = { + .probe = k230_usb_phy_probe, + .driver = { + .name = "k230-usb-phy", + .of_match_table = k230_usb_phy_of_match, + }, +}; +module_platform_driver(k230_usb_phy_driver); + +MODULE_DESCRIPTION("Canaan Kendryte K230 USB 2.0 PHY driver"); +MODULE_AUTHOR("Jiayu Du "); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/eswin/Kconfig b/drivers/phy/eswin/Kconfig new file mode 100644 index 000000000000..cf2bf2efc32f --- /dev/null +++ b/drivers/phy/eswin/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Phy drivers for ESWIN platforms +# +config PHY_EIC7700_SATA + tristate "eic7700 Sata SerDes/PHY driver" + depends on ARCH_ESWIN || COMPILE_TEST + depends on HAS_IOMEM + select GENERIC_PHY + help + Enable this to support SerDes/Phy found on ESWIN's + EIC7700 SoC. This Phy supports SATA 1.5 Gb/s, + SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. + It supports one SATA host port to accept one SATA device. diff --git a/drivers/phy/eswin/Makefile b/drivers/phy/eswin/Makefile new file mode 100644 index 000000000000..db08c66be812 --- /dev/null +++ b/drivers/phy/eswin/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_EIC7700_SATA) += phy-eic7700-sata.o diff --git a/drivers/phy/eswin/phy-eic7700-sata.c b/drivers/phy/eswin/phy-eic7700-sata.c new file mode 100644 index 000000000000..c33653d48daa --- /dev/null +++ b/drivers/phy/eswin/phy-eic7700-sata.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ESWIN SATA PHY driver + * + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * Authors: Yulin Lu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SATA_AXI_LP_CTRL 0x08 +#define SATA_MPLL_CTRL 0x20 +#define SATA_P0_PHY_STAT 0x24 +#define SATA_PHY_CTRL0 0x28 +#define SATA_PHY_CTRL1 0x2c +#define SATA_REF_CTRL 0x34 +#define SATA_REF_CTRL1 0x38 +#define SATA_LOS_IDEN 0x3c + +#define SATA_CLK_RST_SOURCE_PHY BIT(0) +#define SATA_P0_PHY_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0) +#define SATA_P0_PHY_TX_AMPLITUDE_GEN1_DEFAULT 0x42 +#define SATA_P0_PHY_TX_AMPLITUDE_GEN2_MASK GENMASK(14, 8) +#define SATA_P0_PHY_TX_AMPLITUDE_GEN2_DEFAULT 0x46 +#define SATA_P0_PHY_TX_AMPLITUDE_GEN3_MASK GENMASK(22, 16) +#define SATA_P0_PHY_TX_AMPLITUDE_GEN3_DEFAULT 0x73 +#define SATA_P0_PHY_TX_PREEMPH_GEN1_MASK GENMASK(5, 0) +#define SATA_P0_PHY_TX_PREEMPH_GEN1_DEFAULT 0x5 +#define SATA_P0_PHY_TX_PREEMPH_GEN2_MASK GENMASK(13, 8) +#define SATA_P0_PHY_TX_PREEMPH_GEN2_DEFAULT 0x5 +#define SATA_P0_PHY_TX_PREEMPH_GEN3_MASK GENMASK(21, 16) +#define SATA_P0_PHY_TX_PREEMPH_GEN3_DEFAULT 0x23 +#define SATA_LOS_LEVEL_MASK GENMASK(4, 0) +#define SATA_LOS_BIAS_MASK GENMASK(18, 16) +#define SATA_M_CSYSREQ BIT(0) +#define SATA_S_CSYSREQ BIT(16) +#define SATA_REF_REPEATCLK_EN BIT(0) +#define SATA_REF_USE_PAD BIT(20) +#define SATA_MPLL_MULTIPLIER_MASK GENMASK(22, 16) +#define SATA_P0_PHY_READY BIT(0) + +#define PLL_LOCK_SLEEP_US 10 +#define PLL_LOCK_TIMEOUT_US 1000 + +struct eic7700_sata_phy { + u32 tx_amplitude_tuning_val[3]; + u32 tx_preemph_tuning_val[3]; + struct reset_control *rst; + struct regmap *regmap; + struct clk *clk; + struct phy *phy; +}; + +static const struct regmap_config eic7700_sata_phy_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SATA_LOS_IDEN, +}; + +static int wait_for_phy_ready(struct regmap *regmap, u32 reg, u32 checkbit, + u32 status) +{ + u32 val; + int ret; + + ret = regmap_read_poll_timeout(regmap, reg, val, + (val & checkbit) == status, + PLL_LOCK_SLEEP_US, PLL_LOCK_TIMEOUT_US); + + return ret; +} + +static int eic7700_sata_phy_init(struct phy *phy) +{ + struct eic7700_sata_phy *sata_phy = phy_get_drvdata(phy); + u32 val; + int ret; + + ret = clk_prepare_enable(sata_phy->clk); + if (ret) + return ret; + + regmap_write(sata_phy->regmap, SATA_REF_CTRL1, SATA_CLK_RST_SOURCE_PHY); + + val = FIELD_PREP(SATA_P0_PHY_TX_AMPLITUDE_GEN1_MASK, + sata_phy->tx_amplitude_tuning_val[0]) | + FIELD_PREP(SATA_P0_PHY_TX_AMPLITUDE_GEN2_MASK, + sata_phy->tx_amplitude_tuning_val[1]) | + FIELD_PREP(SATA_P0_PHY_TX_AMPLITUDE_GEN3_MASK, + sata_phy->tx_amplitude_tuning_val[2]); + regmap_write(sata_phy->regmap, SATA_PHY_CTRL0, val); + + val = FIELD_PREP(SATA_P0_PHY_TX_PREEMPH_GEN1_MASK, + sata_phy->tx_preemph_tuning_val[0]) | + FIELD_PREP(SATA_P0_PHY_TX_PREEMPH_GEN2_MASK, + sata_phy->tx_preemph_tuning_val[1]) | + FIELD_PREP(SATA_P0_PHY_TX_PREEMPH_GEN3_MASK, + sata_phy->tx_preemph_tuning_val[2]); + regmap_write(sata_phy->regmap, SATA_PHY_CTRL1, val); + + val = FIELD_PREP(SATA_LOS_LEVEL_MASK, 0x9) | + FIELD_PREP(SATA_LOS_BIAS_MASK, 0x2); + regmap_write(sata_phy->regmap, SATA_LOS_IDEN, val); + + val = SATA_M_CSYSREQ | SATA_S_CSYSREQ; + regmap_write(sata_phy->regmap, SATA_AXI_LP_CTRL, val); + + val = SATA_REF_REPEATCLK_EN | SATA_REF_USE_PAD; + regmap_write(sata_phy->regmap, SATA_REF_CTRL, val); + + val = FIELD_PREP(SATA_MPLL_MULTIPLIER_MASK, 0x3c); + regmap_write(sata_phy->regmap, SATA_MPLL_CTRL, val); + + usleep_range(15, 20); + + ret = reset_control_deassert(sata_phy->rst); + if (ret) + goto disable_clk; + + ret = wait_for_phy_ready(sata_phy->regmap, SATA_P0_PHY_STAT, + SATA_P0_PHY_READY, 1); + if (ret < 0) { + dev_err(&sata_phy->phy->dev, "PHY READY check failed\n"); + goto disable_clk; + } + + return 0; + +disable_clk: + clk_disable_unprepare(sata_phy->clk); + return ret; +} + +static int eic7700_sata_phy_exit(struct phy *phy) +{ + struct eic7700_sata_phy *sata_phy = phy_get_drvdata(phy); + int ret; + + ret = reset_control_assert(sata_phy->rst); + if (ret) + return ret; + + clk_disable_unprepare(sata_phy->clk); + + return 0; +} + +static const struct phy_ops eic7700_sata_phy_ops = { + .init = eic7700_sata_phy_init, + .exit = eic7700_sata_phy_exit, + .owner = THIS_MODULE, +}; + +static void eic7700_get_tuning_param(struct device_node *np, + struct eic7700_sata_phy *sata_phy) +{ + if (of_property_read_u32_array + (np, "eswin,tx-amplitude-tuning", + sata_phy->tx_amplitude_tuning_val, + ARRAY_SIZE(sata_phy->tx_amplitude_tuning_val))) { + sata_phy->tx_amplitude_tuning_val[0] = + SATA_P0_PHY_TX_AMPLITUDE_GEN1_DEFAULT; + sata_phy->tx_amplitude_tuning_val[1] = + SATA_P0_PHY_TX_AMPLITUDE_GEN2_DEFAULT; + sata_phy->tx_amplitude_tuning_val[2] = + SATA_P0_PHY_TX_AMPLITUDE_GEN3_DEFAULT; + } + + if (of_property_read_u32_array + (np, "eswin,tx-preemph-tuning", + sata_phy->tx_preemph_tuning_val, + ARRAY_SIZE(sata_phy->tx_preemph_tuning_val))) { + sata_phy->tx_preemph_tuning_val[0] = + SATA_P0_PHY_TX_PREEMPH_GEN1_DEFAULT; + sata_phy->tx_preemph_tuning_val[1] = + SATA_P0_PHY_TX_PREEMPH_GEN2_DEFAULT; + sata_phy->tx_preemph_tuning_val[2] = + SATA_P0_PHY_TX_PREEMPH_GEN3_DEFAULT; + } +} + +static int eic7700_sata_phy_probe(struct platform_device *pdev) +{ + struct eic7700_sata_phy *sata_phy; + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct resource *res; + void __iomem *regs; + + sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL); + if (!sata_phy) + return -ENOMEM; + + /* + * Map the I/O resource with platform_get_resource and devm_ioremap + * instead of the devm_platform_ioremap_resource API, because the + * address region of the SATA-PHY falls into the region of the HSP + * clock & reset that has already been obtained by the HSP + * clock-and-reset driver. + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + regs = devm_ioremap(dev, res->start, resource_size(res)); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + sata_phy->regmap = devm_regmap_init_mmio + (dev, regs, &eic7700_sata_phy_regmap_config); + if (IS_ERR(sata_phy->regmap)) + return dev_err_probe(dev, PTR_ERR(sata_phy->regmap), + "failed to init regmap\n"); + + dev_set_drvdata(dev, sata_phy); + + eic7700_get_tuning_param(np, sata_phy); + + sata_phy->clk = devm_clk_get(dev, "phy"); + if (IS_ERR(sata_phy->clk)) + return PTR_ERR(sata_phy->clk); + + sata_phy->rst = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(sata_phy->rst)) + return dev_err_probe(dev, PTR_ERR(sata_phy->rst), + "failed to get reset control\n"); + + sata_phy->phy = devm_phy_create(dev, NULL, &eic7700_sata_phy_ops); + if (IS_ERR(sata_phy->phy)) + return dev_err_probe(dev, PTR_ERR(sata_phy->phy), + "failed to create PHY\n"); + + phy_set_drvdata(sata_phy->phy, sata_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return dev_err_probe(dev, PTR_ERR(phy_provider), + "failed to register PHY provider\n"); + + return 0; +} + +static const struct of_device_id eic7700_sata_phy_of_match[] = { + { .compatible = "eswin,eic7700-sata-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, eic7700_sata_phy_of_match); + +static struct platform_driver eic7700_sata_phy_driver = { + .probe = eic7700_sata_phy_probe, + .driver = { + .of_match_table = eic7700_sata_phy_of_match, + .name = "eic7700-sata-phy", + } +}; +module_platform_driver(eic7700_sata_phy_driver); + +MODULE_DESCRIPTION("SATA PHY driver for the ESWIN EIC7700 SoC"); +MODULE_AUTHOR("Yulin Lu "); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/marvell/phy-mmp3-hsic.c b/drivers/phy/marvell/phy-mmp3-hsic.c index 271f1a2258ef..72ab6da0ebc3 100644 --- a/drivers/phy/marvell/phy-mmp3-hsic.c +++ b/drivers/phy/marvell/phy-mmp3-hsic.c @@ -14,15 +14,19 @@ #define HSIC_ENABLE BIT(7) #define PLL_BYPASS BIT(4) +struct mmp3_hsic_data { + void __iomem *base; +}; + static int mmp3_hsic_phy_init(struct phy *phy) { - void __iomem *base = (void __iomem *)phy_get_drvdata(phy); + struct mmp3_hsic_data *mmp3 = phy_get_drvdata(phy); u32 hsic_ctrl; - hsic_ctrl = readl_relaxed(base + HSIC_CTRL); + hsic_ctrl = readl_relaxed(mmp3->base + HSIC_CTRL); hsic_ctrl |= HSIC_ENABLE; hsic_ctrl |= PLL_BYPASS; - writel_relaxed(hsic_ctrl, base + HSIC_CTRL); + writel_relaxed(hsic_ctrl, mmp3->base + HSIC_CTRL); return 0; } @@ -41,13 +45,17 @@ MODULE_DEVICE_TABLE(of, mmp3_hsic_phy_of_match); static int mmp3_hsic_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct mmp3_hsic_data *mmp3; struct phy_provider *provider; - void __iomem *base; struct phy *phy; - base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); - if (IS_ERR(base)) - return PTR_ERR(base); + mmp3 = devm_kzalloc(dev, sizeof(*mmp3), GFP_KERNEL); + if (!mmp3) + return -ENOMEM; + + mmp3->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(mmp3->base)) + return PTR_ERR(mmp3->base); phy = devm_phy_create(dev, NULL, &mmp3_hsic_phy_ops); if (IS_ERR(phy)) { @@ -55,7 +63,7 @@ static int mmp3_hsic_phy_probe(struct platform_device *pdev) return PTR_ERR(phy); } - phy_set_drvdata(phy, (void *)base); + phy_set_drvdata(phy, mmp3); provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); if (IS_ERR(provider)) { dev_err(dev, "failed to register PHY provider\n"); diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c index f6504e0ecd1a..acf506529507 100644 --- a/drivers/phy/mediatek/phy-mtk-tphy.c +++ b/drivers/phy/mediatek/phy-mtk-tphy.c @@ -276,14 +276,14 @@ enum mtk_phy_version { }; /** - * mtk_phy_pdata - SoC specific platform data + * struct mtk_phy_pdata - SoC specific platform data * @avoid_rx_sen_degradation: Avoid TX Sensitivity level degradation (MT6795/8173 only) * @sw_pll_48m_to_26m: Workaround for V3 IP (MT8195) - switch the 48MHz PLL from * fractional mode to integer to output 26MHz for U2PHY * @sw_efuse_supported: Switches off eFuse auto-load from PHY and applies values * read from different nvmem (usually different eFuse array) * that is pointed at in the device tree node for this PHY - * @slew_ref_clk_mhz: Default reference clock (in MHz) for slew rate calibration + * @slew_ref_clock_mhz: Default reference clock (in MHz) for slew rate calibration * @slew_rate_coefficient: Coefficient for slew rate calibration * @version: PHY IP Version */ diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c index 95cd3175926d..68f1ba8fec4a 100644 --- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c @@ -83,7 +83,7 @@ static const struct m31_phy_tbl_entry m31_eusb2_setup_tbl[] = { M31_EUSB_PHY_INIT_CFG(USB_PHY_CFG0, UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 1), M31_EUSB_PHY_INIT_CFG(USB_PHY_UTMI_CTRL5, POR, 1), M31_EUSB_PHY_INIT_CFG(USB_PHY_HS_PHY_CTRL_COMMON0, PHY_ENABLE, 1), - M31_EUSB_PHY_INIT_CFG(USB_PHY_CFG1, PLL_EN, 1), + M31_EUSB_PHY_INIT_CFG(USB_PHY_CFG1, PLL_EN, 0), M31_EUSB_PHY_INIT_CFG(USB_PHY_FSEL_SEL, FSEL_SEL, 1), }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c index 14feb77789b3..c342479a3798 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c @@ -1679,7 +1679,7 @@ static int qmp_usbc_register_clocks(struct qmp_usbc *qmp, struct device_node *np if (ret) return ret; - if (qmp->dp_serdes != 0) { + if (qmp->dp_serdes) { ret = phy_dp_clks_register(qmp, np); if (ret) return ret; @@ -1833,7 +1833,7 @@ static int qmp_usbc_parse_dt(struct qmp_usbc *qmp) if (IS_ERR(base)) return PTR_ERR(base); - if (offs->dp_serdes != 0) { + if (offs->dp_serdes) { qmp->dp_serdes = base + offs->dp_serdes; qmp->dp_tx = base + offs->dp_txa; qmp->dp_tx2 = base + offs->dp_txb; @@ -1982,7 +1982,7 @@ static int qmp_usbc_probe(struct platform_device *pdev) phy_set_drvdata(qmp->usb_phy, qmp); - if (qmp->dp_serdes != 0) { + if (qmp->dp_serdes) { qmp->dp_phy = devm_phy_create(dev, np, &qmp_usbc_dp_phy_ops); if (IS_ERR(qmp->dp_phy)) { ret = PTR_ERR(qmp->dp_phy); diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig index 0136aee2e8a2..50b0005acf66 100644 --- a/drivers/phy/spacemit/Kconfig +++ b/drivers/phy/spacemit/Kconfig @@ -2,6 +2,18 @@ # # Phy drivers for SpacemiT platforms # +config PHY_SPACEMIT_K1_PCIE + tristate "PCIe and combo PHY driver for the SpacemiT K1 SoC" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on COMMON_CLK + depends on HAS_IOMEM + depends on OF + select GENERIC_PHY + default ARCH_SPACEMIT + help + Enable support for the PCIe and USB 3 combo PHY and two + PCIe-only PHYs used in the SpacemiT K1 SoC. + config PHY_SPACEMIT_K1_USB2 tristate "SpacemiT K1 USB 2.0 PHY support" depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile index fec0b425a948..a821a21d6142 100644 --- a/drivers/phy/spacemit/Makefile +++ b/drivers/phy/spacemit/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE) += phy-k1-pcie.o obj-$(CONFIG_PHY_SPACEMIT_K1_USB2) += phy-k1-usb2.o diff --git a/drivers/phy/phy-spacemit-k1-pcie.c b/drivers/phy/spacemit/phy-k1-pcie.c similarity index 100% rename from drivers/phy/phy-spacemit-k1-pcie.c rename to drivers/phy/spacemit/phy-k1-pcie.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index afecd9407f53..03f2e3ee065f 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -77,7 +77,6 @@ config PINCTRL_APPLE_GPIO select GPIOLIB_IRQCHIP select GENERIC_PINCTRL_GROUPS select GENERIC_PINMUX_FUNCTIONS - select OF_GPIO help This is the driver for the GPIO controller found on Apple ARM SoCs, including M1. @@ -126,7 +125,6 @@ config PINCTRL_AT91PIO4 select GENERIC_PINCONF select GPIOLIB select GPIOLIB_IRQCHIP - select OF_GPIO help Say Y here to enable the at91 pinctrl/gpio driver for Atmel PIO4 controller available on sama5d2 SoC. @@ -254,11 +252,11 @@ config PINCTRL_EQUILIBRIUM config PINCTRL_EYEQ5 bool "Mobileye EyeQ5 pinctrl driver" depends on OF - depends on MACH_EYEQ5 || COMPILE_TEST + depends on MACH_EYEQ5 || MACH_EYEQ6LPLUS || COMPILE_TEST select PINMUX select GENERIC_PINCONF select AUXILIARY_BUS - default MACH_EYEQ5 + default MACH_EYEQ5 || MACH_EYEQ6LPLUS help Pin controller driver for the Mobileye EyeQ5 platform. It does both pin config & pin muxing. It does not handle GPIO. @@ -293,7 +291,6 @@ config PINCTRL_K210 select GENERIC_PINMUX_FUNCTIONS select GENERIC_PINCONF select GPIOLIB - select OF_GPIO select REGMAP_MMIO default SOC_CANAAN_K210 help @@ -419,7 +416,6 @@ config PINCTRL_MICROCHIP_SGPIO select GENERIC_PINCONF select GENERIC_PINCTRL_GROUPS select GENERIC_PINMUX_FUNCTIONS - select OF_GPIO help Support for the serial GPIO interface used on Microsemi and Microchip SoCs. By using a serial interface, the SIO @@ -441,7 +437,6 @@ config PINCTRL_OCELOT select GENERIC_PINCONF select GENERIC_PINCTRL_GROUPS select GENERIC_PINMUX_FUNCTIONS - select OF_GPIO select REGMAP_MMIO help Support for the internal GPIO interfaces on Microsemi Ocelot and @@ -478,11 +473,10 @@ config PINCTRL_PEF2256 config PINCTRL_PIC32 bool "Microchip PIC32 pin controller driver" depends on OF - depends on MACH_PIC32 + depends on MACH_PIC32 || COMPILE_TEST select PINMUX select GENERIC_PINCONF select GPIOLIB_IRQCHIP - select OF_GPIO help This is the pin controller and gpio driver for Microchip PIC32 microcontrollers. This option is selected automatically when specific @@ -499,7 +493,6 @@ config PINCTRL_PISTACHIO select PINMUX select GENERIC_PINCONF select GPIOLIB_IRQCHIP - select OF_GPIO help This support pinctrl and GPIO driver for IMG Pistachio SoC. @@ -521,7 +514,6 @@ config PINCTRL_ROCKCHIP select GENERIC_PINCONF select GENERIC_IRQ_CHIP select MFD_SYSCON - select OF_GPIO default ARCH_ROCKCHIP help This support pinctrl and GPIO driver for Rockchip SoCs. @@ -557,7 +549,6 @@ config PINCTRL_ST config PINCTRL_STMFX tristate "STMicroelectronics STMFX GPIO expander pinctrl driver" depends on I2C - depends on OF_GPIO depends on HAS_IOMEM select GENERIC_PINCONF select GPIOLIB_IRQCHIP diff --git a/drivers/pinctrl/bcm/Kconfig b/drivers/pinctrl/bcm/Kconfig index 096d0778427e..206f3f1249cf 100644 --- a/drivers/pinctrl/bcm/Kconfig +++ b/drivers/pinctrl/bcm/Kconfig @@ -120,7 +120,7 @@ source "drivers/pinctrl/bcm/Kconfig.stb" config PINCTRL_IPROC_GPIO bool "Broadcom iProc GPIO (with PINCONF) driver" - depends on OF_GPIO && (ARCH_BCM_IPROC || COMPILE_TEST) + depends on ARCH_BCM_IPROC || COMPILE_TEST select GPIOLIB_IRQCHIP select PINCONF select GENERIC_PINCONF @@ -185,7 +185,7 @@ config PINCTRL_NS config PINCTRL_NSP_GPIO bool "Broadcom NSP GPIO (with PINCONF) driver" - depends on OF_GPIO && (ARCH_BCM_NSP || COMPILE_TEST) + depends on ARCH_BCM_NSP || COMPILE_TEST select GPIOLIB_IRQCHIP select PINCONF select GENERIC_PINCONF diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index da0a07742460..6cbcaa6709da 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -1381,7 +1381,8 @@ unapply_mux_setting: goto restore_old_state; unapply_new_state: - dev_err(p->dev, "Error applying setting, reverse things back\n"); + dev_err_probe(p->dev, ret, + "Error applying setting, reverse things back\n"); /* * All we can do here is pinmux_disable_setting. @@ -2022,7 +2023,7 @@ static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) device_root = debugfs_create_dir(debugfs_name, debugfs_root); pctldev->device_root = device_root; - if (IS_ERR(device_root) || !device_root) { + if (IS_ERR_OR_NULL(device_root)) { pr_warn("failed to create debugfs directory for %s\n", dev_name(pctldev->dev)); return; diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index d0c3e26409a8..02a271dd292f 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -175,7 +175,7 @@ static int dt_to_map_one_config(struct pinctrl *p, * return. */ dev_info(p->dev, - "there is not valid maps for state %s\n", statename); + "there are no valid maps for state %s\n", statename); return 0; } diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig index 8d24decd3f07..fd53cf5bb843 100644 --- a/drivers/pinctrl/freescale/Kconfig +++ b/drivers/pinctrl/freescale/Kconfig @@ -9,7 +9,7 @@ config PINCTRL_IMX config PINCTRL_IMX_SCMI tristate "i.MX95 pinctrl driver using SCMI protocol interface" - depends on ARM_SCMI_PROTOCOL && OF + depends on (ARM_SCMI_PROTOCOL && OF && ARCH_MXC) || COMPILE_TEST select PINMUX select GENERIC_PINCONF select GENERIC_PINCTRL_GROUPS diff --git a/drivers/pinctrl/meson/Kconfig b/drivers/pinctrl/meson/Kconfig index ce6aeec2fc79..1af4fc320f42 100644 --- a/drivers/pinctrl/meson/Kconfig +++ b/drivers/pinctrl/meson/Kconfig @@ -8,7 +8,6 @@ menuconfig PINCTRL_MESON select PINCONF select GENERIC_PINCONF select GPIOLIB - select OF_GPIO select REGMAP_MMIO if PINCTRL_MESON diff --git a/drivers/pinctrl/microchip/pinctrl-mpfs-mssio.c b/drivers/pinctrl/microchip/pinctrl-mpfs-mssio.c index 3d5ffd6cb14b..15d73ea1028c 100644 --- a/drivers/pinctrl/microchip/pinctrl-mpfs-mssio.c +++ b/drivers/pinctrl/microchip/pinctrl-mpfs-mssio.c @@ -686,7 +686,7 @@ static int mpfs_pinctrl_probe(struct platform_device *pdev) pctrl->regmap = device_node_to_regmap(pdev->dev.parent->of_node); if (IS_ERR(pctrl->regmap)) - dev_err_probe(dev, PTR_ERR(pctrl->regmap), "Failed to find syscon regmap\n"); + return dev_err_probe(dev, PTR_ERR(pctrl->regmap), "Failed to find syscon regmap\n"); pctrl->sysreg_regmap = syscon_regmap_lookup_by_compatible("microchip,mpfs-sysreg-scb"); if (IS_ERR(pctrl->sysreg_regmap)) diff --git a/drivers/pinctrl/nomadik/pinctrl-abx500.c b/drivers/pinctrl/nomadik/pinctrl-abx500.c index fc7ebeda8440..858fbaebcf8e 100644 --- a/drivers/pinctrl/nomadik/pinctrl-abx500.c +++ b/drivers/pinctrl/nomadik/pinctrl-abx500.c @@ -852,7 +852,7 @@ static int abx500_pin_config_set(struct pinctrl_dev *pctldev, int ret = -EINVAL; int i; enum pin_config_param param; - enum pin_config_param argument; + unsigned int argument; for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); diff --git a/drivers/pinctrl/nxp/pinctrl-s32.h b/drivers/pinctrl/nxp/pinctrl-s32.h index add3c77ddfed..8715befd5f05 100644 --- a/drivers/pinctrl/nxp/pinctrl-s32.h +++ b/drivers/pinctrl/nxp/pinctrl-s32.h @@ -16,7 +16,7 @@ struct platform_device; /** * struct s32_pin_group - describes an S32 pin group * @data: generic data describes group name, number of pins, and a pin array in - this group. + * this group. * @pin_sss: an array of source signal select configs paired with pin array. */ struct s32_pin_group { diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index 2b030bd0e6ad..64ed28309788 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,7 @@ static const struct pin_config_item conf_items[] = { PCONFDUMP(PIN_CONFIG_SKEW_DELAY, "skew delay", NULL, true), PCONFDUMP(PIN_CONFIG_SKEW_DELAY_INPUT_PS, "input skew delay", "ps", true), PCONFDUMP(PIN_CONFIG_SKEW_DELAY_OUTPUT_PS, "output skew delay", "ps", true), + PCONFDUMP(PIN_CONFIG_INPUT_VOLTAGE_UV, "input voltage in microvolt", "uV", true), }; static void pinconf_generic_dump_one(struct pinctrl_dev *pctldev, @@ -202,27 +204,31 @@ static const struct pinconf_generic_params dt_params[] = { { "skew-delay", PIN_CONFIG_SKEW_DELAY, 0 }, { "skew-delay-input-ps", PIN_CONFIG_SKEW_DELAY_INPUT_PS, 0 }, { "skew-delay-output-ps", PIN_CONFIG_SKEW_DELAY_OUTPUT_PS, 0 }, + { "input-threshold-voltage-microvolt", PIN_CONFIG_INPUT_VOLTAGE_UV, 0 }, }; /** - * parse_dt_cfg() - Parse DT pinconf parameters - * @np: DT node + * parse_fw_cfg() - Parse firmware pinconf parameters + * @fwnode: firmware node * @params: Array of describing generic parameters * @count: Number of entries in @params * @cfg: Array of parsed config options * @ncfg: Number of entries in @cfg * - * Parse the config options described in @params from @np and puts the result + * Parse the config options described in @params from @fwnode and puts the result * in @cfg. @cfg does not need to be empty, entries are added beginning at * @ncfg. @ncfg is updated to reflect the number of entries after parsing. @cfg * needs to have enough memory allocated to hold all possible entries. */ -static int parse_dt_cfg(struct device_node *np, +static int parse_fw_cfg(struct fwnode_handle *fwnode, const struct pinconf_generic_params *params, unsigned int count, unsigned long *cfg, unsigned int *ncfg) { - int i; + unsigned long *properties; + int i, test; + + properties = bitmap_zalloc(count, GFP_KERNEL); for (i = 0; i < count; i++) { u32 val; @@ -230,7 +236,7 @@ static int parse_dt_cfg(struct device_node *np, const struct pinconf_generic_params *par = ¶ms[i]; if (par->values && par->num_values) { - ret = fwnode_property_match_property_string(of_fwnode_handle(np), + ret = fwnode_property_match_property_string(fwnode, par->property, par->values, par->num_values); if (ret == -ENOENT) @@ -240,7 +246,7 @@ static int parse_dt_cfg(struct device_node *np, ret = 0; } } else { - ret = of_property_read_u32(np, par->property, &val); + ret = fwnode_property_read_u32(fwnode, par->property, &val); } /* property not found */ @@ -251,11 +257,45 @@ static int parse_dt_cfg(struct device_node *np, if (ret) val = par->default_value; + /* if param is greater than count, these are custom properties */ + if (par->param <= count) { + ret = test_and_set_bit(par->param, properties); + if (ret) { + pr_err("%pfw: conflicting setting detected for %s\n", + fwnode, par->property); + bitmap_free(properties); + return -EINVAL; + } + } + pr_debug("found %s with value %u\n", par->property, val); cfg[*ncfg] = pinconf_to_config_packed(par->param, val); (*ncfg)++; } + if (test_bit(PIN_CONFIG_DRIVE_STRENGTH, properties) && + test_bit(PIN_CONFIG_DRIVE_STRENGTH_UA, properties)) + pr_err("%pfw: cannot have multiple drive strength properties\n", + fwnode); + + test = test_bit(PIN_CONFIG_BIAS_BUS_HOLD, properties) + + test_bit(PIN_CONFIG_BIAS_DISABLE, properties) + + test_bit(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, properties) + + test_bit(PIN_CONFIG_BIAS_PULL_UP, properties) + + test_bit(PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, properties) + + test_bit(PIN_CONFIG_BIAS_PULL_DOWN, properties); + if (test > 1) + pr_err("%pfw: cannot have multiple bias configurations\n", + fwnode); + + test = test_bit(PIN_CONFIG_DRIVE_OPEN_DRAIN, properties) + + test_bit(PIN_CONFIG_DRIVE_OPEN_SOURCE, properties) + + test_bit(PIN_CONFIG_DRIVE_PUSH_PULL, properties); + if (test > 1) + pr_err("%pfw: cannot have multiple drive configurations\n", + fwnode); + + bitmap_free(properties); return 0; } @@ -274,17 +314,22 @@ int pinconf_generic_parse_dt_pinmux(struct device_node *np, struct device *dev, unsigned int **pid, unsigned int **pmux, unsigned int *npins) { + struct fwnode_handle *fwnode = of_fwnode_handle(np); unsigned int *pid_t; unsigned int *pmux_t; - struct property *prop; unsigned int npins_t, i; - u32 value; int ret; - prop = of_find_property(np, "pinmux", NULL); - if (!prop) { + ret = fwnode_property_count_u32(fwnode, "pinmux"); + if (ret < 0) { dev_info(dev, "Missing pinmux property\n"); - return -ENOENT; + return ret; + } + + npins_t = ret; + if (npins_t == 0) { + dev_info(dev, "pinmux property doesn't have entries\n"); + return -ENODATA; } if (!pid || !pmux || !npins) { @@ -292,21 +337,22 @@ int pinconf_generic_parse_dt_pinmux(struct device_node *np, struct device *dev, return -EINVAL; } - npins_t = prop->length / sizeof(u32); pid_t = devm_kcalloc(dev, npins_t, sizeof(*pid_t), GFP_KERNEL); pmux_t = devm_kcalloc(dev, npins_t, sizeof(*pmux_t), GFP_KERNEL); if (!pid_t || !pmux_t) { dev_err(dev, "kalloc memory fail\n"); return -ENOMEM; } + + ret = fwnode_property_read_u32_array(fwnode, "pinmux", pmux_t, npins_t); + if (ret) { + dev_err(dev, "get pinmux value fail\n"); + goto exit; + } + for (i = 0; i < npins_t; i++) { - ret = of_property_read_u32_index(np, "pinmux", i, &value); - if (ret) { - dev_err(dev, "get pinmux value fail\n"); - goto exit; - } - pmux_t[i] = value & 0xff; - pid_t[i] = (value >> 8) & 0xffffff; + pid_t[i] = pmux_t[i] >> 8; + pmux_t[i] = pmux_t[i] & 0xff; } *pid = pid_t; *pmux = pmux_t; @@ -336,9 +382,11 @@ int pinconf_generic_parse_dt_config(struct device_node *np, { unsigned long *cfg; unsigned int max_cfg, ncfg = 0; + struct fwnode_handle *fwnode; int ret; - if (!np) + fwnode = of_fwnode_handle(np); + if (!fwnode) return -EINVAL; /* allocate a temporary array big enough to hold one of each option */ @@ -349,12 +397,12 @@ int pinconf_generic_parse_dt_config(struct device_node *np, if (!cfg) return -ENOMEM; - ret = parse_dt_cfg(np, dt_params, ARRAY_SIZE(dt_params), cfg, &ncfg); + ret = parse_fw_cfg(fwnode, dt_params, ARRAY_SIZE(dt_params), cfg, &ncfg); if (ret) goto out; if (pctldev && pctldev->desc->num_custom_params && pctldev->desc->custom_params) { - ret = parse_dt_cfg(np, pctldev->desc->custom_params, + ret = parse_fw_cfg(fwnode, pctldev->desc->custom_params, pctldev->desc->num_custom_params, cfg, &ncfg); if (ret) goto out; diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index 2af94ef56434..e3128b0045d2 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -1274,6 +1274,7 @@ static const struct acpi_device_id amd_gpio_acpi_match[] = { { "AMD0030", 0 }, { "AMDI0030", 0}, { "AMDI0031", 0}, + { "AMDI0033", 0}, { }, }; MODULE_DEVICE_TABLE(acpi, amd_gpio_acpi_match); diff --git a/drivers/pinctrl/pinctrl-cy8c95x0.c b/drivers/pinctrl/pinctrl-cy8c95x0.c index 5c055d344ac9..ace0be5ec679 100644 --- a/drivers/pinctrl/pinctrl-cy8c95x0.c +++ b/drivers/pinctrl/pinctrl-cy8c95x0.c @@ -72,24 +72,6 @@ #define CY8C95X0_MUX_REGMAP_TO_OFFSET(x, p) \ (CY8C95X0_VIRTUAL + (x) - CY8C95X0_PORTSEL + (p) * MUXED_STRIDE) -static const struct i2c_device_id cy8c95x0_id[] = { - { "cy8c9520", 20, }, - { "cy8c9540", 40, }, - { "cy8c9560", 60, }, - { } -}; -MODULE_DEVICE_TABLE(i2c, cy8c95x0_id); - -#define OF_CY8C95X(__nrgpio) ((void *)(__nrgpio)) - -static const struct of_device_id cy8c95x0_dt_ids[] = { - { .compatible = "cypress,cy8c9520", .data = OF_CY8C95X(20), }, - { .compatible = "cypress,cy8c9540", .data = OF_CY8C95X(40), }, - { .compatible = "cypress,cy8c9560", .data = OF_CY8C95X(60), }, - { } -}; -MODULE_DEVICE_TABLE(of, cy8c95x0_dt_ids); - static const struct acpi_gpio_params cy8c95x0_irq_gpios = { 0, 0, true }; static const struct acpi_gpio_mapping cy8c95x0_acpi_irq_gpios[] = { @@ -144,11 +126,9 @@ static const struct dmi_system_id cy8c95x0_dmi_acpi_irq_info[] = { * @map: Mask used to compensate for Gport2 width * @nport: Number of Gports in this chip * @gpio_chip: gpiolib chip - * @driver_data: private driver data * @dev: struct device * @pctldev: pin controller device * @pinctrl_desc: pin controller description - * @name: Chip controller name * @tpin: Total number of pins * @gpio_reset: GPIO line handler that can reset the IC */ @@ -165,11 +145,9 @@ struct cy8c95x0_pinctrl { DECLARE_BITMAP(map, MAX_LINE); unsigned int nport; struct gpio_chip gpio_chip; - unsigned long driver_data; struct device *dev; struct pinctrl_dev *pctldev; struct pinctrl_desc pinctrl_desc; - char name[32]; unsigned int tpin; struct gpio_desc *gpio_reset; }; @@ -1310,18 +1288,19 @@ static int cy8c95x0_irq_setup(struct cy8c95x0_pinctrl *chip, int irq) { struct gpio_irq_chip *girq = &chip->gpio_chip.irq; DECLARE_BITMAP(pending_irqs, MAX_LINE); + struct device *dev = chip->dev; int ret; - mutex_init(&chip->irq_lock); + ret = devm_mutex_init(chip->dev, &chip->irq_lock); + if (ret) + return ret; bitmap_zero(pending_irqs, MAX_LINE); /* Read IRQ status register to clear all pending interrupts */ ret = cy8c95x0_irq_pending(chip, pending_irqs); - if (ret) { - dev_err(chip->dev, "failed to clear irq status register\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, -EBUSY, "failed to clear irq status register\n"); /* Mask all interrupts */ bitmap_fill(chip->irq_mask, MAX_LINE); @@ -1336,17 +1315,9 @@ static int cy8c95x0_irq_setup(struct cy8c95x0_pinctrl *chip, int irq) girq->handler = handle_simple_irq; girq->threaded = true; - ret = devm_request_threaded_irq(chip->dev, irq, - NULL, cy8c95x0_irq_handler, - IRQF_ONESHOT | IRQF_SHARED, - dev_name(chip->dev), chip); - if (ret) { - dev_err(chip->dev, "failed to request irq %d\n", irq); - return ret; - } - dev_info(chip->dev, "Registered threaded IRQ\n"); - - return 0; + return devm_request_threaded_irq(dev, irq, NULL, cy8c95x0_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + dev_name(chip->dev), chip); } static int cy8c95x0_setup_pinctrl(struct cy8c95x0_pinctrl *chip) @@ -1362,11 +1333,7 @@ static int cy8c95x0_setup_pinctrl(struct cy8c95x0_pinctrl *chip) pd->owner = THIS_MODULE; chip->pctldev = devm_pinctrl_register(chip->dev, pd, chip); - if (IS_ERR(chip->pctldev)) - return dev_err_probe(chip->dev, PTR_ERR(chip->pctldev), - "can't register controller\n"); - - return 0; + return PTR_ERR_OR_ZERO(chip->pctldev); } static int cy8c95x0_detect(struct i2c_client *client, @@ -1384,13 +1351,13 @@ static int cy8c95x0_detect(struct i2c_client *client, return ret; switch (ret & GENMASK(7, 4)) { case 0x20: - name = cy8c95x0_id[0].name; + name = "cy8c9520"; break; case 0x40: - name = cy8c95x0_id[1].name; + name = "cy8c9540"; break; case 0x60: - name = cy8c95x0_id[2].name; + name = "cy8c9560"; break; default: return -ENODEV; @@ -1408,6 +1375,7 @@ static int cy8c95x0_probe(struct i2c_client *client) struct cy8c95x0_pinctrl *chip; struct regmap_config regmap_conf; struct regmap_range_cfg regmap_range_conf; + unsigned long driver_data; int ret; chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); @@ -1417,26 +1385,21 @@ static int cy8c95x0_probe(struct i2c_client *client) chip->dev = dev; /* Set the device type */ - chip->driver_data = (uintptr_t)i2c_get_match_data(client); - if (!chip->driver_data) - return -ENODEV; + driver_data = (unsigned long)i2c_get_match_data(client); - chip->tpin = chip->driver_data & CY8C95X0_GPIO_MASK; + chip->tpin = driver_data & CY8C95X0_GPIO_MASK; chip->nport = DIV_ROUND_UP(CY8C95X0_PIN_TO_OFFSET(chip->tpin), BANK_SZ); memcpy(®map_range_conf, &cy8c95x0_ranges[0], sizeof(regmap_range_conf)); switch (chip->tpin) { case 20: - strscpy(chip->name, cy8c95x0_id[0].name); regmap_range_conf.range_max = CY8C95X0_VIRTUAL + 3 * MUXED_STRIDE - 1; break; case 40: - strscpy(chip->name, cy8c95x0_id[1].name); regmap_range_conf.range_max = CY8C95X0_VIRTUAL + 6 * MUXED_STRIDE - 1; break; case 60: - strscpy(chip->name, cy8c95x0_id[2].name); regmap_range_conf.range_max = CY8C95X0_VIRTUAL + 8 * MUXED_STRIDE - 1; break; default: @@ -1474,7 +1437,9 @@ static int cy8c95x0_probe(struct i2c_client *client) bitmap_fill(chip->map, MAX_LINE); bitmap_clear(chip->map, 20, 4); - mutex_init(&chip->i2c_lock); + ret = devm_mutex_init(dev, &chip->i2c_lock); + if (ret) + return ret; if (dmi_first_match(cy8c95x0_dmi_acpi_irq_info)) { ret = cy8c95x0_acpi_get_irq(&client->dev); @@ -1495,8 +1460,26 @@ static int cy8c95x0_probe(struct i2c_client *client) return cy8c95x0_setup_gpiochip(chip); } +static const struct i2c_device_id cy8c95x0_id[] = { + { "cy8c9520", 20 }, + { "cy8c9540", 40 }, + { "cy8c9560", 60 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cy8c95x0_id); + +#define OF_CY8C95X(__nrgpio) ((void *)(__nrgpio)) + +static const struct of_device_id cy8c95x0_dt_ids[] = { + { .compatible = "cypress,cy8c9520", .data = OF_CY8C95X(20) }, + { .compatible = "cypress,cy8c9540", .data = OF_CY8C95X(40) }, + { .compatible = "cypress,cy8c9560", .data = OF_CY8C95X(60) }, + { } +}; +MODULE_DEVICE_TABLE(of, cy8c95x0_dt_ids); + static const struct acpi_device_id cy8c95x0_acpi_ids[] = { - { "INT3490", 40, }, + { "INT3490", 40 }, { } }; MODULE_DEVICE_TABLE(acpi, cy8c95x0_acpi_ids); diff --git a/drivers/pinctrl/pinctrl-eyeq5.c b/drivers/pinctrl/pinctrl-eyeq5.c index 5f6af934a516..dcdf80f07a90 100644 --- a/drivers/pinctrl/pinctrl-eyeq5.c +++ b/drivers/pinctrl/pinctrl-eyeq5.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -38,18 +39,6 @@ #include "core.h" #include "pinctrl-utils.h" -struct eq5p_pinctrl { - struct pinctrl_desc desc; - void __iomem *base; -}; - -enum eq5p_bank { - EQ5P_BANK_A, - EQ5P_BANK_B, - - EQ5P_BANK_COUNT, -}; - enum eq5p_regs { EQ5P_PD, EQ5P_PU, @@ -60,9 +49,24 @@ enum eq5p_regs { EQ5P_REG_COUNT, }; -static const unsigned int eq5p_regs[EQ5P_BANK_COUNT][EQ5P_REG_COUNT] = { - [EQ5P_BANK_A] = {0x0C0, 0x0C4, 0x0D0, 0x0D4, 0x0B0}, - [EQ5P_BANK_B] = {0x0C8, 0x0CC, 0x0D8, 0x0DC, 0x0B4}, +struct eq5p_bank { + const unsigned int npins; + const unsigned int regs[EQ5P_REG_COUNT]; +}; + +struct eq5p_match_data { + const unsigned int npins; + const unsigned int nfunctions; + const unsigned int nbanks; + const struct pinctrl_pin_desc *pins; + const struct pinfunction *functions; + const struct eq5p_bank *banks; +}; + +struct eq5p_pinctrl { + struct pinctrl_desc desc; + void __iomem *base; + const struct eq5p_match_data *data; }; /* @@ -70,10 +74,18 @@ static const unsigned int eq5p_regs[EQ5P_BANK_COUNT][EQ5P_REG_COUNT] = { */ #define EQ5P_DS_MASK GENMASK(1, 0) +/* + * The GPIO function is always the first function + */ +#define EQ5P_GPIO_FUNC_SELECTOR 0 + +/* Helper to declare pinfunction */ +#define EQ5P_PINFUNCTION(func, groups) PINCTRL_PINFUNCTION(func, groups, ARRAY_SIZE(groups)) + /* * Comments to the right of each pin are the "signal name" in the datasheet. */ -static const struct pinctrl_pin_desc eq5p_pins[] = { +static const struct pinctrl_pin_desc eq5p_eyeq5_pins[] = { /* Bank A */ PINCTRL_PIN(0, "PA0"), /* A0_TIMER0_CK */ PINCTRL_PIN(1, "PA1"), /* A1_TIMER0_EOC */ @@ -105,35 +117,35 @@ static const struct pinctrl_pin_desc eq5p_pins[] = { PINCTRL_PIN(27, "PA27"), /* A27_SPI_1_CS1 */ PINCTRL_PIN(28, "PA28"), /* A28_REF_CLK0 */ -#define EQ5P_PIN_OFFSET_BANK_B 29 +#define EQ5P_EYEQ5_PIN_OFFSET_BANK_B 29 /* Bank B */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 0, "PB0"), /* B0_TIMER3_CK */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 1, "PB1"), /* B1_TIMER3_EOC */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 2, "PB2"), /* B2_TIMER4_CK */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 3, "PB3"), /* B3_TIMER4_EOC */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 4, "PB4"), /* B4_TIMER6_EXT_INCAP1 */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 5, "PB5"), /* B5_TIMER6_EXT_INCAP2 */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 6, "PB6"), /* B6_TIMER6_EXT_OUTCMP1 */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 7, "PB7"), /* B7_TIMER6_EXT_OUTCMP2 */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 8, "PB8"), /* B8_UART_2_TX */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 9, "PB9"), /* B9_UART_2_RX */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 10, "PB10"), /* B10_CAN_2_TX */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 11, "PB11"), /* B11_CAN_2_RX */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 12, "PB12"), /* B12_SPI_2_DO */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 13, "PB13"), /* B13_SPI_2_DI */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 14, "PB14"), /* B14_SPI_2_CK */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 15, "PB15"), /* B15_SPI_2_CS0 */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 16, "PB16"), /* B16_SPI_2_CS1 */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 17, "PB17"), /* B17_SPI_3_DO */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 18, "PB18"), /* B18_SPI_3_DI */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 19, "PB19"), /* B19_SPI_3_CK */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 20, "PB20"), /* B20_SPI_3_CS0 */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 21, "PB21"), /* B21_SPI_3_CS1 */ - PINCTRL_PIN(EQ5P_PIN_OFFSET_BANK_B + 22, "PB22"), /* B22_MCLK0 */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 0, "PB0"), /* B0_TIMER3_CK */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 1, "PB1"), /* B1_TIMER3_EOC */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 2, "PB2"), /* B2_TIMER4_CK */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 3, "PB3"), /* B3_TIMER4_EOC */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 4, "PB4"), /* B4_TIMER6_EXT_INCAP1 */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 5, "PB5"), /* B5_TIMER6_EXT_INCAP2 */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 6, "PB6"), /* B6_TIMER6_EXT_OUTCMP1 */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 7, "PB7"), /* B7_TIMER6_EXT_OUTCMP2 */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 8, "PB8"), /* B8_UART_2_TX */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 9, "PB9"), /* B9_UART_2_RX */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 10, "PB10"), /* B10_CAN_2_TX */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 11, "PB11"), /* B11_CAN_2_RX */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 12, "PB12"), /* B12_SPI_2_DO */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 13, "PB13"), /* B13_SPI_2_DI */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 14, "PB14"), /* B14_SPI_2_CK */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 15, "PB15"), /* B15_SPI_2_CS0 */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 16, "PB16"), /* B16_SPI_2_CS1 */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 17, "PB17"), /* B17_SPI_3_DO */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 18, "PB18"), /* B18_SPI_3_DI */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 19, "PB19"), /* B19_SPI_3_CK */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 20, "PB20"), /* B20_SPI_3_CS0 */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 21, "PB21"), /* B21_SPI_3_CS1 */ + PINCTRL_PIN(EQ5P_EYEQ5_PIN_OFFSET_BANK_B + 22, "PB22"), /* B22_MCLK0 */ }; -static const char * const gpio_groups[] = { +static const char * const eq5p_eyeq5_gpio_groups[] = { /* Bank A */ "PA0", "PA1", "PA2", "PA3", "PA4", "PA5", "PA6", "PA7", "PA8", "PA9", "PA10", "PA11", "PA12", "PA13", "PA14", "PA15", @@ -147,70 +159,184 @@ static const char * const gpio_groups[] = { }; /* Groups of functions on bank A */ -static const char * const timer0_groups[] = { "PA0", "PA1" }; -static const char * const timer1_groups[] = { "PA2", "PA3" }; -static const char * const timer2_groups[] = { "PA4", "PA5" }; -static const char * const timer5_groups[] = { "PA6", "PA7", "PA8", "PA9" }; -static const char * const uart0_groups[] = { "PA10", "PA11" }; -static const char * const uart1_groups[] = { "PA12", "PA13" }; -static const char * const can0_groups[] = { "PA14", "PA15" }; -static const char * const can1_groups[] = { "PA16", "PA17" }; -static const char * const spi0_groups[] = { "PA18", "PA19", "PA20", "PA21", "PA22" }; -static const char * const spi1_groups[] = { "PA23", "PA24", "PA25", "PA26", "PA27" }; -static const char * const refclk0_groups[] = { "PA28" }; +static const char * const eq5p_eyeq5_timer0_groups[] = { "PA0", "PA1" }; +static const char * const eq5p_eyeq5_timer1_groups[] = { "PA2", "PA3" }; +static const char * const eq5p_eyeq5_timer2_groups[] = { "PA4", "PA5" }; +static const char * const eq5p_eyeq5_timer5_groups[] = { "PA6", "PA7", "PA8", "PA9" }; +static const char * const eq5p_eyeq5_uart0_groups[] = { "PA10", "PA11" }; +static const char * const eq5p_eyeq5_uart1_groups[] = { "PA12", "PA13" }; +static const char * const eq5p_eyeq5_can0_groups[] = { "PA14", "PA15" }; +static const char * const eq5p_eyeq5_can1_groups[] = { "PA16", "PA17" }; +static const char * const eq5p_eyeq5_spi0_groups[] = { "PA18", "PA19", "PA20", "PA21", "PA22" }; +static const char * const eq5p_eyeq5_spi1_groups[] = { "PA23", "PA24", "PA25", "PA26", "PA27" }; +static const char * const eq5p_eyeq5_refclk0_groups[] = { "PA28" }; /* Groups of functions on bank B */ -static const char * const timer3_groups[] = { "PB0", "PB1" }; -static const char * const timer4_groups[] = { "PB2", "PB3" }; -static const char * const timer6_groups[] = { "PB4", "PB5", "PB6", "PB7" }; -static const char * const uart2_groups[] = { "PB8", "PB9" }; -static const char * const can2_groups[] = { "PB10", "PB11" }; -static const char * const spi2_groups[] = { "PB12", "PB13", "PB14", "PB15", "PB16" }; -static const char * const spi3_groups[] = { "PB17", "PB18", "PB19", "PB20", "PB21" }; -static const char * const mclk0_groups[] = { "PB22" }; +static const char * const eq5p_eyeq5_timer3_groups[] = { "PB0", "PB1" }; +static const char * const eq5p_eyeq5_timer4_groups[] = { "PB2", "PB3" }; +static const char * const eq5p_eyeq5_timer6_groups[] = { "PB4", "PB5", "PB6", "PB7" }; +static const char * const eq5p_eyeq5_uart2_groups[] = { "PB8", "PB9" }; +static const char * const eq5p_eyeq5_can2_groups[] = { "PB10", "PB11" }; +static const char * const eq5p_eyeq5_spi2_groups[] = { "PB12", "PB13", "PB14", "PB15", "PB16" }; +static const char * const eq5p_eyeq5_spi3_groups[] = { "PB17", "PB18", "PB19", "PB20", "PB21" }; +static const char * const eq5p_eyeq5_mclk0_groups[] = { "PB22" }; -static const struct pinfunction eq5p_functions[] = { - /* GPIO having a fixed index is depended upon, see GPIO_FUNC_SELECTOR. */ - PINCTRL_PINFUNCTION("gpio", gpio_groups, ARRAY_SIZE(gpio_groups)), -#define GPIO_FUNC_SELECTOR 0 +static const struct pinfunction eq5p_eyeq5_functions[] = { + /* GPIO having a fixed index is depended upon, see EQ5P_GPIO_FUNC_SELECTOR. */ + EQ5P_PINFUNCTION("gpio", eq5p_eyeq5_gpio_groups), /* Bank A functions */ - PINCTRL_PINFUNCTION("timer0", timer0_groups, ARRAY_SIZE(timer0_groups)), - PINCTRL_PINFUNCTION("timer1", timer1_groups, ARRAY_SIZE(timer1_groups)), - PINCTRL_PINFUNCTION("timer2", timer2_groups, ARRAY_SIZE(timer2_groups)), - PINCTRL_PINFUNCTION("timer5", timer5_groups, ARRAY_SIZE(timer5_groups)), - PINCTRL_PINFUNCTION("uart0", uart0_groups, ARRAY_SIZE(uart0_groups)), - PINCTRL_PINFUNCTION("uart1", uart1_groups, ARRAY_SIZE(uart1_groups)), - PINCTRL_PINFUNCTION("can0", can0_groups, ARRAY_SIZE(can0_groups)), - PINCTRL_PINFUNCTION("can1", can1_groups, ARRAY_SIZE(can1_groups)), - PINCTRL_PINFUNCTION("spi0", spi0_groups, ARRAY_SIZE(spi0_groups)), - PINCTRL_PINFUNCTION("spi1", spi1_groups, ARRAY_SIZE(spi1_groups)), - PINCTRL_PINFUNCTION("refclk0", refclk0_groups, ARRAY_SIZE(refclk0_groups)), + EQ5P_PINFUNCTION("timer0", eq5p_eyeq5_timer0_groups), + EQ5P_PINFUNCTION("timer1", eq5p_eyeq5_timer1_groups), + EQ5P_PINFUNCTION("timer2", eq5p_eyeq5_timer2_groups), + EQ5P_PINFUNCTION("timer5", eq5p_eyeq5_timer5_groups), + EQ5P_PINFUNCTION("uart0", eq5p_eyeq5_uart0_groups), + EQ5P_PINFUNCTION("uart1", eq5p_eyeq5_uart1_groups), + EQ5P_PINFUNCTION("can0", eq5p_eyeq5_can0_groups), + EQ5P_PINFUNCTION("can1", eq5p_eyeq5_can1_groups), + EQ5P_PINFUNCTION("spi0", eq5p_eyeq5_spi0_groups), + EQ5P_PINFUNCTION("spi1", eq5p_eyeq5_spi1_groups), + EQ5P_PINFUNCTION("refclk0", eq5p_eyeq5_refclk0_groups), /* Bank B functions */ - PINCTRL_PINFUNCTION("timer3", timer3_groups, ARRAY_SIZE(timer3_groups)), - PINCTRL_PINFUNCTION("timer4", timer4_groups, ARRAY_SIZE(timer4_groups)), - PINCTRL_PINFUNCTION("timer6", timer6_groups, ARRAY_SIZE(timer6_groups)), - PINCTRL_PINFUNCTION("uart2", uart2_groups, ARRAY_SIZE(uart2_groups)), - PINCTRL_PINFUNCTION("can2", can2_groups, ARRAY_SIZE(can2_groups)), - PINCTRL_PINFUNCTION("spi2", spi2_groups, ARRAY_SIZE(spi2_groups)), - PINCTRL_PINFUNCTION("spi3", spi3_groups, ARRAY_SIZE(spi3_groups)), - PINCTRL_PINFUNCTION("mclk0", mclk0_groups, ARRAY_SIZE(mclk0_groups)), + EQ5P_PINFUNCTION("timer3", eq5p_eyeq5_timer3_groups), + EQ5P_PINFUNCTION("timer4", eq5p_eyeq5_timer4_groups), + EQ5P_PINFUNCTION("timer6", eq5p_eyeq5_timer6_groups), + EQ5P_PINFUNCTION("uart2", eq5p_eyeq5_uart2_groups), + EQ5P_PINFUNCTION("can2", eq5p_eyeq5_can2_groups), + EQ5P_PINFUNCTION("spi2", eq5p_eyeq5_spi2_groups), + EQ5P_PINFUNCTION("spi3", eq5p_eyeq5_spi3_groups), + EQ5P_PINFUNCTION("mclk0", eq5p_eyeq5_mclk0_groups), +}; + +static const struct eq5p_bank eq5p_eyeq5_banks[] = { + { + .npins = EQ5P_EYEQ5_PIN_OFFSET_BANK_B, + .regs = {0x0C0, 0x0C4, 0x0D0, 0x0D4, 0x0B0}, + }, + { + .npins = ARRAY_SIZE(eq5p_eyeq5_pins) - EQ5P_EYEQ5_PIN_OFFSET_BANK_B, + .regs = {0x0C8, 0x0CC, 0x0D8, 0x0DC, 0x0B4}, + }, +}; + +static const struct eq5p_match_data eq5p_eyeq5_data = { + .npins = ARRAY_SIZE(eq5p_eyeq5_pins), + .nfunctions = ARRAY_SIZE(eq5p_eyeq5_functions), + .nbanks = ARRAY_SIZE(eq5p_eyeq5_banks), + .pins = eq5p_eyeq5_pins, + .functions = eq5p_eyeq5_functions, + .banks = eq5p_eyeq5_banks, +}; + +static const struct pinctrl_pin_desc eq5p_eyeq6lplus_pins[] = { + PINCTRL_PIN(0, "PA0"), /* GPIO_A0_TIMER0_CK0 */ + PINCTRL_PIN(1, "PA1"), /* GPIO_A1_TIMER0_EOC */ + PINCTRL_PIN(2, "PA2"), /* GPIO_A2_TIMER1_CK */ + PINCTRL_PIN(3, "PA3"), /* GPIO_A3_TIMER1_EOC1 */ + PINCTRL_PIN(4, "PA4"), /* GPIO_A4_SSI_UART_RX */ + PINCTRL_PIN(5, "PA5"), /* GPIO_A5_SSI_UART_TX */ + PINCTRL_PIN(6, "PA6"), /* GPIO_A6_SPI_0_CS */ + PINCTRL_PIN(7, "PA7"), /* GPIO_A7_SPI_0_DI */ + PINCTRL_PIN(8, "PA8"), /* GPIO_A8_SPI_0_CK */ + PINCTRL_PIN(9, "PA9"), /* GPIO_A9_SPI_0_DO */ + PINCTRL_PIN(10, "PA10"), /* GPIO_A10_SPI_0_CS1 */ + PINCTRL_PIN(11, "PA11"), /* GPIO_A11_UART_0_RX */ + PINCTRL_PIN(12, "PA12"), /* GPIO_A12_UART_0_TX */ + PINCTRL_PIN(13, "PA13"), /* GPIO_A13_TIMER2_CK */ + PINCTRL_PIN(14, "PA14"), /* GPIO_A14_TIMER2_EOC */ + PINCTRL_PIN(15, "PA15"), /* GPIO_A15_TIMER3_CK */ + PINCTRL_PIN(16, "PA16"), /* GPIO_A16_TIMER_EOC */ + PINCTRL_PIN(17, "PA17"), /* GPIO_A17_TIMER_EXT0_INCA P1 */ + PINCTRL_PIN(18, "PA18"), /* GPIO_A18_TIMER_EXT0_INCA P2 */ + PINCTRL_PIN(19, "PA19"), /* GPIO_A19_TIMER_EXT0_OUT CMP1 */ + PINCTRL_PIN(20, "PA20"), /* GPIO_A20_TIMER_EXT0_OUT CMP2 */ + PINCTRL_PIN(21, "PA21"), /* GPIO_A21_SPI_1_CS0 */ + PINCTRL_PIN(22, "PA22"), /* GPIO_A22_SPI_1_DI */ + PINCTRL_PIN(23, "PA23"), /* GPIO_A23_SPI_1_CK */ + PINCTRL_PIN(24, "PA24"), /* GPIO_A24_SPI_1_DO */ + PINCTRL_PIN(25, "PA25"), /* GPIO_A25_SPI_1_CS1 */ + PINCTRL_PIN(26, "PA26"), /* GPIO_A26_TIMER_EXT1_INCA P1 */ + PINCTRL_PIN(27, "PA27"), /* GPIO_A27_TIMER_EXT1_INCA P2 */ + PINCTRL_PIN(28, "PA28"), /* GPIO_A28_TIMER_EXT1_OUTC MP1 */ + PINCTRL_PIN(29, "PA29"), /* GPIO_A29_TIMER_EXT1_OUTC MP2 */ + PINCTRL_PIN(30, "PA30"), /* GPIO_A30_EXT_CLK */ + PINCTRL_PIN(31, "PA31"), /* GPIO_A31_VDI_MCLK */ +}; + +static const char * const eq5p_eyeq6lplus_gpio_groups[] = { + /* Bank A */ + "PA0", "PA1", "PA2", "PA3", "PA4", "PA5", "PA6", "PA7", + "PA8", "PA9", "PA10", "PA11", "PA12", "PA13", "PA14", "PA15", + "PA16", "PA17", "PA18", "PA19", "PA20", "PA21", "PA22", "PA23", + "PA24", "PA25", "PA26", "PA27", "PA28", "PA29", "PA30", "PA31", +}; + +/* Groups of functions on bank A */ +static const char * const eq5p_eyeq6lplus_timer0_groups[] = { "PA0", "PA1" }; +static const char * const eq5p_eyeq6lplus_timer1_groups[] = { "PA2", "PA3" }; +static const char * const eq5p_eyeq6lplus_uart_ssi_groups[] = { "PA4", "PA5" }; +static const char * const eq5p_eyeq6lplus_spi0_groups[] = { "PA6", "PA7", "PA8", "PA9", "PA10" }; +static const char * const eq5p_eyeq6lplus_uart0_groups[] = { "PA11", "PA12" }; +static const char * const eq5p_eyeq6lplus_timer2_groups[] = { "PA13", "PA14" }; +static const char * const eq5p_eyeq6lplus_timer3_groups[] = { "PA15", "PA16" }; +static const char * const eq5p_eyeq6lplus_timer_ext0_groups[] = { "PA17", "PA18", "PA19", "PA20" }; +static const char * const eq5p_eyeq6lplus_spi1_groups[] = { + "PA21", "PA22", "PA23", "PA24", "PA25" +}; +static const char * const eq5p_eyeq6lplus_timer_ext1_groups[] = { "PA26", "PA27", "PA28", "PA29" }; +static const char * const eq5p_eyeq6lplus_ext_ref_clk_groups[] = { "PA30" }; +static const char * const eq5p_eyeq6lplus_mipi_ref_clk_groups[] = { "PA31" }; + +static const struct pinfunction eq5p_eyeq6lplus_functions[] = { + /* gpios function */ + EQ5P_PINFUNCTION("gpio", eq5p_eyeq6lplus_gpio_groups), + + /* Bank A alternate functions */ + EQ5P_PINFUNCTION("timer0", eq5p_eyeq6lplus_timer0_groups), + EQ5P_PINFUNCTION("timer1", eq5p_eyeq6lplus_timer1_groups), + EQ5P_PINFUNCTION("uart_ssi", eq5p_eyeq6lplus_uart_ssi_groups), + EQ5P_PINFUNCTION("spi0", eq5p_eyeq6lplus_spi0_groups), + EQ5P_PINFUNCTION("uart0", eq5p_eyeq6lplus_uart0_groups), + EQ5P_PINFUNCTION("timer2", eq5p_eyeq6lplus_timer2_groups), + EQ5P_PINFUNCTION("timer3", eq5p_eyeq6lplus_timer3_groups), + EQ5P_PINFUNCTION("timer_ext0", eq5p_eyeq6lplus_timer_ext0_groups), + EQ5P_PINFUNCTION("spi1", eq5p_eyeq6lplus_spi1_groups), + EQ5P_PINFUNCTION("timer_ext1", eq5p_eyeq6lplus_timer_ext1_groups), + EQ5P_PINFUNCTION("ext_ref_clk", eq5p_eyeq6lplus_ext_ref_clk_groups), + EQ5P_PINFUNCTION("mipi_ref_clk", eq5p_eyeq6lplus_mipi_ref_clk_groups), +}; + +static const struct eq5p_bank eq5p_eyeq6lplus_banks[] = { + { + .npins = ARRAY_SIZE(eq5p_eyeq6lplus_pins), + .regs = {0x0C0, 0x0C4, 0x0D0, 0x0D4, 0x0B0}, + }, +}; + +static const struct eq5p_match_data eq5p_eyeq6lplus_data = { + .npins = ARRAY_SIZE(eq5p_eyeq6lplus_pins), + .nfunctions = ARRAY_SIZE(eq5p_eyeq6lplus_functions), + .nbanks = ARRAY_SIZE(eq5p_eyeq6lplus_banks), + .pins = eq5p_eyeq6lplus_pins, + .functions = eq5p_eyeq6lplus_functions, + .banks = eq5p_eyeq6lplus_banks, }; static void eq5p_update_bits(const struct eq5p_pinctrl *pctrl, - enum eq5p_bank bank, enum eq5p_regs reg, - u32 mask, u32 val) + const struct eq5p_bank *bank, + enum eq5p_regs reg, u32 mask, u32 val) { - void __iomem *ptr = pctrl->base + eq5p_regs[bank][reg]; + void __iomem *ptr = pctrl->base + bank->regs[reg]; writel((readl(ptr) & ~mask) | (val & mask), ptr); } static bool eq5p_test_bit(const struct eq5p_pinctrl *pctrl, - enum eq5p_bank bank, enum eq5p_regs reg, int offset) + const struct eq5p_bank *bank, + enum eq5p_regs reg, int offset) { - u32 val = readl(pctrl->base + eq5p_regs[bank][reg]); + u32 val = readl(pctrl->base + bank->regs[reg]); if (WARN_ON(offset > 31)) return false; @@ -218,25 +344,29 @@ static bool eq5p_test_bit(const struct eq5p_pinctrl *pctrl, return (val & BIT(offset)) != 0; } -static enum eq5p_bank eq5p_pin_to_bank(unsigned int pin) +static int eq5p_pin_to_bank_offset(const struct eq5p_pinctrl *pctrl, unsigned int pin, + const struct eq5p_bank **bank, unsigned int *offset) { - if (pin < EQ5P_PIN_OFFSET_BANK_B) - return EQ5P_BANK_A; - else - return EQ5P_BANK_B; -} + for (unsigned int i = 0; i < pctrl->data->nbanks; i++) { + const struct eq5p_bank *_bank = &pctrl->data->banks[i]; + unsigned int npins = _bank->npins; -static unsigned int eq5p_pin_to_offset(unsigned int pin) -{ - if (pin < EQ5P_PIN_OFFSET_BANK_B) - return pin; - else - return pin - EQ5P_PIN_OFFSET_BANK_B; + if (pin < npins) { + *bank = _bank; + *offset = pin; + return 0; + } + pin -= npins; + } + + return -EINVAL; } static int eq5p_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) { - return ARRAY_SIZE(eq5p_pins); + struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->data->npins; } static const char *eq5p_pinctrl_get_group_name(struct pinctrl_dev *pctldev, @@ -260,10 +390,15 @@ static int eq5p_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, { enum pin_config_param param = pinconf_to_config_param(*config); struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); - unsigned int offset = eq5p_pin_to_offset(pin); - enum eq5p_bank bank = eq5p_pin_to_bank(pin); + const struct eq5p_bank *bank; + unsigned int offset; u32 val_ds, arg; bool pd, pu; + int ret; + + ret = eq5p_pin_to_bank_offset(pctrl, pin, &bank, &offset); + if (ret) + return ret; pd = eq5p_test_bit(pctrl, bank, EQ5P_PD, offset); pu = eq5p_test_bit(pctrl, bank, EQ5P_PU, offset); @@ -281,10 +416,10 @@ static int eq5p_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, case PIN_CONFIG_DRIVE_STRENGTH: offset *= 2; /* two bits per pin */ if (offset >= 32) { - val_ds = readl(pctrl->base + eq5p_regs[bank][EQ5P_DS_HIGH]); + val_ds = readl(pctrl->base + bank->regs[EQ5P_DS_HIGH]); offset -= 32; } else { - val_ds = readl(pctrl->base + eq5p_regs[bank][EQ5P_DS_LOW]); + val_ds = readl(pctrl->base + bank->regs[EQ5P_DS_LOW]); } arg = (val_ds >> offset) & EQ5P_DS_MASK; break; @@ -302,30 +437,35 @@ static void eq5p_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, { struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); const char *pin_name = pctrl->desc.pins[pin].name; - unsigned int offset = eq5p_pin_to_offset(pin); - enum eq5p_bank bank = eq5p_pin_to_bank(pin); + const struct eq5p_bank *bank; const char *func_name, *bias; unsigned long ds_config; + unsigned int offset; u32 drive_strength; bool pd, pu; int i, j; + if (eq5p_pin_to_bank_offset(pctrl, pin, &bank, &offset)) { + seq_puts(s, "unknown pin"); + return; + } + /* * First, let's get the function name. All pins have only two functions: * GPIO (IOCR == 0) and something else (IOCR == 1). */ if (eq5p_test_bit(pctrl, bank, EQ5P_IOCR, offset)) { func_name = NULL; - for (i = 0; i < ARRAY_SIZE(eq5p_functions); i++) { - if (i == GPIO_FUNC_SELECTOR) + for (i = 0; i < pctrl->data->nfunctions; i++) { + if (i == EQ5P_GPIO_FUNC_SELECTOR) continue; - for (j = 0; j < eq5p_functions[i].ngroups; j++) { + for (j = 0; j < pctrl->data->functions[i].ngroups; j++) { /* Groups and pins are the same thing for us. */ - const char *x = eq5p_functions[i].groups[j]; + const char *x = pctrl->data->functions[i].groups[j]; if (strcmp(x, pin_name) == 0) { - func_name = eq5p_functions[i].name; + func_name = pctrl->data->functions[i].name; break; } } @@ -341,7 +481,7 @@ static void eq5p_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, if (!func_name) func_name = "unknown"; } else { - func_name = eq5p_functions[GPIO_FUNC_SELECTOR].name; + func_name = pctrl->data->functions[EQ5P_GPIO_FUNC_SELECTOR].name; } /* Second, we retrieve the bias. */ @@ -376,13 +516,17 @@ static const struct pinctrl_ops eq5p_pinctrl_ops = { static int eq5p_pinmux_get_functions_count(struct pinctrl_dev *pctldev) { - return ARRAY_SIZE(eq5p_functions); + struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->data->nfunctions; } static const char *eq5p_pinmux_get_function_name(struct pinctrl_dev *pctldev, unsigned int selector) { - return eq5p_functions[selector].name; + struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->data->functions[selector].name; } static int eq5p_pinmux_get_function_groups(struct pinctrl_dev *pctldev, @@ -390,8 +534,10 @@ static int eq5p_pinmux_get_function_groups(struct pinctrl_dev *pctldev, const char * const **groups, unsigned int *num_groups) { - *groups = eq5p_functions[selector].groups; - *num_groups = eq5p_functions[selector].ngroups; + struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + *groups = pctrl->data->functions[selector].groups; + *num_groups = pctrl->data->functions[selector].ngroups; return 0; } @@ -399,12 +545,17 @@ static int eq5p_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, unsigned int pin) { struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); - const char *func_name = eq5p_functions[func_selector].name; + const char *func_name = pctrl->data->functions[func_selector].name; const char *group_name = pctldev->desc->pins[pin].name; - bool is_gpio = func_selector == GPIO_FUNC_SELECTOR; - unsigned int offset = eq5p_pin_to_offset(pin); - enum eq5p_bank bank = eq5p_pin_to_bank(pin); + bool is_gpio = func_selector == EQ5P_GPIO_FUNC_SELECTOR; + const struct eq5p_bank *bank; + unsigned int offset; u32 mask, val; + int ret; + + ret = eq5p_pin_to_bank_offset(pctrl, pin, &bank, &offset); + if (ret) + return ret; dev_dbg(pctldev->dev, "func=%s group=%s\n", func_name, group_name); @@ -419,7 +570,7 @@ static int eq5p_pinmux_gpio_request_enable(struct pinctrl_dev *pctldev, unsigned int pin) { /* Pin numbers and group selectors are the same thing in our case. */ - return eq5p_pinmux_set_mux(pctldev, GPIO_FUNC_SELECTOR, pin); + return eq5p_pinmux_set_mux(pctldev, EQ5P_GPIO_FUNC_SELECTOR, pin); } static const struct pinmux_ops eq5p_pinmux_ops = { @@ -435,10 +586,15 @@ static int eq5p_pinconf_set_drive_strength(struct pinctrl_dev *pctldev, unsigned int pin, u32 arg) { struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); - unsigned int offset = eq5p_pin_to_offset(pin); - enum eq5p_bank bank = eq5p_pin_to_bank(pin); + const struct eq5p_bank *bank; + unsigned int offset; unsigned int reg; u32 mask, val; + int ret; + + ret = eq5p_pin_to_bank_offset(pctrl, pin, &bank, &offset); + if (ret) + return ret; if (arg & ~EQ5P_DS_MASK) { dev_err(pctldev->dev, "Unsupported drive strength: %u\n", arg); @@ -465,12 +621,18 @@ static int eq5p_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, { struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); const char *pin_name = pctldev->desc->pins[pin].name; - unsigned int offset = eq5p_pin_to_offset(pin); - enum eq5p_bank bank = eq5p_pin_to_bank(pin); struct device *dev = pctldev->dev; - u32 val = BIT(offset); + const struct eq5p_bank *bank; + unsigned int offset; unsigned int i; + u32 val; + int ret; + ret = eq5p_pin_to_bank_offset(pctrl, pin, &bank, &offset); + if (ret) + return ret; + + val = BIT(offset); for (i = 0; i < num_configs; i++) { enum pin_config_param param = pinconf_to_config_param(configs[i]); u32 arg = pinconf_to_config_argument(configs[i]); @@ -533,19 +695,26 @@ static const struct pinconf_ops eq5p_pinconf_ops = { static int eq5p_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { + const struct of_device_id *match; struct device *dev = &adev->dev; struct pinctrl_dev *pctldev; struct eq5p_pinctrl *pctrl; int ret; + /* Get match data based on parent OF node set in clk-eyeq */ + match = of_match_node(dev->driver->of_match_table, dev->of_node); + if (!match || !match->data) + return -ENODEV; + pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL); if (!pctrl) return -ENOMEM; pctrl->base = (void __iomem *)dev_get_platdata(dev); + pctrl->data = match->data; pctrl->desc.name = dev_name(dev); - pctrl->desc.pins = eq5p_pins; - pctrl->desc.npins = ARRAY_SIZE(eq5p_pins); + pctrl->desc.pins = pctrl->data->pins; + pctrl->desc.npins = pctrl->data->npins; pctrl->desc.pctlops = &eq5p_pinctrl_ops; pctrl->desc.pmxops = &eq5p_pinmux_ops; pctrl->desc.confops = &eq5p_pinconf_ops; @@ -562,6 +731,13 @@ static int eq5p_probe(struct auxiliary_device *adev, return 0; } +static const struct of_device_id eq5p_match_table[] = { + { .compatible = "mobileye,eyeq5-olb", .data = &eq5p_eyeq5_data }, + { .compatible = "mobileye,eyeq6lplus-olb", .data = &eq5p_eyeq6lplus_data }, + {} +}; +MODULE_DEVICE_TABLE(of, eq5p_match_table); + static const struct auxiliary_device_id eq5p_id_table[] = { { .name = "clk_eyeq.pinctrl" }, {} @@ -571,5 +747,8 @@ MODULE_DEVICE_TABLE(auxiliary, eq5p_id_table); static struct auxiliary_driver eq5p_driver = { .probe = eq5p_probe, .id_table = eq5p_id_table, + .driver = { + .of_match_table = eq5p_match_table, + } }; module_auxiliary_driver(eq5p_driver); diff --git a/drivers/pinctrl/pinctrl-pic32.c b/drivers/pinctrl/pinctrl-pic32.c index 16bbbcf72062..07a24e17d35b 100644 --- a/drivers/pinctrl/pinctrl-pic32.c +++ b/drivers/pinctrl/pinctrl-pic32.c @@ -1696,7 +1696,7 @@ static inline struct pic32_gpio_bank *irqd_to_bank(struct irq_data *d) } static inline struct pic32_gpio_bank *pctl_to_bank(struct pic32_pinctrl *pctl, - unsigned pin) + unsigned int pin) { return &pctl->gpio_banks[pin / PINS_PER_BANK]; } @@ -1709,7 +1709,7 @@ static int pic32_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) } static const char *pic32_pinctrl_get_group_name(struct pinctrl_dev *pctldev, - unsigned group) + unsigned int group) { struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); @@ -1717,9 +1717,9 @@ static const char *pic32_pinctrl_get_group_name(struct pinctrl_dev *pctldev, } static int pic32_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, - unsigned group, - const unsigned **pins, - unsigned *num_pins) + unsigned int group, + const unsigned int **pins, + unsigned int *num_pins) { struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); @@ -1745,7 +1745,7 @@ static int pic32_pinmux_get_functions_count(struct pinctrl_dev *pctldev) } static const char * -pic32_pinmux_get_function_name(struct pinctrl_dev *pctldev, unsigned func) +pic32_pinmux_get_function_name(struct pinctrl_dev *pctldev, unsigned int func) { struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); @@ -1753,9 +1753,9 @@ pic32_pinmux_get_function_name(struct pinctrl_dev *pctldev, unsigned func) } static int pic32_pinmux_get_function_groups(struct pinctrl_dev *pctldev, - unsigned func, + unsigned int func, const char * const **groups, - unsigned * const num_groups) + unsigned int * const num_groups) { struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); @@ -1766,7 +1766,7 @@ static int pic32_pinmux_get_function_groups(struct pinctrl_dev *pctldev, } static int pic32_pinmux_enable(struct pinctrl_dev *pctldev, - unsigned func, unsigned group) + unsigned int func, unsigned int group) { struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); const struct pic32_pin_group *pg = &pctl->groups[group]; @@ -1795,7 +1795,7 @@ static int pic32_pinmux_enable(struct pinctrl_dev *pctldev, static int pic32_gpio_request_enable(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, - unsigned offset) + unsigned int offset) { struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct pic32_gpio_bank *bank = gpiochip_get_data(range->gc); @@ -1810,7 +1810,7 @@ static int pic32_gpio_request_enable(struct pinctrl_dev *pctldev, } static int pic32_gpio_direction_input(struct gpio_chip *chip, - unsigned offset) + unsigned int offset) { struct pic32_gpio_bank *bank = gpiochip_get_data(chip); u32 mask = BIT(offset); @@ -1820,7 +1820,7 @@ static int pic32_gpio_direction_input(struct gpio_chip *chip, return 0; } -static int pic32_gpio_get(struct gpio_chip *chip, unsigned offset) +static int pic32_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct pic32_gpio_bank *bank = gpiochip_get_data(chip); @@ -1842,7 +1842,7 @@ static int pic32_gpio_set(struct gpio_chip *chip, unsigned int offset, } static int pic32_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) + unsigned int offset, int value) { struct pic32_gpio_bank *bank = gpiochip_get_data(chip); u32 mask = BIT(offset); @@ -1855,7 +1855,7 @@ static int pic32_gpio_direction_output(struct gpio_chip *chip, static int pic32_gpio_set_direction(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, - unsigned offset, bool input) + unsigned int offset, bool input) { struct gpio_chip *chip = range->gc; @@ -1876,12 +1876,12 @@ static const struct pinmux_ops pic32_pinmux_ops = { .gpio_set_direction = pic32_gpio_set_direction, }; -static int pic32_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, +static int pic32_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *config) { struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct pic32_gpio_bank *bank = pctl_to_bank(pctl, pin); - unsigned param = pinconf_to_config_param(*config); + unsigned int param = pinconf_to_config_param(*config); u32 mask = BIT(pin - bank->gpio_chip.base); u32 arg; @@ -1917,12 +1917,12 @@ static int pic32_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, return 0; } -static int pic32_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, - unsigned long *configs, unsigned num_configs) +static int pic32_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) { struct pic32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct pic32_gpio_bank *bank = pctl_to_bank(pctl, pin); - unsigned param; + unsigned int param; u32 arg; unsigned int i; u32 offset = pin - bank->gpio_chip.base; @@ -1938,7 +1938,7 @@ static int pic32_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, switch (param) { case PIN_CONFIG_BIAS_PULL_UP: dev_dbg(pctl->dev, " pullup\n"); - writel(mask, bank->reg_base +PIC32_SET(CNPU_REG)); + writel(mask, bank->reg_base + PIC32_SET(CNPU_REG)); break; case PIN_CONFIG_BIAS_PULL_DOWN: dev_dbg(pctl->dev, " pulldown\n"); @@ -1987,7 +1987,7 @@ static struct pinctrl_desc pic32_pinctrl_desc = { .owner = THIS_MODULE, }; -static int pic32_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +static int pic32_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { struct pic32_gpio_bank *bank = gpiochip_get_data(chip); @@ -2174,16 +2174,10 @@ static int pic32_pinctrl_probe(struct platform_device *pdev) if (IS_ERR(pctl->reg_base)) return PTR_ERR(pctl->reg_base); - pctl->clk = devm_clk_get(&pdev->dev, NULL); + pctl->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(pctl->clk)) { ret = PTR_ERR(pctl->clk); - dev_err(&pdev->dev, "clk get failed\n"); - return ret; - } - - ret = clk_prepare_enable(pctl->clk); - if (ret) { - dev_err(&pdev->dev, "clk enable failed\n"); + dev_err(&pdev->dev, "Failed to get and enable clock\n"); return ret; } @@ -2239,16 +2233,10 @@ static int pic32_gpio_probe(struct platform_device *pdev) if (irq < 0) return irq; - bank->clk = devm_clk_get(&pdev->dev, NULL); + bank->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(bank->clk)) { ret = PTR_ERR(bank->clk); - dev_err(&pdev->dev, "clk get failed\n"); - return ret; - } - - ret = clk_prepare_enable(bank->clk); - if (ret) { - dev_err(&pdev->dev, "clk enable failed\n"); + dev_err(&pdev->dev, "Failed to get and enable clock\n"); return ret; } @@ -2265,7 +2253,7 @@ static int pic32_gpio_probe(struct platform_device *pdev) girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_level_irq; girq->parents[0] = irq; - ret = gpiochip_add_data(&bank->gpio_chip, bank); + ret = devm_gpiochip_add_data(&pdev->dev, &bank->gpio_chip, bank); if (ret < 0) { dev_err(&pdev->dev, "Failed to add GPIO chip %u: %d\n", id, ret); diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index f15b18f334ee..2f14c7f9c95a 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -467,6 +467,22 @@ static const struct pinctrl_ops rockchip_pctrl_ops = { * Hardware access */ +static struct rockchip_mux_recalced_data rv1103b_mux_recalced_data[] = { + { + .num = 1, + .pin = 6, + .reg = 0x10024, + .bit = 8, + .mask = 0xf + }, { + .num = 1, + .pin = 7, + .reg = 0x10024, + .bit = 12, + .mask = 0xf + }, +}; + static struct rockchip_mux_recalced_data rv1108_mux_recalced_data[] = { { .num = 1, @@ -1172,6 +1188,9 @@ static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin) else regmap = info->regmap_base; + if (ctrl->type == RV1103B && bank->bank_num == 2 && pin >= 12) + return 0; + if (ctrl->type == RK3506) { if (bank->bank_num == 1) regmap = info->regmap_ioc1; @@ -1298,6 +1317,9 @@ static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) else regmap = info->regmap_base; + if (ctrl->type == RV1103B && bank->bank_num == 2 && pin >= 12) + return 0; + if (ctrl->type == RK3506) { if (bank->bank_num == 1) regmap = info->regmap_ioc1; @@ -1495,6 +1517,214 @@ static int px30_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank, return 0; } +#define RV1103B_DRV_BITS_PER_PIN 8 +#define RV1103B_DRV_PINS_PER_REG 2 +#define RV1103B_DRV_GPIO0_A_OFFSET 0x40100 +#define RV1103B_DRV_GPIO0_B_OFFSET 0x50110 +#define RV1103B_DRV_GPIO1_A01_OFFSET 0x140 +#define RV1103B_DRV_GPIO1_A67_OFFSET 0x1014C +#define RV1103B_DRV_GPIO2_OFFSET 0x30180 +#define RV1103B_DRV_GPIO2_SARADC_OFFSET 0x3080C + +static int rv1103b_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + int ret = 0; + + *regmap = info->regmap_base; + switch (bank->bank_num) { + case 0: + if (pin_num < 7) + *reg = RV1103B_DRV_GPIO0_A_OFFSET; + else if (pin_num > 7 && pin_num < 14) + *reg = RV1103B_DRV_GPIO0_B_OFFSET - 0x10; + else + ret = -EINVAL; + break; + + case 1: + if (pin_num < 6) + *reg = RV1103B_DRV_GPIO1_A01_OFFSET; + else if (pin_num >= 6 && pin_num < 23) + *reg = RV1103B_DRV_GPIO1_A67_OFFSET - 0xc; + else if (pin_num >= 24 && pin_num < 30) + *reg = RV1103B_DRV_GPIO1_A67_OFFSET - 0xc; + else + ret = -EINVAL; + break; + + case 2: + if (pin_num < 12) { + *reg = RV1103B_DRV_GPIO2_OFFSET; + } else if (pin_num >= 16) { + ret = -EINVAL; + } else { + *reg = RV1103B_DRV_GPIO2_SARADC_OFFSET; + *bit = 10; + + return 0; + } + break; + + default: + ret = -EINVAL; + break; + } + + if (ret) { + dev_err(info->dev, "unsupported bank_num %d pin_num %d\n", bank->bank_num, pin_num); + + return ret; + } + + *reg += ((pin_num / RV1103B_DRV_PINS_PER_REG) * 4); + *bit = pin_num % RV1103B_DRV_PINS_PER_REG; + *bit *= RV1103B_DRV_BITS_PER_PIN; + + return 0; +} + +#define RV1103B_PULL_BITS_PER_PIN 2 +#define RV1103B_PULL_PINS_PER_REG 8 +#define RV1103B_PULL_GPIO0_A_OFFSET 0x40200 +#define RV1103B_PULL_GPIO0_B_OFFSET 0x50204 +#define RV1103B_PULL_GPIO1_A01_OFFSET 0x210 +#define RV1103B_PULL_GPIO1_A67_OFFSET 0x10210 +#define RV1103B_PULL_GPIO2_OFFSET 0x30220 +#define RV1103B_PULL_GPIO2_SARADC_OFFSET 0x3080C + +static int rv1103b_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + int ret = 0; + + *regmap = info->regmap_base; + switch (bank->bank_num) { + case 0: + if (pin_num < 7) + *reg = RV1103B_PULL_GPIO0_A_OFFSET; + else if (pin_num > 7 && pin_num < 14) + *reg = RV1103B_PULL_GPIO0_B_OFFSET - 0x4; + else + ret = -EINVAL; + break; + + case 1: + if (pin_num < 6) + *reg = RV1103B_PULL_GPIO1_A01_OFFSET; + else if (pin_num >= 6 && pin_num < 23) + *reg = RV1103B_PULL_GPIO1_A67_OFFSET; + else if (pin_num >= 24 && pin_num < 30) + *reg = RV1103B_PULL_GPIO1_A67_OFFSET; + else + ret = -EINVAL; + break; + + case 2: + if (pin_num < 12) { + *reg = RV1103B_PULL_GPIO2_OFFSET; + } else if (pin_num >= 16) { + ret = -EINVAL; + } else { + *reg = RV1103B_PULL_GPIO2_SARADC_OFFSET; + *bit = 13; + + return 0; + } + break; + + default: + ret = -EINVAL; + break; + } + + if (ret) { + dev_err(info->dev, "unsupported bank_num %d pin_num %d\n", bank->bank_num, pin_num); + + return ret; + } + + *reg += ((pin_num / RV1103B_PULL_PINS_PER_REG) * 4); + *bit = pin_num % RV1103B_PULL_PINS_PER_REG; + *bit *= RV1103B_PULL_BITS_PER_PIN; + + return 0; +} + +#define RV1103B_SMT_BITS_PER_PIN 1 +#define RV1103B_SMT_PINS_PER_REG 8 +#define RV1103B_SMT_GPIO0_A_OFFSET 0x40400 +#define RV1103B_SMT_GPIO0_B_OFFSET 0x50404 +#define RV1103B_SMT_GPIO1_A01_OFFSET 0x410 +#define RV1103B_SMT_GPIO1_A67_OFFSET 0x10410 +#define RV1103B_SMT_GPIO2_OFFSET 0x30420 +#define RV1103B_SMT_GPIO2_SARADC_OFFSET 0x3080C + +static int rv1103b_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, + struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + int ret = 0; + + *regmap = info->regmap_base; + switch (bank->bank_num) { + case 0: + if (pin_num < 7) + *reg = RV1103B_SMT_GPIO0_A_OFFSET; + else if (pin_num > 7 && pin_num < 14) + *reg = RV1103B_SMT_GPIO0_B_OFFSET - 0x4; + else + ret = -EINVAL; + break; + + case 1: + if (pin_num < 6) + *reg = RV1103B_SMT_GPIO1_A01_OFFSET; + else if (pin_num >= 6 && pin_num < 23) + *reg = RV1103B_SMT_GPIO1_A67_OFFSET; + else if (pin_num >= 24 && pin_num < 30) + *reg = RV1103B_SMT_GPIO1_A67_OFFSET; + else + ret = -EINVAL; + break; + + case 2: + if (pin_num < 12) { + *reg = RV1103B_SMT_GPIO2_OFFSET; + } else if (pin_num >= 16) { + ret = -EINVAL; + } else { + *reg = RV1103B_SMT_GPIO2_SARADC_OFFSET; + *bit = 8; + + return 0; + } + break; + + default: + ret = -EINVAL; + break; + } + + if (ret) { + dev_err(info->dev, "unsupported bank_num %d pin_num %d\n", bank->bank_num, pin_num); + + return ret; + } + + *reg += ((pin_num / RV1103B_SMT_PINS_PER_REG) * 4); + *bit = pin_num % RV1103B_SMT_PINS_PER_REG; + *bit *= RV1103B_SMT_BITS_PER_PIN; + + return 0; +} + #define RV1108_PULL_PMU_OFFSET 0x10 #define RV1108_PULL_OFFSET 0x110 #define RV1108_PULL_PINS_PER_REG 8 @@ -2982,6 +3212,9 @@ static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank, u8 bit; int drv_type = bank->drv[pin_num / 8].drv_type; + if (ctrl->type == RV1103B && pin_num >= 12) + drv_type = DRV_TYPE_IO_LEVEL_2_BIT; + ret = ctrl->drv_calc_reg(bank, pin_num, ®map, ®, &bit); if (ret) return ret; @@ -3043,6 +3276,11 @@ static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank, if (ret) return ret; + if (ctrl->type == RV1103B && bank->bank_num == 2 && pin_num >= 12) { + data = data >> 10; + return data & 0x3; + } + data >>= bit; data &= (1 << rmask_bits) - 1; @@ -3071,7 +3309,8 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank, rmask_bits = RK3588_DRV_BITS_PER_PIN; ret = strength; goto config; - } else if (ctrl->type == RK3506 || + } else if (ctrl->type == RV1103B || + ctrl->type == RK3506 || ctrl->type == RK3528 || ctrl->type == RK3562 || ctrl->type == RK3568) { @@ -3182,6 +3421,12 @@ config: ret = strength; } } + + if (ctrl->type == RV1103B && bank->bank_num == 2 && pin_num >= 12) { + rmask_bits = 2; + ret = strength; + } + /* enable the write to the equivalent lower bits */ data = ((1 << rmask_bits) - 1) << (bit + 16); rmask = data | (data >> 16); @@ -3236,6 +3481,7 @@ static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) ? PIN_CONFIG_BIAS_PULL_PIN_DEFAULT : PIN_CONFIG_BIAS_DISABLE; case PX30: + case RV1103B: case RV1108: case RK3188: case RK3288: @@ -3251,6 +3497,9 @@ static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) pull_type = bank->pull_type[pin_num / 8]; data >>= bit; data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1; + + if (ctrl->type == RV1103B && bank->bank_num == 2 && pin_num >= 12) + pull_type = 1; /* * In the TRM, pull-up being 1 for everything except the GPIO0_D3-D6, * where that pull up value becomes 3. @@ -3297,6 +3546,7 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank, ret = regmap_write(regmap, reg, data); break; case PX30: + case RV1103B: case RV1108: case RV1126: case RK3188: @@ -3312,6 +3562,8 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank, case RK3576: case RK3588: pull_type = bank->pull_type[pin_num / 8]; + if (ctrl->type == RV1103B && bank->bank_num == 2 && pin_num >= 12) + pull_type = 1; ret = -EINVAL; for (i = 0; i < ARRAY_SIZE(rockchip_pull_list[pull_type]); i++) { @@ -3417,6 +3669,11 @@ static int rockchip_get_schmitt(struct rockchip_pin_bank *bank, int pin_num) if (ret) return ret; + if (ctrl->type == RV1103B && bank->bank_num == 2 && pin_num >= 12) { + data >>= 8; + return data & 0x3; + } + data >>= bit; switch (ctrl->type) { case RK3562: @@ -3473,6 +3730,12 @@ static int rockchip_set_schmitt(struct rockchip_pin_bank *bank, } } + if (ctrl->type == RV1103B && bank->bank_num == 2 && pin_num >= 12) { + data = 0x3 << (bit + 16); + rmask = data | (data >> 16); + data |= ((enable ? 0x3 : 0) << bit); + } + return regmap_update_bits(regmap, reg, rmask, data); } @@ -3579,6 +3842,7 @@ static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl, case RK3066B: return pull ? false : true; case PX30: + case RV1103B: case RV1108: case RV1126: case RK3188: @@ -4314,6 +4578,51 @@ static struct rockchip_pin_ctrl px30_pin_ctrl = { .schmitt_calc_reg = px30_calc_schmitt_reg_and_bit, }; +static struct rockchip_pin_bank rv1103b_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS_OFFSET_DRV_FLAGS(0, 32, "gpio0", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0x40000, 0x50008, 0x50010, 0x50018, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT), + PIN_BANK_IOMUX_FLAGS_OFFSET_DRV_FLAGS(1, 32, "gpio1", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0x20, 0x10028, 0x10030, 0x10038, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT), + PIN_BANK_IOMUX_FLAGS_OFFSET_DRV_FLAGS(2, 32, "gpio2", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0x30040, 0x30048, 0x30050, 0x30058, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT), +}; + +static struct rockchip_pin_ctrl rv1103b_pin_ctrl __maybe_unused = { + .pin_banks = rv1103b_pin_banks, + .nr_banks = ARRAY_SIZE(rv1103b_pin_banks), + .label = "RV1103B-GPIO", + .type = RV1103B, + .iomux_recalced = rv1103b_mux_recalced_data, + .niomux_recalced = ARRAY_SIZE(rv1103b_mux_recalced_data), + .pull_calc_reg = rv1103b_calc_pull_reg_and_bit, + .drv_calc_reg = rv1103b_calc_drv_reg_and_bit, + .schmitt_calc_reg = rv1103b_calc_schmitt_reg_and_bit, +}; + static struct rockchip_pin_bank rv1108_pin_banks[] = { PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU, IOMUX_SOURCE_PMU, @@ -4950,6 +5259,8 @@ static struct rockchip_pin_ctrl rk3588_pin_ctrl = { static const struct of_device_id rockchip_pinctrl_dt_match[] = { { .compatible = "rockchip,px30-pinctrl", .data = &px30_pin_ctrl }, + { .compatible = "rockchip,rv1103b-pinctrl", + .data = &rv1103b_pin_ctrl }, { .compatible = "rockchip,rv1108-pinctrl", .data = &rv1108_pin_ctrl }, { .compatible = "rockchip,rv1126-pinctrl", diff --git a/drivers/pinctrl/pinctrl-rockchip.h b/drivers/pinctrl/pinctrl-rockchip.h index 4f4aff42a80a..bb0e803e3b8a 100644 --- a/drivers/pinctrl/pinctrl-rockchip.h +++ b/drivers/pinctrl/pinctrl-rockchip.h @@ -185,6 +185,7 @@ enum rockchip_pinctrl_type { PX30, + RV1103B, RV1108, RV1126, RK2928, diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index d85e6c1f6321..7e022a46ee42 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -1960,7 +1960,7 @@ static const struct pcs_soc_data pinctrl_single_am654 = { .irq_status_mask = (1 << 30), /* WKUP_EVT */ }; -static const struct pcs_soc_data pinctrl_single_j7200 = { +static const struct pcs_soc_data pinctrl_single_loss_off = { .flags = PCS_CONTEXT_LOSS_OFF, }; @@ -1972,6 +1972,7 @@ static const struct pcs_soc_data pinconf_single = { }; static const struct of_device_id pcs_of_match[] = { + { .compatible = "brcm,bcm7038-padconf", .data = &pinctrl_single_loss_off }, { .compatible = "marvell,pxa1908-padconf", .data = &pinconf_single }, { .compatible = "ti,am437-padconf", .data = &pinctrl_single_am437x }, { .compatible = "ti,am654-padconf", .data = &pinctrl_single_am654 }, @@ -1979,7 +1980,8 @@ static const struct of_device_id pcs_of_match[] = { { .compatible = "ti,omap3-padconf", .data = &pinctrl_single_omap_wkup }, { .compatible = "ti,omap4-padconf", .data = &pinctrl_single_omap_wkup }, { .compatible = "ti,omap5-padconf", .data = &pinctrl_single_omap_wkup }, - { .compatible = "ti,j7200-padconf", .data = &pinctrl_single_j7200 }, + { .compatible = "ti,j7200-padconf", .data = &pinctrl_single_loss_off }, + { .compatible = "ti,am62l-padconf", .data = &pinctrl_single_loss_off }, { .compatible = "pinctrl-single", .data = &pinctrl_single }, { .compatible = "pinconf-single", .data = &pinconf_single }, { }, diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index f56592411cf6..80af372a1147 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -60,6 +60,16 @@ config PINCTRL_LPASS_LPI Qualcomm Technologies Inc LPASS (Low Power Audio SubSystem) LPI (Low Power Island) found on the Qualcomm Technologies Inc SoCs. +config PINCTRL_MILOS_LPASS_LPI + tristate "Qualcomm Technologies Inc Milos LPASS LPI pin controller driver" + depends on ARM64 || COMPILE_TEST + depends on PINCTRL_LPASS_LPI + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc LPASS (Low Power Audio SubSystem) LPI + (Low Power Island) found on the Qualcomm Technologies Inc Milos + platform. + config PINCTRL_SC7280_LPASS_LPI tristate "Qualcomm Technologies Inc SC7280 and SM8350 LPASS LPI pin controller driver" depends on ARM64 || COMPILE_TEST @@ -89,6 +99,16 @@ config PINCTRL_SM4250_LPASS_LPI Qualcomm Technologies Inc LPASS (Low Power Audio SubSystem) LPI (Low Power Island) found on the Qualcomm Technologies Inc SM4250 platform. +config PINCTRL_SDM670_LPASS_LPI + tristate "Qualcomm Technologies Inc SDM670 LPASS LPI pin controller driver" + depends on GPIOLIB + depends on ARM64 || COMPILE_TEST + depends on PINCTRL_LPASS_LPI + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc LPASS (Low Power Audio SubSystem) LPI + (Low Power Island) found on the Qualcomm Technologies Inc SDM670 platform. + config PINCTRL_SM6115_LPASS_LPI tristate "Qualcomm Technologies Inc SM6115 LPASS LPI pin controller driver" depends on ARM64 || COMPILE_TEST diff --git a/drivers/pinctrl/qcom/Kconfig.msm b/drivers/pinctrl/qcom/Kconfig.msm index 3e9e02774001..836cdeca1006 100644 --- a/drivers/pinctrl/qcom/Kconfig.msm +++ b/drivers/pinctrl/qcom/Kconfig.msm @@ -15,6 +15,16 @@ config PINCTRL_APQ8084 This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm APQ8084 platform. +config PINCTRL_ELIZA + tristate "Qualcomm Technologies Inc Eliza pin controller driver" + depends on ARM64 || COMPILE_TEST + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc Top Level Mode Multiplexer block (TLMM) + block found on the Qualcomm Technologies Inc Eliza platform. + Say Y here to compile statically, or M here to compile it as a module. + If unsure, say N. + config PINCTRL_GLYMUR tristate "Qualcomm Technologies Inc Glymur pin controller driver" depends on ARM64 || COMPILE_TEST @@ -25,6 +35,16 @@ config PINCTRL_GLYMUR Say Y here to compile statically, or M here to compile it as a module. If unsure, say N. +config PINCTRL_HAWI + tristate "Qualcomm Technologies Inc Hawi pin controller driver" + depends on ARM64 || COMPILE_TEST + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc Top Level Mode Multiplexer block (TLMM) + block found on the Qualcomm Technologies Inc Hawi platform. + Say Y here to compile statically, or M here to compile it as a module. + If unsure, say N. + config PINCTRL_IPQ4019 tristate "Qualcomm IPQ4019 pin controller driver" depends on ARM || COMPILE_TEST @@ -48,6 +68,14 @@ config PINCTRL_IPQ8064 This is the pinctrl, pinmux, pinconf and gpiolib driver for the Qualcomm TLMM block found in the Qualcomm IPQ8064 platform. +config PINCTRL_IPQ5210 + tristate "Qualcomm Technologies Inc IPQ5210 pin controller driver" + depends on ARM64 || COMPILE_TEST + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc TLMM block found on the Qualcomm + Technologies Inc IPQ5210 platform. + config PINCTRL_IPQ5332 tristate "Qualcomm Technologies Inc IPQ5332 pin controller driver" depends on ARM64 || COMPILE_TEST diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index 4269d1781015..84bda3ada874 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -3,10 +3,13 @@ obj-$(CONFIG_PINCTRL_MSM) += pinctrl-msm.o obj-$(CONFIG_PINCTRL_APQ8064) += pinctrl-apq8064.o obj-$(CONFIG_PINCTRL_APQ8084) += pinctrl-apq8084.o +obj-$(CONFIG_PINCTRL_ELIZA) += pinctrl-eliza.o obj-$(CONFIG_PINCTRL_GLYMUR) += pinctrl-glymur.o +obj-$(CONFIG_PINCTRL_HAWI) += pinctrl-hawi.o obj-$(CONFIG_PINCTRL_IPQ4019) += pinctrl-ipq4019.o obj-$(CONFIG_PINCTRL_IPQ5018) += pinctrl-ipq5018.o obj-$(CONFIG_PINCTRL_IPQ8064) += pinctrl-ipq8064.o +obj-$(CONFIG_PINCTRL_IPQ5210) += pinctrl-ipq5210.o obj-$(CONFIG_PINCTRL_IPQ5332) += pinctrl-ipq5332.o obj-$(CONFIG_PINCTRL_IPQ5424) += pinctrl-ipq5424.o obj-$(CONFIG_PINCTRL_IPQ8074) += pinctrl-ipq8074.o @@ -33,6 +36,7 @@ obj-$(CONFIG_PINCTRL_QDF2XXX) += pinctrl-qdf2xxx.o obj-$(CONFIG_PINCTRL_MDM9607) += pinctrl-mdm9607.o obj-$(CONFIG_PINCTRL_MDM9615) += pinctrl-mdm9615.o obj-$(CONFIG_PINCTRL_MILOS) += pinctrl-milos.o +obj-$(CONFIG_PINCTRL_MILOS_LPASS_LPI) += pinctrl-milos-lpass-lpi.o obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-gpio.o obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-mpp.o obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-gpio.o @@ -48,6 +52,7 @@ obj-$(CONFIG_PINCTRL_SC8280XP) += pinctrl-sc8280xp.o obj-$(CONFIG_PINCTRL_SDM660) += pinctrl-sdm660.o obj-$(CONFIG_PINCTRL_SDM660_LPASS_LPI) += pinctrl-sdm660-lpass-lpi.o obj-$(CONFIG_PINCTRL_SDM670) += pinctrl-sdm670.o +obj-$(CONFIG_PINCTRL_SDM670_LPASS_LPI) += pinctrl-sdm670-lpass-lpi.o obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o obj-$(CONFIG_PINCTRL_SDX55) += pinctrl-sdx55.o obj-$(CONFIG_PINCTRL_SDX65) += pinctrl-sdx65.o diff --git a/drivers/pinctrl/qcom/pinctrl-apq8084.c b/drivers/pinctrl/qcom/pinctrl-apq8084.c index 27693cd64881..9fdbe6743512 100644 --- a/drivers/pinctrl/qcom/pinctrl-apq8084.c +++ b/drivers/pinctrl/qcom/pinctrl-apq8084.c @@ -343,7 +343,6 @@ static const unsigned int sdc2_data_pins[] = { 152 }; .io_reg = 0x1004 + 0x10 * id, \ .intr_cfg_reg = 0x1008 + 0x10 * id, \ .intr_status_reg = 0x100c + 0x10 * id, \ - .intr_target_reg = 0x1008 + 0x10 * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -370,7 +369,6 @@ static const unsigned int sdc2_data_pins[] = { 152 }; .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-eliza.c b/drivers/pinctrl/qcom/pinctrl-eliza.c new file mode 100644 index 000000000000..c1f756cbcdeb --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-eliza.c @@ -0,0 +1,1545 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + msm_mux_##f10, \ + msm_mux_##f11 /* egpio mode */ \ + }, \ + .nfuncs = 12, \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .egpio_enable = 12, \ + .egpio_present = 11, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_wakeup_present_bit = 6, \ + .intr_wakeup_enable_bit = 7, \ + .intr_target_bit = 8, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +#define UFS_RESET(pg_name, ctl, io) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = io, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +static const struct pinctrl_pin_desc eliza_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), + PINCTRL_PIN(128, "GPIO_128"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "GPIO_133"), + PINCTRL_PIN(134, "GPIO_134"), + PINCTRL_PIN(135, "GPIO_135"), + PINCTRL_PIN(136, "GPIO_136"), + PINCTRL_PIN(137, "GPIO_137"), + PINCTRL_PIN(138, "GPIO_138"), + PINCTRL_PIN(139, "GPIO_139"), + PINCTRL_PIN(140, "GPIO_140"), + PINCTRL_PIN(141, "GPIO_141"), + PINCTRL_PIN(142, "GPIO_142"), + PINCTRL_PIN(143, "GPIO_143"), + PINCTRL_PIN(144, "GPIO_144"), + PINCTRL_PIN(145, "GPIO_145"), + PINCTRL_PIN(146, "GPIO_146"), + PINCTRL_PIN(147, "GPIO_147"), + PINCTRL_PIN(148, "GPIO_148"), + PINCTRL_PIN(149, "GPIO_149"), + PINCTRL_PIN(150, "GPIO_150"), + PINCTRL_PIN(151, "GPIO_151"), + PINCTRL_PIN(152, "GPIO_152"), + PINCTRL_PIN(153, "GPIO_153"), + PINCTRL_PIN(154, "GPIO_154"), + PINCTRL_PIN(155, "GPIO_155"), + PINCTRL_PIN(156, "GPIO_156"), + PINCTRL_PIN(157, "GPIO_157"), + PINCTRL_PIN(158, "GPIO_158"), + PINCTRL_PIN(159, "GPIO_159"), + PINCTRL_PIN(160, "GPIO_160"), + PINCTRL_PIN(161, "GPIO_161"), + PINCTRL_PIN(162, "GPIO_162"), + PINCTRL_PIN(163, "GPIO_163"), + PINCTRL_PIN(164, "GPIO_164"), + PINCTRL_PIN(165, "GPIO_165"), + PINCTRL_PIN(166, "GPIO_166"), + PINCTRL_PIN(167, "GPIO_167"), + PINCTRL_PIN(168, "GPIO_168"), + PINCTRL_PIN(169, "GPIO_169"), + PINCTRL_PIN(170, "GPIO_170"), + PINCTRL_PIN(171, "GPIO_171"), + PINCTRL_PIN(172, "GPIO_172"), + PINCTRL_PIN(173, "GPIO_173"), + PINCTRL_PIN(174, "GPIO_174"), + PINCTRL_PIN(175, "GPIO_175"), + PINCTRL_PIN(176, "GPIO_176"), + PINCTRL_PIN(177, "GPIO_177"), + PINCTRL_PIN(178, "GPIO_178"), + PINCTRL_PIN(179, "GPIO_179"), + PINCTRL_PIN(180, "GPIO_180"), + PINCTRL_PIN(181, "GPIO_181"), + PINCTRL_PIN(182, "GPIO_182"), + PINCTRL_PIN(183, "GPIO_183"), + PINCTRL_PIN(184, "GPIO_184"), + PINCTRL_PIN(185, "UFS_RESET"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); +DECLARE_MSM_GPIO_PINS(123); +DECLARE_MSM_GPIO_PINS(124); +DECLARE_MSM_GPIO_PINS(125); +DECLARE_MSM_GPIO_PINS(126); +DECLARE_MSM_GPIO_PINS(127); +DECLARE_MSM_GPIO_PINS(128); +DECLARE_MSM_GPIO_PINS(129); +DECLARE_MSM_GPIO_PINS(130); +DECLARE_MSM_GPIO_PINS(131); +DECLARE_MSM_GPIO_PINS(132); +DECLARE_MSM_GPIO_PINS(133); +DECLARE_MSM_GPIO_PINS(134); +DECLARE_MSM_GPIO_PINS(135); +DECLARE_MSM_GPIO_PINS(136); +DECLARE_MSM_GPIO_PINS(137); +DECLARE_MSM_GPIO_PINS(138); +DECLARE_MSM_GPIO_PINS(139); +DECLARE_MSM_GPIO_PINS(140); +DECLARE_MSM_GPIO_PINS(141); +DECLARE_MSM_GPIO_PINS(142); +DECLARE_MSM_GPIO_PINS(143); +DECLARE_MSM_GPIO_PINS(144); +DECLARE_MSM_GPIO_PINS(145); +DECLARE_MSM_GPIO_PINS(146); +DECLARE_MSM_GPIO_PINS(147); +DECLARE_MSM_GPIO_PINS(148); +DECLARE_MSM_GPIO_PINS(149); +DECLARE_MSM_GPIO_PINS(150); +DECLARE_MSM_GPIO_PINS(151); +DECLARE_MSM_GPIO_PINS(152); +DECLARE_MSM_GPIO_PINS(153); +DECLARE_MSM_GPIO_PINS(154); +DECLARE_MSM_GPIO_PINS(155); +DECLARE_MSM_GPIO_PINS(156); +DECLARE_MSM_GPIO_PINS(157); +DECLARE_MSM_GPIO_PINS(158); +DECLARE_MSM_GPIO_PINS(159); +DECLARE_MSM_GPIO_PINS(160); +DECLARE_MSM_GPIO_PINS(161); +DECLARE_MSM_GPIO_PINS(162); +DECLARE_MSM_GPIO_PINS(163); +DECLARE_MSM_GPIO_PINS(164); +DECLARE_MSM_GPIO_PINS(165); +DECLARE_MSM_GPIO_PINS(166); +DECLARE_MSM_GPIO_PINS(167); +DECLARE_MSM_GPIO_PINS(168); +DECLARE_MSM_GPIO_PINS(169); +DECLARE_MSM_GPIO_PINS(170); +DECLARE_MSM_GPIO_PINS(171); +DECLARE_MSM_GPIO_PINS(172); +DECLARE_MSM_GPIO_PINS(173); +DECLARE_MSM_GPIO_PINS(174); +DECLARE_MSM_GPIO_PINS(175); +DECLARE_MSM_GPIO_PINS(176); +DECLARE_MSM_GPIO_PINS(177); +DECLARE_MSM_GPIO_PINS(178); +DECLARE_MSM_GPIO_PINS(179); +DECLARE_MSM_GPIO_PINS(180); +DECLARE_MSM_GPIO_PINS(181); +DECLARE_MSM_GPIO_PINS(182); +DECLARE_MSM_GPIO_PINS(183); +DECLARE_MSM_GPIO_PINS(184); + +static const unsigned int ufs_reset_pins[] = { 185 }; + +enum eliza_functions { + msm_mux_gpio, + msm_mux_aoss_cti, + msm_mux_atest_char, + msm_mux_atest_usb, + msm_mux_audio_ext_mclk0, + msm_mux_audio_ref_clk, + msm_mux_cam_mclk, + msm_mux_cci_async_in, + msm_mux_cci_i2c_scl, + msm_mux_cci_i2c_sda, + msm_mux_cci_timer, + msm_mux_coex_uart1_rx, + msm_mux_coex_uart1_tx, + msm_mux_coex_uart2_rx, + msm_mux_coex_uart2_tx, + msm_mux_dbg_out_clk, + msm_mux_ddr_bist_complete, + msm_mux_ddr_bist_fail, + msm_mux_ddr_bist_start, + msm_mux_ddr_bist_stop, + msm_mux_ddr_pxi0, + msm_mux_ddr_pxi1, + msm_mux_dp0_hot, + msm_mux_egpio, + msm_mux_gcc_gp1, + msm_mux_gcc_gp2, + msm_mux_gcc_gp3, + msm_mux_gnss_adc0, + msm_mux_gnss_adc1, + msm_mux_hdmi_ddc_scl, + msm_mux_hdmi_ddc_sda, + msm_mux_hdmi_dtest0, + msm_mux_hdmi_dtest1, + msm_mux_hdmi_hot_plug, + msm_mux_hdmi_pixel_clk, + msm_mux_hdmi_rcv_det, + msm_mux_hdmi_tx_cec, + msm_mux_host2wlan_sol, + msm_mux_i2s0_data0, + msm_mux_i2s0_data1, + msm_mux_i2s0_sck, + msm_mux_i2s0_ws, + msm_mux_ibi_i3c, + msm_mux_jitter_bist, + msm_mux_mdp_esync0_out, + msm_mux_mdp_esync1_out, + msm_mux_mdp_vsync, + msm_mux_mdp_vsync0_out, + msm_mux_mdp_vsync11_out, + msm_mux_mdp_vsync1_out, + msm_mux_mdp_vsync2_out, + msm_mux_mdp_vsync3_out, + msm_mux_mdp_vsync_e, + msm_mux_nav_gpio0, + msm_mux_nav_gpio1, + msm_mux_nav_gpio2, + msm_mux_nav_gpio3, + msm_mux_pcie0_clk_req_n, + msm_mux_pcie1_clk_req_n, + msm_mux_phase_flag, + msm_mux_pll_bist_sync, + msm_mux_pll_clk_aux, + msm_mux_prng_rosc0, + msm_mux_prng_rosc1, + msm_mux_prng_rosc2, + msm_mux_prng_rosc3, + msm_mux_qdss_cti, + msm_mux_qdss_gpio_traceclk, + msm_mux_qdss_gpio_tracectl, + msm_mux_qdss_gpio_tracedata, + msm_mux_qlink_big_enable, + msm_mux_qlink_big_request, + msm_mux_qlink_little_enable, + msm_mux_qlink_little_request, + msm_mux_qlink_wmss, + msm_mux_qspi0, + msm_mux_qspi_clk, + msm_mux_qspi_cs, + msm_mux_qup1_se0, + msm_mux_qup1_se1, + msm_mux_qup1_se2, + msm_mux_qup1_se3, + msm_mux_qup1_se4, + msm_mux_qup1_se5, + msm_mux_qup1_se6, + msm_mux_qup1_se7, + msm_mux_qup2_se0, + msm_mux_qup2_se1, + msm_mux_qup2_se2, + msm_mux_qup2_se3, + msm_mux_qup2_se4, + msm_mux_qup2_se5, + msm_mux_qup2_se6, + msm_mux_qup2_se7, + msm_mux_resout_gpio, + msm_mux_sd_write_protect, + msm_mux_sdc1, + msm_mux_sdc2, + msm_mux_sdc2_fb_clk, + msm_mux_tb_trig_sdc1, + msm_mux_tb_trig_sdc2, + msm_mux_tmess_prng0, + msm_mux_tmess_prng1, + msm_mux_tmess_prng2, + msm_mux_tmess_prng3, + msm_mux_tsense_pwm1, + msm_mux_tsense_pwm2, + msm_mux_tsense_pwm3, + msm_mux_tsense_pwm4, + msm_mux_uim0_clk, + msm_mux_uim0_data, + msm_mux_uim0_present, + msm_mux_uim0_reset, + msm_mux_uim1_clk, + msm_mux_uim1_data, + msm_mux_uim1_present, + msm_mux_uim1_reset, + msm_mux_usb0_hs, + msm_mux_usb_phy, + msm_mux_vfr_0, + msm_mux_vfr_1, + msm_mux_vsense_trigger_mirnat, + msm_mux_wcn_sw_ctrl, + msm_mux__, +}; + +static const char *const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio16", "gpio17", "gpio18", "gpio19", + "gpio20", "gpio21", "gpio22", "gpio23", "gpio26", "gpio27", + "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", + "gpio34", "gpio35", "gpio36", "gpio37", "gpio38", "gpio39", + "gpio40", "gpio42", "gpio44", "gpio45", "gpio46", "gpio47", + "gpio48", "gpio49", "gpio50", "gpio51", "gpio52", "gpio53", + "gpio54", "gpio55", "gpio56", "gpio57", "gpio58", "gpio59", + "gpio60", "gpio61", "gpio62", "gpio63", "gpio64", "gpio65", + "gpio66", "gpio67", "gpio68", "gpio69", "gpio70", "gpio71", + "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77", + "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio84", + "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", + "gpio91", "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", + "gpio97", "gpio98", "gpio99", "gpio100", "gpio101", "gpio102", + "gpio103", "gpio104", "gpio105", "gpio106", "gpio107", "gpio108", + "gpio109", "gpio110", "gpio111", "gpio112", "gpio113", "gpio114", + "gpio115", "gpio116", "gpio117", "gpio118", "gpio119", "gpio120", + "gpio121", "gpio122", "gpio123", "gpio124", "gpio125", "gpio126", + "gpio127", "gpio128", "gpio129", "gpio130", "gpio131", "gpio132", + "gpio133", "gpio134", "gpio135", "gpio138", "gpio139", "gpio140", + "gpio141", "gpio142", "gpio143", "gpio144", "gpio145", "gpio146", + "gpio147", "gpio148", "gpio149", "gpio150", "gpio151", "gpio152", + "gpio153", "gpio154", "gpio155", "gpio156", "gpio157", "gpio158", + "gpio159", "gpio160", "gpio161", "gpio162", "gpio163", "gpio164", + "gpio165", "gpio166", "gpio167", "gpio168", "gpio169", "gpio170", + "gpio171", "gpio172", "gpio173", "gpio174", "gpio175", "gpio176", + "gpio177", "gpio178", "gpio179", "gpio180", "gpio181", "gpio182", + "gpio184", +}; + +static const char *const aoss_cti_groups[] = { + "gpio0", "gpio1", "gpio26", "gpio27", +}; + +static const char *const atest_char_groups[] = { + "gpio71", "gpio70", "gpio72", "gpio74", "gpio73", +}; + +static const char *const atest_usb_groups[] = { + "gpio55", "gpio54", +}; + +static const char *const audio_ext_mclk0_groups[] = { + "gpio69", +}; + +static const char *const audio_ref_clk_groups[] = { + "gpio32", +}; + +static const char *const cam_mclk_groups[] = { + "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", +}; + +static const char *const cci_async_in_groups[] = { + "gpio115", "gpio31", "gpio30", +}; + +static const char *const cci_i2c_scl_groups[] = { + "gpio71", "gpio73", "gpio75", "gpio77", +}; + +static const char *const cci_i2c_sda_groups[] = { + "gpio70", "gpio72", "gpio74", "gpio76", +}; + +static const char *const cci_timer_groups[] = { + "gpio76", "gpio63", "gpio125", "gpio126", "gpio127", +}; + +static const char *const coex_uart1_rx_groups[] = { + "gpio112", +}; + +static const char *const coex_uart1_tx_groups[] = { + "gpio111", +}; + +static const char *const coex_uart2_rx_groups[] = { + "gpio116", +}; + +static const char *const coex_uart2_tx_groups[] = { + "gpio100", +}; + +static const char *const dbg_out_clk_groups[] = { + "gpio81", +}; + +static const char *const ddr_bist_complete_groups[] = { + "gpio52", +}; + +static const char *const ddr_bist_fail_groups[] = { + "gpio147", +}; + +static const char *const ddr_bist_start_groups[] = { + "gpio34", +}; + +static const char *const ddr_bist_stop_groups[] = { + "gpio53", +}; + +static const char *const ddr_pxi0_groups[] = { + "gpio54", "gpio55", +}; + +static const char *const ddr_pxi1_groups[] = { + "gpio40", "gpio42", +}; + +static const char *const dp0_hot_groups[] = { + "gpio55", +}; + +static const char *const egpio_groups[] = { + "gpio28", "gpio29", "gpio30", "gpio31", "gpio138", "gpio139", + "gpio140", "gpio141", "gpio142", "gpio143", "gpio144", "gpio145", + "gpio146", "gpio147", "gpio148", "gpio149", "gpio150", "gpio151", + "gpio152", "gpio153", "gpio154", "gpio155", "gpio156", "gpio157", + "gpio158", "gpio159", "gpio160", "gpio161", "gpio162", "gpio163", + "gpio164", "gpio165", "gpio166", "gpio167", "gpio168", "gpio169", + "gpio170", "gpio171", "gpio172", "gpio173", "gpio174", "gpio175", + "gpio176", "gpio177", "gpio178", "gpio179", "gpio180", "gpio181", + "gpio182", "gpio184", +}; + +static const char *const gcc_gp1_groups[] = { + "gpio27", "gpio53", +}; + +static const char *const gcc_gp2_groups[] = { + "gpio32", "gpio35", +}; + +static const char *const gcc_gp3_groups[] = { + "gpio30", "gpio33", +}; + +static const char *const gnss_adc0_groups[] = { + "gpio42", "gpio55", +}; + +static const char *const gnss_adc1_groups[] = { + "gpio40", "gpio54", +}; + +static const char *const hdmi_ddc_scl_groups[] = { + "gpio6", +}; + +static const char *const hdmi_ddc_sda_groups[] = { + "gpio7", +}; + +static const char *const hdmi_dtest0_groups[] = { + "gpio132", +}; + +static const char *const hdmi_dtest1_groups[] = { + "gpio133", +}; + +static const char *const hdmi_hot_plug_groups[] = { + "gpio47", +}; + +static const char *const hdmi_pixel_clk_groups[] = { + "gpio18", +}; + +static const char *const hdmi_rcv_det_groups[] = { + "gpio19", +}; + +static const char *const hdmi_tx_cec_groups[] = { + "gpio46", +}; + +static const char *const host2wlan_sol_groups[] = { + "gpio33", +}; + +static const char *const i2s0_data0_groups[] = { + "gpio64", +}; + +static const char *const i2s0_data1_groups[] = { + "gpio63", +}; + +static const char *const i2s0_sck_groups[] = { + "gpio60", +}; + +static const char *const i2s0_ws_groups[] = { + "gpio61", +}; + +static const char *const ibi_i3c_groups[] = { + "gpio0", "gpio1", "gpio4", "gpio5", "gpio12", "gpio13", + "gpio28", "gpio29", "gpio32", "gpio33", "gpio36", "gpio37", +}; + +static const char *const jitter_bist_groups[] = { + "gpio77", +}; + +static const char *const mdp_esync0_out_groups[] = { + "gpio13", +}; + +static const char *const mdp_esync1_out_groups[] = { + "gpio12", +}; + +static const char *const mdp_vsync_groups[] = { + "gpio16", "gpio17", "gpio79", "gpio100", "gpio120", "gpio121", +}; + +static const char *const mdp_vsync0_out_groups[] = { + "gpio17", +}; + +static const char *const mdp_vsync11_out_groups[] = { + "gpio27", +}; + +static const char *const mdp_vsync1_out_groups[] = { + "gpio17", +}; + +static const char *const mdp_vsync2_out_groups[] = { + "gpio16", +}; + +static const char *const mdp_vsync3_out_groups[] = { + "gpio16", +}; + +static const char *const mdp_vsync_e_groups[] = { + "gpio13", +}; + +static const char *const nav_gpio0_groups[] = { + "gpio119", +}; + +static const char *const nav_gpio1_groups[] = { + "gpio117", +}; + +static const char *const nav_gpio2_groups[] = { + "gpio118", +}; + +static const char *const nav_gpio3_groups[] = { + "gpio113", +}; + +static const char *const pcie0_clk_req_n_groups[] = { + "gpio80", +}; + +static const char *const pcie1_clk_req_n_groups[] = { + "gpio52", +}; + +static const char *const phase_flag_groups[] = { + "gpio71", "gpio70", "gpio174", "gpio175", "gpio172", "gpio171", + "gpio170", "gpio169", "gpio168", "gpio167", "gpio166", "gpio165", + "gpio182", "gpio164", "gpio163", "gpio162", "gpio161", "gpio160", + "gpio159", "gpio158", "gpio157", "gpio80", "gpio78", "gpio181", + "gpio76", "gpio75", "gpio180", "gpio179", "gpio178", "gpio177", + "gpio176", "gpio173", +}; + +static const char *const pll_bist_sync_groups[] = { + "gpio184", +}; + +static const char *const pll_clk_aux_groups[] = { + "gpio135", +}; + +static const char *const prng_rosc0_groups[] = { + "gpio67", +}; + +static const char *const prng_rosc1_groups[] = { + "gpio69", +}; + +static const char *const prng_rosc2_groups[] = { + "gpio76", +}; + +static const char *const prng_rosc3_groups[] = { + "gpio74", +}; + +static const char *const qdss_cti_groups[] = { + "gpio18", "gpio19", "gpio32", "gpio73", + "gpio74", "gpio154", "gpio176", "gpio184", +}; + +static const char *const qdss_gpio_traceclk_groups[] = { + "gpio54", "gpio147", +}; + +static const char *const qdss_gpio_tracectl_groups[] = { + "gpio72", "gpio144", +}; + +static const char *const qdss_gpio_tracedata_groups[] = { + "gpio30", "gpio31", "gpio34", "gpio35", "gpio40", "gpio42", + "gpio52", "gpio53", "gpio65", "gpio66", "gpio67", "gpio114", + "gpio132", "gpio133", "gpio134", "gpio135", "gpio145", "gpio146", + "gpio155", "gpio156", "gpio163", "gpio164", "gpio167", "gpio168", + "gpio169", "gpio170", "gpio178", "gpio179", "gpio180", "gpio181", + "gpio182", +}; + +static const char *const qlink_big_enable_groups[] = { + "gpio96", +}; + +static const char *const qlink_big_request_groups[] = { + "gpio95", +}; + +static const char *const qlink_little_enable_groups[] = { + "gpio93", +}; + +static const char *const qlink_little_request_groups[] = { + "gpio92", +}; + +static const char *const qlink_wmss_groups[] = { + "gpio94", +}; + +static const char *const qspi0_groups[] = { + "gpio79", "gpio116", "gpio115", "gpio97", "gpio98", +}; + +static const char *const qspi_clk_groups[] = { + "gpio99", +}; + +static const char *const qspi_cs_groups[] = { + "gpio100", +}; + +static const char *const qup1_se0_groups[] = { + "gpio28", "gpio29", "gpio30", "gpio31", +}; + +static const char *const qup1_se1_groups[] = { + "gpio32", "gpio33", "gpio34", "gpio35", +}; + +static const char *const qup1_se2_groups[] = { + "gpio52", "gpio53", "gpio54", "gpio52", "gpio55", "gpio53", "gpio40", "gpio42", "gpio30", +}; + +static const char *const qup1_se3_groups[] = { + "gpio44", "gpio45", "gpio46", "gpio47", +}; + +static const char *const qup1_se4_groups[] = { + "gpio36", "gpio37", "gpio37", "gpio36", +}; + +static const char *const qup1_se5_groups[] = { + "gpio132", "gpio133", "gpio134", "gpio135", "gpio34", "gpio35", +}; + +static const char *const qup1_se6_groups[] = { + "gpio40", "gpio42", "gpio54", "gpio42", "gpio40", "gpio55", +}; + +static const char *const qup1_se7_groups[] = { + "gpio81", "gpio78", "gpio80", "gpio114", "gpio114", "gpio78", +}; + +static const char *const qup2_se0_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char *const qup2_se1_groups[] = { + "gpio4", "gpio5", "gpio6", "gpio7", +}; + +static const char *const qup2_se2_groups[] = { + "gpio8", "gpio9", "gpio10", "gpio11", "gpio16", "gpio17", "gpio18", +}; + +static const char *const qup2_se3_groups[] = { + "gpio79", "gpio116", "gpio97", "gpio100", "gpio100", "gpio116", +}; + +static const char *const qup2_se4_groups[] = { + "gpio12", "gpio13", "gpio26", "gpio27", +}; + +static const char *const qup2_se5_groups[] = { + "gpio16", "gpio17", "gpio18", "gpio19", +}; + +static const char *const qup2_se6_groups[] = { + "gpio20", "gpio21", "gpio22", "gpio23", +}; + +static const char *const qup2_se7_groups[] = { + "gpio27", "gpio26", "gpio13", "gpio12", +}; + +static const char *const resout_gpio_groups[] = { + "gpio63", + "gpio69", + "gpio175", +}; + +static const char *const sd_write_protect_groups[] = { + "gpio57", +}; + +static const char *const sdc1_groups[] = { + "gpio121", "gpio123", "gpio124", "gpio125", + "gpio126", "gpio127", "gpio128", "gpio129", + "gpio130", "gpio131", "gpio120", +}; + +static const char *const sdc2_groups[] = { + "gpio38", "gpio39", "gpio48", "gpio49", + "gpio51", "gpio62", +}; + +static const char *const sdc2_fb_clk_groups[] = { + "gpio50", +}; + +static const char *const tb_trig_sdc1_groups[] = { + "gpio34", +}; + +static const char *const tb_trig_sdc2_groups[] = { + "gpio35", +}; + +static const char *const tmess_prng0_groups[] = { + "gpio73", +}; + +static const char *const tmess_prng1_groups[] = { + "gpio72", +}; + +static const char *const tmess_prng2_groups[] = { + "gpio70", +}; + +static const char *const tmess_prng3_groups[] = { + "gpio71", +}; + +static const char *const tsense_pwm1_groups[] = { + "gpio56", +}; + +static const char *const tsense_pwm2_groups[] = { + "gpio56", +}; + +static const char *const tsense_pwm3_groups[] = { + "gpio56", +}; + +static const char *const tsense_pwm4_groups[] = { + "gpio56", +}; + +static const char *const uim0_clk_groups[] = { + "gpio85", +}; + +static const char *const uim0_data_groups[] = { + "gpio84", +}; + +static const char *const uim0_present_groups[] = { + "gpio87", +}; + +static const char *const uim0_reset_groups[] = { + "gpio86", +}; + +static const char *const uim1_clk_groups[] = { + "gpio98", "gpio89", +}; + +static const char *const uim1_data_groups[] = { + "gpio97", "gpio88", +}; + +static const char *const uim1_present_groups[] = { + "gpio100", "gpio91", +}; + +static const char *const uim1_reset_groups[] = { + "gpio99", "gpio90", +}; + +static const char *const usb0_hs_groups[] = { + "gpio56", +}; + +static const char *const usb_phy_groups[] = { + "gpio122", +}; + +static const char *const vfr_0_groups[] = { + "gpio63", +}; + +static const char *const vfr_1_groups[] = { + "gpio117", +}; + +static const char *const vsense_trigger_mirnat_groups[] = { + "gpio52", +}; + +static const char *const wcn_sw_ctrl_groups[] = { + "gpio81", +}; + +static const struct pinfunction eliza_functions[] = { + MSM_GPIO_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(aoss_cti), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_usb), + MSM_PIN_FUNCTION(audio_ext_mclk0), + MSM_PIN_FUNCTION(audio_ref_clk), + MSM_PIN_FUNCTION(cam_mclk), + MSM_PIN_FUNCTION(cci_async_in), + MSM_PIN_FUNCTION(cci_i2c_scl), + MSM_PIN_FUNCTION(cci_i2c_sda), + MSM_PIN_FUNCTION(cci_timer), + MSM_PIN_FUNCTION(coex_uart1_rx), + MSM_PIN_FUNCTION(coex_uart1_tx), + MSM_PIN_FUNCTION(coex_uart2_rx), + MSM_PIN_FUNCTION(coex_uart2_tx), + MSM_PIN_FUNCTION(dbg_out_clk), + MSM_PIN_FUNCTION(ddr_bist_complete), + MSM_PIN_FUNCTION(ddr_bist_fail), + MSM_PIN_FUNCTION(ddr_bist_start), + MSM_PIN_FUNCTION(ddr_bist_stop), + MSM_PIN_FUNCTION(ddr_pxi0), + MSM_PIN_FUNCTION(ddr_pxi1), + MSM_PIN_FUNCTION(dp0_hot), + MSM_PIN_FUNCTION(egpio), + MSM_PIN_FUNCTION(gcc_gp1), + MSM_PIN_FUNCTION(gcc_gp2), + MSM_PIN_FUNCTION(gcc_gp3), + MSM_PIN_FUNCTION(gnss_adc0), + MSM_PIN_FUNCTION(gnss_adc1), + MSM_PIN_FUNCTION(hdmi_ddc_scl), + MSM_PIN_FUNCTION(hdmi_ddc_sda), + MSM_PIN_FUNCTION(hdmi_dtest0), + MSM_PIN_FUNCTION(hdmi_dtest1), + MSM_PIN_FUNCTION(hdmi_hot_plug), + MSM_PIN_FUNCTION(hdmi_pixel_clk), + MSM_PIN_FUNCTION(hdmi_rcv_det), + MSM_PIN_FUNCTION(hdmi_tx_cec), + MSM_PIN_FUNCTION(host2wlan_sol), + MSM_PIN_FUNCTION(i2s0_data0), + MSM_PIN_FUNCTION(i2s0_data1), + MSM_PIN_FUNCTION(i2s0_sck), + MSM_PIN_FUNCTION(i2s0_ws), + MSM_PIN_FUNCTION(ibi_i3c), + MSM_PIN_FUNCTION(jitter_bist), + MSM_PIN_FUNCTION(mdp_esync0_out), + MSM_PIN_FUNCTION(mdp_esync1_out), + MSM_PIN_FUNCTION(mdp_vsync), + MSM_PIN_FUNCTION(mdp_vsync0_out), + MSM_PIN_FUNCTION(mdp_vsync11_out), + MSM_PIN_FUNCTION(mdp_vsync1_out), + MSM_PIN_FUNCTION(mdp_vsync2_out), + MSM_PIN_FUNCTION(mdp_vsync3_out), + MSM_PIN_FUNCTION(mdp_vsync_e), + MSM_PIN_FUNCTION(nav_gpio0), + MSM_PIN_FUNCTION(nav_gpio1), + MSM_PIN_FUNCTION(nav_gpio2), + MSM_PIN_FUNCTION(nav_gpio3), + MSM_PIN_FUNCTION(pcie0_clk_req_n), + MSM_PIN_FUNCTION(pcie1_clk_req_n), + MSM_PIN_FUNCTION(phase_flag), + MSM_PIN_FUNCTION(pll_bist_sync), + MSM_PIN_FUNCTION(pll_clk_aux), + MSM_PIN_FUNCTION(prng_rosc0), + MSM_PIN_FUNCTION(prng_rosc1), + MSM_PIN_FUNCTION(prng_rosc2), + MSM_PIN_FUNCTION(prng_rosc3), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qdss_gpio_traceclk), + MSM_PIN_FUNCTION(qdss_gpio_tracectl), + MSM_PIN_FUNCTION(qdss_gpio_tracedata), + MSM_PIN_FUNCTION(qlink_big_enable), + MSM_PIN_FUNCTION(qlink_big_request), + MSM_PIN_FUNCTION(qlink_little_enable), + MSM_PIN_FUNCTION(qlink_little_request), + MSM_PIN_FUNCTION(qlink_wmss), + MSM_PIN_FUNCTION(qspi0), + MSM_PIN_FUNCTION(qspi_clk), + MSM_PIN_FUNCTION(qspi_cs), + MSM_PIN_FUNCTION(qup1_se0), + MSM_PIN_FUNCTION(qup1_se1), + MSM_PIN_FUNCTION(qup1_se2), + MSM_PIN_FUNCTION(qup1_se3), + MSM_PIN_FUNCTION(qup1_se4), + MSM_PIN_FUNCTION(qup1_se5), + MSM_PIN_FUNCTION(qup1_se6), + MSM_PIN_FUNCTION(qup1_se7), + MSM_PIN_FUNCTION(qup2_se0), + MSM_PIN_FUNCTION(qup2_se1), + MSM_PIN_FUNCTION(qup2_se2), + MSM_PIN_FUNCTION(qup2_se3), + MSM_PIN_FUNCTION(qup2_se4), + MSM_PIN_FUNCTION(qup2_se5), + MSM_PIN_FUNCTION(qup2_se6), + MSM_PIN_FUNCTION(qup2_se7), + MSM_PIN_FUNCTION(resout_gpio), + MSM_PIN_FUNCTION(sd_write_protect), + MSM_PIN_FUNCTION(sdc1), + MSM_PIN_FUNCTION(sdc2), + MSM_PIN_FUNCTION(sdc2_fb_clk), + MSM_PIN_FUNCTION(tb_trig_sdc1), + MSM_PIN_FUNCTION(tb_trig_sdc2), + MSM_PIN_FUNCTION(tmess_prng0), + MSM_PIN_FUNCTION(tmess_prng1), + MSM_PIN_FUNCTION(tmess_prng2), + MSM_PIN_FUNCTION(tmess_prng3), + MSM_PIN_FUNCTION(tsense_pwm1), + MSM_PIN_FUNCTION(tsense_pwm2), + MSM_PIN_FUNCTION(tsense_pwm3), + MSM_PIN_FUNCTION(tsense_pwm4), + MSM_PIN_FUNCTION(uim0_clk), + MSM_PIN_FUNCTION(uim0_data), + MSM_PIN_FUNCTION(uim0_present), + MSM_PIN_FUNCTION(uim0_reset), + MSM_PIN_FUNCTION(uim1_clk), + MSM_PIN_FUNCTION(uim1_data), + MSM_PIN_FUNCTION(uim1_present), + MSM_PIN_FUNCTION(uim1_reset), + MSM_PIN_FUNCTION(usb0_hs), + MSM_PIN_FUNCTION(usb_phy), + MSM_PIN_FUNCTION(vfr_0), + MSM_PIN_FUNCTION(vfr_1), + MSM_PIN_FUNCTION(vsense_trigger_mirnat), + MSM_PIN_FUNCTION(wcn_sw_ctrl), +}; + +/* Every pin is maintained as a single group, and missing or non-existing pin + * would be maintained as dummy group to synchronize pin group index with + * pin descriptor registered with pinctrl core. + * Clients would not be able to request these dummy pin groups. + */ +static const struct msm_pingroup eliza_groups[] = { + [0] = PINGROUP(0, qup2_se0, ibi_i3c, aoss_cti, _, _, _, _, _, _, _, _), + [1] = PINGROUP(1, qup2_se0, ibi_i3c, aoss_cti, _, _, _, _, _, _, _, _), + [2] = PINGROUP(2, qup2_se0, _, _, _, _, _, _, _, _, _, _), + [3] = PINGROUP(3, qup2_se0, _, _, _, _, _, _, _, _, _, _), + [4] = PINGROUP(4, qup2_se1, ibi_i3c, _, _, _, _, _, _, _, _, _), + [5] = PINGROUP(5, qup2_se1, ibi_i3c, _, _, _, _, _, _, _, _, _), + [6] = PINGROUP(6, qup2_se1, hdmi_ddc_scl, _, _, _, _, _, _, _, _, _), + [7] = PINGROUP(7, qup2_se1, hdmi_ddc_sda, _, _, _, _, _, _, _, _, _), + [8] = PINGROUP(8, qup2_se2, _, _, _, _, _, _, _, _, _, _), + [9] = PINGROUP(9, qup2_se2, _, _, _, _, _, _, _, _, _, _), + [10] = PINGROUP(10, qup2_se2, _, _, _, _, _, _, _, _, _, _), + [11] = PINGROUP(11, qup2_se2, _, _, _, _, _, _, _, _, _, _), + [12] = PINGROUP(12, qup2_se4, ibi_i3c, mdp_esync1_out, qup2_se7, _, _, _, _, _, _, _), + [13] = PINGROUP(13, qup2_se4, ibi_i3c, mdp_vsync_e, mdp_esync0_out, qup2_se7, _, _, _, _, _, _), + [14] = PINGROUP(14, _, _, _, _, _, _, _, _, _, _, _), + [15] = PINGROUP(15, _, _, _, _, _, _, _, _, _, _, _), + [16] = PINGROUP(16, qup2_se5, qup2_se2, mdp_vsync, mdp_vsync2_out, mdp_vsync3_out, _, _, _, _, _, _), + [17] = PINGROUP(17, qup2_se5, qup2_se2, mdp_vsync, mdp_vsync0_out, mdp_vsync1_out, _, _, _, _, _, _), + [18] = PINGROUP(18, qup2_se5, qup2_se2, hdmi_pixel_clk, _, qdss_cti, _, _, _, _, _, _), + [19] = PINGROUP(19, qup2_se5, hdmi_rcv_det, _, qdss_cti, _, _, _, _, _, _, _), + [20] = PINGROUP(20, qup2_se6, _, _, _, _, _, _, _, _, _, _), + [21] = PINGROUP(21, qup2_se6, _, _, _, _, _, _, _, _, _, _), + [22] = PINGROUP(22, qup2_se6, _, _, _, _, _, _, _, _, _, _), + [23] = PINGROUP(23, qup2_se6, _, _, _, _, _, _, _, _, _, _), + [24] = PINGROUP(24, _, _, _, _, _, _, _, _, _, _, _), + [25] = PINGROUP(25, _, _, _, _, _, _, _, _, _, _, _), + [26] = PINGROUP(26, qup2_se4, aoss_cti, qup2_se7, _, _, _, _, _, _, _, _), + [27] = PINGROUP(27, qup2_se4, aoss_cti, mdp_vsync11_out, qup2_se7, gcc_gp1, _, _, _, _, _, _), + [28] = PINGROUP(28, qup1_se0, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [29] = PINGROUP(29, qup1_se0, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [30] = PINGROUP(30, qup1_se0, qup1_se2, cci_async_in, gcc_gp3, qdss_gpio_tracedata, _, _, _, _, _, egpio), + [31] = PINGROUP(31, qup1_se0, cci_async_in, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [32] = PINGROUP(32, qup1_se1, ibi_i3c, audio_ref_clk, gcc_gp2, qdss_cti, _, _, _, _, _, _), + [33] = PINGROUP(33, qup1_se1, ibi_i3c, host2wlan_sol, gcc_gp3, _, _, _, _, _, _, _), + [34] = PINGROUP(34, qup1_se1, qup1_se5, tb_trig_sdc1, ddr_bist_start, qdss_gpio_tracedata, _, _, _, _, _, _), + [35] = PINGROUP(35, qup1_se1, qup1_se5, tb_trig_sdc2, gcc_gp2, qdss_gpio_tracedata, _, _, _, _, _, _), + [36] = PINGROUP(36, qup1_se4, qup1_se4, ibi_i3c, _, _, _, _, _, _, _, _), + [37] = PINGROUP(37, qup1_se4, qup1_se4, ibi_i3c, _, _, _, _, _, _, _, _), + [38] = PINGROUP(38, _, _, _, _, _, _, _, _, _, _, _), + [39] = PINGROUP(39, _, _, _, _, _, _, _, _, _, _, _), + [40] = PINGROUP(40, qup1_se6, qup1_se2, qup1_se6, _, qdss_gpio_tracedata, gnss_adc1, ddr_pxi1, _, _, _, _), + [41] = PINGROUP(41, _, _, _, _, _, _, _, _, _, _, _), + [42] = PINGROUP(42, qup1_se6, qup1_se2, qup1_se6, qdss_gpio_tracedata, gnss_adc0, ddr_pxi1, _, _, _, _, _), + [43] = PINGROUP(43, _, _, _, _, _, _, _, _, _, _, _), + [44] = PINGROUP(44, qup1_se3, _, _, _, _, _, _, _, _, _, _), + [45] = PINGROUP(45, qup1_se3, _, _, _, _, _, _, _, _, _, _), + [46] = PINGROUP(46, qup1_se3, hdmi_tx_cec, _, _, _, _, _, _, _, _, _), + [47] = PINGROUP(47, qup1_se3, hdmi_hot_plug, _, _, _, _, _, _, _, _, _), + [48] = PINGROUP(48, _, _, _, _, _, _, _, _, _, _, _), + [49] = PINGROUP(49, _, _, _, _, _, _, _, _, _, _, _), + [50] = PINGROUP(50, sdc2_fb_clk, _, _, _, _, _, _, _, _, _, _), + [51] = PINGROUP(51, _, _, _, _, _, _, _, _, _, _, _), + [52] = PINGROUP(52, qup1_se2, pcie1_clk_req_n, qup1_se2, ddr_bist_complete, qdss_gpio_tracedata, _, vsense_trigger_mirnat, _, _, _, _), + [53] = PINGROUP(53, qup1_se2, qup1_se2, gcc_gp1, ddr_bist_stop, _, qdss_gpio_tracedata, _, _, _, _, _), + [54] = PINGROUP(54, qup1_se2, qup1_se6, qdss_gpio_tracedata, gnss_adc1, atest_usb, ddr_pxi0, _, _, _, _, _), + [55] = PINGROUP(55, qup1_se2, dp0_hot, qup1_se6, _, gnss_adc0, atest_usb, ddr_pxi0, _, _, _, _), + [56] = PINGROUP(56, usb0_hs, tsense_pwm1, tsense_pwm2, tsense_pwm3, tsense_pwm4, _, _, _, _, _, _), + [57] = PINGROUP(57, sd_write_protect, _, _, _, _, _, _, _, _, _, _), + [58] = PINGROUP(58, _, _, _, _, _, _, _, _, _, _, _), + [59] = PINGROUP(59, _, _, _, _, _, _, _, _, _, _, _), + [60] = PINGROUP(60, i2s0_sck, _, _, _, _, _, _, _, _, _, _), + [61] = PINGROUP(61, i2s0_ws, _, _, _, _, _, _, _, _, _, _), + [62] = PINGROUP(62, _, _, _, _, _, _, _, _, _, _, _), + [63] = PINGROUP(63, resout_gpio, i2s0_data1, cci_timer, vfr_0, _, _, _, _, _, _, _), + [64] = PINGROUP(64, i2s0_data0, _, _, _, _, _, _, _, _, _, _), + [65] = PINGROUP(65, cam_mclk, _, qdss_gpio_tracedata, _, _, _, _, _, _, _, _), + [66] = PINGROUP(66, cam_mclk, _, qdss_gpio_tracedata, _, _, _, _, _, _, _, _), + [67] = PINGROUP(67, cam_mclk, prng_rosc0, _, qdss_gpio_tracedata, _, _, _, _, _, _, _), + [68] = PINGROUP(68, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [69] = PINGROUP(69, cam_mclk, audio_ext_mclk0, resout_gpio, prng_rosc1, _, _, _, _, _, _, _), + [70] = PINGROUP(70, cci_i2c_sda, tmess_prng2, _, phase_flag, atest_char, _, _, _, _, _, _), + [71] = PINGROUP(71, cci_i2c_scl, tmess_prng3, _, phase_flag, atest_char, _, _, _, _, _, _), + [72] = PINGROUP(72, cci_i2c_sda, tmess_prng1, qdss_gpio_tracedata, atest_char, _, _, _, _, _, _, _), + [73] = PINGROUP(73, cci_i2c_scl, tmess_prng0, qdss_cti, atest_char, _, _, _, _, _, _, _), + [74] = PINGROUP(74, cci_i2c_sda, prng_rosc3, qdss_cti, atest_char, _, _, _, _, _, _, _), + [75] = PINGROUP(75, cci_i2c_scl, _, phase_flag, _, _, _, _, _, _, _, _), + [76] = PINGROUP(76, cci_i2c_sda, cci_timer, prng_rosc2, _, phase_flag, _, _, _, _, _, _), + [77] = PINGROUP(77, cci_i2c_scl, jitter_bist, _, _, _, _, _, _, _, _, _), + [78] = PINGROUP(78, qup1_se7, qup1_se7, _, phase_flag, _, _, _, _, _, _, _), + [79] = PINGROUP(79, qspi0, mdp_vsync, qup2_se3, _, _, _, _, _, _, _, _), + [80] = PINGROUP(80, pcie0_clk_req_n, qup1_se7, _, phase_flag, _, _, _, _, _, _, _), + [81] = PINGROUP(81, wcn_sw_ctrl, qup1_se7, dbg_out_clk, _, _, _, _, _, _, _, _), + [82] = PINGROUP(82, _, _, _, _, _, _, _, _, _, _, _), + [83] = PINGROUP(83, _, _, _, _, _, _, _, _, _, _, _), + [84] = PINGROUP(84, uim0_data, _, _, _, _, _, _, _, _, _, _), + [85] = PINGROUP(85, uim0_clk, _, _, _, _, _, _, _, _, _, _), + [86] = PINGROUP(86, uim0_reset, _, _, _, _, _, _, _, _, _, _), + [87] = PINGROUP(87, uim0_present, _, _, _, _, _, _, _, _, _, _), + [88] = PINGROUP(88, uim1_data, _, _, _, _, _, _, _, _, _, _), + [89] = PINGROUP(89, uim1_clk, _, _, _, _, _, _, _, _, _, _), + [90] = PINGROUP(90, uim1_reset, _, _, _, _, _, _, _, _, _, _), + [91] = PINGROUP(91, uim1_present, _, _, _, _, _, _, _, _, _, _), + [92] = PINGROUP(92, qlink_little_request, _, _, _, _, _, _, _, _, _, _), + [93] = PINGROUP(93, qlink_little_enable, _, _, _, _, _, _, _, _, _, _), + [94] = PINGROUP(94, qlink_wmss, _, _, _, _, _, _, _, _, _, _), + [95] = PINGROUP(95, qlink_big_request, _, _, _, _, _, _, _, _, _, _), + [96] = PINGROUP(96, qlink_big_enable, _, _, _, _, _, _, _, _, _, _), + [97] = PINGROUP(97, uim1_data, qspi0, qup2_se3, _, _, _, _, _, _, _, _), + [98] = PINGROUP(98, uim1_clk, qspi0, _, _, _, _, _, _, _, _, _), + [99] = PINGROUP(99, uim1_reset, qspi0, _, _, _, _, _, _, _, _, _), + [100] = PINGROUP(100, uim1_present, qspi0, qup2_se3, coex_uart2_tx, qup2_se3, mdp_vsync, _, _, _, _, _), + [101] = PINGROUP(101, _, _, _, _, _, _, _, _, _, _, _), + [102] = PINGROUP(102, _, _, _, _, _, _, _, _, _, _, _), + [103] = PINGROUP(103, _, _, _, _, _, _, _, _, _, _, _), + [104] = PINGROUP(104, _, _, _, _, _, _, _, _, _, _, _), + [105] = PINGROUP(105, _, _, _, _, _, _, _, _, _, _, _), + [106] = PINGROUP(106, _, _, _, _, _, _, _, _, _, _, _), + [107] = PINGROUP(107, _, _, _, _, _, _, _, _, _, _, _), + [108] = PINGROUP(108, _, _, _, _, _, _, _, _, _, _, _), + [109] = PINGROUP(109, _, _, _, _, _, _, _, _, _, _, _), + [110] = PINGROUP(110, _, _, _, _, _, _, _, _, _, _, _), + [111] = PINGROUP(111, coex_uart1_tx, _, _, _, _, _, _, _, _, _, _), + [112] = PINGROUP(112, coex_uart1_rx, _, _, _, _, _, _, _, _, _, _), + [113] = PINGROUP(113, _, nav_gpio3, _, _, _, _, _, _, _, _, _), + [114] = PINGROUP(114, qup1_se7, qup1_se7, _, qdss_gpio_tracedata, _, _, _, _, _, _, _), + [115] = PINGROUP(115, _, qspi0, cci_async_in, _, _, _, _, _, _, _, _), + [116] = PINGROUP(116, qspi0, coex_uart2_rx, qup2_se3, qup2_se3, _, _, _, _, _, _, _), + [117] = PINGROUP(117, nav_gpio1, _, vfr_1, _, _, _, _, _, _, _, _), + [118] = PINGROUP(118, nav_gpio2, _, _, _, _, _, _, _, _, _, _), + [119] = PINGROUP(119, nav_gpio0, _, _, _, _, _, _, _, _, _, _), + [120] = PINGROUP(120, sdc1, mdp_vsync, _, _, _, _, _, _, _, _, _), + [121] = PINGROUP(121, sdc1, mdp_vsync, _, _, _, _, _, _, _, _, _), + [122] = PINGROUP(122, usb_phy, _, _, _, _, _, _, _, _, _, _), + [123] = PINGROUP(123, sdc1, _, _, _, _, _, _, _, _, _, _), + [124] = PINGROUP(124, sdc1, _, _, _, _, _, _, _, _, _, _), + [125] = PINGROUP(125, sdc1, cci_timer, _, _, _, _, _, _, _, _, _), + [126] = PINGROUP(126, sdc1, cci_timer, _, _, _, _, _, _, _, _, _), + [127] = PINGROUP(127, sdc1, cci_timer, _, _, _, _, _, _, _, _, _), + [128] = PINGROUP(128, sdc1, _, _, _, _, _, _, _, _, _, _), + [129] = PINGROUP(129, sdc1, _, _, _, _, _, _, _, _, _, _), + [130] = PINGROUP(130, sdc1, _, _, _, _, _, _, _, _, _, _), + [131] = PINGROUP(131, sdc1, _, _, _, _, _, _, _, _, _, _), + [132] = PINGROUP(132, qup1_se5, _, qdss_gpio_tracedata, hdmi_dtest0, _, _, _, _, _, _, _), + [133] = PINGROUP(133, qup1_se5, _, qdss_gpio_tracedata, hdmi_dtest1, _, _, _, _, _, _, _), + [134] = PINGROUP(134, qup1_se5, qdss_gpio_tracedata, _, _, _, _, _, _, _, _, _), + [135] = PINGROUP(135, qup1_se5, _, pll_clk_aux, qdss_gpio_tracedata, _, _, _, _, _, _, _), + [136] = PINGROUP(136, _, _, _, _, _, _, _, _, _, _, _), + [137] = PINGROUP(137, _, _, _, _, _, _, _, _, _, _, _), + [138] = PINGROUP(138, _, _, _, _, _, _, _, _, _, _, egpio), + [139] = PINGROUP(139, _, _, _, _, _, _, _, _, _, _, egpio), + [140] = PINGROUP(140, _, _, _, _, _, _, _, _, _, _, egpio), + [141] = PINGROUP(141, _, _, _, _, _, _, _, _, _, _, egpio), + [142] = PINGROUP(142, _, _, _, _, _, _, _, _, _, _, egpio), + [143] = PINGROUP(143, _, _, _, _, _, _, _, _, _, _, egpio), + [144] = PINGROUP(144, _, qdss_gpio_tracedata, _, _, _, _, _, _, _, _, egpio), + [145] = PINGROUP(145, qdss_gpio_tracedata, _, _, _, _, _, _, _, _, _, egpio), + [146] = PINGROUP(146, _, qdss_gpio_tracedata, _, _, _, _, _, _, _, _, egpio), + [147] = PINGROUP(147, ddr_bist_fail, _, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [148] = PINGROUP(148, _, _, _, _, _, _, _, _, _, _, egpio), + [149] = PINGROUP(149, _, _, _, _, _, _, _, _, _, _, egpio), + [150] = PINGROUP(150, _, _, _, _, _, _, _, _, _, _, egpio), + [151] = PINGROUP(151, _, _, _, _, _, _, _, _, _, _, egpio), + [152] = PINGROUP(152, _, _, _, _, _, _, _, _, _, _, egpio), + [153] = PINGROUP(153, _, _, _, _, _, _, _, _, _, _, egpio), + [154] = PINGROUP(154, qdss_cti, _, _, _, _, _, _, _, _, _, egpio), + [155] = PINGROUP(155, _, qdss_gpio_tracedata, _, _, _, _, _, _, _, _, egpio), + [156] = PINGROUP(156, _, qdss_gpio_tracedata, _, _, _, _, _, _, _, _, egpio), + [157] = PINGROUP(157, _, phase_flag, _, _, _, _, _, _, _, _, egpio), + [158] = PINGROUP(158, _, phase_flag, _, _, _, _, _, _, _, _, egpio), + [159] = PINGROUP(159, _, phase_flag, _, _, _, _, _, _, _, _, egpio), + [160] = PINGROUP(160, _, phase_flag, _, _, _, _, _, _, _, _, egpio), + [161] = PINGROUP(161, _, phase_flag, _, _, _, _, _, _, _, _, egpio), + [162] = PINGROUP(162, _, phase_flag, _, _, _, _, _, _, _, _, egpio), + [163] = PINGROUP(163, _, phase_flag, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [164] = PINGROUP(164, _, phase_flag, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [165] = PINGROUP(165, _, phase_flag, _, _, _, _, _, _, _, _, egpio), + [166] = PINGROUP(166, _, phase_flag, _, _, _, _, _, _, _, _, egpio), + [167] = PINGROUP(167, _, phase_flag, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [168] = PINGROUP(168, _, phase_flag, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [169] = PINGROUP(169, _, phase_flag, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [170] = PINGROUP(170, _, phase_flag, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [171] = PINGROUP(171, _, phase_flag, _, _, _, _, _, _, _, _, egpio), + [172] = PINGROUP(172, _, phase_flag, _, _, _, _, _, _, _, _, egpio), + [173] = PINGROUP(173, _, phase_flag, _, _, _, _, _, _, _, _, egpio), + [174] = PINGROUP(174, _, phase_flag, _, _, _, _, _, _, _, _, egpio), + [175] = PINGROUP(175, resout_gpio, _, phase_flag, _, _, _, _, _, _, _, egpio), + [176] = PINGROUP(176, _, phase_flag, qdss_cti, _, _, _, _, _, _, _, egpio), + [177] = PINGROUP(177, _, phase_flag, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [178] = PINGROUP(178, _, phase_flag, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [179] = PINGROUP(179, _, phase_flag, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [180] = PINGROUP(180, _, phase_flag, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [181] = PINGROUP(181, _, phase_flag, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [182] = PINGROUP(182, _, phase_flag, qdss_gpio_tracedata, _, _, _, _, _, _, _, egpio), + [183] = PINGROUP(183, _, _, _, _, _, _, _, _, _, _, _), + [184] = PINGROUP(184, pll_bist_sync, qdss_cti, _, _, _, _, _, _, _, _, egpio), + [185] = UFS_RESET(ufs_reset, 0xc9004, 0xca000), +}; + +static const struct msm_gpio_wakeirq_map eliza_pdc_map[] = { + { 0, 82 }, { 3, 87 }, { 4, 90 }, { 6, 68 }, { 7, 153 }, + { 11, 85 }, { 12, 107 }, { 13, 106 }, { 16, 88 }, { 17, 70 }, + { 18, 134 }, { 19, 79 }, { 23, 80 }, { 26, 91 }, { 27, 74 }, + { 28, 137 }, { 29, 138 }, { 30, 139 }, { 31, 140 }, { 32, 117 }, + { 34, 100 }, { 35, 98 }, { 36, 141 }, { 39, 89 }, { 40, 142 }, + { 42, 143 }, { 44, 101 }, { 45, 144 }, { 46, 145 }, { 47, 146 }, + { 49, 75 }, { 51, 147 }, { 52, 148 }, { 53, 149 }, { 54, 150 }, + { 55, 151 }, { 56, 152 }, { 58, 71 }, { 59, 155 }, { 63, 99 }, + { 78, 156 }, { 79, 76 }, { 80, 157 }, { 81, 69 }, { 87, 158 }, + { 91, 67 }, { 92, 159 }, { 95, 160 }, { 98, 161 }, { 99, 162 }, + { 100, 83 }, { 108, 154 }, { 109, 84 }, { 112, 86 }, { 113, 92 }, + { 114, 93 }, { 115, 110 }, { 116, 94 }, { 117, 77 }, { 118, 108 }, + { 119, 95 }, { 120, 81 }, { 121, 96 }, { 122, 97 }, { 123, 102 }, + { 125, 103 }, { 127, 104 }, { 128, 105 }, { 129, 78 }, { 130, 112 }, + { 131, 113 }, { 133, 114 }, { 135, 115 }, { 139, 116 }, { 142, 118 }, + { 145, 109 }, { 147, 72 }, { 149, 111 }, { 154, 122 }, { 157, 119 }, + { 159, 120 }, { 161, 121 }, { 164, 123 }, { 165, 124 }, { 167, 125 }, + { 170, 126 }, { 171, 73 }, { 172, 127 }, { 173, 128 }, { 174, 129 }, + { 175, 130 }, { 176, 131 }, { 177, 132 }, { 179, 133 }, { 182, 135 }, + { 184, 136 }, +}; + +static const struct msm_pinctrl_soc_data eliza_tlmm = { + .pins = eliza_pins, + .npins = ARRAY_SIZE(eliza_pins), + .functions = eliza_functions, + .nfunctions = ARRAY_SIZE(eliza_functions), + .groups = eliza_groups, + .ngroups = ARRAY_SIZE(eliza_groups), + .ngpios = 186, + .wakeirq_map = eliza_pdc_map, + .nwakeirq_map = ARRAY_SIZE(eliza_pdc_map), + .egpio_func = 11, +}; + +static int eliza_tlmm_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &eliza_tlmm); +} + +static const struct of_device_id eliza_tlmm_of_match[] = { + { .compatible = "qcom,eliza-tlmm", }, + {}, +}; + +static struct platform_driver eliza_tlmm_driver = { + .driver = { + .name = "eliza-tlmm", + .of_match_table = eliza_tlmm_of_match, + }, + .probe = eliza_tlmm_probe, +}; + +static int __init eliza_tlmm_init(void) +{ + return platform_driver_register(&eliza_tlmm_driver); +} +arch_initcall(eliza_tlmm_init); + +static void __exit eliza_tlmm_exit(void) +{ + platform_driver_unregister(&eliza_tlmm_driver); +} +module_exit(eliza_tlmm_exit); + +MODULE_DESCRIPTION("QTI Eliza TLMM driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, eliza_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-glymur.c b/drivers/pinctrl/qcom/pinctrl-glymur.c index 44f9745325b7..9838c7839923 100644 --- a/drivers/pinctrl/qcom/pinctrl-glymur.c +++ b/drivers/pinctrl/qcom/pinctrl-glymur.c @@ -21,7 +21,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -64,7 +63,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -89,7 +87,6 @@ .io_reg = io, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ @@ -1812,6 +1809,6 @@ static void __exit glymur_tlmm_exit(void) } module_exit(glymur_tlmm_exit); -MODULE_DESCRIPTION("QTI GLYMUR TLMM driver"); +MODULE_DESCRIPTION("QTI Glymur TLMM driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(of, glymur_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-hawi.c b/drivers/pinctrl/qcom/pinctrl-hawi.c new file mode 100644 index 000000000000..5c7894f3b9cb --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-hawi.c @@ -0,0 +1,1610 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + msm_mux_##f10, \ + msm_mux_##f11 /* egpio mode */ \ + }, \ + .nfuncs = 12, \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .egpio_enable = 12, \ + .egpio_present = 11, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_wakeup_present_bit = 6, \ + .intr_wakeup_enable_bit = 7, \ + .intr_target_bit = 8, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +#define UFS_RESET(pg_name, ctl, io) \ + { \ + .grp = PINCTRL_PINGROUP(#pg_name, \ + pg_name##_pins, \ + ARRAY_SIZE(pg_name##_pins)), \ + .ctl_reg = ctl, \ + .io_reg = io, \ + .intr_cfg_reg = 0, \ + .intr_status_reg = 0, \ + .intr_target_reg = 0, \ + .mux_bit = -1, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + .intr_enable_bit = -1, \ + .intr_status_bit = -1, \ + .intr_target_bit = -1, \ + .intr_raw_status_bit = -1, \ + .intr_polarity_bit = -1, \ + .intr_detection_bit = -1, \ + .intr_detection_width = -1, \ + } + +static const struct pinctrl_pin_desc hawi_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(63, "GPIO_63"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(67, "GPIO_67"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(73, "GPIO_73"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(77, "GPIO_77"), + PINCTRL_PIN(78, "GPIO_78"), + PINCTRL_PIN(79, "GPIO_79"), + PINCTRL_PIN(80, "GPIO_80"), + PINCTRL_PIN(81, "GPIO_81"), + PINCTRL_PIN(82, "GPIO_82"), + PINCTRL_PIN(83, "GPIO_83"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(94, "GPIO_94"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(103, "GPIO_103"), + PINCTRL_PIN(104, "GPIO_104"), + PINCTRL_PIN(105, "GPIO_105"), + PINCTRL_PIN(106, "GPIO_106"), + PINCTRL_PIN(107, "GPIO_107"), + PINCTRL_PIN(108, "GPIO_108"), + PINCTRL_PIN(109, "GPIO_109"), + PINCTRL_PIN(110, "GPIO_110"), + PINCTRL_PIN(111, "GPIO_111"), + PINCTRL_PIN(112, "GPIO_112"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(123, "GPIO_123"), + PINCTRL_PIN(124, "GPIO_124"), + PINCTRL_PIN(125, "GPIO_125"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(127, "GPIO_127"), + PINCTRL_PIN(128, "GPIO_128"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "GPIO_133"), + PINCTRL_PIN(134, "GPIO_134"), + PINCTRL_PIN(135, "GPIO_135"), + PINCTRL_PIN(136, "GPIO_136"), + PINCTRL_PIN(137, "GPIO_137"), + PINCTRL_PIN(138, "GPIO_138"), + PINCTRL_PIN(139, "GPIO_139"), + PINCTRL_PIN(140, "GPIO_140"), + PINCTRL_PIN(141, "GPIO_141"), + PINCTRL_PIN(142, "GPIO_142"), + PINCTRL_PIN(143, "GPIO_143"), + PINCTRL_PIN(144, "GPIO_144"), + PINCTRL_PIN(145, "GPIO_145"), + PINCTRL_PIN(146, "GPIO_146"), + PINCTRL_PIN(147, "GPIO_147"), + PINCTRL_PIN(148, "GPIO_148"), + PINCTRL_PIN(149, "GPIO_149"), + PINCTRL_PIN(150, "GPIO_150"), + PINCTRL_PIN(151, "GPIO_151"), + PINCTRL_PIN(152, "GPIO_152"), + PINCTRL_PIN(153, "GPIO_153"), + PINCTRL_PIN(154, "GPIO_154"), + PINCTRL_PIN(155, "GPIO_155"), + PINCTRL_PIN(156, "GPIO_156"), + PINCTRL_PIN(157, "GPIO_157"), + PINCTRL_PIN(158, "GPIO_158"), + PINCTRL_PIN(159, "GPIO_159"), + PINCTRL_PIN(160, "GPIO_160"), + PINCTRL_PIN(161, "GPIO_161"), + PINCTRL_PIN(162, "GPIO_162"), + PINCTRL_PIN(163, "GPIO_163"), + PINCTRL_PIN(164, "GPIO_164"), + PINCTRL_PIN(165, "GPIO_165"), + PINCTRL_PIN(166, "GPIO_166"), + PINCTRL_PIN(167, "GPIO_167"), + PINCTRL_PIN(168, "GPIO_168"), + PINCTRL_PIN(169, "GPIO_169"), + PINCTRL_PIN(170, "GPIO_170"), + PINCTRL_PIN(171, "GPIO_171"), + PINCTRL_PIN(172, "GPIO_172"), + PINCTRL_PIN(173, "GPIO_173"), + PINCTRL_PIN(174, "GPIO_174"), + PINCTRL_PIN(175, "GPIO_175"), + PINCTRL_PIN(176, "GPIO_176"), + PINCTRL_PIN(177, "GPIO_177"), + PINCTRL_PIN(178, "GPIO_178"), + PINCTRL_PIN(179, "GPIO_179"), + PINCTRL_PIN(180, "GPIO_180"), + PINCTRL_PIN(181, "GPIO_181"), + PINCTRL_PIN(182, "GPIO_182"), + PINCTRL_PIN(183, "GPIO_183"), + PINCTRL_PIN(184, "GPIO_184"), + PINCTRL_PIN(185, "GPIO_185"), + PINCTRL_PIN(186, "GPIO_186"), + PINCTRL_PIN(187, "GPIO_187"), + PINCTRL_PIN(188, "GPIO_188"), + PINCTRL_PIN(189, "GPIO_189"), + PINCTRL_PIN(190, "GPIO_190"), + PINCTRL_PIN(191, "GPIO_191"), + PINCTRL_PIN(192, "GPIO_192"), + PINCTRL_PIN(193, "GPIO_193"), + PINCTRL_PIN(194, "GPIO_194"), + PINCTRL_PIN(195, "GPIO_195"), + PINCTRL_PIN(196, "GPIO_196"), + PINCTRL_PIN(197, "GPIO_197"), + PINCTRL_PIN(198, "GPIO_198"), + PINCTRL_PIN(199, "GPIO_199"), + PINCTRL_PIN(200, "GPIO_200"), + PINCTRL_PIN(201, "GPIO_201"), + PINCTRL_PIN(202, "GPIO_202"), + PINCTRL_PIN(203, "GPIO_203"), + PINCTRL_PIN(204, "GPIO_204"), + PINCTRL_PIN(205, "GPIO_205"), + PINCTRL_PIN(206, "GPIO_206"), + PINCTRL_PIN(207, "GPIO_207"), + PINCTRL_PIN(208, "GPIO_208"), + PINCTRL_PIN(209, "GPIO_209"), + PINCTRL_PIN(210, "GPIO_210"), + PINCTRL_PIN(211, "GPIO_211"), + PINCTRL_PIN(212, "GPIO_212"), + PINCTRL_PIN(213, "GPIO_213"), + PINCTRL_PIN(214, "GPIO_214"), + PINCTRL_PIN(215, "GPIO_215"), + PINCTRL_PIN(216, "GPIO_216"), + PINCTRL_PIN(217, "GPIO_217"), + PINCTRL_PIN(218, "GPIO_218"), + PINCTRL_PIN(219, "GPIO_219"), + PINCTRL_PIN(220, "GPIO_220"), + PINCTRL_PIN(221, "GPIO_221"), + PINCTRL_PIN(222, "GPIO_222"), + PINCTRL_PIN(223, "GPIO_223"), + PINCTRL_PIN(224, "GPIO_224"), + PINCTRL_PIN(225, "GPIO_225"), + PINCTRL_PIN(226, "UFS_RESET"), + PINCTRL_PIN(227, "SDC2_CLK"), + PINCTRL_PIN(228, "SDC2_CMD"), + PINCTRL_PIN(229, "SDC2_DATA"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); +DECLARE_MSM_GPIO_PINS(54); +DECLARE_MSM_GPIO_PINS(55); +DECLARE_MSM_GPIO_PINS(56); +DECLARE_MSM_GPIO_PINS(57); +DECLARE_MSM_GPIO_PINS(58); +DECLARE_MSM_GPIO_PINS(59); +DECLARE_MSM_GPIO_PINS(60); +DECLARE_MSM_GPIO_PINS(61); +DECLARE_MSM_GPIO_PINS(62); +DECLARE_MSM_GPIO_PINS(63); +DECLARE_MSM_GPIO_PINS(64); +DECLARE_MSM_GPIO_PINS(65); +DECLARE_MSM_GPIO_PINS(66); +DECLARE_MSM_GPIO_PINS(67); +DECLARE_MSM_GPIO_PINS(68); +DECLARE_MSM_GPIO_PINS(69); +DECLARE_MSM_GPIO_PINS(70); +DECLARE_MSM_GPIO_PINS(71); +DECLARE_MSM_GPIO_PINS(72); +DECLARE_MSM_GPIO_PINS(73); +DECLARE_MSM_GPIO_PINS(74); +DECLARE_MSM_GPIO_PINS(75); +DECLARE_MSM_GPIO_PINS(76); +DECLARE_MSM_GPIO_PINS(77); +DECLARE_MSM_GPIO_PINS(78); +DECLARE_MSM_GPIO_PINS(79); +DECLARE_MSM_GPIO_PINS(80); +DECLARE_MSM_GPIO_PINS(81); +DECLARE_MSM_GPIO_PINS(82); +DECLARE_MSM_GPIO_PINS(83); +DECLARE_MSM_GPIO_PINS(84); +DECLARE_MSM_GPIO_PINS(85); +DECLARE_MSM_GPIO_PINS(86); +DECLARE_MSM_GPIO_PINS(87); +DECLARE_MSM_GPIO_PINS(88); +DECLARE_MSM_GPIO_PINS(89); +DECLARE_MSM_GPIO_PINS(90); +DECLARE_MSM_GPIO_PINS(91); +DECLARE_MSM_GPIO_PINS(92); +DECLARE_MSM_GPIO_PINS(93); +DECLARE_MSM_GPIO_PINS(94); +DECLARE_MSM_GPIO_PINS(95); +DECLARE_MSM_GPIO_PINS(96); +DECLARE_MSM_GPIO_PINS(97); +DECLARE_MSM_GPIO_PINS(98); +DECLARE_MSM_GPIO_PINS(99); +DECLARE_MSM_GPIO_PINS(100); +DECLARE_MSM_GPIO_PINS(101); +DECLARE_MSM_GPIO_PINS(102); +DECLARE_MSM_GPIO_PINS(103); +DECLARE_MSM_GPIO_PINS(104); +DECLARE_MSM_GPIO_PINS(105); +DECLARE_MSM_GPIO_PINS(106); +DECLARE_MSM_GPIO_PINS(107); +DECLARE_MSM_GPIO_PINS(108); +DECLARE_MSM_GPIO_PINS(109); +DECLARE_MSM_GPIO_PINS(110); +DECLARE_MSM_GPIO_PINS(111); +DECLARE_MSM_GPIO_PINS(112); +DECLARE_MSM_GPIO_PINS(113); +DECLARE_MSM_GPIO_PINS(114); +DECLARE_MSM_GPIO_PINS(115); +DECLARE_MSM_GPIO_PINS(116); +DECLARE_MSM_GPIO_PINS(117); +DECLARE_MSM_GPIO_PINS(118); +DECLARE_MSM_GPIO_PINS(119); +DECLARE_MSM_GPIO_PINS(120); +DECLARE_MSM_GPIO_PINS(121); +DECLARE_MSM_GPIO_PINS(122); +DECLARE_MSM_GPIO_PINS(123); +DECLARE_MSM_GPIO_PINS(124); +DECLARE_MSM_GPIO_PINS(125); +DECLARE_MSM_GPIO_PINS(126); +DECLARE_MSM_GPIO_PINS(127); +DECLARE_MSM_GPIO_PINS(128); +DECLARE_MSM_GPIO_PINS(129); +DECLARE_MSM_GPIO_PINS(130); +DECLARE_MSM_GPIO_PINS(131); +DECLARE_MSM_GPIO_PINS(132); +DECLARE_MSM_GPIO_PINS(133); +DECLARE_MSM_GPIO_PINS(134); +DECLARE_MSM_GPIO_PINS(135); +DECLARE_MSM_GPIO_PINS(136); +DECLARE_MSM_GPIO_PINS(137); +DECLARE_MSM_GPIO_PINS(138); +DECLARE_MSM_GPIO_PINS(139); +DECLARE_MSM_GPIO_PINS(140); +DECLARE_MSM_GPIO_PINS(141); +DECLARE_MSM_GPIO_PINS(142); +DECLARE_MSM_GPIO_PINS(143); +DECLARE_MSM_GPIO_PINS(144); +DECLARE_MSM_GPIO_PINS(145); +DECLARE_MSM_GPIO_PINS(146); +DECLARE_MSM_GPIO_PINS(147); +DECLARE_MSM_GPIO_PINS(148); +DECLARE_MSM_GPIO_PINS(149); +DECLARE_MSM_GPIO_PINS(150); +DECLARE_MSM_GPIO_PINS(151); +DECLARE_MSM_GPIO_PINS(152); +DECLARE_MSM_GPIO_PINS(153); +DECLARE_MSM_GPIO_PINS(154); +DECLARE_MSM_GPIO_PINS(155); +DECLARE_MSM_GPIO_PINS(156); +DECLARE_MSM_GPIO_PINS(157); +DECLARE_MSM_GPIO_PINS(158); +DECLARE_MSM_GPIO_PINS(159); +DECLARE_MSM_GPIO_PINS(160); +DECLARE_MSM_GPIO_PINS(161); +DECLARE_MSM_GPIO_PINS(162); +DECLARE_MSM_GPIO_PINS(163); +DECLARE_MSM_GPIO_PINS(164); +DECLARE_MSM_GPIO_PINS(165); +DECLARE_MSM_GPIO_PINS(166); +DECLARE_MSM_GPIO_PINS(167); +DECLARE_MSM_GPIO_PINS(168); +DECLARE_MSM_GPIO_PINS(169); +DECLARE_MSM_GPIO_PINS(170); +DECLARE_MSM_GPIO_PINS(171); +DECLARE_MSM_GPIO_PINS(172); +DECLARE_MSM_GPIO_PINS(173); +DECLARE_MSM_GPIO_PINS(174); +DECLARE_MSM_GPIO_PINS(175); +DECLARE_MSM_GPIO_PINS(176); +DECLARE_MSM_GPIO_PINS(177); +DECLARE_MSM_GPIO_PINS(178); +DECLARE_MSM_GPIO_PINS(179); +DECLARE_MSM_GPIO_PINS(180); +DECLARE_MSM_GPIO_PINS(181); +DECLARE_MSM_GPIO_PINS(182); +DECLARE_MSM_GPIO_PINS(183); +DECLARE_MSM_GPIO_PINS(184); +DECLARE_MSM_GPIO_PINS(185); +DECLARE_MSM_GPIO_PINS(186); +DECLARE_MSM_GPIO_PINS(187); +DECLARE_MSM_GPIO_PINS(188); +DECLARE_MSM_GPIO_PINS(189); +DECLARE_MSM_GPIO_PINS(190); +DECLARE_MSM_GPIO_PINS(191); +DECLARE_MSM_GPIO_PINS(192); +DECLARE_MSM_GPIO_PINS(193); +DECLARE_MSM_GPIO_PINS(194); +DECLARE_MSM_GPIO_PINS(195); +DECLARE_MSM_GPIO_PINS(196); +DECLARE_MSM_GPIO_PINS(197); +DECLARE_MSM_GPIO_PINS(198); +DECLARE_MSM_GPIO_PINS(199); +DECLARE_MSM_GPIO_PINS(200); +DECLARE_MSM_GPIO_PINS(201); +DECLARE_MSM_GPIO_PINS(202); +DECLARE_MSM_GPIO_PINS(203); +DECLARE_MSM_GPIO_PINS(204); +DECLARE_MSM_GPIO_PINS(205); +DECLARE_MSM_GPIO_PINS(206); +DECLARE_MSM_GPIO_PINS(207); +DECLARE_MSM_GPIO_PINS(208); +DECLARE_MSM_GPIO_PINS(209); +DECLARE_MSM_GPIO_PINS(210); +DECLARE_MSM_GPIO_PINS(211); +DECLARE_MSM_GPIO_PINS(212); +DECLARE_MSM_GPIO_PINS(213); +DECLARE_MSM_GPIO_PINS(214); +DECLARE_MSM_GPIO_PINS(215); +DECLARE_MSM_GPIO_PINS(216); +DECLARE_MSM_GPIO_PINS(217); +DECLARE_MSM_GPIO_PINS(218); +DECLARE_MSM_GPIO_PINS(219); +DECLARE_MSM_GPIO_PINS(220); +DECLARE_MSM_GPIO_PINS(221); +DECLARE_MSM_GPIO_PINS(222); +DECLARE_MSM_GPIO_PINS(223); +DECLARE_MSM_GPIO_PINS(224); +DECLARE_MSM_GPIO_PINS(225); + +static const unsigned int ufs_reset_pins[] = { 226 }; +static const unsigned int sdc2_clk_pins[] = { 227 }; +static const unsigned int sdc2_cmd_pins[] = { 228 }; +static const unsigned int sdc2_data_pins[] = { 229 }; + +enum hawi_functions { + msm_mux_gpio, + msm_mux_aoss_cti, + msm_mux_atest_char, + msm_mux_atest_usb, + msm_mux_audio_ext_mclk, + msm_mux_audio_ref_clk, + msm_mux_cam_mclk, + msm_mux_cci_async_in, + msm_mux_cci_i2c0, + msm_mux_cci_i2c1, + msm_mux_cci_i2c2, + msm_mux_cci_i2c3, + msm_mux_cci_i2c4, + msm_mux_cci_i2c5, + msm_mux_cci_timer, + msm_mux_coex_espmi, + msm_mux_coex_uart1_rx, + msm_mux_coex_uart1_tx, + msm_mux_dbg_out_clk, + msm_mux_ddr_bist, + msm_mux_ddr_pxi, + msm_mux_dp_hot, + msm_mux_egpio, + msm_mux_gcc_gp, + msm_mux_gnss_adc, + msm_mux_host_rst, + msm_mux_i2chub0_se0, + msm_mux_i2chub0_se1, + msm_mux_i2chub0_se2, + msm_mux_i2chub0_se3, + msm_mux_i2chub0_se4, + msm_mux_i2s0, + msm_mux_i2s1, + msm_mux_ibi_i3c, + msm_mux_jitter_bist, + msm_mux_mdp_esync0, + msm_mux_mdp_esync1, + msm_mux_mdp_esync2, + msm_mux_mdp_vsync, + msm_mux_mdp_vsync_e, + msm_mux_mdp_vsync_p, + msm_mux_mdp_vsync0_out, + msm_mux_mdp_vsync1_out, + msm_mux_mdp_vsync2_out, + msm_mux_mdp_vsync3_out, + msm_mux_mdp_vsync5_out, + msm_mux_modem_pps_in, + msm_mux_modem_pps_out, + msm_mux_nav_gpio, + msm_mux_nav_gpio0, + msm_mux_nav_gpio3, + msm_mux_nav_rffe, + msm_mux_pcie0_clk_req_n, + msm_mux_pcie0_rst_n, + msm_mux_pcie1_clk_req_n, + msm_mux_phase_flag, + msm_mux_pll_bist_sync, + msm_mux_pll_clk_aux, + msm_mux_qdss_cti, + msm_mux_qlink, + msm_mux_qspi, + msm_mux_qspi_clk, + msm_mux_qspi_cs, + msm_mux_qup1_se0, + msm_mux_qup1_se1, + msm_mux_qup1_se2, + msm_mux_qup1_se3, + msm_mux_qup1_se4, + msm_mux_qup1_se5, + msm_mux_qup1_se6, + msm_mux_qup1_se7, + msm_mux_qup2_se0, + msm_mux_qup2_se1, + msm_mux_qup2_se2, + msm_mux_qup2_se3, + msm_mux_qup2_se4_01, + msm_mux_qup2_se4_23, + msm_mux_qup3_se0_01, + msm_mux_qup3_se0_23, + msm_mux_qup3_se1, + msm_mux_qup3_se2, + msm_mux_qup3_se3, + msm_mux_qup3_se4, + msm_mux_qup3_se5, + msm_mux_qup4_se0, + msm_mux_qup4_se1, + msm_mux_qup4_se2, + msm_mux_qup4_se3_01, + msm_mux_qup4_se3_23, + msm_mux_qup4_se3_l3, + msm_mux_qup4_se4_01, + msm_mux_qup4_se4_23, + msm_mux_qup4_se4_l3, + msm_mux_rng_rosc, + msm_mux_sd_write_protect, + msm_mux_sdc4_clk, + msm_mux_sdc4_cmd, + msm_mux_sdc4_data, + msm_mux_sys_throttle, + msm_mux_tb_trig_sdc, + msm_mux_tmess_rng, + msm_mux_tsense_clm, + msm_mux_tsense_pwm, + msm_mux_uim0, + msm_mux_uim1, + msm_mux_usb0_hs, + msm_mux_usb_phy, + msm_mux_vfr, + msm_mux_vsense_trigger_mirnat, + msm_mux_wcn_sw_ctrl, + msm_mux__, +}; + +static const char *const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15", "gpio16", "gpio17", + "gpio18", "gpio19", "gpio20", "gpio21", "gpio22", "gpio23", + "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29", + "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", + "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", + "gpio42", "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", + "gpio48", "gpio49", "gpio50", "gpio51", "gpio52", "gpio53", + "gpio54", "gpio55", "gpio56", "gpio57", "gpio58", "gpio59", + "gpio60", "gpio61", "gpio62", "gpio63", "gpio64", "gpio65", + "gpio66", "gpio67", "gpio68", "gpio69", "gpio70", "gpio71", + "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77", + "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", + "gpio84", "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", + "gpio90", "gpio91", "gpio92", "gpio93", "gpio94", "gpio95", + "gpio96", "gpio97", "gpio98", "gpio99", "gpio100", "gpio101", + "gpio102", "gpio103", "gpio104", "gpio105", "gpio106", "gpio107", + "gpio108", "gpio109", "gpio110", "gpio111", "gpio112", "gpio113", + "gpio114", "gpio115", "gpio116", "gpio117", "gpio118", "gpio119", + "gpio120", "gpio121", "gpio122", "gpio123", "gpio124", "gpio125", + "gpio126", "gpio127", "gpio128", "gpio129", "gpio130", "gpio131", + "gpio132", "gpio133", "gpio134", "gpio135", "gpio136", "gpio137", + "gpio138", "gpio139", "gpio140", "gpio141", "gpio142", "gpio143", + "gpio144", "gpio145", "gpio146", "gpio147", "gpio148", "gpio149", + "gpio150", "gpio151", "gpio152", "gpio153", "gpio154", "gpio155", + "gpio156", "gpio157", "gpio158", "gpio159", "gpio160", "gpio161", + "gpio162", "gpio163", "gpio164", "gpio165", "gpio166", "gpio167", + "gpio168", "gpio169", "gpio170", "gpio171", "gpio172", "gpio173", + "gpio174", "gpio175", "gpio176", "gpio177", "gpio178", "gpio179", + "gpio180", "gpio181", "gpio182", "gpio183", "gpio184", "gpio185", + "gpio186", "gpio187", "gpio188", "gpio189", "gpio190", "gpio191", + "gpio192", "gpio193", "gpio194", "gpio195", "gpio196", "gpio197", + "gpio198", "gpio199", "gpio200", "gpio201", "gpio202", "gpio203", + "gpio204", "gpio205", "gpio206", "gpio207", "gpio208", "gpio209", + "gpio210", "gpio211", "gpio212", "gpio213", "gpio214", "gpio215", + "gpio216", "gpio217", "gpio218", "gpio219", "gpio220", "gpio221", + "gpio222", "gpio223", "gpio224", "gpio225", +}; + +static const char *const aoss_cti_groups[] = { + "gpio74", "gpio75", "gpio76", "gpio77", +}; + +static const char *const atest_char_groups[] = { + "gpio126", "gpio127", "gpio128", "gpio129", "gpio133", +}; + +static const char *const atest_usb_groups[] = { + "gpio70", "gpio71", "gpio72", "gpio73", "gpio129", +}; + +static const char *const audio_ext_mclk_groups[] = { + "gpio120", "gpio121", +}; + +static const char *const audio_ref_clk_groups[] = { + "gpio120", +}; + +static const char *const cam_mclk_groups[] = { + "gpio89", "gpio90", "gpio91", "gpio92", "gpio93", "gpio94", + "gpio95", "gpio96", +}; + +static const char *const cci_async_in_groups[] = { + "gpio15", "gpio109", "gpio110", +}; + +static const char *const cci_i2c0_groups[] = { + "gpio109", "gpio110", +}; + +static const char *const cci_i2c1_groups[] = { + "gpio111", "gpio112", +}; + +static const char *const cci_i2c2_groups[] = { + "gpio113", "gpio114", +}; + +static const char *const cci_i2c3_groups[] = { + "gpio107", "gpio160", +}; + +static const char *const cci_i2c4_groups[] = { + "gpio108", "gpio149", +}; + +static const char *const cci_i2c5_groups[] = { + "gpio115", "gpio116", +}; + +static const char *const cci_timer_groups[] = { + "gpio105", "gpio106", "gpio107", "gpio159", "gpio160", +}; + +static const char *const coex_espmi_groups[] = { + "gpio144", "gpio145", +}; + +static const char *const coex_uart1_rx_groups[] = { + "gpio144", +}; + +static const char *const coex_uart1_tx_groups[] = { + "gpio145", +}; + +static const char *const dbg_out_clk_groups[] = { + "gpio82", +}; + +static const char *const ddr_bist_groups[] = { + "gpio40", "gpio41", "gpio44", "gpio45", +}; + +static const char *const ddr_pxi_groups[] = { + "gpio43", "gpio44", "gpio45", "gpio46", + "gpio52", "gpio53", "gpio54", "gpio55", +}; + +static const char *const dp_hot_groups[] = { + "gpio47", +}; + +static const char *const egpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio28", "gpio29", "gpio30", "gpio31", + "gpio48", "gpio49", "gpio50", "gpio51", "gpio163", "gpio164", + "gpio165", "gpio166", "gpio167", "gpio168", "gpio169", "gpio170", + "gpio171", "gpio172", "gpio173", "gpio174", "gpio175", "gpio176", + "gpio177", "gpio178", "gpio179", "gpio180", "gpio181", "gpio182", + "gpio183", "gpio184", "gpio185", "gpio186", "gpio187", "gpio188", + "gpio189", "gpio190", "gpio191", "gpio192", "gpio193", "gpio194", + "gpio195", "gpio196", "gpio197", "gpio198", "gpio199", "gpio200", + "gpio201", "gpio202", "gpio203", "gpio204", "gpio205", "gpio206", + "gpio207", "gpio208", "gpio209", "gpio212", "gpio213", "gpio214", + "gpio215", "gpio216", "gpio217", "gpio218", +}; + +static const char *const gcc_gp_groups[] = { + "gpio86", "gpio87", "gpio130", "gpio131", "gpio132", "gpio158", +}; + +static const char *const gnss_adc_groups[] = { + "gpio40", "gpio41", "gpio42", "gpio77", +}; + +static const char *const host_rst_groups[] = { + "gpio106", +}; + +static const char *const i2chub0_se0_groups[] = { + "gpio66", "gpio67", +}; + +static const char *const i2chub0_se1_groups[] = { + "gpio78", "gpio79", +}; + +static const char *const i2chub0_se2_groups[] = { + "gpio68", "gpio69", +}; + +static const char *const i2chub0_se3_groups[] = { + "gpio70", "gpio71", +}; + +static const char *const i2chub0_se4_groups[] = { + "gpio72", "gpio73", +}; + +static const char *const i2s0_groups[] = { + "gpio122", "gpio123", "gpio124", "gpio125", +}; + +static const char *const i2s1_groups[] = { + "gpio117", "gpio118", "gpio119", "gpio120", +}; + +static const char *const ibi_i3c_groups[] = { + "gpio0", "gpio1", "gpio4", "gpio5", "gpio8", "gpio9", + "gpio12", "gpio13", "gpio28", "gpio29", "gpio32", "gpio33", + "gpio36", "gpio37", "gpio48", "gpio49", "gpio60", "gpio61", +}; + +static const char *const jitter_bist_groups[] = { + "gpio73", +}; + +static const char *const mdp_esync0_groups[] = { + "gpio88", "gpio100", +}; + +static const char *const mdp_esync1_groups[] = { + "gpio86", "gpio100", +}; + +static const char *const mdp_esync2_groups[] = { + "gpio87", "gpio97", +}; + +static const char *const mdp_vsync_groups[] = { + "gpio86", "gpio87", "gpio88", "gpio97", +}; + +static const char *const mdp_vsync_e_groups[] = { + "gpio98", +}; + +static const char *const mdp_vsync_p_groups[] = { + "gpio98", +}; + +static const char *const mdp_vsync0_out_groups[] = { + "gpio86", +}; + +static const char *const mdp_vsync1_out_groups[] = { + "gpio86", +}; + +static const char *const mdp_vsync2_out_groups[] = { + "gpio87", +}; + +static const char *const mdp_vsync3_out_groups[] = { + "gpio87", +}; + +static const char *const mdp_vsync5_out_groups[] = { + "gpio87", +}; + +static const char *const modem_pps_in_groups[] = { + "gpio151", +}; + +static const char *const modem_pps_out_groups[] = { + "gpio151", +}; + +static const char *const nav_gpio_groups[] = { + "gpio146", "gpio147", "gpio148", "gpio151", +}; + +static const char *const nav_gpio0_groups[] = { + "gpio150", +}; + +static const char *const nav_gpio3_groups[] = { + "gpio150", +}; + +static const char *const nav_rffe_groups[] = { + "gpio134", "gpio135", "gpio138", "gpio139", +}; + +static const char *const pcie0_clk_req_n_groups[] = { + "gpio103", +}; + +static const char *const pcie0_rst_n_groups[] = { + "gpio102", +}; + +static const char *const pcie1_clk_req_n_groups[] = { + "gpio221", +}; + +static const char *const phase_flag_groups[] = { + "gpio117", "gpio118", "gpio119", "gpio123", "gpio124", "gpio125", + "gpio169", "gpio170", "gpio171", "gpio172", "gpio173", "gpio175", + "gpio176", "gpio179", "gpio180", "gpio181", "gpio184", "gpio185", + "gpio192", "gpio196", "gpio197", "gpio198", "gpio199", "gpio204", + "gpio206", "gpio207", "gpio208", "gpio210", "gpio211", "gpio214", + "gpio215", "gpio216", +}; + +static const char *const pll_bist_sync_groups[] = { + "gpio104", +}; + +static const char *const pll_clk_aux_groups[] = { + "gpio94", +}; + +static const char *const qdss_cti_groups[] = { + "gpio27", "gpio31", "gpio72", "gpio73", "gpio82", "gpio83", + "gpio152", "gpio158", +}; + +static const char *const qlink_groups[] = { + "gpio152", "gpio153", "gpio154", +}; + +static const char *const qspi_groups[] = { + "gpio80", "gpio81", "gpio82", "gpio147", +}; + +static const char *const qspi_clk_groups[] = { + "gpio83", +}; + +static const char *const qspi_cs_groups[] = { + "gpio146", "gpio148", +}; + +static const char *const qup1_se0_groups[] = { + "gpio80", "gpio81", "gpio82", "gpio83", +}; + +static const char *const qup1_se1_groups[] = { + "gpio74", "gpio75", "gpio76", "gpio77", +}; + +static const char *const qup1_se2_groups[] = { + "gpio40", "gpio41", "gpio42", "gpio43", "gpio130", "gpio131", "gpio132", +}; + +static const char *const qup1_se3_groups[] = { + "gpio44", "gpio45", "gpio46", "gpio47", +}; + +static const char *const qup1_se4_groups[] = { + "gpio36", "gpio37", "gpio38", "gpio39", +}; + +static const char *const qup1_se5_groups[] = { + "gpio52", "gpio53", "gpio54", "gpio55", +}; + +static const char *const qup1_se6_groups[] = { + "gpio56", "gpio57", "gpio58", "gpio59", +}; + +static const char *const qup1_se7_groups[] = { + "gpio60", "gpio61", "gpio62", "gpio63", +}; + +static const char *const qup2_se0_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char *const qup2_se1_groups[] = { + "gpio4", "gpio5", "gpio6", "gpio7", +}; + +static const char *const qup2_se2_groups[] = { + "gpio117", "gpio118", "gpio119", "gpio120", +}; + +static const char *const qup2_se3_groups[] = { + "gpio97", "gpio122", "gpio123", "gpio124", "gpio125", +}; + +static const char *const qup2_se4_01_groups[] = { + "gpio208", "gpio209", +}; + +static const char *const qup2_se4_23_groups[] = { + "gpio208", "gpio209", +}; + +static const char *const qup3_se0_01_groups[] = { + "gpio64", "gpio65", +}; + +static const char *const qup3_se0_23_groups[] = { + "gpio64", "gpio65", +}; + +static const char *const qup3_se1_groups[] = { + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio15", +}; + +static const char *const qup3_se2_groups[] = { + "gpio12", "gpio13", "gpio14", "gpio15", +}; + +static const char *const qup3_se3_groups[] = { + "gpio16", "gpio17", "gpio18", "gpio19", +}; + +static const char *const qup3_se4_groups[] = { + "gpio20", "gpio21", "gpio22", "gpio23", +}; + +static const char *const qup3_se5_groups[] = { + "gpio24", "gpio25", "gpio26", "gpio27", +}; + +static const char *const qup4_se0_groups[] = { + "gpio48", "gpio49", "gpio50", "gpio51", +}; + +static const char *const qup4_se1_groups[] = { + "gpio28", "gpio29", "gpio30", "gpio31", +}; + +static const char *const qup4_se2_groups[] = { + "gpio32", "gpio33", "gpio34", "gpio35", +}; + +static const char *const qup4_se3_01_groups[] = { + "gpio84", "gpio121", +}; + +static const char *const qup4_se3_23_groups[] = { + "gpio84", "gpio121", +}; + +static const char *const qup4_se3_l3_groups[] = { + "gpio98", +}; + +static const char *const qup4_se4_01_groups[] = { + "gpio161", "gpio162", +}; + +static const char *const qup4_se4_23_groups[] = { + "gpio161", "gpio162", +}; + +static const char *const qup4_se4_l3_groups[] = { + "gpio88", +}; + +static const char *const rng_rosc_groups[] = { + "gpio64", "gpio65", "gpio66", "gpio84", +}; + +static const char *const sd_write_protect_groups[] = { + "gpio85", +}; + +static const char *const sdc4_clk_groups[] = { + "gpio83", +}; + +static const char *const sdc4_cmd_groups[] = { + "gpio148", +}; + +static const char *const sdc4_data_groups[] = { + "gpio80", "gpio81", "gpio82", "gpio147", +}; + +static const char *const sys_throttle_groups[] = { + "gpio99", +}; + +static const char *const tb_trig_sdc_groups[] = { + "gpio88", "gpio146", +}; + +static const char *const tmess_rng_groups[] = { + "gpio64", "gpio65", "gpio66", "gpio84", +}; + +static const char *const tsense_clm_groups[] = { + "gpio10", "gpio87", "gpio97", "gpio99", "gpio105", "gpio106", + "gpio159", +}; + +static const char *const tsense_pwm_groups[] = { + "gpio10", "gpio87", "gpio97", "gpio99", "gpio223", "gpio224", + "gpio225", +}; + +static const char *const uim0_groups[] = { + "gpio126", "gpio127", "gpio128", "gpio129", +}; + +static const char *const uim1_groups[] = { + "gpio36", "gpio37", "gpio39", "gpio54", "gpio55", "gpio56", + "gpio70", "gpio71", "gpio72", "gpio130", "gpio131", "gpio132", + "gpio133", +}; + +static const char *const usb0_hs_groups[] = { + "gpio79", +}; + +static const char *const usb_phy_groups[] = { + "gpio59", "gpio60", +}; + +static const char *const vfr_groups[] = { + "gpio146", "gpio151", +}; + +static const char *const vsense_trigger_mirnat_groups[] = { + "gpio59", +}; + +static const char *const wcn_sw_ctrl_groups[] = { + "gpio18", "gpio19", "gpio155", "gpio156", +}; + +static const struct pinfunction hawi_functions[] = { + MSM_GPIO_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(aoss_cti), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_usb), + MSM_PIN_FUNCTION(audio_ext_mclk), + MSM_PIN_FUNCTION(audio_ref_clk), + MSM_PIN_FUNCTION(cam_mclk), + MSM_PIN_FUNCTION(cci_async_in), + MSM_PIN_FUNCTION(cci_i2c0), + MSM_PIN_FUNCTION(cci_i2c1), + MSM_PIN_FUNCTION(cci_i2c2), + MSM_PIN_FUNCTION(cci_i2c3), + MSM_PIN_FUNCTION(cci_i2c4), + MSM_PIN_FUNCTION(cci_i2c5), + MSM_PIN_FUNCTION(cci_timer), + MSM_PIN_FUNCTION(coex_espmi), + MSM_PIN_FUNCTION(coex_uart1_rx), + MSM_PIN_FUNCTION(coex_uart1_tx), + MSM_PIN_FUNCTION(dbg_out_clk), + MSM_PIN_FUNCTION(ddr_bist), + MSM_PIN_FUNCTION(ddr_pxi), + MSM_PIN_FUNCTION(dp_hot), + MSM_PIN_FUNCTION(egpio), + MSM_PIN_FUNCTION(gcc_gp), + MSM_PIN_FUNCTION(gnss_adc), + MSM_PIN_FUNCTION(host_rst), + MSM_PIN_FUNCTION(i2chub0_se0), + MSM_PIN_FUNCTION(i2chub0_se1), + MSM_PIN_FUNCTION(i2chub0_se2), + MSM_PIN_FUNCTION(i2chub0_se3), + MSM_PIN_FUNCTION(i2chub0_se4), + MSM_PIN_FUNCTION(i2s0), + MSM_PIN_FUNCTION(i2s1), + MSM_PIN_FUNCTION(ibi_i3c), + MSM_PIN_FUNCTION(jitter_bist), + MSM_PIN_FUNCTION(mdp_esync0), + MSM_PIN_FUNCTION(mdp_esync1), + MSM_PIN_FUNCTION(mdp_esync2), + MSM_PIN_FUNCTION(mdp_vsync), + MSM_PIN_FUNCTION(mdp_vsync_e), + MSM_PIN_FUNCTION(mdp_vsync_p), + MSM_PIN_FUNCTION(mdp_vsync0_out), + MSM_PIN_FUNCTION(mdp_vsync1_out), + MSM_PIN_FUNCTION(mdp_vsync2_out), + MSM_PIN_FUNCTION(mdp_vsync3_out), + MSM_PIN_FUNCTION(mdp_vsync5_out), + MSM_PIN_FUNCTION(modem_pps_in), + MSM_PIN_FUNCTION(modem_pps_out), + MSM_PIN_FUNCTION(nav_gpio), + MSM_PIN_FUNCTION(nav_gpio0), + MSM_PIN_FUNCTION(nav_gpio3), + MSM_PIN_FUNCTION(nav_rffe), + MSM_PIN_FUNCTION(pcie0_clk_req_n), + MSM_PIN_FUNCTION(pcie0_rst_n), + MSM_PIN_FUNCTION(pcie1_clk_req_n), + MSM_PIN_FUNCTION(phase_flag), + MSM_PIN_FUNCTION(pll_bist_sync), + MSM_PIN_FUNCTION(pll_clk_aux), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qlink), + MSM_PIN_FUNCTION(qspi), + MSM_PIN_FUNCTION(qspi_clk), + MSM_PIN_FUNCTION(qspi_cs), + MSM_PIN_FUNCTION(qup1_se0), + MSM_PIN_FUNCTION(qup1_se1), + MSM_PIN_FUNCTION(qup1_se2), + MSM_PIN_FUNCTION(qup1_se3), + MSM_PIN_FUNCTION(qup1_se4), + MSM_PIN_FUNCTION(qup1_se5), + MSM_PIN_FUNCTION(qup1_se6), + MSM_PIN_FUNCTION(qup1_se7), + MSM_PIN_FUNCTION(qup2_se0), + MSM_PIN_FUNCTION(qup2_se1), + MSM_PIN_FUNCTION(qup2_se2), + MSM_PIN_FUNCTION(qup2_se3), + MSM_PIN_FUNCTION(qup2_se4_01), + MSM_PIN_FUNCTION(qup2_se4_23), + MSM_PIN_FUNCTION(qup3_se0_01), + MSM_PIN_FUNCTION(qup3_se0_23), + MSM_PIN_FUNCTION(qup3_se1), + MSM_PIN_FUNCTION(qup3_se2), + MSM_PIN_FUNCTION(qup3_se3), + MSM_PIN_FUNCTION(qup3_se4), + MSM_PIN_FUNCTION(qup3_se5), + MSM_PIN_FUNCTION(qup4_se0), + MSM_PIN_FUNCTION(qup4_se1), + MSM_PIN_FUNCTION(qup4_se2), + MSM_PIN_FUNCTION(qup4_se3_01), + MSM_PIN_FUNCTION(qup4_se3_23), + MSM_PIN_FUNCTION(qup4_se3_l3), + MSM_PIN_FUNCTION(qup4_se4_01), + MSM_PIN_FUNCTION(qup4_se4_23), + MSM_PIN_FUNCTION(qup4_se4_l3), + MSM_PIN_FUNCTION(rng_rosc), + MSM_PIN_FUNCTION(sd_write_protect), + MSM_PIN_FUNCTION(sdc4_clk), + MSM_PIN_FUNCTION(sdc4_cmd), + MSM_PIN_FUNCTION(sdc4_data), + MSM_PIN_FUNCTION(sys_throttle), + MSM_PIN_FUNCTION(tb_trig_sdc), + MSM_PIN_FUNCTION(tmess_rng), + MSM_PIN_FUNCTION(tsense_clm), + MSM_PIN_FUNCTION(tsense_pwm), + MSM_PIN_FUNCTION(uim0), + MSM_PIN_FUNCTION(uim1), + MSM_PIN_FUNCTION(usb0_hs), + MSM_PIN_FUNCTION(usb_phy), + MSM_PIN_FUNCTION(vfr), + MSM_PIN_FUNCTION(vsense_trigger_mirnat), + MSM_PIN_FUNCTION(wcn_sw_ctrl), +}; + +/* + * Every pin is maintained as a single group, and missing or non-existing pin + * would be maintained as dummy group to synchronize pin group index with + * pin descriptor registered with pinctrl core. + * Clients would not be able to request these dummy pin groups. + */ +static const struct msm_pingroup hawi_groups[] = { + [0] = PINGROUP(0, qup2_se0, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [1] = PINGROUP(1, qup2_se0, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [2] = PINGROUP(2, qup2_se0, _, _, _, _, _, _, _, _, _, egpio), + [3] = PINGROUP(3, qup2_se0, _, _, _, _, _, _, _, _, _, egpio), + [4] = PINGROUP(4, qup2_se1, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [5] = PINGROUP(5, qup2_se1, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [6] = PINGROUP(6, qup2_se1, _, _, _, _, _, _, _, _, _, egpio), + [7] = PINGROUP(7, qup2_se1, _, _, _, _, _, _, _, _, _, egpio), + [8] = PINGROUP(8, qup3_se1, ibi_i3c, _, _, _, _, _, _, _, _, _), + [9] = PINGROUP(9, qup3_se1, ibi_i3c, _, _, _, _, _, _, _, _, _), + [10] = PINGROUP(10, qup3_se1, _, tsense_clm, tsense_pwm, _, _, _, _, _, _, _), + [11] = PINGROUP(11, qup3_se1, _, _, _, _, _, _, _, _, _, _), + [12] = PINGROUP(12, qup3_se2, ibi_i3c, qup3_se1, _, _, _, _, _, _, _, _), + [13] = PINGROUP(13, qup3_se2, ibi_i3c, qup3_se1, _, _, _, _, _, _, _, _), + [14] = PINGROUP(14, qup3_se2, _, _, _, _, _, _, _, _, _, _), + [15] = PINGROUP(15, qup3_se2, cci_async_in, qup3_se1, _, _, _, _, _, _, _, _), + [16] = PINGROUP(16, qup3_se3, _, _, _, _, _, _, _, _, _, _), + [17] = PINGROUP(17, qup3_se3, _, _, _, _, _, _, _, _, _, _), + [18] = PINGROUP(18, wcn_sw_ctrl, qup3_se3, _, _, _, _, _, _, _, _, _), + [19] = PINGROUP(19, wcn_sw_ctrl, qup3_se3, _, _, _, _, _, _, _, _, _), + [20] = PINGROUP(20, qup3_se4, _, _, _, _, _, _, _, _, _, _), + [21] = PINGROUP(21, qup3_se4, _, _, _, _, _, _, _, _, _, _), + [22] = PINGROUP(22, qup3_se4, _, _, _, _, _, _, _, _, _, _), + [23] = PINGROUP(23, qup3_se4, _, _, _, _, _, _, _, _, _, _), + [24] = PINGROUP(24, qup3_se5, _, _, _, _, _, _, _, _, _, _), + [25] = PINGROUP(25, qup3_se5, _, _, _, _, _, _, _, _, _, _), + [26] = PINGROUP(26, qup3_se5, _, _, _, _, _, _, _, _, _, _), + [27] = PINGROUP(27, qup3_se5, qdss_cti, _, _, _, _, _, _, _, _, _), + [28] = PINGROUP(28, qup4_se1, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [29] = PINGROUP(29, qup4_se1, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [30] = PINGROUP(30, qup4_se1, _, _, _, _, _, _, _, _, _, egpio), + [31] = PINGROUP(31, qup4_se1, qdss_cti, _, _, _, _, _, _, _, _, egpio), + [32] = PINGROUP(32, qup4_se2, ibi_i3c, _, _, _, _, _, _, _, _, _), + [33] = PINGROUP(33, qup4_se2, ibi_i3c, _, _, _, _, _, _, _, _, _), + [34] = PINGROUP(34, qup4_se2, _, _, _, _, _, _, _, _, _, _), + [35] = PINGROUP(35, qup4_se2, _, _, _, _, _, _, _, _, _, _), + [36] = PINGROUP(36, qup1_se4, uim1, ibi_i3c, _, _, _, _, _, _, _, _), + [37] = PINGROUP(37, qup1_se4, uim1, ibi_i3c, _, _, _, _, _, _, _, _), + [38] = PINGROUP(38, qup1_se4, _, _, _, _, _, _, _, _, _, _), + [39] = PINGROUP(39, qup1_se4, uim1, _, _, _, _, _, _, _, _, _), + [40] = PINGROUP(40, qup1_se2, ddr_bist, _, gnss_adc, _, _, _, _, _, _, _), + [41] = PINGROUP(41, qup1_se2, ddr_bist, _, gnss_adc, _, _, _, _, _, _, _), + [42] = PINGROUP(42, qup1_se2, gnss_adc, _, _, _, _, _, _, _, _, _), + [43] = PINGROUP(43, qup1_se2, _, ddr_pxi, _, _, _, _, _, _, _, _), + [44] = PINGROUP(44, qup1_se3, ddr_bist, ddr_pxi, _, _, _, _, _, _, _, _), + [45] = PINGROUP(45, qup1_se3, ddr_bist, ddr_pxi, _, _, _, _, _, _, _, _), + [46] = PINGROUP(46, qup1_se3, ddr_pxi, _, _, _, _, _, _, _, _, _), + [47] = PINGROUP(47, qup1_se3, dp_hot, _, _, _, _, _, _, _, _, _), + [48] = PINGROUP(48, qup4_se0, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [49] = PINGROUP(49, qup4_se0, ibi_i3c, _, _, _, _, _, _, _, _, egpio), + [50] = PINGROUP(50, qup4_se0, _, _, _, _, _, _, _, _, _, egpio), + [51] = PINGROUP(51, qup4_se0, _, _, _, _, _, _, _, _, _, egpio), + [52] = PINGROUP(52, qup1_se5, ddr_pxi, _, _, _, _, _, _, _, _, _), + [53] = PINGROUP(53, qup1_se5, _, ddr_pxi, _, _, _, _, _, _, _, _), + [54] = PINGROUP(54, qup1_se5, uim1, ddr_pxi, _, _, _, _, _, _, _, _), + [55] = PINGROUP(55, qup1_se5, uim1, ddr_pxi, _, _, _, _, _, _, _, _), + [56] = PINGROUP(56, qup1_se6, uim1, _, _, _, _, _, _, _, _, _), + [57] = PINGROUP(57, qup1_se6, _, _, _, _, _, _, _, _, _, _), + [58] = PINGROUP(58, qup1_se6, _, _, _, _, _, _, _, _, _, _), + [59] = PINGROUP(59, qup1_se6, usb_phy, vsense_trigger_mirnat, _, _, _, _, _, _, _, _), + [60] = PINGROUP(60, qup1_se7, usb_phy, ibi_i3c, _, _, _, _, _, _, _, _), + [61] = PINGROUP(61, qup1_se7, ibi_i3c, _, _, _, _, _, _, _, _, _), + [62] = PINGROUP(62, qup1_se7, _, _, _, _, _, _, _, _, _, _), + [63] = PINGROUP(63, qup1_se7, _, _, _, _, _, _, _, _, _, _), + [64] = PINGROUP(64, qup3_se0_01, qup3_se0_23, rng_rosc, tmess_rng, _, _, _, _, _, _, _), + [65] = PINGROUP(65, qup3_se0_01, qup3_se0_23, rng_rosc, tmess_rng, _, _, _, _, _, _, _), + [66] = PINGROUP(66, i2chub0_se0, rng_rosc, tmess_rng, _, _, _, _, _, _, _, _), + [67] = PINGROUP(67, i2chub0_se0, _, _, _, _, _, _, _, _, _, _), + [68] = PINGROUP(68, i2chub0_se2, _, _, _, _, _, _, _, _, _, _), + [69] = PINGROUP(69, i2chub0_se2, _, _, _, _, _, _, _, _, _, _), + [70] = PINGROUP(70, i2chub0_se3, uim1, _, atest_usb, _, _, _, _, _, _, _), + [71] = PINGROUP(71, i2chub0_se3, uim1, _, atest_usb, _, _, _, _, _, _, _), + [72] = PINGROUP(72, i2chub0_se4, uim1, qdss_cti, _, atest_usb, _, _, _, _, _, _), + [73] = PINGROUP(73, i2chub0_se4, qdss_cti, jitter_bist, atest_usb, _, _, _, _, _, _, _), + [74] = PINGROUP(74, qup1_se1, aoss_cti, _, _, _, _, _, _, _, _, _), + [75] = PINGROUP(75, qup1_se1, aoss_cti, _, _, _, _, _, _, _, _, _), + [76] = PINGROUP(76, qup1_se1, aoss_cti, _, _, _, _, _, _, _, _, _), + [77] = PINGROUP(77, qup1_se1, aoss_cti, gnss_adc, _, _, _, _, _, _, _, _), + [78] = PINGROUP(78, i2chub0_se1, _, _, _, _, _, _, _, _, _, _), + [79] = PINGROUP(79, i2chub0_se1, usb0_hs, _, _, _, _, _, _, _, _, _), + [80] = PINGROUP(80, qup1_se0, sdc4_data, qspi, _, _, _, _, _, _, _, _), + [81] = PINGROUP(81, qup1_se0, sdc4_data, qspi, _, _, _, _, _, _, _, _), + [82] = PINGROUP(82, qup1_se0, sdc4_data, qdss_cti, qspi, dbg_out_clk, _, _, _, _, _, _), + [83] = PINGROUP(83, qup1_se0, sdc4_clk, qdss_cti, qspi_clk, _, _, _, _, _, _, _), + [84] = PINGROUP(84, qup4_se3_01, qup4_se3_23, rng_rosc, tmess_rng, _, _, _, _, _, _, _), + [85] = PINGROUP(85, sd_write_protect, _, _, _, _, _, _, _, _, _, _), + [86] = PINGROUP(86, mdp_vsync, mdp_vsync0_out, mdp_vsync1_out, mdp_esync1, gcc_gp, + _, _, _, _, _, _), + [87] = PINGROUP(87, mdp_vsync, mdp_vsync2_out, mdp_vsync3_out, mdp_vsync5_out, + mdp_esync2, gcc_gp, _, tsense_clm, tsense_pwm, _, _), + [88] = PINGROUP(88, mdp_esync0, mdp_vsync, qup4_se4_l3, tb_trig_sdc, _, _, _, _, _, _, _), + [89] = PINGROUP(89, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [90] = PINGROUP(90, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [91] = PINGROUP(91, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [92] = PINGROUP(92, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [93] = PINGROUP(93, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [94] = PINGROUP(94, cam_mclk, pll_clk_aux, _, _, _, _, _, _, _, _, _), + [95] = PINGROUP(95, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [96] = PINGROUP(96, cam_mclk, _, _, _, _, _, _, _, _, _, _), + [97] = PINGROUP(97, mdp_esync2, qup2_se3, mdp_vsync, tsense_clm, tsense_pwm, _, _, + _, _, _, _), + [98] = PINGROUP(98, mdp_vsync_e, qup4_se3_l3, mdp_vsync_p, _, _, _, _, _, _, _, _), + [99] = PINGROUP(99, sys_throttle, tsense_clm, tsense_pwm, _, _, _, _, _, _, _, _), + [100] = PINGROUP(100, mdp_esync1, mdp_esync0, _, _, _, _, _, _, _, _, _), + [101] = PINGROUP(101, _, _, _, _, _, _, _, _, _, _, _), + [102] = PINGROUP(102, pcie0_rst_n, _, _, _, _, _, _, _, _, _, _), + [103] = PINGROUP(103, pcie0_clk_req_n, _, _, _, _, _, _, _, _, _, _), + [104] = PINGROUP(104, pll_bist_sync, _, _, _, _, _, _, _, _, _, _), + [105] = PINGROUP(105, cci_timer, tsense_clm, _, _, _, _, _, _, _, _, _), + [106] = PINGROUP(106, host_rst, cci_timer, tsense_clm, _, _, _, _, _, _, _, _), + [107] = PINGROUP(107, cci_i2c3, cci_timer, _, _, _, _, _, _, _, _, _), + [108] = PINGROUP(108, cci_i2c4, _, _, _, _, _, _, _, _, _, _), + [109] = PINGROUP(109, cci_i2c0, cci_async_in, _, _, _, _, _, _, _, _, _), + [110] = PINGROUP(110, cci_i2c0, cci_async_in, _, _, _, _, _, _, _, _, _), + [111] = PINGROUP(111, cci_i2c1, _, _, _, _, _, _, _, _, _, _), + [112] = PINGROUP(112, cci_i2c1, _, _, _, _, _, _, _, _, _, _), + [113] = PINGROUP(113, cci_i2c2, _, _, _, _, _, _, _, _, _, _), + [114] = PINGROUP(114, cci_i2c2, _, _, _, _, _, _, _, _, _, _), + [115] = PINGROUP(115, cci_i2c5, _, _, _, _, _, _, _, _, _, _), + [116] = PINGROUP(116, cci_i2c5, _, _, _, _, _, _, _, _, _, _), + [117] = PINGROUP(117, i2s1, qup2_se2, phase_flag, _, _, _, _, _, _, _, _), + [118] = PINGROUP(118, i2s1, qup2_se2, phase_flag, _, _, _, _, _, _, _, _), + [119] = PINGROUP(119, i2s1, qup2_se2, phase_flag, _, _, _, _, _, _, _, _), + [120] = PINGROUP(120, i2s1, qup2_se2, audio_ext_mclk, audio_ref_clk, _, _, + _, _, _, _, _), + [121] = PINGROUP(121, audio_ext_mclk, qup4_se3_01, qup4_se3_23, _, _, _, _, _, _, _, _), + [122] = PINGROUP(122, i2s0, qup2_se3, _, _, _, _, _, _, _, _, _), + [123] = PINGROUP(123, i2s0, qup2_se3, _, phase_flag, _, _, _, _, _, _, _), + [124] = PINGROUP(124, i2s0, qup2_se3, _, phase_flag, _, _, _, _, _, _, _), + [125] = PINGROUP(125, i2s0, qup2_se3, phase_flag, _, _, _, _, _, _, _, _), + [126] = PINGROUP(126, uim0, atest_char, _, _, _, _, _, _, _, _, _), + [127] = PINGROUP(127, uim0, atest_char, _, _, _, _, _, _, _, _, _), + [128] = PINGROUP(128, uim0, atest_char, _, _, _, _, _, _, _, _, _), + [129] = PINGROUP(129, uim0, atest_usb, atest_char, _, _, _, _, _, _, _, _), + [130] = PINGROUP(130, uim1, qup1_se2, gcc_gp, _, _, _, _, _, _, _, _), + [131] = PINGROUP(131, uim1, qup1_se2, gcc_gp, _, _, _, _, _, _, _, _), + [132] = PINGROUP(132, uim1, qup1_se2, gcc_gp, _, _, _, _, _, _, _, _), + [133] = PINGROUP(133, uim1, atest_char, _, _, _, _, _, _, _, _, _), + [134] = PINGROUP(134, _, _, nav_rffe, _, _, _, _, _, _, _, _), + [135] = PINGROUP(135, _, _, nav_rffe, _, _, _, _, _, _, _, _), + [136] = PINGROUP(136, _, _, _, _, _, _, _, _, _, _, _), + [137] = PINGROUP(137, _, _, _, _, _, _, _, _, _, _, _), + [138] = PINGROUP(138, _, _, nav_rffe, _, _, _, _, _, _, _, _), + [139] = PINGROUP(139, _, _, nav_rffe, _, _, _, _, _, _, _, _), + [140] = PINGROUP(140, _, _, _, _, _, _, _, _, _, _, _), + [141] = PINGROUP(141, _, _, _, _, _, _, _, _, _, _, _), + [142] = PINGROUP(142, _, _, _, _, _, _, _, _, _, _, _), + [143] = PINGROUP(143, _, _, _, _, _, _, _, _, _, _, _), + [144] = PINGROUP(144, coex_uart1_rx, coex_espmi, _, _, _, _, _, _, _, _, _), + [145] = PINGROUP(145, coex_uart1_tx, coex_espmi, _, _, _, _, _, _, _, _, _), + [146] = PINGROUP(146, _, vfr, nav_gpio, tb_trig_sdc, qspi_cs, _, _, _, _, _, _), + [147] = PINGROUP(147, _, nav_gpio, sdc4_data, qspi, _, _, _, _, _, _, _), + [148] = PINGROUP(148, nav_gpio, _, sdc4_cmd, qspi_cs, _, _, _, _, _, _, _), + [149] = PINGROUP(149, cci_i2c4, _, _, _, _, _, _, _, _, _, _), + [150] = PINGROUP(150, nav_gpio0, nav_gpio3, _, _, _, _, _, _, _, _, _), + [151] = PINGROUP(151, nav_gpio, vfr, modem_pps_in, modem_pps_out, _, _, _, _, _, _, _), + [152] = PINGROUP(152, qlink, qdss_cti, _, _, _, _, _, _, _, _, _), + [153] = PINGROUP(153, qlink, _, _, _, _, _, _, _, _, _, _), + [154] = PINGROUP(154, qlink, _, _, _, _, _, _, _, _, _, _), + [155] = PINGROUP(155, wcn_sw_ctrl, _, _, _, _, _, _, _, _, _, _), + [156] = PINGROUP(156, wcn_sw_ctrl, _, _, _, _, _, _, _, _, _, _), + [157] = PINGROUP(157, _, _, _, _, _, _, _, _, _, _, _), + [158] = PINGROUP(158, qdss_cti, gcc_gp, _, _, _, _, _, _, _, _, _), + [159] = PINGROUP(159, cci_timer, tsense_clm, _, _, _, _, _, _, _, _, _), + [160] = PINGROUP(160, cci_timer, cci_i2c3, _, _, _, _, _, _, _, _, _), + [161] = PINGROUP(161, qup4_se4_01, qup4_se4_23, _, _, _, _, _, _, _, _, _), + [162] = PINGROUP(162, qup4_se4_01, qup4_se4_23, _, _, _, _, _, _, _, _, _), + [163] = PINGROUP(163, _, _, _, _, _, _, _, _, _, _, egpio), + [164] = PINGROUP(164, _, _, _, _, _, _, _, _, _, _, egpio), + [165] = PINGROUP(165, _, _, _, _, _, _, _, _, _, _, egpio), + [166] = PINGROUP(166, _, _, _, _, _, _, _, _, _, _, egpio), + [167] = PINGROUP(167, _, _, _, _, _, _, _, _, _, _, egpio), + [168] = PINGROUP(168, _, _, _, _, _, _, _, _, _, _, egpio), + [169] = PINGROUP(169, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [170] = PINGROUP(170, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [171] = PINGROUP(171, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [172] = PINGROUP(172, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [173] = PINGROUP(173, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [174] = PINGROUP(174, _, _, _, _, _, _, _, _, _, _, egpio), + [175] = PINGROUP(175, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [176] = PINGROUP(176, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [177] = PINGROUP(177, _, _, _, _, _, _, _, _, _, _, egpio), + [178] = PINGROUP(178, _, _, _, _, _, _, _, _, _, _, egpio), + [179] = PINGROUP(179, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [180] = PINGROUP(180, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [181] = PINGROUP(181, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [182] = PINGROUP(182, _, _, _, _, _, _, _, _, _, _, egpio), + [183] = PINGROUP(183, _, _, _, _, _, _, _, _, _, _, egpio), + [184] = PINGROUP(184, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [185] = PINGROUP(185, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [186] = PINGROUP(186, _, _, _, _, _, _, _, _, _, _, egpio), + [187] = PINGROUP(187, _, _, _, _, _, _, _, _, _, _, egpio), + [188] = PINGROUP(188, _, _, _, _, _, _, _, _, _, _, egpio), + [189] = PINGROUP(189, _, _, _, _, _, _, _, _, _, _, egpio), + [190] = PINGROUP(190, _, _, _, _, _, _, _, _, _, _, egpio), + [191] = PINGROUP(191, _, _, _, _, _, _, _, _, _, _, egpio), + [192] = PINGROUP(192, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [193] = PINGROUP(193, _, _, _, _, _, _, _, _, _, _, egpio), + [194] = PINGROUP(194, _, _, _, _, _, _, _, _, _, _, egpio), + [195] = PINGROUP(195, _, _, _, _, _, _, _, _, _, _, egpio), + [196] = PINGROUP(196, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [197] = PINGROUP(197, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [198] = PINGROUP(198, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [199] = PINGROUP(199, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [200] = PINGROUP(200, _, _, _, _, _, _, _, _, _, _, egpio), + [201] = PINGROUP(201, _, _, _, _, _, _, _, _, _, _, egpio), + [202] = PINGROUP(202, _, _, _, _, _, _, _, _, _, _, egpio), + [203] = PINGROUP(203, _, _, _, _, _, _, _, _, _, _, egpio), + [204] = PINGROUP(204, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [205] = PINGROUP(205, _, _, _, _, _, _, _, _, _, _, egpio), + [206] = PINGROUP(206, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [207] = PINGROUP(207, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [208] = PINGROUP(208, qup2_se4_01, qup2_se4_23, phase_flag, _, _, _, _, _, _, _, egpio), + [209] = PINGROUP(209, qup2_se4_01, qup2_se4_23, _, _, _, _, _, _, _, _, egpio), + [210] = PINGROUP(210, phase_flag, _, _, _, _, _, _, _, _, _, _), + [211] = PINGROUP(211, phase_flag, _, _, _, _, _, _, _, _, _, _), + [212] = PINGROUP(212, _, _, _, _, _, _, _, _, _, _, egpio), + [213] = PINGROUP(213, _, _, _, _, _, _, _, _, _, _, egpio), + [214] = PINGROUP(214, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [215] = PINGROUP(215, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [216] = PINGROUP(216, phase_flag, _, _, _, _, _, _, _, _, _, egpio), + [217] = PINGROUP(217, _, _, _, _, _, _, _, _, _, _, egpio), + [218] = PINGROUP(218, _, _, _, _, _, _, _, _, _, _, egpio), + [219] = PINGROUP(219, _, _, _, _, _, _, _, _, _, _, _), + [220] = PINGROUP(220, _, _, _, _, _, _, _, _, _, _, _), + [221] = PINGROUP(221, pcie1_clk_req_n, _, _, _, _, _, _, _, _, _, _), + [222] = PINGROUP(222, _, _, _, _, _, _, _, _, _, _, _), + [223] = PINGROUP(223, tsense_pwm, _, _, _, _, _, _, _, _, _, _), + [224] = PINGROUP(224, tsense_pwm, _, _, _, _, _, _, _, _, _, _), + [225] = PINGROUP(225, tsense_pwm, _, _, _, _, _, _, _, _, _, _), + [226] = UFS_RESET(ufs_reset, 0xf1004, 0xf2000), + [227] = SDC_QDSD_PINGROUP(sdc2_clk, 0xe6000, 14, 6), + [228] = SDC_QDSD_PINGROUP(sdc2_cmd, 0xe6000, 11, 3), + [229] = SDC_QDSD_PINGROUP(sdc2_data, 0xe6000, 9, 0), +}; + +static const struct msm_gpio_wakeirq_map hawi_pdc_map[] = { + { 0, 105 }, { 3, 113 }, { 4, 106 }, { 7, 107 }, { 8, 108 }, { 11, 109 }, + { 12, 115 }, { 15, 131 }, { 16, 116 }, { 17, 141 }, { 18, 143 }, { 19, 112 }, + { 23, 117 }, { 24, 118 }, { 27, 119 }, { 28, 125 }, { 31, 126 }, { 32, 127 }, + { 35, 101 }, { 36, 128 }, { 39, 129 }, { 43, 130 }, { 47, 154 }, { 48, 135 }, + { 51, 114 }, { 55, 104 }, { 57, 136 }, { 58, 137 }, { 59, 138 }, { 60, 139 }, + { 61, 145 }, { 63, 124 }, { 64, 110 }, { 65, 123 }, { 67, 132 }, { 68, 146 }, + { 69, 147 }, { 75, 151 }, { 77, 148 }, { 78, 149 }, { 79, 155 }, { 80, 156 }, + { 81, 157 }, { 82, 158 }, { 84, 134 }, { 85, 159 }, { 86, 160 }, { 87, 161 }, + { 88, 162 }, { 95, 163 }, { 96, 164 }, { 97, 133 }, { 98, 150 }, { 99, 111 }, + { 101, 165 }, { 102, 166 }, { 103, 167 }, { 104, 168 }, { 120, 169 }, { 123, 170 }, + { 125, 171 }, { 129, 153 }, { 133, 100 }, { 144, 172 }, { 146, 173 }, { 151, 174 }, + { 152, 175 }, { 155, 122 }, { 158, 120 }, { 162, 142 }, { 164, 176 }, { 165, 177 }, + { 167, 178 }, { 168, 179 }, { 174, 180 }, { 177, 181 }, { 179, 182 }, { 183, 183 }, + { 184, 184 }, { 185, 185 }, { 186, 152 }, { 188, 144 }, { 202, 102 }, { 203, 103 }, + { 205, 140 }, { 209, 186 }, { 213, 121 }, { 216, 187 }, { 221, 188 }, { 222, 189 }, + { 223, 190 }, { 224, 191 }, { 225, 192 }, +}; + +static const struct msm_pinctrl_soc_data hawi_tlmm = { + .pins = hawi_pins, + .npins = ARRAY_SIZE(hawi_pins), + .functions = hawi_functions, + .nfunctions = ARRAY_SIZE(hawi_functions), + .groups = hawi_groups, + .ngroups = ARRAY_SIZE(hawi_groups), + .ngpios = 227, + .wakeirq_map = hawi_pdc_map, + .nwakeirq_map = ARRAY_SIZE(hawi_pdc_map), + .egpio_func = 11, +}; + +static int hawi_tlmm_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &hawi_tlmm); +} + +static const struct of_device_id hawi_tlmm_of_match[] = { + { .compatible = "qcom,hawi-tlmm", }, + {}, +}; + +static struct platform_driver hawi_tlmm_driver = { + .driver = { + .name = "hawi-tlmm", + .of_match_table = hawi_tlmm_of_match, + }, + .probe = hawi_tlmm_probe, +}; + +static int __init hawi_tlmm_init(void) +{ + return platform_driver_register(&hawi_tlmm_driver); +} +arch_initcall(hawi_tlmm_init); + +static void __exit hawi_tlmm_exit(void) +{ + platform_driver_unregister(&hawi_tlmm_driver); +} +module_exit(hawi_tlmm_exit); + +MODULE_DESCRIPTION("QTI Hawi TLMM driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, hawi_tlmm_of_match); diff --git a/drivers/pinctrl/qcom/pinctrl-ipq4019.c b/drivers/pinctrl/qcom/pinctrl-ipq4019.c index 6ede3149b6e1..c5f0decc3eb3 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq4019.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq4019.c @@ -242,7 +242,6 @@ DECLARE_QCA_GPIO_PINS(99); .io_reg = 0x4 + 0x1000 * id, \ .intr_cfg_reg = 0x8 + 0x1000 * id, \ .intr_status_reg = 0xc + 0x1000 * id, \ - .intr_target_reg = 0x8 + 0x1000 * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ diff --git a/drivers/pinctrl/qcom/pinctrl-ipq5018.c b/drivers/pinctrl/qcom/pinctrl-ipq5018.c index cbf34854f882..0698c8f0110b 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq5018.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq5018.c @@ -32,7 +32,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ diff --git a/drivers/pinctrl/qcom/pinctrl-ipq5210.c b/drivers/pinctrl/qcom/pinctrl-ipq5210.c new file mode 100644 index 000000000000..827a4ad07a6b --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-ipq5210.c @@ -0,0 +1,897 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +#include "pinctrl-msm.h" + +#define REG_SIZE 0x1000 +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + { \ + .grp = PINCTRL_PINGROUP("gpio" #id, \ + gpio##id##_pins, \ + ARRAY_SIZE(gpio##id##_pins)), \ + .ctl_reg = REG_SIZE * id, \ + .io_reg = 0x4 + REG_SIZE * id, \ + .intr_cfg_reg = 0x8 + REG_SIZE * id, \ + .intr_status_reg = 0xc + REG_SIZE * id, \ + .mux_bit = 2, \ + .pull_bit = 0, \ + .drv_bit = 6, \ + .oe_bit = 9, \ + .in_bit = 0, \ + .out_bit = 1, \ + .intr_enable_bit = 0, \ + .intr_status_bit = 0, \ + .intr_target_bit = 5, \ + .intr_target_kpss_val = 3, \ + .intr_raw_status_bit = 4, \ + .intr_polarity_bit = 1, \ + .intr_detection_bit = 2, \ + .intr_detection_width = 2, \ + .funcs = (int[]){ \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + }, \ + .nfuncs = 10, \ + } + +static const struct pinctrl_pin_desc ipq5210_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(21, "GPIO_21"), + PINCTRL_PIN(22, "GPIO_22"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(27, "GPIO_27"), + PINCTRL_PIN(28, "GPIO_28"), + PINCTRL_PIN(29, "GPIO_29"), + PINCTRL_PIN(30, "GPIO_30"), + PINCTRL_PIN(31, "GPIO_31"), + PINCTRL_PIN(32, "GPIO_32"), + PINCTRL_PIN(33, "GPIO_33"), + PINCTRL_PIN(34, "GPIO_34"), + PINCTRL_PIN(35, "GPIO_35"), + PINCTRL_PIN(36, "GPIO_36"), + PINCTRL_PIN(37, "GPIO_37"), + PINCTRL_PIN(38, "GPIO_38"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(41, "GPIO_41"), + PINCTRL_PIN(42, "GPIO_42"), + PINCTRL_PIN(43, "GPIO_43"), + PINCTRL_PIN(44, "GPIO_44"), + PINCTRL_PIN(45, "GPIO_45"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), +}; + +#define DECLARE_MSM_GPIO_PINS(pin) \ + static const unsigned int gpio##pin##_pins[] = { pin } +DECLARE_MSM_GPIO_PINS(0); +DECLARE_MSM_GPIO_PINS(1); +DECLARE_MSM_GPIO_PINS(2); +DECLARE_MSM_GPIO_PINS(3); +DECLARE_MSM_GPIO_PINS(4); +DECLARE_MSM_GPIO_PINS(5); +DECLARE_MSM_GPIO_PINS(6); +DECLARE_MSM_GPIO_PINS(7); +DECLARE_MSM_GPIO_PINS(8); +DECLARE_MSM_GPIO_PINS(9); +DECLARE_MSM_GPIO_PINS(10); +DECLARE_MSM_GPIO_PINS(11); +DECLARE_MSM_GPIO_PINS(12); +DECLARE_MSM_GPIO_PINS(13); +DECLARE_MSM_GPIO_PINS(14); +DECLARE_MSM_GPIO_PINS(15); +DECLARE_MSM_GPIO_PINS(16); +DECLARE_MSM_GPIO_PINS(17); +DECLARE_MSM_GPIO_PINS(18); +DECLARE_MSM_GPIO_PINS(19); +DECLARE_MSM_GPIO_PINS(20); +DECLARE_MSM_GPIO_PINS(21); +DECLARE_MSM_GPIO_PINS(22); +DECLARE_MSM_GPIO_PINS(23); +DECLARE_MSM_GPIO_PINS(24); +DECLARE_MSM_GPIO_PINS(25); +DECLARE_MSM_GPIO_PINS(26); +DECLARE_MSM_GPIO_PINS(27); +DECLARE_MSM_GPIO_PINS(28); +DECLARE_MSM_GPIO_PINS(29); +DECLARE_MSM_GPIO_PINS(30); +DECLARE_MSM_GPIO_PINS(31); +DECLARE_MSM_GPIO_PINS(32); +DECLARE_MSM_GPIO_PINS(33); +DECLARE_MSM_GPIO_PINS(34); +DECLARE_MSM_GPIO_PINS(35); +DECLARE_MSM_GPIO_PINS(36); +DECLARE_MSM_GPIO_PINS(37); +DECLARE_MSM_GPIO_PINS(38); +DECLARE_MSM_GPIO_PINS(39); +DECLARE_MSM_GPIO_PINS(40); +DECLARE_MSM_GPIO_PINS(41); +DECLARE_MSM_GPIO_PINS(42); +DECLARE_MSM_GPIO_PINS(43); +DECLARE_MSM_GPIO_PINS(44); +DECLARE_MSM_GPIO_PINS(45); +DECLARE_MSM_GPIO_PINS(46); +DECLARE_MSM_GPIO_PINS(47); +DECLARE_MSM_GPIO_PINS(48); +DECLARE_MSM_GPIO_PINS(49); +DECLARE_MSM_GPIO_PINS(50); +DECLARE_MSM_GPIO_PINS(51); +DECLARE_MSM_GPIO_PINS(52); +DECLARE_MSM_GPIO_PINS(53); + +enum ipq5210_functions { + msm_mux_atest_char_start, + msm_mux_atest_char_status0, + msm_mux_atest_char_status1, + msm_mux_atest_char_status2, + msm_mux_atest_char_status3, + msm_mux_atest_tic_en, + msm_mux_audio_pri, + msm_mux_audio_pri_mclk_out0, + msm_mux_audio_pri_mclk_in0, + msm_mux_audio_pri_mclk_out1, + msm_mux_audio_pri_mclk_in1, + msm_mux_audio_pri_mclk_out2, + msm_mux_audio_pri_mclk_in2, + msm_mux_audio_pri_mclk_out3, + msm_mux_audio_pri_mclk_in3, + msm_mux_audio_sec, + msm_mux_audio_sec_mclk_out0, + msm_mux_audio_sec_mclk_in0, + msm_mux_audio_sec_mclk_out1, + msm_mux_audio_sec_mclk_in1, + msm_mux_audio_sec_mclk_out2, + msm_mux_audio_sec_mclk_in2, + msm_mux_audio_sec_mclk_out3, + msm_mux_audio_sec_mclk_in3, + msm_mux_core_voltage_0, + msm_mux_cri_trng0, + msm_mux_cri_trng1, + msm_mux_cri_trng2, + msm_mux_cri_trng3, + msm_mux_dbg_out_clk, + msm_mux_dg_out, + msm_mux_gcc_plltest_bypassnl, + msm_mux_gcc_plltest_resetn, + msm_mux_gcc_tlmm, + msm_mux_gpio, + msm_mux_led0, + msm_mux_led1, + msm_mux_led2, + msm_mux_mdc_mst, + msm_mux_mdc_slv0, + msm_mux_mdc_slv1, + msm_mux_mdc_slv2, + msm_mux_mdio_mst, + msm_mux_mdio_slv0, + msm_mux_mdio_slv1, + msm_mux_mdio_slv2, + msm_mux_mux_tod_out, + msm_mux_pcie0_clk_req_n, + msm_mux_pcie0_wake, + msm_mux_pcie1_clk_req_n, + msm_mux_pcie1_wake, + msm_mux_pll_test, + msm_mux_pon_active_led, + msm_mux_pon_mux_sel, + msm_mux_pon_rx, + msm_mux_pon_rx_los, + msm_mux_pon_tx, + msm_mux_pon_tx_burst, + msm_mux_pon_tx_dis, + msm_mux_pon_tx_fault, + msm_mux_pon_tx_sd, + msm_mux_gpn_rx_los, + msm_mux_gpn_tx_burst, + msm_mux_gpn_tx_dis, + msm_mux_gpn_tx_fault, + msm_mux_gpn_tx_sd, + msm_mux_pps, + msm_mux_pwm0, + msm_mux_pwm1, + msm_mux_pwm2, + msm_mux_pwm3, + msm_mux_qdss_cti_trig_in_a0, + msm_mux_qdss_cti_trig_in_a1, + msm_mux_qdss_cti_trig_in_b0, + msm_mux_qdss_cti_trig_in_b1, + msm_mux_qdss_cti_trig_out_a0, + msm_mux_qdss_cti_trig_out_a1, + msm_mux_qdss_cti_trig_out_b0, + msm_mux_qdss_cti_trig_out_b1, + msm_mux_qdss_traceclk_a, + msm_mux_qdss_tracectl_a, + msm_mux_qdss_tracedata_a, + msm_mux_qrng_rosc0, + msm_mux_qrng_rosc1, + msm_mux_qrng_rosc2, + msm_mux_qspi_data, + msm_mux_qspi_clk, + msm_mux_qspi_cs_n, + msm_mux_qup_se0, + msm_mux_qup_se1, + msm_mux_qup_se2, + msm_mux_qup_se3, + msm_mux_qup_se4, + msm_mux_qup_se5, + msm_mux_qup_se5_l1, + msm_mux_resout, + msm_mux_rx_los0, + msm_mux_rx_los1, + msm_mux_rx_los2, + msm_mux_sdc_clk, + msm_mux_sdc_cmd, + msm_mux_sdc_data, + msm_mux_tsens_max, + msm_mux__, +}; + +static const char *const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", + "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", + "gpio14", "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", + "gpio21", "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", + "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", + "gpio35", "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", + "gpio42", "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", + "gpio49", "gpio50", "gpio51", "gpio52", "gpio53", +}; + +static const char *const atest_char_start_groups[] = { + "gpio46", +}; + +static const char *const atest_char_status0_groups[] = { + "gpio34", +}; + +static const char *const atest_char_status1_groups[] = { + "gpio35", +}; + +static const char *const atest_char_status2_groups[] = { + "gpio36", +}; + +static const char *const atest_char_status3_groups[] = { + "gpio37", +}; + +static const char *const atest_tic_en_groups[] = { + "gpio42", +}; + +static const char *const audio_pri_groups[] = { + "gpio34", "gpio35", "gpio36", "gpio37", +}; + +static const char *const audio_pri_mclk_out0_groups[] = { + "gpio12", +}; + +static const char *const audio_pri_mclk_in0_groups[] = { + "gpio12", +}; + +static const char *const audio_pri_mclk_out1_groups[] = { + "gpio19", +}; + +static const char *const audio_pri_mclk_in1_groups[] = { + "gpio19", +}; + +static const char *const audio_pri_mclk_out2_groups[] = { + "gpio8", +}; + +static const char *const audio_pri_mclk_in2_groups[] = { + "gpio8", +}; + +static const char *const audio_pri_mclk_out3_groups[] = { + "gpio13", +}; + +static const char *const audio_pri_mclk_in3_groups[] = { + "gpio13", +}; + +static const char *const audio_sec_mclk_out0_groups[] = { + "gpio17", +}; + +static const char *const audio_sec_mclk_in0_groups[] = { + "gpio17", +}; + +static const char *const audio_sec_mclk_out1_groups[] = { + "gpio16", +}; + +static const char *const audio_sec_mclk_in1_groups[] = { + "gpio16", +}; + +static const char *const audio_sec_mclk_out2_groups[] = { + "gpio49", +}; + +static const char *const audio_sec_mclk_in2_groups[] = { + "gpio49", +}; + +static const char *const audio_sec_mclk_out3_groups[] = { + "gpio50", +}; + +static const char *const audio_sec_mclk_in3_groups[] = { + "gpio50", +}; + +static const char *const audio_sec_groups[] = { + "gpio40", "gpio41", "gpio42", "gpio43", +}; + +static const char *const core_voltage_0_groups[] = { + "gpio22", +}; + +static const char *const cri_trng0_groups[] = { + "gpio6", +}; + +static const char *const cri_trng1_groups[] = { + "gpio7", +}; + +static const char *const cri_trng2_groups[] = { + "gpio8", +}; + +static const char *const cri_trng3_groups[] = { + "gpio9", +}; + +static const char *const dbg_out_clk_groups[] = { + "gpio23", +}; + +static const char *const dg_out_groups[] = { + "gpio46", +}; + +static const char *const gcc_plltest_bypassnl_groups[] = { + "gpio38", +}; + +static const char *const gcc_plltest_resetn_groups[] = { + "gpio40", +}; + +static const char *const gcc_tlmm_groups[] = { + "gpio39", +}; + +static const char *const led0_groups[] = { + "gpio6", "gpio23", "gpio39", +}; + +static const char *const led1_groups[] = { + "gpio7", "gpio27", "gpio39", +}; + +static const char *const led2_groups[] = { + "gpio9", "gpio26", "gpio38", +}; + +static const char *const mdc_mst_groups[] = { + "gpio26", +}; + +static const char *const mdc_slv0_groups[] = { + "gpio31", +}; + +static const char *const mdc_slv1_groups[] = { + "gpio20", +}; + +static const char *const mdc_slv2_groups[] = { + "gpio47", +}; + +static const char *const mdio_mst_groups[] = { + "gpio27", +}; + +static const char *const mdio_slv0_groups[] = { + "gpio33", +}; + +static const char *const mdio_slv1_groups[] = { + "gpio21", +}; + +static const char *const mdio_slv2_groups[] = { + "gpio49", +}; + +static const char *const mux_tod_out_groups[] = { + "gpio19", +}; + +static const char *const pcie0_clk_req_n_groups[] = { + "gpio31", +}; + +static const char *const pcie0_wake_groups[] = { + "gpio33", +}; + +static const char *const pcie1_clk_req_n_groups[] = { + "gpio28", +}; + +static const char *const pcie1_wake_groups[] = { + "gpio30", +}; + +static const char *const pll_test_groups[] = { + "gpio18", +}; + +static const char *const pon_active_led_groups[] = { + "gpio11", +}; + +static const char *const pon_mux_sel_groups[] = { + "gpio45", +}; + +static const char *const pon_rx_groups[] = { + "gpio48", +}; + +static const char *const pon_rx_los_groups[] = { + "gpio10", +}; + +static const char *const pon_tx_groups[] = { + "gpio15", +}; + +static const char *const pon_tx_burst_groups[] = { + "gpio14", +}; + +static const char *const pon_tx_dis_groups[] = { + "gpio12", +}; + +static const char *const pon_tx_fault_groups[] = { + "gpio17", +}; + +static const char *const pon_tx_sd_groups[] = { + "gpio16", +}; + +static const char *const gpn_rx_los_groups[] = { + "gpio47", +}; + +static const char *const gpn_tx_burst_groups[] = { + "gpio51", +}; + +static const char *const gpn_tx_dis_groups[] = { + "gpio13", +}; + +static const char *const gpn_tx_fault_groups[] = { + "gpio49", +}; + +static const char *const gpn_tx_sd_groups[] = { + "gpio50", +}; + +static const char *const pps_groups[] = { + "gpio18", +}; + +static const char *const pwm0_groups[] = { + "gpio10", "gpio11", "gpio12", "gpio13", +}; + +static const char *const pwm1_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio9", +}; + +static const char *const pwm2_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char *const pwm3_groups[] = { + "gpio22", +}; + +static const char *const qdss_cti_trig_in_a0_groups[] = { + "gpio30", +}; + +static const char *const qdss_cti_trig_in_a1_groups[] = { + "gpio33", +}; + +static const char *const qdss_cti_trig_in_b0_groups[] = { + "gpio34", +}; + +static const char *const qdss_cti_trig_in_b1_groups[] = { + "gpio37", +}; + +static const char *const qdss_cti_trig_out_a0_groups[] = { + "gpio28", +}; + +static const char *const qdss_cti_trig_out_a1_groups[] = { + "gpio31", +}; + +static const char *const qdss_cti_trig_out_b0_groups[] = { + "gpio16", +}; + +static const char *const qdss_cti_trig_out_b1_groups[] = { + "gpio35", +}; + +static const char *const qdss_traceclk_a_groups[] = { + "gpio23", +}; + +static const char *const qdss_tracectl_a_groups[] = { + "gpio26", +}; + +static const char *const qdss_tracedata_a_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15", "gpio20", "gpio21", + "gpio38", "gpio39", "gpio40", "gpio41", +}; + +static const char *const qrng_rosc0_groups[] = { + "gpio12", +}; + +static const char *const qrng_rosc1_groups[] = { + "gpio13", +}; + +static const char *const qrng_rosc2_groups[] = { + "gpio14", +}; + +static const char *const qspi_data_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char *const qspi_clk_groups[] = { + "gpio5", +}; + +static const char *const qspi_cs_n_groups[] = { + "gpio4", +}; + +static const char *const qup_se0_groups[] = { + "gpio6", "gpio7", "gpio8", "gpio9", "gpio14", "gpio15", +}; + +static const char *const qup_se1_groups[] = { + "gpio28", "gpio30", "gpio38", "gpio39", +}; + +static const char *const qup_se2_groups[] = { + "gpio12", "gpio13", "gpio20", "gpio21", "gpio52", "gpio53", +}; + +static const char *const qup_se3_groups[] = { + "gpio10", "gpio11", "gpio22", "gpio23", +}; + +static const char *const qup_se4_groups[] = { + "gpio40", "gpio41", "gpio42", "gpio43", "gpio52", "gpio53", +}; + +static const char *const qup_se5_groups[] = { + "gpio47", "gpio48", "gpio49", "gpio50", "gpio51", "gpio52", +}; + +static const char *const qup_se5_l1_groups[] = { + "gpio52", "gpio53", +}; + +static const char *const resout_groups[] = { + "gpio44", +}; + +static const char *const rx_los0_groups[] = { + "gpio37", "gpio42", +}; + +static const char *const rx_los1_groups[] = { + "gpio36", "gpio41", +}; + +static const char *const rx_los2_groups[] = { + "gpio35", "gpio40", +}; + +static const char *const sdc_clk_groups[] = { + "gpio5", +}; + +static const char *const sdc_cmd_groups[] = { + "gpio4", +}; + +static const char *const sdc_data_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", +}; + +static const char *const tsens_max_groups[] = { + "gpio20", +}; + +static const struct pinfunction ipq5210_functions[] = { + MSM_PIN_FUNCTION(atest_char_start), + MSM_PIN_FUNCTION(atest_char_status0), + MSM_PIN_FUNCTION(atest_char_status1), + MSM_PIN_FUNCTION(atest_char_status2), + MSM_PIN_FUNCTION(atest_char_status3), + MSM_PIN_FUNCTION(atest_tic_en), + MSM_PIN_FUNCTION(audio_pri), + MSM_PIN_FUNCTION(audio_pri_mclk_out0), + MSM_PIN_FUNCTION(audio_pri_mclk_in0), + MSM_PIN_FUNCTION(audio_pri_mclk_out1), + MSM_PIN_FUNCTION(audio_pri_mclk_in1), + MSM_PIN_FUNCTION(audio_pri_mclk_out2), + MSM_PIN_FUNCTION(audio_pri_mclk_in2), + MSM_PIN_FUNCTION(audio_pri_mclk_out3), + MSM_PIN_FUNCTION(audio_pri_mclk_in3), + MSM_PIN_FUNCTION(audio_sec), + MSM_PIN_FUNCTION(audio_sec_mclk_out0), + MSM_PIN_FUNCTION(audio_sec_mclk_in0), + MSM_PIN_FUNCTION(audio_sec_mclk_out1), + MSM_PIN_FUNCTION(audio_sec_mclk_in1), + MSM_PIN_FUNCTION(audio_sec_mclk_out2), + MSM_PIN_FUNCTION(audio_sec_mclk_in2), + MSM_PIN_FUNCTION(audio_sec_mclk_out3), + MSM_PIN_FUNCTION(audio_sec_mclk_in3), + MSM_PIN_FUNCTION(core_voltage_0), + MSM_PIN_FUNCTION(cri_trng0), + MSM_PIN_FUNCTION(cri_trng1), + MSM_PIN_FUNCTION(cri_trng2), + MSM_PIN_FUNCTION(cri_trng3), + MSM_PIN_FUNCTION(dbg_out_clk), + MSM_PIN_FUNCTION(dg_out), + MSM_PIN_FUNCTION(gcc_plltest_bypassnl), + MSM_PIN_FUNCTION(gcc_plltest_resetn), + MSM_PIN_FUNCTION(gcc_tlmm), + MSM_GPIO_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(led0), + MSM_PIN_FUNCTION(led1), + MSM_PIN_FUNCTION(led2), + MSM_PIN_FUNCTION(mdc_mst), + MSM_PIN_FUNCTION(mdc_slv0), + MSM_PIN_FUNCTION(mdc_slv1), + MSM_PIN_FUNCTION(mdc_slv2), + MSM_PIN_FUNCTION(mdio_mst), + MSM_PIN_FUNCTION(mdio_slv0), + MSM_PIN_FUNCTION(mdio_slv1), + MSM_PIN_FUNCTION(mdio_slv2), + MSM_PIN_FUNCTION(mux_tod_out), + MSM_PIN_FUNCTION(pcie0_clk_req_n), + MSM_PIN_FUNCTION(pcie0_wake), + MSM_PIN_FUNCTION(pcie1_clk_req_n), + MSM_PIN_FUNCTION(pcie1_wake), + MSM_PIN_FUNCTION(pll_test), + MSM_PIN_FUNCTION(pon_active_led), + MSM_PIN_FUNCTION(pon_mux_sel), + MSM_PIN_FUNCTION(pon_rx), + MSM_PIN_FUNCTION(pon_rx_los), + MSM_PIN_FUNCTION(pon_tx), + MSM_PIN_FUNCTION(pon_tx_burst), + MSM_PIN_FUNCTION(pon_tx_dis), + MSM_PIN_FUNCTION(pon_tx_fault), + MSM_PIN_FUNCTION(pon_tx_sd), + MSM_PIN_FUNCTION(gpn_rx_los), + MSM_PIN_FUNCTION(gpn_tx_burst), + MSM_PIN_FUNCTION(gpn_tx_dis), + MSM_PIN_FUNCTION(gpn_tx_fault), + MSM_PIN_FUNCTION(gpn_tx_sd), + MSM_PIN_FUNCTION(pps), + MSM_PIN_FUNCTION(pwm0), + MSM_PIN_FUNCTION(pwm1), + MSM_PIN_FUNCTION(pwm2), + MSM_PIN_FUNCTION(pwm3), + MSM_PIN_FUNCTION(qdss_cti_trig_in_a0), + MSM_PIN_FUNCTION(qdss_cti_trig_in_a1), + MSM_PIN_FUNCTION(qdss_cti_trig_in_b0), + MSM_PIN_FUNCTION(qdss_cti_trig_in_b1), + MSM_PIN_FUNCTION(qdss_cti_trig_out_a0), + MSM_PIN_FUNCTION(qdss_cti_trig_out_a1), + MSM_PIN_FUNCTION(qdss_cti_trig_out_b0), + MSM_PIN_FUNCTION(qdss_cti_trig_out_b1), + MSM_PIN_FUNCTION(qdss_traceclk_a), + MSM_PIN_FUNCTION(qdss_tracectl_a), + MSM_PIN_FUNCTION(qdss_tracedata_a), + MSM_PIN_FUNCTION(qrng_rosc0), + MSM_PIN_FUNCTION(qrng_rosc1), + MSM_PIN_FUNCTION(qrng_rosc2), + MSM_PIN_FUNCTION(qspi_data), + MSM_PIN_FUNCTION(qspi_clk), + MSM_PIN_FUNCTION(qspi_cs_n), + MSM_PIN_FUNCTION(qup_se0), + MSM_PIN_FUNCTION(qup_se1), + MSM_PIN_FUNCTION(qup_se2), + MSM_PIN_FUNCTION(qup_se3), + MSM_PIN_FUNCTION(qup_se4), + MSM_PIN_FUNCTION(qup_se5), + MSM_PIN_FUNCTION(qup_se5_l1), + MSM_PIN_FUNCTION(resout), + MSM_PIN_FUNCTION(rx_los0), + MSM_PIN_FUNCTION(rx_los1), + MSM_PIN_FUNCTION(rx_los2), + MSM_PIN_FUNCTION(sdc_clk), + MSM_PIN_FUNCTION(sdc_cmd), + MSM_PIN_FUNCTION(sdc_data), + MSM_PIN_FUNCTION(tsens_max), +}; + +static const struct msm_pingroup ipq5210_groups[] = { + [0] = PINGROUP(0, sdc_data, qspi_data, pwm2, _, _, _, _, _, _), + [1] = PINGROUP(1, sdc_data, qspi_data, pwm2, _, _, _, _, _, _), + [2] = PINGROUP(2, sdc_data, qspi_data, pwm2, _, _, _, _, _, _), + [3] = PINGROUP(3, sdc_data, qspi_data, pwm2, _, _, _, _, _, _), + [4] = PINGROUP(4, sdc_cmd, qspi_cs_n, _, _, _, _, _, _, _), + [5] = PINGROUP(5, sdc_clk, qspi_clk, _, _, _, _, _, _, _), + [6] = PINGROUP(6, qup_se0, led0, pwm1, _, cri_trng0, qdss_tracedata_a, _, _, _), + [7] = PINGROUP(7, qup_se0, led1, pwm1, _, cri_trng1, qdss_tracedata_a, _, _, _), + [8] = PINGROUP(8, qup_se0, pwm1, audio_pri_mclk_out2, audio_pri_mclk_in2, _, cri_trng2, qdss_tracedata_a, _, _), + [9] = PINGROUP(9, qup_se0, led2, pwm1, _, cri_trng3, qdss_tracedata_a, _, _, _), + [10] = PINGROUP(10, pon_rx_los, qup_se3, pwm0, _, _, qdss_tracedata_a, _, _, _), + [11] = PINGROUP(11, pon_active_led, qup_se3, pwm0, _, _, qdss_tracedata_a, _, _, _), + [12] = PINGROUP(12, pon_tx_dis, qup_se2, pwm0, audio_pri_mclk_out0, audio_pri_mclk_in0, _, qrng_rosc0, qdss_tracedata_a, _), + [13] = PINGROUP(13, gpn_tx_dis, qup_se2, pwm0, audio_pri_mclk_out3, audio_pri_mclk_in3, _, qrng_rosc1, qdss_tracedata_a, _), + [14] = PINGROUP(14, pon_tx_burst, qup_se0, _, qrng_rosc2, qdss_tracedata_a, _, _, _, _), + [15] = PINGROUP(15, pon_tx, qup_se0, _, qdss_tracedata_a, _, _, _, _, _), + [16] = PINGROUP(16, pon_tx_sd, audio_sec_mclk_out1, audio_sec_mclk_in1, qdss_cti_trig_out_b0, _, _, _, _, _), + [17] = PINGROUP(17, pon_tx_fault, audio_sec_mclk_out0, audio_sec_mclk_in0, _, _, _, _, _, _), + [18] = PINGROUP(18, pps, pll_test, _, _, _, _, _, _, _), + [19] = PINGROUP(19, mux_tod_out, audio_pri_mclk_out1, audio_pri_mclk_in1, _, _, _, _, _, _), + [20] = PINGROUP(20, qup_se2, mdc_slv1, tsens_max, qdss_tracedata_a, _, _, _, _, _), + [21] = PINGROUP(21, qup_se2, mdio_slv1, qdss_tracedata_a, _, _, _, _, _, _), + [22] = PINGROUP(22, core_voltage_0, qup_se3, pwm3, _, _, _, _, _, _), + [23] = PINGROUP(23, led0, qup_se3, dbg_out_clk, qdss_traceclk_a, _, _, _, _, _), + [24] = PINGROUP(24, _, _, _, _, _, _, _, _, _), + [25] = PINGROUP(25, _, _, _, _, _, _, _, _, _), + [26] = PINGROUP(26, mdc_mst, led2, _, qdss_tracectl_a, _, _, _, _, _), + [27] = PINGROUP(27, mdio_mst, led1, _, _, _, _, _, _, _), + [28] = PINGROUP(28, pcie1_clk_req_n, qup_se1, _, _, qdss_cti_trig_out_a0, _, _, _, _), + [29] = PINGROUP(29, _, _, _, _, _, _, _, _, _), + [30] = PINGROUP(30, pcie1_wake, qup_se1, _, _, qdss_cti_trig_in_a0, _, _, _, _), + [31] = PINGROUP(31, pcie0_clk_req_n, mdc_slv0, _, qdss_cti_trig_out_a1, _, _, _, _, _), + [32] = PINGROUP(32, _, _, _, _, _, _, _, _, _), + [33] = PINGROUP(33, pcie0_wake, mdio_slv0, qdss_cti_trig_in_a1, _, _, _, _, _, _), + [34] = PINGROUP(34, audio_pri, atest_char_status0, qdss_cti_trig_in_b0, _, _, _, _, _, _), + [35] = PINGROUP(35, audio_pri, rx_los2, atest_char_status1, qdss_cti_trig_out_b1, _, _, _, _, _), + [36] = PINGROUP(36, audio_pri, _, rx_los1, atest_char_status2, _, _, _, _, _), + [37] = PINGROUP(37, audio_pri, rx_los0, atest_char_status3, _, qdss_cti_trig_in_b1, _, _, _, _), + [38] = PINGROUP(38, qup_se1, led2, gcc_plltest_bypassnl, qdss_tracedata_a, _, _, _, _, _), + [39] = PINGROUP(39, qup_se1, led1, led0, gcc_tlmm, qdss_tracedata_a, _, _, _, _), + [40] = PINGROUP(40, qup_se4, rx_los2, audio_sec, gcc_plltest_resetn, qdss_tracedata_a, _, _, _, _), + [41] = PINGROUP(41, qup_se4, rx_los1, audio_sec, qdss_tracedata_a, _, _, _, _, _), + [42] = PINGROUP(42, qup_se4, rx_los0, audio_sec, atest_tic_en, _, _, _, _, _), + [43] = PINGROUP(43, qup_se4, audio_sec, _, _, _, _, _, _, _), + [44] = PINGROUP(44, resout, _, _, _, _, _, _, _, _), + [45] = PINGROUP(45, pon_mux_sel, _, _, _, _, _, _, _, _), + [46] = PINGROUP(46, dg_out, atest_char_start, _, _, _, _, _, _, _), + [47] = PINGROUP(47, gpn_rx_los, mdc_slv2, qup_se5, _, _, _, _, _, _), + [48] = PINGROUP(48, pon_rx, qup_se5, _, _, _, _, _, _, _), + [49] = PINGROUP(49, gpn_tx_fault, mdio_slv2, qup_se5, audio_sec_mclk_out2, audio_sec_mclk_in2, _, _, _, _), + [50] = PINGROUP(50, gpn_tx_sd, qup_se5, audio_sec_mclk_out3, audio_sec_mclk_in3, _, _, _, _, _), + [51] = PINGROUP(51, gpn_tx_burst, qup_se5, _, _, _, _, _, _, _), + [52] = PINGROUP(52, qup_se2, qup_se5, qup_se4, qup_se5_l1, _, _, _, _, _), + [53] = PINGROUP(53, qup_se2, qup_se4, qup_se5_l1, _, _, _, _, _, _), +}; + +static const struct msm_pinctrl_soc_data ipq5210_tlmm = { + .pins = ipq5210_pins, + .npins = ARRAY_SIZE(ipq5210_pins), + .functions = ipq5210_functions, + .nfunctions = ARRAY_SIZE(ipq5210_functions), + .groups = ipq5210_groups, + .ngroups = ARRAY_SIZE(ipq5210_groups), + .ngpios = 54, +}; + +static const struct of_device_id ipq5210_tlmm_of_match[] = { + { .compatible = "qcom,ipq5210-tlmm", }, + { }, +}; + +static int ipq5210_tlmm_probe(struct platform_device *pdev) +{ + return msm_pinctrl_probe(pdev, &ipq5210_tlmm); +} + +static struct platform_driver ipq5210_tlmm_driver = { + .driver = { + .name = "ipq5210-tlmm", + .of_match_table = ipq5210_tlmm_of_match, + }, + .probe = ipq5210_tlmm_probe, +}; + +static int __init ipq5210_tlmm_init(void) +{ + return platform_driver_register(&ipq5210_tlmm_driver); +} +arch_initcall(ipq5210_tlmm_init); + +static void __exit ipq5210_tlmm_exit(void) +{ + platform_driver_unregister(&ipq5210_tlmm_driver); +} +module_exit(ipq5210_tlmm_exit); + +MODULE_DESCRIPTION("QTI IPQ5210 TLMM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/qcom/pinctrl-ipq5332.c b/drivers/pinctrl/qcom/pinctrl-ipq5332.c index 239cbe75f198..26a7a8c818f3 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq5332.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq5332.c @@ -32,7 +32,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ diff --git a/drivers/pinctrl/qcom/pinctrl-ipq5424.c b/drivers/pinctrl/qcom/pinctrl-ipq5424.c index 67b452a033d6..362ad88a5386 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq5424.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq5424.c @@ -33,7 +33,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ diff --git a/drivers/pinctrl/qcom/pinctrl-ipq6018.c b/drivers/pinctrl/qcom/pinctrl-ipq6018.c index be177fb0a92d..cc83f9362a85 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq6018.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq6018.c @@ -32,7 +32,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ diff --git a/drivers/pinctrl/qcom/pinctrl-ipq8074.c b/drivers/pinctrl/qcom/pinctrl-ipq8074.c index e94de9083314..64ce8ea8f544 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq8074.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq8074.c @@ -32,7 +32,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ diff --git a/drivers/pinctrl/qcom/pinctrl-ipq9574.c b/drivers/pinctrl/qcom/pinctrl-ipq9574.c index 3ed093ea8eb9..09223eb166c9 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq9574.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq9574.c @@ -32,7 +32,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ diff --git a/drivers/pinctrl/qcom/pinctrl-kaanapali.c b/drivers/pinctrl/qcom/pinctrl-kaanapali.c index 364e6d997337..5cc45b9c55ab 100644 --- a/drivers/pinctrl/qcom/pinctrl-kaanapali.c +++ b/drivers/pinctrl/qcom/pinctrl-kaanapali.c @@ -34,7 +34,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -64,7 +63,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -89,7 +87,6 @@ .io_reg = io, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-mdm9607.c b/drivers/pinctrl/qcom/pinctrl-mdm9607.c index cef330547ce7..5794b0a11010 100644 --- a/drivers/pinctrl/qcom/pinctrl-mdm9607.c +++ b/drivers/pinctrl/qcom/pinctrl-mdm9607.c @@ -225,7 +225,6 @@ static const unsigned int qdsd_data3_pins[] = { 91 }; .io_reg = 0x4 + 0x1000 * id, \ .intr_cfg_reg = 0x8 + 0x1000 * id, \ .intr_status_reg = 0xc + 0x1000 * id, \ - .intr_target_reg = 0x8 + 0x1000 * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -251,7 +250,6 @@ static const unsigned int qdsd_data3_pins[] = { 91 }; .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-milos-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-milos-lpass-lpi.c new file mode 100644 index 000000000000..3bf6fe0cf1bb --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-milos-lpass-lpi.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022-2023 Linaro Ltd. + * Copyright (c) 2026 Luca Weiss + */ + +#include +#include +#include + +#include "pinctrl-lpass-lpi.h" + +enum lpass_lpi_functions { + LPI_MUX_dmic1_clk, + LPI_MUX_dmic1_data, + LPI_MUX_dmic2_clk, + LPI_MUX_dmic2_data, + LPI_MUX_dmic3_clk, + LPI_MUX_dmic3_data, + LPI_MUX_dmic4_clk, + LPI_MUX_dmic4_data, + LPI_MUX_i2s0_clk, + LPI_MUX_i2s0_data, + LPI_MUX_i2s0_ws, + LPI_MUX_i2s1_clk, + LPI_MUX_i2s1_data, + LPI_MUX_i2s1_ws, + LPI_MUX_i2s2_clk, + LPI_MUX_i2s2_data, + LPI_MUX_i2s2_ws, + LPI_MUX_i2s3_clk, + LPI_MUX_i2s3_data, + LPI_MUX_i2s3_ws, + LPI_MUX_qca_swr_clk, + LPI_MUX_qca_swr_data, + LPI_MUX_slimbus_clk, + LPI_MUX_slimbus_data, + LPI_MUX_swr_rx_clk, + LPI_MUX_swr_rx_data, + LPI_MUX_swr_tx_clk, + LPI_MUX_swr_tx_data, + LPI_MUX_wsa_swr_clk, + LPI_MUX_wsa_swr_data, + LPI_MUX_ext_mclk1_a, + LPI_MUX_ext_mclk1_b, + LPI_MUX_ext_mclk1_c, + LPI_MUX_ext_mclk1_d, + LPI_MUX_ext_mclk1_e, + LPI_MUX_gpio, + LPI_MUX__, +}; + +static const struct pinctrl_pin_desc milos_lpi_pins[] = { + PINCTRL_PIN(0, "gpio0"), + PINCTRL_PIN(1, "gpio1"), + PINCTRL_PIN(2, "gpio2"), + PINCTRL_PIN(3, "gpio3"), + PINCTRL_PIN(4, "gpio4"), + PINCTRL_PIN(5, "gpio5"), + PINCTRL_PIN(6, "gpio6"), + PINCTRL_PIN(7, "gpio7"), + PINCTRL_PIN(8, "gpio8"), + PINCTRL_PIN(9, "gpio9"), + PINCTRL_PIN(10, "gpio10"), + PINCTRL_PIN(11, "gpio11"), + PINCTRL_PIN(12, "gpio12"), + PINCTRL_PIN(13, "gpio13"), + PINCTRL_PIN(14, "gpio14"), + PINCTRL_PIN(15, "gpio15"), + PINCTRL_PIN(16, "gpio16"), + PINCTRL_PIN(17, "gpio17"), + PINCTRL_PIN(18, "gpio18"), + PINCTRL_PIN(19, "gpio19"), + PINCTRL_PIN(20, "gpio20"), + PINCTRL_PIN(21, "gpio21"), + PINCTRL_PIN(22, "gpio22"), +}; + +static const char * const gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", +}; + +static const char * const dmic1_clk_groups[] = { "gpio6" }; +static const char * const dmic1_data_groups[] = { "gpio7" }; +static const char * const dmic2_clk_groups[] = { "gpio8" }; +static const char * const dmic2_data_groups[] = { "gpio9" }; +static const char * const dmic3_clk_groups[] = { "gpio12" }; +static const char * const dmic3_data_groups[] = { "gpio13" }; +static const char * const dmic4_clk_groups[] = { "gpio21" }; +static const char * const dmic4_data_groups[] = { "gpio22" }; +static const char * const i2s0_clk_groups[] = { "gpio0" }; +static const char * const i2s0_ws_groups[] = { "gpio1" }; +static const char * const i2s0_data_groups[] = { "gpio2", "gpio3", "gpio4", "gpio5" }; +static const char * const i2s1_clk_groups[] = { "gpio6" }; +static const char * const i2s1_ws_groups[] = { "gpio7" }; +static const char * const i2s1_data_groups[] = { "gpio8", "gpio9" }; +static const char * const i2s2_clk_groups[] = { "gpio10" }; +static const char * const i2s2_ws_groups[] = { "gpio11" }; +static const char * const i2s2_data_groups[] = { "gpio12", "gpio13" }; +static const char * const i2s3_clk_groups[] = { "gpio19" }; +static const char * const i2s3_ws_groups[] = { "gpio20" }; +static const char * const i2s3_data_groups[] = { "gpio21", "gpio22" }; +static const char * const qca_swr_clk_groups[] = { "gpio19" }; +static const char * const qca_swr_data_groups[] = { "gpio20" }; +static const char * const slimbus_clk_groups[] = { "gpio19" }; +static const char * const slimbus_data_groups[] = { "gpio20" }; +static const char * const swr_rx_clk_groups[] = { "gpio3" }; +static const char * const swr_rx_data_groups[] = { "gpio4", "gpio5" }; +static const char * const swr_tx_clk_groups[] = { "gpio0" }; +static const char * const swr_tx_data_groups[] = { "gpio1", "gpio2", "gpio14" }; +static const char * const wsa_swr_clk_groups[] = { "gpio10" }; +static const char * const wsa_swr_data_groups[] = { "gpio11" }; +static const char * const ext_mclk1_a_groups[] = { "gpio13" }; +static const char * const ext_mclk1_b_groups[] = { "gpio9" }; +static const char * const ext_mclk1_c_groups[] = { "gpio5" }; +static const char * const ext_mclk1_d_groups[] = { "gpio14" }; +static const char * const ext_mclk1_e_groups[] = { "gpio22" }; + +static const struct lpi_pingroup milos_groups[] = { + LPI_PINGROUP(0, 0, swr_tx_clk, i2s0_clk, _, _), + LPI_PINGROUP(1, 2, swr_tx_data, i2s0_ws, _, _), + LPI_PINGROUP(2, 4, swr_tx_data, i2s0_data, _, _), + LPI_PINGROUP(3, 8, swr_rx_clk, i2s0_data, _, _), + LPI_PINGROUP(4, 10, swr_rx_data, i2s0_data, _, _), + LPI_PINGROUP(5, 12, swr_rx_data, ext_mclk1_c, i2s0_data, _), + LPI_PINGROUP(6, LPI_NO_SLEW, dmic1_clk, i2s1_clk, _, _), + LPI_PINGROUP(7, LPI_NO_SLEW, dmic1_data, i2s1_ws, _, _), + LPI_PINGROUP(8, LPI_NO_SLEW, dmic2_clk, i2s1_data, _, _), + LPI_PINGROUP(9, LPI_NO_SLEW, dmic2_data, i2s1_data, ext_mclk1_b, _), + LPI_PINGROUP(10, 16, wsa_swr_clk, i2s2_clk, _, _), + LPI_PINGROUP(11, 18, wsa_swr_data, i2s2_ws, _, _), + LPI_PINGROUP(12, LPI_NO_SLEW, dmic3_clk, i2s2_data, _, _), + LPI_PINGROUP(13, LPI_NO_SLEW, dmic3_data, i2s2_data, ext_mclk1_a, _), + LPI_PINGROUP(14, 6, swr_tx_data, ext_mclk1_d, _, _), + /* gpio15 - gpio18 do not really exist */ + LPI_PINGROUP(15, 20, _, _, _, _), + LPI_PINGROUP(16, 22, _, _, _, _), + LPI_PINGROUP(17, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(18, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(19, LPI_NO_SLEW, i2s3_clk, slimbus_clk, qca_swr_clk, _), + LPI_PINGROUP(20, LPI_NO_SLEW, i2s3_ws, slimbus_data, qca_swr_data, _), + LPI_PINGROUP(21, LPI_NO_SLEW, i2s3_data, dmic4_clk, _, _), + LPI_PINGROUP(22, LPI_NO_SLEW, i2s3_data, dmic4_data, ext_mclk1_e, _), +}; + +static const struct lpi_function milos_functions[] = { + LPI_FUNCTION(gpio), + LPI_FUNCTION(dmic1_clk), + LPI_FUNCTION(dmic1_data), + LPI_FUNCTION(dmic2_clk), + LPI_FUNCTION(dmic2_data), + LPI_FUNCTION(dmic3_clk), + LPI_FUNCTION(dmic3_data), + LPI_FUNCTION(dmic4_clk), + LPI_FUNCTION(dmic4_data), + LPI_FUNCTION(i2s0_clk), + LPI_FUNCTION(i2s0_data), + LPI_FUNCTION(i2s0_ws), + LPI_FUNCTION(i2s1_clk), + LPI_FUNCTION(i2s1_data), + LPI_FUNCTION(i2s1_ws), + LPI_FUNCTION(i2s2_clk), + LPI_FUNCTION(i2s2_data), + LPI_FUNCTION(i2s2_ws), + LPI_FUNCTION(i2s3_clk), + LPI_FUNCTION(i2s3_data), + LPI_FUNCTION(i2s3_ws), + LPI_FUNCTION(qca_swr_clk), + LPI_FUNCTION(qca_swr_data), + LPI_FUNCTION(slimbus_clk), + LPI_FUNCTION(slimbus_data), + LPI_FUNCTION(swr_rx_clk), + LPI_FUNCTION(swr_rx_data), + LPI_FUNCTION(swr_tx_clk), + LPI_FUNCTION(swr_tx_data), + LPI_FUNCTION(wsa_swr_clk), + LPI_FUNCTION(wsa_swr_data), + LPI_FUNCTION(ext_mclk1_a), + LPI_FUNCTION(ext_mclk1_b), + LPI_FUNCTION(ext_mclk1_c), + LPI_FUNCTION(ext_mclk1_d), + LPI_FUNCTION(ext_mclk1_e), +}; + +static const struct lpi_pinctrl_variant_data milos_lpi_data = { + .pins = milos_lpi_pins, + .npins = ARRAY_SIZE(milos_lpi_pins), + .groups = milos_groups, + .ngroups = ARRAY_SIZE(milos_groups), + .functions = milos_functions, + .nfunctions = ARRAY_SIZE(milos_functions), +}; + +static const struct of_device_id lpi_pinctrl_of_match[] = { + { + .compatible = "qcom,milos-lpass-lpi-pinctrl", + .data = &milos_lpi_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); + +static struct platform_driver lpi_pinctrl_driver = { + .driver = { + .name = "qcom-milos-lpass-lpi-pinctrl", + .of_match_table = lpi_pinctrl_of_match, + }, + .probe = lpi_pinctrl_probe, + .remove = lpi_pinctrl_remove, +}; + +module_platform_driver(lpi_pinctrl_driver); +MODULE_DESCRIPTION("Qualcomm Milos LPI GPIO pin control driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/qcom/pinctrl-milos.c b/drivers/pinctrl/qcom/pinctrl-milos.c index 19abd5233a2c..74b5253257af 100644 --- a/drivers/pinctrl/qcom/pinctrl-milos.c +++ b/drivers/pinctrl/qcom/pinctrl-milos.c @@ -36,7 +36,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -67,7 +66,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -92,7 +90,6 @@ .io_reg = io, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index e99871b90ab9..45b3a2763eb8 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -98,7 +98,22 @@ MSM_ACCESSOR(ctl) MSM_ACCESSOR(io) MSM_ACCESSOR(intr_cfg) MSM_ACCESSOR(intr_status) -MSM_ACCESSOR(intr_target) + +static u32 msm_readl_intr_target(struct msm_pinctrl *pctrl, + const struct msm_pingroup *g) +{ + u32 reg = g->intr_target_reg ? g->intr_target_reg : g->intr_cfg_reg; + + return readl(pctrl->regs[g->tile] + reg); +} + +static void msm_writel_intr_target(u32 val, struct msm_pinctrl *pctrl, + const struct msm_pingroup *g) +{ + u32 reg = g->intr_target_reg ? g->intr_target_reg : g->intr_cfg_reg; + + writel(val, pctrl->regs[g->tile] + reg); +} static void msm_ack_intr_status(struct msm_pinctrl *pctrl, const struct msm_pingroup *g) @@ -1078,7 +1093,8 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) intr_target_mask = GENMASK(g->intr_target_width - 1, 0); if (pctrl->intr_target_use_scm) { - u32 addr = pctrl->phys_base[0] + g->intr_target_reg; + u32 reg = g->intr_target_reg ? g->intr_target_reg : g->intr_cfg_reg; + u32 addr = pctrl->phys_base[0] + reg; int ret; qcom_scm_io_readl(addr, &val); diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h index 4625fa5320a9..a4af279f748a 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.h +++ b/drivers/pinctrl/qcom/pinctrl-msm.h @@ -52,7 +52,11 @@ struct pinctrl_pin_desc; * @intr_cfg_reg: Offset of the register holding interrupt configuration bits. * @intr_status_reg: Offset of the register holding the status bits for this group. * @intr_target_reg: Offset of the register specifying routing of the interrupts - * from this group. + * from this group. On most SoCs this register is the same as + * @intr_cfg_reg; leaving this field as zero causes the driver + * to fall back to @intr_cfg_reg automatically. Only set this + * explicitly on older SoCs where the interrupt target routing + * lives in a separate register (e.g. APQ8064, MSM8960). * @mux_bit: Offset in @ctl_reg for the pinmux function selection. * @pull_bit: Offset in @ctl_reg for the bias configuration. * @drv_bit: Offset in @ctl_reg for the drive strength configuration. diff --git a/drivers/pinctrl/qcom/pinctrl-msm8226.c b/drivers/pinctrl/qcom/pinctrl-msm8226.c index a81aa092ef12..d27b7599ea83 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8226.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8226.c @@ -282,7 +282,6 @@ static const unsigned int sdc2_data_pins[] = { 122 }; .io_reg = 0x1004 + 0x10 * id, \ .intr_cfg_reg = 0x1008 + 0x10 * id, \ .intr_status_reg = 0x100c + 0x10 * id, \ - .intr_target_reg = 0x1008 + 0x10 * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -308,7 +307,6 @@ static const unsigned int sdc2_data_pins[] = { 122 }; .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-msm8909.c b/drivers/pinctrl/qcom/pinctrl-msm8909.c index 544a52fb8f3d..8fa922d89101 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8909.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8909.c @@ -33,7 +33,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -59,7 +58,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-msm8916.c b/drivers/pinctrl/qcom/pinctrl-msm8916.c index b1b6934bb4b6..709c5d1d4d0a 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8916.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8916.c @@ -307,7 +307,6 @@ static const unsigned int qdsd_data3_pins[] = { 133 }; .io_reg = 0x4 + 0x1000 * id, \ .intr_cfg_reg = 0x8 + 0x1000 * id, \ .intr_status_reg = 0xc + 0x1000 * id, \ - .intr_target_reg = 0x8 + 0x1000 * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -333,7 +332,6 @@ static const unsigned int qdsd_data3_pins[] = { 133 }; .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-msm8917.c b/drivers/pinctrl/qcom/pinctrl-msm8917.c index f23d92d6615b..d1ede4891703 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8917.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8917.c @@ -333,7 +333,6 @@ static const unsigned int qdsd_data3_pins[] = { 146 }; .io_reg = 0x4 + 0x1000 * id, \ .intr_cfg_reg = 0x8 + 0x1000 * id, \ .intr_status_reg = 0xc + 0x1000 * id, \ - .intr_target_reg = 0x8 + 0x1000 * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -359,7 +358,6 @@ static const unsigned int qdsd_data3_pins[] = { 146 }; .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-msm8953.c b/drivers/pinctrl/qcom/pinctrl-msm8953.c index 67db062fdf56..02ea89f5feaa 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8953.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8953.c @@ -29,7 +29,6 @@ .io_reg = 0x4 + 0x1000 * id, \ .intr_cfg_reg = 0x8 + 0x1000 * id, \ .intr_status_reg = 0xc + 0x1000 * id, \ - .intr_target_reg = 0x8 + 0x1000 * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -55,7 +54,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-msm8976.c b/drivers/pinctrl/qcom/pinctrl-msm8976.c index 345539b9e696..906a90778b97 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8976.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8976.c @@ -35,7 +35,6 @@ .io_reg = REG_BASE + 0x4 + REG_SIZE * id, \ .intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \ - .intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -61,7 +60,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-msm8994.c b/drivers/pinctrl/qcom/pinctrl-msm8994.c index 94e042d1f4b2..ecbe6b91d1da 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8994.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8994.c @@ -33,7 +33,6 @@ .io_reg = 0x1004 + 0x10 * id, \ .intr_cfg_reg = 0x1008 + 0x10 * id, \ .intr_status_reg = 0x100c + 0x10 * id, \ - .intr_target_reg = 0x1008 + 0x10 * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -59,7 +58,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-msm8996.c b/drivers/pinctrl/qcom/pinctrl-msm8996.c index e5b55693d023..73b07a10a957 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8996.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8996.c @@ -33,7 +33,6 @@ .io_reg = REG_BASE + 0x4 + REG_SIZE * id, \ .intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \ - .intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -59,7 +58,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-msm8998.c b/drivers/pinctrl/qcom/pinctrl-msm8998.c index b727593af34a..dcf11b79e562 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8998.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8998.c @@ -35,7 +35,6 @@ .io_reg = base + 0x4 + 0x1000 * id, \ .intr_cfg_reg = base + 0x8 + 0x1000 * id, \ .intr_status_reg = base + 0xc + 0x1000 * id, \ - .intr_target_reg = base + 0x8 + 0x1000 * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -61,7 +60,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -86,7 +84,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-msm8x74.c b/drivers/pinctrl/qcom/pinctrl-msm8x74.c index 202bec003e96..ff432ec5815a 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm8x74.c +++ b/drivers/pinctrl/qcom/pinctrl-msm8x74.c @@ -344,7 +344,6 @@ static const unsigned int hsic_data_pins[] = { 153 }; .io_reg = 0x1004 + 0x10 * id, \ .intr_cfg_reg = 0x1008 + 0x10 * id, \ .intr_status_reg = 0x100c + 0x10 * id, \ - .intr_target_reg = 0x1008 + 0x10 * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -370,7 +369,6 @@ static const unsigned int hsic_data_pins[] = { 153 }; .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -401,7 +399,6 @@ static const unsigned int hsic_data_pins[] = { 153 }; .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = 25, \ .pull_bit = -1, \ .drv_bit = -1, \ diff --git a/drivers/pinctrl/qcom/pinctrl-qcm2290.c b/drivers/pinctrl/qcom/pinctrl-qcm2290.c index 38200957451e..3b28ac498885 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcm2290.c +++ b/drivers/pinctrl/qcom/pinctrl-qcm2290.c @@ -33,7 +33,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -61,7 +60,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -86,7 +84,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-qcs404.c b/drivers/pinctrl/qcom/pinctrl-qcs404.c index 0b8db2c7e58a..1048a7093b2e 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs404.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs404.c @@ -43,7 +43,6 @@ enum { .io_reg = 0x1000 * id + 0x4, \ .intr_cfg_reg = 0x1000 * id + 0x8, \ .intr_status_reg = 0x1000 * id + 0xc, \ - .intr_target_reg = 0x1000 * id + 0x8, \ .tile = _tile, \ .mux_bit = 2, \ .pull_bit = 0, \ @@ -70,7 +69,6 @@ enum { .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = SOUTH, \ .mux_bit = -1, \ .pull_bit = pull, \ diff --git a/drivers/pinctrl/qcom/pinctrl-qcs615.c b/drivers/pinctrl/qcom/pinctrl-qcs615.c index f1c827ddbfbf..0ed4332d989e 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs615.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs615.c @@ -43,7 +43,6 @@ static const char * const qcs615_tiles[] = { .io_reg = 0x1000 * id + 0x4, \ .intr_cfg_reg = 0x1000 * id + 0x8, \ .intr_status_reg = 0x1000 * id + 0xc, \ - .intr_target_reg = 0x1000 * id + 0x8, \ .tile = _tile, \ .mux_bit = 2, \ .pull_bit = 0, \ @@ -70,7 +69,6 @@ static const char * const qcs615_tiles[] = { .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = _tile, \ .mux_bit = -1, \ .pull_bit = pull, \ @@ -96,7 +94,6 @@ static const char * const qcs615_tiles[] = { .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = WEST, \ .mux_bit = -1, \ .pull_bit = 3, \ diff --git a/drivers/pinctrl/qcom/pinctrl-qcs8300.c b/drivers/pinctrl/qcom/pinctrl-qcs8300.c index f1af1a620684..852cd36df6d5 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs8300.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs8300.c @@ -34,7 +34,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -62,7 +61,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -87,7 +85,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-qdf2xxx.c b/drivers/pinctrl/qcom/pinctrl-qdf2xxx.c index 9ecc4d40e4dc..3b9edcf8780b 100644 --- a/drivers/pinctrl/qcom/pinctrl-qdf2xxx.c +++ b/drivers/pinctrl/qcom/pinctrl-qdf2xxx.c @@ -106,7 +106,6 @@ static int qdf2xxx_pinctrl_probe(struct platform_device *pdev) groups[gpio].io_reg = 0x04 + 0x10000 * gpio; groups[gpio].intr_cfg_reg = 0x08 + 0x10000 * gpio; groups[gpio].intr_status_reg = 0x0c + 0x10000 * gpio; - groups[gpio].intr_target_reg = 0x08 + 0x10000 * gpio; groups[gpio].mux_bit = 2; groups[gpio].pull_bit = 0; diff --git a/drivers/pinctrl/qcom/pinctrl-qdu1000.c b/drivers/pinctrl/qcom/pinctrl-qdu1000.c index 7c535698a780..5125df7eb127 100644 --- a/drivers/pinctrl/qcom/pinctrl-qdu1000.c +++ b/drivers/pinctrl/qcom/pinctrl-qdu1000.c @@ -35,7 +35,6 @@ .io_reg = REG_BASE + 0x4 + REG_SIZE * id, \ .intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \ - .intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -61,7 +60,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -86,7 +84,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sa8775p.c b/drivers/pinctrl/qcom/pinctrl-sa8775p.c index 53f28b9c49ba..e9a510d3583f 100644 --- a/drivers/pinctrl/qcom/pinctrl-sa8775p.c +++ b/drivers/pinctrl/qcom/pinctrl-sa8775p.c @@ -34,7 +34,6 @@ .io_reg = REG_BASE + 0x4 + REG_SIZE * id, \ .intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \ - .intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -63,7 +62,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -88,7 +86,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sar2130p.c b/drivers/pinctrl/qcom/pinctrl-sar2130p.c index 4a53f4ee2041..1d1b5de4eefd 100644 --- a/drivers/pinctrl/qcom/pinctrl-sar2130p.c +++ b/drivers/pinctrl/qcom/pinctrl-sar2130p.c @@ -34,7 +34,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -62,7 +61,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sc7180.c b/drivers/pinctrl/qcom/pinctrl-sc7180.c index 3eae51472b13..01cfcb416f33 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc7180.c +++ b/drivers/pinctrl/qcom/pinctrl-sc7180.c @@ -41,7 +41,6 @@ enum { .io_reg = 0x1000 * id + 0x4, \ .intr_cfg_reg = 0x1000 * id + 0x8, \ .intr_status_reg = 0x1000 * id + 0xc, \ - .intr_target_reg = 0x1000 * id + 0x8, \ .tile = _tile, \ .mux_bit = 2, \ .pull_bit = 0, \ @@ -68,7 +67,6 @@ enum { .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = SOUTH, \ .mux_bit = -1, \ .pull_bit = pull, \ @@ -94,7 +92,6 @@ enum { .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = SOUTH, \ .mux_bit = -1, \ .pull_bit = 3, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sc7280.c b/drivers/pinctrl/qcom/pinctrl-sc7280.c index 44e09608aad0..f22fd56efd89 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc7280.c +++ b/drivers/pinctrl/qcom/pinctrl-sc7280.c @@ -31,7 +31,6 @@ .io_reg = 0x1000 * id + 0x4, \ .intr_cfg_reg = 0x1000 * id + 0x8, \ .intr_status_reg = 0x1000 * id + 0xc, \ - .intr_target_reg = 0x1000 * id + 0x8, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -59,7 +58,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -84,7 +82,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sc8180x.c b/drivers/pinctrl/qcom/pinctrl-sc8180x.c index d9f9e3dd9dd1..062cb913e5ee 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc8180x.c +++ b/drivers/pinctrl/qcom/pinctrl-sc8180x.c @@ -60,7 +60,6 @@ static const struct tile_info sc8180x_tile_info[] = { .io_reg = REG_SIZE * id + 0x4 + offset, \ .intr_cfg_reg = REG_SIZE * id + 0x8 + offset, \ .intr_status_reg = REG_SIZE * id + 0xc + offset,\ - .intr_target_reg = REG_SIZE * id + 0x8 + offset,\ .tile = _tile, \ .mux_bit = 2, \ .pull_bit = 0, \ @@ -90,7 +89,6 @@ static const struct tile_info sc8180x_tile_info[] = { .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = EAST, \ .mux_bit = -1, \ .pull_bit = pull, \ @@ -116,7 +114,6 @@ static const struct tile_info sc8180x_tile_info[] = { .io_reg = 0xb6004, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = SOUTH, \ .mux_bit = -1, \ .pull_bit = 3, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c index cf8297e8b8f8..4056b9fa32f8 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c @@ -31,7 +31,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -59,7 +58,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -84,7 +82,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sdm660.c b/drivers/pinctrl/qcom/pinctrl-sdm660.c index 687d986de75c..ab0368653d30 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm660.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm660.c @@ -46,7 +46,6 @@ enum { .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .tile = _tile, \ .mux_bit = 2, \ .pull_bit = 0, \ @@ -73,7 +72,6 @@ enum { .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = NORTH, \ .mux_bit = -1, \ .pull_bit = pull, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sdm670-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sdm670-lpass-lpi.c new file mode 100644 index 000000000000..858146c408d0 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sdm670-lpass-lpi.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023-2026, Richard Acayan. All rights reserved. + */ + +#include +#include +#include +#include +#include + +#include "pinctrl-lpass-lpi.h" + +enum lpass_lpi_functions { + LPI_MUX_comp_rx, + LPI_MUX_dmic1_clk, + LPI_MUX_dmic1_data, + LPI_MUX_dmic2_clk, + LPI_MUX_dmic2_data, + LPI_MUX_i2s1_clk, + LPI_MUX_i2s1_data, + LPI_MUX_i2s1_ws, + LPI_MUX_lpi_cdc_rst, + LPI_MUX_mclk0, + LPI_MUX_pdm_rx, + LPI_MUX_pdm_sync, + LPI_MUX_pdm_tx, + LPI_MUX_slimbus_clk, + LPI_MUX_gpio, + LPI_MUX__, +}; + +static const struct pinctrl_pin_desc sdm670_lpi_pinctrl_pins[] = { + PINCTRL_PIN(0, "gpio0"), + PINCTRL_PIN(1, "gpio1"), + PINCTRL_PIN(2, "gpio2"), + PINCTRL_PIN(3, "gpio3"), + PINCTRL_PIN(4, "gpio4"), + PINCTRL_PIN(5, "gpio5"), + PINCTRL_PIN(6, "gpio6"), + PINCTRL_PIN(7, "gpio7"), + PINCTRL_PIN(8, "gpio8"), + PINCTRL_PIN(9, "gpio9"), + PINCTRL_PIN(10, "gpio10"), + PINCTRL_PIN(11, "gpio11"), + PINCTRL_PIN(12, "gpio12"), + PINCTRL_PIN(13, "gpio13"), + PINCTRL_PIN(14, "gpio14"), + PINCTRL_PIN(15, "gpio15"), + PINCTRL_PIN(16, "gpio16"), + PINCTRL_PIN(17, "gpio17"), + PINCTRL_PIN(18, "gpio18"), + PINCTRL_PIN(19, "gpio19"), + PINCTRL_PIN(20, "gpio20"), + PINCTRL_PIN(21, "gpio21"), + PINCTRL_PIN(22, "gpio22"), + PINCTRL_PIN(23, "gpio23"), + PINCTRL_PIN(24, "gpio24"), + PINCTRL_PIN(25, "gpio25"), + PINCTRL_PIN(26, "gpio26"), + PINCTRL_PIN(27, "gpio27"), + PINCTRL_PIN(28, "gpio28"), + PINCTRL_PIN(29, "gpio29"), + PINCTRL_PIN(30, "gpio30"), + PINCTRL_PIN(31, "gpio31"), +}; + +static const char * const comp_rx_groups[] = { "gpio22", "gpio24" }; +static const char * const dmic1_clk_groups[] = { "gpio26" }; +static const char * const dmic1_data_groups[] = { "gpio27" }; +static const char * const dmic2_clk_groups[] = { "gpio28" }; +static const char * const dmic2_data_groups[] = { "gpio29" }; +static const char * const i2s1_clk_groups[] = { "gpio8" }; +static const char * const i2s1_ws_groups[] = { "gpio9" }; +static const char * const i2s1_data_groups[] = { "gpio10", "gpio11" }; +static const char * const lpi_cdc_rst_groups[] = { "gpio29" }; +static const char * const mclk0_groups[] = { "gpio19" }; +static const char * const pdm_rx_groups[] = { "gpio21", "gpio23", "gpio25" }; +static const char * const pdm_sync_groups[] = { "gpio19" }; +static const char * const pdm_tx_groups[] = { "gpio20" }; +static const char * const slimbus_clk_groups[] = { "gpio18" }; + +static const struct lpi_pingroup sdm670_lpi_pinctrl_groups[] = { + LPI_PINGROUP(0, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(1, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(2, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(3, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(4, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(5, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(6, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(7, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(8, LPI_NO_SLEW, _, _, i2s1_clk, _), + LPI_PINGROUP(9, LPI_NO_SLEW, _, _, i2s1_ws, _), + LPI_PINGROUP(10, LPI_NO_SLEW, _, _, _, i2s1_data), + LPI_PINGROUP(11, LPI_NO_SLEW, _, i2s1_data, _, _), + LPI_PINGROUP(12, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(13, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(14, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(15, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(16, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(17, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(18, LPI_NO_SLEW, _, slimbus_clk, _, _), + LPI_PINGROUP(19, LPI_NO_SLEW, mclk0, _, pdm_sync, _), + LPI_PINGROUP(20, LPI_NO_SLEW, _, pdm_tx, _, _), + LPI_PINGROUP(21, LPI_NO_SLEW, _, pdm_rx, _, _), + LPI_PINGROUP(22, LPI_NO_SLEW, _, comp_rx, _, _), + LPI_PINGROUP(23, LPI_NO_SLEW, pdm_rx, _, _, _), + LPI_PINGROUP(24, LPI_NO_SLEW, comp_rx, _, _, _), + LPI_PINGROUP(25, LPI_NO_SLEW, pdm_rx, _, _, _), + LPI_PINGROUP(26, LPI_NO_SLEW, dmic1_clk, _, _, _), + LPI_PINGROUP(27, LPI_NO_SLEW, dmic1_data, _, _, _), + LPI_PINGROUP(28, LPI_NO_SLEW, dmic2_clk, _, _, _), + LPI_PINGROUP(29, LPI_NO_SLEW, dmic2_data, lpi_cdc_rst, _, _), + LPI_PINGROUP(30, LPI_NO_SLEW, _, _, _, _), + LPI_PINGROUP(31, LPI_NO_SLEW, _, _, _, _), +}; + +static const struct lpi_function sdm670_lpi_pinctrl_functions[] = { + LPI_FUNCTION(comp_rx), + LPI_FUNCTION(dmic1_clk), + LPI_FUNCTION(dmic1_data), + LPI_FUNCTION(dmic2_clk), + LPI_FUNCTION(dmic2_data), + LPI_FUNCTION(i2s1_clk), + LPI_FUNCTION(i2s1_data), + LPI_FUNCTION(i2s1_ws), + LPI_FUNCTION(lpi_cdc_rst), + LPI_FUNCTION(mclk0), + LPI_FUNCTION(pdm_tx), + LPI_FUNCTION(pdm_rx), + LPI_FUNCTION(pdm_sync), + LPI_FUNCTION(slimbus_clk), +}; + +static const struct lpi_pinctrl_variant_data sdm670_lpi_pinctrl_data = { + .pins = sdm670_lpi_pinctrl_pins, + .npins = ARRAY_SIZE(sdm670_lpi_pinctrl_pins), + .groups = sdm670_lpi_pinctrl_groups, + .ngroups = ARRAY_SIZE(sdm670_lpi_pinctrl_groups), + .functions = sdm670_lpi_pinctrl_functions, + .nfunctions = ARRAY_SIZE(sdm670_lpi_pinctrl_functions), + .flags = LPI_FLAG_SLEW_RATE_SAME_REG, +}; + +static const struct of_device_id sdm670_lpi_pinctrl_of_match[] = { + { + .compatible = "qcom,sdm670-lpass-lpi-pinctrl", + .data = &sdm670_lpi_pinctrl_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, sdm670_lpi_pinctrl_of_match); + +static struct platform_driver sdm670_lpi_pinctrl_driver = { + .driver = { + .name = "qcom-sdm670-lpass-lpi-pinctrl", + .of_match_table = sdm670_lpi_pinctrl_of_match, + }, + .probe = lpi_pinctrl_probe, + .remove = lpi_pinctrl_remove, +}; +module_platform_driver(sdm670_lpi_pinctrl_driver); + +MODULE_AUTHOR("Richard Acayan "); +MODULE_DESCRIPTION("QTI SDM670 LPI GPIO pin control driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/qcom/pinctrl-sdm670.c b/drivers/pinctrl/qcom/pinctrl-sdm670.c index 486b72edf7b4..533b87c39cd5 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm670.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm670.c @@ -37,7 +37,6 @@ .io_reg = base + 0x4 + REG_SIZE * id, \ .intr_cfg_reg = base + 0x8 + REG_SIZE * id, \ .intr_status_reg = base + 0xc + REG_SIZE * id, \ - .intr_target_reg = base + 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -67,7 +66,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = -1, \ .drv_bit = -1, \ @@ -92,7 +90,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -117,7 +114,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c index 4cf8575797a0..b5ed2311b70e 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm845.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c @@ -37,7 +37,6 @@ .io_reg = base + 0x4 + REG_SIZE * id, \ .intr_cfg_reg = base + 0x8 + REG_SIZE * id, \ .intr_status_reg = base + 0xc + REG_SIZE * id, \ - .intr_target_reg = base + 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -63,7 +62,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -88,7 +86,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sdx55.c b/drivers/pinctrl/qcom/pinctrl-sdx55.c index 79a7010b73f1..3e87f5927924 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdx55.c +++ b/drivers/pinctrl/qcom/pinctrl-sdx55.c @@ -33,7 +33,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -59,7 +58,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sdx65.c b/drivers/pinctrl/qcom/pinctrl-sdx65.c index cc8a99a6a91e..4e787341b2a2 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdx65.c +++ b/drivers/pinctrl/qcom/pinctrl-sdx65.c @@ -33,7 +33,6 @@ .io_reg = REG_BASE + 0x4 + REG_SIZE * id, \ .intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \ - .intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -59,7 +58,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -84,7 +82,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sdx75.c b/drivers/pinctrl/qcom/pinctrl-sdx75.c index 4078d83d818c..9a7e359dbd23 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdx75.c +++ b/drivers/pinctrl/qcom/pinctrl-sdx75.c @@ -19,7 +19,6 @@ .io_reg = REG_BASE + 0x4 + REG_SIZE * id, \ .intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \ - .intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -60,7 +59,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm4450.c b/drivers/pinctrl/qcom/pinctrl-sm4450.c index d51e271e3361..83650f173b01 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm4450.c +++ b/drivers/pinctrl/qcom/pinctrl-sm4450.c @@ -33,7 +33,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -61,7 +60,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -86,7 +84,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm6115.c b/drivers/pinctrl/qcom/pinctrl-sm6115.c index 06700685ea2a..234451fbf47b 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6115.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6115.c @@ -43,7 +43,6 @@ enum { .io_reg = 0x4 + 0x1000 * id, \ .intr_cfg_reg = 0x8 + 0x1000 * id, \ .intr_status_reg = 0xc + 0x1000 * id, \ - .intr_target_reg = 0x8 + 0x1000 * id, \ .tile = _tile, \ .mux_bit = 2, \ .pull_bit = 0, \ @@ -70,7 +69,6 @@ enum { .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = _tile, \ .mux_bit = -1, \ .pull_bit = pull, \ @@ -96,7 +94,6 @@ enum { .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = WEST, \ .mux_bit = -1, \ .pull_bit = 3, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm6125.c b/drivers/pinctrl/qcom/pinctrl-sm6125.c index 5d3d1e402345..2cf9136860fc 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6125.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6125.c @@ -40,7 +40,6 @@ enum { .io_reg = 0x4 + 0x1000 * id, \ .intr_cfg_reg = 0x8 + 0x1000 * id, \ .intr_status_reg = 0xc + 0x1000 * id, \ - .intr_target_reg = 0x8 + 0x1000 * id, \ .tile = _tile, \ .mux_bit = 2, \ .pull_bit = 0, \ @@ -67,7 +66,6 @@ enum { .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = _tile, \ .mux_bit = -1, \ .pull_bit = pull, \ @@ -93,7 +91,6 @@ enum { .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = WEST, \ .mux_bit = -1, \ .pull_bit = 3, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm6350.c b/drivers/pinctrl/qcom/pinctrl-sm6350.c index 220fb582cac9..eb8cd4aa8a97 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6350.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6350.c @@ -33,7 +33,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -59,7 +58,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -84,7 +82,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm6375.c b/drivers/pinctrl/qcom/pinctrl-sm6375.c index 08b8ef6efaf0..d4547dd9f21f 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6375.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6375.c @@ -34,7 +34,6 @@ .io_reg = REG_SIZE * id + 0x4, \ .intr_cfg_reg = REG_SIZE * id + 0x8, \ .intr_status_reg = REG_SIZE * id + 0xc, \ - .intr_target_reg = REG_SIZE * id + 0x8, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -62,7 +61,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -87,7 +85,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm7150.c b/drivers/pinctrl/qcom/pinctrl-sm7150.c index 78dd8153a4d4..a01437c37525 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm7150.c +++ b/drivers/pinctrl/qcom/pinctrl-sm7150.c @@ -47,7 +47,6 @@ enum { .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .tile = _tile, \ .mux_bit = 2, \ .pull_bit = 0, \ @@ -74,7 +73,6 @@ enum { .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = _tile, \ .mux_bit = -1, \ .pull_bit = pull, \ @@ -100,7 +98,6 @@ enum { .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = WEST, \ .mux_bit = -1, \ .pull_bit = 3, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c index ad861cd66958..0767261f5149 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8150.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c @@ -43,7 +43,6 @@ enum { .io_reg = 0x1000 * id + 0x4, \ .intr_cfg_reg = 0x1000 * id + 0x8, \ .intr_status_reg = 0x1000 * id + 0xc, \ - .intr_target_reg = 0x1000 * id + 0x8, \ .tile = _tile, \ .mux_bit = 2, \ .pull_bit = 0, \ @@ -70,7 +69,6 @@ enum { .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = NORTH, \ .mux_bit = -1, \ .pull_bit = pull, \ @@ -96,7 +94,6 @@ enum { .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = SOUTH, \ .mux_bit = -1, \ .pull_bit = 3, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm8250.c b/drivers/pinctrl/qcom/pinctrl-sm8250.c index f05361f3100d..f73f3b052de4 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8250.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8250.c @@ -44,7 +44,6 @@ enum { .io_reg = REG_SIZE * id + 0x4, \ .intr_cfg_reg = REG_SIZE * id + 0x8, \ .intr_status_reg = REG_SIZE * id + 0xc, \ - .intr_target_reg = REG_SIZE * id + 0x8, \ .tile = _tile, \ .mux_bit = 2, \ .pull_bit = 0, \ @@ -73,7 +72,6 @@ enum { .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = NORTH, \ .mux_bit = -1, \ .pull_bit = pull, \ @@ -99,7 +97,6 @@ enum { .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .tile = SOUTH, \ .mux_bit = -1, \ .pull_bit = 3, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm8350.c b/drivers/pinctrl/qcom/pinctrl-sm8350.c index 99949b552021..377ddfc77e4f 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8350.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8350.c @@ -34,7 +34,6 @@ .io_reg = REG_SIZE * id + 0x4, \ .intr_cfg_reg = REG_SIZE * id + 0x8, \ .intr_status_reg = REG_SIZE * id + 0xc, \ - .intr_target_reg = REG_SIZE * id + 0x8, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -60,7 +59,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -85,7 +83,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm8450.c b/drivers/pinctrl/qcom/pinctrl-sm8450.c index 9889fc5dc2cd..a1d84074ea49 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8450.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8450.c @@ -34,7 +34,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -62,7 +61,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -87,7 +85,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm8550.c b/drivers/pinctrl/qcom/pinctrl-sm8550.c index 10a62031fdfd..cc8fbf4d5e84 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8550.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8550.c @@ -35,7 +35,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -64,7 +63,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -89,7 +87,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm8650.c b/drivers/pinctrl/qcom/pinctrl-sm8650.c index e2ae03800206..ab41292e3b4e 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8650.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8650.c @@ -36,7 +36,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -67,7 +66,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -92,7 +90,6 @@ .io_reg = io, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-sm8750.c b/drivers/pinctrl/qcom/pinctrl-sm8750.c index 6f92f176edd4..4cfe73f30fac 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8750.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8750.c @@ -35,7 +35,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -65,7 +64,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -90,7 +88,6 @@ .io_reg = io, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/qcom/pinctrl-x1e80100.c b/drivers/pinctrl/qcom/pinctrl-x1e80100.c index bb36f40b19fa..a9fe75fc45e5 100644 --- a/drivers/pinctrl/qcom/pinctrl-x1e80100.c +++ b/drivers/pinctrl/qcom/pinctrl-x1e80100.c @@ -33,7 +33,6 @@ .io_reg = 0x4 + REG_SIZE * id, \ .intr_cfg_reg = 0x8 + REG_SIZE * id, \ .intr_status_reg = 0xc + REG_SIZE * id, \ - .intr_target_reg = 0x8 + REG_SIZE * id, \ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -62,7 +61,6 @@ .io_reg = 0, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = pull, \ .drv_bit = drv, \ @@ -87,7 +85,6 @@ .io_reg = offset + 0x4, \ .intr_cfg_reg = 0, \ .intr_status_reg = 0, \ - .intr_target_reg = 0, \ .mux_bit = -1, \ .pull_bit = 3, \ .drv_bit = 0, \ diff --git a/drivers/pinctrl/realtek/Kconfig b/drivers/pinctrl/realtek/Kconfig index 400c9e5b16ad..054e85db99e7 100644 --- a/drivers/pinctrl/realtek/Kconfig +++ b/drivers/pinctrl/realtek/Kconfig @@ -22,3 +22,17 @@ config PINCTRL_RTD1315E tristate "Realtek DHC 1315E pin controller driver" depends on PINCTRL_RTD default y + +config PINCTRL_RTD1625 + tristate "Realtek DHC 1625 pin controller driver" + depends on PINCTRL_RTD + default y + help + This driver enables support for the pin controller on the Realtek + RTD1625 SoCs. + + It implements pin multiplexing for function selection and GPIO enabling. + It also utilizes the generic pin configuration interface to manage + electrical properties for both individual pins and pin groups. + + Say Y here to enable the pinctrl driver for RTD1625 SoCs \ No newline at end of file diff --git a/drivers/pinctrl/realtek/Makefile b/drivers/pinctrl/realtek/Makefile index c7bace0001e9..59562543396c 100644 --- a/drivers/pinctrl/realtek/Makefile +++ b/drivers/pinctrl/realtek/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_PINCTRL_RTD) += pinctrl-rtd.o obj-$(CONFIG_PINCTRL_RTD1619B) += pinctrl-rtd1619b.o obj-$(CONFIG_PINCTRL_RTD1319D) += pinctrl-rtd1319d.o obj-$(CONFIG_PINCTRL_RTD1315E) += pinctrl-rtd1315e.o +obj-$(CONFIG_PINCTRL_RTD1625) += pinctrl-rtd1625.o \ No newline at end of file diff --git a/drivers/pinctrl/realtek/pinctrl-rtd.c b/drivers/pinctrl/realtek/pinctrl-rtd.c index 244060486332..a2c672508a4b 100644 --- a/drivers/pinctrl/realtek/pinctrl-rtd.c +++ b/drivers/pinctrl/realtek/pinctrl-rtd.c @@ -30,17 +30,20 @@ struct rtd_pinctrl { struct pinctrl_desc desc; const struct rtd_pinctrl_desc *info; struct regmap *regmap_pinctrl; + u32 **saved_regs; }; /* custom pinconf parameters */ #define RTD_DRIVE_STRENGH_P (PIN_CONFIG_END + 1) #define RTD_DRIVE_STRENGH_N (PIN_CONFIG_END + 2) #define RTD_DUTY_CYCLE (PIN_CONFIG_END + 3) +#define RTD_HIGH_VIL (PIN_CONFIG_END + 4) static const struct pinconf_generic_params rtd_custom_bindings[] = { {"realtek,drive-strength-p", RTD_DRIVE_STRENGH_P, 0}, {"realtek,drive-strength-n", RTD_DRIVE_STRENGH_N, 0}, {"realtek,duty-cycle", RTD_DUTY_CYCLE, 0}, + {"realtek,high-vil-microvolt", RTD_HIGH_VIL, 0}, }; static int rtd_pinctrl_get_groups_count(struct pinctrl_dev *pcdev) @@ -279,7 +282,7 @@ static const struct rtd_pin_sconfig_desc *rtd_pinctrl_find_sconfig(struct rtd_pi static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, unsigned int pinnr, enum pin_config_param param, - enum pin_config_param arg) + unsigned int arg) { const struct rtd_pin_config_desc *config_desc; const struct rtd_pin_sconfig_desc *sconfig_desc; @@ -287,20 +290,21 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, u16 strength; u32 val; u32 mask; - u32 pulsel_off, pulen_off, smt_off, curr_off, pow_off, reg_off, p_off, n_off; + u32 pulsel_off, pulen_off, smt_off, curr_off, pow_off, reg_off, p_off, n_off, + input_volt_off, sr_off, hvil_off; const char *name = data->info->pins[pinnr].name; int ret = 0; config_desc = rtd_pinctrl_find_config(data, pinnr); if (!config_desc) { - dev_err(data->dev, "Not support pin config for pin: %s\n", name); + dev_err(data->dev, "Pin config unsupported for pin: %s\n", name); return -ENOTSUPP; } switch ((u32)param) { case PIN_CONFIG_INPUT_SCHMITT: case PIN_CONFIG_INPUT_SCHMITT_ENABLE: if (config_desc->smt_offset == NA) { - dev_err(data->dev, "Not support input schmitt for pin: %s\n", name); + dev_err(data->dev, "Input schmitt unsupported for pin: %s\n", name); return -ENOTSUPP; } smt_off = config_desc->base_bit + config_desc->smt_offset; @@ -313,7 +317,7 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, case PIN_CONFIG_DRIVE_PUSH_PULL: if (config_desc->pud_en_offset == NA) { - dev_err(data->dev, "Not support push pull for pin: %s\n", name); + dev_err(data->dev, "Push pull unsupported for pin: %s\n", name); return -ENOTSUPP; } pulen_off = config_desc->base_bit + config_desc->pud_en_offset; @@ -325,7 +329,7 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, case PIN_CONFIG_BIAS_DISABLE: if (config_desc->pud_en_offset == NA) { - dev_err(data->dev, "Not support bias disable for pin: %s\n", name); + dev_err(data->dev, "Bias disable unsupported for pin: %s\n", name); return -ENOTSUPP; } pulen_off = config_desc->base_bit + config_desc->pud_en_offset; @@ -337,7 +341,7 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, case PIN_CONFIG_BIAS_PULL_UP: if (config_desc->pud_en_offset == NA) { - dev_err(data->dev, "Not support bias pull up for pin:%s\n", name); + dev_err(data->dev, "Bias pull up unsupported for pin:%s\n", name); return -ENOTSUPP; } pulen_off = config_desc->base_bit + config_desc->pud_en_offset; @@ -350,7 +354,7 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, case PIN_CONFIG_BIAS_PULL_DOWN: if (config_desc->pud_en_offset == NA) { - dev_err(data->dev, "Not support bias pull down for pin: %s\n", name); + dev_err(data->dev, "Bias pull down unsupported for pin: %s\n", name); return -ENOTSUPP; } pulen_off = config_desc->base_bit + config_desc->pud_en_offset; @@ -384,7 +388,7 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, return -EINVAL; break; case NA: - dev_err(data->dev, "Not support drive strength for pin: %s\n", name); + dev_err(data->dev, "Drive strength unsupported for pin: %s\n", name); return -ENOTSUPP; default: return -EINVAL; @@ -394,7 +398,7 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, case PIN_CONFIG_POWER_SOURCE: if (config_desc->power_offset == NA) { - dev_err(data->dev, "Not support power source for pin: %s\n", name); + dev_err(data->dev, "Power source unsupported for pin: %s\n", name); return -ENOTSUPP; } reg_off = config_desc->reg_offset; @@ -408,10 +412,71 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, val = set_val ? mask : 0; break; + case PIN_CONFIG_SLEW_RATE: + if (config_desc->slew_rate_offset == NA) { + dev_err(data->dev, "Slew rate setting unsupported for pin: %s\n", name); + return -ENOTSUPP; + } + + switch (arg) { + case 1: + set_val = 0; + break; + case 10: + set_val = 1; + break; + case 20: + set_val = 2; + break; + case 30: + set_val = 3; + break; + default: + return -EINVAL; + } + + sr_off = config_desc->base_bit + config_desc->slew_rate_offset; + reg_off = config_desc->reg_offset; + mask = 0x3 << sr_off; + val = arg << sr_off; + break; + + case PIN_CONFIG_INPUT_VOLTAGE_UV: + if (config_desc->input_volt_offset == NA) { + dev_err(data->dev, "Input voltage level setting unsupported for pin:%s\n", + name); + return -ENOTSUPP; + } + + if (arg == 3300000) + set_val = 1; + else if (arg == 1800000) + set_val = 0; + else + return -EINVAL; + + input_volt_off = config_desc->base_bit + config_desc->input_volt_offset; + reg_off = config_desc->reg_offset; + + mask = BIT(input_volt_off); + val = set_val ? BIT(input_volt_off) : 0; + break; + + case RTD_HIGH_VIL: + if (config_desc->hvil_offset == NA) { + dev_err(data->dev, "High vil setting unsupported for pin:%s\n", name); + return -ENOTSUPP; + } + hvil_off = config_desc->base_bit + config_desc->hvil_offset; + reg_off = config_desc->reg_offset; + mask = BIT(hvil_off); + val = 1; + break; + case RTD_DRIVE_STRENGH_P: sconfig_desc = rtd_pinctrl_find_sconfig(data, pinnr); if (!sconfig_desc) { - dev_err(data->dev, "Not support P driving for pin: %s\n", name); + dev_err(data->dev, "P driving unsupported for pin: %s\n", name); return -ENOTSUPP; } set_val = arg; @@ -428,7 +493,7 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, case RTD_DRIVE_STRENGH_N: sconfig_desc = rtd_pinctrl_find_sconfig(data, pinnr); if (!sconfig_desc) { - dev_err(data->dev, "Not support N driving for pin: %s\n", name); + dev_err(data->dev, "N driving unsupported for pin: %s\n", name); return -ENOTSUPP; } set_val = arg; @@ -445,7 +510,7 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, case RTD_DUTY_CYCLE: sconfig_desc = rtd_pinctrl_find_sconfig(data, pinnr); if (!sconfig_desc || sconfig_desc->dcycle_offset == NA) { - dev_err(data->dev, "Not support duty cycle for pin: %s\n", name); + dev_err(data->dev, "Duty cycle unsupported for pin: %s\n", name); return -ENOTSUPP; } set_val = arg; @@ -456,8 +521,8 @@ static int rtd_pconf_parse_conf(struct rtd_pinctrl *data, break; default: - dev_err(data->dev, "unsupported pinconf: %d\n", (u32)param); - return -EINVAL; + dev_dbg(data->dev, "unsupported pinconf: %d\n", (u32)param); + return -ENOTSUPP; } ret = regmap_update_bits(data->regmap_pinctrl, reg_off, mask, val); @@ -540,18 +605,42 @@ static const struct regmap_config rtd_pinctrl_regmap_config = { .use_relaxed_mmio = true, }; +static int rtd_pinctrl_init_pm(struct rtd_pinctrl *data) +{ + const struct rtd_pin_range *pin_range = data->info->pin_range; + struct device *dev = data->pcdev->dev; + const struct rtd_reg_range *range; + size_t num_entries; + int i; + + data->saved_regs = devm_kcalloc(dev, pin_range->num_ranges, sizeof(u32 *), GFP_KERNEL); + if (!data->saved_regs) + return -ENOMEM; + + for (i = 0; i < pin_range->num_ranges; i++) { + range = &pin_range->ranges[i]; + num_entries = range->len / 4; + + data->saved_regs[i] = devm_kzalloc(dev, num_entries * sizeof(u32), GFP_KERNEL); + if (!data->saved_regs[i]) + return -ENOMEM; + } + + return 0; +} + int rtd_pinctrl_probe(struct platform_device *pdev, const struct rtd_pinctrl_desc *desc) { struct rtd_pinctrl *data; - int ret; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->base = of_iomap(pdev->dev.of_node, 0); - if (!data->base) - return -ENOMEM; + data->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->base), + "Failed to ioremap resource\n"); data->dev = &pdev->dev; data->info = desc; @@ -567,30 +656,95 @@ int rtd_pinctrl_probe(struct platform_device *pdev, const struct rtd_pinctrl_des data->regmap_pinctrl = devm_regmap_init_mmio(data->dev, data->base, &rtd_pinctrl_regmap_config); - if (IS_ERR(data->regmap_pinctrl)) { - dev_err(data->dev, "failed to init regmap: %ld\n", - PTR_ERR(data->regmap_pinctrl)); - ret = PTR_ERR(data->regmap_pinctrl); - goto unmap; - } + if (IS_ERR(data->regmap_pinctrl)) + return dev_err_probe(data->dev, PTR_ERR(data->regmap_pinctrl), + "Failed to init regmap\n"); - data->pcdev = pinctrl_register(&data->desc, &pdev->dev, data); - if (IS_ERR(data->pcdev)) { - ret = PTR_ERR(data->pcdev); - goto unmap; - } + data->pcdev = devm_pinctrl_register(&pdev->dev, &data->desc, data); + if (IS_ERR(data->pcdev)) + return dev_err_probe(data->dev, PTR_ERR(data->pcdev), + "Failed to register pinctrl\n"); platform_set_drvdata(pdev, data); dev_dbg(&pdev->dev, "probed\n"); - return 0; + if (data->info->pin_range) { + if (rtd_pinctrl_init_pm(data)) + return -ENOMEM; + } -unmap: - iounmap(data->base); - return ret; + return 0; } EXPORT_SYMBOL(rtd_pinctrl_probe); +static int realtek_pinctrl_suspend(struct device *dev) +{ + struct rtd_pinctrl *data = dev_get_drvdata(dev); + const struct rtd_pin_range *pin_range = data->info->pin_range; + const struct rtd_reg_range *range; + u32 *range_regs; + int count; + int i, j; + int ret; + + if (!data->saved_regs) + return 0; + + for (i = 0; i < pin_range->num_ranges; i++) { + range = &pin_range->ranges[i]; + range_regs = data->saved_regs[i]; + count = range->len / 4; + + for (j = 0; j < count; j++) { + ret = regmap_read(data->regmap_pinctrl, range->offset + (j * 4), + &range_regs[j]); + if (ret) { + dev_err(dev, "failed to store register 0x%x: %d\n", + range->offset + (j * 4), ret); + return ret; + } + } + } + + return 0; +} + +static int realtek_pinctrl_resume(struct device *dev) +{ + struct rtd_pinctrl *data = dev_get_drvdata(dev); + const struct rtd_pin_range *pin_range = data->info->pin_range; + const struct rtd_reg_range *range; + u32 *range_regs; + int count; + int i, j; + int ret; + + if (!data->saved_regs) + return 0; + + for (i = 0; i < pin_range->num_ranges; i++) { + range = &pin_range->ranges[i]; + range_regs = data->saved_regs[i]; + count = range->len / 4; + + for (j = 0; j < count; j++) { + ret = regmap_write(data->regmap_pinctrl, range->offset + (j * 4), + range_regs[j]); + if (ret) { + dev_err(dev, "failed to restore register 0x%x: %d\n", + range->offset + (j * 4), ret); + return ret; + } + } + } + return 0; +} + +const struct dev_pm_ops realtek_pinctrl_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(realtek_pinctrl_suspend, realtek_pinctrl_resume) +}; +EXPORT_SYMBOL_GPL(realtek_pinctrl_pm_ops); + MODULE_DESCRIPTION("Realtek DHC SoC pinctrl driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/realtek/pinctrl-rtd.h b/drivers/pinctrl/realtek/pinctrl-rtd.h index e15130896abc..77c30ea76443 100644 --- a/drivers/pinctrl/realtek/pinctrl-rtd.h +++ b/drivers/pinctrl/realtek/pinctrl-rtd.h @@ -34,6 +34,9 @@ struct rtd_pin_config_desc { unsigned int smt_offset; unsigned int power_offset; unsigned int curr_type; + unsigned int input_volt_offset; + unsigned int slew_rate_offset; + unsigned int hvil_offset; }; struct rtd_pin_sconfig_desc { @@ -47,6 +50,16 @@ struct rtd_pin_sconfig_desc { unsigned int pdrive_maskbits; }; +struct rtd_reg_range { + unsigned int offset; + size_t len; +}; + +struct rtd_pin_range { + const struct rtd_reg_range *ranges; + const int num_ranges; +}; + struct rtd_pin_desc { const char *name; unsigned int mux_offset; @@ -85,6 +98,40 @@ struct rtd_pin_reg_list { .curr_type = _curr_type, \ } +#define RTK_PIN_CONFIG_V2(_name, _reg_off, _base_bit, _pud_en_off, \ + _pud_sel_off, _curr_off, _smt_off, _pow_off, _input_volt_off, \ + _curr_type) \ + { \ + .name = # _name, \ + .reg_offset = _reg_off, \ + .base_bit = _base_bit, \ + .pud_en_offset = _pud_en_off, \ + .pud_sel_offset = _pud_sel_off, \ + .curr_offset = _curr_off, \ + .smt_offset = _smt_off, \ + .power_offset = _pow_off, \ + .input_volt_offset = _input_volt_off, \ + .curr_type = _curr_type, \ + } + +#define RTK_PIN_CONFIG_I2C(_name, _reg_off, _base_bit, _pud_en_off, \ + _pud_sel_off, _curr_off, _smt_off, _hvil_off, _sr_off, _pow_off, \ + _input_volt_off, _curr_type) \ + { \ + .name = # _name, \ + .reg_offset = _reg_off, \ + .base_bit = _base_bit, \ + .pud_en_offset = _pud_en_off, \ + .pud_sel_offset = _pud_sel_off, \ + .curr_offset = _curr_off, \ + .smt_offset = _smt_off, \ + .hvil_offset = _hvil_off, \ + .slew_rate_offset = _sr_off, \ + .power_offset = _pow_off, \ + .input_volt_offset = _input_volt_off, \ + .curr_type = _curr_type, \ + } + #define RTK_PIN_SCONFIG(_name, _reg_off, _d_offset, _d_mask, \ _n_offset, _n_mask, _p_offset, _p_mask) \ { \ @@ -119,6 +166,9 @@ struct rtd_pinctrl_desc { unsigned int num_sconfigs; struct rtd_pin_reg_list *lists; unsigned int num_regs; + const struct rtd_pin_range *pin_range; }; int rtd_pinctrl_probe(struct platform_device *pdev, const struct rtd_pinctrl_desc *desc); + +extern const struct dev_pm_ops realtek_pinctrl_pm_ops; diff --git a/drivers/pinctrl/realtek/pinctrl-rtd1625.c b/drivers/pinctrl/realtek/pinctrl-rtd1625.c new file mode 100644 index 000000000000..2d623bdbf9ca --- /dev/null +++ b/drivers/pinctrl/realtek/pinctrl-rtd1625.c @@ -0,0 +1,3138 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Realtek DHC 1625 pin controller driver + * + * Copyright (c) 2023 Realtek Semiconductor Corp. + * + */ + +#include +#include +#include +#include + +#include "pinctrl-rtd.h" + +enum rtd1625_iso_pins_enum { + RTD1625_ISO_GPIO_8 = 0, + RTD1625_ISO_GPIO_9, + RTD1625_ISO_GPIO_10, + RTD1625_ISO_GPIO_11, + RTD1625_ISO_USB_CC1, + RTD1625_ISO_USB_CC2, + RTD1625_ISO_GPIO_45, + RTD1625_ISO_GPIO_46, + RTD1625_ISO_GPIO_47, + RTD1625_ISO_GPIO_48, + RTD1625_ISO_GPIO_49, + RTD1625_ISO_GPIO_50, + RTD1625_ISO_GPIO_52, + RTD1625_ISO_GPIO_94, + RTD1625_ISO_GPIO_95, + RTD1625_ISO_GPIO_96, + RTD1625_ISO_GPIO_97, + RTD1625_ISO_GPIO_98, + RTD1625_ISO_GPIO_99, + RTD1625_ISO_GPIO_100, + RTD1625_ISO_GPIO_101, + RTD1625_ISO_GPIO_102, + RTD1625_ISO_GPIO_103, + RTD1625_ISO_GPIO_104, + RTD1625_ISO_GPIO_105, + RTD1625_ISO_GPIO_106, + RTD1625_ISO_GPIO_107, + RTD1625_ISO_GPIO_108, + RTD1625_ISO_GPIO_109, + RTD1625_ISO_GPIO_110, + RTD1625_ISO_GPIO_111, + RTD1625_ISO_GPIO_112, + RTD1625_ISO_GPIO_128, + RTD1625_ISO_GPIO_129, + RTD1625_ISO_GPIO_130, + RTD1625_ISO_GPIO_131, + RTD1625_ISO_GPIO_145, + RTD1625_ISO_GPIO_146, + RTD1625_ISO_GPIO_147, + RTD1625_ISO_GPIO_148, + RTD1625_ISO_GPIO_149, + RTD1625_ISO_GPIO_150, + RTD1625_ISO_GPIO_151, + RTD1625_ISO_GPIO_152, + RTD1625_ISO_GPIO_153, + RTD1625_ISO_GPIO_154, + RTD1625_ISO_GPIO_155, + RTD1625_ISO_GPIO_156, + RTD1625_ISO_GPIO_157, + RTD1625_ISO_GPIO_158, + RTD1625_ISO_GPIO_159, + RTD1625_ISO_GPIO_160, + RTD1625_ISO_GPIO_161, + RTD1625_ISO_GPIO_162, + RTD1625_ISO_GPIO_163, + RTD1625_ISO_HI_WIDTH, + RTD1625_ISO_SF_EN, + RTD1625_ISO_ARM_TRACE_DBG_EN, + RTD1625_ISO_EJTAG_AUCPU0_LOC, + RTD1625_ISO_EJTAG_AUCPU1_LOC, + RTD1625_ISO_EJTAG_VE2_LOC, + RTD1625_ISO_EJTAG_SCPU_LOC, + RTD1625_ISO_EJTAG_PCPU_LOC, + RTD1625_ISO_EJTAG_ACPU_LOC, + RTD1625_ISO_I2C6_LOC, + RTD1625_ISO_UART0_LOC, + RTD1625_ISO_AI_I2S1_LOC, + RTD1625_ISO_AO_I2S1_LOC, + RTD1625_ISO_ETN_PHY_LOC, + RTD1625_ISO_SPDIF_LOC, + RTD1625_ISO_RGMII_VDSEL, + RTD1625_ISO_CSI_VDSEL, + RTD1625_ISO_SPDIF_IN_MODE, +}; + +static const struct pinctrl_pin_desc rtd1625_iso_pins[] = { + PINCTRL_PIN(RTD1625_ISO_GPIO_8, "gpio_8"), + PINCTRL_PIN(RTD1625_ISO_GPIO_9, "gpio_9"), + PINCTRL_PIN(RTD1625_ISO_GPIO_10, "gpio_10"), + PINCTRL_PIN(RTD1625_ISO_GPIO_11, "gpio_11"), + PINCTRL_PIN(RTD1625_ISO_USB_CC1, "usb_cc1"), + PINCTRL_PIN(RTD1625_ISO_USB_CC2, "usb_cc2"), + PINCTRL_PIN(RTD1625_ISO_GPIO_45, "gpio_45"), + PINCTRL_PIN(RTD1625_ISO_GPIO_46, "gpio_46"), + PINCTRL_PIN(RTD1625_ISO_GPIO_47, "gpio_47"), + PINCTRL_PIN(RTD1625_ISO_GPIO_48, "gpio_48"), + PINCTRL_PIN(RTD1625_ISO_GPIO_49, "gpio_49"), + PINCTRL_PIN(RTD1625_ISO_GPIO_50, "gpio_50"), + PINCTRL_PIN(RTD1625_ISO_GPIO_52, "gpio_52"), + PINCTRL_PIN(RTD1625_ISO_GPIO_94, "gpio_94"), + PINCTRL_PIN(RTD1625_ISO_GPIO_95, "gpio_95"), + PINCTRL_PIN(RTD1625_ISO_GPIO_96, "gpio_96"), + PINCTRL_PIN(RTD1625_ISO_GPIO_97, "gpio_97"), + PINCTRL_PIN(RTD1625_ISO_GPIO_98, "gpio_98"), + PINCTRL_PIN(RTD1625_ISO_GPIO_99, "gpio_99"), + PINCTRL_PIN(RTD1625_ISO_GPIO_100, "gpio_100"), + PINCTRL_PIN(RTD1625_ISO_GPIO_101, "gpio_101"), + PINCTRL_PIN(RTD1625_ISO_GPIO_102, "gpio_102"), + PINCTRL_PIN(RTD1625_ISO_GPIO_103, "gpio_103"), + PINCTRL_PIN(RTD1625_ISO_GPIO_104, "gpio_104"), + PINCTRL_PIN(RTD1625_ISO_GPIO_105, "gpio_105"), + PINCTRL_PIN(RTD1625_ISO_GPIO_106, "gpio_106"), + PINCTRL_PIN(RTD1625_ISO_GPIO_107, "gpio_107"), + PINCTRL_PIN(RTD1625_ISO_GPIO_108, "gpio_108"), + PINCTRL_PIN(RTD1625_ISO_GPIO_109, "gpio_109"), + PINCTRL_PIN(RTD1625_ISO_GPIO_110, "gpio_110"), + PINCTRL_PIN(RTD1625_ISO_GPIO_111, "gpio_111"), + PINCTRL_PIN(RTD1625_ISO_GPIO_112, "gpio_112"), + PINCTRL_PIN(RTD1625_ISO_GPIO_128, "gpio_128"), + PINCTRL_PIN(RTD1625_ISO_GPIO_129, "gpio_129"), + PINCTRL_PIN(RTD1625_ISO_GPIO_130, "gpio_130"), + PINCTRL_PIN(RTD1625_ISO_GPIO_131, "gpio_131"), + PINCTRL_PIN(RTD1625_ISO_GPIO_145, "gpio_145"), + PINCTRL_PIN(RTD1625_ISO_GPIO_146, "gpio_146"), + PINCTRL_PIN(RTD1625_ISO_GPIO_147, "gpio_147"), + PINCTRL_PIN(RTD1625_ISO_GPIO_148, "gpio_148"), + PINCTRL_PIN(RTD1625_ISO_GPIO_149, "gpio_149"), + PINCTRL_PIN(RTD1625_ISO_GPIO_150, "gpio_150"), + PINCTRL_PIN(RTD1625_ISO_GPIO_151, "gpio_151"), + PINCTRL_PIN(RTD1625_ISO_GPIO_152, "gpio_152"), + PINCTRL_PIN(RTD1625_ISO_GPIO_153, "gpio_153"), + PINCTRL_PIN(RTD1625_ISO_GPIO_154, "gpio_154"), + PINCTRL_PIN(RTD1625_ISO_GPIO_155, "gpio_155"), + PINCTRL_PIN(RTD1625_ISO_GPIO_156, "gpio_156"), + PINCTRL_PIN(RTD1625_ISO_GPIO_157, "gpio_157"), + PINCTRL_PIN(RTD1625_ISO_GPIO_158, "gpio_158"), + PINCTRL_PIN(RTD1625_ISO_GPIO_159, "gpio_159"), + PINCTRL_PIN(RTD1625_ISO_GPIO_160, "gpio_160"), + PINCTRL_PIN(RTD1625_ISO_GPIO_161, "gpio_161"), + PINCTRL_PIN(RTD1625_ISO_GPIO_162, "gpio_162"), + PINCTRL_PIN(RTD1625_ISO_GPIO_163, "gpio_163"), + PINCTRL_PIN(RTD1625_ISO_HI_WIDTH, "hi_width"), + PINCTRL_PIN(RTD1625_ISO_SF_EN, "sf_en"), + PINCTRL_PIN(RTD1625_ISO_ARM_TRACE_DBG_EN, "arm_trace_dbg_en"), + PINCTRL_PIN(RTD1625_ISO_EJTAG_AUCPU0_LOC, "ejtag_aucpu0_loc"), + PINCTRL_PIN(RTD1625_ISO_EJTAG_AUCPU1_LOC, "ejtag_aucpu1_loc"), + PINCTRL_PIN(RTD1625_ISO_EJTAG_VE2_LOC, "ejtag_ve2_loc"), + PINCTRL_PIN(RTD1625_ISO_EJTAG_SCPU_LOC, "ejtag_scpu_loc"), + PINCTRL_PIN(RTD1625_ISO_EJTAG_PCPU_LOC, "ejtag_pcpu_loc"), + PINCTRL_PIN(RTD1625_ISO_EJTAG_ACPU_LOC, "ejtag_acpu_loc"), + PINCTRL_PIN(RTD1625_ISO_I2C6_LOC, "i2c6_loc"), + PINCTRL_PIN(RTD1625_ISO_UART0_LOC, "uart0_loc"), + PINCTRL_PIN(RTD1625_ISO_AI_I2S1_LOC, "ai_i2s1_loc"), + PINCTRL_PIN(RTD1625_ISO_AO_I2S1_LOC, "ao_i2s1_loc"), + PINCTRL_PIN(RTD1625_ISO_ETN_PHY_LOC, "etn_phy_loc"), + PINCTRL_PIN(RTD1625_ISO_SPDIF_LOC, "spdif_loc"), + PINCTRL_PIN(RTD1625_ISO_RGMII_VDSEL, "rgmii_vdsel"), + PINCTRL_PIN(RTD1625_ISO_CSI_VDSEL, "csi_vdsel"), + PINCTRL_PIN(RTD1625_ISO_SPDIF_IN_MODE, "spdif_in_mode"), +}; + +enum rtd1625_isom_pins_enum { + RTD1625_ISOM_GPIO_0 = 0, + RTD1625_ISOM_GPIO_1, + RTD1625_ISOM_GPIO_28, + RTD1625_ISOM_GPIO_29, + RTD1625_ISOM_IR_RX_LOC, +}; + +static const struct pinctrl_pin_desc rtd1625_isom_pins[] = { + PINCTRL_PIN(RTD1625_ISOM_GPIO_0, "gpio_0"), + PINCTRL_PIN(RTD1625_ISOM_GPIO_1, "gpio_1"), + PINCTRL_PIN(RTD1625_ISOM_GPIO_28, "gpio_28"), + PINCTRL_PIN(RTD1625_ISOM_GPIO_29, "gpio_29"), + PINCTRL_PIN(RTD1625_ISOM_IR_RX_LOC, "ir_rx_loc"), +}; + +enum rtd1625_ve4_pins_enum { + RTD1625_VE4_GPIO_2 = 0, + RTD1625_VE4_GPIO_3, + RTD1625_VE4_GPIO_4, + RTD1625_VE4_GPIO_5, + RTD1625_VE4_GPIO_6, + RTD1625_VE4_GPIO_7, + RTD1625_VE4_GPIO_12, + RTD1625_VE4_GPIO_13, + RTD1625_VE4_GPIO_16, + RTD1625_VE4_GPIO_17, + RTD1625_VE4_GPIO_18, + RTD1625_VE4_GPIO_19, + RTD1625_VE4_GPIO_23, + RTD1625_VE4_GPIO_24, + RTD1625_VE4_GPIO_25, + RTD1625_VE4_GPIO_30, + RTD1625_VE4_GPIO_31, + RTD1625_VE4_GPIO_32, + RTD1625_VE4_GPIO_33, + RTD1625_VE4_GPIO_34, + RTD1625_VE4_GPIO_35, + RTD1625_VE4_GPIO_42, + RTD1625_VE4_GPIO_43, + RTD1625_VE4_GPIO_44, + RTD1625_VE4_GPIO_51, + RTD1625_VE4_GPIO_53, + RTD1625_VE4_GPIO_54, + RTD1625_VE4_GPIO_55, + RTD1625_VE4_GPIO_56, + RTD1625_VE4_GPIO_57, + RTD1625_VE4_GPIO_58, + RTD1625_VE4_GPIO_59, + RTD1625_VE4_GPIO_60, + RTD1625_VE4_GPIO_61, + RTD1625_VE4_GPIO_62, + RTD1625_VE4_GPIO_63, + RTD1625_VE4_GPIO_92, + RTD1625_VE4_GPIO_93, + RTD1625_VE4_GPIO_132, + RTD1625_VE4_GPIO_133, + RTD1625_VE4_GPIO_134, + RTD1625_VE4_GPIO_135, + RTD1625_VE4_GPIO_136, + RTD1625_VE4_GPIO_137, + RTD1625_VE4_GPIO_138, + RTD1625_VE4_GPIO_139, + RTD1625_VE4_GPIO_140, + RTD1625_VE4_GPIO_141, + RTD1625_VE4_GPIO_142, + RTD1625_VE4_GPIO_143, + RTD1625_VE4_GPIO_144, + RTD1625_VE4_GPIO_164, + RTD1625_VE4_GPIO_165, + RTD1625_VE4_UART_LOC, +}; + +static const struct pinctrl_pin_desc rtd1625_ve4_pins[] = { + PINCTRL_PIN(RTD1625_VE4_GPIO_2, "gpio_2"), + PINCTRL_PIN(RTD1625_VE4_GPIO_3, "gpio_3"), + PINCTRL_PIN(RTD1625_VE4_GPIO_4, "gpio_4"), + PINCTRL_PIN(RTD1625_VE4_GPIO_5, "gpio_5"), + PINCTRL_PIN(RTD1625_VE4_GPIO_6, "gpio_6"), + PINCTRL_PIN(RTD1625_VE4_GPIO_7, "gpio_7"), + PINCTRL_PIN(RTD1625_VE4_GPIO_12, "gpio_12"), + PINCTRL_PIN(RTD1625_VE4_GPIO_13, "gpio_13"), + PINCTRL_PIN(RTD1625_VE4_GPIO_16, "gpio_16"), + PINCTRL_PIN(RTD1625_VE4_GPIO_17, "gpio_17"), + PINCTRL_PIN(RTD1625_VE4_GPIO_18, "gpio_18"), + PINCTRL_PIN(RTD1625_VE4_GPIO_19, "gpio_19"), + PINCTRL_PIN(RTD1625_VE4_GPIO_23, "gpio_23"), + PINCTRL_PIN(RTD1625_VE4_GPIO_24, "gpio_24"), + PINCTRL_PIN(RTD1625_VE4_GPIO_25, "gpio_25"), + PINCTRL_PIN(RTD1625_VE4_GPIO_30, "gpio_30"), + PINCTRL_PIN(RTD1625_VE4_GPIO_31, "gpio_31"), + PINCTRL_PIN(RTD1625_VE4_GPIO_32, "gpio_32"), + PINCTRL_PIN(RTD1625_VE4_GPIO_33, "gpio_33"), + PINCTRL_PIN(RTD1625_VE4_GPIO_34, "gpio_34"), + PINCTRL_PIN(RTD1625_VE4_GPIO_35, "gpio_35"), + PINCTRL_PIN(RTD1625_VE4_GPIO_42, "gpio_42"), + PINCTRL_PIN(RTD1625_VE4_GPIO_43, "gpio_43"), + PINCTRL_PIN(RTD1625_VE4_GPIO_44, "gpio_44"), + PINCTRL_PIN(RTD1625_VE4_GPIO_51, "gpio_51"), + PINCTRL_PIN(RTD1625_VE4_GPIO_53, "gpio_53"), + PINCTRL_PIN(RTD1625_VE4_GPIO_54, "gpio_54"), + PINCTRL_PIN(RTD1625_VE4_GPIO_55, "gpio_55"), + PINCTRL_PIN(RTD1625_VE4_GPIO_56, "gpio_56"), + PINCTRL_PIN(RTD1625_VE4_GPIO_57, "gpio_57"), + PINCTRL_PIN(RTD1625_VE4_GPIO_58, "gpio_58"), + PINCTRL_PIN(RTD1625_VE4_GPIO_59, "gpio_59"), + PINCTRL_PIN(RTD1625_VE4_GPIO_60, "gpio_60"), + PINCTRL_PIN(RTD1625_VE4_GPIO_61, "gpio_61"), + PINCTRL_PIN(RTD1625_VE4_GPIO_62, "gpio_62"), + PINCTRL_PIN(RTD1625_VE4_GPIO_63, "gpio_63"), + PINCTRL_PIN(RTD1625_VE4_GPIO_92, "gpio_92"), + PINCTRL_PIN(RTD1625_VE4_GPIO_93, "gpio_93"), + PINCTRL_PIN(RTD1625_VE4_GPIO_132, "gpio_132"), + PINCTRL_PIN(RTD1625_VE4_GPIO_133, "gpio_133"), + PINCTRL_PIN(RTD1625_VE4_GPIO_134, "gpio_134"), + PINCTRL_PIN(RTD1625_VE4_GPIO_135, "gpio_135"), + PINCTRL_PIN(RTD1625_VE4_GPIO_136, "gpio_136"), + PINCTRL_PIN(RTD1625_VE4_GPIO_137, "gpio_137"), + PINCTRL_PIN(RTD1625_VE4_GPIO_138, "gpio_138"), + PINCTRL_PIN(RTD1625_VE4_GPIO_139, "gpio_139"), + PINCTRL_PIN(RTD1625_VE4_GPIO_140, "gpio_140"), + PINCTRL_PIN(RTD1625_VE4_GPIO_141, "gpio_141"), + PINCTRL_PIN(RTD1625_VE4_GPIO_142, "gpio_142"), + PINCTRL_PIN(RTD1625_VE4_GPIO_143, "gpio_143"), + PINCTRL_PIN(RTD1625_VE4_GPIO_144, "gpio_144"), + PINCTRL_PIN(RTD1625_VE4_GPIO_164, "gpio_164"), + PINCTRL_PIN(RTD1625_VE4_GPIO_165, "gpio_165"), + PINCTRL_PIN(RTD1625_VE4_UART_LOC, "ve4_uart_loc"), +}; + +enum rtd1625_main2_pins_enum { + RTD1625_MAIN2_GPIO_14 = 0, + RTD1625_MAIN2_GPIO_15, + RTD1625_MAIN2_GPIO_20, + RTD1625_MAIN2_GPIO_21, + RTD1625_MAIN2_GPIO_22, + RTD1625_MAIN2_HIF_DATA, + RTD1625_MAIN2_HIF_EN, + RTD1625_MAIN2_HIF_RDY, + RTD1625_MAIN2_HIF_CLK, + RTD1625_MAIN2_GPIO_40, + RTD1625_MAIN2_GPIO_41, + RTD1625_MAIN2_GPIO_64, + RTD1625_MAIN2_GPIO_65, + RTD1625_MAIN2_GPIO_66, + RTD1625_MAIN2_GPIO_67, + RTD1625_MAIN2_EMMC_DATA_0, + RTD1625_MAIN2_EMMC_DATA_1, + RTD1625_MAIN2_EMMC_DATA_2, + RTD1625_MAIN2_EMMC_DATA_3, + RTD1625_MAIN2_EMMC_DATA_4, + RTD1625_MAIN2_EMMC_DATA_5, + RTD1625_MAIN2_EMMC_DATA_6, + RTD1625_MAIN2_EMMC_DATA_7, + RTD1625_MAIN2_EMMC_RST_N, + RTD1625_MAIN2_EMMC_CMD, + RTD1625_MAIN2_EMMC_CLK, + RTD1625_MAIN2_EMMC_DD_SB, + RTD1625_MAIN2_GPIO_80, + RTD1625_MAIN2_GPIO_81, + RTD1625_MAIN2_GPIO_82, + RTD1625_MAIN2_GPIO_83, + RTD1625_MAIN2_GPIO_84, + RTD1625_MAIN2_GPIO_85, + RTD1625_MAIN2_GPIO_86, + RTD1625_MAIN2_GPIO_87, + RTD1625_MAIN2_GPIO_88, + RTD1625_MAIN2_GPIO_89, + RTD1625_MAIN2_GPIO_90, + RTD1625_MAIN2_GPIO_91, +}; + +static const struct pinctrl_pin_desc rtd1625_main2_pins[] = { + PINCTRL_PIN(RTD1625_MAIN2_GPIO_14, "gpio_14"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_15, "gpio_15"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_20, "gpio_20"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_21, "gpio_21"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_22, "gpio_22"), + PINCTRL_PIN(RTD1625_MAIN2_HIF_DATA, "hif_data"), + PINCTRL_PIN(RTD1625_MAIN2_HIF_EN, "hif_en"), + PINCTRL_PIN(RTD1625_MAIN2_HIF_RDY, "hif_rdy"), + PINCTRL_PIN(RTD1625_MAIN2_HIF_CLK, "hif_clk"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_40, "gpio_40"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_41, "gpio_41"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_64, "gpio_64"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_65, "gpio_65"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_66, "gpio_66"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_67, "gpio_67"), + PINCTRL_PIN(RTD1625_MAIN2_EMMC_DATA_0, "emmc_data_0"), + PINCTRL_PIN(RTD1625_MAIN2_EMMC_DATA_1, "emmc_data_1"), + PINCTRL_PIN(RTD1625_MAIN2_EMMC_DATA_2, "emmc_data_2"), + PINCTRL_PIN(RTD1625_MAIN2_EMMC_DATA_3, "emmc_data_3"), + PINCTRL_PIN(RTD1625_MAIN2_EMMC_DATA_4, "emmc_data_4"), + PINCTRL_PIN(RTD1625_MAIN2_EMMC_DATA_5, "emmc_data_5"), + PINCTRL_PIN(RTD1625_MAIN2_EMMC_DATA_6, "emmc_data_6"), + PINCTRL_PIN(RTD1625_MAIN2_EMMC_DATA_7, "emmc_data_7"), + PINCTRL_PIN(RTD1625_MAIN2_EMMC_RST_N, "emmc_rst_n"), + PINCTRL_PIN(RTD1625_MAIN2_EMMC_CMD, "emmc_cmd"), + PINCTRL_PIN(RTD1625_MAIN2_EMMC_CLK, "emmc_clk"), + PINCTRL_PIN(RTD1625_MAIN2_EMMC_DD_SB, "emmc_dd_sb"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_80, "gpio_80"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_81, "gpio_81"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_82, "gpio_82"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_83, "gpio_83"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_84, "gpio_84"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_85, "gpio_85"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_86, "gpio_86"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_87, "gpio_87"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_88, "gpio_88"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_89, "gpio_89"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_90, "gpio_90"), + PINCTRL_PIN(RTD1625_MAIN2_GPIO_91, "gpio_91"), +}; + +#define DECLARE_RTD1625_PIN(_pin, _name) \ + static const unsigned int rtd1625_##_name##_pins[] = { _pin } + +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_8, gpio_8); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_9, gpio_9); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_10, gpio_10); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_11, gpio_11); +DECLARE_RTD1625_PIN(RTD1625_ISO_USB_CC1, usb_cc1); +DECLARE_RTD1625_PIN(RTD1625_ISO_USB_CC2, usb_cc2); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_45, gpio_45); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_46, gpio_46); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_47, gpio_47); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_48, gpio_48); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_49, gpio_49); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_50, gpio_50); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_52, gpio_52); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_94, gpio_94); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_95, gpio_95); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_96, gpio_96); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_97, gpio_97); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_98, gpio_98); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_99, gpio_99); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_100, gpio_100); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_101, gpio_101); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_102, gpio_102); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_103, gpio_103); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_104, gpio_104); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_105, gpio_105); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_106, gpio_106); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_107, gpio_107); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_108, gpio_108); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_109, gpio_109); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_110, gpio_110); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_111, gpio_111); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_112, gpio_112); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_128, gpio_128); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_129, gpio_129); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_130, gpio_130); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_131, gpio_131); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_145, gpio_145); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_146, gpio_146); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_147, gpio_147); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_148, gpio_148); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_149, gpio_149); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_150, gpio_150); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_151, gpio_151); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_152, gpio_152); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_153, gpio_153); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_154, gpio_154); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_155, gpio_155); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_156, gpio_156); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_157, gpio_157); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_158, gpio_158); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_159, gpio_159); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_160, gpio_160); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_161, gpio_161); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_162, gpio_162); +DECLARE_RTD1625_PIN(RTD1625_ISO_GPIO_163, gpio_163); + +DECLARE_RTD1625_PIN(RTD1625_ISO_HI_WIDTH, hi_width); +DECLARE_RTD1625_PIN(RTD1625_ISO_SF_EN, sf_en); +DECLARE_RTD1625_PIN(RTD1625_ISO_ARM_TRACE_DBG_EN, arm_trace_dbg_en); +DECLARE_RTD1625_PIN(RTD1625_ISO_EJTAG_AUCPU0_LOC, ejtag_aucpu0_loc); +DECLARE_RTD1625_PIN(RTD1625_ISO_EJTAG_AUCPU1_LOC, ejtag_aucpu1_loc); +DECLARE_RTD1625_PIN(RTD1625_ISO_EJTAG_VE2_LOC, ejtag_ve2_loc); +DECLARE_RTD1625_PIN(RTD1625_ISO_EJTAG_SCPU_LOC, ejtag_scpu_loc); +DECLARE_RTD1625_PIN(RTD1625_ISO_EJTAG_PCPU_LOC, ejtag_pcpu_loc); +DECLARE_RTD1625_PIN(RTD1625_ISO_EJTAG_ACPU_LOC, ejtag_acpu_loc); + +DECLARE_RTD1625_PIN(RTD1625_ISO_I2C6_LOC, i2c6_loc); +DECLARE_RTD1625_PIN(RTD1625_ISO_UART0_LOC, uart0_loc); +DECLARE_RTD1625_PIN(RTD1625_ISO_AI_I2S1_LOC, ai_i2s1_loc); +DECLARE_RTD1625_PIN(RTD1625_ISO_AO_I2S1_LOC, ao_i2s1_loc); +DECLARE_RTD1625_PIN(RTD1625_ISO_ETN_PHY_LOC, etn_phy_loc); +DECLARE_RTD1625_PIN(RTD1625_ISO_SPDIF_LOC, spdif_loc); +DECLARE_RTD1625_PIN(RTD1625_ISO_RGMII_VDSEL, rgmii_vdsel); +DECLARE_RTD1625_PIN(RTD1625_ISO_CSI_VDSEL, csi_vdsel); +DECLARE_RTD1625_PIN(RTD1625_ISO_SPDIF_IN_MODE, spdif_in_mode); + +DECLARE_RTD1625_PIN(RTD1625_ISOM_GPIO_0, gpio_0); +DECLARE_RTD1625_PIN(RTD1625_ISOM_GPIO_1, gpio_1); +DECLARE_RTD1625_PIN(RTD1625_ISOM_GPIO_28, gpio_28); +DECLARE_RTD1625_PIN(RTD1625_ISOM_GPIO_29, gpio_29); +DECLARE_RTD1625_PIN(RTD1625_ISOM_IR_RX_LOC, ir_rx_loc); + +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_2, gpio_2); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_3, gpio_3); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_4, gpio_4); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_5, gpio_5); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_6, gpio_6); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_7, gpio_7); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_12, gpio_12); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_13, gpio_13); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_16, gpio_16); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_17, gpio_17); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_18, gpio_18); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_19, gpio_19); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_23, gpio_23); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_24, gpio_24); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_25, gpio_25); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_30, gpio_30); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_31, gpio_31); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_32, gpio_32); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_33, gpio_33); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_34, gpio_34); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_35, gpio_35); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_42, gpio_42); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_43, gpio_43); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_44, gpio_44); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_51, gpio_51); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_53, gpio_53); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_54, gpio_54); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_55, gpio_55); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_56, gpio_56); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_57, gpio_57); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_58, gpio_58); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_59, gpio_59); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_60, gpio_60); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_61, gpio_61); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_62, gpio_62); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_63, gpio_63); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_92, gpio_92); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_93, gpio_93); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_132, gpio_132); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_133, gpio_133); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_134, gpio_134); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_135, gpio_135); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_136, gpio_136); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_137, gpio_137); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_138, gpio_138); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_139, gpio_139); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_140, gpio_140); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_141, gpio_141); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_142, gpio_142); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_143, gpio_143); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_144, gpio_144); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_164, gpio_164); +DECLARE_RTD1625_PIN(RTD1625_VE4_GPIO_165, gpio_165); +DECLARE_RTD1625_PIN(RTD1625_VE4_UART_LOC, ve4_uart_loc); + +DECLARE_RTD1625_PIN(RTD1625_MAIN2_EMMC_RST_N, emmc_rst_n); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_EMMC_DD_SB, emmc_dd_sb); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_EMMC_CLK, emmc_clk); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_EMMC_CMD, emmc_cmd); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_EMMC_DATA_0, emmc_data_0); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_EMMC_DATA_1, emmc_data_1); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_EMMC_DATA_2, emmc_data_2); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_EMMC_DATA_3, emmc_data_3); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_EMMC_DATA_4, emmc_data_4); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_EMMC_DATA_5, emmc_data_5); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_EMMC_DATA_6, emmc_data_6); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_EMMC_DATA_7, emmc_data_7); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_14, gpio_14); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_15, gpio_15); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_20, gpio_20); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_21, gpio_21); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_22, gpio_22); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_HIF_DATA, hif_data); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_HIF_EN, hif_en); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_HIF_RDY, hif_rdy); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_HIF_CLK, hif_clk); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_40, gpio_40); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_41, gpio_41); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_64, gpio_64); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_65, gpio_65); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_66, gpio_66); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_67, gpio_67); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_80, gpio_80); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_81, gpio_81); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_82, gpio_82); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_83, gpio_83); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_84, gpio_84); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_85, gpio_85); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_86, gpio_86); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_87, gpio_87); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_88, gpio_88); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_89, gpio_89); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_90, gpio_90); +DECLARE_RTD1625_PIN(RTD1625_MAIN2_GPIO_91, gpio_91); + +#define RTD1625_GROUP(_name) \ + { \ + .name = # _name, \ + .pins = rtd1625_ ## _name ## _pins, \ + .num_pins = ARRAY_SIZE(rtd1625_ ## _name ## _pins), \ + } + +static const struct rtd_pin_group_desc rtd1625_iso_pin_groups[] = { + RTD1625_GROUP(gpio_8), + RTD1625_GROUP(gpio_9), + RTD1625_GROUP(gpio_10), + RTD1625_GROUP(gpio_11), + RTD1625_GROUP(usb_cc1), + RTD1625_GROUP(usb_cc2), + RTD1625_GROUP(gpio_45), + RTD1625_GROUP(gpio_46), + RTD1625_GROUP(gpio_47), + RTD1625_GROUP(gpio_48), + RTD1625_GROUP(gpio_49), + RTD1625_GROUP(gpio_50), + RTD1625_GROUP(gpio_52), + RTD1625_GROUP(gpio_94), + RTD1625_GROUP(gpio_95), + RTD1625_GROUP(gpio_96), + RTD1625_GROUP(gpio_97), + RTD1625_GROUP(gpio_98), + RTD1625_GROUP(gpio_99), + RTD1625_GROUP(gpio_100), + RTD1625_GROUP(gpio_101), + RTD1625_GROUP(gpio_102), + RTD1625_GROUP(gpio_103), + RTD1625_GROUP(gpio_104), + RTD1625_GROUP(gpio_105), + RTD1625_GROUP(gpio_106), + RTD1625_GROUP(gpio_107), + RTD1625_GROUP(gpio_108), + RTD1625_GROUP(gpio_109), + RTD1625_GROUP(gpio_110), + RTD1625_GROUP(gpio_111), + RTD1625_GROUP(gpio_112), + RTD1625_GROUP(gpio_128), + RTD1625_GROUP(gpio_129), + RTD1625_GROUP(gpio_130), + RTD1625_GROUP(gpio_131), + RTD1625_GROUP(gpio_145), + RTD1625_GROUP(gpio_146), + RTD1625_GROUP(gpio_147), + RTD1625_GROUP(gpio_148), + RTD1625_GROUP(gpio_149), + RTD1625_GROUP(gpio_150), + RTD1625_GROUP(gpio_151), + RTD1625_GROUP(gpio_152), + RTD1625_GROUP(gpio_153), + RTD1625_GROUP(gpio_154), + RTD1625_GROUP(gpio_155), + RTD1625_GROUP(gpio_156), + RTD1625_GROUP(gpio_157), + RTD1625_GROUP(gpio_158), + RTD1625_GROUP(gpio_159), + RTD1625_GROUP(gpio_160), + RTD1625_GROUP(gpio_161), + RTD1625_GROUP(gpio_162), + RTD1625_GROUP(gpio_163), + RTD1625_GROUP(hi_width), + RTD1625_GROUP(sf_en), + RTD1625_GROUP(arm_trace_dbg_en), + RTD1625_GROUP(ejtag_aucpu0_loc), + RTD1625_GROUP(ejtag_aucpu1_loc), + RTD1625_GROUP(ejtag_ve2_loc), + RTD1625_GROUP(ejtag_scpu_loc), + RTD1625_GROUP(ejtag_pcpu_loc), + RTD1625_GROUP(ejtag_acpu_loc), + RTD1625_GROUP(i2c6_loc), + RTD1625_GROUP(uart0_loc), + RTD1625_GROUP(ai_i2s1_loc), + RTD1625_GROUP(ao_i2s1_loc), + RTD1625_GROUP(etn_phy_loc), + RTD1625_GROUP(spdif_loc), + RTD1625_GROUP(rgmii_vdsel), + RTD1625_GROUP(csi_vdsel), + RTD1625_GROUP(spdif_in_mode), +}; + +static const struct rtd_pin_group_desc rtd1625_isom_pin_groups[] = { + RTD1625_GROUP(gpio_0), + RTD1625_GROUP(gpio_1), + RTD1625_GROUP(gpio_28), + RTD1625_GROUP(gpio_29), + RTD1625_GROUP(ir_rx_loc), +}; + +static const struct rtd_pin_group_desc rtd1625_ve4_pin_groups[] = { + RTD1625_GROUP(gpio_2), + RTD1625_GROUP(gpio_3), + RTD1625_GROUP(gpio_4), + RTD1625_GROUP(gpio_5), + RTD1625_GROUP(gpio_6), + RTD1625_GROUP(gpio_7), + RTD1625_GROUP(gpio_12), + RTD1625_GROUP(gpio_13), + RTD1625_GROUP(gpio_16), + RTD1625_GROUP(gpio_17), + RTD1625_GROUP(gpio_18), + RTD1625_GROUP(gpio_19), + RTD1625_GROUP(gpio_23), + RTD1625_GROUP(gpio_24), + RTD1625_GROUP(gpio_25), + RTD1625_GROUP(gpio_30), + RTD1625_GROUP(gpio_31), + RTD1625_GROUP(gpio_32), + RTD1625_GROUP(gpio_33), + RTD1625_GROUP(gpio_34), + RTD1625_GROUP(gpio_35), + RTD1625_GROUP(gpio_42), + RTD1625_GROUP(gpio_43), + RTD1625_GROUP(gpio_44), + RTD1625_GROUP(gpio_51), + RTD1625_GROUP(gpio_53), + RTD1625_GROUP(gpio_54), + RTD1625_GROUP(gpio_55), + RTD1625_GROUP(gpio_56), + RTD1625_GROUP(gpio_57), + RTD1625_GROUP(gpio_58), + RTD1625_GROUP(gpio_59), + RTD1625_GROUP(gpio_60), + RTD1625_GROUP(gpio_61), + RTD1625_GROUP(gpio_62), + RTD1625_GROUP(gpio_63), + RTD1625_GROUP(gpio_92), + RTD1625_GROUP(gpio_93), + RTD1625_GROUP(gpio_132), + RTD1625_GROUP(gpio_133), + RTD1625_GROUP(gpio_134), + RTD1625_GROUP(gpio_135), + RTD1625_GROUP(gpio_136), + RTD1625_GROUP(gpio_137), + RTD1625_GROUP(gpio_138), + RTD1625_GROUP(gpio_139), + RTD1625_GROUP(gpio_140), + RTD1625_GROUP(gpio_141), + RTD1625_GROUP(gpio_142), + RTD1625_GROUP(gpio_143), + RTD1625_GROUP(gpio_144), + RTD1625_GROUP(gpio_164), + RTD1625_GROUP(gpio_165), + RTD1625_GROUP(ve4_uart_loc), +}; + +static const struct rtd_pin_group_desc rtd1625_main2_pin_groups[] = { + RTD1625_GROUP(gpio_14), + RTD1625_GROUP(gpio_15), + RTD1625_GROUP(gpio_20), + RTD1625_GROUP(gpio_21), + RTD1625_GROUP(gpio_22), + RTD1625_GROUP(hif_data), + RTD1625_GROUP(hif_en), + RTD1625_GROUP(hif_rdy), + RTD1625_GROUP(hif_clk), + RTD1625_GROUP(gpio_40), + RTD1625_GROUP(gpio_41), + RTD1625_GROUP(gpio_64), + RTD1625_GROUP(gpio_65), + RTD1625_GROUP(gpio_66), + RTD1625_GROUP(gpio_67), + RTD1625_GROUP(emmc_data_0), + RTD1625_GROUP(emmc_data_1), + RTD1625_GROUP(emmc_data_2), + RTD1625_GROUP(emmc_data_3), + RTD1625_GROUP(emmc_data_4), + RTD1625_GROUP(emmc_data_5), + RTD1625_GROUP(emmc_data_6), + RTD1625_GROUP(emmc_data_7), + RTD1625_GROUP(emmc_rst_n), + RTD1625_GROUP(emmc_cmd), + RTD1625_GROUP(emmc_clk), + RTD1625_GROUP(emmc_dd_sb), + RTD1625_GROUP(gpio_80), + RTD1625_GROUP(gpio_81), + RTD1625_GROUP(gpio_82), + RTD1625_GROUP(gpio_83), + RTD1625_GROUP(gpio_84), + RTD1625_GROUP(gpio_85), + RTD1625_GROUP(gpio_86), + RTD1625_GROUP(gpio_87), + RTD1625_GROUP(gpio_88), + RTD1625_GROUP(gpio_89), + RTD1625_GROUP(gpio_90), + RTD1625_GROUP(gpio_91), +}; + +static const char * const rtd1625_iso_gpio_groups[] = { + "gpio_10", "gpio_100", "gpio_101", "gpio_102", "gpio_103", "gpio_104", + "gpio_105", "gpio_106", "gpio_107", "gpio_108", "gpio_109", "gpio_11", + "gpio_110", "gpio_111", "gpio_112", "gpio_128", "gpio_129", "gpio_130", + "gpio_131", "gpio_145", "gpio_146", "gpio_147", "gpio_148", "gpio_149", + "gpio_150", "gpio_151", "gpio_152", "gpio_153", "gpio_154", "gpio_155", + "gpio_156", "gpio_157", "gpio_158", "gpio_159", "gpio_160", "gpio_161", + "gpio_162", "gpio_163", "gpio_45", "gpio_46", "gpio_47", "gpio_48", + "gpio_49", "gpio_50", "gpio_52", "gpio_8", "gpio_9", "gpio_94", "gpio_95", + "gpio_96", "gpio_97", "gpio_98", "gpio_99", "usb_cc1", "usb_cc2" +}; + +static const char * const rtd1625_iso_uart1_groups[] = { + "gpio_10", "gpio_11", "gpio_8", "gpio_9" +}; + +static const char * const rtd1625_iso_iso_tristate_groups[] = { + "gpio_10", "gpio_100", "gpio_101", "gpio_102", "gpio_103", "gpio_104", + "gpio_105", "gpio_106", "gpio_107", "gpio_108", "gpio_109", "gpio_11", + "gpio_110", "gpio_111", "gpio_112", "gpio_128", "gpio_129", "gpio_130", + "gpio_131", "gpio_145", "gpio_146", "gpio_147", "gpio_148", "gpio_149", + "gpio_150", "gpio_151", "gpio_152", "gpio_153", "gpio_154", "gpio_155", + "gpio_156", "gpio_157", "gpio_158", "gpio_159", "gpio_160", "gpio_161", + "gpio_162", "gpio_163", "gpio_45", "gpio_46", "gpio_47", "gpio_48", + "gpio_49", "gpio_50", "gpio_52", "gpio_8", "gpio_9", "gpio_94", "gpio_95", + "gpio_96", "gpio_97", "gpio_98", "gpio_99", "usb_cc1", "usb_cc2" +}; + +static const char * const rtd1625_iso_usb_cc1_groups[] = { + "usb_cc1" +}; + +static const char * const rtd1625_iso_usb_cc2_groups[] = { + "usb_cc2" +}; + +static const char * const rtd1625_iso_sdio_groups[] = { + "gpio_45", "gpio_46", "gpio_47", "gpio_48", "gpio_49", "gpio_50" +}; + +static const char * const rtd1625_iso_scpu_ejtag_loc2_groups[] = { + "ejtag_scpu_loc", "gpio_45", "gpio_46", "gpio_47", "gpio_48", "gpio_49" +}; + +static const char * const rtd1625_iso_acpu_ejtag_loc2_groups[] = { + "ejtag_acpu_loc", "gpio_45", "gpio_46", "gpio_47", "gpio_48", "gpio_49" +}; + +static const char * const rtd1625_iso_pcpu_ejtag_loc2_groups[] = { + "ejtag_pcpu_loc", "gpio_45", "gpio_46", "gpio_47", "gpio_48", "gpio_49" +}; + +static const char * const rtd1625_iso_aucpu0_ejtag_loc2_groups[] = { + "ejtag_aucpu0_loc", "gpio_45", "gpio_46", "gpio_47", "gpio_48", "gpio_49" +}; + +static const char * const rtd1625_iso_ve2_ejtag_loc2_groups[] = { + "ejtag_ve2_loc", "gpio_45", "gpio_46", "gpio_47", "gpio_48", "gpio_49" +}; + +static const char * const rtd1625_iso_aucpu1_ejtag_loc2_groups[] = { + "ejtag_aucpu1_loc", "gpio_45", "gpio_46", "gpio_47", "gpio_48", "gpio_49" +}; + +static const char * const rtd1625_iso_pwm4_groups[] = { + "gpio_52" +}; + +static const char * const rtd1625_iso_uart7_groups[] = { + "gpio_94", "gpio_95" +}; + +static const char * const rtd1625_iso_pwm2_loc1_groups[] = { + "gpio_95" +}; + +static const char * const rtd1625_iso_uart8_groups[] = { + "gpio_96", "gpio_97" +}; + +static const char * const rtd1625_iso_pwm3_loc1_groups[] = { + "gpio_97" +}; + +static const char * const rtd1625_iso_ai_tdm0_groups[] = { + "gpio_100", "gpio_101", "gpio_102", "gpio_98" +}; + +static const char * const rtd1625_iso_vtc_i2s_groups[] = { + "gpio_100", "gpio_101", "gpio_102", "gpio_161", "gpio_98" +}; + +static const char * const rtd1625_iso_ai_i2s0_groups[] = { + "gpio_100", "gpio_101", "gpio_102", "gpio_156", "gpio_160", "gpio_161", + "gpio_98" +}; + +static const char * const rtd1625_iso_ao_tdm0_groups[] = { + "gpio_100", "gpio_101", "gpio_102", "gpio_99" +}; + +static const char * const rtd1625_iso_ao_i2s0_groups[] = { + "gpio_100", "gpio_101", "gpio_102", "gpio_112", "gpio_99" +}; + +static const char * const rtd1625_iso_ai_tdm1_groups[] = { + "gpio_103", "gpio_105", "gpio_106", "gpio_107" +}; + +static const char * const rtd1625_iso_ai_i2s1_loc0_groups[] = { + "ai_i2s1_loc", "gpio_103", "gpio_105", "gpio_106", "gpio_107" +}; + +static const char * const rtd1625_iso_ao_i2s0_loc1_groups[] = { + "gpio_103", "gpio_107" +}; + +static const char * const rtd1625_iso_ao_tdm1_loc0_groups[] = { + "gpio_104" +}; + +static const char * const rtd1625_iso_ao_i2s1_loc0_groups[] = { + "ao_i2s1_loc", "gpio_104", "gpio_105", "gpio_106", "gpio_107" +}; + +static const char * const rtd1625_iso_ao_tdm1_groups[] = { + "gpio_105", "gpio_106", "gpio_107" +}; + +static const char * const rtd1625_iso_ai_tdm2_groups[] = { + "gpio_108", "gpio_110", "gpio_111", "gpio_112" +}; + +static const char * const rtd1625_iso_pcm_groups[] = { + "gpio_108", "gpio_109", "gpio_110", "gpio_111" +}; + +static const char * const rtd1625_iso_ai_i2s2_groups[] = { + "gpio_108", "gpio_110", "gpio_111", "gpio_112" +}; + +static const char * const rtd1625_iso_ao_tdm2_groups[] = { + "gpio_109", "gpio_110", "gpio_111", "gpio_112" +}; + +static const char * const rtd1625_iso_ao_i2s2_groups[] = { + "gpio_109", "gpio_110", "gpio_111", "gpio_112" +}; + +static const char * const rtd1625_iso_vtc_ao_i2s_groups[] = { + "gpio_109", "gpio_110", "gpio_111", "gpio_112" +}; + +static const char * const rtd1625_iso_scpu_ejtag_loc0_groups[] = { + "ejtag_scpu_loc", "gpio_112" +}; + +static const char * const rtd1625_iso_acpu_ejtag_loc0_groups[] = { + "ejtag_acpu_loc", "gpio_112" +}; + +static const char * const rtd1625_iso_pcpu_ejtag_loc0_groups[] = { + "ejtag_pcpu_loc", "gpio_112" +}; + +static const char * const rtd1625_iso_aucpu0_ejtag_loc0_groups[] = { + "ejtag_aucpu0_loc", "gpio_112" +}; + +static const char * const rtd1625_iso_ve2_ejtag_loc0_groups[] = { + "ejtag_ve2_loc", "gpio_112" +}; + +static const char * const rtd1625_iso_gpu_ejtag_loc0_groups[] = { + "gpio_112" +}; + +static const char * const rtd1625_iso_ao_tdm1_loc1_groups[] = { + "gpio_112" +}; + +static const char * const rtd1625_iso_aucpu1_ejtag_loc0_groups[] = { + "ejtag_aucpu1_loc", "gpio_112" +}; + +static const char * const rtd1625_iso_edptx_hdp_groups[] = { + "gpio_128" +}; + +static const char * const rtd1625_iso_pwm5_groups[] = { + "gpio_130" +}; + +static const char * const rtd1625_iso_vi0_dtv_groups[] = { + "gpio_145", "gpio_146", "gpio_147", "gpio_148", "gpio_149", "gpio_150", + "gpio_151", "gpio_152", "gpio_153", "gpio_154", "gpio_155", "gpio_156", + "gpio_157", "gpio_158", "gpio_159", "gpio_160", "gpio_161" +}; + +static const char * const rtd1625_iso_vi1_dtv_groups[] = { + "gpio_154", "gpio_155", "gpio_156", "gpio_157", "gpio_158", "gpio_159", + "gpio_160", "gpio_161", "gpio_162" +}; + +static const char * const rtd1625_iso_ao_i2s0_loc0_groups[] = { + "gpio_154", "gpio_155" +}; + +static const char * const rtd1625_iso_dmic0_groups[] = { + "gpio_156", "gpio_157" +}; + +static const char * const rtd1625_iso_vtc_dmic_groups[] = { + "gpio_156", "gpio_157", "gpio_158", "gpio_159" +}; + +static const char * const rtd1625_iso_ai_i2s1_loc1_groups[] = { + "ai_i2s1_loc", "gpio_157", "gpio_158", "gpio_159", "gpio_160" +}; + +static const char * const rtd1625_iso_ao_i2s1_loc1_groups[] = { + "ao_i2s1_loc", "gpio_157", "gpio_158", "gpio_159", "gpio_161" +}; + +static const char * const rtd1625_iso_dmic1_groups[] = { + "gpio_158", "gpio_159" +}; + +static const char * const rtd1625_iso_dmic2_groups[] = { + "gpio_160", "gpio_161" +}; + +static const char * const rtd1625_iso_pwm0_loc2_groups[] = { + "gpio_162" +}; + +static const char * const rtd1625_iso_spdif_in_coaxial_groups[] = { + "gpio_163", "spdif_sel", "spdif_in_mode" +}; + +static const char * const rtd1625_iso_spdif_in_gpio_groups[] = { + "spdif_in_mode" +}; + +static const char * const rtd1625_iso_hi_width_disable_groups[] = { + "hi_width" +}; + +static const char * const rtd1625_iso_hi_width_1bit_groups[] = { + "hi_width" +}; + +static const char * const rtd1625_iso_sf_disable_groups[] = { + "sf_en" +}; + +static const char * const rtd1625_iso_sf_enable_groups[] = { + "sf_en" +}; + +static const char * const rtd1625_iso_arm_trace_debug_disable_groups[] = { + "arm_trace_dbg_en" +}; + +static const char * const rtd1625_iso_arm_trace_debug_enable_groups[] = { + "arm_trace_dbg_en" +}; + +static const char * const rtd1625_iso_aucpu0_ejtag_loc1_groups[] = { + "ejtag_aucpu0_loc" +}; + +static const char * const rtd1625_iso_aucpu1_ejtag_loc1_groups[] = { + "ejtag_aucpu1_loc" +}; + +static const char * const rtd1625_iso_ve2_ejtag_loc1_groups[] = { + "ejtag_ve2_loc" +}; + +static const char * const rtd1625_iso_scpu_ejtag_loc1_groups[] = { + "ejtag_scpu_loc" +}; + +static const char * const rtd1625_iso_pcpu_ejtag_loc1_groups[] = { + "ejtag_pcpu_loc" +}; + +static const char * const rtd1625_iso_acpu_ejtag_loc1_groups[] = { + "ejtag_acpu_loc" +}; + +static const char * const rtd1625_iso_i2c6_loc0_groups[] = { + "i2c6_loc" +}; + +static const char * const rtd1625_iso_i2c6_loc1_groups[] = { + "i2c6_loc" +}; + +static const char * const rtd1625_iso_uart0_loc0_groups[] = { + "uart0_loc" +}; + +static const char * const rtd1625_iso_uart0_loc1_groups[] = { + "uart0_loc" +}; + +static const char * const rtd1625_iso_etn_phy_loc0_groups[] = { + "etn_phy_loc" +}; + +static const char * const rtd1625_iso_etn_phy_loc1_groups[] = { + "etn_phy_loc" +}; + +static const char * const rtd1625_iso_spdif_loc0_groups[] = { + "spdif_loc" +}; + +static const char * const rtd1625_iso_spdif_loc1_groups[] = { + "spdif_loc" +}; + +static const char * const rtd1625_iso_rgmii_1v2_groups[] = { + "rgmii_vdsel" +}; + +static const char * const rtd1625_iso_rgmii_1v8_groups[] = { + "rgmii_vdsel" +}; + +static const char * const rtd1625_iso_rgmii_2v5_groups[] = { + "rgmii_vdsel" +}; + +static const char * const rtd1625_iso_rgmii_3v3_groups[] = { + "rgmii_vdsel" +}; + +static const char * const rtd1625_iso_csi_1v2_groups[] = { + "csi_vdsel" +}; + +static const char * const rtd1625_iso_csi_1v8_groups[] = { + "csi_vdsel" +}; + +static const char * const rtd1625_iso_csi_2v5_groups[] = { + "csi_vdsel" +}; + +static const char * const rtd1625_iso_csi_3v3_groups[] = { + "csi_vdsel" +}; + +static const char * const rtd1625_isom_gpio_groups[] = { + "gpio_0", "gpio_1", "gpio_28", "gpio_29" +}; + +static const char * const rtd1625_isom_pctrl_groups[] = { + "gpio_0", "gpio_1", "gpio_28", "gpio_29" +}; + +static const char * const rtd1625_isom_iso_tristate_groups[] = { + "gpio_0", "gpio_1", "gpio_28", "gpio_29" +}; + +static const char * const rtd1625_isom_ir_rx_loc1_groups[] = { + "gpio_1", "ir_rx_loc" +}; + +static const char * const rtd1625_isom_uart10_groups[] = { + "gpio_28", "gpio_29" +}; + +static const char * const rtd1625_isom_isom_dbg_out_groups[] = { + "gpio_28", "gpio_29" +}; + +static const char * const rtd1625_isom_ir_rx_loc0_groups[] = { + "gpio_29", "ir_rx_loc" +}; + +static const char * const rtd1625_ve4_gpio_groups[] = { + "gpio_12", "gpio_13", "gpio_132", "gpio_133", "gpio_134", "gpio_135", + "gpio_136", "gpio_137", "gpio_138", "gpio_139", "gpio_140", "gpio_141", + "gpio_142", "gpio_143", "gpio_144", "gpio_16", "gpio_164", "gpio_165", + "gpio_17", "gpio_18", "gpio_19", "gpio_2", "gpio_23", "gpio_24", "gpio_25", + "gpio_3", "gpio_30", "gpio_31", "gpio_32", "gpio_33", "gpio_34", "gpio_35", + "gpio_4", "gpio_42", "gpio_43", "gpio_44", "gpio_5", "gpio_51", "gpio_53", + "gpio_54", "gpio_55", "gpio_56", "gpio_57", "gpio_58", "gpio_59", "gpio_6", + "gpio_60", "gpio_61", "gpio_62", "gpio_63", "gpio_7", "gpio_92", "gpio_93" +}; + +static const char * const rtd1625_ve4_uart0_loc0_groups[] = { + "gpio_2", "gpio_3" +}; + +static const char * const rtd1625_ve4_iso_tristate_groups[] = { + "gpio_12", "gpio_13", "gpio_132", "gpio_133", "gpio_134", "gpio_135", + "gpio_136", "gpio_137", "gpio_138", "gpio_139", "gpio_140", "gpio_141", + "gpio_142", "gpio_143", "gpio_144", "gpio_16", "gpio_164", "gpio_165", + "gpio_17", "gpio_18", "gpio_19", "gpio_2", "gpio_23", "gpio_24", "gpio_25", + "gpio_3", "gpio_30", "gpio_31", "gpio_32", "gpio_33", "gpio_34", "gpio_35", + "gpio_4", "gpio_42", "gpio_43", "gpio_44", "gpio_5", "gpio_51", "gpio_53", + "gpio_54", "gpio_55", "gpio_56", "gpio_57", "gpio_58", "gpio_59", "gpio_6", + "gpio_60", "gpio_61", "gpio_62", "gpio_63", "gpio_7", "gpio_92", "gpio_93" +}; + +static const char * const rtd1625_ve4_uart2_groups[] = { + "gpio_4", "gpio_5", "gpio_6", "gpio_7" +}; + +static const char * const rtd1625_ve4_gspi0_groups[] = { + "gpio_4", "gpio_5", "gpio_6", "gpio_7" +}; + +static const char * const rtd1625_ve4_scpu_ejtag_loc0_groups[] = { + "gpio_4", "gpio_5", "gpio_6", "gpio_7" +}; + +static const char * const rtd1625_ve4_acpu_ejtag_loc0_groups[] = { + "gpio_4", "gpio_5", "gpio_6", "gpio_7" +}; + +static const char * const rtd1625_ve4_pcpu_ejtag_loc0_groups[] = { + "gpio_4", "gpio_5", "gpio_6", "gpio_7" +}; + +static const char * const rtd1625_ve4_aucpu0_ejtag_loc0_groups[] = { + "gpio_4", "gpio_5", "gpio_6", "gpio_7" +}; + +static const char * const rtd1625_ve4_ve2_ejtag_loc0_groups[] = { + "gpio_4", "gpio_5", "gpio_6", "gpio_7" +}; + +static const char * const rtd1625_ve4_gpu_ejtag_loc0_groups[] = { + "gpio_4", "gpio_5", "gpio_6", "gpio_7" +}; + +static const char * const rtd1625_ve4_aucpu1_ejtag_loc0_groups[] = { + "gpio_4", "gpio_5", "gpio_6", "gpio_7" +}; + +static const char * const rtd1625_ve4_pwm0_loc1_groups[] = { + "gpio_6" +}; + +static const char * const rtd1625_ve4_pwm1_loc0_groups[] = { + "gpio_7" +}; + +static const char * const rtd1625_ve4_i2c0_groups[] = { + "gpio_12", "gpio_13" +}; + +static const char * const rtd1625_ve4_pwm0_loc3_groups[] = { + "gpio_12" +}; + +static const char * const rtd1625_ve4_dptx_hpd_groups[] = { + "gpio_16" +}; + +static const char * const rtd1625_ve4_pwm2_loc0_groups[] = { + "gpio_16" +}; + +static const char * const rtd1625_ve4_pcie0_groups[] = { + "gpio_18" +}; + +static const char * const rtd1625_ve4_pwm3_loc0_groups[] = { + "gpio_19" +}; + +static const char * const rtd1625_ve4_i2c3_groups[] = { + "gpio_24", "gpio_25" +}; + +static const char * const rtd1625_ve4_pcie1_groups[] = { + "gpio_30" +}; + +static const char * const rtd1625_ve4_uart9_groups[] = { + "gpio_32", "gpio_33" +}; + +static const char * const rtd1625_ve4_ve4_uart_loc2_groups[] = { + "gpio_32", "gpio_33", "ve4_uart_loc" +}; + +static const char * const rtd1625_ve4_sd_groups[] = { + "gpio_42", "gpio_43" +}; + +static const char * const rtd1625_ve4_i2c6_loc1_groups[] = { + "gpio_51", "gpio_61" +}; + +static const char * const rtd1625_ve4_uart3_groups[] = { + "gpio_53", "gpio_54", "gpio_55", "gpio_56" +}; + +static const char * const rtd1625_ve4_ts0_groups[] = { + "gpio_53", "gpio_54", "gpio_55", "gpio_56" +}; + +static const char * const rtd1625_ve4_gspi2_groups[] = { + "gpio_53", "gpio_54", "gpio_55", "gpio_56" +}; + +static const char * const rtd1625_ve4_ve4_uart_loc0_groups[] = { + "gpio_53", "gpio_54", "ve4_uart_loc" +}; + +static const char * const rtd1625_ve4_uart5_groups[] = { + "gpio_57", "gpio_58" +}; + +static const char * const rtd1625_ve4_uart0_loc1_groups[] = { + "gpio_57", "gpio_58" +}; + +static const char * const rtd1625_ve4_gspi1_groups[] = { + "gpio_57", "gpio_58", "gpio_59", "gpio_60" +}; + +static const char * const rtd1625_ve4_uart4_groups[] = { + "gpio_59", "gpio_60" +}; + +static const char * const rtd1625_ve4_i2c4_groups[] = { + "gpio_59", "gpio_60" +}; + +static const char * const rtd1625_ve4_spdif_out_groups[] = { + "gpio_61" +}; + +static const char * const rtd1625_ve4_spdif_in_optical_groups[] = { + "gpio_61" +}; + +static const char * const rtd1625_ve4_pll_test_loc0_groups[] = { + "gpio_62", "gpio_63" +}; + +static const char * const rtd1625_ve4_uart6_groups[] = { + "gpio_92", "gpio_93" +}; + +static const char * const rtd1625_ve4_i2c7_groups[] = { + "gpio_92", "gpio_93" +}; + +static const char * const rtd1625_ve4_ve4_uart_loc1_groups[] = { + "gpio_92", "gpio_93", "ve4_uart_loc" +}; + +static const char * const rtd1625_ve4_pwm1_loc1_groups[] = { + "gpio_93" +}; + +static const char * const rtd1625_ve4_pwm6_groups[] = { + "gpio_132" +}; + +static const char * const rtd1625_ve4_ts1_groups[] = { + "gpio_133", "gpio_134", "gpio_135", "gpio_136" +}; + +static const char * const rtd1625_ve4_pwm0_loc0_groups[] = { + "gpio_136" +}; + +static const char * const rtd1625_ve4_i2c6_loc0_groups[] = { + "gpio_137", "gpio_138" +}; + +static const char * const rtd1625_ve4_csi0_groups[] = { + "gpio_141" +}; + +static const char * const rtd1625_ve4_csi1_groups[] = { + "gpio_144" +}; + +static const char * const rtd1625_ve4_etn_led_loc1_groups[] = { + "gpio_164", "gpio_165" +}; + +static const char * const rtd1625_ve4_etn_phy_loc1_groups[] = { + "gpio_164", "gpio_165" +}; + +static const char * const rtd1625_ve4_i2c5_groups[] = { + "gpio_164", "gpio_165" +}; + +static const char * const rtd1625_main2_gpio_groups[] = { + "emmc_clk", "emmc_cmd", "emmc_data_0", "emmc_data_1", "emmc_data_2", + "emmc_data_3", "emmc_data_4", "emmc_data_5", "emmc_data_6", "emmc_data_7", + "emmc_dd_sb", "emmc_rst_n", "gpio_14", "gpio_15", "gpio_20", "gpio_21", + "gpio_22", "gpio_40", "gpio_41", "gpio_64", "gpio_65", "gpio_66", "gpio_67", + "gpio_80", "gpio_81", "gpio_82", "gpio_83", "gpio_84", "gpio_85", "gpio_86", + "gpio_87", "gpio_88", "gpio_89", "gpio_90", "gpio_91", "hif_clk", + "hif_data", "hif_en", "hif_rdy" +}; + +static const char * const rtd1625_main2_emmc_groups[] = { + "emmc_clk", "emmc_cmd", "emmc_data_0", "emmc_data_1", "emmc_data_2", + "emmc_data_3", "emmc_data_4", "emmc_data_5", "emmc_data_6", "emmc_data_7", + "emmc_dd_sb", "emmc_rst_n" +}; + +static const char * const rtd1625_main2_iso_tristate_groups[] = { + "emmc_clk", "emmc_cmd", "emmc_data_0", "emmc_data_1", "emmc_data_2", + "emmc_data_3", "emmc_data_4", "emmc_data_5", "emmc_data_6", "emmc_data_7", + "emmc_dd_sb", "emmc_rst_n", "gpio_14", "gpio_15", "gpio_20", "gpio_21", + "gpio_40", "gpio_41", "gpio_64", "gpio_65", "gpio_66", "gpio_67", "gpio_80", + "gpio_81", "gpio_82", "gpio_83", "gpio_84", "gpio_85", "gpio_86", "gpio_87", + "gpio_88", "gpio_89", "gpio_90", "gpio_91", "hif_clk", "hif_data", "hif_en", + "hif_rdy" +}; + +static const char * const rtd1625_main2_nf_groups[] = { + "emmc_data_0", "emmc_data_1", "emmc_data_2", "emmc_data_3", "emmc_data_4", + "emmc_data_5" +}; + +static const char * const rtd1625_main2_etn_led_loc0_groups[] = { + "gpio_14", "gpio_15" +}; + +static const char * const rtd1625_main2_etn_phy_loc0_groups[] = { + "gpio_14", "gpio_15" +}; + +static const char * const rtd1625_main2_rgmii_groups[] = { + "gpio_14", "gpio_15", "gpio_80", "gpio_81", "gpio_82", "gpio_83", "gpio_84", + "gpio_85", "gpio_86", "gpio_87", "gpio_88", "gpio_89", "gpio_90", "gpio_91" +}; + +static const char * const rtd1625_main2_i2c1_groups[] = { + "gpio_20", "gpio_21" +}; + +static const char * const rtd1625_main2_dbg_out1_groups[] = { + "gpio_22" +}; + +static const char * const rtd1625_main2_sd_groups[] = { + "gpio_40", "gpio_41", "hif_clk", "hif_data", "hif_en", "hif_rdy" +}; + +static const char * const rtd1625_main2_scpu_ejtag_loc1_groups[] = { + "gpio_40", "hif_clk", "hif_data", "hif_en", "hif_rdy" +}; + +static const char * const rtd1625_main2_acpu_ejtag_loc1_groups[] = { + "gpio_40", "hif_clk", "hif_data", "hif_en", "hif_rdy" +}; + +static const char * const rtd1625_main2_pcpu_ejtag_loc1_groups[] = { + "gpio_40", "hif_clk", "hif_data", "hif_en", "hif_rdy" +}; + +static const char * const rtd1625_main2_aupu0_ejtag_loc1_groups[] = { + "gpio_40", "hif_clk", "hif_data", "hif_en", "hif_rdy" +}; + +static const char * const rtd1625_main2_ve2_ejtag_loc1_groups[] = { + "gpio_40", "hif_clk", "hif_data", "hif_en", "hif_rdy" +}; + +static const char * const rtd1625_main2_hi_loc0_groups[] = { + "hif_clk", "hif_data", "hif_en", "hif_rdy" +}; + +static const char * const rtd1625_main2_hi_m_groups[] = { + "hif_clk", "hif_data", "hif_en", "hif_rdy" +}; + +static const char * const rtd1625_main2_aupu1_ejtag_loc1_groups[] = { + "gpio_40", "hif_clk", "hif_data", "hif_en", "hif_rdy" +}; + +static const char * const rtd1625_main2_spi_groups[] = { + "gpio_64", "gpio_65", "gpio_66", "gpio_67" +}; + +static const char * const rtd1625_main2_pll_test_loc1_groups[] = { + "gpio_65", "gpio_66" +}; + +static const char * const rtd1625_main2_rmii_groups[] = { + "gpio_80", "gpio_81", "gpio_82", "gpio_83", "gpio_84", "gpio_87", "gpio_88", + "gpio_89" +}; + +#define RTD1625_FUNC(_group, _name) \ + { \ + .name = # _name, \ + .groups = rtd1625_ ## _group ## _ ## _name ## _groups, \ + .num_groups = ARRAY_SIZE(rtd1625_ ## _group ## _ ## _name ## _groups), \ + } + +static const struct rtd_pin_func_desc rtd1625_iso_pin_functions[] = { + RTD1625_FUNC(iso, gpio), + RTD1625_FUNC(iso, uart1), + RTD1625_FUNC(iso, iso_tristate), + RTD1625_FUNC(iso, usb_cc1), + RTD1625_FUNC(iso, usb_cc2), + RTD1625_FUNC(iso, sdio), + RTD1625_FUNC(iso, scpu_ejtag_loc2), + RTD1625_FUNC(iso, acpu_ejtag_loc2), + RTD1625_FUNC(iso, pcpu_ejtag_loc2), + RTD1625_FUNC(iso, aucpu0_ejtag_loc2), + RTD1625_FUNC(iso, ve2_ejtag_loc2), + RTD1625_FUNC(iso, aucpu1_ejtag_loc2), + RTD1625_FUNC(iso, pwm4), + RTD1625_FUNC(iso, uart7), + RTD1625_FUNC(iso, pwm2_loc1), + RTD1625_FUNC(iso, uart8), + RTD1625_FUNC(iso, pwm3_loc1), + RTD1625_FUNC(iso, ai_tdm0), + RTD1625_FUNC(iso, vtc_i2s), + RTD1625_FUNC(iso, ai_i2s0), + RTD1625_FUNC(iso, ao_tdm0), + RTD1625_FUNC(iso, ao_i2s0), + RTD1625_FUNC(iso, ai_tdm1), + RTD1625_FUNC(iso, ai_i2s1_loc0), + RTD1625_FUNC(iso, ao_i2s0_loc1), + RTD1625_FUNC(iso, ao_tdm1_loc0), + RTD1625_FUNC(iso, ao_i2s1_loc0), + RTD1625_FUNC(iso, ao_tdm1), + RTD1625_FUNC(iso, ai_tdm2), + RTD1625_FUNC(iso, pcm), + RTD1625_FUNC(iso, ai_i2s2), + RTD1625_FUNC(iso, ao_tdm2), + RTD1625_FUNC(iso, ao_i2s2), + RTD1625_FUNC(iso, vtc_ao_i2s), + RTD1625_FUNC(iso, scpu_ejtag_loc0), + RTD1625_FUNC(iso, acpu_ejtag_loc0), + RTD1625_FUNC(iso, pcpu_ejtag_loc0), + RTD1625_FUNC(iso, aucpu0_ejtag_loc0), + RTD1625_FUNC(iso, ve2_ejtag_loc0), + RTD1625_FUNC(iso, gpu_ejtag_loc0), + RTD1625_FUNC(iso, ao_tdm1_loc1), + RTD1625_FUNC(iso, aucpu1_ejtag_loc0), + RTD1625_FUNC(iso, edptx_hdp), + RTD1625_FUNC(iso, pwm5), + RTD1625_FUNC(iso, vi0_dtv), + RTD1625_FUNC(iso, vi1_dtv), + RTD1625_FUNC(iso, ao_i2s0_loc0), + RTD1625_FUNC(iso, dmic0), + RTD1625_FUNC(iso, vtc_dmic), + RTD1625_FUNC(iso, ai_i2s1_loc1), + RTD1625_FUNC(iso, ao_i2s1_loc1), + RTD1625_FUNC(iso, dmic1), + RTD1625_FUNC(iso, dmic2), + RTD1625_FUNC(iso, pwm0_loc2), + RTD1625_FUNC(iso, spdif_in_coaxial), + RTD1625_FUNC(iso, spdif_in_gpio), + RTD1625_FUNC(iso, hi_width_disable), + RTD1625_FUNC(iso, hi_width_1bit), + RTD1625_FUNC(iso, sf_disable), + RTD1625_FUNC(iso, sf_enable), + RTD1625_FUNC(iso, arm_trace_debug_disable), + RTD1625_FUNC(iso, arm_trace_debug_enable), + RTD1625_FUNC(iso, aucpu0_ejtag_loc1), + RTD1625_FUNC(iso, aucpu1_ejtag_loc1), + RTD1625_FUNC(iso, ve2_ejtag_loc1), + RTD1625_FUNC(iso, scpu_ejtag_loc1), + RTD1625_FUNC(iso, pcpu_ejtag_loc1), + RTD1625_FUNC(iso, acpu_ejtag_loc1), + RTD1625_FUNC(iso, i2c6_loc0), + RTD1625_FUNC(iso, i2c6_loc1), + RTD1625_FUNC(iso, uart0_loc0), + RTD1625_FUNC(iso, uart0_loc1), + RTD1625_FUNC(iso, etn_phy_loc0), + RTD1625_FUNC(iso, etn_phy_loc1), + RTD1625_FUNC(iso, spdif_loc0), + RTD1625_FUNC(iso, spdif_loc1), + RTD1625_FUNC(iso, rgmii_1v2), + RTD1625_FUNC(iso, rgmii_1v8), + RTD1625_FUNC(iso, rgmii_2v5), + RTD1625_FUNC(iso, rgmii_3v3), + RTD1625_FUNC(iso, csi_1v2), + RTD1625_FUNC(iso, csi_1v8), + RTD1625_FUNC(iso, csi_2v5), + RTD1625_FUNC(iso, csi_3v3), +}; + +static const struct rtd_pin_func_desc rtd1625_isom_pin_functions[] = { + RTD1625_FUNC(isom, gpio), + RTD1625_FUNC(isom, pctrl), + RTD1625_FUNC(isom, iso_tristate), + RTD1625_FUNC(isom, ir_rx_loc1), + RTD1625_FUNC(isom, uart10), + RTD1625_FUNC(isom, isom_dbg_out), + RTD1625_FUNC(isom, ir_rx_loc0), +}; + +static const struct rtd_pin_func_desc rtd1625_ve4_pin_functions[] = { + RTD1625_FUNC(ve4, gpio), + RTD1625_FUNC(ve4, uart0_loc0), + RTD1625_FUNC(ve4, iso_tristate), + RTD1625_FUNC(ve4, uart2), + RTD1625_FUNC(ve4, gspi0), + RTD1625_FUNC(ve4, scpu_ejtag_loc0), + RTD1625_FUNC(ve4, acpu_ejtag_loc0), + RTD1625_FUNC(ve4, pcpu_ejtag_loc0), + RTD1625_FUNC(ve4, aucpu0_ejtag_loc0), + RTD1625_FUNC(ve4, ve2_ejtag_loc0), + RTD1625_FUNC(ve4, gpu_ejtag_loc0), + RTD1625_FUNC(ve4, aucpu1_ejtag_loc0), + RTD1625_FUNC(ve4, pwm0_loc1), + RTD1625_FUNC(ve4, pwm1_loc0), + RTD1625_FUNC(ve4, i2c0), + RTD1625_FUNC(ve4, pwm0_loc3), + RTD1625_FUNC(ve4, dptx_hpd), + RTD1625_FUNC(ve4, pwm2_loc0), + RTD1625_FUNC(ve4, pcie0), + RTD1625_FUNC(ve4, pwm3_loc0), + RTD1625_FUNC(ve4, i2c3), + RTD1625_FUNC(ve4, pcie1), + RTD1625_FUNC(ve4, uart9), + RTD1625_FUNC(ve4, ve4_uart_loc2), + RTD1625_FUNC(ve4, sd), + RTD1625_FUNC(ve4, i2c6_loc1), + RTD1625_FUNC(ve4, uart3), + RTD1625_FUNC(ve4, ts0), + RTD1625_FUNC(ve4, gspi2), + RTD1625_FUNC(ve4, ve4_uart_loc0), + RTD1625_FUNC(ve4, uart5), + RTD1625_FUNC(ve4, uart0_loc1), + RTD1625_FUNC(ve4, gspi1), + RTD1625_FUNC(ve4, uart4), + RTD1625_FUNC(ve4, i2c4), + RTD1625_FUNC(ve4, spdif_out), + RTD1625_FUNC(ve4, spdif_in_optical), + RTD1625_FUNC(ve4, pll_test_loc0), + RTD1625_FUNC(ve4, uart6), + RTD1625_FUNC(ve4, i2c7), + RTD1625_FUNC(ve4, ve4_uart_loc1), + RTD1625_FUNC(ve4, pwm1_loc1), + RTD1625_FUNC(ve4, pwm6), + RTD1625_FUNC(ve4, ts1), + RTD1625_FUNC(ve4, pwm0_loc0), + RTD1625_FUNC(ve4, i2c6_loc0), + RTD1625_FUNC(ve4, csi0), + RTD1625_FUNC(ve4, csi1), + RTD1625_FUNC(ve4, etn_led_loc1), + RTD1625_FUNC(ve4, etn_phy_loc1), + RTD1625_FUNC(ve4, i2c5), +}; + +static const struct rtd_pin_func_desc rtd1625_main2_pin_functions[] = { + RTD1625_FUNC(main2, gpio), + RTD1625_FUNC(main2, emmc), + RTD1625_FUNC(main2, iso_tristate), + RTD1625_FUNC(main2, nf), + RTD1625_FUNC(main2, etn_led_loc0), + RTD1625_FUNC(main2, etn_phy_loc0), + RTD1625_FUNC(main2, rgmii), + RTD1625_FUNC(main2, i2c1), + RTD1625_FUNC(main2, dbg_out1), + RTD1625_FUNC(main2, sd), + RTD1625_FUNC(main2, scpu_ejtag_loc1), + RTD1625_FUNC(main2, acpu_ejtag_loc1), + RTD1625_FUNC(main2, pcpu_ejtag_loc1), + RTD1625_FUNC(main2, aupu0_ejtag_loc1), + RTD1625_FUNC(main2, ve2_ejtag_loc1), + RTD1625_FUNC(main2, hi_loc0), + RTD1625_FUNC(main2, hi_m), + RTD1625_FUNC(main2, aupu1_ejtag_loc1), + RTD1625_FUNC(main2, spi), + RTD1625_FUNC(main2, pll_test_loc1), + RTD1625_FUNC(main2, rmii), +}; + +#undef RTD1625_FUNC + +static const struct rtd_pin_desc rtd1625_iso_muxes[] = { + [RTD1625_ISO_GPIO_8] = RTK_PIN_MUX(gpio_8, 0x0, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "uart1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_ISO_GPIO_9] = RTK_PIN_MUX(gpio_9, 0x0, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 4), "uart1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_ISO_GPIO_10] = RTK_PIN_MUX(gpio_10, 0x0, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 8), "uart1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_ISO_GPIO_11] = RTK_PIN_MUX(gpio_11, 0x0, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "uart1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_ISO_USB_CC1] = RTK_PIN_MUX(usb_cc1, 0x0, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 16), "usb_cc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_ISO_USB_CC2] = RTK_PIN_MUX(usb_cc2, 0x0, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "usb_cc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_ISO_GPIO_45] = RTK_PIN_MUX(gpio_45, 0x0, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 24), "sdio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 24), "scpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 24), "acpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 24), "pcpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 24), "aucpu0_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 24), "ve2_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 24), "aucpu1_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_ISO_GPIO_46] = RTK_PIN_MUX(gpio_46, 0x0, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 28), "sdio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 28), "scpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 28), "acpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 28), "pcpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 28), "aucpu0_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 28), "ve2_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 28), "aucpu1_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_ISO_GPIO_47] = RTK_PIN_MUX(gpio_47, 0x4, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "sdio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 0), "scpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 0), "acpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 0), "pcpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 0), "aucpu0_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 0), "ve2_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 0), "aucpu1_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_ISO_GPIO_48] = RTK_PIN_MUX(gpio_48, 0x4, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 4), "sdio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 4), "scpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 4), "acpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 4), "pcpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 4), "aucpu0_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 4), "ve2_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 4), "aucpu1_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_ISO_GPIO_49] = RTK_PIN_MUX(gpio_49, 0x4, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 8), "sdio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 8), "scpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 8), "acpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 8), "pcpu_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 8), "aucpu0_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 8), "ve2_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 8), "aucpu1_ejtag_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_ISO_GPIO_50] = RTK_PIN_MUX(gpio_50, 0x4, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "sdio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_ISO_GPIO_52] = RTK_PIN_MUX(gpio_52, 0x4, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 16), "pwm4"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_ISO_GPIO_94] = RTK_PIN_MUX(gpio_94, 0x4, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "uart7"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_ISO_GPIO_95] = RTK_PIN_MUX(gpio_95, 0x4, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 24), "uart7"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 24), "pwm2_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_ISO_GPIO_96] = RTK_PIN_MUX(gpio_96, 0x4, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 28), "uart8"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_ISO_GPIO_97] = RTK_PIN_MUX(gpio_97, 0x8, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "uart8"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 0), "pwm3_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_ISO_GPIO_98] = RTK_PIN_MUX(gpio_98, 0x8, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 4), "ai_tdm0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 4), "vtc_i2s"), + RTK_PIN_FUNC(SHIFT_LEFT(0x8, 4), "ai_i2s0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_ISO_GPIO_99] = RTK_PIN_MUX(gpio_99, 0x8, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "ao_tdm0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 8), "ao_i2s0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_ISO_GPIO_100] = RTK_PIN_MUX(gpio_100, 0x8, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "ai_tdm0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 12), "ao_tdm0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 12), "vtc_i2s"), + RTK_PIN_FUNC(SHIFT_LEFT(0x8, 12), "ai_i2s0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 12), "ao_i2s0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_ISO_GPIO_101] = RTK_PIN_MUX(gpio_101, 0x8, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 16), "ai_tdm0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "ao_tdm0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 16), "vtc_i2s"), + RTK_PIN_FUNC(SHIFT_LEFT(0x8, 16), "ai_i2s0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 16), "ao_i2s0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_ISO_GPIO_102] = RTK_PIN_MUX(gpio_102, 0x8, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "ai_tdm0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 20), "ao_tdm0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 20), "vtc_i2s"), + RTK_PIN_FUNC(SHIFT_LEFT(0x8, 20), "ai_i2s0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 20), "ao_i2s0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_ISO_GPIO_103] = RTK_PIN_MUX(gpio_103, 0x8, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 24), "ai_tdm1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 24), "ai_i2s1_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 24), "ao_i2s0_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_ISO_GPIO_104] = RTK_PIN_MUX(gpio_104, 0x8, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 28), "ao_tdm1_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 28), "ao_i2s1_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_ISO_GPIO_105] = RTK_PIN_MUX(gpio_105, 0xc, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "ai_tdm1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "ao_tdm1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 0), "ai_i2s1_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 0), "ao_i2s1_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_ISO_GPIO_106] = RTK_PIN_MUX(gpio_106, 0xc, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 4), "ai_tdm1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 4), "ao_tdm1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 4), "ai_i2s1_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 4), "ao_i2s1_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_ISO_GPIO_107] = RTK_PIN_MUX(gpio_107, 0xc, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 8), "ai_tdm1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "ao_tdm1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 8), "ai_i2s1_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 8), "ao_i2s1_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xb, 8), "ao_i2s0_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_ISO_GPIO_108] = RTK_PIN_MUX(gpio_108, 0xc, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "ai_tdm2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 12), "pcm"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 12), "ai_i2s2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_ISO_GPIO_109] = RTK_PIN_MUX(gpio_109, 0xc, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "ao_tdm2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 16), "pcm"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 16), "ao_i2s2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 16), "vtc_ao_i2s"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_ISO_GPIO_110] = RTK_PIN_MUX(gpio_110, 0xc, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "ai_tdm2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 20), "ao_tdm2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 20), "pcm"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 20), "ai_i2s2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 20), "ao_i2s2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 20), "vtc_ao_i2s"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_ISO_GPIO_111] = RTK_PIN_MUX(gpio_111, 0xc, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 24), "ai_tdm2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 24), "ao_tdm2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 24), "pcm"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 24), "ai_i2s2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 24), "ao_i2s2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 24), "vtc_ao_i2s"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + + [RTD1625_ISO_GPIO_112] = RTK_PIN_MUX(gpio_112, 0x10, GENMASK(4, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "ai_tdm2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "ao_tdm2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 0), "scpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 0), "acpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 0), "pcpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 0), "aucpu0_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 0), "ve2_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 0), "ai_i2s2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 0), "ao_i2s2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xc, 0), "gpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 0), "vtc_ao_i2s"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate"), + RTK_PIN_FUNC(SHIFT_LEFT(0x10, 0), "ao_tdm1_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x11, 0), "aucpu1_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x13, 0), "ao_i2s0") + ), + [RTD1625_ISO_GPIO_128] = RTK_PIN_MUX(gpio_128, 0x10, GENMASK(8, 5), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 5), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 5), "edptx_hdp"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 5), "iso_tristate") + ), + [RTD1625_ISO_GPIO_129] = RTK_PIN_MUX(gpio_129, 0x10, GENMASK(12, 9), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 9), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 9), "iso_tristate") + ), + [RTD1625_ISO_GPIO_130] = RTK_PIN_MUX(gpio_130, 0x10, GENMASK(16, 13), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 13), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 13), "pwm5"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 13), "iso_tristate") + ), + [RTD1625_ISO_GPIO_131] = RTK_PIN_MUX(gpio_131, 0x10, GENMASK(20, 17), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 17), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 17), "iso_tristate") + ), + [RTD1625_ISO_GPIO_145] = RTK_PIN_MUX(gpio_145, 0x10, GENMASK(24, 21), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 21), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 21), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 21), "iso_tristate") + ), + [RTD1625_ISO_GPIO_146] = RTK_PIN_MUX(gpio_146, 0x10, GENMASK(28, 25), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 25), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 25), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 25), "iso_tristate") + ), + + [RTD1625_ISO_GPIO_147] = RTK_PIN_MUX(gpio_147, 0x14, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_ISO_GPIO_148] = RTK_PIN_MUX(gpio_148, 0x14, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 4), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_ISO_GPIO_149] = RTK_PIN_MUX(gpio_149, 0x14, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 8), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_ISO_GPIO_150] = RTK_PIN_MUX(gpio_150, 0x14, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_ISO_GPIO_151] = RTK_PIN_MUX(gpio_151, 0x14, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 16), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_ISO_GPIO_152] = RTK_PIN_MUX(gpio_152, 0x14, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_ISO_GPIO_153] = RTK_PIN_MUX(gpio_153, 0x14, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 24), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_ISO_GPIO_154] = RTK_PIN_MUX(gpio_154, 0x14, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 28), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 28), "vi1_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 28), "ao_i2s0_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_ISO_GPIO_155] = RTK_PIN_MUX(gpio_155, 0x18, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "vi1_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 0), "ao_i2s0_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_ISO_GPIO_156] = RTK_PIN_MUX(gpio_156, 0x18, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 4), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 4), "vi1_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 4), "dmic0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 4), "vtc_dmic"), + RTK_PIN_FUNC(SHIFT_LEFT(0x8, 4), "ai_i2s0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_ISO_GPIO_157] = RTK_PIN_MUX(gpio_157, 0x18, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 8), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "vi1_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 8), "dmic0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 8), "vtc_dmic"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 8), "ai_i2s1_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 8), "ao_i2s1_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_ISO_GPIO_158] = RTK_PIN_MUX(gpio_158, 0x18, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 12), "vi1_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 12), "dmic1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 12), "vtc_dmic"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 12), "ai_i2s1_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 12), "ao_i2s1_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_ISO_GPIO_159] = RTK_PIN_MUX(gpio_159, 0x18, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 16), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "vi1_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 16), "dmic1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 16), "vtc_dmic"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 16), "ai_i2s1_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 16), "ao_i2s1_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_ISO_GPIO_160] = RTK_PIN_MUX(gpio_160, 0x18, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 20), "vi1_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 20), "dmic2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x8, 20), "ai_i2s0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 20), "ai_i2s1_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_ISO_GPIO_161] = RTK_PIN_MUX(gpio_161, 0x18, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 24), "vi0_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 24), "vi1_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 24), "dmic2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 24), "vtc_i2s"), + RTK_PIN_FUNC(SHIFT_LEFT(0x8, 24), "ai_i2s0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 24), "ao_i2s1_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_ISO_GPIO_162] = RTK_PIN_MUX(gpio_162, 0x18, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 28), "vi1_dtv"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 28), "pwm0_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_ISO_GPIO_163] = RTK_PIN_MUX(gpio_163, 0x1c, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 0), "spdif_in_coaxial"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + + [RTD1625_ISO_HI_WIDTH] = RTK_PIN_MUX(hi_width, 0x120, GENMASK(9, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "hi_width_disable"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 8), "hi_width_1bit") + ), + [RTD1625_ISO_SF_EN] = RTK_PIN_MUX(sf_en, 0x120, GENMASK(11, 11), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 11), "sf_disable"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 11), "sf_enable") + ), + [RTD1625_ISO_ARM_TRACE_DBG_EN] = RTK_PIN_MUX(arm_trace_dbg_en, 0x120, GENMASK(13, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "arm_trace_debug_disable"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "arm_trace_debug_enable") + ), + [RTD1625_ISO_EJTAG_AUCPU0_LOC] = RTK_PIN_MUX(ejtag_aucpu0_loc, 0x120, GENMASK(16, 14), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 14), "aucpu0_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 14), "aucpu0_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 14), "aucpu0_ejtag_loc2") + ), + [RTD1625_ISO_EJTAG_AUCPU1_LOC] = RTK_PIN_MUX(ejtag_aucpu1_loc, 0x120, GENMASK(19, 17), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 17), "aucpu1_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 17), "aucpu1_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 17), "aucpu1_ejtag_loc2") + ), + [RTD1625_ISO_EJTAG_VE2_LOC] = RTK_PIN_MUX(ejtag_ve2_loc, 0x120, GENMASK(22, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "ve2_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 20), "ve2_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 20), "ve2_ejtag_loc2") + ), + [RTD1625_ISO_EJTAG_SCPU_LOC] = RTK_PIN_MUX(ejtag_scpu_loc, 0x120, GENMASK(25, 23), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 23), "scpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 23), "scpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 23), "scpu_ejtag_loc2") + ), + [RTD1625_ISO_EJTAG_PCPU_LOC] = RTK_PIN_MUX(ejtag_pcpu_loc, 0x120, GENMASK(28, 26), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 26), "pcpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 26), "pcpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 26), "pcpu_ejtag_loc2") + ), + [RTD1625_ISO_EJTAG_ACPU_LOC] = RTK_PIN_MUX(ejtag_acpu_loc, 0x120, GENMASK(31, 29), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 29), "acpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 29), "acpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 29), "acpu_ejtag_loc2") + ), + [RTD1625_ISO_I2C6_LOC] = RTK_PIN_MUX(i2c6_loc, 0x128, GENMASK(1, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "i2c6_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "i2c6_loc1") + ), + [RTD1625_ISO_UART0_LOC] = RTK_PIN_MUX(uart0_loc, 0x128, GENMASK(3, 2), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 2), "uart0_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 2), "uart0_loc1") + ), + [RTD1625_ISO_AI_I2S1_LOC] = RTK_PIN_MUX(ai_i2s1_loc, 0x128, GENMASK(5, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 4), "ai_i2s1_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 4), "ai_i2s1_loc1") + ), + [RTD1625_ISO_AO_I2S1_LOC] = RTK_PIN_MUX(ao_i2s1_loc, 0x128, GENMASK(7, 6), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 6), "ao_i2s1_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 6), "ao_i2s1_loc1") + ), + [RTD1625_ISO_ETN_PHY_LOC] = RTK_PIN_MUX(etn_phy_loc, 0x128, GENMASK(9, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 8), "etn_phy_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "etn_phy_loc1") + ), + [RTD1625_ISO_SPDIF_LOC] = RTK_PIN_MUX(spdif_loc, 0x128, GENMASK(14, 13), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 13), "spdif_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 13), "spdif_loc1") + ), + [RTD1625_ISO_RGMII_VDSEL] = RTK_PIN_MUX(rgmii_vdsel, 0x188, GENMASK(17, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "rgmii_3v3"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 16), "rgmii_2v5"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "rgmii_1v8"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 16), "rgmii_1v2") + ), + [RTD1625_ISO_CSI_VDSEL] = RTK_PIN_MUX(csi_vdsel, 0x188, GENMASK(19, 18), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 18), "csi_3v3"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 18), "csi_2v5"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 18), "csi_1v8"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 18), "csi_1v2") + ), + + [RTD1625_ISO_SPDIF_IN_MODE] = RTK_PIN_MUX(spdif_in_mode, 0x188, GENMASK(20, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "spdif_in_gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "spdif_in_coaxial") + ), +}; + +static const struct rtd_pin_desc rtd1625_isom_muxes[] = { + [RTD1625_ISOM_GPIO_0] = RTK_PIN_MUX(gpio_0, 0x0, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "pctrl"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_ISOM_GPIO_1] = RTK_PIN_MUX(gpio_1, 0x0, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 4), "pctrl"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 4), "ir_rx_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_ISOM_GPIO_28] = RTK_PIN_MUX(gpio_28, 0x0, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 8), "uart10"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "pctrl"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 8), "isom_dbg_out"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_ISOM_GPIO_29] = RTK_PIN_MUX(gpio_29, 0x0, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "uart10"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 12), "pctrl"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 12), "ir_rx_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 12), "isom_dbg_out"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_ISOM_IR_RX_LOC] = RTK_PIN_MUX(ir_rx_loc, 0x30, GENMASK(1, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "ir_rx_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "ir_rx_loc1") + ), +}; + +static const struct rtd_pin_desc rtd1625_ve4_muxes[] = { + [RTD1625_VE4_GPIO_2] = RTK_PIN_MUX(gpio_2, 0x0, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "uart0_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_VE4_GPIO_3] = RTK_PIN_MUX(gpio_3, 0x0, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 4), "uart0_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_VE4_GPIO_4] = RTK_PIN_MUX(gpio_4, 0x0, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 8), "uart2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "gspi0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 8), "scpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 8), "acpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 8), "pcpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 8), "aucpu0_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 8), "ve2_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xc, 8), "gpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 8), "aucpu1_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_VE4_GPIO_5] = RTK_PIN_MUX(gpio_5, 0x0, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "uart2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 12), "gspi0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 12), "scpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 12), "acpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 12), "pcpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 12), "aucpu0_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 12), "ve2_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xc, 12), "gpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 12), "aucpu1_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_VE4_GPIO_6] = RTK_PIN_MUX(gpio_6, 0x0, GENMASK(20, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 16), "uart2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "gspi0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 16), "scpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 16), "acpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 16), "pcpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 16), "aucpu0_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 16), "ve2_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xc, 16), "gpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 16), "pwm0_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 16), "aucpu1_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_VE4_GPIO_7] = RTK_PIN_MUX(gpio_7, 0x0, GENMASK(24, 21), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 21), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 21), "uart2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 21), "gspi0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 21), "scpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 21), "acpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 21), "pcpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 21), "aucpu0_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 21), "ve2_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xc, 21), "gpu_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 21), "pwm1_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 21), "aucpu1_ejtag_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 21), "iso_tristate") + ), + [RTD1625_VE4_GPIO_12] = RTK_PIN_MUX(gpio_12, 0x0, GENMASK(28, 25), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 25), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 25), "i2c0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 25), "pwm0_loc3"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 25), "iso_tristate") + ), + + [RTD1625_VE4_GPIO_13] = RTK_PIN_MUX(gpio_13, 0x4, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "i2c0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_VE4_GPIO_16] = RTK_PIN_MUX(gpio_16, 0x4, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 4), "dptx_hpd"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 4), "pwm2_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_VE4_GPIO_17] = RTK_PIN_MUX(gpio_17, 0x4, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_VE4_GPIO_18] = RTK_PIN_MUX(gpio_18, 0x4, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "pcie0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_VE4_GPIO_19] = RTK_PIN_MUX(gpio_19, 0x4, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 16), "pwm3_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_VE4_GPIO_23] = RTK_PIN_MUX(gpio_23, 0x4, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_VE4_GPIO_24] = RTK_PIN_MUX(gpio_24, 0x4, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 24), "i2c3"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_VE4_GPIO_25] = RTK_PIN_MUX(gpio_25, 0x4, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 28), "i2c3"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_VE4_GPIO_30] = RTK_PIN_MUX(gpio_30, 0x8, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "pcie1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_VE4_GPIO_31] = RTK_PIN_MUX(gpio_31, 0x8, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_VE4_GPIO_32] = RTK_PIN_MUX(gpio_32, 0x8, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 8), "uart9"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 8), "ve4_uart_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_VE4_GPIO_33] = RTK_PIN_MUX(gpio_33, 0x8, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "uart9"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 12), "ve4_uart_loc2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_VE4_GPIO_34] = RTK_PIN_MUX(gpio_34, 0x8, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_VE4_GPIO_35] = RTK_PIN_MUX(gpio_35, 0x8, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_VE4_GPIO_42] = RTK_PIN_MUX(gpio_42, 0x8, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 24), "sd"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_VE4_GPIO_43] = RTK_PIN_MUX(gpio_43, 0x8, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 28), "sd"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_VE4_GPIO_44] = RTK_PIN_MUX(gpio_44, 0xc, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_VE4_GPIO_51] = RTK_PIN_MUX(gpio_51, 0xc, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 4), "i2c6_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_VE4_GPIO_53] = RTK_PIN_MUX(gpio_53, 0xc, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 8), "uart3"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "ts0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 8), "gspi2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 8), "ve4_uart_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_VE4_GPIO_54] = RTK_PIN_MUX(gpio_54, 0xc, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "uart3"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 12), "ts0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 12), "gspi2"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 12), "ve4_uart_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_VE4_GPIO_55] = RTK_PIN_MUX(gpio_55, 0xc, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 16), "uart3"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "ts0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 16), "gspi2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_VE4_GPIO_56] = RTK_PIN_MUX(gpio_56, 0xc, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "uart3"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 20), "ts0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 20), "gspi2"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_VE4_GPIO_57] = RTK_PIN_MUX(gpio_57, 0xc, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 24), "uart5"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 24), "uart0_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 24), "gspi1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_VE4_GPIO_58] = RTK_PIN_MUX(gpio_58, 0xc, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 28), "uart5"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 28), "uart0_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 28), "gspi1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_VE4_GPIO_59] = RTK_PIN_MUX(gpio_59, 0x10, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "uart4"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "i2c4"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 0), "gspi1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_VE4_GPIO_60] = RTK_PIN_MUX(gpio_60, 0x10, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 4), "uart4"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 4), "i2c4"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 4), "gspi1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_VE4_GPIO_61] = RTK_PIN_MUX(gpio_61, 0x10, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "i2c6_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 8), "spdif_out"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 8), "spdif_in_optical"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_VE4_GPIO_62] = RTK_PIN_MUX(gpio_62, 0x10, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 12), "pll_test_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_VE4_GPIO_63] = RTK_PIN_MUX(gpio_63, 0x10, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "pll_test_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_VE4_GPIO_92] = RTK_PIN_MUX(gpio_92, 0x10, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "uart6"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 20), "i2c7"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 20), "ve4_uart_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_VE4_GPIO_93] = RTK_PIN_MUX(gpio_93, 0x10, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 24), "uart6"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 24), "i2c7"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 24), "ve4_uart_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 24), "pwm1_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_VE4_GPIO_132] = RTK_PIN_MUX(gpio_132, 0x10, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 28), "pwm6"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_VE4_GPIO_133] = RTK_PIN_MUX(gpio_133, 0x14, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "ts1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_VE4_GPIO_134] = RTK_PIN_MUX(gpio_134, 0x14, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 4), "ts1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_VE4_GPIO_135] = RTK_PIN_MUX(gpio_135, 0x14, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "ts1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_VE4_GPIO_136] = RTK_PIN_MUX(gpio_136, 0x14, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 12), "ts1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xd, 12), "pwm0_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_VE4_GPIO_137] = RTK_PIN_MUX(gpio_137, 0x14, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "i2c6_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_VE4_GPIO_138] = RTK_PIN_MUX(gpio_138, 0x14, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 20), "i2c6_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_VE4_GPIO_139] = RTK_PIN_MUX(gpio_139, 0x14, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_VE4_GPIO_140] = RTK_PIN_MUX(gpio_140, 0x14, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_VE4_GPIO_141] = RTK_PIN_MUX(gpio_141, 0x18, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "csi0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_VE4_GPIO_142] = RTK_PIN_MUX(gpio_142, 0x18, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_VE4_GPIO_143] = RTK_PIN_MUX(gpio_143, 0x18, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_VE4_GPIO_144] = RTK_PIN_MUX(gpio_144, 0x18, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "csi1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_VE4_GPIO_164] = RTK_PIN_MUX(gpio_164, 0x18, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 16), "etn_led_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "etn_phy_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 16), "i2c5"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_VE4_GPIO_165] = RTK_PIN_MUX(gpio_165, 0x18, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "etn_led_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 20), "etn_phy_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 20), "i2c5"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_VE4_UART_LOC] = RTK_PIN_MUX(ve4_uart_loc, 0x80, GENMASK(2, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "ve4_uart_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "ve4_uart_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 0), "ve4_uart_loc2") + ), +}; + +static const struct rtd_pin_desc rtd1625_main2_muxes[] = { + [RTD1625_MAIN2_EMMC_RST_N] = RTK_PIN_MUX(emmc_rst_n, 0x0, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "emmc"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_MAIN2_EMMC_DD_SB] = RTK_PIN_MUX(emmc_dd_sb, 0x0, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 4), "emmc"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_MAIN2_EMMC_CLK] = RTK_PIN_MUX(emmc_clk, 0x0, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "emmc"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_MAIN2_EMMC_CMD] = RTK_PIN_MUX(emmc_cmd, 0x0, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 12), "emmc"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_MAIN2_EMMC_DATA_0] = RTK_PIN_MUX(emmc_data_0, 0x0, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "emmc"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 16), "nf"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_MAIN2_EMMC_DATA_1] = RTK_PIN_MUX(emmc_data_1, 0x0, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 20), "emmc"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 20), "nf"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_MAIN2_EMMC_DATA_2] = RTK_PIN_MUX(emmc_data_2, 0x0, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 24), "emmc"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 24), "nf"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_MAIN2_EMMC_DATA_3] = RTK_PIN_MUX(emmc_data_3, 0x0, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 28), "emmc"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 28), "nf"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_MAIN2_EMMC_DATA_4] = RTK_PIN_MUX(emmc_data_4, 0x4, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "emmc"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 0), "nf"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_MAIN2_EMMC_DATA_5] = RTK_PIN_MUX(emmc_data_5, 0x4, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 4), "emmc"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 4), "nf"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_MAIN2_EMMC_DATA_6] = RTK_PIN_MUX(emmc_data_6, 0x4, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "emmc"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_MAIN2_EMMC_DATA_7] = RTK_PIN_MUX(emmc_data_7, 0x4, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 12), "emmc"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_14] = RTK_PIN_MUX(gpio_14, 0x4, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 16), "etn_led_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "etn_phy_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 16), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_15] = RTK_PIN_MUX(gpio_15, 0x4, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "etn_led_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 20), "etn_phy_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 20), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_20] = RTK_PIN_MUX(gpio_20, 0x4, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 24), "i2c1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_21] = RTK_PIN_MUX(gpio_21, 0x4, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 28), "i2c1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_MAIN2_GPIO_22] = RTK_PIN_MUX(gpio_22, 0x8, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "dbg_out1") + ), + [RTD1625_MAIN2_HIF_DATA] = RTK_PIN_MUX(hif_data, 0x8, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 4), "sd"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 4), "scpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 4), "acpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 4), "pcpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 4), "aupu0_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 4), "ve2_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 4), "hi_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 4), "hi_m"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 4), "aupu1_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_MAIN2_HIF_EN] = RTK_PIN_MUX(hif_en, 0x8, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 8), "sd"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 8), "scpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 8), "acpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 8), "pcpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 8), "aupu0_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 8), "ve2_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 8), "hi_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 8), "hi_m"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 8), "aupu1_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_MAIN2_HIF_RDY] = RTK_PIN_MUX(hif_rdy, 0x8, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 12), "sd"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 12), "scpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 12), "acpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 12), "pcpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 12), "aupu0_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 12), "ve2_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 12), "hi_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 12), "hi_m"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 12), "aupu1_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_MAIN2_HIF_CLK] = RTK_PIN_MUX(hif_clk, 0x8, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 16), "sd"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 16), "scpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 16), "acpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 16), "pcpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 16), "aupu0_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 16), "ve2_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x9, 16), "hi_loc0"), + RTK_PIN_FUNC(SHIFT_LEFT(0xa, 16), "hi_m"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 16), "aupu1_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_40] = RTK_PIN_MUX(gpio_40, 0x8, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 20), "sd"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 20), "scpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x4, 20), "acpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x5, 20), "pcpu_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x6, 20), "aupu0_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x7, 20), "ve2_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xe, 20), "aupu1_ejtag_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_41] = RTK_PIN_MUX(gpio_41, 0x8, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 24), "sd"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_64] = RTK_PIN_MUX(gpio_64, 0x8, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 28), "spi"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_MAIN2_GPIO_65] = RTK_PIN_MUX(gpio_65, 0xc, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 0), "pll_test_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 0), "spi"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_66] = RTK_PIN_MUX(gpio_66, 0xc, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x1, 4), "pll_test_loc1"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 4), "spi"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_67] = RTK_PIN_MUX(gpio_67, 0xc, GENMASK(13, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "spi"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_80] = RTK_PIN_MUX(gpio_80, 0xc, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 12), "rmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 12), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_81] = RTK_PIN_MUX(gpio_81, 0xc, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "rmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 16), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_82] = RTK_PIN_MUX(gpio_82, 0xc, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 20), "rmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 20), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_83] = RTK_PIN_MUX(gpio_83, 0xc, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 24), "rmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 24), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_84] = RTK_PIN_MUX(gpio_84, 0xc, GENMASK(31, 28), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 28), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 28), "rmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 28), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 28), "iso_tristate") + ), + + [RTD1625_MAIN2_GPIO_85] = RTK_PIN_MUX(gpio_85, 0x10, GENMASK(3, 0), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 0), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 0), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 0), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_86] = RTK_PIN_MUX(gpio_86, 0x10, GENMASK(7, 4), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 4), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 4), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 4), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_87] = RTK_PIN_MUX(gpio_87, 0x10, GENMASK(11, 8), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 8), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 8), "rmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 8), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 8), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_88] = RTK_PIN_MUX(gpio_88, 0x10, GENMASK(15, 12), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 12), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 12), "rmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 12), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 12), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_89] = RTK_PIN_MUX(gpio_89, 0x10, GENMASK(19, 16), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 16), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x2, 16), "rmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 16), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 16), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_90] = RTK_PIN_MUX(gpio_90, 0x10, GENMASK(23, 20), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 20), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 20), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 20), "iso_tristate") + ), + [RTD1625_MAIN2_GPIO_91] = RTK_PIN_MUX(gpio_91, 0x10, GENMASK(27, 24), + RTK_PIN_FUNC(SHIFT_LEFT(0x0, 24), "gpio"), + RTK_PIN_FUNC(SHIFT_LEFT(0x3, 24), "rgmii"), + RTK_PIN_FUNC(SHIFT_LEFT(0xf, 24), "iso_tristate") + ), +}; + +static const struct rtd_pin_config_desc rtd1625_iso_configs[] = { + [RTD1625_ISO_GPIO_8] = RTK_PIN_CONFIG_V2(gpio_8, 0x20, 0, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_ISO_GPIO_9] = RTK_PIN_CONFIG_V2(gpio_9, 0x20, 6, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_ISO_GPIO_10] = RTK_PIN_CONFIG_V2(gpio_10, 0x20, 12, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_11] = RTK_PIN_CONFIG_V2(gpio_11, 0x20, 18, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_45] = RTK_PIN_CONFIG(gpio_45, 0x24, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_ISO_GPIO_46] = RTK_PIN_CONFIG(gpio_46, 0x24, 13, 0, 1, NA, 2, 12, NA), + [RTD1625_ISO_GPIO_47] = RTK_PIN_CONFIG(gpio_47, 0x28, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_ISO_GPIO_48] = RTK_PIN_CONFIG(gpio_48, 0x28, 13, 0, 1, NA, 2, 12, NA), + [RTD1625_ISO_GPIO_49] = RTK_PIN_CONFIG(gpio_49, 0x2c, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_ISO_GPIO_50] = RTK_PIN_CONFIG(gpio_50, 0x2c, 13, 0, 1, NA, 2, 12, NA), + [RTD1625_ISO_GPIO_52] = RTK_PIN_CONFIG_V2(gpio_52, 0x2c, 26, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_94] = RTK_PIN_CONFIG_V2(gpio_94, 0x30, 0, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_ISO_GPIO_95] = RTK_PIN_CONFIG_V2(gpio_95, 0x30, 6, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_ISO_GPIO_96] = RTK_PIN_CONFIG_V2(gpio_96, 0x30, 12, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_ISO_GPIO_97] = RTK_PIN_CONFIG_V2(gpio_97, 0x30, 18, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_ISO_GPIO_98] = RTK_PIN_CONFIG_V2(gpio_98, 0x30, 24, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_ISO_GPIO_99] = RTK_PIN_CONFIG_V2(gpio_99, 0x34, 0, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_ISO_GPIO_100] = RTK_PIN_CONFIG_V2(gpio_100, 0x34, 6, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_101] = RTK_PIN_CONFIG_V2(gpio_101, 0x34, 12, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_102] = RTK_PIN_CONFIG_V2(gpio_102, 0x34, 18, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_103] = RTK_PIN_CONFIG_V2(gpio_103, 0x34, 24, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_104] = RTK_PIN_CONFIG_V2(gpio_104, 0x38, 0, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_105] = RTK_PIN_CONFIG_V2(gpio_105, 0x38, 6, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_106] = RTK_PIN_CONFIG_V2(gpio_106, 0x38, 12, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_107] = RTK_PIN_CONFIG_V2(gpio_107, 0x38, 18, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_108] = RTK_PIN_CONFIG_V2(gpio_108, 0x38, 24, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_109] = RTK_PIN_CONFIG_V2(gpio_109, 0x3c, 0, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_110] = RTK_PIN_CONFIG_V2(gpio_110, 0x3c, 6, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_111] = RTK_PIN_CONFIG_V2(gpio_111, 0x3c, 12, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_112] = RTK_PIN_CONFIG_V2(gpio_112, 0x3c, 18, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_128] = RTK_PIN_CONFIG_V2(gpio_128, 0x3c, 24, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_129] = RTK_PIN_CONFIG_V2(gpio_129, 0x40, 0, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_130] = RTK_PIN_CONFIG_V2(gpio_130, 0x40, 6, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_131] = RTK_PIN_CONFIG_V2(gpio_131, 0x40, 12, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_145] = RTK_PIN_CONFIG_V2(gpio_145, 0x40, 18, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_146] = RTK_PIN_CONFIG_V2(gpio_146, 0x40, 24, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_147] = RTK_PIN_CONFIG_V2(gpio_147, 0x44, 0, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_148] = RTK_PIN_CONFIG_V2(gpio_148, 0x44, 6, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_149] = RTK_PIN_CONFIG_V2(gpio_149, 0x44, 12, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_150] = RTK_PIN_CONFIG_V2(gpio_150, 0x44, 18, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_151] = RTK_PIN_CONFIG_V2(gpio_151, 0x44, 24, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_152] = RTK_PIN_CONFIG_V2(gpio_152, 0x48, 0, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_153] = RTK_PIN_CONFIG_V2(gpio_153, 0x48, 6, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_154] = RTK_PIN_CONFIG_V2(gpio_154, 0x48, 12, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_155] = RTK_PIN_CONFIG_V2(gpio_155, 0x48, 18, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_156] = RTK_PIN_CONFIG_V2(gpio_156, 0x48, 24, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_157] = RTK_PIN_CONFIG_V2(gpio_157, 0x4c, 0, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_158] = RTK_PIN_CONFIG_V2(gpio_158, 0x4c, 6, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_159] = RTK_PIN_CONFIG_V2(gpio_159, 0x4c, 12, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_160] = RTK_PIN_CONFIG_V2(gpio_160, 0x4c, 18, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_161] = RTK_PIN_CONFIG_V2(gpio_161, 0x4c, 24, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_162] = RTK_PIN_CONFIG_V2(gpio_162, 0x50, 0, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_GPIO_163] = RTK_PIN_CONFIG_V2(gpio_163, 0x50, 6, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_ISO_USB_CC1] = RTK_PIN_CONFIG_V2(usb_cc1, 0x50, 12, NA, NA, 0, 1, 2, 3, + PADDRI_4_8), + [RTD1625_ISO_USB_CC2] = RTK_PIN_CONFIG_V2(usb_cc2, 0x50, 16, NA, NA, 0, 1, 2, 3, + PADDRI_4_8), +}; + +static const struct rtd_pin_config_desc rtd1625_isom_configs[] = { + [RTD1625_ISOM_GPIO_0] = RTK_PIN_CONFIG_V2(gpio_0, 0x4, 5, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_ISOM_GPIO_1] = RTK_PIN_CONFIG_V2(gpio_1, 0x4, 11, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_ISOM_GPIO_28] = RTK_PIN_CONFIG_V2(gpio_28, 0x4, 17, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_ISOM_GPIO_29] = RTK_PIN_CONFIG_V2(gpio_29, 0x4, 23, 1, 2, 0, 3, 4, 5, PADDRI_4_8), +}; + +static const struct rtd_pin_config_desc rtd1625_ve4_configs[] = { + [RTD1625_VE4_GPIO_2] = RTK_PIN_CONFIG_V2(gpio_2, 0x1c, 0, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_3] = RTK_PIN_CONFIG_V2(gpio_3, 0x1c, 6, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_4] = RTK_PIN_CONFIG_V2(gpio_4, 0x1c, 12, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_5] = RTK_PIN_CONFIG_V2(gpio_5, 0x1c, 18, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_6] = RTK_PIN_CONFIG_V2(gpio_6, 0x1c, 24, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_7] = RTK_PIN_CONFIG_V2(gpio_7, 0x20, 0, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_12] = RTK_PIN_CONFIG_V2(gpio_12, 0x20, 6, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_13] = RTK_PIN_CONFIG_V2(gpio_13, 0x20, 18, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_16] = RTK_PIN_CONFIG_V2(gpio_16, 0x20, 18, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_17] = RTK_PIN_CONFIG_V2(gpio_17, 0x20, 24, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_18] = RTK_PIN_CONFIG_V2(gpio_18, 0x24, 0, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_19] = RTK_PIN_CONFIG_V2(gpio_19, 0x24, 6, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_23] = RTK_PIN_CONFIG_V2(gpio_23, 0x24, 12, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_24] = RTK_PIN_CONFIG_V2(gpio_24, 0x24, 18, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_25] = RTK_PIN_CONFIG_V2(gpio_25, 0x24, 24, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_30] = RTK_PIN_CONFIG_V2(gpio_30, 0x28, 0, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_31] = RTK_PIN_CONFIG_V2(gpio_31, 0x28, 6, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_32] = RTK_PIN_CONFIG_V2(gpio_32, 0x28, 12, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_33] = RTK_PIN_CONFIG_V2(gpio_33, 0x28, 18, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_34] = RTK_PIN_CONFIG_V2(gpio_34, 0x28, 24, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_35] = RTK_PIN_CONFIG_V2(gpio_35, 0x2c, 0, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_42] = RTK_PIN_CONFIG_V2(gpio_42, 0x2c, 6, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_43] = RTK_PIN_CONFIG_V2(gpio_43, 0x2c, 12, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_44] = RTK_PIN_CONFIG_V2(gpio_44, 0x2c, 18, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_51] = RTK_PIN_CONFIG_V2(gpio_51, 0x2c, 24, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_53] = RTK_PIN_CONFIG_V2(gpio_53, 0x30, 0, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_54] = RTK_PIN_CONFIG_V2(gpio_54, 0x30, 6, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_55] = RTK_PIN_CONFIG_V2(gpio_55, 0x30, 12, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_56] = RTK_PIN_CONFIG_V2(gpio_56, 0x30, 18, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_57] = RTK_PIN_CONFIG_V2(gpio_57, 0x30, 24, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_58] = RTK_PIN_CONFIG_V2(gpio_58, 0x34, 0, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_59] = RTK_PIN_CONFIG_V2(gpio_59, 0x34, 6, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_60] = RTK_PIN_CONFIG_V2(gpio_60, 0x34, 12, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_61] = RTK_PIN_CONFIG_V2(gpio_61, 0x34, 18, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_62] = RTK_PIN_CONFIG_V2(gpio_62, 0x34, 24, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_63] = RTK_PIN_CONFIG_V2(gpio_63, 0x38, 0, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_92] = RTK_PIN_CONFIG_V2(gpio_92, 0x38, 6, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_93] = RTK_PIN_CONFIG_V2(gpio_93, 0x38, 12, 1, 2, 0, 3, 4, 5, PADDRI_4_8), + [RTD1625_VE4_GPIO_132] = RTK_PIN_CONFIG_V2(gpio_132, 0x38, 18, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_VE4_GPIO_133] = RTK_PIN_CONFIG_V2(gpio_133, 0x38, 24, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_VE4_GPIO_134] = RTK_PIN_CONFIG_V2(gpio_134, 0x3c, 0, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_VE4_GPIO_135] = RTK_PIN_CONFIG_V2(gpio_135, 0x3c, 6, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_VE4_GPIO_136] = RTK_PIN_CONFIG_V2(gpio_136, 0x3c, 12, 1, 2, 0, 3, 4, 5, + PADDRI_4_8), + [RTD1625_VE4_GPIO_137] = RTK_PIN_CONFIG(gpio_137, 0x3c, 18, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_VE4_GPIO_138] = RTK_PIN_CONFIG(gpio_138, 0x3c, 21, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_VE4_GPIO_139] = RTK_PIN_CONFIG(gpio_139, 0x3c, 24, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_VE4_GPIO_140] = RTK_PIN_CONFIG(gpio_140, 0x3c, 27, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_VE4_GPIO_141] = RTK_PIN_CONFIG(gpio_141, 0x40, 0, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_VE4_GPIO_142] = RTK_PIN_CONFIG(gpio_142, 0x40, 3, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_VE4_GPIO_143] = RTK_PIN_CONFIG(gpio_143, 0x40, 6, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_VE4_GPIO_144] = RTK_PIN_CONFIG(gpio_144, 0x40, 9, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_VE4_GPIO_164] = RTK_PIN_CONFIG(gpio_164, 0x40, 12, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_VE4_GPIO_165] = RTK_PIN_CONFIG(gpio_165, 0x40, 15, 1, 2, 0, NA, NA, PADDRI_4_8), +}; + +static const struct rtd_pin_config_desc rtd1625_main2_configs[] = { + [RTD1625_MAIN2_EMMC_CLK] = RTK_PIN_CONFIG(emmc_clk, 0x14, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_EMMC_CMD] = RTK_PIN_CONFIG(emmc_cmd, 0x14, 13, 0, 1, NA, 2, 13, NA), + [RTD1625_MAIN2_EMMC_DATA_0] = RTK_PIN_CONFIG(emmc_data_0, 0x18, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_EMMC_DATA_1] = RTK_PIN_CONFIG(emmc_data_1, 0x18, 13, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_EMMC_DATA_2] = RTK_PIN_CONFIG(emmc_data_2, 0x1c, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_EMMC_DATA_3] = RTK_PIN_CONFIG(emmc_data_3, 0x1c, 13, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_EMMC_DATA_4] = RTK_PIN_CONFIG(emmc_data_4, 0x20, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_EMMC_DATA_5] = RTK_PIN_CONFIG(emmc_data_5, 0x20, 13, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_EMMC_DATA_6] = RTK_PIN_CONFIG(emmc_data_6, 0x24, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_EMMC_DATA_7] = RTK_PIN_CONFIG(emmc_data_7, 0x24, 13, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_EMMC_DD_SB] = RTK_PIN_CONFIG(emmc_dd_sb, 0x28, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_EMMC_RST_N] = RTK_PIN_CONFIG(emmc_rst_n, 0x28, 13, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_GPIO_14] = RTK_PIN_CONFIG(gpio_14, 0x28, 26, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_15] = RTK_PIN_CONFIG(gpio_15, 0x28, 29, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_20] = RTK_PIN_CONFIG_I2C(gpio_20, 0x2c, 0, 1, 2, 0, 3, 4, 5, 7, 8, + PADDRI_4_8), + [RTD1625_MAIN2_GPIO_21] = RTK_PIN_CONFIG_I2C(gpio_21, 0x2c, 9, 1, 2, 0, 3, 4, 5, 7, 8, + PADDRI_4_8), + [RTD1625_MAIN2_GPIO_22] = RTK_PIN_CONFIG_V2(gpio_22, 0x2c, 18, 1, 2, 0, 3, 7, 8, + PADDRI_4_8), + [RTD1625_MAIN2_GPIO_40] = RTK_PIN_CONFIG(gpio_40, 0x30, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_GPIO_41] = RTK_PIN_CONFIG(gpio_41, 0x30, 13, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_GPIO_64] = RTK_PIN_CONFIG(gpio_64, 0x34, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_GPIO_65] = RTK_PIN_CONFIG(gpio_65, 0x34, 13, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_GPIO_66] = RTK_PIN_CONFIG(gpio_66, 0x38, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_GPIO_67] = RTK_PIN_CONFIG(gpio_67, 0x38, 13, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_GPIO_80] = RTK_PIN_CONFIG(gpio_80, 0x38, 26, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_81] = RTK_PIN_CONFIG(gpio_81, 0x38, 29, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_82] = RTK_PIN_CONFIG(gpio_82, 0x3c, 0, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_83] = RTK_PIN_CONFIG(gpio_83, 0x3c, 3, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_84] = RTK_PIN_CONFIG(gpio_84, 0x3c, 6, 1, 2, 0, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_85] = RTK_PIN_CONFIG(gpio_85, 0x3c, 9, 1, 2, NA, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_86] = RTK_PIN_CONFIG(gpio_86, 0x3c, 12, 1, 2, NA, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_87] = RTK_PIN_CONFIG(gpio_87, 0x3c, 22, 1, 2, NA, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_88] = RTK_PIN_CONFIG(gpio_88, 0x40, 0, 1, 2, NA, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_89] = RTK_PIN_CONFIG(gpio_89, 0x40, 10, 1, 2, NA, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_90] = RTK_PIN_CONFIG(gpio_90, 0x40, 20, 1, 2, NA, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_GPIO_91] = RTK_PIN_CONFIG(gpio_91, 0x44, 0, 1, 2, NA, NA, NA, PADDRI_4_8), + [RTD1625_MAIN2_HIF_CLK] = RTK_PIN_CONFIG(hif_clk, 0x44, 10, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_HIF_DATA] = RTK_PIN_CONFIG(hif_data, 0x48, 0, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_HIF_EN] = RTK_PIN_CONFIG(hif_en, 0x48, 13, 0, 1, NA, 2, 12, NA), + [RTD1625_MAIN2_HIF_RDY] = RTK_PIN_CONFIG(hif_rdy, 0x4c, 0, 0, 1, NA, 2, 12, NA), +}; + +static const struct rtd_pin_sconfig_desc rtd1625_iso_sconfigs[] = { + RTK_PIN_SCONFIG(gpio_45, 0x24, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(gpio_46, 0x24, 16, 3, 19, 3, 22, 3), + RTK_PIN_SCONFIG(gpio_47, 0x28, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(gpio_48, 0x28, 16, 3, 19, 3, 22, 3), + RTK_PIN_SCONFIG(gpio_49, 0x2c, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(gpio_50, 0x2c, 16, 3, 19, 3, 22, 3), +}; + +static const struct rtd_pin_sconfig_desc rtd1625_main2_sconfigs[] = { + RTK_PIN_SCONFIG(emmc_clk, 0x14, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(emmc_cmd, 0x14, 16, 3, 19, 3, 22, 3), + RTK_PIN_SCONFIG(emmc_data_0, 0x18, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(emmc_data_1, 0x18, 16, 3, 19, 3, 22, 3), + RTK_PIN_SCONFIG(emmc_data_2, 0x1c, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(emmc_data_3, 0x1c, 16, 3, 19, 3, 22, 3), + RTK_PIN_SCONFIG(emmc_data_4, 0x20, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(emmc_data_5, 0x20, 16, 3, 19, 3, 22, 3), + RTK_PIN_SCONFIG(emmc_data_6, 0x24, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(emmc_data_7, 0x24, 16, 3, 19, 3, 22, 3), + RTK_PIN_SCONFIG(emmc_dd_sb, 0x28, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(emmc_rst_n, 0x28, 16, 3, 19, 3, 22, 3), + RTK_PIN_SCONFIG(gpio_40, 0x30, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(gpio_41, 0x30, 16, 3, 19, 3, 22, 3), + RTK_PIN_SCONFIG(gpio_64, 0x34, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(gpio_65, 0x34, 16, 3, 19, 3, 22, 3), + RTK_PIN_SCONFIG(gpio_66, 0x38, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(gpio_67, 0x38, 16, 3, 19, 3, 22, 3), + RTK_PIN_SCONFIG(gpio_86, 0x3c, 0, 0, 14, 4, 18, 4), + RTK_PIN_SCONFIG(gpio_87, 0x3c, 0, 0, 24, 4, 28, 4), + RTK_PIN_SCONFIG(gpio_88, 0x40, 0, 0, 2, 4, 6, 4), + RTK_PIN_SCONFIG(gpio_89, 0x40, 0, 0, 12, 4, 16, 4), + RTK_PIN_SCONFIG(gpio_90, 0x40, 0, 0, 22, 4, 26, 4), + RTK_PIN_SCONFIG(gpio_91, 0x44, 0, 0, 2, 4, 6, 4), + RTK_PIN_SCONFIG(hif_clk, 0x44, 13, 3, 16, 3, 19, 3), + RTK_PIN_SCONFIG(hif_data, 0x48, 3, 3, 6, 3, 9, 3), + RTK_PIN_SCONFIG(hif_en, 0x48, 16, 3, 19, 3, 22, 3), + RTK_PIN_SCONFIG(hif_rdy, 0x4c, 3, 3, 6, 3, 9, 3), +}; + +static const struct rtd_reg_range rtd1625_iso_reg_ranges[] = { + { .offset = 0x0, .len = 0x58 }, + { .offset = 0x120, .len = 0x10 }, + { .offset = 0x180, .len = 0xc }, + { .offset = 0x1A0, .len = 0xc }, +}; + +static const struct rtd_pin_range rtd1625_iso_pin_ranges = { + .ranges = rtd1625_iso_reg_ranges, + .num_ranges = ARRAY_SIZE(rtd1625_iso_reg_ranges), +}; + +static const struct rtd_reg_range rtd1625_isom_reg_ranges[] = { + { .offset = 0x0, .len = 0xc }, + { .offset = 0x30, .len = 0x4 }, +}; + +static const struct rtd_pin_range rtd1625_isom_pin_ranges = { + .ranges = rtd1625_isom_reg_ranges, + .num_ranges = ARRAY_SIZE(rtd1625_isom_reg_ranges), +}; + +static const struct rtd_reg_range rtd1625_ve4_reg_ranges[] = { + { .offset = 0x0, .len = 0x48 }, + { .offset = 0x80, .len = 0x4 }, +}; + +static const struct rtd_pin_range rtd1625_ve4_pin_ranges = { + .ranges = rtd1625_ve4_reg_ranges, + .num_ranges = ARRAY_SIZE(rtd1625_ve4_reg_ranges), +}; + +static const struct rtd_reg_range rtd1625_main2_reg_ranges[] = { + { .offset = 0x0, .len = 0x50 }, +}; + +static const struct rtd_pin_range rtd1625_main2_pin_ranges = { + .ranges = rtd1625_main2_reg_ranges, + .num_ranges = ARRAY_SIZE(rtd1625_main2_reg_ranges), +}; + +static const struct rtd_pinctrl_desc rtd1625_iso_pinctrl_desc = { + .pins = rtd1625_iso_pins, + .num_pins = ARRAY_SIZE(rtd1625_iso_pins), + .groups = rtd1625_iso_pin_groups, + .num_groups = ARRAY_SIZE(rtd1625_iso_pin_groups), + .functions = rtd1625_iso_pin_functions, + .num_functions = ARRAY_SIZE(rtd1625_iso_pin_functions), + .muxes = rtd1625_iso_muxes, + .num_muxes = ARRAY_SIZE(rtd1625_iso_muxes), + .configs = rtd1625_iso_configs, + .num_configs = ARRAY_SIZE(rtd1625_iso_configs), + .sconfigs = rtd1625_iso_sconfigs, + .num_sconfigs = ARRAY_SIZE(rtd1625_iso_sconfigs), + .pin_range = &rtd1625_iso_pin_ranges, +}; + +static const struct rtd_pinctrl_desc rtd1625_isom_pinctrl_desc = { + .pins = rtd1625_isom_pins, + .num_pins = ARRAY_SIZE(rtd1625_isom_pins), + .groups = rtd1625_isom_pin_groups, + .num_groups = ARRAY_SIZE(rtd1625_isom_pin_groups), + .functions = rtd1625_isom_pin_functions, + .num_functions = ARRAY_SIZE(rtd1625_isom_pin_functions), + .muxes = rtd1625_isom_muxes, + .num_muxes = ARRAY_SIZE(rtd1625_isom_muxes), + .configs = rtd1625_isom_configs, + .num_configs = ARRAY_SIZE(rtd1625_isom_configs), + .pin_range = &rtd1625_isom_pin_ranges, +}; + +static const struct rtd_pinctrl_desc rtd1625_ve4_pinctrl_desc = { + .pins = rtd1625_ve4_pins, + .num_pins = ARRAY_SIZE(rtd1625_ve4_pins), + .groups = rtd1625_ve4_pin_groups, + .num_groups = ARRAY_SIZE(rtd1625_ve4_pin_groups), + .functions = rtd1625_ve4_pin_functions, + .num_functions = ARRAY_SIZE(rtd1625_ve4_pin_functions), + .muxes = rtd1625_ve4_muxes, + .num_muxes = ARRAY_SIZE(rtd1625_ve4_muxes), + .configs = rtd1625_ve4_configs, + .num_configs = ARRAY_SIZE(rtd1625_ve4_configs), + .pin_range = &rtd1625_ve4_pin_ranges, +}; + +static const struct rtd_pinctrl_desc rtd1625_main2_pinctrl_desc = { + .pins = rtd1625_main2_pins, + .num_pins = ARRAY_SIZE(rtd1625_main2_pins), + .groups = rtd1625_main2_pin_groups, + .num_groups = ARRAY_SIZE(rtd1625_main2_pin_groups), + .functions = rtd1625_main2_pin_functions, + .num_functions = ARRAY_SIZE(rtd1625_main2_pin_functions), + .muxes = rtd1625_main2_muxes, + .num_muxes = ARRAY_SIZE(rtd1625_main2_muxes), + .configs = rtd1625_main2_configs, + .num_configs = ARRAY_SIZE(rtd1625_main2_configs), + .sconfigs = rtd1625_main2_sconfigs, + .num_sconfigs = ARRAY_SIZE(rtd1625_main2_sconfigs), + .pin_range = &rtd1625_main2_pin_ranges, +}; + +static int rtd1625_pinctrl_probe(struct platform_device *pdev) +{ + const struct rtd_pinctrl_desc *desc = device_get_match_data(&pdev->dev); + + return rtd_pinctrl_probe(pdev, desc); +} + +static const struct of_device_id rtd1625_pinctrl_of_match[] = { + {.compatible = "realtek,rtd1625-iso-pinctrl", .data = &rtd1625_iso_pinctrl_desc}, + {.compatible = "realtek,rtd1625-isom-pinctrl", .data = &rtd1625_isom_pinctrl_desc}, + {.compatible = "realtek,rtd1625-ve4-pinctrl", .data = &rtd1625_ve4_pinctrl_desc}, + {.compatible = "realtek,rtd1625-main2-pinctrl", .data = &rtd1625_main2_pinctrl_desc}, + {}, +}; +MODULE_DEVICE_TABLE(of, rtd1625_pinctrl_of_match); + +static struct platform_driver rtd1625_pinctrl_driver = { + .driver = { + .name = "rtd1625-pinctrl", + .of_match_table = rtd1625_pinctrl_of_match, + .pm = &realtek_pinctrl_pm_ops, + }, + .probe = rtd1625_pinctrl_probe, +}; + +module_platform_driver(rtd1625_pinctrl_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Realtek Semiconductor Corporation"); +MODULE_DESCRIPTION("Realtek DHC SoC RTD1625 pinctrl driver"); diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 863e779dda02..561e6018fd89 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -1475,7 +1475,6 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, arg = pinconf_to_config_argument(_configs[i]); switch (param) { case PIN_CONFIG_INPUT_ENABLE: - if (!(cfg & PIN_CFG_IEN)) return -EINVAL; @@ -3012,6 +3011,13 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen off = RZG2L_PIN_CFG_TO_PORT_OFFSET(cfg); pincnt = hweight8(FIELD_GET(PIN_CFG_PIN_MAP_MASK, cfg)); + if (cfg & RZG2L_VARIABLE_CFG) { + unsigned int pin = port * RZG2L_PINS_PER_PORT; + + for (unsigned int i = 0; i < RZG2L_PINS_PER_PORT; i++) + cfg |= *(u64 *)pctrl->desc.pins[pin + i].drv_data; + } + caps = FIELD_GET(PIN_CFG_MASK, cfg); has_iolh = !!(caps & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | PIN_CFG_IOLH_C)); has_ien = !!(caps & PIN_CFG_IEN); diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c index 5927744c7a96..4ba11a83b604 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c @@ -7,6 +7,7 @@ * Copyright (C) 2025 Renesas Electronics Corporation. */ +#include #include #include #include @@ -43,6 +44,7 @@ #define PMC(m) (0x400 + (m)) #define PFC(m) (0x600 + 8 * (m)) #define PIN(m) (0x800 + (m)) +#define DRCTL(n) (0xa00 + 8 * (n)) #define RSELP(m) (0xc00 + (m)) #define PM_MASK GENMASK(1, 0) @@ -54,6 +56,15 @@ #define PFC_PIN_MASK(pin) (PFC_MASK << ((pin) * 8)) #define PFC_FUNC_INTERRUPT 0 +#define DRCTL_DRV_PIN_MASK(pin) (GENMASK_ULL(1, 0) << ((pin) * 8)) +#define DRCTL_PUD_PIN_MASK(pin) (GENMASK_ULL(3, 2) << ((pin) * 8)) +#define DRCTL_SMT_PIN_MASK(pin) (BIT_ULL(4) << ((pin) * 8)) +#define DRCTL_SR_PIN_MASK(pin) (BIT_ULL(5) << ((pin) * 8)) + +#define DRCTL_PUD_NONE 0 +#define DRCTL_PUD_PULL_UP 1 +#define DRCTL_PUD_PULL_DOWN 2 + /* * Use 16 lower bits [15:0] for pin identifier * Use 8 higher bits [23:16] for pin mux function @@ -91,6 +102,8 @@ struct rzt2h_pinctrl { atomic_t wakeup_path; }; +static const unsigned int rzt2h_drive_strength_ua[] = { 2500, 5000, 9000, 11800 }; + #define RZT2H_GET_BASE(pctrl, port) \ ((port) > RZT2H_MAX_SAFETY_PORTS ? (pctrl)->base0 : (pctrl)->base1) @@ -110,6 +123,37 @@ RZT2H_PINCTRL_REG_ACCESS(b, u8) RZT2H_PINCTRL_REG_ACCESS(w, u16) RZT2H_PINCTRL_REG_ACCESS(q, u64) +static int rzt2h_drive_strength_ua_to_idx(unsigned int ua) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rzt2h_drive_strength_ua); i++) { + if (rzt2h_drive_strength_ua[i] == ua) + return i; + } + + return -EINVAL; +} + +static int rzt2h_drive_strength_idx_to_ua(unsigned int idx) +{ + if (idx >= ARRAY_SIZE(rzt2h_drive_strength_ua)) + return -EINVAL; + + return rzt2h_drive_strength_ua[idx]; +} + +static void rzt2h_pinctrl_drctl_rmwq(struct rzt2h_pinctrl *pctrl, + u32 port, u64 mask, u64 val) +{ + u32 offset = DRCTL(port); + u64 drctl; + + guard(raw_spinlock_irqsave)(&pctrl->lock); + drctl = rzt2h_pinctrl_readq(pctrl, port, offset) & ~mask; + rzt2h_pinctrl_writeq(pctrl, port, drctl | val, offset); +} + static int rzt2h_validate_pin(struct rzt2h_pinctrl *pctrl, unsigned int offset) { u8 port = RZT2H_PIN_ID_TO_PORT(offset); @@ -443,6 +487,210 @@ done: return ret; } +static int rzt2h_pinctrl_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, + unsigned long *config) +{ + struct rzt2h_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + u32 port, param = pinconf_to_config_param(*config); + unsigned int arg; + u8 port_pin; + u64 drctl; + int ret; + + ret = rzt2h_validate_pin(pctrl, pin); + if (ret) + return ret; + + port = RZT2H_PIN_ID_TO_PORT(pin); + port_pin = RZT2H_PIN_ID_TO_PIN(pin); + + switch (param) { + case PIN_CONFIG_SLEW_RATE: + drctl = rzt2h_pinctrl_readq(pctrl, port, DRCTL(port)); + arg = field_get(DRCTL_SR_PIN_MASK(port_pin), drctl); + break; + + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + drctl = rzt2h_pinctrl_readq(pctrl, port, DRCTL(port)); + arg = field_get(DRCTL_PUD_PIN_MASK(port_pin), drctl); + /* for PIN_CONFIG_BIAS_PULL_UP/DOWN when enabled we just return 1 */ + switch (arg) { + case DRCTL_PUD_NONE: + if (param != PIN_CONFIG_BIAS_DISABLE) + return -EINVAL; + break; + case DRCTL_PUD_PULL_UP: + if (param != PIN_CONFIG_BIAS_PULL_UP) + return -EINVAL; + arg = 1; + break; + case DRCTL_PUD_PULL_DOWN: + if (param != PIN_CONFIG_BIAS_PULL_DOWN) + return -EINVAL; + arg = 1; + break; + default: + return -EINVAL; + } + break; + + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + drctl = rzt2h_pinctrl_readq(pctrl, port, DRCTL(port)); + arg = field_get(DRCTL_SMT_PIN_MASK(port_pin), drctl); + if (!arg) + return -EINVAL; + break; + + case PIN_CONFIG_DRIVE_STRENGTH_UA: { + int idx_drv; + + drctl = rzt2h_pinctrl_readq(pctrl, port, DRCTL(port)); + arg = field_get(DRCTL_DRV_PIN_MASK(port_pin), drctl); + idx_drv = rzt2h_drive_strength_idx_to_ua(arg); + if (idx_drv < 0) + return idx_drv; + arg = idx_drv; + break; + } + + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + return 0; +} + +static int rzt2h_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int pin, + unsigned long *configs, + unsigned int num_configs) +{ + struct rzt2h_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + unsigned int i; + u8 port_pin; + int ret; + + ret = rzt2h_validate_pin(pctrl, pin); + if (ret) + return ret; + + port_pin = RZT2H_PIN_ID_TO_PIN(pin); + + for (i = 0; i < num_configs; i++) { + u32 arg = pinconf_to_config_argument(configs[i]); + u32 param = pinconf_to_config_param(configs[i]); + u64 mask, val; + + switch (param) { + case PIN_CONFIG_SLEW_RATE: + mask = DRCTL_SR_PIN_MASK(port_pin); + val = field_prep(mask, !!arg); + break; + + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: { + u32 bias; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + bias = DRCTL_PUD_NONE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + bias = DRCTL_PUD_PULL_UP; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + bias = DRCTL_PUD_PULL_DOWN; + break; + } + + mask = DRCTL_PUD_PIN_MASK(port_pin); + val = field_prep(mask, bias); + break; + } + + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + mask = DRCTL_SMT_PIN_MASK(port_pin); + val = field_prep(mask, !!arg); + break; + + case PIN_CONFIG_DRIVE_STRENGTH_UA: { + int drv_idx; + + drv_idx = rzt2h_drive_strength_ua_to_idx(arg); + if (drv_idx < 0) + return drv_idx; + + mask = DRCTL_DRV_PIN_MASK(port_pin); + val = field_prep(mask, drv_idx); + break; + } + + default: + return -ENOTSUPP; + } + + rzt2h_pinctrl_drctl_rmwq(pctrl, RZT2H_PIN_ID_TO_PORT(pin), mask, val); + } + + return 0; +} + +static int rzt2h_pinctrl_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned int group, + unsigned long *config) +{ + unsigned long prev_config = 0; + const unsigned int *pins; + unsigned int i, npins; + int ret; + + ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); + if (ret) + return ret; + + for (i = 0; i < npins; i++) { + ret = rzt2h_pinctrl_pinconf_get(pctldev, pins[i], config); + if (ret) + return ret; + + /* Check config matches previous pins */ + if (i && prev_config != *config) + return -ENOTSUPP; + + prev_config = *config; + } + + return 0; +} + +static int rzt2h_pinctrl_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned int group, + unsigned long *configs, + unsigned int num_configs) +{ + const unsigned int *pins; + unsigned int i, npins; + int ret; + + ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); + if (ret) + return ret; + + for (i = 0; i < npins; i++) { + ret = rzt2h_pinctrl_pinconf_set(pctldev, pins[i], configs, + num_configs); + if (ret) + return ret; + } + + return 0; +} + static const struct pinctrl_ops rzt2h_pinctrl_pctlops = { .get_groups_count = pinctrl_generic_get_group_count, .get_group_name = pinctrl_generic_get_group_name, @@ -459,6 +707,15 @@ static const struct pinmux_ops rzt2h_pinctrl_pmxops = { .strict = true, }; +static const struct pinconf_ops rzt2h_pinctrl_confops = { + .is_generic = true, + .pin_config_get = rzt2h_pinctrl_pinconf_get, + .pin_config_set = rzt2h_pinctrl_pinconf_set, + .pin_config_group_set = rzt2h_pinctrl_pinconf_group_set, + .pin_config_group_get = rzt2h_pinctrl_pinconf_group_get, + .pin_config_config_dbg_show = pinconf_generic_dump_config, +}; + static int rzt2h_gpio_request(struct gpio_chip *chip, unsigned int offset) { struct rzt2h_pinctrl *pctrl = gpiochip_get_data(chip); @@ -890,6 +1147,7 @@ static int rzt2h_pinctrl_register(struct rzt2h_pinctrl *pctrl) desc->npins = pctrl->data->n_port_pins; desc->pctlops = &rzt2h_pinctrl_pctlops; desc->pmxops = &rzt2h_pinctrl_pmxops; + desc->confops = &rzt2h_pinctrl_confops; desc->owner = THIS_MODULE; pins = devm_kcalloc(dev, desc->npins, sizeof(*pins), GFP_KERNEL); diff --git a/drivers/pinctrl/sophgo/pinctrl-sg2042.c b/drivers/pinctrl/sophgo/pinctrl-sg2042.c index 185305ac897d..8dba12e122a4 100644 --- a/drivers/pinctrl/sophgo/pinctrl-sg2042.c +++ b/drivers/pinctrl/sophgo/pinctrl-sg2042.c @@ -651,5 +651,5 @@ static struct platform_driver sg2042_pinctrl_driver = { }; module_platform_driver(sg2042_pinctrl_driver); -MODULE_DESCRIPTION("Pinctrl driver for the SG2002 series SoC"); +MODULE_DESCRIPTION("Pinctrl driver for the SG2042 series SoC"); MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/sophgo/pinctrl-sg2044.c b/drivers/pinctrl/sophgo/pinctrl-sg2044.c index b0c46d8954ca..cf0b674c038f 100644 --- a/drivers/pinctrl/sophgo/pinctrl-sg2044.c +++ b/drivers/pinctrl/sophgo/pinctrl-sg2044.c @@ -714,5 +714,5 @@ static struct platform_driver sg2044_pinctrl_driver = { }; module_platform_driver(sg2044_pinctrl_driver); -MODULE_DESCRIPTION("Pinctrl driver for the SG2002 series SoC"); +MODULE_DESCRIPTION("Pinctrl driver for the SG2044 series SoC"); MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c b/drivers/pinctrl/spacemit/pinctrl-k1.c index 62cab6f6cd0a..b0be62b1c816 100644 --- a/drivers/pinctrl/spacemit/pinctrl-k1.c +++ b/drivers/pinctrl/spacemit/pinctrl-k1.c @@ -674,7 +674,7 @@ static int spacemit_pinconf_get(struct pinctrl_dev *pctldev, arg = 0; break; default: - return -EINVAL; + return -ENOTSUPP; } *config = pinconf_to_config_packed(param, arg); @@ -740,7 +740,7 @@ static int spacemit_pinconf_generate_config(struct spacemit_pinctrl *pctrl, } break; default: - return -EINVAL; + return -ENOTSUPP; } } @@ -814,10 +814,12 @@ static int spacemit_pinconf_set(struct pinctrl_dev *pctldev, struct spacemit_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); const struct spacemit_pin *spin = spacemit_get_pin(pctrl, pin); u32 value; + int ret; - if (spacemit_pinconf_generate_config(pctrl, spin, pctrl->data->dconf, - configs, num_configs, &value)) - return -EINVAL; + ret = spacemit_pinconf_generate_config(pctrl, spin, pctrl->data->dconf, + configs, num_configs, &value); + if (ret) + return ret; return spacemit_pin_set_config(pctrl, pin, value); } @@ -831,16 +833,17 @@ static int spacemit_pinconf_group_set(struct pinctrl_dev *pctldev, const struct spacemit_pin *spin; const struct group_desc *group; u32 value; - int i; + int i, ret; group = pinctrl_generic_get_group(pctldev, gsel); if (!group) return -EINVAL; spin = spacemit_get_pin(pctrl, group->grp.pins[0]); - if (spacemit_pinconf_generate_config(pctrl, spin, pctrl->data->dconf, - configs, num_configs, &value)) - return -EINVAL; + ret = spacemit_pinconf_generate_config(pctrl, spin, pctrl->data->dconf, + configs, num_configs, &value); + if (ret) + return ret; for (i = 0; i < group->grp.npins; i++) spacemit_pin_set_config(pctrl, group->grp.pins[i], value); diff --git a/drivers/pinctrl/starfive/Kconfig b/drivers/pinctrl/starfive/Kconfig index 8192ac2087fc..a9a7cb101684 100644 --- a/drivers/pinctrl/starfive/Kconfig +++ b/drivers/pinctrl/starfive/Kconfig @@ -9,7 +9,6 @@ config PINCTRL_STARFIVE_JH7100 select GENERIC_PINCONF select GPIOLIB select GPIOLIB_IRQCHIP - select OF_GPIO default SOC_STARFIVE help Say yes here to support pin control on the StarFive JH7100 SoC. @@ -24,7 +23,6 @@ config PINCTRL_STARFIVE_JH7110 select GENERIC_PINCONF select GPIOLIB select GPIOLIB_IRQCHIP - select OF_GPIO config PINCTRL_STARFIVE_JH7110_SYS tristate "System pinctrl and GPIO driver for the StarFive JH7110 SoC" diff --git a/drivers/pinctrl/stm32/pinctrl-stm32-hdp.c b/drivers/pinctrl/stm32/pinctrl-stm32-hdp.c index 0b1dff01e04c..cce477e86ef9 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32-hdp.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32-hdp.c @@ -4,6 +4,7 @@ * Author: Clément Le Goffic for STMicroelectronics. */ #include +#include #include #include #include @@ -46,9 +47,11 @@ struct stm32_hdp { void __iomem *base; struct clk *clk; struct pinctrl_dev *pctl_dev; + struct stm32_firewall *firewall; struct gpio_generic_chip gpio_chip; u32 mux_conf; u32 gposet_conf; + int nb_firewall_entries; const char * const *func_name; }; @@ -615,6 +618,13 @@ static int stm32_hdp_probe(struct platform_device *pdev) return -ENOMEM; hdp->dev = dev; + if (IS_ENABLED(CONFIG_STM32_FIREWALL)) { + err = stm32_firewall_get_grant_all_access(dev, &hdp->firewall, + &hdp->nb_firewall_entries); + if (err) + return err; + } + platform_set_drvdata(pdev, hdp); hdp->base = devm_platform_ioremap_resource(pdev, 0); @@ -670,8 +680,12 @@ static int stm32_hdp_probe(struct platform_device *pdev) static void stm32_hdp_remove(struct platform_device *pdev) { struct stm32_hdp *hdp = platform_get_drvdata(pdev); + int i; writel_relaxed(HDP_CTRL_DISABLE, hdp->base + HDP_CTRL); + + for (i = 0; i < hdp->nb_firewall_entries; i++) + stm32_firewall_release_access(&hdp->firewall[i]); } static int stm32_hdp_suspend(struct device *dev) diff --git a/drivers/pinctrl/sunplus/Kconfig b/drivers/pinctrl/sunplus/Kconfig index 4b5c47c193d9..69f82590f6d2 100644 --- a/drivers/pinctrl/sunplus/Kconfig +++ b/drivers/pinctrl/sunplus/Kconfig @@ -13,7 +13,6 @@ config PINCTRL_SPPCTL select PINCONF select PINMUX select GPIOLIB - select OF_GPIO help Say Y here to support Sunplus SP7021 pinmux controller. This driver requires the pinctrl framework. diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c index 11ecbd6a9b2a..bac2adeb5c63 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -832,18 +832,14 @@ int tegra_pinctrl_probe(struct platform_device *pdev, int fn, gn, gfn; unsigned long backup_regs_size = 0; - pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); + pmx = devm_kzalloc(&pdev->dev, + struct_size(pmx, pingroup_configs, soc_data->ngroups), GFP_KERNEL); if (!pmx) return -ENOMEM; pmx->dev = &pdev->dev; pmx->soc = soc_data; - - pmx->pingroup_configs = devm_kcalloc(&pdev->dev, - pmx->soc->ngroups, sizeof(*pmx->pingroup_configs), - GFP_KERNEL); - if (!pmx->pingroup_configs) - return -ENOMEM; + pmx->num_pingroup_configs = soc_data->ngroups; /* * Each mux group will appear in 4 functions' list of groups. diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.h b/drivers/pinctrl/tegra/pinctrl-tegra.h index bc7b70913b89..deb589cda31e 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.h +++ b/drivers/pinctrl/tegra/pinctrl-tegra.h @@ -25,8 +25,8 @@ struct tegra_pmx { int nbanks; void __iomem **regs; u32 *backup_regs; - /* Array of size soc->ngroups */ - struct tegra_pingroup_config *pingroup_configs; + unsigned int num_pingroup_configs; + struct tegra_pingroup_config pingroup_configs[] __counted_by(num_pingroup_configs); }; enum tegra_pinconf_param { diff --git a/drivers/platform/chrome/cros_typec_altmode.c b/drivers/platform/chrome/cros_typec_altmode.c index 557340b53af0..66c546bf89b5 100644 --- a/drivers/platform/chrome/cros_typec_altmode.c +++ b/drivers/platform/chrome/cros_typec_altmode.c @@ -359,6 +359,7 @@ cros_typec_register_thunderbolt(struct cros_typec_port *port, } INIT_WORK(&adata->work, cros_typec_altmode_work); + mutex_init(&adata->lock); adata->alt = alt; adata->port = port; adata->ap_mode_entry = true; diff --git a/drivers/platform/mellanox/nvsw-sn2201.c b/drivers/platform/mellanox/nvsw-sn2201.c index 51504113c17e..92b58ba8f97b 100644 --- a/drivers/platform/mellanox/nvsw-sn2201.c +++ b/drivers/platform/mellanox/nvsw-sn2201.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/platform/surface/surface_hotplug.c b/drivers/platform/surface/surface_hotplug.c index c0d83ed5a208..33a8a9d41900 100644 --- a/drivers/platform/surface/surface_hotplug.c +++ b/drivers/platform/surface/surface_hotplug.c @@ -14,7 +14,7 @@ */ #include -#include +#include #include #include #include diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c index 9bd39f09c7db..0293bc517b54 100644 --- a/drivers/platform/surface/surfacepro3_button.c +++ b/drivers/platform/surface/surfacepro3_button.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #define SURFACE_PRO3_BUTTON_HID "MSHW0028" @@ -72,9 +73,10 @@ struct surface_button { bool suspended; }; -static void surface_button_notify(struct acpi_device *device, u32 event) +static void surface_button_notify(acpi_handle handle, u32 event, void *data) { - struct surface_button *button = acpi_driver_data(device); + struct device *dev = data; + struct surface_button *button = dev_get_drvdata(dev); struct input_dev *input; int key_code = KEY_RESERVED; bool pressed = false; @@ -109,18 +111,17 @@ static void surface_button_notify(struct acpi_device *device, u32 event) key_code = KEY_VOLUMEDOWN; break; case SURFACE_BUTTON_NOTIFY_TABLET_MODE: - dev_warn_once(&device->dev, "Tablet mode is not supported\n"); + dev_warn_once(dev, "Tablet mode is not supported\n"); break; default: - dev_info_ratelimited(&device->dev, - "Unsupported event [0x%x]\n", event); + dev_info_ratelimited(dev, "Unsupported event [0x%x]\n", event); break; } input = button->input; if (key_code == KEY_RESERVED) return; if (pressed) - pm_wakeup_dev_event(&device->dev, 0, button->suspended); + pm_wakeup_dev_event(dev, 0, button->suspended); if (button->suspended) return; input_report_key(input, key_code, pressed?1:0); @@ -130,8 +131,7 @@ static void surface_button_notify(struct acpi_device *device, u32 event) #ifdef CONFIG_PM_SLEEP static int surface_button_suspend(struct device *dev) { - struct acpi_device *device = to_acpi_device(dev); - struct surface_button *button = acpi_driver_data(device); + struct surface_button *button = dev_get_drvdata(dev); button->suspended = true; return 0; @@ -139,8 +139,7 @@ static int surface_button_suspend(struct device *dev) static int surface_button_resume(struct device *dev) { - struct acpi_device *device = to_acpi_device(dev); - struct surface_button *button = acpi_driver_data(device); + struct surface_button *button = dev_get_drvdata(dev); button->suspended = false; return 0; @@ -155,9 +154,8 @@ static int surface_button_resume(struct device *dev) * Returns true if the driver should bind to this device, i.e. the device is * either MSWH0028 (Pro 3) or MSHW0040 on a Pro 4 or Book 1. */ -static bool surface_button_check_MSHW0040(struct acpi_device *dev) +static bool surface_button_check_MSHW0040(struct device *dev, acpi_handle handle) { - acpi_handle handle = dev->handle; union acpi_object *result; u64 oem_platform_rev = 0; // valid revisions are nonzero @@ -179,14 +177,15 @@ static bool surface_button_check_MSHW0040(struct acpi_device *dev) ACPI_FREE(result); } - dev_dbg(&dev->dev, "OEM Platform Revision %llu\n", oem_platform_rev); + dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); return oem_platform_rev == 0; } -static int surface_button_add(struct acpi_device *device) +static int surface_button_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct surface_button *button; struct input_dev *input; const char *hid = acpi_device_hid(device); @@ -196,14 +195,14 @@ static int surface_button_add(struct acpi_device *device) strlen(SURFACE_BUTTON_OBJ_NAME))) return -ENODEV; - if (!surface_button_check_MSHW0040(device)) + if (!surface_button_check_MSHW0040(&pdev->dev, device->handle)) return -ENODEV; button = kzalloc_obj(struct surface_button); if (!button) return -ENOMEM; - device->driver_data = button; + platform_set_drvdata(pdev, button); button->input = input = input_allocate_device(); if (!input) { error = -ENOMEM; @@ -216,7 +215,7 @@ static int surface_button_add(struct acpi_device *device) input->name = acpi_device_name(device); input->phys = button->phys; input->id.bustype = BUS_HOST; - input->dev.parent = &device->dev; + input->dev.parent = &pdev->dev; input_set_capability(input, EV_KEY, KEY_POWER); input_set_capability(input, EV_KEY, KEY_LEFTMETA); input_set_capability(input, EV_KEY, KEY_VOLUMEUP); @@ -226,8 +225,17 @@ static int surface_button_add(struct acpi_device *device) if (error) goto err_free_input; - device_init_wakeup(&device->dev, true); - dev_info(&device->dev, "%s [%s]\n", acpi_device_name(device), + device_init_wakeup(&pdev->dev, true); + + error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + surface_button_notify, &pdev->dev); + if (error) { + device_init_wakeup(&pdev->dev, false); + input_unregister_device(input); + goto err_free_button; + } + + dev_info(&pdev->dev, "%s [%s]\n", acpi_device_name(device), acpi_device_bid(device)); return 0; @@ -238,10 +246,13 @@ static int surface_button_add(struct acpi_device *device) return error; } -static void surface_button_remove(struct acpi_device *device) +static void surface_button_remove(struct platform_device *pdev) { - struct surface_button *button = acpi_driver_data(device); + struct surface_button *button = platform_get_drvdata(pdev); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, surface_button_notify); + device_init_wakeup(&pdev->dev, false); input_unregister_device(button->input); kfree(button); } @@ -249,16 +260,14 @@ static void surface_button_remove(struct acpi_device *device) static SIMPLE_DEV_PM_OPS(surface_button_pm, surface_button_suspend, surface_button_resume); -static struct acpi_driver surface_button_driver = { - .name = "surface_pro3_button", - .class = "SurfacePro3", - .ids = surface_button_device_ids, - .ops = { - .add = surface_button_add, - .remove = surface_button_remove, - .notify = surface_button_notify, +static struct platform_driver surface_button_driver = { + .probe = surface_button_probe, + .remove = surface_button_remove, + .driver = { + .name = "surface_pro3_button", + .acpi_match_table = surface_button_device_ids, + .pm = &surface_button_pm, }, - .drv.pm = &surface_button_pm, }; -module_acpi_driver(surface_button_driver); +module_platform_driver(surface_button_driver); diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c index 750e3619724e..5a2ffcbab6af 100644 --- a/drivers/platform/wmi/core.c +++ b/drivers/platform/wmi/core.c @@ -364,20 +364,23 @@ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 met EXPORT_SYMBOL_GPL(wmidev_evaluate_method); /** - * wmidev_invoke_method - Invoke a WMI method + * wmidev_invoke_method - Invoke a WMI method that returns values * @wdev: A wmi bus device from a driver * @instance: Instance index * @method_id: Method ID to call * @in: Mandatory WMI buffer containing input for the method call - * @out: Optional WMI buffer to return the method results + * @out: Mandatory WMI buffer to return the method results + * @min_size: Minimum size of the method result data in bytes * - * Invoke a WMI method, the caller must free the resulting data inside @out. - * Said data is guaranteed to be aligned on a 8-byte boundary. + * Invoke a WMI method that returns values, the caller must free the resulting + * data inside @out using kfree(). Said data is guaranteed to be aligned on a + * 8-byte boundary. Use wmidev_invoke_procedure() for WMI methods that + * return no values. * * Return: 0 on success or negative error code on failure. */ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, - const struct wmi_buffer *in, struct wmi_buffer *out) + const struct wmi_buffer *in, struct wmi_buffer *out, size_t min_size) { struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); struct acpi_buffer aout = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -398,10 +401,7 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, ain.pointer = in->data; } - if (out) - status = wmidev_evaluate_method(wdev, instance, method_id, &ain, &aout); - else - status = wmidev_evaluate_method(wdev, instance, method_id, &ain, NULL); + status = wmidev_evaluate_method(wdev, instance, method_id, &ain, &aout); if (wblock->gblock.flags & ACPI_WMI_STRING) kfree(ain.pointer); @@ -409,24 +409,68 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, if (ACPI_FAILURE(status)) return -EIO; - if (!out) - return 0; - obj = aout.pointer; if (!obj) { + if (min_size != 0) + return -ENOMSG; + out->length = 0; out->data = ZERO_SIZE_PTR; return 0; } - ret = wmi_unmarshal_acpi_object(obj, out); + ret = wmi_unmarshal_acpi_object(obj, out, min_size); kfree(obj); return ret; } EXPORT_SYMBOL_GPL(wmidev_invoke_method); +/** + * wmidev_invoke_procedure - Invoke a WMI method that does not return values + * @wdev: A wmi bus device from a driver + * @instance: Instance index + * @method_id: Method ID to call + * @in: Mandatory WMI buffer containing input for the method call + * + * Invoke a WMI method that does not return any values. Use wmidev_invoke_method() + * for WMI methods that do return values. + * + * Return: 0 on success or negative error code on failure. + */ +int wmidev_invoke_procedure(struct wmi_device *wdev, u8 instance, u32 method_id, + const struct wmi_buffer *in) +{ + struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); + struct acpi_buffer ain; + acpi_status status; + int ret; + + if (wblock->gblock.flags & ACPI_WMI_STRING) { + ret = wmi_marshal_string(in, &ain); + if (ret < 0) + return ret; + } else { + if (in->length > U32_MAX) + return -E2BIG; + + ain.length = in->length; + ain.pointer = in->data; + } + + status = wmidev_evaluate_method(wdev, instance, method_id, &ain, NULL); + + if (wblock->gblock.flags & ACPI_WMI_STRING) + kfree(ain.pointer); + + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(wmidev_invoke_procedure); + static acpi_status __query_block(struct wmi_block *wblock, u8 instance, struct acpi_buffer *out) { @@ -524,13 +568,15 @@ EXPORT_SYMBOL_GPL(wmidev_block_query); * @wdev: A wmi bus device from a driver * @instance: Instance index * @out: WMI buffer to fill + * @min_size: Minimum size of the result data in bytes * - * Query a WMI data block, the caller must free the resulting data inside @out. - * Said data is guaranteed to be aligned on a 8-byte boundary. + * Query a WMI data block, the caller must free the resulting data inside @out + * using kfree(). Said data is guaranteed to be aligned on a 8-byte boundary. * * Return: 0 on success or a negative error code on failure. */ -int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out) +int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out, + size_t min_size) { union acpi_object *obj; int ret; @@ -539,7 +585,7 @@ int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer * if (!obj) return -EIO; - ret = wmi_unmarshal_acpi_object(obj, out); + ret = wmi_unmarshal_acpi_object(obj, out, min_size); kfree(obj); return ret; @@ -970,7 +1016,7 @@ static int wmi_dev_probe(struct device *dev) } if (wdriver->notify || wdriver->notify_new) { - if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && !wdriver->no_notify_data) + if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && wdriver->min_event_size) return -ENODEV; } @@ -1329,10 +1375,14 @@ static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) { struct wmi_driver *driver = to_wmi_driver(wblock->dev.dev.driver); + struct wmi_buffer dummy = { + .length = 0, + .data = ZERO_SIZE_PTR, + }; struct wmi_buffer buffer; int ret; - if (!obj && !driver->no_notify_data) { + if (!obj && driver->min_event_size) { dev_warn(&wblock->dev.dev, "Event contains no event data\n"); return; } @@ -1342,11 +1392,11 @@ static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) if (driver->notify_new) { if (!obj) { - driver->notify_new(&wblock->dev, NULL); + driver->notify_new(&wblock->dev, &dummy); return; } - ret = wmi_unmarshal_acpi_object(obj, &buffer); + ret = wmi_unmarshal_acpi_object(obj, &buffer, driver->min_event_size); if (ret < 0) { dev_warn(&wblock->dev.dev, "Failed to unmarshal event data: %d\n", ret); return; diff --git a/drivers/platform/wmi/internal.h b/drivers/platform/wmi/internal.h index 9a39ffa31ad1..c02908694563 100644 --- a/drivers/platform/wmi/internal.h +++ b/drivers/platform/wmi/internal.h @@ -11,7 +11,8 @@ union acpi_object; struct wmi_buffer; -int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer); +int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer, + size_t min_size); int wmi_marshal_string(const struct wmi_buffer *buffer, struct acpi_buffer *out); #endif /* _WMI_INTERNAL_H_ */ diff --git a/drivers/platform/wmi/marshalling.c b/drivers/platform/wmi/marshalling.c index 63a92c4ebab5..87091832568e 100644 --- a/drivers/platform/wmi/marshalling.c +++ b/drivers/platform/wmi/marshalling.c @@ -151,7 +151,8 @@ static int wmi_obj_transform(const union acpi_object *obj, u8 *buffer) return 0; } -int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer) +int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer, + size_t min_size) { size_t length, alloc_length; u8 *data; @@ -161,6 +162,9 @@ int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *b if (ret < 0) return ret; + if (length < min_size) + return -ENODATA; + if (ARCH_KMALLOC_MINALIGN < 8) { /* * kmalloc() guarantees that the alignment of the resulting memory allocation is at diff --git a/drivers/platform/wmi/tests/marshalling_kunit.c b/drivers/platform/wmi/tests/marshalling_kunit.c index 0c7cd8774aa3..471963076d58 100644 --- a/drivers/platform/wmi/tests/marshalling_kunit.c +++ b/drivers/platform/wmi/tests/marshalling_kunit.c @@ -372,7 +372,7 @@ static void wmi_unmarshal_acpi_object_test(struct kunit *test) struct wmi_buffer result; int ret; - ret = wmi_unmarshal_acpi_object(¶m->obj, &result); + ret = wmi_unmarshal_acpi_object(¶m->obj, &result, param->buffer.length); if (ret < 0) KUNIT_FAIL_AND_ABORT(test, "Unmarshalling of ACPI object failed\n"); @@ -389,7 +389,7 @@ static void wmi_unmarshal_acpi_object_failure_test(struct kunit *test) struct wmi_buffer result; int ret; - ret = wmi_unmarshal_acpi_object(¶m->obj, &result); + ret = wmi_unmarshal_acpi_object(¶m->obj, &result, 0); if (ret < 0) return; @@ -427,6 +427,25 @@ static void wmi_marshal_string_failure_test(struct kunit *test) KUNIT_FAIL(test, "Invalid string was not rejected\n"); } +static void wmi_unmarshal_acpi_object_undersized_test(struct kunit *test) +{ + const union acpi_object obj = { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = 0xdeadbeef, + }, + }; + struct wmi_buffer result; + int ret; + + ret = wmi_unmarshal_acpi_object(&obj, &result, sizeof(expected_single_integer) + 1); + if (ret < 0) + return; + + kfree(result.data); + KUNIT_FAIL(test, "Undersized unmarshalling result was not rejected\n"); +} + static struct kunit_case wmi_marshalling_test_cases[] = { KUNIT_CASE_PARAM(wmi_unmarshal_acpi_object_test, wmi_unmarshal_acpi_object_gen_params), @@ -436,6 +455,7 @@ static struct kunit_case wmi_marshalling_test_cases[] = { wmi_unmarshal_acpi_object_failure_gen_params), KUNIT_CASE_PARAM(wmi_marshal_string_failure_test, wmi_marshal_string_failure_gen_params), + KUNIT_CASE(wmi_unmarshal_acpi_object_undersized_test), {} }; diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 4cb7d97a9fcc..2ffa4ecf65b0 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -113,6 +113,24 @@ config GIGABYTE_WMI To compile this driver as a module, choose M here: the module will be called gigabyte-wmi. +config BITLAND_MIFS_WMI + tristate "Bitland MIFS (MiInterface) WMI driver" + depends on ACPI_WMI + depends on HWMON + depends on INPUT + depends on POWER_SUPPLY + select ACPI_PLATFORM_PROFILE + select INPUT_SPARSEKMAP + help + This is a driver for Bitland MiInterface based laptops. + + It provides the access to the temperature, fan speed, gpu + control, keyboard backlight brightness and platform profile + via hwmon and sysfs. + + To compile this driver as a module, choose M here: the module will + be called bitland-mifs-wmi. + config ACERHDF tristate "Acer Aspire One temperature and fan driver" depends on ACPI_EC && THERMAL diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index d25762f7114f..872ac3842391 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o obj-$(CONFIG_REDMI_WMI) += redmi-wmi.o obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o +obj-$(CONFIG_BITLAND_MIFS_WMI) += bitland-mifs-wmi.o # Acer obj-$(CONFIG_ACERHDF) += acerhdf.o diff --git a/drivers/platform/x86/acer-wireless.c b/drivers/platform/x86/acer-wireless.c index 1b5d935d085a..f464b13a58af 100644 --- a/drivers/platform/x86/acer-wireless.c +++ b/drivers/platform/x86/acer-wireless.c @@ -10,6 +10,7 @@ #include #include #include +#include #include static const struct acpi_device_id acer_wireless_acpi_ids[] = { @@ -18,13 +19,14 @@ static const struct acpi_device_id acer_wireless_acpi_ids[] = { }; MODULE_DEVICE_TABLE(acpi, acer_wireless_acpi_ids); -static void acer_wireless_notify(struct acpi_device *adev, u32 event) +static void acer_wireless_notify(acpi_handle handle, u32 event, void *data) { - struct input_dev *idev = acpi_driver_data(adev); + struct device *dev = data; + struct input_dev *idev = dev_get_drvdata(dev); - dev_dbg(&adev->dev, "event=%#x\n", event); + dev_dbg(dev, "event=%#x\n", event); if (event != 0x80) { - dev_notice(&adev->dev, "Unknown SMKB event: %#x\n", event); + dev_notice(dev, "Unknown SMKB event: %#x\n", event); return; } input_report_key(idev, KEY_RFKILL, 1); @@ -33,15 +35,16 @@ static void acer_wireless_notify(struct acpi_device *adev, u32 event) input_sync(idev); } -static int acer_wireless_add(struct acpi_device *adev) +static int acer_wireless_probe(struct platform_device *pdev) { struct input_dev *idev; + int ret; - idev = devm_input_allocate_device(&adev->dev); + idev = devm_input_allocate_device(&pdev->dev); if (!idev) return -ENOMEM; - adev->driver_data = idev; + platform_set_drvdata(pdev, idev); idev->name = "Acer Wireless Radio Control"; idev->phys = "acer-wireless/input0"; idev->id.bustype = BUS_HOST; @@ -50,19 +53,32 @@ static int acer_wireless_add(struct acpi_device *adev) set_bit(EV_KEY, idev->evbit); set_bit(KEY_RFKILL, idev->keybit); - return input_register_device(idev); + ret = input_register_device(idev); + if (ret) + return ret; + + return acpi_dev_install_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, + acer_wireless_notify, + &pdev->dev); } -static struct acpi_driver acer_wireless_driver = { - .name = "Acer Wireless Radio Control Driver", - .class = "hotkey", - .ids = acer_wireless_acpi_ids, - .ops = { - .add = acer_wireless_add, - .notify = acer_wireless_notify, +static void acer_wireless_remove(struct platform_device *pdev) +{ + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, + acer_wireless_notify); +} + +static struct platform_driver acer_wireless_driver = { + .probe = acer_wireless_probe, + .remove = acer_wireless_remove, + .driver = { + .name = "Acer Wireless Radio Control Driver", + .acpi_match_table = acer_wireless_acpi_ids, }, }; -module_acpi_driver(acer_wireless_driver); +module_platform_driver(acer_wireless_driver); MODULE_DESCRIPTION("Acer Wireless Radio Control Driver"); MODULE_AUTHOR("Chris Chiu "); diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index d96f6af26ff7..dbbb6292cd11 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1517,9 +1517,9 @@ static void asus_input_exit(struct asus_laptop *asus) /* * ACPI driver */ -static void asus_acpi_notify(struct acpi_device *device, u32 event) +static void asus_acpi_notify(acpi_handle handle, u32 event, void *data) { - struct asus_laptop *asus = acpi_driver_data(device); + struct asus_laptop *asus = data; u16 count; /* TODO Find a better way to handle events count. */ @@ -1824,8 +1824,9 @@ static void asus_dmi_check(void) static bool asus_device_present; -static int asus_acpi_add(struct acpi_device *device) +static int asus_acpi_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct asus_laptop *asus; int result; @@ -1837,7 +1838,6 @@ static int asus_acpi_add(struct acpi_device *device) asus->handle = device->handle; strscpy(acpi_device_name(device), ASUS_LAPTOP_DEVICE_NAME); strscpy(acpi_device_class(device), ASUS_LAPTOP_CLASS); - device->driver_data = asus; asus->device = device; asus_dmi_check(); @@ -1846,6 +1846,8 @@ static int asus_acpi_add(struct acpi_device *device) if (result) goto fail_platform; + platform_set_drvdata(pdev, asus); + /* * Need platform type detection first, then the platform * device. It is used as a parent for the sub-devices below. @@ -1881,6 +1883,11 @@ static int asus_acpi_add(struct acpi_device *device) if (result && result != -ENODEV) goto fail_pega_rfkill; + result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + asus_acpi_notify, asus); + if (result) + goto fail_pega_rfkill; + asus_device_present = true; return 0; @@ -1902,10 +1909,12 @@ fail_platform: return result; } -static void asus_acpi_remove(struct acpi_device *device) +static void asus_acpi_remove(struct platform_device *pdev) { - struct asus_laptop *asus = acpi_driver_data(device); + struct asus_laptop *asus = platform_get_drvdata(pdev); + acpi_dev_remove_notify_handler(asus->device, ACPI_DEVICE_NOTIFY, + asus_acpi_notify); asus_backlight_exit(asus); asus_rfkill_exit(asus); asus_led_exit(asus); @@ -1924,16 +1933,13 @@ static const struct acpi_device_id asus_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, asus_device_ids); -static struct acpi_driver asus_acpi_driver = { - .name = ASUS_LAPTOP_NAME, - .class = ASUS_LAPTOP_CLASS, - .ids = asus_device_ids, - .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, - .ops = { - .add = asus_acpi_add, - .remove = asus_acpi_remove, - .notify = asus_acpi_notify, - }, +static struct platform_driver asus_acpi_driver = { + .probe = asus_acpi_probe, + .remove = asus_acpi_remove, + .driver = { + .name = ASUS_LAPTOP_NAME, + .acpi_match_table = asus_device_ids, + }, }; static int __init asus_laptop_init(void) @@ -1944,7 +1950,7 @@ static int __init asus_laptop_init(void) if (result < 0) return result; - result = acpi_bus_register_driver(&asus_acpi_driver); + result = platform_driver_register(&asus_acpi_driver); if (result < 0) goto fail_acpi_driver; if (!asus_device_present) { @@ -1954,7 +1960,7 @@ static int __init asus_laptop_init(void) return 0; fail_no_device: - acpi_bus_unregister_driver(&asus_acpi_driver); + platform_driver_unregister(&asus_acpi_driver); fail_acpi_driver: platform_driver_unregister(&platform_driver); return result; @@ -1962,7 +1968,7 @@ fail_acpi_driver: static void __exit asus_laptop_exit(void) { - acpi_bus_unregister_driver(&asus_acpi_driver); + platform_driver_unregister(&asus_acpi_driver); platform_driver_unregister(&platform_driver); } diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index b4677c5bba5b..8005c088e9ee 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -544,6 +544,15 @@ static const struct dmi_system_id asus_quirks[] = { }, .driver_data = &quirk_asus_zenbook_duo_kbd, }, + { + .callback = dmi_matched, + .ident = "ASUS Zenbook Duo UX8407AA", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUS"), + DMI_MATCH(DMI_PRODUCT_NAME, "Zenbook Duo UX8407AA"), + }, + .driver_data = &quirk_asus_zenbook_duo_kbd, + }, { .callback = dmi_matched, .ident = "ASUS ROG Z13", diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c index 41227bf95878..2b494bf3cba8 100644 --- a/drivers/platform/x86/asus-wireless.c +++ b/drivers/platform/x86/asus-wireless.c @@ -12,6 +12,7 @@ #include #include #include +#include #include struct hswc_params { @@ -108,9 +109,10 @@ static void led_state_set(struct led_classdev *led, enum led_brightness value) queue_work(data->wq, &data->led_work); } -static void asus_wireless_notify(struct acpi_device *adev, u32 event) +static void asus_wireless_notify(acpi_handle handle, u32 event, void *context) { - struct asus_wireless_data *data = acpi_driver_data(adev); + struct asus_wireless_data *data = context; + struct acpi_device *adev = data->adev; dev_dbg(&adev->dev, "event=%#x\n", event); if (event != 0x88) { @@ -123,19 +125,22 @@ static void asus_wireless_notify(struct acpi_device *adev, u32 event) input_sync(data->idev); } -static int asus_wireless_add(struct acpi_device *adev) +static int asus_wireless_probe(struct platform_device *pdev) { + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); struct asus_wireless_data *data; const struct acpi_device_id *id; int err; - data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - adev->driver_data = data; + + platform_set_drvdata(pdev, data); + data->adev = adev; - data->idev = devm_input_allocate_device(&adev->dev); + data->idev = devm_input_allocate_device(&pdev->dev); if (!data->idev) return -ENOMEM; data->idev->name = "Asus Wireless Radio Control"; @@ -164,34 +169,44 @@ static int asus_wireless_add(struct acpi_device *adev) data->led.flags = LED_CORE_SUSPENDRESUME; data->led.max_brightness = 1; data->led.default_trigger = "rfkill-none"; - err = devm_led_classdev_register(&adev->dev, &data->led); + err = devm_led_classdev_register(&pdev->dev, &data->led); if (err) - destroy_workqueue(data->wq); + goto err; + err = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, + asus_wireless_notify, data); + if (err) { + devm_led_classdev_unregister(&pdev->dev, &data->led); + goto err; + } + return 0; + +err: + destroy_workqueue(data->wq); return err; } -static void asus_wireless_remove(struct acpi_device *adev) +static void asus_wireless_remove(struct platform_device *pdev) { - struct asus_wireless_data *data = acpi_driver_data(adev); + struct asus_wireless_data *data = platform_get_drvdata(pdev); + acpi_dev_remove_notify_handler(data->adev, ACPI_DEVICE_NOTIFY, + asus_wireless_notify); if (data->wq) { - devm_led_classdev_unregister(&adev->dev, &data->led); + devm_led_classdev_unregister(&pdev->dev, &data->led); destroy_workqueue(data->wq); } } -static struct acpi_driver asus_wireless_driver = { - .name = "Asus Wireless Radio Control Driver", - .class = "hotkey", - .ids = device_ids, - .ops = { - .add = asus_wireless_add, - .remove = asus_wireless_remove, - .notify = asus_wireless_notify, +static struct platform_driver asus_wireless_driver = { + .probe = asus_wireless_probe, + .remove = asus_wireless_remove, + .driver = { + .name = "Asus Wireless Radio Control Driver", + .acpi_match_table = device_ids, }, }; -module_acpi_driver(asus_wireless_driver); +module_platform_driver(asus_wireless_driver); MODULE_DESCRIPTION("Asus Wireless Radio Control Driver"); MODULE_AUTHOR("João Paulo Rechi Vita "); diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 7c0915e097ba..80144c412b90 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -125,7 +125,6 @@ module_param(fnlock_default, bool, 0444); #define NVIDIA_TEMP_MIN 75 #define NVIDIA_TEMP_MAX 87 -#define ASUS_SCREENPAD_BRIGHT_MIN 20 #define ASUS_SCREENPAD_BRIGHT_MAX 255 #define ASUS_SCREENPAD_BRIGHT_DEFAULT 60 @@ -1557,7 +1556,10 @@ static ssize_t charge_control_end_threshold_show(struct device *device, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%d\n", charge_end_threshold); + if ((charge_end_threshold >= 0) && (charge_end_threshold <= 100)) + return sysfs_emit(buf, "%d\n", charge_end_threshold); + + return -ENODATA; } static DEVICE_ATTR_RW(charge_control_end_threshold); @@ -1580,11 +1582,11 @@ static int asus_wmi_battery_add(struct power_supply *battery, struct acpi_batter return -ENODEV; /* The charge threshold is only reset when the system is power cycled, - * and we can't get the current threshold so let set it to 100% when - * a battery is added. + * and we can't read the current threshold, however the majority of + * platforms retains it, therefore signal the threshold as unknown + * until user explicitly sets it to a new value. */ - asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, 100, NULL); - charge_end_threshold = 100; + charge_end_threshold = -1; return 0; } @@ -4408,43 +4410,35 @@ static int read_screenpad_brightness(struct backlight_device *bd) return err; /* The device brightness can only be read if powered, so return stored */ if (err == BACKLIGHT_POWER_OFF) - return asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN; + return bd->props.brightness; err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &retval); if (err < 0) return err; - return (retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK) - ASUS_SCREENPAD_BRIGHT_MIN; + return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK; } static int update_screenpad_bl_status(struct backlight_device *bd) { - struct asus_wmi *asus = bl_get_data(bd); - int power, err = 0; - u32 ctrl_param; + u32 ctrl_param = bd->props.brightness; + int err = 0; - power = read_screenpad_backlight_power(asus); - if (power < 0) - return power; + if (bd->props.power) { + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 1, NULL); + if (err < 0) + return err; - if (bd->props.power != power) { - if (power != BACKLIGHT_POWER_ON) { - /* Only brightness > 0 can power it back on */ - ctrl_param = asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN; - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, - ctrl_param, NULL); - } else { - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 0, NULL); - } - } else if (power == BACKLIGHT_POWER_ON) { - /* Only set brightness if powered on or we get invalid/unsync state */ - ctrl_param = bd->props.brightness + ASUS_SCREENPAD_BRIGHT_MIN; err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, ctrl_param, NULL); + if (err < 0) + return err; } - /* Ensure brightness is stored to turn back on with */ - if (err == 0) - asus->driver->screenpad_brightness = bd->props.brightness + ASUS_SCREENPAD_BRIGHT_MIN; + if (!bd->props.power) { + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 0, NULL); + if (err < 0) + return err; + } return err; } @@ -4462,22 +4456,19 @@ static int asus_screenpad_init(struct asus_wmi *asus) int err, power; int brightness = 0; - power = read_screenpad_backlight_power(asus); + power = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_SCREENPAD_POWER); if (power < 0) return power; - if (power != BACKLIGHT_POWER_OFF) { + if (power) { err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &brightness); if (err < 0) return err; } - /* default to an acceptable min brightness on boot if too low */ - if (brightness < ASUS_SCREENPAD_BRIGHT_MIN) - brightness = ASUS_SCREENPAD_BRIGHT_DEFAULT; memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; /* ensure this bd is last to be picked */ - props.max_brightness = ASUS_SCREENPAD_BRIGHT_MAX - ASUS_SCREENPAD_BRIGHT_MIN; + props.max_brightness = ASUS_SCREENPAD_BRIGHT_MAX; bd = backlight_device_register("asus_screenpad", &asus->platform_device->dev, asus, &asus_screenpad_bl_ops, &props); @@ -4488,7 +4479,7 @@ static int asus_screenpad_init(struct asus_wmi *asus) asus->screenpad_backlight_device = bd; asus->driver->screenpad_brightness = brightness; - bd->props.brightness = brightness - ASUS_SCREENPAD_BRIGHT_MIN; + bd->props.brightness = brightness; bd->props.power = power; backlight_update_status(bd); @@ -5413,17 +5404,3 @@ void asus_wmi_unregister_driver(struct asus_wmi_driver *driver) used = false; } EXPORT_SYMBOL_GPL(asus_wmi_unregister_driver); - -static int __init asus_wmi_init(void) -{ - pr_info("ASUS WMI generic driver loaded\n"); - return 0; -} - -static void __exit asus_wmi_exit(void) -{ - pr_info("ASUS WMI generic driver unloaded\n"); -} - -module_init(asus_wmi_init); -module_exit(asus_wmi_exit); diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c index 6f13e81f98fb..2a6d8607c402 100644 --- a/drivers/platform/x86/barco-p50-gpio.c +++ b/drivers/platform/x86/barco-p50-gpio.c @@ -272,30 +272,27 @@ static int p50_gpio_get(struct gpio_chip *gc, unsigned int offset) struct p50_gpio *p50 = gpiochip_get_data(gc); int ret; - mutex_lock(&p50->lock); + guard(mutex)(&p50->lock); ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_READ_GPIO, gpio_params[offset], 0); - if (ret == 0) - ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA); + if (ret < 0) + return ret; - mutex_unlock(&p50->lock); + ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA); + if (ret < 0) + return ret; - return ret; + return !!ret; } static int p50_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { struct p50_gpio *p50 = gpiochip_get_data(gc); - int ret; - mutex_lock(&p50->lock); + guard(mutex)(&p50->lock); - ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, - gpio_params[offset], value); - - mutex_unlock(&p50->lock); - - return ret; + return p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, + gpio_params[offset], value); } static int p50_gpio_probe(struct platform_device *pdev) diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c new file mode 100644 index 000000000000..b0d06a80e89e --- /dev/null +++ b/drivers/platform/x86/bitland-mifs-wmi.c @@ -0,0 +1,837 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux driver for Bitland notebooks. + * + * Copyright (C) 2026 2 Mingyou Chen + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "bitland-mifs-wmi" +#define BITLAND_MIFS_GUID "B60BFB48-3E5B-49E4-A0E9-8CFFE1B3434B" +#define BITLAND_EVENT_GUID "46C93E13-EE9B-4262-8488-563BCA757FEF" + +enum bitland_mifs_operation { + WMI_METHOD_GET = 250, + WMI_METHOD_SET = 251, +}; + +enum bitland_mifs_function { + WMI_FN_SYSTEM_PER_MODE = 8, + WMI_FN_GPU_MODE = 9, + WMI_FN_KBD_TYPE = 10, + WMI_FN_FN_LOCK = 11, + WMI_FN_TP_LOCK = 12, + WMI_FN_FAN_SPEEDS = 13, + WMI_FN_RGB_KB_MODE = 16, + WMI_FN_RGB_KB_COLOR = 17, + WMI_FN_RGB_KB_BRIGHTNESS = 18, + WMI_FN_SYSTEM_AC_TYPE = 19, + WMI_FN_MAX_FAN_SWITCH = 20, + WMI_FN_MAX_FAN_SPEED = 21, + WMI_FN_CPU_THERMOMETER = 22, + WMI_FN_CPU_POWER = 23, +}; + +enum bitland_system_ac_mode { + WMI_SYSTEM_AC_TYPEC = 1, + /* Unknown type, this is unused in the original driver */ + WMI_SYSTEM_AC_CIRCULARHOLE = 2, +}; + +enum bitland_mifs_power_profile { + WMI_PP_BALANCED = 0, + WMI_PP_PERFORMANCE = 1, + WMI_PP_QUIET = 2, + WMI_PP_FULL_SPEED = 3, +}; + +enum bitland_mifs_event_id { + WMI_EVENT_RESERVED_1 = 1, + WMI_EVENT_RESERVED_2 = 2, + WMI_EVENT_RESERVED_3 = 3, + WMI_EVENT_AIRPLANE_MODE = 4, + WMI_EVENT_KBD_BRIGHTNESS = 5, + WMI_EVENT_TOUCHPAD_STATE = 6, + WMI_EVENT_FNLOCK_STATE = 7, + WMI_EVENT_KBD_MODE = 8, + WMI_EVENT_CAPSLOCK_STATE = 9, + WMI_EVENT_CALCULATOR_START = 11, + WMI_EVENT_BROWSER_START = 12, + WMI_EVENT_NUMLOCK_STATE = 13, + WMI_EVENT_SCROLLLOCK_STATE = 14, + WMI_EVENT_PERFORMANCE_PLAN = 15, + WMI_EVENT_FN_J = 16, + WMI_EVENT_FN_F = 17, + WMI_EVENT_FN_0 = 18, + WMI_EVENT_FN_1 = 19, + WMI_EVENT_FN_2 = 20, + WMI_EVENT_FN_3 = 21, + WMI_EVENT_FN_4 = 22, + WMI_EVENT_FN_5 = 24, + WMI_EVENT_REFRESH_RATE = 25, + WMI_EVENT_CPU_FAN_SPEED = 26, + WMI_EVENT_GPU_FAN_SPEED = 32, + WMI_EVENT_WIN_KEY_LOCK = 33, + WMI_EVENT_RESERVED_23 = 34, + WMI_EVENT_OPEN_APP = 35, +}; + +enum bitland_mifs_event_type { + WMI_EVENT_TYPE_HOTKEY = 1, +}; + +enum bitland_wmi_device_type { + BITLAND_WMI_CONTROL = 0, + BITLAND_WMI_EVENT = 1, +}; + +struct bitland_mifs_input { + u8 reserved1; + u8 operation; + u8 reserved2; + u8 function; + u8 payload[28]; +} __packed; + +struct bitland_mifs_output { + u8 reserved1; + u8 operation; + u8 reserved2; + u8 function; + u8 data[28]; +} __packed; + +struct bitland_mifs_event { + u8 event_type; + u8 event_id; + u8 value_low; /* For most events, this is the value */ + u8 value_high; /* For fan speed events, combined with value_low */ + u8 reserved[4]; +} __packed; + +static BLOCKING_NOTIFIER_HEAD(bitland_notifier_list); + +enum bitland_notifier_actions { + BITLAND_NOTIFY_KBD_BRIGHTNESS, + BITLAND_NOTIFY_PLATFORM_PROFILE, + BITLAND_NOTIFY_HWMON, +}; + +struct bitland_fan_notify_data { + int channel; /* 0 = CPU, 1 = GPU */ + u16 speed; +}; + +struct bitland_mifs_wmi_data { + struct wmi_device *wdev; + struct mutex lock; /* Protects WMI calls */ + struct led_classdev kbd_led; + struct notifier_block notifier; + struct input_dev *input_dev; + struct device *hwmon_dev; + struct device *pp_dev; + enum platform_profile_option saved_profile; +}; + +static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data, + const struct bitland_mifs_input *input, + struct bitland_mifs_output *output) +{ + struct wmi_buffer in_buf = { .length = sizeof(*input), .data = (void *)input }; + struct wmi_buffer out_buf = { 0 }; + int ret; + + guard(mutex)(&data->lock); + + if (!output) + return wmidev_invoke_procedure(data->wdev, 0, 1, &in_buf); + + ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, &out_buf, sizeof(*output)); + if (ret) + return ret; + + memcpy(output, out_buf.data, sizeof(*output)); + kfree(out_buf.data); + + return 0; +} + +static int laptop_profile_get(struct device *dev, + enum platform_profile_option *profile) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_GET, + .reserved2 = 0, + .function = WMI_FN_SYSTEM_PER_MODE, + }; + struct bitland_mifs_output result; + int ret; + + ret = bitland_mifs_wmi_call(data, &input, &result); + if (ret) + return ret; + + switch (result.data[0]) { + case WMI_PP_BALANCED: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case WMI_PP_PERFORMANCE: + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; + break; + case WMI_PP_QUIET: + *profile = PLATFORM_PROFILE_LOW_POWER; + break; + case WMI_PP_FULL_SPEED: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + default: + return -EINVAL; + } + return 0; +} + +static int bitland_check_performance_capability(struct bitland_mifs_wmi_data *data) +{ + struct bitland_mifs_input input = { + .operation = WMI_METHOD_GET, + .function = WMI_FN_SYSTEM_AC_TYPE, + }; + struct bitland_mifs_output output; + int ret; + + /* Full-speed/performance mode requires DC power (not USB-C) */ + if (!power_supply_is_system_supplied()) + return -EOPNOTSUPP; + + ret = bitland_mifs_wmi_call(data, &input, &output); + if (ret) + return ret; + + if (output.data[0] != WMI_SYSTEM_AC_CIRCULARHOLE) + return -EOPNOTSUPP; + + return 0; +} + +static int laptop_profile_set(struct device *dev, + enum platform_profile_option profile) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_SET, + .reserved2 = 0, + .function = WMI_FN_SYSTEM_PER_MODE, + }; + int ret; + u8 val; + + switch (profile) { + case PLATFORM_PROFILE_LOW_POWER: + val = WMI_PP_QUIET; + break; + case PLATFORM_PROFILE_BALANCED: + val = WMI_PP_BALANCED; + break; + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: + ret = bitland_check_performance_capability(data); + if (ret) + return ret; + val = WMI_PP_PERFORMANCE; + break; + case PLATFORM_PROFILE_PERFORMANCE: + ret = bitland_check_performance_capability(data); + if (ret) + return ret; + val = WMI_PP_FULL_SPEED; + break; + default: + return -EOPNOTSUPP; + } + + input.payload[0] = val; + + return bitland_mifs_wmi_call(data, &input, NULL); +} + +static int platform_profile_probe(void *drvdata, unsigned long *choices) +{ + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} + +static int bitland_mifs_wmi_suspend(struct device *dev) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + enum platform_profile_option profile; + int ret; + + ret = laptop_profile_get(data->pp_dev, &profile); + if (ret == 0) + data->saved_profile = profile; + + return ret; +} + +static int bitland_mifs_wmi_resume(struct device *dev) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + + dev_dbg(dev, "Resuming, restoring profile %d\n", data->saved_profile); + return laptop_profile_set(dev, data->saved_profile); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(bitland_mifs_wmi_pm_ops, + bitland_mifs_wmi_suspend, + bitland_mifs_wmi_resume); + +static const struct platform_profile_ops laptop_profile_ops = { + .probe = platform_profile_probe, + .profile_get = laptop_profile_get, + .profile_set = laptop_profile_set, +}; + +static const char *const fan_labels[] = { + "CPU", /* 0 */ + "GPU", /* 1 */ + "SYS", /* 2 */ +}; + +static int laptop_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_GET, + .reserved2 = 0, + }; + struct bitland_mifs_output res; + int ret; + + switch (type) { + case hwmon_temp: + input.function = WMI_FN_CPU_THERMOMETER; + ret = bitland_mifs_wmi_call(data, &input, &res); + if (!ret) + *val = res.data[0] * MILLIDEGREE_PER_DEGREE; + return ret; + case hwmon_fan: + input.function = WMI_FN_FAN_SPEEDS; + ret = bitland_mifs_wmi_call(data, &input, &res); + if (ret) + return ret; + + switch (channel) { + case 0: /* CPU */ + *val = get_unaligned_le16(&res.data[0]); + return 0; + case 1: /* GPU */ + *val = get_unaligned_le16(&res.data[2]); + return 0; + case 2: /* SYS */ + *val = get_unaligned_le16(&res.data[6]); + return 0; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int laptop_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + if (type == hwmon_fan && attr == hwmon_fan_label) { + if (channel >= 0 && channel < ARRAY_SIZE(fan_labels)) { + *str = fan_labels[channel]; + return 0; + } + } + return -EINVAL; +} + +static const struct hwmon_channel_info *laptop_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL), + NULL +}; + +static const struct hwmon_ops laptop_hwmon_ops = { + .visible = 0444, + .read = laptop_hwmon_read, + .read_string = laptop_hwmon_read_string, +}; + +static const struct hwmon_chip_info laptop_chip_info = { + .ops = &laptop_hwmon_ops, + .info = laptop_hwmon_info, +}; + +static int laptop_kbd_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct bitland_mifs_wmi_data *data = + container_of(led_cdev, struct bitland_mifs_wmi_data, kbd_led); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_SET, + .reserved2 = 0, + .function = WMI_FN_RGB_KB_BRIGHTNESS, + }; + + input.payload[0] = (u8)value; + + return bitland_mifs_wmi_call(data, &input, NULL); +} + +static enum led_brightness laptop_kbd_led_get(struct led_classdev *led_cdev) +{ + struct bitland_mifs_wmi_data *data = + container_of(led_cdev, struct bitland_mifs_wmi_data, kbd_led); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_GET, + .reserved2 = 0, + .function = WMI_FN_RGB_KB_BRIGHTNESS, + }; + struct bitland_mifs_output res; + int ret; + + ret = bitland_mifs_wmi_call(data, &input, &res); + if (ret) + return ret; + + return res.data[0]; +} + +static const char *const gpu_mode_strings[] = { + "hybrid", + "discrete", + "uma", +}; + +/* GPU Mode: 0:Hybrid, 1:Discrete, 2:UMA */ +static ssize_t gpu_mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_GET, + .reserved2 = 0, + .function = WMI_FN_GPU_MODE, + }; + struct bitland_mifs_output res; + u8 mode_val; + int ret; + + ret = bitland_mifs_wmi_call(data, &input, &res); + if (ret) + return ret; + + mode_val = res.data[0]; + if (mode_val >= ARRAY_SIZE(gpu_mode_strings)) + return -EPROTO; + + return sysfs_emit(buf, "%s\n", gpu_mode_strings[mode_val]); +} + +static ssize_t gpu_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_SET, + .reserved2 = 0, + .function = WMI_FN_GPU_MODE, + }; + int val; + int ret; + + val = sysfs_match_string(gpu_mode_strings, buf); + if (val < 0) + return -EINVAL; + + input.payload[0] = (u8)val; + + ret = bitland_mifs_wmi_call(data, &input, NULL); + if (ret) + return ret; + + return count; +} + +static const char *const kb_mode_strings[] = { + "off", /* 0 */ + "cyclic", /* 1 */ + "fixed", /* 2 */ + "custom", /* 3 */ +}; + +static ssize_t kb_mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_GET, + .reserved2 = 0, + .function = WMI_FN_RGB_KB_MODE, + }; + struct bitland_mifs_output res; + u8 mode_val; + int ret; + + ret = bitland_mifs_wmi_call(data, &input, &res); + if (ret) + return ret; + + mode_val = res.data[0]; + if (mode_val >= ARRAY_SIZE(kb_mode_strings)) + return -EPROTO; + + return sysfs_emit(buf, "%s\n", kb_mode_strings[mode_val]); +} + +static ssize_t kb_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_SET, + .reserved2 = 0, + .function = WMI_FN_RGB_KB_MODE, + }; + // the wmi value (0, 1, 2 or 3) + int val; + int ret; + + val = sysfs_match_string(kb_mode_strings, buf); + if (val < 0) + return -EINVAL; + + input.payload[0] = (u8)val; + + ret = bitland_mifs_wmi_call(data, &input, NULL); + if (ret) + return ret; + + return count; +} + +/* Fan Boost: 0:Normal, 1:Max Speed */ +static ssize_t fan_boost_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_SET, + .reserved2 = 0, + .function = WMI_FN_MAX_FAN_SWITCH, + }; + bool val; + int ret; + + if (kstrtobool(buf, &val)) + return -EINVAL; + + input.payload[0] = 0; /* CPU/GPU Fan */ + input.payload[1] = val; + + ret = bitland_mifs_wmi_call(data, &input, NULL); + if (ret) + return ret; + + return count; +} + +static const DEVICE_ATTR_RW(gpu_mode); +static const DEVICE_ATTR_RW(kb_mode); +static const DEVICE_ATTR_WO(fan_boost); + +static const struct attribute *const laptop_attrs[] = { + &dev_attr_gpu_mode.attr, + &dev_attr_kb_mode.attr, + &dev_attr_fan_boost.attr, + NULL, +}; +ATTRIBUTE_GROUPS(laptop); + +static const struct key_entry bitland_mifs_wmi_keymap[] = { + { KE_KEY, WMI_EVENT_OPEN_APP, { KEY_PROG1 } }, + { KE_KEY, WMI_EVENT_CALCULATOR_START, { KEY_CALC } }, + { KE_KEY, WMI_EVENT_BROWSER_START, { KEY_WWW } }, + { KE_IGNORE, WMI_EVENT_FN_J, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_F, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_0, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_1, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_2, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_3, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_4, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_5, { KEY_RESERVED } }, + { KE_END, 0 } +}; + +static void bitland_notifier_unregister(void *data) +{ + struct notifier_block *nb = data; + + blocking_notifier_chain_unregister(&bitland_notifier_list, nb); +} + +static int bitland_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct bitland_mifs_wmi_data *data_ctx = + container_of(nb, struct bitland_mifs_wmi_data, notifier); + struct bitland_fan_notify_data *fan_info; + u8 *brightness; + + switch (action) { + case BITLAND_NOTIFY_KBD_BRIGHTNESS: + brightness = data; + led_classdev_notify_brightness_hw_changed(&data_ctx->kbd_led, + *brightness); + break; + case BITLAND_NOTIFY_PLATFORM_PROFILE: + platform_profile_notify(data_ctx->pp_dev); + break; + case BITLAND_NOTIFY_HWMON: + fan_info = data; + + hwmon_notify_event(data_ctx->hwmon_dev, hwmon_fan, + hwmon_fan_input, fan_info->channel); + break; + } + + return NOTIFY_OK; +} + +static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context) +{ + struct bitland_mifs_wmi_data *drv_data; + enum bitland_wmi_device_type dev_type = + (enum bitland_wmi_device_type)(unsigned long)context; + struct led_init_data init_data = { + .devicename = DRV_NAME, + .default_label = ":" LED_FUNCTION_KBD_BACKLIGHT, + .devname_mandatory = true, + }; + int ret; + + drv_data = devm_kzalloc(&wdev->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + drv_data->wdev = wdev; + + ret = devm_mutex_init(&wdev->dev, &drv_data->lock); + if (ret) + return ret; + + dev_set_drvdata(&wdev->dev, drv_data); + + if (dev_type == BITLAND_WMI_EVENT) { + /* Register input device for hotkeys */ + drv_data->input_dev = devm_input_allocate_device(&wdev->dev); + if (!drv_data->input_dev) + return -ENOMEM; + + drv_data->input_dev->name = "Bitland MIFS WMI hotkeys"; + drv_data->input_dev->phys = "wmi/input0"; + drv_data->input_dev->id.bustype = BUS_HOST; + drv_data->input_dev->dev.parent = &wdev->dev; + + ret = sparse_keymap_setup(drv_data->input_dev, + bitland_mifs_wmi_keymap, NULL); + if (ret) + return ret; + + return input_register_device(drv_data->input_dev); + } + + /* Register platform profile */ + drv_data->pp_dev = devm_platform_profile_register(&wdev->dev, DRV_NAME, drv_data, + &laptop_profile_ops); + if (IS_ERR(drv_data->pp_dev)) + return PTR_ERR(drv_data->pp_dev); + + /* Register hwmon */ + drv_data->hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev, + "bitland_mifs", + drv_data, + &laptop_chip_info, + NULL); + if (IS_ERR(drv_data->hwmon_dev)) + return PTR_ERR(drv_data->hwmon_dev); + + /* Register keyboard LED */ + drv_data->kbd_led.max_brightness = 3; + drv_data->kbd_led.brightness_set_blocking = laptop_kbd_led_set; + drv_data->kbd_led.brightness_get = laptop_kbd_led_get; + drv_data->kbd_led.brightness = laptop_kbd_led_get(&drv_data->kbd_led); + drv_data->kbd_led.flags = LED_CORE_SUSPENDRESUME | + LED_BRIGHT_HW_CHANGED | + LED_REJECT_NAME_CONFLICT; + ret = devm_led_classdev_register_ext(&wdev->dev, &drv_data->kbd_led, &init_data); + if (ret) + return ret; + + drv_data->notifier.notifier_call = bitland_notifier_callback; + ret = blocking_notifier_chain_register(&bitland_notifier_list, &drv_data->notifier); + if (ret) + return ret; + + return devm_add_action_or_reset(&wdev->dev, + bitland_notifier_unregister, + &drv_data->notifier); +} + +static void bitland_mifs_wmi_notify(struct wmi_device *wdev, + const struct wmi_buffer *buffer) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(&wdev->dev); + const struct bitland_mifs_event *event = buffer->data; + struct bitland_fan_notify_data fan_data; + u8 brightness; + + /* Validate event type */ + if (event->event_type != WMI_EVENT_TYPE_HOTKEY) + return; + + dev_dbg(&wdev->dev, + "WMI event: id=0x%02x value_low=0x%02x value_high=0x%02x\n", + event->event_id, event->value_low, event->value_high); + + switch (event->event_id) { + case WMI_EVENT_KBD_BRIGHTNESS: + brightness = event->value_low; + blocking_notifier_call_chain(&bitland_notifier_list, + BITLAND_NOTIFY_KBD_BRIGHTNESS, + &brightness); + break; + + case WMI_EVENT_PERFORMANCE_PLAN: + blocking_notifier_call_chain(&bitland_notifier_list, + BITLAND_NOTIFY_PLATFORM_PROFILE, + NULL); + break; + + case WMI_EVENT_OPEN_APP: + case WMI_EVENT_CALCULATOR_START: + case WMI_EVENT_BROWSER_START: { + guard(mutex)(&data->lock); + if (!sparse_keymap_report_event(data->input_dev, + event->event_id, 1, true)) + dev_warn(&wdev->dev, "Unknown key pressed: 0x%02x\n", + event->event_id); + break; + } + + /* + * The device has 3 fans (CPU, GPU, SYS), + * but there are only the CPU and GPU fan has events + */ + case WMI_EVENT_CPU_FAN_SPEED: + case WMI_EVENT_GPU_FAN_SPEED: + if (event->event_id == WMI_EVENT_CPU_FAN_SPEED) + fan_data.channel = 0; + else + fan_data.channel = 1; + + /* Fan speed is 16-bit value (value_low is LSB, value_high is MSB) */ + fan_data.speed = (event->value_high << 8) | event->value_low; + blocking_notifier_call_chain(&bitland_notifier_list, + BITLAND_NOTIFY_HWMON, + &fan_data); + break; + + case WMI_EVENT_AIRPLANE_MODE: + case WMI_EVENT_TOUCHPAD_STATE: + case WMI_EVENT_FNLOCK_STATE: + case WMI_EVENT_KBD_MODE: + case WMI_EVENT_CAPSLOCK_STATE: + case WMI_EVENT_NUMLOCK_STATE: + case WMI_EVENT_SCROLLLOCK_STATE: + case WMI_EVENT_REFRESH_RATE: + case WMI_EVENT_WIN_KEY_LOCK: + /* These events are informational or handled by firmware */ + dev_dbg(&wdev->dev, "State change event: id=%d value=%d\n", + event->event_id, event->value_low); + break; + + default: + dev_dbg(&wdev->dev, "Unknown event: id=0x%02x value=0x%02x\n", + event->event_id, event->value_low); + break; + } +} + +static const struct wmi_device_id bitland_mifs_wmi_id_table[] = { + { BITLAND_MIFS_GUID, (void *)BITLAND_WMI_CONTROL }, + { BITLAND_EVENT_GUID, (void *)BITLAND_WMI_EVENT }, + {} +}; +MODULE_DEVICE_TABLE(wmi, bitland_mifs_wmi_id_table); + +static struct wmi_driver bitland_mifs_wmi_driver = { + .no_singleton = true, + .driver = { + .name = DRV_NAME, + .dev_groups = laptop_groups, + .pm = pm_sleep_ptr(&bitland_mifs_wmi_pm_ops), + }, + .id_table = bitland_mifs_wmi_id_table, + .min_event_size = sizeof(struct bitland_mifs_event), + .probe = bitland_mifs_wmi_probe, + .notify_new = bitland_mifs_wmi_notify, +}; + +module_wmi_driver(bitland_mifs_wmi_driver); + +MODULE_AUTHOR("Mingyou Chen "); +MODULE_DESCRIPTION("Bitland MIFS (MiInterface) WMI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell/dell-rbtn.c b/drivers/platform/x86/dell/dell-rbtn.c index a415c432d4c3..34af9f4ff741 100644 --- a/drivers/platform/x86/dell/dell-rbtn.c +++ b/drivers/platform/x86/dell/dell-rbtn.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "dell-rbtn.h" @@ -109,9 +110,9 @@ static const struct rfkill_ops rbtn_ops = { .set_block = rbtn_rfkill_set_block, }; -static int rbtn_rfkill_init(struct acpi_device *device) +static int rbtn_rfkill_init(struct device *dev) { - struct rbtn_data *rbtn_data = device->driver_data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); int ret; if (rbtn_data->rfkill) @@ -122,8 +123,8 @@ static int rbtn_rfkill_init(struct acpi_device *device) * but rfkill interface does not support "ANY" type * so "WLAN" type is used */ - rbtn_data->rfkill = rfkill_alloc("dell-rbtn", &device->dev, - RFKILL_TYPE_WLAN, &rbtn_ops, device); + rbtn_data->rfkill = rfkill_alloc("dell-rbtn", dev, RFKILL_TYPE_WLAN, + &rbtn_ops, ACPI_COMPANION(dev)); if (!rbtn_data->rfkill) return -ENOMEM; @@ -137,9 +138,9 @@ static int rbtn_rfkill_init(struct acpi_device *device) return 0; } -static void rbtn_rfkill_exit(struct acpi_device *device) +static void rbtn_rfkill_exit(struct device *dev) { - struct rbtn_data *rbtn_data = device->driver_data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); if (!rbtn_data->rfkill) return; @@ -149,12 +150,12 @@ static void rbtn_rfkill_exit(struct acpi_device *device) rbtn_data->rfkill = NULL; } -static void rbtn_rfkill_event(struct acpi_device *device) +static void rbtn_rfkill_event(struct device *dev) { - struct rbtn_data *rbtn_data = device->driver_data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); if (rbtn_data->rfkill) - rbtn_rfkill_query(rbtn_data->rfkill, device); + rbtn_rfkill_query(rbtn_data->rfkill, ACPI_COMPANION(dev)); } @@ -205,9 +206,9 @@ static void rbtn_input_event(struct rbtn_data *rbtn_data) * acpi driver */ -static int rbtn_add(struct acpi_device *device); -static void rbtn_remove(struct acpi_device *device); -static void rbtn_notify(struct acpi_device *device, u32 event); +static int rbtn_probe(struct platform_device *pdev); +static void rbtn_remove(struct platform_device *pdev); +static void rbtn_notify(acpi_handle handle, u32 event, void *data); static const struct acpi_device_id rbtn_ids[] = { { "DELRBTN", 0 }, @@ -251,8 +252,7 @@ static void ACPI_SYSTEM_XFACE rbtn_clear_suspended_flag(void *context) static int rbtn_suspend(struct device *dev) { - struct acpi_device *device = to_acpi_device(dev); - struct rbtn_data *rbtn_data = acpi_driver_data(device); + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); rbtn_data->suspended = true; @@ -261,8 +261,7 @@ static int rbtn_suspend(struct device *dev) static int rbtn_resume(struct device *dev) { - struct acpi_device *device = to_acpi_device(dev); - struct rbtn_data *rbtn_data = acpi_driver_data(device); + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); acpi_status status; /* @@ -286,14 +285,13 @@ static int rbtn_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(rbtn_pm_ops, rbtn_suspend, rbtn_resume); -static struct acpi_driver rbtn_driver = { - .name = "dell-rbtn", - .ids = rbtn_ids, - .drv.pm = &rbtn_pm_ops, - .ops = { - .add = rbtn_add, - .remove = rbtn_remove, - .notify = rbtn_notify, +static struct platform_driver rbtn_driver = { + .probe = rbtn_probe, + .remove = rbtn_remove, + .driver = { + .name = "dell-rbtn", + .acpi_match_table = rbtn_ids, + .pm = &rbtn_pm_ops, }, }; @@ -308,8 +306,7 @@ static ATOMIC_NOTIFIER_HEAD(rbtn_chain_head); static int rbtn_inc_count(struct device *dev, void *data) { - struct acpi_device *device = to_acpi_device(dev); - struct rbtn_data *rbtn_data = device->driver_data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); int *count = data; if (rbtn_data->type == RBTN_SLIDER) @@ -320,17 +317,16 @@ static int rbtn_inc_count(struct device *dev, void *data) static int rbtn_switch_dev(struct device *dev, void *data) { - struct acpi_device *device = to_acpi_device(dev); - struct rbtn_data *rbtn_data = device->driver_data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); bool enable = data; if (rbtn_data->type != RBTN_SLIDER) return 0; if (enable) - rbtn_rfkill_init(device); + rbtn_rfkill_init(dev); else - rbtn_rfkill_exit(device); + rbtn_rfkill_exit(dev); return 0; } @@ -342,7 +338,7 @@ int dell_rbtn_notifier_register(struct notifier_block *nb) int ret; count = 0; - ret = driver_for_each_device(&rbtn_driver.drv, NULL, &count, + ret = driver_for_each_device(&rbtn_driver.driver, NULL, &count, rbtn_inc_count); if (ret || count == 0) return -ENODEV; @@ -354,7 +350,7 @@ int dell_rbtn_notifier_register(struct notifier_block *nb) return ret; if (auto_remove_rfkill && first) - ret = driver_for_each_device(&rbtn_driver.drv, NULL, + ret = driver_for_each_device(&rbtn_driver.driver, NULL, (void *)false, rbtn_switch_dev); return ret; @@ -370,7 +366,7 @@ int dell_rbtn_notifier_unregister(struct notifier_block *nb) return ret; if (auto_remove_rfkill && !rbtn_chain_head.head) - ret = driver_for_each_device(&rbtn_driver.drv, NULL, + ret = driver_for_each_device(&rbtn_driver.driver, NULL, (void *)true, rbtn_switch_dev); return ret; @@ -382,30 +378,48 @@ EXPORT_SYMBOL_GPL(dell_rbtn_notifier_unregister); * acpi driver functions */ -static int rbtn_add(struct acpi_device *device) +static void rbtn_cleanup(struct device *dev) { + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); + + switch (rbtn_data->type) { + case RBTN_TOGGLE: + rbtn_input_exit(rbtn_data); + break; + case RBTN_SLIDER: + rbtn_rfkill_exit(dev); + break; + default: + break; + } +} + +static int rbtn_probe(struct platform_device *pdev) +{ + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct rbtn_data *rbtn_data; enum rbtn_type type; int ret = 0; type = rbtn_check(device); if (type == RBTN_UNKNOWN) { - dev_info(&device->dev, "Unknown device type\n"); + dev_info(&pdev->dev, "Unknown device type\n"); return -EINVAL; } - rbtn_data = devm_kzalloc(&device->dev, sizeof(*rbtn_data), GFP_KERNEL); + rbtn_data = devm_kzalloc(&pdev->dev, sizeof(*rbtn_data), GFP_KERNEL); if (!rbtn_data) return -ENOMEM; ret = rbtn_acquire(device, true); if (ret < 0) { - dev_err(&device->dev, "Cannot enable device\n"); + dev_err(&pdev->dev, "Cannot enable device\n"); return ret; } + platform_set_drvdata(pdev, rbtn_data); + rbtn_data->type = type; - device->driver_data = rbtn_data; switch (rbtn_data->type) { case RBTN_TOGGLE: @@ -415,51 +429,54 @@ static int rbtn_add(struct acpi_device *device) if (auto_remove_rfkill && rbtn_chain_head.head) ret = 0; else - ret = rbtn_rfkill_init(device); + ret = rbtn_rfkill_init(&pdev->dev); break; default: ret = -EINVAL; break; } if (ret) - rbtn_acquire(device, false); + goto err; + ret = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + rbtn_notify, &pdev->dev); + if (ret) + goto err_cleanup; + + return 0; + +err_cleanup: + rbtn_cleanup(&pdev->dev); +err: + rbtn_acquire(device, false); return ret; } -static void rbtn_remove(struct acpi_device *device) +static void rbtn_remove(struct platform_device *pdev) { - struct rbtn_data *rbtn_data = device->driver_data; - - switch (rbtn_data->type) { - case RBTN_TOGGLE: - rbtn_input_exit(rbtn_data); - break; - case RBTN_SLIDER: - rbtn_rfkill_exit(device); - break; - default: - break; - } + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, rbtn_notify); + rbtn_cleanup(&pdev->dev); rbtn_acquire(device, false); } -static void rbtn_notify(struct acpi_device *device, u32 event) +static void rbtn_notify(acpi_handle handle, u32 event, void *data) { - struct rbtn_data *rbtn_data = device->driver_data; + struct device *dev = data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); /* * Some BIOSes send a notification at resume. * Ignore it to prevent unwanted input events. */ if (rbtn_data->suspended) { - dev_dbg(&device->dev, "ACPI notification ignored\n"); + dev_dbg(dev, "ACPI notification ignored\n"); return; } if (event != 0x80) { - dev_info(&device->dev, "Received unknown event (0x%x)\n", + dev_info(dev, "Received unknown event (0x%x)\n", event); return; } @@ -469,20 +486,15 @@ static void rbtn_notify(struct acpi_device *device, u32 event) rbtn_input_event(rbtn_data); break; case RBTN_SLIDER: - rbtn_rfkill_event(device); - atomic_notifier_call_chain(&rbtn_chain_head, event, device); + rbtn_rfkill_event(dev); + atomic_notifier_call_chain(&rbtn_chain_head, event, NULL); break; default: break; } } - -/* - * module functions - */ - -module_acpi_driver(rbtn_driver); +module_platform_driver(rbtn_driver); module_param(auto_remove_rfkill, bool, 0444); diff --git a/drivers/platform/x86/dell/dell-wmi-base.c b/drivers/platform/x86/dell/dell-wmi-base.c index e7a411ae9ca1..2a5804efd3ea 100644 --- a/drivers/platform/x86/dell/dell-wmi-base.c +++ b/drivers/platform/x86/dell/dell-wmi-base.c @@ -825,6 +825,7 @@ static struct wmi_driver dell_wmi_driver = { .name = "dell-wmi", }, .id_table = dell_wmi_id_table, + .min_event_size = sizeof(u16), .probe = dell_wmi_probe, .remove = dell_wmi_remove, .notify = dell_wmi_notify, diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h b/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h index 817ee7ba07ca..5278a93fdaf7 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h +++ b/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h @@ -189,8 +189,8 @@ void exit_bios_attr_set_interface(void); int init_bios_attr_set_interface(void); int map_wmi_error(int error_code); size_t calculate_string_buffer(const char *str); -size_t calculate_security_buffer(char *authentication); -void populate_security_buffer(char *buffer, char *authentication); +size_t calculate_security_buffer(const char *authentication); +void populate_security_buffer(char *buffer, const char *authentication); ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str); int set_new_password(const char *password_type, const char *new); int init_bios_attr_pass_interface(void); diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c b/drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c index 09996fbdc707..a85639d8a076 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c @@ -6,10 +6,32 @@ * Copyright (c) 2020 Dell Inc. */ +#include + #include "dell-wmi-sysman.h" get_instance_id(enumeration); +static int append_enum_string(char *dest, const char *src) +{ + size_t dest_len = strlen(dest); + ssize_t copied; + + if (WARN_ON_ONCE(dest_len >= MAX_BUFF)) + return -EINVAL; + + copied = strscpy(dest + dest_len, src, MAX_BUFF - dest_len); + if (copied < 0) + return -EINVAL; + + dest_len += copied; + copied = strscpy(dest + dest_len, ";", MAX_BUFF - dest_len); + if (copied < 0) + return -EINVAL; + + return 0; +} + static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int instance_id = get_enumeration_instance_id(kobj); @@ -176,9 +198,9 @@ int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, return -EINVAL; if (check_property_type(enumeration, next_obj, ACPI_TYPE_STRING)) return -EINVAL; - strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier, - enumeration_obj[next_obj++].string.pointer); - strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier, ";"); + if (append_enum_string(wmi_priv.enumeration_data[instance_id].dell_value_modifier, + enumeration_obj[next_obj++].string.pointer)) + return -EINVAL; } if (next_obj >= enum_property_count) @@ -193,9 +215,9 @@ int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, return -EINVAL; if (check_property_type(enumeration, next_obj, ACPI_TYPE_STRING)) return -EINVAL; - strcat(wmi_priv.enumeration_data[instance_id].possible_values, - enumeration_obj[next_obj++].string.pointer); - strcat(wmi_priv.enumeration_data[instance_id].possible_values, ";"); + if (append_enum_string(wmi_priv.enumeration_data[instance_id].possible_values, + enumeration_obj[next_obj++].string.pointer)) + return -EINVAL; } return sysfs_create_group(attr_name_kobj, &enumeration_attr_group); diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c index 9dddab6c9397..51d25fdc1389 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c @@ -7,10 +7,13 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include #include +#include +#include #include #include "dell-wmi-sysman.h" #include "../../firmware_attributes_class.h" @@ -72,13 +75,9 @@ size_t calculate_string_buffer(const char *str) * * Currently only supported type is Admin password */ -size_t calculate_security_buffer(char *authentication) +size_t calculate_security_buffer(const char *authentication) { - if (strlen(authentication) > 0) { - return (sizeof(u32) * 2) + strlen(authentication) + - strlen(authentication) % 2; - } - return sizeof(u32) * 2; + return sizeof(u32) * 2 + ALIGN(strlen(authentication), 2); } /** @@ -88,18 +87,18 @@ size_t calculate_security_buffer(char *authentication) * * Currently only supported type is PLAIN TEXT */ -void populate_security_buffer(char *buffer, char *authentication) +void populate_security_buffer(char *buffer, const char *authentication) { + size_t seclen = strlen(authentication); char *auth = buffer + sizeof(u32) * 2; u32 *sectype = (u32 *) buffer; - u32 *seclen = sectype + 1; + u32 *seclenp = sectype + 1; - *sectype = strlen(authentication) > 0 ? 1 : 0; - *seclen = strlen(authentication); + *sectype = !!seclen; + *seclenp = seclen; /* plain text */ - if (strlen(authentication) > 0) - memcpy(auth, authentication, *seclen); + memcpy(auth, authentication, seclen); } /** @@ -143,17 +142,17 @@ int map_wmi_error(int error_code) */ static ssize_t reset_bios_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - char *start = buf; + ssize_t len = 0; int i; for (i = 0; i < MAX_TYPES; i++) { if (i == reset_option) - buf += sprintf(buf, "[%s] ", reset_types[i]); + len += sysfs_emit_at(buf, len, "[%s] ", reset_types[i]); else - buf += sprintf(buf, "%s ", reset_types[i]); + len += sysfs_emit_at(buf, len, "%s ", reset_types[i]); } - buf += sprintf(buf, "\n"); - return buf-start; + len += sysfs_emit_at(buf, len, "\n"); + return len; } /** @@ -194,7 +193,7 @@ static ssize_t reset_bios_store(struct kobject *kobj, static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", wmi_priv.pending_changes); + return sysfs_emit(buf, "%d\n", wmi_priv.pending_changes); } static struct kobj_attribute reset_bios = __ATTR_RW(reset_bios); @@ -220,35 +219,6 @@ static int create_attributes_level_sysfs_files(void) return 0; } -static ssize_t wmi_sysman_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct kobj_attribute *kattr; - ssize_t ret = -EIO; - - kattr = container_of(attr, struct kobj_attribute, attr); - if (kattr->show) - ret = kattr->show(kobj, kattr, buf); - return ret; -} - -static ssize_t wmi_sysman_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - struct kobj_attribute *kattr; - ssize_t ret = -EIO; - - kattr = container_of(attr, struct kobj_attribute, attr); - if (kattr->store) - ret = kattr->store(kobj, kattr, buf, count); - return ret; -} - -static const struct sysfs_ops wmi_sysman_kobj_sysfs_ops = { - .show = wmi_sysman_attr_show, - .store = wmi_sysman_attr_store, -}; - static void attr_name_release(struct kobject *kobj) { kfree(kobj); @@ -256,7 +226,7 @@ static void attr_name_release(struct kobject *kobj) static const struct kobj_type attr_name_ktype = { .release = attr_name_release, - .sysfs_ops = &wmi_sysman_kobj_sysfs_ops, + .sysfs_ops = &kobj_sysfs_ops, }; /** @@ -343,7 +313,7 @@ static int alloc_attributes_data(int attr_type) * destroy_attribute_objs() - Free a kset of kobjects * @kset: The kset to destroy * - * Fress kobjects created for each attribute_name under attribute type kset + * Frees kobjects created for each attribute_name under attribute type kset. */ static void destroy_attribute_objs(struct kset *kset) { diff --git a/drivers/platform/x86/dell/dell_rbu.c b/drivers/platform/x86/dell/dell_rbu.c index eb50f1d75d0c..3fa9de9aa47b 100644 --- a/drivers/platform/x86/dell/dell_rbu.c +++ b/drivers/platform/x86/dell/dell_rbu.c @@ -30,6 +30,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -619,9 +620,12 @@ static ssize_t packet_size_write(struct file *filp, struct kobject *kobj, char *buffer, loff_t pos, size_t count) { unsigned long temp; + + if (kstrtoul(buffer, 10, &temp)) + return -EINVAL; + spin_lock(&rbu_data.lock); packet_empty_list(); - sscanf(buffer, "%lu", &temp); if (temp < 0xffffffff) rbu_data.packetsize = temp; diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 974f55e0b36f..02a71095920e 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1204,9 +1204,10 @@ static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event) pr_info("Unknown key %x pressed\n", event); } -static void eeepc_acpi_notify(struct acpi_device *device, u32 event) +static void eeepc_acpi_notify(acpi_handle handle, u32 event, void *data) { - struct eeepc_laptop *eeepc = acpi_driver_data(device); + struct eeepc_laptop *eeepc = data; + struct acpi_device *device = eeepc->device; int old_brightness, new_brightness; u16 count; @@ -1360,8 +1361,9 @@ static void eeepc_enable_camera(struct eeepc_laptop *eeepc) static bool eeepc_device_present; -static int eeepc_acpi_add(struct acpi_device *device) +static int eeepc_acpi_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct eeepc_laptop *eeepc; int result; @@ -1372,9 +1374,10 @@ static int eeepc_acpi_add(struct acpi_device *device) eeepc->handle = device->handle; strscpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); strscpy(acpi_device_class(device), EEEPC_ACPI_CLASS); - device->driver_data = eeepc; eeepc->device = device; + platform_set_drvdata(pdev, eeepc); + eeepc->hotplug_disabled = hotplug_disabled; eeepc_dmi_check(eeepc); @@ -1422,9 +1425,16 @@ static int eeepc_acpi_add(struct acpi_device *device) if (result) goto fail_rfkill; + result = acpi_dev_install_notify_handler(device, ACPI_ALL_NOTIFY, + eeepc_acpi_notify, eeepc); + if (result) + goto fail_acpi_notifier; + eeepc_device_present = true; return 0; +fail_acpi_notifier: + eeepc_rfkill_exit(eeepc); fail_rfkill: eeepc_led_exit(eeepc); fail_led: @@ -1440,10 +1450,12 @@ fail_platform: return result; } -static void eeepc_acpi_remove(struct acpi_device *device) +static void eeepc_acpi_remove(struct platform_device *pdev) { - struct eeepc_laptop *eeepc = acpi_driver_data(device); + struct eeepc_laptop *eeepc = platform_get_drvdata(pdev); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_ALL_NOTIFY, eeepc_acpi_notify); eeepc_backlight_exit(eeepc); eeepc_rfkill_exit(eeepc); eeepc_input_exit(eeepc); @@ -1460,15 +1472,12 @@ static const struct acpi_device_id eeepc_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, eeepc_device_ids); -static struct acpi_driver eeepc_acpi_driver = { - .name = EEEPC_LAPTOP_NAME, - .class = EEEPC_ACPI_CLASS, - .ids = eeepc_device_ids, - .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, - .ops = { - .add = eeepc_acpi_add, - .remove = eeepc_acpi_remove, - .notify = eeepc_acpi_notify, +static struct platform_driver eeepc_acpi_driver = { + .probe = eeepc_acpi_probe, + .remove = eeepc_acpi_remove, + .driver = { + .name = EEEPC_LAPTOP_NAME, + .acpi_match_table = eeepc_device_ids, }, }; @@ -1481,7 +1490,7 @@ static int __init eeepc_laptop_init(void) if (result < 0) return result; - result = acpi_bus_register_driver(&eeepc_acpi_driver); + result = platform_driver_register(&eeepc_acpi_driver); if (result < 0) goto fail_acpi_driver; @@ -1493,7 +1502,7 @@ static int __init eeepc_laptop_init(void) return 0; fail_no_device: - acpi_bus_unregister_driver(&eeepc_acpi_driver); + platform_driver_unregister(&eeepc_acpi_driver); fail_acpi_driver: platform_driver_unregister(&platform_driver); return result; @@ -1501,7 +1510,7 @@ fail_acpi_driver: static void __exit eeepc_laptop_exit(void) { - acpi_bus_unregister_driver(&eeepc_acpi_driver); + platform_driver_unregister(&eeepc_acpi_driver); platform_driver_unregister(&platform_driver); } diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 931fbcdd21b8..2e265be2267e 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -144,11 +144,11 @@ struct fujitsu_laptop { bool charge_control_supported; }; -static struct acpi_device *fext; +static struct device *fext; /* Fujitsu ACPI interface function */ -static int call_fext_func(struct acpi_device *device, +static int call_fext_func(struct device *dev, int func, int op, int feature, int state) { union acpi_object params[4] = { @@ -158,18 +158,17 @@ static int call_fext_func(struct acpi_device *device, { .integer.type = ACPI_TYPE_INTEGER, .integer.value = state } }; struct acpi_object_list arg_list = { 4, params }; + acpi_handle handle = ACPI_HANDLE(dev); unsigned long long value; acpi_status status; - status = acpi_evaluate_integer(device->handle, "FUNC", &arg_list, - &value); + status = acpi_evaluate_integer(handle, "FUNC", &arg_list, &value); if (ACPI_FAILURE(status)) { - acpi_handle_err(device->handle, "Failed to evaluate FUNC\n"); + acpi_handle_err(handle, "Failed to evaluate FUNC\n"); return -ENODEV; } - acpi_handle_debug(device->handle, - "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", + acpi_handle_debug(handle, "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", func, op, feature, state, (int)value); return value; } @@ -251,9 +250,9 @@ static struct acpi_battery_hook battery_hook = { * These functions are intended to be called from acpi_fujitsu_laptop_add and * acpi_fujitsu_laptop_remove. */ -static int fujitsu_battery_charge_control_add(struct acpi_device *device) +static int fujitsu_battery_charge_control_add(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); int s006_cc_return; priv->charge_control_supported = false; @@ -274,9 +273,9 @@ static int fujitsu_battery_charge_control_add(struct acpi_device *device) return 0; } -static void fujitsu_battery_charge_control_remove(struct acpi_device *device) +static void fujitsu_battery_charge_control_remove(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); if (priv->charge_control_supported) battery_hook_unregister(&battery_hook); @@ -284,15 +283,16 @@ static void fujitsu_battery_charge_control_remove(struct acpi_device *device) /* Hardware access for LCD brightness control */ -static int set_lcd_level(struct acpi_device *device, int level) +static int set_lcd_level(struct device *dev, int level) { - struct fujitsu_bl *priv = acpi_driver_data(device); + struct fujitsu_bl *priv = dev_get_drvdata(dev); + acpi_handle handle = ACPI_HANDLE(dev); acpi_status status; char *method; switch (use_alt_lcd_levels) { case -1: - if (acpi_has_method(device->handle, "SBL2")) + if (acpi_has_method(handle, "SBL2")) method = "SBL2"; else method = "SBLL"; @@ -305,16 +305,14 @@ static int set_lcd_level(struct acpi_device *device, int level) break; } - acpi_handle_debug(device->handle, "set lcd level via %s [%d]\n", method, - level); + acpi_handle_debug(handle, "set lcd level via %s [%d]\n", method, level); if (level < 0 || level >= priv->max_brightness) return -EINVAL; - status = acpi_execute_simple_method(device->handle, method, level); + status = acpi_execute_simple_method(handle, method, level); if (ACPI_FAILURE(status)) { - acpi_handle_err(device->handle, "Failed to evaluate %s\n", - method); + acpi_handle_err(handle, "Failed to evaluate %s\n", method); return -ENODEV; } @@ -323,15 +321,16 @@ static int set_lcd_level(struct acpi_device *device, int level) return 0; } -static int get_lcd_level(struct acpi_device *device) +static int get_lcd_level(struct device *dev) { - struct fujitsu_bl *priv = acpi_driver_data(device); + struct fujitsu_bl *priv = dev_get_drvdata(dev); + acpi_handle handle = ACPI_HANDLE(dev); unsigned long long state = 0; acpi_status status = AE_OK; - acpi_handle_debug(device->handle, "get lcd level via GBLL\n"); + acpi_handle_debug(handle, "get lcd level via GBLL\n"); - status = acpi_evaluate_integer(device->handle, "GBLL", NULL, &state); + status = acpi_evaluate_integer(handle, "GBLL", NULL, &state); if (ACPI_FAILURE(status)) return 0; @@ -340,15 +339,16 @@ static int get_lcd_level(struct acpi_device *device) return priv->brightness_level; } -static int get_max_brightness(struct acpi_device *device) +static int get_max_brightness(struct device *dev) { - struct fujitsu_bl *priv = acpi_driver_data(device); + struct fujitsu_bl *priv = dev_get_drvdata(dev); + acpi_handle handle = ACPI_HANDLE(dev); unsigned long long state = 0; acpi_status status = AE_OK; - acpi_handle_debug(device->handle, "get max lcd level via RBLL\n"); + acpi_handle_debug(handle, "get max lcd level via RBLL\n"); - status = acpi_evaluate_integer(device->handle, "RBLL", NULL, &state); + status = acpi_evaluate_integer(handle, "RBLL", NULL, &state); if (ACPI_FAILURE(status)) return -1; @@ -361,15 +361,13 @@ static int get_max_brightness(struct acpi_device *device) static int bl_get_brightness(struct backlight_device *b) { - struct acpi_device *device = bl_get_data(b); + struct device *dev = bl_get_data(b); - return b->props.power == BACKLIGHT_POWER_OFF ? 0 : get_lcd_level(device); + return b->props.power == BACKLIGHT_POWER_OFF ? 0 : get_lcd_level(dev); } static int bl_update_status(struct backlight_device *b) { - struct acpi_device *device = bl_get_data(b); - if (fext) { if (b->props.power == BACKLIGHT_POWER_OFF) call_fext_func(fext, FUNC_BACKLIGHT, 0x1, @@ -379,7 +377,7 @@ static int bl_update_status(struct backlight_device *b) BACKLIGHT_PARAM_POWER, BACKLIGHT_ON); } - return set_lcd_level(device, b->props.brightness); + return set_lcd_level(bl_get_data(b), b->props.brightness); } static const struct backlight_ops fujitsu_bl_ops = { @@ -455,12 +453,13 @@ static const struct key_entry keymap_backlight[] = { { KE_END, 0 } }; -static int acpi_fujitsu_bl_input_setup(struct acpi_device *device) +static int acpi_fujitsu_bl_input_setup(struct device *dev) { - struct fujitsu_bl *priv = acpi_driver_data(device); + struct fujitsu_bl *priv = dev_get_drvdata(dev); + struct acpi_device *device = ACPI_COMPANION(dev); int ret; - priv->input = devm_input_allocate_device(&device->dev); + priv->input = devm_input_allocate_device(dev); if (!priv->input) return -ENOMEM; @@ -479,9 +478,9 @@ static int acpi_fujitsu_bl_input_setup(struct acpi_device *device) return input_register_device(priv->input); } -static int fujitsu_backlight_register(struct acpi_device *device) +static int fujitsu_backlight_register(struct device *dev) { - struct fujitsu_bl *priv = acpi_driver_data(device); + struct fujitsu_bl *priv = dev_get_drvdata(dev); const struct backlight_properties props = { .brightness = priv->brightness_level, .max_brightness = priv->max_brightness - 1, @@ -489,9 +488,8 @@ static int fujitsu_backlight_register(struct acpi_device *device) }; struct backlight_device *bd; - bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop", - &device->dev, device, - &fujitsu_bl_ops, &props); + bd = devm_backlight_device_register(dev, "fujitsu-laptop", + dev, dev, &fujitsu_bl_ops, &props); if (IS_ERR(bd)) return PTR_ERR(bd); @@ -500,65 +498,78 @@ static int fujitsu_backlight_register(struct acpi_device *device) return 0; } -static int acpi_fujitsu_bl_add(struct acpi_device *device) +/* Brightness notify */ + +static void acpi_fujitsu_bl_notify(acpi_handle handle, u32 event, void *data) { + struct device *dev = data; + struct fujitsu_bl *priv = dev_get_drvdata(dev); + int oldb, newb; + + if (event != ACPI_FUJITSU_NOTIFY_CODE) { + acpi_handle_info(handle, "unsupported event [0x%x]\n", event); + sparse_keymap_report_event(priv->input, -1, 1, true); + return; + } + + oldb = priv->brightness_level; + get_lcd_level(dev); + newb = priv->brightness_level; + + acpi_handle_debug(handle, "brightness button event [%i -> %i]\n", + oldb, newb); + + if (oldb == newb) + return; + + if (!disable_brightness_adjust) + set_lcd_level(dev, newb); + + sparse_keymap_report_event(priv->input, oldb < newb, 1, true); +} + +static int acpi_fujitsu_bl_probe(struct platform_device *pdev) +{ + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct fujitsu_bl *priv; int ret; if (acpi_video_get_backlight_type() != acpi_backlight_vendor) return -ENODEV; - priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; fujitsu_bl = priv; strscpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME); strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); - device->driver_data = priv; + + platform_set_drvdata(pdev, priv); pr_info("ACPI: %s [%s]\n", acpi_device_name(device), acpi_device_bid(device)); - if (get_max_brightness(device) <= 0) + if (get_max_brightness(&pdev->dev) <= 0) priv->max_brightness = FUJITSU_LCD_N_LEVELS; - get_lcd_level(device); + get_lcd_level(&pdev->dev); - ret = acpi_fujitsu_bl_input_setup(device); + ret = acpi_fujitsu_bl_input_setup(&pdev->dev); if (ret) return ret; - return fujitsu_backlight_register(device); + ret = fujitsu_backlight_register(&pdev->dev); + if (ret) + return ret; + + return acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_fujitsu_bl_notify, &pdev->dev); } -/* Brightness notify */ - -static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event) +static void acpi_fujitsu_bl_remove(struct platform_device *pdev) { - struct fujitsu_bl *priv = acpi_driver_data(device); - int oldb, newb; - - if (event != ACPI_FUJITSU_NOTIFY_CODE) { - acpi_handle_info(device->handle, "unsupported event [0x%x]\n", - event); - sparse_keymap_report_event(priv->input, -1, 1, true); - return; - } - - oldb = priv->brightness_level; - get_lcd_level(device); - newb = priv->brightness_level; - - acpi_handle_debug(device->handle, - "brightness button event [%i -> %i]\n", oldb, newb); - - if (oldb == newb) - return; - - if (!disable_brightness_adjust) - set_lcd_level(device, newb); - - sparse_keymap_report_event(priv->input, oldb < newb, 1, true); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, acpi_fujitsu_bl_notify); } /* ACPI device for hotkey handling */ @@ -653,12 +664,13 @@ static const struct dmi_system_id fujitsu_laptop_dmi_table[] = { {} }; -static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device) +static int acpi_fujitsu_laptop_input_setup(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); + struct acpi_device *device = ACPI_COMPANION(dev); int ret; - priv->input = devm_input_allocate_device(&device->dev); + priv->input = devm_input_allocate_device(dev); if (!priv->input) return -ENOMEM; @@ -677,9 +689,9 @@ static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device) return input_register_device(priv->input); } -static int fujitsu_laptop_platform_add(struct acpi_device *device) +static int fujitsu_laptop_platform_add(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); int ret; priv->pf_device = platform_device_alloc("fujitsu-laptop", PLATFORM_DEVID_NONE); @@ -707,9 +719,9 @@ err_put_platform_device: return ret; } -static void fujitsu_laptop_platform_remove(struct acpi_device *device) +static void fujitsu_laptop_platform_remove(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); sysfs_remove_group(&priv->pf_device->dev.kobj, &fujitsu_pf_attribute_group); @@ -719,7 +731,7 @@ static void fujitsu_laptop_platform_remove(struct acpi_device *device) static int logolamp_set(struct led_classdev *cdev, enum led_brightness brightness) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; int poweron = FUNC_LED_ON, always = FUNC_LED_ON; int ret; @@ -729,23 +741,23 @@ static int logolamp_set(struct led_classdev *cdev, if (brightness < LED_FULL) always = FUNC_LED_OFF; - ret = call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron); + ret = call_fext_func(parent, FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron); if (ret < 0) return ret; - return call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always); + return call_fext_func(parent, FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always); } static enum led_brightness logolamp_get(struct led_classdev *cdev) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; int ret; - ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); + ret = call_fext_func(parent, FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); if (ret == FUNC_LED_ON) return LED_FULL; - ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); + ret = call_fext_func(parent, FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); if (ret == FUNC_LED_ON) return LED_HALF; @@ -755,22 +767,21 @@ static enum led_brightness logolamp_get(struct led_classdev *cdev) static int kblamps_set(struct led_classdev *cdev, enum led_brightness brightness) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; if (brightness >= LED_FULL) - return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS, + return call_fext_func(parent, FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON); else - return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS, + return call_fext_func(parent, FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF); } static enum led_brightness kblamps_get(struct led_classdev *cdev) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); enum led_brightness brightness = LED_OFF; - if (call_fext_func(device, + if (call_fext_func(cdev->dev->parent, FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON) brightness = LED_FULL; @@ -780,22 +791,22 @@ static enum led_brightness kblamps_get(struct led_classdev *cdev) static int radio_led_set(struct led_classdev *cdev, enum led_brightness brightness) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; if (brightness >= LED_FULL) - return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON, + return call_fext_func(parent, FUNC_FLAGS, 0x5, RADIO_LED_ON, RADIO_LED_ON); else - return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON, + return call_fext_func(parent, FUNC_FLAGS, 0x5, RADIO_LED_ON, 0x0); } static enum led_brightness radio_led_get(struct led_classdev *cdev) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; enum led_brightness brightness = LED_OFF; - if (call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON) + if (call_fext_func(parent, FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON) brightness = LED_FULL; return brightness; @@ -804,60 +815,58 @@ static enum led_brightness radio_led_get(struct led_classdev *cdev) static int eco_led_set(struct led_classdev *cdev, enum led_brightness brightness) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; int curr; - curr = call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0); + curr = call_fext_func(parent, FUNC_LEDS, 0x2, ECO_LED, 0x0); if (brightness >= LED_FULL) - return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED, + return call_fext_func(parent, FUNC_LEDS, 0x1, ECO_LED, curr | ECO_LED_ON); else - return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED, + return call_fext_func(parent, FUNC_LEDS, 0x1, ECO_LED, curr & ~ECO_LED_ON); } static enum led_brightness eco_led_get(struct led_classdev *cdev) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; enum led_brightness brightness = LED_OFF; - if (call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON) + if (call_fext_func(parent, FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON) brightness = LED_FULL; return brightness; } -static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) +static int acpi_fujitsu_laptop_leds_register(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); struct led_classdev *led; int ret; - if (call_fext_func(device, - FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { - led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); + if (call_fext_func(dev, FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; led->name = "fujitsu::logolamp"; led->brightness_set_blocking = logolamp_set; led->brightness_get = logolamp_get; - ret = devm_led_classdev_register(&device->dev, led); + ret = devm_led_classdev_register(dev, led); if (ret) return ret; } - if ((call_fext_func(device, - FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && - (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { - led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); + if ((call_fext_func(dev, FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && + (call_fext_func(dev, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; led->name = "fujitsu::kblamps"; led->brightness_set_blocking = kblamps_set; led->brightness_get = kblamps_get; - ret = devm_led_classdev_register(&device->dev, led); + ret = devm_led_classdev_register(dev, led); if (ret) return ret; } @@ -872,7 +881,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) * whether given model has a radio toggle button. */ if (priv->flags_supported & BIT(17)) { - led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; @@ -880,7 +889,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) led->brightness_set_blocking = radio_led_set; led->brightness_get = radio_led_get; led->default_trigger = "rfkill-any"; - ret = devm_led_classdev_register(&device->dev, led); + ret = devm_led_classdev_register(dev, led); if (ret) return ret; } @@ -890,17 +899,16 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) * bit 14 seems to indicate presence of said led as well. * Confirm by testing the status. */ - if ((call_fext_func(device, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) && - (call_fext_func(device, - FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) { - led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); + if ((call_fext_func(dev, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) && + (call_fext_func(dev, FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) { + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; led->name = "fujitsu::eco_led"; led->brightness_set_blocking = eco_led_set; led->brightness_get = eco_led_get; - ret = devm_led_classdev_register(&device->dev, led); + ret = devm_led_classdev_register(dev, led); if (ret) return ret; } @@ -908,102 +916,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) return 0; } -static int acpi_fujitsu_laptop_add(struct acpi_device *device) +static void acpi_fujitsu_laptop_press(struct device *dev, int scancode) { - struct fujitsu_laptop *priv; - int ret, i = 0; - - priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found. Driver may not work as intended."); - fext = device; - - strscpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME); - strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); - device->driver_data = priv; - - /* kfifo */ - spin_lock_init(&priv->fifo_lock); - ret = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int), - GFP_KERNEL); - if (ret) - return ret; - - pr_info("ACPI: %s [%s]\n", - acpi_device_name(device), acpi_device_bid(device)); - - while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 && - i++ < MAX_HOTKEY_RINGBUFFER_SIZE) - ; /* No action, result is discarded */ - acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n", - i); - - priv->flags_supported = call_fext_func(device, FUNC_FLAGS, 0x0, 0x0, - 0x0); - - /* Make sure our bitmask of supported functions is cleared if the - RFKILL function block is not implemented, like on the S7020. */ - if (priv->flags_supported == UNSUPPORTED_CMD) - priv->flags_supported = 0; - - if (priv->flags_supported) - priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, - 0x0); - - /* Suspect this is a keymap of the application panel, print it */ - acpi_handle_info(device->handle, "BTNI: [0x%x]\n", - call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0)); - - /* Sync backlight power status */ - if (fujitsu_bl && fujitsu_bl->bl_device && - acpi_video_get_backlight_type() == acpi_backlight_vendor) { - if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, - BACKLIGHT_PARAM_POWER, 0x0) == BACKLIGHT_OFF) - fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_OFF; - else - fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_ON; - } - - ret = acpi_fujitsu_laptop_input_setup(device); - if (ret) - goto err_free_fifo; - - ret = acpi_fujitsu_laptop_leds_register(device); - if (ret) - goto err_free_fifo; - - ret = fujitsu_laptop_platform_add(device); - if (ret) - goto err_free_fifo; - - ret = fujitsu_battery_charge_control_add(device); - if (ret < 0) - pr_warn("Unable to register battery charge control: %d\n", ret); - - return 0; - -err_free_fifo: - kfifo_free(&priv->fifo); - - return ret; -} - -static void acpi_fujitsu_laptop_remove(struct acpi_device *device) -{ - struct fujitsu_laptop *priv = acpi_driver_data(device); - - fujitsu_battery_charge_control_remove(device); - - fujitsu_laptop_platform_remove(device); - - kfifo_free(&priv->fifo); -} - -static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode) -{ - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); int ret; ret = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode, @@ -1018,9 +933,9 @@ static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode) scancode); } -static void acpi_fujitsu_laptop_release(struct acpi_device *device) +static void acpi_fujitsu_laptop_release(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); int scancode, ret; while (true) { @@ -1034,35 +949,32 @@ static void acpi_fujitsu_laptop_release(struct acpi_device *device) } } -static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event) +static void acpi_fujitsu_laptop_notify(acpi_handle handle, u32 event, void *data) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct device *dev = data; + struct fujitsu_laptop *priv = dev_get_drvdata(dev); unsigned long flags; int scancode, i = 0; unsigned int irb; if (event != ACPI_FUJITSU_NOTIFY_CODE) { - acpi_handle_info(device->handle, "Unsupported event [0x%x]\n", - event); + acpi_handle_info(handle, "Unsupported event [0x%x]\n", event); sparse_keymap_report_event(priv->input, -1, 1, true); return; } if (priv->flags_supported) - priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, - 0x0); + priv->flags_state = call_fext_func(dev, FUNC_FLAGS, 0x4, 0x0, 0x0); - while ((irb = call_fext_func(device, - FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 && + while ((irb = call_fext_func(dev, FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 && i++ < MAX_HOTKEY_RINGBUFFER_SIZE) { scancode = irb & 0x4ff; if (sparse_keymap_entry_from_scancode(priv->input, scancode)) - acpi_fujitsu_laptop_press(device, scancode); + acpi_fujitsu_laptop_press(dev, scancode); else if (scancode == 0) - acpi_fujitsu_laptop_release(device); + acpi_fujitsu_laptop_release(dev); else - acpi_handle_info(device->handle, - "Unknown GIRB result [%x]\n", irb); + acpi_handle_info(handle, "Unknown GIRB result [%x]\n", irb); } /* @@ -1072,13 +984,117 @@ static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event) * status flags queried using FUNC_FLAGS. */ if (priv->flags_supported & (FLAG_SOFTKEYS)) { - flags = call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0); + flags = call_fext_func(dev, FUNC_FLAGS, 0x1, 0x0, 0x0); flags &= (FLAG_SOFTKEYS); for_each_set_bit(i, &flags, BITS_PER_LONG) sparse_keymap_report_event(priv->input, BIT(i), 1, true); } } +static int acpi_fujitsu_laptop_probe(struct platform_device *pdev) +{ + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct fujitsu_laptop *priv; + int ret, i = 0; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found. Driver may not work as intended."); + fext = &pdev->dev; + + strscpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME); + strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); + + platform_set_drvdata(pdev, priv); + + /* kfifo */ + spin_lock_init(&priv->fifo_lock); + ret = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int), + GFP_KERNEL); + if (ret) + return ret; + + pr_info("ACPI: %s [%s]\n", + acpi_device_name(device), acpi_device_bid(device)); + + while (call_fext_func(fext, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 && + i++ < MAX_HOTKEY_RINGBUFFER_SIZE) + ; /* No action, result is discarded */ + acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n", + i); + + priv->flags_supported = call_fext_func(fext, FUNC_FLAGS, 0x0, 0x0, 0x0); + + /* Make sure our bitmask of supported functions is cleared if the + RFKILL function block is not implemented, like on the S7020. */ + if (priv->flags_supported == UNSUPPORTED_CMD) + priv->flags_supported = 0; + + if (priv->flags_supported) + priv->flags_state = call_fext_func(fext, FUNC_FLAGS, 0x4, 0x0, + 0x0); + + /* Suspect this is a keymap of the application panel, print it */ + acpi_handle_info(device->handle, "BTNI: [0x%x]\n", + call_fext_func(fext, FUNC_BUTTONS, 0x0, 0x0, 0x0)); + + /* Sync backlight power status */ + if (fujitsu_bl && fujitsu_bl->bl_device && + acpi_video_get_backlight_type() == acpi_backlight_vendor) { + if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, + BACKLIGHT_PARAM_POWER, 0x0) == BACKLIGHT_OFF) + fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_OFF; + else + fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_ON; + } + + ret = acpi_fujitsu_laptop_input_setup(fext); + if (ret) + goto err_free_fifo; + + ret = acpi_fujitsu_laptop_leds_register(fext); + if (ret) + goto err_free_fifo; + + ret = fujitsu_laptop_platform_add(fext); + if (ret) + goto err_free_fifo; + + ret = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_fujitsu_laptop_notify, fext); + if (ret) + goto err_platform_remove; + + ret = fujitsu_battery_charge_control_add(fext); + if (ret < 0) + pr_warn("Unable to register battery charge control: %d\n", ret); + + return 0; + +err_platform_remove: + fujitsu_laptop_platform_remove(fext); +err_free_fifo: + kfifo_free(&priv->fifo); + + return ret; +} + +static void acpi_fujitsu_laptop_remove(struct platform_device *pdev) +{ + struct fujitsu_laptop *priv = platform_get_drvdata(pdev); + + fujitsu_battery_charge_control_remove(&pdev->dev); + + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), ACPI_DEVICE_NOTIFY, + acpi_fujitsu_laptop_notify); + + fujitsu_laptop_platform_remove(&pdev->dev); + + kfifo_free(&priv->fifo); +} + /* Initialization */ static const struct acpi_device_id fujitsu_bl_device_ids[] = { @@ -1086,14 +1102,13 @@ static const struct acpi_device_id fujitsu_bl_device_ids[] = { {"", 0}, }; -static struct acpi_driver acpi_fujitsu_bl_driver = { - .name = ACPI_FUJITSU_BL_DRIVER_NAME, - .class = ACPI_FUJITSU_CLASS, - .ids = fujitsu_bl_device_ids, - .ops = { - .add = acpi_fujitsu_bl_add, - .notify = acpi_fujitsu_bl_notify, - }, +static struct platform_driver acpi_fujitsu_bl_driver = { + .probe = acpi_fujitsu_bl_probe, + .remove = acpi_fujitsu_bl_remove, + .driver = { + .name = ACPI_FUJITSU_BL_DRIVER_NAME, + .acpi_match_table = fujitsu_bl_device_ids, + }, }; static const struct acpi_device_id fujitsu_laptop_device_ids[] = { @@ -1101,15 +1116,13 @@ static const struct acpi_device_id fujitsu_laptop_device_ids[] = { {"", 0}, }; -static struct acpi_driver acpi_fujitsu_laptop_driver = { - .name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME, - .class = ACPI_FUJITSU_CLASS, - .ids = fujitsu_laptop_device_ids, - .ops = { - .add = acpi_fujitsu_laptop_add, - .remove = acpi_fujitsu_laptop_remove, - .notify = acpi_fujitsu_laptop_notify, - }, +static struct platform_driver acpi_fujitsu_laptop_driver = { + .probe = acpi_fujitsu_laptop_probe, + .remove = acpi_fujitsu_laptop_remove, + .driver = { + .name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME, + .acpi_match_table = fujitsu_laptop_device_ids, + }, }; static const struct acpi_device_id fujitsu_ids[] __used = { @@ -1123,7 +1136,7 @@ static int __init fujitsu_init(void) { int ret; - ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver); + ret = platform_driver_register(&acpi_fujitsu_bl_driver); if (ret) return ret; @@ -1135,7 +1148,7 @@ static int __init fujitsu_init(void) /* Register laptop driver */ - ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver); + ret = platform_driver_register(&acpi_fujitsu_laptop_driver); if (ret) goto err_unregister_platform_driver; @@ -1146,18 +1159,18 @@ static int __init fujitsu_init(void) err_unregister_platform_driver: platform_driver_unregister(&fujitsu_pf_driver); err_unregister_acpi: - acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver); + platform_driver_unregister(&acpi_fujitsu_bl_driver); return ret; } static void __exit fujitsu_cleanup(void) { - acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver); + platform_driver_unregister(&acpi_fujitsu_laptop_driver); platform_driver_unregister(&fujitsu_pf_driver); - acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver); + platform_driver_unregister(&acpi_fujitsu_bl_driver); pr_info("driver unloaded\n"); } diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 17f08ce7552d..8319df28e9b8 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -18,6 +18,7 @@ #include #include #include +#include #define MODULENAME "fujitsu-tablet" @@ -442,14 +443,12 @@ static acpi_status fujitsu_walk_resources(struct acpi_resource *res, void *data) } } -static int acpi_fujitsu_add(struct acpi_device *adev) +static int acpi_fujitsu_probe(struct platform_device *pdev) { + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); acpi_status status; int error; - if (!adev) - return -EINVAL; - status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS, fujitsu_walk_resources, NULL); if (ACPI_FAILURE(status) || !fujitsu.irq || !fujitsu.io_base) @@ -461,7 +460,7 @@ static int acpi_fujitsu_add(struct acpi_device *adev) snprintf(fujitsu.phys, sizeof(fujitsu.phys), "%s/input0", acpi_device_hid(adev)); - error = input_fujitsu_setup(&adev->dev, + error = input_fujitsu_setup(&pdev->dev, acpi_device_name(adev), fujitsu.phys); if (error) return error; @@ -484,7 +483,7 @@ static int acpi_fujitsu_add(struct acpi_device *adev) return 0; } -static void acpi_fujitsu_remove(struct acpi_device *adev) +static void acpi_fujitsu_remove(struct platform_device *pdev) { free_irq(fujitsu.irq, fujitsu_interrupt); release_region(fujitsu.io_base, fujitsu.io_length); @@ -501,15 +500,14 @@ static int acpi_fujitsu_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(acpi_fujitsu_pm, NULL, acpi_fujitsu_resume); -static struct acpi_driver acpi_fujitsu_driver = { - .name = MODULENAME, - .class = "hotkey", - .ids = fujitsu_ids, - .ops = { - .add = acpi_fujitsu_add, - .remove = acpi_fujitsu_remove, +static struct platform_driver acpi_fujitsu_driver = { + .probe = acpi_fujitsu_probe, + .remove = acpi_fujitsu_remove, + .driver = { + .name = MODULENAME, + .acpi_match_table = fujitsu_ids, + .pm = &acpi_fujitsu_pm, }, - .drv.pm = &acpi_fujitsu_pm, }; static int __init fujitsu_module_init(void) @@ -518,7 +516,7 @@ static int __init fujitsu_module_init(void) dmi_check_system(dmi_ids); - error = acpi_bus_register_driver(&acpi_fujitsu_driver); + error = platform_driver_register(&acpi_fujitsu_driver); if (error) return error; @@ -527,7 +525,7 @@ static int __init fujitsu_module_init(void) static void __exit fujitsu_module_exit(void) { - acpi_bus_unregister_driver(&acpi_fujitsu_driver); + platform_driver_unregister(&acpi_fujitsu_driver); } module_init(fujitsu_module_init); diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 7df00447bba4..6950bec2a9d8 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -48,6 +48,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4"); enum hp_ec_offsets { HP_EC_OFFSET_UNKNOWN = 0x00, + HP_NO_THERMAL_PROFILE_OFFSET = 0x01, HP_VICTUS_S_EC_THERMAL_PROFILE_OFFSET = 0x59, HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET = 0x62, HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET = 0x63, @@ -125,6 +126,13 @@ static const struct thermal_profile_params omen_v1_legacy_thermal_params = { .ec_tp_offset = HP_OMEN_EC_THERMAL_PROFILE_OFFSET, }; +static const struct thermal_profile_params omen_v1_no_ec_thermal_params = { + .performance = HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE, + .balanced = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT, + .low_power = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT, + .ec_tp_offset = HP_NO_THERMAL_PROFILE_OFFSET, +}; + /* * A generic pointer for the currently-active board's thermal profile * parameters. @@ -151,6 +159,7 @@ static const char * const omen_thermal_profile_boards[] = { "8900", "8901", "8902", "8912", "8917", "8918", "8949", "894A", "89EB", "8A15", "8A42", "8BAD", + "8C58", "8E41", }; @@ -180,6 +189,10 @@ static const char * const victus_thermal_profile_boards[] = { /* DMI Board names of Victus 16-r and Victus 16-s laptops */ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst = { + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8A44") }, + .driver_data = (void *)&omen_v1_legacy_thermal_params, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8A4D") }, .driver_data = (void *)&omen_v1_legacy_thermal_params, @@ -192,6 +205,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BBE") }, .driver_data = (void *)&victus_s_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BC2") }, + .driver_data = (void *)&omen_v1_thermal_params, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BCA") }, .driver_data = (void *)&omen_v1_thermal_params, @@ -212,6 +229,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C76") }, .driver_data = (void *)&omen_v1_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C77") }, + .driver_data = (void *)&omen_v1_thermal_params, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C78") }, .driver_data = (void *)&omen_v1_thermal_params, @@ -226,7 +247,11 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D41") }, - .driver_data = (void *)&victus_s_thermal_params, + .driver_data = (void *)&omen_v1_no_ec_thermal_params, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D87") }, + .driver_data = (void *)&omen_v1_no_ec_thermal_params, }, {}, }; @@ -397,6 +422,11 @@ static const struct key_entry hp_wmi_keymap[] = { { KE_KEY, 0x21a9, { KEY_TOUCHPAD_OFF } }, { KE_KEY, 0x121a9, { KEY_TOUCHPAD_ON } }, { KE_KEY, 0x231b, { KEY_HELP } }, + { KE_IGNORE, 0x21ab, }, /* FnLock on */ + { KE_IGNORE, 0x121ab, }, /* FnLock off */ + { KE_IGNORE, 0x30021aa, }, /* kbd backlight: level 2 -> off */ + { KE_IGNORE, 0x33221aa, }, /* kbd backlight: off -> level 1 */ + { KE_IGNORE, 0x36421aa, }, /* kbd backlight: level 1 -> level 2*/ { KE_END, 0 } }; @@ -451,23 +481,24 @@ enum pwm_modes { }; struct hp_wmi_hwmon_priv { + struct mutex lock; /* protects mode, pwm */ u8 min_rpm; u8 max_rpm; - u8 gpu_delta; + int gpu_delta; u8 mode; u8 pwm; struct delayed_work keep_alive_dwork; }; struct victus_s_fan_table_header { + u8 num_fans; u8 unknown; - u8 num_entries; } __packed; struct victus_s_fan_table_entry { u8 cpu_rpm; u8 gpu_rpm; - u8 unknown; + u8 noise_db; } __packed; struct victus_s_fan_table { @@ -1828,7 +1859,8 @@ static int platform_profile_victus_s_get_ec(enum platform_profile_option *profil const struct thermal_profile_params *params; params = active_thermal_profile_params; - if (params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) { + if (params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN || + params->ec_tp_offset == HP_NO_THERMAL_PROFILE_OFFSET) { *profile = active_platform_profile; return 0; } @@ -2183,7 +2215,8 @@ static int thermal_profile_setup(struct platform_device *device) * behaves like a wrapper around active_platform_profile, to avoid using * uninitialized data, we default to PLATFORM_PROFILE_BALANCED. */ - if (active_thermal_profile_params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) { + if (active_thermal_profile_params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN || + active_thermal_profile_params->ec_tp_offset == HP_NO_THERMAL_PROFILE_OFFSET) { active_platform_profile = PLATFORM_PROFILE_BALANCED; } else { err = platform_profile_victus_s_get_ec(&active_platform_profile); @@ -2351,13 +2384,16 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) switch (priv->mode) { case PWM_MODE_MAX: - if (is_victus_s_thermal_profile()) - hp_wmi_get_fan_count_userdefine_trigger(); + if (is_victus_s_thermal_profile()) { + ret = hp_wmi_get_fan_count_userdefine_trigger(); + if (ret < 0) + return ret; + } ret = hp_wmi_fan_speed_max_set(1); if (ret < 0) return ret; - schedule_delayed_work(&priv->keep_alive_dwork, - secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); + mod_delayed_work(system_wq, &priv->keep_alive_dwork, + secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); return 0; case PWM_MODE_MANUAL: if (!is_victus_s_thermal_profile()) @@ -2365,26 +2401,26 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv)); if (ret < 0) return ret; - schedule_delayed_work(&priv->keep_alive_dwork, - secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); + mod_delayed_work(system_wq, &priv->keep_alive_dwork, + secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); return 0; case PWM_MODE_AUTO: if (is_victus_s_thermal_profile()) { - hp_wmi_get_fan_count_userdefine_trigger(); + ret = hp_wmi_get_fan_count_userdefine_trigger(); + if (ret < 0) + return ret; ret = hp_wmi_fan_speed_max_reset(priv); } else { ret = hp_wmi_fan_speed_max_set(0); } if (ret < 0) return ret; - cancel_delayed_work_sync(&priv->keep_alive_dwork); + cancel_delayed_work(&priv->keep_alive_dwork); return 0; default: /* shouldn't happen */ return -EINVAL; } - - return 0; } static umode_t hp_wmi_hwmon_is_visible(const void *data, @@ -2417,6 +2453,7 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, { struct hp_wmi_hwmon_priv *priv; int rpm, ret; + u8 mode; priv = dev_get_drvdata(dev); switch (type) { @@ -2440,11 +2477,13 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, *val = rpm_to_pwm(rpm / 100, priv); return 0; } - switch (priv->mode) { + scoped_guard(mutex, &priv->lock) + mode = priv->mode; + switch (mode) { case PWM_MODE_MAX: case PWM_MODE_MANUAL: case PWM_MODE_AUTO: - *val = priv->mode; + *val = mode; return 0; default: /* shouldn't happen */ @@ -2462,6 +2501,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, int rpm; priv = dev_get_drvdata(dev); + guard(mutex)(&priv->lock); switch (type) { case hwmon_pwm: if (attr == hwmon_pwm_input) { @@ -2526,22 +2566,30 @@ static void hp_wmi_hwmon_keep_alive_handler(struct work_struct *work) { struct delayed_work *dwork; struct hp_wmi_hwmon_priv *priv; + int ret; dwork = to_delayed_work(work); priv = container_of(dwork, struct hp_wmi_hwmon_priv, keep_alive_dwork); + + guard(mutex)(&priv->lock); /* * Re-apply the current hwmon context settings. * NOTE: hp_wmi_apply_fan_settings will handle the re-scheduling. */ - hp_wmi_apply_fan_settings(priv); + ret = hp_wmi_apply_fan_settings(priv); + if (ret) + pr_warn_ratelimited("keep-alive failed to refresh fan settings: %d\n", + ret); } static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) { u8 fan_data[128] = { 0 }; struct victus_s_fan_table *fan_table; - u8 min_rpm, max_rpm, gpu_delta; - int ret; + u8 min_rpm, max_rpm; + u8 cpu_rpm, gpu_rpm, noise_db; + int gpu_delta, i, num_entries, ret; + size_t header_size, entry_size; /* Default behaviour on hwmon init is automatic mode */ priv->mode = PWM_MODE_AUTO; @@ -2556,13 +2604,36 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) return ret; fan_table = (struct victus_s_fan_table *)fan_data; - if (fan_table->header.num_entries == 0 || - sizeof(struct victus_s_fan_table_header) + - sizeof(struct victus_s_fan_table_entry) * fan_table->header.num_entries > sizeof(fan_data)) + if (fan_table->header.num_fans == 0) + return -EINVAL; + + header_size = sizeof(struct victus_s_fan_table_header); + entry_size = sizeof(struct victus_s_fan_table_entry); + num_entries = (sizeof(fan_data) - header_size) / entry_size; + min_rpm = U8_MAX; + max_rpm = 0; + + for (i = 0 ; i < num_entries ; i++) { + cpu_rpm = fan_table->entries[i].cpu_rpm; + gpu_rpm = fan_table->entries[i].gpu_rpm; + noise_db = fan_table->entries[i].noise_db; + + /* + * On some devices, the fan table is truncated with an all-zero row, + * hence we stop parsing here. + */ + if (cpu_rpm == 0 && gpu_rpm == 0 && noise_db == 0) + break; + + if (cpu_rpm < min_rpm) + min_rpm = cpu_rpm; + if (cpu_rpm > max_rpm) + max_rpm = cpu_rpm; + } + + if (min_rpm == U8_MAX || max_rpm == 0) return -EINVAL; - min_rpm = fan_table->entries[0].cpu_rpm; - max_rpm = fan_table->entries[fan_table->header.num_entries - 1].cpu_rpm; gpu_delta = fan_table->entries[0].gpu_rpm - fan_table->entries[0].cpu_rpm; priv->min_rpm = min_rpm; priv->max_rpm = max_rpm; @@ -2582,6 +2653,10 @@ static int hp_wmi_hwmon_init(void) if (!priv) return -ENOMEM; + ret = devm_mutex_init(dev, &priv->lock); + if (ret) + return ret; + ret = hp_wmi_setup_fan_settings(priv); if (ret) return ret; @@ -2595,7 +2670,9 @@ static int hp_wmi_hwmon_init(void) INIT_DELAYED_WORK(&priv->keep_alive_dwork, hp_wmi_hwmon_keep_alive_handler); platform_set_drvdata(hp_wmi_platform_dev, priv); - hp_wmi_apply_fan_settings(priv); + ret = hp_wmi_apply_fan_settings(priv); + if (ret) + dev_warn(dev, "Failed to apply initial fan settings: %d\n", ret); return 0; } diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 1c65ce87cde0..115bb37577a1 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -212,7 +212,11 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 *gpio_flags = GPIO_ACTIVE_HIGH; break; case INT3472_GPIO_TYPE_PRIVACY_LED: - *con_id = "privacy-led"; + *con_id = "privacy"; + *gpio_flags = GPIO_ACTIVE_HIGH; + break; + case INT3472_GPIO_TYPE_STROBE: + *con_id = "ir_flood"; *gpio_flags = GPIO_ACTIVE_HIGH; break; case INT3472_GPIO_TYPE_HOTPLUG_DETECT: @@ -252,6 +256,7 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 * * 0x00 Reset * 0x01 Power down + * 0x02 Strobe * 0x0b Power enable * 0x0c Clock enable * 0x0d Privacy LED @@ -336,6 +341,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case INT3472_GPIO_TYPE_CLK_ENABLE: case INT3472_GPIO_TYPE_PRIVACY_LED: + case INT3472_GPIO_TYPE_STROBE: case INT3472_GPIO_TYPE_POWER_ENABLE: case INT3472_GPIO_TYPE_DOVDD: case INT3472_GPIO_TYPE_HANDSHAKE: @@ -354,7 +360,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case INT3472_GPIO_TYPE_PRIVACY_LED: - ret = skl_int3472_register_pled(int3472, gpio); + case INT3472_GPIO_TYPE_STROBE: + ret = skl_int3472_register_led(int3472, gpio, con_id); if (ret) err_msg = "Failed to register LED\n"; @@ -429,7 +436,7 @@ void int3472_discrete_cleanup(struct int3472_discrete_device *int3472) gpiod_remove_lookup_table(&int3472->gpios); skl_int3472_unregister_clock(int3472); - skl_int3472_unregister_pled(int3472); + skl_int3472_unregister_leds(int3472); skl_int3472_unregister_regulator(int3472); } EXPORT_SYMBOL_NS_GPL(int3472_discrete_cleanup, "INTEL_INT3472_DISCRETE"); diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c index b1d84b968112..9b2573cc347b 100644 --- a/drivers/platform/x86/intel/int3472/led.c +++ b/drivers/platform/x86/intel/int3472/led.c @@ -6,55 +6,58 @@ #include #include -static int int3472_pled_set(struct led_classdev *led_cdev, - enum led_brightness brightness) +static int int3472_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) { - struct int3472_discrete_device *int3472 = - container_of(led_cdev, struct int3472_discrete_device, pled.classdev); + struct int3472_led *led = container_of(led_cdev, struct int3472_led, classdev); - gpiod_set_value_cansleep(int3472->pled.gpio, brightness); + gpiod_set_value_cansleep(led->gpio, brightness); return 0; } -int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gpio_desc *gpio) +int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, + const char *con_id) { + struct int3472_led *led; char *p; int ret; - if (int3472->pled.classdev.dev) - return -EBUSY; + if (int3472->n_leds >= INT3472_MAX_LEDS) + return -ENOSPC; - int3472->pled.gpio = gpio; + led = &int3472->leds[int3472->n_leds]; + led->gpio = gpio; /* Generate the name, replacing the ':' in the ACPI devname with '_' */ - snprintf(int3472->pled.name, sizeof(int3472->pled.name), - "%s::privacy_led", acpi_dev_name(int3472->sensor)); - p = strchr(int3472->pled.name, ':'); + snprintf(led->name, sizeof(led->name), + "%s::%s_led", acpi_dev_name(int3472->sensor), con_id); + p = strchr(led->name, ':'); if (p) *p = '_'; - int3472->pled.classdev.name = int3472->pled.name; - int3472->pled.classdev.max_brightness = 1; - int3472->pled.classdev.brightness_set_blocking = int3472_pled_set; + led->classdev.name = led->name; + led->classdev.max_brightness = 1; + led->classdev.brightness_set_blocking = int3472_led_set; - ret = led_classdev_register(int3472->dev, &int3472->pled.classdev); + ret = led_classdev_register(int3472->dev, &led->classdev); if (ret) return ret; - int3472->pled.lookup.provider = int3472->pled.name; - int3472->pled.lookup.dev_id = int3472->sensor_name; - int3472->pled.lookup.con_id = "privacy"; - led_add_lookup(&int3472->pled.lookup); + led->lookup.provider = led->name; + led->lookup.dev_id = int3472->sensor_name; + led->lookup.con_id = con_id; + led_add_lookup(&led->lookup); + int3472->n_leds++; return 0; } -void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472) +void skl_int3472_unregister_leds(struct int3472_discrete_device *int3472) { - if (IS_ERR_OR_NULL(int3472->pled.classdev.dev)) - return; + for (unsigned int i = 0; i < int3472->n_leds; i++) { + struct int3472_led *led = &int3472->leds[i]; - led_remove_lookup(&int3472->pled.lookup); - led_classdev_unregister(&int3472->pled.classdev); - gpiod_put(int3472->pled.gpio); + led_remove_lookup(&led->lookup); + led_classdev_unregister(&led->classdev); + gpiod_put(led->gpio); + } } diff --git a/drivers/platform/x86/intel/plr_tpmi.c b/drivers/platform/x86/intel/plr_tpmi.c index 05727169f49c..8faecc311038 100644 --- a/drivers/platform/x86/intel/plr_tpmi.c +++ b/drivers/platform/x86/intel/plr_tpmi.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,8 @@ struct tpmi_plr { struct tpmi_plr_die *die_info; int num_dies; struct auxiliary_device *auxdev; + struct notifier_block nb; + struct mutex lock; /* Protect access to dbgfs_dir */ }; static const char * const plr_coarse_reasons[] = { @@ -255,6 +258,30 @@ static ssize_t plr_status_write(struct file *filp, const char __user *ubuf, } DEFINE_SHOW_STORE_ATTRIBUTE(plr_status); +static int intel_plr_notify(struct notifier_block *self, unsigned long action, void *data) +{ + struct tpmi_plr *plr = container_of(self, struct tpmi_plr, nb); + + if (action == TPMI_CORE_EXIT) { + guard(mutex)(&plr->lock); + plr->dbgfs_dir = NULL; + } + + return NOTIFY_DONE; +} + +static int intel_plr_register_notifier(struct notifier_block *nb) +{ + nb->notifier_call = intel_plr_notify; + nb->priority = 0; + return tpmi_register_notifier(nb); +} + +static void intel_plr_unregister_notifier(struct notifier_block *nb) +{ + tpmi_unregister_notifier(nb); +} + static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { struct oobmsm_plat_info *plat_info; @@ -282,10 +309,18 @@ static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxilia if (!plr) return -ENOMEM; + err = devm_mutex_init(&auxdev->dev, &plr->lock); + if (err) + return err; + + intel_plr_register_notifier(&plr->nb); + plr->die_info = devm_kcalloc(&auxdev->dev, num_resources, sizeof(*plr->die_info), GFP_KERNEL); - if (!plr->die_info) - return -ENOMEM; + if (!plr->die_info) { + err = -ENOMEM; + goto err_notify; + } plr->num_dies = num_resources; plr->dbgfs_dir = debugfs_create_dir("plr", dentry); @@ -326,6 +361,9 @@ static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxilia err: debugfs_remove_recursive(plr->dbgfs_dir); +err_notify: + intel_plr_unregister_notifier(&plr->nb); + return err; } @@ -333,6 +371,9 @@ static void intel_plr_remove(struct auxiliary_device *auxdev) { struct tpmi_plr *plr = auxiliary_get_drvdata(auxdev); + intel_plr_unregister_notifier(&plr->nb); + + guard(mutex)(&plr->lock); debugfs_remove_recursive(plr->dbgfs_dir); } diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 02b303418d18..d91e1ab842d6 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1315,7 +1315,7 @@ static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev, unsigned int i; for (i = 0; guids[i]; i++) { - ep = pmt_telem_find_and_register_endpoint(pcidev, guids[i], 0); + ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, guids[i], 0); if (!IS_ERR(ep)) return ep; } @@ -1600,7 +1600,7 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * if (!pmc->map->lpm_req_guid) return -ENXIO; - ep = pmt_telem_find_and_register_endpoint(pcidev, pmc->map->lpm_req_guid, 0); + ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, pmc->map->lpm_req_guid, 0); if (IS_ERR(ep)) { dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep); return -EPROBE_DEFER; diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 03fad9331fc0..6f6e83e70fc5 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -60,7 +60,7 @@ pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem info.base_addr = ssram_base; info.parent = &pcidev->dev; - return intel_vsec_register(pcidev, &info); + return intel_vsec_register(&pcidev->dev, &info); } static inline u64 get_base(void __iomem *addr, u32 offset) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index be3c8d9e4fff..b4c9964df807 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -60,11 +60,11 @@ pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count) return count; } -int pmt_telem_read_mmio(struct pci_dev *pdev, struct pmt_callbacks *cb, u32 guid, void *buf, +int pmt_telem_read_mmio(struct device *dev, struct pmt_callbacks *cb, u32 guid, void *buf, void __iomem *addr, loff_t off, u32 count) { if (cb && cb->read_telem) - return cb->read_telem(pdev, guid, buf, off, count); + return cb->read_telem(dev, guid, buf, off, count); addr += off; @@ -99,7 +99,7 @@ intel_pmt_read(struct file *filp, struct kobject *kobj, if (count > entry->size - off) count = entry->size - off; - count = pmt_telem_read_mmio(entry->pcidev, entry->cb, entry->header.guid, buf, + count = pmt_telem_read_mmio(entry->ep->dev, entry->cb, entry->header.guid, buf, entry->base, off, count); return count; @@ -208,7 +208,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, struct intel_vsec_device *ivdev, struct resource *disc_res) { - struct pci_dev *pci_dev = ivdev->pcidev; + struct pci_dev *pci_dev = to_pci_dev(ivdev->dev); struct device *dev = &ivdev->auxdev.dev; struct intel_pmt_header *header = &entry->header; u8 bir; diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 3c5ad5f52bca..1ae56a5baad2 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -19,11 +19,12 @@ #define GET_BIR(v) ((v) & GENMASK(2, 0)) #define GET_ADDRESS(v) ((v) & GENMASK(31, 3)) +struct device; struct pci_dev; extern struct class intel_pmt_class; struct telem_endpoint { - struct pci_dev *pcidev; + struct device *dev; struct telem_header header; struct pmt_callbacks *cb; void __iomem *base; @@ -65,7 +66,7 @@ struct intel_pmt_namespace { struct intel_pmt_entry *entry); }; -int pmt_telem_read_mmio(struct pci_dev *pdev, struct pmt_callbacks *cb, u32 guid, void *buf, +int pmt_telem_read_mmio(struct device *dev, struct pmt_callbacks *cb, u32 guid, void *buf, void __iomem *addr, loff_t off, u32 count); bool intel_pmt_is_early_client_hw(struct device *dev); int intel_pmt_dev_create(struct intel_pmt_entry *entry, diff --git a/drivers/platform/x86/intel/pmt/discovery.c b/drivers/platform/x86/intel/pmt/discovery.c index e500aa327d23..c482368bfaae 100644 --- a/drivers/platform/x86/intel/pmt/discovery.c +++ b/drivers/platform/x86/intel/pmt/discovery.c @@ -542,7 +542,7 @@ static int pmt_features_probe(struct auxiliary_device *auxdev, const struct auxi if (!priv) return -ENOMEM; - priv->parent = &ivdev->pcidev->dev; + priv->parent = ivdev->dev; auxiliary_set_drvdata(auxdev, priv); priv->dev = device_create(&intel_pmt_class, &auxdev->dev, MKDEV(0, 0), priv, @@ -609,7 +609,7 @@ void intel_pmt_get_features(struct intel_pmt_entry *entry) mutex_lock(&feature_list_lock); list_for_each_entry(feature, &pmt_feature_list, list) { - if (feature->priv->parent != &entry->ep->pcidev->dev) + if (feature->priv->parent != entry->ep->dev) continue; pmt_get_features(entry, feature); diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index a52803bfe124..bdc7c24a3678 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -112,7 +112,7 @@ static int pmt_telem_add_endpoint(struct intel_vsec_device *ivdev, return -ENOMEM; ep = entry->ep; - ep->pcidev = ivdev->pcidev; + ep->dev = ivdev->dev; ep->header.access_type = entry->header.access_type; ep->header.guid = entry->header.guid; ep->header.base_offset = entry->header.base_offset; @@ -204,7 +204,7 @@ int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info) goto unlock; } - info->pdev = entry->ep->pcidev; + info->dev = entry->ep->dev; info->header = entry->ep->header; unlock: @@ -218,9 +218,10 @@ static int pmt_copy_region(struct telemetry_region *region, struct intel_pmt_entry *entry) { + struct pci_dev *pdev = to_pci_dev(entry->ep->dev); struct oobmsm_plat_info *plat_info; - plat_info = intel_vsec_get_mapping(entry->ep->pcidev); + plat_info = intel_vsec_get_mapping(pdev); if (IS_ERR(plat_info)) return PTR_ERR(plat_info); @@ -308,7 +309,7 @@ int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count) if (offset + NUM_BYTES_QWORD(count) > size) return -EINVAL; - pmt_telem_read_mmio(ep->pcidev, ep->cb, ep->header.guid, data, ep->base, offset, + pmt_telem_read_mmio(ep->dev, ep->cb, ep->header.guid, data, ep->base, offset, NUM_BYTES_QWORD(count)); return ep->present ? 0 : -EPIPE; @@ -335,7 +336,7 @@ int pmt_telem_read32(struct telem_endpoint *ep, u32 id, u32 *data, u32 count) EXPORT_SYMBOL_NS_GPL(pmt_telem_read32, "INTEL_PMT_TELEMETRY"); struct telem_endpoint * -pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, u32 guid, u16 pos) +pmt_telem_find_and_register_endpoint(struct device *dev, u32 guid, u16 pos) { int devid = 0; int inst = 0; @@ -348,7 +349,7 @@ pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, u32 guid, u16 pos) if (err) return ERR_PTR(err); - if (ep_info.header.guid == guid && ep_info.pdev == pcidev) { + if (ep_info.header.guid == guid && ep_info.dev == dev) { if (inst == pos) return pmt_telem_register_endpoint(devid); ++inst; diff --git a/drivers/platform/x86/intel/pmt/telemetry.h b/drivers/platform/x86/intel/pmt/telemetry.h index d45af5512b4e..0f88c5e7d90e 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.h +++ b/drivers/platform/x86/intel/pmt/telemetry.h @@ -6,8 +6,8 @@ #define PMT_TELEM_TELEMETRY 0 #define PMT_TELEM_CRASHLOG 1 +struct device; struct telem_endpoint; -struct pci_dev; struct telem_header { u8 access_type; @@ -17,7 +17,7 @@ struct telem_header { }; struct telem_endpoint_info { - struct pci_dev *pdev; + struct device *dev; struct telem_header header; }; @@ -71,8 +71,8 @@ int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info); /** * pmt_telem_find_and_register_endpoint() - Get a telemetry endpoint from - * pci_dev device, guid and pos - * @pdev: PCI device inside the Intel vsec + * device, guid and pos + * @dev: device inside the Intel vsec * @guid: GUID of the telemetry space * @pos: Instance of the guid * @@ -80,8 +80,8 @@ int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info); * * endpoint - On success returns pointer to the telemetry endpoint * * -ENXIO - telemetry endpoint not found */ -struct telem_endpoint *pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, - u32 guid, u16 pos); +struct telem_endpoint * +pmt_telem_find_and_register_endpoint(struct device *dev, u32 guid, u16 pos); /** * pmt_telem_read() - Read qwords from counter sram using sample id diff --git a/drivers/platform/x86/intel/rst.c b/drivers/platform/x86/intel/rst.c index f3a60e14d4c1..4bd10927aad9 100644 --- a/drivers/platform/x86/intel/rst.c +++ b/drivers/platform/x86/intel/rst.c @@ -5,6 +5,7 @@ #include #include +#include #include MODULE_DESCRIPTION("Intel Rapid Start Technology Driver"); @@ -99,8 +100,9 @@ static struct device_attribute irst_timeout_attr = { .store = irst_store_wakeup_time }; -static int irst_add(struct acpi_device *acpi) +static int irst_probe(struct platform_device *pdev) { + struct acpi_device *acpi = ACPI_COMPANION(&pdev->dev); int error; error = device_create_file(&acpi->dev, &irst_timeout_attr); @@ -114,8 +116,10 @@ static int irst_add(struct acpi_device *acpi) return error; } -static void irst_remove(struct acpi_device *acpi) +static void irst_remove(struct platform_device *pdev) { + struct acpi_device *acpi = ACPI_COMPANION(&pdev->dev); + device_remove_file(&acpi->dev, &irst_wakeup_attr); device_remove_file(&acpi->dev, &irst_timeout_attr); } @@ -125,16 +129,15 @@ static const struct acpi_device_id irst_ids[] = { {"", 0} }; -static struct acpi_driver irst_driver = { - .name = "intel_rapid_start", - .class = "intel_rapid_start", - .ids = irst_ids, - .ops = { - .add = irst_add, - .remove = irst_remove, +static struct platform_driver irst_driver = { + .probe = irst_probe, + .remove = irst_remove, + .driver = { + .name = "intel_rapid_start", + .acpi_match_table = irst_ids, }, }; -module_acpi_driver(irst_driver); +module_platform_driver(irst_driver); MODULE_DEVICE_TABLE(acpi, irst_ids); diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index da75f53d0bcc..d7e37d4ace23 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -599,13 +599,14 @@ static int sdsi_get_layout(struct sdsi_priv *priv, struct disc_table *table) return 0; } -static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *parent, +static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct device *dev, struct disc_table *disc_table, struct resource *disc_res) { u32 access_type = FIELD_GET(DT_ACCESS_TYPE, disc_table->access_info); u32 size = FIELD_GET(DT_SIZE, disc_table->access_info); u32 tbir = FIELD_GET(DT_TBIR, disc_table->offset); u32 offset = DT_OFFSET(disc_table->offset); + struct pci_dev *parent = to_pci_dev(dev); struct resource res = {}; /* Starting location of SDSi MMIO region based on access type */ @@ -681,7 +682,7 @@ static int sdsi_probe(struct auxiliary_device *auxdev, const struct auxiliary_de return ret; /* Map the SDSi mailbox registers */ - ret = sdsi_map_mbox_registers(priv, intel_cap_dev->pcidev, &disc_table, disc_res); + ret = sdsi_map_mbox_registers(priv, intel_cap_dev->dev, &disc_table, disc_res); if (ret) return ret; diff --git a/drivers/platform/x86/intel/smartconnect.c b/drivers/platform/x86/intel/smartconnect.c index 31019a1a6d5e..4d866b6366d6 100644 --- a/drivers/platform/x86/intel/smartconnect.c +++ b/drivers/platform/x86/intel/smartconnect.c @@ -5,22 +5,24 @@ #include #include +#include MODULE_DESCRIPTION("Intel Smart Connect disabling driver"); MODULE_LICENSE("GPL"); -static int smartconnect_acpi_init(struct acpi_device *acpi) +static int smartconnect_acpi_probe(struct platform_device *pdev) { + acpi_handle handle = ACPI_HANDLE(&pdev->dev); unsigned long long value; acpi_status status; - status = acpi_evaluate_integer(acpi->handle, "GAOS", NULL, &value); + status = acpi_evaluate_integer(handle, "GAOS", NULL, &value); if (ACPI_FAILURE(status)) return -EINVAL; if (value & 0x1) { - dev_info(&acpi->dev, "Disabling Intel Smart Connect\n"); - status = acpi_execute_simple_method(acpi->handle, "SAOS", 0); + dev_info(&pdev->dev, "Disabling Intel Smart Connect\n"); + status = acpi_execute_simple_method(handle, "SAOS", 0); } return 0; @@ -32,13 +34,12 @@ static const struct acpi_device_id smartconnect_ids[] = { }; MODULE_DEVICE_TABLE(acpi, smartconnect_ids); -static struct acpi_driver smartconnect_driver = { - .name = "intel_smart_connect", - .class = "intel_smart_connect", - .ids = smartconnect_ids, - .ops = { - .add = smartconnect_acpi_init, +static struct platform_driver smartconnect_driver = { + .probe = smartconnect_acpi_probe, + .driver = { + .name = "intel_smart_connect", + .acpi_match_table = smartconnect_ids, }, }; -module_acpi_driver(smartconnect_driver); +module_platform_driver(smartconnect_driver); diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 5059d320edf8..7d5dbc1c1d05 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include #include #define PMT_XA_START 0 @@ -42,7 +44,7 @@ enum vsec_device_state { }; struct vsec_priv { - struct intel_vsec_platform_info *info; + const struct intel_vsec_platform_info *info; struct device *suppliers[VSEC_FEATURE_COUNT]; struct oobmsm_plat_info plat_info; enum vsec_device_state state[VSEC_FEATURE_COUNT]; @@ -109,6 +111,7 @@ static void intel_vsec_dev_release(struct device *dev) ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); + kfree(intel_vsec_dev->acpi_disc); kfree(intel_vsec_dev->resource); kfree(intel_vsec_dev); } @@ -158,18 +161,23 @@ static bool vsec_driver_present(int cap_id) */ static const struct pci_device_id intel_vsec_pci_ids[]; -static int intel_vsec_link_devices(struct pci_dev *pdev, struct device *dev, +static int intel_vsec_link_devices(struct device *parent, struct device *dev, int consumer_id) { const struct vsec_feature_dependency *deps; enum vsec_device_state *state; struct device **suppliers; struct vsec_priv *priv; + struct pci_dev *pdev; int supplier_id; if (!consumer_id) return 0; + if (!dev_is_pci(parent)) + return 0; + + pdev = to_pci_dev(parent); if (!pci_match_id(intel_vsec_pci_ids, pdev)) return 0; @@ -204,7 +212,7 @@ static int intel_vsec_link_devices(struct pci_dev *pdev, struct device *dev, return 0; } -int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, +int intel_vsec_add_aux(struct device *parent, struct intel_vsec_device *intel_vsec_dev, const char *name) { @@ -252,7 +260,7 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, if (ret) goto cleanup_aux; - ret = intel_vsec_link_devices(pdev, &auxdev->dev, intel_vsec_dev->cap_id); + ret = intel_vsec_link_devices(parent, &auxdev->dev, intel_vsec_dev->cap_id); if (ret) goto cleanup_aux; @@ -269,33 +277,32 @@ cleanup_aux: } EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, "INTEL_VSEC"); -static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, - struct intel_vsec_platform_info *info, - unsigned long cap_id) +static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *header, + const struct intel_vsec_platform_info *info, + unsigned long cap_id, u64 base_addr) { struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL; struct resource __free(kfree) *res = NULL; struct resource *tmp; struct device *parent; unsigned long quirks = info->quirks; - u64 base_addr; int i; if (info->parent) parent = info->parent; else - parent = &pdev->dev; + parent = dev; if (!intel_vsec_supported(header->id, info->caps)) return -EINVAL; if (!header->num_entries) { - dev_dbg(&pdev->dev, "Invalid 0 entry count for header id %d\n", header->id); + dev_dbg(dev, "Invalid 0 entry count for header id %d\n", header->id); return -EINVAL; } if (!header->entry_size) { - dev_dbg(&pdev->dev, "Invalid 0 entry size for header id %d\n", header->id); + dev_dbg(dev, "Invalid 0 entry size for header id %d\n", header->id); return -EINVAL; } @@ -310,17 +317,19 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he if (quirks & VSEC_QUIRK_TABLE_SHIFT) header->offset >>= TABLE_OFFSET_SHIFT; - if (info->base_addr) - base_addr = info->base_addr; - else - base_addr = pdev->resource[header->tbir].start; - /* * The DVSEC/VSEC contains the starting offset and count for a block of * discovery tables. Create a resource array of these tables to the * auxiliary device driver. */ for (i = 0, tmp = res; i < header->num_entries; i++, tmp++) { + /* + * Skip resource mapping check for ACPI-based discovery + * since those tables are read from _DSD, not MMIO. + */ + if (info->src == INTEL_VSEC_DISC_ACPI) + break; + tmp->start = base_addr + header->offset + i * (header->entry_size * sizeof(u32)); tmp->end = tmp->start + (header->entry_size * sizeof(u32)) - 1; tmp->flags = IORESOURCE_MEM; @@ -332,13 +341,26 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he release_mem_region(tmp->start, resource_size(tmp)); } - intel_vsec_dev->pcidev = pdev; + intel_vsec_dev->dev = dev; intel_vsec_dev->resource = no_free_ptr(res); intel_vsec_dev->num_resources = header->num_entries; intel_vsec_dev->quirks = info->quirks; intel_vsec_dev->base_addr = info->base_addr; intel_vsec_dev->priv_data = info->priv_data; intel_vsec_dev->cap_id = cap_id; + intel_vsec_dev->src = info->src; + + if (info->src == INTEL_VSEC_DISC_ACPI) { + size_t bytes; + + if (check_mul_overflow(intel_vsec_dev->num_resources, + sizeof(*info->acpi_disc), &bytes)) + return -EOVERFLOW; + + intel_vsec_dev->acpi_disc = kmemdup(info->acpi_disc, bytes, GFP_KERNEL); + if (!intel_vsec_dev->acpi_disc) + return -ENOMEM; + } if (header->id == VSEC_ID_SDSI) intel_vsec_dev->ida = &intel_vsec_sdsi_ida; @@ -349,7 +371,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he * Pass the ownership of intel_vsec_dev and resource within it to * intel_vsec_add_aux() */ - return intel_vsec_add_aux(pdev, parent, no_free_ptr(intel_vsec_dev), + return intel_vsec_add_aux(parent, no_free_ptr(intel_vsec_dev), intel_vsec_name(header->id)); } @@ -410,12 +432,14 @@ static int get_cap_id(u32 header_id, unsigned long *cap_id) return 0; } -static int intel_vsec_register_device(struct pci_dev *pdev, +static int intel_vsec_register_device(struct device *dev, struct intel_vsec_header *header, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info, + u64 base_addr) { const struct vsec_feature_dependency *consumer_deps; struct vsec_priv *priv; + struct pci_dev *pdev; unsigned long cap_id; int ret; @@ -427,8 +451,12 @@ static int intel_vsec_register_device(struct pci_dev *pdev, * Only track dependencies for devices probed by the VSEC driver. * For others using the exported APIs, add the device directly. */ + if (!dev_is_pci(dev)) + return intel_vsec_add_dev(dev, header, info, cap_id, base_addr); + + pdev = to_pci_dev(dev); if (!pci_match_id(intel_vsec_pci_ids, pdev)) - return intel_vsec_add_dev(pdev, header, info, cap_id); + return intel_vsec_add_dev(dev, header, info, cap_id, base_addr); priv = pci_get_drvdata(pdev); if (priv->state[cap_id] == STATE_REGISTERED || @@ -444,7 +472,7 @@ static int intel_vsec_register_device(struct pci_dev *pdev, consumer_deps = get_consumer_dependencies(priv, cap_id); if (!consumer_deps || suppliers_ready(priv, consumer_deps, cap_id)) { - ret = intel_vsec_add_dev(pdev, header, info, cap_id); + ret = intel_vsec_add_dev(dev, header, info, cap_id, base_addr); if (ret) priv->state[cap_id] = STATE_SKIP; else @@ -456,24 +484,23 @@ static int intel_vsec_register_device(struct pci_dev *pdev, return -EAGAIN; } -static bool intel_vsec_walk_header(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) +static int intel_vsec_walk_header(struct device *dev, + const struct intel_vsec_platform_info *info) { struct intel_vsec_header **header = info->headers; - bool have_devices = false; int ret; for ( ; *header; header++) { - ret = intel_vsec_register_device(pdev, *header, info); - if (!ret) - have_devices = true; + ret = intel_vsec_register_device(dev, *header, info, info->base_addr); + if (ret) + return ret; } - return have_devices; + return 0; } static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { bool have_devices = false; int pos = 0; @@ -512,7 +539,8 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); header.id = PCI_DVSEC_HEADER2_ID(hdr); - ret = intel_vsec_register_device(pdev, &header, info); + ret = intel_vsec_register_device(&pdev->dev, &header, info, + pci_resource_start(pdev, header.tbir)); if (ret) continue; @@ -523,7 +551,7 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, } static bool intel_vsec_walk_vsec(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { bool have_devices = false; int pos = 0; @@ -557,7 +585,8 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, header.tbir = INTEL_DVSEC_TABLE_BAR(table); header.offset = INTEL_DVSEC_TABLE_OFFSET(table); - ret = intel_vsec_register_device(pdev, &header, info); + ret = intel_vsec_register_device(&pdev->dev, &header, info, + pci_resource_start(pdev, header.tbir)); if (ret) continue; @@ -567,21 +596,18 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, return have_devices; } -int intel_vsec_register(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) +int intel_vsec_register(struct device *dev, + const struct intel_vsec_platform_info *info) { - if (!pdev || !info || !info->headers) + if (!dev || !info || !info->headers) return -EINVAL; - if (!intel_vsec_walk_header(pdev, info)) - return -ENODEV; - else - return 0; + return intel_vsec_walk_header(dev, info); } EXPORT_SYMBOL_NS_GPL(intel_vsec_register, "INTEL_VSEC"); static bool intel_vsec_get_features(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { bool found = false; @@ -599,7 +625,7 @@ static bool intel_vsec_get_features(struct pci_dev *pdev, found = true; if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && - intel_vsec_walk_header(pdev, info)) + intel_vsec_walk_header(&pdev->dev, info)) found = true; return found; @@ -625,7 +651,7 @@ static void intel_vsec_skip_missing_dependencies(struct pci_dev *pdev) static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct intel_vsec_platform_info *info; + const struct intel_vsec_platform_info *info; struct vsec_priv *priv; int num_caps, ret; int run_once = 0; @@ -636,7 +662,7 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id return ret; pci_save_state(pdev); - info = (struct intel_vsec_platform_info *)id->driver_data; + info = (const struct intel_vsec_platform_info *)id->driver_data; if (!info) return -EINVAL; @@ -671,7 +697,10 @@ int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, { struct vsec_priv *priv; - priv = pci_get_drvdata(vsec_dev->pcidev); + if (!dev_is_pci(vsec_dev->dev)) + return -ENODEV; + + priv = pci_get_drvdata(to_pci_dev(vsec_dev->dev)); if (!priv) return -EINVAL; @@ -819,7 +848,7 @@ static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev) xa_for_each(&auxdev_array, index, intel_vsec_dev) { /* check if pdev doesn't match */ - if (pdev != intel_vsec_dev->pcidev) + if (&pdev->dev != intel_vsec_dev->dev) continue; devm_release_action(&pdev->dev, intel_vsec_remove_aux, &intel_vsec_dev->auxdev); diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 98846e88d3d0..16fd7aa41f20 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -46,6 +46,7 @@ * provided by the Intel VSEC driver. */ +#include #include #include #include @@ -55,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -187,6 +189,20 @@ struct tpmi_feature_state { /* Used during auxbus device creation */ static DEFINE_IDA(intel_vsec_tpmi_ida); +static BLOCKING_NOTIFIER_HEAD(tpmi_notify_list); + +int tpmi_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&tpmi_notify_list, nb); +} +EXPORT_SYMBOL_NS_GPL(tpmi_register_notifier, "INTEL_TPMI"); + +int tpmi_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&tpmi_notify_list, nb); +} +EXPORT_SYMBOL_NS_GPL(tpmi_unregister_notifier, "INTEL_TPMI"); + struct oobmsm_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev) { struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev); @@ -479,6 +495,9 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l addr = array[2]; value = array[3]; + if (!IS_ALIGNED(addr, sizeof(u32))) + return -EINVAL; + if (punit >= pfs->pfs_header.num_entries) { ret = -EINVAL; goto exit_write; @@ -530,7 +549,7 @@ static const struct file_operations mem_write_ops = { .release = single_release, }; -#define tpmi_to_dev(info) (&info->vsec_dev->pcidev->dev) +#define tpmi_to_dev(info) ((info)->vsec_dev->dev) static void tpmi_dbgfs_register(struct intel_tpmi_info *tpmi_info) { @@ -642,7 +661,7 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info, tmp->flags = IORESOURCE_MEM; } - feature_vsec_dev->pcidev = vsec_dev->pcidev; + feature_vsec_dev->dev = vsec_dev->dev; feature_vsec_dev->resource = res; feature_vsec_dev->num_resources = pfs->pfs_header.num_entries; feature_vsec_dev->priv_data = &tpmi_info->plat_info; @@ -655,7 +674,7 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info, * feature_vsec_dev and res memory are also freed as part of * device deletion. */ - return intel_vsec_add_aux(vsec_dev->pcidev, &vsec_dev->auxdev.dev, + return intel_vsec_add_aux(&vsec_dev->auxdev.dev, feature_vsec_dev, feature_id_name); } @@ -742,7 +761,7 @@ static int tpmi_fetch_pfs_header(struct intel_tpmi_pm_feature *pfs, u64 start, i static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev) { struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev); - struct pci_dev *pci_dev = vsec_dev->pcidev; + struct pci_dev *pci_dev = to_pci_dev(vsec_dev->dev); struct intel_tpmi_info *tpmi_info; u64 pfs_start = 0; int ret, i; @@ -813,10 +832,6 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev) auxiliary_set_drvdata(auxdev, tpmi_info); - ret = tpmi_create_devices(tpmi_info); - if (ret) - return ret; - /* * Allow debugfs when security policy allows. Everything this debugfs * interface provides, can also be done via /dev/mem access. If @@ -826,6 +841,14 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev) if (!security_locked_down(LOCKDOWN_DEV_MEM) && capable(CAP_SYS_RAWIO)) tpmi_dbgfs_register(tpmi_info); + ret = tpmi_create_devices(tpmi_info); + if (ret) { + debugfs_remove_recursive(tpmi_info->dbgfs_dir); + return ret; + } + + blocking_notifier_call_chain(&tpmi_notify_list, TPMI_CORE_INIT, auxdev); + return 0; } @@ -839,6 +862,8 @@ static void tpmi_remove(struct auxiliary_device *auxdev) { struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(auxdev); + blocking_notifier_call_chain(&tpmi_notify_list, TPMI_CORE_EXIT, auxdev); + debugfs_remove_recursive(tpmi_info->dbgfs_dir); } diff --git a/drivers/platform/x86/intel/wmi/sbl-fw-update.c b/drivers/platform/x86/intel/wmi/sbl-fw-update.c index 3716ccaaed6a..62c9c7f1842b 100644 --- a/drivers/platform/x86/intel/wmi/sbl-fw-update.c +++ b/drivers/platform/x86/intel/wmi/sbl-fw-update.c @@ -28,15 +28,10 @@ static int get_fwu_request(struct device *dev, u32 *out) __le32 *result; int ret; - ret = wmidev_query_block(to_wmi_device(dev), 0, &buffer); + ret = wmidev_query_block(to_wmi_device(dev), 0, &buffer, sizeof(*result)); if (ret < 0) return ret; - if (buffer.length < sizeof(*result)) { - kfree(buffer.data); - return -ENODATA; - } - result = buffer.data; *out = le32_to_cpu(*result); kfree(result); diff --git a/drivers/platform/x86/intel/wmi/thunderbolt.c b/drivers/platform/x86/intel/wmi/thunderbolt.c index 47017f2d7597..9b1920d61674 100644 --- a/drivers/platform/x86/intel/wmi/thunderbolt.c +++ b/drivers/platform/x86/intel/wmi/thunderbolt.c @@ -34,7 +34,7 @@ static ssize_t force_power_store(struct device *dev, if (mode > 1) return -EINVAL; - ret = wmidev_invoke_method(to_wmi_device(dev), 0, 1, &buffer, NULL); + ret = wmidev_invoke_procedure(to_wmi_device(dev), 0, 1, &buffer); if (ret < 0) return ret; diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig index f885127b007f..09b1b055d2e0 100644 --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -252,7 +252,6 @@ config LENOVO_WMI_GAMEZONE select ACPI_PLATFORM_PROFILE select LENOVO_WMI_EVENTS select LENOVO_WMI_HELPERS - select LENOVO_WMI_TUNING help Say Y here if you have a WMI aware Lenovo Legion device and would like to use the platform-profile firmware interface to manage power usage. diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c index ae1ebb071fab..4fbc904f1fc3 100644 --- a/drivers/platform/x86/lenovo/ideapad-laptop.c +++ b/drivers/platform/x86/lenovo/ideapad-laptop.c @@ -2340,6 +2340,7 @@ static struct wmi_driver ideapad_wmi_driver = { .name = "ideapad_wmi", }, .id_table = ideapad_wmi_ids, + .min_event_size = sizeof(u32), .probe = ideapad_wmi_probe, .notify = ideapad_wmi_notify, }; diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c index 8982d92dfd97..e1cee42a1683 100644 --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -299,7 +299,6 @@ struct ibm_struct; struct tp_acpi_drv_struct { const struct acpi_device_id *hid; - struct acpi_driver *driver; void (*notify) (struct ibm_struct *, u32); acpi_handle *handle; @@ -322,7 +321,6 @@ struct ibm_struct { struct tp_acpi_drv_struct *acpi; struct { - u8 acpi_driver_registered:1; u8 acpi_notify_installed:1; u8 proc_created:1; u8 init_called:1; @@ -374,7 +372,7 @@ static struct { u32 hotkey_poll_active:1; u32 has_adaptive_kbd:1; u32 kbd_lang:1; - u32 trackpoint_doubletap:1; + u32 trackpoint_doubletap_enable:1; struct quirk_entry *quirks; } tp_features; @@ -832,9 +830,9 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) vdbg_printk(TPACPI_DBG_INIT, "setting up ACPI notify for %s\n", ibm->name); - ibm->acpi->device = acpi_fetch_acpi_dev(*ibm->acpi->handle); + ibm->acpi->device = acpi_get_acpi_dev(*ibm->acpi->handle); if (!ibm->acpi->device) { - pr_err("acpi_fetch_acpi_dev(%s) failed\n", ibm->name); + pr_err("acpi_get_acpi_dev(%s) failed\n", ibm->name); return -ENODEV; } @@ -859,44 +857,6 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) return 0; } -static int __init tpacpi_device_add(struct acpi_device *device) -{ - return 0; -} - -static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) -{ - int rc; - - dbg_printk(TPACPI_DBG_INIT, - "registering %s as an ACPI driver\n", ibm->name); - - BUG_ON(!ibm->acpi); - - ibm->acpi->driver = kzalloc_obj(struct acpi_driver); - if (!ibm->acpi->driver) { - pr_err("failed to allocate memory for ibm->acpi->driver\n"); - return -ENOMEM; - } - - sprintf(ibm->acpi->driver->name, "%s_%s", TPACPI_NAME, ibm->name); - ibm->acpi->driver->ids = ibm->acpi->hid; - - ibm->acpi->driver->ops.add = &tpacpi_device_add; - - rc = acpi_bus_register_driver(ibm->acpi->driver); - if (rc < 0) { - pr_err("acpi_bus_register_driver(%s) failed: %d\n", - ibm->name, rc); - kfree(ibm->acpi->driver); - ibm->acpi->driver = NULL; - } else if (!rc) - ibm->flags.acpi_driver_registered = 1; - - return rc; -} - - /**************************************************************************** **************************************************************************** * @@ -1315,7 +1275,7 @@ static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id, static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, struct seq_file *m) { if (id >= TPACPI_RFK_SW_MAX) - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); else { int status; @@ -1330,7 +1290,7 @@ static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, struct seq_file * } seq_printf(m, "status:\t\t%s\n", str_enabled_disabled(status == TPACPI_RFK_RADIO_ON)); - seq_printf(m, "commands:\tenable, disable\n"); + seq_puts(m, "commands:\tenable, disable\n"); } return 0; @@ -3019,6 +2979,31 @@ static const struct attribute_group adaptive_kbd_attr_group = { .attrs = adaptive_kbd_attributes, }; +/* sysfs doubletap enable --------------------------------------------- */ +static ssize_t doubletap_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%d\n", tp_features.trackpoint_doubletap_enable); +} + +static ssize_t doubletap_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + bool enable; + int err; + + err = kstrtobool(buf, &enable); + if (err) + return err; + + tp_features.trackpoint_doubletap_enable = enable; + return count; +} + +static DEVICE_ATTR_RW(doubletap_enable); + /* --------------------------------------------------------------------- */ static struct attribute *hotkey_attributes[] = { @@ -3033,6 +3018,7 @@ static struct attribute *hotkey_attributes[] = { &dev_attr_hotkey_recommended_mask.attr, &dev_attr_hotkey_tablet_mode.attr, &dev_attr_hotkey_radio_sw.attr, + &dev_attr_doubletap_enable.attr, #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL &dev_attr_hotkey_source_mask.attr, &dev_attr_hotkey_poll_freq.attr, @@ -3558,8 +3544,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) hotkey_poll_setup_safe(true); - /* Enable doubletap by default */ - tp_features.trackpoint_doubletap = 1; + /* Enable TrackPoint doubletap event reporting by default. */ + tp_features.trackpoint_doubletap_enable = 1; return 0; } @@ -3864,9 +3850,9 @@ static bool hotkey_notify_8xxx(const u32 hkey, bool *send_acpi_ev) { switch (hkey) { case TP_HKEY_EV_TRACK_DOUBLETAP: - if (tp_features.trackpoint_doubletap) - tpacpi_input_send_key(hkey, send_acpi_ev); - + /* Only send event if doubletap is enabled */ + if (!tp_features.trackpoint_doubletap_enable) + *send_acpi_ev = false; return true; default: return false; @@ -4017,7 +4003,7 @@ static int hotkey_read(struct seq_file *m) int res, status; if (!tp_features.hotkey) { - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); return 0; } @@ -4033,10 +4019,10 @@ static int hotkey_read(struct seq_file *m) seq_printf(m, "status:\t\t%s\n", str_enabled_disabled(status & BIT(0))); if (hotkey_all_mask) { seq_printf(m, "mask:\t\t0x%08x\n", hotkey_user_mask); - seq_printf(m, "commands:\tenable, disable, reset, \n"); + seq_puts(m, "commands:\tenable, disable, reset, \n"); } else { - seq_printf(m, "mask:\t\tnot supported\n"); - seq_printf(m, "commands:\tenable, disable, reset\n"); + seq_puts(m, "mask:\t\tnot supported\n"); + seq_puts(m, "commands:\tenable, disable, reset\n"); } return 0; @@ -4933,7 +4919,7 @@ static int video_read(struct seq_file *m) int status, autosw; if (video_supported == TPACPI_VIDEO_NONE) { - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); return 0; } @@ -4949,18 +4935,18 @@ static int video_read(struct seq_file *m) if (autosw < 0) return autosw; - seq_printf(m, "status:\t\tsupported\n"); + seq_puts(m, "status:\t\tsupported\n"); seq_printf(m, "lcd:\t\t%s\n", str_enabled_disabled(status & BIT(0))); seq_printf(m, "crt:\t\t%s\n", str_enabled_disabled(status & BIT(1))); if (video_supported == TPACPI_VIDEO_NEW) seq_printf(m, "dvi:\t\t%s\n", str_enabled_disabled(status & BIT(3))); seq_printf(m, "auto:\t\t%s\n", str_enabled_disabled(autosw & BIT(0))); - seq_printf(m, "commands:\tlcd_enable, lcd_disable\n"); - seq_printf(m, "commands:\tcrt_enable, crt_disable\n"); + seq_puts(m, "commands:\tlcd_enable, lcd_disable\n"); + seq_puts(m, "commands:\tcrt_enable, crt_disable\n"); if (video_supported == TPACPI_VIDEO_NEW) - seq_printf(m, "commands:\tdvi_enable, dvi_disable\n"); - seq_printf(m, "commands:\tauto_enable, auto_disable\n"); - seq_printf(m, "commands:\tvideo_switch, expand_toggle\n"); + seq_puts(m, "commands:\tdvi_enable, dvi_disable\n"); + seq_puts(m, "commands:\tauto_enable, auto_disable\n"); + seq_puts(m, "commands:\tvideo_switch, expand_toggle\n"); return 0; } @@ -5204,14 +5190,14 @@ static int kbdlight_read(struct seq_file *m) int level; if (!tp_features.kbdlight) { - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); } else { level = kbdlight_get_level(); if (level < 0) seq_printf(m, "status:\t\terror %d\n", level); else seq_printf(m, "status:\t\t%d\n", level); - seq_printf(m, "commands:\t0, 1, 2\n"); + seq_puts(m, "commands:\t0, 1, 2\n"); } return 0; @@ -5378,16 +5364,16 @@ static int light_read(struct seq_file *m) int status; if (!tp_features.light) { - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); } else if (!tp_features.light_status) { - seq_printf(m, "status:\t\tunknown\n"); - seq_printf(m, "commands:\ton, off\n"); + seq_puts(m, "status:\t\tunknown\n"); + seq_puts(m, "commands:\ton, off\n"); } else { status = light_get_status(); if (status < 0) return status; seq_printf(m, "status:\t\t%s\n", str_on_off(status & BIT(0))); - seq_printf(m, "commands:\ton, off\n"); + seq_puts(m, "commands:\ton, off\n"); } return 0; @@ -5477,10 +5463,10 @@ static int cmos_read(struct seq_file *m) /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, R30, R31, T20-22, X20-21 */ if (!cmos_handle) - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); else { - seq_printf(m, "status:\t\tsupported\n"); - seq_printf(m, "commands:\t ( is 0-21)\n"); + seq_puts(m, "status:\t\tsupported\n"); + seq_puts(m, "commands:\t ( is 0-21)\n"); } return 0; @@ -5846,10 +5832,10 @@ static int __init led_init(struct ibm_init_struct *iibm) static int led_read(struct seq_file *m) { if (!led_supported) { - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); return 0; } - seq_printf(m, "status:\t\tsupported\n"); + seq_puts(m, "status:\t\tsupported\n"); if (led_supported == TPACPI_LED_570) { /* 570 */ @@ -5862,7 +5848,7 @@ static int led_read(struct seq_file *m) } } - seq_printf(m, "commands:\t on, off, blink ( is 0-15)\n"); + seq_puts(m, "commands:\t on, off, blink ( is 0-15)\n"); return 0; } @@ -5946,10 +5932,10 @@ static int __init beep_init(struct ibm_init_struct *iibm) static int beep_read(struct seq_file *m) { if (!beep_handle) - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); else { - seq_printf(m, "status:\t\tsupported\n"); - seq_printf(m, "commands:\t ( is 0-17)\n"); + seq_puts(m, "status:\t\tsupported\n"); + seq_puts(m, "commands:\t ( is 0-17)\n"); } return 0; @@ -6398,14 +6384,14 @@ static int thermal_read(struct seq_file *m) if (unlikely(n < 0)) return n; - seq_printf(m, "temperatures:\t"); + seq_puts(m, "temperatures:\t"); if (n > 0) { for (i = 0; i < (n - 1); i++) seq_printf(m, "%d ", t.temp[i] / 1000); seq_printf(m, "%d\n", t.temp[i] / 1000); } else - seq_printf(m, "not supported\n"); + seq_puts(m, "not supported\n"); return 0; } @@ -6918,10 +6904,10 @@ static int brightness_read(struct seq_file *m) level = brightness_get(NULL); if (level < 0) { - seq_printf(m, "level:\t\tunreadable\n"); + seq_puts(m, "level:\t\tunreadable\n"); } else { seq_printf(m, "level:\t\t%d\n", level); - seq_printf(m, "commands:\tup, down\n"); + seq_puts(m, "commands:\tup, down\n"); seq_printf(m, "commands:\tlevel ( is 0-%d)\n", bright_maxlvl); } @@ -7637,10 +7623,10 @@ static int volume_read(struct seq_file *m) u8 status; if (volume_get_status(&status) < 0) { - seq_printf(m, "level:\t\tunreadable\n"); + seq_puts(m, "level:\t\tunreadable\n"); } else { if (tp_features.mixer_no_level_control) - seq_printf(m, "level:\t\tunsupported\n"); + seq_puts(m, "level:\t\tunsupported\n"); else seq_printf(m, "level:\t\t%d\n", status & TP_EC_AUDIO_LVL_MSK); @@ -7648,9 +7634,9 @@ static int volume_read(struct seq_file *m) seq_printf(m, "mute:\t\t%s\n", str_on_off(status & BIT(TP_EC_AUDIO_MUTESW))); if (volume_control_allowed) { - seq_printf(m, "commands:\tunmute, mute\n"); + seq_puts(m, "commands:\tunmute, mute\n"); if (!tp_features.mixer_no_level_control) { - seq_printf(m, "commands:\tup, down\n"); + seq_puts(m, "commands:\tup, down\n"); seq_printf(m, "commands:\tlevel ( is 0-%d)\n", TP_EC_VOLUME_MAX); } @@ -9156,9 +9142,9 @@ static int fan_read(struct seq_file *m) } else if (fan_status_access_mode == TPACPI_FAN_RD_TPEC) { if (status & TP_EC_FAN_FULLSPEED) /* Disengaged mode takes precedence */ - seq_printf(m, "level:\t\tdisengaged\n"); + seq_puts(m, "level:\t\tdisengaged\n"); else if (status & TP_EC_FAN_AUTO) - seq_printf(m, "level:\t\tauto\n"); + seq_puts(m, "level:\t\tauto\n"); else seq_printf(m, "level:\t\t%d\n", status); } @@ -9166,19 +9152,19 @@ static int fan_read(struct seq_file *m) case TPACPI_FAN_NONE: default: - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); } if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) { - seq_printf(m, "commands:\tlevel "); + seq_puts(m, "commands:\tlevel "); switch (fan_control_access_mode) { case TPACPI_FAN_WR_ACPI_SFAN: - seq_printf(m, " ( is 0-7)\n"); + seq_puts(m, " ( is 0-7)\n"); break; default: - seq_printf(m, " ( is 0-7, auto, disengaged, full-speed)\n"); + seq_puts(m, " ( is 0-7, auto, disengaged, full-speed)\n"); break; } } @@ -9188,7 +9174,7 @@ static int fan_read(struct seq_file *m) "commands:\twatchdog ( is 0 (off), 1-120 (seconds))\n"); if (fan_control_commands & TPACPI_FAN_CMD_SPEED) - seq_printf(m, "commands:\tspeed ( is 0-65535)\n"); + seq_puts(m, "commands:\tspeed ( is 0-65535)\n"); return 0; } @@ -9249,9 +9235,6 @@ static int fan_write_cmd_speed(const char *cmd, int *rc) { int speed; - /* TODO: - * Support speed ? */ - if (sscanf(cmd, "speed %d", &speed) != 1) return 0; @@ -11488,7 +11471,9 @@ static bool tpacpi_driver_event(const unsigned int hkey_event) mutex_unlock(&tpacpi_inputdev_send_mutex); return true; case TP_HKEY_EV_DOUBLETAP_TOGGLE: - tp_features.trackpoint_doubletap = !tp_features.trackpoint_doubletap; + /* Toggle kernel-level doubletap event filtering */ + tp_features.trackpoint_doubletap_enable = + !tp_features.trackpoint_doubletap_enable; return true; case TP_HKEY_EV_PROFILE_TOGGLE: case TP_HKEY_EV_PROFILE_TOGGLE2: @@ -11532,6 +11517,8 @@ static void ibm_exit(struct ibm_struct *ibm) acpi_remove_notify_handler(*ibm->acpi->handle, ibm->acpi->type, dispatch_acpi_notify); + ibm->acpi->device->driver_data = NULL; + acpi_dev_put(ibm->acpi->device); ibm->flags.acpi_notify_installed = 0; } @@ -11542,16 +11529,6 @@ static void ibm_exit(struct ibm_struct *ibm) ibm->flags.proc_created = 0; } - if (ibm->flags.acpi_driver_registered) { - dbg_printk(TPACPI_DBG_EXIT, - "%s: acpi_bus_unregister_driver\n", ibm->name); - BUG_ON(!ibm->acpi); - acpi_bus_unregister_driver(ibm->acpi->driver); - kfree(ibm->acpi->driver); - ibm->acpi->driver = NULL; - ibm->flags.acpi_driver_registered = 0; - } - if (ibm->flags.init_called && ibm->exit) { ibm->exit(); ibm->flags.init_called = 0; @@ -11587,12 +11564,6 @@ static int __init ibm_init(struct ibm_init_struct *iibm) } if (ibm->acpi) { - if (ibm->acpi->hid) { - ret = register_tpacpi_subdriver(ibm); - if (ret) - goto err_out; - } - if (ibm->acpi->notify) { ret = setup_acpi_notify(ibm); if (ret == -ENODEV) { diff --git a/drivers/platform/x86/lenovo/wmi-camera.c b/drivers/platform/x86/lenovo/wmi-camera.c index eb60fb9a5b3f..89ecbce60bf4 100644 --- a/drivers/platform/x86/lenovo/wmi-camera.c +++ b/drivers/platform/x86/lenovo/wmi-camera.c @@ -134,6 +134,7 @@ static struct wmi_driver lenovo_wmi_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .id_table = lenovo_wmi_id_table, + .min_event_size = sizeof(u8), .no_singleton = true, .probe = lenovo_wmi_probe, .notify = lenovo_wmi_notify, diff --git a/drivers/platform/x86/lenovo/wmi-capdata.c b/drivers/platform/x86/lenovo/wmi-capdata.c index b73d378f0e8b..714aa6fd6f1f 100644 --- a/drivers/platform/x86/lenovo/wmi-capdata.c +++ b/drivers/platform/x86/lenovo/wmi-capdata.c @@ -27,7 +27,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include #include #include #include @@ -48,6 +47,7 @@ #include #include "wmi-capdata.h" +#include "wmi-helpers.h" #define LENOVO_CAPABILITY_DATA_00_GUID "362A3AFE-3D96-4665-8530-96DAD5BB300E" #define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154" @@ -57,9 +57,9 @@ #define LWMI_FEATURE_ID_FAN_TEST 0x05 -#define LWMI_ATTR_ID_FAN_TEST \ - (FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) | \ - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_TEST)) +#define LWMI_ATTR_ID_FAN_TEST \ + lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_TEST, \ + LWMI_GZ_THERMAL_MODE_NONE, LWMI_TYPE_ID_NONE) enum lwmi_cd_type { LENOVO_CAPABILITY_DATA_00, diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h index 8c1df3efcc55..c3e760b8c3c3 100644 --- a/drivers/platform/x86/lenovo/wmi-capdata.h +++ b/drivers/platform/x86/lenovo/wmi-capdata.h @@ -6,6 +6,7 @@ #define _LENOVO_WMI_CAPDATA_H_ #include +#include #include #define LWMI_SUPP_VALID BIT(0) @@ -19,6 +20,8 @@ #define LWMI_DEVICE_ID_FAN 0x04 +#define LWMI_TYPE_ID_NONE 0x00 + struct component_match; struct device; struct cd_list; @@ -57,6 +60,23 @@ struct lwmi_cd_binder { cd_list_cb_t cd_fan_list_cb; }; +/** + * lwmi_attr_id() - Formats a capability data attribute ID + * @dev_id: The u8 corresponding to the device ID. + * @feat_id: The u8 corresponding to the feature ID on the device. + * @mode_id: The u8 corresponding to the wmi-gamezone mode for set/get. + * @type_id: The u8 corresponding to the sub-device. + * + * Return: encoded capability data attribute ID. + */ +static inline u32 lwmi_attr_id(u8 dev_id, u8 feat_id, u8 mode_id, u8 type_id) +{ + return (FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, dev_id) | + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, feat_id) | + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode_id) | + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, type_id)); +} + void lwmi_cd_match_add_all(struct device *master, struct component_match **matchptr); int lwmi_cd00_get_data(struct cd_list *list, u32 attribute_id, struct capdata00 *output); int lwmi_cd01_get_data(struct cd_list *list, u32 attribute_id, struct capdata01 *output); diff --git a/drivers/platform/x86/lenovo/wmi-events.c b/drivers/platform/x86/lenovo/wmi-events.c index 0994cd7dd504..fc25bba68a7c 100644 --- a/drivers/platform/x86/lenovo/wmi-events.c +++ b/drivers/platform/x86/lenovo/wmi-events.c @@ -17,7 +17,7 @@ #include #include "wmi-events.h" -#include "wmi-gamezone.h" +#include "wmi-helpers.h" #define THERMAL_MODE_EVENT_GUID "D320289E-8FEA-41E0-86F9-911D83151B5F" @@ -183,6 +183,7 @@ static struct wmi_driver lwmi_events_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .id_table = lwmi_events_id_table, + .min_event_size = sizeof(u32), .probe = lwmi_events_probe, .notify = lwmi_events_notify, .no_singleton = true, diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.c b/drivers/platform/x86/lenovo/wmi-gamezone.c index c7fe7e3c9f17..109c0b564a9f 100644 --- a/drivers/platform/x86/lenovo/wmi-gamezone.c +++ b/drivers/platform/x86/lenovo/wmi-gamezone.c @@ -21,9 +21,7 @@ #include #include "wmi-events.h" -#include "wmi-gamezone.h" #include "wmi-helpers.h" -#include "wmi-other.h" #define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0" @@ -201,7 +199,7 @@ static int lwmi_gz_profile_set(struct device *dev, enum platform_profile_option profile) { struct lwmi_gz_priv *priv = dev_get_drvdata(dev); - struct wmi_method_args_32 args; + struct wmi_method_args_32 args = {}; enum thermal_mode mode; int ret; @@ -383,7 +381,7 @@ static int lwmi_gz_probe(struct wmi_device *wdev, const void *context) return ret; priv->mode_nb.notifier_call = lwmi_gz_mode_call; - return devm_lwmi_om_register_notifier(&wdev->dev, &priv->mode_nb); + return devm_lwmi_tm_register_notifier(&wdev->dev, &priv->mode_nb); } static const struct wmi_device_id lwmi_gz_id_table[] = { @@ -405,7 +403,6 @@ module_wmi_driver(lwmi_gz_driver); MODULE_IMPORT_NS("LENOVO_WMI_EVENTS"); MODULE_IMPORT_NS("LENOVO_WMI_HELPERS"); -MODULE_IMPORT_NS("LENOVO_WMI_OTHER"); MODULE_DEVICE_TABLE(wmi, lwmi_gz_id_table); MODULE_AUTHOR("Derek J. Clark "); MODULE_DESCRIPTION("Lenovo GameZone WMI Driver"); diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h deleted file mode 100644 index 6b163a5eeb95..000000000000 --- a/drivers/platform/x86/lenovo/wmi-gamezone.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* Copyright (C) 2025 Derek J. Clark */ - -#ifndef _LENOVO_WMI_GAMEZONE_H_ -#define _LENOVO_WMI_GAMEZONE_H_ - -enum gamezone_events_type { - LWMI_GZ_GET_THERMAL_MODE = 1, -}; - -enum thermal_mode { - LWMI_GZ_THERMAL_MODE_QUIET = 0x01, - LWMI_GZ_THERMAL_MODE_BALANCED = 0x02, - LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03, - LWMI_GZ_THERMAL_MODE_EXTREME = 0xE0, /* Ver 6+ */ - LWMI_GZ_THERMAL_MODE_CUSTOM = 0xFF, -}; - -#endif /* !_LENOVO_WMI_GAMEZONE_H_ */ diff --git a/drivers/platform/x86/lenovo/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c index 7379defac500..7a198259e393 100644 --- a/drivers/platform/x86/lenovo/wmi-helpers.c +++ b/drivers/platform/x86/lenovo/wmi-helpers.c @@ -21,11 +21,15 @@ #include #include #include +#include #include #include #include "wmi-helpers.h" +/* Thermal mode notifier chain. */ +static BLOCKING_NOTIFIER_HEAD(tm_chain_head); + /** * lwmi_dev_evaluate_int() - Helper function for calling WMI methods that * return an integer. @@ -46,7 +50,6 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id, unsigned char *buf, size_t size, u32 *retval) { struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *ret_obj __free(kfree) = NULL; struct acpi_buffer input = { size, buf }; acpi_status status; @@ -55,8 +58,9 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id, if (ACPI_FAILURE(status)) return -EIO; + union acpi_object *ret_obj __free(kfree) = output.pointer; + if (retval) { - ret_obj = output.pointer; if (!ret_obj) return -ENODATA; @@ -84,6 +88,103 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id, }; EXPORT_SYMBOL_NS_GPL(lwmi_dev_evaluate_int, "LENOVO_WMI_HELPERS"); +/** + * lwmi_tm_register_notifier() - Add a notifier to the blocking notifier chain + * @nb: The notifier_block struct to register + * + * Call blocking_notifier_chain_register to register the notifier block to the + * thermal mode notifier chain. + * + * Return: 0 on success, %-EEXIST on error. + */ +int lwmi_tm_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&tm_chain_head, nb); +} +EXPORT_SYMBOL_NS_GPL(lwmi_tm_register_notifier, "LENOVO_WMI_HELPERS"); + +/** + * lwmi_tm_unregister_notifier() - Remove a notifier from the blocking notifier + * chain. + * @nb: The notifier_block struct to register + * + * Call blocking_notifier_chain_unregister to unregister the notifier block from the + * thermal mode notifier chain. + * + * Return: 0 on success, %-ENOENT on error. + */ +int lwmi_tm_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&tm_chain_head, nb); +} +EXPORT_SYMBOL_NS_GPL(lwmi_tm_unregister_notifier, "LENOVO_WMI_HELPERS"); + +/** + * devm_lwmi_tm_unregister_notifier() - Remove a notifier from the blocking + * notifier chain. + * @data: Void pointer to the notifier_block struct to register. + * + * Call lwmi_tm_unregister_notifier to unregister the notifier block from the + * thermal mode notifier chain. + * + * Return: 0 on success, %-ENOENT on error. + */ +static void devm_lwmi_tm_unregister_notifier(void *data) +{ + struct notifier_block *nb = data; + + lwmi_tm_unregister_notifier(nb); +} + +/** + * devm_lwmi_tm_register_notifier() - Add a notifier to the blocking notifier + * chain. + * @dev: The parent device of the notifier_block struct. + * @nb: The notifier_block struct to register + * + * Call lwmi_tm_register_notifier to register the notifier block to the + * thermal mode notifier chain. Then add devm_lwmi_tm_unregister_notifier + * as a device managed action to automatically unregister the notifier block + * upon parent device removal. + * + * Return: 0 on success, or an error code. + */ +int devm_lwmi_tm_register_notifier(struct device *dev, + struct notifier_block *nb) +{ + int ret; + + ret = lwmi_tm_register_notifier(nb); + if (ret < 0) + return ret; + + return devm_add_action_or_reset(dev, devm_lwmi_tm_unregister_notifier, + nb); +} +EXPORT_SYMBOL_NS_GPL(devm_lwmi_tm_register_notifier, "LENOVO_WMI_HELPERS"); + +/** + * lwmi_tm_notifier_call() - Call functions for the notifier call chain. + * @mode: Pointer to a thermal mode enum to retrieve the data from. + * + * Call blocking_notifier_call_chain to retrieve the thermal mode from the + * lenovo-wmi-gamezone driver. + * + * Return: 0 on success, or an error code. + */ +int lwmi_tm_notifier_call(enum thermal_mode *mode) +{ + int ret; + + ret = blocking_notifier_call_chain(&tm_chain_head, + LWMI_GZ_GET_THERMAL_MODE, &mode); + if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(lwmi_tm_notifier_call, "LENOVO_WMI_HELPERS"); + MODULE_AUTHOR("Derek J. Clark "); MODULE_DESCRIPTION("Lenovo WMI Helpers Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/lenovo/wmi-helpers.h b/drivers/platform/x86/lenovo/wmi-helpers.h index 20fd21749803..ed7db3ebba6c 100644 --- a/drivers/platform/x86/lenovo/wmi-helpers.h +++ b/drivers/platform/x86/lenovo/wmi-helpers.h @@ -7,6 +7,8 @@ #include +struct device; +struct notifier_block; struct wmi_device; struct wmi_method_args_32 { @@ -14,7 +16,26 @@ struct wmi_method_args_32 { u32 arg1; }; +enum lwmi_event_type { + LWMI_GZ_GET_THERMAL_MODE = 0x01, +}; + +enum thermal_mode { + LWMI_GZ_THERMAL_MODE_NONE = 0x00, + LWMI_GZ_THERMAL_MODE_QUIET = 0x01, + LWMI_GZ_THERMAL_MODE_BALANCED = 0x02, + LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03, + LWMI_GZ_THERMAL_MODE_EXTREME = 0xE0, /* Ver 6+ */ + LWMI_GZ_THERMAL_MODE_CUSTOM = 0xFF, +}; + int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id, unsigned char *buf, size_t size, u32 *retval); +int lwmi_tm_register_notifier(struct notifier_block *nb); +int lwmi_tm_unregister_notifier(struct notifier_block *nb); +int devm_lwmi_tm_register_notifier(struct device *dev, + struct notifier_block *nb); +int lwmi_tm_notifier_call(enum thermal_mode *mode); + #endif /* !_LENOVO_WMI_HELPERS_H_ */ diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c index 6040f45aa2b0..d318ba432fdc 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -40,16 +40,13 @@ #include #include #include -#include #include #include #include #include "wmi-capdata.h" #include "wmi-events.h" -#include "wmi-gamezone.h" #include "wmi-helpers.h" -#include "wmi-other.h" #include "../firmware_attributes_class.h" #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B" @@ -62,8 +59,6 @@ #define LWMI_FEATURE_ID_FAN_RPM 0x03 -#define LWMI_TYPE_ID_NONE 0x00 - #define LWMI_FEATURE_VALUE_GET 17 #define LWMI_FEATURE_VALUE_SET 18 @@ -71,17 +66,15 @@ #define LWMI_FAN_NR 4 #define LWMI_FAN_ID(x) ((x) + LWMI_FAN_ID_BASE) -#define LWMI_ATTR_ID_FAN_RPM(x) \ - (FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) | \ - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_RPM) | \ - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, LWMI_FAN_ID(x))) - #define LWMI_FAN_DIV 100 +#define LWMI_ATTR_ID_FAN_RPM(x) \ + lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_RPM, \ + LWMI_GZ_THERMAL_MODE_NONE, LWMI_FAN_ID(x)) + #define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other" #define LWMI_OM_HWMON_NAME "lenovo_wmi_other" -static BLOCKING_NOTIFIER_HEAD(om_chain_head); static DEFINE_IDA(lwmi_om_ida); enum attribute_property { @@ -109,7 +102,6 @@ struct lwmi_om_priv { struct device *hwmon_dev; struct device *fw_attr_dev; struct kset *fw_attr_kset; - struct notifier_block nb; struct wmi_device *wdev; int ida_id; @@ -166,7 +158,7 @@ MODULE_PARM_DESC(relax_fan_constraint, */ static int lwmi_om_fan_get_set(struct lwmi_om_priv *priv, int channel, u32 *val, bool set) { - struct wmi_method_args_32 args; + struct wmi_method_args_32 args = {}; u32 method_id, retval; int err; @@ -349,6 +341,8 @@ static int lwmi_om_hwmon_write(struct device *dev, enum hwmon_sensor_types type, */ if (!relax_fan_constraint) raw = val / LWMI_FAN_DIV * LWMI_FAN_DIV; + else + raw = val; err = lwmi_om_fan_get_set(priv, channel, &raw, true); if (err) @@ -546,13 +540,26 @@ out: /* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */ struct tunable_attr_01 { - struct capdata01 *capdata; struct device *dev; - u32 feature_id; - u32 device_id; - u32 type_id; + u8 feature_id; + u8 device_id; + u8 type_id; + u8 cd_mode_id; /* mode arg for searching capdata */ + u8 cv_mode_id; /* mode arg for set/get current_value */ }; +/** + * tunable_attr_01_id() - Formats a tunable_attr_01 to a capdata attribute ID + * @attr: The tunable_attr_01 to format. + * @mode: The u8 corresponding to the wmi-gamezone mode for set/get. + * + * Return: encoded capability data attribute ID. + */ +static u32 tunable_attr_01_id(struct tunable_attr_01 *attr, u8 mode) +{ + return lwmi_attr_id(attr->device_id, attr->feature_id, mode, attr->type_id); +} + static struct tunable_attr_01 ppt_pl1_spl = { .device_id = LWMI_DEVICE_ID_CPU, .feature_id = LWMI_FEATURE_ID_CPU_SPL, @@ -576,102 +583,6 @@ struct capdata01_attr_group { struct tunable_attr_01 *tunable_attr; }; -/** - * lwmi_om_register_notifier() - Add a notifier to the blocking notifier chain - * @nb: The notifier_block struct to register - * - * Call blocking_notifier_chain_register to register the notifier block to the - * lenovo-wmi-other driver notifier chain. - * - * Return: 0 on success, %-EEXIST on error. - */ -int lwmi_om_register_notifier(struct notifier_block *nb) -{ - return blocking_notifier_chain_register(&om_chain_head, nb); -} -EXPORT_SYMBOL_NS_GPL(lwmi_om_register_notifier, "LENOVO_WMI_OTHER"); - -/** - * lwmi_om_unregister_notifier() - Remove a notifier from the blocking notifier - * chain. - * @nb: The notifier_block struct to register - * - * Call blocking_notifier_chain_unregister to unregister the notifier block from the - * lenovo-wmi-other driver notifier chain. - * - * Return: 0 on success, %-ENOENT on error. - */ -int lwmi_om_unregister_notifier(struct notifier_block *nb) -{ - return blocking_notifier_chain_unregister(&om_chain_head, nb); -} -EXPORT_SYMBOL_NS_GPL(lwmi_om_unregister_notifier, "LENOVO_WMI_OTHER"); - -/** - * devm_lwmi_om_unregister_notifier() - Remove a notifier from the blocking - * notifier chain. - * @data: Void pointer to the notifier_block struct to register. - * - * Call lwmi_om_unregister_notifier to unregister the notifier block from the - * lenovo-wmi-other driver notifier chain. - * - * Return: 0 on success, %-ENOENT on error. - */ -static void devm_lwmi_om_unregister_notifier(void *data) -{ - struct notifier_block *nb = data; - - lwmi_om_unregister_notifier(nb); -} - -/** - * devm_lwmi_om_register_notifier() - Add a notifier to the blocking notifier - * chain. - * @dev: The parent device of the notifier_block struct. - * @nb: The notifier_block struct to register - * - * Call lwmi_om_register_notifier to register the notifier block to the - * lenovo-wmi-other driver notifier chain. Then add devm_lwmi_om_unregister_notifier - * as a device managed action to automatically unregister the notifier block - * upon parent device removal. - * - * Return: 0 on success, or an error code. - */ -int devm_lwmi_om_register_notifier(struct device *dev, - struct notifier_block *nb) -{ - int ret; - - ret = lwmi_om_register_notifier(nb); - if (ret < 0) - return ret; - - return devm_add_action_or_reset(dev, devm_lwmi_om_unregister_notifier, - nb); -} -EXPORT_SYMBOL_NS_GPL(devm_lwmi_om_register_notifier, "LENOVO_WMI_OTHER"); - -/** - * lwmi_om_notifier_call() - Call functions for the notifier call chain. - * @mode: Pointer to a thermal mode enum to retrieve the data from. - * - * Call blocking_notifier_call_chain to retrieve the thermal mode from the - * lenovo-wmi-gamezone driver. - * - * Return: 0 on success, or an error code. - */ -static int lwmi_om_notifier_call(enum thermal_mode *mode) -{ - int ret; - - ret = blocking_notifier_call_chain(&om_chain_head, - LWMI_GZ_GET_THERMAL_MODE, &mode); - if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK) - return -EINVAL; - - return 0; -} - /* Attribute Methods */ /** @@ -716,12 +627,7 @@ static ssize_t attr_capdata01_show(struct kobject *kobj, u32 attribute_id; int value, ret; - attribute_id = - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, - LWMI_GZ_THERMAL_MODE_CUSTOM) | - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); + attribute_id = tunable_attr_01_id(tunable_attr, tunable_attr->cd_mode_id); ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata); if (ret) @@ -773,27 +679,22 @@ static ssize_t attr_current_value_store(struct kobject *kobj, struct tunable_attr_01 *tunable_attr) { struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev); - struct wmi_method_args_32 args; + struct wmi_method_args_32 args = {}; struct capdata01 capdata; enum thermal_mode mode; - u32 attribute_id; u32 value; int ret; - ret = lwmi_om_notifier_call(&mode); + ret = lwmi_tm_notifier_call(&mode); if (ret) return ret; if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM) return -EBUSY; - attribute_id = - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) | - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); + args.arg0 = tunable_attr_01_id(tunable_attr, tunable_attr->cd_mode_id); - ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata); + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata); if (ret) return ret; @@ -804,7 +705,7 @@ static ssize_t attr_current_value_store(struct kobject *kobj, if (value < capdata.min_value || value > capdata.max_value) return -EINVAL; - args.arg0 = attribute_id; + args.arg0 = tunable_attr_01_id(tunable_attr, tunable_attr->cv_mode_id); args.arg1 = value; ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET, @@ -836,23 +737,20 @@ static ssize_t attr_current_value_show(struct kobject *kobj, struct tunable_attr_01 *tunable_attr) { struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev); - struct wmi_method_args_32 args; + struct wmi_method_args_32 args = {}; enum thermal_mode mode; - u32 attribute_id; int retval; int ret; - ret = lwmi_om_notifier_call(&mode); + ret = lwmi_tm_notifier_call(&mode); if (ret) return ret; - attribute_id = - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) | - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); + /* If "no-mode" is the supported mode, ensure we never send current mode */ + if (tunable_attr->cv_mode_id == LWMI_GZ_THERMAL_MODE_NONE) + mode = tunable_attr->cv_mode_id; - args.arg0 = attribute_id; + args.arg0 = tunable_attr_01_id(tunable_attr, mode); ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET, (unsigned char *)&args, sizeof(args), @@ -863,6 +761,81 @@ static ssize_t attr_current_value_show(struct kobject *kobj, return sysfs_emit(buf, "%d\n", retval); } +/** + * lwmi_attr_01_is_supported() - Determine if the given attribute is supported. + * @tunable_attr: The attribute to verify. + * + * For an attribute to be supported it must have a functional get/set method, + * as well as associated capability data stored in the capdata01 table. + * + * First check if the attribute has a corresponding data table under custom mode + * (0xff), then under no mode (0x00). If either of those passes, check if the + * supported field of the capdata struct is > 0. If it is supported, store the + * successful mode in the cd_mode_id field of tunable_attr. + * + * If the attribute capdata shows it is supported, attempt to determine the mode + * for the current value property get/set methods using a similar pattern to the + * capdata table check. If the value returned by either mode is 0 or an error, + * assume that mode is not supported. Otherwise, store the successful mode in the + * cv_mode_id field of tunable_attr. + * + * If any of the above checks fail then the attribute is not fully supported. + * + * Return: true if capdata and set/get modes are found, otherwise false. + */ +static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr) +{ + u8 modes[2] = { LWMI_GZ_THERMAL_MODE_CUSTOM, LWMI_GZ_THERMAL_MODE_NONE }; + struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev); + struct wmi_method_args_32 args = {}; + bool cd_mode_found = false; + bool cv_mode_found = false; + struct capdata01 capdata; + int retval, ret, i; + + /* Determine tunable_attr->cd_mode_id */ + for (i = 0; i < ARRAY_SIZE(modes); i++) { + args.arg0 = tunable_attr_01_id(tunable_attr, modes[i]); + + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata); + if (ret || !capdata.supported) + continue; + + tunable_attr->cd_mode_id = modes[i]; + cd_mode_found = true; + break; + } + + if (!cd_mode_found) + return cd_mode_found; + + dev_dbg(tunable_attr->dev, + "cd_mode_id: %#010x\n", args.arg0); + + /* Determine tunable_attr->cv_mode_id, returns 1 if supported */ + for (i = 0; i < ARRAY_SIZE(modes); i++) { + args.arg0 = tunable_attr_01_id(tunable_attr, modes[i]); + + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET, + (u8 *)&args, sizeof(args), + &retval); + if (ret || !retval) + continue; + + tunable_attr->cv_mode_id = modes[i]; + cv_mode_found = true; + break; + } + + if (!cv_mode_found) + return cv_mode_found; + + dev_dbg(tunable_attr->dev, "cv_mode_id: %#010x, attribute support level: %#010x\n", + args.arg0, capdata.supported); + + return capdata.supported > 0; +} + /* Lenovo WMI Other Mode Attribute macros */ #define __LWMI_ATTR_RO(_func, _name) \ { \ @@ -957,17 +930,17 @@ static struct capdata01_attr_group cd01_attr_groups[] = { /** * lwmi_om_fw_attr_add() - Register all firmware_attributes_class members * @priv: The Other Mode driver data. - * - * Return: Either 0, or an error code. */ -static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv) +static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv) { unsigned int i; int err; - priv->ida_id = ida_alloc(&lwmi_om_ida, GFP_KERNEL); - if (priv->ida_id < 0) - return priv->ida_id; + err = ida_alloc(&lwmi_om_ida, GFP_KERNEL); + if (err < 0) + goto err_no_ida; + + priv->ida_id = err; priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), NULL, "%s-%u", @@ -986,14 +959,16 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv) } for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) { + cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev; + if (!lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr)) + continue; + err = sysfs_create_group(&priv->fw_attr_kset->kobj, cd01_attr_groups[i].attr_group); if (err) goto err_remove_groups; - - cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev; } - return 0; + return; err_remove_groups: while (i--) @@ -1007,7 +982,12 @@ err_destroy_classdev: err_free_ida: ida_free(&lwmi_om_ida, priv->ida_id); - return err; + +err_no_ida: + priv->ida_id = -EIDRM; + + dev_warn(&priv->wdev->dev, + "failed to register firmware-attributes device: %d\n", err); } /** @@ -1016,12 +996,17 @@ err_free_ida: */ static void lwmi_om_fw_attr_remove(struct lwmi_om_priv *priv) { + if (priv->ida_id < 0) + return; + for (unsigned int i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) sysfs_remove_group(&priv->fw_attr_kset->kobj, cd01_attr_groups[i].attr_group); kset_unregister(priv->fw_attr_kset); device_unregister(priv->fw_attr_dev); + ida_free(&lwmi_om_ida, priv->ida_id); + priv->ida_id = -EIDRM; } /* ======== Self (master: lenovo-wmi-other) ======== */ @@ -1058,12 +1043,17 @@ static int lwmi_om_master_bind(struct device *dev) priv->cd00_list = binder.cd00_list; priv->cd01_list = binder.cd01_list; - if (!priv->cd00_list || !priv->cd01_list) + if (!priv->cd00_list || !priv->cd01_list) { + component_unbind_all(dev, NULL); + return -ENODEV; + } lwmi_om_fan_info_collect_cd00(priv); - return lwmi_om_fw_attr_add(priv); + lwmi_om_fw_attr_add(priv); + + return 0; } /** @@ -1115,13 +1105,7 @@ static int lwmi_other_probe(struct wmi_device *wdev, const void *context) static void lwmi_other_remove(struct wmi_device *wdev) { - struct lwmi_om_priv *priv = dev_get_drvdata(&wdev->dev); - component_master_del(&wdev->dev, &lwmi_om_master_ops); - - /* No IDA to free if the driver is never bound to its components. */ - if (priv->ida_id >= 0) - ida_free(&lwmi_om_ida, priv->ida_id); } static const struct wmi_device_id lwmi_other_id_table[] = { diff --git a/drivers/platform/x86/lenovo/wmi-other.h b/drivers/platform/x86/lenovo/wmi-other.h deleted file mode 100644 index 8ebf5602bb99..000000000000 --- a/drivers/platform/x86/lenovo/wmi-other.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/* Copyright (C) 2025 Derek J. Clark */ - -#ifndef _LENOVO_WMI_OTHER_H_ -#define _LENOVO_WMI_OTHER_H_ - -struct device; -struct notifier_block; - -int lwmi_om_register_notifier(struct notifier_block *nb); -int lwmi_om_unregister_notifier(struct notifier_block *nb); -int devm_lwmi_om_register_notifier(struct device *dev, - struct notifier_block *nb); - -#endif /* !_LENOVO_WMI_OTHER_H_ */ diff --git a/drivers/platform/x86/lenovo/ymc.c b/drivers/platform/x86/lenovo/ymc.c index 470d53e3c9d2..1b73a55f1b89 100644 --- a/drivers/platform/x86/lenovo/ymc.c +++ b/drivers/platform/x86/lenovo/ymc.c @@ -153,6 +153,7 @@ static struct wmi_driver lenovo_ymc_driver = { .name = "lenovo-ymc", }, .id_table = lenovo_ymc_wmi_id_table, + .min_event_size = sizeof(u32), .probe = lenovo_ymc_probe, .notify = lenovo_ymc_notify, }; diff --git a/drivers/platform/x86/lenovo/yogabook.c b/drivers/platform/x86/lenovo/yogabook.c index 69887de36c9b..1a4b2ab1f35d 100644 --- a/drivers/platform/x86/lenovo/yogabook.c +++ b/drivers/platform/x86/lenovo/yogabook.c @@ -411,8 +411,8 @@ static struct wmi_driver yogabook_wmi_driver = { .name = "yogabook-wmi", .pm = pm_sleep_ptr(&yogabook_pm_ops), }, - .no_notify_data = true, .id_table = yogabook_wmi_id_table, + .min_event_size = 0, .probe = yogabook_wmi_probe, .remove = yogabook_wmi_remove, .notify = yogabook_wmi_notify, diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c index 61ef7a218a80..9681412d694b 100644 --- a/drivers/platform/x86/lg-laptop.c +++ b/drivers/platform/x86/lg-laptop.c @@ -271,11 +271,6 @@ static void wmi_input_setup(void) } } -static void acpi_notify(struct acpi_device *device, u32 event) -{ - acpi_handle_debug(device->handle, "notify: %d\n", event); -} - static ssize_t fan_mode_store(struct device *dev, struct device_attribute *attr, const char *buffer, size_t count) @@ -764,8 +759,9 @@ static void lg_laptop_remove_address_space_handler(void *data) &lg_laptop_address_space_handler); } -static int acpi_add(struct acpi_device *device) +static int acpi_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct platform_device_info pdev_info = { .fwnode = acpi_fwnode_handle(device), .name = PLATFORM_NAME, @@ -781,11 +777,11 @@ static int acpi_add(struct acpi_device *device) status = acpi_install_address_space_handler(device->handle, LG_ADDRESS_SPACE_ID, &lg_laptop_address_space_handler, - NULL, &device->dev); + NULL, &pdev->dev); if (ACPI_FAILURE(status)) return -ENODEV; - ret = devm_add_action_or_reset(&device->dev, lg_laptop_remove_address_space_handler, + ret = devm_add_action_or_reset(&pdev->dev, lg_laptop_remove_address_space_handler, device); if (ret < 0) return ret; @@ -879,7 +875,7 @@ out_platform_registered: return ret; } -static void acpi_remove(struct acpi_device *device) +static void acpi_remove(struct platform_device *pdev) { sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group); @@ -899,34 +895,13 @@ static const struct acpi_device_id device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, device_ids); -static struct acpi_driver acpi_driver = { - .name = "LG Gram Laptop Support", - .class = "lg-laptop", - .ids = device_ids, - .ops = { - .add = acpi_add, - .remove = acpi_remove, - .notify = acpi_notify, - }, +static struct platform_driver acpi_driver = { + .probe = acpi_probe, + .remove = acpi_remove, + .driver = { + .name = "LG Gram Laptop Support", + .acpi_match_table = device_ids, + }, }; -static int __init acpi_init(void) -{ - int result; - - result = acpi_bus_register_driver(&acpi_driver); - if (result < 0) { - pr_debug("Error registering driver\n"); - return -ENODEV; - } - - return 0; -} - -static void __exit acpi_exit(void) -{ - acpi_bus_unregister_driver(&acpi_driver); -} - -module_init(acpi_init); -module_exit(acpi_exit); +module_platform_driver(acpi_driver); diff --git a/drivers/platform/x86/mxm-wmi.c b/drivers/platform/x86/mxm-wmi.c index 9a457956025a..dbc5e35ec38b 100644 --- a/drivers/platform/x86/mxm-wmi.c +++ b/drivers/platform/x86/mxm-wmi.c @@ -80,15 +80,3 @@ bool mxm_wmi_supported(void) return guid_valid; } EXPORT_SYMBOL_GPL(mxm_wmi_supported); - -static int __init mxm_wmi_init(void) -{ - return 0; -} - -static void __exit mxm_wmi_exit(void) -{ -} - -module_init(mxm_wmi_init); -module_exit(mxm_wmi_exit); diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index d923ddaa4849..1337f7c49805 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -183,9 +183,9 @@ enum SINF_BITS { SINF_NUM_BATTERIES = 0, }; /* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */ -static int acpi_pcc_hotkey_add(struct acpi_device *device); -static void acpi_pcc_hotkey_remove(struct acpi_device *device); -static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event); +static int acpi_pcc_hotkey_probe(struct platform_device *pdev); +static void acpi_pcc_hotkey_remove(struct platform_device *pdev); +static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data); static const struct acpi_device_id pcc_device_ids[] = { { "MAT0012", 0}, @@ -201,16 +201,14 @@ static int acpi_pcc_hotkey_resume(struct device *dev); #endif static SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume); -static struct acpi_driver acpi_pcc_driver = { - .name = ACPI_PCC_DRIVER_NAME, - .class = ACPI_PCC_CLASS, - .ids = pcc_device_ids, - .ops = { - .add = acpi_pcc_hotkey_add, - .remove = acpi_pcc_hotkey_remove, - .notify = acpi_pcc_hotkey_notify, - }, - .drv.pm = &acpi_pcc_hotkey_pm, +static struct platform_driver acpi_pcc_driver = { + .probe = acpi_pcc_hotkey_probe, + .remove = acpi_pcc_hotkey_remove, + .driver = { + .name = ACPI_PCC_DRIVER_NAME, + .acpi_match_table = pcc_device_ids, + .pm = &acpi_pcc_hotkey_pm, + }, }; static const struct key_entry panasonic_keymap[] = { @@ -869,9 +867,9 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) pr_err("Unknown hotkey event: 0x%04llx\n", result); } -static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) +static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data) { - struct pcc_acpi *pcc = acpi_driver_data(device); + struct pcc_acpi *pcc = data; switch (event) { case HKEY_NOTIFY: @@ -891,7 +889,7 @@ static void pcc_optd_notify(acpi_handle handle, u32 event, void *data) set_optd_power_state(0); } -static int pcc_register_optd_notifier(struct pcc_acpi *pcc, char *node) +static void pcc_register_optd_notifier(struct pcc_acpi *pcc, char *node) { acpi_status status; acpi_handle handle; @@ -904,10 +902,7 @@ static int pcc_register_optd_notifier(struct pcc_acpi *pcc, char *node) pcc_optd_notify, pcc); if (ACPI_FAILURE(status)) pr_err("Failed to register notify on %s\n", node); - } else - return -ENODEV; - - return 0; + } } static void pcc_unregister_optd_notifier(struct pcc_acpi *pcc, char *node) @@ -968,14 +963,7 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc) #ifdef CONFIG_PM_SLEEP static int acpi_pcc_hotkey_resume(struct device *dev) { - struct pcc_acpi *pcc; - - if (!dev) - return -EINVAL; - - pcc = acpi_driver_data(to_acpi_device(dev)); - if (!pcc) - return -EINVAL; + struct pcc_acpi *pcc = acpi_driver_data(ACPI_COMPANION(dev)); if (pcc->num_sifr > SINF_MUTE) acpi_pcc_write_sset(pcc, SINF_MUTE, pcc->mute); @@ -991,15 +979,13 @@ static int acpi_pcc_hotkey_resume(struct device *dev) } #endif -static int acpi_pcc_hotkey_add(struct acpi_device *device) +static int acpi_pcc_hotkey_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct backlight_properties props; struct pcc_acpi *pcc; int num_sifr, result; - if (!device) - return -EINVAL; - num_sifr = acpi_pcc_get_sqty(device); /* @@ -1083,19 +1069,25 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) if (result) goto out_backlight; + result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_pcc_hotkey_notify, pcc); + if (result) + goto out_sysfs; + /* optical drive initialization */ if (ACPI_SUCCESS(check_optd_present())) { pcc->platform = platform_device_register_simple("panasonic", PLATFORM_DEVID_NONE, NULL, 0); if (IS_ERR(pcc->platform)) { result = PTR_ERR(pcc->platform); - goto out_sysfs; + goto out_notify_handler; } result = device_create_file(&pcc->platform->dev, &dev_attr_cdpower); - pcc_register_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD"); if (result) goto out_platform; + + pcc_register_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD"); } else { pcc->platform = NULL; } @@ -1105,6 +1097,9 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) out_platform: platform_device_unregister(pcc->platform); +out_notify_handler: + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_pcc_hotkey_notify); out_sysfs: sysfs_remove_group(&device->dev.kobj, &pcc_attr_group); out_backlight: @@ -1112,6 +1107,7 @@ out_backlight: out_input: input_unregister_device(pcc->input_dev); out_sinf: + device->driver_data = NULL; kfree(pcc->sinf); out_hotkey: kfree(pcc); @@ -1119,20 +1115,21 @@ out_hotkey: return result; } -static void acpi_pcc_hotkey_remove(struct acpi_device *device) +static void acpi_pcc_hotkey_remove(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct pcc_acpi *pcc = acpi_driver_data(device); - if (!device || !pcc) - return; - i8042_remove_filter(panasonic_i8042_filter); if (pcc->platform) { + pcc_unregister_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD"); device_remove_file(&pcc->platform->dev, &dev_attr_cdpower); platform_device_unregister(pcc->platform); } - pcc_unregister_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD"); + + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_pcc_hotkey_notify); sysfs_remove_group(&device->dev.kobj, &pcc_attr_group); @@ -1140,8 +1137,10 @@ static void acpi_pcc_hotkey_remove(struct acpi_device *device) input_unregister_device(pcc->input_dev); + device->driver_data = NULL; + kfree(pcc->sinf); kfree(pcc); } -module_acpi_driver(acpi_pcc_driver); +module_platform_driver(acpi_pcc_driver); diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c index 3b086863c6ac..3f19589d1ba0 100644 --- a/drivers/platform/x86/pcengines-apuv2.c +++ b/drivers/platform/x86/pcengines-apuv2.c @@ -294,7 +294,8 @@ static int __init apu_board_init(void) } apu_gpio_pdev = apu_create_pdev(AMD_FCH_GPIO_DRIVER_NAME, - id->driver_data, sizeof(struct amd_fch_gpio_pdata), NULL); + id->driver_data, sizeof(struct amd_fch_gpio_pdata), + &apu2_gpiochip_node); err = PTR_ERR_OR_ZERO(apu_gpio_pdev); if (err) goto err_unregister_swnodes; diff --git a/drivers/platform/x86/redmi-wmi.c b/drivers/platform/x86/redmi-wmi.c index e5cb348e3a39..58898630eda6 100644 --- a/drivers/platform/x86/redmi-wmi.c +++ b/drivers/platform/x86/redmi-wmi.c @@ -141,6 +141,7 @@ static struct wmi_driver redmi_wmi_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .id_table = redmi_wmi_id_table, + .min_event_size = 32, .probe = redmi_wmi_probe, .notify = redmi_wmi_notify, .no_singleton = true, diff --git a/drivers/platform/x86/samsung-galaxybook.c b/drivers/platform/x86/samsung-galaxybook.c index 755cb82bdb60..6382af0b106c 100644 --- a/drivers/platform/x86/samsung-galaxybook.c +++ b/drivers/platform/x86/samsung-galaxybook.c @@ -53,7 +53,7 @@ struct samsung_galaxybook { void *i8042_filter_ptr; struct work_struct block_recording_hotkey_work; - struct input_dev *camera_lens_cover_switch; + struct input_dev *input; struct acpi_battery_hook battery_hook; @@ -197,6 +197,9 @@ static const guid_t performance_mode_guid = #define GB_ACPI_NOTIFY_DEVICE_ON_TABLE 0x6c #define GB_ACPI_NOTIFY_DEVICE_OFF_TABLE 0x6d #define GB_ACPI_NOTIFY_HOTKEY_PERFORMANCE_MODE 0x70 +#define GB_ACPI_NOTIFY_HOTKEY_KBD_BACKLIGHT 0x7d +#define GB_ACPI_NOTIFY_HOTKEY_MICMUTE 0x6e +#define GB_ACPI_NOTIFY_HOTKEY_CAMERA 0x6f #define GB_KEY_KBD_BACKLIGHT_KEYDOWN 0x2c #define GB_KEY_KBD_BACKLIGHT_KEYUP 0xac @@ -859,13 +862,29 @@ static int block_recording_acpi_set(struct samsung_galaxybook *galaxybook, const if (err) return err; - input_report_switch(galaxybook->camera_lens_cover_switch, + input_report_switch(galaxybook->input, SW_CAMERA_LENS_COVER, value ? 1 : 0); - input_sync(galaxybook->camera_lens_cover_switch); + input_sync(galaxybook->input); return 0; } +static int galaxybook_input_init(struct samsung_galaxybook *galaxybook) +{ + galaxybook->input = devm_input_allocate_device(&galaxybook->platform->dev); + if (!galaxybook->input) + return -ENOMEM; + + galaxybook->input->name = "Samsung Galaxy Book Camera Lens Cover"; + galaxybook->input->phys = DRIVER_NAME "/input0"; + galaxybook->input->id.bustype = BUS_HOST; + + input_set_capability(galaxybook->input, EV_KEY, KEY_MICMUTE); + input_set_capability(galaxybook->input, EV_SW, SW_CAMERA_LENS_COVER); + + return input_register_device(galaxybook->input); +} + static int galaxybook_block_recording_init(struct samsung_galaxybook *galaxybook) { bool value; @@ -887,24 +906,8 @@ static int galaxybook_block_recording_init(struct samsung_galaxybook *galaxybook return GB_NOT_SUPPORTED; } - galaxybook->camera_lens_cover_switch = - devm_input_allocate_device(&galaxybook->platform->dev); - if (!galaxybook->camera_lens_cover_switch) - return -ENOMEM; - - galaxybook->camera_lens_cover_switch->name = "Samsung Galaxy Book Camera Lens Cover"; - galaxybook->camera_lens_cover_switch->phys = DRIVER_NAME "/input0"; - galaxybook->camera_lens_cover_switch->id.bustype = BUS_HOST; - - input_set_capability(galaxybook->camera_lens_cover_switch, EV_SW, SW_CAMERA_LENS_COVER); - - err = input_register_device(galaxybook->camera_lens_cover_switch); - if (err) - return err; - - input_report_switch(galaxybook->camera_lens_cover_switch, - SW_CAMERA_LENS_COVER, value ? 1 : 0); - input_sync(galaxybook->camera_lens_cover_switch); + input_report_switch(galaxybook->input, SW_CAMERA_LENS_COVER, value ? 1 : 0); + input_sync(galaxybook->input); return 0; } @@ -1260,6 +1263,25 @@ static void galaxybook_acpi_notify(acpi_handle handle, u32 event, void *data) if (galaxybook->has_performance_mode) platform_profile_cycle(); break; + case GB_ACPI_NOTIFY_HOTKEY_KBD_BACKLIGHT: + if (galaxybook->has_kbd_backlight) + schedule_work(&galaxybook->kbd_backlight_hotkey_work); + break; + case GB_ACPI_NOTIFY_HOTKEY_MICMUTE: + input_report_key(galaxybook->input, KEY_MICMUTE, 1); + input_sync(galaxybook->input); + input_report_key(galaxybook->input, KEY_MICMUTE, 0); + input_sync(galaxybook->input); + break; + case GB_ACPI_NOTIFY_HOTKEY_CAMERA: + if (galaxybook->has_block_recording) { + schedule_work(&galaxybook->block_recording_hotkey_work); + } else { + input_report_switch(galaxybook->input, SW_CAMERA_LENS_COVER, + !test_bit(SW_CAMERA_LENS_COVER, galaxybook->input->sw)); + input_sync(galaxybook->input); + } + break; default: dev_warn(&galaxybook->platform->dev, "unknown ACPI notification event: 0x%x\n", event); @@ -1392,6 +1414,11 @@ static int galaxybook_probe(struct platform_device *pdev) return dev_err_probe(&galaxybook->platform->dev, err, "failed to initialize kbd_backlight\n"); + err = galaxybook_input_init(galaxybook); + if (err) + return dev_err_probe(&galaxybook->platform->dev, err, + "failed to initialize input device\n"); + err = galaxybook_fw_attrs_init(galaxybook); if (err) return dev_err_probe(&galaxybook->platform->dev, err, diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index d3e7a52c22a7..b18f00e9082f 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -178,8 +178,7 @@ enum sony_nc_rfkill { static int sony_rfkill_handle; static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL]; static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900}; -static int sony_nc_rfkill_setup(struct acpi_device *device, - unsigned int handle); +static int sony_nc_rfkill_setup(struct device *dev, unsigned int handle); static void sony_nc_rfkill_cleanup(void); static void sony_nc_rfkill_update(void); @@ -435,7 +434,7 @@ static void sony_laptop_report_input_event(u8 event) dprintk("unknown input event %.2x\n", event); } -static int sony_laptop_setup_input(struct acpi_device *acpi_device) +static int sony_laptop_setup_input(struct device *parent) { struct input_dev *jog_dev; struct input_dev *key_dev; @@ -468,7 +467,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) key_dev->name = "Sony Vaio Keys"; key_dev->id.bustype = BUS_ISA; key_dev->id.vendor = PCI_VENDOR_ID_SONY; - key_dev->dev.parent = &acpi_device->dev; + key_dev->dev.parent = parent; /* Initialize the Input Drivers: special keys */ input_set_capability(key_dev, EV_MSC, MSC_SCAN); @@ -497,7 +496,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) jog_dev->name = "Sony Vaio Jogdial"; jog_dev->id.bustype = BUS_ISA; jog_dev->id.vendor = PCI_VENDOR_ID_SONY; - jog_dev->dev.parent = &acpi_device->dev; + jog_dev->dev.parent = parent; input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE); input_set_capability(jog_dev, EV_REL, REL_WHEEL); @@ -1176,7 +1175,7 @@ enum event_types { KILLSWITCH, GFX_SWITCH }; -static void sony_nc_notify(struct acpi_device *device, u32 event) +static void sony_nc_notify(acpi_handle ah, u32 event, void *data) { u32 real_ev = event; u8 ev_type = 0; @@ -1287,7 +1286,7 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, /* * ACPI device */ -static void sony_nc_function_setup(struct acpi_device *device, +static void sony_nc_function_setup(struct device *dev, struct platform_device *pf_device) { unsigned int i, result, bitmask, arg; @@ -1360,7 +1359,7 @@ static void sony_nc_function_setup(struct acpi_device *device, break; case 0x0124: case 0x0135: - result = sony_nc_rfkill_setup(device, handle); + result = sony_nc_rfkill_setup(dev, handle); if (result) pr_err("couldn't set up rfkill support (%d)\n", result); @@ -1600,8 +1599,7 @@ static const struct rfkill_ops sony_rfkill_ops = { .set_block = sony_nc_rfkill_set, }; -static int sony_nc_setup_rfkill(struct acpi_device *device, - enum sony_nc_rfkill nc_type) +static int sony_nc_setup_rfkill(struct device *parent, enum sony_nc_rfkill nc_type) { int err; struct rfkill *rfk; @@ -1631,8 +1629,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, return -EINVAL; } - rfk = rfkill_alloc(name, &device->dev, type, - &sony_rfkill_ops, (void *)nc_type); + rfk = rfkill_alloc(name, parent, type, &sony_rfkill_ops, (void *)nc_type); if (!rfk) return -ENOMEM; @@ -1692,8 +1689,7 @@ static void sony_nc_rfkill_update(void) } } -static int sony_nc_rfkill_setup(struct acpi_device *device, - unsigned int handle) +static int sony_nc_rfkill_setup(struct device *parent, unsigned int handle) { u64 offset; int i; @@ -1734,18 +1730,18 @@ static int sony_nc_rfkill_setup(struct acpi_device *device, dprintk("Radio devices, found 0x%.2x\n", buffer[i]); if (buffer[i] == 0 && !sony_rfkill_devices[SONY_WIFI]) - sony_nc_setup_rfkill(device, SONY_WIFI); + sony_nc_setup_rfkill(parent, SONY_WIFI); if (buffer[i] == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH]) - sony_nc_setup_rfkill(device, SONY_BLUETOOTH); + sony_nc_setup_rfkill(parent, SONY_BLUETOOTH); if (((0xf0 & buffer[i]) == 0x20 || (0xf0 & buffer[i]) == 0x50) && !sony_rfkill_devices[SONY_WWAN]) - sony_nc_setup_rfkill(device, SONY_WWAN); + sony_nc_setup_rfkill(parent, SONY_WWAN); if (buffer[i] == 0x30 && !sony_rfkill_devices[SONY_WIMAX]) - sony_nc_setup_rfkill(device, SONY_WIMAX); + sony_nc_setup_rfkill(parent, SONY_WIMAX); } return 0; } @@ -3149,8 +3145,9 @@ static void sony_nc_backlight_cleanup(void) backlight_device_unregister(sony_bl_props.dev); } -static int sony_nc_add(struct acpi_device *device) +static int sony_nc_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_status status; int result = 0; struct sony_nc_value *item; @@ -3184,7 +3181,7 @@ static int sony_nc_add(struct acpi_device *device) } } - result = sony_laptop_setup_input(device); + result = sony_laptop_setup_input(&pdev->dev); if (result) { pr_err("Unable to create input devices\n"); goto outplatform; @@ -3201,7 +3198,7 @@ static int sony_nc_add(struct acpi_device *device) /* retrieve the available handles */ result = sony_nc_handles_setup(sony_pf_device); if (!result) - sony_nc_function_setup(device, sony_pf_device); + sony_nc_function_setup(&pdev->dev, sony_pf_device); } if (acpi_video_get_backlight_type() == acpi_backlight_vendor) @@ -3244,6 +3241,11 @@ static int sony_nc_add(struct acpi_device *device) } } + result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + sony_nc_notify, NULL); + if (result) + goto out_sysfs; + pr_info("SNC setup done.\n"); return 0; @@ -3266,10 +3268,13 @@ outwalk: return result; } -static void sony_nc_remove(struct acpi_device *device) +static void sony_nc_remove(struct platform_device *pdev) { struct sony_nc_value *item; + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, sony_nc_notify); + sony_nc_backlight_cleanup(); sony_nc_acpi_device = NULL; @@ -3297,16 +3302,14 @@ static const struct acpi_device_id sony_nc_device_ids[] = { {"", 0}, }; -static struct acpi_driver sony_nc_driver = { - .name = SONY_NC_DRIVER_NAME, - .class = SONY_NC_CLASS, - .ids = sony_nc_device_ids, - .ops = { - .add = sony_nc_add, - .remove = sony_nc_remove, - .notify = sony_nc_notify, - }, - .drv.pm = &sony_nc_pm, +static struct platform_driver sony_nc_driver = { + .probe = sony_nc_probe, + .remove = sony_nc_remove, + .driver = { + .name = SONY_NC_DRIVER_NAME, + .acpi_match_table = sony_nc_device_ids, + .pm = &sony_nc_pm, + }, }; /*********** SPIC (SNY6001) Device ***********/ @@ -4276,9 +4279,9 @@ end: /* * Disable the spic device by calling its _DIS method */ -static int sony_pic_disable(struct acpi_device *device) +static int sony_pic_disable(struct device *dev) { - acpi_status ret = acpi_evaluate_object(device->handle, "_DIS", NULL, + acpi_status ret = acpi_evaluate_object(ACPI_HANDLE(dev), "_DIS", NULL, NULL); if (ACPI_FAILURE(ret) && ret != AE_NOT_FOUND) @@ -4294,7 +4297,7 @@ static int sony_pic_disable(struct acpi_device *device) * * Call _SRS to set current resources */ -static int sony_pic_enable(struct acpi_device *device, +static int sony_pic_enable(struct device *dev, struct sony_pic_ioport *ioport, struct sony_pic_irq *irq) { acpi_status status; @@ -4376,7 +4379,7 @@ static int sony_pic_enable(struct acpi_device *device, /* Attempt to set the resource */ dprintk("Evaluating _SRS\n"); - status = acpi_set_current_resources(device->handle, &buffer); + status = acpi_set_current_resources(ACPI_HANDLE(dev), &buffer); /* check for total failure */ if (ACPI_FAILURE(status)) { @@ -4465,12 +4468,12 @@ found: * ACPI driver * *****************/ -static void sony_pic_remove(struct acpi_device *device) +static void sony_pic_remove(struct platform_device *pdev) { struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; - if (sony_pic_disable(device)) { + if (sony_pic_disable(&pdev->dev)) { pr_err("Couldn't disable device\n"); return; } @@ -4504,11 +4507,12 @@ static void sony_pic_remove(struct acpi_device *device) dprintk(SONY_PIC_DRIVER_NAME " removed.\n"); } -static int sony_pic_add(struct acpi_device *device) +static int sony_pic_probe(struct platform_device *pdev) { - int result; + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; + int result; spic_dev.acpi_dev = device; strscpy(acpi_device_class(device), "sony/hotkey"); @@ -4523,7 +4527,7 @@ static int sony_pic_add(struct acpi_device *device) } /* setup input devices and helper fifo */ - result = sony_laptop_setup_input(device); + result = sony_laptop_setup_input(&pdev->dev); if (result) { pr_err("Unable to create input devices\n"); goto err_free_resources; @@ -4593,7 +4597,7 @@ static int sony_pic_add(struct acpi_device *device) } /* set resource status _SRS */ - result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); + result = sony_pic_enable(&pdev->dev, spic_dev.cur_ioport, spic_dev.cur_irq); if (result) { pr_err("Couldn't enable device\n"); goto err_free_irq; @@ -4616,7 +4620,7 @@ err_remove_pf: sony_pf_remove(); err_disable_device: - sony_pic_disable(device); + sony_pic_disable(&pdev->dev); err_free_irq: free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); @@ -4652,15 +4656,14 @@ err_free_resources: #ifdef CONFIG_PM_SLEEP static int sony_pic_suspend(struct device *dev) { - if (sony_pic_disable(to_acpi_device(dev))) + if (sony_pic_disable(dev)) return -ENXIO; return 0; } static int sony_pic_resume(struct device *dev) { - sony_pic_enable(to_acpi_device(dev), - spic_dev.cur_ioport, spic_dev.cur_irq); + sony_pic_enable(dev, spic_dev.cur_ioport, spic_dev.cur_irq); return 0; } #endif @@ -4672,15 +4675,14 @@ static const struct acpi_device_id sony_pic_device_ids[] = { {"", 0}, }; -static struct acpi_driver sony_pic_driver = { - .name = SONY_PIC_DRIVER_NAME, - .class = SONY_PIC_CLASS, - .ids = sony_pic_device_ids, - .ops = { - .add = sony_pic_add, - .remove = sony_pic_remove, - }, - .drv.pm = &sony_pic_pm, +static struct platform_driver sony_pic_driver = { + .probe = sony_pic_probe, + .remove = sony_pic_remove, + .driver = { + .name = SONY_PIC_DRIVER_NAME, + .acpi_match_table = sony_pic_device_ids, + .pm = &sony_pic_pm, + }, }; static const struct dmi_system_id sonypi_dmi_table[] __initconst = { @@ -4706,7 +4708,7 @@ static int __init sony_laptop_init(void) int result; if (!no_spic && dmi_check_system(sonypi_dmi_table)) { - result = acpi_bus_register_driver(&sony_pic_driver); + result = platform_driver_register(&sony_pic_driver); if (result) { pr_err("Unable to register SPIC driver\n"); goto out; @@ -4714,7 +4716,7 @@ static int __init sony_laptop_init(void) spic_drv_registered = 1; } - result = acpi_bus_register_driver(&sony_nc_driver); + result = platform_driver_register(&sony_nc_driver); if (result) { pr_err("Unable to register SNC driver\n"); goto out_unregister_pic; @@ -4724,16 +4726,16 @@ static int __init sony_laptop_init(void) out_unregister_pic: if (spic_drv_registered) - acpi_bus_unregister_driver(&sony_pic_driver); + platform_driver_unregister(&sony_pic_driver); out: return result; } static void __exit sony_laptop_exit(void) { - acpi_bus_unregister_driver(&sony_nc_driver); + platform_driver_unregister(&sony_nc_driver); if (spic_drv_registered) - acpi_bus_unregister_driver(&sony_pic_driver); + platform_driver_unregister(&sony_pic_driver); } module_init(sony_laptop_init); diff --git a/drivers/platform/x86/system76_acpi.c b/drivers/platform/x86/system76_acpi.c index 3da753b3d00d..693cbb461382 100644 --- a/drivers/platform/x86/system76_acpi.c +++ b/drivers/platform/x86/system76_acpi.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -644,11 +645,10 @@ static void input_key(struct system76_data *data, unsigned int code) } // Handle ACPI notification -static void system76_notify(struct acpi_device *acpi_dev, u32 event) +static void system76_notify(acpi_handle handle, u32 event, void *context) { - struct system76_data *data; + struct system76_data *data = context; - data = acpi_driver_data(acpi_dev); switch (event) { case 0x80: kb_led_hotkey_hardware(data); @@ -671,16 +671,19 @@ static void system76_notify(struct acpi_device *acpi_dev, u32 event) } } -// Add a System76 ACPI device -static int system76_add(struct acpi_device *acpi_dev) +// Probe a System76 platform device +static int system76_probe(struct platform_device *pdev) { + struct acpi_device *acpi_dev = ACPI_COMPANION(&pdev->dev); struct system76_data *data; int err; - data = devm_kzalloc(&acpi_dev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - acpi_dev->driver_data = data; + + platform_set_drvdata(pdev, data); + data->acpi_dev = acpi_dev; // Some models do not run open EC firmware. Check for an ACPI method @@ -696,7 +699,7 @@ static int system76_add(struct acpi_device *acpi_dev) data->ap_led.brightness_set_blocking = ap_led_set; data->ap_led.max_brightness = 1; data->ap_led.default_trigger = "rfkill-none"; - err = devm_led_classdev_register(&acpi_dev->dev, &data->ap_led); + err = devm_led_classdev_register(&pdev->dev, &data->ap_led); if (err) return err; @@ -740,24 +743,29 @@ static int system76_add(struct acpi_device *acpi_dev) } if (data->kbled_type != KBLED_NONE) { - err = devm_led_classdev_register(&acpi_dev->dev, &data->kb_led); + err = devm_led_classdev_register(&pdev->dev, &data->kb_led); if (err) return err; } - data->input = devm_input_allocate_device(&acpi_dev->dev); + data->input = devm_input_allocate_device(&pdev->dev); if (!data->input) return -ENOMEM; data->input->name = "System76 ACPI Hotkeys"; data->input->phys = "system76_acpi/input0"; data->input->id.bustype = BUS_HOST; - data->input->dev.parent = &acpi_dev->dev; + data->input->dev.parent = &pdev->dev; input_set_capability(data->input, EV_KEY, KEY_SCREENLOCK); err = input_register_device(data->input); if (err) - goto error; + return err; + + err = acpi_dev_install_notify_handler(acpi_dev, ACPI_DEVICE_NOTIFY, + system76_notify, data); + if (err) + return err; if (data->has_open_ec) { err = system76_get_object(data, "NFAN", &data->nfan); @@ -768,7 +776,7 @@ static int system76_add(struct acpi_device *acpi_dev) if (err) goto error; - data->therm = devm_hwmon_device_register_with_info(&acpi_dev->dev, + data->therm = devm_hwmon_device_register_with_info(&pdev->dev, "system76_acpi", data, &thermal_chip_info, NULL); err = PTR_ERR_OR_ZERO(data->therm); if (err) @@ -784,15 +792,14 @@ error: kfree(data->ntmp); kfree(data->nfan); } + acpi_dev_remove_notify_handler(acpi_dev, ACPI_DEVICE_NOTIFY, system76_notify); return err; } -// Remove a System76 ACPI device -static void system76_remove(struct acpi_device *acpi_dev) +// Remove a System76 platform device +static void system76_remove(struct platform_device *pdev) { - struct system76_data *data; - - data = acpi_driver_data(acpi_dev); + struct system76_data *data = platform_get_drvdata(pdev); if (data->has_open_ec) { system76_battery_exit(); @@ -800,23 +807,21 @@ static void system76_remove(struct acpi_device *acpi_dev) kfree(data->ntmp); } - devm_led_classdev_unregister(&acpi_dev->dev, &data->ap_led); - devm_led_classdev_unregister(&acpi_dev->dev, &data->kb_led); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, system76_notify); system76_get(data, "FINI"); } -static struct acpi_driver system76_driver = { - .name = "System76 ACPI Driver", - .class = "hotkey", - .ids = device_ids, - .ops = { - .add = system76_add, - .remove = system76_remove, - .notify = system76_notify, +static struct platform_driver system76_driver = { + .probe = system76_probe, + .remove = system76_remove, + .driver = { + .name = "System76 ACPI Driver", + .acpi_match_table = device_ids, }, }; -module_acpi_driver(system76_driver); +module_platform_driver(system76_driver); MODULE_DESCRIPTION("System76 ACPI Driver"); MODULE_AUTHOR("Jeremy Soller "); diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index a7b4b6c8e549..e09d7f8ce45f 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -232,9 +232,9 @@ static int topstar_acpi_fncx_switch(struct acpi_device *device, bool state) return 0; } -static void topstar_acpi_notify(struct acpi_device *device, u32 event) +static void topstar_acpi_notify(acpi_handle handle, u32 event, void *data) { - struct topstar_laptop *topstar = acpi_driver_data(device); + struct topstar_laptop *topstar = data; static bool dup_evnt[2]; bool *dup; @@ -285,8 +285,9 @@ static const struct dmi_system_id topstar_dmi_ids[] = { {} }; -static int topstar_acpi_add(struct acpi_device *device) +static int topstar_acpi_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct topstar_laptop *topstar; int err; @@ -296,9 +297,10 @@ static int topstar_acpi_add(struct acpi_device *device) if (!topstar) return -ENOMEM; + platform_set_drvdata(pdev, topstar); + strscpy(acpi_device_name(device), "Topstar TPSACPI"); strscpy(acpi_device_class(device), TOPSTAR_LAPTOP_CLASS); - device->driver_data = topstar; topstar->device = device; err = topstar_acpi_init(topstar); @@ -313,14 +315,21 @@ static int topstar_acpi_add(struct acpi_device *device) if (err) goto err_platform_exit; + err = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + topstar_acpi_notify, topstar); + if (err) + goto err_input_exit; + if (led_workaround) { err = topstar_led_init(topstar); if (err) - goto err_input_exit; + goto err_notify_handler_exit; } return 0; +err_notify_handler_exit: + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, topstar_acpi_notify); err_input_exit: topstar_input_exit(topstar); err_platform_exit: @@ -332,13 +341,15 @@ err_free: return err; } -static void topstar_acpi_remove(struct acpi_device *device) +static void topstar_acpi_remove(struct platform_device *pdev) { - struct topstar_laptop *topstar = acpi_driver_data(device); + struct topstar_laptop *topstar = platform_get_drvdata(pdev); if (led_workaround) topstar_led_exit(topstar); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, topstar_acpi_notify); topstar_input_exit(topstar); topstar_platform_exit(topstar); topstar_acpi_exit(topstar); @@ -353,14 +364,12 @@ static const struct acpi_device_id topstar_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, topstar_device_ids); -static struct acpi_driver topstar_acpi_driver = { - .name = "Topstar laptop ACPI driver", - .class = TOPSTAR_LAPTOP_CLASS, - .ids = topstar_device_ids, - .ops = { - .add = topstar_acpi_add, - .remove = topstar_acpi_remove, - .notify = topstar_acpi_notify, +static struct platform_driver topstar_acpi_driver = { + .probe = topstar_acpi_probe, + .remove = topstar_acpi_remove, + .driver = { + .name = "Topstar laptop ACPI driver", + .acpi_match_table = topstar_device_ids, }, }; @@ -372,7 +381,7 @@ static int __init topstar_laptop_init(void) if (ret < 0) return ret; - ret = acpi_bus_register_driver(&topstar_acpi_driver); + ret = platform_driver_register(&topstar_acpi_driver); if (ret < 0) goto err_driver_unreg; @@ -386,7 +395,7 @@ err_driver_unreg: static void __exit topstar_laptop_exit(void) { - acpi_bus_unregister_driver(&topstar_acpi_driver); + platform_driver_unregister(&topstar_acpi_driver); platform_driver_unregister(&topstar_platform_driver); } diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 18fb558115aa..35d899c01740 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -223,6 +224,7 @@ struct toshiba_acpi_dev { unsigned int cooling_method_supported:1; unsigned int battery_charge_mode_supported:1; unsigned int sysfs_created:1; + unsigned int notify_handler_installed:1; unsigned int special_functions; bool kbd_event_generated; @@ -3193,14 +3195,80 @@ static void print_supported_features(struct toshiba_acpi_dev *dev) pr_cont("\n"); } -static void toshiba_acpi_remove(struct acpi_device *acpi_dev) +static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *data) { - struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + struct toshiba_acpi_dev *dev = data; + struct acpi_device *acpi_dev = dev->acpi_dev; + + switch (event) { + case 0x80: /* Hotkeys and some system events */ + /* + * Machines with this WMI GUID aren't supported due to bugs in + * their AML. + * + * Return silently to avoid triggering a netlink event. + */ + if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) + return; + toshiba_acpi_process_hotkeys(dev); + break; + case 0x81: /* Dock events */ + case 0x82: + case 0x83: + pr_info("Dock event received %x\n", event); + break; + case 0x88: /* Thermal events */ + pr_info("Thermal event received\n"); + break; + case 0x8f: /* LID closed */ + case 0x90: /* LID is closed and Dock has been ejected */ + break; + case 0x8c: /* SATA power events */ + case 0x8b: + pr_info("SATA power event received %x\n", event); + break; + case 0x92: /* Keyboard backlight mode changed */ + dev->kbd_event_generated = true; + /* Update sysfs entries */ + if (sysfs_update_group(&acpi_dev->dev.kobj, + &toshiba_attr_group)) + pr_err("Unable to update sysfs entries\n"); + /* Notify LED subsystem about keyboard backlight change */ + if (dev->kbd_type == 2 && dev->kbd_mode != SCI_KBD_MODE_AUTO) + led_classdev_notify_brightness_hw_changed(&dev->kbd_led, + (dev->kbd_mode == SCI_KBD_MODE_ON) ? + LED_FULL : LED_OFF); + break; + case 0x8e: /* Power button pressed */ + break; + case 0x85: /* Unknown */ + case 0x8d: /* Unknown */ + case 0x94: /* Unknown */ + case 0x95: /* Unknown */ + default: + pr_info("Unknown event received %x\n", event); + break; + } + + acpi_bus_generate_netlink_event(acpi_dev->pnp.device_class, + dev_name(&acpi_dev->dev), + event, (event == 0x80) ? + dev->last_key_event : 0); +} + +static void toshiba_acpi_remove(struct platform_device *pdev) +{ + struct toshiba_acpi_dev *dev = platform_get_drvdata(pdev); misc_deregister(&dev->miscdev); remove_toshiba_proc_entries(dev); + if (dev->notify_handler_installed) + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, + toshiba_acpi_notify); + #if IS_ENABLED(CONFIG_HWMON) if (dev->hwmon_device) hwmon_device_unregister(dev->hwmon_device); @@ -3240,6 +3308,8 @@ static void toshiba_acpi_remove(struct acpi_device *acpi_dev) if (toshiba_acpi) toshiba_acpi = NULL; + dev_set_drvdata(&dev->acpi_dev->dev, NULL); + kfree(dev); } @@ -3302,8 +3372,9 @@ static const struct dmi_system_id toshiba_dmi_quirks[] __initconst = { { } }; -static int toshiba_acpi_add(struct acpi_device *acpi_dev) +static int toshiba_acpi_probe(struct platform_device *pdev) { + struct acpi_device *acpi_dev = ACPI_COMPANION(&pdev->dev); struct toshiba_acpi_dev *dev; const char *hci_method; u32 dummy; @@ -3337,7 +3408,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) return ret; } - acpi_dev->driver_data = dev; + platform_set_drvdata(pdev, dev); dev_set_drvdata(&acpi_dev->dev, dev); /* Query the BIOS for supported features */ @@ -3368,7 +3439,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) dev->led_dev.max_brightness = 1; dev->led_dev.brightness_set = toshiba_illumination_set; dev->led_dev.brightness_get = toshiba_illumination_get; - led_classdev_register(&acpi_dev->dev, &dev->led_dev); + led_classdev_register(&pdev->dev, &dev->led_dev); } toshiba_eco_mode_available(dev); @@ -3377,7 +3448,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) dev->eco_led.max_brightness = 1; dev->eco_led.brightness_set = toshiba_eco_mode_set_status; dev->eco_led.brightness_get = toshiba_eco_mode_get_status; - led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led); + led_classdev_register(&pdev->dev, &dev->eco_led); } toshiba_kbd_illum_available(dev); @@ -3393,7 +3464,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) dev->kbd_led.max_brightness = 1; dev->kbd_led.brightness_set = toshiba_kbd_backlight_set; dev->kbd_led.brightness_get = toshiba_kbd_backlight_get; - led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led); + led_classdev_register(&pdev->dev, &dev->kbd_led); } ret = toshiba_touchpad_get(dev, &dummy); @@ -3401,7 +3472,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) toshiba_accelerometer_available(dev); if (dev->accelerometer_supported) { - dev->indio_dev = iio_device_alloc(&acpi_dev->dev, sizeof(*dev)); + dev->indio_dev = iio_device_alloc(&pdev->dev, sizeof(*dev)); if (!dev->indio_dev) { pr_err("Unable to allocate iio device\n"); goto iio_error; @@ -3450,7 +3521,7 @@ iio_error: #if IS_ENABLED(CONFIG_HWMON) if (dev->fan_rpm_supported) { dev->hwmon_device = hwmon_device_register_with_info( - &dev->acpi_dev->dev, "toshiba_acpi_sensors", NULL, + &pdev->dev, "toshiba_acpi_sensors", NULL, &toshiba_acpi_hwmon_chip_info, NULL); if (IS_ERR(dev->hwmon_device)) { dev->hwmon_device = NULL; @@ -3477,6 +3548,13 @@ iio_error: } dev->sysfs_created = !ret; + ret = acpi_dev_install_notify_handler(acpi_dev, ACPI_DEVICE_NOTIFY, + toshiba_acpi_notify, dev); + if (ret) + goto error; + + dev->notify_handler_installed = 1; + create_toshiba_proc_entries(dev); toshiba_acpi = dev; @@ -3491,74 +3569,14 @@ iio_error: return 0; error: - toshiba_acpi_remove(acpi_dev); + toshiba_acpi_remove(pdev); return ret; } -static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) -{ - struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); - - switch (event) { - case 0x80: /* Hotkeys and some system events */ - /* - * Machines with this WMI GUID aren't supported due to bugs in - * their AML. - * - * Return silently to avoid triggering a netlink event. - */ - if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) - return; - toshiba_acpi_process_hotkeys(dev); - break; - case 0x81: /* Dock events */ - case 0x82: - case 0x83: - pr_info("Dock event received %x\n", event); - break; - case 0x88: /* Thermal events */ - pr_info("Thermal event received\n"); - break; - case 0x8f: /* LID closed */ - case 0x90: /* LID is closed and Dock has been ejected */ - break; - case 0x8c: /* SATA power events */ - case 0x8b: - pr_info("SATA power event received %x\n", event); - break; - case 0x92: /* Keyboard backlight mode changed */ - dev->kbd_event_generated = true; - /* Update sysfs entries */ - if (sysfs_update_group(&acpi_dev->dev.kobj, - &toshiba_attr_group)) - pr_err("Unable to update sysfs entries\n"); - /* Notify LED subsystem about keyboard backlight change */ - if (dev->kbd_type == 2 && dev->kbd_mode != SCI_KBD_MODE_AUTO) - led_classdev_notify_brightness_hw_changed(&dev->kbd_led, - (dev->kbd_mode == SCI_KBD_MODE_ON) ? - LED_FULL : LED_OFF); - break; - case 0x8e: /* Power button pressed */ - break; - case 0x85: /* Unknown */ - case 0x8d: /* Unknown */ - case 0x94: /* Unknown */ - case 0x95: /* Unknown */ - default: - pr_info("Unknown event received %x\n", event); - break; - } - - acpi_bus_generate_netlink_event(acpi_dev->pnp.device_class, - dev_name(&acpi_dev->dev), - event, (event == 0x80) ? - dev->last_key_event : 0); -} - #ifdef CONFIG_PM_SLEEP static int toshiba_acpi_suspend(struct device *device) { - struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); + struct toshiba_acpi_dev *dev = dev_get_drvdata(device); if (dev->hotkey_dev) { u32 result; @@ -3573,7 +3591,7 @@ static int toshiba_acpi_suspend(struct device *device) static int toshiba_acpi_resume(struct device *device) { - struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); + struct toshiba_acpi_dev *dev = dev_get_drvdata(device); if (dev->hotkey_dev) { if (toshiba_acpi_enable_hotkeys(dev)) @@ -3595,16 +3613,14 @@ static int toshiba_acpi_resume(struct device *device) static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm, toshiba_acpi_suspend, toshiba_acpi_resume); -static struct acpi_driver toshiba_acpi_driver = { - .name = "Toshiba ACPI driver", - .ids = toshiba_device_ids, - .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, - .ops = { - .add = toshiba_acpi_add, - .remove = toshiba_acpi_remove, - .notify = toshiba_acpi_notify, +static struct platform_driver toshiba_acpi_driver = { + .probe = toshiba_acpi_probe, + .remove = toshiba_acpi_remove, + .driver = { + .name = "Toshiba ACPI driver", + .acpi_match_table = toshiba_device_ids, + .pm = &toshiba_acpi_pm, }, - .drv.pm = &toshiba_acpi_pm, }; static void __init toshiba_dmi_init(void) @@ -3634,7 +3650,7 @@ static int __init toshiba_acpi_init(void) return -ENODEV; } - ret = acpi_bus_register_driver(&toshiba_acpi_driver); + ret = platform_driver_register(&toshiba_acpi_driver); if (ret) { pr_err("Failed to register ACPI driver: %d\n", ret); remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); @@ -3645,7 +3661,7 @@ static int __init toshiba_acpi_init(void) static void __exit toshiba_acpi_exit(void) { - acpi_bus_unregister_driver(&toshiba_acpi_driver); + platform_driver_unregister(&toshiba_acpi_driver); if (toshiba_proc_dir) remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); } diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index e587beef05b9..e50d4fc1e603 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -17,6 +17,7 @@ #include #include #include +#include #define BT_KILLSWITCH_MASK 0x01 #define BT_PLUGGED_MASK 0x40 @@ -35,9 +36,9 @@ struct toshiba_bluetooth_dev { bool powered; }; -static int toshiba_bt_rfkill_add(struct acpi_device *device); -static void toshiba_bt_rfkill_remove(struct acpi_device *device); -static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event); +static int toshiba_bt_rfkill_probe(struct platform_device *pdev); +static void toshiba_bt_rfkill_remove(struct platform_device *pdev); +static void toshiba_bt_rfkill_notify(acpi_handle handle, u32 event, void *data); static const struct acpi_device_id bt_device_ids[] = { { "TOS6205", 0}, @@ -50,16 +51,14 @@ static int toshiba_bt_resume(struct device *dev); #endif static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume); -static struct acpi_driver toshiba_bt_rfkill_driver = { - .name = "Toshiba BT", - .class = "Toshiba", - .ids = bt_device_ids, - .ops = { - .add = toshiba_bt_rfkill_add, - .remove = toshiba_bt_rfkill_remove, - .notify = toshiba_bt_rfkill_notify, - }, - .drv.pm = &toshiba_bt_pm, +static struct platform_driver toshiba_bt_rfkill_driver = { + .probe = toshiba_bt_rfkill_probe, + .remove = toshiba_bt_rfkill_remove, + .driver = { + .name = "Toshiba BT", + .acpi_match_table = bt_device_ids, + .pm = &toshiba_bt_pm, + }, }; static int toshiba_bluetooth_present(acpi_handle handle) @@ -203,9 +202,9 @@ static const struct rfkill_ops rfk_ops = { }; /* ACPI driver functions */ -static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event) +static void toshiba_bt_rfkill_notify(acpi_handle handle, u32 event, void *data) { - struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device); + struct toshiba_bluetooth_dev *bt_dev = data; if (toshiba_bluetooth_sync_status(bt_dev)) return; @@ -216,11 +215,9 @@ static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event) #ifdef CONFIG_PM_SLEEP static int toshiba_bt_resume(struct device *dev) { - struct toshiba_bluetooth_dev *bt_dev; + struct toshiba_bluetooth_dev *bt_dev = dev_get_drvdata(dev); int ret; - bt_dev = acpi_driver_data(to_acpi_device(dev)); - ret = toshiba_bluetooth_sync_status(bt_dev); if (ret) return ret; @@ -231,8 +228,9 @@ static int toshiba_bt_resume(struct device *dev) } #endif -static int toshiba_bt_rfkill_add(struct acpi_device *device) +static int toshiba_bt_rfkill_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct toshiba_bluetooth_dev *bt_dev; int result; @@ -246,8 +244,8 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device) if (!bt_dev) return -ENOMEM; bt_dev->acpi_dev = device; - device->driver_data = bt_dev; - dev_set_drvdata(&device->dev, bt_dev); + + platform_set_drvdata(pdev, bt_dev); result = toshiba_bluetooth_sync_status(bt_dev); if (result) { @@ -256,14 +254,14 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device) } bt_dev->rfk = rfkill_alloc("Toshiba Bluetooth", - &device->dev, + &pdev->dev, RFKILL_TYPE_BLUETOOTH, &rfk_ops, bt_dev); if (!bt_dev->rfk) { pr_err("Unable to allocate rfkill device\n"); - kfree(bt_dev); - return -ENOMEM; + result = -ENOMEM; + goto err_free_bt_dev; } rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch); @@ -271,18 +269,36 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device) result = rfkill_register(bt_dev->rfk); if (result) { pr_err("Unable to register rfkill device\n"); - rfkill_destroy(bt_dev->rfk); - kfree(bt_dev); + goto err_rfkill_destroy; } + result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + toshiba_bt_rfkill_notify, bt_dev); + if (result) { + pr_err("Unable to register ACPI notify handler\n"); + goto err_rfkill_unregister; + } + + return 0; + +err_rfkill_unregister: + rfkill_unregister(bt_dev->rfk); +err_rfkill_destroy: + rfkill_destroy(bt_dev->rfk); +err_free_bt_dev: + kfree(bt_dev); return result; } -static void toshiba_bt_rfkill_remove(struct acpi_device *device) +static void toshiba_bt_rfkill_remove(struct platform_device *pdev) { - struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device); + struct toshiba_bluetooth_dev *bt_dev = platform_get_drvdata(pdev); + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); /* clean up */ + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + toshiba_bt_rfkill_notify); + if (bt_dev->rfk) { rfkill_unregister(bt_dev->rfk); rfkill_destroy(bt_dev->rfk); @@ -293,4 +309,4 @@ static void toshiba_bt_rfkill_remove(struct acpi_device *device) toshiba_bluetooth_disable(device->handle); } -module_acpi_driver(toshiba_bt_rfkill_driver); +module_platform_driver(toshiba_bt_rfkill_driver); diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c index e9324bf16aea..1486252b5983 100644 --- a/drivers/platform/x86/toshiba_haps.c +++ b/drivers/platform/x86/toshiba_haps.c @@ -12,6 +12,7 @@ #include #include #include +#include MODULE_AUTHOR("Azael Avalos "); MODULE_DESCRIPTION("Toshiba HDD Active Protection Sensor"); @@ -129,8 +130,10 @@ static const struct attribute_group haps_attr_group = { /* * ACPI stuff */ -static void toshiba_haps_notify(struct acpi_device *device, u32 event) +static void toshiba_haps_notify(acpi_handle handle, u32 event, void *data) { + struct acpi_device *device = data; + pr_debug("Received event: 0x%x\n", event); acpi_bus_generate_netlink_event(device->pnp.device_class, @@ -138,12 +141,19 @@ static void toshiba_haps_notify(struct acpi_device *device, u32 event) event, 0); } -static void toshiba_haps_remove(struct acpi_device *device) +static void toshiba_haps_remove(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + toshiba_haps_notify); + sysfs_remove_group(&device->dev.kobj, &haps_attr_group); if (toshiba_haps) toshiba_haps = NULL; + + dev_set_drvdata(&device->dev, NULL); } /* Helper function */ @@ -170,8 +180,9 @@ static int toshiba_haps_available(acpi_handle handle) return 1; } -static int toshiba_haps_add(struct acpi_device *acpi_dev) +static int toshiba_haps_probe(struct platform_device *pdev) { + struct acpi_device *acpi_dev = ACPI_COMPANION(&pdev->dev); struct toshiba_haps_dev *haps; int ret; @@ -183,14 +194,15 @@ static int toshiba_haps_add(struct acpi_device *acpi_dev) pr_info("Toshiba HDD Active Protection Sensor device\n"); - haps = devm_kzalloc(&acpi_dev->dev, sizeof(*haps), GFP_KERNEL); + haps = devm_kzalloc(&pdev->dev, sizeof(*haps), GFP_KERNEL); if (!haps) return -ENOMEM; haps->acpi_dev = acpi_dev; haps->protection_level = 2; - acpi_dev->driver_data = haps; + dev_set_drvdata(&acpi_dev->dev, haps); + platform_set_drvdata(pdev, haps); /* Set the protection level, currently at level 2 (Medium) */ ret = toshiba_haps_protection_level(acpi_dev->handle, 2); @@ -201,19 +213,26 @@ static int toshiba_haps_add(struct acpi_device *acpi_dev) if (ret) return ret; + ret = acpi_dev_install_notify_handler(acpi_dev, ACPI_DEVICE_NOTIFY, + toshiba_haps_notify, acpi_dev); + if (ret) + goto err; + toshiba_haps = haps; return 0; + +err: + sysfs_remove_group(&acpi_dev->dev.kobj, &haps_attr_group); + return ret; } #ifdef CONFIG_PM_SLEEP static int toshiba_haps_suspend(struct device *device) { - struct toshiba_haps_dev *haps; + struct toshiba_haps_dev *haps = dev_get_drvdata(device); int ret; - haps = acpi_driver_data(to_acpi_device(device)); - /* Deactivate the protection on suspend */ ret = toshiba_haps_protection_level(haps->acpi_dev->handle, 0); @@ -222,11 +241,9 @@ static int toshiba_haps_suspend(struct device *device) static int toshiba_haps_resume(struct device *device) { - struct toshiba_haps_dev *haps; + struct toshiba_haps_dev *haps = dev_get_drvdata(device); int ret; - haps = acpi_driver_data(to_acpi_device(device)); - /* Set the stored protection level */ ret = toshiba_haps_protection_level(haps->acpi_dev->handle, haps->protection_level); @@ -249,16 +266,14 @@ static const struct acpi_device_id haps_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, haps_device_ids); -static struct acpi_driver toshiba_haps_driver = { - .name = "Toshiba HAPS", - .ids = haps_device_ids, - .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, - .ops = { - .add = toshiba_haps_add, - .remove = toshiba_haps_remove, - .notify = toshiba_haps_notify, +static struct platform_driver toshiba_haps_driver = { + .probe = toshiba_haps_probe, + .remove = toshiba_haps_remove, + .driver = { + .name = "Toshiba HAPS", + .acpi_match_table = haps_device_ids, + .pm = &toshiba_haps_pm, }, - .drv.pm = &toshiba_haps_pm, }; -module_acpi_driver(toshiba_haps_driver); +module_platform_driver(toshiba_haps_driver); diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index 6341dca20b76..945df5092637 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -110,6 +110,8 @@ #define EC_ADDR_BAT_CYCLE_COUNT_2 0x04A7 #define EC_ADDR_PROJECT_ID 0x0740 +#define PROJECT_ID_PH4TRX1 0x12 +#define PROJECT_ID_PH6TRX1 0x15 #define EC_ADDR_AP_OEM 0x0741 #define ENABLE_MANUAL_CTRL BIT(0) @@ -266,8 +268,8 @@ #define BATTERY_CHARGE_FULL_OVER_24H BIT(3) #define BATTERY_ERM_STATUS_REACHED BIT(4) -#define EC_ADDR_CHARGE_PRIO 0x07CC -#define CHARGING_PERFORMANCE BIT(7) +#define EC_ADDR_USB_C_POWER_PRIORITY 0x07CC +#define USB_C_POWER_PRIORITY BIT(7) /* Same bits as EC_ADDR_LIGHTBAR_AC_CTRL except LIGHTBAR_S3_OFF */ #define EC_ADDR_LIGHTBAR_BAT_CTRL 0x07E2 @@ -319,8 +321,17 @@ #define UNIWILL_FEATURE_TOUCHPAD_TOGGLE BIT(2) #define UNIWILL_FEATURE_LIGHTBAR BIT(3) #define UNIWILL_FEATURE_BATTERY BIT(4) -#define UNIWILL_FEATURE_HWMON BIT(5) -#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(6) +#define UNIWILL_FEATURE_CPU_TEMP BIT(5) +#define UNIWILL_FEATURE_GPU_TEMP BIT(6) +#define UNIWILL_FEATURE_PRIMARY_FAN BIT(7) +#define UNIWILL_FEATURE_SECONDARY_FAN BIT(8) +#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(9) +#define UNIWILL_FEATURE_USB_C_POWER_PRIORITY BIT(10) + +enum usb_c_power_priority_options { + USB_C_POWER_PRIORITY_CHARGING = 0, + USB_C_POWER_PRIORITY_PERFORMANCE, +}; struct uniwill_data { struct device *dev; @@ -340,6 +351,8 @@ struct uniwill_data { struct mutex input_lock; /* Protects input sequence during notify */ struct input_dev *input_device; struct notifier_block nb; + struct mutex usb_c_power_priority_lock; /* Protects dependent bit write and state safe */ + enum usb_c_power_priority_options last_usb_c_power_priority_option; }; struct uniwill_battery_entry { @@ -427,7 +440,7 @@ static const struct key_entry uniwill_keymap[] = { { KE_END } }; -static inline bool uniwill_device_supports(struct uniwill_data *data, +static inline bool uniwill_device_supports(const struct uniwill_data *data, unsigned int features) { return (data->features & features) == features; @@ -524,6 +537,7 @@ static bool uniwill_writeable_reg(struct device *dev, unsigned int reg) case EC_ADDR_CTGP_DB_CTGP_OFFSET: case EC_ADDR_CTGP_DB_TPP_OFFSET: case EC_ADDR_CTGP_DB_DB_OFFSET: + case EC_ADDR_USB_C_POWER_PRIORITY: return true; default: return false; @@ -562,6 +576,7 @@ static bool uniwill_readable_reg(struct device *dev, unsigned int reg) case EC_ADDR_CTGP_DB_CTGP_OFFSET: case EC_ADDR_CTGP_DB_TPP_OFFSET: case EC_ADDR_CTGP_DB_DB_OFFSET: + case EC_ADDR_USB_C_POWER_PRIORITY: return true; default: return false; @@ -584,6 +599,7 @@ static bool uniwill_volatile_reg(struct device *dev, unsigned int reg) case EC_ADDR_TRIGGER: case EC_ADDR_SWITCH_STATUS: case EC_ADDR_CHARGE_CTRL: + case EC_ADDR_USB_C_POWER_PRIORITY: return true; default: return false; @@ -880,6 +896,105 @@ static int uniwill_nvidia_ctgp_init(struct uniwill_data *data) return 0; } +static const char * const usb_c_power_priority_text[] = { + [USB_C_POWER_PRIORITY_CHARGING] = "charging", + [USB_C_POWER_PRIORITY_PERFORMANCE] = "performance", +}; + +static const u8 usb_c_power_priority_value[] = { + [USB_C_POWER_PRIORITY_CHARGING] = 0, + [USB_C_POWER_PRIORITY_PERFORMANCE] = USB_C_POWER_PRIORITY, +}; + +static ssize_t usb_c_power_priority_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + enum usb_c_power_priority_options option; + unsigned int value; + int ret; + + ret = sysfs_match_string(usb_c_power_priority_text, buf); + if (ret < 0) + return ret; + + option = ret; + value = usb_c_power_priority_value[option]; + + guard(mutex)(&data->usb_c_power_priority_lock); + + ret = regmap_update_bits(data->regmap, EC_ADDR_USB_C_POWER_PRIORITY, + USB_C_POWER_PRIORITY, value); + if (ret < 0) + return ret; + + data->last_usb_c_power_priority_option = option; + + return count; +} + +static ssize_t usb_c_power_priority_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + unsigned int value; + int ret; + + ret = regmap_read(data->regmap, EC_ADDR_USB_C_POWER_PRIORITY, &value); + if (ret < 0) + return ret; + + value &= USB_C_POWER_PRIORITY; + + if (usb_c_power_priority_value[USB_C_POWER_PRIORITY_PERFORMANCE] == value) + return sysfs_emit(buf, "%s\n", + usb_c_power_priority_text[USB_C_POWER_PRIORITY_PERFORMANCE]); + + return sysfs_emit(buf, "%s\n", usb_c_power_priority_text[USB_C_POWER_PRIORITY_CHARGING]); +} + +static DEVICE_ATTR_RW(usb_c_power_priority); + +static int usb_c_power_priority_restore(struct uniwill_data *data) +{ + unsigned int value; + + value = usb_c_power_priority_value[data->last_usb_c_power_priority_option]; + + guard(mutex)(&data->usb_c_power_priority_lock); + + return regmap_update_bits(data->regmap, EC_ADDR_USB_C_POWER_PRIORITY, + USB_C_POWER_PRIORITY, value); +} + +static int usb_c_power_priority_init(struct uniwill_data *data) +{ + unsigned int value; + int ret; + + if (!uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) + return 0; + + ret = devm_mutex_init(data->dev, &data->usb_c_power_priority_lock); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, EC_ADDR_USB_C_POWER_PRIORITY, &value); + if (ret < 0) + return ret; + + value &= USB_C_POWER_PRIORITY; + + data->last_usb_c_power_priority_option = + usb_c_power_priority_value[USB_C_POWER_PRIORITY_PERFORMANCE] == value ? + USB_C_POWER_PRIORITY_PERFORMANCE : + USB_C_POWER_PRIORITY_CHARGING; + + return 0; +} + static struct attribute *uniwill_attrs[] = { /* Keyboard-related */ &dev_attr_fn_lock.attr, @@ -890,6 +1005,7 @@ static struct attribute *uniwill_attrs[] = { &dev_attr_breathing_in_suspend.attr, /* Power-management-related */ &dev_attr_ctgp_offset.attr, + &dev_attr_usb_c_power_priority.attr, NULL }; @@ -924,6 +1040,11 @@ static umode_t uniwill_attr_is_visible(struct kobject *kobj, struct attribute *a return attr->mode; } + if (attr == &dev_attr_usb_c_power_priority.attr) { + if (uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) + return attr->mode; + } + return 0; } @@ -937,6 +1058,48 @@ static const struct attribute_group *uniwill_groups[] = { NULL }; +static umode_t uniwill_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, + int channel) +{ + const struct uniwill_data *data = drvdata; + unsigned int feature; + + switch (type) { + case hwmon_temp: + switch (channel) { + case 0: + feature = UNIWILL_FEATURE_CPU_TEMP; + break; + case 1: + feature = UNIWILL_FEATURE_GPU_TEMP; + break; + default: + return 0; + } + break; + case hwmon_fan: + case hwmon_pwm: + switch (channel) { + case 0: + feature = UNIWILL_FEATURE_PRIMARY_FAN; + break; + case 1: + feature = UNIWILL_FEATURE_SECONDARY_FAN; + break; + default: + return 0; + } + break; + default: + return 0; + } + + if (uniwill_device_supports(data, feature)) + return 0444; + + return 0; +} + static int uniwill_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { @@ -1020,7 +1183,7 @@ static int uniwill_read_string(struct device *dev, enum hwmon_sensor_types type, } static const struct hwmon_ops uniwill_ops = { - .visible = 0444, + .is_visible = uniwill_is_visible, .read = uniwill_read, .read_string = uniwill_read_string, }; @@ -1048,7 +1211,10 @@ static int uniwill_hwmon_init(struct uniwill_data *data) { struct device *hdev; - if (!uniwill_device_supports(data, UNIWILL_FEATURE_HWMON)) + if (!uniwill_device_supports(data, UNIWILL_FEATURE_CPU_TEMP) && + !uniwill_device_supports(data, UNIWILL_FEATURE_GPU_TEMP) && + !uniwill_device_supports(data, UNIWILL_FEATURE_PRIMARY_FAN) && + !uniwill_device_supports(data, UNIWILL_FEATURE_SECONDARY_FAN)) return 0; hdev = devm_hwmon_device_register_with_info(data->dev, "uniwill", data, @@ -1369,11 +1535,10 @@ static int uniwill_notifier_call(struct notifier_block *nb, unsigned long action return NOTIFY_OK; case UNIWILL_OSD_DC_ADAPTER_CHANGED: - /* noop for the time being, will change once charging priority - * gets implemented. - */ + if (!uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) + return NOTIFY_DONE; - return NOTIFY_OK; + return notifier_from_errno(usb_c_power_priority_restore(data)); case UNIWILL_OSD_FN_LOCK: if (!uniwill_device_supports(data, UNIWILL_FEATURE_FN_LOCK)) return NOTIFY_DONE; @@ -1467,6 +1632,7 @@ static int uniwill_probe(struct platform_device *pdev) return PTR_ERR(regmap); data->regmap = regmap; + ret = devm_mutex_init(&pdev->dev, &data->super_key_lock); if (ret < 0) return ret; @@ -1504,6 +1670,10 @@ static int uniwill_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = usb_c_power_priority_init(data); + if (ret < 0) + return ret; + return uniwill_input_init(data); } @@ -1633,6 +1803,14 @@ static int uniwill_resume_nvidia_ctgp(struct uniwill_data *data) CTGP_DB_DB_ENABLE | CTGP_DB_CTGP_ENABLE); } +static int uniwill_resume_usb_c_power_priority(struct uniwill_data *data) +{ + if (!uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) + return 0; + + return usb_c_power_priority_restore(data); +} + static int uniwill_resume(struct device *dev) { struct uniwill_data *data = dev_get_drvdata(dev); @@ -1656,7 +1834,11 @@ static int uniwill_resume(struct device *dev) if (ret < 0) return ret; - return uniwill_resume_nvidia_ctgp(data); + ret = uniwill_resume_nvidia_ctgp(data); + if (ret < 0) + return ret; + + return uniwill_resume_usb_c_power_priority(data); } static DEFINE_SIMPLE_DEV_PM_OPS(uniwill_pm_ops, uniwill_suspend, uniwill_resume); @@ -1682,12 +1864,24 @@ static struct platform_driver uniwill_driver = { .shutdown = uniwill_shutdown, }; +static struct uniwill_device_descriptor lapqc71a_lapqc71b_descriptor __initdata = { + .features = UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN, +}; + static struct uniwill_device_descriptor lapac71h_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | UNIWILL_FEATURE_TOUCHPAD_TOGGLE | UNIWILL_FEATURE_BATTERY | - UNIWILL_FEATURE_HWMON, + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN, }; static struct uniwill_device_descriptor lapkc71f_descriptor __initdata = { @@ -1696,7 +1890,89 @@ static struct uniwill_device_descriptor lapkc71f_descriptor __initdata = { UNIWILL_FEATURE_TOUCHPAD_TOGGLE | UNIWILL_FEATURE_LIGHTBAR | UNIWILL_FEATURE_BATTERY | - UNIWILL_FEATURE_HWMON, + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN, +}; + +/* + * The featuresets below reflect somewhat chronological changes: + * 1 -> 2: UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL is added to the EC firmware. + * 2 -> 3: UNIWILL_FEATURE_USB_C_POWER_PRIORITY is removed from the EC firmware. + * Some devices might divert from this timeline. + */ + +static struct uniwill_device_descriptor tux_featureset_1_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN | + UNIWILL_FEATURE_USB_C_POWER_PRIORITY, +}; + +static struct uniwill_device_descriptor tux_featureset_1_nvidia_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN | + UNIWILL_FEATURE_USB_C_POWER_PRIORITY, +}; + +static struct uniwill_device_descriptor tux_featureset_2_nvidia_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN | + UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL | + UNIWILL_FEATURE_USB_C_POWER_PRIORITY, +}; + +static struct uniwill_device_descriptor tux_featureset_3_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN, +}; + +static struct uniwill_device_descriptor tux_featureset_3_nvidia_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN | + UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL, +}; + +static int phxtxx1_probe(struct uniwill_data *data) +{ + unsigned int value; + int ret; + + ret = regmap_read(data->regmap, EC_ADDR_PROJECT_ID, &value); + if (ret < 0) + return ret; + + if (value == PROJECT_ID_PH4TRX1 || value == PROJECT_ID_PH6TRX1) + data->features |= UNIWILL_FEATURE_SECONDARY_FAN; + + return 0; +}; + +static struct uniwill_device_descriptor phxtxx1_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_USB_C_POWER_PRIORITY, + .probe = phxtxx1_probe, }; static int phxarx1_phxaqf1_probe(struct uniwill_data *data) @@ -1709,37 +1985,61 @@ static int phxarx1_phxaqf1_probe(struct uniwill_data *data) return ret; if (value & HAS_GPU) - data->features |= UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL; + data->features |= UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL; return 0; }; static struct uniwill_device_descriptor phxarx1_phxaqf1_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN | + UNIWILL_FEATURE_USB_C_POWER_PRIORITY, .probe = phxarx1_phxaqf1_probe, }; -static struct uniwill_device_descriptor tux_featureset_1_descriptor __initdata = { - .features = UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL, +static struct uniwill_device_descriptor pf5pu1g_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN, }; -static struct uniwill_device_descriptor empty_descriptor __initdata = {}; - static const struct dmi_system_id uniwill_dmi_table[] __initconst = { { - .ident = "XMG FUSION 15", + .ident = "XMG FUSION 15 (L19)", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71A"), }, - .driver_data = &empty_descriptor, + .driver_data = &lapqc71a_lapqc71b_descriptor, }, { - .ident = "XMG FUSION 15", + .ident = "XMG FUSION 15 (L19)", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71B"), }, - .driver_data = &empty_descriptor, + .driver_data = &lapqc71a_lapqc71b_descriptor, + }, + { + .ident = "XMG FUSION 15 (L19)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71A"), + }, + .driver_data = &lapqc71a_lapqc71b_descriptor, + }, + { + .ident = "XMG FUSION 15 (L19)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71B"), + }, + .driver_data = &lapqc71a_lapqc71b_descriptor, }, { .ident = "Intel NUC x15", @@ -1763,7 +2063,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxTxX1"), }, - .driver_data = &empty_descriptor, + .driver_data = &phxtxx1_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14 Gen6 Intel", @@ -1771,7 +2071,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxTQx1"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14/16 Gen7 Intel", @@ -1787,7 +2087,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH6AG01_PH6AQ71_PH6AQI1"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14/16 Gen8 Intel/Commodore Omnia-Book Pro Gen 8", @@ -1795,7 +2095,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH4PRX1_PH6PRX1"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14 Gen8 Intel/Commodore Omnia-Book Pro Gen 8", @@ -1803,7 +2103,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH4PG31"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 16 Gen8 Intel", @@ -1811,7 +2111,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH6PG01_PH6PG71"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14/15 Gen9 AMD", @@ -1819,7 +2119,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GXxHRXx"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14/15 Gen9 Intel/Commodore Omnia-Book 15 Gen9", @@ -1827,7 +2127,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GXxMRXx"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14/15 Gen10 AMD", @@ -1835,7 +2135,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxHP4NAx"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14/15 Gen10 AMD", @@ -1843,7 +2143,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxKK4NAx_XxSP4NAx"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 15 Gen10 Intel", @@ -1851,7 +2151,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxAR4NAx"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_descriptor, }, { .ident = "TUXEDO InfinityBook Max 15 Gen10 AMD", @@ -1859,7 +2159,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X5KK45xS_X5SP45xS"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Max 16 Gen10 AMD", @@ -1867,7 +2167,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6HP45xU"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Max 16 Gen10 AMD", @@ -1875,7 +2175,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6KK45xU_X6SP45xU"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Max 15 Gen10 Intel", @@ -1883,7 +2183,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X5AR45xS"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Max 16 Gen10 Intel", @@ -1891,7 +2191,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR55xU"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15 Gen1 AMD", @@ -1899,7 +2199,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501A1650TI"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15 Gen1 AMD", @@ -1907,7 +2207,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501A2060"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 17 Gen1 AMD", @@ -1915,7 +2215,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701A1650TI"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 17 Gen1 AMD", @@ -1923,7 +2223,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701A2060"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15 Gen1 Intel", @@ -1931,7 +2231,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501I1650TI"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15 Gen1 Intel", @@ -1939,7 +2239,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501I2060"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 17 Gen1 Intel", @@ -1947,7 +2247,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701I1650TI"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 17 Gen1 Intel", @@ -1955,7 +2255,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701I2060"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Trinity 15 Intel Gen1", @@ -1963,7 +2263,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "TRINITY1501I"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Trinity 17 Intel Gen1", @@ -1971,7 +2271,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "TRINITY1701I"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15/17 Gen2 AMD", @@ -1979,7 +2279,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxMGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15/17 Gen2 Intel", @@ -1987,7 +2287,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxNGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris/Polaris 15/17 Gen3 AMD", @@ -1995,7 +2295,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxZGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris/Polaris 15/17 Gen3 Intel", @@ -2003,7 +2303,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxTGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris/Polaris 15/17 Gen4 AMD", @@ -2011,7 +2311,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxRGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 15 Gen4 Intel", @@ -2019,7 +2319,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxAGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15/17 Gen5 AMD", @@ -2027,7 +2327,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxXGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16 Gen5 AMD", @@ -2035,7 +2335,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6XGxX"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16/17 Gen5 Intel/Commodore ORION Gen 5", @@ -2043,7 +2343,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxPXxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris Slim 15 Gen6 AMD", @@ -2051,7 +2351,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxHGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris Slim 15 Gen6 Intel/Commodore ORION Slim 15 Gen6", @@ -2059,7 +2359,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM5IXxA"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16 Gen6 Intel/Commodore ORION 16 Gen6", @@ -2067,7 +2367,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6IXxB_MB1"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16 Gen6 Intel/Commodore ORION 16 Gen6", @@ -2075,7 +2375,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6IXxB_MB2"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 17 Gen6 Intel/Commodore ORION 17 Gen6", @@ -2083,7 +2383,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM7IXxN"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16 Gen7 AMD", @@ -2091,7 +2391,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6FR5xxY"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16 Gen7 Intel", @@ -2099,7 +2399,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR5xxY"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16 Gen7 Intel", @@ -2107,7 +2407,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR5xxY_mLED"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Book BA15 Gen10 AMD", @@ -2115,7 +2415,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PF5PU1G"), }, - .driver_data = &empty_descriptor, + .driver_data = &pf5pu1g_descriptor, }, { .ident = "TUXEDO Pulse 14 Gen1 AMD", @@ -2123,7 +2423,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PULSE1401"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_descriptor, }, { .ident = "TUXEDO Pulse 15 Gen1 AMD", @@ -2131,7 +2431,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PULSE1501"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_descriptor, }, { .ident = "TUXEDO Pulse 15 Gen2 AMD", @@ -2139,7 +2439,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PF5LUXG"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_descriptor, }, { } }; diff --git a/drivers/platform/x86/uniwill/uniwill-wmi.c b/drivers/platform/x86/uniwill/uniwill-wmi.c index 31d9c39f14ab..097882f10b1e 100644 --- a/drivers/platform/x86/uniwill/uniwill-wmi.c +++ b/drivers/platform/x86/uniwill/uniwill-wmi.c @@ -77,6 +77,7 @@ static struct wmi_driver uniwill_wmi_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .id_table = uniwill_wmi_id_table, + .min_event_size = sizeof(u32), .notify = uniwill_wmi_notify, .no_singleton = true, }; diff --git a/drivers/platform/x86/wireless-hotkey.c b/drivers/platform/x86/wireless-hotkey.c index e5083c0e1515..f680d8ff8e87 100644 --- a/drivers/platform/x86/wireless-hotkey.c +++ b/drivers/platform/x86/wireless-hotkey.c @@ -35,16 +35,17 @@ static const struct acpi_device_id wl_ids[] = { {"", 0}, }; -static int wireless_input_setup(struct acpi_device *device) +static int wireless_input_setup(struct device *dev) { - struct wl_button *button = acpi_driver_data(device); + struct wl_button *button = dev_get_drvdata(dev); int err; button->input_dev = input_allocate_device(); if (!button->input_dev) return -ENOMEM; - snprintf(button->phys, sizeof(button->phys), "%s/input0", acpi_device_hid(device)); + snprintf(button->phys, sizeof(button->phys), "%s/input0", + acpi_device_hid(ACPI_COMPANION(dev))); button->input_dev->name = "Wireless hotkeys"; button->input_dev->phys = button->phys; @@ -63,17 +64,17 @@ err_free_dev: return err; } -static void wireless_input_destroy(struct acpi_device *device) +static void wireless_input_destroy(struct device *dev) { - struct wl_button *button = acpi_driver_data(device); + struct wl_button *button = dev_get_drvdata(dev); input_unregister_device(button->input_dev); kfree(button); } -static void wl_notify(struct acpi_device *acpi_dev, u32 event) +static void wl_notify(acpi_handle handle, u32 event, void *data) { - struct wl_button *button = acpi_driver_data(acpi_dev); + struct wl_button *button = data; if (event != 0x80) { pr_info("Received unknown event (0x%x)\n", event); @@ -86,7 +87,7 @@ static void wl_notify(struct acpi_device *acpi_dev, u32 event) input_sync(button->input_dev); } -static int wl_add(struct acpi_device *device) +static int wl_probe(struct platform_device *pdev) { struct wl_button *button; int err; @@ -95,30 +96,38 @@ static int wl_add(struct acpi_device *device) if (!button) return -ENOMEM; - device->driver_data = button; + platform_set_drvdata(pdev, button); - err = wireless_input_setup(device); + err = wireless_input_setup(&pdev->dev); if (err) { pr_err("Failed to setup wireless hotkeys\n"); kfree(button); + return err; + } + err = acpi_dev_install_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, wl_notify, button); + if (err) { + pr_err("Failed to install ACPI notify handler\n"); + wireless_input_destroy(&pdev->dev); } return err; } -static void wl_remove(struct acpi_device *device) +static void wl_remove(struct platform_device *pdev) { - wireless_input_destroy(device); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, wl_notify); + wireless_input_destroy(&pdev->dev); } -static struct acpi_driver wl_driver = { - .name = "wireless-hotkey", - .ids = wl_ids, - .ops = { - .add = wl_add, - .remove = wl_remove, - .notify = wl_notify, +static struct platform_driver wl_driver = { + .probe = wl_probe, + .remove = wl_remove, + .driver = { + .name = "wireless-hotkey", + .acpi_match_table = wl_ids, }, }; -module_acpi_driver(wl_driver); +module_platform_driver(wl_driver); diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c index e3a126de421b..6623cf60e4b4 100644 --- a/drivers/platform/x86/wmi-bmof.c +++ b/drivers/platform/x86/wmi-bmof.c @@ -62,7 +62,7 @@ static int wmi_bmof_probe(struct wmi_device *wdev, const void *context) if (!buffer) return -ENOMEM; - ret = wmidev_query_block(wdev, 0, buffer); + ret = wmidev_query_block(wdev, 0, buffer, 0); if (ret < 0) return ret; diff --git a/drivers/platform/x86/xiaomi-wmi.c b/drivers/platform/x86/xiaomi-wmi.c index badf9e42e015..3874f3336a0d 100644 --- a/drivers/platform/x86/xiaomi-wmi.c +++ b/drivers/platform/x86/xiaomi-wmi.c @@ -83,6 +83,7 @@ static struct wmi_driver xiaomi_wmi_driver = { .name = "xiaomi-wmi", }, .id_table = xiaomi_wmi_id_table, + .min_event_size = 0, .probe = xiaomi_wmi_probe, .notify_new = xiaomi_wmi_notify, .no_singleton = true, diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 4d32fc676aaf..71e930e80178 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -3089,6 +3089,7 @@ static const struct bus_type genpd_bus_type = { static void genpd_dev_pm_detach(struct device *dev, bool power_off) { struct generic_pm_domain *pd; + bool is_virt_dev; unsigned int i; int ret = 0; @@ -3098,6 +3099,13 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off) dev_dbg(dev, "removing from PM domain %s\n", pd->name); + /* Check if the device was created by genpd at attach. */ + is_virt_dev = dev->bus == &genpd_bus_type; + + /* Disable runtime PM if we enabled it at attach. */ + if (is_virt_dev) + pm_runtime_disable(dev); + /* Drop the default performance state */ if (dev_gpd_data(dev)->default_pstate) { dev_pm_genpd_set_performance_state(dev, 0); @@ -3123,7 +3131,7 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off) genpd_queue_power_off_work(pd); /* Unregister the device if it was created by genpd. */ - if (dev->bus == &genpd_bus_type) + if (is_virt_dev) device_unregister(dev); } diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index d2b8d0332951..e1cfd4223473 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -1015,6 +1015,7 @@ static int scpsys_get_bus_protection_legacy(struct device *dev, struct scpsys *s struct device_node *node, *smi_np; int num_regmaps = 0, i, j; struct regmap *regmap[3]; + int ret = 0; /* * Legacy code retrieves a maximum of three bus protection handles: @@ -1065,11 +1066,14 @@ static int scpsys_get_bus_protection_legacy(struct device *dev, struct scpsys *s if (node) { regmap[2] = syscon_regmap_lookup_by_phandle(node, "mediatek,infracfg-nao"); num_regmaps++; - of_node_put(node); - if (IS_ERR(regmap[2])) - return dev_err_probe(dev, PTR_ERR(regmap[2]), + if (IS_ERR(regmap[2])) { + ret = dev_err_probe(dev, PTR_ERR(regmap[2]), "%pOF: failed to get infracfg regmap\n", node); + of_node_put(node); + return ret; + } + of_node_put(node); } else { regmap[2] = NULL; } diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 99e3334726a5..124afb99febe 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -97,7 +97,7 @@ config POWER_RESET_GEMINI_POWEROFF config POWER_RESET_GPIO bool "GPIO power-off driver" - depends on OF_GPIO + depends on OF help This driver supports turning off your board via a GPIO line. If your board needs a GPIO high/low to power down, say Y and @@ -105,7 +105,7 @@ config POWER_RESET_GPIO config POWER_RESET_GPIO_RESTART bool "GPIO restart driver" - depends on OF_GPIO + depends on OF help This driver supports restarting your board via a GPIO line. If your board needs a GPIO high/low to restart, say Y and @@ -181,7 +181,7 @@ config POWER_RESET_PIIX4_POWEROFF config POWER_RESET_LTC2952 bool "LTC2952 PowerPath power-off driver" - depends on OF_GPIO + depends on OF help This driver supports an external powerdown trigger and board power down via the LTC2952. Bindings are made in the device tree. @@ -198,7 +198,7 @@ config POWER_RESET_MT6323 config POWER_RESET_QNAP bool "QNAP power-off driver" - depends on OF_GPIO && PLAT_ORION + depends on PLAT_ORION help This driver supports turning off QNAP NAS devices by sending commands to the microcontroller which controls the main power. diff --git a/drivers/power/reset/keystone-reset.c b/drivers/power/reset/keystone-reset.c index d9268d150e1f..3c44cd6cee0a 100644 --- a/drivers/power/reset/keystone-reset.c +++ b/drivers/power/reset/keystone-reset.c @@ -48,8 +48,7 @@ static inline int rsctrl_enable_rspll_write(void) RSCTRL_KEY_MASK, RSCTRL_KEY); } -static int rsctrl_restart_handler(struct notifier_block *this, - unsigned long mode, void *cmd) +static int rsctrl_restart_handler(struct sys_off_data *data) { /* enable write access to RSTCTRL */ rsctrl_enable_rspll_write(); @@ -61,11 +60,6 @@ static int rsctrl_restart_handler(struct notifier_block *this, return NOTIFY_DONE; } -static struct notifier_block rsctrl_restart_nb = { - .notifier_call = rsctrl_restart_handler, - .priority = 128, -}; - static const struct of_device_id rsctrl_of_match[] = { {.compatible = "ti,keystone-reset", }, {}, @@ -140,7 +134,8 @@ static int rsctrl_probe(struct platform_device *pdev) return ret; } - ret = register_restart_handler(&rsctrl_restart_nb); + ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART, 128, + rsctrl_restart_handler, NULL); if (ret) dev_err(dev, "cannot register restart handler (err=%d)\n", ret); diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c index fba53f638da0..d20e44db0532 100644 --- a/drivers/power/reset/reboot-mode.c +++ b/drivers/power/reset/reboot-mode.c @@ -4,12 +4,16 @@ */ #include +#include #include #include +#include #include #include #include #include +#include +#include #define PREFIX "mode-" @@ -19,6 +23,54 @@ struct mode_info { struct list_head list; }; +struct reboot_mode_sysfs_data { + struct device *reboot_mode_device; + struct list_head head; +}; + +static inline void reboot_mode_release_list(struct reboot_mode_sysfs_data *priv) +{ + struct mode_info *info; + struct mode_info *next; + + list_for_each_entry_safe(info, next, &priv->head, list) { + list_del(&info->list); + kfree_const(info->mode); + kfree(info); + } +} + +static ssize_t reboot_modes_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct reboot_mode_sysfs_data *priv; + struct mode_info *sysfs_info; + ssize_t size = 0; + + priv = dev_get_drvdata(dev); + if (!priv) + return -ENODATA; + + list_for_each_entry(sysfs_info, &priv->head, list) + size += sysfs_emit_at(buf, size, "%s ", sysfs_info->mode); + + if (!size) + return -ENODATA; + + return size + sysfs_emit_at(buf, size - 1, "\n"); +} +static DEVICE_ATTR_RO(reboot_modes); + +static struct attribute *reboot_mode_attrs[] = { + &dev_attr_reboot_modes.attr, + NULL, +}; +ATTRIBUTE_GROUPS(reboot_mode); + +static const struct class reboot_mode_class = { + .name = "reboot-mode", + .dev_groups = reboot_mode_groups, +}; + static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot, const char *cmd) { @@ -62,6 +114,52 @@ static int reboot_mode_notify(struct notifier_block *this, return NOTIFY_DONE; } +static int reboot_mode_create_device(struct reboot_mode_driver *reboot) +{ + struct reboot_mode_sysfs_data *priv; + struct mode_info *sysfs_info; + struct mode_info *info; + int ret; + + priv = kzalloc_obj(*priv, GFP_KERNEL); + if (!priv) + return -ENOMEM; + + INIT_LIST_HEAD(&priv->head); + + list_for_each_entry(info, &reboot->head, list) { + sysfs_info = kzalloc_obj(*sysfs_info, GFP_KERNEL); + if (!sysfs_info) { + ret = -ENOMEM; + goto error; + } + + sysfs_info->mode = kstrdup_const(info->mode, GFP_KERNEL); + if (!sysfs_info->mode) { + kfree(sysfs_info); + ret = -ENOMEM; + goto error; + } + + list_add_tail(&sysfs_info->list, &priv->head); + } + + priv->reboot_mode_device = device_create(&reboot_mode_class, NULL, 0, + (void *)priv, "%s", + reboot->dev->driver->name); + if (IS_ERR(priv->reboot_mode_device)) { + ret = PTR_ERR(priv->reboot_mode_device); + goto error; + } + + return 0; + +error: + reboot_mode_release_list(priv); + kfree(priv); + return ret; +} + /** * reboot_mode_register - register a reboot mode driver * @reboot: reboot mode driver @@ -113,16 +211,49 @@ int reboot_mode_register(struct reboot_mode_driver *reboot) reboot->reboot_notifier.notifier_call = reboot_mode_notify; register_reboot_notifier(&reboot->reboot_notifier); + ret = reboot_mode_create_device(reboot); + if (ret) + goto error; + return 0; error: - list_for_each_entry(info, &reboot->head, list) - kfree_const(info->mode); - + reboot_mode_unregister(reboot); return ret; } EXPORT_SYMBOL_GPL(reboot_mode_register); +static int reboot_mode_match_by_name(struct device *dev, const void *data) +{ + const char *name = data; + + if (!dev || !data) + return 0; + + return dev_name(dev) && strcmp(dev_name(dev), name) == 0; +} + +static inline void reboot_mode_unregister_device(struct reboot_mode_driver *reboot) +{ + struct reboot_mode_sysfs_data *priv; + struct device *reboot_mode_device; + + reboot_mode_device = class_find_device(&reboot_mode_class, NULL, reboot->dev->driver->name, + reboot_mode_match_by_name); + + if (!reboot_mode_device) + return; + + priv = dev_get_drvdata(reboot_mode_device); + device_unregister(reboot_mode_device); + + if (!priv) + return; + + reboot_mode_release_list(priv); + kfree(priv); +} + /** * reboot_mode_unregister - unregister a reboot mode driver * @reboot: reboot mode driver @@ -132,6 +263,7 @@ int reboot_mode_unregister(struct reboot_mode_driver *reboot) struct mode_info *info; unregister_reboot_notifier(&reboot->reboot_notifier); + reboot_mode_unregister_device(reboot); list_for_each_entry(info, &reboot->head, list) kfree_const(info->mode); @@ -199,6 +331,19 @@ void devm_reboot_mode_unregister(struct device *dev, } EXPORT_SYMBOL_GPL(devm_reboot_mode_unregister); +static int __init reboot_mode_init(void) +{ + return class_register(&reboot_mode_class); +} + +static void __exit reboot_mode_exit(void) +{ + class_unregister(&reboot_mode_class); +} + +subsys_initcall(reboot_mode_init); +module_exit(reboot_mode_exit); + MODULE_AUTHOR("Andy Yan "); MODULE_DESCRIPTION("System reboot mode core library"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 92f9f7aae92f..83392ed6a8da 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -229,6 +229,17 @@ config BATTERY_SAMSUNG_SDI Say Y to enable support for Samsung SDI battery data. These batteries are used in Samsung mobile phones. +config BATTERY_S2MU005 + tristate "Samsung S2MU005 PMIC fuel gauge driver" + depends on I2C + select REGMAP_I2C + help + Say Y to enable support for the Samsung S2MU005 PMIC integrated + fuel gauge, which works indepenently of the PMIC battery charger + counterpart, and reports battery metrics. + + This driver, if built as a module, will be called s2mu005-fuel-gauge. + config BATTERY_COLLIE tristate "Sharp SL-5500 (collie) battery" depends on SA1100_COLLIE && MCP_UCB1200 @@ -631,6 +642,17 @@ config CHARGER_MAX77705 help Say Y to enable support for the Maxim MAX77705 battery charger. +config CHARGER_MAX77759 + tristate "Maxim MAX77759 battery charger driver" + depends on MFD_MAX77759 && REGULATOR + default MFD_MAX77759 + help + Say M or Y here to enable the MAX77759 battery charger. MAX77759 + charger is a function of the MAX77759 PMIC. This is a dual input + switch-mode charger. This driver supports buck and OTG boost modes. + + If built as a module, it will be called max77759_charger. + config CHARGER_MAX77976 tristate "Maxim MAX77976 battery charger driver" depends on I2C @@ -1132,4 +1154,15 @@ config FUEL_GAUGE_MM8013 the state of charge, temperature, cycle count, actual and design capacity, etc. +config MACSMC_POWER + tristate "Apple SMC Battery and Power Driver" + depends on MFD_MACSMC + help + This driver provides support for the battery and AC adapter on + Apple Silicon machines. It exposes battery telemetry (voltage, + current, health) and AC adapter status through the standard Linux + power supply framework. + + Say Y or M here if you have an Apple Silicon based Mac. + endif # POWER_SUPPLY diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 4b79d5abc49a..7ee839dca7f3 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_QCOM_BATTMGR) += qcom_battmgr.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_SAMSUNG_SDI) += samsung-sdi-battery.o +obj-$(CONFIG_BATTERY_S2MU005) += s2mu005-battery.o obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o obj-$(CONFIG_BATTERY_INGENIC) += ingenic-battery.o obj-$(CONFIG_BATTERY_INTEL_DC_TI) += intel_dc_ti_battery.o @@ -128,3 +129,5 @@ obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_smbx.o obj-$(CONFIG_FUEL_GAUGE_MM8013) += mm8013.o +obj-$(CONFIG_MACSMC_POWER) += macsmc-power.o +obj-$(CONFIG_CHARGER_MAX77759) += max77759_charger.o diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index ac05942e4e6a..ea0f5caee8f0 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -821,14 +822,6 @@ static int charger_init_hw_regs(struct axp288_chrg_info *info) return 0; } -static void axp288_charger_cancel_work(void *data) -{ - struct axp288_chrg_info *info = data; - - cancel_work_sync(&info->otg.work); - cancel_work_sync(&info->cable.work); -} - static int axp288_charger_probe(struct platform_device *pdev) { int ret, i, pirq; @@ -866,12 +859,10 @@ static int axp288_charger_probe(struct platform_device *pdev) info->regmap_irqc = axp20x->regmap_irqc; info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME); - if (IS_ERR(info->cable.edev)) { - dev_err_probe(dev, PTR_ERR(info->cable.edev), - "extcon_get_extcon_dev(%s) failed\n", - AXP288_EXTCON_DEV_NAME); - return PTR_ERR(info->cable.edev); - } + if (IS_ERR(info->cable.edev)) + return dev_err_probe(dev, PTR_ERR(info->cable.edev), + "extcon_get_extcon_dev(%s) failed\n", + AXP288_EXTCON_DEV_NAME); /* * On devices with broken ACPI GPIO event handlers there also is no ACPI @@ -885,12 +876,11 @@ static int axp288_charger_probe(struct platform_device *pdev) if (extcon_name) { info->otg.cable = extcon_get_extcon_dev(extcon_name); - if (IS_ERR(info->otg.cable)) { - dev_err_probe(dev, PTR_ERR(info->otg.cable), - "extcon_get_extcon_dev(%s) failed\n", - USB_HOST_EXTCON_NAME); - return PTR_ERR(info->otg.cable); - } + if (IS_ERR(info->otg.cable)) + return dev_err_probe(dev, PTR_ERR(info->otg.cable), + "extcon_get_extcon_dev(%s) failed\n", + USB_HOST_EXTCON_NAME); + dev_info(dev, "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n"); } @@ -904,38 +894,39 @@ static int axp288_charger_probe(struct platform_device *pdev) charger_cfg.drv_data = info; info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc, &charger_cfg); - if (IS_ERR(info->psy_usb)) { - ret = PTR_ERR(info->psy_usb); - dev_err(dev, "failed to register power supply: %d\n", ret); - return ret; - } + if (IS_ERR(info->psy_usb)) + return dev_err_probe(dev, PTR_ERR(info->psy_usb), + "failed to register power supply: %d\n", ret); /* Cancel our work on cleanup, register this before the notifiers */ - ret = devm_add_action(dev, axp288_charger_cancel_work, info); + ret = devm_work_autocancel(dev, &info->cable.work, + axp288_charger_extcon_evt_worker); if (ret) return ret; /* Register for extcon notification */ - INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); info->cable.nb.notifier_call = axp288_charger_handle_cable_evt; ret = devm_extcon_register_notifier_all(dev, info->cable.edev, &info->cable.nb); - if (ret) { - dev_err(dev, "failed to register cable extcon notifier\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to register cable extcon notifier\n"); + schedule_work(&info->cable.work); + ret = devm_work_autocancel(dev, &info->otg.work, + axp288_charger_otg_evt_worker); + if (ret) + return ret; + /* Register for OTG notification */ - INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker); info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt; if (info->otg.cable) { ret = devm_extcon_register_notifier(dev, info->otg.cable, EXTCON_USB_HOST, &info->otg.id_nb); - if (ret) { - dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, + "failed to register EXTCON_USB_HOST notifier\n"); + schedule_work(&info->otg.work); } @@ -954,11 +945,9 @@ static int axp288_charger_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i], NULL, axp288_charger_irq_thread_handler, IRQF_ONESHOT, info->pdev->name, info); - if (ret) { - dev_err(dev, "failed to request interrupt=%d\n", - info->irq[i]); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to request interrupt=%d\n", + info->irq[i]); } return 0; diff --git a/drivers/power/supply/bd71828-power.c b/drivers/power/supply/bd71828-power.c index 0e00acb58993..5e78faa0a4aa 100644 --- a/drivers/power/supply/bd71828-power.c +++ b/drivers/power/supply/bd71828-power.c @@ -24,6 +24,7 @@ #define BD7182x_MASK_CONF_PON BIT(0) #define BD71815_MASK_CONF_XSTB BIT(1) #define BD7182x_MASK_BAT_STAT 0x3f +#define BD7182x_MASK_ILIM 0x3f #define BD7182x_MASK_DCIN_STAT 0x07 #define BD7182x_MASK_WDT_AUTO 0x40 @@ -48,9 +49,11 @@ struct pwr_regs { unsigned int vbat_avg; unsigned int ibat; unsigned int ibat_avg; + unsigned int ilim_stat; unsigned int btemp_vth; unsigned int chg_state; unsigned int bat_temp; + unsigned int dcin_set; unsigned int dcin_stat; unsigned int dcin_online_mask; unsigned int dcin_collapse_limit; @@ -66,9 +69,11 @@ static const struct pwr_regs pwr_regs_bd71828 = { .vbat_avg = BD71828_REG_VBAT_U, .ibat = BD71828_REG_IBAT_U, .ibat_avg = BD71828_REG_IBAT_AVG_U, + .ilim_stat = BD71828_REG_ILIM_STAT, .btemp_vth = BD71828_REG_VM_BTMP_U, .chg_state = BD71828_REG_CHG_STATE, .bat_temp = BD71828_REG_BAT_TEMP, + .dcin_set = BD71828_REG_DCIN_SET, .dcin_stat = BD71828_REG_DCIN_STAT, .dcin_online_mask = BD7182x_MASK_DCIN_DET, .dcin_collapse_limit = BD71828_REG_DCIN_CLPS, @@ -441,6 +446,7 @@ static int bd71828_charger_get_property(struct power_supply *psy, struct bd71828_power *pwr = dev_get_drvdata(psy->dev.parent); u32 vot; u16 tmp; + int t; int online; int ret; @@ -459,6 +465,20 @@ static int bd71828_charger_get_property(struct power_supply *psy, vot = tmp; /* 5 milli volt steps */ val->intval = 5000 * vot; + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + if (!pwr->regs->ilim_stat) + return -ENODATA; + + ret = regmap_read(pwr->regmap, pwr->regs->ilim_stat, &t); + if (ret) + return ret; + + t++; + val->intval = (t & BD7182x_MASK_ILIM) * 50000; + if (val->intval > 2000000) + val->intval = 2000000; + break; default: return -EINVAL; @@ -467,6 +487,45 @@ static int bd71828_charger_get_property(struct power_supply *psy, return 0; } +static int bd71828_charger_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct bd71828_power *pwr = dev_get_drvdata(psy->dev.parent); + + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + if (val->intval > 2000000) + return -EINVAL; + + if (val->intval < 50000) + return -EINVAL; + + if (!pwr->regs->dcin_set) + return -EINVAL; + + return regmap_update_bits(pwr->regmap, pwr->regs->dcin_set, + BD7182x_MASK_ILIM, + val->intval / 50000 - 1); + break; + default: + return -EINVAL; + } +} + +static int bd71828_charger_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + struct bd71828_power *pwr = dev_get_drvdata(psy->dev.parent); + + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return !!(pwr->regs->dcin_set); + default: + return false; + } +} + static int bd71828_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -571,6 +630,7 @@ static int bd71828_battery_property_is_writeable(struct power_supply *psy, /** @brief ac properties */ static const enum power_supply_property bd71828_charger_props[] = { + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_VOLTAGE_NOW, }; @@ -600,6 +660,8 @@ static const struct power_supply_desc bd71828_ac_desc = { .properties = bd71828_charger_props, .num_properties = ARRAY_SIZE(bd71828_charger_props), .get_property = bd71828_charger_get_property, + .set_property = bd71828_charger_set_property, + .property_is_writeable = bd71828_charger_property_is_writeable, }; static const struct power_supply_desc bd71828_bat_desc = { diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index ed0ceae8d90b..55da91bacc3e 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -2087,8 +2088,11 @@ static int bq24190_probe(struct i2c_client *client) bdi->charge_type = POWER_SUPPLY_CHARGE_TYPE_FAST; bdi->f_reg = 0; bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */ - INIT_DELAYED_WORK(&bdi->input_current_limit_work, - bq24190_input_current_limit_work); + + ret = devm_delayed_work_autocancel(dev, &bdi->input_current_limit_work, + bq24190_input_current_limit_work); + if (ret) + return ret; i2c_set_clientdata(client, bdi); @@ -2198,7 +2202,6 @@ static void bq24190_remove(struct i2c_client *client) struct bq24190_dev_info *bdi = i2c_get_clientdata(client); int error; - cancel_delayed_work_sync(&bdi->input_current_limit_work); error = pm_runtime_resume_and_get(bdi->dev); if (error < 0) dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error); diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 507fdc1c866d..7b7bdce3162f 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -387,7 +387,7 @@ static const struct cpcap_battery_config cpcap_battery_bw8x_data = { * Safe values for any lipo battery likely to fit into a mapphone * battery bay. */ -static const struct cpcap_battery_config cpcap_battery_unkown_data = { +static const struct cpcap_battery_config cpcap_battery_unknown_data = { .cd_factor = 0x3cc, .info.technology = POWER_SUPPLY_TECHNOLOGY_LION, .info.voltage_max_design = 4200000, @@ -404,6 +404,30 @@ static int cpcap_battery_match_nvmem(struct device *dev, const void *data) return 0; } +static void cpcap_battery_update_battery_data(struct cpcap_battery_ddata *ddata) +{ + struct power_supply_battery_info *info; + + if (power_supply_get_battery_info(ddata->psy, &info) < 0) + return; + + if (info->technology > 0) + ddata->config.info.technology = info->technology; + + if (info->voltage_max_design_uv > 0) + ddata->config.info.voltage_max_design = info->voltage_max_design_uv; + + if (info->voltage_min_design_uv > 0) + ddata->config.info.voltage_min_design = info->voltage_min_design_uv; + + if (info->charge_full_design_uah > 0) + ddata->config.info.charge_full_design = info->charge_full_design_uah; + + if (info->constant_charge_voltage_max_uv > 0) + ddata->config.bat.constant_charge_voltage_max_uv = + info->constant_charge_voltage_max_uv; +} + static void cpcap_battery_detect_battery_type(struct cpcap_battery_ddata *ddata) { struct nvmem_device *nvmem; @@ -429,8 +453,11 @@ static void cpcap_battery_detect_battery_type(struct cpcap_battery_ddata *ddata) ddata->config = cpcap_battery_bw8x_data; break; default: - ddata->config = cpcap_battery_unkown_data; + ddata->config = cpcap_battery_unknown_data; } + + if (ddata->psy) + cpcap_battery_update_battery_data(ddata); } /** diff --git a/drivers/power/supply/cw2015_battery.c b/drivers/power/supply/cw2015_battery.c index a05dcc4a48f2..286524d2318c 100644 --- a/drivers/power/supply/cw2015_battery.c +++ b/drivers/power/supply/cw2015_battery.c @@ -694,7 +694,8 @@ static int cw_bat_probe(struct i2c_client *client) "No monitored battery, some properties will be missing\n"); } - cw_bat->battery_workqueue = create_singlethread_workqueue("rk_battery"); + cw_bat->battery_workqueue = devm_alloc_ordered_workqueue(&client->dev, + "rk_battery", 0); if (!cw_bat->battery_workqueue) return -ENOMEM; diff --git a/drivers/power/supply/ipaq_micro_battery.c b/drivers/power/supply/ipaq_micro_battery.c index ff8573a5ca6d..5e3fe3852d0e 100644 --- a/drivers/power/supply/ipaq_micro_battery.c +++ b/drivers/power/supply/ipaq_micro_battery.c @@ -7,6 +7,7 @@ * Author : Linus Walleij */ +#include #include #include #include @@ -232,49 +233,31 @@ static int micro_batt_probe(struct platform_device *pdev) return -ENOMEM; mb->micro = dev_get_drvdata(pdev->dev.parent); - mb->wq = alloc_workqueue("ipaq-battery-wq", - WQ_MEM_RECLAIM | WQ_PERCPU, 0); + mb->wq = devm_alloc_workqueue(&pdev->dev, "ipaq-battery-wq", + WQ_MEM_RECLAIM | WQ_PERCPU, 0); if (!mb->wq) return -ENOMEM; - INIT_DELAYED_WORK(&mb->update, micro_battery_work); + ret = devm_delayed_work_autocancel(&pdev->dev, &mb->update, micro_battery_work); + if (ret) + return ret; + platform_set_drvdata(pdev, mb); queue_delayed_work(mb->wq, &mb->update, 1); - micro_batt_power = power_supply_register(&pdev->dev, - µ_batt_power_desc, NULL); - if (IS_ERR(micro_batt_power)) { - ret = PTR_ERR(micro_batt_power); - goto batt_err; - } + micro_batt_power = devm_power_supply_register(&pdev->dev, + µ_batt_power_desc, + NULL); + if (IS_ERR(micro_batt_power)) + return PTR_ERR(micro_batt_power); - micro_ac_power = power_supply_register(&pdev->dev, - µ_ac_power_desc, NULL); - if (IS_ERR(micro_ac_power)) { - ret = PTR_ERR(micro_ac_power); - goto ac_err; - } + micro_ac_power = devm_power_supply_register(&pdev->dev, + µ_ac_power_desc, NULL); + if (IS_ERR(micro_ac_power)) + return PTR_ERR(micro_ac_power); dev_info(&pdev->dev, "iPAQ micro battery driver\n"); return 0; - -ac_err: - power_supply_unregister(micro_batt_power); -batt_err: - cancel_delayed_work_sync(&mb->update); - destroy_workqueue(mb->wq); - return ret; -} - -static void micro_batt_remove(struct platform_device *pdev) - -{ - struct micro_battery *mb = platform_get_drvdata(pdev); - - power_supply_unregister(micro_ac_power); - power_supply_unregister(micro_batt_power); - cancel_delayed_work_sync(&mb->update); - destroy_workqueue(mb->wq); } static int __maybe_unused micro_batt_suspend(struct device *dev) @@ -303,7 +286,6 @@ static struct platform_driver micro_batt_device_driver = { .pm = µ_batt_dev_pm_ops, }, .probe = micro_batt_probe, - .remove = micro_batt_remove, }; module_platform_driver(micro_batt_device_driver); diff --git a/drivers/power/supply/macsmc-power.c b/drivers/power/supply/macsmc-power.c new file mode 100644 index 000000000000..33ca07460f3a --- /dev/null +++ b/drivers/power/supply/macsmc-power.c @@ -0,0 +1,855 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC Power/Battery Management Driver + * + * This driver exposes battery telemetry (voltage, current, temperature, health) + * and AC adapter status provided by the Apple SMC (System Management Controller) + * on Apple Silicon systems. + * + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_STRING_LENGTH 256 + +/* + * The SMC reports charge in mAh (Coulombs) but energy in mWh (Joules). + * We lack a register for "Nominal Voltage" or "Energy Accumulator". + * We use a fixed 3.8V/cell constant to approximate energy stats for userspace, + * derived from empirical data across supported MacBook models. + */ +#define MACSMC_NOMINAL_CELL_VOLTAGE_MV 3800 + +/* SMC Key Flags */ +#define CHNC_BATTERY_FULL BIT(0) +#define CHNC_NO_CHARGER BIT(7) +#define CHNC_NOCHG_CH0C BIT(14) +#define CHNC_NOCHG_CH0B_CH0K BIT(15) +#define CHNC_BATTERY_FULL_2 BIT(18) +#define CHNC_BMS_BUSY BIT(23) +#define CHNC_CHLS_LIMIT BIT(24) +#define CHNC_NOAC_CH0J BIT(53) +#define CHNC_NOAC_CH0I BIT(54) + +#define CH0R_LOWER_FLAGS GENMASK(15, 0) +#define CH0R_NOAC_CH0I BIT(0) +#define CH0R_NOAC_DISCONNECTED BIT(4) +#define CH0R_NOAC_CH0J BIT(5) +#define CH0R_BMS_BUSY BIT(8) +#define CH0R_NOAC_CH0K BIT(9) +#define CH0R_NOAC_CHWA BIT(11) + +#define CH0X_CH0C BIT(0) +#define CH0X_CH0B BIT(1) + +#define ACSt_CAN_BOOT_AP BIT(2) +#define ACSt_CAN_BOOT_IBOOT BIT(1) + +#define CHWA_CHLS_FIXED_START_OFFSET 5 +#define CHLS_MIN_END_THRESHOLD 10 +#define CHLS_FORCE_DISCHARGE 0x100 +#define CHWA_FIXED_END_THRESHOLD 80 +#define CHWA_PROP_WRITE_THRESHOLD 95 + +#define MACSMC_MAX_BATT_PROPS 50 +#define MACSMC_MAX_AC_PROPS 10 + +struct macsmc_power { + struct device *dev; + struct apple_smc *smc; + + struct power_supply_desc ac_desc; + struct power_supply_desc batt_desc; + + struct power_supply *batt; + struct power_supply *ac; + + char model_name[MAX_STRING_LENGTH]; + char serial_number[MAX_STRING_LENGTH]; + char mfg_date[MAX_STRING_LENGTH]; + + /* Supported feature flags based on SMC key presence */ + bool has_chwa; /* Charge limit (Modern firmware) */ + bool has_chls; /* Charge limit (Older firmware) */ + bool has_ch0i; /* Force discharge (Older firmware) */ + bool has_ch0c; /* Inhibit charge (Older firmware) */ + bool has_chte; /* Inhibit charge (Modern firmware) */ + + u8 num_cells; + int nominal_voltage_mv; + + struct notifier_block nb; + struct work_struct critical_work; + bool emergency_shutdown_triggered; + bool orderly_shutdown_triggered; +}; + +static int macsmc_battery_get_status(struct macsmc_power *power) +{ + u64 nocharge_flags; + u32 nopower_flags; + u16 ac_current; + int charge_limit = 0; + bool limited = false; + bool flag; + int ret; + + /* + * B0AV (Voltage) is fundamental. If we can't read it, we assume the + * battery is gone. CHCE (Hardware charger present) / CHCC (Hardware + * charger capable) are fundamental status flags. + * BSFC (System full charge) / CHSC (System charging) are fundamental + * status flags. + */ + + /* Check if power input is inhibited (e.g. BMS balancing cycle) */ + ret = apple_smc_read_u32(power->smc, SMC_KEY(CH0R), &nopower_flags); + if (!ret && (nopower_flags & CH0R_LOWER_FLAGS & ~CH0R_BMS_BUSY)) + return POWER_SUPPLY_STATUS_DISCHARGING; + + /* Check if charger is present */ + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHCE), &flag); + if (ret < 0) + return ret; + if (!flag) + return POWER_SUPPLY_STATUS_DISCHARGING; + + /* Check if AC is charge capable */ + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHCC), &flag); + if (ret < 0) + return ret; + if (!flag) + return POWER_SUPPLY_STATUS_DISCHARGING; + + /* Check if AC input limit is too low */ + ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-i), &ac_current); + if (!ret && ac_current < 100) + return POWER_SUPPLY_STATUS_DISCHARGING; + + /* Check if battery is full */ + ret = apple_smc_read_flag(power->smc, SMC_KEY(BSFC), &flag); + if (ret < 0) + return ret; + if (flag) + return POWER_SUPPLY_STATUS_FULL; + + /* Check for user-defined charge limits */ + if (power->has_chls) { + u16 vu16; + + ret = apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16); + if (ret == 0 && (vu16 & 0xff) >= CHLS_MIN_END_THRESHOLD) + charge_limit = (vu16 & 0xff) - CHWA_CHLS_FIXED_START_OFFSET; + } else if (power->has_chwa) { + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHWA), &flag); + if (ret == 0 && flag) + charge_limit = CHWA_FIXED_END_THRESHOLD - CHWA_CHLS_FIXED_START_OFFSET; + } + + if (charge_limit > 0) { + u8 buic = 0; + + if (apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &buic) >= 0 && + buic >= charge_limit) + limited = true; + } + + /* Check charging inhibitors */ + ret = apple_smc_read_u64(power->smc, SMC_KEY(CHNC), &nocharge_flags); + if (!ret) { + if (nocharge_flags & CHNC_BATTERY_FULL) + return POWER_SUPPLY_STATUS_FULL; + /* BMS busy shows up as inhibit, but we treat it as charging */ + else if (nocharge_flags == CHNC_BMS_BUSY && !limited) + return POWER_SUPPLY_STATUS_CHARGING; + else if (nocharge_flags) + return POWER_SUPPLY_STATUS_NOT_CHARGING; + else + return POWER_SUPPLY_STATUS_CHARGING; + } + + /* Fallback: System charging flag */ + ret = apple_smc_read_flag(power->smc, SMC_KEY(CHSC), &flag); + if (ret < 0) + return ret; + if (!flag) + return POWER_SUPPLY_STATUS_NOT_CHARGING; + + return POWER_SUPPLY_STATUS_CHARGING; +} + +static int macsmc_battery_get_charge_behaviour(struct macsmc_power *power) +{ + int ret; + u8 val8; + u8 chte_buf[4]; + + if (power->has_ch0i) { + ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0I), &val8); + if (ret) + return ret; + if (val8 & CH0R_NOAC_CH0I) + return POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE; + } + + if (power->has_chte) { + ret = apple_smc_read(power->smc, SMC_KEY(CHTE), chte_buf, 4); + if (ret < 0) + return ret; + + if (chte_buf[0] == 0x01) + return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; + } else if (power->has_ch0c) { + ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0C), &val8); + if (ret) + return ret; + if (val8 & CH0X_CH0C) + return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; + } + + return POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; +} + +static int macsmc_battery_set_charge_behaviour(struct macsmc_power *power, int val) +{ + int ret; + + switch (val) { + case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: + /* Reset all inhibitors to a known-good 'auto' state */ + if (power->has_ch0i) { + ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0I), 0); + if (ret) + return ret; + } + + if (power->has_chte) { + ret = apple_smc_write_u32(power->smc, SMC_KEY(CHTE), 0); + if (ret) + return ret; + } else if (power->has_ch0c) { + ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0C), 0); + if (ret) + return ret; + } + return 0; + + case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: + if (power->has_chte) + return apple_smc_write_u32(power->smc, SMC_KEY(CHTE), 1); + else if (power->has_ch0c) + return apple_smc_write_u8(power->smc, SMC_KEY(CH0C), 1); + else + return -EOPNOTSUPP; + + case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE: + if (!power->has_ch0i) + return -EOPNOTSUPP; + return apple_smc_write_u8(power->smc, SMC_KEY(CH0I), 1); + + default: + return -EINVAL; + } +} + +static int macsmc_battery_get_date(const char *s, int *out) +{ + if (!isdigit(s[0]) || !isdigit(s[1])) + return -EOPNOTSUPP; + + *out = (s[0] - '0') * 10 + s[1] - '0'; + return 0; +} + +static int macsmc_battery_get_capacity_level(struct macsmc_power *power) +{ + bool flag; + u32 val; + int ret; + + /* Check for emergency shutdown condition */ + if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val) >= 0 && val) + return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + + /* Check AC status for whether we could boot in this state */ + if (apple_smc_read_u32(power->smc, SMC_KEY(ACSt), &val) >= 0) { + if (!(val & ACSt_CAN_BOOT_IBOOT)) + return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + + if (!(val & ACSt_CAN_BOOT_AP)) + return POWER_SUPPLY_CAPACITY_LEVEL_LOW; + } + + /* BSFC = Battery System Full Charge */ + ret = apple_smc_read_flag(power->smc, SMC_KEY(BSFC), &flag); + if (ret < 0) + return POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; + + if (flag) + return POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else + return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; +} + +static int macsmc_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct macsmc_power *power = power_supply_get_drvdata(psy); + int ret = 0; + u8 vu8; + u16 vu16; + s16 vs16; + s32 vs32; + s64 vs64; + bool flag; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = macsmc_battery_get_status(power); + ret = val->intval < 0 ? val->intval : 0; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + val->intval = macsmc_battery_get_charge_behaviour(power); + ret = val->intval < 0 ? val->intval : 0; + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TE), &vu16); + val->intval = vu16 == 0xffff ? 0 : vu16 * 60; + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TF), &vu16); + val->intval = vu16 == 0xffff ? 0 : vu16 * 60; + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &vu8); + val->intval = vu8; + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + val->intval = macsmc_battery_get_capacity_level(power); + ret = val->intval < 0 ? val->intval : 0; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = apple_smc_read_s16(power->smc, SMC_KEY(B0AC), &vs16); + val->intval = vs16 * 1000; + break; + case POWER_SUPPLY_PROP_POWER_NOW: + ret = apple_smc_read_s32(power->smc, SMC_KEY(B0AP), &vs32); + val->intval = vs32 * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + ret = apple_smc_read_u16(power->smc, SMC_KEY(BITV), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + /* Calculate total max design voltage from per-cell maximum voltage */ + ret = apple_smc_read_u16(power->smc, SMC_KEY(BVVN), &vu16); + val->intval = vu16 * 1000 * power->num_cells; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + /* Lifetime min */ + ret = apple_smc_read_s16(power->smc, SMC_KEY(BLPM), &vs16); + val->intval = vs16 * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + /* Lifetime max */ + ret = apple_smc_read_s16(power->smc, SMC_KEY(BLPX), &vs16); + val->intval = vs16 * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RC), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RI), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RV), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0FC), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16); + /* B0RM is Big Endian, likely pass through from TI gas gauge */ + val->intval = (s16)swab16(vu16) * 1000; + break; + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16); + val->intval = vu16 * power->nominal_voltage_mv; + break; + case POWER_SUPPLY_PROP_ENERGY_FULL: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0FC), &vu16); + val->intval = vu16 * power->nominal_voltage_mv; + break; + case POWER_SUPPLY_PROP_ENERGY_NOW: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16); + /* B0RM is Big Endian, likely pass through from TI gas gauge */ + val->intval = (s16)swab16(vu16) * power->nominal_voltage_mv; + break; + case POWER_SUPPLY_PROP_TEMP: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AT), &vu16); + val->intval = vu16 - 2732; /* Kelvin x10 to Celsius x10 */ + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + ret = apple_smc_read_s64(power->smc, SMC_KEY(BAAC), &vs64); + val->intval = vs64; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + ret = apple_smc_read_u16(power->smc, SMC_KEY(B0CT), &vu16); + val->intval = vu16; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_SYSTEM; + break; + case POWER_SUPPLY_PROP_HEALTH: + flag = false; + ret = apple_smc_read_flag(power->smc, SMC_KEY(BBAD), &flag); + val->intval = flag ? POWER_SUPPLY_HEALTH_DEAD : POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = power->model_name; + break; + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + val->strval = power->serial_number; + break; + case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: + ret = macsmc_battery_get_date(&power->mfg_date[0], &val->intval); + /* The SMC reports the manufacture year as an offset from 1992. */ + val->intval += 1992; + break; + case POWER_SUPPLY_PROP_MANUFACTURE_MONTH: + ret = macsmc_battery_get_date(&power->mfg_date[2], &val->intval); + break; + case POWER_SUPPLY_PROP_MANUFACTURE_DAY: + ret = macsmc_battery_get_date(&power->mfg_date[4], &val->intval); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int macsmc_battery_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct macsmc_power *power = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + return macsmc_battery_set_charge_behaviour(power, val->intval); + default: + return -EINVAL; + } +} + +static int macsmc_battery_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + return true; + default: + return false; + } +} + +static const struct power_supply_desc macsmc_battery_desc_template = { + .name = "macsmc-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = macsmc_battery_get_property, + .set_property = macsmc_battery_set_property, + .property_is_writeable = macsmc_battery_property_is_writeable, +}; + +static int macsmc_ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct macsmc_power *power = power_supply_get_drvdata(psy); + int ret = 0; + u16 vu16; + u32 vu32; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = apple_smc_read_u32(power->smc, SMC_KEY(CHIS), &vu32); + val->intval = !!vu32; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-n), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-i), &vu16); + val->intval = vu16 * 1000; + break; + case POWER_SUPPLY_PROP_INPUT_POWER_LIMIT: + ret = apple_smc_read_u32(power->smc, SMC_KEY(ACPW), &vu32); + val->intval = vu32 * 1000; + break; + default: + return -EINVAL; + } + + return ret; +} + +static const struct power_supply_desc macsmc_ac_desc_template = { + .name = "macsmc-ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .get_property = macsmc_ac_get_property, +}; + +static void macsmc_power_critical_work(struct work_struct *wrk) +{ + struct macsmc_power *power = container_of(wrk, struct macsmc_power, critical_work); + u16 bitv, b0av; + u32 bcf0; + + if (!power->batt) + return; + + /* + * Avoid duplicate atempts at emergency shutdown + */ + if (power->emergency_shutdown_triggered || system_state > SYSTEM_RUNNING) + return; + + /* + * EMERGENCY: Check voltage vs design minimum. + * If we are below BITV, the battery is physically exhausted. + * We must shut down NOW to protect the filesystem. + */ + if (apple_smc_read_u16(power->smc, SMC_KEY(BITV), &bitv) >= 0 && + apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &b0av) >= 0 && + b0av < bitv) { + power->emergency_shutdown_triggered = true; + dev_emerg(power->dev, + "Battery voltage (%d mV) below design minimum (%d mV)! Emergency shutdown.\n", + b0av, bitv); + + /* + * Shutdown is now imminent. Kick userspace again and give it some + * brief time to (hopefully) flush what's needed, before forcing. + */ + hw_protection_trigger("Battery voltage below design minimum", 1500); + } + + /* + * Avoid duplicate attempts at orderly shutdown. + * Voltage check is above this as we may want to + * "upgrade" an orderly shutdown to a critical power + * off if voltage drops. + */ + if (power->orderly_shutdown_triggered || system_state > SYSTEM_RUNNING) + return; + + /* + * Check if SMC flagged the battery as empty. + * We trigger a graceful shutdown to let the OS save data. + */ + if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &bcf0) == 0 && bcf0 != 0) { + power->orderly_shutdown_triggered = true; + dev_crit(power->dev, "Battery critical (empty flag set). Triggering orderly shutdown.\n"); + orderly_poweroff(true); + } +} + +static int macsmc_power_event(struct notifier_block *nb, unsigned long event, void *data) +{ + struct macsmc_power *power = container_of(nb, struct macsmc_power, nb); + + /* + * SMC Event IDs are correlated to physical events (e.g. charger + * connect/disconnect) but the exact meaning of each ID is predicted. + * 0x71... indicates power/battery events. + */ + if ((event & 0xffffff00) == 0x71010100 || /* Charger status change */ + (event & 0xffff0000) == 0x71060000 || /* Port charge state change */ + (event & 0xffff0000) == 0x71130000) { /* Connector insert/remove event */ + if (power->batt) + power_supply_changed(power->batt); + if (power->ac) + power_supply_changed(power->ac); + return NOTIFY_OK; + } else if (event == 0x71020000) { + /* Critical battery warning */ + if (power->batt) + schedule_work(&power->critical_work); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + +static int macsmc_power_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + struct power_supply_config psy_cfg = {}; + struct macsmc_power *power; + bool has_battery = false; + bool has_ac_adapter = false; + int ret = -ENODEV; + bool flag; + u16 vu16; + u32 val32; + enum power_supply_property *props; + size_t nprops; + + if (!smc) + return -ENODEV; + + power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); + if (!power) + return -ENOMEM; + + power->dev = dev; + power->smc = smc; + dev_set_drvdata(dev, power); + + INIT_WORK(&power->critical_work, macsmc_power_critical_work); + ret = devm_work_autocancel(dev, &power->critical_work, macsmc_power_critical_work); + if (ret) + return ret; + + /* + * Check for battery presence. + * B0AV is a fundamental key. + */ + if (apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &vu16) == 0 && + macsmc_battery_get_status(power) > POWER_SUPPLY_STATUS_UNKNOWN) + has_battery = true; + + /* + * Check for AC adapter presence. + * CHIS is a fundamental key. + */ + if (apple_smc_key_exists(smc, SMC_KEY(CHIS))) + has_ac_adapter = true; + + if (!has_battery && !has_ac_adapter) + return -ENODEV; + + if (has_battery) { + power->batt_desc = macsmc_battery_desc_template; + props = devm_kcalloc(dev, MACSMC_MAX_BATT_PROPS, + sizeof(enum power_supply_property), + GFP_KERNEL); + if (!props) + return -ENOMEM; + + nprops = 0; + + /* Fundamental properties */ + props[nprops++] = POWER_SUPPLY_PROP_STATUS; + props[nprops++] = POWER_SUPPLY_PROP_PRESENT; + props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; + props[nprops++] = POWER_SUPPLY_PROP_CURRENT_NOW; + props[nprops++] = POWER_SUPPLY_PROP_POWER_NOW; + props[nprops++] = POWER_SUPPLY_PROP_CAPACITY; + props[nprops++] = POWER_SUPPLY_PROP_CAPACITY_LEVEL; + props[nprops++] = POWER_SUPPLY_PROP_TEMP; + props[nprops++] = POWER_SUPPLY_PROP_CYCLE_COUNT; + props[nprops++] = POWER_SUPPLY_PROP_HEALTH; + props[nprops++] = POWER_SUPPLY_PROP_SCOPE; + props[nprops++] = POWER_SUPPLY_PROP_MODEL_NAME; + props[nprops++] = POWER_SUPPLY_PROP_SERIAL_NUMBER; + props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_YEAR; + props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_MONTH; + props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_DAY; + + /* Extended properties usually present */ + props[nprops++] = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW; + props[nprops++] = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW; + props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN; + props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN; + props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; + props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; + props[nprops++] = POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT; + props[nprops++] = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX; + props[nprops++] = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE; + props[nprops++] = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; + props[nprops++] = POWER_SUPPLY_PROP_CHARGE_FULL; + props[nprops++] = POWER_SUPPLY_PROP_CHARGE_NOW; + props[nprops++] = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; + props[nprops++] = POWER_SUPPLY_PROP_ENERGY_FULL; + props[nprops++] = POWER_SUPPLY_PROP_ENERGY_NOW; + props[nprops++] = POWER_SUPPLY_PROP_CHARGE_COUNTER; + + /* Detect features based on key availability */ + if (apple_smc_key_exists(smc, SMC_KEY(CHTE))) + power->has_chte = true; + if (apple_smc_key_exists(smc, SMC_KEY(CH0C))) + power->has_ch0c = true; + if (apple_smc_key_exists(smc, SMC_KEY(CH0I))) + power->has_ch0i = true; + + /* Reset "Optimised Battery Charging" flags to default state */ + if (power->has_chte) + apple_smc_write_u32(smc, SMC_KEY(CHTE), 0); + else if (power->has_ch0c) + apple_smc_write_u8(smc, SMC_KEY(CH0C), 0); + + if (power->has_ch0i) + apple_smc_write_u8(smc, SMC_KEY(CH0I), 0); + + apple_smc_write_u8(smc, SMC_KEY(CH0K), 0); + apple_smc_write_u8(smc, SMC_KEY(CH0B), 0); + + /* Configure charge behaviour if supported */ + if (power->has_ch0i || power->has_ch0c || power->has_chte) { + props[nprops++] = POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR; + + power->batt_desc.charge_behaviours = + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO); + + if (power->has_ch0i) + power->batt_desc.charge_behaviours |= + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE); + + if (power->has_chte || power->has_ch0c) + power->batt_desc.charge_behaviours |= + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE); + } + + /* Detect charge limit method (CHWA vs CHLS) */ + if (apple_smc_read_flag(power->smc, SMC_KEY(CHWA), &flag) == 0) + power->has_chwa = true; + else if (apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16) >= 0) + power->has_chls = true; + + if (nprops > MACSMC_MAX_BATT_PROPS) + return -ENOMEM; + + power->batt_desc.properties = props; + power->batt_desc.num_properties = nprops; + + /* Fetch identity strings */ + apple_smc_read(smc, SMC_KEY(BMDN), power->model_name, + sizeof(power->model_name) - 1); + apple_smc_read(smc, SMC_KEY(BMSN), power->serial_number, + sizeof(power->serial_number) - 1); + apple_smc_read(smc, SMC_KEY(BMDT), power->mfg_date, + sizeof(power->mfg_date) - 1); + + apple_smc_read_u8(power->smc, SMC_KEY(BNCB), &power->num_cells); + power->nominal_voltage_mv = MACSMC_NOMINAL_CELL_VOLTAGE_MV * power->num_cells; + + /* Enable critical shutdown notifications by reading status once */ + apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val32); + + psy_cfg.drv_data = power; + power->batt = devm_power_supply_register(dev, &power->batt_desc, &psy_cfg); + if (IS_ERR(power->batt)) { + dev_err_probe(dev, PTR_ERR(power->batt), + "Failed to register battery\n"); + /* Don't return failure yet; try AC registration first */ + power->batt = NULL; + } + } + + if (has_ac_adapter) { + power->ac_desc = macsmc_ac_desc_template; + props = devm_kcalloc(dev, MACSMC_MAX_AC_PROPS, + sizeof(enum power_supply_property), + GFP_KERNEL); + if (!props) + return -ENOMEM; + + nprops = 0; + + /* Online status is fundamental */ + props[nprops++] = POWER_SUPPLY_PROP_ONLINE; + + /* Input power limits are usually available */ + if (apple_smc_key_exists(power->smc, SMC_KEY(ACPW))) + props[nprops++] = POWER_SUPPLY_PROP_INPUT_POWER_LIMIT; + + /* macOS 15.4+ firmware dropped legacy AC keys (AC-n, AC-i) */ + if (apple_smc_read_u16(power->smc, SMC_KEY(AC-n), &vu16) >= 0) { + props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; + props[nprops++] = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT; + } + + if (nprops > MACSMC_MAX_AC_PROPS) + return -ENOMEM; + + power->ac_desc.properties = props; + power->ac_desc.num_properties = nprops; + + psy_cfg.drv_data = power; + power->ac = devm_power_supply_register(dev, &power->ac_desc, &psy_cfg); + if (IS_ERR(power->ac)) { + dev_err_probe(dev, PTR_ERR(power->ac), + "Failed to register AC adapter\n"); + power->ac = NULL; + } + } + + /* Final check: did we register anything? */ + if (!power->batt && !power->ac) + return -ENODEV; + + power->nb.notifier_call = macsmc_power_event; + blocking_notifier_chain_register(&smc->event_handlers, &power->nb); + + return 0; +} + +static void macsmc_power_remove(struct platform_device *pdev) +{ + struct macsmc_power *power = dev_get_drvdata(&pdev->dev); + + blocking_notifier_chain_unregister(&power->smc->event_handlers, &power->nb); +} + +static const struct platform_device_id macsmc_power_id[] = { + { "macsmc-power" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, macsmc_power_id); + +static struct platform_driver macsmc_power_driver = { + .driver = { + .name = "macsmc-power", + }, + .id_table = macsmc_power_id, + .probe = macsmc_power_probe, + .remove = macsmc_power_remove, +}; +module_platform_driver(macsmc_power_driver); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC battery and power management driver"); +MODULE_AUTHOR("Hector Martin "); +MODULE_AUTHOR("Michael Reeves "); diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index acea176101fa..167fb3fb3732 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -61,6 +61,7 @@ struct max17042_chip { struct work_struct work; int init_complete; int irq; + int task_period; }; static enum power_supply_property max17042_battery_props[] = { @@ -88,6 +89,7 @@ static enum power_supply_property max17042_battery_props[] = { POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, // these two have to be at the end on the list POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, @@ -131,7 +133,7 @@ static int max17042_get_status(struct max17042_chip *chip, int *status) * FullCAP to match RepCap when it detects end of charging. * * When this cycle the battery gets charged to a higher (calculated) - * capacity then the previous cycle then FullCAP will get updated + * capacity than the previous cycle then FullCAP will get updated * continuously once end-of-charge detection kicks in, so allow the * 2 to differ a bit. */ @@ -201,7 +203,7 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health) goto out; } - if (vbatt > chip->pdata->vmax + MAX17042_VMAX_TOLERANCE) { + if (vbatt > size_add(chip->pdata->vmax, MAX17042_VMAX_TOLERANCE)) { *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; goto out; } @@ -331,6 +333,8 @@ static int max17042_get_property(struct power_supply *psy, return ret; data64 = data * 5000000ll; + data64 *= chip->task_period; + do_div(data64, MAX17042_DEFAULT_TASK_PERIOD); do_div(data64, chip->pdata->r_sns); val->intval = data64; break; @@ -340,6 +344,8 @@ static int max17042_get_property(struct power_supply *psy, return ret; data64 = data * 5000000ll; + data64 *= chip->task_period; + do_div(data64, MAX17042_DEFAULT_TASK_PERIOD); do_div(data64, chip->pdata->r_sns); val->intval = data64; break; @@ -349,6 +355,8 @@ static int max17042_get_property(struct power_supply *psy, return ret; data64 = data * 5000000ll; + data64 *= chip->task_period; + do_div(data64, MAX17042_DEFAULT_TASK_PERIOD); do_div(data64, chip->pdata->r_sns); val->intval = data64; break; @@ -358,6 +366,8 @@ static int max17042_get_property(struct power_supply *psy, return ret; data64 = sign_extend64(data, 15) * 5000000ll; + data64 *= chip->task_period; + data64 = div_s64(data64, MAX17042_DEFAULT_TASK_PERIOD); val->intval = div_s64(data64, chip->pdata->r_sns); break; case POWER_SUPPLY_PROP_TEMP: @@ -430,6 +440,25 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; + /* when charging, the value is not meaningful */ + if (data == U16_MAX) + return -ENODATA; + + val->intval = data * 5625 / 1000; + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: + if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055 && + chip->chip_type != MAXIM_DEVICE_TYPE_MAX77759) + return -EINVAL; + + ret = regmap_read(map, MAX17055_TTF, &data); + if (ret < 0) + return ret; + + /* when discharging, the value is not meaningful */ + if (data == U16_MAX) + return -ENODATA; + val->intval = data * 5625 / 1000; break; default: @@ -646,7 +675,8 @@ static void max17042_write_config_regs(struct max17042_chip *chip) regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg); if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 || chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050 || - chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) + chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055 || + chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759) regmap_write(map, MAX17047_FullSOCThr, config->full_soc_thresh); } @@ -783,7 +813,8 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) || (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) || - (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) { + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) || + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759)) { max17042_override_por(map, MAX17042_IAvg_empty, config->iavg_empty); max17042_override_por(map, MAX17042_TempNom, config->temp_nom); max17042_override_por(map, MAX17042_TempLim, config->temp_lim); @@ -792,7 +823,8 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) || (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) || - (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)) { + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) || + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759)) { max17042_override_por(map, MAX17047_V_empty, config->vempty); } } @@ -921,8 +953,12 @@ max17042_get_of_pdata(struct max17042_chip *chip) /* * Require current sense resistor value to be specified for * current-sense functionality to be enabled at all. + * maxim,rsns-microohm is the property name used by older DTs and kept + * for compatibility. */ - if (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0) { + if ((of_property_read_u32(np, "shunt-resistor-micro-ohms", + &prop) == 0) || + (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0)) { pdata->r_sns = prop; pdata->enable_current_sense = true; } @@ -1011,6 +1047,45 @@ static const struct regmap_config max17042_regmap_config = { .val_format_endian = REGMAP_ENDIAN_NATIVE, }; +static const struct regmap_range max77759_fg_registers[] = { + regmap_reg_range(MAX17042_STATUS, MAX77759_MixAtFull), + regmap_reg_range(MAX17042_VFSOC0Enable, MAX17042_VFSOC0Enable), + regmap_reg_range(MAX17042_MLOCKReg1, MAX17042_MLOCKReg2), + regmap_reg_range(MAX17042_MODELChrTbl, MAX17055_TimerH), + regmap_reg_range(MAX77759_IIn, MAX77759_IIn), + regmap_reg_range(MAX17055_AtQResidual, MAX17055_AtAvCap), + regmap_reg_range(MAX17042_OCVInternal, MAX17042_OCVInternal), + regmap_reg_range(MAX17042_VFSOC, MAX17042_VFSOC), +}; + +static const struct regmap_range max77759_fg_ro_registers[] = { + regmap_reg_range(MAX17042_FSTAT, MAX17042_FSTAT), + regmap_reg_range(MAX17042_OCVInternal, MAX17042_OCVInternal), + regmap_reg_range(MAX17042_VFSOC, MAX17042_VFSOC), +}; + +static const struct regmap_access_table max77759_fg_write_table = { + .yes_ranges = max77759_fg_registers, + .n_yes_ranges = ARRAY_SIZE(max77759_fg_registers), + .no_ranges = max77759_fg_ro_registers, + .n_no_ranges = ARRAY_SIZE(max77759_fg_ro_registers), +}; + +static const struct regmap_access_table max77759_fg_rd_table = { + .yes_ranges = max77759_fg_registers, + .n_yes_ranges = ARRAY_SIZE(max77759_fg_registers), +}; + +static const struct regmap_config max77759_fg_regmap_cfg = { + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xff, + .wr_table = &max77759_fg_write_table, + .rd_table = &max77759_fg_rd_table, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .cache_type = REGCACHE_NONE, +}; + static const struct power_supply_desc max17042_psy_desc = { .name = "max170xx_battery", .type = POWER_SUPPLY_TYPE_BATTERY, @@ -1037,6 +1112,7 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq { struct i2c_adapter *adapter = client->adapter; const struct power_supply_desc *max17042_desc = &max17042_psy_desc; + const struct regmap_config *regmap_config; struct power_supply_config psy_cfg = {}; struct max17042_chip *chip; int ret; @@ -1052,17 +1128,20 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq chip->dev = dev; chip->chip_type = chip_type; - chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config); - if (IS_ERR(chip->regmap)) { - dev_err(dev, "Failed to initialize regmap\n"); - return -EINVAL; - } + + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759) + regmap_config = &max77759_fg_regmap_cfg; + else + regmap_config = &max17042_regmap_config; + chip->regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(chip->regmap)) + return dev_err_probe(dev, PTR_ERR(chip->regmap), + "Failed to initialize regmap\n"); chip->pdata = max17042_get_pdata(chip); - if (!chip->pdata) { - dev_err(dev, "no platform data provided\n"); - return -EINVAL; - } + if (!chip->pdata) + return dev_err_probe(dev, -EINVAL, + "no platform data provided\n"); dev_set_drvdata(dev, chip); psy_cfg.drv_data = chip; @@ -1088,12 +1167,22 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007); } + chip->task_period = MAX17042_DEFAULT_TASK_PERIOD; + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759) { + ret = regmap_read(chip->regmap, MAX17042_TaskPeriod, &val); + if (ret) + return dev_err_probe(dev, ret, + "failed to read task period\n"); + chip->task_period = val; + } + dev_dbg(dev, "task period: %#.4x (%d)\n", chip->task_period, + chip->task_period); + chip->battery = devm_power_supply_register(dev, max17042_desc, &psy_cfg); - if (IS_ERR(chip->battery)) { - dev_err(dev, "failed: power supply register\n"); - return PTR_ERR(chip->battery); - } + if (IS_ERR(chip->battery)) + return dev_err_probe(dev, PTR_ERR(chip->battery), + "failed: power supply register\n"); if (irq) { unsigned int flags = IRQF_ONESHOT | IRQF_SHARED | IRQF_PROBE_SHARED; @@ -1236,6 +1325,8 @@ static const struct of_device_id max17042_dt_match[] __used = { .data = (void *) MAXIM_DEVICE_TYPE_MAX17055 }, { .compatible = "maxim,max77705-battery", .data = (void *) MAXIM_DEVICE_TYPE_MAX17047 }, + { .compatible = "maxim,max77759-fg", + .data = (void *) MAXIM_DEVICE_TYPE_MAX77759 }, { .compatible = "maxim,max77849-battery", .data = (void *) MAXIM_DEVICE_TYPE_MAX17047 }, { }, @@ -1248,6 +1339,7 @@ static const struct i2c_device_id max17042_id[] = { { "max17047", MAXIM_DEVICE_TYPE_MAX17047 }, { "max17050", MAXIM_DEVICE_TYPE_MAX17050 }, { "max17055", MAXIM_DEVICE_TYPE_MAX17055 }, + { "max77759-fg", MAXIM_DEVICE_TYPE_MAX77759 }, { "max77849-battery", MAXIM_DEVICE_TYPE_MAX17047 }, { } }; diff --git a/drivers/power/supply/max77705_charger.c b/drivers/power/supply/max77705_charger.c index 5dd02f658f5b..63b0b4f0cd21 100644 --- a/drivers/power/supply/max77705_charger.c +++ b/drivers/power/supply/max77705_charger.c @@ -646,51 +646,37 @@ static int max77705_charger_probe(struct i2c_client *i2c) if (ret) return dev_err_probe(dev, ret, "failed to add irq chip\n"); - chg->wqueue = create_singlethread_workqueue(dev_name(dev)); + chg->wqueue = devm_alloc_ordered_workqueue(dev, "%s", 0, dev_name(dev)); if (!chg->wqueue) return -ENOMEM; ret = devm_work_autocancel(dev, &chg->chgin_work, max77705_chgin_isr_work); - if (ret) { - dev_err_probe(dev, ret, "failed to initialize interrupt work\n"); - goto destroy_wq; - } + if (ret) + return dev_err_probe(dev, ret, "failed to initialize interrupt work\n"); ret = max77705_charger_initialize(chg); - if (ret) { - dev_err_probe(dev, ret, "failed to initialize charger IC\n"); - goto destroy_wq; - } + if (ret) + return dev_err_probe(dev, ret, "failed to initialize charger IC\n"); ret = devm_request_threaded_irq(dev, regmap_irq_get_virq(irq_data, MAX77705_CHGIN_I), NULL, max77705_chgin_irq, IRQF_TRIGGER_NONE, "chgin-irq", chg); - if (ret) { - dev_err_probe(dev, ret, "Failed to Request chgin IRQ\n"); - goto destroy_wq; - } + if (ret) + return ret; ret = devm_request_threaded_irq(dev, regmap_irq_get_virq(irq_data, MAX77705_AICL_I), NULL, max77705_aicl_irq, IRQF_TRIGGER_NONE, "aicl-irq", chg); - if (ret) { - dev_err_probe(dev, ret, "Failed to Request aicl IRQ\n"); - goto destroy_wq; - } + if (ret) + return ret; ret = max77705_charger_enable(chg); - if (ret) { - dev_err_probe(dev, ret, "failed to enable charge\n"); - goto destroy_wq; - } + if (ret) + return dev_err_probe(dev, ret, "failed to enable charge\n"); return devm_add_action_or_reset(dev, max77705_charger_disable, chg); - -destroy_wq: - destroy_workqueue(chg->wqueue); - return ret; } static const struct of_device_id max77705_charger_of_match[] = { diff --git a/drivers/power/supply/max77759_charger.c b/drivers/power/supply/max77759_charger.c new file mode 100644 index 000000000000..9bb414599f16 --- /dev/null +++ b/drivers/power/supply/max77759_charger.c @@ -0,0 +1,774 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * max77759_charger.c - Battery charger driver for MAX77759 charger device. + * + * Copyright 2025 Google LLC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Default values for Fast Charge Current & Float Voltage */ +#define CHG_CC_DEFAULT_UA 2266770 +#define CHG_FV_DEFAULT_MV 4300 + +#define MAX_NUM_RETRIES 3 +#define PSY_WORK_RETRY_DELAY_MS 10 + +#define FOREACH_IRQ(S) \ + S(AICL), \ + S(CHGIN), \ + S(CHG), \ + S(INLIM), \ + S(BAT_OILO), \ + S(CHG_STA_CC), \ + S(CHG_STA_CV), \ + S(CHG_STA_TO), \ + S(CHG_STA_DONE) + +#define GENERATE_ENUM(e) e +#define GENERATE_STRING(s) #s + +enum { + FOREACH_IRQ(GENERATE_ENUM) +}; + +static const char *const chgr_irqs_str[] = { + FOREACH_IRQ(GENERATE_STRING) +}; + +#define NUM_IRQS ARRAY_SIZE(chgr_irqs_str) + +/* Fast charge current limits (in uA) */ +static const struct linear_range chgcc_limit_ranges[] = { + LINEAR_RANGE(133330, 0x0, 0x2, 0), + LINEAR_RANGE(200000, 0x3, 0x3C, 66670), +}; + +/* Charge Termination Voltage Limits (in mV) */ +static const struct linear_range chg_cv_prm_ranges[] = { + LINEAR_RANGE(3800, 0x38, 0x39, 100), + LINEAR_RANGE(4000, 0x0, 0x32, 10), +}; + +/* USB input current limits (in uA) */ +static const struct linear_range chgin_ilim_ranges[] = { + LINEAR_RANGE(100000, 0x3, 0x7F, 25000), +}; + +struct max77759_charger { + struct device *dev; + struct regmap *regmap; + struct power_supply *psy; + struct regulator_dev *chgin_otg_rdev; + struct notifier_block nb; + struct power_supply *tcpm_psy; + struct delayed_work psy_work; + struct mutex retry_lock; /* Protects psy_work_retry_cnt */ + u32 psy_work_retry_cnt; + int irqs[NUM_IRQS]; + struct mutex lock; /* protects the state below */ + enum max77759_chgr_mode mode; +}; + +static inline int unlock_prot_regs(struct max77759_charger *chg, bool unlock) +{ + return regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_06, + MAX77759_CHGR_REG_CHG_CNFG_06_CHGPROT, unlock + ? MAX77759_CHGR_REG_CHG_CNFG_06_CHGPROT : 0); +} + +static int charger_input_valid(struct max77759_charger *chg) +{ + u32 val; + int ret; + + ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_INT_OK, &val); + if (ret) + return ret; + + return (val & MAX77759_CHGR_REG_CHG_INT_CHG) && + (val & MAX77759_CHGR_REG_CHG_INT_CHGIN); +} + +static int get_online(struct max77759_charger *chg) +{ + u32 val; + int ret; + + ret = charger_input_valid(chg); + if (ret <= 0) + return ret; + + ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_02, &val); + if (ret) + return ret; + + guard(mutex)(&chg->lock); + + return (val & MAX77759_CHGR_REG_CHG_DETAILS_02_CHGIN_STS) && + (chg->mode == MAX77759_CHGR_MODE_CHG_BUCK_ON); +} + +static int get_status(struct max77759_charger *chg) +{ + u32 val; + int ret; + + ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_01, &val); + if (ret) + return ret; + + switch (FIELD_GET(MAX77759_CHGR_REG_CHG_DETAILS_01_CHG_DTLS, val)) { + case MAX77759_CHGR_CHG_DTLS_PREQUAL: + case MAX77759_CHGR_CHG_DTLS_CC: + case MAX77759_CHGR_CHG_DTLS_CV: + case MAX77759_CHGR_CHG_DTLS_TO: + return POWER_SUPPLY_STATUS_CHARGING; + case MAX77759_CHGR_CHG_DTLS_DONE: + return POWER_SUPPLY_STATUS_FULL; + case MAX77759_CHGR_CHG_DTLS_TIMER_FAULT: + case MAX77759_CHGR_CHG_DTLS_SUSP_BATT_THM: + case MAX77759_CHGR_CHG_DTLS_OFF_WDOG_TIMER: + case MAX77759_CHGR_CHG_DTLS_SUSP_JEITA: + return POWER_SUPPLY_STATUS_NOT_CHARGING; + case MAX77759_CHGR_CHG_DTLS_OFF: + return POWER_SUPPLY_STATUS_DISCHARGING; + default: + break; + } + + return POWER_SUPPLY_STATUS_UNKNOWN; +} + +static int get_charge_type(struct max77759_charger *chg) +{ + u32 val; + int ret; + + ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_01, &val); + if (ret) + return ret; + + switch (FIELD_GET(MAX77759_CHGR_REG_CHG_DETAILS_01_CHG_DTLS, val)) { + case MAX77759_CHGR_CHG_DTLS_PREQUAL: + return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + case MAX77759_CHGR_CHG_DTLS_CC: + case MAX77759_CHGR_CHG_DTLS_CV: + return POWER_SUPPLY_CHARGE_TYPE_FAST; + case MAX77759_CHGR_CHG_DTLS_TO: + return POWER_SUPPLY_CHARGE_TYPE_STANDARD; + case MAX77759_CHGR_CHG_DTLS_DONE: + case MAX77759_CHGR_CHG_DTLS_TIMER_FAULT: + case MAX77759_CHGR_CHG_DTLS_SUSP_BATT_THM: + case MAX77759_CHGR_CHG_DTLS_OFF_WDOG_TIMER: + case MAX77759_CHGR_CHG_DTLS_SUSP_JEITA: + case MAX77759_CHGR_CHG_DTLS_OFF: + return POWER_SUPPLY_CHARGE_TYPE_NONE; + default: + break; + } + + return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; +} + +static int get_chg_health(struct max77759_charger *chg) +{ + u32 val; + int ret; + + ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_00, &val); + if (ret) + return ret; + + switch (FIELD_GET(MAX77759_CHGR_REG_CHG_DETAILS_00_CHGIN_DTLS, val)) { + case MAX77759_CHGR_CHGIN_DTLS_VBUS_UNDERVOLTAGE: + case MAX77759_CHGR_CHGIN_DTLS_VBUS_MARGINAL_VOLTAGE: + return POWER_SUPPLY_HEALTH_UNDERVOLTAGE; + case MAX77759_CHGR_CHGIN_DTLS_VBUS_OVERVOLTAGE: + return POWER_SUPPLY_HEALTH_OVERVOLTAGE; + case MAX77759_CHGR_CHGIN_DTLS_VBUS_VALID: + return POWER_SUPPLY_HEALTH_GOOD; + default: + break; + } + + return POWER_SUPPLY_HEALTH_UNKNOWN; +} + +static int get_batt_health(struct max77759_charger *chg) +{ + u32 val; + int ret; + + ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_01, &val); + if (ret) + return ret; + + switch (FIELD_GET(MAX77759_CHGR_REG_CHG_DETAILS_01_BAT_DTLS, val)) { + case MAX77759_CHGR_BAT_DTLS_NO_BATT_CHG_SUSP: + return POWER_SUPPLY_HEALTH_NO_BATTERY; + case MAX77759_CHGR_BAT_DTLS_DEAD_BATTERY: + return POWER_SUPPLY_HEALTH_DEAD; + case MAX77759_CHGR_BAT_DTLS_BAT_CHG_TIMER_FAULT: + return POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; + case MAX77759_CHGR_BAT_DTLS_BAT_OKAY: + case MAX77759_CHGR_BAT_DTLS_BAT_ONLY_MODE: + return POWER_SUPPLY_HEALTH_GOOD; + case MAX77759_CHGR_BAT_DTLS_BAT_UNDERVOLTAGE: + return POWER_SUPPLY_HEALTH_UNDERVOLTAGE; + case MAX77759_CHGR_BAT_DTLS_BAT_OVERVOLTAGE: + return POWER_SUPPLY_HEALTH_OVERVOLTAGE; + case MAX77759_CHGR_BAT_DTLS_BAT_OVERCURRENT: + return POWER_SUPPLY_HEALTH_OVERCURRENT; + default: + break; + } + + return POWER_SUPPLY_HEALTH_UNKNOWN; +} + +static int get_health(struct max77759_charger *chg) +{ + int ret; + + ret = get_online(chg); + if (ret < 0) + return ret; + + if (ret) { + ret = get_chg_health(chg); + if (ret < 0 || ret != POWER_SUPPLY_HEALTH_GOOD) + return ret; + } + + return get_batt_health(chg); +} + +static int get_fast_charge_current(struct max77759_charger *chg) +{ + u32 regval, val; + int ret; + + ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_02, ®val); + if (ret) + return ret; + + regval = FIELD_GET(MAX77759_CHGR_REG_CHG_CNFG_02_CHGCC, regval); + ret = linear_range_get_value_array(chgcc_limit_ranges, + ARRAY_SIZE(chgcc_limit_ranges), + regval, &val); + return ret ? ret : val; +} + +static int set_fast_charge_current_limit(struct max77759_charger *chg, + u32 cc_max_ua) +{ + bool found; + u32 regval; + + linear_range_get_selector_high_array(chgcc_limit_ranges, + ARRAY_SIZE(chgcc_limit_ranges), + cc_max_ua, ®val, &found); + if (!found) + return -EINVAL; + + return regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_02, + MAX77759_CHGR_REG_CHG_CNFG_02_CHGCC, regval); +} + +static int get_float_voltage(struct max77759_charger *chg) +{ + u32 regval, val; + int ret; + + ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_04, ®val); + if (ret) + return ret; + + regval = FIELD_GET(MAX77759_CHGR_REG_CHG_CNFG_04_CHG_CV_PRM, regval); + ret = linear_range_get_value_array(chg_cv_prm_ranges, + ARRAY_SIZE(chg_cv_prm_ranges), + regval, &val); + + return ret ? ret : val; +} + +static int set_float_voltage_limit(struct max77759_charger *chg, u32 fv_mv) +{ + u32 regval; + bool found; + + linear_range_get_selector_high_array(chg_cv_prm_ranges, + ARRAY_SIZE(chg_cv_prm_ranges), + fv_mv, ®val, &found); + if (!found) + return -EINVAL; + + return regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_04, + MAX77759_CHGR_REG_CHG_CNFG_04_CHG_CV_PRM, + regval); +} + +static int get_input_current_limit(struct max77759_charger *chg) +{ + u32 regval, val; + int ret; + + ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_09, ®val); + if (ret) + return ret; + + regval = FIELD_GET(MAX77759_CHGR_REG_CHG_CNFG_09_CHGIN_ILIM, regval); + regval = umax(regval, chgin_ilim_ranges[0].min_sel); + + ret = linear_range_get_value_array(chgin_ilim_ranges, + ARRAY_SIZE(chgin_ilim_ranges), + regval, &val); + + return ret ? ret : val; +} + +static int set_input_current_limit(struct max77759_charger *chg, int ilim_ua) +{ + u32 regval; + + if (ilim_ua < 0) + return -EINVAL; + + linear_range_get_selector_within(chgin_ilim_ranges, ilim_ua, ®val); + + return regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_09, + MAX77759_CHGR_REG_CHG_CNFG_09_CHGIN_ILIM, + regval); +} + +static const enum power_supply_property max77759_charger_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, +}; + +static int max77759_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *pval) +{ + struct max77759_charger *chg = power_supply_get_drvdata(psy); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = get_online(chg); + break; + case POWER_SUPPLY_PROP_PRESENT: + ret = charger_input_valid(chg); + break; + case POWER_SUPPLY_PROP_STATUS: + ret = get_status(chg); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = get_charge_type(chg); + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = get_health(chg); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + ret = get_fast_charge_current(chg); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + ret = get_float_voltage(chg); + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = get_input_current_limit(chg); + break; + default: + ret = -EINVAL; + } + + pval->intval = ret; + return ret < 0 ? ret : 0; +} + +static const struct power_supply_desc max77759_charger_desc = { + .name = "max77759-charger", + .type = POWER_SUPPLY_TYPE_USB, + .properties = max77759_charger_props, + .num_properties = ARRAY_SIZE(max77759_charger_props), + .get_property = max77759_charger_get_property, +}; + +static int charger_set_mode(struct max77759_charger *chg, + enum max77759_chgr_mode mode) +{ + int ret; + + guard(mutex)(&chg->lock); + + if (chg->mode == mode) + return 0; + + if ((mode == MAX77759_CHGR_MODE_CHG_BUCK_ON || + mode == MAX77759_CHGR_MODE_OTG_BOOST_ON) && + chg->mode != MAX77759_CHGR_MODE_OFF) { + dev_err(chg->dev, "Invalid mode transition from %d to %d\n", + chg->mode, mode); + return -EINVAL; + } + + ret = regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_00, + MAX77759_CHGR_REG_CHG_CNFG_00_MODE, mode); + if (ret) + return ret; + + chg->mode = mode; + return 0; +} + +static int enable_chgin_otg(struct regulator_dev *rdev) +{ + struct max77759_charger *chg = rdev_get_drvdata(rdev); + + return charger_set_mode(chg, MAX77759_CHGR_MODE_OTG_BOOST_ON); +} + +static int disable_chgin_otg(struct regulator_dev *rdev) +{ + struct max77759_charger *chg = rdev_get_drvdata(rdev); + + return charger_set_mode(chg, MAX77759_CHGR_MODE_OFF); +} + +static int chgin_otg_status(struct regulator_dev *rdev) +{ + struct max77759_charger *chg = rdev_get_drvdata(rdev); + + guard(mutex)(&chg->lock); + + return chg->mode == MAX77759_CHGR_MODE_OTG_BOOST_ON; +} + +static const struct regulator_ops chgin_otg_reg_ops = { + .enable = enable_chgin_otg, + .disable = disable_chgin_otg, + .is_enabled = chgin_otg_status, +}; + +static const struct regulator_desc chgin_otg_reg_desc = { + .name = "chgin-otg", + .of_match = of_match_ptr("chgin-otg-regulator"), + .owner = THIS_MODULE, + .ops = &chgin_otg_reg_ops, + .fixed_uV = 5000000, + .n_voltages = 1, +}; + +static irqreturn_t irq_handler(int irq, void *data) +{ + struct max77759_charger *chg = data; + + power_supply_changed(chg->psy); + + return IRQ_HANDLED; +} + +static irqreturn_t bat_oilo_irq_handler(int irq, void *data) +{ + struct max77759_charger *chg = data; + + dev_warn_ratelimited(chg->dev, + "Battery over-current threshold crossed\n"); + + return irq_handler(irq, data); +} + +static int max77759_init_irqhandler(struct max77759_charger *chg) +{ + struct device *dev = chg->dev; + irq_handler_t thread_fn; + char *name; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(chgr_irqs_str); i++) { + ret = platform_get_irq_byname(to_platform_device(dev), + chgr_irqs_str[i]); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to get irq resource for %s\n", + chgr_irqs_str[i]); + + chg->irqs[i] = ret; + name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", dev_name(dev), + chgr_irqs_str[i]); + if (!name) + return dev_err_probe(dev, -ENOMEM, + "Failed to allocate space for irqname: %s\n", + chgr_irqs_str[i]); + + if (i == BAT_OILO) + thread_fn = bat_oilo_irq_handler; + else + thread_fn = irq_handler; + + ret = devm_request_threaded_irq(dev, chg->irqs[i], NULL, + thread_fn, 0, name, chg); + if (ret) + return dev_err_probe(dev, ret, + "Unable to register irq handler for %s\n", + chgr_irqs_str[i]); + } + + return 0; +} + +static int max77759_charger_init(struct max77759_charger *chg) +{ + struct power_supply_battery_info *info; + u32 regval, fast_chg_curr, fv; + int ret; + + ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_00, ®val); + if (ret) + return ret; + + chg->mode = FIELD_GET(MAX77759_CHGR_REG_CHG_CNFG_00_MODE, regval); + ret = charger_set_mode(chg, MAX77759_CHGR_MODE_OFF); + if (ret) + return ret; + + if (power_supply_get_battery_info(chg->psy, &info)) { + fv = CHG_FV_DEFAULT_MV; + fast_chg_curr = CHG_CC_DEFAULT_UA; + } else { + fv = info->constant_charge_voltage_max_uv / 1000; + fast_chg_curr = info->constant_charge_current_max_ua; + } + + ret = set_fast_charge_current_limit(chg, fast_chg_curr); + if (ret) + return ret; + + ret = set_float_voltage_limit(chg, fv); + if (ret) + return ret; + + ret = unlock_prot_regs(chg, true); + if (ret) + return ret; + + /* Disable wireless charging input */ + ret = regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_12, + MAX77759_CHGR_REG_CHG_CNFG_12_WCINSEL, 0); + if (ret) + goto relock; + + ret = regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_18, + MAX77759_CHGR_REG_CHG_CNFG_18_WDTEN, 0); + if (ret) + goto relock; + + return unlock_prot_regs(chg, false); + +relock: + (void)unlock_prot_regs(chg, false); + return ret; +} + +static void psy_work_item(struct work_struct *work) +{ + struct max77759_charger *chg = + container_of(work, struct max77759_charger, psy_work.work); + union power_supply_propval current_limit, online; + int ret; + + ret = power_supply_get_property(chg->tcpm_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, + ¤t_limit); + if (ret) { + dev_err(chg->dev, + "Failed to get CURRENT_MAX psy property, ret=%d\n", + ret); + goto err; + } + + ret = power_supply_get_property(chg->tcpm_psy, POWER_SUPPLY_PROP_ONLINE, + &online); + if (ret) { + dev_err(chg->dev, + "Failed to get ONLINE psy property, ret=%d\n", + ret); + goto err; + } + + if (online.intval && current_limit.intval) { + ret = set_input_current_limit(chg, current_limit.intval); + if (ret) { + dev_err(chg->dev, + "Unable to set current limit, ret=%d\n", ret); + goto err; + } + + charger_set_mode(chg, MAX77759_CHGR_MODE_CHG_BUCK_ON); + } else { + charger_set_mode(chg, MAX77759_CHGR_MODE_OFF); + } + + scoped_guard(mutex, &chg->retry_lock) { + if (chg->psy_work_retry_cnt) + dev_dbg(chg->dev, + "chg psy_work succeeded after %u tries\n", + chg->psy_work_retry_cnt); + chg->psy_work_retry_cnt = 0; + } + + return; + +err: + charger_set_mode(chg, MAX77759_CHGR_MODE_OFF); + scoped_guard(mutex, &chg->retry_lock) { + if (chg->psy_work_retry_cnt >= MAX_NUM_RETRIES) { + dev_err(chg->dev, "chg psy work failed, giving up\n"); + return; + } + + ++chg->psy_work_retry_cnt; + dev_dbg(chg->dev, "Retrying %u/%u chg psy_work\n", + chg->psy_work_retry_cnt, MAX_NUM_RETRIES); + schedule_delayed_work(&chg->psy_work, + msecs_to_jiffies(PSY_WORK_RETRY_DELAY_MS)); + } +} + +static int psy_changed(struct notifier_block *nb, unsigned long evt, void *data) +{ + struct max77759_charger *chg = container_of(nb, struct max77759_charger, + nb); + static const char *psy_name = "tcpm-source"; + struct power_supply *psy = data; + + if (!strnstr(psy->desc->name, psy_name, strlen(psy_name)) || + evt != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + chg->tcpm_psy = psy; + scoped_guard(mutex, &chg->retry_lock) + chg->psy_work_retry_cnt = 0; + + schedule_delayed_work(&chg->psy_work, 0); + + return NOTIFY_OK; +} + +static void max_tcpci_unregister_psy_notifier(void *nb) +{ + power_supply_unreg_notifier(nb); +} + +static int max77759_charger_probe(struct platform_device *pdev) +{ + struct regulator_config chgin_otg_reg_cfg; + struct power_supply_config psy_cfg; + struct device *dev = &pdev->dev; + struct max77759_charger *chg; + int ret; + + device_set_of_node_from_dev(dev, dev->parent); + chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL); + if (!chg) + return -ENOMEM; + + platform_set_drvdata(pdev, chg); + chg->dev = dev; + chg->regmap = dev_get_regmap(dev->parent, "charger"); + if (!chg->regmap) + return dev_err_probe(dev, -ENODEV, "Missing regmap\n"); + + ret = devm_mutex_init(dev, &chg->lock); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize lock\n"); + + ret = devm_mutex_init(dev, &chg->retry_lock); + if (ret) + return dev_err_probe(dev, ret, + "Failed to initialize retry_lock\n"); + + psy_cfg.fwnode = dev_fwnode(dev); + psy_cfg.drv_data = chg; + chg->psy = devm_power_supply_register(dev, &max77759_charger_desc, + &psy_cfg); + if (IS_ERR(chg->psy)) + return dev_err_probe(dev, PTR_ERR(chg->psy), + "Failed to register psy\n"); + + ret = max77759_charger_init(chg); + if (ret) + return dev_err_probe(dev, ret, + "Failed to initialize max77759 charger\n"); + + chgin_otg_reg_cfg.dev = dev; + chgin_otg_reg_cfg.driver_data = chg; + chgin_otg_reg_cfg.of_node = dev_of_node(dev); + chg->chgin_otg_rdev = devm_regulator_register(dev, &chgin_otg_reg_desc, + &chgin_otg_reg_cfg); + if (IS_ERR(chg->chgin_otg_rdev)) + return dev_err_probe(dev, PTR_ERR(chg->chgin_otg_rdev), + "Failed to register chgin otg regulator\n"); + + ret = devm_delayed_work_autocancel(dev, &chg->psy_work, psy_work_item); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize psy work\n"); + + chg->nb.notifier_call = psy_changed; + ret = power_supply_reg_notifier(&chg->nb); + if (ret) + return dev_err_probe(dev, ret, + "Unable to register psy notifier\n"); + + ret = devm_add_action_or_reset(dev, max_tcpci_unregister_psy_notifier, + &chg->nb); + if (ret) + return dev_err_probe(dev, ret, + "Failed to add devm action to unregister psy notifier\n"); + + return max77759_init_irqhandler(chg); +} + +static const struct platform_device_id max77759_charger_id[] = { + { .name = "max77759-charger", }, + { } +}; +MODULE_DEVICE_TABLE(platform, max77759_charger_id); + +static struct platform_driver max77759_charger_driver = { + .driver = { + .name = "max77759-charger", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = max77759_charger_probe, + .id_table = max77759_charger_id, +}; +module_platform_driver(max77759_charger_driver); + +MODULE_AUTHOR("Amit Sunil Dhamne "); +MODULE_DESCRIPTION("Maxim MAX77759 charger driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/mt6370-charger.c b/drivers/power/supply/mt6370-charger.c index e6db961d5818..916556baa854 100644 --- a/drivers/power/supply/mt6370-charger.c +++ b/drivers/power/supply/mt6370-charger.c @@ -761,13 +761,6 @@ static int mt6370_chg_init_psy(struct mt6370_priv *priv) return PTR_ERR_OR_ZERO(priv->psy); } -static void mt6370_chg_destroy_wq(void *data) -{ - struct workqueue_struct *wq = data; - - destroy_workqueue(wq); -} - static irqreturn_t mt6370_attach_i_handler(int irq, void *data) { struct mt6370_priv *priv = data; @@ -893,14 +886,10 @@ static int mt6370_chg_probe(struct platform_device *pdev) priv->attach = MT6370_ATTACH_STAT_DETACH; - priv->wq = create_singlethread_workqueue(dev_name(priv->dev)); + priv->wq = devm_alloc_ordered_workqueue(dev, "%s", 0, dev_name(priv->dev)); if (!priv->wq) return -ENOMEM; - ret = devm_add_action_or_reset(dev, mt6370_chg_destroy_wq, priv->wq); - if (ret) - return ret; - ret = devm_work_autocancel(dev, &priv->bc12_work, mt6370_chg_bc12_work_func); if (ret) return dev_err_probe(dev, ret, "Failed to init bc12 work\n"); diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index dd3a48d72d2b..f30a7b9ccd5e 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -70,6 +70,8 @@ static const char * const POWER_SUPPLY_USB_TYPE_TEXT[] = { [POWER_SUPPLY_USB_TYPE_PD] = "PD", [POWER_SUPPLY_USB_TYPE_PD_DRP] = "PD_DRP", [POWER_SUPPLY_USB_TYPE_PD_PPS] = "PD_PPS", + [POWER_SUPPLY_USB_TYPE_PD_SPR_AVS] = "PD_SPR_AVS", + [POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS] = "PD_PPS_SPR_AVS", [POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID] = "BrickID", }; diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c index 80572ee945b4..490137a23d00 100644 --- a/drivers/power/supply/qcom_battmgr.c +++ b/drivers/power/supply/qcom_battmgr.c @@ -1611,6 +1611,8 @@ static void qcom_battmgr_pdr_notify(void *priv, int state) } static const struct of_device_id qcom_battmgr_of_variants[] = { + { .compatible = "qcom,glymur-pmic-glink", .data = (void *)QCOM_BATTMGR_X1E80100 }, + { .compatible = "qcom,kaanapali-pmic-glink", .data = (void *)QCOM_BATTMGR_SM8550 }, { .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP }, { .compatible = "qcom,sc8280xp-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP }, { .compatible = "qcom,sm8550-pmic-glink", .data = (void *)QCOM_BATTMGR_SM8550 }, diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c index b1cb925581ec..bf2e2ccc454a 100644 --- a/drivers/power/supply/qcom_smbx.c +++ b/drivers/power/supply/qcom_smbx.c @@ -134,6 +134,9 @@ #define OCP_CHARGER_BIT BIT(1) #define SDP_CHARGER_BIT BIT(0) +#define USBIN_CMD_IL 0x340 +#define USBIN_SUSPEND_BIT BIT(0) + #define TYPE_C_STATUS_1 0x30B #define UFP_TYPEC_MASK GENMASK(7, 5) #define UFP_TYPEC_RDSTD_BIT BIT(7) @@ -693,6 +696,9 @@ static int smb_set_property(struct power_supply *psy, struct smb_chip *chip = power_supply_get_drvdata(psy); switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + return regmap_update_bits(chip->regmap, chip->base + USBIN_CMD_IL, + USBIN_SUSPEND_BIT, !val->intval); case POWER_SUPPLY_PROP_CURRENT_MAX: return smb_set_current_limit(chip, val->intval); default: @@ -705,6 +711,7 @@ static int smb_property_is_writable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { + case POWER_SUPPLY_PROP_STATUS: case POWER_SUPPLY_PROP_CURRENT_MAX: return 1; default: diff --git a/drivers/power/supply/s2mu005-battery.c b/drivers/power/supply/s2mu005-battery.c new file mode 100644 index 000000000000..64c57a14ef7d --- /dev/null +++ b/drivers/power/supply/s2mu005-battery.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Battery Fuel Gauge Driver for Samsung S2MU005 PMIC. + * + * Copyright (C) 2015 Samsung Electronics + * Copyright (C) 2023 Yassine Oudjana + * Copyright (C) 2025 Kaustabh Chakraborty + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define S2MU005_FG_REG_STATUS 0x00 +#define S2MU005_FG_REG_IRQ 0x02 +#define S2MU005_FG_REG_RVBAT 0x04 +#define S2MU005_FG_REG_RCURCC 0x06 +#define S2MU005_FG_REG_RSOC 0x08 +#define S2MU005_FG_REG_MONOUT 0x0a +#define S2MU005_FG_REG_MONOUTSEL 0x0c +#define S2MU005_FG_REG_RBATCAP 0x0e +#define S2MU005_FG_REG_RZADJ 0x12 +#define S2MU005_FG_REG_RBATZ0 0x16 +#define S2MU005_FG_REG_RBATZ1 0x18 +#define S2MU005_FG_REG_IRQLVL 0x1a +#define S2MU005_FG_REG_START 0x1e + +#define S2MU005_FG_MONOUTSEL_AVGCURRENT 0x26 +#define S2MU005_FG_MONOUTSEL_AVGVOLTAGE 0x27 + +struct s2mu005_fg { + struct device *dev; + struct regmap *regmap; + struct power_supply *psy; + struct mutex monout_mutex; +}; + +static const struct regmap_config s2mu005_fg_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static irqreturn_t s2mu005_handle_irq(int irq, void *data) +{ + struct s2mu005_fg *priv = data; + + msleep(100); + power_supply_changed(priv->psy); + + return IRQ_HANDLED; +} + +static int s2mu005_fg_get_voltage_now(struct s2mu005_fg *priv, int *value) +{ + struct regmap *regmap = priv->regmap; + u32 val; + int ret; + + ret = regmap_read(regmap, S2MU005_FG_REG_RVBAT, &val); + if (ret < 0) { + dev_err(priv->dev, "failed to read voltage register (%d)\n", ret); + return ret; + } + + *value = (val * MICRO) >> 13; + + return 0; +} + +static int s2mu005_fg_get_voltage_avg(struct s2mu005_fg *priv, int *value) +{ + struct regmap *regmap = priv->regmap; + u32 val; + int ret; + + mutex_lock(&priv->monout_mutex); + + ret = regmap_write(regmap, S2MU005_FG_REG_MONOUTSEL, + S2MU005_FG_MONOUTSEL_AVGVOLTAGE); + if (ret < 0) { + dev_err(priv->dev, "failed to enable average voltage monitoring (%d)\n", + ret); + goto unlock; + } + + ret = regmap_read(regmap, S2MU005_FG_REG_MONOUT, &val); + if (ret < 0) { + dev_err(priv->dev, "failed to read current register (%d)\n", ret); + goto unlock; + } + + *value = (val * MICRO) >> 12; + +unlock: + mutex_unlock(&priv->monout_mutex); + + return ret; +} +static int s2mu005_fg_get_current_now(struct s2mu005_fg *priv, int *value) +{ + struct regmap *regmap = priv->regmap; + u32 val; + int ret; + + ret = regmap_read(regmap, S2MU005_FG_REG_RCURCC, &val); + if (ret < 0) { + dev_err(priv->dev, "failed to read current register (%d)\n", ret); + return ret; + } + + *value = -((s16)val * MICRO) >> 12; + + return 0; +} + +static int s2mu005_fg_get_current_avg(struct s2mu005_fg *priv, int *value) +{ + struct regmap *regmap = priv->regmap; + u32 val; + int ret; + + mutex_lock(&priv->monout_mutex); + + ret = regmap_write(regmap, S2MU005_FG_REG_MONOUTSEL, + S2MU005_FG_MONOUTSEL_AVGCURRENT); + if (ret < 0) { + dev_err(priv->dev, "failed to enable average current monitoring (%d)\n", + ret); + goto unlock; + } + + ret = regmap_read(regmap, S2MU005_FG_REG_MONOUT, &val); + if (ret < 0) { + dev_err(priv->dev, "failed to read current register (%d)\n", ret); + goto unlock; + } + + *value = -((s16)val * MICRO) >> 12; + +unlock: + mutex_unlock(&priv->monout_mutex); + + return ret; +} + +static int s2mu005_fg_get_capacity(struct s2mu005_fg *priv, int *value) +{ + struct regmap *regmap = priv->regmap; + u32 val; + int ret; + + ret = regmap_read(regmap, S2MU005_FG_REG_RSOC, &val); + if (ret < 0) { + dev_err(priv->dev, "failed to read capacity register (%d)\n", ret); + return ret; + } + + *value = (val * CENTI) >> 14; + + return 0; +} + +static int s2mu005_fg_get_status(struct s2mu005_fg *priv, int *value) +{ + int current_now, current_avg, capacity; + int ret; + + ret = s2mu005_fg_get_current_now(priv, ¤t_now); + if (ret < 0) + return ret; + + ret = s2mu005_fg_get_current_avg(priv, ¤t_avg); + if (ret < 0) + return ret; + + /* + * Verify both current values reported to reduce inaccuracies due to + * internal hysteresis. + */ + if (current_now < 0 && current_avg < 0) { + *value = POWER_SUPPLY_STATUS_DISCHARGING; + } else if (current_now == 0) { + *value = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + *value = POWER_SUPPLY_STATUS_CHARGING; + + ret = s2mu005_fg_get_capacity(priv, &capacity); + if (!ret && capacity > 98) + *value = POWER_SUPPLY_STATUS_FULL; + return ret; + } + + return 0; +} + +static const enum power_supply_property s2mu005_fg_properties[] = { + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_STATUS, +}; + +static int s2mu005_fg_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct s2mu005_fg *priv = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + return s2mu005_fg_get_voltage_now(priv, &val->intval); + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + return s2mu005_fg_get_voltage_avg(priv, &val->intval); + case POWER_SUPPLY_PROP_CURRENT_NOW: + return s2mu005_fg_get_current_now(priv, &val->intval); + case POWER_SUPPLY_PROP_CURRENT_AVG: + return s2mu005_fg_get_current_avg(priv, &val->intval); + case POWER_SUPPLY_PROP_CAPACITY: + return s2mu005_fg_get_capacity(priv, &val->intval); + case POWER_SUPPLY_PROP_STATUS: + return s2mu005_fg_get_status(priv, &val->intval); + default: + return -EINVAL; + } +} + +static const struct power_supply_desc s2mu005_fg_desc = { + .name = "s2mu005-fuel-gauge", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = s2mu005_fg_properties, + .num_properties = ARRAY_SIZE(s2mu005_fg_properties), + .get_property = s2mu005_fg_get_property, +}; + +static int s2mu005_fg_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct s2mu005_fg *priv; + struct power_supply_config psy_cfg = {}; + const struct power_supply_desc *psy_desc; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + priv->dev = dev; + + priv->regmap = devm_regmap_init_i2c(client, &s2mu005_fg_regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), + "failed to initialize regmap\n"); + + ret = devm_mutex_init(dev, &priv->monout_mutex); + if (ret) + dev_err_probe(dev, ret, "failed to initialize MONOUT mutex\n"); + + psy_desc = device_get_match_data(dev); + + psy_cfg.drv_data = priv; + psy_cfg.fwnode = dev_fwnode(dev); + priv->psy = devm_power_supply_register(priv->dev, psy_desc, &psy_cfg); + if (IS_ERR(priv->psy)) + return dev_err_probe(dev, PTR_ERR(priv->psy), + "failed to register power supply subsystem\n"); + + ret = devm_request_threaded_irq(priv->dev, client->irq, NULL, + s2mu005_handle_irq, IRQF_ONESHOT, + psy_desc->name, priv); + if (ret) + dev_err_probe(dev, ret, "failed to request IRQ\n"); + + return 0; +} + +static const struct of_device_id s2mu005_fg_of_match_table[] = { + { + .compatible = "samsung,s2mu005-fuel-gauge", + .data = &s2mu005_fg_desc, + }, + { } +}; +MODULE_DEVICE_TABLE(of, s2mu005_fg_of_match_table); + +static struct i2c_driver s2mu005_fg_i2c_driver = { + .probe = s2mu005_fg_i2c_probe, + .driver = { + .name = "s2mu005-fuel-gauge", + .of_match_table = s2mu005_fg_of_match_table, + }, +}; +module_i2c_driver(s2mu005_fg_i2c_driver); + +MODULE_DESCRIPTION("Samsung S2MU005 PMIC Battery Fuel Gauge Driver"); +MODULE_AUTHOR("Yassine Oudjana "); +MODULE_AUTHOR("Kaustabh Chakraborty "); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/sbs-manager.c b/drivers/power/supply/sbs-manager.c index 6fe526222f7f..343ad4ab4082 100644 --- a/drivers/power/supply/sbs-manager.c +++ b/drivers/power/supply/sbs-manager.c @@ -199,7 +199,7 @@ static int sbsm_gpio_get_value(struct gpio_chip *gc, unsigned int off) if (ret < 0) return ret; - return ret & BIT(off); + return !!(ret & BIT(off)); } /* diff --git a/drivers/power/supply/twl4030_madc_battery.c b/drivers/power/supply/twl4030_madc_battery.c index 3935162e350b..a99b3ff26929 100644 --- a/drivers/power/supply/twl4030_madc_battery.c +++ b/drivers/power/supply/twl4030_madc_battery.c @@ -11,9 +11,7 @@ */ #include -#include #include -#include #include #include #include diff --git a/drivers/pps/generators/pps_gen.c b/drivers/pps/generators/pps_gen.c index 7143c003366c..5e207c75e340 100644 --- a/drivers/pps/generators/pps_gen.c +++ b/drivers/pps/generators/pps_gen.c @@ -26,7 +26,10 @@ */ static dev_t pps_gen_devt; -static struct class *pps_gen_class; +static const struct class pps_gen_class = { + .name = "pps-gen", + .dev_groups = pps_gen_groups +}; static DEFINE_IDA(pps_gen_ida); @@ -183,7 +186,7 @@ static int pps_gen_register_cdev(struct pps_gen_device *pps_gen) MAJOR(pps_gen_devt), pps_gen->id); goto free_ida; } - pps_gen->dev = device_create(pps_gen_class, pps_gen->info->parent, devt, + pps_gen->dev = device_create(&pps_gen_class, pps_gen->info->parent, devt, pps_gen, "pps-gen%d", pps_gen->id); if (IS_ERR(pps_gen->dev)) { err = PTR_ERR(pps_gen->dev); @@ -207,7 +210,7 @@ free_ida: static void pps_gen_unregister_cdev(struct pps_gen_device *pps_gen) { pr_debug("unregistering pps-gen%d\n", pps_gen->id); - device_destroy(pps_gen_class, pps_gen->dev->devt); + device_destroy(&pps_gen_class, pps_gen->dev->devt); } /* @@ -307,7 +310,7 @@ EXPORT_SYMBOL(pps_gen_event); static void __exit pps_gen_exit(void) { - class_destroy(pps_gen_class); + class_unregister(&pps_gen_class); unregister_chrdev_region(pps_gen_devt, PPS_GEN_MAX_SOURCES); } @@ -315,12 +318,11 @@ static int __init pps_gen_init(void) { int err; - pps_gen_class = class_create("pps-gen"); - if (IS_ERR(pps_gen_class)) { - pr_err("failed to allocate class\n"); - return PTR_ERR(pps_gen_class); + err = class_register(&pps_gen_class); + if (err) { + pr_err("failed to register class\n"); + return err; } - pps_gen_class->dev_groups = pps_gen_groups; err = alloc_chrdev_region(&pps_gen_devt, 0, PPS_GEN_MAX_SOURCES, "pps-gen"); @@ -332,7 +334,7 @@ static int __init pps_gen_init(void) return 0; remove_class: - class_destroy(pps_gen_class); + class_unregister(&pps_gen_class); return err; } diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index c6b8b6478276..de1122bb69ea 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -26,7 +26,10 @@ */ static int pps_major; -static struct class *pps_class; +static const struct class pps_class = { + .name = "pps", + .dev_groups = pps_groups +}; static DEFINE_MUTEX(pps_idr_lock); static DEFINE_IDR(pps_idr); @@ -379,7 +382,7 @@ int pps_register_cdev(struct pps_device *pps) } pps->id = err; - pps->dev.class = pps_class; + pps->dev.class = &pps_class; pps->dev.parent = pps->info.dev; pps->dev.devt = MKDEV(pps_major, pps->id); dev_set_drvdata(&pps->dev, pps); @@ -408,7 +411,7 @@ void pps_unregister_cdev(struct pps_device *pps) { pr_debug("unregistering pps%d\n", pps->id); pps->lookup_cookie = NULL; - device_destroy(pps_class, pps->dev.devt); + device_destroy(&pps_class, pps->dev.devt); /* Now we can release the ID for re-use */ mutex_lock(&pps_idr_lock); @@ -460,18 +463,19 @@ EXPORT_SYMBOL(pps_lookup_dev); static void __exit pps_exit(void) { - class_destroy(pps_class); + class_unregister(&pps_class); __unregister_chrdev(pps_major, 0, PPS_MAX_SOURCES, "pps"); } static int __init pps_init(void) { - pps_class = class_create("pps"); - if (IS_ERR(pps_class)) { - pr_err("failed to allocate class\n"); - return PTR_ERR(pps_class); + int err; + + err = class_register(&pps_class); + if (err) { + pr_err("failed to register class\n"); + return err; } - pps_class->dev_groups = pps_groups; pps_major = __register_chrdev(0, 0, PPS_MAX_SOURCES, "pps", &pps_cdev_fops); @@ -487,7 +491,7 @@ static int __init pps_init(void) return 0; remove_class: - class_destroy(pps_class); + class_unregister(&pps_class); return pps_major; } diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index f9ff78ba122d..3d30aeab507e 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -50,6 +50,8 @@ struct atmel_tcb_pwm_chip { spinlock_t lock; u8 channel; u8 width; + unsigned long rate; + unsigned long slow_rate; struct regmap *regmap; struct clk *clk; struct clk *gclk; @@ -266,7 +268,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int slowclk = 0; unsigned period; unsigned duty; - unsigned rate = clk_get_rate(tcbpwmc->clk); + unsigned long rate = tcbpwmc->rate; unsigned long long min; unsigned long long max; @@ -294,7 +296,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, */ if (i == ARRAY_SIZE(atmel_tcb_divisors)) { i = slowclk; - rate = clk_get_rate(tcbpwmc->slow_clk); + rate = tcbpwmc->slow_rate; min = div_u64(NSEC_PER_SEC, rate); max = min << tcbpwmc->width; @@ -431,24 +433,49 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) } chip->ops = &atmel_tcb_pwm_ops; + chip->atomic = true; tcbpwmc->channel = channel; tcbpwmc->width = config->counter_width; - err = clk_prepare_enable(tcbpwmc->slow_clk); + err = clk_prepare_enable(tcbpwmc->clk); if (err) goto err_gclk; + err = clk_prepare_enable(tcbpwmc->slow_clk); + if (err) + goto err_disable_clk;; + + err = clk_rate_exclusive_get(tcbpwmc->clk); + if (err) + goto err_disable_slow_clk; + + err = clk_rate_exclusive_get(tcbpwmc->slow_clk); + if (err) + goto err_clk_unlock; + + tcbpwmc->rate = clk_get_rate(tcbpwmc->clk); + tcbpwmc->slow_rate = clk_get_rate(tcbpwmc->slow_clk); + spin_lock_init(&tcbpwmc->lock); err = pwmchip_add(chip); if (err < 0) - goto err_disable_clk; + goto err_slow_clk_unlock; platform_set_drvdata(pdev, chip); return 0; +err_slow_clk_unlock: + clk_rate_exclusive_put(tcbpwmc->slow_clk); + +err_clk_unlock: + clk_rate_exclusive_put(tcbpwmc->clk); + err_disable_clk: + clk_disable_unprepare(tcbpwmc->clk); + +err_disable_slow_clk: clk_disable_unprepare(tcbpwmc->slow_clk); err_gclk: @@ -470,6 +497,9 @@ static void atmel_tcb_pwm_remove(struct platform_device *pdev) pwmchip_remove(chip); + clk_rate_exclusive_put(tcbpwmc->slow_clk); + clk_rate_exclusive_put(tcbpwmc->clk); + clk_disable_unprepare(tcbpwmc->clk); clk_disable_unprepare(tcbpwmc->slow_clk); clk_put(tcbpwmc->gclk); clk_put(tcbpwmc->clk); diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 2594fb771b04..935257a890b0 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -68,7 +68,7 @@ static int stm32_pwm_round_waveform_tohw(struct pwm_chip *chip, struct stm32_pwm *priv = to_stm32_pwm_dev(chip); unsigned int ch = pwm->hwpwm; unsigned long rate; - u64 ccr, duty; + u64 duty_ticks, offset_ticks; int ret; if (wf->period_length_ns == 0) { @@ -164,23 +164,25 @@ static int stm32_pwm_round_waveform_tohw(struct pwm_chip *chip, wfhw->arr = min_t(u64, arr, priv->max_arr) - 1; } - duty = mul_u64_u64_div_u64(wf->duty_length_ns, rate, - (u64)NSEC_PER_SEC * (wfhw->psc + 1)); - duty = min_t(u64, duty, wfhw->arr + 1); + duty_ticks = mul_u64_u64_div_u64(wf->duty_length_ns, rate, + (u64)NSEC_PER_SEC * (wfhw->psc + 1)); + duty_ticks = min_t(u64, duty_ticks, wfhw->arr + 1); - if (wf->duty_length_ns && wf->duty_offset_ns && - wf->duty_length_ns + wf->duty_offset_ns >= wf->period_length_ns) { + offset_ticks = mul_u64_u64_div_u64(wf->duty_offset_ns, rate, + (u64)NSEC_PER_SEC * (wfhw->psc + 1)); + offset_ticks = min_t(u64, offset_ticks, wfhw->arr + 1); + + if (duty_ticks && offset_ticks && + duty_ticks + offset_ticks >= wfhw->arr + 1) { wfhw->ccer |= TIM_CCER_CCxP(ch + 1); if (priv->have_complementary_output) wfhw->ccer |= TIM_CCER_CCxNP(ch + 1); - ccr = wfhw->arr + 1 - duty; + wfhw->ccr = wfhw->arr + 1 - duty_ticks; } else { - ccr = duty; + wfhw->ccr = duty_ticks; } - wfhw->ccr = min_t(u64, ccr, wfhw->arr + 1); - out: dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] @%lu -> CCER: %08x, PSC: %08x, ARR: %08x, CCR: %08x\n", pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 10e64e3ffb1f..d71dac9436e3 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1173,7 +1173,7 @@ config REGULATOR_QCOM_RPM "qcom_rpm-regulator". config REGULATOR_QCOM_RPMH - tristate "Qualcomm Technologies, Inc. RPMh regulator driver" + tristate "Qualcomm RPMh regulator driver" depends on QCOM_RPMH || (QCOM_RPMH=n && COMPILE_TEST) depends on QCOM_COMMAND_DB || (QCOM_COMMAND_DB=n && COMPILE_TEST) help @@ -1231,6 +1231,7 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY tristate "Raspberry Pi 7-inch touchscreen panel ATTINY regulator" depends on ARM || ARM64 || COMPILE_TEST depends on BACKLIGHT_CLASS_DEVICE + depends on GPIOLIB depends on I2C select REGMAP_I2C help @@ -1877,7 +1878,7 @@ config REGULATOR_WM8994 WM8994 CODEC. config REGULATOR_QCOM_LABIBB - tristate "QCOM LAB/IBB regulator support" + tristate "Qualcomm LAB/IBB regulator support" depends on SPMI || COMPILE_TEST help This driver supports Qualcomm's LAB/IBB regulators present on the diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 6e4cb2871fca..0dcb50bf5c35 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1512,7 +1512,7 @@ static const struct rpmh_vreg_init_data pmh0101_vreg_data[] = { RPMH_VREG("ldo13", LDO, 13, &pmic5_pldo530_mvp150, "vdd-l2-l13-l14"), RPMH_VREG("ldo14", LDO, 14, &pmic5_pldo530_mvp150, "vdd-l2-l13-l14"), RPMH_VREG("ldo15", LDO, 15, &pmic5_nldo530, "vdd-l15"), - RPMH_VREG("ldo16", LDO, 15, &pmic5_pldo530_mvp600, "vdd-l5-l16"), + RPMH_VREG("ldo16", LDO, 16, &pmic5_pldo530_mvp600, "vdd-l5-l16"), RPMH_VREG("ldo17", LDO, 17, &pmic5_pldo515_mv, "vdd-l17"), RPMH_VREG("ldo18", LDO, 18, &pmic5_nldo530, "vdd-l18"), RPMH_VREG("bob1", BOB, 1, &pmic5_bob, "vdd-bob1"), diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c index e418a2bf5d2e..23fca7176539 100644 --- a/drivers/remoteproc/da8xx_remoteproc.c +++ b/drivers/remoteproc/da8xx_remoteproc.c @@ -57,11 +57,10 @@ struct da8xx_rproc_mem { * @mem: internal memory regions data * @num_mems: number of internal memory regions * @dsp_clk: placeholder for platform's DSP clk - * @ack_fxn: chip-specific ack function for ack'ing irq + * @dsp_reset: control for local reset * @irq_data: ack_fxn function parameter * @chipsig: virt ptr to DSP interrupt registers (CHIPSIG & CHIPSIG_CLR) * @bootreg: virt ptr to DSP boot address register (HOST1CFG) - * @irq: irq # used by this instance */ struct da8xx_rproc { struct rproc *rproc; @@ -69,11 +68,9 @@ struct da8xx_rproc { int num_mems; struct clk *dsp_clk; struct reset_control *dsp_reset; - void (*ack_fxn)(struct irq_data *data); struct irq_data *irq_data; void __iomem *chipsig; void __iomem *bootreg; - int irq; }; /** @@ -122,7 +119,7 @@ static irqreturn_t da8xx_rproc_callback(int irq, void *p) * we need to ack it after taking down the level else we'll * be called again immediately after returning. */ - drproc->ack_fxn(drproc->irq_data); + drproc->irq_data->chip->irq_ack(drproc->irq_data); return IRQ_WAKE_THREAD; } @@ -245,45 +242,9 @@ static int da8xx_rproc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct da8xx_rproc *drproc; struct rproc *rproc; - struct irq_data *irq_data; - struct clk *dsp_clk; - struct reset_control *dsp_reset; - void __iomem *chipsig; - void __iomem *bootreg; int irq; int ret; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - irq_data = irq_get_irq_data(irq); - if (!irq_data) - return dev_err_probe(dev, -EINVAL, "irq_get_irq_data(%d): NULL\n", irq); - - bootreg = devm_platform_ioremap_resource_byname(pdev, "host1cfg"); - if (IS_ERR(bootreg)) - return PTR_ERR(bootreg); - - chipsig = devm_platform_ioremap_resource_byname(pdev, "chipsig"); - if (IS_ERR(chipsig)) - return PTR_ERR(chipsig); - - dsp_clk = devm_clk_get(dev, NULL); - if (IS_ERR(dsp_clk)) - return dev_err_probe(dev, PTR_ERR(dsp_clk), "clk_get error\n"); - - dsp_reset = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(dsp_reset)) - return dev_err_probe(dev, PTR_ERR(dsp_reset), "unable to get reset control\n"); - - if (dev->of_node) { - ret = of_reserved_mem_device_init(dev); - if (ret) - return dev_err_probe(dev, ret, "device does not have specific CMA pool\n"); - devm_add_action_or_reset(&pdev->dev, da8xx_rproc_mem_release, &pdev->dev); - } - rproc = devm_rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name, sizeof(*drproc)); if (!rproc) @@ -294,43 +255,63 @@ static int da8xx_rproc_probe(struct platform_device *pdev) drproc = rproc->priv; drproc->rproc = rproc; - drproc->dsp_clk = dsp_clk; - drproc->dsp_reset = dsp_reset; rproc->has_iommu = false; + drproc->dsp_clk = devm_clk_get(dev, NULL); + if (IS_ERR(drproc->dsp_clk)) + return dev_err_probe(dev, PTR_ERR(drproc->dsp_clk), "clk_get error\n"); + + drproc->dsp_reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(drproc->dsp_reset)) + return dev_err_probe(dev, PTR_ERR(drproc->dsp_reset), + "unable to get reset control\n"); + + if (dev->of_node) { + ret = of_reserved_mem_device_init(dev); + if (ret) + return dev_err_probe(dev, ret, "device does not have specific CMA pool\n"); + devm_add_action_or_reset(&pdev->dev, da8xx_rproc_mem_release, &pdev->dev); + } + ret = da8xx_rproc_get_internal_memories(pdev, drproc); if (ret) return ret; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + drproc->irq_data = irq_get_irq_data(irq); + if (!drproc->irq_data) + return dev_err_probe(dev, -EINVAL, "irq_get_irq_data(%d): NULL\n", irq); + + drproc->chipsig = devm_platform_ioremap_resource_byname(pdev, "chipsig"); + if (IS_ERR(drproc->chipsig)) + return PTR_ERR(drproc->chipsig); + + drproc->bootreg = devm_platform_ioremap_resource_byname(pdev, "host1cfg"); + if (IS_ERR(drproc->bootreg)) + return PTR_ERR(drproc->bootreg); + /* everything the ISR needs is now setup, so hook it up */ ret = devm_request_threaded_irq(dev, irq, da8xx_rproc_callback, handle_event, 0, "da8xx-remoteproc", rproc); - if (ret) { - dev_err(dev, "devm_request_threaded_irq error: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "devm_request_threaded_irq error\n"); /* * rproc_add() can end up enabling the DSP's clk with the DSP * *not* in reset, but da8xx_rproc_start() needs the DSP to be * held in reset at the time it is called. */ - ret = reset_control_assert(dsp_reset); + ret = reset_control_assert(drproc->dsp_reset); if (ret) return ret; - drproc->chipsig = chipsig; - drproc->bootreg = bootreg; - drproc->ack_fxn = irq_data->chip->irq_ack; - drproc->irq_data = irq_data; - drproc->irq = irq; - ret = devm_rproc_add(dev, rproc); - if (ret) { - dev_err(dev, "rproc_add failed: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "rproc_add failed\n"); return 0; } diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 8c8ddbf995a4..0dd80e688b0e 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -812,7 +812,7 @@ static int imx_rproc_addr_init(struct imx_rproc *priv, /* Not use resource version, because we might share region */ priv->mem[b].cpu_addr = devm_ioremap_resource_wc(&pdev->dev, &res); - if (!priv->mem[b].cpu_addr) { + if (IS_ERR(priv->mem[b].cpu_addr)) { dev_err(dev, "failed to remap %pr\n", &res); return -ENOMEM; } @@ -1007,7 +1007,11 @@ static int imx_rproc_mmio_detect_mode(struct rproc *rproc) } priv->regmap = regmap; - regmap_attach_dev(dev, regmap, &config); + ret = regmap_attach_dev(dev, regmap, &config); + if (ret) { + dev_err(dev, "regmap attach failed\n"); + return ret; + } if (priv->gpr) { ret = regmap_read(priv->gpr, dcfg->gpr_reg, &val); diff --git a/drivers/remoteproc/keystone_remoteproc.c b/drivers/remoteproc/keystone_remoteproc.c index 4d6550b48567..e7fde5509786 100644 --- a/drivers/remoteproc/keystone_remoteproc.c +++ b/drivers/remoteproc/keystone_remoteproc.c @@ -173,35 +173,16 @@ static int keystone_rproc_start(struct rproc *rproc) INIT_WORK(&ksproc->workqueue, handle_event); - ret = request_irq(ksproc->irq_ring, keystone_rproc_vring_interrupt, 0, - dev_name(ksproc->dev), ksproc); - if (ret) { - dev_err(ksproc->dev, "failed to enable vring interrupt, ret = %d\n", - ret); - goto out; - } - - ret = request_irq(ksproc->irq_fault, keystone_rproc_exception_interrupt, - 0, dev_name(ksproc->dev), ksproc); - if (ret) { - dev_err(ksproc->dev, "failed to enable exception interrupt, ret = %d\n", - ret); - goto free_vring_irq; - } + enable_irq(ksproc->irq_ring); + enable_irq(ksproc->irq_fault); ret = keystone_rproc_dsp_boot(ksproc, rproc->bootaddr); - if (ret) - goto free_exc_irq; + if (ret) { + flush_work(&ksproc->workqueue); + return ret; + } return 0; - -free_exc_irq: - free_irq(ksproc->irq_fault, ksproc); -free_vring_irq: - free_irq(ksproc->irq_ring, ksproc); - flush_work(&ksproc->workqueue); -out: - return ret; } /* @@ -215,8 +196,8 @@ static int keystone_rproc_stop(struct rproc *rproc) struct keystone_rproc *ksproc = rproc->priv; keystone_rproc_dsp_reset(ksproc); - free_irq(ksproc->irq_fault, ksproc); - free_irq(ksproc->irq_ring, ksproc); + disable_irq(ksproc->irq_fault); + disable_irq(ksproc->irq_ring); flush_work(&ksproc->workqueue); return 0; @@ -427,10 +408,18 @@ static int keystone_rproc_probe(struct platform_device *pdev) ksproc->irq_ring = platform_get_irq_byname(pdev, "vring"); if (ksproc->irq_ring < 0) return ksproc->irq_ring; + ret = devm_request_irq(dev, ksproc->irq_ring, keystone_rproc_vring_interrupt, + IRQF_NO_AUTOEN, dev_name(dev), ksproc); + if (ret) + return dev_err_probe(dev, ret, "failed to request vring interrupt\n"); ksproc->irq_fault = platform_get_irq_byname(pdev, "exception"); if (ksproc->irq_fault < 0) return ksproc->irq_fault; + ret = devm_request_irq(dev, ksproc->irq_fault, keystone_rproc_exception_interrupt, + IRQF_NO_AUTOEN, dev_name(dev), ksproc); + if (ret) + return dev_err_probe(dev, ret, "failed to enable exception interrupt\n"); ksproc->kick_gpio = devm_gpiod_get(dev, "kick", GPIOD_ASIS); ret = PTR_ERR_OR_ZERO(ksproc->kick_gpio); diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index bb6f6a16d895..85a74c9ec521 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -1078,7 +1078,7 @@ static void scp_unregister_ipi(struct platform_device *pdev, u32 id) scp_ipi_unregister(scp, id); } -static int scp_send_ipi(struct platform_device *pdev, u32 id, void *buf, +static int scp_send_ipi(struct platform_device *pdev, u32 id, const void *buf, unsigned int len, unsigned int wait) { struct mtk_scp *scp = platform_get_drvdata(pdev); diff --git a/drivers/remoteproc/mtk_scp_ipi.c b/drivers/remoteproc/mtk_scp_ipi.c index 7a37e273b3af..ee2f1121411f 100644 --- a/drivers/remoteproc/mtk_scp_ipi.c +++ b/drivers/remoteproc/mtk_scp_ipi.c @@ -156,7 +156,7 @@ EXPORT_SYMBOL_GPL(scp_ipi_unlock); * * Return: 0 if sending data successfully, -error on error. **/ -int scp_ipi_send(struct mtk_scp *scp, u32 id, void *buf, unsigned int len, +int scp_ipi_send(struct mtk_scp *scp, u32 id, const void *buf, unsigned int len, unsigned int wait) { struct mtk_share_obj __iomem *send_obj = scp->send_buf; diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c index 5e3eb7b86a0e..a4636c7bc6b7 100644 --- a/drivers/remoteproc/pru_rproc.c +++ b/drivers/remoteproc/pru_rproc.c @@ -1003,11 +1003,9 @@ static int pru_rproc_probe(struct platform_device *pdev) if (!data) return -ENODEV; - ret = of_property_read_string(np, "firmware-name", &fw_name); - if (ret) { - dev_err(dev, "unable to retrieve firmware-name %d\n", ret); - return ret; - } + ret = rproc_of_parse_firmware(dev, 0, &fw_name); + if (ret) + return dev_err_probe(dev, ret, "unable to retrieve firmware-name\n"); rproc = devm_rproc_alloc(dev, pdev->name, &pru_rproc_ops, fw_name, sizeof(*pru)); @@ -1080,14 +1078,6 @@ static int pru_rproc_probe(struct platform_device *pdev) return 0; } -static void pru_rproc_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rproc *rproc = platform_get_drvdata(pdev); - - dev_dbg(dev, "%s: removing rproc %s\n", __func__, rproc->name); -} - static const struct pru_private_data pru_data = { .type = PRU_TYPE_PRU, }; @@ -1133,7 +1123,6 @@ static struct platform_driver pru_rproc_driver = { .suppress_bind_attrs = true, }, .probe = pru_rproc_probe, - .remove = pru_rproc_remove, }; module_platform_driver(pru_rproc_driver); diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index 6c31140268ac..fd2b6824ad26 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -28,7 +28,6 @@ #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev) #define to_pdm_subdev(d) container_of(d, struct qcom_rproc_pdm, subdev) -#define MAX_NUM_OF_SS 10 #define MAX_REGION_NAME_LENGTH 16 #define SBL_MINIDUMP_SMEM_ID 602 #define MINIDUMP_REGION_VALID ('V' << 24 | 'A' << 16 | 'L' << 8 | 'I' << 0) @@ -80,7 +79,7 @@ struct minidump_global_toc { __le32 status; __le32 md_revision; __le32 enabled; - struct minidump_subsystem subsystems[MAX_NUM_OF_SS]; + struct minidump_subsystem subsystems[]; }; struct qcom_ssr_subsystem { @@ -151,9 +150,11 @@ void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, int ret; struct minidump_subsystem *subsystem; struct minidump_global_toc *toc; + unsigned int num_ss; + size_t toc_size; /* Get Global minidump ToC*/ - toc = qcom_smem_get(QCOM_SMEM_HOST_ANY, SBL_MINIDUMP_SMEM_ID, NULL); + toc = qcom_smem_get(QCOM_SMEM_HOST_ANY, SBL_MINIDUMP_SMEM_ID, &toc_size); /* check if global table pointer exists and init is set */ if (IS_ERR(toc) || !toc->status) { @@ -161,6 +162,16 @@ void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, return; } + /* Derive the number of subsystems from the actual SMEM item size */ + num_ss = (toc_size - offsetof(struct minidump_global_toc, subsystems)) / + sizeof(struct minidump_subsystem); + + if (minidump_id >= num_ss) { + dev_err(&rproc->dev, "Minidump id %d is out of range: %d\n", + minidump_id, num_ss); + return; + } + /* Get subsystem table of contents using the minidump id */ subsystem = &toc->subsystems[minidump_id]; diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 91940977ca89..ae78f5c7c1b6 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -124,6 +124,7 @@ #define QDSP6v56_CLAMP_QMC_MEM BIT(22) #define QDSP6SS_XO_CBCR 0x0038 #define QDSP6SS_ACC_OVERRIDE_VAL 0x20 +#define QDSP6SS_ACC_OVERRIDE_VAL_9607 0x80800000 #define QDSP6v55_BHS_EN_REST_ACK BIT(0) /* QDSP6v65 parameters */ @@ -162,6 +163,7 @@ struct rproc_hexagon_res { char **proxy_pd_names; int version; bool need_mem_protection; + bool need_pas_mem_setup; bool has_alt_reset; bool has_mba_logs; bool has_spare_reg; @@ -240,6 +242,7 @@ struct q6v5 { struct qcom_sysmon *sysmon; struct platform_device *bam_dmux; bool need_mem_protection; + bool need_pas_mem_setup; bool has_alt_reset; bool has_mba_logs; bool has_spare_reg; @@ -254,10 +257,14 @@ struct q6v5 { }; enum { + MSS_MDM9607, MSS_MSM8226, MSS_MSM8909, MSS_MSM8916, + MSS_MSM8917, MSS_MSM8926, + MSS_MSM8937, + MSS_MSM8940, MSS_MSM8953, MSS_MSM8974, MSS_MSM8996, @@ -745,15 +752,25 @@ static int q6v5proc_reset(struct q6v5 *qproc) return ret; } goto pbl_wait; - } else if (qproc->version == MSS_MSM8909 || + } else if (qproc->version == MSS_MDM9607 || + qproc->version == MSS_MSM8909 || + qproc->version == MSS_MSM8917 || + qproc->version == MSS_MSM8937 || + qproc->version == MSS_MSM8940 || qproc->version == MSS_MSM8953 || qproc->version == MSS_MSM8996 || qproc->version == MSS_MSM8998 || qproc->version == MSS_SDM660) { - if (qproc->version != MSS_MSM8909 && - qproc->version != MSS_MSM8953) - /* Override the ACC value if required */ + /* Override the ACC value if required */ + if (qproc->version == MSS_MDM9607 || + qproc->version == MSS_MSM8917 || + qproc->version == MSS_MSM8937 || + qproc->version == MSS_MSM8940) + writel(QDSP6SS_ACC_OVERRIDE_VAL_9607, + qproc->reg_base + QDSP6SS_STRAP_ACC); + else if (qproc->version != MSS_MSM8909 && + qproc->version != MSS_MSM8953) writel(QDSP6SS_ACC_OVERRIDE_VAL, qproc->reg_base + QDSP6SS_STRAP_ACC); @@ -799,6 +816,7 @@ static int q6v5proc_reset(struct q6v5 *qproc) if (qproc->version != MSS_MSM8909) { int mem_pwr_ctl; + int reverse; /* Deassert QDSP6 compiler memory clamp */ val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); @@ -810,17 +828,37 @@ static int q6v5proc_reset(struct q6v5 *qproc) writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); /* Turn on L1, L2, ETB and JU memories 1 at a time */ - if (qproc->version == MSS_MSM8953 || + if (qproc->version == MSS_MSM8940 || + qproc->version == MSS_MSM8953 || qproc->version == MSS_MSM8996) { mem_pwr_ctl = QDSP6SS_MEM_PWR_CTL; i = 19; + reverse = 0; + } else if (qproc->version == MSS_MDM9607 || + qproc->version == MSS_MSM8917 || + qproc->version == MSS_MSM8937) { + mem_pwr_ctl = QDSP6SS_MEM_PWR_CTL; + i = 19; + /* + * Set first 5 bits in reverse to avoid + * "inrush current" issues. + */ + reverse = 6; } else { /* MSS_MSM8998, MSS_SDM660 */ mem_pwr_ctl = QDSP6V6SS_MEM_PWR_CTL; i = 28; + reverse = 0; } + val = readl(qproc->reg_base + mem_pwr_ctl); - for (; i >= 0; i--) { + for (; i >= reverse; i--) { + val |= BIT(i); + writel(val, qproc->reg_base + mem_pwr_ctl); + val = readl(qproc->reg_base + mem_pwr_ctl); + udelay(1); + } + for (i = 0; i < reverse; i++) { val |= BIT(i); writel(val, qproc->reg_base + mem_pwr_ctl); /* @@ -828,7 +866,7 @@ static int q6v5proc_reset(struct q6v5 *qproc) * wait for 1us for both memory peripheral and data * array to turn on. */ - val |= readl(qproc->reg_base + mem_pwr_ctl); + val = readl(qproc->reg_base + mem_pwr_ctl); udelay(1); } } else { @@ -1441,7 +1479,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); } - if (qproc->version == MSS_MSM8953) { + if (qproc->need_pas_mem_setup) { ret = qcom_scm_pas_mem_setup(MPSS_PAS_ID, qproc->mpss_phys, qproc->mpss_size); if (ret) { dev_err(qproc->dev, @@ -2224,6 +2262,7 @@ static const struct rproc_hexagon_res sc7180_mss = { NULL }, .need_mem_protection = true, + .need_pas_mem_setup = false, .has_alt_reset = false, .has_mba_logs = true, .has_spare_reg = true, @@ -2253,6 +2292,7 @@ static const struct rproc_hexagon_res sc7280_mss = { NULL }, .need_mem_protection = true, + .need_pas_mem_setup = false, .has_alt_reset = false, .has_mba_logs = true, .has_spare_reg = false, @@ -2285,6 +2325,7 @@ static const struct rproc_hexagon_res sdm660_mss = { NULL }, .need_mem_protection = true, + .need_pas_mem_setup = false, .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, @@ -2321,6 +2362,7 @@ static const struct rproc_hexagon_res sdm845_mss = { NULL }, .need_mem_protection = true, + .need_pas_mem_setup = false, .has_alt_reset = true, .has_mba_logs = false, .has_spare_reg = false, @@ -2353,6 +2395,7 @@ static const struct rproc_hexagon_res msm8998_mss = { NULL }, .need_mem_protection = true, + .need_pas_mem_setup = false, .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, @@ -2392,6 +2435,7 @@ static const struct rproc_hexagon_res msm8996_mss = { NULL }, .need_mem_protection = true, + .need_pas_mem_setup = false, .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, @@ -2402,6 +2446,41 @@ static const struct rproc_hexagon_res msm8996_mss = { .version = MSS_MSM8996, }; +static const struct rproc_hexagon_res mdm9607_mss = { + .hexagon_mba_image = "mba.mbn", + .proxy_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "pll", + .uA = 100000, + }, + {} + }, + .proxy_clk_names = (char*[]){ + "xo", + NULL + }, + .active_clk_names = (char*[]){ + "iface", + "bus", + "mem", + NULL + }, + .proxy_pd_names = (char*[]){ + "mx", + "cx", + NULL + }, + .need_mem_protection = false, + .has_alt_reset = false, + .has_mba_logs = false, + .has_spare_reg = false, + .has_qaccept_regs = false, + .has_ext_bhs_reg = false, + .has_ext_cntl_regs = false, + .has_vq6 = false, + .version = MSS_MDM9607, +}; + static const struct rproc_hexagon_res msm8909_mss = { .hexagon_mba_image = "mba.mbn", .proxy_supply = (struct qcom_mss_reg_res[]) { @@ -2427,6 +2506,7 @@ static const struct rproc_hexagon_res msm8909_mss = { NULL }, .need_mem_protection = false, + .need_pas_mem_setup = false, .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, @@ -2473,6 +2553,7 @@ static const struct rproc_hexagon_res msm8916_mss = { NULL }, .need_mem_protection = false, + .need_pas_mem_setup = false, .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, @@ -2483,6 +2564,138 @@ static const struct rproc_hexagon_res msm8916_mss = { .version = MSS_MSM8916, }; +static const struct rproc_hexagon_res msm8917_mss = { + .hexagon_mba_image = "mba.mbn", + .proxy_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "pll", + .uA = 100000, + }, + {} + }, + .active_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "mss", + .uV = 1050000, + .uA = 100000, + }, + {} + }, + .proxy_clk_names = (char*[]){ + "xo", + NULL + }, + .active_clk_names = (char*[]){ + "iface", + "bus", + "mem", + NULL + }, + .proxy_pd_names = (char*[]) { + "cx", + "mx", + NULL + }, + .need_mem_protection = false, + .need_pas_mem_setup = false, + .has_alt_reset = false, + .has_mba_logs = false, + .has_spare_reg = false, + .has_qaccept_regs = false, + .has_ext_bhs_reg = false, + .has_ext_cntl_regs = false, + .has_vq6 = false, + .version = MSS_MSM8917, +}; + +static const struct rproc_hexagon_res msm8937_mss = { + .hexagon_mba_image = "mba.mbn", + .proxy_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "pll", + .uA = 100000, + }, + {} + }, + .active_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "mss", + .uV = 1050000, + .uA = 100000, + }, + {} + }, + .proxy_clk_names = (char*[]){ + "xo", + NULL + }, + .active_clk_names = (char*[]){ + "iface", + "bus", + "mem", + NULL + }, + .proxy_pd_names = (char*[]) { + "cx", + "mx", + NULL + }, + .need_mem_protection = false, + .need_pas_mem_setup = true, + .has_alt_reset = false, + .has_mba_logs = false, + .has_spare_reg = false, + .has_qaccept_regs = false, + .has_ext_bhs_reg = false, + .has_ext_cntl_regs = false, + .has_vq6 = false, + .version = MSS_MSM8937, +}; + +static const struct rproc_hexagon_res msm8940_mss = { + .hexagon_mba_image = "mba.mbn", + .proxy_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "pll", + .uA = 100000, + }, + {} + }, + .active_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "mss", + .uV = 1050000, + .uA = 100000, + }, + {} + }, + .proxy_clk_names = (char*[]){ + "xo", + NULL + }, + .active_clk_names = (char*[]){ + "iface", + "bus", + "mem", + NULL + }, + .proxy_pd_names = (char*[]) { + "cx", + "mx", + NULL + }, + .need_mem_protection = false, + .need_pas_mem_setup = true, + .has_alt_reset = false, + .has_mba_logs = false, + .has_spare_reg = false, + .has_qaccept_regs = false, + .has_ext_bhs_reg = false, + .has_ext_cntl_regs = false, + .has_vq6 = false, + .version = MSS_MSM8940, +}; + static const struct rproc_hexagon_res msm8953_mss = { .hexagon_mba_image = "mba.mbn", .proxy_supply = (struct qcom_mss_reg_res[]) { @@ -2509,6 +2722,7 @@ static const struct rproc_hexagon_res msm8953_mss = { NULL }, .need_mem_protection = false, + .need_pas_mem_setup = true, .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, @@ -2562,6 +2776,7 @@ static const struct rproc_hexagon_res msm8974_mss = { NULL }, .need_mem_protection = false, + .need_pas_mem_setup = false, .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, @@ -2600,6 +2815,7 @@ static const struct rproc_hexagon_res msm8226_mss = { NULL }, .need_mem_protection = false, + .need_pas_mem_setup = false, .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, @@ -2646,6 +2862,7 @@ static const struct rproc_hexagon_res msm8926_mss = { NULL }, .need_mem_protection = false, + .need_pas_mem_setup = false, .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, @@ -2657,19 +2874,23 @@ static const struct rproc_hexagon_res msm8926_mss = { }; static const struct of_device_id q6v5_of_match[] = { - { .compatible = "qcom,q6v5-pil", .data = &msm8916_mss}, - { .compatible = "qcom,msm8226-mss-pil", .data = &msm8226_mss}, - { .compatible = "qcom,msm8909-mss-pil", .data = &msm8909_mss}, - { .compatible = "qcom,msm8916-mss-pil", .data = &msm8916_mss}, - { .compatible = "qcom,msm8926-mss-pil", .data = &msm8926_mss}, - { .compatible = "qcom,msm8953-mss-pil", .data = &msm8953_mss}, - { .compatible = "qcom,msm8974-mss-pil", .data = &msm8974_mss}, - { .compatible = "qcom,msm8996-mss-pil", .data = &msm8996_mss}, - { .compatible = "qcom,msm8998-mss-pil", .data = &msm8998_mss}, - { .compatible = "qcom,sc7180-mss-pil", .data = &sc7180_mss}, - { .compatible = "qcom,sc7280-mss-pil", .data = &sc7280_mss}, - { .compatible = "qcom,sdm660-mss-pil", .data = &sdm660_mss}, - { .compatible = "qcom,sdm845-mss-pil", .data = &sdm845_mss}, + { .compatible = "qcom,q6v5-pil", .data = &msm8916_mss }, + { .compatible = "qcom,mdm9607-mss-pil", .data = &mdm9607_mss }, + { .compatible = "qcom,msm8226-mss-pil", .data = &msm8226_mss }, + { .compatible = "qcom,msm8909-mss-pil", .data = &msm8909_mss }, + { .compatible = "qcom,msm8916-mss-pil", .data = &msm8916_mss }, + { .compatible = "qcom,msm8917-mss-pil", .data = &msm8917_mss }, + { .compatible = "qcom,msm8926-mss-pil", .data = &msm8926_mss }, + { .compatible = "qcom,msm8937-mss-pil", .data = &msm8937_mss }, + { .compatible = "qcom,msm8940-mss-pil", .data = &msm8940_mss }, + { .compatible = "qcom,msm8953-mss-pil", .data = &msm8953_mss }, + { .compatible = "qcom,msm8974-mss-pil", .data = &msm8974_mss }, + { .compatible = "qcom,msm8996-mss-pil", .data = &msm8996_mss }, + { .compatible = "qcom,msm8998-mss-pil", .data = &msm8998_mss }, + { .compatible = "qcom,sc7180-mss-pil", .data = &sc7180_mss }, + { .compatible = "qcom,sc7280-mss-pil", .data = &sc7280_mss }, + { .compatible = "qcom,sdm660-mss-pil", .data = &sdm660_mss }, + { .compatible = "qcom,sdm845-mss-pil", .data = &sdm845_mss }, { }, }; MODULE_DEVICE_TABLE(of, q6v5_of_match); diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 46204da046fa..da27d1d3c9da 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -1531,78 +1531,79 @@ static const struct qcom_pas_data sm8750_mpss_resource = { }; static const struct of_device_id qcom_pas_of_match[] = { - { .compatible = "qcom,milos-adsp-pas", .data = &sm8550_adsp_resource}, - { .compatible = "qcom,milos-cdsp-pas", .data = &milos_cdsp_resource}, - { .compatible = "qcom,milos-mpss-pas", .data = &sm8450_mpss_resource}, - { .compatible = "qcom,milos-wpss-pas", .data = &sc7280_wpss_resource}, - { .compatible = "qcom,msm8226-adsp-pil", .data = &msm8996_adsp_resource}, - { .compatible = "qcom,msm8953-adsp-pil", .data = &msm8996_adsp_resource}, - { .compatible = "qcom,msm8974-adsp-pil", .data = &msm8996_adsp_resource}, - { .compatible = "qcom,msm8996-adsp-pil", .data = &msm8996_adsp_resource}, - { .compatible = "qcom,msm8996-slpi-pil", .data = &msm8996_slpi_resource_init}, - { .compatible = "qcom,msm8998-adsp-pas", .data = &msm8996_adsp_resource}, - { .compatible = "qcom,msm8998-slpi-pas", .data = &msm8996_slpi_resource_init}, + { .compatible = "qcom,eliza-adsp-pas", .data = &sm8550_adsp_resource }, + { .compatible = "qcom,milos-adsp-pas", .data = &sm8550_adsp_resource }, + { .compatible = "qcom,milos-cdsp-pas", .data = &milos_cdsp_resource }, + { .compatible = "qcom,milos-mpss-pas", .data = &sm8450_mpss_resource }, + { .compatible = "qcom,milos-wpss-pas", .data = &sc7280_wpss_resource }, + { .compatible = "qcom,msm8226-adsp-pil", .data = &msm8996_adsp_resource }, + { .compatible = "qcom,msm8953-adsp-pil", .data = &msm8996_adsp_resource }, + { .compatible = "qcom,msm8974-adsp-pil", .data = &msm8996_adsp_resource }, + { .compatible = "qcom,msm8996-adsp-pil", .data = &msm8996_adsp_resource }, + { .compatible = "qcom,msm8996-slpi-pil", .data = &msm8996_slpi_resource_init }, + { .compatible = "qcom,msm8998-adsp-pas", .data = &msm8996_adsp_resource }, + { .compatible = "qcom,msm8998-slpi-pas", .data = &msm8996_slpi_resource_init }, { .compatible = "qcom,qcs404-adsp-pas", .data = &adsp_resource_init }, { .compatible = "qcom,qcs404-cdsp-pas", .data = &cdsp_resource_init }, { .compatible = "qcom,qcs404-wcss-pas", .data = &wcss_resource_init }, - { .compatible = "qcom,sa8775p-adsp-pas", .data = &sa8775p_adsp_resource}, - { .compatible = "qcom,sa8775p-cdsp0-pas", .data = &sa8775p_cdsp0_resource}, - { .compatible = "qcom,sa8775p-cdsp1-pas", .data = &sa8775p_cdsp1_resource}, - { .compatible = "qcom,sa8775p-gpdsp0-pas", .data = &sa8775p_gpdsp0_resource}, - { .compatible = "qcom,sa8775p-gpdsp1-pas", .data = &sa8775p_gpdsp1_resource}, - { .compatible = "qcom,sar2130p-adsp-pas", .data = &sm8350_adsp_resource}, - { .compatible = "qcom,sc7180-adsp-pas", .data = &sm8250_adsp_resource}, - { .compatible = "qcom,sc7180-mpss-pas", .data = &mpss_resource_init}, - { .compatible = "qcom,sc7280-adsp-pas", .data = &sm8350_adsp_resource}, - { .compatible = "qcom,sc7280-cdsp-pas", .data = &sm6350_cdsp_resource}, - { .compatible = "qcom,sc7280-mpss-pas", .data = &mpss_resource_init}, - { .compatible = "qcom,sc7280-wpss-pas", .data = &sc7280_wpss_resource}, - { .compatible = "qcom,sc8180x-adsp-pas", .data = &sm8150_adsp_resource}, - { .compatible = "qcom,sc8180x-cdsp-pas", .data = &sm8150_cdsp_resource}, - { .compatible = "qcom,sc8180x-mpss-pas", .data = &sc8180x_mpss_resource}, - { .compatible = "qcom,sc8280xp-adsp-pas", .data = &sm8250_adsp_resource}, - { .compatible = "qcom,sc8280xp-nsp0-pas", .data = &sc8280xp_nsp0_resource}, - { .compatible = "qcom,sc8280xp-nsp1-pas", .data = &sc8280xp_nsp1_resource}, - { .compatible = "qcom,sdm660-adsp-pas", .data = &adsp_resource_init}, - { .compatible = "qcom,sdm660-cdsp-pas", .data = &cdsp_resource_init}, - { .compatible = "qcom,sdm845-adsp-pas", .data = &sdm845_adsp_resource_init}, - { .compatible = "qcom,sdm845-cdsp-pas", .data = &sdm845_cdsp_resource_init}, - { .compatible = "qcom,sdm845-slpi-pas", .data = &sdm845_slpi_resource_init}, - { .compatible = "qcom,sdx55-mpss-pas", .data = &sdx55_mpss_resource}, - { .compatible = "qcom,sdx75-mpss-pas", .data = &sm8650_mpss_resource}, - { .compatible = "qcom,sm6115-adsp-pas", .data = &adsp_resource_init}, - { .compatible = "qcom,sm6115-cdsp-pas", .data = &cdsp_resource_init}, - { .compatible = "qcom,sm6115-mpss-pas", .data = &sc8180x_mpss_resource}, - { .compatible = "qcom,sm6350-adsp-pas", .data = &sm6350_adsp_resource}, - { .compatible = "qcom,sm6350-cdsp-pas", .data = &sm6350_cdsp_resource}, - { .compatible = "qcom,sm6350-mpss-pas", .data = &mpss_resource_init}, - { .compatible = "qcom,sm6375-adsp-pas", .data = &sm6350_adsp_resource}, - { .compatible = "qcom,sm6375-cdsp-pas", .data = &sm8150_cdsp_resource}, - { .compatible = "qcom,sm6375-mpss-pas", .data = &sm6375_mpss_resource}, - { .compatible = "qcom,sm8150-adsp-pas", .data = &sm8150_adsp_resource}, - { .compatible = "qcom,sm8150-cdsp-pas", .data = &sm8150_cdsp_resource}, - { .compatible = "qcom,sm8150-mpss-pas", .data = &mpss_resource_init}, - { .compatible = "qcom,sm8150-slpi-pas", .data = &sdm845_slpi_resource_init}, - { .compatible = "qcom,sm8250-adsp-pas", .data = &sm8250_adsp_resource}, - { .compatible = "qcom,sm8250-cdsp-pas", .data = &sm8250_cdsp_resource}, - { .compatible = "qcom,sm8250-slpi-pas", .data = &sdm845_slpi_resource_init}, - { .compatible = "qcom,sm8350-adsp-pas", .data = &sm8350_adsp_resource}, - { .compatible = "qcom,sm8350-cdsp-pas", .data = &sm8350_cdsp_resource}, - { .compatible = "qcom,sm8350-slpi-pas", .data = &sdm845_slpi_resource_init}, - { .compatible = "qcom,sm8350-mpss-pas", .data = &mpss_resource_init}, - { .compatible = "qcom,sm8450-adsp-pas", .data = &sm8350_adsp_resource}, - { .compatible = "qcom,sm8450-cdsp-pas", .data = &sm8350_cdsp_resource}, - { .compatible = "qcom,sm8450-slpi-pas", .data = &sdm845_slpi_resource_init}, - { .compatible = "qcom,sm8450-mpss-pas", .data = &sm8450_mpss_resource}, - { .compatible = "qcom,sm8550-adsp-pas", .data = &sm8550_adsp_resource}, - { .compatible = "qcom,sm8550-cdsp-pas", .data = &sm8550_cdsp_resource}, - { .compatible = "qcom,sm8550-mpss-pas", .data = &sm8550_mpss_resource}, - { .compatible = "qcom,sm8650-adsp-pas", .data = &sm8550_adsp_resource}, - { .compatible = "qcom,sm8650-cdsp-pas", .data = &sm8650_cdsp_resource}, - { .compatible = "qcom,sm8650-mpss-pas", .data = &sm8650_mpss_resource}, - { .compatible = "qcom,sm8750-mpss-pas", .data = &sm8750_mpss_resource}, - { .compatible = "qcom,x1e80100-adsp-pas", .data = &x1e80100_adsp_resource}, - { .compatible = "qcom,x1e80100-cdsp-pas", .data = &x1e80100_cdsp_resource}, + { .compatible = "qcom,sa8775p-adsp-pas", .data = &sa8775p_adsp_resource }, + { .compatible = "qcom,sa8775p-cdsp0-pas", .data = &sa8775p_cdsp0_resource }, + { .compatible = "qcom,sa8775p-cdsp1-pas", .data = &sa8775p_cdsp1_resource }, + { .compatible = "qcom,sa8775p-gpdsp0-pas", .data = &sa8775p_gpdsp0_resource }, + { .compatible = "qcom,sa8775p-gpdsp1-pas", .data = &sa8775p_gpdsp1_resource }, + { .compatible = "qcom,sar2130p-adsp-pas", .data = &sm8350_adsp_resource }, + { .compatible = "qcom,sc7180-adsp-pas", .data = &sm8250_adsp_resource }, + { .compatible = "qcom,sc7180-mpss-pas", .data = &mpss_resource_init }, + { .compatible = "qcom,sc7280-adsp-pas", .data = &sm8350_adsp_resource }, + { .compatible = "qcom,sc7280-cdsp-pas", .data = &sm6350_cdsp_resource }, + { .compatible = "qcom,sc7280-mpss-pas", .data = &mpss_resource_init }, + { .compatible = "qcom,sc7280-wpss-pas", .data = &sc7280_wpss_resource }, + { .compatible = "qcom,sc8180x-adsp-pas", .data = &sm8150_adsp_resource }, + { .compatible = "qcom,sc8180x-cdsp-pas", .data = &sm8150_cdsp_resource }, + { .compatible = "qcom,sc8180x-mpss-pas", .data = &sc8180x_mpss_resource }, + { .compatible = "qcom,sc8280xp-adsp-pas", .data = &sm8250_adsp_resource }, + { .compatible = "qcom,sc8280xp-nsp0-pas", .data = &sc8280xp_nsp0_resource }, + { .compatible = "qcom,sc8280xp-nsp1-pas", .data = &sc8280xp_nsp1_resource }, + { .compatible = "qcom,sdm660-adsp-pas", .data = &adsp_resource_init }, + { .compatible = "qcom,sdm660-cdsp-pas", .data = &cdsp_resource_init }, + { .compatible = "qcom,sdm845-adsp-pas", .data = &sdm845_adsp_resource_init }, + { .compatible = "qcom,sdm845-cdsp-pas", .data = &sdm845_cdsp_resource_init }, + { .compatible = "qcom,sdm845-slpi-pas", .data = &sdm845_slpi_resource_init }, + { .compatible = "qcom,sdx55-mpss-pas", .data = &sdx55_mpss_resource }, + { .compatible = "qcom,sdx75-mpss-pas", .data = &sm8650_mpss_resource }, + { .compatible = "qcom,sm6115-adsp-pas", .data = &adsp_resource_init }, + { .compatible = "qcom,sm6115-cdsp-pas", .data = &cdsp_resource_init }, + { .compatible = "qcom,sm6115-mpss-pas", .data = &sc8180x_mpss_resource }, + { .compatible = "qcom,sm6350-adsp-pas", .data = &sm6350_adsp_resource }, + { .compatible = "qcom,sm6350-cdsp-pas", .data = &sm6350_cdsp_resource }, + { .compatible = "qcom,sm6350-mpss-pas", .data = &mpss_resource_init }, + { .compatible = "qcom,sm6375-adsp-pas", .data = &sm6350_adsp_resource }, + { .compatible = "qcom,sm6375-cdsp-pas", .data = &sm8150_cdsp_resource }, + { .compatible = "qcom,sm6375-mpss-pas", .data = &sm6375_mpss_resource }, + { .compatible = "qcom,sm8150-adsp-pas", .data = &sm8150_adsp_resource }, + { .compatible = "qcom,sm8150-cdsp-pas", .data = &sm8150_cdsp_resource }, + { .compatible = "qcom,sm8150-mpss-pas", .data = &mpss_resource_init }, + { .compatible = "qcom,sm8150-slpi-pas", .data = &sdm845_slpi_resource_init }, + { .compatible = "qcom,sm8250-adsp-pas", .data = &sm8250_adsp_resource }, + { .compatible = "qcom,sm8250-cdsp-pas", .data = &sm8250_cdsp_resource }, + { .compatible = "qcom,sm8250-slpi-pas", .data = &sdm845_slpi_resource_init }, + { .compatible = "qcom,sm8350-adsp-pas", .data = &sm8350_adsp_resource }, + { .compatible = "qcom,sm8350-cdsp-pas", .data = &sm8350_cdsp_resource }, + { .compatible = "qcom,sm8350-slpi-pas", .data = &sdm845_slpi_resource_init }, + { .compatible = "qcom,sm8350-mpss-pas", .data = &mpss_resource_init }, + { .compatible = "qcom,sm8450-adsp-pas", .data = &sm8350_adsp_resource }, + { .compatible = "qcom,sm8450-cdsp-pas", .data = &sm8350_cdsp_resource }, + { .compatible = "qcom,sm8450-slpi-pas", .data = &sdm845_slpi_resource_init }, + { .compatible = "qcom,sm8450-mpss-pas", .data = &sm8450_mpss_resource }, + { .compatible = "qcom,sm8550-adsp-pas", .data = &sm8550_adsp_resource }, + { .compatible = "qcom,sm8550-cdsp-pas", .data = &sm8550_cdsp_resource }, + { .compatible = "qcom,sm8550-mpss-pas", .data = &sm8550_mpss_resource }, + { .compatible = "qcom,sm8650-adsp-pas", .data = &sm8550_adsp_resource }, + { .compatible = "qcom,sm8650-cdsp-pas", .data = &sm8650_cdsp_resource }, + { .compatible = "qcom,sm8650-mpss-pas", .data = &sm8650_mpss_resource }, + { .compatible = "qcom,sm8750-mpss-pas", .data = &sm8750_mpss_resource }, + { .compatible = "qcom,x1e80100-adsp-pas", .data = &x1e80100_adsp_resource }, + { .compatible = "qcom,x1e80100-cdsp-pas", .data = &x1e80100_cdsp_resource }, { }, }; MODULE_DEVICE_TABLE(of, qcom_pas_of_match); diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index 3ceec1fd6d99..913e3b750a86 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -677,7 +677,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, return ERR_PTR(ret); } - qmi_add_lookup(&sysmon->qmi, 43, 0, 0); + qmi_add_lookup(&sysmon->qmi, QMI_SERVICE_ID_SSCTL, 0, 0); sysmon->subdev.prepare = sysmon_prepare; sysmon->subdev.start = sysmon_start; diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 0cd09e67ac14..0a5e15744b1d 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -218,7 +218,7 @@ bool rproc_u64_fit_in_size_t(u64 val) if (sizeof(size_t) == sizeof(u64)) return true; - return (val <= (size_t) -1); + return val <= SIZE_MAX; } #endif /* REMOTEPROC_INTERNAL_H */ diff --git a/drivers/remoteproc/ti_k3_common.c b/drivers/remoteproc/ti_k3_common.c index 32aa954dc5be..3cb8ae5d72f6 100644 --- a/drivers/remoteproc/ti_k3_common.c +++ b/drivers/remoteproc/ti_k3_common.c @@ -513,7 +513,7 @@ int k3_reserved_mem_init(struct k3_rproc *kproc) kproc->rmem[i].dev_addr = (u32)res.start; kproc->rmem[i].size = resource_size(&res); kproc->rmem[i].cpu_addr = devm_ioremap_resource_wc(dev, &res); - if (!kproc->rmem[i].cpu_addr) { + if (IS_ERR(kproc->rmem[i].cpu_addr)) { dev_err(dev, "failed to map reserved memory#%d at %pR\n", i + 1, &res); return -ENOMEM; diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index b71ce69afe9f..50a9974f3202 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -232,17 +232,19 @@ static void zynqmp_r5_mb_rx_cb(struct mbox_client *cl, void *msg) ipi = container_of(cl, struct mbox_info, mbox_cl); - /* copy data from ipi buffer to r5_core */ + /* copy data from ipi buffer to r5_core if IPI is buffered. */ ipi_msg = (struct zynqmp_ipi_message *)msg; - buf_msg = (struct zynqmp_ipi_message *)ipi->rx_mc_buf; - len = ipi_msg->len; - if (len > IPI_BUF_LEN_MAX) { - dev_warn(cl->dev, "msg size exceeded than %d\n", - IPI_BUF_LEN_MAX); - len = IPI_BUF_LEN_MAX; + if (ipi_msg) { + buf_msg = (struct zynqmp_ipi_message *)ipi->rx_mc_buf; + len = ipi_msg->len; + if (len > IPI_BUF_LEN_MAX) { + dev_warn(cl->dev, "msg size exceeded than %d\n", + IPI_BUF_LEN_MAX); + len = IPI_BUF_LEN_MAX; + } + buf_msg->len = len; + memcpy(buf_msg->data, ipi_msg->data, len); } - buf_msg->len = len; - memcpy(buf_msg->data, ipi_msg->data, len); /* received and processed interrupt ack */ if (mbox_send_message(ipi->rx_chan, NULL) < 0) @@ -265,6 +267,10 @@ static struct mbox_info *zynqmp_r5_setup_mbox(struct device *cdev) struct mbox_client *mbox_cl; struct mbox_info *ipi; + if (!of_property_present(dev_of_node(cdev), "mboxes") || + !of_property_present(dev_of_node(cdev), "mbox-names")) + return NULL; + ipi = kzalloc_obj(*ipi); if (!ipi) return NULL; @@ -1005,7 +1011,7 @@ static int zynqmp_r5_get_sram_banks(struct zynqmp_r5_core *r5_core) } /* Get SRAM device address */ - ret = of_property_read_reg(sram_np, i, &abs_addr, &size); + ret = of_property_read_reg(sram_np, 0, &abs_addr, &size); if (ret) { dev_err(dev, "failed to get reg property\n"); goto fail_sram_get; @@ -1484,6 +1490,8 @@ static void zynqmp_r5_remoteproc_shutdown(struct platform_device *pdev) dev_err(cluster->dev, "failed to %s rproc %d\n", rproc_state_str, rproc->index); } + + zynqmp_r5_free_mbox(r5_core->ipi); } } diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 41b14344b16f..988fc291241d 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -164,11 +164,17 @@ static void mpam_free_garbage(void) /* * Once mpam is enabled, new requestors cannot further reduce the available * partid. Assert that the size is fixed, and new requestors will be turned - * away. + * away. This is needed when walking over structures sized by PARTID. + * + * During mpam_disable() these structures are not fixed, but the MSC state + * is still reset using whatever sizes have been discovered so far. As only + * PARTID 0 will be used after mpam_disable(), any race would be benign. + * Skip the check if a mpam_disable_reason has been set. */ static void mpam_assert_partid_sizes_fixed(void) { - WARN_ON_ONCE(!partid_max_published); + if (!mpam_disable_reason) + WARN_ON_ONCE(!partid_max_published); } static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg) @@ -728,10 +734,9 @@ static void mpam_enable_quirks(struct mpam_msc *msc) * Try and see what values stick in this bit. If we can write either value, * its probably not implemented by hardware. */ -static bool _mpam_ris_hw_probe_hw_nrdy(struct mpam_msc_ris *ris, u32 mon_reg) +static bool mpam_ris_hw_probe_csu_nrdy(struct mpam_msc_ris *ris) { - u32 now; - u64 mon_sel; + u32 now, mon_sel, ctl_val; bool can_set, can_clear; struct mpam_msc *msc = ris->vmsc->msc; @@ -740,23 +745,30 @@ static bool _mpam_ris_hw_probe_hw_nrdy(struct mpam_msc_ris *ris, u32 mon_reg) mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, 0) | FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); - _mpam_write_monsel_reg(msc, mon_reg, mon_sel); + mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel); - _mpam_write_monsel_reg(msc, mon_reg, MSMON___NRDY); - now = _mpam_read_monsel_reg(msc, mon_reg); + /* Hardware might ignore nrdy if it's not enabled */ + ctl_val = MSMON_CFG_CSU_CTL_TYPE_CSU; + ctl_val |= MSMON_CFG_x_CTL_MATCH_PARTID; + ctl_val |= MSMON_CFG_x_CTL_MATCH_PMG; + ctl_val |= MSMON_CFG_x_CTL_EN; + mpam_write_monsel_reg(msc, CFG_CSU_FLT, 0); + mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val); + + _mpam_write_monsel_reg(msc, MSMON_CSU, MSMON___NRDY); + now = _mpam_read_monsel_reg(msc, MSMON_CSU); can_set = now & MSMON___NRDY; - _mpam_write_monsel_reg(msc, mon_reg, 0); - now = _mpam_read_monsel_reg(msc, mon_reg); + _mpam_write_monsel_reg(msc, MSMON_CSU, 0); + /* Configuration change to try and coax hardware into setting nrdy */ + mpam_write_monsel_reg(msc, CFG_CSU_FLT, 0x1); + now = _mpam_read_monsel_reg(msc, MSMON_CSU); can_clear = !(now & MSMON___NRDY); mpam_mon_sel_unlock(msc); return (!can_set || !can_clear); } -#define mpam_ris_hw_probe_hw_nrdy(_ris, _mon_reg) \ - _mpam_ris_hw_probe_hw_nrdy(_ris, MSMON_##_mon_reg) - static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) { int err; @@ -873,20 +885,18 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) mpam_set_feature(mpam_feat_msmon_csu_xcl, props); /* Is NRDY hardware managed? */ - hw_managed = mpam_ris_hw_probe_hw_nrdy(ris, CSU); - if (hw_managed) - mpam_set_feature(mpam_feat_msmon_csu_hw_nrdy, props); - } + hw_managed = mpam_ris_hw_probe_csu_nrdy(ris); - /* - * Accept the missing firmware property if NRDY appears - * un-implemented. - */ - if (err && mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, props)) - dev_err_once(dev, "Counters are not usable because not-ready timeout was not provided by firmware."); + /* + * Accept the missing firmware property if NRDY appears + * un-implemented. + */ + if (err && hw_managed) + dev_err_once(dev, "Counters are not usable because not-ready timeout was not provided by firmware."); + } } if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_MBWU, msmon_features)) { - bool has_long, hw_managed; + bool has_long; u32 mbwumon_idr = mpam_read_partsel_reg(msc, MBWUMON_IDR); props->num_mbwu_mon = FIELD_GET(MPAMF_MBWUMON_IDR_NUM_MON, mbwumon_idr); @@ -905,16 +915,6 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) } else { mpam_set_feature(mpam_feat_msmon_mbwu_31counter, props); } - - /* Is NRDY hardware managed? */ - hw_managed = mpam_ris_hw_probe_hw_nrdy(ris, MBWU); - if (hw_managed) - mpam_set_feature(mpam_feat_msmon_mbwu_hw_nrdy, props); - - /* - * Don't warn about any missing firmware property for - * MBWU NRDY - it doesn't make any sense! - */ } } } @@ -1197,7 +1197,6 @@ static void __ris_msmon_read(void *arg) bool reset_on_next_read = false; struct mpam_msc_ris *ris = m->ris; struct msmon_mbwu_state *mbwu_state; - struct mpam_props *rprops = &ris->props; struct mpam_msc *msc = m->ris->vmsc->msc; u32 mon_sel, ctl_val, flt_val, cur_ctl, cur_flt; @@ -1253,8 +1252,7 @@ static void __ris_msmon_read(void *arg) switch (m->type) { case mpam_feat_msmon_csu: now = mpam_read_monsel_reg(msc, CSU); - if (mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, rprops)) - nrdy = now & MSMON___NRDY; + nrdy = now & MSMON___NRDY; now = FIELD_GET(MSMON___VALUE, now); if (mpam_has_quirk(IGNORE_CSU_NRDY, msc) && m->waited_timeout) @@ -1266,8 +1264,7 @@ static void __ris_msmon_read(void *arg) case mpam_feat_msmon_mbwu_63counter: if (m->type != mpam_feat_msmon_mbwu_31counter) { now = mpam_msc_read_mbwu_l(msc); - if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) - nrdy = now & MSMON___L_NRDY; + nrdy = now & MSMON___L_NRDY; if (m->type == mpam_feat_msmon_mbwu_63counter) now = FIELD_GET(MSMON___LWD_VALUE, now); @@ -1275,8 +1272,7 @@ static void __ris_msmon_read(void *arg) now = FIELD_GET(MSMON___L_VALUE, now); } else { now = mpam_read_monsel_reg(msc, MBWU); - if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) - nrdy = now & MSMON___NRDY; + nrdy = now & MSMON___NRDY; now = FIELD_GET(MSMON___VALUE, now); } @@ -2585,6 +2581,9 @@ static void __destroy_component_cfg(struct mpam_component *comp) lockdep_assert_held(&mpam_list_lock); + if (!comp->cfg) + return; + add_to_garbage(comp->cfg); list_for_each_entry(vmsc, &comp->vmsc, comp_list) { msc = vmsc->msc; diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h index 1914aefdcba9..04d1a59f02af 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -181,14 +181,12 @@ enum mpam_device_features { mpam_feat_msmon_csu, mpam_feat_msmon_csu_capture, mpam_feat_msmon_csu_xcl, - mpam_feat_msmon_csu_hw_nrdy, mpam_feat_msmon_mbwu, mpam_feat_msmon_mbwu_31counter, mpam_feat_msmon_mbwu_44counter, mpam_feat_msmon_mbwu_63counter, mpam_feat_msmon_mbwu_capture, mpam_feat_msmon_mbwu_rwbw, - mpam_feat_msmon_mbwu_hw_nrdy, mpam_feat_partid_nrw, MPAM_FEATURE_LAST }; diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c index a9938006d0e6..226ff6f532fa 100644 --- a/drivers/resctrl/mpam_resctrl.c +++ b/drivers/resctrl/mpam_resctrl.c @@ -22,7 +22,7 @@ #include "mpam_internal.h" -DECLARE_WAIT_QUEUE_HEAD(resctrl_mon_ctx_waiters); +static DECLARE_WAIT_QUEUE_HEAD(resctrl_mon_ctx_waiters); /* * The classes we've picked to map to resctrl resources, wrapped @@ -220,10 +220,18 @@ int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable) if (cdp_enabled && !mpam_resctrl_controls[RDT_RESOURCE_MBA].cdp_enabled) mpam_resctrl_controls[RDT_RESOURCE_MBA].resctrl_res.alloc_capable = false; + /* + * If resctrl has attempted to enable CDP on MBA, re-enable MBA as two + * configurations will be provided so there is no aliasing problem. + */ if (mpam_resctrl_controls[RDT_RESOURCE_MBA].cdp_enabled && mpam_resctrl_controls[RDT_RESOURCE_MBA].class) mpam_resctrl_controls[RDT_RESOURCE_MBA].resctrl_res.alloc_capable = true; + /* On unmount when CDP is disabled, re-enable MBA */ + if (!cdp_enabled && mpam_resctrl_controls[RDT_RESOURCE_MBA].class) + mpam_resctrl_controls[RDT_RESOURCE_MBA].resctrl_res.alloc_capable = true; + if (enable) { if (mpam_partid_max < 1) return -EINVAL; @@ -1399,7 +1407,7 @@ mpam_resctrl_alloc_domain(unsigned int cpu, struct mpam_resctrl_res *res) } if (r->mon_capable) { - struct mpam_component *any_mon_comp; + struct mpam_component *any_mon_comp = NULL; struct mpam_resctrl_mon *mon; enum resctrl_event_id eventid; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 7ce151f6a7e4..d009eb0849a3 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -85,9 +85,9 @@ config RESET_EIC7700 config RESET_EYEQ bool "Mobileye EyeQ reset controller" - depends on MACH_EYEQ5 || MACH_EYEQ6H || COMPILE_TEST + depends on EYEQ || COMPILE_TEST select AUXILIARY_BUS - default MACH_EYEQ5 || MACH_EYEQ6H + default EYEQ help This enables the Mobileye EyeQ reset controller, used in EyeQ5, EyeQ6L and EyeQ6H SoCs. @@ -257,6 +257,8 @@ config RESET_RZG2L_USBPHY_CTRL config RESET_RZV2H_USB2PHY tristate "Renesas RZ/V2H(P) (and similar SoCs) USB2PHY Reset driver" depends on ARCH_RENESAS || COMPILE_TEST + select AUXILIARY_BUS + select REGMAP_MMIO help Support for USB2PHY Port reset Control found on the RZ/V2H(P) SoC (and similar SoCs). @@ -291,6 +293,13 @@ config RESET_SIMPLE - SiFive FU740 SoCs - Sophgo SoCs +config RESET_SKY1 + bool "Cix Sky1 reset controller" + depends on ARCH_CIX || COMPILE_TEST + select REGMAP_MMIO + help + This enables the reset controller for Cix Sky1. + config RESET_SOCFPGA bool "SoCFPGA Reset Driver" if COMPILE_TEST && (!ARM || !ARCH_INTEL_SOCFPGA) default ARM && ARCH_INTEL_SOCFPGA @@ -315,6 +324,17 @@ config RESET_SUNXI help This enables the reset driver for Allwinner SoCs. +config RESET_TENSTORRENT_ATLANTIS + tristate "Tenstorrent atlantis reset driver" + depends on ARCH_TENSTORRENT || COMPILE_TEST + select AUXILIARY_BUS + default ARCH_TENSTORRENT + help + This enables the driver for the reset controller + present in the Tenstorrent Atlantis SoC. + Enable this option to be able to use hardware + resets on Atalantis based systems. + config RESET_TH1520 tristate "T-HEAD TH1520 reset controller" depends on ARCH_THEAD || COMPILE_TEST diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index fc0cc99f8514..3e52569bd276 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -38,9 +38,11 @@ obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o obj-$(CONFIG_RESET_SCMI) += reset-scmi.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o +obj-$(CONFIG_RESET_SKY1) += reset-sky1.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o +obj-$(CONFIG_RESET_TENSTORRENT_ATLANTIS) += reset-tenstorrent-atlantis.o obj-$(CONFIG_RESET_TH1520) += reset-th1520.o obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o diff --git a/drivers/reset/amlogic/reset-meson.c b/drivers/reset/amlogic/reset-meson.c index 84610365a823..c303e8590dd6 100644 --- a/drivers/reset/amlogic/reset-meson.c +++ b/drivers/reset/amlogic/reset-meson.c @@ -42,6 +42,7 @@ static const struct meson_reset_param meson_s4_param = { }; static const struct meson_reset_param t7_param = { + .reset_ops = &meson_reset_ops, .reset_num = 224, .reset_offset = 0x0, .level_offset = 0x40, diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 352c2360603b..38e189d04d09 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -20,9 +21,11 @@ #include #include #include +#include #include #include #include +#include static DEFINE_MUTEX(reset_list_mutex); static LIST_HEAD(reset_controller_list); @@ -36,6 +39,7 @@ static DEFINE_IDA(reset_gpio_ida); * struct reset_control - a reset control * @rcdev: a pointer to the reset controller device * this reset control belongs to + * @srcu: protects the rcdev pointer from removal during consumer access * @list: list entry for the rcdev's reset controller list * @id: ID of the reset controller in the reset * controller device @@ -47,9 +51,11 @@ static DEFINE_IDA(reset_gpio_ida); * @triggered_count: Number of times this reset line has been reset. Currently * only used for shared resets, which means that the value * will be either 0 or 1. + * @lock: serializes the internals of reset_control_acquire() */ struct reset_control { - struct reset_controller_dev *rcdev; + struct reset_controller_dev __rcu *rcdev; + struct srcu_struct srcu; struct list_head list; unsigned int id; struct kref refcnt; @@ -58,6 +64,7 @@ struct reset_control { bool array; atomic_t deassert_count; atomic_t triggered_count; + struct mutex lock; }; /** @@ -74,14 +81,16 @@ struct reset_control_array { /** * struct reset_gpio_lookup - lookup key for ad-hoc created reset-gpio devices - * @of_args: phandle to the reset controller with all the args like GPIO number + * @ref_args: Reference to the reset controller with all the args like GPIO number * @swnode: Software node containing the reference to the GPIO provider * @list: list entry for the reset_gpio_lookup_list + * @adev: Auxiliary device representing the reset controller */ struct reset_gpio_lookup { - struct of_phandle_args of_args; + struct fwnode_reference_args ref_args; struct fwnode_handle *swnode; struct list_head list; + struct auxiliary_device adev; }; static const char *rcdev_name(struct reset_controller_dev *rcdev) @@ -89,27 +98,24 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev) if (rcdev->dev) return dev_name(rcdev->dev); - if (rcdev->of_node) - return rcdev->of_node->full_name; - - if (rcdev->of_args) - return rcdev->of_args->np->full_name; + if (rcdev->fwnode) + return fwnode_get_name(rcdev->fwnode); return NULL; } /** - * of_reset_simple_xlate - translate reset_spec to the reset line number + * fwnode_reset_simple_xlate - translate reset_spec to the reset line number * @rcdev: a pointer to the reset controller device - * @reset_spec: reset line specifier as found in the device tree + * @reset_spec: reset line specifier as found in firmware * - * This static translation function is used by default if of_xlate in - * :c:type:`reset_controller_dev` is not set. It is useful for all reset - * controllers with 1:1 mapping, where reset lines can be indexed by number - * without gaps. + * This static translation function is used by default if neither fwnode_xlate + * not of_xlate in :c:type:`reset_controller_dev` is not set. It is useful for + * all reset controllers with 1:1 mapping, where reset lines can be indexed by + * number without gaps. */ -static int of_reset_simple_xlate(struct reset_controller_dev *rcdev, - const struct of_phandle_args *reset_spec) +static int fwnode_reset_simple_xlate(struct reset_controller_dev *rcdev, + const struct fwnode_reference_args *reset_spec) { if (reset_spec->args[0] >= rcdev->nr_resets) return -EINVAL; @@ -123,33 +129,71 @@ static int of_reset_simple_xlate(struct reset_controller_dev *rcdev, */ int reset_controller_register(struct reset_controller_dev *rcdev) { - if (rcdev->of_node && rcdev->of_args) + if ((rcdev->of_node && rcdev->fwnode) || (rcdev->of_xlate && rcdev->fwnode_xlate)) return -EINVAL; - if (!rcdev->of_xlate) { - rcdev->of_reset_n_cells = 1; - rcdev->of_xlate = of_reset_simple_xlate; + if (rcdev->of_node && !rcdev->fwnode) + rcdev->fwnode = of_fwnode_handle(rcdev->of_node); + + if (!rcdev->fwnode) { + rcdev->fwnode = dev_fwnode(rcdev->dev); + if (!rcdev->fwnode) + return -EINVAL; + } + + if (rcdev->of_xlate) + rcdev->fwnode_reset_n_cells = rcdev->of_reset_n_cells; + + if (!rcdev->fwnode_xlate && !rcdev->of_xlate) { + rcdev->fwnode_xlate = fwnode_reset_simple_xlate; + rcdev->fwnode_reset_n_cells = 1; } INIT_LIST_HEAD(&rcdev->reset_control_head); + mutex_init(&rcdev->lock); + + guard(mutex)(&reset_list_mutex); - mutex_lock(&reset_list_mutex); list_add(&rcdev->list, &reset_controller_list); - mutex_unlock(&reset_list_mutex); return 0; } EXPORT_SYMBOL_GPL(reset_controller_register); +static void reset_controller_remove(struct reset_controller_dev *rcdev, + struct reset_control *rstc) +{ + lockdep_assert_held(&rcdev->lock); + + list_del(&rstc->list); + module_put(rcdev->owner); + put_device(rcdev->dev); +} + /** * reset_controller_unregister - unregister a reset controller device * @rcdev: a pointer to the reset controller device */ void reset_controller_unregister(struct reset_controller_dev *rcdev) { - mutex_lock(&reset_list_mutex); - list_del(&rcdev->list); - mutex_unlock(&reset_list_mutex); + struct reset_control *rstc, *pos; + + scoped_guard(mutex, &reset_list_mutex) + list_del(&rcdev->list); + + scoped_guard(mutex, &rcdev->lock) { + /* + * Numb but don't free the remaining reset control handles that are + * still held by consumers. + */ + list_for_each_entry_safe(rstc, pos, &rcdev->reset_control_head, list) { + rcu_assign_pointer(rstc->rcdev, NULL); + synchronize_srcu(&rstc->srcu); + reset_controller_remove(rcdev, rstc); + } + } + + mutex_destroy(&rcdev->lock); } EXPORT_SYMBOL_GPL(reset_controller_unregister); @@ -326,6 +370,7 @@ static inline bool reset_control_is_array(struct reset_control *rstc) */ int reset_control_reset(struct reset_control *rstc) { + struct reset_controller_dev *rcdev; int ret; if (!rstc) @@ -337,7 +382,13 @@ int reset_control_reset(struct reset_control *rstc) if (reset_control_is_array(rstc)) return reset_control_array_reset(rstc_to_array(rstc)); - if (!rstc->rcdev->ops->reset) + guard(srcu)(&rstc->srcu); + + rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu); + if (!rcdev) + return -ENODEV; + + if (!rcdev->ops->reset) return -ENOTSUPP; if (rstc->shared) { @@ -351,7 +402,7 @@ int reset_control_reset(struct reset_control *rstc) return -EPERM; } - ret = rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); + ret = rcdev->ops->reset(rcdev, rstc->id); if (rstc->shared && ret) atomic_dec(&rstc->triggered_count); @@ -384,7 +435,7 @@ int reset_control_bulk_reset(int num_rstcs, EXPORT_SYMBOL_GPL(reset_control_bulk_reset); /** - * reset_control_rearm - allow shared reset line to be re-triggered" + * reset_control_rearm - allow shared reset line to be re-triggered * @rstc: reset controller * * On a shared reset line the actual reset pulse is only triggered once for the @@ -441,6 +492,8 @@ EXPORT_SYMBOL_GPL(reset_control_rearm); */ int reset_control_assert(struct reset_control *rstc) { + struct reset_controller_dev *rcdev; + if (!rstc) return 0; @@ -450,6 +503,12 @@ int reset_control_assert(struct reset_control *rstc) if (reset_control_is_array(rstc)) return reset_control_array_assert(rstc_to_array(rstc)); + guard(srcu)(&rstc->srcu); + + rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu); + if (!rcdev) + return -ENODEV; + if (rstc->shared) { if (WARN_ON(atomic_read(&rstc->triggered_count) != 0)) return -EINVAL; @@ -464,7 +523,7 @@ int reset_control_assert(struct reset_control *rstc) * Shared reset controls allow the reset line to be in any state * after this call, so doing nothing is a valid option. */ - if (!rstc->rcdev->ops->assert) + if (!rcdev->ops->assert) return 0; } else { /* @@ -472,17 +531,17 @@ int reset_control_assert(struct reset_control *rstc) * is no way to guarantee that the reset line is asserted after * this call. */ - if (!rstc->rcdev->ops->assert) + if (!rcdev->ops->assert) return -ENOTSUPP; if (!rstc->acquired) { WARN(1, "reset %s (ID: %u) is not acquired\n", - rcdev_name(rstc->rcdev), rstc->id); + rcdev_name(rcdev), rstc->id); return -EPERM; } } - return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); + return rcdev->ops->assert(rcdev, rstc->id); } EXPORT_SYMBOL_GPL(reset_control_assert); @@ -529,6 +588,8 @@ EXPORT_SYMBOL_GPL(reset_control_bulk_assert); */ int reset_control_deassert(struct reset_control *rstc) { + struct reset_controller_dev *rcdev; + if (!rstc) return 0; @@ -538,6 +599,12 @@ int reset_control_deassert(struct reset_control *rstc) if (reset_control_is_array(rstc)) return reset_control_array_deassert(rstc_to_array(rstc)); + guard(srcu)(&rstc->srcu); + + rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu); + if (!rcdev) + return -ENODEV; + if (rstc->shared) { if (WARN_ON(atomic_read(&rstc->triggered_count) != 0)) return -EINVAL; @@ -547,7 +614,7 @@ int reset_control_deassert(struct reset_control *rstc) } else { if (!rstc->acquired) { WARN(1, "reset %s (ID: %u) is not acquired\n", - rcdev_name(rstc->rcdev), rstc->id); + rcdev_name(rcdev), rstc->id); return -EPERM; } } @@ -559,10 +626,10 @@ int reset_control_deassert(struct reset_control *rstc) * case, the reset controller driver should implement .deassert() and * return -ENOTSUPP. */ - if (!rstc->rcdev->ops->deassert) + if (!rcdev->ops->deassert) return 0; - return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); + return rcdev->ops->deassert(rcdev, rstc->id); } EXPORT_SYMBOL_GPL(reset_control_deassert); @@ -604,14 +671,22 @@ EXPORT_SYMBOL_GPL(reset_control_bulk_deassert); */ int reset_control_status(struct reset_control *rstc) { + struct reset_controller_dev *rcdev; + if (!rstc) return 0; if (WARN_ON(IS_ERR(rstc)) || reset_control_is_array(rstc)) return -EINVAL; - if (rstc->rcdev->ops->status) - return rstc->rcdev->ops->status(rstc->rcdev, rstc->id); + guard(srcu)(&rstc->srcu); + + rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu); + if (!rcdev) + return -ENODEV; + + if (rcdev->ops->status) + return rcdev->ops->status(rcdev, rstc->id); return -ENOTSUPP; } @@ -639,6 +714,7 @@ EXPORT_SYMBOL_GPL(reset_control_status); */ int reset_control_acquire(struct reset_control *rstc) { + struct reset_controller_dev *rcdev; struct reset_control *rc; if (!rstc) @@ -650,25 +726,28 @@ int reset_control_acquire(struct reset_control *rstc) if (reset_control_is_array(rstc)) return reset_control_array_acquire(rstc_to_array(rstc)); - mutex_lock(&reset_list_mutex); + guard(mutex)(&rstc->lock); - if (rstc->acquired) { - mutex_unlock(&reset_list_mutex); + if (rstc->acquired) return 0; - } - list_for_each_entry(rc, &rstc->rcdev->reset_control_head, list) { - if (rstc != rc && rstc->id == rc->id) { - if (rc->acquired) { - mutex_unlock(&reset_list_mutex); - return -EBUSY; + guard(srcu)(&rstc->srcu); + + rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu); + if (!rcdev) + return -ENODEV; + + scoped_guard(mutex, &rcdev->lock) { + list_for_each_entry(rc, &rcdev->reset_control_head, list) { + if (rstc != rc && rstc->id == rc->id) { + if (rc->acquired) + return -EBUSY; } } } rstc->acquired = true; - mutex_unlock(&reset_list_mutex); return 0; } EXPORT_SYMBOL_GPL(reset_control_acquire); @@ -752,8 +831,9 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev, bool shared = flags & RESET_CONTROL_FLAGS_BIT_SHARED; bool acquired = flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED; struct reset_control *rstc; + int ret; - lockdep_assert_held(&reset_list_mutex); + lockdep_assert_held(&rcdev->lock); /* Expect callers to filter out OPTIONAL and DEASSERTED bits */ if (WARN_ON(flags & ~(RESET_CONTROL_FLAGS_BIT_SHARED | @@ -782,15 +862,23 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev, if (!rstc) return ERR_PTR(-ENOMEM); + ret = init_srcu_struct(&rstc->srcu); + if (ret) { + kfree(rstc); + return ERR_PTR(ret); + } + if (!try_module_get(rcdev->owner)) { + cleanup_srcu_struct(&rstc->srcu); kfree(rstc); return ERR_PTR(-ENODEV); } - rstc->rcdev = rcdev; + rcu_assign_pointer(rstc->rcdev, rcdev); list_add(&rstc->list, &rcdev->reset_control_head); rstc->id = index; kref_init(&rstc->refcnt); + mutex_init(&rstc->lock); rstc->acquired = acquired; rstc->shared = shared; get_device(rcdev->dev); @@ -802,76 +890,133 @@ static void __reset_control_release(struct kref *kref) { struct reset_control *rstc = container_of(kref, struct reset_control, refcnt); + struct reset_controller_dev *rcdev; - lockdep_assert_held(&reset_list_mutex); + lockdep_assert_held(&rstc->srcu); - module_put(rstc->rcdev->owner); + rcdev = rcu_replace_pointer(rstc->rcdev, NULL, true); + if (rcdev) { + lockdep_assert_held(&rcdev->lock); + reset_controller_remove(rcdev, rstc); + } - list_del(&rstc->list); - put_device(rstc->rcdev->dev); - kfree(rstc); + mutex_destroy(&rstc->lock); } -static void __reset_control_put_internal(struct reset_control *rstc) +static void reset_control_put_internal(struct reset_control *rstc) { - lockdep_assert_held(&reset_list_mutex); + struct reset_controller_dev *rcdev; + int ret = 0; if (IS_ERR_OR_NULL(rstc)) return; - kref_put(&rstc->refcnt, __reset_control_release); + scoped_guard(srcu, &rstc->srcu) { + rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu); + if (!rcdev) + /* Already released. */ + return; + + guard(mutex)(&rcdev->lock); + ret = kref_put(&rstc->refcnt, __reset_control_release); + } + + if (ret) { + synchronize_srcu(&rstc->srcu); + cleanup_srcu_struct(&rstc->srcu); + kfree(rstc); + } } static void reset_gpio_aux_device_release(struct device *dev) { - struct auxiliary_device *adev = to_auxiliary_dev(dev); - - kfree(adev); + WARN(1, "reset-gpio device %s should never have been removed", dev_name(dev)); } -static int reset_add_gpio_aux_device(struct device *parent, - struct fwnode_handle *swnode, - int id, void *pdata) +static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev, + struct device *parent) { - struct auxiliary_device *adev; - int ret; + struct auxiliary_device *adev = &rgpio_dev->adev; + int ret, id; - adev = kzalloc_obj(*adev); - if (!adev) + id = ida_alloc(&reset_gpio_ida, GFP_KERNEL); + if (id < 0) return -ENOMEM; adev->id = id; adev->name = "gpio"; adev->dev.parent = parent; - adev->dev.platform_data = pdata; + adev->dev.platform_data = &rgpio_dev->ref_args; adev->dev.release = reset_gpio_aux_device_release; - device_set_node(&adev->dev, swnode); + device_set_node(&adev->dev, rgpio_dev->swnode); ret = auxiliary_device_init(adev); if (ret) { - kfree(adev); + ida_free(&reset_gpio_ida, id); return ret; } ret = __auxiliary_device_add(adev, "reset"); if (ret) { auxiliary_device_uninit(adev); + ida_free(&reset_gpio_ida, id); return ret; } - return ret; + return 0; +} + +static void reset_gpio_add_devlink(struct fwnode_handle *fwnode, + struct reset_gpio_lookup *rgpio_dev) +{ + struct device *consumer; + + /* + * We must use get_dev_from_fwnode() and not ref_find_device_by_node() + * because the latter only considers the platform bus while we want to + * get consumers of any kind that can be associated with firmware + * nodes: auxiliary, soundwire, etc. + */ + consumer = get_dev_from_fwnode(fwnode); + if (consumer) { + if (!device_link_add(consumer, &rgpio_dev->adev.dev, + DL_FLAG_AUTOREMOVE_CONSUMER)) + pr_warn("Failed to create a device link between reset-gpio and its consumer"); + + put_device(consumer); + } + /* + * else { } + * + * TODO: If ever there's a case where we need to support shared + * reset-gpios retrieved from a device node for which there's no + * device present yet, this is where we'd set up a notifier waiting + * for the device to appear in the system. This would be a lot of code + * that would go unused for now so let's cross that bridge when and if + * we get there. + */ +} + +/* TODO: move it out into drivers/base/ */ +static bool fwnode_reference_args_equal(const struct fwnode_reference_args *left, + const struct fwnode_reference_args *right) +{ + return left->fwnode == right->fwnode && left->nargs == right->nargs && + !memcmp(left->args, right->args, sizeof(left->args[0]) * left->nargs); } /* - * @args: phandle to the GPIO provider with all the args like GPIO number + * @np: OF-node associated with the consumer + * @args: Reference to the GPIO provider with all the args like GPIO number */ -static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) +static int __reset_add_reset_gpio_device(struct fwnode_handle *fwnode, + const struct fwnode_reference_args *args) { struct property_entry properties[3] = { }; - unsigned int offset, of_flags, lflags; + unsigned int offset, flags, lflags; struct reset_gpio_lookup *rgpio_dev; struct device *parent; - int id, ret, prop = 0; + int ret, prop = 0; /* * Currently only #gpio-cells=2 is supported with the meaning of: @@ -879,7 +1024,7 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) * args[1]: GPIO flags * TODO: Handle other cases. */ - if (args->args_count != 2) + if (args->nargs != 2) return -ENOENT; /* @@ -890,7 +1035,7 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) lockdep_assert_not_held(&reset_list_mutex); offset = args->args[0]; - of_flags = args->args[1]; + flags = args->args[1]; /* * Later we map GPIO flags between OF and Linux, however not all @@ -900,90 +1045,89 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) * FIXME: Find a better way of translating OF flags to GPIO lookup * flags. */ - if (of_flags > GPIO_ACTIVE_LOW) { + if (flags > GPIO_ACTIVE_LOW) { pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n", - of_flags, offset); + flags, offset); return -EINVAL; } struct gpio_device *gdev __free(gpio_device_put) = - gpio_device_find_by_fwnode(of_fwnode_handle(args->np)); + gpio_device_find_by_fwnode(args->fwnode); if (!gdev) return -EPROBE_DEFER; guard(mutex)(&reset_gpio_lookup_mutex); list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) { - if (args->np == rgpio_dev->of_args.np) { - if (of_phandle_args_equal(args, &rgpio_dev->of_args)) - return 0; /* Already on the list, done */ + if (fwnode_reference_args_equal(args, &rgpio_dev->ref_args)) { + /* + * Already on the list, create the device link + * and stop here. + */ + reset_gpio_add_devlink(fwnode, rgpio_dev); + return 0; } } - lflags = GPIO_PERSISTENT | (of_flags & GPIO_ACTIVE_LOW); + lflags = GPIO_PERSISTENT | (flags & GPIO_ACTIVE_LOW); parent = gpio_device_to_device(gdev); properties[prop++] = PROPERTY_ENTRY_STRING("compatible", "reset-gpio"); properties[prop++] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags); - id = ida_alloc(&reset_gpio_ida, GFP_KERNEL); - if (id < 0) - return id; - /* Not freed on success, because it is persisent subsystem data. */ rgpio_dev = kzalloc_obj(*rgpio_dev); - if (!rgpio_dev) { - ret = -ENOMEM; - goto err_ida_free; - } + if (!rgpio_dev) + return -ENOMEM; - rgpio_dev->of_args = *args; + rgpio_dev->ref_args = *args; /* - * We keep the device_node reference, but of_args.np is put at the end - * of __of_reset_control_get(), so get it one more time. + * We keep the fwnode_handle reference, but ref_args.fwnode is put at + * the end of __fwnode_reset_control_get(), so get it one more time. * Hold reference as long as rgpio_dev memory is valid. */ - of_node_get(rgpio_dev->of_args.np); + fwnode_handle_get(rgpio_dev->ref_args.fwnode); rgpio_dev->swnode = fwnode_create_software_node(properties, NULL); if (IS_ERR(rgpio_dev->swnode)) { ret = PTR_ERR(rgpio_dev->swnode); - goto err_put_of_node; + goto err_put_fwnode; } - ret = reset_add_gpio_aux_device(parent, rgpio_dev->swnode, id, - &rgpio_dev->of_args); + ret = reset_create_gpio_aux_device(rgpio_dev, parent); if (ret) goto err_del_swnode; + reset_gpio_add_devlink(fwnode, rgpio_dev); list_add(&rgpio_dev->list, &reset_gpio_lookup_list); return 0; err_del_swnode: fwnode_remove_software_node(rgpio_dev->swnode); -err_put_of_node: - of_node_put(rgpio_dev->of_args.np); +err_put_fwnode: + fwnode_handle_put(rgpio_dev->ref_args.fwnode); kfree(rgpio_dev); -err_ida_free: - ida_free(&reset_gpio_ida, id); return ret; } -static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_args *args, - bool gpio_fallback) +static struct reset_controller_dev * +__reset_find_rcdev(const struct fwnode_reference_args *args, bool gpio_fallback) { + struct fwnode_reference_args *rc_args; struct reset_controller_dev *rcdev; lockdep_assert_held(&reset_list_mutex); list_for_each_entry(rcdev, &reset_controller_list, list) { - if (gpio_fallback) { - if (rcdev->of_args && of_phandle_args_equal(args, - rcdev->of_args)) + if (gpio_fallback && rcdev->dev && + device_is_compatible(rcdev->dev, "reset-gpio")) { + rc_args = dev_get_platdata(rcdev->dev); + + if (fwnode_reference_args_equal(args, rc_args)) return rcdev; } else { - if (args->np == rcdev->of_node) + if (args->fwnode == rcdev->fwnode) return rcdev; } } @@ -992,31 +1136,31 @@ static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_a } struct reset_control * -__of_reset_control_get(struct device_node *node, const char *id, int index, - enum reset_control_flags flags) +__fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int index, + enum reset_control_flags flags) { bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; bool gpio_fallback = false; - struct reset_control *rstc; + struct reset_control *rstc = ERR_PTR(-EINVAL); struct reset_controller_dev *rcdev; - struct of_phandle_args args; - int rstc_id; + struct fwnode_reference_args args; + struct of_phandle_args of_args; + int rstc_id = -EINVAL; int ret; - if (!node) + if (!fwnode) return ERR_PTR(-EINVAL); if (id) { - index = of_property_match_string(node, - "reset-names", id); + index = fwnode_property_match_string(fwnode, "reset-names", id); if (index == -EILSEQ) return ERR_PTR(index); if (index < 0) return optional ? NULL : ERR_PTR(-ENOENT); } - ret = of_parse_phandle_with_args(node, "resets", "#reset-cells", - index, &args); + ret = fwnode_property_get_reference_args(fwnode, "resets", "#reset-cells", + 0, index, &args); if (ret == -EINVAL) return ERR_PTR(ret); if (ret) { @@ -1027,51 +1171,65 @@ __of_reset_control_get(struct device_node *node, const char *id, int index, * There can be only one reset-gpio for regular devices, so * don't bother with the "reset-gpios" phandle index. */ - ret = of_parse_phandle_with_args(node, "reset-gpios", "#gpio-cells", - 0, &args); + ret = fwnode_property_get_reference_args(fwnode, "reset-gpios", + "#gpio-cells", 0, 0, &args); if (ret) return optional ? NULL : ERR_PTR(ret); gpio_fallback = true; - ret = __reset_add_reset_gpio_device(&args); + ret = __reset_add_reset_gpio_device(fwnode, &args); + if (ret) { + fwnode_handle_put(args.fwnode); + return ERR_PTR(ret); + } + } + + guard(mutex)(&reset_list_mutex); + + rcdev = __reset_find_rcdev(&args, gpio_fallback); + if (!rcdev) { + rstc = ERR_PTR(-EPROBE_DEFER); + goto out_put; + } + + if (WARN_ON(args.nargs != rcdev->fwnode_reset_n_cells)) { + rstc = ERR_PTR(-EINVAL); + goto out_put; + } + + if (rcdev->of_xlate && is_of_node(fwnode)) { + ret = of_parse_phandle_with_args(to_of_node(fwnode), + gpio_fallback ? "reset-gpios" : "resets", + gpio_fallback ? "#gpio-cells" : "#reset-cells", + gpio_fallback ? 0 : index, + &of_args); if (ret) { rstc = ERR_PTR(ret); goto out_put; } - } - mutex_lock(&reset_list_mutex); - rcdev = __reset_find_rcdev(&args, gpio_fallback); - if (!rcdev) { - rstc = ERR_PTR(-EPROBE_DEFER); - goto out_unlock; + rstc_id = rcdev->of_xlate(rcdev, &of_args); + of_node_put(of_args.np); + } else if (rcdev->fwnode_xlate) { + rstc_id = rcdev->fwnode_xlate(rcdev, &args); } - - if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) { - rstc = ERR_PTR(-EINVAL); - goto out_unlock; - } - - rstc_id = rcdev->of_xlate(rcdev, &args); if (rstc_id < 0) { rstc = ERR_PTR(rstc_id); - goto out_unlock; + goto out_put; } flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL; - /* reset_list_mutex also protects the rcdev's reset_control list */ - rstc = __reset_control_get_internal(rcdev, rstc_id, flags); + scoped_guard(mutex, &rcdev->lock) + rstc = __reset_control_get_internal(rcdev, rstc_id, flags); -out_unlock: - mutex_unlock(&reset_list_mutex); out_put: - of_node_put(args.np); + fwnode_handle_put(args.fwnode); return rstc; } -EXPORT_SYMBOL_GPL(__of_reset_control_get); +EXPORT_SYMBOL_GPL(__fwnode_reset_control_get); struct reset_control *__reset_control_get(struct device *dev, const char *id, int index, enum reset_control_flags flags) @@ -1079,12 +1237,13 @@ struct reset_control *__reset_control_get(struct device *dev, const char *id, bool shared = flags & RESET_CONTROL_FLAGS_BIT_SHARED; bool acquired = flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED; bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; + struct fwnode_handle *fwnode = dev_fwnode(dev); if (WARN_ON(shared && acquired)) return ERR_PTR(-EINVAL); - if (dev->of_node) - return __of_reset_control_get(dev->of_node, id, index, flags); + if (fwnode) + return __fwnode_reset_control_get(fwnode, id, index, flags); return optional ? NULL : ERR_PTR(-ENOENT); } @@ -1107,10 +1266,9 @@ int __reset_control_bulk_get(struct device *dev, int num_rstcs, return 0; err: - mutex_lock(&reset_list_mutex); while (i--) - __reset_control_put_internal(rstcs[i].rstc); - mutex_unlock(&reset_list_mutex); + reset_control_put_internal(rstcs[i].rstc); + return ret; } EXPORT_SYMBOL_GPL(__reset_control_bulk_get); @@ -1119,10 +1277,8 @@ static void reset_control_array_put(struct reset_control_array *resets) { int i; - mutex_lock(&reset_list_mutex); for (i = 0; i < resets->num_rstcs; i++) - __reset_control_put_internal(resets->rstc[i]); - mutex_unlock(&reset_list_mutex); + reset_control_put_internal(resets->rstc[i]); kfree(resets); } @@ -1140,9 +1296,7 @@ void reset_control_put(struct reset_control *rstc) return; } - mutex_lock(&reset_list_mutex); - __reset_control_put_internal(rstc); - mutex_unlock(&reset_list_mutex); + reset_control_put_internal(rstc); } EXPORT_SYMBOL_GPL(reset_control_put); @@ -1153,10 +1307,8 @@ EXPORT_SYMBOL_GPL(reset_control_put); */ void reset_control_bulk_put(int num_rstcs, struct reset_control_bulk_data *rstcs) { - mutex_lock(&reset_list_mutex); while (num_rstcs--) - __reset_control_put_internal(rstcs[num_rstcs].rstc); - mutex_unlock(&reset_list_mutex); + reset_control_put_internal(rstcs[num_rstcs].rstc); } EXPORT_SYMBOL_GPL(reset_control_bulk_put); @@ -1317,21 +1469,35 @@ EXPORT_SYMBOL_GPL(__device_reset); */ /** - * of_reset_control_get_count - Count number of resets available with a device + * fwnode_reset_control_get_count - Count number of resets available with a device * - * @node: device node that contains 'resets'. + * @fwnode: firmware node that contains 'resets'. * * Returns positive reset count on success, or error number on failure and * on count being zero. */ -static int of_reset_control_get_count(struct device_node *node) +static int fwnode_reset_control_get_count(struct fwnode_handle *fwnode) { - int count; + struct fwnode_reference_args args; + int count = 0, ret; - if (!node) + if (!fwnode) return -EINVAL; - count = of_count_phandle_with_args(node, "resets", "#reset-cells"); + for (;;) { + ret = fwnode_property_get_reference_args(fwnode, "resets", "#reset-cells", + 0, count, &args); + if (ret) { + if (ret == -ENOENT) + break; + + return ret; + } + + fwnode_handle_put(args.fwnode); + count++; + } + if (count == 0) count = -ENOENT; @@ -1339,23 +1505,24 @@ static int of_reset_control_get_count(struct device_node *node) } /** - * of_reset_control_array_get - Get a list of reset controls using - * device node. + * fwnode_reset_control_array_get - Get a list of reset controls using + * a firmware node. * - * @np: device node for the device that requests the reset controls array + * @fwnode: firmware node for the device that requests the reset controls array * @flags: whether reset controls are shared, optional, acquired * * Returns pointer to allocated reset_control on success or error on failure */ struct reset_control * -of_reset_control_array_get(struct device_node *np, enum reset_control_flags flags) +fwnode_reset_control_array_get(struct fwnode_handle *fwnode, + enum reset_control_flags flags) { bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; struct reset_control_array *resets; struct reset_control *rstc; int num, i; - num = of_reset_control_get_count(np); + num = fwnode_reset_control_get_count(fwnode); if (num < 0) return optional ? NULL : ERR_PTR(num); @@ -1365,7 +1532,7 @@ of_reset_control_array_get(struct device_node *np, enum reset_control_flags flag resets->num_rstcs = num; for (i = 0; i < num; i++) { - rstc = __of_reset_control_get(np, NULL, i, flags); + rstc = __fwnode_reset_control_get(fwnode, NULL, i, flags); if (IS_ERR(rstc)) goto err_rst; resets->rstc[i] = rstc; @@ -1375,16 +1542,14 @@ of_reset_control_array_get(struct device_node *np, enum reset_control_flags flag return &resets->base; err_rst: - mutex_lock(&reset_list_mutex); while (--i >= 0) - __reset_control_put_internal(resets->rstc[i]); - mutex_unlock(&reset_list_mutex); + reset_control_put_internal(resets->rstc[i]); kfree(resets); return rstc; } -EXPORT_SYMBOL_GPL(of_reset_control_array_get); +EXPORT_SYMBOL_GPL(fwnode_reset_control_array_get); /** * devm_reset_control_array_get - Resource managed reset control array get @@ -1408,7 +1573,7 @@ devm_reset_control_array_get(struct device *dev, enum reset_control_flags flags) if (!ptr) return ERR_PTR(-ENOMEM); - rstc = of_reset_control_array_get(dev->of_node, flags); + rstc = fwnode_reset_control_array_get(dev_fwnode(dev), flags); if (IS_ERR_OR_NULL(rstc)) { devres_free(ptr); return rstc; @@ -1431,8 +1596,10 @@ EXPORT_SYMBOL_GPL(devm_reset_control_array_get); */ int reset_control_get_count(struct device *dev) { - if (dev->of_node) - return of_reset_control_get_count(dev->of_node); + struct fwnode_handle *fwnode = dev_fwnode(dev); + + if (fwnode) + return fwnode_reset_control_get_count(fwnode); return -ENOENT; } diff --git a/drivers/reset/reset-ath79.c b/drivers/reset/reset-ath79.c index b5d620132052..4c4e69eb32bb 100644 --- a/drivers/reset/reset-ath79.c +++ b/drivers/reset/reset-ath79.c @@ -15,7 +15,6 @@ struct ath79_reset { struct reset_controller_dev rcdev; - struct notifier_block restart_nb; void __iomem *base; spinlock_t lock; }; @@ -72,11 +71,9 @@ static const struct reset_control_ops ath79_reset_ops = { .status = ath79_reset_status, }; -static int ath79_reset_restart_handler(struct notifier_block *nb, - unsigned long action, void *data) +static int ath79_reset_restart_handler(struct sys_off_data *data) { - struct ath79_reset *ath79_reset = - container_of(nb, struct ath79_reset, restart_nb); + struct ath79_reset *ath79_reset = data->cb_data; ath79_reset_assert(&ath79_reset->rcdev, FULL_CHIP_RESET); @@ -108,10 +105,7 @@ static int ath79_reset_probe(struct platform_device *pdev) if (err) return err; - ath79_reset->restart_nb.notifier_call = ath79_reset_restart_handler; - ath79_reset->restart_nb.priority = 128; - - err = register_restart_handler(&ath79_reset->restart_nb); + err = devm_register_restart_handler(&pdev->dev, ath79_reset_restart_handler, ath79_reset); if (err) dev_warn(&pdev->dev, "Failed to register restart handler\n"); diff --git a/drivers/reset/reset-eyeq.c b/drivers/reset/reset-eyeq.c index 2d3998368a1c..1a3857983897 100644 --- a/drivers/reset/reset-eyeq.c +++ b/drivers/reset/reset-eyeq.c @@ -49,6 +49,18 @@ * 8. MPC0 9. MPC1 10. MPC2 11. MPC3 * 12. MPC4 * + * Known resets in EyeQ6Lplus domain 0 (type EQR_EYEQ5_PCIE): + * 0. SPI0 1. SPI1 2. UART0 3. I2C0 + * 4. I2C1 5. TIMER0 6. TIMER1 7. TIMER2 + * 8. TIMER3 9. WD0 10. WD1 11. EXT0 + * 12. EXT1 13. GPIO + * + * Known resets in EyeQ6Lplus domain 1 (type EQR_EYEQ5_ACRP): + * 0. VMP0 1. VMP1 2. VMP2 3. VMP3 + * 4. PMA0 5. PMA1 6. PMAC0 7. PMAC1 + * 8. MPC0 9. MPC1 10. MPC2 11. MPC3 + * 12. MPC4 + * * Known resets in EyeQ6H west/east (type EQR_EYEQ6H_SARCR): * 0. CAN 1. SPI0 2. SPI1 3. UART0 * 4. UART1 5. I2C0 6. I2C1 7. -hole- @@ -410,13 +422,6 @@ static int eqr_of_xlate_twocells(struct reset_controller_dev *rcdev, return eqr_of_xlate_internal(rcdev, reset_spec->args[0], reset_spec->args[1]); } -static void eqr_of_node_put(void *_dev) -{ - struct device *dev = _dev; - - of_node_put(dev->of_node); -} - static int eqr_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { @@ -427,21 +432,8 @@ static int eqr_probe(struct auxiliary_device *adev, int ret; /* - * We are an auxiliary device of clk-eyeq. We do not have an OF node by - * default; let's reuse our parent's OF node. - */ - WARN_ON(dev->of_node); - device_set_of_node_from_dev(dev, dev->parent); - if (!dev->of_node) - return -ENODEV; - - ret = devm_add_action_or_reset(dev, eqr_of_node_put, dev); - if (ret) - return ret; - - /* - * Using our newfound OF node, we can get match data. We cannot use - * device_get_match_data() because it does not match reused OF nodes. + * Get match data. We cannot use device_get_match_data() because it does + * not accept reused OF nodes; see device_set_of_node_from_dev(). */ match = of_match_node(dev->driver->of_match_table, dev->of_node); if (!match || !match->data) @@ -521,6 +513,24 @@ static const struct eqr_match_data eqr_eyeq6l_data = { .domains = eqr_eyeq6l_domains, }; +static const struct eqr_domain_descriptor eqr_eyeq6lplus_domains[] = { + { + .type = EQR_EYEQ5_PCIE, + .valid_mask = 0x3FFF, + .offset = 0x004, + }, + { + .type = EQR_EYEQ5_ACRP, + .valid_mask = 0x00FF, + .offset = 0x200, + }, +}; + +static const struct eqr_match_data eqr_eyeq6lplus_data = { + .domain_count = ARRAY_SIZE(eqr_eyeq6lplus_domains), + .domains = eqr_eyeq6lplus_domains, +}; + /* West and east OLBs each have an instance. */ static const struct eqr_domain_descriptor eqr_eyeq6h_we_domains[] = { { @@ -555,6 +565,7 @@ static const struct eqr_match_data eqr_eyeq6h_acc_data = { static const struct of_device_id eqr_match_table[] = { { .compatible = "mobileye,eyeq5-olb", .data = &eqr_eyeq5_data }, { .compatible = "mobileye,eyeq6l-olb", .data = &eqr_eyeq6l_data }, + { .compatible = "mobileye,eyeq6lplus-olb", .data = &eqr_eyeq6lplus_data }, { .compatible = "mobileye,eyeq6h-west-olb", .data = &eqr_eyeq6h_we_data }, { .compatible = "mobileye,eyeq6h-east-olb", .data = &eqr_eyeq6h_we_data }, { .compatible = "mobileye,eyeq6h-acc-olb", .data = &eqr_eyeq6h_acc_data }, diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c index 0a1610d9e78a..26aa2c3a2e68 100644 --- a/drivers/reset/reset-gpio.c +++ b/drivers/reset/reset-gpio.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include struct reset_gpio_priv { @@ -46,34 +46,22 @@ static const struct reset_control_ops reset_gpio_ops = { .status = reset_gpio_status, }; -static int reset_gpio_of_xlate(struct reset_controller_dev *rcdev, - const struct of_phandle_args *reset_spec) +static int reset_gpio_fwnode_xlate(struct reset_controller_dev *rcdev, + const struct fwnode_reference_args *reset_spec) { return reset_spec->args[0]; } -static void reset_gpio_of_node_put(void *data) -{ - of_node_put(data); -} - static int reset_gpio_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { struct device *dev = &adev->dev; - struct of_phandle_args *platdata = dev_get_platdata(dev); struct reset_gpio_priv *priv; - int ret; - - if (!platdata) - return -EINVAL; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - auxiliary_set_drvdata(adev, &priv->rc); - priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(priv->reset)) return dev_err_probe(dev, PTR_ERR(priv->reset), @@ -82,15 +70,10 @@ static int reset_gpio_probe(struct auxiliary_device *adev, priv->rc.ops = &reset_gpio_ops; priv->rc.owner = THIS_MODULE; priv->rc.dev = dev; - priv->rc.of_args = platdata; - ret = devm_add_action_or_reset(dev, reset_gpio_of_node_put, - priv->rc.of_node); - if (ret) - return ret; /* Cells to match GPIO specifier, but it's not really used */ - priv->rc.of_reset_n_cells = 2; - priv->rc.of_xlate = reset_gpio_of_xlate; + priv->rc.fwnode_reset_n_cells = 2; + priv->rc.fwnode_xlate = reset_gpio_fwnode_xlate; priv->rc.nr_resets = 1; return devm_reset_controller_register(dev, &priv->rc); diff --git a/drivers/reset/reset-intel-gw.c b/drivers/reset/reset-intel-gw.c index a5ce3350cb5e..0db64cc8a282 100644 --- a/drivers/reset/reset-intel-gw.c +++ b/drivers/reset/reset-intel-gw.c @@ -28,7 +28,6 @@ struct intel_reset_soc { struct intel_reset_data { struct reset_controller_dev rcdev; - struct notifier_block restart_nb; const struct intel_reset_soc *soc_data; struct regmap *regmap; struct device *dev; @@ -153,12 +152,10 @@ static int intel_reset_xlate(struct reset_controller_dev *rcdev, return id; } -static int intel_reset_restart_handler(struct notifier_block *nb, - unsigned long action, void *data) +static int intel_reset_restart_handler(struct sys_off_data *data) { - struct intel_reset_data *reset_data; + struct intel_reset_data *reset_data = data->cb_data; - reset_data = container_of(nb, struct intel_reset_data, restart_nb); intel_assert_device(&reset_data->rcdev, reset_data->reboot_id); return NOTIFY_DONE; @@ -215,9 +212,7 @@ static int intel_reset_probe(struct platform_device *pdev) if (data->soc_data->legacy) data->reboot_id |= FIELD_PREP(STAT_BIT_OFFSET_MASK, rb_id[2]); - data->restart_nb.notifier_call = intel_reset_restart_handler; - data->restart_nb.priority = 128; - register_restart_handler(&data->restart_nb); + devm_register_restart_handler(&pdev->dev, intel_reset_restart_handler, data); return 0; } diff --git a/drivers/reset/reset-lpc18xx.c b/drivers/reset/reset-lpc18xx.c index e42b2f24a93d..8ac9f237e1ce 100644 --- a/drivers/reset/reset-lpc18xx.c +++ b/drivers/reset/reset-lpc18xx.c @@ -31,7 +31,6 @@ struct lpc18xx_rgu_data { struct reset_controller_dev rcdev; - struct notifier_block restart_nb; struct clk *clk_delay; struct clk *clk_reg; void __iomem *base; @@ -41,11 +40,9 @@ struct lpc18xx_rgu_data { #define to_rgu_data(p) container_of(p, struct lpc18xx_rgu_data, rcdev) -static int lpc18xx_rgu_restart(struct notifier_block *nb, unsigned long mode, - void *cmd) +static int lpc18xx_rgu_restart(struct sys_off_data *data) { - struct lpc18xx_rgu_data *rc = container_of(nb, struct lpc18xx_rgu_data, - restart_nb); + struct lpc18xx_rgu_data *rc = data->cb_data; writel(BIT(LPC18XX_RGU_CORE_RST), rc->base + LPC18XX_RGU_CTRL0); mdelay(2000); @@ -178,9 +175,8 @@ static int lpc18xx_rgu_probe(struct platform_device *pdev) if (ret) return dev_err_probe(&pdev->dev, ret, "unable to register device\n"); - rc->restart_nb.priority = 192, - rc->restart_nb.notifier_call = lpc18xx_rgu_restart, - ret = register_restart_handler(&rc->restart_nb); + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, 192, + lpc18xx_rgu_restart, rc); if (ret) dev_warn(&pdev->dev, "failed to register restart handler\n"); diff --git a/drivers/reset/reset-ma35d1.c b/drivers/reset/reset-ma35d1.c index 54e53863c98a..4ee901f00132 100644 --- a/drivers/reset/reset-ma35d1.c +++ b/drivers/reset/reset-ma35d1.c @@ -19,7 +19,6 @@ struct ma35d1_reset_data { struct reset_controller_dev rcdev; - struct notifier_block restart_handler; void __iomem *base; /* protect registers against concurrent read-modify-write */ spinlock_t lock; @@ -125,10 +124,9 @@ static const struct { [MA35D1_RESET_SSPCC] = {0x2C, 31} }; -static int ma35d1_restart_handler(struct notifier_block *this, unsigned long mode, void *cmd) +static int ma35d1_restart_handler(struct sys_off_data *sys_off_data) { - struct ma35d1_reset_data *data = - container_of(this, struct ma35d1_reset_data, restart_handler); + struct ma35d1_reset_data *data = sys_off_data->cb_data; u32 id = MA35D1_RESET_CHIP; writel_relaxed(BIT(ma35d1_reset_map[id].bit), @@ -213,11 +211,10 @@ static int ma35d1_reset_probe(struct platform_device *pdev) reset_data->rcdev.nr_resets = MA35D1_RESET_COUNT; reset_data->rcdev.ops = &ma35d1_reset_ops; reset_data->rcdev.of_node = dev->of_node; - reset_data->restart_handler.notifier_call = ma35d1_restart_handler; - reset_data->restart_handler.priority = 192; spin_lock_init(&reset_data->lock); - err = register_restart_handler(&reset_data->restart_handler); + err = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART, 192, + ma35d1_restart_handler, reset_data); if (err) dev_warn(&pdev->dev, "failed to register restart handler\n"); diff --git a/drivers/reset/reset-npcm.c b/drivers/reset/reset-npcm.c index 4070a4b6663f..a762d0215e76 100644 --- a/drivers/reset/reset-npcm.c +++ b/drivers/reset/reset-npcm.c @@ -89,7 +89,6 @@ static const struct npcm_reset_info npxm8xx_reset_info[] = { struct npcm_rc_data { struct reset_controller_dev rcdev; - struct notifier_block restart_nb; const struct npcm_reset_info *info; struct regmap *gcr_regmap; u32 sw_reset_number; @@ -100,11 +99,9 @@ struct npcm_rc_data { #define to_rc_data(p) container_of(p, struct npcm_rc_data, rcdev) -static int npcm_rc_restart(struct notifier_block *nb, unsigned long mode, - void *cmd) +static int npcm_rc_restart(struct sys_off_data *data) { - struct npcm_rc_data *rc = container_of(nb, struct npcm_rc_data, - restart_nb); + struct npcm_rc_data *rc = data->cb_data; writel(NPCM_SWRST << rc->sw_reset_number, rc->base + NPCM_SWRSTR); mdelay(1000); @@ -472,9 +469,8 @@ static int npcm_rc_probe(struct platform_device *pdev) if (!of_property_read_u32(pdev->dev.of_node, "nuvoton,sw-reset-number", &rc->sw_reset_number)) { if (rc->sw_reset_number && rc->sw_reset_number < 5) { - rc->restart_nb.priority = 192; - rc->restart_nb.notifier_call = npcm_rc_restart; - ret = register_restart_handler(&rc->restart_nb); + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, 192, + npcm_rc_restart, rc); if (ret) { dev_warn(&pdev->dev, "failed to register restart handler\n"); return ret; diff --git a/drivers/reset/reset-rzv2h-usb2phy.c b/drivers/reset/reset-rzv2h-usb2phy.c index ae643575b067..d96042e28cd5 100644 --- a/drivers/reset/reset-rzv2h-usb2phy.c +++ b/drivers/reset/reset-rzv2h-usb2phy.c @@ -5,42 +5,39 @@ * Copyright (C) 2025 Renesas Electronics Corporation */ -#include +#include #include +#include #include #include #include #include #include +#include #include #include -struct rzv2h_usb2phy_regval { - u16 reg; - u16 val; -}; +static DEFINE_IDA(auxiliary_ids); struct rzv2h_usb2phy_reset_of_data { - const struct rzv2h_usb2phy_regval *init_vals; - unsigned int init_val_count; + const struct reg_sequence *init_seq; + unsigned int init_nseq; + + const struct reg_sequence *assert_seq; + unsigned int assert_nseq; + + const struct reg_sequence *deassert_seq; + unsigned int deassert_nseq; u16 reset_reg; - u16 reset_assert_val; - u16 reset_deassert_val; u16 reset_status_bits; - u16 reset_release_val; - - u16 reset2_reg; - u16 reset2_acquire_val; - u16 reset2_release_val; }; struct rzv2h_usb2phy_reset_priv { const struct rzv2h_usb2phy_reset_of_data *data; - void __iomem *base; + struct regmap *regmap; struct device *dev; struct reset_controller_dev rcdev; - spinlock_t lock; /* protects register accesses */ }; static inline struct rzv2h_usb2phy_reset_priv @@ -49,81 +46,31 @@ static inline struct rzv2h_usb2phy_reset_priv return container_of(rcdev, struct rzv2h_usb2phy_reset_priv, rcdev); } -/* This function must be called only after pm_runtime_resume_and_get() has been called */ -static void rzv2h_usbphy_assert_helper(struct rzv2h_usb2phy_reset_priv *priv) -{ - const struct rzv2h_usb2phy_reset_of_data *data = priv->data; - - scoped_guard(spinlock, &priv->lock) { - writel(data->reset2_acquire_val, priv->base + data->reset2_reg); - writel(data->reset_assert_val, priv->base + data->reset_reg); - } - - usleep_range(11, 20); -} - static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) { struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev); - struct device *dev = priv->dev; - int ret; - ret = pm_runtime_resume_and_get(dev); - if (ret) { - dev_err(dev, "pm_runtime_resume_and_get failed\n"); - return ret; - } - - rzv2h_usbphy_assert_helper(priv); - - pm_runtime_put(dev); - - return 0; + return regmap_multi_reg_write(priv->regmap, priv->data->assert_seq, + priv->data->assert_nseq); } static int rzv2h_usbphy_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) { struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev); - const struct rzv2h_usb2phy_reset_of_data *data = priv->data; - struct device *dev = priv->dev; - int ret; - ret = pm_runtime_resume_and_get(dev); - if (ret) { - dev_err(dev, "pm_runtime_resume_and_get failed\n"); - return ret; - } - - scoped_guard(spinlock, &priv->lock) { - writel(data->reset_deassert_val, priv->base + data->reset_reg); - writel(data->reset2_release_val, priv->base + data->reset2_reg); - writel(data->reset_release_val, priv->base + data->reset_reg); - } - - pm_runtime_put(dev); - - return 0; + return regmap_multi_reg_write(priv->regmap, priv->data->deassert_seq, + priv->data->deassert_nseq); } static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev, unsigned long id) { struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev); - struct device *dev = priv->dev; - int ret; u32 reg; - ret = pm_runtime_resume_and_get(dev); - if (ret) { - dev_err(dev, "pm_runtime_resume_and_get failed\n"); - return ret; - } - - reg = readl(priv->base + priv->data->reset_reg); - - pm_runtime_put(dev); + regmap_read(priv->regmap, priv->data->reset_reg, ®); return (reg & priv->data->reset_status_bits) == priv->data->reset_status_bits; } @@ -141,12 +88,52 @@ static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev, return 0; } +static void rzv2h_usb2phy_reset_ida_free(void *data) +{ + struct auxiliary_device *adev = data; + + ida_free(&auxiliary_ids, adev->id); +} + +static int rzv2h_usb2phy_reset_mux_register(struct device *dev, + const char *mux_name) +{ + struct auxiliary_device *adev; + int id; + + id = ida_alloc(&auxiliary_ids, GFP_KERNEL); + if (id < 0) + return id; + + adev = __devm_auxiliary_device_create(dev, dev->driver->name, + mux_name, NULL, id); + if (!adev) { + ida_free(&auxiliary_ids, id); + return -ENOMEM; + } + + return devm_add_action_or_reset(dev, rzv2h_usb2phy_reset_ida_free, adev); +} + +static const struct regmap_config rzv2h_usb2phy_reset_regconf = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .can_sleep = true, +}; + +static void rzv2h_usb2phy_reset_pm_runtime_put(void *data) +{ + pm_runtime_put(data); +} + static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) { const struct rzv2h_usb2phy_reset_of_data *data; struct rzv2h_usb2phy_reset_priv *priv; struct device *dev = &pdev->dev; struct reset_control *rstc; + void __iomem *base; int error; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -156,17 +143,19 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) data = of_device_get_match_data(dev); priv->data = data; priv->dev = dev; - priv->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, &rzv2h_usb2phy_reset_regconf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); rstc = devm_reset_control_get_shared_deasserted(dev, NULL); if (IS_ERR(rstc)) return dev_err_probe(dev, PTR_ERR(rstc), "failed to get deasserted reset\n"); - spin_lock_init(&priv->lock); - error = devm_pm_runtime_enable(dev); if (error) return dev_err_probe(dev, error, "Failed to enable pm_runtime\n"); @@ -175,13 +164,14 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) if (error) return dev_err_probe(dev, error, "pm_runtime_resume_and_get failed\n"); - for (unsigned int i = 0; i < data->init_val_count; i++) - writel(data->init_vals[i].val, priv->base + data->init_vals[i].reg); + error = devm_add_action_or_reset(dev, rzv2h_usb2phy_reset_pm_runtime_put, + dev); + if (error) + return dev_err_probe(dev, error, "unable to register cleanup action\n"); - /* keep usb2phy in asserted state */ - rzv2h_usbphy_assert_helper(priv); - - pm_runtime_put(dev); + error = regmap_multi_reg_write(priv->regmap, data->init_seq, data->init_nseq); + if (error) + return dev_err_probe(dev, error, "failed to initialize PHY registers\n"); priv->rcdev.ops = &rzv2h_usbphy_reset_ops; priv->rcdev.of_reset_n_cells = 0; @@ -190,30 +180,47 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) priv->rcdev.of_node = dev->of_node; priv->rcdev.dev = dev; - return devm_reset_controller_register(dev, &priv->rcdev); + error = devm_reset_controller_register(dev, &priv->rcdev); + if (error) + return dev_err_probe(dev, error, "could not register reset controller\n"); + + error = rzv2h_usb2phy_reset_mux_register(dev, "vbenctl"); + if (error) + return dev_err_probe(dev, error, "could not register aux mux\n"); + + return 0; } /* * initialization values required to prepare the PHY to receive * assert and deassert requests. */ -static const struct rzv2h_usb2phy_regval rzv2h_init_vals[] = { - { .reg = 0xc10, .val = 0x67c }, - { .reg = 0xc14, .val = 0x1f }, - { .reg = 0x600, .val = 0x909 }, +static const struct reg_sequence rzv2h_init_seq[] = { + { .reg = 0xc10, .def = 0x67c }, + { .reg = 0xc14, .def = 0x01f }, + { .reg = 0x600, .def = 0x909 }, +}; + +static const struct reg_sequence rzv2h_assert_seq[] = { + { .reg = 0xb04, .def = 0x303 }, + { .reg = 0x000, .def = 0x206, .delay_us = 11 }, +}; + +static const struct reg_sequence rzv2h_deassert_seq[] = { + { .reg = 0x000, .def = 0x200 }, + { .reg = 0xb04, .def = 0x003 }, + { .reg = 0x000, .def = 0x000 }, }; static const struct rzv2h_usb2phy_reset_of_data rzv2h_reset_of_data = { - .init_vals = rzv2h_init_vals, - .init_val_count = ARRAY_SIZE(rzv2h_init_vals), + .init_seq = rzv2h_init_seq, + .init_nseq = ARRAY_SIZE(rzv2h_init_seq), + .assert_seq = rzv2h_assert_seq, + .assert_nseq = ARRAY_SIZE(rzv2h_assert_seq), + .deassert_seq = rzv2h_deassert_seq, + .deassert_nseq = ARRAY_SIZE(rzv2h_deassert_seq), .reset_reg = 0, - .reset_assert_val = 0x206, .reset_status_bits = BIT(2), - .reset_deassert_val = 0x200, - .reset_release_val = 0x0, - .reset2_reg = 0xb04, - .reset2_acquire_val = 0x303, - .reset2_release_val = 0x3, }; static const struct of_device_id rzv2h_usb2phy_reset_of_match[] = { diff --git a/drivers/reset/reset-sky1.c b/drivers/reset/reset-sky1.c new file mode 100644 index 000000000000..78e80a533c39 --- /dev/null +++ b/drivers/reset/reset-sky1.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * + * CIX System Reset Controller (SRC) driver + * + * Author: Jerry Zhu + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define SKY1_RESET_SLEEP_MIN_US 50 +#define SKY1_RESET_SLEEP_MAX_US 100 + +struct sky1_src_signal { + unsigned int offset; + unsigned int bit; +}; + +struct sky1_src_variant { + const struct sky1_src_signal *signals; + unsigned int signals_num; +}; + +struct sky1_src { + struct reset_controller_dev rcdev; + const struct sky1_src_signal *signals; + struct regmap *regmap; +}; + +enum { + CSU_PM_RESET = 0x304, + SENSORFUSION_RESET = 0x308, + SENSORFUSION_NOC_RESET = 0x30c, + RESET_GROUP0_S0_DOMAIN_0 = 0x400, + RESET_GROUP0_S0_DOMAIN_1 = 0x404, + RESET_GROUP1_USB_PHYS = 0x408, + RESET_GROUP1_USB_CONTROLLERS = 0x40c, + RESET_GROUP0_RCSU = 0x800, + RESET_GROUP1_RCSU = 0x804, +}; + +static const struct sky1_src_signal sky1_src_signals[] = { + /* reset group1 for s0 domain modules */ + [SKY1_CSU_PM_RESET_N] = { CSU_PM_RESET, BIT(0) }, + [SKY1_SENSORFUSION_RESET_N] = { SENSORFUSION_RESET, BIT(0) }, + [SKY1_SENSORFUSION_NOC_RESET_N] = { SENSORFUSION_NOC_RESET, BIT(0) }, + [SKY1_DDRC_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(0) }, + [SKY1_GIC_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(1) }, + [SKY1_CI700_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(2) }, + [SKY1_SYS_NI700_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(3) }, + [SKY1_MM_NI700_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(4) }, + [SKY1_PCIE_NI700_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(5) }, + [SKY1_GPU_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(6) }, + [SKY1_NPUTOP_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(7) }, + [SKY1_NPUCORE0_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(8) }, + [SKY1_NPUCORE1_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(9) }, + [SKY1_NPUCORE2_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(10) }, + [SKY1_VPU_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(11) }, + [SKY1_ISP_SRESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(12) }, + [SKY1_ISP_ARESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(13) }, + [SKY1_ISP_HRESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(14) }, + [SKY1_ISP_GDCRESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(15) }, + [SKY1_DPU_RESET0_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(16) }, + [SKY1_DPU_RESET1_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(17) }, + [SKY1_DPU_RESET2_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(18) }, + [SKY1_DPU_RESET3_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(19) }, + [SKY1_DPU_RESET4_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(20) }, + [SKY1_DP_RESET0_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(21) }, + [SKY1_DP_RESET1_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(22) }, + [SKY1_DP_RESET2_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(23) }, + [SKY1_DP_RESET3_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(24) }, + [SKY1_DP_RESET4_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(25) }, + [SKY1_DP_PHY_RST_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(26) }, + + /* reset group1 for s0 domain modules */ + [SKY1_AUDIO_HIFI5_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(0) }, + [SKY1_AUDIO_HIFI5_NOC_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(1) }, + [SKY1_CSIDPHY_PRST0_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(2) }, + [SKY1_CSIDPHY_CMNRST0_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(3) }, + [SKY1_CSI0_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(4) }, + [SKY1_CSIDPHY_PRST1_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(5) }, + [SKY1_CSIDPHY_CMNRST1_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(6) }, + [SKY1_CSI1_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(7) }, + [SKY1_CSI2_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(8) }, + [SKY1_CSI3_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(9) }, + [SKY1_CSIBRDGE0_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(10) }, + [SKY1_CSIBRDGE1_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(11) }, + [SKY1_CSIBRDGE2_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(12) }, + [SKY1_CSIBRDGE3_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(13) }, + [SKY1_GMAC0_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(14) }, + [SKY1_GMAC1_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(15) }, + [SKY1_PCIE0_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(16) }, + [SKY1_PCIE1_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(17) }, + [SKY1_PCIE2_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(18) }, + [SKY1_PCIE3_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(19) }, + [SKY1_PCIE4_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(20) }, + + /* reset group1 for usb phys */ + [SKY1_USB_DP_PHY0_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(0) }, + [SKY1_USB_DP_PHY1_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(1) }, + [SKY1_USB_DP_PHY2_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(2) }, + [SKY1_USB_DP_PHY3_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(3) }, + [SKY1_USB_DP_PHY0_RST_N] = { RESET_GROUP1_USB_PHYS, BIT(4) }, + [SKY1_USB_DP_PHY1_RST_N] = { RESET_GROUP1_USB_PHYS, BIT(5) }, + [SKY1_USB_DP_PHY2_RST_N] = { RESET_GROUP1_USB_PHYS, BIT(6) }, + [SKY1_USB_DP_PHY3_RST_N] = { RESET_GROUP1_USB_PHYS, BIT(7) }, + [SKY1_USBPHY_SS_PST_N] = { RESET_GROUP1_USB_PHYS, BIT(8) }, + [SKY1_USBPHY_SS_RST_N] = { RESET_GROUP1_USB_PHYS, BIT(9) }, + [SKY1_USBPHY_HS0_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(10) }, + [SKY1_USBPHY_HS1_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(11) }, + [SKY1_USBPHY_HS2_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(12) }, + [SKY1_USBPHY_HS3_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(13) }, + [SKY1_USBPHY_HS4_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(14) }, + [SKY1_USBPHY_HS5_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(15) }, + [SKY1_USBPHY_HS6_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(16) }, + [SKY1_USBPHY_HS7_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(17) }, + [SKY1_USBPHY_HS8_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(18) }, + [SKY1_USBPHY_HS9_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(19) }, + + /* reset group1 for usb controllers */ + [SKY1_USBC_SS0_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(0) }, + [SKY1_USBC_SS1_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(1) }, + [SKY1_USBC_SS2_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(2) }, + [SKY1_USBC_SS3_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(3) }, + [SKY1_USBC_SS4_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(4) }, + [SKY1_USBC_SS5_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(5) }, + [SKY1_USBC_SS0_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(6) }, + [SKY1_USBC_SS1_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(7) }, + [SKY1_USBC_SS2_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(8) }, + [SKY1_USBC_SS3_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(9) }, + [SKY1_USBC_SS4_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(10) }, + [SKY1_USBC_SS5_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(11) }, + [SKY1_USBC_HS0_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(12) }, + [SKY1_USBC_HS1_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(13) }, + [SKY1_USBC_HS2_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(14) }, + [SKY1_USBC_HS3_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(15) }, + [SKY1_USBC_HS0_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(16) }, + [SKY1_USBC_HS1_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(17) }, + [SKY1_USBC_HS2_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(18) }, + [SKY1_USBC_HS3_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(19) }, + + /* reset group0 for rcsu */ + [SKY1_AUDIO_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(0) }, + [SKY1_CI700_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(1) }, + [SKY1_CSI_RCSU0_RESET_N] = { RESET_GROUP0_RCSU, BIT(2) }, + [SKY1_CSI_RCSU1_RESET_N] = { RESET_GROUP0_RCSU, BIT(3) }, + [SKY1_CSU_PM_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(4) }, + [SKY1_DDR_BROADCAST_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(5) }, + [SKY1_DDR_CTRL_RCSU_0_RESET_N] = { RESET_GROUP0_RCSU, BIT(6) }, + [SKY1_DDR_CTRL_RCSU_1_RESET_N] = { RESET_GROUP0_RCSU, BIT(7) }, + [SKY1_DDR_CTRL_RCSU_2_RESET_N] = { RESET_GROUP0_RCSU, BIT(8) }, + [SKY1_DDR_CTRL_RCSU_3_RESET_N] = { RESET_GROUP0_RCSU, BIT(9) }, + [SKY1_DDR_TZC400_RCSU_0_RESET_N] = { RESET_GROUP0_RCSU, BIT(10) }, + [SKY1_DDR_TZC400_RCSU_1_RESET_N] = { RESET_GROUP0_RCSU, BIT(11) }, + [SKY1_DDR_TZC400_RCSU_2_RESET_N] = { RESET_GROUP0_RCSU, BIT(12) }, + [SKY1_DDR_TZC400_RCSU_3_RESET_N] = { RESET_GROUP0_RCSU, BIT(13) }, + [SKY1_DP0_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(14) }, + [SKY1_DP1_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(15) }, + [SKY1_DP2_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(16) }, + [SKY1_DP3_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(17) }, + [SKY1_DP4_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(18) }, + [SKY1_DPU0_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(19) }, + [SKY1_DPU1_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(20) }, + [SKY1_DPU2_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(21) }, + [SKY1_DPU3_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(22) }, + [SKY1_DPU4_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(23) }, + [SKY1_DSU_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(24) }, + [SKY1_FCH_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(25) }, + [SKY1_GICD_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(26) }, + [SKY1_GMAC_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(27) }, + [SKY1_GPU_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(28) }, + [SKY1_ISP_RCSU0_RESET_N] = { RESET_GROUP0_RCSU, BIT(29) }, + [SKY1_ISP_RCSU1_RESET_N] = { RESET_GROUP0_RCSU, BIT(30) }, + [SKY1_NI700_MMHUB_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(31) }, + + /* reset group1 for rcsu */ + [SKY1_NPU_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(0) }, + [SKY1_NI700_PCIE_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(1) }, + [SKY1_PCIE_X421_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(2) }, + [SKY1_PCIE_X8_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(3) }, + [SKY1_SF_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(4) }, + [SKY1_RCSU_SMMU_MMHUB_RESET_N] = { RESET_GROUP1_RCSU, BIT(5) }, + [SKY1_RCSU_SMMU_PCIEHUB_RESET_N] = { RESET_GROUP1_RCSU, BIT(6) }, + [SKY1_RCSU_SYSHUB_RESET_N] = { RESET_GROUP1_RCSU, BIT(7) }, + [SKY1_NI700_SMN_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(8) }, + [SKY1_NI700_SYSHUB_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(9) }, + [SKY1_RCSU_USB2_HOST0_RESET_N] = { RESET_GROUP1_RCSU, BIT(10) }, + [SKY1_RCSU_USB2_HOST1_RESET_N] = { RESET_GROUP1_RCSU, BIT(11) }, + [SKY1_RCSU_USB2_HOST2_RESET_N] = { RESET_GROUP1_RCSU, BIT(12) }, + [SKY1_RCSU_USB2_HOST3_RESET_N] = { RESET_GROUP1_RCSU, BIT(13) }, + [SKY1_RCSU_USB3_TYPEA_DRD_RESET_N] = { RESET_GROUP1_RCSU, BIT(14) }, + [SKY1_RCSU_USB3_TYPEC_DRD_RESET_N] = { RESET_GROUP1_RCSU, BIT(15) }, + [SKY1_RCSU_USB3_TYPEC_HOST0_RESET_N] = { RESET_GROUP1_RCSU, BIT(16) }, + [SKY1_RCSU_USB3_TYPEC_HOST1_RESET_N] = { RESET_GROUP1_RCSU, BIT(17) }, + [SKY1_RCSU_USB3_TYPEC_HOST2_RESET_N] = { RESET_GROUP1_RCSU, BIT(18) }, + [SKY1_VPU_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(19) }, +}; + +static const struct sky1_src_variant variant_sky1 = { + .signals = sky1_src_signals, + .signals_num = ARRAY_SIZE(sky1_src_signals), +}; + +enum { + FCH_SW_RST_FUNC = 0x8, + FCH_SW_RST_BUS = 0xc, + FCH_SW_XSPI = 0x10, +}; + +static const struct sky1_src_signal sky1_src_fch_signals[] = { + /* resets for fch_sw_rst_func */ + [SW_I3C0_RST_FUNC_G_N] = { FCH_SW_RST_FUNC, BIT(0) }, + [SW_I3C0_RST_FUNC_I_N] = { FCH_SW_RST_FUNC, BIT(1) }, + [SW_I3C1_RST_FUNC_G_N] = { FCH_SW_RST_FUNC, BIT(2) }, + [SW_I3C1_RST_FUNC_I_N] = { FCH_SW_RST_FUNC, BIT(3) }, + [SW_UART0_RST_FUNC_N] = { FCH_SW_RST_FUNC, BIT(4) }, + [SW_UART1_RST_FUNC_N] = { FCH_SW_RST_FUNC, BIT(5) }, + [SW_UART2_RST_FUNC_N] = { FCH_SW_RST_FUNC, BIT(6) }, + [SW_UART3_RST_FUNC_N] = { FCH_SW_RST_FUNC, BIT(7) }, + [SW_TIMER_RST_FUNC_N] = { FCH_SW_RST_FUNC, BIT(20) }, + + /* resets for fch_sw_rst_bus */ + [SW_I3C0_RST_APB_N] = { FCH_SW_RST_BUS, BIT(0) }, + [SW_I3C1_RST_APB_N] = { FCH_SW_RST_BUS, BIT(1) }, + [SW_DMA_RST_AXI_N] = { FCH_SW_RST_BUS, BIT(2) }, + [SW_UART0_RST_APB_N] = { FCH_SW_RST_BUS, BIT(4) }, + [SW_UART1_RST_APB_N] = { FCH_SW_RST_BUS, BIT(5) }, + [SW_UART2_RST_APB_N] = { FCH_SW_RST_BUS, BIT(6) }, + [SW_UART3_RST_APB_N] = { FCH_SW_RST_BUS, BIT(7) }, + [SW_SPI0_RST_APB_N] = { FCH_SW_RST_BUS, BIT(8) }, + [SW_SPI1_RST_APB_N] = { FCH_SW_RST_BUS, BIT(9) }, + [SW_I2C0_RST_APB_N] = { FCH_SW_RST_BUS, BIT(12) }, + [SW_I2C1_RST_APB_N] = { FCH_SW_RST_BUS, BIT(13) }, + [SW_I2C2_RST_APB_N] = { FCH_SW_RST_BUS, BIT(14) }, + [SW_I2C3_RST_APB_N] = { FCH_SW_RST_BUS, BIT(15) }, + [SW_I2C4_RST_APB_N] = { FCH_SW_RST_BUS, BIT(16) }, + [SW_I2C5_RST_APB_N] = { FCH_SW_RST_BUS, BIT(17) }, + [SW_I2C6_RST_APB_N] = { FCH_SW_RST_BUS, BIT(18) }, + [SW_I2C7_RST_APB_N] = { FCH_SW_RST_BUS, BIT(19) }, + [SW_GPIO_RST_APB_N] = { FCH_SW_RST_BUS, BIT(21) }, + + /* resets for fch_sw_xspi */ + [SW_XSPI_REG_RST_N] = { FCH_SW_XSPI, BIT(0) }, + [SW_XSPI_SYS_RST_N] = { FCH_SW_XSPI, BIT(1) }, +}; + +static const struct sky1_src_variant variant_sky1_fch = { + .signals = sky1_src_fch_signals, + .signals_num = ARRAY_SIZE(sky1_src_fch_signals), +}; + +static struct sky1_src *to_sky1_src(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct sky1_src, rcdev); +} + +static int sky1_reset_set(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct sky1_src *sky1src = to_sky1_src(rcdev); + const struct sky1_src_signal *signal = &sky1src->signals[id]; + unsigned int value = assert ? 0 : signal->bit; + + return regmap_update_bits(sky1src->regmap, + signal->offset, signal->bit, value); +} + +static int sky1_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + sky1_reset_set(rcdev, id, true); + usleep_range(SKY1_RESET_SLEEP_MIN_US, + SKY1_RESET_SLEEP_MAX_US); + return 0; +} + +static int sky1_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + sky1_reset_set(rcdev, id, false); + usleep_range(SKY1_RESET_SLEEP_MIN_US, + SKY1_RESET_SLEEP_MAX_US); + return 0; +} + +static int sky1_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + sky1_reset_assert(rcdev, id); + sky1_reset_deassert(rcdev, id); + return 0; +} + +static int sky1_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + unsigned int value = 0; + struct sky1_src *sky1src = to_sky1_src(rcdev); + const struct sky1_src_signal *signal = &sky1src->signals[id]; + + regmap_read(sky1src->regmap, signal->offset, &value); + return !(value & signal->bit); +} + +static const struct reset_control_ops sky1_src_ops = { + .reset = sky1_reset, + .assert = sky1_reset_assert, + .deassert = sky1_reset_deassert, + .status = sky1_reset_status +}; + +static int sky1_reset_probe(struct platform_device *pdev) +{ + struct sky1_src *sky1src; + struct device *dev = &pdev->dev; + const struct sky1_src_variant *variant; + + sky1src = devm_kzalloc(dev, sizeof(*sky1src), GFP_KERNEL); + if (!sky1src) + return -ENOMEM; + + variant = of_device_get_match_data(dev); + + sky1src->regmap = device_node_to_regmap(dev->of_node); + if (IS_ERR(sky1src->regmap)) { + return dev_err_probe(dev, PTR_ERR(sky1src->regmap), + "Unable to get sky1-src regmap"); + } + + sky1src->signals = variant->signals; + sky1src->rcdev.owner = THIS_MODULE; + sky1src->rcdev.nr_resets = variant->signals_num; + sky1src->rcdev.ops = &sky1_src_ops; + sky1src->rcdev.of_node = dev->of_node; + sky1src->rcdev.dev = dev; + + return devm_reset_controller_register(dev, &sky1src->rcdev); +} + +static const struct of_device_id sky1_sysreg_of_match[] = { + { .compatible = "cix,sky1-system-control", .data = &variant_sky1_fch}, + { .compatible = "cix,sky1-s5-system-control", .data = &variant_sky1}, + {}, +}; +MODULE_DEVICE_TABLE(of, sky1_sysreg_of_match); + +static struct platform_driver sky1_reset_driver = { + .probe = sky1_reset_probe, + .driver = { + .name = "cix,sky1-rst", + .of_match_table = sky1_sysreg_of_match, + }, +}; +module_platform_driver(sky1_reset_driver) + +MODULE_AUTHOR("Jerry Zhu "); +MODULE_DESCRIPTION("Cix Sky1 reset driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/reset/reset-sunplus.c b/drivers/reset/reset-sunplus.c index df58decab64d..58b087433759 100644 --- a/drivers/reset/reset-sunplus.c +++ b/drivers/reset/reset-sunplus.c @@ -100,7 +100,6 @@ static const u32 sp_resets[] = { struct sp_reset { struct reset_controller_dev rcdev; - struct notifier_block notifier; void __iomem *base; }; @@ -154,10 +153,9 @@ static const struct reset_control_ops sp_reset_ops = { .status = sp_reset_status, }; -static int sp_restart(struct notifier_block *nb, unsigned long mode, - void *cmd) +static int sp_restart(struct sys_off_data *data) { - struct sp_reset *reset = container_of(nb, struct sp_reset, notifier); + struct sp_reset *reset = data->cb_data; sp_reset_assert(&reset->rcdev, 0); sp_reset_deassert(&reset->rcdev, 0); @@ -189,10 +187,8 @@ static int sp_reset_probe(struct platform_device *pdev) if (ret) return ret; - reset->notifier.notifier_call = sp_restart; - reset->notifier.priority = 192; - - return register_restart_handler(&reset->notifier); + return devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, + 192, sp_restart, reset); } static const struct of_device_id sp_reset_dt_ids[] = { diff --git a/drivers/reset/reset-tenstorrent-atlantis.c b/drivers/reset/reset-tenstorrent-atlantis.c new file mode 100644 index 000000000000..ab8be52fdd5e --- /dev/null +++ b/drivers/reset/reset-tenstorrent-atlantis.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Tenstorrent Atlantis PRCM Reset Driver + * + * Copyright (c) 2026 Tenstorrent + */ + +#include +#include +#include +#include + +/* RCPU Reset Register Offsets */ +#define RCPU_BLK_RST_REG 0x001c +#define LSIO_BLK_RST_REG 0x0020 +#define HSIO_BLK_RST_REG 0x000c +#define PCIE_SUBS_RST_REG 0x0000 +#define MM_RSTN_REG 0x0014 + +struct atlantis_reset_data { + u8 bit; + u16 reg; + bool active_low; +}; + +struct atlantis_reset_controller_data { + const struct atlantis_reset_data *reset_data; + size_t count; +}; + +struct atlantis_reset_controller { + struct reset_controller_dev rcdev; + const struct atlantis_reset_controller_data *data; + struct regmap *regmap; +}; + +static inline struct atlantis_reset_controller * +to_atlantis_reset_controller(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct atlantis_reset_controller, rcdev); +} + +#define RESET_DATA(_reg, _bit, _active_low) \ + { \ + .bit = _bit, .reg = _reg, .active_low = _active_low, \ + } + +static const struct atlantis_reset_data atlantis_rcpu_resets[] = { + [RST_SMNDMA0] = RESET_DATA(RCPU_BLK_RST_REG, 0, true), + [RST_SMNDMA1] = RESET_DATA(RCPU_BLK_RST_REG, 1, true), + [RST_WDT0] = RESET_DATA(RCPU_BLK_RST_REG, 2, true), + [RST_WDT1] = RESET_DATA(RCPU_BLK_RST_REG, 3, true), + [RST_TMR] = RESET_DATA(RCPU_BLK_RST_REG, 4, true), + [RST_PVTC] = RESET_DATA(RCPU_BLK_RST_REG, 12, true), + [RST_PMU] = RESET_DATA(RCPU_BLK_RST_REG, 13, true), + [RST_MAILBOX] = RESET_DATA(RCPU_BLK_RST_REG, 14, true), + [RST_SPACC] = RESET_DATA(RCPU_BLK_RST_REG, 26, true), + [RST_OTP] = RESET_DATA(RCPU_BLK_RST_REG, 28, true), + [RST_TRNG] = RESET_DATA(RCPU_BLK_RST_REG, 29, true), + [RST_CRC] = RESET_DATA(RCPU_BLK_RST_REG, 30, true), + [RST_QSPI] = RESET_DATA(LSIO_BLK_RST_REG, 0, true), + [RST_I2C0] = RESET_DATA(LSIO_BLK_RST_REG, 1, true), + [RST_I2C1] = RESET_DATA(LSIO_BLK_RST_REG, 2, true), + [RST_I2C2] = RESET_DATA(LSIO_BLK_RST_REG, 3, true), + [RST_I2C3] = RESET_DATA(LSIO_BLK_RST_REG, 4, true), + [RST_I2C4] = RESET_DATA(LSIO_BLK_RST_REG, 5, true), + [RST_UART0] = RESET_DATA(LSIO_BLK_RST_REG, 6, true), + [RST_UART1] = RESET_DATA(LSIO_BLK_RST_REG, 7, true), + [RST_UART2] = RESET_DATA(LSIO_BLK_RST_REG, 8, true), + [RST_UART3] = RESET_DATA(LSIO_BLK_RST_REG, 9, true), + [RST_UART4] = RESET_DATA(LSIO_BLK_RST_REG, 10, true), + [RST_SPI0] = RESET_DATA(LSIO_BLK_RST_REG, 11, true), + [RST_SPI1] = RESET_DATA(LSIO_BLK_RST_REG, 12, true), + [RST_SPI2] = RESET_DATA(LSIO_BLK_RST_REG, 13, true), + [RST_SPI3] = RESET_DATA(LSIO_BLK_RST_REG, 14, true), + [RST_GPIO] = RESET_DATA(LSIO_BLK_RST_REG, 15, true), + [RST_CAN0] = RESET_DATA(LSIO_BLK_RST_REG, 17, true), + [RST_CAN1] = RESET_DATA(LSIO_BLK_RST_REG, 18, true), + [RST_I2S0] = RESET_DATA(LSIO_BLK_RST_REG, 19, true), + [RST_I2S1] = RESET_DATA(LSIO_BLK_RST_REG, 20, true), + +}; + +static const struct atlantis_reset_controller_data atlantis_rcpu_reset_data = { + .reset_data = atlantis_rcpu_resets, + .count = ARRAY_SIZE(atlantis_rcpu_resets), +}; + +static int atlantis_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + unsigned int val; + struct atlantis_reset_controller *rst = + to_atlantis_reset_controller(rcdev); + const struct atlantis_reset_data *data = &rst->data->reset_data[id]; + unsigned int mask = BIT(data->bit); + struct regmap *regmap = rst->regmap; + + if (data->active_low ^ assert) + val = mask; + else + val = 0; + + return regmap_update_bits(regmap, data->reg, mask, val); +} + +static int atlantis_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return atlantis_reset_update(rcdev, id, true); +} + +static int atlantis_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return atlantis_reset_update(rcdev, id, false); +} + +static const struct reset_control_ops atlantis_reset_control_ops = { + .assert = atlantis_reset_assert, + .deassert = atlantis_reset_deassert, +}; + +static int +atlantis_reset_controller_register(struct device *dev, + struct atlantis_reset_controller *controller) +{ + struct reset_controller_dev *rcdev = &controller->rcdev; + + rcdev->ops = &atlantis_reset_control_ops; + rcdev->owner = THIS_MODULE; + rcdev->of_node = dev->of_node; + rcdev->nr_resets = controller->data->count; + + return devm_reset_controller_register(dev, &controller->rcdev); +} +static int atlantis_reset_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct atlantis_reset_controller *controller; + struct device *dev = &adev->dev; + struct regmap *regmap; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL); + if (!controller) + return -ENOMEM; + controller->data = + (const struct atlantis_reset_controller_data *)id->driver_data; + controller->regmap = regmap; + + return atlantis_reset_controller_register(dev, controller); +} + +static const struct auxiliary_device_id atlantis_reset_ids[] = { + { .name = "atlantis_prcm.rcpu-reset", + .driver_data = (kernel_ulong_t)&atlantis_rcpu_reset_data }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, atlantis_reset_ids); + +static struct auxiliary_driver atlantis_reset_driver = { + .probe = atlantis_reset_probe, + .id_table = atlantis_reset_ids, +}; +module_auxiliary_driver(atlantis_reset_driver); + +MODULE_AUTHOR("Anirudh Srinivasan "); +MODULE_DESCRIPTION("Atlantis PRCM reset controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/reset/sti/reset-syscfg.c b/drivers/reset/sti/reset-syscfg.c index 2324060b747c..38f78d78fa4f 100644 --- a/drivers/reset/sti/reset-syscfg.c +++ b/drivers/reset/sti/reset-syscfg.c @@ -41,7 +41,7 @@ struct syscfg_reset_channel { struct syscfg_reset_controller { struct reset_controller_dev rst; bool active_low; - struct syscfg_reset_channel *channels; + struct syscfg_reset_channel channels[]; }; #define to_syscfg_reset_controller(_rst) \ @@ -135,15 +135,10 @@ static int syscfg_reset_controller_register(struct device *dev, struct syscfg_reset_controller *rc; int i, err; - rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL); + rc = devm_kzalloc(dev, struct_size(rc, channels, data->nr_channels), GFP_KERNEL); if (!rc) return -ENOMEM; - rc->channels = devm_kcalloc(dev, data->nr_channels, - sizeof(*rc->channels), GFP_KERNEL); - if (!rc->channels) - return -ENOMEM; - rc->rst.ops = &syscfg_reset_ops; rc->rst.of_node = dev->of_node; rc->rst.nr_resets = data->nr_channels; diff --git a/drivers/rpmsg/mtk_rpmsg.c b/drivers/rpmsg/mtk_rpmsg.c index 0e03c5336609..1b670ed54cfa 100644 --- a/drivers/rpmsg/mtk_rpmsg.c +++ b/drivers/rpmsg/mtk_rpmsg.c @@ -135,7 +135,7 @@ static void mtk_rpmsg_destroy_ept(struct rpmsg_endpoint *ept) kref_put(&ept->refcount, __mtk_ept_release); } -static int mtk_rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len) +static int mtk_rpmsg_send(struct rpmsg_endpoint *ept, const void *data, int len) { struct mtk_rpmsg_rproc_subdev *mtk_subdev = to_mtk_rpmsg_endpoint(ept)->mtk_subdev; @@ -144,7 +144,7 @@ static int mtk_rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len) len, 0); } -static int mtk_rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len) +static int mtk_rpmsg_trysend(struct rpmsg_endpoint *ept, const void *data, int len) { struct mtk_rpmsg_rproc_subdev *mtk_subdev = to_mtk_rpmsg_endpoint(ept)->mtk_subdev; diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 9ef17c2e45b0..401a4ece0c97 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -1474,7 +1474,7 @@ unlock: } static int __qcom_glink_send(struct glink_channel *channel, - void *data, int len, bool wait) + const void *data, int len, bool wait) { struct qcom_glink *glink = channel->glink; struct glink_core_rx_intent *intent = NULL; @@ -1553,28 +1553,31 @@ static int __qcom_glink_send(struct glink_channel *channel, return 0; } -static int qcom_glink_send(struct rpmsg_endpoint *ept, void *data, int len) +static int qcom_glink_send(struct rpmsg_endpoint *ept, const void *data, int len) { struct glink_channel *channel = to_glink_channel(ept); return __qcom_glink_send(channel, data, len, true); } -static int qcom_glink_trysend(struct rpmsg_endpoint *ept, void *data, int len) +static int qcom_glink_trysend(struct rpmsg_endpoint *ept, const void *data, + int len) { struct glink_channel *channel = to_glink_channel(ept); return __qcom_glink_send(channel, data, len, false); } -static int qcom_glink_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) +static int qcom_glink_sendto(struct rpmsg_endpoint *ept, const void *data, + int len, u32 dst) { struct glink_channel *channel = to_glink_channel(ept); return __qcom_glink_send(channel, data, len, true); } -static int qcom_glink_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) +static int qcom_glink_trysendto(struct rpmsg_endpoint *ept, const void *data, + int len, u32 dst) { struct glink_channel *channel = to_glink_channel(ept); diff --git a/drivers/rpmsg/qcom_smd.c b/drivers/rpmsg/qcom_smd.c index e1eb450f4fea..3ac863f400ec 100644 --- a/drivers/rpmsg/qcom_smd.c +++ b/drivers/rpmsg/qcom_smd.c @@ -960,28 +960,30 @@ static void qcom_smd_destroy_ept(struct rpmsg_endpoint *ept) kref_put(&ept->refcount, __ept_release); } -static int qcom_smd_send(struct rpmsg_endpoint *ept, void *data, int len) +static int qcom_smd_send(struct rpmsg_endpoint *ept, const void *data, int len) { struct qcom_smd_endpoint *qsept = to_smd_endpoint(ept); return __qcom_smd_send(qsept->qsch, data, len, true); } -static int qcom_smd_trysend(struct rpmsg_endpoint *ept, void *data, int len) +static int qcom_smd_trysend(struct rpmsg_endpoint *ept, const void *data, int len) { struct qcom_smd_endpoint *qsept = to_smd_endpoint(ept); return __qcom_smd_send(qsept->qsch, data, len, false); } -static int qcom_smd_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) +static int qcom_smd_sendto(struct rpmsg_endpoint *ept, const void *data, int len, + u32 dst) { struct qcom_smd_endpoint *qsept = to_smd_endpoint(ept); return __qcom_smd_send(qsept->qsch, data, len, true); } -static int qcom_smd_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) +static int qcom_smd_trysendto(struct rpmsg_endpoint *ept, const void *data, + int len, u32 dst) { struct qcom_smd_endpoint *qsept = to_smd_endpoint(ept); diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 96964745065b..e7f7831d37f8 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -153,7 +153,7 @@ EXPORT_SYMBOL(rpmsg_destroy_ept); * * Return: 0 on success and an appropriate error value on failure. */ -int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len) +int rpmsg_send(struct rpmsg_endpoint *ept, const void *data, int len) { if (WARN_ON(!ept)) return -EINVAL; @@ -182,7 +182,7 @@ EXPORT_SYMBOL(rpmsg_send); * * Return: 0 on success and an appropriate error value on failure. */ -int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) +int rpmsg_sendto(struct rpmsg_endpoint *ept, const void *data, int len, u32 dst) { if (WARN_ON(!ept)) return -EINVAL; @@ -210,7 +210,7 @@ EXPORT_SYMBOL(rpmsg_sendto); * * Return: 0 on success and an appropriate error value on failure. */ -int rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len) +int rpmsg_trysend(struct rpmsg_endpoint *ept, const void *data, int len) { if (WARN_ON(!ept)) return -EINVAL; @@ -238,7 +238,7 @@ EXPORT_SYMBOL(rpmsg_trysend); * * Return: 0 on success and an appropriate error value on failure. */ -int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) +int rpmsg_trysendto(struct rpmsg_endpoint *ept, const void *data, int len, u32 dst) { if (WARN_ON(!ept)) return -EINVAL; @@ -650,7 +650,7 @@ static int __init rpmsg_init(void) ret = bus_register(&rpmsg_bus); if (ret) { pr_err("failed to register rpmsg bus: %d\n", ret); - class_destroy(&rpmsg_class); + class_unregister(&rpmsg_class); } return ret; } @@ -659,7 +659,7 @@ postcore_initcall(rpmsg_init); static void __exit rpmsg_fini(void) { bus_unregister(&rpmsg_bus); - class_destroy(&rpmsg_class); + class_unregister(&rpmsg_class); } module_exit(rpmsg_fini); diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h index 397e4926bd02..a8b7065fd165 100644 --- a/drivers/rpmsg/rpmsg_internal.h +++ b/drivers/rpmsg/rpmsg_internal.h @@ -63,11 +63,11 @@ struct rpmsg_device_ops { struct rpmsg_endpoint_ops { void (*destroy_ept)(struct rpmsg_endpoint *ept); - int (*send)(struct rpmsg_endpoint *ept, void *data, int len); - int (*sendto)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); + int (*send)(struct rpmsg_endpoint *ept, const void *data, int len); + int (*sendto)(struct rpmsg_endpoint *ept, const void *data, int len, u32 dst); - int (*trysend)(struct rpmsg_endpoint *ept, void *data, int len); - int (*trysendto)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); + int (*trysend)(struct rpmsg_endpoint *ept, const void *data, int len); + int (*trysendto)(struct rpmsg_endpoint *ept, const void *data, int len, u32 dst); __poll_t (*poll)(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait); int (*set_flow_control)(struct rpmsg_endpoint *ept, bool pause, u32 dst); diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 8d9e2b4dc7c1..5ae15111fb4f 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -136,11 +136,12 @@ struct virtio_rpmsg_channel { #define RPMSG_RESERVED_ADDRESSES (1024) static void virtio_rpmsg_destroy_ept(struct rpmsg_endpoint *ept); -static int virtio_rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len); -static int virtio_rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, - u32 dst); -static int virtio_rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len); -static int virtio_rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, +static int virtio_rpmsg_send(struct rpmsg_endpoint *ept, const void *data, int len); +static int virtio_rpmsg_sendto(struct rpmsg_endpoint *ept, const void *data, + int len, u32 dst); +static int virtio_rpmsg_trysend(struct rpmsg_endpoint *ept, const void *data, + int len); +static int virtio_rpmsg_trysendto(struct rpmsg_endpoint *ept, const void *data, int len, u32 dst); static __poll_t virtio_rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait); @@ -490,7 +491,7 @@ static void *get_a_tx_buf(struct virtproc_info *vrp) */ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev, u32 src, u32 dst, - void *data, int len, bool wait) + const void *data, int len, bool wait) { struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); struct virtproc_info *vrp = vch->vrp; @@ -580,7 +581,7 @@ out: return err; } -static int virtio_rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len) +static int virtio_rpmsg_send(struct rpmsg_endpoint *ept, const void *data, int len) { struct rpmsg_device *rpdev = ept->rpdev; u32 src = ept->addr, dst = rpdev->dst; @@ -588,8 +589,8 @@ static int virtio_rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len) return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true); } -static int virtio_rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, - u32 dst) +static int virtio_rpmsg_sendto(struct rpmsg_endpoint *ept, const void *data, + int len, u32 dst) { struct rpmsg_device *rpdev = ept->rpdev; u32 src = ept->addr; @@ -597,7 +598,8 @@ static int virtio_rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true); } -static int virtio_rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len) +static int virtio_rpmsg_trysend(struct rpmsg_endpoint *ept, const void *data, + int len) { struct rpmsg_device *rpdev = ept->rpdev; u32 src = ept->addr, dst = rpdev->dst; @@ -605,7 +607,7 @@ static int virtio_rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len) return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false); } -static int virtio_rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, +static int virtio_rpmsg_trysendto(struct rpmsg_endpoint *ept, const void *data, int len, u32 dst) { struct rpmsg_device *rpdev = ept->rpdev; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index b46ac73a2124..364afc73f8ab 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1986,7 +1986,7 @@ config RTC_DRV_XGENE config RTC_DRV_PIC32 tristate "Microchip PIC32 RTC" - depends on MACH_PIC32 + depends on MACH_PIC32 || COMPILE_TEST default y help If you say yes here you get support for the PIC32 RTC module. diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c index baf1a8ca8b2b..8ba7c25d2565 100644 --- a/drivers/rtc/dev.c +++ b/drivers/rtc/dev.c @@ -195,7 +195,16 @@ static __poll_t rtc_dev_poll(struct file *file, poll_table *wait) poll_wait(file, &rtc->irq_queue, wait); - data = rtc->irq_data; + /* + * This read can race with the write in rtc_handle_legacy_irq(). + * + * - If this check misses a zero to non-zero transition the next check + * will pick it up (rtc_handle_legacy_irq() wakes up rtc->irq_queue). + * - Non-zero to non-zero transition misses do not change return value. + * - And a non-zero to zero transition is unlikely to be missed, since + * it occurs on rtc_dev_read(), during which polling is not expected. + */ + data = data_race(rtc->irq_data); return (data != 0) ? (EPOLLIN | EPOLLRDNORM) : 0; } diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index 3fee27914ba8..00d7de64ed3e 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -772,8 +772,7 @@ static int abx80x_probe(struct i2c_client *client) struct abx80x_priv *priv; int i, data, err, trickle_cfg = -EINVAL; char buf[7]; - const struct i2c_device_id *id = i2c_match_id(abx80x_id, client); - unsigned int part = id->driver_data; + unsigned int part = (uintptr_t)i2c_get_match_data(client); unsigned int partnumber; unsigned int majrev, minrev; unsigned int lot; @@ -933,6 +932,8 @@ static int abx80x_probe(struct i2c_client *client) client->irq = 0; } } + if (client->irq <= 0) + clear_bit(RTC_FEATURE_ALARM, priv->rtc->features); err = rtc_add_group(priv->rtc, &rtc_calib_attr_group); if (err) { diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c index 713fa0d077cd..245290ae1a8d 100644 --- a/drivers/rtc/rtc-armada38x.c +++ b/drivers/rtc/rtc-armada38x.c @@ -72,8 +72,8 @@ struct armada38x_rtc { spinlock_t lock; int irq; bool initialized; - struct value_to_freq *val_to_freq; const struct armada38x_rtc_data *data; + struct value_to_freq val_to_freq[]; }; #define ALARM1 0 @@ -490,18 +490,13 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev) { struct armada38x_rtc *rtc; - rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc), + rtc = devm_kzalloc(&pdev->dev, struct_size(rtc, val_to_freq, SAMPLE_NR), GFP_KERNEL); if (!rtc) return -ENOMEM; rtc->data = of_device_get_match_data(&pdev->dev); - rtc->val_to_freq = devm_kcalloc(&pdev->dev, SAMPLE_NR, - sizeof(struct value_to_freq), GFP_KERNEL); - if (!rtc->val_to_freq) - return -ENOMEM; - spin_lock_init(&rtc->lock); rtc->regs = devm_platform_ioremap_resource_byname(pdev, "rtc"); diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index acc064baefcd..f89ab58f5048 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -1429,9 +1429,18 @@ static int __init cmos_platform_probe(struct platform_device *pdev) resource = platform_get_resource(pdev, IORESOURCE_IO, 0); else resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (irq < 0) + irq = platform_get_irq_optional(pdev, 0); + if (irq < 0) { irq = -1; +#ifdef CONFIG_X86 + /* + * On some x86 systems, the IRQ is not defined, but it should + * always be safe to hardcode it on systems with a legacy PIC. + */ + if (nr_legacy_irqs()) + irq = RTC_IRQ; +#endif + } return cmos_do_probe(&pdev->dev, resource, irq); } diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 740cab013f59..b26afef37d9c 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -924,13 +924,7 @@ static int m41t80_probe(struct i2c_client *client) return -ENOMEM; m41t80_data->client = client; - if (client->dev.of_node) { - m41t80_data->features = (unsigned long) - of_device_get_match_data(&client->dev); - } else { - const struct i2c_device_id *id = i2c_match_id(m41t80_id, client); - m41t80_data->features = id->driver_data; - } + m41t80_data->features = (unsigned long)i2c_get_match_data(client); i2c_set_clientdata(client, m41t80_data); m41t80_data->rtc = devm_rtc_allocate_device(&client->dev); diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 69ea3ce75b5a..3cdfd78a07cc 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -686,6 +686,11 @@ static int max77686_rtc_init_reg(struct max77686_rtc_info *info) return ret; } +static void max77686_rtc_release_dev(void *client) +{ + i2c_unregister_device(client); +} + static int max77686_init_rtc_regmap(struct max77686_rtc_info *info) { struct device *parent = info->dev->parent; @@ -713,12 +718,17 @@ static int max77686_init_rtc_regmap(struct max77686_rtc_info *info) goto add_rtc_irq; } - client = devm_i2c_new_dummy_device(info->dev, parent_i2c->adapter, - info->drv_data->rtc_i2c_addr); + client = i2c_new_ancillary_device(parent_i2c, "rtc", + info->drv_data->rtc_i2c_addr); if (IS_ERR(client)) return dev_err_probe(info->dev, PTR_ERR(client), "Failed to allocate I2C device for RTC\n"); + ret = devm_add_action_or_reset(info->dev, max77686_rtc_release_dev, + client); + if (ret) + return ret; + info->rtc_regmap = devm_regmap_init_i2c(client, info->drv_data->regmap_config); if (IS_ERR(info->rtc_regmap)) diff --git a/drivers/rtc/rtc-ntxec.c b/drivers/rtc/rtc-ntxec.c index 850ca49186fd..d28ddb34e19e 100644 --- a/drivers/rtc/rtc-ntxec.c +++ b/drivers/rtc/rtc-ntxec.c @@ -110,7 +110,7 @@ static int ntxec_rtc_probe(struct platform_device *pdev) struct rtc_device *dev; struct ntxec_rtc *rtc; - pdev->dev.of_node = pdev->dev.parent->of_node; + device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); if (!rtc) diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c index bb4fe81d3d62..e4785c5a55d0 100644 --- a/drivers/rtc/rtc-pcf2127.c +++ b/drivers/rtc/rtc-pcf2127.c @@ -1449,10 +1449,10 @@ static const struct regmap_bus pcf2127_i2c_regmap = { static struct i2c_driver pcf2127_i2c_driver; static const struct i2c_device_id pcf2127_i2c_id[] = { - { "pcf2127", PCF2127 }, - { "pcf2129", PCF2129 }, - { "pca2129", PCF2129 }, - { "pcf2131", PCF2131 }, + { "pcf2127", (kernel_ulong_t)&pcf21xx_cfg[PCF2127] }, + { "pcf2129", (kernel_ulong_t)&pcf21xx_cfg[PCF2129] }, + { "pca2129", (kernel_ulong_t)&pcf21xx_cfg[PCF2129] }, + { "pcf2131", (kernel_ulong_t)&pcf21xx_cfg[PCF2131] }, { } }; MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id); @@ -1469,18 +1469,9 @@ static int pcf2127_i2c_probe(struct i2c_client *client) if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; - if (client->dev.of_node) { - variant = of_device_get_match_data(&client->dev); - if (!variant) - return -ENODEV; - } else { - enum pcf21xx_type type = - i2c_match_id(pcf2127_i2c_id, client)->driver_data; - - if (type >= PCF21XX_LAST_ID) - return -ENODEV; - variant = &pcf21xx_cfg[type]; - } + variant = i2c_get_match_data(client); + if (!variant) + return -ENODEV; config.max_register = variant->max_register, diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index f8fab0205f8c..936f4f05c8c7 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -825,12 +825,7 @@ static int rs5c372_probe(struct i2c_client *client) rs5c372->client = client; i2c_set_clientdata(client, rs5c372); - if (client->dev.of_node) { - rs5c372->type = (uintptr_t)of_device_get_match_data(&client->dev); - } else { - const struct i2c_device_id *id = i2c_match_id(rs5c372_id, client); - rs5c372->type = id->driver_data; - } + rs5c372->type = (uintptr_t)i2c_get_match_data(client); /* we read registers 0x0f then 0x00-0x0f; skip the first one */ rs5c372->regs = &rs5c372->buf[1]; diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index 4e9e04cbec89..2bf988a89fd7 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -667,13 +667,7 @@ static int rv8803_probe(struct i2c_client *client) mutex_init(&rv8803->flags_lock); rv8803->client = client; - if (client->dev.of_node) { - rv8803->type = (uintptr_t)of_device_get_match_data(&client->dev); - } else { - const struct i2c_device_id *id = i2c_match_id(rv8803_id, client); - - rv8803->type = id->driver_data; - } + rv8803->type = (uintptr_t)i2c_get_match_data(client); i2c_set_clientdata(client, rv8803); flags = rv8803_read_reg(client, RV8803_FLAG); diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c index ced6e7adfe8d..c57081f9e02b 100644 --- a/drivers/rtc/rtc-rx8025.c +++ b/drivers/rtc/rtc-rx8025.c @@ -522,7 +522,6 @@ static const struct attribute_group rx8025_attr_group = { static int rx8025_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_match_id(rx8025_id, client); struct i2c_adapter *adapter = client->adapter; struct rx8025_data *rx8025; int err = 0; @@ -540,8 +539,7 @@ static int rx8025_probe(struct i2c_client *client) i2c_set_clientdata(client, rx8025); - if (id) - rx8025->model = id->driver_data; + rx8025->model = (uintptr_t)i2c_get_match_data(client); err = rx8025_init_client(client); if (err) diff --git a/drivers/rtc/rtc-ti-k3.c b/drivers/rtc/rtc-ti-k3.c index ec759d8f7023..e801f5b9d757 100644 --- a/drivers/rtc/rtc-ti-k3.c +++ b/drivers/rtc/rtc-ti-k3.c @@ -640,10 +640,18 @@ static int __maybe_unused ti_k3_rtc_suspend(struct device *dev) static int __maybe_unused ti_k3_rtc_resume(struct device *dev) { struct ti_k3_rtc *priv = dev_get_drvdata(dev); + int ret = 0; + + if (k3rtc_check_unlocked(priv)) { + /* RTC locked implies low power mode exit where RTC loses context */ + ret = k3rtc_configure(dev); + if (ret) + return ret; + } if (device_may_wakeup(dev)) disable_irq_wake(priv->irq); - return 0; + return ret; } static SIMPLE_DEV_PM_OPS(ti_k3_rtc_pm_ops, ti_k3_rtc_suspend, ti_k3_rtc_resume); diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 4d8f09910a46..7416f941e5b6 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -85,14 +85,6 @@ config HMC_DRV transfer cache size from its default value 0.5MB to N bytes. If N is zero, then no caching is performed. -config SCLP_OFB - def_bool n - prompt "Support for Open-for-Business SCLP Event" - depends on S390 - help - This option enables the Open-for-Business interface to the s390 - Service Element. - config S390_UV_UAPI def_tristate m prompt "Ultravisor userspace API" diff --git a/drivers/s390/char/hmcdrv_dev.c b/drivers/s390/char/hmcdrv_dev.c index 04b938c5357f..0d9c636df2c6 100644 --- a/drivers/s390/char/hmcdrv_dev.c +++ b/drivers/s390/char/hmcdrv_dev.c @@ -30,26 +30,12 @@ #include "hmcdrv_dev.h" #include "hmcdrv_ftp.h" -/* If the following macro is defined, then the HMC device creates it's own - * separated device class (and dynamically assigns a major number). If not - * defined then the HMC device is assigned to the "misc" class devices. - * -#define HMCDRV_DEV_CLASS "hmcftp" - */ - #define HMCDRV_DEV_NAME "hmcdrv" #define HMCDRV_DEV_BUSY_DELAY 500 /* delay between -EBUSY trials in ms */ #define HMCDRV_DEV_BUSY_RETRIES 3 /* number of retries on -EBUSY */ struct hmcdrv_dev_node { - -#ifdef HMCDRV_DEV_CLASS - struct cdev dev; /* character device structure */ - umode_t mode; /* mode of device node (unused, zero) */ -#else struct miscdevice dev; /* "misc" device structure */ -#endif - }; static int hmcdrv_dev_open(struct inode *inode, struct file *fp); @@ -75,38 +61,6 @@ static const struct file_operations hmcdrv_dev_fops = { static struct hmcdrv_dev_node hmcdrv_dev; /* HMC device struct (static) */ -#ifdef HMCDRV_DEV_CLASS - -static struct class *hmcdrv_dev_class; /* device class pointer */ -static dev_t hmcdrv_dev_no; /* device number (major/minor) */ - -/** - * hmcdrv_dev_name() - provides a naming hint for a device node in /dev - * @dev: device for which the naming/mode hint is - * @mode: file mode for device node created in /dev - * - * See: devtmpfs.c, function devtmpfs_create_node() - * - * Return: recommended device file name in /dev - */ -static char *hmcdrv_dev_name(const struct device *dev, umode_t *mode) -{ - char *nodename = NULL; - const char *devname = dev_name(dev); /* kernel device name */ - - if (devname) - nodename = kasprintf(GFP_KERNEL, "%s", devname); - - /* on device destroy (rmmod) the mode pointer may be NULL - */ - if (mode) - *mode = hmcdrv_dev.mode; - - return nodename; -} - -#endif /* HMCDRV_DEV_CLASS */ - /* * open() */ @@ -276,67 +230,11 @@ static ssize_t hmcdrv_dev_write(struct file *fp, const char __user *ubuf, */ int hmcdrv_dev_init(void) { - int rc; - -#ifdef HMCDRV_DEV_CLASS - struct device *dev; - - rc = alloc_chrdev_region(&hmcdrv_dev_no, 0, 1, HMCDRV_DEV_NAME); - - if (rc) - goto out_err; - - cdev_init(&hmcdrv_dev.dev, &hmcdrv_dev_fops); - hmcdrv_dev.dev.owner = THIS_MODULE; - rc = cdev_add(&hmcdrv_dev.dev, hmcdrv_dev_no, 1); - - if (rc) - goto out_unreg; - - /* At this point the character device exists in the kernel (see - * /proc/devices), but not under /dev nor /sys/devices/virtual. So - * we have to create an associated class (see /sys/class). - */ - hmcdrv_dev_class = class_create(HMCDRV_DEV_CLASS); - - if (IS_ERR(hmcdrv_dev_class)) { - rc = PTR_ERR(hmcdrv_dev_class); - goto out_devdel; - } - - /* Finally a device node in /dev has to be established (as 'mkdev' - * does from the command line). Notice that assignment of a device - * node name/mode function is optional (only for mode != 0600). - */ - hmcdrv_dev.mode = 0; /* "unset" */ - hmcdrv_dev_class->devnode = hmcdrv_dev_name; - - dev = device_create(hmcdrv_dev_class, NULL, hmcdrv_dev_no, NULL, - "%s", HMCDRV_DEV_NAME); - if (!IS_ERR(dev)) - return 0; - - rc = PTR_ERR(dev); - class_destroy(hmcdrv_dev_class); - hmcdrv_dev_class = NULL; - -out_devdel: - cdev_del(&hmcdrv_dev.dev); - -out_unreg: - unregister_chrdev_region(hmcdrv_dev_no, 1); - -out_err: - -#else /* !HMCDRV_DEV_CLASS */ hmcdrv_dev.dev.minor = MISC_DYNAMIC_MINOR; hmcdrv_dev.dev.name = HMCDRV_DEV_NAME; hmcdrv_dev.dev.fops = &hmcdrv_dev_fops; hmcdrv_dev.dev.mode = 0; /* finally produces 0600 */ - rc = misc_register(&hmcdrv_dev.dev); -#endif /* HMCDRV_DEV_CLASS */ - - return rc; + return misc_register(&hmcdrv_dev.dev); } /** @@ -344,15 +242,5 @@ out_err: */ void hmcdrv_dev_exit(void) { -#ifdef HMCDRV_DEV_CLASS - if (!IS_ERR_OR_NULL(hmcdrv_dev_class)) { - device_destroy(hmcdrv_dev_class, hmcdrv_dev_no); - class_destroy(hmcdrv_dev_class); - } - - cdev_del(&hmcdrv_dev.dev); - unregister_chrdev_region(hmcdrv_dev_no, 1); -#else /* !HMCDRV_DEV_CLASS */ misc_deregister(&hmcdrv_dev.dev); -#endif /* HMCDRV_DEV_CLASS */ } diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c index 9cfbe3fc3dca..8c77e8c44fc2 100644 --- a/drivers/s390/char/sclp_config.c +++ b/drivers/s390/char/sclp_config.c @@ -80,14 +80,11 @@ static void sclp_conf_receiver_fn(struct evbuf_header *evbuf) static struct sclp_register sclp_conf_register = { -#ifdef CONFIG_SCLP_OFB .send_mask = EVTYP_CONFMGMDATA_MASK, -#endif .receive_mask = EVTYP_CONFMGMDATA_MASK, .receiver_fn = sclp_conf_receiver_fn, }; -#ifdef CONFIG_SCLP_OFB static int sclp_ofb_send_req(char *ev_data, size_t len) { static DEFINE_MUTEX(send_mutex); @@ -143,11 +140,9 @@ static const struct bin_attribute ofb_bin_attr = { }, .write = sysfs_ofb_data_write, }; -#endif static int __init sclp_ofb_setup(void) { -#ifdef CONFIG_SCLP_OFB struct kset *ofb_kset; int rc; @@ -159,7 +154,6 @@ static int __init sclp_ofb_setup(void) kset_unregister(ofb_kset); return rc; } -#endif return 0; } diff --git a/drivers/s390/crypto/pkey_base.c b/drivers/s390/crypto/pkey_base.c index d60cd987c16d..c5677e1e110a 100644 --- a/drivers/s390/crypto/pkey_base.c +++ b/drivers/s390/crypto/pkey_base.c @@ -60,6 +60,13 @@ int pkey_handler_register(struct pkey_handler *handler) list_add_rcu(&handler->list, &handler_list); spin_unlock(&handler_list_write_lock); + /* + * Fast path to push the info about the updated list to the other + * CPUs. If removed, the other CPUs may get the updated list when the + * RCU context is synched. As this code is in general not performance + * critical and the list update mostly only occurs at the early time in + * system startup the focus is on concurrency versus performance. + */ synchronize_rcu(); module_put(handler->module); diff --git a/drivers/s390/crypto/pkey_cca.c b/drivers/s390/crypto/pkey_cca.c index 9bfb518db893..4bc47952324e 100644 --- a/drivers/s390/crypto/pkey_cca.c +++ b/drivers/s390/crypto/pkey_cca.c @@ -87,50 +87,52 @@ static int cca_apqns4key(const u8 *key, u32 keylen, u32 flags, zcrypt_wait_api_operational(); if (hdr->type == TOKTYPE_CCA_INTERNAL) { - u64 cur_mkvp = 0, old_mkvp = 0; + const u8 *ptr_cur_mkvp = NULL; + const u8 *ptr_old_mkvp = NULL; int minhwtype = ZCRYPT_CEX3C; if (hdr->version == TOKVER_CCA_AES) { struct secaeskeytoken *t = (struct secaeskeytoken *)key; if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) - cur_mkvp = t->mkvp; + ptr_cur_mkvp = t->mkvp; if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) - old_mkvp = t->mkvp; + ptr_old_mkvp = t->mkvp; } else if (hdr->version == TOKVER_CCA_VLSC) { struct cipherkeytoken *t = (struct cipherkeytoken *)key; minhwtype = ZCRYPT_CEX6; if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) - cur_mkvp = t->mkvp0; + ptr_cur_mkvp = t->mkvp0; if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) - old_mkvp = t->mkvp0; + ptr_old_mkvp = t->mkvp0; } else { /* unknown CCA internal token type */ return -EINVAL; } rc = cca_findcard2(_apqns, &_nr_apqns, 0xFFFF, 0xFFFF, minhwtype, AES_MK_SET, - cur_mkvp, old_mkvp, xflags); + ptr_cur_mkvp, ptr_old_mkvp, xflags); if (rc) goto out; } else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) { struct eccprivkeytoken *t = (struct eccprivkeytoken *)key; - u64 cur_mkvp = 0, old_mkvp = 0; + const u8 *ptr_cur_mkvp = NULL; + const u8 *ptr_old_mkvp = NULL; if (t->secid == 0x20) { if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) - cur_mkvp = t->mkvp; + ptr_cur_mkvp = t->mkvp; if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) - old_mkvp = t->mkvp; + ptr_old_mkvp = t->mkvp; } else { /* unknown CCA internal 2 token type */ return -EINVAL; } rc = cca_findcard2(_apqns, &_nr_apqns, 0xFFFF, 0xFFFF, ZCRYPT_CEX7, APKA_MK_SET, - cur_mkvp, old_mkvp, xflags); + ptr_cur_mkvp, ptr_old_mkvp, xflags); if (rc) goto out; @@ -167,31 +169,33 @@ static int cca_apqns4type(enum pkey_key_type ktype, zcrypt_wait_api_operational(); if (ktype == PKEY_TYPE_CCA_DATA || ktype == PKEY_TYPE_CCA_CIPHER) { - u64 cur_mkvp = 0, old_mkvp = 0; + const u8 *ptr_cur_mkvp = NULL; + const u8 *ptr_old_mkvp = NULL; int minhwtype = ZCRYPT_CEX3C; if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) - cur_mkvp = *((u64 *)cur_mkvp); + ptr_cur_mkvp = cur_mkvp; if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) - old_mkvp = *((u64 *)alt_mkvp); + ptr_old_mkvp = alt_mkvp; if (ktype == PKEY_TYPE_CCA_CIPHER) minhwtype = ZCRYPT_CEX6; rc = cca_findcard2(_apqns, &_nr_apqns, 0xFFFF, 0xFFFF, minhwtype, AES_MK_SET, - cur_mkvp, old_mkvp, xflags); + ptr_cur_mkvp, ptr_old_mkvp, xflags); if (rc) goto out; } else if (ktype == PKEY_TYPE_CCA_ECC) { - u64 cur_mkvp = 0, old_mkvp = 0; + const u8 *ptr_cur_mkvp = NULL; + const u8 *ptr_old_mkvp = NULL; if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) - cur_mkvp = *((u64 *)cur_mkvp); + ptr_cur_mkvp = cur_mkvp; if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) - old_mkvp = *((u64 *)alt_mkvp); + ptr_old_mkvp = alt_mkvp; rc = cca_findcard2(_apqns, &_nr_apqns, 0xFFFF, 0xFFFF, ZCRYPT_CEX7, APKA_MK_SET, - cur_mkvp, old_mkvp, xflags); + ptr_cur_mkvp, ptr_old_mkvp, xflags); if (rc) goto out; @@ -487,14 +491,14 @@ static int cca_verifykey(const u8 *key, u32 keylen, *keybitsize = t->bitsize; rc = cca_findcard2(apqns, &nr_apqns, *card, *dom, ZCRYPT_CEX3C, AES_MK_SET, - t->mkvp, 0, xflags); + t->mkvp, NULL, xflags); if (!rc) *flags = PKEY_FLAGS_MATCH_CUR_MKVP; if (rc == -ENODEV) { nr_apqns = ARRAY_SIZE(apqns); rc = cca_findcard2(apqns, &nr_apqns, *card, *dom, ZCRYPT_CEX3C, AES_MK_SET, - 0, t->mkvp, xflags); + NULL, t->mkvp, xflags); if (!rc) *flags = PKEY_FLAGS_MATCH_ALT_MKVP; } @@ -521,14 +525,14 @@ static int cca_verifykey(const u8 *key, u32 keylen, *keybitsize = PKEY_SIZE_AES_256; rc = cca_findcard2(apqns, &nr_apqns, *card, *dom, ZCRYPT_CEX6, AES_MK_SET, - t->mkvp0, 0, xflags); + t->mkvp0, NULL, xflags); if (!rc) *flags = PKEY_FLAGS_MATCH_CUR_MKVP; if (rc == -ENODEV) { nr_apqns = ARRAY_SIZE(apqns); rc = cca_findcard2(apqns, &nr_apqns, *card, *dom, ZCRYPT_CEX6, AES_MK_SET, - 0, t->mkvp0, xflags); + NULL, t->mkvp0, xflags); if (!rc) *flags = PKEY_FLAGS_MATCH_ALT_MKVP; } diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 023ea279361f..d6a455df228d 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -854,13 +854,12 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms, struct ica_xcRB *xcrb) { bool userspace = xflags & ZCRYPT_XFLAG_USERSPACE; - struct zcrypt_card *zc, *pref_zc; - struct zcrypt_queue *zq, *pref_zq; - struct ap_message ap_msg; + unsigned int card, domain, func_code = 0; unsigned int wgt = 0, pref_wgt = 0; - unsigned int func_code = 0; - unsigned short *domain, tdom; + struct zcrypt_queue *zq, *pref_zq; + struct zcrypt_card *zc, *pref_zc; int cpen, qpen, qid = 0, rc; + struct ap_message ap_msg; struct module *mod; trace_s390_zcrypt_req(xcrb, TB_ZSECSENDCPRB); @@ -878,10 +877,9 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms, print_hex_dump_debug("ccareq: ", DUMP_PREFIX_ADDRESS, 16, 1, ap_msg.msg, ap_msg.len, false); - tdom = *domain; - if (perms != &ap_perms && tdom < AP_DOMAINS) { + if (perms != &ap_perms && domain < AP_DOMAINS) { if (ap_msg.flags & AP_MSG_FLAG_ADMIN) { - if (!test_bit_inv(tdom, perms->adm)) { + if (!test_bit_inv(domain, perms->adm)) { rc = -ENODEV; goto out; } @@ -894,13 +892,14 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms, * If a valid target domain is set and this domain is NOT a usage * domain but a control only domain, autoselect target domain. */ - if (tdom < AP_DOMAINS && - !ap_test_config_usage_domain(tdom) && - ap_test_config_ctrl_domain(tdom)) - tdom = AUTOSEL_DOM; + if (domain < AP_DOMAINS && + !ap_test_config_usage_domain(domain) && + ap_test_config_ctrl_domain(domain)) + domain = AUTOSEL_DOM; pref_zc = NULL; pref_zq = NULL; + card = xcrb->user_defined; spin_lock(&zcrypt_list_lock); for_each_zcrypt_card(zc) { /* Check for usable CCA card */ @@ -908,8 +907,7 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms, !zc->card->hwinfo.cca) continue; /* Check for user selected CCA card */ - if (xcrb->user_defined != AUTOSELECT && - xcrb->user_defined != zc->card->id) + if (card != AUTOSELECT && card != zc->card->id) continue; /* check if request size exceeds card max msg size */ if (ap_msg.len > zc->card->maxmsgsize) @@ -929,8 +927,8 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms, /* check for device usable and eligible */ if (!zq->online || !zq->ops->send_cprb || !ap_queue_usable(zq->queue) || - (tdom != AUTOSEL_DOM && - tdom != AP_QID_QUEUE(zq->queue->qid))) + (domain != AUTOSEL_DOM && + domain != AP_QID_QUEUE(zq->queue->qid))) continue; /* check if device node has admission for this queue */ if (!zcrypt_check_queue(perms, @@ -953,16 +951,11 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms, if (!pref_zq) { pr_debug("no match for address %02x.%04x => ENODEV\n", - xcrb->user_defined, *domain); + card, domain); rc = -ENODEV; goto out; } - /* in case of auto select, provide the correct domain */ - qid = pref_zq->queue->qid; - if (*domain == AUTOSEL_DOM) - *domain = AP_QID_QUEUE(qid); - rc = pref_zq->ops->send_cprb(userspace, pref_zq, xcrb, &ap_msg); if (!rc) { print_hex_dump_debug("ccarpl: ", DUMP_PREFIX_ADDRESS, 16, 1, @@ -1220,7 +1213,6 @@ static long zcrypt_rng(char *buffer) unsigned int wgt = 0, pref_wgt = 0; unsigned int func_code = 0; struct ap_message ap_msg; - unsigned int domain; int qid = 0, rc = -ENODEV; struct module *mod; @@ -1229,7 +1221,7 @@ static long zcrypt_rng(char *buffer) rc = ap_init_apmsg(&ap_msg, 0); if (rc) goto out; - rc = prep_rng_ap_msg(&ap_msg, &func_code, &domain); + rc = prep_rng_ap_msg(&ap_msg, &func_code, NULL); if (rc) goto out; diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index 37a157a1d969..84936a795b95 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -305,7 +305,7 @@ static inline void prep_xcrb(struct ica_xcRB *pxcrb, struct CPRBX *prepcblk) { memset(pxcrb, 0, sizeof(*pxcrb)); - pxcrb->agent_ID = 0x4341; /* 'CA' */ + memcpy(&pxcrb->agent_ID, "CA", 2); pxcrb->user_defined = (cardnr == 0xFFFF ? AUTOSELECT : cardnr); pxcrb->request_control_blk_length = preqcblk->cprb_len + preqcblk->req_parml; @@ -1710,8 +1710,8 @@ out: EXPORT_SYMBOL(cca_get_info); int cca_findcard2(u32 *apqns, u32 *nr_apqns, u16 cardnr, u16 domain, - int minhwtype, int mktype, u64 cur_mkvp, u64 old_mkvp, - u32 xflags) + int minhwtype, int mktype, + const u8 *ptr_cur_mkvp, const u8 *ptr_old_mkvp, u32 xflags) { struct zcrypt_device_status_ext *device_status; int i, card, dom, curmatch, oldmatch; @@ -1755,20 +1755,28 @@ int cca_findcard2(u32 *apqns, u32 *nr_apqns, u16 cardnr, u16 domain, /* check min hardware type */ if (minhwtype > 0 && minhwtype > ci.hwtype) continue; - if (cur_mkvp || old_mkvp) { + if (ptr_cur_mkvp || ptr_old_mkvp) { /* check mkvps */ curmatch = oldmatch = 0; if (mktype == AES_MK_SET) { - if (cur_mkvp && cur_mkvp == ci.cur_aes_mkvp) + if (ptr_cur_mkvp && + !memcmp(ptr_cur_mkvp, ci.cur_aes_mkvp, + sizeof(ci.cur_aes_mkvp))) curmatch = 1; - if (old_mkvp && ci.old_aes_mk_state == '2' && - old_mkvp == ci.old_aes_mkvp) + if (ptr_old_mkvp && + ci.old_aes_mk_state == '2' && + !memcmp(ptr_old_mkvp, ci.old_aes_mkvp, + sizeof(ci.old_aes_mkvp))) oldmatch = 1; } else { - if (cur_mkvp && cur_mkvp == ci.cur_apka_mkvp) + if (ptr_cur_mkvp && + !memcmp(ptr_cur_mkvp, ci.cur_apka_mkvp, + sizeof(ci.cur_apka_mkvp))) curmatch = 1; - if (old_mkvp && ci.old_apka_mk_state == '2' && - old_mkvp == ci.old_apka_mkvp) + if (ptr_old_mkvp && + ci.old_apka_mk_state == '2' && + !memcmp(ptr_old_mkvp, ci.old_apka_mkvp, + sizeof(ci.old_apka_mkvp))) oldmatch = 1; } if (curmatch + oldmatch < 1) diff --git a/drivers/s390/crypto/zcrypt_ccamisc.h b/drivers/s390/crypto/zcrypt_ccamisc.h index 1ecc4e37e9ad..06507363947b 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.h +++ b/drivers/s390/crypto/zcrypt_ccamisc.h @@ -47,7 +47,7 @@ struct secaeskeytoken { u8 res1[1]; u8 flag; /* key flags */ u8 res2[1]; - u64 mkvp; /* master key verification pattern */ + u8 mkvp[8]; /* master key verification pattern */ u8 key[32]; /* key value (encrypted) */ u8 cv[8]; /* control vector */ u16 bitsize; /* key bit size */ @@ -64,8 +64,8 @@ struct cipherkeytoken { u8 res1[3]; u8 kms; /* key material state, 0x03 means wrapped with MK */ u8 kvpt; /* key verification pattern type, should be 0x01 */ - u64 mkvp0; /* master key verification pattern, lo part */ - u64 mkvp1; /* master key verification pattern, hi part (unused) */ + u8 mkvp0[8]; /* master key verification pattern, lo part */ + u8 mkvp1[8]; /* master key verification pattern, hi part (unused) */ u8 eskwm; /* encrypted section key wrapping method */ u8 hashalg; /* hash algorithmus used for wrapping key */ u8 plfver; /* pay load format version */ @@ -113,7 +113,7 @@ struct eccprivkeytoken { u8 ksrc; /* key source */ u16 pbitlen; /* length of prime p in bits */ u16 ibmadlen; /* IBM associated data length in bytes */ - u64 mkvp; /* master key verification pattern */ + u8 mkvp[8]; /* master key verification pattern */ u8 opk[48]; /* encrypted object protection key data */ u16 adatalen; /* associated data length in bytes */ u16 fseclen; /* formatted section length in bytes */ @@ -227,8 +227,8 @@ int cca_query_crypto_facility(u16 cardnr, u16 domain, * If no apqn meeting the criteria is found, -ENODEV is returned. */ int cca_findcard2(u32 *apqns, u32 *nr_apqns, u16 cardnr, u16 domain, - int minhwtype, int mktype, u64 cur_mkvp, u64 old_mkvp, - u32 xflags); + int minhwtype, int mktype, + const u8 *cur_mkvp, const u8 *old_mkvp, u32 xflags); #define AES_MK_SET 0 #define APKA_MK_SET 1 @@ -245,12 +245,12 @@ struct cca_info { char new_asym_mk_state; /* '1' empty, '2' partially full, '3' full */ char cur_asym_mk_state; /* '1' invalid, '2' valid */ char old_asym_mk_state; /* '1' invalid, '2' valid */ - u64 new_aes_mkvp; /* truncated sha256 of new aes master key */ - u64 cur_aes_mkvp; /* truncated sha256 of current aes master key */ - u64 old_aes_mkvp; /* truncated sha256 of old aes master key */ - u64 new_apka_mkvp; /* truncated sha256 of new apka master key */ - u64 cur_apka_mkvp; /* truncated sha256 of current apka mk */ - u64 old_apka_mkvp; /* truncated sha256 of old apka mk */ + u8 new_aes_mkvp[8]; /* truncated sha256 of new aes master key */ + u8 cur_aes_mkvp[8]; /* truncated sha256 of current aes master key */ + u8 old_aes_mkvp[8]; /* truncated sha256 of old aes master key */ + u8 new_apka_mkvp[8]; /* truncated sha256 of new apka master key */ + u8 cur_apka_mkvp[8]; /* truncated sha256 of current apka mk */ + u8 old_apka_mkvp[8]; /* truncated sha256 of old apka mk */ u8 new_asym_mkvp[16]; /* verify pattern of new asym master key */ u8 cur_asym_mkvp[16]; /* verify pattern of current asym master key */ u8 old_asym_mkvp[16]; /* verify pattern of old asym master key */ diff --git a/drivers/s390/crypto/zcrypt_cex4.c b/drivers/s390/crypto/zcrypt_cex4.c index e7b0ed26a9ec..d307f40706e8 100644 --- a/drivers/s390/crypto/zcrypt_cex4.c +++ b/drivers/s390/crypto/zcrypt_cex4.c @@ -102,9 +102,19 @@ static const struct attribute_group cca_card_attr_grp = { .attrs = cca_card_attrs, }; - /* - * CCA queue additional device attributes - */ +/* + * Simple helper macro to format raw mkvp byte array into hex + */ +#define MKVP_TO_HEXBUF(mkvp, buf) \ + do { \ + BUILD_BUG_ON(sizeof(buf) <= 2 * sizeof(mkvp)); \ + bin2hex(buf, mkvp, sizeof(mkvp)); \ + buf[2 * sizeof(mkvp)] = '\0'; \ + } while (0) + +/* + * CCA queue additional device attributes + */ static ssize_t cca_mkvps_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -113,6 +123,7 @@ static ssize_t cca_mkvps_show(struct device *dev, static const char * const cao_state[] = { "invalid", "valid" }; struct zcrypt_queue *zq = dev_get_drvdata(dev); struct cca_info ci; + char hexbuf[2 * 16 + 1]; int n = 0; memset(&ci, 0, sizeof(ci)); @@ -121,71 +132,86 @@ static ssize_t cca_mkvps_show(struct device *dev, AP_QID_QUEUE(zq->queue->qid), &ci, 0); - if (ci.new_aes_mk_state >= '1' && ci.new_aes_mk_state <= '3') - n += sysfs_emit_at(buf, n, "AES NEW: %s 0x%016llx\n", + if (ci.new_aes_mk_state >= '1' && ci.new_aes_mk_state <= '3') { + MKVP_TO_HEXBUF(ci.new_aes_mkvp, hexbuf); + n += sysfs_emit_at(buf, n, "AES NEW: %s 0x%s\n", new_state[ci.new_aes_mk_state - '1'], - ci.new_aes_mkvp); - else + hexbuf); + } else { n += sysfs_emit_at(buf, n, "AES NEW: - -\n"); + } - if (ci.cur_aes_mk_state >= '1' && ci.cur_aes_mk_state <= '2') - n += sysfs_emit_at(buf, n, "AES CUR: %s 0x%016llx\n", + if (ci.cur_aes_mk_state >= '1' && ci.cur_aes_mk_state <= '2') { + MKVP_TO_HEXBUF(ci.cur_aes_mkvp, hexbuf); + n += sysfs_emit_at(buf, n, "AES CUR: %s 0x%s\n", cao_state[ci.cur_aes_mk_state - '1'], - ci.cur_aes_mkvp); - else + hexbuf); + } else { n += sysfs_emit_at(buf, n, "AES CUR: - -\n"); + } - if (ci.old_aes_mk_state >= '1' && ci.old_aes_mk_state <= '2') - n += sysfs_emit_at(buf, n, "AES OLD: %s 0x%016llx\n", + if (ci.old_aes_mk_state >= '1' && ci.old_aes_mk_state <= '2') { + MKVP_TO_HEXBUF(ci.old_aes_mkvp, hexbuf); + n += sysfs_emit_at(buf, n, "AES OLD: %s 0x%s\n", cao_state[ci.old_aes_mk_state - '1'], - ci.old_aes_mkvp); - else + hexbuf); + } else { n += sysfs_emit_at(buf, n, "AES OLD: - -\n"); + } - if (ci.new_apka_mk_state >= '1' && ci.new_apka_mk_state <= '3') - n += sysfs_emit_at(buf, n, "APKA NEW: %s 0x%016llx\n", + if (ci.new_apka_mk_state >= '1' && ci.new_apka_mk_state <= '3') { + MKVP_TO_HEXBUF(ci.new_apka_mkvp, hexbuf); + n += sysfs_emit_at(buf, n, "APKA NEW: %s 0x%s\n", new_state[ci.new_apka_mk_state - '1'], - ci.new_apka_mkvp); - else + hexbuf); + } else { n += sysfs_emit_at(buf, n, "APKA NEW: - -\n"); + } - if (ci.cur_apka_mk_state >= '1' && ci.cur_apka_mk_state <= '2') - n += sysfs_emit_at(buf, n, "APKA CUR: %s 0x%016llx\n", + if (ci.cur_apka_mk_state >= '1' && ci.cur_apka_mk_state <= '2') { + MKVP_TO_HEXBUF(ci.cur_apka_mkvp, hexbuf); + n += sysfs_emit_at(buf, n, "APKA CUR: %s 0x%s\n", cao_state[ci.cur_apka_mk_state - '1'], - ci.cur_apka_mkvp); - else + hexbuf); + } else { n += sysfs_emit_at(buf, n, "APKA CUR: - -\n"); + } - if (ci.old_apka_mk_state >= '1' && ci.old_apka_mk_state <= '2') - n += sysfs_emit_at(buf, n, "APKA OLD: %s 0x%016llx\n", + if (ci.old_apka_mk_state >= '1' && ci.old_apka_mk_state <= '2') { + MKVP_TO_HEXBUF(ci.old_apka_mkvp, hexbuf); + n += sysfs_emit_at(buf, n, "APKA OLD: %s 0x%s\n", cao_state[ci.old_apka_mk_state - '1'], - ci.old_apka_mkvp); - else + hexbuf); + } else { n += sysfs_emit_at(buf, n, "APKA OLD: - -\n"); + } - if (ci.new_asym_mk_state >= '1' && ci.new_asym_mk_state <= '3') - n += sysfs_emit_at(buf, n, "ASYM NEW: %s 0x%016llx%016llx\n", + if (ci.new_asym_mk_state >= '1' && ci.new_asym_mk_state <= '3') { + MKVP_TO_HEXBUF(ci.new_asym_mkvp, hexbuf); + n += sysfs_emit_at(buf, n, "ASYM NEW: %s 0x%s\n", new_state[ci.new_asym_mk_state - '1'], - *((u64 *)(ci.new_asym_mkvp)), - *((u64 *)(ci.new_asym_mkvp + sizeof(u64)))); - else + hexbuf); + } else { n += sysfs_emit_at(buf, n, "ASYM NEW: - -\n"); + } - if (ci.cur_asym_mk_state >= '1' && ci.cur_asym_mk_state <= '2') - n += sysfs_emit_at(buf, n, "ASYM CUR: %s 0x%016llx%016llx\n", + if (ci.cur_asym_mk_state >= '1' && ci.cur_asym_mk_state <= '2') { + MKVP_TO_HEXBUF(ci.cur_asym_mkvp, hexbuf); + n += sysfs_emit_at(buf, n, "ASYM CUR: %s 0x%s\n", cao_state[ci.cur_asym_mk_state - '1'], - *((u64 *)(ci.cur_asym_mkvp)), - *((u64 *)(ci.cur_asym_mkvp + sizeof(u64)))); - else + hexbuf); + } else { n += sysfs_emit_at(buf, n, "ASYM CUR: - -\n"); + } - if (ci.old_asym_mk_state >= '1' && ci.old_asym_mk_state <= '2') - n += sysfs_emit_at(buf, n, "ASYM OLD: %s 0x%016llx%016llx\n", + if (ci.old_asym_mk_state >= '1' && ci.old_asym_mk_state <= '2') { + MKVP_TO_HEXBUF(ci.old_asym_mkvp, hexbuf); + n += sysfs_emit_at(buf, n, "ASYM OLD: %s 0x%s\n", cao_state[ci.old_asym_mk_state - '1'], - *((u64 *)(ci.old_asym_mkvp)), - *((u64 *)(ci.old_asym_mkvp + sizeof(u64)))); - else + hexbuf); + } else { n += sysfs_emit_at(buf, n, "ASYM OLD: - -\n"); + } return n; } diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h index 46e27b43a8af..fd10302e68d3 100644 --- a/drivers/s390/crypto/zcrypt_error.h +++ b/drivers/s390/crypto/zcrypt_error.h @@ -78,9 +78,13 @@ struct error_hdr { static inline int convert_error(struct zcrypt_queue *zq, struct ap_message *reply) { - struct error_hdr *ehdr = reply->msg; - int card = AP_QID_CARD(zq->queue->qid); int queue = AP_QID_QUEUE(zq->queue->qid); + int card = AP_QID_CARD(zq->queue->qid); + struct error_hdr *ehdr = reply->msg; + struct { + struct type86_hdr hdr; + struct type86_fmt2_ext fmt2; + } __packed * t86hdr = reply->msg; switch (ehdr->reply_code) { case REP82_ERROR_INVALID_MSG_LEN: /* 0x23 */ @@ -100,19 +104,12 @@ static inline int convert_error(struct zcrypt_queue *zq, /* RY indicates malformed request */ if (ehdr->reply_code == REP82_ERROR_FILTERED_BY_HYPERVISOR && ehdr->type == TYPE86_RSP_CODE) { - struct { - struct type86_hdr hdr; - struct type86_fmt2_ext fmt2; - } __packed * head = reply->msg; - unsigned int apfs = *((u32 *)head->fmt2.apfs); - ZCRYPT_DBF_WARN("%s dev=%02x.%04x RY=0x%02x apfs=0x%x => rc=EINVAL\n", __func__, card, queue, - ehdr->reply_code, apfs); + ehdr->reply_code, t86hdr->fmt2.apfs); } else { ZCRYPT_DBF_WARN("%s dev=%02x.%04x RY=0x%02x => rc=EINVAL\n", - __func__, card, queue, - ehdr->reply_code); + __func__, card, queue, ehdr->reply_code); } return -EINVAL; case REP82_ERROR_MACHINE_FAILURE: /* 0x10 */ @@ -125,15 +122,10 @@ static inline int convert_error(struct zcrypt_queue *zq, /* For type 86 response show the apfs value (failure reason) */ if (ehdr->reply_code == REP82_ERROR_TRANSPORT_FAIL && ehdr->type == TYPE86_RSP_CODE) { - struct { - struct type86_hdr hdr; - struct type86_fmt2_ext fmt2; - } __packed * head = reply->msg; - unsigned int apfs = *((u32 *)head->fmt2.apfs); - ZCRYPT_DBF_WARN( "%s dev=%02x.%04x RY=0x%02x apfs=0x%x => bus rescan, rc=EAGAIN\n", - __func__, card, queue, ehdr->reply_code, apfs); + __func__, card, queue, ehdr->reply_code, + t86hdr->fmt2.apfs); } else { ZCRYPT_DBF_WARN("%s dev=%02x.%04x RY=0x%02x => bus rescan, rc=EAGAIN\n", __func__, card, queue, diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c index 23a32221e41a..40f72cdf284d 100644 --- a/drivers/s390/crypto/zcrypt_msgtype6.c +++ b/drivers/s390/crypto/zcrypt_msgtype6.c @@ -65,7 +65,7 @@ struct function_and_rules_block { static const struct CPRBX static_cprbx = { .cprb_len = 0x00DC, .cprb_ver_id = 0x02, - .func_id = {0x54, 0x32}, + .func_id = {'T', '2'}, }; int speed_idx_cca(int req_type) @@ -328,7 +328,7 @@ struct type86_fmt2_msg { static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg, struct ica_xcRB *xcrb, unsigned int *fcode, - unsigned short **dom) + unsigned int *domain) { static struct type6_hdr static_type6_hdrX = { .type = 0x06, @@ -412,7 +412,8 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg, sizeof(msg->hdr.function_code)); *fcode = (msg->hdr.function_code[0] << 8) | msg->hdr.function_code[1]; - *dom = (unsigned short *)&msg->cprbx.domain; + if (domain) + *domain = msg->cprbx.domain; /* check subfunction, US and AU need special flag with NQAP */ if (memcmp(function_code, "US", 2) == 0 || @@ -454,8 +455,7 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap .type = 0x06, .rqid = {0x00, 0x01}, .function_code = {0x00, 0x00}, - .agent_id[0] = 0x58, /* {'X'} */ - .agent_id[1] = 0x43, /* {'C'} */ + .agent_id = {'X', 'C'}, .offset1 = 0x00000058, }; @@ -529,7 +529,8 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap else ap_msg->flags |= AP_MSG_FLAG_USAGE; - *domain = msg->cprbx.target_id; + if (domain) + *domain = msg->cprbx.target_id; return 0; } @@ -751,7 +752,7 @@ static int convert_response_xcrb(bool userspace, struct zcrypt_queue *zq, return convert_error(zq, reply); case TYPE86_RSP_CODE: if (msg->hdr.reply_code) { - memcpy(&xcrb->status, msg->fmt2.apfs, sizeof(u32)); + xcrb->status = msg->fmt2.apfs; return convert_error(zq, reply); } if (msg->cprbx.cprb_ver_id == 0x02) @@ -1052,7 +1053,7 @@ out: */ int prep_cca_ap_msg(bool userspace, struct ica_xcRB *xcrb, struct ap_message *ap_msg, - unsigned int *func_code, unsigned short **dom) + unsigned int *func_code, unsigned int *domain) { struct ap_response_type *resp_type = &ap_msg->response; @@ -1060,7 +1061,8 @@ int prep_cca_ap_msg(bool userspace, struct ica_xcRB *xcrb, ap_msg->psmid = (((unsigned long)current->pid) << 32) + atomic_inc_return(&zcrypt_step); resp_type->type = CEXXC_RESPONSE_TYPE_XCRB; - return xcrb_msg_to_type6cprb_msgx(userspace, ap_msg, xcrb, func_code, dom); + return xcrb_msg_to_type6cprb_msgx(userspace, ap_msg, + xcrb, func_code, domain); } /* @@ -1105,6 +1107,9 @@ static long zcrypt_msgtype6_send_cprb(bool userspace, struct zcrypt_queue *zq, msg->hdr.fromcardlen1 -= delta; } + /* update domain field within the CPRB struct */ + msg->cprbx.domain = AP_QID_QUEUE(zq->queue->qid); + init_completion(&resp_type->work); rc = ap_queue_message(zq->queue, ap_msg); if (rc) @@ -1210,8 +1215,7 @@ static long zcrypt_msgtype6_send_ep11_cprb(bool userspace, struct zcrypt_queue * lfmt = 1; /* length format #1 */ } payload_hdr = (struct pld_hdr *)((&msg->pld_lenfmt) + lfmt); - payload_hdr->dom_val = (unsigned int) - AP_QID_QUEUE(zq->queue->qid); + payload_hdr->dom_val = AP_QID_QUEUE(zq->queue->qid); } /* @@ -1246,6 +1250,56 @@ out: return rc; } +/* + * Prepare a type6 CPRB message for random number generation + * + * @ap_dev: AP device pointer + * @ap_msg: pointer to AP message + */ +static inline void rng_type6cprb_msgx(struct ap_message *ap_msg, + unsigned int random_number_length, + unsigned int *domain) +{ + struct { + struct type6_hdr hdr; + struct CPRBX cprbx; + char function_code[2]; + short int rule_length; + char rule[8]; + short int verb_length; + short int key_length; + } __packed * msg = ap_msg->msg; + static struct type6_hdr static_type6_hdrX = { + .type = 0x06, + .offset1 = 0x00000058, + .agent_id = {'C', 'A'}, + .function_code = {'R', 'L'}, + .tocardlen1 = sizeof(*msg) - sizeof(msg->hdr), + .fromcardlen1 = sizeof(*msg) - sizeof(msg->hdr), + }; + static struct CPRBX local_cprbx = { + .cprb_len = 0x00dc, + .cprb_ver_id = 0x02, + .func_id = {'T', '2'}, + .req_parml = sizeof(*msg) - sizeof(msg->hdr) - + sizeof(msg->cprbx), + .rpl_msgbl = sizeof(*msg) - sizeof(msg->hdr), + }; + + msg->hdr = static_type6_hdrX; + msg->hdr.fromcardlen2 = random_number_length; + msg->cprbx = local_cprbx; + msg->cprbx.rpl_datal = random_number_length; + memcpy(msg->function_code, msg->hdr.function_code, 0x02); + msg->rule_length = 0x0a; + memcpy(msg->rule, "RANDOM ", 8); + msg->verb_length = 0x02; + msg->key_length = 0x02; + ap_msg->len = sizeof(*msg); + if (domain) + *domain = msg->cprbx.domain; +} + /* * Prepare a CEXXC get random request ap message. * This function assumes that ap_msg has been initialized with diff --git a/drivers/s390/crypto/zcrypt_msgtype6.h b/drivers/s390/crypto/zcrypt_msgtype6.h index 6f5ced8d6cda..8cc227df55c8 100644 --- a/drivers/s390/crypto/zcrypt_msgtype6.h +++ b/drivers/s390/crypto/zcrypt_msgtype6.h @@ -34,7 +34,7 @@ struct type6_hdr { unsigned char right[4]; /* 0x00000000 */ unsigned char reserved3[2]; /* 0x0000 */ unsigned char reserved4[2]; /* 0x0000 */ - unsigned char apfs[4]; /* 0x00000000 */ + unsigned int apfs; /* 0x00000000 */ unsigned int offset1; /* 0x00000058 (offset to CPRB) */ unsigned int offset2; /* 0x00000000 */ unsigned int offset3; /* 0x00000000 */ @@ -83,7 +83,7 @@ struct type86_hdr { struct type86_fmt2_ext { unsigned char reserved[4]; /* 0x00000000 */ - unsigned char apfs[4]; /* final status */ + unsigned int apfs; /* final status */ unsigned int count1; /* length of CPRB + parameters */ unsigned int offset1; /* offset to CPRB */ unsigned int count2; /* 0x00000000 */ @@ -96,7 +96,7 @@ struct type86_fmt2_ext { int prep_cca_ap_msg(bool userspace, struct ica_xcRB *xcrb, struct ap_message *ap_msg, - unsigned int *fc, unsigned short **dom); + unsigned int *fc, unsigned int *dom); int prep_ep11_ap_msg(bool userspace, struct ep11_urb *xcrb, struct ap_message *ap_msg, unsigned int *fc, unsigned int *dom); @@ -110,55 +110,6 @@ int prep_rng_ap_msg(struct ap_message *ap_msg, int speed_idx_cca(int); int speed_idx_ep11(int); -/** - * Prepare a type6 CPRB message for random number generation - * - * @ap_dev: AP device pointer - * @ap_msg: pointer to AP message - */ -static inline void rng_type6cprb_msgx(struct ap_message *ap_msg, - unsigned int random_number_length, - unsigned int *domain) -{ - struct { - struct type6_hdr hdr; - struct CPRBX cprbx; - char function_code[2]; - short int rule_length; - char rule[8]; - short int verb_length; - short int key_length; - } __packed * msg = ap_msg->msg; - static struct type6_hdr static_type6_hdrX = { - .type = 0x06, - .offset1 = 0x00000058, - .agent_id = {'C', 'A'}, - .function_code = {'R', 'L'}, - .tocardlen1 = sizeof(*msg) - sizeof(msg->hdr), - .fromcardlen1 = sizeof(*msg) - sizeof(msg->hdr), - }; - static struct CPRBX local_cprbx = { - .cprb_len = 0x00dc, - .cprb_ver_id = 0x02, - .func_id = {0x54, 0x32}, - .req_parml = sizeof(*msg) - sizeof(msg->hdr) - - sizeof(msg->cprbx), - .rpl_msgbl = sizeof(*msg) - sizeof(msg->hdr), - }; - - msg->hdr = static_type6_hdrX; - msg->hdr.fromcardlen2 = random_number_length; - msg->cprbx = local_cprbx; - msg->cprbx.rpl_datal = random_number_length; - memcpy(msg->function_code, msg->hdr.function_code, 0x02); - msg->rule_length = 0x0a; - memcpy(msg->rule, "RANDOM ", 8); - msg->verb_length = 0x02; - msg->key_length = 0x02; - ap_msg->len = sizeof(*msg); - *domain = (unsigned short)msg->cprbx.domain; -} - void zcrypt_msgtype6_init(void); void zcrypt_msgtype6_exit(void); diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c index da6599ae3d0d..5304d2febd63 100644 --- a/drivers/scsi/BusLogic.c +++ b/drivers/scsi/BusLogic.c @@ -1632,8 +1632,8 @@ common: /* Initialize the Host Adapter Full Model Name from the Model Name. */ - strcpy(adapter->full_model, "BusLogic "); - strcat(adapter->full_model, adapter->model); + scnprintf(adapter->full_model, sizeof(adapter->full_model), + "BusLogic %s", adapter->model); /* Select an appropriate value for the Tagged Queue Depth either from a BusLogic Driver Options specification, or based on whether this Host diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 19d0884479a2..fc8e8b0bfa39 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -304,8 +304,6 @@ config ISCSI_TCP tristate "iSCSI Initiator over TCP/IP" depends on SCSI && INET select CRC32 - select CRYPTO - select CRYPTO_MD5 select SCSI_ISCSI_ATTRS help The iSCSI Driver provides a host with the ability to access storage @@ -1151,6 +1149,7 @@ config SCSI_LPFC depends on NVME_TARGET_FC || NVME_TARGET_FC=n depends on NVME_FC || NVME_FC=n select CRC_T10DIF + select CRC32 select IRQ_POLL help This lpfc driver supports the Emulex LightPulse diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm.h b/drivers/scsi/aic7xxx/aicasm/aicasm.h index 716a2aefc925..f290b50c6475 100644 --- a/drivers/scsi/aic7xxx/aicasm/aicasm.h +++ b/drivers/scsi/aic7xxx/aicasm/aicasm.h @@ -82,7 +82,7 @@ extern int src_mode; extern int dst_mode; struct symbol; -void stop(const char *errstring, int err_code); +void __attribute__((noreturn)) stop(const char *errstring, int err_code); void include_file(char *file_name, include_type type); void expand_macro(struct symbol *macro_symbol); struct instruction *seq_alloc(void); diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y b/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y index b1c9ce477cbd..f6dbb9855daa 100644 --- a/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y +++ b/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y @@ -1104,7 +1104,7 @@ conditional: last_scope = TAILQ_LAST(&scope_context->inner_scope, scope_tailq); if (last_scope == NULL - || last_scope->type == T_ELSE) { + || last_scope->type == (int)T_ELSE) { stop("'else if' without leading 'if'", EX_DATAERR); /* NOTREACHED */ diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l b/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l index fc7e6c58148d..c0d92cf5f9b5 100644 --- a/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l +++ b/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l @@ -389,7 +389,7 @@ nop { return T_NOP; } char c; yptr = yytext; - while (c = *yptr++) { + while ((c = *yptr++)) { /* * Strip carriage returns. */ diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index efb08b9b145a..80ab0ff921d4 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -37,7 +37,7 @@ #define TPGS_MODE_EXPLICIT 0x2 #define ALUA_RTPG_SIZE 128 -#define ALUA_FAILOVER_TIMEOUT 60 +#define ALUA_FAILOVER_TIMEOUT 255 /* max 255 (8-bit value) */ #define ALUA_FAILOVER_RETRIES 5 #define ALUA_RTPG_DELAY_MSECS 5 #define ALUA_RTPG_RETRY_DELAY 2 diff --git a/drivers/scsi/elx/efct/efct_lio.c b/drivers/scsi/elx/efct/efct_lio.c index d6e35ee8fee0..67d686dd6fb3 100644 --- a/drivers/scsi/elx/efct/efct_lio.c +++ b/drivers/scsi/elx/efct/efct_lio.c @@ -1612,6 +1612,7 @@ static const struct target_core_fabric_ops efct_lio_ops = { .sess_get_initiator_sid = NULL, .tfc_tpg_base_attrs = efct_lio_tpg_attrs, .tfc_tpg_attrib_attrs = efct_lio_tpg_attrib_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_DIRECT_SUBMIT, .direct_submit_supp = 1, }; @@ -1650,6 +1651,7 @@ static const struct target_core_fabric_ops efct_lio_npiv_ops = { .tfc_tpg_base_attrs = efct_lio_npiv_tpg_attrs, .tfc_tpg_attrib_attrs = efct_lio_npiv_tpg_attrib_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_DIRECT_SUBMIT, .direct_submit_supp = 1, }; diff --git a/drivers/scsi/esas2r/esas2r_log.c b/drivers/scsi/esas2r/esas2r_log.c index d6c87a0bae09..46f489b2263c 100644 --- a/drivers/scsi/esas2r/esas2r_log.c +++ b/drivers/scsi/esas2r/esas2r_log.c @@ -101,11 +101,6 @@ static const char *translate_esas2r_event_level_to_kernel(const long level) } } -#pragma GCC diagnostic push -#ifndef __clang__ -#pragma GCC diagnostic ignored "-Wsuggest-attribute=format" -#endif - /* * the master logging function. this function will format the message as * outlined by the formatting string, the input device information and the @@ -118,10 +113,9 @@ static const char *translate_esas2r_event_level_to_kernel(const long level) * * @return 0 on success, or -1 if an error occurred. */ -static int esas2r_log_master(const long level, - const struct device *dev, - const char *format, - va_list args) +static __printf(3, 0) +int esas2r_log_master(const long level, const struct device *dev, + const char *format, va_list args) { if (level <= event_log_level) { unsigned long flags = 0; @@ -175,8 +169,6 @@ static int esas2r_log_master(const long level, return 0; } -#pragma GCC diagnostic pop - /* * formats and logs a message to the system log. * diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c index 455426564ca0..554dea767885 100644 --- a/drivers/scsi/fnic/fdls_disc.c +++ b/drivers/scsi/fnic/fdls_disc.c @@ -4613,7 +4613,7 @@ void fnic_fdls_disc_start(struct fnic_iport_s *iport) if (!iport->usefip) { if (iport->flags & FNIC_FIRST_LINK_UP) { spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - fnic_scsi_fcpio_reset(iport->fnic); + fnic_fcpio_reset(iport->fnic); spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); iport->flags &= ~FNIC_FIRST_LINK_UP; @@ -5072,7 +5072,7 @@ void fnic_fdls_link_down(struct fnic_iport_s *iport) iport->fabric.flags = 0; spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - fnic_scsi_fcpio_reset(iport->fnic); + fnic_fcpio_reset(iport->fnic); spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); list_for_each_entry_safe(tport, next, &iport->tport_list, links) { FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, diff --git a/drivers/scsi/fnic/fip.c b/drivers/scsi/fnic/fip.c index ccd0da7d490f..132f00512ee1 100644 --- a/drivers/scsi/fnic/fip.c +++ b/drivers/scsi/fnic/fip.c @@ -737,7 +737,7 @@ void fnic_work_on_fip_timer(struct work_struct *work) if (memcmp(iport->selected_fcf.fcf_mac, zmac, ETH_ALEN) != 0) { if (iport->flags & FNIC_FIRST_LINK_UP) { - fnic_scsi_fcpio_reset(iport->fnic); + fnic_fcpio_reset(iport->fnic); iport->flags &= ~FNIC_FIRST_LINK_UP; } diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index 42237eb3222f..8724d64f2525 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -30,7 +30,7 @@ #define DRV_NAME "fnic" #define DRV_DESCRIPTION "Cisco FCoE HBA Driver" -#define DRV_VERSION "1.8.0.2" +#define DRV_VERSION "1.8.0.3" #define PFX DRV_NAME ": " #define DFX DRV_NAME "%d: " @@ -438,6 +438,7 @@ struct fnic { struct list_head tx_queue; mempool_t *frame_pool; mempool_t *frame_elem_pool; + mempool_t *frame_recv_pool; struct work_struct tport_work; struct list_head tport_event_list; @@ -512,7 +513,6 @@ int fnic_host_reset(struct Scsi_Host *shost); void fnic_reset(struct Scsi_Host *shost); int fnic_issue_fc_host_lip(struct Scsi_Host *shost); void fnic_get_host_port_state(struct Scsi_Host *shost); -void fnic_scsi_fcpio_reset(struct fnic *fnic); int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int copy_work_to_do, unsigned int cq_index); int fnic_wq_cmpl_handler(struct fnic *fnic, int); int fnic_flogi_reg_handler(struct fnic *fnic, u32); @@ -541,7 +541,8 @@ fnic_chk_state_flags_locked(struct fnic *fnic, unsigned long st_flags) } void __fnic_set_state_flags(struct fnic *, unsigned long, unsigned long); void fnic_dump_fchost_stats(struct Scsi_Host *, struct fc_host_statistics *); -void fnic_free_txq(struct list_head *head); +void fnic_free_txq(struct fnic *fnic); +void fnic_free_rxq(struct fnic *fnic); int fnic_get_desc_by_devid(struct pci_dev *pdev, char **desc, char **subsys_desc); void fnic_fdls_link_status_change(struct fnic *fnic, int linkup); diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c index 405b341b73d7..063eb864a5cd 100644 --- a/drivers/scsi/fnic/fnic_fcs.c +++ b/drivers/scsi/fnic/fnic_fcs.c @@ -291,7 +291,7 @@ void fnic_handle_frame(struct work_struct *work) if (fnic->stop_rx_link_events) { list_del(&cur_frame->links); spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - kfree(cur_frame->fp); + mempool_free(cur_frame->fp, fnic->frame_recv_pool); mempool_free(cur_frame, fnic->frame_elem_pool); return; } @@ -317,7 +317,7 @@ void fnic_handle_frame(struct work_struct *work) fnic_fdls_recv_frame(&fnic->iport, cur_frame->fp, cur_frame->frame_len, fchdr_offset); - kfree(cur_frame->fp); + mempool_free(cur_frame->fp, fnic->frame_recv_pool); mempool_free(cur_frame, fnic->frame_elem_pool); } spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); @@ -337,8 +337,8 @@ void fnic_handle_fip_frame(struct work_struct *work) if (fnic->stop_rx_link_events) { list_del(&cur_frame->links); spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - kfree(cur_frame->fp); - kfree(cur_frame); + mempool_free(cur_frame->fp, fnic->frame_recv_pool); + mempool_free(cur_frame, fnic->frame_elem_pool); return; } @@ -355,8 +355,8 @@ void fnic_handle_fip_frame(struct work_struct *work) list_del(&cur_frame->links); if (fdls_fip_recv_frame(fnic, cur_frame->fp)) { - kfree(cur_frame->fp); - kfree(cur_frame); + mempool_free(cur_frame->fp, fnic->frame_recv_pool); + mempool_free(cur_frame, fnic->frame_elem_pool); } } spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); @@ -375,10 +375,10 @@ static inline int fnic_import_rq_eth_pkt(struct fnic *fnic, void *fp) eh = (struct ethhdr *) fp; if ((eh->h_proto == cpu_to_be16(ETH_P_FIP)) && (fnic->iport.usefip)) { - fip_fr_elem = (struct fnic_frame_list *) - kzalloc_obj(struct fnic_frame_list, GFP_ATOMIC); + fip_fr_elem = mempool_alloc(fnic->frame_elem_pool, GFP_ATOMIC); if (!fip_fr_elem) return 0; + memset(fip_fr_elem, 0, sizeof(struct fnic_frame_list)); fip_fr_elem->fp = fp; spin_lock_irqsave(&fnic->fnic_lock, flags); list_add_tail(&fip_fr_elem->links, &fnic->fip_frame_queue); @@ -519,13 +519,13 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc spin_unlock_irqrestore(&fnic->fnic_lock, flags); - frame_elem = mempool_alloc(fnic->frame_elem_pool, - GFP_ATOMIC | __GFP_ZERO); + frame_elem = mempool_alloc(fnic->frame_elem_pool, GFP_ATOMIC); if (!frame_elem) { FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Failed to allocate memory for frame elem"); goto drop; } + memset(frame_elem, 0, sizeof(struct fnic_frame_list)); frame_elem->fp = fp; frame_elem->rx_ethhdr_stripped = ethhdr_stripped; frame_elem->frame_len = bytes_written; @@ -538,7 +538,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc return; drop: - kfree(fp); + mempool_free(fp, fnic->frame_recv_pool); } static int fnic_rq_cmpl_handler_cont(struct vnic_dev *vdev, @@ -591,7 +591,7 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq) int ret; len = FNIC_FRAME_HT_ROOM; - buf = kmalloc(len, GFP_ATOMIC); + buf = mempool_alloc(fnic->frame_recv_pool, GFP_ATOMIC); if (!buf) { FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Unable to allocate RQ buffer of size: %d\n", len); @@ -609,7 +609,7 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq) fnic_queue_rq_desc(rq, buf, pa, len); return 0; free_buf: - kfree(buf); + mempool_free(buf, fnic->frame_recv_pool); return ret; } @@ -621,7 +621,7 @@ void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf) dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len, DMA_FROM_DEVICE); - kfree(rq_buf); + mempool_free(rq_buf, fnic->frame_recv_pool); buf->os_buf = NULL; } @@ -704,13 +704,13 @@ fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size, */ if ((fnic->state != FNIC_IN_FC_MODE) && (fnic->state != FNIC_IN_ETH_MODE)) { - frame_elem = mempool_alloc(fnic->frame_elem_pool, - GFP_ATOMIC | __GFP_ZERO); + frame_elem = mempool_alloc(fnic->frame_elem_pool, GFP_ATOMIC); if (!frame_elem) { FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Failed to allocate memory for frame elem"); return -ENOMEM; } + memset(frame_elem, 0, sizeof(struct fnic_frame_list)); FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, "Queueing FC frame: sid/did/type/oxid = 0x%x/0x%x/0x%x/0x%x\n", @@ -836,14 +836,34 @@ fnic_fdls_register_portid(struct fnic_iport_s *iport, u32 port_id, return 0; } -void fnic_free_txq(struct list_head *head) +void fnic_free_txq(struct fnic *fnic) { struct fnic_frame_list *cur_frame, *next; - list_for_each_entry_safe(cur_frame, next, head, links) { + list_for_each_entry_safe(cur_frame, next, &fnic->tx_queue, links) { list_del(&cur_frame->links); - kfree(cur_frame->fp); - kfree(cur_frame); + mempool_free(cur_frame->fp, fnic->frame_pool); + mempool_free(cur_frame, fnic->frame_elem_pool); + } +} + +void fnic_free_rxq(struct fnic *fnic) +{ + struct fnic_frame_list *cur_frame, *next; + + list_for_each_entry_safe(cur_frame, next, &fnic->frame_queue, links) { + list_del(&cur_frame->links); + mempool_free(cur_frame->fp, fnic->frame_recv_pool); + mempool_free(cur_frame, fnic->frame_elem_pool); + } + + if (fnic->config.flags & VFCF_FIP_CAPABLE) { + list_for_each_entry_safe(cur_frame, next, + &fnic->fip_frame_queue, links) { + list_del(&cur_frame->links); + mempool_free(cur_frame->fp, fnic->frame_recv_pool); + mempool_free(cur_frame, fnic->frame_elem_pool); + } } } @@ -898,7 +918,7 @@ void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf) dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len, DMA_TO_DEVICE); - kfree(buf->os_buf); + mempool_free(buf->os_buf, fnic->frame_pool); buf->os_buf = NULL; } @@ -1108,3 +1128,53 @@ void fnic_reset_work_handler(struct work_struct *work) spin_unlock_irqrestore(&reset_fnic_list_lock, reset_fnic_list_lock_flags); } + +void fnic_fcpio_reset(struct fnic *fnic) +{ + unsigned long flags; + enum fnic_state old_state; + struct fnic_iport_s *iport = &fnic->iport; + DECLARE_COMPLETION_ONSTACK(fw_reset_done); + int time_remain; + + /* issue fw reset */ + spin_lock_irqsave(&fnic->fnic_lock, flags); + if (unlikely(fnic->state == FNIC_IN_FC_TRANS_ETH_MODE)) { + /* fw reset is in progress, poll for its completion */ + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + "fnic is in unexpected state: %d for fw_reset\n", + fnic->state); + return; + } + + old_state = fnic->state; + fnic->state = FNIC_IN_FC_TRANS_ETH_MODE; + + fnic_update_mac_locked(fnic, iport->hwmac); + fnic->fw_reset_done = &fw_reset_done; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + + FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + "Issuing fw reset\n"); + if (fnic_fw_reset_handler(fnic)) { + spin_lock_irqsave(&fnic->fnic_lock, flags); + if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE) + fnic->state = old_state; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + } else { + FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + "Waiting for fw completion\n"); + time_remain = wait_for_completion_timeout(&fw_reset_done, + msecs_to_jiffies(FNIC_FW_RESET_TIMEOUT)); + FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + "Woken up after fw completion timeout\n"); + if (time_remain == 0) { + FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + "FW reset completion timed out after %d ms\n", + FNIC_FW_RESET_TIMEOUT); + } + atomic64_inc(&fnic->fnic_stats.reset_stats.fw_reset_timeouts); + } + fnic->fw_reset_done = NULL; +} diff --git a/drivers/scsi/fnic/fnic_fdls.h b/drivers/scsi/fnic/fnic_fdls.h index 531d0b37e450..e2959120c4f9 100644 --- a/drivers/scsi/fnic/fnic_fdls.h +++ b/drivers/scsi/fnic/fnic_fdls.h @@ -410,6 +410,7 @@ void fnic_fdls_add_tport(struct fnic_iport_s *iport, void fnic_fdls_remove_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport, unsigned long flags); +void fnic_fcpio_reset(struct fnic *fnic); /* fip.c */ void fnic_fcoe_send_vlan_req(struct fnic *fnic); @@ -422,7 +423,6 @@ void fnic_handle_fip_timer(struct timer_list *t); extern void fdls_fabric_timer_callback(struct timer_list *t); /* fnic_scsi.c */ -void fnic_scsi_fcpio_reset(struct fnic *fnic); extern void fdls_fabric_timer_callback(struct timer_list *t); void fnic_rport_exch_reset(struct fnic *fnic, u32 fcid); int fnic_fdls_register_portid(struct fnic_iport_s *iport, u32 port_id, diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 8b551b79e087..24d62c0874ac 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -40,6 +40,7 @@ static struct kmem_cache *fnic_sgl_cache[FNIC_SGL_NUM_CACHES]; static struct kmem_cache *fnic_io_req_cache; static struct kmem_cache *fdls_frame_cache; static struct kmem_cache *fdls_frame_elem_cache; +static struct kmem_cache *fdls_frame_recv_cache; static LIST_HEAD(fnic_list); static DEFINE_SPINLOCK(fnic_list_lock); static DEFINE_IDA(fnic_ida); @@ -554,6 +555,7 @@ static int fnic_cleanup(struct fnic *fnic) mempool_destroy(fnic->io_req_pool); mempool_destroy(fnic->frame_pool); mempool_destroy(fnic->frame_elem_pool); + mempool_destroy(fnic->frame_recv_pool); for (i = 0; i < FNIC_SGL_NUM_CACHES; i++) mempool_destroy(fnic->io_sgl_pool[i]); @@ -928,6 +930,14 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } fnic->frame_elem_pool = pool; + pool = mempool_create_slab_pool(FDLS_MIN_FRAMES, + fdls_frame_recv_cache); + if (!pool) { + err = -ENOMEM; + goto err_out_fdls_frame_recv_pool; + } + fnic->frame_recv_pool = pool; + /* setup vlan config, hw inserts vlan header */ fnic->vlan_hw_insert = 1; fnic->vlan_id = 0; @@ -1085,6 +1095,8 @@ err_out_alloc_rq_buf: } vnic_dev_notify_unset(fnic->vdev); err_out_fnic_notify_set: + mempool_destroy(fnic->frame_recv_pool); +err_out_fdls_frame_recv_pool: mempool_destroy(fnic->frame_elem_pool); err_out_fdls_frame_elem_pool: mempool_destroy(fnic->frame_pool); @@ -1157,7 +1169,6 @@ static void fnic_remove(struct pci_dev *pdev) timer_delete_sync(&fnic->enode_ka_timer); timer_delete_sync(&fnic->vn_ka_timer); - fnic_free_txq(&fnic->fip_frame_queue); fnic_fcoe_reset_vlans(fnic); } @@ -1177,8 +1188,8 @@ static void fnic_remove(struct pci_dev *pdev) list_del(&fnic->list); spin_unlock_irqrestore(&fnic_list_lock, flags); - fnic_free_txq(&fnic->frame_queue); - fnic_free_txq(&fnic->tx_queue); + fnic_free_rxq(fnic); + fnic_free_txq(fnic); vnic_dev_notify_unset(fnic->vdev); fnic_free_intr(fnic); @@ -1287,6 +1298,15 @@ static int __init fnic_init_module(void) goto err_create_fdls_frame_cache_elem; } + fdls_frame_recv_cache = kmem_cache_create("fdls_frame_recv", + FNIC_FRAME_HT_ROOM, + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!fdls_frame_recv_cache) { + pr_err("fnic fdls frame recv cach create failed\n"); + err = -ENOMEM; + goto err_create_fdls_frame_recv_cache; + } + fnic_event_queue = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, "fnic_event_wq"); if (!fnic_event_queue) { @@ -1339,6 +1359,8 @@ err_create_fip_workq: if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON) destroy_workqueue(reset_fnic_work_queue); err_create_reset_fnic_workq: + kmem_cache_destroy(fdls_frame_recv_cache); +err_create_fdls_frame_recv_cache: destroy_workqueue(fnic_event_queue); err_create_fnic_workq: kmem_cache_destroy(fdls_frame_elem_cache); diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index 29d7aca06958..6ee3c559e129 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -471,7 +471,6 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost, int sg_count = 0; unsigned long flags = 0; unsigned long ptr; - int io_lock_acquired = 0; uint16_t hwq = 0; struct fnic_tport_s *tport = NULL; struct rport_dd_data_s *rdd_data; @@ -636,7 +635,6 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost, spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags); /* initialize rest of io_req */ - io_lock_acquired = 1; io_req->port_id = rport->port_id; io_req->start_time = jiffies; fnic_priv(sc)->state = FNIC_IOREQ_CMD_PENDING; @@ -689,6 +687,9 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost, /* REVISIT: Use per IO lock in the final code */ fnic_priv(sc)->flags |= FNIC_IO_ISSUED; } + + spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); + out: cmd_trace = ((u64)sc->cmnd[0] << 56 | (u64)sc->cmnd[7] << 40 | (u64)sc->cmnd[8] << 32 | (u64)sc->cmnd[2] << 24 | @@ -699,10 +700,6 @@ out: mqtag, sc, io_req, sg_count, cmd_trace, fnic_flags_and_state(sc)); - /* if only we issued IO, will we have the io lock */ - if (io_lock_acquired) - spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); - atomic_dec(&fnic->in_flight); atomic_dec(&tport->in_flight); @@ -777,7 +774,7 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic, */ if (ret) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); - fnic_free_txq(&fnic->tx_queue); + fnic_free_txq(fnic); goto reset_cmpl_handler_end; } @@ -1972,15 +1969,11 @@ void fnic_scsi_unload(struct fnic *fnic) */ spin_lock_irqsave(&fnic->fnic_lock, flags); fnic->iport.state = FNIC_IPORT_STATE_LINK_WAIT; - spin_unlock_irqrestore(&fnic->fnic_lock, flags); - - if (fdls_get_state(&fnic->iport.fabric) != FDLS_STATE_INIT) - fnic_scsi_fcpio_reset(fnic); - - spin_lock_irqsave(&fnic->fnic_lock, flags); fnic->in_remove = 1; spin_unlock_irqrestore(&fnic->fnic_lock, flags); + fnic_fcpio_reset(fnic); + fnic_flush_tport_event_list(fnic); fnic_delete_fcp_tports(fnic); } @@ -3040,54 +3033,3 @@ int fnic_eh_host_reset_handler(struct scsi_cmnd *sc) ret = fnic_host_reset(shost); return ret; } - - -void fnic_scsi_fcpio_reset(struct fnic *fnic) -{ - unsigned long flags; - enum fnic_state old_state; - struct fnic_iport_s *iport = &fnic->iport; - DECLARE_COMPLETION_ONSTACK(fw_reset_done); - int time_remain; - - /* issue fw reset */ - spin_lock_irqsave(&fnic->fnic_lock, flags); - if (unlikely(fnic->state == FNIC_IN_FC_TRANS_ETH_MODE)) { - /* fw reset is in progress, poll for its completion */ - spin_unlock_irqrestore(&fnic->fnic_lock, flags); - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "fnic is in unexpected state: %d for fw_reset\n", - fnic->state); - return; - } - - old_state = fnic->state; - fnic->state = FNIC_IN_FC_TRANS_ETH_MODE; - - fnic_update_mac_locked(fnic, iport->hwmac); - fnic->fw_reset_done = &fw_reset_done; - spin_unlock_irqrestore(&fnic->fnic_lock, flags); - - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "Issuing fw reset\n"); - if (fnic_fw_reset_handler(fnic)) { - spin_lock_irqsave(&fnic->fnic_lock, flags); - if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE) - fnic->state = old_state; - spin_unlock_irqrestore(&fnic->fnic_lock, flags); - } else { - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "Waiting for fw completion\n"); - time_remain = wait_for_completion_timeout(&fw_reset_done, - msecs_to_jiffies(FNIC_FW_RESET_TIMEOUT)); - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "Woken up after fw completion timeout\n"); - if (time_remain == 0) { - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "FW reset completion timed out after %d ms)\n", - FNIC_FW_RESET_TIMEOUT); - } - atomic64_inc(&fnic->fnic_stats.reset_stats.fw_reset_timeouts); - } - fnic->fw_reset_done = NULL; -} diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index c2b082f1252c..944ce19ae2fc 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1326,7 +1326,7 @@ static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func, if (sts && !wait_for_completion_timeout(&completion, HISI_SAS_WAIT_PHYUP_TIMEOUT)) { - dev_warn(dev, "phy%d wait phyup timed out for func %d\n", + dev_warn(dev, "phy%d wait phyup timed out for func %u\n", phy_no, func); if (phy->in_reset) ret = -ETIMEDOUT; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index f69efc6494b8..14d563e82d20 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -432,7 +432,7 @@ #define CMPLT_HDR_IPTT_OFF 0 #define CMPLT_HDR_IPTT_MSK (0xffff << CMPLT_HDR_IPTT_OFF) #define CMPLT_HDR_DEV_ID_OFF 16 -#define CMPLT_HDR_DEV_ID_MSK (0xffff << CMPLT_HDR_DEV_ID_OFF) +#define CMPLT_HDR_DEV_ID_MSK (0xffffU << CMPLT_HDR_DEV_ID_OFF) /* dw3 */ #define SATA_DISK_IN_ERROR_STATUS_OFF 8 #define SATA_DISK_IN_ERROR_STATUS_MSK (0x1 << SATA_DISK_IN_ERROR_STATUS_OFF) @@ -444,7 +444,7 @@ #define FIS_ATA_STATUS_ERR_OFF 18 #define FIS_ATA_STATUS_ERR_MSK (0x1 << FIS_ATA_STATUS_ERR_OFF) #define FIS_TYPE_SDB_OFF 31 -#define FIS_TYPE_SDB_MSK (0x1 << FIS_TYPE_SDB_OFF) +#define FIS_TYPE_SDB_MSK (0x1U << FIS_TYPE_SDB_OFF) /* ITCT header */ /* qw0 */ @@ -896,7 +896,7 @@ static void setup_itct_v3_hw(struct hisi_hba *hisi_hba, qw0 = HISI_SAS_DEV_TYPE_SATA << ITCT_HDR_DEV_TYPE_OFF; break; default: - dev_warn(dev, "setup itct: unsupported dev type (%d)\n", + dev_warn(dev, "setup itct: unsupported dev type (%u)\n", sas_dev->dev_type); } @@ -1491,7 +1491,7 @@ static void prep_ata_v3_hw(struct hisi_hba *hisi_hba, phy_id = device->phy->identify.phy_identifier; hdr->dw0 |= cpu_to_le32((1U << phy_id) << CMD_HDR_PHY_ID_OFF); - hdr->dw0 |= CMD_HDR_FORCE_PHY_MSK; + hdr->dw0 |= cpu_to_le32(CMD_HDR_FORCE_PHY_MSK); hdr->dw0 |= cpu_to_le32(4U << CMD_HDR_CMD_OFF); } @@ -2847,7 +2847,7 @@ static void wait_cmds_complete_timeout_v3_hw(struct hisi_hba *hisi_hba, static ssize_t intr_conv_v3_hw_show(struct device *dev, struct device_attribute *attr, char *buf) { - return scnprintf(buf, PAGE_SIZE, "%u\n", hisi_sas_intr_conv); + return scnprintf(buf, PAGE_SIZE, "%d\n", hisi_sas_intr_conv); } static DEVICE_ATTR_RO(intr_conv_v3_hw); @@ -3293,7 +3293,7 @@ static int debugfs_set_bist_v3_hw(struct hisi_hba *hisi_hba, bool enable) u32 *fix_code = &hisi_hba->debugfs_bist_fixed_code[0]; struct device *dev = hisi_hba->dev; - dev_info(dev, "BIST info:phy%d link_rate=%d code_mode=%d path_mode=%d ffe={0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x} fixed_code={0x%x, 0x%x}\n", + dev_info(dev, "BIST info:phy%u link_rate=%u code_mode=%u path_mode=%u ffe={0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x} fixed_code={0x%x, 0x%x}\n", phy_no, linkrate, code_mode, path_mode, ffe[FFE_SAS_1_5_GBPS], ffe[FFE_SAS_3_0_GBPS], ffe[FFE_SAS_6_0_GBPS], ffe[FFE_SAS_12_0_GBPS], @@ -3650,7 +3650,7 @@ static void debugfs_print_reg_v3_hw(u32 *regs_val, struct seq_file *s, int i; for (i = 0; i < reg->count; i++) { - int off = i * HISI_SAS_REG_MEM_SIZE; + u32 off = i * HISI_SAS_REG_MEM_SIZE; const char *name; name = debugfs_to_reg_name_v3_hw(off, reg->base_off, diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 99b0750850b2..f6bfe75dd696 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -164,7 +164,7 @@ struct bmic_controller_parameters { struct ctlr_info { unsigned int *reply_map; int ctlr; - char devname[8]; + char devname[16]; char *product_name; struct pci_dev *pdev; u32 board_id; @@ -255,7 +255,7 @@ struct ctlr_info { int remove_in_progress; /* Address of h->q[x] is passed to intr handler to know which queue */ u8 q[MAX_REPLY_QUEUES]; - char intrname[MAX_REPLY_QUEUES][16]; /* "hpsa0-msix00" names */ + char intrname[MAX_REPLY_QUEUES][32]; /* controller and IRQ names */ u32 TMFSupportFlags; /* cache what task mgmt funcs are supported. */ #define HPSATMF_BITS_SUPPORTED (1 << 0) #define HPSATMF_PHYS_LUN_RESET (1 << 1) diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c index b395a9d7c640..61f682800765 100644 --- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c +++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c @@ -3968,6 +3968,7 @@ static const struct target_core_fabric_ops ibmvscsis_ops = { .tfc_wwn_attrs = ibmvscsis_wwn_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_DIRECT_SUBMIT, .direct_submit_supp = 1, }; diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 7b4fe0e6afb2..9260b1c9b0e0 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -267,7 +267,7 @@ iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_conn *conn) struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; struct sock *sk = tcp_sw_conn->sock->sk; - /* restore socket callbacks, see also: iscsi_conn_set_callbacks() */ + /* restore socket callbacks, see also: iscsi_sw_tcp_conn_set_callbacks() */ write_lock_bh(&sk->sk_callback_lock); sk->sk_user_data = NULL; sk->sk_data_ready = tcp_sw_conn->old_data_ready; diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 689793d03c20..b67ea1730dcf 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -552,8 +552,6 @@ struct lpfc_cgn_info { ); __le32 cgn_info_crc; -#define LPFC_CGN_CRC32_MAGIC_NUMBER 0x1EDC6F41 -#define LPFC_CGN_CRC32_SEED 0xFFFFFFFF }; #define LPFC_CGN_INFO_SZ (sizeof(struct lpfc_cgn_info) - \ @@ -812,9 +810,10 @@ struct unsol_rcv_ct_ctx { #define LPFC_USER_LINK_SPEED_16G 16 /* 16 Gigabaud */ #define LPFC_USER_LINK_SPEED_32G 32 /* 32 Gigabaud */ #define LPFC_USER_LINK_SPEED_64G 64 /* 64 Gigabaud */ -#define LPFC_USER_LINK_SPEED_MAX LPFC_USER_LINK_SPEED_64G +#define LPFC_USER_LINK_SPEED_128G 128 /* 128 Gigabaud */ +#define LPFC_USER_LINK_SPEED_MAX LPFC_USER_LINK_SPEED_128G -#define LPFC_LINK_SPEED_STRING "0, 1, 2, 4, 8, 10, 16, 32, 64" +#define LPFC_LINK_SPEED_STRING "0, 1, 2, 4, 8, 10, 16, 32, 64, 128" enum nemb_type { nemb_mse = 1, @@ -1017,7 +1016,6 @@ struct lpfc_hba { #define LPFC_SLI3_CRP_ENABLED 0x08 #define LPFC_SLI3_BG_ENABLED 0x20 #define LPFC_SLI3_DSS_ENABLED 0x40 -#define LPFC_SLI4_PERFH_ENABLED 0x80 #define LPFC_SLI4_PHWQ_ENABLED 0x100 uint32_t iocb_cmd_size; uint32_t iocb_rsp_size; @@ -1190,7 +1188,6 @@ struct lpfc_hba { uint32_t cfg_ras_fwlog_func; uint32_t cfg_enable_bbcr; /* Enable BB Credit Recovery */ uint32_t cfg_enable_dpp; /* Enable Direct Packet Push */ - uint32_t cfg_enable_pbde; uint32_t cfg_enable_mi; struct nvmet_fc_target_port *targetport; lpfc_vpd_t vpd; /* vital product data */ @@ -1667,8 +1664,9 @@ lpfc_phba_elsring(struct lpfc_hba *phba) * @mask: Pointer to phba's cpumask member. * @start: starting cpu index * - * Note: If no valid cpu found, then nr_cpu_ids is returned. + * Returns: next online CPU in @mask on success * + * Note: If no valid cpu found, then nr_cpu_ids is returned. **/ static __always_inline unsigned int lpfc_next_online_cpu(const struct cpumask *mask, unsigned int start) @@ -1680,8 +1678,9 @@ lpfc_next_online_cpu(const struct cpumask *mask, unsigned int start) * lpfc_next_present_cpu - Finds next present CPU after n * @n: the cpu prior to search * - * Note: If no next present cpu, then fallback to first present cpu. + * Returns: next present CPU after CPU @n * + * Note: If no next present cpu, then fallback to first present cpu. **/ static __always_inline unsigned int lpfc_next_present_cpu(int n) { @@ -1691,7 +1690,7 @@ static __always_inline unsigned int lpfc_next_present_cpu(int n) /** * lpfc_sli4_mod_hba_eq_delay - update EQ delay * @phba: Pointer to HBA context object. - * @q: The Event Queue to update. + * @eq: The Event Queue to update. * @delay: The delay value (in us) to be written. * **/ @@ -1753,8 +1752,9 @@ static const char *routine(enum enum_name table_key) \ * Pr Tag 1 0 N * Pr Tag 1 1 Y * Pr Tag 2 * Y - --------------------------------------------------- + * --------------------------------------------------- * + * Returns: whether VMID is enabled **/ static inline int lpfc_is_vmid_enabled(struct lpfc_hba *phba) { diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 53f41d68e0d8..c91fa44b12d4 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -1,8 +1,8 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * @@ -4415,7 +4415,7 @@ static DEVICE_ATTR_RO(lpfc_static_vport); /* # lpfc_link_speed: Link speed selection for initializing the Fibre Channel # connection. -# Value range is [0,16]. Default value is 0. +# Value range is [0,128]. Default value is 0. */ /** * lpfc_link_speed_store - Set the adapters link speed @@ -4468,14 +4468,15 @@ lpfc_link_speed_store(struct device *dev, struct device_attribute *attr, "3055 lpfc_link_speed changed from %d to %d %s\n", phba->cfg_link_speed, val, nolip ? "(nolip)" : "(lip)"); - if (((val == LPFC_USER_LINK_SPEED_1G) && !(phba->lmt & LMT_1Gb)) || - ((val == LPFC_USER_LINK_SPEED_2G) && !(phba->lmt & LMT_2Gb)) || - ((val == LPFC_USER_LINK_SPEED_4G) && !(phba->lmt & LMT_4Gb)) || - ((val == LPFC_USER_LINK_SPEED_8G) && !(phba->lmt & LMT_8Gb)) || - ((val == LPFC_USER_LINK_SPEED_10G) && !(phba->lmt & LMT_10Gb)) || - ((val == LPFC_USER_LINK_SPEED_16G) && !(phba->lmt & LMT_16Gb)) || - ((val == LPFC_USER_LINK_SPEED_32G) && !(phba->lmt & LMT_32Gb)) || - ((val == LPFC_USER_LINK_SPEED_64G) && !(phba->lmt & LMT_64Gb))) { + if ((val == LPFC_USER_LINK_SPEED_1G && !(phba->lmt & LMT_1Gb)) || + (val == LPFC_USER_LINK_SPEED_2G && !(phba->lmt & LMT_2Gb)) || + (val == LPFC_USER_LINK_SPEED_4G && !(phba->lmt & LMT_4Gb)) || + (val == LPFC_USER_LINK_SPEED_8G && !(phba->lmt & LMT_8Gb)) || + (val == LPFC_USER_LINK_SPEED_10G && !(phba->lmt & LMT_10Gb)) || + (val == LPFC_USER_LINK_SPEED_16G && !(phba->lmt & LMT_16Gb)) || + (val == LPFC_USER_LINK_SPEED_32G && !(phba->lmt & LMT_32Gb)) || + (val == LPFC_USER_LINK_SPEED_64G && !(phba->lmt & LMT_64Gb)) || + (val == LPFC_USER_LINK_SPEED_128G && !(phba->lmt & LMT_128Gb))) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2879 lpfc_link_speed attribute cannot be set " "to %d. Speed is not supported by this port.\n", @@ -4500,6 +4501,7 @@ lpfc_link_speed_store(struct device *dev, struct device_attribute *attr, case LPFC_USER_LINK_SPEED_16G: case LPFC_USER_LINK_SPEED_32G: case LPFC_USER_LINK_SPEED_64G: + case LPFC_USER_LINK_SPEED_128G: prev_val = phba->cfg_link_speed; phba->cfg_link_speed = val; if (nolip) @@ -4564,6 +4566,7 @@ lpfc_link_speed_init(struct lpfc_hba *phba, int val) case LPFC_USER_LINK_SPEED_16G: case LPFC_USER_LINK_SPEED_32G: case LPFC_USER_LINK_SPEED_64G: + case LPFC_USER_LINK_SPEED_128G: phba->cfg_link_speed = val; return 0; default: @@ -7467,8 +7470,6 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) phba->cfg_auto_imax = (phba->cfg_fcp_imax) ? 0 : 1; - phba->cfg_enable_pbde = 0; - /* A value of 0 means use the number of CPUs found in the system */ if (phba->cfg_hdw_queue == 0) phba->cfg_hdw_queue = phba->sli4_hba.num_present_cpu; diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index efeb61b15a5b..8a5b76bdea06 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -86,7 +86,7 @@ void lpfc_cmf_stop(struct lpfc_hba *phba); void lpfc_init_congestion_stat(struct lpfc_hba *phba); void lpfc_init_congestion_buf(struct lpfc_hba *phba); int lpfc_sli4_cgn_params_read(struct lpfc_hba *phba); -uint32_t lpfc_cgn_calc_crc32(void *bufp, uint32_t sz, uint32_t seed); +uint32_t lpfc_cgn_calc_crc32(const void *data, size_t size); int lpfc_config_cgn_signal(struct lpfc_hba *phba); int lpfc_issue_cmf_sync_wqe(struct lpfc_hba *phba, u32 ms, u64 total); void lpfc_cgn_dump_rxmonitor(struct lpfc_hba *phba); @@ -660,6 +660,7 @@ void lpfc_wqe_cmd_template(void); void lpfc_nvmet_cmd_template(void); void lpfc_nvme_cancel_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, uint32_t stat, uint32_t param); +void lpfc_nvme_flush_abts_list(struct lpfc_hba *phba); void lpfc_nvmels_flush_cmd(struct lpfc_hba *phba); extern int lpfc_enable_nvmet_cnt; extern unsigned long long lpfc_enable_nvmet[]; diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index d64f4acfcdae..c7853e7fe071 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -2427,13 +2427,14 @@ lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* CGN is only for the physical port, no vports */ if (lpfc_fdmi_cmd(vport, ndlp, cmd, - LPFC_FDMI_VENDOR_ATTR_mi) == 0) + LPFC_FDMI_VENDOR_ATTR_mi) == 0) { phba->link_flag |= LS_CT_VEN_RPA; - lpfc_printf_log(phba, KERN_INFO, + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY | LOG_ELS, "6458 Send MI FDMI:%x Flag x%x\n", phba->sli4_hba.pc_sli4_params.mi_ver, phba->link_flag); + } } else { lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY | LOG_ELS, @@ -3214,7 +3215,7 @@ lpfc_fdmi_cmd(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_iocbq *rspiocb); if (!ndlp) - return 0; + goto fdmi_cmd_exit; cmpl = lpfc_cmpl_ct_disc_fdmi; /* called from discovery */ @@ -3320,7 +3321,7 @@ hba_out: if (vport->port_type != LPFC_PHYSICAL_PORT) { ndlp = lpfc_findnode_did(phba->pport, FDMI_DID); if (!ndlp) - return 0; + goto fdmi_cmd_free_rspvirt; } fallthrough; case SLI_MGMT_RPA: @@ -3396,7 +3397,7 @@ port_out: if (vport->port_type != LPFC_PHYSICAL_PORT) { ndlp = lpfc_findnode_did(phba->pport, FDMI_DID); if (!ndlp) - return 0; + goto fdmi_cmd_free_rspvirt; } fallthrough; case SLI_MGMT_DPA: diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h index de0adeecf668..a377e97cbe65 100644 --- a/drivers/scsi/lpfc/lpfc_disc.h +++ b/drivers/scsi/lpfc/lpfc_disc.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2013 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -137,7 +137,8 @@ struct lpfc_nodelist { uint16_t nlp_maxframe; /* Max RCV frame size */ uint8_t nlp_class_sup; /* Supported Classes */ uint8_t nlp_retry; /* used for ELS retries */ - uint8_t nlp_fcp_info; /* class info, bits 0-3 */ + uint8_t nlp_fcp_info; /* class info, bits 0-2 */ +#define NLP_FCP_CLASS_MASK 0x07 /* class info bitmask */ #define NLP_FCP_2_DEVICE 0x10 /* FCP-2 device */ u8 nlp_nvme_info; /* NVME NSLER Support */ uint8_t vmid_support; /* destination VMID support */ diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index cee709617a31..4e3fe89283e4 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -1107,7 +1107,7 @@ stop_rr_fcf_flogi: vport->vmid_flag = 0; } if (sp->cmn.priority_tagging) - vport->phba->pport->vmid_flag |= (LPFC_VMID_ISSUE_QFPA | + vport->vmid_flag |= (LPFC_VMID_ISSUE_QFPA | LPFC_VMID_TYPE_PRIO); /* @@ -1303,8 +1303,12 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, ndlp->nlp_DID, ELS_CMD_FLOGI); - if (!elsiocb) + if (!elsiocb) { + lpfc_vport_set_state(vport, FC_VPORT_FAILED); + lpfc_printf_vlog(vport, KERN_WARNING, LOG_ELS | LOG_DISCOVERY, + "4296 Unable to prepare FLOGI iocb\n"); return 1; + } wqe = &elsiocb->wqe; pcmd = (uint8_t *)elsiocb->cmd_dmabuf->virt; @@ -1394,10 +1398,8 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, phba->sli3_options, 0, 0); elsiocb->ndlp = lpfc_nlp_get(ndlp); - if (!elsiocb->ndlp) { - lpfc_els_free_iocb(phba, elsiocb); - return 1; - } + if (!elsiocb->ndlp) + goto err_out; /* Avoid race with FLOGI completion and hba_flags. */ set_bit(HBA_FLOGI_ISSUED, &phba->hba_flag); @@ -1407,9 +1409,8 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (rc == IOCB_ERROR) { clear_bit(HBA_FLOGI_ISSUED, &phba->hba_flag); clear_bit(HBA_FLOGI_OUTSTANDING, &phba->hba_flag); - lpfc_els_free_iocb(phba, elsiocb); lpfc_nlp_put(ndlp); - return 1; + goto err_out; } /* Clear external loopback plug detected flag */ @@ -1474,6 +1475,13 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, } return 0; + + err_out: + lpfc_els_free_iocb(phba, elsiocb); + lpfc_vport_set_state(vport, FC_VPORT_FAILED); + lpfc_printf_vlog(vport, KERN_WARNING, LOG_ELS | LOG_DISCOVERY, + "4297 Issue FLOGI: Cannot send IOCB\n"); + return 1; } /** @@ -2641,7 +2649,9 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, } npr->estabImagePair = 1; npr->readXferRdyDis = 1; - if (vport->cfg_first_burst_size) + if (phba->sli_rev == LPFC_SLI_REV4 && + !test_bit(HBA_FCOE_MODE, &phba->hba_flag) && + vport->cfg_first_burst_size) npr->writeXferRdyDis = 1; /* For FCP support */ @@ -4319,18 +4329,28 @@ lpfc_format_edc_cgn_desc(struct lpfc_hba *phba, struct fc_tlv_desc *tlv) static bool lpfc_link_is_lds_capable(struct lpfc_hba *phba) { - if (!(phba->lmt & LMT_64Gb)) + if (!(phba->lmt & (LMT_64Gb | LMT_128Gb))) return false; if (phba->sli_rev != LPFC_SLI_REV4) return false; if (phba->sli4_hba.conf_trunk) { - if (phba->trunk_link.phy_lnk_speed == LPFC_USER_LINK_SPEED_64G) + switch (phba->trunk_link.phy_lnk_speed) { + case LPFC_USER_LINK_SPEED_128G: + case LPFC_USER_LINK_SPEED_64G: return true; - } else if (phba->fc_linkspeed == LPFC_LINK_SPEED_64GHZ) { + default: + return false; + } + } + + switch (phba->fc_linkspeed) { + case LPFC_LINK_SPEED_128GHZ: + case LPFC_LINK_SPEED_64GHZ: return true; + default: + return false; } - return false; } /** @@ -10291,10 +10311,8 @@ cleanup: cpu_to_le16(value); cp->cgn_warn_freq = cpu_to_le16(value); - crc = lpfc_cgn_calc_crc32 - (cp, - LPFC_CGN_INFO_SZ, - LPFC_CGN_CRC32_SEED); + crc = lpfc_cgn_calc_crc32( + cp, LPFC_CGN_INFO_SZ); cp->cgn_info_crc = cpu_to_le32(crc); } diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 8aaf05d7bb0a..f3a85f6c796e 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -425,7 +425,6 @@ lpfc_check_nlp_post_devloss(struct lpfc_vport *vport, { if (test_and_clear_bit(NLP_IN_RECOV_POST_DEV_LOSS, &ndlp->save_flags)) { clear_bit(NLP_DROPPED, &ndlp->nlp_flag); - lpfc_nlp_get(ndlp); lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY | LOG_NODE, "8438 Devloss timeout reversed on DID x%x " "refcnt %d ndlp %p flag x%lx " @@ -3174,7 +3173,11 @@ lpfc_init_vfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) return; } - lpfc_initial_flogi(vport); + if (!lpfc_initial_flogi(vport)) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX | LOG_ELS, + "2345 Can't issue initial FLOGI\n"); + lpfc_vport_set_state(vport, FC_VPORT_FAILED); + } mempool_free(mboxq, phba->mbox_mem_pool); return; } @@ -3247,8 +3250,14 @@ lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) return; } - if (phba->link_flag & LS_NPIV_FAB_SUPPORTED) - lpfc_initial_fdisc(vport); + if (phba->link_flag & LS_NPIV_FAB_SUPPORTED) { + if (!lpfc_initial_fdisc(vport)) { + lpfc_printf_vlog(vport, KERN_WARNING, + LOG_MBOX | LOG_ELS, + "2346 Can't issue initial FDISC\n"); + lpfc_vport_set_state(vport, FC_VPORT_FAILED); + } + } else { lpfc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP); lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, @@ -3808,7 +3817,7 @@ lpfc_mbx_cmpl_read_topology(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) if (phba->cmf_active_mode != LPFC_CFG_OFF) lpfc_cmf_signal_init(phba); - if (phba->lmt & LMT_64Gb) + if (phba->lmt & (LMT_64Gb | LMT_128Gb)) lpfc_read_lds_params(phba); } else if (attn_type == LPFC_ATT_LINK_DOWN || @@ -4401,7 +4410,7 @@ out: LOG_INIT | LOG_ELS | LOG_DISCOVERY, "4220 Issue EDC status x%x Data x%x\n", rc, phba->cgn_init_reg_signal); - } else if (phba->lmt & LMT_64Gb) { + } else if (phba->lmt & (LMT_64Gb | LMT_128Gb)) { /* may send link fault capability descriptor */ lpfc_issue_els_edc(vport, 0); } else { @@ -5228,12 +5237,11 @@ lpfc_set_unreg_login_mbx_cmpl(struct lpfc_hba *phba, struct lpfc_vport *vport, /* * Free rpi associated with LPFC_NODELIST entry. - * This routine is called from lpfc_freenode(), when we are removing - * a LPFC_NODELIST entry. It is also called if the driver initiates a - * LOGO that completes successfully, and we are waiting to PLOGI back - * to the remote NPort. In addition, it is called after we receive - * and unsolicated ELS cmd, send back a rsp, the rsp completes and - * we are waiting to PLOGI back to the remote NPort. + * This routine is called if the driver initiates a LOGO that completes + * successfully, and we are waiting to PLOGI back to the remote NPort. + * In addition, it is called after we receive and unsolicated ELS cmd, + * send back a rsp, the rsp completes and we are waiting to PLOGI back + * to the remote NPort. */ int lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) @@ -6599,11 +6607,6 @@ lpfc_nlp_get(struct lpfc_nodelist *ndlp) unsigned long flags; if (ndlp) { - lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE, - "node get: did:x%x flg:x%lx refcnt:x%x", - ndlp->nlp_DID, ndlp->nlp_flag, - kref_read(&ndlp->kref)); - /* The check of ndlp usage to prevent incrementing the * ndlp reference count that is in the process of being * released. @@ -6611,9 +6614,8 @@ lpfc_nlp_get(struct lpfc_nodelist *ndlp) spin_lock_irqsave(&ndlp->lock, flags); if (!kref_get_unless_zero(&ndlp->kref)) { spin_unlock_irqrestore(&ndlp->lock, flags); - lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_NODE, - "0276 %s: ndlp:x%px refcnt:%d\n", - __func__, (void *)ndlp, kref_read(&ndlp->kref)); + pr_info("0276 %s: NDLP x%px has zero reference count. " + "Exiting\n", __func__, ndlp); return NULL; } spin_unlock_irqrestore(&ndlp->lock, flags); diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index b2e353590ebb..6326f7353dd6 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -1771,6 +1771,7 @@ struct lpfc_fdmi_reg_portattr { #define PCI_DEVICE_ID_LANCER_G6_FC 0xe300 #define PCI_DEVICE_ID_LANCER_G7_FC 0xf400 #define PCI_DEVICE_ID_LANCER_G7P_FC 0xf500 +#define PCI_DEVICE_ID_LANCER_G8_FC 0xd300 #define PCI_DEVICE_ID_SAT_SMB 0xf011 #define PCI_DEVICE_ID_SAT_MID 0xf015 #define PCI_DEVICE_ID_RFLY 0xf095 diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index c000474c3066..f91bde4a6c38 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -1,8 +1,8 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2009-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * @@ -100,7 +100,8 @@ struct lpfc_sli_intf { #define lpfc_sli_intf_sli_family_MASK 0x0000000F #define lpfc_sli_intf_sli_family_WORD word0 #define LPFC_SLI_INTF_FAMILY_BE2 0x0 -#define LPFC_SLI_INTF_FAMILY_BE3 0x1 +#define LPFC_SLI_INTF_ASIC_ID 0x1 /* Refer to ASIC_ID register */ +#define LPFC_SLI_INTF_FAMILY_BE3 0x3 #define LPFC_SLI_INTF_FAMILY_LNCR_A0 0xa #define LPFC_SLI_INTF_FAMILY_LNCR_B0 0xb #define LPFC_SLI_INTF_FAMILY_G6 0xc @@ -118,6 +119,17 @@ struct lpfc_sli_intf { #define LPFC_SLI_INTF_IF_TYPE_VIRT 1 }; +struct lpfc_asic_id { + u32 word0; +#define lpfc_asic_id_gen_num_SHIFT 8 +#define lpfc_asic_id_gen_num_MASK 0x000000FF +#define lpfc_asic_id_gen_num_WORD word0 +#define LPFC_SLI_INTF_FAMILY_G8 0x10 +#define lpfc_asic_id_rev_num_SHIFT 0 +#define lpfc_asic_id_rev_num_MASK 0x000000FF +#define lpfc_asic_id_rev_num_WORD word0 +}; + #define LPFC_SLI4_MBX_EMBED true #define LPFC_SLI4_MBX_NEMBED false @@ -624,6 +636,10 @@ struct lpfc_register { #define LPFC_PORT_SEM_UE_RECOVERABLE 0xE000 #define LPFC_PORT_SEM_MASK 0xF000 + +/* The following are config space register offsets */ +#define LPFC_ASIC_ID_OFFSET 0x0308 + /* The following BAR0 Registers apply to SLI4 if_type 0 UCNAs. */ #define LPFC_UERR_STATUS_HI 0x00A4 #define LPFC_UERR_STATUS_LO 0x00A0 @@ -632,7 +648,6 @@ struct lpfc_register { /* The following BAR0 register sets are defined for if_type 0 and 2 UCNAs. */ #define LPFC_SLI_INTF 0x0058 -#define LPFC_SLI_ASIC_VER 0x009C #define LPFC_CTL_PORT_SEM_OFFSET 0x400 #define lpfc_port_smphr_perr_SHIFT 31 @@ -3062,9 +3077,6 @@ struct lpfc_mbx_request_features { #define lpfc_mbx_rq_ftr_rq_iaar_SHIFT 9 #define lpfc_mbx_rq_ftr_rq_iaar_MASK 0x00000001 #define lpfc_mbx_rq_ftr_rq_iaar_WORD word2 -#define lpfc_mbx_rq_ftr_rq_perfh_SHIFT 11 -#define lpfc_mbx_rq_ftr_rq_perfh_MASK 0x00000001 -#define lpfc_mbx_rq_ftr_rq_perfh_WORD word2 #define lpfc_mbx_rq_ftr_rq_mrqp_SHIFT 16 #define lpfc_mbx_rq_ftr_rq_mrqp_MASK 0x00000001 #define lpfc_mbx_rq_ftr_rq_mrqp_WORD word2 @@ -3096,9 +3108,6 @@ struct lpfc_mbx_request_features { #define lpfc_mbx_rq_ftr_rsp_ifip_SHIFT 7 #define lpfc_mbx_rq_ftr_rsp_ifip_MASK 0x00000001 #define lpfc_mbx_rq_ftr_rsp_ifip_WORD word3 -#define lpfc_mbx_rq_ftr_rsp_perfh_SHIFT 11 -#define lpfc_mbx_rq_ftr_rsp_perfh_MASK 0x00000001 -#define lpfc_mbx_rq_ftr_rsp_perfh_WORD word3 #define lpfc_mbx_rq_ftr_rsp_mrqp_SHIFT 16 #define lpfc_mbx_rq_ftr_rsp_mrqp_MASK 0x00000001 #define lpfc_mbx_rq_ftr_rsp_mrqp_WORD word3 @@ -3461,10 +3470,6 @@ struct lpfc_sli4_parameters { #define cfg_pvl_MASK 0x00000001 #define cfg_pvl_WORD word19 -#define cfg_pbde_SHIFT 20 -#define cfg_pbde_MASK 0x00000001 -#define cfg_pbde_WORD word19 - uint32_t word20; #define cfg_max_tow_xri_SHIFT 0 #define cfg_max_tow_xri_MASK 0x0000ffff @@ -4484,9 +4489,6 @@ struct wqe_common { #define wqe_irsp_SHIFT 4 #define wqe_irsp_MASK 0x00000001 #define wqe_irsp_WORD word11 -#define wqe_pbde_SHIFT 5 -#define wqe_pbde_MASK 0x00000001 -#define wqe_pbde_WORD word11 #define wqe_sup_SHIFT 6 #define wqe_sup_MASK 0x00000001 #define wqe_sup_WORD word11 @@ -4978,6 +4980,7 @@ union lpfc_wqe128 { #define MAGIC_NUMBER_G6 0xFEAA0003 #define MAGIC_NUMBER_G7 0xFEAA0005 #define MAGIC_NUMBER_G7P 0xFEAA0020 +#define MAGIC_NUMBER_G8 0xFEAA0070 struct lpfc_grp_hdr { uint32_t size; diff --git a/drivers/scsi/lpfc/lpfc_ids.h b/drivers/scsi/lpfc/lpfc_ids.h index 0b1616e93cf4..a0a6e2d379b8 100644 --- a/drivers/scsi/lpfc/lpfc_ids.h +++ b/drivers/scsi/lpfc/lpfc_ids.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2022 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -118,6 +118,8 @@ const struct pci_device_id lpfc_id_table[] = { PCI_ANY_ID, PCI_ANY_ID, }, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_G7P_FC, PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_G8_FC, + PCI_ANY_ID, PCI_ANY_ID, }, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SKYHAWK, PCI_ANY_ID, PCI_ANY_ID, }, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SKYHAWK_VF, diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index e9d9ac7da485..968a25235a2d 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1,8 +1,8 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * @@ -22,6 +22,7 @@ *******************************************************************/ #include +#include #include #include #include @@ -788,7 +789,9 @@ lpfc_hba_init_link_fc_topology(struct lpfc_hba *phba, uint32_t fc_topology, ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_32G) && !(phba->lmt & LMT_32Gb)) || ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_64G) && - !(phba->lmt & LMT_64Gb))) { + !(phba->lmt & LMT_64Gb)) || + ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_128G) && + !(phba->lmt & LMT_128Gb))) { /* Reset link speed to auto */ lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "1302 Invalid speed for this board:%d " @@ -1087,7 +1090,6 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba) struct lpfc_async_xchg_ctx *ctxp, *ctxp_next; struct lpfc_sli4_hdw_queue *qp; LIST_HEAD(aborts); - LIST_HEAD(nvme_aborts); LIST_HEAD(nvmet_aborts); struct lpfc_sglq *sglq_entry = NULL; int cnt, idx; @@ -1946,6 +1948,7 @@ lpfc_sli4_port_sta_fn_reset(struct lpfc_hba *phba, int mbx_action, lpfc_offline_prep(phba, mbx_action); lpfc_sli_flush_io_rings(phba); + lpfc_nvme_flush_abts_list(phba); lpfc_nvmels_flush_cmd(phba); lpfc_offline(phba); /* release interrupt for possible resource change */ @@ -2534,7 +2537,9 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) return; } - if (phba->lmt & LMT_64Gb) + if (phba->lmt & LMT_128Gb) + max_speed = 128; + else if (phba->lmt & LMT_64Gb) max_speed = 64; else if (phba->lmt & LMT_32Gb) max_speed = 32; @@ -2752,6 +2757,9 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) case PCI_DEVICE_ID_LANCER_G7P_FC: m = (typeof(m)){"LPe38000", "PCIe", "Fibre Channel Adapter"}; break; + case PCI_DEVICE_ID_LANCER_G8_FC: + m = (typeof(m)){"LPe42100", "PCIe", "Fibre Channel Adapter"}; + break; case PCI_DEVICE_ID_SKYHAWK: case PCI_DEVICE_ID_SKYHAWK_VF: oneConnect = 1; @@ -5634,8 +5642,7 @@ lpfc_cgn_update_stat(struct lpfc_hba *phba, uint32_t dtag) cp->cgn_stat_npm = value; } - value = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ, - LPFC_CGN_CRC32_SEED); + value = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ); cp->cgn_info_crc = cpu_to_le32(value); } @@ -5897,8 +5904,7 @@ lpfc_cmf_stats_timer(struct hrtimer *timer) cp->cgn_warn_freq = cpu_to_le16(value); cp->cgn_alarm_freq = cpu_to_le16(value); - lvalue = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ, - LPFC_CGN_CRC32_SEED); + lvalue = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ); cp->cgn_info_crc = cpu_to_le32(lvalue); hrtimer_forward_now(timer, ktime_set(0, LPFC_SEC_MIN * NSEC_PER_SEC)); @@ -7121,8 +7127,7 @@ lpfc_cgn_params_parse(struct lpfc_hba *phba, cp->cgn_info_level0 = phba->cgn_p.cgn_param_level0; cp->cgn_info_level1 = phba->cgn_p.cgn_param_level1; cp->cgn_info_level2 = phba->cgn_p.cgn_param_level2; - crc = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ, - LPFC_CGN_CRC32_SEED); + crc = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ); cp->cgn_info_crc = cpu_to_le32(crc); } spin_unlock_irq(&phba->hbalock); @@ -8283,7 +8288,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) phba->cfg_total_seg_cnt, phba->cfg_scsi_seg_cnt, phba->cfg_nvme_seg_cnt); - i = min(phba->cfg_sg_dma_buf_size, SLI4_PAGE_SIZE); + i = min_t(u32, phba->cfg_sg_dma_buf_size, SLI4_PAGE_SIZE); phba->lpfc_sg_dma_buf_pool = dma_pool_create("lpfc_sg_dma_buf_pool", @@ -10146,6 +10151,10 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) phba->cfg_link_speed = LPFC_USER_LINK_SPEED_64G; break; + case LINK_SPEED_128G: + phba->cfg_link_speed = + LPFC_USER_LINK_SPEED_128G; + break; case 0xffff: phba->cfg_link_speed = LPFC_USER_LINK_SPEED_AUTO; @@ -11795,6 +11804,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) unsigned long bar0map_len, bar1map_len, bar2map_len; int error; uint32_t if_type; + u8 sli_family; if (!pdev) return -ENODEV; @@ -11825,6 +11835,14 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) return -ENODEV; } + /* Check if ASIC_ID register should be read */ + sli_family = bf_get(lpfc_sli_intf_sli_family, &phba->sli4_hba.sli_intf); + if (sli_family == LPFC_SLI_INTF_ASIC_ID) { + if (pci_read_config_dword(pdev, LPFC_ASIC_ID_OFFSET, + &phba->sli4_hba.asic_id.word0)) + return -ENODEV; + } + if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf); /* * Get the bus address of SLI4 device Bar regions and the @@ -13043,6 +13061,10 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) /* Iterate to next offline or online cpu in aff_mask */ cpu = cpumask_next(cpu, aff_mask); + /* Reached the end of the aff_mask */ + if (cpu >= nr_cpu_ids) + break; + /* Find next online cpu in aff_mask to set affinity */ cpu_select = lpfc_next_online_cpu(aff_mask, cpu); } else if (vectors == 1) { @@ -13495,54 +13517,14 @@ lpfc_sli4_hba_unset(struct lpfc_hba *phba) phba->pport->work_port_events = 0; } -static uint32_t -lpfc_cgn_crc32(uint32_t crc, u8 byte) -{ - uint32_t msb = 0; - uint32_t bit; - - for (bit = 0; bit < 8; bit++) { - msb = (crc >> 31) & 1; - crc <<= 1; - - if (msb ^ (byte & 1)) { - crc ^= LPFC_CGN_CRC32_MAGIC_NUMBER; - crc |= 1; - } - byte >>= 1; - } - return crc; -} - -static uint32_t -lpfc_cgn_reverse_bits(uint32_t wd) -{ - uint32_t result = 0; - uint32_t i; - - for (i = 0; i < 32; i++) { - result <<= 1; - result |= (1 & (wd >> i)); - } - return result; -} - /* * The routine corresponds with the algorithm the HBA firmware * uses to validate the data integrity. */ uint32_t -lpfc_cgn_calc_crc32(void *ptr, uint32_t byteLen, uint32_t crc) +lpfc_cgn_calc_crc32(const void *data, size_t size) { - uint32_t i; - uint32_t result; - uint8_t *data = (uint8_t *)ptr; - - for (i = 0; i < byteLen; ++i) - crc = lpfc_cgn_crc32(crc, data[i]); - - result = ~lpfc_cgn_reverse_bits(crc); - return result; + return ~crc32c(~0, data, size); } void @@ -13591,7 +13573,7 @@ lpfc_init_congestion_buf(struct lpfc_hba *phba) cp->cgn_warn_freq = cpu_to_le16(LPFC_FPIN_INIT_FREQ); cp->cgn_alarm_freq = cpu_to_le16(LPFC_FPIN_INIT_FREQ); - crc = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ, LPFC_CGN_CRC32_SEED); + crc = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ); cp->cgn_info_crc = cpu_to_le32(crc); phba->cgn_evt_timestamp = jiffies + @@ -13614,7 +13596,7 @@ lpfc_init_congestion_stat(struct lpfc_hba *phba) memset(&cp->cgn_stat, 0, sizeof(cp->cgn_stat)); lpfc_cgn_update_tstamp(phba, &cp->stat_start); - crc = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ, LPFC_CGN_CRC32_SEED); + crc = lpfc_cgn_calc_crc32(cp, LPFC_CGN_INFO_SZ); cp->cgn_info_crc = cpu_to_le32(crc); } @@ -13756,7 +13738,9 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) sli4_params->cqv = bf_get(cfg_cqv, mbx_sli4_parameters); sli4_params->mqv = bf_get(cfg_mqv, mbx_sli4_parameters); sli4_params->wqv = bf_get(cfg_wqv, mbx_sli4_parameters); - sli4_params->rqv = bf_get(cfg_rqv, mbx_sli4_parameters); + sli4_params->rqv = + (sli4_params->if_type < LPFC_SLI_INTF_IF_TYPE_2) ? + LPFC_Q_CREATE_VERSION_0 : LPFC_Q_CREATE_VERSION_1; sli4_params->eqav = bf_get(cfg_eqav, mbx_sli4_parameters); sli4_params->cqav = bf_get(cfg_cqav, mbx_sli4_parameters); sli4_params->wqsize = bf_get(cfg_wqsize, mbx_sli4_parameters); @@ -13818,12 +13802,6 @@ fcponly: if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) phba->cfg_sg_seg_cnt = LPFC_MAX_NVME_SEG_CNT; - /* Enable embedded Payload BDE if support is indicated */ - if (bf_get(cfg_pbde, mbx_sli4_parameters)) - phba->cfg_enable_pbde = 1; - else - phba->cfg_enable_pbde = 0; - /* * To support Suppress Response feature we must satisfy 3 conditions. * lpfc_suppress_rsp module parameter must be set (default). @@ -13858,9 +13836,8 @@ fcponly: phba->fcp_embed_io = 0; lpfc_printf_log(phba, KERN_INFO, LOG_INIT | LOG_NVME, - "6422 XIB %d PBDE %d: FCP %d NVME %d %d %d\n", + "6422 XIB %d: FCP %d NVME %d %d %d\n", bf_get(cfg_xib, mbx_sli4_parameters), - phba->cfg_enable_pbde, phba->fcp_embed_io, sli4_params->nvme, phba->cfg_nvme_embed_cmd, phba->cfg_suppress_rsp); @@ -14525,6 +14502,12 @@ lpfc_log_write_firmware_error(struct lpfc_hba *phba, uint32_t offset, u8 sli_family; sli_family = bf_get(lpfc_sli_intf_sli_family, &phba->sli4_hba.sli_intf); + + /* Refer to ASIC_ID register case */ + if (sli_family == LPFC_SLI_INTF_ASIC_ID) + sli_family = bf_get(lpfc_asic_id_gen_num, + &phba->sli4_hba.asic_id); + /* Three cases: (1) FW was not supported on the detected adapter. * (2) FW update has been locked out administratively. * (3) Some other error during FW update. @@ -14537,7 +14520,9 @@ lpfc_log_write_firmware_error(struct lpfc_hba *phba, uint32_t offset, (sli_family == LPFC_SLI_INTF_FAMILY_G7 && magic_number != MAGIC_NUMBER_G7) || (sli_family == LPFC_SLI_INTF_FAMILY_G7P && - magic_number != MAGIC_NUMBER_G7P)) { + magic_number != MAGIC_NUMBER_G7P) || + (sli_family == LPFC_SLI_INTF_FAMILY_G8 && + magic_number != MAGIC_NUMBER_G8)) { lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "3030 This firmware version is not supported on" " this HBA model. Device:%x Magic:%x Type:%x " diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index d07c2786cb12..4c058904758d 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -625,6 +625,10 @@ lpfc_init_link(struct lpfc_hba * phba, mb->un.varInitLnk.link_flags |= FLAGS_LINK_SPEED; mb->un.varInitLnk.link_speed = LINK_SPEED_64G; break; + case LPFC_USER_LINK_SPEED_128G: + mb->un.varInitLnk.link_flags |= FLAGS_LINK_SPEED; + mb->un.varInitLnk.link_speed = LINK_SPEED_128G; + break; case LPFC_USER_LINK_SPEED_AUTO: default: mb->un.varInitLnk.link_speed = LINK_SPEED_AUTO; @@ -2139,7 +2143,6 @@ lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq) /* Set up host requested features. */ bf_set(lpfc_mbx_rq_ftr_rq_fcpi, &mboxq->u.mqe.un.req_ftrs, 1); - bf_set(lpfc_mbx_rq_ftr_rq_perfh, &mboxq->u.mqe.un.req_ftrs, 1); /* Enable DIF (block guard) only if configured to do so. */ if (phba->cfg_enable_bg) diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 5e431928de0b..9c449055a55e 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -316,8 +316,7 @@ lpfc_defer_plogi_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *login_mbox) struct lpfc_iocbq *save_iocb; struct lpfc_nodelist *ndlp; MAILBOX_t *mb = &login_mbox->u.mb; - - int rc; + int rc = 0; ndlp = login_mbox->ctx_ndlp; save_iocb = login_mbox->ctx_u.save_iocb; @@ -346,7 +345,7 @@ lpfc_defer_plogi_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *login_mbox) * completes. This ensures, in Pt2Pt, that the PLOGI LS_ACC is sent * before the PRLI. */ - if (!test_bit(FC_PT2PT, &ndlp->vport->fc_flag)) { + if (!test_bit(FC_PT2PT, &ndlp->vport->fc_flag) || mb->mbxStatus || rc) { /* Now process the REG_RPI cmpl */ lpfc_mbx_cmpl_reg_login(phba, login_mbox); clear_bit(NLP_ACC_REGLOGIN, &ndlp->nlp_flag); @@ -525,13 +524,13 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* Issue CONFIG_LINK for SLI3 or REG_VFI for SLI4, * to account for updated TOV's / parameters */ - if (phba->sli_rev == LPFC_SLI_REV4) - lpfc_issue_reg_vfi(vport); - else { + if (phba->sli_rev == LPFC_SLI_REV4) { + rc = lpfc_issue_reg_vfi(vport); + } else { link_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!link_mbox) - goto out; + goto rsp_rjt; lpfc_config_link(phba, link_mbox); link_mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl; link_mbox->vport = vport; @@ -544,11 +543,13 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, rc = lpfc_sli_issue_mbox(phba, link_mbox, MBX_NOWAIT); if (rc == MBX_NOT_FINISHED) { mempool_free(link_mbox, phba->mbox_mem_pool); - goto out; + goto rsp_rjt; } } lpfc_can_disctmo(vport); + if (rc) + goto rsp_rjt; } clear_bit(NLP_SUPPRESS_RSP, &ndlp->nlp_flag); @@ -562,11 +563,11 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, login_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!login_mbox) - goto out; + goto rsp_rjt; save_iocb = kzalloc_obj(*save_iocb); if (!save_iocb) - goto out; + goto free_login_mbox; /* Save info from cmd IOCB to be used in rsp after all mbox completes */ memcpy((uint8_t *)save_iocb, (uint8_t *)cmdiocb, @@ -586,7 +587,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, rc = lpfc_reg_rpi(phba, vport->vpi, remote_did, (uint8_t *)sp, login_mbox, ndlp->nlp_rpi); if (rc) - goto out; + goto free_save_iocb; login_mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login; login_mbox->vport = vport; @@ -659,7 +660,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, login_mbox->mbox_cmpl = lpfc_defer_plogi_acc; login_mbox->ctx_ndlp = lpfc_nlp_get(ndlp); if (!login_mbox->ctx_ndlp) - goto out; + goto free_save_iocb; login_mbox->ctx_u.save_iocb = save_iocb; /* For PLOGI ACC */ @@ -670,16 +671,17 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, rc = lpfc_sli_issue_mbox(phba, login_mbox, MBX_NOWAIT); if (rc == MBX_NOT_FINISHED) { lpfc_nlp_put(ndlp); - goto out; + goto free_save_iocb; } lpfc_nlp_set_state(vport, ndlp, NLP_STE_REG_LOGIN_ISSUE); return 1; -out: - kfree(save_iocb); - if (login_mbox) - mempool_free(login_mbox, phba->mbox_mem_pool); +free_save_iocb: + kfree(save_iocb); +free_login_mbox: + mempool_free(login_mbox, phba->mbox_mem_pool); +rsp_rjt: stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL); diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index a6b3b16f870d..71714ea390d9 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -1296,8 +1296,6 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, /* Word 10 */ bf_set(wqe_xchg, &wqe->fcp_iwrite.wqe_com, LPFC_NVME_XCHG); - /* Words 13 14 15 are for PBDE support */ - /* add the VMID tags as per switch response */ if (unlikely(lpfc_ncmd->cur_iocbq.cmd_flag & LPFC_IO_VMID)) { if (phba->pport->vmid_priority_tagging) { @@ -1335,16 +1333,13 @@ lpfc_nvme_prep_io_dma(struct lpfc_vport *vport, { struct lpfc_hba *phba = vport->phba; struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd; - union lpfc_wqe128 *wqe = &lpfc_ncmd->cur_iocbq.wqe; struct sli4_sge *sgl = lpfc_ncmd->dma_sgl; struct sli4_hybrid_sgl *sgl_xtra = NULL; struct scatterlist *data_sg; - struct sli4_sge *first_data_sgl; - struct ulp_bde64 *bde; dma_addr_t physaddr = 0; uint32_t dma_len = 0; uint32_t dma_offset = 0; - int nseg, i, j; + int nseg, i, j, k; bool lsp_just_set = false; /* Fix up the command and response DMA stuff. */ @@ -1361,7 +1356,6 @@ lpfc_nvme_prep_io_dma(struct lpfc_vport *vport, */ sgl += 2; - first_data_sgl = sgl; lpfc_ncmd->seg_cnt = nCmd->sg_cnt; if (lpfc_ncmd->seg_cnt > lpfc_nvme_template.max_sgl_segments) { lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, @@ -1385,6 +1379,9 @@ lpfc_nvme_prep_io_dma(struct lpfc_vport *vport, /* for tracking the segment boundaries */ j = 2; + k = 5; + if (unlikely(!phba->cfg_xpsgl)) + k = 1; for (i = 0; i < nseg; i++) { if (data_sg == NULL) { lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, @@ -1403,9 +1400,8 @@ lpfc_nvme_prep_io_dma(struct lpfc_vport *vport, bf_set(lpfc_sli4_sge_last, sgl, 0); /* expand the segment */ - if (!lsp_just_set && - !((j + 1) % phba->border_sge_num) && - ((nseg - 1) != i)) { + if (!lsp_just_set && (nseg != (i + k)) && + !((j + k) % phba->border_sge_num)) { /* set LSP type */ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_LSP); @@ -1428,8 +1424,8 @@ lpfc_nvme_prep_io_dma(struct lpfc_vport *vport, } } - if (!(bf_get(lpfc_sli4_sge_type, sgl) & - LPFC_SGE_TYPE_LSP)) { + if (bf_get(lpfc_sli4_sge_type, sgl) != + LPFC_SGE_TYPE_LSP) { if ((nseg - 1) == i) bf_set(lpfc_sli4_sge_last, sgl, 1); @@ -1450,40 +1446,26 @@ lpfc_nvme_prep_io_dma(struct lpfc_vport *vport, sgl++; lsp_just_set = false; + j++; } else { sgl->word2 = cpu_to_le32(sgl->word2); - - sgl->sge_len = cpu_to_le32( - phba->cfg_sg_dma_buf_size); + /* will remaining SGEs fill the next SGL? */ + if ((nseg - i) < phba->border_sge_num) + sgl->sge_len = + cpu_to_le32((nseg - i) * + sizeof(*sgl)); + else + sgl->sge_len = + cpu_to_le32(phba->cfg_sg_dma_buf_size); sgl = (struct sli4_sge *)sgl_xtra->dma_sgl; i = i - 1; lsp_just_set = true; + j += k; + k = 1; } - - j++; } - - /* PBDE support for first data SGE only */ - if (nseg == 1 && phba->cfg_enable_pbde) { - /* Words 13-15 */ - bde = (struct ulp_bde64 *) - &wqe->words[13]; - bde->addrLow = first_data_sgl->addr_lo; - bde->addrHigh = first_data_sgl->addr_hi; - bde->tus.f.bdeSize = - le32_to_cpu(first_data_sgl->sge_len); - bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64; - bde->tus.w = cpu_to_le32(bde->tus.w); - - /* Word 11 - set PBDE bit */ - bf_set(wqe_pbde, &wqe->generic.wqe_com, 1); - } else { - memset(&wqe->words[13], 0, (sizeof(uint32_t) * 3)); - /* Word 11 - PBDE bit disabled by default template */ - } - } else { lpfc_ncmd->seg_cnt = 0; @@ -2846,6 +2828,54 @@ lpfc_nvme_cancel_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, #endif } +/** + * lpfc_nvme_flush_abts_list - Clean up nvme commands from the abts list + * @phba: Pointer to HBA context object. + * + **/ +void +lpfc_nvme_flush_abts_list(struct lpfc_hba *phba) +{ +#if (IS_ENABLED(CONFIG_NVME_FC)) + struct lpfc_io_buf *psb, *psb_next; + struct lpfc_sli4_hdw_queue *qp; + LIST_HEAD(aborts); + int i; + + /* abts_xxxx_buf_list_lock required because worker thread uses this + * list. + */ + spin_lock_irq(&phba->hbalock); + for (i = 0; i < phba->cfg_hdw_queue; i++) { + qp = &phba->sli4_hba.hdwq[i]; + + spin_lock(&qp->abts_io_buf_list_lock); + list_for_each_entry_safe(psb, psb_next, + &qp->lpfc_abts_io_buf_list, list) { + if (!(psb->cur_iocbq.cmd_flag & LPFC_IO_NVME)) + continue; + list_move(&psb->list, &aborts); + qp->abts_nvme_io_bufs--; + } + spin_unlock(&qp->abts_io_buf_list_lock); + } + spin_unlock_irq(&phba->hbalock); + + list_for_each_entry_safe(psb, psb_next, &aborts, list) { + list_del_init(&psb->list); + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, + "6195 %s: lpfc_ncmd x%px flags x%x " + "cmd_flag x%x xri x%x\n", __func__, + psb, psb->flags, + psb->cur_iocbq.cmd_flag, + psb->cur_iocbq.sli4_xritag); + psb->flags &= ~LPFC_SBUF_XBUSY; + psb->status = IOSTAT_SUCCESS; + lpfc_sli4_nvme_pci_offline_aborted(phba, psb); + } +#endif +} + /** * lpfc_nvmels_flush_cmd - Clean up outstanding nvmels commands for a port * @phba: Pointer to HBA context object. diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index debd9317103a..72f3f6f9444d 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2024 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -118,12 +118,9 @@ lpfc_nvmet_cmd_template(void) bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 0); bf_set(wqe_irsp, &wqe->fcp_tsend.wqe_com, 0); bf_set(wqe_irsplen, &wqe->fcp_tsend.wqe_com, 0); - bf_set(wqe_pbde, &wqe->fcp_tsend.wqe_com, 0); /* Word 12 - fcp_data_len is variable */ - /* Word 13, 14, 15 - PBDE is zero */ - /* TRECEIVE template */ wqe = &lpfc_treceive_cmd_template; memset(wqe, 0, sizeof(union lpfc_wqe128)); @@ -158,18 +155,15 @@ lpfc_nvmet_cmd_template(void) bf_set(wqe_lenloc, &wqe->fcp_treceive.wqe_com, LPFC_WQE_LENLOC_WORD12); bf_set(wqe_xc, &wqe->fcp_tsend.wqe_com, 1); - /* Word 11 - pbde is variable */ + /* Word 11 */ bf_set(wqe_cmd_type, &wqe->fcp_treceive.wqe_com, FCP_COMMAND_TRECEIVE); bf_set(wqe_cqid, &wqe->fcp_treceive.wqe_com, LPFC_WQE_CQ_ID_DEFAULT); bf_set(wqe_sup, &wqe->fcp_treceive.wqe_com, 0); bf_set(wqe_irsp, &wqe->fcp_treceive.wqe_com, 0); bf_set(wqe_irsplen, &wqe->fcp_treceive.wqe_com, 0); - bf_set(wqe_pbde, &wqe->fcp_treceive.wqe_com, 1); /* Word 12 - fcp_data_len is variable */ - /* Word 13, 14, 15 - PBDE is variable */ - /* TRSP template */ wqe = &lpfc_trsp_cmd_template; memset(wqe, 0, sizeof(union lpfc_wqe128)); @@ -207,7 +201,6 @@ lpfc_nvmet_cmd_template(void) bf_set(wqe_sup, &wqe->fcp_trsp.wqe_com, 0); bf_set(wqe_irsp, &wqe->fcp_trsp.wqe_com, 0); bf_set(wqe_irsplen, &wqe->fcp_trsp.wqe_com, 0); - bf_set(wqe_pbde, &wqe->fcp_trsp.wqe_com, 0); /* Word 12, 13, 14, 15 - is zero */ } @@ -2722,7 +2715,6 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba, struct ulp_bde64 *bde; dma_addr_t physaddr; int i, cnt, nsegs; - bool use_pbde = false; int xc = 1; if (!lpfc_is_link_up(phba)) { @@ -2907,15 +2899,6 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba, if (!xc) bf_set(wqe_xc, &wqe->fcp_treceive.wqe_com, 0); - /* Word 11 - check for pbde */ - if (nsegs == 1 && phba->cfg_enable_pbde) { - use_pbde = true; - /* Word 11 - PBDE bit already preset by template */ - } else { - /* Overwrite default template setting */ - bf_set(wqe_pbde, &wqe->fcp_treceive.wqe_com, 0); - } - /* Word 12 */ wqe->fcp_tsend.fcp_data_len = rsp->transfer_length; @@ -3023,19 +3006,9 @@ lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba, } bde = (struct ulp_bde64 *)&wqe->words[13]; - if (use_pbde) { - /* decrement sgl ptr backwards once to first data sge */ - sgl--; - /* Words 13-15 (PBDE) */ - bde->addrLow = sgl->addr_lo; - bde->addrHigh = sgl->addr_hi; - bde->tus.f.bdeSize = le32_to_cpu(sgl->sge_len); - bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64; - bde->tus.w = cpu_to_le32(bde->tus.w); - } else { - memset(bde, 0, sizeof(struct ulp_bde64)); - } + memset(bde, 0, sizeof(struct ulp_bde64)); + ctxp->state = LPFC_NVME_STE_DATA; ctxp->entry_cnt++; return nvmewqe; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 69bf1ac6f846..1dce33b79beb 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -1938,7 +1938,7 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc, uint32_t dma_len; uint32_t dma_offset = 0; struct sli4_hybrid_sgl *sgl_xtra = NULL; - int j; + int j, k; bool lsp_just_set = false; status = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop); @@ -2001,13 +2001,16 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc, /* assumption: caller has already run dma_map_sg on command data */ sgde = scsi_sglist(sc); j = 3; + k = 5; + if (unlikely(!phba->cfg_xpsgl)) + k = 1; for (i = 0; i < datasegcnt; i++) { /* clear it */ sgl->word2 = 0; - /* do we need to expand the segment */ - if (!lsp_just_set && !((j + 1) % phba->border_sge_num) && - ((datasegcnt - 1) != i)) { + /* do we need to expand the segment? */ + if (!lsp_just_set && (datasegcnt != (i + k)) && + !((j + k) % phba->border_sge_num)) { /* set LSP type */ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_LSP); @@ -2026,7 +2029,7 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc, bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DATA); } - if (!(bf_get(lpfc_sli4_sge_type, sgl) & LPFC_SGE_TYPE_LSP)) { + if (bf_get(lpfc_sli4_sge_type, sgl) != LPFC_SGE_TYPE_LSP) { if ((datasegcnt - 1) == i) bf_set(lpfc_sli4_sge_last, sgl, 1); physaddr = sg_dma_address(sgde); @@ -2043,20 +2046,23 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc, sgl++; num_sge++; + j++; lsp_just_set = false; - } else { sgl->word2 = cpu_to_le32(sgl->word2); - sgl->sge_len = cpu_to_le32(phba->cfg_sg_dma_buf_size); - + /* will remaining SGEs fill the next SGL? */ + if ((datasegcnt - i) < phba->border_sge_num) + sgl->sge_len = cpu_to_le32((datasegcnt - i) * + sizeof(*sgl)); + else + sgl->sge_len = + cpu_to_le32(phba->cfg_sg_dma_buf_size); sgl = (struct sli4_sge *)sgl_xtra->dma_sgl; i = i - 1; - + j += k; lsp_just_set = true; + k = 1; } - - j++; - } out: @@ -2109,6 +2115,7 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, struct scatterlist *sgde = NULL; /* s/g data entry */ struct scatterlist *sgpe = NULL; /* s/g prot entry */ struct sli4_sge_diseed *diseed = NULL; + struct sli4_sge_le *lsp_sgl = NULL; dma_addr_t dataphysaddr, protphysaddr; unsigned short curr_prot = 0; unsigned int split_offset; @@ -2125,8 +2132,8 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, uint32_t rc; #endif uint32_t checking = 1; - uint32_t dma_offset = 0, num_sge = 0; - int j = 2; + uint32_t dma_offset = 0, num_sge = 0, lsp_len; + int j = 2, k = 4; struct sli4_hybrid_sgl *sgl_xtra = NULL; sgpe = scsi_prot_sglist(sc); @@ -2157,6 +2164,8 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, } #endif + if (unlikely(!phba->cfg_xpsgl)) + k = 0; split_offset = 0; do { /* Check to see if we ran out of space */ @@ -2164,10 +2173,10 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, !(phba->cfg_xpsgl)) return num_sge + 3; - /* DISEED and DIF have to be together */ - if (!((j + 1) % phba->border_sge_num) || - !((j + 2) % phba->border_sge_num) || - !((j + 3) % phba->border_sge_num)) { + /* DISEED and DIF have to be together */ + if (!((j + k + 1) % phba->border_sge_num) || + !((j + k + 2) % phba->border_sge_num) || + !((j + k + 3) % phba->border_sge_num)) { sgl->word2 = 0; /* set LSP type */ @@ -2186,9 +2195,18 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, sgl->word2 = cpu_to_le32(sgl->word2); sgl->sge_len = cpu_to_le32(phba->cfg_sg_dma_buf_size); + if (lsp_sgl) { + j++; + if (j % phba->border_sge_num) { + lsp_len = j * (sizeof(*sgl)); + lsp_sgl->sge_len = cpu_to_le32(lsp_len); + } + } + lsp_sgl = (struct sli4_sge_le *)sgl; sgl = (struct sli4_sge *)sgl_xtra->dma_sgl; j = 0; + k = 0; } /* setup DISEED with what we have */ @@ -2291,7 +2309,7 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, return 0; } - if (!((j + 1) % phba->border_sge_num)) { + if (!((j + k + 1) % phba->border_sge_num)) { sgl->word2 = 0; /* set LSP type */ @@ -2313,8 +2331,11 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, sgl->word2 = cpu_to_le32(sgl->word2); sgl->sge_len = cpu_to_le32( phba->cfg_sg_dma_buf_size); + lsp_sgl = (struct sli4_sge_le *)sgl; sgl = (struct sli4_sge *)sgl_xtra->dma_sgl; + j = 0; + k = 0; } else { dataphysaddr = sg_dma_address(sgde) + split_offset; @@ -2362,11 +2383,9 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, /* Move to the next s/g segment if possible */ sgde = sg_next(sgde); - sgl++; + j++; } - - j++; } if (protgroup_offset) { @@ -2381,6 +2400,14 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, sgl--; bf_set(lpfc_sli4_sge_last, sgl, 1); alldone = 1; + + /* Reset length in previous LSP where necessary */ + if (lsp_sgl) { + if (j % phba->border_sge_num) { + lsp_len = j * (sizeof(*sgl)); + lsp_sgl->sge_len = cpu_to_le32(lsp_len); + } + } } else if (curr_prot < protcnt) { /* advance to next prot buffer */ sgpe = sg_next(sgpe); @@ -2392,7 +2419,6 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, "9085 BLKGRD: bug in %s\n", __func__); } - } while (!alldone); out: @@ -3050,15 +3076,13 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) struct scatterlist *sgel = NULL; struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd; struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->dma_sgl; - struct sli4_sge *first_data_sgl; struct lpfc_iocbq *pwqeq = &lpfc_cmd->cur_iocbq; struct lpfc_vport *vport = phba->pport; union lpfc_wqe128 *wqe = &pwqeq->wqe; dma_addr_t physaddr; uint32_t dma_len; uint32_t dma_offset = 0; - int nseg, i, j; - struct ulp_bde64 *bde; + int nseg, i, j, k; bool lsp_just_set = false; struct sli4_hybrid_sgl *sgl_xtra = NULL; @@ -3085,7 +3109,6 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) bf_set(lpfc_sli4_sge_last, sgl, 0); sgl->word2 = cpu_to_le32(sgl->word2); sgl += 1; - first_data_sgl = sgl; lpfc_cmd->seg_cnt = nseg; if (!phba->cfg_xpsgl && lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) { @@ -3114,6 +3137,9 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) /* for tracking segment boundaries */ sgel = scsi_sglist(scsi_cmnd); j = 2; + k = 5; + if (unlikely(!phba->cfg_xpsgl)) + k = 1; for (i = 0; i < nseg; i++) { sgl->word2 = 0; if (nseg == 1) { @@ -3124,9 +3150,8 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) bf_set(lpfc_sli4_sge_last, sgl, 0); /* do we need to expand the segment */ - if (!lsp_just_set && - !((j + 1) % phba->border_sge_num) && - ((nseg - 1) != i)) { + if (!lsp_just_set && (nseg != (i + k)) && + !((j + k) % phba->border_sge_num)) { /* set LSP type */ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_LSP); @@ -3150,8 +3175,8 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) } } - if (!(bf_get(lpfc_sli4_sge_type, sgl) & - LPFC_SGE_TYPE_LSP)) { + if (bf_get(lpfc_sli4_sge_type, sgl) != + LPFC_SGE_TYPE_LSP) { if ((nseg - 1) == i) bf_set(lpfc_sli4_sge_last, sgl, 1); @@ -3171,43 +3196,24 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) sgl++; lsp_just_set = false; - + j++; } else { sgl->word2 = cpu_to_le32(sgl->word2); - sgl->sge_len = cpu_to_le32( - phba->cfg_sg_dma_buf_size); - + /* will remaining SGEs fill the next SGL? */ + if ((nseg - i) < phba->border_sge_num) + sgl->sge_len = + cpu_to_le32((nseg - i) * + sizeof(*sgl)); + else + sgl->sge_len = + cpu_to_le32(phba->cfg_sg_dma_buf_size); sgl = (struct sli4_sge *)sgl_xtra->dma_sgl; i = i - 1; lsp_just_set = true; + j += k; + k = 1; } - - j++; - } - - /* PBDE support for first data SGE only. - * For FCoE, we key off Performance Hints. - * For FC, we key off lpfc_enable_pbde. - */ - if (nseg == 1 && - ((phba->sli3_options & LPFC_SLI4_PERFH_ENABLED) || - phba->cfg_enable_pbde)) { - /* Words 13-15 */ - bde = (struct ulp_bde64 *) - &wqe->words[13]; - bde->addrLow = first_data_sgl->addr_lo; - bde->addrHigh = first_data_sgl->addr_hi; - bde->tus.f.bdeSize = - le32_to_cpu(first_data_sgl->sge_len); - bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64; - bde->tus.w = cpu_to_le32(bde->tus.w); - - /* Word 11 - set PBDE bit */ - bf_set(wqe_pbde, &wqe->generic.wqe_com, 1); - } else { - memset(&wqe->words[13], 0, (sizeof(uint32_t) * 3)); - /* Word 11 - PBDE bit disabled by default template */ } } else { sgl += 1; @@ -3215,13 +3221,6 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) sgl->word2 = le32_to_cpu(sgl->word2); bf_set(lpfc_sli4_sge_last, sgl, 1); sgl->word2 = cpu_to_le32(sgl->word2); - - if ((phba->sli3_options & LPFC_SLI4_PERFH_ENABLED) || - phba->cfg_enable_pbde) { - bde = (struct ulp_bde64 *) - &wqe->words[13]; - memset(bde, 0, (sizeof(uint32_t) * 3)); - } } /* @@ -4665,7 +4664,7 @@ static int lpfc_scsi_prep_cmnd_buf_s3(struct lpfc_vport *vport, else piocbq->iocb.ulpFCP2Rcvy = 0; - piocbq->iocb.ulpClass = (pnode->nlp_fcp_info & 0x0f); + piocbq->iocb.ulpClass = (pnode->nlp_fcp_info & NLP_FCP_CLASS_MASK); piocbq->io_buf = lpfc_cmd; if (!piocbq->cmd_cmpl) piocbq->cmd_cmpl = lpfc_scsi_cmd_iocb_cmpl; @@ -4777,7 +4776,7 @@ static int lpfc_scsi_prep_cmnd_buf_s4(struct lpfc_vport *vport, bf_set(wqe_erp, &wqe->generic.wqe_com, 1); bf_set(wqe_class, &wqe->generic.wqe_com, - (pnode->nlp_fcp_info & 0x0f)); + (pnode->nlp_fcp_info & NLP_FCP_CLASS_MASK)); /* Word 8 */ wqe->generic.wqe_com.abort_tag = pwqeq->iotag; @@ -4877,7 +4876,7 @@ lpfc_scsi_prep_task_mgmt_cmd_s3(struct lpfc_vport *vport, piocb->ulpCommand = CMD_FCP_ICMND64_CR; piocb->ulpContext = ndlp->nlp_rpi; piocb->ulpFCP2Rcvy = (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) ? 1 : 0; - piocb->ulpClass = (ndlp->nlp_fcp_info & 0x0f); + piocb->ulpClass = (ndlp->nlp_fcp_info & NLP_FCP_CLASS_MASK); piocb->ulpPU = 0; piocb->un.fcpi.fcpi_parm = 0; @@ -4945,7 +4944,7 @@ lpfc_scsi_prep_task_mgmt_cmd_s4(struct lpfc_vport *vport, bf_set(wqe_erp, &wqe->fcp_icmd.wqe_com, ((ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) ? 1 : 0)); bf_set(wqe_class, &wqe->fcp_icmd.wqe_com, - (ndlp->nlp_fcp_info & 0x0f)); + (ndlp->nlp_fcp_info & NLP_FCP_CLASS_MASK)); /* ulpTimeout is only one byte */ if (lpfc_cmd->timeout > 0xff) { diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 303523f754b8..d38fb374b379 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -136,15 +136,12 @@ void lpfc_wqe_cmd_template(void) bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 0); bf_set(wqe_wqes, &wqe->fcp_iread.wqe_com, 1); - /* Word 11 - pbde is variable */ + /* Word 11 */ bf_set(wqe_cmd_type, &wqe->fcp_iread.wqe_com, COMMAND_DATA_IN); bf_set(wqe_cqid, &wqe->fcp_iread.wqe_com, LPFC_WQE_CQ_ID_DEFAULT); - bf_set(wqe_pbde, &wqe->fcp_iread.wqe_com, 0); /* Word 12 - is zero */ - /* Word 13, 14, 15 - PBDE is variable */ - /* IWRITE template */ wqe = &lpfc_iwrite_cmd_template; memset(wqe, 0, sizeof(union lpfc_wqe128)); @@ -176,15 +173,12 @@ void lpfc_wqe_cmd_template(void) bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 0); bf_set(wqe_wqes, &wqe->fcp_iwrite.wqe_com, 1); - /* Word 11 - pbde is variable */ + /* Word 11 */ bf_set(wqe_cmd_type, &wqe->fcp_iwrite.wqe_com, COMMAND_DATA_OUT); bf_set(wqe_cqid, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_CQ_ID_DEFAULT); - bf_set(wqe_pbde, &wqe->fcp_iwrite.wqe_com, 0); /* Word 12 - is zero */ - /* Word 13, 14, 15 - PBDE is variable */ - /* ICMND template */ wqe = &lpfc_icmnd_cmd_template; memset(wqe, 0, sizeof(union lpfc_wqe128)); @@ -217,7 +211,6 @@ void lpfc_wqe_cmd_template(void) /* Word 11 */ bf_set(wqe_cmd_type, &wqe->fcp_icmd.wqe_com, COMMAND_DATA_IN); bf_set(wqe_cqid, &wqe->fcp_icmd.wqe_com, LPFC_WQE_CQ_ID_DEFAULT); - bf_set(wqe_pbde, &wqe->fcp_icmd.wqe_com, 0); /* Word 12, 13, 14, 15 - is zero */ } @@ -4572,59 +4565,41 @@ void lpfc_sli_abort_iocb_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring) { LIST_HEAD(tx_completions); - LIST_HEAD(txcmplq_completions); + spinlock_t *plock; /* for transmit queue access */ struct lpfc_iocbq *iocb, *next_iocb; int offline; - if (pring->ringno == LPFC_ELS_RING) { + if (phba->sli_rev >= LPFC_SLI_REV4) + plock = &pring->ring_lock; + else + plock = &phba->hbalock; + + if (pring->ringno == LPFC_ELS_RING) lpfc_fabric_abort_hba(phba); - } + offline = pci_channel_offline(phba->pcidev); - /* Error everything on txq and txcmplq - * First do the txq. - */ - if (phba->sli_rev >= LPFC_SLI_REV4) { - spin_lock_irq(&pring->ring_lock); - list_splice_init(&pring->txq, &tx_completions); - pring->txq_cnt = 0; - - if (offline) { - list_splice_init(&pring->txcmplq, - &txcmplq_completions); - } else { - /* Next issue ABTS for everything on the txcmplq */ - list_for_each_entry_safe(iocb, next_iocb, - &pring->txcmplq, list) - lpfc_sli_issue_abort_iotag(phba, pring, - iocb, NULL); - } - spin_unlock_irq(&pring->ring_lock); - } else { - spin_lock_irq(&phba->hbalock); - list_splice_init(&pring->txq, &tx_completions); - pring->txq_cnt = 0; - - if (offline) { - list_splice_init(&pring->txcmplq, &txcmplq_completions); - } else { - /* Next issue ABTS for everything on the txcmplq */ - list_for_each_entry_safe(iocb, next_iocb, - &pring->txcmplq, list) - lpfc_sli_issue_abort_iotag(phba, pring, - iocb, NULL); - } - spin_unlock_irq(&phba->hbalock); - } + /* Cancel everything on txq */ + spin_lock_irq(plock); + list_splice_init(&pring->txq, &tx_completions); + pring->txq_cnt = 0; if (offline) { - /* Cancel all the IOCBs from the completions list */ - lpfc_sli_cancel_iocbs(phba, &txcmplq_completions, - IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED); + /* Cancel everything on txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) + iocb->cmd_flag &= ~LPFC_IO_ON_TXCMPLQ; + list_splice_init(&pring->txcmplq, &tx_completions); + pring->txcmplq_cnt = 0; } else { - /* Make sure HBA is alive */ - lpfc_issue_hb_tmo(phba); + /* Issue ABTS for everything on the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) + lpfc_sli_issue_abort_iotag(phba, pring, iocb, NULL); } + spin_unlock_irq(plock); + + if (!offline) + lpfc_issue_hb_tmo(phba); + /* Cancel all the IOCBs from the completions list */ lpfc_sli_cancel_iocbs(phba, &tx_completions, IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED); @@ -8750,14 +8725,6 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) ftr_rsp++; } - /* Performance Hints are ONLY for FCoE */ - if (test_bit(HBA_FCOE_MODE, &phba->hba_flag)) { - if (bf_get(lpfc_mbx_rq_ftr_rsp_perfh, &mqe->un.req_ftrs)) - phba->sli3_options |= LPFC_SLI4_PERFH_ENABLED; - else - phba->sli3_options &= ~LPFC_SLI4_PERFH_ENABLED; - } - /* * If the port cannot support the host's requested features * then turn off the global config parameters to disable the @@ -14355,13 +14322,15 @@ lpfc_sli4_sp_handle_mbox_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe) /* Get the reference to the active mbox command */ spin_lock_irqsave(&phba->hbalock, iflags); pmb = phba->sli.mbox_active; + spin_unlock_irqrestore(&phba->hbalock, iflags); if (unlikely(!pmb)) { lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, - "1832 No pending MBOX command to handle\n"); - spin_unlock_irqrestore(&phba->hbalock, iflags); + "1832 No pending MBOX command to handle, " + "mcqe: x%08x x%08x x%08x x%08x\n", + mcqe->word0, mcqe->mcqe_tag0, + mcqe->mcqe_tag1, mcqe->trailer); goto out_no_mqe_complete; } - spin_unlock_irqrestore(&phba->hbalock, iflags); mqe = &pmb->u.mqe; pmbox = (MAILBOX_t *)&pmb->u.mqe; mbox = phba->mbox; @@ -14736,11 +14705,22 @@ lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_rcqe *rcqe) atomic_read(&tgtp->rcv_fcp_cmd_out), atomic_read(&tgtp->xmt_fcp_release)); } + hrq->RQ_discard_frm++; fallthrough; - case FC_STATUS_INSUFF_BUF_NEED_BUF: + /* Unexpected event - bump the counter for support. */ hrq->RQ_no_posted_buf++; - /* Post more buffers if possible */ + + lpfc_log_msg(phba, KERN_WARNING, + LOG_ELS | LOG_DISCOVERY | LOG_SLI, + "6423 RQE completion Status x%x, needed x%x " + "discarded x%x\n", status, + hrq->RQ_no_posted_buf - hrq->RQ_discard_frm, + hrq->RQ_discard_frm); + + /* For SLI3, post more buffers if possible. No action for SLI4. + * SLI4 is reposting immediately after processing the RQE. + */ set_bit(HBA_POST_RECEIVE_BUFFER, &phba->hba_flag); workposted = true; break; diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index b6d90604bb61..2744786d9c94 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2009-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -246,6 +246,8 @@ struct lpfc_queue { uint32_t q_cnt_2; uint32_t q_cnt_3; uint64_t q_cnt_4; + uint32_t q_cnt_5; + /* defines for EQ stats */ #define EQ_max_eqe q_cnt_1 #define EQ_no_entry q_cnt_2 @@ -268,6 +270,7 @@ struct lpfc_queue { #define RQ_no_buf_found q_cnt_2 #define RQ_buf_posted q_cnt_3 #define RQ_rcv_buf q_cnt_4 +#define RQ_discard_frm q_cnt_5 struct work_struct irqwork; struct work_struct spwork; @@ -838,6 +841,7 @@ struct lpfc_sli4_hba { uint32_t ue_to_sr; uint32_t ue_to_rp; struct lpfc_register sli_intf; + struct lpfc_register asic_id; struct lpfc_pc_sli4_params pc_sli4_params; struct lpfc_bbscn_params bbscn_params; struct lpfc_hba_eq_hdl *hba_eq_hdl; /* HBA per-WQ handle */ diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index c4ca8bf5843a..d6e6e436fbfc 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2026 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -20,7 +20,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "14.4.0.13" +#define LPFC_DRIVER_VERSION "15.0.0.0" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ @@ -32,6 +32,6 @@ #define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \ LPFC_DRIVER_VERSION -#define LPFC_COPYRIGHT "Copyright (C) 2017-2025 Broadcom. All Rights " \ +#define LPFC_COPYRIGHT "Copyright (C) 2017-2026 Broadcom. All Rights " \ "Reserved. The term \"Broadcom\" refers to Broadcom Inc. " \ "and/or its subsidiaries." diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index ac71ea4898b2..ecd365d78ae3 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -6365,11 +6365,13 @@ static int megasas_init_fw(struct megasas_instance *instance) megasas_setup_jbod_map(instance); - if (megasas_get_device_list(instance) != SUCCESS) { - dev_err(&instance->pdev->dev, - "%s: megasas_get_device_list failed\n", - __func__); - goto fail_get_ld_pd_list; + scoped_guard(mutex, &instance->reset_mutex) { + if (megasas_get_device_list(instance) != SUCCESS) { + dev_err(&instance->pdev->dev, + "%s: megasas_get_device_list failed\n", + __func__); + goto fail_get_ld_pd_list; + } } /* stream detection initialization */ @@ -6468,7 +6470,8 @@ static int megasas_init_fw(struct megasas_instance *instance) } if (instance->snapdump_wait_time) { - megasas_get_snapdump_properties(instance); + scoped_guard(mutex, &instance->reset_mutex) + megasas_get_snapdump_properties(instance); dev_info(&instance->pdev->dev, "Snap dump wait time\t: %d\n", instance->snapdump_wait_time); } diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h index 6e962092577d..c25525fe0671 100644 --- a/drivers/scsi/mpi3mr/mpi3mr.h +++ b/drivers/scsi/mpi3mr/mpi3mr.h @@ -159,6 +159,7 @@ extern atomic64_t event_counter; /* Controller Reset related definitions */ #define MPI3MR_HOSTDIAG_UNLOCK_RETRY_COUNT 5 #define MPI3MR_MAX_RESET_RETRY_COUNT 3 +#define MPI3MR_MAX_SHUTDOWN_RETRY_COUNT 2 /* ResponseCode definitions */ #define MPI3MR_RI_MASK_RESPCODE (0x000000FF) @@ -323,6 +324,7 @@ enum mpi3mr_reset_reason { MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT = 29, MPI3MR_RESET_FROM_SAS_TRANSPORT_TIMEOUT = 30, MPI3MR_RESET_FROM_TRIGGER = 31, + MPI3MR_RESET_FROM_INVALID_COMPLETION = 32, }; #define MPI3MR_RESET_REASON_OSTYPE_LINUX 1 @@ -428,6 +430,14 @@ struct segments { * @q_segments: Segment descriptor pointer * @q_segment_list: Segment list base virtual address * @q_segment_list_dma: Segment list base DMA address + * @last_full_host_tag: Hosttag of last IO returned to SML + * due to queue full + * @qfull_io_count: Number of IOs returned back to SML + * due to queue full + * @qfull_instances: Total queue full occurrences.One occurrence + * starts with queue full detection and ends + * with queue full breaks. + * */ struct op_req_qinfo { u16 ci; @@ -441,6 +451,10 @@ struct op_req_qinfo { struct segments *q_segments; void *q_segment_list; dma_addr_t q_segment_list_dma; + u16 last_full_host_tag; + u64 qfull_io_count; + u32 qfull_instances; + }; /** @@ -1183,6 +1197,7 @@ struct scmd_priv { * @num_tb_segs: Number of Segments in Trace buffer * @trace_buf_pool: DMA pool for Segmented trace buffer segments * @trace_buf: Trace buffer segments memory descriptor + * @invalid_io_comp: Invalid IO completion */ struct mpi3mr_ioc { struct list_head list; @@ -1394,6 +1409,7 @@ struct mpi3mr_ioc { u32 num_tb_segs; struct dma_pool *trace_buf_pool; struct segments *trace_buf; + u8 invalid_io_comp; }; diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c index c744210cc901..31b19ed1528e 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c @@ -996,6 +996,7 @@ static const struct { { MPI3MR_RESET_FROM_FIRMWARE, "firmware asynchronous reset" }, { MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT, "configuration request timeout"}, { MPI3MR_RESET_FROM_SAS_TRANSPORT_TIMEOUT, "timeout of a SAS transport layer request" }, + { MPI3MR_RESET_FROM_INVALID_COMPLETION, "invalid cmd completion" }, }; /** @@ -2371,6 +2372,9 @@ static int mpi3mr_create_op_req_q(struct mpi3mr_ioc *mrioc, u16 idx, op_req_q->ci = 0; op_req_q->pi = 0; op_req_q->reply_qid = reply_qid; + op_req_q->last_full_host_tag = MPI3MR_HOSTTAG_INVALID; + op_req_q->qfull_io_count = 0; + op_req_q->qfull_instances = 0; spin_lock_init(&op_req_q->q_lock); if (!op_req_q->q_segments) { @@ -2557,6 +2561,8 @@ int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc, u16 req_sz = mrioc->facts.op_req_sz; struct segments *segments = op_req_q->q_segments; struct op_reply_qinfo *op_reply_q = NULL; + struct mpi3_scsi_io_request *scsiio_req = + (struct mpi3_scsi_io_request *)req; reply_qidx = op_req_q->reply_qid - 1; op_reply_q = mrioc->op_reply_qinfo + reply_qidx; @@ -2574,11 +2580,21 @@ int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc, mpi3mr_process_op_reply_q(mrioc, mrioc->intr_info[midx].op_reply_q); if (mpi3mr_check_req_qfull(op_req_q)) { + + if (op_req_q->last_full_host_tag == + MPI3MR_HOSTTAG_INVALID) + op_req_q->qfull_instances++; + + op_req_q->last_full_host_tag = scsiio_req->host_tag; + op_req_q->qfull_io_count++; retval = -EAGAIN; goto out; } } + if (op_req_q->last_full_host_tag != MPI3MR_HOSTTAG_INVALID) + op_req_q->last_full_host_tag = MPI3MR_HOSTTAG_INVALID; + if (mrioc->reset_in_progress) { ioc_err(mrioc, "OpReqQ submit reset in progress\n"); retval = -EAGAIN; @@ -2699,7 +2715,7 @@ void mpi3mr_check_rh_fault_ioc(struct mpi3mr_ioc *mrioc, u32 reason_code) * mpi3mr_sync_timestamp - Issue time stamp sync request * @mrioc: Adapter reference * - * Issue IO unit control MPI request to synchornize firmware + * Issue IO unit control MPI request to synchronize firmware * timestamp with host time. * * Return: 0 on success, non-zero on failure. @@ -2889,6 +2905,11 @@ static void mpi3mr_watchdog_work(struct work_struct *work) return; } + if (mrioc->invalid_io_comp) { + mpi3mr_soft_reset_handler(mrioc, MPI3MR_RESET_FROM_INVALID_COMPLETION, 1); + return; + } + if (atomic_read(&mrioc->admin_pend_isr)) { ioc_err(mrioc, "Unprocessed admin ISR instance found\n" "flush admin replies\n"); @@ -4834,6 +4855,7 @@ void mpi3mr_memset_buffers(struct mpi3mr_ioc *mrioc) mrioc->req_qinfo[i].qid = 0; mrioc->req_qinfo[i].reply_qid = 0; spin_lock_init(&mrioc->req_qinfo[i].q_lock); + mrioc->req_qinfo[i].last_full_host_tag = 0; mpi3mr_memset_op_req_q_buffers(mrioc, i); } } @@ -5050,9 +5072,9 @@ void mpi3mr_free_mem(struct mpi3mr_ioc *mrioc) */ static void mpi3mr_issue_ioc_shutdown(struct mpi3mr_ioc *mrioc) { - u32 ioc_config, ioc_status; - u8 retval = 1; - u32 timeout = MPI3MR_DEFAULT_SHUTDOWN_TIME * 10; + u32 ioc_config, ioc_status, shutdown_action; + u8 retval = 1, retry = 0; + u32 timeout = MPI3MR_DEFAULT_SHUTDOWN_TIME * 10, timeout_remaining = 0; ioc_info(mrioc, "Issuing shutdown Notification\n"); if (mrioc->unrecoverable) { @@ -5067,14 +5089,16 @@ static void mpi3mr_issue_ioc_shutdown(struct mpi3mr_ioc *mrioc) return; } + shutdown_action = MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NORMAL | + MPI3_SYSIF_IOC_CONFIG_DEVICE_SHUTDOWN_SEND_REQ; ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); - ioc_config |= MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NORMAL; - ioc_config |= MPI3_SYSIF_IOC_CONFIG_DEVICE_SHUTDOWN_SEND_REQ; + ioc_config |= shutdown_action; writel(ioc_config, &mrioc->sysif_regs->ioc_configuration); if (mrioc->facts.shutdown_timeout) timeout = mrioc->facts.shutdown_timeout * 10; + timeout_remaining = timeout; do { ioc_status = readl(&mrioc->sysif_regs->ioc_status); @@ -5083,8 +5107,26 @@ static void mpi3mr_issue_ioc_shutdown(struct mpi3mr_ioc *mrioc) retval = 0; break; } + if (mrioc->unrecoverable) + break; + if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) { + mpi3mr_print_fault_info(mrioc); + if (retry >= MPI3MR_MAX_SHUTDOWN_RETRY_COUNT) + break; + if (mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET, + MPI3MR_RESET_FROM_CTLR_CLEANUP)) + break; + ioc_config = + readl(&mrioc->sysif_regs->ioc_configuration); + ioc_config |= shutdown_action; + writel(ioc_config, + &mrioc->sysif_regs->ioc_configuration); + timeout_remaining = timeout; + retry++; + } msleep(100); - } while (--timeout); + } while (--timeout_remaining); ioc_status = readl(&mrioc->sysif_regs->ioc_status); ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); @@ -5658,6 +5700,7 @@ int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc, ssleep(MPI3MR_RESET_TOPOLOGY_SETTLE_TIME); out: + mrioc->invalid_io_comp = 0; if (!retval) { mrioc->diagsave_timeout = 0; mrioc->reset_in_progress = 0; diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c index 90f8b9d1c2ac..402d1f35d214 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_os.c +++ b/drivers/scsi/mpi3mr/mpi3mr_os.c @@ -3459,8 +3459,15 @@ void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc, } scmd = mpi3mr_scmd_from_host_tag(mrioc, host_tag, qidx); if (!scmd) { - panic("%s: Cannot Identify scmd for host_tag 0x%x\n", - mrioc->name, host_tag); + ioc_err(mrioc, "Cannot Identify scmd for host_tag 0x%x", host_tag); + ioc_err(mrioc, + "reply_desc_type(%d) host_tag(%d(0x%04x)): qid(%d): command issued to\n" + "handle(0x%04x) returned with ioc_status(0x%04x), log_info(0x%08x),\n" + "scsi_state(0x%02x), scsi_status(0x%02x), xfer_count(%d), resp_data(0x%08x)\n", + reply_desc_type, host_tag, host_tag, qidx+1, dev_handle, ioc_status, + ioc_loginfo, scsi_state, scsi_status, xfer_count, + resp_data); + mrioc->invalid_io_comp = 1; goto out; } priv = scsi_cmd_priv(scmd); diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 6ff788557294..12caffeed3a0 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -2738,8 +2738,20 @@ scsih_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim) pcie_device->enclosure_level, pcie_device->connector_name); + /* + * The HBA firmware passes the NVMe drive's MDTS + * (Maximum Data Transfer Size) up to the driver. However, + * the driver hardcodes a 4K buffer size for the PRP list, + * accommodating at most 512 entries. This strictly limits + * the maximum supported NVMe I/O transfer to 2 MiB. + * + * Cap max_hw_sectors to the smaller of the drive's reported + * MDTS or the 2 MiB driver limit to prevent kernel oopses. + */ + lim->max_hw_sectors = SZ_2M >> SECTOR_SHIFT; if (pcie_device->nvme_mdts) - lim->max_hw_sectors = pcie_device->nvme_mdts / 512; + lim->max_hw_sectors = min(lim->max_hw_sectors, + pcie_device->nvme_mdts >> SECTOR_SHIFT); pcie_device_put(pcie_device); spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h index 9f59930e8b4f..cd059b7599b4 100644 --- a/drivers/scsi/pmcraid.h +++ b/drivers/scsi/pmcraid.h @@ -657,7 +657,7 @@ struct pmcraid_hostrcb { */ struct pmcraid_instance { /* Array of allowed-to-be-exposed resources, initialized from - * Configutation Table, later updated with CCNs + * Configuration Table, later updated with CCNs */ struct pmcraid_resource_entry *res_entries; diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 2e584a8bf66b..6a05ce195aa0 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -1638,7 +1638,7 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr, { scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); int rval = QLA_FUNCTION_FAILED; - uint16_t state[6]; + uint16_t state[16]; uint32_t pstate; if (IS_QLAFX00(vha->hw)) { @@ -2402,6 +2402,63 @@ qla2x00_dport_diagnostics_show(struct device *dev, vha->dport_data[0], vha->dport_data[1], vha->dport_data[2], vha->dport_data[3]); } + +static ssize_t +qla2x00_mpi_fw_state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + int rval = QLA_FUNCTION_FAILED; + u16 state[16]; + u16 mpi_state; + struct qla_hw_data *ha = vha->hw; + + if (!(IS_QLA27XX(ha) || IS_QLA28XX(ha))) + return scnprintf(buf, PAGE_SIZE, + "MPI state reporting is not supported for this HBA.\n"); + + memset(state, 0, sizeof(state)); + + mutex_lock(&vha->hw->optrom_mutex); + if (qla2x00_chip_is_down(vha)) { + mutex_unlock(&vha->hw->optrom_mutex); + ql_dbg(ql_dbg_user, vha, 0x70df, + "ISP reset is in progress, failing mpi_fw_state.\n"); + return -EBUSY; + } else if (vha->hw->flags.eeh_busy) { + mutex_unlock(&vha->hw->optrom_mutex); + ql_dbg(ql_dbg_user, vha, 0x70ea, + "HBA in PCI error state, failing mpi_fw_state.\n"); + return -EBUSY; + } + + rval = qla2x00_get_firmware_state(vha, state); + mutex_unlock(&vha->hw->optrom_mutex); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_user, vha, 0x70eb, + "MB Command to retrieve MPI state failed (%d), failing mpi_fw_state.\n", + rval); + return -EIO; + } + + mpi_state = state[11]; + + if (!(mpi_state & BIT_15)) + return scnprintf(buf, PAGE_SIZE, + "MPI firmware state reporting is not supported by this firmware. (0x%02x)\n", + mpi_state); + + if (!(mpi_state & BIT_8)) + return scnprintf(buf, PAGE_SIZE, + "MPI firmware is disabled. (0x%02x)\n", + mpi_state); + + return scnprintf(buf, PAGE_SIZE, + "MPI firmware is enabled, state is %s. (0x%02x)\n", + mpi_state & BIT_9 ? "active" : "inactive", + mpi_state); +} + static DEVICE_ATTR(dport_diagnostics, 0444, qla2x00_dport_diagnostics_show, NULL); @@ -2469,6 +2526,8 @@ static DEVICE_ATTR(port_speed, 0644, qla2x00_port_speed_show, qla2x00_port_speed_store); static DEVICE_ATTR(port_no, 0444, qla2x00_port_no_show, NULL); static DEVICE_ATTR(fw_attr, 0444, qla2x00_fw_attr_show, NULL); +static DEVICE_ATTR(mpi_fw_state, 0444, qla2x00_mpi_fw_state_show, NULL); + static struct attribute *qla2x00_host_attrs[] = { &dev_attr_driver_version.attr.attr, @@ -2517,6 +2576,7 @@ static struct attribute *qla2x00_host_attrs[] = { &dev_attr_qlini_mode.attr, &dev_attr_ql2xiniexchg.attr, &dev_attr_ql2xexchoffld.attr, + &dev_attr_mpi_fw_state.attr, NULL, }; diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 730c42b1a7b9..e746c9274cde 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -4914,7 +4914,7 @@ qla2x00_fw_ready(scsi_qla_host_t *vha) unsigned long wtime, mtime, cs84xx_time; uint16_t min_wait; /* Minimum wait time if loop is down */ uint16_t wait_time; /* Wait time if loop is coming ready */ - uint16_t state[6]; + uint16_t state[16]; struct qla_hw_data *ha = vha->hw; if (IS_QLAFX00(vha->hw)) diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 53eaff1e0f65..47fbd830ff16 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -621,7 +621,7 @@ static inline int qla_mapq_alloc_qp_cpu_map(struct qla_hw_data *ha) scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); if (!ha->qp_cpu_map) { - ha->qp_cpu_map = kzalloc_objs(struct qla_qpair *, NR_CPUS); + ha->qp_cpu_map = kzalloc_objs(struct qla_qpair *, nr_cpu_ids); if (!ha->qp_cpu_map) { ql_log(ql_log_fatal, vha, 0x0180, "Unable to allocate memory for qp_cpu_map ptrs.\n"); diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 0d598be6f3ea..44e310f1a370 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -2268,6 +2268,13 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states) mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; else mcp->in_mb = MBX_1|MBX_0; + + if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + mcp->mb[12] = 0; + mcp->out_mb |= MBX_12; + mcp->in_mb |= MBX_12; + } + mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); @@ -2280,6 +2287,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states) states[3] = mcp->mb[4]; states[4] = mcp->mb[5]; states[5] = mcp->mb[6]; /* DPORT status */ + if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) + states[11] = mcp->mb[12]; /* MPI state. */ } if (rval != QLA_SUCCESS) { diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 28df9025def0..3be23ed067e6 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -1841,6 +1841,7 @@ static const struct target_core_fabric_ops tcm_qla2xxx_ops = { .tfc_tpg_base_attrs = tcm_qla2xxx_tpg_attrs, .tfc_tpg_attrib_attrs = tcm_qla2xxx_tpg_attrib_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_DIRECT_SUBMIT, .direct_submit_supp = 1, }; @@ -1881,6 +1882,7 @@ static const struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = { .tfc_wwn_attrs = tcm_qla2xxx_wwn_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_DIRECT_SUBMIT, .direct_submit_supp = 1, }; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index d3a8cd4166f9..6e8c7a42603e 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -3460,6 +3461,52 @@ int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len) } EXPORT_SYMBOL(scsi_vpd_lun_id); +/** + * scsi_vpd_lun_serial - return a unique device serial number + * @sdev: SCSI device + * @sn: buffer for the serial number + * @sn_size: size of the buffer + * + * Copies the device serial number into @sn based on the information in + * the VPD page 0x80 of the device. The string will be null terminated + * and have leading and trailing whitespace stripped. + * + * Returns the length of the serial number or error on failure. + */ +int scsi_vpd_lun_serial(struct scsi_device *sdev, char *sn, size_t sn_size) +{ + const struct scsi_vpd *vpd_pg80; + const unsigned char *d; + int len; + + guard(rcu)(); + vpd_pg80 = rcu_dereference(sdev->vpd_pg80); + if (!vpd_pg80) + return -ENXIO; + + len = vpd_pg80->len - 4; + d = vpd_pg80->data + 4; + + /* Skip leading spaces */ + while (len > 0 && isspace(*d)) { + len--; + d++; + } + + /* Skip trailing spaces */ + while (len > 0 && isspace(d[len - 1])) + len--; + + if (sn_size < len + 1) + return -EINVAL; + + memcpy(sn, d, len); + sn[len] = '\0'; + + return len; +} +EXPORT_SYMBOL(scsi_vpd_lun_serial); + /** * scsi_vpd_tpg_id - return a target port group identifier * @sdev: SCSI device diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 7b11bc7de0e3..ef22a4228b85 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1940,7 +1940,6 @@ static void scsi_sysfs_add_devices(struct Scsi_Host *shost) static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) { struct async_scan_data *data = NULL; - unsigned long flags; if (strncmp(scsi_scan_type, "sync", 4) == 0) return NULL; @@ -1959,9 +1958,7 @@ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) goto err; init_completion(&data->prev_finished); - spin_lock_irqsave(shost->host_lock, flags); - shost->async_scan = 1; - spin_unlock_irqrestore(shost->host_lock, flags); + shost->async_scan = true; mutex_unlock(&shost->scan_mutex); spin_lock(&async_scan_lock); @@ -1989,7 +1986,6 @@ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) static void scsi_finish_async_scan(struct async_scan_data *data) { struct Scsi_Host *shost; - unsigned long flags; if (!data) return; @@ -2009,9 +2005,7 @@ static void scsi_finish_async_scan(struct async_scan_data *data) scsi_sysfs_add_devices(shost); - spin_lock_irqsave(shost->host_lock, flags); - shost->async_scan = 0; - spin_unlock_irqrestore(shost->host_lock, flags); + shost->async_scan = false; mutex_unlock(&shost->scan_mutex); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 6b8c5c05f294..dfc3559e7e04 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1051,6 +1051,21 @@ sdev_show_wwid(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(wwid, S_IRUGO, sdev_show_wwid, NULL); +static ssize_t +sdev_show_serial(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + ssize_t ret; + + ret = scsi_vpd_lun_serial(sdev, buf, PAGE_SIZE - 1); + if (ret < 0) + return ret; + + buf[ret] = '\n'; + return ret + 1; +} +static DEVICE_ATTR(serial, S_IRUGO, sdev_show_serial, NULL); + #define BLIST_FLAG_NAME(name) \ [const_ilog2((__force __u64)BLIST_##name)] = #name static const char *const sdev_bflags_name[] = { @@ -1295,6 +1310,7 @@ static struct attribute *scsi_sdev_attrs[] = { &dev_attr_device_busy.attr, &dev_attr_vendor.attr, &dev_attr_model.attr, + &dev_attr_serial.attr, &dev_attr_rev.attr, &dev_attr_rescan.attr, &dev_attr_delete.attr, diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 628a1d0a74ba..adc3fa55ca2c 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -107,8 +107,11 @@ static void sd_config_write_same(struct scsi_disk *sdkp, static void sd_revalidate_disk(struct gendisk *); static DEFINE_IDA(sd_index_ida); +static DEFINE_MUTEX(sd_mutex_lock); static mempool_t *sd_page_pool; +static mempool_t *sd_large_page_pool; +static atomic_t sd_large_page_pool_users = ATOMIC_INIT(0); static struct lock_class_key sd_bio_compl_lkclass; static const char *sd_cache_types[] = { @@ -116,6 +119,33 @@ static const char *sd_cache_types[] = { "write back, no read (daft)" }; +static int sd_large_pool_create(void) +{ + mutex_lock(&sd_mutex_lock); + if (!sd_large_page_pool) { + sd_large_page_pool = mempool_create_page_pool( + SD_MEMPOOL_SIZE, get_order(BLK_MAX_BLOCK_SIZE)); + if (!sd_large_page_pool) { + printk(KERN_ERR "sd: can't create large page mempool\n"); + mutex_unlock(&sd_mutex_lock); + return -ENOMEM; + } + } + atomic_inc(&sd_large_page_pool_users); + mutex_unlock(&sd_mutex_lock); + return 0; +} + +static void sd_large_pool_destroy(void) +{ + mutex_lock(&sd_mutex_lock); + if (atomic_dec_and_test(&sd_large_page_pool_users)) { + mempool_destroy(sd_large_page_pool); + sd_large_page_pool = NULL; + } + mutex_unlock(&sd_mutex_lock); +} + static void sd_disable_discard(struct scsi_disk *sdkp) { sdkp->provisioning_mode = SD_LBP_DISABLE; @@ -928,14 +958,24 @@ static unsigned char sd_setup_protect_cmnd(struct scsi_cmnd *scmd, return protect; } -static void *sd_set_special_bvec(struct request *rq, unsigned int data_len) +static void *sd_set_special_bvec(struct scsi_cmnd *cmd, unsigned int data_len) { struct page *page; + struct request *rq = scsi_cmd_to_rq(cmd); + struct scsi_device *sdp = cmd->device; + unsigned sector_size = sdp->sector_size; + unsigned int nr_pages = DIV_ROUND_UP(sector_size, PAGE_SIZE); + int n; - page = mempool_alloc(sd_page_pool, GFP_ATOMIC); + if (sector_size > PAGE_SIZE) + page = mempool_alloc(sd_large_page_pool, GFP_ATOMIC); + else + page = mempool_alloc(sd_page_pool, GFP_ATOMIC); if (!page) return NULL; - clear_highpage(page); + + for (n = 0; n < nr_pages; n++) + clear_highpage(page + n); bvec_set_page(&rq->special_vec, page, data_len, 0); rq->rq_flags |= RQF_SPECIAL_PAYLOAD; return bvec_virt(&rq->special_vec); @@ -951,7 +991,7 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd) unsigned int data_len = 24; char *buf; - buf = sd_set_special_bvec(rq, data_len); + buf = sd_set_special_bvec(cmd, data_len); if (!buf) return BLK_STS_RESOURCE; @@ -1040,7 +1080,7 @@ static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd, u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); u32 data_len = sdp->sector_size; - if (!sd_set_special_bvec(rq, data_len)) + if (!sd_set_special_bvec(cmd, data_len)) return BLK_STS_RESOURCE; cmd->cmd_len = 16; @@ -1067,7 +1107,7 @@ static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd, u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); u32 data_len = sdp->sector_size; - if (!sd_set_special_bvec(rq, data_len)) + if (!sd_set_special_bvec(cmd, data_len)) return BLK_STS_RESOURCE; cmd->cmd_len = 10; @@ -1513,9 +1553,15 @@ static blk_status_t sd_init_command(struct scsi_cmnd *cmd) static void sd_uninit_command(struct scsi_cmnd *SCpnt) { struct request *rq = scsi_cmd_to_rq(SCpnt); + struct scsi_device *sdp = SCpnt->device; + unsigned sector_size = sdp->sector_size; - if (rq->rq_flags & RQF_SPECIAL_PAYLOAD) - mempool_free(rq->special_vec.bv_page, sd_page_pool); + if (rq->rq_flags & RQF_SPECIAL_PAYLOAD) { + if (sector_size > PAGE_SIZE) + mempool_free(rq->special_vec.bv_page, sd_large_page_pool); + else + mempool_free(rq->special_vec.bv_page, sd_page_pool); + } } static bool sd_need_revalidate(struct gendisk *disk, struct scsi_disk *sdkp) @@ -2912,10 +2958,7 @@ got_data: "Sector size 0 reported, assuming 512.\n"); } - if (sector_size != 512 && - sector_size != 1024 && - sector_size != 2048 && - sector_size != 4096) { + if (blk_validate_block_size(sector_size)) { sd_printk(KERN_NOTICE, sdkp, "Unsupported sector size %d.\n", sector_size); /* @@ -4018,6 +4061,7 @@ static int sd_probe(struct scsi_device *sdp) error = device_add(&sdkp->disk_dev); if (error) { put_device(&sdkp->disk_dev); + put_disk(gd); goto out; } @@ -4043,6 +4087,12 @@ static int sd_probe(struct scsi_device *sdp) sdkp->max_medium_access_timeouts = SD_MAX_MEDIUM_TIMEOUTS; sd_revalidate_disk(gd); + if (sdp->sector_size > PAGE_SIZE) { + if (sd_large_pool_create()) { + error = -ENOMEM; + goto out_free_index; + } + } if (sdp->removable) { gd->flags |= GENHD_FL_REMOVABLE; @@ -4060,6 +4110,8 @@ static int sd_probe(struct scsi_device *sdp) if (error) { device_unregister(&sdkp->disk_dev); put_disk(gd); + if (sdp->sector_size > PAGE_SIZE) + sd_large_pool_destroy(); goto out; } @@ -4212,6 +4264,9 @@ static void sd_remove(struct scsi_device *sdp) sd_shutdown(sdp); put_disk(sdkp->disk); + + if (sdp->sector_size > PAGE_SIZE) + sd_large_pool_destroy(); } static inline bool sd_do_start_stop(struct scsi_device *sdev, bool runtime) @@ -4435,6 +4490,8 @@ static void __exit exit_sd(void) scsi_unregister_driver(&sd_template); mempool_destroy(sd_page_pool); + if (sd_large_page_pool) + mempool_destroy(sd_large_page_pool); class_unregister(&sd_disk_class); diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 37bac49f30f0..74cd4e8a61c2 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -81,14 +81,14 @@ static int sg_proc_init(void); #define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ) -static int sg_big_buff = SG_DEF_RESERVED_SIZE; /* N.B. This variable is readable and writeable via - /proc/scsi/sg/def_reserved_size . Each time sg_open() is called a buffer - of this size (or less if there is not enough memory) will be reserved - for use by this file descriptor. [Deprecated usage: this variable is also - readable via /proc/sys/kernel/sg-big-buff if the sg driver is built into - the kernel (i.e. it is not a module).] */ -static int def_reserved_size = -1; /* picks up init parameter */ + * /proc/scsi/sg/def_reserved_size . Each time sg_open() is called a buffer + * of this size (or less if there is not enough memory) will be reserved + * for use by this file descriptor. + */ + +/* picks up init parameter */ +static int def_reserved_size = SG_DEF_RESERVED_SIZE; static int sg_allow_dio = SG_ALLOW_DIO_DEF; static int scatter_elem_sz = SG_SCATTER_SZ; @@ -1623,10 +1623,35 @@ sg_remove_device(struct device *cl_dev) } module_param_named(scatter_elem_sz, scatter_elem_sz, int, S_IRUGO | S_IWUSR); -module_param_named(def_reserved_size, def_reserved_size, int, - S_IRUGO | S_IWUSR); module_param_named(allow_dio, sg_allow_dio, int, S_IRUGO | S_IWUSR); +static int def_reserved_size_set(const char *val, const struct kernel_param *kp) +{ + int size, ret; + + if (!val) + return -EINVAL; + + ret = kstrtoint(val, 0, &size); + if (ret) + return ret; + + /* limit to 1 MB */ + if (size < 0 || size > 1048576) + return -ERANGE; + + def_reserved_size = size; + return 0; +} + +static const struct kernel_param_ops def_reserved_size_ops = { + .set = def_reserved_size_set, + .get = param_get_int, +}; + +module_param_cb(def_reserved_size, &def_reserved_size_ops, &def_reserved_size, + S_IRUGO | S_IWUSR); + MODULE_AUTHOR("Douglas Gilbert"); MODULE_DESCRIPTION("SCSI generic (sg) driver"); MODULE_LICENSE("GPL"); @@ -1638,35 +1663,6 @@ MODULE_PARM_DESC(scatter_elem_sz, "scatter gather element " MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd"); MODULE_PARM_DESC(allow_dio, "allow direct I/O (default: 0 (disallow))"); -#ifdef CONFIG_SYSCTL -#include - -static const struct ctl_table sg_sysctls[] = { - { - .procname = "sg-big-buff", - .data = &sg_big_buff, - .maxlen = sizeof(int), - .mode = 0444, - .proc_handler = proc_dointvec, - }, -}; - -static struct ctl_table_header *hdr; -static void register_sg_sysctls(void) -{ - if (!hdr) - hdr = register_sysctl("kernel", sg_sysctls); -} - -static void unregister_sg_sysctls(void) -{ - unregister_sysctl_table(hdr); -} -#else -#define register_sg_sysctls() do { } while (0) -#define unregister_sg_sysctls() do { } while (0) -#endif /* CONFIG_SYSCTL */ - static int __init init_sg(void) { @@ -1676,10 +1672,6 @@ init_sg(void) scatter_elem_sz = PAGE_SIZE; scatter_elem_sz_prev = scatter_elem_sz; } - if (def_reserved_size >= 0) - sg_big_buff = def_reserved_size; - else - def_reserved_size = sg_big_buff; rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS, "sg"); @@ -1697,7 +1689,6 @@ init_sg(void) return 0; } class_unregister(&sg_sysfs_class); - register_sg_sysctls(); err_out: unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS); return rc; @@ -1706,7 +1697,6 @@ err_out: static void __exit exit_sg(void) { - unregister_sg_sysctls(); #ifdef CONFIG_SCSI_PROC_FS remove_proc_subtree("scsi/sg", NULL); #endif /* CONFIG_SCSI_PROC_FS */ @@ -1811,7 +1801,7 @@ sg_start_req(Sg_request *srp, unsigned char *cmd) } res = blk_rq_map_user_io(rq, md, hp->dxferp, hp->dxfer_len, - GFP_ATOMIC, iov_count, iov_count, 1, rw); + GFP_KERNEL, iov_count, iov_count, 1, rw); if (!res) { srp->bio = rq->bio; @@ -2182,10 +2172,8 @@ sg_add_sfp(Sg_device * sdp) write_unlock_irqrestore(&sdp->sfd_lock, iflags); SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_add_sfp: sfp=0x%p\n", sfp)); - if (unlikely(sg_big_buff != def_reserved_size)) - sg_big_buff = def_reserved_size; - bufflen = min_t(int, sg_big_buff, + bufflen = min_t(int, def_reserved_size, max_sectors_bytes(sdp->device->request_queue)); sg_build_reserve(sfp, bufflen); SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, @@ -2413,7 +2401,7 @@ sg_proc_write_adio(struct file *filp, const char __user *buffer, static int sg_proc_single_open_dressz(struct inode *inode, struct file *file) { - return single_open(file, sg_proc_seq_show_int, &sg_big_buff); + return single_open(file, sg_proc_seq_show_int, &def_reserved_size); } static ssize_t @@ -2430,7 +2418,7 @@ sg_proc_write_dressz(struct file *filp, const char __user *buffer, if (err) return err; if (k <= 1048576) { /* limit "big buff" to 1 MB */ - sg_big_buff = k; + def_reserved_size = k; return count; } return -ERANGE; @@ -2603,7 +2591,7 @@ static int sg_proc_seq_show_debug(struct seq_file *s, void *v) if (it && (0 == it->index)) seq_printf(s, "max_active_device=%d def_reserved_size=%d\n", - (int)it->max, sg_big_buff); + (int)it->max, def_reserved_size); read_lock_irqsave(&sg_index_lock, iflags); sdp = it ? sg_lookup_dev(it->index) : NULL; diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index b4ed991976d0..2026ac645d6a 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -9427,6 +9427,7 @@ static void pqi_shutdown(struct pci_dev *pci_dev) pqi_crash_if_pending_command(ctrl_info); pqi_reset(ctrl_info); + pqi_ctrl_unblock_device_reset(ctrl_info); } static void pqi_process_lockup_action_param(void) diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 7adb2573f50d..c36c54ecd354 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -395,7 +395,7 @@ static blk_status_t sr_init_command(struct scsi_cmnd *SCpnt) switch (req_op(rq)) { case REQ_OP_WRITE: - if (!cd->writeable) + if (get_disk_ro(cd->disk)) goto out; SCpnt->cmnd[0] = WRITE_10; cd->cdi.media_written = 1; @@ -681,6 +681,7 @@ static int sr_probe(struct scsi_device *sdev) error = -ENOMEM; if (get_capabilities(cd)) goto fail_minor; + cdrom_probe_write_features(&cd->cdi); sr_vendor_init(cd); set_capacity(disk, cd->capacity); @@ -899,14 +900,6 @@ static int get_capabilities(struct scsi_cd *cd) /*else I don't think it can close its tray cd->cdi.mask |= CDC_CLOSE_TRAY; */ - /* - * if DVD-RAM, MRW-W or CD-RW, we are randomly writable - */ - if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) != - (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) { - cd->writeable = 1; - } - kfree(buffer); return 0; } diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h index dc899277b3a4..2d92f9cb6fec 100644 --- a/drivers/scsi/sr.h +++ b/drivers/scsi/sr.h @@ -35,7 +35,6 @@ typedef struct scsi_cd { struct scsi_device *device; unsigned int vendor; /* vendor code, see sr_vendor.c */ unsigned long ms_offset; /* for reading multisession-CD's */ - unsigned writeable : 1; unsigned use:1; /* is this device still supportable */ unsigned xa_flag:1; /* CD has XA sectors ? */ unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */ diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index ae1abab97835..6977ca8a0658 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1131,6 +1131,26 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request, kfree(payload); } +/* + * The current SCSI handling on the host side does not correctly handle: + * INQUIRY with page code 0x80, MODE_SENSE / MODE_SENSE_10 with cmd[2] == 0x1c, + * and (for FC) MAINTENANCE_IN / PERSISTENT_RESERVE_IN passthrough. + */ +static bool storvsc_host_mishandles_cmd(u8 opcode, struct hv_device *device) +{ + switch (opcode) { + case INQUIRY: + case MODE_SENSE: + case MODE_SENSE_10: + return true; + case MAINTENANCE_IN: + case PERSISTENT_RESERVE_IN: + return hv_dev_is_fc(device); + default: + return false; + } +} + static void storvsc_on_io_completion(struct storvsc_device *stor_device, struct vstor_packet *vstor_packet, struct storvsc_cmd_request *request) @@ -1141,22 +1161,12 @@ static void storvsc_on_io_completion(struct storvsc_device *stor_device, stor_pkt = &request->vstor_packet; /* - * The current SCSI handling on the host side does - * not correctly handle: - * INQUIRY command with page code parameter set to 0x80 - * MODE_SENSE and MODE_SENSE_10 command with cmd[2] == 0x1c - * MAINTENANCE_IN is not supported by HyperV FC passthrough - * * Setup srb and scsi status so this won't be fatal. * We do this so we can distinguish truly fatal failues * (srb status == 0x4) and off-line the device in that case. */ - if ((stor_pkt->vm_srb.cdb[0] == INQUIRY) || - (stor_pkt->vm_srb.cdb[0] == MODE_SENSE) || - (stor_pkt->vm_srb.cdb[0] == MODE_SENSE_10) || - (stor_pkt->vm_srb.cdb[0] == MAINTENANCE_IN && - hv_dev_is_fc(device))) { + if (storvsc_host_mishandles_cmd(stor_pkt->vm_srb.cdb[0], device)) { vstor_packet->vm_srb.scsi_status = 0; vstor_packet->vm_srb.srb_status = SRB_STATUS_SUCCESS; } diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 0ed8558dad72..5fdaa71f0652 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -233,7 +233,6 @@ static void virtscsi_ctrl_done(struct virtqueue *vq) virtscsi_vq_done(vscsi, &vscsi->ctrl_vq, virtscsi_complete_free); }; -static void virtscsi_handle_event(struct work_struct *work); static int virtscsi_kick_event(struct virtio_scsi *vscsi, struct virtio_scsi_event_node *event_node) @@ -242,7 +241,6 @@ static int virtscsi_kick_event(struct virtio_scsi *vscsi, struct scatterlist sg; unsigned long flags; - INIT_WORK(&event_node->work, virtscsi_handle_event); sg_init_one(&sg, event_node->event, sizeof(struct virtio_scsi_event)); spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags); @@ -984,8 +982,10 @@ static int virtscsi_probe(struct virtio_device *vdev) virtio_device_ready(vdev); - if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) - virtscsi_kick_event_all(vscsi); + for (int i = 0; i < VIRTIO_SCSI_EVENT_LEN; i++) + INIT_WORK(&vscsi->event_list[i].work, virtscsi_handle_event); + + virtscsi_kick_event_all(vscsi); scsi_scan_host(shost); return 0; @@ -1002,8 +1002,7 @@ static void virtscsi_remove(struct virtio_device *vdev) struct Scsi_Host *shost = virtio_scsi_host(vdev); struct virtio_scsi *vscsi = shost_priv(shost); - if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) - virtscsi_cancel_event_work(vscsi); + virtscsi_cancel_event_work(vscsi); scsi_remove_host(shost); virtscsi_remove_vqs(vdev); @@ -1029,8 +1028,7 @@ static int virtscsi_restore(struct virtio_device *vdev) virtio_device_ready(vdev); - if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) - virtscsi_kick_event_all(vscsi); + virtscsi_kick_event_all(vscsi); return err; } diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index 2af3bfda313e..0cc2953d59a5 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -961,7 +961,7 @@ static ssize_t link_fsm_show(struct kobject *kobj, struct hccs_link_status link_status = {0}; const struct { u8 link_fsm; - char *str; + const char *str; } link_fsm_map[] = { {HCCS_PORT_RESET, "reset"}, {HCCS_PORT_SETUP, "setup"}, @@ -1621,8 +1621,7 @@ static void hccs_remove_topo_dirs(struct hccs_dev *hdev) hccs_remove_misc_sysfs(hdev); } -static int hccs_create_hccs_dir(struct hccs_dev *hdev, - struct hccs_die_info *die, +static int hccs_create_hccs_dir(struct hccs_die_info *die, struct hccs_port_info *port) { int ret; @@ -1654,7 +1653,7 @@ static int hccs_create_die_dir(struct hccs_dev *hdev, for (i = 0; i < die->port_num; i++) { port = &die->ports[i]; - ret = hccs_create_hccs_dir(hdev, die, port); + ret = hccs_create_hccs_dir(die, port); if (ret) { dev_err(hdev->dev, "create hccs%u dir failed.\n", port->port_id); diff --git a/drivers/soc/microchip/Kconfig b/drivers/soc/microchip/Kconfig index bcf554602561..af7946741bce 100644 --- a/drivers/soc/microchip/Kconfig +++ b/drivers/soc/microchip/Kconfig @@ -1,3 +1,14 @@ +config POLARFIRE_SOC_IRQ_MUX + bool "Microchip PolarFire SoC's GPIO IRQ Mux" + depends on ARCH_MICROCHIP + select REGMAP + select REGMAP_MMIO + default y + help + Support for the interrupt mux on Polarfire SoC. It sits between + the GPIO controllers and the PLIC, as only 41 interrupts are shared + between 3 GPIO controllers with a total of 70 interrupts. + config POLARFIRE_SOC_SYS_CTRL tristate "Microchip PolarFire SoC (MPFS) system controller support" depends on POLARFIRE_SOC_MAILBOX diff --git a/drivers/soc/microchip/Makefile b/drivers/soc/microchip/Makefile index 1a3a1594b089..55775db45ee7 100644 --- a/drivers/soc/microchip/Makefile +++ b/drivers/soc/microchip/Makefile @@ -1,2 +1,3 @@ +obj-$(CONFIG_POLARFIRE_SOC_IRQ_MUX) += mpfs-irqmux.o obj-$(CONFIG_POLARFIRE_SOC_SYS_CTRL) += mpfs-sys-controller.o obj-$(CONFIG_POLARFIRE_SOC_SYSCONS) += mpfs-control-scb.o mpfs-mss-top-sysreg.o diff --git a/drivers/soc/microchip/mpfs-irqmux.c b/drivers/soc/microchip/mpfs-irqmux.c new file mode 100644 index 000000000000..ae15e913e780 --- /dev/null +++ b/drivers/soc/microchip/mpfs-irqmux.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Largely copied from rzn1_irqmux.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MPFS_IRQMUX_CR 0x54 +#define MPFS_IRQMUX_NUM_CHILDREN 96 +#define MPFS_IRQMUX_NUM_DIRECT 38 +#define MPFS_IRQMUX_DIRECT_START 13 +#define MPFS_IRQMUX_DIRECT_END 50 +#define MPFS_IRQMUX_NONDIRECT_END 53 + +static int mpfs_irqmux_is_direct_mode(struct device *dev, + const struct of_phandle_args *parent_args) +{ + if (parent_args->args_count != 1) { + dev_err(dev, "Invalid interrupt-map item\n"); + return -EINVAL; + } + + if (parent_args->args[0] < MPFS_IRQMUX_DIRECT_START || + parent_args->args[0] > MPFS_IRQMUX_NONDIRECT_END) { + dev_err(dev, "Invalid interrupt %u\n", parent_args->args[0]); + return -EINVAL; + } + + if (parent_args->args[0] > MPFS_IRQMUX_DIRECT_END) + return 0; + + return 1; +} + +static int mpfs_irqmux_probe(struct platform_device *pdev) +{ + DECLARE_BITMAP(child_done, MPFS_IRQMUX_NUM_CHILDREN) = {}; + DECLARE_BITMAP(parent_done, MPFS_IRQMUX_NUM_DIRECT) = {}; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct of_imap_parser imap_parser; + struct of_imap_item imap_item; + struct regmap *regmap; + int ret, direct_mode, line, controller, gpio, parent_line; + u32 tmp, val = 0, old; + + regmap = device_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to find syscon regmap\n"); + + /* We support only #interrupt-cells = <1> and #address-cells = <0> */ + ret = of_property_read_u32(np, "#interrupt-cells", &tmp); + if (ret) + return ret; + if (tmp != 1) + return -EINVAL; + + ret = of_property_read_u32(np, "#address-cells", &tmp); + if (ret) + return ret; + if (tmp != 0) + return -EINVAL; + + ret = of_imap_parser_init(&imap_parser, np, &imap_item); + if (ret) + return ret; + + for_each_of_imap_item(&imap_parser, &imap_item) { + + direct_mode = mpfs_irqmux_is_direct_mode(dev, &imap_item.parent_args); + if (direct_mode < 0) { + of_node_put(imap_item.parent_args.np); + return direct_mode; + } + + line = imap_item.child_imap[0]; + gpio = line % 32; + controller = line / 32; + + if (controller > 2) { + of_node_put(imap_item.parent_args.np); + dev_err(dev, "child interrupt number too large: %d\n", line); + return -EINVAL; + } + + if (test_and_set_bit(line, child_done)) { + of_node_put(imap_item.parent_args.np); + dev_err(dev, "mux child line %d already defined in interrupt-map\n", + line); + return -EINVAL; + } + + parent_line = imap_item.parent_args.args[0] - MPFS_IRQMUX_DIRECT_START; + if (direct_mode && test_and_set_bit(parent_line, parent_done)) { + of_node_put(imap_item.parent_args.np); + dev_err(dev, "mux parent line %d already defined in interrupt-map\n", + line); + return -EINVAL; + } + + /* + * There are 41 interrupts assigned to GPIOs, of which 38 are "direct". Since the + * mux has 32 bits only, 6 of these exclusive/"direct" interrupts remain. These + * are used by GPIO controller 1's lines 18 to 23. Nothing needs to be done + * for these interrupts. + */ + if (controller == 1 && gpio >= 18) + continue; + + /* + * The mux has a single register, where bits 0 to 13 mux between GPIO controller + * 1's 14 GPIOs and GPIO controller 2's first 14 GPIOs. The remaining bits mux + * between the first 18 GPIOs of controller 1 and the last 18 GPIOS of + * controller 2. If a bit in the mux's control register is set, the + * corresponding interrupt line for GPIO controller 0 or 1 will be put in + * "non-direct" mode. If cleared, the "fabric" controller's will. + * + * Register layout: + * GPIO 1 interrupt line 17 | mux bit 31 | GPIO 2 interrupt line 31 + * ... | ... | ... + * ... | ... | ... + * GPIO 1 interrupt line 0 | mux bit 14 | GPIO 2 interrupt line 14 + * GPIO 0 interrupt line 13 | mux bit 13 | GPIO 2 interrupt line 13 + * ... | ... | ... + * ... | ... | ... + * GPIO 0 interrupt line 0 | mux bit 0 | GPIO 2 interrupt line 0 + * + * As the binding mandates 70 items, one for each GPIO line, there's no need to + * handle anything for GPIO controller 2, since the bit will be set for the + * corresponding line in GPIO controller 0 or 1. + */ + if (controller == 2) + continue; + + /* + * If in direct mode, the bit is cleared, nothing needs to be done as val is zero + * initialised and that's the direct mode setting for GPIO controller 0 and 1. + */ + if (direct_mode) + continue; + + if (controller == 0) + val |= 1U << gpio; + else + val |= 1U << (gpio + 14); + } + + regmap_read(regmap, MPFS_IRQMUX_CR, &old); + regmap_write(regmap, MPFS_IRQMUX_CR, val); + + if (val != old) + dev_info(dev, "firmware mux setting of 0x%x overwritten to 0x%x\n", old, val); + + return 0; +} + +static const struct of_device_id mpfs_irqmux_of_match[] = { + { .compatible = "microchip,mpfs-irqmux", }, + { } +}; +MODULE_DEVICE_TABLE(of, mpfs_irqmux_of_match); + +static struct platform_driver mpfs_irqmux_driver = { + .probe = mpfs_irqmux_probe, + .driver = { + .name = "mpfs_irqmux", + .of_match_table = mpfs_irqmux_of_match, + }, +}; +module_platform_driver(mpfs_irqmux_driver); + +MODULE_AUTHOR("Conor Dooley "); +MODULE_DESCRIPTION("Polarfire SoC interrupt mux driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/microchip/mpfs-sys-controller.c b/drivers/soc/microchip/mpfs-sys-controller.c index 10b2fc39da66..92d1142a59e6 100644 --- a/drivers/soc/microchip/mpfs-sys-controller.c +++ b/drivers/soc/microchip/mpfs-sys-controller.c @@ -36,6 +36,11 @@ struct mpfs_sys_controller { struct kref consumers; }; +struct mpfs_syscon_config { + unsigned int nb_subdevs; + struct platform_device *subdevs; +}; + int mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct mpfs_mss_msg *msg) { unsigned long timeout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS); @@ -110,25 +115,11 @@ struct mtd_info *mpfs_sys_controller_get_flash(struct mpfs_sys_controller *mpfs_ } EXPORT_SYMBOL(mpfs_sys_controller_get_flash); -static struct platform_device subdevs[] = { - { - .name = "mpfs-rng", - .id = -1, - }, - { - .name = "mpfs-generic-service", - .id = -1, - }, - { - .name = "mpfs-auto-update", - .id = -1, - }, -}; - static int mpfs_sys_controller_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mpfs_sys_controller *sys_controller; + struct mpfs_syscon_config *of_data; struct device_node *np; int i, ret; @@ -165,11 +156,17 @@ no_flash: platform_set_drvdata(pdev, sys_controller); + of_data = (struct mpfs_syscon_config *) device_get_match_data(dev); + if (!of_data) { + dev_err(dev, "Error getting match data\n"); + return -EINVAL; + } - for (i = 0; i < ARRAY_SIZE(subdevs); i++) { - subdevs[i].dev.parent = dev; - if (platform_device_register(&subdevs[i])) - dev_warn(dev, "Error registering sub device %s\n", subdevs[i].name); + for (i = 0; i < of_data->nb_subdevs; i++) { + of_data->subdevs[i].dev.parent = dev; + if (platform_device_register(&of_data->subdevs[i])) + dev_warn(dev, "Error registering sub device %s\n", + of_data->subdevs[i].name); } dev_info(&pdev->dev, "Registered MPFS system controller\n"); @@ -188,8 +185,45 @@ static void mpfs_sys_controller_remove(struct platform_device *pdev) mpfs_sys_controller_put(sys_controller); } +static struct platform_device mpfs_subdevs[] = { + { + .name = "mpfs-rng", + .id = -1, + }, + { + .name = "mpfs-generic-service", + .id = -1, + }, + { + .name = "mpfs-auto-update", + .id = -1, + }, +}; + +static struct platform_device pic64gx_subdevs[] = { + { + .name = "mpfs-rng", + .id = -1, + }, + { + .name = "mpfs-generic-service", + .id = -1, + }, +}; + +static const struct mpfs_syscon_config mpfs_config = { + .nb_subdevs = ARRAY_SIZE(mpfs_subdevs), + .subdevs = mpfs_subdevs, +}; + +static const struct mpfs_syscon_config pic64gx_config = { + .nb_subdevs = ARRAY_SIZE(pic64gx_subdevs), + .subdevs = pic64gx_subdevs, +}; + static const struct of_device_id mpfs_sys_controller_of_match[] = { - {.compatible = "microchip,mpfs-sys-controller", }, + {.compatible = "microchip,mpfs-sys-controller", .data = &mpfs_config}, + {.compatible = "microchip,pic64gx-sys-controller", .data = &pic64gx_config}, {}, }; MODULE_DEVICE_TABLE(of, mpfs_sys_controller_of_match); diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c index 78e72379a6e0..ea7f83916d8d 100644 --- a/drivers/soc/qcom/apr.c +++ b/drivers/soc/qcom/apr.c @@ -123,10 +123,10 @@ gpr_port_t *gpr_alloc_port(struct apr_device *gdev, struct device *dev, } EXPORT_SYMBOL_GPL(gpr_alloc_port); -static int pkt_router_send_svc_pkt(struct pkt_router_svc *svc, struct gpr_pkt *pkt) +static int pkt_router_send_svc_pkt(struct pkt_router_svc *svc, const struct gpr_pkt *pkt) { struct packet_router *pr = svc->pr; - struct gpr_hdr *hdr; + const struct gpr_hdr *hdr; unsigned long flags; int ret; @@ -139,13 +139,13 @@ static int pkt_router_send_svc_pkt(struct pkt_router_svc *svc, struct gpr_pkt *p return ret ? ret : hdr->pkt_size; } -int gpr_send_pkt(struct apr_device *gdev, struct gpr_pkt *pkt) +int gpr_send_pkt(struct apr_device *gdev, const struct gpr_pkt *pkt) { return pkt_router_send_svc_pkt(&gdev->svc, pkt); } EXPORT_SYMBOL_GPL(gpr_send_pkt); -int gpr_send_port_pkt(gpr_port_t *port, struct gpr_pkt *pkt) +int gpr_send_port_pkt(gpr_port_t *port, const struct gpr_pkt *pkt) { return pkt_router_send_svc_pkt(port, pkt); } diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index ad5899d083f3..0161ceec8842 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -5,7 +5,6 @@ */ #include -#include #include #include #include @@ -1782,6 +1781,94 @@ static const struct llcc_slice_config sc8280xp_data[] = { }, }; +static const struct llcc_slice_config sdm670_data[] = { + { + .usecase_id = LLCC_CPUSS, + .slice_id = 1, + .max_cap = 512, + .priority = 1, + .bonus_ways = 0xf, + .res_ways = 0x0, + .cache_mode = 0, + .dis_cap_alloc = true, + .retain_on_pc = true, + .activate_on_init = true, + }, { + .usecase_id = LLCC_ROTATOR, + .slice_id = 4, + .max_cap = 384, + .priority = 2, + .fixed_size = true, + .bonus_ways = 0x0, + .res_ways = 0xe, + .cache_mode = 2, + .dis_cap_alloc = true, + .retain_on_pc = true, + }, { + .usecase_id = LLCC_VOICE, + .slice_id = 5, + .max_cap = 512, + .priority = 1, + .bonus_ways = 0xf, + .res_ways = 0x0, + .cache_mode = 0, + .dis_cap_alloc = true, + .retain_on_pc = true, + }, { + .usecase_id = LLCC_AUDIO, + .slice_id = 6, + .max_cap = 512, + .priority = 1, + .bonus_ways = 0xf, + .res_ways = 0x0, + .cache_mode = 0, + .dis_cap_alloc = true, + .retain_on_pc = true, + }, { + .usecase_id = LLCC_MDM, + .slice_id = 8, + .max_cap = 512, + .priority = 1, + .bonus_ways = 0xf, + .res_ways = 0x0, + .cache_mode = 0, + .dis_cap_alloc = true, + .retain_on_pc = true, + }, { + .usecase_id = LLCC_GPU, + .slice_id = 12, + .max_cap = 384, + .priority = 1, + .fixed_size = true, + .bonus_ways = 0x0, + .res_ways = 0x0, + .cache_mode = 0, + .dis_cap_alloc = true, + .retain_on_pc = true, + }, { + .usecase_id = LLCC_MMUHWT, + .slice_id = 13, + .max_cap = 512, + .priority = 1, + .bonus_ways = 0xf, + .res_ways = 0x0, + .cache_mode = 0, + .dis_cap_alloc = true, + .activate_on_init = true, + }, { + .usecase_id = LLCC_AUDHW, + .slice_id = 22, + .max_cap = 512, + .priority = 1, + .fixed_size = true, + .bonus_ways = 0xf, + .res_ways = 0x0, + .cache_mode = 0, + .dis_cap_alloc = true, + .retain_on_pc = true, + }, +}; + static const struct llcc_slice_config sdm845_data[] = {{ .usecase_id = LLCC_CPUSS, .slice_id = 1, @@ -3943,7 +4030,7 @@ static const struct llcc_slice_config x1e80100_data[] = { static const struct llcc_edac_reg_offset llcc_v1_edac_reg_offset = { .trp_ecc_error_status0 = 0x20344, .trp_ecc_error_status1 = 0x20348, - .trp_ecc_sb_err_syn0 = 0x2304c, + .trp_ecc_sb_err_syn0 = 0x2034c, .trp_ecc_db_err_syn0 = 0x20370, .trp_ecc_error_cntr_clear = 0x20440, .trp_interrupt_0_status = 0x20480, @@ -4196,6 +4283,17 @@ static const struct qcom_llcc_config sc8280xp_cfg[] = { }, }; +static const struct qcom_llcc_config sdm670_cfg[] = { + { + .sct_data = sdm670_data, + .size = ARRAY_SIZE(sdm670_data), + .skip_llcc_cfg = true, + .reg_offset = llcc_v1_reg_offset, + .edac_reg_offset = &llcc_v1_edac_reg_offset, + .no_edac = true, + }, +}; + static const struct qcom_llcc_config sdm845_cfg[] = { { .sct_data = sdm845_data, @@ -4364,6 +4462,11 @@ static const struct qcom_sct_config sc8280xp_cfgs = { .num_config = ARRAY_SIZE(sc8280xp_cfg), }; +static const struct qcom_sct_config sdm670_cfgs = { + .llcc_config = sdm670_cfg, + .num_config = ARRAY_SIZE(sdm670_cfg), +}; + static const struct qcom_sct_config sdm845_cfgs = { .llcc_config = sdm845_cfg, .num_config = ARRAY_SIZE(sdm845_cfg), @@ -4431,8 +4534,7 @@ static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; struct llcc_slice_desc *llcc_slice_getd(u32 uid) { const struct llcc_slice_config *cfg; - struct llcc_slice_desc *desc; - u32 sz, count; + u32 sz, i; if (IS_ERR(drv_data)) return ERR_CAST(drv_data); @@ -4440,21 +4542,14 @@ struct llcc_slice_desc *llcc_slice_getd(u32 uid) cfg = drv_data->cfg; sz = drv_data->cfg_size; - for (count = 0; cfg && count < sz; count++, cfg++) + for (i = 0; cfg && i < sz; i++, cfg++) if (cfg->usecase_id == uid) break; - if (count == sz || !cfg) + if (i == sz) return ERR_PTR(-ENODEV); - desc = kzalloc_obj(*desc); - if (!desc) - return ERR_PTR(-ENOMEM); - - desc->slice_id = cfg->slice_id; - desc->slice_size = cfg->max_cap; - - return desc; + return &drv_data->desc[i]; } EXPORT_SYMBOL_GPL(llcc_slice_getd); @@ -4465,7 +4560,7 @@ EXPORT_SYMBOL_GPL(llcc_slice_getd); void llcc_slice_putd(struct llcc_slice_desc *desc) { if (!IS_ERR_OR_NULL(desc)) - kfree(desc); + return; } EXPORT_SYMBOL_GPL(llcc_slice_putd); @@ -4540,25 +4635,21 @@ int llcc_slice_activate(struct llcc_slice_desc *desc) if (IS_ERR_OR_NULL(desc)) return -EINVAL; - mutex_lock(&drv_data->lock); - if (test_bit(desc->slice_id, drv_data->bitmap)) { - mutex_unlock(&drv_data->lock); + guard(mutex)(&drv_data->lock); + /* Already active; try to take another reference. */ + if (refcount_inc_not_zero(&desc->refcount)) return 0; - } act_ctrl_val = ACT_CTRL_OPCODE_ACTIVATE << ACT_CTRL_OPCODE_SHIFT; - ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val, DEACTIVATE); - if (ret) { - mutex_unlock(&drv_data->lock); + if (ret) return ret; - } - __set_bit(desc->slice_id, drv_data->bitmap); - mutex_unlock(&drv_data->lock); + /* Set first reference */ + refcount_set(&desc->refcount, 1); - return ret; + return 0; } EXPORT_SYMBOL_GPL(llcc_slice_activate); @@ -4580,24 +4671,21 @@ int llcc_slice_deactivate(struct llcc_slice_desc *desc) if (IS_ERR_OR_NULL(desc)) return -EINVAL; - mutex_lock(&drv_data->lock); - if (!test_bit(desc->slice_id, drv_data->bitmap)) { - mutex_unlock(&drv_data->lock); + guard(mutex)(&drv_data->lock); + /* refcount > 1, drop one ref and we’re done. */ + if (refcount_dec_not_one(&desc->refcount)) return 0; - } - act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT; + act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT; ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val, ACTIVATE); - if (ret) { - mutex_unlock(&drv_data->lock); + if (ret) return ret; - } - __clear_bit(desc->slice_id, drv_data->bitmap); - mutex_unlock(&drv_data->lock); + /* Finalize: atomically transition 1 -> 0 */ + WARN_ON_ONCE(!refcount_dec_if_one(&desc->refcount)); - return ret; + return 0; } EXPORT_SYMBOL_GPL(llcc_slice_deactivate); @@ -4638,7 +4726,7 @@ static int _qcom_llcc_cfg_program(const struct llcc_slice_config *config, u32 attr1_val; u32 attr0_val; u32 max_cap_cacheline; - struct llcc_slice_desc desc; + struct llcc_slice_desc *desc; attr1_val = config->cache_mode; attr1_val |= config->probe_target_ways << ATTR1_PROBE_TARGET_WAYS_SHIFT; @@ -4787,8 +4875,11 @@ static int _qcom_llcc_cfg_program(const struct llcc_slice_config *config, } if (config->activate_on_init) { - desc.slice_id = config->slice_id; - ret = llcc_slice_activate(&desc); + desc = llcc_slice_getd(config->usecase_id); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + ret = llcc_slice_activate(desc); } return ret; @@ -5101,18 +5192,18 @@ static int qcom_llcc_probe(struct platform_device *pdev) llcc_cfg = cfg->sct_data; sz = cfg->size; - - for (i = 0; i < sz; i++) - if (llcc_cfg[i].slice_id > drv_data->max_slices) - drv_data->max_slices = llcc_cfg[i].slice_id; - - drv_data->bitmap = devm_bitmap_zalloc(dev, drv_data->max_slices, - GFP_KERNEL); - if (!drv_data->bitmap) { + drv_data->desc = devm_kcalloc(dev, sz, sizeof(struct llcc_slice_desc), GFP_KERNEL); + if (!drv_data->desc) { ret = -ENOMEM; goto err; } + for (i = 0; i < sz; i++) { + drv_data->desc[i].slice_id = llcc_cfg[i].slice_id; + drv_data->desc[i].slice_size = llcc_cfg[i].max_cap; + refcount_set(&drv_data->desc[i].refcount, 0); + } + drv_data->cfg = llcc_cfg; drv_data->cfg_size = sz; drv_data->edac_reg_offset = cfg->edac_reg_offset; @@ -5160,6 +5251,7 @@ static const struct of_device_id qcom_llcc_of_match[] = { { .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfgs }, { .compatible = "qcom,sc8180x-llcc", .data = &sc8180x_cfgs }, { .compatible = "qcom,sc8280xp-llcc", .data = &sc8280xp_cfgs }, + { .compatible = "qcom,sdm670-llcc", .data = &sdm670_cfgs }, { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfgs }, { .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfgs }, { .compatible = "qcom,sm7150-llcc", .data = &sm7150_cfgs }, diff --git a/drivers/soc/qcom/ocmem.c b/drivers/soc/qcom/ocmem.c index 6a23f18b0281..96ca0b87bfc4 100644 --- a/drivers/soc/qcom/ocmem.c +++ b/drivers/soc/qcom/ocmem.c @@ -196,17 +196,16 @@ struct ocmem *of_get_ocmem(struct device *dev) } pdev = of_find_device_by_node(devnode->parent); - if (!pdev) { - dev_err(dev, "Cannot find device node %s\n", devnode->name); - return ERR_PTR(-EPROBE_DEFER); - } + if (!pdev) + return dev_err_ptr_probe(dev, -EPROBE_DEFER, + "Cannot find device node %s\n", + devnode->name); ocmem = platform_get_drvdata(pdev); put_device(&pdev->dev); - if (!ocmem) { - dev_err(dev, "Cannot get ocmem\n"); - return ERR_PTR(-ENODEV); - } + if (!ocmem) + return dev_err_ptr_probe(dev, -EPROBE_DEFER, "Cannot get ocmem\n"); + return ocmem; } EXPORT_SYMBOL_GPL(of_get_ocmem); @@ -308,7 +307,7 @@ static int ocmem_dev_probe(struct platform_device *pdev) ocmem->dev = dev; ocmem->config = device_get_match_data(dev); - ocmem->core_clk = devm_clk_get(dev, "core"); + ocmem->core_clk = devm_clk_get_optional(dev, "core"); if (IS_ERR(ocmem->core_clk)) return dev_err_probe(dev, PTR_ERR(ocmem->core_clk), "Unable to get core clock\n"); diff --git a/drivers/soc/qcom/pdr_interface.c b/drivers/soc/qcom/pdr_interface.c index 72259f489075..6d879e1540b0 100644 --- a/drivers/soc/qcom/pdr_interface.c +++ b/drivers/soc/qcom/pdr_interface.c @@ -523,7 +523,7 @@ struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr, if (!pds) return ERR_PTR(-ENOMEM); - pds->service = SERVREG_NOTIFIER_SERVICE; + pds->service = QMI_SERVICE_ID_SERVREG_NOTIF; strscpy(pds->service_name, service_name, sizeof(pds->service_name)); strscpy(pds->service_path, service_path, sizeof(pds->service_path)); pds->need_locator_lookup = true; @@ -678,7 +678,7 @@ struct pdr_handle *pdr_handle_alloc(void (*status)(int state, if (ret < 0) goto destroy_indack; - ret = qmi_add_lookup(&pdr->locator_hdl, SERVREG_LOCATOR_SERVICE, 1, 1); + ret = qmi_add_lookup(&pdr->locator_hdl, QMI_SERVICE_ID_SERVREG_LOC, 1, 1); if (ret < 0) goto release_qmi_handle; diff --git a/drivers/soc/qcom/pdr_internal.h b/drivers/soc/qcom/pdr_internal.h index 047c0160b617..d867e9b5e973 100644 --- a/drivers/soc/qcom/pdr_internal.h +++ b/drivers/soc/qcom/pdr_internal.h @@ -4,9 +4,6 @@ #include -#define SERVREG_LOCATOR_SERVICE 0x40 -#define SERVREG_NOTIFIER_SERVICE 0x42 - #define SERVREG_REGISTER_LISTENER_REQ 0x20 #define SERVREG_GET_DOMAIN_LIST_REQ 0x21 #define SERVREG_STATE_UPDATED_IND_ID 0x22 diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c index 627f96ca322e..3042261578aa 100644 --- a/drivers/soc/qcom/pmic_glink.c +++ b/drivers/soc/qcom/pmic_glink.c @@ -23,13 +23,19 @@ enum { PMIC_GLINK_CLIENT_UCSI, }; +struct pmic_glink_data { + unsigned long client_mask; + const char *charger_pdr_service_name; + const char *charger_pdr_service_path; +}; + struct pmic_glink { struct device *dev; struct pdr_handle *pdr; struct rpmsg_endpoint *ept; - unsigned long client_mask; + const struct pmic_glink_data *data; struct auxiliary_device altmode_aux; struct auxiliary_device ps_aux; @@ -292,7 +298,6 @@ static struct rpmsg_driver pmic_glink_rpmsg_driver = { static int pmic_glink_probe(struct platform_device *pdev) { - const unsigned long *match_data; struct pdr_service *service; struct pmic_glink *pg; int ret; @@ -309,12 +314,10 @@ static int pmic_glink_probe(struct platform_device *pdev) spin_lock_init(&pg->client_lock); mutex_init(&pg->state_lock); - match_data = (unsigned long *)of_device_get_match_data(&pdev->dev); - if (!match_data) + pg->data = of_device_get_match_data(&pdev->dev); + if (!pg->data) return -EINVAL; - pg->client_mask = *match_data; - pg->pdr = pdr_handle_alloc(pmic_glink_pdr_callback, pg); if (IS_ERR(pg->pdr)) { ret = dev_err_probe(&pdev->dev, PTR_ERR(pg->pdr), @@ -322,27 +325,30 @@ static int pmic_glink_probe(struct platform_device *pdev) return ret; } - if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_UCSI)) { + if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_UCSI)) { ret = pmic_glink_add_aux_device(pg, &pg->ucsi_aux, "ucsi"); if (ret) goto out_release_pdr_handle; } - if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_ALTMODE)) { + if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_ALTMODE)) { ret = pmic_glink_add_aux_device(pg, &pg->altmode_aux, "altmode"); if (ret) goto out_release_ucsi_aux; } - if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_BATT)) { + if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_BATT)) { ret = pmic_glink_add_aux_device(pg, &pg->ps_aux, "power-supply"); if (ret) goto out_release_altmode_aux; } - service = pdr_add_lookup(pg->pdr, "tms/servreg", "msm/adsp/charger_pd"); - if (IS_ERR(service)) { - ret = dev_err_probe(&pdev->dev, PTR_ERR(service), - "failed adding pdr lookup for charger_pd\n"); - goto out_release_aux_devices; + if (pg->data->charger_pdr_service_name && pg->data->charger_pdr_service_path) { + service = pdr_add_lookup(pg->pdr, pg->data->charger_pdr_service_name, + pg->data->charger_pdr_service_path); + if (IS_ERR(service)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(service), + "failed adding pdr lookup for charger_pd\n"); + goto out_release_aux_devices; + } } mutex_lock(&__pmic_glink_lock); @@ -352,13 +358,13 @@ static int pmic_glink_probe(struct platform_device *pdev) return 0; out_release_aux_devices: - if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_BATT)) + if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_BATT)) pmic_glink_del_aux_device(pg, &pg->ps_aux); out_release_altmode_aux: - if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_ALTMODE)) + if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_ALTMODE)) pmic_glink_del_aux_device(pg, &pg->altmode_aux); out_release_ucsi_aux: - if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_UCSI)) + if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_UCSI)) pmic_glink_del_aux_device(pg, &pg->ucsi_aux); out_release_pdr_handle: pdr_handle_release(pg->pdr); @@ -372,23 +378,35 @@ static void pmic_glink_remove(struct platform_device *pdev) pdr_handle_release(pg->pdr); - if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_BATT)) + if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_BATT)) pmic_glink_del_aux_device(pg, &pg->ps_aux); - if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_ALTMODE)) + if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_ALTMODE)) pmic_glink_del_aux_device(pg, &pg->altmode_aux); - if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_UCSI)) + if (pg->data->client_mask & BIT(PMIC_GLINK_CLIENT_UCSI)) pmic_glink_del_aux_device(pg, &pg->ucsi_aux); guard(mutex)(&__pmic_glink_lock); __pmic_glink = NULL; } -static const unsigned long pmic_glink_sm8450_client_mask = BIT(PMIC_GLINK_CLIENT_BATT) | - BIT(PMIC_GLINK_CLIENT_ALTMODE) | - BIT(PMIC_GLINK_CLIENT_UCSI); +static const struct pmic_glink_data pmic_glink_adsp_data = { + .client_mask = BIT(PMIC_GLINK_CLIENT_BATT) | + BIT(PMIC_GLINK_CLIENT_ALTMODE) | + BIT(PMIC_GLINK_CLIENT_UCSI), + .charger_pdr_service_name = "tms/servreg", + .charger_pdr_service_path = "msm/adsp/charger_pd", +}; + +static const struct pmic_glink_data pmic_glink_soccp_data = { + .client_mask = BIT(PMIC_GLINK_CLIENT_BATT) | + BIT(PMIC_GLINK_CLIENT_ALTMODE) | + BIT(PMIC_GLINK_CLIENT_UCSI), +}; static const struct of_device_id pmic_glink_of_match[] = { - { .compatible = "qcom,pmic-glink", .data = &pmic_glink_sm8450_client_mask }, + { .compatible = "qcom,glymur-pmic-glink", .data = &pmic_glink_soccp_data }, + { .compatible = "qcom,kaanapali-pmic-glink", .data = &pmic_glink_soccp_data }, + { .compatible = "qcom,pmic-glink", .data = &pmic_glink_adsp_data }, {} }; MODULE_DEVICE_TABLE(of, pmic_glink_of_match); diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index a543ab9bee6c..c255662b8fc3 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -355,7 +355,7 @@ static int qmp_cdev_set_cur_state(struct thermal_cooling_device *cdev, /* Normalize state */ cdev_state = !!state; - if (qmp_cdev->state == state) + if (qmp_cdev->state == cdev_state) return 0; ret = qmp_send(qmp_cdev->qmp, "{class: volt_flr, event:zero_temp, res:%s, value:%s}", diff --git a/drivers/soc/qcom/qcom_pd_mapper.c b/drivers/soc/qcom/qcom_pd_mapper.c index dc10bc859ff4..7bb14c20ab5d 100644 --- a/drivers/soc/qcom/qcom_pd_mapper.c +++ b/drivers/soc/qcom/qcom_pd_mapper.c @@ -360,6 +360,14 @@ static const struct qcom_pdm_domain_data mpss_wlan_pd = { }, }; +static const struct qcom_pdm_domain_data *glymur_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &adsp_sensor_pd, + &cdsp_root_pd, + NULL, +}; + static const struct qcom_pdm_domain_data *kaanapali_domains[] = { &adsp_audio_pd, &adsp_root_pd, @@ -401,6 +409,16 @@ static const struct qcom_pdm_domain_data *qcs404_domains[] = { NULL, }; +static const struct qcom_pdm_domain_data *qcs615_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &adsp_sensor_pd, + &cdsp_root_pd, + &mpss_root_pd, + &mpss_wlan_pd, + NULL, +}; + static const struct qcom_pdm_domain_data *sc7180_domains[] = { &adsp_audio_pd, &adsp_root_pd_pdr, @@ -560,8 +578,12 @@ static const struct of_device_id qcom_pdm_domains[] __maybe_unused = { { .compatible = "qcom,apq8064", .data = NULL, }, { .compatible = "qcom,apq8074", .data = NULL, }, { .compatible = "qcom,apq8084", .data = NULL, }, + { .compatible = "qcom,eliza", .data = sm8550_domains, }, { .compatible = "qcom,apq8096", .data = msm8996_domains, }, + { .compatible = "qcom,glymur", .data = glymur_domains, }, { .compatible = "qcom,kaanapali", .data = kaanapali_domains, }, + { .compatible = "qcom,mahua", .data = glymur_domains, }, + { .compatible = "qcom,milos", .data = sm8550_domains, }, { .compatible = "qcom,msm8226", .data = NULL, }, { .compatible = "qcom,msm8909", .data = NULL, }, { .compatible = "qcom,msm8916", .data = NULL, }, @@ -572,6 +594,7 @@ static const struct of_device_id qcom_pdm_domains[] __maybe_unused = { { .compatible = "qcom,qcm2290", .data = qcm2290_domains, }, { .compatible = "qcom,qcm6490", .data = sc7280_domains, }, { .compatible = "qcom,qcs404", .data = qcs404_domains, }, + { .compatible = "qcom,qcs615", .data = qcs615_domains, }, { .compatible = "qcom,sc7180", .data = sc7180_domains, }, { .compatible = "qcom,sc7280", .data = sc7280_domains, }, { .compatible = "qcom,sc8180x", .data = sc8180x_domains, }, @@ -615,15 +638,9 @@ static struct qcom_pdm_data *qcom_pdm_start(void) const struct qcom_pdm_domain_data * const *domains; const struct of_device_id *match; struct qcom_pdm_data *data; - struct device_node *root; int ret, i; - root = of_find_node_by_path("/"); - if (!root) - return ERR_PTR(-ENODEV); - - match = of_match_node(qcom_pdm_domains, root); - of_node_put(root); + match = of_machine_get_match(qcom_pdm_domains); if (!match) { pr_notice("PDM: no support for the platform, userspace daemon might be required.\n"); return ERR_PTR(-ENODEV); @@ -656,7 +673,7 @@ static struct qcom_pdm_data *qcom_pdm_start(void) goto err_stop; } - ret = qmi_add_server(&data->handle, SERVREG_LOCATOR_SERVICE, + ret = qmi_add_server(&data->handle, QMI_SERVICE_ID_SERVREG_LOC, SERVREG_QMI_VERSION, SERVREG_QMI_INSTANCE); if (ret) { pr_err("PDM: error adding server %d\n", ret); diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index cb515c2340c1..af0ceeaf6e07 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -36,6 +36,10 @@ * The driver uses the Linux GPIO and interrupt framework to expose a virtual * GPIO for each outbound entry and a virtual interrupt controller for each * inbound entry. + * + * V2 of SMP2P allows remote processors to write to outbound smp2p items before + * the full smp2p connection is negotiated. This is important for processors + * started before linux runs. */ #define SMP2P_MAX_ENTRY 16 @@ -47,11 +51,12 @@ #define SMP2P_MAGIC 0x504d5324 #define SMP2P_ALL_FEATURES SMP2P_FEATURE_SSR_ACK +#define MAX_VERSION 2 /** * struct smp2p_smem_item - in memory communication structure * @magic: magic number - * @version: version - must be 1 + * @version: version * @features: features flag - currently unused * @local_pid: processor id of sending end * @remote_pid: processor id of receiving end @@ -180,14 +185,22 @@ static void qcom_smp2p_kick(struct qcom_smp2p *smp2p) static bool qcom_smp2p_check_ssr(struct qcom_smp2p *smp2p) { struct smp2p_smem_item *in = smp2p->in; + struct smp2p_entry *entry; + bool restart_done; bool restart; if (!smp2p->ssr_ack_enabled) return false; - restart = in->flags & BIT(SMP2P_FLAGS_RESTART_DONE_BIT); + restart_done = in->flags & BIT(SMP2P_FLAGS_RESTART_DONE_BIT); + restart = restart_done != smp2p->ssr_ack; + list_for_each_entry(entry, &smp2p->inbound, node) { + if (!entry->value) + continue; + entry->last_value = 0; + } - return restart != smp2p->ssr_ack; + return restart; } static void qcom_smp2p_do_ssr_ack(struct qcom_smp2p *smp2p) @@ -219,9 +232,59 @@ static void qcom_smp2p_negotiate(struct qcom_smp2p *smp2p) smp2p->negotiation_done = true; trace_smp2p_negotiate(smp2p->dev, out->features); + } else if (in->version && in->version < out->version) { + out->version = in->version; + qcom_smp2p_kick(smp2p); } } +static int qcom_smp2p_in_version(struct qcom_smp2p *smp2p) +{ + unsigned int smem_id = smp2p->smem_items[SMP2P_INBOUND]; + unsigned int pid = smp2p->remote_pid; + struct smp2p_smem_item *in; + size_t size; + + in = qcom_smem_get(pid, smem_id, &size); + if (IS_ERR(in)) + return 0; + + return in->version; +} + +static void qcom_smp2p_start_in(struct qcom_smp2p *smp2p) +{ + unsigned int smem_id = smp2p->smem_items[SMP2P_INBOUND]; + unsigned int pid = smp2p->remote_pid; + char buf[SMP2P_MAX_ENTRY_NAME]; + struct smp2p_smem_item *in; + struct smp2p_entry *entry; + size_t size; + int i; + + in = qcom_smem_get(pid, smem_id, &size); + if (IS_ERR(in)) + return; + + smp2p->in = in; + + /* Check if version is initialized by the remote. */ + if (in->version == 0) + return; + + for (i = smp2p->valid_entries; i < in->valid_entries; i++) { + list_for_each_entry(entry, &smp2p->inbound, node) { + memcpy(buf, in->entries[i].name, sizeof(buf)); + if (!strcmp(buf, entry->name)) { + entry->value = &in->entries[i].value; + entry->last_value = readl(entry->value); + break; + } + } + } + smp2p->valid_entries = i; +} + static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p) { struct smp2p_smem_item *in; @@ -368,12 +431,31 @@ static void smp2p_irq_print_chip(struct irq_data *irqd, struct seq_file *p) seq_printf(p, "%8s", dev_name(entry->smp2p->dev)); } +static int smp2p_irq_get_irqchip_state(struct irq_data *irqd, enum irqchip_irq_state which, + bool *state) +{ + struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd); + u32 val; + + if (which != IRQCHIP_STATE_LINE_LEVEL) + return -EINVAL; + + if (!entry->value) + return -ENODEV; + + val = readl(entry->value); + *state = !!(val & BIT(irqd_to_hwirq(irqd))); + + return 0; +} + static struct irq_chip smp2p_irq_chip = { .name = "smp2p", .irq_mask = smp2p_mask_irq, .irq_unmask = smp2p_unmask_irq, .irq_set_type = smp2p_set_irq_type, .irq_print_chip = smp2p_irq_print_chip, + .irq_get_irqchip_state = smp2p_irq_get_irqchip_state, }; static int smp2p_irq_map(struct irq_domain *d, @@ -464,6 +546,7 @@ static int qcom_smp2p_alloc_outbound_item(struct qcom_smp2p *smp2p) struct smp2p_smem_item *out; unsigned smem_id = smp2p->smem_items[SMP2P_OUTBOUND]; unsigned pid = smp2p->remote_pid; + u8 in_version; int ret; ret = qcom_smem_alloc(pid, smem_id, sizeof(*out)); @@ -485,12 +568,21 @@ static int qcom_smp2p_alloc_outbound_item(struct qcom_smp2p *smp2p) out->valid_entries = 0; out->features = SMP2P_ALL_FEATURES; + in_version = qcom_smp2p_in_version(smp2p); + if (in_version > MAX_VERSION) { + dev_err(smp2p->dev, "Unsupported smp2p version %d\n", in_version); + return -EINVAL; + } + /* * Make sure the rest of the header is written before we validate the * item by writing a valid version number. */ wmb(); - out->version = 1; + if (in_version && in_version <= 2) + out->version = in_version; + else + out->version = 2; qcom_smp2p_kick(smp2p); @@ -618,6 +710,9 @@ static int qcom_smp2p_probe(struct platform_device *pdev) } } + /* Check inbound entries in the case of early boot processor */ + qcom_smp2p_start_in(smp2p); + /* Kick the outgoing edge after allocating entries */ qcom_smp2p_kick(smp2p); diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 003a2304d535..8ffd903ebddb 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -182,6 +182,7 @@ static const char *const pmic_models[] = { [72] = "PMR735D", [73] = "PM8550", [74] = "PMK8550", + [76] = "PM7550BA", [78] = "PMM8650AU", [79] = "PMM8650AU_PSAIL", [80] = "PM7550", @@ -473,6 +474,7 @@ static const struct soc_id soc_id[] = { { qcom_board_id(IPQ5000) }, { qcom_board_id(IPQ0509) }, { qcom_board_id(IPQ0518) }, + { qcom_board_id(SM7450) }, { qcom_board_id(SM6375) }, { qcom_board_id(IPQ9514) }, { qcom_board_id(IPQ9550) }, @@ -488,10 +490,12 @@ static const struct soc_id soc_id[] = { { qcom_board_id(SM8475) }, { qcom_board_id(SM8475P) }, { qcom_board_id(SA8255P) }, + { qcom_board_id(SA8650P) }, { qcom_board_id(SA8775P) }, { qcom_board_id(QRU1000) }, { qcom_board_id(SM8475_2) }, { qcom_board_id(QDU1000) }, + { qcom_board_id(SM7450P) }, { qcom_board_id(X1E80100) }, { qcom_board_id(SM8650) }, { qcom_board_id(SM4450) }, @@ -522,6 +526,13 @@ static const struct soc_id soc_id[] = { { qcom_board_id(QCS8275) }, { qcom_board_id(QCS9075) }, { qcom_board_id(QCS615) }, + { qcom_board_id(CQ7790M) }, + { qcom_board_id(CQ7790S) }, + { qcom_board_id(IPQ5200) }, + { qcom_board_id(IPQ5210) }, + { qcom_board_id(QCF2200) }, + { qcom_board_id(QCF3200) }, + { qcom_board_id(QCF3210) }, }; static const char *socinfo_machine(struct device *dev, unsigned int id) diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 1c25aaf55e52..3fe47d8f0f63 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -16,6 +16,17 @@ static const struct qcom_ubwc_cfg_data no_ubwc_data = { /* no UBWC, no HBB */ }; +static const struct qcom_ubwc_cfg_data eliza_data = { + .ubwc_enc_version = UBWC_5_0, + .ubwc_dec_version = UBWC_5_0, + .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | + UBWC_SWIZZLE_ENABLE_LVL3, + .ubwc_bank_spread = true, + /* TODO: highest_bank_bit = 14 for LP_DDR4 */ + .highest_bank_bit = 15, + .macrotile_mode = true, +}; + static const struct qcom_ubwc_cfg_data kaanapali_data = { .ubwc_enc_version = UBWC_6_0, .ubwc_dec_version = UBWC_6_0, @@ -217,22 +228,10 @@ static const struct qcom_ubwc_cfg_data sm8750_data = { .macrotile_mode = true, }; -static const struct qcom_ubwc_cfg_data x1e80100_data = { - .ubwc_enc_version = UBWC_4_0, - .ubwc_dec_version = UBWC_4_3, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, - /* TODO: highest_bank_bit = 15 for LP_DDR4 */ - .highest_bank_bit = 16, - .macrotile_mode = true, -}; - static const struct qcom_ubwc_cfg_data glymur_data = { .ubwc_enc_version = UBWC_5_0, .ubwc_dec_version = UBWC_5_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, + .ubwc_swizzle = 0, .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, @@ -244,8 +243,10 @@ static const struct of_device_id qcom_ubwc_configs[] __maybe_unused = { { .compatible = "qcom,apq8026", .data = &no_ubwc_data }, { .compatible = "qcom,apq8074", .data = &no_ubwc_data }, { .compatible = "qcom,apq8096", .data = &msm8998_data }, + { .compatible = "qcom,eliza", .data = &eliza_data, }, { .compatible = "qcom,kaanapali", .data = &kaanapali_data, }, { .compatible = "qcom,glymur", .data = &glymur_data}, + { .compatible = "qcom,mahua", .data = &glymur_data }, { .compatible = "qcom,msm8226", .data = &no_ubwc_data }, { .compatible = "qcom,msm8916", .data = &no_ubwc_data }, { .compatible = "qcom,msm8917", .data = &no_ubwc_data }, @@ -294,8 +295,8 @@ static const struct of_device_id qcom_ubwc_configs[] __maybe_unused = { { .compatible = "qcom,sm8550", .data = &sm8550_data, }, { .compatible = "qcom,sm8650", .data = &sm8550_data, }, { .compatible = "qcom,sm8750", .data = &sm8750_data, }, - { .compatible = "qcom,x1e80100", .data = &x1e80100_data, }, - { .compatible = "qcom,x1p42100", .data = &x1e80100_data, }, + { .compatible = "qcom,x1e80100", .data = &sm8550_data, }, + { .compatible = "qcom,x1p42100", .data = &sm8550_data, }, { } }; diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c index 62b424e90d90..ffb31a049d4a 100644 --- a/drivers/soc/qcom/wcnss_ctrl.c +++ b/drivers/soc/qcom/wcnss_ctrl.c @@ -94,7 +94,7 @@ struct wcnss_download_nv_req { u16 seq; u16 last; u32 frag_size; - u8 fragment[]; + u8 fragment[] __counted_by(frag_size); } __packed; /** @@ -201,16 +201,12 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc) { const struct firmware *fw; struct device *dev = wcnss->dev; + struct wcnss_download_nv_req *req; const char *nvbin = NVBIN_FILE; const void *data; ssize_t left; int ret; - struct wcnss_download_nv_req *req __free(kfree) = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, - GFP_KERNEL); - if (!req) - return -ENOMEM; - ret = of_property_read_string(dev->of_node, "firmware-name", &nvbin); if (ret < 0 && ret != -EINVAL) return ret; @@ -224,11 +220,15 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc) data = fw->data; left = fw->size; + req = kzalloc_flex(*req, fragment, NV_FRAGMENT_SIZE); + if (!req) + return -ENOMEM; + + req->frag_size = NV_FRAGMENT_SIZE; req->hdr.type = WCNSS_DOWNLOAD_NV_REQ; - req->hdr.len = sizeof(*req) + NV_FRAGMENT_SIZE; + req->hdr.len = struct_size(req, fragment, NV_FRAGMENT_SIZE); req->last = 0; - req->frag_size = NV_FRAGMENT_SIZE; req->seq = 0; do { @@ -264,6 +264,7 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc) release_fw: release_firmware(fw); + kfree(req); return ret; } diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 1e50dc7c31cd..26bed0fdceb0 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -390,6 +390,14 @@ config ARCH_R9A08G045 help This enables support for the Renesas RZ/G3S SoC variants. +config ARCH_R9A08G046 + bool "ARM64 Platform support for R9A08G046 (RZ/G3L)" + default y if ARCH_RENESAS + select ARCH_RZG2L + select SYSC_R9A08G046 + help + This enables support for the Renesas RZ/G3L SoC variants. + config ARCH_R9A09G011 bool "ARM64 Platform support for R9A09G011 (RZ/V2M)" default y if ARCH_RENESAS @@ -474,6 +482,10 @@ config SYSC_R9A08G045 bool "Renesas System controller support for R9A08G045 (RZ/G3S)" if COMPILE_TEST select SYSC_RZ +config SYSC_R9A08G046 + bool "Renesas System controller support for R9A08G046 (RZ/G3L)" if COMPILE_TEST + select SYSC_RZ + config SYS_R9A09G047 bool "Renesas System controller support for R9A09G047 (RZ/G3E)" if COMPILE_TEST select SYSC_RZ diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index 33d44d964d61..655dbcb08747 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -7,6 +7,7 @@ ifdef CONFIG_SMP obj-$(CONFIG_ARCH_R9A06G032) += r9a06g032-smp.o endif obj-$(CONFIG_SYSC_R9A08G045) += r9a08g045-sysc.o +obj-$(CONFIG_SYSC_R9A08G046) += r9a08g046-sysc.o obj-$(CONFIG_SYS_R9A09G047) += r9a09g047-sys.o obj-$(CONFIG_SYS_R9A09G056) += r9a09g056-sys.o obj-$(CONFIG_SYS_R9A09G057) += r9a09g057-sys.o diff --git a/drivers/soc/renesas/r9a08g046-sysc.c b/drivers/soc/renesas/r9a08g046-sysc.c new file mode 100644 index 000000000000..fd98df196d0a --- /dev/null +++ b/drivers/soc/renesas/r9a08g046-sysc.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G3L System controller (SYSC) driver + * + * Copyright (C) 2026 Renesas Electronics Corp. + */ + +#include +#include +#include + +#include "rz-sysc.h" + +#define SYS_XSPI_MAP_STAADD_CS0 0x348 +#define SYS_XSPI_MAP_ENDADD_CS0 0x34c +#define SYS_XSPI_MAP_STAADD_CS1 0x350 +#define SYS_XSPI_MAP_ENDADD_CS1 0x354 +#define SYS_GETH0_CFG 0x380 +#define SYS_GETH1_CFG 0x390 +#define SYS_PCIE_CFG 0x3a0 +#define SYS_PCIE_MON 0x3a4 +#define SYS_PCIE_PHY 0x3b4 +#define SYS_I2C0_CFG 0x400 +#define SYS_I2C1_CFG 0x410 +#define SYS_I2C2_CFG 0x420 +#define SYS_I2C3_CFG 0x430 +#define SYS_I3C_CFG 0x440 +#define SYS_PWRRDY_N 0xd70 +#define SYS_IPCONT_SEL_CLONECH 0xe2c + +static bool rzg3l_regmap_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SYS_XSPI_MAP_STAADD_CS0: + case SYS_XSPI_MAP_ENDADD_CS0: + case SYS_XSPI_MAP_STAADD_CS1: + case SYS_XSPI_MAP_ENDADD_CS1: + case SYS_GETH0_CFG: + case SYS_GETH1_CFG: + case SYS_PCIE_CFG: + case SYS_PCIE_MON: + case SYS_PCIE_PHY: + case SYS_I2C0_CFG: + case SYS_I2C1_CFG: + case SYS_I2C2_CFG: + case SYS_I2C3_CFG: + case SYS_I3C_CFG: + case SYS_PWRRDY_N: + case SYS_IPCONT_SEL_CLONECH: + return true; + default: + return false; + } +} + +static bool rzg3l_regmap_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SYS_XSPI_MAP_STAADD_CS0: + case SYS_XSPI_MAP_ENDADD_CS0: + case SYS_XSPI_MAP_STAADD_CS1: + case SYS_XSPI_MAP_ENDADD_CS1: + case SYS_PCIE_CFG: + case SYS_PCIE_PHY: + case SYS_I2C0_CFG: + case SYS_I2C1_CFG: + case SYS_I2C2_CFG: + case SYS_I2C3_CFG: + case SYS_I3C_CFG: + case SYS_PWRRDY_N: + case SYS_IPCONT_SEL_CLONECH: + return true; + default: + return false; + } +} + +static const struct rz_sysc_soc_id_init_data rzg3l_sysc_soc_id_init_data __initconst = { + .family = "RZ/G3L", + .id = 0x87d9447, + .devid_offset = 0xa04, + .revision_mask = GENMASK(31, 28), + .specific_id_mask = GENMASK(27, 0), +}; + +const struct rz_sysc_init_data rzg3l_sysc_init_data __initconst = { + .soc_id_init_data = &rzg3l_sysc_soc_id_init_data, + .readable_reg = rzg3l_regmap_readable_reg, + .writeable_reg = rzg3l_regmap_writeable_reg, + .max_register = 0xe2c, +}; diff --git a/drivers/soc/renesas/r9a09g047-sys.c b/drivers/soc/renesas/r9a09g047-sys.c index e413b0eff9bf..ea3ca10fcc33 100644 --- a/drivers/soc/renesas/r9a09g047-sys.c +++ b/drivers/soc/renesas/r9a09g047-sys.c @@ -139,7 +139,7 @@ static bool rzg3e_regmap_writeable_reg(struct device *dev, unsigned int reg) } } -const struct rz_sysc_init_data rzg3e_sys_init_data = { +const struct rz_sysc_init_data rzg3e_sys_init_data __initconst = { .soc_id_init_data = &rzg3e_sys_soc_id_init_data, .readable_reg = rzg3e_regmap_readable_reg, .writeable_reg = rzg3e_regmap_writeable_reg, diff --git a/drivers/soc/renesas/r9a09g056-sys.c b/drivers/soc/renesas/r9a09g056-sys.c index 42f5eff291fd..2a8ebc209961 100644 --- a/drivers/soc/renesas/r9a09g056-sys.c +++ b/drivers/soc/renesas/r9a09g056-sys.c @@ -136,7 +136,7 @@ static bool rzv2n_regmap_writeable_reg(struct device *dev, unsigned int reg) } } -const struct rz_sysc_init_data rzv2n_sys_init_data = { +const struct rz_sysc_init_data rzv2n_sys_init_data __initconst = { .soc_id_init_data = &rzv2n_sys_soc_id_init_data, .readable_reg = rzv2n_regmap_readable_reg, .writeable_reg = rzv2n_regmap_writeable_reg, diff --git a/drivers/soc/renesas/r9a09g057-sys.c b/drivers/soc/renesas/r9a09g057-sys.c index 827c718ac7c5..f3e054206acb 100644 --- a/drivers/soc/renesas/r9a09g057-sys.c +++ b/drivers/soc/renesas/r9a09g057-sys.c @@ -161,7 +161,7 @@ static bool rzv2h_regmap_writeable_reg(struct device *dev, unsigned int reg) } } -const struct rz_sysc_init_data rzv2h_sys_init_data = { +const struct rz_sysc_init_data rzv2h_sys_init_data __initconst = { .soc_id_init_data = &rzv2h_sys_soc_id_init_data, .readable_reg = rzv2h_regmap_readable_reg, .writeable_reg = rzv2h_regmap_writeable_reg, diff --git a/drivers/soc/renesas/rz-sysc.c b/drivers/soc/renesas/rz-sysc.c index 7471dc8736e6..161e8c38eea6 100644 --- a/drivers/soc/renesas/rz-sysc.c +++ b/drivers/soc/renesas/rz-sysc.c @@ -88,6 +88,9 @@ static const struct of_device_id rz_sysc_match[] = { #ifdef CONFIG_SYSC_R9A08G045 { .compatible = "renesas,r9a08g045-sysc", .data = &rzg3s_sysc_init_data }, #endif +#ifdef CONFIG_SYSC_R9A08G046 + { .compatible = "renesas,r9a08g046-sysc", .data = &rzg3l_sysc_init_data }, +#endif #ifdef CONFIG_SYS_R9A09G047 { .compatible = "renesas,r9a09g047-sys", .data = &rzg3e_sys_init_data }, #endif diff --git a/drivers/soc/renesas/rz-sysc.h b/drivers/soc/renesas/rz-sysc.h index 88929bf21cb1..921ee0d26c47 100644 --- a/drivers/soc/renesas/rz-sysc.h +++ b/drivers/soc/renesas/rz-sysc.h @@ -46,6 +46,7 @@ struct rz_sysc_init_data { }; extern const struct rz_sysc_init_data rzg3e_sys_init_data; +extern const struct rz_sysc_init_data rzg3l_sysc_init_data; extern const struct rz_sysc_init_data rzg3s_sysc_init_data; extern const struct rz_sysc_init_data rzv2h_sys_init_data; extern const struct rz_sysc_init_data rzv2n_sys_init_data; diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index c0fc54c3cd35..073346c1542b 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -6,6 +6,7 @@ if ARM config ARCH_TEGRA_2x_SOC bool "Enable support for Tegra20 family" + default ARCH_TEGRA select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP select ARM_ERRATA_720789 select ARM_ERRATA_754327 if SMP @@ -23,6 +24,7 @@ config ARCH_TEGRA_2x_SOC config ARCH_TEGRA_3x_SOC bool "Enable support for Tegra30 family" + default ARCH_TEGRA select ARM_ERRATA_754322 select ARM_ERRATA_764369 if SMP select PINCTRL_TEGRA30 @@ -37,6 +39,7 @@ config ARCH_TEGRA_3x_SOC config ARCH_TEGRA_114_SOC bool "Enable support for Tegra114 family" + default ARCH_TEGRA select ARM_ERRATA_798181 if SMP select HAVE_ARM_ARCH_TIMER select PINCTRL_TEGRA114 @@ -49,6 +52,7 @@ config ARCH_TEGRA_114_SOC config ARCH_TEGRA_124_SOC bool "Enable support for Tegra124 family" + default ARCH_TEGRA select HAVE_ARM_ARCH_TIMER select PINCTRL_TEGRA124 select SOC_TEGRA_FLOWCTRL @@ -65,6 +69,7 @@ if ARM64 config ARCH_TEGRA_132_SOC bool "NVIDIA Tegra132 SoC" + default ARCH_TEGRA select PINCTRL_TEGRA124 select SOC_TEGRA_FLOWCTRL select SOC_TEGRA_PMC @@ -76,6 +81,7 @@ config ARCH_TEGRA_132_SOC config ARCH_TEGRA_210_SOC bool "NVIDIA Tegra210 SoC" + default ARCH_TEGRA select PINCTRL_TEGRA210 select SOC_TEGRA_FLOWCTRL select SOC_TEGRA_PMC @@ -95,6 +101,7 @@ config ARCH_TEGRA_210_SOC config ARCH_TEGRA_186_SOC bool "NVIDIA Tegra186 SoC" + default ARCH_TEGRA depends on !CPU_BIG_ENDIAN select PINCTRL_TEGRA186 select MAILBOX @@ -109,6 +116,7 @@ config ARCH_TEGRA_186_SOC config ARCH_TEGRA_194_SOC bool "NVIDIA Tegra194 SoC" + default ARCH_TEGRA depends on !CPU_BIG_ENDIAN select MAILBOX select PINCTRL_TEGRA194 @@ -118,6 +126,7 @@ config ARCH_TEGRA_194_SOC config ARCH_TEGRA_234_SOC bool "NVIDIA Tegra234 SoC" + default ARCH_TEGRA depends on !CPU_BIG_ENDIAN select MAILBOX select PINCTRL_TEGRA234 @@ -125,13 +134,24 @@ config ARCH_TEGRA_234_SOC help Enable support for the NVIDIA Tegra234 SoC. +config ARCH_TEGRA_238_SOC + bool "NVIDIA Tegra238 SoC" + default ARCH_TEGRA + depends on !CPU_BIG_ENDIAN + select MAILBOX + select SOC_TEGRA_PMC + help + Enable support for the NVIDIA Tegra238 SoC. + config ARCH_TEGRA_241_SOC bool "NVIDIA Tegra241 SoC" + default ARCH_TEGRA help Enable support for the NVIDIA Tegra241 SoC. config ARCH_TEGRA_264_SOC bool "NVIDIA Tegra264 SoC" + default ARCH_TEGRA depends on !CPU_BIG_ENDIAN select MAILBOX select SOC_TEGRA_PMC diff --git a/drivers/soc/tegra/cbb/tegra234-cbb.c b/drivers/soc/tegra/cbb/tegra234-cbb.c index a9adbcecd47c..fb26f085f691 100644 --- a/drivers/soc/tegra/cbb/tegra234-cbb.c +++ b/drivers/soc/tegra/cbb/tegra234-cbb.c @@ -89,6 +89,15 @@ enum tegra234_cbb_fabric_ids { T234_MAX_FABRIC_ID, }; +enum tegra238_cbb_fabric_ids { + T238_CBB_FABRIC_ID = 0, + T238_AON_FABRIC_ID = 4, + T238_PSC_FABRIC_ID = 5, + T238_BPMP_FABRIC_ID = 6, + T238_APE_FABRIC_ID = 7, + T238_MAX_FABRIC_ID, +}; + enum tegra264_cbb_fabric_ids { T264_SYSTEM_CBB_FABRIC_ID, T264_TOP_0_CBB_FABRIC_ID, @@ -313,12 +322,37 @@ static void tegra234_cbb_lookup_apbslv(struct seq_file *file, const char *target } } +static struct tegra234_cbb *tegra234_cbb_get_fabric(u8 fab_id) +{ + struct tegra_cbb *entry; + + list_for_each_entry(entry, &cbb_list, node) { + struct tegra234_cbb *priv = to_tegra234_cbb(entry); + + if (priv->fabric->fab_id == fab_id) + return priv; + } + + return NULL; +} + static void tegra234_sw_lookup_target_timeout(struct seq_file *file, struct tegra234_cbb *cbb, u8 target_id, u8 fab_id) { const struct tegra234_target_lookup *map = cbb->fabric->fab_list[fab_id].target_map; + struct tegra234_cbb *target_cbb = NULL; void __iomem *addr; + if (fab_id == cbb->fabric->fab_id) + target_cbb = cbb; + else + target_cbb = tegra234_cbb_get_fabric(fab_id); + + if (!target_cbb) { + dev_err(cbb->base.dev, "could not find fabric for fab_id:%d\n", fab_id); + return; + } + if (target_id >= cbb->fabric->fab_list[fab_id].max_targets) { tegra_cbb_print_err(file, "\t Invalid target_id:%d\n", target_id); return; @@ -341,7 +375,7 @@ static void tegra234_sw_lookup_target_timeout(struct seq_file *file, struct tegr * e) Goto step-a till all bits are set. */ - addr = cbb->regs + map[target_id].offset; + addr = target_cbb->regs + map[target_id].offset; if (strstr(map[target_id].name, "AXI2APB")) { addr += APB_BLOCK_TMO_STATUS_0; @@ -881,7 +915,7 @@ static const struct tegra234_fabric_lookup tegra234_cbb_fab_list[] = { ARRAY_SIZE(tegra234_common_target_map) }, [T234_AON_FABRIC_ID] = { "aon-fabric", true, tegra234_aon_target_map, - ARRAY_SIZE(tegra234_bpmp_target_map) }, + ARRAY_SIZE(tegra234_aon_target_map) }, [T234_PSC_FABRIC_ID] = { "psc-fabric" }, [T234_BPMP_FABRIC_ID] = { "bpmp-fabric", true, tegra234_bpmp_target_map, @@ -974,6 +1008,127 @@ static const struct tegra234_cbb_fabric tegra234_sce_fabric = { .firewall_wr_ctl = 0x288, }; +static const struct tegra234_target_lookup tegra238_ape_target_map[] = { + { "AXI2APB", 0x00000 }, + { "AGIC", 0x15000 }, + { "AMC", 0x16000 }, + { "AST0", 0x17000 }, + { "AST1", 0x18000 }, + { "AST2", 0x19000 }, + { "CBB", 0x1A000 }, +}; + +static const struct tegra234_target_lookup tegra238_cbb_target_map[] = { + { "AON", 0x40000 }, + { "APE", 0x50000 }, + { "BPMP", 0x41000 }, + { "HOST1X", 0x43000 }, + { "STM", 0x44000 }, + { "CBB_CENTRAL", 0x00000 }, + { "PCIE_C0", 0x51000 }, + { "PCIE_C1", 0x47000 }, + { "PCIE_C2", 0x48000 }, + { "PCIE_C3", 0x49000 }, + { "GPU", 0x4C000 }, + { "SMMU0", 0x4D000 }, + { "SMMU1", 0x4E000 }, + { "SMMU2", 0x4F000 }, + { "PSC", 0x52000 }, + { "AXI2APB_1", 0x70000 }, + { "AXI2APB_12", 0x73000 }, + { "AXI2APB_13", 0x74000 }, + { "AXI2APB_15", 0x76000 }, + { "AXI2APB_16", 0x77000 }, + { "AXI2APB_18", 0x79000 }, + { "AXI2APB_19", 0x7A000 }, + { "AXI2APB_2", 0x7B000 }, + { "AXI2APB_23", 0x7F000 }, + { "AXI2APB_25", 0x80000 }, + { "AXI2APB_26", 0x81000 }, + { "AXI2APB_27", 0x82000 }, + { "AXI2APB_28", 0x83000 }, + { "AXI2APB_32", 0x87000 }, + { "AXI2APB_33", 0x88000 }, + { "AXI2APB_4", 0x8B000 }, + { "AXI2APB_5", 0x8C000 }, + { "AXI2APB_6", 0x93000 }, + { "AXI2APB_9", 0x90000 }, + { "AXI2APB_3", 0x91000 }, +}; + +static const struct tegra234_fabric_lookup tegra238_cbb_fab_list[] = { + [T238_CBB_FABRIC_ID] = { "cbb-fabric", true, + tegra238_cbb_target_map, + ARRAY_SIZE(tegra238_cbb_target_map) }, + [T238_AON_FABRIC_ID] = { "aon-fabric", true, + tegra234_aon_target_map, + ARRAY_SIZE(tegra234_aon_target_map) }, + [T238_PSC_FABRIC_ID] = { "psc-fabric" }, + [T238_BPMP_FABRIC_ID] = { "bpmp-fabric", true, + tegra234_bpmp_target_map, + ARRAY_SIZE(tegra234_bpmp_target_map) }, + [T238_APE_FABRIC_ID] = { "ape-fabric", true, + tegra238_ape_target_map, + ARRAY_SIZE(tegra238_ape_target_map) }, +}; + +static const struct tegra234_cbb_fabric tegra238_aon_fabric = { + .fab_id = T238_AON_FABRIC_ID, + .fab_list = tegra238_cbb_fab_list, + .initiator_id = tegra234_initiator_id, + .errors = tegra234_cbb_errors, + .max_errors = ARRAY_SIZE(tegra234_cbb_errors), + .err_intr_enbl = 0x7, + .err_status_clr = 0x3f, + .notifier_offset = 0x17000, + .firewall_base = 0x30000, + .firewall_ctl = 0x8f0, + .firewall_wr_ctl = 0x8e8, +}; + +static const struct tegra234_cbb_fabric tegra238_ape_fabric = { + .fab_id = T238_APE_FABRIC_ID, + .fab_list = tegra238_cbb_fab_list, + .initiator_id = tegra234_initiator_id, + .errors = tegra234_cbb_errors, + .max_errors = ARRAY_SIZE(tegra234_cbb_errors), + .err_intr_enbl = 0xf, + .err_status_clr = 0x3f, + .notifier_offset = 0x1E000, + .firewall_base = 0x30000, + .firewall_ctl = 0xad0, + .firewall_wr_ctl = 0xac8, +}; + +static const struct tegra234_cbb_fabric tegra238_bpmp_fabric = { + .fab_id = T238_BPMP_FABRIC_ID, + .fab_list = tegra238_cbb_fab_list, + .initiator_id = tegra234_initiator_id, + .errors = tegra234_cbb_errors, + .max_errors = ARRAY_SIZE(tegra234_cbb_errors), + .err_intr_enbl = 0xf, + .err_status_clr = 0x3f, + .notifier_offset = 0x19000, + .firewall_base = 0x30000, + .firewall_ctl = 0x8f0, + .firewall_wr_ctl = 0x8e8, +}; + +static const struct tegra234_cbb_fabric tegra238_cbb_fabric = { + .fab_id = T238_CBB_FABRIC_ID, + .fab_list = tegra238_cbb_fab_list, + .initiator_id = tegra234_initiator_id, + .errors = tegra234_cbb_errors, + .max_errors = ARRAY_SIZE(tegra234_cbb_errors), + .err_intr_enbl = 0x3f, + .err_status_clr = 0x3f, + .notifier_offset = 0x60000, + .off_mask_erd = 0x3d004, + .firewall_base = 0x10000, + .firewall_ctl = 0x2230, + .firewall_wr_ctl = 0x2228, +}; + static const char * const tegra241_initiator_id[] = { [0x0] = "TZ", [0x1] = "CCPLEX", @@ -1160,7 +1315,7 @@ static const struct tegra234_fabric_lookup tegra241_cbb_fab_list[] = { [T234_CBB_FABRIC_ID] = { "cbb-fabric", true, tegra241_cbb_target_map, ARRAY_SIZE(tegra241_cbb_target_map) }, [T234_BPMP_FABRIC_ID] = { "bpmp-fabric", true, - tegra241_bpmp_target_map, ARRAY_SIZE(tegra241_cbb_target_map) }, + tegra241_bpmp_target_map, ARRAY_SIZE(tegra241_bpmp_target_map) }, }; static const struct tegra234_cbb_fabric tegra241_cbb_fabric = { .fab_id = T234_CBB_FABRIC_ID, @@ -1480,6 +1635,10 @@ static const struct of_device_id tegra234_cbb_dt_ids[] = { { .compatible = "nvidia,tegra234-dce-fabric", .data = &tegra234_dce_fabric }, { .compatible = "nvidia,tegra234-rce-fabric", .data = &tegra234_rce_fabric }, { .compatible = "nvidia,tegra234-sce-fabric", .data = &tegra234_sce_fabric }, + { .compatible = "nvidia,tegra238-aon-fabric", .data = &tegra238_aon_fabric }, + { .compatible = "nvidia,tegra238-ape-fabric", .data = &tegra238_ape_fabric }, + { .compatible = "nvidia,tegra238-bpmp-fabric", .data = &tegra238_bpmp_fabric }, + { .compatible = "nvidia,tegra238-cbb-fabric", .data = &tegra238_cbb_fabric }, { .compatible = "nvidia,tegra264-sys-cbb-fabric", .data = &tegra264_sys_cbb_fabric }, { .compatible = "nvidia,tegra264-top0-cbb-fabric", .data = &tegra264_top0_cbb_fabric }, { .compatible = "nvidia,tegra264-uphy0-cbb-fabric", .data = &tegra264_uphy0_cbb_fabric }, @@ -1586,6 +1745,10 @@ static int __maybe_unused tegra234_cbb_resume_noirq(struct device *dev) { struct tegra234_cbb *cbb = dev_get_drvdata(dev); + /* set ERD bit to mask SError and generate interrupt to report error */ + if (cbb->fabric->off_mask_erd) + tegra234_cbb_mask_serror(cbb); + tegra234_cbb_error_enable(&cbb->base); dev_dbg(dev, "%s resumed\n", cbb->fabric->fab_list[cbb->fabric->fab_id].name); diff --git a/drivers/soc/tegra/common.c b/drivers/soc/tegra/common.c index d82b7670abb7..0864eb0185b4 100644 --- a/drivers/soc/tegra/common.c +++ b/drivers/soc/tegra/common.c @@ -118,7 +118,8 @@ int devm_tegra_core_dev_init_opp_table(struct device *dev, hw_version = BIT(tegra_sku_info.soc_process_id); config.supported_hw = &hw_version; config.supported_hw_count = 1; - } else if (of_machine_is_compatible("nvidia,tegra30")) { + } else if (of_machine_is_compatible("nvidia,tegra30") || + of_machine_is_compatible("nvidia,tegra114")) { hw_version = BIT(tegra_sku_info.soc_speedo_id); config.supported_hw = &hw_version; config.supported_hw_count = 1; @@ -131,7 +132,7 @@ int devm_tegra_core_dev_init_opp_table(struct device *dev, } /* - * Tegra114+ doesn't support OPP yet, return early for non tegra20/30 + * Tegra124+ doesn't support OPP yet, return early for pre-Tegra124 * case. */ if (!config.supported_hw) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index a1a2966512d1..2ee6539d796a 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #define PMC_CNTRL 0x0 @@ -180,19 +181,18 @@ #define WAKE_AOWAKE_CNTRL(x) (0x000 + ((x) << 2)) #define WAKE_AOWAKE_CNTRL_LEVEL (1 << 3) #define WAKE_AOWAKE_CNTRL_SR_CAPTURE_EN (1 << 1) -#define WAKE_AOWAKE_MASK_W(x) (0x180 + ((x) << 2)) -#define WAKE_AOWAKE_MASK_R(x) (0x300 + ((x) << 2)) -#define WAKE_AOWAKE_STATUS_W(x) (0x30c + ((x) << 2)) -#define WAKE_AOWAKE_STATUS_R(x) (0x48c + ((x) << 2)) -#define WAKE_AOWAKE_TIER0_ROUTING(x) (0x4b4 + ((x) << 2)) -#define WAKE_AOWAKE_TIER1_ROUTING(x) (0x4c0 + ((x) << 2)) -#define WAKE_AOWAKE_TIER2_ROUTING(x) (0x4cc + ((x) << 2)) -#define WAKE_AOWAKE_SW_STATUS_W_0 0x49c -#define WAKE_AOWAKE_SW_STATUS(x) (0x4a0 + ((x) << 2)) -#define WAKE_LATCH_SW 0x498 +#define WAKE_AOWAKE_MASK_W(_pmc, x) \ + ((_pmc)->soc->regs->aowake_mask_w + ((x) << 2)) +#define WAKE_AOWAKE_STATUS_W(_pmc, x) \ + ((_pmc)->soc->regs->aowake_status_w + ((x) << 2)) +#define WAKE_AOWAKE_STATUS_R(_pmc, x) \ + ((_pmc)->soc->regs->aowake_status_r + ((x) << 2)) +#define WAKE_AOWAKE_TIER2_ROUTING(_pmc, x) \ + ((_pmc)->soc->regs->aowake_tier2_routing + ((x) << 2)) +#define WAKE_AOWAKE_SW_STATUS(_pmc, x) \ + ((_pmc)->soc->regs->aowake_sw_status + ((x) << 2)) -#define WAKE_AOWAKE_CTRL 0x4f4 -#define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0) +#define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0) #define SW_WAKE_ID 83 /* wake83 */ @@ -201,6 +201,9 @@ #define TEGRA_SMC_PMC_READ 0xaa #define TEGRA_SMC_PMC_WRITE 0xbb +/* Tegra264 and later */ +#define PMC_IMPL_SDMMC1_HV_PADCTL_0 0x41004 + struct pmc_clk { struct clk_hw hw; struct tegra_pmc *pmc; @@ -294,10 +297,16 @@ struct tegra_io_pad_soc { unsigned int dpd; unsigned int request; unsigned int status; - unsigned int voltage; const char *name; }; +struct tegra_io_pad_vctrl { + enum tegra_io_pad id; + unsigned int offset; + unsigned int ena_3v3; + unsigned int ena_1v8; +}; + struct tegra_pmc_regs { unsigned int scratch0; unsigned int rst_status; @@ -305,6 +314,14 @@ struct tegra_pmc_regs { unsigned int rst_source_mask; unsigned int rst_level_shift; unsigned int rst_level_mask; + unsigned int aowake_mask_w; + unsigned int aowake_status_w; + unsigned int aowake_status_r; + unsigned int aowake_tier2_routing; + unsigned int aowake_sw_status_w; + unsigned int aowake_sw_status; + unsigned int aowake_latch_sw; + unsigned int aowake_ctrl; }; struct tegra_wake_event { @@ -359,11 +376,13 @@ struct tegra_pmc_soc { bool has_tsense_reset; bool has_gpu_clamps; bool needs_mbist_war; - bool has_impl_33v_pwr; + bool has_io_pad_wren; bool maybe_tz_only; const struct tegra_io_pad_soc *io_pads; unsigned int num_io_pads; + const struct tegra_io_pad_vctrl *io_pad_vctrls; + unsigned int num_io_pad_vctrls; const struct pinctrl_pin_desc *pin_descs; unsigned int num_pin_descs; @@ -437,7 +456,10 @@ struct tegra_pmc_soc { * @wake_sw_status_map: Bitmap to hold raw status of wakes without mask * @wake_cntrl_level_map: Bitmap to hold wake levels to be programmed in * cntrl register associated with each wake during system suspend. + * @reboot_notifier: PMC reboot notifier handler * @syscore: syscore suspend/resume callbacks + * @wake_work: IRQ work handler for processing wake-up events. + * @wake_status: Status of wake-up events. */ struct tegra_pmc { struct device *dev; @@ -1004,7 +1026,7 @@ static struct tegra_pmc *tegra_pmc_get(struct device *dev) } /** - * tegra_pmc_get() - find the PMC for a given device + * devm_tegra_pmc_get() - find the PMC for a given device * @dev: device for which to find the PMC * * Returns a pointer to the PMC on success or an ERR_PTR()-encoded error code @@ -1688,6 +1710,18 @@ tegra_io_pad_find(struct tegra_pmc *pmc, enum tegra_io_pad id) return NULL; } +static const struct tegra_io_pad_vctrl * +tegra_io_pad_vctrl_find(struct tegra_pmc *pmc, enum tegra_io_pad id) +{ + unsigned int i; + + for (i = 0; i < pmc->soc->num_io_pad_vctrls; i++) + if (pmc->soc->io_pad_vctrls[i].id == id) + return &pmc->soc->io_pad_vctrls[i]; + + return NULL; +} + static int tegra_io_pad_prepare(struct tegra_pmc *pmc, const struct tegra_io_pad_soc *pad, unsigned long *request, @@ -1746,7 +1780,7 @@ static void tegra_io_pad_unprepare(struct tegra_pmc *pmc) } /** - * tegra_io_pad_power_enable() - enable power to I/O pad + * tegra_pmc_io_pad_power_enable() - enable power to I/O pad * @pmc: power management controller * @id: Tegra I/O pad ID for which to enable power * @@ -1883,44 +1917,38 @@ static int tegra_io_pad_is_powered(struct tegra_pmc *pmc, enum tegra_io_pad id) static int tegra_io_pad_set_voltage(struct tegra_pmc *pmc, enum tegra_io_pad id, int voltage) { - const struct tegra_io_pad_soc *pad; + const struct tegra_io_pad_vctrl *pad; u32 value; - pad = tegra_io_pad_find(pmc, id); + pad = tegra_io_pad_vctrl_find(pmc, id); if (!pad) return -ENOENT; - if (pad->voltage == UINT_MAX) - return -ENOTSUPP; - mutex_lock(&pmc->powergates_lock); - if (pmc->soc->has_impl_33v_pwr) { - value = tegra_pmc_readl(pmc, PMC_IMPL_E_33V_PWR); - - if (voltage == TEGRA_IO_PAD_VOLTAGE_1V8) - value &= ~BIT(pad->voltage); - else - value |= BIT(pad->voltage); - - tegra_pmc_writel(pmc, value, PMC_IMPL_E_33V_PWR); - } else { - /* write-enable PMC_PWR_DET_VALUE[pad->voltage] */ + if (pmc->soc->has_io_pad_wren) { + /* write-enable PMC_PWR_DET_VALUE[pad->ena_3v3] */ value = tegra_pmc_readl(pmc, PMC_PWR_DET); - value |= BIT(pad->voltage); + value |= BIT(pad->ena_3v3); tegra_pmc_writel(pmc, value, PMC_PWR_DET); - - /* update I/O voltage */ - value = tegra_pmc_readl(pmc, PMC_PWR_DET_VALUE); - - if (voltage == TEGRA_IO_PAD_VOLTAGE_1V8) - value &= ~BIT(pad->voltage); - else - value |= BIT(pad->voltage); - - tegra_pmc_writel(pmc, value, PMC_PWR_DET_VALUE); } + value = tegra_pmc_readl(pmc, pad->offset); + + if (voltage == TEGRA_IO_PAD_VOLTAGE_1V8) { + value &= ~BIT(pad->ena_3v3); + + if (pad->ena_1v8) + value |= pad->ena_1v8; + } else { + value |= BIT(pad->ena_3v3); + + if (pad->ena_1v8) + value &= ~pad->ena_1v8; + } + + tegra_pmc_writel(pmc, value, pad->offset); + mutex_unlock(&pmc->powergates_lock); usleep_range(100, 250); @@ -1930,22 +1958,16 @@ static int tegra_io_pad_set_voltage(struct tegra_pmc *pmc, enum tegra_io_pad id, static int tegra_io_pad_get_voltage(struct tegra_pmc *pmc, enum tegra_io_pad id) { - const struct tegra_io_pad_soc *pad; + const struct tegra_io_pad_vctrl *pad; u32 value; - pad = tegra_io_pad_find(pmc, id); + pad = tegra_io_pad_vctrl_find(pmc, id); if (!pad) return -ENOENT; - if (pad->voltage == UINT_MAX) - return -ENOTSUPP; + value = tegra_pmc_readl(pmc, pad->offset); - if (pmc->soc->has_impl_33v_pwr) - value = tegra_pmc_readl(pmc, PMC_IMPL_E_33V_PWR); - else - value = tegra_pmc_readl(pmc, PMC_PWR_DET_VALUE); - - if ((value & BIT(pad->voltage)) == 0) + if ((value & BIT(pad->ena_3v3)) == 0) return TEGRA_IO_PAD_VOLTAGE_1V8; return TEGRA_IO_PAD_VOLTAGE_3V3; @@ -2629,20 +2651,20 @@ static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on) bit = data->hwirq % 32; /* clear wake status */ - writel(0x1, pmc->wake + WAKE_AOWAKE_STATUS_W(data->hwirq)); + writel(0x1, pmc->wake + WAKE_AOWAKE_STATUS_W(pmc, data->hwirq)); /* route wake to tier 2 */ - value = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(offset)); + value = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(pmc, offset)); if (!on) value &= ~(1 << bit); else value |= 1 << bit; - writel(value, pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(offset)); + writel(value, pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(pmc, offset)); /* enable wakeup event */ - writel(!!on, pmc->wake + WAKE_AOWAKE_MASK_W(data->hwirq)); + writel(!!on, pmc->wake + WAKE_AOWAKE_MASK_W(pmc, data->hwirq)); return 0; } @@ -3309,7 +3331,7 @@ static void wke_write_wake_levels(struct tegra_pmc *pmc) static void wke_clear_sw_wake_status(struct tegra_pmc *pmc) { - wke_32kwritel(pmc, 1, WAKE_AOWAKE_SW_STATUS_W_0); + wke_32kwritel(pmc, 1, pmc->soc->regs->aowake_sw_status_w); } static void wke_read_sw_wake_status(struct tegra_pmc *pmc) @@ -3322,7 +3344,7 @@ static void wke_read_sw_wake_status(struct tegra_pmc *pmc) wke_clear_sw_wake_status(pmc); - wke_32kwritel(pmc, 1, WAKE_LATCH_SW); + wke_32kwritel(pmc, 1, pmc->soc->regs->aowake_latch_sw); /* * WAKE_AOWAKE_SW_STATUS is edge triggered, so in order to @@ -3340,12 +3362,12 @@ static void wke_read_sw_wake_status(struct tegra_pmc *pmc) */ udelay(300); - wke_32kwritel(pmc, 0, WAKE_LATCH_SW); + wke_32kwritel(pmc, 0, pmc->soc->regs->aowake_latch_sw); bitmap_zero(pmc->wake_sw_status_map, pmc->soc->max_wake_events); for (i = 0; i < pmc->soc->max_wake_vectors; i++) { - status = readl(pmc->wake + WAKE_AOWAKE_SW_STATUS(i)); + status = readl(pmc->wake + WAKE_AOWAKE_SW_STATUS(pmc, i)); for_each_set_bit(wake, &status, 32) set_bit(wake + (i * 32), pmc->wake_sw_status_map); @@ -3359,11 +3381,12 @@ static void wke_clear_wake_status(struct tegra_pmc *pmc) u32 mask; for (i = 0; i < pmc->soc->max_wake_vectors; i++) { - mask = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(i)); - status = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask; + mask = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(pmc, i)); + status = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(pmc, i)) & mask; for_each_set_bit(wake, &status, 32) - wke_32kwritel(pmc, 0x1, WAKE_AOWAKE_STATUS_W((i * 32) + wake)); + wke_32kwritel(pmc, 0x1, WAKE_AOWAKE_STATUS_W(pmc, + (i * 32) + wake)); } } @@ -3374,8 +3397,9 @@ static void tegra186_pmc_wake_syscore_resume(void *data) u32 mask; for (i = 0; i < pmc->soc->max_wake_vectors; i++) { - mask = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(i)); - pmc->wake_status[i] = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask; + mask = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(pmc, i)); + pmc->wake_status[i] = readl(pmc->wake + + WAKE_AOWAKE_STATUS_R(pmc, i)) & mask; } /* Schedule IRQ work to process wake IRQs (if any) */ @@ -3523,7 +3547,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = { .has_tsense_reset = false, .has_gpu_clamps = false, .needs_mbist_war = false, - .has_impl_33v_pwr = false, + .has_io_pad_wren = true, .maybe_tz_only = false, .num_io_pads = 0, .io_pads = NULL, @@ -3585,7 +3609,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = { .has_tsense_reset = true, .has_gpu_clamps = false, .needs_mbist_war = false, - .has_impl_33v_pwr = false, + .has_io_pad_wren = true, .maybe_tz_only = false, .num_io_pads = 0, .io_pads = NULL, @@ -3635,7 +3659,7 @@ static const u8 tegra114_cpu_powergates[] = { }; static const struct tegra_pmc_soc tegra114_pmc_soc = { - .supports_core_domain = false, + .supports_core_domain = true, .num_powergates = ARRAY_SIZE(tegra114_powergates), .powergates = tegra114_powergates, .num_cpu_powergates = ARRAY_SIZE(tegra114_cpu_powergates), @@ -3643,7 +3667,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = { .has_tsense_reset = true, .has_gpu_clamps = false, .needs_mbist_war = false, - .has_impl_33v_pwr = false, + .has_io_pad_wren = true, .maybe_tz_only = false, .num_io_pads = 0, .io_pads = NULL, @@ -3697,16 +3721,23 @@ static const u8 tegra124_cpu_powergates[] = { TEGRA_POWERGATE_CPU3, }; -#define TEGRA_IO_PAD(_id, _dpd, _request, _status, _voltage, _name) \ +#define TEGRA_IO_PAD(_id, _dpd, _request, _status, _name) \ ((struct tegra_io_pad_soc) { \ .id = (_id), \ .dpd = (_dpd), \ .request = (_request), \ .status = (_status), \ - .voltage = (_voltage), \ .name = (_name), \ }) +#define TEGRA_IO_PAD_VCTRL(_id, _offset, _ena_3v3) \ + ((struct tegra_io_pad_vctrl) { \ + .id = (_id), \ + .offset = (_offset), \ + .ena_3v3 = (_ena_3v3), \ + .ena_1v8 = 0, \ + }) + #define TEGRA_IO_PIN_DESC(_id, _name) \ ((struct pinctrl_pin_desc) { \ .number = (_id), \ @@ -3714,36 +3745,36 @@ static const u8 tegra124_cpu_powergates[] = { }) static const struct tegra_io_pad_soc tegra124_io_pads[] = { - TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO, 17, 0x1b8, 0x1bc, UINT_MAX, "audio"), - TEGRA_IO_PAD(TEGRA_IO_PAD_BB, 15, 0x1b8, 0x1bc, UINT_MAX, "bb"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CAM, 4, 0x1c0, 0x1c4, UINT_MAX, "cam"), - TEGRA_IO_PAD(TEGRA_IO_PAD_COMP, 22, 0x1b8, 0x1bc, UINT_MAX, "comp"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIA, 0, 0x1b8, 0x1bc, UINT_MAX, "csia"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIB, 1, 0x1b8, 0x1bc, UINT_MAX, "csib"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIE, 12, 0x1c0, 0x1c4, UINT_MAX, "csie"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DSI, 2, 0x1b8, 0x1bc, UINT_MAX, "dsi"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DSIB, 7, 0x1c0, 0x1c4, UINT_MAX, "dsib"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DSIC, 8, 0x1c0, 0x1c4, UINT_MAX, "dsic"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DSID, 9, 0x1c0, 0x1c4, UINT_MAX, "dsid"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI, 28, 0x1b8, 0x1bc, UINT_MAX, "hdmi"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HSIC, 19, 0x1b8, 0x1bc, UINT_MAX, "hsic"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HV, 6, 0x1c0, 0x1c4, UINT_MAX, "hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_LVDS, 25, 0x1c0, 0x1c4, UINT_MAX, "lvds"), - TEGRA_IO_PAD(TEGRA_IO_PAD_MIPI_BIAS, 3, 0x1b8, 0x1bc, UINT_MAX, "mipi-bias"), - TEGRA_IO_PAD(TEGRA_IO_PAD_NAND, 13, 0x1b8, 0x1bc, UINT_MAX, "nand"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_BIAS, 4, 0x1b8, 0x1bc, UINT_MAX, "pex-bias"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK1, 5, 0x1b8, 0x1bc, UINT_MAX, "pex-clk1"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK2, 6, 0x1b8, 0x1bc, UINT_MAX, "pex-clk2"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CNTRL, 0, 0x1c0, 0x1c4, UINT_MAX, "pex-cntrl"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC1, 1, 0x1c0, 0x1c4, UINT_MAX, "sdmmc1"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC3, 2, 0x1c0, 0x1c4, UINT_MAX, "sdmmc3"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC4, 3, 0x1c0, 0x1c4, UINT_MAX, "sdmmc4"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SYS_DDC, 26, 0x1c0, 0x1c4, UINT_MAX, "sys_ddc"), - TEGRA_IO_PAD(TEGRA_IO_PAD_UART, 14, 0x1b8, 0x1bc, UINT_MAX, "uart"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB0, 9, 0x1b8, 0x1bc, UINT_MAX, "usb0"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB1, 10, 0x1b8, 0x1bc, UINT_MAX, "usb1"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB2, 11, 0x1b8, 0x1bc, UINT_MAX, "usb2"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB_BIAS, 12, 0x1b8, 0x1bc, UINT_MAX, "usb_bias"), + TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO, 17, 0x1b8, 0x1bc, "audio"), + TEGRA_IO_PAD(TEGRA_IO_PAD_BB, 15, 0x1b8, 0x1bc, "bb"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CAM, 4, 0x1c0, 0x1c4, "cam"), + TEGRA_IO_PAD(TEGRA_IO_PAD_COMP, 22, 0x1b8, 0x1bc, "comp"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIA, 0, 0x1b8, 0x1bc, "csia"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIB, 1, 0x1b8, 0x1bc, "csib"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIE, 12, 0x1c0, 0x1c4, "csie"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DSI, 2, 0x1b8, 0x1bc, "dsi"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DSIB, 7, 0x1c0, 0x1c4, "dsib"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DSIC, 8, 0x1c0, 0x1c4, "dsic"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DSID, 9, 0x1c0, 0x1c4, "dsid"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI, 28, 0x1b8, 0x1bc, "hdmi"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HSIC, 19, 0x1b8, 0x1bc, "hsic"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HV, 6, 0x1c0, 0x1c4, "hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_LVDS, 25, 0x1c0, 0x1c4, "lvds"), + TEGRA_IO_PAD(TEGRA_IO_PAD_MIPI_BIAS, 3, 0x1b8, 0x1bc, "mipi-bias"), + TEGRA_IO_PAD(TEGRA_IO_PAD_NAND, 13, 0x1b8, 0x1bc, "nand"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_BIAS, 4, 0x1b8, 0x1bc, "pex-bias"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK1, 5, 0x1b8, 0x1bc, "pex-clk1"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK2, 6, 0x1b8, 0x1bc, "pex-clk2"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CNTRL, 0, 0x1c0, 0x1c4, "pex-cntrl"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC1, 1, 0x1c0, 0x1c4, "sdmmc1"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC3, 2, 0x1c0, 0x1c4, "sdmmc3"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC4, 3, 0x1c0, 0x1c4, "sdmmc4"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SYS_DDC, 26, 0x1c0, 0x1c4, "sys_ddc"), + TEGRA_IO_PAD(TEGRA_IO_PAD_UART, 14, 0x1b8, 0x1bc, "uart"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB0, 9, 0x1b8, 0x1bc, "usb0"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB1, 10, 0x1b8, 0x1bc, "usb1"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB2, 11, 0x1b8, 0x1bc, "usb2"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB_BIAS, 12, 0x1b8, 0x1bc, "usb_bias"), }; static const struct pinctrl_pin_desc tegra124_pin_descs[] = { @@ -3788,7 +3819,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = { .has_tsense_reset = true, .has_gpu_clamps = true, .needs_mbist_war = false, - .has_impl_33v_pwr = false, + .has_io_pad_wren = true, .maybe_tz_only = false, .num_io_pads = ARRAY_SIZE(tegra124_io_pads), .io_pads = tegra124_io_pads, @@ -3844,46 +3875,60 @@ static const u8 tegra210_cpu_powergates[] = { }; static const struct tegra_io_pad_soc tegra210_io_pads[] = { - TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO, 17, 0x1b8, 0x1bc, 5, "audio"), - TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO_HV, 29, 0x1c0, 0x1c4, 18, "audio-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CAM, 4, 0x1c0, 0x1c4, 10, "cam"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIA, 0, 0x1b8, 0x1bc, UINT_MAX, "csia"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIB, 1, 0x1b8, 0x1bc, UINT_MAX, "csib"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIC, 10, 0x1c0, 0x1c4, UINT_MAX, "csic"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSID, 11, 0x1c0, 0x1c4, UINT_MAX, "csid"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIE, 12, 0x1c0, 0x1c4, UINT_MAX, "csie"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIF, 13, 0x1c0, 0x1c4, UINT_MAX, "csif"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DBG, 25, 0x1b8, 0x1bc, 19, "dbg"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DEBUG_NONAO, 26, 0x1b8, 0x1bc, UINT_MAX, "debug-nonao"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DMIC, 18, 0x1c0, 0x1c4, 20, "dmic"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DP, 19, 0x1c0, 0x1c4, UINT_MAX, "dp"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DSI, 2, 0x1b8, 0x1bc, UINT_MAX, "dsi"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DSIB, 7, 0x1c0, 0x1c4, UINT_MAX, "dsib"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DSIC, 8, 0x1c0, 0x1c4, UINT_MAX, "dsic"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DSID, 9, 0x1c0, 0x1c4, UINT_MAX, "dsid"), - TEGRA_IO_PAD(TEGRA_IO_PAD_EMMC, 3, 0x1c0, 0x1c4, UINT_MAX, "emmc"), - TEGRA_IO_PAD(TEGRA_IO_PAD_EMMC2, 5, 0x1c0, 0x1c4, UINT_MAX, "emmc2"), - TEGRA_IO_PAD(TEGRA_IO_PAD_GPIO, 27, 0x1b8, 0x1bc, 21, "gpio"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI, 28, 0x1b8, 0x1bc, UINT_MAX, "hdmi"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HSIC, 19, 0x1b8, 0x1bc, UINT_MAX, "hsic"), - TEGRA_IO_PAD(TEGRA_IO_PAD_LVDS, 25, 0x1c0, 0x1c4, UINT_MAX, "lvds"), - TEGRA_IO_PAD(TEGRA_IO_PAD_MIPI_BIAS, 3, 0x1b8, 0x1bc, UINT_MAX, "mipi-bias"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_BIAS, 4, 0x1b8, 0x1bc, UINT_MAX, "pex-bias"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK1, 5, 0x1b8, 0x1bc, UINT_MAX, "pex-clk1"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK2, 6, 0x1b8, 0x1bc, UINT_MAX, "pex-clk2"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CNTRL, UINT_MAX, UINT_MAX, UINT_MAX, 11, "pex-cntrl"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC1, 1, 0x1c0, 0x1c4, 12, "sdmmc1"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC3, 2, 0x1c0, 0x1c4, 13, "sdmmc3"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SPI, 14, 0x1c0, 0x1c4, 22, "spi"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SPI_HV, 15, 0x1c0, 0x1c4, 23, "spi-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_UART, 14, 0x1b8, 0x1bc, 2, "uart"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB0, 9, 0x1b8, 0x1bc, UINT_MAX, "usb0"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB1, 10, 0x1b8, 0x1bc, UINT_MAX, "usb1"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB2, 11, 0x1b8, 0x1bc, UINT_MAX, "usb2"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB3, 18, 0x1b8, 0x1bc, UINT_MAX, "usb3"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB_BIAS, 12, 0x1b8, 0x1bc, UINT_MAX, "usb-bias"), + TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO, 17, 0x1b8, 0x1bc, "audio"), + TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO_HV, 29, 0x1c0, 0x1c4, "audio-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CAM, 4, 0x1c0, 0x1c4, "cam"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIA, 0, 0x1b8, 0x1bc, "csia"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIB, 1, 0x1b8, 0x1bc, "csib"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIC, 10, 0x1c0, 0x1c4, "csic"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSID, 11, 0x1c0, 0x1c4, "csid"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIE, 12, 0x1c0, 0x1c4, "csie"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIF, 13, 0x1c0, 0x1c4, "csif"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DBG, 25, 0x1b8, 0x1bc, "dbg"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DEBUG_NONAO, 26, 0x1b8, 0x1bc, "debug-nonao"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DMIC, 18, 0x1c0, 0x1c4, "dmic"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DP, 19, 0x1c0, 0x1c4, "dp"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DSI, 2, 0x1b8, 0x1bc, "dsi"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DSIB, 7, 0x1c0, 0x1c4, "dsib"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DSIC, 8, 0x1c0, 0x1c4, "dsic"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DSID, 9, 0x1c0, 0x1c4, "dsid"), + TEGRA_IO_PAD(TEGRA_IO_PAD_EMMC, 3, 0x1c0, 0x1c4, "emmc"), + TEGRA_IO_PAD(TEGRA_IO_PAD_EMMC2, 5, 0x1c0, 0x1c4, "emmc2"), + TEGRA_IO_PAD(TEGRA_IO_PAD_GPIO, 27, 0x1b8, 0x1bc, "gpio"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI, 28, 0x1b8, 0x1bc, "hdmi"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HSIC, 19, 0x1b8, 0x1bc, "hsic"), + TEGRA_IO_PAD(TEGRA_IO_PAD_LVDS, 25, 0x1c0, 0x1c4, "lvds"), + TEGRA_IO_PAD(TEGRA_IO_PAD_MIPI_BIAS, 3, 0x1b8, 0x1bc, "mipi-bias"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_BIAS, 4, 0x1b8, 0x1bc, "pex-bias"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK1, 5, 0x1b8, 0x1bc, "pex-clk1"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK2, 6, 0x1b8, 0x1bc, "pex-clk2"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CNTRL, UINT_MAX, UINT_MAX, UINT_MAX, "pex-cntrl"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC1, 1, 0x1c0, 0x1c4, "sdmmc1"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC3, 2, 0x1c0, 0x1c4, "sdmmc3"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SPI, 14, 0x1c0, 0x1c4, "spi"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SPI_HV, 15, 0x1c0, 0x1c4, "spi-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_UART, 14, 0x1b8, 0x1bc, "uart"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB0, 9, 0x1b8, 0x1bc, "usb0"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB1, 10, 0x1b8, 0x1bc, "usb1"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB2, 11, 0x1b8, 0x1bc, "usb2"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB3, 18, 0x1b8, 0x1bc, "usb3"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB_BIAS, 12, 0x1b8, 0x1bc, "usb-bias"), }; +static const struct tegra_io_pad_vctrl tegra210_io_pad_vctrls[] = { + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_AUDIO, PMC_PWR_DET_VALUE, 5), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_AUDIO_HV, PMC_PWR_DET_VALUE, 18), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_CAM, PMC_PWR_DET_VALUE, 10), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_DBG, PMC_PWR_DET_VALUE, 19), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_DMIC, PMC_PWR_DET_VALUE, 20), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_GPIO, PMC_PWR_DET_VALUE, 21), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_PEX_CNTRL, PMC_PWR_DET_VALUE, 11), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_SDMMC1, PMC_PWR_DET_VALUE, 12), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_SDMMC3, PMC_PWR_DET_VALUE, 13), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_SPI, PMC_PWR_DET_VALUE, 22), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_SPI_HV, PMC_PWR_DET_VALUE, 23), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_UART, PMC_PWR_DET_VALUE, 2), +}; static const struct pinctrl_pin_desc tegra210_pin_descs[] = { TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_AUDIO, "audio"), TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_AUDIO_HV, "audio-hv"), @@ -3948,10 +3993,12 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { .has_tsense_reset = true, .has_gpu_clamps = true, .needs_mbist_war = true, - .has_impl_33v_pwr = false, + .has_io_pad_wren = true, .maybe_tz_only = true, .num_io_pads = ARRAY_SIZE(tegra210_io_pads), .io_pads = tegra210_io_pads, + .num_io_pad_vctrls = ARRAY_SIZE(tegra210_io_pad_vctrls), + .io_pad_vctrls = tegra210_io_pad_vctrls, .num_pin_descs = ARRAY_SIZE(tegra210_pin_descs), .pin_descs = tegra210_pin_descs, .regs = &tegra20_pmc_regs, @@ -3974,44 +4021,53 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { }; static const struct tegra_io_pad_soc tegra186_io_pads[] = { - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIA, 0, 0x74, 0x78, UINT_MAX, "csia"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIB, 1, 0x74, 0x78, UINT_MAX, "csib"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DSI, 2, 0x74, 0x78, UINT_MAX, "dsi"), - TEGRA_IO_PAD(TEGRA_IO_PAD_MIPI_BIAS, 3, 0x74, 0x78, UINT_MAX, "mipi-bias"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK_BIAS, 4, 0x74, 0x78, UINT_MAX, "pex-clk-bias"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK3, 5, 0x74, 0x78, UINT_MAX, "pex-clk3"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK2, 6, 0x74, 0x78, UINT_MAX, "pex-clk2"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK1, 7, 0x74, 0x78, UINT_MAX, "pex-clk1"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB0, 9, 0x74, 0x78, UINT_MAX, "usb0"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB1, 10, 0x74, 0x78, UINT_MAX, "usb1"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB2, 11, 0x74, 0x78, UINT_MAX, "usb2"), - TEGRA_IO_PAD(TEGRA_IO_PAD_USB_BIAS, 12, 0x74, 0x78, UINT_MAX, "usb-bias"), - TEGRA_IO_PAD(TEGRA_IO_PAD_UART, 14, 0x74, 0x78, UINT_MAX, "uart"), - TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO, 17, 0x74, 0x78, UINT_MAX, "audio"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HSIC, 19, 0x74, 0x78, UINT_MAX, "hsic"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DBG, 25, 0x74, 0x78, UINT_MAX, "dbg"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP0, 28, 0x74, 0x78, UINT_MAX, "hdmi-dp0"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP1, 29, 0x74, 0x78, UINT_MAX, "hdmi-dp1"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CNTRL, 0, 0x7c, 0x80, UINT_MAX, "pex-cntrl"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC2_HV, 2, 0x7c, 0x80, 5, "sdmmc2-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC4, 4, 0x7c, 0x80, UINT_MAX, "sdmmc4"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CAM, 6, 0x7c, 0x80, UINT_MAX, "cam"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DSIB, 8, 0x7c, 0x80, UINT_MAX, "dsib"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DSIC, 9, 0x7c, 0x80, UINT_MAX, "dsic"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DSID, 10, 0x7c, 0x80, UINT_MAX, "dsid"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIC, 11, 0x7c, 0x80, UINT_MAX, "csic"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSID, 12, 0x7c, 0x80, UINT_MAX, "csid"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIE, 13, 0x7c, 0x80, UINT_MAX, "csie"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIF, 14, 0x7c, 0x80, UINT_MAX, "csif"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SPI, 15, 0x7c, 0x80, UINT_MAX, "spi"), - TEGRA_IO_PAD(TEGRA_IO_PAD_UFS, 17, 0x7c, 0x80, UINT_MAX, "ufs"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DMIC_HV, 20, 0x7c, 0x80, 2, "dmic-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_EDP, 21, 0x7c, 0x80, UINT_MAX, "edp"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC1_HV, 23, 0x7c, 0x80, 4, "sdmmc1-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC3_HV, 24, 0x7c, 0x80, 6, "sdmmc3-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CONN, 28, 0x7c, 0x80, UINT_MAX, "conn"), - TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO_HV, 29, 0x7c, 0x80, 1, "audio-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_AO_HV, UINT_MAX, UINT_MAX, UINT_MAX, 0, "ao-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIA, 0, 0x74, 0x78, "csia"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIB, 1, 0x74, 0x78, "csib"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DSI, 2, 0x74, 0x78, "dsi"), + TEGRA_IO_PAD(TEGRA_IO_PAD_MIPI_BIAS, 3, 0x74, 0x78, "mipi-bias"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK_BIAS, 4, 0x74, 0x78, "pex-clk-bias"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK3, 5, 0x74, 0x78, "pex-clk3"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK2, 6, 0x74, 0x78, "pex-clk2"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK1, 7, 0x74, 0x78, "pex-clk1"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB0, 9, 0x74, 0x78, "usb0"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB1, 10, 0x74, 0x78, "usb1"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB2, 11, 0x74, 0x78, "usb2"), + TEGRA_IO_PAD(TEGRA_IO_PAD_USB_BIAS, 12, 0x74, 0x78, "usb-bias"), + TEGRA_IO_PAD(TEGRA_IO_PAD_UART, 14, 0x74, 0x78, "uart"), + TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO, 17, 0x74, 0x78, "audio"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HSIC, 19, 0x74, 0x78, "hsic"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DBG, 25, 0x74, 0x78, "dbg"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP0, 28, 0x74, 0x78, "hdmi-dp0"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP1, 29, 0x74, 0x78, "hdmi-dp1"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CNTRL, 0, 0x7c, 0x80, "pex-cntrl"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC2_HV, 2, 0x7c, 0x80, "sdmmc2-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC4, 4, 0x7c, 0x80, "sdmmc4"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CAM, 6, 0x7c, 0x80, "cam"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DSIB, 8, 0x7c, 0x80, "dsib"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DSIC, 9, 0x7c, 0x80, "dsic"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DSID, 10, 0x7c, 0x80, "dsid"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIC, 11, 0x7c, 0x80, "csic"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSID, 12, 0x7c, 0x80, "csid"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIE, 13, 0x7c, 0x80, "csie"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIF, 14, 0x7c, 0x80, "csif"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SPI, 15, 0x7c, 0x80, "spi"), + TEGRA_IO_PAD(TEGRA_IO_PAD_UFS, 17, 0x7c, 0x80, "ufs"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DMIC_HV, 20, 0x7c, 0x80, "dmic-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_EDP, 21, 0x7c, 0x80, "edp"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC1_HV, 23, 0x7c, 0x80, "sdmmc1-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC3_HV, 24, 0x7c, 0x80, "sdmmc3-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CONN, 28, 0x7c, 0x80, "conn"), + TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO_HV, 29, 0x7c, 0x80, "audio-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_AO_HV, UINT_MAX, UINT_MAX, UINT_MAX, "ao-hv"), +}; + +static const struct tegra_io_pad_vctrl tegra186_io_pad_vctrls[] = { + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_SDMMC2_HV, PMC_IMPL_E_33V_PWR, 5), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_DMIC_HV, PMC_IMPL_E_33V_PWR, 2), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_SDMMC1_HV, PMC_IMPL_E_33V_PWR, 4), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_SDMMC3_HV, PMC_IMPL_E_33V_PWR, 6), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_AUDIO_HV, PMC_IMPL_E_33V_PWR, 1), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_AO_HV, PMC_IMPL_E_33V_PWR, 0), }; static const struct pinctrl_pin_desc tegra186_pin_descs[] = { @@ -4062,6 +4118,14 @@ static const struct tegra_pmc_regs tegra186_pmc_regs = { .rst_source_mask = 0x3c, .rst_level_shift = 0x0, .rst_level_mask = 0x3, + .aowake_mask_w = 0x180, + .aowake_status_w = 0x30c, + .aowake_status_r = 0x48c, + .aowake_tier2_routing = 0x4cc, + .aowake_sw_status_w = 0x49c, + .aowake_sw_status = 0x4a0, + .aowake_latch_sw = 0x498, + .aowake_ctrl = 0x4f4, }; static void tegra186_pmc_init(struct tegra_pmc *pmc) @@ -4094,14 +4158,14 @@ static void tegra186_pmc_setup_irq_polarity(struct tegra_pmc *pmc, return; } - value = readl(wake + WAKE_AOWAKE_CTRL); + value = readl(wake + pmc->soc->regs->aowake_ctrl); if (invert) value |= WAKE_AOWAKE_CTRL_INTR_POLARITY; else value &= ~WAKE_AOWAKE_CTRL_INTR_POLARITY; - writel(value, wake + WAKE_AOWAKE_CTRL); + writel(value, wake + pmc->soc->regs->aowake_ctrl); iounmap(wake); } @@ -4143,10 +4207,12 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { .has_tsense_reset = false, .has_gpu_clamps = false, .needs_mbist_war = false, - .has_impl_33v_pwr = true, + .has_io_pad_wren = false, .maybe_tz_only = false, .num_io_pads = ARRAY_SIZE(tegra186_io_pads), .io_pads = tegra186_io_pads, + .num_io_pad_vctrls = ARRAY_SIZE(tegra186_io_pad_vctrls), + .io_pad_vctrls = tegra186_io_pad_vctrls, .num_pin_descs = ARRAY_SIZE(tegra186_pin_descs), .pin_descs = tegra186_pin_descs, .regs = &tegra186_pmc_regs, @@ -4171,55 +4237,62 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { }; static const struct tegra_io_pad_soc tegra194_io_pads[] = { - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIA, 0, 0x74, 0x78, UINT_MAX, "csia"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIB, 1, 0x74, 0x78, UINT_MAX, "csib"), - TEGRA_IO_PAD(TEGRA_IO_PAD_MIPI_BIAS, 3, 0x74, 0x78, UINT_MAX, "mipi-bias"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK_BIAS, 4, 0x74, 0x78, UINT_MAX, "pex-clk-bias"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK3, 5, 0x74, 0x78, UINT_MAX, "pex-clk3"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK2, 6, 0x74, 0x78, UINT_MAX, "pex-clk2"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK1, 7, 0x74, 0x78, UINT_MAX, "pex-clk1"), - TEGRA_IO_PAD(TEGRA_IO_PAD_EQOS, 8, 0x74, 0x78, UINT_MAX, "eqos"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK_2_BIAS, 9, 0x74, 0x78, UINT_MAX, "pex-clk-2-bias"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK_2, 10, 0x74, 0x78, UINT_MAX, "pex-clk-2"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DAP3, 11, 0x74, 0x78, UINT_MAX, "dap3"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DAP5, 12, 0x74, 0x78, UINT_MAX, "dap5"), - TEGRA_IO_PAD(TEGRA_IO_PAD_UART, 14, 0x74, 0x78, UINT_MAX, "uart"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PWR_CTL, 15, 0x74, 0x78, UINT_MAX, "pwr-ctl"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SOC_GPIO53, 16, 0x74, 0x78, UINT_MAX, "soc-gpio53"), - TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO, 17, 0x74, 0x78, UINT_MAX, "audio"), - TEGRA_IO_PAD(TEGRA_IO_PAD_GP_PWM2, 18, 0x74, 0x78, UINT_MAX, "gp-pwm2"), - TEGRA_IO_PAD(TEGRA_IO_PAD_GP_PWM3, 19, 0x74, 0x78, UINT_MAX, "gp-pwm3"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SOC_GPIO12, 20, 0x74, 0x78, UINT_MAX, "soc-gpio12"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SOC_GPIO13, 21, 0x74, 0x78, UINT_MAX, "soc-gpio13"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SOC_GPIO10, 22, 0x74, 0x78, UINT_MAX, "soc-gpio10"), - TEGRA_IO_PAD(TEGRA_IO_PAD_UART4, 23, 0x74, 0x78, UINT_MAX, "uart4"), - TEGRA_IO_PAD(TEGRA_IO_PAD_UART5, 24, 0x74, 0x78, UINT_MAX, "uart5"), - TEGRA_IO_PAD(TEGRA_IO_PAD_DBG, 25, 0x74, 0x78, UINT_MAX, "dbg"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP3, 26, 0x74, 0x78, UINT_MAX, "hdmi-dp3"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP2, 27, 0x74, 0x78, UINT_MAX, "hdmi-dp2"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP0, 28, 0x74, 0x78, UINT_MAX, "hdmi-dp0"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP1, 29, 0x74, 0x78, UINT_MAX, "hdmi-dp1"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CNTRL, 0, 0x7c, 0x80, UINT_MAX, "pex-cntrl"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CTL2, 1, 0x7c, 0x80, UINT_MAX, "pex-ctl2"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_L0_RST, 2, 0x7c, 0x80, UINT_MAX, "pex-l0-rst"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_L1_RST, 3, 0x7c, 0x80, UINT_MAX, "pex-l1-rst"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC4, 4, 0x7c, 0x80, UINT_MAX, "sdmmc4"), - TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_L5_RST, 5, 0x7c, 0x80, UINT_MAX, "pex-l5-rst"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CAM, 6, 0x7c, 0x80, UINT_MAX, "cam"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIC, 11, 0x7c, 0x80, UINT_MAX, "csic"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSID, 12, 0x7c, 0x80, UINT_MAX, "csid"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIE, 13, 0x7c, 0x80, UINT_MAX, "csie"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIF, 14, 0x7c, 0x80, UINT_MAX, "csif"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SPI, 15, 0x7c, 0x80, UINT_MAX, "spi"), - TEGRA_IO_PAD(TEGRA_IO_PAD_UFS, 17, 0x7c, 0x80, UINT_MAX, "ufs"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIG, 18, 0x7c, 0x80, UINT_MAX, "csig"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIH, 19, 0x7c, 0x80, UINT_MAX, "csih"), - TEGRA_IO_PAD(TEGRA_IO_PAD_EDP, 21, 0x7c, 0x80, UINT_MAX, "edp"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC1_HV, 23, 0x7c, 0x80, 4, "sdmmc1-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC3_HV, 24, 0x7c, 0x80, 6, "sdmmc3-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CONN, 28, 0x7c, 0x80, UINT_MAX, "conn"), - TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO_HV, 29, 0x7c, 0x80, 1, "audio-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_AO_HV, UINT_MAX, UINT_MAX, UINT_MAX, 0, "ao-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIA, 0, 0x74, 0x78, "csia"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIB, 1, 0x74, 0x78, "csib"), + TEGRA_IO_PAD(TEGRA_IO_PAD_MIPI_BIAS, 3, 0x74, 0x78, "mipi-bias"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK_BIAS, 4, 0x74, 0x78, "pex-clk-bias"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK3, 5, 0x74, 0x78, "pex-clk3"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK2, 6, 0x74, 0x78, "pex-clk2"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK1, 7, 0x74, 0x78, "pex-clk1"), + TEGRA_IO_PAD(TEGRA_IO_PAD_EQOS, 8, 0x74, 0x78, "eqos"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK_2_BIAS, 9, 0x74, 0x78, "pex-clk-2-bias"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CLK_2, 10, 0x74, 0x78, "pex-clk-2"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DAP3, 11, 0x74, 0x78, "dap3"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DAP5, 12, 0x74, 0x78, "dap5"), + TEGRA_IO_PAD(TEGRA_IO_PAD_UART, 14, 0x74, 0x78, "uart"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PWR_CTL, 15, 0x74, 0x78, "pwr-ctl"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SOC_GPIO53, 16, 0x74, 0x78, "soc-gpio53"), + TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO, 17, 0x74, 0x78, "audio"), + TEGRA_IO_PAD(TEGRA_IO_PAD_GP_PWM2, 18, 0x74, 0x78, "gp-pwm2"), + TEGRA_IO_PAD(TEGRA_IO_PAD_GP_PWM3, 19, 0x74, 0x78, "gp-pwm3"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SOC_GPIO12, 20, 0x74, 0x78, "soc-gpio12"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SOC_GPIO13, 21, 0x74, 0x78, "soc-gpio13"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SOC_GPIO10, 22, 0x74, 0x78, "soc-gpio10"), + TEGRA_IO_PAD(TEGRA_IO_PAD_UART4, 23, 0x74, 0x78, "uart4"), + TEGRA_IO_PAD(TEGRA_IO_PAD_UART5, 24, 0x74, 0x78, "uart5"), + TEGRA_IO_PAD(TEGRA_IO_PAD_DBG, 25, 0x74, 0x78, "dbg"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP3, 26, 0x74, 0x78, "hdmi-dp3"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP2, 27, 0x74, 0x78, "hdmi-dp2"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP0, 28, 0x74, 0x78, "hdmi-dp0"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP1, 29, 0x74, 0x78, "hdmi-dp1"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CNTRL, 0, 0x7c, 0x80, "pex-cntrl"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_CTL2, 1, 0x7c, 0x80, "pex-ctl2"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_L0_RST, 2, 0x7c, 0x80, "pex-l0-rst"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_L1_RST, 3, 0x7c, 0x80, "pex-l1-rst"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC4, 4, 0x7c, 0x80, "sdmmc4"), + TEGRA_IO_PAD(TEGRA_IO_PAD_PEX_L5_RST, 5, 0x7c, 0x80, "pex-l5-rst"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CAM, 6, 0x7c, 0x80, "cam"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIC, 11, 0x7c, 0x80, "csic"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSID, 12, 0x7c, 0x80, "csid"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIE, 13, 0x7c, 0x80, "csie"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIF, 14, 0x7c, 0x80, "csif"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SPI, 15, 0x7c, 0x80, "spi"), + TEGRA_IO_PAD(TEGRA_IO_PAD_UFS, 17, 0x7c, 0x80, "ufs"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIG, 18, 0x7c, 0x80, "csig"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIH, 19, 0x7c, 0x80, "csih"), + TEGRA_IO_PAD(TEGRA_IO_PAD_EDP, 21, 0x7c, 0x80, "edp"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC1_HV, 23, 0x7c, 0x80, "sdmmc1-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC3_HV, 24, 0x7c, 0x80, "sdmmc3-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CONN, 28, 0x7c, 0x80, "conn"), + TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO_HV, 29, 0x7c, 0x80, "audio-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_AO_HV, UINT_MAX, UINT_MAX, UINT_MAX, "ao-hv"), +}; + +static const struct tegra_io_pad_vctrl tegra194_io_pad_vctrls[] = { + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_SDMMC1_HV, PMC_IMPL_E_33V_PWR, 4), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_SDMMC3_HV, PMC_IMPL_E_33V_PWR, 6), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_AUDIO_HV, PMC_IMPL_E_33V_PWR, 1), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_AO_HV, PMC_IMPL_E_33V_PWR, 0), }; static const struct pinctrl_pin_desc tegra194_pin_descs[] = { @@ -4281,6 +4354,14 @@ static const struct tegra_pmc_regs tegra194_pmc_regs = { .rst_source_mask = 0x7c, .rst_level_shift = 0x0, .rst_level_mask = 0x3, + .aowake_mask_w = 0x180, + .aowake_status_w = 0x30c, + .aowake_status_r = 0x48c, + .aowake_tier2_routing = 0x4cc, + .aowake_sw_status_w = 0x49c, + .aowake_sw_status = 0x4a0, + .aowake_latch_sw = 0x498, + .aowake_ctrl = 0x4f4, }; static const char * const tegra194_reset_sources[] = { @@ -4330,10 +4411,12 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = { .has_tsense_reset = false, .has_gpu_clamps = false, .needs_mbist_war = false, - .has_impl_33v_pwr = true, + .has_io_pad_wren = false, .maybe_tz_only = false, .num_io_pads = ARRAY_SIZE(tegra194_io_pads), .io_pads = tegra194_io_pads, + .num_io_pad_vctrls = ARRAY_SIZE(tegra194_io_pad_vctrls), + .io_pad_vctrls = tegra194_io_pad_vctrls, .num_pin_descs = ARRAY_SIZE(tegra194_pin_descs), .pin_descs = tegra194_pin_descs, .regs = &tegra194_pmc_regs, @@ -4358,21 +4441,28 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = { }; static const struct tegra_io_pad_soc tegra234_io_pads[] = { - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIA, 0, 0xe0c0, 0xe0c4, UINT_MAX, "csia"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIB, 1, 0xe0c0, 0xe0c4, UINT_MAX, "csib"), - TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP0, 0, 0xe0d0, 0xe0d4, UINT_MAX, "hdmi-dp0"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIC, 2, 0xe0c0, 0xe0c4, UINT_MAX, "csic"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSID, 3, 0xe0c0, 0xe0c4, UINT_MAX, "csid"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIE, 4, 0xe0c0, 0xe0c4, UINT_MAX, "csie"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIF, 5, 0xe0c0, 0xe0c4, UINT_MAX, "csif"), - TEGRA_IO_PAD(TEGRA_IO_PAD_UFS, 0, 0xe064, 0xe068, UINT_MAX, "ufs"), - TEGRA_IO_PAD(TEGRA_IO_PAD_EDP, 1, 0xe05c, 0xe060, UINT_MAX, "edp"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC1_HV, 0, 0xe054, 0xe058, 4, "sdmmc1-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC3_HV, UINT_MAX, UINT_MAX, UINT_MAX, 6, "sdmmc3-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO_HV, UINT_MAX, UINT_MAX, UINT_MAX, 1, "audio-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_AO_HV, UINT_MAX, UINT_MAX, UINT_MAX, 0, "ao-hv"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIG, 6, 0xe0c0, 0xe0c4, UINT_MAX, "csig"), - TEGRA_IO_PAD(TEGRA_IO_PAD_CSIH, 7, 0xe0c0, 0xe0c4, UINT_MAX, "csih"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIA, 0, 0xe0c0, 0xe0c4, "csia"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIB, 1, 0xe0c0, 0xe0c4, "csib"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP0, 0, 0xe0d0, 0xe0d4, "hdmi-dp0"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIC, 2, 0xe0c0, 0xe0c4, "csic"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSID, 3, 0xe0c0, 0xe0c4, "csid"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIE, 4, 0xe0c0, 0xe0c4, "csie"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIF, 5, 0xe0c0, 0xe0c4, "csif"), + TEGRA_IO_PAD(TEGRA_IO_PAD_UFS, 0, 0xe064, 0xe068, "ufs"), + TEGRA_IO_PAD(TEGRA_IO_PAD_EDP, 1, 0xe05c, 0xe060, "edp"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC1_HV, 0, 0xe054, 0xe058, "sdmmc1-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC3_HV, UINT_MAX, UINT_MAX, UINT_MAX, "sdmmc3-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_AUDIO_HV, UINT_MAX, UINT_MAX, UINT_MAX, "audio-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_AO_HV, UINT_MAX, UINT_MAX, UINT_MAX, "ao-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIG, 6, 0xe0c0, 0xe0c4, "csig"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIH, 7, 0xe0c0, 0xe0c4, "csih"), +}; + +static const struct tegra_io_pad_vctrl tegra234_io_pad_vctrls[] = { + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_SDMMC1_HV, PMC_IMPL_E_33V_PWR, 4), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_SDMMC3_HV, PMC_IMPL_E_33V_PWR, 6), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_AUDIO_HV, PMC_IMPL_E_33V_PWR, 1), + TEGRA_IO_PAD_VCTRL(TEGRA_IO_PAD_AO_HV, PMC_IMPL_E_33V_PWR, 0), }; static const struct pinctrl_pin_desc tegra234_pin_descs[] = { @@ -4400,6 +4490,14 @@ static const struct tegra_pmc_regs tegra234_pmc_regs = { .rst_source_mask = 0xfc, .rst_level_shift = 0x0, .rst_level_mask = 0x3, + .aowake_mask_w = 0x180, + .aowake_status_w = 0x30c, + .aowake_status_r = 0x48c, + .aowake_tier2_routing = 0x4cc, + .aowake_sw_status_w = 0x49c, + .aowake_sw_status = 0x4a0, + .aowake_latch_sw = 0x498, + .aowake_ctrl = 0x4f4, }; static const char * const tegra234_reset_sources[] = { @@ -4469,10 +4567,12 @@ static const struct tegra_pmc_soc tegra234_pmc_soc = { .has_tsense_reset = false, .has_gpu_clamps = false, .needs_mbist_war = false, - .has_impl_33v_pwr = true, + .has_io_pad_wren = false, .maybe_tz_only = false, .num_io_pads = ARRAY_SIZE(tegra234_io_pads), .io_pads = tegra234_io_pads, + .num_io_pad_vctrls = ARRAY_SIZE(tegra234_io_pad_vctrls), + .io_pad_vctrls = tegra234_io_pad_vctrls, .num_pin_descs = ARRAY_SIZE(tegra234_pin_descs), .pin_descs = tegra234_pin_descs, .regs = &tegra234_pmc_regs, @@ -4495,6 +4595,50 @@ static const struct tegra_pmc_soc tegra234_pmc_soc = { .has_single_mmio_aperture = false, }; +#define TEGRA264_IO_PAD_VCTRL(_id, _offset, _ena_3v3, _ena_1v8) \ + ((struct tegra_io_pad_vctrl) { \ + .id = (_id), \ + .offset = (_offset), \ + .ena_3v3 = (_ena_3v3), \ + .ena_1v8 = (_ena_1v8), \ + }) + +static const struct tegra_io_pad_soc tegra264_io_pads[] = { + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIA, 0, 0x41020, 0x41024, "csia"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIB, 1, 0x41020, 0x41024, "csib"), + TEGRA_IO_PAD(TEGRA_IO_PAD_HDMI_DP0, 0, 0x41050, 0x41054, "hdmi-dp0"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIC, 2, 0x41020, 0x41024, "csic"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSID, 3, 0x41020, 0x41024, "csid"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIE, 4, 0x41020, 0x41024, "csie"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIF, 5, 0x41020, 0x41024, "csif"), + TEGRA_IO_PAD(TEGRA_IO_PAD_UFS, 4, 0x41040, 0x41044, "ufs0"), + TEGRA_IO_PAD(TEGRA_IO_PAD_EDP, 0, 0x41028, 0x4102c, "edp"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC1, 0, 0x41090, 0x41094, "sdmmc1"), + TEGRA_IO_PAD(TEGRA_IO_PAD_SDMMC1_HV, UINT_MAX, UINT_MAX, UINT_MAX, "sdmmc1-hv"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIG, 6, 0x41020, 0x41024, "csig"), + TEGRA_IO_PAD(TEGRA_IO_PAD_CSIH, 7, 0x41020, 0x41024, "csih"), +}; + +static const struct tegra_io_pad_vctrl tegra264_io_pad_vctrls[] = { + TEGRA264_IO_PAD_VCTRL(TEGRA_IO_PAD_SDMMC1_HV, PMC_IMPL_SDMMC1_HV_PADCTL_0, 0, 0x6), +}; + +static const struct pinctrl_pin_desc tegra264_pin_descs[] = { + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIA, "csia"), + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIB, "csib"), + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_HDMI_DP0, "hdmi-dp0"), + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIC, "csic"), + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSID, "csid"), + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIE, "csie"), + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIF, "csif"), + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_UFS, "ufs0"), + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_EDP, "edp"), + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_SDMMC1, "sdmmc1"), + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_SDMMC1_HV, "sdmmc1-hv"), + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIG, "csig"), + TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIH, "csih"), +}; + static const struct tegra_pmc_regs tegra264_pmc_regs = { .scratch0 = 0x684, .rst_status = 0x4, @@ -4502,6 +4646,14 @@ static const struct tegra_pmc_regs tegra264_pmc_regs = { .rst_source_mask = 0x1fc, .rst_level_shift = 0x0, .rst_level_mask = 0x3, + .aowake_mask_w = 0x200, + .aowake_status_w = 0x410, + .aowake_status_r = 0x610, + .aowake_tier2_routing = 0x660, + .aowake_sw_status_w = 0x624, + .aowake_sw_status = 0x628, + .aowake_latch_sw = 0x620, + .aowake_ctrl = 0x68c, }; static const char * const tegra264_reset_sources[] = { @@ -4595,10 +4747,26 @@ static const char * const tegra264_reset_sources[] = { }; static const struct tegra_wake_event tegra264_wake_events[] = { + TEGRA_WAKE_IRQ("pmu", 0, 727), + TEGRA_WAKE_GPIO("power", 5, 1, TEGRA264_AON_GPIO(AA, 5)), + TEGRA_WAKE_IRQ("rtc", 65, 548), + TEGRA_WAKE_IRQ("usb3-port-0", 79, 965), + TEGRA_WAKE_IRQ("usb3-port-1", 80, 965), + TEGRA_WAKE_IRQ("usb3-port-3", 82, 965), + TEGRA_WAKE_IRQ("usb2-port-0", 83, 965), + TEGRA_WAKE_IRQ("usb2-port-1", 84, 965), + TEGRA_WAKE_IRQ("usb2-port-2", 85, 965), + TEGRA_WAKE_IRQ("usb2-port-3", 86, 965), }; static const struct tegra_pmc_soc tegra264_pmc_soc = { - .has_impl_33v_pwr = true, + .has_io_pad_wren = false, + .num_io_pads = ARRAY_SIZE(tegra264_io_pads), + .io_pads = tegra264_io_pads, + .num_io_pad_vctrls = ARRAY_SIZE(tegra264_io_pad_vctrls), + .io_pad_vctrls = tegra264_io_pad_vctrls, + .num_pin_descs = ARRAY_SIZE(tegra264_pin_descs), + .pin_descs = tegra264_pin_descs, .regs = &tegra264_pmc_regs, .init = tegra186_pmc_init, .setup_irq_polarity = tegra186_pmc_setup_irq_polarity, diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index b9ccb3343896..a3316efdf8ac 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -27,6 +27,36 @@ #define to_amd_sdw(b) container_of(b, struct amd_sdw_manager, bus) +static int amd_sdw_clk_init_ctrl(struct amd_sdw_manager *amd_manager) +{ + struct sdw_bus *bus = &amd_manager->bus; + struct sdw_master_prop *prop = &bus->prop; + u32 divider; + + dev_dbg(amd_manager->dev, "mclk %d max %d row %d col %d frame_rate:%d\n", + prop->mclk_freq, prop->max_clk_freq, prop->default_row, + prop->default_col, prop->default_frame_rate); + + if (!prop->default_frame_rate || !prop->default_row) { + dev_err(amd_manager->dev, "Default frame_rate %d or row %d is invalid\n", + prop->default_frame_rate, prop->default_row); + return -EINVAL; + } + + /* Set clock divider */ + divider = (prop->mclk_freq / bus->params.curr_dr_freq); + writel(divider, amd_manager->mmio + ACP_SW_CLK_FREQUENCY_CTRL); + + /* Set frame shape base on the actual bus frequency. */ + prop->default_col = bus->params.curr_dr_freq / + prop->default_frame_rate / prop->default_row; + amd_manager->cols_index = sdw_find_col_index(prop->default_col); + amd_manager->rows_index = sdw_find_row_index(prop->default_row); + bus->params.col = prop->default_col; + bus->params.row = prop->default_row; + return 0; +} + static int amd_init_sdw_manager(struct amd_sdw_manager *amd_manager) { u32 val; @@ -437,12 +467,16 @@ static u32 amd_sdw_read_ping_status(struct sdw_bus *bus) static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { + struct amd_sdw_manager *amd_manager = to_amd_sdw(bus); struct sdw_transport_data t_data = {0}; struct sdw_master_runtime *m_rt; struct sdw_port_runtime *p_rt; struct sdw_bus_params *b_params = &bus->params; int port_bo, hstart, hstop, sample_int; - unsigned int rate, bps; + unsigned int rate, bps, channels; + unsigned int stream_slot_size, max_slots; + static unsigned int next_offset[AMD_SDW_MAX_MANAGER_COUNT] = {1}; + unsigned int inst_id = amd_manager->instance; port_bo = 0; hstart = 1; @@ -453,11 +487,51 @@ static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { rate = m_rt->stream->params.rate; bps = m_rt->stream->params.bps; + channels = m_rt->stream->params.ch_count; sample_int = (bus->params.curr_dr_freq / rate); + + /* Compute slots required for this stream dynamically */ + stream_slot_size = bps * channels; + list_for_each_entry(p_rt, &m_rt->port_list, port_node) { - port_bo = (p_rt->num * 64) + 1; - dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n", - p_rt->num, hstart, hstop, port_bo); + if (p_rt->num >= amd_manager->max_ports) { + dev_err(bus->dev, "Port %d exceeds max ports %d\n", + p_rt->num, amd_manager->max_ports); + return -EINVAL; + } + + if (!amd_manager->port_offset_map[p_rt->num]) { + /* + * port block offset calculation for 6MHz bus clock frequency with + * different frame sizes 50 x 10 and 125 x 2 + */ + if (bus->params.curr_dr_freq == 12000000) { + max_slots = bus->params.row * (bus->params.col - 1); + if (next_offset[inst_id] + stream_slot_size <= + (max_slots - 1)) { + amd_manager->port_offset_map[p_rt->num] = + next_offset[inst_id]; + next_offset[inst_id] += stream_slot_size; + } else { + dev_err(bus->dev, + "No space for port %d\n", p_rt->num); + return -ENOMEM; + } + } else { + /* + * port block offset calculation for 12MHz bus clock + * frequency + */ + amd_manager->port_offset_map[p_rt->num] = + (p_rt->num * 64) + 1; + } + } + port_bo = amd_manager->port_offset_map[p_rt->num]; + dev_dbg(bus->dev, + "Port=%d hstart=%d hstop=%d port_bo=%d slots=%d max_ports=%d\n", + p_rt->num, hstart, hstop, port_bo, stream_slot_size, + amd_manager->max_ports); + sdw_fill_xport_params(&p_rt->transport_params, p_rt->num, false, SDW_BLK_GRP_CNT_1, sample_int, port_bo, port_bo >> 8, hstart, hstop, @@ -960,6 +1034,9 @@ int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager) prop = &amd_manager->bus.prop; if (!prop->hw_disabled) { + ret = amd_sdw_clk_init_ctrl(amd_manager); + if (ret) + return ret; ret = amd_init_sdw_manager(amd_manager); if (ret) return ret; @@ -984,7 +1061,6 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) struct resource *res; struct device *dev = &pdev->dev; struct sdw_master_prop *prop; - struct sdw_bus_params *params; struct amd_sdw_manager *amd_manager; int ret; @@ -1047,15 +1123,14 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) default: return -EINVAL; } + amd_manager->max_ports = amd_manager->num_dout_ports + amd_manager->num_din_ports; + amd_manager->port_offset_map = devm_kcalloc(dev, amd_manager->max_ports, + sizeof(int), GFP_KERNEL); + if (!amd_manager->port_offset_map) + return -ENOMEM; - params = &amd_manager->bus.params; - - params->col = AMD_SDW_DEFAULT_COLUMNS; - params->row = AMD_SDW_DEFAULT_ROWS; prop = &amd_manager->bus.prop; - prop->clk_freq = &amd_sdw_freq_tbl[0]; prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ; - prop->max_clk_freq = AMD_SDW_DEFAULT_CLK_FREQ; ret = sdw_bus_master_add(&amd_manager->bus, dev, dev->fwnode); if (ret) { @@ -1347,6 +1422,9 @@ static int __maybe_unused amd_resume_runtime(struct device *dev) } } sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); + ret = amd_sdw_clk_init_ctrl(amd_manager); + if (ret) + return ret; amd_init_sdw_manager(amd_manager); amd_enable_sdw_interrupts(amd_manager); ret = amd_enable_sdw_manager(amd_manager); diff --git a/drivers/soundwire/amd_manager.h b/drivers/soundwire/amd_manager.h index 6cc916b0c820..88cf8a426a0c 100644 --- a/drivers/soundwire/amd_manager.h +++ b/drivers/soundwire/amd_manager.h @@ -203,10 +203,6 @@ #define AMD_SDW_DEVICE_STATE_D3 3 #define ACP_PME_EN 0x0001400 -static u32 amd_sdw_freq_tbl[AMD_SDW_MAX_FREQ_NUM] = { - AMD_SDW_DEFAULT_CLK_FREQ, -}; - struct sdw_manager_dp_reg { u32 frame_fmt_reg; u32 sample_int_reg; diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fb68738dfb9b..fe5316d93fef 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1899,8 +1899,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (status[i] == SDW_SLAVE_UNATTACHED && slave->status != SDW_SLAVE_UNATTACHED) { - dev_warn(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n", - i, slave->status); + dev_dbg(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n", + i, slave->status); sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); /* Ensure driver knows that peripheral unattached */ @@ -1951,8 +1951,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus, if (slave->status == SDW_SLAVE_UNATTACHED) break; - dev_warn(&slave->dev, "Slave %d state check2: UNATTACHED, status was %d\n", - i, slave->status); + dev_dbg(&slave->dev, "Slave %d state check2: UNATTACHED, status was %d\n", + i, slave->status); sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED); break; diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index f245c3ffb9e9..b8b62735c893 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -933,6 +933,14 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) cdns_read_response(cdns); + /* + * Clear interrupt before signalling the completion to avoid + * a race between this thread and the main thread starting + * another TX. + */ + cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_RX_WL); + int_status &= ~CDNS_MCP_INT_RX_WL; + if (defer && defer->msg) { cdns_fill_msg_resp(cdns, defer->msg, defer->length, 0); diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 7f01e43ae978..20422534baf1 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -82,6 +82,11 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * int len; int i; + if (cdns->bus.bpt_stream) { + dev_err(cdns->dev, "%s: BPT stream already exists\n", __func__); + return -EAGAIN; + } + stream = sdw_alloc_stream("BPT", SDW_STREAM_BPT); if (!stream) return -ENOMEM; diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index 8752b0e3ce74..913e95207ee1 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -53,6 +53,8 @@ struct wake_capable_part { static struct wake_capable_part wake_capable_list[] = { {0x01fa, 0x4243}, {0x01fa, 0x4245}, + {0x01fa, 0x4249}, + {0x01fa, 0x4747}, {0x025d, 0x5682}, {0x025d, 0x700}, {0x025d, 0x711}, diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index f5a3ca3b9dda..ff763b692078 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -115,6 +115,9 @@ static bool find_slave(struct sdw_bus *bus, u64 addr; int ret; + if (acpi_bus_get_status(adev) || !acpi_dev_ready_for_enumeration(adev)) + return false; + ret = acpi_get_local_u64_address(adev->handle, &addr); if (ret < 0) return false; diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index b6982f8dcf17..4ed8fb7663ad 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,8 @@ #include #include "bus.h" +#define SDW_PORT_PREP_POLL_USEC 1000 + /* * Array of supported rows and columns as per MIPI SoundWire Specification 1.1 * @@ -443,7 +446,6 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, struct sdw_port_runtime *p_rt, bool prep) { - struct completion *port_ready; struct sdw_dpn_prop *dpn_prop; struct sdw_prepare_ch prep_ch; u32 imp_def_interrupts; @@ -518,14 +520,18 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, return ret; } - /* Wait for completion on port ready */ - port_ready = &s_rt->slave->port_ready[prep_ch.num]; - wait_for_completion_timeout(port_ready, - msecs_to_jiffies(ch_prep_timeout)); + /* + * Poll for NOT_PREPARED==0. Cannot use the interrupt because + * this code holds bus_lock which blocks interrupt handling. + */ + ret = read_poll_timeout(sdw_read_no_pm, val, + (val < 0) || ((val & p_rt->ch_mask) == 0), + SDW_PORT_PREP_POLL_USEC, ch_prep_timeout * USEC_PER_MSEC, + false, s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num)); + if (ret || (val < 0)) { + if (val < 0) + ret = val; - val = sdw_read_no_pm(s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num)); - if ((val < 0) || (val & p_rt->ch_mask)) { - ret = (val < 0) ? val : -ETIMEDOUT; dev_err(&s_rt->slave->dev, "Chn prep failed for port %d: %d\n", prep_ch.num, ret); return ret; diff --git a/drivers/spi/spi-amlogic-spisg.c b/drivers/spi/spi-amlogic-spisg.c index 19c5eba412ef..f9de2d2c9213 100644 --- a/drivers/spi/spi-amlogic-spisg.c +++ b/drivers/spi/spi-amlogic-spisg.c @@ -794,6 +794,7 @@ static int aml_spisg_probe(struct platform_device *pdev) dma_set_max_seg_size(&pdev->dev, SPISG_BLOCK_MAX); + init_completion(&spisg->completion); ret = devm_request_irq(&pdev->dev, irq, aml_spisg_irq, 0, NULL, spisg); if (ret) { dev_err(&pdev->dev, "irq request failed\n"); @@ -806,8 +807,6 @@ static int aml_spisg_probe(struct platform_device *pdev) goto out_clk; } - init_completion(&spisg->completion); - pm_runtime_put(&spisg->pdev->dev); return 0; diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index 48ee5bf48173..02bbc5d0cfc5 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -953,7 +953,7 @@ static int spi_engine_transfer_one_message(struct spi_controller *host, struct spi_transfer *xfer; list_for_each_entry(xfer, &msg->transfers, transfer_list) - trace_spi_transfer_start(msg, xfer); + trace_call__spi_transfer_start(msg, xfer); } spin_lock_irqsave(&spi_engine->lock, flags); @@ -987,7 +987,7 @@ static int spi_engine_transfer_one_message(struct spi_controller *host, struct spi_transfer *xfer; list_for_each_entry(xfer, &msg->transfers, transfer_list) - trace_spi_transfer_stop(msg, xfer); + trace_call__spi_transfer_stop(msg, xfer); } out: diff --git a/drivers/spi/spi-axiado.c b/drivers/spi/spi-axiado.c index dc55c55ae63c..649f149617ce 100644 --- a/drivers/spi/spi-axiado.c +++ b/drivers/spi/spi-axiado.c @@ -201,7 +201,7 @@ static void ax_spi_fill_tx_fifo(struct ax_spi *xspi) * then spi control did't work thoroughly, add one byte delay */ if (ax_spi_read(xspi, AX_SPI_IVR) & AX_SPI_IVR_TFOV) - usleep_range(10, 10); + udelay(10); if (xspi->tx_buf) ax_spi_write_b(xspi, AX_SPI_TXFIFO, *xspi->tx_buf++); else @@ -751,9 +751,9 @@ static const struct spi_controller_mem_ops ax_spi_mem_ops = { */ static int ax_spi_probe(struct platform_device *pdev) { - int ret = 0, irq; struct spi_controller *ctlr; struct ax_spi *xspi; + int ret, irq; u32 num_cs; ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*xspi)); @@ -785,7 +785,7 @@ static int ax_spi_probe(struct platform_device *pdev) ret = clk_prepare_enable(xspi->ref_clk); if (ret) { dev_err(&pdev->dev, "Unable to enable device clock.\n"); - goto clk_dis_apb; + goto err_disable_apb; } pm_runtime_use_autosuspend(&pdev->dev); @@ -815,7 +815,7 @@ static int ax_spi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq <= 0) { ret = -ENXIO; - goto clk_dis_all; + goto err_disable_rpm; } ret = devm_request_irq(&pdev->dev, irq, ax_spi_irq, @@ -823,7 +823,7 @@ static int ax_spi_probe(struct platform_device *pdev) if (ret != 0) { ret = -ENXIO; dev_err(&pdev->dev, "request_irq failed\n"); - goto clk_dis_all; + goto err_disable_rpm; } ctlr->use_gpio_descriptors = true; @@ -842,23 +842,26 @@ static int ax_spi_probe(struct platform_device *pdev) ctlr->bits_per_word_mask = SPI_BPW_MASK(8); - pm_runtime_put_autosuspend(&pdev->dev); - ctlr->mem_ops = &ax_spi_mem_ops; ret = spi_register_controller(ctlr); if (ret) { dev_err(&pdev->dev, "spi_register_controller failed\n"); - goto clk_dis_all; + goto err_disable_rpm; } - return ret; + pm_runtime_put_autosuspend(&pdev->dev); -clk_dis_all: - pm_runtime_set_suspended(&pdev->dev); + return 0; + +err_disable_rpm: pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + clk_disable_unprepare(xspi->ref_clk); -clk_dis_apb: +err_disable_apb: clk_disable_unprepare(xspi->pclk); return ret; @@ -877,10 +880,14 @@ static void ax_spi_remove(struct platform_device *pdev) struct spi_controller *ctlr = platform_get_drvdata(pdev); struct ax_spi *xspi = spi_controller_get_devdata(ctlr); + pm_runtime_get_sync(&pdev->dev); + spi_unregister_controller(ctlr); - pm_runtime_set_suspended(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); clk_disable_unprepare(xspi->ref_clk); clk_disable_unprepare(xspi->pclk); diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index b6f7f95e8bd3..057381e56a7f 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1860,14 +1860,10 @@ static int cqspi_probe(struct platform_device *pdev) if (irq < 0) return -ENXIO; - ret = pm_runtime_set_active(dev); - if (ret) - return ret; - ret = clk_bulk_prepare_enable(CLK_QSPI_NUM, cqspi->clks); if (ret) { dev_err(dev, "Cannot enable QSPI clocks.\n"); - goto disable_rpm; + return ret; } /* Obtain QSPI reset control */ @@ -1962,10 +1958,11 @@ static int cqspi_probe(struct platform_device *pdev) cqspi->sclk = 0; if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { - pm_runtime_enable(dev); pm_runtime_set_autosuspend_delay(dev, CQSPI_AUTOSUSPEND_TIMEOUT); pm_runtime_use_autosuspend(dev); pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); } host->num_chipselect = cqspi->num_chipselect; @@ -1977,7 +1974,7 @@ static int cqspi_probe(struct platform_device *pdev) ret = cqspi_request_mmap_dma(cqspi); if (ret == -EPROBE_DEFER) { dev_err_probe(&pdev->dev, ret, "Failed to request mmap DMA\n"); - goto disable_controller; + goto disable_rpm; } } @@ -1995,14 +1992,16 @@ static int cqspi_probe(struct platform_device *pdev) release_dma_chan: if (cqspi->rx_chan) dma_release_channel(cqspi->rx_chan); -disable_controller: +disable_rpm: + if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + pm_runtime_dont_use_autosuspend(dev); + } cqspi_controller_enable(cqspi, 0); disable_clks: - if (pm_runtime_get_sync(&pdev->dev) >= 0) - clk_bulk_disable_unprepare(CLK_QSPI_NUM, cqspi->clks); -disable_rpm: - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) - pm_runtime_disable(dev); + clk_bulk_disable_unprepare(CLK_QSPI_NUM, cqspi->clks); return ret; } @@ -2016,28 +2015,29 @@ static void cqspi_remove(struct platform_device *pdev) ddata = of_device_get_match_data(dev); + spi_unregister_controller(cqspi->host); + refcount_set(&cqspi->refcount, 0); if (!refcount_dec_and_test(&cqspi->inflight_ops)) cqspi_wait_idle(cqspi); - spi_unregister_controller(cqspi->host); - if (cqspi->rx_chan) dma_release_channel(cqspi->rx_chan); - cqspi_controller_enable(cqspi, 0); - - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) ret = pm_runtime_get_sync(&pdev->dev); - if (ret >= 0) + if (ret >= 0) { + cqspi_controller_enable(cqspi, 0); clk_bulk_disable_unprepare(CLK_QSPI_NUM, cqspi->clks); + } if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); } } diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index caa7a57e6d27..891e2ba36958 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -741,7 +741,6 @@ static int cdns_spi_probe(struct platform_device *pdev) /* Set to default valid value */ ctlr->max_speed_hz = xspi->clk_rate / 4; xspi->speed_hz = ctlr->max_speed_hz; - pm_runtime_put_autosuspend(&pdev->dev); } else { ctlr->mode_bits |= SPI_NO_CS; ctlr->target_abort = cdns_target_abort; @@ -752,12 +751,17 @@ static int cdns_spi_probe(struct platform_device *pdev) goto clk_dis_all; } + if (!spi_controller_is_target(ctlr)) + pm_runtime_put_autosuspend(&pdev->dev); + return ret; clk_dis_all: if (!spi_controller_is_target(ctlr)) { pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); } remove_ctlr: spi_controller_put(ctlr); @@ -776,15 +780,26 @@ static void cdns_spi_remove(struct platform_device *pdev) { struct spi_controller *ctlr = platform_get_drvdata(pdev); struct cdns_spi *xspi = spi_controller_get_devdata(ctlr); + int ret = 0; - cdns_spi_write(xspi, CDNS_SPI_ER, CDNS_SPI_ER_DISABLE); + if (!spi_controller_is_target(ctlr)) + ret = pm_runtime_get_sync(&pdev->dev); + + spi_controller_get(ctlr); + + spi_unregister_controller(ctlr); + + if (ret >= 0) + cdns_spi_write(xspi, CDNS_SPI_ER, CDNS_SPI_ER_DISABLE); if (!spi_controller_is_target(ctlr)) { pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); } - spi_unregister_controller(ctlr); + spi_controller_put(ctlr); } /** diff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index 3eaa8f176f63..6448a44a8b67 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -250,5 +250,5 @@ static struct usb_driver ch341a_usb_driver = { module_usb_driver(ch341a_usb_driver); MODULE_AUTHOR("Johannes Thumshirn "); -MODULE_DESCRIPTION("QiHeng Electronics ch341 USB2SPI"); +MODULE_DESCRIPTION("Nanjing Qinheng Microelectronics CH341 USB2SPI driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 4747899e0646..480d1e8b281f 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1382,9 +1382,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, spi_imx->target_burst = t->len; } - spi_imx->devtype_data->prepare_transfer(spi_imx, spi, t); - - return 0; + return spi_imx->devtype_data->prepare_transfer(spi_imx, spi, t); } static void spi_imx_sdma_exit(struct spi_imx_data *spi_imx) @@ -1709,6 +1707,7 @@ static int spi_imx_dma_data_prepare(struct spi_imx_data *spi_imx, kfree(spi_imx->dma_data[0].dma_tx_buf); kfree(spi_imx->dma_data[0].dma_rx_buf); kfree(spi_imx->dma_data); + return ret; } } @@ -1836,7 +1835,7 @@ static void spi_imx_dma_max_wml_find(struct spi_imx_data *spi_imx, unsigned int i; for (i = spi_imx->devtype_data->fifo_size / 2; i > 0; i--) { - if (!dma_data->dma_len % (i * bytes_per_word)) + if (!(dma_data->dma_len % (i * bytes_per_word))) break; } /* Use 1 as wml in case no available burst length got */ @@ -2373,6 +2372,7 @@ out_register_controller: out_runtime_pm_put: pm_runtime_dont_use_autosuspend(spi_imx->dev); pm_runtime_disable(spi_imx->dev); + pm_runtime_put_noidle(spi_imx->dev); pm_runtime_set_suspended(&pdev->dev); clk_disable_unprepare(spi_imx->clk_ipg); diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c index eab059fb0bc2..4dee0fea1df8 100644 --- a/drivers/spi/spi-microchip-core-qspi.c +++ b/drivers/spi/spi-microchip-core-qspi.c @@ -74,6 +74,13 @@ #define STATUS_FLAGSX4 BIT(8) #define STATUS_MASK GENMASK(8, 0) +/* + * QSPI Direct Access register defines + */ +#define DIRECT_ACCESS_EN_SSEL BIT(0) +#define DIRECT_ACCESS_OP_SSEL BIT(1) +#define DIRECT_ACCESS_OP_SSEL_SHIFT 1 + #define BYTESUPPER_MASK GENMASK(31, 16) #define BYTESLOWER_MASK GENMASK(15, 0) @@ -158,7 +165,39 @@ static int mchp_coreqspi_set_mode(struct mchp_coreqspi *qspi, const struct spi_m return 0; } -static inline void mchp_coreqspi_read_op(struct mchp_coreqspi *qspi) +static void mchp_coreqspi_set_cs(struct spi_device *spi, bool enable) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata(spi->controller); + u32 val; + + val = readl(qspi->regs + REG_DIRECT_ACCESS); + + val &= ~DIRECT_ACCESS_OP_SSEL; + val |= !enable << DIRECT_ACCESS_OP_SSEL_SHIFT; + + writel(val, qspi->regs + REG_DIRECT_ACCESS); +} + +static int mchp_coreqspi_setup(struct spi_device *spi) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata(spi->controller); + u32 val; + + /* + * Active low devices need to be specifically set to their inactive + * states during probe. + */ + if (spi->mode & SPI_CS_HIGH) + return 0; + + val = readl(qspi->regs + REG_DIRECT_ACCESS); + val |= DIRECT_ACCESS_OP_SSEL; + writel(val, qspi->regs + REG_DIRECT_ACCESS); + + return 0; +} + +static void mchp_coreqspi_read_op(struct mchp_coreqspi *qspi) { u32 control, data; @@ -194,7 +233,7 @@ static inline void mchp_coreqspi_read_op(struct mchp_coreqspi *qspi) } } -static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi) +static void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi) { u32 control, data; @@ -222,7 +261,7 @@ static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi) } } -static inline void mchp_coreqspi_write_read_op(struct mchp_coreqspi *qspi) +static void mchp_coreqspi_write_read_op(struct mchp_coreqspi *qspi) { u32 control, data; @@ -380,20 +419,7 @@ static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_devi return 0; } -static int mchp_coreqspi_setup_op(struct spi_device *spi_dev) -{ - struct spi_controller *ctlr = spi_dev->controller; - struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); - u32 control = readl_relaxed(qspi->regs + REG_CONTROL); - - control |= (CONTROL_MASTER | CONTROL_ENABLE); - control &= ~CONTROL_CLKIDLE; - writel_relaxed(control, qspi->regs + REG_CONTROL); - - return 0; -} - -static inline void mchp_coreqspi_config_op(struct mchp_coreqspi *qspi, const struct spi_mem_op *op) +static void mchp_coreqspi_config_op(struct mchp_coreqspi *qspi, const struct spi_mem_op *op) { u32 idle_cycles = 0; int total_bytes, cmd_bytes, frames, ctrl; @@ -483,6 +509,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o reinit_completion(&qspi->data_completion); mchp_coreqspi_config_op(qspi, op); + mchp_coreqspi_set_cs(mem->spi, true); if (op->cmd.opcode) { qspi->txbuf = &opcode; qspi->rxbuf = NULL; @@ -523,6 +550,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o err = -ETIMEDOUT; error: + mchp_coreqspi_set_cs(mem->spi, false); mutex_unlock(&qspi->op_lock); mchp_coreqspi_disable_ints(qspi); @@ -662,18 +690,28 @@ static int mchp_coreqspi_transfer_one(struct spi_controller *ctlr, struct spi_de struct spi_transfer *t) { struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); + bool dual_quad = false; qspi->tx_len = t->len; + if (t->tx_nbits == SPI_NBITS_QUAD || t->rx_nbits == SPI_NBITS_QUAD || + t->tx_nbits == SPI_NBITS_DUAL || + t->rx_nbits == SPI_NBITS_DUAL) + dual_quad = true; + if (t->tx_buf) qspi->txbuf = (u8 *)t->tx_buf; if (!t->rx_buf) { mchp_coreqspi_write_op(qspi); - } else { + } else if (!dual_quad) { qspi->rxbuf = (u8 *)t->rx_buf; qspi->rx_len = t->len; mchp_coreqspi_write_read_op(qspi); + } else { + qspi->rxbuf = (u8 *)t->rx_buf; + qspi->rx_len = t->len; + mchp_coreqspi_read_op(qspi); } return 0; @@ -686,6 +724,7 @@ static int mchp_coreqspi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; int ret; + u32 num_cs, val; ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*qspi)); if (!ctlr) @@ -718,10 +757,18 @@ static int mchp_coreqspi_probe(struct platform_device *pdev) return ret; } + /* + * The IP core only has a single CS, any more have to be provided via + * gpios + */ + if (of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs)) + num_cs = 1; + + ctlr->num_chipselect = num_cs; + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); ctlr->mem_ops = &mchp_coreqspi_mem_ops; ctlr->mem_caps = &mchp_coreqspi_mem_caps; - ctlr->setup = mchp_coreqspi_setup_op; ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; ctlr->dev.of_node = np; @@ -729,9 +776,21 @@ static int mchp_coreqspi_probe(struct platform_device *pdev) ctlr->prepare_message = mchp_coreqspi_prepare_message; ctlr->unprepare_message = mchp_coreqspi_unprepare_message; ctlr->transfer_one = mchp_coreqspi_transfer_one; - ctlr->num_chipselect = 2; + ctlr->setup = mchp_coreqspi_setup; + ctlr->set_cs = mchp_coreqspi_set_cs; ctlr->use_gpio_descriptors = true; + val = readl_relaxed(qspi->regs + REG_CONTROL); + val |= (CONTROL_MASTER | CONTROL_ENABLE); + writel_relaxed(val, qspi->regs + REG_CONTROL); + + /* + * Put cs into software controlled mode + */ + val = readl_relaxed(qspi->regs + REG_DIRECT_ACCESS); + val |= DIRECT_ACCESS_EN_SSEL; + writel(val, qspi->regs + REG_DIRECT_ACCESS); + ret = spi_register_controller(ctlr); if (ret) return dev_err_probe(&pdev->dev, ret, diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index 05bbd3795e7d..924d820448fb 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -498,6 +498,9 @@ static int mpc52xx_spi_probe(struct platform_device *op) err_register: dev_err(&ms->host->dev, "initialization failed\n"); + free_irq(ms->irq0, ms); + free_irq(ms->irq1, ms); + cancel_work_sync(&ms->work); err_gpio: while (i-- > 0) gpiod_put(ms->gpio_cs[i]); @@ -517,15 +520,17 @@ static void mpc52xx_spi_remove(struct platform_device *op) struct mpc52xx_spi *ms = spi_controller_get_devdata(host); int i; - cancel_work_sync(&ms->work); + spi_unregister_controller(host); + free_irq(ms->irq0, ms); free_irq(ms->irq1, ms); + cancel_work_sync(&ms->work); + for (i = 0; i < ms->gpio_cs_count; i++) gpiod_put(ms->gpio_cs[i]); kfree(ms->gpio_cs); - spi_unregister_controller(host); iounmap(ms->regs); spi_controller_put(host); } diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c index 73fa84475f0e..e616e6800e92 100644 --- a/drivers/spi/spi-mtk-snfi.c +++ b/drivers/spi/spi-mtk-snfi.c @@ -1447,14 +1447,14 @@ static int mtk_snand_probe(struct platform_device *pdev) ret = nand_ecc_register_on_host_hw_engine(&ms->ecc_eng); if (ret) { dev_err(&pdev->dev, "failed to register ecc engine.\n"); - goto release_ecc; + goto free_buf; } ret = devm_add_action_or_reset(&pdev->dev, mtk_unregister_ecc_engine, &ms->ecc_eng); if (ret) { dev_err_probe(&pdev->dev, ret, "failed to add ECC unregister action\n"); - goto release_ecc; + goto free_buf; } ctlr->num_chipselect = 1; @@ -1465,10 +1465,12 @@ static int mtk_snand_probe(struct platform_device *pdev) ret = spi_register_controller(ctlr); if (ret) { dev_err(&pdev->dev, "spi_register_controller failed.\n"); - goto release_ecc; + goto free_buf; } return 0; +free_buf: + kfree(ms->buf); release_ecc: mtk_ecc_release(ms->ecc); return ret; diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c index f9369c69911c..b0e7fc828a50 100644 --- a/drivers/spi/spi-mxic.c +++ b/drivers/spi/spi-mxic.c @@ -832,9 +832,10 @@ static void mxic_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct mxic_spi *mxic = spi_controller_get_devdata(host); + spi_unregister_controller(host); + pm_runtime_disable(&pdev->dev); mxic_spi_mem_ecc_remove(mxic); - spi_unregister_controller(host); } static const struct of_device_id mxic_spi_of_ids[] = { diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 7a2186b51b4c..64bf215c1804 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -648,8 +648,8 @@ static int orion_spi_probe(struct platform_device *pdev) struct orion_spi *spi; struct resource *r; unsigned long tclk_hz; - int status = 0; struct device_node *np; + int status; host = spi_alloc_host(&pdev->dev, sizeof(*spi)); if (host == NULL) { @@ -774,6 +774,7 @@ static int orion_spi_probe(struct platform_device *pdev) pm_runtime_set_active(&pdev->dev); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); + pm_runtime_get_noresume(&pdev->dev); pm_runtime_enable(&pdev->dev); status = orion_spi_reset(spi); @@ -784,10 +785,15 @@ static int orion_spi_probe(struct platform_device *pdev) if (status < 0) goto out_rel_pm; - return status; + pm_runtime_put_autosuspend(&pdev->dev); + + return 0; out_rel_pm: pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); out_rel_axi_clk: clk_disable_unprepare(spi->axi_clk); out: @@ -801,11 +807,19 @@ static void orion_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct orion_spi *spi = spi_controller_get_devdata(host); + spi_controller_get(host); + + spi_unregister_controller(host); + pm_runtime_get_sync(&pdev->dev); clk_disable_unprepare(spi->axi_clk); - spi_unregister_controller(host); + spi_controller_put(host); + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); } MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 14cd1b9d9793..231fbcf0e7aa 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -98,7 +98,6 @@ #define CR0_FRF_MICROWIRE 0x2 #define CR0_XFM_OFFSET 18 -#define CR0_XFM_MASK (0x03 << SPI_XFM_OFFSET) #define CR0_XFM_TR 0x0 #define CR0_XFM_TO 0x1 #define CR0_XFM_RO 0x2 @@ -109,8 +108,6 @@ #define CR0_SOI_OFFSET 23 -#define CR0_MTM_OFFSET 0x21 - /* Bit fields in SER, 2bit */ #define SER_MASK 0x3 @@ -357,7 +354,8 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id) struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); /* When int_cs_inactive comes, spi target abort */ - if (rs->cs_inactive && readl_relaxed(rs->regs + ROCKCHIP_SPI_IMR) & INT_CS_INACTIVE) { + if (rs->cs_inactive && + (readl_relaxed(rs->regs + ROCKCHIP_SPI_ISR) & INT_CS_INACTIVE)) { ctlr->target_abort(ctlr); writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR); writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR); diff --git a/drivers/spi/spi-rzv2h-rspi.c b/drivers/spi/spi-rzv2h-rspi.c index f45af5884638..1655efda7d20 100644 --- a/drivers/spi/spi-rzv2h-rspi.c +++ b/drivers/spi/spi-rzv2h-rspi.c @@ -579,7 +579,7 @@ static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz) rspi->info->find_pclk_rate(rspi->pclk, hz, &best_clock); if (!best_clock.clk_rate) - return -EINVAL; + return 0; ret = clk_set_rate(best_clock.clk, best_clock.clk_rate); if (ret) diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index cae2dcefabea..14d11450e86d 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1406,8 +1406,9 @@ static void pch_spi_pd_remove(struct platform_device *plat_dev) dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n", __func__, plat_dev->id, board_dat->pdev->irq); - if (use_dma) - pch_free_dma_buf(board_dat, data); + spi_controller_get(data->host); + + spi_unregister_controller(data->host); /* check for any pending messages; no action is taken if the queue * is still full; but at least we tried. Unload anyway */ @@ -1432,8 +1433,12 @@ static void pch_spi_pd_remove(struct platform_device *plat_dev) free_irq(board_dat->pdev->irq, data); } + if (use_dma) + pch_free_dma_buf(board_dat, data); + pci_iounmap(board_dat->pdev, data->io_remap_addr); - spi_unregister_controller(data->host); + + spi_controller_put(data->host); } #ifdef CONFIG_PM static int pch_spi_pd_suspend(struct platform_device *pd_dev, diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 7001f5dce8bd..104279858f56 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -43,6 +43,8 @@ EXPORT_TRACEPOINT_SYMBOL(spi_transfer_stop); #include "internals.h" +static int __spi_setup(struct spi_device *spi, bool initial_setup); + static DEFINE_IDR(spi_controller_idr); static void spidev_release(struct device *dev) @@ -743,7 +745,7 @@ static int __spi_add_device(struct spi_device *spi, struct spi_device *parent) * normally rely on the device being setup. Devices * using SPI_CS_HIGH can't coexist well otherwise... */ - status = spi_setup(spi); + status = __spi_setup(spi, true); if (status < 0) { dev_err(dev, "can't setup %s, status %d\n", dev_name(&spi->dev), status); @@ -4049,27 +4051,7 @@ static int spi_set_cs_timing(struct spi_device *spi) return status; } -/** - * spi_setup - setup SPI mode and clock rate - * @spi: the device whose settings are being modified - * Context: can sleep, and no requests are queued to the device - * - * SPI protocol drivers may need to update the transfer mode if the - * device doesn't work with its default. They may likewise need - * to update clock rates or word sizes from initial values. This function - * changes those settings, and must be called from a context that can sleep. - * Except for SPI_CS_HIGH, which takes effect immediately, the changes take - * effect the next time the device is selected and data is transferred to - * or from it. When this function returns, the SPI device is deselected. - * - * Note that this call will fail if the protocol driver specifies an option - * that the underlying controller or its driver does not support. For - * example, not all hardware supports wire transfers using nine bit words, - * LSB-first wire encoding, or active-high chipselects. - * - * Return: zero on success, else a negative error code. - */ -int spi_setup(struct spi_device *spi) +static int __spi_setup(struct spi_device *spi, bool initial_setup) { unsigned bad_bits, ugly_bits; int status; @@ -4154,7 +4136,7 @@ int spi_setup(struct spi_device *spi) status = spi_set_cs_timing(spi); if (status) { mutex_unlock(&spi->controller->io_mutex); - return status; + goto err_cleanup; } if (spi->controller->auto_runtime_pm && spi->controller->set_cs) { @@ -4163,7 +4145,7 @@ int spi_setup(struct spi_device *spi) mutex_unlock(&spi->controller->io_mutex); dev_err(&spi->controller->dev, "Failed to power device: %d\n", status); - return status; + goto err_cleanup; } /* @@ -4199,6 +4181,37 @@ int spi_setup(struct spi_device *spi) status); return status; + +err_cleanup: + if (initial_setup) + spi_cleanup(spi); + + return status; +} + +/** + * spi_setup - setup SPI mode and clock rate + * @spi: the device whose settings are being modified + * Context: can sleep, and no requests are queued to the device + * + * SPI protocol drivers may need to update the transfer mode if the + * device doesn't work with its default. They may likewise need + * to update clock rates or word sizes from initial values. This function + * changes those settings, and must be called from a context that can sleep. + * Except for SPI_CS_HIGH, which takes effect immediately, the changes take + * effect the next time the device is selected and data is transferred to + * or from it. When this function returns, the SPI device is deselected. + * + * Note that this call will fail if the protocol driver specifies an option + * that the underlying controller or its driver does not support. For + * example, not all hardware supports wire transfers using nine bit words, + * LSB-first wire encoding, or active-high chipselects. + * + * Return: zero on success, else a negative error code. + */ +int spi_setup(struct spi_device *spi) +{ + return __spi_setup(spi, false); } EXPORT_SYMBOL_GPL(spi_setup); diff --git a/drivers/staging/axis-fifo/axis-fifo.c b/drivers/staging/axis-fifo/axis-fifo.c index aa90b27197cf..3aa2aa870ea9 100644 --- a/drivers/staging/axis-fifo/axis-fifo.c +++ b/drivers/staging/axis-fifo/axis-fifo.c @@ -71,8 +71,8 @@ struct axis_fifo { unsigned int rx_fifo_depth; unsigned int tx_fifo_depth; - int has_rx_fifo; - int has_tx_fifo; + u32 has_rx_fifo; + u32 has_tx_fifo; wait_queue_head_t read_queue; struct mutex read_lock; /* lock for reading */ @@ -392,60 +392,39 @@ static int axis_fifo_parse_dt(struct axis_fifo *fifo) ret = of_property_read_u32(node, "xlnx,axi-str-rxd-tdata-width", &value); - if (ret) { - dev_err(fifo->dt_device, "missing xlnx,axi-str-rxd-tdata-width property\n"); - goto end; - } else if (value != 32) { - dev_err(fifo->dt_device, "xlnx,axi-str-rxd-tdata-width only supports 32 bits\n"); - ret = -EIO; - goto end; - } + if (ret) + return ret; + if (value != 32) + return -EINVAL; ret = of_property_read_u32(node, "xlnx,axi-str-txd-tdata-width", &value); - if (ret) { - dev_err(fifo->dt_device, "missing xlnx,axi-str-txd-tdata-width property\n"); - goto end; - } else if (value != 32) { - dev_err(fifo->dt_device, "xlnx,axi-str-txd-tdata-width only supports 32 bits\n"); - ret = -EIO; - goto end; - } + if (ret) + return ret; + if (value != 32) + return -EINVAL; ret = of_property_read_u32(node, "xlnx,rx-fifo-depth", &fifo->rx_fifo_depth); - if (ret) { - dev_err(fifo->dt_device, "missing xlnx,rx-fifo-depth property\n"); - ret = -EIO; - goto end; - } + if (ret) + return ret; ret = of_property_read_u32(node, "xlnx,tx-fifo-depth", &fifo->tx_fifo_depth); - if (ret) { - dev_err(fifo->dt_device, "missing xlnx,tx-fifo-depth property\n"); - ret = -EIO; - goto end; - } + if (ret) + return ret; ret = of_property_read_u32(node, "xlnx,use-rx-data", &fifo->has_rx_fifo); - if (ret) { - dev_err(fifo->dt_device, "missing xlnx,use-rx-data property\n"); - ret = -EIO; - goto end; - } + if (ret) + return ret; ret = of_property_read_u32(node, "xlnx,use-tx-data", &fifo->has_tx_fifo); - if (ret) { - dev_err(fifo->dt_device, "missing xlnx,use-tx-data property\n"); - ret = -EIO; - goto end; - } + if (ret) + return ret; -end: - return ret; + return 0; } static int axis_fifo_probe(struct platform_device *pdev) diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig index 578412a2f379..92943564cb91 100644 --- a/drivers/staging/fbtft/Kconfig +++ b/drivers/staging/fbtft/Kconfig @@ -86,7 +86,11 @@ config FB_TFT_PCD8544 config FB_TFT_RA8875 tristate "FB driver for the RA8875 LCD Controller" help - Generic Framebuffer support for RA8875 + This enables generic framebuffer support for the RAiO RA8875 + display controller. The controller is intended for medium size text/graphic + mixed displays with a resolution of up to 800x480 pixels. + + Say Y if you have such a display that utilizes this controller. config FB_TFT_S6D02A1 tristate "FB driver for the S6D02A1 LCD Controller" diff --git a/drivers/staging/fbtft/README b/drivers/staging/fbtft/README index ba4c74c92e4c..91f152d622bd 100644 --- a/drivers/staging/fbtft/README +++ b/drivers/staging/fbtft/README @@ -6,27 +6,12 @@ The module 'fbtft' makes writing drivers for some of these displays very easy. Development is done on a Raspberry Pi running the Raspbian "wheezy" distribution. -INSTALLATION - Download kernel sources +For new hardware support consider using DRM subsystem (see TODO). - From Linux 3.15 - cd drivers/video/fbdev/fbtft - git clone https://github.com/notro/fbtft.git +NOTE: - Add to drivers/video/fbdev/Kconfig: source "drivers/video/fbdev/fbtft/Kconfig" - Add to drivers/video/fbdev/Makefile: obj-y += fbtft/ - - Before Linux 3.15 - cd drivers/video - git clone https://github.com/notro/fbtft.git - - Add to drivers/video/Kconfig: source "drivers/video/fbtft/Kconfig" - Add to drivers/video/Makefile: obj-y += fbtft/ - - Enable driver(s) in menuconfig and build the kernel - - -See wiki for more information: https://github.com/notro/fbtft/wiki - - -Source: https://github.com/notro/fbtft/ +The driver is in maintenance mode, only performance issue or bug fixes +are accepted, which effectively means the patches must be tested on +the real hardware (the patch must be accompanied with the information +what hardware is that). The treewide changes may also be accepted as +an exception. diff --git a/drivers/staging/fbtft/fb_agm1264k-fl.c b/drivers/staging/fbtft/fb_agm1264k-fl.c index af2dbebefc72..6fc8f4e9c814 100644 --- a/drivers/staging/fbtft/fb_agm1264k-fl.c +++ b/drivers/staging/fbtft/fb_agm1264k-fl.c @@ -376,7 +376,7 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) /* write bitmap */ gpiod_set_value(par->RS, 1); /* RS->1 (data mode) */ - par->fbtftops.write(par, buf, len); + ret = par->fbtftops.write(par, buf, len); if (ret < 0) dev_err(par->info->device, "write failed and returned: %d\n", diff --git a/drivers/staging/fbtft/fb_hx8340bn.c b/drivers/staging/fbtft/fb_hx8340bn.c index 2fd7b87ea0ce..ca27914f1412 100644 --- a/drivers/staging/fbtft/fb_hx8340bn.c +++ b/drivers/staging/fbtft/fb_hx8340bn.c @@ -106,13 +106,6 @@ static int init_display(struct fbtft_par *par) return 0; } -static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) -{ - write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, 0x00, xs, 0x00, xe); - write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, 0x00, ys, 0x00, ye); - write_reg(par, MIPI_DCS_WRITE_MEMORY_START); -} - static int set_var(struct fbtft_par *par) { /* MADCTL - Memory data access control */ @@ -207,7 +200,6 @@ static struct fbtft_display display = { .gamma = DEFAULT_GAMMA, .fbtftops = { .init_display = init_display, - .set_addr_win = set_addr_win, .set_var = set_var, .set_gamma = set_gamma, }, diff --git a/drivers/staging/fbtft/fb_hx8353d.c b/drivers/staging/fbtft/fb_hx8353d.c index 3e73b69b6a27..f6cd82df4da6 100644 --- a/drivers/staging/fbtft/fb_hx8353d.c +++ b/drivers/staging/fbtft/fb_hx8353d.c @@ -61,18 +61,6 @@ static int init_display(struct fbtft_par *par) return 0; }; -static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) -{ - /* column address */ - write_reg(par, 0x2a, xs >> 8, xs & 0xff, xe >> 8, xe & 0xff); - - /* Row address */ - write_reg(par, 0x2b, ys >> 8, ys & 0xff, ye >> 8, ye & 0xff); - - /* memory write */ - write_reg(par, 0x2c); -} - #define my BIT(7) #define mx BIT(6) #define mv BIT(5) @@ -130,7 +118,6 @@ static struct fbtft_display display = { .gamma = DEFAULT_GAMMA, .fbtftops = { .init_display = init_display, - .set_addr_win = set_addr_win, .set_var = set_var, .set_gamma = set_gamma, }, diff --git a/drivers/staging/fbtft/fb_hx8357d.c b/drivers/staging/fbtft/fb_hx8357d.c index 94a357e8fdf6..7b9f020a956f 100644 --- a/drivers/staging/fbtft/fb_hx8357d.c +++ b/drivers/staging/fbtft/fb_hx8357d.c @@ -129,19 +129,6 @@ static int init_display(struct fbtft_par *par) return 0; } -static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) -{ - write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, - xs >> 8, xs & 0xff, /* XSTART */ - xe >> 8, xe & 0xff); /* XEND */ - - write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, - ys >> 8, ys & 0xff, /* YSTART */ - ye >> 8, ye & 0xff); /* YEND */ - - write_reg(par, MIPI_DCS_WRITE_MEMORY_START); -} - #define HX8357D_MADCTL_MY 0x80 #define HX8357D_MADCTL_MX 0x40 #define HX8357D_MADCTL_MV 0x20 @@ -184,7 +171,6 @@ static struct fbtft_display display = { .gamma_len = 14, .fbtftops = { .init_display = init_display, - .set_addr_win = set_addr_win, .set_var = set_var, }, }; diff --git a/drivers/staging/fbtft/fb_ili9163.c b/drivers/staging/fbtft/fb_ili9163.c index 6582a2c90aaf..a2b5033a9860 100644 --- a/drivers/staging/fbtft/fb_ili9163.c +++ b/drivers/staging/fbtft/fb_ili9163.c @@ -60,7 +60,7 @@ * configure to constrain the memory and resolution to a fixed dimension (in * that case 128x128) but they leaved those pins configured for 128x160 so * there was several pixel memory addressing problems. - * I solved by setup several parameters that dinamically fix the resolution as + * I solved by setup several parameters that dynamically fix the resolution as * needit so below the parameters for this display. If you have a strain or a * correct display (can happen with chinese) you can copy those parameters and * create setup for different displays. diff --git a/drivers/staging/fbtft/fb_ili9340.c b/drivers/staging/fbtft/fb_ili9340.c index 704236bcaf3f..023d8cb96f95 100644 --- a/drivers/staging/fbtft/fb_ili9340.c +++ b/drivers/staging/fbtft/fb_ili9340.c @@ -78,17 +78,6 @@ static int init_display(struct fbtft_par *par) return 0; } -static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) -{ - write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, - xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF); - - write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, - ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF); - - write_reg(par, MIPI_DCS_WRITE_MEMORY_START); -} - #define ILI9340_MADCTL_MV 0x20 #define ILI9340_MADCTL_MX 0x40 #define ILI9340_MADCTL_MY 0x80 @@ -122,7 +111,6 @@ static struct fbtft_display display = { .height = HEIGHT, .fbtftops = { .init_display = init_display, - .set_addr_win = set_addr_win, .set_var = set_var, }, }; diff --git a/drivers/staging/fbtft/fb_ili9341.c b/drivers/staging/fbtft/fb_ili9341.c index 47e72b87d76d..428922dee9f9 100644 --- a/drivers/staging/fbtft/fb_ili9341.c +++ b/drivers/staging/fbtft/fb_ili9341.c @@ -65,17 +65,6 @@ static int init_display(struct fbtft_par *par) return 0; } -static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) -{ - write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, - (xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF); - - write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, - (ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF); - - write_reg(par, MIPI_DCS_WRITE_MEMORY_START); -} - #define MEM_Y BIT(7) /* MY row address order */ #define MEM_X BIT(6) /* MX column address order */ #define MEM_V BIT(5) /* MV row / column exchange */ @@ -139,7 +128,6 @@ static struct fbtft_display display = { .gamma = DEFAULT_GAMMA, .fbtftops = { .init_display = init_display, - .set_addr_win = set_addr_win, .set_var = set_var, .set_gamma = set_gamma, }, diff --git a/drivers/staging/fbtft/fb_ili9481.c b/drivers/staging/fbtft/fb_ili9481.c index 19eba085ea53..5f31b5d5590f 100644 --- a/drivers/staging/fbtft/fb_ili9481.c +++ b/drivers/staging/fbtft/fb_ili9481.c @@ -42,17 +42,6 @@ static const s16 default_init_sequence[] = { -3 }; -static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) -{ - write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, - xs >> 8, xs & 0xff, xe >> 8, xe & 0xff); - - write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, - ys >> 8, ys & 0xff, ye >> 8, ye & 0xff); - - write_reg(par, MIPI_DCS_WRITE_MEMORY_START); -} - #define HFLIP 0x01 #define VFLIP 0x02 #define ROW_X_COL 0x20 @@ -86,7 +75,6 @@ static struct fbtft_display display = { .height = HEIGHT, .init_sequence = default_init_sequence, .fbtftops = { - .set_addr_win = set_addr_win, .set_var = set_var, }, }; diff --git a/drivers/staging/fbtft/fb_ili9486.c b/drivers/staging/fbtft/fb_ili9486.c index 66210a7137fc..a4d699ef57e4 100644 --- a/drivers/staging/fbtft/fb_ili9486.c +++ b/drivers/staging/fbtft/fb_ili9486.c @@ -43,17 +43,6 @@ static const s16 default_init_sequence[] = { -3 }; -static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) -{ - write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, - xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF); - - write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, - ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF); - - write_reg(par, MIPI_DCS_WRITE_MEMORY_START); -} - static int set_var(struct fbtft_par *par) { switch (par->info->var.rotate) { @@ -86,7 +75,6 @@ static struct fbtft_display display = { .height = HEIGHT, .init_sequence = default_init_sequence, .fbtftops = { - .set_addr_win = set_addr_win, .set_var = set_var, }, }; diff --git a/drivers/staging/fbtft/fb_s6d02a1.c b/drivers/staging/fbtft/fb_s6d02a1.c index d3d6871d8c47..d8ddc804d626 100644 --- a/drivers/staging/fbtft/fb_s6d02a1.c +++ b/drivers/staging/fbtft/fb_s6d02a1.c @@ -97,17 +97,6 @@ static const s16 default_init_sequence[] = { }; -static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) -{ - write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, - xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF); - - write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, - ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF); - - write_reg(par, MIPI_DCS_WRITE_MEMORY_START); -} - #define MY BIT(7) #define MX BIT(6) #define MV BIT(5) @@ -149,7 +138,6 @@ static struct fbtft_display display = { .height = 160, .init_sequence = default_init_sequence, .fbtftops = { - .set_addr_win = set_addr_win, .set_var = set_var, }, }; diff --git a/drivers/staging/fbtft/fb_st7735r.c b/drivers/staging/fbtft/fb_st7735r.c index 9670a8989b91..6d9735fa2332 100644 --- a/drivers/staging/fbtft/fb_st7735r.c +++ b/drivers/staging/fbtft/fb_st7735r.c @@ -83,17 +83,6 @@ static const s16 default_init_sequence[] = { -3 }; -static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) -{ - write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, - xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF); - - write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, - ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF); - - write_reg(par, MIPI_DCS_WRITE_MEMORY_START); -} - #define MY BIT(7) #define MX BIT(6) #define MV BIT(5) @@ -168,7 +157,6 @@ static struct fbtft_display display = { .gamma_len = 16, .gamma = DEFAULT_GAMMA, .fbtftops = { - .set_addr_win = set_addr_win, .set_var = set_var, .set_gamma = set_gamma, }, diff --git a/drivers/staging/fbtft/fb_tinylcd.c b/drivers/staging/fbtft/fb_tinylcd.c index 9469248f2c50..afa8f1c740c5 100644 --- a/drivers/staging/fbtft/fb_tinylcd.c +++ b/drivers/staging/fbtft/fb_tinylcd.c @@ -41,23 +41,12 @@ static int init_display(struct fbtft_par *par) 0x00, 0x35, 0x33, 0x00, 0x00, 0x00); write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, 0x55); write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE); - udelay(250); + fsleep(250); write_reg(par, MIPI_DCS_SET_DISPLAY_ON); return 0; } -static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) -{ - write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, - xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF); - - write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, - ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF); - - write_reg(par, MIPI_DCS_WRITE_MEMORY_START); -} - static int set_var(struct fbtft_par *par) { switch (par->info->var.rotate) { @@ -88,7 +77,6 @@ static struct fbtft_display display = { .height = HEIGHT, .fbtftops = { .init_display = init_display, - .set_addr_win = set_addr_win, .set_var = set_var, }, }; diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c index f427c0914907..3da42c8ca6e3 100644 --- a/drivers/staging/fbtft/fbtft-core.c +++ b/drivers/staging/fbtft/fbtft-core.c @@ -300,12 +300,6 @@ static void fbtft_mkdirty(struct fb_info *info, int y, int height) struct fbtft_par *par = info->par; struct fb_deferred_io *fbdefio = info->fbdefio; - /* special case, needed ? */ - if (y == -1) { - y = 0; - height = info->var.yres; - } - /* Mark display lines/area as dirty */ spin_lock(&par->dirty_lock); if (y < par->dirty_lines_start) @@ -413,9 +407,12 @@ static int fbtft_fb_blank(int blank, struct fb_info *info) static void fbtft_ops_damage_range(struct fb_info *info, off_t off, size_t len) { struct fbtft_par *par = info->par; + u32 start, end; - /* TODO: only mark changed area update all for now */ - par->fbtftops.mkdirty(info, -1, 0); + start = off / info->fix.line_length; + end = (off + len - 1) / info->fix.line_length; + + par->fbtftops.mkdirty(info, start, end - start + 1); } static void fbtft_ops_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u32 height) diff --git a/drivers/staging/greybus/Documentation/firmware/authenticate.c b/drivers/staging/greybus/Documentation/firmware/authenticate.c index 3d2c6f88a138..0ef88b7d24de 100644 --- a/drivers/staging/greybus/Documentation/firmware/authenticate.c +++ b/drivers/staging/greybus/Documentation/firmware/authenticate.c @@ -58,7 +58,7 @@ int main(int argc, char *argv[]) goto close_fd; } - printf("UID received: 0x%llx\n", *(unsigned long long int *)(uid.uid)); + printf("UID received: 0x%llx\n", *(unsigned long long *)(uid.uid)); /* Get certificate */ printf("Get IMS certificate\n"); diff --git a/drivers/staging/greybus/Kconfig b/drivers/staging/greybus/Kconfig index 1e745a8d439c..7635f3e850fa 100644 --- a/drivers/staging/greybus/Kconfig +++ b/drivers/staging/greybus/Kconfig @@ -123,7 +123,7 @@ menuconfig GREYBUS_BRIDGED_PHY help Select this option to pick from a variety of Greybus Bridged PHY class drivers. These drivers emulate a number of - different "traditional" busses by tunneling them over Greybus. + different "traditional" buses by tunneling them over Greybus. Examples of this include serial, SPI, USB, and others. To compile this code as a module, chose M here: the module diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c index 33f26a65f0cc..19a6e59b6d5c 100644 --- a/drivers/staging/greybus/arche-apb-ctrl.c +++ b/drivers/staging/greybus/arche-apb-ctrl.c @@ -300,16 +300,16 @@ static ssize_t state_show(struct device *dev, switch (apb->state) { case ARCHE_PLATFORM_STATE_OFF: - return sprintf(buf, "off%s\n", + return sysfs_emit(buf, "off%s\n", apb->init_disabled ? ",disabled" : ""); case ARCHE_PLATFORM_STATE_ACTIVE: - return sprintf(buf, "active\n"); + return sysfs_emit(buf, "active\n"); case ARCHE_PLATFORM_STATE_STANDBY: - return sprintf(buf, "standby\n"); + return sysfs_emit(buf, "standby\n"); case ARCHE_PLATFORM_STATE_FW_FLASHING: - return sprintf(buf, "fw_flashing\n"); + return sysfs_emit(buf, "fw_flashing\n"); default: - return sprintf(buf, "unknown state\n"); + return sysfs_emit(buf, "unknown state\n"); } } diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index f669a7e2eb11..de5de59ea8ab 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -374,15 +374,15 @@ static ssize_t state_show(struct device *dev, switch (arche_pdata->state) { case ARCHE_PLATFORM_STATE_OFF: - return sprintf(buf, "off\n"); + return sysfs_emit(buf, "off\n"); case ARCHE_PLATFORM_STATE_ACTIVE: - return sprintf(buf, "active\n"); + return sysfs_emit(buf, "active\n"); case ARCHE_PLATFORM_STATE_STANDBY: - return sprintf(buf, "standby\n"); + return sysfs_emit(buf, "standby\n"); case ARCHE_PLATFORM_STATE_FW_FLASHING: - return sprintf(buf, "fw_flashing\n"); + return sysfs_emit(buf, "fw_flashing\n"); default: - return sprintf(buf, "unknown state\n"); + return sysfs_emit(buf, "unknown state\n"); } } diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index 444c53b4e08d..720aa752e17e 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -781,7 +781,7 @@ static int gbaudio_init_jack(struct gbaudio_module_info *module, ret = snd_jack_set_key(module->button.jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); if (ret) { - dev_err(module->dev, "Failed to set BTN_0\n"); + dev_err(module->dev, "Failed to set BTN_3\n"); goto free_jacks; } } diff --git a/drivers/staging/greybus/audio_manager.c b/drivers/staging/greybus/audio_manager.c index 27ca5f796c5f..118ada9b909b 100644 --- a/drivers/staging/greybus/audio_manager.c +++ b/drivers/staging/greybus/audio_manager.c @@ -105,18 +105,6 @@ void gb_audio_manager_remove_all(void) } EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all); -struct gb_audio_manager_module *gb_audio_manager_get_module(int id) -{ - struct gb_audio_manager_module *module; - - down_read(&modules_rwsem); - module = gb_audio_manager_get_locked(id); - kobject_get(&module->kobj); - up_read(&modules_rwsem); - return module; -} -EXPORT_SYMBOL_GPL(gb_audio_manager_get_module); - void gb_audio_manager_put_module(struct gb_audio_manager_module *module) { kobject_put(&module->kobj); diff --git a/drivers/staging/greybus/audio_manager.h b/drivers/staging/greybus/audio_manager.h index be605485a8ce..c3ef62ee22c8 100644 --- a/drivers/staging/greybus/audio_manager.h +++ b/drivers/staging/greybus/audio_manager.h @@ -54,13 +54,6 @@ int gb_audio_manager_remove(int id); */ void gb_audio_manager_remove_all(void); -/* - * Retrieves a gb_audio_manager_module_descriptor for the specified id. - * Returns the gb_audio_manager_module_descriptor structure, - * or NULL if there is no module with the specified ID. - */ -struct gb_audio_manager_module *gb_audio_manager_get_module(int id); - /* * Decreases the refcount of the module, obtained by the get function. * Modules are removed via gb_audio_manager_remove diff --git a/drivers/staging/greybus/audio_manager_module.c b/drivers/staging/greybus/audio_manager_module.c index e87b82ca6d8a..dc90cc2d2308 100644 --- a/drivers/staging/greybus/audio_manager_module.c +++ b/drivers/staging/greybus/audio_manager_module.c @@ -69,14 +69,13 @@ static void gb_audio_module_release(struct kobject *kobj) struct gb_audio_manager_module *module = to_gb_audio_module(kobj); pr_info("Destroying audio module #%d\n", module->id); - /* TODO -> delete from list */ kfree(module); } static ssize_t gb_audio_module_name_show(struct gb_audio_manager_module *module, struct gb_audio_manager_module_attribute *attr, char *buf) { - return sprintf(buf, "%s", module->desc.name); + return sysfs_emit(buf, "%s", module->desc.name); } static struct gb_audio_manager_module_attribute gb_audio_module_name_attribute = @@ -85,7 +84,7 @@ static struct gb_audio_manager_module_attribute gb_audio_module_name_attribute = static ssize_t gb_audio_module_vid_show(struct gb_audio_manager_module *module, struct gb_audio_manager_module_attribute *attr, char *buf) { - return sprintf(buf, "%d", module->desc.vid); + return sysfs_emit(buf, "%d", module->desc.vid); } static struct gb_audio_manager_module_attribute gb_audio_module_vid_attribute = @@ -94,7 +93,7 @@ static struct gb_audio_manager_module_attribute gb_audio_module_vid_attribute = static ssize_t gb_audio_module_pid_show(struct gb_audio_manager_module *module, struct gb_audio_manager_module_attribute *attr, char *buf) { - return sprintf(buf, "%d", module->desc.pid); + return sysfs_emit(buf, "%d", module->desc.pid); } static struct gb_audio_manager_module_attribute gb_audio_module_pid_attribute = @@ -104,7 +103,7 @@ static ssize_t gb_audio_module_intf_id_show(struct gb_audio_manager_module *modu struct gb_audio_manager_module_attribute *attr, char *buf) { - return sprintf(buf, "%d", module->desc.intf_id); + return sysfs_emit(buf, "%d", module->desc.intf_id); } static struct gb_audio_manager_module_attribute @@ -115,7 +114,7 @@ static ssize_t gb_audio_module_ip_devices_show(struct gb_audio_manager_module *m struct gb_audio_manager_module_attribute *attr, char *buf) { - return sprintf(buf, "0x%X", module->desc.ip_devices); + return sysfs_emit(buf, "0x%X", module->desc.ip_devices); } static struct gb_audio_manager_module_attribute @@ -126,7 +125,7 @@ static ssize_t gb_audio_module_op_devices_show(struct gb_audio_manager_module *m struct gb_audio_manager_module_attribute *attr, char *buf) { - return sprintf(buf, "0x%X", module->desc.op_devices); + return sysfs_emit(buf, "0x%X", module->desc.op_devices); } static struct gb_audio_manager_module_attribute diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index 1f58c907c036..f1f9f6fbc00e 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -201,7 +201,7 @@ static void gb_hid_init_report(struct gb_hid *ghid, struct hid_report *report) * we just need to setup the input fields, so using * hid_report_raw_event is safe. */ - hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, size, 1); + hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, ghid->bufsize, size, 1); } static void gb_hid_init_reports(struct gb_hid *ghid) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index aa9c73cb0ae5..ea57b1f5d156 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -125,7 +125,7 @@ static ssize_t field##_show(struct device *dev, \ char *buf) \ { \ struct gb_loopback *gb = dev_get_drvdata(dev); \ - return sprintf(buf, "%u\n", gb->field); \ + return sysfs_emit(buf, "%u\n", gb->field); \ } \ static DEVICE_ATTR_RO(field) @@ -137,8 +137,8 @@ static ssize_t name##_##field##_show(struct device *dev, \ struct gb_loopback *gb = dev_get_drvdata(dev); \ /* Report 0 for min and max if no transfer succeeded */ \ if (!gb->requests_completed) \ - return sprintf(buf, "0\n"); \ - return sprintf(buf, "%" #type "\n", gb->name.field); \ + return sysfs_emit(buf, "0\n"); \ + return sysfs_emit(buf, "%" #type "\n", gb->name.field); \ } \ static DEVICE_ATTR_RO(name##_##field) @@ -158,7 +158,7 @@ static ssize_t name##_avg_show(struct device *dev, \ rem = do_div(avg, count); \ rem *= 1000000; \ do_div(rem, count); \ - return sprintf(buf, "%llu.%06u\n", avg, (u32)rem); \ + return sysfs_emit(buf, "%llu.%06u\n", avg, (u32)rem); \ } \ static DEVICE_ATTR_RO(name##_avg) @@ -173,7 +173,7 @@ static ssize_t field##_show(struct device *dev, \ char *buf) \ { \ struct gb_loopback *gb = dev_get_drvdata(dev); \ - return sprintf(buf, "%" #type "\n", gb->field); \ + return sysfs_emit(buf, "%" #type "\n", gb->field); \ } \ static ssize_t field##_store(struct device *dev, \ struct device_attribute *attr, \ @@ -193,13 +193,13 @@ static ssize_t field##_store(struct device *dev, \ } \ static DEVICE_ATTR_RW(field) -#define gb_dev_loopback_ro_attr(field, conn) \ +#define gb_dev_loopback_ro_attr(field) \ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct gb_loopback *gb = dev_get_drvdata(dev); \ - return sprintf(buf, "%u\n", gb->field); \ + return sysfs_emit(buf, "%u\n", gb->field); \ } \ static DEVICE_ATTR_RO(field) @@ -209,7 +209,7 @@ static ssize_t field##_show(struct device *dev, \ char *buf) \ { \ struct gb_loopback *gb = dev_get_drvdata(dev); \ - return sprintf(buf, "%" #type "\n", gb->field); \ + return sysfs_emit(buf, "%" #type "\n", gb->field); \ } \ static ssize_t field##_store(struct device *dev, \ struct device_attribute *attr, \ @@ -305,7 +305,7 @@ gb_dev_loopback_rw_attr(us_wait, d); /* Maximum iterations for a given operation: 1-(2^32-1), 0 implies infinite */ gb_dev_loopback_rw_attr(iteration_max, u); /* The current index of the for (i = 0; i < iteration_max; i++) loop */ -gb_dev_loopback_ro_attr(iteration_count, false); +gb_dev_loopback_ro_attr(iteration_count); /* A flag to indicate synchronous or asynchronous operations */ gb_dev_loopback_rw_attr(async, u); /* Timeout of an individual asynchronous request */ diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index 1cb7b9089ead..52515a4acb12 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -215,7 +215,7 @@ static int gb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, /* * Set period and duty cycle * - * PWM privodes 64-bit period and duty_cycle, but greybus only accepts + * PWM provides 64-bit period and duty_cycle, but greybus only accepts * 32-bit, so their values have to be limited to U32_MAX. */ if (period > U32_MAX) diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index 3027a2c25bcd..459aed0f1240 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -21,9 +21,10 @@ struct gb_raw { struct list_head list; int list_data; struct mutex list_lock; - dev_t dev; + struct rw_semaphore disconnect_lock; + bool disconnected; struct cdev cdev; - struct device *device; + struct device dev; }; struct raw_data { @@ -148,6 +149,15 @@ static int gb_raw_send(struct gb_raw *raw, u32 len, const char __user *data) return retval; } +static void raw_dev_release(struct device *dev) +{ + struct gb_raw *raw = container_of(dev, struct gb_raw, dev); + + ida_free(&minors, MINOR(raw->dev.devt)); + + kfree(raw); +} + static int gb_raw_probe(struct gb_bundle *bundle, const struct greybus_bundle_id *id) { @@ -164,63 +174,59 @@ static int gb_raw_probe(struct gb_bundle *bundle, if (cport_desc->protocol_id != GREYBUS_PROTOCOL_RAW) return -ENODEV; - raw = kzalloc_obj(*raw); - if (!raw) + minor = ida_alloc(&minors, GFP_KERNEL); + if (minor < 0) + return minor; + + raw = kzalloc_obj(*raw, GFP_KERNEL); + if (!raw) { + ida_free(&minors, minor); return -ENOMEM; + } + + device_initialize(&raw->dev); + raw->dev.devt = MKDEV(raw_major, minor); + raw->dev.class = &raw_class; + raw->dev.parent = &bundle->dev; + raw->dev.release = raw_dev_release; + retval = dev_set_name(&raw->dev, "gb!raw%d", minor); + if (retval) + goto error_put_device; connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), gb_raw_request_handler); if (IS_ERR(connection)) { retval = PTR_ERR(connection); - goto error_free; + goto error_put_device; } INIT_LIST_HEAD(&raw->list); mutex_init(&raw->list_lock); + init_rwsem(&raw->disconnect_lock); raw->connection = connection; greybus_set_drvdata(bundle, raw); - minor = ida_alloc(&minors, GFP_KERNEL); - if (minor < 0) { - retval = minor; - goto error_connection_destroy; - } - - raw->dev = MKDEV(raw_major, minor); cdev_init(&raw->cdev, &raw_fops); retval = gb_connection_enable(connection); if (retval) - goto error_remove_ida; + goto error_connection_destroy; - retval = cdev_add(&raw->cdev, raw->dev, 1); + retval = cdev_device_add(&raw->cdev, &raw->dev); if (retval) goto error_connection_disable; - raw->device = device_create(&raw_class, &connection->bundle->dev, - raw->dev, raw, "gb!raw%d", minor); - if (IS_ERR(raw->device)) { - retval = PTR_ERR(raw->device); - goto error_del_cdev; - } - return 0; -error_del_cdev: - cdev_del(&raw->cdev); - error_connection_disable: gb_connection_disable(connection); -error_remove_ida: - ida_free(&minors, minor); - error_connection_destroy: gb_connection_destroy(connection); -error_free: - kfree(raw); +error_put_device: + put_device(&raw->dev); return retval; } @@ -231,11 +237,13 @@ static void gb_raw_disconnect(struct gb_bundle *bundle) struct raw_data *raw_data; struct raw_data *temp; - // FIXME - handle removing a connection when the char device node is open. - device_destroy(&raw_class, raw->dev); - cdev_del(&raw->cdev); + cdev_device_del(&raw->cdev, &raw->dev); + + down_write(&raw->disconnect_lock); + raw->disconnected = true; + up_write(&raw->disconnect_lock); + gb_connection_disable(connection); - ida_free(&minors, MINOR(raw->dev)); gb_connection_destroy(connection); mutex_lock(&raw->list_lock); @@ -244,8 +252,7 @@ static void gb_raw_disconnect(struct gb_bundle *bundle) kfree(raw_data); } mutex_unlock(&raw->list_lock); - - kfree(raw); + put_device(&raw->dev); } /* @@ -278,11 +285,22 @@ static ssize_t raw_write(struct file *file, const char __user *buf, if (count > MAX_PACKET_SIZE) return -E2BIG; + down_read(&raw->disconnect_lock); + + if (raw->disconnected) { + retval = -ENODEV; + goto exit; + } + retval = gb_raw_send(raw, count, buf); if (retval) - return retval; + goto exit; - return count; + retval = count; +exit: + up_read(&raw->disconnect_lock); + + return retval; } static ssize_t raw_read(struct file *file, char __user *buf, size_t count, diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 12c36a5e1d8c..3952f3d225db 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -206,7 +206,7 @@ static int gb_sdio_request_handler(struct gb_operation *op) struct gb_message *request; struct gb_sdio_event_request *payload; u8 type = op->type; - int ret = 0; + int ret = 0; u8 event; if (type != GB_SDIO_TYPE_EVENT) { @@ -372,7 +372,7 @@ static int gb_sdio_transfer(struct gb_sdio_host *host, struct mmc_data *data) left = data->blksz * data->blocks; while (left) { - /* check is a stop transmission is pending */ + /* check if a stop transmission is pending */ spin_lock(&host->xfer); if (host->xfer_stop) { host->xfer_stop = false; diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c index 172acf135f3b..0e32a2295990 100644 --- a/drivers/staging/iio/adc/ad7816.c +++ b/drivers/staging/iio/adc/ad7816.c @@ -124,8 +124,8 @@ static ssize_t ad7816_show_mode(struct device *dev, struct ad7816_chip_info *chip = iio_priv(indio_dev); if (chip->mode) - return sprintf(buf, "power-save\n"); - return sprintf(buf, "full\n"); + return sysfs_emit(buf, "power-save\n"); + return sysfs_emit(buf, "full\n"); } static ssize_t ad7816_store_mode(struct device *dev, @@ -156,7 +156,7 @@ static ssize_t ad7816_show_available_modes(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "full\npower-save\n"); + return sysfs_emit(buf, "full\npower-save\n"); } static IIO_DEVICE_ATTR(available_modes, 0444, ad7816_show_available_modes, @@ -169,7 +169,7 @@ static ssize_t ad7816_show_channel(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7816_chip_info *chip = iio_priv(indio_dev); - return sprintf(buf, "%d\n", chip->channel_id); + return sysfs_emit(buf, "%d\n", chip->channel_id); } static ssize_t ad7816_store_channel(struct device *dev, @@ -231,9 +231,9 @@ static ssize_t ad7816_show_value(struct device *dev, data &= AD7816_TEMP_FLOAT_MASK; if (value < 0) data = BIT(AD7816_TEMP_FLOAT_OFFSET) - data; - return sprintf(buf, "%d.%.2d\n", value, data * 25); + return sysfs_emit(buf, "%d.%.2d\n", value, data * 25); } - return sprintf(buf, "%u\n", data); + return sysfs_emit(buf, "%u\n", data); } static IIO_DEVICE_ATTR(value, 0444, ad7816_show_value, NULL, 0); @@ -281,9 +281,9 @@ static ssize_t ad7816_show_oti(struct device *dev, value = AD7816_BOUND_VALUE_MIN + (chip->oti_data[chip->channel_id] - AD7816_BOUND_VALUE_BASE); - return sprintf(buf, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } - return sprintf(buf, "%u\n", chip->oti_data[chip->channel_id]); + return sysfs_emit(buf, "%u\n", chip->oti_data[chip->channel_id]); } static inline ssize_t ad7816_set_oti(struct device *dev, diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index d339d5e8e043..bdb2580e29bf 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -281,16 +281,12 @@ ssize_t ad9834_show_out0_wavetype_available(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad9834_state *st = iio_priv(indio_dev); - char *str; if (st->devid == ID_AD9833 || st->devid == ID_AD9837) - str = "sine triangle square"; - else if (st->control & AD9834_OPBITEN) - str = "sine"; - else - str = "sine triangle"; - - return sprintf(buf, "%s\n", str); + return sysfs_emit(buf, "sine triangle square\n"); + if (st->control & AD9834_OPBITEN) + return sysfs_emit(buf, "sine\n"); + return sysfs_emit(buf, "sine triangle\n"); } static IIO_DEVICE_ATTR(out_altvoltage0_out0_wavetype_available, 0444, @@ -303,14 +299,10 @@ ssize_t ad9834_show_out1_wavetype_available(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad9834_state *st = iio_priv(indio_dev); - char *str; if (st->control & AD9834_MODE) - str = ""; - else - str = "square"; - - return sprintf(buf, "%s\n", str); + return sysfs_emit(buf, "\n"); + return sysfs_emit(buf, "square\n"); } static IIO_DEVICE_ATTR(out_altvoltage0_out1_wavetype_available, 0444, diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index 85a4223295cd..dde2ec9d1f6a 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -5,6 +5,7 @@ * Copyright 2011 Analog Devices Inc. */ +#include #include #include #include @@ -194,8 +195,7 @@ static int ad5933_set_freq(struct ad5933_state *st, u8 d8[4]; } dat; - freqreg = (u64)freq * (u64)(1 << 27); - do_div(freqreg, st->mclk_hz / 4); + freqreg = div64_ul(BIT_ULL(27) * freq, st->mclk_hz / 4); switch (reg) { case AD5933_REG_FREQ_START: @@ -285,7 +285,7 @@ static ssize_t ad5933_show_frequency(struct device *dev, freqreg = (u64)freqreg * (u64)(st->mclk_hz / 4); do_div(freqreg, BIT(27)); - return sprintf(buf, "%d\n", (int)freqreg); + return sysfs_emit(buf, "%llu\n", freqreg); } static ssize_t ad5933_store_frequency(struct device *dev, @@ -338,27 +338,27 @@ static ssize_t ad5933_show(struct device *dev, mutex_lock(&st->lock); switch ((u32)this_attr->address) { case AD5933_OUT_RANGE: - len = sprintf(buf, "%u\n", - st->range_avail[(st->ctrl_hb >> 1) & 0x3]); + len = sysfs_emit(buf, "%u\n", + st->range_avail[(st->ctrl_hb >> 1) & 0x3]); break; case AD5933_OUT_RANGE_AVAIL: - len = sprintf(buf, "%u %u %u %u\n", st->range_avail[0], - st->range_avail[3], st->range_avail[2], - st->range_avail[1]); + len = sysfs_emit(buf, "%u %u %u %u\n", st->range_avail[0], + st->range_avail[3], st->range_avail[2], + st->range_avail[1]); break; case AD5933_OUT_SETTLING_CYCLES: - len = sprintf(buf, "%d\n", st->settling_cycles); + len = sysfs_emit(buf, "%d\n", st->settling_cycles); break; case AD5933_IN_PGA_GAIN: - len = sprintf(buf, "%s\n", - (st->ctrl_hb & AD5933_CTRL_PGA_GAIN_1) ? - "1" : "0.2"); + len = sysfs_emit(buf, "%s\n", + (st->ctrl_hb & AD5933_CTRL_PGA_GAIN_1) ? + "1" : "0.2"); break; case AD5933_IN_PGA_GAIN_AVAIL: - len = sprintf(buf, "1 0.2\n"); + len = sysfs_emit(buf, "1 0.2\n"); break; case AD5933_FREQ_POINTS: - len = sprintf(buf, "%d\n", st->freq_points); + len = sysfs_emit(buf, "%d\n", st->freq_points); break; default: ret = -EINVAL; diff --git a/drivers/staging/most/Documentation/driver_usage.txt b/drivers/staging/most/Documentation/driver_usage.txt index 2fa8dea1da4d..f1b6977f4543 100644 --- a/drivers/staging/most/Documentation/driver_usage.txt +++ b/drivers/staging/most/Documentation/driver_usage.txt @@ -101,11 +101,11 @@ following components are available Userspace can access the driver by means of character devices. 2) Networking - Standard networking applications (e.g. iperf) can by used to access + Standard networking applications (e.g. iperf) can be used to access the driver via the networking subsystem. 3) Video4Linux (v4l2) - Standard video applications (e.g. VLC) can by used to access the + Standard video applications (e.g. VLC) can be used to access the driver via the V4L subsystem. 4) Advanced Linux Sound Architecture (ALSA) @@ -121,7 +121,7 @@ The driver is to be configured via configfs. Each loaded component kernel object (see section 1.3) registers a subsystem with configfs, which is used to configure and establish communication pathways (links) to attached devices on the bus. To do so, the user has to descend into the component's configuration -directory and create a new directory (child config itmes). The name of this +directory and create a new directory (child config items). The name of this directory will be used as a reference for the link and it will contain the following attributes: @@ -129,7 +129,7 @@ following attributes: configure the buffer size for this channel - subbuffer_size configure the sub-buffer size for this channel (needed for - synchronous and isochrnous data) + synchronous and isochronous data) - num_buffers configure number of buffers used for this channel - datatype @@ -229,7 +229,7 @@ packets within one USB transaction. This renders bytes for padding. Note that at least (2 * subbuffer_size) bytes for isochronous data or -(subbuffer_size * packts_per_xact) bytes for synchronous data need to +(subbuffer_size * packets_per_xact) bytes for synchronous data need to be put in the transmission buffer and passed to the driver. Since adapter drivers are allowed to change a chosen configuration to best diff --git a/drivers/staging/most/dim2/dim2.c b/drivers/staging/most/dim2/dim2.c index 8d649d920433..7953d4626752 100644 --- a/drivers/staging/most/dim2/dim2.c +++ b/drivers/staging/most/dim2/dim2.c @@ -7,9 +7,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include #include -#include #include #include #include @@ -17,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -168,8 +165,8 @@ static int try_start_dim_transfer(struct hdm_channel *hdm_ch) unsigned long flags; struct dim_ch_state st; - BUG_ON(!hdm_ch); - BUG_ON(!hdm_ch->is_initialized); + if (!hdm_ch || !hdm_ch->is_initialized) + return -EINVAL; spin_lock_irqsave(&dim_lock, flags); if (list_empty(head)) { @@ -190,7 +187,10 @@ static int try_start_dim_transfer(struct hdm_channel *hdm_ch) return -EAGAIN; } - BUG_ON(mbo->bus_address == 0); + if (mbo->bus_address == 0) { + spin_unlock_irqrestore(&dim_lock, flags); + return -EFAULT; + } if (!dim_enqueue_buffer(&hdm_ch->ch, mbo->bus_address, buf_size)) { list_del(head->next); spin_unlock_irqrestore(&dim_lock, flags); @@ -246,9 +246,9 @@ static void retrieve_netinfo(struct dim2_hdm *dev, struct mbo *mbo) { u8 *data = mbo->virt_address; - pr_info("Node Address: 0x%03x\n", (u16)data[16] << 8 | data[17]); + dev_dbg(&dev->dev, "Node Address: 0x%03x\n", (u16)data[16] << 8 | data[17]); dev->link_state = data[18]; - pr_info("NIState: %d\n", dev->link_state); + dev_dbg(&dev->dev, "NIState: %d\n", dev->link_state); memcpy(dev->mac_addrs, data + 19, 6); dev->deliver_netinfo++; wake_up_interruptible(&dev->netinfo_waitq); @@ -271,8 +271,8 @@ static void service_done_flag(struct dim2_hdm *dev, int ch_idx) unsigned long flags; u8 *data; - BUG_ON(!hdm_ch); - BUG_ON(!hdm_ch->is_initialized); + if (!hdm_ch || !hdm_ch->is_initialized) + return; spin_lock_irqsave(&dim_lock, flags); @@ -457,7 +457,8 @@ static int configure_channel(struct most_interface *most_iface, int ch_idx, int const ch_addr = ch_idx * 2 + 2; struct hdm_channel *const hdm_ch = dev->hch + ch_idx; - BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); + if (ch_idx < 0 || ch_idx >= DMA_CHANNELS) + return -EINVAL; if (hdm_ch->is_initialized) return -EPERM; @@ -472,13 +473,13 @@ static int configure_channel(struct most_interface *most_iface, int ch_idx, case MOST_CH_CONTROL: new_size = dim_norm_ctrl_async_buffer_size(buf_size); if (new_size == 0) { - pr_err("%s: too small buffer size\n", hdm_ch->name); + dev_err(&dev->dev, "%s: too small buffer size\n", hdm_ch->name); return -EINVAL; } ccfg->buffer_size = new_size; if (new_size != buf_size) - pr_warn("%s: fixed buffer size (%d -> %d)\n", - hdm_ch->name, buf_size, new_size); + dev_warn(&dev->dev, "%s: fixed buffer size (%d -> %d)\n", + hdm_ch->name, buf_size, new_size); spin_lock_irqsave(&dim_lock, flags); hal_ret = dim_init_control(&hdm_ch->ch, is_tx, ch_addr, is_tx ? new_size * 2 : new_size); @@ -486,13 +487,13 @@ static int configure_channel(struct most_interface *most_iface, int ch_idx, case MOST_CH_ASYNC: new_size = dim_norm_ctrl_async_buffer_size(buf_size); if (new_size == 0) { - pr_err("%s: too small buffer size\n", hdm_ch->name); + dev_err(&dev->dev, "%s: too small buffer size\n", hdm_ch->name); return -EINVAL; } ccfg->buffer_size = new_size; if (new_size != buf_size) - pr_warn("%s: fixed buffer size (%d -> %d)\n", - hdm_ch->name, buf_size, new_size); + dev_warn(&dev->dev, "%s: fixed buffer size (%d -> %d)\n", + hdm_ch->name, buf_size, new_size); spin_lock_irqsave(&dim_lock, flags); hal_ret = dim_init_async(&hdm_ch->ch, is_tx, ch_addr, is_tx ? new_size * 2 : new_size); @@ -500,41 +501,41 @@ static int configure_channel(struct most_interface *most_iface, int ch_idx, case MOST_CH_ISOC: new_size = dim_norm_isoc_buffer_size(buf_size, sub_size); if (new_size == 0) { - pr_err("%s: invalid sub-buffer size or too small buffer size\n", - hdm_ch->name); + dev_err(&dev->dev, "%s: invalid sub-buffer size or too small buffer size\n", + hdm_ch->name); return -EINVAL; } ccfg->buffer_size = new_size; if (new_size != buf_size) - pr_warn("%s: fixed buffer size (%d -> %d)\n", - hdm_ch->name, buf_size, new_size); + dev_warn(&dev->dev, "%s: fixed buffer size (%d -> %d)\n", + hdm_ch->name, buf_size, new_size); spin_lock_irqsave(&dim_lock, flags); hal_ret = dim_init_isoc(&hdm_ch->ch, is_tx, ch_addr, sub_size); break; case MOST_CH_SYNC: new_size = dim_norm_sync_buffer_size(buf_size, sub_size); if (new_size == 0) { - pr_err("%s: invalid sub-buffer size or too small buffer size\n", - hdm_ch->name); + dev_err(&dev->dev, "%s: invalid sub-buffer size or too small buffer size\n", + hdm_ch->name); return -EINVAL; } ccfg->buffer_size = new_size; if (new_size != buf_size) - pr_warn("%s: fixed buffer size (%d -> %d)\n", - hdm_ch->name, buf_size, new_size); + dev_warn(&dev->dev, "%s: fixed buffer size (%d -> %d)\n", + hdm_ch->name, buf_size, new_size); spin_lock_irqsave(&dim_lock, flags); hal_ret = dim_init_sync(&hdm_ch->ch, is_tx, ch_addr, sub_size); break; default: - pr_err("%s: configure failed, bad channel type: %d\n", - hdm_ch->name, ccfg->data_type); + dev_err(&dev->dev, "%s: configure failed, bad channel type: %d\n", + hdm_ch->name, ccfg->data_type); return -EINVAL; } if (hal_ret != DIM_NO_ERROR) { spin_unlock_irqrestore(&dim_lock, flags); - pr_err("%s: configure failed (%d), type: %d, is_tx: %d\n", - hdm_ch->name, hal_ret, ccfg->data_type, (int)is_tx); + dev_err(&dev->dev, "%s: configure failed (%d), type: %d, is_tx: %d\n", + hdm_ch->name, hal_ret, ccfg->data_type, (int)is_tx); return -ENODEV; } @@ -569,7 +570,8 @@ static int enqueue(struct most_interface *most_iface, int ch_idx, struct hdm_channel *hdm_ch = dev->hch + ch_idx; unsigned long flags; - BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); + if (ch_idx < 0 || ch_idx >= DMA_CHANNELS) + return -EINVAL; if (!hdm_ch->is_initialized) return -EPERM; @@ -608,7 +610,7 @@ static void request_netinfo(struct most_interface *most_iface, int ch_idx, return; if (dev->atx_idx < 0) { - pr_err("Async Tx Not initialized\n"); + dev_err(&dev->dev, "Async Tx Not initialized\n"); return; } @@ -645,7 +647,8 @@ static int poison_channel(struct most_interface *most_iface, int ch_idx) u8 hal_ret; int ret = 0; - BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); + if (ch_idx < 0 || ch_idx >= DMA_CHANNELS) + return -EINVAL; if (!hdm_ch->is_initialized) return -EPERM; @@ -657,7 +660,7 @@ static int poison_channel(struct most_interface *most_iface, int ch_idx) dev->atx_idx = -1; spin_unlock_irqrestore(&dim_lock, flags); if (hal_ret != DIM_NO_ERROR) { - pr_err("HAL Failed to close channel %s\n", hdm_ch->name); + dev_err(&dev->dev, "HAL Failed to close channel %s\n", hdm_ch->name); ret = -EFAULT; } @@ -799,8 +802,7 @@ static int dim2_probe(struct platform_device *pdev) dev_fcnt = pdata->fcnt; } - dev_info(&pdev->dev, "sync: num of frames per sub-buffer: %u\n", - dev_fcnt); + dev_dbg(&pdev->dev, "sync: num of frames per sub-buffer: %u\n", dev_fcnt); hal_ret = dim_startup(dev->io_base, dev->clk_speed, dev_fcnt); if (hal_ret != DIM_NO_ERROR) { dev_err(&pdev->dev, "dim_startup failed: %d\n", hal_ret); @@ -925,28 +927,32 @@ static int fsl_mx6_enable(struct platform_device *pdev) int ret; dev->clk = devm_clk_get(&pdev->dev, "mlb"); - if (IS_ERR_OR_NULL(dev->clk)) { - dev_err(&pdev->dev, "unable to get mlb clock\n"); - return -EFAULT; - } + if (IS_ERR(dev->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(dev->clk), + "unable to get mlb clock\n"); ret = clk_prepare_enable(dev->clk); if (ret) { - dev_err(&pdev->dev, "%s\n", "clk_prepare_enable failed"); + dev_err(&pdev->dev, "clk_prepare_enable failed\n"); return ret; } if (dev->clk_speed >= CLK_2048FS) { /* enable pll */ dev->clk_pll = devm_clk_get(&pdev->dev, "pll8_mlb"); - if (IS_ERR_OR_NULL(dev->clk_pll)) { - dev_err(&pdev->dev, "unable to get mlb pll clock\n"); + if (IS_ERR(dev->clk_pll)) { clk_disable_unprepare(dev->clk); - return -EFAULT; + return dev_err_probe(&pdev->dev, PTR_ERR(dev->clk_pll), + "unable to get mlb pll clock\n"); } writel(0x888, dev->io_base + 0x38); - clk_prepare_enable(dev->clk_pll); + ret = clk_prepare_enable(dev->clk_pll); + if (ret) { + dev_err(&pdev->dev, "failed to enable pll clock\n"); + clk_disable_unprepare(dev->clk); + return ret; + } } return 0; @@ -975,7 +981,7 @@ static int rcar_gen2_enable(struct platform_device *pdev) ret = clk_prepare_enable(dev->clk); if (ret) { - dev_err(&pdev->dev, "%s\n", "clk_prepare_enable failed"); + dev_err(&pdev->dev, "clk_prepare_enable failed\n"); return ret; } @@ -1020,7 +1026,7 @@ static int rcar_gen3_enable(struct platform_device *pdev) ret = clk_prepare_enable(dev->clk); if (ret) { - dev_err(&pdev->dev, "%s\n", "clk_prepare_enable failed"); + dev_err(&pdev->dev, "clk_prepare_enable failed\n"); return ret; } diff --git a/drivers/staging/most/dim2/hal.c b/drivers/staging/most/dim2/hal.c index 6abe3ab2b2cf..38376d3e3f0d 100644 --- a/drivers/staging/most/dim2/hal.c +++ b/drivers/staging/most/dim2/hal.c @@ -11,9 +11,8 @@ #include "hal.h" #include "errors.h" #include "reg.h" -#include -#include #include +#include /* * Size factor for isochronous DBR buffer. @@ -45,8 +44,6 @@ #define DBR_SIZE (16 * 1024) /* specified by IP */ #define DBR_BLOCK_SIZE (DBR_SIZE / 32 / DBR_MAP_SIZE) -#define ROUND_UP_TO(x, d) (DIV_ROUND_UP(x, (d)) * (d)) - /* -------------------------------------------------------------------------- */ /* generic helper functions and macros */ @@ -758,7 +755,7 @@ static u8 init_ctrl_async(struct dim_channel *ch, u8 type, u8 is_tx, return DIM_INIT_ERR_CHANNEL_ADDRESS; if (!ch->dbr_size) - ch->dbr_size = ROUND_UP_TO(hw_buffer_size, DBR_BLOCK_SIZE); + ch->dbr_size = round_up(hw_buffer_size, DBR_BLOCK_SIZE); ch->dbr_addr = alloc_dbr(ch->dbr_size); if (ch->dbr_addr >= DBR_SIZE) return DIM_INIT_ERR_OUT_OF_MEMORY; diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c index 1d1fe8bff7ee..fffdb60cd230 100644 --- a/drivers/staging/most/net/net.c +++ b/drivers/staging/most/net/net.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c index e9af66a08448..952c5a849a56 100644 --- a/drivers/staging/nvec/nvec.c +++ b/drivers/staging/nvec/nvec.c @@ -659,8 +659,10 @@ static irqreturn_t nvec_interrupt(int irq, void *dev) nvec_tx_set(nvec); to_send = nvec->tx->data[0]; nvec->tx->pos = 1; - /* delay ACK due to AP20 HW Bug - do not replace by usleep_range */ + /* + * delay ACK due to AP20 HW Bug + * do not replace by usleep_range + */ udelay(33); } else if (status == (I2C_SL_IRQ)) { nvec->rx->data[1] = received; @@ -811,13 +813,12 @@ static int tegra_nvec_probe(struct platform_device *pdev) nvec->irq = platform_get_irq(pdev, 0); if (nvec->irq < 0) - return -ENODEV; + return nvec->irq; i2c_clk = devm_clk_get(dev, "div-clk"); - if (IS_ERR(i2c_clk)) { - dev_err(dev, "failed to get controller clock\n"); - return -ENODEV; - } + if (IS_ERR(i2c_clk)) + return dev_err_probe(dev, PTR_ERR(i2c_clk), + "failed to get controller clock\n"); nvec->rst = devm_reset_control_get_exclusive(dev, "i2c"); if (IS_ERR(nvec->rst)) { @@ -849,10 +850,8 @@ static int tegra_nvec_probe(struct platform_device *pdev) err = devm_request_irq(dev, nvec->irq, nvec_interrupt, IRQF_NO_AUTOEN, "nvec", nvec); - if (err) { - dev_err(dev, "couldn't request irq\n"); - return -ENODEV; - } + if (err) + return dev_err_probe(dev, err, "couldn't request irq\n"); tegra_init_i2c_slave(nvec); diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c index 965330eec80a..d0b43d50b83c 100644 --- a/drivers/staging/octeon/ethernet-rx.c +++ b/drivers/staging/octeon/ethernet-rx.c @@ -535,7 +535,7 @@ void cvm_oct_rx_shutdown(void) cvmx_write_csr(CVMX_POW_WQ_INT_THRX(i), 0); /* Free the interrupt handler */ - free_irq(oct_rx_group[i].irq, cvm_oct_device); + free_irq(oct_rx_group[i].irq, &oct_rx_group[i].napi); netif_napi_del(&oct_rx_group[i].napi); } diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c index f5bbedac6a65..14d10659bce7 100644 --- a/drivers/staging/octeon/ethernet-tx.c +++ b/drivers/staging/octeon/ethernet-tx.c @@ -449,8 +449,6 @@ skip_xmit: case QUEUE_CORE: __skb_queue_tail(&priv->tx_free_list[qos], skb); break; - default: - BUG(); } while (skb_to_free > 0) { diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h index 35b5078ba51e..291eaffd2543 100644 --- a/drivers/staging/octeon/octeon-stubs.h +++ b/drivers/staging/octeon/octeon-stubs.h @@ -124,65 +124,65 @@ union cvmx_pip_wqe_word2 { union cvmx_pip_wqe_word0 { struct { - uint64_t next_ptr:40; - uint8_t unused; + u64 next_ptr:40; + u8 unused; __wsum hw_chksum; } cn38xx; struct { - uint64_t pknd:6; /* 0..5 */ - uint64_t unused2:2; /* 6..7 */ - uint64_t bpid:6; /* 8..13 */ - uint64_t unused1:18; /* 14..31 */ - uint64_t l2ptr:8; /* 32..39 */ - uint64_t l3ptr:8; /* 40..47 */ - uint64_t unused0:8; /* 48..55 */ - uint64_t l4ptr:8; /* 56..63 */ + u64 pknd:6; /* 0..5 */ + u64 unused2:2; /* 6..7 */ + u64 bpid:6; /* 8..13 */ + u64 unused1:18; /* 14..31 */ + u64 l2ptr:8; /* 32..39 */ + u64 l3ptr:8; /* 40..47 */ + u64 unused0:8; /* 48..55 */ + u64 l4ptr:8; /* 56..63 */ } cn68xx; }; union cvmx_wqe_word0 { - uint64_t u64; + u64 u64; union cvmx_pip_wqe_word0 pip; }; union cvmx_wqe_word1 { - uint64_t u64; + u64 u64; struct { - uint64_t tag:32; - uint64_t tag_type:2; - uint64_t varies:14; - uint64_t len:16; + u64 tag:32; + u64 tag_type:2; + u64 varies:14; + u64 len:16; }; struct { - uint64_t tag:32; - uint64_t tag_type:2; - uint64_t zero_2:3; - uint64_t grp:6; - uint64_t zero_1:1; - uint64_t qos:3; - uint64_t zero_0:1; - uint64_t len:16; + u64 tag:32; + u64 tag_type:2; + u64 zero_2:3; + u64 grp:6; + u64 zero_1:1; + u64 qos:3; + u64 zero_0:1; + u64 len:16; } cn68xx; struct { - uint64_t tag:32; - uint64_t tag_type:2; - uint64_t zero_2:1; - uint64_t grp:4; - uint64_t qos:3; - uint64_t ipprt:6; - uint64_t len:16; + u64 tag:32; + u64 tag_type:2; + u64 zero_2:1; + u64 grp:4; + u64 qos:3; + u64 ipprt:6; + u64 len:16; } cn38xx; }; union cvmx_buf_ptr { void *ptr; - uint64_t u64; + u64 u64; struct { - uint64_t i:1; - uint64_t back:4; - uint64_t pool:3; - uint64_t size:16; - uint64_t addr:40; + u64 i:1; + u64 back:4; + u64 pool:3; + u64 size:16; + u64 addr:40; } s; }; @@ -191,16 +191,16 @@ struct cvmx_wqe { union cvmx_wqe_word1 word1; union cvmx_pip_wqe_word2 word2; union cvmx_buf_ptr packet_ptr; - uint8_t packet_data[96]; + u8 packet_data[96]; }; union cvmx_helper_link_info { - uint64_t u64; + u64 u64; struct { - uint64_t reserved_20_63:44; - uint64_t link_up:1; /**< Is the physical link up? */ - uint64_t full_duplex:1; /**< 1 if the link is full duplex */ - uint64_t speed:18; /**< Speed of the link in Mbps */ + u64 reserved_20_63:44; + u64 link_up:1; /**< Is the physical link up? */ + u64 full_duplex:1; /**< 1 if the link is full duplex */ + u64 speed:18; /**< Speed of the link in Mbps */ } s; }; @@ -264,921 +264,921 @@ enum cvmx_pow_tag_type { }; union cvmx_ipd_ctl_status { - uint64_t u64; + u64 u64; struct cvmx_ipd_ctl_status_s { - uint64_t reserved_18_63:46; - uint64_t use_sop:1; - uint64_t rst_done:1; - uint64_t clken:1; - uint64_t no_wptr:1; - uint64_t pq_apkt:1; - uint64_t pq_nabuf:1; - uint64_t ipd_full:1; - uint64_t pkt_off:1; - uint64_t len_m8:1; - uint64_t reset:1; - uint64_t addpkt:1; - uint64_t naddbuf:1; - uint64_t pkt_lend:1; - uint64_t wqe_lend:1; - uint64_t pbp_en:1; - uint64_t opc_mode:2; - uint64_t ipd_en:1; + u64 reserved_18_63:46; + u64 use_sop:1; + u64 rst_done:1; + u64 clken:1; + u64 no_wptr:1; + u64 pq_apkt:1; + u64 pq_nabuf:1; + u64 ipd_full:1; + u64 pkt_off:1; + u64 len_m8:1; + u64 reset:1; + u64 addpkt:1; + u64 naddbuf:1; + u64 pkt_lend:1; + u64 wqe_lend:1; + u64 pbp_en:1; + u64 opc_mode:2; + u64 ipd_en:1; } s; struct cvmx_ipd_ctl_status_cn30xx { - uint64_t reserved_10_63:54; - uint64_t len_m8:1; - uint64_t reset:1; - uint64_t addpkt:1; - uint64_t naddbuf:1; - uint64_t pkt_lend:1; - uint64_t wqe_lend:1; - uint64_t pbp_en:1; - uint64_t opc_mode:2; - uint64_t ipd_en:1; + u64 reserved_10_63:54; + u64 len_m8:1; + u64 reset:1; + u64 addpkt:1; + u64 naddbuf:1; + u64 pkt_lend:1; + u64 wqe_lend:1; + u64 pbp_en:1; + u64 opc_mode:2; + u64 ipd_en:1; } cn30xx; struct cvmx_ipd_ctl_status_cn38xxp2 { - uint64_t reserved_9_63:55; - uint64_t reset:1; - uint64_t addpkt:1; - uint64_t naddbuf:1; - uint64_t pkt_lend:1; - uint64_t wqe_lend:1; - uint64_t pbp_en:1; - uint64_t opc_mode:2; - uint64_t ipd_en:1; + u64 reserved_9_63:55; + u64 reset:1; + u64 addpkt:1; + u64 naddbuf:1; + u64 pkt_lend:1; + u64 wqe_lend:1; + u64 pbp_en:1; + u64 opc_mode:2; + u64 ipd_en:1; } cn38xxp2; struct cvmx_ipd_ctl_status_cn50xx { - uint64_t reserved_15_63:49; - uint64_t no_wptr:1; - uint64_t pq_apkt:1; - uint64_t pq_nabuf:1; - uint64_t ipd_full:1; - uint64_t pkt_off:1; - uint64_t len_m8:1; - uint64_t reset:1; - uint64_t addpkt:1; - uint64_t naddbuf:1; - uint64_t pkt_lend:1; - uint64_t wqe_lend:1; - uint64_t pbp_en:1; - uint64_t opc_mode:2; - uint64_t ipd_en:1; + u64 reserved_15_63:49; + u64 no_wptr:1; + u64 pq_apkt:1; + u64 pq_nabuf:1; + u64 ipd_full:1; + u64 pkt_off:1; + u64 len_m8:1; + u64 reset:1; + u64 addpkt:1; + u64 naddbuf:1; + u64 pkt_lend:1; + u64 wqe_lend:1; + u64 pbp_en:1; + u64 opc_mode:2; + u64 ipd_en:1; } cn50xx; struct cvmx_ipd_ctl_status_cn58xx { - uint64_t reserved_12_63:52; - uint64_t ipd_full:1; - uint64_t pkt_off:1; - uint64_t len_m8:1; - uint64_t reset:1; - uint64_t addpkt:1; - uint64_t naddbuf:1; - uint64_t pkt_lend:1; - uint64_t wqe_lend:1; - uint64_t pbp_en:1; - uint64_t opc_mode:2; - uint64_t ipd_en:1; + u64 reserved_12_63:52; + u64 ipd_full:1; + u64 pkt_off:1; + u64 len_m8:1; + u64 reset:1; + u64 addpkt:1; + u64 naddbuf:1; + u64 pkt_lend:1; + u64 wqe_lend:1; + u64 pbp_en:1; + u64 opc_mode:2; + u64 ipd_en:1; } cn58xx; struct cvmx_ipd_ctl_status_cn63xxp1 { - uint64_t reserved_16_63:48; - uint64_t clken:1; - uint64_t no_wptr:1; - uint64_t pq_apkt:1; - uint64_t pq_nabuf:1; - uint64_t ipd_full:1; - uint64_t pkt_off:1; - uint64_t len_m8:1; - uint64_t reset:1; - uint64_t addpkt:1; - uint64_t naddbuf:1; - uint64_t pkt_lend:1; - uint64_t wqe_lend:1; - uint64_t pbp_en:1; - uint64_t opc_mode:2; - uint64_t ipd_en:1; + u64 reserved_16_63:48; + u64 clken:1; + u64 no_wptr:1; + u64 pq_apkt:1; + u64 pq_nabuf:1; + u64 ipd_full:1; + u64 pkt_off:1; + u64 len_m8:1; + u64 reset:1; + u64 addpkt:1; + u64 naddbuf:1; + u64 pkt_lend:1; + u64 wqe_lend:1; + u64 pbp_en:1; + u64 opc_mode:2; + u64 ipd_en:1; } cn63xxp1; }; union cvmx_ipd_sub_port_fcs { - uint64_t u64; + u64 u64; struct cvmx_ipd_sub_port_fcs_s { - uint64_t port_bit:32; - uint64_t reserved_32_35:4; - uint64_t port_bit2:4; - uint64_t reserved_40_63:24; + u64 port_bit:32; + u64 reserved_32_35:4; + u64 port_bit2:4; + u64 reserved_40_63:24; } s; struct cvmx_ipd_sub_port_fcs_cn30xx { - uint64_t port_bit:3; - uint64_t reserved_3_63:61; + u64 port_bit:3; + u64 reserved_3_63:61; } cn30xx; struct cvmx_ipd_sub_port_fcs_cn38xx { - uint64_t port_bit:32; - uint64_t reserved_32_63:32; + u64 port_bit:32; + u64 reserved_32_63:32; } cn38xx; }; union cvmx_ipd_sub_port_qos_cnt { - uint64_t u64; + u64 u64; struct cvmx_ipd_sub_port_qos_cnt_s { - uint64_t cnt:32; - uint64_t port_qos:9; - uint64_t reserved_41_63:23; + u64 cnt:32; + u64 port_qos:9; + u64 reserved_41_63:23; } s; }; typedef struct { - uint32_t dropped_octets; - uint32_t dropped_packets; - uint32_t pci_raw_packets; - uint32_t octets; - uint32_t packets; - uint32_t multicast_packets; - uint32_t broadcast_packets; - uint32_t len_64_packets; - uint32_t len_65_127_packets; - uint32_t len_128_255_packets; - uint32_t len_256_511_packets; - uint32_t len_512_1023_packets; - uint32_t len_1024_1518_packets; - uint32_t len_1519_max_packets; - uint32_t fcs_align_err_packets; - uint32_t runt_packets; - uint32_t runt_crc_packets; - uint32_t oversize_packets; - uint32_t oversize_crc_packets; - uint32_t inb_packets; - uint64_t inb_octets; - uint16_t inb_errors; + u32 dropped_octets; + u32 dropped_packets; + u32 pci_raw_packets; + u32 octets; + u32 packets; + u32 multicast_packets; + u32 broadcast_packets; + u32 len_64_packets; + u32 len_65_127_packets; + u32 len_128_255_packets; + u32 len_256_511_packets; + u32 len_512_1023_packets; + u32 len_1024_1518_packets; + u32 len_1519_max_packets; + u32 fcs_align_err_packets; + u32 runt_packets; + u32 runt_crc_packets; + u32 oversize_packets; + u32 oversize_crc_packets; + u32 inb_packets; + u64 inb_octets; + u16 inb_errors; } cvmx_pip_port_status_t; typedef struct { - uint32_t packets; - uint64_t octets; - uint64_t doorbell; + u32 packets; + u64 octets; + u64 doorbell; } cvmx_pko_port_status_t; union cvmx_pip_frm_len_chkx { - uint64_t u64; + u64 u64; struct cvmx_pip_frm_len_chkx_s { - uint64_t reserved_32_63:32; - uint64_t maxlen:16; - uint64_t minlen:16; + u64 reserved_32_63:32; + u64 maxlen:16; + u64 minlen:16; } s; }; union cvmx_gmxx_rxx_frm_ctl { - uint64_t u64; + u64 u64; struct cvmx_gmxx_rxx_frm_ctl_s { - uint64_t pre_chk:1; - uint64_t pre_strp:1; - uint64_t ctl_drp:1; - uint64_t ctl_bck:1; - uint64_t ctl_mcst:1; - uint64_t ctl_smac:1; - uint64_t pre_free:1; - uint64_t vlan_len:1; - uint64_t pad_len:1; - uint64_t pre_align:1; - uint64_t null_dis:1; - uint64_t reserved_11_11:1; - uint64_t ptp_mode:1; - uint64_t reserved_13_63:51; + u64 pre_chk:1; + u64 pre_strp:1; + u64 ctl_drp:1; + u64 ctl_bck:1; + u64 ctl_mcst:1; + u64 ctl_smac:1; + u64 pre_free:1; + u64 vlan_len:1; + u64 pad_len:1; + u64 pre_align:1; + u64 null_dis:1; + u64 reserved_11_11:1; + u64 ptp_mode:1; + u64 reserved_13_63:51; } s; struct cvmx_gmxx_rxx_frm_ctl_cn30xx { - uint64_t pre_chk:1; - uint64_t pre_strp:1; - uint64_t ctl_drp:1; - uint64_t ctl_bck:1; - uint64_t ctl_mcst:1; - uint64_t ctl_smac:1; - uint64_t pre_free:1; - uint64_t vlan_len:1; - uint64_t pad_len:1; - uint64_t reserved_9_63:55; + u64 pre_chk:1; + u64 pre_strp:1; + u64 ctl_drp:1; + u64 ctl_bck:1; + u64 ctl_mcst:1; + u64 ctl_smac:1; + u64 pre_free:1; + u64 vlan_len:1; + u64 pad_len:1; + u64 reserved_9_63:55; } cn30xx; struct cvmx_gmxx_rxx_frm_ctl_cn31xx { - uint64_t pre_chk:1; - uint64_t pre_strp:1; - uint64_t ctl_drp:1; - uint64_t ctl_bck:1; - uint64_t ctl_mcst:1; - uint64_t ctl_smac:1; - uint64_t pre_free:1; - uint64_t vlan_len:1; - uint64_t reserved_8_63:56; + u64 pre_chk:1; + u64 pre_strp:1; + u64 ctl_drp:1; + u64 ctl_bck:1; + u64 ctl_mcst:1; + u64 ctl_smac:1; + u64 pre_free:1; + u64 vlan_len:1; + u64 reserved_8_63:56; } cn31xx; struct cvmx_gmxx_rxx_frm_ctl_cn50xx { - uint64_t pre_chk:1; - uint64_t pre_strp:1; - uint64_t ctl_drp:1; - uint64_t ctl_bck:1; - uint64_t ctl_mcst:1; - uint64_t ctl_smac:1; - uint64_t pre_free:1; - uint64_t reserved_7_8:2; - uint64_t pre_align:1; - uint64_t null_dis:1; - uint64_t reserved_11_63:53; + u64 pre_chk:1; + u64 pre_strp:1; + u64 ctl_drp:1; + u64 ctl_bck:1; + u64 ctl_mcst:1; + u64 ctl_smac:1; + u64 pre_free:1; + u64 reserved_7_8:2; + u64 pre_align:1; + u64 null_dis:1; + u64 reserved_11_63:53; } cn50xx; struct cvmx_gmxx_rxx_frm_ctl_cn56xxp1 { - uint64_t pre_chk:1; - uint64_t pre_strp:1; - uint64_t ctl_drp:1; - uint64_t ctl_bck:1; - uint64_t ctl_mcst:1; - uint64_t ctl_smac:1; - uint64_t pre_free:1; - uint64_t reserved_7_8:2; - uint64_t pre_align:1; - uint64_t reserved_10_63:54; + u64 pre_chk:1; + u64 pre_strp:1; + u64 ctl_drp:1; + u64 ctl_bck:1; + u64 ctl_mcst:1; + u64 ctl_smac:1; + u64 pre_free:1; + u64 reserved_7_8:2; + u64 pre_align:1; + u64 reserved_10_63:54; } cn56xxp1; struct cvmx_gmxx_rxx_frm_ctl_cn58xx { - uint64_t pre_chk:1; - uint64_t pre_strp:1; - uint64_t ctl_drp:1; - uint64_t ctl_bck:1; - uint64_t ctl_mcst:1; - uint64_t ctl_smac:1; - uint64_t pre_free:1; - uint64_t vlan_len:1; - uint64_t pad_len:1; - uint64_t pre_align:1; - uint64_t null_dis:1; - uint64_t reserved_11_63:53; + u64 pre_chk:1; + u64 pre_strp:1; + u64 ctl_drp:1; + u64 ctl_bck:1; + u64 ctl_mcst:1; + u64 ctl_smac:1; + u64 pre_free:1; + u64 vlan_len:1; + u64 pad_len:1; + u64 pre_align:1; + u64 null_dis:1; + u64 reserved_11_63:53; } cn58xx; struct cvmx_gmxx_rxx_frm_ctl_cn61xx { - uint64_t pre_chk:1; - uint64_t pre_strp:1; - uint64_t ctl_drp:1; - uint64_t ctl_bck:1; - uint64_t ctl_mcst:1; - uint64_t ctl_smac:1; - uint64_t pre_free:1; - uint64_t reserved_7_8:2; - uint64_t pre_align:1; - uint64_t null_dis:1; - uint64_t reserved_11_11:1; - uint64_t ptp_mode:1; - uint64_t reserved_13_63:51; + u64 pre_chk:1; + u64 pre_strp:1; + u64 ctl_drp:1; + u64 ctl_bck:1; + u64 ctl_mcst:1; + u64 ctl_smac:1; + u64 pre_free:1; + u64 reserved_7_8:2; + u64 pre_align:1; + u64 null_dis:1; + u64 reserved_11_11:1; + u64 ptp_mode:1; + u64 reserved_13_63:51; } cn61xx; }; union cvmx_gmxx_rxx_int_reg { - uint64_t u64; + u64 u64; struct cvmx_gmxx_rxx_int_reg_s { - uint64_t minerr:1; - uint64_t carext:1; - uint64_t maxerr:1; - uint64_t jabber:1; - uint64_t fcserr:1; - uint64_t alnerr:1; - uint64_t lenerr:1; - uint64_t rcverr:1; - uint64_t skperr:1; - uint64_t niberr:1; - uint64_t ovrerr:1; - uint64_t pcterr:1; - uint64_t rsverr:1; - uint64_t falerr:1; - uint64_t coldet:1; - uint64_t ifgerr:1; - uint64_t phy_link:1; - uint64_t phy_spd:1; - uint64_t phy_dupx:1; - uint64_t pause_drp:1; - uint64_t loc_fault:1; - uint64_t rem_fault:1; - uint64_t bad_seq:1; - uint64_t bad_term:1; - uint64_t unsop:1; - uint64_t uneop:1; - uint64_t undat:1; - uint64_t hg2fld:1; - uint64_t hg2cc:1; - uint64_t reserved_29_63:35; + u64 minerr:1; + u64 carext:1; + u64 maxerr:1; + u64 jabber:1; + u64 fcserr:1; + u64 alnerr:1; + u64 lenerr:1; + u64 rcverr:1; + u64 skperr:1; + u64 niberr:1; + u64 ovrerr:1; + u64 pcterr:1; + u64 rsverr:1; + u64 falerr:1; + u64 coldet:1; + u64 ifgerr:1; + u64 phy_link:1; + u64 phy_spd:1; + u64 phy_dupx:1; + u64 pause_drp:1; + u64 loc_fault:1; + u64 rem_fault:1; + u64 bad_seq:1; + u64 bad_term:1; + u64 unsop:1; + u64 uneop:1; + u64 undat:1; + u64 hg2fld:1; + u64 hg2cc:1; + u64 reserved_29_63:35; } s; struct cvmx_gmxx_rxx_int_reg_cn30xx { - uint64_t minerr:1; - uint64_t carext:1; - uint64_t maxerr:1; - uint64_t jabber:1; - uint64_t fcserr:1; - uint64_t alnerr:1; - uint64_t lenerr:1; - uint64_t rcverr:1; - uint64_t skperr:1; - uint64_t niberr:1; - uint64_t ovrerr:1; - uint64_t pcterr:1; - uint64_t rsverr:1; - uint64_t falerr:1; - uint64_t coldet:1; - uint64_t ifgerr:1; - uint64_t phy_link:1; - uint64_t phy_spd:1; - uint64_t phy_dupx:1; - uint64_t reserved_19_63:45; + u64 minerr:1; + u64 carext:1; + u64 maxerr:1; + u64 jabber:1; + u64 fcserr:1; + u64 alnerr:1; + u64 lenerr:1; + u64 rcverr:1; + u64 skperr:1; + u64 niberr:1; + u64 ovrerr:1; + u64 pcterr:1; + u64 rsverr:1; + u64 falerr:1; + u64 coldet:1; + u64 ifgerr:1; + u64 phy_link:1; + u64 phy_spd:1; + u64 phy_dupx:1; + u64 reserved_19_63:45; } cn30xx; struct cvmx_gmxx_rxx_int_reg_cn50xx { - uint64_t reserved_0_0:1; - uint64_t carext:1; - uint64_t reserved_2_2:1; - uint64_t jabber:1; - uint64_t fcserr:1; - uint64_t alnerr:1; - uint64_t reserved_6_6:1; - uint64_t rcverr:1; - uint64_t skperr:1; - uint64_t niberr:1; - uint64_t ovrerr:1; - uint64_t pcterr:1; - uint64_t rsverr:1; - uint64_t falerr:1; - uint64_t coldet:1; - uint64_t ifgerr:1; - uint64_t phy_link:1; - uint64_t phy_spd:1; - uint64_t phy_dupx:1; - uint64_t pause_drp:1; - uint64_t reserved_20_63:44; + u64 reserved_0_0:1; + u64 carext:1; + u64 reserved_2_2:1; + u64 jabber:1; + u64 fcserr:1; + u64 alnerr:1; + u64 reserved_6_6:1; + u64 rcverr:1; + u64 skperr:1; + u64 niberr:1; + u64 ovrerr:1; + u64 pcterr:1; + u64 rsverr:1; + u64 falerr:1; + u64 coldet:1; + u64 ifgerr:1; + u64 phy_link:1; + u64 phy_spd:1; + u64 phy_dupx:1; + u64 pause_drp:1; + u64 reserved_20_63:44; } cn50xx; struct cvmx_gmxx_rxx_int_reg_cn52xx { - uint64_t reserved_0_0:1; - uint64_t carext:1; - uint64_t reserved_2_2:1; - uint64_t jabber:1; - uint64_t fcserr:1; - uint64_t reserved_5_6:2; - uint64_t rcverr:1; - uint64_t skperr:1; - uint64_t reserved_9_9:1; - uint64_t ovrerr:1; - uint64_t pcterr:1; - uint64_t rsverr:1; - uint64_t falerr:1; - uint64_t coldet:1; - uint64_t ifgerr:1; - uint64_t reserved_16_18:3; - uint64_t pause_drp:1; - uint64_t loc_fault:1; - uint64_t rem_fault:1; - uint64_t bad_seq:1; - uint64_t bad_term:1; - uint64_t unsop:1; - uint64_t uneop:1; - uint64_t undat:1; - uint64_t hg2fld:1; - uint64_t hg2cc:1; - uint64_t reserved_29_63:35; + u64 reserved_0_0:1; + u64 carext:1; + u64 reserved_2_2:1; + u64 jabber:1; + u64 fcserr:1; + u64 reserved_5_6:2; + u64 rcverr:1; + u64 skperr:1; + u64 reserved_9_9:1; + u64 ovrerr:1; + u64 pcterr:1; + u64 rsverr:1; + u64 falerr:1; + u64 coldet:1; + u64 ifgerr:1; + u64 reserved_16_18:3; + u64 pause_drp:1; + u64 loc_fault:1; + u64 rem_fault:1; + u64 bad_seq:1; + u64 bad_term:1; + u64 unsop:1; + u64 uneop:1; + u64 undat:1; + u64 hg2fld:1; + u64 hg2cc:1; + u64 reserved_29_63:35; } cn52xx; struct cvmx_gmxx_rxx_int_reg_cn56xxp1 { - uint64_t reserved_0_0:1; - uint64_t carext:1; - uint64_t reserved_2_2:1; - uint64_t jabber:1; - uint64_t fcserr:1; - uint64_t reserved_5_6:2; - uint64_t rcverr:1; - uint64_t skperr:1; - uint64_t reserved_9_9:1; - uint64_t ovrerr:1; - uint64_t pcterr:1; - uint64_t rsverr:1; - uint64_t falerr:1; - uint64_t coldet:1; - uint64_t ifgerr:1; - uint64_t reserved_16_18:3; - uint64_t pause_drp:1; - uint64_t loc_fault:1; - uint64_t rem_fault:1; - uint64_t bad_seq:1; - uint64_t bad_term:1; - uint64_t unsop:1; - uint64_t uneop:1; - uint64_t undat:1; - uint64_t reserved_27_63:37; + u64 reserved_0_0:1; + u64 carext:1; + u64 reserved_2_2:1; + u64 jabber:1; + u64 fcserr:1; + u64 reserved_5_6:2; + u64 rcverr:1; + u64 skperr:1; + u64 reserved_9_9:1; + u64 ovrerr:1; + u64 pcterr:1; + u64 rsverr:1; + u64 falerr:1; + u64 coldet:1; + u64 ifgerr:1; + u64 reserved_16_18:3; + u64 pause_drp:1; + u64 loc_fault:1; + u64 rem_fault:1; + u64 bad_seq:1; + u64 bad_term:1; + u64 unsop:1; + u64 uneop:1; + u64 undat:1; + u64 reserved_27_63:37; } cn56xxp1; struct cvmx_gmxx_rxx_int_reg_cn58xx { - uint64_t minerr:1; - uint64_t carext:1; - uint64_t maxerr:1; - uint64_t jabber:1; - uint64_t fcserr:1; - uint64_t alnerr:1; - uint64_t lenerr:1; - uint64_t rcverr:1; - uint64_t skperr:1; - uint64_t niberr:1; - uint64_t ovrerr:1; - uint64_t pcterr:1; - uint64_t rsverr:1; - uint64_t falerr:1; - uint64_t coldet:1; - uint64_t ifgerr:1; - uint64_t phy_link:1; - uint64_t phy_spd:1; - uint64_t phy_dupx:1; - uint64_t pause_drp:1; - uint64_t reserved_20_63:44; + u64 minerr:1; + u64 carext:1; + u64 maxerr:1; + u64 jabber:1; + u64 fcserr:1; + u64 alnerr:1; + u64 lenerr:1; + u64 rcverr:1; + u64 skperr:1; + u64 niberr:1; + u64 ovrerr:1; + u64 pcterr:1; + u64 rsverr:1; + u64 falerr:1; + u64 coldet:1; + u64 ifgerr:1; + u64 phy_link:1; + u64 phy_spd:1; + u64 phy_dupx:1; + u64 pause_drp:1; + u64 reserved_20_63:44; } cn58xx; struct cvmx_gmxx_rxx_int_reg_cn61xx { - uint64_t minerr:1; - uint64_t carext:1; - uint64_t reserved_2_2:1; - uint64_t jabber:1; - uint64_t fcserr:1; - uint64_t reserved_5_6:2; - uint64_t rcverr:1; - uint64_t skperr:1; - uint64_t reserved_9_9:1; - uint64_t ovrerr:1; - uint64_t pcterr:1; - uint64_t rsverr:1; - uint64_t falerr:1; - uint64_t coldet:1; - uint64_t ifgerr:1; - uint64_t reserved_16_18:3; - uint64_t pause_drp:1; - uint64_t loc_fault:1; - uint64_t rem_fault:1; - uint64_t bad_seq:1; - uint64_t bad_term:1; - uint64_t unsop:1; - uint64_t uneop:1; - uint64_t undat:1; - uint64_t hg2fld:1; - uint64_t hg2cc:1; - uint64_t reserved_29_63:35; + u64 minerr:1; + u64 carext:1; + u64 reserved_2_2:1; + u64 jabber:1; + u64 fcserr:1; + u64 reserved_5_6:2; + u64 rcverr:1; + u64 skperr:1; + u64 reserved_9_9:1; + u64 ovrerr:1; + u64 pcterr:1; + u64 rsverr:1; + u64 falerr:1; + u64 coldet:1; + u64 ifgerr:1; + u64 reserved_16_18:3; + u64 pause_drp:1; + u64 loc_fault:1; + u64 rem_fault:1; + u64 bad_seq:1; + u64 bad_term:1; + u64 unsop:1; + u64 uneop:1; + u64 undat:1; + u64 hg2fld:1; + u64 hg2cc:1; + u64 reserved_29_63:35; } cn61xx; }; union cvmx_gmxx_prtx_cfg { - uint64_t u64; + u64 u64; struct cvmx_gmxx_prtx_cfg_s { - uint64_t reserved_22_63:42; - uint64_t pknd:6; - uint64_t reserved_14_15:2; - uint64_t tx_idle:1; - uint64_t rx_idle:1; - uint64_t reserved_9_11:3; - uint64_t speed_msb:1; - uint64_t reserved_4_7:4; - uint64_t slottime:1; - uint64_t duplex:1; - uint64_t speed:1; - uint64_t en:1; + u64 reserved_22_63:42; + u64 pknd:6; + u64 reserved_14_15:2; + u64 tx_idle:1; + u64 rx_idle:1; + u64 reserved_9_11:3; + u64 speed_msb:1; + u64 reserved_4_7:4; + u64 slottime:1; + u64 duplex:1; + u64 speed:1; + u64 en:1; } s; struct cvmx_gmxx_prtx_cfg_cn30xx { - uint64_t reserved_4_63:60; - uint64_t slottime:1; - uint64_t duplex:1; - uint64_t speed:1; - uint64_t en:1; + u64 reserved_4_63:60; + u64 slottime:1; + u64 duplex:1; + u64 speed:1; + u64 en:1; } cn30xx; struct cvmx_gmxx_prtx_cfg_cn52xx { - uint64_t reserved_14_63:50; - uint64_t tx_idle:1; - uint64_t rx_idle:1; - uint64_t reserved_9_11:3; - uint64_t speed_msb:1; - uint64_t reserved_4_7:4; - uint64_t slottime:1; - uint64_t duplex:1; - uint64_t speed:1; - uint64_t en:1; + u64 reserved_14_63:50; + u64 tx_idle:1; + u64 rx_idle:1; + u64 reserved_9_11:3; + u64 speed_msb:1; + u64 reserved_4_7:4; + u64 slottime:1; + u64 duplex:1; + u64 speed:1; + u64 en:1; } cn52xx; }; union cvmx_gmxx_rxx_adr_ctl { - uint64_t u64; + u64 u64; struct cvmx_gmxx_rxx_adr_ctl_s { - uint64_t reserved_4_63:60; - uint64_t cam_mode:1; - uint64_t mcst:2; - uint64_t bcst:1; + u64 reserved_4_63:60; + u64 cam_mode:1; + u64 mcst:2; + u64 bcst:1; } s; }; union cvmx_pip_prt_tagx { - uint64_t u64; + u64 u64; struct cvmx_pip_prt_tagx_s { - uint64_t reserved_54_63:10; - uint64_t portadd_en:1; - uint64_t inc_hwchk:1; - uint64_t reserved_50_51:2; - uint64_t grptagbase_msb:2; - uint64_t reserved_46_47:2; - uint64_t grptagmask_msb:2; - uint64_t reserved_42_43:2; - uint64_t grp_msb:2; - uint64_t grptagbase:4; - uint64_t grptagmask:4; - uint64_t grptag:1; - uint64_t grptag_mskip:1; - uint64_t tag_mode:2; - uint64_t inc_vs:2; - uint64_t inc_vlan:1; - uint64_t inc_prt_flag:1; - uint64_t ip6_dprt_flag:1; - uint64_t ip4_dprt_flag:1; - uint64_t ip6_sprt_flag:1; - uint64_t ip4_sprt_flag:1; - uint64_t ip6_nxth_flag:1; - uint64_t ip4_pctl_flag:1; - uint64_t ip6_dst_flag:1; - uint64_t ip4_dst_flag:1; - uint64_t ip6_src_flag:1; - uint64_t ip4_src_flag:1; - uint64_t tcp6_tag_type:2; - uint64_t tcp4_tag_type:2; - uint64_t ip6_tag_type:2; - uint64_t ip4_tag_type:2; - uint64_t non_tag_type:2; - uint64_t grp:4; + u64 reserved_54_63:10; + u64 portadd_en:1; + u64 inc_hwchk:1; + u64 reserved_50_51:2; + u64 grptagbase_msb:2; + u64 reserved_46_47:2; + u64 grptagmask_msb:2; + u64 reserved_42_43:2; + u64 grp_msb:2; + u64 grptagbase:4; + u64 grptagmask:4; + u64 grptag:1; + u64 grptag_mskip:1; + u64 tag_mode:2; + u64 inc_vs:2; + u64 inc_vlan:1; + u64 inc_prt_flag:1; + u64 ip6_dprt_flag:1; + u64 ip4_dprt_flag:1; + u64 ip6_sprt_flag:1; + u64 ip4_sprt_flag:1; + u64 ip6_nxth_flag:1; + u64 ip4_pctl_flag:1; + u64 ip6_dst_flag:1; + u64 ip4_dst_flag:1; + u64 ip6_src_flag:1; + u64 ip4_src_flag:1; + u64 tcp6_tag_type:2; + u64 tcp4_tag_type:2; + u64 ip6_tag_type:2; + u64 ip4_tag_type:2; + u64 non_tag_type:2; + u64 grp:4; } s; struct cvmx_pip_prt_tagx_cn30xx { - uint64_t reserved_40_63:24; - uint64_t grptagbase:4; - uint64_t grptagmask:4; - uint64_t grptag:1; - uint64_t reserved_30_30:1; - uint64_t tag_mode:2; - uint64_t inc_vs:2; - uint64_t inc_vlan:1; - uint64_t inc_prt_flag:1; - uint64_t ip6_dprt_flag:1; - uint64_t ip4_dprt_flag:1; - uint64_t ip6_sprt_flag:1; - uint64_t ip4_sprt_flag:1; - uint64_t ip6_nxth_flag:1; - uint64_t ip4_pctl_flag:1; - uint64_t ip6_dst_flag:1; - uint64_t ip4_dst_flag:1; - uint64_t ip6_src_flag:1; - uint64_t ip4_src_flag:1; - uint64_t tcp6_tag_type:2; - uint64_t tcp4_tag_type:2; - uint64_t ip6_tag_type:2; - uint64_t ip4_tag_type:2; - uint64_t non_tag_type:2; - uint64_t grp:4; + u64 reserved_40_63:24; + u64 grptagbase:4; + u64 grptagmask:4; + u64 grptag:1; + u64 reserved_30_30:1; + u64 tag_mode:2; + u64 inc_vs:2; + u64 inc_vlan:1; + u64 inc_prt_flag:1; + u64 ip6_dprt_flag:1; + u64 ip4_dprt_flag:1; + u64 ip6_sprt_flag:1; + u64 ip4_sprt_flag:1; + u64 ip6_nxth_flag:1; + u64 ip4_pctl_flag:1; + u64 ip6_dst_flag:1; + u64 ip4_dst_flag:1; + u64 ip6_src_flag:1; + u64 ip4_src_flag:1; + u64 tcp6_tag_type:2; + u64 tcp4_tag_type:2; + u64 ip6_tag_type:2; + u64 ip4_tag_type:2; + u64 non_tag_type:2; + u64 grp:4; } cn30xx; struct cvmx_pip_prt_tagx_cn50xx { - uint64_t reserved_40_63:24; - uint64_t grptagbase:4; - uint64_t grptagmask:4; - uint64_t grptag:1; - uint64_t grptag_mskip:1; - uint64_t tag_mode:2; - uint64_t inc_vs:2; - uint64_t inc_vlan:1; - uint64_t inc_prt_flag:1; - uint64_t ip6_dprt_flag:1; - uint64_t ip4_dprt_flag:1; - uint64_t ip6_sprt_flag:1; - uint64_t ip4_sprt_flag:1; - uint64_t ip6_nxth_flag:1; - uint64_t ip4_pctl_flag:1; - uint64_t ip6_dst_flag:1; - uint64_t ip4_dst_flag:1; - uint64_t ip6_src_flag:1; - uint64_t ip4_src_flag:1; - uint64_t tcp6_tag_type:2; - uint64_t tcp4_tag_type:2; - uint64_t ip6_tag_type:2; - uint64_t ip4_tag_type:2; - uint64_t non_tag_type:2; - uint64_t grp:4; + u64 reserved_40_63:24; + u64 grptagbase:4; + u64 grptagmask:4; + u64 grptag:1; + u64 grptag_mskip:1; + u64 tag_mode:2; + u64 inc_vs:2; + u64 inc_vlan:1; + u64 inc_prt_flag:1; + u64 ip6_dprt_flag:1; + u64 ip4_dprt_flag:1; + u64 ip6_sprt_flag:1; + u64 ip4_sprt_flag:1; + u64 ip6_nxth_flag:1; + u64 ip4_pctl_flag:1; + u64 ip6_dst_flag:1; + u64 ip4_dst_flag:1; + u64 ip6_src_flag:1; + u64 ip4_src_flag:1; + u64 tcp6_tag_type:2; + u64 tcp4_tag_type:2; + u64 ip6_tag_type:2; + u64 ip4_tag_type:2; + u64 non_tag_type:2; + u64 grp:4; } cn50xx; }; union cvmx_spxx_int_reg { - uint64_t u64; + u64 u64; struct cvmx_spxx_int_reg_s { - uint64_t reserved_32_63:32; - uint64_t spf:1; - uint64_t reserved_12_30:19; - uint64_t calerr:1; - uint64_t syncerr:1; - uint64_t diperr:1; - uint64_t tpaovr:1; - uint64_t rsverr:1; - uint64_t drwnng:1; - uint64_t clserr:1; - uint64_t spiovr:1; - uint64_t reserved_2_3:2; - uint64_t abnorm:1; - uint64_t prtnxa:1; + u64 reserved_32_63:32; + u64 spf:1; + u64 reserved_12_30:19; + u64 calerr:1; + u64 syncerr:1; + u64 diperr:1; + u64 tpaovr:1; + u64 rsverr:1; + u64 drwnng:1; + u64 clserr:1; + u64 spiovr:1; + u64 reserved_2_3:2; + u64 abnorm:1; + u64 prtnxa:1; } s; }; union cvmx_spxx_int_msk { - uint64_t u64; + u64 u64; struct cvmx_spxx_int_msk_s { - uint64_t reserved_12_63:52; - uint64_t calerr:1; - uint64_t syncerr:1; - uint64_t diperr:1; - uint64_t tpaovr:1; - uint64_t rsverr:1; - uint64_t drwnng:1; - uint64_t clserr:1; - uint64_t spiovr:1; - uint64_t reserved_2_3:2; - uint64_t abnorm:1; - uint64_t prtnxa:1; + u64 reserved_12_63:52; + u64 calerr:1; + u64 syncerr:1; + u64 diperr:1; + u64 tpaovr:1; + u64 rsverr:1; + u64 drwnng:1; + u64 clserr:1; + u64 spiovr:1; + u64 reserved_2_3:2; + u64 abnorm:1; + u64 prtnxa:1; } s; }; union cvmx_pow_wq_int { - uint64_t u64; + u64 u64; struct cvmx_pow_wq_int_s { - uint64_t wq_int:16; - uint64_t iq_dis:16; - uint64_t reserved_32_63:32; + u64 wq_int:16; + u64 iq_dis:16; + u64 reserved_32_63:32; } s; }; union cvmx_sso_wq_int_thrx { - uint64_t u64; + u64 u64; struct { - uint64_t iq_thr:12; - uint64_t reserved_12_13:2; - uint64_t ds_thr:12; - uint64_t reserved_26_27:2; - uint64_t tc_thr:4; - uint64_t tc_en:1; - uint64_t reserved_33_63:31; + u64 iq_thr:12; + u64 reserved_12_13:2; + u64 ds_thr:12; + u64 reserved_26_27:2; + u64 tc_thr:4; + u64 tc_en:1; + u64 reserved_33_63:31; } s; }; union cvmx_stxx_int_reg { - uint64_t u64; + u64 u64; struct cvmx_stxx_int_reg_s { - uint64_t reserved_9_63:55; - uint64_t syncerr:1; - uint64_t frmerr:1; - uint64_t unxfrm:1; - uint64_t nosync:1; - uint64_t diperr:1; - uint64_t datovr:1; - uint64_t ovrbst:1; - uint64_t calpar1:1; - uint64_t calpar0:1; + u64 reserved_9_63:55; + u64 syncerr:1; + u64 frmerr:1; + u64 unxfrm:1; + u64 nosync:1; + u64 diperr:1; + u64 datovr:1; + u64 ovrbst:1; + u64 calpar1:1; + u64 calpar0:1; } s; }; union cvmx_stxx_int_msk { - uint64_t u64; + u64 u64; struct cvmx_stxx_int_msk_s { - uint64_t reserved_8_63:56; - uint64_t frmerr:1; - uint64_t unxfrm:1; - uint64_t nosync:1; - uint64_t diperr:1; - uint64_t datovr:1; - uint64_t ovrbst:1; - uint64_t calpar1:1; - uint64_t calpar0:1; + u64 reserved_8_63:56; + u64 frmerr:1; + u64 unxfrm:1; + u64 nosync:1; + u64 diperr:1; + u64 datovr:1; + u64 ovrbst:1; + u64 calpar1:1; + u64 calpar0:1; } s; }; union cvmx_pow_wq_int_pc { - uint64_t u64; + u64 u64; struct cvmx_pow_wq_int_pc_s { - uint64_t reserved_0_7:8; - uint64_t pc_thr:20; - uint64_t reserved_28_31:4; - uint64_t pc:28; - uint64_t reserved_60_63:4; + u64 reserved_0_7:8; + u64 pc_thr:20; + u64 reserved_28_31:4; + u64 pc:28; + u64 reserved_60_63:4; } s; }; union cvmx_pow_wq_int_thrx { - uint64_t u64; + u64 u64; struct cvmx_pow_wq_int_thrx_s { - uint64_t reserved_29_63:35; - uint64_t tc_en:1; - uint64_t tc_thr:4; - uint64_t reserved_23_23:1; - uint64_t ds_thr:11; - uint64_t reserved_11_11:1; - uint64_t iq_thr:11; + u64 reserved_29_63:35; + u64 tc_en:1; + u64 tc_thr:4; + u64 reserved_23_23:1; + u64 ds_thr:11; + u64 reserved_11_11:1; + u64 iq_thr:11; } s; struct cvmx_pow_wq_int_thrx_cn30xx { - uint64_t reserved_29_63:35; - uint64_t tc_en:1; - uint64_t tc_thr:4; - uint64_t reserved_18_23:6; - uint64_t ds_thr:6; - uint64_t reserved_6_11:6; - uint64_t iq_thr:6; + u64 reserved_29_63:35; + u64 tc_en:1; + u64 tc_thr:4; + u64 reserved_18_23:6; + u64 ds_thr:6; + u64 reserved_6_11:6; + u64 iq_thr:6; } cn30xx; struct cvmx_pow_wq_int_thrx_cn31xx { - uint64_t reserved_29_63:35; - uint64_t tc_en:1; - uint64_t tc_thr:4; - uint64_t reserved_20_23:4; - uint64_t ds_thr:8; - uint64_t reserved_8_11:4; - uint64_t iq_thr:8; + u64 reserved_29_63:35; + u64 tc_en:1; + u64 tc_thr:4; + u64 reserved_20_23:4; + u64 ds_thr:8; + u64 reserved_8_11:4; + u64 iq_thr:8; } cn31xx; struct cvmx_pow_wq_int_thrx_cn52xx { - uint64_t reserved_29_63:35; - uint64_t tc_en:1; - uint64_t tc_thr:4; - uint64_t reserved_21_23:3; - uint64_t ds_thr:9; - uint64_t reserved_9_11:3; - uint64_t iq_thr:9; + u64 reserved_29_63:35; + u64 tc_en:1; + u64 tc_thr:4; + u64 reserved_21_23:3; + u64 ds_thr:9; + u64 reserved_9_11:3; + u64 iq_thr:9; } cn52xx; struct cvmx_pow_wq_int_thrx_cn63xx { - uint64_t reserved_29_63:35; - uint64_t tc_en:1; - uint64_t tc_thr:4; - uint64_t reserved_22_23:2; - uint64_t ds_thr:10; - uint64_t reserved_10_11:2; - uint64_t iq_thr:10; + u64 reserved_29_63:35; + u64 tc_en:1; + u64 tc_thr:4; + u64 reserved_22_23:2; + u64 ds_thr:10; + u64 reserved_10_11:2; + u64 iq_thr:10; } cn63xx; }; union cvmx_npi_rsl_int_blocks { - uint64_t u64; + u64 u64; struct cvmx_npi_rsl_int_blocks_s { - uint64_t reserved_32_63:32; - uint64_t rint_31:1; - uint64_t iob:1; - uint64_t reserved_28_29:2; - uint64_t rint_27:1; - uint64_t rint_26:1; - uint64_t rint_25:1; - uint64_t rint_24:1; - uint64_t asx1:1; - uint64_t asx0:1; - uint64_t rint_21:1; - uint64_t pip:1; - uint64_t spx1:1; - uint64_t spx0:1; - uint64_t lmc:1; - uint64_t l2c:1; - uint64_t rint_15:1; - uint64_t reserved_13_14:2; - uint64_t pow:1; - uint64_t tim:1; - uint64_t pko:1; - uint64_t ipd:1; - uint64_t rint_8:1; - uint64_t zip:1; - uint64_t dfa:1; - uint64_t fpa:1; - uint64_t key:1; - uint64_t npi:1; - uint64_t gmx1:1; - uint64_t gmx0:1; - uint64_t mio:1; + u64 reserved_32_63:32; + u64 rint_31:1; + u64 iob:1; + u64 reserved_28_29:2; + u64 rint_27:1; + u64 rint_26:1; + u64 rint_25:1; + u64 rint_24:1; + u64 asx1:1; + u64 asx0:1; + u64 rint_21:1; + u64 pip:1; + u64 spx1:1; + u64 spx0:1; + u64 lmc:1; + u64 l2c:1; + u64 rint_15:1; + u64 reserved_13_14:2; + u64 pow:1; + u64 tim:1; + u64 pko:1; + u64 ipd:1; + u64 rint_8:1; + u64 zip:1; + u64 dfa:1; + u64 fpa:1; + u64 key:1; + u64 npi:1; + u64 gmx1:1; + u64 gmx0:1; + u64 mio:1; } s; struct cvmx_npi_rsl_int_blocks_cn30xx { - uint64_t reserved_32_63:32; - uint64_t rint_31:1; - uint64_t iob:1; - uint64_t rint_29:1; - uint64_t rint_28:1; - uint64_t rint_27:1; - uint64_t rint_26:1; - uint64_t rint_25:1; - uint64_t rint_24:1; - uint64_t asx1:1; - uint64_t asx0:1; - uint64_t rint_21:1; - uint64_t pip:1; - uint64_t spx1:1; - uint64_t spx0:1; - uint64_t lmc:1; - uint64_t l2c:1; - uint64_t rint_15:1; - uint64_t rint_14:1; - uint64_t usb:1; - uint64_t pow:1; - uint64_t tim:1; - uint64_t pko:1; - uint64_t ipd:1; - uint64_t rint_8:1; - uint64_t zip:1; - uint64_t dfa:1; - uint64_t fpa:1; - uint64_t key:1; - uint64_t npi:1; - uint64_t gmx1:1; - uint64_t gmx0:1; - uint64_t mio:1; + u64 reserved_32_63:32; + u64 rint_31:1; + u64 iob:1; + u64 rint_29:1; + u64 rint_28:1; + u64 rint_27:1; + u64 rint_26:1; + u64 rint_25:1; + u64 rint_24:1; + u64 asx1:1; + u64 asx0:1; + u64 rint_21:1; + u64 pip:1; + u64 spx1:1; + u64 spx0:1; + u64 lmc:1; + u64 l2c:1; + u64 rint_15:1; + u64 rint_14:1; + u64 usb:1; + u64 pow:1; + u64 tim:1; + u64 pko:1; + u64 ipd:1; + u64 rint_8:1; + u64 zip:1; + u64 dfa:1; + u64 fpa:1; + u64 key:1; + u64 npi:1; + u64 gmx1:1; + u64 gmx0:1; + u64 mio:1; } cn30xx; struct cvmx_npi_rsl_int_blocks_cn38xx { - uint64_t reserved_32_63:32; - uint64_t rint_31:1; - uint64_t iob:1; - uint64_t rint_29:1; - uint64_t rint_28:1; - uint64_t rint_27:1; - uint64_t rint_26:1; - uint64_t rint_25:1; - uint64_t rint_24:1; - uint64_t asx1:1; - uint64_t asx0:1; - uint64_t rint_21:1; - uint64_t pip:1; - uint64_t spx1:1; - uint64_t spx0:1; - uint64_t lmc:1; - uint64_t l2c:1; - uint64_t rint_15:1; - uint64_t rint_14:1; - uint64_t rint_13:1; - uint64_t pow:1; - uint64_t tim:1; - uint64_t pko:1; - uint64_t ipd:1; - uint64_t rint_8:1; - uint64_t zip:1; - uint64_t dfa:1; - uint64_t fpa:1; - uint64_t key:1; - uint64_t npi:1; - uint64_t gmx1:1; - uint64_t gmx0:1; - uint64_t mio:1; + u64 reserved_32_63:32; + u64 rint_31:1; + u64 iob:1; + u64 rint_29:1; + u64 rint_28:1; + u64 rint_27:1; + u64 rint_26:1; + u64 rint_25:1; + u64 rint_24:1; + u64 asx1:1; + u64 asx0:1; + u64 rint_21:1; + u64 pip:1; + u64 spx1:1; + u64 spx0:1; + u64 lmc:1; + u64 l2c:1; + u64 rint_15:1; + u64 rint_14:1; + u64 rint_13:1; + u64 pow:1; + u64 tim:1; + u64 pko:1; + u64 ipd:1; + u64 rint_8:1; + u64 zip:1; + u64 dfa:1; + u64 fpa:1; + u64 key:1; + u64 npi:1; + u64 gmx1:1; + u64 gmx0:1; + u64 mio:1; } cn38xx; struct cvmx_npi_rsl_int_blocks_cn50xx { - uint64_t reserved_31_63:33; - uint64_t iob:1; - uint64_t lmc1:1; - uint64_t agl:1; - uint64_t reserved_24_27:4; - uint64_t asx1:1; - uint64_t asx0:1; - uint64_t reserved_21_21:1; - uint64_t pip:1; - uint64_t spx1:1; - uint64_t spx0:1; - uint64_t lmc:1; - uint64_t l2c:1; - uint64_t reserved_15_15:1; - uint64_t rad:1; - uint64_t usb:1; - uint64_t pow:1; - uint64_t tim:1; - uint64_t pko:1; - uint64_t ipd:1; - uint64_t reserved_8_8:1; - uint64_t zip:1; - uint64_t dfa:1; - uint64_t fpa:1; - uint64_t key:1; - uint64_t npi:1; - uint64_t gmx1:1; - uint64_t gmx0:1; - uint64_t mio:1; + u64 reserved_31_63:33; + u64 iob:1; + u64 lmc1:1; + u64 agl:1; + u64 reserved_24_27:4; + u64 asx1:1; + u64 asx0:1; + u64 reserved_21_21:1; + u64 pip:1; + u64 spx1:1; + u64 spx0:1; + u64 lmc:1; + u64 l2c:1; + u64 reserved_15_15:1; + u64 rad:1; + u64 usb:1; + u64 pow:1; + u64 tim:1; + u64 pko:1; + u64 ipd:1; + u64 reserved_8_8:1; + u64 zip:1; + u64 dfa:1; + u64 fpa:1; + u64 key:1; + u64 npi:1; + u64 gmx1:1; + u64 gmx0:1; + u64 mio:1; } cn50xx; }; union cvmx_pko_command_word0 { - uint64_t u64; + u64 u64; struct { - uint64_t total_bytes:16; - uint64_t segs:6; - uint64_t dontfree:1; - uint64_t ignore_i:1; - uint64_t ipoffp1:7; - uint64_t gather:1; - uint64_t rsp:1; - uint64_t wqp:1; - uint64_t n2:1; - uint64_t le:1; - uint64_t reg0:11; - uint64_t subone0:1; - uint64_t reg1:11; - uint64_t subone1:1; - uint64_t size0:2; - uint64_t size1:2; + u64 total_bytes:16; + u64 segs:6; + u64 dontfree:1; + u64 ignore_i:1; + u64 ipoffp1:7; + u64 gather:1; + u64 rsp:1; + u64 wqp:1; + u64 n2:1; + u64 le:1; + u64 reg0:11; + u64 subone0:1; + u64 reg1:11; + u64 subone1:1; + u64 size0:2; + u64 size1:2; } s; }; union cvmx_ciu_timx { - uint64_t u64; + u64 u64; struct cvmx_ciu_timx_s { - uint64_t reserved_37_63:27; - uint64_t one_shot:1; - uint64_t len:36; + u64 reserved_37_63:27; + u64 one_shot:1; + u64 len:36; } s; }; union cvmx_gmxx_rxx_rx_inbnd { - uint64_t u64; + u64 u64; struct cvmx_gmxx_rxx_rx_inbnd_s { - uint64_t status:1; - uint64_t speed:2; - uint64_t duplex:1; - uint64_t reserved_4_63:60; + u64 status:1; + u64 speed:2; + u64 duplex:1; + u64 reserved_4_63:60; } s; }; @@ -1196,12 +1196,12 @@ static inline void cvmx_fau_atomic_write32(enum cvmx_fau_reg_32 reg, int32_t value) { } -static inline uint64_t cvmx_scratch_read64(uint64_t address) +static inline u64 cvmx_scratch_read64(u64 address) { return 0; } -static inline void cvmx_scratch_write64(uint64_t address, uint64_t value) +static inline void cvmx_scratch_write64(u64 address, u64 value) { } static inline int cvmx_wqe_get_grp(struct cvmx_wqe *work) @@ -1209,7 +1209,7 @@ static inline int cvmx_wqe_get_grp(struct cvmx_wqe *work) return 0; } -static inline void *cvmx_phys_to_ptr(uint64_t physical_address) +static inline void *cvmx_phys_to_ptr(u64 physical_address) { return (void *)(uintptr_t)(physical_address); } @@ -1232,12 +1232,12 @@ static inline int cvmx_helper_get_interface_index_num(int ipd_port) static inline void cvmx_fpa_enable(void) { } -static inline uint64_t cvmx_read_csr(uint64_t csr_addr) +static inline u64 cvmx_read_csr(u64 csr_addr) { return 0; } -static inline void cvmx_write_csr(uint64_t csr_addr, uint64_t val) +static inline void cvmx_write_csr(u64 csr_addr, u64 val) { } static inline int cvmx_helper_setup_red(int pass_thresh, int drop_thresh) @@ -1245,13 +1245,13 @@ static inline int cvmx_helper_setup_red(int pass_thresh, int drop_thresh) return 0; } -static inline void *cvmx_fpa_alloc(uint64_t pool) +static inline void *cvmx_fpa_alloc(u64 pool) { return NULL; } -static inline void cvmx_fpa_free(void *ptr, uint64_t pool, - uint64_t num_cache_lines) +static inline void cvmx_fpa_free(void *ptr, u64 pool, + u64 num_cache_lines) { } static inline int octeon_is_simulation(void) @@ -1259,11 +1259,11 @@ static inline int octeon_is_simulation(void) return 1; } -static inline void cvmx_pip_get_port_status(uint64_t port_num, uint64_t clear, +static inline void cvmx_pip_get_port_status(u64 port_num, u64 clear, cvmx_pip_port_status_t *status) { } -static inline void cvmx_pko_get_port_status(uint64_t port_num, uint64_t clear, +static inline void cvmx_pko_get_port_status(u64 port_num, u64 clear, cvmx_pko_port_status_t *status) { } @@ -1369,7 +1369,7 @@ static inline int cvmx_spi_restart_interface(int interface, return 0; } -static inline void cvmx_fau_async_fetch_and_add32(uint64_t scraddr, +static inline void cvmx_fau_async_fetch_and_add32(u64 scraddr, enum cvmx_fau_reg_32 reg, int32_t value) { } @@ -1382,12 +1382,12 @@ static inline union cvmx_gmxx_rxx_rx_inbnd cvmx_spi4000_check_speed(int interfac return r; } -static inline void cvmx_pko_send_packet_prepare(uint64_t port, uint64_t queue, +static inline void cvmx_pko_send_packet_prepare(u64 port, u64 queue, cvmx_pko_lock_t use_locking) { } -static inline cvmx_pko_status_t cvmx_pko_send_packet_finish(uint64_t port, - uint64_t queue, union cvmx_pko_command_word0 pko_command, +static inline cvmx_pko_status_t cvmx_pko_send_packet_finish(u64 port, + u64 queue, union cvmx_pko_command_word0 pko_command, union cvmx_buf_ptr packet, cvmx_pko_lock_t use_locking) { return 0; @@ -1407,9 +1407,9 @@ static inline int cvmx_wqe_get_qos(struct cvmx_wqe *work) static inline void cvmx_wqe_set_grp(struct cvmx_wqe *work, int grp) { } -static inline void cvmx_pow_work_submit(struct cvmx_wqe *wqp, uint32_t tag, +static inline void cvmx_pow_work_submit(struct cvmx_wqe *wqp, u32 tag, enum cvmx_pow_tag_type tag_type, - uint64_t qos, uint64_t grp) + u64 qos, u64 grp) { } #define CVMX_ASXX_RX_CLK_SETX(a, b) ((a) + (b)) diff --git a/drivers/staging/rtl8723bs/core/rtw_ap.c b/drivers/staging/rtl8723bs/core/rtw_ap.c index 864cd8b6d1f1..4b4012411011 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ap.c +++ b/drivers/staging/rtl8723bs/core/rtw_ap.c @@ -367,7 +367,6 @@ void add_ratid(struct adapter *padapter, struct sta_info *psta, u8 rssi_level) void update_bmc_sta(struct adapter *padapter) { unsigned char network_type; - int support_rate_num = 0; unsigned int tx_ra_bitmap = 0; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; @@ -388,12 +387,10 @@ void update_bmc_sta(struct adapter *padapter) psta->ieee8021x_blocked = false; - memset((void *)&psta->sta_stats, 0, sizeof(struct stainfo_stats)); + memset(&psta->sta_stats, 0, sizeof(struct stainfo_stats)); /* prepare for add_ratid */ - support_rate_num = rtw_get_rateset_len((u8 *)&pcur_network->supported_rates); network_type = rtw_check_network_type((u8 *)&pcur_network->supported_rates, - support_rate_num, pcur_network->configuration.ds_config ); if (is_supported_tx_cck(network_type)) { @@ -545,7 +542,7 @@ void update_sta_info_apmode(struct adapter *padapter, struct sta_info *psta) /* todo: init other variables */ - memset((void *)&psta->sta_stats, 0, sizeof(struct stainfo_stats)); + memset(&psta->sta_stats, 0, sizeof(struct stainfo_stats)); /* add ratid */ /* add_ratid(padapter, psta); move to ap_sta_info_defer_update() */ @@ -800,7 +797,7 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) { int ret = _SUCCESS; u8 *p; - u8 *pHT_caps_ie = NULL; + u8 *ht_caps_ie = NULL; u8 *pHT_info_ie = NULL; struct sta_info *psta = NULL; u16 cap, ht_cap = false; @@ -885,12 +882,10 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) WLAN_EID_EXT_SUPP_RATES, &ie_len, pbss_network->ie_length - _BEACON_IE_OFFSET_); - if (p) { + if (p) memcpy(support_rate + support_rate_num, p + 2, ie_len); - support_rate_num += ie_len; - } - network_type = rtw_check_network_type(support_rate, support_rate_num, channel); + network_type = rtw_check_network_type(support_rate, channel); rtw_set_supported_rate(pbss_network->supported_rates, network_type); @@ -1006,17 +1001,17 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) u8 max_rx_ampdu_factor = 0; struct ieee80211_ht_cap *pht_cap = (struct ieee80211_ht_cap *)(p + 2); - pHT_caps_ie = p; + ht_caps_ie = p; ht_cap = true; network_type |= WIRELESS_11_24N; rtw_ht_use_default_setting(padapter); - if (pmlmepriv->htpriv.sgi_20m == false) + if (!pmlmepriv->htpriv.sgi_20m) pht_cap->cap_info &= cpu_to_le16(~(IEEE80211_HT_CAP_SGI_20)); - if (pmlmepriv->htpriv.sgi_40m == false) + if (!pmlmepriv->htpriv.sgi_40m) pht_cap->cap_info &= cpu_to_le16(~(IEEE80211_HT_CAP_SGI_40)); if (!TEST_FLAG(pmlmepriv->htpriv.ldpc_cap, LDPC_HT_ENABLE_RX)) @@ -1094,7 +1089,7 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) if (pregistrypriv->ampdu_enable == 1) pmlmepriv->htpriv.ampdu_enable = true; - HT_caps_handler(padapter, (struct ndis_80211_var_ie *)pHT_caps_ie); + HT_caps_handler(padapter, (struct ndis_80211_var_ie *)ht_caps_ie); HT_info_handler(padapter, (struct ndis_80211_var_ie *)pHT_info_ie); } @@ -1156,7 +1151,7 @@ int rtw_acl_add_sta(struct adapter *padapter, u8 *addr) paclnode = list_entry(plist, struct rtw_wlan_acl_node, list); if (!memcmp(paclnode->addr, addr, ETH_ALEN)) { - if (paclnode->valid == true) { + if (paclnode->valid) { added = true; break; } @@ -1369,20 +1364,20 @@ static void update_bcn_erpinfo_ie(struct adapter *padapter) &len, (pnetwork->ie_length - _BEACON_IE_OFFSET_)); if (p && len > 0) { - struct ndis_80211_var_ie *pIE = (struct ndis_80211_var_ie *)p; + struct ndis_80211_var_ie *ie = (struct ndis_80211_var_ie *)p; if (pmlmepriv->num_sta_non_erp == 1) - pIE->data[0] |= RTW_ERP_INFO_NON_ERP_PRESENT | RTW_ERP_INFO_USE_PROTECTION; + ie->data[0] |= RTW_ERP_INFO_NON_ERP_PRESENT | RTW_ERP_INFO_USE_PROTECTION; else - pIE->data[0] &= ~(RTW_ERP_INFO_NON_ERP_PRESENT | + ie->data[0] &= ~(RTW_ERP_INFO_NON_ERP_PRESENT | RTW_ERP_INFO_USE_PROTECTION); if (pmlmepriv->num_sta_no_short_preamble > 0) - pIE->data[0] |= RTW_ERP_INFO_BARKER_PREAMBLE_MODE; + ie->data[0] |= RTW_ERP_INFO_BARKER_PREAMBLE_MODE; else - pIE->data[0] &= ~(RTW_ERP_INFO_BARKER_PREAMBLE_MODE); + ie->data[0] &= ~(RTW_ERP_INFO_BARKER_PREAMBLE_MODE); - ERP_IE_handler(padapter, pIE); + ERP_IE_handler(padapter, ie); } } @@ -1439,7 +1434,7 @@ static void update_bcn_wps_ie(struct adapter *padapter) remainder_ielen = ielen - wps_offset - wps_ielen; - if (premainder_ie && remainder_ielen) + if (remainder_ielen) pbackup_remainder_ie = kmemdup(premainder_ie, remainder_ielen, GFP_ATOMIC); wps_ielen = (uint)pwps_ie_src[1];/* to get ie data len */ @@ -1573,8 +1568,8 @@ static int rtw_ht_operation_update(struct adapter *padapter) if (pmlmepriv->htpriv.ht_option) return 0; - if (!(pmlmepriv->ht_op_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) - && pmlmepriv->num_sta_ht_no_gf) { + if (!(pmlmepriv->ht_op_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) && + pmlmepriv->num_sta_ht_no_gf) { pmlmepriv->ht_op_mode |= IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT; op_mode_changes++; @@ -1780,8 +1775,8 @@ u8 bss_cap_update_on_sta_leave(struct adapter *padapter, struct sta_info *psta) if (psta->no_short_preamble_set) { psta->no_short_preamble_set = 0; pmlmepriv->num_sta_no_short_preamble--; - if (pmlmeext->cur_wireless_mode > WIRELESS_11B - && pmlmepriv->num_sta_no_short_preamble == 0){ + if (pmlmeext->cur_wireless_mode > WIRELESS_11B && + pmlmepriv->num_sta_no_short_preamble == 0){ beacon_updated = true; update_beacon(padapter, 0xFF, NULL, true); } @@ -1799,8 +1794,8 @@ u8 bss_cap_update_on_sta_leave(struct adapter *padapter, struct sta_info *psta) if (psta->no_short_slot_time_set) { psta->no_short_slot_time_set = 0; pmlmepriv->num_sta_no_short_slot_time--; - if (pmlmeext->cur_wireless_mode > WIRELESS_11B - && pmlmepriv->num_sta_no_short_slot_time == 0){ + if (pmlmeext->cur_wireless_mode > WIRELESS_11B && + pmlmepriv->num_sta_no_short_slot_time == 0){ beacon_updated = true; update_beacon(padapter, 0xFF, NULL, true); } diff --git a/drivers/staging/rtl8723bs/core/rtw_btcoex.c b/drivers/staging/rtl8723bs/core/rtw_btcoex.c index f4b19ef7b341..0191a943f0a4 100644 --- a/drivers/staging/rtl8723bs/core/rtw_btcoex.c +++ b/drivers/staging/rtl8723bs/core/rtw_btcoex.c @@ -54,7 +54,6 @@ void rtw_btcoex_LPS_Enter(struct adapter *padapter) struct pwrctrl_priv *pwrpriv; u8 lps_val; - pwrpriv = adapter_to_pwrctl(padapter); pwrpriv->bpower_saving = true; diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index abb84f8aecbe..c1185c25ed36 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -8,6 +8,7 @@ #include #include #include +#include static struct _cmd_callback rtw_cmd_callback[] = { {GEN_CMD_CODE(_Read_MACREG), NULL}, /*0*/ @@ -214,7 +215,7 @@ void _rtw_free_evt_priv(struct evt_priv *pevtpriv) { _cancel_workitem_sync(&pevtpriv->c2h_wk); while (pevtpriv->c2h_wk_alive) - msleep(10); + fsleep(10 * USEC_PER_MSEC); while (!rtw_cbuf_empty(pevtpriv->c2h_queue)) { void *c2h = rtw_cbuf_pop(pevtpriv->c2h_queue); @@ -1125,14 +1126,18 @@ static void collect_traffic_statistics(struct adapter *padapter) pdvobjpriv->traffic_stat.cur_rx_tp = (u32)(pdvobjpriv->traffic_stat.cur_rx_bytes * 8 / 2 / 1024 / 1024); } -u8 traffic_status_watchdog(struct adapter *padapter, u8 from_timer) +bool traffic_status_watchdog(struct adapter *padapter, bool from_timer) { - u8 bEnterPS = false; - u16 BusyThresholdHigh = 25; - u16 BusyThresholdLow = 10; - u16 BusyThreshold = BusyThresholdHigh; - u8 bBusyTraffic = false, bTxBusyTraffic = false, bRxBusyTraffic = false; - u8 bHigherBusyTraffic = false, bHigherBusyRxTraffic = false, bHigherBusyTxTraffic = false; + bool should_enter_ps = false; + u16 busy_threshold_high = 25; + u16 busy_threshold_low = 10; + u16 busy_threshold = busy_threshold_high; + bool busy_traffic = false; + bool tx_busy_traffic = false; + bool rx_busy_traffic = false; + bool higher_busy_traffic = false; + bool higher_busy_rx_traffic = false; + bool higher_busy_tx_traffic = false; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; collect_traffic_statistics(padapter); @@ -1142,57 +1147,60 @@ u8 traffic_status_watchdog(struct adapter *padapter, u8 from_timer) /* */ if ((check_fwstate(pmlmepriv, _FW_LINKED)) /*&& !MgntInitAdapterInProgress(pMgntInfo)*/) { - /* if we raise bBusyTraffic in last watchdog, using lower threshold. */ - if (pmlmepriv->LinkDetectInfo.bBusyTraffic) - BusyThreshold = BusyThresholdLow; + /* if we raise busy_traffic in last watchdog, using lower threshold. */ + if (pmlmepriv->link_detect_info.busy_traffic) + busy_threshold = busy_threshold_low; - if (pmlmepriv->LinkDetectInfo.NumRxOkInPeriod > BusyThreshold || - pmlmepriv->LinkDetectInfo.NumTxOkInPeriod > BusyThreshold) { - bBusyTraffic = true; + if (pmlmepriv->link_detect_info.num_rx_ok_in_period > busy_threshold || + pmlmepriv->link_detect_info.num_tx_ok_in_period > busy_threshold) { + busy_traffic = true; - if (pmlmepriv->LinkDetectInfo.NumRxOkInPeriod > pmlmepriv->LinkDetectInfo.NumTxOkInPeriod) - bRxBusyTraffic = true; + if (pmlmepriv->link_detect_info.num_rx_ok_in_period > + pmlmepriv->link_detect_info.num_tx_ok_in_period) + rx_busy_traffic = true; else - bTxBusyTraffic = true; + tx_busy_traffic = true; } /* Higher Tx/Rx data. */ - if (pmlmepriv->LinkDetectInfo.NumRxOkInPeriod > 4000 || - pmlmepriv->LinkDetectInfo.NumTxOkInPeriod > 4000) { - bHigherBusyTraffic = true; + if (pmlmepriv->link_detect_info.num_rx_ok_in_period > 4000 || + pmlmepriv->link_detect_info.num_tx_ok_in_period > 4000) { + higher_busy_traffic = true; - if (pmlmepriv->LinkDetectInfo.NumRxOkInPeriod > pmlmepriv->LinkDetectInfo.NumTxOkInPeriod) - bHigherBusyRxTraffic = true; + if (pmlmepriv->link_detect_info.num_rx_ok_in_period > + pmlmepriv->link_detect_info.num_tx_ok_in_period) + higher_busy_rx_traffic = true; else - bHigherBusyTxTraffic = true; + higher_busy_tx_traffic = true; } /* check traffic for powersaving. */ - if (((pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod + pmlmepriv->LinkDetectInfo.NumTxOkInPeriod) > 8) || - (pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod > 2)) { - bEnterPS = false; + if (((pmlmepriv->link_detect_info.num_rx_unicast_ok_in_period + + pmlmepriv->link_detect_info.num_tx_ok_in_period) > 8) || + (pmlmepriv->link_detect_info.num_rx_unicast_ok_in_period > 2)) { + should_enter_ps = false; - if (bBusyTraffic) { - if (pmlmepriv->LinkDetectInfo.TrafficTransitionCount <= 4) - pmlmepriv->LinkDetectInfo.TrafficTransitionCount = 4; + if (busy_traffic) { + if (pmlmepriv->link_detect_info.traffic_transition_count <= 4) + pmlmepriv->link_detect_info.traffic_transition_count = 4; - pmlmepriv->LinkDetectInfo.TrafficTransitionCount++; + pmlmepriv->link_detect_info.traffic_transition_count++; - if (pmlmepriv->LinkDetectInfo.TrafficTransitionCount > 30/*TrafficTransitionLevel*/) - pmlmepriv->LinkDetectInfo.TrafficTransitionCount = 30; + if (pmlmepriv->link_detect_info.traffic_transition_count > 30) + pmlmepriv->link_detect_info.traffic_transition_count = 30; } } else { - if (pmlmepriv->LinkDetectInfo.TrafficTransitionCount >= 2) - pmlmepriv->LinkDetectInfo.TrafficTransitionCount -= 2; + if (pmlmepriv->link_detect_info.traffic_transition_count >= 2) + pmlmepriv->link_detect_info.traffic_transition_count -= 2; else - pmlmepriv->LinkDetectInfo.TrafficTransitionCount = 0; + pmlmepriv->link_detect_info.traffic_transition_count = 0; - if (pmlmepriv->LinkDetectInfo.TrafficTransitionCount == 0) - bEnterPS = true; + if (pmlmepriv->link_detect_info.traffic_transition_count == 0) + should_enter_ps = true; } /* LeisurePS only work in infra mode. */ - if (bEnterPS) { + if (should_enter_ps) { if (!from_timer) LPS_Enter(padapter, "TRAFFIC_IDLE"); } else { @@ -1212,17 +1220,17 @@ u8 traffic_status_watchdog(struct adapter *padapter, u8 from_timer) LPS_Leave(padapter, "NON_LINKED"); } - pmlmepriv->LinkDetectInfo.NumRxOkInPeriod = 0; - pmlmepriv->LinkDetectInfo.NumTxOkInPeriod = 0; - pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod = 0; - pmlmepriv->LinkDetectInfo.bBusyTraffic = bBusyTraffic; - pmlmepriv->LinkDetectInfo.bTxBusyTraffic = bTxBusyTraffic; - pmlmepriv->LinkDetectInfo.bRxBusyTraffic = bRxBusyTraffic; - pmlmepriv->LinkDetectInfo.bHigherBusyTraffic = bHigherBusyTraffic; - pmlmepriv->LinkDetectInfo.bHigherBusyRxTraffic = bHigherBusyRxTraffic; - pmlmepriv->LinkDetectInfo.bHigherBusyTxTraffic = bHigherBusyTxTraffic; + pmlmepriv->link_detect_info.num_rx_ok_in_period = 0; + pmlmepriv->link_detect_info.num_tx_ok_in_period = 0; + pmlmepriv->link_detect_info.num_rx_unicast_ok_in_period = 0; + pmlmepriv->link_detect_info.busy_traffic = busy_traffic; + pmlmepriv->link_detect_info.tx_busy_traffic = tx_busy_traffic; + pmlmepriv->link_detect_info.rx_busy_traffic = rx_busy_traffic; + pmlmepriv->link_detect_info.higher_busy_traffic = higher_busy_traffic; + pmlmepriv->link_detect_info.higher_busy_rx_traffic = higher_busy_rx_traffic; + pmlmepriv->link_detect_info.higher_busy_tx_traffic = higher_busy_tx_traffic; - return bEnterPS; + return should_enter_ps; } static void dynamic_chk_wk_hdl(struct adapter *padapter) @@ -1239,7 +1247,7 @@ static void dynamic_chk_wk_hdl(struct adapter *padapter) /* if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING|_FW_UNDER_SURVEY) ==false) */ { linked_status_chk(padapter); - traffic_status_watchdog(padapter, 0); + traffic_status_watchdog(padapter, false); } rtw_hal_dm_watchdog(padapter); @@ -1495,7 +1503,7 @@ static void rtw_chk_hi_queue_hdl(struct adapter *padapter) rtw_hal_get_hwreg(padapter, HW_VAR_CHK_HI_QUEUE_EMPTY, &empty); while (!empty && jiffies_to_msecs(jiffies - start) < g_wait_hiq_empty) { - msleep(100); + fsleep(100 * USEC_PER_MSEC); rtw_hal_get_hwreg(padapter, HW_VAR_CHK_HI_QUEUE_EMPTY, &empty); } @@ -1761,7 +1769,8 @@ u8 rtw_drvextra_cmd_hdl(struct adapter *padapter, unsigned char *pbuf) rtw_free_assoc_resources(padapter, 1); break; case C2H_WK_CID: - rtw_hal_set_hwreg_with_buf(padapter, HW_VAR_C2H_HANDLE, pdrvextra_cmd->pbuf, pdrvextra_cmd->size); + rtw_hal_set_hwreg_with_buf(padapter, HW_VAR_C2H_HANDLE, + pdrvextra_cmd->pbuf, pdrvextra_cmd->size); break; case DM_RA_MSK_WK_CID: rtw_dm_ra_mask_hdl(padapter, (struct sta_info *)pdrvextra_cmd->pbuf); diff --git a/drivers/staging/rtl8723bs/core/rtw_efuse.c b/drivers/staging/rtl8723bs/core/rtw_efuse.c index 98b15ca10074..099320e4eb50 100644 --- a/drivers/staging/rtl8723bs/core/rtw_efuse.c +++ b/drivers/staging/rtl8723bs/core/rtw_efuse.c @@ -8,24 +8,6 @@ #include #include - -/* Define global variables */ -u8 fakeEfuseBank; -u32 fakeEfuseUsedBytes; -u8 fakeEfuseContent[EFUSE_MAX_HW_SIZE] = {0}; -u8 fakeEfuseInitMap[EFUSE_MAX_MAP_LEN] = {0}; -u8 fakeEfuseModifiedMap[EFUSE_MAX_MAP_LEN] = {0}; - -u32 BTEfuseUsedBytes; -u8 BTEfuseContent[EFUSE_MAX_BT_BANK][EFUSE_MAX_HW_SIZE]; -u8 BTEfuseInitMap[EFUSE_BT_MAX_MAP_LEN] = {0}; -u8 BTEfuseModifiedMap[EFUSE_BT_MAX_MAP_LEN] = {0}; - -u32 fakeBTEfuseUsedBytes; -u8 fakeBTEfuseContent[EFUSE_MAX_BT_BANK][EFUSE_MAX_HW_SIZE]; -u8 fakeBTEfuseInitMap[EFUSE_BT_MAX_MAP_LEN] = {0}; -u8 fakeBTEfuseModifiedMap[EFUSE_BT_MAX_MAP_LEN] = {0}; - /* 11/16/2008 MH Add description. Get current efuse area enabled word!!. */ u8 Efuse_CalculateWordCnts(u8 word_en) @@ -69,7 +51,7 @@ u16 Address) u32 k = 0; u16 contentLen = 0; - Hal_GetEfuseDefinition(Adapter, EFUSE_WIFI, TYPE_EFUSE_REAL_CONTENT_LEN, (void *)&contentLen); + Hal_GetEfuseDefinition(Adapter, EFUSE_WIFI, TYPE_EFUSE_REAL_CONTENT_LEN, &contentLen); if (Address < contentLen) {/* E-fuse 512Byte */ /* Write E-fuse Register address bit0~7 */ @@ -163,7 +145,7 @@ static void Efuse_ReadAllMap(struct adapter *padapter, u8 efuseType, u8 *Efuse) Hal_EfusePowerSwitch(padapter, true); - Hal_GetEfuseDefinition(padapter, efuseType, TYPE_EFUSE_MAP_LEN, (void *)&mapLen); + Hal_GetEfuseDefinition(padapter, efuseType, TYPE_EFUSE_MAP_LEN, &mapLen); Hal_ReadEFuse(padapter, efuseType, 0, mapLen, Efuse); @@ -239,7 +221,7 @@ void EFUSE_ShadowMapUpdate(struct adapter *padapter, u8 efuseType) struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter); u16 mapLen = 0; - Hal_GetEfuseDefinition(padapter, efuseType, TYPE_EFUSE_MAP_LEN, (void *)&mapLen); + Hal_GetEfuseDefinition(padapter, efuseType, TYPE_EFUSE_MAP_LEN, &mapLen); if (pEEPROM->bautoload_fail_flag) memset(pEEPROM->efuse_eeprom_data, 0xFF, mapLen); diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index 3e2b5e6b07f9..72b7f731dd47 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -94,7 +94,7 @@ bool rtw_is_cckratesonly_included(u8 *rate) return true; } -int rtw_check_network_type(unsigned char *rate, int ratelen, int channel) +int rtw_check_network_type(unsigned char *rate, int channel) { if (channel > 14) return WIRELESS_INVALID; @@ -109,7 +109,7 @@ int rtw_check_network_type(unsigned char *rate, int ratelen, int channel) u8 *rtw_set_fixed_ie(unsigned char *pbuf, unsigned int len, unsigned char *source, unsigned int *frlen) { - memcpy((void *)pbuf, (void *)source, len); + memcpy(pbuf, source, len); *frlen = *frlen + len; return pbuf + len; } @@ -126,7 +126,7 @@ u8 *rtw_set_ie(u8 *pbuf, *(pbuf + 1) = (u8)len; if (len > 0) - memcpy((void *)(pbuf + 2), (void *)source, len); + memcpy(pbuf + 2, source, len); *frlen = *frlen + (len + 2); @@ -192,8 +192,8 @@ u8 *rtw_get_ie_ex(u8 *in_ie, uint in_len, u8 eid, u8 *oui, u8 oui_len, u8 *ie, u if (cnt + 2 + ie_len > in_len) break; - if (eid == in_ie[cnt] - && (!oui || (ie_len >= oui_len && !memcmp(&in_ie[cnt + 2], oui, oui_len)))) { + if (eid == in_ie[cnt] && + (!oui || (ie_len >= oui_len && !memcmp(&in_ie[cnt + 2], oui, oui_len)))) { target_ie = &in_ie[cnt]; if (ie) @@ -291,7 +291,7 @@ uint rtw_get_rateset_len(u8 *rateset) int rtw_generate_ie(struct registry_priv *pregistrypriv) { u8 wireless_mode; - int sz = 0, rateLen; + int sz = 0, rate_len; struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network; u8 *ie = pdev_network->ies; @@ -326,13 +326,13 @@ int rtw_generate_ie(struct registry_priv *pregistrypriv) rtw_set_supported_rate(pdev_network->supported_rates, wireless_mode); - rateLen = rtw_get_rateset_len(pdev_network->supported_rates); + rate_len = rtw_get_rateset_len(pdev_network->supported_rates); - if (rateLen > 8) { + if (rate_len > 8) { ie = rtw_set_ie(ie, WLAN_EID_SUPP_RATES, 8, pdev_network->supported_rates, &sz); - /* ie = rtw_set_ie(ie, WLAN_EID_EXT_SUPP_RATES, (rateLen - 8), (pdev_network->supported_rates + 8), &sz); */ + /* ie = rtw_set_ie(ie, WLAN_EID_EXT_SUPP_RATES, (rate_len - 8), (pdev_network->supported_rates + 8), &sz); */ } else { - ie = rtw_set_ie(ie, WLAN_EID_SUPP_RATES, rateLen, pdev_network->supported_rates, &sz); + ie = rtw_set_ie(ie, WLAN_EID_SUPP_RATES, rate_len, pdev_network->supported_rates, &sz); } /* DS parameter set */ @@ -342,8 +342,8 @@ int rtw_generate_ie(struct registry_priv *pregistrypriv) ie = rtw_set_ie(ie, WLAN_EID_IBSS_PARAMS, 2, (u8 *)&(pdev_network->configuration.atim_window), &sz); - if (rateLen > 8) - ie = rtw_set_ie(ie, WLAN_EID_EXT_SUPP_RATES, (rateLen - 8), (pdev_network->supported_rates + 8), &sz); + if (rate_len > 8) + ie = rtw_set_ie(ie, WLAN_EID_EXT_SUPP_RATES, (rate_len - 8), (pdev_network->supported_rates + 8), &sz); /* HT Cap. */ if ((pregistrypriv->wireless_mode & WIRELESS_11_24N) && @@ -455,10 +455,10 @@ int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwis return _FAIL; } - if ((*wpa_ie != WLAN_EID_VENDOR_SPECIFIC) || (*(wpa_ie+1) != (u8)(wpa_ie_len - 2)) || - (memcmp(wpa_ie+2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN))) { + if ((*wpa_ie != WLAN_EID_VENDOR_SPECIFIC) || + (*(wpa_ie + 1) != (u8)(wpa_ie_len - 2)) || + (memcmp(wpa_ie + 2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN))) return _FAIL; - } pos = wpa_ie; @@ -518,7 +518,7 @@ int rtw_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, int *pairwi return _FAIL; } - if ((*rsn_ie != WLAN_EID_RSN) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2))) + if ((*rsn_ie != WLAN_EID_RSN) || (*(rsn_ie + 1) != (u8)(rsn_ie_len - 2))) return _FAIL; pos = rsn_ie; @@ -585,19 +585,18 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_len, u8 *wapi_ie, u16 *wapi_len) while (cnt < in_len) { authmode = in_ie[cnt]; - /* if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY) */ - if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY && (!memcmp(&in_ie[cnt+6], wapi_oui1, 4) || - !memcmp(&in_ie[cnt+6], wapi_oui2, 4))) { + if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY && + (!memcmp(&in_ie[cnt + 6], wapi_oui1, 4) || + !memcmp(&in_ie[cnt + 6], wapi_oui2, 4))) { if (wapi_ie) - memcpy(wapi_ie, &in_ie[cnt], in_ie[cnt+1]+2); + memcpy(wapi_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); if (wapi_len) - *wapi_len = in_ie[cnt+1]+2; + *wapi_len = in_ie[cnt + 1] + 2; - cnt += in_ie[cnt+1]+2; /* get next */ - } else { - cnt += in_ie[cnt+1]+2; /* get next */ } + + cnt += in_ie[cnt + 1] + 2; /* get next */ } if (wapi_len) @@ -619,23 +618,20 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie while (cnt < in_len) { authmode = in_ie[cnt]; - if ((authmode == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt+2], &wpa_oui[0], 4))) { + if ((authmode == WLAN_EID_VENDOR_SPECIFIC) && + (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) { if (wpa_ie) - memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt+1]+2); + memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); *wpa_len = in_ie[cnt + 1] + 2; - cnt += in_ie[cnt + 1] + 2; /* get next */ - } else { - if (authmode == WLAN_EID_RSN) { - if (rsn_ie) - memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); + } else if (authmode == WLAN_EID_RSN) { + if (rsn_ie) + memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); - *rsn_len = in_ie[cnt+1]+2; - cnt += in_ie[cnt+1]+2; /* get next */ - } else { - cnt += in_ie[cnt+1]+2; /* get next */ - } + *rsn_len = in_ie[cnt + 1] + 2; } + + cnt += in_ie[cnt + 1] + 2; /* get next */ } } @@ -665,20 +661,20 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen) while (cnt < in_len) { eid = in_ie[cnt]; - if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt+2], wps_oui, 4))) { + if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) { wpsie_ptr = &in_ie[cnt]; if (wps_ie) - memcpy(wps_ie, &in_ie[cnt], in_ie[cnt+1]+2); + memcpy(wps_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); if (wps_ielen) - *wps_ielen = in_ie[cnt+1]+2; + *wps_ielen = in_ie[cnt + 1] + 2; - cnt += in_ie[cnt+1]+2; + cnt += in_ie[cnt + 1] + 2; break; } - cnt += in_ie[cnt+1]+2; /* goto next */ + cnt += in_ie[cnt + 1] + 2; /* goto next */ } return wpsie_ptr; @@ -756,12 +752,12 @@ u8 *rtw_get_wps_attr_content(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 if (attr_ptr && attr_len) { if (buf_content) - memcpy(buf_content, attr_ptr+4, attr_len-4); + memcpy(buf_content, attr_ptr + 4, attr_len - 4); if (len_content) - *len_content = attr_len-4; + *len_content = attr_len - 4; - return attr_ptr+4; + return attr_ptr + 4; } return NULL; @@ -851,9 +847,9 @@ static int rtw_ieee802_11_parse_vendor_specific(u8 *pos, uint elen, * @show_errors: Whether to show parsing errors in debug log * Returns: Parsing result */ -enum ParseRes rtw_ieee802_11_parse_elems(u8 *start, uint len, - struct rtw_ieee802_11_elems *elems, - int show_errors) +enum parse_result rtw_ieee802_11_parse_elems(u8 *start, uint len, + struct rtw_ieee802_11_elems *elems, + int show_errors) { uint left = len; u8 *pos = start; @@ -869,7 +865,7 @@ enum ParseRes rtw_ieee802_11_parse_elems(u8 *start, uint len, left -= 2; if (elen > left) - return ParseFailed; + return PARSE_FAILED; switch (id) { case WLAN_EID_SSID: @@ -972,9 +968,9 @@ enum ParseRes rtw_ieee802_11_parse_elems(u8 *start, uint len, } if (left) - return ParseFailed; + return PARSE_FAILED; - return unknown ? ParseUnknown : ParseOK; + return unknown ? PARSE_UNKNOWN : PARSE_OK; } void rtw_macaddr_cfg(struct device *dev, u8 *mac_addr) @@ -1012,20 +1008,25 @@ static int rtw_get_cipher_info(struct wlan_network *pnetwork) int group_cipher = 0, pairwise_cipher = 0, is8021x = 0; int ret = _FAIL; - pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12); + pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12], + &wpa_ielen, + pnetwork->network.ie_length - 12); if (pbuf && (wpa_ielen > 0)) { - if (rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) { + if (rtw_parse_wpa_ie(pbuf, wpa_ielen + 2, &group_cipher, + &pairwise_cipher, &is8021x) == _SUCCESS) { pnetwork->bcn_info.pairwise_cipher = pairwise_cipher; pnetwork->bcn_info.group_cipher = group_cipher; pnetwork->bcn_info.is_8021x = is8021x; ret = _SUCCESS; } } else { - pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12); + pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen, + pnetwork->network.ie_length - 12); if (pbuf && (wpa_ielen > 0)) { - if (rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) { + if (rtw_parse_wpa2_ie(pbuf, wpa_ielen + 2, &group_cipher, + &pairwise_cipher, &is8021x) == _SUCCESS) { pnetwork->bcn_info.pairwise_cipher = pairwise_cipher; pnetwork->bcn_info.group_cipher = group_cipher; pnetwork->bcn_info.is_8021x = is8021x; @@ -1094,21 +1095,21 @@ u16 rtw_mcs_rate(u8 bw_40MHz, u8 short_GI, unsigned char *MCS_rate) u16 max_rate = 0; if (MCS_rate[0] & BIT(7)) - max_rate = (bw_40MHz) ? ((short_GI)?1500:1350):((short_GI)?722:650); + max_rate = (bw_40MHz) ? ((short_GI) ? 1500 : 1350) : ((short_GI) ? 722 : 650); else if (MCS_rate[0] & BIT(6)) - max_rate = (bw_40MHz) ? ((short_GI)?1350:1215):((short_GI)?650:585); + max_rate = (bw_40MHz) ? ((short_GI) ? 1350 : 1215) : ((short_GI) ? 650 : 585); else if (MCS_rate[0] & BIT(5)) - max_rate = (bw_40MHz) ? ((short_GI)?1200:1080):((short_GI)?578:520); + max_rate = (bw_40MHz) ? ((short_GI) ? 1200 : 1080) : ((short_GI) ? 578 : 520); else if (MCS_rate[0] & BIT(4)) - max_rate = (bw_40MHz) ? ((short_GI)?900:810):((short_GI)?433:390); + max_rate = (bw_40MHz) ? ((short_GI) ? 900 : 810) : ((short_GI) ? 433 : 390); else if (MCS_rate[0] & BIT(3)) - max_rate = (bw_40MHz) ? ((short_GI)?600:540):((short_GI)?289:260); + max_rate = (bw_40MHz) ? ((short_GI) ? 600 : 540) : ((short_GI) ? 289 : 260); else if (MCS_rate[0] & BIT(2)) - max_rate = (bw_40MHz) ? ((short_GI)?450:405):((short_GI)?217:195); + max_rate = (bw_40MHz) ? ((short_GI) ? 450 : 405) : ((short_GI) ? 217 : 195); else if (MCS_rate[0] & BIT(1)) - max_rate = (bw_40MHz) ? ((short_GI)?300:270):((short_GI)?144:130); + max_rate = (bw_40MHz) ? ((short_GI) ? 300 : 270) : ((short_GI) ? 144 : 130); else if (MCS_rate[0] & BIT(0)) - max_rate = (bw_40MHz) ? ((short_GI)?150:135):((short_GI)?72:65); + max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65); return max_rate; } @@ -1122,11 +1123,9 @@ int rtw_action_frame_parse(const u8 *frame, u32 frame_len, u8 *category, u8 *act fc = le16_to_cpu(((struct ieee80211_hdr_3addr *)frame)->frame_control); - if ((fc & (IEEE80211_FCTL_FTYPE|IEEE80211_FCTL_STYPE)) - != (IEEE80211_FTYPE_MGMT|IEEE80211_STYPE_ACTION) - ) { + if ((fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) != + (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION)) return false; - } c = frame_body[0]; diff --git a/drivers/staging/rtl8723bs/core/rtw_io.c b/drivers/staging/rtl8723bs/core/rtw_io.c index 965c3cfea103..53ba99e1de00 100644 --- a/drivers/staging/rtl8723bs/core/rtw_io.c +++ b/drivers/staging/rtl8723bs/core/rtw_io.c @@ -75,6 +75,7 @@ int rtw_write8(struct adapter *adapter, u32 addr, u8 val) return RTW_STATUS_CODE(ret); } + int rtw_write16(struct adapter *adapter, u32 addr, u16 val) { /* struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue; */ @@ -88,6 +89,7 @@ int rtw_write16(struct adapter *adapter, u32 addr, u16 val) ret = _write16(pintfhdl, addr, val); return RTW_STATUS_CODE(ret); } + int rtw_write32(struct adapter *adapter, u32 addr, u32 val) { /* struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue; */ @@ -114,7 +116,9 @@ u32 rtw_write_port(struct adapter *adapter, u32 addr, u32 cnt, u8 *pmem) return _write_port(pintfhdl, addr, cnt, pmem); } -int rtw_init_io_priv(struct adapter *padapter, void (*set_intf_ops)(struct adapter *padapter, struct _io_ops *pops)) +int rtw_init_io_priv(struct adapter *padapter, + void (*set_intf_ops)(struct adapter *padapter, + struct _io_ops *pops)) { struct io_priv *piopriv = &padapter->iopriv; struct intf_hdl *pintf = &piopriv->intf; @@ -132,7 +136,8 @@ int rtw_init_io_priv(struct adapter *padapter, void (*set_intf_ops)(struct adapt } /* - * Increase and check if the continual_io_error of this @param dvobjprive is larger than MAX_CONTINUAL_IO_ERR + * Increase and check if the continual_io_error of this @param dvobjprive + * is larger than MAX_CONTINUAL_IO_ERR * @return true: * @return false: */ diff --git a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c index 587a87fbffeb..c70541f95a73 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c +++ b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c @@ -11,12 +11,10 @@ u8 rtw_validate_bssid(u8 *bssid) { u8 ret = true; - if (is_zero_mac_addr(bssid) - || is_broadcast_mac_addr(bssid) - || is_multicast_mac_addr(bssid) - ) { + if (is_zero_ether_addr(bssid) || + is_broadcast_ether_addr(bssid) || + is_multicast_ether_addr(bssid)) ret = false; - } return ret; } @@ -61,9 +59,7 @@ u8 rtw_do_join(struct adapter *padapter) /* when set_ssid/set_bssid for rtw_do_join(), but scanning queue is empty */ /* we try to issue sitesurvey firstly */ - if (pmlmepriv->LinkDetectInfo.bBusyTraffic == false - || rtw_to_roam(padapter) > 0 - ) { + if (!pmlmepriv->link_detect_info.busy_traffic || rtw_to_roam(padapter) > 0) { /* submit site_survey_cmd */ ret = rtw_sitesurvey_cmd(padapter, &pmlmepriv->assoc_ssid, 1, NULL, 0); if (ret != _SUCCESS) @@ -113,9 +109,8 @@ u8 rtw_do_join(struct adapter *padapter) /* when set_ssid/set_bssid for rtw_do_join(), but there are no desired bss in scanning queue */ /* we try to issue sitesurvey firstly */ - if (pmlmepriv->LinkDetectInfo.bBusyTraffic == false - || rtw_to_roam(padapter) > 0 - ) { + if (!pmlmepriv->link_detect_info.busy_traffic || + rtw_to_roam(padapter) > 0) { ret = rtw_sitesurvey_cmd(padapter, &pmlmepriv->assoc_ssid, 1, NULL, 0); if (ret != _SUCCESS) pmlmepriv->to_join = false; @@ -379,8 +374,8 @@ u8 rtw_set_802_11_bssid_list_scan(struct adapter *padapter, struct ndis_802_11_s goto exit; } - if ((check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING) == true) || - (pmlmepriv->LinkDetectInfo.bBusyTraffic == true)) { + if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | _FW_UNDER_LINKING) || + pmlmepriv->link_detect_info.busy_traffic) { /* Scan or linking is in progress, do nothing. */ res = true; diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index 1ef48bf6581c..ddfc56f0253d 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -262,17 +262,13 @@ void rtw_free_network_queue(struct adapter *padapter, u8 isfreeall) spin_unlock_bh(&scanned_queue->lock); } -signed int rtw_if_up(struct adapter *padapter) +bool rtw_if_up(struct adapter *padapter) { - signed int res; - if (padapter->bDriverStopped || padapter->bSurpriseRemoved || !check_fwstate(&padapter->mlmepriv, _FW_LINKED)) - res = false; - else - res = true; + return false; - return res; + return true; } void rtw_generate_random_ibss(u8 *pibss) @@ -330,21 +326,18 @@ struct wlan_network *rtw_find_network(struct __queue *scanned_queue, u8 *addr) return pnetwork; } -int rtw_is_same_ibss(struct adapter *adapter, struct wlan_network *pnetwork) +bool rtw_is_same_ibss(struct adapter *adapter, struct wlan_network *pnetwork) { - int ret = true; struct security_priv *psecuritypriv = &adapter->securitypriv; if ((psecuritypriv->dot11PrivacyAlgrthm != _NO_PRIVACY_) && (pnetwork->network.privacy == 0)) - ret = false; + return false; else if ((psecuritypriv->dot11PrivacyAlgrthm == _NO_PRIVACY_) && (pnetwork->network.privacy == 1)) - ret = false; - else - ret = true; + return false; - return ret; + return true; } inline int is_same_ess(struct wlan_bssid_ex *a, struct wlan_bssid_ex *b) @@ -595,15 +588,14 @@ void rtw_add_network(struct adapter *adapter, struct wlan_bssid_ex *pnetwork) * (4) HT * (5) others */ -int rtw_is_desired_network(struct adapter *adapter, struct wlan_network *pnetwork); -int rtw_is_desired_network(struct adapter *adapter, struct wlan_network *pnetwork) +static bool rtw_is_desired_network(struct adapter *adapter, struct wlan_network *pnetwork) { struct security_priv *psecuritypriv = &adapter->securitypriv; struct mlme_priv *pmlmepriv = &adapter->mlmepriv; u32 desired_encmode; u32 privacy; uint wps_ielen; - int bselected = true; + bool bselected = true; desired_encmode = psecuritypriv->ndisencryptstatus; privacy = pnetwork->network.privacy; @@ -877,11 +869,10 @@ void rtw_indicate_connect(struct adapter *padapter) set_fwstate(pmlmepriv, _FW_LINKED); if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) || - check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) { + check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) rtw_cfg80211_ibss_indicate_connect(padapter); - } else { + else rtw_cfg80211_indicate_connect(padapter); - } netif_carrier_on(padapter->pnetdev); @@ -1154,10 +1145,8 @@ void rtw_reset_securitypriv(struct adapter *adapter) /* if join_res > 0, for (fw_state ==WIFI_ADHOC_STATE), we only check if "ptarget_wlan" exist. */ /* if join_res > 0, update "cur_network->network" from "pnetwork->network" if (ptarget_wlan != NULL). */ /* */ -/* define REJOIN */ void rtw_joinbss_event_prehandle(struct adapter *adapter, u8 *pbuf) { - static u8 __maybe_unused retry; struct sta_info *ptarget_sta = NULL, *pcur_sta = NULL; struct sta_priv *pstapriv = &adapter->stapriv; struct mlme_priv *pmlmepriv = &adapter->mlmepriv; @@ -1174,111 +1163,94 @@ void rtw_joinbss_event_prehandle(struct adapter *adapter, u8 *pbuf) spin_lock_bh(&pmlmepriv->lock); - pmlmepriv->LinkDetectInfo.TrafficTransitionCount = 0; - pmlmepriv->LinkDetectInfo.LowPowerTransitionCount = 0; + pmlmepriv->link_detect_info.traffic_transition_count = 0; + pmlmepriv->link_detect_info.low_power_transition_count = 0; - if (pnetwork->join_res > 0) { - spin_lock_bh(&pmlmepriv->scanned_queue.lock); - retry = 0; - if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) { - /* s1. find ptarget_wlan */ - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - if (the_same_macaddr) { - ptarget_wlan = rtw_find_network(&pmlmepriv->scanned_queue, cur_network->network.mac_address); - } else { - pcur_wlan = rtw_find_network(&pmlmepriv->scanned_queue, cur_network->network.mac_address); - if (pcur_wlan) - pcur_wlan->fixed = false; - - pcur_sta = rtw_get_stainfo(pstapriv, cur_network->network.mac_address); - if (pcur_sta) - rtw_free_stainfo(adapter, pcur_sta); - - ptarget_wlan = rtw_find_network(&pmlmepriv->scanned_queue, pnetwork->network.mac_address); - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { - if (ptarget_wlan) - ptarget_wlan->fixed = true; - } - } - - } else { - ptarget_wlan = _rtw_find_same_network(&pmlmepriv->scanned_queue, pnetwork); - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { - if (ptarget_wlan) - ptarget_wlan->fixed = true; - } - } - - /* s2. update cur_network */ - if (ptarget_wlan) { - rtw_joinbss_update_network(adapter, ptarget_wlan, pnetwork); - } else { - netdev_dbg(adapter->pnetdev, - "Can't find ptarget_wlan when joinbss_event callback\n"); - spin_unlock_bh(&pmlmepriv->scanned_queue.lock); - goto ignore_joinbss_callback; - } - - /* s3. find ptarget_sta & update ptarget_sta after update cur_network only for station mode */ - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { - ptarget_sta = rtw_joinbss_update_stainfo(adapter, pnetwork); - if (!ptarget_sta) { - spin_unlock_bh(&pmlmepriv->scanned_queue.lock); - goto ignore_joinbss_callback; - } - } - - /* s4. indicate connect */ - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { - pmlmepriv->cur_network_scanned = ptarget_wlan; - rtw_indicate_connect(adapter); - } - - spin_unlock_bh(&pmlmepriv->scanned_queue.lock); - - spin_unlock_bh(&pmlmepriv->lock); - /* s5. Cancel assoc_timer */ - timer_delete_sync(&pmlmepriv->assoc_timer); - spin_lock_bh(&pmlmepriv->lock); - } else { - spin_unlock_bh(&pmlmepriv->scanned_queue.lock); - } - } else if (pnetwork->join_res == -4) { + if (pnetwork->join_res == -4) { rtw_reset_securitypriv(adapter); _set_timer(&pmlmepriv->assoc_timer, 1); if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); - } else {/* if join_res < 0 (join fails), then try again */ - - #ifdef REJOIN - res = _FAIL; - if (retry < 2) - res = rtw_select_and_join_from_scanned_queue(pmlmepriv); - - if (res == _SUCCESS) { - /* extend time of assoc_timer */ - _set_timer(&pmlmepriv->assoc_timer, MAX_JOIN_TIMEOUT); - retry++; - } else if (res == 2) {/* there is no need to wait for join */ - _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); - rtw_indicate_connect(adapter); - } else { - #endif - - _set_timer(&pmlmepriv->assoc_timer, 1); - _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); - - #ifdef REJOIN - retry = 0; - } - #endif + spin_unlock_bh(&pmlmepriv->lock); + return; } -ignore_joinbss_callback: + if (pnetwork->join_res <= 0) { /* if join_res < 0 (join fails), then try again */ + _set_timer(&pmlmepriv->assoc_timer, 1); + _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); + spin_unlock_bh(&pmlmepriv->lock); + return; + } + + spin_lock_bh(&pmlmepriv->scanned_queue.lock); + + if (!check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) { + spin_unlock_bh(&pmlmepriv->scanned_queue.lock); + spin_unlock_bh(&pmlmepriv->lock); + return; + } + + /* s1. find ptarget_wlan */ + if (check_fwstate(pmlmepriv, _FW_LINKED)) { + if (the_same_macaddr) { + ptarget_wlan = rtw_find_network(&pmlmepriv->scanned_queue, cur_network->network.mac_address); + } else { + pcur_wlan = rtw_find_network(&pmlmepriv->scanned_queue, cur_network->network.mac_address); + if (pcur_wlan) + pcur_wlan->fixed = false; + + pcur_sta = rtw_get_stainfo(pstapriv, cur_network->network.mac_address); + if (pcur_sta) + rtw_free_stainfo(adapter, pcur_sta); + + ptarget_wlan = rtw_find_network(&pmlmepriv->scanned_queue, pnetwork->network.mac_address); + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { + if (ptarget_wlan) + ptarget_wlan->fixed = true; + } + } + } else { + ptarget_wlan = _rtw_find_same_network(&pmlmepriv->scanned_queue, pnetwork); + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { + if (ptarget_wlan) + ptarget_wlan->fixed = true; + } + } + + /* s2. update cur_network */ + if (!ptarget_wlan) { + netdev_dbg(adapter->pnetdev, + "Can't find ptarget_wlan when joinbss_event callback\n"); + spin_unlock_bh(&pmlmepriv->scanned_queue.lock); + spin_unlock_bh(&pmlmepriv->lock); + return; + } + + rtw_joinbss_update_network(adapter, ptarget_wlan, pnetwork); + + /* s3. find ptarget_sta & update ptarget_sta after update cur_network only for station mode */ + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { + ptarget_sta = rtw_joinbss_update_stainfo(adapter, pnetwork); + if (!ptarget_sta) { + spin_unlock_bh(&pmlmepriv->scanned_queue.lock); + spin_unlock_bh(&pmlmepriv->lock); + return; + } + } + + /* s4. indicate connect */ + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) { + pmlmepriv->cur_network_scanned = ptarget_wlan; + rtw_indicate_connect(adapter); + } + + spin_unlock_bh(&pmlmepriv->scanned_queue.lock); spin_unlock_bh(&pmlmepriv->lock); + /* s5. Cancel assoc_timer */ + timer_delete_sync(&pmlmepriv->assoc_timer); } void rtw_joinbss_event_callback(struct adapter *adapter, u8 *pbuf) @@ -1615,7 +1587,7 @@ static void rtw_auto_scan_handler(struct adapter *padapter) if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | _FW_UNDER_LINKING)) goto exit; - if (pmlmepriv->LinkDetectInfo.bBusyTraffic) + if (pmlmepriv->link_detect_info.busy_traffic) goto exit; } @@ -1643,12 +1615,12 @@ void rtw_dynamic_check_timer_handler(struct adapter *adapter) if ((adapter_to_pwrctl(adapter)->fw_current_in_ps_mode) && !(hal_btcoex_IsBtControlLps(adapter)) ) { - u8 bEnterPS; + bool should_enter_ps; linked_status_chk(adapter); - bEnterPS = traffic_status_watchdog(adapter, 1); - if (bEnterPS) { + should_enter_ps = traffic_status_watchdog(adapter, true); + if (should_enter_ps) { /* rtw_lps_ctrl_wk_cmd(adapter, LPS_CTRL_ENTER, 1); */ rtw_hal_dm_watchdog_in_lps(adapter); } else { @@ -1703,7 +1675,7 @@ static int rtw_check_roaming_candidate(struct mlme_priv *mlme goto exit; /* got specific addr to roam */ - if (!is_zero_mac_addr(mlme->roam_tgt_addr)) { + if (!is_zero_ether_addr(mlme->roam_tgt_addr)) { if (!memcmp(mlme->roam_tgt_addr, competitor->network.mac_address, ETH_ALEN)) goto update; else @@ -2507,8 +2479,7 @@ void rtw_issue_addbareq_cmd(struct adapter *padapter, struct xmit_frame *pxmitfr struct pkt_attrib *pattrib = &pxmitframe->attrib; s32 bmcst = is_multicast_ether_addr(pattrib->ra); - /* if (bmcst || (padapter->mlmepriv.LinkDetectInfo.bTxBusyTraffic == false)) */ - if (bmcst || (padapter->mlmepriv.LinkDetectInfo.NumTxOkInPeriod < 100)) + if (bmcst || (padapter->mlmepriv.link_detect_info.num_tx_ok_in_period < 100)) return; priority = pattrib->priority; @@ -2595,7 +2566,7 @@ void _rtw_roaming(struct adapter *padapter, struct wlan_network *tgt_network) } } -signed int rtw_linked_check(struct adapter *padapter) +bool rtw_linked_check(struct adapter *padapter) { if (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) || check_fwstate(&padapter->mlmepriv, WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE)) { diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index b1f20aa81efb..5f00fe282d1b 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -8,6 +8,7 @@ #include #include #include +#include #include static struct mlme_handler mlme_sta_tbl[] = { @@ -183,12 +184,12 @@ int rtw_ch_set_search_ch(struct rt_channel_info *ch_set, const u32 ch) /* Following are the initialization functions for WiFi MLME */ -int init_hw_mlme_ext(struct adapter *padapter) +void init_hw_mlme_ext(struct adapter *padapter) { - struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; + struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; - set_channel_bwmode(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode); - return _SUCCESS; + set_channel_bwmode(padapter, pmlmeext->cur_channel, + pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode); } void init_mlme_default_rate_set(struct adapter *padapter) @@ -525,7 +526,7 @@ unsigned int OnProbeReq(struct adapter *padapter, union recv_frame *precv_frame) if (is_valid_p2p_probereq) goto _issue_probersp; - if ((ielen != 0 && false == !memcmp((void *)(p+2), (void *)cur->ssid.ssid, cur->ssid.ssid_length)) + if ((ielen != 0 && false == !memcmp((p+2), cur->ssid.ssid, cur->ssid.ssid_length)) || (ielen == 0 && pmlmeinfo->hidden_ssid_mode) ) return _SUCCESS; @@ -791,7 +792,7 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) } else { /* shared system or auto authentication */ if (seq == 1) { /* prepare for the challenging txt... */ - memset((void *)pstat->chg_txt, 78, 128); + memset(pstat->chg_txt, 78, 128); pstat->state &= ~WIFI_FW_AUTH_NULL; pstat->state |= WIFI_FW_AUTH_STATE; @@ -806,7 +807,7 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame) goto auth_fail; } - if (!memcmp((void *)(p + 2), pstat->chg_txt, 128)) { + if (!memcmp((p + 2), pstat->chg_txt, 128)) { pstat->state &= (~WIFI_FW_AUTH_STATE); pstat->state |= WIFI_FW_AUTH_SUCCESS; /* challenging txt is correct... */ @@ -893,7 +894,7 @@ unsigned int OnAuthClient(struct adapter *padapter, union recv_frame *precv_fram if (!p) goto authclnt_fail; - memcpy((void *)(pmlmeinfo->chg_txt), (void *)(p + 2), len); + memcpy(pmlmeinfo->chg_txt, p + 2, len); pmlmeinfo->auth_seq = 3; issue_auth(padapter, NULL, 0); set_link_timer(pmlmeext, REAUTH_TO); @@ -933,9 +934,10 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) struct sta_info *pstat; unsigned char *p, *pos, *wpa_ie; unsigned char WMM_IE[] = {0x00, 0x50, 0xf2, 0x02, 0x00, 0x01}; - int i, ie_len, wpa_ie_len, left; + int i, ie_len, left; + u8 wpa_ie_len; unsigned char supportRate[16]; - int supportRateNum; + int support_rate_num; unsigned short status = WLAN_STATUS_SUCCESS; unsigned short frame_type, ie_offset = 0; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; @@ -990,7 +992,7 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) pstat->capability = capab_info; /* now parse all ieee802_11 ie to point to elems */ - if (rtw_ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed || + if (rtw_ieee802_11_parse_elems(pos, left, &elems, 1) == PARSE_FAILED || !elems.ssid) { status = WLAN_STATUS_CHALLENGE_FAIL; goto OnAssocReqFail; @@ -1007,7 +1009,7 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) goto OnAssocReqFail; } else { /* check if ssid match */ - if (memcmp((void *)(p+2), cur->ssid.ssid, cur->ssid.ssid_length)) + if (memcmp(p+2, cur->ssid.ssid, cur->ssid.ssid_length)) status = WLAN_STATUS_CHALLENGE_FAIL; if (ie_len != cur->ssid.ssid_length) @@ -1022,7 +1024,7 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) if (!p) { /* use our own rate set as statoin used */ /* memcpy(supportRate, AP_BSSRATE, AP_BSSRATE_LEN); */ - /* supportRateNum = AP_BSSRATE_LEN; */ + /* support_rate_num = AP_BSSRATE_LEN; */ status = WLAN_STATUS_CHALLENGE_FAIL; goto OnAssocReqFail; @@ -1031,25 +1033,25 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) ie_len = sizeof(supportRate); memcpy(supportRate, p+2, ie_len); - supportRateNum = ie_len; + support_rate_num = ie_len; p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + ie_offset, WLAN_EID_EXT_SUPP_RATES, &ie_len, pkt_len - WLAN_HDR_A3_LEN - ie_offset); if (p) { - if (supportRateNum + ie_len <= sizeof(supportRate)) { - memcpy(supportRate+supportRateNum, p+2, ie_len); - supportRateNum += ie_len; + if (support_rate_num + ie_len <= sizeof(supportRate)) { + memcpy(supportRate + support_rate_num, p + 2, ie_len); + support_rate_num += ie_len; } } } /* todo: mask supportRate between AP & STA -> move to update raid */ - /* get_matched_rate(pmlmeext, supportRate, &supportRateNum, 0); */ + /* get_matched_rate(pmlmeext, supportRate, &support_rate_num, 0); */ /* update station supportRate */ - pstat->bssratelen = supportRateNum; - memcpy(pstat->bssrateset, supportRate, supportRateNum); + pstat->bssratelen = support_rate_num; + memcpy(pstat->bssrateset, supportRate, support_rate_num); update_basic_rate_table_soft_ap(pstat->bssrateset, pstat->bssratelen); /* check RSN/WPA/WPS */ @@ -1154,7 +1156,7 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) pstat->flags |= WLAN_STA_WPS; copy_len = 0; } else { - copy_len = ((wpa_ie_len+2) > sizeof(pstat->wpa_ie)) ? (sizeof(pstat->wpa_ie)):(wpa_ie_len+2); + copy_len = min(sizeof(pstat->wpa_ie), wpa_ie_len + 2u); } @@ -1337,7 +1339,7 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) asoc_class2_error: - issue_deauth(padapter, (void *)GetAddr2Ptr(pframe), status); + issue_deauth(padapter, GetAddr2Ptr(pframe), status); return _FAIL; @@ -1511,7 +1513,7 @@ unsigned int OnDeAuth(struct adapter *padapter, union recv_frame *precv_frame) if (ignore_received_deauth == 0) receive_disconnect(padapter, GetAddr3Ptr(pframe), reason); - pmlmepriv->LinkDetectInfo.bBusyTraffic = false; + pmlmepriv->link_detect_info.busy_traffic = false; return _SUCCESS; } @@ -1563,7 +1565,7 @@ unsigned int OnDisassoc(struct adapter *padapter, union recv_frame *precv_frame) receive_disconnect(padapter, GetAddr3Ptr(pframe), reason); - pmlmepriv->LinkDetectInfo.bBusyTraffic = false; + pmlmepriv->link_detect_info.busy_traffic = false; return _SUCCESS; } @@ -2715,9 +2717,9 @@ void issue_asocrsp(struct adapter *padapter, unsigned short status, struct sta_i fctrl = &(pwlanhdr->frame_control); *(fctrl) = 0; - memcpy((void *)GetAddr1Ptr(pwlanhdr), pstat->hwaddr, ETH_ALEN); - memcpy((void *)GetAddr2Ptr(pwlanhdr), myid(&(padapter->eeprompriv)), ETH_ALEN); - memcpy((void *)GetAddr3Ptr(pwlanhdr), get_my_bssid(&(pmlmeinfo->network)), ETH_ALEN); + memcpy(GetAddr1Ptr(pwlanhdr), pstat->hwaddr, ETH_ALEN); + memcpy(GetAddr2Ptr(pwlanhdr), myid(&(padapter->eeprompriv)), ETH_ALEN); + memcpy(GetAddr3Ptr(pwlanhdr), get_my_bssid(&(pmlmeinfo->network)), ETH_ALEN); SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); @@ -4756,9 +4758,8 @@ static void rtw_mlmeext_disconnect(struct adapter *padapter) timer_delete_sync(&pmlmeext->link_timer); - /* pmlmepriv->LinkDetectInfo.TrafficBusyState = false; */ - pmlmepriv->LinkDetectInfo.TrafficTransitionCount = 0; - pmlmepriv->LinkDetectInfo.LowPowerTransitionCount = 0; + pmlmepriv->link_detect_info.traffic_transition_count = 0; + pmlmepriv->link_detect_info.low_power_transition_count = 0; } @@ -4885,7 +4886,7 @@ void mlmeext_sta_add_event_callback(struct adapter *padapter, struct sta_info *p rtw_hal_update_sta_rate_mask(padapter, psta); /* ToDo: HT for Ad-hoc */ - psta->wireless_mode = rtw_check_network_type(psta->bssrateset, psta->bssratelen, pmlmeext->cur_channel); + psta->wireless_mode = rtw_check_network_type(psta->bssrateset, pmlmeext->cur_channel); psta->raid = networktype_to_raid_ex(padapter, psta); /* rate radaptive */ diff --git a/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c b/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c index 1c9e02732687..f074ea6e0f38 100644 --- a/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c +++ b/drivers/staging/rtl8723bs/core/rtw_pwrctrl.c @@ -29,14 +29,12 @@ void _ips_enter(struct adapter *padapter) pwrpriv->rf_pwrstate = rf_off; } pwrpriv->bips_processing = false; - } void ips_enter(struct adapter *padapter) { struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(padapter); - hal_btcoex_IpsNotify(padapter, pwrpriv->ips_mode_req); mutex_lock(&pwrpriv->lock); @@ -84,7 +82,7 @@ int ips_leave(struct adapter *padapter) static bool rtw_pwr_unassociated_idle(struct adapter *adapter) { struct adapter *buddy = adapter->pbuddy_adapter; - struct mlme_priv *pmlmepriv = &(adapter->mlmepriv); + struct mlme_priv *pmlmepriv = &adapter->mlmepriv; struct xmit_priv *pxmit_priv = &adapter->xmitpriv; bool ret = false; @@ -95,27 +93,27 @@ static bool rtw_pwr_unassociated_idle(struct adapter *adapter) if (time_before(jiffies, adapter_to_pwrctl(adapter)->ips_deny_time)) goto exit; - if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE|WIFI_SITE_MONITOR) - || check_fwstate(pmlmepriv, WIFI_UNDER_LINKING|WIFI_UNDER_WPS) - || check_fwstate(pmlmepriv, WIFI_AP_STATE) - || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE|WIFI_ADHOC_STATE) + if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE | WIFI_SITE_MONITOR) || + check_fwstate(pmlmepriv, WIFI_UNDER_LINKING | WIFI_UNDER_WPS) || + check_fwstate(pmlmepriv, WIFI_AP_STATE) || + check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE | WIFI_ADHOC_STATE) ) goto exit; /* consider buddy, if exist */ if (buddy) { - struct mlme_priv *b_pmlmepriv = &(buddy->mlmepriv); + struct mlme_priv *b_pmlmepriv = &buddy->mlmepriv; - if (check_fwstate(b_pmlmepriv, WIFI_ASOC_STATE|WIFI_SITE_MONITOR) - || check_fwstate(b_pmlmepriv, WIFI_UNDER_LINKING|WIFI_UNDER_WPS) - || check_fwstate(b_pmlmepriv, WIFI_AP_STATE) - || check_fwstate(b_pmlmepriv, WIFI_ADHOC_MASTER_STATE|WIFI_ADHOC_STATE) + if (check_fwstate(b_pmlmepriv, WIFI_ASOC_STATE | WIFI_SITE_MONITOR) || + check_fwstate(b_pmlmepriv, WIFI_UNDER_LINKING | WIFI_UNDER_WPS) || + check_fwstate(b_pmlmepriv, WIFI_AP_STATE) || + check_fwstate(b_pmlmepriv, WIFI_ADHOC_MASTER_STATE | WIFI_ADHOC_STATE) ) goto exit; } if (pxmit_priv->free_xmitbuf_cnt != NR_XMITBUFF || - pxmit_priv->free_xmit_extbuf_cnt != NR_XMIT_EXTBUFF) { + pxmit_priv->free_xmit_extbuf_cnt != NR_XMIT_EXTBUFF) { netdev_dbg(adapter->pnetdev, "There are some pkts to transmit\n"); netdev_dbg(adapter->pnetdev, @@ -131,7 +129,6 @@ exit: return ret; } - /* * ATTENTION: *rtw_ps_processor() doesn't handle LPS. @@ -158,7 +155,7 @@ void rtw_ps_processor(struct adapter *padapter) if (!rtw_pwr_unassociated_idle(padapter)) goto exit; - if ((pwrpriv->rf_pwrstate == rf_on) && ((pwrpriv->pwr_state_check_cnts%4) == 0)) { + if ((pwrpriv->rf_pwrstate == rf_on) && ((pwrpriv->pwr_state_check_cnts % 4) == 0)) { pwrpriv->change_rfpwrstate = rf_off; { ips_enter(padapter); @@ -184,8 +181,6 @@ void traffic_check_for_leave_lps(struct adapter *padapter, u8 tx, u32 tx_packets u8 bLeaveLPS = false; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - - if (tx) { /* from tx */ xmit_cnt += tx_packets; @@ -194,9 +189,9 @@ void traffic_check_for_leave_lps(struct adapter *padapter, u8 tx, u32 tx_packets if (jiffies_to_msecs(jiffies - start_time) > 2000) { /* 2 sec == watch dog timer */ if (xmit_cnt > 8) { - if (adapter_to_pwrctl(padapter)->bLeisurePs - && (adapter_to_pwrctl(padapter)->pwr_mode != PS_MODE_ACTIVE) - && !(hal_btcoex_IsBtControlLps(padapter))) { + if (adapter_to_pwrctl(padapter)->bLeisurePs && + (adapter_to_pwrctl(padapter)->pwr_mode != PS_MODE_ACTIVE) && + !(hal_btcoex_IsBtControlLps(padapter))) { bLeaveLPS = true; } } @@ -206,17 +201,17 @@ void traffic_check_for_leave_lps(struct adapter *padapter, u8 tx, u32 tx_packets } } else { /* from rx path */ - if (pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod > 4/*2*/) { - if (adapter_to_pwrctl(padapter)->bLeisurePs - && (adapter_to_pwrctl(padapter)->pwr_mode != PS_MODE_ACTIVE) - && !(hal_btcoex_IsBtControlLps(padapter))) + if (pmlmepriv->link_detect_info.num_rx_unicast_ok_in_period > 4) { + if (adapter_to_pwrctl(padapter)->bLeisurePs && + (adapter_to_pwrctl(padapter)->pwr_mode != PS_MODE_ACTIVE) && + !(hal_btcoex_IsBtControlLps(padapter))) bLeaveLPS = true; } } if (bLeaveLPS) /* rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_LEAVE, 1); */ - rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_LEAVE, tx?0:1); + rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_LEAVE, tx ? 0 : 1); } /* @@ -240,12 +235,10 @@ void rtw_set_rpwm(struct adapter *padapter, u8 pslv) if (pwrpriv->rpwm == pslv || (pwrpriv->rpwm >= PS_STATE_S2 && pslv >= PS_STATE_S2)) return; - } if ((padapter->bSurpriseRemoved) || !(padapter->hw_init_completed)) { pwrpriv->cpwm = PS_STATE_S4; - return; } @@ -293,15 +286,16 @@ void rtw_set_rpwm(struct adapter *padapter, u8 pslv) break; } } while (1); - } else + } else { pwrpriv->cpwm = pslv; + } } static u8 PS_RDY_CHECK(struct adapter *padapter) { unsigned long curr_time, delta_time; struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(padapter); - struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); + struct mlme_priv *pmlmepriv = &padapter->mlmepriv; if (pwrpriv->bInSuspend) return false; @@ -313,11 +307,11 @@ static u8 PS_RDY_CHECK(struct adapter *padapter) if (delta_time < LPS_DELAY_TIME) return false; - if (check_fwstate(pmlmepriv, WIFI_SITE_MONITOR) - || check_fwstate(pmlmepriv, WIFI_UNDER_LINKING|WIFI_UNDER_WPS) - || check_fwstate(pmlmepriv, WIFI_AP_STATE) - || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE|WIFI_ADHOC_STATE) - || rtw_is_scan_deny(padapter) + if (check_fwstate(pmlmepriv, WIFI_SITE_MONITOR) || + check_fwstate(pmlmepriv, WIFI_UNDER_LINKING | WIFI_UNDER_WPS) || + check_fwstate(pmlmepriv, WIFI_AP_STATE) || + check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE | WIFI_ADHOC_STATE) || + rtw_is_scan_deny(padapter) ) return false; @@ -331,7 +325,8 @@ static u8 PS_RDY_CHECK(struct adapter *padapter) return true; } -void rtw_set_ps_mode(struct adapter *padapter, u8 ps_mode, u8 smart_ps, u8 bcn_ant_mode, const char *msg) +void rtw_set_ps_mode(struct adapter *padapter, u8 ps_mode, + u8 smart_ps, u8 bcn_ant_mode, const char *msg) { struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(padapter); @@ -342,14 +337,13 @@ void rtw_set_ps_mode(struct adapter *padapter, u8 ps_mode, u8 smart_ps, u8 bcn_a if (ps_mode == PS_MODE_ACTIVE) return; - mutex_lock(&pwrpriv->lock); /* if (pwrpriv->pwr_mode == PS_MODE_ACTIVE) */ if (ps_mode == PS_MODE_ACTIVE) { - if (!(hal_btcoex_IsBtControlLps(padapter)) - || (hal_btcoex_IsBtControlLps(padapter) - && !(hal_btcoex_IsLpsOn(padapter)))) { + if (!(hal_btcoex_IsBtControlLps(padapter)) || + (hal_btcoex_IsBtControlLps(padapter) && + !(hal_btcoex_IsLpsOn(padapter)))) { pwrpriv->pwr_mode = ps_mode; rtw_set_rpwm(padapter, PS_STATE_S4); @@ -359,7 +353,8 @@ void rtw_set_ps_mode(struct adapter *padapter, u8 ps_mode, u8 smart_ps, u8 bcn_a hal_btcoex_LpsNotify(padapter, ps_mode); } } else { - if ((PS_RDY_CHECK(padapter) && check_fwstate(&padapter->mlmepriv, WIFI_ASOC_STATE)) || + if ((PS_RDY_CHECK(padapter) && + check_fwstate(&padapter->mlmepriv, WIFI_ASOC_STATE)) || ((hal_btcoex_IsBtControlLps(padapter)) && (hal_btcoex_IsLpsOn(padapter))) ) { u8 pslv; @@ -404,7 +399,6 @@ s32 LPS_RF_ON_check(struct adapter *padapter, u32 delay_ms) u8 bAwake = false; s32 err = 0; - start_time = jiffies; while (1) { rtw_hal_get_hwreg(padapter, HW_VAR_FWLPS_RF_ON, &bAwake); @@ -438,7 +432,7 @@ void LPS_Enter(struct adapter *padapter, const char *msg) return; /* Skip lps enter request if number of associated adapters is not 1 */ - if (check_fwstate(&(dvobj->padapters->mlmepriv), WIFI_ASOC_STATE)) + if (check_fwstate(&dvobj->padapters->mlmepriv, WIFI_ASOC_STATE)) n_assoc_iface++; if (n_assoc_iface != 1) return; @@ -452,10 +446,12 @@ void LPS_Enter(struct adapter *padapter, const char *msg) if (pwrpriv->pwr_mode == PS_MODE_ACTIVE) { scnprintf(buf, sizeof(buf), "WIFI-%s", msg); pwrpriv->bpower_saving = true; - rtw_set_ps_mode(padapter, pwrpriv->power_mgnt, padapter->registrypriv.smart_ps, 0, buf); + rtw_set_ps_mode(padapter, pwrpriv->power_mgnt, + padapter->registrypriv.smart_ps, 0, buf); } - } else + } else { pwrpriv->LpsIdleCount++; + } } } @@ -487,7 +483,7 @@ void LPS_Leave(struct adapter *padapter, const char *msg) void LeaveAllPowerSaveModeDirect(struct adapter *Adapter) { struct adapter *pri_padapter = GET_PRIMARY_ADAPTER(Adapter); - struct mlme_priv *pmlmepriv = &(Adapter->mlmepriv); + struct mlme_priv *pmlmepriv = &Adapter->mlmepriv; struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(Adapter); if (Adapter->bSurpriseRemoved) @@ -527,7 +523,7 @@ void LeaveAllPowerSaveMode(struct adapter *Adapter) if (Adapter->bSurpriseRemoved) return; - if (check_fwstate(&(dvobj->padapters->mlmepriv), WIFI_ASOC_STATE)) + if (check_fwstate(&dvobj->padapters->mlmepriv, WIFI_ASOC_STATE)) n_assoc_iface++; if (n_assoc_iface) { /* connect */ @@ -606,7 +602,6 @@ void cpwm_int_hdl(struct adapter *padapter, struct reportpwrstate_parm *preportp exit: mutex_unlock(&pwrpriv->lock); - } static void cpwm_event_callback(struct work_struct *work) @@ -626,7 +621,6 @@ static void rpwmtimeout_workitem_callback(struct work_struct *work) struct dvobj_priv *dvobj; struct pwrctrl_priv *pwrpriv; - pwrpriv = container_of(work, struct pwrctrl_priv, rpwmtimeoutwi); dvobj = pwrctl_to_dvobj(pwrpriv); padapter = dvobj->if1; @@ -683,7 +677,6 @@ static inline void unregister_task_alive(struct pwrctrl_priv *pwrctrl, u32 tag) pwrctrl->alives &= ~tag; } - /* * Description: *Check if the fw_pwrstate is okay for I/O. @@ -762,10 +755,10 @@ void rtw_unregister_task_alive(struct adapter *padapter, u32 task) unregister_task_alive(pwrctrl, task); if ((pwrctrl->pwr_mode != PS_MODE_ACTIVE) && pwrctrl->fw_current_in_ps_mode) { - if (pwrctrl->cpwm > pslv) + if (pwrctrl->cpwm > pslv) { if ((pslv >= PS_STATE_S2) || (pwrctrl->alives == 0)) rtw_set_rpwm(padapter, pslv); - + } } mutex_unlock(&pwrctrl->lock); @@ -991,6 +984,7 @@ void rtw_free_pwrctrl_priv(struct adapter *adapter) inline void rtw_set_ips_deny(struct adapter *padapter, u32 ms) { struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(padapter); + pwrpriv->ips_deny_time = jiffies + msecs_to_jiffies(ms); } @@ -1020,7 +1014,6 @@ int _rtw_pwr_wakeup(struct adapter *padapter, u32 ips_deffer_ms, const char *cal if (time_before(pwrpriv->ips_deny_time, deny_time)) pwrpriv->ips_deny_time = deny_time; - if (pwrpriv->ps_processing) while (pwrpriv->ps_processing && jiffies_to_msecs(jiffies - start) <= 3000) mdelay(10); @@ -1068,7 +1061,6 @@ exit: if (time_before(pwrpriv->ips_deny_time, deny_time)) pwrpriv->ips_deny_time = deny_time; return ret; - } int rtw_pm_set_lps(struct adapter *padapter, u8 mode) @@ -1087,8 +1079,9 @@ int rtw_pm_set_lps(struct adapter *padapter, u8 mode) pwrctrlpriv->bLeisurePs = pwrctrlpriv->power_mgnt != PS_MODE_ACTIVE; } - } else + } else { ret = -EINVAL; + } return ret; } @@ -1104,8 +1097,9 @@ int rtw_pm_set_ips(struct adapter *padapter, u8 mode) rtw_ips_mode_req(pwrctrlpriv, mode); if ((padapter->bSurpriseRemoved == 0) && (rtw_pwr_wakeup(padapter) == _FAIL)) return -EFAULT; - } else + } else { return -EINVAL; + } return 0; } diff --git a/drivers/staging/rtl8723bs/core/rtw_recv.c b/drivers/staging/rtl8723bs/core/rtw_recv.c index 337671b1211f..f78194d508df 100644 --- a/drivers/staging/rtl8723bs/core/rtw_recv.c +++ b/drivers/staging/rtl8723bs/core/rtw_recv.c @@ -322,7 +322,7 @@ static void rtw_handle_tkip_mic_err(struct adapter *padapter, u8 bgroup) } else { cur_time = jiffies; - if (cur_time - psecuritypriv->last_mic_err_time < 60*HZ) { + if (cur_time - psecuritypriv->last_mic_err_time < 60 * HZ) { psecuritypriv->btkip_countermeasure = true; psecuritypriv->last_mic_err_time = 0; psecuritypriv->btkip_countermeasure_time = cur_time; @@ -390,13 +390,13 @@ static signed int recvframe_chkmic(struct adapter *adapter, union recv_frame *p mickey = &stainfo->dot11tkiprxmickey.skey[0]; } - datalen = precvframe->u.hdr.len-prxattrib->hdrlen-prxattrib->iv_len-prxattrib->icv_len-8;/* icv_len included the mic code */ + datalen = precvframe->u.hdr.len - prxattrib->hdrlen - prxattrib->iv_len - prxattrib->icv_len - 8;/* icv_len included the mic code */ pframe = precvframe->u.hdr.rx_data; - payload = pframe+prxattrib->hdrlen+prxattrib->iv_len; + payload = pframe + prxattrib->hdrlen + prxattrib->iv_len; rtw_seccalctkipmic(mickey, pframe, payload, datalen, &miccode[0], (unsigned char)prxattrib->priority); /* care the length of the data */ - pframemic = payload+datalen; + pframemic = payload + datalen; bmic_err = false; @@ -444,9 +444,9 @@ static union recv_frame *decryptor(struct adapter *padapter, union recv_frame *p u32 res = _SUCCESS; if (prxattrib->encrypt > 0) { - u8 *iv = precv_frame->u.hdr.rx_data+prxattrib->hdrlen; + u8 *iv = precv_frame->u.hdr.rx_data + prxattrib->hdrlen; - prxattrib->key_index = (((iv[3])>>6)&0x3); + prxattrib->key_index = (((iv[3]) >> 6) & 0x3); if (prxattrib->key_index > WEP_KEYS) { switch (prxattrib->encrypt) { @@ -568,7 +568,7 @@ static signed int recv_decache(union recv_frame *precv_frame, u8 bretry, struct { signed int tid = precv_frame->u.hdr.attrib.priority; - u16 seq_ctrl = ((precv_frame->u.hdr.attrib.seq_num&0xffff) << 4) | + u16 seq_ctrl = ((precv_frame->u.hdr.attrib.seq_num & 0xffff) << 4) | (precv_frame->u.hdr.attrib.frag_num & 0xf); if (tid > 15) @@ -632,29 +632,29 @@ static void process_wmmps_data(struct adapter *padapter, union recv_frame *precv if (!psta->qos_option) return; - if (!(psta->qos_info&0xf)) + if (!(psta->qos_info & 0xf)) return; - if (psta->state&WIFI_SLEEP_STATE) { + if (psta->state & WIFI_SLEEP_STATE) { u8 wmmps_ac = 0; switch (pattrib->priority) { case 1: case 2: - wmmps_ac = psta->uapsd_bk&BIT(1); + wmmps_ac = psta->uapsd_bk & BIT(1); break; case 4: case 5: - wmmps_ac = psta->uapsd_vi&BIT(1); + wmmps_ac = psta->uapsd_vi & BIT(1); break; case 6: case 7: - wmmps_ac = psta->uapsd_vo&BIT(1); + wmmps_ac = psta->uapsd_vo & BIT(1); break; case 0: case 3: default: - wmmps_ac = psta->uapsd_be&BIT(1); + wmmps_ac = psta->uapsd_be & BIT(1); break; } @@ -680,10 +680,10 @@ static void count_rx_stats(struct adapter *padapter, union recv_frame *prframe, sz = get_recvframe_len(prframe); precvpriv->rx_bytes += sz; - padapter->mlmepriv.LinkDetectInfo.NumRxOkInPeriod++; + padapter->mlmepriv.link_detect_info.num_rx_ok_in_period++; if ((!is_broadcast_ether_addr(pattrib->dst)) && (!is_multicast_ether_addr(pattrib->dst))) - padapter->mlmepriv.LinkDetectInfo.NumRxUnicastOkInPeriod++; + padapter->mlmepriv.link_detect_info.num_rx_unicast_ok_in_period++; if (sta) psta = sta; @@ -979,20 +979,20 @@ static signed int validate_recv_ctrl_frame(struct adapter *padapter, union recv_ switch (pattrib->priority) { case 1: case 2: - wmmps_ac = psta->uapsd_bk&BIT(0); + wmmps_ac = psta->uapsd_bk & BIT(0); break; case 4: case 5: - wmmps_ac = psta->uapsd_vi&BIT(0); + wmmps_ac = psta->uapsd_vi & BIT(0); break; case 6: case 7: - wmmps_ac = psta->uapsd_vo&BIT(0); + wmmps_ac = psta->uapsd_vo & BIT(0); break; case 0: case 3: default: - wmmps_ac = psta->uapsd_be&BIT(0); + wmmps_ac = psta->uapsd_be & BIT(0); break; } @@ -1004,7 +1004,7 @@ static signed int validate_recv_ctrl_frame(struct adapter *padapter, union recv_ psta->state ^= WIFI_STA_ALIVE_CHK_STATE; } - if ((psta->state&WIFI_SLEEP_STATE) && (pstapriv->sta_dz_bitmap&BIT(psta->aid))) { + if ((psta->state & WIFI_SLEEP_STATE) && (pstapriv->sta_dz_bitmap & BIT(psta->aid))) { struct list_head *xmitframe_plist, *xmitframe_phead; struct xmit_frame *pxmitframe = NULL; struct xmit_priv *pxmitpriv = &padapter->xmitpriv; @@ -1048,7 +1048,7 @@ static signed int validate_recv_ctrl_frame(struct adapter *padapter, union recv_ /* spin_unlock_bh(&psta->sleep_q.lock); */ spin_unlock_bh(&pxmitpriv->lock); - if (pstapriv->tim_bitmap&BIT(psta->aid)) { + if (pstapriv->tim_bitmap & BIT(psta->aid)) { if (psta->sleepq_len == 0) { /* issue nulldata with More data bit = 0 to indicate we have no buffered packets */ issue_nulldata_in_interrupt(padapter, psta->hwaddr); @@ -1249,34 +1249,41 @@ static union recv_frame *recvframe_chk_defrag(struct adapter *padapter, union re static signed int validate_recv_mgnt_frame(struct adapter *padapter, union recv_frame *precv_frame) { - /* struct mlme_priv *pmlmepriv = &adapter->mlmepriv; */ + struct sta_info *psta; precv_frame = recvframe_chk_defrag(padapter, precv_frame); if (!precv_frame) return _SUCCESS; - { - /* for rx pkt statistics */ - struct sta_info *psta = rtw_get_stainfo(&padapter->stapriv, GetAddr2Ptr(precv_frame->u.hdr.rx_data)); + /* for rx pkt statistics */ + psta = rtw_get_stainfo(&padapter->stapriv, GetAddr2Ptr(precv_frame->u.hdr.rx_data)); + if (!psta) + goto exit; - if (psta) { - psta->sta_stats.rx_mgnt_pkts++; - if (GetFrameSubType(precv_frame->u.hdr.rx_data) == WIFI_BEACON) - psta->sta_stats.rx_beacon_pkts++; - else if (GetFrameSubType(precv_frame->u.hdr.rx_data) == WIFI_PROBEREQ) - psta->sta_stats.rx_probereq_pkts++; - else if (GetFrameSubType(precv_frame->u.hdr.rx_data) == WIFI_PROBERSP) { - if (!memcmp(padapter->eeprompriv.mac_addr, GetAddr1Ptr(precv_frame->u.hdr.rx_data), ETH_ALEN)) - psta->sta_stats.rx_probersp_pkts++; - else if (is_broadcast_mac_addr(GetAddr1Ptr(precv_frame->u.hdr.rx_data)) || - is_multicast_mac_addr(GetAddr1Ptr(precv_frame->u.hdr.rx_data))) - psta->sta_stats.rx_probersp_bm_pkts++; - else - psta->sta_stats.rx_probersp_uo_pkts++; - } - } + psta->sta_stats.rx_mgnt_pkts++; + + switch (GetFrameSubType(precv_frame->u.hdr.rx_data)) { + case WIFI_BEACON: + psta->sta_stats.rx_beacon_pkts++; + break; + case WIFI_PROBEREQ: + psta->sta_stats.rx_probereq_pkts++; + break; + case WIFI_PROBERSP: + if (!memcmp(padapter->eeprompriv.mac_addr, + GetAddr1Ptr(precv_frame->u.hdr.rx_data), + ETH_ALEN)) + psta->sta_stats.rx_probersp_pkts++; + else if (is_broadcast_ether_addr(GetAddr1Ptr(precv_frame->u.hdr.rx_data)) || + is_multicast_ether_addr(GetAddr1Ptr(precv_frame->u.hdr.rx_data))) + psta->sta_stats.rx_probersp_bm_pkts++; + else + psta->sta_stats.rx_probersp_uo_pkts++; + + break; } +exit: mgt_dispatcher(padapter, precv_frame); return _SUCCESS; @@ -1339,11 +1346,8 @@ static signed int validate_recv_data_frame(struct adapter *adapter, union recv_f } - if (ret == _FAIL) { + if (ret == _FAIL || ret == RTW_RX_HANDLED) goto exit; - } else if (ret == RTW_RX_HANDLED) { - goto exit; - } if (!psta) { @@ -1430,9 +1434,9 @@ static signed int validate_80211w_mgmt(struct adapter *adapter, union recv_frame goto validate_80211w_fail; precv_frame = decryptor(adapter, precv_frame); /* save actual management data frame body */ - memcpy(mgmt_DATA, ptr+pattrib->hdrlen+pattrib->iv_len, data_len); + memcpy(mgmt_DATA, ptr + pattrib->hdrlen + pattrib->iv_len, data_len); /* overwrite the iv field */ - memcpy(ptr+pattrib->hdrlen, mgmt_DATA, data_len); + memcpy(ptr + pattrib->hdrlen, mgmt_DATA, data_len); /* remove the iv and icv length */ pattrib->pkt_len = pattrib->pkt_len - pattrib->iv_len - pattrib->icv_len; kfree(mgmt_DATA); @@ -1488,7 +1492,7 @@ static signed int validate_recv_frame(struct adapter *adapter, union recv_frame struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib; u8 *ptr = precv_frame->u.hdr.rx_data; - u8 ver = (unsigned char) (*ptr)&0x3; + u8 ver = (unsigned char) (*ptr) & 0x3; /* add version chk */ if (ver != 0) { @@ -1526,7 +1530,7 @@ static signed int validate_recv_frame(struct adapter *adapter, union recv_frame retval = _FAIL; /* only data frame return _SUCCESS */ break; case WIFI_DATA_TYPE: /* data */ - pattrib->qos = (subtype & BIT(7)) ? 1:0; + pattrib->qos = (subtype & BIT(7)) ? 1 : 0; retval = validate_recv_data_frame(adapter, precv_frame); if (retval == _FAIL) { struct recv_priv *precvpriv = &adapter->recvpriv; @@ -1571,8 +1575,8 @@ static signed int wlanhdr_to_ethhdr(union recv_frame *precvframe) if (pattrib->encrypt) recvframe_pull_tail(precvframe, pattrib->icv_len); - psnap = (struct ieee80211_snap_hdr *)(ptr+pattrib->hdrlen + pattrib->iv_len); - psnap_type = ptr+pattrib->hdrlen + pattrib->iv_len+SNAP_SIZE; + psnap = (struct ieee80211_snap_hdr *)(ptr + pattrib->hdrlen + pattrib->iv_len); + psnap_type = ptr + pattrib->hdrlen + pattrib->iv_len + SNAP_SIZE; /* convert hdr + possible LLC headers into Ethernet header */ /* eth_type = (psnap_type[0] << 8) | psnap_type[1]; */ if ((!memcmp(psnap, rfc1042_header, SNAP_SIZE) && @@ -1586,37 +1590,37 @@ static signed int wlanhdr_to_ethhdr(union recv_frame *precvframe) /* Leave Ethernet header part of hdr and full payload */ bsnaphdr = false; - rmv_len = pattrib->hdrlen + pattrib->iv_len + (bsnaphdr?SNAP_SIZE:0); + rmv_len = pattrib->hdrlen + pattrib->iv_len + (bsnaphdr ? SNAP_SIZE : 0); len = precvframe->u.hdr.len - rmv_len; - memcpy(&be_tmp, ptr+rmv_len, 2); + memcpy(&be_tmp, ptr + rmv_len, 2); eth_type = ntohs(be_tmp); /* pattrib->ether_type */ pattrib->eth_type = eth_type; if ((check_fwstate(pmlmepriv, WIFI_MP_STATE) == true)) { ptr += rmv_len; *ptr = 0x87; - *(ptr+1) = 0x12; + *(ptr + 1) = 0x12; eth_type = 0x8712; /* append rx status for mp test packets */ - ptr = recvframe_pull(precvframe, (rmv_len-sizeof(struct ethhdr)+2)-24); + ptr = recvframe_pull(precvframe, (rmv_len - sizeof(struct ethhdr) + 2) - 24); if (!ptr) return _FAIL; memcpy(ptr, get_rxmem(precvframe), 24); ptr += 24; } else { - ptr = recvframe_pull(precvframe, (rmv_len-sizeof(struct ethhdr) + (bsnaphdr?2:0))); + ptr = recvframe_pull(precvframe, (rmv_len - sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0))); if (!ptr) return _FAIL; } memcpy(ptr, pattrib->dst, ETH_ALEN); - memcpy(ptr+ETH_ALEN, pattrib->src, ETH_ALEN); + memcpy(ptr + ETH_ALEN, pattrib->src, ETH_ALEN); if (!bsnaphdr) { be_tmp = htons(len); - memcpy(ptr+12, &be_tmp, 2); + memcpy(ptr + 12, &be_tmp, 2); } return _SUCCESS; @@ -1754,7 +1758,7 @@ static int amsdu_to_msdu(struct adapter *padapter, union recv_frame *prframe) pdata += nSubframe_Length; a_len -= nSubframe_Length; if (a_len != 0) { - padding_len = 4 - ((nSubframe_Length + ETH_HLEN) & (4-1)); + padding_len = 4 - ((nSubframe_Length + ETH_HLEN) & (4 - 1)); if (padding_len == 4) padding_len = 0; @@ -2004,7 +2008,7 @@ static int recv_indicatepkt_reorder(struct adapter *padapter, union recv_frame * rtw_recv_indicatepkt(padapter, prframe); - preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + 1)%4096; + preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + 1) % 4096; return _SUCCESS; } @@ -2014,7 +2018,7 @@ static int recv_indicatepkt_reorder(struct adapter *padapter, union recv_frame * retval = amsdu_to_msdu(padapter, prframe); - preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + 1)%4096; + preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + 1) % 4096; return retval; } @@ -2200,7 +2204,7 @@ static int recv_func(struct adapter *padapter, union recv_frame *rframe) !psecuritypriv->busetkipkey) { rtw_enqueue_recvframe(rframe, &padapter->recvpriv.uc_swdec_pending_queue); - if (recvpriv->free_recvframe_cnt < NR_RECVFRAME/4) { + if (recvpriv->free_recvframe_cnt < NR_RECVFRAME / 4) { /* to prevent from recvframe starvation, get recvframe from uc_swdec_pending_queue to free_recvframe_cnt */ rframe = rtw_alloc_recvframe(&padapter->recvpriv.uc_swdec_pending_queue); if (rframe) @@ -2289,19 +2293,19 @@ static void rtw_signal_stat_timer_hdl(struct timer_list *t) } /* update value of signal_strength, rssi, signal_qual */ - tmp_s = (avg_signal_strength+(_alpha-1)*recvpriv->signal_strength); + tmp_s = (avg_signal_strength + (_alpha - 1) * recvpriv->signal_strength); if (tmp_s % _alpha) - tmp_s = tmp_s/_alpha + 1; + tmp_s = tmp_s / _alpha + 1; else - tmp_s = tmp_s/_alpha; + tmp_s = tmp_s / _alpha; if (tmp_s > 100) tmp_s = 100; - tmp_q = (avg_signal_qual+(_alpha-1)*recvpriv->signal_qual); + tmp_q = (avg_signal_qual + (_alpha - 1) * recvpriv->signal_qual); if (tmp_q % _alpha) - tmp_q = tmp_q/_alpha + 1; + tmp_q = tmp_q / _alpha + 1; else - tmp_q = tmp_q/_alpha; + tmp_q = tmp_q / _alpha; if (tmp_q > 100) tmp_q = 100; diff --git a/drivers/staging/rtl8723bs/core/rtw_security.c b/drivers/staging/rtl8723bs/core/rtw_security.c index b489babe7432..a00504ff2910 100644 --- a/drivers/staging/rtl8723bs/core/rtw_security.c +++ b/drivers/staging/rtl8723bs/core/rtw_security.c @@ -196,7 +196,6 @@ void rtw_secgetmic(struct mic_data *pmicdata, u8 *dst) secmicclear(pmicdata); } - void rtw_seccalctkipmic(u8 *key, u8 *header, u8 *data, u32 data_len, u8 *mic_code, u8 pri) { @@ -222,7 +221,6 @@ void rtw_seccalctkipmic(u8 *key, u8 *header, u8 *data, u32 data_len, u8 *mic_cod } rtw_secmicappend(&micdata, &priority[0], 4); - rtw_secmicappend(&micdata, data, data_len); rtw_secgetmic(&micdata, mic_code); @@ -282,7 +280,6 @@ static const unsigned short Sbox1[2][256] = { /* Sbox for hash (can be in R 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, }, - { /* second half of table is unsigned char-reversed version of first! */ 0xA5C6, 0x84F8, 0x99EE, 0x8DF6, 0x0DFF, 0xBDD6, 0xB1DE, 0x5491, 0x5060, 0x0302, 0xA9CE, 0x7D56, 0x19E7, 0x62B5, 0xE64D, 0x9AEC, @@ -357,7 +354,6 @@ static void phase1(u16 *p1k, const u8 *tk, const u8 *ta, u32 iv32) } } - /* * Routine: Phase 2 -- generate RC4KEY, given TK, P1K, IV16 * @@ -535,7 +531,7 @@ u32 rtw_tkip_decrypt(struct adapter *padapter, u8 *precvframe) if (start == 0) start = jiffies; - if (is_broadcast_mac_addr(prxattrib->ra)) + if (is_broadcast_ether_addr(prxattrib->ra)) no_gkey_bc_cnt++; else no_gkey_mc_cnt++; @@ -662,8 +658,8 @@ static void construct_mic_iv(u8 *mic_iv, for (i = 8; i < 14; i++) mic_iv[i] = pn_vector[13 - i]; /* mic_iv[8:13] = PN[5:0] */ #endif - mic_iv[14] = (unsigned char) (payload_length / 256); - mic_iv[15] = (unsigned char) (payload_length % 256); + mic_iv[14] = (unsigned char)(payload_length / 256); + mic_iv[15] = (unsigned char)(payload_length % 256); } /************************************************/ @@ -785,8 +781,8 @@ static void construct_ctr_preload(u8 *ctr_preload, for (i = 8; i < 14; i++) ctr_preload[i] = pn_vector[13 - i]; /* ctr_preload[8:13] = PN[5:0] */ #endif - ctr_preload[14] = (unsigned char) (c / 256); /* Ctr */ - ctr_preload[15] = (unsigned char) (c % 256); + ctr_preload[14] = (unsigned char)(c / 256); /* Ctr */ + ctr_preload[15] = (unsigned char)(c % 256); } static signed int aes_cipher(u8 *key, uint hdrlen, @@ -1091,7 +1087,7 @@ static signed int aes_decipher(u8 *key, uint hdrlen, /* start to calculate the mic */ if ((hdrlen + plen + 8) <= MAX_MSG_SIZE) - memcpy((void *)message, pframe, (hdrlen + plen + 8)); /* 8 is for ext iv len */ + memcpy(message, pframe, (hdrlen + plen + 8)); /* 8 is for ext iv len */ pn_vector[0] = pframe[hdrlen]; pn_vector[1] = pframe[hdrlen + 1]; @@ -1213,69 +1209,67 @@ u32 rtw_aes_decrypt(struct adapter *padapter, u8 *precvframe) pframe = (unsigned char *)((union recv_frame *)precvframe)->u.hdr.rx_data; /* 4 start to encrypt each fragment */ - if (prxattrib->encrypt == _AES_) { - stainfo = rtw_get_stainfo(&padapter->stapriv, &prxattrib->ta[0]); - if (stainfo) { - if (is_multicast_ether_addr(prxattrib->ra)) { - static unsigned long start; - static u32 no_gkey_bc_cnt; - static u32 no_gkey_mc_cnt; + if (prxattrib->encrypt != _AES_) + return _SUCCESS; + stainfo = rtw_get_stainfo(&padapter->stapriv, &prxattrib->ta[0]); + if (stainfo) + return _FAIL; + if (is_multicast_ether_addr(prxattrib->ra)) { + static unsigned long start; + static u32 no_gkey_bc_cnt; + static u32 no_gkey_mc_cnt; - if (!psecuritypriv->binstallGrpkey) { - res = _FAIL; + if (!psecuritypriv->binstallGrpkey) { + res = _FAIL; - if (start == 0) - start = jiffies; + if (start == 0) + start = jiffies; - if (is_broadcast_mac_addr(prxattrib->ra)) - no_gkey_bc_cnt++; - else - no_gkey_mc_cnt++; - - if (jiffies_to_msecs(jiffies - start) > 1000) { - if (no_gkey_bc_cnt || no_gkey_mc_cnt) { - netdev_dbg(padapter->pnetdev, - FUNC_ADPT_FMT " no_gkey_bc_cnt:%u, no_gkey_mc_cnt:%u\n", - FUNC_ADPT_ARG(padapter), - no_gkey_bc_cnt, - no_gkey_mc_cnt); - } - start = jiffies; - no_gkey_bc_cnt = 0; - no_gkey_mc_cnt = 0; - } - - goto exit; - } + if (is_broadcast_ether_addr(prxattrib->ra)) + no_gkey_bc_cnt++; + else + no_gkey_mc_cnt++; + if (jiffies_to_msecs(jiffies - start) > 1000) { if (no_gkey_bc_cnt || no_gkey_mc_cnt) { netdev_dbg(padapter->pnetdev, - FUNC_ADPT_FMT " gkey installed. no_gkey_bc_cnt:%u, no_gkey_mc_cnt:%u\n", + FUNC_ADPT_FMT " no_gkey_bc_cnt:%u, no_gkey_mc_cnt:%u\n", FUNC_ADPT_ARG(padapter), no_gkey_bc_cnt, no_gkey_mc_cnt); } - start = 0; + start = jiffies; no_gkey_bc_cnt = 0; no_gkey_mc_cnt = 0; - - prwskey = psecuritypriv->dot118021XGrpKey[prxattrib->key_index].skey; - if (psecuritypriv->dot118021XGrpKeyid != prxattrib->key_index) { - res = _FAIL; - goto exit; - } - } else { - prwskey = &stainfo->dot118021x_UncstKey.skey[0]; } - length = ((union recv_frame *)precvframe)->u.hdr.len - prxattrib->hdrlen - prxattrib->iv_len; - - res = aes_decipher(prwskey, prxattrib->hdrlen, pframe, length); - - } else { - res = _FAIL; + goto exit; } + + if (no_gkey_bc_cnt || no_gkey_mc_cnt) { + netdev_dbg(padapter->pnetdev, + FUNC_ADPT_FMT " gkey installed. no_gkey_bc_cnt:%u, no_gkey_mc_cnt:%u\n", + FUNC_ADPT_ARG(padapter), + no_gkey_bc_cnt, + no_gkey_mc_cnt); + } + start = 0; + no_gkey_bc_cnt = 0; + no_gkey_mc_cnt = 0; + + prwskey = psecuritypriv->dot118021XGrpKey[prxattrib->key_index].skey; + if (psecuritypriv->dot118021XGrpKeyid != prxattrib->key_index) { + res = _FAIL; + goto exit; + } + } else { + prwskey = &stainfo->dot118021x_UncstKey.skey[0]; } + + length = ((union recv_frame *)precvframe)->u.hdr.len - prxattrib->hdrlen - prxattrib->iv_len; + + res = aes_decipher(prwskey, prxattrib->hdrlen, pframe, length); + exit: return res; } @@ -1291,7 +1285,7 @@ u32 rtw_BIP_verify(struct adapter *padapter, u8 *precvframe) u8 mic[16]; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; __le16 le_tmp; - __le64 le_tmp64; + __le64 le_tmp64 = 0; ori_len = pattrib->pkt_len - WLAN_HDR_A3_LEN + BIP_AAD_SIZE; BIP_AAD = kzalloc(ori_len, GFP_KERNEL); @@ -1339,7 +1333,7 @@ u32 rtw_BIP_verify(struct adapter *padapter, u8 *precvframe) goto BIP_exit; /* MIC field should be last 8 bytes of packet (packet without FCS) */ - if (!memcmp(mic, pframe+pattrib->pkt_len-8, 8)) { + if (!memcmp(mic, pframe + pattrib->pkt_len - 8, 8)) { pmlmeext->mgnt_80211w_IPN_rx = temp_ipn; res = _SUCCESS; } else { @@ -1462,7 +1456,7 @@ int omac1_aes_128(u8 *key, u8 *data, size_t data_len, u8 *mac) /* Restore HW wep key setting according to key_mask */ void rtw_sec_restore_wep_key(struct adapter *adapter) { - struct security_priv *securitypriv = &(adapter->securitypriv); + struct security_priv *securitypriv = &adapter->securitypriv; signed int keyid; if ((securitypriv->dot11PrivacyAlgrthm == _WEP40_) || (securitypriv->dot11PrivacyAlgrthm == _WEP104_)) { @@ -1479,7 +1473,7 @@ void rtw_sec_restore_wep_key(struct adapter *adapter) u8 rtw_handle_tkip_countermeasure(struct adapter *adapter, const char *caller) { - struct security_priv *securitypriv = &(adapter->securitypriv); + struct security_priv *securitypriv = &adapter->securitypriv; u8 status = _SUCCESS; if (securitypriv->btkip_countermeasure) { diff --git a/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c b/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c index bdd4b6d8fd2e..07a6db1d2317 100644 --- a/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c +++ b/drivers/staging/rtl8723bs/core/rtw_sta_mgt.c @@ -79,7 +79,7 @@ u32 _rtw_init_sta_priv(struct sta_priv *pstapriv) for (i = 0; i < NUM_STA; i++) { _rtw_init_stainfo(psta); - INIT_LIST_HEAD(&(pstapriv->sta_hash[i])); + INIT_LIST_HEAD(&pstapriv->sta_hash[i]); list_add_tail(&psta->list, get_list_head(&pstapriv->free_sta_queue)); @@ -149,7 +149,7 @@ u32 _rtw_free_sta_priv(struct sta_priv *pstapriv) /*delete all reordering_ctrl_timer */ spin_lock_bh(&pstapriv->sta_hash_lock); for (index = 0; index < NUM_STA; index++) { - phead = &(pstapriv->sta_hash[index]); + phead = &pstapriv->sta_hash[index]; list_for_each(plist, phead) { int i; @@ -186,15 +186,15 @@ struct sta_info *rtw_alloc_stainfo(struct sta_priv *pstapriv, u8 *hwaddr) pfree_sta_queue = &pstapriv->free_sta_queue; /* spin_lock_bh(&(pfree_sta_queue->lock)); */ - spin_lock_bh(&(pstapriv->sta_hash_lock)); + spin_lock_bh(&pstapriv->sta_hash_lock); if (list_empty(&pfree_sta_queue->queue)) { /* spin_unlock_bh(&(pfree_sta_queue->lock)); */ - spin_unlock_bh(&(pstapriv->sta_hash_lock)); + spin_unlock_bh(&pstapriv->sta_hash_lock); return NULL; } psta = container_of(get_next(&pfree_sta_queue->queue), struct sta_info, list); - list_del_init(&(psta->list)); + list_del_init(&psta->list); /* spin_unlock_bh(&(pfree_sta_queue->lock)); */ @@ -207,11 +207,11 @@ struct sta_info *rtw_alloc_stainfo(struct sta_priv *pstapriv, u8 *hwaddr) index = wifi_mac_hash(hwaddr); if (index >= NUM_STA) { - spin_unlock_bh(&(pstapriv->sta_hash_lock)); + spin_unlock_bh(&pstapriv->sta_hash_lock); psta = NULL; goto exit; } - phash_list = &(pstapriv->sta_hash[index]); + phash_list = &pstapriv->sta_hash[index]; /* spin_lock_bh(&(pstapriv->sta_hash_lock)); */ @@ -258,7 +258,7 @@ struct sta_info *rtw_alloc_stainfo(struct sta_priv *pstapriv, u8 *hwaddr) /* init for the sequence number of received management frame */ psta->RxMgmtFrameSeqNum = 0xffff; - spin_unlock_bh(&(pstapriv->sta_hash_lock)); + spin_unlock_bh(&pstapriv->sta_hash_lock); /* alloc mac id for non-bc/mc station, */ rtw_alloc_macid(pstapriv->padapter, psta); @@ -300,7 +300,7 @@ u32 rtw_free_stainfo(struct adapter *padapter, struct sta_info *psta) /* vo */ /* spin_lock_bh(&(pxmitpriv->vo_pending.lock)); */ rtw_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->vo_q.sta_pending); - list_del_init(&(pstaxmitpriv->vo_q.tx_pending)); + list_del_init(&pstaxmitpriv->vo_q.tx_pending); phwxmit = pxmitpriv->hwxmits; phwxmit->accnt -= pstaxmitpriv->vo_q.qcnt; pstaxmitpriv->vo_q.qcnt = 0; @@ -309,7 +309,7 @@ u32 rtw_free_stainfo(struct adapter *padapter, struct sta_info *psta) /* vi */ /* spin_lock_bh(&(pxmitpriv->vi_pending.lock)); */ rtw_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->vi_q.sta_pending); - list_del_init(&(pstaxmitpriv->vi_q.tx_pending)); + list_del_init(&pstaxmitpriv->vi_q.tx_pending); phwxmit = pxmitpriv->hwxmits + 1; phwxmit->accnt -= pstaxmitpriv->vi_q.qcnt; pstaxmitpriv->vi_q.qcnt = 0; @@ -318,7 +318,7 @@ u32 rtw_free_stainfo(struct adapter *padapter, struct sta_info *psta) /* be */ /* spin_lock_bh(&(pxmitpriv->be_pending.lock)); */ rtw_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->be_q.sta_pending); - list_del_init(&(pstaxmitpriv->be_q.tx_pending)); + list_del_init(&pstaxmitpriv->be_q.tx_pending); phwxmit = pxmitpriv->hwxmits + 2; phwxmit->accnt -= pstaxmitpriv->be_q.qcnt; pstaxmitpriv->be_q.qcnt = 0; @@ -327,7 +327,7 @@ u32 rtw_free_stainfo(struct adapter *padapter, struct sta_info *psta) /* bk */ /* spin_lock_bh(&(pxmitpriv->bk_pending.lock)); */ rtw_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->bk_q.sta_pending); - list_del_init(&(pstaxmitpriv->bk_q.tx_pending)); + list_del_init(&pstaxmitpriv->bk_q.tx_pending); phwxmit = pxmitpriv->hwxmits + 3; phwxmit->accnt -= pstaxmitpriv->bk_q.qcnt; pstaxmitpriv->bk_q.qcnt = 0; @@ -369,7 +369,7 @@ u32 rtw_free_stainfo(struct adapter *padapter, struct sta_info *psta) plist = get_next(plist); - list_del_init(&(prframe->u.hdr.list)); + list_del_init(&prframe->u.hdr.list); rtw_free_recvframe(prframe, pfree_recv_queue); } @@ -435,7 +435,7 @@ void rtw_free_all_stainfo(struct adapter *padapter) spin_lock_bh(&pstapriv->sta_hash_lock); for (index = 0; index < NUM_STA; index++) { - phead = &(pstapriv->sta_hash[index]); + phead = &pstapriv->sta_hash[index]; list_for_each_safe(plist, tmp, phead) { psta = list_entry(plist, struct sta_info, hash_list); @@ -473,7 +473,7 @@ struct sta_info *rtw_get_stainfo(struct sta_priv *pstapriv, u8 *hwaddr) spin_lock_bh(&pstapriv->sta_hash_lock); - phead = &(pstapriv->sta_hash[index]); + phead = &pstapriv->sta_hash[index]; list_for_each(plist, phead) { psta = list_entry(plist, struct sta_info, hash_list); @@ -491,7 +491,7 @@ struct sta_info *rtw_get_stainfo(struct sta_priv *pstapriv, u8 *hwaddr) u32 rtw_init_bcmc_stainfo(struct adapter *padapter) { struct sta_info *psta; - NDIS_802_11_MAC_ADDRESS bcast_addr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + u8 bcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; struct sta_priv *pstapriv = &padapter->stapriv; /* struct __queue *pstapending = &padapter->xmitpriv.bm_pending; */ @@ -525,7 +525,7 @@ u8 rtw_access_ctrl(struct adapter *padapter, u8 *mac_addr) struct wlan_acl_pool *pacl_list = &pstapriv->acl_list; struct __queue *pacl_node_q = &pacl_list->acl_node_q; - spin_lock_bh(&(pacl_node_q->lock)); + spin_lock_bh(&pacl_node_q->lock); phead = get_list_head(pacl_node_q); list_for_each(plist, phead) { paclnode = list_entry(plist, struct rtw_wlan_acl_node, list); @@ -536,7 +536,7 @@ u8 rtw_access_ctrl(struct adapter *padapter, u8 *mac_addr) break; } } - spin_unlock_bh(&(pacl_node_q->lock)); + spin_unlock_bh(&pacl_node_q->lock); if (pacl_list->mode == 1) /* accept unless in deny list */ res = !match; diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index 3242978da36c..6a7c09db4cd9 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -572,7 +572,7 @@ s16 rtw_camid_alloc(struct adapter *adapter, struct sta_info *sta, u8 kid) if ((((mlmeinfo->state & 0x03) == WIFI_FW_AP_STATE) || ((mlmeinfo->state & 0x03) == WIFI_FW_ADHOC_STATE)) && !sta) { - /* AP/Ad-hoc mode group key: static alloction to default key by key ID */ + /* AP/Ad-hoc mode group key: static allocation to default key by key ID */ if (kid > 3) { netdev_dbg(adapter->pnetdev, FUNC_ADPT_FMT " group key with invalid key id:%u\n", @@ -597,7 +597,9 @@ s16 rtw_camid_alloc(struct adapter *adapter, struct sta_info *sta, u8 kid) i = _rtw_camid_search(adapter, addr, kid); if (i >= 0) { - /* Fix issue that pairwise and group key have same key id. Pairwise key first, group key can overwirte group only(ex: rekey) */ + /* Fix issue that pairwise and group key have same key id. + * Pairwise key first, group key can overwrite group only(ex: rekey) + */ if (sta || _rtw_camid_is_gk(adapter, i)) cam_id = i; else @@ -704,7 +706,7 @@ static void sort_wmm_ac_params(u32 *inx, u32 *edca) { u32 i, j, change_inx = false; - /* entry indx: 0->vo, 1->vi, 2->be, 3->bk. */ + /* entry index: 0->vo, 1->vi, 2->be, 3->bk. */ for (i = 0; i < 4; i++) { for (j = i + 1; j < 4; j++) { /* compare CW and AIFS */ @@ -1593,7 +1595,7 @@ int update_sta_support_rate(struct adapter *padapter, u8 *pvar_ie, uint var_ie_l { unsigned int ie_len; struct ndis_80211_var_ie *pIE; - int supportRateNum = 0; + int support_rate_num = 0; struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv); struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); @@ -1604,11 +1606,11 @@ int update_sta_support_rate(struct adapter *padapter, u8 *pvar_ie, uint var_ie_l return _FAIL; memcpy(pmlmeinfo->FW_sta_info[cam_idx].SupportedRates, pIE->data, ie_len); - supportRateNum = ie_len; + support_rate_num = ie_len; pIE = (struct ndis_80211_var_ie *)rtw_get_ie(pvar_ie, WLAN_EID_EXT_SUPP_RATES, &ie_len, var_ie_len); - if (pIE && (ie_len <= sizeof(pmlmeinfo->FW_sta_info[cam_idx].SupportedRates) - supportRateNum)) - memcpy((pmlmeinfo->FW_sta_info[cam_idx].SupportedRates + supportRateNum), pIE->data, ie_len); + if (pIE && (ie_len <= sizeof(pmlmeinfo->FW_sta_info[cam_idx].SupportedRates) - support_rate_num)) + memcpy((pmlmeinfo->FW_sta_info[cam_idx].SupportedRates + support_rate_num), pIE->data, ie_len); return _SUCCESS; } diff --git a/drivers/staging/rtl8723bs/core/rtw_xmit.c b/drivers/staging/rtl8723bs/core/rtw_xmit.c index 7b18be8912e6..7bce0343d59f 100644 --- a/drivers/staging/rtl8723bs/core/rtw_xmit.c +++ b/drivers/staging/rtl8723bs/core/rtw_xmit.c @@ -5,6 +5,8 @@ * ******************************************************************************/ #include +#include +#include static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 }; static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 }; @@ -128,7 +130,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) /* Tx buf allocation may fail sometimes, so sleep and retry. */ res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, (MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ), true); if (res == _FAIL) { - msleep(10); + fsleep(10 * USEC_PER_MSEC); res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, (MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ), true); if (res == _FAIL) goto exit; @@ -1194,12 +1196,13 @@ s32 rtw_mgmt_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, s pwlanhdr = (struct ieee80211_hdr *)pframe; ori_len = BIP_AAD_SIZE + pattrib->pktlen; - tmp_buf = BIP_AAD = kzalloc(ori_len, GFP_ATOMIC); - subtype = GetFrameSubType(pframe); /* bit(7)~bit(2) */ + BIP_AAD = kzalloc(ori_len, GFP_ATOMIC); if (!BIP_AAD) return _FAIL; + tmp_buf = BIP_AAD; + subtype = GetFrameSubType(pframe); /* bit(7)~bit(2) */ spin_lock_bh(&padapter->security_key_mutex); /* only support station mode */ @@ -1414,7 +1417,7 @@ void rtw_count_tx_stats(struct adapter *padapter, struct xmit_frame *pxmitframe, if ((pxmitframe->frame_tag & 0x0f) == DATA_FRAMETAG) { pkt_num = pxmitframe->agg_num; - pmlmepriv->LinkDetectInfo.NumTxOkInPeriod += pkt_num; + pmlmepriv->link_detect_info.num_tx_ok_in_period += pkt_num; pxmitpriv->tx_pkts += pkt_num; @@ -2026,8 +2029,8 @@ inline bool xmitframe_hiq_filter(struct xmit_frame *xmitframe) if (registry->hiq_filter == RTW_HIQ_FILTER_ALLOW_SPECIAL) { struct pkt_attrib *attrib = &xmitframe->attrib; - if (attrib->ether_type == 0x0806 || - attrib->ether_type == 0x888e || + if (attrib->ether_type == ETH_P_ARP || + attrib->ether_type == ETH_P_PAE || attrib->dhcp_pkt ) allow = true; diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c index b3d7f50fac4c..b3e34f97cfc6 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b1Ant.c @@ -684,52 +684,22 @@ static void halbtc8723b1ant_CoexTableWithType( struct btc_coexist *pBtCoexist, bool bForceExec, u8 type ) { + static const u32 table[8][2] = { + {0x55555555, 0x55555555}, + {0x55555555, 0x5a5a5a5a}, + {0x5a5a5a5a, 0x5a5a5a5a}, + {0xaaaa5555, 0xaaaa5a5a}, + {0x55555555, 0xaaaa5a5a}, + {0x5a5a5a5a, 0xaaaa5a5a}, + {0x55555555, 0xaaaaaaaa}, + {0xaaaaaaaa, 0xaaaaaaaa} + }; + pCoexSta->nCoexTableType = type; - switch (type) { - case 0: - halbtc8723b1ant_CoexTable( - pBtCoexist, bForceExec, 0x55555555, 0x55555555, 0xffffff, 0x3 - ); - break; - case 1: - halbtc8723b1ant_CoexTable( - pBtCoexist, bForceExec, 0x55555555, 0x5a5a5a5a, 0xffffff, 0x3 - ); - break; - case 2: - halbtc8723b1ant_CoexTable( - pBtCoexist, bForceExec, 0x5a5a5a5a, 0x5a5a5a5a, 0xffffff, 0x3 - ); - break; - case 3: - halbtc8723b1ant_CoexTable( - pBtCoexist, bForceExec, 0xaaaa5555, 0xaaaa5a5a, 0xffffff, 0x3 - ); - break; - case 4: - halbtc8723b1ant_CoexTable( - pBtCoexist, bForceExec, 0x55555555, 0xaaaa5a5a, 0xffffff, 0x3 - ); - break; - case 5: - halbtc8723b1ant_CoexTable( - pBtCoexist, bForceExec, 0x5a5a5a5a, 0xaaaa5a5a, 0xffffff, 0x3 - ); - break; - case 6: - halbtc8723b1ant_CoexTable( - pBtCoexist, bForceExec, 0x55555555, 0xaaaaaaaa, 0xffffff, 0x3 - ); - break; - case 7: - halbtc8723b1ant_CoexTable( - pBtCoexist, bForceExec, 0xaaaaaaaa, 0xaaaaaaaa, 0xffffff, 0x3 - ); - break; - default: - break; - } + if (type < 8) + halbtc8723b1ant_CoexTable(pBtCoexist, bForceExec, table[type][0], + table[type][1], 0xffffff, 0x3); } static void halbtc8723b1ant_SetFwIgnoreWlanAct( @@ -2316,7 +2286,7 @@ void EXhalbtc8723b1ant_ConnectNotify(struct btc_coexist *pBtCoexist, u8 type) if (type == BTC_ASSOCIATE_START) { pCoexSta->bWiFiIsHighPriTask = true; - pCoexDm->nArpCnt = 0; + pCoexDm->nArpCnt = 0; } else { pCoexSta->bWiFiIsHighPriTask = false; /* pCoexDm->nArpCnt = 0; */ diff --git a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c index 9df3274c1048..8f6849f2277e 100644 --- a/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c +++ b/drivers/staging/rtl8723bs/hal/HalPhyRf_8723B.c @@ -121,25 +121,15 @@ static void setIqkMatrix_8723B( static void setCCKFilterCoefficient(struct dm_odm_t *pDM_Odm, u8 CCKSwingIndex) { - if (!pDM_Odm->RFCalibrateInfo.bCCKinCH14) { - rtw_write8(pDM_Odm->Adapter, 0xa22, CCKSwingTable_Ch1_Ch13_New[CCKSwingIndex][0]); - rtw_write8(pDM_Odm->Adapter, 0xa23, CCKSwingTable_Ch1_Ch13_New[CCKSwingIndex][1]); - rtw_write8(pDM_Odm->Adapter, 0xa24, CCKSwingTable_Ch1_Ch13_New[CCKSwingIndex][2]); - rtw_write8(pDM_Odm->Adapter, 0xa25, CCKSwingTable_Ch1_Ch13_New[CCKSwingIndex][3]); - rtw_write8(pDM_Odm->Adapter, 0xa26, CCKSwingTable_Ch1_Ch13_New[CCKSwingIndex][4]); - rtw_write8(pDM_Odm->Adapter, 0xa27, CCKSwingTable_Ch1_Ch13_New[CCKSwingIndex][5]); - rtw_write8(pDM_Odm->Adapter, 0xa28, CCKSwingTable_Ch1_Ch13_New[CCKSwingIndex][6]); - rtw_write8(pDM_Odm->Adapter, 0xa29, CCKSwingTable_Ch1_Ch13_New[CCKSwingIndex][7]); - } else { - rtw_write8(pDM_Odm->Adapter, 0xa22, CCKSwingTable_Ch14_New[CCKSwingIndex][0]); - rtw_write8(pDM_Odm->Adapter, 0xa23, CCKSwingTable_Ch14_New[CCKSwingIndex][1]); - rtw_write8(pDM_Odm->Adapter, 0xa24, CCKSwingTable_Ch14_New[CCKSwingIndex][2]); - rtw_write8(pDM_Odm->Adapter, 0xa25, CCKSwingTable_Ch14_New[CCKSwingIndex][3]); - rtw_write8(pDM_Odm->Adapter, 0xa26, CCKSwingTable_Ch14_New[CCKSwingIndex][4]); - rtw_write8(pDM_Odm->Adapter, 0xa27, CCKSwingTable_Ch14_New[CCKSwingIndex][5]); - rtw_write8(pDM_Odm->Adapter, 0xa28, CCKSwingTable_Ch14_New[CCKSwingIndex][6]); - rtw_write8(pDM_Odm->Adapter, 0xa29, CCKSwingTable_Ch14_New[CCKSwingIndex][7]); - } + u8 (*swingtable)[8]; + + if (!pDM_Odm->RFCalibrateInfo.bCCKinCH14) + swingtable = CCKSwingTable_Ch1_Ch13_New; + else + swingtable = CCKSwingTable_Ch14_New; + + for (int i = 0; i < 8; i++) + rtw_write8(pDM_Odm->Adapter, 0xa22 + i, swingtable[CCKSwingIndex][i]); } /*----------------------------------------------------------------------------- @@ -208,8 +198,6 @@ void ODM_TxPwrTrackSetPwr_8723B( } if (Method == TXAGC) { - struct adapter *Adapter = pDM_Odm->Adapter; - pDM_Odm->Remnant_OFDMSwingIdx[RFPath] = pDM_Odm->Absolute_OFDMSwingIdx[RFPath]; pDM_Odm->Modify_TxAGC_Flag_PathA = true; @@ -1407,8 +1395,10 @@ static void phy_IQCalibrate_8723B( PHY_SetBBReg(pDM_Odm->Adapter, rFPGA0_IQK, bMaskH3Bytes, 0x000000); pDM_Odm->RFCalibrateInfo.TxLOK[RF_PATH_A] = PHY_QueryRFReg(pDM_Odm->Adapter, RF_PATH_A, 0x8, bRFRegOffsetMask); - result[t][0] = (PHY_QueryBBReg(pDM_Odm->Adapter, rTx_Power_Before_IQK_A, bMaskDWord)&0x3FF0000)>>16; - result[t][1] = (PHY_QueryBBReg(pDM_Odm->Adapter, rTx_Power_After_IQK_A, bMaskDWord)&0x3FF0000)>>16; + result[t][0] = (PHY_QueryBBReg(pDM_Odm->Adapter, rTx_Power_Before_IQK_A, + bMaskDWord) & 0x3FF0000) >> 16; + result[t][1] = (PHY_QueryBBReg(pDM_Odm->Adapter, rTx_Power_After_IQK_A, + bMaskDWord) & 0x3FF0000) >> 16; break; } } diff --git a/drivers/staging/rtl8723bs/hal/hal_btcoex.c b/drivers/staging/rtl8723bs/hal/hal_btcoex.c index 9105594d2dde..2a2dd60be8bb 100644 --- a/drivers/staging/rtl8723bs/hal/hal_btcoex.c +++ b/drivers/staging/rtl8723bs/hal/hal_btcoex.c @@ -167,7 +167,7 @@ static u8 halbtcoutsrc_IsWifiBusy(struct adapter *padapter) if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) == true) { if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) return true; - if (pmlmepriv->LinkDetectInfo.bBusyTraffic) + if (pmlmepriv->link_detect_info.busy_traffic) return true; } @@ -364,9 +364,10 @@ static u8 halbtcoutsrc_Get(void *pBtcContext, u8 getType, void *pOutBuf) case BTC_GET_U4_WIFI_TRAFFIC_DIRECTION: { struct rt_link_detect_t *plinkinfo; - plinkinfo = &padapter->mlmepriv.LinkDetectInfo; - if (plinkinfo->NumTxOkInPeriod > plinkinfo->NumRxOkInPeriod) + plinkinfo = &padapter->mlmepriv.link_detect_info; + + if (plinkinfo->num_tx_ok_in_period > plinkinfo->num_rx_ok_in_period) *pU4Tmp = BTC_WIFI_TRAFFIC_TX; else *pU4Tmp = BTC_WIFI_TRAFFIC_RX; diff --git a/drivers/staging/rtl8723bs/hal/hal_com.c b/drivers/staging/rtl8723bs/hal/hal_com.c index 31b3e880ae6a..728a2171fbcb 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com.c +++ b/drivers/staging/rtl8723bs/hal/hal_com.c @@ -31,44 +31,44 @@ void rtw_hal_data_deinit(struct adapter *padapter) } -void dump_chip_info(struct hal_version ChipVersion) +void dump_chip_info(struct hal_version chip_version) { char buf[128]; size_t cnt = 0; cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "Chip Version Info: CHIP_8723B_%s_", - IS_NORMAL_CHIP(ChipVersion) ? "Normal_Chip" : "Test_Chip"); + IS_NORMAL_CHIP(chip_version) ? "Normal_Chip" : "Test_Chip"); - if (IS_CHIP_VENDOR_TSMC(ChipVersion)) + if (IS_CHIP_VENDOR_TSMC(chip_version)) cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "TSMC_"); - else if (IS_CHIP_VENDOR_UMC(ChipVersion)) + else if (IS_CHIP_VENDOR_UMC(chip_version)) cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "UMC_"); - else if (IS_CHIP_VENDOR_SMIC(ChipVersion)) + else if (IS_CHIP_VENDOR_SMIC(chip_version)) cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "SMIC_"); - if (IS_A_CUT(ChipVersion)) + if (IS_A_CUT(chip_version)) cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "A_CUT_"); - else if (IS_B_CUT(ChipVersion)) + else if (IS_B_CUT(chip_version)) cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "B_CUT_"); - else if (IS_C_CUT(ChipVersion)) + else if (IS_C_CUT(chip_version)) cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "C_CUT_"); - else if (IS_D_CUT(ChipVersion)) + else if (IS_D_CUT(chip_version)) cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "D_CUT_"); - else if (IS_E_CUT(ChipVersion)) + else if (IS_E_CUT(chip_version)) cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "E_CUT_"); - else if (IS_I_CUT(ChipVersion)) + else if (IS_I_CUT(chip_version)) cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "I_CUT_"); - else if (IS_J_CUT(ChipVersion)) + else if (IS_J_CUT(chip_version)) cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "J_CUT_"); - else if (IS_K_CUT(ChipVersion)) + else if (IS_K_CUT(chip_version)) cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "K_CUT_"); else cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, - "UNKNOWN_CUT(%d)_", ChipVersion.CUTVersion); + "UNKNOWN_CUT(%d)_", chip_version.CUTVersion); cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "1T1R_"); - cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "RomVer(%d)\n", ChipVersion.ROMVer); + cnt += scnprintf(buf + cnt, sizeof(buf) - cnt, "RomVer(%d)\n", chip_version.ROMVer); } @@ -86,7 +86,7 @@ void dump_chip_info(struct hal_version ChipVersion) * BIT[6:0] Channel Plan *sw_channel_plan channel plan from SW (registry/module param) *def_channel_plan channel plan used when HW/SW both invalid - *AutoLoadFail efuse autoload fail or not + *auto_load_fail efuse autoload fail or not * * Return: *Final channel plan decision @@ -97,7 +97,7 @@ u8 hal_com_config_channel_plan( u8 hw_channel_plan, u8 sw_channel_plan, u8 def_channel_plan, - bool AutoLoadFail + bool auto_load_fail ) { struct hal_com_data *pHalData; @@ -108,9 +108,9 @@ u8 hal_com_config_channel_plan( chnlPlan = def_channel_plan; if (0xFF == hw_channel_plan) - AutoLoadFail = true; + auto_load_fail = true; - if (!AutoLoadFail) { + if (!auto_load_fail) { u8 hw_chnlPlan; hw_chnlPlan = hw_channel_plan & (~EEPROM_CHANNEL_PLAN_BY_HW_MASK); @@ -616,6 +616,7 @@ void SetHwReg(struct adapter *adapter, u8 variable, u8 *val) case HW_VAR_DM_FUNC_SET: if (*((u32 *)val) == DYNAMIC_ALL_FUNC_ENABLE) { struct dm_priv *dm = &hal_data->dmpriv; + dm->DMFlag = dm->InitDMFlag; odm->SupportAbility = dm->InitODMFlag; } else { @@ -727,6 +728,7 @@ void SetHalODMVar( case HAL_ODM_STA_INFO: { struct sta_info *psta = pValue1; + if (bSet) { ODM_CmnInfoPtrArrayHook(podmpriv, ODM_CMNINFO_STA_STATUS, psta->mac_id, psta); } else { @@ -750,18 +752,6 @@ void SetHalODMVar( } -bool eqNByte(u8 *str1, u8 *str2, u32 num) -{ - if (num == 0) - return false; - while (num > 0) { - num--; - if (str1[num] != str2[num]) - return false; - } - return true; -} - bool GetU1ByteIntegerFromStringInDecimal(char *Str, u8 *pInt) { u16 i = 0; diff --git a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c index dc2da49e6738..bdd595a99b98 100644 --- a/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/hal_com_phycfg.c @@ -8,6 +8,7 @@ #include #include #include +#include u8 PHY_GetTxPowerByRateBase(struct adapter *Adapter, u8 RfPath, enum rate_section RateSection) @@ -423,6 +424,7 @@ void PHY_SetTxPowerIndexByRateSection( if (RateSection == CCK) { u8 cckRates[] = {MGN_1M, MGN_2M, MGN_5_5M, MGN_11M}; + PHY_SetTxPowerIndexByRateArray(padapter, RFPath, pHalData->CurrentChannelBW, Channel, cckRates, @@ -430,6 +432,7 @@ void PHY_SetTxPowerIndexByRateSection( } else if (RateSection == OFDM) { u8 ofdmRates[] = {MGN_6M, MGN_9M, MGN_12M, MGN_18M, MGN_24M, MGN_36M, MGN_48M, MGN_54M}; + PHY_SetTxPowerIndexByRateArray(padapter, RFPath, pHalData->CurrentChannelBW, Channel, ofdmRates, @@ -437,6 +440,7 @@ void PHY_SetTxPowerIndexByRateSection( } else if (RateSection == HT_MCS0_MCS7) { u8 htRates1T[] = {MGN_MCS0, MGN_MCS1, MGN_MCS2, MGN_MCS3, MGN_MCS4, MGN_MCS5, MGN_MCS6, MGN_MCS7}; + PHY_SetTxPowerIndexByRateArray(padapter, RFPath, pHalData->CurrentChannelBW, Channel, htRates1T, @@ -500,6 +504,7 @@ s8 PHY_GetTxPowerTrackingOffset(struct adapter *padapter, u8 RFPath, u8 Rate) u8 PHY_GetRateIndexOfTxPowerByRate(u8 Rate) { u8 index = 0; + switch (Rate) { case MGN_1M: index = 0; @@ -819,27 +824,27 @@ void PHY_SetTxPowerLimit( powerLimit = powerLimit > MAX_POWER_INDEX ? MAX_POWER_INDEX : powerLimit; - if (eqNByte(Regulation, (u8 *)("FCC"), 3)) + if (strcmp(Regulation, "FCC") == 0) regulation = 0; - else if (eqNByte(Regulation, (u8 *)("MKK"), 3)) + else if (strcmp(Regulation, "MKK") == 0) regulation = 1; - else if (eqNByte(Regulation, (u8 *)("ETSI"), 4)) + else if (strcmp(Regulation, "ETSI") == 0) regulation = 2; - else if (eqNByte(Regulation, (u8 *)("WW13"), 4)) + else if (strcmp(Regulation, "WW13") == 0) regulation = 3; - if (eqNByte(RateSection, (u8 *)("CCK"), 3) && eqNByte(RfPath, (u8 *)("1T"), 2)) + if (strcmp(RateSection, "CCK") == 0 && strcmp(RfPath, "1T") == 0) rateSection = 0; - else if (eqNByte(RateSection, (u8 *)("OFDM"), 4) && eqNByte(RfPath, (u8 *)("1T"), 2)) + else if (strcmp(RateSection, "OFDM") == 0 && strcmp(RfPath, "1T") == 0) rateSection = 1; - else if (eqNByte(RateSection, (u8 *)("HT"), 2) && eqNByte(RfPath, (u8 *)("1T"), 2)) + else if (strcmp(RateSection, "HT") == 0 && strcmp(RfPath, "1T") == 0) rateSection = 2; else return; - if (eqNByte(Bandwidth, (u8 *)("20M"), 3)) + if (strcmp(Bandwidth, "20M") == 0) bandwidth = 0; - else if (eqNByte(Bandwidth, (u8 *)("40M"), 3)) + else if (strcmp(Bandwidth, "40M") == 0) bandwidth = 1; channelIndex = phy_GetChannelIndexOfTxPowerLimit(channel); @@ -856,6 +861,7 @@ void PHY_SetTxPowerLimit( void Hal_ChannelPlanToRegulation(struct adapter *Adapter, u16 ChannelPlan) { struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); + pHalData->Regulation2_4G = TXPWR_LMT_WW; switch (ChannelPlan) { diff --git a/drivers/staging/rtl8723bs/hal/odm.c b/drivers/staging/rtl8723bs/hal/odm.c index 639b6da2302b..3dffa7620768 100644 --- a/drivers/staging/rtl8723bs/hal/odm.c +++ b/drivers/staging/rtl8723bs/hal/odm.c @@ -131,8 +131,8 @@ u8 CCKSwingTable_Ch14_New[CCK_TABLE_SIZE][8] = { static void odm_CommonInfoSelfInit(struct dm_odm_t *pDM_Odm) { - pDM_Odm->bCckHighPower = (bool) PHY_QueryBBReg(pDM_Odm->Adapter, ODM_REG(CCK_RPT_FORMAT, pDM_Odm), ODM_BIT(CCK_RPT_FORMAT, pDM_Odm)); - pDM_Odm->RFPathRxEnable = (u8) PHY_QueryBBReg(pDM_Odm->Adapter, ODM_REG(BB_RX_PATH, pDM_Odm), ODM_BIT(BB_RX_PATH, pDM_Odm)); + pDM_Odm->bCckHighPower = (bool) PHY_QueryBBReg(pDM_Odm->Adapter, ODM_REG(CCK_RPT_FORMAT), ODM_BIT(CCK_RPT_FORMAT)); + pDM_Odm->RFPathRxEnable = (u8) PHY_QueryBBReg(pDM_Odm->Adapter, ODM_REG(BB_RX_PATH), ODM_BIT(BB_RX_PATH)); pDM_Odm->TxRate = 0xFF; } diff --git a/drivers/staging/rtl8723bs/hal/odm.h b/drivers/staging/rtl8723bs/hal/odm.h index 1c929d88e596..aa4e36b6f2b4 100644 --- a/drivers/staging/rtl8723bs/hal/odm.h +++ b/drivers/staging/rtl8723bs/hal/odm.h @@ -202,14 +202,7 @@ struct odm_rate_adaptive { /* */ /* Declare for common info */ /* */ -#define MAX_PATH_NUM_92CS 2 -#define MAX_PATH_NUM_8188E 1 -#define MAX_PATH_NUM_8192E 2 #define MAX_PATH_NUM_8723B 1 -#define MAX_PATH_NUM_8812A 2 -#define MAX_PATH_NUM_8821A 1 -#define MAX_PATH_NUM_8814A 4 -#define MAX_PATH_NUM_8822B 2 #define IQK_THRESHOLD 8 #define DPK_THRESHOLD 4 @@ -657,10 +650,6 @@ struct ant_detected_info { /* 2011/09/22 MH Copy from SD4 defined structure. We use to support PHY DM integration. */ /* */ struct dm_odm_t { /* DM_Out_Source_Dynamic_Mechanism_Structure */ - /* struct timer_list FastAntTrainingTimer; */ - /* */ - /* Add for different team use temporarily */ - /* */ struct adapter *Adapter; /* For CE/NIC team */ /* WHen you use Adapter or priv pointer, you must make sure the pointer is ready. */ bool odm_ready; @@ -680,17 +669,6 @@ struct dm_odm_t { /* DM_Out_Source_Dynamic_Mechanism_Structure */ u8 ControlChannel; /* ODM HANDLE, DRIVER NEEDS NOT TO HOOK------ */ -/* REMOVED COMMON INFO---------- */ - /* u8 PseudoMacPhyMode; */ - /* bool *BTCoexist; */ - /* bool PseudoBtCoexist; */ - /* u8 OPMode; */ - /* bool bAPMode; */ - /* bool bClientMode; */ - /* bool bAdHocMode; */ - /* bool bSlaveOfDMSP; */ -/* REMOVED COMMON INFO---------- */ - /* 1 COMMON INFORMATION */ /* */ @@ -773,7 +751,6 @@ struct dm_odm_t { /* DM_Out_Source_Dynamic_Mechanism_Structure */ u8 *pAntennaTest; bool *pbNet_closed; u8 *mp_mode; - /* u8 *pAidMap; */ u8 *pu1ForcedIgiLb; /* For 8723B IQK----------- */ bool *pIs1Antenna; @@ -872,18 +849,9 @@ struct dm_odm_t { /* DM_Out_Source_Dynamic_Mechanism_Structure */ /* Latest packet phy info (ODM write) */ struct odm_phy_dbg_info PhyDbgInfo; - /* PHY_INFO_88E PhyInfo; */ /* Latest packet phy info (ODM write) */ struct odm_mac_status_info *pMacInfo; - /* MAC_INFO_88E MacInfo; */ - - /* Different Team independent structure?? */ - - /* */ - /* TX_RTP_CMN TX_retrpo; */ - /* TX_RTP_88E TX_retrpo; */ - /* TX_RTP_8195 TX_retrpo; */ /* */ /* ODM Structure */ @@ -909,15 +877,6 @@ struct dm_odm_t { /* DM_Out_Source_Dynamic_Mechanism_Structure */ /* */ /* common */ - /* u8 DM_Type; */ - /* u8 PSD_Report_RXHP[80]; Add By Gary */ - /* u8 PSD_func_flag; Add By Gary */ - /* for DIG */ - /* u8 bDMInitialGainEnable; */ - /* u8 binitialized; for dm_initial_gain_Multi_STA use. */ - /* for Antenna diversity */ - /* u8 AntDivCfg; 0:OFF , 1:ON, 2:by efuse */ - /* PSTA_INFO_T RSSI_target; */ bool *pbDriverStopped; bool *pbDriverIsGoingToPnpSetPowerSleep; @@ -985,7 +944,7 @@ struct dm_odm_t { /* DM_Out_Source_Dynamic_Mechanism_Structure */ #endif }; - enum odm_rf_content { +enum odm_rf_content { odm_radioa_txt = 0x1000, odm_radiob_txt = 0x1001, odm_radioc_txt = 0x1002, diff --git a/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c b/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c index 166af5f6c9e0..0eaedd8f6469 100644 --- a/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c +++ b/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c @@ -47,8 +47,8 @@ static void odm_SetATCStatus(void *pDM_VOID, bool ATCStatus) PHY_SetBBReg( pDM_Odm->Adapter, - ODM_REG(BB_ATC, pDM_Odm), - ODM_BIT(BB_ATC, pDM_Odm), + ODM_REG(BB_ATC), + ODM_BIT(BB_ATC), ATCStatus ); pCfoTrack->bATCStatus = ATCStatus; @@ -61,8 +61,8 @@ static bool odm_GetATCStatus(void *pDM_VOID) ATCStatus = (bool)PHY_QueryBBReg( pDM_Odm->Adapter, - ODM_REG(BB_ATC, pDM_Odm), - ODM_BIT(BB_ATC, pDM_Odm) + ODM_REG(BB_ATC), + ODM_BIT(BB_ATC) ); return ATCStatus; } diff --git a/drivers/staging/rtl8723bs/hal/odm_DIG.c b/drivers/staging/rtl8723bs/hal/odm_DIG.c index f10427abd849..a31c8368f9d9 100644 --- a/drivers/staging/rtl8723bs/hal/odm_DIG.c +++ b/drivers/staging/rtl8723bs/hal/odm_DIG.c @@ -220,9 +220,8 @@ void odm_Adaptivity(void *pDM_VOID, u8 IGI) s8 Diff, IGI_target; bool EDCCA_State = false; - if (!(pDM_Odm->SupportAbility & ODM_BB_ADAPTIVITY)) { + if (!(pDM_Odm->SupportAbility & ODM_BB_ADAPTIVITY)) return; - } if (*pDM_Odm->pBandWidth == ODM_BW20M) /* CHANNEL_WIDTH_20 */ IGI_target = pDM_Odm->IGI_Base; @@ -286,23 +285,21 @@ void ODM_Write_DIG(void *pDM_VOID, u8 CurrentIGI) struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID; struct dig_t *pDM_DigTable = &pDM_Odm->DM_DigTable; - if (pDM_DigTable->bStopDIG) { + if (pDM_DigTable->bStopDIG) return; - } if (pDM_DigTable->CurIGValue != CurrentIGI) { /* 1 Check initial gain by upper bound */ if (!pDM_DigTable->bPSDInProgress) { - if (CurrentIGI > pDM_DigTable->rx_gain_range_max) { + if (CurrentIGI > pDM_DigTable->rx_gain_range_max) CurrentIGI = pDM_DigTable->rx_gain_range_max; - } } /* 1 Set IGI value */ - PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG(IGI_A, pDM_Odm), ODM_BIT(IGI, pDM_Odm), CurrentIGI); + PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG(IGI_A), ODM_BIT(IGI), CurrentIGI); - PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG(IGI_B, pDM_Odm), ODM_BIT(IGI, pDM_Odm), CurrentIGI); + PHY_SetBBReg(pDM_Odm->Adapter, ODM_REG(IGI_B), ODM_BIT(IGI), CurrentIGI); pDM_DigTable->CurIGValue = CurrentIGI; } @@ -314,24 +311,20 @@ bool odm_DigAbort(void *pDM_VOID) struct dm_odm_t *pDM_Odm = (struct dm_odm_t *)pDM_VOID; /* SupportAbility */ - if (!(pDM_Odm->SupportAbility & ODM_BB_FA_CNT)) { + if (!(pDM_Odm->SupportAbility & ODM_BB_FA_CNT)) return true; - } /* SupportAbility */ - if (!(pDM_Odm->SupportAbility & ODM_BB_DIG)) { + if (!(pDM_Odm->SupportAbility & ODM_BB_DIG)) return true; - } /* ScanInProcess */ - if (*(pDM_Odm->pbScanInProcess)) { + if (*(pDM_Odm->pbScanInProcess)) return true; - } /* add by Neil Chen to avoid PSD is processing */ - if (pDM_Odm->bDMInitialGainEnable == false) { + if (pDM_Odm->bDMInitialGainEnable == false) return true; - } return false; } @@ -343,7 +336,7 @@ void odm_DIGInit(void *pDM_VOID) pDM_DigTable->bStopDIG = false; pDM_DigTable->bPSDInProgress = false; - pDM_DigTable->CurIGValue = (u8) PHY_QueryBBReg(pDM_Odm->Adapter, ODM_REG(IGI_A, pDM_Odm), ODM_BIT(IGI, pDM_Odm)); + pDM_DigTable->CurIGValue = (u8) PHY_QueryBBReg(pDM_Odm->Adapter, ODM_REG(IGI_A), ODM_BIT(IGI)); pDM_DigTable->RssiLowThresh = DM_DIG_THRESH_LOW; pDM_DigTable->RssiHighThresh = DM_DIG_THRESH_HIGH; pDM_DigTable->FALowThresh = DMfalseALARM_THRESH_LOW; @@ -413,9 +406,9 @@ void odm_DIG(void *pDM_VOID) if (pDM_Odm->bLinked && bPerformance) { /* 2 Modify DIG upper bound */ /* 4 Modify DIG upper bound for 92E, 8723A\B, 8821 & 8812 BT */ - if (pDM_Odm->bBtLimitedDig == 1) { + if (pDM_Odm->bBtLimitedDig == 1) offset = 10; - } else + else offset = 15; if ((pDM_Odm->RSSI_Min + offset) > dm_dig_max) @@ -475,9 +468,8 @@ void odm_DIG(void *pDM_VOID) } /* 2 Abnormal lower bound case */ - if (pDM_DigTable->rx_gain_range_min > pDM_DigTable->rx_gain_range_max) { + if (pDM_DigTable->rx_gain_range_min > pDM_DigTable->rx_gain_range_max) pDM_DigTable->rx_gain_range_min = pDM_DigTable->rx_gain_range_max; - } /* 1 False alarm threshold decision */ @@ -814,7 +806,7 @@ void ODM_Write_CCK_CCA_Thres(void *pDM_VOID, u8 CurCCK_CCAThres) /* modify by Guo.Mingzhi 2012-01-03 */ if (pDM_DigTable->CurCCK_CCAThres != CurCCK_CCAThres) - rtw_write8(pDM_Odm->Adapter, ODM_REG(CCK_CCA, pDM_Odm), CurCCK_CCAThres); + rtw_write8(pDM_Odm->Adapter, ODM_REG(CCK_CCA), CurCCK_CCAThres); pDM_DigTable->PreCCK_CCAThres = pDM_DigTable->CurCCK_CCAThres; pDM_DigTable->CurCCK_CCAThres = CurCCK_CCAThres; diff --git a/drivers/staging/rtl8723bs/hal/odm_RegConfig8723B.c b/drivers/staging/rtl8723bs/hal/odm_RegConfig8723B.c index 1df42069bd5c..b0ca46aec1a5 100644 --- a/drivers/staging/rtl8723bs/hal/odm_RegConfig8723B.c +++ b/drivers/staging/rtl8723bs/hal/odm_RegConfig8723B.c @@ -125,9 +125,8 @@ void odm_ConfigBB_PHY_REG_PG_8723B( { if (Addr == 0xfe || Addr == 0xffe) msleep(50); - else { + else PHY_StoreTxPowerByRate(pDM_Odm->Adapter, RfPath, Addr, Bitmask, Data); - } } void odm_ConfigBB_PHY_8723B( @@ -149,9 +148,8 @@ void odm_ConfigBB_PHY_8723B( udelay(5); else if (Addr == 0xf9) udelay(1); - else { + else PHY_SetBBReg(pDM_Odm->Adapter, Addr, Bitmask, Data); - } /* Add 1us delay between BB/RF register setting. */ udelay(1); diff --git a/drivers/staging/rtl8723bs/hal/odm_interface.h b/drivers/staging/rtl8723bs/hal/odm_interface.h index d19347b02890..400473ca58ca 100644 --- a/drivers/staging/rtl8723bs/hal/odm_interface.h +++ b/drivers/staging/rtl8723bs/hal/odm_interface.h @@ -23,18 +23,18 @@ #define ODM_REG_DIG_11N 0xC50 #define ODM_REG_DIG_11AC 0xDDD -ODM_REG(DIG, _pDM_Odm) +ODM_REG(DIG) =====================================*/ #define _reg_11N(_name) ODM_REG_##_name##_11N #define _bit_11N(_name) ODM_BIT_##_name##_11N -#define _cat(_name, _ic_type, _func) _func##_11N(_name) +#define _cat(_name, _func) _func##_11N(_name) /* _name: name of register or bit. */ -/* Example: "ODM_REG(R_A_AGC_CORE1, pDM_Odm)" */ -/* gets "ODM_R_A_AGC_CORE1" or "ODM_R_A_AGC_CORE1_8192C", depends on SupportICType. */ -#define ODM_REG(_name, _pDM_Odm) _cat(_name, _pDM_Odm->SupportICType, _reg) -#define ODM_BIT(_name, _pDM_Odm) _cat(_name, _pDM_Odm->SupportICType, _bit) +/* Example: "ODM_REG(R_A_AGC_CORE1)" */ +/* gets "ODM_R_A_AGC_CORE1" or "ODM_R_A_AGC_CORE1_8192C". */ +#define ODM_REG(_name) _cat(_name, _reg) +#define ODM_BIT(_name) _cat(_name, _bit) #endif /* __ODM_INTERFACE_H__ */ diff --git a/drivers/staging/rtl8723bs/hal/odm_types.h b/drivers/staging/rtl8723bs/hal/odm_types.h index 8168dc14e879..0da662e1493a 100644 --- a/drivers/staging/rtl8723bs/hal/odm_types.h +++ b/drivers/staging/rtl8723bs/hal/odm_types.h @@ -36,10 +36,6 @@ enum hal_status { #define STA_INFO_T struct sta_info #define PSTA_INFO_T struct sta_info * - #define SET_TX_DESC_ANTSEL_A_88E(__pTxDesc, __Value) SET_BITS_TO_LE_4BYTE(__pTxDesc+8, 24, 1, __Value) - #define SET_TX_DESC_ANTSEL_B_88E(__pTxDesc, __Value) SET_BITS_TO_LE_4BYTE(__pTxDesc+8, 25, 1, __Value) - #define SET_TX_DESC_ANTSEL_C_88E(__pTxDesc, __Value) SET_BITS_TO_LE_4BYTE(__pTxDesc+28, 29, 1, __Value) - /* define useless flag to avoid compile warning */ #define USE_WORKITEM 0 #define FPGA_TWO_MAC_VERIFICATION 0 diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c b/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c index af6cdda8238d..12416e499ac3 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c @@ -672,6 +672,7 @@ void rtl8723b_download_rsvd_page(struct adapter *padapter, u8 mstatus) if (padapter->bSurpriseRemoved || padapter->bDriverStopped) { } else { struct pwrctrl_priv *pwrctl = adapter_to_pwrctl(padapter); + pwrctl->fw_psmode_iface_id = padapter->iface_id; } @@ -935,6 +936,7 @@ void rtl8723b_download_BTCoex_AP_mode_rsvd_page(struct adapter *padapter) if (bcn_valid) { struct pwrctrl_priv *pwrctl = adapter_to_pwrctl(padapter); + pwrctl->fw_psmode_iface_id = padapter->iface_id; } diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c index 8d259820f103..e794fe3caf9d 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_hal_init.c @@ -1185,12 +1185,12 @@ void Hal_InitPGData(struct adapter *padapter, u8 *PROMContent) if (!pEEPROM->EepromOrEfuse) { /* Read EFUSE real map to shadow. */ EFUSE_ShadowMapUpdate(padapter, EFUSE_WIFI); - memcpy((void *)PROMContent, (void *)pEEPROM->efuse_eeprom_data, HWSET_MAX_SIZE_8723B); + memcpy(PROMContent, pEEPROM->efuse_eeprom_data, HWSET_MAX_SIZE_8723B); } } else {/* autoload fail */ if (!pEEPROM->EepromOrEfuse) EFUSE_ShadowMapUpdate(padapter, EFUSE_WIFI); - memcpy((void *)PROMContent, (void *)pEEPROM->efuse_eeprom_data, HWSET_MAX_SIZE_8723B); + memcpy(PROMContent, pEEPROM->efuse_eeprom_data, HWSET_MAX_SIZE_8723B); } } @@ -1631,6 +1631,7 @@ static void rtl8723b_cal_txdesc_chksum(struct tx_desc *ptxdesc) static u8 fill_txdesc_sectype(struct pkt_attrib *pattrib) { u8 sectype = 0; + if ((pattrib->encrypt > 0) && !pattrib->bswenc) { switch (pattrib->encrypt) { /* SEC_TYPE */ @@ -2031,6 +2032,7 @@ static void hw_var_set_bcn_func(struct adapter *padapter, u8 variable, u8 *val) rtw_write8(padapter, bcn_ctrl_reg, (EN_BCN_FUNCTION | EN_TXBCN_RPT)); else { u8 val8; + val8 = rtw_read8(padapter, bcn_ctrl_reg); val8 &= ~(EN_BCN_FUNCTION | EN_TXBCN_RPT); @@ -2225,6 +2227,7 @@ s32 c2h_id_filter_ccx_8723b(u8 *buf) { struct c2h_evt_hdr_88xx *c2h_evt = (struct c2h_evt_hdr_88xx *)buf; s32 ret = false; + if (c2h_evt->id == C2H_CCX_TX_RPT) ret = true; @@ -2314,6 +2317,7 @@ void C2HPacketHandler_8723B(struct adapter *padapter, u8 *pbuffer, u16 length) { struct c2h_evt_hdr_t C2hEvent; u8 *tmpBuf = NULL; + C2hEvent.CmdID = pbuffer[0]; C2hEvent.CmdSeq = pbuffer[1]; C2hEvent.CmdLen = length-2; @@ -2397,6 +2401,7 @@ void SetHwReg8723B(struct adapter *padapter, u8 variable, u8 *val) case HW_VAR_CHECK_BSSID: { u32 val32; + val32 = rtw_read32(padapter, REG_RCR); if (*val) val32 |= RCR_CBSSID_DATA|RCR_CBSSID_BCN; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c index 6d5e531505f9..7fac1c2ba8e0 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_phycfg.c @@ -575,6 +575,7 @@ static void phy_SetRegBW_8723B( ) { u16 RegRfMod_BW, u2tmp = 0; + RegRfMod_BW = rtw_read16(Adapter, REG_TRXPTCL_CTL_8723B); switch (CurrentBW) { diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c index 5faac9f28b02..cab4707091e2 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_recv.c @@ -143,6 +143,7 @@ static void update_recvframe_phyinfo(union recv_frame *precvframe, } } else if (pkt_info.to_self || pkt_info.is_beacon) { u32 adhoc_state = WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE; + if (check_fwstate(&padapter->mlmepriv, adhoc_state)) if (psta) precvframe->u.hdr.psta = psta; diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c index a1f2cbf2cf55..f50726d2ed0c 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c @@ -202,7 +202,7 @@ static s32 xmit_xmitframes(struct adapter *padapter, struct xmit_priv *pxmitpriv if ( (check_pending_xmitbuf(pxmitpriv)) && - (padapter->mlmepriv.LinkDetectInfo.bHigherBusyTxTraffic) + (padapter->mlmepriv.link_detect_info.higher_busy_tx_traffic) ) { if ((phwxmit->accnt > 0) && (phwxmit->accnt < 5)) { err = -2; @@ -240,6 +240,7 @@ static s32 xmit_xmitframes(struct adapter *padapter, struct xmit_priv *pxmitpriv if (pxmitbuf->len > 0 && pxmitbuf->priv_data) { struct xmit_frame *pframe; + pframe = (struct xmit_frame *)pxmitbuf->priv_data; pframe->agg_num = k; pxmitbuf->agg_num = k; @@ -324,6 +325,7 @@ static s32 xmit_xmitframes(struct adapter *padapter, struct xmit_priv *pxmitpriv if (pxmitbuf) { if (pxmitbuf->len > 0) { struct xmit_frame *pframe; + pframe = (struct xmit_frame *)pxmitbuf->priv_data; pframe->agg_num = k; pxmitbuf->agg_num = k; @@ -482,7 +484,7 @@ s32 rtl8723bs_hal_xmit( (pxmitframe->attrib.ether_type != 0x888e) && (pxmitframe->attrib.dhcp_pkt != 1) ) { - if (padapter->mlmepriv.LinkDetectInfo.bBusyTraffic) + if (padapter->mlmepriv.link_detect_info.busy_traffic) rtw_issue_addbareq_cmd(padapter, pxmitframe); } diff --git a/drivers/staging/rtl8723bs/hal/sdio_halinit.c b/drivers/staging/rtl8723bs/hal/sdio_halinit.c index 0fa1b22fdf9a..f2f73c65a636 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_halinit.c +++ b/drivers/staging/rtl8723bs/hal/sdio_halinit.c @@ -30,7 +30,7 @@ static u8 CardEnable(struct adapter *padapter) ret = HalPwrSeqCmdParsing(padapter, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, rtl8723B_card_enable_flow); if (ret == _SUCCESS) { - u8 bMacPwrCtrlOn = true; + bMacPwrCtrlOn = true; rtw_hal_set_hwreg(padapter, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn); } } else @@ -214,6 +214,7 @@ static void _InitNormalChipOneOutEpPriority(struct adapter *Adapter) struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); u16 value = 0; + switch (pHalData->OutEpQueueSel) { case TX_SELE_HQ: value = QUEUE_HIGH; @@ -341,6 +342,7 @@ static void _InitTransferPageSize(struct adapter *padapter) /* Tx page size is always 128. */ u8 value8; + value8 = _PSRX(PBP_128) | _PSTX(PBP_128); rtw_write8(padapter, REG_PBP, value8); } @@ -388,7 +390,7 @@ static void _InitWMACSetting(struct adapter *padapter) /* 2010.09.08 hpfan */ /* Since ADF is removed from RCR, ps-poll will not be indicate to driver, */ - /* RxFilterMap should mask ps-poll to gurantee AP mode can rx ps-poll. */ + /* RxFilterMap should mask ps-poll to guarantee AP mode can rx ps-poll. */ value16 = 0x400; rtw_write16(padapter, REG_RXFLTMAP1, value16); @@ -606,7 +608,7 @@ u32 rtl8723bs_hal_init(struct adapter *padapter) cpwm_orig = 0; rtw_hal_get_hwreg(padapter, HW_VAR_CPWM, &cpwm_orig); - /* ser rpwm */ + /* set rpwm */ val8 = rtw_read8(padapter, SDIO_LOCAL_BASE | SDIO_REG_HRPWM1); val8 &= 0x80; val8 += 0x80; @@ -901,7 +903,7 @@ u32 rtl8723bs_hal_deinit(struct adapter *padapter) } while (cnt < 100 && (val8 != 0)); /* H2C done, enter 32k */ if (val8 == 0) { - /* ser rpwm to enter 32k */ + /* set rpwm to enter 32k */ val8 = rtw_read8(padapter, SDIO_LOCAL_BASE | SDIO_REG_HRPWM1); val8 += 0x80; val8 |= BIT(0); @@ -1147,6 +1149,7 @@ void SetHwReg8723BS(struct adapter *padapter, u8 variable, u8 *val) case HW_VAR_SET_REQ_FW_PS: { u8 req_fw_ps = 0; + req_fw_ps = rtw_read8(padapter, 0x8f); req_fw_ps |= 0x10; rtw_write8(padapter, 0x8f, req_fw_ps); diff --git a/drivers/staging/rtl8723bs/hal/sdio_ops.c b/drivers/staging/rtl8723bs/hal/sdio_ops.c index c9cb20c61a2b..8cece69dbf79 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_ops.c +++ b/drivers/staging/rtl8723bs/hal/sdio_ops.c @@ -13,12 +13,8 @@ /* */ /* Creadted by Roger, 2011.01.31. */ /* */ -static void hal_sdio_get_cmd_addr_8723b( - struct adapter *adapter, - u8 device_id, - u32 addr, - u32 *cmdaddr -) +static void hal_sdio_get_cmd_addr_8723b(struct adapter *adapter, u8 device_id, + u32 addr, u32 *cmdaddr) { switch (device_id) { case SDIO_LOCAL_DEVICE_ID: @@ -583,12 +579,10 @@ s32 sdio_local_write( ) return sd_cmd52_write(intfhdl, addr, cnt, buf); - tmpbuf = kmalloc(cnt, GFP_ATOMIC); + tmpbuf = kmemdup(buf, cnt, GFP_ATOMIC); if (!tmpbuf) return -ENOMEM; - memcpy(tmpbuf, buf, cnt); - err = sd_write(intfhdl, addr, cnt, tmpbuf); kfree(tmpbuf); diff --git a/drivers/staging/rtl8723bs/include/basic_types.h b/drivers/staging/rtl8723bs/include/basic_types.h index 8adb95f9f1e5..b6b1afaa9ab8 100644 --- a/drivers/staging/rtl8723bs/include/basic_types.h +++ b/drivers/staging/rtl8723bs/include/basic_types.h @@ -7,7 +7,6 @@ #ifndef __BASIC_TYPES_H__ #define __BASIC_TYPES_H__ - #define SUCCESS 0 #define FAIL (-1) @@ -42,31 +41,6 @@ #define EF4BYTE(_val) \ (le32_to_cpu(_val)) -/* Read data from memory */ -#define READEF1BYTE(_ptr) \ - EF1BYTE(*((u8 *)(_ptr))) -/* Read le16 data from memory and convert to host ordering */ -#define READEF2BYTE(_ptr) \ - EF2BYTE(*(_ptr)) -#define READEF4BYTE(_ptr) \ - EF4BYTE(*(_ptr)) - -/* Write data to memory */ -#define WRITEEF1BYTE(_ptr, _val) \ - do { \ - (*((u8 *)(_ptr))) = EF1BYTE(_val); \ - } while (0) -/* Write le data to memory in host ordering */ -#define WRITEEF2BYTE(_ptr, _val) \ - do { \ - (*((u16 *)(_ptr))) = EF2BYTE(_val); \ - } while (0) - -#define WRITEEF4BYTE(_ptr, _val) \ - do { \ - (*((u32 *)(_ptr))) = EF2BYTE(_val); \ - } while (0) - /* * Create a bit mask * Examples: @@ -154,25 +128,25 @@ * Set subfield of little-endian 4-byte value to specified value. */ #define SET_BITS_TO_LE_4BYTE(__pstart, __bitoffset, __bitlen, __val) \ - *((u32 *)(__pstart)) = \ + (*((u32 *)(__pstart)) = \ ( \ LE_BITS_CLEARED_TO_4BYTE(__pstart, __bitoffset, __bitlen) | \ ((((u32)__val) & BIT_LEN_MASK_32(__bitlen)) << (__bitoffset)) \ - ) + )) #define SET_BITS_TO_LE_2BYTE(__pstart, __bitoffset, __bitlen, __val) \ - *((u16 *)(__pstart)) = \ + (*((u16 *)(__pstart)) = \ ( \ LE_BITS_CLEARED_TO_2BYTE(__pstart, __bitoffset, __bitlen) | \ ((((u16)__val) & BIT_LEN_MASK_16(__bitlen)) << (__bitoffset)) \ - ) + )) #define SET_BITS_TO_LE_1BYTE(__pstart, __bitoffset, __bitlen, __val) \ - *((u8 *)(__pstart)) = EF1BYTE \ + (*((u8 *)(__pstart)) = EF1BYTE \ ( \ LE_BITS_CLEARED_TO_1BYTE(__pstart, __bitoffset, __bitlen) | \ ((((u8)__val) & BIT_LEN_MASK_8(__bitlen)) << (__bitoffset)) \ - ) + )) #define LE_BITS_CLEARED_TO_1BYTE_8BIT(__pStart, __BitOffset, __BitLen) \ (\ diff --git a/drivers/staging/rtl8723bs/include/hal_com.h b/drivers/staging/rtl8723bs/include/hal_com.h index 74d6c892c401..483f0390addc 100644 --- a/drivers/staging/rtl8723bs/include/hal_com.h +++ b/drivers/staging/rtl8723bs/include/hal_com.h @@ -141,8 +141,6 @@ void rtw_hal_check_rxfifo_full(struct adapter *adapter); u8 GetHalDefVar(struct adapter *adapter, enum hal_def_variable variable, void *value); -bool eqNByte(u8 *str1, u8 *str2, u32 num); - bool GetU1ByteIntegerFromStringInDecimal(char *str, u8 *in); #define HWSET_MAX_SIZE 512 diff --git a/drivers/staging/rtl8723bs/include/hal_data.h b/drivers/staging/rtl8723bs/include/hal_data.h index b87c90f693ec..ff4383e30322 100644 --- a/drivers/staging/rtl8723bs/include/hal_data.h +++ b/drivers/staging/rtl8723bs/include/hal_data.h @@ -385,7 +385,7 @@ struct hal_com_data { struct dm_priv dmpriv; struct dm_odm_t odmpriv; - /* For bluetooth co-existance */ + /* For bluetooth co-existence */ struct bt_coexist bt_coexist; /* Interrupt related register information. */ diff --git a/drivers/staging/rtl8723bs/include/ieee80211.h b/drivers/staging/rtl8723bs/include/ieee80211.h index 0f28c904a714..fbb12fe31a6c 100644 --- a/drivers/staging/rtl8723bs/include/ieee80211.h +++ b/drivers/staging/rtl8723bs/include/ieee80211.h @@ -11,7 +11,6 @@ #define MGMT_QUEUE_NUM 5 -#define ETH_ALEN 6 #define ETH_TYPE_LEN 2 #define PAYLOAD_TYPE_LEN 1 @@ -508,23 +507,6 @@ join_res: #define DEFAULT_FTS 2346 #define IP_ARG(x) (x) -static inline int is_multicast_mac_addr(const u8 *addr) -{ - return ((addr[0] != 0xff) && (0x01 & addr[0])); -} - -static inline int is_broadcast_mac_addr(const u8 *addr) -{ - return ((addr[0] == 0xff) && (addr[1] == 0xff) && (addr[2] == 0xff) && \ - (addr[3] == 0xff) && (addr[4] == 0xff) && (addr[5] == 0xff)); -} - -static inline int is_zero_mac_addr(const u8 *addr) -{ - return ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && \ - (addr[3] == 0x00) && (addr[4] == 0x00) && (addr[5] == 0x00)); -} - #define CFG_IEEE80211_RESERVE_FCS (1<<0) #define CFG_IEEE80211_COMPUTE_FCS (1<<1) @@ -723,11 +705,15 @@ struct rtw_ieee802_11_elems { u8 vht_op_mode_notify_len; }; -enum ParseRes { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 }; +enum parse_result { + PARSE_OK = 0, + PARSE_UNKNOWN = 1, + PARSE_FAILED = -1 +}; -enum ParseRes rtw_ieee802_11_parse_elems(u8 *start, uint len, - struct rtw_ieee802_11_elems *elems, - int show_errors); +enum parse_result rtw_ieee802_11_parse_elems(u8 *start, uint len, + struct rtw_ieee802_11_elems *elems, + int show_errors); u8 *rtw_set_fixed_ie(unsigned char *pbuf, unsigned int len, unsigned char *source, unsigned int *frlen); u8 *rtw_set_ie(u8 *pbuf, signed int index, uint len, u8 *source, uint *frlen); @@ -774,7 +760,7 @@ bool rtw_is_cckrates_included(u8 *rate); bool rtw_is_cckratesonly_included(u8 *rate); -int rtw_check_network_type(unsigned char *rate, int ratelen, int channel); +int rtw_check_network_type(unsigned char *rate, int channel); void rtw_get_bcn_info(struct wlan_network *pnetwork); diff --git a/drivers/staging/rtl8723bs/include/ioctl_cfg80211.h b/drivers/staging/rtl8723bs/include/ioctl_cfg80211.h index 753009b07451..bc62b7285950 100644 --- a/drivers/staging/rtl8723bs/include/ioctl_cfg80211.h +++ b/drivers/staging/rtl8723bs/include/ioctl_cfg80211.h @@ -7,58 +7,6 @@ #ifndef __IOCTL_CFG80211_H__ #define __IOCTL_CFG80211_H__ -struct rtw_wdev_invit_info { - u8 state; /* 0: req, 1:rep */ - u8 peer_mac[ETH_ALEN]; - u8 active; - u8 token; - u8 flags; - u8 status; - u8 req_op_ch; - u8 rsp_op_ch; -}; - -#define rtw_wdev_invit_info_init(invit_info) \ - do { \ - (invit_info)->state = 0xff; \ - memset((invit_info)->peer_mac, 0, ETH_ALEN); \ - (invit_info)->active = 0xff; \ - (invit_info)->token = 0; \ - (invit_info)->flags = 0x00; \ - (invit_info)->status = 0xff; \ - (invit_info)->req_op_ch = 0; \ - (invit_info)->rsp_op_ch = 0; \ - } while (0) - -struct rtw_wdev_nego_info { - u8 state; /* 0: req, 1:rep, 2:conf */ - u8 peer_mac[ETH_ALEN]; - u8 active; - u8 token; - u8 status; - u8 req_intent; - u8 req_op_ch; - u8 req_listen_ch; - u8 rsp_intent; - u8 rsp_op_ch; - u8 conf_op_ch; -}; - -#define rtw_wdev_nego_info_init(nego_info) \ - do { \ - (nego_info)->state = 0xff; \ - memset((nego_info)->peer_mac, 0, ETH_ALEN); \ - (nego_info)->active = 0xff; \ - (nego_info)->token = 0; \ - (nego_info)->status = 0xff; \ - (nego_info)->req_intent = 0xff; \ - (nego_info)->req_op_ch = 0; \ - (nego_info)->req_listen_ch = 0; \ - (nego_info)->rsp_intent = 0xff; \ - (nego_info)->rsp_op_ch = 0; \ - (nego_info)->conf_op_ch = 0; \ - } while (0) - struct rtw_wdev_priv { struct wireless_dev *rtw_wdev; @@ -70,14 +18,6 @@ struct rtw_wdev_priv { struct net_device *pmon_ndev;/* for monitor interface */ char ifname_mon[IFNAMSIZ + 1]; /* interface name for monitor interface */ - u8 p2p_enabled; - - u8 provdisc_req_issued; - - struct rtw_wdev_invit_info invit_info; - struct rtw_wdev_nego_info nego_info; - - u8 bandroid_scan; bool block; bool power_mgmt; }; diff --git a/drivers/staging/rtl8723bs/include/rtl8192c_recv.h b/drivers/staging/rtl8723bs/include/rtl8192c_recv.h index 9664758e21be..e2e9aa03ffdf 100644 --- a/drivers/staging/rtl8723bs/include/rtl8192c_recv.h +++ b/drivers/staging/rtl8723bs/include/rtl8192c_recv.h @@ -7,10 +7,6 @@ #ifndef _RTL8192C_RECV_H_ #define _RTL8192C_RECV_H_ -#define RECV_BLK_SZ 512 -#define RECV_BLK_CNT 16 -#define RECV_BLK_TH RECV_BLK_CNT - #define MAX_RECVBUF_SZ (10240) struct phy_stat { diff --git a/drivers/staging/rtl8723bs/include/rtl8723b_hal.h b/drivers/staging/rtl8723bs/include/rtl8723b_hal.h index 7ec84304a19e..ffd03927841c 100644 --- a/drivers/staging/rtl8723bs/include/rtl8723b_hal.h +++ b/drivers/staging/rtl8723bs/include/rtl8723b_hal.h @@ -236,8 +236,6 @@ int FirmwareDownloadBT(struct adapter *adapter, struct rt_firmware *firmware); void CCX_FwC2HTxRpt_8723b(struct adapter *padapter, u8 *pdata, u8 len); s32 c2h_id_filter_ccx_8723b(u8 *buf); s32 c2h_handler_8723b(struct adapter *padapter, u8 *pC2hEvent); -u8 MRateToHwRate8723B(u8 rate); -u8 HwRateToMRate8723B(u8 rate); void Hal_ReadRFGainOffset(struct adapter *padapter, u8 *hwinfo, bool AutoLoadFail); diff --git a/drivers/staging/rtl8723bs/include/rtw_cmd.h b/drivers/staging/rtl8723bs/include/rtw_cmd.h index cb44119ce9a9..c4c3edee809d 100644 --- a/drivers/staging/rtl8723bs/include/rtw_cmd.h +++ b/drivers/staging/rtl8723bs/include/rtw_cmd.h @@ -559,7 +559,7 @@ struct RunInThread_param { Result: 0x00: success 0x01: success, and check Response. -0x02: cmd ignored due to duplicated sequcne number +0x02: cmd ignored due to duplicated sequence number 0x03: cmd dropped due to invalid cmd code 0x04: reserved. diff --git a/drivers/staging/rtl8723bs/include/rtw_efuse.h b/drivers/staging/rtl8723bs/include/rtw_efuse.h index 936b204b8830..191ffdf593d4 100644 --- a/drivers/staging/rtl8723bs/include/rtw_efuse.h +++ b/drivers/staging/rtl8723bs/include/rtw_efuse.h @@ -68,25 +68,6 @@ struct efuse_hal { u8 fakeBTEfuseModifiedMap[EFUSE_BT_MAX_MAP_LEN]; }; - -/*------------------------Export global variable----------------------------*/ -extern u8 fakeEfuseBank; -extern u32 fakeEfuseUsedBytes; -extern u8 fakeEfuseContent[]; -extern u8 fakeEfuseInitMap[]; -extern u8 fakeEfuseModifiedMap[]; - -extern u32 BTEfuseUsedBytes; -extern u8 BTEfuseContent[EFUSE_MAX_BT_BANK][EFUSE_MAX_HW_SIZE]; -extern u8 BTEfuseInitMap[]; -extern u8 BTEfuseModifiedMap[]; - -extern u32 fakeBTEfuseUsedBytes; -extern u8 fakeBTEfuseContent[EFUSE_MAX_BT_BANK][EFUSE_MAX_HW_SIZE]; -extern u8 fakeBTEfuseInitMap[]; -extern u8 fakeBTEfuseModifiedMap[]; -/*------------------------Export global variable----------------------------*/ - u8 Efuse_CalculateWordCnts(u8 word_en); u8 efuse_OneByteRead(struct adapter *padapter, u16 addr, u8 *data); diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme.h b/drivers/staging/rtl8723bs/include/rtw_mlme.h index 2a128568c6df..ac3ba746b64c 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mlme.h +++ b/drivers/staging/rtl8723bs/include/rtw_mlme.h @@ -76,7 +76,7 @@ Each struct __queue has its own locks, already. Other items in mlme_priv are protected by mlme_priv.lock, while items in xmit_priv are protected by xmit_priv.lock. -To avoid possible dead lock, any thread trying to modifiying mlme_priv +To avoid possible dead lock, any thread trying to modifying mlme_priv SHALL not lock up more than one locks at a time! The only exception is that queue functions which take the __queue.lock @@ -93,18 +93,20 @@ struct sitesurvey_ctrl { }; struct rt_link_detect_t { - u32 NumTxOkInPeriod; - u32 NumRxOkInPeriod; - u32 NumRxUnicastOkInPeriod; - bool bBusyTraffic; - bool bTxBusyTraffic; - bool bRxBusyTraffic; - bool bHigherBusyTraffic; /* For interrupt migration purpose. */ - bool bHigherBusyRxTraffic; /* We may disable Tx interrupt according as Rx traffic. */ - bool bHigherBusyTxTraffic; /* We may disable Tx interrupt according as Tx traffic. */ - /* u8 TrafficBusyState; */ - u8 TrafficTransitionCount; - u32 LowPowerTransitionCount; + u32 num_tx_ok_in_period; + u32 num_rx_ok_in_period; + u32 num_rx_unicast_ok_in_period; + bool busy_traffic; + bool tx_busy_traffic; + bool rx_busy_traffic; + /* For interrupt migration purpose. */ + bool higher_busy_traffic; + /* We may disable Tx interrupt according as Rx traffic. */ + bool higher_busy_rx_traffic; + /* We may disable Tx interrupt according as Tx traffic. */ + bool higher_busy_tx_traffic; + u8 traffic_transition_count; + u32 low_power_transition_count; }; /* used for mlme_priv.roam_flags */ @@ -171,7 +173,7 @@ struct mlme_priv { struct ht_priv htpriv; - struct rt_link_detect_t LinkDetectInfo; + struct rt_link_detect_t link_detect_info; struct timer_list dynamic_chk_timer; /* dynamic/periodic check timer */ u8 acm_mask; /* for wmm acm mask */ @@ -360,9 +362,9 @@ extern void _rtw_free_network_nolock(struct mlme_priv *pmlmepriv, struct wlan_ne extern struct wlan_network *_rtw_find_network(struct __queue *scanned_queue, u8 *addr); -extern signed int rtw_if_up(struct adapter *padapter); +bool rtw_if_up(struct adapter *padapter); -signed int rtw_linked_check(struct adapter *padapter); +bool rtw_linked_check(struct adapter *padapter); u8 *rtw_get_capability_from_ie(u8 *ie); u8 *rtw_get_beacon_interval_from_ie(u8 *ie); @@ -377,7 +379,7 @@ void rtw_update_ht_cap(struct adapter *padapter, u8 *pie, uint ie_len, u8 channe void rtw_issue_addbareq_cmd(struct adapter *padapter, struct xmit_frame *pxmitframe); void rtw_append_exented_cap(struct adapter *padapter, u8 *out_ie, uint *pout_len); -int rtw_is_same_ibss(struct adapter *adapter, struct wlan_network *pnetwork); +bool rtw_is_same_ibss(struct adapter *adapter, struct wlan_network *pnetwork); int is_same_network(struct wlan_bssid_ex *src, struct wlan_bssid_ex *dst, u8 feature); #define rtw_roam_flags(adapter) ((adapter)->mlmepriv.roam_flags) diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h index afa5631a441a..95769f90d196 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h +++ b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h @@ -424,7 +424,7 @@ struct mlme_ext_priv { void init_mlme_default_rate_set(struct adapter *padapter); void init_mlme_ext_priv(struct adapter *padapter); -int init_hw_mlme_ext(struct adapter *padapter); +void init_hw_mlme_ext(struct adapter *padapter); void free_mlme_ext_priv(struct mlme_ext_priv *pmlmeext); extern struct xmit_frame *alloc_mgtxmitframe(struct xmit_priv *pxmitpriv); @@ -615,7 +615,7 @@ extern void process_addba_req(struct adapter *padapter, u8 *paddba_req, u8 *addr extern void update_TSF(struct mlme_ext_priv *pmlmeext, u8 *pframe, uint len); extern void correct_TSF(struct adapter *padapter, struct mlme_ext_priv *pmlmeext); extern void adaptive_early_32k(struct mlme_ext_priv *pmlmeext, u8 *pframe, uint len); -extern u8 traffic_status_watchdog(struct adapter *padapter, u8 from_timer); +extern bool traffic_status_watchdog(struct adapter *padapter, bool from_timer); int rtw_chk_start_clnt_join(struct adapter *padapter, u8 *ch, u8 *bw, u8 *offset); diff --git a/drivers/staging/rtl8723bs/include/wlan_bssdef.h b/drivers/staging/rtl8723bs/include/wlan_bssdef.h index eb38594c8f5c..812a68394268 100644 --- a/drivers/staging/rtl8723bs/include/wlan_bssdef.h +++ b/drivers/staging/rtl8723bs/include/wlan_bssdef.h @@ -15,7 +15,6 @@ #define NDIS_802_11_LENGTH_RATES 8 #define NDIS_802_11_LENGTH_RATES_EX 16 -typedef unsigned char NDIS_802_11_MAC_ADDRESS[6]; typedef unsigned char NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES]; /* Set of 8 data rates */ typedef unsigned char NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; /* Set of 16 data rates */ @@ -64,7 +63,7 @@ struct ndis_80211_var_ie { }; /* Length is the 4 bytes multiples of the sum of - * sizeof (NDIS_802_11_MAC_ADDRESS) + 2 + + * ETH_ALEN + 2 + * sizeof (struct ndis_802_11_ssid) + sizeof (u32) + * sizeof (long) + sizeof (enum ndis_802_11_network_type) + * sizeof (struct ndis_802_11_conf) + sizeof (NDIS_802_11_RATES_EX) + ie_length @@ -155,7 +154,7 @@ struct wlan_bcn_info { */ struct wlan_bssid_ex { u32 length; - NDIS_802_11_MAC_ADDRESS mac_address; + u8 mac_address[ETH_ALEN]; u8 reserved[2];/* 0]: IS beacon frame */ struct ndis_802_11_ssid ssid; u32 privacy; diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 7c714ef73ea0..fd3bae31b0ed 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -93,7 +93,7 @@ static struct ieee80211_channel rtw_2ghz_channels[] = { static void rtw_2g_channels_init(struct ieee80211_channel *channels) { - memcpy((void *)channels, (void *)rtw_2ghz_channels, + memcpy(channels, rtw_2ghz_channels, sizeof(struct ieee80211_channel) * RTW_2G_CHANNELS_NUM ); } @@ -380,9 +380,9 @@ void rtw_cfg80211_ibss_indicate_connect(struct adapter *padapter) rtw_warn_on(1); return; } - if (!memcmp(&(scanned->network.ssid), &(pnetwork->ssid), sizeof(struct ndis_802_11_ssid)) - && !memcmp(scanned->network.mac_address, pnetwork->mac_address, sizeof(NDIS_802_11_MAC_ADDRESS)) - ) + if (!memcmp(&scanned->network.ssid, &pnetwork->ssid, + sizeof(struct ndis_802_11_ssid)) && + !memcmp(scanned->network.mac_address, pnetwork->mac_address, ETH_ALEN)) rtw_cfg80211_inform_bss(padapter, scanned); else rtw_warn_on(1); @@ -422,9 +422,9 @@ void rtw_cfg80211_indicate_connect(struct adapter *padapter) goto check_bss; } - if (!memcmp(scanned->network.mac_address, pnetwork->mac_address, sizeof(NDIS_802_11_MAC_ADDRESS)) - && !memcmp(&(scanned->network.ssid), &(pnetwork->ssid), sizeof(struct ndis_802_11_ssid)) - ) + if (!memcmp(scanned->network.mac_address, pnetwork->mac_address, ETH_ALEN) && + !memcmp(&scanned->network.ssid, &pnetwork->ssid, + sizeof(struct ndis_802_11_ssid))) rtw_cfg80211_inform_bss(padapter, scanned); else rtw_warn_on(1); @@ -896,7 +896,7 @@ static int cfg80211_rtw_add_key(struct wiphy *wiphy, struct wireless_dev *wdev, ret = rtw_cfg80211_set_encryption(ndev, param, param_len); } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { if (mac_addr) - memcpy(param->sta_addr, (void *)mac_addr, ETH_ALEN); + memcpy(param->sta_addr, mac_addr, ETH_ALEN); ret = rtw_cfg80211_ap_set_encryption(ndev, param, param_len); } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true @@ -1233,7 +1233,7 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy goto check_need_indicate_scan_done; } - if (pmlmepriv->LinkDetectInfo.bBusyTraffic == true) { + if (pmlmepriv->link_detect_info.busy_traffic) { static unsigned long lastscantime; unsigned long passtime; @@ -1432,14 +1432,12 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel goto exit; } - buf = kzalloc(ielen, GFP_KERNEL); + buf = kmemdup(pie, ielen, GFP_KERNEL); if (!buf) { ret = -ENOMEM; goto exit; } - memcpy(buf, pie, ielen); - if (ielen < RSN_HEADER_LEN) { ret = -1; goto exit; @@ -1737,7 +1735,7 @@ static int cfg80211_rtw_connect(struct wiphy *wiphy, struct net_device *ndev, pwep->key_index = wep_key_idx; pwep->key_index |= 0x80000000; - memcpy(pwep->key_material, (void *)sme->key, pwep->key_length); + memcpy(pwep->key_material, sme->key, pwep->key_length); if (rtw_set_802_11_add_wep(padapter, pwep) == (u8)_FAIL) ret = -EOPNOTSUPP; @@ -2086,7 +2084,7 @@ static netdev_tx_t rtw_cfg80211_monitor_if_xmit_entry(struct sk_buff *skb, struc pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; - memcpy(pframe, (void *)buf, len); + memcpy(pframe, buf, len); pattrib->pktlen = len; pwlanhdr = (struct ieee80211_hdr *)pframe; @@ -2261,8 +2259,8 @@ static int rtw_add_beacon(struct adapter *adapter, const u8 *head, size_t head_l if (!pbuf) return -ENOMEM; - memcpy(pbuf, (void *)head + 24, head_len - 24);/* 24 =beacon header len. */ - memcpy(pbuf + head_len - 24, (void *)tail, tail_len); + memcpy(pbuf, head + 24, head_len - 24);/* 24 =beacon header len. */ + memcpy(pbuf + head_len - 24, tail, tail_len); len = head_len + tail_len - 24; @@ -2299,9 +2297,9 @@ static int cfg80211_rtw_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct wlan_bssid_ex *pbss_network = &adapter->mlmepriv.cur_network.network; struct wlan_bssid_ex *pbss_network_ext = &adapter->mlmeextpriv.mlmext_info.network; - memcpy(pbss_network->ssid.ssid, (void *)settings->ssid, settings->ssid_len); + memcpy(pbss_network->ssid.ssid, settings->ssid, settings->ssid_len); pbss_network->ssid.ssid_length = settings->ssid_len; - memcpy(pbss_network_ext->ssid.ssid, (void *)settings->ssid, settings->ssid_len); + memcpy(pbss_network_ext->ssid.ssid, settings->ssid, settings->ssid_len); pbss_network_ext->ssid.ssid_length = settings->ssid_len; } @@ -2495,7 +2493,7 @@ static int _cfg80211_rtw_mgmt_tx(struct adapter *padapter, u8 tx_ch, const u8 *b pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; - memcpy(pframe, (void *)buf, len); + memcpy(pframe, buf, len); pattrib->pktlen = len; pwlanhdr = (struct ieee80211_hdr *)pframe; @@ -2752,13 +2750,6 @@ int rtw_wdev_alloc(struct adapter *padapter, struct device *dev) pwdev_priv->scan_request = NULL; spin_lock_init(&pwdev_priv->scan_req_lock); - pwdev_priv->p2p_enabled = false; - pwdev_priv->provdisc_req_issued = false; - rtw_wdev_invit_info_init(&pwdev_priv->invit_info); - rtw_wdev_nego_info_init(&pwdev_priv->nego_info); - - pwdev_priv->bandroid_scan = false; - if (padapter->registrypriv.power_mgnt != PS_MODE_ACTIVE) pwdev_priv->power_mgmt = true; else diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c index 29939bf5a156..e943dcea1a21 100644 --- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c @@ -492,7 +492,7 @@ void rtw_stop_drv_threads(struct adapter *padapter) { rtw_stop_cmd_thread(padapter); - /* Below is to termindate tx_thread... */ + /* Below is to terminate tx_thread... */ complete(&padapter->xmitpriv.xmit_comp); wait_for_completion(&padapter->xmitpriv.terminate_xmitthread_comp); @@ -618,11 +618,10 @@ void rtw_reset_drv_sw(struct adapter *padapter) padapter->xmitpriv.tx_pkts = 0; padapter->recvpriv.rx_pkts = 0; - pmlmepriv->LinkDetectInfo.bBusyTraffic = false; + pmlmepriv->link_detect_info.busy_traffic = false; - /* pmlmepriv->LinkDetectInfo.TrafficBusyState = false; */ - pmlmepriv->LinkDetectInfo.TrafficTransitionCount = 0; - pmlmepriv->LinkDetectInfo.LowPowerTransitionCount = 0; + pmlmepriv->link_detect_info.traffic_transition_count = 0; + pmlmepriv->link_detect_info.low_power_transition_count = 0; _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY | _FW_UNDER_LINKING); @@ -960,7 +959,6 @@ static int netdev_close(struct net_device *pnetdev) } rtw_scan_abort(padapter); - adapter_wdev_data(padapter)->bandroid_scan = false; return 0; } @@ -1137,10 +1135,10 @@ static int rtw_resume_process_normal(struct adapter *padapter) pwrpriv = adapter_to_pwrctl(padapter); pmlmepriv = &padapter->mlmepriv; /* interface init */ - /* if (sdio_init(adapter_to_dvobj(padapter)) != _SUCCESS) */ - if ((padapter->intf_init) && (padapter->intf_init(adapter_to_dvobj(padapter)) != _SUCCESS)) { - ret = -1; - goto exit; + if (padapter->intf_init) { + ret = padapter->intf_init(adapter_to_dvobj(padapter)); + if (ret) + goto exit; } rtw_hal_disable_interrupt(padapter); /* if (sdio_alloc_irq(adapter_to_dvobj(padapter)) != _SUCCESS) */ diff --git a/drivers/staging/rtl8723bs/os_dep/osdep_service.c b/drivers/staging/rtl8723bs/os_dep/osdep_service.c index 7959daeabc6f..4cfdf7c62344 100644 --- a/drivers/staging/rtl8723bs/os_dep/osdep_service.c +++ b/drivers/staging/rtl8723bs/os_dep/osdep_service.c @@ -194,7 +194,8 @@ struct rtw_cbuf *rtw_cbuf_alloc(u32 size) struct rtw_cbuf *cbuf; cbuf = kzalloc_flex(*cbuf, bufs, size); - cbuf->size = size; + if (cbuf) + cbuf->size = size; return cbuf; } diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index d664e254912c..d0feb28b7043 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -131,9 +131,7 @@ static u32 sdio_init(struct dvobj_priv *dvobj) release: sdio_release_host(func); - if (err) - return _FAIL; - return _SUCCESS; + return err; } static void sdio_deinit(struct dvobj_priv *dvobj) @@ -155,35 +153,32 @@ static void sdio_deinit(struct dvobj_priv *dvobj) } static struct dvobj_priv *sdio_dvobj_init(struct sdio_func *func) { - int status = _FAIL; struct dvobj_priv *dvobj = NULL; struct sdio_data *psdio; + int ret; dvobj = devobj_init(); if (!dvobj) - goto exit; + return NULL; sdio_set_drvdata(func, dvobj); psdio = &dvobj->intf_data; psdio->func = func; - if (sdio_init(dvobj) != _SUCCESS) + ret = sdio_init(dvobj); + if (ret) goto free_dvobj; rtw_reset_continual_io_error(dvobj); - status = _SUCCESS; + + return dvobj; free_dvobj: - if (status != _SUCCESS && dvobj) { - sdio_set_drvdata(func, NULL); + sdio_set_drvdata(func, NULL); + devobj_deinit(dvobj); - devobj_deinit(dvobj); - - dvobj = NULL; - } -exit: - return dvobj; + return NULL; } static void sdio_dvobj_deinit(struct sdio_func *func) diff --git a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c index 0be3143fffe5..dc0b77f38b1a 100644 --- a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c @@ -193,12 +193,9 @@ void _rtw_xmit_entry(struct sk_buff *pkt, struct net_device *pnetdev) rtw_check_xmit_resource(padapter, pkt); - if (!rtw_mc2u_disable - && check_fwstate(pmlmepriv, WIFI_AP_STATE) == true - && (IP_MCAST_MAC(pkt->data) - || ICMPV6_MCAST_MAC(pkt->data) - ) - && padapter->registrypriv.wifi_spec == 0) { + if (!rtw_mc2u_disable && check_fwstate(pmlmepriv, WIFI_AP_STATE) && + (IP_MCAST_MAC(pkt->data) || ICMPV6_MCAST_MAC(pkt->data)) && + !padapter->registrypriv.wifi_spec) { if (pxmitpriv->free_xmitframe_cnt > (NR_XMITFRAME / 4)) { res = rtw_mlcst2unicst(padapter, pkt); if (res) diff --git a/drivers/staging/sm750fb/ddk750_mode.c b/drivers/staging/sm750fb/ddk750_mode.c index 3b25892af713..7163232c0701 100644 --- a/drivers/staging/sm750fb/ddk750_mode.c +++ b/drivers/staging/sm750fb/ddk750_mode.c @@ -74,7 +74,7 @@ display_control_adjust_SM750LE(struct mode_parameter *mode_param, return disp_control; } -/* only timing related registers will be programed */ +/* only timing related registers will be programmed */ static void program_mode_registers(struct mode_parameter *mode_param, struct pll_value *pll) { diff --git a/drivers/staging/sm750fb/ddk750_swi2c.c b/drivers/staging/sm750fb/ddk750_swi2c.c index 0ef8d4ff2ef9..e63f3b00ec4c 100644 --- a/drivers/staging/sm750fb/ddk750_swi2c.c +++ b/drivers/staging/sm750fb/ddk750_swi2c.c @@ -27,7 +27,7 @@ * * I.e. the SCL may only be changed in section 1. and section 3. while * the SDA may only be changed in section 2. and section 4. The table - * below gives the changes for these 2 lines in the varios sections. + * below gives the changes for these 2 lines in the various sections. * * Section changes Table: * ====================== diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index 62f6e0cdff4d..9f3e3d37e82a 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -375,7 +375,6 @@ static int lynxfb_ops_set_par(struct fb_info *info) line_length = var->xres_virtual * var->bits_per_pixel / 8; line_length = ALIGN(line_length, crtc->line_pad); fix->line_length = line_length; - pr_info("fix->line_length = %d\n", fix->line_length); /* * var->red,green,blue,transp are need to be set by driver @@ -389,7 +388,8 @@ static int lynxfb_ops_set_par(struct fb_info *info) var->accel_flags = 0;/*FB_ACCELF_TEXT;*/ if (ret) { - pr_err("bpp %d not supported\n", var->bits_per_pixel); + dev_err(info->device, "bpp %d not supported\n", + var->bits_per_pixel); return ret; } ret = hw_sm750_crtc_set_mode(crtc, var, fix); @@ -481,19 +481,18 @@ static int lynxfb_ops_check_var(struct fb_var_screeninfo *var, struct lynxfb_crtc *crtc; resource_size_t request; + if (!var->pixclock) + return -EINVAL; + ret = 0; par = info->par; crtc = &par->crtc; - pr_debug("check var:%dx%d-%d\n", - var->xres, - var->yres, - var->bits_per_pixel); - ret = lynxfb_set_color_offsets(info); if (ret) { - pr_err("bpp %d not supported\n", var->bits_per_pixel); + dev_err(info->device, "bpp %d not supported\n", + var->bits_per_pixel); return ret; } @@ -508,7 +507,7 @@ static int lynxfb_ops_check_var(struct fb_var_screeninfo *var, request = ALIGN(request, crtc->line_pad); request = request * var->yres_virtual; if (crtc->vidmem_size < request) { - pr_err("not enough video memory for mode\n"); + dev_err(info->device, "not enough video memory for mode\n"); return -ENOMEM; } @@ -533,7 +532,7 @@ static int lynxfb_ops_setcolreg(unsigned int regno, ret = 0; if (regno > 256) { - pr_err("regno = %d\n", regno); + dev_err(info->device, "regno = %d\n", regno); return -EINVAL; } @@ -580,7 +579,6 @@ static int lynxfb_ops_blank(int blank, struct fb_info *info) struct lynxfb_par *par; struct lynxfb_output *output; - pr_debug("blank = %d.\n", blank); par = info->par; output = &par->output; sm750_dev = par->dev; @@ -625,7 +623,6 @@ static int sm750fb_set_drv(struct lynxfb_par *par) crtc->channel = sm750_primary; crtc->o_screen = 0; crtc->v_screen = sm750_dev->pvMem; - pr_info("use simul primary mode\n"); break; case sm750_simul_sec: output->paths = sm750_pnc; @@ -734,12 +731,6 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index) lynx750_ext, NULL, vesa_modes, }; int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE}; - static const char * const mdb_desc[] = { - "driver prepared modes", - "kernel prepared default modedb", - "kernel HELPERS prepared vesa_modes", - }; - static const char *fix_id[2] = { "sm750_fb1", "sm750_fb2", }; @@ -767,7 +758,6 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index) crtc->cursor.mmio = sm750_dev->pvReg + 0x800f0 + (int)crtc->channel * 0x140; - pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio); crtc->cursor.max_h = 64; crtc->cursor.max_w = 64; crtc->cursor.size = crtc->cursor.max_h * crtc->cursor.max_w * 2 / 8; @@ -801,47 +791,10 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index) ret = fb_find_mode(var, info, g_fbmode[index], pdb[i], cdb[i], NULL, 8); - if (ret == 1) { - pr_info("success! use specified mode:%s in %s\n", - g_fbmode[index], - mdb_desc[i]); + if (ret == 1 || ret == 2) break; - } else if (ret == 2) { - pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n", - g_fbmode[index], - mdb_desc[i]); - break; - } else if (ret == 3) { - pr_warn("wanna use default mode\n"); - /*break;*/ - } else if (ret == 4) { - pr_warn("fall back to any valid mode\n"); - } else { - pr_warn("ret = %d,fb_find_mode failed,with %s\n", - ret, - mdb_desc[i]); - } } - /* some member of info->var had been set by fb_find_mode */ - - pr_info("Member of info->var is :\n" - "xres=%d\n" - "yres=%d\n" - "xres_virtual=%d\n" - "yres_virtual=%d\n" - "xoffset=%d\n" - "yoffset=%d\n" - "bits_per_pixel=%d\n" - " ...\n", - var->xres, - var->yres, - var->xres_virtual, - var->yres_virtual, - var->xoffset, - var->yoffset, - var->bits_per_pixel); - /* set par */ par->info = info; @@ -851,7 +804,6 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index) info->pseudo_palette = &par->pseudo_palette[0]; info->screen_base = crtc->v_screen; - pr_debug("screen_base vaddr = %p\n", info->screen_base); info->screen_size = line_length * var->yres_virtual; /* set info->fix */ @@ -865,7 +817,6 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index) strscpy(fix->id, fix_id[index], sizeof(fix->id)); fix->smem_start = crtc->o_screen + sm750_dev->vidmem_start; - pr_info("fix->smem_start = %lx\n", fix->smem_start); /* * according to mmap experiment from user space application, * fix->mmio_len should not larger than virtual size @@ -874,13 +825,10 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index) * data into the bound over virtual size */ fix->smem_len = crtc->vidmem_size; - pr_info("fix->smem_len = %x\n", fix->smem_len); info->screen_size = fix->smem_len; fix->line_length = line_length; fix->mmio_start = sm750_dev->vidreg_start; - pr_info("fix->mmio_start = %lx\n", fix->mmio_start); fix->mmio_len = sm750_dev->vidreg_size; - pr_info("fix->mmio_len = %x\n", fix->mmio_len); lynxfb_set_visual_mode(info); @@ -889,22 +837,12 @@ static int lynxfb_set_fbinfo(struct fb_info *info, int index) var->accel_flags = 0; var->vmode = FB_VMODE_NONINTERLACED; - pr_debug("#1 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n", - info->cmap.start, info->cmap.len, - info->cmap.red, info->cmap.green, info->cmap.blue, - info->cmap.transp); - ret = fb_alloc_cmap(&info->cmap, 256, 0); if (ret < 0) { - pr_err("Could not allocate memory for cmap.\n"); + dev_err(info->device, "Could not allocate memory for cmap.\n"); goto exit; } - pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n", - info->cmap.start, info->cmap.len, - info->cmap.red, info->cmap.green, info->cmap.blue, - info->cmap.transp); - exit: lynxfb_ops_check_var(var, info); return ret; @@ -1132,12 +1070,8 @@ static int __init lynxfb_setup(char *options) int len; char *opt, *tmp; - if (!options || !*options) { - pr_warn("no options.\n"); + if (!options || !*options) return 0; - } - - pr_info("options:%s\n", options); len = strlen(options) + 1; g_settings = kzalloc(len, GFP_KERNEL); @@ -1174,7 +1108,6 @@ static int __init lynxfb_setup(char *options) } /* misc g_settings are transport to chip specific routines */ - pr_info("parameter left for chip specific analysis:%s\n", g_settings); return 0; } diff --git a/drivers/staging/sm750fb/sm750_hw.c b/drivers/staging/sm750fb/sm750_hw.c index f60b152a647d..a2798d428663 100644 --- a/drivers/staging/sm750fb/sm750_hw.c +++ b/drivers/staging/sm750fb/sm750_hw.c @@ -34,12 +34,10 @@ int hw_sm750_map(struct sm750_dev *sm750_dev, struct pci_dev *pdev) sm750_dev->vidreg_start = pci_resource_start(pdev, 1); sm750_dev->vidreg_size = SZ_2M; - pr_info("mmio phyAddr = %lx\n", sm750_dev->vidreg_start); - /* reserve the vidreg space of smi adaptor */ ret = pci_request_region(pdev, 1, "sm750fb"); if (ret) { - pr_err("Can not request PCI regions.\n"); + dev_err(&pdev->dev, "Can not request PCI regions.\n"); return ret; } @@ -47,11 +45,10 @@ int hw_sm750_map(struct sm750_dev *sm750_dev, struct pci_dev *pdev) sm750_dev->pvReg = ioremap(sm750_dev->vidreg_start, sm750_dev->vidreg_size); if (!sm750_dev->pvReg) { - pr_err("mmio failed\n"); + dev_err(&pdev->dev, "mmio failed\n"); ret = -EFAULT; goto err_release_region; } - pr_info("mmio virtual addr = %p\n", sm750_dev->pvReg); sm750_dev->accel.dpr_base = sm750_dev->pvReg + DE_BASE_ADDR_TYPE1; sm750_dev->accel.dp_port_base = sm750_dev->pvReg + DE_PORT_ADDR_TYPE1; @@ -67,18 +64,15 @@ int hw_sm750_map(struct sm750_dev *sm750_dev, struct pci_dev *pdev) * @ddk750_get_vm_size function can be safe. */ sm750_dev->vidmem_size = ddk750_get_vm_size(); - pr_info("video memory phyAddr = %lx, size = %u bytes\n", - sm750_dev->vidmem_start, sm750_dev->vidmem_size); /* reserve the vidmem space of smi adaptor */ sm750_dev->pvMem = ioremap_wc(sm750_dev->vidmem_start, sm750_dev->vidmem_size); if (!sm750_dev->pvMem) { - pr_err("Map video memory failed\n"); + dev_err(&pdev->dev, "Map video memory failed\n"); ret = -EFAULT; goto err_unmap_reg; } - pr_info("video memory vaddr = %p\n", sm750_dev->pvMem); return 0; @@ -163,11 +157,9 @@ int hw_sm750_inithw(struct sm750_dev *sm750_dev, struct pci_dev *pdev) * The following register values for CH7301 are from * Chrontel app note and our experiment. */ - pr_info("yes,CH7301 DVI chip found\n"); sm750_sw_i2c_write_reg(0xec, 0x1d, 0x16); sm750_sw_i2c_write_reg(0xec, 0x21, 0x9); sm750_sw_i2c_write_reg(0xec, 0x49, 0xC0); - pr_info("okay,CH7301 DVI chip setup done\n"); } } @@ -192,14 +184,12 @@ int hw_sm750_output_set_mode(struct lynxfb_output *output, if (sm750_get_chip_type() != SM750LE) { if (channel == sm750_primary) { - pr_info("primary channel\n"); if (output->paths & sm750_panel) disp_set |= do_LCD1_PRI; if (output->paths & sm750_crt) disp_set |= do_CRT_PRI; } else { - pr_info("secondary channel\n"); if (output->paths & sm750_panel) disp_set |= do_LCD1_SEC; if (output->paths & sm750_crt) @@ -215,7 +205,6 @@ int hw_sm750_output_set_mode(struct lynxfb_output *output, poke32(DISPLAY_CONTROL_750LE, reg); } - pr_info("ddk setlogicdispout done\n"); return ret; } @@ -232,10 +221,8 @@ int hw_sm750_crtc_check_mode(struct lynxfb_crtc *crtc, case 16: break; case 32: - if (sm750_dev->revid == SM750LE_REVISION_ID) { - pr_debug("750le do not support 32bpp\n"); + if (sm750_dev->revid == SM750LE_REVISION_ID) return -EINVAL; - } break; default: return -EINVAL; @@ -302,10 +289,9 @@ int hw_sm750_crtc_set_mode(struct lynxfb_crtc *crtc, else clock = SECONDARY_PLL; - pr_debug("Request pixel clock = %lu\n", modparm.pixel_clock); ret = ddk750_set_mode_timing(&modparm, clock); if (ret) { - pr_err("Set mode timing failed\n"); + dev_err(&sm750_dev->pdev->dev, "Set mode timing failed\n"); goto exit; } @@ -431,12 +417,10 @@ int hw_sm750_set_blank(struct lynxfb_output *output, int blank) switch (blank) { case FB_BLANK_UNBLANK: - pr_debug("flag = FB_BLANK_UNBLANK\n"); dpms = SYSTEM_CTRL_DPMS_VPHP; pps = PANEL_DISPLAY_CTRL_DATA; break; case FB_BLANK_NORMAL: - pr_debug("flag = FB_BLANK_NORMAL\n"); dpms = SYSTEM_CTRL_DPMS_VPHP; crtdb = CRT_DISPLAY_CTRL_BLANK; break; diff --git a/drivers/staging/vme_user/vme_fake.c b/drivers/staging/vme_user/vme_fake.c index be4ad47ed526..8abaa3165fbb 100644 --- a/drivers/staging/vme_user/vme_fake.c +++ b/drivers/staging/vme_user/vme_fake.c @@ -1230,6 +1230,8 @@ err_master: err_driver: kfree(fake_bridge); err_struct: + root_device_unregister(vme_root); + return retval; } diff --git a/drivers/staging/vme_user/vme_user.h b/drivers/staging/vme_user/vme_user.h index 19ecb05781cc..9e010974351d 100644 --- a/drivers/staging/vme_user/vme_user.h +++ b/drivers/staging/vme_user/vme_user.h @@ -14,11 +14,6 @@ struct vme_master { __u32 aspace; /* Address Space */ __u32 cycle; /* Cycle properties */ __u32 dwidth; /* Maximum Data Width */ -#if 0 - char prefetchenable; /* Prefetch Read Enable State */ - int prefetchsize; /* Prefetch Read Size (Cache Lines) */ - char wrpostenable; /* Write Post State */ -#endif } __packed; /* @@ -35,11 +30,6 @@ struct vme_slave { __u64 size; /* Window Size */ __u32 aspace; /* Address Space */ __u32 cycle; /* Cycle properties */ -#if 0 - char wrpostenable; /* Write Post State */ - char rmwlock; /* Lock PCI during RMW Cycles */ - char data64bitcapable; /* non-VMEbus capable of 64-bit Data */ -#endif } __packed; struct vme_irq_id { diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index e80449f6ce15..cb832fd523af 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -995,6 +995,7 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, int data_direction, payload_length; struct iscsi_ecdb_ahdr *ecdb_ahdr; struct iscsi_scsi_req *hdr; + u16 ahslength, cdb_length; int iscsi_task_attr; unsigned char *cdb; int sam_task_attr; @@ -1108,14 +1109,27 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, ISCSI_REASON_CMD_NOT_SUPPORTED, buf); } - cdb = kmalloc(be16_to_cpu(ecdb_ahdr->ahslength) + 15, - GFP_KERNEL); + ahslength = be16_to_cpu(ecdb_ahdr->ahslength); + if (!ahslength) { + pr_err("Extended CDB AHS with zero length, protocol error.\n"); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_PROTOCOL_ERROR, buf); + } + if (ahslength > (hdr->hlength * 4) - 3) { + pr_err("Extended CDB AHS length %u exceeds available PDU buffer.\n", + ahslength); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_PROTOCOL_ERROR, buf); + } + + cdb_length = ahslength - 1 + ISCSI_CDB_SIZE; + + cdb = kmalloc(cdb_length, GFP_KERNEL); if (cdb == NULL) return iscsit_add_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); memcpy(cdb, hdr->cdb, ISCSI_CDB_SIZE); - memcpy(cdb + ISCSI_CDB_SIZE, ecdb_ahdr->ecdb, - be16_to_cpu(ecdb_ahdr->ahslength) - 1); + memcpy(cdb + ISCSI_CDB_SIZE, ecdb_ahdr->ecdb, cdb_length - ISCSI_CDB_SIZE); } data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE : diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index efe8cdb20060..704ec94383c3 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -1591,6 +1591,7 @@ const struct target_core_fabric_ops iscsi_ops = { .write_pending_must_be_called = 1, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_DIRECT_SUBMIT, .direct_submit_supp = 1, }; diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 528883d989b8..a25fd826b542 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -1147,6 +1147,7 @@ static const struct target_core_fabric_ops loop_ops = { .tfc_wwn_attrs = tcm_loop_wwn_attrs, .tfc_tpg_base_attrs = tcm_loop_tpg_attrs, .tfc_tpg_attrib_attrs = tcm_loop_tpg_attrib_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_QUEUE_SUBMIT, .direct_submit_supp = 0, }; diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c index ad1da7edbb08..896fc0f0379f 100644 --- a/drivers/target/sbp/sbp_target.c +++ b/drivers/target/sbp/sbp_target.c @@ -2278,6 +2278,7 @@ static const struct target_core_fabric_ops sbp_ops = { .tfc_tpg_base_attrs = sbp_tpg_base_attrs, .tfc_tpg_attrib_attrs = sbp_tpg_attrib_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_DIRECT_SUBMIT, .direct_submit_supp = 1, }; diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index a1c91d4515bc..2b19a956007b 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -575,6 +575,7 @@ DEF_CONFIGFS_ATTRIB_SHOW(unmap_zeroes_data); DEF_CONFIGFS_ATTRIB_SHOW(max_write_same_len); DEF_CONFIGFS_ATTRIB_SHOW(emulate_rsoc); DEF_CONFIGFS_ATTRIB_SHOW(submit_type); +DEF_CONFIGFS_ATTRIB_SHOW(complete_type); DEF_CONFIGFS_ATTRIB_SHOW(atomic_max_len); DEF_CONFIGFS_ATTRIB_SHOW(atomic_alignment); DEF_CONFIGFS_ATTRIB_SHOW(atomic_granularity); @@ -1266,6 +1267,24 @@ static ssize_t submit_type_store(struct config_item *item, const char *page, return count; } +static ssize_t complete_type_store(struct config_item *item, const char *page, + size_t count) +{ + struct se_dev_attrib *da = to_attrib(item); + int ret; + u8 val; + + ret = kstrtou8(page, 0, &val); + if (ret < 0) + return ret; + + if (val > TARGET_QUEUE_COMPL) + return -EINVAL; + + da->complete_type = val; + return count; +} + CONFIGFS_ATTR(, emulate_model_alias); CONFIGFS_ATTR(, emulate_dpo); CONFIGFS_ATTR(, emulate_fua_write); @@ -1302,6 +1321,7 @@ CONFIGFS_ATTR(, max_write_same_len); CONFIGFS_ATTR(, alua_support); CONFIGFS_ATTR(, pgr_support); CONFIGFS_ATTR(, submit_type); +CONFIGFS_ATTR(, complete_type); CONFIGFS_ATTR_RO(, atomic_max_len); CONFIGFS_ATTR_RO(, atomic_alignment); CONFIGFS_ATTR_RO(, atomic_granularity); @@ -1350,6 +1370,7 @@ struct configfs_attribute *sbc_attrib_attrs[] = { &attr_pgr_support, &attr_emulate_rsoc, &attr_submit_type, + &attr_complete_type, &attr_atomic_alignment, &attr_atomic_max_len, &attr_atomic_granularity, @@ -1373,6 +1394,7 @@ struct configfs_attribute *passthrough_attrib_attrs[] = { &attr_alua_support, &attr_pgr_support, &attr_submit_type, + &attr_complete_type, NULL, }; EXPORT_SYMBOL(passthrough_attrib_attrs); @@ -3227,7 +3249,7 @@ static ssize_t target_tg_pt_gp_members_show(struct config_item *item, config_item_name(&lun->lun_group.cg_item)); cur_len++; /* Extra byte for NULL terminator */ - if ((cur_len + len) > PAGE_SIZE) { + if (cur_len > TG_PT_GROUP_NAME_BUF || (cur_len + len) > PAGE_SIZE) { pr_warn("Ran out of lu_gp_show_attr" "_members buffer\n"); break; diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 74c6383f9eed..9db2201aa553 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -813,6 +813,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) DA_UNMAP_ZEROES_DATA_DEFAULT; dev->dev_attrib.max_write_same_len = DA_MAX_WRITE_SAME_LEN; dev->dev_attrib.submit_type = TARGET_FABRIC_DEFAULT_SUBMIT; + dev->dev_attrib.complete_type = TARGET_FABRIC_DEFAULT_COMPL; /* Skip allocating lun_stats since we can't export them. */ xcopy_lun = &dev->xcopy_lun; diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 331689b30f85..166dbf4c4061 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -1065,6 +1065,28 @@ target_fabric_wwn_cmd_completion_affinity_store(struct config_item *item, } CONFIGFS_ATTR(target_fabric_wwn_, cmd_completion_affinity); +static ssize_t +target_fabric_wwn_default_complete_type_show(struct config_item *item, + char *page) +{ + struct se_wwn *wwn = container_of(to_config_group(item), struct se_wwn, + param_group); + return sysfs_emit(page, "%u\n", + wwn->wwn_tf->tf_ops->default_compl_type); +} +CONFIGFS_ATTR_RO(target_fabric_wwn_, default_complete_type); + +static ssize_t +target_fabric_wwn_direct_complete_supported_show(struct config_item *item, + char *page) +{ + struct se_wwn *wwn = container_of(to_config_group(item), struct se_wwn, + param_group); + return sysfs_emit(page, "%u\n", + wwn->wwn_tf->tf_ops->direct_compl_supp); +} +CONFIGFS_ATTR_RO(target_fabric_wwn_, direct_complete_supported); + static ssize_t target_fabric_wwn_default_submit_type_show(struct config_item *item, char *page) @@ -1089,6 +1111,8 @@ CONFIGFS_ATTR_RO(target_fabric_wwn_, direct_submit_supported); static struct configfs_attribute *target_fabric_wwn_param_attrs[] = { &target_fabric_wwn_attr_cmd_completion_affinity, + &target_fabric_wwn_attr_default_complete_type, + &target_fabric_wwn_attr_direct_complete_supported, &target_fabric_wwn_attr_default_submit_type, &target_fabric_wwn_attr_direct_submit_supported, NULL, diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index f88e63aefcd8..11790f2c5d80 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -2809,7 +2809,7 @@ static void core_scsi3_release_preempt_and_abort( } static sense_reason_t -core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key, +core_scsi3_emulate_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key, u64 sa_res_key, enum preempt_type preempt_type) { struct se_device *dev = cmd->se_dev; @@ -2838,11 +2838,6 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key, core_scsi3_put_pr_reg(pr_reg_n); return TCM_RESERVATION_CONFLICT; } - if (scope != PR_SCOPE_LU_SCOPE) { - pr_err("SPC-3 PR: Illegal SCOPE: 0x%02x\n", scope); - core_scsi3_put_pr_reg(pr_reg_n); - return TCM_INVALID_PARAMETER_LIST; - } spin_lock(&dev->dev_reservation_lock); pr_res_holder = dev->dev_pr_res_holder; @@ -2856,6 +2851,37 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key, core_scsi3_put_pr_reg(pr_reg_n); return TCM_INVALID_PARAMETER_LIST; } + + /* Validate TYPE and SCOPE fields if they will be used */ + if (pr_res_holder && + (pr_res_holder->pr_res_key == sa_res_key || + (all_reg && !sa_res_key))) { + switch (type) { + case PR_TYPE_WRITE_EXCLUSIVE: + case PR_TYPE_EXCLUSIVE_ACCESS: + case PR_TYPE_WRITE_EXCLUSIVE_REGONLY: + case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY: + case PR_TYPE_WRITE_EXCLUSIVE_ALLREG: + case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG: + break; + default: + pr_err("SPC-3 PR: Unknown Service Action PREEMPT%s" + " Type: 0x%02x\n", + (preempt_type == PREEMPT_AND_ABORT) ? + "_AND_ABORT" : "", type); + spin_unlock(&dev->dev_reservation_lock); + core_scsi3_put_pr_reg(pr_reg_n); + return TCM_INVALID_CDB_FIELD; + } + + if (scope != PR_SCOPE_LU_SCOPE) { + pr_err("SPC-3 PR: Illegal SCOPE: 0x%02x\n", scope); + spin_unlock(&dev->dev_reservation_lock); + core_scsi3_put_pr_reg(pr_reg_n); + return TCM_INVALID_PARAMETER_LIST; + } + } + /* * From spc4r17, section 5.7.11.4.4 Removing Registrations: * @@ -3118,27 +3144,6 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key, return 0; } -static sense_reason_t -core_scsi3_emulate_pro_preempt(struct se_cmd *cmd, int type, int scope, - u64 res_key, u64 sa_res_key, enum preempt_type preempt_type) -{ - switch (type) { - case PR_TYPE_WRITE_EXCLUSIVE: - case PR_TYPE_EXCLUSIVE_ACCESS: - case PR_TYPE_WRITE_EXCLUSIVE_REGONLY: - case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY: - case PR_TYPE_WRITE_EXCLUSIVE_ALLREG: - case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG: - return core_scsi3_pro_preempt(cmd, type, scope, res_key, - sa_res_key, preempt_type); - default: - pr_err("SPC-3 PR: Unknown Service Action PREEMPT%s" - " Type: 0x%02x\n", (preempt_type == PREEMPT_AND_ABORT) ? "_AND_ABORT" : "", type); - return TCM_INVALID_CDB_FIELD; - } -} - - static sense_reason_t core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, u64 sa_res_key, int aptpl, int unreg) diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index abe91dc8722e..21f5cb86d70c 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -1187,7 +1187,8 @@ sbc_execute_unmap(struct se_cmd *cmd) goto err; } - if (lba + range > dev->transport->get_blocks(dev) + 1) { + if (lba + range < lba || + lba + range > dev->transport->get_blocks(dev) + 1) { ret = TCM_ADDRESS_OUT_OF_RANGE; goto err; } diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index a7330c4fedde..fad03a15c969 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -902,13 +902,59 @@ static bool target_cmd_interrupted(struct se_cmd *cmd) return false; } +static void target_complete(struct se_cmd *cmd, int success) +{ + struct se_wwn *wwn = cmd->se_sess->se_tpg->se_tpg_wwn; + struct se_dev_attrib *da; + u8 compl_type; + int cpu; + + if (!wwn) { + cpu = cmd->cpuid; + goto queue_work; + } + + da = &cmd->se_dev->dev_attrib; + if (da->complete_type == TARGET_FABRIC_DEFAULT_COMPL) + compl_type = wwn->wwn_tf->tf_ops->default_compl_type; + else if (da->complete_type == TARGET_DIRECT_COMPL && + wwn->wwn_tf->tf_ops->direct_compl_supp) + compl_type = TARGET_DIRECT_COMPL; + else + compl_type = TARGET_QUEUE_COMPL; + + if (compl_type == TARGET_DIRECT_COMPL) { + /* + * Failure handling and processing secondary stages of + * complex commands can be too heavy to handle from the + * fabric driver so always defer. + */ + if (success && !cmd->transport_complete_callback) { + target_complete_ok_work(&cmd->work); + return; + } + + compl_type = TARGET_QUEUE_COMPL; + } + +queue_work: + INIT_WORK(&cmd->work, success ? target_complete_ok_work : + target_complete_failure_work); + + if (!wwn || wwn->cmd_compl_affinity == SE_COMPL_AFFINITY_CPUID) + cpu = cmd->cpuid; + else + cpu = wwn->cmd_compl_affinity; + + queue_work_on(cpu, target_completion_wq, &cmd->work); +} + /* May be called from interrupt context so must not sleep. */ void target_complete_cmd_with_sense(struct se_cmd *cmd, u8 scsi_status, sense_reason_t sense_reason) { - struct se_wwn *wwn = cmd->se_sess->se_tpg->se_tpg_wwn; - int success, cpu; unsigned long flags; + int success; if (target_cmd_interrupted(cmd)) return; @@ -933,15 +979,7 @@ void target_complete_cmd_with_sense(struct se_cmd *cmd, u8 scsi_status, cmd->transport_state |= (CMD_T_COMPLETE | CMD_T_ACTIVE); spin_unlock_irqrestore(&cmd->t_state_lock, flags); - INIT_WORK(&cmd->work, success ? target_complete_ok_work : - target_complete_failure_work); - - if (!wwn || wwn->cmd_compl_affinity == SE_COMPL_AFFINITY_CPUID) - cpu = cmd->cpuid; - else - cpu = wwn->cmd_compl_affinity; - - queue_work_on(cpu, target_completion_wq, &cmd->work); + target_complete(cmd, success); } EXPORT_SYMBOL(target_complete_cmd_with_sense); @@ -1112,7 +1150,7 @@ void transport_dump_vpd_proto_id( } if (p_buf) - strncpy(p_buf, buf, p_buf_len); + strscpy(p_buf, buf, p_buf_len); else pr_debug("%s", buf); } @@ -1162,7 +1200,7 @@ int transport_dump_vpd_assoc( } if (p_buf) - strncpy(p_buf, buf, p_buf_len); + strscpy(p_buf, buf, p_buf_len); else pr_debug("%s", buf); @@ -1222,7 +1260,7 @@ int transport_dump_vpd_ident_type( if (p_buf) { if (p_buf_len < strlen(buf)+1) return -EINVAL; - strncpy(p_buf, buf, p_buf_len); + strscpy(p_buf, buf, p_buf_len); } else { pr_debug("%s", buf); } @@ -1276,7 +1314,7 @@ int transport_dump_vpd_ident( } if (p_buf) - strncpy(p_buf, buf, p_buf_len); + strscpy(p_buf, buf, p_buf_len); else pr_debug("%s", buf); diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index 88cf1e5a5810..3920fb02d9fc 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -434,6 +434,7 @@ static const struct target_core_fabric_ops ft_fabric_ops = { .tfc_wwn_attrs = ft_wwn_attrs, .tfc_tpg_nacl_base_attrs = ft_nacl_base_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_DIRECT_SUBMIT, .direct_submit_supp = 1, }; diff --git a/drivers/tee/optee/device.c b/drivers/tee/optee/device.c index bae954e79fdc..f751d743a120 100644 --- a/drivers/tee/optee/device.c +++ b/drivers/tee/optee/device.c @@ -13,10 +13,7 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) { - if (ver->impl_id == TEE_IMPL_ID_OPTEE) - return 1; - else - return 0; + return (ver->impl_id == TEE_IMPL_ID_OPTEE); } static int get_devices(struct tee_context *ctx, u32 session, diff --git a/drivers/thunderbolt/dma_port.c b/drivers/thunderbolt/dma_port.c index 334fefe21255..c7c2942fa7be 100644 --- a/drivers/thunderbolt/dma_port.c +++ b/drivers/thunderbolt/dma_port.c @@ -55,7 +55,7 @@ struct tb_dma_port { struct tb_switch *sw; u8 port; u32 base; - u8 *buf; + u8 buf[]; }; /* @@ -209,16 +209,10 @@ struct tb_dma_port *dma_port_alloc(struct tb_switch *sw) if (port < 0) return NULL; - dma = kzalloc_obj(*dma); + dma = kzalloc_flex(*dma, buf, MAIL_DATA_DWORDS); if (!dma) return NULL; - dma->buf = kmalloc_array(MAIL_DATA_DWORDS, sizeof(u32), GFP_KERNEL); - if (!dma->buf) { - kfree(dma); - return NULL; - } - dma->sw = sw; dma->port = port; dma->base = DMA_PORT_CAP; @@ -232,10 +226,7 @@ struct tb_dma_port *dma_port_alloc(struct tb_switch *sw) */ void dma_port_free(struct tb_dma_port *dma) { - if (dma) { - kfree(dma->buf); - kfree(dma); - } + kfree(dma); } static int dma_port_wait_for_completion(struct tb_dma_port *dma, diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c index 22fb4a1e1acd..8713ea0f47c1 100644 --- a/drivers/thunderbolt/path.c +++ b/drivers/thunderbolt/path.c @@ -150,22 +150,17 @@ struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid, num_hops++; } - path = kzalloc_obj(*path); + path = kzalloc_flex(*path, hops, num_hops); if (!path) return NULL; + path->path_length = num_hops; + path->name = name; path->tb = src->sw->tb; - path->path_length = num_hops; path->activated = true; path->alloc_hopid = alloc_hopid; - path->hops = kzalloc_objs(*path->hops, num_hops); - if (!path->hops) { - kfree(path); - return NULL; - } - tb_dbg(path->tb, "discovering %s path starting from %llx:%u\n", path->name, tb_route(src->sw), src->port); @@ -245,10 +240,6 @@ struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid, size_t num_hops; int i, ret; - path = kzalloc_obj(*path); - if (!path) - return NULL; - first_port = last_port = NULL; i = 0; tb_for_each_port_on_path(src, dst, in_port) { @@ -259,20 +250,17 @@ struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid, } /* Check that src and dst are reachable */ - if (first_port != src || last_port != dst) { - kfree(path); + if (first_port != src || last_port != dst) return NULL; - } /* Each hop takes two ports */ num_hops = i / 2; - path->hops = kzalloc_objs(*path->hops, num_hops); - if (!path->hops) { - kfree(path); + path = kzalloc_flex(*path, hops, num_hops); + if (!path) return NULL; - } + path->path_length = num_hops; path->alloc_hopid = true; in_hopid = src_hopid; @@ -339,7 +327,6 @@ struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid, } path->tb = tb; - path->path_length = num_hops; path->name = name; return path; @@ -372,7 +359,6 @@ void tb_path_free(struct tb_path *path) } } - kfree(path->hops); kfree(path); } diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c index e81de9c30eac..9f7914ac2f48 100644 --- a/drivers/thunderbolt/quirks.c +++ b/drivers/thunderbolt/quirks.c @@ -23,6 +23,9 @@ static void quirk_dp_credit_allocation(struct tb_switch *sw) static void quirk_clx_disable(struct tb_switch *sw) { + if (tb_switch_is_titan_ridge(sw) && sw->nvm && sw->nvm->major >= 0x65) + return; + sw->quirks |= QUIRK_NO_CLX; tb_sw_dbg(sw, "disabling CL states\n"); } @@ -61,6 +64,10 @@ static const struct tb_quirk tb_quirks[] = { /* Dell WD19TB supports self-authentication on unplug */ { 0x0000, 0x0000, 0x00d4, 0xb070, quirk_force_power_link }, { 0x0000, 0x0000, 0x00d4, 0xb071, quirk_force_power_link }, + + /* Intel Titan Ridge CLx is unstable on early firmware versions */ + { 0x8086, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE, 0x0000, 0x0000, + quirk_clx_disable }, /* * Intel Goshen Ridge NVM 27 and before report wrong number of * DP buffers. diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index e5b48a331c58..c2ad58b19e7b 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -347,7 +347,7 @@ static int nvm_write(void *priv, unsigned int offset, void *val, size_t bytes) return ret; } -static int tb_switch_nvm_add(struct tb_switch *sw) +static int tb_switch_nvm_init(struct tb_switch *sw) { struct tb_nvm *nvm; int ret; @@ -365,6 +365,26 @@ static int tb_switch_nvm_add(struct tb_switch *sw) if (ret) goto err_nvm; + sw->nvm = nvm; + return 0; + +err_nvm: + tb_sw_dbg(sw, "NVM upgrade disabled\n"); + sw->no_nvm_upgrade = true; + if (!IS_ERR(nvm)) + tb_nvm_free(nvm); + + return ret; +} + +static int tb_switch_nvm_add(struct tb_switch *sw) +{ + struct tb_nvm *nvm = sw->nvm; + int ret; + + if (!nvm) + return 0; + /* * If the switch is in safe-mode the only accessible portion of * the NVM is the non-active one where userspace is expected to @@ -383,14 +403,12 @@ static int tb_switch_nvm_add(struct tb_switch *sw) goto err_nvm; } - sw->nvm = nvm; return 0; err_nvm: tb_sw_dbg(sw, "NVM upgrade disabled\n"); sw->no_nvm_upgrade = true; - if (!IS_ERR(nvm)) - tb_nvm_free(nvm); + tb_nvm_free(nvm); return ret; } @@ -3311,6 +3329,10 @@ int tb_switch_add(struct tb_switch *sw) return ret; } + ret = tb_switch_nvm_init(sw); + if (ret) + return ret; + if (!sw->safe_mode) { tb_switch_credits_init(sw); diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index e96474f17067..217c3114bec8 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -419,9 +419,9 @@ enum tb_path_port { * @activated: Is the path active * @clear_fc: Clear all flow control from the path config space entries * when deactivating this path - * @hops: Path hops * @path_length: How many hops the path uses * @alloc_hopid: Does this path consume port HopID + * @hops: Path hops * * A path consists of a number of hops (see &struct tb_path_hop). To * establish a PCIe tunnel two paths have to be created between the two @@ -440,9 +440,10 @@ struct tb_path { bool drop_packages; bool activated; bool clear_fc; - struct tb_path_hop *hops; int path_length; bool alloc_hopid; + + struct tb_path_hop hops[] __counted_by(path_length); }; /* HopIDs 0-7 are reserved by the Thunderbolt protocol */ diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 89676acf1290..f38f7753b6e4 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -180,19 +180,14 @@ static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths, { struct tb_tunnel *tunnel; - tunnel = kzalloc_obj(*tunnel); + tunnel = kzalloc_flex(*tunnel, paths, npaths); if (!tunnel) return NULL; - tunnel->paths = kzalloc_objs(tunnel->paths[0], npaths); - if (!tunnel->paths) { - kfree(tunnel); - return NULL; - } + tunnel->npaths = npaths; INIT_LIST_HEAD(&tunnel->list); tunnel->tb = tb; - tunnel->npaths = npaths; tunnel->type = type; kref_init(&tunnel->kref); @@ -219,7 +214,6 @@ static void tb_tunnel_destroy(struct kref *kref) tb_path_free(tunnel->paths[i]); } - kfree(tunnel->paths); kfree(tunnel); } diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h index 2c44fc8a10bc..4878763a82b3 100644 --- a/drivers/thunderbolt/tunnel.h +++ b/drivers/thunderbolt/tunnel.h @@ -37,7 +37,6 @@ enum tb_tunnel_state { * @src_port: Source port of the tunnel * @dst_port: Destination port of the tunnel. For discovered incomplete * tunnels may be %NULL or null adapter port instead. - * @paths: All paths required by the tunnel * @npaths: Number of paths in @paths * @pre_activate: Optional tunnel specific initialization called before * activation. Can touch hardware. @@ -69,13 +68,13 @@ enum tb_tunnel_state { * @dprx_work: Worker that is scheduled to poll completion of DPRX capabilities read * @callback: Optional callback called when DP tunnel is fully activated * @callback_data: Optional data for @callback + * @paths: All paths required by the tunnel */ struct tb_tunnel { struct kref kref; struct tb *tb; struct tb_port *src_port; struct tb_port *dst_port; - struct tb_path **paths; size_t npaths; int (*pre_activate)(struct tb_tunnel *tunnel); int (*activate)(struct tb_tunnel *tunnel, bool activate); @@ -107,6 +106,8 @@ struct tb_tunnel { struct delayed_work dprx_work; void (*callback)(struct tb_tunnel *tunnel, void *data); void *callback_data; + + struct tb_path *paths[] __counted_by(npaths); }; struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down, diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index 1dcdb9e99bd8..37db8a3e5158 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -130,7 +130,7 @@ static struct iucv_handler hvc_iucv_handler = { */ static struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num) { - if (num > hvc_iucv_devices) + if (num >= hvc_iucv_devices) return NULL; return hvc_iucv_table[num]; } diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index cb427e93372d..cc7f7091ed9a 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -532,14 +532,16 @@ static void __init legacy_pty_init(void) pty_driver = tty_alloc_driver(legacy_count, TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_ALLOC); + TTY_DRIVER_DYNAMIC_ALLOC | + TTY_DRIVER_NO_WORKQUEUE); if (IS_ERR(pty_driver)) panic("Couldn't allocate pty driver"); pty_slave_driver = tty_alloc_driver(legacy_count, TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_ALLOC); + TTY_DRIVER_DYNAMIC_ALLOC | + TTY_DRIVER_NO_WORKQUEUE); if (IS_ERR(pty_slave_driver)) panic("Couldn't allocate pty slave driver"); @@ -849,7 +851,8 @@ static void __init unix98_pty_init(void) TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM | - TTY_DRIVER_DYNAMIC_ALLOC); + TTY_DRIVER_DYNAMIC_ALLOC | + TTY_DRIVER_NO_WORKQUEUE); if (IS_ERR(ptm_driver)) panic("Couldn't allocate Unix98 ptm driver"); pts_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX, @@ -857,7 +860,8 @@ static void __init unix98_pty_init(void) TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM | - TTY_DRIVER_DYNAMIC_ALLOC); + TTY_DRIVER_DYNAMIC_ALLOC | + TTY_DRIVER_NO_WORKQUEUE); if (IS_ERR(pts_driver)) panic("Couldn't allocate Unix98 pts driver"); diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index d2e2c5dfef99..a428e88938eb 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -524,7 +524,7 @@ console_initcall(univ8250_console_init); struct uart_driver serial8250_reg = { .owner = THIS_MODULE, - .driver_name = "serial", + .driver_name = "serial_8250", .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index b4461a89b8d0..976c5748905c 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Probe for F81216A LPC to 4 UART + * Probe for F81216A LPC to 4 UART and F81214E LPC/eSPI to 2 UART * * Copyright (C) 2014-2016 Ricardo Ribalda, Qtechnology A/S */ @@ -23,6 +23,7 @@ #define CHIP_ID_F81216AD 0x1602 #define CHIP_ID_F81216E 0x1617 #define CHIP_ID_F81216H 0x0501 +#define CHIP_ID_F81214E 0x1417 #define CHIP_ID_F81216 0x0802 #define VENDOR_ID1 0x23 #define VENDOR_ID1_VAL 0x19 @@ -161,6 +162,7 @@ static int fintek_8250_check_id(struct fintek_8250 *pdata) case CHIP_ID_F81216AD: case CHIP_ID_F81216E: case CHIP_ID_F81216H: + case CHIP_ID_F81214E: case CHIP_ID_F81216: break; default: @@ -185,6 +187,7 @@ static int fintek_8250_get_ldn_range(struct fintek_8250 *pdata, int *min, case CHIP_ID_F81216AD: case CHIP_ID_F81216E: case CHIP_ID_F81216H: + case CHIP_ID_F81214E: case CHIP_ID_F81216: *min = F81216_LDN_LOW; *max = F81216_LDN_HIGH; @@ -255,6 +258,7 @@ static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) case CHIP_ID_F81216AD: case CHIP_ID_F81216E: case CHIP_ID_F81216H: + case CHIP_ID_F81214E: case CHIP_ID_F81216: sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, IRQ_SHARE); @@ -269,6 +273,7 @@ static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata) switch (pdata->pid) { case CHIP_ID_F81216E: /* 128Bytes FIFO */ case CHIP_ID_F81216H: + case CHIP_ID_F81214E: case CHIP_ID_F81966: case CHIP_ID_F81866: sio_write_mask_reg(pdata, FIFO_CTRL, @@ -304,6 +309,7 @@ static void fintek_8250_set_termios(struct uart_port *port, switch (pdata->pid) { case CHIP_ID_F81216E: case CHIP_ID_F81216H: + case CHIP_ID_F81214E: reg = RS485; break; case CHIP_ID_F81966: @@ -354,6 +360,7 @@ static void fintek_8250_set_termios_handler(struct uart_8250_port *uart) switch (pdata->pid) { case CHIP_ID_F81216E: case CHIP_ID_F81216H: + case CHIP_ID_F81214E: case CHIP_ID_F81966: case CHIP_ID_F81866: uart->port.set_termios = fintek_8250_set_termios; @@ -446,6 +453,7 @@ static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) break; case CHIP_ID_F81216E: /* F81216E does not support RS485 delays */ + case CHIP_ID_F81214E: /* F81214E does not support RS485 delays */ uart->port.rs485_config = fintek_8250_rs485_config; uart->port.rs485_supported = fintek_8250_rs485_supported; break; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 6cfd1b2af5b7..2fbd8f2603b5 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -100,6 +100,8 @@ #define PCI_DEVICE_ID_ADDIDATA_CPCI7420_NG 0x7025 #define PCI_DEVICE_ID_ADDIDATA_CPCI7300_NG 0x7026 +#define PCI_VENDOR_ID_SYSTEMBASE 0x14a1 + /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 #define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588 @@ -2133,6 +2135,35 @@ pci_moxa_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, 0); } +#define SB_OPTR_IMR0 0x0c /* Interrupt mask register, p0 to p7 */ +static int pci_systembase_init(struct pci_dev *dev) +{ + resource_size_t iobase; + + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(dev); + + iobase = pci_resource_start(dev, 1); + + /* This will support up to 8 ports */ + outb(0xff, iobase + SB_OPTR_IMR0); + + return 0; +} + +static void pci_systembase_exit(struct pci_dev *dev) +{ + resource_size_t iobase; + + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) { + serial_8250_warn_need_ioport(dev); + return; + } + + iobase = pci_resource_start(dev, 0); + outb(0x00, iobase + SB_OPTR_IMR0); +} + /* * Master list of serial port init/setup/exit quirks. * This does not describe the general nature of the port. @@ -2481,6 +2512,16 @@ static struct pci_serial_quirk pci_serial_quirks[] = { .init = pci_siig_init, .setup = pci_siig_setup, }, + /* Systembase */ + { + .vendor = PCI_VENDOR_ID_SYSTEMBASE, + .device = 0x0008, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_systembase_init, + .setup = pci_default_setup, + .exit = pci_systembase_exit, + }, /* * Titan cards */ @@ -3054,6 +3095,7 @@ enum pci_board_num_t { pbn_b0_1_921600, pbn_b0_2_921600, pbn_b0_4_921600, + pbn_b0_8_921600, pbn_b0_2_1130000, @@ -3254,6 +3296,12 @@ static struct pciserial_board pci_boards[] = { .base_baud = 921600, .uart_offset = 8, }, + [pbn_b0_8_921600] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, [pbn_b0_2_1130000] = { .flags = FL_BASE0, @@ -6169,6 +6217,9 @@ static const struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_1_115200 }, + /* Systembase Multi I/O cards */ + { PCI_VDEVICE(SYSTEMBASE, 0x0008), pbn_b0_8_921600 }, + /* Fintek PCI serial cards */ { PCI_DEVICE(0x1c29, 0x1104), .driver_data = pbn_fintek_4 }, { PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 }, diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 328711b5df1a..af78cc02f38e 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2372,8 +2372,8 @@ void serial8250_do_shutdown(struct uart_port *port) synchronize_irq(port->irq); - if (up->dma) - serial8250_release_dma(up); + serial8250_release_dma(up); + up->dma = NULL; scoped_guard(uart_port_lock_irqsave, port) { if (port->flags & UPF_FOURPORT) { diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index fd4e8b6ab60d..fc3e58d62233 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -465,11 +465,12 @@ config SERIAL_8250_OMAP_TTYO_FIXUP config SERIAL_8250_LOONGSON tristate "Loongson 8250 based serial port" depends on SERIAL_8250 - depends on LOONGARCH || COMPILE_TEST + depends on LOONGARCH || MACH_LOONGSON64 || COMPILE_TEST help - If you have a machine based on LoongArch CPU you can enable - its onboard serial ports by enabling this option. The option - is applicable to both devicetree and ACPI, say Y to this option. + If you have a machine based on LoongArch CPU or MIPS-based Loongson + 3A4000 CPU you can enable its onboard serial ports by enabling this + option. The option is applicable to both devicetree and ACPI, say Y + to enable this option. If unsure, say N. config SERIAL_8250_LPC18XX diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index f86775cfdcc9..9aa61c93d7bc 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -255,14 +255,13 @@ config SERIAL_SAMSUNG_CONSOLE config SERIAL_TEGRA tristate "NVIDIA Tegra20/30 SoC serial controller" - depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST + depends on ARCH_TEGRA || COMPILE_TEST select SERIAL_CORE help Support for the on-chip UARTs on the NVIDIA Tegra series SOCs providing /dev/ttyTHS0, 1, 2, 3 and 4 (note, some machines may not provide all of these ports, depending on how the serial port - are enabled). This driver uses the APB DMA to achieve higher baudrate - and better performance. + are enabled). config SERIAL_TEGRA_TCU tristate "NVIDIA Tegra Combined UART" @@ -803,7 +802,7 @@ config SERIAL_CPM_CONSOLE config SERIAL_PIC32 tristate "Microchip PIC32 serial support" - depends on MACH_PIC32 || (MIPS && COMPILE_TEST) + depends on MACH_PIC32 || COMPILE_TEST select SERIAL_CORE help If you have a PIC32, this driver supports the serial ports. @@ -1593,32 +1592,6 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE but you can alter that using a kernel command line option such as "console=ttyNVTx". -config SERIAL_ESP32 - tristate "Espressif ESP32 UART support" - depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF) - select SERIAL_CORE - select SERIAL_CORE_CONSOLE - select SERIAL_EARLYCON - help - Driver for the UART controllers of the Espressif ESP32xx SoCs. - When earlycon option is enabled the following kernel command line - snippets may be used: - earlycon=esp32s3uart,mmio32,0x60000000,115200n8,40000000 - earlycon=esp32uart,mmio32,0x3ff40000,115200n8 - -config SERIAL_ESP32_ACM - tristate "Espressif ESP32 USB ACM gadget support" - depends on XTENSA_PLATFORM_ESP32 || (COMPILE_TEST && OF) - select SERIAL_CORE - select SERIAL_CORE_CONSOLE - select SERIAL_EARLYCON - help - Driver for the CDC ACM gadget controller of the Espressif ESP32S3 - SoCs that share separate USB controller with the JTAG adapter. - When earlycon option is enabled the following kernel command line - snippet may be used: - earlycon=esp32s3acm,mmio32,0x60038000 - endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index a2ccbc508ec5..bba7b21a4a1d 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -37,8 +37,6 @@ obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o obj-$(CONFIG_SERIAL_CPM) += cpm_uart.o obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR) += digicolor-usart.o obj-$(CONFIG_SERIAL_DZ) += dz.o -obj-$(CONFIG_SERIAL_ESP32) += esp32_uart.o -obj-$(CONFIG_SERIAL_ESP32_ACM) += esp32_acm.o obj-$(CONFIG_SERIAL_FSL_LINFLEXUART) += fsl_linflexuart.o obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o obj-$(CONFIG_SERIAL_ICOM) += icom.o diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 7f17d288c807..028e37ad8d79 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -114,6 +114,8 @@ struct vendor_data { bool cts_event_workaround; bool always_enabled; bool fixed_options; + bool skip_ibrd_fbrd; + bool set_uartclk_rate; unsigned int (*get_fifosize)(struct amba_device *dev); }; @@ -216,6 +218,28 @@ static struct vendor_data vendor_st = { .get_fifosize = get_fifosize_st, }; +static unsigned int get_fifosize_nvidia(struct amba_device *dev) +{ + return 32; +} + +static struct vendor_data vendor_nvidia = { + .reg_offset = pl011_std_offsets, + .ifls = UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8, + .fr_busy = UART01x_FR_BUSY, + .fr_dsr = UART01x_FR_DSR, + .fr_cts = UART01x_FR_CTS, + .fr_ri = UART011_FR_RI, + .oversampling = false, + .dma_threshold = false, + .cts_event_workaround = false, + .always_enabled = false, + .fixed_options = false, + .skip_ibrd_fbrd = true, + .set_uartclk_rate = true, + .get_fifosize = get_fifosize_nvidia, +}; + /* Deals with DMA transactions */ struct pl011_dmabuf { @@ -625,6 +649,15 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) count = PL011_DMA_BUFFER_SIZE; count = kfifo_out_peek(&tport->xmit_fifo, dmatx->buf, count); + + /* + * Align the TX buffer length to the DMA controller's copy_align + * requirements. Some DMA controllers (e.g., Tegra GPC DMA) require + * word-aligned transfers. Unaligned bytes will be sent via PIO. + */ + if (chan->device->copy_align) + count = ALIGN_DOWN(count, 1 << chan->device->copy_align); + dmatx->len = count; dmatx->dma = dma_map_single(dma_dev->dev, dmatx->buf, count, DMA_TO_DEVICE); @@ -2095,6 +2128,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, unsigned int lcr_h, old_cr; unsigned long flags; unsigned int baud, quot, clkdiv; + unsigned int max_baud; unsigned int bits; if (uap->vendor->oversampling) @@ -2102,11 +2136,34 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, else clkdiv = 16; + max_baud = port->uartclk / clkdiv; + + if (uap->vendor->set_uartclk_rate) { + long max_clkrate = clk_round_rate(uap->clk, UINT_MAX); + + /* + * Clock is reprogrammable - determine max baud from the clock's + * maximum rate, not the current uartclk. + */ + if (max_clkrate > 0) + max_baud = max_clkrate / clkdiv; + } + /* * Ask the core to calculate the divisor for us. */ - baud = uart_get_baud_rate(port, termios, old, 0, - port->uartclk / clkdiv); + baud = uart_get_baud_rate(port, termios, old, 0, max_baud); + + if (uap->vendor->set_uartclk_rate) { + int err; + + err = clk_set_rate(uap->clk, baud * clkdiv); + if (err) { + dev_err(port->dev, "Failed to set clock rate: %d\n", err); + return; + } + } + #ifdef CONFIG_DMA_ENGINE /* * Adjust RX DMA polling rate with baud rate if not specified. @@ -2115,11 +2172,6 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, uap->dmarx.poll_rate = DIV_ROUND_UP(10000000, baud); #endif - if (baud > port->uartclk / 16) - quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); - else - quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); - switch (termios->c_cflag & CSIZE) { case CS5: lcr_h = UART01x_LCRH_WLEN_5; @@ -2190,21 +2242,28 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, old_cr &= ~ST_UART011_CR_OVSFACT; } - /* - * Workaround for the ST Micro oversampling variants to - * increase the bitrate slightly, by lowering the divisor, - * to avoid delayed sampling of start bit at high speeds, - * else we see data corruption. - */ - if (uap->vendor->oversampling) { - if (baud >= 3000000 && baud < 3250000 && quot > 1) - quot -= 1; - else if (baud > 3250000 && quot > 2) - quot -= 2; + if (!uap->vendor->skip_ibrd_fbrd) { + if (baud > port->uartclk / 16) + quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); + else + quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); + + /* + * Workaround for the ST Micro oversampling variants to + * increase the bitrate slightly, by lowering the divisor, + * to avoid delayed sampling of start bit at high speeds, + * else we see data corruption. + */ + if (uap->vendor->oversampling) { + if (baud >= 3000000 && baud < 3250000 && quot > 1) + quot -= 1; + else if (baud > 3250000 && quot > 2) + quot -= 2; + } + /* Set baud rate */ + pl011_write(quot & 0x3f, uap, REG_FBRD); + pl011_write(quot >> 6, uap, REG_IBRD); } - /* Set baud rate */ - pl011_write(quot & 0x3f, uap, REG_FBRD); - pl011_write(quot >> 6, uap, REG_IBRD); /* * ----------v----------v----------v----------v----- @@ -2374,6 +2433,7 @@ static void pl011_console_get_options(struct uart_amba_port *uap, int *baud, int *parity, int *bits) { unsigned int lcr_h, ibrd, fbrd; + unsigned int clkdiv; if (!(pl011_read(uap, REG_CR) & UART01x_CR_UARTEN)) return; @@ -2393,10 +2453,15 @@ static void pl011_console_get_options(struct uart_amba_port *uap, int *baud, else *bits = 8; - ibrd = pl011_read(uap, REG_IBRD); - fbrd = pl011_read(uap, REG_FBRD); + if (uap->vendor->skip_ibrd_fbrd) { + clkdiv = 64; + } else { + ibrd = pl011_read(uap, REG_IBRD); + fbrd = pl011_read(uap, REG_FBRD); + clkdiv = 64 * ibrd + fbrd; + } - *baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd); + *baud = uap->port.uartclk * 4 / clkdiv; if (uap->vendor->oversampling && (pl011_read(uap, REG_CR) & ST_UART011_CR_OVSFACT)) @@ -2700,6 +2765,37 @@ static int pl011_early_read(struct console *con, char *s, unsigned int n) */ static int __init pl011_early_console_setup(struct earlycon_device *device, const char *opt) +{ + unsigned int cr; + + if (!device->port.membase) + return -ENODEV; + + device->con->write = pl011_early_write; + device->con->read = pl011_early_read; + + if (device->port.iotype == UPIO_MEM32) + cr = readl(device->port.membase + UART011_CR); + else + cr = readw(device->port.membase + UART011_CR); + cr &= UART011_CR_RTS | UART011_CR_DTR; + cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; + if (device->port.iotype == UPIO_MEM32) + writel(cr, device->port.membase + UART011_CR); + else + writew(cr, device->port.membase + UART011_CR); + + return 0; +} + +OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup); + +/* + * The SBSA UART has no defined control register and is assumed to + * be pre-enabled by firmware, so we do not write to UART011_CR. + */ +static int __init sbsa_uart_early_console_setup(struct earlycon_device *device, + const char *opt) { if (!device->port.membase) return -ENODEV; @@ -2710,9 +2806,7 @@ static int __init pl011_early_console_setup(struct earlycon_device *device, return 0; } -OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup); - -OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", pl011_early_console_setup); +OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", sbsa_uart_early_console_setup); /* * On Qualcomm Datacenter Technologies QDF2400 SOCs affected by @@ -3081,6 +3175,11 @@ static const struct amba_id pl011_ids[] = { .mask = 0x00ffffff, .data = &vendor_st, }, + { + .id = 0x0006b011, + .mask = 0x000fffff, + .data = &vendor_nvidia, + }, { 0, 0 }, }; diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c index 364599f256db..3e46341cfff8 100644 --- a/drivers/tty/serial/apbuart.c +++ b/drivers/tty/serial/apbuart.c @@ -505,7 +505,7 @@ console_initcall(apbuart_console_init); static struct uart_driver grlib_apbuart_driver = { .owner = THIS_MODULE, - .driver_name = "serial", + .driver_name = "serial_apbuart", .dev_name = "ttyS", .major = SERIAL_APBUART_MAJOR, .minor = SERIAL_APBUART_MINOR, diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 08dd8f887956..5d8c1cfc1c60 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1927,7 +1927,7 @@ static int atmel_startup(struct uart_port *port) atmel_uart_writel(port, ATMEL_US_FMR, fmr); } - /* Save current CSR for comparison in atmel_tasklet_func() */ + /* Save current CSR for comparison in atmel_handle_status() */ atmel_port->irq_status_prev = atmel_uart_readl(port, ATMEL_US_CSR); /* diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index eba91daedef8..e53c54353c3e 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -914,7 +914,7 @@ console_initcall(dz_serial_console_init); static struct uart_driver dz_reg = { .owner = THIS_MODULE, - .driver_name = "serial", + .driver_name = "serial_dz", .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, diff --git a/drivers/tty/serial/esp32_acm.c b/drivers/tty/serial/esp32_acm.c deleted file mode 100644 index bb7cc65427f0..000000000000 --- a/drivers/tty/serial/esp32_acm.c +++ /dev/null @@ -1,459 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "esp32s3-acm" -#define DEV_NAME "ttyGS" -#define UART_NR 4 - -#define ESP32S3_ACM_TX_FIFO_SIZE 64 - -#define USB_SERIAL_JTAG_EP1_REG 0x00 -#define USB_SERIAL_JTAG_EP1_CONF_REG 0x04 -#define USB_SERIAL_JTAG_WR_DONE BIT(0) -#define USB_SERIAL_JTAG_SERIAL_IN_EP_DATA_FREE BIT(1) -#define USB_SERIAL_JTAG_INT_ST_REG 0x0c -#define USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ST BIT(2) -#define USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ST BIT(3) -#define USB_SERIAL_JTAG_INT_ENA_REG 0x10 -#define USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA BIT(2) -#define USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA BIT(3) -#define USB_SERIAL_JTAG_INT_CLR_REG 0x14 -#define USB_SERIAL_JTAG_IN_EP1_ST_REG 0x2c -#define USB_SERIAL_JTAG_IN_EP1_WR_ADDR GENMASK(8, 2) -#define USB_SERIAL_JTAG_OUT_EP1_ST_REG 0x3c -#define USB_SERIAL_JTAG_OUT_EP1_REC_DATA_CNT GENMASK(22, 16) - -static const struct of_device_id esp32s3_acm_dt_ids[] = { - { - .compatible = "esp,esp32s3-acm", - }, { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, esp32s3_acm_dt_ids); - -static struct uart_port *esp32s3_acm_ports[UART_NR]; - -static void esp32s3_acm_write(struct uart_port *port, unsigned long reg, u32 v) -{ - writel(v, port->membase + reg); -} - -static u32 esp32s3_acm_read(struct uart_port *port, unsigned long reg) -{ - return readl(port->membase + reg); -} - -static u32 esp32s3_acm_tx_fifo_free(struct uart_port *port) -{ - u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_CONF_REG); - - return status & USB_SERIAL_JTAG_SERIAL_IN_EP_DATA_FREE; -} - -static u32 esp32s3_acm_tx_fifo_cnt(struct uart_port *port) -{ - u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_IN_EP1_ST_REG); - - return FIELD_GET(USB_SERIAL_JTAG_IN_EP1_WR_ADDR, status); -} - -static u32 esp32s3_acm_rx_fifo_cnt(struct uart_port *port) -{ - u32 status = esp32s3_acm_read(port, USB_SERIAL_JTAG_OUT_EP1_ST_REG); - - return FIELD_GET(USB_SERIAL_JTAG_OUT_EP1_REC_DATA_CNT, status); -} - -/* return TIOCSER_TEMT when transmitter is not busy */ -static unsigned int esp32s3_acm_tx_empty(struct uart_port *port) -{ - return esp32s3_acm_tx_fifo_cnt(port) == 0 ? TIOCSER_TEMT : 0; -} - -static void esp32s3_acm_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} - -static unsigned int esp32s3_acm_get_mctrl(struct uart_port *port) -{ - return TIOCM_CAR; -} - -static void esp32s3_acm_stop_tx(struct uart_port *port) -{ - u32 int_ena; - - int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG); - int_ena &= ~USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA; - esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena); -} - -static void esp32s3_acm_rxint(struct uart_port *port) -{ - struct tty_port *tty_port = &port->state->port; - u32 rx_fifo_cnt = esp32s3_acm_rx_fifo_cnt(port); - unsigned long flags; - u32 i; - - if (!rx_fifo_cnt) - return; - - spin_lock_irqsave(&port->lock, flags); - - for (i = 0; i < rx_fifo_cnt; ++i) { - u32 rx = esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_REG); - - ++port->icount.rx; - tty_insert_flip_char(tty_port, rx, TTY_NORMAL); - } - spin_unlock_irqrestore(&port->lock, flags); - - tty_flip_buffer_push(tty_port); -} - -static void esp32s3_acm_push(struct uart_port *port) -{ - if (esp32s3_acm_tx_fifo_free(port)) - esp32s3_acm_write(port, USB_SERIAL_JTAG_EP1_CONF_REG, - USB_SERIAL_JTAG_WR_DONE); -} - -static void esp32s3_acm_put_char(struct uart_port *port, u8 c) -{ - esp32s3_acm_write(port, USB_SERIAL_JTAG_EP1_REG, c); -} - -static void esp32s3_acm_put_char_sync(struct uart_port *port, u8 c) -{ - unsigned long timeout = jiffies + HZ; - - while (!esp32s3_acm_tx_fifo_free(port)) { - if (time_after(jiffies, timeout)) { - dev_warn(port->dev, "timeout waiting for TX FIFO\n"); - return; - } - cpu_relax(); - } - esp32s3_acm_put_char(port, c); - esp32s3_acm_push(port); -} - -static void esp32s3_acm_transmit_buffer(struct uart_port *port) -{ - u32 tx_fifo_used; - unsigned int pending; - u8 ch; - - if (!esp32s3_acm_tx_fifo_free(port)) - return; - - tx_fifo_used = esp32s3_acm_tx_fifo_cnt(port); - pending = uart_port_tx_limited(port, ch, - ESP32S3_ACM_TX_FIFO_SIZE - tx_fifo_used, - true, esp32s3_acm_put_char(port, ch), - ({})); - if (pending) { - u32 int_ena; - - int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG); - int_ena |= USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ENA; - esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena); - } - esp32s3_acm_push(port); -} - -static void esp32s3_acm_txint(struct uart_port *port) -{ - esp32s3_acm_transmit_buffer(port); -} - -static irqreturn_t esp32s3_acm_int(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - u32 status; - - status = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ST_REG); - esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_CLR_REG, status); - - if (status & USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ST) - esp32s3_acm_rxint(port); - if (status & USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT_ST) - esp32s3_acm_txint(port); - - return IRQ_RETVAL(status); -} - -static void esp32s3_acm_start_tx(struct uart_port *port) -{ - esp32s3_acm_transmit_buffer(port); -} - -static void esp32s3_acm_stop_rx(struct uart_port *port) -{ - u32 int_ena; - - int_ena = esp32s3_acm_read(port, USB_SERIAL_JTAG_INT_ENA_REG); - int_ena &= ~USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA; - esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, int_ena); -} - -static int esp32s3_acm_startup(struct uart_port *port) -{ - int ret; - - ret = request_irq(port->irq, esp32s3_acm_int, 0, DRIVER_NAME, port); - if (ret) - return ret; - esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, - USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT_INT_ENA); - - return 0; -} - -static void esp32s3_acm_shutdown(struct uart_port *port) -{ - esp32s3_acm_write(port, USB_SERIAL_JTAG_INT_ENA_REG, 0); - free_irq(port->irq, port); -} - -static void esp32s3_acm_set_termios(struct uart_port *port, - struct ktermios *termios, - const struct ktermios *old) -{ -} - -static const char *esp32s3_acm_type(struct uart_port *port) -{ - return "ESP32S3 ACM"; -} - -/* configure/auto-configure the port */ -static void esp32s3_acm_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) - port->type = PORT_GENERIC; -} - -#ifdef CONFIG_CONSOLE_POLL -static void esp32s3_acm_poll_put_char(struct uart_port *port, unsigned char c) -{ - esp32s3_acm_put_char_sync(port, c); -} - -static int esp32s3_acm_poll_get_char(struct uart_port *port) -{ - if (esp32s3_acm_rx_fifo_cnt(port)) - return esp32s3_acm_read(port, USB_SERIAL_JTAG_EP1_REG); - else - return NO_POLL_CHAR; -} -#endif - -static const struct uart_ops esp32s3_acm_pops = { - .tx_empty = esp32s3_acm_tx_empty, - .set_mctrl = esp32s3_acm_set_mctrl, - .get_mctrl = esp32s3_acm_get_mctrl, - .stop_tx = esp32s3_acm_stop_tx, - .start_tx = esp32s3_acm_start_tx, - .stop_rx = esp32s3_acm_stop_rx, - .startup = esp32s3_acm_startup, - .shutdown = esp32s3_acm_shutdown, - .set_termios = esp32s3_acm_set_termios, - .type = esp32s3_acm_type, - .config_port = esp32s3_acm_config_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_put_char = esp32s3_acm_poll_put_char, - .poll_get_char = esp32s3_acm_poll_get_char, -#endif -}; - -static void esp32s3_acm_string_write(struct uart_port *port, const char *s, - unsigned int count) -{ - uart_console_write(port, s, count, esp32s3_acm_put_char_sync); -} - -static void -esp32s3_acm_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_port *port = esp32s3_acm_ports[co->index]; - unsigned long flags; - bool locked = true; - - if (port->sysrq) - locked = false; - else if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); - else - spin_lock_irqsave(&port->lock, flags); - - esp32s3_acm_string_write(port, s, count); - - if (locked) - spin_unlock_irqrestore(&port->lock, flags); -} - -static struct uart_driver esp32s3_acm_reg; -static struct console esp32s3_acm_console = { - .name = DEV_NAME, - .write = esp32s3_acm_console_write, - .device = uart_console_device, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &esp32s3_acm_reg, -}; - -static void esp32s3_acm_earlycon_write(struct console *con, const char *s, - unsigned int n) -{ - struct earlycon_device *dev = con->data; - - uart_console_write(&dev->port, s, n, esp32s3_acm_put_char_sync); -} - -#ifdef CONFIG_CONSOLE_POLL -static int esp32s3_acm_earlycon_read(struct console *con, char *s, unsigned int n) -{ - struct earlycon_device *dev = con->data; - unsigned int num_read = 0; - - while (num_read < n) { - int c = esp32s3_acm_poll_get_char(&dev->port); - - if (c == NO_POLL_CHAR) - break; - s[num_read++] = c; - } - return num_read; -} -#endif - -static int __init esp32s3_acm_early_console_setup(struct earlycon_device *device, - const char *options) -{ - if (!device->port.membase) - return -ENODEV; - - device->con->write = esp32s3_acm_earlycon_write; -#ifdef CONFIG_CONSOLE_POLL - device->con->read = esp32s3_acm_earlycon_read; -#endif - return 0; -} - -OF_EARLYCON_DECLARE(esp32s3acm, "esp,esp32s3-acm", - esp32s3_acm_early_console_setup); - -static struct uart_driver esp32s3_acm_reg = { - .owner = THIS_MODULE, - .driver_name = DRIVER_NAME, - .dev_name = DEV_NAME, - .nr = ARRAY_SIZE(esp32s3_acm_ports), - .cons = &esp32s3_acm_console, -}; - -static int esp32s3_acm_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct uart_port *port; - struct resource *res; - int ret; - - port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); - if (!port) - return -ENOMEM; - - ret = of_alias_get_id(np, "serial"); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); - return ret; - } - if (ret >= UART_NR) { - dev_err(&pdev->dev, "driver limited to %d serial ports\n", - UART_NR); - return -ENOMEM; - } - - port->line = ret; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - port->mapbase = res->start; - port->membase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(port->membase)) - return PTR_ERR(port->membase); - - port->dev = &pdev->dev; - port->type = PORT_GENERIC; - port->iotype = UPIO_MEM; - port->irq = platform_get_irq(pdev, 0); - port->ops = &esp32s3_acm_pops; - port->flags = UPF_BOOT_AUTOCONF; - port->has_sysrq = 1; - port->fifosize = ESP32S3_ACM_TX_FIFO_SIZE; - - esp32s3_acm_ports[port->line] = port; - - platform_set_drvdata(pdev, port); - - return uart_add_one_port(&esp32s3_acm_reg, port); -} - -static void esp32s3_acm_remove(struct platform_device *pdev) -{ - struct uart_port *port = platform_get_drvdata(pdev); - - uart_remove_one_port(&esp32s3_acm_reg, port); -} - - -static struct platform_driver esp32s3_acm_driver = { - .probe = esp32s3_acm_probe, - .remove = esp32s3_acm_remove, - .driver = { - .name = DRIVER_NAME, - .of_match_table = esp32s3_acm_dt_ids, - }, -}; - -static int __init esp32s3_acm_init(void) -{ - int ret; - - ret = uart_register_driver(&esp32s3_acm_reg); - if (ret) - return ret; - - ret = platform_driver_register(&esp32s3_acm_driver); - if (ret) - uart_unregister_driver(&esp32s3_acm_reg); - - return ret; -} - -static void __exit esp32s3_acm_exit(void) -{ - platform_driver_unregister(&esp32s3_acm_driver); - uart_unregister_driver(&esp32s3_acm_reg); -} - -module_init(esp32s3_acm_init); -module_exit(esp32s3_acm_exit); - -MODULE_AUTHOR("Max Filippov "); -MODULE_DESCRIPTION("Espressif ESP32 USB ACM gadget support"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/esp32_uart.c b/drivers/tty/serial/esp32_uart.c deleted file mode 100644 index 667c2198a03a..000000000000 --- a/drivers/tty/serial/esp32_uart.c +++ /dev/null @@ -1,779 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "esp32-uart" -#define DEV_NAME "ttyS" -#define UART_NR 3 - -#define ESP32_UART_TX_FIFO_SIZE 127 -#define ESP32_UART_RX_FIFO_SIZE 127 - -#define UART_FIFO_REG 0x00 -#define UART_INT_RAW_REG 0x04 -#define UART_INT_ST_REG 0x08 -#define UART_INT_ENA_REG 0x0c -#define UART_INT_CLR_REG 0x10 -#define UART_RXFIFO_FULL_INT BIT(0) -#define UART_TXFIFO_EMPTY_INT BIT(1) -#define UART_BRK_DET_INT BIT(7) -#define UART_CLKDIV_REG 0x14 -#define ESP32_UART_CLKDIV GENMASK(19, 0) -#define ESP32S3_UART_CLKDIV GENMASK(11, 0) -#define UART_CLKDIV_SHIFT 0 -#define UART_CLKDIV_FRAG GENMASK(23, 20) -#define UART_STATUS_REG 0x1c -#define ESP32_UART_RXFIFO_CNT GENMASK(7, 0) -#define ESP32S3_UART_RXFIFO_CNT GENMASK(9, 0) -#define UART_RXFIFO_CNT_SHIFT 0 -#define UART_DSRN BIT(13) -#define UART_CTSN BIT(14) -#define ESP32_UART_TXFIFO_CNT GENMASK(23, 16) -#define ESP32S3_UART_TXFIFO_CNT GENMASK(25, 16) -#define UART_TXFIFO_CNT_SHIFT 16 -#define UART_CONF0_REG 0x20 -#define UART_PARITY BIT(0) -#define UART_PARITY_EN BIT(1) -#define UART_BIT_NUM GENMASK(3, 2) -#define UART_BIT_NUM_5 0 -#define UART_BIT_NUM_6 1 -#define UART_BIT_NUM_7 2 -#define UART_BIT_NUM_8 3 -#define UART_STOP_BIT_NUM GENMASK(5, 4) -#define UART_STOP_BIT_NUM_1 1 -#define UART_STOP_BIT_NUM_2 3 -#define UART_SW_RTS BIT(6) -#define UART_SW_DTR BIT(7) -#define UART_LOOPBACK BIT(14) -#define UART_TX_FLOW_EN BIT(15) -#define UART_RTS_INV BIT(23) -#define UART_DTR_INV BIT(24) -#define UART_CONF1_REG 0x24 -#define UART_RXFIFO_FULL_THRHD_SHIFT 0 -#define ESP32_UART_TXFIFO_EMPTY_THRHD_SHIFT 8 -#define ESP32S3_UART_TXFIFO_EMPTY_THRHD_SHIFT 10 -#define ESP32_UART_RX_FLOW_EN BIT(23) -#define ESP32S3_UART_RX_FLOW_EN BIT(22) -#define ESP32S3_UART_CLK_CONF_REG 0x78 -#define ESP32S3_UART_SCLK_DIV_B GENMASK(5, 0) -#define ESP32S3_UART_SCLK_DIV_A GENMASK(11, 6) -#define ESP32S3_UART_SCLK_DIV_NUM GENMASK(19, 12) -#define ESP32S3_UART_SCLK_SEL GENMASK(21, 20) -#define APB_CLK 1 -#define RC_FAST_CLK 2 -#define XTAL_CLK 3 -#define ESP32S3_UART_SCLK_EN BIT(22) -#define ESP32S3_UART_RST_CORE BIT(23) -#define ESP32S3_UART_TX_SCLK_EN BIT(24) -#define ESP32S3_UART_RX_SCLK_EN BIT(25) -#define ESP32S3_UART_TX_RST_CORE BIT(26) -#define ESP32S3_UART_RX_RST_CORE BIT(27) - -#define ESP32S3_UART_CLK_CONF_DEFAULT \ - (ESP32S3_UART_RX_SCLK_EN | \ - ESP32S3_UART_TX_SCLK_EN | \ - ESP32S3_UART_SCLK_EN | \ - FIELD_PREP(ESP32S3_UART_SCLK_SEL, XTAL_CLK)) - -struct esp32_port { - struct uart_port port; - struct clk *clk; -}; - -struct esp32_uart_variant { - u32 clkdiv_mask; - u32 rxfifo_cnt_mask; - u32 txfifo_cnt_mask; - u32 txfifo_empty_thrhd_shift; - u32 rx_flow_en; - const char *type; - bool has_clkconf; -}; - -static const struct esp32_uart_variant esp32_variant = { - .clkdiv_mask = ESP32_UART_CLKDIV, - .rxfifo_cnt_mask = ESP32_UART_RXFIFO_CNT, - .txfifo_cnt_mask = ESP32_UART_TXFIFO_CNT, - .txfifo_empty_thrhd_shift = ESP32_UART_TXFIFO_EMPTY_THRHD_SHIFT, - .rx_flow_en = ESP32_UART_RX_FLOW_EN, - .type = "ESP32 UART", -}; - -static const struct esp32_uart_variant esp32s3_variant = { - .clkdiv_mask = ESP32S3_UART_CLKDIV, - .rxfifo_cnt_mask = ESP32S3_UART_RXFIFO_CNT, - .txfifo_cnt_mask = ESP32S3_UART_TXFIFO_CNT, - .txfifo_empty_thrhd_shift = ESP32S3_UART_TXFIFO_EMPTY_THRHD_SHIFT, - .rx_flow_en = ESP32S3_UART_RX_FLOW_EN, - .type = "ESP32S3 UART", - .has_clkconf = true, -}; - -static const struct of_device_id esp32_uart_dt_ids[] = { - { - .compatible = "esp,esp32-uart", - .data = &esp32_variant, - }, { - .compatible = "esp,esp32s3-uart", - .data = &esp32s3_variant, - }, { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, esp32_uart_dt_ids); - -static struct esp32_port *esp32_uart_ports[UART_NR]; - -static const struct esp32_uart_variant *port_variant(struct uart_port *port) -{ - return port->private_data; -} - -static void esp32_uart_write(struct uart_port *port, unsigned long reg, u32 v) -{ - writel(v, port->membase + reg); -} - -static u32 esp32_uart_read(struct uart_port *port, unsigned long reg) -{ - return readl(port->membase + reg); -} - -static u32 esp32_uart_tx_fifo_cnt(struct uart_port *port) -{ - u32 status = esp32_uart_read(port, UART_STATUS_REG); - - return (status & port_variant(port)->txfifo_cnt_mask) >> UART_TXFIFO_CNT_SHIFT; -} - -static u32 esp32_uart_rx_fifo_cnt(struct uart_port *port) -{ - u32 status = esp32_uart_read(port, UART_STATUS_REG); - - return (status & port_variant(port)->rxfifo_cnt_mask) >> UART_RXFIFO_CNT_SHIFT; -} - -/* return TIOCSER_TEMT when transmitter is not busy */ -static unsigned int esp32_uart_tx_empty(struct uart_port *port) -{ - return esp32_uart_tx_fifo_cnt(port) ? 0 : TIOCSER_TEMT; -} - -static void esp32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - u32 conf0 = esp32_uart_read(port, UART_CONF0_REG); - - conf0 &= ~(UART_LOOPBACK | - UART_SW_RTS | UART_RTS_INV | - UART_SW_DTR | UART_DTR_INV); - - if (mctrl & TIOCM_RTS) - conf0 |= UART_SW_RTS; - if (mctrl & TIOCM_DTR) - conf0 |= UART_SW_DTR; - if (mctrl & TIOCM_LOOP) - conf0 |= UART_LOOPBACK; - - esp32_uart_write(port, UART_CONF0_REG, conf0); -} - -static unsigned int esp32_uart_get_mctrl(struct uart_port *port) -{ - u32 status = esp32_uart_read(port, UART_STATUS_REG); - unsigned int ret = TIOCM_CAR; - - if (status & UART_DSRN) - ret |= TIOCM_DSR; - if (status & UART_CTSN) - ret |= TIOCM_CTS; - - return ret; -} - -static void esp32_uart_stop_tx(struct uart_port *port) -{ - u32 int_ena; - - int_ena = esp32_uart_read(port, UART_INT_ENA_REG); - int_ena &= ~UART_TXFIFO_EMPTY_INT; - esp32_uart_write(port, UART_INT_ENA_REG, int_ena); -} - -static void esp32_uart_rxint(struct uart_port *port) -{ - struct tty_port *tty_port = &port->state->port; - u32 rx_fifo_cnt = esp32_uart_rx_fifo_cnt(port); - unsigned long flags; - u32 i; - - if (!rx_fifo_cnt) - return; - - spin_lock_irqsave(&port->lock, flags); - - for (i = 0; i < rx_fifo_cnt; ++i) { - u32 rx = esp32_uart_read(port, UART_FIFO_REG); - - if (!rx && - (esp32_uart_read(port, UART_INT_ST_REG) & UART_BRK_DET_INT)) { - esp32_uart_write(port, UART_INT_CLR_REG, UART_BRK_DET_INT); - ++port->icount.brk; - uart_handle_break(port); - } else { - if (uart_handle_sysrq_char(port, (unsigned char)rx)) - continue; - tty_insert_flip_char(tty_port, rx, TTY_NORMAL); - ++port->icount.rx; - } - } - spin_unlock_irqrestore(&port->lock, flags); - - tty_flip_buffer_push(tty_port); -} - -static void esp32_uart_put_char(struct uart_port *port, u8 c) -{ - esp32_uart_write(port, UART_FIFO_REG, c); -} - -static void esp32_uart_put_char_sync(struct uart_port *port, u8 c) -{ - unsigned long timeout = jiffies + HZ; - - while (esp32_uart_tx_fifo_cnt(port) >= ESP32_UART_TX_FIFO_SIZE) { - if (time_after(jiffies, timeout)) { - dev_warn(port->dev, "timeout waiting for TX FIFO\n"); - return; - } - cpu_relax(); - } - esp32_uart_put_char(port, c); -} - -static void esp32_uart_transmit_buffer(struct uart_port *port) -{ - u32 tx_fifo_used = esp32_uart_tx_fifo_cnt(port); - unsigned int pending; - u8 ch; - - if (tx_fifo_used >= ESP32_UART_TX_FIFO_SIZE) - return; - - pending = uart_port_tx_limited(port, ch, - ESP32_UART_TX_FIFO_SIZE - tx_fifo_used, - true, esp32_uart_put_char(port, ch), - ({})); - if (pending) { - u32 int_ena; - - int_ena = esp32_uart_read(port, UART_INT_ENA_REG); - int_ena |= UART_TXFIFO_EMPTY_INT; - esp32_uart_write(port, UART_INT_ENA_REG, int_ena); - } -} - -static void esp32_uart_txint(struct uart_port *port) -{ - esp32_uart_transmit_buffer(port); -} - -static irqreturn_t esp32_uart_int(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - u32 status; - - status = esp32_uart_read(port, UART_INT_ST_REG); - - if (status & (UART_RXFIFO_FULL_INT | UART_BRK_DET_INT)) - esp32_uart_rxint(port); - if (status & UART_TXFIFO_EMPTY_INT) - esp32_uart_txint(port); - - esp32_uart_write(port, UART_INT_CLR_REG, status); - - return IRQ_RETVAL(status); -} - -static void esp32_uart_start_tx(struct uart_port *port) -{ - esp32_uart_transmit_buffer(port); -} - -static void esp32_uart_stop_rx(struct uart_port *port) -{ - u32 int_ena; - - int_ena = esp32_uart_read(port, UART_INT_ENA_REG); - int_ena &= ~UART_RXFIFO_FULL_INT; - esp32_uart_write(port, UART_INT_ENA_REG, int_ena); -} - -static int esp32_uart_startup(struct uart_port *port) -{ - int ret = 0; - unsigned long flags; - struct esp32_port *sport = container_of(port, struct esp32_port, port); - - ret = clk_prepare_enable(sport->clk); - if (ret) - return ret; - - ret = request_irq(port->irq, esp32_uart_int, 0, DRIVER_NAME, port); - if (ret) { - clk_disable_unprepare(sport->clk); - return ret; - } - - spin_lock_irqsave(&port->lock, flags); - if (port_variant(port)->has_clkconf) - esp32_uart_write(port, ESP32S3_UART_CLK_CONF_REG, - ESP32S3_UART_CLK_CONF_DEFAULT); - esp32_uart_write(port, UART_CONF1_REG, - (1 << UART_RXFIFO_FULL_THRHD_SHIFT) | - (1 << port_variant(port)->txfifo_empty_thrhd_shift)); - esp32_uart_write(port, UART_INT_CLR_REG, UART_RXFIFO_FULL_INT | UART_BRK_DET_INT); - esp32_uart_write(port, UART_INT_ENA_REG, UART_RXFIFO_FULL_INT | UART_BRK_DET_INT); - spin_unlock_irqrestore(&port->lock, flags); - - return ret; -} - -static void esp32_uart_shutdown(struct uart_port *port) -{ - struct esp32_port *sport = container_of(port, struct esp32_port, port); - - esp32_uart_write(port, UART_INT_ENA_REG, 0); - free_irq(port->irq, port); - clk_disable_unprepare(sport->clk); -} - -static bool esp32_uart_set_baud(struct uart_port *port, u32 baud) -{ - u32 sclk = port->uartclk; - u32 div = sclk / baud; - - if (port_variant(port)->has_clkconf) { - u32 sclk_div = div / port_variant(port)->clkdiv_mask; - - if (div > port_variant(port)->clkdiv_mask) { - sclk /= (sclk_div + 1); - div = sclk / baud; - } - esp32_uart_write(port, ESP32S3_UART_CLK_CONF_REG, - FIELD_PREP(ESP32S3_UART_SCLK_DIV_NUM, sclk_div) | - ESP32S3_UART_CLK_CONF_DEFAULT); - } - - if (div <= port_variant(port)->clkdiv_mask) { - u32 frag = (sclk * 16) / baud - div * 16; - - esp32_uart_write(port, UART_CLKDIV_REG, - div | FIELD_PREP(UART_CLKDIV_FRAG, frag)); - return true; - } - - return false; -} - -static void esp32_uart_set_termios(struct uart_port *port, - struct ktermios *termios, - const struct ktermios *old) -{ - unsigned long flags; - u32 conf0, conf1; - u32 baud; - const u32 rx_flow_en = port_variant(port)->rx_flow_en; - u32 max_div = port_variant(port)->clkdiv_mask; - - termios->c_cflag &= ~CMSPAR; - - if (port_variant(port)->has_clkconf) - max_div *= FIELD_MAX(ESP32S3_UART_SCLK_DIV_NUM); - - baud = uart_get_baud_rate(port, termios, old, - port->uartclk / max_div, - port->uartclk / 16); - - spin_lock_irqsave(&port->lock, flags); - - conf0 = esp32_uart_read(port, UART_CONF0_REG); - conf0 &= ~(UART_PARITY_EN | UART_PARITY | UART_BIT_NUM | UART_STOP_BIT_NUM); - - conf1 = esp32_uart_read(port, UART_CONF1_REG); - conf1 &= ~rx_flow_en; - - if (termios->c_cflag & PARENB) { - conf0 |= UART_PARITY_EN; - if (termios->c_cflag & PARODD) - conf0 |= UART_PARITY; - } - - switch (termios->c_cflag & CSIZE) { - case CS5: - conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_5); - break; - case CS6: - conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_6); - break; - case CS7: - conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_7); - break; - case CS8: - conf0 |= FIELD_PREP(UART_BIT_NUM, UART_BIT_NUM_8); - break; - } - - if (termios->c_cflag & CSTOPB) - conf0 |= FIELD_PREP(UART_STOP_BIT_NUM, UART_STOP_BIT_NUM_2); - else - conf0 |= FIELD_PREP(UART_STOP_BIT_NUM, UART_STOP_BIT_NUM_1); - - if (termios->c_cflag & CRTSCTS) - conf1 |= rx_flow_en; - - esp32_uart_write(port, UART_CONF0_REG, conf0); - esp32_uart_write(port, UART_CONF1_REG, conf1); - - if (baud) { - esp32_uart_set_baud(port, baud); - uart_update_timeout(port, termios->c_cflag, baud); - } else { - if (esp32_uart_set_baud(port, 115200)) { - baud = 115200; - tty_termios_encode_baud_rate(termios, baud, baud); - uart_update_timeout(port, termios->c_cflag, baud); - } else { - dev_warn(port->dev, - "unable to set speed to %d baud or the default 115200\n", - baud); - } - } - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *esp32_uart_type(struct uart_port *port) -{ - return port_variant(port)->type; -} - -/* configure/auto-configure the port */ -static void esp32_uart_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) - port->type = PORT_GENERIC; -} - -#ifdef CONFIG_CONSOLE_POLL -static int esp32_uart_poll_init(struct uart_port *port) -{ - struct esp32_port *sport = container_of(port, struct esp32_port, port); - - return clk_prepare_enable(sport->clk); -} - -static void esp32_uart_poll_put_char(struct uart_port *port, unsigned char c) -{ - esp32_uart_put_char_sync(port, c); -} - -static int esp32_uart_poll_get_char(struct uart_port *port) -{ - if (esp32_uart_rx_fifo_cnt(port)) - return esp32_uart_read(port, UART_FIFO_REG); - else - return NO_POLL_CHAR; - -} -#endif - -static const struct uart_ops esp32_uart_pops = { - .tx_empty = esp32_uart_tx_empty, - .set_mctrl = esp32_uart_set_mctrl, - .get_mctrl = esp32_uart_get_mctrl, - .stop_tx = esp32_uart_stop_tx, - .start_tx = esp32_uart_start_tx, - .stop_rx = esp32_uart_stop_rx, - .startup = esp32_uart_startup, - .shutdown = esp32_uart_shutdown, - .set_termios = esp32_uart_set_termios, - .type = esp32_uart_type, - .config_port = esp32_uart_config_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_init = esp32_uart_poll_init, - .poll_put_char = esp32_uart_poll_put_char, - .poll_get_char = esp32_uart_poll_get_char, -#endif -}; - -static void esp32_uart_console_putchar(struct uart_port *port, u8 c) -{ - esp32_uart_put_char_sync(port, c); -} - -static void esp32_uart_string_write(struct uart_port *port, const char *s, - unsigned int count) -{ - uart_console_write(port, s, count, esp32_uart_console_putchar); -} - -static void -esp32_uart_console_write(struct console *co, const char *s, unsigned int count) -{ - struct esp32_port *sport = esp32_uart_ports[co->index]; - struct uart_port *port = &sport->port; - unsigned long flags; - bool locked = true; - - if (port->sysrq) - locked = false; - else if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); - else - spin_lock_irqsave(&port->lock, flags); - - esp32_uart_string_write(port, s, count); - - if (locked) - spin_unlock_irqrestore(&port->lock, flags); -} - -static int __init esp32_uart_console_setup(struct console *co, char *options) -{ - struct esp32_port *sport; - int baud = 115200; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - int ret; - - /* - * check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index == -1 || co->index >= ARRAY_SIZE(esp32_uart_ports)) - co->index = 0; - - sport = esp32_uart_ports[co->index]; - if (!sport) - return -ENODEV; - - ret = clk_prepare_enable(sport->clk); - if (ret) - return ret; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&sport->port, co, baud, parity, bits, flow); -} - -static int esp32_uart_console_exit(struct console *co) -{ - struct esp32_port *sport = esp32_uart_ports[co->index]; - - clk_disable_unprepare(sport->clk); - return 0; -} - -static struct uart_driver esp32_uart_reg; -static struct console esp32_uart_console = { - .name = DEV_NAME, - .write = esp32_uart_console_write, - .device = uart_console_device, - .setup = esp32_uart_console_setup, - .exit = esp32_uart_console_exit, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &esp32_uart_reg, -}; - -static void esp32_uart_earlycon_putchar(struct uart_port *port, u8 c) -{ - esp32_uart_put_char_sync(port, c); -} - -static void esp32_uart_earlycon_write(struct console *con, const char *s, - unsigned int n) -{ - struct earlycon_device *dev = con->data; - - uart_console_write(&dev->port, s, n, esp32_uart_earlycon_putchar); -} - -#ifdef CONFIG_CONSOLE_POLL -static int esp32_uart_earlycon_read(struct console *con, char *s, unsigned int n) -{ - struct earlycon_device *dev = con->data; - unsigned int num_read = 0; - - while (num_read < n) { - int c = esp32_uart_poll_get_char(&dev->port); - - if (c == NO_POLL_CHAR) - break; - s[num_read++] = c; - } - return num_read; -} -#endif - -static int __init esp32xx_uart_early_console_setup(struct earlycon_device *device, - const char *options) -{ - if (!device->port.membase) - return -ENODEV; - - device->con->write = esp32_uart_earlycon_write; -#ifdef CONFIG_CONSOLE_POLL - device->con->read = esp32_uart_earlycon_read; -#endif - if (device->port.uartclk != BASE_BAUD * 16) - esp32_uart_set_baud(&device->port, device->baud); - - return 0; -} - -static int __init esp32_uart_early_console_setup(struct earlycon_device *device, - const char *options) -{ - device->port.private_data = (void *)&esp32_variant; - - return esp32xx_uart_early_console_setup(device, options); -} - -OF_EARLYCON_DECLARE(esp32uart, "esp,esp32-uart", - esp32_uart_early_console_setup); - -static int __init esp32s3_uart_early_console_setup(struct earlycon_device *device, - const char *options) -{ - device->port.private_data = (void *)&esp32s3_variant; - - return esp32xx_uart_early_console_setup(device, options); -} - -OF_EARLYCON_DECLARE(esp32s3uart, "esp,esp32s3-uart", - esp32s3_uart_early_console_setup); - -static struct uart_driver esp32_uart_reg = { - .owner = THIS_MODULE, - .driver_name = DRIVER_NAME, - .dev_name = DEV_NAME, - .nr = ARRAY_SIZE(esp32_uart_ports), - .cons = &esp32_uart_console, -}; - -static int esp32_uart_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct uart_port *port; - struct esp32_port *sport; - struct resource *res; - int ret; - - sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); - if (!sport) - return -ENOMEM; - - port = &sport->port; - - ret = of_alias_get_id(np, "serial"); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); - return ret; - } - if (ret >= UART_NR) { - dev_err(&pdev->dev, "driver limited to %d serial ports\n", UART_NR); - return -ENOMEM; - } - - port->line = ret; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - port->mapbase = res->start; - port->membase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(port->membase)) - return PTR_ERR(port->membase); - - sport->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(sport->clk)) - return PTR_ERR(sport->clk); - - port->uartclk = clk_get_rate(sport->clk); - port->dev = &pdev->dev; - port->type = PORT_GENERIC; - port->iotype = UPIO_MEM; - port->irq = platform_get_irq(pdev, 0); - port->ops = &esp32_uart_pops; - port->flags = UPF_BOOT_AUTOCONF; - port->has_sysrq = 1; - port->fifosize = ESP32_UART_TX_FIFO_SIZE; - port->private_data = (void *)device_get_match_data(&pdev->dev); - - esp32_uart_ports[port->line] = sport; - - platform_set_drvdata(pdev, port); - - return uart_add_one_port(&esp32_uart_reg, port); -} - -static void esp32_uart_remove(struct platform_device *pdev) -{ - struct uart_port *port = platform_get_drvdata(pdev); - - uart_remove_one_port(&esp32_uart_reg, port); -} - - -static struct platform_driver esp32_uart_driver = { - .probe = esp32_uart_probe, - .remove = esp32_uart_remove, - .driver = { - .name = DRIVER_NAME, - .of_match_table = esp32_uart_dt_ids, - }, -}; - -static int __init esp32_uart_init(void) -{ - int ret; - - ret = uart_register_driver(&esp32_uart_reg); - if (ret) - return ret; - - ret = platform_driver_register(&esp32_uart_driver); - if (ret) - uart_unregister_driver(&esp32_uart_reg); - - return ret; -} - -static void __exit esp32_uart_exit(void) -{ - platform_driver_unregister(&esp32_uart_driver); - uart_unregister_driver(&esp32_uart_reg); -} - -module_init(esp32_uart_init); -module_exit(esp32_uart_exit); - -MODULE_AUTHOR("Max Filippov "); -MODULE_DESCRIPTION("Espressif ESP32 UART support"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index c488e5d372ff..251a50c8aa38 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1442,9 +1442,9 @@ static void imx_uart_enable_dma(struct imx_port *sport) imx_uart_setup_ufcr(sport, TXTL_DMA, RXTL_DMA); - /* set UCR1 */ + /* set UCR1 except TXDMAEN which would be enabled in imx_uart_dma_tx */ ucr1 = imx_uart_readl(sport, UCR1); - ucr1 |= UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN; + ucr1 |= UCR1_RXDMAEN | UCR1_ATDMAEN; imx_uart_writel(sport, ucr1, UCR1); sport->dma_is_enabled = 1; @@ -1567,8 +1567,9 @@ static int imx_uart_startup(struct uart_port *port) imx_uart_enable_ms(&sport->port); if (dma_is_inited) { - imx_uart_enable_dma(sport); + /* Note: enable dma request after transfer start! */ imx_uart_start_rx_dma(sport); + imx_uart_enable_dma(sport); } else { ucr1 = imx_uart_readl(sport, UCR1); ucr1 |= UCR1_RRDYEN; diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c index 6e19c6713849..3fb8fdf8a853 100644 --- a/drivers/tty/serial/ip22zilog.c +++ b/drivers/tty/serial/ip22zilog.c @@ -1015,7 +1015,7 @@ static struct console ip22zilog_console = { static struct uart_driver ip22zilog_reg = { .owner = THIS_MODULE, - .driver_name = "serial", + .driver_name = "serial_ip22zilog", .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, @@ -1025,7 +1025,7 @@ static struct uart_driver ip22zilog_reg = { #endif }; -static void __init ip22zilog_prepare(struct uart_ip22zilog_port *up) +static void ip22zilog_prepare(struct uart_ip22zilog_port *up) { unsigned char sysrq_on = IS_ENABLED(CONFIG_SERIAL_IP22_ZILOG_CONSOLE); int brg; diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index cc65c9fb6446..693b491f1e75 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1318,7 +1318,8 @@ auart_console_write(struct console *co, const char *str, unsigned int count) s = auart_port[co->index]; port = &s->port; - clk_enable(s->clk); + if (clk_enable(s->clk)) + return; /* First save the CR then disable the interrupts */ old_ctrl2 = mxs_read(s, REG_CTRL2); diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index e6b0a55f0cfb..b365dd5da3cb 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -146,6 +146,7 @@ struct qcom_geni_serial_port { int wakeup_irq; bool rx_tx_swap; bool cts_rts_swap; + bool manual_flow; struct qcom_geni_private_data private_data; const struct qcom_geni_device_data *dev_data; @@ -250,7 +251,7 @@ static void qcom_geni_serial_set_mctrl(struct uart_port *uport, if (mctrl & TIOCM_LOOP) port->loopback = RX_TX_CTS_RTS_SORTED; - if (!(mctrl & TIOCM_RTS) && !uport->suspended) + if (port->manual_flow && !(mctrl & TIOCM_RTS) && !uport->suspended) uart_manual_rfr = UART_MANUAL_RFR_EN | UART_RFR_NOT_READY; writel(uart_manual_rfr, uport->membase + SE_UART_MANUAL_RFR); } @@ -1281,7 +1282,7 @@ static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud) return -EINVAL; } - dev_dbg(port->se.dev, "desired_rate = %u, clk_rate = %lu, clk_div = %u\n, clk_idx = %u\n", + dev_dbg(port->se.dev, "desired_rate = %u, clk_rate = %lu, clk_div = %u, clk_idx = %u\n", baud * sampling_rate, clk_rate, clk_div, clk_idx); uport->uartclk = clk_rate; @@ -1401,11 +1402,21 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, else stop_bit_len = TX_STOP_BIT_LEN_1; - /* flow control, clear the CTS_MASK bit if using flow control. */ - if (termios->c_cflag & CRTSCTS) + /* Configure flow control based on CRTSCTS flag. + * When CRTSCTS is set, use HW/auto flow control mode, where HW + * controls the RTS/CTS pin based FIFO state. + * When CRTSCTS is clear, the CTS pin value is ignored for TX + * path and RTS pin can be set/cleared using registers, for RX + * path. + */ + + if (termios->c_cflag & CRTSCTS) { tx_trans_cfg &= ~UART_CTS_MASK; - else + port->manual_flow = false; + } else { tx_trans_cfg |= UART_CTS_MASK; + port->manual_flow = true; + } if (baud) { uart_update_timeout(uport, termios->c_cflag, baud); diff --git a/drivers/tty/serial/rsci.c b/drivers/tty/serial/rsci.c index c3f12df693ad..b00c9e385169 100644 --- a/drivers/tty/serial/rsci.c +++ b/drivers/tty/serial/rsci.c @@ -695,6 +695,13 @@ struct sci_of_data of_rsci_rzg3e_data = { .params = &rsci_rzg3e_port_params, }; +struct sci_of_data of_rsci_rzg3l_data = { + .type = RSCI_PORT_SCIF32_SINGLE_TCLK, + .ops = &rsci_port_ops, + .uart_ops = &rsci_uart_ops, + .params = &rsci_rzg3e_port_params, +}; + struct sci_of_data of_rsci_rzt2h_data = { .type = RSCI_PORT_SCIF16, .ops = &rsci_port_ops, @@ -703,6 +710,11 @@ struct sci_of_data of_rsci_rzt2h_data = { }; #ifdef CONFIG_SERIAL_SH_SCI_EARLYCON +static int __init rsci_rzg3l_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + return scix_early_console_setup(device, &of_rsci_rzg3l_data); +} static int __init rsci_rzg3e_early_console_setup(struct earlycon_device *device, const char *opt) @@ -716,6 +728,7 @@ static int __init rsci_rzt2h_early_console_setup(struct earlycon_device *device, return scix_early_console_setup(device, &of_rsci_rzt2h_data); } +OF_EARLYCON_DECLARE(rsci, "renesas,r9a08g046-rsci", rsci_rzg3l_early_console_setup); OF_EARLYCON_DECLARE(rsci, "renesas,r9a09g047-rsci", rsci_rzg3e_early_console_setup); OF_EARLYCON_DECLARE(rsci, "renesas,r9a09g077-rsci", rsci_rzt2h_early_console_setup); diff --git a/drivers/tty/serial/rsci.h b/drivers/tty/serial/rsci.h index 2aa2ba3973ee..0985fd1b3348 100644 --- a/drivers/tty/serial/rsci.h +++ b/drivers/tty/serial/rsci.h @@ -6,6 +6,7 @@ #include "sh-sci-common.h" extern struct sci_of_data of_rsci_rzg3e_data; +extern struct sci_of_data of_rsci_rzg3l_data; extern struct sci_of_data of_rsci_rzt2h_data; #endif /* __RSCI_H__ */ diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index c1fabad6ba1f..e27806bf2cf3 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -1562,12 +1562,12 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, ulcon |= S3C2410_LCON_PNONE; } - uart_port_lock_irqsave(port, &flags); - dev_dbg(port->dev, "setting ulcon to %08x, brddiv to %d, udivslot %08x\n", ulcon, quot, udivslot); + uart_port_lock_irqsave(port, &flags); + wr_regl(port, S3C2410_ULCON, ulcon); wr_regl(port, S3C2410_UBRDIV, quot); @@ -1587,12 +1587,6 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, if (ourport->info->has_divslot) wr_regl(port, S3C2443_DIVSLOT, udivslot); - dev_dbg(port->dev, - "uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n", - rd_regl(port, S3C2410_ULCON), - rd_regl(port, S3C2410_UCON), - rd_regl(port, S3C2410_UFCON)); - /* * Update the per-port timeout. */ diff --git a/drivers/tty/serial/sh-sci-common.h b/drivers/tty/serial/sh-sci-common.h index f363a659c46a..01ff9fced803 100644 --- a/drivers/tty/serial/sh-sci-common.h +++ b/drivers/tty/serial/sh-sci-common.h @@ -9,6 +9,7 @@ enum SCI_PORT_TYPE { RSCI_PORT_SCIF16 = BIT(7) | 0, RSCI_PORT_SCIF32 = BIT(7) | 1, + RSCI_PORT_SCIF32_SINGLE_TCLK = BIT(7) | 2, }; enum SCI_CLKS { diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index bd7486315338..6c819b6b2425 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1184,7 +1184,8 @@ static int sci_handle_errors(struct uart_port *port) static bool sci_is_rsci_type(u8 type) { - return (type == RSCI_PORT_SCIF16 || type == RSCI_PORT_SCIF32); + return (type == RSCI_PORT_SCIF16 || type == RSCI_PORT_SCIF32 || + type == RSCI_PORT_SCIF32_SINGLE_TCLK); } static int sci_handle_fifo_overrun(struct uart_port *port) @@ -3181,7 +3182,8 @@ static int sci_init_clocks(struct sci_port *sci_port, struct device *dev) if (sci_port->type == PORT_HSCIF) { clk_names[SCI_SCK] = "hsck"; - } else if (sci_port->type == RSCI_PORT_SCIF16) { + } else if (sci_port->type == RSCI_PORT_SCIF16 || + sci_port->type == RSCI_PORT_SCIF32_SINGLE_TCLK) { clk_names[SCI_FCK] = "operation"; clk_names[SCI_BRG_INT] = "bus"; } else if (sci_port->type == RSCI_PORT_SCIF32) { @@ -3196,7 +3198,8 @@ static int sci_init_clocks(struct sci_port *sci_port, struct device *dev) if (IS_ERR(clk)) return PTR_ERR(clk); - if (!clk && sci_port->type == RSCI_PORT_SCIF16 && + if (!clk && (sci_port->type == RSCI_PORT_SCIF16 || + sci_port->type == RSCI_PORT_SCIF32_SINGLE_TCLK) && (i == SCI_FCK || i == SCI_BRG_INT)) return dev_err_probe(dev, -ENODEV, "failed to get %s\n", name); @@ -3330,6 +3333,7 @@ static int sci_init_single(struct platform_device *dev, break; case PORT_SCIFA: case RSCI_PORT_SCIF32: + case RSCI_PORT_SCIF32_SINGLE_TCLK: sci_port->rx_trigger = 32; break; case PORT_SCIF: @@ -3663,6 +3667,10 @@ static const struct of_device_id of_sci_match[] __maybe_unused = { .data = &of_sci_scif_rzv2h, }, #ifdef CONFIG_SERIAL_RSCI + { + .compatible = "renesas,r9a08g046-rsci", + .data = &of_rsci_rzg3l_data, + }, { .compatible = "renesas,r9a09g047-rsci", .data = &of_rsci_rzg3e_data, diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index c593d20a1b5b..a072b75dbaf2 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 79ea7108a0f3..72a3c0d90f40 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -1252,7 +1252,7 @@ console_initcall(zs_serial_console_init); static struct uart_driver zs_reg = { .owner = THIS_MODULE, - .driver_name = "serial", + .driver_name = "serial_zs", .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 79ec953824d5..96be90db53b7 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -59,6 +59,13 @@ void tty_buffer_lock_exclusive(struct tty_port *port) } EXPORT_SYMBOL_GPL(tty_buffer_lock_exclusive); +static bool tty_buffer_queue_work(struct tty_bufhead *buf) +{ + struct workqueue_struct *flip_wq = READ_ONCE(buf->flip_wq); + + return queue_work(flip_wq ?: system_dfl_wq, &buf->work); +} + /** * tty_buffer_unlock_exclusive - release exclusive access * @port: tty port owning the flip buffer @@ -76,7 +83,7 @@ void tty_buffer_unlock_exclusive(struct tty_port *port) mutex_unlock(&buf->lock); if (restart) - queue_work(system_dfl_wq, &buf->work); + tty_buffer_queue_work(buf); } EXPORT_SYMBOL_GPL(tty_buffer_unlock_exclusive); @@ -530,7 +537,7 @@ void tty_flip_buffer_push(struct tty_port *port) struct tty_bufhead *buf = &port->buf; tty_flip_buffer_commit(buf->tail); - queue_work(system_dfl_wq, &buf->work); + tty_buffer_queue_work(buf); } EXPORT_SYMBOL(tty_flip_buffer_push); @@ -560,7 +567,7 @@ int tty_insert_flip_string_and_push_buffer(struct tty_port *port, tty_flip_buffer_commit(buf->tail); spin_unlock_irqrestore(&port->lock, flags); - queue_work(system_dfl_wq, &buf->work); + tty_buffer_queue_work(buf); return size; } @@ -613,7 +620,7 @@ void tty_buffer_set_lock_subclass(struct tty_port *port) bool tty_buffer_restart_work(struct tty_port *port) { - return queue_work(system_dfl_wq, &port->buf.work); + return tty_buffer_queue_work(&port->buf); } bool tty_buffer_cancel_work(struct tty_port *port) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index a5d0457e0e28..6b283fd03ff8 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3443,10 +3443,27 @@ int tty_register_driver(struct tty_driver *driver) if (error < 0) goto err; + /* + * Drivers that do not define driver_name are potentially in-memory devices + * like vty, which generally do not require special workqueue settings. + */ + if (!(driver->flags & TTY_DRIVER_NO_WORKQUEUE) && driver->driver_name) { + driver->flip_wq = alloc_workqueue("%s-%s", WQ_UNBOUND | WQ_SYSFS, + 0, driver->name, driver->driver_name); + if (!driver->flip_wq) { + error = -ENOMEM; + goto err_unreg_char; + } + for (i = 0; i < driver->num; i++) { + if (driver->ports[i]) + tty_port_link_driver_wq(driver->ports[i], driver); + } + } + if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) { error = tty_cdev_add(driver, dev, 0, driver->num); if (error) - goto err_unreg_char; + goto err_destroy_wq; } scoped_guard(mutex, &tty_mutex) @@ -3472,6 +3489,10 @@ err_unreg_devs: scoped_guard(mutex, &tty_mutex) list_del(&driver->tty_drivers); +err_destroy_wq: + if (driver->flip_wq) + destroy_workqueue(driver->flip_wq); + err_unreg_char: unregister_chrdev_region(dev, driver->num); err: @@ -3491,6 +3512,8 @@ void tty_unregister_driver(struct tty_driver *driver) driver->num); scoped_guard(mutex, &tty_mutex) list_del(&driver->tty_drivers); + if (driver->flip_wq) + destroy_workqueue(driver->flip_wq); } EXPORT_SYMBOL(tty_unregister_driver); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 888f2f8f9481..27fe8236f662 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -44,7 +44,7 @@ enum { static DEFINE_RAW_SPINLOCK(tty_ldiscs_lock); /* Line disc dispatch table */ -static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; +static const struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; /** * tty_register_ldisc - install a line discipline @@ -55,7 +55,7 @@ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; * * Locking: takes %tty_ldiscs_lock to guard against ldisc races */ -int tty_register_ldisc(struct tty_ldisc_ops *new_ldisc) +int tty_register_ldisc(const struct tty_ldisc_ops *new_ldisc) { unsigned long flags; @@ -80,7 +80,7 @@ EXPORT_SYMBOL(tty_register_ldisc); * Locking: takes %tty_ldiscs_lock to guard against ldisc races */ -void tty_unregister_ldisc(struct tty_ldisc_ops *ldisc) +void tty_unregister_ldisc(const struct tty_ldisc_ops *ldisc) { unsigned long flags; @@ -90,10 +90,10 @@ void tty_unregister_ldisc(struct tty_ldisc_ops *ldisc) } EXPORT_SYMBOL(tty_unregister_ldisc); -static struct tty_ldisc_ops *get_ldops(int disc) +static const struct tty_ldisc_ops *get_ldops(int disc) { unsigned long flags; - struct tty_ldisc_ops *ldops, *ret; + const struct tty_ldisc_ops *ldops, *ret; raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); ret = ERR_PTR(-EINVAL); @@ -107,7 +107,7 @@ static struct tty_ldisc_ops *get_ldops(int disc) return ret; } -static void put_ldops(struct tty_ldisc_ops *ldops) +static void put_ldops(const struct tty_ldisc_ops *ldops) { unsigned long flags; @@ -139,7 +139,7 @@ int tty_ldisc_autoload = IS_BUILTIN(CONFIG_LDISC_AUTOLOAD); static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc) { struct tty_ldisc *ld; - struct tty_ldisc_ops *ldops; + const struct tty_ldisc_ops *ldops; if (disc < N_TTY || disc >= NR_LDISCS) return ERR_PTR(-EINVAL); @@ -202,7 +202,7 @@ static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) static int tty_ldiscs_seq_show(struct seq_file *m, void *v) { int i = *(loff_t *)v; - struct tty_ldisc_ops *ldops; + const struct tty_ldisc_ops *ldops; ldops = get_ldops(i); if (IS_ERR(ldops)) diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index fe67c5cb0a3f..54359310e293 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -99,6 +99,23 @@ void tty_port_init(struct tty_port *port) } EXPORT_SYMBOL(tty_port_init); +/** + * tty_port_link_wq - link tty_port and flip workqueue + * @port: tty_port of the device + * @flip_wq: workqueue to queue flip buffer work on + * + * Whenever %TTY_DRIVER_NO_WORKQUEUE is used, every tty_port can be linked to + * a workqueue manually by this function. + * tty_port will use system_dfl_wq when buf.flip_wq is NULL. + * + * Note that tty_port API will NOT destroy the workqueue. + */ +void tty_port_link_wq(struct tty_port *port, struct workqueue_struct *flip_wq) +{ + port->buf.flip_wq = flip_wq; +} +EXPORT_SYMBOL_GPL(tty_port_link_wq); + /** * tty_port_link_device - link tty and tty_port * @port: tty_port of the device @@ -157,6 +174,7 @@ struct device *tty_port_register_device_attr(struct tty_port *port, const struct attribute_group **attr_grp) { tty_port_link_device(port, driver, index); + tty_port_link_driver_wq(port, driver); return tty_register_device_attr(driver, index, device, drvdata, attr_grp); } @@ -183,6 +201,7 @@ struct device *tty_port_register_device_attr_serdev(struct tty_port *port, struct device *dev; tty_port_link_device(port, driver, index); + tty_port_link_driver_wq(port, driver); dev = serdev_tty_port_register(port, host, parent, driver, index); if (PTR_ERR(dev) != -ENODEV) { @@ -210,6 +229,7 @@ void tty_port_unregister_device(struct tty_port *port, { int ret; + WRITE_ONCE(port->buf.flip_wq, NULL); ret = serdev_tty_port_unregister(port); if (ret == 0) return; @@ -257,6 +277,7 @@ void tty_port_destroy(struct tty_port *port) { tty_buffer_cancel_work(port); tty_buffer_free_all(port); + WRITE_ONCE(port->buf.flip_wq, NULL); } EXPORT_SYMBOL(tty_port_destroy); @@ -703,6 +724,7 @@ int tty_port_install(struct tty_port *port, struct tty_driver *driver, struct tty_struct *tty) { tty->port = port; + tty_port_link_driver_wq(port, driver); return tty_standard_install(driver, tty); } EXPORT_SYMBOL_GPL(tty_port_install); diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 13bc048f45e8..dfdea0842149 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -74,7 +74,7 @@ static inline int kbd_defleds(void) k_self, k_fn, k_spec, k_pad,\ k_dead, k_cons, k_cur, k_shift,\ k_meta, k_ascii, k_lock, k_lowercase,\ - k_slock, k_dead2, k_brl, k_ignore + k_slock, k_dead2, k_brl, k_csi typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value, char up_flag); @@ -127,6 +127,7 @@ static const unsigned char max_vals[] = { [ KT_SLOCK ] = NR_LOCK - 1, [ KT_DEAD2 ] = 255, [ KT_BRL ] = NR_BRL - 1, + [ KT_CSI ] = 99, }; static const int NR_TYPES = ARRAY_SIZE(max_vals); @@ -644,10 +645,6 @@ static void fn_null(struct vc_data *vc) /* * Special key handlers */ -static void k_ignore(struct vc_data *vc, unsigned char value, char up_flag) -{ -} - static void k_spec(struct vc_data *vc, unsigned char value, char up_flag) { if (up_flag) @@ -765,14 +762,39 @@ static void k_fn(struct vc_data *vc, unsigned char value, char up_flag) pr_err("k_fn called with value=%d\n", value); } +/* + * Compute xterm-style modifier parameter for CSI sequences. + * Returns 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0) + */ +static int csi_modifier_param(void) +{ + int mod = 1; + + if (shift_state & (BIT(KG_SHIFT) | BIT(KG_SHIFTL) | BIT(KG_SHIFTR))) + mod += 1; + if (shift_state & (BIT(KG_ALT) | BIT(KG_ALTGR))) + mod += 2; + if (shift_state & (BIT(KG_CTRL) | BIT(KG_CTRLL) | BIT(KG_CTRLR))) + mod += 4; + return mod; +} + static void k_cur(struct vc_data *vc, unsigned char value, char up_flag) { static const char cur_chars[] = "BDCA"; + int mod; if (up_flag) return; - applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE)); + mod = csi_modifier_param(); + if (mod > 1) { + char buf[] = { 0x1b, '[', '1', ';', '0' + mod, cur_chars[value], 0x00 }; + + puts_queue(vc, buf); + } else { + applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE)); + } } static void k_pad(struct vc_data *vc, unsigned char value, char up_flag) @@ -1004,6 +1026,37 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) } } +/* + * Handle KT_CSI keysym type: generate CSI tilde sequences with modifier + * support. The value encodes the CSI parameter number, producing sequences + * like ESC [ ~ or ESC [ ; ~ when modifiers are held. + */ +static void k_csi(struct vc_data *vc, unsigned char value, char up_flag) +{ + char buf[10]; + int i = 0; + int mod; + + if (up_flag) + return; + + mod = csi_modifier_param(); + + buf[i++] = 0x1b; + buf[i++] = '['; + if (value >= 10) + buf[i++] = '0' + value / 10; + buf[i++] = '0' + value % 10; + if (mod > 1) { + buf[i++] = ';'; + buf[i++] = '0' + mod; + } + buf[i++] = '~'; + buf[i] = 0x00; + + puts_queue(vc, buf); +} + #if IS_ENABLED(CONFIG_INPUT_LEDS) && IS_ENABLED(CONFIG_LEDS_TRIGGERS) struct kbd_led_trigger { @@ -1445,6 +1498,21 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw) param.ledstate = kbd->ledflagstate; key_map = key_maps[shift_final]; + /* + * Fall back to the plain map if modifiers are active, the modifier- + * specific map is missing or has no entry, and the plain map has a + * modifier-aware key type (KT_CUR or KT_CSI). These handlers encode + * the modifier state into the emitted escape sequence. + */ + if (shift_final && keycode < NR_KEYS && + (!key_map || key_map[keycode] == K_HOLE) && key_maps[0]) { + unsigned short plain = key_maps[0][keycode]; + unsigned char type = KTYP(plain); + + if (type >= 0xf0 && (type - 0xf0 == KT_CUR || type - 0xf0 == KT_CSI)) + key_map = key_maps[0]; + } + rc = atomic_notifier_call_chain(&keyboard_notifier_list, KBD_KEYCODE, ¶m); if (rc == NOTIFY_STOP || !key_map) { diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8470aaf00531..e99636ab9db5 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1680,9 +1680,7 @@ static void rgb_background(struct vc_data *vc, const struct rgb *c) /* * ITU T.416 Higher colour modes. They break the usual properties of SGR codes - * and thus need to be detected and ignored by hand. That standard also - * wants : rather than ; as separators but sequences containing : are currently - * completely ignored by the parser. + * and thus need to be detected and ignored by hand. * * Subcommands 3 (CMY) and 4 (CMYK) are so insane there's no point in * supporting them. @@ -1739,6 +1737,7 @@ enum { CSI_m_BG_COLOR_END = 47, CSI_m_BG_COLOR = 48, CSI_m_DEFAULT_BG_COLOR = 49, + CSI_m_UNDERLINE_COLOR = 58, CSI_m_BRIGHT_FG_COLOR_BEG = 90, CSI_m_BRIGHT_FG_COLOR_END = 97, CSI_m_BRIGHT_FG_COLOR_OFF = CSI_m_BRIGHT_FG_COLOR_BEG - CSI_m_FG_COLOR_BEG, @@ -2218,6 +2217,7 @@ static void restore_cur(struct vc_data *vc) * @ESesc: ESC parsed * @ESsquare: CSI parsed -- modifiers/parameters/ctrl chars expected * @ESgetpars: CSI parsed -- parameters/ctrl chars expected + * @ESgetsubpars: CSI m parsed -- subparameters expected * @ESfunckey: CSI [ parsed * @EShash: ESC # parsed * @ESsetG0: ESC ( parsed @@ -2238,6 +2238,7 @@ enum vc_ctl_state { ESesc, ESsquare, ESgetpars, + ESgetsubpars, ESfunckey, EShash, ESsetG0, @@ -2759,6 +2760,47 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c) fallthrough; case ESgetpars: /* ESC [ aka CSI, parameters expected */ switch (c) { + case ':': /* ITU-T T.416 color subparameters */ + if (vc->vc_par[vc->vc_npar] == CSI_m_FG_COLOR || + vc->vc_par[vc->vc_npar] == CSI_m_BG_COLOR || + vc->vc_par[vc->vc_npar] == CSI_m_UNDERLINE_COLOR) + vc->vc_state = ESgetsubpars; + else + break; + fallthrough; + case ';': + if (vc->vc_npar < NPAR - 1) { + vc->vc_npar++; + return; + } + break; + case '0' ... '9': + vc->vc_par[vc->vc_npar] *= 10; + vc->vc_par[vc->vc_npar] += c - '0'; + return; + } + if (c >= ASCII_CSI_IGNORE_FIRST && c <= ASCII_CSI_IGNORE_LAST) { + vc->vc_state = EScsiignore; + return; + } + + /* parameters done, handle the control char @c */ + + vc->vc_state = ESnormal; + + switch (vc->vc_priv) { + case EPdec: + csi_DEC(tty, vc, c); + return; + case EPecma: + csi_ECMA(tty, vc, c); + return; + default: + return; + } + case ESgetsubpars: /* ESC [ 38/48/58, subparameters expected */ + switch (c) { + case ':': case ';': if (vc->vc_npar < NPAR - 1) { vc->vc_npar++; diff --git a/drivers/ufs/core/Makefile b/drivers/ufs/core/Makefile index 51e1867e524e..ce7d16d2cf35 100644 --- a/drivers/ufs/core/Makefile +++ b/drivers/ufs/core/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_SCSI_UFSHCD) += ufshcd-core.o -ufshcd-core-y += ufshcd.o ufs-sysfs.o ufs-mcq.o +ufshcd-core-y += ufshcd.o ufs-sysfs.o ufs-mcq.o ufs-txeq.o ufshcd-core-$(CONFIG_RPMB) += ufs-rpmb.o ufshcd-core-$(CONFIG_DEBUG_FS) += ufs-debugfs.o ufshcd-core-$(CONFIG_SCSI_UFS_BSG) += ufs_bsg.o diff --git a/drivers/ufs/core/ufs-debugfs.c b/drivers/ufs/core/ufs-debugfs.c index e3baed6c70bd..e3dd81d6fe82 100644 --- a/drivers/ufs/core/ufs-debugfs.c +++ b/drivers/ufs/core/ufs-debugfs.c @@ -209,6 +209,265 @@ static const struct ufs_debugfs_attr ufs_attrs[] = { { } }; +static int ufs_tx_eq_params_show(struct seq_file *s, void *data) +{ + const char *file_name = s->file->f_path.dentry->d_name.name; + u32 gear = (u32)(uintptr_t)s->file->f_inode->i_private; + struct ufs_hba *hba = hba_from_file(s->file); + struct ufshcd_tx_eq_settings *settings; + struct ufs_pa_layer_attr *pwr_info; + struct ufshcd_tx_eq_params *params; + u32 rate = hba->pwr_info.hs_rate; + u32 num_lanes; + int lane; + + if (!ufshcd_is_tx_eq_supported(hba)) + return -EOPNOTSUPP; + + if (gear < UFS_HS_G1 || gear > UFS_HS_GEAR_MAX) { + seq_printf(s, "Invalid gear selected: %u\n", gear); + return 0; + } + + if (!hba->max_pwr_info.is_valid) { + seq_puts(s, "Max power info is invalid\n"); + return 0; + } + + pwr_info = &hba->max_pwr_info.info; + params = &hba->tx_eq_params[gear - 1]; + if (!params->is_valid) { + seq_printf(s, "TX EQ params are invalid for HS-G%u, Rate-%s\n", + gear, ufs_hs_rate_to_str(rate)); + return 0; + } + + if (strcmp(file_name, "host_tx_eq_params") == 0) { + settings = params->host; + num_lanes = pwr_info->lane_tx; + seq_printf(s, "Host TX EQ PreShoot Cap: 0x%02x, DeEmphasis Cap: 0x%02x\n", + hba->host_preshoot_cap, hba->host_deemphasis_cap); + } else if (strcmp(file_name, "device_tx_eq_params") == 0) { + settings = params->device; + num_lanes = pwr_info->lane_rx; + seq_printf(s, "Device TX EQ PreShoot Cap: 0x%02x, DeEmphasis Cap: 0x%02x\n", + hba->device_preshoot_cap, hba->device_deemphasis_cap); + } else { + return -ENOENT; + } + + seq_printf(s, "TX EQ setting for HS-G%u, Rate-%s:\n", gear, + ufs_hs_rate_to_str(rate)); + for (lane = 0; lane < num_lanes; lane++) + seq_printf(s, "TX Lane %d - PreShoot: %d, DeEmphasis: %d, Pre-Coding %senabled\n", + lane, settings[lane].preshoot, + settings[lane].deemphasis, + settings[lane].precode_en ? "" : "not "); + + return 0; +} + +static int ufs_tx_eq_params_open(struct inode *inode, struct file *file) +{ + return single_open(file, ufs_tx_eq_params_show, inode->i_private); +} + +static const struct file_operations ufs_tx_eq_params_fops = { + .owner = THIS_MODULE, + .open = ufs_tx_eq_params_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct ufs_debugfs_attr ufs_tx_eq_attrs[] = { + { "host_tx_eq_params", 0400, &ufs_tx_eq_params_fops }, + { "device_tx_eq_params", 0400, &ufs_tx_eq_params_fops }, + { } +}; + +static int ufs_tx_eqtr_record_show(struct seq_file *s, void *data) +{ + const char *file_name = s->file->f_path.dentry->d_name.name; + u8 (*fom_array)[TX_HS_NUM_PRESHOOT][TX_HS_NUM_DEEMPHASIS]; + u32 gear = (u32)(uintptr_t)s->file->f_inode->i_private; + unsigned long preshoot_bitmap, deemphasis_bitmap; + struct ufs_hba *hba = hba_from_file(s->file); + struct ufs_pa_layer_attr *pwr_info; + struct ufshcd_tx_eq_params *params; + struct ufshcd_tx_eqtr_record *rec; + u32 rate = hba->pwr_info.hs_rate; + u8 preshoot, deemphasis; + u32 num_lanes; + char name[32]; + int lane; + + if (!ufshcd_is_tx_eq_supported(hba)) + return -EOPNOTSUPP; + + if (gear < UFS_HS_G1 || gear > UFS_HS_GEAR_MAX) { + seq_printf(s, "Invalid gear selected: %u\n", gear); + return 0; + } + + if (!hba->max_pwr_info.is_valid) { + seq_puts(s, "Max power info is invalid\n"); + return 0; + } + + pwr_info = &hba->max_pwr_info.info; + params = &hba->tx_eq_params[gear - 1]; + if (!params->is_valid) { + seq_printf(s, "TX EQ params are invalid for HS-G%u, Rate-%s\n", + gear, ufs_hs_rate_to_str(rate)); + return 0; + } + + rec = params->eqtr_record; + if (!rec || !rec->last_record_index) { + seq_printf(s, "No TX EQTR records found for HS-G%u, Rate-%s.\n", + gear, ufs_hs_rate_to_str(rate)); + return 0; + } + + if (strcmp(file_name, "host_tx_eqtr_record") == 0) { + preshoot_bitmap = (hba->host_preshoot_cap << 0x1) | 0x1; + deemphasis_bitmap = (hba->host_deemphasis_cap << 0x1) | 0x1; + num_lanes = pwr_info->lane_tx; + fom_array = rec->host_fom; + snprintf(name, sizeof(name), "%s", "Host"); + } else if (strcmp(file_name, "device_tx_eqtr_record") == 0) { + preshoot_bitmap = (hba->device_preshoot_cap << 0x1) | 0x1; + deemphasis_bitmap = (hba->device_deemphasis_cap << 0x1) | 0x1; + num_lanes = pwr_info->lane_rx; + fom_array = rec->device_fom; + snprintf(name, sizeof(name), "%s", "Device"); + } else { + return -ENOENT; + } + + seq_printf(s, "%s TX EQTR record summary -\n", name); + seq_printf(s, "Target Power Mode: HS-G%u, Rate-%s\n", gear, + ufs_hs_rate_to_str(rate)); + seq_printf(s, "Most recent record index: %d\n", + rec->last_record_index); + seq_printf(s, "Most recent record timestamp: %llu us\n", + ktime_to_us(rec->last_record_ts)); + + for (lane = 0; lane < num_lanes; lane++) { + seq_printf(s, "\nTX Lane %d FOM - %s\n", lane, "PreShoot\\DeEmphasis"); + seq_puts(s, "\\"); + /* Print DeEmphasis header as X-axis. */ + for (deemphasis = 0; deemphasis < TX_HS_NUM_DEEMPHASIS; deemphasis++) + seq_printf(s, "%8d%s", deemphasis, " "); + seq_puts(s, "\n"); + /* Print matrix rows with PreShoot as Y-axis. */ + for (preshoot = 0; preshoot < TX_HS_NUM_PRESHOOT; preshoot++) { + seq_printf(s, "%d", preshoot); + for (deemphasis = 0; deemphasis < TX_HS_NUM_DEEMPHASIS; deemphasis++) { + if (test_bit(preshoot, &preshoot_bitmap) && + test_bit(deemphasis, &deemphasis_bitmap)) { + u8 fom = fom_array[lane][preshoot][deemphasis]; + u8 fom_val = fom & RX_FOM_VALUE_MASK; + bool precode_en = fom & RX_FOM_PRECODING_EN_BIT; + + if (ufshcd_is_txeq_presets_used(hba) && + !ufshcd_is_txeq_preset_selected(preshoot, deemphasis)) + seq_printf(s, "%8s%s", "-", " "); + else + seq_printf(s, "%8u%s", fom_val, + precode_en ? "*" : " "); + } else { + seq_printf(s, "%8s%s", "x", " "); + } + } + seq_puts(s, "\n"); + } + } + + return 0; +} + +static int ufs_tx_eqtr_record_open(struct inode *inode, struct file *file) +{ + return single_open(file, ufs_tx_eqtr_record_show, inode->i_private); +} + +static const struct file_operations ufs_tx_eqtr_record_fops = { + .owner = THIS_MODULE, + .open = ufs_tx_eqtr_record_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t ufs_tx_eq_ctrl_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + u32 gear = (u32)(uintptr_t)file->f_inode->i_private; + struct ufs_hba *hba = hba_from_file(file); + char kbuf[32]; + int ret; + + if (count >= sizeof(kbuf)) + return -EINVAL; + + if (copy_from_user(kbuf, buf, count)) + return -EFAULT; + + if (!ufshcd_is_tx_eq_supported(hba)) + return -EOPNOTSUPP; + + if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL || + !hba->max_pwr_info.is_valid) + return -EBUSY; + + if (!hba->ufs_device_wlun) + return -ENODEV; + + kbuf[count] = '\0'; + + if (sysfs_streq(kbuf, "retrain")) { + ret = ufs_debugfs_get_user_access(hba); + if (ret) + return ret; + ret = ufshcd_retrain_tx_eq(hba, gear); + ufs_debugfs_put_user_access(hba); + } else { + /* Unknown operation */ + return -EINVAL; + } + + return ret ? ret : count; +} + +static int ufs_tx_eq_ctrl_show(struct seq_file *s, void *data) +{ + seq_puts(s, "write 'retrain' to retrain TX Equalization settings\n"); + return 0; +} + +static int ufs_tx_eq_ctrl_open(struct inode *inode, struct file *file) +{ + return single_open(file, ufs_tx_eq_ctrl_show, inode->i_private); +} + +static const struct file_operations ufs_tx_eq_ctrl_fops = { + .owner = THIS_MODULE, + .open = ufs_tx_eq_ctrl_open, + .read = seq_read, + .llseek = seq_lseek, + .write = ufs_tx_eq_ctrl_write, + .release = single_release, +}; + +static const struct ufs_debugfs_attr ufs_tx_eqtr_attrs[] = { + { "host_tx_eqtr_record", 0400, &ufs_tx_eqtr_record_fops }, + { "device_tx_eqtr_record", 0400, &ufs_tx_eqtr_record_fops }, + { "tx_eq_ctrl", 0600, &ufs_tx_eq_ctrl_fops }, + { } +}; + void ufs_debugfs_hba_init(struct ufs_hba *hba) { const struct ufs_debugfs_attr *attr; @@ -230,6 +489,37 @@ void ufs_debugfs_hba_init(struct ufs_hba *hba) hba, &ee_usr_mask_fops); debugfs_create_u32("exception_event_rate_limit_ms", 0600, hba->debugfs_root, &hba->debugfs_ee_rate_limit_ms); + + if (!(hba->caps & UFSHCD_CAP_TX_EQUALIZATION)) + return; + + for (u32 gear = UFS_HS_G1; gear <= UFS_HS_GEAR_MAX; gear++) { + struct dentry *txeq_dir; + char name[32]; + + snprintf(name, sizeof(name), "tx_eq_hs_gear%d", gear); + txeq_dir = debugfs_create_dir(name, hba->debugfs_root); + if (IS_ERR_OR_NULL(txeq_dir)) + return; + + d_inode(txeq_dir)->i_private = hba; + + /* Create files for TX Equalization parameters */ + for (attr = ufs_tx_eq_attrs; attr->name; attr++) + debugfs_create_file(attr->name, attr->mode, txeq_dir, + (void *)(uintptr_t)gear, + attr->fops); + + /* TX EQTR is supported for HS-G4 and higher Gears */ + if (gear < UFS_HS_G4) + continue; + + /* Create files for TX EQTR related attributes */ + for (attr = ufs_tx_eqtr_attrs; attr->name; attr++) + debugfs_create_file(attr->name, attr->mode, txeq_dir, + (void *)(uintptr_t)gear, + attr->fops); + } } void ufs_debugfs_hba_exit(struct ufs_hba *hba) diff --git a/drivers/ufs/core/ufs-debugfs.h b/drivers/ufs/core/ufs-debugfs.h index 97548a3f90eb..e5bba9671862 100644 --- a/drivers/ufs/core/ufs-debugfs.h +++ b/drivers/ufs/core/ufs-debugfs.h @@ -5,6 +5,9 @@ #ifndef __UFS_DEBUGFS_H__ #define __UFS_DEBUGFS_H__ +#include +#include + struct ufs_hba; #ifdef CONFIG_DEBUG_FS diff --git a/drivers/ufs/core/ufs-fault-injection.h b/drivers/ufs/core/ufs-fault-injection.h index 996a35769781..d0c870e19f0e 100644 --- a/drivers/ufs/core/ufs-fault-injection.h +++ b/drivers/ufs/core/ufs-fault-injection.h @@ -6,6 +6,8 @@ #include #include +struct ufs_hba; + #ifdef CONFIG_SCSI_UFS_FAULT_INJECTION void ufs_fault_inject_hba_init(struct ufs_hba *hba); bool ufs_trigger_eh(struct ufs_hba *hba); diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c index 18a95b728633..c1b1d67a1ddc 100644 --- a/drivers/ufs/core/ufs-mcq.c +++ b/drivers/ufs/core/ufs-mcq.c @@ -31,7 +31,8 @@ #define UFSHCD_ENABLE_MCQ_INTRS (UTP_TASK_REQ_COMPL |\ UFSHCD_ERROR_MASK |\ - MCQ_CQ_EVENT_STATUS) + MCQ_CQ_EVENT_STATUS |\ + MCQ_IAG_EVENT_STATUS) /* Max mcq register polling time in microseconds */ #define MCQ_POLL_US 500000 @@ -272,14 +273,29 @@ void ufshcd_mcq_write_cqis(struct ufs_hba *hba, u32 val, int i) } EXPORT_SYMBOL_GPL(ufshcd_mcq_write_cqis); +u32 ufshcd_mcq_read_mcqiacr(struct ufs_hba *hba, int i) +{ + return readl(mcq_opr_base(hba, OPR_CQIS, i) + REG_MCQIACR); +} + +void ufshcd_mcq_write_mcqiacr(struct ufs_hba *hba, u32 val, int i) +{ + writel(val, mcq_opr_base(hba, OPR_CQIS, i) + REG_MCQIACR); +} + /* - * Current MCQ specification doesn't provide a Task Tag or its equivalent in + * UFSHCI 4.0 MCQ specification doesn't provide a Task Tag or its equivalent in * the Completion Queue Entry. Find the Task Tag using an indirect method. + * UFSHCI 4.1 and above can directly return the Task Tag in the Completion Queue + * Entry. */ static int ufshcd_mcq_get_tag(struct ufs_hba *hba, struct cq_entry *cqe) { u64 addr; + if (hba->ufs_version >= ufshci_version(4, 1)) + return cqe->task_tag; + /* sizeof(struct utp_transfer_cmd_desc) must be a multiple of 128 */ BUILD_BUG_ON(sizeof(struct utp_transfer_cmd_desc) & GENMASK(6, 0)); @@ -301,9 +317,19 @@ static void ufshcd_mcq_process_cqe(struct ufs_hba *hba, ufshcd_compl_one_cqe(hba, tag, cqe); /* After processed the cqe, mark it empty (invalid) entry */ cqe->command_desc_base_addr = 0; + } else { + dev_err(hba->dev, "Abnormal CQ entry!\n"); } } +/* + * This function is called from the UFS error handler with the UFS host + * controller disabled (HCE = 0). Reading host controller registers, e.g. the + * CQ tail pointer (CQTPy), may not be safe with the host controller disabled. + * Hence, iterate over all completion queue entries. This won't result in + * double completions because ufshcd_mcq_process_cqe() clears a CQE after it + * has been processed. + */ void ufshcd_mcq_compl_all_cqes_lock(struct ufs_hba *hba, struct ufs_hw_queue *hwq) { diff --git a/drivers/ufs/core/ufs-sysfs.c b/drivers/ufs/core/ufs-sysfs.c index 384d958615d7..99af3c73f1af 100644 --- a/drivers/ufs/core/ufs-sysfs.c +++ b/drivers/ufs/core/ufs-sysfs.c @@ -605,6 +605,34 @@ static ssize_t device_lvl_exception_id_show(struct device *dev, return sysfs_emit(buf, "%llu\n", exception_id); } +static ssize_t dme_qos_notification_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + return sysfs_emit(buf, "0x%x\n", atomic_read(&hba->dme_qos_notification)); +} + +static ssize_t dme_qos_notification_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + unsigned int value; + + if (kstrtouint(buf, 0, &value)) + return -EINVAL; + + /* the only supported usecase is to reset the dme_qos_notification */ + if (value) + return -EINVAL; + + atomic_set(&hba->dme_qos_notification, 0); + + return count; +} + static DEVICE_ATTR_RW(rpm_lvl); static DEVICE_ATTR_RO(rpm_target_dev_state); static DEVICE_ATTR_RO(rpm_target_link_state); @@ -621,6 +649,7 @@ static DEVICE_ATTR_RW(pm_qos_enable); static DEVICE_ATTR_RO(critical_health); static DEVICE_ATTR_RW(device_lvl_exception_count); static DEVICE_ATTR_RO(device_lvl_exception_id); +static DEVICE_ATTR_RW(dme_qos_notification); static struct attribute *ufs_sysfs_ufshcd_attrs[] = { &dev_attr_rpm_lvl.attr, @@ -639,6 +668,7 @@ static struct attribute *ufs_sysfs_ufshcd_attrs[] = { &dev_attr_critical_health.attr, &dev_attr_device_lvl_exception_count.attr, &dev_attr_device_lvl_exception_id.attr, + &dev_attr_dme_qos_notification.attr, NULL }; diff --git a/drivers/ufs/core/ufs-txeq.c b/drivers/ufs/core/ufs-txeq.c new file mode 100644 index 000000000000..b2dc89124353 --- /dev/null +++ b/drivers/ufs/core/ufs-txeq.c @@ -0,0 +1,1293 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Qualcomm Technologies, Inc. + * + * Author: + * Can Guo + */ + +#include +#include +#include +#include +#include +#include +#include "ufshcd-priv.h" + +static bool use_adaptive_txeq; +module_param(use_adaptive_txeq, bool, 0644); +MODULE_PARM_DESC(use_adaptive_txeq, "Find and apply optimal TX Equalization settings before changing Power Mode (default: false)"); + +static int txeq_gear_set(const char *val, const struct kernel_param *kp) +{ + return param_set_uint_minmax(val, kp, UFS_HS_G1, UFS_HS_GEAR_MAX); +} + +static const struct kernel_param_ops txeq_gear_ops = { + .set = txeq_gear_set, + .get = param_get_uint, +}; + +static unsigned int adaptive_txeq_gear = UFS_HS_G6; +module_param_cb(adaptive_txeq_gear, &txeq_gear_ops, &adaptive_txeq_gear, 0644); +MODULE_PARM_DESC(adaptive_txeq_gear, "For HS-Gear[n] and above, adaptive txeq shall be used"); + +static bool use_txeq_presets; +module_param(use_txeq_presets, bool, 0644); +MODULE_PARM_DESC(use_txeq_presets, "Use only the 8 TX Equalization Presets (pre-defined Pre-Shoot & De-Emphasis combinations) for TX EQTR (default: false)"); + +static bool txeq_presets_selected[UFS_TX_EQ_PRESET_MAX] = {[0 ... (UFS_TX_EQ_PRESET_MAX - 1)] = 1}; +module_param_array(txeq_presets_selected, bool, NULL, 0644); +MODULE_PARM_DESC(txeq_presets_selected, "Use only the selected Presets out of the 8 TX Equalization Presets for TX EQTR"); + +/* + * ufs_tx_eq_preset - Table of minimum required list of presets. + * + * A HS-G6 capable M-TX shall support the presets defined in M-PHY v6.0 spec. + * Preset Pre-Shoot(dB) De-Emphasis(dB) + * P0 0.0 0.0 + * P1 0.0 0.8 + * P2 0.0 1.6 + * P3 0.8 0.0 + * P4 1.6 0.0 + * P5 0.8 0.8 + * P6 0.8 1.6 + * P7 1.6 0.8 + */ +static const struct __ufs_tx_eq_preset { + u8 preshoot; + u8 deemphasis; +} ufs_tx_eq_preset[UFS_TX_EQ_PRESET_MAX] = { + [UFS_TX_EQ_PRESET_P0] = {UFS_TX_HS_PRESHOOT_DB_0P0, UFS_TX_HS_DEEMPHASIS_DB_0P0}, + [UFS_TX_EQ_PRESET_P1] = {UFS_TX_HS_PRESHOOT_DB_0P0, UFS_TX_HS_DEEMPHASIS_DB_0P8}, + [UFS_TX_EQ_PRESET_P2] = {UFS_TX_HS_PRESHOOT_DB_0P0, UFS_TX_HS_DEEMPHASIS_DB_1P6}, + [UFS_TX_EQ_PRESET_P3] = {UFS_TX_HS_PRESHOOT_DB_0P8, UFS_TX_HS_DEEMPHASIS_DB_0P0}, + [UFS_TX_EQ_PRESET_P4] = {UFS_TX_HS_PRESHOOT_DB_1P6, UFS_TX_HS_DEEMPHASIS_DB_0P0}, + [UFS_TX_EQ_PRESET_P5] = {UFS_TX_HS_PRESHOOT_DB_0P8, UFS_TX_HS_DEEMPHASIS_DB_0P8}, + [UFS_TX_EQ_PRESET_P6] = {UFS_TX_HS_PRESHOOT_DB_0P8, UFS_TX_HS_DEEMPHASIS_DB_1P6}, + [UFS_TX_EQ_PRESET_P7] = {UFS_TX_HS_PRESHOOT_DB_1P6, UFS_TX_HS_DEEMPHASIS_DB_0P8}, +}; + +/* + * pa_peer_rx_adapt_initial - Table of UniPro PA_PeerRxHSGnAdaptInitial + * attribute IDs for High Speed (HS) Gears. + * + * This table maps HS Gears to their respective UniPro PA_PeerRxHSGnAdaptInitial + * attribute IDs. Entries for Gears 1-3 are 0 (unsupported). + */ +static const u32 pa_peer_rx_adapt_initial[UFS_HS_GEAR_MAX] = { + 0, + 0, + 0, + PA_PEERRXHSG4ADAPTINITIAL, + PA_PEERRXHSG5ADAPTINITIAL, + PA_PEERRXHSG6ADAPTINITIALL0L3 +}; + +/* + * rx_adapt_initial_cap - Table of M-PHY RX_HS_Gn_ADAPT_INITIAL_Capability + * attribute IDs for High Speed (HS) Gears. + * + * This table maps HS Gears to their respective M-PHY + * RX_HS_Gn_ADAPT_INITIAL_Capability attribute IDs. Entries for Gears 1-3 are 0 + * (unsupported). + */ +static const u32 rx_adapt_initial_cap[UFS_HS_GEAR_MAX] = { + 0, + 0, + 0, + RX_HS_G4_ADAPT_INITIAL_CAP, + RX_HS_G5_ADAPT_INITIAL_CAP, + RX_HS_G6_ADAPT_INITIAL_CAP +}; + +/* + * pa_tx_eq_setting - Table of UniPro PA_TxEQGnSetting attribute IDs for High + * Speed (HS) Gears. + * + * This table maps HS Gears to their respective UniPro PA_TxEQGnSetting + * attribute IDs. + */ +static const u32 pa_tx_eq_setting[UFS_HS_GEAR_MAX] = { + PA_TXEQG1SETTING, + PA_TXEQG2SETTING, + PA_TXEQG3SETTING, + PA_TXEQG4SETTING, + PA_TXEQG5SETTING, + PA_TXEQG6SETTING +}; + +/** + * ufshcd_configure_precoding - Configure Pre-Coding for all active lanes + * @hba: per adapter instance + * @params: TX EQ parameters data structure + * + * Bit[7] in RX_FOM indicates that the receiver needs to enable Pre-Coding when + * set. Pre-Coding must be enabled on both the transmitter and receiver to + * ensure proper operation. + * + * Returns 0 on success, non-zero error code otherwise + */ +static int ufshcd_configure_precoding(struct ufs_hba *hba, + struct ufshcd_tx_eq_params *params) +{ + struct ufs_pa_layer_attr *pwr_info = &hba->max_pwr_info.info; + u32 local_precode_en = 0; + u32 peer_precode_en = 0; + int lane, ret; + + /* Enable Pre-Coding for Host's TX & Device's RX pair */ + for (lane = 0; lane < pwr_info->lane_tx; lane++) { + if (params->host[lane].precode_en) { + local_precode_en |= PRECODEEN_TX_BIT(lane); + peer_precode_en |= PRECODEEN_RX_BIT(lane); + } + } + + /* Enable Pre-Coding for Device's TX & Host's RX pair */ + for (lane = 0; lane < pwr_info->lane_rx; lane++) { + if (params->device[lane].precode_en) { + peer_precode_en |= PRECODEEN_TX_BIT(lane); + local_precode_en |= PRECODEEN_RX_BIT(lane); + } + } + + if (!local_precode_en && !peer_precode_en) { + dev_dbg(hba->dev, "Pre-Coding is not required for Host and Device\n"); + return 0; + } + + /* Set local PA_PreCodeEn */ + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PRECODEEN), local_precode_en); + if (ret) { + dev_err(hba->dev, "Failed to set local PA_PreCodeEn: %d\n", ret); + return ret; + } + + /* Set peer PA_PreCodeEn */ + ret = ufshcd_dme_peer_set(hba, UIC_ARG_MIB(PA_PRECODEEN), peer_precode_en); + if (ret) { + dev_err(hba->dev, "Failed to set peer PA_PreCodeEn: %d\n", ret); + return ret; + } + + dev_dbg(hba->dev, "Local PA_PreCodeEn: 0x%02x, Peer PA_PreCodeEn: 0x%02x\n", + local_precode_en, peer_precode_en); + + return 0; +} + +void ufshcd_print_tx_eq_params(struct ufs_hba *hba) +{ + struct ufs_pa_layer_attr *pwr_info = &hba->max_pwr_info.info; + struct ufshcd_tx_eq_params *params; + u32 gear = hba->pwr_info.gear_tx; + int lane; + + if (!ufshcd_is_tx_eq_supported(hba)) + return; + + if (gear < UFS_HS_G1 || gear > UFS_HS_GEAR_MAX) + return; + + params = &hba->tx_eq_params[gear - 1]; + if (!params->is_valid || !params->is_applied) + return; + + for (lane = 0; lane < pwr_info->lane_tx; lane++) + dev_dbg(hba->dev, "Host TX Lane %d: PreShoot %u, DeEmphasis %u, FOM %u, PreCodeEn %d\n", + lane, params->host[lane].preshoot, + params->host[lane].deemphasis, + params->host[lane].fom_val, + params->host[lane].precode_en); + + for (lane = 0; lane < pwr_info->lane_rx; lane++) + dev_dbg(hba->dev, "Device TX Lane %d: PreShoot %u, DeEmphasis %u, FOM %u, PreCodeEn %d\n", + lane, params->device[lane].preshoot, + params->device[lane].deemphasis, + params->device[lane].fom_val, + params->device[lane].precode_en); +} + +static inline u32 +ufshcd_compose_tx_eq_setting(struct ufshcd_tx_eq_settings *settings, + int num_lanes) +{ + u32 setting = 0; + int lane; + + for (lane = 0; lane < num_lanes; lane++, settings++) { + setting |= TX_HS_PRESHOOT_BITS(lane, settings->preshoot); + setting |= TX_HS_DEEMPHASIS_BITS(lane, settings->deemphasis); + } + + return setting; +} + +/** + * ufshcd_apply_tx_eq_settings - Apply TX Equalization settings for target gear + * @hba: per adapter instance + * @params: TX EQ parameters data structure + * @gear: target gear + * + * Returns 0 on success, negative error code otherwise + */ +int ufshcd_apply_tx_eq_settings(struct ufs_hba *hba, + struct ufshcd_tx_eq_params *params, u32 gear) +{ + struct ufs_pa_layer_attr *pwr_info = &hba->max_pwr_info.info; + u32 setting; + int ret; + + /* Compose settings for Host's TX Lanes */ + setting = ufshcd_compose_tx_eq_setting(params->host, pwr_info->lane_tx); + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(pa_tx_eq_setting[gear - 1]), setting); + if (ret) + return ret; + + /* Compose settings for Device's TX Lanes */ + setting = ufshcd_compose_tx_eq_setting(params->device, pwr_info->lane_rx); + ret = ufshcd_dme_peer_set(hba, UIC_ARG_MIB(pa_tx_eq_setting[gear - 1]), setting); + if (ret) + return ret; + + /* Configure Pre-Coding */ + if (gear >= UFS_HS_G6) { + ret = ufshcd_configure_precoding(hba, params); + if (ret) { + dev_err(hba->dev, "Failed to configure pre-coding: %d\n", ret); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(ufshcd_apply_tx_eq_settings); + +/** + * ufshcd_evaluate_tx_eqtr_fom - Evaluate TX EQTR FOM results + * @hba: per adapter instance + * @pwr_mode: target power mode containing gear and rate information + * @eqtr_data: TX EQTR data structure + * @h_iter: host TX EQTR iterator data structure + * @d_iter: device TX EQTR iterator data structure + * + * Evaluate TX EQTR FOM results, update host and device TX EQTR data accordingy + * if FOM have been improved compared to previous iteration, and record TX EQTR + * FOM results. + */ +static void ufshcd_evaluate_tx_eqtr_fom(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + struct ufshcd_tx_eqtr_data *eqtr_data, + struct tx_eqtr_iter *h_iter, + struct tx_eqtr_iter *d_iter) +{ + u8 preshoot, deemphasis, fom_value; + bool precode_en; + int lane; + + for (lane = 0; h_iter->is_updated && lane < pwr_mode->lane_tx; lane++) { + preshoot = h_iter->preshoot; + deemphasis = h_iter->deemphasis; + fom_value = h_iter->fom[lane] & RX_FOM_VALUE_MASK; + precode_en = h_iter->fom[lane] & RX_FOM_PRECODING_EN_BIT; + + /* Record host TX EQTR FOM */ + eqtr_data->host_fom[lane][preshoot][deemphasis] = h_iter->fom[lane]; + + /* Check if FOM has been improved for host's TX Lanes */ + if (fom_value > eqtr_data->host[lane].fom_val) { + eqtr_data->host[lane].preshoot = preshoot; + eqtr_data->host[lane].deemphasis = deemphasis; + eqtr_data->host[lane].fom_val = fom_value; + eqtr_data->host[lane].precode_en = precode_en; + } + + dev_dbg(hba->dev, "TX EQTR: Host TX Lane %d: PreShoot %u, DeEmphasis %u, FOM value %u, PreCodeEn %d\n", + lane, preshoot, deemphasis, fom_value, precode_en); + } + + for (lane = 0; d_iter->is_updated && lane < pwr_mode->lane_rx; lane++) { + preshoot = d_iter->preshoot; + deemphasis = d_iter->deemphasis; + fom_value = d_iter->fom[lane] & RX_FOM_VALUE_MASK; + precode_en = d_iter->fom[lane] & RX_FOM_PRECODING_EN_BIT; + + /* Record device TX EQTR FOM */ + eqtr_data->device_fom[lane][preshoot][deemphasis] = d_iter->fom[lane]; + + /* Check if FOM has been improved for Device's TX Lanes */ + if (fom_value > eqtr_data->device[lane].fom_val) { + eqtr_data->device[lane].preshoot = preshoot; + eqtr_data->device[lane].deemphasis = deemphasis; + eqtr_data->device[lane].fom_val = fom_value; + eqtr_data->device[lane].precode_en = precode_en; + } + + dev_dbg(hba->dev, "TX EQTR: Device TX Lane %d: PreShoot %u, DeEmphasis %u, FOM value %u, PreCodeEn %d\n", + lane, preshoot, deemphasis, fom_value, precode_en); + } +} + +/** + * ufshcd_get_rx_fom - Get Figure of Merit (FOM) for both sides + * @hba: per adapter instance + * @pwr_mode: target power mode containing gear and rate information + * @h_iter: host TX EQTR iterator data structure + * @d_iter: device TX EQTR iterator data structure + * + * Returns 0 on success, negative error code otherwise + */ +static int ufshcd_get_rx_fom(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + struct tx_eqtr_iter *h_iter, + struct tx_eqtr_iter *d_iter) +{ + int lane, ret; + u32 fom; + + /* Get FOM of host's TX lanes from device's RX_FOM. */ + for (lane = 0; lane < pwr_mode->lane_tx; lane++) { + ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB_SEL(RX_FOM, + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), + &fom); + if (ret) + return ret; + + h_iter->fom[lane] = (u8)fom; + } + + /* Get FOM of device's TX lanes from host's RX_FOM. */ + for (lane = 0; lane < pwr_mode->lane_rx; lane++) { + ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(RX_FOM, + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), + &fom); + if (ret) + return ret; + + d_iter->fom[lane] = (u8)fom; + } + + ret = ufshcd_vops_get_rx_fom(hba, pwr_mode, h_iter, d_iter); + if (ret) + dev_err(hba->dev, "Failed to get FOM via vops: %d\n", ret); + + return ret; +} + +bool ufshcd_is_txeq_presets_used(struct ufs_hba *hba) +{ + return use_txeq_presets; +} + +bool ufshcd_is_txeq_preset_selected(u8 preshoot, u8 deemphasis) +{ + int i; + + for (i = 0; i < UFS_TX_EQ_PRESET_MAX; i++) { + if (!txeq_presets_selected[i]) + continue; + + if (preshoot == ufs_tx_eq_preset[i].preshoot && + deemphasis == ufs_tx_eq_preset[i].deemphasis) + return true; + } + + return false; +} + +/** + * tx_eqtr_iter_try_update - Try to update a TX EQTR iterator + * @iter: TX EQTR iterator data structure + * @preshoot: PreShoot value + * @deemphasis: DeEmphasis value + * + * This function validates whether the provided PreShoot and DeEmphasis + * combination can be used or not. If yes, it updates the TX EQTR iterator with + * the provided PreShoot and DeEmphasis, it also sets the is_updated flag + * to indicate the iterator has been updated. + */ +static void tx_eqtr_iter_try_update(struct tx_eqtr_iter *iter, + u8 preshoot, u8 deemphasis) +{ + if (!test_bit(preshoot, &iter->preshoot_bitmap) || + !test_bit(deemphasis, &iter->deemphasis_bitmap) || + (use_txeq_presets && !ufshcd_is_txeq_preset_selected(preshoot, deemphasis))) { + iter->is_updated = false; + return; + } + + iter->preshoot = preshoot; + iter->deemphasis = deemphasis; + iter->is_updated = true; +} + +/** + * tx_eqtr_iter_update() - Update host and deviceTX EQTR iterators + * @preshoot: PreShoot value + * @deemphasis: DeEmphasis value + * @h_iter: Host TX EQTR iterator data structure + * @d_iter: Device TX EQTR iterator data structure + * + * Updates host and device TX Equalization training iterators with the + * provided PreShoot and DeEmphasis. + * + * Return: true if host and/or device TX Equalization training iterator has + * been updated to the provided PreShoot and DeEmphasis, false otherwise. + */ +static bool tx_eqtr_iter_update(u8 preshoot, u8 deemphasis, + struct tx_eqtr_iter *h_iter, + struct tx_eqtr_iter *d_iter) +{ + tx_eqtr_iter_try_update(h_iter, preshoot, deemphasis); + tx_eqtr_iter_try_update(d_iter, preshoot, deemphasis); + + return h_iter->is_updated || d_iter->is_updated; +} + +/** + * ufshcd_tx_eqtr_iter_init - Initialize host and device TX EQTR iterators + * @hba: per adapter instance + * @h_iter: host TX EQTR iterator data structure + * @d_iter: device TX EQTR iterator data structure + * + * This function initializes the TX EQTR iterator structures for both host and + * device by reading their TX equalization capabilities. The capabilities are + * cached in the hba structure to avoid redundant DME operations in subsequent + * calls. In the TX EQTR procedure, the iterator structures are updated by + * tx_eqtr_iter_update() to systematically iterate through supported TX + * Equalization setting combinations. + * + * Returns 0 on success, negative error code otherwise + */ +static int ufshcd_tx_eqtr_iter_init(struct ufs_hba *hba, + struct tx_eqtr_iter *h_iter, + struct tx_eqtr_iter *d_iter) +{ + u32 cap; + int ret; + + if (!hba->host_preshoot_cap) { + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(TX_HS_PRESHOOT_SETTING_CAP), &cap); + if (ret) + return ret; + + hba->host_preshoot_cap = cap & TX_EQTR_CAP_MASK; + } + + if (!hba->host_deemphasis_cap) { + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(TX_HS_DEEMPHASIS_SETTING_CAP), &cap); + if (ret) + return ret; + + hba->host_deemphasis_cap = cap & TX_EQTR_CAP_MASK; + } + + if (!hba->device_preshoot_cap) { + ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(TX_HS_PRESHOOT_SETTING_CAP), &cap); + if (ret) + return ret; + + hba->device_preshoot_cap = cap & TX_EQTR_CAP_MASK; + } + + if (!hba->device_deemphasis_cap) { + ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(TX_HS_DEEMPHASIS_SETTING_CAP), &cap); + if (ret) + return ret; + + hba->device_deemphasis_cap = cap & TX_EQTR_CAP_MASK; + } + + /* + * Support PreShoot & DeEmphasis of value 0 is mandatory, hence they are + * not reflected in PreShoot/DeEmphasis capabilities. Left shift the + * capability bitmap by 1 and set bit[0] to reflect value 0 is + * supported, such that test_bit() can be used later for convenience. + */ + h_iter->preshoot_bitmap = (hba->host_preshoot_cap << 0x1) | 0x1; + h_iter->deemphasis_bitmap = (hba->host_deemphasis_cap << 0x1) | 0x1; + d_iter->preshoot_bitmap = (hba->device_preshoot_cap << 0x1) | 0x1; + d_iter->deemphasis_bitmap = (hba->device_deemphasis_cap << 0x1) | 0x1; + + return 0; +} + +/** + * adapt_cap_to_t_adapt - Calculate TAdapt from adapt capability + * @adapt_cap: Adapt capability + * + * For NRZ: + * IF (ADAPT_range = FINE) + * TADAPT = 650 x (ADAPT_length + 1) + * ELSE (IF ADAPT_range = COARSE) + * TADAPT = 650 x 2^ADAPT_length + * + * Returns calculated TAdapt value in term of Unit Intervals (UI) + */ +static inline u64 adapt_cap_to_t_adapt(u32 adapt_cap) +{ + u64 tadapt; + u8 adapt_length = adapt_cap & ADAPT_LENGTH_MASK; + + if (!IS_ADAPT_RANGE_COARSE(adapt_cap)) + tadapt = TADAPT_FACTOR * (adapt_length + 1); + else + tadapt = TADAPT_FACTOR * (1 << adapt_length); + + return tadapt; +} + +/** + * adapt_cap_to_t_adapt_l0l3 - Calculate TAdapt_L0_L3 from adapt capability + * @adapt_cap: Adapt capability + * + * For PAM-4: + * IF (ADAPT_range = FINE) + * TADAPT_L0_L3 = 2^9 x ADAPT_length + * ELSE IF (ADAPT_range = COARSE) + * TADAPT_L0_L3 = 2^9 x (2^ADAPT_length) + * + * Returns calculated TAdapt value in term of Unit Intervals (UI) + */ +static inline u64 adapt_cap_to_t_adapt_l0l3(u32 adapt_cap) +{ + u64 tadapt; + u8 adapt_length = adapt_cap & ADAPT_LENGTH_MASK; + + if (!IS_ADAPT_RANGE_COARSE(adapt_cap)) + tadapt = TADAPT_L0L3_FACTOR * adapt_length; + else + tadapt = TADAPT_L0L3_FACTOR * (1 << adapt_length); + + return tadapt; +} + +/** + * adapt_cap_to_t_adapt_l0l1l2l3 - Calculate TAdapt_L0_L1_L2_L3 from adapt capability + * @adapt_cap: Adapt capability + * + * For PAM-4: + * IF (ADAPT_range_L0_L1_L2_L3 = FINE) + * TADAPT_L0_L1_L2_L3 = 2^15 x (ADAPT_length_L0_L1_L2_L3 + 1) + * ELSE IF (ADAPT_range_L0_L1_L2_L3 = COARSE) + * TADAPT_L0_L1_L2_L3 = 2^15 x 2^ADAPT_length_L0_L1_L2_L3 + * + * Returns calculated TAdapt value in term of Unit Intervals (UI) + */ +static inline u64 adapt_cap_to_t_adapt_l0l1l2l3(u32 adapt_cap) +{ + u64 tadapt; + u8 adapt_length = adapt_cap & ADAPT_LENGTH_MASK; + + if (!IS_ADAPT_RANGE_COARSE(adapt_cap)) + tadapt = TADAPT_L0L1L2L3_FACTOR * (adapt_length + 1); + else + tadapt = TADAPT_L0L1L2L3_FACTOR * (1 << adapt_length); + + return tadapt; +} + +/** + * ufshcd_setup_tx_eqtr_adapt_length - Setup TX adapt length for EQTR + * @hba: per adapter instance + * @params: TX EQ parameters data structure + * @gear: target gear for EQTR + * + * This function determines and configures the proper TX adapt length (TAdapt) + * for the TX EQTR procedure based on the target gear and RX adapt capabilities + * of both host and device. + * + * Guidelines from MIPI UniPro v3.0 spec - select the minimum Adapt Length for + * the Equalization Training procedure based on the following conditions: + * + * If the target High-Speed Gear n is HS-G4 or HS-G5: + * PA_TxAdaptLength_EQTR[7:0] >= Max (10us, RX_HS_Gn_ADAPT_INITIAL_Capability, + * PA_PeerRxHsGnAdaptInitial) + * PA_TxAdaptLength_EQTR[7:0] shall be shorter than PACP_REQUEST_TIMER (10ms) + * PA_TxAdaptLength_EQTR[15:8] is not relevant for HS-G4 and HS-G5. This field + * is set to 255 (reserved value). + * + * If the target High-Speed Gear n is HS-G6: + * PA_TxAdapthLength_EQTR >= 10us + * PA_TxAdapthLength_EQTR[7:0] >= Max (RX_HS_G6_ADAPT_INITIAL_Capability, + * PA_PeerRxHsG6AdaptInitialL0L3) + * PA_TxAdapthLength_EQTR[15:8] >= Max (RX_HS_G6_ADAPT_INITIAL_L0_L1_L2_L3_Capability, + * PA_PeerRxHsG6AdaptInitialL0L1L2L3) + * PA_TxAdaptLength_EQTR shall be shorter than PACP_REQUEST_TIMER value of 10ms. + * + * Since adapt capabilities encode both range (fine/coarse) and length values, + * direct comparison is not possible. This function converts adapt capabilities + * to actual time durations in Unit Intervals (UI) using the Adapt time + * calculation formular in M-PHY v6.0 spec (Table 8), then selects the maximum + * to ensure both host and device use adequate TX adapt length. + * + * Returns 0 on success, negative error code otherwise + */ +static int ufshcd_setup_tx_eqtr_adapt_length(struct ufs_hba *hba, + struct ufshcd_tx_eq_params *params, + u32 gear) +{ + struct ufshcd_tx_eqtr_record *rec = params->eqtr_record; + u32 adapt_eqtr; + int ret; + + if (rec && rec->saved_adapt_eqtr) { + adapt_eqtr = rec->saved_adapt_eqtr; + goto set_adapt_eqtr; + } + + if (gear == UFS_HS_G4 || gear == UFS_HS_G5) { + u64 t_adapt, t_adapt_local, t_adapt_peer; + u32 adapt_cap_local, adapt_cap_peer, adapt_length; + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(rx_adapt_initial_cap[gear - 1], + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), + &adapt_cap_local); + if (ret) + return ret; + + if (adapt_cap_local > ADAPT_LENGTH_MAX) { + dev_err(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_CAP (0x%x) exceeds MAX\n", + gear, adapt_cap_local); + return -EINVAL; + } + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(pa_peer_rx_adapt_initial[gear - 1]), + &adapt_cap_peer); + if (ret) + return ret; + + if (adapt_cap_peer > ADAPT_LENGTH_MAX) { + dev_err(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_CAP (0x%x) exceeds MAX\n", + gear, adapt_cap_peer); + return -EINVAL; + } + + t_adapt_local = adapt_cap_to_t_adapt(adapt_cap_local); + t_adapt_peer = adapt_cap_to_t_adapt(adapt_cap_peer); + t_adapt = max(t_adapt_local, t_adapt_peer); + + dev_dbg(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_CAP = 0x%x\n", + gear, adapt_cap_local); + dev_dbg(hba->dev, "peer RX_HS_G%u_ADAPT_INITIAL_CAP = 0x%x\n", + gear, adapt_cap_peer); + dev_dbg(hba->dev, "t_adapt_local = %llu UI, t_adapt_peer = %llu UI\n", + t_adapt_local, t_adapt_peer); + dev_dbg(hba->dev, "TAdapt %llu UI selected for TX EQTR\n", + t_adapt); + + adapt_length = (t_adapt_local >= t_adapt_peer) ? + adapt_cap_local : adapt_cap_peer; + + if (gear == UFS_HS_G4 && t_adapt < TX_EQTR_HS_G4_MIN_T_ADAPT) { + dev_dbg(hba->dev, "TAdapt %llu UI is too short for TX EQTR for HS-G%u, use default Adapt 0x%x\n", + t_adapt, gear, TX_EQTR_HS_G4_ADAPT_DEFAULT); + adapt_length = TX_EQTR_HS_G4_ADAPT_DEFAULT; + } else if (gear == UFS_HS_G5 && t_adapt < TX_EQTR_HS_G5_MIN_T_ADAPT) { + dev_dbg(hba->dev, "TAdapt %llu UI is too short for TX EQTR for HS-G%u, use default Adapt 0x%x\n", + t_adapt, gear, TX_EQTR_HS_G5_ADAPT_DEFAULT); + adapt_length = TX_EQTR_HS_G5_ADAPT_DEFAULT; + } + + adapt_eqtr = adapt_length | + (TX_EQTR_ADAPT_RESERVED << TX_EQTR_ADAPT_LENGTH_L0L1L2L3_SHIFT); + } else if (gear == UFS_HS_G6) { + u64 t_adapt, t_adapt_l0l3, t_adapt_l0l3_local, t_adapt_l0l3_peer; + u64 t_adapt_l0l1l2l3, t_adapt_l0l1l2l3_local, t_adapt_l0l1l2l3_peer; + u32 adapt_l0l3_cap_local, adapt_l0l3_cap_peer, adapt_length_l0l3; + u32 adapt_l0l1l2l3_cap_local, adapt_l0l1l2l3_cap_peer, adapt_length_l0l1l2l3; + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(rx_adapt_initial_cap[gear - 1], + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), + &adapt_l0l3_cap_local); + if (ret) + return ret; + + if (adapt_l0l3_cap_local > ADAPT_L0L3_LENGTH_MAX) { + dev_err(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_CAP (0x%x) exceeds MAX\n", + gear, adapt_l0l3_cap_local); + return -EINVAL; + } + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(pa_peer_rx_adapt_initial[gear - 1]), + &adapt_l0l3_cap_peer); + if (ret) + return ret; + + if (adapt_l0l3_cap_peer > ADAPT_L0L3_LENGTH_MAX) { + dev_err(hba->dev, "peer RX_HS_G%u_ADAPT_INITIAL_CAP (0x%x) exceeds MAX\n", + gear, adapt_l0l3_cap_peer); + return -EINVAL; + } + + t_adapt_l0l3_local = adapt_cap_to_t_adapt_l0l3(adapt_l0l3_cap_local); + t_adapt_l0l3_peer = adapt_cap_to_t_adapt_l0l3(adapt_l0l3_cap_peer); + + dev_dbg(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_CAP = 0x%x\n", + gear, adapt_l0l3_cap_local); + dev_dbg(hba->dev, "peer RX_HS_G%u_ADAPT_INITIAL_CAP = 0x%x\n", + gear, adapt_l0l3_cap_peer); + dev_dbg(hba->dev, "t_adapt_l0l3_local = %llu UI, t_adapt_l0l3_peer = %llu UI\n", + t_adapt_l0l3_local, t_adapt_l0l3_peer); + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(RX_HS_G6_ADAPT_INITIAL_L0L1L2L3_CAP, + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), + &adapt_l0l1l2l3_cap_local); + if (ret) + return ret; + + if (adapt_l0l1l2l3_cap_local > ADAPT_L0L1L2L3_LENGTH_MAX) { + dev_err(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_L0L1L2L3_CAP (0x%x) exceeds MAX\n", + gear, adapt_l0l1l2l3_cap_local); + return -EINVAL; + } + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PEERRXHSG6ADAPTINITIALL0L1L2L3), + &adapt_l0l1l2l3_cap_peer); + if (ret) + return ret; + + if (adapt_l0l1l2l3_cap_peer > ADAPT_L0L1L2L3_LENGTH_MAX) { + dev_err(hba->dev, "peer RX_HS_G%u_ADAPT_INITIAL_L0L1L2L3_CAP (0x%x) exceeds MAX\n", + gear, adapt_l0l1l2l3_cap_peer); + return -EINVAL; + } + + t_adapt_l0l1l2l3_local = adapt_cap_to_t_adapt_l0l1l2l3(adapt_l0l1l2l3_cap_local); + t_adapt_l0l1l2l3_peer = adapt_cap_to_t_adapt_l0l1l2l3(adapt_l0l1l2l3_cap_peer); + + dev_dbg(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_L0L1L2L3_CAP = 0x%x\n", + gear, adapt_l0l1l2l3_cap_local); + dev_dbg(hba->dev, "peer RX_HS_G%u_ADAPT_INITIAL_L0L1L2L3_CAP = 0x%x\n", + gear, adapt_l0l1l2l3_cap_peer); + dev_dbg(hba->dev, "t_adapt_l0l1l2l3_local = %llu UI, t_adapt_l0l1l2l3_peer = %llu UI\n", + t_adapt_l0l1l2l3_local, t_adapt_l0l1l2l3_peer); + + t_adapt_l0l1l2l3 = max(t_adapt_l0l1l2l3_local, t_adapt_l0l1l2l3_peer); + t_adapt_l0l3 = max(t_adapt_l0l3_local, t_adapt_l0l3_peer); + t_adapt = t_adapt_l0l3 + t_adapt_l0l1l2l3; + + dev_dbg(hba->dev, "TAdapt %llu PAM-4 UI selected for TX EQTR\n", + t_adapt); + + adapt_length_l0l3 = (t_adapt_l0l3_local >= t_adapt_l0l3_peer) ? + adapt_l0l3_cap_local : adapt_l0l3_cap_peer; + adapt_length_l0l1l2l3 = (t_adapt_l0l1l2l3_local >= t_adapt_l0l1l2l3_peer) ? + adapt_l0l1l2l3_cap_local : adapt_l0l1l2l3_cap_peer; + + if (t_adapt < TX_EQTR_HS_G6_MIN_T_ADAPT) { + dev_dbg(hba->dev, "TAdapt %llu UI is too short for TX EQTR for HS-G%u, use default Adapt 0x%x\n", + t_adapt, gear, TX_EQTR_HS_G6_ADAPT_DEFAULT); + adapt_length_l0l3 = TX_EQTR_HS_G6_ADAPT_DEFAULT; + } + + adapt_eqtr = adapt_length_l0l3 | + (adapt_length_l0l1l2l3 << TX_EQTR_ADAPT_LENGTH_L0L1L2L3_SHIFT); + } else { + return -EINVAL; + } + + if (rec) + rec->saved_adapt_eqtr = (u16)adapt_eqtr; + +set_adapt_eqtr: + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXADAPTLENGTH_EQTR), adapt_eqtr); + if (ret) + dev_err(hba->dev, "Failed to set adapt length for TX EQTR: %d\n", ret); + else + dev_dbg(hba->dev, "PA_TXADAPTLENGTH_EQTR configured to 0x%08x\n", adapt_eqtr); + + return ret; +} + +/** + * ufshcd_compose_tx_eqtr_setting - Compose TX EQTR setting + * @iter: TX EQTR iterator data structure + * @num_lanes: number of active lanes + * + * Returns composed TX EQTR setting, same setting is used for all active lanes + */ +static inline u32 ufshcd_compose_tx_eqtr_setting(struct tx_eqtr_iter *iter, + int num_lanes) +{ + u32 setting = 0; + int lane; + + for (lane = 0; lane < num_lanes; lane++) { + setting |= TX_HS_PRESHOOT_BITS(lane, iter->preshoot); + setting |= TX_HS_DEEMPHASIS_BITS(lane, iter->deemphasis); + } + + return setting; +} + +/** + * ufshcd_apply_tx_eqtr_settings - Apply TX EQTR setting + * @hba: per adapter instance + * @pwr_mode: target power mode containing gear and rate information + * @h_iter: host TX EQTR iterator data structure + * @d_iter: device TX EQTR iterator data structure + * + * Returns 0 on success, negative error code otherwise + */ +static int ufshcd_apply_tx_eqtr_settings(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + struct tx_eqtr_iter *h_iter, + struct tx_eqtr_iter *d_iter) +{ + u32 setting; + int ret; + + setting = ufshcd_compose_tx_eqtr_setting(h_iter, pwr_mode->lane_tx); + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXEQTRSETTING), setting); + if (ret) + return ret; + + setting = ufshcd_compose_tx_eqtr_setting(d_iter, pwr_mode->lane_rx); + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PEERTXEQTRSETTING), setting); + if (ret) + return ret; + + ret = ufshcd_vops_apply_tx_eqtr_settings(hba, pwr_mode, h_iter, d_iter); + + return ret; +} + +/** + * ufshcd_update_tx_eq_params - Update TX Equalization params + * @params: TX EQ parameters data structure + * @pwr_mode: target power mode containing gear and rate + * @eqtr_data: TX EQTR data structure + * + * Update TX Equalization params using results from TX EQTR data. Check also + * the TX EQTR FOM value for each TX lane in the TX EQTR data. If a TX lane got + * a FOM value of 0, restore the TX Equalization settings from the last known + * valid TX Equalization params for that specific TX lane. + */ +static inline void +ufshcd_update_tx_eq_params(struct ufshcd_tx_eq_params *params, + struct ufs_pa_layer_attr *pwr_mode, + struct ufshcd_tx_eqtr_data *eqtr_data) +{ + struct ufshcd_tx_eqtr_record *rec = params->eqtr_record; + + if (params->is_valid) { + int lane; + + for (lane = 0; lane < pwr_mode->lane_tx; lane++) + if (eqtr_data->host[lane].fom_val == 0) + eqtr_data->host[lane] = params->host[lane]; + + for (lane = 0; lane < pwr_mode->lane_rx; lane++) + if (eqtr_data->device[lane].fom_val == 0) + eqtr_data->device[lane] = params->device[lane]; + } + + memcpy(params->host, eqtr_data->host, sizeof(params->host)); + memcpy(params->device, eqtr_data->device, sizeof(params->device)); + + if (!rec) + return; + + memcpy(rec->host_fom, eqtr_data->host_fom, sizeof(rec->host_fom)); + memcpy(rec->device_fom, eqtr_data->device_fom, sizeof(rec->device_fom)); + rec->last_record_ts = ktime_get(); + rec->last_record_index++; +} + +/** + * __ufshcd_tx_eqtr - TX Equalization Training (EQTR) procedure + * @hba: per adapter instance + * @params: TX EQ parameters data structure + * @pwr_mode: target power mode containing gear and rate information + * + * This function implements the complete TX EQTR procedure as defined in UFSHCI + * v5.0 specification. It iterates through all possible combinations of PreShoot + * and DeEmphasis settings to find the optimal TX Equalization settings for all + * active lanes. + * + * Returns 0 on success, negative error code otherwise + */ +static int __ufshcd_tx_eqtr(struct ufs_hba *hba, + struct ufshcd_tx_eq_params *params, + struct ufs_pa_layer_attr *pwr_mode) +{ + struct ufshcd_tx_eqtr_data *eqtr_data __free(kfree) = + kzalloc(sizeof(*eqtr_data), GFP_KERNEL); + struct tx_eqtr_iter h_iter = {}; + struct tx_eqtr_iter d_iter = {}; + u32 gear = pwr_mode->gear_tx; + u8 preshoot, deemphasis; + ktime_t start; + int ret; + + if (!eqtr_data) + return -ENOMEM; + + dev_info(hba->dev, "Start TX EQTR procedure for HS-G%u, Rate-%s, RX Lanes: %u, TX Lanes: %u\n", + gear, ufs_hs_rate_to_str(pwr_mode->hs_rate), + pwr_mode->lane_rx, pwr_mode->lane_tx); + + start = ktime_get(); + + /* Step 1 - Determine the TX Adapt Length for EQTR */ + ret = ufshcd_setup_tx_eqtr_adapt_length(hba, params, gear); + if (ret) { + dev_err(hba->dev, "Failed to setup TX EQTR Adaptation length: %d\n", ret); + return ret; + } + + /* Step 2 - Determine TX Equalization setting capabilities */ + ret = ufshcd_tx_eqtr_iter_init(hba, &h_iter, &d_iter); + if (ret) { + dev_err(hba->dev, "Failed to init TX EQTR data: %d\n", ret); + return ret; + } + + /* TX EQTR main loop */ + for (preshoot = 0; preshoot < TX_HS_NUM_PRESHOOT; preshoot++) { + for (deemphasis = 0; deemphasis < TX_HS_NUM_DEEMPHASIS; deemphasis++) { + if (!tx_eqtr_iter_update(preshoot, deemphasis, &h_iter, &d_iter)) + continue; + + /* Step 3 - Apply TX EQTR settings */ + ret = ufshcd_apply_tx_eqtr_settings(hba, pwr_mode, &h_iter, &d_iter); + if (ret) { + dev_err(hba->dev, "Failed to apply TX EQTR settings (PreShoot %u, DeEmphasis %u): %d\n", + preshoot, deemphasis, ret); + return ret; + } + + /* Step 4 - Trigger UIC TX EQTR */ + ret = ufshcd_uic_tx_eqtr(hba, gear); + if (ret) { + dev_err(hba->dev, "Failed to trigger UIC TX EQTR for target gear %u: %d\n", + gear, ret); + return ret; + } + + /* Step 5 - Get FOM */ + ret = ufshcd_get_rx_fom(hba, pwr_mode, &h_iter, &d_iter); + if (ret) { + dev_err(hba->dev, "Failed to get RX_FOM: %d\n", + ret); + return ret; + } + + ufshcd_evaluate_tx_eqtr_fom(hba, pwr_mode, eqtr_data, &h_iter, &d_iter); + } + } + + dev_info(hba->dev, "TX EQTR procedure completed! Time elapsed: %llu ms\n", + ktime_to_ms(ktime_sub(ktime_get(), start))); + + ufshcd_update_tx_eq_params(params, pwr_mode, eqtr_data); + + return ret; +} + +/** + * ufshcd_tx_eqtr_prepare - Prepare UFS link for TX EQTR procedure + * @hba: per adapter instance + * @pwr_mode: target power mode containing gear and rate + * + * This function prepares the UFS link for TX Equalization Training (EQTR) by + * establishing the proper initial conditions required by the EQTR procedure. + * It ensures that EQTR starts from the most reliable Power Mode (HS-G1) with + * all connected lanes activated and sets host TX HS Adapt Type to INITIAL. + * + * Returns 0 on successful preparation, negative error code on failure + */ +static int ufshcd_tx_eqtr_prepare(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode) +{ + struct ufs_pa_layer_attr pwr_mode_hs_g1 = { + /* TX EQTR shall be initiated from the most reliable HS-G1 */ + .gear_rx = UFS_HS_G1, + .gear_tx = UFS_HS_G1, + .lane_rx = pwr_mode->lane_rx, + .lane_tx = pwr_mode->lane_tx, + .pwr_rx = FAST_MODE, + .pwr_tx = FAST_MODE, + /* Use the target power mode's HS rate */ + .hs_rate = pwr_mode->hs_rate, + }; + u32 rate = pwr_mode->hs_rate; + int ret; + + /* Change power mode to HS-G1, activate all connected lanes. */ + ret = ufshcd_change_power_mode(hba, &pwr_mode_hs_g1, + UFSHCD_PMC_POLICY_DONT_FORCE); + if (ret) { + dev_err(hba->dev, "TX EQTR: Failed to change power mode to HS-G1, Rate-%s: %d\n", + ufs_hs_rate_to_str(rate), ret); + return ret; + } + + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXHSADAPTTYPE), + PA_INITIAL_ADAPT); + if (ret) + dev_err(hba->dev, "TX EQTR: Failed to set Host Adapt type to INITIAL: %d\n", + ret); + + return ret; +} + +static void ufshcd_tx_eqtr_unprepare(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode) +{ + int err; + + if (pwr_mode->pwr_rx == SLOWAUTO_MODE || pwr_mode->hs_rate == 0) + return; + + err = ufshcd_change_power_mode(hba, pwr_mode, + UFSHCD_PMC_POLICY_DONT_FORCE); + if (err) + dev_err(hba->dev, "%s: Failed to restore Power Mode: %d\n", + __func__, err); +} + +/** + * ufshcd_tx_eqtr - Perform TX EQTR procedures with vops callbacks + * @hba: per adapter instance + * @params: TX EQ parameters data structure to populate + * @pwr_mode: target power mode containing gear and rate information + * + * This is the main entry point for performing TX Equalization Training (EQTR) + * procedure as defined in UFSCHI v5.0 specification. It serves as a wrapper + * around __ufshcd_tx_eqtr() to provide vops support through the variant + * operations framework. + * + * Returns 0 on success, negative error code on failure + */ +static int ufshcd_tx_eqtr(struct ufs_hba *hba, + struct ufshcd_tx_eq_params *params, + struct ufs_pa_layer_attr *pwr_mode) +{ + struct ufs_pa_layer_attr old_pwr_info; + int ret; + + if (!params->eqtr_record) { + params->eqtr_record = devm_kzalloc(hba->dev, + sizeof(*params->eqtr_record), + GFP_KERNEL); + if (!params->eqtr_record) + return -ENOMEM; + } + + memcpy(&old_pwr_info, &hba->pwr_info, sizeof(struct ufs_pa_layer_attr)); + + ret = ufshcd_tx_eqtr_prepare(hba, pwr_mode); + if (ret) { + dev_err(hba->dev, "Failed to prepare TX EQTR: %d\n", ret); + goto out; + } + + ret = ufshcd_vops_tx_eqtr_notify(hba, PRE_CHANGE, pwr_mode); + if (ret) + goto out; + + ret = __ufshcd_tx_eqtr(hba, params, pwr_mode); + if (ret) + goto out; + + ret = ufshcd_vops_tx_eqtr_notify(hba, POST_CHANGE, pwr_mode); + +out: + if (ret) + ufshcd_tx_eqtr_unprepare(hba, &old_pwr_info); + + return ret; +} + +/** + * ufshcd_config_tx_eq_settings - Configure TX Equalization settings + * @hba: per adapter instance + * @pwr_mode: target power mode containing gear and rate information + * @force_tx_eqtr: execute the TX EQTR procedure + * + * This function finds and sets the TX Equalization settings for the given + * target power mode. + * + * Returns 0 on success, error code otherwise + */ +int ufshcd_config_tx_eq_settings(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + bool force_tx_eqtr) +{ + struct ufshcd_tx_eq_params *params; + u32 gear, rate; + + if (!ufshcd_is_tx_eq_supported(hba) || !use_adaptive_txeq) + return 0; + + if (!hba->max_pwr_info.is_valid) { + dev_err(hba->dev, "Max power info is invalid\n"); + return -EINVAL; + } + + if (!pwr_mode) { + dev_err(hba->dev, "Target power mode is NULL\n"); + return -EINVAL; + } + + gear = pwr_mode->gear_tx; + rate = pwr_mode->hs_rate; + + if (gear < UFS_HS_G1 || gear > UFS_HS_GEAR_MAX) { + dev_err(hba->dev, "Invalid HS-Gear (%u) for TX Equalization\n", + gear); + return -EINVAL; + } else if (gear < max_t(u32, adaptive_txeq_gear, UFS_HS_G4)) { + /* TX EQTR is supported for HS-G4 and higher Gears */ + return 0; + } + + if (rate != PA_HS_MODE_A && rate != PA_HS_MODE_B) { + dev_err(hba->dev, "Invalid HS-Rate (%u) for TX Equalization\n", + rate); + return -EINVAL; + } + + params = &hba->tx_eq_params[gear - 1]; + if (!params->is_valid || force_tx_eqtr) { + int ret; + + ret = ufshcd_tx_eqtr(hba, params, pwr_mode); + if (ret) { + dev_err(hba->dev, "Failed to train TX Equalization for HS-G%u, Rate-%s: %d\n", + gear, ufs_hs_rate_to_str(rate), ret); + return ret; + } + + /* Mark TX Equalization settings as valid */ + params->is_valid = true; + params->is_applied = false; + } + + if (params->is_valid && !params->is_applied) { + int ret; + + ret = ufshcd_apply_tx_eq_settings(hba, params, gear); + if (ret) { + dev_err(hba->dev, "Failed to apply TX Equalization settings for HS-G%u, Rate-%s: %d\n", + gear, ufs_hs_rate_to_str(rate), ret); + return ret; + } + + params->is_applied = true; + } + + return 0; +} + +/** + * ufshcd_apply_valid_tx_eq_settings - Apply valid TX Equalization settings + * @hba: per-adapter instance + * + * This function iterates through all supported High-Speed (HS) gears and + * applies valid TX Equalization settings to both Host and Device. + */ +void ufshcd_apply_valid_tx_eq_settings(struct ufs_hba *hba) +{ + struct ufshcd_tx_eq_params *params; + int gear, err; + + if (!ufshcd_is_tx_eq_supported(hba)) + return; + + if (!hba->max_pwr_info.is_valid) { + dev_err(hba->dev, "Max power info is invalid, cannot apply TX Equalization settings\n"); + return; + } + + for (gear = UFS_HS_G1; gear <= UFS_HS_GEAR_MAX; gear++) { + params = &hba->tx_eq_params[gear - 1]; + + if (params->is_valid) { + err = ufshcd_apply_tx_eq_settings(hba, params, gear); + if (err) { + params->is_applied = false; + dev_err(hba->dev, "Failed to apply TX Equalization settings for HS-G%u: %d\n", + gear, err); + } else { + params->is_applied = true; + } + } + } +} + +/** + * ufshcd_retrain_tx_eq - Retrain TX Equalization and apply new settings + * @hba: per-adapter instance + * @gear: target High-Speed (HS) gear for retraining + * + * This function initiates a refresh of the TX Equalization settings for a + * specific HS gear. It scales the clocks to maximum frequency, negotiates the + * power mode with the device, retrains TX EQ and applies new TX EQ settings + * by conducting a Power Mode change. + * + * Returns 0 on success, non-zero error code otherwise + */ +int ufshcd_retrain_tx_eq(struct ufs_hba *hba, u32 gear) +{ + struct ufs_pa_layer_attr new_pwr_info, final_params = {}; + int ret; + + if (!ufshcd_is_tx_eq_supported(hba) || !use_adaptive_txeq) + return -EOPNOTSUPP; + + if (gear < adaptive_txeq_gear) + return -ERANGE; + + ufshcd_hold(hba); + + ret = ufshcd_pause_command_processing(hba, 1 * USEC_PER_SEC); + if (ret) { + ufshcd_release(hba); + return ret; + } + + /* scale up clocks to max frequency before TX EQTR */ + if (ufshcd_is_clkscaling_supported(hba)) + ufshcd_scale_clks(hba, ULONG_MAX, true); + + new_pwr_info = hba->pwr_info; + new_pwr_info.gear_tx = gear; + new_pwr_info.gear_rx = gear; + + ret = ufshcd_vops_negotiate_pwr_mode(hba, &new_pwr_info, &final_params); + if (ret) + memcpy(&final_params, &new_pwr_info, sizeof(final_params)); + + if (final_params.gear_tx != gear) { + dev_err(hba->dev, "Negotiated Gear (%u) does not match target Gear (%u)\n", + final_params.gear_tx, gear); + ret = -EINVAL; + goto out; + } + + ret = ufshcd_config_tx_eq_settings(hba, &final_params, true); + if (ret) { + dev_err(hba->dev, "Failed to config TX Equalization for HS-G%u, Rate-%s: %d\n", + final_params.gear_tx, + ufs_hs_rate_to_str(final_params.hs_rate), ret); + goto out; + } + + /* Change Power Mode to apply the new TX EQ settings */ + ret = ufshcd_change_power_mode(hba, &final_params, + UFSHCD_PMC_POLICY_FORCE); + if (ret) + dev_err(hba->dev, "%s: Failed to change Power Mode to HS-G%u, Rate-%s: %d\n", + __func__, final_params.gear_tx, + ufs_hs_rate_to_str(final_params.hs_rate), ret); + +out: + ufshcd_resume_command_processing(hba); + ufshcd_release(hba); + + return ret; +} diff --git a/drivers/ufs/core/ufshcd-crypto.h b/drivers/ufs/core/ufshcd-crypto.h index c148a5194378..8f66db94e179 100644 --- a/drivers/ufs/core/ufshcd-crypto.h +++ b/drivers/ufs/core/ufshcd-crypto.h @@ -8,7 +8,6 @@ #include #include -#include "ufshcd-priv.h" #include #ifdef CONFIG_SCSI_UFS_CRYPTO diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h index 37c32071e754..0a72148cb053 100644 --- a/drivers/ufs/core/ufshcd-priv.h +++ b/drivers/ufs/core/ufshcd-priv.h @@ -76,8 +76,13 @@ void ufshcd_mcq_compl_all_cqes_lock(struct ufs_hba *hba, bool ufshcd_cmd_inflight(struct scsi_cmnd *cmd); int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag); int ufshcd_mcq_abort(struct scsi_cmnd *cmd); +u32 ufshcd_mcq_read_mcqiacr(struct ufs_hba *hba, int i); +void ufshcd_mcq_write_mcqiacr(struct ufs_hba *hba, u32 val, int i); int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag); void ufshcd_release_scsi_cmd(struct ufs_hba *hba, struct scsi_cmnd *cmd); +int ufshcd_pause_command_processing(struct ufs_hba *hba, u64 timeout_us); +void ufshcd_resume_command_processing(struct ufs_hba *hba); +int ufshcd_scale_clks(struct ufs_hba *hba, unsigned long freq, bool scale_up); /** * enum ufs_descr_fmt - UFS string descriptor format @@ -103,6 +108,16 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, int ufshcd_wb_toggle(struct ufs_hba *hba, bool enable); int ufshcd_read_device_lvl_exception_id(struct ufs_hba *hba, u64 *exception_id); +int ufshcd_uic_tx_eqtr(struct ufs_hba *hba, int gear); +void ufshcd_apply_valid_tx_eq_settings(struct ufs_hba *hba); +int ufshcd_config_tx_eq_settings(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + bool force_tx_eqtr); +void ufshcd_print_tx_eq_params(struct ufs_hba *hba); +bool ufshcd_is_txeq_presets_used(struct ufs_hba *hba); +bool ufshcd_is_txeq_preset_selected(u8 preshoot, u8 deemphasis); +int ufshcd_retrain_tx_eq(struct ufs_hba *hba, u32 gear); + /* Wrapper functions for safely calling variant operations */ static inline const char *ufshcd_get_var_name(struct ufs_hba *hba) { @@ -167,14 +182,24 @@ static inline int ufshcd_vops_link_startup_notify(struct ufs_hba *hba, return 0; } +static inline int ufshcd_vops_negotiate_pwr_mode(struct ufs_hba *hba, + const struct ufs_pa_layer_attr *dev_max_params, + struct ufs_pa_layer_attr *dev_req_params) +{ + if (hba->vops && hba->vops->negotiate_pwr_mode) + return hba->vops->negotiate_pwr_mode(hba, dev_max_params, + dev_req_params); + + return -ENOTSUPP; +} + static inline int ufshcd_vops_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_change_status status, - const struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) { if (hba->vops && hba->vops->pwr_change_notify) return hba->vops->pwr_change_notify(hba, status, - dev_max_params, dev_req_params); + dev_req_params); return -ENOTSUPP; } @@ -287,6 +312,38 @@ static inline u32 ufshcd_vops_freq_to_gear_speed(struct ufs_hba *hba, unsigned l return 0; } +static inline int ufshcd_vops_get_rx_fom(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + struct tx_eqtr_iter *h_iter, + struct tx_eqtr_iter *d_iter) +{ + if (hba->vops && hba->vops->get_rx_fom) + return hba->vops->get_rx_fom(hba, pwr_mode, h_iter, d_iter); + + return 0; +} + +static inline int ufshcd_vops_apply_tx_eqtr_settings(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + struct tx_eqtr_iter *h_iter, + struct tx_eqtr_iter *d_iter) +{ + if (hba->vops && hba->vops->apply_tx_eqtr_settings) + return hba->vops->apply_tx_eqtr_settings(hba, pwr_mode, h_iter, d_iter); + + return 0; +} + +static inline int ufshcd_vops_tx_eqtr_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status, + struct ufs_pa_layer_attr *pwr_mode) +{ + if (hba->vops && hba->vops->tx_eqtr_notify) + return hba->vops->tx_eqtr_notify(hba, status, pwr_mode); + + return 0; +} + extern const struct ufs_pm_lvl_states ufs_pm_lvl_states[]; /** diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 9ceb6d6d479d..c3f08957d179 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -315,6 +315,9 @@ static const struct ufs_dev_quirk ufs_fixups[] = { { .wmanufacturerid = UFS_VENDOR_TOSHIBA, .model = "THGLF2G9D8KBADG", .quirk = UFS_DEVICE_QUIRK_PA_TACTIVATE }, + { .wmanufacturerid = UFS_VENDOR_TOSHIBA, + .model = "THGJFJT0E25BAIP", + .quirk = UFS_DEVICE_QUIRK_NO_TIMESTAMP_SUPPORT }, { .wmanufacturerid = UFS_VENDOR_TOSHIBA, .model = "THGJFJT1E45BATP", .quirk = UFS_DEVICE_QUIRK_NO_TIMESTAMP_SUPPORT }, @@ -334,11 +337,7 @@ static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba); static int ufshcd_host_reset_and_restore(struct ufs_hba *hba); static void ufshcd_resume_clkscaling(struct ufs_hba *hba); static void ufshcd_suspend_clkscaling(struct ufs_hba *hba); -static int ufshcd_scale_clks(struct ufs_hba *hba, unsigned long freq, - bool scale_up); static irqreturn_t ufshcd_intr(int irq, void *__hba); -static int ufshcd_change_power_mode(struct ufs_hba *hba, - struct ufs_pa_layer_attr *pwr_mode); static int ufshcd_setup_hba_vreg(struct ufs_hba *hba, bool on); static int ufshcd_setup_vreg(struct ufs_hba *hba, bool on); static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba, @@ -1212,8 +1211,7 @@ static int ufshcd_opp_set_rate(struct ufs_hba *hba, unsigned long freq) * * Return: 0 if successful; < 0 upon failure. */ -static int ufshcd_scale_clks(struct ufs_hba *hba, unsigned long freq, - bool scale_up) +int ufshcd_scale_clks(struct ufs_hba *hba, unsigned long freq, bool scale_up) { int ret = 0; ktime_t start = ktime_get(); @@ -1366,6 +1364,48 @@ out: return ret; } +/** + * ufshcd_pause_command_processing - Pause command processing + * @hba: per-adapter instance + * @timeout_us: timeout in microseconds to wait for pending commands to finish + * + * This function stops new command submissions and waits for existing commands + * to complete. + * + * Return: 0 on success, %-EBUSY if commands did not finish within @timeout_us. + * On failure, all acquired locks are released and the tagset is unquiesced. + */ +int ufshcd_pause_command_processing(struct ufs_hba *hba, u64 timeout_us) +{ + int ret = 0; + + mutex_lock(&hba->host->scan_mutex); + blk_mq_quiesce_tagset(&hba->host->tag_set); + down_write(&hba->clk_scaling_lock); + + if (ufshcd_wait_for_pending_cmds(hba, timeout_us)) { + ret = -EBUSY; + up_write(&hba->clk_scaling_lock); + blk_mq_unquiesce_tagset(&hba->host->tag_set); + mutex_unlock(&hba->host->scan_mutex); + } + + return ret; +} + +/** + * ufshcd_resume_command_processing - Resume command processing + * @hba: per-adapter instance + * + * This function resumes command submissions. + */ +void ufshcd_resume_command_processing(struct ufs_hba *hba) +{ + up_write(&hba->clk_scaling_lock); + blk_mq_unquiesce_tagset(&hba->host->tag_set); + mutex_unlock(&hba->host->scan_mutex); +} + /** * ufshcd_scale_gear - scale up/down UFS gear * @hba: per adapter instance @@ -1411,7 +1451,8 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, u32 target_gear, bool scale_up config_pwr_mode: /* check if the power mode needs to be changed or not? */ - ret = ufshcd_config_pwr_mode(hba, &new_pwr_info); + ret = ufshcd_config_pwr_mode(hba, &new_pwr_info, + UFSHCD_PMC_POLICY_DONT_FORCE); if (ret) dev_err(hba->dev, "%s: failed err %d, old gear: (tx %d rx %d), new gear: (tx %d rx %d)", __func__, ret, @@ -4252,7 +4293,8 @@ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, pwr_mode_change = true; } if (pwr_mode_change) { - ret = ufshcd_change_power_mode(hba, &temp_pwr_info); + ret = ufshcd_change_power_mode(hba, &temp_pwr_info, + UFSHCD_PMC_POLICY_DONT_FORCE); if (ret) goto out; } @@ -4276,7 +4318,8 @@ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, if (peer && (hba->quirks & UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE) && pwr_mode_change) - ufshcd_change_power_mode(hba, &orig_pwr_info); + ufshcd_change_power_mode(hba, &orig_pwr_info, + UFSHCD_PMC_POLICY_DONT_FORCE); out: return ret; } @@ -4343,16 +4386,18 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) ret = __ufshcd_send_uic_cmd(hba, cmd); if (ret) { dev_err(hba->dev, - "pwr ctrl cmd 0x%x with mode 0x%x uic error %d\n", - cmd->command, cmd->argument3, ret); + "pwr ctrl cmd 0x%x with (MIBattribute 0x%x, mode 0x%x) uic error %d\n", + cmd->command, UIC_GET_ATTR_ID(cmd->argument1), + cmd->argument3, ret); goto out; } if (!wait_for_completion_timeout(hba->uic_async_done, msecs_to_jiffies(uic_cmd_timeout))) { dev_err(hba->dev, - "pwr ctrl cmd 0x%x with mode 0x%x completion timeout\n", - cmd->command, cmd->argument3); + "pwr ctrl cmd 0x%x with (MIBattribute 0x%x, mode 0x%x) completion timeout\n", + cmd->command, UIC_GET_ATTR_ID(cmd->argument1), + cmd->argument3); if (!cmd->cmd_active) { dev_err(hba->dev, "%s: Power Mode Change operation has been completed, go check UPMCRS\n", @@ -4368,14 +4413,16 @@ check_upmcrs: status = ufshcd_get_upmcrs(hba); if (status != PWR_LOCAL) { dev_err(hba->dev, - "pwr ctrl cmd 0x%x failed, host upmcrs:0x%x\n", - cmd->command, status); + "pwr ctrl cmd 0x%x with (MIBattribute 0x%x, mode 0x%x) failed, host upmcrs:0x%x\n", + cmd->command, UIC_GET_ATTR_ID(cmd->argument1), + cmd->argument3, status); ret = (status != PWR_OK) ? status : -1; } out: if (ret) { ufshcd_print_host_state(hba); ufshcd_print_pwr_info(hba); + ufshcd_print_tx_eq_params(hba); ufshcd_print_evt_hist(hba); } @@ -4393,6 +4440,29 @@ out_unlock: return ret; } +/** + * ufshcd_uic_tx_eqtr - Perform UIC TX Equalization Training + * @hba: per adapter instance + * @gear: target gear for EQTR + * + * Returns 0 on success, negative error code otherwise + */ +int ufshcd_uic_tx_eqtr(struct ufs_hba *hba, int gear) +{ + struct uic_command uic_cmd = { + .command = UIC_CMD_DME_SET, + .argument1 = UIC_ARG_MIB(PA_EQTR_GEAR), + .argument3 = gear, + }; + int ret; + + ufshcd_hold(hba); + ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd); + ufshcd_release(hba); + + return ret; +} + /** * ufshcd_send_bsg_uic_cmd - Send UIC commands requested via BSG layer and retrieve the result * @hba: per adapter instance @@ -4656,13 +4726,33 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) return 0; } -static int ufshcd_change_power_mode(struct ufs_hba *hba, - struct ufs_pa_layer_attr *pwr_mode) +/** + * ufshcd_dme_change_power_mode() - UniPro DME Power Mode change sequence + * @hba: per-adapter instance + * @pwr_mode: pointer to the target power mode (gear/lane) attributes + * @pmc_policy: Power Mode change policy + * + * This function handles the low-level DME (Device Management Entity) + * configuration required to transition the UFS link to a new power mode. It + * performs the following steps: + * 1. Checks if the requested mode matches the current state. + * 2. Sets M-PHY and UniPro attributes including Gear (PA_RXGEAR/TXGEAR), + * Lanes, Termination, and HS Series (PA_HSSERIES). + * 3. Configures default UniPro timeout values (DL_FC0, etc.) unless + * explicitly skipped via quirks. + * 4. Triggers the actual hardware mode change via ufshcd_uic_change_pwr_mode(). + * 5. Updates the HBA's cached power information on success. + * + * Return: 0 on success, non-zero error code on failure. + */ +static int ufshcd_dme_change_power_mode(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + enum ufshcd_pmc_policy pmc_policy) { int ret; /* if already configured to the requested pwr_mode */ - if (!hba->force_pmc && + if (pmc_policy == UFSHCD_PMC_POLICY_DONT_FORCE && pwr_mode->gear_rx == hba->pwr_info.gear_rx && pwr_mode->gear_tx == hba->pwr_info.gear_tx && pwr_mode->lane_rx == hba->pwr_info.lane_rx && @@ -4741,32 +4831,68 @@ static int ufshcd_change_power_mode(struct ufs_hba *hba, return ret; } +/** + * ufshcd_change_power_mode() - Change UFS Link Power Mode + * @hba: per-adapter instance + * @pwr_mode: pointer to the target power mode (gear/lane) attributes + * @pmc_policy: Power Mode change policy + * + * This function handles the high-level sequence for changing the UFS link + * power mode. It triggers vendor-specific pre-change notification, + * executes the DME (Device Management Entity) power mode change sequence, + * and, upon success, triggers vendor-specific post-change notification. + * + * Return: 0 on success, non-zero error code on failure. + */ +int ufshcd_change_power_mode(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + enum ufshcd_pmc_policy pmc_policy) +{ + int ret; + + ufshcd_vops_pwr_change_notify(hba, PRE_CHANGE, pwr_mode); + + ret = ufshcd_dme_change_power_mode(hba, pwr_mode, pmc_policy); + + if (!ret) + ufshcd_vops_pwr_change_notify(hba, POST_CHANGE, pwr_mode); + + return ret; +} +EXPORT_SYMBOL_GPL(ufshcd_change_power_mode); + /** * ufshcd_config_pwr_mode - configure a new power mode * @hba: per-adapter instance * @desired_pwr_mode: desired power configuration + * @pmc_policy: Power Mode change policy * * Return: 0 upon success; < 0 upon failure. */ int ufshcd_config_pwr_mode(struct ufs_hba *hba, - struct ufs_pa_layer_attr *desired_pwr_mode) + struct ufs_pa_layer_attr *desired_pwr_mode, + enum ufshcd_pmc_policy pmc_policy) { struct ufs_pa_layer_attr final_params = { 0 }; int ret; - ret = ufshcd_vops_pwr_change_notify(hba, PRE_CHANGE, - desired_pwr_mode, &final_params); + ret = ufshcd_vops_negotiate_pwr_mode(hba, desired_pwr_mode, + &final_params); + if (ret) { + if (ret != -ENOTSUPP) + dev_err(hba->dev, "Failed to negotiate power mode: %d, use desired as is\n", + ret); - if (ret) memcpy(&final_params, desired_pwr_mode, sizeof(final_params)); + } - ret = ufshcd_change_power_mode(hba, &final_params); + ret = ufshcd_config_tx_eq_settings(hba, &final_params, false); + if (ret) + dev_warn(hba->dev, "Failed to configure TX Equalization for HS-G%u, Rate-%s: %d\n", + final_params.gear_tx, + ufs_hs_rate_to_str(final_params.hs_rate), ret); - if (!ret) - ufshcd_vops_pwr_change_notify(hba, POST_CHANGE, NULL, - &final_params); - - return ret; + return ufshcd_change_power_mode(hba, &final_params, pmc_policy); } EXPORT_SYMBOL_GPL(ufshcd_config_pwr_mode); @@ -5580,8 +5706,11 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) guard(spinlock_irqsave)(hba->host->host_lock); cmd = hba->active_uic_cmd; - if (!cmd) + if (!cmd) { + dev_err(hba->dev, + "No active UIC command. Maybe a timeout occurred?\n"); return retval; + } if (ufshcd_is_auto_hibern8_error(hba, intr_status)) hba->errors |= (UFSHCD_UIC_HIBERN8_MASK & intr_status); @@ -6785,6 +6914,7 @@ again: spin_unlock_irqrestore(hba->host->host_lock, flags); ufshcd_print_host_state(hba); ufshcd_print_pwr_info(hba); + ufshcd_print_tx_eq_params(hba); ufshcd_print_evt_hist(hba); ufshcd_print_tmrs(hba, hba->outstanding_tasks); ufshcd_print_trs_all(hba, pr_prdt); @@ -6843,14 +6973,13 @@ again: * are sent via bsg and/or sysfs. */ down_write(&hba->clk_scaling_lock); - hba->force_pmc = true; - pmc_err = ufshcd_config_pwr_mode(hba, &(hba->pwr_info)); + pmc_err = ufshcd_config_pwr_mode(hba, &hba->pwr_info, + UFSHCD_PMC_POLICY_FORCE); if (pmc_err) { needs_reset = true; dev_err(hba->dev, "%s: Failed to restore power mode, err = %d\n", __func__, pmc_err); } - hba->force_pmc = false; ufshcd_print_pwr_info(hba); up_write(&hba->clk_scaling_lock); spin_lock_irqsave(hba->host->host_lock, flags); @@ -6976,10 +7105,19 @@ static irqreturn_t ufshcd_update_uic_error(struct ufs_hba *hba) } reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DME); - if ((reg & UIC_DME_ERROR) && - (reg & UIC_DME_ERROR_CODE_MASK)) { + if (reg & UIC_DME_ERROR) { ufshcd_update_evt_hist(hba, UFS_EVT_DME_ERR, reg); - hba->uic_error |= UFSHCD_UIC_DME_ERROR; + + if (reg & UIC_DME_ERROR_CODE_MASK) + hba->uic_error |= UFSHCD_UIC_DME_ERROR; + + if (reg & UIC_DME_QOS_MASK) { + atomic_set(&hba->dme_qos_notification, + reg & UIC_DME_QOS_MASK); + if (hba->dme_qos_sysfs_handle) + sysfs_notify_dirent(hba->dme_qos_sysfs_handle); + } + retval |= IRQ_HANDLED; } @@ -7049,6 +7187,7 @@ static irqreturn_t ufshcd_check_errors(struct ufs_hba *hba, u32 intr_status) ufshcd_dump_regs(hba, 0, UFSHCI_REG_SPACE_SIZE, "host_regs: "); ufshcd_print_pwr_info(hba); + ufshcd_print_tx_eq_params(hba); } ufshcd_schedule_eh_work(hba); retval |= IRQ_HANDLED; @@ -7097,16 +7236,17 @@ static irqreturn_t ufshcd_tmc_handler(struct ufs_hba *hba) /** * ufshcd_handle_mcq_cq_events - handle MCQ completion queue events * @hba: per adapter instance + * @reset_iag: true, to reset MCQ IAG counter and timer of the CQ * * Return: IRQ_HANDLED if interrupt is handled. */ -static irqreturn_t ufshcd_handle_mcq_cq_events(struct ufs_hba *hba) +static irqreturn_t ufshcd_handle_mcq_cq_events(struct ufs_hba *hba, bool reset_iag) { struct ufs_hw_queue *hwq; unsigned long outstanding_cqs; unsigned int nr_queues; int i, ret; - u32 events; + u32 events, reg; ret = ufshcd_vops_get_outstanding_cqs(hba, &outstanding_cqs); if (ret) @@ -7121,6 +7261,12 @@ static irqreturn_t ufshcd_handle_mcq_cq_events(struct ufs_hba *hba) if (events) ufshcd_mcq_write_cqis(hba, events, i); + if (reset_iag) { + reg = ufshcd_mcq_read_mcqiacr(hba, i); + reg |= INT_AGGR_COUNTER_AND_TIMER_RESET; + ufshcd_mcq_write_mcqiacr(hba, reg, i); + } + if (events & UFSHCD_MCQ_CQIS_TAIL_ENT_PUSH_STS) ufshcd_mcq_poll_cqe_lock(hba, hwq); } @@ -7154,7 +7300,10 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) retval |= ufshcd_transfer_req_compl(hba); if (intr_status & MCQ_CQ_EVENT_STATUS) - retval |= ufshcd_handle_mcq_cq_events(hba); + retval |= ufshcd_handle_mcq_cq_events(hba, false); + + if (intr_status & MCQ_IAG_EVENT_STATUS) + retval |= ufshcd_handle_mcq_cq_events(hba, true); return retval; } @@ -7222,8 +7371,12 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) struct ufs_hba *hba = __hba; u32 intr_status, enabled_intr_status; - /* Move interrupt handling to thread when MCQ & ESI are not enabled */ - if (!hba->mcq_enabled || !hba->mcq_esi_enabled) + /* + * Handle interrupt in thread if MCQ or ESI is disabled, + * and no active UIC command. + */ + if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) && + !hba->active_uic_cmd) return IRQ_WAKE_THREAD; intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); @@ -7830,6 +7983,7 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) ufshcd_print_evt_hist(hba); ufshcd_print_host_state(hba); ufshcd_print_pwr_info(hba); + ufshcd_print_tx_eq_params(hba); ufshcd_print_tr(hba, cmd, true); } else { ufshcd_print_tr(hba, cmd, false); @@ -8807,6 +8961,8 @@ static void ufshcd_tune_unipro_params(struct ufs_hba *hba) if (hba->dev_quirks & UFS_DEVICE_QUIRK_PA_HIBER8TIME) ufshcd_quirk_override_pa_h8time(hba); + + ufshcd_apply_valid_tx_eq_settings(hba); } static void ufshcd_clear_dbg_ufs_stats(struct ufs_hba *hba) @@ -9103,6 +9259,30 @@ static void ufshcd_config_mcq(struct ufs_hba *hba) hba->nutrs); } +/** + * ufshcd_get_op_mode - get UFS operating mode. + * @hba: per-adapter instance + * + * Use the PA_PWRMODE value to represent the operating mode of UFS. + * + */ +static enum ufs_op_mode ufshcd_get_op_mode(struct ufs_hba *hba) +{ + u32 mode; + u8 rx_mode; + u8 tx_mode; + + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PWRMODE), &mode); + rx_mode = (mode >> PWRMODE_RX_OFFSET) & PWRMODE_MASK; + tx_mode = mode & PWRMODE_MASK; + + if ((rx_mode == SLOW_MODE || rx_mode == SLOWAUTO_MODE) && + (tx_mode == SLOW_MODE || tx_mode == SLOWAUTO_MODE)) + return LS_MODE; + + return HS_MODE; +} + static int ufshcd_post_device_init(struct ufs_hba *hba) { int ret; @@ -9111,6 +9291,12 @@ static int ufshcd_post_device_init(struct ufs_hba *hba) /* UFS device is also active now */ ufshcd_set_ufs_dev_active(hba); + + /* Indicate that DME QoS Monitor has been reset */ + atomic_set(&hba->dme_qos_notification, 0x1); + if (hba->dme_qos_sysfs_handle) + sysfs_notify_dirent(hba->dme_qos_sysfs_handle); + ufshcd_force_reset_auto_bkops(hba); ufshcd_set_timestamp_attr(hba); @@ -9119,13 +9305,16 @@ static int ufshcd_post_device_init(struct ufs_hba *hba) return 0; /* - * Set the right value to bRefClkFreq before attempting to + * Set the right value to bRefClkFreq in LS_MODE before attempting to * switch to HS gears. */ - if (hba->dev_ref_clk_freq != REF_CLK_FREQ_INVAL) + if (ufshcd_get_op_mode(hba) == LS_MODE && + hba->dev_ref_clk_freq != REF_CLK_FREQ_INVAL) ufshcd_set_dev_ref_clk(hba); + /* Gear up to HS gear. */ - ret = ufshcd_config_pwr_mode(hba, &hba->max_pwr_info.info); + ret = ufshcd_config_pwr_mode(hba, &hba->max_pwr_info.info, + UFSHCD_PMC_POLICY_DONT_FORCE); if (ret) { dev_err(hba->dev, "%s: Failed setting power mode, err = %d\n", __func__, ret); @@ -9743,6 +9932,7 @@ static void ufshcd_hba_exit(struct ufs_hba *hba) hba->is_powered = false; ufs_put_device_desc(hba); } + sysfs_put(hba->dme_qos_sysfs_handle); } static int ufshcd_execute_start_stop(struct scsi_device *sdev, @@ -9942,11 +10132,13 @@ static void ufshcd_vreg_set_lpm(struct ufs_hba *hba) #ifdef CONFIG_PM static int ufshcd_vreg_set_hpm(struct ufs_hba *hba) { + bool vcc_on = false; int ret = 0; if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba) && !hba->dev_info.is_lu_power_on_wp) { ret = ufshcd_setup_vreg(hba, true); + vcc_on = true; } else if (!ufshcd_is_ufs_dev_active(hba)) { if (!ufshcd_is_link_active(hba)) { ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq); @@ -9957,6 +10149,7 @@ static int ufshcd_vreg_set_hpm(struct ufs_hba *hba) goto vccq_lpm; } ret = ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true); + vcc_on = true; } goto out; @@ -9965,6 +10158,15 @@ vccq_lpm: vcc_disable: ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false); out: + /* + * On platforms with a slow VCC ramp-up, a delay is needed after + * turning on VCC to ensure the voltage is stable before the + * reference clock is enabled. + */ + if (hba->quirks & UFSHCD_QUIRK_VCC_ON_DELAY && !ret && vcc_on && + hba->vreg_info.vcc && !hba->vreg_info.vcc->always_on) + usleep_range(1000, 1100); + return ret; } #endif /* CONFIG_PM */ @@ -11070,6 +11272,8 @@ initialized: goto out_disable; ufs_sysfs_add_nodes(hba->dev); + hba->dme_qos_sysfs_handle = sysfs_get_dirent(hba->dev->kobj.sd, + "dme_qos_notification"); async_schedule(ufshcd_async_scan, hba); device_enable_async_suspend(dev); diff --git a/drivers/ufs/host/ufs-amd-versal2.c b/drivers/ufs/host/ufs-amd-versal2.c index 6c454ae8a9c8..2154d6286817 100644 --- a/drivers/ufs/host/ufs-amd-versal2.c +++ b/drivers/ufs/host/ufs-amd-versal2.c @@ -443,7 +443,6 @@ static int ufs_versal2_phy_ratesel(struct ufs_hba *hba, u32 activelanes, u32 rx_ } static int ufs_versal2_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_change_status status, - const struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) { struct ufs_versal2_host *host = ufshcd_get_variant(hba); @@ -451,8 +450,6 @@ static int ufs_versal2_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_ch int ret = 0; if (status == PRE_CHANGE) { - memcpy(dev_req_params, dev_max_params, sizeof(struct ufs_pa_layer_attr)); - /* If it is not a calibrated part, switch PWRMODE to SLOW_MODE */ if (!host->attcompval0 && !host->attcompval1 && !host->ctlecompval0 && !host->ctlecompval1) { diff --git a/drivers/ufs/host/ufs-exynos.c b/drivers/ufs/host/ufs-exynos.c index 76fee3a79c77..77a6c8e44485 100644 --- a/drivers/ufs/host/ufs-exynos.c +++ b/drivers/ufs/host/ufs-exynos.c @@ -818,12 +818,10 @@ static u32 exynos_ufs_get_hs_gear(struct ufs_hba *hba) } static int exynos_ufs_pre_pwr_mode(struct ufs_hba *hba, - const struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) { struct exynos_ufs *ufs = ufshcd_get_variant(hba); struct phy *generic_phy = ufs->phy; - struct ufs_host_params host_params; int ret; if (!dev_req_params) { @@ -832,18 +830,6 @@ static int exynos_ufs_pre_pwr_mode(struct ufs_hba *hba, goto out; } - ufshcd_init_host_params(&host_params); - - /* This driver only support symmetric gear setting e.g. hs_tx_gear == hs_rx_gear */ - host_params.hs_tx_gear = exynos_ufs_get_hs_gear(hba); - host_params.hs_rx_gear = exynos_ufs_get_hs_gear(hba); - - ret = ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params); - if (ret) { - pr_err("%s: failed to determine capabilities\n", __func__); - goto out; - } - if (ufs->drv_data->pre_pwr_change) ufs->drv_data->pre_pwr_change(ufs, dev_req_params); @@ -1677,17 +1663,30 @@ static int exynos_ufs_link_startup_notify(struct ufs_hba *hba, return ret; } +static int exynos_ufs_negotiate_pwr_mode(struct ufs_hba *hba, + const struct ufs_pa_layer_attr *dev_max_params, + struct ufs_pa_layer_attr *dev_req_params) +{ + struct ufs_host_params host_params; + + ufshcd_init_host_params(&host_params); + + /* This driver only support symmetric gear setting e.g. hs_tx_gear == hs_rx_gear */ + host_params.hs_tx_gear = exynos_ufs_get_hs_gear(hba); + host_params.hs_rx_gear = exynos_ufs_get_hs_gear(hba); + + return ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params); +} + static int exynos_ufs_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_change_status status, - const struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) { int ret = 0; switch (status) { case PRE_CHANGE: - ret = exynos_ufs_pre_pwr_mode(hba, dev_max_params, - dev_req_params); + ret = exynos_ufs_pre_pwr_mode(hba, dev_req_params); break; case POST_CHANGE: ret = exynos_ufs_post_pwr_mode(hba, dev_req_params); @@ -2015,6 +2014,7 @@ static const struct ufs_hba_variant_ops ufs_hba_exynos_ops = { .exit = exynos_ufs_exit, .hce_enable_notify = exynos_ufs_hce_enable_notify, .link_startup_notify = exynos_ufs_link_startup_notify, + .negotiate_pwr_mode = exynos_ufs_negotiate_pwr_mode, .pwr_change_notify = exynos_ufs_pwr_change_notify, .setup_clocks = exynos_ufs_setup_clocks, .setup_xfer_req = exynos_ufs_specify_nexus_t_xfer_req, diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c index 6f2e6bf31225..993e20ac211d 100644 --- a/drivers/ufs/host/ufs-hisi.c +++ b/drivers/ufs/host/ufs-hisi.c @@ -298,6 +298,17 @@ static void ufs_hisi_set_dev_cap(struct ufs_host_params *host_params) ufshcd_init_host_params(host_params); } +static int ufs_hisi_negotiate_pwr_mode(struct ufs_hba *hba, + const struct ufs_pa_layer_attr *dev_max_params, + struct ufs_pa_layer_attr *dev_req_params) +{ + struct ufs_host_params host_params; + + ufs_hisi_set_dev_cap(&host_params); + + return ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params); +} + static void ufs_hisi_pwr_change_pre_change(struct ufs_hba *hba) { struct ufs_hisi_host *host = ufshcd_get_variant(hba); @@ -362,10 +373,8 @@ static void ufs_hisi_pwr_change_pre_change(struct ufs_hba *hba) static int ufs_hisi_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_change_status status, - const struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) { - struct ufs_host_params host_params; int ret = 0; if (!dev_req_params) { @@ -377,14 +386,6 @@ static int ufs_hisi_pwr_change_notify(struct ufs_hba *hba, switch (status) { case PRE_CHANGE: - ufs_hisi_set_dev_cap(&host_params); - ret = ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params); - if (ret) { - dev_err(hba->dev, - "%s: failed to determine capabilities\n", __func__); - goto out; - } - ufs_hisi_pwr_change_pre_change(hba); break; case POST_CHANGE: @@ -543,6 +544,7 @@ static const struct ufs_hba_variant_ops ufs_hba_hi3660_vops = { .name = "hi3660", .init = ufs_hi3660_init, .link_startup_notify = ufs_hisi_link_startup_notify, + .negotiate_pwr_mode = ufs_hisi_negotiate_pwr_mode, .pwr_change_notify = ufs_hisi_pwr_change_notify, .suspend = ufs_hisi_suspend, .resume = ufs_hisi_resume, @@ -552,6 +554,7 @@ static const struct ufs_hba_variant_ops ufs_hba_hi3670_vops = { .name = "hi3670", .init = ufs_hi3670_init, .link_startup_notify = ufs_hisi_link_startup_notify, + .negotiate_pwr_mode = ufs_hisi_negotiate_pwr_mode, .pwr_change_notify = ufs_hisi_pwr_change_notify, .suspend = ufs_hisi_suspend, .resume = ufs_hisi_resume, diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index b3daaa07e925..3991a51263a6 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -1317,6 +1317,23 @@ out: return err; } +static int ufs_mtk_negotiate_pwr_mode(struct ufs_hba *hba, + const struct ufs_pa_layer_attr *dev_max_params, + struct ufs_pa_layer_attr *dev_req_params) +{ + struct ufs_host_params host_params; + + ufshcd_init_host_params(&host_params); + host_params.hs_rx_gear = UFS_HS_G5; + host_params.hs_tx_gear = UFS_HS_G5; + + if (dev_max_params->pwr_rx == SLOW_MODE || + dev_max_params->pwr_tx == SLOW_MODE) + host_params.desired_working_mode = UFS_PWM_MODE; + + return ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params); +} + static bool ufs_mtk_pmc_via_fastauto(struct ufs_hba *hba, struct ufs_pa_layer_attr *dev_req_params) { @@ -1372,26 +1389,10 @@ static void ufs_mtk_adjust_sync_length(struct ufs_hba *hba) } static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba, - const struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); - struct ufs_host_params host_params; - int ret; - - ufshcd_init_host_params(&host_params); - host_params.hs_rx_gear = UFS_HS_G5; - host_params.hs_tx_gear = UFS_HS_G5; - - if (dev_max_params->pwr_rx == SLOW_MODE || - dev_max_params->pwr_tx == SLOW_MODE) - host_params.desired_working_mode = UFS_PWM_MODE; - - ret = ufshcd_negotiate_pwr_params(&host_params, dev_max_params, dev_req_params); - if (ret) { - pr_info("%s: failed to determine capabilities\n", - __func__); - } + int ret = 0; if (ufs_mtk_pmc_via_fastauto(hba, dev_req_params)) { ufs_mtk_adjust_sync_length(hba); @@ -1503,7 +1504,6 @@ out: static int ufs_mtk_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_change_status stage, - const struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) { int ret = 0; @@ -1515,8 +1515,7 @@ static int ufs_mtk_pwr_change_notify(struct ufs_hba *hba, reg = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER); ufs_mtk_auto_hibern8_disable(hba); } - ret = ufs_mtk_pre_pwr_change(hba, dev_max_params, - dev_req_params); + ret = ufs_mtk_pre_pwr_change(hba, dev_req_params); break; case POST_CHANGE: if (ufshcd_is_auto_hibern8_supported(hba)) @@ -1960,6 +1959,8 @@ static int ufs_mtk_apply_dev_quirks(struct ufs_hba *hba) static void ufs_mtk_fixup_dev_quirks(struct ufs_hba *hba) { + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + ufshcd_fixup_dev_quirks(hba, ufs_mtk_dev_fixups); if (ufs_mtk_is_broken_vcc(hba) && hba->vreg_info.vcc) { @@ -1971,6 +1972,15 @@ static void ufs_mtk_fixup_dev_quirks(struct ufs_hba *hba) hba->dev_quirks &= ~UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM; } + /* + * Add a delay after enabling UFS5 VCC to ensure the voltage + * is stable before the refclk is enabled. + */ + if (hba->dev_info.wspecversion >= 0x0500 && + (host->ip_ver == IP_VER_MT6995_A0 || + host->ip_ver == IP_VER_MT6995_B0)) + hba->quirks |= UFSHCD_QUIRK_VCC_ON_DELAY; + ufs_mtk_vreg_fix_vcc(hba); ufs_mtk_vreg_fix_vccqx(hba); ufs_mtk_fix_ahit(hba); @@ -2318,6 +2328,7 @@ static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = { .setup_clocks = ufs_mtk_setup_clocks, .hce_enable_notify = ufs_mtk_hce_enable_notify, .link_startup_notify = ufs_mtk_link_startup_notify, + .negotiate_pwr_mode = ufs_mtk_negotiate_pwr_mode, .pwr_change_notify = ufs_mtk_pwr_change_notify, .apply_dev_quirks = ufs_mtk_apply_dev_quirks, .fixup_dev_quirks = ufs_mtk_fixup_dev_quirks, diff --git a/drivers/ufs/host/ufs-mediatek.h b/drivers/ufs/host/ufs-mediatek.h index 9747277f11e8..8547a6f04990 100644 --- a/drivers/ufs/host/ufs-mediatek.h +++ b/drivers/ufs/host/ufs-mediatek.h @@ -220,6 +220,10 @@ enum { IP_VER_MT6991_B0 = 0x10470000, IP_VER_MT6993 = 0x10480000, + /* UFSHCI 5.0 */ + IP_VER_MT6995_A0 = 0x10490000, + IP_VER_MT6995_B0 = 0x10500000, + IP_VER_NONE = 0xFFFFFFFF }; diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index 375fd24ba458..bc037db46624 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -966,13 +966,21 @@ static void ufs_qcom_set_tx_hs_equalizer(struct ufs_hba *hba, u32 gear, u32 tx_l } } -static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, - enum ufs_notify_change_status status, - const struct ufs_pa_layer_attr *dev_max_params, - struct ufs_pa_layer_attr *dev_req_params) +static int ufs_qcom_negotiate_pwr_mode(struct ufs_hba *hba, + const struct ufs_pa_layer_attr *dev_max_params, + struct ufs_pa_layer_attr *dev_req_params) { struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct ufs_host_params *host_params = &host->host_params; + + return ufshcd_negotiate_pwr_params(host_params, dev_max_params, dev_req_params); +} + +static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status, + struct ufs_pa_layer_attr *dev_req_params) +{ + struct ufs_qcom_host *host = ufshcd_get_variant(hba); int ret = 0; if (!dev_req_params) { @@ -982,13 +990,6 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, switch (status) { case PRE_CHANGE: - ret = ufshcd_negotiate_pwr_params(host_params, dev_max_params, dev_req_params); - if (ret) { - dev_err(hba->dev, "%s: failed to determine capabilities\n", - __func__); - return ret; - } - /* * During UFS driver probe, always update the PHY gear to match the negotiated * gear, so that, if quirk UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH is enabled, @@ -1068,10 +1069,188 @@ static void ufs_qcom_override_pa_tx_hsg1_sync_len(struct ufs_hba *hba) dev_err(hba->dev, "Failed (%d) set PA_TX_HSG1_SYNC_LENGTH\n", err); } +/** + * ufs_qcom_double_t_adapt_l0l1l2l3 - Create a new adapt that doubles the + * adaptation duration TADAPT_L0_L1_L2_L3 derived from the old adapt. + * + * @old_adapt: Original ADAPT_L0_L1_L2_L3 capability + * + * ADAPT_length_L0_L1_L2_L3 formula from M-PHY spec: + * if (ADAPT_range_L0_L1_L2_L3 == COARSE) { + * ADAPT_length_L0_L1_L2_L3 = [0, 12] + * ADAPT_L0_L1_L2_L3 = 215 x 2^ADAPT_length_L0_L1_L2_L3 + * } else if (ADAPT_range_L0_L1_L2_L3 == FINE) { + * ADAPT_length_L0_L1_L2_L3 = [0, 127] + * TADAPT_L0_L1_L2_L3 = 215 x (ADAPT_length_L0_L1_L2_L3 + 1) + * } + * + * To double the adaptation duration TADAPT_L0_L1_L2_L3: + * 1. If adapt range is COARSE (1'b1), new adapt = old adapt + 1. + * 2. If adapt range is FINE (1'b0): + * a) If old adapt length is < 64, (new adapt + 1) = 2 * (old adapt + 1). + * b) If old adapt length is >= 64, set new adapt to 0x88 using COARSE + * range, because new adapt get from equation in a) shall exceed 127. + * + * Examples: + * ADAPT_range_L0_L1_L2_L3 | ADAPT_length_L0_L1_L2_L3 | TADAPT_L0_L1_L2_L3 (PAM-4 UI) + * 0 3 131072 + * 0 7 262144 + * 0 63 2097152 + * 0 64 2129920 + * 0 127 4194304 + * 1 8 8388608 + * 1 9 16777216 + * 1 10 33554432 + * 1 11 67108864 + * 1 12 134217728 + * + * Return: new adapt. + */ +static u32 ufs_qcom_double_t_adapt_l0l1l2l3(u32 old_adapt) +{ + u32 adapt_length = old_adapt & ADAPT_LENGTH_MASK; + u32 new_adapt; + + if (IS_ADAPT_RANGE_COARSE(old_adapt)) { + new_adapt = (adapt_length + 1) | ADAPT_RANGE_BIT; + } else { + if (adapt_length < 64) + new_adapt = (adapt_length << 1) + 1; + else + /* + * 0x88 is the very coarse Adapt value which is two + * times of the largest fine Adapt value (0x7F) + */ + new_adapt = 0x88; + } + + return new_adapt; +} + +static void ufs_qcom_limit_max_gear(struct ufs_hba *hba, + enum ufs_hs_gear_tag gear) +{ + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + struct ufs_pa_layer_attr *pwr_info = &hba->max_pwr_info.info; + struct ufs_host_params *host_params = &host->host_params; + + host_params->hs_tx_gear = gear; + host_params->hs_rx_gear = gear; + pwr_info->gear_tx = gear; + pwr_info->gear_rx = gear; + + dev_warn(hba->dev, "Limited max gear of host and device to HS-G%d\n", gear); +} + +static void ufs_qcom_fixup_tx_adapt_l0l1l2l3(struct ufs_hba *hba) +{ + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + struct ufs_pa_layer_attr *pwr_info = &hba->max_pwr_info.info; + struct ufs_host_params *host_params = &host->host_params; + u32 old_adapt, new_adapt, actual_adapt; + bool limit_speed = false; + int err; + + if (host->hw_ver.major != 0x7 || host->hw_ver.minor > 0x1 || + host_params->hs_tx_gear <= UFS_HS_G5 || + pwr_info->gear_tx <= UFS_HS_G5) + return; + + err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PEERRXHSG6ADAPTINITIALL0L1L2L3), &old_adapt); + if (err) + goto out; + + if (old_adapt > ADAPT_L0L1L2L3_LENGTH_MAX) { + dev_err(hba->dev, "PA_PeerRxHsG6AdaptInitialL0L1L2L3 value (0x%x) exceeds MAX\n", + old_adapt); + err = -ERANGE; + goto out; + } + + new_adapt = ufs_qcom_double_t_adapt_l0l1l2l3(old_adapt); + dev_dbg(hba->dev, "Original PA_PeerRxHsG6AdaptInitialL0L1L2L3 = 0x%x, new value = 0x%x\n", + old_adapt, new_adapt); + + /* + * 0x8C is the max possible value allowed by UniPro v3.0 spec, some HWs + * can accept 0x8D but some cannot. + */ + if (new_adapt <= ADAPT_L0L1L2L3_LENGTH_MAX || + (new_adapt == ADAPT_L0L1L2L3_LENGTH_MAX + 1 && host->hw_ver.minor == 0x1)) { + err = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PEERRXHSG6ADAPTINITIALL0L1L2L3), + new_adapt); + if (err) + goto out; + + err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PEERRXHSG6ADAPTINITIALL0L1L2L3), + &actual_adapt); + if (err) + goto out; + + if (actual_adapt != new_adapt) { + limit_speed = true; + dev_warn(hba->dev, "PA_PeerRxHsG6AdaptInitialL0L1L2L3 0x%x, expect 0x%x\n", + actual_adapt, new_adapt); + } + } else { + limit_speed = true; + dev_warn(hba->dev, "New PA_PeerRxHsG6AdaptInitialL0L1L2L3 (0x%x) is too large!\n", + new_adapt); + } + + err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PEERRXHSG6ADAPTREFRESHL0L1L2L3), &old_adapt); + if (err) + goto out; + + if (old_adapt > ADAPT_L0L1L2L3_LENGTH_MAX) { + dev_err(hba->dev, "PA_PeerRxHsG6AdaptRefreshL0L1L2L3 value (0x%x) exceeds MAX\n", + old_adapt); + err = -ERANGE; + goto out; + } + + new_adapt = ufs_qcom_double_t_adapt_l0l1l2l3(old_adapt); + dev_dbg(hba->dev, "Original PA_PeerRxHsG6AdaptRefreshL0L1L2L3 = 0x%x, new value = 0x%x\n", + old_adapt, new_adapt); + + /* + * 0x8C is the max possible value allowed by UniPro v3.0 spec, some HWs + * can accept 0x8D but some cannot. + */ + if (new_adapt <= ADAPT_L0L1L2L3_LENGTH_MAX || + (new_adapt == ADAPT_L0L1L2L3_LENGTH_MAX + 1 && host->hw_ver.minor == 0x1)) { + err = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PEERRXHSG6ADAPTREFRESHL0L1L2L3), + new_adapt); + if (err) + goto out; + + err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PEERRXHSG6ADAPTREFRESHL0L1L2L3), + &actual_adapt); + if (err) + goto out; + + if (actual_adapt != new_adapt) { + limit_speed = true; + dev_warn(hba->dev, "PA_PeerRxHsG6AdaptRefreshL0L1L2L3 0x%x, expect 0x%x\n", + new_adapt, actual_adapt); + } + } else { + limit_speed = true; + dev_warn(hba->dev, "New PA_PeerRxHsG6AdaptRefreshL0L1L2L3 (0x%x) is too large!\n", + new_adapt); + } + +out: + if (limit_speed || err) + ufs_qcom_limit_max_gear(hba, UFS_HS_G5); +} + static int ufs_qcom_apply_dev_quirks(struct ufs_hba *hba) { int err = 0; + ufs_qcom_fixup_tx_adapt_l0l1l2l3(hba); + if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME) err = ufs_qcom_quirk_host_pa_saveconfigtime(hba); @@ -1205,6 +1384,8 @@ static void ufs_qcom_set_host_caps(struct ufs_hba *hba) static void ufs_qcom_set_caps(struct ufs_hba *hba) { + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; hba->caps |= UFSHCD_CAP_CLK_SCALING | UFSHCD_CAP_WB_WITH_CLK_SCALING; hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND; @@ -1212,6 +1393,9 @@ static void ufs_qcom_set_caps(struct ufs_hba *hba) hba->caps |= UFSHCD_CAP_AGGR_POWER_COLLAPSE; hba->caps |= UFSHCD_CAP_RPM_AUTOSUSPEND; + if (host->hw_ver.major >= 0x7) + hba->caps |= UFSHCD_CAP_TX_EQUALIZATION; + ufs_qcom_set_host_caps(hba); } @@ -2326,6 +2510,387 @@ static u32 ufs_qcom_freq_to_gear_speed(struct ufs_hba *hba, unsigned long freq) return min_t(u32, gear, hba->max_pwr_info.info.gear_rx); } +static int ufs_qcom_host_eom_config(struct ufs_hba *hba, int lane, + const struct ufs_eom_coord *eom_coord, + u32 target_test_count) +{ + enum ufs_eom_eye_mask eye_mask = eom_coord->eye_mask; + int v_step = eom_coord->v_step; + int t_step = eom_coord->t_step; + u32 volt_step, timing_step; + int ret; + + if (abs(v_step) > UFS_QCOM_EOM_VOLTAGE_STEPS_MAX) { + dev_err(hba->dev, "Invalid EOM Voltage Step: %d\n", v_step); + return -ERANGE; + } + + if (abs(t_step) > UFS_QCOM_EOM_TIMING_STEPS_MAX) { + dev_err(hba->dev, "Invalid EOM Timing Step: %d\n", t_step); + return -ERANGE; + } + + if (v_step < 0) + volt_step = RX_EYEMON_NEGATIVE_STEP_BIT | (u32)(-v_step); + else + volt_step = (u32)v_step; + + if (t_step < 0) + timing_step = RX_EYEMON_NEGATIVE_STEP_BIT | (u32)(-t_step); + else + timing_step = (u32)t_step; + + ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_EYEMON_ENABLE, + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), + BIT(eye_mask) | RX_EYEMON_EXTENDED_VRANGE_BIT); + if (ret) { + dev_err(hba->dev, "Failed to enable Host EOM on Lane %d: %d\n", + lane, ret); + return ret; + } + + ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_EYEMON_TIMING_STEPS, + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), + timing_step); + if (ret) { + dev_err(hba->dev, "Failed to set Host EOM timing step on Lane %d: %d\n", + lane, ret); + return ret; + } + + ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_EYEMON_VOLTAGE_STEPS, + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), + volt_step); + if (ret) { + dev_err(hba->dev, "Failed to set Host EOM voltage step on Lane %d: %d\n", + lane, ret); + return ret; + } + + ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_EYEMON_TARGET_TEST_COUNT, + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), + target_test_count); + if (ret) + dev_err(hba->dev, "Failed to set Host EOM target test count on Lane %d: %d\n", + lane, ret); + + return ret; +} + +static int ufs_qcom_host_eom_may_stop(struct ufs_hba *hba, int lane, + u32 target_test_count, u32 *err_count) +{ + u32 start, tested_count, error_count; + int ret; + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(RX_EYEMON_START, + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), + &start); + if (ret) { + dev_err(hba->dev, "Failed to get Host EOM start status on Lane %d: %d\n", + lane, ret); + return ret; + } + + if (start & 0x1) + return -EAGAIN; + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(RX_EYEMON_TESTED_COUNT, + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), + &tested_count); + if (ret) { + dev_err(hba->dev, "Failed to get Host EOM tested count on Lane %d: %d\n", + lane, ret); + return ret; + } + + ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(RX_EYEMON_ERROR_COUNT, + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), + &error_count); + if (ret) { + dev_err(hba->dev, "Failed to get Host EOM error count on Lane %d: %d\n", + lane, ret); + return ret; + } + + /* EOM can stop */ + if ((tested_count >= target_test_count - 3) || error_count > 0) { + *err_count = error_count; + + /* Disable EOM */ + ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_EYEMON_ENABLE, + UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), + 0x0); + if (ret) { + dev_err(hba->dev, "Failed to disable Host EOM on Lane %d: %d\n", + lane, ret); + return ret; + } + } else { + return -EAGAIN; + } + + return 0; +} + +static int ufs_qcom_host_eom_scan(struct ufs_hba *hba, int num_lanes, + const struct ufs_eom_coord *eom_coord, + u32 target_test_count, u32 *err_count) +{ + bool eom_stopped[PA_MAXDATALANES] = { 0 }; + int lane, ret; + u32 setting; + + if (!err_count || !eom_coord) + return -EINVAL; + + if (target_test_count < UFS_QCOM_EOM_TARGET_TEST_COUNT_MIN) { + dev_err(hba->dev, "Target test count (%u) too small for Host EOM\n", + target_test_count); + return -ERANGE; + } + + for (lane = 0; lane < num_lanes; lane++) { + ret = ufs_qcom_host_eom_config(hba, lane, eom_coord, + target_test_count); + if (ret) { + dev_err(hba->dev, "Failed to config Host RX EOM: %d\n", ret); + return ret; + } + } + + /* + * Trigger a PACP_PWR_req to kick start EOM, but not to really change + * the Power Mode. + */ + ret = ufshcd_uic_change_pwr_mode(hba, FAST_MODE << 4 | FAST_MODE); + if (ret) { + dev_err(hba->dev, "Failed to change power mode to kick start Host EOM: %d\n", + ret); + return ret; + } + +more_burst: + /* Create burst on Host RX Lane. */ + ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_LOCALVERINFO), &setting); + + for (lane = 0; lane < num_lanes; lane++) { + if (eom_stopped[lane]) + continue; + + ret = ufs_qcom_host_eom_may_stop(hba, lane, target_test_count, + &err_count[lane]); + if (!ret) { + eom_stopped[lane] = true; + } else if (ret == -EAGAIN) { + /* Need more burst to excercise EOM */ + goto more_burst; + } else { + dev_err(hba->dev, "Failed to stop Host EOM: %d\n", ret); + return ret; + } + + dev_dbg(hba->dev, "Host RX Lane %d EOM, v_step %d, t_step %d, error count %u\n", + lane, eom_coord->v_step, eom_coord->t_step, + err_count[lane]); + } + + return 0; +} + +static int ufs_qcom_host_sw_rx_fom(struct ufs_hba *hba, int num_lanes, u32 *fom) +{ + const struct ufs_eom_coord *eom_coord = sw_rx_fom_eom_coords_g6; + u32 eom_err_count[PA_MAXDATALANES] = { 0 }; + u32 curr_ahit; + int lane, i, ret; + + if (!fom) + return -EINVAL; + + /* Stop the auto hibernate idle timer */ + curr_ahit = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER); + if (curr_ahit) + ufshcd_writel(hba, 0, REG_AUTO_HIBERNATE_IDLE_TIMER); + + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXHSADAPTTYPE), PA_NO_ADAPT); + if (ret) { + dev_err(hba->dev, "Failed to select NO_ADAPT before starting Host EOM: %d\n", ret); + goto out; + } + + for (i = 0; i < SW_RX_FOM_EOM_COORDS; i++, eom_coord++) { + ret = ufs_qcom_host_eom_scan(hba, num_lanes, eom_coord, + UFS_QCOM_EOM_TARGET_TEST_COUNT_G6, + eom_err_count); + if (ret) { + dev_err(hba->dev, "Failed to run Host EOM scan: %d\n", ret); + break; + } + + for (lane = 0; lane < num_lanes; lane++) { + /* Bad coordinates have no weights */ + if (eom_err_count[lane]) + continue; + fom[lane] += SW_RX_FOM_EOM_COORDS_WEIGHT; + } + } + +out: + /* Restore the auto hibernate idle timer */ + if (curr_ahit) + ufshcd_writel(hba, curr_ahit, REG_AUTO_HIBERNATE_IDLE_TIMER); + + return ret; +} + +static int ufs_qcom_get_rx_fom(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + struct tx_eqtr_iter *h_iter, + struct tx_eqtr_iter *d_iter) +{ + struct ufshcd_tx_eq_params *params __free(kfree) = + kzalloc(sizeof(*params), GFP_KERNEL); + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + struct ufs_pa_layer_attr old_pwr_info; + u32 fom[PA_MAXDATALANES] = { 0 }; + u32 gear = pwr_mode->gear_tx; + u32 rate = pwr_mode->hs_rate; + int lane, ret; + + if (host->hw_ver.major != 0x7 || host->hw_ver.minor > 0x1 || + gear <= UFS_HS_G5 || !d_iter || !d_iter->is_updated) + return 0; + + if (gear < UFS_HS_G1 || gear > UFS_HS_GEAR_MAX) + return -ERANGE; + + if (!params) + return -ENOMEM; + + memcpy(&old_pwr_info, &hba->pwr_info, sizeof(struct ufs_pa_layer_attr)); + + memcpy(params, &hba->tx_eq_params[gear - 1], sizeof(struct ufshcd_tx_eq_params)); + for (lane = 0; lane < pwr_mode->lane_rx; lane++) { + params->device[lane].preshoot = d_iter->preshoot; + params->device[lane].deemphasis = d_iter->deemphasis; + } + + /* Use TX EQTR settings as Device's TX Equalization settings. */ + ret = ufshcd_apply_tx_eq_settings(hba, params, gear); + if (ret) { + dev_err(hba->dev, "%s: Failed to apply TX EQ settings for HS-G%u: %d\n", + __func__, gear, ret); + return ret; + } + + /* Force PMC to target HS Gear to use new TX Equalization settings. */ + ret = ufshcd_change_power_mode(hba, pwr_mode, UFSHCD_PMC_POLICY_FORCE); + if (ret) { + dev_err(hba->dev, "%s: Failed to change power mode to HS-G%u, Rate-%s: %d\n", + __func__, gear, ufs_hs_rate_to_str(rate), ret); + return ret; + } + + ret = ufs_qcom_host_sw_rx_fom(hba, pwr_mode->lane_rx, fom); + if (ret) { + dev_err(hba->dev, "Failed to get SW FOM of TX (PreShoot: %u, DeEmphasis: %u): %d\n", + d_iter->preshoot, d_iter->deemphasis, ret); + return ret; + } + + /* Restore Device's TX Equalization settings. */ + ret = ufshcd_apply_tx_eq_settings(hba, &hba->tx_eq_params[gear - 1], gear); + if (ret) { + dev_err(hba->dev, "%s: Failed to apply TX EQ settings for HS-G%u: %d\n", + __func__, gear, ret); + return ret; + } + + /* Restore Power Mode. */ + ret = ufshcd_change_power_mode(hba, &old_pwr_info, UFSHCD_PMC_POLICY_FORCE); + if (ret) { + dev_err(hba->dev, "%s: Failed to restore power mode to HS-G%u: %d\n", + __func__, old_pwr_info.gear_tx, ret); + return ret; + } + + for (lane = 0; lane < pwr_mode->lane_rx; lane++) + d_iter->fom[lane] = fom[lane]; + + return 0; +} + +static int ufs_qcom_apply_tx_eqtr_settings(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + struct tx_eqtr_iter *h_iter, + struct tx_eqtr_iter *d_iter) +{ + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + u32 setting = 0; + int lane; + + if (host->hw_ver.major != 0x7 || host->hw_ver.minor > 0x1) + return 0; + + for (lane = 0; lane < pwr_mode->lane_tx; lane++) { + setting |= TX_HS_PRESHOOT_BITS(lane, h_iter->preshoot); + setting |= TX_HS_DEEMPHASIS_BITS(lane, h_iter->deemphasis); + } + + return ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXEQG1SETTING), setting); +} + +static int ufs_qcom_tx_eqtr_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status, + struct ufs_pa_layer_attr *pwr_mode) +{ + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + struct ufs_pa_layer_attr pwr_mode_hs_g1 = { + .gear_rx = UFS_HS_G1, + .gear_tx = UFS_HS_G1, + .lane_rx = pwr_mode->lane_rx, + .lane_tx = pwr_mode->lane_tx, + .pwr_rx = FAST_MODE, + .pwr_tx = FAST_MODE, + .hs_rate = pwr_mode->hs_rate, + }; + u32 gear = pwr_mode->gear_tx; + u32 rate = pwr_mode->hs_rate; + int ret; + + if (host->hw_ver.major != 0x7 || host->hw_ver.minor > 0x1) + return 0; + + if (status == PRE_CHANGE) { + ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_TXEQG1SETTING), + &host->saved_tx_eq_g1_setting); + if (ret) + return ret; + + /* PMC to target HS Gear. */ + ret = ufshcd_change_power_mode(hba, pwr_mode, + UFSHCD_PMC_POLICY_DONT_FORCE); + if (ret) + dev_err(hba->dev, "%s: Failed to PMC to target HS-G%u, Rate-%s: %d\n", + __func__, gear, ufs_hs_rate_to_str(rate), ret); + } else { + ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXEQG1SETTING), + host->saved_tx_eq_g1_setting); + if (ret) + return ret; + + /* PMC back to HS-G1. */ + ret = ufshcd_change_power_mode(hba, &pwr_mode_hs_g1, + UFSHCD_PMC_POLICY_DONT_FORCE); + if (ret) + dev_err(hba->dev, "%s: Failed to PMC to HS-G1, Rate-%s: %d\n", + __func__, ufs_hs_rate_to_str(rate), ret); + } + + return ret; +} + /* * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations * @@ -2341,6 +2906,7 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = { .setup_clocks = ufs_qcom_setup_clocks, .hce_enable_notify = ufs_qcom_hce_enable_notify, .link_startup_notify = ufs_qcom_link_startup_notify, + .negotiate_pwr_mode = ufs_qcom_negotiate_pwr_mode, .pwr_change_notify = ufs_qcom_pwr_change_notify, .apply_dev_quirks = ufs_qcom_apply_dev_quirks, .fixup_dev_quirks = ufs_qcom_fixup_dev_quirks, @@ -2355,6 +2921,9 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = { .get_outstanding_cqs = ufs_qcom_get_outstanding_cqs, .config_esi = ufs_qcom_config_esi, .freq_to_gear_speed = ufs_qcom_freq_to_gear_speed, + .get_rx_fom = ufs_qcom_get_rx_fom, + .apply_tx_eqtr_settings = ufs_qcom_apply_tx_eqtr_settings, + .tx_eqtr_notify = ufs_qcom_tx_eqtr_notify, }; static const struct ufs_hba_variant_ops ufs_hba_qcom_sa8255p_vops = { diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h index 1111ab34da01..5d083331a7f4 100644 --- a/drivers/ufs/host/ufs-qcom.h +++ b/drivers/ufs/host/ufs-qcom.h @@ -33,6 +33,46 @@ #define DL_VS_CLK_CFG_MASK GENMASK(9, 0) #define DME_VS_CORE_CLK_CTRL_DME_HW_CGC_EN BIT(9) +#define UFS_QCOM_EOM_VOLTAGE_STEPS_MAX 127 +#define UFS_QCOM_EOM_TIMING_STEPS_MAX 63 +#define UFS_QCOM_EOM_TARGET_TEST_COUNT_MIN 8 +#define UFS_QCOM_EOM_TARGET_TEST_COUNT_G6 0x3F + +#define SW_RX_FOM_EOM_COORDS 23 +#define SW_RX_FOM_EOM_COORDS_WEIGHT (127 / SW_RX_FOM_EOM_COORDS) + +struct ufs_eom_coord { + int t_step; + int v_step; + u8 eye_mask; +}; + +static const struct ufs_eom_coord sw_rx_fom_eom_coords_g6[SW_RX_FOM_EOM_COORDS] = { + [0] = { -2, -15, UFS_EOM_EYE_MASK_M }, + [1] = { 0, -15, UFS_EOM_EYE_MASK_M }, + [2] = { 2, -15, UFS_EOM_EYE_MASK_M }, + [3] = { -4, -10, UFS_EOM_EYE_MASK_M }, + [4] = { -2, -10, UFS_EOM_EYE_MASK_M }, + [5] = { 0, -10, UFS_EOM_EYE_MASK_M }, + [6] = { 2, -10, UFS_EOM_EYE_MASK_M }, + [7] = { 4, -10, UFS_EOM_EYE_MASK_M }, + [8] = { -6, 0, UFS_EOM_EYE_MASK_M }, + [9] = { -4, 0, UFS_EOM_EYE_MASK_M }, + [10] = { -2, 0, UFS_EOM_EYE_MASK_M }, + [11] = { 0, 0, UFS_EOM_EYE_MASK_M }, + [12] = { 2, 0, UFS_EOM_EYE_MASK_M }, + [13] = { 4, 0, UFS_EOM_EYE_MASK_M }, + [14] = { 6, 0, UFS_EOM_EYE_MASK_M }, + [15] = { -4, 10, UFS_EOM_EYE_MASK_M }, + [16] = { -2, 10, UFS_EOM_EYE_MASK_M }, + [17] = { 0, 10, UFS_EOM_EYE_MASK_M }, + [18] = { 2, 10, UFS_EOM_EYE_MASK_M }, + [19] = { 4, 10, UFS_EOM_EYE_MASK_M }, + [20] = { -2, 15, UFS_EOM_EYE_MASK_M }, + [21] = { 0, 15, UFS_EOM_EYE_MASK_M }, + [22] = { 2, 15, UFS_EOM_EYE_MASK_M }, +}; + /* Qualcomm MCQ Configuration */ #define UFS_QCOM_MCQCAP_QCFGPTR 224 /* 0xE0 in hex */ #define UFS_QCOM_MCQ_CONFIG_OFFSET (UFS_QCOM_MCQCAP_QCFGPTR * 0x200) /* 0x1C000 */ @@ -308,6 +348,8 @@ struct ufs_qcom_host { u32 phy_gear; bool esi_enabled; + + u32 saved_tx_eq_g1_setting; }; struct ufs_qcom_drvdata { diff --git a/drivers/ufs/host/ufs-rockchip.c b/drivers/ufs/host/ufs-rockchip.c index 7fff34513a60..bac68f238e1c 100644 --- a/drivers/ufs/host/ufs-rockchip.c +++ b/drivers/ufs/host/ufs-rockchip.c @@ -6,7 +6,6 @@ */ #include -#include #include #include #include diff --git a/drivers/ufs/host/ufs-sprd.c b/drivers/ufs/host/ufs-sprd.c index 65bd8fb96b99..a5e8c591bead 100644 --- a/drivers/ufs/host/ufs-sprd.c +++ b/drivers/ufs/host/ufs-sprd.c @@ -161,14 +161,11 @@ static int ufs_sprd_common_init(struct ufs_hba *hba) static int sprd_ufs_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_change_status status, - const struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) { struct ufs_sprd_host *host = ufshcd_get_variant(hba); if (status == PRE_CHANGE) { - memcpy(dev_req_params, dev_max_params, - sizeof(struct ufs_pa_layer_attr)); if (host->unipro_ver >= UFS_UNIPRO_VER_1_8) ufshcd_dme_configure_adapt(hba, dev_req_params->gear_tx, PA_INITIAL_ADAPT); diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c index 5f65dfad1a71..effa3c7a01c5 100644 --- a/drivers/ufs/host/ufshcd-pci.c +++ b/drivers/ufs/host/ufshcd-pci.c @@ -145,7 +145,8 @@ static int ufs_intel_set_lanes(struct ufs_hba *hba, u32 lanes) pwr_info.lane_rx = lanes; pwr_info.lane_tx = lanes; - ret = ufshcd_config_pwr_mode(hba, &pwr_info); + ret = ufshcd_change_power_mode(hba, &pwr_info, + UFSHCD_PMC_POLICY_DONT_FORCE); if (ret) dev_err(hba->dev, "%s: Setting %u lanes, err = %d\n", __func__, lanes, ret); @@ -154,17 +155,15 @@ static int ufs_intel_set_lanes(struct ufs_hba *hba, u32 lanes) static int ufs_intel_lkf_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_change_status status, - const struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) { int err = 0; switch (status) { case PRE_CHANGE: - if (ufshcd_is_hs_mode(dev_max_params) && + if (ufshcd_is_hs_mode(dev_req_params) && (hba->pwr_info.lane_rx != 2 || hba->pwr_info.lane_tx != 2)) ufs_intel_set_lanes(hba, 2); - memcpy(dev_req_params, dev_max_params, sizeof(*dev_req_params)); break; case POST_CHANGE: if (ufshcd_is_hs_mode(dev_req_params)) { @@ -695,6 +694,7 @@ static const struct pci_device_id ufshcd_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x7747), (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, { PCI_VDEVICE(INTEL, 0xE447), (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, { PCI_VDEVICE(INTEL, 0x4D47), (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, + { PCI_VDEVICE(INTEL, 0xD335), (kernel_ulong_t)&ufs_intel_mtl_hba_vops }, { } /* terminate list */ }; diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index fac11f20cf0a..7cfabb04a4fb 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -544,30 +544,31 @@ static irqreturn_t ci_irq_handler(int irq, void *data) if (ret == IRQ_HANDLED) return ret; } - } - /* - * Handle id change interrupt, it indicates device/host function - * switch. - */ - if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { - ci->id_event = true; - /* Clear ID change irq status */ - hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); - ci_otg_queue_work(ci); - return IRQ_HANDLED; - } + /* + * Handle id change interrupt, it indicates device/host function + * switch. + */ + if ((otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { + ci->id_event = true; + /* Clear ID change irq status */ + hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); + } - /* - * Handle vbus change interrupt, it indicates device connection - * and disconnection events. - */ - if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { - ci->b_sess_valid_event = true; - /* Clear BSV irq */ - hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); - ci_otg_queue_work(ci); - return IRQ_HANDLED; + /* + * Handle vbus change interrupt, it indicates device connection + * and disconnection events. + */ + if ((otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { + ci->b_sess_valid_event = true; + /* Clear BSV irq */ + hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); + } + + if (ci->id_event || ci->b_sess_valid_event) { + ci_otg_queue_work(ci); + return IRQ_HANDLED; + } } /* Handle device/host interrupt */ @@ -618,28 +619,13 @@ static int ci_usb_role_switch_set(struct usb_role_switch *sw, struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw); struct ci_hdrc_cable *cable; - if (role == USB_ROLE_HOST) { - cable = &ci->platdata->id_extcon; - cable->changed = true; - cable->connected = true; - cable = &ci->platdata->vbus_extcon; - cable->changed = true; - cable->connected = false; - } else if (role == USB_ROLE_DEVICE) { - cable = &ci->platdata->id_extcon; - cable->changed = true; - cable->connected = false; - cable = &ci->platdata->vbus_extcon; - cable->changed = true; - cable->connected = true; - } else { - cable = &ci->platdata->id_extcon; - cable->changed = true; - cable->connected = false; - cable = &ci->platdata->vbus_extcon; - cable->changed = true; - cable->connected = false; - } + cable = &ci->platdata->id_extcon; + cable->changed = true; + cable->connected = (role == USB_ROLE_HOST); + + cable = &ci->platdata->vbus_extcon; + cable->changed = true; + cable->connected = (role == USB_ROLE_DEVICE); ci_irq(ci); return 0; diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 647e98f4e351..fecc7d7e2f0d 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -130,6 +130,9 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci) void ci_handle_vbus_change(struct ci_hdrc *ci) { + if (ci->role != CI_ROLE_GADGET) + return; + if (!ci->is_otg) { if (ci->platdata->flags & CI_HDRC_FORCE_VBUS_ACTIVE_ALWAYS) usb_gadget_vbus_connect(&ci->gadget); @@ -187,8 +190,8 @@ void ci_handle_id_switch(struct ci_hdrc *ci) ci_role_stop(ci); - if (role == CI_ROLE_GADGET && - IS_ERR(ci->platdata->vbus_extcon.edev)) + if (role == CI_ROLE_GADGET && !ci->role_switch && + IS_ERR(ci->platdata->vbus_extcon.edev)) /* * Wait vbus lower than OTGSC_BSV before connecting * to host. If connecting status is from an external diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index cf3c3eede1a5..54059e4fc6ed 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -114,6 +114,8 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, int retval; retval = usb_autopm_get_interface(acm->control); +#define VENDOR_CLASS_DATA_IFACE BIT(9) /* data interface uses vendor-specific class */ +#define ALWAYS_POLL_CTRL BIT(10) /* keep ctrl URB active even without an open TTY */ if (retval) return retval; @@ -710,12 +712,14 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty) set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); acm->control->needs_remote_wakeup = 1; - acm->ctrlurb->dev = acm->dev; - retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL); - if (retval) { - dev_err(&acm->control->dev, - "%s - usb_submit_urb(ctrl irq) failed\n", __func__); - goto error_submit_urb; + if (!(acm->quirks & ALWAYS_POLL_CTRL)) { + acm->ctrlurb->dev = acm->dev; + retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL); + if (retval) { + dev_err(&acm->control->dev, + "%s - usb_submit_urb(ctrl irq) failed\n", __func__); + goto error_submit_urb; + } } acm_tty_set_termios(tty, NULL); @@ -788,6 +792,14 @@ static void acm_port_shutdown(struct tty_port *port) acm_unpoison_urbs(acm); + if (acm->quirks & ALWAYS_POLL_CTRL) { + acm->ctrlurb->dev = acm->dev; + if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) + dev_dbg(&acm->control->dev, + "ctrl polling restart failed after port close\n"); + /* port_shutdown() cleared DTR/RTS; restore them */ + acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS); + } } static void acm_tty_cleanup(struct tty_struct *tty) @@ -1328,6 +1340,9 @@ skip_normal_probe: dev_dbg(&intf->dev, "Your device has switched interfaces.\n"); swap(control_interface, data_interface); + } else if (quirks & VENDOR_CLASS_DATA_IFACE) { + dev_dbg(&intf->dev, + "Vendor-specific data interface class, continuing.\n"); } else { return -EINVAL; } @@ -1522,6 +1537,9 @@ skip_countries: acm->line.bDataBits = 8; acm_set_line(acm, &acm->line); + if (quirks & ALWAYS_POLL_CTRL) + acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS); + if (!acm->combined_interfaces) { rv = usb_driver_claim_interface(&acm_driver, data_interface, acm); if (rv) @@ -1543,6 +1561,13 @@ skip_countries: dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); + if (acm->quirks & ALWAYS_POLL_CTRL) { + acm->ctrlurb->dev = acm->dev; + if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) + dev_warn(&intf->dev, + "failed to start persistent ctrl polling\n"); + } + return 0; err_release_data_interface: @@ -1669,7 +1694,7 @@ static int acm_resume(struct usb_interface *intf) acm_unpoison_urbs(acm); - if (tty_port_initialized(&acm->port)) { + if (tty_port_initialized(&acm->port) || (acm->quirks & ALWAYS_POLL_CTRL)) { rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC); for (;;) { @@ -2016,6 +2041,20 @@ static const struct usb_device_id acm_ids[] = { /* CH343 supports CAP_BRK, but doesn't advertise it */ { USB_DEVICE(0x1a86, 0x55d3), .driver_info = MISSING_CAP_BRK, }, + /* + * Lenovo Yoga Book 9 14IAH10 (83KJ) — INGENIC 17EF:6161 touchscreen + * composite device. The CDC ACM control interface (0) uses a standard + * Union descriptor, but the data interface (1) is declared as vendor- + * specific class (0xff) with no CDC data descriptors, so cdc-acm would + * normally reject it. The firmware also requires continuous polling of + * the notification endpoint (EP 0x82) to suppress a 20-second watchdog + * reset; ALWAYS_POLL_CTRL keeps the ctrlurb active even when no TTY is + * open. Match only the control interface by class to avoid probing the + * vendor-specific data interface. + */ + { USB_DEVICE_INTERFACE_CLASS(0x17ef, 0x6161, USB_CLASS_COMM), + .driver_info = VENDOR_CLASS_DATA_IFACE | ALWAYS_POLL_CTRL }, + /* control interfaces without any protocol set */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTO_NONE) }, diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 669b9e6879bf..746414763da5 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -1178,7 +1178,7 @@ static int usblp_probe(struct usb_interface *intf, } /* Allocate buffer for printer status */ - usblp->statusbuf = kmalloc(STATUS_BUF_SIZE, GFP_KERNEL); + usblp->statusbuf = kzalloc(STATUS_BUF_SIZE, GFP_KERNEL); if (!usblp->statusbuf) { retval = -ENOMEM; goto abort; @@ -1377,6 +1377,7 @@ static int usblp_cache_device_id_string(struct usblp *usblp) { int err, length; + memset(usblp->device_id_string, 0, USBLP_DEVICE_ID_SIZE); err = usblp_get_id(usblp, 0, usblp->device_id_string, USBLP_DEVICE_ID_SIZE - 1); if (err < 0) { dev_dbg(&usblp->intf->dev, diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index b34fb65813c4..9b69148128e5 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -286,12 +286,15 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); ret = ulpi_of_register(ulpi); - if (ret) + if (ret) { + kfree(ulpi); return ret; + } ret = ulpi_read_id(ulpi); if (ret) { of_node_put(ulpi->dev.of_node); + kfree(ulpi); return ret; } diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 6a1fd967e0a6..417140b012bb 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -54,8 +54,8 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev, * follows the SuperSpeed Endpoint Companion descriptor */ desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer; - if (desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP || - size < USB_DT_SSP_ISOC_EP_COMP_SIZE) { + if (size < USB_DT_SSP_ISOC_EP_COMP_SIZE || + desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP) { dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion" "for config %d interface %d altsetting %d ep %d.\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 4137ab47f1cd..e00eaf9e22cd 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -26,14 +26,6 @@ struct ep_device { #define to_ep_device(_dev) \ container_of(_dev, struct ep_device, dev) -struct ep_attribute { - struct attribute attr; - ssize_t (*show)(struct usb_device *, - struct usb_endpoint_descriptor *, char *); -}; -#define to_ep_attribute(_attr) \ - container_of(_attr, struct ep_attribute, attr) - #define usb_ep_attr(field, format_string) \ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 2ab120ce2fa8..75e2bfd744a9 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1063,7 +1063,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) } EXPORT_SYMBOL_GPL(usb_string); -/* one UTF-8-encoded 16-bit character has at most three bytes */ +/* one 16-bit character, when UTF-8-encoded, has at most three bytes */ #define MAX_USB_STRING_SIZE (127 * 3 + 1) /** @@ -1084,16 +1084,18 @@ char *usb_cache_string(struct usb_device *udev, int index) return NULL; buf = kmalloc(MAX_USB_STRING_SIZE, GFP_NOIO); - if (buf) { - len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE); - if (len > 0) { - smallbuf = kmalloc(++len, GFP_NOIO); - if (!smallbuf) - return buf; - memcpy(smallbuf, buf, len); - } + if (!buf) + return NULL; + + len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE); + if (len <= 0) { kfree(buf); + return NULL; } + + smallbuf = krealloc(buf, len + 1, GFP_NOIO); + if (unlikely(!smallbuf)) + return buf; return smallbuf; } EXPORT_SYMBOL_GPL(usb_cache_string); diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c index 763e4122ed5b..a6ee20d8774e 100644 --- a/drivers/usb/core/of.c +++ b/drivers/usb/core/of.c @@ -79,17 +79,13 @@ EXPORT_SYMBOL_GPL(usb_of_has_combined_node); static bool usb_of_has_devices_or_graph(const struct usb_device *hub) { const struct device_node *np = hub->dev.of_node; - struct device_node *child; if (of_graph_is_present(np)) return true; - for_each_child_of_node(np, child) { - if (of_property_present(child, "reg")) { - of_node_put(child); + for_each_child_of_node_scoped(np, child) + if (of_property_present(child, "reg")) return true; - } - } return false; } diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 44e38f922bc5..b1364f0c384c 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -21,6 +21,20 @@ static int usb_port_block_power_off; static const struct attribute_group *port_dev_group[]; +static bool usb_port_allow_power_off(struct usb_device *hdev, + struct usb_hub *hub, + struct usb_port *port_dev) +{ + if (hub_is_port_power_switchable(hub)) + return true; + + if (!IS_ENABLED(CONFIG_ACPI)) + return false; + + return port_dev->connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED && + usb_acpi_power_manageable(hdev, port_dev->portnum - 1); +} + static ssize_t early_stop_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -141,6 +155,7 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr, usb_disconnect(&port_dev->child); rc = usb_hub_set_port_power(hdev, hub, port1, !disabled); + msleep(2 * hub_power_on_good_delay(hub)); if (disabled) { usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION); @@ -805,10 +820,10 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) device_enable_async_suspend(&port_dev->dev); /* - * Keep hidden the ability to enable port-poweroff if the hub - * does not support power switching. + * Keep hidden the ability to enable port-poweroff if neither the + * USB hub nor platform firmware can manage downstream port power. */ - if (!hub_is_port_power_switchable(hub)) + if (!usb_port_allow_power_off(hdev, hub, port_dev)) return 0; /* Attempt to let userspace take over the policy. */ diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 240b15bc52cb..18169727a413 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -150,6 +150,18 @@ config USB_DWC3_IMX8MP functionality. Say 'Y' or 'M' if you have one such device. +config USB_DWC3_IMX + tristate "NXP iMX Platform" + depends on OF && COMMON_CLK + depends on (ARCH_MXC && ARM64) || COMPILE_TEST + default USB_DWC3 + help + NXP iMX SoC use DesignWare Core IP for USB2/3 + functionality. + This driver also handles the wakeup feature outside + of DesignWare Core. + Say 'Y' or 'M' if you have one such device. + config USB_DWC3_XILINX tristate "Xilinx Platforms" depends on (ARCH_ZYNQMP || COMPILE_TEST) && OF diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 073bef5309b5..f37971197203 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom-legacy.o obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o +obj-$(CONFIG_USB_DWC3_IMX) += dwc3-imx.o obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 161a4d58b2ce..65213896de99 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -782,6 +782,24 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index) return 0; } +static void dwc3_ulpi_setup(struct dwc3 *dwc) +{ + int index; + u32 reg; + + /* Don't do anything if there is no ULPI PHY */ + if (!dwc->ulpi) + return; + + if (dwc->enable_usb2_transceiver_delay) { + for (index = 0; index < dwc->num_usb2_ports; index++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); + reg |= DWC3_GUSB2PHYCFG_XCVRDLY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + } + } +} + /** * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core * @dwc: Pointer to our controller context structure @@ -1341,12 +1359,6 @@ int dwc3_core_init(struct dwc3 *dwc) hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); - /* - * Write Linux Version Code to our GUID register so it's easy to figure - * out which kernel version a bug was found. - */ - dwc3_writel(dwc, DWC3_GUID, LINUX_VERSION_CODE); - ret = dwc3_phy_setup(dwc); if (ret) return ret; @@ -1363,6 +1375,8 @@ int dwc3_core_init(struct dwc3 *dwc) dwc->ulpi_ready = true; } + dwc3_ulpi_setup(dwc); + if (!dwc->phys_ready) { ret = dwc3_core_get_phy(dwc); if (ret) @@ -1378,6 +1392,12 @@ int dwc3_core_init(struct dwc3 *dwc) if (ret) goto err_exit_phy; + /* + * Write Linux Version Code to our GUID register so it's easy to figure + * out which kernel version a bug was found. + */ + dwc3_writel(dwc, DWC3_GUID, LINUX_VERSION_CODE); + dwc3_core_setup_global_control(dwc); dwc3_core_num_eps(dwc); @@ -1675,6 +1695,9 @@ static void dwc3_get_software_properties(struct dwc3 *dwc, u16 gsbuscfg0_reqinfo; int ret; + if (properties->needs_full_reinit) + dwc->needs_full_reinit = true; + dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED; if (properties->gsbuscfg0_reqinfo != @@ -2479,7 +2502,8 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) dwc3_core_exit(dwc); break; case DWC3_GCTL_PRTCAP_HOST: - if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) { + if (!PMSG_IS_AUTO(msg) && + (!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) { dwc3_core_exit(dwc); break; } @@ -2542,7 +2566,8 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) dwc3_gadget_resume(dwc); break; case DWC3_GCTL_PRTCAP_HOST: - if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) { + if (!PMSG_IS_AUTO(msg) && + (!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) { ret = dwc3_core_init_for_resume(dwc); if (ret) return ret; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index a35b3db1f9f3..e0dee9d28740 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -302,6 +302,7 @@ #define DWC3_GUSB2PHYCFG_SUSPHY BIT(6) #define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4) #define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8) +#define DWC3_GUSB2PHYCFG_XCVRDLY BIT(9) #define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3) #define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1) #define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10) @@ -1119,6 +1120,8 @@ struct dwc3_glue_ops { * @usb3_lpm_capable: set if hadrware supports Link Power Management * @usb2_lpm_disable: set to disable usb2 lpm for host * @usb2_gadget_lpm_disable: set to disable usb2 lpm for gadget + * @needs_full_reinit: set to indicate the core may lose power and need full + * initialization during system pm * @disable_scramble_quirk: set if we enable the disable scramble quirk * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk @@ -1149,7 +1152,7 @@ struct dwc3_glue_ops { * VBUS with an external supply. * @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed * instances in park mode. - * @parkmode_disable_hs_quirk: set if we need to disable all HishSpeed + * @parkmode_disable_hs_quirk: set if we need to disable all HighSpeed * instances in park mode. * @gfladj_refclk_lpm_sel: set if we need to enable SOF/ITP counter * running based on ref_clk @@ -1161,6 +1164,8 @@ struct dwc3_glue_ops { * 3 - Reserved * @dis_metastability_quirk: set to disable metastability quirk. * @dis_split_quirk: set to disable split boundary. + * @enable_usb2_transceiver_delay: Set to insert a delay before the + * assertion of the TxValid signal during a HS Chirp. * @sys_wakeup: set if the device may do system wakeup. * @wakeup_configured: set if the device is configured for remote wakeup. * @suspended: set to track suspend event due to U3/L2. @@ -1373,6 +1378,7 @@ struct dwc3 { unsigned usb3_lpm_capable:1; unsigned usb2_lpm_disable:1; unsigned usb2_gadget_lpm_disable:1; + unsigned needs_full_reinit:1; unsigned disable_scramble_quirk:1; unsigned u2exit_lfps_quirk:1; @@ -1403,6 +1409,7 @@ struct dwc3 { unsigned dis_metastability_quirk:1; unsigned dis_split_quirk:1; + unsigned enable_usb2_transceiver_delay:1; unsigned async_callbacks:1; unsigned sys_wakeup:1; unsigned wakeup_configured:1; diff --git a/drivers/usb/dwc3/dwc3-generic-plat.c b/drivers/usb/dwc3/dwc3-generic-plat.c index e846844e0023..ca69ac0eb07c 100644 --- a/drivers/usb/dwc3/dwc3-generic-plat.c +++ b/drivers/usb/dwc3/dwc3-generic-plat.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include "glue.h" #define EIC7700_HSP_BUS_FILTER_EN BIT(0) @@ -69,6 +71,20 @@ static int dwc3_eic7700_init(struct dwc3_generic *dwc3g) return 0; } +static int dwc3_spacemit_k1_init(struct dwc3_generic *dwc3g) +{ + struct device *dev = dwc3g->dev; + + if (usb_get_dr_mode(dev) == USB_DR_MODE_HOST) { + int ret = devm_regulator_get_enable_optional(dev, "vbus"); + + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable VBUS\n"); + } + + return 0; +} + static int dwc3_generic_probe(struct platform_device *pdev) { const struct dwc3_generic_config *plat_config; @@ -201,6 +217,11 @@ static const struct dev_pm_ops dwc3_generic_dev_pm_ops = { dwc3_generic_runtime_idle) }; +static const struct dwc3_generic_config spacemit_k1_dwc3 = { + .init = dwc3_spacemit_k1_init, + .properties = DWC3_DEFAULT_PROPERTIES, +}; + static const struct dwc3_generic_config fsl_ls1028_dwc3 = { .properties.gsbuscfg0_reqinfo = 0x2222, }; @@ -211,9 +232,11 @@ static const struct dwc3_generic_config eic7700_dwc3 = { }; static const struct of_device_id dwc3_generic_of_match[] = { - { .compatible = "spacemit,k1-dwc3", }, + { .compatible = "spacemit,k1-dwc3", &spacemit_k1_dwc3}, + { .compatible = "spacemit,k3-dwc3", }, { .compatible = "fsl,ls1028a-dwc3", &fsl_ls1028_dwc3}, { .compatible = "eswin,eic7700-dwc3", &eic7700_dwc3}, + { .compatible = "starfive,jhb100-dwc3", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, dwc3_generic_of_match); diff --git a/drivers/usb/dwc3/dwc3-imx.c b/drivers/usb/dwc3/dwc3-imx.c new file mode 100644 index 000000000000..973a486b544d --- /dev/null +++ b/drivers/usb/dwc3/dwc3-imx.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dwc3-imx.c - NXP i.MX Soc USB3 Specific Glue layer + * + * Copyright 2026 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "glue.h" + +/* USB wakeup registers */ +#define USB_WAKEUP_CTRL 0x00 + +/* Global wakeup interrupt enable, also used to clear interrupt */ +#define USB_WAKEUP_EN BIT(31) +/* Wakeup from connect or disconnect, only for superspeed */ +#define USB_WAKEUP_SS_CONN BIT(5) +/* 0 select vbus_valid, 1 select sessvld */ +#define USB_WAKEUP_VBUS_SRC_SESS_VAL BIT(4) +/* Enable signal for wake up from u3 state */ +#define USB_WAKEUP_U3_EN BIT(3) +/* Enable signal for wake up from id change */ +#define USB_WAKEUP_ID_EN BIT(2) +/* Enable signal for wake up from vbus change */ +#define USB_WAKEUP_VBUS_EN BIT(1) +/* Enable signal for wake up from dp/dm change */ +#define USB_WAKEUP_DPDM_EN BIT(0) + +#define USB_WAKEUP_EN_MASK GENMASK(5, 0) + +/* USB glue registers */ +#define USB_CTRL0 0x00 +#define USB_CTRL1 0x04 + +#define USB_CTRL0_PORTPWR_EN BIT(12) /* 1 - PPC enabled (default) */ +#define USB_CTRL0_USB3_FIXED BIT(22) /* 1 - USB3 permanent attached */ +#define USB_CTRL0_USB2_FIXED BIT(23) /* 1 - USB2 permanent attached */ + +#define USB_CTRL1_OC_POLARITY BIT(16) /* 0 - HIGH / 1 - LOW */ +#define USB_CTRL1_PWR_POLARITY BIT(17) /* 0 - HIGH / 1 - LOW */ + +struct dwc3_imx { + struct dwc3 dwc; + struct device *dev; + void __iomem *blkctl_base; + void __iomem *glue_base; + struct clk *hsio_clk; + struct clk *suspend_clk; + int irq; + bool pm_suspended; + bool wakeup_pending; + unsigned permanent_attached:1; + unsigned disable_pwr_ctrl:1; + unsigned overcur_active_low:1; + unsigned power_active_low:1; +}; + +#define to_dwc3_imx(d) container_of((d), struct dwc3_imx, dwc) + +static void dwc3_imx_get_property(struct dwc3_imx *dwc_imx) +{ + struct device *dev = dwc_imx->dev; + + dwc_imx->permanent_attached = + device_property_read_bool(dev, "fsl,permanently-attached"); + dwc_imx->disable_pwr_ctrl = + device_property_read_bool(dev, "fsl,disable-port-power-control"); + dwc_imx->overcur_active_low = + device_property_read_bool(dev, "fsl,over-current-active-low"); + dwc_imx->power_active_low = + device_property_read_bool(dev, "fsl,power-active-low"); +} + +static void dwc3_imx_configure_glue(struct dwc3_imx *dwc_imx) +{ + u32 value; + + if (!dwc_imx->glue_base) + return; + + value = readl(dwc_imx->glue_base + USB_CTRL0); + + if (dwc_imx->permanent_attached) + value |= USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED; + else + value &= ~(USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED); + + if (dwc_imx->disable_pwr_ctrl) + value &= ~USB_CTRL0_PORTPWR_EN; + else + value |= USB_CTRL0_PORTPWR_EN; + + writel(value, dwc_imx->glue_base + USB_CTRL0); + + value = readl(dwc_imx->glue_base + USB_CTRL1); + if (dwc_imx->overcur_active_low) + value |= USB_CTRL1_OC_POLARITY; + else + value &= ~USB_CTRL1_OC_POLARITY; + + if (dwc_imx->power_active_low) + value |= USB_CTRL1_PWR_POLARITY; + else + value &= ~USB_CTRL1_PWR_POLARITY; + + writel(value, dwc_imx->glue_base + USB_CTRL1); +} + +static void dwc3_imx_wakeup_enable(struct dwc3_imx *dwc_imx, pm_message_t msg) +{ + struct dwc3 *dwc = &dwc_imx->dwc; + u32 val; + + val = readl(dwc_imx->blkctl_base + USB_WAKEUP_CTRL); + + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && dwc->xhci) { + val |= USB_WAKEUP_EN | USB_WAKEUP_DPDM_EN; + if (PMSG_IS_AUTO(msg)) + val |= USB_WAKEUP_SS_CONN | USB_WAKEUP_U3_EN; + } else { + val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN | + USB_WAKEUP_VBUS_SRC_SESS_VAL; + } + + writel(val, dwc_imx->blkctl_base + USB_WAKEUP_CTRL); +} + +static void dwc3_imx_wakeup_disable(struct dwc3_imx *dwc_imx) +{ + u32 val; + + val = readl(dwc_imx->blkctl_base + USB_WAKEUP_CTRL); + val &= ~(USB_WAKEUP_EN | USB_WAKEUP_EN_MASK); + writel(val, dwc_imx->blkctl_base + USB_WAKEUP_CTRL); +} + +static irqreturn_t dwc3_imx_interrupt(int irq, void *data) +{ + struct dwc3_imx *dwc_imx = data; + struct dwc3 *dwc = &dwc_imx->dwc; + + if (!dwc_imx->pm_suspended) + return IRQ_HANDLED; + + disable_irq_nosync(dwc_imx->irq); + dwc_imx->wakeup_pending = true; + + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && dwc->xhci) + pm_runtime_resume(&dwc->xhci->dev); + else if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) + pm_runtime_get(dwc->dev); + + return IRQ_HANDLED; +} + +static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role) +{ + if (role == USB_ROLE_HOST) + /* + * For xhci host, we need disable dwc core auto + * suspend, because during this auto suspend delay(5s), + * xhci host RUN_STOP is cleared and wakeup is not + * enabled, if device is inserted, xhci host can't + * response the connection. + */ + pm_runtime_dont_use_autosuspend(dwc->dev); + else + pm_runtime_use_autosuspend(dwc->dev); +} + +static struct dwc3_glue_ops dwc3_imx_glue_ops = { + .pre_set_role = dwc3_imx_pre_set_role, +}; + +static const struct property_entry dwc3_imx_properties[] = { + PROPERTY_ENTRY_BOOL("xhci-missing-cas-quirk"), + PROPERTY_ENTRY_BOOL("xhci-skip-phy-init-quirk"), + {}, +}; + +static const struct software_node dwc3_imx_swnode = { + .properties = dwc3_imx_properties, +}; + +static int dwc3_imx_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dwc3_imx *dwc_imx; + struct dwc3 *dwc; + struct resource *res; + const char *irq_name; + struct dwc3_probe_data probe_data = {}; + int ret, irq; + + dwc_imx = devm_kzalloc(dev, sizeof(*dwc_imx), GFP_KERNEL); + if (!dwc_imx) + return -ENOMEM; + + platform_set_drvdata(pdev, dwc_imx); + dwc_imx->dev = dev; + + dwc3_imx_get_property(dwc_imx); + + dwc_imx->blkctl_base = devm_platform_ioremap_resource_byname(pdev, "blkctl"); + if (IS_ERR(dwc_imx->blkctl_base)) + return PTR_ERR(dwc_imx->blkctl_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "glue"); + if (!res) { + dev_warn(dev, "Base address for glue layer missing\n"); + } else { + dwc_imx->glue_base = devm_ioremap_resource(dev, res); + if (IS_ERR(dwc_imx->glue_base)) + return PTR_ERR(dwc_imx->glue_base); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); + if (!res) + return dev_err_probe(dev, -ENODEV, "missing core memory resource\n"); + + dwc_imx->hsio_clk = devm_clk_get_enabled(dev, "hsio"); + if (IS_ERR(dwc_imx->hsio_clk)) + return dev_err_probe(dev, PTR_ERR(dwc_imx->hsio_clk), + "Failed to get hsio clk\n"); + + dwc_imx->suspend_clk = devm_clk_get_enabled(dev, "suspend"); + if (IS_ERR(dwc_imx->suspend_clk)) + return dev_err_probe(dev, PTR_ERR(dwc_imx->suspend_clk), + "Failed to get suspend clk\n"); + + irq = platform_get_irq_byname(pdev, "wakeup"); + if (irq < 0) + return irq; + dwc_imx->irq = irq; + + irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s:wakeup", dev_name(dev)); + if (!irq_name) + return dev_err_probe(dev, -ENOMEM, "failed to create irq_name\n"); + + ret = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx_interrupt, + IRQF_ONESHOT | IRQF_NO_AUTOEN, + irq_name, dwc_imx); + if (ret) + return dev_err_probe(dev, ret, "failed to request IRQ #%d\n", irq); + + ret = device_add_software_node(dev, &dwc3_imx_swnode); + if (ret) + return dev_err_probe(dev, ret, "failed to add software node\n"); + + dwc3_imx_configure_glue(dwc_imx); + + dwc = &dwc_imx->dwc; + dwc->dev = dev; + dwc->glue_ops = &dwc3_imx_glue_ops; + + probe_data.res = res; + probe_data.dwc = dwc; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; + probe_data.properties.needs_full_reinit = true; + + ret = dwc3_core_probe(&probe_data); + if (ret) { + device_remove_software_node(dev); + return ret; + } + + device_set_wakeup_capable(dev, true); + return 0; +} + +static void dwc3_imx_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dwc3 *dwc = dev_get_drvdata(dev); + + dwc3_core_remove(dwc); + device_remove_software_node(dev); +} + +static void dwc3_imx_suspend(struct dwc3_imx *dwc_imx, pm_message_t msg) +{ + if (dwc_imx->pm_suspended) + return; + + if (PMSG_IS_AUTO(msg) || device_may_wakeup(dwc_imx->dev)) + dwc3_imx_wakeup_enable(dwc_imx, msg); + + enable_irq(dwc_imx->irq); + dwc_imx->pm_suspended = true; +} + +static void dwc3_imx_resume(struct dwc3_imx *dwc_imx, pm_message_t msg) +{ + struct dwc3 *dwc = &dwc_imx->dwc; + + if (!dwc_imx->pm_suspended) + return; + + dwc_imx->pm_suspended = false; + if (!dwc_imx->wakeup_pending) + disable_irq_nosync(dwc_imx->irq); + + dwc3_imx_wakeup_disable(dwc_imx); + + /* Upon power loss any previous configuration is lost, restore it */ + dwc3_imx_configure_glue(dwc_imx); + + if (dwc_imx->wakeup_pending) { + dwc_imx->wakeup_pending = false; + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) + pm_runtime_put_autosuspend(dwc->dev); + else + /* + * Add wait for xhci switch from suspend + * clock to normal clock to detect connection. + */ + usleep_range(9000, 10000); + } +} + +static int dwc3_imx_runtime_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc); + int ret; + + ret = dwc3_runtime_suspend(dwc); + if (ret) + return ret; + + dwc3_imx_suspend(dwc_imx, PMSG_AUTO_SUSPEND); + return 0; +} + +static int dwc3_imx_runtime_resume(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc); + + dwc3_imx_resume(dwc_imx, PMSG_AUTO_RESUME); + return dwc3_runtime_resume(dwc); +} + +static int dwc3_imx_runtime_idle(struct device *dev) +{ + return dwc3_runtime_idle(dev_get_drvdata(dev)); +} + +static int dwc3_imx_pm_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc); + int ret; + + ret = dwc3_pm_suspend(dwc); + if (ret) + return ret; + + dwc3_imx_suspend(dwc_imx, PMSG_SUSPEND); + + if (device_may_wakeup(dev)) { + enable_irq_wake(dwc_imx->irq); + device_set_out_band_wakeup(dev); + } else { + clk_disable_unprepare(dwc_imx->suspend_clk); + } + + clk_disable_unprepare(dwc_imx->hsio_clk); + + return 0; +} + +static int dwc3_imx_pm_resume(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc); + int ret; + + if (device_may_wakeup(dwc_imx->dev)) { + disable_irq_wake(dwc_imx->irq); + } else { + ret = clk_prepare_enable(dwc_imx->suspend_clk); + if (ret) + return ret; + } + + ret = clk_prepare_enable(dwc_imx->hsio_clk); + if (ret) { + clk_disable_unprepare(dwc_imx->suspend_clk); + return ret; + } + + dwc3_imx_resume(dwc_imx, PMSG_RESUME); + + ret = dwc3_pm_resume(dwc); + if (ret) + return ret; + + return 0; +} + +static void dwc3_imx_complete(struct device *dev) +{ + dwc3_pm_complete(dev_get_drvdata(dev)); +} + +static int dwc3_imx_prepare(struct device *dev) +{ + return dwc3_pm_prepare(dev_get_drvdata(dev)); +} + +static const struct dev_pm_ops dwc3_imx_dev_pm_ops = { + SYSTEM_SLEEP_PM_OPS(dwc3_imx_pm_suspend, dwc3_imx_pm_resume) + RUNTIME_PM_OPS(dwc3_imx_runtime_suspend, dwc3_imx_runtime_resume, + dwc3_imx_runtime_idle) + .complete = pm_sleep_ptr(dwc3_imx_complete), + .prepare = pm_sleep_ptr(dwc3_imx_prepare), +}; + +static const struct of_device_id dwc3_imx_of_match[] = { + { .compatible = "nxp,imx8mp-dwc3", }, + {}, +}; +MODULE_DEVICE_TABLE(of, dwc3_imx_of_match); + +static struct platform_driver dwc3_imx_driver = { + .probe = dwc3_imx_probe, + .remove = dwc3_imx_remove, + .driver = { + .name = "imx-dwc3", + .pm = pm_ptr(&dwc3_imx_dev_pm_ops), + .of_match_table = dwc3_imx_of_match, + }, +}; + +module_platform_driver(dwc3_imx_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DesignWare USB3 i.MX Glue Layer"); diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 9ac75547820d..f43f73ac36ff 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -526,14 +526,14 @@ static int dwc3_qcom_find_num_ports(struct platform_device *pdev) int irq; irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_1"); - if (irq <= 0) + if (irq < 0) return 1; for (port_num = 2; port_num <= DWC3_QCOM_MAX_PORTS; port_num++) { sprintf(irq_name, "dp_hs_phy_%d", port_num); irq = platform_get_irq_byname_optional(pdev, irq_name); - if (irq <= 0) + if (irq < 0) return port_num - 1; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0a688904ce8c..3d4ca68e584c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1688,7 +1688,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) * transfer, there's no need to update the transfer. */ if (!ret && !starting) - return ret; + return 0; req = next_request(&dep->started_list); if (!req) { diff --git a/drivers/usb/dwc3/glue.h b/drivers/usb/dwc3/glue.h index df86e14cb706..d738e1739ae0 100644 --- a/drivers/usb/dwc3/glue.h +++ b/drivers/usb/dwc3/glue.h @@ -12,9 +12,12 @@ /** * dwc3_properties: DWC3 core properties * @gsbuscfg0_reqinfo: Value to be programmed in the GSBUSCFG0.REQINFO field + * @needs_full_reinit: indicate the controller may not remain power during system + * pm and need full initialization */ struct dwc3_properties { u32 gsbuscfg0_reqinfo; + unsigned needs_full_reinit:1; }; #define DWC3_DEFAULT_PROPERTIES ((struct dwc3_properties){ \ diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c index 57daad15f502..a256b7f5d78b 100644 --- a/drivers/usb/dwc3/ulpi.c +++ b/drivers/usb/dwc3/ulpi.c @@ -10,10 +10,13 @@ #include #include #include +#include #include "core.h" #include "io.h" +#define USB_VENDOR_MICROCHIP 0x0424 + #define DWC3_ULPI_ADDR(a) \ ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ @@ -83,6 +86,26 @@ static const struct ulpi_ops dwc3_ulpi_ops = { .write = dwc3_ulpi_write, }; +static void dwc3_ulpi_detect_config(struct dwc3 *dwc) +{ + struct ulpi *ulpi = dwc->ulpi; + + switch (ulpi->id.vendor) { + case USB_VENDOR_MICROCHIP: + switch (ulpi->id.product) { + case 0x0009: + /* Microchip USB3340 ULPI PHY */ + dwc->enable_usb2_transceiver_delay = true; + break; + default: + break; + } + break; + default: + break; + } +} + int dwc3_ulpi_init(struct dwc3 *dwc) { /* Register the interface */ @@ -92,6 +115,8 @@ int dwc3_ulpi_init(struct dwc3 *dwc) return PTR_ERR(dwc->ulpi); } + dwc3_ulpi_detect_config(dwc); + return 0; } diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index e5ccaec7750c..c5a12a6760ea 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -106,7 +106,7 @@ struct f_hidg { struct list_head report_list; struct device dev; - struct cdev cdev; + struct cdev *cdev; struct usb_function func; struct usb_ep *in_ep; @@ -749,8 +749,9 @@ static int f_hidg_release(struct inode *inode, struct file *fd) static int f_hidg_open(struct inode *inode, struct file *fd) { + struct kobject *parent = inode->i_cdev->kobj.parent; struct f_hidg *hidg = - container_of(inode->i_cdev, struct f_hidg, cdev); + container_of(parent, struct f_hidg, dev.kobj); fd->private_data = hidg; @@ -1276,8 +1277,14 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) } /* create char device */ - cdev_init(&hidg->cdev, &f_hidg_fops); - status = cdev_device_add(&hidg->cdev, &hidg->dev); + hidg->cdev = cdev_alloc(); + if (!hidg->cdev) { + status = -ENOMEM; + goto fail_free_all; + } + hidg->cdev->ops = &f_hidg_fops; + + status = cdev_device_add(hidg->cdev, &hidg->dev); if (status) goto fail_free_all; @@ -1579,7 +1586,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_hidg *hidg = func_to_hidg(f); - cdev_device_del(&hidg->cdev, &hidg->dev); + cdev_device_del(hidg->cdev, &hidg->dev); destroy_workqueue(hidg->workqueue); usb_free_all_descriptors(f); } diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index b5f0defde95d..19fdac024343 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -1541,9 +1541,9 @@ static int f_midi2_create_card(struct f_midi2 *midi2) return err; midi2->card = card; - strcpy(card->driver, "f_midi2"); - strcpy(card->shortname, "MIDI 2.0 Gadget"); - strcpy(card->longname, "MIDI 2.0 Gadget"); + strscpy(card->driver, "f_midi2"); + strscpy(card->shortname, "MIDI 2.0 Gadget"); + strscpy(card->longname, "MIDI 2.0 Gadget"); id = 0; for (i = 0; i < midi2->num_eps; i++) { diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index a6fa5ed3d6cb..c5bf8a448d64 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1210,8 +1210,8 @@ parse_ntb: block_len = get_ncm(&tmp, opts->block_length); /* (d)wBlockLength */ - if (block_len > ntb_max) { - INFO(port->func.config->cdev, "OUT size exceeded\n"); + if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max)) { + INFO(port->func.config->cdev, "Bad block length: %#X\n", block_len); goto err; } diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index 2c007790ead2..b1ee9a7c2e94 100644 --- a/drivers/usb/gadget/function/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -333,6 +333,15 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req) if (unlikely(!skb)) break; + if (unlikely(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS)) { + /* Frame count from host exceeds frags[] capacity */ + dev_kfree_skb_any(skb); + if (fp->rx.skb == skb) + fp->rx.skb = NULL; + dev->stats.rx_length_errors++; + break; + } + if (skb->len == 0) { /* First fragment */ skb->protocol = htons(ETH_P_PHONET); skb_reset_mac_header(skb); diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index a7853dcbb14c..34d9f49e9987 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -2030,6 +2030,7 @@ static const struct target_core_fabric_ops usbg_ops = { .tfc_wwn_attrs = usbg_wwn_attrs, .tfc_tpg_base_attrs = usbg_base_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_DIRECT_SUBMIT, .direct_submit_supp = 1, }; diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index e43ad6373846..cdd1dfc666c4 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -1086,7 +1086,7 @@ static int gs_console_init(struct gs_port *port) if (!cons) return -ENOMEM; - strcpy(cons->console.name, "ttyGS"); + strscpy(cons->console.name, "ttyGS"); cons->console.write = gs_console_write; cons->console.device = gs_console_device; cons->console.flags = CON_PRINTBUFFER; diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index c0ab3347059a..a7a22e5ec47b 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -1647,6 +1647,10 @@ void bdc_sr_xsf(struct bdc *bdc, struct bdc_sr *sreport) u8 ep_num; ep_num = (le32_to_cpu(sreport->offset[3])>>4) & 0x1f; + if (ep_num >= bdc->num_eps) { + dev_err(bdc->dev, "xsf for invalid ep %u\n", ep_num); + return; + } ep = bdc->bdc_ep_array[ep_num]; if (!ep || !(ep->flags & BDC_EP_ENABLED)) { dev_err(bdc->dev, "xsf for ep not enabled\n"); diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 08d2a93b3bba..e8861eaad907 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1266,8 +1266,9 @@ static inline void usb_gadget_udc_stop_locked(struct usb_udc *udc) * @speed: The maximum speed to allowed to run * * This call is issued by the UDC Class driver before calling - * usb_gadget_udc_start() in order to make sure that we don't try to - * connect on speeds the gadget driver doesn't support. + * usb_gadget_udc_start_locked() in order to make sure that + * we don't try to connect on speeds the gadget driver + * doesn't support. */ static inline void usb_gadget_udc_set_speed(struct usb_udc *udc, enum usb_device_speed speed) diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index 91139ae668f4..f3ca79cece1b 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -733,8 +733,6 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) if (status == 0) { omap_writew(reg, UDC_TXDMA_CFG); /* EMIFF or SDRC */ - omap_set_dma_src_burst_mode(ep->lch, - OMAP_DMA_DATA_BURST_4); omap_set_dma_src_data_pack(ep->lch, 1); /* TIPB */ omap_set_dma_dest_params(ep->lch, @@ -756,8 +754,6 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) UDC_DATA_DMA, 0, 0); /* EMIFF or SDRC */ - omap_set_dma_dest_burst_mode(ep->lch, - OMAP_DMA_DATA_BURST_4); omap_set_dma_dest_data_pack(ep->lch, 1); } } diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 4fac477d24c0..1abea0d48c35 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -1462,7 +1462,7 @@ static int pxa_udc_wakeup(struct usb_gadget *_gadget) return 0; } -static void udc_enable(struct pxa_udc *udc); +static int udc_enable(struct pxa_udc *udc); static void udc_disable(struct pxa_udc *udc); /** @@ -1519,14 +1519,20 @@ static int should_disable_udc(struct pxa_udc *udc) static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active) { struct pxa_udc *udc = to_gadget_udc(_gadget); + int ret; if (!udc->gpiod && !udc->udc_command) return -EOPNOTSUPP; dplus_pullup(udc, is_active); - if (should_enable_udc(udc)) - udc_enable(udc); + if (should_enable_udc(udc)) { + ret = udc_enable(udc); + if (ret) { + dplus_pullup(udc, !is_active); + return ret; + } + } if (should_disable_udc(udc)) udc_disable(udc); return 0; @@ -1545,10 +1551,16 @@ static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active) static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active) { struct pxa_udc *udc = to_gadget_udc(_gadget); + int ret; udc->vbus_sensed = is_active; - if (should_enable_udc(udc)) - udc_enable(udc); + if (should_enable_udc(udc)) { + ret = udc_enable(udc); + if (ret) { + udc->vbus_sensed = !is_active; + return ret; + } + } if (should_disable_udc(udc)) udc_disable(udc); @@ -1691,12 +1703,18 @@ static void udc_init_data(struct pxa_udc *dev) * Enables the udc device : enables clocks, udc interrupts, control endpoint * interrupts, sets usb as UDC client and setups endpoints. */ -static void udc_enable(struct pxa_udc *udc) +static int udc_enable(struct pxa_udc *udc) { - if (udc->enabled) - return; + int ret; - clk_enable(udc->clk); + if (udc->enabled) + return 0; + + ret = clk_enable(udc->clk); + if (ret) { + dev_err(udc->dev, "clk_enable failed: %d\n", ret); + return ret; + } udc_writel(udc, UDCICR0, 0); udc_writel(udc, UDCICR1, 0); udc_clear_mask_UDCCR(udc, UDCCR_UDE); @@ -1726,6 +1744,8 @@ static void udc_enable(struct pxa_udc *udc) pio_irq_enable(&udc->pxa_ep[0]); udc->enabled = 1; + + return 0; } /** @@ -1761,10 +1781,16 @@ static int pxa27x_udc_start(struct usb_gadget *g, } } - if (should_enable_udc(udc)) - udc_enable(udc); + if (should_enable_udc(udc)) { + retval = udc_enable(udc); + if (retval) + goto fail_enable; + } return 0; +fail_enable: + if (!IS_ERR_OR_NULL(udc->transceiver)) + otg_set_peripheral(udc->transceiver->otg, NULL); fail: udc->driver = NULL; return retval; @@ -2430,10 +2456,16 @@ static int pxa_udc_probe(struct platform_device *pdev) goto err_add_gadget; pxa_init_debugfs(udc); - if (should_enable_udc(udc)) - udc_enable(udc); + if (should_enable_udc(udc)) { + retval = udc_enable(udc); + if (retval) + goto err_enable; + } return 0; +err_enable: + usb_del_gadget_udc(&udc->gadget); + pxa_cleanup_debugfs(udc); err_add_gadget: if (!IS_ERR_OR_NULL(udc->transceiver)) usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy); @@ -2509,13 +2541,19 @@ static int pxa_udc_resume(struct platform_device *_dev) { struct pxa_udc *udc = platform_get_drvdata(_dev); struct pxa_ep *ep; + int ret; ep = &udc->pxa_ep[0]; udc_ep_writel(ep, UDCCSR, udc->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME)); dplus_pullup(udc, udc->pullup_resume); - if (should_enable_udc(udc)) - udc_enable(udc); + if (should_enable_udc(udc)) { + ret = udc_enable(udc); + if (ret) { + dplus_pullup(udc, !udc->pullup_resume); + return ret; + } + } /* * We do not handle OTG yet. * diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index b0b264d34919..2c9c3e935a5e 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -1669,6 +1669,10 @@ static bool usb3_std_req_get_status(struct renesas_usb3 *usb3, break; case USB_RECIP_ENDPOINT: num = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; + if (num >= usb3->num_usb3_eps) { + stall = true; + break; + } usb3_ep = usb3_get_ep(usb3, num); if (usb3_ep->halt) status |= 1 << USB_ENDPOINT_HALT; @@ -1781,7 +1785,8 @@ static bool usb3_std_req_feature_endpoint(struct renesas_usb3 *usb3, struct renesas_usb3_ep *usb3_ep; struct renesas_usb3_request *usb3_req; - if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT) + if ((le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT) || + (num >= usb3->num_usb3_eps)) return true; /* stall */ usb3_ep = usb3_get_ep(usb3, num); diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c index 5f9514623956..0e0db68e0b27 100644 --- a/drivers/usb/gadget/udc/snps_udc_core.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c @@ -3151,7 +3151,7 @@ int udc_probe(struct udc *dev) tmp, dev->phys_addr, dev->chiprev, (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1"); - strcpy(tmp, UDC_DRIVER_VERSION_STRING); + strscpy(tmp, UDC_DRIVER_VERSION_STRING); if (dev->chiprev == UDC_HSA0_REV) { dev_err(dev->dev, "chip revision is A0; too old\n"); retval = -ENODEV; diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 34abff8669f8..eaaa49712a8c 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -60,7 +59,6 @@ struct orion_ehci_hcd { struct clk *clk; - struct phy *phy; }; static struct hc_driver __read_mostly ehci_orion_hc_driver; @@ -276,13 +274,6 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) goto err_put_hcd; } - priv->phy = devm_phy_optional_get(&pdev->dev, "usb"); - if (IS_ERR(priv->phy)) { - err = PTR_ERR(priv->phy); - if (err != -ENOSYS) - goto err_dis_clk; - } - /* * (Re-)program MBUS remapping windows if we are asked to. */ diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 271bcbe9b326..71e785f445a3 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -426,16 +426,11 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, } /* allocate the private part of the URB */ - urb_priv = kzalloc_obj(*urb_priv, mem_flags); + urb_priv = kzalloc_flex(*urb_priv, tds, size, mem_flags); if (!urb_priv) return -ENOMEM; - /* allocate the private part of the URB */ - urb_priv->tds = kzalloc_objs(*urb_priv->tds, size, mem_flags); - if (!urb_priv->tds) { - kfree(urb_priv); - return -ENOMEM; - } + urb_priv->num_of_tds = size; spin_lock_irqsave(&fhci->lock, flags); @@ -444,8 +439,6 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, goto err; /* fill the private part of the URB */ - urb_priv->num_of_tds = size; - urb->status = -EINPROGRESS; urb->actual_length = 0; urb->error_count = 0; @@ -453,10 +446,8 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, fhci_queue_urb(fhci, urb); err: - if (ret) { - kfree(urb_priv->tds); + if (ret) kfree(urb_priv); - } spin_unlock_irqrestore(&fhci->lock, flags); return ret; } diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h index 1f57b0989485..e221b28e8608 100644 --- a/drivers/usb/host/fhci.h +++ b/drivers/usb/host/fhci.h @@ -387,9 +387,10 @@ struct urb_priv { int tds_cnt; int state; - struct td **tds; struct ed *ed; struct timer_list time_out; + + struct td *tds[] __counted_by(num_of_tds); }; struct endpoint { diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index ade178ab34a7..d07276192256 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -700,6 +700,10 @@ static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed, seq_printf(s, "port[%d] available bw: %d%%.\n", i, ctx->bytes[i]); err_out: + if (ret == -EIO) { + seq_puts(s, "Get Port Bandwidth failed\n"); + ret = 0; + } pm_runtime_put_sync(dev); xhci_free_port_bw_ctx(xhci, ctx); return ret; @@ -710,7 +714,7 @@ static int xhci_ss_bw_show(struct seq_file *s, void *unused) int ret; struct xhci_hcd *xhci = (struct xhci_hcd *)s->private; - ret = xhci_port_bw_show(xhci, USB_SPEED_SUPER, s); + ret = xhci_port_bw_show(xhci, DEV_PORT_SPEED(XDEV_SS), s); return ret; } @@ -719,7 +723,7 @@ static int xhci_hs_bw_show(struct seq_file *s, void *unused) int ret; struct xhci_hcd *xhci = (struct xhci_hcd *)s->private; - ret = xhci_port_bw_show(xhci, USB_SPEED_HIGH, s); + ret = xhci_port_bw_show(xhci, DEV_PORT_SPEED(XDEV_HS), s); return ret; } @@ -728,7 +732,7 @@ static int xhci_fs_bw_show(struct seq_file *s, void *unused) int ret; struct xhci_hcd *xhci = (struct xhci_hcd *)s->private; - ret = xhci_port_bw_show(xhci, USB_SPEED_FULL, s); + ret = xhci_port_bw_show(xhci, DEV_PORT_SPEED(XDEV_FS), s); return ret; } diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 04cc3d681495..bacd0ddd0d09 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -375,11 +375,11 @@ static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, } -static unsigned int xhci_port_speed(unsigned int port_status) +static unsigned int xhci_port_speed(int portsc) { - if (DEV_LOWSPEED(port_status)) + if (DEV_LOWSPEED(portsc)) return USB_PORT_STAT_LOW_SPEED; - if (DEV_HIGHSPEED(port_status)) + if (DEV_HIGHSPEED(portsc)) return USB_PORT_STAT_HIGH_SPEED; /* * FIXME: Yes, we should check for full speed, but the core uses that as @@ -429,9 +429,9 @@ static unsigned int xhci_port_speed(unsigned int port_status) /** * xhci_port_state_to_neutral() - Clean up read portsc value back into writeable - * @state: u32 port value read from portsc register to be cleanup up + * @portsc: u32 port value read from portsc register to be cleanup up * - * Given a port state, this function returns a value that would result in the + * Given a portsc, this function returns a value that would result in the * port being in the same state, if the value was written to the port status * control register. * Save Read Only (RO) bits and save read/write bits where @@ -442,10 +442,10 @@ static unsigned int xhci_port_speed(unsigned int port_status) * changing port state. */ -u32 xhci_port_state_to_neutral(u32 state) +u32 xhci_port_state_to_neutral(u32 portsc) { /* Save read-only status and port state */ - return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); + return (portsc & XHCI_PORT_RO) | (portsc & XHCI_PORT_RWS); } EXPORT_SYMBOL_GPL(xhci_port_state_to_neutral); @@ -577,8 +577,8 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port) hcd->self.busnum, port->hcd_portnum + 1, portsc); } -static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, - u16 wIndex, struct xhci_port *port, u32 port_status) +static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, struct xhci_port *port, + u32 portsc) { char *port_change_bit; u32 status; @@ -621,11 +621,11 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, return; } /* Change bits are all write 1 to clear */ - xhci_portsc_writel(port, port_status | status); - port_status = xhci_portsc_readl(port); + xhci_portsc_writel(port, portsc | status); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n", - wIndex + 1, port_change_bit, port_status); + port->hcd_portnum + 1, port_change_bit, portsc); } struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd) @@ -675,14 +675,13 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, spin_lock_irqsave(&xhci->lock, *flags); } -static void xhci_port_set_test_mode(struct xhci_hcd *xhci, - u16 test_mode, u16 wIndex) +static void xhci_port_set_test_mode(struct xhci_hcd *xhci, u16 test_mode, int portnum) { u32 temp; struct xhci_port *port; /* xhci only supports test mode for usb2 ports */ - port = xhci->usb2_rhub.ports[wIndex]; + port = xhci->usb2_rhub.ports[portnum]; temp = readl(&port->port_reg->portpmsc); temp |= test_mode << PORT_TEST_MODE_SHIFT; writel(temp, &port->port_reg->portpmsc); @@ -691,8 +690,8 @@ static void xhci_port_set_test_mode(struct xhci_hcd *xhci, xhci_start(xhci); } -static int xhci_enter_test_mode(struct xhci_hcd *xhci, - u16 test_mode, u16 wIndex, unsigned long *flags) +static int xhci_enter_test_mode(struct xhci_hcd *xhci, u16 test_mode, int portnum, + unsigned long *flags) __must_hold(&xhci->lock) { int i, retval; @@ -726,10 +725,8 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, /* Disable runtime PM for test mode */ pm_runtime_forbid(xhci_to_hcd(xhci)->self.controller); /* Set PORTPMSC.PTC field to enter selected test mode */ - /* Port is selected by wIndex. port_id = wIndex + 1 */ - xhci_dbg(xhci, "Enter Test Mode: %d, Port_id=%d\n", - test_mode, wIndex + 1); - xhci_port_set_test_mode(xhci, test_mode, wIndex); + xhci_dbg(xhci, "Enter Test Mode: %u, Port_id=%d\n", test_mode, portnum + 1); + xhci_port_set_test_mode(xhci, test_mode, portnum); return retval; } @@ -853,53 +850,37 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port, } /* Updates Link Status for super Speed port */ -static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci, - u32 *status, u32 status_reg) +static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci, u32 *status, u32 portsc) { - u32 pls = status_reg & PORT_PLS_MASK; + u32 pls = portsc & PORT_PLS_MASK; - /* When the CAS bit is set then warm reset - * should be performed on port + /* + * CAS indicates that a warm reset is required, it may be set in any + * link state and is only present on roothubs. */ - if (status_reg & PORT_CAS) { - /* The CAS bit can be set while the port is - * in any link state. - * Only roothubs have CAS bit, so we - * pretend to be in compliance mode - * unless we're already in compliance - * or the inactive state. + if (portsc & PORT_CAS) { + /* + * If not already in Compliance or Inactive state, + * report Compliance Mode so the hub logic triggers a warm reset. */ - if (pls != USB_SS_PORT_LS_COMP_MOD && - pls != USB_SS_PORT_LS_SS_INACTIVE) { + if (pls != XDEV_COMP_MODE && pls != XDEV_INACTIVE) pls = USB_SS_PORT_LS_COMP_MOD; - } - /* Return also connection bit - - * hub state machine resets port - * when this bit is set. - */ - pls |= USB_PORT_STAT_CONNECTION; - } else { - /* - * Resume state is an xHCI internal state. Do not report it to - * usb core, instead, pretend to be U3, thus usb core knows - * it's not ready for transfer. - */ - if (pls == XDEV_RESUME) { - *status |= USB_SS_PORT_LS_U3; - return; - } + /* Signal a connection change to force a reset */ + *status |= USB_PORT_STAT_CONNECTION; + } else if (pls == XDEV_RESUME) { /* - * If CAS bit isn't set but the Port is already at - * Compliance Mode, fake a connection so the USB core - * notices the Compliance state and resets the port. - * This resolves an issue generated by the SN65LVPE502CP - * in which sometimes the port enters compliance mode - * caused by a delay on the host-device negotiation. + * Resume is an internal xHCI-only state and must not be exposed + * to usbcore. Report it as U3 so transfers are blocked. */ - if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && - (pls == USB_SS_PORT_LS_COMP_MOD)) - pls |= USB_PORT_STAT_CONNECTION; + pls = USB_SS_PORT_LS_U3; + } else if (pls == XDEV_COMP_MODE) { + /* + * Some hardware may enter Compliance Mode without CAS. + * Fake a connection event so usbcore notices and resets the port. + */ + if (xhci->quirks & XHCI_COMP_MODE_QUIRK) + *status |= USB_PORT_STAT_CONNECTION; } /* update status field */ @@ -913,17 +894,16 @@ static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci, * the compliance mode timer is deleted. A port won't enter * compliance mode if it has previously entered U0. */ -static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, - u16 wIndex) +static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 portsc, int portnum) { u32 all_ports_seen_u0 = ((1 << xhci->usb3_rhub.num_ports) - 1); - bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0); + bool port_in_u0 = ((portsc & PORT_PLS_MASK) == XDEV_U0); if (!(xhci->quirks & XHCI_COMP_MODE_QUIRK)) return; if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) { - xhci->port_status_u0 |= 1 << wIndex; + xhci->port_status_u0 |= 1 << portnum; if (xhci->port_status_u0 == all_ports_seen_u0) { timer_delete_sync(&xhci->comp_mode_recovery_timer); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, @@ -941,12 +921,12 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, struct xhci_bus_state *bus_state; struct xhci_hcd *xhci; struct usb_hcd *hcd; - u32 wIndex; + int portnum; hcd = port->rhub->hcd; bus_state = &port->rhub->bus_state; xhci = hcd_to_xhci(hcd); - wIndex = port->hcd_portnum; + portnum = port->hcd_portnum; if ((portsc & PORT_RESET) || !(portsc & PORT_PE)) { return -EINVAL; @@ -954,7 +934,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, /* did port event handler already start resume timing? */ if (!port->resume_timestamp) { /* If not, maybe we are in a host initiated resume? */ - if (test_bit(wIndex, &bus_state->resuming_ports)) { + if (test_bit(portnum, &bus_state->resuming_ports)) { /* Host initiated resume doesn't time the resume * signalling using resume_done[]. * It manually sets RESUME state, sleeps 20ms @@ -968,20 +948,20 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, unsigned long timeout = jiffies + msecs_to_jiffies(USB_RESUME_TIMEOUT); - set_bit(wIndex, &bus_state->resuming_ports); + set_bit(portnum, &bus_state->resuming_ports); port->resume_timestamp = timeout; mod_timer(&hcd->rh_timer, timeout); - usb_hcd_start_port_resume(&hcd->self, wIndex); + usb_hcd_start_port_resume(&hcd->self, portnum); } /* Has resume been signalled for USB_RESUME_TIME yet? */ } else if (time_after_eq(jiffies, port->resume_timestamp)) { int time_left; xhci_dbg(xhci, "resume USB2 port %d-%d\n", - hcd->self.busnum, wIndex + 1); + hcd->self.busnum, portnum + 1); port->resume_timestamp = 0; - clear_bit(wIndex, &bus_state->resuming_ports); + clear_bit(portnum, &bus_state->resuming_ports); reinit_completion(&port->rexit_done); port->rexit_active = true; @@ -1005,7 +985,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, int port_status = xhci_portsc_readl(port); xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n", - hcd->self.busnum, wIndex + 1, port_status); + hcd->self.busnum, portnum + 1, port_status); /* * keep rexit_active set if U0 transition failed so we * know to report PORT_STAT_SUSPEND status back to @@ -1014,21 +994,21 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, */ } - usb_hcd_end_port_resume(&hcd->self, wIndex); - bus_state->port_c_suspend |= 1 << wIndex; - bus_state->suspended_ports &= ~(1 << wIndex); + usb_hcd_end_port_resume(&hcd->self, portnum); + bus_state->port_c_suspend |= 1 << portnum; + bus_state->suspended_ports &= ~(1 << portnum); } return 0; } -static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li) +static u32 xhci_get_ext_port_status(u32 portsc, u32 port_li) { u32 ext_stat = 0; int speed_id; /* only support rx and tx lane counts of 1 in usb3.1 spec */ - speed_id = DEV_PORT_SPEED(raw_port_status); + speed_id = DEV_PORT_SPEED(portsc); ext_stat |= speed_id; /* bits 3:0, RX speed id */ ext_stat |= speed_id << 4; /* bits 7:4, TX speed id */ @@ -1153,10 +1133,8 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, * - Stop the Synopsys redriver Compliance Mode polling. * - Drop and reacquire the xHCI lock, in order to wait for port resume. */ -static u32 xhci_get_port_status(struct usb_hcd *hcd, - struct xhci_bus_state *bus_state, - u16 wIndex, u32 raw_port_status, - unsigned long *flags) +static u32 xhci_get_port_status(struct usb_hcd *hcd, struct xhci_bus_state *bus_state, + int portnum, u32 portsc, unsigned long *flags) __releases(&xhci->lock) __acquires(&xhci->lock) { @@ -1165,38 +1143,37 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, struct xhci_port *port; rhub = xhci_get_rhub(hcd); - port = rhub->ports[wIndex]; + port = rhub->ports[portnum]; /* common wPortChange bits */ - if (raw_port_status & PORT_CSC) + if (portsc & PORT_CSC) status |= USB_PORT_STAT_C_CONNECTION << 16; - if (raw_port_status & PORT_PEC) + if (portsc & PORT_PEC) status |= USB_PORT_STAT_C_ENABLE << 16; - if ((raw_port_status & PORT_OCC)) + if (portsc & PORT_OCC) status |= USB_PORT_STAT_C_OVERCURRENT << 16; - if ((raw_port_status & PORT_RC)) + if (portsc & PORT_RC) status |= USB_PORT_STAT_C_RESET << 16; /* common wPortStatus bits */ - if (raw_port_status & PORT_CONNECT) { + if (portsc & PORT_CONNECT) { status |= USB_PORT_STAT_CONNECTION; - status |= xhci_port_speed(raw_port_status); + status |= xhci_port_speed(portsc); } - if (raw_port_status & PORT_PE) + if (portsc & PORT_PE) status |= USB_PORT_STAT_ENABLE; - if (raw_port_status & PORT_OC) + if (portsc & PORT_OC) status |= USB_PORT_STAT_OVERCURRENT; - if (raw_port_status & PORT_RESET) + if (portsc & PORT_RESET) status |= USB_PORT_STAT_RESET; /* USB2 and USB3 specific bits, including Port Link State */ if (hcd->speed >= HCD_USB3) - xhci_get_usb3_port_status(port, &status, raw_port_status); + xhci_get_usb3_port_status(port, &status, portsc); else - xhci_get_usb2_port_status(port, &status, raw_port_status, - flags); + xhci_get_usb2_port_status(port, &status, portsc, flags); - if (bus_state->port_c_suspend & (1 << wIndex)) + if (bus_state->port_c_suspend & (1 << portnum)) status |= USB_PORT_STAT_C_SUSPEND << 16; return status; @@ -1208,23 +1185,23 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports; unsigned long flags; - u32 temp, status; + u32 portsc, portpmsc, status; int retval = 0; struct xhci_bus_state *bus_state; - u16 link_state = 0; - u16 wake_mask = 0; - u16 timeout = 0; - u16 test_mode = 0; + u16 link_state; + u16 wake_mask; + u8 timeout; + u8 test_mode; + u8 desc_type; struct xhci_hub *rhub; struct xhci_port **ports; struct xhci_port *port; - int portnum1; + int portnum; rhub = xhci_get_rhub(hcd); ports = rhub->ports; max_ports = rhub->num_ports; bus_state = &rhub->bus_state; - portnum1 = wIndex & 0xff; spin_lock_irqsave(&xhci->lock, flags); switch (typeReq) { @@ -1233,13 +1210,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, memset(buf, 0, 4); break; case GetHubDescriptor: + desc_type = (wValue & 0xff00) >> 8; /* Check to make sure userspace is asking for the USB 3.0 hub * descriptor for the USB 3.0 roothub. If not, we stall the * endpoint, like external hubs do. */ if (hcd->speed >= HCD_USB3 && - (wLength < USB_DT_SS_HUB_SIZE || - wValue != (USB_DT_SS_HUB << 8))) { + (wLength < USB_DT_SS_HUB_SIZE || desc_type != USB_DT_SS_HUB)) { xhci_dbg(xhci, "Wrong hub descriptor type for " "USB 3.0 roothub.\n"); goto error; @@ -1248,7 +1225,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, (struct usb_hub_descriptor *) buf); break; case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - if ((wValue & 0xff00) != (USB_DT_BOS << 8)) + desc_type = (wValue & 0xff00) >> 8; + if (desc_type != USB_DT_BOS) goto error; if (hcd->speed < HCD_USB3) @@ -1258,29 +1236,28 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, spin_unlock_irqrestore(&xhci->lock, flags); return retval; case GetPortStatus: - if (!portnum1 || portnum1 > max_ports) + portnum = (wIndex & 0xff) - 1; + if (!in_range(portnum, 0, max_ports)) goto error; - wIndex--; - port = ports[portnum1 - 1]; - temp = xhci_portsc_readl(port); - if (temp == ~(u32)0) { + port = ports[portnum]; + portsc = xhci_portsc_readl(port); + if (portsc == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; break; } - trace_xhci_get_port_status(port, temp); - status = xhci_get_port_status(hcd, bus_state, wIndex, temp, - &flags); + trace_xhci_get_port_status(port, portsc); + status = xhci_get_port_status(hcd, bus_state, portnum, portsc, &flags); if (status == 0xffffffff) goto error; xhci_dbg(xhci, "Get port status %d-%d read: 0x%x, return 0x%x", - hcd->self.busnum, portnum1, temp, status); + hcd->self.busnum, portnum + 1, portsc, status); put_unaligned(cpu_to_le32(status), (__le32 *) buf); /* if USB 3.1 extended port status return additional 4 bytes */ - if (wValue == 0x02) { + if (wValue == HUB_EXT_PORT_STATUS) { u32 port_li; if (hcd->speed < HCD_USB31 || wLength != 8) { @@ -1289,38 +1266,28 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; } port_li = readl(&port->port_reg->portli); - status = xhci_get_ext_port_status(temp, port_li); + status = xhci_get_ext_port_status(portsc, port_li); put_unaligned_le32(status, &buf[4]); } break; case SetPortFeature: - if (wValue == USB_PORT_FEAT_LINK_STATE) - link_state = (wIndex & 0xff00) >> 3; - if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK) - wake_mask = wIndex & 0xff00; - if (wValue == USB_PORT_FEAT_TEST) - test_mode = (wIndex & 0xff00) >> 8; - /* The MSB of wIndex is the U1/U2 timeout */ - timeout = (wIndex & 0xff00) >> 8; - - wIndex &= 0xff; - if (!portnum1 || portnum1 > max_ports) + portnum = (wIndex & 0xff) - 1; + if (!in_range(portnum, 0, max_ports)) goto error; - port = ports[portnum1 - 1]; - wIndex--; - temp = xhci_portsc_readl(port); - if (temp == ~(u32)0) { + port = ports[portnum]; + portsc = xhci_portsc_readl(port); + if (portsc == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; break; } - temp = xhci_port_state_to_neutral(temp); + portsc = xhci_port_state_to_neutral(portsc); /* FIXME: What new port features do we need to support? */ switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = xhci_portsc_readl(port); - if ((temp & PORT_PLS_MASK) != XDEV_U0) { + portsc = xhci_portsc_readl(port); + if ((portsc & PORT_PLS_MASK) != XDEV_U0) { /* Resume the port to U0 first */ xhci_set_link_state(xhci, port, XDEV_U0); spin_unlock_irqrestore(&xhci->lock, flags); @@ -1331,11 +1298,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ - temp = xhci_portsc_readl(port); - if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) - || (temp & PORT_PLS_MASK) >= XDEV_U3) { + portsc = xhci_portsc_readl(port); + if ((portsc & PORT_PE) == 0 || (portsc & PORT_RESET) || + (portsc & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending port %d-%d not in U0/U1/U2\n", - hcd->self.busnum, portnum1); + hcd->self.busnum, portnum + 1); goto error; } @@ -1354,34 +1321,35 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, msleep(10); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_portsc_readl(port); - bus_state->suspended_ports |= 1 << wIndex; + portsc = xhci_portsc_readl(port); + bus_state->suspended_ports |= 1 << portnum; break; case USB_PORT_FEAT_LINK_STATE: - temp = xhci_portsc_readl(port); + link_state = (wIndex & 0xff00) >> 3; + portsc = xhci_portsc_readl(port); /* Disable port */ if (link_state == USB_SS_PORT_LS_SS_DISABLED) { xhci_dbg(xhci, "Disable port %d-%d\n", - hcd->self.busnum, portnum1); - temp = xhci_port_state_to_neutral(temp); + hcd->self.busnum, portnum + 1); + portsc = xhci_port_state_to_neutral(portsc); /* * Clear all change bits, so that we get a new * connection event. */ - temp |= PORT_CSC | PORT_PEC | PORT_WRC | - PORT_OCC | PORT_RC | PORT_PLC | - PORT_CEC; - xhci_portsc_writel(port, temp | PORT_PE); - temp = xhci_portsc_readl(port); + portsc |= PORT_CSC | PORT_PEC | PORT_WRC | + PORT_OCC | PORT_RC | PORT_PLC | + PORT_CEC; + xhci_portsc_writel(port, portsc | PORT_PE); + portsc = xhci_portsc_readl(port); break; } /* Put link in RxDetect (enable port) */ if (link_state == USB_SS_PORT_LS_RX_DETECT) { xhci_dbg(xhci, "Enable port %d-%d\n", - hcd->self.busnum, portnum1); - xhci_set_link_state(xhci, port, link_state); - temp = xhci_portsc_readl(port); + hcd->self.busnum, portnum + 1); + xhci_set_link_state(xhci, port, XDEV_RXDETECT); + portsc = xhci_portsc_readl(port); break; } @@ -1405,27 +1373,27 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; } - if ((temp & PORT_CONNECT)) { + if ((portsc & PORT_CONNECT)) { xhci_warn(xhci, "Can't set compliance mode when port is connected\n"); goto error; } xhci_dbg(xhci, "Enable compliance mode transition for port %d-%d\n", - hcd->self.busnum, portnum1); - xhci_set_link_state(xhci, port, link_state); + hcd->self.busnum, portnum + 1); + xhci_set_link_state(xhci, port, XDEV_COMP_MODE); - temp = xhci_portsc_readl(port); + portsc = xhci_portsc_readl(port); break; } /* Port must be enabled */ - if (!(temp & PORT_PE)) { + if (!(portsc & PORT_PE)) { retval = -ENODEV; break; } /* Can't set port link state above '3' (U3) */ if (link_state > USB_SS_PORT_LS_U3) { xhci_warn(xhci, "Cannot set port %d-%d link state %d\n", - hcd->self.busnum, portnum1, link_state); + hcd->self.busnum, portnum + 1, link_state); goto error; } @@ -1437,7 +1405,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * completion */ if (link_state == USB_SS_PORT_LS_U0) { - u32 pls = temp & PORT_PLS_MASK; + u32 pls = portsc & PORT_PLS_MASK; bool wait_u0 = false; /* already in U0 */ @@ -1450,7 +1418,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, reinit_completion(&port->u3exit_done); } if (pls <= XDEV_U3) /* U1, U2, U3 */ - xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U0); + xhci_set_link_state(xhci, port, XDEV_U0); if (!wait_u0) { if (pls > XDEV_U3) goto error; @@ -1460,9 +1428,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (!wait_for_completion_timeout(&port->u3exit_done, msecs_to_jiffies(500))) xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n", - hcd->self.busnum, portnum1); + hcd->self.busnum, portnum + 1); spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_portsc_readl(port); + portsc = xhci_portsc_readl(port); break; } @@ -1476,17 +1444,17 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_stop_device(xhci, port->slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); } - xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U3); + xhci_set_link_state(xhci, port, XDEV_U3); spin_unlock_irqrestore(&xhci->lock, flags); while (retries--) { usleep_range(4000, 8000); - temp = xhci_portsc_readl(port); - if ((temp & PORT_PLS_MASK) == XDEV_U3) + portsc = xhci_portsc_readl(port); + if ((portsc & PORT_PLS_MASK) == XDEV_U3) break; } spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_portsc_readl(port); - bus_state->suspended_ports |= 1 << wIndex; + portsc = xhci_portsc_readl(port); + bus_state->suspended_ports |= 1 << portnum; } break; case USB_PORT_FEAT_POWER: @@ -1499,93 +1467,98 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_set_port_power(xhci, port, true, &flags); break; case USB_PORT_FEAT_RESET: - temp = (temp | PORT_RESET); - xhci_portsc_writel(port, temp); + portsc |= PORT_RESET; + xhci_portsc_writel(port, portsc); - temp = xhci_portsc_readl(port); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n", - hcd->self.busnum, portnum1, temp); + hcd->self.busnum, portnum + 1, portsc); break; case USB_PORT_FEAT_REMOTE_WAKE_MASK: + wake_mask = wIndex & 0xff00; xhci_set_remote_wake_mask(xhci, port, wake_mask); - temp = xhci_portsc_readl(port); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n", - hcd->self.busnum, portnum1, temp); + hcd->self.busnum, portnum + 1, portsc); break; case USB_PORT_FEAT_BH_PORT_RESET: - temp |= PORT_WR; - xhci_portsc_writel(port, temp); - temp = xhci_portsc_readl(port); + portsc |= PORT_WR; + xhci_portsc_writel(port, portsc); + portsc = xhci_portsc_readl(port); break; case USB_PORT_FEAT_U1_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(&port->port_reg->portpmsc); - temp &= ~PORT_U1_TIMEOUT_MASK; - temp |= PORT_U1_TIMEOUT(timeout); - writel(temp, &port->port_reg->portpmsc); + + timeout = (wIndex & 0xff00) >> 8; + portpmsc = readl(&port->port_reg->portpmsc); + portpmsc &= ~PORT_U1_TIMEOUT_MASK; + portpmsc |= PORT_U1_TIMEOUT(timeout); + writel(portpmsc, &port->port_reg->portpmsc); break; case USB_PORT_FEAT_U2_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(&port->port_reg->portpmsc); - temp &= ~PORT_U2_TIMEOUT_MASK; - temp |= PORT_U2_TIMEOUT(timeout); - writel(temp, &port->port_reg->portpmsc); + + timeout = (wIndex & 0xff00) >> 8; + portpmsc = readl(&port->port_reg->portpmsc); + portpmsc &= ~PORT_U2_TIMEOUT_MASK; + portpmsc |= PORT_U2_TIMEOUT(timeout); + writel(portpmsc, &port->port_reg->portpmsc); break; case USB_PORT_FEAT_TEST: /* 4.19.6 Port Test Modes (USB2 Test Mode) */ if (hcd->speed != HCD_USB2) goto error; + + test_mode = (wIndex & 0xff00) >> 8; if (test_mode > USB_TEST_FORCE_ENABLE || test_mode < USB_TEST_J) goto error; - retval = xhci_enter_test_mode(xhci, test_mode, wIndex, - &flags); + retval = xhci_enter_test_mode(xhci, test_mode, portnum, &flags); break; default: goto error; } /* unblock any posted writes */ - temp = xhci_portsc_readl(port); + portsc = xhci_portsc_readl(port); break; case ClearPortFeature: - if (!portnum1 || portnum1 > max_ports) + portnum = (wIndex & 0xff) - 1; + if (!in_range(portnum, 0, max_ports)) goto error; - port = ports[portnum1 - 1]; - - wIndex--; - temp = xhci_portsc_readl(port); - if (temp == ~(u32)0) { + port = ports[portnum]; + portsc = xhci_portsc_readl(port); + if (portsc == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; break; } /* FIXME: What new port features do we need to support? */ - temp = xhci_port_state_to_neutral(temp); + portsc = xhci_port_state_to_neutral(portsc); switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = xhci_portsc_readl(port); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n"); - xhci_dbg(xhci, "PORTSC %04x\n", temp); - if (temp & PORT_RESET) + xhci_dbg(xhci, "PORTSC %04x\n", portsc); + if (portsc & PORT_RESET) goto error; - if ((temp & PORT_PLS_MASK) == XDEV_U3) { - if ((temp & PORT_PE) == 0) + if ((portsc & PORT_PLS_MASK) == XDEV_U3) { + if ((portsc & PORT_PE) == 0) goto error; - set_bit(wIndex, &bus_state->resuming_ports); - usb_hcd_start_port_resume(&hcd->self, wIndex); + set_bit(portnum, &bus_state->resuming_ports); + usb_hcd_start_port_resume(&hcd->self, portnum); xhci_set_link_state(xhci, port, XDEV_RESUME); spin_unlock_irqrestore(&xhci->lock, flags); msleep(USB_RESUME_TIMEOUT); spin_lock_irqsave(&xhci->lock, flags); xhci_set_link_state(xhci, port, XDEV_U0); - clear_bit(wIndex, &bus_state->resuming_ports); - usb_hcd_end_port_resume(&hcd->self, wIndex); + clear_bit(portnum, &bus_state->resuming_ports); + usb_hcd_end_port_resume(&hcd->self, portnum); } - bus_state->port_c_suspend |= 1 << wIndex; + bus_state->port_c_suspend |= 1 << portnum; if (!port->slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); @@ -1594,7 +1567,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_ring_device(xhci, port->slot_id); break; case USB_PORT_FEAT_C_SUSPEND: - bus_state->port_c_suspend &= ~(1 << wIndex); + bus_state->port_c_suspend &= ~(1 << portnum); fallthrough; case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_BH_PORT_RESET: @@ -1603,7 +1576,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_PORT_LINK_STATE: case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: - xhci_clear_port_change_bit(xhci, wValue, wIndex, port, temp); + xhci_clear_port_change_bit(xhci, wValue, port, portsc); break; case USB_PORT_FEAT_ENABLE: xhci_disable_port(xhci, port); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 75bc1eb98b76..997fe90f54e5 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -129,6 +129,13 @@ static void xhci_initialize_ring_segments(struct xhci_hcd *xhci, struct xhci_rin ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= cpu_to_le32(LINK_TOGGLE); } +void xhci_ring_init(struct xhci_hcd *xhci, struct xhci_ring *ring) +{ + xhci_initialize_ring_segments(xhci, ring); + xhci_initialize_ring_info(ring); + trace_xhci_ring_alloc(ring); +} + /* * Link the src ring segments to the dst ring. * Set Toggle Cycle for the new ring if needed. @@ -389,9 +396,6 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs, if (ret) goto fail; - xhci_initialize_ring_segments(xhci, ring); - xhci_initialize_ring_info(ring); - trace_xhci_ring_alloc(ring); return ring; fail: @@ -668,6 +672,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, cur_ring = stream_info->stream_rings[cur_stream]; if (!cur_ring) goto cleanup_rings; + + xhci_ring_init(xhci, cur_ring); cur_ring->stream_id = cur_stream; cur_ring->trb_address_map = &stream_info->trb_address_map; /* Set deq ptr, cycle bit, and stream context type */ @@ -930,7 +936,7 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, * that tt_info, then free the child first. Recursive. * We can't rely on udev at this point to find child-parent relationships. */ -static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) +void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) { struct xhci_virt_device *vdev; struct list_head *tt_list_head; @@ -988,14 +994,14 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, if (!dev->out_ctx) goto fail; - xhci_dbg(xhci, "Slot %d output ctx = 0x%pad (dma)\n", slot_id, &dev->out_ctx->dma); + xhci_dbg(xhci, "Slot %d output ctx = %pad (dma)\n", slot_id, &dev->out_ctx->dma); /* Allocate the (input) device context for address device command */ dev->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, flags); if (!dev->in_ctx) goto fail; - xhci_dbg(xhci, "Slot %d input ctx = 0x%pad (dma)\n", slot_id, &dev->in_ctx->dma); + xhci_dbg(xhci, "Slot %d input ctx = %pad (dma)\n", slot_id, &dev->in_ctx->dma); /* Initialize the cancellation and bandwidth list for each ep */ for (i = 0; i < 31; i++) { @@ -1011,6 +1017,8 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, if (!dev->eps[0].ring) goto fail; + xhci_ring_init(xhci, dev->eps[0].ring); + dev->udev = udev; /* Point to output device context in dcbaa. */ @@ -1492,6 +1500,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, virt_dev->eps[ep_index].skip = false; ep_ring = virt_dev->eps[ep_index].new_ring; + xhci_ring_init(xhci, ep_ring); /* Fill the endpoint context */ ep_ctx->ep_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) | @@ -1895,10 +1904,36 @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup } EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter); +/* Cleanup roothub bandwidth data */ +void xhci_rh_bw_cleanup(struct xhci_hcd *xhci) +{ + struct xhci_root_port_bw_info *rh_bw; + struct xhci_tt_bw_info *tt_info, *tt_next; + struct list_head *eps, *ep, *ep_next; + + for (int i = 0; i < xhci->max_ports; i++) { + rh_bw = &xhci->rh_bw[i]; + + /* Clear and free all TT bandwidth entries */ + list_for_each_entry_safe(tt_info, tt_next, &rh_bw->tts, tt_list) { + list_del(&tt_info->tt_list); + kfree(tt_info); + } + + /* Clear per-interval endpoint lists */ + for (int j = 0; j < XHCI_MAX_INTERVAL; j++) { + eps = &rh_bw->bw_table.interval_bw[j].endpoints; + + list_for_each_safe(ep, ep_next, eps) + list_del_init(ep); + } + } +} + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - int i, j; + int i; cancel_delayed_work_sync(&xhci->cmd_timer); @@ -1917,15 +1952,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring"); xhci_cleanup_command_queue(xhci); - for (i = 0; i < xhci->max_ports && xhci->rh_bw; i++) { - struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table; - for (j = 0; j < XHCI_MAX_INTERVAL; j++) { - struct list_head *ep = &bwt->interval_bw[j].endpoints; - while (!list_empty(ep)) - list_del_init(ep->next); - } - } - for (i = xhci->max_slots; i > 0; i--) xhci_free_virt_devices_depth_first(xhci, i); @@ -1959,18 +1985,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) scratchpad_free(xhci); - if (!xhci->rh_bw) - goto no_bw; + if (xhci->rh_bw) + xhci_rh_bw_cleanup(xhci); - for (i = 0; i < xhci->max_ports; i++) { - struct xhci_tt_bw_info *tt, *n; - list_for_each_entry_safe(tt, n, &xhci->rh_bw[i].tts, tt_list) { - list_del(&tt->tt_list); - kfree(tt); - } - } - -no_bw: xhci->cmd_ring_reserved_trbs = 0; xhci->usb2_rhub.num_ports = 0; xhci->usb3_rhub.num_ports = 0; @@ -1992,7 +2009,6 @@ no_bw: xhci->port_caps = NULL; xhci->interrupters = NULL; - xhci->page_size = 0; xhci->usb2_rhub.bus_state.bus_suspended = 0; xhci->usb3_rhub.bus_state.bus_suspended = 0; } @@ -2149,15 +2165,28 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, /* FIXME: Should we disable ports not in the Extended Capabilities? */ } -static void xhci_create_rhub_port_array(struct xhci_hcd *xhci, - struct xhci_hub *rhub, gfp_t flags) +static void xhci_create_rhub_port_array(struct xhci_hcd *xhci, struct xhci_hub *rhub, + unsigned int max_ports, gfp_t flags) { int port_index = 0; int i; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - if (!rhub->num_ports) + if (!rhub->num_ports) { + xhci_info(xhci, "USB%u root hub has no ports\n", rhub->maj_rev); return; + } + + /* + * Place limits on the number of roothub ports so that the hub + * descriptors aren't longer than the USB core will allocate. + */ + if (rhub->num_ports > max_ports) { + xhci->usb3_rhub.num_ports = max_ports; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Limiting USB%u root hub ports to %u", + rhub->maj_rev, max_ports); + } + rhub->ports = kcalloc_node(rhub->num_ports, sizeof(*rhub->ports), flags, dev_to_node(dev)); if (!rhub->ports) @@ -2253,30 +2282,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) "Found %u USB 2.0 ports and %u USB 3.0 ports.", xhci->usb2_rhub.num_ports, xhci->usb3_rhub.num_ports); - /* Place limits on the number of roothub ports so that the hub - * descriptors aren't longer than the USB core will allocate. - */ - if (xhci->usb3_rhub.num_ports > USB_SS_MAXPORTS) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Limiting USB 3.0 roothub ports to %u.", - USB_SS_MAXPORTS); - xhci->usb3_rhub.num_ports = USB_SS_MAXPORTS; - } - if (xhci->usb2_rhub.num_ports > USB_MAXCHILDREN) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Limiting USB 2.0 roothub ports to %u.", - USB_MAXCHILDREN); - xhci->usb2_rhub.num_ports = USB_MAXCHILDREN; - } - - if (!xhci->usb2_rhub.num_ports) - xhci_info(xhci, "USB2 root hub has no ports\n"); - - if (!xhci->usb3_rhub.num_ports) - xhci_info(xhci, "USB3 root hub has no ports\n"); - - xhci_create_rhub_port_array(xhci, &xhci->usb2_rhub, flags); - xhci_create_rhub_port_array(xhci, &xhci->usb3_rhub, flags); + xhci_create_rhub_port_array(xhci, &xhci->usb2_rhub, USB_MAXCHILDREN, flags); + xhci_create_rhub_port_array(xhci, &xhci->usb3_rhub, USB_SS_MAXPORTS, flags); return 0; } @@ -2362,6 +2369,8 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs, if (!ir) return NULL; + xhci_ring_init(xhci, ir->event_ring); + spin_lock_irq(&xhci->lock); if (!intr_num) { /* Find available secondary interrupter, interrupter 0 is reserved for primary */ @@ -2403,6 +2412,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) struct device *dev = xhci_to_hcd(xhci)->self.sysdev; dma_addr_t dma; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__); + /* * xHCI section 5.4.6 - Device Context array must be * "physically contiguous and 64-byte (cache line) aligned". @@ -2413,7 +2424,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci->dcbaa->dma = dma; xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Device context base array address = 0x%pad (DMA), %p (virt)", + "Device context base array address = %pad (DMA), %p (virt)", &xhci->dcbaa->dma, xhci->dcbaa); /* @@ -2474,15 +2485,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) goto fail; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocated command ring at %p", xhci->cmd_ring); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "First segment DMA is 0x%pad", - &xhci->cmd_ring->first_seg->dma); - - /* - * Reserve one command ring TRB for disabling LPM. - * Since the USB core grabs the shared usb_bus bandwidth mutex before - * disabling LPM, we only need to reserve one TRB for all devices. - */ - xhci->cmd_ring_reserved_trbs++; /* Allocate and set up primary interrupter 0 with an event ring. */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring"); @@ -2501,6 +2503,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) if (xhci_setup_port_arrays(xhci, flags)) goto fail; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished %s", __func__); return 0; fail: diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c index 257e4d79971f..f91c5004fade 100644 --- a/drivers/usb/host/xhci-mvebu.c +++ b/drivers/usb/host/xhci-mvebu.c @@ -30,7 +30,7 @@ static void xhci_mvebu_mbus_config(void __iomem *base, writel(0, base + USB3_WIN_BASE(win)); } - /* Program each DRAM CS in a seperate window */ + /* Program each DRAM CS in a separate window */ for (win = 0; win < dram->num_cs; win++) { const struct mbus_dram_window *cs = &dram->cs[win]; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 1cbefee3c4ca..e47e644b296e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -755,7 +755,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, } if ((ep->ep_state & SET_DEQ_PENDING)) { - xhci_warn(xhci, "Set TR Deq already pending, don't submit for 0x%pad\n", + xhci_warn(xhci, "Set TR Deq already pending, don't submit for %pad\n", &addr); return -EBUSY; } @@ -763,7 +763,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, /* This function gets called from contexts where it cannot sleep */ cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!cmd) { - xhci_warn(xhci, "Can't alloc Set TR Deq cmd 0x%pad\n", &addr); + xhci_warn(xhci, "Can't alloc Set TR Deq cmd %pad\n", &addr); return -ENOMEM; } @@ -3208,10 +3208,9 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) /* * Clear the op reg interrupt status first, * so we can receive interrupts from other MSI-X interrupters. - * Write 1 to clear the interrupt status. + * USBSTS bits are write 1 to clear. */ - status |= STS_EINT; - writel(status, &xhci->op_regs->status); + writel(STS_EINT, &xhci->op_regs->status); /* This is the handler of the primary interrupter */ xhci_handle_events(xhci, xhci->interrupters[0], false); diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 3f6aa2440b05..d2214d309e96 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1357,15 +1357,11 @@ static void tegra_xhci_id_work(struct work_struct *work) dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode)); - mutex_lock(&tegra->lock); - if (tegra->host_mode) phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST); else phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE); - mutex_unlock(&tegra->lock); - tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion(tegra->padctl, tegra->otg_usb2_port); @@ -2566,7 +2562,9 @@ static const struct tegra_xusb_soc tegra124_soc = { .smi_intr = XUSB_CFG_ARU_SMI_INTR, }, }; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) || IS_ENABLED(CONFIG_ARCH_TEGRA_132_SOC) MODULE_FIRMWARE("nvidia/tegra124/xusb.bin"); +#endif static const char * const tegra210_supply_names[] = { "dvddio-pex", @@ -2604,11 +2602,15 @@ static const struct tegra_xusb_soc tegra210_soc = { .smi_intr = XUSB_CFG_ARU_SMI_INTR, }, }; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) MODULE_FIRMWARE("nvidia/tegra210/xusb.bin"); +#endif static const char * const tegra186_supply_names[] = { }; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) MODULE_FIRMWARE("nvidia/tegra186/xusb.bin"); +#endif static const struct tegra_xusb_phy_type tegra186_phy_types[] = { { .name = "usb3", .num = 3, }, @@ -2681,7 +2683,9 @@ static const struct tegra_xusb_soc tegra194_soc = { }, .lpm_support = true, }; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) MODULE_FIRMWARE("nvidia/tegra194/xusb.bin"); +#endif static const struct tegra_xusb_soc_ops tegra234_ops = { .mbox_reg_readl = &bar2_readl, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index ef6d8662adec..a54f5b57f205 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -536,34 +536,24 @@ static void xhci_set_dev_notifications(struct xhci_hcd *xhci) writel(dev_notf, &xhci->op_regs->dev_notification); } -/* - * Initialize memory for HCD and xHC (one-time init). - * - * Program the PAGESIZE register, initialize the device context array, create - * device contexts (?), set up a command ring segment (or two?), create event - * ring (one for now). - */ -static int xhci_init(struct usb_hcd *hcd) +/* Setup basic xHCI registers */ +static void xhci_init(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - int retval; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__); - spin_lock_init(&xhci->lock); - - INIT_LIST_HEAD(&xhci->cmd_list); - INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout); - init_completion(&xhci->cmd_ring_stop_completion); - xhci_hcd_page_size(xhci); - memset(xhci->devs, 0, MAX_HC_SLOTS * sizeof(*xhci->devs)); - - retval = xhci_mem_init(xhci, GFP_KERNEL); - if (retval) - return retval; /* Set the Number of Device Slots Enabled to the maximum supported value */ xhci_enable_max_dev_slots(xhci); + /* Initialize the Command ring */ + xhci_ring_init(xhci, xhci->cmd_ring); + /* + * Reserve one command ring TRB for disabling LPM. + * Since the USB core grabs the shared usb_bus bandwidth mutex before + * disabling LPM, we only need to reserve one TRB for all devices. + */ + xhci->cmd_ring_reserved_trbs = 1; /* Set the address in the Command Ring Control register */ xhci_set_cmd_ring_deq(xhci); @@ -577,6 +567,7 @@ static int xhci_init(struct usb_hcd *hcd) xhci_set_dev_notifications(xhci); /* Initialize the Primary interrupter */ + xhci_ring_init(xhci, xhci->interrupters[0]->event_ring); xhci_add_interrupter(xhci, 0); xhci->interrupters[0]->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; @@ -587,7 +578,6 @@ static int xhci_init(struct usb_hcd *hcd) } xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished %s", __func__); - return 0; } /*-------------------------------------------------------------------------*/ @@ -967,11 +957,11 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci) */ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) { - int rc = 0; + int err; unsigned int delay = XHCI_MAX_HALT_USEC * 2; struct usb_hcd *hcd = xhci_to_hcd(xhci); u32 command; - u32 res; + u32 usbsts; if (!hcd->state) return 0; @@ -1017,11 +1007,10 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) /* Some chips from Fresco Logic need an extraordinary delay */ delay *= (xhci->quirks & XHCI_SLOW_SUSPEND) ? 10 : 1; - if (xhci_handshake(&xhci->op_regs->status, - STS_HALT, STS_HALT, delay)) { - xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n"); - spin_unlock_irq(&xhci->lock); - return -ETIMEDOUT; + err = xhci_handshake(&xhci->op_regs->status, STS_HALT, STS_HALT, delay); + if (err) { + xhci_warn(xhci, "Clearing Run/Stop bit failed %d\n", err); + goto handshake_error; } xhci_clear_command_ring(xhci); @@ -1032,28 +1021,34 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) command = readl(&xhci->op_regs->command); command |= CMD_CSS; writel(command, &xhci->op_regs->command); + + err = xhci_handshake(&xhci->op_regs->status, STS_SAVE, 0, 20 * USEC_PER_MSEC); + usbsts = readl(&xhci->op_regs->status); xhci->broken_suspend = 0; - if (xhci_handshake(&xhci->op_regs->status, - STS_SAVE, 0, 20 * 1000)) { - /* - * AMD SNPS xHC 3.0 occasionally does not clear the - * SSS bit of USBSTS and when driver tries to poll - * to see if the xHC clears BIT(8) which never happens - * and driver assumes that controller is not responding - * and times out. To workaround this, its good to check - * if SRE and HCE bits are not set (as per xhci - * Section 5.4.2) and bypass the timeout. - */ - res = readl(&xhci->op_regs->status); - if ((xhci->quirks & XHCI_SNPS_BROKEN_SUSPEND) && - (((res & STS_SRE) == 0) && - ((res & STS_HCE) == 0))) { - xhci->broken_suspend = 1; - } else { - xhci_warn(xhci, "WARN: xHC save state timeout\n"); - spin_unlock_irq(&xhci->lock); - return -ETIMEDOUT; + if (err) { + /* + * AMD SNPS xHC 3.0 occasionally does not clear the + * SSS bit of USBSTS and when driver tries to poll + * to see if the xHC clears BIT(8) which never happens + * and driver assumes that controller is not responding + * and times out. To workaround this, its good to check + * if SRE and HCE bits are not set (as per xhci + * Section 5.4.2) and bypass the timeout. + */ + if (!(xhci->quirks & XHCI_SNPS_BROKEN_SUSPEND)) { + xhci_warn(xhci, "Controller Save State failed %d\n", err); + goto handshake_error; } + + if (usbsts & (STS_SRE | STS_HCE)) { + xhci_warn(xhci, "Controller Save State failed, USBSTS 0x%08x\n", usbsts); + goto handshake_error; + } + + xhci_dbg(xhci, "SNPS broken suspend, save state unreliable\n"); + xhci->broken_suspend = 1; + } else if (usbsts & STS_SRE) { + xhci_warn(xhci, "Suspend Save Error (SRE), USBSTS 0x%08x\n", usbsts); } spin_unlock_irq(&xhci->lock); @@ -1069,7 +1064,11 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) __func__); } - return rc; + return 0; + +handshake_error: + spin_unlock_irq(&xhci->lock); + return -ETIMEDOUT; } EXPORT_SYMBOL_GPL(xhci_suspend); @@ -1083,10 +1082,11 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume) { u32 command, temp = 0; struct usb_hcd *hcd = xhci_to_hcd(xhci); + struct xhci_segment *seg; int retval = 0; - bool comp_timer_running = false; bool pending_portevent = false; bool suspended_usb3_devs = false; + bool reset_registers = false; if (!hcd->state) return 0; @@ -1105,10 +1105,11 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume) spin_lock_irq(&xhci->lock); - if (xhci->quirks & XHCI_RESET_ON_RESUME || xhci->broken_suspend) - power_lost = true; - - if (!power_lost) { + if (power_lost || xhci->broken_suspend || xhci->quirks & XHCI_RESET_ON_RESUME) { + xhci_dbg(xhci, "HC state lost, performing host controller reset\n"); + reset_registers = true; + } else { + xhci_dbg(xhci, "HC state intact, continuing without reset\n"); /* * Some controllers might lose power during suspend, so wait * for controller not ready bit to clear, just as in xHC init. @@ -1141,19 +1142,16 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume) spin_unlock_irq(&xhci->lock); return -ETIMEDOUT; } - } - temp = readl(&xhci->op_regs->status); - - /* re-initialize the HC on Restore Error, or Host Controller Error */ - if ((temp & (STS_SRE | STS_HCE)) && - !(xhci->xhc_state & XHCI_STATE_REMOVING)) { - if (!power_lost) + /* re-initialize the HC on Restore Error, or Host Controller Error */ + temp = readl(&xhci->op_regs->status); + if ((temp & (STS_SRE | STS_HCE)) && !(xhci->xhc_state & XHCI_STATE_REMOVING)) { xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp); - power_lost = true; + reset_registers = true; + } } - if (power_lost) { + if (reset_registers) { if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !(xhci_all_ports_seen_u0(xhci))) { timer_delete_sync(&xhci->comp_mode_recovery_timer); @@ -1177,27 +1175,33 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume) if (retval) return retval; - xhci_dbg(xhci, "// Disabling event ring interrupts\n"); - temp = readl(&xhci->op_regs->status); - writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - xhci_disable_interrupter(xhci, xhci->interrupters[0]); + cancel_delayed_work_sync(&xhci->cmd_timer); + + /* Delete all remaining commands */ + xhci_cleanup_command_queue(xhci); + + /* Clear data which is re-initilized during runtime */ + xhci_for_each_ring_seg(xhci->interrupters[0]->event_ring->first_seg, seg) + memset(seg->trbs, 0, sizeof(union xhci_trb) * TRBS_PER_SEGMENT); + + for (int i = xhci->max_slots; i > 0; i--) + xhci_free_virt_devices_depth_first(xhci, i); + + xhci_rh_bw_cleanup(xhci); + + xhci->cmd_ring_reserved_trbs = 0; + xhci_for_each_ring_seg(xhci->cmd_ring->first_seg, seg) + memset(seg->trbs, 0, sizeof(union xhci_trb) * TRBS_PER_SEGMENT); - xhci_dbg(xhci, "cleaning up memory\n"); - xhci_mem_cleanup(xhci); xhci_debugfs_exit(xhci); - xhci_dbg(xhci, "xhci_stop completed - status = %x\n", - readl(&xhci->op_regs->status)); - /* USB core calls the PCI reinit and start functions twice: + xhci_init(hcd); + + /* + * USB core calls the PCI reinit and start functions twice: * first with the primary HCD, and then with the secondary HCD. * If we don't do the same, the host will never be started. */ - xhci_dbg(xhci, "Initialize the xhci_hcd\n"); - retval = xhci_init(hcd); - if (retval) - return retval; - comp_timer_running = true; - xhci_dbg(xhci, "Start the primary HCD\n"); retval = xhci_run(hcd); if (!retval && xhci->shared_hcd) { @@ -1241,40 +1245,36 @@ int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume) xhci_dbc_resume(xhci); - if (retval == 0) { - /* - * Resume roothubs only if there are pending events. - * USB 3 devices resend U3 LFPS wake after a 100ms delay if - * the first wake signalling failed, give it that chance if - * there are suspended USB 3 devices. - */ - if (xhci->usb3_rhub.bus_state.suspended_ports || - xhci->usb3_rhub.bus_state.bus_suspended) - suspended_usb3_devs = true; + /* + * Resume roothubs only if there are pending events. + * USB 3 devices resend U3 LFPS wake after a 100ms delay if + * the first wake signalling failed, give it that chance if + * there are suspended USB 3 devices. + */ + if (xhci->usb3_rhub.bus_state.suspended_ports || xhci->usb3_rhub.bus_state.bus_suspended) + suspended_usb3_devs = true; + pending_portevent = xhci_pending_portevent(xhci); + if (suspended_usb3_devs && !pending_portevent && is_auto_resume) { + msleep(120); pending_portevent = xhci_pending_portevent(xhci); - - if (suspended_usb3_devs && !pending_portevent && is_auto_resume) { - msleep(120); - pending_portevent = xhci_pending_portevent(xhci); - } - - if (pending_portevent) { - if (xhci->shared_hcd) - usb_hcd_resume_root_hub(xhci->shared_hcd); - usb_hcd_resume_root_hub(hcd); - } } -done: + + if (pending_portevent) { + if (xhci->shared_hcd) + usb_hcd_resume_root_hub(xhci->shared_hcd); + usb_hcd_resume_root_hub(hcd); + } + /* * If system is subject to the Quirk, Compliance Mode Timer needs to * be re-initialized Always after a system resume. Ports are subject * to suffer the Compliance Mode issue again. It doesn't matter if * ports have entered previously to U0 before system's suspension. */ - if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !comp_timer_running) + if (xhci->quirks & XHCI_COMP_MODE_QUIRK) compliance_mode_recovery_timer_init(xhci); - +done: if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL) usb_asmedia_modifyflowcontrol(to_pci_dev(hcd->self.controller)); @@ -3201,7 +3201,12 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) } EXPORT_SYMBOL_GPL(xhci_reset_bandwidth); -/* Get the available bandwidth of the ports under the xhci roothub */ +/* + * Get the available bandwidth of the ports under the xhci roothub. + * EIO means the command failed: command not implemented or unsupported + * speed (TRB Error), some ASMedia complete with Parameter Error when + * querying the root hub (slot_id = 0), or other error or timeout. + */ int xhci_get_port_bandwidth(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, u8 dev_speed) { @@ -3230,6 +3235,8 @@ int xhci_get_port_bandwidth(struct xhci_hcd *xhci, struct xhci_container_ctx *ct spin_unlock_irqrestore(&xhci->lock, flags); wait_for_completion(cmd->completion); + if (cmd->status != COMP_SUCCESS) + ret = -EIO; err_out: kfree(cmd->completion); kfree(cmd); @@ -3285,7 +3292,6 @@ rescan: xhci_dbg(xhci, "endpoint disable with ep_state 0x%x\n", ep->ep_state); done: - host_ep->hcpriv = NULL; spin_unlock_irqrestore(&xhci->lock, flags); } @@ -5521,12 +5527,21 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); } - xhci_dbg(xhci, "Calling HCD init\n"); - /* Initialize HCD and host controller data structures. */ - retval = xhci_init(hcd); + spin_lock_init(&xhci->lock); + INIT_LIST_HEAD(&xhci->cmd_list); + INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout); + init_completion(&xhci->cmd_ring_stop_completion); + xhci_hcd_page_size(xhci); + + memset(xhci->devs, 0, MAX_HC_SLOTS * sizeof(*xhci->devs)); + + /* Allocate xHCI data structures */ + retval = xhci_mem_init(xhci, GFP_KERNEL); if (retval) return retval; - xhci_dbg(xhci, "Called HCD init\n"); + + /* Initialize HCD and host controller data structures */ + xhci_init(hcd); if (xhci_hcd_is_usb3(hcd)) xhci_hcd_init_usb3_data(xhci, hcd); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 2b0796f6d00e..aeecd301f207 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -12,6 +12,7 @@ #ifndef __LINUX_XHCI_HCD_H #define __LINUX_XHCI_HCD_H +#include #include #include #include @@ -125,17 +126,17 @@ struct xhci_op_regs { * PCI config regs). HC does NOT drive a USB reset on the downstream ports. * The xHCI driver must reinitialize the xHC after setting this bit. */ -#define CMD_RESET (1 << 1) +#define CMD_RESET BIT(1) /* Event Interrupt Enable - a '1' allows interrupts from the host controller */ #define CMD_EIE XHCI_CMD_EIE /* Host System Error Interrupt Enable - get out-of-band signal for HC errors */ #define CMD_HSEIE XHCI_CMD_HSEIE /* bits 4:6 are reserved (and should be preserved on writes). */ /* light reset (port status stays unchanged) - reset completed when this is 0 */ -#define CMD_LRESET (1 << 7) +#define CMD_LRESET BIT(7) /* host controller save/restore state. */ -#define CMD_CSS (1 << 8) -#define CMD_CRS (1 << 9) +#define CMD_CSS BIT(8) +#define CMD_CRS BIT(9) /* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */ #define CMD_EWE XHCI_CMD_EWE /* MFINDEX power management - '1' means xHC can stop MFINDEX counter if all root @@ -143,9 +144,9 @@ struct xhci_op_regs { * '0' means the xHC can power it off if all ports are in the disconnect, * disabled, or powered-off state. */ -#define CMD_PM_INDEX (1 << 11) +#define CMD_PM_INDEX BIT(11) /* bit 14 Extended TBC Enable, changes Isoc TRB fields to support larger TBC */ -#define CMD_ETE (1 << 14) +#define CMD_ETE BIT(14) /* bits 15:31 are reserved (and should be preserved on writes). */ #define XHCI_RESET_LONG_USEC (10 * 1000 * 1000) @@ -155,22 +156,22 @@ struct xhci_op_regs { /* HC not running - set to 1 when run/stop bit is cleared. */ #define STS_HALT XHCI_STS_HALT /* serious error, e.g. PCI parity error. The HC will clear the run/stop bit. */ -#define STS_FATAL (1 << 2) +#define STS_FATAL BIT(2) /* event interrupt - clear this prior to clearing any IP flags in IR set*/ -#define STS_EINT (1 << 3) +#define STS_EINT BIT(3) /* port change detect */ -#define STS_PORT (1 << 4) +#define STS_PORT BIT(4) /* bits 5:7 reserved and zeroed */ /* save state status - '1' means xHC is saving state */ -#define STS_SAVE (1 << 8) +#define STS_SAVE BIT(8) /* restore state status - '1' means xHC is restoring state */ -#define STS_RESTORE (1 << 9) +#define STS_RESTORE BIT(9) /* true: save or restore error */ -#define STS_SRE (1 << 10) +#define STS_SRE BIT(10) /* true: Controller Not Ready to accept doorbell or op reg writes after reset */ #define STS_CNR XHCI_STS_CNR /* true: internal Host Controller Error - SW needs to reset and reinitialize */ -#define STS_HCE (1 << 12) +#define STS_HCE BIT(12) /* bits 13:31 reserved and should be preserved */ /* @@ -182,17 +183,17 @@ struct xhci_op_regs { /* Most of the device notification types should only be used for debug. * SW does need to pay attention to function wake notifications. */ -#define DEV_NOTE_FWAKE (1 << 1) +#define DEV_NOTE_FWAKE BIT(1) /* CRCR - Command Ring Control Register - cmd_ring bitmasks */ /* bit 0 - Cycle bit indicates the ownership of the command ring */ -#define CMD_RING_CYCLE (1 << 0) +#define CMD_RING_CYCLE BIT(0) /* stop ring operation after completion of the currently executing command */ -#define CMD_RING_PAUSE (1 << 1) +#define CMD_RING_PAUSE BIT(1) /* stop ring immediately - abort the currently executing command */ -#define CMD_RING_ABORT (1 << 2) +#define CMD_RING_ABORT BIT(2) /* true: command ring is running */ -#define CMD_RING_RUNNING (1 << 3) +#define CMD_RING_RUNNING BIT(3) /* bits 63:6 - Command Ring pointer */ #define CMD_RING_PTR_MASK GENMASK_ULL(63, 6) @@ -200,9 +201,9 @@ struct xhci_op_regs { /* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */ #define MAX_DEVS(p) ((p) & 0xff) /* bit 8: U3 Entry Enabled, assert PLC when root port enters U3, xhci 1.1 */ -#define CONFIG_U3E (1 << 8) +#define CONFIG_U3E BIT(8) /* bit 9: Configuration Information Enable, xhci 1.1 */ -#define CONFIG_CIE (1 << 9) +#define CONFIG_CIE BIT(9) /* bits 10:31 - reserved and should be preserved */ /* bits 15:0 - HCD page shift bit */ @@ -235,9 +236,9 @@ struct xhci_intr_reg { /* iman bitmasks */ /* bit 0 - Interrupt Pending (IP), whether there is an interrupt pending. Write-1-to-clear. */ -#define IMAN_IP (1 << 0) +#define IMAN_IP BIT(0) /* bit 1 - Interrupt Enable (IE), whether the interrupter is capable of generating an interrupt */ -#define IMAN_IE (1 << 1) +#define IMAN_IE BIT(1) /* imod bitmasks */ /* @@ -267,7 +268,7 @@ struct xhci_intr_reg { * bit 3 - Event Handler Busy (EHB), whether the event ring is scheduled to be serviced by * a work queue (or delayed service routine)? */ -#define ERST_EHB (1 << 3) +#define ERST_EHB BIT(3) /* bits 63:4 - Event Ring Dequeue Pointer */ #define ERST_PTR_MASK GENMASK_ULL(63, 4) @@ -356,15 +357,15 @@ struct xhci_slot_ctx { #define GET_DEV_SPEED(n) (((n) & DEV_SPEED) >> 20) /* bit 24 reserved */ /* Is this LS/FS device connected through a HS hub? - bit 25 */ -#define DEV_MTT (0x1 << 25) +#define DEV_MTT BIT(25) /* Set if the device is a hub - bit 26 */ -#define DEV_HUB (0x1 << 26) +#define DEV_HUB BIT(26) /* Index of the last valid endpoint context in this device context - 27:31 */ #define LAST_CTX_MASK (0x1f << 27) #define LAST_CTX(p) ((p) << 27) #define LAST_CTX_TO_EP_NUM(p) (((p) >> 27) - 1) -#define SLOT_FLAG (1 << 0) -#define EP0_FLAG (1 << 1) +#define SLOT_FLAG BIT(0) +#define EP0_FLAG BIT(1) /* dev_info2 bitmasks */ /* Max Exit Latency (ms) - worst case time to wake up all links in dev path */ @@ -463,7 +464,7 @@ struct xhci_ep_ctx { #define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK) #define CTX_TO_EP_MAXPSTREAMS(p) (((p) & EP_MAXPSTREAMS_MASK) >> 10) /* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ -#define EP_HAS_LSA (1 << 15) +#define EP_HAS_LSA BIT(15) /* hosts with LEC=1 use bits 31:24 as ESIT high bits. */ #define CTX_TO_MAX_ESIT_PAYLOAD_HI(p) (((p) >> 24) & 0xff) @@ -498,7 +499,7 @@ struct xhci_ep_ctx { #define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff) /* deq bitmasks */ -#define EP_CTX_CYCLE_MASK (1 << 0) +#define EP_CTX_CYCLE_MASK BIT(0) /* bits 63:4 - TR Dequeue Pointer */ #define TR_DEQ_PTR_MASK GENMASK_ULL(63, 4) @@ -661,18 +662,18 @@ struct xhci_virt_ep { struct xhci_ring *new_ring; unsigned int err_count; unsigned int ep_state; -#define SET_DEQ_PENDING (1 << 0) -#define EP_HALTED (1 << 1) /* For stall handling */ -#define EP_STOP_CMD_PENDING (1 << 2) /* For URB cancellation */ +#define SET_DEQ_PENDING BIT(0) +#define EP_HALTED BIT(1) /* For stall handling */ +#define EP_STOP_CMD_PENDING BIT(2) /* For URB cancellation */ /* Transitioning the endpoint to using streams, don't enqueue URBs */ -#define EP_GETTING_STREAMS (1 << 3) -#define EP_HAS_STREAMS (1 << 4) +#define EP_GETTING_STREAMS BIT(3) +#define EP_HAS_STREAMS BIT(4) /* Transitioning the endpoint to not using streams, don't enqueue URBs */ -#define EP_GETTING_NO_STREAMS (1 << 5) -#define EP_HARD_CLEAR_TOGGLE (1 << 6) -#define EP_SOFT_CLEAR_TOGGLE (1 << 7) +#define EP_GETTING_NO_STREAMS BIT(5) +#define EP_HARD_CLEAR_TOGGLE BIT(6) +#define EP_SOFT_CLEAR_TOGGLE BIT(7) /* usb_hub_clear_tt_buffer is in progress */ -#define EP_CLEARING_TT (1 << 8) +#define EP_CLEARING_TT BIT(8) /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; struct xhci_hcd *xhci; @@ -954,7 +955,7 @@ struct xhci_link_trb { }; /* control bitfields */ -#define LINK_TOGGLE (0x1<<1) +#define LINK_TOGGLE BIT(1) /* Command completion event TRB */ struct xhci_event_cmd { @@ -968,13 +969,13 @@ struct xhci_event_cmd { #define COMP_PARAM(p) ((p) & 0xffffff) /* Command Completion Parameter */ /* Address device - disable SetAddress */ -#define TRB_BSR (1<<9) +#define TRB_BSR BIT(9) /* Configure Endpoint - Deconfigure */ -#define TRB_DC (1<<9) +#define TRB_DC BIT(9) /* Stop Ring - Transfer State Preserve */ -#define TRB_TSP (1<<9) +#define TRB_TSP BIT(9) enum xhci_ep_reset_type { EP_HARD_RESET, @@ -1017,13 +1018,13 @@ enum xhci_setup_dev { #define SCT_FOR_TRB(p) (((p) & 0x7) << 1) /* Link TRB specific fields */ -#define TRB_TC (1<<1) +#define TRB_TC BIT(1) /* Port Status Change Event TRB fields */ /* Port ID - bits 31:24 */ #define GET_PORT_ID(p) (((p) & (0xff << 24)) >> 24) -#define EVENT_DATA (1 << 2) +#define EVENT_DATA BIT(2) /* Normal TRB fields */ /* transfer_len bitmasks - bits 0:16 */ @@ -1038,36 +1039,36 @@ enum xhci_setup_dev { #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) /* Cycle bit - indicates TRB ownership by HC or HCD */ -#define TRB_CYCLE (1<<0) +#define TRB_CYCLE BIT(0) /* * Force next event data TRB to be evaluated before task switch. * Used to pass OS data back after a TD completes. */ -#define TRB_ENT (1<<1) +#define TRB_ENT BIT(1) /* Interrupt on short packet */ -#define TRB_ISP (1<<2) +#define TRB_ISP BIT(2) /* Set PCIe no snoop attribute */ -#define TRB_NO_SNOOP (1<<3) +#define TRB_NO_SNOOP BIT(3) /* Chain multiple TRBs into a TD */ -#define TRB_CHAIN (1<<4) +#define TRB_CHAIN BIT(4) /* Interrupt on completion */ -#define TRB_IOC (1<<5) +#define TRB_IOC BIT(5) /* The buffer pointer contains immediate data */ -#define TRB_IDT (1<<6) +#define TRB_IDT BIT(6) /* TDs smaller than this might use IDT */ #define TRB_IDT_MAX_SIZE 8 /* Block Event Interrupt */ -#define TRB_BEI (1<<9) +#define TRB_BEI BIT(9) /* Control transfer TRB specific fields */ -#define TRB_DIR_IN (1<<16) +#define TRB_DIR_IN BIT(16) #define TRB_TX_TYPE(p) ((p) << 16) #define TRB_DATA_OUT 2 #define TRB_DATA_IN 3 /* Isochronous TRB specific fields */ -#define TRB_SIA (1<<31) +#define TRB_SIA BIT(31) #define TRB_FRAME_ID(p) (((p) & 0x7ff) << 20) #define GET_FRAME_ID(p) (((p) >> 20) & 0x7ff) /* Total burst count field, Rsvdz on xhci 1.1 with Extended TBC enabled (ETE) */ @@ -1535,9 +1536,9 @@ struct xhci_hcd { struct xhci_interrupter **interrupters; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; -#define CMD_RING_STATE_RUNNING (1 << 0) -#define CMD_RING_STATE_ABORTED (1 << 1) -#define CMD_RING_STATE_STOPPED (1 << 2) +#define CMD_RING_STATE_RUNNING BIT(0) +#define CMD_RING_STATE_ABORTED BIT(1) +#define CMD_RING_STATE_STOPPED BIT(2) struct list_head cmd_list; unsigned int cmd_ring_reserved_trbs; struct delayed_work cmd_timer; @@ -1578,9 +1579,9 @@ struct xhci_hcd { * * There are no reports of xHCI host controllers that display this issue. */ -#define XHCI_STATE_DYING (1 << 0) -#define XHCI_STATE_HALTED (1 << 1) -#define XHCI_STATE_REMOVING (1 << 2) +#define XHCI_STATE_DYING BIT(0) +#define XHCI_STATE_HALTED BIT(1) +#define XHCI_STATE_REMOVING BIT(2) unsigned long long quirks; #define XHCI_LINK_TRB_QUIRK BIT_ULL(0) #define XHCI_RESET_EP_QUIRK BIT_ULL(1) /* Deprecated */ @@ -1792,6 +1793,7 @@ void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), void xhci_mem_cleanup(struct xhci_hcd *xhci); int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags); void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, int slot_id); +void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id); int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags); int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev); void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci, @@ -1803,6 +1805,7 @@ void xhci_update_tt_active_eps(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, int old_active_eps); void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info); +void xhci_rh_bw_cleanup(struct xhci_hcd *xhci); void xhci_update_bw_info(struct xhci_hcd *xhci, struct xhci_container_ctx *in_ctx, struct xhci_input_control_ctx *ctrl_ctx, @@ -1823,6 +1826,7 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring); int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, unsigned int num_trbs, gfp_t flags); void xhci_initialize_ring_info(struct xhci_ring *ring); +void xhci_ring_init(struct xhci_hcd *xhci, struct xhci_ring *ring); void xhci_free_endpoint_ring(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, unsigned int ep_index); diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 45dc209f5fe5..1f0b3c44d388 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -55,7 +55,6 @@ * Status: * * Untested with multiple scanners. - * Untested on SMP. * Untested on a bigendian machine. * * History: @@ -170,25 +169,13 @@ static struct usb_driver mts_usb_driver = { #define MTS_VERSION "0.4.3" #define MTS_NAME "microtek usb (rev " MTS_VERSION "): " -#define MTS_WARNING(x...) \ - printk( KERN_WARNING MTS_NAME x ) -#define MTS_ERROR(x...) \ - printk( KERN_ERR MTS_NAME x ) -#define MTS_INT_ERROR(x...) \ - MTS_ERROR(x) -#define MTS_MESSAGE(x...) \ - printk( KERN_INFO MTS_NAME x ) - #if defined MTS_DO_DEBUG #define MTS_DEBUG(x...) \ printk( KERN_DEBUG MTS_NAME x ) -#define MTS_DEBUG_GOT_HERE() \ - MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __func__ ) #define MTS_DEBUG_INT() \ - do { MTS_DEBUG_GOT_HERE(); \ - MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \ + do { MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \ MTS_DEBUG("status = 0x%x data-length = 0x%x sent = 0x%x\n",transfer->status,(int)context->data_length, (int)transfer->actual_length ); \ mts_debug_dump(context->instance);\ } while(0) @@ -197,7 +184,6 @@ static struct usb_driver mts_usb_driver = { #define MTS_NUL_STATEMENT do { } while(0) #define MTS_DEBUG(x...) MTS_NUL_STATEMENT -#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT #define MTS_DEBUG_INT() MTS_NUL_STATEMENT #endif @@ -316,7 +302,6 @@ static inline void mts_debug_dump(struct mts_desc* dummy) #endif static inline void mts_urb_abort(struct mts_desc* desc) { - MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); usb_kill_urb( desc->urb ); @@ -332,8 +317,6 @@ static int mts_scsi_abort(struct scsi_cmnd *srb) { struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); - MTS_DEBUG_GOT_HERE(); - mts_urb_abort(desc); return FAILED; @@ -344,7 +327,6 @@ static int mts_scsi_host_reset(struct scsi_cmnd *srb) struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); int result; - MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); result = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf); @@ -386,7 +368,8 @@ void mts_int_submit_urb (struct urb* transfer, res = usb_submit_urb( transfer, GFP_ATOMIC ); if ( unlikely(res) ) { - MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res ); + dev_err(&context->instance->usb_dev->dev, + "could not submit URB! Error was %d\n",(int)res ); set_host_byte(context->srb, DID_ERROR); mts_transfer_cleanup(transfer); } @@ -452,12 +435,9 @@ static void mts_command_done( struct urb *transfer ) if ( unlikely(status) ) { if (status == -ENOENT) { /* We are being killed */ - MTS_DEBUG_GOT_HERE(); set_host_byte(context->srb, DID_ABORT); } else { /* A genuine error has occurred */ - MTS_DEBUG_GOT_HERE(); - set_host_byte(context->srb, DID_ERROR); } mts_transfer_cleanup(transfer); @@ -523,8 +503,6 @@ mts_build_transfer_context(struct scsi_cmnd *srb, struct mts_desc* desc) { int pipe; - MTS_DEBUG_GOT_HERE(); - desc->context.instance = desc; desc->context.srb = srb; @@ -565,7 +543,6 @@ static enum scsi_qc_status mts_scsi_queuecommand_lck(struct scsi_cmnd *srb) struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); int res; - MTS_DEBUG_GOT_HERE(); mts_show_command(srb); mts_debug_dump(desc); @@ -601,7 +578,7 @@ static enum scsi_qc_status mts_scsi_queuecommand_lck(struct scsi_cmnd *srb) res=usb_submit_urb(desc->urb, GFP_ATOMIC); if(unlikely(res)){ - MTS_ERROR("error %d submitting URB\n",(int)res); + dev_err(&desc->usb_dev->dev, "error %d submitting URB\n",(int)res); set_host_byte(srb, DID_ERROR); if(likely(callback != NULL)) @@ -666,59 +643,47 @@ static int mts_usb_probe(struct usb_interface *intf, /* the current altsetting on the interface we're probing */ struct usb_host_interface *altsetting; - MTS_DEBUG_GOT_HERE(); MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev ); MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n", le16_to_cpu(dev->descriptor.idProduct), le16_to_cpu(dev->descriptor.idVendor) ); - MTS_DEBUG_GOT_HERE(); - /* the current altsetting on the interface we're probing */ altsetting = intf->cur_altsetting; - /* Check if the config is sane */ if ( altsetting->desc.bNumEndpoints != MTS_EP_TOTAL ) { - MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n", + dev_warn(&dev->dev, "expecting %d got %d endpoints! Bailing out.\n", (int)MTS_EP_TOTAL, (int)altsetting->desc.bNumEndpoints ); return -ENODEV; } for( i = 0; i < altsetting->desc.bNumEndpoints; i++ ) { - if ((altsetting->endpoint[i].desc.bmAttributes & - USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) { - - MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n", - (int)altsetting->endpoint[i].desc.bEndpointAddress ); - } else { - if (altsetting->endpoint[i].desc.bEndpointAddress & - USB_DIR_IN) - *ep_in_current++ - = altsetting->endpoint[i].desc.bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK; - else { - if ( ep_out != -1 ) { - MTS_WARNING( "can only deal with one output endpoints. Bailing out." ); - return -ENODEV; - } - - ep_out = altsetting->endpoint[i].desc.bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK; + if (usb_endpoint_is_bulk_in(&altsetting->endpoint[i].desc)) { + *ep_in_current++ = usb_endpoint_num(&altsetting->endpoint[i].desc); + } else if (usb_endpoint_is_bulk_out(&altsetting->endpoint[i].desc)) { + if (ep_out == -1) { + ep_out = usb_endpoint_num(&altsetting->endpoint[i].desc); + } else { + dev_warn(&dev->dev, "can only deal with bulk endpoints; endpoint %d is not bulk.\n", + usb_endpoint_num(&altsetting->endpoint[i].desc)); + return -ENODEV; } + } else { + dev_warn(&dev->dev, "can only deal with bulk endpoints; endpoint %d is not bulk.\n", + usb_endpoint_num(&altsetting->endpoint[i].desc)); } - } if (ep_in_current != &ep_in_set[2]) { - MTS_WARNING("couldn't find two input bulk endpoints. Bailing out.\n"); + dev_warn(&dev->dev, "couldn't find two input bulk endpoints. Bailing out.\n"); return -ENODEV; } if ( ep_out == -1 ) { - MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" ); + dev_warn(&dev->dev, "couldn't find an output bulk endpoint. Bailing out.\n" ); return -ENODEV; } @@ -744,15 +709,15 @@ static int mts_usb_probe(struct usb_interface *intf, new_desc->ep_image = ep_in_set[1]; if ( new_desc->ep_out != MTS_EP_OUT ) - MTS_WARNING( "will this work? Command EP is not usually %d\n", + dev_warn(&dev->dev, "will this work? Command EP is not usually %d\n", (int)new_desc->ep_out ); if ( new_desc->ep_response != MTS_EP_RESPONSE ) - MTS_WARNING( "will this work? Response EP is not usually %d\n", + dev_warn(&dev->dev, "will this work? Response EP is not usually %d\n", (int)new_desc->ep_response ); if ( new_desc->ep_image != MTS_EP_IMAGE ) - MTS_WARNING( "will this work? Image data EP is not usually %d\n", + dev_warn(&dev->dev, "will this work? Image data EP is not usually %d\n", (int)new_desc->ep_image ); new_desc->host = scsi_host_alloc(&mts_scsi_host_template, diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c index 339f6cd2e9b2..af266e19f2fd 100644 --- a/drivers/usb/misc/apple-mfi-fastcharge.c +++ b/drivers/usb/misc/apple-mfi-fastcharge.c @@ -210,7 +210,7 @@ static int mfi_fc_probe(struct usb_device *udev) goto err_free_name; } - mfi->udev = usb_get_dev(udev); + mfi->udev = udev; dev_set_drvdata(&udev->dev, mfi); return 0; @@ -231,7 +231,6 @@ static void mfi_fc_disconnect(struct usb_device *udev) power_supply_unregister(mfi->battery); kfree(mfi->battery_desc.name); dev_set_drvdata(&udev->dev, NULL); - usb_put_dev(mfi->udev); kfree(mfi); } diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index 4beebde59842..16883592f7fc 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -20,9 +21,6 @@ #define APPLE_VENDOR_ID 0x05AC -#define USB_REQ_GET_REPORT 0x01 -#define USB_REQ_SET_REPORT 0x09 - #define ACD_USB_TIMEOUT 250 #define ACD_USB_EDID 0x0302 @@ -140,7 +138,7 @@ static int appledisplay_bl_update_status(struct backlight_device *bd) retval = usb_control_msg( pdata->udev, usb_sndctrlpipe(pdata->udev, 0), - USB_REQ_SET_REPORT, + HID_REQ_SET_REPORT, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, ACD_USB_BRIGHTNESS, 0, @@ -163,7 +161,7 @@ static int appledisplay_bl_get_brightness(struct backlight_device *bd) retval = usb_control_msg( pdata->udev, usb_rcvctrlpipe(pdata->udev, 0), - USB_REQ_GET_REPORT, + HID_REQ_GET_REPORT, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, ACD_USB_BRIGHTNESS, 0, diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index 99185fc3e9df..4a7f955ba85b 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -215,7 +215,7 @@ static int cypress_probe(struct usb_interface *interface, if (!dev) goto error_mem; - dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->udev = interface_to_usbdev(interface); /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); @@ -239,8 +239,6 @@ static void cypress_disconnect(struct usb_interface *interface) * device files have been removed */ usb_set_intfdata(interface, NULL); - usb_put_dev(dev->udev); - dev_info(&interface->dev, "Cypress CY7C63xxx device now disconnected\n"); diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index 2bf082474e9d..b183df9826bc 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c @@ -311,7 +311,7 @@ static int cytherm_probe(struct usb_interface *interface, if (!dev) goto error_mem; - dev->udev = usb_get_dev(udev); + dev->udev = udev; usb_set_intfdata(interface, dev); @@ -334,8 +334,6 @@ static void cytherm_disconnect(struct usb_interface *interface) /* first remove the files, then NULL the pointer */ usb_set_intfdata(interface, NULL); - usb_put_dev(dev->udev); - kfree(dev); dev_info(&interface->dev, "Cypress thermometer now disconnected\n"); diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 18670dfed2e7..22504c0a2841 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #define DRIVER_AUTHOR "Christian Lucht " @@ -74,7 +75,6 @@ struct iowarrior { struct mutex mutex; /* locks this structure */ struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface; /* the interface for this device */ - unsigned char minor; /* the starting minor number for this device */ struct usb_endpoint_descriptor *int_out_endpoint; /* endpoint for reading (needed for IOW56 only) */ struct usb_endpoint_descriptor *int_in_endpoint; /* endpoint for reading */ struct urb *int_in_urb; /* the urb for reading data */ @@ -99,14 +99,13 @@ struct iowarrior { /* globals */ /*--------------*/ -#define USB_REQ_GET_REPORT 0x01 //#if 0 static int usb_get_report(struct usb_device *dev, struct usb_host_interface *inter, unsigned char type, unsigned char id, void *buf, int size) { return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_REPORT, + HID_REQ_GET_REPORT, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, (type << 8) + id, inter->desc.bInterfaceNumber, buf, size, @@ -114,14 +113,12 @@ static int usb_get_report(struct usb_device *dev, } //#endif -#define USB_REQ_SET_REPORT 0x09 - static int usb_set_report(struct usb_interface *intf, unsigned char type, unsigned char id, void *buf, int size) { return usb_control_msg(interface_to_usbdev(intf), usb_sndctrlpipe(interface_to_usbdev(intf), 0), - USB_REQ_SET_REPORT, + HID_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (type << 8) + id, intf->cur_altsetting->desc.bInterfaceNumber, buf, @@ -234,8 +231,7 @@ static void iowarrior_write_callback(struct urb *urb) "nonzero write bulk status received: %d\n", status); } /* free up our allocated buffer */ - usb_free_coherent(urb->dev, urb->transfer_buffer_length, - urb->transfer_buffer, urb->transfer_dma); + kfree(urb->transfer_buffer); /* tell a waiting writer the interrupt-out-pipe is available again */ atomic_dec(&dev->write_busy); wake_up_interruptible(&dev->write_wait); @@ -246,7 +242,6 @@ static void iowarrior_write_callback(struct urb *urb) */ static inline void iowarrior_delete(struct iowarrior *dev) { - dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor); kfree(dev->int_in_buffer); usb_free_urb(dev->int_in_urb); kfree(dev->read_queue); @@ -297,9 +292,6 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer, goto exit; } - dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n", - dev->minor, count); - /* read count must be packet size (+ time stamp) */ if ((count != dev->report_size) && (count != (dev->report_size + 1))) { @@ -367,20 +359,21 @@ static ssize_t iowarrior_write(struct file *file, size_t count, loff_t *ppos) { struct iowarrior *dev; - int retval = 0; + int retval; char *buf = NULL; /* for IOW24 and IOW56 we need a buffer */ struct urb *int_out_urb = NULL; dev = file->private_data; - mutex_lock(&dev->mutex); + retval = mutex_lock_interruptible(&dev->mutex); + if (retval < 0) + return -EINTR; + /* verify that the device wasn't unplugged */ if (!dev->present) { retval = -ENODEV; goto exit; } - dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n", - dev->minor, count); /* if count is 0 we're already done */ if (count == 0) { retval = 0; @@ -443,8 +436,7 @@ static ssize_t iowarrior_write(struct file *file, retval = -ENOMEM; goto error_no_urb; } - buf = usb_alloc_coherent(dev->udev, dev->report_size, - GFP_KERNEL, &int_out_urb->transfer_dma); + buf = kmalloc(dev->report_size, GFP_KERNEL); if (!buf) { retval = -ENOMEM; dev_dbg(&dev->interface->dev, @@ -457,7 +449,6 @@ static ssize_t iowarrior_write(struct file *file, buf, dev->report_size, iowarrior_write_callback, dev, dev->int_out_endpoint->bInterval); - int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; if (copy_from_user(buf, user_buffer, count)) { retval = -EFAULT; goto error; @@ -483,8 +474,7 @@ static ssize_t iowarrior_write(struct file *file, goto exit; } error: - usb_free_coherent(dev->udev, dev->report_size, buf, - int_out_urb->transfer_dma); + kfree(buf); error_no_buffer: usb_free_urb(int_out_urb); error_no_urb: @@ -523,9 +513,6 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd, goto error_out; } - dev_dbg(&dev->interface->dev, "minor %d, cmd 0x%.4x, arg %ld\n", - dev->minor, cmd, arg); - retval = 0; switch (cmd) { case IOW_WRITE: @@ -671,8 +658,6 @@ static int iowarrior_release(struct inode *inode, struct file *file) if (!dev) return -ENODEV; - dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor); - /* lock our device */ mutex_lock(&dev->mutex); @@ -775,6 +760,7 @@ static int iowarrior_probe(struct usb_interface *interface, struct usb_host_interface *iface_desc; int retval = -ENOMEM; int res; + int minor; /* allocate memory for our device state and initialize it */ dev = kzalloc_obj(struct iowarrior); @@ -890,12 +876,12 @@ static int iowarrior_probe(struct usb_interface *interface, goto error; } - dev->minor = interface->minor; + minor = interface->minor; /* let the user know what node this device is now attached to */ dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d " "now attached to iowarrior%d\n", dev->product_id, dev->chip_serial, - iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE); + iface_desc->desc.bInterfaceNumber, minor - IOWARRIOR_MINOR_BASE); return retval; error: diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c index ba37eb99efba..7cdbdfe07a76 100644 --- a/drivers/usb/misc/onboard_usb_dev.c +++ b/drivers/usb/misc/onboard_usb_dev.c @@ -565,11 +565,13 @@ static struct platform_driver onboard_dev_driver = { /************************** USB driver **************************/ #define VENDOR_ID_BISON 0x5986 +#define VENDOR_ID_CORECHIPS 0x3431 #define VENDOR_ID_CYPRESS 0x04b4 #define VENDOR_ID_GENESYS 0x05e3 #define VENDOR_ID_MICROCHIP 0x0424 #define VENDOR_ID_PARADE 0x1da0 #define VENDOR_ID_REALTEK 0x0bda +#define VENDOR_ID_TERMINUS 0x1a40 #define VENDOR_ID_TI 0x0451 #define VENDOR_ID_VIA 0x2109 #define VENDOR_ID_XMOS 0x20B1 @@ -649,6 +651,8 @@ static void onboard_dev_usbdev_disconnect(struct usb_device *udev) static const struct usb_device_id onboard_dev_id_table[] = { { USB_DEVICE(VENDOR_ID_BISON, 0x1198) }, /* Bison Electronics Inc. Integrated Camera */ + { USB_DEVICE(VENDOR_ID_CORECHIPS, 0x6241) }, /* SL6341 2.0 HUB */ + { USB_DEVICE(VENDOR_ID_CORECHIPS, 0x6341) }, /* SL6341 3.0 HUB */ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6500) }, /* CYUSB330x 3.0 HUB */ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6502) }, /* CYUSB330x 2.0 HUB */ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6503) }, /* CYUSB33{0,1}x 2.0 HUB, Vendor Mode */ @@ -673,6 +677,7 @@ static const struct usb_device_id onboard_dev_id_table[] = { { USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 HUB */ { USB_DEVICE(VENDOR_ID_REALTEK, 0x5414) }, /* RTS5414 USB 2.1 HUB */ { USB_DEVICE(VENDOR_ID_REALTEK, 0x0179) }, /* RTL8188ETV 2.4GHz WiFi */ + { USB_DEVICE(VENDOR_ID_TERMINUS, 0x0101) }, /* Terminus FE1.1s 2.0 HUB */ { USB_DEVICE(VENDOR_ID_TI, 0x8025) }, /* TI USB8020B 3.0 HUB */ { USB_DEVICE(VENDOR_ID_TI, 0x8027) }, /* TI USB8020B 2.0 HUB */ { USB_DEVICE(VENDOR_ID_TI, 0x8140) }, /* TI USB8041 3.0 HUB */ diff --git a/drivers/usb/misc/onboard_usb_dev.h b/drivers/usb/misc/onboard_usb_dev.h index 1a1e86e60e04..ac1aa3e122ad 100644 --- a/drivers/usb/misc/onboard_usb_dev.h +++ b/drivers/usb/misc/onboard_usb_dev.h @@ -66,13 +66,6 @@ static const struct onboard_dev_pdata ti_tusb8020b_data = { .is_hub = true, }; -static const struct onboard_dev_pdata ti_tusb8041_data = { - .reset_us = 3000, - .num_supplies = 1, - .supply_names = { "vdd" }, - .is_hub = true, -}; - static const struct onboard_dev_pdata bison_intcamera_data = { .reset_us = 1000, .num_supplies = 1, @@ -80,6 +73,13 @@ static const struct onboard_dev_pdata bison_intcamera_data = { .is_hub = false, }; +static const struct onboard_dev_pdata corechips_sl6341_data = { + .reset_us = 10000, + .num_supplies = 2, + .supply_names = { "vdd1v1", "vdd3v3" }, + .is_hub = true, +}; + static const struct onboard_dev_pdata cypress_hx3_data = { .reset_us = 10000, .num_supplies = 2, @@ -87,13 +87,6 @@ static const struct onboard_dev_pdata cypress_hx3_data = { .is_hub = true, }; -static const struct onboard_dev_pdata cypress_hx2vl_data = { - .reset_us = 1, - .num_supplies = 1, - .supply_names = { "vdd" }, - .is_hub = true, -}; - static const struct onboard_dev_pdata genesys_gl850g_data = { .reset_us = 3, .num_supplies = 1, @@ -108,6 +101,11 @@ static const struct onboard_dev_pdata genesys_gl852g_data = { .is_hub = true, }; +static const struct onboard_dev_pdata usb_a_conn_data = { + .num_supplies = 1, + .supply_names = { "vbus" }, +}; + static const struct onboard_dev_pdata vialab_vl817_data = { .reset_us = 10, .num_supplies = 1, @@ -130,6 +128,7 @@ static const struct onboard_dev_pdata xmos_xvf3500_data = { }; static const struct of_device_id onboard_dev_match[] = { + { .compatible = "usb-a-connector", .data = &usb_a_conn_data, }, { .compatible = "usb424,2412", .data = µchip_usb424_data, }, { .compatible = "usb424,2514", .data = µchip_usb2514_data, }, { .compatible = "usb424,2517", .data = µchip_usb424_data, }, @@ -137,13 +136,13 @@ static const struct of_device_id onboard_dev_match[] = { { .compatible = "usb424,5744", .data = µchip_usb5744_data, }, { .compatible = "usb451,8025", .data = &ti_tusb8020b_data, }, { .compatible = "usb451,8027", .data = &ti_tusb8020b_data, }, - { .compatible = "usb451,8140", .data = &ti_tusb8041_data, }, - { .compatible = "usb451,8142", .data = &ti_tusb8041_data, }, - { .compatible = "usb451,8440", .data = &ti_tusb8041_data, }, - { .compatible = "usb451,8442", .data = &ti_tusb8041_data, }, + { .compatible = "usb451,8140", .data = &ti_tusb8020b_data, }, + { .compatible = "usb451,8142", .data = &ti_tusb8020b_data, }, + { .compatible = "usb451,8440", .data = &ti_tusb8020b_data, }, + { .compatible = "usb451,8442", .data = &ti_tusb8020b_data, }, { .compatible = "usb4b4,6504", .data = &cypress_hx3_data, }, { .compatible = "usb4b4,6506", .data = &cypress_hx3_data, }, - { .compatible = "usb4b4,6570", .data = &cypress_hx2vl_data, }, + { .compatible = "usb4b4,6570", .data = µchip_usb424_data, }, { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, }, { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, }, { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, }, @@ -153,12 +152,15 @@ static const struct of_device_id onboard_dev_match[] = { { .compatible = "usbbda,5411", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,414", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,5414", .data = &realtek_rts5411_data, }, + { .compatible = "usb1a40,0101", .data = &vialab_vl817_data, }, { .compatible = "usb1a86,8091", .data = &wch_ch334_data, }, { .compatible = "usb1da0,5511", .data = ¶de_ps5511_data, }, { .compatible = "usb1da0,55a1", .data = ¶de_ps5511_data, }, { .compatible = "usb2109,817", .data = &vialab_vl817_data, }, { .compatible = "usb2109,2817", .data = &vialab_vl817_data, }, { .compatible = "usb20b1,0013", .data = &xmos_xvf3500_data, }, + { .compatible = "usb3431,6241", .data = &corechips_sl6341_data, }, + { .compatible = "usb3431,6341", .data = &corechips_sl6341_data, }, { .compatible = "usb5986,1198", .data = &bison_intcamera_data, }, {} }; diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c index 6aaec2db360b..37f6b79889a6 100644 --- a/drivers/usb/misc/trancevibrator.c +++ b/drivers/usb/misc/trancevibrator.c @@ -92,7 +92,7 @@ static int tv_probe(struct usb_interface *interface, goto error; } - dev->udev = usb_get_dev(udev); + dev->udev = udev; usb_set_intfdata(interface, dev); return 0; @@ -108,7 +108,6 @@ static void tv_disconnect(struct usb_interface *interface) dev = usb_get_intfdata (interface); usb_set_intfdata(interface, NULL); - usb_put_dev(dev->udev); kfree(dev); } diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c index 7e85fd12da56..c60121faa3da 100644 --- a/drivers/usb/misc/usb-ljca.c +++ b/drivers/usb/misc/usb-ljca.c @@ -776,7 +776,7 @@ static int ljca_probe(struct usb_interface *interface, init_completion(&adap->cmd_completion); INIT_LIST_HEAD(&adap->client_list); - adap->intf = usb_get_intf(interface); + adap->intf = interface; adap->usb_dev = usb_dev; adap->dev = dev; @@ -787,7 +787,7 @@ static int ljca_probe(struct usb_interface *interface, ret = usb_find_common_endpoints(alt, &ep_in, &ep_out, NULL, NULL); if (ret) { dev_err(dev, "bulk endpoints not found\n"); - goto err_put; + goto err_destroy_mutex; } adap->rx_pipe = usb_rcvbulkpipe(usb_dev, usb_endpoint_num(ep_in)); adap->tx_pipe = usb_sndbulkpipe(usb_dev, usb_endpoint_num(ep_out)); @@ -797,14 +797,14 @@ static int ljca_probe(struct usb_interface *interface, adap->rx_buf = devm_kzalloc(dev, adap->rx_len, GFP_KERNEL); if (!adap->rx_buf) { ret = -ENOMEM; - goto err_put; + goto err_destroy_mutex; } /* alloc rx urb */ adap->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!adap->rx_urb) { ret = -ENOMEM; - goto err_put; + goto err_destroy_mutex; } usb_fill_bulk_urb(adap->rx_urb, usb_dev, adap->rx_pipe, adap->rx_buf, adap->rx_len, ljca_recv, adap); @@ -836,10 +836,7 @@ static int ljca_probe(struct usb_interface *interface, err_free: usb_free_urb(adap->rx_urb); - -err_put: - usb_put_intf(adap->intf); - +err_destroy_mutex: mutex_destroy(&adap->mutex); return ret; @@ -864,8 +861,6 @@ static void ljca_disconnect(struct usb_interface *interface) usb_free_urb(adap->rx_urb); - usb_put_intf(adap->intf); - mutex_destroy(&adap->mutex); } diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index b37bf53dd54f..89d25fcef642 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c @@ -312,7 +312,7 @@ static int sevseg_probe(struct usb_interface *interface, if (!mydev) goto error_mem; - mydev->udev = usb_get_dev(udev); + mydev->udev = udev; mydev->intf = interface; usb_set_intfdata(interface, mydev); @@ -338,7 +338,6 @@ static void sevseg_disconnect(struct usb_interface *interface) mydev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); - usb_put_dev(mydev->udev); kfree(mydev); dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); } diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index a8af7615b1bf..b7d3c44b970e 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -677,35 +677,32 @@ static int uss720_probe(struct usb_interface *intf, struct parport_uss720_private *priv; struct parport *pp; unsigned char reg; - int ret; + int ret = -ENODEV; dev_dbg(&intf->dev, "probe: vendor id 0x%x, device id 0x%x\n", le16_to_cpu(usbdev->descriptor.idVendor), le16_to_cpu(usbdev->descriptor.idProduct)); /* our known interfaces have 3 alternate settings */ - if (intf->num_altsetting != 3) { - usb_put_dev(usbdev); - return -ENODEV; - } + if (intf->num_altsetting != 3) + goto bail_out_early; + ret = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2); dev_dbg(&intf->dev, "set interface result %d\n", ret); interface = intf->cur_altsetting; - if (interface->desc.bNumEndpoints < 2) { - usb_put_dev(usbdev); - return -ENODEV; - } + if (interface->desc.bNumEndpoints < 2) + goto bail_out_early; /* * Allocate parport interface */ + ret = -ENOMEM; priv = kzalloc_obj(struct parport_uss720_private); - if (!priv) { - usb_put_dev(usbdev); - return -ENOMEM; - } + if (!priv) + goto bail_out_early; + priv->pp = NULL; priv->usbdev = usbdev; kref_init(&priv->ref_count); @@ -752,6 +749,10 @@ probe_abort: kill_all_async_requests_priv(priv); kref_put(&priv->ref_count, destroy_priv); return -ENODEV; + +bail_out_early: + usb_put_dev(usbdev); + return ret; } static void uss720_disconnect(struct usb_interface *intf) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 0acc62569ae5..73ac25f53698 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1600,7 +1600,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb) /* log core options (read using indexed model) */ reg = musb_read_configdata(mbase); - strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8"); + strscpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8"); if (reg & MUSB_CONFIGDATA_DYNFIFO) { strcat(aInfo, ", dyn FIFOs"); musb->dyn_fifo = true; diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 1539e8e6901d..0c7fe109d5c7 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -40,7 +40,6 @@ struct usbhsg_gpriv { struct usb_gadget gadget; struct usbhs_mod mod; - struct usbhsg_uep *uep; int uep_size; struct usb_gadget_driver *driver; @@ -53,6 +52,7 @@ struct usbhsg_gpriv { #define USBHSG_STATUS_WEDGE (1 << 2) #define USBHSG_STATUS_SELF_POWERED (1 << 3) #define USBHSG_STATUS_SOFT_CONNECT (1 << 4) + struct usbhsg_uep uep[] __counted_by(uep_size); }; struct usbhsg_recip_handle { @@ -1084,15 +1084,11 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) int i; int ret; - gpriv = kzalloc_obj(struct usbhsg_gpriv); + gpriv = kzalloc_flex(*gpriv, uep, pipe_size); if (!gpriv) return -ENOMEM; - uep = kzalloc_objs(struct usbhsg_uep, pipe_size); - if (!uep) { - ret = -ENOMEM; - goto usbhs_mod_gadget_probe_err_gpriv; - } + gpriv->uep_size = pipe_size; gpriv->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_UNDEFINED); dev_info(dev, "%stransceiver found\n", @@ -1115,8 +1111,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) gpriv->mod.name = "gadget"; gpriv->mod.start = usbhsg_start; gpriv->mod.stop = usbhsg_stop; - gpriv->uep = uep; - gpriv->uep_size = pipe_size; usbhsg_status_init(gpriv); /* @@ -1175,9 +1169,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) return 0; err_add_udc: - kfree(gpriv->uep); - -usbhs_mod_gadget_probe_err_gpriv: kfree(gpriv); return ret; @@ -1189,6 +1180,5 @@ void usbhs_mod_gadget_remove(struct usbhs_priv *priv) usb_del_gadget_udc(&gpriv->gadget); - kfree(gpriv->uep); kfree(gpriv); } diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index f8d6aa30a3e1..0ca111b111c7 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -6,7 +6,7 @@ * Copyright (C) 2007 Alain Degreffe (eczema@ecze.com) * - * Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás) + * Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás) * * And tested with help of WB Electronics */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 313612114db9..42e4cecd28ac 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1383,6 +1383,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = NCTRL(2) | RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1073, 0xff), /* Telit FN990A (ECM) */ .driver_info = NCTRL(0) | RSVD(1) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1074, 0xff), /* Telit FN990A (MBIM) */ + .driver_info = NCTRL(5) | RSVD(6) | RSVD(7) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff), /* Telit FN990A (PCIe) */ .driver_info = RSVD(0) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1077, 0xff), /* Telit FN990A (rmnet + audio) */ @@ -1511,7 +1513,11 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1231, 0xff), /* Telit LE910Cx (RNDIS) */ .driver_info = NCTRL(2) | RSVD(3) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x1250, 0xff, 0x00, 0x00) }, /* Telit LE910Cx (rmnet) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1251, 0xff) }, /* Telit LE910Cx (RNDIS) */ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1252, 0xff) }, /* Telit LE910Cx (MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1253, 0xff) }, /* Telit LE910Cx (ECM) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1254, 0xff) }, /* Telit LE910Cx */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1255, 0xff) }, /* Telit LE910Cx */ { USB_DEVICE(TELIT_VENDOR_ID, 0x1260), .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, { USB_DEVICE(TELIT_VENDOR_ID, 0x1261), diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 658b54d8fcef..b3591d6d7645 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -1600,29 +1600,29 @@ static int ti_download_firmware(struct ti_device *tdev) if (le16_to_cpu(dev->descriptor.idVendor) == MTS_VENDOR_ID) { switch (le16_to_cpu(dev->descriptor.idProduct)) { case MTS_CDMA_PRODUCT_ID: - strcpy(buf, "mts_cdma.fw"); + strscpy(buf, "mts_cdma.fw"); break; case MTS_GSM_PRODUCT_ID: - strcpy(buf, "mts_gsm.fw"); + strscpy(buf, "mts_gsm.fw"); break; case MTS_EDGE_PRODUCT_ID: - strcpy(buf, "mts_edge.fw"); + strscpy(buf, "mts_edge.fw"); break; case MTS_MT9234MU_PRODUCT_ID: - strcpy(buf, "mts_mt9234mu.fw"); + strscpy(buf, "mts_mt9234mu.fw"); break; case MTS_MT9234ZBA_PRODUCT_ID: - strcpy(buf, "mts_mt9234zba.fw"); + strscpy(buf, "mts_mt9234zba.fw"); break; case MTS_MT9234ZBAOLD_PRODUCT_ID: - strcpy(buf, "mts_mt9234zba.fw"); + strscpy(buf, "mts_mt9234zba.fw"); break; } } if (buf[0] == '\0') { if (tdev->td_is_3410) - strcpy(buf, "ti_3410.fw"); + strscpy(buf, "ti_3410.fw"); else - strcpy(buf, "ti_5052.fw"); + strscpy(buf, "ti_5052.fw"); } status = request_firmware(&fw_p, buf, &dev->dev); } diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 0a9902d2b118..265162981269 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -772,7 +772,7 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) return FAILED; } -static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) +static int uas_eh_host_reset_handler(struct scsi_cmnd *cmnd) { struct scsi_device *sdev = cmnd->device; struct uas_dev_info *devinfo = sdev->hostdata; @@ -918,7 +918,7 @@ static const struct scsi_host_template uas_host_template = { .sdev_init = uas_sdev_init, .sdev_configure = uas_sdev_configure, .eh_abort_handler = uas_eh_abort_handler, - .eh_device_reset_handler = uas_eh_device_reset_handler, + .eh_host_reset_handler = uas_eh_host_reset_handler, .this_id = -1, .skip_settle_delay = 1, /* diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 47f50d7a385c..255968f9ca42 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2350,10 +2350,11 @@ UNUSUAL_DEV( 0x2027, 0xa001, 0x0000, 0x9999, US_FL_SCM_MULT_TARG ), /* - * Reported by DocMAX - * and Thomas Weißschuh + * Reported by DocMAX , + * Thomas Weißschuh + * and Daniel Brát */ -UNUSUAL_DEV( 0x2109, 0x0715, 0x9999, 0x9999, +UNUSUAL_DEV( 0x2109, 0x0715, 0x0000, 0x9999, "VIA Labs, Inc.", "VL817 SATA Bridge", USB_SC_DEVICE, USB_PR_DEVICE, NULL, diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index 58fb97ea6877..db5e4a4c0a99 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -35,7 +35,9 @@ static int switch_fwnode_match(struct device *dev, const void *fwnode) static void *typec_switch_match(const struct fwnode_handle *fwnode, const char *id, void *data) { + struct typec_switch_dev **sw_devs = data; struct device *dev; + int i; /* * Device graph (OF graph) does not give any means to identify the @@ -56,6 +58,13 @@ static void *typec_switch_match(const struct fwnode_handle *fwnode, dev = class_find_device(&typec_mux_class, NULL, fwnode, switch_fwnode_match); + /* Skip duplicates */ + for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++) + if (to_typec_switch_dev(dev) == sw_devs[i]) { + put_device(dev); + return NULL; + } + return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -80,7 +89,8 @@ struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode) if (!sw) return ERR_PTR(-ENOMEM); - count = fwnode_connection_find_matches(fwnode, "orientation-switch", NULL, + count = fwnode_connection_find_matches(fwnode, "orientation-switch", + (void **)sw_devs, typec_switch_match, (void **)sw_devs, ARRAY_SIZE(sw_devs)); @@ -265,7 +275,9 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode) static void *typec_mux_match(const struct fwnode_handle *fwnode, const char *id, void *data) { + struct typec_mux_dev **mux_devs = data; struct device *dev; + int i; /* * Device graph (OF graph) does not give any means to identify the @@ -281,6 +293,14 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode, dev = class_find_device(&typec_mux_class, NULL, fwnode, mux_fwnode_match); + /* Skip duplicates */ + for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++) + if (to_typec_mux_dev(dev) == mux_devs[i]) { + put_device(dev); + return NULL; + } + + return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -306,7 +326,8 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode) return ERR_PTR(-ENOMEM); count = fwnode_connection_find_matches(fwnode, "mode-switch", - NULL, typec_mux_match, + (void **)mux_devs, + typec_mux_match, (void **)mux_devs, ARRAY_SIZE(mux_devs)); if (count <= 0) { diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c index 5f2879749769..1256252eceed 100644 --- a/drivers/usb/typec/mux/ps883x.c +++ b/drivers/usb/typec/mux/ps883x.c @@ -444,6 +444,7 @@ static int ps883x_retimer_probe(struct i2c_client *client) goto err_switch_unregister; } + i2c_set_clientdata(client, retimer); return 0; err_switch_unregister: diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig index 8cdd84ca5d6f..00baa7503d45 100644 --- a/drivers/usb/typec/tcpm/Kconfig +++ b/drivers/usb/typec/tcpm/Kconfig @@ -58,6 +58,8 @@ config TYPEC_FUSB302 tristate "Fairchild FUSB302 Type-C chip driver" depends on I2C depends on EXTCON || !EXTCON + depends on DRM || DRM=n + select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF help The Fairchild FUSB302 Type-C chip driver that works with Type-C Port Controller Manager to provide USB PD and USB diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 19ff8217818e..889c4c29c1b8 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -5,6 +5,7 @@ * Fairchild FUSB302 Type-C Chip Driver */ +#include #include #include #include @@ -1689,6 +1690,7 @@ static int fusb302_probe(struct i2c_client *client) { struct fusb302_chip *chip; struct i2c_adapter *adapter = client->adapter; + struct auxiliary_device *bridge_dev; struct device *dev = &client->dev; const char *name; int ret = 0; @@ -1747,6 +1749,13 @@ static int fusb302_probe(struct i2c_client *client) goto destroy_workqueue; } + bridge_dev = devm_drm_dp_hpd_bridge_alloc(chip->dev, to_of_node(chip->tcpc_dev.fwnode)); + if (IS_ERR(bridge_dev)) { + ret = PTR_ERR(bridge_dev); + dev_err_probe(chip->dev, ret, "failed to alloc bridge\n"); + goto destroy_workqueue; + } + chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev); if (IS_ERR(chip->tcpm_port)) { fwnode_handle_put(chip->tcpc_dev.fwnode); @@ -1755,8 +1764,9 @@ static int fusb302_probe(struct i2c_client *client) goto destroy_workqueue; } - ret = request_irq(chip->gpio_int_n_irq, fusb302_irq_intn, - IRQF_TRIGGER_LOW, "fsc_interrupt_int_n", chip); + ret = request_threaded_irq(chip->gpio_int_n_irq, NULL, fusb302_irq_intn, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "fsc_interrupt_int_n", chip); if (ret < 0) { dev_err(dev, "cannot request IRQ for GPIO Int_N, ret=%d", ret); goto tcpm_unregister_port; @@ -1764,6 +1774,10 @@ static int fusb302_probe(struct i2c_client *client) enable_irq_wake(chip->gpio_int_n_irq); i2c_set_clientdata(client, chip); + ret = devm_drm_dp_hpd_bridge_add(chip->dev, bridge_dev); + if (ret) + return ret; + return ret; tcpm_unregister_port: diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 2a951c585e92..0148b8f50412 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,7 @@ struct tcpci { struct tcpc_dev tcpc; struct tcpci_data *data; + struct gpio_desc *orientation_gpio; }; struct tcpci_chip { @@ -316,6 +318,10 @@ static int tcpci_set_orientation(struct tcpc_dev *tcpc, struct tcpci *tcpci = tcpc_to_tcpci(tcpc); unsigned int reg; + if (tcpci->orientation_gpio) + return gpiod_set_value_cansleep(tcpci->orientation_gpio, + orientation != TYPEC_ORIENTATION_NORMAL); + switch (orientation) { case TYPEC_ORIENTATION_NONE: /* We can't put a single output into high impedance */ @@ -903,6 +909,7 @@ EXPORT_SYMBOL_GPL(tcpci_unregister_port); static int tcpci_probe(struct i2c_client *client) { struct tcpci_chip *chip; + struct gpio_desc *orient_gpio = NULL; int err; u16 val = 0; @@ -931,12 +938,23 @@ static int tcpci_probe(struct i2c_client *client) if (err < 0) return err; + if (err == 0) { + orient_gpio = devm_gpiod_get_optional(&client->dev, "orientation", + GPIOD_OUT_LOW); + if (IS_ERR(orient_gpio)) + return dev_err_probe(&client->dev, PTR_ERR(orient_gpio), + "unable to acquire orientation gpio\n"); + err = !!orient_gpio; + } + chip->data.set_orientation = err; chip->tcpci = tcpci_register_port(&client->dev, &chip->data); if (IS_ERR(chip->tcpci)) return PTR_ERR(chip->tcpci); + chip->tcpci->orientation_gpio = orient_gpio; + err = devm_request_threaded_irq(&client->dev, client->irq, NULL, _tcpci_irq, IRQF_SHARED | IRQF_ONESHOT, @@ -999,7 +1017,7 @@ static int tcpci_resume(struct device *dev) return ret; } -DEFINE_SIMPLE_DEV_PM_OPS(tcpci_pm_ops, tcpci_suspend, tcpci_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tcpci_pm_ops, tcpci_suspend, tcpci_resume); static const struct i2c_device_id tcpci_id[] = { { "tcpci" }, diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.h b/drivers/usb/typec/tcpm/tcpci_maxim.h index b33540a42a95..b314606eb0f6 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim.h +++ b/drivers/usb/typec/tcpm/tcpci_maxim.h @@ -60,6 +60,7 @@ struct max_tcpci_chip { struct tcpm_port *port; enum contamiant_state contaminant_state; bool veto_vconn_swap; + struct regulator *vbus_reg; }; static inline int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val) diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index 19f638650796..c0ee7e6959ed 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -35,12 +36,6 @@ */ #define TCPC_RECEIVE_BUFFER_LEN 32 -#define MAX_BUCK_BOOST_SID 0x69 -#define MAX_BUCK_BOOST_OP 0xb9 -#define MAX_BUCK_BOOST_OFF 0 -#define MAX_BUCK_BOOST_SOURCE 0xa -#define MAX_BUCK_BOOST_SINK 0x5 - static const struct regmap_range max_tcpci_tcpci_range[] = { regmap_reg_range(0x00, 0x95) }; @@ -202,32 +197,49 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status) tcpm_pd_receive(chip->port, &msg, rx_type); } +static int get_vbus_regulator_handle(struct max_tcpci_chip *chip) +{ + if (IS_ERR_OR_NULL(chip->vbus_reg)) { + chip->vbus_reg = devm_regulator_get_exclusive(chip->dev, + "vbus"); + if (IS_ERR_OR_NULL(chip->vbus_reg)) { + dev_err(chip->dev, + "Failed to get vbus regulator handle\n"); + return -ENODEV; + } + } + + return 0; +} + static int max_tcpci_set_vbus(struct tcpci *tcpci, struct tcpci_data *tdata, bool source, bool sink) { struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata); - u8 buffer_source[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SOURCE}; - u8 buffer_sink[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SINK}; - u8 buffer_none[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_OFF}; - struct i2c_client *i2c = chip->client; int ret; - struct i2c_msg msgs[] = { - { - .addr = MAX_BUCK_BOOST_SID, - .flags = i2c->flags & I2C_M_TEN, - .len = 2, - .buf = source ? buffer_source : sink ? buffer_sink : buffer_none, - }, - }; - if (source && sink) { dev_err(chip->dev, "Both source and sink set\n"); return -EINVAL; } - ret = i2c_transfer(i2c->adapter, msgs, 1); + ret = get_vbus_regulator_handle(chip); + if (ret) { + /* + * Regulator is not necessary for sink only applications. Return + * success in cases where sink mode is being modified. + */ + return source ? ret : 1; + } - return ret < 0 ? ret : 1; + if (source) { + if (!regulator_is_enabled(chip->vbus_reg)) + ret = regulator_enable(chip->vbus_reg); + } else { + if (regulator_is_enabled(chip->vbus_reg)) + ret = regulator_disable(chip->vbus_reg); + } + + return ret < 0 ? ret : 1; } static void process_power_status(struct max_tcpci_chip *chip) diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index 88c50b984e8a..4b3e4e22a82e 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -18,11 +18,6 @@ #include #include -#define RT1711H_VID 0x29CF -#define RT1711H_PID 0x1711 -#define RT1711H_DID 0x2171 -#define RT1715_DID 0x2173 - #define RT1711H_PHYCTRL1 0x80 #define RT1711H_PHYCTRL2 0x81 @@ -55,7 +50,6 @@ struct rt1711h_chip_info { u32 rxdz_sel; - u16 did; bool enable_pd30_extended_message; }; @@ -301,35 +295,6 @@ static int rt1711h_sw_reset(struct rt1711h_chip *chip) return 0; } -static int rt1711h_check_revision(struct i2c_client *i2c, struct rt1711h_chip *chip) -{ - int ret; - - ret = i2c_smbus_read_word_data(i2c, TCPC_VENDOR_ID); - if (ret < 0) - return ret; - if (ret != RT1711H_VID) { - dev_err(&i2c->dev, "vid is not correct, 0x%04x\n", ret); - return -ENODEV; - } - ret = i2c_smbus_read_word_data(i2c, TCPC_PRODUCT_ID); - if (ret < 0) - return ret; - if (ret != RT1711H_PID) { - dev_err(&i2c->dev, "pid is not correct, 0x%04x\n", ret); - return -ENODEV; - } - ret = i2c_smbus_read_word_data(i2c, TCPC_BCD_DEV); - if (ret < 0) - return ret; - if (ret != chip->info->did) { - dev_err(&i2c->dev, "did is not correct, 0x%04x\n", ret); - return -ENODEV; - } - dev_dbg(&i2c->dev, "did is 0x%04x\n", ret); - return ret; -} - static int rt1711h_probe(struct i2c_client *client) { int ret; @@ -346,12 +311,6 @@ static int rt1711h_probe(struct i2c_client *client) chip->info = i2c_get_match_data(client); - ret = rt1711h_check_revision(client, chip); - if (ret < 0) { - dev_err(&client->dev, "check vid/pid fail\n"); - return ret; - } - chip->data.regmap = devm_regmap_init_i2c(client, &rt1711h_regmap_config); if (IS_ERR(chip->data.regmap)) @@ -406,16 +365,15 @@ static void rt1711h_remove(struct i2c_client *client) } static const struct rt1711h_chip_info rt1711h = { - .did = RT1711H_DID, }; static const struct rt1711h_chip_info rt1715 = { .rxdz_sel = RT1711H_BMCIO_RXDZSEL, - .did = RT1715_DID, .enable_pd30_extended_message = true, }; static const struct i2c_device_id rt1711h_id[] = { + { "et7304", (kernel_ulong_t)&rt1715 }, { "rt1711h", (kernel_ulong_t)&rt1711h }, { "rt1715", (kernel_ulong_t)&rt1715 }, {} @@ -423,6 +381,7 @@ static const struct i2c_device_id rt1711h_id[] = { MODULE_DEVICE_TABLE(i2c, rt1711h_id); static const struct of_device_id rt1711h_of_match[] = { + { .compatible = "etekmicro,et7304", .data = &rt1715 }, { .compatible = "richtek,rt1711h", .data = &rt1711h }, { .compatible = "richtek,rt1715", .data = &rt1715 }, {} diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 8e0e14a2704e..55fee96d3342 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +62,7 @@ S(SNK_WAIT_CAPABILITIES_TIMEOUT), \ S(SNK_NEGOTIATE_CAPABILITIES), \ S(SNK_NEGOTIATE_PPS_CAPABILITIES), \ + S(SNK_NEGOTIATE_SPR_AVS_CAPABILITIES), \ S(SNK_TRANSITION_SINK), \ S(SNK_TRANSITION_SINK_VBUS), \ S(SNK_READY), \ @@ -188,7 +190,8 @@ S(STRUCTURED_VDMS), \ S(COUNTRY_INFO), \ S(COUNTRY_CODES), \ - S(REVISION_INFORMATION) + S(REVISION_INFORMATION), \ + S(GETTING_SINK_EXTENDED_CAPABILITIES) #define GENERATE_ENUM(e) e #define GENERATE_STRING(s) #s @@ -229,6 +232,7 @@ enum pd_msg_request { PD_MSG_DATA_SINK_CAP, PD_MSG_DATA_SOURCE_CAP, PD_MSG_DATA_REV, + PD_MSG_EXT_SINK_CAP_EXT }; enum adev_actions { @@ -305,6 +309,51 @@ struct pd_pps_data { bool active; }; +enum spr_avs_status { + SPR_AVS_UNKNOWN, + SPR_AVS_NOT_SUPPORTED, + SPR_AVS_SUPPORTED +}; + +static const char * const spr_avs_status_strings[] = { + [SPR_AVS_UNKNOWN] = "Unknown", + [SPR_AVS_SUPPORTED] = "Supported", + [SPR_AVS_NOT_SUPPORTED] = "Not Supported", +}; + +/* + * Standard Power Range Adjustable Voltage Supply (SPR - AVS) data + * @max_current_ma_9v_to_15v: Max current for 9V to 15V range derived from + * source cap & sink cap + * @max_current_ma_15v_to_20v: Max current for 15V to 20V range derived from + * source cap & sink cap + * @req_op_curr_ma: Requested operating current to the port partner acting as source + * @req_out_volt_mv: Requested output voltage to the port partner acting as source + * @max_out_volt_mv: Max SPR voltage supported by the port and the port partner + * @max_current_ma; MAX SPR current supported by the port and the port partner + * @port_partner_src_status: SPR AVS status of port partner acting as source + * @port_partner_src_pdo_index: PDO index of SPR AVS cap of the port partner + * acting as source. Valid only when + * port_partner_src_status is SPR_AVS_SUPPORTED. + * @port_snk_status: SPR AVS status of the local port acting as sink. + * @port_snk_pdo_index: PDO index of SPR AVS cap of local port acting as sink + * @active: True when the local port acting as the sink has negotiated SPR AVS + * with the partner acting as source. + */ +struct pd_spr_avs_data { + u32 max_current_ma_9v_to_15v; + u32 max_current_ma_15v_to_20v; + u32 req_op_curr_ma; + u32 req_out_volt_mv; + u32 max_out_volt_mv; + u32 max_current_ma; + enum spr_avs_status port_partner_src_status; + unsigned int port_partner_src_pdo_index; + enum spr_avs_status port_snk_status; + unsigned int port_snk_pdo_index; + bool active; +}; + struct pd_data { struct usb_power_delivery *pd; struct usb_power_delivery_capabilities *source_cap; @@ -337,6 +386,47 @@ struct pd_timings { u32 snk_bc12_cmpletion_time; }; +/* Convert microwatt to watt */ +#define UW_TO_W(pow) ((pow) / 1000000) + +/* + * struct pd_identifier - Contains info about PD identifiers + * @vid: Vendor ID (assigned by USB-IF) + * @pid: Product ID (assigned by manufacturer) + * @xid: Value assigned by USB-IF for product + */ +struct pd_identifier { + u16 vid; + u16 pid; + u32 xid; +}; + +/* + * struct sink_caps_ext_data - Sink extended capability data + * @load_step: Indicates the load step slew rate. Value of 0 indicates 150mA/us + * & 1 indicates 500 mA/us + * @load_char: Snk overload characteristics + * @compliance: Types of sources the sink has been tested & certified on + * @modes: Charging caps & power sources supported + * @spr_min_pdp: Sink Minimum PDP for SPR mode (in Watts) + * @spr_op_pdp: Sink Operational PDP for SPR mode (in Watts) + * @spr_max_pdp: Sink Maximum PDP for SPR mode (in Watts) + */ +struct sink_caps_ext_data { + u8 load_step; + u16 load_char; + u8 compliance; + u8 modes; + u8 spr_min_pdp; + u8 spr_op_pdp; + u8 spr_max_pdp; +}; + +enum aug_req_type { + PD_PPS, + PD_SPR_AVS, +}; + struct tcpm_port { struct device *dev; @@ -499,9 +589,14 @@ struct tcpm_port { /* PPS */ struct pd_pps_data pps_data; - struct completion pps_complete; - bool pps_pending; - int pps_status; + + /* SPR AVS */ + struct pd_spr_avs_data spr_avs_data; + + /* Augmented supply request - PPS; SPR_AVS */ + struct completion aug_supply_req_complete; + bool aug_supply_req_pending; + int aug_supply_req_status; /* Alternate mode data */ struct pd_mode_data mode_data; @@ -585,6 +680,9 @@ struct tcpm_port { /* Indicates maximum (revision, version) supported */ struct pd_revision_info pd_rev; + + struct pd_identifier pd_ident; + struct sink_caps_ext_data sink_caps_ext; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -605,9 +703,9 @@ struct altmode_vdm_event { struct kthread_work work; struct tcpm_port *port; u32 header; - u32 *data; int cnt; enum tcpm_transmit_type tx_sop_type; + u32 data[] __counted_by(cnt); }; static const char * const pd_rev[] = { @@ -634,9 +732,14 @@ static const char * const pd_rev[] = { (tcpm_cc_is_source((port)->cc2) && \ !tcpm_cc_is_source((port)->cc1))) +#define tcpm_port_is_debug_source(port) \ + (tcpm_cc_is_source((port)->cc1) && tcpm_cc_is_source((port)->cc2)) + +#define tcpm_port_is_debug_sink(port) \ + (tcpm_cc_is_sink((port)->cc1) && tcpm_cc_is_sink((port)->cc2)) + #define tcpm_port_is_debug(port) \ - ((tcpm_cc_is_source((port)->cc1) && tcpm_cc_is_source((port)->cc2)) || \ - (tcpm_cc_is_sink((port)->cc1) && tcpm_cc_is_sink((port)->cc2))) + (tcpm_port_is_debug_source(port) || tcpm_port_is_debug_sink(port)) #define tcpm_port_is_audio(port) \ (tcpm_cc_is_audio((port)->cc1) && tcpm_cc_is_audio((port)->cc2)) @@ -725,7 +828,7 @@ static void _tcpm_log(struct tcpm_port *port, const char *fmt, va_list args) if (tcpm_log_full(port)) { port->logbuffer_head = max(port->logbuffer_head - 1, 0); - strcpy(tmpbuffer, "overflow"); + strscpy(tmpbuffer, "overflow"); } if (port->logbuffer_head < 0 || @@ -841,10 +944,10 @@ static void tcpm_log_source_caps(struct tcpm_port *port) pdo_spr_avs_apdo_15v_to_20v_max_current_ma(pdo), pdo_spr_avs_apdo_src_peak_current(pdo)); else - strcpy(msg, "undefined APDO"); + strscpy(msg, "undefined APDO"); break; default: - strcpy(msg, "undefined"); + strscpy(msg, "undefined"); break; } tcpm_log(port, " PDO %d: type %d, %s", @@ -1367,6 +1470,64 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port) return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); } +static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) +{ + u16 operating_snk_watt = port->operating_snk_mw / 1000; + struct sink_caps_ext_data *data = &port->sink_caps_ext; + struct pd_identifier *pd_ident = &port->pd_ident; + struct sink_caps_ext_msg skedb = {0}; + struct pd_message msg; + u8 data_obj_cnt; + + if (!port->self_powered) + data->spr_op_pdp = operating_snk_watt; + + /* + * SPR Sink Minimum PDP indicates the minimum power required to operate + * a sink device in its lowest level of functionality without requiring + * power from the battery. We can use the operating_snk_watt value to + * populate it, as operating_snk_watt indicates device's min operating + * power. + */ + data->spr_min_pdp = operating_snk_watt; + + if (data->spr_op_pdp < data->spr_min_pdp || + data->spr_max_pdp < data->spr_op_pdp) { + tcpm_log(port, + "Invalid PDP values, Min PDP:%u, Op PDP:%u, Max PDP:%u", + data->spr_min_pdp, data->spr_op_pdp, data->spr_max_pdp); + return -EOPNOTSUPP; + } + + memset(&msg, 0, sizeof(msg)); + skedb.vid = cpu_to_le16(pd_ident->vid); + skedb.pid = cpu_to_le16(pd_ident->pid); + skedb.xid = cpu_to_le32(pd_ident->xid); + skedb.skedb_ver = SKEDB_VER_1_0; + skedb.load_step = data->load_step; + skedb.load_char = cpu_to_le16(data->load_char); + skedb.compliance = data->compliance; + skedb.modes = data->modes; + skedb.spr_min_pdp = data->spr_min_pdp; + skedb.spr_op_pdp = data->spr_op_pdp; + skedb.spr_max_pdp = data->spr_max_pdp; + memcpy(msg.ext_msg.data, &skedb, sizeof(skedb)); + msg.ext_msg.header = PD_EXT_HDR_LE(sizeof(skedb), + 0, /* Denotes if request chunk */ + 0, /* Chunk Number */ + 1 /* Chunked */); + + data_obj_cnt = count_chunked_data_objs(sizeof(skedb)); + msg.header = cpu_to_le16(PD_HEADER(PD_EXT_SINK_CAP_EXT, + port->pwr_role, + port->data_role, + port->negotiated_rev, + port->message_id, + data_obj_cnt, + 1 /* Denotes if ext header */)); + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + static void mod_tcpm_delayed_work(struct tcpm_port *port, unsigned int delay_ms) { if (delay_ms) { @@ -1653,7 +1814,6 @@ static void tcpm_queue_vdm_work(struct kthread_work *work) tcpm_queue_vdm(port, event->header, event->data, event->cnt, event->tx_sop_type); port_unlock: - kfree(event->data); kfree(event); mutex_unlock(&port->lock); } @@ -1662,35 +1822,27 @@ static int tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 header, const u32 *data, int cnt, enum tcpm_transmit_type tx_sop_type) { struct altmode_vdm_event *event; - u32 *data_cpy; int ret = -ENOMEM; - event = kzalloc_obj(*event); + event = kzalloc_flex(*event, data, cnt); if (!event) goto err_event; - data_cpy = kcalloc(cnt, sizeof(u32), GFP_KERNEL); - if (!data_cpy) - goto err_data; - kthread_init_work(&event->work, tcpm_queue_vdm_work); + event->cnt = cnt; event->port = port; event->header = header; - memcpy(data_cpy, data, sizeof(u32) * cnt); - event->data = data_cpy; - event->cnt = cnt; + memcpy(event->data, data, sizeof(u32) * cnt); event->tx_sop_type = tx_sop_type; ret = kthread_queue_work(port->wq, &event->work); if (!ret) { ret = -EBUSY; - goto err_queue; + goto err_data; } return 0; -err_queue: - kfree(data_cpy); err_data: kfree(event); err_event: @@ -3194,6 +3346,7 @@ static void tcpm_pd_data_request(struct tcpm_port *port, switch (type) { case PD_DATA_SOURCE_CAP: + port->spr_avs_data.port_partner_src_status = SPR_AVS_UNKNOWN; for (i = 0; i < cnt; i++) port->source_caps[i] = le32_to_cpu(msg->payload[i]); @@ -3365,12 +3518,12 @@ static void tcpm_pd_data_request(struct tcpm_port *port, } } -static void tcpm_pps_complete(struct tcpm_port *port, int result) +static void tcpm_aug_supply_req_complete(struct tcpm_port *port, int result) { - if (port->pps_pending) { - port->pps_status = result; - port->pps_pending = false; - complete(&port->pps_complete); + if (port->aug_supply_req_pending) { + port->aug_supply_req_status = result; + port->aug_supply_req_pending = false; + complete(&port->aug_supply_req_complete); } } @@ -3468,7 +3621,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, /* Revert data back from any requested PPS updates */ port->pps_data.req_out_volt = port->supply_voltage; port->pps_data.req_op_curr = port->current_limit; - port->pps_status = (type == PD_CTRL_WAIT ? + port->aug_supply_req_status = (type == PD_CTRL_WAIT ? -EAGAIN : -EOPNOTSUPP); /* Threshold was relaxed before sending Request. Restore it back. */ @@ -3476,6 +3629,20 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, port->pps_data.active, port->supply_voltage); + tcpm_set_state(port, SNK_READY, 0); + break; + case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES: + /* Revert data back from any requested SPR AVS updates */ + port->spr_avs_data.req_out_volt_mv = port->supply_voltage; + port->spr_avs_data.req_op_curr_ma = port->current_limit; + port->aug_supply_req_status = (type == PD_CTRL_WAIT ? + -EAGAIN : -EOPNOTSUPP); + + /* Threshold was relaxed before sending Request. Restore it back. */ + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD, + port->spr_avs_data.active, + port->supply_voltage); + tcpm_set_state(port, SNK_READY, 0); break; case DR_SWAP_SEND: @@ -3530,6 +3697,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, switch (port->state) { case SNK_NEGOTIATE_CAPABILITIES: port->pps_data.active = false; + port->spr_avs_data.active = false; tcpm_set_state(port, SNK_TRANSITION_SINK, 0); break; case SNK_NEGOTIATE_PPS_CAPABILITIES: @@ -3542,6 +3710,13 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, power_supply_changed(port->psy); tcpm_set_state(port, SNK_TRANSITION_SINK, 0); break; + case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES: + port->spr_avs_data.active = true; + port->req_supply_voltage = port->spr_avs_data.req_out_volt_mv; + port->req_current_limit = port->spr_avs_data.req_op_curr_ma; + power_supply_changed(port->psy); + tcpm_set_state(port, SNK_TRANSITION_SINK, 0); + break; case SOFT_RESET_SEND: if (port->ams == SOFT_RESET_AMS) tcpm_ams_finish(port); @@ -3655,6 +3830,19 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); break; + case PD_CTRL_GET_SINK_CAP_EXT: + /* This is an unsupported message if port type is SRC */ + if (port->negotiated_rev >= PD_REV30 && + port->port_type != TYPEC_PORT_SRC) + tcpm_pd_handle_msg(port, PD_MSG_EXT_SINK_CAP_EXT, + GETTING_SINK_EXTENDED_CAPABILITIES); + else + tcpm_pd_handle_msg(port, + port->negotiated_rev < PD_REV30 ? + PD_MSG_CTRL_REJECT : + PD_MSG_CTRL_NOT_SUPP, + NONE_AMS); + break; default: tcpm_pd_handle_msg(port, port->negotiated_rev < PD_REV30 ? @@ -3907,6 +4095,16 @@ static bool tcpm_send_queued_message(struct tcpm_port *port) ret); tcpm_ams_finish(port); break; + case PD_MSG_EXT_SINK_CAP_EXT: + ret = tcpm_pd_send_sink_cap_ext(port); + if (ret == -EOPNOTSUPP) + tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP, TCPC_TX_SOP); + else if (ret < 0) + tcpm_log(port, + "Unable to transmit sink cap extended, ret=%d", + ret); + tcpm_ams_finish(port); + break; default: break; } @@ -4016,9 +4214,9 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, case PDO_TYPE_APDO: if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) { port->pps_data.supported = true; - port->usb_type = - POWER_SUPPLY_USB_TYPE_PD_PPS; - power_supply_changed(port->psy); + } else if (pdo_apdo_type(pdo) == APDO_TYPE_SPR_AVS) { + port->spr_avs_data.port_partner_src_status = SPR_AVS_SUPPORTED; + port->spr_avs_data.port_partner_src_pdo_index = i; } continue; default: @@ -4056,6 +4254,10 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, min_snk_mv = pdo_min_voltage(pdo); break; case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) == APDO_TYPE_SPR_AVS) { + port->spr_avs_data.port_snk_status = SPR_AVS_SUPPORTED; + port->spr_avs_data.port_snk_pdo_index = j; + } continue; default: tcpm_log(port, "Invalid sink PDO type, ignoring"); @@ -4077,6 +4279,23 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, } } + if (port->spr_avs_data.port_snk_status == SPR_AVS_UNKNOWN) + port->spr_avs_data.port_snk_status = SPR_AVS_NOT_SUPPORTED; + + if (port->spr_avs_data.port_partner_src_status == SPR_AVS_UNKNOWN) + port->spr_avs_data.port_partner_src_status = SPR_AVS_NOT_SUPPORTED; + + if (port->pps_data.supported && + port->spr_avs_data.port_partner_src_status == SPR_AVS_SUPPORTED) + port->usb_type = POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS; + else if (port->pps_data.supported) + port->usb_type = POWER_SUPPLY_USB_TYPE_PD_PPS; + else if (port->spr_avs_data.port_partner_src_status == SPR_AVS_SUPPORTED) + port->usb_type = POWER_SUPPLY_USB_TYPE_PD_SPR_AVS; + + if (port->usb_type != POWER_SUPPLY_USB_TYPE_PD) + power_supply_changed(port->psy); + return ret; } @@ -4127,6 +4346,88 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) return src_pdo; } +static int tcpm_pd_select_spr_avs_apdo(struct tcpm_port *port) +{ + u32 req_out_volt_mv, req_op_curr_ma, src_max_curr_ma = 0, source_cap; + u32 snk_max_curr_ma = 0, src_pdo_index, snk_pdo_index, snk_pdo; + + if (port->spr_avs_data.port_snk_status != SPR_AVS_SUPPORTED || + port->spr_avs_data.port_partner_src_status != + SPR_AVS_SUPPORTED) { + tcpm_log(port, "SPR AVS not supported. port:%s partner:%s", + spr_avs_status_strings[port->spr_avs_data.port_snk_status], + spr_avs_status_strings[port->spr_avs_data.port_partner_src_status]); + return -EOPNOTSUPP; + } + + /* Round up to SPR_AVS_VOLT_MV_STEP */ + req_out_volt_mv = port->spr_avs_data.req_out_volt_mv; + if (req_out_volt_mv % SPR_AVS_VOLT_MV_STEP) { + req_out_volt_mv += SPR_AVS_VOLT_MV_STEP - + (req_out_volt_mv % SPR_AVS_VOLT_MV_STEP); + port->spr_avs_data.req_out_volt_mv = req_out_volt_mv; + } + + /* Round up to RDO_SPR_AVS_CURR_MA_STEP */ + req_op_curr_ma = port->spr_avs_data.req_op_curr_ma; + if (req_op_curr_ma % RDO_SPR_AVS_CURR_MA_STEP) { + req_op_curr_ma += RDO_SPR_AVS_CURR_MA_STEP - + (req_op_curr_ma % RDO_SPR_AVS_CURR_MA_STEP); + port->spr_avs_data.req_op_curr_ma = req_op_curr_ma; + } + + src_pdo_index = port->spr_avs_data.port_partner_src_pdo_index; + snk_pdo_index = port->spr_avs_data.port_snk_pdo_index; + source_cap = port->source_caps[src_pdo_index]; + snk_pdo = port->snk_pdo[snk_pdo_index]; + tcpm_log(port, + "SPR AVS src_pdo_index:%d snk_pdo_index:%d req_op_curr_ma roundup:%u req_out_volt_mv roundup:%u", + src_pdo_index, snk_pdo_index, req_op_curr_ma, req_out_volt_mv); + + if (req_out_volt_mv >= SPR_AVS_TIER1_MIN_VOLT_MV && + req_out_volt_mv <= SPR_AVS_TIER1_MAX_VOLT_MV) { + src_max_curr_ma = + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(source_cap); + snk_max_curr_ma = + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(snk_pdo); + } else if (req_out_volt_mv > SPR_AVS_TIER1_MAX_VOLT_MV && + req_out_volt_mv <= SPR_AVS_TIER2_MAX_VOLT_MV) { + src_max_curr_ma = + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(source_cap); + snk_max_curr_ma = + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(snk_pdo); + } else { + tcpm_log(port, "Invalid SPR AVS req_volt:%umV", req_out_volt_mv); + return -EINVAL; + } + + if (req_op_curr_ma > src_max_curr_ma || + req_op_curr_ma > snk_max_curr_ma) { + tcpm_log(port, + "Invalid SPR AVS request. req_volt:%umV req_curr:%umA src_max_cur:%umA snk_max_cur:%umA", + req_out_volt_mv, req_op_curr_ma, src_max_curr_ma, + snk_max_curr_ma); + return -EINVAL; + } + + /* Max SPR voltage based on both the port and the partner caps */ + if (pdo_spr_avs_apdo_15v_to_20v_max_current_ma(snk_pdo) && + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(source_cap)) + port->spr_avs_data.max_out_volt_mv = SPR_AVS_TIER2_MAX_VOLT_MV; + else + port->spr_avs_data.max_out_volt_mv = SPR_AVS_TIER1_MAX_VOLT_MV; + + /* + * Max SPR AVS curr based on 9V to 15V. This should be higher than or + * equal to 15V to 20V range. + */ + port->spr_avs_data.max_current_ma = + min(pdo_spr_avs_apdo_9v_to_15v_max_current_ma(source_cap), + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(snk_pdo)); + + return src_pdo_index; +} + static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) { unsigned int mv, ma, mw, flags; @@ -4294,13 +4595,74 @@ static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo) return 0; } -static int tcpm_pd_send_pps_request(struct tcpm_port *port) +static int tcpm_pd_build_spr_avs_request(struct tcpm_port *port, u32 *rdo) +{ + u32 out_mv, op_ma, flags, snk_pdo_index, source_cap; + unsigned int src_power_mw, snk_power_mw; + int src_pdo_index; + u32 snk_pdo; + + src_pdo_index = tcpm_pd_select_spr_avs_apdo(port); + if (src_pdo_index < 0) + return src_pdo_index; + snk_pdo_index = port->spr_avs_data.port_snk_pdo_index; + source_cap = port->source_caps[src_pdo_index]; + snk_pdo = port->snk_pdo[snk_pdo_index]; + out_mv = port->spr_avs_data.req_out_volt_mv; + op_ma = port->spr_avs_data.req_op_curr_ma; + + flags = RDO_USB_COMM | RDO_NO_SUSPEND; + + /* + * Set capability mismatch when the maximum power needs in the current + * requested AVS voltage tier range is greater than + * port->operating_snk_mw, however, the maximum power offered by the + * source at the current requested AVS voltage tier is less than + * port->operating_sink_mw. + */ + if (out_mv > SPR_AVS_TIER1_MAX_VOLT_MV) { + src_power_mw = + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(source_cap) * + SPR_AVS_TIER2_MAX_VOLT_MV / 1000; + snk_power_mw = + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(snk_pdo) * + SPR_AVS_TIER2_MAX_VOLT_MV / 1000; + } else { + src_power_mw = + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(source_cap) * + SPR_AVS_TIER1_MAX_VOLT_MV / 1000; + snk_power_mw = + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(snk_pdo) * + SPR_AVS_TIER1_MAX_VOLT_MV / 1000; + } + + if (snk_power_mw >= port->operating_snk_mw && + src_power_mw < port->operating_snk_mw) + flags |= RDO_CAP_MISMATCH; + + *rdo = RDO_AVS(src_pdo_index + 1, out_mv, op_ma, flags); + + tcpm_log(port, "Requesting APDO SPR AVS %d: %u mV, %u mA", + src_pdo_index, out_mv, op_ma); + + return 0; +} + +static int tcpm_pd_send_aug_supply_request(struct tcpm_port *port, + enum aug_req_type type) { struct pd_message msg; int ret; u32 rdo; - ret = tcpm_pd_build_pps_request(port, &rdo); + if (type == PD_PPS) { + ret = tcpm_pd_build_pps_request(port, &rdo); + } else if (type == PD_SPR_AVS) { + ret = tcpm_pd_build_spr_avs_request(port, &rdo); + } else { + tcpm_log(port, "Invalid aug_req_type %d", type); + ret = -EOPNOTSUPP; + } if (ret < 0) return ret; @@ -4523,6 +4885,14 @@ static void tcpm_set_partner_usb_comm_capable(struct tcpm_port *port, bool capab port->tcpc->set_partner_usb_comm_capable(port->tcpc, capable); } +static void tcpm_partner_source_caps_reset(struct tcpm_port *port) +{ + usb_power_delivery_unregister_capabilities(port->partner_source_caps); + port->partner_source_caps = NULL; + port->spr_avs_data.port_partner_src_status = SPR_AVS_UNKNOWN; + port->spr_avs_data.active = false; +} + static void tcpm_reset_port(struct tcpm_port *port) { tcpm_enable_auto_vbus_discharge(port, false); @@ -4562,8 +4932,7 @@ static void tcpm_reset_port(struct tcpm_port *port) usb_power_delivery_unregister_capabilities(port->partner_sink_caps); port->partner_sink_caps = NULL; - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps = NULL; + tcpm_partner_source_caps_reset(port); usb_power_delivery_unregister(port->partner_pd); port->partner_pd = NULL; } @@ -4812,7 +5181,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SNK_UNATTACHED, PD_T_DRP_SNK); break; case SRC_ATTACH_WAIT: - if (tcpm_port_is_debug(port)) + if (tcpm_port_is_debug_source(port)) tcpm_set_state(port, DEBUG_ACC_ATTACHED, port->timings.cc_debounce_time); else if (tcpm_port_is_audio(port)) @@ -5055,7 +5424,7 @@ static void run_state_machine(struct tcpm_port *port) case SNK_UNATTACHED: if (!port->non_pd_role_swap) tcpm_swap_complete(port, -ENOTCONN); - tcpm_pps_complete(port, -ENOTCONN); + tcpm_aug_supply_req_complete(port, -ENOTCONN); tcpm_snk_detach(port); if (port->potential_contaminant) { tcpm_set_state(port, CHECK_CONTAMINANT, 0); @@ -5070,7 +5439,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_UNATTACHED, PD_T_DRP_SRC); break; case SNK_ATTACH_WAIT: - if (tcpm_port_is_debug(port)) + if (tcpm_port_is_debug_sink(port)) tcpm_set_state(port, DEBUG_ACC_ATTACHED, PD_T_CC_DEBOUNCE); else if (tcpm_port_is_audio(port)) @@ -5090,7 +5459,7 @@ static void run_state_machine(struct tcpm_port *port) if (tcpm_port_is_disconnected(port)) tcpm_set_state(port, SNK_UNATTACHED, PD_T_PD_DEBOUNCE); - else if (tcpm_port_is_debug(port)) + else if (tcpm_port_is_debug_sink(port)) tcpm_set_state(port, DEBUG_ACC_ATTACHED, PD_T_CC_DEBOUNCE); else if (tcpm_port_is_audio(port)) @@ -5286,13 +5655,16 @@ static void run_state_machine(struct tcpm_port *port) } break; case SNK_NEGOTIATE_PPS_CAPABILITIES: - ret = tcpm_pd_send_pps_request(port); + case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES: + ret = tcpm_pd_send_aug_supply_request(port, port->state == + SNK_NEGOTIATE_PPS_CAPABILITIES ? + PD_PPS : PD_SPR_AVS); if (ret < 0) { /* Restore back to the original state */ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD, port->pps_data.active, port->supply_voltage); - port->pps_status = ret; + port->aug_supply_req_status = ret; /* * If this was called due to updates to sink * capabilities, and pps is no longer valid, we should @@ -5308,23 +5680,58 @@ static void run_state_machine(struct tcpm_port *port) } break; case SNK_TRANSITION_SINK: - /* From the USB PD spec: - * "The Sink Shall transition to Sink Standby before a positive or - * negative voltage transition of VBUS. During Sink Standby - * the Sink Shall reduce its power draw to pSnkStdby." - * - * This is not applicable to PPS though as the port can continue - * to draw negotiated power without switching to standby. - */ - if (port->supply_voltage != port->req_supply_voltage && !port->pps_data.active && - port->current_limit * port->supply_voltage / 1000 > PD_P_SNK_STDBY_MW) { - u32 stdby_ma = PD_P_SNK_STDBY_MW * 1000 / port->supply_voltage; + if (port->spr_avs_data.active) { + if (abs(port->req_supply_voltage - port->supply_voltage) > + SPR_AVS_AVS_SMALL_STEP_V * 1000) { + /* + * The Sink Shall reduce its current draw to + * iSnkStdby within tSnkStdby. The reduction to + * iSnkStdby is not required if the voltage + * increase is less than or equal to + * vAvsSmallStep. + */ + tcpm_log(port, + "SPR AVS Setting iSnkstandby. Req vol: %u mV Curr vol: %u mV", + port->req_supply_voltage, + port->supply_voltage); + tcpm_set_current_limit(port, PD_I_SNK_STBY_MA, + port->supply_voltage); + } + /* + * Although tAvsSrcTransSmall is expected to be used + * for voltage transistions smaller than 1V, using + * tAvsSrcTransLarge to be resilient against chargers + * which strictly cannot honor tAvsSrcTransSmall to + * improve interoperability. + */ + tcpm_set_state(port, hard_reset_state(port), + PD_T_AVS_SRC_TRANS_LARGE); + /* + * From the USB PD spec: + * "The Sink Shall transition to Sink Standby before a + * positive ornegative voltage transition of VBUS. + * During Sink Standby the Sink Shall reduce its power + * draw to pSnkStdby." + * + * This is not applicable to PPS though as the port can + * continue to draw negotiated power without switching + * to standby. + */ + } else if (port->supply_voltage != port->req_supply_voltage && + !port->pps_data.active && + (port->current_limit * port->supply_voltage / 1000 > + PD_P_SNK_STDBY_MW)) { + u32 stdby_ma = PD_P_SNK_STDBY_MW * 1000 / + port->supply_voltage; tcpm_log(port, "Setting standby current %u mV @ %u mA", port->supply_voltage, stdby_ma); - tcpm_set_current_limit(port, stdby_ma, port->supply_voltage); + tcpm_set_current_limit(port, stdby_ma, + port->supply_voltage); + tcpm_set_state(port, hard_reset_state(port), + PD_T_PS_TRANSITION); } - fallthrough; + break; case SNK_TRANSITION_SINK_VBUS: tcpm_set_state(port, hard_reset_state(port), PD_T_PS_TRANSITION); @@ -5344,7 +5751,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_typec_connect(port); if (port->pd_capable && port->source_caps[0] & PDO_FIXED_DUAL_ROLE) mod_enable_frs_delayed_work(port, 0); - tcpm_pps_complete(port, port->pps_status); + tcpm_aug_supply_req_complete(port, port->aug_supply_req_status); if (port->ams != NONE_AMS) tcpm_ams_finish(port); @@ -5531,9 +5938,10 @@ static void run_state_machine(struct tcpm_port *port) port->message_id = 0; port->rx_msgid = -1; /* remove existing capabilities */ - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps = NULL; + tcpm_partner_source_caps_reset(port); tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); + port->vdm_sm_running = false; + port->explicit_contract = false; tcpm_ams_finish(port); if (port->pwr_role == TYPEC_SOURCE) { port->upcoming_state = SRC_SEND_CAPABILITIES; @@ -5565,8 +5973,7 @@ static void run_state_machine(struct tcpm_port *port) port->message_id = 0; port->rx_msgid = -1; /* remove existing capabilities */ - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps = NULL; + tcpm_partner_source_caps_reset(port); if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET, TCPC_TX_SOP)) tcpm_set_state_cond(port, hard_reset_state(port), 0); else @@ -5703,8 +6110,7 @@ static void run_state_machine(struct tcpm_port *port) break; case PR_SWAP_SNK_SRC_SINK_OFF: /* will be source, remove existing capabilities */ - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps = NULL; + tcpm_partner_source_caps_reset(port); /* * Prevent vbus discharge circuit from turning on during PR_SWAP * as this is not a disconnect. @@ -5852,7 +6258,7 @@ static void run_state_machine(struct tcpm_port *port) break; case ERROR_RECOVERY: tcpm_swap_complete(port, -EPROTO); - tcpm_pps_complete(port, -EPROTO); + tcpm_aug_supply_req_complete(port, -EPROTO); tcpm_set_state(port, PORT_RESET, 0); break; case PORT_RESET: @@ -5961,10 +6367,10 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, switch (port->state) { case TOGGLING: - if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || + if (tcpm_port_is_debug_source(port) || tcpm_port_is_audio(port) || tcpm_port_is_source(port)) tcpm_set_state(port, SRC_ATTACH_WAIT, 0); - else if (tcpm_port_is_sink(port)) + else if (tcpm_port_is_debug_sink(port) || tcpm_port_is_sink(port)) tcpm_set_state(port, SNK_ATTACH_WAIT, 0); break; case CHECK_CONTAMINANT: @@ -5972,9 +6378,11 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, break; case SRC_UNATTACHED: case ACC_UNATTACHED: - if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || + if (tcpm_port_is_debug_source(port) || tcpm_port_is_audio(port) || tcpm_port_is_source(port)) tcpm_set_state(port, SRC_ATTACH_WAIT, 0); + else if (tcpm_port_is_debug_sink(port)) + tcpm_set_state(port, SNK_ATTACH_WAIT, 0); break; case SRC_ATTACH_WAIT: if (tcpm_port_is_disconnected(port) || @@ -5996,7 +6404,7 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, } break; case SNK_UNATTACHED: - if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || + if (tcpm_port_is_debug_sink(port) || tcpm_port_is_audio(port) || tcpm_port_is_sink(port)) tcpm_set_state(port, SNK_ATTACH_WAIT, 0); break; @@ -6826,7 +7234,7 @@ static int tcpm_try_role(struct typec_port *p, int role) return ret; } -static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 req_op_curr) +static int tcpm_aug_set_op_curr(struct tcpm_port *port, u16 req_op_curr_ma) { unsigned int target_mw; int ret; @@ -6834,7 +7242,19 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 req_op_curr) mutex_lock(&port->swap_lock); mutex_lock(&port->lock); - if (!port->pps_data.active) { + if (port->pps_data.active) { + req_op_curr_ma = req_op_curr_ma - + (req_op_curr_ma % RDO_PROG_CURR_MA_STEP); + if (req_op_curr_ma > port->pps_data.max_curr) { + ret = -EINVAL; + goto port_unlock; + } + target_mw = (req_op_curr_ma * port->supply_voltage) / 1000; + if (target_mw < port->operating_snk_mw) { + ret = -EINVAL; + goto port_unlock; + } + } else if (!port->spr_avs_data.active) { ret = -EOPNOTSUPP; goto port_unlock; } @@ -6844,38 +7264,31 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 req_op_curr) goto port_unlock; } - if (req_op_curr > port->pps_data.max_curr) { - ret = -EINVAL; - goto port_unlock; - } + if (port->pps_data.active) + port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES; + else + port->upcoming_state = SNK_NEGOTIATE_SPR_AVS_CAPABILITIES; - target_mw = (req_op_curr * port->supply_voltage) / 1000; - if (target_mw < port->operating_snk_mw) { - ret = -EINVAL; - goto port_unlock; - } - - port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES; ret = tcpm_ams_start(port, POWER_NEGOTIATION); if (ret == -EAGAIN) { port->upcoming_state = INVALID_STATE; goto port_unlock; } - /* Round down operating current to align with PPS valid steps */ - req_op_curr = req_op_curr - (req_op_curr % RDO_PROG_CURR_MA_STEP); - - reinit_completion(&port->pps_complete); - port->pps_data.req_op_curr = req_op_curr; - port->pps_status = 0; - port->pps_pending = true; + reinit_completion(&port->aug_supply_req_complete); + if (port->pps_data.active) + port->pps_data.req_op_curr = req_op_curr_ma; + else + port->spr_avs_data.req_op_curr_ma = req_op_curr_ma; + port->aug_supply_req_status = 0; + port->aug_supply_req_pending = true; mutex_unlock(&port->lock); - if (!wait_for_completion_timeout(&port->pps_complete, - msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + if (!wait_for_completion_timeout(&port->aug_supply_req_complete, + msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT))) ret = -ETIMEDOUT; else - ret = port->pps_status; + ret = port->aug_supply_req_status; goto swap_unlock; @@ -6887,7 +7300,7 @@ swap_unlock: return ret; } -static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt) +static int tcpm_aug_set_out_volt(struct tcpm_port *port, u16 req_out_volt_mv) { unsigned int target_mw; int ret; @@ -6895,7 +7308,16 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt) mutex_lock(&port->swap_lock); mutex_lock(&port->lock); - if (!port->pps_data.active) { + if (port->pps_data.active) { + req_out_volt_mv = req_out_volt_mv - (req_out_volt_mv % + RDO_PROG_VOLT_MV_STEP); + /* Round down output voltage to align with PPS valid steps */ + target_mw = (port->current_limit * req_out_volt_mv) / 1000; + if (target_mw < port->operating_snk_mw) { + ret = -EINVAL; + goto port_unlock; + } + } else if (!port->spr_avs_data.active) { ret = -EOPNOTSUPP; goto port_unlock; } @@ -6905,33 +7327,31 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt) goto port_unlock; } - target_mw = (port->current_limit * req_out_volt) / 1000; - if (target_mw < port->operating_snk_mw) { - ret = -EINVAL; - goto port_unlock; - } + if (port->pps_data.active) + port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES; + else + port->upcoming_state = SNK_NEGOTIATE_SPR_AVS_CAPABILITIES; - port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES; ret = tcpm_ams_start(port, POWER_NEGOTIATION); if (ret == -EAGAIN) { port->upcoming_state = INVALID_STATE; goto port_unlock; } - /* Round down output voltage to align with PPS valid steps */ - req_out_volt = req_out_volt - (req_out_volt % RDO_PROG_VOLT_MV_STEP); - - reinit_completion(&port->pps_complete); - port->pps_data.req_out_volt = req_out_volt; - port->pps_status = 0; - port->pps_pending = true; + reinit_completion(&port->aug_supply_req_complete); + if (port->pps_data.active) + port->pps_data.req_out_volt = req_out_volt_mv; + else + port->spr_avs_data.req_out_volt_mv = req_out_volt_mv; + port->aug_supply_req_status = 0; + port->aug_supply_req_pending = true; mutex_unlock(&port->lock); - if (!wait_for_completion_timeout(&port->pps_complete, - msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + if (!wait_for_completion_timeout(&port->aug_supply_req_complete, + msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT))) ret = -ETIMEDOUT; else - ret = port->pps_status; + ret = port->aug_supply_req_status; goto swap_unlock; @@ -6974,9 +7394,9 @@ static int tcpm_pps_activate(struct tcpm_port *port, bool activate) goto port_unlock; } - reinit_completion(&port->pps_complete); - port->pps_status = 0; - port->pps_pending = true; + reinit_completion(&port->aug_supply_req_complete); + port->aug_supply_req_status = 0; + port->aug_supply_req_pending = true; /* Trigger PPS request or move back to standard PDO contract */ if (activate) { @@ -6985,11 +7405,75 @@ static int tcpm_pps_activate(struct tcpm_port *port, bool activate) } mutex_unlock(&port->lock); - if (!wait_for_completion_timeout(&port->pps_complete, - msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + if (!wait_for_completion_timeout(&port->aug_supply_req_complete, + msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT))) ret = -ETIMEDOUT; else - ret = port->pps_status; + ret = port->aug_supply_req_status; + + goto swap_unlock; + +port_unlock: + mutex_unlock(&port->lock); +swap_unlock: + mutex_unlock(&port->swap_lock); + + return ret; +} + +static int tcpm_spr_avs_activate(struct tcpm_port *port, bool activate) +{ + int ret = 0; + + mutex_lock(&port->swap_lock); + mutex_lock(&port->lock); + + if (port->spr_avs_data.port_snk_status == SPR_AVS_NOT_SUPPORTED || + port->spr_avs_data.port_partner_src_status == SPR_AVS_NOT_SUPPORTED) { + tcpm_log(port, "SPR_AVS not supported"); + ret = -EOPNOTSUPP; + goto port_unlock; + } + + /* Trying to deactivate SPR AVS when already deactivated so just bail */ + if (!port->spr_avs_data.active && !activate) + goto port_unlock; + + if (port->state != SNK_READY) { + tcpm_log(port, + "SPR_AVS cannot be activated. Port not in SNK_READY"); + ret = -EAGAIN; + goto port_unlock; + } + + if (activate) + port->upcoming_state = SNK_NEGOTIATE_SPR_AVS_CAPABILITIES; + else + port->upcoming_state = SNK_NEGOTIATE_CAPABILITIES; + ret = tcpm_ams_start(port, POWER_NEGOTIATION); + if (ret == -EAGAIN) { + tcpm_log(port, "SPR_AVS cannot be %s. AMS start failed", + activate ? "activated" : "deactivated"); + port->upcoming_state = INVALID_STATE; + goto port_unlock; + } + + reinit_completion(&port->aug_supply_req_complete); + port->aug_supply_req_status = 0; + port->aug_supply_req_pending = true; + + /* Trigger AVS request or move back to standard PDO contract */ + if (activate) { + port->spr_avs_data.req_out_volt_mv = port->supply_voltage; + port->spr_avs_data.req_op_curr_ma = port->current_limit; + } + mutex_unlock(&port->lock); + + if (!wait_for_completion_timeout(&port->aug_supply_req_complete, + msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT))) + ret = -ETIMEDOUT; + else + ret = port->aug_supply_req_status; goto swap_unlock; @@ -7145,16 +7629,26 @@ static int tcpm_pd_set(struct typec_port *p, struct usb_power_delivery *pd) break; case SNK_NEGOTIATE_CAPABILITIES: case SNK_NEGOTIATE_PPS_CAPABILITIES: + case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES: case SNK_READY: case SNK_TRANSITION_SINK: case SNK_TRANSITION_SINK_VBUS: - if (port->pps_data.active) + if (port->pps_data.active) { port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES; - else if (port->pd_capable) + } else if (port->pd_capable) { port->upcoming_state = SNK_NEGOTIATE_CAPABILITIES; - else + if (port->spr_avs_data.active) { + /* + * De-activate AVS and fallback to PD to + * re-evaluate whether AVS is supported in the + * current sink cap set. + */ + port->spr_avs_data.active = false; + port->spr_avs_data.port_snk_status = SPR_AVS_UNKNOWN; + } + } else { break; - + } port->update_sink_caps = true; ret = tcpm_ams_start(port, POWER_NEGOTIATION); @@ -7291,6 +7785,129 @@ static void tcpm_fw_get_timings(struct tcpm_port *port, struct fwnode_handle *fw port->timings.snk_bc12_cmpletion_time = val; } +static void tcpm_fw_get_pd_ident(struct tcpm_port *port) +{ + struct pd_identifier *pd_ident = &port->pd_ident; + u32 *vdo; + + /* First 3 vdo values contain info regarding USB PID, VID & XID */ + if (port->nr_snk_vdo >= 3) + vdo = port->snk_vdo; + else if (port->nr_snk_vdo_v1 >= 3) + vdo = port->snk_vdo_v1; + else + return; + + pd_ident->vid = PD_IDH_VID(vdo[0]); + pd_ident->pid = PD_PRODUCT_PID(vdo[2]); + pd_ident->xid = PD_CSTAT_XID(vdo[1]); + tcpm_log(port, "vid:%#x pid:%#x xid:%#x", + pd_ident->vid, pd_ident->pid, pd_ident->xid); +} + +static void tcpm_parse_snk_pdos(struct tcpm_port *port) +{ + struct sink_caps_ext_data *caps = &port->sink_caps_ext; + u32 max_mv, max_ma; + u8 avs_tier1_pdp, avs_tier2_pdp; + int i, pdo_itr; + u32 *snk_pdos; + + for (i = 0; i < port->pd_count; ++i) { + snk_pdos = port->pd_list[i]->sink_desc.pdo; + for (pdo_itr = 0; pdo_itr < PDO_MAX_OBJECTS && snk_pdos[pdo_itr]; + ++pdo_itr) { + u32 pdo = snk_pdos[pdo_itr]; + u8 curr_snk_pdp = 0; + + switch (pdo_type(pdo)) { + case PDO_TYPE_FIXED: + max_mv = pdo_fixed_voltage(pdo); + max_ma = pdo_fixed_current(pdo); + curr_snk_pdp = UW_TO_W(max_mv * max_ma); + break; + case PDO_TYPE_BATT: + curr_snk_pdp = UW_TO_W(pdo_max_power(pdo)); + break; + case PDO_TYPE_VAR: + max_mv = pdo_max_voltage(pdo); + max_ma = pdo_max_current(pdo); + curr_snk_pdp = UW_TO_W(max_mv * max_ma); + break; + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) { + max_mv = pdo_pps_apdo_max_voltage(pdo); + max_ma = pdo_pps_apdo_max_current(pdo); + curr_snk_pdp = UW_TO_W(max_mv * max_ma); + caps->modes |= SINK_MODE_PPS; + } else if (pdo_apdo_type(pdo) == + APDO_TYPE_SPR_AVS) { + avs_tier1_pdp = UW_TO_W(SPR_AVS_TIER1_MAX_VOLT_MV + * pdo_spr_avs_apdo_9v_to_15v_max_current_ma(pdo)); + avs_tier2_pdp = UW_TO_W(SPR_AVS_TIER2_MAX_VOLT_MV + * pdo_spr_avs_apdo_15v_to_20v_max_current_ma(pdo)); + curr_snk_pdp = max(avs_tier1_pdp, avs_tier2_pdp); + caps->modes |= SINK_MODE_AVS; + } + break; + default: + tcpm_log(port, "Invalid source PDO type, ignoring"); + continue; + } + + caps->spr_max_pdp = max(caps->spr_max_pdp, + curr_snk_pdp); + } + } +} + +static void tcpm_fw_get_sink_caps_ext(struct tcpm_port *port, + struct fwnode_handle *fwnode) +{ + struct sink_caps_ext_data *caps = &port->sink_caps_ext; + int ret; + u32 val; + + /* + * Load step represents the change in current per usec that a given + * source can tolerate while maintaining Vbus within the vSrcValid + * range. For a sink this represents the "preferred" load-step value. It + * can only have 2 values (150 mA/usec or 500 mA/usec) with 150 mA/usec + * being the default. + */ + ret = fwnode_property_read_u32(fwnode, "sink-load-step", &val); + if (!ret) + caps->load_step = val == 500 ? 1 : 0; + + fwnode_property_read_u16(fwnode, "sink-load-characteristics", + &caps->load_char); + fwnode_property_read_u8(fwnode, "sink-compliance", &caps->compliance); + caps->modes = SINK_MODE_VBUS; + + /* + * As per "6.5.13.14" SPR Sink Operational PDP definition, for battery + * powered devices, this value will correspond to the PDP of the + * charging adapter either shipped or recommended for use with it. For + * batteryless sink devices SPR Operational PDP indicates the power + * required to operate all the device's functional modes. Hence, this + * value may be considered equal to port's operating_snk_mw. As + * operating_sink_mw can change as per the pd set used thus, OP PDP + * is determined when populating Sink Caps Extended Data Block. + */ + if (port->self_powered) { + fwnode_property_read_u32(fwnode, "charging-adapter-pdp-milliwatt", + &val); + caps->spr_op_pdp = (u8)(val / 1000); + caps->modes |= SINK_MODE_BATT; + } + + tcpm_parse_snk_pdos(port); + tcpm_log(port, + "load-step:%#x load-char:%#x compl:%#x op-pdp:%#x max-pdp:%#x", + caps->load_step, caps->load_char, caps->compliance, + caps->spr_op_pdp, caps->spr_max_pdp); +} + static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) { struct fwnode_handle *capabilities, *caps = NULL; @@ -7464,6 +8081,9 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode } } + if (port->port_type != TYPEC_PORT_SRC) + tcpm_fw_get_sink_caps_ext(port, fwnode); + put_caps: if (caps != fwnode) fwnode_handle_put(caps); @@ -7506,6 +8126,8 @@ static int tcpm_fw_get_snk_vdos(struct tcpm_port *port, struct fwnode_handle *fw return ret; } + tcpm_fw_get_pd_ident(port); + return 0; } @@ -7536,7 +8158,8 @@ static void tcpm_fw_get_pd_revision(struct tcpm_port *port, struct fwnode_handle enum tcpm_psy_online_states { TCPM_PSY_OFFLINE = 0, TCPM_PSY_FIXED_ONLINE, - TCPM_PSY_PROG_ONLINE, + TCPM_PSY_PPS_ONLINE, + TCPM_PSY_SPR_AVS_ONLINE, }; static enum power_supply_property tcpm_psy_props[] = { @@ -7554,7 +8177,9 @@ static int tcpm_psy_get_online(struct tcpm_port *port, { if (port->vbus_charge) { if (port->pps_data.active) - val->intval = TCPM_PSY_PROG_ONLINE; + val->intval = TCPM_PSY_PPS_ONLINE; + else if (port->spr_avs_data.active) + val->intval = TCPM_PSY_SPR_AVS_ONLINE; else val->intval = TCPM_PSY_FIXED_ONLINE; } else { @@ -7569,6 +8194,8 @@ static int tcpm_psy_get_voltage_min(struct tcpm_port *port, { if (port->pps_data.active) val->intval = port->pps_data.min_volt * 1000; + else if (port->spr_avs_data.active) + val->intval = SPR_AVS_TIER1_MIN_VOLT_MV * 1000; else val->intval = port->supply_voltage * 1000; @@ -7580,6 +8207,8 @@ static int tcpm_psy_get_voltage_max(struct tcpm_port *port, { if (port->pps_data.active) val->intval = port->pps_data.max_volt * 1000; + else if (port->spr_avs_data.active) + val->intval = port->spr_avs_data.max_out_volt_mv * 1000; else val->intval = port->supply_voltage * 1000; @@ -7599,6 +8228,8 @@ static int tcpm_psy_get_current_max(struct tcpm_port *port, { if (port->pps_data.active) val->intval = port->pps_data.max_curr * 1000; + else if (port->spr_avs_data.active) + val->intval = port->spr_avs_data.max_current_ma * 1000; else val->intval = port->current_limit * 1000; @@ -7674,17 +8305,41 @@ static int tcpm_psy_get_prop(struct power_supply *psy, return ret; } +static int tcpm_disable_pps_avs(struct tcpm_port *port) +{ + int ret = 0; + + if (port->pps_data.active) + ret = tcpm_pps_activate(port, false); + else if (port->spr_avs_data.active) + ret = tcpm_spr_avs_activate(port, false); + + return ret; +} + static int tcpm_psy_set_online(struct tcpm_port *port, const union power_supply_propval *val) { - int ret; + int ret = 0; switch (val->intval) { case TCPM_PSY_FIXED_ONLINE: - ret = tcpm_pps_activate(port, false); + ret = tcpm_disable_pps_avs(port); break; - case TCPM_PSY_PROG_ONLINE: - ret = tcpm_pps_activate(port, true); + case TCPM_PSY_PPS_ONLINE: + if (port->spr_avs_data.active) + ret = tcpm_spr_avs_activate(port, false); + if (!ret) + ret = tcpm_pps_activate(port, true); + break; + case TCPM_PSY_SPR_AVS_ONLINE: + tcpm_log(port, "request to set AVS online"); + if (port->spr_avs_data.active) + return 0; + ret = tcpm_disable_pps_avs(port); + if (ret) + break; + ret = tcpm_spr_avs_activate(port, true); break; default: ret = -EINVAL; @@ -7713,13 +8368,10 @@ static int tcpm_psy_set_prop(struct power_supply *psy, ret = tcpm_psy_set_online(port, val); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - ret = tcpm_pps_set_out_volt(port, val->intval / 1000); + ret = tcpm_aug_set_out_volt(port, val->intval / 1000); break; case POWER_SUPPLY_PROP_CURRENT_NOW: - if (val->intval > port->pps_data.max_curr * 1000) - ret = -EINVAL; - else - ret = tcpm_pps_set_op_curr(port, val->intval / 1000); + ret = tcpm_aug_set_op_curr(port, val->intval / 1000); break; default: ret = -EINVAL; @@ -7764,7 +8416,9 @@ static int devm_tcpm_psy_register(struct tcpm_port *port) port->psy_desc.type = POWER_SUPPLY_TYPE_USB; port->psy_desc.usb_types = BIT(POWER_SUPPLY_USB_TYPE_C) | BIT(POWER_SUPPLY_USB_TYPE_PD) | - BIT(POWER_SUPPLY_USB_TYPE_PD_PPS); + BIT(POWER_SUPPLY_USB_TYPE_PD_PPS) | + BIT(POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS) | + BIT(POWER_SUPPLY_USB_TYPE_PD_SPR_AVS); port->psy_desc.properties = tcpm_psy_props; port->psy_desc.num_properties = ARRAY_SIZE(tcpm_psy_props); port->psy_desc.get_property = tcpm_psy_get_prop; @@ -7859,7 +8513,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) init_completion(&port->tx_complete); init_completion(&port->swap_complete); - init_completion(&port->pps_complete); + init_completion(&port->aug_supply_req_complete); tcpm_debugfs_init(port); err = tcpm_fw_get_caps(port, tcpc->fwnode); diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index e2b26af2b84a..43faec794b95 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -820,8 +820,10 @@ static void cd321x_update_work(struct work_struct *work) desc.identity = &st.partner_identity; tps->partner = typec_register_partner(tps->port, &desc); - if (IS_ERR(tps->partner)) - dev_warn(tps->dev, "%s: failed to register partnet\n", __func__); + if (IS_ERR(tps->partner)) { + dev_warn(tps->dev, "%s: failed to register partner\n", __func__); + return; + } if (desc.identity) { typec_partner_set_identity(tps->partner); diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c index 6bca2dce211c..251aa7251ce6 100644 --- a/drivers/usb/typec/ucsi/cros_ec_ucsi.c +++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c @@ -5,11 +5,13 @@ * Copyright 2024 Google LLC. */ +#include #include #include #include #include #include +#include #include #include #include @@ -257,7 +259,6 @@ static void cros_ucsi_destroy(struct cros_ucsi_data *udata) static int cros_ucsi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct cros_ec_dev *ec_data = dev_get_drvdata(dev->parent); struct cros_ucsi_data *udata; int ret; @@ -265,9 +266,16 @@ static int cros_ucsi_probe(struct platform_device *pdev) if (!udata) return -ENOMEM; + /* ACPI and OF FW nodes for cros_ec_ucsi are children of the ChromeOS EC. If the + * cros_ec_ucsi device has an ACPI or OF FW node, its parent is the ChromeOS EC device. + * Platforms without a FW node for cros_ec_ucsi may add it as a subdevice of cros_ec_dev. + */ udata->dev = dev; + if (is_acpi_device_node(dev->fwnode) || is_of_node(dev->fwnode)) + udata->ec = dev_get_drvdata(dev->parent); + else + udata->ec = ((struct cros_ec_dev *)dev_get_drvdata(dev->parent))->ec_dev; - udata->ec = ec_data->ec_dev; if (!udata->ec) return dev_err_probe(dev, -ENODEV, "couldn't find parent EC device\n"); @@ -348,10 +356,24 @@ static const struct platform_device_id cros_ucsi_id[] = { }; MODULE_DEVICE_TABLE(platform, cros_ucsi_id); +static const struct acpi_device_id cros_ec_ucsi_acpi_device_ids[] = { + { "GOOG0021", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cros_ec_ucsi_acpi_device_ids); + +static const struct of_device_id cros_ucsi_of_match[] = { + { .compatible = "google,cros-ec-ucsi", }, + {} +}; +MODULE_DEVICE_TABLE(of, cros_ucsi_of_match); + static struct platform_driver cros_ucsi_driver = { .driver = { .name = KBUILD_MODNAME, .pm = &cros_ucsi_pm_ops, + .acpi_match_table = cros_ec_ucsi_acpi_device_ids, + .of_match_table = cros_ucsi_of_match, }, .id_table = cros_ucsi_id, .probe = cros_ucsi_probe, diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 46262ee0d192..5b7ad9e99cb9 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -241,6 +241,8 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, if (cci & UCSI_CCI_ERROR) ret = ucsi_read_error(ucsi, connector_num); + trace_ucsi_run_command(cmd, ret); + mutex_unlock(&ucsi->ppm_lock); return ret; } @@ -1186,10 +1188,18 @@ static void ucsi_partner_change(struct ucsi_connector *con) if (UCSI_CONSTAT(con, PARTNER_FLAG_USB)) typec_set_mode(con->port, TYPEC_STATE_USB); } + + if (((con->ucsi->version >= UCSI_VERSION_3_0 && + UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN4)) || + (con->ucsi->version >= UCSI_VERSION_2_0 && + UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN3))) && con->partner) + typec_partner_set_usb_mode(con->partner, USB_MODE_USB4); } - /* Only notify USB controller if partner supports USB data */ - if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB))) + if ((!UCSI_CONSTAT(con, PARTNER_FLAG_USB)) && + ((con->ucsi->quirks & UCSI_USB4_IMPLIES_USB) && + (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN3) || + UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN4))))) u_role = USB_ROLE_NONE; ret = usb_role_switch_set_role(con->usb_role_sw, u_role); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 43a0d01ade8f..cff9ddc2ae21 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -497,6 +497,9 @@ struct ucsi { unsigned long quirks; #define UCSI_NO_PARTNER_PDOS BIT(0) /* Don't read partner's PDOs */ #define UCSI_DELAY_DEVICE_PDOS BIT(1) /* Reading PDOs fails until the parter is in PD mode */ + +/* USB4 connection can imply that USB communcation is supported */ +#define UCSI_USB4_IMPLIES_USB BIT(2) }; #define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff) diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 11b3e24e34e2..12e07b9fe622 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -371,14 +371,18 @@ static void pmic_glink_ucsi_destroy(void *data) static unsigned long quirk_sc8180x = UCSI_NO_PARTNER_PDOS; static unsigned long quirk_sc8280xp = UCSI_NO_PARTNER_PDOS | UCSI_DELAY_DEVICE_PDOS; static unsigned long quirk_sm8450 = UCSI_DELAY_DEVICE_PDOS; +static unsigned long quirk_x1e80100 = UCSI_DELAY_DEVICE_PDOS | UCSI_USB4_IMPLIES_USB; static const struct of_device_id pmic_glink_ucsi_of_quirks[] = { + { .compatible = "qcom,glymur-pmic-glink", .data = &quirk_sm8450, }, + { .compatible = "qcom,kaanapali-pmic-glink", .data = &quirk_sm8450, }, { .compatible = "qcom,qcm6490-pmic-glink", .data = &quirk_sc8280xp, }, { .compatible = "qcom,sc8180x-pmic-glink", .data = &quirk_sc8180x, }, { .compatible = "qcom,sc8280xp-pmic-glink", .data = &quirk_sc8280xp, }, { .compatible = "qcom,sm8350-pmic-glink", .data = &quirk_sc8180x, }, { .compatible = "qcom,sm8450-pmic-glink", .data = &quirk_sm8450, }, { .compatible = "qcom,sm8550-pmic-glink", .data = &quirk_sm8450, }, + { .compatible = "qcom,x1e80100-pmic-glink", .data = &quirk_x1e80100, }, {} }; diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c index c5965656baba..ca749fde49bd 100644 --- a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c +++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c @@ -193,7 +193,7 @@ static void gaokun_ucsi_connector_status(struct ucsi_connector *con) gaokun_set_orientation(con, &uec->ports[idx]); } -const struct ucsi_operations gaokun_ucsi_ops = { +static const struct ucsi_operations gaokun_ucsi_ops = { .read_version = gaokun_ucsi_read_version, .read_cci = gaokun_ucsi_read_cci, .poll_cci = gaokun_ucsi_read_cci, diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c index 34990b7e2d18..abfa11d6bde7 100644 --- a/drivers/usb/usbip/stub_dev.c +++ b/drivers/usb/usbip/stub_dev.c @@ -267,7 +267,7 @@ static struct stub_device *stub_device_alloc(struct usb_device *udev) if (!sdev) return NULL; - sdev->udev = usb_get_dev(udev); + sdev->udev = udev; /* * devid is defined with devnum when this driver is first allocated. @@ -409,7 +409,6 @@ call_put_busid_priv: put_busid_priv(busid_priv); sdev_free: - usb_put_dev(udev); stub_device_free(sdev); return rc; @@ -488,8 +487,6 @@ static void stub_disconnect(struct usb_device *udev) /* shutdown the current connection */ shutdown_busid(busid_priv); - usb_put_dev(sdev->udev); - /* we already have busid_priv, just lock busid_lock */ spin_lock(&busid_priv->busid_lock); /* free sdev */ diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index a2b2da1255dd..a5837c0feb05 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -470,6 +470,18 @@ static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, urb->status = rpdu->status; urb->actual_length = rpdu->actual_length; urb->start_frame = rpdu->start_frame; + /* + * The number_of_packets field determines the length of + * iso_frame_desc[], which is a flexible array allocated + * at URB creation time. A response must never claim more + * packets than originally submitted; doing so would cause + * an out-of-bounds write in usbip_recv_iso() and + * usbip_pad_iso(). Clamp to zero on violation so both + * functions safely return early. + */ + if (rpdu->number_of_packets < 0 || + rpdu->number_of_packets > urb->number_of_packets) + rpdu->number_of_packets = 0; urb->number_of_packets = rpdu->number_of_packets; urb->error_count = rpdu->error_count; } @@ -662,19 +674,29 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) void *buff; struct usbip_iso_packet_descriptor *iso; int np = urb->number_of_packets; - int size = np * sizeof(*iso); + int size; int i; int ret; - int total_length = 0; + u32 total_length = 0; if (!usb_pipeisoc(urb->pipe)) return 0; - /* my Bluetooth dongle gets ISO URBs which are np = 0 */ - if (np == 0) - return 0; + if (np <= 0 || np > USBIP_MAX_ISO_PACKETS) { + dev_err(&urb->dev->dev, + "recv iso: invalid number_of_packets %d\n", np); + /* + * usbip_pack_ret_submit() already set urb->number_of_packets + * from the wire. Zero it so processcompl() does not iterate + * OOB descriptors on the way out. + */ + urb->number_of_packets = 0; + return -EPROTO; + } - buff = kzalloc(size, GFP_KERNEL); + size = np * sizeof(*iso); + + buff = kcalloc(np, sizeof(*iso), GFP_KERNEL); if (!buff) return -ENOMEM; @@ -696,14 +718,23 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) for (i = 0; i < np; i++) { usbip_iso_packet_correct_endian(&iso[i], 0); usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 0); + if (urb->iso_frame_desc[i].actual_length > + (unsigned int)urb->transfer_buffer_length) { + dev_err(&urb->dev->dev, + "recv iso: frame actual_length %u exceeds buffer %d\n", + urb->iso_frame_desc[i].actual_length, + urb->transfer_buffer_length); + kfree(buff); + return -EPROTO; + } total_length += urb->iso_frame_desc[i].actual_length; } kfree(buff); - if (total_length != urb->actual_length) { + if (total_length != (u32)urb->actual_length) { dev_err(&urb->dev->dev, - "total length of iso packets %d not equal to actual length of buffer %d\n", + "total length of iso packets %u not equal to actual length of buffer %d\n", total_length, urb->actual_length); if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) @@ -751,6 +782,42 @@ void usbip_pad_iso(struct usbip_device *ud, struct urb *urb) */ for (i = np-1; i > 0; i--) { actualoffset -= urb->iso_frame_desc[i].actual_length; + + /* + * Validate source range: actualoffset can go negative + * via crafted actual_length values from the wire. + */ + if (actualoffset < 0 || + (unsigned int)actualoffset > + (unsigned int)urb->transfer_buffer_length || + urb->iso_frame_desc[i].actual_length > + (unsigned int)urb->transfer_buffer_length - + (unsigned int)actualoffset) { + dev_err(&urb->dev->dev, + "pad_iso: bad src off=%d len=%u bufsz=%d\n", + actualoffset, + urb->iso_frame_desc[i].actual_length, + urb->transfer_buffer_length); + return; + } + + /* + * Validate destination range: iso_frame_desc[i].offset + * is wire-supplied and must not exceed the buffer. + */ + if (urb->iso_frame_desc[i].offset > + (unsigned int)urb->transfer_buffer_length || + urb->iso_frame_desc[i].actual_length > + (unsigned int)urb->transfer_buffer_length - + urb->iso_frame_desc[i].offset) { + dev_err(&urb->dev->dev, + "pad_iso: bad dst off=%u len=%u bufsz=%d\n", + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length, + urb->transfer_buffer_length); + return; + } + memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset, urb->transfer_buffer + actualoffset, urb->iso_frame_desc[i].actual_length); diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index bfc10f665e52..5bc8c47788d4 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -463,7 +463,7 @@ static void set_status_attr(int id) status = status_attrs + id; if (id == 0) - strcpy(status->name, "status"); + strscpy(status->name, "status"); else snprintf(status->name, MAX_STATUS_NAME+1, "status.%d", id); status->attr.attr.name = status->name; diff --git a/drivers/vfio/cdx/intr.c b/drivers/vfio/cdx/intr.c index 8f4402cec9c5..4439481fe633 100644 --- a/drivers/vfio/cdx/intr.c +++ b/drivers/vfio/cdx/intr.c @@ -32,26 +32,27 @@ static int vfio_cdx_msi_enable(struct vfio_cdx_device *vdev, int nvec) return -ENOMEM; ret = cdx_enable_msi(cdx_dev); - if (ret) { - kfree(vdev->cdx_irqs); - return ret; - } + if (ret) + goto err_free; /* Allocate cdx MSIs */ ret = msi_domain_alloc_irqs(dev, MSI_DEFAULT_DOMAIN, nvec); - if (ret) { - cdx_disable_msi(cdx_dev); - kfree(vdev->cdx_irqs); - return ret; - } + if (ret) + goto err_disable; for (msi_idx = 0; msi_idx < nvec; msi_idx++) vdev->cdx_irqs[msi_idx].irq_no = msi_get_virq(dev, msi_idx); vdev->msi_count = nvec; - vdev->config_msi = 1; return 0; + +err_disable: + cdx_disable_msi(cdx_dev); +err_free: + kfree(vdev->cdx_irqs); + vdev->cdx_irqs = NULL; + return ret; } static int vfio_cdx_msi_set_vector_signal(struct vfio_cdx_device *vdev, @@ -129,7 +130,7 @@ static void vfio_cdx_msi_disable(struct vfio_cdx_device *vdev) vfio_cdx_msi_set_block(vdev, 0, vdev->msi_count, NULL); - if (!vdev->config_msi) + if (!vdev->cdx_irqs) return; msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN); @@ -138,7 +139,6 @@ static void vfio_cdx_msi_disable(struct vfio_cdx_device *vdev) vdev->cdx_irqs = NULL; vdev->msi_count = 0; - vdev->config_msi = 0; } static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev, @@ -152,6 +152,8 @@ static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev, if (start + count > cdx_dev->num_msi) return -EINVAL; + guard(mutex)(&vdev->cdx_irqs_lock); + if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) { vfio_cdx_msi_disable(vdev); return 0; @@ -161,7 +163,7 @@ static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev, s32 *fds = data; int ret; - if (vdev->config_msi) + if (vdev->cdx_irqs) return vfio_cdx_msi_set_block(vdev, start, count, fds); ret = vfio_cdx_msi_enable(vdev, cdx_dev->num_msi); @@ -175,6 +177,9 @@ static int vfio_cdx_set_msi_trigger(struct vfio_cdx_device *vdev, return ret; } + if (!vdev->cdx_irqs) + return -EINVAL; + for (i = start; i < start + count; i++) { if (!vdev->cdx_irqs[i].trigger) continue; @@ -206,12 +211,5 @@ int vfio_cdx_set_irqs_ioctl(struct vfio_cdx_device *vdev, /* Free All IRQs for the given device */ void vfio_cdx_irqs_cleanup(struct vfio_cdx_device *vdev) { - /* - * Device does not support any interrupt or the interrupts - * were not configured - */ - if (!vdev->cdx_irqs) - return; - vfio_cdx_set_msi_trigger(vdev, 0, 0, 0, VFIO_IRQ_SET_DATA_NONE, NULL); } diff --git a/drivers/vfio/cdx/main.c b/drivers/vfio/cdx/main.c index 8ab97405b2bd..b31ed4be7bdc 100644 --- a/drivers/vfio/cdx/main.c +++ b/drivers/vfio/cdx/main.c @@ -8,6 +8,23 @@ #include "private.h" +static int vfio_cdx_init_dev(struct vfio_device *core_vdev) +{ + struct vfio_cdx_device *vdev = + container_of(core_vdev, struct vfio_cdx_device, vdev); + + mutex_init(&vdev->cdx_irqs_lock); + return 0; +} + +static void vfio_cdx_release_dev(struct vfio_device *core_vdev) +{ + struct vfio_cdx_device *vdev = + container_of(core_vdev, struct vfio_cdx_device, vdev); + + mutex_destroy(&vdev->cdx_irqs_lock); +} + static int vfio_cdx_open_device(struct vfio_device *core_vdev) { struct vfio_cdx_device *vdev = @@ -273,6 +290,8 @@ static int vfio_cdx_mmap(struct vfio_device *core_vdev, static const struct vfio_device_ops vfio_cdx_ops = { .name = "vfio-cdx", + .init = vfio_cdx_init_dev, + .release = vfio_cdx_release_dev, .open_device = vfio_cdx_open_device, .close_device = vfio_cdx_close_device, .ioctl = vfio_cdx_ioctl, diff --git a/drivers/vfio/cdx/private.h b/drivers/vfio/cdx/private.h index 172e48caa3a0..4c00bf633356 100644 --- a/drivers/vfio/cdx/private.h +++ b/drivers/vfio/cdx/private.h @@ -6,6 +6,8 @@ #ifndef VFIO_CDX_PRIVATE_H #define VFIO_CDX_PRIVATE_H +#include + #define VFIO_CDX_OFFSET_SHIFT 40 static inline u64 vfio_cdx_index_to_offset(u32 index) @@ -31,11 +33,11 @@ struct vfio_cdx_region { struct vfio_cdx_device { struct vfio_device vdev; struct vfio_cdx_region *regions; + struct mutex cdx_irqs_lock; struct vfio_cdx_irq *cdx_irqs; u32 flags; #define BME_SUPPORT BIT(0) u32 msi_count; - u8 config_msi; }; #ifdef CONFIG_GENERIC_MSI_IRQ diff --git a/drivers/vfio/device_cdev.c b/drivers/vfio/device_cdev.c index 8ceca24ac136..54abf312cf04 100644 --- a/drivers/vfio/device_cdev.c +++ b/drivers/vfio/device_cdev.c @@ -293,14 +293,8 @@ int vfio_df_ioctl_detach_pt(struct vfio_device_file *df, return 0; } -static char *vfio_device_devnode(const struct device *dev, umode_t *mode) +int vfio_cdev_init(void) { - return kasprintf(GFP_KERNEL, "vfio/devices/%s", dev_name(dev)); -} - -int vfio_cdev_init(struct class *device_class) -{ - device_class->devnode = vfio_device_devnode; return alloc_chrdev_region(&device_devt, 0, MINORMASK + 1, "vfio-dev"); } diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index ad52abc46c04..050e7542952e 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -482,6 +482,40 @@ static int vfio_pci_core_runtime_resume(struct device *dev) } #endif /* CONFIG_PM */ +/* + * Eager-request BAR resources, and iomap them. Soft failures are + * allowed, and consumers must check the barmap before use in order to + * give compatible user-visible behaviour with the previous on-demand + * allocation method. + */ +static void vfio_pci_core_map_bars(struct vfio_pci_core_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + int i; + + for (i = 0; i < PCI_STD_NUM_BARS; i++) { + int bar = i + PCI_STD_RESOURCES; + + vdev->barmap[bar] = IOMEM_ERR_PTR(-ENODEV); + + if (!pci_resource_len(pdev, i)) + continue; + + if (pci_request_selected_regions(pdev, 1 << bar, "vfio")) { + pci_dbg(pdev, "Failed to reserve region %d\n", bar); + vdev->barmap[bar] = IOMEM_ERR_PTR(-EBUSY); + continue; + } + + vdev->barmap[bar] = pci_iomap(pdev, bar, 0); + if (!vdev->barmap[bar]) { + pci_dbg(pdev, "Failed to iomap region %d\n", bar); + pci_release_selected_regions(pdev, 1 << bar); + vdev->barmap[bar] = IOMEM_ERR_PTR(-ENOMEM); + } + } +} + /* * The pci-driver core runtime PM routines always save the device state * before going into suspended state. If the device is going into low power @@ -568,6 +602,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev) if (!vfio_vga_disabled() && vfio_pci_is_vga(pdev)) vdev->has_vga = true; + vfio_pci_core_map_bars(vdev); return 0; @@ -648,7 +683,7 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev) for (i = 0; i < PCI_STD_NUM_BARS; i++) { bar = i + PCI_STD_RESOURCES; - if (!vdev->barmap[bar]) + if (IS_ERR_OR_NULL(vdev->barmap[bar])) continue; pci_iounmap(pdev, vdev->barmap[bar]); pci_release_selected_regions(pdev, 1 << bar); @@ -734,10 +769,10 @@ void vfio_pci_core_close_device(struct vfio_device *core_vdev) #if IS_ENABLED(CONFIG_EEH) eeh_dev_release(vdev->pdev); #endif - vfio_pci_core_disable(vdev); - vfio_pci_dma_buf_cleanup(vdev); + vfio_pci_core_disable(vdev); + mutex_lock(&vdev->igate); vfio_pci_eventfd_replace_locked(vdev, &vdev->err_trigger, NULL); vfio_pci_eventfd_replace_locked(vdev, &vdev->req_trigger, NULL); diff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c index f87fd32e4a01..1a177ce7de54 100644 --- a/drivers/vfio/pci/vfio_pci_dmabuf.c +++ b/drivers/vfio/pci/vfio_pci_dmabuf.c @@ -244,9 +244,11 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags, return -EINVAL; /* - * For PCI the region_index is the BAR number like everything else. + * For PCI the region_index is the BAR number like everything + * else. Check that PCI resources have been claimed for it. */ - if (get_dma_buf.region_index >= VFIO_PCI_ROM_REGION_INDEX) + if (get_dma_buf.region_index >= VFIO_PCI_ROM_REGION_INDEX || + vfio_pci_core_setup_barmap(vdev, get_dma_buf.region_index)) return -ENODEV; dma_ranges = memdup_array_user(&arg->dma_ranges, get_dma_buf.nr_ranges, @@ -354,19 +356,18 @@ void vfio_pci_dma_buf_move(struct vfio_pci_core_device *vdev, bool revoked) if (revoked) { kref_put(&priv->kref, vfio_pci_dma_buf_done); wait_for_completion(&priv->comp); - } else { /* - * Kref is initialize again, because when revoke - * was performed the reference counter was decreased - * to zero to trigger completion. + * Re-arm the registered kref reference and the + * completion so the post-revoke state matches the + * post-creation state. An un-revoke followed by a + * new mapping needs the kref to be non-zero before + * kref_get(), and vfio_pci_dma_buf_cleanup() + * delegates its drain back through this revoke + * path on a possibly-already-revoked dma-buf. */ kref_init(&priv->kref); - /* - * There is no need to wait as no mapping was - * performed when the previous status was - * priv->revoked == true. - */ reinit_completion(&priv->comp); + } else { dma_resv_lock(priv->dmabuf->resv, NULL); priv->revoked = false; dma_resv_unlock(priv->dmabuf->resv); @@ -382,21 +383,22 @@ void vfio_pci_dma_buf_cleanup(struct vfio_pci_core_device *vdev) struct vfio_pci_dma_buf *tmp; down_write(&vdev->memory_lock); + + /* + * Drain any active mappings via the revoke path. The move is + * idempotent for dma-bufs already in the revoked state and + * leaves every priv with the kref re-armed and the completion + * ready, so cleanup itself does not need to participate in kref + * bookkeeping. + */ + vfio_pci_dma_buf_move(vdev, true); + list_for_each_entry_safe(priv, tmp, &vdev->dmabufs, dmabufs_elm) { if (!get_file_active(&priv->dmabuf->file)) continue; - dma_resv_lock(priv->dmabuf->resv, NULL); list_del_init(&priv->dmabufs_elm); priv->vdev = NULL; - priv->revoked = true; - dma_buf_invalidate_mappings(priv->dmabuf); - dma_resv_wait_timeout(priv->dmabuf->resv, - DMA_RESV_USAGE_BOOKKEEP, false, - MAX_SCHEDULE_TIMEOUT); - dma_resv_unlock(priv->dmabuf->resv); - kref_put(&priv->kref, vfio_pci_dma_buf_done); - wait_for_completion(&priv->comp); vfio_device_put_registration(&vdev->vdev); fput(priv->dmabuf->file); } diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c index 4251ee03e146..3bfbb879a005 100644 --- a/drivers/vfio/pci/vfio_pci_rdwr.c +++ b/drivers/vfio/pci/vfio_pci_rdwr.c @@ -198,27 +198,15 @@ ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem, } EXPORT_SYMBOL_GPL(vfio_pci_core_do_io_rw); +/* + * The barmap is set up in vfio_pci_core_enable(). Callers use this + * function to check that the BAR resources are requested or that the + * pci_iomap() was done. + */ int vfio_pci_core_setup_barmap(struct vfio_pci_core_device *vdev, int bar) { - struct pci_dev *pdev = vdev->pdev; - int ret; - void __iomem *io; - - if (vdev->barmap[bar]) - return 0; - - ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); - if (ret) - return ret; - - io = pci_iomap(pdev, bar, 0); - if (!io) { - pci_release_selected_regions(pdev, 1 << bar); - return -ENOMEM; - } - - vdev->barmap[bar] = io; - + if (IS_ERR(vdev->barmap[bar])) + return PTR_ERR(vdev->barmap[bar]); return 0; } EXPORT_SYMBOL_GPL(vfio_pci_core_setup_barmap); diff --git a/drivers/vfio/pci/virtio/common.h b/drivers/vfio/pci/virtio/common.h index cb3d5e57d3a3..3ccbd49e6abe 100644 --- a/drivers/vfio/pci/virtio/common.h +++ b/drivers/vfio/pci/virtio/common.h @@ -68,7 +68,7 @@ struct virtiovf_migration_file { enum virtiovf_migf_state state; enum virtiovf_load_state load_state; /* synchronize access to the lists */ - spinlock_t list_lock; + struct mutex list_lock; struct list_head buf_list; struct list_head avail_list; struct virtiovf_data_buffer *buf; diff --git a/drivers/vfio/pci/virtio/legacy_io.c b/drivers/vfio/pci/virtio/legacy_io.c index 1ed349a55629..f022301e60d6 100644 --- a/drivers/vfio/pci/virtio/legacy_io.c +++ b/drivers/vfio/pci/virtio/legacy_io.c @@ -34,7 +34,9 @@ virtiovf_issue_legacy_rw_cmd(struct virtiovf_pci_core_device *virtvdev, common = pos < VIRTIO_PCI_CONFIG_OFF(msix_enabled); /* offset within the relevant configuration area */ offset = common ? pos : pos - VIRTIO_PCI_CONFIG_OFF(msix_enabled); - mutex_lock(&virtvdev->bar_mutex); + + guard(mutex)(&virtvdev->bar_mutex); + if (read) { if (common) ret = virtio_pci_admin_legacy_common_io_read(pdev, offset, @@ -43,14 +45,12 @@ virtiovf_issue_legacy_rw_cmd(struct virtiovf_pci_core_device *virtvdev, ret = virtio_pci_admin_legacy_device_io_read(pdev, offset, count, bar0_buf + pos); if (ret) - goto out; + return ret; if (copy_to_user(buf, bar0_buf + pos, count)) - ret = -EFAULT; + return -EFAULT; } else { - if (copy_from_user(bar0_buf + pos, buf, count)) { - ret = -EFAULT; - goto out; - } + if (copy_from_user(bar0_buf + pos, buf, count)) + return -EFAULT; if (common) ret = virtio_pci_admin_legacy_common_io_write(pdev, offset, @@ -59,8 +59,7 @@ virtiovf_issue_legacy_rw_cmd(struct virtiovf_pci_core_device *virtvdev, ret = virtio_pci_admin_legacy_device_io_write(pdev, offset, count, bar0_buf + pos); } -out: - mutex_unlock(&virtvdev->bar_mutex); + return ret; } diff --git a/drivers/vfio/pci/virtio/migrate.c b/drivers/vfio/pci/virtio/migrate.c index 7e11834ad512..4b1e5502f0a7 100644 --- a/drivers/vfio/pci/virtio/migrate.c +++ b/drivers/vfio/pci/virtio/migrate.c @@ -142,9 +142,8 @@ end: static void virtiovf_put_data_buffer(struct virtiovf_data_buffer *buf) { - spin_lock_irq(&buf->migf->list_lock); + guard(mutex)(&buf->migf->list_lock); list_add_tail(&buf->buf_elm, &buf->migf->avail_list); - spin_unlock_irq(&buf->migf->list_lock); } static int @@ -170,21 +169,21 @@ virtiovf_get_data_buffer(struct virtiovf_migration_file *migf, size_t length) INIT_LIST_HEAD(&free_list); - spin_lock_irq(&migf->list_lock); + mutex_lock(&migf->list_lock); list_for_each_entry_safe(buf, temp_buf, &migf->avail_list, buf_elm) { list_del_init(&buf->buf_elm); if (buf->allocated_length >= length) { - spin_unlock_irq(&migf->list_lock); + mutex_unlock(&migf->list_lock); goto found; } /* * Prevent holding redundant buffers. Put in a free - * list and call at the end not under the spin lock + * list and call at the end not under the mutex * (&migf->list_lock) to minimize its scope usage. */ list_add(&buf->buf_elm, &free_list); } - spin_unlock_irq(&migf->list_lock); + mutex_unlock(&migf->list_lock); buf = virtiovf_alloc_data_buffer(migf, length); found: @@ -225,10 +224,9 @@ static void virtiovf_clean_migf_resources(struct virtiovf_migration_file *migf) static void virtiovf_disable_fd(struct virtiovf_migration_file *migf) { - mutex_lock(&migf->lock); + guard(mutex)(&migf->lock); migf->state = VIRTIOVF_MIGF_STATE_ERROR; migf->filp->f_pos = 0; - mutex_unlock(&migf->lock); } static void virtiovf_disable_fds(struct virtiovf_pci_core_device *virtvdev) @@ -295,6 +293,7 @@ static int virtiovf_release_file(struct inode *inode, struct file *filp) struct virtiovf_migration_file *migf = filp->private_data; virtiovf_disable_fd(migf); + mutex_destroy(&migf->list_lock); mutex_destroy(&migf->lock); kfree(migf); return 0; @@ -305,32 +304,27 @@ virtiovf_get_data_buff_from_pos(struct virtiovf_migration_file *migf, loff_t pos, bool *end_of_data) { struct virtiovf_data_buffer *buf; - bool found = false; *end_of_data = false; - spin_lock_irq(&migf->list_lock); + guard(mutex)(&migf->list_lock); + if (list_empty(&migf->buf_list)) { *end_of_data = true; - goto end; + return NULL; } buf = list_first_entry(&migf->buf_list, struct virtiovf_data_buffer, buf_elm); if (pos >= buf->start_pos && - pos < buf->start_pos + buf->length) { - found = true; - goto end; - } + pos < buf->start_pos + buf->length) + return buf; /* * As we use a stream based FD we may expect having the data always * on first chunk */ migf->state = VIRTIOVF_MIGF_STATE_ERROR; - -end: - spin_unlock_irq(&migf->list_lock); - return found ? buf : NULL; + return NULL; } static ssize_t virtiovf_buf_read(struct virtiovf_data_buffer *vhca_buf, @@ -369,10 +363,9 @@ static ssize_t virtiovf_buf_read(struct virtiovf_data_buffer *vhca_buf, } if (*pos >= vhca_buf->start_pos + vhca_buf->length) { - spin_lock_irq(&vhca_buf->migf->list_lock); + guard(mutex)(&vhca_buf->migf->list_lock); list_del_init(&vhca_buf->buf_elm); list_add_tail(&vhca_buf->buf_elm, &vhca_buf->migf->avail_list); - spin_unlock_irq(&vhca_buf->migf->list_lock); } return done; @@ -391,11 +384,10 @@ static ssize_t virtiovf_save_read(struct file *filp, char __user *buf, size_t le return -ESPIPE; pos = &filp->f_pos; - mutex_lock(&migf->lock); - if (migf->state == VIRTIOVF_MIGF_STATE_ERROR) { - done = -ENODEV; - goto out_unlock; - } + guard(mutex)(&migf->lock); + + if (migf->state == VIRTIOVF_MIGF_STATE_ERROR) + return -ENODEV; while (len) { ssize_t count; @@ -404,34 +396,24 @@ static ssize_t virtiovf_save_read(struct file *filp, char __user *buf, size_t le if (first_loop_call) { first_loop_call = false; /* Temporary end of file as part of PRE_COPY */ - if (end_of_data && migf->state == VIRTIOVF_MIGF_STATE_PRECOPY) { - done = -ENOMSG; - goto out_unlock; - } - if (end_of_data && migf->state != VIRTIOVF_MIGF_STATE_COMPLETE) { - done = -EINVAL; - goto out_unlock; - } + if (end_of_data && migf->state == VIRTIOVF_MIGF_STATE_PRECOPY) + return -ENOMSG; + if (end_of_data && migf->state != VIRTIOVF_MIGF_STATE_COMPLETE) + return -EINVAL; } if (end_of_data) - goto out_unlock; + return done; - if (!vhca_buf) { - done = -EINVAL; - goto out_unlock; - } + if (!vhca_buf) + return -EINVAL; count = virtiovf_buf_read(vhca_buf, &buf, &len, pos); - if (count < 0) { - done = count; - goto out_unlock; - } + if (count < 0) + return count; done += count; } -out_unlock: - mutex_unlock(&migf->lock); return done; } @@ -549,9 +531,10 @@ virtiovf_add_buf_header(struct virtiovf_data_buffer *header_buf, header_buf->length = sizeof(header); header_buf->start_pos = header_buf->migf->max_pos; migf->max_pos += header_buf->length; - spin_lock_irq(&migf->list_lock); - list_add_tail(&header_buf->buf_elm, &migf->buf_list); - spin_unlock_irq(&migf->list_lock); + + scoped_guard(mutex, &migf->list_lock) + list_add_tail(&header_buf->buf_elm, &migf->buf_list); + return 0; } @@ -616,9 +599,10 @@ virtiovf_read_device_context_chunk(struct virtiovf_migration_file *migf, buf->start_pos = buf->migf->max_pos; migf->max_pos += buf->length; - spin_lock(&migf->list_lock); - list_add_tail(&buf->buf_elm, &migf->buf_list); - spin_unlock_irq(&migf->list_lock); + + scoped_guard(mutex, &migf->list_lock) + list_add_tail(&buf->buf_elm, &migf->buf_list); + return 0; out_header: @@ -687,7 +671,7 @@ virtiovf_pci_save_device_data(struct virtiovf_pci_core_device *virtvdev, mutex_init(&migf->lock); INIT_LIST_HEAD(&migf->buf_list); INIT_LIST_HEAD(&migf->avail_list); - spin_lock_init(&migf->list_lock); + mutex_init(&migf->list_lock); migf->virtvdev = virtvdev; lockdep_assert_held(&virtvdev->state_mutex); @@ -1077,7 +1061,7 @@ virtiovf_pci_resume_device_data(struct virtiovf_pci_core_device *virtvdev) mutex_init(&migf->lock); INIT_LIST_HEAD(&migf->buf_list); INIT_LIST_HEAD(&migf->avail_list); - spin_lock_init(&migf->list_lock); + mutex_init(&migf->list_lock); buf = virtiovf_alloc_data_buffer(migf, VIRTIOVF_TARGET_INITIAL_BUF_SIZE); if (IS_ERR(buf)) { diff --git a/drivers/vfio/vfio.h b/drivers/vfio/vfio.h index 0854f3fa1a22..e4b72e79b7e3 100644 --- a/drivers/vfio/vfio.h +++ b/drivers/vfio/vfio.h @@ -377,7 +377,7 @@ int vfio_device_fops_cdev_open(struct inode *inode, struct file *filep); long vfio_df_ioctl_bind_iommufd(struct vfio_device_file *df, struct vfio_device_bind_iommufd __user *arg); void vfio_df_unbind_iommufd(struct vfio_device_file *df); -int vfio_cdev_init(struct class *device_class); +int vfio_cdev_init(void); void vfio_cdev_cleanup(void); #else static inline void vfio_init_device_cdev(struct vfio_device *device) @@ -410,7 +410,7 @@ static inline void vfio_df_unbind_iommufd(struct vfio_device_file *df) { } -static inline int vfio_cdev_init(struct class *device_class) +static inline int vfio_cdev_init(void) { return 0; } diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c index 8666f35fb3f0..6222376ab6ab 100644 --- a/drivers/vfio/vfio_main.c +++ b/drivers/vfio/vfio_main.c @@ -49,7 +49,6 @@ #define VFIO_MAGIC 0x5646494f /* "VFIO" */ static struct vfio { - struct class *device_class; struct ida device_ida; struct vfsmount *vfs_mount; int fs_count; @@ -64,6 +63,16 @@ MODULE_PARM_DESC(enable_unsafe_noiommu_mode, "Enable UNSAFE, no-IOMMU mode. Thi static DEFINE_XARRAY(vfio_device_set_xa); +static char *vfio_device_devnode(const struct device *dev, umode_t *mode) +{ + return kasprintf(GFP_KERNEL, "vfio/devices/%s", dev_name(dev)); +} + +static const struct class vfio_device_class = { + .name = "vfio-dev", + .devnode = vfio_device_devnode +}; + int vfio_assign_device_set(struct vfio_device *device, void *set_id) { unsigned long idx = (unsigned long)set_id; @@ -299,7 +308,7 @@ static int vfio_init_device(struct vfio_device *device, struct device *dev, device_initialize(&device->device); device->device.release = vfio_device_release; - device->device.class = vfio.device_class; + device->device.class = &vfio_device_class; device->device.parent = device->dev; return 0; @@ -1804,13 +1813,11 @@ static int __init vfio_init(void) goto err_virqfd; /* /sys/class/vfio-dev/vfioX */ - vfio.device_class = class_create("vfio-dev"); - if (IS_ERR(vfio.device_class)) { - ret = PTR_ERR(vfio.device_class); + ret = class_register(&vfio_device_class); + if (ret) goto err_dev_class; - } - ret = vfio_cdev_init(vfio.device_class); + ret = vfio_cdev_init(); if (ret) goto err_alloc_dev_chrdev; @@ -1819,8 +1826,7 @@ static int __init vfio_init(void) return 0; err_alloc_dev_chrdev: - class_destroy(vfio.device_class); - vfio.device_class = NULL; + class_unregister(&vfio_device_class); err_dev_class: vfio_virqfd_exit(); err_virqfd: @@ -1833,8 +1839,7 @@ static void __exit vfio_cleanup(void) vfio_debugfs_remove_root(); ida_destroy(&vfio.device_ida); vfio_cdev_cleanup(); - class_destroy(vfio.device_class); - vfio.device_class = NULL; + class_unregister(&vfio_device_class); vfio_virqfd_exit(); vfio_group_cleanup(); xa_destroy(&vfio_device_set_xa); diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 80965181920c..c6536cad9c4f 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -560,7 +560,7 @@ static void vhost_net_busy_poll(struct vhost_net *net, busyloop_timeout = poll_rx ? rvq->busyloop_timeout: tvq->busyloop_timeout; - preempt_disable(); + migrate_disable(); endtime = busy_clock() + busyloop_timeout; while (vhost_can_busy_poll(endtime)) { @@ -577,7 +577,7 @@ static void vhost_net_busy_poll(struct vhost_net *net, cpu_relax(); } - preempt_enable(); + migrate_enable(); if (poll_rx || sock_has_rx_data(sock)) vhost_net_busy_poll_try_queue(net, vq); diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 1c22880e7226..9a1253b9d8c5 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -2950,6 +2950,8 @@ static const struct target_core_fabric_ops vhost_scsi_ops = { .tfc_tpg_base_attrs = vhost_scsi_tpg_attrs, .tfc_tpg_attrib_attrs = vhost_scsi_tpg_attrib_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, + .direct_compl_supp = 1, .default_submit_type = TARGET_QUEUE_SUBMIT, .direct_submit_supp = 1, }; diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c index aaa824437a2a..423513d68b5b 100644 --- a/drivers/video/backlight/apple_bl.c +++ b/drivers/video/backlight/apple_bl.c @@ -24,6 +24,7 @@ #include #include #include +#include #include static struct backlight_device *apple_backlight_device; @@ -134,7 +135,7 @@ static const struct hw_data nvidia_chipset_data = { .set_brightness = nvidia_chipset_set_brightness, }; -static int apple_bl_add(struct acpi_device *dev) +static int apple_bl_probe(struct platform_device *pdev) { struct backlight_properties props; struct pci_dev *host; @@ -193,7 +194,7 @@ static int apple_bl_add(struct acpi_device *dev) return 0; } -static void apple_bl_remove(struct acpi_device *dev) +static void apple_bl_remove(struct platform_device *pdev) { backlight_device_unregister(apple_backlight_device); @@ -206,12 +207,12 @@ static const struct acpi_device_id apple_bl_ids[] = { {"", 0}, }; -static struct acpi_driver apple_bl_driver = { - .name = "Apple backlight", - .ids = apple_bl_ids, - .ops = { - .add = apple_bl_add, - .remove = apple_bl_remove, +static struct platform_driver apple_bl_driver = { + .probe = apple_bl_probe, + .remove = apple_bl_remove, + .driver = { + .name = "Apple backlight", + .acpi_match_table = apple_bl_ids, }, }; @@ -224,12 +225,12 @@ static int __init apple_bl_init(void) if (acpi_video_get_backlight_type() != acpi_backlight_vendor) return -ENODEV; - return acpi_bus_register_driver(&apple_bl_driver); + return platform_driver_register(&apple_bl_driver); } static void __exit apple_bl_exit(void) { - acpi_bus_unregister_driver(&apple_bl_driver); + platform_driver_unregister(&apple_bl_driver); } module_init(apple_bl_init); diff --git a/drivers/video/backlight/sky81452-backlight.c b/drivers/video/backlight/sky81452-backlight.c index 2749231f0385..b2679b24de14 100644 --- a/drivers/video/backlight/sky81452-backlight.c +++ b/drivers/video/backlight/sky81452-backlight.c @@ -202,6 +202,9 @@ static struct sky81452_bl_platform_data *sky81452_bl_parse_dt( pdata->dpwm_mode = of_property_read_bool(np, "skyworks,dpwm-mode"); pdata->phase_shift = of_property_read_bool(np, "skyworks,phase-shift"); pdata->gpiod_enable = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); + if (IS_ERR(pdata->gpiod_enable)) + return dev_err_cast_probe(dev, pdata->gpiod_enable, + "failed to get gpio\n"); ret = of_property_count_u32_elems(np, "led-sources"); if (ret < 0) { diff --git a/drivers/video/fbdev/aty/radeon_monitor.c b/drivers/video/fbdev/aty/radeon_monitor.c index df55e23b7a5a..621d13a1a1d9 100644 --- a/drivers/video/fbdev/aty/radeon_monitor.c +++ b/drivers/video/fbdev/aty/radeon_monitor.c @@ -654,7 +654,7 @@ static void radeon_fixup_panel_info(struct radeonfb_info *rinfo) { #ifdef CONFIG_PPC /* - * LCD Flat panels should use fixed dividers, we enfore that on + * LCD Flat panels should use fixed dividers, we enforce that on * PPC only for now... */ if (!rinfo->panel_info.use_bios_dividers && rinfo->mon1_type == MT_LCD diff --git a/drivers/video/fbdev/clps711x-fb.c b/drivers/video/fbdev/clps711x-fb.c index 5e61a349a4ab..7a7db7100499 100644 --- a/drivers/video/fbdev/clps711x-fb.c +++ b/drivers/video/fbdev/clps711x-fb.c @@ -216,12 +216,9 @@ static int clps711x_fb_probe(struct platform_device *pdev) cfb = info->par; platform_set_drvdata(pdev, info); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - goto out_fb_release; - cfb->base = devm_ioremap(dev, res->start, resource_size(res)); - if (!cfb->base) { - ret = -ENOMEM; + cfb->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(cfb->base)) { + ret = PTR_ERR(cfb->base); goto out_fb_release; } diff --git a/drivers/video/fbdev/cobalt_lcdfb.c b/drivers/video/fbdev/cobalt_lcdfb.c index 308967b5096a..f7faa95fefd3 100644 --- a/drivers/video/fbdev/cobalt_lcdfb.c +++ b/drivers/video/fbdev/cobalt_lcdfb.c @@ -295,19 +295,13 @@ static int cobalt_lcdfb_probe(struct platform_device *dev) if (!info) return -ENOMEM; - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) { + info->screen_base = devm_platform_get_and_ioremap_resource(dev, 0, &res); + if (IS_ERR(info->screen_base)) { framebuffer_release(info); - return -EBUSY; + return PTR_ERR(info->screen_base); } info->screen_size = resource_size(res); - info->screen_base = devm_ioremap(&dev->dev, res->start, - info->screen_size); - if (!info->screen_base) { - framebuffer_release(info); - return -ENOMEM; - } info->fbops = &cobalt_lcd_fbops; info->fix = cobalt_lcdfb_fix; diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index a12dd25ab697..fd00b86e1ae6 100644 --- a/drivers/video/fbdev/core/fb_defio.c +++ b/drivers/video/fbdev/core/fb_defio.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/fbdev/hgafb.c b/drivers/video/fbdev/hgafb.c index 14418aa3791a..d32fd1c5217c 100644 --- a/drivers/video/fbdev/hgafb.c +++ b/drivers/video/fbdev/hgafb.c @@ -276,7 +276,7 @@ static void hga_blank(int blank_mode) spin_unlock_irqrestore(&hga_reg_lock, flags); } -static int hga_card_detect(void) +static int hga_card_detect(struct platform_device *pdev) { int count = 0; void __iomem *p, *q; @@ -284,6 +284,11 @@ static int hga_card_detect(void) hga_vram_len = 0x08000; + if (!devm_request_mem_region(&pdev->dev, 0xb0000, hga_vram_len, "hgafb")) { + dev_err(&pdev->dev, "cannot reserve video memory at 0xb0000\n"); + return -EBUSY; + } + hga_vram = ioremap(0xb0000, hga_vram_len); if (!hga_vram) return -ENOMEM; @@ -568,7 +573,7 @@ static int hgafb_probe(struct platform_device *pdev) struct fb_info *info; int ret; - ret = hga_card_detect(); + ret = hga_card_detect(pdev); if (ret) return ret; diff --git a/drivers/video/fbdev/offb.c b/drivers/video/fbdev/offb.c index f85428e13996..166b2dff36f5 100644 --- a/drivers/video/fbdev/offb.c +++ b/drivers/video/fbdev/offb.c @@ -640,8 +640,13 @@ static void offb_init_nodriver(struct platform_device *parent, struct device_nod vid = be32_to_cpup(vidp); did = be32_to_cpup(didp); pdev = pci_get_device(vid, did, NULL); - if (!pdev || pci_enable_device(pdev)) + if (!pdev) return; + + if (pci_enable_device(pdev)) { + pci_dev_put(pdev); + return; + } } #endif /* kludge for valkyrie */ diff --git a/drivers/video/fbdev/savage/savagefb_driver.c b/drivers/video/fbdev/savage/savagefb_driver.c index ac41f8f37589..c2f79357c8da 100644 --- a/drivers/video/fbdev/savage/savagefb_driver.c +++ b/drivers/video/fbdev/savage/savagefb_driver.c @@ -2322,6 +2322,8 @@ static int savagefb_probe(struct pci_dev *dev, const struct pci_device_id *id) failed: #ifdef CONFIG_FB_SAVAGE_I2C savagefb_delete_i2c_busses(info); + fb_destroy_modelist(&info->modelist); + fb_destroy_modedb(info->monspecs.modedb); #endif fb_alloc_cmap(&info->cmap, 0, 0); savage_unmap_video(info); diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c index c341d76bc564..fdbb8671a810 100644 --- a/drivers/video/fbdev/udlfb.c +++ b/drivers/video/fbdev/udlfb.c @@ -321,12 +321,32 @@ static int dlfb_set_video_mode(struct dlfb_data *dlfb, return retval; } +static void dlfb_vm_open(struct vm_area_struct *vma) +{ + struct dlfb_data *dlfb = vma->vm_private_data; + + atomic_inc(&dlfb->mmap_count); +} + +static void dlfb_vm_close(struct vm_area_struct *vma) +{ + struct dlfb_data *dlfb = vma->vm_private_data; + + atomic_dec(&dlfb->mmap_count); +} + +static const struct vm_operations_struct dlfb_vm_ops = { + .open = dlfb_vm_open, + .close = dlfb_vm_close, +}; + static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) { unsigned long start = vma->vm_start; unsigned long size = vma->vm_end - vma->vm_start; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long page, pos; + struct dlfb_data *dlfb = info->par; if (info->fbdefio) return fb_deferred_io_mmap(info, vma); @@ -358,6 +378,9 @@ static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) size = 0; } + vma->vm_ops = &dlfb_vm_ops; + vma->vm_private_data = dlfb; + atomic_inc(&dlfb->mmap_count); return 0; } @@ -1176,7 +1199,6 @@ static void dlfb_deferred_vfree(struct dlfb_data *dlfb, void *mem) /* * Assumes &info->lock held by caller - * Assumes no active clients have framebuffer open */ static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info, u32 new_len) { @@ -1188,6 +1210,13 @@ static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info new_len = PAGE_ALIGN(new_len); if (new_len > old_len) { + if (atomic_read(&dlfb->mmap_count) > 0) { + dev_warn(info->dev, + "refusing realloc: %d active mmaps\n", + atomic_read(&dlfb->mmap_count)); + return -EBUSY; + } + /* * Alloc system memory for virtual framebuffer */ diff --git a/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c b/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c index 0c9ea24a200c..66d00b6ceb78 100644 --- a/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c +++ b/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c @@ -157,7 +157,8 @@ static int arm_cca_report_new(struct tsm_report *report, void *data) } while (info.result == RSI_INCOMPLETE && info.offset < RSI_GRANULE_SIZE); - if (info.result != RSI_SUCCESS) { + /* Break out in case of failure */ + if (info.result != RSI_SUCCESS && info.result != RSI_INCOMPLETE) { ret = -ENXIO; token_size = 0; goto exit_free_granule_page; diff --git a/drivers/virt/coco/pkvm-guest/Kconfig b/drivers/virt/coco/pkvm-guest/Kconfig index d2f344f1f98f..928b8e1668cc 100644 --- a/drivers/virt/coco/pkvm-guest/Kconfig +++ b/drivers/virt/coco/pkvm-guest/Kconfig @@ -1,6 +1,6 @@ config ARM_PKVM_GUEST bool "Arm pKVM protected guest driver" - depends on ARM64 + depends on ARM64 && DMA_RESTRICTED_POOL help Protected guests running under the pKVM hypervisor on arm64 are isolated from the host and must issue hypercalls to enable diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c index e001e6769a43..910a1de0d5a7 100644 --- a/drivers/virt/coco/sev-guest/sev-guest.c +++ b/drivers/virt/coco/sev-guest/sev-guest.c @@ -176,7 +176,6 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques struct snp_guest_req req = {}; int ret, npages = 0, resp_len; sockptr_t certs_address; - struct page *page; if (sockptr_is_null(io->req_data) || sockptr_is_null(io->resp_data)) return -EINVAL; @@ -211,16 +210,15 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques * zeros to indicate that certificate data was not provided. */ npages = report_req->certs_len >> PAGE_SHIFT; - page = alloc_pages(GFP_KERNEL_ACCOUNT | __GFP_ZERO, - get_order(report_req->certs_len)); - if (!page) + req.certs_data = alloc_pages_exact(npages << PAGE_SHIFT, + GFP_KERNEL_ACCOUNT | __GFP_ZERO); + if (!req.certs_data) return -ENOMEM; - req.certs_data = page_address(page); ret = set_memory_decrypted((unsigned long)req.certs_data, npages); if (ret) { pr_err("failed to mark page shared, ret=%d\n", ret); - __free_pages(page, get_order(report_req->certs_len)); + free_pages_exact(req.certs_data, npages << PAGE_SHIFT); return -EFAULT; } @@ -277,7 +275,7 @@ e_free_data: if (set_memory_encrypted((unsigned long)req.certs_data, npages)) WARN_ONCE(ret, "failed to restore encryption mask (leak it)\n"); else - __free_pages(page, get_order(report_req->certs_len)); + free_pages_exact(req.certs_data, npages << PAGE_SHIFT); } return ret; } diff --git a/drivers/virt/coco/tsm-core.c b/drivers/virt/coco/tsm-core.c index 98dcf7d836df..e784993353d8 100644 --- a/drivers/virt/coco/tsm-core.c +++ b/drivers/virt/coco/tsm-core.c @@ -9,7 +9,11 @@ #include #include -static struct class *tsm_class; +static void tsm_release(struct device *); +static const struct class tsm_class = { + .name = "tsm", + .dev_release = tsm_release +}; static DEFINE_IDA(tsm_ida); static int match_id(struct device *dev, const void *data) @@ -22,7 +26,7 @@ static int match_id(struct device *dev, const void *data) struct tsm_dev *find_tsm_dev(int id) { - struct device *dev = class_find_device(tsm_class, NULL, &id, match_id); + struct device *dev = class_find_device(&tsm_class, NULL, &id, match_id); if (!dev) return NULL; @@ -46,7 +50,7 @@ static struct tsm_dev *alloc_tsm_dev(struct device *parent) tsm_dev->id = id; dev = &tsm_dev->dev; dev->parent = parent; - dev->class = tsm_class; + dev->class = &tsm_class; device_initialize(dev); return no_free_ptr(tsm_dev); @@ -114,18 +118,13 @@ static void tsm_release(struct device *dev) static int __init tsm_init(void) { - tsm_class = class_create("tsm"); - if (IS_ERR(tsm_class)) - return PTR_ERR(tsm_class); - - tsm_class->dev_release = tsm_release; - return 0; + return class_register(&tsm_class); } module_init(tsm_init) static void __exit tsm_exit(void) { - class_destroy(tsm_class); + class_unregister(&tsm_class); } module_exit(tsm_exit) diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index 35e75d07ef5d..aa1f57f74397 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -1022,11 +1022,8 @@ static int ds_probe(struct usb_interface *intf, if (!dev) return -ENOMEM; - dev->udev = usb_get_dev(udev); - if (!dev->udev) { - err = -ENOMEM; - goto err_out_free; - } + dev->udev = udev; + memset(dev->ep, 0, sizeof(dev->ep)); usb_set_intfdata(intf, dev); @@ -1085,9 +1082,8 @@ static int ds_probe(struct usb_interface *intf, err_out_clear: usb_set_intfdata(intf, NULL); - usb_put_dev(dev->udev); -err_out_free: kfree(dev); + return err; } @@ -1107,7 +1103,6 @@ static void ds_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); - usb_put_dev(dev->udev); kfree(dev); } diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 15ba592236e8..725a49a0eee7 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -1620,6 +1620,12 @@ static void privcmd_close(struct vm_area_struct *vma) kvfree(pages); } +static int privcmd_may_split(struct vm_area_struct *area, unsigned long addr) +{ + /* Forbid splitting, avoids double free via privcmd_close(). */ + return -EINVAL; +} + static vm_fault_t privcmd_fault(struct vm_fault *vmf) { printk(KERN_DEBUG "privcmd_fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n", @@ -1631,6 +1637,7 @@ static vm_fault_t privcmd_fault(struct vm_fault *vmf) static const struct vm_operations_struct privcmd_vm_ops = { .close = privcmd_close, + .may_split = privcmd_may_split, .fault = privcmd_fault }; diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 4a734ee38994..2cbf2b588f5b 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -262,10 +262,12 @@ static dma_addr_t xen_swiotlb_map_phys(struct device *dev, phys_addr_t phys, done: if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) { - if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dev_addr)))) + if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dev_addr)))) { arch_sync_dma_for_device(phys, size, dir); - else + arch_sync_dma_flush(); + } else { xen_dma_sync_for_device(dev, dev_addr, size, dir); + } } return dev_addr; } @@ -287,10 +289,12 @@ static void xen_swiotlb_unmap_phys(struct device *hwdev, dma_addr_t dev_addr, BUG_ON(dir == DMA_NONE); if (!dev_is_dma_coherent(hwdev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) { - if (pfn_valid(PFN_DOWN(dma_to_phys(hwdev, dev_addr)))) + if (pfn_valid(PFN_DOWN(dma_to_phys(hwdev, dev_addr)))) { arch_sync_dma_for_cpu(paddr, size, dir); - else + arch_sync_dma_flush(); + } else { xen_dma_sync_for_cpu(hwdev, dev_addr, size, dir); + } } /* NOTE: We use dev_addr here, not paddr! */ @@ -308,10 +312,12 @@ xen_swiotlb_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr, struct io_tlb_pool *pool; if (!dev_is_dma_coherent(dev)) { - if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dma_addr)))) + if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dma_addr)))) { arch_sync_dma_for_cpu(paddr, size, dir); - else + arch_sync_dma_flush(); + } else { xen_dma_sync_for_cpu(dev, dma_addr, size, dir); + } } pool = xen_swiotlb_find_pool(dev, dma_addr); @@ -331,10 +337,12 @@ xen_swiotlb_sync_single_for_device(struct device *dev, dma_addr_t dma_addr, __swiotlb_sync_single_for_device(dev, paddr, size, dir, pool); if (!dev_is_dma_coherent(dev)) { - if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dma_addr)))) + if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dma_addr)))) { arch_sync_dma_for_device(paddr, size, dir); - else + arch_sync_dma_flush(); + } else { xen_dma_sync_for_device(dev, dma_addr, size, dir); + } } } diff --git a/drivers/xen/sys-hypervisor.c b/drivers/xen/sys-hypervisor.c index b1bb01ba82f8..91923242a5ae 100644 --- a/drivers/xen/sys-hypervisor.c +++ b/drivers/xen/sys-hypervisor.c @@ -366,6 +366,8 @@ static ssize_t buildid_show(struct hyp_sysfs_attr *attr, char *buffer) ret = sprintf(buffer, ""); return ret; } + if (ret > PAGE_SIZE) + return -ENOSPC; buildid = kmalloc(sizeof(*buildid) + ret, GFP_KERNEL); if (!buildid) @@ -373,8 +375,10 @@ static ssize_t buildid_show(struct hyp_sysfs_attr *attr, char *buffer) buildid->len = ret; ret = HYPERVISOR_xen_version(XENVER_build_id, buildid); - if (ret > 0) - ret = sprintf(buffer, "%s", buildid->buf); + if (ret > 0) { + /* Build id is binary, not a string. */ + memcpy(buffer, buildid->buf, ret); + } kfree(buildid); return ret; diff --git a/drivers/xen/xen-acpi-pad.c b/drivers/xen/xen-acpi-pad.c index 75a39862c1df..5b98e0e93807 100644 --- a/drivers/xen/xen-acpi-pad.c +++ b/drivers/xen/xen-acpi-pad.c @@ -110,9 +110,13 @@ static void acpi_pad_notify(acpi_handle handle, u32 event, static int acpi_pad_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_device *device; acpi_status status; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS); diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index 3035c7d0f1b7..e33f95c91b09 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -1832,6 +1832,7 @@ static const struct target_core_fabric_ops scsiback_ops = { .tfc_tpg_base_attrs = scsiback_tpg_attrs, .tfc_tpg_param_attrs = scsiback_param_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_DIRECT_SUBMIT, .direct_submit_supp = 1, }; diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 057487efaaeb..acda42499ca9 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -413,7 +413,11 @@ static void v9fs_apply_options(struct v9fs_session_info *v9ses, /* * Note that we must |= flags here as session_init already * set basic flags. This adds in flags from parsed options. + * Default access flags must be cleared if session options + * changes them to avoid mangling the setting. */ + if (ctx->session_opts.flags & V9FS_ACCESS_MASK) + v9ses->flags &= ~V9FS_ACCESS_MASK; v9ses->flags |= ctx->session_opts.flags; #ifdef CONFIG_9P_FSCACHE v9ses->cachetag = ctx->session_opts.cachetag; diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 0a1c4f7cb001..431f24938a1d 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -312,6 +312,9 @@ static int v9fs_init_fs_context(struct fs_context *fc) if (!ctx) return -ENOMEM; + fc->ops = &v9fs_context_ops; + fc->fs_private = ctx; + /* initialize core options */ ctx->session_opts.afid = ~0; ctx->session_opts.cache = CACHE_NONE; @@ -345,9 +348,6 @@ static int v9fs_init_fs_context(struct fs_context *fc) ctx->rdma_opts.timeout = P9_RDMA_TIMEOUT; ctx->rdma_opts.privport = false; - fc->ops = &v9fs_context_ops; - fc->fs_private = ctx; - return 0; error: fc->need_free = 1; diff --git a/fs/Kconfig b/fs/Kconfig index 0bfdaecaa877..43cb06de297f 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -152,6 +152,7 @@ menu "DOS/FAT/EXFAT/NT Filesystems" source "fs/fat/Kconfig" source "fs/exfat/Kconfig" +source "fs/ntfs/Kconfig" source "fs/ntfs3/Kconfig" endmenu diff --git a/fs/Makefile b/fs/Makefile index cf4a745e9679..ae1b07f9c6a0 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -90,6 +90,7 @@ obj-$(CONFIG_NLS) += nls/ obj-y += unicode/ obj-$(CONFIG_SMBFS) += smb/ obj-$(CONFIG_HPFS_FS) += hpfs/ +obj-$(CONFIG_NTFS_FS) += ntfs/ obj-$(CONFIG_NTFS3_FS) += ntfs3/ obj-$(CONFIG_UFS_FS) += ufs/ obj-$(CONFIG_EFS_FS) += efs/ diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 2c5b2076acf9..a4cd0a5159dd 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -317,6 +317,9 @@ static int adfs_validate_bblk(struct super_block *sb, struct buffer_head *bh, if (adfs_checkdiscrecord(dr)) return -EILSEQ; + if ((dr->nzones | dr->nzones_high << 8) == 0) + return -EILSEQ; + *drp = dr; return 0; } diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c index ee512baf57e5..bed4fc805e8e 100644 --- a/fs/affs/amigaffs.c +++ b/fs/affs/amigaffs.c @@ -126,7 +126,7 @@ affs_fix_dcache(struct inode *inode, u32 entry_ino) { struct dentry *dentry; spin_lock(&inode->i_lock); - hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { + for_each_alias(dentry, inode) { if (entry_ino == (u32)(long)dentry->d_fsdata) { dentry->d_fsdata = (void *)(unsigned long)inode->i_ino; break; diff --git a/fs/aio.c b/fs/aio.c index ba9b9fa2446b..722476560848 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -422,7 +422,8 @@ static int aio_ring_mremap(struct vm_area_struct *vma) ctx = rcu_dereference(table->table[i]); if (ctx && ctx->aio_ring_file == file) { - if (!atomic_read(&ctx->dead)) { + if (!atomic_read(&ctx->dead) && + (ctx->mmap_size == (vma->vm_end - vma->vm_start))) { ctx->user_id = ctx->mmap_base = vma->vm_start; res = 0; } @@ -447,7 +448,7 @@ static const struct vm_operations_struct aio_ring_vm_ops = { static int aio_ring_mmap_prepare(struct vm_area_desc *desc) { - vma_desc_set_flags(desc, VMA_DONTEXPAND_BIT); + vma_desc_set_flags(desc, VMA_DONTEXPAND_BIT, VMA_DONTCOPY_BIT); desc->vm_ops = &aio_ring_vm_ops; return 0; } diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index e6f5a17a13e3..b611c64119db 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -2412,29 +2412,25 @@ static struct btrfs_block_group *btrfs_create_block_group( */ static int check_chunk_block_group_mappings(struct btrfs_fs_info *fs_info) { - u64 start = 0; + struct rb_node *node; int ret = 0; - while (1) { + /* + * This is called during mount from btrfs_read_block_groups(), before + * any background threads are started, so no concurrent writers can + * modify the mapping_tree. No lock is needed here. + */ + for (node = rb_first_cached(&fs_info->mapping_tree); node; node = rb_next(node)) { struct btrfs_chunk_map *map; struct btrfs_block_group *bg; - /* - * btrfs_find_chunk_map() will return the first chunk map - * intersecting the range, so setting @length to 1 is enough to - * get the first chunk. - */ - map = btrfs_find_chunk_map(fs_info, start, 1); - if (!map) - break; - + map = rb_entry(node, struct btrfs_chunk_map, rb_node); bg = btrfs_lookup_block_group(fs_info, map->start); if (unlikely(!bg)) { btrfs_err(fs_info, "chunk start=%llu len=%llu doesn't have corresponding block group", map->start, map->chunk_len); ret = -EUCLEAN; - btrfs_free_chunk_map(map); break; } if (unlikely(bg->start != map->start || bg->length != map->chunk_len || @@ -2447,12 +2443,9 @@ static int check_chunk_block_group_mappings(struct btrfs_fs_info *fs_info) bg->start, bg->length, bg->flags & BTRFS_BLOCK_GROUP_TYPE_MASK); ret = -EUCLEAN; - btrfs_free_chunk_map(map); btrfs_put_block_group(bg); break; } - start = map->start + map->chunk_len; - btrfs_free_chunk_map(map); btrfs_put_block_group(bg); } return ret; diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index b2393a48a8fe..a02b62e0a8f3 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -407,22 +407,18 @@ static noinline int add_ra_bio_pages(struct inode *inode, end_index = (i_size_read(inode) - 1) >> PAGE_SHIFT; - /* - * Avoid direct reclaim when the caller does not allow it. Since - * add_ra_bio_pages() is always speculative, suppress allocation warnings - * in either case. - */ + /* Avoid direct reclaim when the caller does not allow it. */ + constraint_gfp = ~__GFP_FS; + cache_gfp = GFP_NOFS | __GFP_NOWARN; if (!direct_reclaim) { - constraint_gfp = ~(__GFP_FS | __GFP_DIRECT_RECLAIM) | __GFP_NOWARN; - cache_gfp = (GFP_NOFS & ~__GFP_DIRECT_RECLAIM) | __GFP_NOWARN; - } else { - constraint_gfp = (~__GFP_FS) | __GFP_NOWARN; - cache_gfp = GFP_NOFS | __GFP_NOWARN; + constraint_gfp &= ~__GFP_DIRECT_RECLAIM; + cache_gfp &= ~__GFP_DIRECT_RECLAIM; } while (cur < compressed_end) { pgoff_t page_end; pgoff_t pg_index = cur >> PAGE_SHIFT; + gfp_t masked_constraint_gfp; u32 add_size; if (pg_index > end_index) @@ -449,8 +445,14 @@ static noinline int add_ra_bio_pages(struct inode *inode, continue; } - folio = filemap_alloc_folio(mapping_gfp_constraint(mapping, constraint_gfp), - 0, NULL); + /* + * Since add_ra_bio_pages() is always speculative, suppress + * allocation warnings. + */ + masked_constraint_gfp = mapping_gfp_constraint(mapping, constraint_gfp); + masked_constraint_gfp |= __GFP_NOWARN; + + folio = filemap_alloc_folio(masked_constraint_gfp, 0, NULL); if (!folio) break; diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 8a11be02eeb9..c0a30bb213d7 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -4686,6 +4686,7 @@ static void btrfs_destroy_marked_extents(struct btrfs_fs_info *fs_info, free_extent_buffer_stale(eb); } } + btrfs_extent_io_tree_release(dirty_pages); } static void btrfs_destroy_pinned_extent(struct btrfs_fs_info *fs_info, diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index ca3e4b99aec2..2275189b7860 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4641,7 +4641,8 @@ int try_release_extent_buffer(struct folio *folio) * to read the block we will not block on anything. */ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, - u64 bytenr, u64 owner_root, u64 gen, int level) + u64 bytenr, u64 owner_root, u64 gen, int level, + const struct btrfs_key *first_key) { struct btrfs_tree_parent_check check = { .level = level, @@ -4650,6 +4651,11 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, struct extent_buffer *eb; int ret; + if (first_key) { + memcpy(&check.first_key, first_key, sizeof(struct btrfs_key)); + check.has_first_key = true; + } + eb = btrfs_find_create_tree_block(fs_info, bytenr, owner_root, level); if (IS_ERR(eb)) return; @@ -4677,9 +4683,13 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, */ void btrfs_readahead_node_child(struct extent_buffer *node, int slot) { + struct btrfs_key node_key; + + btrfs_node_key_to_cpu(node, &node_key, slot); btrfs_readahead_tree_block(node->fs_info, btrfs_node_blockptr(node, slot), btrfs_header_owner(node), btrfs_node_ptr_generation(node, slot), - btrfs_header_level(node) - 1); + btrfs_header_level(node) - 1, + &node_key); } diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index fd209233317f..b310a5145cf6 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -287,7 +287,8 @@ static inline void wait_on_extent_buffer_writeback(struct extent_buffer *eb) } void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, - u64 bytenr, u64 owner_root, u64 gen, int level); + u64 bytenr, u64 owner_root, u64 gen, int level, + const struct btrfs_key *first_key); void btrfs_readahead_node_child(struct extent_buffer *node, int slot); /* Note: this can be used in for loops without caching the value in a variable. */ diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 095a561d733f..9284c0a81bef 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -1318,7 +1318,7 @@ static void btrfs_extent_map_shrinker_worker(struct work_struct *work) if (trace_btrfs_extent_map_shrinker_scan_enter_enabled()) { s64 nr = percpu_counter_sum_positive(&fs_info->evictable_extent_maps); - trace_btrfs_extent_map_shrinker_scan_enter(fs_info, nr); + trace_call__btrfs_extent_map_shrinker_scan_enter(fs_info, nr); } while (ctx.scanned < ctx.nr_to_scan && !btrfs_fs_closing(fs_info)) { @@ -1358,7 +1358,7 @@ static void btrfs_extent_map_shrinker_worker(struct work_struct *work) if (trace_btrfs_extent_map_shrinker_scan_exit_enabled()) { s64 nr = percpu_counter_sum_positive(&fs_info->evictable_extent_maps); - trace_btrfs_extent_map_shrinker_scan_exit(fs_info, nr_dropped, nr); + trace_call__btrfs_extent_map_shrinker_scan_exit(fs_info, nr_dropped, nr); } atomic64_set(&fs_info->em_shrinker_nr_to_scan, 0); diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 9efd1ec90f03..472b3060e5ac 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -259,7 +259,11 @@ int btrfs_convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans, nr++; path->slots[0]--; } else { - ASSERT(0); + btrfs_err(fs_info, "unexpected free space tree key type %u", + found_key.type); + ret = -EUCLEAN; + btrfs_abort_transaction(trans, ret); + goto out; } } @@ -405,7 +409,11 @@ int btrfs_convert_free_space_to_extents(struct btrfs_trans_handle *trans, nr++; } else { - ASSERT(0); + btrfs_err(fs_info, "unexpected free space tree key type %u", + found_key.type); + ret = -EUCLEAN; + btrfs_abort_transaction(trans, ret); + goto out; } } @@ -1518,7 +1526,11 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans, nr++; path->slots[0]--; } else { - ASSERT(0); + btrfs_err(trans->fs_info, "unexpected free space tree key type %u", + found_key.type); + ret = -EUCLEAN; + btrfs_abort_transaction(trans, ret); + return ret; } } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 40474014c03f..75136a172710 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1153,7 +1153,7 @@ out_free_reserve: NULL, &cached, EXTENT_LOCKED | EXTENT_DELALLOC | EXTENT_DELALLOC_NEW | - EXTENT_DEFRAG | EXTENT_DO_ACCOUNTING, + EXTENT_DEFRAG | EXTENT_CLEAR_META_RESV, PAGE_UNLOCK | PAGE_START_WRITEBACK | PAGE_END_WRITEBACK); if (async_extent->cb) @@ -4959,6 +4959,8 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry) if (ret) goto out; + btrfs_record_unlink_dir(trans, dir, inode, false); + /* now the directory is empty */ ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); if (!ret) @@ -9297,10 +9299,38 @@ next: if (!(mode & FALLOC_FL_KEEP_SIZE) && (actual_len > inode->i_size) && (cur_offset > inode->i_size)) { + u64 range_start; + u64 range_end; + if (cur_offset > actual_len) i_size = actual_len; else i_size = cur_offset; + + /* + * Make sure the file_extent_tree covers the entire + * range [old_i_size, new_i_size) before we update + * disk_i_size. Without this, a previous KEEP_SIZE + * prealloc that extended past i_size (and was lost + * across umount/mount because file_extent_tree is + * only populated up to round_up(i_size) on inode + * load) can leave a gap inside this range. That gap + * would cause btrfs_inode_safe_disk_i_size_write() + * (via find_contiguous_extent_bit() starting at 0) + * to truncate disk_i_size to the start of the gap, + * making the persisted size smaller than i_size. + */ + range_start = round_down(inode->i_size, fs_info->sectorsize); + range_end = round_up(i_size, fs_info->sectorsize); + ret = btrfs_inode_set_file_extent_range(BTRFS_I(inode), + range_start, range_end - range_start); + if (ret) { + btrfs_abort_transaction(trans, ret); + if (own_trans) + btrfs_end_transaction(trans); + break; + } + i_size_write(inode, i_size); btrfs_inode_safe_disk_i_size_write(BTRFS_I(inode), 0); } diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index b2e447f5005c..a39460bf68a7 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -5102,7 +5102,6 @@ static int btrfs_ioctl_subvol_sync(struct btrfs_fs_info *fs_info, void __user *a return 0; } -#ifdef CONFIG_BTRFS_EXPERIMENTAL static int btrfs_ioctl_shutdown(struct btrfs_fs_info *fs_info, unsigned long arg) { int ret = 0; @@ -5134,10 +5133,12 @@ static int btrfs_ioctl_shutdown(struct btrfs_fs_info *fs_info, unsigned long arg case BTRFS_SHUTDOWN_FLAGS_NOLOGFLUSH: btrfs_force_shutdown(fs_info); break; + default: + ret = -EINVAL; + break; } return ret; } -#endif long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) @@ -5294,10 +5295,8 @@ long btrfs_ioctl(struct file *file, unsigned int #endif case BTRFS_IOC_SUBVOL_SYNC_WAIT: return btrfs_ioctl_subvol_sync(fs_info, argp); -#ifdef CONFIG_BTRFS_EXPERIMENTAL case BTRFS_IOC_SHUTDOWN: return btrfs_ioctl_shutdown(fs_info, arg); -#endif } return -ENOTTY; diff --git a/fs/btrfs/raid-stripe-tree.c b/fs/btrfs/raid-stripe-tree.c index 638c4ad572c9..4b0186c83ad1 100644 --- a/fs/btrfs/raid-stripe-tree.c +++ b/fs/btrfs/raid-stripe-tree.c @@ -45,8 +45,11 @@ static int btrfs_partially_delete_raid_extent(struct btrfs_trans_handle *trans, for (int i = 0; i < btrfs_num_raid_stripes(item_size); i++) { struct btrfs_raid_stride *stride = &extent->strides[i]; + u64 devid; u64 phys; + devid = btrfs_raid_stride_devid(leaf, stride); + btrfs_set_stack_raid_stride_devid(&newitem->strides[i], devid); phys = btrfs_raid_stride_physical(leaf, stride) + frontpad; btrfs_set_stack_raid_stride_physical(&newitem->strides[i], phys); } @@ -95,14 +98,26 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le while (1) { key.objectid = start; key.type = BTRFS_RAID_STRIPE_KEY; - key.offset = 0; + key.offset = (u64)-1; ret = btrfs_search_slot(trans, stripe_root, &key, path, -1, 1); if (ret < 0) break; - if (path->slots[0] == btrfs_header_nritems(path->nodes[0])) - path->slots[0]--; + /* + * Search with offset=(u64)-1 ensures we land on the correct + * leaf even when the target entry is the first item on a leaf. + * Since no real entry has offset=(u64)-1, ret is always 1 and + * slot points past the last entry with objectid==start (or + * past the end of the leaf if that entry is the last item). + * Back up one slot to find the actual entry. + */ + if (path->slots[0] == 0) { + /* No entry with objectid <= start exists. */ + ret = 0; + break; + } + path->slots[0]--; leaf = path->nodes[0]; slot = path->slots[0]; @@ -123,7 +138,7 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le */ if (found_start > start) { if (slot == 0) { - ret = btrfs_previous_item(stripe_root, path, start, + ret = btrfs_previous_item(stripe_root, path, 0, BTRFS_RAID_STRIPE_KEY); if (ret) { if (ret > 0) @@ -139,7 +154,10 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le btrfs_item_key_to_cpu(leaf, &key, slot); found_start = key.objectid; found_end = found_start + key.offset; - ASSERT(found_start <= start); + if (found_start > start || found_end <= start) { + ret = -ENOENT; + break; + } } if (key.type != BTRFS_RAID_STRIPE_KEY) @@ -176,9 +194,19 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le /* The "right" item. */ ret = btrfs_duplicate_item(trans, stripe_root, path, &newkey); + if (ret == -EAGAIN) { + btrfs_release_path(path); + continue; + } if (ret) break; + /* + * btrfs_duplicate_item() may have triggered a leaf + * split via setup_leaf_for_split(), so we must refresh + * our leaf pointer from the path. + */ + leaf = path->nodes[0]; item_size = btrfs_item_size(leaf, path->slots[0]); extent = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_stripe_extent); @@ -195,8 +223,9 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le /* The "left" item. */ path->slots[0]--; btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); - btrfs_partially_delete_raid_extent(trans, path, &key, - diff_start, 0); + ret = btrfs_partially_delete_raid_extent(trans, path, + &key, + diff_start, 0); break; } @@ -212,8 +241,11 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le if (found_start < start) { u64 diff_start = start - found_start; - btrfs_partially_delete_raid_extent(trans, path, &key, - diff_start, 0); + ret = btrfs_partially_delete_raid_extent(trans, path, + &key, + diff_start, 0); + if (ret) + break; start += (key.offset - diff_start); length -= (key.offset - diff_start); @@ -236,9 +268,10 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le if (found_end > end) { u64 diff_end = found_end - end; - btrfs_partially_delete_raid_extent(trans, path, &key, - key.offset - length, - length); + ret = btrfs_partially_delete_raid_extent(trans, path, + &key, + key.offset - length, + length); ASSERT(key.offset - diff_end == length); break; } diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index e31d57d6ab1e..08ee8f316d96 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -617,26 +617,6 @@ static void cache_rbio(struct btrfs_raid_bio *rbio) spin_unlock(&table->cache_lock); } -/* - * helper function to run the xor_blocks api. It is only - * able to do MAX_XOR_BLOCKS at a time, so we need to - * loop through. - */ -static void run_xor(void **pages, int src_cnt, ssize_t len) -{ - int src_off = 0; - int xor_src_cnt = 0; - void *dest = pages[src_cnt]; - - while(src_cnt > 0) { - xor_src_cnt = min(src_cnt, MAX_XOR_BLOCKS); - xor_blocks(xor_src_cnt, len, dest, pages + src_off); - - src_cnt -= xor_src_cnt; - src_off += xor_src_cnt; - } -} - /* * Returns true if the bio list inside this rbio covers an entire stripe (no * rmw required). @@ -1434,7 +1414,8 @@ static void generate_pq_vertical_step(struct btrfs_raid_bio *rbio, unsigned int } else { /* raid5 */ memcpy(pointers[rbio->nr_data], pointers[0], step); - run_xor(pointers + 1, rbio->nr_data - 1, step); + xor_gen(pointers[rbio->nr_data], pointers + 1, rbio->nr_data - 1, + step); } for (stripe = stripe - 1; stripe >= 0; stripe--) kunmap_local(pointers[stripe]); @@ -1738,7 +1719,7 @@ static void submit_read_wait_bio_list(struct btrfs_raid_bio *rbio, struct raid56_bio_trace_info trace_info = { 0 }; bio_get_trace_info(rbio, bio, &trace_info); - trace_raid56_read(rbio, bio, &trace_info); + trace_call__raid56_read(rbio, bio, &trace_info); } submit_bio(bio); } @@ -2029,7 +2010,7 @@ pstripe: pointers[rbio->nr_data - 1] = p; /* Xor in the rest */ - run_xor(pointers, rbio->nr_data - 1, step); + xor_gen(p, pointers, rbio->nr_data - 1, step); } cleanup: @@ -2423,7 +2404,7 @@ static void submit_write_bios(struct btrfs_raid_bio *rbio, struct raid56_bio_trace_info trace_info = { 0 }; bio_get_trace_info(rbio, bio, &trace_info); - trace_raid56_write(rbio, bio, &trace_info); + trace_call__raid56_write(rbio, bio, &trace_info); } submit_bio(bio); } @@ -2667,7 +2648,7 @@ static bool verify_one_parity_step(struct btrfs_raid_bio *rbio, } else { /* RAID5. */ memcpy(pointers[nr_data], pointers[0], step); - run_xor(pointers + 1, nr_data - 1, step); + xor_gen(pointers[nr_data], pointers + 1, nr_data - 1, step); } /* Check scrubbing parity and repair it. */ diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 1c42c5180bdd..3ebaf5880125 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -2607,7 +2607,7 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans, if (!block->key_ready) btrfs_readahead_tree_block(fs_info, block->bytenr, block->owner, 0, - block->level); + block->level, NULL); } /* Get first keys */ @@ -3876,7 +3876,7 @@ static int add_remap_tree_entries(struct btrfs_trans_handle *trans, struct btrfs ret = btrfs_insert_empty_items(trans, fs_info->remap_root, path, &batch); btrfs_release_path(path); - if (num_entries <= max_items) + if (ret || num_entries <= max_items) break; num_entries -= max_items; @@ -4174,6 +4174,12 @@ static int move_existing_remap(struct btrfs_fs_info *fs_info, return ret; } + if (ins.offset < length) { + spin_lock(&sinfo->lock); + btrfs_space_info_update_bytes_may_use(sinfo, ins.offset - length); + spin_unlock(&sinfo->lock); + } + dest_addr = ins.objectid; dest_length = ins.offset; @@ -5000,6 +5006,12 @@ static int do_remap_reloc_trans(struct btrfs_fs_info *fs_info, return ret; } + if (ins.offset < remap_length) { + spin_lock(&sinfo->lock); + btrfs_space_info_update_bytes_may_use(sinfo, ins.offset - remap_length); + spin_unlock(&sinfo->lock); + } + made_reservation = true; new_addr = ins.objectid; @@ -5023,21 +5035,27 @@ static int do_remap_reloc_trans(struct btrfs_fs_info *fs_info, if (bg_needs_free_space) { ret = btrfs_add_block_group_free_space(trans, dest_bg); - if (ret) + if (ret) { + btrfs_abort_transaction(trans, ret); goto fail; + } } ret = copy_remapped_data(fs_info, start, new_addr, length); - if (ret) + if (ret) { + btrfs_abort_transaction(trans, ret); goto fail; + } ret = btrfs_remove_from_free_space_tree(trans, new_addr, length); - if (ret) + if (ret) { + btrfs_abort_transaction(trans, ret); goto fail; + } ret = add_remap_entry(trans, path, src_bg, start, new_addr, length); if (ret) { - btrfs_add_to_free_space_tree(trans, new_addr, length); + btrfs_abort_transaction(trans, ret); goto fail; } diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 248adb785051..194f581b36f3 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -1293,14 +1293,13 @@ static int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans) blk_finish_plug(&plug); ret2 = btrfs_wait_extents(fs_info, dirty_pages); - btrfs_extent_io_tree_release(&trans->transaction->dirty_pages); - if (ret) return ret; - else if (ret2) + if (ret2) return ret2; - else - return 0; + + btrfs_extent_io_tree_release(&trans->transaction->dirty_pages); + return 0; } /* diff --git a/fs/buffer.c b/fs/buffer.c index 4d7f84e77d2f..b0b3792b1496 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -719,8 +719,15 @@ void mmb_mark_buffer_dirty(struct buffer_head *bh, mark_buffer_dirty(bh); if (!bh->b_mmb) { spin_lock(&mmb->lock); + /* + * For a corrupted filesystem with multiply claimed blocks this + * can fail. Avoid corrupting the linked list in that case. + */ + if (cmpxchg(&bh->b_mmb, NULL, mmb) != NULL) { + spin_unlock(&mmb->lock); + return; + } list_move_tail(&bh->b_assoc_buffers, &mmb->list); - bh->b_mmb = mmb; spin_unlock(&mmb->lock); } } @@ -822,8 +829,7 @@ struct buffer_head *folio_alloc_buffers(struct folio *folio, unsigned long size, long offset; struct mem_cgroup *memcg, *old_memcg; - /* The folio lock pins the memcg */ - memcg = folio_memcg(folio); + memcg = get_mem_cgroup_from_folio(folio); old_memcg = set_active_memcg(memcg); head = NULL; @@ -844,6 +850,7 @@ struct buffer_head *folio_alloc_buffers(struct folio *folio, unsigned long size, } out: set_active_memcg(old_memcg); + mem_cgroup_put(memcg); return head; /* * In case anything failed, we just free everything we got. diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile index 1f77ca04c426..ebb29d11ac22 100644 --- a/fs/ceph/Makefile +++ b/fs/ceph/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_CEPH_FS) += ceph.o ceph-y := super.o inode.o dir.o file.o locks.o addr.o ioctl.o \ export.o caps.o snap.o xattr.o quota.o io.o \ mds_client.o mdsmap.o strings.o ceph_frag.o \ - debugfs.o util.o metric.o + debugfs.o util.o metric.o subvolume_metrics.o ceph-$(CONFIG_CEPH_FSCACHE) += cache.o ceph-$(CONFIG_CEPH_FS_POSIX_ACL) += acl.o diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index bbeafbc777ee..0a86f672cc09 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -19,6 +19,7 @@ #include "mds_client.h" #include "cache.h" #include "metric.h" +#include "subvolume_metrics.h" #include "crypto.h" #include #include @@ -259,6 +260,10 @@ static void finish_netfs_read(struct ceph_osd_request *req) osd_data->length), false); } if (err > 0) { + ceph_subvolume_metrics_record_io(fsc->mdsc, ceph_inode(inode), + false, err, + req->r_start_latency, + req->r_end_latency); subreq->transferred = err; err = 0; } @@ -823,6 +828,10 @@ static int write_folio_nounlock(struct folio *folio, ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, len, err); + if (err >= 0 && len > 0) + ceph_subvolume_metrics_record_io(fsc->mdsc, ci, true, len, + req->r_start_latency, + req->r_end_latency); fscrypt_free_bounce_page(bounce_page); ceph_osdc_put_request(req); if (err == 0) @@ -963,6 +972,11 @@ static void writepages_finish(struct ceph_osd_request *req) ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, len, rc); + if (rc >= 0 && len > 0) + ceph_subvolume_metrics_record_io(mdsc, ci, true, len, + req->r_start_latency, + req->r_end_latency); + ceph_put_wrbuffer_cap_refs(ci, total_pages, snapc); osd_data = osd_req_op_extent_osd_data(req, 0); @@ -1322,6 +1336,7 @@ void ceph_process_folio_batch(struct address_space *mapping, ceph_wbc, folio); if (rc == -ENODATA) { folio_unlock(folio); + folio_put(folio); ceph_wbc->fbatch.folios[i] = NULL; continue; } else if (rc == -E2BIG) { @@ -1332,6 +1347,7 @@ void ceph_process_folio_batch(struct address_space *mapping, if (!folio_clear_dirty_for_io(folio)) { doutc(cl, "%p !folio_clear_dirty_for_io\n", folio); folio_unlock(folio); + folio_put(folio); ceph_wbc->fbatch.folios[i] = NULL; continue; } @@ -1365,6 +1381,10 @@ void ceph_process_folio_batch(struct address_space *mapping, rc = move_dirty_folio_in_page_array(mapping, wbc, ceph_wbc, folio); if (rc) { + /* Did we just begin a new contiguous op? Nevermind! */ + if (ceph_wbc->len == 0) + ceph_wbc->num_ops--; + folio_redirty_for_writepage(wbc, folio); folio_unlock(folio); break; diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 7dc307790240..e2463f93cf6b 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -9,11 +9,13 @@ #include #include #include +#include #include #include #include #include +#include #include "super.h" @@ -21,6 +23,36 @@ #include "mds_client.h" #include "metric.h" +#include "subvolume_metrics.h" + +/** + * struct ceph_session_feature_desc - Maps feature bits to names for debugfs + * @bit: Feature bit number from enum ceph_feature_type (see mds_client.h) + * @name: Human-readable feature name for debugfs output + * + * Used by metric_features_show() to display negotiated session features. + */ +struct ceph_session_feature_desc { + unsigned int bit; + const char *name; +}; + +static const struct ceph_session_feature_desc ceph_session_feature_table[] = { + { CEPHFS_FEATURE_METRIC_COLLECT, "METRIC_COLLECT" }, + { CEPHFS_FEATURE_REPLY_ENCODING, "REPLY_ENCODING" }, + { CEPHFS_FEATURE_RECLAIM_CLIENT, "RECLAIM_CLIENT" }, + { CEPHFS_FEATURE_LAZY_CAP_WANTED, "LAZY_CAP_WANTED" }, + { CEPHFS_FEATURE_MULTI_RECONNECT, "MULTI_RECONNECT" }, + { CEPHFS_FEATURE_DELEG_INO, "DELEG_INO" }, + { CEPHFS_FEATURE_ALTERNATE_NAME, "ALTERNATE_NAME" }, + { CEPHFS_FEATURE_NOTIFY_SESSION_STATE, "NOTIFY_SESSION_STATE" }, + { CEPHFS_FEATURE_OP_GETVXATTR, "OP_GETVXATTR" }, + { CEPHFS_FEATURE_32BITS_RETRY_FWD, "32BITS_RETRY_FWD" }, + { CEPHFS_FEATURE_NEW_SNAPREALM_INFO, "NEW_SNAPREALM_INFO" }, + { CEPHFS_FEATURE_HAS_OWNER_UIDGID, "HAS_OWNER_UIDGID" }, + { CEPHFS_FEATURE_MDS_AUTH_CAPS_CHECK, "MDS_AUTH_CAPS_CHECK" }, + { CEPHFS_FEATURE_SUBVOLUME_METRICS, "SUBVOLUME_METRICS" }, +}; static int mdsmap_show(struct seq_file *s, void *p) { @@ -360,6 +392,59 @@ static int status_show(struct seq_file *s, void *p) return 0; } +static int subvolume_metrics_show(struct seq_file *s, void *p) +{ + struct ceph_fs_client *fsc = s->private; + struct ceph_mds_client *mdsc = fsc->mdsc; + struct ceph_subvol_metric_snapshot *snapshot = NULL; + u32 nr = 0; + u64 total_sent = 0; + u64 nonzero_sends = 0; + u32 i; + + if (!mdsc) { + seq_puts(s, "mds client unavailable\n"); + return 0; + } + + mutex_lock(&mdsc->subvol_metrics_last_mutex); + if (mdsc->subvol_metrics_last && mdsc->subvol_metrics_last_nr) { + nr = mdsc->subvol_metrics_last_nr; + snapshot = kmemdup_array(mdsc->subvol_metrics_last, nr, + sizeof(*snapshot), GFP_KERNEL); + if (!snapshot) + nr = 0; + } + total_sent = mdsc->subvol_metrics_sent; + nonzero_sends = mdsc->subvol_metrics_nonzero_sends; + mutex_unlock(&mdsc->subvol_metrics_last_mutex); + + seq_puts(s, "Last sent subvolume metrics:\n"); + if (!nr) { + seq_puts(s, " (none)\n"); + } else { + seq_puts(s, " subvol_id rd_ops wr_ops rd_bytes wr_bytes rd_lat_us wr_lat_us\n"); + for (i = 0; i < nr; i++) { + const struct ceph_subvol_metric_snapshot *e = &snapshot[i]; + + seq_printf(s, " %-18llu %-9llu %-9llu %-14llu %-14llu %-14llu %-14llu\n", + e->subvolume_id, + e->read_ops, e->write_ops, + e->read_bytes, e->write_bytes, + e->read_latency_us, e->write_latency_us); + } + } + kfree(snapshot); + + seq_puts(s, "\nStatistics:\n"); + seq_printf(s, " entries_sent: %llu\n", total_sent); + seq_printf(s, " non_zero_sends: %llu\n", nonzero_sends); + + seq_puts(s, "\nPending (unsent) subvolume metrics:\n"); + ceph_subvolume_metrics_dump(&mdsc->subvol_metrics, s); + return 0; +} + DEFINE_SHOW_ATTRIBUTE(mdsmap); DEFINE_SHOW_ATTRIBUTE(mdsc); DEFINE_SHOW_ATTRIBUTE(caps); @@ -369,7 +454,72 @@ DEFINE_SHOW_ATTRIBUTE(metrics_file); DEFINE_SHOW_ATTRIBUTE(metrics_latency); DEFINE_SHOW_ATTRIBUTE(metrics_size); DEFINE_SHOW_ATTRIBUTE(metrics_caps); +DEFINE_SHOW_ATTRIBUTE(subvolume_metrics); +static int metric_features_show(struct seq_file *s, void *p) +{ + struct ceph_fs_client *fsc = s->private; + struct ceph_mds_client *mdsc = fsc->mdsc; + unsigned long session_features = 0; + bool have_session = false; + bool metric_collect = false; + bool subvol_support = false; + bool metrics_enabled = false; + bool subvol_enabled = false; + int i; + + if (!mdsc) { + seq_puts(s, "mds client unavailable\n"); + return 0; + } + + mutex_lock(&mdsc->mutex); + if (mdsc->metric.session) { + have_session = true; + session_features = mdsc->metric.session->s_features; + } + mutex_unlock(&mdsc->mutex); + + if (have_session) { + metric_collect = + test_bit(CEPHFS_FEATURE_METRIC_COLLECT, + &session_features); + subvol_support = + test_bit(CEPHFS_FEATURE_SUBVOLUME_METRICS, + &session_features); + } + + metrics_enabled = !disable_send_metrics && have_session && metric_collect; + subvol_enabled = metrics_enabled && subvol_support; + + seq_printf(s, + "metrics_enabled: %s (disable_send_metrics=%d, session=%s, metric_collect=%s)\n", + metrics_enabled ? "yes" : "no", + disable_send_metrics ? 1 : 0, + have_session ? "yes" : "no", + metric_collect ? "yes" : "no"); + seq_printf(s, "subvolume_metrics_enabled: %s\n", + subvol_enabled ? "yes" : "no"); + seq_printf(s, "session_feature_bits: 0x%lx\n", session_features); + + if (!have_session) { + seq_puts(s, "(no active MDS session for metrics)\n"); + return 0; + } + + for (i = 0; i < ARRAY_SIZE(ceph_session_feature_table); i++) { + const struct ceph_session_feature_desc *desc = + &ceph_session_feature_table[i]; + bool set = test_bit(desc->bit, &session_features); + + seq_printf(s, " %-24s : %s\n", desc->name, + set ? "yes" : "no"); + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(metric_features); /* * debugfs @@ -404,6 +554,7 @@ void ceph_fs_debugfs_cleanup(struct ceph_fs_client *fsc) debugfs_remove(fsc->debugfs_caps); debugfs_remove(fsc->debugfs_status); debugfs_remove(fsc->debugfs_mdsc); + debugfs_remove(fsc->debugfs_subvolume_metrics); debugfs_remove_recursive(fsc->debugfs_metrics_dir); doutc(fsc->client, "done\n"); } @@ -468,6 +619,12 @@ void ceph_fs_debugfs_init(struct ceph_fs_client *fsc) &metrics_size_fops); debugfs_create_file("caps", 0400, fsc->debugfs_metrics_dir, fsc, &metrics_caps_fops); + debugfs_create_file("metric_features", 0400, fsc->debugfs_metrics_dir, + fsc, &metric_features_fops); + fsc->debugfs_subvolume_metrics = + debugfs_create_file("subvolumes", 0400, + fsc->debugfs_metrics_dir, fsc, + &subvolume_metrics_fops); doutc(fsc->client, "done\n"); } diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index bac9cfb6b982..27ce9e55e947 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -769,7 +769,8 @@ struct dentry *ceph_finish_lookup(struct ceph_mds_request *req, d_drop(dentry); err = -ENOENT; } else { - d_add(dentry, NULL); + if (d_unhashed(dentry)) + d_add(dentry, NULL); } } } @@ -840,7 +841,8 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry, spin_unlock(&ci->i_ceph_lock); doutc(cl, " dir %llx.%llx complete, -ENOENT\n", ceph_vinop(dir)); - d_add(dentry, NULL); + if (d_unhashed(dentry)) + d_add(dentry, NULL); di->lease_shared_gen = atomic_read(&ci->i_shared_gen); return NULL; } diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 5e7c73a29aa3..d54d71669176 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -19,6 +19,25 @@ #include "cache.h" #include "io.h" #include "metric.h" +#include "subvolume_metrics.h" + +/* + * Record I/O for subvolume metrics tracking. + * + * Callers must ensure bytes > 0 for reads (ret > 0 check) to avoid counting + * EOF as an I/O operation. For writes, the condition is (ret >= 0 && len > 0). + */ +static inline void ceph_record_subvolume_io(struct inode *inode, bool is_write, + ktime_t start, ktime_t end, + size_t bytes) +{ + if (!bytes) + return; + + ceph_subvolume_metrics_record_io(ceph_sb_to_mdsc(inode->i_sb), + ceph_inode(inode), + is_write, bytes, start, end); +} static __le32 ceph_flags_sys2wire(struct ceph_mds_client *mdsc, u32 flags) { @@ -1140,6 +1159,15 @@ ssize_t __ceph_sync_read(struct inode *inode, loff_t *ki_pos, req->r_start_latency, req->r_end_latency, read_len, ret); + /* + * Only record subvolume metrics for actual bytes read. + * ret == 0 means EOF (no data), not an I/O operation. + */ + if (ret > 0) + ceph_record_subvolume_io(inode, false, + req->r_start_latency, + req->r_end_latency, + ret); if (ret > 0) objver = req->r_version; @@ -1385,12 +1413,23 @@ static void ceph_aio_complete_req(struct ceph_osd_request *req) /* r_start_latency == 0 means the request was not submitted */ if (req->r_start_latency) { - if (aio_req->write) + if (aio_req->write) { ceph_update_write_metrics(metric, req->r_start_latency, req->r_end_latency, len, rc); - else + if (rc >= 0 && len) + ceph_record_subvolume_io(inode, true, + req->r_start_latency, + req->r_end_latency, + len); + } else { ceph_update_read_metrics(metric, req->r_start_latency, req->r_end_latency, len, rc); + if (rc > 0) + ceph_record_subvolume_io(inode, false, + req->r_start_latency, + req->r_end_latency, + rc); + } } put_bvecs(osd_data->bvec_pos.bvecs, osd_data->num_bvecs, @@ -1614,12 +1653,23 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, ceph_osdc_start_request(req->r_osdc, req); ret = ceph_osdc_wait_request(&fsc->client->osdc, req); - if (write) + if (write) { ceph_update_write_metrics(metric, req->r_start_latency, req->r_end_latency, len, ret); - else + if (ret >= 0 && len) + ceph_record_subvolume_io(inode, true, + req->r_start_latency, + req->r_end_latency, + len); + } else { ceph_update_read_metrics(metric, req->r_start_latency, req->r_end_latency, len, ret); + if (ret > 0) + ceph_record_subvolume_io(inode, false, + req->r_start_latency, + req->r_end_latency, + ret); + } size = i_size_read(inode); if (!write) { @@ -1872,6 +1922,11 @@ ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos, req->r_start_latency, req->r_end_latency, read_len, ret); + if (ret > 0) + ceph_record_subvolume_io(inode, false, + req->r_start_latency, + req->r_end_latency, + ret); /* Ok if object is not already present */ if (ret == -ENOENT) { @@ -2036,6 +2091,11 @@ ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos, ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, len, ret); + if (ret >= 0 && write_len) + ceph_record_subvolume_io(inode, true, + req->r_start_latency, + req->r_end_latency, + write_len); ceph_osdc_put_request(req); if (ret != 0) { doutc(cl, "osd write returned %d\n", ret); diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index d99e12d1100b..22c7da1ea61c 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -638,6 +638,7 @@ struct inode *ceph_alloc_inode(struct super_block *sb) ci->i_max_bytes = 0; ci->i_max_files = 0; + ci->i_subvolume_id = CEPH_SUBVOLUME_ID_NONE; memset(&ci->i_dir_layout, 0, sizeof(ci->i_dir_layout)); memset(&ci->i_cached_layout, 0, sizeof(ci->i_cached_layout)); @@ -742,6 +743,8 @@ void ceph_evict_inode(struct inode *inode) percpu_counter_dec(&mdsc->metric.total_inodes); + ci->i_subvolume_id = CEPH_SUBVOLUME_ID_NONE; + netfs_wait_for_outstanding_io(inode); truncate_inode_pages_final(&inode->i_data); if (inode_state_read_once(inode) & I_PINNING_NETFS_WB) @@ -873,6 +876,40 @@ int ceph_fill_file_size(struct inode *inode, int issued, return queue_trunc; } +/* + * Set the subvolume ID for an inode. + * + * The subvolume_id identifies which CephFS subvolume this inode belongs to. + * CEPH_SUBVOLUME_ID_NONE (0) means unknown/unset - the MDS only sends + * non-zero IDs for inodes within subvolumes. + * + * An inode's subvolume membership is immutable - once an inode is created + * in a subvolume, it stays there. Therefore, if we already have a valid + * (non-zero) subvolume_id and receive a different one, that indicates a bug. + */ +void ceph_inode_set_subvolume(struct inode *inode, u64 subvolume_id) +{ + struct ceph_inode_info *ci; + u64 old; + + if (!inode || subvolume_id == CEPH_SUBVOLUME_ID_NONE) + return; + + ci = ceph_inode(inode); + old = READ_ONCE(ci->i_subvolume_id); + + if (old == subvolume_id) + return; + + if (old != CEPH_SUBVOLUME_ID_NONE) { + /* subvolume_id should not change once set */ + WARN_ON_ONCE(1); + return; + } + + WRITE_ONCE(ci->i_subvolume_id, subvolume_id); +} + void ceph_fill_file_time(struct inode *inode, int issued, u64 time_warp_seq, struct timespec64 *ctime, struct timespec64 *mtime, struct timespec64 *atime) @@ -1076,6 +1113,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page, new_issued = ~issued & info_caps; __ceph_update_quota(ci, iinfo->max_bytes, iinfo->max_files); + ceph_inode_set_subvolume(inode, iinfo->subvolume_id); #ifdef CONFIG_FS_ENCRYPTION if (iinfo->fscrypt_auth_len && @@ -1583,6 +1621,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) goto done; } if (parent_dir) { + ceph_inode_set_subvolume(parent_dir, + rinfo->diri.subvolume_id); err = ceph_fill_inode(parent_dir, NULL, &rinfo->diri, rinfo->dirfrag, session, -1, &req->r_caps_reservation); @@ -1671,6 +1711,7 @@ retry_lookup: BUG_ON(!req->r_target_inode); in = req->r_target_inode; + ceph_inode_set_subvolume(in, rinfo->targeti.subvolume_id); err = ceph_fill_inode(in, req->r_locked_page, &rinfo->targeti, NULL, session, (!test_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags) && diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index b1746273f186..ed17e0023705 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -68,6 +68,21 @@ static void ceph_cap_reclaim_work(struct work_struct *work); static const struct ceph_connection_operations mds_con_ops; +static void ceph_metric_bind_session(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + struct ceph_mds_session *old; + + if (!mdsc || !session || disable_send_metrics) + return; + + old = mdsc->metric.session; + mdsc->metric.session = ceph_get_mds_session(session); + if (old) + ceph_put_mds_session(old); + + metric_schedule_delayed(&mdsc->metric); +} /* * mds reply parsing @@ -96,19 +111,19 @@ bad: return -EIO; } -/* - * parse individual inode info - */ static int parse_reply_info_in(void **p, void *end, struct ceph_mds_reply_info_in *info, - u64 features) + u64 features, + struct ceph_mds_client *mdsc) { int err = 0; u8 struct_v = 0; + u8 struct_compat = 0; + u32 struct_len = 0; + + info->subvolume_id = CEPH_SUBVOLUME_ID_NONE; if (features == (u64)-1) { - u32 struct_len; - u8 struct_compat; ceph_decode_8_safe(p, end, struct_v, bad); ceph_decode_8_safe(p, end, struct_compat, bad); /* struct_v is expected to be >= 1. we only understand @@ -232,6 +247,30 @@ static int parse_reply_info_in(void **p, void *end, info->fscrypt_file_len, bad); } } + + /* + * InodeStat encoding versions: + * v1-v7: various fields added over time + * v8: added optmetadata (versioned sub-structure containing + * optional inode metadata like charmap for case-insensitive + * filesystems). The kernel client doesn't support + * case-insensitive lookups, so we skip this field. + * v9: added subvolume_id (parsed below) + */ + if (struct_v >= 8) { + u32 v8_struct_len; + + /* skip optmetadata versioned sub-structure */ + ceph_decode_skip_8(p, end, bad); /* struct_v */ + ceph_decode_skip_8(p, end, bad); /* struct_compat */ + ceph_decode_32_safe(p, end, v8_struct_len, bad); + ceph_decode_skip_n(p, end, v8_struct_len, bad); + } + + /* struct_v 9 added subvolume_id */ + if (struct_v >= 9) + ceph_decode_64_safe(p, end, info->subvolume_id, bad); + *p = end; } else { /* legacy (unversioned) struct */ @@ -364,12 +403,13 @@ bad: */ static int parse_reply_info_trace(void **p, void *end, struct ceph_mds_reply_info_parsed *info, - u64 features) + u64 features, + struct ceph_mds_client *mdsc) { int err; if (info->head->is_dentry) { - err = parse_reply_info_in(p, end, &info->diri, features); + err = parse_reply_info_in(p, end, &info->diri, features, mdsc); if (err < 0) goto out_bad; @@ -389,7 +429,8 @@ static int parse_reply_info_trace(void **p, void *end, } if (info->head->is_target) { - err = parse_reply_info_in(p, end, &info->targeti, features); + err = parse_reply_info_in(p, end, &info->targeti, features, + mdsc); if (err < 0) goto out_bad; } @@ -410,7 +451,8 @@ out_bad: */ static int parse_reply_info_readdir(void **p, void *end, struct ceph_mds_request *req, - u64 features) + u64 features, + struct ceph_mds_client *mdsc) { struct ceph_mds_reply_info_parsed *info = &req->r_reply_info; struct ceph_client *cl = req->r_mdsc->fsc->client; @@ -525,7 +567,7 @@ static int parse_reply_info_readdir(void **p, void *end, rde->name_len = oname.len; /* inode */ - err = parse_reply_info_in(p, end, &rde->inode, features); + err = parse_reply_info_in(p, end, &rde->inode, features, mdsc); if (err < 0) goto out_bad; /* ceph_readdir_prepopulate() will update it */ @@ -733,7 +775,8 @@ static int parse_reply_info_extra(void **p, void *end, if (op == CEPH_MDS_OP_GETFILELOCK) return parse_reply_info_filelock(p, end, info, features); else if (op == CEPH_MDS_OP_READDIR || op == CEPH_MDS_OP_LSSNAP) - return parse_reply_info_readdir(p, end, req, features); + return parse_reply_info_readdir(p, end, req, features, + req->r_mdsc); else if (op == CEPH_MDS_OP_CREATE) return parse_reply_info_create(p, end, info, features, s); else if (op == CEPH_MDS_OP_GETVXATTR) @@ -762,7 +805,8 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg, ceph_decode_32_safe(&p, end, len, bad); if (len > 0) { ceph_decode_need(&p, end, len, bad); - err = parse_reply_info_trace(&p, p+len, info, features); + err = parse_reply_info_trace(&p, p + len, info, features, + s->s_mdsc); if (err < 0) goto out_bad; } @@ -771,7 +815,7 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg, ceph_decode_32_safe(&p, end, len, bad); if (len > 0) { ceph_decode_need(&p, end, len, bad); - err = parse_reply_info_extra(&p, p+len, req, features, s); + err = parse_reply_info_extra(&p, p + len, req, features, s); if (err < 0) goto out_bad; } @@ -3969,6 +4013,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) goto out_err; } req->r_target_inode = in; + ceph_inode_set_subvolume(in, rinfo->targeti.subvolume_id); } mutex_lock(&session->s_mutex); @@ -4317,6 +4362,11 @@ skip_cap_auths: } mdsc->s_cap_auths_num = cap_auths_num; mdsc->s_cap_auths = cap_auths; + + session->s_features = features; + if (test_bit(CEPHFS_FEATURE_METRIC_COLLECT, + &session->s_features)) + ceph_metric_bind_session(mdsc, session); } if (op == CEPH_SESSION_CLOSE) { ceph_get_mds_session(session); @@ -4343,7 +4393,11 @@ skip_cap_auths: pr_info_client(cl, "mds%d reconnect success\n", session->s_mds); - session->s_features = features; + if (test_bit(CEPHFS_FEATURE_SUBVOLUME_METRICS, + &session->s_features)) + ceph_subvolume_metrics_enable(&mdsc->subvol_metrics, true); + else + ceph_subvolume_metrics_enable(&mdsc->subvol_metrics, false); if (session->s_state == CEPH_MDS_SESSION_OPEN) { pr_notice_client(cl, "mds%d is already opened\n", session->s_mds); @@ -4608,13 +4662,13 @@ static struct dentry* d_find_primary(struct inode *inode) goto out_unlock; if (S_ISDIR(inode->i_mode)) { - alias = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias); + alias = hlist_entry(inode->i_dentry.first, struct dentry, d_alias); if (!IS_ROOT(alias)) dn = dget(alias); goto out_unlock; } - hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) { + for_each_alias(alias, inode) { spin_lock(&alias->d_lock); if (!d_unhashed(alias) && (ceph_dentry(alias)->flags & CEPH_DENTRY_PRIMARY_LINK)) { @@ -4956,7 +5010,7 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, /* placeholder for nr_caps */ err = ceph_pagelist_encode_32(recon_state.pagelist, 0); if (err) - goto fail; + goto fail_clear_cap_reconnect; if (test_bit(CEPHFS_FEATURE_MULTI_RECONNECT, &session->s_features)) { recon_state.msg_version = 3; @@ -5046,6 +5100,10 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, ceph_pagelist_release(recon_state.pagelist); return; +fail_clear_cap_reconnect: + spin_lock(&session->s_cap_lock); + session->s_cap_reconnect = 0; + spin_unlock(&session->s_cap_lock); fail: ceph_msg_put(reply); up_read(&mdsc->snap_rwsem); @@ -5582,6 +5640,12 @@ int ceph_mdsc_init(struct ceph_fs_client *fsc) err = ceph_metric_init(&mdsc->metric); if (err) goto err_mdsmap; + ceph_subvolume_metrics_init(&mdsc->subvol_metrics); + mutex_init(&mdsc->subvol_metrics_last_mutex); + mdsc->subvol_metrics_last = NULL; + mdsc->subvol_metrics_last_nr = 0; + mdsc->subvol_metrics_sent = 0; + mdsc->subvol_metrics_nonzero_sends = 0; spin_lock_init(&mdsc->dentry_list_lock); INIT_LIST_HEAD(&mdsc->dentry_leases); @@ -6115,6 +6179,8 @@ void ceph_mdsc_destroy(struct ceph_fs_client *fsc) ceph_mdsc_stop(mdsc); ceph_metric_destroy(&mdsc->metric); + ceph_subvolume_metrics_destroy(&mdsc->subvol_metrics); + kfree(mdsc->subvol_metrics_last); fsc->mdsc = NULL; kfree(mdsc); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 0428a5eaf28c..4e6c87f8414c 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -18,6 +18,7 @@ #include "mdsmap.h" #include "metric.h" +#include "subvolume_metrics.h" #include "super.h" /* The first 8 bits are reserved for old ceph releases */ @@ -36,8 +37,9 @@ enum ceph_feature_type { CEPHFS_FEATURE_NEW_SNAPREALM_INFO, CEPHFS_FEATURE_HAS_OWNER_UIDGID, CEPHFS_FEATURE_MDS_AUTH_CAPS_CHECK, + CEPHFS_FEATURE_SUBVOLUME_METRICS, - CEPHFS_FEATURE_MAX = CEPHFS_FEATURE_MDS_AUTH_CAPS_CHECK, + CEPHFS_FEATURE_MAX = CEPHFS_FEATURE_SUBVOLUME_METRICS, }; #define CEPHFS_FEATURES_CLIENT_SUPPORTED { \ @@ -54,6 +56,7 @@ enum ceph_feature_type { CEPHFS_FEATURE_32BITS_RETRY_FWD, \ CEPHFS_FEATURE_HAS_OWNER_UIDGID, \ CEPHFS_FEATURE_MDS_AUTH_CAPS_CHECK, \ + CEPHFS_FEATURE_SUBVOLUME_METRICS, \ } /* @@ -118,6 +121,7 @@ struct ceph_mds_reply_info_in { u32 fscrypt_file_len; u64 rsnaps; u64 change_attr; + u64 subvolume_id; }; struct ceph_mds_reply_dir_entry { @@ -536,6 +540,14 @@ struct ceph_mds_client { struct list_head dentry_dir_leases; /* lru list */ struct ceph_client_metric metric; + struct ceph_subvolume_metrics_tracker subvol_metrics; + + /* Subvolume metrics send tracking */ + struct mutex subvol_metrics_last_mutex; + struct ceph_subvol_metric_snapshot *subvol_metrics_last; + u32 subvol_metrics_last_nr; + u64 subvol_metrics_sent; + u64 subvol_metrics_nonzero_sends; spinlock_t snapid_map_lock; struct rb_root snapid_map_tree; diff --git a/fs/ceph/metric.c b/fs/ceph/metric.c index 871c1090e520..b6450fdace94 100644 --- a/fs/ceph/metric.c +++ b/fs/ceph/metric.c @@ -4,10 +4,84 @@ #include #include #include +#include + +#include #include "metric.h" #include "mds_client.h" +static bool metrics_disable_warned; + +static inline u32 ceph_subvolume_entry_payload_len(void) +{ + return sizeof(struct ceph_subvolume_metric_entry_wire); +} + +static inline u32 ceph_subvolume_entry_encoded_len(void) +{ + return CEPH_ENCODING_START_BLK_LEN + + ceph_subvolume_entry_payload_len(); +} + +static inline u32 ceph_subvolume_outer_payload_len(u32 nr_subvols) +{ + /* count is encoded as le64 (size_t on wire) to match FUSE client */ + return sizeof(__le64) + + nr_subvols * ceph_subvolume_entry_encoded_len(); +} + +static inline u32 ceph_subvolume_metric_data_len(u32 nr_subvols) +{ + return CEPH_ENCODING_START_BLK_LEN + + ceph_subvolume_outer_payload_len(nr_subvols); +} + +static inline u32 ceph_subvolume_clamp_u32(u64 val) +{ + return val > U32_MAX ? U32_MAX : (u32)val; +} + +static void ceph_init_subvolume_wire_entry( + struct ceph_subvolume_metric_entry_wire *dst, + const struct ceph_subvol_metric_snapshot *src) +{ + dst->subvolume_id = cpu_to_le64(src->subvolume_id); + dst->read_ops = cpu_to_le32(ceph_subvolume_clamp_u32(src->read_ops)); + dst->write_ops = cpu_to_le32(ceph_subvolume_clamp_u32(src->write_ops)); + dst->read_bytes = cpu_to_le64(src->read_bytes); + dst->write_bytes = cpu_to_le64(src->write_bytes); + dst->read_latency_us = cpu_to_le64(src->read_latency_us); + dst->write_latency_us = cpu_to_le64(src->write_latency_us); + dst->time_stamp = 0; +} + +static int ceph_encode_subvolume_metrics(void **p, void *end, + struct ceph_subvol_metric_snapshot *subvols, + u32 nr_subvols) +{ + u32 i; + + ceph_start_encoding(p, 1, 1, + ceph_subvolume_outer_payload_len(nr_subvols)); + /* count is encoded as le64 (size_t on wire) to match FUSE client */ + ceph_encode_64_safe(p, end, (u64)nr_subvols, enc_err); + + for (i = 0; i < nr_subvols; i++) { + struct ceph_subvolume_metric_entry_wire wire_entry; + + ceph_init_subvolume_wire_entry(&wire_entry, &subvols[i]); + ceph_start_encoding(p, 1, 1, + ceph_subvolume_entry_payload_len()); + ceph_encode_copy_safe(p, end, &wire_entry, + sizeof(wire_entry), enc_err); + } + + return 0; +enc_err: + return -ERANGE; +} + static void ktime_to_ceph_timespec(struct ceph_timespec *ts, ktime_t val) { struct timespec64 t = ktime_to_timespec64(val); @@ -29,10 +103,14 @@ static bool ceph_mdsc_send_metrics(struct ceph_mds_client *mdsc, struct ceph_read_io_size *rsize; struct ceph_write_io_size *wsize; struct ceph_client_metric *m = &mdsc->metric; + struct ceph_subvol_metric_snapshot *subvols = NULL; u64 nr_caps = atomic64_read(&m->total_caps); u32 header_len = sizeof(struct ceph_metric_header); struct ceph_client *cl = mdsc->fsc->client; struct ceph_msg *msg; + u32 nr_subvols = 0; + size_t subvol_len = 0; + void *cursor; s64 sum; s32 items = 0; s32 len; @@ -45,15 +123,42 @@ static bool ceph_mdsc_send_metrics(struct ceph_mds_client *mdsc, } mutex_unlock(&mdsc->mutex); + if (ceph_subvolume_metrics_enabled(&mdsc->subvol_metrics) && + test_bit(CEPHFS_FEATURE_SUBVOLUME_METRICS, &s->s_features)) { + int ret; + + ret = ceph_subvolume_metrics_snapshot(&mdsc->subvol_metrics, + &subvols, &nr_subvols, + true); + if (ret) { + pr_warn_client(cl, "failed to snapshot subvolume metrics: %d\n", + ret); + /* + * On error, ceph_subvolume_metrics_snapshot() guarantees + * *out = NULL and *nr = 0 at function entry, so subvols + * is already NULL here - no cleanup needed. + */ + nr_subvols = 0; + subvols = NULL; + } + } + + if (nr_subvols) { + /* type (le32) + ENCODE_START payload - no metric header */ + subvol_len = sizeof(__le32) + + ceph_subvolume_metric_data_len(nr_subvols); + } + len = sizeof(*head) + sizeof(*cap) + sizeof(*read) + sizeof(*write) + sizeof(*meta) + sizeof(*dlease) + sizeof(*files) + sizeof(*icaps) + sizeof(*inodes) + sizeof(*rsize) - + sizeof(*wsize); + + sizeof(*wsize) + subvol_len; msg = ceph_msg_new(CEPH_MSG_CLIENT_METRICS, len, GFP_NOFS, true); if (!msg) { pr_err_client(cl, "to mds%d, failed to allocate message\n", s->s_mds); + kfree(subvols); return false; } @@ -172,13 +277,56 @@ static bool ceph_mdsc_send_metrics(struct ceph_mds_client *mdsc, wsize->total_size = cpu_to_le64(m->metric[METRIC_WRITE].size_sum); items++; + cursor = wsize + 1; + + if (nr_subvols) { + void *payload; + void *payload_end; + int ret; + + /* Emit only the type (le32), no ver/compat/data_len */ + ceph_encode_32(&cursor, CLIENT_METRIC_TYPE_SUBVOLUME_METRICS); + items++; + + payload = cursor; + payload_end = (char *)payload + + ceph_subvolume_metric_data_len(nr_subvols); + + ret = ceph_encode_subvolume_metrics(&payload, payload_end, + subvols, nr_subvols); + if (ret) { + pr_warn_client(cl, + "failed to encode subvolume metrics\n"); + kfree(subvols); + ceph_msg_put(msg); + return false; + } + + WARN_ON(payload != payload_end); + cursor = payload; + } + put_unaligned_le32(items, &head->num); - msg->front.iov_len = len; + msg->front.iov_len = (char *)cursor - (char *)head; msg->hdr.version = cpu_to_le16(1); msg->hdr.compat_version = cpu_to_le16(1); msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); + ceph_con_send(&s->s_con, msg); + if (nr_subvols) { + mutex_lock(&mdsc->subvol_metrics_last_mutex); + kfree(mdsc->subvol_metrics_last); + mdsc->subvol_metrics_last = subvols; + mdsc->subvol_metrics_last_nr = nr_subvols; + mdsc->subvol_metrics_sent += nr_subvols; + mdsc->subvol_metrics_nonzero_sends++; + mutex_unlock(&mdsc->subvol_metrics_last_mutex); + + subvols = NULL; + } + kfree(subvols); + return true; } @@ -198,9 +346,20 @@ static void metric_get_session(struct ceph_mds_client *mdsc) * Skip it if MDS doesn't support the metric collection, * or the MDS will close the session's socket connection * directly when it get this message. + * + * Also skip sessions that don't support SUBVOLUME_METRICS + * when subvolume metrics collection is enabled. This ensures + * we only send subvolume metrics to MDSs that understand them. + * If no session supports the feature, metrics won't be sent. */ if (check_session_state(s) && test_bit(CEPHFS_FEATURE_METRIC_COLLECT, &s->s_features)) { + if (ceph_subvolume_metrics_enabled(&mdsc->subvol_metrics) && + !test_bit(CEPHFS_FEATURE_SUBVOLUME_METRICS, + &s->s_features)) { + ceph_put_mds_session(s); + continue; + } mdsc->metric.session = s; break; } @@ -217,9 +376,18 @@ static void metric_delayed_work(struct work_struct *work) struct ceph_mds_client *mdsc = container_of(m, struct ceph_mds_client, metric); - if (mdsc->stopping || disable_send_metrics) + if (mdsc->stopping) return; + if (disable_send_metrics) { + if (!metrics_disable_warned) { + pr_info("ceph: metrics sending disabled via module parameter\n"); + metrics_disable_warned = true; + } + return; + } + metrics_disable_warned = false; + if (!m->session || !check_session_state(m->session)) { if (m->session) { ceph_put_mds_session(m->session); @@ -227,10 +395,13 @@ static void metric_delayed_work(struct work_struct *work) } metric_get_session(mdsc); } - if (m->session) { + + if (m->session) ceph_mdsc_send_metrics(mdsc, m->session); - metric_schedule_delayed(m); - } + else + pr_warn_ratelimited("ceph: metrics worker has no MDS session\n"); + + metric_schedule_delayed(m); } int ceph_metric_init(struct ceph_client_metric *m) diff --git a/fs/ceph/metric.h b/fs/ceph/metric.h index 0d0c44bd3332..519cd4d47aaa 100644 --- a/fs/ceph/metric.h +++ b/fs/ceph/metric.h @@ -25,8 +25,9 @@ enum ceph_metric_type { CLIENT_METRIC_TYPE_STDEV_WRITE_LATENCY, CLIENT_METRIC_TYPE_AVG_METADATA_LATENCY, CLIENT_METRIC_TYPE_STDEV_METADATA_LATENCY, + CLIENT_METRIC_TYPE_SUBVOLUME_METRICS, - CLIENT_METRIC_TYPE_MAX = CLIENT_METRIC_TYPE_STDEV_METADATA_LATENCY, + CLIENT_METRIC_TYPE_MAX = CLIENT_METRIC_TYPE_SUBVOLUME_METRICS, }; /* @@ -50,6 +51,7 @@ enum ceph_metric_type { CLIENT_METRIC_TYPE_STDEV_WRITE_LATENCY, \ CLIENT_METRIC_TYPE_AVG_METADATA_LATENCY, \ CLIENT_METRIC_TYPE_STDEV_METADATA_LATENCY, \ + CLIENT_METRIC_TYPE_SUBVOLUME_METRICS, \ \ CLIENT_METRIC_TYPE_MAX, \ } @@ -139,6 +141,41 @@ struct ceph_write_io_size { __le64 total_size; } __packed; +/** + * struct ceph_subvolume_metric_entry_wire - On-wire format sent to MDS + * @subvolume_id: Subvolume identifier + * @read_ops: Read operation count (32-bit, clamped from 64-bit internal) + * @write_ops: Write operation count (32-bit, clamped from 64-bit internal) + * @read_bytes: Total bytes read + * @write_bytes: Total bytes written + * @read_latency_us: Cumulative read latency in microseconds + * @write_latency_us: Cumulative write latency in microseconds + * @time_stamp: Collection timestamp (currently unused, set to 0) + * + * Wire format must match C++ AggregatedIOMetrics struct in MDS. + */ +struct ceph_subvolume_metric_entry_wire { + __le64 subvolume_id; + __le32 read_ops; + __le32 write_ops; + __le64 read_bytes; + __le64 write_bytes; + __le64 read_latency_us; + __le64 write_latency_us; + __le64 time_stamp; +} __packed; + +/* Old struct kept for internal tracking, not used on wire */ +struct ceph_subvolume_metric_entry { + __le64 subvolume_id; + __le64 read_ops; + __le64 write_ops; + __le64 read_bytes; + __le64 write_bytes; + __le64 read_latency_us; + __le64 write_latency_us; +} __packed; + struct ceph_metric_head { __le32 num; /* the number of metrics that will be sent */ } __packed; diff --git a/fs/ceph/quota.c b/fs/ceph/quota.c index 4dc9426643e8..053d5bf0c9f0 100644 --- a/fs/ceph/quota.c +++ b/fs/ceph/quota.c @@ -228,12 +228,19 @@ static int get_quota_realm(struct ceph_mds_client *mdsc, struct inode *inode, restart: realm = ceph_inode(inode)->i_snap_realm; - if (realm) + if (realm) { ceph_get_snap_realm(mdsc, realm); - else - pr_err_ratelimited_client(cl, - "%p %llx.%llx null i_snap_realm\n", - inode, ceph_vinop(inode)); + } else { + /* + * i_snap_realm is NULL when all caps have been released, e.g. + * after an MDS session rejection. This is a transient state; + * the realm will be restored once caps are re-granted. + * Treat it as "no quota realm found". + */ + doutc(cl, "%p %llx.%llx null i_snap_realm\n", + inode, ceph_vinop(inode)); + } + while (realm) { bool has_inode; @@ -340,12 +347,19 @@ static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op, down_read(&mdsc->snap_rwsem); restart: realm = ceph_inode(inode)->i_snap_realm; - if (realm) + if (realm) { ceph_get_snap_realm(mdsc, realm); - else - pr_err_ratelimited_client(cl, - "%p %llx.%llx null i_snap_realm\n", - inode, ceph_vinop(inode)); + } else { + /* + * i_snap_realm is NULL when all caps have been released, e.g. + * after an MDS session rejection. This is a transient state; + * the realm will be restored once caps are re-granted. + * Treat it as "quota not exceeded". + */ + doutc(cl, "%p %llx.%llx null i_snap_realm\n", + inode, ceph_vinop(inode)); + } + while (realm) { bool has_inode; @@ -496,6 +510,9 @@ bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf) u64 total = 0, used, free; bool is_updated = false; + if (!ceph_has_realms_with_quotas(d_inode(fsc->sb->s_root))) + return false; + down_read(&mdsc->snap_rwsem); get_quota_realm(mdsc, d_inode(fsc->sb->s_root), QUOTA_GET_MAX_BYTES, &realm, true); diff --git a/fs/ceph/subvolume_metrics.c b/fs/ceph/subvolume_metrics.c new file mode 100644 index 000000000000..03fda1f9257b --- /dev/null +++ b/fs/ceph/subvolume_metrics.c @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +#include +#include +#include + +#include "subvolume_metrics.h" +#include "mds_client.h" +#include "super.h" + +/** + * struct ceph_subvol_metric_rb_entry - Per-subvolume I/O metrics node + * @node: Red-black tree linkage for tracker->tree + * @subvolume_id: Subvolume identifier (key for rb-tree lookup) + * @read_ops: Accumulated read operation count since last snapshot + * @write_ops: Accumulated write operation count since last snapshot + * @read_bytes: Accumulated bytes read since last snapshot + * @write_bytes: Accumulated bytes written since last snapshot + * @read_latency_us: Sum of read latencies in microseconds + * @write_latency_us: Sum of write latencies in microseconds + */ +struct ceph_subvol_metric_rb_entry { + struct rb_node node; + u64 subvolume_id; + u64 read_ops; + u64 write_ops; + u64 read_bytes; + u64 write_bytes; + u64 read_latency_us; + u64 write_latency_us; +}; + +static struct kmem_cache *ceph_subvol_metric_entry_cachep; + +void ceph_subvolume_metrics_init(struct ceph_subvolume_metrics_tracker *tracker) +{ + spin_lock_init(&tracker->lock); + tracker->tree = RB_ROOT_CACHED; + tracker->nr_entries = 0; + tracker->enabled = false; + atomic64_set(&tracker->snapshot_attempts, 0); + atomic64_set(&tracker->snapshot_empty, 0); + atomic64_set(&tracker->snapshot_failures, 0); + atomic64_set(&tracker->record_calls, 0); + atomic64_set(&tracker->record_disabled, 0); + atomic64_set(&tracker->record_no_subvol, 0); + atomic64_set(&tracker->total_read_ops, 0); + atomic64_set(&tracker->total_read_bytes, 0); + atomic64_set(&tracker->total_write_ops, 0); + atomic64_set(&tracker->total_write_bytes, 0); +} + +static struct ceph_subvol_metric_rb_entry * +__lookup_entry(struct ceph_subvolume_metrics_tracker *tracker, u64 subvol_id) +{ + struct rb_node *node; + + node = tracker->tree.rb_root.rb_node; + while (node) { + struct ceph_subvol_metric_rb_entry *entry = + rb_entry(node, struct ceph_subvol_metric_rb_entry, node); + + if (subvol_id < entry->subvolume_id) + node = node->rb_left; + else if (subvol_id > entry->subvolume_id) + node = node->rb_right; + else + return entry; + } + + return NULL; +} + +static struct ceph_subvol_metric_rb_entry * +__insert_entry(struct ceph_subvolume_metrics_tracker *tracker, + struct ceph_subvol_metric_rb_entry *entry) +{ + struct rb_node **link = &tracker->tree.rb_root.rb_node; + struct rb_node *parent = NULL; + bool leftmost = true; + + while (*link) { + struct ceph_subvol_metric_rb_entry *cur = + rb_entry(*link, struct ceph_subvol_metric_rb_entry, node); + + parent = *link; + if (entry->subvolume_id < cur->subvolume_id) + link = &(*link)->rb_left; + else if (entry->subvolume_id > cur->subvolume_id) { + link = &(*link)->rb_right; + leftmost = false; + } else + return cur; + } + + rb_link_node(&entry->node, parent, link); + rb_insert_color_cached(&entry->node, &tracker->tree, leftmost); + tracker->nr_entries++; + return entry; +} + +static void ceph_subvolume_metrics_clear_locked( + struct ceph_subvolume_metrics_tracker *tracker) +{ + struct rb_node *node = rb_first_cached(&tracker->tree); + + while (node) { + struct ceph_subvol_metric_rb_entry *entry = + rb_entry(node, struct ceph_subvol_metric_rb_entry, node); + struct rb_node *next = rb_next(node); + + rb_erase_cached(&entry->node, &tracker->tree); + tracker->nr_entries--; + kmem_cache_free(ceph_subvol_metric_entry_cachep, entry); + node = next; + } + + tracker->tree = RB_ROOT_CACHED; +} + +void ceph_subvolume_metrics_destroy(struct ceph_subvolume_metrics_tracker *tracker) +{ + spin_lock(&tracker->lock); + ceph_subvolume_metrics_clear_locked(tracker); + tracker->enabled = false; + spin_unlock(&tracker->lock); +} + +void ceph_subvolume_metrics_enable(struct ceph_subvolume_metrics_tracker *tracker, + bool enable) +{ + spin_lock(&tracker->lock); + if (enable) { + tracker->enabled = true; + } else { + tracker->enabled = false; + ceph_subvolume_metrics_clear_locked(tracker); + } + spin_unlock(&tracker->lock); +} + +void ceph_subvolume_metrics_record(struct ceph_subvolume_metrics_tracker *tracker, + u64 subvol_id, bool is_write, + size_t size, u64 latency_us) +{ + struct ceph_subvol_metric_rb_entry *entry, *new_entry = NULL; + bool retry = false; + + /* CEPH_SUBVOLUME_ID_NONE (0) means unknown/unset subvolume */ + if (!READ_ONCE(tracker->enabled) || + subvol_id == CEPH_SUBVOLUME_ID_NONE || !size || !latency_us) + return; + + /* + * Retry loop for lock-free allocation pattern: + * 1. First iteration: lookup under lock, if miss -> drop lock, alloc, retry + * 2. Second iteration: lookup again (may have been inserted), insert if still missing + * 3. On race (another thread inserted same key): free our alloc, retry + * All successful paths exit via return, so retry flag doesn't need reset. + */ + do { + spin_lock(&tracker->lock); + if (!tracker->enabled) { + spin_unlock(&tracker->lock); + if (new_entry) + kmem_cache_free(ceph_subvol_metric_entry_cachep, new_entry); + return; + } + + entry = __lookup_entry(tracker, subvol_id); + if (!entry) { + if (!new_entry) { + spin_unlock(&tracker->lock); + new_entry = kmem_cache_zalloc(ceph_subvol_metric_entry_cachep, + GFP_NOFS); + if (!new_entry) + return; + new_entry->subvolume_id = subvol_id; + retry = true; + continue; + } + entry = __insert_entry(tracker, new_entry); + if (entry != new_entry) { + /* raced with another insert */ + spin_unlock(&tracker->lock); + kmem_cache_free(ceph_subvol_metric_entry_cachep, new_entry); + new_entry = NULL; + retry = true; + continue; + } + new_entry = NULL; + } + + if (is_write) { + entry->write_ops++; + entry->write_bytes += size; + entry->write_latency_us += latency_us; + atomic64_inc(&tracker->total_write_ops); + atomic64_add(size, &tracker->total_write_bytes); + } else { + entry->read_ops++; + entry->read_bytes += size; + entry->read_latency_us += latency_us; + atomic64_inc(&tracker->total_read_ops); + atomic64_add(size, &tracker->total_read_bytes); + } + spin_unlock(&tracker->lock); + if (new_entry) + kmem_cache_free(ceph_subvol_metric_entry_cachep, new_entry); + return; + } while (retry); +} + +int ceph_subvolume_metrics_snapshot(struct ceph_subvolume_metrics_tracker *tracker, + struct ceph_subvol_metric_snapshot **out, + u32 *nr, bool consume) +{ + struct ceph_subvol_metric_snapshot *snap = NULL; + struct rb_node *node; + u32 count = 0, idx = 0; + int ret = 0; + + *out = NULL; + *nr = 0; + + if (!READ_ONCE(tracker->enabled)) + return 0; + + atomic64_inc(&tracker->snapshot_attempts); + + spin_lock(&tracker->lock); + for (node = rb_first_cached(&tracker->tree); node; node = rb_next(node)) { + struct ceph_subvol_metric_rb_entry *entry = + rb_entry(node, struct ceph_subvol_metric_rb_entry, node); + + /* Include entries with ANY I/O activity (read OR write) */ + if (entry->read_ops || entry->write_ops) + count++; + } + spin_unlock(&tracker->lock); + + if (!count) { + atomic64_inc(&tracker->snapshot_empty); + return 0; + } + + snap = kcalloc(count, sizeof(*snap), GFP_NOFS); + if (!snap) { + atomic64_inc(&tracker->snapshot_failures); + return -ENOMEM; + } + + spin_lock(&tracker->lock); + node = rb_first_cached(&tracker->tree); + while (node) { + struct ceph_subvol_metric_rb_entry *entry = + rb_entry(node, struct ceph_subvol_metric_rb_entry, node); + struct rb_node *next = rb_next(node); + + /* Skip entries with NO I/O activity at all */ + if (!entry->read_ops && !entry->write_ops) { + rb_erase_cached(&entry->node, &tracker->tree); + tracker->nr_entries--; + kmem_cache_free(ceph_subvol_metric_entry_cachep, entry); + node = next; + continue; + } + + if (idx >= count) { + pr_warn("ceph: subvol metrics snapshot race (idx=%u count=%u)\n", + idx, count); + break; + } + + snap[idx].subvolume_id = entry->subvolume_id; + snap[idx].read_ops = entry->read_ops; + snap[idx].write_ops = entry->write_ops; + snap[idx].read_bytes = entry->read_bytes; + snap[idx].write_bytes = entry->write_bytes; + snap[idx].read_latency_us = entry->read_latency_us; + snap[idx].write_latency_us = entry->write_latency_us; + idx++; + + if (consume) { + entry->read_ops = 0; + entry->write_ops = 0; + entry->read_bytes = 0; + entry->write_bytes = 0; + entry->read_latency_us = 0; + entry->write_latency_us = 0; + rb_erase_cached(&entry->node, &tracker->tree); + tracker->nr_entries--; + kmem_cache_free(ceph_subvol_metric_entry_cachep, entry); + } + node = next; + } + spin_unlock(&tracker->lock); + + if (!idx) { + kfree(snap); + snap = NULL; + ret = 0; + } else { + *nr = idx; + *out = snap; + } + + return ret; +} + +void ceph_subvolume_metrics_free_snapshot(struct ceph_subvol_metric_snapshot *snapshot) +{ + kfree(snapshot); +} + +/* + * Dump subvolume metrics to a seq_file for debugfs. + * + * Iterates the rb-tree directly under spinlock to avoid allocation. + * The lock hold time is minimal since we're only doing seq_printf calls. + */ +void ceph_subvolume_metrics_dump(struct ceph_subvolume_metrics_tracker *tracker, + struct seq_file *s) +{ + struct rb_node *node; + bool found = false; + + spin_lock(&tracker->lock); + if (!tracker->enabled) { + spin_unlock(&tracker->lock); + seq_puts(s, "subvolume metrics disabled\n"); + return; + } + + for (node = rb_first_cached(&tracker->tree); node; node = rb_next(node)) { + struct ceph_subvol_metric_rb_entry *entry = + rb_entry(node, struct ceph_subvol_metric_rb_entry, node); + u64 avg_rd_lat, avg_wr_lat; + + if (!entry->read_ops && !entry->write_ops) + continue; + + if (!found) { + seq_puts(s, "subvol_id rd_ops rd_bytes rd_avg_lat_us wr_ops wr_bytes wr_avg_lat_us\n"); + seq_puts(s, "------------------------------------------------------------------------------------------------\n"); + found = true; + } + + avg_rd_lat = entry->read_ops ? + div64_u64(entry->read_latency_us, entry->read_ops) : 0; + avg_wr_lat = entry->write_ops ? + div64_u64(entry->write_latency_us, entry->write_ops) : 0; + + seq_printf(s, "%-15llu%-10llu%-12llu%-16llu%-10llu%-12llu%-16llu\n", + entry->subvolume_id, + entry->read_ops, + entry->read_bytes, + avg_rd_lat, + entry->write_ops, + entry->write_bytes, + avg_wr_lat); + } + spin_unlock(&tracker->lock); + + if (!found) + seq_puts(s, "(no subvolume metrics collected)\n"); +} + +void ceph_subvolume_metrics_record_io(struct ceph_mds_client *mdsc, + struct ceph_inode_info *ci, + bool is_write, size_t bytes, + ktime_t start, ktime_t end) +{ + struct ceph_subvolume_metrics_tracker *tracker; + u64 subvol_id; + s64 delta_us; + + if (!mdsc || !ci || !bytes) + return; + + tracker = &mdsc->subvol_metrics; + atomic64_inc(&tracker->record_calls); + + if (!ceph_subvolume_metrics_enabled(tracker)) { + atomic64_inc(&tracker->record_disabled); + return; + } + + subvol_id = READ_ONCE(ci->i_subvolume_id); + if (subvol_id == CEPH_SUBVOLUME_ID_NONE) { + atomic64_inc(&tracker->record_no_subvol); + return; + } + + delta_us = ktime_to_us(ktime_sub(end, start)); + if (delta_us <= 0) + delta_us = 1; + + ceph_subvolume_metrics_record(tracker, subvol_id, is_write, + bytes, (u64)delta_us); +} + +int __init ceph_subvolume_metrics_cache_init(void) +{ + ceph_subvol_metric_entry_cachep = KMEM_CACHE(ceph_subvol_metric_rb_entry, + SLAB_RECLAIM_ACCOUNT); + if (!ceph_subvol_metric_entry_cachep) + return -ENOMEM; + return 0; +} + +void ceph_subvolume_metrics_cache_destroy(void) +{ + kmem_cache_destroy(ceph_subvol_metric_entry_cachep); +} diff --git a/fs/ceph/subvolume_metrics.h b/fs/ceph/subvolume_metrics.h new file mode 100644 index 000000000000..6f53ff726c75 --- /dev/null +++ b/fs/ceph/subvolume_metrics.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _FS_CEPH_SUBVOLUME_METRICS_H +#define _FS_CEPH_SUBVOLUME_METRICS_H + +#include +#include +#include +#include +#include + +struct seq_file; +struct ceph_mds_client; +struct ceph_inode_info; + +/** + * struct ceph_subvol_metric_snapshot - Point-in-time snapshot of subvolume metrics + * @subvolume_id: Subvolume identifier (inode number of subvolume root) + * @read_ops: Number of read operations since last snapshot + * @write_ops: Number of write operations since last snapshot + * @read_bytes: Total bytes read since last snapshot + * @write_bytes: Total bytes written since last snapshot + * @read_latency_us: Sum of read latencies in microseconds (for avg calculation) + * @write_latency_us: Sum of write latencies in microseconds (for avg calculation) + */ +struct ceph_subvol_metric_snapshot { + u64 subvolume_id; + u64 read_ops; + u64 write_ops; + u64 read_bytes; + u64 write_bytes; + u64 read_latency_us; + u64 write_latency_us; +}; + +/** + * struct ceph_subvolume_metrics_tracker - Tracks per-subvolume I/O metrics + * @lock: Protects @tree and @nr_entries during concurrent access + * @tree: Red-black tree of per-subvolume entries, keyed by subvolume_id + * @nr_entries: Number of entries currently in @tree + * @enabled: Whether collection is enabled (requires MDS feature support) + * @snapshot_attempts: Debug counter: total ceph_subvolume_metrics_snapshot() calls + * @snapshot_empty: Debug counter: snapshots that found no data to report + * @snapshot_failures: Debug counter: snapshots that failed to allocate memory + * @record_calls: Debug counter: total ceph_subvolume_metrics_record() calls + * @record_disabled: Debug counter: record calls skipped because disabled + * @record_no_subvol: Debug counter: record calls skipped (no subvolume_id) + * @total_read_ops: Cumulative read ops across all snapshots (never reset) + * @total_read_bytes: Cumulative bytes read across all snapshots (never reset) + * @total_write_ops: Cumulative write ops across all snapshots (never reset) + * @total_write_bytes: Cumulative bytes written across all snapshots (never reset) + */ +struct ceph_subvolume_metrics_tracker { + spinlock_t lock; + struct rb_root_cached tree; + u32 nr_entries; + bool enabled; + atomic64_t snapshot_attempts; + atomic64_t snapshot_empty; + atomic64_t snapshot_failures; + atomic64_t record_calls; + atomic64_t record_disabled; + atomic64_t record_no_subvol; + atomic64_t total_read_ops; + atomic64_t total_read_bytes; + atomic64_t total_write_ops; + atomic64_t total_write_bytes; +}; + +void ceph_subvolume_metrics_init(struct ceph_subvolume_metrics_tracker *tracker); +void ceph_subvolume_metrics_destroy(struct ceph_subvolume_metrics_tracker *tracker); +void ceph_subvolume_metrics_enable(struct ceph_subvolume_metrics_tracker *tracker, + bool enable); +void ceph_subvolume_metrics_record(struct ceph_subvolume_metrics_tracker *tracker, + u64 subvol_id, bool is_write, + size_t size, u64 latency_us); +int ceph_subvolume_metrics_snapshot(struct ceph_subvolume_metrics_tracker *tracker, + struct ceph_subvol_metric_snapshot **out, + u32 *nr, bool consume); +void ceph_subvolume_metrics_free_snapshot(struct ceph_subvol_metric_snapshot *snapshot); +void ceph_subvolume_metrics_dump(struct ceph_subvolume_metrics_tracker *tracker, + struct seq_file *s); + +void ceph_subvolume_metrics_record_io(struct ceph_mds_client *mdsc, + struct ceph_inode_info *ci, + bool is_write, size_t bytes, + ktime_t start, ktime_t end); + +static inline bool ceph_subvolume_metrics_enabled( + const struct ceph_subvolume_metrics_tracker *tracker) +{ + return READ_ONCE(tracker->enabled); +} + +int __init ceph_subvolume_metrics_cache_init(void); +void ceph_subvolume_metrics_cache_destroy(void); + +#endif /* _FS_CEPH_SUBVOLUME_METRICS_H */ diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 2aed6b3359b6..c05fbd4237f8 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -21,6 +21,7 @@ #include "mds_client.h" #include "cache.h" #include "crypto.h" +#include "subvolume_metrics.h" #include #include @@ -966,8 +967,14 @@ static int __init init_caches(void) if (!ceph_wb_pagevec_pool) goto bad_pagevec_pool; + error = ceph_subvolume_metrics_cache_init(); + if (error) + goto bad_subvol_metrics; + return 0; +bad_subvol_metrics: + mempool_destroy(ceph_wb_pagevec_pool); bad_pagevec_pool: kmem_cache_destroy(ceph_mds_request_cachep); bad_mds_req: @@ -1004,6 +1011,7 @@ static void destroy_caches(void) kmem_cache_destroy(ceph_dir_file_cachep); kmem_cache_destroy(ceph_mds_request_cachep); mempool_destroy(ceph_wb_pagevec_pool); + ceph_subvolume_metrics_cache_destroy(); } static void __ceph_umount_begin(struct ceph_fs_client *fsc) diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 29a980e22dc2..afc89ce91804 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -179,6 +179,7 @@ struct ceph_fs_client { struct dentry *debugfs_status; struct dentry *debugfs_mds_sessions; struct dentry *debugfs_metrics_dir; + struct dentry *debugfs_subvolume_metrics; #endif #ifdef CONFIG_CEPH_FSCACHE @@ -398,6 +399,15 @@ struct ceph_inode_info { /* quotas */ u64 i_max_bytes, i_max_files; + /* + * Subvolume ID this inode belongs to. CEPH_SUBVOLUME_ID_NONE (0) + * means unknown/unset, matching the FUSE client convention. + * Once set to a valid (non-zero) value, it should not change + * during the inode's lifetime. + */ +#define CEPH_SUBVOLUME_ID_NONE 0 + u64 i_subvolume_id; + s32 i_dir_pin; struct rb_root i_fragtree; @@ -1069,6 +1079,7 @@ extern struct inode *ceph_get_inode(struct super_block *sb, extern struct inode *ceph_get_snapdir(struct inode *parent); extern int ceph_fill_file_size(struct inode *inode, int issued, u32 truncate_seq, u64 truncate_size, u64 size); +extern void ceph_inode_set_subvolume(struct inode *inode, u64 subvolume_id); extern void ceph_fill_file_time(struct inode *inode, int issued, u64 time_warp_seq, struct timespec64 *ctime, struct timespec64 *mtime, diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 5f87f62091a1..e773be07f767 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -1254,6 +1254,22 @@ retry: ceph_vinop(inode), name, ceph_cap_string(issued)); __build_xattrs(inode); + /* + * __build_xattrs() may have released and reacquired i_ceph_lock, + * during which handle_cap_grant() could have replaced i_xattrs.blob + * with a newer MDS-provided blob and bumped i_xattrs.version. If that + * caused __build_xattrs() to rebuild the rb-tree from the new blob, + * count/names_size/vals_size may now be larger than when + * required_blob_size was computed above. Recompute it here so the + * prealloc_blob size check below reflects the current tree state. + */ + required_blob_size = __get_required_blob_size(ci, name_len, val_len); + if (required_blob_size > mdsc->mdsmap->m_max_xattr_size) { + doutc(cl, "sync (size too large): %d > %llu\n", + required_blob_size, mdsc->mdsmap->m_max_xattr_size); + goto do_sync; + } + if (!ci->i_xattrs.prealloc_blob || required_blob_size > ci->i_xattrs.prealloc_blob->alloc_len) { struct ceph_buffer *blob; @@ -1294,6 +1310,7 @@ retry: do_sync: spin_unlock(&ci->i_ceph_lock); + ceph_buffer_put(old_blob); do_sync_unlocked: if (lock_snap_rwsem) up_read(&mdsc->snap_rwsem); diff --git a/fs/coda/cache.c b/fs/coda/cache.c index 970f0022ec52..245131296300 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -93,12 +93,14 @@ static void coda_flag_children(struct dentry *parent, int flag) struct dentry *de; spin_lock(&parent->d_lock); + rcu_read_lock(); hlist_for_each_entry(de, &parent->d_children, d_sib) { struct inode *inode = d_inode_rcu(de); /* don't know what to do with negative dentries */ if (inode) coda_flag_inode(inode, flag); } + rcu_read_unlock(); spin_unlock(&parent->d_lock); } diff --git a/fs/coda/dir.c b/fs/coda/dir.c index d6b9fc3cc1ca..835eb7fdfdad 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -449,8 +449,6 @@ static int coda_dentry_revalidate(struct inode *dir, const struct qstr *name, inode = d_inode(de); if (!inode || is_root_inode(inode)) goto out; - if (is_bad_inode(inode)) - goto bad; cii = ITOC(d_inode(de)); if (!(cii->c_flags & (C_PURGE | C_FLUSH))) @@ -470,7 +468,6 @@ static int coda_dentry_revalidate(struct inode *dir, const struct qstr *name, spin_lock(&cii->c_lock); cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH); spin_unlock(&cii->c_lock); -bad: return 0; out: return 1; @@ -482,18 +479,12 @@ out: */ static int coda_dentry_delete(const struct dentry * dentry) { - struct inode *inode; - struct coda_inode_info *cii; + struct inode *inode = d_inode(dentry); - if (d_really_is_negative(dentry)) + if (!inode) return 0; - inode = d_inode(dentry); - if (!inode || is_bad_inode(inode)) - return 1; - - cii = ITOC(inode); - if (cii->c_flags & C_PURGE) + if (ITOC(inode)->c_flags & C_PURGE) return 1; return 0; diff --git a/fs/dax.c b/fs/dax.c index a5237169b467..6d175cd47a99 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -377,6 +377,59 @@ static void dax_folio_make_shared(struct folio *folio) folio->share = 1; } +/** + * dax_folio_reset_order - Reset a compound DAX folio to order-0 pages + * @folio: The folio to reset + * + * Splits a compound folio back into individual order-0 pages, + * clearing compound state and restoring pgmap pointers. + * + * Returns: the original folio order (0 if already order-0) + */ +int dax_folio_reset_order(struct folio *folio) +{ + struct dev_pagemap *pgmap = page_pgmap(&folio->page); + int order = folio_order(folio); + + /* + * DAX maintains the invariant that folio->share != 0 only when + * folio->mapping == NULL (enforced by dax_folio_make_shared()). + * Equivalently: folio->mapping != NULL implies folio->share == 0. + * Callers ensure share has been decremented to zero before + * calling here, so unconditionally clearing both fields is + * correct. + */ + folio->mapping = NULL; + folio->share = 0; + + if (!order) { + /* + * Restore pgmap explicitly even for order-0 folios. For the + * dax_folio_put() caller this is a no-op (same value), but + * fsdev_clear_folio_state() may call this on folios that + * were previously compound and need pgmap re-established. + */ + folio->pgmap = pgmap; + return 0; + } + + folio_reset_order(folio); + + for (int i = 0; i < (1UL << order); i++) { + struct page *page = folio_page(folio, i); + struct folio *f = (struct folio *)page; + + ClearPageHead(page); + clear_compound_head(page); + f->mapping = NULL; + f->share = 0; + f->pgmap = pgmap; + } + + return order; +} +EXPORT_SYMBOL_GPL(dax_folio_reset_order); + static inline unsigned long dax_folio_put(struct folio *folio) { unsigned long ref; @@ -390,28 +443,13 @@ static inline unsigned long dax_folio_put(struct folio *folio) if (ref) return ref; - folio->mapping = NULL; - order = folio_order(folio); - if (!order) - return 0; - folio_reset_order(folio); + order = dax_folio_reset_order(folio); + /* Debug check: verify refcounts are zero for all sub-folios */ for (i = 0; i < (1UL << order); i++) { - struct dev_pagemap *pgmap = page_pgmap(&folio->page); struct page *page = folio_page(folio, i); - struct folio *new_folio = (struct folio *)page; - ClearPageHead(page); - clear_compound_head(page); - - new_folio->mapping = NULL; - /* - * Reset pgmap which was over-written by - * prep_compound_page(). - */ - new_folio->pgmap = pgmap; - new_folio->share = 0; - WARN_ON_ONCE(folio_ref_count(new_folio)); + WARN_ON_ONCE(folio_ref_count((struct folio *)page)); } return ref; diff --git a/fs/dcache.c b/fs/dcache.c index df11bbba0342..2c61aeea41f4 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -40,7 +40,7 @@ /* * Usage: * dcache->d_inode->i_lock protects: - * - i_dentry, d_u.d_alias, d_inode of aliases + * - i_dentry, d_alias, d_inode of aliases * dcache_hash_bucket lock protects: * - the dcache hash table * s_roots bl list spinlock protects: @@ -55,7 +55,7 @@ * - d_unhashed() * - d_parent and d_chilren * - childrens' d_sib and d_parent - * - d_u.d_alias, d_inode + * - d_alias, d_inode * * Ordering: * dentry->d_inode->i_lock @@ -341,14 +341,14 @@ static inline struct external_name *external_name(struct dentry *dentry) static void __d_free(struct rcu_head *head) { - struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); + struct dentry *dentry = container_of(head, struct dentry, d_rcu); kmem_cache_free(dentry_cache, dentry); } static void __d_free_external(struct rcu_head *head) { - struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); + struct dentry *dentry = container_of(head, struct dentry, d_rcu); kfree(external_name(dentry)); kmem_cache_free(dentry_cache, dentry); } @@ -428,19 +428,19 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry) static void dentry_free(struct dentry *dentry) { - WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias)); + WARN_ON(d_really_is_positive(dentry)); if (unlikely(dname_external(dentry))) { struct external_name *p = external_name(dentry); if (likely(atomic_dec_and_test(&p->count))) { - call_rcu(&dentry->d_u.d_rcu, __d_free_external); + call_rcu(&dentry->d_rcu, __d_free_external); return; } } /* if dentry was never visible to RCU, immediate free is OK */ if (dentry->d_flags & DCACHE_NORCU) - __d_free(&dentry->d_u.d_rcu); + __d_free(&dentry->d_rcu); else - call_rcu(&dentry->d_u.d_rcu, __d_free); + call_rcu(&dentry->d_rcu, __d_free); } /* @@ -455,7 +455,16 @@ static void dentry_unlink_inode(struct dentry * dentry) raw_write_seqcount_begin(&dentry->d_seq); __d_clear_type_and_inode(dentry); - hlist_del_init(&dentry->d_u.d_alias); + hlist_del_init(&dentry->d_alias); + /* + * dentry becomes negative, so the space occupied by ->d_alias + * belongs to ->waiters now; we could use __hlist_del() instead + * of hlist_del_init(), if not for the stunt pulled by nfs + * dummy root dentries - positive dentry *not* included into + * the alias list of its inode. Open-coding hlist_del_init() + * and removing zeroing would be too clumsy... + */ + dentry->waiters = NULL; raw_write_seqcount_end(&dentry->d_seq); spin_unlock(&dentry->d_lock); spin_unlock(&inode->i_lock); @@ -605,6 +614,44 @@ void d_drop(struct dentry *dentry) } EXPORT_SYMBOL(d_drop); +struct completion_list { + struct completion_list *next; + struct completion completion; +}; + +/* + * shrink_dcache_tree() needs to be notified when dentry in process of + * being evicted finally gets unlisted. Such dentries are + * already with negative ->d_count + * already negative + * already not in in-lookup hash + * reachable only via ->d_sib. + * + * Use ->waiters for a single-linked list of struct completion_list of + * waiters. + */ +static inline void d_add_waiter(struct dentry *dentry, struct completion_list *p) +{ + struct completion_list *v = dentry->waiters; + init_completion(&p->completion); + p->next = v; + dentry->waiters = p; +} + +static inline void d_complete_waiters(struct dentry *dentry) +{ + struct completion_list *v = dentry->waiters; + if (unlikely(v)) { + /* some shrink_dcache_tree() instances are waiting */ + dentry->waiters = NULL; + while (v) { + struct completion *r = &v->completion; + v = v->next; + complete(r); + } + } +} + static inline void dentry_unlist(struct dentry *dentry) { struct dentry *next; @@ -613,6 +660,7 @@ static inline void dentry_unlist(struct dentry *dentry) * attached to the dentry tree */ dentry->d_flags |= DCACHE_DENTRY_KILLED; + d_complete_waiters(dentry); if (unlikely(hlist_unhashed(&dentry->d_sib))) return; __hlist_del(&dentry->d_sib); @@ -790,7 +838,7 @@ void d_mark_dontcache(struct inode *inode) struct dentry *de; spin_lock(&inode->i_lock); - hlist_for_each_entry(de, &inode->i_dentry, d_u.d_alias) { + for_each_alias(de, inode) { spin_lock(&de->d_lock); de->d_flags |= DCACHE_DONTCACHE; spin_unlock(&de->d_lock); @@ -1010,7 +1058,7 @@ static struct dentry * __d_find_any_alias(struct inode *inode) if (hlist_empty(&inode->i_dentry)) return NULL; - alias = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias); + alias = hlist_entry(inode->i_dentry.first, struct dentry, d_alias); lockref_get(&alias->d_lockref); return alias; } @@ -1040,7 +1088,7 @@ static struct dentry *__d_find_alias(struct inode *inode) if (S_ISDIR(inode->i_mode)) return __d_find_any_alias(inode); - hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) { + for_each_alias(alias, inode) { spin_lock(&alias->d_lock); if (!d_unhashed(alias)) { dget_dlock(alias); @@ -1093,9 +1141,9 @@ struct dentry *d_find_alias_rcu(struct inode *inode) // used without having I_FREEING set, which means no aliases left if (likely(!(inode_state_read(inode) & I_FREEING) && !hlist_empty(l))) { if (S_ISDIR(inode->i_mode)) { - de = hlist_entry(l->first, struct dentry, d_u.d_alias); + de = hlist_entry(l->first, struct dentry, d_alias); } else { - hlist_for_each_entry(de, l, d_u.d_alias) + hlist_for_each_entry(de, l, d_alias) if (!d_unhashed(de)) break; } @@ -1133,7 +1181,7 @@ void d_prune_aliases(struct inode *inode) struct dentry *dentry; spin_lock(&inode->i_lock); - hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) + for_each_alias(dentry, inode) d_dispose_if_unused(dentry, &dispose); spin_unlock(&inode->i_lock); shrink_dentry_list(&dispose); @@ -1569,6 +1617,10 @@ static enum d_walk_ret select_collect2(void *_data, struct dentry *dentry) return D_WALK_QUIT; } to_shrink_list(dentry, &data->dispose); + } else if (dentry->d_lockref.count < 0) { + rcu_read_lock(); + data->victim = dentry; + return D_WALK_QUIT; } /* * We can return to the caller if we have found some (this @@ -1608,12 +1660,27 @@ static void shrink_dcache_tree(struct dentry *parent, bool for_umount) data.victim = NULL; d_walk(parent, &data, select_collect2); if (data.victim) { - spin_lock(&data.victim->d_lock); - if (!lock_for_kill(data.victim)) { - spin_unlock(&data.victim->d_lock); + struct dentry *v = data.victim; + + spin_lock(&v->d_lock); + if (v->d_lockref.count < 0 && + !(v->d_flags & DCACHE_DENTRY_KILLED)) { + struct completion_list wait; + // It's busy dying; have it notify us once + // it becomes invisible to d_walk(). + d_add_waiter(v, &wait); + spin_unlock(&v->d_lock); + rcu_read_unlock(); + if (!list_empty(&data.dispose)) + shrink_dentry_list(&data.dispose); + wait_for_completion(&wait.completion); + continue; + } + if (!lock_for_kill(v)) { + spin_unlock(&v->d_lock); rcu_read_unlock(); } else { - shrink_kill(data.victim); + shrink_kill(v); } } if (!list_empty(&data.dispose)) @@ -1787,7 +1854,7 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name) INIT_HLIST_BL_NODE(&dentry->d_hash); INIT_LIST_HEAD(&dentry->d_lru); INIT_HLIST_HEAD(&dentry->d_children); - INIT_HLIST_NODE(&dentry->d_u.d_alias); + dentry->waiters = NULL; INIT_HLIST_NODE(&dentry->d_sib); if (dentry->d_op && dentry->d_op->d_init) { @@ -1980,7 +2047,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode) if ((dentry->d_flags & (DCACHE_LRU_LIST|DCACHE_SHRINK_LIST)) == DCACHE_LRU_LIST) this_cpu_dec(nr_dentry_negative); - hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); + hlist_add_head(&dentry->d_alias, &inode->i_dentry); raw_write_seqcount_begin(&dentry->d_seq); __d_set_inode_and_type(dentry, inode, add_flags); raw_write_seqcount_end(&dentry->d_seq); @@ -2004,7 +2071,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode) void d_instantiate(struct dentry *entry, struct inode * inode) { - BUG_ON(!hlist_unhashed(&entry->d_u.d_alias)); + BUG_ON(d_really_is_positive(entry)); if (inode) { security_d_instantiate(entry, inode); spin_lock(&inode->i_lock); @@ -2024,7 +2091,7 @@ EXPORT_SYMBOL(d_instantiate); */ void d_instantiate_new(struct dentry *entry, struct inode *inode) { - BUG_ON(!hlist_unhashed(&entry->d_u.d_alias)); + BUG_ON(d_really_is_positive(entry)); BUG_ON(!inode); lockdep_annotate_inode_mutex_key(inode); security_d_instantiate(entry, inode); @@ -2087,7 +2154,7 @@ static struct dentry *__d_obtain_alias(struct inode *inode, bool disconnected) spin_lock(&new->d_lock); __d_set_inode_and_type(new, inode, add_flags); - hlist_add_head(&new->d_u.d_alias, &inode->i_dentry); + hlist_add_head(&new->d_alias, &inode->i_dentry); if (!disconnected) { hlist_bl_lock(&sb->s_roots); hlist_bl_add_head(&new->d_hash, &sb->s_roots); @@ -2658,7 +2725,7 @@ retry: * we unlock the chain. All fields are stable in everything * we encounter. */ - hlist_bl_for_each_entry(dentry, node, b, d_u.d_in_lookup_hash) { + hlist_bl_for_each_entry(dentry, node, b, d_in_lookup_hash) { if (dentry->d_name.hash != hash) continue; if (dentry->d_parent != parent) @@ -2700,7 +2767,7 @@ retry: } rcu_read_unlock(); new->d_wait = wq; - hlist_bl_add_head(&new->d_u.d_in_lookup_hash, b); + hlist_bl_add_head(&new->d_in_lookup_hash, b); hlist_bl_unlock(b); return new; mismatch: @@ -2725,11 +2792,11 @@ static wait_queue_head_t *__d_lookup_unhash(struct dentry *dentry) b = in_lookup_hash(dentry->d_parent, dentry->d_name.hash); hlist_bl_lock(b); dentry->d_flags &= ~DCACHE_PAR_LOOKUP; - __hlist_bl_del(&dentry->d_u.d_in_lookup_hash); + __hlist_bl_del(&dentry->d_in_lookup_hash); d_wait = dentry->d_wait; dentry->d_wait = NULL; hlist_bl_unlock(b); - INIT_HLIST_NODE(&dentry->d_u.d_alias); + dentry->waiters = NULL; INIT_LIST_HEAD(&dentry->d_lru); return d_wait; } @@ -2760,7 +2827,7 @@ static inline void __d_add(struct dentry *dentry, struct inode *inode, d_set_d_op(dentry, ops); if (inode) { unsigned add_flags = d_flags_for_inode(inode); - hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); + hlist_add_head(&dentry->d_alias, &inode->i_dentry); raw_write_seqcount_begin(&dentry->d_seq); __d_set_inode_and_type(dentry, inode, add_flags); raw_write_seqcount_end(&dentry->d_seq); @@ -2795,7 +2862,7 @@ EXPORT_SYMBOL(d_add); struct dentry *d_make_persistent(struct dentry *dentry, struct inode *inode) { - WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias)); + WARN_ON(d_really_is_positive(dentry)); WARN_ON(!inode); security_d_instantiate(dentry, inode); spin_lock(&inode->i_lock); @@ -3185,7 +3252,7 @@ void d_mark_tmpfile(struct file *file, struct inode *inode) struct dentry *dentry = file->f_path.dentry; BUG_ON(dname_external(dentry) || - !hlist_unhashed(&dentry->d_u.d_alias) || + d_really_is_positive(dentry) || !d_unlinked(dentry)); spin_lock(&dentry->d_parent->d_lock); spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); @@ -3196,15 +3263,18 @@ void d_mark_tmpfile(struct file *file, struct inode *inode) } EXPORT_SYMBOL(d_mark_tmpfile); -void d_mark_tmpfile_name(struct file *file, const struct qstr *name) +int d_mark_tmpfile_name(struct file *file, const struct qstr *name) { struct dentry *dentry = file->f_path.dentry; char *dname = dentry->d_shortname.string; - BUG_ON(dname_external(dentry)); - BUG_ON(d_really_is_positive(dentry)); - BUG_ON(!d_unlinked(dentry)); - BUG_ON(name->len > DNAME_INLINE_LEN - 1); + if (unlikely(dname_external(dentry) || + d_really_is_positive(dentry) || + !d_unlinked(dentry))) + return -EINVAL; + if (unlikely(name->len > DNAME_INLINE_LEN - 1)) + return -ENAMETOOLONG; + spin_lock(&dentry->d_parent->d_lock); spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); dentry->__d_name.len = name->len; @@ -3212,6 +3282,7 @@ void d_mark_tmpfile_name(struct file *file, const struct qstr *name) dname[name->len] = '\0'; spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_parent->d_lock); + return 0; } EXPORT_SYMBOL(d_mark_tmpfile_name); diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index f25c9a49e251..5ef67b2674ee 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -72,7 +72,7 @@ out: /** * ecryptfs_derive_iv - * @iv: destination for the derived iv vale + * @iv: destination for the derived iv value * @crypt_stat: Pointer to crypt_stat struct for the current inode * @offset: Offset of the extent whose IV we are to derive * @@ -89,10 +89,6 @@ void ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat, ecryptfs_printk(KERN_DEBUG, "root iv:\n"); ecryptfs_dump_hex(crypt_stat->root_iv, crypt_stat->iv_bytes); } - /* TODO: It is probably secure to just cast the least - * significant bits of the root IV into an unsigned long and - * add the offset to that rather than go through all this - * hashing business. -Halcrow */ memcpy(src, crypt_stat->root_iv, crypt_stat->iv_bytes); memset((src + crypt_stat->iv_bytes), 0, 16); snprintf((src + crypt_stat->iv_bytes), 16, "%lld", offset); @@ -1376,9 +1372,9 @@ ecryptfs_encrypt_filename(struct ecryptfs_filename *filename, mount_crypt_stat, NULL, filename->filename_size); if (rc) { - printk(KERN_ERR "%s: Error attempting to get packet " - "size for tag 72; rc = [%d]\n", __func__, - rc); + ecryptfs_printk(KERN_ERR, + "Error attempting to get packet size for tag 70; rc = [%d]\n", + rc); filename->encrypted_filename_size = 0; goto out; } @@ -1802,8 +1798,9 @@ int ecryptfs_encrypt_and_encode_filename( filename->filename_size = name_size; rc = ecryptfs_encrypt_filename(filename, mount_crypt_stat); if (rc) { - printk(KERN_ERR "%s: Error attempting to encrypt " - "filename; rc = [%d]\n", __func__, rc); + ecryptfs_printk(KERN_ERR, + "Error attempting to encrypt filename; rc = [%d]\n", + rc); kfree(filename); goto out; } @@ -1811,9 +1808,8 @@ int ecryptfs_encrypt_and_encode_filename( NULL, &encoded_name_no_prefix_size, filename->encrypted_filename, filename->encrypted_filename_size); - if (mount_crypt_stat - && (mount_crypt_stat->flags - & ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK)) + if (mount_crypt_stat->flags + & ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK) (*encoded_name_size) = (ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE + encoded_name_no_prefix_size); @@ -1828,9 +1824,8 @@ int ecryptfs_encrypt_and_encode_filename( kfree(filename); goto out; } - if (mount_crypt_stat - && (mount_crypt_stat->flags - & ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK)) { + if (mount_crypt_stat->flags + & ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK) { memcpy((*encoded_name), ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX, ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE); @@ -1848,9 +1843,9 @@ int ecryptfs_encrypt_and_encode_filename( rc = -EOPNOTSUPP; } if (rc) { - printk(KERN_ERR "%s: Error attempting to encode " - "encrypted filename; rc = [%d]\n", __func__, - rc); + ecryptfs_printk(KERN_ERR, + "Error attempting to encode encrypted filename; rc = [%d]\n", + rc); kfree((*encoded_name)); (*encoded_name) = NULL; (*encoded_name_size) = 0; @@ -1925,8 +1920,7 @@ int ecryptfs_decode_and_decrypt_filename(char **plaintext_name, decoded_name_size); if (rc) { ecryptfs_printk(KERN_DEBUG, - "%s: Could not parse tag 70 packet from filename\n", - __func__); + "Could not parse tag 70 packet from filename\n"); goto out_free; } } else { diff --git a/fs/ecryptfs/debug.c b/fs/ecryptfs/debug.c index c185a8cb5fe2..42643702457c 100644 --- a/fs/ecryptfs/debug.c +++ b/fs/ecryptfs/debug.c @@ -7,6 +7,7 @@ * Author(s): Michael A. Halcrow */ +#include #include "ecryptfs_kernel.h" /* @@ -33,9 +34,7 @@ void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok) ECRYPTFS_PERSISTENT_PASSWORD) { ecryptfs_printk(KERN_DEBUG, " * persistent\n"); } - memcpy(sig, auth_tok->token.password.signature, - ECRYPTFS_SIG_SIZE_HEX); - sig[ECRYPTFS_SIG_SIZE_HEX] = '\0'; + strscpy(sig, auth_tok->token.password.signature); ecryptfs_printk(KERN_DEBUG, " * signature = [%s]\n", sig); } ecryptfs_printk(KERN_DEBUG, " * session_key.flags = [0x%x]\n", diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 8ab014db3e03..546c1fe692c0 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -677,6 +677,20 @@ static const char *ecryptfs_get_link(struct dentry *dentry, return buf; } +static void ecryptfs_iattr_to_lower(struct iattr *lower_ia, + const struct iattr *ia) +{ + memcpy(lower_ia, ia, sizeof(*lower_ia)); + if (ia->ia_valid & ATTR_FILE) + lower_ia->ia_file = ecryptfs_file_to_lower(ia->ia_file); + /* + * If the mode change is for clearing setuid/setgid bits, allow the lower + * file system to interpret this in its own way. + */ + if (lower_ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) + lower_ia->ia_valid &= ~ATTR_MODE; +} + /** * upper_size_to_lower_size * @crypt_stat: Crypt_stat associated with file @@ -707,127 +721,113 @@ upper_size_to_lower_size(struct ecryptfs_crypt_stat *crypt_stat, } /** - * truncate_upper + * __ecryptfs_truncate * @dentry: The ecryptfs layer dentry * @ia: Address of the ecryptfs inode's attributes - * @lower_ia: Address of the lower inode's attributes * - * Function to handle truncations modifying the size of the file. Note - * that the file sizes are interpolated. When expanding, we are simply - * writing strings of 0's out. When truncating, we truncate the upper - * inode and update the lower_ia according to the page index - * interpolations. If ATTR_SIZE is set in lower_ia->ia_valid upon return, - * the caller must use lower_ia in a call to notify_change() to perform - * the truncation of the lower inode. + * Handle truncations modifying the size of the file. Note that the file sizes + * are interpolated. When expanding, we are simply writing strings of 0's out. + * When truncating, we truncate the upper inode and update the lower_ia + * according to the page index interpolations. * * Returns zero on success; non-zero otherwise */ -static int truncate_upper(struct dentry *dentry, struct iattr *ia, - struct iattr *lower_ia) +static int __ecryptfs_truncate(struct dentry *dentry, const struct iattr *ia) { - int rc = 0; + struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); struct inode *inode = d_inode(dentry); struct ecryptfs_crypt_stat *crypt_stat; loff_t i_size = i_size_read(inode); loff_t lower_size_before_truncate; loff_t lower_size_after_truncate; + struct iattr lower_ia; + size_t num_zeros; + int rc; - if (unlikely((ia->ia_size == i_size))) { - lower_ia->ia_valid &= ~ATTR_SIZE; + ecryptfs_iattr_to_lower(&lower_ia, ia); + + if (unlikely((ia->ia_size == i_size))) return 0; - } - rc = ecryptfs_get_lower_file(dentry, inode); - if (rc) - return rc; - crypt_stat = &ecryptfs_inode_to_private(d_inode(dentry))->crypt_stat; - /* Switch on growing or shrinking file */ - if (ia->ia_size > i_size) { - char zero[] = { 0x00 }; - - lower_ia->ia_valid &= ~ATTR_SIZE; - /* Write a single 0 at the last position of the file; - * this triggers code that will fill in 0's throughout - * the intermediate portion of the previous end of the - * file and the new and of the file */ - rc = ecryptfs_write(inode, zero, - (ia->ia_size - 1), 1); - } else { /* ia->ia_size < i_size_read(inode) */ - /* We're chopping off all the pages down to the page - * in which ia->ia_size is located. Fill in the end of - * that page from (ia->ia_size & ~PAGE_MASK) to - * PAGE_SIZE with zeros. */ - size_t num_zeros = (PAGE_SIZE - - (ia->ia_size & ~PAGE_MASK)); - - if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { - truncate_setsize(inode, ia->ia_size); - lower_ia->ia_size = ia->ia_size; - lower_ia->ia_valid |= ATTR_SIZE; - goto out; - } - if (num_zeros) { - char *zeros_virt; - - zeros_virt = kzalloc(num_zeros, GFP_KERNEL); - if (!zeros_virt) { - rc = -ENOMEM; - goto out; - } - rc = ecryptfs_write(inode, zeros_virt, - ia->ia_size, num_zeros); - kfree(zeros_virt); - if (rc) { - printk(KERN_ERR "Error attempting to zero out " - "the remainder of the end page on " - "reducing truncate; rc = [%d]\n", rc); - goto out; - } - } - truncate_setsize(inode, ia->ia_size); - rc = ecryptfs_write_inode_size_to_metadata(inode); - if (rc) { - printk(KERN_ERR "Problem with " - "ecryptfs_write_inode_size_to_metadata; " - "rc = [%d]\n", rc); - goto out; - } - /* We are reducing the size of the ecryptfs file, and need to - * know if we need to reduce the size of the lower file. */ - lower_size_before_truncate = - upper_size_to_lower_size(crypt_stat, i_size); - lower_size_after_truncate = - upper_size_to_lower_size(crypt_stat, ia->ia_size); - if (lower_size_after_truncate < lower_size_before_truncate) { - lower_ia->ia_size = lower_size_after_truncate; - lower_ia->ia_valid |= ATTR_SIZE; - } else - lower_ia->ia_valid &= ~ATTR_SIZE; - } -out: - ecryptfs_put_lower_file(inode); - return rc; -} - -static int ecryptfs_inode_newsize_ok(struct inode *inode, loff_t offset) -{ - struct ecryptfs_crypt_stat *crypt_stat; - loff_t lower_oldsize, lower_newsize; crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; - lower_oldsize = upper_size_to_lower_size(crypt_stat, - i_size_read(inode)); - lower_newsize = upper_size_to_lower_size(crypt_stat, offset); - if (lower_newsize > lower_oldsize) { + lower_size_before_truncate = + upper_size_to_lower_size(crypt_stat, i_size); + lower_size_after_truncate = + upper_size_to_lower_size(crypt_stat, ia->ia_size); + if (lower_size_after_truncate > lower_size_before_truncate) { /* * The eCryptfs inode and the new *lower* size are mixed here * because we may not have the lower i_mutex held and/or it may * not be appropriate to call inode_newsize_ok() with inodes * from other filesystems. */ - return inode_newsize_ok(inode, lower_newsize); + rc = inode_newsize_ok(inode, lower_size_after_truncate); + if (rc) + return rc; } - return 0; + rc = ecryptfs_get_lower_file(dentry, inode); + if (rc) + return rc; + + if (ia->ia_size > i_size) { + char zero[] = { 0x00 }; + + /* + * Write a single 0 at the last position of the file; this + * triggers code that will fill in 0's throughout the + * intermediate portion of the previous end of the file and the + * new end of the file. + */ + rc = ecryptfs_write(inode, zero, ia->ia_size - 1, 1); + goto out; + } + + if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { + truncate_setsize(inode, ia->ia_size); + lower_ia.ia_size = ia->ia_size; + goto set_size; + } + + /* + * We're chopping off all the pages down to the page in which + * ia->ia_size is located. Fill in the end of that page from + * (ia->ia_size & ~PAGE_MASK) to PAGE_SIZE with zeros. + */ + num_zeros = PAGE_SIZE - (ia->ia_size & ~PAGE_MASK); + if (num_zeros) { + rc = ecryptfs_write(inode, page_address(ZERO_PAGE(0)), + ia->ia_size, num_zeros); + if (rc) { + pr_err("Error attempting to zero out the remainder of the end page on reducing truncate; rc = [%d]\n", + rc); + goto out; + } + } + truncate_setsize(inode, ia->ia_size); + rc = ecryptfs_write_inode_size_to_metadata(inode); + if (rc) { + pr_err("Problem with ecryptfs_write_inode_size_to_metadata; rc = [%d]\n", + rc); + goto out; + } + + /* + * We are reducing the size of the ecryptfs file, and need to know if we + * need to reduce the size of the lower file. + */ + if (lower_size_after_truncate >= lower_size_before_truncate) + goto out; + + lower_ia.ia_size = lower_size_after_truncate; +set_size: + lower_ia.ia_valid |= ATTR_SIZE; + inode_lock(d_inode(lower_dentry)); + rc = notify_change(&nop_mnt_idmap, lower_dentry, &lower_ia, NULL); + inode_unlock(d_inode(lower_dentry)); +out: + ecryptfs_put_lower_file(inode); + return rc; } /** @@ -842,24 +842,12 @@ static int ecryptfs_inode_newsize_ok(struct inode *inode, loff_t offset) */ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) { - struct iattr ia = { .ia_valid = ATTR_SIZE, .ia_size = new_length }; - struct iattr lower_ia = { .ia_valid = 0 }; - int rc; + const struct iattr ia = { + .ia_valid = ATTR_SIZE, + .ia_size = new_length, + }; - rc = ecryptfs_inode_newsize_ok(d_inode(dentry), new_length); - if (rc) - return rc; - - rc = truncate_upper(dentry, &ia, &lower_ia); - if (!rc && lower_ia.ia_valid & ATTR_SIZE) { - struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); - - inode_lock(d_inode(lower_dentry)); - rc = notify_change(&nop_mnt_idmap, lower_dentry, - &lower_ia, NULL); - inode_unlock(d_inode(lower_dentry)); - } - return rc; + return __ecryptfs_truncate(dentry, &ia); } static int @@ -886,25 +874,22 @@ ecryptfs_permission(struct mnt_idmap *idmap, struct inode *inode, static int ecryptfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *ia) { - int rc = 0; - struct dentry *lower_dentry; - struct iattr lower_ia; - struct inode *inode; - struct inode *lower_inode; + struct inode *inode = d_inode(dentry); + struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); + struct inode *lower_inode = ecryptfs_inode_to_lower(inode); struct ecryptfs_crypt_stat *crypt_stat; + int rc; crypt_stat = &ecryptfs_inode_to_private(d_inode(dentry))->crypt_stat; if (!(crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED)) ecryptfs_init_crypt_stat(crypt_stat); - inode = d_inode(dentry); - lower_inode = ecryptfs_inode_to_lower(inode); - lower_dentry = ecryptfs_dentry_to_lower(dentry); + mutex_lock(&crypt_stat->cs_mutex); if (d_is_dir(dentry)) crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); - else if (d_is_reg(dentry) - && (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED) - || !(crypt_stat->flags & ECRYPTFS_KEY_VALID))) { + else if (d_is_reg(dentry) && + (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED) || + !(crypt_stat->flags & ECRYPTFS_KEY_VALID))) { struct ecryptfs_mount_crypt_stat *mount_crypt_stat; mount_crypt_stat = &ecryptfs_superblock_to_private( @@ -917,8 +902,8 @@ static int ecryptfs_setattr(struct mnt_idmap *idmap, rc = ecryptfs_read_metadata(dentry); ecryptfs_put_lower_file(inode); if (rc) { - if (!(mount_crypt_stat->flags - & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { + if (!(mount_crypt_stat->flags & + ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { rc = -EIO; printk(KERN_WARNING "Either the lower file " "is not in a valid eCryptfs format, " @@ -938,31 +923,19 @@ static int ecryptfs_setattr(struct mnt_idmap *idmap, rc = setattr_prepare(&nop_mnt_idmap, dentry, ia); if (rc) goto out; + if (ia->ia_valid & ATTR_SIZE) { - rc = ecryptfs_inode_newsize_ok(inode, ia->ia_size); - if (rc) - goto out; + rc = __ecryptfs_truncate(dentry, ia); + } else { + struct iattr lower_ia; + + ecryptfs_iattr_to_lower(&lower_ia, ia); + + inode_lock(d_inode(lower_dentry)); + rc = notify_change(&nop_mnt_idmap, lower_dentry, &lower_ia, + NULL); + inode_unlock(d_inode(lower_dentry)); } - - memcpy(&lower_ia, ia, sizeof(lower_ia)); - if (ia->ia_valid & ATTR_FILE) - lower_ia.ia_file = ecryptfs_file_to_lower(ia->ia_file); - if (ia->ia_valid & ATTR_SIZE) { - rc = truncate_upper(dentry, ia, &lower_ia); - if (rc < 0) - goto out; - } - - /* - * mode change is for clearing setuid/setgid bits. Allow lower fs - * to interpret this in its own way. - */ - if (lower_ia.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) - lower_ia.ia_valid &= ~ATTR_MODE; - - inode_lock(d_inode(lower_dentry)); - rc = notify_change(&nop_mnt_idmap, lower_dentry, &lower_ia, NULL); - inode_unlock(d_inode(lower_dentry)); out: fsstack_copy_attr_all(inode, lower_inode); return rc; diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index e8494903bb42..0be746493e56 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -2458,8 +2458,7 @@ int ecryptfs_add_keysig(struct ecryptfs_crypt_stat *crypt_stat, char *sig) if (!new_key_sig) return -ENOMEM; - memcpy(new_key_sig->keysig, sig, ECRYPTFS_SIG_SIZE_HEX); - new_key_sig->keysig[ECRYPTFS_SIG_SIZE_HEX] = '\0'; + strscpy(new_key_sig->keysig, sig); /* Caller must hold keysig_list_mutex */ list_add(&new_key_sig->crypt_stat_list, &crypt_stat->keysig_list); @@ -2479,9 +2478,8 @@ ecryptfs_add_global_auth_tok(struct ecryptfs_mount_crypt_stat *mount_crypt_stat, if (!new_auth_tok) return -ENOMEM; - memcpy(new_auth_tok->sig, sig, ECRYPTFS_SIG_SIZE_HEX); + strscpy(new_auth_tok->sig, sig); new_auth_tok->flags = global_auth_tok_flags; - new_auth_tok->sig[ECRYPTFS_SIG_SIZE_HEX] = '\0'; mutex_lock(&mount_crypt_stat->global_auth_tok_list_mutex); list_add(&new_auth_tok->mount_crypt_stat_list, &mount_crypt_stat->global_auth_tok_list); diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index f4ab387eb4ed..5f37cddb956f 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -531,6 +531,7 @@ static int ecryptfs_get_tree(struct fs_context *fc) s->s_blocksize = path.dentry->d_sb->s_blocksize; s->s_magic = ECRYPTFS_SUPER_MAGIC; s->s_stack_depth = path.dentry->d_sb->s_stack_depth + 1; + s->s_time_gran = path.dentry->d_sb->s_time_gran; rc = -EINVAL; if (s->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c index 30c8e15d87b5..03c60f0850ca 100644 --- a/fs/ecryptfs/messaging.c +++ b/fs/ecryptfs/messaging.c @@ -6,6 +6,7 @@ * Author(s): Michael A. Halcrow * Tyler Hicks */ +#include #include #include #include @@ -232,7 +233,7 @@ int ecryptfs_process_response(struct ecryptfs_daemon *daemon, msg_ctx->counter, seq); goto unlock; } - msg_size = (sizeof(*msg) + msg->data_len); + msg_size = struct_size(msg, data, msg->data_len); msg_ctx->msg = kmemdup(msg, msg_size, GFP_KERNEL); if (!msg_ctx->msg) { rc = -ENOMEM; diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c index 4e62c3cef70f..5a7d08149922 100644 --- a/fs/ecryptfs/miscdev.c +++ b/fs/ecryptfs/miscdev.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -148,8 +149,10 @@ int ecryptfs_send_miscdev(char *data, size_t data_size, u16 msg_flags, struct ecryptfs_daemon *daemon) { struct ecryptfs_message *msg; + size_t msg_size; - msg = kmalloc((sizeof(*msg) + data_size), GFP_KERNEL); + msg_size = struct_size(msg, data, data_size); + msg = kmalloc(msg_size, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -159,7 +162,7 @@ int ecryptfs_send_miscdev(char *data, size_t data_size, msg_ctx->msg->data_len = data_size; msg_ctx->type = msg_type; memcpy(msg_ctx->msg->data, data, data_size); - msg_ctx->msg_size = (sizeof(*msg_ctx->msg) + data_size); + msg_ctx->msg_size = msg_size; list_add_tail(&msg_ctx->daemon_out_list, &daemon->msg_ctx_out_queue); mutex_unlock(&msg_ctx->mux); diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 1c5224cf183e..733c19571f1c 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -191,13 +191,10 @@ static const struct dentry_operations efivarfs_d_ops = { static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) { + struct qstr q = QSTR(name); struct dentry *d; - struct qstr q; int err; - q.name = name; - q.len = strlen(name); - err = efivarfs_d_hash(parent, &q); if (err) return ERR_PTR(err); diff --git a/fs/erofs/data.c b/fs/erofs/data.c index 1e022142b188..44da21c9d777 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -39,7 +39,7 @@ void *erofs_bread(struct erofs_buf *buf, erofs_off_t offset, bool need_kmap) * However, the data access range must be verified here in advance. */ if (buf->file) { - fpos = index << PAGE_SHIFT; + fpos = (loff_t)index << PAGE_SHIFT; err = rw_verify_area(READ, buf->file, &fpos, PAGE_SIZE); if (err < 0) return ERR_PTR(err); diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c index e5132575b9d3..4aa52a5f204a 100644 --- a/fs/erofs/dir.c +++ b/fs/erofs/dir.c @@ -19,20 +19,18 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx, const char *de_name = (char *)dentry_blk + nameoff; unsigned int de_namelen; - /* the last dirent in the block? */ - if (de + 1 >= end) - de_namelen = strnlen(de_name, maxsize - nameoff); - else + /* non-trailing dirent in the directory block? */ + if (de + 1 < end) de_namelen = le16_to_cpu(de[1].nameoff) - nameoff; + else if (maxsize <= nameoff) + goto err_bogus; + else + de_namelen = strnlen(de_name, maxsize - nameoff); - /* a corrupted entry is found */ - if (nameoff + de_namelen > maxsize || - de_namelen > EROFS_NAME_LEN) { - erofs_err(dir->i_sb, "bogus dirent @ nid %llu", - EROFS_I(dir)->nid); - DBG_BUGON(1); - return -EFSCORRUPTED; - } + /* a corrupted entry is found (including negative namelen) */ + if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) || + nameoff + de_namelen > maxsize) + goto err_bogus; if (!dir_emit(ctx, de_name, de_namelen, erofs_nid_to_ino64(EROFS_SB(dir->i_sb), @@ -42,6 +40,10 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx, ctx->pos += sizeof(struct erofs_dirent); } return 0; +err_bogus: + erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid); + DBG_BUGON(1); + return -EFSCORRUPTED; } static int erofs_readdir(struct file *f, struct dir_context *ctx) @@ -88,7 +90,7 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx) } nameoff = le16_to_cpu(de->nameoff); - if (nameoff < sizeof(struct erofs_dirent) || nameoff >= bsz) { + if (!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))) { erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu", nameoff, EROFS_I(dir)->nid); err = -EFSCORRUPTED; diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 8a0b15511931..43bb5a6a9924 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -1872,7 +1872,7 @@ static void z_erofs_pcluster_readmore(struct z_erofs_frontend *f, if (cur < PAGE_SIZE) break; - cur = (index << PAGE_SHIFT) - 1; + cur = ((loff_t)index << PAGE_SHIFT) - 1; } } diff --git a/fs/erofs/zmap.c b/fs/erofs/zmap.c index 72b96e295716..a72db36096ca 100644 --- a/fs/erofs/zmap.c +++ b/fs/erofs/zmap.c @@ -10,7 +10,7 @@ struct z_erofs_maprecorder { struct inode *inode; struct erofs_map_blocks *map; - unsigned long lcn; + u64 lcn; /* compression extent information gathered */ u8 type, headtype; u16 clusterofs; @@ -20,8 +20,7 @@ struct z_erofs_maprecorder { bool partialref, in_mbox; }; -static int z_erofs_load_full_lcluster(struct z_erofs_maprecorder *m, - unsigned long lcn) +static int z_erofs_load_full_lcluster(struct z_erofs_maprecorder *m, u64 lcn) { struct inode *const inode = m->inode; struct erofs_inode *const vi = EROFS_I(inode); @@ -94,7 +93,7 @@ static int get_compacted_la_distance(unsigned int lobits, } static int z_erofs_load_compact_lcluster(struct z_erofs_maprecorder *m, - unsigned long lcn, bool lookahead) + u64 lcn, bool lookahead) { struct inode *const inode = m->inode; struct erofs_inode *const vi = EROFS_I(inode); @@ -234,7 +233,7 @@ static int z_erofs_load_compact_lcluster(struct z_erofs_maprecorder *m, } static int z_erofs_load_lcluster_from_disk(struct z_erofs_maprecorder *m, - unsigned int lcn, bool lookahead) + u64 lcn, bool lookahead) { struct erofs_inode *vi = EROFS_I(m->inode); int err; @@ -249,7 +248,7 @@ static int z_erofs_load_lcluster_from_disk(struct z_erofs_maprecorder *m, return err; if (m->type >= Z_EROFS_LCLUSTER_TYPE_MAX) { - erofs_err(m->inode->i_sb, "unknown type %u @ lcn %u of nid %llu", + erofs_err(m->inode->i_sb, "unknown type %u @ lcn %llu of nid %llu", m->type, lcn, EROFS_I(m->inode)->nid); DBG_BUGON(1); return -EOPNOTSUPP; @@ -269,7 +268,7 @@ static int z_erofs_extent_lookback(struct z_erofs_maprecorder *m, const unsigned int lclusterbits = vi->z_lclusterbits; while (m->lcn >= lookback_distance) { - unsigned long lcn = m->lcn - lookback_distance; + u64 lcn = m->lcn - lookback_distance; int err; if (!lookback_distance) @@ -286,7 +285,7 @@ static int z_erofs_extent_lookback(struct z_erofs_maprecorder *m, m->map->m_la = (lcn << lclusterbits) | m->clusterofs; return 0; } - erofs_err(sb, "bogus lookback distance %u @ lcn %lu of nid %llu", + erofs_err(sb, "bogus lookback distance %u @ lcn %llu of nid %llu", lookback_distance, m->lcn, vi->nid); DBG_BUGON(1); return -EFSCORRUPTED; @@ -300,7 +299,7 @@ static int z_erofs_get_extent_compressedlen(struct z_erofs_maprecorder *m, struct erofs_inode *vi = EROFS_I(inode); bool bigpcl1 = vi->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_1; bool bigpcl2 = vi->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_2; - unsigned long lcn = m->lcn + 1; + u64 lcn = m->lcn + 1; int err; DBG_BUGON(m->type == Z_EROFS_LCLUSTER_TYPE_NONHEAD); @@ -331,7 +330,7 @@ static int z_erofs_get_extent_compressedlen(struct z_erofs_maprecorder *m, m->type == Z_EROFS_LCLUSTER_TYPE_NONHEAD); if (m->type == Z_EROFS_LCLUSTER_TYPE_NONHEAD && m->delta[0] != 1) { - erofs_err(sb, "bogus CBLKCNT @ lcn %lu of nid %llu", lcn, vi->nid); + erofs_err(sb, "bogus CBLKCNT @ lcn %llu of nid %llu", lcn, vi->nid); DBG_BUGON(1); return -EFSCORRUPTED; } diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 23f3c6ac0bad..a3090b446af1 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -148,13 +148,6 @@ struct epitem { /* The file descriptor information this item refers to */ struct epoll_filefd ffd; - /* - * Protected by file->f_lock, true for to-be-released epitem already - * removed from the "struct file" items list; together with - * eventpoll->refcount orchestrates "struct eventpoll" disposal - */ - bool dying; - /* List containing poll wait queues */ struct eppoll_entry *pwqlist; @@ -220,10 +213,7 @@ struct eventpoll { struct hlist_head refs; u8 loop_check_depth; - /* - * usage count, used together with epitem->dying to - * orchestrate the disposal of this struct - */ + /* usage count, orchestrates "struct eventpoll" disposal */ refcount_t refcount; /* used to defer freeing past ep_get_upwards_depth_proc() RCU walk */ @@ -827,36 +817,47 @@ static void ep_free(struct eventpoll *ep) } /* - * Removes a "struct epitem" from the eventpoll RB tree and deallocates - * all the associated resources. Must be called with "mtx" held. - * If the dying flag is set, do the removal only if force is true. - * This prevents ep_clear_and_put() from dropping all the ep references - * while running concurrently with eventpoll_release_file(). - * Returns true if the eventpoll can be disposed. + * The ffd.file pointer may be in the process of being torn down due to + * being closed, but we may not have finished eventpoll_release() yet. + * + * Normally, even with the atomic_long_inc_not_zero, the file may have + * been free'd and then gotten re-allocated to something else (since + * files are not RCU-delayed, they are SLAB_TYPESAFE_BY_RCU). + * + * But for epoll, users hold the ep->mtx mutex, and as such any file in + * the process of being free'd will block in eventpoll_release_file() + * and thus the underlying file allocation will not be free'd, and the + * file re-use cannot happen. + * + * For the same reason we can avoid a rcu_read_lock() around the + * operation - 'ffd.file' cannot go away even if the refcount has + * reached zero (but we must still not call out to ->poll() functions + * etc). */ -static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force) +static struct file *epi_fget(const struct epitem *epi) { - struct file *file = epi->ffd.file; - struct epitems_head *to_free; + struct file *file; + + file = epi->ffd.file; + if (!file_ref_get(&file->f_ref)) + file = NULL; + return file; +} + +/* + * Takes &file->f_lock; returns with it released. + */ +static void ep_remove_file(struct eventpoll *ep, struct epitem *epi, + struct file *file) +{ + struct epitems_head *to_free = NULL; struct hlist_head *head; - lockdep_assert_irqs_enabled(); + lockdep_assert_held(&ep->mtx); - /* - * Removes poll wait queue hooks. - */ - ep_unregister_pollwait(ep, epi); - - /* Remove the current item from the list of epoll hooks */ spin_lock(&file->f_lock); - if (epi->dying && !force) { - spin_unlock(&file->f_lock); - return false; - } - - to_free = NULL; head = file->f_ep; - if (head->first == &epi->fllink && !epi->fllink.next) { + if (hlist_is_singular_node(&epi->fllink, head)) { /* See eventpoll_release() for details. */ WRITE_ONCE(file->f_ep, NULL); if (!is_file_epoll(file)) { @@ -869,6 +870,11 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force) hlist_del_rcu(&epi->fllink); spin_unlock(&file->f_lock); free_ephead(to_free); +} + +static void ep_remove_epi(struct eventpoll *ep, struct epitem *epi) +{ + lockdep_assert_held(&ep->mtx); rb_erase_cached(&epi->rbn, &ep->rbr); @@ -888,16 +894,32 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force) kfree_rcu(epi, rcu); percpu_counter_dec(&ep->user->epoll_watches); - return true; } /* * ep_remove variant for callers owing an additional reference to the ep */ -static void ep_remove_safe(struct eventpoll *ep, struct epitem *epi) +static void ep_remove(struct eventpoll *ep, struct epitem *epi) { - if (__ep_remove(ep, epi, false)) - WARN_ON_ONCE(ep_refcount_dec_and_test(ep)); + struct file *file __free(fput) = NULL; + + lockdep_assert_irqs_enabled(); + lockdep_assert_held(&ep->mtx); + + ep_unregister_pollwait(ep, epi); + + /* + * If we manage to grab a reference it means we're not in + * eventpoll_release_file() and aren't going to be: once @file's + * refcount has reached zero, file_ref_get() cannot bring it back. + */ + file = epi_fget(epi); + if (!file) + return; + + ep_remove_file(ep, epi, file); + ep_remove_epi(ep, epi); + WARN_ON_ONCE(ep_refcount_dec_and_test(ep)); } static void ep_clear_and_put(struct eventpoll *ep) @@ -923,7 +945,7 @@ static void ep_clear_and_put(struct eventpoll *ep) /* * Walks through the whole tree and try to free each "struct epitem". - * Note that ep_remove_safe() will not remove the epitem in case of a + * Note that ep_remove() will not remove the epitem in case of a * racing eventpoll_release_file(); the latter will do the removal. * At this point we are sure no poll callbacks will be lingering around. * Since we still own a reference to the eventpoll struct, the loop can't @@ -932,7 +954,7 @@ static void ep_clear_and_put(struct eventpoll *ep) for (rbp = rb_first_cached(&ep->rbr); rbp; rbp = next) { next = rb_next(rbp); epi = rb_entry(rbp, struct epitem, rbn); - ep_remove_safe(ep, epi); + ep_remove(ep, epi); cond_resched(); } @@ -1012,34 +1034,6 @@ static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int dep return res; } -/* - * The ffd.file pointer may be in the process of being torn down due to - * being closed, but we may not have finished eventpoll_release() yet. - * - * Normally, even with the atomic_long_inc_not_zero, the file may have - * been free'd and then gotten re-allocated to something else (since - * files are not RCU-delayed, they are SLAB_TYPESAFE_BY_RCU). - * - * But for epoll, users hold the ep->mtx mutex, and as such any file in - * the process of being free'd will block in eventpoll_release_file() - * and thus the underlying file allocation will not be free'd, and the - * file re-use cannot happen. - * - * For the same reason we can avoid a rcu_read_lock() around the - * operation - 'ffd.file' cannot go away even if the refcount has - * reached zero (but we must still not call out to ->poll() functions - * etc). - */ -static struct file *epi_fget(const struct epitem *epi) -{ - struct file *file; - - file = epi->ffd.file; - if (!file_ref_get(&file->f_ref)) - file = NULL; - return file; -} - /* * Differs from ep_eventpoll_poll() in that internal callers already have * the ep->mtx so we need to start from depth=1, such that mutex_lock_nested() @@ -1117,18 +1111,17 @@ void eventpoll_release_file(struct file *file) { struct eventpoll *ep; struct epitem *epi; - bool dispose; /* - * Use the 'dying' flag to prevent a concurrent ep_clear_and_put() from - * touching the epitems list before eventpoll_release_file() can access - * the ep->mtx. + * A concurrent ep_remove() cannot outrace us: it pins @file via + * epi_fget(), which fails once __fput() has dropped the refcount + * to zero -- the path we're on. So any racing ep_remove() bails + * and leaves the epi for us to clean up here. */ again: spin_lock(&file->f_lock); if (file->f_ep && file->f_ep->first) { epi = hlist_entry(file->f_ep->first, struct epitem, fllink); - epi->dying = true; spin_unlock(&file->f_lock); /* @@ -1137,10 +1130,15 @@ again: */ ep = epi->ep; mutex_lock(&ep->mtx); - dispose = __ep_remove(ep, epi, true); + + ep_unregister_pollwait(ep, epi); + + ep_remove_file(ep, epi, file); + ep_remove_epi(ep, epi); + mutex_unlock(&ep->mtx); - if (dispose && ep_refcount_dec_and_test(ep)) + if (ep_refcount_dec_and_test(ep)) ep_free(ep); goto again; } @@ -1619,21 +1617,21 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, mutex_unlock(&tep->mtx); /* - * ep_remove_safe() calls in the later error paths can't lead to + * ep_remove() calls in the later error paths can't lead to * ep_free() as the ep file itself still holds an ep reference. */ ep_get(ep); /* now check if we've created too many backpaths */ if (unlikely(full_check && reverse_path_check())) { - ep_remove_safe(ep, epi); + ep_remove(ep, epi); return -EINVAL; } if (epi->event.events & EPOLLWAKEUP) { error = ep_create_wakeup_source(epi); if (error) { - ep_remove_safe(ep, epi); + ep_remove(ep, epi); return error; } } @@ -1657,7 +1655,7 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event, * high memory pressure. */ if (unlikely(!epq.epi)) { - ep_remove_safe(ep, epi); + ep_remove(ep, epi); return -ENOMEM; } @@ -2352,7 +2350,7 @@ int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds, * The eventpoll itself is still alive: the refcount * can't go to zero here. */ - ep_remove_safe(ep, epi); + ep_remove(ep, epi); error = 0; } else { error = -ENOENT; diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 5c3183ce350e..fbd45e7ae706 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -52,7 +52,7 @@ find_acceptable_alias(struct dentry *result, inode = result->d_inode; spin_lock(&inode->i_lock); - hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { + for_each_alias(dentry, inode) { dget(dentry); spin_unlock(&inode->i_lock); if (toput) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 0cf68f85dfd1..94283a991e5c 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -1082,9 +1081,6 @@ struct ext4_inode_info { spinlock_t i_raw_lock; /* protects updates to the raw inode */ - /* Fast commit wait queue for this inode */ - wait_queue_head_t i_fc_wait; - /* * Protect concurrent accesses on i_fc_lblk_start, i_fc_lblk_len * and inode's EXT4_FC_STATE_COMMITTING state bit. @@ -2976,7 +2972,8 @@ void __ext4_fc_track_unlink(handle_t *handle, struct inode *inode, void __ext4_fc_track_link(handle_t *handle, struct inode *inode, struct dentry *dentry); void ext4_fc_track_unlink(handle_t *handle, struct dentry *dentry); -void ext4_fc_track_link(handle_t *handle, struct dentry *dentry); +void ext4_fc_track_link(handle_t *handle, struct inode *inode, + struct dentry *dentry); void __ext4_fc_track_create(handle_t *handle, struct inode *inode, struct dentry *dentry); void ext4_fc_track_create(handle_t *handle, struct dentry *dentry); @@ -3101,8 +3098,9 @@ extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks); extern int ext4_chunk_trans_extent(struct inode *inode, int nrblocks); extern int ext4_meta_trans_blocks(struct inode *inode, int lblocks, int pextents); -extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode, - loff_t lstart, loff_t lend); +extern int ext4_block_zero_eof(struct inode *inode, loff_t from, loff_t end); +extern int ext4_zero_partial_blocks(struct inode *inode, loff_t lstart, + loff_t length, bool *did_zero); extern vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf); extern qsize_t *ext4_get_reserved_space(struct inode *inode); extern int ext4_get_projid(struct inode *inode, kprojid_t *projid); @@ -3721,7 +3719,7 @@ extern int ext4_handle_dirty_dirblock(handle_t *handle, struct inode *inode, extern int __ext4_unlink(struct inode *dir, const struct qstr *d_name, struct inode *inode, struct dentry *dentry); extern int __ext4_link(struct inode *dir, struct inode *inode, - struct dentry *dentry); + const struct qstr *d_name, struct dentry *dentry); #define S_SHIFT 12 static const unsigned char ext4_type_by_mode[(S_IFMT >> S_SHIFT) + 1] = { diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c index 5496b2c8e2cd..6b53a3f39fcd 100644 --- a/fs/ext4/extents-test.c +++ b/fs/ext4/extents-test.c @@ -142,10 +142,14 @@ static struct file_system_type ext_fs_type = { static void extents_kunit_exit(struct kunit *test) { - struct super_block *sb = k_ctx.k_ei->vfs_inode.i_sb; - struct ext4_sb_info *sbi = sb->s_fs_info; + struct ext4_sb_info *sbi; + if (!k_ctx.k_ei) + return; + + sbi = k_ctx.k_ei->vfs_inode.i_sb->s_fs_info; ext4_es_unregister_shrinker(sbi); + deactivate_super(sbi->s_sb); kfree(sbi); kfree(k_ctx.k_ei); kfree(k_ctx.k_data); @@ -224,33 +228,37 @@ static int extents_kunit_init(struct kunit *test) (struct kunit_ext_test_param *)(test->param_value); int err; - sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL); - if (IS_ERR(sb)) - return PTR_ERR(sb); - - sb->s_blocksize = 4096; - sb->s_blocksize_bits = 12; - sbi = kzalloc_obj(struct ext4_sb_info); if (sbi == NULL) return -ENOMEM; + sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL); + if (IS_ERR(sb)) { + kfree(sbi); + return PTR_ERR(sb); + } + sbi->s_sb = sb; sb->s_fs_info = sbi; + sb->s_blocksize = 4096; + sb->s_blocksize_bits = 12; + if (!param || !param->disable_zeroout) sbi->s_extent_max_zeroout_kb = 32; - /* setup the mock inode */ - k_ctx.k_ei = kzalloc_obj(struct ext4_inode_info); - if (k_ctx.k_ei == NULL) - return -ENOMEM; - ei = k_ctx.k_ei; - inode = &ei->vfs_inode; - err = ext4_es_register_shrinker(sbi); if (err) - return err; + goto out_deactivate; + + /* setup the mock inode */ + k_ctx.k_ei = kzalloc_obj(struct ext4_inode_info); + if (k_ctx.k_ei == NULL) { + err = -ENOMEM; + goto out; + } + ei = k_ctx.k_ei; + inode = &ei->vfs_inode; ext4_es_init_tree(&ei->i_es_tree); rwlock_init(&ei->i_es_lock); @@ -266,8 +274,10 @@ static int extents_kunit_init(struct kunit *test) inode->i_sb = sb; k_ctx.k_data = kzalloc(EXT_DATA_LEN * 4096, GFP_KERNEL); - if (k_ctx.k_data == NULL) - return -ENOMEM; + if (k_ctx.k_data == NULL) { + err = -ENOMEM; + goto out; + } /* * set the data area to a junk value @@ -309,7 +319,23 @@ static int extents_kunit_init(struct kunit *test) kunit_activate_static_stub(test, ext4_ext_zeroout, ext4_ext_zeroout_stub); kunit_activate_static_stub(test, ext4_issue_zeroout, ext4_issue_zeroout_stub); + up_write(&sb->s_umount); + return 0; + +out: + kfree(k_ctx.k_ei); + k_ctx.k_ei = NULL; + + kfree(k_ctx.k_data); + k_ctx.k_data = NULL; + + ext4_es_unregister_shrinker(sbi); +out_deactivate: + deactivate_locked_super(sb); + kfree(sbi); + + return err; } /* diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 95d404486f1a..125f628e738a 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -4571,30 +4571,30 @@ retry_remove_space: return err; } -static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, - ext4_lblk_t len, loff_t new_size, - int flags) +static int ext4_alloc_file_blocks(struct file *file, loff_t offset, loff_t len, + loff_t new_size, int flags) { struct inode *inode = file_inode(file); handle_t *handle; int ret = 0, ret2 = 0, ret3 = 0; int retries = 0; int depth = 0; + ext4_lblk_t len_lblk; struct ext4_map_blocks map; unsigned int credits; - loff_t epos, old_size = i_size_read(inode); + loff_t epos = 0, old_size = i_size_read(inode); unsigned int blkbits = inode->i_blkbits; bool alloc_zero = false; BUG_ON(!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)); - map.m_lblk = offset; - map.m_len = len; + map.m_lblk = offset >> blkbits; + map.m_len = len_lblk = EXT4_MAX_BLOCKS(len, offset, blkbits); /* * Don't normalize the request if it can fit in one extent so * that it doesn't get unnecessarily split into multiple * extents. */ - if (len <= EXT_UNWRITTEN_MAX_LEN) + if (len_lblk <= EXT_UNWRITTEN_MAX_LEN) flags |= EXT4_GET_BLOCKS_NO_NORMALIZE; /* @@ -4611,16 +4611,23 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, /* * credits to insert 1 extent into extent tree */ - credits = ext4_chunk_trans_blocks(inode, len); + credits = ext4_chunk_trans_blocks(inode, len_lblk); depth = ext_depth(inode); + /* Zero to the end of the block containing i_size */ + if (new_size > old_size) { + ret = ext4_block_zero_eof(inode, old_size, LLONG_MAX); + if (ret) + return ret; + } + retry: - while (len) { + while (len_lblk) { /* * Recalculate credits when extent tree depth changes. */ if (depth != ext_depth(inode)) { - credits = ext4_chunk_trans_blocks(inode, len); + credits = ext4_chunk_trans_blocks(inode, len_lblk); depth = ext_depth(inode); } @@ -4640,50 +4647,60 @@ retry: ext4_journal_stop(handle); break; } + ext4_update_inode_fsync_trans(handle, inode, 1); + ret = ext4_journal_stop(handle); + if (unlikely(ret)) + break; + /* * allow a full retry cycle for any remaining allocations */ retries = 0; - epos = EXT4_LBLK_TO_B(inode, map.m_lblk + ret); - inode_set_ctime_current(inode); - if (new_size) { - if (epos > new_size) - epos = new_size; - if (ext4_update_inode_size(inode, epos) & 0x1) - inode_set_mtime_to_ts(inode, - inode_get_ctime(inode)); - if (epos > old_size) { - pagecache_isize_extended(inode, old_size, epos); - ext4_zero_partial_blocks(handle, inode, - old_size, epos - old_size); - } - } - ret2 = ext4_mark_inode_dirty(handle, inode); - ext4_update_inode_fsync_trans(handle, inode, 1); - ret3 = ext4_journal_stop(handle); - ret2 = ret3 ? ret3 : ret2; - if (unlikely(ret2)) - break; if (alloc_zero && (map.m_flags & (EXT4_MAP_MAPPED | EXT4_MAP_UNWRITTEN))) { - ret2 = ext4_issue_zeroout(inode, map.m_lblk, map.m_pblk, - map.m_len); - if (likely(!ret2)) - ret2 = ext4_convert_unwritten_extents(NULL, + ret = ext4_issue_zeroout(inode, map.m_lblk, map.m_pblk, + map.m_len); + if (likely(!ret)) + ret = ext4_convert_unwritten_extents(NULL, inode, (loff_t)map.m_lblk << blkbits, (loff_t)map.m_len << blkbits); - if (ret2) + if (ret) break; } - map.m_lblk += ret; - map.m_len = len = len - ret; + map.m_lblk += map.m_len; + map.m_len = len_lblk = len_lblk - map.m_len; + epos = EXT4_LBLK_TO_B(inode, map.m_lblk); } + if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) goto retry; - return ret > 0 ? ret2 : ret; + if (!epos || !new_size) + return ret; + + /* + * Allocate blocks, update the file size to match the size of the + * already successfully allocated blocks. + */ + if (epos > new_size) + epos = new_size; + + handle = ext4_journal_start(inode, EXT4_HT_MISC, 1); + if (IS_ERR(handle)) + return ret ? ret : PTR_ERR(handle); + + ext4_update_inode_size(inode, epos); + ret2 = ext4_mark_inode_dirty(handle, inode); + ext4_update_inode_fsync_trans(handle, inode, 1); + ret3 = ext4_journal_stop(handle); + ret2 = ret3 ? ret3 : ret2; + + if (epos > old_size) + pagecache_isize_extended(inode, old_size, epos); + + return ret ? ret : ret2; } static int ext4_collapse_range(struct file *file, loff_t offset, loff_t len); @@ -4695,12 +4712,11 @@ static long ext4_zero_range(struct file *file, loff_t offset, { struct inode *inode = file_inode(file); handle_t *handle = NULL; - loff_t new_size = 0; + loff_t align_start, align_end, new_size = 0; loff_t end = offset + len; - ext4_lblk_t start_lblk, end_lblk; unsigned int blocksize = i_blocksize(inode); - unsigned int blkbits = inode->i_blkbits; - int ret, flags, credits; + bool partial_zeroed = false; + int ret, flags; trace_ext4_zero_range(inode, offset, len, mode); WARN_ON_ONCE(!inode_is_locked(inode)); @@ -4720,11 +4736,8 @@ static long ext4_zero_range(struct file *file, loff_t offset, flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT; /* Preallocate the range including the unaligned edges */ if (!IS_ALIGNED(offset | end, blocksize)) { - ext4_lblk_t alloc_lblk = offset >> blkbits; - ext4_lblk_t len_lblk = EXT4_MAX_BLOCKS(len, offset, blkbits); - - ret = ext4_alloc_file_blocks(file, alloc_lblk, len_lblk, - new_size, flags); + ret = ext4_alloc_file_blocks(file, offset, len, new_size, + flags); if (ret) return ret; } @@ -4739,18 +4752,17 @@ static long ext4_zero_range(struct file *file, loff_t offset, return ret; /* Zero range excluding the unaligned edges */ - start_lblk = EXT4_B_TO_LBLK(inode, offset); - end_lblk = end >> blkbits; - if (end_lblk > start_lblk) { - ext4_lblk_t zero_blks = end_lblk - start_lblk; - + align_start = round_up(offset, blocksize); + align_end = round_down(end, blocksize); + if (align_end > align_start) { if (mode & FALLOC_FL_WRITE_ZEROES) flags = EXT4_GET_BLOCKS_CREATE_ZERO | EXT4_EX_NOCACHE; else flags |= (EXT4_GET_BLOCKS_CONVERT_UNWRITTEN | EXT4_EX_NOCACHE); - ret = ext4_alloc_file_blocks(file, start_lblk, zero_blks, - new_size, flags); + ret = ext4_alloc_file_blocks(file, align_start, + align_end - align_start, new_size, + flags); if (ret) return ret; } @@ -4758,25 +4770,24 @@ static long ext4_zero_range(struct file *file, loff_t offset, if (IS_ALIGNED(offset | end, blocksize)) return ret; - /* - * In worst case we have to writeout two nonadjacent unwritten - * blocks and update the inode - */ - credits = (2 * ext4_ext_index_trans_blocks(inode, 2)) + 1; - if (ext4_should_journal_data(inode)) - credits += 2; - handle = ext4_journal_start(inode, EXT4_HT_MISC, credits); + /* Zero out partial block at the edges of the range */ + ret = ext4_zero_partial_blocks(inode, offset, len, &partial_zeroed); + if (ret) + return ret; + if (((file->f_flags & O_SYNC) || IS_SYNC(inode)) && partial_zeroed) { + ret = filemap_write_and_wait_range(inode->i_mapping, offset, + end - 1); + if (ret) + return ret; + } + + handle = ext4_journal_start(inode, EXT4_HT_MISC, 1); if (IS_ERR(handle)) { ret = PTR_ERR(handle); ext4_std_error(inode->i_sb, ret); return ret; } - /* Zero out partial block at the edges of the range */ - ret = ext4_zero_partial_blocks(handle, inode, offset, len); - if (ret) - goto out_handle; - if (new_size) ext4_update_inode_size(inode, new_size); ret = ext4_mark_inode_dirty(handle, inode); @@ -4784,7 +4795,7 @@ static long ext4_zero_range(struct file *file, loff_t offset, goto out_handle; ext4_update_inode_fsync_trans(handle, inode, 1); - if (file->f_flags & O_SYNC) + if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) ext4_handle_sync(handle); out_handle: @@ -4798,15 +4809,11 @@ static long ext4_do_fallocate(struct file *file, loff_t offset, struct inode *inode = file_inode(file); loff_t end = offset + len; loff_t new_size = 0; - ext4_lblk_t start_lblk, len_lblk; int ret; trace_ext4_fallocate_enter(inode, offset, len, mode); WARN_ON_ONCE(!inode_is_locked(inode)); - start_lblk = offset >> inode->i_blkbits; - len_lblk = EXT4_MAX_BLOCKS(len, offset, inode->i_blkbits); - /* We only support preallocation for extent-based files only. */ if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) { ret = -EOPNOTSUPP; @@ -4821,17 +4828,19 @@ static long ext4_do_fallocate(struct file *file, loff_t offset, goto out; } - ret = ext4_alloc_file_blocks(file, start_lblk, len_lblk, new_size, + ret = ext4_alloc_file_blocks(file, offset, len, new_size, EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT); if (ret) goto out; - if (file->f_flags & O_SYNC && EXT4_SB(inode->i_sb)->s_journal) { + if (((file->f_flags & O_SYNC) || IS_SYNC(inode)) && + EXT4_SB(inode->i_sb)->s_journal) { ret = ext4_fc_commit(EXT4_SB(inode->i_sb)->s_journal, EXT4_I(inode)->i_sync_tid); } out: - trace_ext4_fallocate_exit(inode, offset, len_lblk, ret); + trace_ext4_fallocate_exit(inode, offset, + EXT4_MAX_BLOCKS(len, offset, inode->i_blkbits), ret); return ret; } @@ -5598,7 +5607,7 @@ static int ext4_collapse_range(struct file *file, loff_t offset, loff_t len) goto out_handle; ext4_update_inode_fsync_trans(handle, inode, 1); - if (IS_SYNC(inode)) + if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) ext4_handle_sync(handle); out_handle: @@ -5722,7 +5731,7 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len) goto out_handle; ext4_update_inode_fsync_trans(handle, inode, 1); - if (IS_SYNC(inode)) + if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) ext4_handle_sync(handle); out_handle: diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c index 390f25dee2b1..b3c22636251d 100644 --- a/fs/ext4/fast_commit.c +++ b/fs/ext4/fast_commit.c @@ -13,6 +13,7 @@ #include "mballoc.h" #include +#include /* * Ext4 Fast Commits * ----------------- @@ -215,7 +216,6 @@ void ext4_fc_init_inode(struct inode *inode) ext4_clear_inode_state(inode, EXT4_STATE_FC_COMMITTING); INIT_LIST_HEAD(&ei->i_fc_list); INIT_LIST_HEAD(&ei->i_fc_dilist); - init_waitqueue_head(&ei->i_fc_wait); } static bool ext4_fc_disabled(struct super_block *sb) @@ -224,6 +224,12 @@ static bool ext4_fc_disabled(struct super_block *sb) (EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY)); } +static bool ext4_fc_eligible(struct super_block *sb) +{ + return !ext4_fc_disabled(sb) && + !(ext4_test_mount_flag(sb, EXT4_MF_FC_INELIGIBLE)); +} + /* * Remove inode from fast commit list. If the inode is being committed * we wait until inode commit is done. @@ -320,7 +326,7 @@ void ext4_fc_mark_ineligible(struct super_block *sb, int reason, handle_t *handl if (ext4_fc_disabled(sb)) return; - if (handle && !IS_ERR(handle)) + if (!IS_ERR_OR_NULL(handle)) tid = handle->h_transaction->t_tid; else { read_lock(&sbi->s_journal->j_state_lock); @@ -473,13 +479,8 @@ void ext4_fc_track_unlink(handle_t *handle, struct dentry *dentry) { struct inode *inode = d_inode(dentry); - if (ext4_fc_disabled(inode->i_sb)) - return; - - if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE)) - return; - - __ext4_fc_track_unlink(handle, inode, dentry); + if (ext4_fc_eligible(inode->i_sb)) + __ext4_fc_track_unlink(handle, inode, dentry); } void __ext4_fc_track_link(handle_t *handle, @@ -496,17 +497,11 @@ void __ext4_fc_track_link(handle_t *handle, trace_ext4_fc_track_link(handle, inode, dentry, ret); } -void ext4_fc_track_link(handle_t *handle, struct dentry *dentry) +void ext4_fc_track_link(handle_t *handle, struct inode *inode, + struct dentry *dentry) { - struct inode *inode = d_inode(dentry); - - if (ext4_fc_disabled(inode->i_sb)) - return; - - if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE)) - return; - - __ext4_fc_track_link(handle, inode, dentry); + if (ext4_fc_eligible(inode->i_sb)) + __ext4_fc_track_link(handle, inode, dentry); } void __ext4_fc_track_create(handle_t *handle, struct inode *inode, @@ -527,13 +522,8 @@ void ext4_fc_track_create(handle_t *handle, struct dentry *dentry) { struct inode *inode = d_inode(dentry); - if (ext4_fc_disabled(inode->i_sb)) - return; - - if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE)) - return; - - __ext4_fc_track_create(handle, inode, dentry); + if (ext4_fc_eligible(inode->i_sb)) + __ext4_fc_track_create(handle, inode, dentry); } /* __track_fn for inode tracking */ @@ -557,16 +547,13 @@ void ext4_fc_track_inode(handle_t *handle, struct inode *inode) if (S_ISDIR(inode->i_mode)) return; - if (ext4_fc_disabled(inode->i_sb)) - return; - if (ext4_should_journal_data(inode)) { ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_INODE_JOURNAL_DATA, handle); return; } - if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE)) + if (!ext4_fc_eligible(inode->i_sb)) return; /* @@ -644,10 +631,7 @@ void ext4_fc_track_range(handle_t *handle, struct inode *inode, ext4_lblk_t star if (S_ISDIR(inode->i_mode)) return; - if (ext4_fc_disabled(inode->i_sb)) - return; - - if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE)) + if (!ext4_fc_eligible(inode->i_sb)) return; if (ext4_has_inline_data(inode)) { @@ -1446,7 +1430,6 @@ static int ext4_fc_replay_link_internal(struct super_block *sb, struct inode *inode) { struct inode *dir = NULL; - struct dentry *dentry_dir = NULL, *dentry_inode = NULL; struct qstr qstr_dname = QSTR_INIT(darg->dname, darg->dname_len); int ret = 0; @@ -1457,21 +1440,7 @@ static int ext4_fc_replay_link_internal(struct super_block *sb, goto out; } - dentry_dir = d_obtain_alias(dir); - if (IS_ERR(dentry_dir)) { - ext4_debug("Failed to obtain dentry"); - dentry_dir = NULL; - goto out; - } - - dentry_inode = d_alloc(dentry_dir, &qstr_dname); - if (!dentry_inode) { - ext4_debug("Inode dentry not created."); - ret = -ENOMEM; - goto out; - } - - ret = __ext4_link(dir, inode, dentry_inode); + ret = __ext4_link(dir, inode, &qstr_dname, NULL); /* * It's possible that link already existed since data blocks * for the dir in question got persisted before we crashed OR @@ -1485,16 +1454,8 @@ static int ext4_fc_replay_link_internal(struct super_block *sb, ret = 0; out: - if (dentry_dir) { - d_drop(dentry_dir); - dput(dentry_dir); - } else if (dir) { + if (dir) iput(dir); - } - if (dentry_inode) { - d_drop(dentry_inode); - dput(dentry_inode); - } return ret; } @@ -1759,8 +1720,7 @@ int ext4_fc_record_regions(struct super_block *sb, int ino, } /* Replay add range tag */ -static int ext4_fc_replay_add_range(struct super_block *sb, - struct ext4_fc_tl_mem *tl, u8 *val) +static int ext4_fc_replay_add_range(struct super_block *sb, u8 *val) { struct ext4_fc_add_range fc_add_ex; struct ext4_extent newex, *ex; @@ -1880,8 +1840,7 @@ out: /* Replay DEL_RANGE tag */ static int -ext4_fc_replay_del_range(struct super_block *sb, - struct ext4_fc_tl_mem *tl, u8 *val) +ext4_fc_replay_del_range(struct super_block *sb, u8 *val) { struct inode *inode; struct ext4_fc_del_range lrange; @@ -2251,13 +2210,13 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh, ret = ext4_fc_replay_unlink(sb, &tl, val); break; case EXT4_FC_TAG_ADD_RANGE: - ret = ext4_fc_replay_add_range(sb, &tl, val); + ret = ext4_fc_replay_add_range(sb, val); break; case EXT4_FC_TAG_CREAT: ret = ext4_fc_replay_create(sb, &tl, val); break; case EXT4_FC_TAG_DEL_RANGE: - ret = ext4_fc_replay_del_range(sb, &tl, val); + ret = ext4_fc_replay_del_range(sb, val); break; case EXT4_FC_TAG_INODE: ret = ext4_fc_replay_inode(sb, &tl, val); diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 5e02f6cf653e..eb1a323962b1 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -270,6 +270,8 @@ static ssize_t ext4_generic_write_checks(struct kiocb *iocb, static ssize_t ext4_write_checks(struct kiocb *iocb, struct iov_iter *from) { + struct inode *inode = file_inode(iocb->ki_filp); + loff_t old_size = i_size_read(inode); ssize_t ret, count; count = ext4_generic_write_checks(iocb, from); @@ -279,6 +281,21 @@ static ssize_t ext4_write_checks(struct kiocb *iocb, struct iov_iter *from) ret = file_modified(iocb->ki_filp); if (ret) return ret; + + /* + * If the position is beyond the EOF, it is necessary to zero out the + * partial block that beyond the existing EOF, as it may contains + * stale data written through mmap. + */ + if (iocb->ki_pos > old_size && !ext4_verity_in_progress(inode)) { + if (iocb->ki_flags & IOCB_NOWAIT) + return -EAGAIN; + + ret = ext4_block_zero_eof(inode, old_size, iocb->ki_pos); + if (ret) + return ret; + } + return count; } diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 64dba7679245..c2c2d6ac7f3d 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1468,10 +1468,9 @@ static int ext4_write_end(const struct kiocb *iocb, folio_unlock(folio); folio_put(folio); - if (old_size < pos && !verity) { + if (old_size < pos && !verity) pagecache_isize_extended(inode, old_size, pos); - ext4_zero_partial_blocks(handle, inode, old_size, pos - old_size); - } + /* * Don't mark the inode dirty under folio lock. First, it unnecessarily * makes the holding time of folio lock longer. Second, it forces lock @@ -1586,10 +1585,8 @@ static int ext4_journalled_write_end(const struct kiocb *iocb, folio_unlock(folio); folio_put(folio); - if (old_size < pos && !verity) { + if (old_size < pos && !verity) pagecache_isize_extended(inode, old_size, pos); - ext4_zero_partial_blocks(handle, inode, old_size, pos - old_size); - } if (size_changed) { ret2 = ext4_mark_inode_dirty(handle, inode); @@ -1759,8 +1756,22 @@ static void mpage_release_unused_pages(struct mpage_da_data *mpd, BUG_ON(!folio_test_locked(folio)); BUG_ON(folio_test_writeback(folio)); if (invalidate) { - if (folio_mapped(folio)) + if (folio_mapped(folio)) { folio_clear_dirty_for_io(folio); + /* + * Unmap folio from page + * tables to prevent + * subsequent accesses through + * stale PTEs. This ensures + * future accesses trigger new + * page faults rather than + * reusing the invalidated + * folio. + */ + unmap_mapping_pages(folio->mapping, + folio->index, + folio_nr_pages(folio), false); + } block_invalidate_folio(folio, 0, folio_size(folio)); folio_clear_uptodate(folio); @@ -3043,17 +3054,23 @@ static int ext4_writepages(struct address_space *mapping, int ext4_normal_submit_inode_data_buffers(struct jbd2_inode *jinode) { + loff_t range_start, range_end; struct writeback_control wbc = { .sync_mode = WB_SYNC_ALL, .nr_to_write = LONG_MAX, - .range_start = jinode->i_dirty_start, - .range_end = jinode->i_dirty_end, }; struct mpage_da_data mpd = { .inode = jinode->i_vfs_inode, .wbc = &wbc, .can_map = 0, }; + + if (!jbd2_jinode_get_dirty_range(jinode, &range_start, &range_end)) + return 0; + + wbc.range_start = range_start; + wbc.range_end = range_end; + return ext4_do_writepages(&mpd); } @@ -3208,7 +3225,7 @@ static int ext4_da_do_write_end(struct address_space *mapping, struct inode *inode = mapping->host; loff_t old_size = inode->i_size; bool disksize_changed = false; - loff_t new_i_size, zero_len = 0; + loff_t new_i_size; handle_t *handle; if (unlikely(!folio_buffers(folio))) { @@ -3252,19 +3269,15 @@ static int ext4_da_do_write_end(struct address_space *mapping, folio_unlock(folio); folio_put(folio); - if (pos > old_size) { + if (pos > old_size) pagecache_isize_extended(inode, old_size, pos); - zero_len = pos - old_size; - } - if (!disksize_changed && !zero_len) + if (!disksize_changed) return copied; - handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); + handle = ext4_journal_start(inode, EXT4_HT_INODE, 1); if (IS_ERR(handle)) return PTR_ERR(handle); - if (zero_len) - ext4_zero_partial_blocks(handle, inode, old_size, zero_len); ext4_mark_inode_dirty(handle, inode); ext4_journal_stop(handle); @@ -4014,12 +4027,11 @@ void ext4_set_aops(struct inode *inode) * ext4_punch_hole, etc) which needs to be properly zeroed out. Otherwise a * racing writeback can come later and flush the stale pagecache to disk. */ -static int __ext4_block_zero_page_range(handle_t *handle, - struct address_space *mapping, loff_t from, loff_t length) +static struct buffer_head *ext4_load_tail_bh(struct inode *inode, loff_t from) { unsigned int offset, blocksize, pos; ext4_lblk_t iblock; - struct inode *inode = mapping->host; + struct address_space *mapping = inode->i_mapping; struct buffer_head *bh; struct folio *folio; int err = 0; @@ -4028,7 +4040,7 @@ static int __ext4_block_zero_page_range(handle_t *handle, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, mapping_gfp_constraint(mapping, ~__GFP_FS)); if (IS_ERR(folio)) - return PTR_ERR(folio); + return ERR_CAST(folio); blocksize = inode->i_sb->s_blocksize; @@ -4080,47 +4092,92 @@ static int __ext4_block_zero_page_range(handle_t *handle, } } } - if (ext4_should_journal_data(inode)) { - BUFFER_TRACE(bh, "get write access"); - err = ext4_journal_get_write_access(handle, inode->i_sb, bh, - EXT4_JTR_NONE); - if (err) - goto unlock; - } - folio_zero_range(folio, offset, length); - BUFFER_TRACE(bh, "zeroed end of block"); - - if (ext4_should_journal_data(inode)) { - err = ext4_dirty_journalled_data(handle, bh); - } else { - mark_buffer_dirty(bh); - /* - * Only the written block requires ordered data to prevent - * exposing stale data. - */ - if (!buffer_unwritten(bh) && !buffer_delay(bh) && - ext4_should_order_data(inode)) - err = ext4_jbd2_inode_add_write(handle, inode, from, - length); - } + return bh; unlock: folio_unlock(folio); folio_put(folio); + return err ? ERR_PTR(err) : NULL; +} + +static int ext4_block_do_zero_range(struct inode *inode, loff_t from, + loff_t length, bool *did_zero, + bool *zero_written) +{ + struct buffer_head *bh; + struct folio *folio; + + bh = ext4_load_tail_bh(inode, from); + if (IS_ERR_OR_NULL(bh)) + return PTR_ERR_OR_ZERO(bh); + + folio = bh->b_folio; + folio_zero_range(folio, offset_in_folio(folio, from), length); + BUFFER_TRACE(bh, "zeroed end of block"); + + mark_buffer_dirty(bh); + if (did_zero) + *did_zero = true; + if (zero_written && !buffer_unwritten(bh) && !buffer_delay(bh)) + *zero_written = true; + + folio_unlock(folio); + folio_put(folio); + return 0; +} + +static int ext4_block_journalled_zero_range(struct inode *inode, loff_t from, + loff_t length, bool *did_zero) +{ + struct buffer_head *bh; + struct folio *folio; + handle_t *handle; + int err; + + handle = ext4_journal_start(inode, EXT4_HT_MISC, 1); + if (IS_ERR(handle)) + return PTR_ERR(handle); + + bh = ext4_load_tail_bh(inode, from); + if (IS_ERR_OR_NULL(bh)) { + err = PTR_ERR_OR_ZERO(bh); + goto out_handle; + } + folio = bh->b_folio; + + BUFFER_TRACE(bh, "get write access"); + err = ext4_journal_get_write_access(handle, inode->i_sb, bh, + EXT4_JTR_NONE); + if (err) + goto out; + + folio_zero_range(folio, offset_in_folio(folio, from), length); + BUFFER_TRACE(bh, "zeroed end of block"); + + err = ext4_dirty_journalled_data(handle, bh); + if (err) + goto out; + + if (did_zero) + *did_zero = true; +out: + folio_unlock(folio); + folio_put(folio); +out_handle: + ext4_journal_stop(handle); return err; } /* - * ext4_block_zero_page_range() zeros out a mapping of length 'length' - * starting from file offset 'from'. The range to be zero'd must - * be contained with in one block. If the specified range exceeds - * the end of the block it will be shortened to end of the block - * that corresponds to 'from' + * Zeros out a mapping of length 'length' starting from file offset + * 'from'. The range to be zero'd must be contained with in one block. + * If the specified range exceeds the end of the block it will be + * shortened to end of the block that corresponds to 'from'. */ -static int ext4_block_zero_page_range(handle_t *handle, - struct address_space *mapping, loff_t from, loff_t length) +static int ext4_block_zero_range(struct inode *inode, + loff_t from, loff_t length, bool *did_zero, + bool *zero_written) { - struct inode *inode = mapping->host; unsigned blocksize = inode->i_sb->s_blocksize; unsigned int max = blocksize - (from & (blocksize - 1)); @@ -4132,40 +4189,73 @@ static int ext4_block_zero_page_range(handle_t *handle, length = max; if (IS_DAX(inode)) { - return dax_zero_range(inode, from, length, NULL, + return dax_zero_range(inode, from, length, did_zero, &ext4_iomap_ops); + } else if (ext4_should_journal_data(inode)) { + return ext4_block_journalled_zero_range(inode, from, length, + did_zero); } - return __ext4_block_zero_page_range(handle, mapping, from, length); + return ext4_block_do_zero_range(inode, from, length, did_zero, + zero_written); } /* - * ext4_block_truncate_page() zeroes out a mapping from file offset `from' - * up to the end of the block which corresponds to `from'. - * This required during truncate. We need to physically zero the tail end - * of that block so it doesn't yield old data if the file is later grown. + * Zero out a mapping from file offset 'from' up to the end of the block + * which corresponds to 'from' or to the given 'end' inside this block. + * This required during truncate up and performing append writes. We need + * to physically zero the tail end of that block so it doesn't yield old + * data if the file is grown. */ -static int ext4_block_truncate_page(handle_t *handle, - struct address_space *mapping, loff_t from) +int ext4_block_zero_eof(struct inode *inode, loff_t from, loff_t end) { - unsigned length; - unsigned blocksize; - struct inode *inode = mapping->host; + unsigned int blocksize = i_blocksize(inode); + unsigned int offset; + loff_t length = end - from; + bool did_zero = false; + bool zero_written = false; + int err; + offset = from & (blocksize - 1); + if (!offset || from >= end) + return 0; /* If we are processing an encrypted inode during orphan list handling */ if (IS_ENCRYPTED(inode) && !fscrypt_has_encryption_key(inode)) return 0; - blocksize = i_blocksize(inode); - length = blocksize - (from & (blocksize - 1)); + if (length > blocksize - offset) + length = blocksize - offset; - return ext4_block_zero_page_range(handle, mapping, from, length); + err = ext4_block_zero_range(inode, from, length, + &did_zero, &zero_written); + if (err) + return err; + /* + * It's necessary to order zeroed data before update i_disksize when + * truncating up or performing an append write, because there might be + * exposing stale on-disk data which may caused by concurrent post-EOF + * mmap write during folio writeback. + */ + if (ext4_should_order_data(inode) && + did_zero && zero_written && !IS_DAX(inode)) { + handle_t *handle; + + handle = ext4_journal_start(inode, EXT4_HT_MISC, 1); + if (IS_ERR(handle)) + return PTR_ERR(handle); + + err = ext4_jbd2_inode_add_write(handle, inode, from, length); + ext4_journal_stop(handle); + if (err) + return err; + } + + return 0; } -int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode, - loff_t lstart, loff_t length) +int ext4_zero_partial_blocks(struct inode *inode, loff_t lstart, loff_t length, + bool *did_zero) { struct super_block *sb = inode->i_sb; - struct address_space *mapping = inode->i_mapping; unsigned partial_start, partial_end; ext4_fsblk_t start, end; loff_t byte_end = (lstart + length - 1); @@ -4180,22 +4270,21 @@ int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode, /* Handle partial zero within the single block */ if (start == end && (partial_start || (partial_end != sb->s_blocksize - 1))) { - err = ext4_block_zero_page_range(handle, mapping, - lstart, length); + err = ext4_block_zero_range(inode, lstart, length, did_zero, + NULL); return err; } /* Handle partial zero out on the start of the range */ if (partial_start) { - err = ext4_block_zero_page_range(handle, mapping, - lstart, sb->s_blocksize); + err = ext4_block_zero_range(inode, lstart, sb->s_blocksize, + did_zero, NULL); if (err) return err; } /* Handle partial zero out on the end of the range */ if (partial_end != sb->s_blocksize - 1) - err = ext4_block_zero_page_range(handle, mapping, - byte_end - partial_end, - partial_end + 1); + err = ext4_block_zero_range(inode, byte_end - partial_end, + partial_end + 1, did_zero, NULL); return err; } @@ -4344,6 +4433,7 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) loff_t end = offset + length; handle_t *handle; unsigned int credits; + bool partial_zeroed = false; int ret; trace_ext4_punch_hole(inode, offset, length, 0); @@ -4370,17 +4460,6 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) end = max_end; length = end - offset; - /* - * Attach jinode to inode for jbd2 if we do any zeroing of partial - * block. - */ - if (!IS_ALIGNED(offset | end, sb->s_blocksize)) { - ret = ext4_inode_attach_jinode(inode); - if (ret < 0) - return ret; - } - - ret = ext4_update_disksize_before_punch(inode, offset, length); if (ret) return ret; @@ -4390,8 +4469,18 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) if (ret) return ret; + ret = ext4_zero_partial_blocks(inode, offset, length, &partial_zeroed); + if (ret) + return ret; + if (((file->f_flags & O_SYNC) || IS_SYNC(inode)) && partial_zeroed) { + ret = filemap_write_and_wait_range(inode->i_mapping, offset, + end - 1); + if (ret) + return ret; + } + if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) - credits = ext4_chunk_trans_extent(inode, 2); + credits = ext4_chunk_trans_extent(inode, 0); else credits = ext4_blocks_for_truncate(inode); handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, credits); @@ -4401,10 +4490,6 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) return ret; } - ret = ext4_zero_partial_blocks(handle, inode, offset, length); - if (ret) - goto out_handle; - /* If there are blocks to remove, do it */ start_lblk = EXT4_B_TO_LBLK(inode, offset); end_lblk = end >> inode->i_blkbits; @@ -4441,7 +4526,7 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) goto out_handle; ext4_update_inode_fsync_trans(handle, inode, 1); - if (IS_SYNC(inode)) + if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) ext4_handle_sync(handle); out_handle: ext4_journal_stop(handle); @@ -4512,7 +4597,6 @@ int ext4_truncate(struct inode *inode) unsigned int credits; int err = 0, err2; handle_t *handle; - struct address_space *mapping = inode->i_mapping; /* * There is a possibility that we're either freeing the inode @@ -4542,6 +4626,11 @@ int ext4_truncate(struct inode *inode) err = ext4_inode_attach_jinode(inode); if (err) goto out_trace; + + /* Zero to the end of the block containing i_size */ + err = ext4_block_zero_eof(inode, inode->i_size, LLONG_MAX); + if (err) + goto out_trace; } if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) @@ -4555,9 +4644,6 @@ int ext4_truncate(struct inode *inode) goto out_trace; } - if (inode->i_size & (inode->i_sb->s_blocksize - 1)) - ext4_block_truncate_page(handle, mapping, inode->i_size); - /* * We add the inode to the orphan list, so that if this * truncate spans multiple transactions, and we crash, we will @@ -5927,15 +6013,6 @@ int ext4_setattr(struct mnt_idmap *idmap, struct dentry *dentry, goto out_mmap_sem; } - handle = ext4_journal_start(inode, EXT4_HT_INODE, 3); - if (IS_ERR(handle)) { - error = PTR_ERR(handle); - goto out_mmap_sem; - } - if (ext4_handle_valid(handle) && shrink) { - error = ext4_orphan_add(handle, inode); - orphan = 1; - } /* * Update c/mtime and tail zero the EOF folio on * truncate up. ext4_truncate() handles the shrink case @@ -5944,9 +6021,22 @@ int ext4_setattr(struct mnt_idmap *idmap, struct dentry *dentry, if (!shrink) { inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); - if (oldsize & (inode->i_sb->s_blocksize - 1)) - ext4_block_truncate_page(handle, - inode->i_mapping, oldsize); + if (oldsize & (inode->i_sb->s_blocksize - 1)) { + error = ext4_block_zero_eof(inode, + oldsize, LLONG_MAX); + if (error) + goto out_mmap_sem; + } + } + + handle = ext4_journal_start(inode, EXT4_HT_INODE, 3); + if (IS_ERR(handle)) { + error = PTR_ERR(handle); + goto out_mmap_sem; + } + if (ext4_handle_valid(handle) && shrink) { + error = ext4_orphan_add(handle, inode); + orphan = 1; } if (shrink) diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c index c75b91ae0cf0..90ed505fa4b1 100644 --- a/fs/ext4/mballoc-test.c +++ b/fs/ext4/mballoc-test.c @@ -362,7 +362,6 @@ static int mbt_kunit_init(struct kunit *test) return ret; } - test->priv = sb; kunit_activate_static_stub(test, ext4_read_block_bitmap_nowait, ext4_read_block_bitmap_nowait_stub); @@ -383,6 +382,8 @@ static int mbt_kunit_init(struct kunit *test) return -ENOMEM; } + test->priv = sb; + return 0; } @@ -390,6 +391,9 @@ static void mbt_kunit_exit(struct kunit *test) { struct super_block *sb = (struct super_block *)test->priv; + if (!sb) + return; + mbt_mb_release(sb); mbt_ctx_release(sb); mbt_ext4_free_super_block(sb); diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index a44514a3f5fe..ed1bd00e11cd 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2876,7 +2876,7 @@ ext4_group_t ext4_mb_prefetch(struct super_block *sb, ext4_group_t group, EXT4_MB_GRP_NEED_INIT(grp) && ext4_free_group_clusters(sb, gdp) > 0 ) { bh = ext4_read_block_bitmap_nowait(sb, group, true); - if (bh && !IS_ERR(bh)) { + if (!IS_ERR_OR_NULL(bh)) { if (!buffer_uptodate(bh) && cnt) (*cnt)++; brelse(bh); @@ -4561,22 +4561,16 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac, (req <= (size) || max <= (chunk_size)) /* first, try to predict filesize */ - /* XXX: should this table be tunable? */ start_off = 0; - if (size <= 16 * 1024) { - size = 16 * 1024; - } else if (size <= 32 * 1024) { - size = 32 * 1024; - } else if (size <= 64 * 1024) { - size = 64 * 1024; - } else if (size <= 128 * 1024) { - size = 128 * 1024; - } else if (size <= 256 * 1024) { - size = 256 * 1024; - } else if (size <= 512 * 1024) { - size = 512 * 1024; - } else if (size <= 1024 * 1024) { - size = 1024 * 1024; + if (size <= SZ_1M) { + /* + * For files up to 1MB, round up the preallocation size to + * the next power of two, with a minimum of 16KB. + */ + if (size <= (unsigned long)SZ_16K) + size = SZ_16K; + else + size = roundup_pow_of_two(size); } else if (NRL_CHECK_SIZE(size, 4 * 1024 * 1024, max, 2 * 1024)) { start_off = ((loff_t)ac->ac_o_ex.fe_logical >> (21 - bsbits)) << 21; diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index ab17c1d3a7b5..3329b7ad5dbd 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -224,8 +224,8 @@ static int mext_move_begin(struct mext_data *mext, struct folio *folio[2], } /* Adjust the moving length according to the length of shorter folio. */ - move_len = umin(folio_pos(folio[0]) + folio_size(folio[0]) - orig_pos, - folio_pos(folio[1]) + folio_size(folio[1]) - donor_pos); + move_len = umin(folio_next_pos(folio[0]) - orig_pos, + folio_next_pos(folio[1]) - donor_pos); move_len >>= blkbits; if (move_len < mext->orig_map.m_len) mext->orig_map.m_len = move_len; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 503dc9ffd614..4a47fbd8dd30 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -647,7 +647,7 @@ static struct stats dx_show_leaf(struct inode *dir, /* Directory is not encrypted */ (void) ext4fs_dirhash(dir, de->name, de->name_len, &h); - printk("%*.s:(U)%x.%u ", len, + printk("%.*s:(U)%x.%u ", len, name, h.hash, (unsigned) ((char *) de - base)); @@ -683,7 +683,7 @@ static struct stats dx_show_leaf(struct inode *dir, (void) ext4fs_dirhash(dir, de->name, de->name_len, &h); - printk("%*.s:(E)%x.%u ", len, name, + printk("%.*s:(E)%x.%u ", len, name, h.hash, (unsigned) ((char *) de - base)); fscrypt_fname_free_buffer( @@ -694,7 +694,7 @@ static struct stats dx_show_leaf(struct inode *dir, char *name = de->name; (void) ext4fs_dirhash(dir, de->name, de->name_len, &h); - printk("%*.s:%x.%u ", len, name, h.hash, + printk("%.*s:%x.%u ", len, name, h.hash, (unsigned) ((char *) de - base)); #endif } @@ -723,7 +723,7 @@ struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir, struct stats stats; printk("%s%3u:%03u hash %8x/%8x ",levels?"":" ", i, block, hash, range); bh = ext4_bread(NULL,dir, block, 0); - if (!bh || IS_ERR(bh)) + if (IS_ERR_OR_NULL(bh)) continue; stats = levels? dx_show_entries(hinfo, dir, ((struct dx_node *) bh->b_data)->entries, levels - 1): @@ -2353,10 +2353,10 @@ out_frames: * may not sleep between calling this and putting something into * the entry, as someone else might have used it while you slept. */ -static int ext4_add_entry(handle_t *handle, struct dentry *dentry, +static int __ext4_add_entry(handle_t *handle, struct inode *dir, + const struct qstr *d_name, struct inode *inode) { - struct inode *dir = d_inode(dentry->d_parent); struct buffer_head *bh = NULL; struct ext4_dir_entry_2 *de; struct super_block *sb; @@ -2373,13 +2373,10 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, sb = dir->i_sb; blocksize = sb->s_blocksize; - if (fscrypt_is_nokey_name(dentry)) - return -ENOKEY; - - if (!generic_ci_validate_strict_name(dir, &dentry->d_name)) + if (!generic_ci_validate_strict_name(dir, d_name)) return -EINVAL; - retval = ext4_fname_setup_filename(dir, &dentry->d_name, 0, &fname); + retval = ext4_fname_setup_filename(dir, d_name, 0, &fname); if (retval) return retval; @@ -2460,6 +2457,16 @@ out: return retval; } +static int ext4_add_entry(handle_t *handle, struct dentry *dentry, + struct inode *inode) +{ + struct inode *dir = d_inode(dentry->d_parent); + + if (fscrypt_is_nokey_name(dentry)) + return -ENOKEY; + return __ext4_add_entry(handle, dir, &dentry->d_name, inode); +} + /* * Returns 0 for success, or a negative error value */ @@ -3445,7 +3452,8 @@ out_retry: return err; } -int __ext4_link(struct inode *dir, struct inode *inode, struct dentry *dentry) +int __ext4_link(struct inode *dir, struct inode *inode, + const struct qstr *d_name, struct dentry *dentry) { handle_t *handle; int err, retries = 0; @@ -3461,9 +3469,8 @@ retry: inode_set_ctime_current(inode); ext4_inc_count(inode); - ihold(inode); - err = ext4_add_entry(handle, dentry, inode); + err = __ext4_add_entry(handle, dir, d_name, inode); if (!err) { err = ext4_mark_inode_dirty(handle, inode); /* this can happen only for tmpfile being @@ -3471,11 +3478,10 @@ retry: */ if (inode->i_nlink == 1) ext4_orphan_del(handle, inode); - d_instantiate(dentry, inode); - ext4_fc_track_link(handle, dentry); + if (dentry) + ext4_fc_track_link(handle, inode, dentry); } else { drop_nlink(inode); - iput(inode); } ext4_journal_stop(handle); if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) @@ -3504,9 +3510,13 @@ static int ext4_link(struct dentry *old_dentry, err = dquot_initialize(dir); if (err) return err; - return __ext4_link(dir, inode, dentry); + err = __ext4_link(dir, inode, &dentry->d_name, dentry); + if (!err) { + ihold(inode); + d_instantiate(dentry, inode); + } + return err; } - /* * Try to find buffer head where contains the parent block. * It should be the inode block if it is inlined or the 1st block diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 578508eb4f1a..6a77db4d3124 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -521,6 +521,7 @@ static bool ext4_journalled_writepage_needs_redirty(struct jbd2_inode *jinode, { struct buffer_head *bh, *head; struct journal_head *jh; + transaction_t *trans = READ_ONCE(jinode->i_transaction); bh = head = folio_buffers(folio); do { @@ -539,7 +540,7 @@ static bool ext4_journalled_writepage_needs_redirty(struct jbd2_inode *jinode, */ jh = bh2jh(bh); if (buffer_dirty(bh) || - (jh && (jh->b_transaction != jinode->i_transaction || + (jh && (jh->b_transaction != trans || jh->b_next_transaction))) return true; } while ((bh = bh->b_this_page) != head); @@ -550,15 +551,20 @@ static bool ext4_journalled_writepage_needs_redirty(struct jbd2_inode *jinode, static int ext4_journalled_submit_inode_data_buffers(struct jbd2_inode *jinode) { struct address_space *mapping = jinode->i_vfs_inode->i_mapping; + loff_t range_start, range_end; struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, + .sync_mode = WB_SYNC_ALL, .nr_to_write = LONG_MAX, - .range_start = jinode->i_dirty_start, - .range_end = jinode->i_dirty_end, - }; + }; struct folio *folio = NULL; int error; + if (!jbd2_jinode_get_dirty_range(jinode, &range_start, &range_end)) + return 0; + + wbc.range_start = range_start; + wbc.range_end = range_end; + /* * writeback_iter() already checks for dirty pages and calls * folio_clear_dirty_for_io(), which we want to write protect the diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index 645240cc0229..b612262719ed 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -92,7 +92,7 @@ static const char *ext4_get_link(struct dentry *dentry, struct inode *inode, if (!dentry) { bh = ext4_getblk(NULL, inode, 0, EXT4_GET_BLOCKS_CACHED_NOWAIT); - if (IS_ERR(bh) || !bh) + if (IS_ERR_OR_NULL(bh)) return ERR_PTR(-ECHILD); if (!ext4_buffer_uptodate(bh)) { brelse(bh); diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 60aec4712f7f..982a1f831e22 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -226,7 +226,7 @@ check_xattrs(struct inode *inode, struct buffer_head *bh, /* Find the end of the names list */ while (!IS_LAST_ENTRY(e)) { struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(e); - if ((void *)next >= end) { + if ((void *)next + sizeof(u32) > end) { err_str = "e_name out of bounds"; goto errout; } @@ -1165,7 +1165,7 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent, { struct inode *ea_inode; struct ext4_xattr_entry *entry; - struct ext4_iloc iloc; + struct ext4_iloc iloc = { .bh = NULL }; bool dirty = false; unsigned int ea_ino; int err; @@ -1260,6 +1260,8 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent, ext4_warning_inode(parent, "handle dirty metadata err=%d", err); } + + brelse(iloc.bh); } /* diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 0143365c07dc..c00a6b6ebcbd 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -232,15 +232,6 @@ static inline void f2fs_unlock_all(struct f2fs_sb_info *sbi) static struct kmem_cache *ino_entry_slab; struct kmem_cache *f2fs_inode_entry_slab; -void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io, - unsigned char reason) -{ - f2fs_build_fault_attr(sbi, 0, 0, FAULT_ALL); - if (!end_io) - f2fs_flush_merged_writes(sbi); - f2fs_handle_critical_error(sbi, reason); -} - /* * We guarantee no failure on the returned page. */ diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 50fac72734ac..881e76158b96 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1491,10 +1491,10 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio) f2fs_compress_free_page(page); - dec_page_count(sbi, type); - - if (atomic_dec_return(&cic->pending_pages)) + if (atomic_dec_return(&cic->pending_pages)) { + dec_page_count(sbi, type); return; + } for (i = 0; i < cic->nr_rpages; i++) { WARN_ON(!cic->rpages[i]); @@ -1504,6 +1504,14 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio) page_array_free(sbi, cic->rpages, cic->nr_rpages); kmem_cache_free(cic_entry_slab, cic); + + /* + * Make sure dec_page_count() is the last access to sbi. + * Once it drops the F2FS_WB_CP_DATA counter to zero, the + * unmount thread can proceed to destroy sbi and + * sbi->page_array_slab. + */ + dec_page_count(sbi, type); } static int f2fs_write_raw_pages(struct compress_ctx *cc, diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index cf05014fa5e3..8d4f1e75dee3 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -173,7 +173,8 @@ static void f2fs_finish_read_bio(struct bio *bio, bool in_task) while (nr_pages--) dec_page_count(F2FS_F_SB(folio), __read_io_type(folio)); - if (F2FS_F_SB(folio)->node_inode && is_node_folio(folio) && + if (bio->bi_status == BLK_STS_OK && + F2FS_F_SB(folio)->node_inode && is_node_folio(folio) && f2fs_sanity_check_node_footer(F2FS_F_SB(folio), folio, folio->index, NODE_TYPE_REGULAR, true)) bio->bi_status = BLK_STS_IOERR; @@ -386,6 +387,8 @@ static void f2fs_write_end_io(struct bio *bio) folio->index, NODE_TYPE_REGULAR, true); f2fs_bug_on(sbi, folio->index != nid_of_node(folio)); } + if (f2fs_in_warm_node_list(folio)) + f2fs_del_fsync_node_entry(sbi, folio); dec_page_count(sbi, type); @@ -397,8 +400,6 @@ static void f2fs_write_end_io(struct bio *bio) wq_has_sleeper(&sbi->cp_wait)) wake_up(&sbi->cp_wait); - if (f2fs_in_warm_node_list(sbi, folio)) - f2fs_del_fsync_node_entry(sbi, folio); folio_clear_f2fs_gcing(folio); folio_end_writeback(folio); } @@ -1578,7 +1579,8 @@ static bool f2fs_map_blocks_cached(struct inode *inode, f2fs_wait_on_block_writeback_range(inode, map->m_pblk, map->m_len); - if (f2fs_allow_multi_device_dio(sbi, flag)) { + map->m_multidev_dio = f2fs_allow_multi_device_dio(sbi, flag); + if (map->m_multidev_dio) { int bidx = f2fs_target_device_index(sbi, map->m_pblk); struct f2fs_dev_info *dev = &sbi->devs[bidx]; @@ -1638,8 +1640,26 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag) lfs_dio_write = (flag == F2FS_GET_BLOCK_DIO && f2fs_lfs_mode(sbi) && map->m_may_create); - if (!map->m_may_create && f2fs_map_blocks_cached(inode, map, flag)) - goto out; + if (!map->m_may_create && f2fs_map_blocks_cached(inode, map, flag)) { + struct extent_info ei; + + /* + * 1. If map->m_multidev_dio is true, map->m_pblk cannot be + * waitted by f2fs_wait_on_block_writeback_range() and are not + * mergeable. + * 2. If pgofs hits the read extent cache, it means the mapping + * is already cached in the extent cache, but it is not + * mergeable, and there is no need to query the mapping again + * via f2fs_get_dnode_of_data(). + */ + pgofs = (pgoff_t)map->m_lblk + map->m_len; + if (map->m_len == maxblocks || + map->m_multidev_dio || + f2fs_lookup_read_extent_cache(inode, pgofs, &ei)) + goto out; + ofs = map->m_len; + goto map_more; + } map->m_bdev = inode->i_sb->s_bdev; map->m_multidev_dio = @@ -1650,7 +1670,8 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag) /* it only supports block size == page size */ pgofs = (pgoff_t)map->m_lblk; - end = pgofs + maxblocks; +map_more: + end = (pgoff_t)map->m_lblk + maxblocks; if (flag == F2FS_GET_BLOCK_PRECACHE) mode = LOOKUP_NODE_RA; @@ -2490,6 +2511,8 @@ next_folio: if (!folio) goto out; + f2fs_update_read_folio_count(F2FS_I_SB(inode), folio); + folio_in_bio = false; index = folio->index; offset = 0; @@ -2664,6 +2687,8 @@ static int f2fs_mpage_readpages(struct inode *inode, struct fsverity_info *vi, prefetchw(&folio->flags); } + f2fs_update_read_folio_count(F2FS_I_SB(inode), folio); + #ifdef CONFIG_F2FS_FS_COMPRESSION index = folio->index; @@ -2790,7 +2815,6 @@ int f2fs_encrypt_one_page(struct f2fs_io_info *fio) struct inode *inode = fio_inode(fio); struct folio *mfolio; struct page *page; - gfp_t gfp_flags = GFP_NOFS; if (!f2fs_encrypted_file(inode)) return 0; @@ -2800,19 +2824,10 @@ int f2fs_encrypt_one_page(struct f2fs_io_info *fio) if (fscrypt_inode_uses_inline_crypto(inode)) return 0; -retry_encrypt: fio->encrypted_page = fscrypt_encrypt_pagecache_blocks(page_folio(page), - PAGE_SIZE, 0, gfp_flags); - if (IS_ERR(fio->encrypted_page)) { - /* flush pending IOs and wait for a while in the ENOMEM case */ - if (PTR_ERR(fio->encrypted_page) == -ENOMEM) { - f2fs_flush_merged_writes(fio->sbi); - memalloc_retry_wait(GFP_NOFS); - gfp_flags |= __GFP_NOFAIL; - goto retry_encrypt; - } + PAGE_SIZE, 0, GFP_NOFS); + if (IS_ERR(fio->encrypted_page)) return PTR_ERR(fio->encrypted_page); - } mfolio = filemap_lock_folio(META_MAPPING(fio->sbi), fio->old_blkaddr); if (!IS_ERR(mfolio)) { diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 8e1040e375a7..af88db8fdb71 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -659,6 +659,7 @@ static int stat_show(struct seq_file *s, void *v) si->bg_node_blks); seq_printf(s, "BG skip : IO: %u, Other: %u\n", si->io_skip_bggc, si->other_skip_bggc); + seq_printf(s, "defrag blocks : %u\n", si->defrag_blks); seq_puts(s, "\nExtent Cache (Read):\n"); seq_printf(s, " - Hit Count: L1-1:%llu L1-2:%llu L2:%llu\n", si->hit_largest, si->hit_cached[EX_READ], diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index d73aeef333a2..d2e006420f04 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -119,9 +119,10 @@ static bool __may_extent_tree(struct inode *inode, enum extent_type type) if (!__init_may_extent_tree(inode, type)) return false; + if (is_inode_flag_set(inode, FI_NO_EXTENT)) + return false; + if (type == EX_READ) { - if (is_inode_flag_set(inode, FI_NO_EXTENT)) - return false; if (is_inode_flag_set(inode, FI_COMPRESSED_FILE) && !f2fs_sb_has_readonly(F2FS_I_SB(inode))) return false; @@ -644,6 +645,8 @@ static unsigned int __destroy_extent_node(struct inode *inode, while (atomic_read(&et->node_cnt)) { write_lock(&et->lock); + if (!is_inode_flag_set(inode, FI_NO_EXTENT)) + set_inode_flag(inode, FI_NO_EXTENT); node_cnt += __free_extent_tree(sbi, et, nr_shrink); write_unlock(&et->lock); } @@ -688,12 +691,12 @@ static void __update_extent_tree_range(struct inode *inode, write_lock(&et->lock); - if (type == EX_READ) { - if (is_inode_flag_set(inode, FI_NO_EXTENT)) { - write_unlock(&et->lock); - return; - } + if (is_inode_flag_set(inode, FI_NO_EXTENT)) { + write_unlock(&et->lock); + return; + } + if (type == EX_READ) { prev = et->largest; dei.len = 0; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 7d0a467982d6..91f506e7c9cf 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -2032,6 +2033,8 @@ struct f2fs_sb_info { unsigned long long iostat_count[NR_IO_TYPE]; unsigned long long iostat_bytes[NR_IO_TYPE]; unsigned long long prev_iostat_bytes[NR_IO_TYPE]; + unsigned long long iostat_read_folio_count[NR_PAGE_ORDERS]; + unsigned long long prev_iostat_read_folio_count[NR_PAGE_ORDERS]; bool iostat_enable; unsigned long iostat_next_period; unsigned int iostat_period_ms; @@ -2040,6 +2043,9 @@ struct f2fs_sb_info { spinlock_t iostat_lat_lock; struct iostat_lat_info *iostat_io_lat; #endif +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lock_class_key cp_global_sem_key; +#endif }; /* Definitions to access f2fs_sb_info */ @@ -3900,7 +3906,6 @@ int f2fs_do_quota_sync(struct super_block *sb, int type); loff_t max_file_blocks(struct inode *inode); void f2fs_quota_off_umount(struct super_block *sb); void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag); -void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, unsigned char reason); void f2fs_handle_error(struct f2fs_sb_info *sbi, unsigned char error); int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover); int f2fs_sync_fs(struct super_block *sb, int sync); @@ -3919,11 +3924,11 @@ enum node_type; int f2fs_check_nid_range(struct f2fs_sb_info *sbi, nid_t nid); bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type); -bool f2fs_in_warm_node_list(struct f2fs_sb_info *sbi, struct folio *folio); +bool f2fs_in_warm_node_list(struct folio *folio); void f2fs_init_fsync_node_info(struct f2fs_sb_info *sbi); void f2fs_del_fsync_node_entry(struct f2fs_sb_info *sbi, struct folio *folio); void f2fs_reset_fsync_node_info(struct f2fs_sb_info *sbi); -int f2fs_need_dentry_mark(struct f2fs_sb_info *sbi, nid_t nid); +bool f2fs_need_dentry_mark(struct f2fs_sb_info *sbi, nid_t nid); bool f2fs_is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid); bool f2fs_need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino); int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid, @@ -3945,6 +3950,8 @@ int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi, enum node_type ntype, bool in_irq); struct folio *f2fs_get_inode_folio(struct f2fs_sb_info *sbi, pgoff_t ino); struct folio *f2fs_get_xnode_folio(struct f2fs_sb_info *sbi, pgoff_t xnid); +int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode, + bool mark_dirty, enum iostat_type io_type); int f2fs_move_node_folio(struct folio *node_folio, int gc_type); void f2fs_flush_inline_data(struct f2fs_sb_info *sbi); int f2fs_fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, @@ -3987,7 +3994,7 @@ bool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr); int f2fs_start_discard_thread(struct f2fs_sb_info *sbi); void f2fs_drop_discard_cmd(struct f2fs_sb_info *sbi); void f2fs_stop_discard_thread(struct f2fs_sb_info *sbi); -bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi); +bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi, bool need_check); void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc); void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi); @@ -4286,6 +4293,7 @@ struct f2fs_stat_info { int gc_secs[2][2]; int tot_blks, data_blks, node_blks; int bg_data_blks, bg_node_blks; + unsigned int defrag_blks; int blkoff[NR_CURSEG_TYPE]; int curseg[NR_CURSEG_TYPE]; int cursec[NR_CURSEG_TYPE]; @@ -4420,6 +4428,9 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) si->bg_node_blks += ((gc_type) == BG_GC) ? (blks) : 0; \ } while (0) +#define stat_inc_defrag_blk_count(sbi, blks) \ + (F2FS_STAT(sbi)->defrag_blks += (blks)) + int f2fs_build_stats(struct f2fs_sb_info *sbi); void f2fs_destroy_stats(struct f2fs_sb_info *sbi); void __init f2fs_create_root_stats(void); @@ -4461,6 +4472,7 @@ void f2fs_update_sit_info(struct f2fs_sb_info *sbi); #define stat_inc_tot_blk_count(si, blks) do { } while (0) #define stat_inc_data_blk_count(sbi, blks, gc_type) do { } while (0) #define stat_inc_node_blk_count(sbi, blks, gc_type) do { } while (0) +#define stat_inc_defrag_blk_count(sbi, blks) do { } while (0) static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; } static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { } @@ -5063,8 +5075,25 @@ static inline void f2fs_handle_page_eio(struct f2fs_sb_info *sbi, return; if (ofs == sbi->page_eio_ofs[type]) { - if (sbi->page_eio_cnt[type]++ == MAX_RETRY_PAGE_EIO) - set_ckpt_flags(sbi, CP_ERROR_FLAG); + if (sbi->page_eio_cnt[type]++ == MAX_RETRY_PAGE_EIO) { + enum stop_cp_reason stop_reason; + + switch (type) { + case META: + stop_reason = STOP_CP_REASON_READ_META; + break; + case NODE: + stop_reason = STOP_CP_REASON_READ_NODE; + break; + case DATA: + stop_reason = STOP_CP_REASON_READ_DATA; + break; + default: + f2fs_bug_on(sbi, 1); + return; + } + f2fs_stop_checkpoint(sbi, false, stop_reason); + } } else { sbi->page_eio_ofs[type] = ofs; sbi->page_eio_cnt[type] = 0; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 42f5832242b3..fb12c5c9affd 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -81,8 +81,17 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf) int err = 0; vm_fault_t ret; - if (unlikely(IS_IMMUTABLE(inode))) + /* + * We only support large folio on the read case. + * Don't make any dirty pages. + */ + if (unlikely(IS_IMMUTABLE(inode)) || + mapping_large_folio_support(inode->i_mapping)) { + f2fs_err(sbi, "Not expected: immutable: %d large_folio: %d", + IS_IMMUTABLE(inode), + mapping_large_folio_support(inode->i_mapping)); return VM_FAULT_SIGBUS; + } if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) { err = -EIO; @@ -3042,8 +3051,10 @@ out: clear_inode_flag(inode, FI_OPU_WRITE); unlock_out: inode_unlock(inode); - if (!err) + if (!err) { range->len = (u64)total << PAGE_SHIFT; + stat_inc_defrag_blk_count(sbi, total); + } return err; } diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c0c8a1056d6b..ba93010924c0 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -316,10 +316,11 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type, p->max_search = sbi->max_victim_search; /* let's select beginning hot/small space first. */ - if (f2fs_need_rand_seg(sbi)) + if (f2fs_need_rand_seg(sbi)) { p->offset = get_random_u32_below(MAIN_SECS(sbi) * SEGS_PER_SEC(sbi)); - else if (type == CURSEG_HOT_DATA || IS_NODESEG(type)) + SIT_I(sbi)->last_victim[p->gc_mode] = p->offset; + } else if (type == CURSEG_HOT_DATA || IS_NODESEG(type)) p->offset = 0; else p->offset = SIT_I(sbi)->last_victim[p->gc_mode]; @@ -909,6 +910,9 @@ retry: if (!f2fs_segment_has_free_slot(sbi, segno)) goto next; } + + if (!get_valid_blocks(sbi, segno, true)) + goto next; } if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap)) @@ -1230,7 +1234,7 @@ static int ra_data_block(struct inode *inode, pgoff_t index) .encrypted_page = NULL, .in_list = 0, }; - int err; + int err = 0; folio = f2fs_grab_cache_folio(mapping, index, true); if (IS_ERR(folio)) @@ -1283,6 +1287,9 @@ got_it: fio.encrypted_page = &efolio->page; + if (folio_test_uptodate(efolio)) + goto put_encrypted_page; + err = f2fs_submit_page_bio(&fio); if (err) goto put_encrypted_page; @@ -1888,12 +1895,18 @@ freed: sbi->next_victim_seg[gc_type] = (cur_segno + 1 < sec_end_segno) ? cur_segno + 1 : NULL_SEGNO; + + if (unlikely(freezing(current))) { + folio_put_refs(sum_folio, 2); + goto stop; + } } next_block: folio_put_refs(sum_folio, 2); segno = block_end_segno; } +stop: if (submitted) f2fs_submit_merged_write(sbi, data_type); @@ -1967,6 +1980,10 @@ gc_more: goto stop; } retry: + if (unlikely(freezing(current))) { + ret = 0; + goto stop; + } ret = __get_victim(sbi, &segno, gc_type, gc_control->one_time); if (ret) { /* allow to search victim from sections has pinned data */ diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 2669439b9413..7aabfc9b43cb 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -792,7 +792,7 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, int f2fs_inline_data_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len) { - __u64 byteaddr, ilen; + __u64 byteaddr = 0, ilen; __u32 flags = FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_LAST; struct node_info ni; @@ -814,6 +814,15 @@ int f2fs_inline_data_fiemap(struct inode *inode, goto out; } + if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC) { + err = f2fs_write_single_node_folio(ifolio, true, false, FS_NODE_IO); + if (err) + return err; + ifolio = f2fs_get_inode_folio(F2FS_I_SB(inode), inode->i_ino); + if (IS_ERR(ifolio)) + return PTR_ERR(ifolio); + f2fs_folio_wait_writeback(ifolio, NODE, true, true); + } ilen = min_t(size_t, MAX_INLINE_DATA(inode), i_size_read(inode)); if (start >= ilen) goto out; @@ -825,9 +834,14 @@ int f2fs_inline_data_fiemap(struct inode *inode, if (err) goto out; - byteaddr = (__u64)ni.blk_addr << inode->i_sb->s_blocksize_bits; - byteaddr += (char *)inline_data_addr(inode, ifolio) - - (char *)F2FS_INODE(ifolio); + if (__is_valid_data_blkaddr(ni.blk_addr)) { + byteaddr = (__u64)ni.blk_addr << inode->i_sb->s_blocksize_bits; + byteaddr += (char *)inline_data_addr(inode, ifolio) - + (char *)F2FS_INODE(ifolio); + } else { + f2fs_bug_on(F2FS_I_SB(inode), ni.blk_addr != NEW_ADDR); + flags |= FIEMAP_EXTENT_DELALLOC | FIEMAP_EXTENT_UNKNOWN; + } err = fiemap_fill_next_extent(fieinfo, start, byteaddr, ilen, flags); trace_f2fs_fiemap(inode, start, byteaddr, ilen, flags, err); out: diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index f27198d6695b..c6dcda447882 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -687,7 +687,7 @@ void f2fs_update_inode(struct inode *inode, struct folio *node_folio) ri->i_uid = cpu_to_le32(i_uid_read(inode)); ri->i_gid = cpu_to_le32(i_gid_read(inode)); ri->i_links = cpu_to_le32(inode->i_nlink); - ri->i_blocks = cpu_to_le64(SECTOR_TO_BLOCK(inode->i_blocks) + 1); + ri->i_blocks = cpu_to_le64(SECTOR_TO_BLOCK(READ_ONCE(inode->i_blocks)) + 1); if (!f2fs_is_atomic_file(inode) || is_inode_flag_set(inode, FI_ATOMIC_COMMITTED)) diff --git a/fs/f2fs/iostat.c b/fs/f2fs/iostat.c index f8703038e1d8..ae265e3e9b2c 100644 --- a/fs/f2fs/iostat.c +++ b/fs/f2fs/iostat.c @@ -34,6 +34,7 @@ int __maybe_unused iostat_info_seq_show(struct seq_file *seq, void *offset) { struct super_block *sb = seq->private; struct f2fs_sb_info *sbi = F2FS_SB(sb); + int i; if (!sbi->iostat_enable) return 0; @@ -76,6 +77,12 @@ int __maybe_unused iostat_info_seq_show(struct seq_file *seq, void *offset) IOSTAT_INFO_SHOW("fs node", FS_NODE_READ_IO); IOSTAT_INFO_SHOW("fs meta", FS_META_READ_IO); + /* print read folio order stats */ + seq_printf(seq, "%-23s", "fs read folio order:"); + for (i = 0; i < NR_PAGE_ORDERS; i++) + seq_printf(seq, " %llu", sbi->iostat_read_folio_count[i]); + seq_putc(seq, '\n'); + /* print other IOs */ seq_puts(seq, "[OTHER]\n"); IOSTAT_INFO_SHOW("fs discard", FS_DISCARD_IO); @@ -113,6 +120,7 @@ static inline void __record_iostat_latency(struct f2fs_sb_info *sbi) static inline void f2fs_record_iostat(struct f2fs_sb_info *sbi) { unsigned long long iostat_diff[NR_IO_TYPE]; + unsigned long long read_folio_count_diff[NR_PAGE_ORDERS]; int i; unsigned long flags; @@ -133,9 +141,15 @@ static inline void f2fs_record_iostat(struct f2fs_sb_info *sbi) sbi->prev_iostat_bytes[i]; sbi->prev_iostat_bytes[i] = sbi->iostat_bytes[i]; } + + for (i = 0; i < NR_PAGE_ORDERS; i++) { + read_folio_count_diff[i] = sbi->iostat_read_folio_count[i] - + sbi->prev_iostat_read_folio_count[i]; + sbi->prev_iostat_read_folio_count[i] = sbi->iostat_read_folio_count[i]; + } spin_unlock_irqrestore(&sbi->iostat_lock, flags); - trace_f2fs_iostat(sbi, iostat_diff); + trace_f2fs_iostat(sbi, iostat_diff, read_folio_count_diff); __record_iostat_latency(sbi); } @@ -151,6 +165,10 @@ void f2fs_reset_iostat(struct f2fs_sb_info *sbi) sbi->iostat_bytes[i] = 0; sbi->prev_iostat_bytes[i] = 0; } + for (i = 0; i < NR_PAGE_ORDERS; i++) { + sbi->iostat_read_folio_count[i] = 0; + sbi->prev_iostat_read_folio_count[i] = 0; + } spin_unlock_irq(&sbi->iostat_lock); spin_lock_irq(&sbi->iostat_lat_lock); @@ -165,6 +183,24 @@ static inline void __f2fs_update_iostat(struct f2fs_sb_info *sbi, sbi->iostat_count[type]++; } +void f2fs_update_read_folio_count(struct f2fs_sb_info *sbi, struct folio *folio) +{ + unsigned int order = folio_order(folio); + unsigned long flags; + + if (!sbi->iostat_enable) + return; + + if (order >= NR_PAGE_ORDERS) + order = NR_PAGE_ORDERS - 1; + + spin_lock_irqsave(&sbi->iostat_lock, flags); + sbi->iostat_read_folio_count[order]++; + spin_unlock_irqrestore(&sbi->iostat_lock, flags); + + f2fs_record_iostat(sbi); +} + void f2fs_update_iostat(struct f2fs_sb_info *sbi, struct inode *inode, enum iostat_type type, unsigned long long io_bytes) { diff --git a/fs/f2fs/iostat.h b/fs/f2fs/iostat.h index eb99d05cf272..2025225b5bed 100644 --- a/fs/f2fs/iostat.h +++ b/fs/f2fs/iostat.h @@ -34,6 +34,8 @@ extern int __maybe_unused iostat_info_seq_show(struct seq_file *seq, extern void f2fs_reset_iostat(struct f2fs_sb_info *sbi); extern void f2fs_update_iostat(struct f2fs_sb_info *sbi, struct inode *inode, enum iostat_type type, unsigned long long io_bytes); +extern void f2fs_update_read_folio_count(struct f2fs_sb_info *sbi, + struct folio *folio); struct bio_iostat_ctx { struct f2fs_sb_info *sbi; @@ -68,6 +70,8 @@ extern void f2fs_destroy_iostat(struct f2fs_sb_info *sbi); #else static inline void f2fs_update_iostat(struct f2fs_sb_info *sbi, struct inode *inode, enum iostat_type type, unsigned long long io_bytes) {} +static inline void f2fs_update_read_folio_count(struct f2fs_sb_info *sbi, + struct folio *folio) {} static inline void iostat_update_and_unbind_ctx(struct bio *bio) {} static inline void iostat_alloc_and_bind_ctx(struct f2fs_sb_info *sbi, struct bio *bio, struct bio_post_read_ctx *ctx) {} diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index efbb0732d420..cac03b8e91a1 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -83,6 +83,21 @@ int f2fs_update_extension_list(struct f2fs_sb_info *sbi, const char *name, if (set) { if (total_count == F2FS_MAX_EXTENSION) return -EINVAL; + + if (hot) { + start = 0; + count = cold_count; + } else { + start = cold_count; + count = total_count; + } + for (i = start; i < count; i++) { + if (!strcmp(name, extlist[i])) { + f2fs_warn(sbi, "extension '%s' already exists in %s list", + name, hot ? "cold" : "hot"); + return -EINVAL; + } + } } else { if (!hot && !cold_count) return -EINVAL; @@ -964,6 +979,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir, return err; err = f2fs_create_whiteout(idmap, old_dir, &whiteout, &fname); + f2fs_free_filename(&fname); if (err) return err; } diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index e5b4a5b97b57..4e5bd9e4cfc3 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -325,7 +325,7 @@ static unsigned int __gang_lookup_nat_set(struct f2fs_nm_info *nm_i, start, nr); } -bool f2fs_in_warm_node_list(struct f2fs_sb_info *sbi, struct folio *folio) +bool f2fs_in_warm_node_list(struct folio *folio) { return is_node_folio(folio) && IS_DNODE(folio) && is_cold_node(folio); } @@ -391,7 +391,7 @@ void f2fs_reset_fsync_node_info(struct f2fs_sb_info *sbi) spin_unlock_irqrestore(&sbi->fsync_node_lock, flags); } -int f2fs_need_dentry_mark(struct f2fs_sb_info *sbi, nid_t nid) +bool f2fs_need_dentry_mark(struct f2fs_sb_info *sbi, nid_t nid) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct nat_entry *e; @@ -427,7 +427,9 @@ bool f2fs_need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino) struct f2fs_nm_info *nm_i = NM_I(sbi); struct nat_entry *e; bool need_update = true; + struct f2fs_lock_context lc; + f2fs_down_read_trace(&sbi->node_write, &lc); f2fs_down_read(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, ino, false); if (e && get_nat_flag(e, HAS_LAST_FSYNC) && @@ -435,6 +437,7 @@ bool f2fs_need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino) get_nat_flag(e, HAS_FSYNCED_INODE))) need_update = false; f2fs_up_read(&nm_i->nat_tree_lock); + f2fs_up_read_trace(&sbi->node_write, &lc); return need_update; } @@ -1113,7 +1116,7 @@ out_err: } static int truncate_partial_nodes(struct dnode_of_data *dn, - struct f2fs_inode *ri, int *offset, int depth) + int *offset, int depth) { struct folio *folios[2]; nid_t nid[3]; @@ -1184,7 +1187,6 @@ int f2fs_truncate_inode_blocks(struct inode *inode, pgoff_t from) int err = 0, cont = 1; int level, offset[4], noffset[4]; unsigned int nofs = 0; - struct f2fs_inode *ri; struct dnode_of_data dn; struct folio *folio; @@ -1212,7 +1214,6 @@ int f2fs_truncate_inode_blocks(struct inode *inode, pgoff_t from) set_new_dnode(&dn, inode, folio, NULL, 0); folio_unlock(folio); - ri = F2FS_INODE(folio); switch (level) { case 0: case 1: @@ -1222,7 +1223,7 @@ int f2fs_truncate_inode_blocks(struct inode *inode, pgoff_t from) nofs = noffset[1]; if (!offset[level - 1]) goto skip_partial; - err = truncate_partial_nodes(&dn, ri, offset, level); + err = truncate_partial_nodes(&dn, offset, level); if (err < 0 && err != -ENOENT) goto fail; nofs += 1 + NIDS_PER_BLOCK; @@ -1231,7 +1232,7 @@ int f2fs_truncate_inode_blocks(struct inode *inode, pgoff_t from) nofs = 5 + 2 * NIDS_PER_BLOCK; if (!offset[level - 1]) goto skip_partial; - err = truncate_partial_nodes(&dn, ri, offset, level); + err = truncate_partial_nodes(&dn, offset, level); if (err < 0 && err != -ENOENT) goto fail; break; @@ -1729,9 +1730,10 @@ continue_unlock: return last_folio; } -static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted, - struct writeback_control *wbc, bool do_balance, - enum iostat_type io_type, unsigned int *seq_id) +static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync, + bool *submitted, struct writeback_control *wbc, + bool do_balance, enum iostat_type io_type, + unsigned int *seq_id) { struct f2fs_sb_info *sbi = F2FS_F_SB(folio); nid_t nid; @@ -1776,7 +1778,7 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted if (f2fs_sanity_check_node_footer(sbi, folio, nid, NODE_TYPE_REGULAR, false)) { - f2fs_handle_critical_error(sbi, STOP_CP_REASON_CORRUPTED_NID); + f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_CORRUPTED_NID); goto redirty_out; } @@ -1801,16 +1803,17 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted goto redirty_out; } - if (atomic) { - if (!test_opt(sbi, NOBARRIER)) - fio.op_flags |= REQ_PREFLUSH | REQ_FUA; - if (IS_INODE(folio)) - set_dentry_mark(folio, + if (atomic && !test_opt(sbi, NOBARRIER)) + fio.op_flags |= REQ_PREFLUSH | REQ_FUA; + + set_dentry_mark(folio, false); + set_fsync_mark(folio, do_fsync); + if (IS_INODE(folio) && (atomic || is_fsync_dnode(folio))) + set_dentry_mark(folio, f2fs_need_dentry_mark(sbi, ino_of_node(folio))); - } /* should add to global list before clearing PAGECACHE status */ - if (f2fs_in_warm_node_list(sbi, folio)) { + if (f2fs_in_warm_node_list(folio)) { seq = f2fs_add_fsync_node_entry(sbi, folio); if (seq_id) *seq_id = seq; @@ -1843,41 +1846,51 @@ redirty_out: return false; } -int f2fs_move_node_folio(struct folio *node_folio, int gc_type) +int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode, + bool mark_dirty, enum iostat_type io_type) { int err = 0; + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = 1, + }; - if (gc_type == FG_GC) { - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = 1, - }; - - f2fs_folio_wait_writeback(node_folio, NODE, true, true); - - folio_mark_dirty(node_folio); - - if (!folio_clear_dirty_for_io(node_folio)) { - err = -EAGAIN; - goto out_page; - } - - if (!__write_node_folio(node_folio, false, NULL, - &wbc, false, FS_GC_NODE_IO, NULL)) - err = -EAGAIN; - goto release_page; - } else { + if (!sync_mode) { /* set page dirty and write it */ if (!folio_test_writeback(node_folio)) folio_mark_dirty(node_folio); + goto out_folio; } -out_page: + + f2fs_folio_wait_writeback(node_folio, NODE, true, true); + + if (mark_dirty) + folio_mark_dirty(node_folio); + else if (!folio_test_dirty(node_folio)) + goto out_folio; + + if (!folio_clear_dirty_for_io(node_folio)) { + err = -EAGAIN; + goto out_folio; + } + + if (!__write_node_folio(node_folio, false, false, NULL, + &wbc, false, FS_GC_NODE_IO, NULL)) + err = -EAGAIN; + goto release_folio; +out_folio: folio_unlock(node_folio); -release_page: +release_folio: f2fs_folio_put(node_folio, false); return err; } +int f2fs_move_node_folio(struct folio *node_folio, int gc_type) +{ + return f2fs_write_single_node_folio(node_folio, gc_type == FG_GC, + true, FS_GC_NODE_IO); +} + int f2fs_fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, struct writeback_control *wbc, bool atomic, unsigned int *seq_id) @@ -1908,6 +1921,7 @@ retry: for (i = 0; i < nr_folios; i++) { struct folio *folio = fbatch.folios[i]; bool submitted = false; + bool do_fsync = false; if (unlikely(f2fs_cp_error(sbi))) { f2fs_folio_put(last_folio, false); @@ -1938,19 +1952,13 @@ continue_unlock: f2fs_folio_wait_writeback(folio, NODE, true, true); - set_fsync_mark(folio, 0); - set_dentry_mark(folio, 0); - if (!atomic || folio == last_folio) { - set_fsync_mark(folio, 1); + do_fsync = true; percpu_counter_inc(&sbi->rf_node_block_count); if (IS_INODE(folio)) { if (is_inode_flag_set(inode, FI_DIRTY_INODE)) f2fs_update_inode(inode, folio); - if (!atomic) - set_dentry_mark(folio, - f2fs_need_dentry_mark(sbi, ino)); } /* may be written by other thread */ if (!folio_test_dirty(folio)) @@ -1962,8 +1970,9 @@ continue_unlock: if (!__write_node_folio(folio, atomic && folio == last_folio, - &submitted, wbc, true, - FS_NODE_IO, seq_id)) { + do_fsync, &submitted, + wbc, true, FS_NODE_IO, + seq_id)) { f2fs_folio_put(last_folio, false); folio_batch_release(&fbatch); ret = -EIO; @@ -2163,10 +2172,7 @@ write_node: if (!folio_clear_dirty_for_io(folio)) goto continue_unlock; - set_fsync_mark(folio, 0); - set_dentry_mark(folio, 0); - - if (!__write_node_folio(folio, false, &submitted, + if (!__write_node_folio(folio, false, false, &submitted, wbc, do_balance, io_type, NULL)) { folio_batch_release(&fbatch); ret = -EIO; diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 824ac9f0e6e4..bcf2034e4263 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -400,27 +400,26 @@ static inline int is_node(const struct folio *folio, int type) #define is_fsync_dnode(folio) is_node(folio, FSYNC_BIT_SHIFT) #define is_dent_dnode(folio) is_node(folio, DENT_BIT_SHIFT) -static inline void set_cold_node(const struct folio *folio, bool is_dir) +static inline void __set_mark(const struct folio *folio, bool mark, int type) { struct f2fs_node *rn = F2FS_NODE(folio); unsigned int flag = le32_to_cpu(rn->footer.flag); - if (is_dir) - flag &= ~BIT(COLD_BIT_SHIFT); - else - flag |= BIT(COLD_BIT_SHIFT); - rn->footer.flag = cpu_to_le32(flag); -} - -static inline void set_mark(struct folio *folio, int mark, int type) -{ - struct f2fs_node *rn = F2FS_NODE(folio); - unsigned int flag = le32_to_cpu(rn->footer.flag); if (mark) flag |= BIT(type); else flag &= ~BIT(type); rn->footer.flag = cpu_to_le32(flag); +} + +static inline void set_cold_node(const struct folio *folio, bool is_dir) +{ + __set_mark(folio, !is_dir, COLD_BIT_SHIFT); +} + +static inline void set_mark(struct folio *folio, bool mark, int type) +{ + __set_mark(folio, mark, type); #ifdef CONFIG_F2FS_CHECK_FS f2fs_inode_chksum_set(F2FS_F_SB(folio), folio); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 6a97fe76712b..788f8b050249 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1606,6 +1606,9 @@ static void __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi, if (dc->state != D_PREP) goto next; + if (*issued > 0 && unlikely(freezing(current))) + break; + if (dpolicy->io_aware && !is_idle(sbi, DISCARD_TIME)) { io_interrupted = true; break; @@ -1645,6 +1648,7 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi, struct blk_plug plug; int i, issued; bool io_interrupted = false; + bool suspended = false; if (dpolicy->timeout) f2fs_update_time(sbi, UMOUNT_DISCARD_TIMEOUT); @@ -1675,6 +1679,11 @@ retry: list_for_each_entry_safe(dc, tmp, pend_list, list) { f2fs_bug_on(sbi, dc->state != D_PREP); + if (issued > 0 && unlikely(freezing(current))) { + suspended = true; + break; + } + if (dpolicy->timeout && f2fs_time_over(sbi, UMOUNT_DISCARD_TIMEOUT)) break; @@ -1694,7 +1703,8 @@ retry: next: mutex_unlock(&dcc->cmd_lock); - if (issued >= dpolicy->max_requests || io_interrupted) + if (issued >= dpolicy->max_requests || io_interrupted || + suspended) break; } @@ -1880,7 +1890,7 @@ void f2fs_stop_discard_thread(struct f2fs_sb_info *sbi) * * Return true if issued all discard cmd or no discard cmd need issue, otherwise return false. */ -bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi) +bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi, bool need_check) { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; struct discard_policy dpolicy; @@ -1897,7 +1907,7 @@ bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi) /* just to make sure there is no pending discard commands */ __wait_all_discard_cmd(sbi, NULL); - f2fs_bug_on(sbi, atomic_read(&dcc->discard_cmd_cnt)); + f2fs_bug_on(sbi, need_check && atomic_read(&dcc->discard_cmd_cnt)); return !dropped; } @@ -2367,7 +2377,7 @@ static void destroy_discard_cmd_control(struct f2fs_sb_info *sbi) * Recovery can cache discard commands, so in error path of * fill_super(), it needs to give a chance to handle them. */ - f2fs_issue_discard_timeout(sbi); + f2fs_issue_discard_timeout(sbi, true); kfree(dcc); SM_I(sbi)->dcc_info = NULL; @@ -3980,7 +3990,7 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) if (fscrypt_inode_uses_fs_layer_crypto(folio->mapping->host)) fscrypt_finalize_bounce_page(&fio->encrypted_page); folio_end_writeback(folio); - if (f2fs_in_warm_node_list(fio->sbi, folio)) + if (f2fs_in_warm_node_list(folio)) f2fs_del_fsync_node_entry(fio->sbi, folio); f2fs_bug_on(fio->sbi, !is_set_ckpt_flags(fio->sbi, CP_ERROR_FLAG)); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 8774c60b4be4..ccf806b676f5 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -336,9 +336,12 @@ static const struct fs_parameter_spec f2fs_param_specs[] = { fsparam_flag("usrquota", Opt_usrquota), fsparam_flag("grpquota", Opt_grpquota), fsparam_flag("prjquota", Opt_prjquota), - fsparam_string_empty("usrjquota", Opt_usrjquota), - fsparam_string_empty("grpjquota", Opt_grpjquota), - fsparam_string_empty("prjjquota", Opt_prjjquota), + fsparam_string("usrjquota", Opt_usrjquota), + fsparam_flag("usrjquota", Opt_usrjquota), + fsparam_string("grpjquota", Opt_grpjquota), + fsparam_flag("grpjquota", Opt_grpjquota), + fsparam_string("prjjquota", Opt_prjjquota), + fsparam_flag("prjjquota", Opt_prjjquota), fsparam_flag("nat_bits", Opt_nat_bits), fsparam_enum("jqfmt", Opt_jqfmt, f2fs_param_jqfmt), fsparam_enum("alloc_mode", Opt_alloc, f2fs_param_alloc_mode), @@ -979,26 +982,26 @@ static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param) ctx_set_opt(ctx, F2FS_MOUNT_PRJQUOTA); break; case Opt_usrjquota: - if (!*param->string) - ret = f2fs_unnote_qf_name(fc, USRQUOTA); - else + if (param->type == fs_value_is_string && *param->string) ret = f2fs_note_qf_name(fc, USRQUOTA, param); + else + ret = f2fs_unnote_qf_name(fc, USRQUOTA); if (ret) return ret; break; case Opt_grpjquota: - if (!*param->string) - ret = f2fs_unnote_qf_name(fc, GRPQUOTA); - else + if (param->type == fs_value_is_string && *param->string) ret = f2fs_note_qf_name(fc, GRPQUOTA, param); + else + ret = f2fs_unnote_qf_name(fc, GRPQUOTA); if (ret) return ret; break; case Opt_prjjquota: - if (!*param->string) - ret = f2fs_unnote_qf_name(fc, PRJQUOTA); - else + if (param->type == fs_value_is_string && *param->string) ret = f2fs_note_qf_name(fc, PRJQUOTA, param); + else + ret = f2fs_unnote_qf_name(fc, PRJQUOTA); if (ret) return ret; break; @@ -1515,6 +1518,7 @@ static int f2fs_check_opt_consistency(struct fs_context *fc, F2FS_OPTION(sbi).root_reserved_blocks); ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_ROOT); ctx->opt_mask &= ~BIT(F2FS_MOUNT_RESERVE_ROOT); + ctx->spec_mask &= ~F2FS_SPEC_reserve_root; } if (test_opt(sbi, RESERVE_NODE) && (ctx->opt_mask & BIT(F2FS_MOUNT_RESERVE_NODE)) && @@ -1523,6 +1527,7 @@ static int f2fs_check_opt_consistency(struct fs_context *fc, F2FS_OPTION(sbi).root_reserved_nodes); ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_NODE); ctx->opt_mask &= ~BIT(F2FS_MOUNT_RESERVE_NODE); + ctx->spec_mask &= ~F2FS_SPEC_reserve_node; } err = f2fs_check_test_dummy_encryption(fc, sb); @@ -2009,7 +2014,7 @@ static void f2fs_put_super(struct super_block *sb) } /* be sure to wait for any on-going discard commands */ - done = f2fs_issue_discard_timeout(sbi); + done = f2fs_issue_discard_timeout(sbi, true); if (f2fs_realtime_discard_enable(sbi) && !sbi->discard_blks && done) { struct cp_control cpc = { .reason = CP_UMOUNT | CP_TRIMMED, @@ -2088,6 +2093,12 @@ static void f2fs_put_super(struct super_block *sb) #if IS_ENABLED(CONFIG_UNICODE) utf8_unload(sb->s_encoding); #endif + sync_blockdev(sb->s_bdev); + invalidate_bdev(sb->s_bdev); + for (i = 1; i < sbi->s_ndevs; i++) { + sync_blockdev(FDEV(i).bdev); + invalidate_bdev(FDEV(i).bdev); + } } int f2fs_sync_fs(struct super_block *sb, int sync) @@ -2152,7 +2163,7 @@ static int f2fs_unfreeze(struct super_block *sb) * will recover after removal of snapshot. */ if (test_opt(sbi, DISCARD) && !f2fs_hw_support_discard(sbi)) - f2fs_issue_discard_timeout(sbi); + f2fs_issue_discard_timeout(sbi, true); clear_sbi_flag(F2FS_SB(sb), SBI_IS_FREEZING); return 0; @@ -2957,7 +2968,12 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb) need_stop_discard = true; } else { f2fs_stop_discard_thread(sbi); - f2fs_issue_discard_timeout(sbi); + /* + * f2fs_ioc_fitrim() won't race w/ "remount ro" + * so it's safe to check discard_cmd_cnt in + * f2fs_issue_discard_timeout(). + */ + f2fs_issue_discard_timeout(sbi, flags & SB_RDONLY); need_restart_discard = true; } } @@ -4650,7 +4666,8 @@ static bool system_going_down(void) || system_state == SYSTEM_RESTART; } -void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, unsigned char reason) +static void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, + unsigned char reason) { struct super_block *sb = sbi->sb; bool shutdown = reason == STOP_CP_REASON_SHUTDOWN; @@ -4707,6 +4724,16 @@ void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, unsigned char reason) */ } +void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io, + unsigned char reason) +{ + f2fs_build_fault_attr(sbi, 0, 0, FAULT_ALL); + if (!end_io) + f2fs_flush_merged_writes(sbi); + f2fs_handle_critical_error(sbi, reason); +} + + static void f2fs_record_error_work(struct work_struct *work) { struct f2fs_sb_info *sbi = container_of(work, @@ -4948,6 +4975,11 @@ try_onemore: init_f2fs_rwsem_trace(&sbi->gc_lock, sbi, LOCK_NAME_GC_LOCK); mutex_init(&sbi->writepages); init_f2fs_rwsem_trace(&sbi->cp_global_sem, sbi, LOCK_NAME_CP_GLOBAL); +#ifdef CONFIG_DEBUG_LOCK_ALLOC + lockdep_register_key(&sbi->cp_global_sem_key); + lockdep_set_class(&sbi->cp_global_sem.internal_rwsem, + &sbi->cp_global_sem_key); +#endif init_f2fs_rwsem_trace(&sbi->node_write, sbi, LOCK_NAME_NODE_WRITE); init_f2fs_rwsem_trace(&sbi->node_change, sbi, LOCK_NAME_NODE_CHANGE); spin_lock_init(&sbi->stat_lock); @@ -5419,6 +5451,9 @@ free_options: free_sb_buf: kfree(raw_super); free_sbi: +#ifdef CONFIG_DEBUG_LOCK_ALLOC + lockdep_unregister_key(&sbi->cp_global_sem_key); +#endif kfree(sbi); sb->s_fs_info = NULL; @@ -5500,6 +5535,9 @@ static void kill_f2fs_super(struct super_block *sb) /* Release block devices last, after fscrypt_destroy_keyring(). */ if (sbi) { destroy_device_list(sbi); +#ifdef CONFIG_DEBUG_LOCK_ALLOC + lockdep_unregister_key(&sbi->cp_global_sem_key); +#endif kfree(sbi); sb->s_fs_info = NULL; } diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 5fbfdc96e502..352e96ad5c3a 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -338,6 +338,14 @@ static ssize_t avg_vblocks_show(struct f2fs_attr *a, f2fs_update_sit_info(sbi); return sysfs_emit(buf, "%llu\n", (unsigned long long)(si->avg_vblocks)); } + +static ssize_t defrag_blocks_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + struct f2fs_stat_info *si = F2FS_STAT(sbi); + + return sysfs_emit(buf, "%llu\n", (unsigned long long)(si->defrag_blks)); +} #endif static ssize_t main_blkaddr_show(struct f2fs_attr *a, @@ -379,10 +387,12 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a, if (!strcmp(a->attr.name, "extension_list")) { __u8 (*extlist)[F2FS_EXTENSION_LEN] = sbi->raw_super->extension_list; - int cold_count = le32_to_cpu(sbi->raw_super->extension_count); - int hot_count = sbi->raw_super->hot_ext_count; + int cold_count, hot_count; int len = 0, i; + f2fs_down_read(&sbi->sb_lock); + cold_count = le32_to_cpu(sbi->raw_super->extension_count); + hot_count = sbi->raw_super->hot_ext_count; len += sysfs_emit_at(buf, len, "cold file extension:\n"); for (i = 0; i < cold_count; i++) len += sysfs_emit_at(buf, len, "%s\n", extlist[i]); @@ -390,6 +400,7 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a, len += sysfs_emit_at(buf, len, "hot file extension:\n"); for (i = cold_count; i < cold_count + hot_count; i++) len += sysfs_emit_at(buf, len, "%s\n", extlist[i]); + f2fs_up_read(&sbi->sb_lock); return len; } @@ -1351,6 +1362,7 @@ F2FS_GENERAL_RO_ATTR(gc_mode); F2FS_GENERAL_RO_ATTR(moved_blocks_background); F2FS_GENERAL_RO_ATTR(moved_blocks_foreground); F2FS_GENERAL_RO_ATTR(avg_vblocks); +F2FS_GENERAL_RO_ATTR(defrag_blocks); #endif #ifdef CONFIG_FS_ENCRYPTION @@ -1473,6 +1485,7 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(moved_blocks_foreground), ATTR_LIST(moved_blocks_background), ATTR_LIST(avg_vblocks), + ATTR_LIST(defrag_blocks), #endif #ifdef CONFIG_BLK_DEV_ZONED ATTR_LIST(unusable_blocks_per_sec), @@ -1984,24 +1997,26 @@ int __init f2fs_init_sysfs(void) ret = kobject_init_and_add(&f2fs_feat, &f2fs_feat_ktype, NULL, "features"); if (ret) - goto put_kobject; + goto unregister_kset; ret = kobject_init_and_add(&f2fs_tune, &f2fs_tune_ktype, NULL, "tuning"); if (ret) - goto put_kobject; + goto put_feat; f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); if (!f2fs_proc_root) { ret = -ENOMEM; - goto put_kobject; + goto put_tune; } return 0; -put_kobject: +put_tune: kobject_put(&f2fs_tune); +put_feat: kobject_put(&f2fs_feat); +unregister_kset: kset_unregister(&f2fs_kset); return ret; } diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 3c75ee025bda..a65694cbfe68 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -280,15 +280,13 @@ void __inode_attach_wb(struct inode *inode, struct folio *folio) if (inode_cgwb_enabled(inode)) { struct cgroup_subsys_state *memcg_css; - if (folio) { - memcg_css = mem_cgroup_css_from_folio(folio); - wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC); - } else { - /* must pin memcg_css, see wb_get_create() */ + /* must pin memcg_css, see wb_get_create() */ + if (folio) + memcg_css = get_mem_cgroup_css_from_folio(folio); + else memcg_css = task_get_css(current, memory_cgrp_id); - wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC); - css_put(memcg_css); - } + wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC); + css_put(memcg_css); } if (!wb) @@ -570,28 +568,30 @@ void inode_switch_wbs_work_fn(struct work_struct *work) struct inode_switch_wbs_context *isw, *next_isw; struct llist_node *list; + list = llist_del_all(&new_wb->switch_wbs_ctxs); /* - * Grab out reference to wb so that it cannot get freed under us + * Nothing to do? That would be a problem as references held by isw + * items protect wb from freeing... + */ + if (WARN_ON_ONCE(!list)) + return; + + /* + * Grab our reference to wb so that it cannot get freed under us * after we process all the isw items. */ wb_get(new_wb); - while (1) { - list = llist_del_all(&new_wb->switch_wbs_ctxs); - /* Nothing to do? */ - if (!list) - break; - /* - * In addition to synchronizing among switchers, I_WB_SWITCH - * tells the RCU protected stat update paths to grab the i_page - * lock so that stat transfer can synchronize against them. - * Let's continue after I_WB_SWITCH is guaranteed to be - * visible. - */ - synchronize_rcu(); + /* + * In addition to synchronizing among switchers, I_WB_SWITCH + * tells the RCU protected stat update paths to grab the i_page + * lock so that stat transfer can synchronize against them. + * Let's continue after I_WB_SWITCH is guaranteed to be + * visible. + */ + synchronize_rcu(); - llist_for_each_entry_safe(isw, next_isw, list, list) - process_inode_switch_wbs(new_wb, isw); - } + llist_for_each_entry_safe(isw, next_isw, list, list) + process_inode_switch_wbs(new_wb, isw); wb_put(new_wb); } @@ -979,16 +979,16 @@ void wbc_account_cgroup_owner(struct writeback_control *wbc, struct folio *folio if (!wbc->wb || wbc->no_cgroup_owner) return; - css = mem_cgroup_css_from_folio(folio); + css = get_mem_cgroup_css_from_folio(folio); /* dead cgroups shouldn't contribute to inode ownership arbitration */ if (!css_is_online(css)) - return; + goto out; id = css->id; if (id == wbc->wb_id) { wbc->wb_bytes += bytes; - return; + goto out; } if (id == wbc->wb_lcand_id) @@ -1001,6 +1001,8 @@ void wbc_account_cgroup_owner(struct writeback_control *wbc, struct folio *folio wbc->wb_tcand_bytes += bytes; else wbc->wb_tcand_bytes -= min(bytes, wbc->wb_tcand_bytes); +out: + css_put(css); } EXPORT_SYMBOL_GPL(wbc_account_cgroup_owner); diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c index c88194e52d18..db5ae8ec1030 100644 --- a/fs/fuse/readdir.c +++ b/fs/fuse/readdir.c @@ -41,6 +41,10 @@ static void fuse_add_dirent_to_cache(struct file *file, unsigned int offset; void *addr; + /* Dirent doesn't fit in readdir cache page? Skip caching. */ + if (reclen > PAGE_SIZE) + return; + spin_lock(&fi->rdc.lock); /* * Is cache already completed? Or this entry does not go at the end of diff --git a/fs/inode.c b/fs/inode.c index 69e219f0cfcb..6a3cbc7dcd28 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -750,7 +750,7 @@ void dump_mapping(const struct address_space *mapping) return; } - dentry_ptr = container_of(dentry_first, struct dentry, d_u.d_alias); + dentry_ptr = container_of(dentry_first, struct dentry, d_alias); if (get_kernel_nofault(dentry, dentry_ptr) || !dentry.d_parent || !dentry.d_name.name) { pr_warn("aops:%ps ino:%llx invalid dentry:%px\n", diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index b0a6549b3848..b36ee619cdcd 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -355,7 +355,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, if (dio->flags & IOMAP_DIO_BOUNCE) ret = bio_iov_iter_bounce(bio, dio->submit.iter, - iomap_max_bio_size(&iter->iomap)); + iomap_max_bio_size(&iter->iomap), alignment); else ret = bio_iov_iter_get_pages(bio, dio->submit.iter, alignment - 1); diff --git a/fs/isofs/export.c b/fs/isofs/export.c index 421d247fae52..78f80c1a5c54 100644 --- a/fs/isofs/export.c +++ b/fs/isofs/export.c @@ -24,7 +24,7 @@ isofs_export_iget(struct super_block *sb, { struct inode *inode; - if (block == 0) + if (block == 0 || block >= ISOFS_SB(sb)->s_nzones) return ERR_PTR(-ESTALE); inode = isofs_iget(sb, block, offset); if (IS_ERR(inode)) diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index 8dd3911717e0..3ace3d6a55e7 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -10,20 +10,13 @@ #include #include "isofs.h" -/* - * ok, we cannot use strncmp, as the name is not in our data space. - * Thus we'll have to use isofs_match. No big problem. Match also makes - * some sanity tests. - */ static int isofs_cmp(struct dentry *dentry, const char *compare, int dlen) { - struct qstr qstr; - qstr.name = compare; - qstr.len = dlen; if (likely(!dentry->d_op)) return dentry->d_name.len != dlen || memcmp(dentry->d_name.name, compare, dlen); - return dentry->d_op->d_compare(NULL, dentry->d_name.len, dentry->d_name.name, &qstr); + return dentry->d_op->d_compare(NULL, dentry->d_name.len, dentry->d_name.name, + &QSTR_LEN(compare, dlen)); } /* diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 6fe6dbd0c740..1232fab59a4e 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -101,6 +101,15 @@ static int rock_continue(struct rock_state *rs) goto out; } + if ((unsigned)rs->cont_extent >= ISOFS_SB(rs->inode->i_sb)->s_nzones) { + printk(KERN_NOTICE "rock: corrupted directory entry. " + "extent=%u out of volume (nzones=%lu)\n", + (unsigned)rs->cont_extent, + ISOFS_SB(rs->inode->i_sb)->s_nzones); + ret = -EIO; + goto out; + } + if (rs->cont_extent) { struct buffer_head *bh; diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 7203d2d2624d..8cf61e7185c4 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -180,7 +180,13 @@ static int journal_wait_on_commit_record(journal_t *journal, /* Send all the data buffers related to an inode */ int jbd2_submit_inode_data(journal_t *journal, struct jbd2_inode *jinode) { - if (!jinode || !(jinode->i_flags & JI_WRITE_DATA)) + unsigned long flags; + + if (!jinode) + return 0; + + flags = READ_ONCE(jinode->i_flags); + if (!(flags & JI_WRITE_DATA)) return 0; trace_jbd2_submit_inode_data(jinode->i_vfs_inode); @@ -191,12 +197,30 @@ EXPORT_SYMBOL(jbd2_submit_inode_data); int jbd2_wait_inode_data(journal_t *journal, struct jbd2_inode *jinode) { - if (!jinode || !(jinode->i_flags & JI_WAIT_DATA) || - !jinode->i_vfs_inode || !jinode->i_vfs_inode->i_mapping) + struct address_space *mapping; + struct inode *inode; + unsigned long flags; + loff_t start_byte, end_byte; + + if (!jinode) + return 0; + + flags = READ_ONCE(jinode->i_flags); + if (!(flags & JI_WAIT_DATA)) + return 0; + + inode = jinode->i_vfs_inode; + if (!inode) + return 0; + + mapping = inode->i_mapping; + if (!mapping) + return 0; + + if (!jbd2_jinode_get_dirty_range(jinode, &start_byte, &end_byte)) return 0; return filemap_fdatawait_range_keep_errors( - jinode->i_vfs_inode->i_mapping, jinode->i_dirty_start, - jinode->i_dirty_end); + mapping, start_byte, end_byte); } EXPORT_SYMBOL(jbd2_wait_inode_data); @@ -218,7 +242,8 @@ static int journal_submit_data_buffers(journal_t *journal, list_for_each_entry(jinode, &commit_transaction->t_inode_list, i_list) { if (!(jinode->i_flags & JI_WRITE_DATA)) continue; - jinode->i_flags |= JI_COMMIT_RUNNING; + WRITE_ONCE(jinode->i_flags, + jinode->i_flags | JI_COMMIT_RUNNING); spin_unlock(&journal->j_list_lock); /* submit the inode data buffers. */ trace_jbd2_submit_inode_data(jinode->i_vfs_inode); @@ -229,7 +254,8 @@ static int journal_submit_data_buffers(journal_t *journal, } spin_lock(&journal->j_list_lock); J_ASSERT(jinode->i_transaction == commit_transaction); - jinode->i_flags &= ~JI_COMMIT_RUNNING; + WRITE_ONCE(jinode->i_flags, + jinode->i_flags & ~JI_COMMIT_RUNNING); smp_mb(); wake_up_bit(&jinode->i_flags, __JI_COMMIT_RUNNING); } @@ -240,10 +266,13 @@ static int journal_submit_data_buffers(journal_t *journal, int jbd2_journal_finish_inode_data_buffers(struct jbd2_inode *jinode) { struct address_space *mapping = jinode->i_vfs_inode->i_mapping; + loff_t start_byte, end_byte; + + if (!jbd2_jinode_get_dirty_range(jinode, &start_byte, &end_byte)) + return 0; return filemap_fdatawait_range_keep_errors(mapping, - jinode->i_dirty_start, - jinode->i_dirty_end); + start_byte, end_byte); } /* @@ -262,7 +291,7 @@ static int journal_finish_inode_data_buffers(journal_t *journal, list_for_each_entry(jinode, &commit_transaction->t_inode_list, i_list) { if (!(jinode->i_flags & JI_WAIT_DATA)) continue; - jinode->i_flags |= JI_COMMIT_RUNNING; + WRITE_ONCE(jinode->i_flags, jinode->i_flags | JI_COMMIT_RUNNING); spin_unlock(&journal->j_list_lock); /* wait for the inode data buffers writeout. */ if (journal->j_finish_inode_data_buffers) { @@ -272,7 +301,7 @@ static int journal_finish_inode_data_buffers(journal_t *journal, } cond_resched(); spin_lock(&journal->j_list_lock); - jinode->i_flags &= ~JI_COMMIT_RUNNING; + WRITE_ONCE(jinode->i_flags, jinode->i_flags & ~JI_COMMIT_RUNNING); smp_mb(); wake_up_bit(&jinode->i_flags, __JI_COMMIT_RUNNING); } @@ -288,8 +317,8 @@ static int journal_finish_inode_data_buffers(journal_t *journal, &jinode->i_transaction->t_inode_list); } else { jinode->i_transaction = NULL; - jinode->i_dirty_start = 0; - jinode->i_dirty_end = 0; + WRITE_ONCE(jinode->i_dirty_start_page, 0); + WRITE_ONCE(jinode->i_dirty_end_page, 0); } } spin_unlock(&journal->j_list_lock); diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index b60918ed8a99..4f397fcdb13c 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -3018,8 +3018,8 @@ void jbd2_journal_init_jbd_inode(struct jbd2_inode *jinode, struct inode *inode) jinode->i_next_transaction = NULL; jinode->i_vfs_inode = inode; jinode->i_flags = 0; - jinode->i_dirty_start = 0; - jinode->i_dirty_end = 0; + jinode->i_dirty_start_page = 0; + jinode->i_dirty_end_page = 0; INIT_LIST_HEAD(&jinode->i_list); } @@ -3176,4 +3176,3 @@ MODULE_DESCRIPTION("Generic filesystem journal-writing module"); MODULE_LICENSE("GPL"); module_init(journal_init); module_exit(journal_exit); - diff --git a/fs/jbd2/revoke.c b/fs/jbd2/revoke.c index 9016ddb82447..e4c2fbd381f1 100644 --- a/fs/jbd2/revoke.c +++ b/fs/jbd2/revoke.c @@ -428,6 +428,7 @@ void jbd2_journal_cancel_revoke(handle_t *handle, struct journal_head *jh) journal_t *journal = handle->h_transaction->t_journal; int need_cancel; struct buffer_head *bh = jh2bh(jh); + struct address_space *bh_mapping = bh->b_folio->mapping; jbd2_debug(4, "journal_head %p, cancelling revoke\n", jh); @@ -464,13 +465,14 @@ void jbd2_journal_cancel_revoke(handle_t *handle, struct journal_head *jh) * buffer_head? If so, we'd better make sure we clear the * revoked status on any hashed alias too, otherwise the revoke * state machine will get very upset later on. */ - if (need_cancel) { + if (need_cancel && !sb_is_blkdev_sb(bh_mapping->host->i_sb)) { struct buffer_head *bh2; + bh2 = __find_get_block_nonatomic(bh->b_bdev, bh->b_blocknr, bh->b_size); if (bh2) { - if (bh2 != bh) - clear_buffer_revoked(bh2); + WARN_ON_ONCE(bh2 == bh); + clear_buffer_revoked(bh2); __brelse(bh2); } } diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index a90f9092706c..4885903bbd10 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -474,7 +474,8 @@ handle_t *jbd2__journal_start(journal_t *journal, int nblocks, int rsv_blocks, return ERR_PTR(-EROFS); if (handle) { - J_ASSERT(handle->h_transaction->t_journal == journal); + if (WARN_ON_ONCE(handle->h_transaction->t_journal != journal)) + return ERR_PTR(-EINVAL); handle->h_ref++; return handle; } @@ -1036,7 +1037,13 @@ repeat: */ if (!jh->b_transaction) { JBUFFER_TRACE(jh, "no transaction"); - J_ASSERT_JH(jh, !jh->b_next_transaction); + if (WARN_ON_ONCE(jh->b_next_transaction)) { + spin_unlock(&jh->b_state_lock); + unlock_buffer(bh); + error = -EINVAL; + jbd2_journal_abort(journal, error); + goto out; + } JBUFFER_TRACE(jh, "file as BJ_Reserved"); /* * Make sure all stores to jh (b_modified, b_frozen_data) are @@ -1069,13 +1076,27 @@ repeat: */ if (jh->b_frozen_data) { JBUFFER_TRACE(jh, "has frozen data"); - J_ASSERT_JH(jh, jh->b_next_transaction == NULL); + if (WARN_ON_ONCE(jh->b_next_transaction)) { + spin_unlock(&jh->b_state_lock); + error = -EINVAL; + jbd2_journal_abort(journal, error); + goto out; + } goto attach_next; } JBUFFER_TRACE(jh, "owned by older transaction"); - J_ASSERT_JH(jh, jh->b_next_transaction == NULL); - J_ASSERT_JH(jh, jh->b_transaction == journal->j_committing_transaction); + if (WARN_ON_ONCE(jh->b_next_transaction || + jh->b_transaction != + journal->j_committing_transaction)) { + pr_err("JBD2: %s: assertion failure: b_next_transaction=%p b_transaction=%p j_committing_transaction=%p\n", + journal->j_devname, jh->b_next_transaction, + jh->b_transaction, journal->j_committing_transaction); + spin_unlock(&jh->b_state_lock); + error = -EINVAL; + jbd2_journal_abort(journal, error); + goto out; + } /* * There is one case we have to be very careful about. If the @@ -1302,7 +1323,12 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh) goto out; } - J_ASSERT_JH(jh, buffer_locked(jh2bh(jh))); + if (WARN_ON_ONCE(!buffer_locked(jh2bh(jh)))) { + err = -EINVAL; + spin_unlock(&jh->b_state_lock); + jbd2_journal_abort(journal, err); + goto out; + } if (jh->b_transaction == NULL) { /* @@ -1491,7 +1517,7 @@ void jbd2_buffer_abort_trigger(struct journal_head *jh, int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) { transaction_t *transaction = handle->h_transaction; - journal_t *journal; + journal_t *journal = transaction->t_journal; struct journal_head *jh; int ret = 0; @@ -1515,8 +1541,14 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) if (data_race(jh->b_transaction != transaction && jh->b_next_transaction != transaction)) { spin_lock(&jh->b_state_lock); - J_ASSERT_JH(jh, jh->b_transaction == transaction || - jh->b_next_transaction == transaction); + if (WARN_ON_ONCE(jh->b_transaction != transaction && + jh->b_next_transaction != transaction)) { + pr_err("JBD2: %s: assertion failure: b_transaction=%p transaction=%p b_next_transaction=%p\n", + journal->j_devname, jh->b_transaction, + transaction, jh->b_next_transaction); + ret = -EINVAL; + goto out_unlock_bh; + } spin_unlock(&jh->b_state_lock); } if (data_race(jh->b_modified == 1)) { @@ -1524,15 +1556,15 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) if (data_race(jh->b_transaction == transaction && jh->b_jlist != BJ_Metadata)) { spin_lock(&jh->b_state_lock); - if (jh->b_transaction == transaction && - jh->b_jlist != BJ_Metadata) - pr_err("JBD2: assertion failure: h_type=%u " - "h_line_no=%u block_no=%llu jlist=%u\n", + if (WARN_ON_ONCE(jh->b_transaction == transaction && + jh->b_jlist != BJ_Metadata)) { + pr_err("JBD2: assertion failure: h_type=%u h_line_no=%u block_no=%llu jlist=%u\n", handle->h_type, handle->h_line_no, (unsigned long long) bh->b_blocknr, jh->b_jlist); - J_ASSERT_JH(jh, jh->b_transaction != transaction || - jh->b_jlist == BJ_Metadata); + ret = -EINVAL; + goto out_unlock_bh; + } spin_unlock(&jh->b_state_lock); } goto out; @@ -1552,8 +1584,6 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) goto out_unlock_bh; } - journal = transaction->t_journal; - if (jh->b_modified == 0) { /* * This buffer's got modified and becoming part @@ -1631,7 +1661,10 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) } /* That test should have eliminated the following case: */ - J_ASSERT_JH(jh, jh->b_frozen_data == NULL); + if (WARN_ON_ONCE(jh->b_frozen_data)) { + ret = -EINVAL; + goto out_unlock_bh; + } JBUFFER_TRACE(jh, "file as BJ_Metadata"); spin_lock(&journal->j_list_lock); @@ -1670,6 +1703,7 @@ int jbd2_journal_forget(handle_t *handle, struct buffer_head *bh) int err = 0; int was_modified = 0; int wait_for_writeback = 0; + int abort_journal = 0; if (is_handle_aborted(handle)) return -EROFS; @@ -1703,7 +1737,11 @@ int jbd2_journal_forget(handle_t *handle, struct buffer_head *bh) jh->b_modified = 0; if (jh->b_transaction == transaction) { - J_ASSERT_JH(jh, !jh->b_frozen_data); + if (WARN_ON_ONCE(jh->b_frozen_data)) { + err = -EINVAL; + abort_journal = 1; + goto drop; + } /* If we are forgetting a buffer which is already part * of this transaction, then we can just drop it from @@ -1742,8 +1780,11 @@ int jbd2_journal_forget(handle_t *handle, struct buffer_head *bh) } spin_unlock(&journal->j_list_lock); } else if (jh->b_transaction) { - J_ASSERT_JH(jh, (jh->b_transaction == - journal->j_committing_transaction)); + if (WARN_ON_ONCE(jh->b_transaction != journal->j_committing_transaction)) { + err = -EINVAL; + abort_journal = 1; + goto drop; + } /* However, if the buffer is still owned by a prior * (committing) transaction, we can't drop it yet... */ JBUFFER_TRACE(jh, "belongs to older transaction"); @@ -1761,7 +1802,11 @@ int jbd2_journal_forget(handle_t *handle, struct buffer_head *bh) jh->b_next_transaction = transaction; spin_unlock(&journal->j_list_lock); } else { - J_ASSERT(jh->b_next_transaction == transaction); + if (WARN_ON_ONCE(jh->b_next_transaction != transaction)) { + err = -EINVAL; + abort_journal = 1; + goto drop; + } /* * only drop a reference if this transaction modified @@ -1807,6 +1852,8 @@ int jbd2_journal_forget(handle_t *handle, struct buffer_head *bh) drop: __brelse(bh); spin_unlock(&jh->b_state_lock); + if (abort_journal) + jbd2_journal_abort(journal, err); if (wait_for_writeback) wait_on_buffer(bh); jbd2_journal_put_journal_head(jh); @@ -2131,7 +2178,8 @@ bool jbd2_journal_try_to_free_buffers(journal_t *journal, struct folio *folio) struct buffer_head *bh; bool ret = false; - J_ASSERT(folio_test_locked(folio)); + if (WARN_ON_ONCE(!folio_test_locked(folio))) + return false; head = folio_buffers(folio); bh = head; @@ -2646,6 +2694,9 @@ static int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode, { transaction_t *transaction = handle->h_transaction; journal_t *journal; + pgoff_t start_page, end_page; + int err = 0; + int abort_transaction = 0; if (is_handle_aborted(handle)) return -EROFS; @@ -2654,15 +2705,21 @@ static int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode, jbd2_debug(4, "Adding inode %llu, tid:%d\n", jinode->i_vfs_inode->i_ino, transaction->t_tid); - spin_lock(&journal->j_list_lock); - jinode->i_flags |= flags; + start_page = (pgoff_t)(start_byte >> PAGE_SHIFT); + end_page = (pgoff_t)(end_byte >> PAGE_SHIFT) + 1; - if (jinode->i_dirty_end) { - jinode->i_dirty_start = min(jinode->i_dirty_start, start_byte); - jinode->i_dirty_end = max(jinode->i_dirty_end, end_byte); + spin_lock(&journal->j_list_lock); + WRITE_ONCE(jinode->i_flags, jinode->i_flags | flags); + + if (jinode->i_dirty_start_page != jinode->i_dirty_end_page) { + WRITE_ONCE(jinode->i_dirty_start_page, + min(jinode->i_dirty_start_page, start_page)); + WRITE_ONCE(jinode->i_dirty_end_page, + max(jinode->i_dirty_end_page, end_page)); } else { - jinode->i_dirty_start = start_byte; - jinode->i_dirty_end = end_byte; + /* Publish a new non-empty range by making end visible first. */ + WRITE_ONCE(jinode->i_dirty_end_page, end_page); + WRITE_ONCE(jinode->i_dirty_start_page, start_page); } /* Is inode already attached where we need it? */ @@ -2680,20 +2737,33 @@ static int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode, /* On some different transaction's list - should be * the committing one */ if (jinode->i_transaction) { - J_ASSERT(jinode->i_next_transaction == NULL); - J_ASSERT(jinode->i_transaction == - journal->j_committing_transaction); + if (WARN_ON_ONCE(jinode->i_next_transaction || + jinode->i_transaction != + journal->j_committing_transaction)) { + pr_err("JBD2: %s: assertion failure: i_next_transaction=%p i_transaction=%p j_committing_transaction=%p\n", + journal->j_devname, jinode->i_next_transaction, + jinode->i_transaction, + journal->j_committing_transaction); + err = -EINVAL; + abort_transaction = 1; + goto done; + } jinode->i_next_transaction = transaction; goto done; } /* Not on any transaction list... */ - J_ASSERT(!jinode->i_next_transaction); + if (WARN_ON_ONCE(jinode->i_next_transaction)) { + err = -EINVAL; + abort_transaction = 1; + goto done; + } jinode->i_transaction = transaction; list_add(&jinode->i_list, &transaction->t_inode_list); done: spin_unlock(&journal->j_list_lock); - - return 0; + if (abort_transaction) + jbd2_journal_abort(journal, err); + return err; } int jbd2_journal_inode_ranged_write(handle_t *handle, @@ -2739,7 +2809,7 @@ int jbd2_journal_begin_ordered_truncate(journal_t *journal, int ret = 0; /* This is a quick check to avoid locking if not necessary */ - if (!jinode->i_transaction) + if (!READ_ONCE(jinode->i_transaction)) goto out; /* Locks are here just to force reading of recent values, it is * enough that the transaction was not committing before we started diff --git a/fs/lockd/Makefile b/fs/lockd/Makefile index 51bbe22d21e3..808f0f2a7be1 100644 --- a/fs/lockd/Makefile +++ b/fs/lockd/Makefile @@ -9,5 +9,33 @@ obj-$(CONFIG_LOCKD) += lockd.o lockd-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \ svcshare.o svcproc.o svcsubs.o mon.o trace.o xdr.o netlink.o -lockd-$(CONFIG_LOCKD_V4) += clnt4xdr.o xdr4.o svc4proc.o +lockd-$(CONFIG_LOCKD_V4) += clnt4xdr.o svc4proc.o nlm4xdr_gen.o lockd-$(CONFIG_PROC_FS) += procfs.o + +# +# XDR code generation (requires Python and additional packages) +# +# The generated *xdr_gen.{h,c} files are checked into git. Normal kernel +# builds do not require the xdrgen tool or its Python dependencies. +# +# Developers modifying .x files in Documentation/sunrpc/xdr/ should run +# "make xdrgen" to regenerate the affected files. +# +.PHONY: xdrgen + +XDRGEN = ../../tools/net/sunrpc/xdrgen/xdrgen + +XDRGEN_DEFINITIONS = ../../include/linux/sunrpc/xdrgen/nlm4.h +XDRGEN_DECLARATIONS = nlm4xdr_gen.h +XDRGEN_SOURCE = nlm4xdr_gen.c + +xdrgen: $(XDRGEN_DEFINITIONS) $(XDRGEN_DECLARATIONS) $(XDRGEN_SOURCE) + +../../include/linux/sunrpc/xdrgen/nlm4.h: ../../Documentation/sunrpc/xdr/nlm4.x + $(XDRGEN) definitions $< > $@ + +nlm4xdr_gen.h: ../../Documentation/sunrpc/xdr/nlm4.x + $(XDRGEN) declarations $< > $@ + +nlm4xdr_gen.c: ../../Documentation/sunrpc/xdr/nlm4.x + $(XDRGEN) source --peer server $< > $@ diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c index 527458db4525..2058733eacf8 100644 --- a/fs/lockd/clnt4xdr.c +++ b/fs/lockd/clnt4xdr.c @@ -13,7 +13,8 @@ #include #include #include -#include + +#include "lockd.h" #include @@ -284,7 +285,7 @@ static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result) fl->c.flc_type = exclusive != 0 ? F_WRLCK : F_RDLCK; p = xdr_decode_hyper(p, &l_offset); xdr_decode_hyper(p, &l_len); - nlm4svc_set_file_lock_range(fl, l_offset, l_len); + lockd_set_file_lock_range4(fl, l_offset, l_len); error = 0; out: return error; diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 85bc0f3e91df..8fa30c42c92a 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -15,9 +15,9 @@ #include #include #include -#include #include +#include "lockd.h" #include "trace.h" #define NLMDBG_FACILITY NLMDBG_CLIENT diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index fb4d0752c9bb..7f211008a5d2 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -18,8 +18,8 @@ #include #include #include -#include +#include "lockd.h" #include "trace.h" #define NLMDBG_FACILITY NLMDBG_CLIENT diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c index 6ea3448d2d31..65555f5224b1 100644 --- a/fs/lockd/clntxdr.c +++ b/fs/lockd/clntxdr.c @@ -15,7 +15,8 @@ #include #include #include -#include + +#include "lockd.h" #include diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 1a9582a10a86..ea8a8e166f7e 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -16,13 +16,13 @@ #include #include #include -#include #include #include #include +#include "lockd.h" #include "netns.h" #define NLMDBG_FACILITY NLMDBG_HOSTCACHE @@ -306,6 +306,35 @@ void nlmclnt_release_host(struct nlm_host *host) } } +/* Callback for rpc_cancel_tasks() - matches all tasks for cancellation */ +static bool nlmclnt_match_all(const struct rpc_task *task, const void *data) +{ + return true; +} + +/** + * nlmclnt_shutdown_rpc_clnt - safely shut down NLM client RPC operations + * @host: nlm_host to shut down + * + * Cancels outstanding RPC tasks and marks the client as shut down. + * Synchronizes with nlmclnt_release_host() via nlm_host_mutex to prevent + * races between shutdown and host destruction. Safe to call if h_rpcclnt + * is NULL or already shut down. + */ +void nlmclnt_shutdown_rpc_clnt(struct nlm_host *host) +{ + struct rpc_clnt *clnt; + + mutex_lock(&nlm_host_mutex); + clnt = host->h_rpcclnt; + if (clnt) { + clnt->cl_shutdown = 1; + rpc_cancel_tasks(clnt, -EIO, nlmclnt_match_all, NULL); + } + mutex_unlock(&nlm_host_mutex); +} +EXPORT_SYMBOL_GPL(nlmclnt_shutdown_rpc_clnt); + /** * nlmsvc_lookup_host - Find an NLM host handle matching a remote client * @rqstp: incoming NLM request diff --git a/include/linux/lockd/lockd.h b/fs/lockd/lockd.h similarity index 83% rename from include/linux/lockd/lockd.h rename to fs/lockd/lockd.h index 330e38776bb2..a7c85ab6d4b5 100644 --- a/include/linux/lockd/lockd.h +++ b/fs/lockd/lockd.h @@ -1,16 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * linux/include/linux/lockd/lockd.h - * - * General-purpose lockd include file. - * * Copyright (C) 1996 Olaf Kirch */ -#ifndef LINUX_LOCKD_LOCKD_H -#define LINUX_LOCKD_LOCKD_H - -/* XXX: a lot of this should really be under fs/lockd. */ +#ifndef _LOCKD_LOCKD_H +#define _LOCKD_LOCKD_H #include #include @@ -20,14 +14,34 @@ #include #include #include +#include "nlm.h" #include -#include -#ifdef CONFIG_LOCKD_V4 -#include -#endif -#include +#include "xdr.h" +#include #include +/* + * Enable lockd debugging. + * Requires CONFIG_SUNRPC_DEBUG. + */ +#undef ifdebug +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) +# define ifdebug(flag) if (unlikely(nlm_debug & NLMDBG_##flag)) +#else +# define ifdebug(flag) if (0) +#endif + +#define NLMDBG_SVC 0x0001 +#define NLMDBG_CLIENT 0x0002 +#define NLMDBG_CLNTLOCK 0x0004 +#define NLMDBG_SVCLOCK 0x0008 +#define NLMDBG_MONITOR 0x0010 +#define NLMDBG_CLNTSUBS 0x0020 +#define NLMDBG_SVCSUBS 0x0040 +#define NLMDBG_HOSTCACHE 0x0080 +#define NLMDBG_XDR 0x0100 +#define NLMDBG_ALL 0x7fff + /* * Version string */ @@ -38,6 +52,22 @@ */ #define LOCKD_DFLT_TIMEO 10 +/* error codes new to NLMv4 */ +#define nlm4_deadlock cpu_to_be32(NLM_DEADLCK) +#define nlm4_rofs cpu_to_be32(NLM_ROFS) +#define nlm4_stale_fh cpu_to_be32(NLM_STALE_FH) +#define nlm4_fbig cpu_to_be32(NLM_FBIG) +#define nlm4_failed cpu_to_be32(NLM_FAILED) + +/* + * Internal-use status codes, not to be placed on the wire. + * Version handlers translate these to appropriate wire values. + */ +#define nlm__int__drop_reply cpu_to_be32(30000) +#define nlm__int__deadlock cpu_to_be32(30001) +#define nlm__int__stale_fh cpu_to_be32(30002) +#define nlm__int__failed cpu_to_be32(30003) + /* * Lockd host handle (used both by the client and server personality). */ @@ -149,6 +179,8 @@ struct nlm_rqst { void * a_callback_data; /* sent to nlmclnt_operations callbacks */ }; +struct nlm_share; + /* * This struct describes a file held open by lockd on behalf of * an NFS client. @@ -196,9 +228,10 @@ struct nlm_block { * Global variables */ extern const struct rpc_program nlm_program; -extern const struct svc_procedure nlmsvc_procedures[24]; +extern const struct svc_version nlmsvc_version1; +extern const struct svc_version nlmsvc_version3; #ifdef CONFIG_LOCKD_V4 -extern const struct svc_procedure nlmsvc_procedures4[24]; +extern const struct svc_version nlmsvc_version4; #endif extern int nlmsvc_grace_period; extern unsigned long nlm_timeout; @@ -226,6 +259,10 @@ int nlmclnt_reclaim(struct nlm_host *, struct file_lock *, struct nlm_rqst *); void nlmclnt_next_cookie(struct nlm_cookie *); +#ifdef CONFIG_LOCKD_V4 +extern const struct rpc_version nlm_version4; +#endif + /* * Host cache */ @@ -289,6 +326,7 @@ void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, void nlmsvc_grant_reply(struct nlm_cookie *, __be32); void nlmsvc_release_call(struct nlm_rqst *); void nlmsvc_locks_init_private(struct file_lock *, struct nlm_host *, pid_t); +int nlmsvc_dispatch(struct svc_rqst *rqstp); /* * File handling for the server personality @@ -302,12 +340,6 @@ void nlmsvc_mark_resources(struct net *); void nlmsvc_free_host_resources(struct nlm_host *); void nlmsvc_invalidate_all(void); -/* - * Cluster failover support - */ -int nlmsvc_unlock_all_by_sb(struct super_block *sb); -int nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr); - static inline struct file *nlmsvc_file_file(const struct nlm_file *file) { return file->f_file[O_RDONLY] ? @@ -390,6 +422,31 @@ static inline int nlm_compare_locks(const struct file_lock *fl1, &&(fl1->c.flc_type == fl2->c.flc_type || fl2->c.flc_type == F_UNLCK); } +/** + * lockd_set_file_lock_range4 - set the byte range of a file_lock + * @fl: file_lock whose length fields are to be initialized + * @off: starting offset of the lock, in bytes + * @len: length of the byte range, in bytes, or zero + * + * The NLMv4 protocol represents lock byte ranges as (start, length), + * where length zero means "lock to end of file." The kernel's file_lock + * structure uses (start, end) representation. Convert from NLMv4 format + * to file_lock format, clamping the starting offset and treating + * arithmetic overflow as "lock to EOF." + */ +static inline void +lockd_set_file_lock_range4(struct file_lock *fl, u64 off, u64 len) +{ + u64 clamped_off = (off > OFFSET_MAX) ? OFFSET_MAX : off; + s64 end = clamped_off + len - 1; + + fl->fl_start = clamped_off; + if (len == 0 || end < 0) + fl->fl_end = OFFSET_MAX; + else + fl->fl_end = end; +} + extern const struct lock_manager_operations nlmsvc_lock_operations; -#endif /* LINUX_LOCKD_LOCKD_H */ +#endif /* _LOCKD_LOCKD_H */ diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index b8fc732e1c67..3d3ee88ca4dc 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -16,10 +16,10 @@ #include #include #include -#include #include +#include "lockd.h" #include "netns.h" #define NLMDBG_FACILITY NLMDBG_MONITOR diff --git a/include/linux/lockd/nlm.h b/fs/lockd/nlm.h similarity index 91% rename from include/linux/lockd/nlm.h rename to fs/lockd/nlm.h index 6e343ef760dc..47be65d0111f 100644 --- a/include/linux/lockd/nlm.h +++ b/fs/lockd/nlm.h @@ -1,14 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * linux/include/linux/lockd/nlm.h - * * Declarations for the Network Lock Manager protocol. * * Copyright (C) 1996, Olaf Kirch */ -#ifndef LINUX_LOCKD_NLM_H -#define LINUX_LOCKD_NLM_H +#ifndef _LOCKD_NLM_H +#define _LOCKD_NLM_H /* Maximum file offset in file_lock.fl_end */ @@ -55,4 +53,4 @@ enum { #define NLMPROC_NM_LOCK 22 #define NLMPROC_FREE_ALL 23 -#endif /* LINUX_LOCKD_NLM_H */ +#endif /* _LOCKD_NLM_H */ diff --git a/fs/lockd/nlm4xdr_gen.c b/fs/lockd/nlm4xdr_gen.c new file mode 100644 index 000000000000..1c8c221db456 --- /dev/null +++ b/fs/lockd/nlm4xdr_gen.c @@ -0,0 +1,724 @@ +// SPDX-License-Identifier: GPL-2.0 +// Generated by xdrgen. Manual edits will be lost. +// XDR specification file: ../../Documentation/sunrpc/xdr/nlm4.x +// XDR specification modification time: Thu Dec 25 13:10:19 2025 + +#include + +#include "nlm4xdr_gen.h" + +static bool __maybe_unused +xdrgen_decode_netobj(struct xdr_stream *xdr, netobj *ptr) +{ + return xdrgen_decode_opaque(xdr, ptr, MAXNETOBJ_SZ); +} + +static bool __maybe_unused +xdrgen_decode_fsh4_mode(struct xdr_stream *xdr, fsh4_mode *ptr) +{ + u32 val; + + if (xdr_stream_decode_u32(xdr, &val) < 0) + return false; + *ptr = val; + return true; +} + +static bool __maybe_unused +xdrgen_decode_fsh4_access(struct xdr_stream *xdr, fsh4_access *ptr) +{ + u32 val; + + if (xdr_stream_decode_u32(xdr, &val) < 0) + return false; + *ptr = val; + return true; +} + +static bool __maybe_unused +xdrgen_decode_uint64(struct xdr_stream *xdr, uint64 *ptr) +{ + return xdrgen_decode_unsigned_hyper(xdr, ptr); +} + +static bool __maybe_unused +xdrgen_decode_int64(struct xdr_stream *xdr, int64 *ptr) +{ + return xdrgen_decode_hyper(xdr, ptr); +} + +static bool __maybe_unused +xdrgen_decode_uint32(struct xdr_stream *xdr, uint32 *ptr) +{ + return xdrgen_decode_unsigned_long(xdr, ptr); +} + +static bool __maybe_unused +xdrgen_decode_int32(struct xdr_stream *xdr, int32 *ptr) +{ + return xdrgen_decode_long(xdr, ptr); +} + +static bool __maybe_unused +xdrgen_decode_nlm4_stats(struct xdr_stream *xdr, nlm4_stats *ptr) +{ + return xdr_stream_decode_be32(xdr, ptr) == 0; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_holder(struct xdr_stream *xdr, struct nlm4_holder *ptr) +{ + if (!xdrgen_decode_bool(xdr, &ptr->exclusive)) + return false; + if (!xdrgen_decode_int32(xdr, &ptr->svid)) + return false; + if (!xdrgen_decode_netobj(xdr, &ptr->oh)) + return false; + if (!xdrgen_decode_uint64(xdr, &ptr->l_offset)) + return false; + if (!xdrgen_decode_uint64(xdr, &ptr->l_len)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_testrply(struct xdr_stream *xdr, struct nlm4_testrply *ptr) +{ + if (!xdrgen_decode_nlm4_stats(xdr, &ptr->stat)) + return false; + switch (ptr->stat) { + case __constant_cpu_to_be32(NLM4_DENIED): + if (!xdrgen_decode_nlm4_holder(xdr, &ptr->u.holder)) + return false; + break; + default: + break; + } + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_stat(struct xdr_stream *xdr, struct nlm4_stat *ptr) +{ + if (!xdrgen_decode_nlm4_stats(xdr, &ptr->stat)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_res(struct xdr_stream *xdr, struct nlm4_res *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_nlm4_stat(xdr, &ptr->stat)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_testres(struct xdr_stream *xdr, struct nlm4_testres *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_nlm4_testrply(xdr, &ptr->stat)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_lock(struct xdr_stream *xdr, struct nlm4_lock *ptr) +{ + if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXSTRLEN)) + return false; + if (!xdrgen_decode_netobj(xdr, &ptr->fh)) + return false; + if (!xdrgen_decode_netobj(xdr, &ptr->oh)) + return false; + if (!xdrgen_decode_int32(xdr, &ptr->svid)) + return false; + if (!xdrgen_decode_uint64(xdr, &ptr->l_offset)) + return false; + if (!xdrgen_decode_uint64(xdr, &ptr->l_len)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_lockargs(struct xdr_stream *xdr, struct nlm4_lockargs *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->block)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->exclusive)) + return false; + if (!xdrgen_decode_nlm4_lock(xdr, &ptr->alock)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->reclaim)) + return false; + if (!xdrgen_decode_int32(xdr, &ptr->state)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_cancargs(struct xdr_stream *xdr, struct nlm4_cancargs *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->block)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->exclusive)) + return false; + if (!xdrgen_decode_nlm4_lock(xdr, &ptr->alock)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_testargs(struct xdr_stream *xdr, struct nlm4_testargs *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->exclusive)) + return false; + if (!xdrgen_decode_nlm4_lock(xdr, &ptr->alock)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_unlockargs(struct xdr_stream *xdr, struct nlm4_unlockargs *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_nlm4_lock(xdr, &ptr->alock)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_share(struct xdr_stream *xdr, struct nlm4_share *ptr) +{ + if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXSTRLEN)) + return false; + if (!xdrgen_decode_netobj(xdr, &ptr->fh)) + return false; + if (!xdrgen_decode_netobj(xdr, &ptr->oh)) + return false; + if (!xdrgen_decode_fsh4_mode(xdr, &ptr->mode)) + return false; + if (!xdrgen_decode_fsh4_access(xdr, &ptr->access)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_shareargs(struct xdr_stream *xdr, struct nlm4_shareargs *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_nlm4_share(xdr, &ptr->share)) + return false; + if (!xdrgen_decode_bool(xdr, &ptr->reclaim)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_shareres(struct xdr_stream *xdr, struct nlm4_shareres *ptr) +{ + if (!xdrgen_decode_netobj(xdr, &ptr->cookie)) + return false; + if (!xdrgen_decode_nlm4_stats(xdr, &ptr->stat)) + return false; + if (!xdrgen_decode_int32(xdr, &ptr->sequence)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_notify(struct xdr_stream *xdr, struct nlm4_notify *ptr) +{ + if (!xdrgen_decode_string(xdr, (string *)ptr, LM_MAXNAMELEN)) + return false; + if (!xdrgen_decode_int32(xdr, &ptr->state)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_decode_nlm4_notifyargs(struct xdr_stream *xdr, struct nlm4_notifyargs *ptr) +{ + if (!xdrgen_decode_nlm4_notify(xdr, &ptr->notify)) + return false; + if (xdr_stream_decode_opaque_fixed(xdr, ptr->private, SM_PRIV_SIZE) < 0) + return false; + return true; +} + +/** + * nlm4_svc_decode_void - Decode a void argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm4_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + return xdrgen_decode_void(xdr); +} + +/** + * nlm4_svc_decode_nlm4_testargs - Decode a nlm4_testargs argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm4_svc_decode_nlm4_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm4_testargs *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm4_testargs(xdr, argp); +} + +/** + * nlm4_svc_decode_nlm4_lockargs - Decode a nlm4_lockargs argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm4_svc_decode_nlm4_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm4_lockargs *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm4_lockargs(xdr, argp); +} + +/** + * nlm4_svc_decode_nlm4_cancargs - Decode a nlm4_cancargs argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm4_svc_decode_nlm4_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm4_cancargs *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm4_cancargs(xdr, argp); +} + +/** + * nlm4_svc_decode_nlm4_unlockargs - Decode a nlm4_unlockargs argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm4_svc_decode_nlm4_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm4_unlockargs *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm4_unlockargs(xdr, argp); +} + +/** + * nlm4_svc_decode_nlm4_testres - Decode a nlm4_testres argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm4_svc_decode_nlm4_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm4_testres *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm4_testres(xdr, argp); +} + +/** + * nlm4_svc_decode_nlm4_res - Decode a nlm4_res argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm4_svc_decode_nlm4_res(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm4_res *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm4_res(xdr, argp); +} + +/** + * nlm4_svc_decode_nlm4_notifyargs - Decode a nlm4_notifyargs argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm4_svc_decode_nlm4_notifyargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm4_notifyargs *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm4_notifyargs(xdr, argp); +} + +/** + * nlm4_svc_decode_nlm4_shareargs - Decode a nlm4_shareargs argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm4_svc_decode_nlm4_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm4_shareargs *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm4_shareargs(xdr, argp); +} + +/** + * nlm4_svc_decode_nlm4_notify - Decode a nlm4_notify argument + * @rqstp: RPC transaction context + * @xdr: source XDR data stream + * + * Return values: + * %true: procedure arguments decoded successfully + * %false: decode failed + */ +bool nlm4_svc_decode_nlm4_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm4_notify *argp = rqstp->rq_argp; + + return xdrgen_decode_nlm4_notify(xdr, argp); +} + +static bool __maybe_unused +xdrgen_encode_netobj(struct xdr_stream *xdr, const netobj value) +{ + return xdr_stream_encode_opaque(xdr, value.data, value.len) >= 0; +} + +static bool __maybe_unused +xdrgen_encode_fsh4_mode(struct xdr_stream *xdr, fsh4_mode value) +{ + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; +} + +static bool __maybe_unused +xdrgen_encode_fsh4_access(struct xdr_stream *xdr, fsh4_access value) +{ + return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; +} + +static bool __maybe_unused +xdrgen_encode_uint64(struct xdr_stream *xdr, const uint64 value) +{ + return xdrgen_encode_unsigned_hyper(xdr, value); +} + +static bool __maybe_unused +xdrgen_encode_int64(struct xdr_stream *xdr, const int64 value) +{ + return xdrgen_encode_hyper(xdr, value); +} + +static bool __maybe_unused +xdrgen_encode_uint32(struct xdr_stream *xdr, const uint32 value) +{ + return xdrgen_encode_unsigned_long(xdr, value); +} + +static bool __maybe_unused +xdrgen_encode_int32(struct xdr_stream *xdr, const int32 value) +{ + return xdrgen_encode_long(xdr, value); +} + +static bool __maybe_unused +xdrgen_encode_nlm4_stats(struct xdr_stream *xdr, nlm4_stats value) +{ + return xdr_stream_encode_be32(xdr, value) == XDR_UNIT; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_holder(struct xdr_stream *xdr, const struct nlm4_holder *value) +{ + if (!xdrgen_encode_bool(xdr, value->exclusive)) + return false; + if (!xdrgen_encode_int32(xdr, value->svid)) + return false; + if (!xdrgen_encode_netobj(xdr, value->oh)) + return false; + if (!xdrgen_encode_uint64(xdr, value->l_offset)) + return false; + if (!xdrgen_encode_uint64(xdr, value->l_len)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_testrply(struct xdr_stream *xdr, const struct nlm4_testrply *ptr) +{ + if (!xdrgen_encode_nlm4_stats(xdr, ptr->stat)) + return false; + switch (ptr->stat) { + case __constant_cpu_to_be32(NLM4_DENIED): + if (!xdrgen_encode_nlm4_holder(xdr, &ptr->u.holder)) + return false; + break; + default: + break; + } + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_stat(struct xdr_stream *xdr, const struct nlm4_stat *value) +{ + if (!xdrgen_encode_nlm4_stats(xdr, value->stat)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_res(struct xdr_stream *xdr, const struct nlm4_res *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_nlm4_stat(xdr, &value->stat)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_testres(struct xdr_stream *xdr, const struct nlm4_testres *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_nlm4_testrply(xdr, &value->stat)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_lock(struct xdr_stream *xdr, const struct nlm4_lock *value) +{ + if (value->caller_name.len > LM_MAXSTRLEN) + return false; + if (xdr_stream_encode_opaque(xdr, value->caller_name.data, value->caller_name.len) < 0) + return false; + if (!xdrgen_encode_netobj(xdr, value->fh)) + return false; + if (!xdrgen_encode_netobj(xdr, value->oh)) + return false; + if (!xdrgen_encode_int32(xdr, value->svid)) + return false; + if (!xdrgen_encode_uint64(xdr, value->l_offset)) + return false; + if (!xdrgen_encode_uint64(xdr, value->l_len)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_lockargs(struct xdr_stream *xdr, const struct nlm4_lockargs *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_bool(xdr, value->block)) + return false; + if (!xdrgen_encode_bool(xdr, value->exclusive)) + return false; + if (!xdrgen_encode_nlm4_lock(xdr, &value->alock)) + return false; + if (!xdrgen_encode_bool(xdr, value->reclaim)) + return false; + if (!xdrgen_encode_int32(xdr, value->state)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_cancargs(struct xdr_stream *xdr, const struct nlm4_cancargs *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_bool(xdr, value->block)) + return false; + if (!xdrgen_encode_bool(xdr, value->exclusive)) + return false; + if (!xdrgen_encode_nlm4_lock(xdr, &value->alock)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_testargs(struct xdr_stream *xdr, const struct nlm4_testargs *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_bool(xdr, value->exclusive)) + return false; + if (!xdrgen_encode_nlm4_lock(xdr, &value->alock)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_unlockargs(struct xdr_stream *xdr, const struct nlm4_unlockargs *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_nlm4_lock(xdr, &value->alock)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_share(struct xdr_stream *xdr, const struct nlm4_share *value) +{ + if (value->caller_name.len > LM_MAXSTRLEN) + return false; + if (xdr_stream_encode_opaque(xdr, value->caller_name.data, value->caller_name.len) < 0) + return false; + if (!xdrgen_encode_netobj(xdr, value->fh)) + return false; + if (!xdrgen_encode_netobj(xdr, value->oh)) + return false; + if (!xdrgen_encode_fsh4_mode(xdr, value->mode)) + return false; + if (!xdrgen_encode_fsh4_access(xdr, value->access)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_shareargs(struct xdr_stream *xdr, const struct nlm4_shareargs *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_nlm4_share(xdr, &value->share)) + return false; + if (!xdrgen_encode_bool(xdr, value->reclaim)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_shareres(struct xdr_stream *xdr, const struct nlm4_shareres *value) +{ + if (!xdrgen_encode_netobj(xdr, value->cookie)) + return false; + if (!xdrgen_encode_nlm4_stats(xdr, value->stat)) + return false; + if (!xdrgen_encode_int32(xdr, value->sequence)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_notify(struct xdr_stream *xdr, const struct nlm4_notify *value) +{ + if (value->name.len > LM_MAXNAMELEN) + return false; + if (xdr_stream_encode_opaque(xdr, value->name.data, value->name.len) < 0) + return false; + if (!xdrgen_encode_int32(xdr, value->state)) + return false; + return true; +} + +static bool __maybe_unused +xdrgen_encode_nlm4_notifyargs(struct xdr_stream *xdr, const struct nlm4_notifyargs *value) +{ + if (!xdrgen_encode_nlm4_notify(xdr, &value->notify)) + return false; + if (xdr_stream_encode_opaque_fixed(xdr, value->private, SM_PRIV_SIZE) < 0) + return false; + return true; +} + +/** + * nlm4_svc_encode_void - Encode a void result + * @rqstp: RPC transaction context + * @xdr: target XDR data stream + * + * Return values: + * %true: procedure results encoded successfully + * %false: encode failed + */ +bool nlm4_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + return xdrgen_encode_void(xdr); +} + +/** + * nlm4_svc_encode_nlm4_testres - Encode a nlm4_testres result + * @rqstp: RPC transaction context + * @xdr: target XDR data stream + * + * Return values: + * %true: procedure results encoded successfully + * %false: encode failed + */ +bool nlm4_svc_encode_nlm4_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm4_testres *resp = rqstp->rq_resp; + + return xdrgen_encode_nlm4_testres(xdr, resp); +} + +/** + * nlm4_svc_encode_nlm4_res - Encode a nlm4_res result + * @rqstp: RPC transaction context + * @xdr: target XDR data stream + * + * Return values: + * %true: procedure results encoded successfully + * %false: encode failed + */ +bool nlm4_svc_encode_nlm4_res(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm4_res *resp = rqstp->rq_resp; + + return xdrgen_encode_nlm4_res(xdr, resp); +} + +/** + * nlm4_svc_encode_nlm4_shareres - Encode a nlm4_shareres result + * @rqstp: RPC transaction context + * @xdr: target XDR data stream + * + * Return values: + * %true: procedure results encoded successfully + * %false: encode failed + */ +bool nlm4_svc_encode_nlm4_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr) +{ + struct nlm4_shareres *resp = rqstp->rq_resp; + + return xdrgen_encode_nlm4_shareres(xdr, resp); +} diff --git a/fs/lockd/nlm4xdr_gen.h b/fs/lockd/nlm4xdr_gen.h new file mode 100644 index 000000000000..b6008b296a3e --- /dev/null +++ b/fs/lockd/nlm4xdr_gen.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Generated by xdrgen. Manual edits will be lost. */ +/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm4.x */ +/* XDR specification modification time: Thu Dec 25 13:10:19 2025 */ + +#ifndef _LINUX_XDRGEN_NLM4_DECL_H +#define _LINUX_XDRGEN_NLM4_DECL_H + +#include + +#include +#include +#include +#include + +bool nlm4_svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm4_svc_decode_nlm4_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm4_svc_decode_nlm4_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm4_svc_decode_nlm4_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm4_svc_decode_nlm4_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm4_svc_decode_nlm4_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm4_svc_decode_nlm4_res(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm4_svc_decode_nlm4_notifyargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm4_svc_decode_nlm4_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm4_svc_decode_nlm4_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr); + +bool nlm4_svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm4_svc_encode_nlm4_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm4_svc_encode_nlm4_res(struct svc_rqst *rqstp, struct xdr_stream *xdr); +bool nlm4_svc_encode_nlm4_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr); + +#endif /* _LINUX_XDRGEN_NLM4_DECL_H */ diff --git a/include/linux/lockd/share.h b/fs/lockd/share.h similarity index 58% rename from include/linux/lockd/share.h rename to fs/lockd/share.h index 1f18a9faf645..20ea8ee49168 100644 --- a/include/linux/lockd/share.h +++ b/fs/lockd/share.h @@ -1,14 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * linux/include/linux/lockd/share.h - * * DOS share management for lockd. * * Copyright (C) 1996, Olaf Kirch */ -#ifndef LINUX_LOCKD_SHARE_H -#define LINUX_LOCKD_SHARE_H +#ifndef _LOCKD_SHARE_H +#define _LOCKD_SHARE_H + +/* Synthetic svid for lockowner lookup during share operations */ +#define LOCKD_SHARE_SVID (~(u32)0) /* * DOS share for a specific file @@ -22,11 +23,11 @@ struct nlm_share { u32 s_mode; /* deny mode */ }; -__be32 nlmsvc_share_file(struct nlm_host *, struct nlm_file *, - struct nlm_args *); -__be32 nlmsvc_unshare_file(struct nlm_host *, struct nlm_file *, - struct nlm_args *); +__be32 nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file, + struct xdr_netobj *oh, u32 access, u32 mode); +__be32 nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file, + struct xdr_netobj *oh); void nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *, nlm_host_match_fn_t); -#endif /* LINUX_LOCKD_SHARE_H */ +#endif /* _LOCKD_SHARE_H */ diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index dcd80c4e74c9..490551369ef2 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -36,15 +36,14 @@ #include #include #include -#include #include +#include "lockd.h" #include "netns.h" #include "procfs.h" #include "netlink.h" #define NLMDBG_FACILITY NLMDBG_SVC -#define LOCKD_BUFSIZE (1024 + NLMSVC_XDRSIZE) static struct svc_program nlmsvc_program; @@ -319,6 +318,7 @@ static struct notifier_block lockd_inet6addr_notifier = { static int lockd_get(void) { struct svc_serv *serv; + unsigned int bufsize; int error; if (nlmsvc_serv) { @@ -334,7 +334,15 @@ static int lockd_get(void) printk(KERN_WARNING "lockd_up: no pid, %d users??\n", nlmsvc_users); - serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, lockd); +#ifdef CONFIG_LOCKD_V4 + bufsize = 1024 + max3(nlmsvc_version1.vs_xdrsize, + nlmsvc_version3.vs_xdrsize, + nlmsvc_version4.vs_xdrsize); +#else + bufsize = 1024 + max(nlmsvc_version1.vs_xdrsize, + nlmsvc_version3.vs_xdrsize); +#endif + serv = svc_create(&nlmsvc_program, bufsize, lockd); if (!serv) { printk(KERN_WARNING "lockd_up: create service failed\n"); return -ENOMEM; @@ -640,7 +648,7 @@ module_exit(exit_nlm); * %0: Processing complete; do not send a Reply * %1: Processing complete; send Reply in rqstp->rq_res */ -static int nlmsvc_dispatch(struct svc_rqst *rqstp) +int nlmsvc_dispatch(struct svc_rqst *rqstp) { const struct svc_procedure *procp = rqstp->rq_procinfo; __be32 *statp = rqstp->rq_accept_statp; @@ -671,40 +679,6 @@ out_encode_err: /* * Define NLM program and procedures */ -static DEFINE_PER_CPU_ALIGNED(unsigned long, nlmsvc_version1_count[17]); -static const struct svc_version nlmsvc_version1 = { - .vs_vers = 1, - .vs_nproc = 17, - .vs_proc = nlmsvc_procedures, - .vs_count = nlmsvc_version1_count, - .vs_dispatch = nlmsvc_dispatch, - .vs_xdrsize = NLMSVC_XDRSIZE, -}; - -static DEFINE_PER_CPU_ALIGNED(unsigned long, - nlmsvc_version3_count[ARRAY_SIZE(nlmsvc_procedures)]); -static const struct svc_version nlmsvc_version3 = { - .vs_vers = 3, - .vs_nproc = ARRAY_SIZE(nlmsvc_procedures), - .vs_proc = nlmsvc_procedures, - .vs_count = nlmsvc_version3_count, - .vs_dispatch = nlmsvc_dispatch, - .vs_xdrsize = NLMSVC_XDRSIZE, -}; - -#ifdef CONFIG_LOCKD_V4 -static DEFINE_PER_CPU_ALIGNED(unsigned long, - nlmsvc_version4_count[ARRAY_SIZE(nlmsvc_procedures4)]); -static const struct svc_version nlmsvc_version4 = { - .vs_vers = 4, - .vs_nproc = ARRAY_SIZE(nlmsvc_procedures4), - .vs_proc = nlmsvc_procedures4, - .vs_count = nlmsvc_version4_count, - .vs_dispatch = nlmsvc_dispatch, - .vs_xdrsize = NLMSVC_XDRSIZE, -}; -#endif - static const struct svc_version *nlmsvc_version[] = { [1] = &nlmsvc_version1, [3] = &nlmsvc_version3, diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 4b6f18d97734..5de41e249534 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -10,257 +10,528 @@ #include #include -#include -#include #include -#define NLMDBG_FACILITY NLMDBG_CLIENT +#include "lockd.h" /* - * Obtain client and file from arguments + * xdr.h defines SM_MAXSTRLEN and SM_PRIV_SIZE as macros. + * nlm4xdr_gen.h defines them as enum constants. Undefine the + * macros to allow the xdrgen enum definitions to be used. */ -static __be32 -nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, - struct nlm_host **hostp, struct nlm_file **filp) -{ - struct nlm_host *host = NULL; - struct nlm_file *file = NULL; - struct nlm_lock *lock = &argp->lock; - __be32 error = 0; +#undef SM_MAXSTRLEN +#undef SM_PRIV_SIZE - /* nfsd callbacks must have been installed for this procedure */ - if (!nlmsvc_ops) +#include "share.h" +#include "nlm4xdr_gen.h" + +/* + * Wrapper structures combine xdrgen types with legacy nlm_lock. + * The xdrgen field must be first so the structure can be cast + * to its XDR type for the RPC dispatch layer. + */ +struct nlm4_testargs_wrapper { + struct nlm4_testargs xdrgen; + struct nlm_lock lock; +}; + +static_assert(offsetof(struct nlm4_testargs_wrapper, xdrgen) == 0); + +struct nlm4_lockargs_wrapper { + struct nlm4_lockargs xdrgen; + struct nlm_cookie cookie; + struct nlm_lock lock; +}; + +static_assert(offsetof(struct nlm4_lockargs_wrapper, xdrgen) == 0); + +struct nlm4_cancargs_wrapper { + struct nlm4_cancargs xdrgen; + struct nlm_lock lock; +}; + +static_assert(offsetof(struct nlm4_cancargs_wrapper, xdrgen) == 0); + +struct nlm4_unlockargs_wrapper { + struct nlm4_unlockargs xdrgen; + struct nlm_lock lock; +}; + +static_assert(offsetof(struct nlm4_unlockargs_wrapper, xdrgen) == 0); + +struct nlm4_notifyargs_wrapper { + struct nlm4_notifyargs xdrgen; + struct nlm_reboot reboot; +}; + +static_assert(offsetof(struct nlm4_notifyargs_wrapper, xdrgen) == 0); + +struct nlm4_notify_wrapper { + struct nlm4_notify xdrgen; +}; + +static_assert(offsetof(struct nlm4_notify_wrapper, xdrgen) == 0); + +struct nlm4_testres_wrapper { + struct nlm4_testres xdrgen; + struct nlm_lock lock; +}; + +struct nlm4_shareargs_wrapper { + struct nlm4_shareargs xdrgen; + struct nlm_lock lock; +}; + +static_assert(offsetof(struct nlm4_shareargs_wrapper, xdrgen) == 0); + +static_assert(offsetof(struct nlm4_testres_wrapper, xdrgen) == 0); + +struct nlm4_res_wrapper { + struct nlm4_res xdrgen; + struct nlm_cookie cookie; +}; + +static_assert(offsetof(struct nlm4_res_wrapper, xdrgen) == 0); + +struct nlm4_shareres_wrapper { + struct nlm4_shareres xdrgen; +}; + +static_assert(offsetof(struct nlm4_shareres_wrapper, xdrgen) == 0); + +static __be32 +nlm4_netobj_to_cookie(struct nlm_cookie *cookie, netobj *object) +{ + if (object->len > NLM_MAXCOOKIELEN) return nlm_lck_denied_nolocks; + cookie->len = object->len; + memcpy(cookie->data, object->data, object->len); + return nlm_granted; +} + +static __be32 +nlm4_lock_to_nlm_lock(struct nlm_lock *lock, struct nlm4_lock *alock) +{ + if (alock->fh.len > NFS_MAXFHSIZE) + return nlm_lck_denied; + lock->fh.size = alock->fh.len; + memcpy(lock->fh.data, alock->fh.data, alock->fh.len); + lock->oh.len = alock->oh.len; + lock->oh.data = alock->oh.data; + lock->svid = alock->svid; + locks_init_lock(&lock->fl); + lockd_set_file_lock_range4(&lock->fl, alock->l_offset, alock->l_len); + return nlm_granted; +} + +static struct nlm_host * +nlm4svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored) +{ + struct nlm_host *host; + + if (!nlmsvc_ops) + return NULL; + host = nlmsvc_lookup_host(rqstp, caller.data, caller.len); + if (!host) + return NULL; + if (monitored && nsm_monitor(host) < 0) { + nlmsvc_release_host(host); + return NULL; + } + return host; +} + +static __be32 +nlm4svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host, + struct nlm_lock *lock, struct nlm_file **filp, + struct nlm4_lock *xdr_lock, unsigned char type) +{ + struct file_lock *fl = &lock->fl; + struct nlm_file *file = NULL; + __be32 error; + + if (xdr_lock->fh.len > NFS_MAXFHSIZE) + return nlm_lck_denied_nolocks; + lock->fh.size = xdr_lock->fh.len; + memcpy(lock->fh.data, xdr_lock->fh.data, xdr_lock->fh.len); + + lock->oh.len = xdr_lock->oh.len; + lock->oh.data = xdr_lock->oh.data; + + lock->svid = xdr_lock->svid; + lock->lock_start = xdr_lock->l_offset; + lock->lock_len = xdr_lock->l_len; if (lock->lock_start > OFFSET_MAX || (lock->lock_len && ((lock->lock_len - 1) > (OFFSET_MAX - lock->lock_start)))) return nlm4_fbig; - /* Obtain host handle */ - if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len)) - || (argp->monitor && nsm_monitor(host) < 0)) - goto no_locks; - *hostp = host; + locks_init_lock(fl); + fl->c.flc_type = type; + lockd_set_file_lock_range4(fl, lock->lock_start, lock->lock_len); - /* Obtain file pointer. Not used by FREE_ALL call. */ - if (filp != NULL) { - int mode = lock_to_openmode(&lock->fl); - - lock->fl.c.flc_flags = FL_POSIX; - - error = nlm_lookup_file(rqstp, &file, lock); - if (error) - goto no_locks; - *filp = file; - - /* Set up the missing parts of the file_lock structure */ - lock->fl.c.flc_file = file->f_file[mode]; - lock->fl.c.flc_pid = current->tgid; - lock->fl.fl_start = (loff_t)lock->lock_start; - lock->fl.fl_end = lock->lock_len ? - (loff_t)(lock->lock_start + lock->lock_len - 1) : - OFFSET_MAX; - lock->fl.fl_lmops = &nlmsvc_lock_operations; - nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid); - if (!lock->fl.c.flc_owner) { - /* lockowner allocation has failed */ - nlmsvc_release_host(host); - return nlm_lck_denied_nolocks; - } + error = nlm_lookup_file(rqstp, &file, lock); + switch (error) { + case nlm_granted: + break; + case nlm__int__stale_fh: + return nlm4_stale_fh; + case nlm__int__failed: + return nlm4_failed; + default: + return error; } + *filp = file; - return 0; + fl->c.flc_flags = FL_POSIX; + fl->c.flc_file = file->f_file[lock_to_openmode(fl)]; + fl->c.flc_pid = current->tgid; + fl->fl_lmops = &nlmsvc_lock_operations; + nlmsvc_locks_init_private(fl, host, (pid_t)lock->svid); + if (!fl->c.flc_owner) + return nlm_lck_denied_nolocks; -no_locks: - nlmsvc_release_host(host); - if (error) - return error; - return nlm_lck_denied_nolocks; + return nlm_granted; } -/* - * NULL: Test for presence of service +/** + * nlm4svc_proc_null - NULL: Test for presence of service + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully + * + * RPC synopsis: + * void NLMPROC4_NULL(void) = 0; */ static __be32 nlm4svc_proc_null(struct svc_rqst *rqstp) { - dprintk("lockd: NULL called\n"); return rpc_success; } -/* - * TEST: Check for conflicting lock +/** + * nlm4svc_proc_test - TEST: Check for conflicting lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm4_testres NLMPROC4_TEST(nlm4_testargs) = 1; + * + * Permissible procedure status codes: + * %NLM4_GRANTED: The server would be able to grant the + * requested lock. + * %NLM4_DENIED: The requested lock conflicted with existing + * lock reservations for the file. + * %NLM4_DENIED_NOLOCKS: The server could not allocate the resources + * needed to process the request. + * %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + * + * The Linux NLM server implementation also returns: + * %NLM4_STALE_FH: The request specified an invalid file handle. + * %NLM4_FBIG: The request specified a length or offset + * that exceeds the range supported by the + * server. + * %NLM4_FAILED: The request failed for an unspecified reason. */ -static __be32 -__nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp) +static __be32 nlm4svc_proc_test(struct svc_rqst *rqstp) { - struct nlm_args *argp = rqstp->rq_argp; + struct nlm4_testargs_wrapper *argp = rqstp->rq_argp; + unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; + struct nlm4_testres_wrapper *resp = rqstp->rq_resp; + struct nlm_file *file = NULL; struct nlm_host *host; - struct nlm_file *file; - __be32 rc = rpc_success; - dprintk("lockd: TEST4 called\n"); - resp->cookie = argp->cookie; + resp->xdrgen.cookie = argp->xdrgen.cookie; - /* Obtain client and file */ - if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; + resp->xdrgen.stat.stat = nlm_lck_denied_nolocks; + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + goto out; - /* Now check for conflicting locks */ - resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, - &resp->lock); - if (resp->status == nlm_drop_reply) - rc = rpc_drop_reply; - else - dprintk("lockd: TEST4 status %d\n", ntohl(resp->status)); + resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, + type); + if (resp->xdrgen.stat.stat) + goto out; + + resp->xdrgen.stat.stat = nlmsvc_testlock(rqstp, file, host, + &argp->lock, &resp->lock); + nlmsvc_release_lockowner(&argp->lock); + + if (resp->xdrgen.stat.stat == nlm_lck_denied) { + struct nlm_lock *conf = &resp->lock; + struct nlm4_holder *holder = &resp->xdrgen.stat.u.holder; + + holder->exclusive = (conf->fl.c.flc_type != F_RDLCK); + holder->svid = conf->svid; + holder->oh.len = conf->oh.len; + holder->oh.data = conf->oh.data; + holder->l_offset = conf->fl.fl_start; + if (conf->fl.fl_end == OFFSET_MAX) + holder->l_len = 0; + else + holder->l_len = conf->fl.fl_end - conf->fl.fl_start + 1; + } + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->xdrgen.stat.stat == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; +} + +static __be32 +nlm4svc_do_lock(struct svc_rqst *rqstp, bool monitored) +{ + struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp; + unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; + struct nlm4_res_wrapper *resp = rqstp->rq_resp; + struct nlm_file *file = NULL; + struct nlm_host *host = NULL; + + resp->xdrgen.cookie = argp->xdrgen.cookie; + + resp->xdrgen.stat.stat = nlm4_netobj_to_cookie(&argp->cookie, + &argp->xdrgen.cookie); + if (resp->xdrgen.stat.stat) + goto out; + + resp->xdrgen.stat.stat = nlm_lck_denied_nolocks; + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, + monitored); + if (!host) + goto out; + + resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, + type); + if (resp->xdrgen.stat.stat) + goto out; + + resp->xdrgen.stat.stat = nlmsvc_lock(rqstp, file, host, &argp->lock, + argp->xdrgen.block, &argp->cookie, + argp->xdrgen.reclaim); + if (resp->xdrgen.stat.stat == nlm__int__deadlock) + resp->xdrgen.stat.stat = nlm4_deadlock; nlmsvc_release_lockowner(&argp->lock); + +out: + if (file) + nlm_release_file(file); nlmsvc_release_host(host); - nlm_release_file(file); - return rc; -} - -static __be32 -nlm4svc_proc_test(struct svc_rqst *rqstp) -{ - return __nlm4svc_proc_test(rqstp, rqstp->rq_resp); -} - -static __be32 -__nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp) -{ - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_host *host; - struct nlm_file *file; - __be32 rc = rpc_success; - - dprintk("lockd: LOCK called\n"); - - resp->cookie = argp->cookie; - - /* Obtain client and file */ - if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; - - /* Now try to lock the file */ - resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock, - argp->block, &argp->cookie, - argp->reclaim); - if (resp->status == nlm_drop_reply) - rc = rpc_drop_reply; - else - dprintk("lockd: LOCK status %d\n", ntohl(resp->status)); - - nlmsvc_release_lockowner(&argp->lock); - nlmsvc_release_host(host); - nlm_release_file(file); - return rc; + return resp->xdrgen.stat.stat == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; } +/** + * nlm4svc_proc_lock - LOCK: Establish a monitored lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm4_res NLMPROC4_LOCK(nlm4_lockargs) = 2; + * + * Permissible procedure status codes: + * %NLM4_GRANTED: The requested lock was granted. + * %NLM4_DENIED: The requested lock conflicted with existing + * lock reservations for the file. + * %NLM4_DENIED_NOLOCKS: The server could not allocate the resources + * needed to process the request. + * %NLM4_BLOCKED: The blocking request cannot be granted + * immediately. The server will send an + * NLMPROC4_GRANTED callback to the client when + * the lock can be granted. + * %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + * + * The Linux NLM server implementation also returns: + * %NLM4_DEADLCK: The request could not be granted and + * blocking would cause a deadlock. + * %NLM4_STALE_FH: The request specified an invalid file handle. + * %NLM4_FBIG: The request specified a length or offset + * that exceeds the range supported by the + * server. + * %NLM4_FAILED: The request failed for an unspecified reason. + */ static __be32 nlm4svc_proc_lock(struct svc_rqst *rqstp) { - return __nlm4svc_proc_lock(rqstp, rqstp->rq_resp); -} - -static __be32 -__nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_res *resp) -{ - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_host *host; - struct nlm_file *file; - - dprintk("lockd: CANCEL called\n"); - - resp->cookie = argp->cookie; - - /* Don't accept requests during grace period */ - if (locks_in_grace(SVC_NET(rqstp))) { - resp->status = nlm_lck_denied_grace_period; - return rpc_success; - } - - /* Obtain client and file */ - if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; - - /* Try to cancel request. */ - resp->status = nlmsvc_cancel_blocked(SVC_NET(rqstp), file, &argp->lock); - - dprintk("lockd: CANCEL status %d\n", ntohl(resp->status)); - nlmsvc_release_lockowner(&argp->lock); - nlmsvc_release_host(host); - nlm_release_file(file); - return rpc_success; + return nlm4svc_do_lock(rqstp, true); } +/** + * nlm4svc_proc_cancel - CANCEL: Cancel an outstanding blocked lock request + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully + * %rpc_drop_reply: Do not send an RPC reply + * + * RPC synopsis: + * nlm4_res NLMPROC4_CANCEL(nlm4_cancargs) = 3; + * + * Permissible procedure status codes: + * %NLM4_LCK_GRANTED: The requested lock was canceled. + * %NLM4_LCK_DENIED: There was no lock to cancel. + * %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + * + * The Linux NLM server implementation also returns: + * %NLM4_DENIED_NOLOCKS: A needed resource could not be allocated. + * %NLM4_STALE_FH: The request specified an invalid file handle. + * %NLM4_FBIG: The request specified a length or offset + * that exceeds the range supported by the + * server. + * %NLM4_FAILED: The request failed for an unspecified reason. + */ static __be32 nlm4svc_proc_cancel(struct svc_rqst *rqstp) { - return __nlm4svc_proc_cancel(rqstp, rqstp->rq_resp); -} + struct nlm4_cancargs_wrapper *argp = rqstp->rq_argp; + unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; + struct nlm4_res_wrapper *resp = rqstp->rq_resp; + struct net *net = SVC_NET(rqstp); + struct nlm_host *host = NULL; + struct nlm_file *file = NULL; -/* - * UNLOCK: release a lock - */ -static __be32 -__nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_res *resp) -{ - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_host *host; - struct nlm_file *file; + resp->xdrgen.cookie = argp->xdrgen.cookie; - dprintk("lockd: UNLOCK called\n"); + resp->xdrgen.stat.stat = nlm_lck_denied_grace_period; + if (locks_in_grace(net)) + goto out; - resp->cookie = argp->cookie; + resp->xdrgen.stat.stat = nlm_lck_denied_nolocks; + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + goto out; - /* Don't accept new lock requests during grace period */ - if (locks_in_grace(SVC_NET(rqstp))) { - resp->status = nlm_lck_denied_grace_period; - return rpc_success; - } + resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, + type); + if (resp->xdrgen.stat.stat) + goto out; - /* Obtain client and file */ - if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; - - /* Now try to remove the lock */ - resp->status = nlmsvc_unlock(SVC_NET(rqstp), file, &argp->lock); - - dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status)); + resp->xdrgen.stat.stat = nlmsvc_cancel_blocked(net, file, &argp->lock); nlmsvc_release_lockowner(&argp->lock); + +out: + if (file) + nlm_release_file(file); nlmsvc_release_host(host); - nlm_release_file(file); - return rpc_success; + return resp->xdrgen.stat.stat == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; } +/** + * nlm4svc_proc_unlock - UNLOCK: Remove a lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm4_res NLMPROC4_UNLOCK(nlm4_unlockargs) = 4; + * + * Permissible procedure status codes: + * %NLM4_GRANTED: The requested lock was released. + * %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + * + * The Linux NLM server implementation also returns: + * %NLM4_DENIED_NOLOCKS: A needed resource could not be allocated. + * %NLM4_STALE_FH: The request specified an invalid file handle. + * %NLM4_FBIG: The request specified a length or offset + * that exceeds the range supported by the + * server. + * %NLM4_FAILED: The request failed for an unspecified reason. + */ static __be32 nlm4svc_proc_unlock(struct svc_rqst *rqstp) { - return __nlm4svc_proc_unlock(rqstp, rqstp->rq_resp); + struct nlm4_unlockargs_wrapper *argp = rqstp->rq_argp; + struct nlm4_res_wrapper *resp = rqstp->rq_resp; + struct net *net = SVC_NET(rqstp); + struct nlm_host *host = NULL; + struct nlm_file *file = NULL; + + resp->xdrgen.cookie = argp->xdrgen.cookie; + + resp->xdrgen.stat.stat = nlm_lck_denied_grace_period; + if (locks_in_grace(net)) + goto out; + + resp->xdrgen.stat.stat = nlm_lck_denied_nolocks; + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + goto out; + + resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, + F_UNLCK); + if (resp->xdrgen.stat.stat) + goto out; + + resp->xdrgen.stat.stat = nlmsvc_unlock(net, file, &argp->lock); + nlmsvc_release_lockowner(&argp->lock); + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->xdrgen.stat.stat == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; } -/* - * GRANTED: A server calls us to tell that a process' lock request - * was granted +/** + * nlm4svc_proc_granted - GRANTED: Server grants a previously blocked lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * + * RPC synopsis: + * nlm4_res NLMPROC4_GRANTED(nlm4_testargs) = 5; + * + * Permissible procedure status codes: + * %NLM4_GRANTED: The requested lock was granted. + * %NLM4_DENIED: The server could not allocate the resources + * needed to process the request. + * %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. */ -static __be32 -__nlm4svc_proc_granted(struct svc_rqst *rqstp, struct nlm_res *resp) -{ - struct nlm_args *argp = rqstp->rq_argp; - - resp->cookie = argp->cookie; - - dprintk("lockd: GRANTED called\n"); - resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock); - dprintk("lockd: GRANTED status %d\n", ntohl(resp->status)); - return rpc_success; -} - static __be32 nlm4svc_proc_granted(struct svc_rqst *rqstp) { - return __nlm4svc_proc_granted(rqstp, rqstp->rq_resp); + struct nlm4_testargs_wrapper *argp = rqstp->rq_argp; + struct nlm4_res_wrapper *resp = rqstp->rq_resp; + + resp->xdrgen.cookie = argp->xdrgen.cookie; + + resp->xdrgen.stat.stat = nlm4_lock_to_nlm_lock(&argp->lock, + &argp->xdrgen.alock); + if (resp->xdrgen.stat.stat) + goto out; + + resp->xdrgen.stat.stat = nlmclnt_grant(svc_addr(rqstp), &argp->lock); + +out: + return rpc_success; } /* @@ -281,24 +552,17 @@ static const struct rpc_call_ops nlm4svc_callback_ops = { }; /* - * `Async' versions of the above service routines. They aren't really, - * because we send the callback before the reply proper. I hope this - * doesn't break any clients. + * Dispatch an async callback RPC to a client with a pre-resolved host. + * Caller provides a reference to @host; this function takes ownership + * and releases it via nlmsvc_release_host() before returning. */ -static __be32 nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, - __be32 (*func)(struct svc_rqst *, struct nlm_res *)) +static __be32 +nlm4svc_callback(struct svc_rqst *rqstp, struct nlm_host *host, u32 proc, + __be32 (*func)(struct svc_rqst *, struct nlm_res *)) { - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_host *host; struct nlm_rqst *call; __be32 stat; - host = nlmsvc_lookup_host(rqstp, - argp->lock.caller, - argp->lock.len); - if (host == NULL) - return rpc_system_err; - call = nlm_alloc_call(host); nlmsvc_release_host(host); if (call == NULL) @@ -316,433 +580,845 @@ static __be32 nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, return rpc_success; } +static __be32 +__nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_res *resp) +{ + struct nlm4_testargs_wrapper *argp = rqstp->rq_argp; + unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; + struct nlm_lockowner *owner; + struct nlm_file *file = NULL; + struct nlm_host *host = NULL; + + resp->status = nlm_lck_denied_nolocks; + if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) + goto out; + + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + goto out; + + resp->status = nlm4svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, type); + if (resp->status) + goto out; + + owner = argp->lock.fl.c.flc_owner; + resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, + &resp->lock); + nlmsvc_put_lockowner(owner); + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->status == nlm__int__drop_reply ? rpc_drop_reply : rpc_success; +} + +/** + * nlm4svc_proc_test_msg - TEST_MSG: Check for conflicting lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_system_err: RPC execution failed. + * + * RPC synopsis: + * void NLMPROC4_TEST_MSG(nlm4_testargs) = 6; + * + * The response to this request is delivered via the TEST_RES procedure. + */ static __be32 nlm4svc_proc_test_msg(struct svc_rqst *rqstp) { - dprintk("lockd: TEST_MSG called\n"); - return nlm4svc_callback(rqstp, NLMPROC_TEST_RES, __nlm4svc_proc_test); + struct nlm4_testargs_wrapper *argp = rqstp->rq_argp; + struct nlm_host *host; + + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + return rpc_system_err; + + return nlm4svc_callback(rqstp, host, NLMPROC4_TEST_RES, + __nlm4svc_proc_test_msg); } +static __be32 +__nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_res *resp) +{ + struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp; + unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; + struct nlm_file *file = NULL; + struct nlm_host *host = NULL; + + resp->status = nlm_lck_denied_nolocks; + if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) + goto out; + + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, true); + if (!host) + goto out; + + resp->status = nlm4svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, type); + if (resp->status) + goto out; + + resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock, + argp->xdrgen.block, &resp->cookie, + argp->xdrgen.reclaim); + nlmsvc_release_lockowner(&argp->lock); + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->status == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; +} + +/** + * nlm4svc_proc_lock_msg - LOCK_MSG: Establish a monitored lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_system_err: RPC execution failed. + * + * RPC synopsis: + * void NLMPROC4_LOCK_MSG(nlm4_lockargs) = 7; + * + * The response to this request is delivered via the LOCK_RES procedure. + */ static __be32 nlm4svc_proc_lock_msg(struct svc_rqst *rqstp) { - dprintk("lockd: LOCK_MSG called\n"); - return nlm4svc_callback(rqstp, NLMPROC_LOCK_RES, __nlm4svc_proc_lock); + struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp; + struct nlm_host *host; + + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, true); + if (!host) + return rpc_system_err; + + return nlm4svc_callback(rqstp, host, NLMPROC4_LOCK_RES, + __nlm4svc_proc_lock_msg); } +static __be32 +__nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_res *resp) +{ + struct nlm4_cancargs_wrapper *argp = rqstp->rq_argp; + unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK; + struct net *net = SVC_NET(rqstp); + struct nlm_file *file = NULL; + struct nlm_host *host = NULL; + + resp->status = nlm_lck_denied_nolocks; + if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) + goto out; + + resp->status = nlm_lck_denied_grace_period; + if (locks_in_grace(net)) + goto out; + + resp->status = nlm_lck_denied_nolocks; + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + goto out; + + resp->status = nlm4svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, type); + if (resp->status) + goto out; + + resp->status = nlmsvc_cancel_blocked(net, file, &argp->lock); + nlmsvc_release_lockowner(&argp->lock); + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->status == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; +} + +/** + * nlm4svc_proc_cancel_msg - CANCEL_MSG: Cancel an outstanding lock request + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_system_err: RPC execution failed. + * + * RPC synopsis: + * void NLMPROC4_CANCEL_MSG(nlm4_cancargs) = 8; + * + * The response to this request is delivered via the CANCEL_RES procedure. + */ static __be32 nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp) { - dprintk("lockd: CANCEL_MSG called\n"); - return nlm4svc_callback(rqstp, NLMPROC_CANCEL_RES, __nlm4svc_proc_cancel); + struct nlm4_cancargs_wrapper *argp = rqstp->rq_argp; + struct nlm_host *host; + + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + return rpc_system_err; + + return nlm4svc_callback(rqstp, host, NLMPROC4_CANCEL_RES, + __nlm4svc_proc_cancel_msg); } +static __be32 +__nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_res *resp) +{ + struct nlm4_unlockargs_wrapper *argp = rqstp->rq_argp; + struct net *net = SVC_NET(rqstp); + struct nlm_file *file = NULL; + struct nlm_host *host = NULL; + + resp->status = nlm_lck_denied_nolocks; + if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) + goto out; + + resp->status = nlm_lck_denied_grace_period; + if (locks_in_grace(net)) + goto out; + + resp->status = nlm_lck_denied_nolocks; + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + goto out; + + resp->status = nlm4svc_lookup_file(rqstp, host, &argp->lock, + &file, &argp->xdrgen.alock, F_UNLCK); + if (resp->status) + goto out; + + resp->status = nlmsvc_unlock(net, file, &argp->lock); + nlmsvc_release_lockowner(&argp->lock); + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->status == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; +} + +/** + * nlm4svc_proc_unlock_msg - UNLOCK_MSG: Remove an existing lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_system_err: RPC execution failed. + * + * RPC synopsis: + * void NLMPROC4_UNLOCK_MSG(nlm4_unlockargs) = 9; + * + * The response to this request is delivered via the UNLOCK_RES procedure. + */ static __be32 nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp) { - dprintk("lockd: UNLOCK_MSG called\n"); - return nlm4svc_callback(rqstp, NLMPROC_UNLOCK_RES, __nlm4svc_proc_unlock); + struct nlm4_unlockargs_wrapper *argp = rqstp->rq_argp; + struct nlm_host *host; + + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + return rpc_system_err; + + return nlm4svc_callback(rqstp, host, NLMPROC4_UNLOCK_RES, + __nlm4svc_proc_unlock_msg); } +static __be32 +__nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_res *resp) +{ + struct nlm4_testargs_wrapper *argp = rqstp->rq_argp; + + resp->status = nlm_lck_denied; + if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie)) + goto out; + + if (nlm4_lock_to_nlm_lock(&argp->lock, &argp->xdrgen.alock)) + goto out; + + resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock); + +out: + return rpc_success; +} + +/** + * nlm4svc_proc_granted_msg - GRANTED_MSG: Blocked lock has been granted + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_system_err: RPC execution failed. + * + * RPC synopsis: + * void NLMPROC4_GRANTED_MSG(nlm4_testargs) = 10; + * + * The response to this request is delivered via the GRANTED_RES procedure. + */ static __be32 nlm4svc_proc_granted_msg(struct svc_rqst *rqstp) { - dprintk("lockd: GRANTED_MSG called\n"); - return nlm4svc_callback(rqstp, NLMPROC_GRANTED_RES, __nlm4svc_proc_granted); + struct nlm4_testargs_wrapper *argp = rqstp->rq_argp; + struct nlm_host *host; + + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + if (!host) + return rpc_system_err; + + return nlm4svc_callback(rqstp, host, NLMPROC4_GRANTED_RES, + __nlm4svc_proc_granted_msg); } -/* - * SHARE: create a DOS share or alter existing share. +/** + * nlm4svc_proc_granted_res - GRANTED_RES: Lock Granted result + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * + * RPC synopsis: + * void NLMPROC4_GRANTED_RES(nlm4_res) = 15; */ -static __be32 -nlm4svc_proc_share(struct svc_rqst *rqstp) +static __be32 nlm4svc_proc_granted_res(struct svc_rqst *rqstp) { - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_res *resp = rqstp->rq_resp; - struct nlm_host *host; - struct nlm_file *file; + struct nlm4_res_wrapper *argp = rqstp->rq_argp; - dprintk("lockd: SHARE called\n"); - - resp->cookie = argp->cookie; - - /* Don't accept new lock requests during grace period */ - if (locks_in_grace(SVC_NET(rqstp)) && !argp->reclaim) { - resp->status = nlm_lck_denied_grace_period; + if (!nlmsvc_ops) return rpc_success; - } - /* Obtain client and file */ - if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; + if (nlm4_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie)) + return rpc_success; + nlmsvc_grant_reply(&argp->cookie, argp->xdrgen.stat.stat); - /* Now try to create the share */ - resp->status = nlmsvc_share_file(host, file, argp); - - dprintk("lockd: SHARE status %d\n", ntohl(resp->status)); - nlmsvc_release_lockowner(&argp->lock); - nlmsvc_release_host(host); - nlm_release_file(file); return rpc_success; } -/* - * UNSHARE: Release a DOS share. +/** + * nlm4svc_proc_sm_notify - SM_NOTIFY: Peer has rebooted + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_system_err: RPC execution failed. + * + * The SM_NOTIFY procedure is a private callback from Linux statd and is + * not part of the official NLM protocol. + * + * RPC synopsis: + * void NLMPROC4_SM_NOTIFY(nlm4_notifyargs) = 16; */ -static __be32 -nlm4svc_proc_unshare(struct svc_rqst *rqstp) +static __be32 nlm4svc_proc_sm_notify(struct svc_rqst *rqstp) { - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_res *resp = rqstp->rq_resp; - struct nlm_host *host; - struct nlm_file *file; - - dprintk("lockd: UNSHARE called\n"); - - resp->cookie = argp->cookie; - - /* Don't accept requests during grace period */ - if (locks_in_grace(SVC_NET(rqstp))) { - resp->status = nlm_lck_denied_grace_period; - return rpc_success; - } - - /* Obtain client and file */ - if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; - - /* Now try to lock the file */ - resp->status = nlmsvc_unshare_file(host, file, argp); - - dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status)); - nlmsvc_release_lockowner(&argp->lock); - nlmsvc_release_host(host); - nlm_release_file(file); - return rpc_success; -} - -/* - * NM_LOCK: Create an unmonitored lock - */ -static __be32 -nlm4svc_proc_nm_lock(struct svc_rqst *rqstp) -{ - struct nlm_args *argp = rqstp->rq_argp; - - dprintk("lockd: NM_LOCK called\n"); - - argp->monitor = 0; /* just clean the monitor flag */ - return nlm4svc_proc_lock(rqstp); -} - -/* - * FREE_ALL: Release all locks and shares held by client - */ -static __be32 -nlm4svc_proc_free_all(struct svc_rqst *rqstp) -{ - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_host *host; - - /* Obtain client */ - if (nlm4svc_retrieve_args(rqstp, argp, &host, NULL)) - return rpc_success; - - nlmsvc_free_host_resources(host); - nlmsvc_release_host(host); - return rpc_success; -} - -/* - * SM_NOTIFY: private callback from statd (not part of official NLM proto) - */ -static __be32 -nlm4svc_proc_sm_notify(struct svc_rqst *rqstp) -{ - struct nlm_reboot *argp = rqstp->rq_argp; - - dprintk("lockd: SM_NOTIFY called\n"); + struct nlm4_notifyargs_wrapper *argp = rqstp->rq_argp; + struct nlm_reboot *reboot = &argp->reboot; if (!nlm_privileged_requester(rqstp)) { char buf[RPC_MAX_ADDRBUFLEN]; - printk(KERN_WARNING "lockd: rejected NSM callback from %s\n", - svc_print_addr(rqstp, buf, sizeof(buf))); + + pr_warn("lockd: rejected NSM callback from %s\n", + svc_print_addr(rqstp, buf, sizeof(buf))); return rpc_system_err; } - nlm_host_rebooted(SVC_NET(rqstp), argp); + reboot->len = argp->xdrgen.notify.name.len; + reboot->mon = (char *)argp->xdrgen.notify.name.data; + reboot->state = argp->xdrgen.notify.state; + memcpy(&reboot->priv.data, argp->xdrgen.private, + sizeof(reboot->priv.data)); + + nlm_host_rebooted(SVC_NET(rqstp), reboot); + return rpc_success; } -/* - * client sent a GRANTED_RES, let's remove the associated block +/** + * nlm4svc_proc_unused - stub for unused procedures + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_proc_unavail: Program can't support procedure. */ -static __be32 -nlm4svc_proc_granted_res(struct svc_rqst *rqstp) -{ - struct nlm_res *argp = rqstp->rq_argp; - - if (!nlmsvc_ops) - return rpc_success; - - dprintk("lockd: GRANTED_RES called\n"); - - nlmsvc_grant_reply(&argp->cookie, argp->status); - return rpc_success; -} - -static __be32 -nlm4svc_proc_unused(struct svc_rqst *rqstp) +static __be32 nlm4svc_proc_unused(struct svc_rqst *rqstp) { return rpc_proc_unavail; } +/** + * nlm4svc_proc_share - SHARE: Open a file using DOS file-sharing modes + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm4_shareres NLMPROC4_SHARE(nlm4_shareargs) = 20; + * + * Permissible procedure status codes: + * %NLM4_GRANTED: The requested share lock was granted. + * %NLM4_DENIED: The requested lock conflicted with existing + * lock reservations for the file. + * %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + * + * The Linux NLM server implementation also returns: + * %NLM4_DENIED_NOLOCKS: A needed resource could not be allocated. + * %NLM4_STALE_FH: The request specified an invalid file handle. + * %NLM4_FBIG: The request specified a length or offset + * that exceeds the range supported by the + * server. + * %NLM4_FAILED: The request failed for an unspecified reason. + */ +static __be32 nlm4svc_proc_share(struct svc_rqst *rqstp) +{ + struct nlm4_shareargs_wrapper *argp = rqstp->rq_argp; + struct nlm4_shareres_wrapper *resp = rqstp->rq_resp; + struct nlm_lock *lock = &argp->lock; + struct nlm_host *host = NULL; + struct nlm_file *file = NULL; + struct nlm4_lock xdr_lock = { + .fh = argp->xdrgen.share.fh, + .oh = argp->xdrgen.share.oh, + .svid = LOCKD_SHARE_SVID, + }; + + resp->xdrgen.cookie = argp->xdrgen.cookie; + + resp->xdrgen.stat = nlm_lck_denied_grace_period; + if (locks_in_grace(SVC_NET(rqstp)) && !argp->xdrgen.reclaim) + goto out; + + resp->xdrgen.stat = nlm_lck_denied_nolocks; + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.share.caller_name, true); + if (!host) + goto out; + + resp->xdrgen.stat = nlm4svc_lookup_file(rqstp, host, lock, &file, + &xdr_lock, F_RDLCK); + if (resp->xdrgen.stat) + goto out; + + resp->xdrgen.stat = nlmsvc_share_file(host, file, &lock->oh, + argp->xdrgen.share.access, + argp->xdrgen.share.mode); + + nlmsvc_release_lockowner(lock); + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->xdrgen.stat == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; +} + +/** + * nlm4svc_proc_unshare - UNSHARE: Release a share reservation + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm4_shareres NLMPROC4_UNSHARE(nlm4_shareargs) = 21; + * + * Permissible procedure status codes: + * %NLM4_GRANTED: The share reservation was released. + * %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + * + * The Linux NLM server implementation also returns: + * %NLM4_DENIED_NOLOCKS: A needed resource could not be allocated. + * %NLM4_STALE_FH: The request specified an invalid file handle. + * %NLM4_FBIG: The request specified a length or offset + * that exceeds the range supported by the + * server. + * %NLM4_FAILED: The request failed for an unspecified reason. + */ +static __be32 nlm4svc_proc_unshare(struct svc_rqst *rqstp) +{ + struct nlm4_shareargs_wrapper *argp = rqstp->rq_argp; + struct nlm4_shareres_wrapper *resp = rqstp->rq_resp; + struct nlm_lock *lock = &argp->lock; + struct nlm4_lock xdr_lock = { + .fh = argp->xdrgen.share.fh, + .oh = argp->xdrgen.share.oh, + .svid = LOCKD_SHARE_SVID, + }; + struct nlm_host *host = NULL; + struct nlm_file *file = NULL; + + resp->xdrgen.cookie = argp->xdrgen.cookie; + + resp->xdrgen.stat = nlm_lck_denied_grace_period; + if (locks_in_grace(SVC_NET(rqstp))) + goto out; + + resp->xdrgen.stat = nlm_lck_denied_nolocks; + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.share.caller_name, true); + if (!host) + goto out; + + resp->xdrgen.stat = nlm4svc_lookup_file(rqstp, host, lock, &file, + &xdr_lock, F_RDLCK); + if (resp->xdrgen.stat) + goto out; + + resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh); + + nlmsvc_release_lockowner(lock); + +out: + if (file) + nlm_release_file(file); + nlmsvc_release_host(host); + return resp->xdrgen.stat == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; +} + +/** + * nlm4svc_proc_nm_lock - NM_LOCK: Establish a non-monitored lock + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * %rpc_drop_reply: Do not send an RPC reply. + * + * RPC synopsis: + * nlm4_res NLMPROC4_NM_LOCK(nlm4_lockargs) = 22; + * + * Permissible procedure status codes: + * %NLM4_GRANTED: The requested lock was granted. + * %NLM4_DENIED: The requested lock conflicted with existing + * lock reservations for the file. + * %NLM4_DENIED_NOLOCKS: The server could not allocate the resources + * needed to process the request. + * %NLM4_BLOCKED: The blocking request cannot be granted + * immediately. The server will send an + * NLMPROC4_GRANTED callback to the client when + * the lock can be granted. + * %NLM4_DENIED_GRACE_PERIOD: The server has recently restarted and is + * re-establishing existing locks, and is not + * yet ready to accept normal service requests. + * + * The Linux NLM server implementation also returns: + * %NLM4_DEADLCK: The request could not be granted and + * blocking would cause a deadlock. + * %NLM4_STALE_FH: The request specified an invalid file handle. + * %NLM4_FBIG: The request specified a length or offset + * that exceeds the range supported by the + * server. + * %NLM4_FAILED: The request failed for an unspecified reason. + */ +static __be32 nlm4svc_proc_nm_lock(struct svc_rqst *rqstp) +{ + return nlm4svc_do_lock(rqstp, false); +} + +/** + * nlm4svc_proc_free_all - FREE_ALL: Discard client's lock and share state + * @rqstp: RPC transaction context + * + * Returns: + * %rpc_success: RPC executed successfully. + * + * RPC synopsis: + * void NLMPROC4_FREE_ALL(nlm4_notify) = 23; + */ +static __be32 nlm4svc_proc_free_all(struct svc_rqst *rqstp) +{ + struct nlm4_notify_wrapper *argp = rqstp->rq_argp; + struct nlm_host *host; + + host = nlm4svc_lookup_host(rqstp, argp->xdrgen.name, false); + if (!host) + goto out; + + nlmsvc_free_host_resources(host); + + nlmsvc_release_host(host); + +out: + return rpc_success; +} + /* - * NLM Server procedures. + * NLMv4 Server procedures. */ -struct nlm_void { int dummy; }; - -#define Ck (1+XDR_QUADLEN(NLM_MAXCOOKIELEN)) /* cookie */ -#define No (1+1024/4) /* netobj */ -#define St 1 /* status */ -#define Rg 4 /* range (offset + length) */ - -const struct svc_procedure nlmsvc_procedures4[24] = { - [NLMPROC_NULL] = { - .pc_func = nlm4svc_proc_null, - .pc_decode = nlm4svc_decode_void, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_void), - .pc_argzero = sizeof(struct nlm_void), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "NULL", +static const struct svc_procedure nlm4svc_procedures[24] = { + [NLMPROC4_NULL] = { + .pc_func = nlm4svc_proc_null, + .pc_decode = nlm4_svc_decode_void, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = XDR_void, + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "NULL", }, - [NLMPROC_TEST] = { - .pc_func = nlm4svc_proc_test, - .pc_decode = nlm4svc_decode_testargs, - .pc_encode = nlm4svc_encode_testres, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St+2+No+Rg, - .pc_name = "TEST", + [NLMPROC4_TEST] = { + .pc_func = nlm4svc_proc_test, + .pc_decode = nlm4_svc_decode_nlm4_testargs, + .pc_encode = nlm4_svc_encode_nlm4_testres, + .pc_argsize = sizeof(struct nlm4_testargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm4_testres_wrapper), + .pc_xdrressize = NLM4_nlm4_testres_sz, + .pc_name = "TEST", }, - [NLMPROC_LOCK] = { - .pc_func = nlm4svc_proc_lock, - .pc_decode = nlm4svc_decode_lockargs, - .pc_encode = nlm4svc_encode_res, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St, - .pc_name = "LOCK", + [NLMPROC4_LOCK] = { + .pc_func = nlm4svc_proc_lock, + .pc_decode = nlm4_svc_decode_nlm4_lockargs, + .pc_encode = nlm4_svc_encode_nlm4_res, + .pc_argsize = sizeof(struct nlm4_lockargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm4_res_wrapper), + .pc_xdrressize = NLM4_nlm4_res_sz, + .pc_name = "LOCK", }, - [NLMPROC_CANCEL] = { - .pc_func = nlm4svc_proc_cancel, - .pc_decode = nlm4svc_decode_cancargs, - .pc_encode = nlm4svc_encode_res, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St, - .pc_name = "CANCEL", + [NLMPROC4_CANCEL] = { + .pc_func = nlm4svc_proc_cancel, + .pc_decode = nlm4_svc_decode_nlm4_cancargs, + .pc_encode = nlm4_svc_encode_nlm4_res, + .pc_argsize = sizeof(struct nlm4_cancargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm4_res_wrapper), + .pc_xdrressize = NLM4_nlm4_res_sz, + .pc_name = "CANCEL", }, - [NLMPROC_UNLOCK] = { - .pc_func = nlm4svc_proc_unlock, - .pc_decode = nlm4svc_decode_unlockargs, - .pc_encode = nlm4svc_encode_res, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St, - .pc_name = "UNLOCK", + [NLMPROC4_UNLOCK] = { + .pc_func = nlm4svc_proc_unlock, + .pc_decode = nlm4_svc_decode_nlm4_unlockargs, + .pc_encode = nlm4_svc_encode_nlm4_res, + .pc_argsize = sizeof(struct nlm4_unlockargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm4_res_wrapper), + .pc_xdrressize = NLM4_nlm4_res_sz, + .pc_name = "UNLOCK", }, - [NLMPROC_GRANTED] = { - .pc_func = nlm4svc_proc_granted, - .pc_decode = nlm4svc_decode_testargs, - .pc_encode = nlm4svc_encode_res, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St, - .pc_name = "GRANTED", + [NLMPROC4_GRANTED] = { + .pc_func = nlm4svc_proc_granted, + .pc_decode = nlm4_svc_decode_nlm4_testargs, + .pc_encode = nlm4_svc_encode_nlm4_res, + .pc_argsize = sizeof(struct nlm4_testargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm4_res_wrapper), + .pc_xdrressize = NLM4_nlm4_res_sz, + .pc_name = "GRANTED", }, - [NLMPROC_TEST_MSG] = { - .pc_func = nlm4svc_proc_test_msg, - .pc_decode = nlm4svc_decode_testargs, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "TEST_MSG", + [NLMPROC4_TEST_MSG] = { + .pc_func = nlm4svc_proc_test_msg, + .pc_decode = nlm4_svc_decode_nlm4_testargs, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = sizeof(struct nlm4_testargs_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "TEST_MSG", }, - [NLMPROC_LOCK_MSG] = { - .pc_func = nlm4svc_proc_lock_msg, - .pc_decode = nlm4svc_decode_lockargs, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "LOCK_MSG", + [NLMPROC4_LOCK_MSG] = { + .pc_func = nlm4svc_proc_lock_msg, + .pc_decode = nlm4_svc_decode_nlm4_lockargs, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = sizeof(struct nlm4_lockargs_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "LOCK_MSG", }, - [NLMPROC_CANCEL_MSG] = { - .pc_func = nlm4svc_proc_cancel_msg, - .pc_decode = nlm4svc_decode_cancargs, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "CANCEL_MSG", + [NLMPROC4_CANCEL_MSG] = { + .pc_func = nlm4svc_proc_cancel_msg, + .pc_decode = nlm4_svc_decode_nlm4_cancargs, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = sizeof(struct nlm4_cancargs_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "CANCEL_MSG", }, - [NLMPROC_UNLOCK_MSG] = { - .pc_func = nlm4svc_proc_unlock_msg, - .pc_decode = nlm4svc_decode_unlockargs, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "UNLOCK_MSG", + [NLMPROC4_UNLOCK_MSG] = { + .pc_func = nlm4svc_proc_unlock_msg, + .pc_decode = nlm4_svc_decode_nlm4_unlockargs, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = sizeof(struct nlm4_unlockargs_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "UNLOCK_MSG", }, - [NLMPROC_GRANTED_MSG] = { - .pc_func = nlm4svc_proc_granted_msg, - .pc_decode = nlm4svc_decode_testargs, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "GRANTED_MSG", + [NLMPROC4_GRANTED_MSG] = { + .pc_func = nlm4svc_proc_granted_msg, + .pc_decode = nlm4_svc_decode_nlm4_testargs, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = sizeof(struct nlm4_testargs_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "GRANTED_MSG", }, - [NLMPROC_TEST_RES] = { - .pc_func = nlm4svc_proc_null, - .pc_decode = nlm4svc_decode_void, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_res), - .pc_argzero = sizeof(struct nlm_res), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "TEST_RES", + [NLMPROC4_TEST_RES] = { + .pc_func = nlm4svc_proc_null, + .pc_decode = nlm4_svc_decode_nlm4_testres, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = sizeof(struct nlm4_testres), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "TEST_RES", }, - [NLMPROC_LOCK_RES] = { - .pc_func = nlm4svc_proc_null, - .pc_decode = nlm4svc_decode_void, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_res), - .pc_argzero = sizeof(struct nlm_res), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "LOCK_RES", + [NLMPROC4_LOCK_RES] = { + .pc_func = nlm4svc_proc_null, + .pc_decode = nlm4_svc_decode_nlm4_res, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = sizeof(struct nlm4_res), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "LOCK_RES", }, - [NLMPROC_CANCEL_RES] = { - .pc_func = nlm4svc_proc_null, - .pc_decode = nlm4svc_decode_void, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_res), - .pc_argzero = sizeof(struct nlm_res), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "CANCEL_RES", + [NLMPROC4_CANCEL_RES] = { + .pc_func = nlm4svc_proc_null, + .pc_decode = nlm4_svc_decode_nlm4_res, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = sizeof(struct nlm4_res), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "CANCEL_RES", }, - [NLMPROC_UNLOCK_RES] = { - .pc_func = nlm4svc_proc_null, - .pc_decode = nlm4svc_decode_void, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_res), - .pc_argzero = sizeof(struct nlm_res), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "UNLOCK_RES", + [NLMPROC4_UNLOCK_RES] = { + .pc_func = nlm4svc_proc_null, + .pc_decode = nlm4_svc_decode_nlm4_res, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = sizeof(struct nlm4_res), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "UNLOCK_RES", }, - [NLMPROC_GRANTED_RES] = { - .pc_func = nlm4svc_proc_granted_res, - .pc_decode = nlm4svc_decode_res, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_res), - .pc_argzero = sizeof(struct nlm_res), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "GRANTED_RES", + [NLMPROC4_GRANTED_RES] = { + .pc_func = nlm4svc_proc_granted_res, + .pc_decode = nlm4_svc_decode_nlm4_res, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = sizeof(struct nlm4_res_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "GRANTED_RES", }, - [NLMPROC_NSM_NOTIFY] = { - .pc_func = nlm4svc_proc_sm_notify, - .pc_decode = nlm4svc_decode_reboot, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_reboot), - .pc_argzero = sizeof(struct nlm_reboot), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "SM_NOTIFY", + [NLMPROC4_SM_NOTIFY] = { + .pc_func = nlm4svc_proc_sm_notify, + .pc_decode = nlm4_svc_decode_nlm4_notifyargs, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = sizeof(struct nlm4_notifyargs_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "SM_NOTIFY", }, [17] = { - .pc_func = nlm4svc_proc_unused, - .pc_decode = nlm4svc_decode_void, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_void), - .pc_argzero = sizeof(struct nlm_void), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = 0, - .pc_name = "UNUSED", + .pc_func = nlm4svc_proc_unused, + .pc_decode = nlm4_svc_decode_void, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = 0, + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "UNUSED", }, [18] = { - .pc_func = nlm4svc_proc_unused, - .pc_decode = nlm4svc_decode_void, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_void), - .pc_argzero = sizeof(struct nlm_void), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = 0, - .pc_name = "UNUSED", + .pc_func = nlm4svc_proc_unused, + .pc_decode = nlm4_svc_decode_void, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = 0, + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "UNUSED", }, [19] = { - .pc_func = nlm4svc_proc_unused, - .pc_decode = nlm4svc_decode_void, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_void), - .pc_argzero = sizeof(struct nlm_void), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = 0, - .pc_name = "UNUSED", + .pc_func = nlm4svc_proc_unused, + .pc_decode = nlm4_svc_decode_void, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = 0, + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "UNUSED", }, - [NLMPROC_SHARE] = { - .pc_func = nlm4svc_proc_share, - .pc_decode = nlm4svc_decode_shareargs, - .pc_encode = nlm4svc_encode_shareres, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St+1, - .pc_name = "SHARE", + [NLMPROC4_SHARE] = { + .pc_func = nlm4svc_proc_share, + .pc_decode = nlm4_svc_decode_nlm4_shareargs, + .pc_encode = nlm4_svc_encode_nlm4_shareres, + .pc_argsize = sizeof(struct nlm4_shareargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm4_shareres_wrapper), + .pc_xdrressize = NLM4_nlm4_shareres_sz, + .pc_name = "SHARE", }, - [NLMPROC_UNSHARE] = { - .pc_func = nlm4svc_proc_unshare, - .pc_decode = nlm4svc_decode_shareargs, - .pc_encode = nlm4svc_encode_shareres, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St+1, - .pc_name = "UNSHARE", + [NLMPROC4_UNSHARE] = { + .pc_func = nlm4svc_proc_unshare, + .pc_decode = nlm4_svc_decode_nlm4_shareargs, + .pc_encode = nlm4_svc_encode_nlm4_shareres, + .pc_argsize = sizeof(struct nlm4_shareargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm4_shareres_wrapper), + .pc_xdrressize = NLM4_nlm4_shareres_sz, + .pc_name = "UNSHARE", }, - [NLMPROC_NM_LOCK] = { - .pc_func = nlm4svc_proc_nm_lock, - .pc_decode = nlm4svc_decode_lockargs, - .pc_encode = nlm4svc_encode_res, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_res), - .pc_xdrressize = Ck+St, - .pc_name = "NM_LOCK", + [NLMPROC4_NM_LOCK] = { + .pc_func = nlm4svc_proc_nm_lock, + .pc_decode = nlm4_svc_decode_nlm4_lockargs, + .pc_encode = nlm4_svc_encode_nlm4_res, + .pc_argsize = sizeof(struct nlm4_lockargs_wrapper), + .pc_argzero = 0, + .pc_ressize = sizeof(struct nlm4_res_wrapper), + .pc_xdrressize = NLM4_nlm4_res_sz, + .pc_name = "NM_LOCK", }, - [NLMPROC_FREE_ALL] = { - .pc_func = nlm4svc_proc_free_all, - .pc_decode = nlm4svc_decode_notify, - .pc_encode = nlm4svc_encode_void, - .pc_argsize = sizeof(struct nlm_args), - .pc_argzero = sizeof(struct nlm_args), - .pc_ressize = sizeof(struct nlm_void), - .pc_xdrressize = St, - .pc_name = "FREE_ALL", + [NLMPROC4_FREE_ALL] = { + .pc_func = nlm4svc_proc_free_all, + .pc_decode = nlm4_svc_decode_nlm4_notify, + .pc_encode = nlm4_svc_encode_void, + .pc_argsize = sizeof(struct nlm4_notify_wrapper), + .pc_argzero = 0, + .pc_ressize = 0, + .pc_xdrressize = XDR_void, + .pc_name = "FREE_ALL", }, }; + +/* + * Storage requirements for XDR arguments and results + */ +union nlm4svc_xdrstore { + struct nlm4_testargs_wrapper testargs; + struct nlm4_lockargs_wrapper lockargs; + struct nlm4_cancargs_wrapper cancargs; + struct nlm4_unlockargs_wrapper unlockargs; + struct nlm4_notifyargs_wrapper notifyargs; + struct nlm4_shareargs_wrapper shareargs; + struct nlm4_notify_wrapper notify; + struct nlm4_testres_wrapper testres; + struct nlm4_res_wrapper res; + struct nlm4_shareres_wrapper shareres; +}; + +static DEFINE_PER_CPU_ALIGNED(unsigned long, + nlm4svc_call_counters[ARRAY_SIZE(nlm4svc_procedures)]); + +const struct svc_version nlmsvc_version4 = { + .vs_vers = 4, + .vs_nproc = ARRAY_SIZE(nlm4svc_procedures), + .vs_proc = nlm4svc_procedures, + .vs_count = nlm4svc_call_counters, + .vs_dispatch = nlmsvc_dispatch, + .vs_xdrsize = sizeof(union nlm4svc_xdrstore), +}; diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 0b6be8b8aeb1..b98b1d0ada35 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -28,17 +28,11 @@ #include #include #include -#include -#include + +#include "lockd.h" #define NLMDBG_FACILITY NLMDBG_SVCLOCK -#ifdef CONFIG_LOCKD_V4 -#define nlm_deadlock nlm4_deadlock -#else -#define nlm_deadlock nlm_lck_denied -#endif - static void nlmsvc_release_block(struct nlm_block *block); static void nlmsvc_insert_block(struct nlm_block *block, unsigned long); static void nlmsvc_remove_block(struct nlm_block *block); @@ -80,6 +74,11 @@ static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie) return buf; } +#else +static inline const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie) +{ + return "???"; +} #endif /* @@ -463,7 +462,7 @@ nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block) block->b_deferred_req = rqstp->rq_chandle.defer(block->b_cache_req); if (block->b_deferred_req != NULL) - status = nlm_drop_reply; + status = nlm__int__drop_reply; } dprintk("lockd: nlmsvc_defer_lock_rqst block %p flags %d status %d\n", block, block->b_flags, ntohl(status)); @@ -531,7 +530,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, ret = nlm_lck_denied; goto out; } - ret = nlm_drop_reply; + ret = nlm__int__drop_reply; goto out; } @@ -589,7 +588,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, goto out; case -EDEADLK: nlmsvc_remove_block(block); - ret = nlm_deadlock; + ret = nlm__int__deadlock; goto out; default: /* includes ENOLCK */ nlmsvc_remove_block(block); diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 5817ef272332..749abf8886ba 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -10,39 +10,52 @@ #include #include -#include -#include #include +#include "lockd.h" +#include "share.h" + #define NLMDBG_FACILITY NLMDBG_CLIENT #ifdef CONFIG_LOCKD_V4 -static __be32 -cast_to_nlm(__be32 status, u32 vers) +static inline __be32 cast_status(__be32 status) { - /* Note: status is assumed to be in network byte order !!! */ - if (vers != 4){ - switch (status) { - case nlm_granted: - case nlm_lck_denied: - case nlm_lck_denied_nolocks: - case nlm_lck_blocked: - case nlm_lck_denied_grace_period: - case nlm_drop_reply: - break; - case nlm4_deadlock: - status = nlm_lck_denied; - break; - default: - status = nlm_lck_denied_nolocks; - } + switch (status) { + case nlm_granted: + case nlm_lck_denied: + case nlm_lck_denied_nolocks: + case nlm_lck_blocked: + case nlm_lck_denied_grace_period: + case nlm__int__drop_reply: + break; + case nlm__int__deadlock: + status = nlm_lck_denied; + break; + default: + status = nlm_lck_denied_nolocks; } - return (status); + return status; } -#define cast_status(status) (cast_to_nlm(status, rqstp->rq_vers)) #else -#define cast_status(status) (status) +static inline __be32 cast_status(__be32 status) +{ + switch (status) { + case nlm__int__deadlock: + status = nlm_lck_denied; + break; + case nlm__int__stale_fh: + case nlm__int__failed: + status = nlm_lck_denied_nolocks; + break; + default: + if (be32_to_cpu(status) >= 30000) + pr_warn_once("lockd: unhandled internal status %u\n", + be32_to_cpu(status)); + break; + } + return status; +} #endif /* @@ -124,12 +137,13 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp) /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; + return resp->status == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; /* Now check for conflicting locks */ resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock)); - if (resp->status == nlm_drop_reply) + if (resp->status == nlm__int__drop_reply) rc = rpc_drop_reply; else dprintk("lockd: TEST status %d vers %d\n", @@ -161,13 +175,14 @@ __nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp) /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; + return resp->status == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; /* Now try to lock the file */ resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock, argp->block, &argp->cookie, argp->reclaim)); - if (resp->status == nlm_drop_reply) + if (resp->status == nlm__int__drop_reply) rc = rpc_drop_reply; else dprintk("lockd: LOCK status %d\n", ntohl(resp->status)); @@ -204,7 +219,8 @@ __nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_res *resp) /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; + return resp->status == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; /* Try to cancel request. */ resp->status = cast_status(nlmsvc_cancel_blocked(net, file, &argp->lock)); @@ -245,7 +261,8 @@ __nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_res *resp) /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; + return resp->status == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; /* Now try to remove the lock */ resp->status = cast_status(nlmsvc_unlock(net, file, &argp->lock)); @@ -402,10 +419,13 @@ nlmsvc_proc_share(struct svc_rqst *rqstp) /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; + return resp->status == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; /* Now try to create the share */ - resp->status = cast_status(nlmsvc_share_file(host, file, argp)); + resp->status = cast_status(nlmsvc_share_file(host, file, &argp->lock.oh, + argp->fsm_access, + argp->fsm_mode)); dprintk("lockd: SHARE status %d\n", ntohl(resp->status)); nlmsvc_release_lockowner(&argp->lock); @@ -437,10 +457,12 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp) /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; + return resp->status == nlm__int__drop_reply ? + rpc_drop_reply : rpc_success; /* Now try to unshare the file */ - resp->status = cast_status(nlmsvc_unshare_file(host, file, argp)); + resp->status = cast_status(nlmsvc_unshare_file(host, file, + &argp->lock.oh)); dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status)); nlmsvc_release_lockowner(&argp->lock); @@ -536,7 +558,7 @@ struct nlm_void { int dummy; }; #define No (1+1024/4) /* Net Obj */ #define Rg 2 /* range - offset + size */ -const struct svc_procedure nlmsvc_procedures[24] = { +static const struct svc_procedure nlmsvc_procedures[24] = { [NLMPROC_NULL] = { .pc_func = nlmsvc_proc_null, .pc_decode = nlmsvc_decode_void, @@ -778,3 +800,39 @@ const struct svc_procedure nlmsvc_procedures[24] = { .pc_name = "FREE_ALL", }, }; + +/* + * Storage requirements for XDR arguments and results + */ +union nlmsvc_xdrstore { + struct nlm_args args; + struct nlm_res res; + struct nlm_reboot reboot; +}; + +/* + * NLMv1 defines only procedures 1 - 15. Linux lockd also implements + * procedures 0 (NULL) and 16 (SM_NOTIFY). + */ +static DEFINE_PER_CPU_ALIGNED(unsigned long, nlm1svc_call_counters[17]); + +const struct svc_version nlmsvc_version1 = { + .vs_vers = 1, + .vs_nproc = 17, + .vs_proc = nlmsvc_procedures, + .vs_count = nlm1svc_call_counters, + .vs_dispatch = nlmsvc_dispatch, + .vs_xdrsize = sizeof(union nlmsvc_xdrstore), +}; + +static DEFINE_PER_CPU_ALIGNED(unsigned long, + nlm3svc_call_counters[ARRAY_SIZE(nlmsvc_procedures)]); + +const struct svc_version nlmsvc_version3 = { + .vs_vers = 3, + .vs_nproc = ARRAY_SIZE(nlmsvc_procedures), + .vs_proc = nlmsvc_procedures, + .vs_count = nlm3svc_call_counters, + .vs_dispatch = nlmsvc_dispatch, + .vs_xdrsize = sizeof(union nlmsvc_xdrstore), +}; diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c index 88c81ce1148d..53f5655c128c 100644 --- a/fs/lockd/svcshare.c +++ b/fs/lockd/svcshare.c @@ -14,8 +14,9 @@ #include #include -#include -#include + +#include "lockd.h" +#include "share.h" static inline int nlm_cmp_owner(struct nlm_share *share, struct xdr_netobj *oh) @@ -24,12 +25,21 @@ nlm_cmp_owner(struct nlm_share *share, struct xdr_netobj *oh) && !memcmp(share->s_owner.data, oh->data, oh->len); } +/** + * nlmsvc_share_file - create a share + * @host: Network client peer + * @file: File to be shared + * @oh: Share owner handle + * @access: Requested access mode + * @mode: Requested file sharing mode + * + * Returns an NLM status code. + */ __be32 nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file, - struct nlm_args *argp) + struct xdr_netobj *oh, u32 access, u32 mode) { struct nlm_share *share; - struct xdr_netobj *oh = &argp->lock.oh; u8 *ohdata; if (nlmsvc_file_cannot_lock(file)) @@ -38,13 +48,11 @@ nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file, for (share = file->f_shares; share; share = share->s_next) { if (share->s_host == host && nlm_cmp_owner(share, oh)) goto update; - if ((argp->fsm_access & share->s_mode) - || (argp->fsm_mode & share->s_access )) + if ((access & share->s_mode) || (mode & share->s_access)) return nlm_lck_denied; } - share = kmalloc(sizeof(*share) + oh->len, - GFP_KERNEL); + share = kmalloc(sizeof(*share) + oh->len, GFP_KERNEL); if (share == NULL) return nlm_lck_denied_nolocks; @@ -60,20 +68,24 @@ nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file, file->f_shares = share; update: - share->s_access = argp->fsm_access; - share->s_mode = argp->fsm_mode; + share->s_access = access; + share->s_mode = mode; return nlm_granted; } -/* - * Delete a share. +/** + * nlmsvc_unshare_file - delete a share + * @host: Network client peer + * @file: File to be unshared + * @oh: Share owner handle + * + * Returns an NLM status code. */ __be32 nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file, - struct nlm_args *argp) + struct xdr_netobj *oh) { struct nlm_share *share, **shpp; - struct xdr_netobj *oh = &argp->lock.oh; if (nlmsvc_file_cannot_lock(file)) return nlm_lck_denied_nolocks; diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 79f3dd2fd366..344e6c187cde 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -15,12 +15,13 @@ #include #include #include -#include -#include #include #include #include +#include "lockd.h" +#include "share.h" + #define NLMDBG_FACILITY NLMDBG_SVCSUBS @@ -87,14 +88,29 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp, struct nlm_file *file, int mode) { struct file **fp = &file->f_file[mode]; - __be32 nfserr; + __be32 nlmerr = nlm_granted; + int error; if (*fp) - return 0; - nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode); - if (nfserr) - dprintk("lockd: open failed (error %d)\n", nfserr); - return nfserr; + return nlmerr; + + error = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode); + if (error) { + dprintk("lockd: open failed (errno %d)\n", error); + switch (error) { + case -EWOULDBLOCK: + nlmerr = nlm__int__drop_reply; + break; + case -ESTALE: + nlmerr = nlm__int__stale_fh; + break; + default: + nlmerr = nlm__int__failed; + break; + } + } + + return nlmerr; } /* diff --git a/fs/lockd/trace.h b/fs/lockd/trace.h index 7461b13b6e74..7214d7e96a42 100644 --- a/fs/lockd/trace.h +++ b/fs/lockd/trace.h @@ -8,7 +8,8 @@ #include #include #include -#include + +#include "lockd.h" #ifdef CONFIG_LOCKD_V4 #define NLM_STATUS_LIST \ diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index adfcce2bf11b..dfca8b8dab73 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -15,13 +15,13 @@ #include #include #include -#include #include +#include "lockd.h" +#include "share.h" #include "svcxdr.h" - static inline loff_t s32_to_loff_t(__s32 offset) { @@ -275,7 +275,7 @@ nlmsvc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) memset(lock, 0, sizeof(*lock)); locks_init_lock(&lock->fl); - lock->svid = ~(u32)0; + lock->svid = LOCKD_SHARE_SVID; if (!svcxdr_decode_cookie(xdr, &argp->cookie)) return false; diff --git a/include/linux/lockd/xdr.h b/fs/lockd/xdr.h similarity index 91% rename from include/linux/lockd/xdr.h rename to fs/lockd/xdr.h index 17d53165d9f2..3c60817c4349 100644 --- a/include/linux/lockd/xdr.h +++ b/fs/lockd/xdr.h @@ -1,14 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * linux/include/linux/lockd/xdr.h - * * XDR types for the NLM protocol * * Copyright (C) 1996 Olaf Kirch */ -#ifndef LOCKD_XDR_H -#define LOCKD_XDR_H +#ifndef _LOCKD_XDR_H +#define _LOCKD_XDR_H #include #include @@ -33,8 +31,6 @@ struct svc_rqst; #define nlm_lck_blocked cpu_to_be32(NLM_LCK_BLOCKED) #define nlm_lck_denied_grace_period cpu_to_be32(NLM_LCK_DENIED_GRACE_PERIOD) -#define nlm_drop_reply cpu_to_be32(30000) - /* Lock info passed via NLM */ struct nlm_lock { char * caller; @@ -92,11 +88,6 @@ struct nlm_reboot { struct nsm_private priv; }; -/* - * Contents of statd callback when monitored host rebooted - */ -#define NLMSVC_XDRSIZE sizeof(struct nlm_args) - bool nlmsvc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr); bool nlmsvc_decode_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); bool nlmsvc_decode_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); @@ -112,4 +103,4 @@ bool nlmsvc_encode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr); bool nlmsvc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr); bool nlmsvc_encode_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr); -#endif /* LOCKD_XDR_H */ +#endif /* _LOCKD_XDR_H */ diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c deleted file mode 100644 index e343c820301f..000000000000 --- a/fs/lockd/xdr4.c +++ /dev/null @@ -1,347 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * linux/fs/lockd/xdr4.c - * - * XDR support for lockd and the lock client. - * - * Copyright (C) 1995, 1996 Olaf Kirch - * Copyright (C) 1999, Trond Myklebust - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "svcxdr.h" - -static inline s64 -loff_t_to_s64(loff_t offset) -{ - s64 res; - if (offset > NLM4_OFFSET_MAX) - res = NLM4_OFFSET_MAX; - else if (offset < -NLM4_OFFSET_MAX) - res = -NLM4_OFFSET_MAX; - else - res = offset; - return res; -} - -void nlm4svc_set_file_lock_range(struct file_lock *fl, u64 off, u64 len) -{ - s64 end = off + len - 1; - - fl->fl_start = off; - if (len == 0 || end < 0) - fl->fl_end = OFFSET_MAX; - else - fl->fl_end = end; -} - -/* - * NLM file handles are defined by specification to be a variable-length - * XDR opaque no longer than 1024 bytes. However, this implementation - * limits their length to the size of an NFSv3 file handle. - */ -static bool -svcxdr_decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh) -{ - __be32 *p; - u32 len; - - if (xdr_stream_decode_u32(xdr, &len) < 0) - return false; - if (len > NFS_MAXFHSIZE) - return false; - - p = xdr_inline_decode(xdr, len); - if (!p) - return false; - fh->size = len; - memcpy(fh->data, p, len); - memset(fh->data + len, 0, sizeof(fh->data) - len); - - return true; -} - -static bool -svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock) -{ - struct file_lock *fl = &lock->fl; - - if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) - return false; - if (!svcxdr_decode_fhandle(xdr, &lock->fh)) - return false; - if (!svcxdr_decode_owner(xdr, &lock->oh)) - return false; - if (xdr_stream_decode_u32(xdr, &lock->svid) < 0) - return false; - if (xdr_stream_decode_u64(xdr, &lock->lock_start) < 0) - return false; - if (xdr_stream_decode_u64(xdr, &lock->lock_len) < 0) - return false; - - locks_init_lock(fl); - fl->c.flc_type = F_RDLCK; - nlm4svc_set_file_lock_range(fl, lock->lock_start, lock->lock_len); - return true; -} - -static bool -svcxdr_encode_holder(struct xdr_stream *xdr, const struct nlm_lock *lock) -{ - const struct file_lock *fl = &lock->fl; - s64 start, len; - - /* exclusive */ - if (xdr_stream_encode_bool(xdr, fl->c.flc_type != F_RDLCK) < 0) - return false; - if (xdr_stream_encode_u32(xdr, lock->svid) < 0) - return false; - if (!svcxdr_encode_owner(xdr, &lock->oh)) - return false; - start = loff_t_to_s64(fl->fl_start); - if (fl->fl_end == OFFSET_MAX) - len = 0; - else - len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1); - if (xdr_stream_encode_u64(xdr, start) < 0) - return false; - if (xdr_stream_encode_u64(xdr, len) < 0) - return false; - - return true; -} - -static bool -svcxdr_encode_testrply(struct xdr_stream *xdr, const struct nlm_res *resp) -{ - if (!svcxdr_encode_stats(xdr, resp->status)) - return false; - switch (resp->status) { - case nlm_lck_denied: - if (!svcxdr_encode_holder(xdr, &resp->lock)) - return false; - } - - return true; -} - - -/* - * Decode Call arguments - */ - -bool -nlm4svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - return true; -} - -bool -nlm4svc_decode_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_args *argp = rqstp->rq_argp; - u32 exclusive; - - if (!svcxdr_decode_cookie(xdr, &argp->cookie)) - return false; - if (xdr_stream_decode_bool(xdr, &exclusive) < 0) - return false; - if (!svcxdr_decode_lock(xdr, &argp->lock)) - return false; - if (exclusive) - argp->lock.fl.c.flc_type = F_WRLCK; - - return true; -} - -bool -nlm4svc_decode_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_args *argp = rqstp->rq_argp; - u32 exclusive; - - if (!svcxdr_decode_cookie(xdr, &argp->cookie)) - return false; - if (xdr_stream_decode_bool(xdr, &argp->block) < 0) - return false; - if (xdr_stream_decode_bool(xdr, &exclusive) < 0) - return false; - if (!svcxdr_decode_lock(xdr, &argp->lock)) - return false; - if (exclusive) - argp->lock.fl.c.flc_type = F_WRLCK; - if (xdr_stream_decode_bool(xdr, &argp->reclaim) < 0) - return false; - if (xdr_stream_decode_u32(xdr, &argp->state) < 0) - return false; - argp->monitor = 1; /* monitor client by default */ - - return true; -} - -bool -nlm4svc_decode_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_args *argp = rqstp->rq_argp; - u32 exclusive; - - if (!svcxdr_decode_cookie(xdr, &argp->cookie)) - return false; - if (xdr_stream_decode_bool(xdr, &argp->block) < 0) - return false; - if (xdr_stream_decode_bool(xdr, &exclusive) < 0) - return false; - if (!svcxdr_decode_lock(xdr, &argp->lock)) - return false; - if (exclusive) - argp->lock.fl.c.flc_type = F_WRLCK; - - return true; -} - -bool -nlm4svc_decode_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_args *argp = rqstp->rq_argp; - - if (!svcxdr_decode_cookie(xdr, &argp->cookie)) - return false; - if (!svcxdr_decode_lock(xdr, &argp->lock)) - return false; - argp->lock.fl.c.flc_type = F_UNLCK; - - return true; -} - -bool -nlm4svc_decode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_res *resp = rqstp->rq_argp; - - if (!svcxdr_decode_cookie(xdr, &resp->cookie)) - return false; - if (!svcxdr_decode_stats(xdr, &resp->status)) - return false; - - return true; -} - -bool -nlm4svc_decode_reboot(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_reboot *argp = rqstp->rq_argp; - __be32 *p; - u32 len; - - if (xdr_stream_decode_u32(xdr, &len) < 0) - return false; - if (len > SM_MAXSTRLEN) - return false; - p = xdr_inline_decode(xdr, len); - if (!p) - return false; - argp->len = len; - argp->mon = (char *)p; - if (xdr_stream_decode_u32(xdr, &argp->state) < 0) - return false; - p = xdr_inline_decode(xdr, SM_PRIV_SIZE); - if (!p) - return false; - memcpy(&argp->priv.data, p, sizeof(argp->priv.data)); - - return true; -} - -bool -nlm4svc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_lock *lock = &argp->lock; - - locks_init_lock(&lock->fl); - lock->svid = ~(u32)0; - - if (!svcxdr_decode_cookie(xdr, &argp->cookie)) - return false; - if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) - return false; - if (!svcxdr_decode_fhandle(xdr, &lock->fh)) - return false; - if (!svcxdr_decode_owner(xdr, &lock->oh)) - return false; - /* XXX: Range checks are missing in the original code */ - if (xdr_stream_decode_u32(xdr, &argp->fsm_mode) < 0) - return false; - if (xdr_stream_decode_u32(xdr, &argp->fsm_access) < 0) - return false; - - return true; -} - -bool -nlm4svc_decode_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_args *argp = rqstp->rq_argp; - struct nlm_lock *lock = &argp->lock; - - if (!svcxdr_decode_string(xdr, &lock->caller, &lock->len)) - return false; - if (xdr_stream_decode_u32(xdr, &argp->state) < 0) - return false; - - return true; -} - - -/* - * Encode Reply results - */ - -bool -nlm4svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - return true; -} - -bool -nlm4svc_encode_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_res *resp = rqstp->rq_resp; - - return svcxdr_encode_cookie(xdr, &resp->cookie) && - svcxdr_encode_testrply(xdr, resp); -} - -bool -nlm4svc_encode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_res *resp = rqstp->rq_resp; - - return svcxdr_encode_cookie(xdr, &resp->cookie) && - svcxdr_encode_stats(xdr, resp->status); -} - -bool -nlm4svc_encode_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr) -{ - struct nlm_res *resp = rqstp->rq_resp; - - if (!svcxdr_encode_cookie(xdr, &resp->cookie)) - return false; - if (!svcxdr_encode_stats(xdr, resp->status)) - return false; - /* sequence */ - if (xdr_stream_encode_u32(xdr, 0) < 0) - return false; - - return true; -} diff --git a/fs/locks.c b/fs/locks.c index d8b066fb4210..fead53474c30 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1534,6 +1534,7 @@ static void time_out_leases(struct inode *inode, struct list_head *dispose) { struct file_lock_context *ctx = inode->i_flctx; struct file_lease *fl, *tmp; + bool remove; lockdep_assert_held(&ctx->flc_lock); @@ -1541,8 +1542,19 @@ static void time_out_leases(struct inode *inode, struct list_head *dispose) trace_time_out_leases(inode, fl); if (past_time(fl->fl_downgrade_time)) lease_modify(fl, F_RDLCK, dispose); - if (past_time(fl->fl_break_time)) - lease_modify(fl, F_UNLCK, dispose); + + remove = true; + if (past_time(fl->fl_break_time)) { + /* + * Consult the lease manager when a lease break times + * out to determine whether the lease should be disposed + * of. + */ + if (fl->fl_lmops && fl->fl_lmops->lm_breaker_timedout) + remove = fl->fl_lmops->lm_breaker_timedout(fl); + if (remove) + lease_modify(fl, F_UNLCK, dispose); + } } } @@ -1670,9 +1682,13 @@ int __break_lease(struct inode *inode, unsigned int flags) restart: fl = list_first_entry(&ctx->flc_lease, struct file_lease, c.flc_list); break_time = fl->fl_break_time; - if (break_time != 0) - break_time -= jiffies; - if (break_time == 0) + if (break_time != 0) { + if (time_after(jiffies, break_time)) { + fl->fl_break_time = jiffies + lease_break_time * HZ; + break_time = lease_break_time * HZ; + } else + break_time -= jiffies; + } else break_time++; locks_insert_block(&fl->c, &new_fl->c, leases_conflict); trace_break_lease_block(inode, new_fl); diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 11f9f69cde61..d54a141a89b3 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -380,14 +380,13 @@ bl_write_pagelist(struct nfs_pgio_header *header, int sync) sector_t isect, extent_length = 0; struct parallel_io *par = NULL; loff_t offset = header->args.offset; - size_t count = header->args.count; struct page **pages = header->args.pages; int pg_index = header->args.pgbase >> PAGE_SHIFT; unsigned int pg_len; struct blk_plug plug; int i; - dprintk("%s enter, %zu@%lld\n", __func__, count, offset); + dprintk("%s enter, %u@%lld\n", __func__, header->args.count, offset); /* At this point, header->page_aray is a (sequential) list of nfs_pages. * We want to write each, and if there is an error set pnfs_error @@ -428,7 +427,6 @@ bl_write_pagelist(struct nfs_pgio_header *header, int sync) } offset += pg_len; - count -= pg_len; isect += (pg_len >> SECTOR_SHIFT); extent_length -= (pg_len >> SECTOR_SHIFT); } diff --git a/fs/nfs/blocklayout/dev.c b/fs/nfs/blocklayout/dev.c index cc6327d97a91..bb35f88501ce 100644 --- a/fs/nfs/blocklayout/dev.c +++ b/fs/nfs/blocklayout/dev.c @@ -370,11 +370,14 @@ bl_open_path(struct pnfs_block_volume *v, const char *prefix) if (!devname) return ERR_PTR(-ENOMEM); - bdev_file = bdev_file_open_by_path(devname, BLK_OPEN_READ | BLK_OPEN_WRITE, - NULL, NULL); + bdev_file = bdev_file_open_by_path(devname, + BLK_OPEN_READ | BLK_OPEN_WRITE, NULL, NULL); if (IS_ERR(bdev_file)) { dprintk("failed to open device %s (%ld)\n", devname, PTR_ERR(bdev_file)); + } else { + pr_info("pNFS: using block device %s\n", + file_bdev(bdev_file)->bd_disk->disk_name); } kfree(devname); diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 176873f45677..4382baddc9ee 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -96,8 +96,7 @@ static __be32 decode_fh(struct xdr_stream *xdr, struct nfs_fh *fh) p = xdr_inline_decode(xdr, fh->size); if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); - memcpy(&fh->data[0], p, fh->size); - memset(&fh->data[fh->size], 0, sizeof(fh->data) - fh->size); + memcpy_and_pad(fh->data, sizeof(fh->data), p, fh->size, 0); return 0; } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index c1e629fbd131..e9ce1883288c 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1470,7 +1470,7 @@ static void nfs_clear_verifier_file(struct inode *inode) struct dentry *alias; struct inode *dir; - hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) { + for_each_alias(alias, inode) { spin_lock(&alias->d_lock); dir = d_inode_rcu(alias->d_parent); if (!dir || @@ -1489,7 +1489,7 @@ static void nfs_clear_verifier_directory(struct inode *dir) if (hlist_empty(&dir->i_dentry)) return; this_parent = - hlist_entry(dir->i_dentry.first, struct dentry, d_u.d_alias); + hlist_entry(dir->i_dentry.first, struct dentry, d_alias); spin_lock(&this_parent->d_lock); nfs_unset_verifier_delegated(&this_parent->d_time); diff --git a/fs/nfs/flexfilelayout/flexfilelayoutdev.c b/fs/nfs/flexfilelayout/flexfilelayoutdev.c index c40395ae0814..1109462a9699 100644 --- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c +++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c @@ -97,6 +97,11 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev, if (unlikely(!p)) goto out_err_drain_dsaddrs; version_count = be32_to_cpup(p); + + if (version_count == 0) { + ret = -EINVAL; + goto out_err_drain_dsaddrs; + } dprintk("%s: version count %d\n", __func__, version_count); ds_versions = kzalloc_objs(struct nfs4_ff_ds_version, version_count, diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c index f13d25d95b85..eef0736beb67 100644 --- a/fs/nfs/getroot.c +++ b/fs/nfs/getroot.c @@ -54,7 +54,7 @@ static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *i */ spin_lock(&d_inode(sb->s_root)->i_lock); spin_lock(&sb->s_root->d_lock); - hlist_del_init(&sb->s_root->d_u.d_alias); + hlist_del_init(&sb->s_root->d_alias); spin_unlock(&sb->s_root->d_lock); spin_unlock(&d_inode(sb->s_root)->i_lock); } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 98a8f0de1199..e26030e73696 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -692,7 +692,8 @@ void nfs_update_delegated_atime(struct inode *inode) void nfs_update_delegated_mtime_locked(struct inode *inode) { - if (nfs_have_delegated_mtime(inode)) + if (nfs_have_delegated_mtime(inode) || + nfs_have_directory_delegation(inode)) nfs_update_mtime(inode); } @@ -757,14 +758,7 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, } else if (nfs_have_delegated_atime(inode) && attr->ia_valid & ATTR_ATIME && !(attr->ia_valid & ATTR_MTIME)) { - if (attr->ia_valid & ATTR_ATIME_SET) { - if (uid_eq(task_uid, owner_uid)) { - spin_lock(&inode->i_lock); - nfs_set_timestamps_to_ts(inode, attr); - spin_unlock(&inode->i_lock); - attr->ia_valid &= ~(ATTR_ATIME|ATTR_ATIME_SET); - } - } else { + if (!(attr->ia_valid & ATTR_ATIME_SET)) { nfs_update_delegated_atime(inode); attr->ia_valid &= ~ATTR_ATIME; } diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index fc5456377160..18d46b0e71dd 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -253,6 +253,8 @@ extern struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv, u32 minor_version); extern struct rpc_clnt *nfs4_find_or_create_ds_client(struct nfs_client *, struct inode *); +extern void nfs4_session_limit_rwsize(struct nfs_server *server); +extern void nfs4_session_limit_xasize(struct nfs_server *server); extern struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv, const struct sockaddr_storage *ds_addr, int ds_addrlen, int ds_proto, unsigned int ds_timeo, diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c index 4c7d16a99ed6..e55c5977fcc3 100644 --- a/fs/nfs/localio.c +++ b/fs/nfs/localio.c @@ -865,6 +865,8 @@ static void nfs_local_call_write(struct work_struct *work) file_start_write(filp); n_iters = atomic_read(&iocb->n_iters); for (int i = 0; i < n_iters ; i++) { + size_t icount; + if (iocb->iter_is_dio_aligned[i]) { iocb->kiocb.ki_flags |= IOCB_DIRECT; /* Only use AIO completion if DIO-aligned segment is last */ @@ -881,8 +883,16 @@ static void nfs_local_call_write(struct work_struct *work) if (status == -EIOCBQUEUED) continue; /* Break on completion, errors, or short writes */ + icount = iov_iter_count(&iocb->iters[i]); if (nfs_local_pgio_done(iocb, status) || status < 0 || - (size_t)status < iov_iter_count(&iocb->iters[i])) { + (size_t)status < icount) { + if ((size_t)status < icount) { + struct nfs_lock_context *ctx = + iocb->hdr->req->wb_lock_context; + + set_bit(NFS_CONTEXT_WRITE_SYNC, + &ctx->open_context->flags); + } nfs_local_write_iocb_done(iocb); break; } @@ -901,6 +911,9 @@ static void nfs_local_do_write(struct nfs_local_kiocb *iocb, __func__, hdr->args.count, hdr->args.offset, (hdr->args.stable == NFS_UNSTABLE) ? "unstable" : "stable"); + if (test_bit(NFS_CONTEXT_WRITE_SYNC, + &hdr->req->wb_lock_context->open_context->flags)) + hdr->args.stable = NFS_FILE_SYNC; switch (hdr->args.stable) { default: break; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index be2aebf62056..95d7cd564b74 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c index 7b3ca68fb4bb..7602ede6f75f 100644 --- a/fs/nfs/nfs42proc.c +++ b/fs/nfs/nfs42proc.c @@ -401,6 +401,7 @@ static void nfs42_copy_dest_done(struct file *file, loff_t pos, loff_t len, NFS_INO_INVALID_MTIME | NFS_INO_INVALID_BLOCKS); spin_unlock(&inode->i_lock); + nfs_update_delegated_mtime(inode); } static ssize_t _nfs42_proc_copy(struct file *src, @@ -1372,11 +1373,15 @@ out_put_src_lock: static int _nfs42_proc_removexattr(struct inode *inode, const char *name) { struct nfs_server *server = NFS_SERVER(inode); + __u32 bitmask[NFS_BITMASK_SZ]; struct nfs42_removexattrargs args = { .fh = NFS_FH(inode), + .bitmask = bitmask, .xattr_name = name, }; - struct nfs42_removexattrres res; + struct nfs42_removexattrres res = { + .server = server, + }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVEXATTR], .rpc_argp = &args, @@ -1385,12 +1390,22 @@ static int _nfs42_proc_removexattr(struct inode *inode, const char *name) int ret; unsigned long timestamp = jiffies; + res.fattr = nfs_alloc_fattr(); + if (!res.fattr) + return -ENOMEM; + + nfs4_bitmask_set(bitmask, server->cache_consistency_bitmask, + inode, NFS_INO_INVALID_CHANGE); + ret = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 1); trace_nfs4_removexattr(inode, name, ret); - if (!ret) + if (!ret) { nfs4_update_changeattr(inode, &res.cinfo, timestamp, 0); + ret = nfs_post_op_update_inode(inode, res.fattr); + } + kfree(res.fattr); return ret; } diff --git a/fs/nfs/nfs42xdr.c b/fs/nfs/nfs42xdr.c index 5c7452ce6e8a..ec105c62f721 100644 --- a/fs/nfs/nfs42xdr.c +++ b/fs/nfs/nfs42xdr.c @@ -263,11 +263,13 @@ #define NFS4_enc_removexattr_sz (compound_encode_hdr_maxsz + \ encode_sequence_maxsz + \ encode_putfh_maxsz + \ - encode_removexattr_maxsz) + encode_removexattr_maxsz + \ + encode_getattr_maxsz) #define NFS4_dec_removexattr_sz (compound_decode_hdr_maxsz + \ decode_sequence_maxsz + \ decode_putfh_maxsz + \ - decode_removexattr_maxsz) + decode_removexattr_maxsz + \ + decode_getattr_maxsz) /* * These values specify the maximum amount of data that is not @@ -869,6 +871,7 @@ static void nfs4_xdr_enc_removexattr(struct rpc_rqst *req, encode_sequence(xdr, &args->seq_args, &hdr); encode_putfh(xdr, args->fh, &hdr); encode_removexattr(xdr, args->xattr_name, &hdr); + encode_getfattr(xdr, args->bitmask, &hdr); encode_nops(&hdr); } @@ -1818,6 +1821,9 @@ static int nfs4_xdr_dec_removexattr(struct rpc_rqst *req, goto out; status = decode_removexattr(xdr, &res->cinfo); + if (status) + goto out; + status = decode_getfattr(xdr, res->fattr, res->server); out: return status; } diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index c211639949c2..71c271a1700a 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -855,7 +855,7 @@ EXPORT_SYMBOL_GPL(nfs4_set_ds_client); * Limit the mount rsize, wsize and dtsize using negotiated fore * channel attributes. */ -static void nfs4_session_limit_rwsize(struct nfs_server *server) +void nfs4_session_limit_rwsize(struct nfs_server *server) { struct nfs4_session *sess; u32 server_resp_sz; @@ -878,7 +878,7 @@ static void nfs4_session_limit_rwsize(struct nfs_server *server) /* * Limit xattr sizes using the channel attributes. */ -static void nfs4_session_limit_xasize(struct nfs_server *server) +void nfs4_session_limit_xasize(struct nfs_server *server) { #ifdef CONFIG_NFS_V4_2 struct nfs4_session *sess; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d839a97df822..a9b8d482d289 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4469,6 +4469,13 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, case -ENOTSUPP: case -EOPNOTSUPP: server->caps &= ~NFS_CAP_DIR_DELEG; + break; + case -NFS4ERR_INVAL: + case -NFS4ERR_IO: + case -NFS4ERR_DIRDELEG_UNAVAIL: + case -NFS4ERR_NOTDIR: + clear_bit(NFS_INO_REQ_DIR_DELEG, &(NFS_I(inode)->flags)); + status = -EAGAIN; } } @@ -4490,6 +4497,7 @@ int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, default: err = nfs4_handle_exception(server, err, &exception); break; + case -EAGAIN: case -ENOTSUPP: case -EOPNOTSUPP: exception.retry = true; @@ -5052,6 +5060,7 @@ static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir, res->new_fattr->time_start, NFS_INO_INVALID_NLINK | NFS_INO_INVALID_DATA); + nfs_update_delegated_mtime(new_dir); } else nfs4_update_changeattr(old_dir, &res->old_cinfo, res->old_fattr->time_start, @@ -9769,16 +9778,26 @@ static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata) if (!nfs41_sequence_process(task, &lrp->res.seq_res)) return; - if (task->tk_rpc_status == -ETIMEDOUT) { - lrp->rpc_status = -EAGAIN; - lrp->res.lrs_present = 0; - return; - } - /* - * Was there an RPC level error? Assume the call succeeded, - * and that we need to release the layout - */ - if (task->tk_rpc_status != 0 && RPC_WAS_SENT(task)) { + if (task->tk_rpc_status < 0) { + switch (task->tk_rpc_status) { + case -EACCES: + case -EIO: + case -EKEYEXPIRED: + case -ERESTARTSYS: + case -EINTR: + lrp->rpc_status = 0; + break; + case -ENETDOWN: + case -ENETUNREACH: + if (task->tk_flags & RPC_TASK_NETUNREACH_FATAL) + lrp->rpc_status = 0; + else + lrp->rpc_status = -EAGAIN; + break; + default: + lrp->rpc_status = -EAGAIN; + break; + } lrp->res.lrs_present = 0; return; } @@ -10618,6 +10637,9 @@ static struct nfs_server *nfs4_clone_server(struct nfs_server *source, if (IS_ERR(server)) return server; + nfs4_session_limit_rwsize(server); + nfs4_session_limit_xasize(server); + error = nfs4_delegation_hash_alloc(server); if (error) { nfs_free_server(server); diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h index 9f9ce4a565ea..ff467959f733 100644 --- a/fs/nfs/nfstrace.h +++ b/fs/nfs/nfstrace.h @@ -1496,7 +1496,7 @@ DECLARE_EVENT_CLASS(nfs_page_class, __field(dev_t, dev) __field(u32, fhandle) __field(u64, fileid) - __field(const struct nfs_page *__private, req) + __field(const struct nfs_page *, req) __field(loff_t, offset) __field(unsigned int, count) __field(unsigned long, flags) diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index a9373de891c9..4a87b2fdb2e6 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -1186,6 +1186,9 @@ static int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc, nfs_page_group_lock(req); + if (test_bit(NFS_CONTEXT_WRITE_SYNC, + &req->wb_lock_context->open_context->flags)) + desc->pg_ioflags |= FLUSH_STABLE; subreq = req; subreq_size = subreq->wb_bytes; for(;;) { diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index e79deb9bf664..743467e9ba20 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1698,11 +1698,23 @@ int pnfs_roc_done(struct rpc_task *task, struct nfs4_layoutreturn_args **argpp, /* If the call was not sent, let caller handle it */ if (!RPC_WAS_SENT(task)) return 0; - /* - * Otherwise, assume the call succeeded and - * that we need to release the layout - */ - *ret = 0; + switch (task->tk_rpc_status) { + default: + /* + * Defer the layoutreturn if it was due + * to the server being down. + */ + *ret = -NFS4ERR_NOMATCHING_LAYOUT; + break; + case -EACCES: + case -EIO: + case -EKEYEXPIRED: + case -ERESTARTSYS: + case -EINTR: + /* Don't retry */ + *ret = 0; + break; + } (*respp)->lrs_present = 0; retval = 0; break; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 8c3d2efa2636..70795684b8e8 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include "internal.h" diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 7a318581f85b..4cd420b14ce3 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1166,12 +1166,18 @@ static int nfs_set_super(struct super_block *s, struct fs_context *fc) static int nfs_compare_super_address(struct nfs_server *server1, struct nfs_server *server2) { + struct rpc_xprt *xprt1, *xprt2; struct sockaddr *sap1, *sap2; - struct rpc_xprt *xprt1 = server1->client->cl_xprt; - struct rpc_xprt *xprt2 = server2->client->cl_xprt; + + rcu_read_lock(); + + xprt1 = rcu_dereference(server1->client->cl_xprt); + xprt2 = rcu_dereference(server2->client->cl_xprt); if (!net_eq(xprt1->xprt_net, xprt2->xprt_net)) - return 0; + goto out_unlock; + + rcu_read_unlock(); sap1 = (struct sockaddr *)&server1->nfs_client->cl_addr; sap2 = (struct sockaddr *)&server2->nfs_client->cl_addr; @@ -1203,6 +1209,10 @@ static int nfs_compare_super_address(struct nfs_server *server1, } return 1; + +out_unlock: + rcu_read_unlock(); + return 0; } static int nfs_compare_userns(const struct nfs_server *old, diff --git a/fs/nfs/sysfs.c b/fs/nfs/sysfs.c index 1da4f707f9ef..3a197252a132 100644 --- a/fs/nfs/sysfs.c +++ b/fs/nfs/sysfs.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include "internal.h" #include "nfs4_fs.h" @@ -288,7 +288,7 @@ shutdown_store(struct kobject *kobj, struct kobj_attribute *attr, shutdown_client(server->client_acl); if (server->nlm_host) - shutdown_client(server->nlm_host->h_rpcclnt); + nlmclnt_shutdown_rpc_clnt(server->nlm_host); out: shutdown_nfs_client(server->nfs_client); return count; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index cc02b57de3c7..3134bb17f3e3 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -926,9 +926,13 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr) goto remove_req; } if (nfs_write_need_commit(hdr)) { + struct nfs_open_context *ctx = + hdr->req->wb_lock_context->open_context; + /* Reset wb_nio, since the write was successful. */ req->wb_nio = 0; memcpy(&req->wb_verf, &hdr->verf.verifier, sizeof(req->wb_verf)); + clear_bit(NFS_CONTEXT_WRITE_SYNC, &ctx->flags); nfs_mark_request_commit(req, hdr->lseg, &cinfo, hdr->ds_commit_idx); goto next; @@ -1550,9 +1554,12 @@ static void nfs_writeback_result(struct rpc_task *task, struct nfs_pgio_args *argp = &hdr->args; struct nfs_pgio_res *resp = &hdr->res; - if (resp->count < argp->count) { + if (resp->count < argp->count && !list_empty(&hdr->pages)) { static unsigned long complain; + struct nfs_open_context *ctx = + hdr->req->wb_lock_context->open_context; + set_bit(NFS_CONTEXT_WRITE_SYNC, &ctx->flags); /* This a short write! */ nfs_inc_stats(hdr->inode, NFSIOS_SHORTWRITE); @@ -1836,6 +1843,8 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data) /* We have a mismatch. Write the page again */ dprintk(" mismatch\n"); nfs_mark_request_dirty(req); + set_bit(NFS_CONTEXT_WRITE_SYNC, + &req->wb_lock_context->open_context->flags); atomic_long_inc(&NFS_I(data->inode)->redirtied_pages); next: nfs_unlock_and_release_request(req); diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig index 4fd6e818565e..ffb76761d6a8 100644 --- a/fs/nfsd/Kconfig +++ b/fs/nfsd/Kconfig @@ -7,6 +7,7 @@ config NFSD select CRC32 select CRYPTO_LIB_MD5 if NFSD_LEGACY_CLIENT_TRACKING select CRYPTO_LIB_SHA256 if NFSD_V4 + select CRYPTO # required by RPCSEC_GSS_KRB5 and signed filehandles select LOCKD select SUNRPC select EXPORTFS @@ -78,7 +79,6 @@ config NFSD_V4 depends on NFSD && PROC_FS select FS_POSIX_ACL select RPCSEC_GSS_KRB5 - select CRYPTO # required by RPCSEC_GSS_KRB5 select GRACE_PERIOD select NFS_V4_2_SSC_HELPER if NFS_V4_2 help @@ -177,16 +177,6 @@ config NFSD_LEGACY_CLIENT_TRACKING and will be removed in the future. Say Y here if you need support for them in the interim. -config NFSD_V4_DELEG_TIMESTAMPS - bool "Support delegated timestamps" - depends on NFSD_V4 - default n - help - NFSD implements delegated timestamps according to - draft-ietf-nfsv4-delstid-08 "Extending the Opening of Files". This - is currently an experimental feature and is therefore left disabled - by default. - config NFSD_V4_POSIX_ACLS bool "Support NFSv4 POSIX draft ACLs" depends on NFSD_V4 diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c index a7cfba29990e..9d829c84f374 100644 --- a/fs/nfsd/blocklayout.c +++ b/fs/nfsd/blocklayout.c @@ -273,6 +273,52 @@ const struct nfsd4_layout_ops bl_layout_ops = { #endif /* CONFIG_NFSD_BLOCKLAYOUT */ #ifdef CONFIG_NFSD_SCSILAYOUT + +#define NFSD_MDS_PR_FENCED XA_MARK_0 + +/* + * Clear the fence flag if the device already has an entry. This occurs + * when a client re-registers after a previous fence, allowing new + * layouts for this device. + * + * Insert only on first registration. This bounds cl_dev_fences to the + * count of devices this client has accessed, preventing unbounded growth. + */ +static inline int nfsd4_scsi_fence_insert(struct nfs4_client *clp, + dev_t device) +{ + struct xarray *xa = &clp->cl_dev_fences; + int ret; + + xa_lock(xa); + ret = __xa_insert(xa, device, XA_ZERO_ENTRY, GFP_KERNEL); + if (ret == -EBUSY) { + __xa_clear_mark(xa, device, NFSD_MDS_PR_FENCED); + ret = 0; + } + xa_unlock(xa); + clp->cl_fence_retry_warn = false; + return ret; +} + +static inline bool nfsd4_scsi_fence_set(struct nfs4_client *clp, dev_t device) +{ + struct xarray *xa = &clp->cl_dev_fences; + bool skip; + + xa_lock(xa); + skip = xa_get_mark(xa, device, NFSD_MDS_PR_FENCED); + if (!skip) + __xa_set_mark(xa, device, NFSD_MDS_PR_FENCED); + xa_unlock(xa); + return skip; +} + +static inline void nfsd4_scsi_fence_clear(struct nfs4_client *clp, dev_t device) +{ + xa_clear_mark(&clp->cl_dev_fences, device, NFSD_MDS_PR_FENCED); +} + #define NFSD_MDS_PR_KEY 0x0100000000000000ULL /* @@ -342,6 +388,10 @@ nfsd4_block_get_device_info_scsi(struct super_block *sb, goto out_free_dev; } + ret = nfsd4_scsi_fence_insert(clp, sb->s_bdev->bd_dev); + if (ret < 0) + goto out_free_dev; + ret = ops->pr_register(sb->s_bdev, 0, NFSD_MDS_PR_KEY, true); if (ret) { pr_err("pNFS: failed to register key for device %s.\n", @@ -394,17 +444,67 @@ nfsd4_scsi_proc_layoutcommit(struct inode *inode, struct svc_rqst *rqstp, return nfsd4_block_commit_blocks(inode, lcp, iomaps, nr_iomaps); } -static void +/* + * Perform the fence operation to prevent the client from accessing the + * block device. If a fence operation is already in progress, wait for + * it to complete before checking the NFSD_MDS_PR_FENCED flag. Once the + * operation is complete, check the flag. If NFSD_MDS_PR_FENCED is set, + * update the layout stateid by setting the ls_fenced flag to indicate + * that the client has been fenced. + * + * The cl_fence_mutex ensures that the fence operation has been fully + * completed, rather than just in progress, when returning from this + * function. + * + * Return true if client was fenced otherwise return false. + */ +static bool nfsd4_scsi_fence_client(struct nfs4_layout_stateid *ls, struct nfsd_file *file) { struct nfs4_client *clp = ls->ls_stid.sc_client; struct block_device *bdev = file->nf_file->f_path.mnt->mnt_sb->s_bdev; int status; + bool ret; + + mutex_lock(&clp->cl_fence_mutex); + if (nfsd4_scsi_fence_set(clp, bdev->bd_dev)) { + mutex_unlock(&clp->cl_fence_mutex); + return true; + } status = bdev->bd_disk->fops->pr_ops->pr_preempt(bdev, NFSD_MDS_PR_KEY, nfsd4_scsi_pr_key(clp), PR_EXCLUSIVE_ACCESS_REG_ONLY, true); + /* + * Reset to allow retry only when the command could not have + * reached the device. Negative status means a local error + * (e.g., -ENOMEM) prevented the command from being sent. + * PR_STS_PATH_FAILED, PR_STS_PATH_FAST_FAILED, and + * PR_STS_RETRY_PATH_FAILURE indicate transport path failures + * before device delivery. + * + * For all other errors, the command may have reached the device + * and the preempt may have succeeded. Avoid resetting, since + * retrying a successful preempt returns PR_STS_IOERR or + * PR_STS_RESERVATION_CONFLICT, which would cause an infinite + * retry loop. + */ + switch (status) { + case 0: + case PR_STS_IOERR: + case PR_STS_RESERVATION_CONFLICT: + ret = true; + break; + default: + /* retry-able and other errors */ + ret = false; + nfsd4_scsi_fence_clear(clp, bdev->bd_dev); + break; + } + mutex_unlock(&clp->cl_fence_mutex); + trace_nfsd_pnfs_fence(clp, bdev->bd_disk->disk_name, status); + return ret; } const struct nfsd4_layout_ops scsi_layout_ops = { diff --git a/fs/nfsd/debugfs.c b/fs/nfsd/debugfs.c index 7f44689e0a53..386fd1c54f52 100644 --- a/fs/nfsd/debugfs.c +++ b/fs/nfsd/debugfs.c @@ -140,4 +140,8 @@ void nfsd_debugfs_init(void) debugfs_create_file("io_cache_write", 0644, nfsd_top_dir, NULL, &nfsd_io_cache_write_fops); +#ifdef CONFIG_NFSD_V4 + debugfs_create_bool("delegated_timestamps", 0644, nfsd_top_dir, + &nfsd_delegts_enabled); +#endif } diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 1aadfa8e0406..665153f1720e 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -1362,13 +1362,14 @@ static struct flags { { NFSEXP_ASYNC, {"async", "sync"}}, { NFSEXP_GATHERED_WRITES, {"wdelay", "no_wdelay"}}, { NFSEXP_NOREADDIRPLUS, {"nordirplus", ""}}, + { NFSEXP_SECURITY_LABEL, {"security_label", ""}}, + { NFSEXP_SIGN_FH, {"sign_fh", ""}}, { NFSEXP_NOHIDE, {"nohide", ""}}, - { NFSEXP_CROSSMOUNT, {"crossmnt", ""}}, { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}}, + { NFSEXP_CROSSMOUNT, {"crossmnt", ""}}, { NFSEXP_V4ROOT, {"v4root", ""}}, { NFSEXP_PNFS, {"pnfs", ""}}, - { NFSEXP_SECURITY_LABEL, {"security_label", ""}}, { 0, {"", ""}} }; diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index c774ce9aa296..6fe1325815e0 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -14,19 +14,20 @@ #define NFSDDBG_FACILITY NFSDDBG_LOCKD -#ifdef CONFIG_LOCKD_V4 -#define nlm_stale_fh nlm4_stale_fh -#define nlm_failed nlm4_failed -#else -#define nlm_stale_fh nlm_lck_denied_nolocks -#define nlm_failed nlm_lck_denied_nolocks -#endif -/* - * Note: we hold the dentry use count while the file is open. +/** + * nlm_fopen - Open an NFSD file + * @rqstp: NLM RPC procedure execution context + * @f: NFS file handle to be opened + * @filp: OUT: an opened struct file + * @flags: the POSIX open flags to use + * + * nlm_fopen() holds the dentry reference until nlm_fclose() releases it. + * + * Returns zero on success or a negative errno value if the file + * cannot be opened. */ -static __be32 -nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp, - int mode) +static int nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, + struct file **filp, int flags) { __be32 nfserr; int access; @@ -47,18 +48,17 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp, * if NFSEXP_NOAUTHNLM is set. Some older clients use AUTH_NULL * for NLM requests. */ - access = (mode == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ; + access = (flags == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ; access |= NFSD_MAY_NLM | NFSD_MAY_OWNER_OVERRIDE | NFSD_MAY_BYPASS_GSS; nfserr = nfsd_open(rqstp, &fh, S_IFREG, access, filp); fh_put(&fh); - /* We return nlm error codes as nlm doesn't know - * about nfsd, but nfsd does know about nlm.. - */ + switch (nfserr) { case nfs_ok: - return 0; + break; case nfserr_jukebox: - /* this error can indicate a presence of a conflicting + /* + * This error can indicate a presence of a conflicting * delegation to an NLM lock request. Options are: * (1) For now, drop this request and make the client * retry. When delegation is returned, client's lock retry @@ -66,19 +66,25 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp, * (2) NLM4_DENIED as per "spec" signals to the client * that the lock is unavailable now but client can retry. * Linux client implementation does not. It treats - * NLM4_DENIED same as NLM4_FAILED and errors the request. + * NLM4_DENIED same as NLM4_FAILED and fails the request. * (3) For the future, treat this as blocked lock and try * to callback when the delegation is returned but might * not have a proper lock request to block on. */ - return nlm_drop_reply; + return -EWOULDBLOCK; case nfserr_stale: - return nlm_stale_fh; + return -ESTALE; default: - return nlm_failed; + return -ENOLCK; } + + return 0; } +/** + * nlm_fclose - Close an NFSD file + * @filp: a struct file that was opened by nlm_fopen() + */ static void nlm_fclose(struct file *filp) { diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c index 887525964451..81c943345d13 100644 --- a/fs/nfsd/netlink.c +++ b/fs/nfsd/netlink.c @@ -24,12 +24,13 @@ const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1] = { }; /* NFSD_CMD_THREADS_SET - do */ -static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_MIN_THREADS + 1] = { +static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_FH_KEY + 1] = { [NFSD_A_SERVER_THREADS] = { .type = NLA_U32, }, [NFSD_A_SERVER_GRACETIME] = { .type = NLA_U32, }, [NFSD_A_SERVER_LEASETIME] = { .type = NLA_U32, }, [NFSD_A_SERVER_SCOPE] = { .type = NLA_NUL_STRING, }, [NFSD_A_SERVER_MIN_THREADS] = { .type = NLA_U32, }, + [NFSD_A_SERVER_FH_KEY] = NLA_POLICY_EXACT_LEN(16), }; /* NFSD_CMD_VERSION_SET - do */ @@ -58,7 +59,7 @@ static const struct genl_split_ops nfsd_nl_ops[] = { .cmd = NFSD_CMD_THREADS_SET, .doit = nfsd_nl_threads_set_doit, .policy = nfsd_threads_set_nl_policy, - .maxattr = NFSD_A_SERVER_MIN_THREADS, + .maxattr = NFSD_A_SERVER_FH_KEY, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index 9fa600602658..27da1a3edacb 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -25,6 +25,7 @@ #define SESSION_HASH_SIZE 512 struct cld_net; +struct nfsd_net_cb; struct nfsd4_client_tracking_ops; enum { @@ -99,6 +100,9 @@ struct nfsd_net { */ struct list_head client_lru; struct list_head close_lru; + + /* protects del_recall_lru and delegation hash/unhash */ + spinlock_t deleg_lock ____cacheline_aligned; struct list_head del_recall_lru; /* protected by blocked_locks_lock */ @@ -224,6 +228,9 @@ struct nfsd_net { spinlock_t local_clients_lock; struct list_head local_clients; #endif + siphash_key_t *fh_key; + + struct nfsd_net_cb *nfsd_cb; }; /* Simple check to find out if a given net was properly initialized */ diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index ef4971d71ac4..2ff9a991a8fb 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -1069,7 +1069,7 @@ svcxdr_encode_entry3_common(struct nfsd3_readdirres *resp, const char *name, * * Return values: * %0: Entry was successfully encoded. - * %-EINVAL: An encoding problem occured, secondary status code in resp->common.err + * %-EINVAL: An encoding problem occurred, secondary status code in resp->common.err * * On exit, the following fields are updated: * - resp->xdr @@ -1144,7 +1144,7 @@ out_noattrs: * * Return values: * %0: Entry was successfully encoded. - * %-EINVAL: An encoding problem occured, secondary status code in resp->common.err + * %-EINVAL: An encoding problem occurred, secondary status code in resp->common.err * * On exit, the following fields are updated: * - resp->xdr diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index aea8bdd2fdc4..50827405468d 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -1016,7 +1016,7 @@ static int nfs4_xdr_dec_cb_offload(struct rpc_rqst *rqstp, .p_decode = nfs4_xdr_dec_##restype, \ .p_arglen = NFS4_enc_##argtype##_sz, \ .p_replen = NFS4_dec_##restype##_sz, \ - .p_statidx = NFSPROC4_CB_##call, \ + .p_statidx = NFSPROC4_CLNT_##proc, \ .p_name = #proc, \ } @@ -1032,39 +1032,14 @@ static const struct rpc_procinfo nfs4_cb_procedures[] = { PROC(CB_GETATTR, COMPOUND, cb_getattr, cb_getattr), }; -static unsigned int nfs4_cb_counts[ARRAY_SIZE(nfs4_cb_procedures)]; -static const struct rpc_version nfs_cb_version4 = { -/* - * Note on the callback rpc program version number: despite language in rfc - * 5661 section 18.36.3 requiring servers to use 4 in this field, the - * official xdr descriptions for both 4.0 and 4.1 specify version 1, and - * in practice that appears to be what implementations use. The section - * 18.36.3 language is expected to be fixed in an erratum. - */ - .number = 1, - .nrprocs = ARRAY_SIZE(nfs4_cb_procedures), - .procs = nfs4_cb_procedures, - .counts = nfs4_cb_counts, -}; +#define NFS4_CB_PROGRAM 0x40000000 +#define NFS4_CB_VERSION 1 -static const struct rpc_version *nfs_cb_version[2] = { - [1] = &nfs_cb_version4, -}; - -static const struct rpc_program cb_program; - -static struct rpc_stat cb_stats = { - .program = &cb_program -}; - -#define NFS4_CALLBACK 0x40000000 -static const struct rpc_program cb_program = { - .name = "nfs4_cb", - .number = NFS4_CALLBACK, - .nrvers = ARRAY_SIZE(nfs_cb_version), - .version = nfs_cb_version, - .stats = &cb_stats, - .pipe_dir_name = "nfsd4_cb", +struct nfsd_net_cb { + struct rpc_version version4; + const struct rpc_version *versions[NFS4_CB_VERSION + 1]; + struct rpc_program program; + struct rpc_stat stat; }; static int max_cb_time(struct net *net) @@ -1140,6 +1115,7 @@ static const struct cred *get_backchannel_cred(struct nfs4_client *clp, struct r static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses) { + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); int maxtime = max_cb_time(clp->net); struct rpc_timeout timeparms = { .to_initval = maxtime, @@ -1152,14 +1128,14 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c .addrsize = conn->cb_addrlen, .saddress = (struct sockaddr *) &conn->cb_saddr, .timeout = &timeparms, - .program = &cb_program, - .version = 1, + .version = NFS4_CB_VERSION, .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), .cred = current_cred(), }; struct rpc_clnt *client; const struct cred *cred; + args.program = &nn->nfsd_cb->program; if (clp->cl_minorversion == 0) { if (!clp->cl_cred.cr_principal && (clp->cl_cred.cr_flavor >= RPC_AUTH_GSS_KRB5)) { @@ -1786,3 +1762,70 @@ bool nfsd4_run_cb(struct nfsd4_callback *cb) nfsd41_cb_inflight_end(clp); return queued; } + +/** + * nfsd_net_cb_shutdown - release per-netns callback RPC program resources + * @nn: NFS server network namespace + * + * Frees resources allocated by nfsd_net_cb_init(). + */ +void nfsd_net_cb_shutdown(struct nfsd_net *nn) +{ + struct nfsd_net_cb *cb = nn->nfsd_cb; + + if (cb) { + kfree(cb->version4.counts); + kfree(cb); + nn->nfsd_cb = NULL; + } +} + +/** + * nfsd_net_cb_init - initialize per-netns callback RPC program + * @nn: NFS server network namespace + * + * Sets up the callback RPC program, version table, procedure + * counts, and statistics structure for @nn. Caller must release + * these resources using nfsd_net_cb_shutdown(). + * + * Return: 0 on success, or -ENOMEM if allocation fails. + */ +int nfsd_net_cb_init(struct nfsd_net *nn) +{ + struct nfsd_net_cb *cb; + + cb = kzalloc(sizeof(*cb), GFP_KERNEL); + if (!cb) + return -ENOMEM; + + cb->version4.counts = kzalloc_objs(unsigned int, + ARRAY_SIZE(nfs4_cb_procedures), GFP_KERNEL); + if (!cb->version4.counts) { + kfree(cb); + return -ENOMEM; + } + /* + * Note on the callback rpc program version number: despite language + * in rfc 5661 section 18.36.3 requiring servers to use 4 in this + * field, the official xdr descriptions for both 4.0 and 4.1 specify + * version 1, and in practice that appears to be what implementations + * use. The section 18.36.3 language is expected to be fixed in an + * erratum. + */ + cb->version4.number = NFS4_CB_VERSION; + cb->version4.nrprocs = ARRAY_SIZE(nfs4_cb_procedures); + cb->version4.procs = nfs4_cb_procedures; + cb->versions[NFS4_CB_VERSION] = &cb->version4; + + cb->program.name = "nfs4_cb"; + cb->program.number = NFS4_CB_PROGRAM; + cb->program.nrvers = ARRAY_SIZE(cb->versions); + cb->program.version = &cb->versions[0]; + cb->program.pipe_dir_name = "nfsd4_cb"; + cb->program.stats = &cb->stat; + cb->stat.program = &cb->program; + + nn->nfsd_cb = cb; + + return 0; +} diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c index ad7af8cfcf1f..69e41105efdd 100644 --- a/fs/nfsd/nfs4layouts.c +++ b/fs/nfsd/nfs4layouts.c @@ -27,6 +27,8 @@ static struct kmem_cache *nfs4_layout_stateid_cache; static const struct nfsd4_callback_ops nfsd4_cb_layout_ops; static const struct lease_manager_operations nfsd4_layouts_lm_ops; +static void nfsd4_layout_fence_worker(struct work_struct *work); + const struct nfsd4_layout_ops *nfsd4_layout_ops[LAYOUT_TYPE_MAX] = { #ifdef CONFIG_NFSD_FLEXFILELAYOUT [LAYOUT_FLEX_FILES] = &ff_layout_ops, @@ -177,6 +179,13 @@ nfsd4_free_layout_stateid(struct nfs4_stid *stid) trace_nfsd_layoutstate_free(&ls->ls_stid.sc_stateid); + spin_lock(&ls->ls_lock); + if (delayed_work_pending(&ls->ls_fence_work)) { + spin_unlock(&ls->ls_lock); + cancel_delayed_work_sync(&ls->ls_fence_work); + } else + spin_unlock(&ls->ls_lock); + spin_lock(&clp->cl_lock); list_del_init(&ls->ls_perclnt); spin_unlock(&clp->cl_lock); @@ -271,6 +280,10 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate, list_add(&ls->ls_perfile, &fp->fi_lo_states); spin_unlock(&fp->fi_lock); + ls->ls_fenced = false; + ls->ls_fence_delay = 0; + INIT_DELAYED_WORK(&ls->ls_fence_work, nfsd4_layout_fence_worker); + trace_nfsd_layoutstate_alloc(&ls->ls_stid.sc_stateid); return ls; } @@ -747,11 +760,9 @@ static bool nfsd4_layout_lm_break(struct file_lease *fl) { /* - * We don't want the locks code to timeout the lease for us; - * we'll remove it ourself if a layout isn't returned - * in time: + * Enforce break lease timeout to prevent NFSD + * thread from hanging in __break_lease. */ - fl->fl_break_time = 0; nfsd4_recall_file_layout(fl->c.flc_owner); return false; } @@ -782,10 +793,143 @@ nfsd4_layout_lm_open_conflict(struct file *filp, int arg) return 0; } +static void +nfsd4_layout_fence_worker(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct nfs4_layout_stateid *ls = container_of(dwork, + struct nfs4_layout_stateid, ls_fence_work); + struct nfsd_file *nf; + struct block_device *bdev; + struct nfs4_client *clp; + struct nfsd_net *nn; + + /* + * The workqueue clears WORK_STRUCT_PENDING before invoking + * this callback. Re-arm immediately so that + * delayed_work_pending() returns true while the fence + * operation is in progress, preventing + * lm_breaker_timedout() from taking a duplicate reference. + */ + mod_delayed_work(system_dfl_wq, &ls->ls_fence_work, 0); + + spin_lock(&ls->ls_lock); + if (list_empty(&ls->ls_layouts)) { + spin_unlock(&ls->ls_lock); +dispose: + cancel_delayed_work(&ls->ls_fence_work); + /* unlock the lease so that tasks waiting on it can proceed */ + nfsd4_close_layout(ls); + + ls->ls_fenced = true; + nfs4_put_stid(&ls->ls_stid); + return; + } + spin_unlock(&ls->ls_lock); + + rcu_read_lock(); + nf = nfsd_file_get(ls->ls_file); + rcu_read_unlock(); + if (!nf) + goto dispose; + + clp = ls->ls_stid.sc_client; + nn = net_generic(clp->net, nfsd_net_id); + bdev = nf->nf_file->f_path.mnt->mnt_sb->s_bdev; + if (nfsd4_layout_ops[ls->ls_layout_type]->fence_client(ls, nf)) { + /* fenced ok */ + nfsd_file_put(nf); + pr_warn("%s: FENCED client[%pISpc] clid[%d] to device[%s]\n", + __func__, (struct sockaddr *)&clp->cl_addr, + clp->cl_clientid.cl_id - nn->clientid_base, + bdev->bd_disk->disk_name); + goto dispose; + } + /* fence failed */ + nfsd_file_put(nf); + + if (!clp->cl_fence_retry_warn) { + pr_warn("%s: FENCE failed client[%pISpc] clid[%d] device[%s]\n", + __func__, (struct sockaddr *)&clp->cl_addr, + clp->cl_clientid.cl_id - nn->clientid_base, + bdev->bd_disk->disk_name); + clp->cl_fence_retry_warn = true; + } + /* + * The fence worker retries the fencing operation indefinitely to + * prevent data corruption. The admin needs to take the following + * actions to restore access to the file for other clients: + * + * . shutdown or power off the client being fenced. + * . manually expire the client to release all its state on the server; + * echo 'expire' > /proc/fs/nfsd/clients/clid/ctl'. + * + * Where: + * + * clid: is the unique client identifier displayed in + * the warning message above. + */ + if (!ls->ls_fence_delay) + ls->ls_fence_delay = HZ; + else + ls->ls_fence_delay = min(ls->ls_fence_delay << 1, + MAX_FENCE_DELAY); + mod_delayed_work(system_dfl_wq, &ls->ls_fence_work, ls->ls_fence_delay); +} + +/** + * nfsd4_layout_lm_breaker_timedout - The layout recall has timed out. + * @fl: file to check + * + * If the layout type supports a fence operation, schedule a worker to + * fence the client from accessing the block device. + * + * This function runs under the protection of the spin_lock flc_lock. + * At this time, the file_lease associated with the layout stateid is + * on the flc_list. A reference count is incremented on the layout + * stateid to prevent it from being freed while the fence worker is + * executing. Once the fence worker finishes its operation, it releases + * this reference. + * + * The fence worker continues to run until either the client has been + * fenced or the layout becomes invalid. The layout can become invalid + * as a result of a LAYOUTRETURN or when the CB_LAYOUT recall callback + * has completed. + * + * Return true if the file_lease should be disposed of by the caller; + * otherwise, return false. + */ +static bool +nfsd4_layout_lm_breaker_timedout(struct file_lease *fl) +{ + struct nfs4_layout_stateid *ls = fl->c.flc_owner; + + if ((!nfsd4_layout_ops[ls->ls_layout_type]->fence_client) || + ls->ls_fenced) + return true; + if (delayed_work_pending(&ls->ls_fence_work)) + return false; + /* + * Make sure layout has not been returned yet before + * taking a reference count on the layout stateid. + */ + spin_lock(&ls->ls_lock); + if (list_empty(&ls->ls_layouts) || + !refcount_inc_not_zero(&ls->ls_stid.sc_count)) { + spin_unlock(&ls->ls_lock); + return true; + } + spin_unlock(&ls->ls_lock); + + mod_delayed_work(system_dfl_wq, &ls->ls_fence_work, 0); + return false; +} + static const struct lease_manager_operations nfsd4_layouts_lm_ops = { .lm_break = nfsd4_layout_lm_break, .lm_change = nfsd4_layout_lm_change, .lm_open_conflict = nfsd4_layout_lm_open_conflict, + .lm_breaker_timedout = nfsd4_layout_lm_breaker_timedout, }; int diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 6880c5c520e7..ab39ec885440 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1413,6 +1413,9 @@ nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, dst, clone->cl_dst_pos, clone->cl_count, EX_ISSYNC(cstate->current_fh.fh_export)); + if (!status && (READ_ONCE(dst->nf_file->f_mode) & FMODE_NOCMTIME) != 0) + nfsd_update_cmtime_attr(dst->nf_file, 0); + nfsd_file_put(dst); nfsd_file_put(src); out: @@ -2118,8 +2121,10 @@ do_callback: set_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags); trace_nfsd_copy_async_done(copy); - nfsd4_send_cb_offload(copy); atomic_dec(©->cp_nn->pending_async_copies); + if (copy->cp_res.wr_bytes_written > 0 && copy->attr_update) + nfsd_update_cmtime_attr(copy->nf_dst->nf_file, 0); + nfsd4_send_cb_offload(copy); return 0; } @@ -2179,6 +2184,9 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, memcpy(&result->cb_stateid, ©->cp_stateid.cs_stid, sizeof(result->cb_stateid)); dup_copy_fields(copy, async_copy); + if ((READ_ONCE(copy->nf_dst->nf_file->f_mode) & + FMODE_NOCMTIME) != 0) + async_copy->attr_update = true; memcpy(async_copy->cp_cb_offload.co_referring_sessionid.data, cstate->session->se_sessionid.data, NFS4_MAX_SESSIONID_LEN); @@ -2197,6 +2205,10 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } else { status = nfsd4_do_copy(copy, copy->nf_src->nf_file, copy->nf_dst->nf_file, true); + if ((READ_ONCE(copy->nf_dst->nf_file->f_mode) & + FMODE_NOCMTIME) != 0 && + copy->cp_res.wr_bytes_written > 0) + nfsd_update_cmtime_attr(copy->nf_dst->nf_file, 0); } out: trace_nfsd_copy_done(copy, status); @@ -2535,10 +2547,6 @@ nfsd4_get_dir_delegation(struct svc_rqst *rqstp, dd = nfsd_get_dir_deleg(cstate, gdd, nf); nfsd_file_put(nf); if (IS_ERR(dd)) { - int err = PTR_ERR(dd); - - if (err != -EAGAIN) - return nfserrno(err); gdd->gddrnf_status = GDD4_UNAVAIL; return nfs_ok; } @@ -3043,6 +3051,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp) struct svc_fh *current_fh = &cstate->current_fh; struct svc_fh *save_fh = &cstate->save_fh; struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + struct nfsd_thread_local_info *ntli = rqstp->rq_private; __be32 status; resp->xdr = &rqstp->rq_res_stream; @@ -3081,7 +3090,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp) } check_if_stalefh_allowed(args); - rqstp->rq_lease_breaker = (void **)&cstate->clp; + ntli->ntli_lease_breaker = &cstate->clp; trace_nfsd_compound(rqstp, args->tag, args->taglen, args->client_opcnt); while (!status && resp->opcnt < args->opcnt) { diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index a569d89ac912..6837b63d9864 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -76,6 +76,8 @@ static const stateid_t close_stateid = { static u64 current_sessionid = 1; +bool nfsd_delegts_enabled __read_mostly = true; + #define ZERO_STATEID(stateid) (!memcmp((stateid), &zero_stateid, sizeof(stateid_t))) #define ONE_STATEID(stateid) (!memcmp((stateid), &one_stateid, sizeof(stateid_t))) #define CURRENT_STATEID(stateid) (!memcmp((stateid), ¤tstateid, sizeof(stateid_t))) @@ -91,13 +93,6 @@ static void deleg_reaper(struct nfsd_net *nn); /* Locking: */ -/* - * Currently used for the del_recall_lru and file hash table. In an - * effort to decrease the scope of the client_mutex, this spinlock may - * eventually cover more: - */ -static DEFINE_SPINLOCK(state_lock); - enum nfsd4_st_mutex_lock_subclass { OPEN_STATEID_MUTEX = 0, LOCK_STATEID_MUTEX = 1, @@ -1226,10 +1221,6 @@ static void put_deleg_file(struct nfs4_file *fp) static void nfsd4_finalize_deleg_timestamps(struct nfs4_delegation *dp, struct file *f) { - struct iattr ia = { .ia_valid = ATTR_ATIME | ATTR_CTIME | ATTR_MTIME | ATTR_DELEG }; - struct inode *inode = file_inode(f); - int ret; - /* don't do anything if FMODE_NOCMTIME isn't set */ if ((READ_ONCE(f->f_mode) & FMODE_NOCMTIME) == 0) return; @@ -1247,17 +1238,7 @@ static void nfsd4_finalize_deleg_timestamps(struct nfs4_delegation *dp, struct f return; /* Stamp everything to "now" */ - inode_lock(inode); - ret = notify_change(&nop_mnt_idmap, f->f_path.dentry, &ia, NULL); - inode_unlock(inode); - if (ret) { - struct inode *inode = file_inode(f); - - pr_notice_ratelimited("nfsd: Unable to update timestamps on inode %02x:%02x:%llu: %d\n", - MAJOR(inode->i_sb->s_dev), - MINOR(inode->i_sb->s_dev), - inode->i_ino, ret); - } + nfsd_update_cmtime_attr(f, ATTR_ATIME); } static void nfs4_unlock_deleg_lease(struct nfs4_delegation *dp) @@ -1293,8 +1274,9 @@ nfs4_delegation_exists(struct nfs4_client *clp, struct nfs4_file *fp) { struct nfs4_delegation *searchdp = NULL; struct nfs4_client *searchclp = NULL; + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - lockdep_assert_held(&state_lock); + lockdep_assert_held(&nn->deleg_lock); lockdep_assert_held(&fp->fi_lock); list_for_each_entry(searchdp, &fp->fi_delegations, dl_perfile) { @@ -1323,8 +1305,9 @@ static int hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp) { struct nfs4_client *clp = dp->dl_stid.sc_client; + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - lockdep_assert_held(&state_lock); + lockdep_assert_held(&nn->deleg_lock); lockdep_assert_held(&fp->fi_lock); lockdep_assert_held(&clp->cl_lock); @@ -1346,8 +1329,10 @@ static bool unhash_delegation_locked(struct nfs4_delegation *dp, unsigned short statusmask) { struct nfs4_file *fp = dp->dl_stid.sc_file; + struct nfsd_net *nn = net_generic(dp->dl_stid.sc_client->net, + nfsd_net_id); - lockdep_assert_held(&state_lock); + lockdep_assert_held(&nn->deleg_lock); if (!delegation_hashed(dp)) return false; @@ -1372,10 +1357,12 @@ unhash_delegation_locked(struct nfs4_delegation *dp, unsigned short statusmask) static void destroy_delegation(struct nfs4_delegation *dp) { bool unhashed; + struct nfsd_net *nn = net_generic(dp->dl_stid.sc_client->net, + nfsd_net_id); - spin_lock(&state_lock); + spin_lock(&nn->deleg_lock); unhashed = unhash_delegation_locked(dp, SC_STATUS_CLOSED); - spin_unlock(&state_lock); + spin_unlock(&nn->deleg_lock); if (unhashed) destroy_unhashed_deleg(dp); } @@ -1495,8 +1482,24 @@ release_all_access(struct nfs4_ol_stateid *stp) } } +/** + * nfs4_replay_free_cache - release dynamically allocated replay buffer + * @rp: replay cache to reset + * + * If @rp->rp_buf points to a kmalloc'd buffer, free it and reset + * rp_buf to the inline rp_ibuf. Always zeroes rp_buflen. + */ +void nfs4_replay_free_cache(struct nfs4_replay *rp) +{ + if (rp->rp_buf != rp->rp_ibuf) + kfree(rp->rp_buf); + rp->rp_buf = rp->rp_ibuf; + rp->rp_buflen = 0; +} + static inline void nfs4_free_stateowner(struct nfs4_stateowner *sop) { + nfs4_replay_free_cache(&sop->so_replay); kfree(sop->so_owner.data); sop->so_ops->so_free(sop); } @@ -1838,16 +1841,23 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) case SC_TYPE_DELEG: refcount_inc(&stid->sc_count); dp = delegstateid(stid); - spin_lock(&state_lock); + spin_lock(&nn->deleg_lock); if (!unhash_delegation_locked( dp, SC_STATUS_ADMIN_REVOKED)) dp = NULL; - spin_unlock(&state_lock); + spin_unlock(&nn->deleg_lock); if (dp) revoke_delegation(dp); break; case SC_TYPE_LAYOUT: ls = layoutstateid(stid); + spin_lock(&clp->cl_lock); + if (stid->sc_status == 0) { + stid->sc_status |= + SC_STATUS_ADMIN_REVOKED; + atomic_inc(&clp->cl_admin_revoked); + } + spin_unlock(&clp->cl_lock); nfsd4_close_layout(ls); break; } @@ -2381,6 +2391,10 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name, INIT_LIST_HEAD(&clp->cl_revoked); #ifdef CONFIG_NFSD_PNFS INIT_LIST_HEAD(&clp->cl_lo_states); +#endif +#ifdef CONFIG_NFSD_SCSILAYOUT + xa_init(&clp->cl_dev_fences); + mutex_init(&clp->cl_fence_mutex); #endif INIT_LIST_HEAD(&clp->async_copies); spin_lock_init(&clp->async_lock); @@ -2504,13 +2518,13 @@ __destroy_client(struct nfs4_client *clp) struct nfs4_delegation *dp; LIST_HEAD(reaplist); - spin_lock(&state_lock); + spin_lock(&nn->deleg_lock); while (!list_empty(&clp->cl_delegations)) { dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt); unhash_delegation_locked(dp, SC_STATUS_CLOSED); list_add(&dp->dl_recall_lru, &reaplist); } - spin_unlock(&state_lock); + spin_unlock(&nn->deleg_lock); while (!list_empty(&reaplist)) { dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); list_del_init(&dp->dl_recall_lru); @@ -2543,6 +2557,9 @@ __destroy_client(struct nfs4_client *clp) svc_xprt_put(clp->cl_cb_conn.cb_xprt); atomic_add_unless(&nn->nfs4_client_count, -1, 0); nfsd4_dec_courtesy_client_count(nn, clp); +#ifdef CONFIG_NFSD_SCSILAYOUT + xa_destroy(&clp->cl_dev_fences); +#endif free_client(clp); wake_up_all(&expiry_wq); } @@ -5418,12 +5435,12 @@ static void nfsd4_cb_recall_prepare(struct nfsd4_callback *cb) * If the dl_time != 0, then we know that it has already been * queued for a lease break. Don't queue it again. */ - spin_lock(&state_lock); + spin_lock(&nn->deleg_lock); if (delegation_hashed(dp) && dp->dl_time == 0) { dp->dl_time = ktime_get_boottime_seconds(); list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru); } - spin_unlock(&state_lock); + spin_unlock(&nn->deleg_lock); } static int nfsd4_cb_recall_done(struct nfsd4_callback *cb, @@ -5535,13 +5552,15 @@ nfsd_break_deleg_cb(struct file_lease *fl) static bool nfsd_breaker_owns_lease(struct file_lease *fl) { struct nfs4_delegation *dl = fl->c.flc_owner; + struct nfsd_thread_local_info *ntli; struct svc_rqst *rqst; struct nfs4_client *clp; rqst = nfsd_current_rqst(); if (!nfsd_v4client(rqst)) return false; - clp = *(rqst->rq_lease_breaker); + ntli = rqst->rq_private; + clp = *ntli->ntli_lease_breaker; return dl->dl_stid.sc_client == clp; } @@ -6036,17 +6055,16 @@ nfsd4_verify_setuid_write(struct nfsd4_open *open, struct nfsd_file *nf) return 0; } -#ifdef CONFIG_NFSD_V4_DELEG_TIMESTAMPS +/* + * Timestamp delegation was introduced in RFC7862. Runtime switch for disabling + * this feature is /sys/kernel/debug/nfsd/delegated_timestamps. + */ static bool nfsd4_want_deleg_timestamps(const struct nfsd4_open *open) { + if (!nfsd_delegts_enabled) + return false; return open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS; } -#else /* CONFIG_NFSD_V4_DELEG_TIMESTAMPS */ -static bool nfsd4_want_deleg_timestamps(const struct nfsd4_open *open) -{ - return false; -} -#endif /* CONFIG NFSD_V4_DELEG_TIMESTAMPS */ static struct nfs4_delegation * nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, @@ -6054,6 +6072,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, { bool deleg_ts = nfsd4_want_deleg_timestamps(open); struct nfs4_client *clp = stp->st_stid.sc_client; + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); struct nfs4_file *fp = stp->st_stid.sc_file; struct nfs4_clnt_odstate *odstate = stp->st_clnt_odstate; struct nfs4_delegation *dp; @@ -6113,7 +6132,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, return ERR_PTR(-EOPNOTSUPP); } - spin_lock(&state_lock); + spin_lock(&nn->deleg_lock); spin_lock(&fp->fi_lock); if (nfs4_delegation_exists(clp, fp)) status = -EAGAIN; @@ -6128,7 +6147,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, } else fp->fi_delegees++; spin_unlock(&fp->fi_lock); - spin_unlock(&state_lock); + spin_unlock(&nn->deleg_lock); if (nf) nfsd_file_put(nf); if (status) @@ -6172,13 +6191,13 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp, if (fp->fi_had_conflict) goto out_unlock; - spin_lock(&state_lock); + spin_lock(&nn->deleg_lock); spin_lock(&clp->cl_lock); spin_lock(&fp->fi_lock); status = hash_delegation_locked(dp, fp); spin_unlock(&fp->fi_lock); spin_unlock(&clp->cl_lock); - spin_unlock(&state_lock); + spin_unlock(&nn->deleg_lock); if (status) goto out_unlock; @@ -6257,12 +6276,12 @@ nfsd4_add_rdaccess_to_wrdeleg(struct svc_rqst *rqstp, struct nfsd4_open *open, return (false); fp = stp->st_stid.sc_file; spin_lock(&fp->fi_lock); - __nfs4_file_get_access(fp, NFS4_SHARE_ACCESS_READ); if (!fp->fi_fds[O_RDONLY]) { + __nfs4_file_get_access(fp, NFS4_SHARE_ACCESS_READ); fp->fi_fds[O_RDONLY] = nf; + fp->fi_rdeleg_file = nfsd_file_get(fp->fi_fds[O_RDONLY]); nf = NULL; } - fp->fi_rdeleg_file = nfsd_file_get(fp->fi_fds[O_RDONLY]); spin_unlock(&fp->fi_lock); if (nf) nfsd_file_put(nf); @@ -6352,7 +6371,6 @@ nfs4_open_delegation(struct svc_rqst *rqstp, struct nfsd4_open *open, } open->op_delegate_type = deleg_ts ? OPEN_DELEGATE_WRITE_ATTRS_DELEG : OPEN_DELEGATE_WRITE; - dp->dl_cb_fattr.ncf_cur_fsize = stat.size; dp->dl_cb_fattr.ncf_initial_cinfo = nfsd4_change_attribute(&stat); dp->dl_atime = stat.atime; dp->dl_ctime = stat.ctime; @@ -6954,7 +6972,7 @@ nfs4_laundromat(struct nfsd_net *nn) nfs40_clean_admin_revoked(nn, <); - spin_lock(&state_lock); + spin_lock(&nn->deleg_lock); list_for_each_safe(pos, next, &nn->del_recall_lru) { dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); if (!state_expired(<, dp->dl_time)) @@ -6963,7 +6981,7 @@ nfs4_laundromat(struct nfsd_net *nn) unhash_delegation_locked(dp, SC_STATUS_REVOKED); list_add(&dp->dl_recall_lru, &reaplist); } - spin_unlock(&state_lock); + spin_unlock(&nn->deleg_lock); while (!list_empty(&reaplist)) { dp = list_first_entry(&reaplist, struct nfs4_delegation, dl_recall_lru); @@ -8986,6 +9004,7 @@ static int nfs4_state_create_net(struct net *net) INIT_LIST_HEAD(&nn->client_lru); INIT_LIST_HEAD(&nn->close_lru); INIT_LIST_HEAD(&nn->del_recall_lru); + spin_lock_init(&nn->deleg_lock); spin_lock_init(&nn->client_lock); spin_lock_init(&nn->s2s_cp_lock); idr_init(&nn->s2s_cp_stateids); @@ -9117,13 +9136,13 @@ nfs4_state_shutdown_net(struct net *net) locks_end_grace(&nn->nfsd4_manager); INIT_LIST_HEAD(&reaplist); - spin_lock(&state_lock); + spin_lock(&nn->deleg_lock); list_for_each_safe(pos, next, &nn->del_recall_lru) { dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); unhash_delegation_locked(dp, SC_STATUS_CLOSED); list_add(&dp->dl_recall_lru, &reaplist); } - spin_unlock(&state_lock); + spin_unlock(&nn->deleg_lock); list_for_each_safe(pos, next, &reaplist) { dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); list_del_init(&dp->dl_recall_lru); @@ -9348,13 +9367,14 @@ __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_delegation **pdp) { - __be32 status; struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + struct nfsd_thread_local_info *ntli = rqstp->rq_private; struct file_lock_context *ctx; struct nfs4_delegation *dp = NULL; struct file_lease *fl; struct nfs4_cb_fattr *ncf; struct inode *inode = d_inode(dentry); + __be32 status; ctx = locks_inode_context(inode); if (!ctx) @@ -9375,7 +9395,7 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, break; } if (dp == NULL || dp == NON_NFSD_LEASE || - dp->dl_recall.cb_clp == *(rqstp->rq_lease_breaker)) { + dp->dl_recall.cb_clp == *(ntli->ntli_lease_breaker)) { spin_unlock(&ctx->flc_lock); if (dp == NON_NFSD_LEASE) { status = nfserrno(nfsd_open_break_lease(inode, @@ -9401,11 +9421,15 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, if (status != nfserr_jukebox || !nfsd_wait_for_delegreturn(rqstp, inode)) goto out_status; + status = nfs_ok; + goto out_status; + } + if (!ncf->ncf_file_modified) { + if (ncf->ncf_initial_cinfo != ncf->ncf_cb_change) + ncf->ncf_file_modified = true; + else if (i_size_read(inode) != ncf->ncf_cb_fsize) + ncf->ncf_file_modified = true; } - if (!ncf->ncf_file_modified && - (ncf->ncf_initial_cinfo != ncf->ncf_cb_change || - ncf->ncf_cur_fsize != ncf->ncf_cb_fsize)) - ncf->ncf_file_modified = true; if (ncf->ncf_file_modified) { int err; @@ -9445,6 +9469,7 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate, struct nfsd_file *nf) { struct nfs4_client *clp = cstate->clp; + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); struct nfs4_delegation *dp; struct file_lease *fl; struct nfs4_file *fp, *rfp; @@ -9468,7 +9493,7 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate, } /* if this client already has one, return that it's unavailable */ - spin_lock(&state_lock); + spin_lock(&nn->deleg_lock); spin_lock(&fp->fi_lock); /* existing delegation? */ if (nfs4_delegation_exists(clp, fp)) { @@ -9480,7 +9505,7 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate, ++fp->fi_delegees; } spin_unlock(&fp->fi_lock); - spin_unlock(&state_lock); + spin_unlock(&nn->deleg_lock); if (status) { put_nfs4_file(fp); @@ -9509,13 +9534,13 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate, * trying to set a delegation on the same file. If that happens, * then just say UNAVAIL. */ - spin_lock(&state_lock); + spin_lock(&nn->deleg_lock); spin_lock(&clp->cl_lock); spin_lock(&fp->fi_lock); status = hash_delegation_locked(dp, fp); spin_unlock(&fp->fi_lock); spin_unlock(&clp->cl_lock); - spin_unlock(&state_lock); + spin_unlock(&nn->deleg_lock); if (!status) { put_nfs4_file(fp); @@ -9531,3 +9556,31 @@ out_delegees: put_nfs4_file(fp); return ERR_PTR(status); } + +/** + * nfsd_update_cmtime_attr - update file's delegated ctime/mtime, + * and optionally other attributes (ie ATTR_ATIME). + * @f: pointer to an opened file + * @flags: any additional flags that should be updated + * + * Given upon opening a file delegated attributes were issues, update + * @f attributes to current times. + */ +void nfsd_update_cmtime_attr(struct file *f, unsigned int flags) +{ + int ret; + struct inode *inode = file_inode(f); + struct iattr attr = { + .ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_DELEG | flags, + }; + + inode_lock(inode); + ret = notify_change(&nop_mnt_idmap, f->f_path.dentry, &attr, NULL); + inode_unlock(inode); + if (ret) + pr_notice_ratelimited("nfsd: Unable to update timestamps on " + "inode %02x:%02x:%llu: %d\n", + MAJOR(inode->i_sb->s_dev), + MINOR(inode->i_sb->s_dev), + inode->i_ino, ret); +} diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 9d234913100b..2a0946c630e1 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -2598,6 +2598,7 @@ nfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op) static bool nfsd4_decode_compound(struct nfsd4_compoundargs *argp) { + struct nfsd_thread_local_info *ntli = argp->rqstp->rq_private; struct nfsd4_op *op; bool cachethis = false; int auth_slack= argp->rqstp->rq_auth_slack; @@ -2690,7 +2691,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) if (argp->minorversion) cachethis = false; svc_reserve_auth(argp->rqstp, max_reply + readbytes); - argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE; + ntli->ntli_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE; argp->splice_ok = nfsd_read_splice_ok(argp->rqstp); if (readcount > 1 || max_reply > PAGE_SIZE - auth_slack) @@ -6281,14 +6282,23 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) int len = xdr->buf->len - (op_status_offset + XDR_UNIT); so->so_replay.rp_status = op->status; - if (len <= NFSD4_REPLAY_ISIZE) { - so->so_replay.rp_buflen = len; - read_bytes_from_xdr_buf(xdr->buf, - op_status_offset + XDR_UNIT, - so->so_replay.rp_buf, len); - } else { - so->so_replay.rp_buflen = 0; + if (len > NFSD4_REPLAY_ISIZE) { + char *buf = kmalloc(len, GFP_KERNEL); + + nfs4_replay_free_cache(&so->so_replay); + if (buf) { + so->so_replay.rp_buf = buf; + } else { + /* rp_buflen already zeroed; skip caching */ + goto status; + } + } else if (so->so_replay.rp_buf != so->so_replay.rp_ibuf) { + nfs4_replay_free_cache(&so->so_replay); } + so->so_replay.rp_buflen = len; + read_bytes_from_xdr_buf(xdr->buf, + op_status_offset + XDR_UNIT, + so->so_replay.rp_buf, len); } status: op->status = nfsd4_map_status(op->status, diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c index ab13ee9c7fd8..154468ceccdc 100644 --- a/fs/nfsd/nfscache.c +++ b/fs/nfsd/nfscache.c @@ -467,10 +467,11 @@ int nfsd_cache_lookup(struct svc_rqst *rqstp, unsigned int start, unsigned int len, struct nfsd_cacherep **cacherep) { struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + struct nfsd_thread_local_info *ntli = rqstp->rq_private; struct nfsd_cacherep *rp, *found; __wsum csum; struct nfsd_drc_bucket *b; - int type = rqstp->rq_cachetype; + int type = ntli->ntli_cachetype; LIST_HEAD(dispose); int rtn = RC_DOIT; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 71aabdaa1d15..39e7012a60d8 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include #include @@ -1581,6 +1581,32 @@ out_unlock: return ret; } +/** + * nfsd_nl_fh_key_set - helper to copy fh_key from userspace + * @attr: nlattr NFSD_A_SERVER_FH_KEY + * @nn: nfsd_net + * + * Callers should hold nfsd_mutex, returns 0 on success or negative errno. + * Callers must ensure the server is shut down (sv_nrthreads == 0), + * userspace documentation asserts the key may only be set when the server + * is not running. + */ +static int nfsd_nl_fh_key_set(const struct nlattr *attr, struct nfsd_net *nn) +{ + siphash_key_t *fh_key = nn->fh_key; + + if (!fh_key) { + fh_key = kmalloc(sizeof(siphash_key_t), GFP_KERNEL); + if (!fh_key) + return -ENOMEM; + nn->fh_key = fh_key; + } + + fh_key->key[0] = get_unaligned_le64(nla_data(attr)); + fh_key->key[1] = get_unaligned_le64(nla_data(attr) + 8); + return 0; +} + /** * nfsd_nl_threads_set_doit - set the number of running threads * @skb: reply buffer @@ -1622,7 +1648,8 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NFSD_A_SERVER_GRACETIME] || info->attrs[NFSD_A_SERVER_LEASETIME] || - info->attrs[NFSD_A_SERVER_SCOPE]) { + info->attrs[NFSD_A_SERVER_SCOPE] || + info->attrs[NFSD_A_SERVER_FH_KEY]) { ret = -EBUSY; if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads) goto out_unlock; @@ -1651,6 +1678,14 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info) attr = info->attrs[NFSD_A_SERVER_SCOPE]; if (attr) scope = nla_data(attr); + + attr = info->attrs[NFSD_A_SERVER_FH_KEY]; + if (attr) { + ret = nfsd_nl_fh_key_set(attr, nn); + trace_nfsd_ctl_fh_key_set((const char *)nn->fh_key, ret); + if (ret) + goto out_unlock; + } } attr = info->attrs[NFSD_A_SERVER_MIN_THREADS]; @@ -2168,6 +2203,9 @@ static __net_init int nfsd_net_init(struct net *net) int retval; int i; + retval = nfsd_net_cb_init(nn); + if (retval) + return retval; retval = nfsd_export_init(net); if (retval) goto out_export_error; @@ -2208,6 +2246,7 @@ out_repcache_error: out_idmap_error: nfsd_export_shutdown(net); out_export_error: + nfsd_net_cb_shutdown(nn); return retval; } @@ -2237,6 +2276,8 @@ static __net_exit void nfsd_net_exit(struct net *net) { struct nfsd_net *nn = net_generic(net, nfsd_net_id); + kfree_sensitive(nn->fh_key); + nfsd_net_cb_shutdown(nn); nfsd_proc_stat_shutdown(net); percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM); nfsd_idmap_shutdown(net); diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index a01d70953358..7c009f07c90b 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -82,6 +82,11 @@ extern atomic_t nfsd_th_cnt; /* number of available threads */ extern const struct seq_operations nfs_exports_op; +struct nfsd_thread_local_info { + struct nfs4_client **ntli_lease_breaker; + int ntli_cachetype; +}; + /* * Common void argument and result helpers */ @@ -155,6 +160,7 @@ static inline void nfsd_debugfs_exit(void) {} #endif extern bool nfsd_disable_splice_read __read_mostly; +extern bool nfsd_delegts_enabled __read_mostly; enum { /* Any new NFSD_IO enum value must be added at the end */ diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index ee72c9565e4f..429ca5c6ec08 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -11,6 +11,7 @@ #include #include +#include #include "nfsd.h" #include "vfs.h" #include "auth.h" @@ -105,9 +106,12 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp, { /* Check if the request originated from a secure port. */ if (rqstp && !nfsd_originating_port_ok(rqstp, cred, exp)) { - RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); - dprintk("nfsd: request from insecure port %s!\n", - svc_print_addr(rqstp, buf, sizeof(buf))); + if (IS_ENABLED(CONFIG_SUNRPC_DEBUG)) { + char buf[RPC_MAX_ADDRBUFLEN]; + + dprintk("nfsd: request from insecure port %s!\n", + svc_print_addr(rqstp, buf, sizeof(buf))); + } return nfserr_perm; } @@ -137,6 +141,57 @@ static inline __be32 check_pseudo_root(struct dentry *dentry, return nfs_ok; } +/* Size of a file handle MAC, in 4-octet words */ +#define FH_MAC_WORDS (sizeof(__le64) / 4) + +static bool fh_append_mac(struct svc_fh *fhp, struct net *net) +{ + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct knfsd_fh *fh = &fhp->fh_handle; + siphash_key_t *fh_key = nn->fh_key; + __le64 hash; + + if (!fh_key) + goto out_no_key; + if (fh->fh_size + sizeof(hash) > fhp->fh_maxsize) + goto out_no_space; + + hash = cpu_to_le64(siphash(&fh->fh_raw, fh->fh_size, fh_key)); + memcpy(&fh->fh_raw[fh->fh_size], &hash, sizeof(hash)); + fh->fh_size += sizeof(hash); + return true; + +out_no_key: + pr_warn_ratelimited("NFSD: unable to sign filehandles, fh_key not set.\n"); + return false; + +out_no_space: + pr_warn_ratelimited("NFSD: unable to sign filehandles, fh_size %zu would be greater than fh_maxsize %d.\n", + fh->fh_size + sizeof(hash), fhp->fh_maxsize); + return false; +} + +/* + * Verify that the filehandle's MAC was hashed from this filehandle + * given the server's fh_key: + */ +static bool fh_verify_mac(struct svc_fh *fhp, struct net *net) +{ + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct knfsd_fh *fh = &fhp->fh_handle; + siphash_key_t *fh_key = nn->fh_key; + __le64 hash; + + if (!fh_key) { + pr_warn_ratelimited("NFSD: unable to verify signed filehandles, fh_key not set.\n"); + return false; + } + + hash = cpu_to_le64(siphash(&fh->fh_raw, fh->fh_size - sizeof(hash), fh_key)); + return crypto_memneq(&fh->fh_raw[fh->fh_size - sizeof(hash)], + &hash, sizeof(hash)) == 0; +} + /* * Use the given filehandle to look up the corresponding export and * dentry. On success, the results are used to set fh_export and @@ -233,13 +288,21 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct net *net, /* * Look up the dentry using the NFS file handle. */ - error = nfserr_badhandle; - fileid_type = fh->fh_fileid_type; + error = nfserr_stale; - if (fileid_type == FILEID_ROOT) + if (fileid_type == FILEID_ROOT) { + /* We don't sign or verify the root, no per-file identity */ dentry = dget(exp->ex_path.dentry); - else { + } else { + if (exp->ex_flags & NFSEXP_SIGN_FH) { + if (!fh_verify_mac(fhp, net)) { + trace_nfsd_set_fh_dentry_badmac(rqstp, fhp, -ESTALE); + goto out; + } + data_left -= FH_MAC_WORDS; + } + dentry = exportfs_decode_fh_raw(exp->ex_path.mnt, fid, data_left, fileid_type, 0, nfsd_acceptable, exp); @@ -255,6 +318,8 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct net *net, } } } + + error = nfserr_badhandle; if (dentry == NULL) goto out; if (IS_ERR(dentry)) { @@ -495,6 +560,10 @@ static void _fh_update(struct svc_fh *fhp, struct svc_export *exp, fhp->fh_handle.fh_fileid_type = fileid_type > 0 ? fileid_type : FILEID_INVALID; fhp->fh_handle.fh_size += maxsize * 4; + + if (exp->ex_flags & NFSEXP_SIGN_FH) + if (!fh_append_mac(fhp, exp->cd->net)) + fhp->fh_handle.fh_fileid_type = FILEID_INVALID; } else { fhp->fh_handle.fh_fileid_type = FILEID_ROOT; } diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 4a04208393b8..4f1ab3222a4d 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -887,6 +887,7 @@ nfsd(void *vrqstp) struct svc_xprt *perm_sock = list_entry(rqstp->rq_server->sv_permsocks.next, typeof(struct svc_xprt), xpt_list); struct net *net = perm_sock->xpt_net; struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct nfsd_thread_local_info ntli = { }; bool have_mutex = false; /* At this point, the thread shares current->fs @@ -901,6 +902,10 @@ nfsd(void *vrqstp) set_freezable(); + /* use dynamic allocation if ntli should ever become large */ + static_assert(sizeof(struct nfsd_thread_local_info) < 256); + rqstp->rq_private = &ntli; + /* * The main request loop */ @@ -967,6 +972,7 @@ nfsd(void *vrqstp) */ int nfsd_dispatch(struct svc_rqst *rqstp) { + struct nfsd_thread_local_info *ntli = rqstp->rq_private; const struct svc_procedure *proc = rqstp->rq_procinfo; __be32 *statp = rqstp->rq_accept_statp; struct nfsd_cacherep *rp; @@ -977,7 +983,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp) * Give the xdr decoder a chance to change this if it wants * (necessary in the NFSv4.0 compound case) */ - rqstp->rq_cachetype = proc->pc_cachetype; + ntli->ntli_cachetype = proc->pc_cachetype; /* * ->pc_decode advances the argument stream past the NFS @@ -1022,7 +1028,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp) */ smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1); - nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, nfs_reply); + nfsd_cache_update(rqstp, rp, ntli->ntli_cachetype, nfs_reply); out_cached_reply: return 1; diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index fc262ceafca9..ae71e0621317 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -605,7 +605,7 @@ svcxdr_encode_entry_common(struct nfsd_readdirres *resp, const char *name, * * Return values: * %0: Entry was successfully encoded. - * %-EINVAL: An encoding problem occured, secondary status code in resp->common.err + * %-EINVAL: An encoding problem occurred, secondary status code in resp->common.err * * On exit, the following fields are updated: * - resp->xdr diff --git a/fs/nfsd/pnfs.h b/fs/nfsd/pnfs.h index db9af780438b..f7bee4dc5d3d 100644 --- a/fs/nfsd/pnfs.h +++ b/fs/nfsd/pnfs.h @@ -11,6 +11,9 @@ struct xdr_stream; +/* Cap exponential backoff between fence retries at 3 minutes */ +#define MAX_FENCE_DELAY ((unsigned int)(3 * 60 * HZ)) + struct nfsd4_deviceid_map { struct list_head hash; u64 idx; @@ -38,7 +41,7 @@ struct nfsd4_layout_ops { struct svc_rqst *rqstp, struct nfsd4_layoutcommit *lcp); - void (*fence_client)(struct nfs4_layout_stateid *ls, + bool (*fence_client)(struct nfs4_layout_stateid *ls, struct nfsd_file *file); }; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index c0ca115c3b74..c5ccea64c281 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -123,7 +123,7 @@ struct nfs4_stid { #define SC_TYPE_LAYOUT BIT(3) unsigned short sc_type; -/* state_lock protects sc_status for delegation stateids. +/* nn->deleg_lock protects sc_status for delegation stateids. * ->cl_lock protects sc_status for open and lock stateids. * ->st_mutex also protect sc_status for open stateids. * ->ls_lock protects sc_status for layout stateids. @@ -456,6 +456,7 @@ struct nfs4_client { struct list_head cl_lru; /* tail queue */ #ifdef CONFIG_NFSD_PNFS struct list_head cl_lo_states; /* outstanding layout states */ + bool cl_fence_retry_warn; #endif struct xdr_netobj cl_name; /* id generated by client */ nfs4_verifier cl_verifier; /* generated by client */ @@ -527,6 +528,10 @@ struct nfs4_client { struct nfsd4_cb_recall_any *cl_ra; time64_t cl_ra_time; +#ifdef CONFIG_NFSD_SCSILAYOUT + struct xarray cl_dev_fences; + struct mutex cl_fence_mutex; +#endif }; /* struct nfs4_client_reset @@ -549,10 +554,10 @@ struct nfs4_client_reclaim { * ~32(deleg. ace) = 112 bytes * * Some responses can exceed this. A LOCK denial includes the conflicting - * lock owner, which can be up to 1024 bytes (NFS4_OPAQUE_LIMIT). Responses - * larger than REPLAY_ISIZE are not cached in rp_ibuf; only rp_status is - * saved. Enlarging this constant increases the size of every - * nfs4_stateowner. + * lock owner, which can be up to 1024 bytes (NFS4_OPAQUE_LIMIT). When a + * response exceeds REPLAY_ISIZE, a buffer is dynamically allocated. If + * that allocation fails, only rp_status is saved. Enlarging this constant + * increases the size of every nfs4_stateowner. */ #define NFSD4_REPLAY_ISIZE 112 @@ -564,12 +569,14 @@ struct nfs4_client_reclaim { struct nfs4_replay { __be32 rp_status; unsigned int rp_buflen; - char *rp_buf; + char *rp_buf; /* rp_ibuf or kmalloc'd */ struct knfsd_fh rp_openfh; int rp_locked; char rp_ibuf[NFSD4_REPLAY_ISIZE]; }; +extern void nfs4_replay_free_cache(struct nfs4_replay *rp); + struct nfs4_stateowner; struct nfs4_stateowner_operations { @@ -742,6 +749,10 @@ struct nfs4_layout_stateid { stateid_t ls_recall_sid; bool ls_recalled; struct mutex ls_mutex; + + struct delayed_work ls_fence_work; + unsigned int ls_fence_delay; + bool ls_fenced; }; static inline struct nfs4_layout_stateid *layoutstateid(struct nfs4_stid *s) @@ -832,6 +843,7 @@ extern void nfsd4_shutdown_copy(struct nfs4_client *clp); void nfsd4_put_client(struct nfs4_client *clp); void nfsd4_async_copy_reaper(struct nfsd_net *nn); bool nfsd4_has_active_async_copies(struct nfs4_client *clp); +void nfsd_update_cmtime_attr(struct file *f, unsigned int flags); extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, struct nfsd_net *nn); extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn); @@ -851,6 +863,8 @@ struct nfsd_file *find_any_file(struct nfs4_file *f); #ifdef CONFIG_NFSD_V4 void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb); void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb); +int nfsd_net_cb_init(struct nfsd_net *nn); +void nfsd_net_cb_shutdown(struct nfsd_net *nn); #else static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) { @@ -858,6 +872,13 @@ static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block * static inline void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb) { } +static inline int nfsd_net_cb_init(struct nfsd_net *nn) +{ + return 0; +} +static inline void nfsd_net_cb_shutdown(struct nfsd_net *nn) +{ +} #endif /* grace period management */ diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index d1d0b0dd0545..5ad38f50836d 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -373,6 +373,7 @@ DEFINE_EVENT_CONDITION(nfsd_fh_err_class, nfsd_##name, \ DEFINE_NFSD_FH_ERR_EVENT(set_fh_dentry_badexport); DEFINE_NFSD_FH_ERR_EVENT(set_fh_dentry_badhandle); +DEFINE_NFSD_FH_ERR_EVENT(set_fh_dentry_badmac); TRACE_EVENT(nfsd_exp_find_key, TP_PROTO(const struct svc_expkey *key, @@ -2240,6 +2241,28 @@ TRACE_EVENT(nfsd_end_grace, ) ); +TRACE_EVENT(nfsd_ctl_fh_key_set, + TP_PROTO( + const char *key, + int result + ), + TP_ARGS(key, result), + TP_STRUCT__entry( + __field(u32, key_hash) + __field(int, result) + ), + TP_fast_assign( + if (key) + __entry->key_hash = ~crc32_le(0xFFFFFFFF, key, 16); + else + __entry->key_hash = 0; + __entry->result = result; + ), + TP_printk("key=0x%08x result=%d", + __entry->key_hash, __entry->result + ) +); + DECLARE_EVENT_CLASS(nfsd_copy_class, TP_PROTO( const struct nfsd4_copy *copy diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 417e9ad9fbb3..9a4124c77e04 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -752,6 +752,7 @@ struct nfsd4_copy { struct nfsd_file *nf_src; struct nfsd_file *nf_dst; + bool attr_update; copy_stateid_t cp_stateid; diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index bfe884d624e7..38290b9c07f7 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -457,7 +457,7 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, /* * Unlike file_handle, type and len of struct fanotify_fh are u8. * Traditionally, filesystem return handle_type < 0xff, but there - * is no enforecement for that in vfs. + * is no enforcement for that in vfs. */ BUILD_BUG_ON(MAX_HANDLE_SZ > 0xff || FILEID_INVALID > 0xff); if (type <= 0 || type >= FILEID_INVALID || fh_len != dwords << 2) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 9995de1710e5..2dac70b99b0d 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -76,7 +76,7 @@ void fsnotify_set_children_dentry_flags(struct inode *inode) spin_lock(&inode->i_lock); /* run all of the dentries associated with this inode. Since this is a * directory, there damn well better only be one item on this list */ - hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) { + for_each_alias(alias, inode) { struct dentry *child; /* run all of the children of the original inode and fix their @@ -388,7 +388,7 @@ static struct fsnotify_mark *fsnotify_first_mark(struct fsnotify_mark_connector return hlist_entry_safe(node, struct fsnotify_mark, obj_list); } -static struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark) +struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark) { struct hlist_node *node = NULL; diff --git a/fs/notify/mark.c b/fs/notify/mark.c index c2ed5b11b0fe..e256b420100d 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -238,7 +238,12 @@ static struct inode *fsnotify_update_iref(struct fsnotify_mark_connector *conn, return inode; } -static void *__fsnotify_recalc_mask(struct fsnotify_mark_connector *conn) +/* + * Calculate mask of events for a list of marks. + * + * Return true if any of the attached marks want to hold an inode reference. + */ +static bool __fsnotify_recalc_mask(struct fsnotify_mark_connector *conn) { u32 new_mask = 0; bool want_iref = false; @@ -262,6 +267,34 @@ static void *__fsnotify_recalc_mask(struct fsnotify_mark_connector *conn) */ WRITE_ONCE(*fsnotify_conn_mask_p(conn), new_mask); + return want_iref; +} + +/* + * Calculate mask of events for a list of marks after attach/modify mark + * and get an inode reference for the connector if needed. + * + * A concurrent add of evictable mark and detach of non-evictable mark can + * lead to __fsnotify_recalc_mask() returning false want_iref, but in this + * case we defer clearing iref to fsnotify_recalc_mask_clear_iref() called + * from fsnotify_put_mark(). + */ +static void fsnotify_recalc_mask_set_iref(struct fsnotify_mark_connector *conn) +{ + bool has_iref = conn->flags & FSNOTIFY_CONN_FLAG_HAS_IREF; + bool want_iref = __fsnotify_recalc_mask(conn) || has_iref; + + (void) fsnotify_update_iref(conn, want_iref); +} + +/* + * Calculate mask of events for a list of marks after detach mark + * and return the inode object if its reference is no longer needed. + */ +static void *fsnotify_recalc_mask_clear_iref(struct fsnotify_mark_connector *conn) +{ + bool want_iref = __fsnotify_recalc_mask(conn); + return fsnotify_update_iref(conn, want_iref); } @@ -298,7 +331,7 @@ void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn) spin_lock(&conn->lock); update_children = !fsnotify_conn_watches_children(conn); - __fsnotify_recalc_mask(conn); + fsnotify_recalc_mask_set_iref(conn); update_children &= fsnotify_conn_watches_children(conn); spin_unlock(&conn->lock); /* @@ -419,7 +452,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) /* Update watched objects after detaching mark */ if (sb) fsnotify_update_sb_watchers(sb, conn); - objp = __fsnotify_recalc_mask(conn); + objp = fsnotify_recalc_mask_clear_iref(conn); type = conn->type; } WRITE_ONCE(mark->connector, NULL); @@ -457,9 +490,6 @@ EXPORT_SYMBOL_GPL(fsnotify_put_mark); */ static bool fsnotify_get_mark_safe(struct fsnotify_mark *mark) { - if (!mark) - return true; - if (refcount_inc_not_zero(&mark->refcnt)) { spin_lock(&mark->lock); if (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED) { @@ -500,15 +530,22 @@ bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info) int type; fsnotify_foreach_iter_type(type) { + struct fsnotify_mark *mark = iter_info->marks[type]; + /* This can fail if mark is being removed */ - if (!fsnotify_get_mark_safe(iter_info->marks[type])) { - __release(&fsnotify_mark_srcu); - goto fail; + while (mark && !fsnotify_get_mark_safe(mark)) { + if (mark->group == iter_info->current_group) { + __release(&fsnotify_mark_srcu); + goto fail; + } + /* This is a mark in an unrelated group, skip */ + mark = fsnotify_next_mark(mark); + iter_info->marks[type] = mark; } } /* - * Now that both marks are pinned by refcount in the inode / vfsmount + * Now that all marks are pinned by refcount in the inode / vfsmount / etc * lists, we can drop SRCU lock, and safely resume the list iteration * once userspace returns. */ diff --git a/fs/ntfs/Kconfig b/fs/ntfs/Kconfig new file mode 100644 index 000000000000..6a6acde9ba91 --- /dev/null +++ b/fs/ntfs/Kconfig @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0-only +config NTFS_FS + tristate "NTFS file system support" + select NLS + select FS_IOMAP + help + NTFS is the file system of Microsoft Windows NT, 2000, XP and 2003. + This allows you to mount devices formatted with the ntfs file system. + + To compile this as a module, choose M here: the module will be called + ntfs. + +config NTFS_DEBUG + bool "NTFS debugging support" + depends on NTFS_FS + help + If you are experiencing any problems with the NTFS file system, say + Y here. This will result in additional consistency checks to be + performed by the driver as well as additional debugging messages to + be written to the system log. Note that debugging messages are + disabled by default. To enable them, supply the option debug_msgs=1 + at the kernel command line when booting the kernel or as an option + to insmod when loading the ntfs module. Once the driver is active, + you can enable debugging messages by doing (as root): + echo 1 > /proc/sys/fs/ntfs-debug + Replacing the "1" with "0" would disable debug messages. + + If you leave debugging messages disabled, this results in little + overhead, but enabling debug messages results in very significant + slowdown of the system. + + When reporting bugs, please try to have available a full dump of + debugging messages while the misbehaviour was occurring. + +config NTFS_FS_POSIX_ACL + bool "NTFS POSIX Access Control Lists" + depends on NTFS_FS + select FS_POSIX_ACL + help + POSIX Access Control Lists (ACLs) support additional access rights + for users and groups beyond the standard owner/group/world scheme. + + This option enables ACL support for ntfs, providing functional parity + with ntfs3 drivier. + + NOTE: this is linux only feature. Windows will ignore these ACLs. + + If you don't know what Access Control Lists are, say N. diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile new file mode 100644 index 000000000000..0ce4d9a9388a --- /dev/null +++ b/fs/ntfs/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_NTFS_FS) += ntfs.o + +ntfs-y := aops.o attrib.o collate.o dir.o file.o index.o inode.o \ + mft.o mst.o namei.o runlist.o super.o unistr.o attrlist.o ea.o \ + upcase.o bitmap.o lcnalloc.o logfile.o reparse.o compress.o \ + iomap.o debug.o sysctl.o quota.o object_id.o bdev-io.o + +ccflags-$(CONFIG_NTFS_DEBUG) += -DDEBUG diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c new file mode 100644 index 000000000000..1fbf832ad165 --- /dev/null +++ b/fs/ntfs/aops.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel address space operations and page cache handling. + * + * Copyright (c) 2001-2014 Anton Altaparmakov and Tuxera Inc. + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#include + +#include "attrib.h" +#include "mft.h" +#include "ntfs.h" +#include "debug.h" +#include "iomap.h" + +static void ntfs_iomap_read_end_io(struct bio *bio) +{ + int error = blk_status_to_errno(bio->bi_status); + struct folio_iter iter; + + bio_for_each_folio_all(iter, bio) { + struct folio *folio = iter.folio; + struct ntfs_inode *ni = NTFS_I(folio->mapping->host); + s64 init_size; + loff_t pos = folio_pos(folio); + + init_size = ni->initialized_size; + if (pos + iter.offset < init_size && + pos + iter.offset + iter.length > init_size) + folio_zero_segment(folio, offset_in_folio(folio, init_size), + iter.offset + iter.length); + + iomap_finish_folio_read(folio, iter.offset, iter.length, error); + } + bio_put(bio); +} + +static void ntfs_iomap_bio_submit_read(const struct iomap_iter *iter, + struct iomap_read_folio_ctx *ctx) +{ + struct bio *bio = ctx->read_ctx; + bio->bi_end_io = ntfs_iomap_read_end_io; + submit_bio(bio); +} + +static const struct iomap_read_ops ntfs_iomap_bio_read_ops = { + .read_folio_range = iomap_bio_read_folio_range, + .submit_read = ntfs_iomap_bio_submit_read, +}; + +/* + * ntfs_read_folio - Read data for a folio from the device + * @file: open file to which the folio @folio belongs or NULL + * @folio: page cache folio to fill with data + * + * This function handles reading data into the page cache. It first checks + * for specific ntfs attribute type like encryption and compression. + * + * - If the attribute is encrypted, access is denied (-EACCES) because + * decryption is not supported in this path. + * - If the attribute is non-resident and compressed, the read operation is + * delegated to ntfs_read_compressed_block(). + * - For normal resident or non-resident attribute, it utilizes the generic + * iomap infrastructure via iomap_bio_read_folio() to perform the I/O. + * + * Return: 0 on success, or -errno on error. + */ +static int ntfs_read_folio(struct file *file, struct folio *folio) +{ + struct ntfs_inode *ni = NTFS_I(folio->mapping->host); + struct iomap_read_folio_ctx ctx = { + .cur_folio = folio, + .ops = &ntfs_iomap_bio_read_ops, + }; + + /* + * Only $DATA attributes can be encrypted and only unnamed $DATA + * attributes can be compressed. Index root can have the flags set but + * this means to create compressed/encrypted files, not that the + * attribute is compressed/encrypted. Note we need to check for + * AT_INDEX_ALLOCATION since this is the type of both directory and + * index inodes. + */ + if (ni->type != AT_INDEX_ALLOCATION) { + /* + * EFS-encrypted files are not supported. + * (decryption/encryption is not implemented yet) + */ + if (NInoEncrypted(ni)) { + folio_unlock(folio); + return -EOPNOTSUPP; + } + /* Compressed data streams are handled in compress.c. */ + if (NInoNonResident(ni) && NInoCompressed(ni)) + return ntfs_read_compressed_block(folio); + } + + iomap_read_folio(&ntfs_read_iomap_ops, &ctx, NULL); + return 0; +} + +/* + * ntfs_bmap - map logical file block to physical device block + * @mapping: address space mapping to which the block to be mapped belongs + * @block: logical block to map to its physical device block + * + * For regular, non-resident files (i.e. not compressed and not encrypted), map + * the logical @block belonging to the file described by the address space + * mapping @mapping to its physical device block. + * + * The size of the block is equal to the @s_blocksize field of the super block + * of the mounted file system which is guaranteed to be smaller than or equal + * to the cluster size thus the block is guaranteed to fit entirely inside the + * cluster which means we do not need to care how many contiguous bytes are + * available after the beginning of the block. + * + * Return the physical device block if the mapping succeeded or 0 if the block + * is sparse or there was an error. + * + * Note: This is a problem if someone tries to run bmap() on $Boot system file + * as that really is in block zero but there is nothing we can do. bmap() is + * just broken in that respect (just like it cannot distinguish sparse from + * not available or error). + */ +static sector_t ntfs_bmap(struct address_space *mapping, sector_t block) +{ + s64 ofs, size; + loff_t i_size; + s64 lcn; + unsigned long blocksize, flags; + struct ntfs_inode *ni = NTFS_I(mapping->host); + struct ntfs_volume *vol = ni->vol; + unsigned int delta; + unsigned char blocksize_bits; + + ntfs_debug("Entering for mft_no 0x%llx, logical block 0x%llx.", + ni->mft_no, (unsigned long long)block); + if (ni->type != AT_DATA || !NInoNonResident(ni) || NInoEncrypted(ni) || + NInoMstProtected(ni)) { + ntfs_error(vol->sb, "BMAP does not make sense for %s attributes, returning 0.", + (ni->type != AT_DATA) ? "non-data" : + (!NInoNonResident(ni) ? "resident" : + "encrypted")); + return 0; + } + /* None of these can happen. */ + blocksize = vol->sb->s_blocksize; + blocksize_bits = vol->sb->s_blocksize_bits; + ofs = (s64)block << blocksize_bits; + read_lock_irqsave(&ni->size_lock, flags); + size = ni->initialized_size; + i_size = i_size_read(VFS_I(ni)); + read_unlock_irqrestore(&ni->size_lock, flags); + /* + * If the offset is outside the initialized size or the block straddles + * the initialized size then pretend it is a hole unless the + * initialized size equals the file size. + */ + if (unlikely(ofs >= size || (ofs + blocksize > size && size < i_size))) + goto hole; + down_read(&ni->runlist.lock); + lcn = ntfs_attr_vcn_to_lcn_nolock(ni, ntfs_bytes_to_cluster(vol, ofs), + false); + up_read(&ni->runlist.lock); + if (unlikely(lcn < LCN_HOLE)) { + /* + * Step down to an integer to avoid gcc doing a long long + * comparision in the switch when we know @lcn is between + * LCN_HOLE and LCN_EIO (i.e. -1 to -5). + * + * Otherwise older gcc (at least on some architectures) will + * try to use __cmpdi2() which is of course not available in + * the kernel. + */ + switch ((int)lcn) { + case LCN_ENOENT: + /* + * If the offset is out of bounds then pretend it is a + * hole. + */ + goto hole; + case LCN_ENOMEM: + ntfs_error(vol->sb, + "Not enough memory to complete mapping for inode 0x%llx. Returning 0.", + ni->mft_no); + break; + default: + ntfs_error(vol->sb, + "Failed to complete mapping for inode 0x%llx. Run chkdsk. Returning 0.", + ni->mft_no); + break; + } + return 0; + } + if (lcn < 0) { + /* It is a hole. */ +hole: + ntfs_debug("Done (returning hole)."); + return 0; + } + /* + * The block is really allocated and fullfils all our criteria. + * Convert the cluster to units of block size and return the result. + */ + delta = ofs & vol->cluster_size_mask; + if (unlikely(sizeof(block) < sizeof(lcn))) { + block = lcn = (ntfs_cluster_to_bytes(vol, lcn) + delta) >> + blocksize_bits; + /* If the block number was truncated return 0. */ + if (unlikely(block != lcn)) { + ntfs_error(vol->sb, + "Physical block 0x%llx is too large to be returned, returning 0.", + (long long)lcn); + return 0; + } + } else + block = (ntfs_cluster_to_bytes(vol, lcn) + delta) >> + blocksize_bits; + ntfs_debug("Done (returning block 0x%llx).", (unsigned long long)lcn); + return block; +} + +static void ntfs_readahead(struct readahead_control *rac) +{ + struct address_space *mapping = rac->mapping; + struct inode *inode = mapping->host; + struct ntfs_inode *ni = NTFS_I(inode); + struct iomap_read_folio_ctx ctx = { + .ops = &ntfs_iomap_bio_read_ops, + .rac = rac, + }; + + /* + * Resident files are not cached in the page cache, + * and readahead is not implemented for compressed files. + */ + if (!NInoNonResident(ni) || NInoCompressed(ni)) + return; + iomap_readahead(&ntfs_read_iomap_ops, &ctx, NULL); +} + +static int ntfs_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct inode *inode = mapping->host; + struct ntfs_inode *ni = NTFS_I(inode); + struct iomap_writepage_ctx wpc = { + .inode = mapping->host, + .wbc = wbc, + .ops = &ntfs_writeback_ops, + }; + + if (NVolShutdown(ni->vol)) + return -EIO; + + if (!NInoNonResident(ni)) + return 0; + + /* + * EFS-encrypted files are not supported. + * (decryption/encryption is not implemented yet) + */ + if (NInoEncrypted(ni)) { + ntfs_debug("Encrypted I/O not supported"); + return -EOPNOTSUPP; + } + + return iomap_writepages(&wpc); +} + +static int ntfs_swap_activate(struct swap_info_struct *sis, + struct file *swap_file, sector_t *span) +{ + return iomap_swapfile_activate(sis, swap_file, span, + &ntfs_read_iomap_ops); +} + +const struct address_space_operations ntfs_aops = { + .read_folio = ntfs_read_folio, + .readahead = ntfs_readahead, + .writepages = ntfs_writepages, + .dirty_folio = iomap_dirty_folio, + .bmap = ntfs_bmap, + .migrate_folio = filemap_migrate_folio, + .is_partially_uptodate = iomap_is_partially_uptodate, + .error_remove_folio = generic_error_remove_folio, + .release_folio = iomap_release_folio, + .invalidate_folio = iomap_invalidate_folio, + .swap_activate = ntfs_swap_activate, +}; + +const struct address_space_operations ntfs_mft_aops = { + .read_folio = ntfs_read_folio, + .readahead = ntfs_readahead, + .writepages = ntfs_mft_writepages, + .dirty_folio = iomap_dirty_folio, + .bmap = ntfs_bmap, + .migrate_folio = filemap_migrate_folio, + .is_partially_uptodate = iomap_is_partially_uptodate, + .error_remove_folio = generic_error_remove_folio, + .release_folio = iomap_release_folio, + .invalidate_folio = iomap_invalidate_folio, +}; diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c new file mode 100644 index 000000000000..97b660eaa00c --- /dev/null +++ b/fs/ntfs/attrib.c @@ -0,0 +1,5553 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS attribute operations. + * + * Copyright (c) 2001-2012 Anton Altaparmakov and Tuxera Inc. + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. + * + * Part of this file is based on code from the NTFS-3G. + * and is copyrighted by the respective authors below: + * Copyright (c) 2000-2010 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2007-2021 Jean-Pierre Andre + * Copyright (c) 2010 Erik Larsson + */ + +#include +#include + +#include "attrib.h" +#include "attrlist.h" +#include "lcnalloc.h" +#include "debug.h" +#include "mft.h" +#include "ntfs.h" +#include "iomap.h" + +__le16 AT_UNNAMED[] = { cpu_to_le16('\0') }; + +/* + * Maximum size allowed for reading attributes by ntfs_attr_readall(). + * Extended attribute, reparse point are not expected to be larger than this size. + */ + +#define NTFS_ATTR_READALL_MAX_SIZE (64 * 1024) + +/* + * ntfs_map_runlist_nolock - map (a part of) a runlist of an ntfs inode + * @ni: ntfs inode for which to map (part of) a runlist + * @vcn: map runlist part containing this vcn + * @ctx: active attribute search context if present or NULL if not + * + * Map the part of a runlist containing the @vcn of the ntfs inode @ni. + * + * If @ctx is specified, it is an active search context of @ni and its base mft + * record. This is needed when ntfs_map_runlist_nolock() encounters unmapped + * runlist fragments and allows their mapping. If you do not have the mft + * record mapped, you can specify @ctx as NULL and ntfs_map_runlist_nolock() + * will perform the necessary mapping and unmapping. + * + * Note, ntfs_map_runlist_nolock() saves the state of @ctx on entry and + * restores it before returning. Thus, @ctx will be left pointing to the same + * attribute on return as on entry. However, the actual pointers in @ctx may + * point to different memory locations on return, so you must remember to reset + * any cached pointers from the @ctx, i.e. after the call to + * ntfs_map_runlist_nolock(), you will probably want to do: + * m = ctx->mrec; + * a = ctx->attr; + * Assuming you cache ctx->attr in a variable @a of type struct attr_record * + * and that you cache ctx->mrec in a variable @m of type struct mft_record *. + * + * Return 0 on success and -errno on error. There is one special error code + * which is not an error as such. This is -ENOENT. It means that @vcn is out + * of bounds of the runlist. + * + * Note the runlist can be NULL after this function returns if @vcn is zero and + * the attribute has zero allocated size, i.e. there simply is no runlist. + * + * WARNING: If @ctx is supplied, regardless of whether success or failure is + * returned, you need to check IS_ERR(@ctx->mrec) and if 'true' the @ctx + * is no longer valid, i.e. you need to either call + * ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it. + * In that case PTR_ERR(@ctx->mrec) will give you the error code for + * why the mapping of the old inode failed. + * + * Locking: - The runlist described by @ni must be locked for writing on entry + * and is locked on return. Note the runlist will be modified. + * - If @ctx is NULL, the base mft record of @ni must not be mapped on + * entry and it will be left unmapped on return. + * - If @ctx is not NULL, the base mft record must be mapped on entry + * and it will be left mapped on return. + */ +int ntfs_map_runlist_nolock(struct ntfs_inode *ni, s64 vcn, struct ntfs_attr_search_ctx *ctx) +{ + s64 end_vcn; + unsigned long flags; + struct ntfs_inode *base_ni; + struct mft_record *m; + struct attr_record *a; + struct runlist_element *rl; + struct folio *put_this_folio = NULL; + int err = 0; + bool ctx_is_temporary = false, ctx_needs_reset = false; + struct ntfs_attr_search_ctx old_ctx = { NULL, }; + size_t new_rl_count; + + ntfs_debug("Mapping runlist part containing vcn 0x%llx.", + (unsigned long long)vcn); + if (!NInoAttr(ni)) + base_ni = ni; + else + base_ni = ni->ext.base_ntfs_ino; + if (!ctx) { + ctx_is_temporary = ctx_needs_reset = true; + m = map_mft_record(base_ni); + if (IS_ERR(m)) + return PTR_ERR(m); + ctx = ntfs_attr_get_search_ctx(base_ni, m); + if (unlikely(!ctx)) { + err = -ENOMEM; + goto err_out; + } + } else { + s64 allocated_size_vcn; + + WARN_ON(IS_ERR(ctx->mrec)); + a = ctx->attr; + if (!a->non_resident) { + err = -EIO; + goto err_out; + } + end_vcn = le64_to_cpu(a->data.non_resident.highest_vcn); + read_lock_irqsave(&ni->size_lock, flags); + allocated_size_vcn = + ntfs_bytes_to_cluster(ni->vol, ni->allocated_size); + read_unlock_irqrestore(&ni->size_lock, flags); + if (!a->data.non_resident.lowest_vcn && end_vcn <= 0) + end_vcn = allocated_size_vcn - 1; + /* + * If we already have the attribute extent containing @vcn in + * @ctx, no need to look it up again. We slightly cheat in + * that if vcn exceeds the allocated size, we will refuse to + * map the runlist below, so there is definitely no need to get + * the right attribute extent. + */ + if (vcn >= allocated_size_vcn || (a->type == ni->type && + a->name_length == ni->name_len && + !memcmp((u8 *)a + le16_to_cpu(a->name_offset), + ni->name, ni->name_len) && + le64_to_cpu(a->data.non_resident.lowest_vcn) + <= vcn && end_vcn >= vcn)) + ctx_needs_reset = false; + else { + /* Save the old search context. */ + old_ctx = *ctx; + /* + * If the currently mapped (extent) inode is not the + * base inode we will unmap it when we reinitialize the + * search context which means we need to get a + * reference to the page containing the mapped mft + * record so we do not accidentally drop changes to the + * mft record when it has not been marked dirty yet. + */ + if (old_ctx.base_ntfs_ino && old_ctx.ntfs_ino != + old_ctx.base_ntfs_ino) { + put_this_folio = old_ctx.ntfs_ino->folio; + folio_get(put_this_folio); + } + /* + * Reinitialize the search context so we can lookup the + * needed attribute extent. + */ + ntfs_attr_reinit_search_ctx(ctx); + ctx_needs_reset = true; + } + } + if (ctx_needs_reset) { + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, vcn, NULL, 0, ctx); + if (unlikely(err)) { + if (err == -ENOENT) + err = -EIO; + goto err_out; + } + WARN_ON(!ctx->attr->non_resident); + } + a = ctx->attr; + /* + * Only decompress the mapping pairs if @vcn is inside it. Otherwise + * we get into problems when we try to map an out of bounds vcn because + * we then try to map the already mapped runlist fragment and + * ntfs_mapping_pairs_decompress() fails. + */ + end_vcn = le64_to_cpu(a->data.non_resident.highest_vcn) + 1; + if (unlikely(vcn && vcn >= end_vcn)) { + err = -ENOENT; + goto err_out; + } + rl = ntfs_mapping_pairs_decompress(ni->vol, a, &ni->runlist, &new_rl_count); + if (IS_ERR(rl)) + err = PTR_ERR(rl); + else { + ni->runlist.rl = rl; + ni->runlist.count = new_rl_count; + } +err_out: + if (ctx_is_temporary) { + if (likely(ctx)) + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(base_ni); + } else if (ctx_needs_reset) { + /* + * If there is no attribute list, restoring the search context + * is accomplished simply by copying the saved context back over + * the caller supplied context. If there is an attribute list, + * things are more complicated as we need to deal with mapping + * of mft records and resulting potential changes in pointers. + */ + if (NInoAttrList(base_ni)) { + /* + * If the currently mapped (extent) inode is not the + * one we had before, we need to unmap it and map the + * old one. + */ + if (ctx->ntfs_ino != old_ctx.ntfs_ino) { + /* + * If the currently mapped inode is not the + * base inode, unmap it. + */ + if (ctx->base_ntfs_ino && ctx->ntfs_ino != + ctx->base_ntfs_ino) { + unmap_extent_mft_record(ctx->ntfs_ino); + ctx->mrec = ctx->base_mrec; + WARN_ON(!ctx->mrec); + } + /* + * If the old mapped inode is not the base + * inode, map it. + */ + if (old_ctx.base_ntfs_ino && + old_ctx.ntfs_ino != old_ctx.base_ntfs_ino) { +retry_map: + ctx->mrec = map_mft_record(old_ctx.ntfs_ino); + /* + * Something bad has happened. If out + * of memory retry till it succeeds. + * Any other errors are fatal and we + * return the error code in ctx->mrec. + * Let the caller deal with it... We + * just need to fudge things so the + * caller can reinit and/or put the + * search context safely. + */ + if (IS_ERR(ctx->mrec)) { + if (PTR_ERR(ctx->mrec) == -ENOMEM) { + schedule(); + goto retry_map; + } else + old_ctx.ntfs_ino = + old_ctx.base_ntfs_ino; + } + } + } + /* Update the changed pointers in the saved context. */ + if (ctx->mrec != old_ctx.mrec) { + if (!IS_ERR(ctx->mrec)) + old_ctx.attr = (struct attr_record *)( + (u8 *)ctx->mrec + + ((u8 *)old_ctx.attr - + (u8 *)old_ctx.mrec)); + old_ctx.mrec = ctx->mrec; + } + } + /* Restore the search context to the saved one. */ + *ctx = old_ctx; + /* + * We drop the reference on the page we took earlier. In the + * case that IS_ERR(ctx->mrec) is true this means we might lose + * some changes to the mft record that had been made between + * the last time it was marked dirty/written out and now. This + * at this stage is not a problem as the mapping error is fatal + * enough that the mft record cannot be written out anyway and + * the caller is very likely to shutdown the whole inode + * immediately and mark the volume dirty for chkdsk to pick up + * the pieces anyway. + */ + if (put_this_folio) + folio_put(put_this_folio); + } + return err; +} + +/* + * ntfs_map_runlist - map (a part of) a runlist of an ntfs inode + * @ni: ntfs inode for which to map (part of) a runlist + * @vcn: map runlist part containing this vcn + * + * Map the part of a runlist containing the @vcn of the ntfs inode @ni. + * + * Return 0 on success and -errno on error. There is one special error code + * which is not an error as such. This is -ENOENT. It means that @vcn is out + * of bounds of the runlist. + * + * Locking: - The runlist must be unlocked on entry and is unlocked on return. + * - This function takes the runlist lock for writing and may modify + * the runlist. + */ +int ntfs_map_runlist(struct ntfs_inode *ni, s64 vcn) +{ + int err = 0; + + down_write(&ni->runlist.lock); + /* Make sure someone else didn't do the work while we were sleeping. */ + if (likely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) <= + LCN_RL_NOT_MAPPED)) + err = ntfs_map_runlist_nolock(ni, vcn, NULL); + up_write(&ni->runlist.lock); + return err; +} + +struct runlist_element *ntfs_attr_vcn_to_rl(struct ntfs_inode *ni, s64 vcn, s64 *lcn) +{ + struct runlist_element *rl = ni->runlist.rl; + int err; + bool is_retry = false; + + if (!rl) { + err = ntfs_attr_map_whole_runlist(ni); + if (err) + return ERR_PTR(-ENOENT); + rl = ni->runlist.rl; + } + +remap_rl: + /* Seek to element containing target vcn. */ + while (rl->length && rl[1].vcn <= vcn) + rl++; + *lcn = ntfs_rl_vcn_to_lcn(rl, vcn); + + if (*lcn <= LCN_RL_NOT_MAPPED && is_retry == false) { + is_retry = true; + if (!ntfs_map_runlist_nolock(ni, vcn, NULL)) { + rl = ni->runlist.rl; + goto remap_rl; + } + } + + return rl; +} + +/* + * ntfs_attr_vcn_to_lcn_nolock - convert a vcn into a lcn given an ntfs inode + * @ni: ntfs inode of the attribute whose runlist to search + * @vcn: vcn to convert + * @write_locked: true if the runlist is locked for writing + * + * Find the virtual cluster number @vcn in the runlist of the ntfs attribute + * described by the ntfs inode @ni and return the corresponding logical cluster + * number (lcn). + * + * If the @vcn is not mapped yet, the attempt is made to map the attribute + * extent containing the @vcn and the vcn to lcn conversion is retried. + * + * If @write_locked is true the caller has locked the runlist for writing and + * if false for reading. + * + * Since lcns must be >= 0, we use negative return codes with special meaning: + * + * Return code Meaning / Description + * ========================================== + * LCN_HOLE Hole / not allocated on disk. + * LCN_ENOENT There is no such vcn in the runlist, i.e. @vcn is out of bounds. + * LCN_ENOMEM Not enough memory to map runlist. + * LCN_EIO Critical error (runlist/file is corrupt, i/o error, etc). + * + * Locking: - The runlist must be locked on entry and is left locked on return. + * - If @write_locked is 'false', i.e. the runlist is locked for reading, + * the lock may be dropped inside the function so you cannot rely on + * the runlist still being the same when this function returns. + */ +s64 ntfs_attr_vcn_to_lcn_nolock(struct ntfs_inode *ni, const s64 vcn, + const bool write_locked) +{ + s64 lcn; + unsigned long flags; + bool is_retry = false; + + ntfs_debug("Entering for i_ino 0x%llx, vcn 0x%llx, %s_locked.", + ni->mft_no, (unsigned long long)vcn, + write_locked ? "write" : "read"); + if (!ni->runlist.rl) { + read_lock_irqsave(&ni->size_lock, flags); + if (!ni->allocated_size) { + read_unlock_irqrestore(&ni->size_lock, flags); + return LCN_ENOENT; + } + read_unlock_irqrestore(&ni->size_lock, flags); + } +retry_remap: + /* Convert vcn to lcn. If that fails map the runlist and retry once. */ + lcn = ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn); + if (likely(lcn >= LCN_HOLE)) { + ntfs_debug("Done, lcn 0x%llx.", (long long)lcn); + return lcn; + } + if (lcn != LCN_RL_NOT_MAPPED) { + if (lcn != LCN_ENOENT) + lcn = LCN_EIO; + } else if (!is_retry) { + int err; + + if (!write_locked) { + up_read(&ni->runlist.lock); + down_write(&ni->runlist.lock); + if (unlikely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) != + LCN_RL_NOT_MAPPED)) { + up_write(&ni->runlist.lock); + down_read(&ni->runlist.lock); + goto retry_remap; + } + } + err = ntfs_map_runlist_nolock(ni, vcn, NULL); + if (!write_locked) { + up_write(&ni->runlist.lock); + down_read(&ni->runlist.lock); + } + if (likely(!err)) { + is_retry = true; + goto retry_remap; + } + if (err == -ENOENT) + lcn = LCN_ENOENT; + else if (err == -ENOMEM) + lcn = LCN_ENOMEM; + else + lcn = LCN_EIO; + } + if (lcn != LCN_ENOENT) + ntfs_error(ni->vol->sb, "Failed with error code %lli.", + (long long)lcn); + return lcn; +} + +struct runlist_element *__ntfs_attr_find_vcn_nolock(struct runlist *runlist, const s64 vcn) +{ + size_t lower_idx, upper_idx, idx; + struct runlist_element *run; + int rh = runlist->rl_hint; + + if (runlist->count <= 1) + return ERR_PTR(-ENOENT); + + if (runlist->count - 1 > rh && runlist->rl[rh].vcn <= vcn) { + if (vcn < runlist->rl[rh].vcn + runlist->rl[rh].length) + return &runlist->rl[rh]; + if (runlist->count - 2 == rh) + return ERR_PTR(-ENOENT); + + lower_idx = rh + 1; + } else { + run = &runlist->rl[0]; + if (vcn < run->vcn) + return ERR_PTR(-ENOENT); + else if (vcn < run->vcn + run->length) { + runlist->rl_hint = 0; + return run; + } + + lower_idx = 1; + } + + run = &runlist->rl[runlist->count - 2]; + if (vcn >= run->vcn && vcn < run->vcn + run->length) { + runlist->rl_hint = runlist->count - 2; + return run; + } + if (vcn >= run->vcn + run->length) + return ERR_PTR(-ENOENT); + + upper_idx = runlist->count - 2; + + while (lower_idx <= upper_idx) { + idx = (lower_idx + upper_idx) >> 1; + run = &runlist->rl[idx]; + + if (vcn < run->vcn) + upper_idx = idx - 1; + else if (vcn >= run->vcn + run->length) + lower_idx = idx + 1; + else { + runlist->rl_hint = idx; + return run; + } + } + + return ERR_PTR(-ENOENT); +} + +/* + * ntfs_attr_find_vcn_nolock - find a vcn in the runlist of an ntfs inode + * @ni: ntfs inode describing the runlist to search + * @vcn: vcn to find + * @ctx: active attribute search context if present or NULL if not + * + * Find the virtual cluster number @vcn in the runlist described by the ntfs + * inode @ni and return the address of the runlist element containing the @vcn. + * + * If the @vcn is not mapped yet, the attempt is made to map the attribute + * extent containing the @vcn and the vcn to lcn conversion is retried. + * + * If @ctx is specified, it is an active search context of @ni and its base mft + * record. This is needed when ntfs_attr_find_vcn_nolock() encounters unmapped + * runlist fragments and allows their mapping. If you do not have the mft + * record mapped, you can specify @ctx as NULL and ntfs_attr_find_vcn_nolock() + * will perform the necessary mapping and unmapping. + * + * Note, ntfs_attr_find_vcn_nolock() saves the state of @ctx on entry and + * restores it before returning. Thus, @ctx will be left pointing to the same + * attribute on return as on entry. However, the actual pointers in @ctx may + * point to different memory locations on return, so you must remember to reset + * any cached pointers from the @ctx, i.e. after the call to + * ntfs_attr_find_vcn_nolock(), you will probably want to do: + * m = ctx->mrec; + * a = ctx->attr; + * Assuming you cache ctx->attr in a variable @a of type attr_record * and that + * you cache ctx->mrec in a variable @m of type struct mft_record *. + * Note you need to distinguish between the lcn of the returned runlist element + * being >= 0 and LCN_HOLE. In the later case you have to return zeroes on + * read and allocate clusters on write. + */ +struct runlist_element *ntfs_attr_find_vcn_nolock(struct ntfs_inode *ni, const s64 vcn, + struct ntfs_attr_search_ctx *ctx) +{ + unsigned long flags; + struct runlist_element *rl; + int err = 0; + bool is_retry = false; + + ntfs_debug("Entering for i_ino 0x%llx, vcn 0x%llx, with%s ctx.", + ni->mft_no, (unsigned long long)vcn, ctx ? "" : "out"); + if (!ni->runlist.rl) { + read_lock_irqsave(&ni->size_lock, flags); + if (!ni->allocated_size) { + read_unlock_irqrestore(&ni->size_lock, flags); + return ERR_PTR(-ENOENT); + } + read_unlock_irqrestore(&ni->size_lock, flags); + } + +retry_remap: + rl = ni->runlist.rl; + if (likely(rl && vcn >= rl[0].vcn)) { + rl = __ntfs_attr_find_vcn_nolock(&ni->runlist, vcn); + if (IS_ERR(rl)) + err = PTR_ERR(rl); + else if (rl->lcn >= LCN_HOLE) + return rl; + else if (rl->lcn <= LCN_ENOENT) + err = -EIO; + } + if (!err && !is_retry) { + /* + * If the search context is invalid we cannot map the unmapped + * region. + */ + if (ctx && IS_ERR(ctx->mrec)) + err = PTR_ERR(ctx->mrec); + else { + /* + * The @vcn is in an unmapped region, map the runlist + * and retry. + */ + err = ntfs_map_runlist_nolock(ni, vcn, ctx); + if (likely(!err)) { + is_retry = true; + goto retry_remap; + } + } + if (err == -EINVAL) + err = -EIO; + } else if (!err) + err = -EIO; + if (err != -ENOENT) + ntfs_error(ni->vol->sb, "Failed with error code %i.", err); + return ERR_PTR(err); +} + +static u32 ntfs_resident_attr_min_value_length(const __le32 type) +{ + switch (type) { + case AT_STANDARD_INFORMATION: + return offsetof(struct standard_information, ver) + + sizeof(((struct standard_information *)0)->ver.v1.reserved12); + case AT_ATTRIBUTE_LIST: + return offsetof(struct attr_list_entry, name); + case AT_FILE_NAME: + return offsetof(struct file_name_attr, file_name); + case AT_OBJECT_ID: + return sizeof(struct guid); + case AT_SECURITY_DESCRIPTOR: + return sizeof(struct security_descriptor_relative); + case AT_VOLUME_INFORMATION: + return sizeof(struct volume_information); + case AT_INDEX_ROOT: + return sizeof(struct index_root); + case AT_REPARSE_POINT: + return offsetof(struct reparse_point, reparse_data); + case AT_EA_INFORMATION: + return sizeof(struct ea_information); + case AT_EA: + return offsetof(struct ea_attr, ea_name) + 1; + default: + return 0; + } +} + +/* + * ntfs_attr_find - find (next) attribute in mft record + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * You should not need to call this function directly. Use ntfs_attr_lookup() + * instead. + * + * ntfs_attr_find() takes a search context @ctx as parameter and searches the + * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an + * attribute of @type, optionally @name and @val. + * + * If the attribute is found, ntfs_attr_find() returns 0 and @ctx->attr will + * point to the found attribute. + * + * If the attribute is not found, ntfs_attr_find() returns -ENOENT and + * @ctx->attr will point to the attribute before which the attribute being + * searched for would need to be inserted if such an action were to be desired. + * + * On actual error, ntfs_attr_find() returns -EIO. In this case @ctx->attr is + * undefined and in particular do not rely on it not changing. + * + * If @ctx->is_first is 'true', the search begins with @ctx->attr itself. If it + * is 'false', the search begins after @ctx->attr. + * + * If @ic is IGNORE_CASE, the @name comparisson is not case sensitive and + * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record + * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at + * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case + * sensitive. When @name is present, @name_len is the @name length in Unicode + * characters. + * + * If @name is not present (NULL), we assume that the unnamed attribute is + * being searched for. + * + * Finally, the resident attribute value @val is looked for, if present. If + * @val is not present (NULL), @val_len is ignored. + * + * ntfs_attr_find() only searches the specified mft record and it ignores the + * presence of an attribute list attribute (unless it is the one being searched + * for, obviously). If you need to take attribute lists into consideration, + * use ntfs_attr_lookup() instead (see below). This also means that you cannot + * use ntfs_attr_find() to search for extent records of non-resident + * attributes, as extents with lowest_vcn != 0 are usually described by the + * attribute list attribute only. - Note that it is possible that the first + * extent is only in the attribute list while the last extent is in the base + * mft record, so do not rely on being able to find the first extent in the + * base mft record. + * + * Warning: Never use @val when looking for attribute types which can be + * non-resident as this most likely will result in a crash! + */ +static int ntfs_attr_find(const __le32 type, const __le16 *name, + const u32 name_len, const u32 ic, + const u8 *val, const u32 val_len, struct ntfs_attr_search_ctx *ctx) +{ + struct attr_record *a; + struct ntfs_volume *vol = ctx->ntfs_ino->vol; + __le16 *upcase = vol->upcase; + u32 upcase_len = vol->upcase_len; + unsigned int space; + + /* + * Iterate over attributes in mft record starting at @ctx->attr, or the + * attribute following that, if @ctx->is_first is 'true'. + */ + if (ctx->is_first) { + a = ctx->attr; + ctx->is_first = false; + } else + a = (struct attr_record *)((u8 *)ctx->attr + + le32_to_cpu(ctx->attr->length)); + for (;; a = (struct attr_record *)((u8 *)a + le32_to_cpu(a->length))) { + if ((u8 *)a < (u8 *)ctx->mrec || (u8 *)a > (u8 *)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_allocated)) + break; + + space = le32_to_cpu(ctx->mrec->bytes_in_use) - ((u8 *)a - (u8 *)ctx->mrec); + if ((space < offsetof(struct attr_record, data.resident.reserved) + 1 || + space < le32_to_cpu(a->length)) && (space < 4 || a->type != AT_END)) + break; + + ctx->attr = a; + if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > le32_to_cpu(type))) || + a->type == AT_END) + return -ENOENT; + if (unlikely(!a->length)) + break; + if (type == AT_UNUSED) + return 0; + if (a->type != type) + continue; + /* + * If @name is present, compare the two names. If @name is + * missing, assume we want an unnamed attribute. + */ + if (!name || name == AT_UNNAMED) { + /* The search failed if the found attribute is named. */ + if (a->name_length) + return -ENOENT; + } else { + if (a->name_length && ((le16_to_cpu(a->name_offset) + + a->name_length * sizeof(__le16)) > + le32_to_cpu(a->length))) { + ntfs_error(vol->sb, "Corrupt attribute name in MFT record %llu\n", + ctx->ntfs_ino->mft_no); + break; + } + + if (!ntfs_are_names_equal(name, name_len, + (__le16 *)((u8 *)a + le16_to_cpu(a->name_offset)), + a->name_length, ic, upcase, upcase_len)) { + register int rc; + + rc = ntfs_collate_names(name, name_len, + (__le16 *)((u8 *)a + le16_to_cpu(a->name_offset)), + a->name_length, 1, IGNORE_CASE, + upcase, upcase_len); + /* + * If @name collates before a->name, there is no + * matching attribute. + */ + if (rc == -1) + return -ENOENT; + /* If the strings are not equal, continue search. */ + if (rc) + continue; + rc = ntfs_collate_names(name, name_len, + (__le16 *)((u8 *)a + le16_to_cpu(a->name_offset)), + a->name_length, 1, CASE_SENSITIVE, + upcase, upcase_len); + if (rc == -1) + return -ENOENT; + if (rc) + continue; + } + } + + /* Validate attribute's value offset/length */ + if (!a->non_resident) { + u32 min_len; + u32 value_length = le32_to_cpu(a->data.resident.value_length); + u16 value_offset = le16_to_cpu(a->data.resident.value_offset); + + if (value_length > le32_to_cpu(a->length) || + value_offset > le32_to_cpu(a->length) - value_length) + break; + + min_len = ntfs_resident_attr_min_value_length(a->type); + if (min_len && value_length < min_len) { + ntfs_error(vol->sb, + "Too small %#x resident attribute value in MFT record %lld\n", + le32_to_cpu(a->type), (long long)ctx->ntfs_ino->mft_no); + break; + } + } else { + u32 min_len; + u16 mp_offset; + + min_len = offsetof(struct attr_record, data.non_resident.initialized_size) + + sizeof(a->data.non_resident.initialized_size); + if (le32_to_cpu(a->length) < min_len) + break; + + mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset); + if (mp_offset < min_len || + mp_offset > le32_to_cpu(a->length)) + break; + } + + /* + * The names match or @name not present and attribute is + * unnamed. If no @val specified, we have found the attribute + * and are done. + */ + if (!val || a->non_resident) + return 0; + /* @val is present; compare values. */ + else { + u32 value_length = le32_to_cpu(a->data.resident.value_length); + int rc; + + rc = memcmp(val, (u8 *)a + le16_to_cpu( + a->data.resident.value_offset), + min_t(u32, val_len, value_length)); + /* + * If @val collates before the current attribute's + * value, there is no matching attribute. + */ + if (!rc) { + if (val_len == value_length) + return 0; + if (val_len < value_length) + return -ENOENT; + } else if (rc < 0) + return -ENOENT; + } + } + ntfs_error(vol->sb, "mft %#llx, type %#x is corrupt. Run chkdsk.", + (long long)ctx->ntfs_ino->mft_no, le32_to_cpu(type)); + NVolSetErrors(vol); + return -EIO; +} + +void ntfs_attr_name_free(unsigned char **name) +{ + if (*name) { + kfree(*name); + *name = NULL; + } +} + +char *ntfs_attr_name_get(const struct ntfs_volume *vol, const __le16 *uname, + const int uname_len) +{ + unsigned char *name = NULL; + int name_len; + + name_len = ntfs_ucstonls(vol, uname, uname_len, &name, 0); + if (name_len < 0) { + ntfs_error(vol->sb, "ntfs_ucstonls error"); + /* This function when returns -1, memory for name might + * be allocated. So lets free this memory. + */ + ntfs_attr_name_free(&name); + return NULL; + + } else if (name_len > 0) + return name; + + ntfs_attr_name_free(&name); + return NULL; +} + +int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size) +{ + struct inode *attr_vi = NULL; + u8 *al; + struct attr_list_entry *ale; + + if (!al_start || size <= 0) + return -EINVAL; + + attr_vi = ntfs_attr_iget(VFS_I(base_ni), AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (IS_ERR(attr_vi)) { + ntfs_error(base_ni->vol->sb, + "Failed to open an inode for Attribute list, mft = %llu", + base_ni->mft_no); + return PTR_ERR(attr_vi); + } + + if (ntfs_inode_attr_pread(attr_vi, 0, size, al_start) != size) { + iput(attr_vi); + ntfs_error(base_ni->vol->sb, + "Failed to read attribute list, mft = %llu", + base_ni->mft_no); + return -EIO; + } + iput(attr_vi); + + for (al = al_start; al < al_start + size; al += le16_to_cpu(ale->length)) { + ale = (struct attr_list_entry *)al; + if (ale->name_offset != sizeof(struct attr_list_entry)) + break; + if (le16_to_cpu(ale->length) <= ale->name_offset + ale->name_length || + al + le16_to_cpu(ale->length) > al_start + size) + break; + if (ale->type == AT_UNUSED) + break; + if (MSEQNO_LE(ale->mft_reference) == 0) + break; + } + if (al != al_start + size) { + ntfs_error(base_ni->vol->sb, "Corrupt attribute list, mft = %llu", + base_ni->mft_no); + return -EIO; + } + return 0; +} + +/* + * ntfs_external_attr_find - find an attribute in the attribute list of an inode + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * You should not need to call this function directly. Use ntfs_attr_lookup() + * instead. + * + * Find an attribute by searching the attribute list for the corresponding + * attribute list entry. Having found the entry, map the mft record if the + * attribute is in a different mft record/inode, ntfs_attr_find() the attribute + * in there and return it. + * + * On first search @ctx->ntfs_ino must be the base mft record and @ctx must + * have been obtained from a call to ntfs_attr_get_search_ctx(). On subsequent + * calls @ctx->ntfs_ino can be any extent inode, too (@ctx->base_ntfs_ino is + * then the base inode). + * + * After finishing with the attribute/mft record you need to call + * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any + * mapped inodes, etc). + * + * If the attribute is found, ntfs_external_attr_find() returns 0 and + * @ctx->attr will point to the found attribute. @ctx->mrec will point to the + * mft record in which @ctx->attr is located and @ctx->al_entry will point to + * the attribute list entry for the attribute. + * + * If the attribute is not found, ntfs_external_attr_find() returns -ENOENT and + * @ctx->attr will point to the attribute in the base mft record before which + * the attribute being searched for would need to be inserted if such an action + * were to be desired. @ctx->mrec will point to the mft record in which + * @ctx->attr is located and @ctx->al_entry will point to the attribute list + * entry of the attribute before which the attribute being searched for would + * need to be inserted if such an action were to be desired. + * + * Thus to insert the not found attribute, one wants to add the attribute to + * @ctx->mrec (the base mft record) and if there is not enough space, the + * attribute should be placed in a newly allocated extent mft record. The + * attribute list entry for the inserted attribute should be inserted in the + * attribute list attribute at @ctx->al_entry. + * + * On actual error, ntfs_external_attr_find() returns -EIO. In this case + * @ctx->attr is undefined and in particular do not rely on it not changing. + */ +static int ntfs_external_attr_find(const __le32 type, + const __le16 *name, const u32 name_len, + const u32 ic, const s64 lowest_vcn, + const u8 *val, const u32 val_len, struct ntfs_attr_search_ctx *ctx) +{ + struct ntfs_inode *base_ni = ctx->base_ntfs_ino, *ni = ctx->ntfs_ino; + struct ntfs_volume *vol; + struct attr_list_entry *al_entry, *next_al_entry; + u8 *al_start, *al_end; + struct attr_record *a; + __le16 *al_name; + u32 al_name_len; + u32 attr_len, mft_free_len; + bool is_first_search = false; + int err = 0; + static const char *es = " Unmount and run chkdsk."; + + ntfs_debug("Entering for inode 0x%llx, type 0x%x.", ni->mft_no, type); + if (!base_ni) { + /* First call happens with the base mft record. */ + base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino; + ctx->base_mrec = ctx->mrec; + ctx->mapped_base_mrec = ctx->mapped_mrec; + } + if (ni == base_ni) + ctx->base_attr = ctx->attr; + if (type == AT_END) + goto not_found; + vol = base_ni->vol; + al_start = base_ni->attr_list; + al_end = al_start + base_ni->attr_list_size; + if (!ctx->al_entry) { + ctx->al_entry = (struct attr_list_entry *)al_start; + is_first_search = true; + } + /* + * Iterate over entries in attribute list starting at @ctx->al_entry, + * or the entry following that, if @ctx->is_first is 'true'. + */ + if (ctx->is_first) { + al_entry = ctx->al_entry; + ctx->is_first = false; + /* + * If an enumeration and the first attribute is higher than + * the attribute list itself, need to return the attribute list + * attribute. + */ + if ((type == AT_UNUSED) && is_first_search && + le32_to_cpu(al_entry->type) > + le32_to_cpu(AT_ATTRIBUTE_LIST)) + goto find_attr_list_attr; + } else { + /* Check for small entry */ + if (((al_end - (u8 *)ctx->al_entry) < + (long)offsetof(struct attr_list_entry, name)) || + (le16_to_cpu(ctx->al_entry->length) & 7) || + (le16_to_cpu(ctx->al_entry->length) < offsetof(struct attr_list_entry, name))) + goto corrupt; + + al_entry = (struct attr_list_entry *)((u8 *)ctx->al_entry + + le16_to_cpu(ctx->al_entry->length)); + + if ((u8 *)al_entry == al_end) + goto not_found; + + /* Preliminary check for small entry */ + if ((al_end - (u8 *)al_entry) < + (long)offsetof(struct attr_list_entry, name)) + goto corrupt; + + /* + * If this is an enumeration and the attribute list attribute + * is the next one in the enumeration sequence, just return the + * attribute list attribute from the base mft record as it is + * not listed in the attribute list itself. + */ + if ((type == AT_UNUSED) && le32_to_cpu(ctx->al_entry->type) < + le32_to_cpu(AT_ATTRIBUTE_LIST) && + le32_to_cpu(al_entry->type) > + le32_to_cpu(AT_ATTRIBUTE_LIST)) { +find_attr_list_attr: + + /* Check for bogus calls. */ + if (name || name_len || val || val_len || lowest_vcn) + return -EINVAL; + + /* We want the base record. */ + if (ctx->ntfs_ino != base_ni) + unmap_mft_record(ctx->ntfs_ino); + ctx->ntfs_ino = base_ni; + ctx->mapped_mrec = ctx->mapped_base_mrec; + ctx->mrec = ctx->base_mrec; + ctx->is_first = true; + + /* Sanity checks are performed elsewhere. */ + ctx->attr = (struct attr_record *)((u8 *)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + + /* Find the attribute list attribute. */ + err = ntfs_attr_find(AT_ATTRIBUTE_LIST, NULL, 0, + IGNORE_CASE, NULL, 0, ctx); + + /* + * Setup the search context so the correct + * attribute is returned next time round. + */ + ctx->al_entry = al_entry; + ctx->is_first = true; + + /* Got it. Done. */ + if (!err) + return 0; + + /* Error! If other than not found return it. */ + if (err != -ENOENT) + return err; + + /* Not found?!? Absurd! */ + ntfs_error(ctx->ntfs_ino->vol->sb, "Attribute list wasn't found"); + return -EIO; + } + } + for (;; al_entry = next_al_entry) { + /* Out of bounds check. */ + if ((u8 *)al_entry < base_ni->attr_list || + (u8 *)al_entry > al_end) + break; /* Inode is corrupt. */ + ctx->al_entry = al_entry; + /* Catch the end of the attribute list. */ + if ((u8 *)al_entry == al_end) + goto not_found; + + if ((((u8 *)al_entry + offsetof(struct attr_list_entry, name)) > al_end) || + ((u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end) || + (le16_to_cpu(al_entry->length) & 7) || + (le16_to_cpu(al_entry->length) < + offsetof(struct attr_list_entry, name_length)) || + (al_entry->name_length && ((u8 *)al_entry + al_entry->name_offset + + al_entry->name_length * sizeof(__le16)) > al_end)) + break; /* corrupt */ + + next_al_entry = (struct attr_list_entry *)((u8 *)al_entry + + le16_to_cpu(al_entry->length)); + if (type != AT_UNUSED) { + if (le32_to_cpu(al_entry->type) > le32_to_cpu(type)) + goto not_found; + if (type != al_entry->type) + continue; + } + /* + * If @name is present, compare the two names. If @name is + * missing, assume we want an unnamed attribute. + */ + al_name_len = al_entry->name_length; + al_name = (__le16 *)((u8 *)al_entry + al_entry->name_offset); + + /* + * If !@type we want the attribute represented by this + * attribute list entry. + */ + if (type == AT_UNUSED) + goto is_enumeration; + + if (!name || name == AT_UNNAMED) { + if (al_name_len) + goto not_found; + } else if (!ntfs_are_names_equal(al_name, al_name_len, name, + name_len, ic, vol->upcase, vol->upcase_len)) { + register int rc; + + rc = ntfs_collate_names(name, name_len, al_name, + al_name_len, 1, IGNORE_CASE, + vol->upcase, vol->upcase_len); + /* + * If @name collates before al_name, there is no + * matching attribute. + */ + if (rc == -1) + goto not_found; + /* If the strings are not equal, continue search. */ + if (rc) + continue; + + rc = ntfs_collate_names(name, name_len, al_name, + al_name_len, 1, CASE_SENSITIVE, + vol->upcase, vol->upcase_len); + if (rc == -1) + goto not_found; + if (rc) + continue; + } + /* + * The names match or @name not present and attribute is + * unnamed. Now check @lowest_vcn. Continue search if the + * next attribute list entry still fits @lowest_vcn. Otherwise + * we have reached the right one or the search has failed. + */ + if (lowest_vcn && (u8 *)next_al_entry >= al_start && + (u8 *)next_al_entry + 6 < al_end && + (u8 *)next_al_entry + le16_to_cpu( + next_al_entry->length) <= al_end && + le64_to_cpu(next_al_entry->lowest_vcn) <= + lowest_vcn && + next_al_entry->type == al_entry->type && + next_al_entry->name_length == al_name_len && + ntfs_are_names_equal((__le16 *)((u8 *) + next_al_entry + + next_al_entry->name_offset), + next_al_entry->name_length, + al_name, al_name_len, CASE_SENSITIVE, + vol->upcase, vol->upcase_len)) + continue; + +is_enumeration: + if (MREF_LE(al_entry->mft_reference) == ni->mft_no) { + if (MSEQNO_LE(al_entry->mft_reference) != ni->seq_no) { + ntfs_error(vol->sb, + "Found stale mft reference in attribute list of base inode 0x%llx.%s", + base_ni->mft_no, es); + err = -EIO; + break; + } + } else { /* Mft references do not match. */ + /* If there is a mapped record unmap it first. */ + if (ni != base_ni) + unmap_extent_mft_record(ni); + /* Do we want the base record back? */ + if (MREF_LE(al_entry->mft_reference) == + base_ni->mft_no) { + ni = ctx->ntfs_ino = base_ni; + ctx->mrec = ctx->base_mrec; + ctx->mapped_mrec = ctx->mapped_base_mrec; + } else { + /* We want an extent record. */ + ctx->mrec = map_extent_mft_record(base_ni, + le64_to_cpu( + al_entry->mft_reference), &ni); + if (IS_ERR(ctx->mrec)) { + ntfs_error(vol->sb, + "Failed to map extent mft record 0x%lx of base inode 0x%llx.%s", + MREF_LE(al_entry->mft_reference), + base_ni->mft_no, es); + err = PTR_ERR(ctx->mrec); + if (err == -ENOENT) + err = -EIO; + /* Cause @ctx to be sanitized below. */ + ni = NULL; + break; + } + ctx->ntfs_ino = ni; + ctx->mapped_mrec = true; + + } + } + a = ctx->attr = (struct attr_record *)((u8 *)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + /* + * ctx->vfs_ino, ctx->mrec, and ctx->attr now point to the + * mft record containing the attribute represented by the + * current al_entry. + */ + /* + * We could call into ntfs_attr_find() to find the right + * attribute in this mft record but this would be less + * efficient and not quite accurate as ntfs_attr_find() ignores + * the attribute instance numbers for example which become + * important when one plays with attribute lists. Also, + * because a proper match has been found in the attribute list + * entry above, the comparison can now be optimized. So it is + * worth re-implementing a simplified ntfs_attr_find() here. + */ + /* + * Use a manual loop so we can still use break and continue + * with the same meanings as above. + */ +do_next_attr_loop: + if ((u8 *)a < (u8 *)ctx->mrec || + (u8 *)a >= (u8 *)ctx->mrec + le32_to_cpu(ctx->mrec->bytes_allocated) || + (u8 *)a >= (u8 *)ctx->mrec + le32_to_cpu(ctx->mrec->bytes_in_use)) + break; + + mft_free_len = le32_to_cpu(ctx->mrec->bytes_in_use) - + ((u8 *)a - (u8 *)ctx->mrec); + if (mft_free_len >= sizeof(a->type) && a->type == AT_END) + continue; + + attr_len = le32_to_cpu(a->length); + if (!attr_len || + attr_len < offsetof(struct attr_record, data.resident.reserved) + + sizeof(a->data.resident.reserved) || + attr_len > mft_free_len) + break; + + if (al_entry->instance != a->instance) + goto do_next_attr; + /* + * If the type and/or the name are mismatched between the + * attribute list entry and the attribute record, there is + * corruption so we break and return error EIO. + */ + if (al_entry->type != a->type) + break; + if (a->name_length && ((le16_to_cpu(a->name_offset) + + a->name_length * sizeof(__le16)) > attr_len)) + break; + if (!ntfs_are_names_equal((__le16 *)((u8 *)a + + le16_to_cpu(a->name_offset)), a->name_length, + al_name, al_name_len, CASE_SENSITIVE, + vol->upcase, vol->upcase_len)) + break; + + ctx->attr = a; + + if (a->non_resident) { + u32 min_len; + u16 mp_offset; + + min_len = offsetof(struct attr_record, + data.non_resident.initialized_size) + + sizeof(a->data.non_resident.initialized_size); + + if (le32_to_cpu(a->length) < min_len) + break; + + mp_offset = + le16_to_cpu(a->data.non_resident.mapping_pairs_offset); + if (mp_offset < min_len || mp_offset > attr_len) + break; + } + + /* + * If no @val specified or @val specified and it matches, we + * have found it! + */ + if ((type == AT_UNUSED) || !val) + goto attr_found; + if (!a->non_resident) { + u32 value_length = le32_to_cpu(a->data.resident.value_length); + u16 value_offset = le16_to_cpu(a->data.resident.value_offset); + + if (attr_len < offsetof(struct attr_record, data.resident.reserved) + + sizeof(a->data.resident.reserved)) + break; + if (value_length > attr_len || value_offset > attr_len - value_length) + break; + + value_length = ntfs_resident_attr_min_value_length(a->type); + if (value_length && le32_to_cpu(a->data.resident.value_length) < + value_length) { + pr_err("Too small resident attribute value in MFT record %lld, type %#x\n", + (long long)ctx->ntfs_ino->mft_no, a->type); + break; + } + if (value_length == val_len && + !memcmp((u8 *)a + value_offset, val, val_len)) { +attr_found: + ntfs_debug("Done, found."); + return 0; + } + } +do_next_attr: + /* Proceed to the next attribute in the current mft record. */ + a = (struct attr_record *)((u8 *)a + attr_len); + goto do_next_attr_loop; + } + +corrupt: + if (ni != base_ni) { + if (ni) + unmap_extent_mft_record(ni); + ctx->ntfs_ino = base_ni; + ctx->mrec = ctx->base_mrec; + ctx->attr = ctx->base_attr; + ctx->mapped_mrec = ctx->mapped_base_mrec; + } + + if (!err) { + u64 mft_no = ctx->al_entry ? MREF_LE(ctx->al_entry->mft_reference) : 0; + u32 type = ctx->al_entry ? le32_to_cpu(ctx->al_entry->type) : 0; + + ntfs_error(vol->sb, + "Base inode 0x%llx contains corrupt attribute, mft %#llx, type %#x. %s", + (long long)base_ni->mft_no, (long long)mft_no, type, + "Unmount and run chkdsk."); + err = -EIO; + } + + if (err != -ENOMEM) + NVolSetErrors(vol); + return err; +not_found: + /* + * If we were looking for AT_END, we reset the search context @ctx and + * use ntfs_attr_find() to seek to the end of the base mft record. + */ + if (type == AT_UNUSED || type == AT_END) { + ntfs_attr_reinit_search_ctx(ctx); + return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len, + ctx); + } + /* + * The attribute was not found. Before we return, we want to ensure + * @ctx->mrec and @ctx->attr indicate the position at which the + * attribute should be inserted in the base mft record. Since we also + * want to preserve @ctx->al_entry we cannot reinitialize the search + * context using ntfs_attr_reinit_search_ctx() as this would set + * @ctx->al_entry to NULL. Thus we do the necessary bits manually (see + * ntfs_attr_init_search_ctx() below). Note, we _only_ preserve + * @ctx->al_entry as the remaining fields (base_*) are identical to + * their non base_ counterparts and we cannot set @ctx->base_attr + * correctly yet as we do not know what @ctx->attr will be set to by + * the call to ntfs_attr_find() below. + */ + if (ni != base_ni) + unmap_extent_mft_record(ni); + ctx->mrec = ctx->base_mrec; + ctx->attr = (struct attr_record *)((u8 *)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + ctx->is_first = true; + ctx->ntfs_ino = base_ni; + ctx->base_ntfs_ino = NULL; + ctx->base_mrec = NULL; + ctx->base_attr = NULL; + ctx->mapped_mrec = ctx->mapped_base_mrec; + /* + * In case there are multiple matches in the base mft record, need to + * keep enumerating until we get an attribute not found response (or + * another error), otherwise we would keep returning the same attribute + * over and over again and all programs using us for enumeration would + * lock up in a tight loop. + */ + do { + err = ntfs_attr_find(type, name, name_len, ic, val, val_len, + ctx); + } while (!err); + ntfs_debug("Done, not found."); + return err; +} + +/* + * ntfs_attr_lookup - find an attribute in an ntfs inode + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must + * be the base mft record and @ctx must have been obtained from a call to + * ntfs_attr_get_search_ctx(). + * + * This function transparently handles attribute lists and @ctx is used to + * continue searches where they were left off at. + * + * After finishing with the attribute/mft record you need to call + * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any + * mapped inodes, etc). + * + * Return 0 if the search was successful and -errno if not. + * + * When 0, @ctx->attr is the found attribute and it is in mft record + * @ctx->mrec. If an attribute list attribute is present, @ctx->al_entry is + * the attribute list entry of the found attribute. + * + * When -ENOENT, @ctx->attr is the attribute which collates just after the + * attribute being searched for, i.e. if one wants to add the attribute to the + * mft record this is the correct place to insert it into. If an attribute + * list attribute is present, @ctx->al_entry is the attribute list entry which + * collates just after the attribute list entry of the attribute being searched + * for, i.e. if one wants to add the attribute to the mft record this is the + * correct place to insert its attribute list entry into. + */ +int ntfs_attr_lookup(const __le32 type, const __le16 *name, + const u32 name_len, const u32 ic, + const s64 lowest_vcn, const u8 *val, const u32 val_len, + struct ntfs_attr_search_ctx *ctx) +{ + struct ntfs_inode *base_ni; + + ntfs_debug("Entering."); + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + /* Sanity check, just for debugging really. */ + if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) + return ntfs_attr_find(type, name, name_len, ic, val, val_len, + ctx); + return ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn, + val, val_len, ctx); +} + +/** + * ntfs_attr_init_search_ctx - initialize an attribute search context + * @ctx: attribute search context to initialize + * @ni: ntfs inode with which to initialize the search context + * @mrec: mft record with which to initialize the search context + * + * Initialize the attribute search context @ctx with @ni and @mrec. + */ +static bool ntfs_attr_init_search_ctx(struct ntfs_attr_search_ctx *ctx, + struct ntfs_inode *ni, struct mft_record *mrec) +{ + if (!mrec) { + mrec = map_mft_record(ni); + if (IS_ERR(mrec)) + return false; + ctx->mapped_mrec = true; + } else { + ctx->mapped_mrec = false; + } + + ctx->mrec = mrec; + /* Sanity checks are performed elsewhere. */ + ctx->attr = (struct attr_record *)((u8 *)mrec + le16_to_cpu(mrec->attrs_offset)); + ctx->is_first = true; + ctx->ntfs_ino = ni; + ctx->al_entry = NULL; + ctx->base_ntfs_ino = NULL; + ctx->base_mrec = NULL; + ctx->base_attr = NULL; + ctx->mapped_base_mrec = false; + return true; +} + +/* + * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context + * @ctx: attribute search context to reinitialize + * + * Reinitialize the attribute search context @ctx, unmapping an associated + * extent mft record if present, and initialize the search context again. + * + * This is used when a search for a new attribute is being started to reset + * the search context to the beginning. + */ +void ntfs_attr_reinit_search_ctx(struct ntfs_attr_search_ctx *ctx) +{ + bool mapped_mrec; + + if (likely(!ctx->base_ntfs_ino)) { + /* No attribute list. */ + ctx->is_first = true; + /* Sanity checks are performed elsewhere. */ + ctx->attr = (struct attr_record *)((u8 *)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + /* + * This needs resetting due to ntfs_external_attr_find() which + * can leave it set despite having zeroed ctx->base_ntfs_ino. + */ + ctx->al_entry = NULL; + return; + } /* Attribute list. */ + if (ctx->ntfs_ino != ctx->base_ntfs_ino && ctx->ntfs_ino) + unmap_extent_mft_record(ctx->ntfs_ino); + + mapped_mrec = ctx->mapped_base_mrec; + ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec); + ctx->mapped_mrec = mapped_mrec; +} + +/* + * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context + * @ni: ntfs inode with which to initialize the search context + * @mrec: mft record with which to initialize the search context + * + * Allocate a new attribute search context, initialize it with @ni and @mrec, + * and return it. Return NULL if allocation failed. + */ +struct ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(struct ntfs_inode *ni, + struct mft_record *mrec) +{ + struct ntfs_attr_search_ctx *ctx; + bool init; + + ctx = kmem_cache_alloc(ntfs_attr_ctx_cache, GFP_NOFS); + if (ctx) { + init = ntfs_attr_init_search_ctx(ctx, ni, mrec); + if (init == false) { + kmem_cache_free(ntfs_attr_ctx_cache, ctx); + ctx = NULL; + } + } + + return ctx; +} + +/* + * ntfs_attr_put_search_ctx - release an attribute search context + * @ctx: attribute search context to free + * + * Release the attribute search context @ctx, unmapping an associated extent + * mft record if present. + */ +void ntfs_attr_put_search_ctx(struct ntfs_attr_search_ctx *ctx) +{ + if (ctx->mapped_mrec) + unmap_mft_record(ctx->ntfs_ino); + + if (ctx->mapped_base_mrec && ctx->base_ntfs_ino && + ctx->ntfs_ino != ctx->base_ntfs_ino) + unmap_extent_mft_record(ctx->base_ntfs_ino); + kmem_cache_free(ntfs_attr_ctx_cache, ctx); +} + +/* + * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to find + * + * Search for the attribute definition record corresponding to the attribute + * @type in the $AttrDef system file. + * + * Return the attribute type definition record if found and NULL if not found. + */ +static struct attr_def *ntfs_attr_find_in_attrdef(const struct ntfs_volume *vol, + const __le32 type) +{ + struct attr_def *ad; + + WARN_ON(!type); + for (ad = vol->attrdef; (u8 *)ad - (u8 *)vol->attrdef < + vol->attrdef_size && ad->type; ++ad) { + /* We have not found it yet, carry on searching. */ + if (likely(le32_to_cpu(ad->type) < le32_to_cpu(type))) + continue; + /* We found the attribute; return it. */ + if (likely(ad->type == type)) + return ad; + /* We have gone too far already. No point in continuing. */ + break; + } + /* Attribute not found. */ + ntfs_debug("Attribute type 0x%x not found in $AttrDef.", + le32_to_cpu(type)); + return NULL; +} + +/* + * ntfs_attr_size_bounds_check - check a size of an attribute type for validity + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to check + * @size: size which to check + * + * Check whether the @size in bytes is valid for an attribute of @type on the + * ntfs volume @vol. This information is obtained from $AttrDef system file. + */ +int ntfs_attr_size_bounds_check(const struct ntfs_volume *vol, const __le32 type, + const s64 size) +{ + struct attr_def *ad; + + if (size < 0) + return -EINVAL; + + /* + * $ATTRIBUTE_LIST has a maximum size of 256kiB, but this is not + * listed in $AttrDef. + */ + if (unlikely(type == AT_ATTRIBUTE_LIST && size > 256 * 1024)) + return -ERANGE; + /* Get the $AttrDef entry for the attribute @type. */ + ad = ntfs_attr_find_in_attrdef(vol, type); + if (unlikely(!ad)) + return -ENOENT; + /* Do the bounds check. */ + if (((le64_to_cpu(ad->min_size) > 0) && + size < le64_to_cpu(ad->min_size)) || + ((le64_to_cpu(ad->max_size) > 0) && size > + le64_to_cpu(ad->max_size))) + return -ERANGE; + return 0; +} + +/* + * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to check + * + * Check whether the attribute of @type on the ntfs volume @vol is allowed to + * be non-resident. This information is obtained from $AttrDef system file. + */ +static int ntfs_attr_can_be_non_resident(const struct ntfs_volume *vol, + const __le32 type) +{ + struct attr_def *ad; + + /* Find the attribute definition record in $AttrDef. */ + ad = ntfs_attr_find_in_attrdef(vol, type); + if (unlikely(!ad)) + return -ENOENT; + /* Check the flags and return the result. */ + if (ad->flags & ATTR_DEF_RESIDENT) + return -EPERM; + return 0; +} + +/* + * ntfs_attr_can_be_resident - check if an attribute can be resident + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to check + * + * Check whether the attribute of @type on the ntfs volume @vol is allowed to + * be resident. This information is derived from our ntfs knowledge and may + * not be completely accurate, especially when user defined attributes are + * present. Basically we allow everything to be resident except for index + * allocation and $EA attributes. + * + * Return 0 if the attribute is allowed to be non-resident and -EPERM if not. + * + * Warning: In the system file $MFT the attribute $Bitmap must be non-resident + * otherwise windows will not boot (blue screen of death)! We cannot + * check for this here as we do not know which inode's $Bitmap is + * being asked about so the caller needs to special case this. + */ +int ntfs_attr_can_be_resident(const struct ntfs_volume *vol, const __le32 type) +{ + if (type == AT_INDEX_ALLOCATION) + return -EPERM; + return 0; +} + +/* + * ntfs_attr_record_resize - resize an attribute record + * @m: mft record containing attribute record + * @a: attribute record to resize + * @new_size: new size in bytes to which to resize the attribute record @a + * + * Resize the attribute record @a, i.e. the resident part of the attribute, in + * the mft record @m to @new_size bytes. + */ +int ntfs_attr_record_resize(struct mft_record *m, struct attr_record *a, u32 new_size) +{ + u32 old_size, alloc_size, attr_size; + + old_size = le32_to_cpu(m->bytes_in_use); + alloc_size = le32_to_cpu(m->bytes_allocated); + attr_size = le32_to_cpu(a->length); + + ntfs_debug("Sizes: old=%u alloc=%u attr=%u new=%u\n", + (unsigned int)old_size, (unsigned int)alloc_size, + (unsigned int)attr_size, (unsigned int)new_size); + + /* Align to 8 bytes if it is not already done. */ + if (new_size & 7) + new_size = (new_size + 7) & ~7; + /* If the actual attribute length has changed, move things around. */ + if (new_size != attr_size) { + u32 new_muse = le32_to_cpu(m->bytes_in_use) - + attr_size + new_size; + /* Not enough space in this mft record. */ + if (new_muse > le32_to_cpu(m->bytes_allocated)) + return -ENOSPC; + + if (a->type == AT_INDEX_ROOT && new_size > attr_size && + new_muse + 120 > alloc_size && old_size + 120 <= alloc_size) { + ntfs_debug("Too big struct index_root (%u > %u)\n", + new_muse, alloc_size); + return -ENOSPC; + } + + /* Move attributes following @a to their new location. */ + memmove((u8 *)a + new_size, (u8 *)a + le32_to_cpu(a->length), + le32_to_cpu(m->bytes_in_use) - ((u8 *)a - + (u8 *)m) - attr_size); + /* Adjust @m to reflect the change in used space. */ + m->bytes_in_use = cpu_to_le32(new_muse); + /* Adjust @a to reflect the new size. */ + if (new_size >= offsetof(struct attr_record, length) + sizeof(a->length)) + a->length = cpu_to_le32(new_size); + } + return 0; +} + +/* + * ntfs_resident_attr_value_resize - resize the value of a resident attribute + * @m: mft record containing attribute record + * @a: attribute record whose value to resize + * @new_size: new size in bytes to which to resize the attribute value of @a + * + * Resize the value of the attribute @a in the mft record @m to @new_size bytes. + * If the value is made bigger, the newly allocated space is cleared. + */ +int ntfs_resident_attr_value_resize(struct mft_record *m, struct attr_record *a, + const u32 new_size) +{ + u32 old_size; + + /* Resize the resident part of the attribute record. */ + if (ntfs_attr_record_resize(m, a, + le16_to_cpu(a->data.resident.value_offset) + new_size)) + return -ENOSPC; + /* + * The resize succeeded! If we made the attribute value bigger, clear + * the area between the old size and @new_size. + */ + old_size = le32_to_cpu(a->data.resident.value_length); + if (new_size > old_size) + memset((u8 *)a + le16_to_cpu(a->data.resident.value_offset) + + old_size, 0, new_size - old_size); + /* Finally update the length of the attribute value. */ + a->data.resident.value_length = cpu_to_le32(new_size); + return 0; +} + +/* + * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute + * @ni: ntfs inode describing the attribute to convert + * @data_size: size of the resident data to copy to the non-resident attribute + * + * Convert the resident ntfs attribute described by the ntfs inode @ni to a + * non-resident one. + * + * @data_size must be equal to the attribute value size. This is needed since + * we need to know the size before we can map the mft record and our callers + * always know it. The reason we cannot simply read the size from the vfs + * inode i_size is that this is not necessarily uptodate. This happens when + * ntfs_attr_make_non_resident() is called in the ->truncate call path(s). + */ +int ntfs_attr_make_non_resident(struct ntfs_inode *ni, const u32 data_size) +{ + s64 new_size; + struct inode *vi = VFS_I(ni); + struct ntfs_volume *vol = ni->vol; + struct ntfs_inode *base_ni; + struct mft_record *m; + struct attr_record *a; + struct ntfs_attr_search_ctx *ctx; + struct folio *folio; + struct runlist_element *rl; + unsigned long flags; + int mp_size, mp_ofs, name_ofs, arec_size, err, err2; + u32 attr_size; + u8 old_res_attr_flags; + + if (NInoNonResident(ni)) { + ntfs_warning(vol->sb, + "Trying to make non-resident attribute non-resident. Aborting...\n"); + return -EINVAL; + } + + /* Check that the attribute is allowed to be non-resident. */ + err = ntfs_attr_can_be_non_resident(vol, ni->type); + if (unlikely(err)) { + if (err == -EPERM) + ntfs_debug("Attribute is not allowed to be non-resident."); + else + ntfs_debug("Attribute not defined on the NTFS volume!"); + return err; + } + + if (NInoEncrypted(ni)) + return -EIO; + + if (!NInoAttr(ni)) + base_ni = ni; + else + base_ni = ni->ext.base_ntfs_ino; + m = map_mft_record(base_ni); + if (IS_ERR(m)) { + err = PTR_ERR(m); + m = NULL; + ctx = NULL; + goto err_out; + } + ctx = ntfs_attr_get_search_ctx(base_ni, m); + if (unlikely(!ctx)) { + err = -ENOMEM; + goto err_out; + } + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) { + if (err == -ENOENT) + err = -EIO; + goto err_out; + } + m = ctx->mrec; + a = ctx->attr; + + /* + * The size needs to be aligned to a cluster boundary for allocation + * purposes. + */ + new_size = (data_size + vol->cluster_size - 1) & + ~(vol->cluster_size - 1); + if (new_size > 0) { + if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) { + /* must allocate full compression blocks */ + new_size = + ((new_size - 1) | + ((1L << (STANDARD_COMPRESSION_UNIT + + vol->cluster_size_bits)) - 1)) + 1; + } + + /* + * Will need folio later and since folio lock nests + * outside all ntfs locks, we need to get the folio now. + */ + folio = __filemap_get_folio(vi->i_mapping, 0, + FGP_CREAT | FGP_LOCK, + mapping_gfp_mask(vi->i_mapping)); + if (IS_ERR(folio)) { + err = -ENOMEM; + goto err_out; + } + + /* Start by allocating clusters to hold the attribute value. */ + rl = ntfs_cluster_alloc(vol, 0, + ntfs_bytes_to_cluster(vol, new_size), + -1, DATA_ZONE, true, false, false); + if (IS_ERR(rl)) { + err = PTR_ERR(rl); + ntfs_debug("Failed to allocate cluster%s, error code %i.", + ntfs_bytes_to_cluster(vol, new_size) > 1 ? "s" : "", + err); + goto folio_err_out; + } + } else { + rl = NULL; + folio = NULL; + } + + down_write(&ni->runlist.lock); + /* Determine the size of the mapping pairs array. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, -1, -1); + if (unlikely(mp_size < 0)) { + err = mp_size; + ntfs_debug("Failed to get size for mapping pairs array, error code %i.\n", err); + goto rl_err_out; + } + + if (NInoNonResident(ni) || a->non_resident) { + err = -EIO; + goto rl_err_out; + } + + /* + * Calculate new offsets for the name and the mapping pairs array. + */ + if (NInoSparse(ni) || NInoCompressed(ni)) + name_ofs = (offsetof(struct attr_record, + data.non_resident.compressed_size) + + sizeof(a->data.non_resident.compressed_size) + + 7) & ~7; + else + name_ofs = (offsetof(struct attr_record, + data.non_resident.compressed_size) + 7) & ~7; + mp_ofs = (name_ofs + a->name_length * sizeof(__le16) + 7) & ~7; + /* + * Determine the size of the resident part of the now non-resident + * attribute record. + */ + arec_size = (mp_ofs + mp_size + 7) & ~7; + /* + * If the folio is not uptodate bring it uptodate by copying from the + * attribute value. + */ + attr_size = le32_to_cpu(a->data.resident.value_length); + WARN_ON(attr_size != data_size); + if (folio && !folio_test_uptodate(folio)) { + folio_fill_tail(folio, 0, (u8 *)a + + le16_to_cpu(a->data.resident.value_offset), + attr_size); + folio_mark_uptodate(folio); + } + + /* Backup the attribute flag. */ + old_res_attr_flags = a->data.resident.flags; + /* Resize the resident part of the attribute record. */ + err = ntfs_attr_record_resize(m, a, arec_size); + if (unlikely(err)) + goto rl_err_out; + + /* + * Convert the resident part of the attribute record to describe a + * non-resident attribute. + */ + a->non_resident = 1; + /* Move the attribute name if it exists and update the offset. */ + if (a->name_length) + memmove((u8 *)a + name_ofs, (u8 *)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(__le16)); + a->name_offset = cpu_to_le16(name_ofs); + /* Setup the fields specific to non-resident attributes. */ + a->data.non_resident.lowest_vcn = 0; + a->data.non_resident.highest_vcn = + cpu_to_le64(ntfs_bytes_to_cluster(vol, new_size - 1)); + a->data.non_resident.mapping_pairs_offset = cpu_to_le16(mp_ofs); + memset(&a->data.non_resident.reserved, 0, + sizeof(a->data.non_resident.reserved)); + a->data.non_resident.allocated_size = cpu_to_le64(new_size); + a->data.non_resident.data_size = + a->data.non_resident.initialized_size = + cpu_to_le64(attr_size); + if (NInoSparse(ni) || NInoCompressed(ni)) { + a->data.non_resident.compression_unit = 0; + if (NInoCompressed(ni) || vol->major_ver < 3) + a->data.non_resident.compression_unit = 4; + a->data.non_resident.compressed_size = + a->data.non_resident.allocated_size; + } else + a->data.non_resident.compression_unit = 0; + /* Generate the mapping pairs array into the attribute record. */ + err = ntfs_mapping_pairs_build(vol, (u8 *)a + mp_ofs, + arec_size - mp_ofs, rl, 0, -1, NULL, NULL, NULL); + if (unlikely(err)) { + ntfs_error(vol->sb, "Failed to build mapping pairs, error code %i.", + err); + goto undo_err_out; + } + + /* Setup the in-memory attribute structure to be non-resident. */ + ni->runlist.rl = rl; + if (rl) { + for (ni->runlist.count = 1; rl->length != 0; rl++) + ni->runlist.count++; + } else + ni->runlist.count = 0; + write_lock_irqsave(&ni->size_lock, flags); + ni->allocated_size = new_size; + if (NInoSparse(ni) || NInoCompressed(ni)) { + ni->itype.compressed.size = ni->allocated_size; + if (a->data.non_resident.compression_unit) { + ni->itype.compressed.block_size = 1U << + (a->data.non_resident.compression_unit + + vol->cluster_size_bits); + ni->itype.compressed.block_size_bits = + ffs(ni->itype.compressed.block_size) - + 1; + ni->itype.compressed.block_clusters = 1U << + a->data.non_resident.compression_unit; + } else { + ni->itype.compressed.block_size = 0; + ni->itype.compressed.block_size_bits = 0; + ni->itype.compressed.block_clusters = 0; + } + vi->i_blocks = ni->itype.compressed.size >> 9; + } else + vi->i_blocks = ni->allocated_size >> 9; + write_unlock_irqrestore(&ni->size_lock, flags); + /* + * This needs to be last since the address space operations ->read_folio + * and ->writepage can run concurrently with us as they are not + * serialized on i_mutex. Note, we are not allowed to fail once we flip + * this switch, which is another reason to do this last. + */ + NInoSetNonResident(ni); + NInoSetFullyMapped(ni); + /* Mark the mft record dirty, so it gets written back. */ + mark_mft_record_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(base_ni); + up_write(&ni->runlist.lock); + if (folio) { + iomap_dirty_folio(vi->i_mapping, folio); + folio_unlock(folio); + folio_put(folio); + } + ntfs_debug("Done."); + return 0; +undo_err_out: + /* Convert the attribute back into a resident attribute. */ + a->non_resident = 0; + /* Move the attribute name if it exists and update the offset. */ + name_ofs = (offsetof(struct attr_record, data.resident.reserved) + + sizeof(a->data.resident.reserved) + 7) & ~7; + if (a->name_length) + memmove((u8 *)a + name_ofs, (u8 *)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(__le16)); + mp_ofs = (name_ofs + a->name_length * sizeof(__le16) + 7) & ~7; + a->name_offset = cpu_to_le16(name_ofs); + arec_size = (mp_ofs + attr_size + 7) & ~7; + /* Resize the resident part of the attribute record. */ + err2 = ntfs_attr_record_resize(m, a, arec_size); + if (unlikely(err2)) { + /* + * This cannot happen (well if memory corruption is at work it + * could happen in theory), but deal with it as well as we can. + * If the old size is too small, truncate the attribute, + * otherwise simply give it a larger allocated size. + */ + arec_size = le32_to_cpu(a->length); + if ((mp_ofs + attr_size) > arec_size) { + err2 = attr_size; + attr_size = arec_size - mp_ofs; + ntfs_error(vol->sb, + "Failed to undo partial resident to non-resident attribute conversion. Truncating inode 0x%llx, attribute type 0x%x from %i bytes to %i bytes to maintain metadata consistency. THIS MEANS YOU ARE LOSING %i BYTES DATA FROM THIS %s.", + ni->mft_no, + (unsigned int)le32_to_cpu(ni->type), + err2, attr_size, err2 - attr_size, + ((ni->type == AT_DATA) && + !ni->name_len) ? "FILE" : "ATTRIBUTE"); + write_lock_irqsave(&ni->size_lock, flags); + ni->initialized_size = attr_size; + i_size_write(vi, attr_size); + write_unlock_irqrestore(&ni->size_lock, flags); + } + } + /* Setup the fields specific to resident attributes. */ + a->data.resident.value_length = cpu_to_le32(attr_size); + a->data.resident.value_offset = cpu_to_le16(mp_ofs); + a->data.resident.flags = old_res_attr_flags; + memset(&a->data.resident.reserved, 0, + sizeof(a->data.resident.reserved)); + /* Copy the data from folio back to the attribute value. */ + if (folio) + memcpy_from_folio((u8 *)a + mp_ofs, folio, 0, attr_size); + /* Setup the allocated size in the ntfs inode in case it changed. */ + write_lock_irqsave(&ni->size_lock, flags); + ni->allocated_size = arec_size - mp_ofs; + write_unlock_irqrestore(&ni->size_lock, flags); + /* Mark the mft record dirty, so it gets written back. */ + mark_mft_record_dirty(ctx->ntfs_ino); +rl_err_out: + up_write(&ni->runlist.lock); + if (rl) { + if (ntfs_cluster_free_from_rl(vol, rl) < 0) { + ntfs_error(vol->sb, + "Failed to release allocated cluster(s) in error code path. Run chkdsk to recover the lost cluster(s)."); + NVolSetErrors(vol); + } + kvfree(rl); +folio_err_out: + folio_unlock(folio); + folio_put(folio); + } +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (m) + unmap_mft_record(base_ni); + ni->runlist.rl = NULL; + + if (err == -EINVAL) + err = -EIO; + return err; +} + +/* + * ntfs_attr_set - fill (a part of) an attribute with a byte + * @ni: ntfs inode describing the attribute to fill + * @ofs: offset inside the attribute at which to start to fill + * @cnt: number of bytes to fill + * @val: the unsigned 8-bit value with which to fill the attribute + * + * Fill @cnt bytes of the attribute described by the ntfs inode @ni starting at + * byte offset @ofs inside the attribute with the constant byte @val. + * + * This function is effectively like memset() applied to an ntfs attribute. + * Note thie function actually only operates on the page cache pages belonging + * to the ntfs attribute and it marks them dirty after doing the memset(). + * Thus it relies on the vm dirty page write code paths to cause the modified + * pages to be written to the mft record/disk. + */ +int ntfs_attr_set(struct ntfs_inode *ni, s64 ofs, s64 cnt, const u8 val) +{ + struct address_space *mapping = VFS_I(ni)->i_mapping; + struct folio *folio; + pgoff_t index; + u8 *addr; + unsigned long offset; + size_t attr_len; + int ret = 0; + + index = ofs >> PAGE_SHIFT; + while (cnt) { + folio = read_mapping_folio(mapping, index, NULL); + if (IS_ERR(folio)) { + ret = PTR_ERR(folio); + ntfs_error(VFS_I(ni)->i_sb, "Failed to read a page %lu for attr %#x: %ld", + index, ni->type, PTR_ERR(folio)); + break; + } + + offset = offset_in_folio(folio, ofs); + attr_len = min_t(size_t, (size_t)cnt, folio_size(folio) - offset); + + folio_lock(folio); + addr = kmap_local_folio(folio, offset); + memset(addr, val, attr_len); + kunmap_local(addr); + + folio_mark_dirty(folio); + folio_unlock(folio); + folio_put(folio); + + ofs += attr_len; + cnt -= attr_len; + index++; + cond_resched(); + } + + return ret; +} + +int ntfs_attr_set_initialized_size(struct ntfs_inode *ni, loff_t new_size) +{ + struct ntfs_attr_search_ctx *ctx; + int err = 0; + + if (!NInoNonResident(ni)) + return -EINVAL; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -ENOMEM; + + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (err) + goto out_ctx; + + ctx->attr->data.non_resident.initialized_size = cpu_to_le64(new_size); + ni->initialized_size = new_size; + mark_mft_record_dirty(ctx->ntfs_ino); +out_ctx: + ntfs_attr_put_search_ctx(ctx); + return err; +} + +/* + * ntfs_make_room_for_attr - make room for an attribute inside an mft record + * @m: mft record + * @pos: position at which to make space + * @size: byte size to make available at this position + * + * @pos points to the attribute in front of which we want to make space. + */ +static int ntfs_make_room_for_attr(struct mft_record *m, u8 *pos, u32 size) +{ + u32 biu; + + ntfs_debug("Entering for pos 0x%x, size %u.\n", + (int)(pos - (u8 *)m), (unsigned int) size); + + /* Make size 8-byte alignment. */ + size = (size + 7) & ~7; + + /* Rigorous consistency checks. */ + if (!m || !pos || pos < (u8 *)m) { + pr_err("%s: pos=%p m=%p\n", __func__, pos, m); + return -EINVAL; + } + + /* The -8 is for the attribute terminator. */ + if (pos - (u8 *)m > (int)le32_to_cpu(m->bytes_in_use) - 8) + return -EINVAL; + /* Nothing to do. */ + if (!size) + return 0; + + biu = le32_to_cpu(m->bytes_in_use); + /* Do we have enough space? */ + if (biu + size > le32_to_cpu(m->bytes_allocated) || + pos + size > (u8 *)m + le32_to_cpu(m->bytes_allocated)) { + ntfs_debug("No enough space in the MFT record\n"); + return -ENOSPC; + } + /* Move everything after pos to pos + size. */ + memmove(pos + size, pos, biu - (pos - (u8 *)m)); + /* Update mft record. */ + m->bytes_in_use = cpu_to_le32(biu + size); + return 0; +} + +/* + * ntfs_resident_attr_record_add - add resident attribute to inode + * @ni: opened ntfs inode to which MFT record add attribute + * @type: type of the new attribute + * @name: name of the new attribute + * @name_len: name length of the new attribute + * @val: value of the new attribute + * @size: size of new attribute (length of @val, if @val != NULL) + * @flags: flags of the new attribute + */ +int ntfs_resident_attr_record_add(struct ntfs_inode *ni, __le32 type, + __le16 *name, u8 name_len, u8 *val, u32 size, + __le16 flags) +{ + struct ntfs_attr_search_ctx *ctx; + u32 length; + struct attr_record *a; + struct mft_record *m; + int err, offset; + struct ntfs_inode *base_ni; + + if (!ni || (!name && name_len)) + return -EINVAL; + + ntfs_debug("Entering for inode 0x%llx, attr 0x%x, flags 0x%x.\n", + (long long) ni->mft_no, (unsigned int) le32_to_cpu(type), + (unsigned int) le16_to_cpu(flags)); + + err = ntfs_attr_can_be_resident(ni->vol, type); + if (err) { + if (err == -EPERM) + ntfs_debug("Attribute can't be resident.\n"); + else + ntfs_debug("ntfs_attr_can_be_resident failed.\n"); + return err; + } + + /* Locate place where record should be. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_error(ni->vol->sb, "%s: Failed to get search context", + __func__); + return -ENOMEM; + } + /* + * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for + * attribute in @ni->mrec, not any extent inode in case if @ni is base + * file record. + */ + err = ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, val, size, ctx); + if (!err) { + err = -EEXIST; + ntfs_debug("Attribute already present.\n"); + goto put_err_out; + } + if (err != -ENOENT) { + err = -EIO; + goto put_err_out; + } + a = ctx->attr; + m = ctx->mrec; + + /* Make room for attribute. */ + length = offsetof(struct attr_record, data.resident.reserved) + + sizeof(a->data.resident.reserved) + + ((name_len * sizeof(__le16) + 7) & ~7) + + ((size + 7) & ~7); + err = ntfs_make_room_for_attr(ctx->mrec, (u8 *) ctx->attr, length); + if (err) { + ntfs_debug("Failed to make room for attribute.\n"); + goto put_err_out; + } + + /* Setup record fields. */ + offset = ((u8 *)a - (u8 *)m); + a->type = type; + a->length = cpu_to_le32(length); + a->non_resident = 0; + a->name_length = name_len; + a->name_offset = + name_len ? cpu_to_le16((offsetof(struct attr_record, data.resident.reserved) + + sizeof(a->data.resident.reserved))) : cpu_to_le16(0); + + a->flags = flags; + a->instance = m->next_attr_instance; + a->data.resident.value_length = cpu_to_le32(size); + a->data.resident.value_offset = cpu_to_le16(length - ((size + 7) & ~7)); + if (val) + memcpy((u8 *)a + le16_to_cpu(a->data.resident.value_offset), val, size); + else + memset((u8 *)a + le16_to_cpu(a->data.resident.value_offset), 0, size); + if (type == AT_FILE_NAME) + a->data.resident.flags = RESIDENT_ATTR_IS_INDEXED; + else + a->data.resident.flags = 0; + if (name_len) + memcpy((u8 *)a + le16_to_cpu(a->name_offset), + name, sizeof(__le16) * name_len); + m->next_attr_instance = + cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); + if (ni->nr_extents == -1) + base_ni = ni->ext.base_ntfs_ino; + else + base_ni = ni; + if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { + err = ntfs_attrlist_entry_add(ni, a); + if (err) { + ntfs_attr_record_resize(m, a, 0); + mark_mft_record_dirty(ctx->ntfs_ino); + ntfs_debug("Failed add attribute entry to ATTRIBUTE_LIST.\n"); + goto put_err_out; + } + } + mark_mft_record_dirty(ni); + ntfs_attr_put_search_ctx(ctx); + return offset; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + return -EIO; +} + +/* + * ntfs_non_resident_attr_record_add - add extent of non-resident attribute + * @ni: opened ntfs inode to which MFT record add attribute + * @type: type of the new attribute extent + * @name: name of the new attribute extent + * @name_len: name length of the new attribute extent + * @lowest_vcn: lowest vcn of the new attribute extent + * @dataruns_size: dataruns size of the new attribute extent + * @flags: flags of the new attribute extent + */ +static int ntfs_non_resident_attr_record_add(struct ntfs_inode *ni, __le32 type, + __le16 *name, u8 name_len, s64 lowest_vcn, int dataruns_size, + __le16 flags) +{ + struct ntfs_attr_search_ctx *ctx; + u32 length; + struct attr_record *a; + struct mft_record *m; + struct ntfs_inode *base_ni; + int err, offset; + + if (!ni || dataruns_size <= 0 || (!name && name_len)) + return -EINVAL; + + ntfs_debug("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld, dataruns_size %d, flags 0x%x.\n", + (long long) ni->mft_no, (unsigned int) le32_to_cpu(type), + (long long) lowest_vcn, dataruns_size, + (unsigned int) le16_to_cpu(flags)); + + err = ntfs_attr_can_be_non_resident(ni->vol, type); + if (err) { + if (err == -EPERM) + pr_err("Attribute can't be non resident\n"); + else + pr_err("ntfs_attr_can_be_non_resident failed\n"); + return err; + } + + /* Locate place where record should be. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + pr_err("%s: Failed to get search context\n", __func__); + return -ENOMEM; + } + /* + * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for + * attribute in @ni->mrec, not any extent inode in case if @ni is base + * file record. + */ + err = ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, NULL, 0, ctx); + if (!err) { + err = -EEXIST; + pr_err("Attribute 0x%x already present\n", type); + goto put_err_out; + } + if (err != -ENOENT) { + pr_err("ntfs_attr_find failed\n"); + err = -EIO; + goto put_err_out; + } + a = ctx->attr; + m = ctx->mrec; + + /* Make room for attribute. */ + dataruns_size = (dataruns_size + 7) & ~7; + length = offsetof(struct attr_record, data.non_resident.compressed_size) + + ((sizeof(__le16) * name_len + 7) & ~7) + dataruns_size + + ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? + sizeof(a->data.non_resident.compressed_size) : 0); + err = ntfs_make_room_for_attr(ctx->mrec, (u8 *) ctx->attr, length); + if (err) { + pr_err("Failed to make room for attribute\n"); + goto put_err_out; + } + + /* Setup record fields. */ + a->type = type; + a->length = cpu_to_le32(length); + a->non_resident = 1; + a->name_length = name_len; + a->name_offset = cpu_to_le16(offsetof(struct attr_record, + data.non_resident.compressed_size) + + ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? + sizeof(a->data.non_resident.compressed_size) : 0)); + a->flags = flags; + a->instance = m->next_attr_instance; + a->data.non_resident.lowest_vcn = cpu_to_le64(lowest_vcn); + a->data.non_resident.mapping_pairs_offset = cpu_to_le16(length - dataruns_size); + a->data.non_resident.compression_unit = + (flags & ATTR_IS_COMPRESSED) ? STANDARD_COMPRESSION_UNIT : 0; + /* If @lowest_vcn == 0, than setup empty attribute. */ + if (!lowest_vcn) { + a->data.non_resident.highest_vcn = cpu_to_le64(-1); + a->data.non_resident.allocated_size = 0; + a->data.non_resident.data_size = 0; + a->data.non_resident.initialized_size = 0; + /* Set empty mapping pairs. */ + *((u8 *)a + le16_to_cpu(a->data.non_resident.mapping_pairs_offset)) = 0; + } + if (name_len) + memcpy((u8 *)a + le16_to_cpu(a->name_offset), + name, sizeof(__le16) * name_len); + m->next_attr_instance = + cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); + if (ni->nr_extents == -1) + base_ni = ni->ext.base_ntfs_ino; + else + base_ni = ni; + if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { + err = ntfs_attrlist_entry_add(ni, a); + if (err) { + pr_err("Failed add attr entry to attrlist\n"); + ntfs_attr_record_resize(m, a, 0); + goto put_err_out; + } + } + mark_mft_record_dirty(ni); + /* + * Locate offset from start of the MFT record where new attribute is + * placed. We need relookup it, because record maybe moved during + * update of attribute list. + */ + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, + lowest_vcn, NULL, 0, ctx); + if (err) { + pr_err("%s: attribute lookup failed\n", __func__); + ntfs_attr_put_search_ctx(ctx); + return err; + + } + offset = (u8 *)ctx->attr - (u8 *)ctx->mrec; + ntfs_attr_put_search_ctx(ctx); + return offset; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + return -1; +} + +/* + * ntfs_attr_record_rm - remove attribute extent + * @ctx: search context describing the attribute which should be removed + * + * If this function succeed, user should reinit search context if he/she wants + * use it anymore. + */ +int ntfs_attr_record_rm(struct ntfs_attr_search_ctx *ctx) +{ + struct ntfs_inode *base_ni, *ni; + __le32 type; + int err; + + if (!ctx || !ctx->ntfs_ino || !ctx->mrec || !ctx->attr) + return -EINVAL; + + ntfs_debug("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) ctx->ntfs_ino->mft_no, + (unsigned int) le32_to_cpu(ctx->attr->type)); + type = ctx->attr->type; + ni = ctx->ntfs_ino; + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + + /* Remove attribute itself. */ + if (ntfs_attr_record_resize(ctx->mrec, ctx->attr, 0)) { + ntfs_debug("Couldn't remove attribute record. Bug or damaged MFT record.\n"); + return -EIO; + } + mark_mft_record_dirty(ni); + + /* + * Remove record from $ATTRIBUTE_LIST if present and we don't want + * delete $ATTRIBUTE_LIST itself. + */ + if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) { + err = ntfs_attrlist_entry_rm(ctx); + if (err) { + ntfs_debug("Couldn't delete record from $ATTRIBUTE_LIST.\n"); + return err; + } + } + + /* Post $ATTRIBUTE_LIST delete setup. */ + if (type == AT_ATTRIBUTE_LIST) { + if (NInoAttrList(base_ni) && base_ni->attr_list) + kvfree(base_ni->attr_list); + base_ni->attr_list = NULL; + NInoClearAttrList(base_ni); + } + + /* Free MFT record, if it doesn't contain attributes. */ + if (le32_to_cpu(ctx->mrec->bytes_in_use) - + le16_to_cpu(ctx->mrec->attrs_offset) == 8) { + if (ntfs_mft_record_free(ni->vol, ni)) { + ntfs_debug("Couldn't free MFT record.\n"); + return -EIO; + } + /* Remove done if we freed base inode. */ + if (ni == base_ni) + return 0; + ntfs_inode_close(ni); + ctx->ntfs_ino = ni = NULL; + } + + if (type == AT_ATTRIBUTE_LIST || !NInoAttrList(base_ni)) + return 0; + + /* Remove attribute list if we don't need it any more. */ + if (!ntfs_attrlist_need(base_ni)) { + struct ntfs_attr na; + struct inode *attr_vi; + + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + ntfs_debug("Couldn't find attribute list. Succeed anyway.\n"); + return 0; + } + /* Deallocate clusters. */ + if (ctx->attr->non_resident) { + struct runlist_element *al_rl; + size_t new_rl_count; + + al_rl = ntfs_mapping_pairs_decompress(base_ni->vol, + ctx->attr, NULL, &new_rl_count); + if (IS_ERR(al_rl)) { + ntfs_debug("Couldn't decompress attribute list runlist. Succeed anyway.\n"); + return 0; + } + if (ntfs_cluster_free_from_rl(base_ni->vol, al_rl)) + ntfs_debug("Leaking clusters! Run chkdsk. Couldn't free clusters from attribute list runlist.\n"); + kvfree(al_rl); + } + /* Remove attribute record itself. */ + if (ntfs_attr_record_rm(ctx)) { + ntfs_debug("Couldn't remove attribute list. Succeed anyway.\n"); + return 0; + } + + na.mft_no = VFS_I(base_ni)->i_ino; + na.type = AT_ATTRIBUTE_LIST; + na.name = NULL; + na.name_len = 0; + + attr_vi = ilookup5(VFS_I(base_ni)->i_sb, VFS_I(base_ni)->i_ino, + ntfs_test_inode, &na); + if (attr_vi) { + clear_nlink(attr_vi); + iput(attr_vi); + } + + } + return 0; +} + +/* + * ntfs_attr_add - add attribute to inode + * @ni: opened ntfs inode to which add attribute + * @type: type of the new attribute + * @name: name in unicode of the new attribute + * @name_len: name length in unicode characters of the new attribute + * @val: value of new attribute + * @size: size of the new attribute / length of @val (if specified) + * + * @val should always be specified for always resident attributes (eg. FILE_NAME + * attribute), for attributes that can become non-resident @val can be NULL + * (eg. DATA attribute). @size can be specified even if @val is NULL, in this + * case data size will be equal to @size and initialized size will be equal + * to 0. + * + * If inode haven't got enough space to add attribute, add attribute to one of + * it extents, if no extents present or no one of them have enough space, than + * allocate new extent and add attribute to it. + * + * If on one of this steps attribute list is needed but not present, than it is + * added transparently to caller. So, this function should not be called with + * @type == AT_ATTRIBUTE_LIST, if you really need to add attribute list call + * ntfs_inode_add_attrlist instead. + * + * On success return 0. On error return -1 with errno set to the error code. + */ +int ntfs_attr_add(struct ntfs_inode *ni, __le32 type, + __le16 *name, u8 name_len, u8 *val, s64 size) +{ + struct super_block *sb; + u32 attr_rec_size; + int err, i, offset; + bool is_resident; + bool can_be_non_resident = false; + struct ntfs_inode *attr_ni; + struct inode *attr_vi; + struct mft_record *ni_mrec; + + if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) + return -EINVAL; + + ntfs_debug("Entering for inode 0x%llx, attr %x, size %lld.\n", + (long long) ni->mft_no, type, size); + + if (ni->nr_extents == -1) + ni = ni->ext.base_ntfs_ino; + + /* Check the attribute type and the size. */ + err = ntfs_attr_size_bounds_check(ni->vol, type, size); + if (err) { + if (err == -ENOENT) + err = -EIO; + return err; + } + + sb = ni->vol->sb; + /* Sanity checks for always resident attributes. */ + err = ntfs_attr_can_be_non_resident(ni->vol, type); + if (err) { + if (err != -EPERM) { + ntfs_error(sb, "ntfs_attr_can_be_non_resident failed"); + goto err_out; + } + /* @val is mandatory. */ + if (!val) { + ntfs_error(sb, + "val is mandatory for always resident attributes"); + return -EINVAL; + } + if (size > ni->vol->mft_record_size) { + ntfs_error(sb, "Attribute is too big"); + return -ERANGE; + } + } else + can_be_non_resident = true; + + /* + * Determine resident or not will be new attribute. We add 8 to size in + * non resident case for mapping pairs. + */ + err = ntfs_attr_can_be_resident(ni->vol, type); + if (!err) { + is_resident = true; + } else { + if (err != -EPERM) { + ntfs_error(sb, "ntfs_attr_can_be_resident failed"); + goto err_out; + } + is_resident = false; + } + + /* Calculate attribute record size. */ + if (is_resident) + attr_rec_size = offsetof(struct attr_record, data.resident.reserved) + + 1 + + ((name_len * sizeof(__le16) + 7) & ~7) + + ((size + 7) & ~7); + else + attr_rec_size = offsetof(struct attr_record, data.non_resident.compressed_size) + + ((name_len * sizeof(__le16) + 7) & ~7) + 8; + + /* + * If we have enough free space for the new attribute in the base MFT + * record, then add attribute to it. + */ +retry: + ni_mrec = map_mft_record(ni); + if (IS_ERR(ni_mrec)) { + err = -EIO; + goto err_out; + } + + if (le32_to_cpu(ni_mrec->bytes_allocated) - + le32_to_cpu(ni_mrec->bytes_in_use) >= attr_rec_size) { + attr_ni = ni; + unmap_mft_record(ni); + goto add_attr_record; + } + unmap_mft_record(ni); + + /* Try to add to extent inodes. */ + err = ntfs_inode_attach_all_extents(ni); + if (err) { + ntfs_error(sb, "Failed to attach all extents to inode"); + goto err_out; + } + + for (i = 0; i < ni->nr_extents; i++) { + attr_ni = ni->ext.extent_ntfs_inos[i]; + ni_mrec = map_mft_record(attr_ni); + if (IS_ERR(ni_mrec)) { + err = -EIO; + goto err_out; + } + + if (le32_to_cpu(ni_mrec->bytes_allocated) - + le32_to_cpu(ni_mrec->bytes_in_use) >= + attr_rec_size) { + unmap_mft_record(attr_ni); + goto add_attr_record; + } + unmap_mft_record(attr_ni); + } + + /* There is no extent that contain enough space for new attribute. */ + if (!NInoAttrList(ni)) { + /* Add attribute list not present, add it and retry. */ + err = ntfs_inode_add_attrlist(ni); + if (err) { + ntfs_error(sb, "Failed to add attribute list"); + goto err_out; + } + goto retry; + } + + attr_ni = NULL; + /* Allocate new extent. */ + err = ntfs_mft_record_alloc(ni->vol, 0, &attr_ni, ni, NULL); + if (err) { + ntfs_error(sb, "Failed to allocate extent record"); + goto err_out; + } + unmap_mft_record(attr_ni); + +add_attr_record: + if (is_resident) { + /* Add resident attribute. */ + offset = ntfs_resident_attr_record_add(attr_ni, type, name, + name_len, val, size, 0); + if (offset < 0) { + if (offset == -ENOSPC && can_be_non_resident) + goto add_non_resident; + err = offset; + ntfs_error(sb, "Failed to add resident attribute"); + goto free_err_out; + } + return 0; + } + +add_non_resident: + /* Add non resident attribute. */ + offset = ntfs_non_resident_attr_record_add(attr_ni, type, name, + name_len, 0, 8, 0); + if (offset < 0) { + err = offset; + ntfs_error(sb, "Failed to add non resident attribute"); + goto free_err_out; + } + + /* If @size == 0, we are done. */ + if (!size) + return 0; + + /* Open new attribute and resize it. */ + attr_vi = ntfs_attr_iget(VFS_I(ni), type, name, name_len); + if (IS_ERR(attr_vi)) { + err = PTR_ERR(attr_vi); + ntfs_error(sb, "Failed to open just added attribute"); + goto rm_attr_err_out; + } + attr_ni = NTFS_I(attr_vi); + + /* Resize and set attribute value. */ + if (ntfs_attr_truncate(attr_ni, size) || + (val && (ntfs_inode_attr_pwrite(attr_vi, 0, size, val, false) != size))) { + err = -EIO; + ntfs_error(sb, "Failed to initialize just added attribute"); + if (ntfs_attr_rm(attr_ni)) + ntfs_error(sb, "Failed to remove just added attribute"); + iput(attr_vi); + goto err_out; + } + iput(attr_vi); + return 0; + +rm_attr_err_out: + /* Remove just added attribute. */ + ni_mrec = map_mft_record(attr_ni); + if (!IS_ERR(ni_mrec)) { + if (ntfs_attr_record_resize(ni_mrec, + (struct attr_record *)((u8 *)ni_mrec + offset), 0)) + ntfs_error(sb, "Failed to remove just added attribute #2"); + unmap_mft_record(attr_ni); + } else + pr_err("EIO when try to remove new added attr\n"); + +free_err_out: + /* Free MFT record, if it doesn't contain attributes. */ + ni_mrec = map_mft_record(attr_ni); + if (!IS_ERR(ni_mrec)) { + int attr_size; + + attr_size = le32_to_cpu(ni_mrec->bytes_in_use) - + le16_to_cpu(ni_mrec->attrs_offset); + unmap_mft_record(attr_ni); + if (attr_size == 8) { + if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) + ntfs_error(sb, "Failed to free MFT record"); + if (attr_ni->nr_extents < 0) + ntfs_inode_close(attr_ni); + } + } else + pr_err("EIO when testing mft record is free-able\n"); + +err_out: + return err; +} + +/* + * __ntfs_attr_init - primary initialization of an ntfs attribute structure + * @ni: ntfs attribute inode to initialize + * @ni: ntfs inode with which to initialize the ntfs attribute + * @type: attribute type + * @name: attribute name in little endian Unicode or NULL + * @name_len: length of attribute @name in Unicode characters (if @name given) + * + * Initialize the ntfs attribute @na with @ni, @type, @name, and @name_len. + */ +static void __ntfs_attr_init(struct ntfs_inode *ni, + const __le32 type, __le16 *name, const u32 name_len) +{ + ni->runlist.rl = NULL; + ni->type = type; + ni->name = name; + if (name) + ni->name_len = name_len; + else + ni->name_len = 0; +} + +/* + * ntfs_attr_init - initialize an ntfs_attr with data sizes and status + * @ni: ntfs inode to initialize + * @non_resident: true if attribute is non-resident + * @compressed: true if attribute is compressed + * @encrypted: true if attribute is encrypted + * @sparse: true if attribute is sparse + * @allocated_size: allocated size of the attribute + * @data_size: actual data size of the attribute + * @initialized_size: initialized size of the attribute + * @compressed_size: compressed size (if compressed or sparse) + * @compression_unit: compression unit size (log2 of clusters) + * + * Final initialization for an ntfs attribute. + */ +static void ntfs_attr_init(struct ntfs_inode *ni, const bool non_resident, + const bool compressed, const bool encrypted, const bool sparse, + const s64 allocated_size, const s64 data_size, + const s64 initialized_size, const s64 compressed_size, + const u8 compression_unit) +{ + if (non_resident) + NInoSetNonResident(ni); + if (compressed) { + NInoSetCompressed(ni); + ni->flags |= FILE_ATTR_COMPRESSED; + } + if (encrypted) { + NInoSetEncrypted(ni); + ni->flags |= FILE_ATTR_ENCRYPTED; + } + if (sparse) { + NInoSetSparse(ni); + ni->flags |= FILE_ATTR_SPARSE_FILE; + } + ni->allocated_size = allocated_size; + ni->data_size = data_size; + ni->initialized_size = initialized_size; + if (compressed || sparse) { + struct ntfs_volume *vol = ni->vol; + + ni->itype.compressed.size = compressed_size; + ni->itype.compressed.block_clusters = 1 << compression_unit; + ni->itype.compressed.block_size = 1 << (compression_unit + + vol->cluster_size_bits); + ni->itype.compressed.block_size_bits = ffs( + ni->itype.compressed.block_size) - 1; + } +} + +/* + * ntfs_attr_open - open an ntfs attribute for access + * @ni: open ntfs inode in which the ntfs attribute resides + * @type: attribute type + * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL + * @name_len: length of attribute @name in Unicode characters (if @name given) + */ +int ntfs_attr_open(struct ntfs_inode *ni, const __le32 type, + __le16 *name, u32 name_len) +{ + struct ntfs_attr_search_ctx *ctx; + __le16 *newname = NULL; + struct attr_record *a; + bool cs; + struct ntfs_inode *base_ni; + int err; + + ntfs_debug("Entering for inode %lld, attr 0x%x.\n", + (unsigned long long)ni->mft_no, type); + + if (!ni || !ni->vol) + return -EINVAL; + + if (NInoAttr(ni)) + base_ni = ni->ext.base_ntfs_ino; + else + base_ni = ni; + + if (name && name != AT_UNNAMED && name != I30) { + name = ntfs_ucsndup(name, name_len); + if (!name) { + err = -ENOMEM; + goto err_out; + } + newname = name; + } + + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); + if (!ctx) { + err = -ENOMEM; + pr_err("%s: Failed to get search context\n", __func__); + goto err_out; + } + + err = ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx); + if (err) + goto put_err_out; + + a = ctx->attr; + + if (!name) { + if (a->name_length) { + name = ntfs_ucsndup((__le16 *)((u8 *)a + le16_to_cpu(a->name_offset)), + a->name_length); + if (!name) + goto put_err_out; + newname = name; + name_len = a->name_length; + } else { + name = AT_UNNAMED; + name_len = 0; + } + } + + __ntfs_attr_init(ni, type, name, name_len); + + /* + * Wipe the flags in case they are not zero for an attribute list + * attribute. Windows does not complain about invalid flags and chkdsk + * does not detect or fix them so we need to cope with it, too. + */ + if (type == AT_ATTRIBUTE_LIST) + a->flags = 0; + + if ((type == AT_DATA) && + (a->non_resident ? !a->data.non_resident.initialized_size : + !a->data.resident.value_length)) { + /* + * Define/redefine the compression state if stream is + * empty, based on the compression mark on parent + * directory (for unnamed data streams) or on current + * inode (for named data streams). The compression mark + * may change any time, the compression state can only + * change when stream is wiped out. + * + * Also prevent compression on NTFS version < 3.0 + * or cluster size > 4K or compression is disabled + */ + a->flags &= ~ATTR_COMPRESSION_MASK; + if (NInoCompressed(ni) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE)) + a->flags |= ATTR_IS_COMPRESSED; + } + + cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); + + if (ni->type == AT_DATA && ni->name == AT_UNNAMED && + ((!(a->flags & ATTR_IS_COMPRESSED) != !NInoCompressed(ni)) || + (!(a->flags & ATTR_IS_SPARSE) != !NInoSparse(ni)) || + (!(a->flags & ATTR_IS_ENCRYPTED) != !NInoEncrypted(ni)))) { + err = -EIO; + pr_err("Inode %lld has corrupt attribute flags (0x%x <> 0x%x)\n", + (unsigned long long)ni->mft_no, + a->flags, ni->flags); + goto put_err_out; + } + + if (a->non_resident) { + if (((a->flags & ATTR_COMPRESSION_MASK) || a->data.non_resident.compression_unit) && + (ni->vol->major_ver < 3)) { + err = -EIO; + pr_err("Compressed inode %lld not allowed on NTFS %d.%d\n", + (unsigned long long)ni->mft_no, + ni->vol->major_ver, + ni->vol->major_ver); + goto put_err_out; + } + + if ((a->flags & ATTR_IS_COMPRESSED) && !a->data.non_resident.compression_unit) { + err = -EIO; + pr_err("Compressed inode %lld attr 0x%x has no compression unit\n", + (unsigned long long)ni->mft_no, type); + goto put_err_out; + } + if ((a->flags & ATTR_COMPRESSION_MASK) && + (a->data.non_resident.compression_unit != STANDARD_COMPRESSION_UNIT)) { + err = -EIO; + pr_err("Compressed inode %lld attr 0x%lx has an unsupported compression unit %d\n", + (unsigned long long)ni->mft_no, + (long)le32_to_cpu(type), + (int)a->data.non_resident.compression_unit); + goto put_err_out; + } + ntfs_attr_init(ni, true, a->flags & ATTR_IS_COMPRESSED, + a->flags & ATTR_IS_ENCRYPTED, + a->flags & ATTR_IS_SPARSE, + le64_to_cpu(a->data.non_resident.allocated_size), + le64_to_cpu(a->data.non_resident.data_size), + le64_to_cpu(a->data.non_resident.initialized_size), + cs ? le64_to_cpu(a->data.non_resident.compressed_size) : 0, + cs ? a->data.non_resident.compression_unit : 0); + } else { + s64 l = le32_to_cpu(a->data.resident.value_length); + + ntfs_attr_init(ni, false, a->flags & ATTR_IS_COMPRESSED, + a->flags & ATTR_IS_ENCRYPTED, + a->flags & ATTR_IS_SPARSE, (l + 7) & ~7, l, l, + cs ? (l + 7) & ~7 : 0, 0); + } + ntfs_attr_put_search_ctx(ctx); +out: + ntfs_debug("\n"); + return err; + +put_err_out: + ntfs_attr_put_search_ctx(ctx); +err_out: + kfree(newname); + goto out; +} + +/* + * ntfs_attr_close - free an ntfs attribute structure + * @ni: ntfs inode to free + * + * Release all memory associated with the ntfs attribute @na and then release + * @na itself. + */ +void ntfs_attr_close(struct ntfs_inode *ni) +{ + if (NInoNonResident(ni) && ni->runlist.rl) + kvfree(ni->runlist.rl); + /* Don't release if using an internal constant. */ + if (ni->name != AT_UNNAMED && ni->name != I30) + kfree(ni->name); +} + +/* + * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute + * @ni: ntfs inode for which to map the runlist + * + * Map the whole runlist of the ntfs attribute @na. For an attribute made up + * of only one attribute extent this is the same as calling + * ntfs_map_runlist(ni, 0) but for an attribute with multiple extents this + * will map the runlist fragments from each of the extents thus giving access + * to the entirety of the disk allocation of an attribute. + */ +int ntfs_attr_map_whole_runlist(struct ntfs_inode *ni) +{ + s64 next_vcn, last_vcn, highest_vcn; + struct ntfs_attr_search_ctx *ctx; + struct ntfs_volume *vol = ni->vol; + struct super_block *sb = vol->sb; + struct attr_record *a; + int err; + struct ntfs_inode *base_ni; + int not_mapped; + size_t new_rl_count; + + ntfs_debug("Entering for inode 0x%llx, attr 0x%x.\n", + (unsigned long long)ni->mft_no, ni->type); + + if (NInoFullyMapped(ni) && ni->runlist.rl) + return 0; + + if (NInoAttr(ni)) + base_ni = ni->ext.base_ntfs_ino; + else + base_ni = ni; + + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); + if (!ctx) { + ntfs_error(sb, "%s: Failed to get search context", __func__); + return -ENOMEM; + } + + /* Map all attribute extents one by one. */ + next_vcn = last_vcn = highest_vcn = 0; + a = NULL; + while (1) { + struct runlist_element *rl; + + not_mapped = 0; + if (ntfs_rl_vcn_to_lcn(ni->runlist.rl, next_vcn) == LCN_RL_NOT_MAPPED) + not_mapped = 1; + + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, next_vcn, NULL, 0, ctx); + if (err) + break; + + a = ctx->attr; + + if (not_mapped) { + /* Decode the runlist. */ + rl = ntfs_mapping_pairs_decompress(ni->vol, a, &ni->runlist, + &new_rl_count); + if (IS_ERR(rl)) { + err = PTR_ERR(rl); + goto err_out; + } + ni->runlist.rl = rl; + ni->runlist.count = new_rl_count; + } + + /* Are we in the first extent? */ + if (!next_vcn) { + if (a->data.non_resident.lowest_vcn) { + err = -EIO; + ntfs_error(sb, + "First extent of inode %llu attribute has non-zero lowest_vcn", + (unsigned long long)ni->mft_no); + goto err_out; + } + /* Get the last vcn in the attribute. */ + last_vcn = ntfs_bytes_to_cluster(vol, + le64_to_cpu(a->data.non_resident.allocated_size)); + } + + /* Get the lowest vcn for the next extent. */ + highest_vcn = le64_to_cpu(a->data.non_resident.highest_vcn); + next_vcn = highest_vcn + 1; + + /* Only one extent or error, which we catch below. */ + if (next_vcn <= 0) { + err = -ENOENT; + break; + } + + /* Avoid endless loops due to corruption. */ + if (next_vcn < le64_to_cpu(a->data.non_resident.lowest_vcn)) { + err = -EIO; + ntfs_error(sb, "Inode %llu has corrupt attribute list", + (unsigned long long)ni->mft_no); + goto err_out; + } + } + if (!a) { + ntfs_error(sb, "Couldn't find attribute for runlist mapping"); + goto err_out; + } + if (not_mapped && highest_vcn && highest_vcn != last_vcn - 1) { + err = -EIO; + ntfs_error(sb, + "Failed to load full runlist: inode: %llu highest_vcn: 0x%llx last_vcn: 0x%llx", + (unsigned long long)ni->mft_no, + (long long)highest_vcn, (long long)last_vcn); + goto err_out; + } + ntfs_attr_put_search_ctx(ctx); + if (err == -ENOENT) { + NInoSetFullyMapped(ni); + return 0; + } + + return err; + +err_out: + ntfs_attr_put_search_ctx(ctx); + return err; +} + +/* + * ntfs_attr_record_move_to - move attribute record to target inode + * @ctx: attribute search context describing the attribute record + * @ni: opened ntfs inode to which move attribute record + */ +int ntfs_attr_record_move_to(struct ntfs_attr_search_ctx *ctx, struct ntfs_inode *ni) +{ + struct ntfs_attr_search_ctx *nctx; + struct attr_record *a; + int err; + struct mft_record *ni_mrec; + struct super_block *sb; + + if (!ctx || !ctx->attr || !ctx->ntfs_ino || !ni) { + ntfs_debug("Invalid arguments passed.\n"); + return -EINVAL; + } + + sb = ni->vol->sb; + ntfs_debug("Entering for ctx->attr->type 0x%x, ctx->ntfs_ino->mft_no 0x%llx, ni->mft_no 0x%llx.\n", + (unsigned int) le32_to_cpu(ctx->attr->type), + (long long) ctx->ntfs_ino->mft_no, + (long long) ni->mft_no); + + if (ctx->ntfs_ino == ni) + return 0; + + if (!ctx->al_entry) { + ntfs_debug("Inode should contain attribute list to use this function.\n"); + return -EINVAL; + } + + /* Find place in MFT record where attribute will be moved. */ + a = ctx->attr; + nctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!nctx) { + ntfs_error(sb, "%s: Failed to get search context", __func__); + return -ENOMEM; + } + + /* + * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for + * attribute in @ni->mrec, not any extent inode in case if @ni is base + * file record. + */ + err = ntfs_attr_find(a->type, (__le16 *)((u8 *)a + le16_to_cpu(a->name_offset)), + a->name_length, CASE_SENSITIVE, NULL, + 0, nctx); + if (!err) { + ntfs_debug("Attribute of such type, with same name already present in this MFT record.\n"); + err = -EEXIST; + goto put_err_out; + } + if (err != -ENOENT) { + ntfs_debug("Attribute lookup failed.\n"); + goto put_err_out; + } + + /* Make space and move attribute. */ + ni_mrec = map_mft_record(ni); + if (IS_ERR(ni_mrec)) { + err = -EIO; + goto put_err_out; + } + + err = ntfs_make_room_for_attr(ni_mrec, (u8 *) nctx->attr, + le32_to_cpu(a->length)); + if (err) { + ntfs_debug("Couldn't make space for attribute.\n"); + unmap_mft_record(ni); + goto put_err_out; + } + memcpy(nctx->attr, a, le32_to_cpu(a->length)); + nctx->attr->instance = nctx->mrec->next_attr_instance; + nctx->mrec->next_attr_instance = + cpu_to_le16((le16_to_cpu(nctx->mrec->next_attr_instance) + 1) & 0xffff); + ntfs_attr_record_resize(ctx->mrec, a, 0); + mark_mft_record_dirty(ctx->ntfs_ino); + mark_mft_record_dirty(ni); + + /* Update attribute list. */ + ctx->al_entry->mft_reference = + MK_LE_MREF(ni->mft_no, le16_to_cpu(ni_mrec->sequence_number)); + ctx->al_entry->instance = nctx->attr->instance; + unmap_mft_record(ni); +put_err_out: + ntfs_attr_put_search_ctx(nctx); + return err; +} + +/* + * ntfs_attr_record_move_away - move away attribute record from it's mft record + * @ctx: attribute search context describing the attribute record + * @extra: minimum amount of free space in the new holder of record + */ +int ntfs_attr_record_move_away(struct ntfs_attr_search_ctx *ctx, int extra) +{ + struct ntfs_inode *base_ni, *ni = NULL; + struct mft_record *m; + int i, err; + struct super_block *sb; + + if (!ctx || !ctx->attr || !ctx->ntfs_ino || extra < 0) + return -EINVAL; + + ntfs_debug("Entering for attr 0x%x, inode %llu\n", + (unsigned int) le32_to_cpu(ctx->attr->type), + (unsigned long long)ctx->ntfs_ino->mft_no); + + if (ctx->ntfs_ino->nr_extents == -1) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + + sb = ctx->ntfs_ino->vol->sb; + if (!NInoAttrList(base_ni)) { + ntfs_error(sb, "Inode %llu has no attrlist", + (unsigned long long)base_ni->mft_no); + return -EINVAL; + } + + err = ntfs_inode_attach_all_extents(ctx->ntfs_ino); + if (err) { + ntfs_error(sb, "Couldn't attach extents, inode=%llu", + (unsigned long long)base_ni->mft_no); + return err; + } + + mutex_lock(&base_ni->extent_lock); + /* Walk through all extents and try to move attribute to them. */ + for (i = 0; i < base_ni->nr_extents; i++) { + ni = base_ni->ext.extent_ntfs_inos[i]; + + if (ctx->ntfs_ino->mft_no == ni->mft_no) + continue; + m = map_mft_record(ni); + if (IS_ERR(m)) { + ntfs_error(sb, "Can not map mft record for mft_no %lld", + (unsigned long long)ni->mft_no); + mutex_unlock(&base_ni->extent_lock); + return -EIO; + } + if (le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) < le32_to_cpu(ctx->attr->length) + extra) { + unmap_mft_record(ni); + continue; + } + unmap_mft_record(ni); + + /* + * ntfs_attr_record_move_to can fail if extent with other lowest + * s64 already present in inode we trying move record to. So, + * do not return error. + */ + if (!ntfs_attr_record_move_to(ctx, ni)) { + mutex_unlock(&base_ni->extent_lock); + return 0; + } + } + mutex_unlock(&base_ni->extent_lock); + + /* + * Failed to move attribute to one of the current extents, so allocate + * new extent and move attribute to it. + */ + ni = NULL; + err = ntfs_mft_record_alloc(base_ni->vol, 0, &ni, base_ni, NULL); + if (err) { + ntfs_error(sb, "Couldn't allocate MFT record, err : %d", err); + return err; + } + unmap_mft_record(ni); + + err = ntfs_attr_record_move_to(ctx, ni); + if (err) + ntfs_error(sb, "Couldn't move attribute to MFT record"); + + return err; +} + +/* + * If we are in the first extent, then set/clean sparse bit, + * update allocated and compressed size. + */ +static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni, + struct mft_record *m, struct ntfs_attr_search_ctx *ctx) +{ + int sparse, err = 0; + struct ntfs_inode *base_ni; + struct super_block *sb = ni->vol->sb; + + ntfs_debug("Entering for inode 0x%llx, attr 0x%x\n", + (unsigned long long)ni->mft_no, ni->type); + + if (NInoAttr(ni)) + base_ni = ni->ext.base_ntfs_ino; + else + base_ni = ni; + + if (a->data.non_resident.lowest_vcn) + goto out; + + a->data.non_resident.allocated_size = cpu_to_le64(ni->allocated_size); + + sparse = ntfs_rl_sparse(ni->runlist.rl); + if (sparse < 0) { + err = -EIO; + goto out; + } + + /* Attribute become sparse. */ + if (sparse && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) { + /* + * Move attribute to another mft record, if attribute is too + * small to add compressed_size field to it and we have no + * free space in the current mft record. + */ + if ((le32_to_cpu(a->length) - + le16_to_cpu(a->data.non_resident.mapping_pairs_offset) == 8) && + !(le32_to_cpu(m->bytes_allocated) - le32_to_cpu(m->bytes_in_use))) { + + if (!NInoAttrList(base_ni)) { + err = ntfs_inode_add_attrlist(base_ni); + if (err) + goto out; + err = -EAGAIN; + goto out; + } + err = ntfs_attr_record_move_away(ctx, 8); + if (err) { + ntfs_error(sb, "Failed to move attribute"); + goto out; + } + + err = ntfs_attrlist_update(base_ni); + if (err) + goto out; + err = -EAGAIN; + goto out; + } + if (!(le32_to_cpu(a->length) - + le16_to_cpu(a->data.non_resident.mapping_pairs_offset))) { + err = -EIO; + ntfs_error(sb, "Mapping pairs space is 0"); + goto out; + } + + NInoSetSparse(ni); + ni->flags |= FILE_ATTR_SPARSE_FILE; + a->flags |= ATTR_IS_SPARSE; + a->data.non_resident.compression_unit = 0; + + memmove((u8 *)a + le16_to_cpu(a->name_offset) + 8, + (u8 *)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(__le16)); + + a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) + 8); + + a->data.non_resident.mapping_pairs_offset = + cpu_to_le16(le16_to_cpu(a->data.non_resident.mapping_pairs_offset) + 8); + } + + /* Attribute no longer sparse. */ + if (!sparse && (a->flags & ATTR_IS_SPARSE) && + !(a->flags & ATTR_IS_COMPRESSED)) { + NInoClearSparse(ni); + ni->flags &= ~FILE_ATTR_SPARSE_FILE; + a->flags &= ~ATTR_IS_SPARSE; + a->data.non_resident.compression_unit = 0; + + memmove((u8 *)a + le16_to_cpu(a->name_offset) - 8, + (u8 *)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(__le16)); + + if (le16_to_cpu(a->name_offset) >= 8) + a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) - 8); + + a->data.non_resident.mapping_pairs_offset = + cpu_to_le16(le16_to_cpu(a->data.non_resident.mapping_pairs_offset) - 8); + } + + /* Update compressed size if required. */ + if (NInoFullyMapped(ni) && (sparse || NInoCompressed(ni))) { + s64 new_compr_size; + + new_compr_size = ntfs_rl_get_compressed_size(ni->vol, ni->runlist.rl); + if (new_compr_size < 0) { + err = new_compr_size; + goto out; + } + + ni->itype.compressed.size = new_compr_size; + a->data.non_resident.compressed_size = cpu_to_le64(new_compr_size); + } + + if (NInoSparse(ni) || NInoCompressed(ni)) + VFS_I(base_ni)->i_blocks = ni->itype.compressed.size >> 9; + else + VFS_I(base_ni)->i_blocks = ni->allocated_size >> 9; + /* + * Set FILE_NAME dirty flag, to update sparse bit and + * allocated size in the index. + */ + if (ni->type == AT_DATA && ni->name == AT_UNNAMED) + NInoSetFileNameDirty(ni); +out: + return err; +} + +#define NTFS_VCN_DELETE_MARK -2 +/* + * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute + * @ni: non-resident ntfs inode for which we need update + * @from_vcn: update runlist starting this VCN + * + * Build mapping pairs from @na->rl and write them to the disk. Also, this + * function updates sparse bit, allocated and compressed size (allocates/frees + * space for this field if required). + * + * @na->allocated_size should be set to correct value for the new runlist before + * call to this function. Vice-versa @na->compressed_size will be calculated and + * set to correct value during this function. + */ +int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn) +{ + struct ntfs_attr_search_ctx *ctx; + struct ntfs_inode *base_ni; + struct mft_record *m; + struct attr_record *a; + s64 stop_vcn; + int err = 0, mp_size, cur_max_mp_size, exp_max_mp_size; + bool finished_build; + bool first_updated = false; + struct super_block *sb; + struct runlist_element *start_rl; + unsigned int de_cluster_count = 0; + +retry: + if (!ni || !ni->runlist.rl) + return -EINVAL; + + ntfs_debug("Entering for inode %llu, attr 0x%x\n", + (unsigned long long)ni->mft_no, ni->type); + + sb = ni->vol->sb; + if (!NInoNonResident(ni)) { + ntfs_error(sb, "%s: resident attribute", __func__); + return -EINVAL; + } + + if (ni->nr_extents == -1) + base_ni = ni->ext.base_ntfs_ino; + else + base_ni = ni; + + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); + if (!ctx) { + ntfs_error(sb, "%s: Failed to get search context", __func__); + return -ENOMEM; + } + + /* Fill attribute records with new mapping pairs. */ + stop_vcn = 0; + finished_build = false; + start_rl = ni->runlist.rl; + while (!(err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, from_vcn, NULL, 0, ctx))) { + unsigned int de_cnt = 0; + + a = ctx->attr; + m = ctx->mrec; + if (!a->data.non_resident.lowest_vcn) + first_updated = true; + + /* + * If runlist is updating not from the beginning, then set + * @stop_vcn properly, i.e. to the lowest vcn of record that + * contain @from_vcn. Also we do not need @from_vcn anymore, + * set it to 0 to make ntfs_attr_lookup enumerate attributes. + */ + if (from_vcn) { + s64 first_lcn; + + stop_vcn = le64_to_cpu(a->data.non_resident.lowest_vcn); + from_vcn = 0; + /* + * Check whether the first run we need to update is + * the last run in runlist, if so, then deallocate + * all attrubute extents starting this one. + */ + first_lcn = ntfs_rl_vcn_to_lcn(ni->runlist.rl, stop_vcn); + if (first_lcn == LCN_EINVAL) { + err = -EIO; + ntfs_error(sb, "Bad runlist"); + goto put_err_out; + } + if (first_lcn == LCN_ENOENT || + first_lcn == LCN_RL_NOT_MAPPED) + finished_build = true; + } + + /* + * Check whether we finished mapping pairs build, if so mark + * extent as need to delete (by setting highest vcn to + * NTFS_VCN_DELETE_MARK (-2), we shall check it later and + * delete extent) and continue search. + */ + if (finished_build) { + ntfs_debug("Mark attr 0x%x for delete in inode 0x%llx.\n", + (unsigned int)le32_to_cpu(a->type), ctx->ntfs_ino->mft_no); + a->data.non_resident.highest_vcn = cpu_to_le64(NTFS_VCN_DELETE_MARK); + mark_mft_record_dirty(ctx->ntfs_ino); + continue; + } + + err = ntfs_attr_update_meta(a, ni, m, ctx); + if (err < 0) { + if (err == -EAGAIN) { + ntfs_attr_put_search_ctx(ctx); + goto retry; + } + goto put_err_out; + } + + /* + * Determine maximum possible length of mapping pairs, + * if we shall *not* expand space for mapping pairs. + */ + cur_max_mp_size = le32_to_cpu(a->length) - + le16_to_cpu(a->data.non_resident.mapping_pairs_offset); + /* + * Determine maximum possible length of mapping pairs in the + * current mft record, if we shall expand space for mapping + * pairs. + */ + exp_max_mp_size = le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) + cur_max_mp_size; + + /* Get the size for the rest of mapping pairs array. */ + mp_size = ntfs_get_size_for_mapping_pairs(ni->vol, start_rl, + stop_vcn, -1, exp_max_mp_size); + if (mp_size <= 0) { + err = mp_size; + ntfs_error(sb, "%s: get MP size failed", __func__); + goto put_err_out; + } + /* Test mapping pairs for fitting in the current mft record. */ + if (mp_size > exp_max_mp_size) { + /* + * Mapping pairs of $ATTRIBUTE_LIST attribute must fit + * in the base mft record. Try to move out other + * attributes and try again. + */ + if (ni->type == AT_ATTRIBUTE_LIST) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_free_space(base_ni, mp_size - + cur_max_mp_size)) { + ntfs_debug("Attribute list is too big. Defragment the volume\n"); + return -ENOSPC; + } + if (ntfs_attrlist_update(base_ni)) + return -EIO; + goto retry; + } + + /* Add attribute list if it isn't present, and retry. */ + if (!NInoAttrList(base_ni)) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_add_attrlist(base_ni)) { + ntfs_error(sb, "Can not add attrlist"); + return -EIO; + } + goto retry; + } + + /* + * Set mapping pairs size to maximum possible for this + * mft record. We shall write the rest of mapping pairs + * to another MFT records. + */ + mp_size = exp_max_mp_size; + } + + /* Change space for mapping pairs if we need it. */ + if (((mp_size + 7) & ~7) != cur_max_mp_size) { + if (ntfs_attr_record_resize(m, a, + le16_to_cpu(a->data.non_resident.mapping_pairs_offset) + + mp_size)) { + err = -EIO; + ntfs_error(sb, "Failed to resize attribute"); + goto put_err_out; + } + } + + /* Update lowest vcn. */ + a->data.non_resident.lowest_vcn = cpu_to_le64(stop_vcn); + mark_mft_record_dirty(ctx->ntfs_ino); + if ((ctx->ntfs_ino->nr_extents == -1 || NInoAttrList(ctx->ntfs_ino)) && + ctx->attr->type != AT_ATTRIBUTE_LIST) { + ctx->al_entry->lowest_vcn = cpu_to_le64(stop_vcn); + err = ntfs_attrlist_update(base_ni); + if (err) + goto put_err_out; + } + + /* + * Generate the new mapping pairs array directly into the + * correct destination, i.e. the attribute record itself. + */ + err = ntfs_mapping_pairs_build(ni->vol, + (u8 *)a + le16_to_cpu(a->data.non_resident.mapping_pairs_offset), + mp_size, start_rl, stop_vcn, -1, &stop_vcn, &start_rl, &de_cnt); + if (!err) + finished_build = true; + if (!finished_build && err != -ENOSPC) { + ntfs_error(sb, "Failed to build mapping pairs"); + goto put_err_out; + } + a->data.non_resident.highest_vcn = cpu_to_le64(stop_vcn - 1); + mark_mft_record_dirty(ctx->ntfs_ino); + de_cluster_count += de_cnt; + } + + /* Check whether error occurred. */ + if (err && err != -ENOENT) { + ntfs_error(sb, "%s: Attribute lookup failed", __func__); + goto put_err_out; + } + + /* + * If the base extent was skipped in the above process, + * we still may have to update the sizes. + */ + if (!first_updated) { + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (!err) { + a = ctx->attr; + a->data.non_resident.allocated_size = cpu_to_le64(ni->allocated_size); + if (NInoCompressed(ni) || NInoSparse(ni)) + a->data.non_resident.compressed_size = + cpu_to_le64(ni->itype.compressed.size); + /* Updating sizes taints the extent holding the attr */ + if (ni->type == AT_DATA && ni->name == AT_UNNAMED) + NInoSetFileNameDirty(ni); + mark_mft_record_dirty(ctx->ntfs_ino); + } else { + ntfs_error(sb, "Failed to update sizes in base extent\n"); + goto put_err_out; + } + } + + /* Deallocate not used attribute extents and return with success. */ + if (finished_build) { + ntfs_attr_reinit_search_ctx(ctx); + ntfs_debug("Deallocate marked extents.\n"); + while (!(err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx))) { + if (le64_to_cpu(ctx->attr->data.non_resident.highest_vcn) != + NTFS_VCN_DELETE_MARK) + continue; + /* Remove unused attribute record. */ + err = ntfs_attr_record_rm(ctx); + if (err) { + ntfs_error(sb, "Could not remove unused attr"); + goto put_err_out; + } + ntfs_attr_reinit_search_ctx(ctx); + } + if (err && err != -ENOENT) { + ntfs_error(sb, "%s: Attr lookup failed", __func__); + goto put_err_out; + } + ntfs_debug("Deallocate done.\n"); + ntfs_attr_put_search_ctx(ctx); + goto out; + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + + /* Allocate new MFT records for the rest of mapping pairs. */ + while (1) { + struct ntfs_inode *ext_ni = NULL; + unsigned int de_cnt = 0; + + /* Allocate new mft record. */ + err = ntfs_mft_record_alloc(ni->vol, 0, &ext_ni, base_ni, NULL); + if (err) { + ntfs_error(sb, "Failed to allocate extent record"); + goto put_err_out; + } + unmap_mft_record(ext_ni); + + m = map_mft_record(ext_ni); + if (IS_ERR(m)) { + ntfs_error(sb, "Could not map new MFT record"); + if (ntfs_mft_record_free(ni->vol, ext_ni)) + ntfs_error(sb, "Could not free MFT record"); + ntfs_inode_close(ext_ni); + err = -ENOMEM; + ext_ni = NULL; + goto put_err_out; + } + /* + * If mapping size exceed available space, set them to + * possible maximum. + */ + cur_max_mp_size = le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) - + (sizeof(struct attr_record) + + ((NInoCompressed(ni) || NInoSparse(ni)) ? + sizeof(a->data.non_resident.compressed_size) : 0)) - + ((sizeof(__le16) * ni->name_len + 7) & ~7); + + /* Calculate size of rest mapping pairs. */ + mp_size = ntfs_get_size_for_mapping_pairs(ni->vol, + start_rl, stop_vcn, -1, cur_max_mp_size); + if (mp_size <= 0) { + unmap_mft_record(ext_ni); + ntfs_inode_close(ext_ni); + err = mp_size; + ntfs_error(sb, "%s: get mp size failed", __func__); + goto put_err_out; + } + + if (mp_size > cur_max_mp_size) + mp_size = cur_max_mp_size; + /* Add attribute extent to new record. */ + err = ntfs_non_resident_attr_record_add(ext_ni, ni->type, + ni->name, ni->name_len, stop_vcn, mp_size, 0); + if (err < 0) { + ntfs_error(sb, "Could not add attribute extent"); + unmap_mft_record(ext_ni); + if (ntfs_mft_record_free(ni->vol, ext_ni)) + ntfs_error(sb, "Could not free MFT record"); + ntfs_inode_close(ext_ni); + goto put_err_out; + } + a = (struct attr_record *)((u8 *)m + err); + + err = ntfs_mapping_pairs_build(ni->vol, (u8 *)a + + le16_to_cpu(a->data.non_resident.mapping_pairs_offset), + mp_size, start_rl, stop_vcn, -1, &stop_vcn, &start_rl, + &de_cnt); + if (err < 0 && err != -ENOSPC) { + ntfs_error(sb, "Failed to build MP"); + unmap_mft_record(ext_ni); + if (ntfs_mft_record_free(ni->vol, ext_ni)) + ntfs_error(sb, "Couldn't free MFT record"); + goto put_err_out; + } + a->data.non_resident.highest_vcn = cpu_to_le64(stop_vcn - 1); + mark_mft_record_dirty(ext_ni); + unmap_mft_record(ext_ni); + + de_cluster_count += de_cnt; + /* All mapping pairs has been written. */ + if (!err) + break; + } +out: + if (from_vcn == 0) + ni->i_dealloc_clusters = de_cluster_count; + return 0; + +put_err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return err; +} + +/* + * ntfs_attr_make_resident - convert a non-resident to a resident attribute + * @ni: open ntfs attribute to make resident + * @ctx: ntfs search context describing the attribute + * + * Convert a non-resident ntfs attribute to a resident one. + */ +static int ntfs_attr_make_resident(struct ntfs_inode *ni, struct ntfs_attr_search_ctx *ctx) +{ + struct ntfs_volume *vol = ni->vol; + struct super_block *sb = vol->sb; + struct attr_record *a = ctx->attr; + int name_ofs, val_ofs, err; + s64 arec_size; + + ntfs_debug("Entering for inode 0x%llx, attr 0x%x.\n", + (unsigned long long)ni->mft_no, ni->type); + + /* Should be called for the first extent of the attribute. */ + if (le64_to_cpu(a->data.non_resident.lowest_vcn)) { + ntfs_debug("Eeek! Should be called for the first extent of the attribute. Aborting...\n"); + return -EINVAL; + } + + /* Some preliminary sanity checking. */ + if (!NInoNonResident(ni)) { + ntfs_debug("Eeek! Trying to make resident attribute resident. Aborting...\n"); + return -EINVAL; + } + + /* Make sure this is not $MFT/$BITMAP or Windows will not boot! */ + if (ni->type == AT_BITMAP && ni->mft_no == FILE_MFT) + return -EPERM; + + /* Check that the attribute is allowed to be resident. */ + err = ntfs_attr_can_be_resident(vol, ni->type); + if (err) + return err; + + if (NInoCompressed(ni) || NInoEncrypted(ni)) { + ntfs_debug("Making compressed or encrypted files resident is not implemented yet.\n"); + return -EOPNOTSUPP; + } + + /* Work out offsets into and size of the resident attribute. */ + name_ofs = 24; /* = sizeof(resident_struct attr_record); */ + val_ofs = (name_ofs + a->name_length * sizeof(__le16) + 7) & ~7; + arec_size = (val_ofs + ni->data_size + 7) & ~7; + + /* Sanity check the size before we start modifying the attribute. */ + if (le32_to_cpu(ctx->mrec->bytes_in_use) - le32_to_cpu(a->length) + + arec_size > le32_to_cpu(ctx->mrec->bytes_allocated)) { + ntfs_debug("Not enough space to make attribute resident\n"); + return -ENOSPC; + } + + /* Read and cache the whole runlist if not already done. */ + err = ntfs_attr_map_whole_runlist(ni); + if (err) + return err; + + /* Move the attribute name if it exists and update the offset. */ + if (a->name_length) { + memmove((u8 *)a + name_ofs, (u8 *)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(__le16)); + } + a->name_offset = cpu_to_le16(name_ofs); + + /* Resize the resident part of the attribute record. */ + if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { + /* + * Bug, because ntfs_attr_record_resize should not fail (we + * already checked that attribute fits MFT record). + */ + ntfs_error(ctx->ntfs_ino->vol->sb, "BUG! Failed to resize attribute record. "); + return -EIO; + } + + /* Convert the attribute record to describe a resident attribute. */ + a->non_resident = 0; + a->flags = 0; + a->data.resident.value_length = cpu_to_le32(ni->data_size); + a->data.resident.value_offset = cpu_to_le16(val_ofs); + /* + * File names cannot be non-resident so we would never see this here + * but at least it serves as a reminder that there may be attributes + * for which we do need to set this flag. (AIA) + */ + if (a->type == AT_FILE_NAME) + a->data.resident.flags = RESIDENT_ATTR_IS_INDEXED; + else + a->data.resident.flags = 0; + a->data.resident.reserved = 0; + + /* + * Deallocate clusters from the runlist. + * + * NOTE: We can use ntfs_cluster_free() because we have already mapped + * the whole run list and thus it doesn't matter that the attribute + * record is in a transiently corrupted state at this moment in time. + */ + err = ntfs_cluster_free(ni, 0, -1, ctx); + if (err) { + ntfs_error(sb, "Eeek! Failed to release allocated clusters"); + ntfs_debug("Ignoring error and leaving behind wasted clusters.\n"); + } + + /* Throw away the now unused runlist. */ + kvfree(ni->runlist.rl); + ni->runlist.rl = NULL; + ni->runlist.count = 0; + /* Update in-memory struct ntfs_attr. */ + NInoClearNonResident(ni); + NInoClearCompressed(ni); + ni->flags &= ~FILE_ATTR_COMPRESSED; + NInoClearSparse(ni); + ni->flags &= ~FILE_ATTR_SPARSE_FILE; + NInoClearEncrypted(ni); + ni->flags &= ~FILE_ATTR_ENCRYPTED; + ni->initialized_size = ni->data_size; + ni->allocated_size = ni->itype.compressed.size = (ni->data_size + 7) & ~7; + ni->itype.compressed.block_size = 0; + ni->itype.compressed.block_size_bits = ni->itype.compressed.block_clusters = 0; + return 0; +} + +/* + * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute + * @ni: non-resident ntfs attribute to shrink + * @newsize: new size (in bytes) to which to shrink the attribute + * + * Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes. + */ +static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsize) +{ + struct ntfs_volume *vol; + struct ntfs_attr_search_ctx *ctx; + s64 first_free_vcn; + s64 nr_freed_clusters; + int err; + struct ntfs_inode *base_ni; + + ntfs_debug("Inode 0x%llx attr 0x%x new size %lld\n", + (unsigned long long)ni->mft_no, ni->type, (long long)newsize); + + vol = ni->vol; + + if (NInoAttr(ni)) + base_ni = ni->ext.base_ntfs_ino; + else + base_ni = ni; + + /* + * Check the attribute type and the corresponding minimum size + * against @newsize and fail if @newsize is too small. + */ + err = ntfs_attr_size_bounds_check(vol, ni->type, newsize); + if (err) { + if (err == -ERANGE) + ntfs_debug("Eeek! Size bounds check failed. Aborting...\n"); + else if (err == -ENOENT) + err = -EIO; + return err; + } + + /* The first cluster outside the new allocation. */ + if (NInoCompressed(ni)) + /* + * For compressed files we must keep full compressions blocks, + * but currently we do not decompress/recompress the last + * block to truncate the data, so we may leave more allocated + * clusters than really needed. + */ + first_free_vcn = ntfs_bytes_to_cluster(vol, + ((newsize - 1) | (ni->itype.compressed.block_size - 1)) + 1); + else + first_free_vcn = + ntfs_bytes_to_cluster(vol, newsize + vol->cluster_size - 1); + + if (first_free_vcn < 0) + return -EINVAL; + /* + * Compare the new allocation with the old one and only deallocate + * clusters if there is a change. + */ + if (ntfs_bytes_to_cluster(vol, ni->allocated_size) != first_free_vcn) { + struct ntfs_attr_search_ctx *ctx; + + err = ntfs_attr_map_whole_runlist(ni); + if (err) { + ntfs_debug("Eeek! ntfs_attr_map_whole_runlist failed.\n"); + return err; + } + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_error(vol->sb, "%s: Failed to get search context", __func__); + return -ENOMEM; + } + + /* Deallocate all clusters starting with the first free one. */ + nr_freed_clusters = ntfs_cluster_free(ni, first_free_vcn, -1, ctx); + if (nr_freed_clusters < 0) { + ntfs_debug("Eeek! Freeing of clusters failed. Aborting...\n"); + ntfs_attr_put_search_ctx(ctx); + return (int)nr_freed_clusters; + } + ntfs_attr_put_search_ctx(ctx); + + /* Truncate the runlist itself. */ + if (ntfs_rl_truncate_nolock(vol, &ni->runlist, first_free_vcn)) { + /* + * Failed to truncate the runlist, so just throw it + * away, it will be mapped afresh on next use. + */ + kvfree(ni->runlist.rl); + ni->runlist.rl = NULL; + ntfs_error(vol->sb, "Eeek! Run list truncation failed.\n"); + return -EIO; + } + + /* Prepare to mapping pairs update. */ + ni->allocated_size = ntfs_cluster_to_bytes(vol, first_free_vcn); + + if (NInoSparse(ni) || NInoCompressed(ni)) { + if (nr_freed_clusters) { + ni->itype.compressed.size -= + ntfs_cluster_to_bytes(vol, nr_freed_clusters); + VFS_I(base_ni)->i_blocks = ni->itype.compressed.size >> 9; + } + } else + VFS_I(base_ni)->i_blocks = ni->allocated_size >> 9; + + /* Write mapping pairs for new runlist. */ + err = ntfs_attr_update_mapping_pairs(ni, 0 /*first_free_vcn*/); + if (err) { + ntfs_debug("Eeek! Mapping pairs update failed. Leaving inconstant metadata. Run chkdsk.\n"); + return err; + } + } + + /* Get the first attribute record. */ + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); + if (!ctx) { + ntfs_error(vol->sb, "%s: Failed to get search context", __func__); + return -ENOMEM; + } + + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx); + if (err) { + if (err == -ENOENT) + err = -EIO; + ntfs_debug("Eeek! Lookup of first attribute extent failed. Leaving inconstant metadata.\n"); + goto put_err_out; + } + + /* Update data and initialized size. */ + ni->data_size = newsize; + ctx->attr->data.non_resident.data_size = cpu_to_le64(newsize); + if (newsize < ni->initialized_size) { + ni->initialized_size = newsize; + ctx->attr->data.non_resident.initialized_size = cpu_to_le64(newsize); + } + /* Update data size in the index. */ + if (ni->type == AT_DATA && ni->name == AT_UNNAMED) + NInoSetFileNameDirty(ni); + + /* If the attribute now has zero size, make it resident. */ + if (!newsize && !NInoEncrypted(ni) && !NInoCompressed(ni)) { + err = ntfs_attr_make_resident(ni, ctx); + if (err) { + /* If couldn't make resident, just continue. */ + if (err != -EPERM) + ntfs_error(ni->vol->sb, + "Failed to make attribute resident. Leaving as is...\n"); + } + } + + /* Set the inode dirty so it is written out later. */ + mark_mft_record_dirty(ctx->ntfs_ino); + /* Done! */ + ntfs_attr_put_search_ctx(ctx); + return 0; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + return err; +} + +/* + * ntfs_non_resident_attr_expand - expand a non-resident, open ntfs attribute + * @ni: non-resident ntfs attribute to expand + * @prealloc_size: preallocation size (in bytes) to which to expand the attribute + * @newsize: new size (in bytes) to which to expand the attribute + * @holes: how to create a hole if expanding + * @need_lock: whether mrec lock is needed or not + * + * Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes, + * by allocating new clusters. + */ +static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsize, + const s64 prealloc_size, unsigned int holes, bool need_lock) +{ + s64 lcn_seek_from; + s64 first_free_vcn; + struct ntfs_volume *vol; + struct ntfs_attr_search_ctx *ctx = NULL; + struct runlist_element *rl, *rln; + s64 org_alloc_size, org_compressed_size; + int err, err2; + struct ntfs_inode *base_ni; + struct super_block *sb = ni->vol->sb; + size_t new_rl_count; + + ntfs_debug("Inode 0x%llx, attr 0x%x, new size %lld old size %lld\n", + (unsigned long long)ni->mft_no, ni->type, + (long long)newsize, (long long)ni->data_size); + + vol = ni->vol; + + if (NInoAttr(ni)) + base_ni = ni->ext.base_ntfs_ino; + else + base_ni = ni; + + /* + * Check the attribute type and the corresponding maximum size + * against @newsize and fail if @newsize is too big. + */ + err = ntfs_attr_size_bounds_check(vol, ni->type, newsize); + if (err < 0) { + ntfs_error(sb, "%s: bounds check failed", __func__); + return err; + } + + /* Save for future use. */ + org_alloc_size = ni->allocated_size; + org_compressed_size = ni->itype.compressed.size; + + /* The first cluster outside the new allocation. */ + if (prealloc_size) + first_free_vcn = + ntfs_bytes_to_cluster(vol, prealloc_size + vol->cluster_size - 1); + else + first_free_vcn = + ntfs_bytes_to_cluster(vol, newsize + vol->cluster_size - 1); + if (first_free_vcn < 0) + return -EFBIG; + + /* + * Compare the new allocation with the old one and only allocate + * clusters if there is a change. + */ + if (ntfs_bytes_to_cluster(vol, ni->allocated_size) < first_free_vcn) { + err = ntfs_attr_map_whole_runlist(ni); + if (err) { + ntfs_error(sb, "ntfs_attr_map_whole_runlist failed"); + return err; + } + + /* + * If we extend $DATA attribute on NTFS 3+ volume, we can add + * sparse runs instead of real allocation of clusters. + */ + if ((ni->type == AT_DATA && (vol->major_ver >= 3 || !NInoSparseDisabled(ni))) && + (holes != HOLES_NO)) { + if (NInoCompressed(ni)) { + int last = 0, i = 0; + s64 alloc_size; + u64 more_entries = round_up(first_free_vcn - + ntfs_bytes_to_cluster(vol, ni->allocated_size), + ni->itype.compressed.block_clusters); + + do_div(more_entries, ni->itype.compressed.block_clusters); + + while (ni->runlist.rl[last].length) + last++; + + rl = ntfs_rl_realloc(ni->runlist.rl, last + 1, + last + more_entries + 1); + if (IS_ERR(rl)) { + err = -ENOMEM; + goto put_err_out; + } + + alloc_size = ni->allocated_size; + while (i++ < more_entries) { + rl[last].vcn = ntfs_bytes_to_cluster(vol, + round_up(alloc_size, vol->cluster_size)); + rl[last].length = ni->itype.compressed.block_clusters - + (rl[last].vcn & + (ni->itype.compressed.block_clusters - 1)); + rl[last].lcn = LCN_HOLE; + last++; + alloc_size += ni->itype.compressed.block_size; + } + + rl[last].vcn = first_free_vcn; + rl[last].lcn = LCN_ENOENT; + rl[last].length = 0; + + ni->runlist.rl = rl; + ni->runlist.count += more_entries; + } else { + rl = kmalloc(sizeof(struct runlist_element) * 2, GFP_NOFS); + if (!rl) { + err = -ENOMEM; + goto put_err_out; + } + + rl[0].vcn = ntfs_bytes_to_cluster(vol, ni->allocated_size); + rl[0].lcn = LCN_HOLE; + rl[0].length = first_free_vcn - + ntfs_bytes_to_cluster(vol, ni->allocated_size); + rl[1].vcn = first_free_vcn; + rl[1].lcn = LCN_ENOENT; + rl[1].length = 0; + } + } else { + /* + * Determine first after last LCN of attribute. + * We will start seek clusters from this LCN to avoid + * fragmentation. If there are no valid LCNs in the + * attribute let the cluster allocator choose the + * starting LCN. + */ + lcn_seek_from = -1; + if (ni->runlist.rl->length) { + /* Seek to the last run list element. */ + for (rl = ni->runlist.rl; (rl + 1)->length; rl++) + ; + /* + * If the last LCN is a hole or similar seek + * back to last valid LCN. + */ + while (rl->lcn < 0 && rl != ni->runlist.rl) + rl--; + /* + * Only set lcn_seek_from it the LCN is valid. + */ + if (rl->lcn >= 0) + lcn_seek_from = rl->lcn + rl->length; + } + + rl = ntfs_cluster_alloc(vol, + ntfs_bytes_to_cluster(vol, ni->allocated_size), + first_free_vcn - + ntfs_bytes_to_cluster(vol, ni->allocated_size), + lcn_seek_from, DATA_ZONE, false, false, false); + if (IS_ERR(rl)) { + ntfs_debug("Cluster allocation failed (%lld)", + (long long)first_free_vcn - + ntfs_bytes_to_cluster(vol, ni->allocated_size)); + return PTR_ERR(rl); + } + } + + if (!NInoCompressed(ni)) { + /* Append new clusters to attribute runlist. */ + rln = ntfs_runlists_merge(&ni->runlist, rl, 0, &new_rl_count); + if (IS_ERR(rln)) { + /* Failed, free just allocated clusters. */ + ntfs_error(sb, "Run list merge failed"); + ntfs_cluster_free_from_rl(vol, rl); + kvfree(rl); + return -EIO; + } + ni->runlist.rl = rln; + ni->runlist.count = new_rl_count; + } + + /* Prepare to mapping pairs update. */ + ni->allocated_size = ntfs_cluster_to_bytes(vol, first_free_vcn); + err = ntfs_attr_update_mapping_pairs(ni, 0); + if (err) { + ntfs_debug("Mapping pairs update failed"); + goto rollback; + } + } + + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); + if (!ctx) { + err = -ENOMEM; + if (ni->allocated_size == org_alloc_size) + return err; + goto rollback; + } + + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx); + if (err) { + if (err == -ENOENT) + err = -EIO; + if (ni->allocated_size != org_alloc_size) + goto rollback; + goto put_err_out; + } + + /* Update data size. */ + ni->data_size = newsize; + ctx->attr->data.non_resident.data_size = cpu_to_le64(newsize); + /* Update data size in the index. */ + if (ni->type == AT_DATA && ni->name == AT_UNNAMED) + NInoSetFileNameDirty(ni); + /* Set the inode dirty so it is written out later. */ + mark_mft_record_dirty(ctx->ntfs_ino); + /* Done! */ + ntfs_attr_put_search_ctx(ctx); + return 0; +rollback: + /* Free allocated clusters. */ + err2 = ntfs_cluster_free(ni, ntfs_bytes_to_cluster(vol, org_alloc_size), + -1, ctx); + if (err2) + ntfs_debug("Leaking clusters"); + + /* Now, truncate the runlist itself. */ + if (need_lock) + down_write(&ni->runlist.lock); + err2 = ntfs_rl_truncate_nolock(vol, &ni->runlist, + ntfs_bytes_to_cluster(vol, org_alloc_size)); + if (need_lock) + up_write(&ni->runlist.lock); + if (err2) { + /* + * Failed to truncate the runlist, so just throw it away, it + * will be mapped afresh on next use. + */ + kvfree(ni->runlist.rl); + ni->runlist.rl = NULL; + ntfs_error(sb, "Couldn't truncate runlist. Rollback failed"); + } else { + /* Prepare to mapping pairs update. */ + ni->allocated_size = org_alloc_size; + /* Restore mapping pairs. */ + if (need_lock) + down_read(&ni->runlist.lock); + if (ntfs_attr_update_mapping_pairs(ni, 0)) + ntfs_error(sb, "Failed to restore old mapping pairs"); + if (need_lock) + up_read(&ni->runlist.lock); + + if (NInoSparse(ni) || NInoCompressed(ni)) { + ni->itype.compressed.size = org_compressed_size; + VFS_I(base_ni)->i_blocks = ni->itype.compressed.size >> 9; + } else + VFS_I(base_ni)->i_blocks = ni->allocated_size >> 9; + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return err; +put_err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return err; +} + +/* + * ntfs_resident_attr_resize - resize a resident, open ntfs attribute + * @attr_ni: resident ntfs inode to resize + * @newsize: new size (in bytes) to which to resize the attribute + * @prealloc_size: preallocation size (in bytes) to which to resize the attribute + * @holes: flags indicating how to handle holes + * + * Change the size of a resident, open ntfs attribute @na to @newsize bytes. + */ +static int ntfs_resident_attr_resize(struct ntfs_inode *attr_ni, const s64 newsize, + const s64 prealloc_size, unsigned int holes) +{ + struct ntfs_attr_search_ctx *ctx; + struct ntfs_volume *vol = attr_ni->vol; + struct super_block *sb = vol->sb; + int err = -EIO; + struct ntfs_inode *base_ni, *ext_ni = NULL; + +attr_resize_again: + ntfs_debug("Inode 0x%llx attr 0x%x new size %lld\n", + (unsigned long long)attr_ni->mft_no, attr_ni->type, + (long long)newsize); + + if (NInoAttr(attr_ni)) + base_ni = attr_ni->ext.base_ntfs_ino; + else + base_ni = attr_ni; + + /* Get the attribute record that needs modification. */ + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); + if (!ctx) { + ntfs_error(sb, "%s: Failed to get search context", __func__); + return -ENOMEM; + } + + err = ntfs_attr_lookup(attr_ni->type, attr_ni->name, attr_ni->name_len, + 0, 0, NULL, 0, ctx); + if (err) { + ntfs_error(sb, "ntfs_attr_lookup failed"); + goto put_err_out; + } + + /* + * Check the attribute type and the corresponding minimum and maximum + * sizes against @newsize and fail if @newsize is out of bounds. + */ + err = ntfs_attr_size_bounds_check(vol, attr_ni->type, newsize); + if (err) { + if (err == -ENOENT) + err = -EIO; + ntfs_debug("%s: bounds check failed", __func__); + goto put_err_out; + } + /* + * If @newsize is bigger than the mft record we need to make the + * attribute non-resident if the attribute type supports it. If it is + * smaller we can go ahead and attempt the resize. + */ + if (newsize < vol->mft_record_size) { + /* Perform the resize of the attribute record. */ + err = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, + newsize); + if (!err) { + /* Update attribute size everywhere. */ + attr_ni->data_size = attr_ni->initialized_size = newsize; + attr_ni->allocated_size = (newsize + 7) & ~7; + if (NInoCompressed(attr_ni) || NInoSparse(attr_ni)) + attr_ni->itype.compressed.size = attr_ni->allocated_size; + if (attr_ni->type == AT_DATA && attr_ni->name == AT_UNNAMED) + NInoSetFileNameDirty(attr_ni); + goto resize_done; + } + + /* Prefer AT_INDEX_ALLOCATION instead of AT_ATTRIBUTE_LIST */ + if (err == -ENOSPC && ctx->attr->type == AT_INDEX_ROOT) + goto put_err_out; + + } + /* There is not enough space in the mft record to perform the resize. */ + + /* Make the attribute non-resident if possible. */ + err = ntfs_attr_make_non_resident(attr_ni, + le32_to_cpu(ctx->attr->data.resident.value_length)); + if (!err) { + mark_mft_record_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + /* Resize non-resident attribute */ + return ntfs_non_resident_attr_expand(attr_ni, newsize, prealloc_size, holes, true); + } else if (err != -ENOSPC && err != -EPERM) { + ntfs_error(sb, "Failed to make attribute non-resident"); + goto put_err_out; + } + + /* Try to make other attributes non-resident and retry each time. */ + ntfs_attr_reinit_search_ctx(ctx); + while (!(err = ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx))) { + struct inode *tvi; + struct attr_record *a; + + a = ctx->attr; + if (a->non_resident || a->type == AT_ATTRIBUTE_LIST) + continue; + + if (ntfs_attr_can_be_non_resident(vol, a->type)) + continue; + + /* + * Check out whether convert is reasonable. Assume that mapping + * pairs will take 8 bytes. + */ + if (le32_to_cpu(a->length) <= (sizeof(struct attr_record) - sizeof(s64)) + + ((a->name_length * sizeof(__le16) + 7) & ~7) + 8) + continue; + + if (a->type == AT_DATA) + tvi = ntfs_iget(sb, base_ni->mft_no); + else + tvi = ntfs_attr_iget(VFS_I(base_ni), a->type, + (__le16 *)((u8 *)a + le16_to_cpu(a->name_offset)), + a->name_length); + if (IS_ERR(tvi)) { + ntfs_error(sb, "Couldn't open attribute"); + continue; + } + + if (ntfs_attr_make_non_resident(NTFS_I(tvi), + le32_to_cpu(ctx->attr->data.resident.value_length))) { + iput(tvi); + continue; + } + + mark_mft_record_dirty(ctx->ntfs_ino); + iput(tvi); + ntfs_attr_put_search_ctx(ctx); + goto attr_resize_again; + } + + /* Check whether error occurred. */ + if (err != -ENOENT) { + ntfs_error(sb, "%s: Attribute lookup failed 1", __func__); + goto put_err_out; + } + + /* + * The standard information and attribute list attributes can't be + * moved out from the base MFT record, so try to move out others. + */ + if (attr_ni->type == AT_STANDARD_INFORMATION || + attr_ni->type == AT_ATTRIBUTE_LIST) { + ntfs_attr_put_search_ctx(ctx); + + if (!NInoAttrList(base_ni)) { + err = ntfs_inode_add_attrlist(base_ni); + if (err) + return err; + } + + err = ntfs_inode_free_space(base_ni, sizeof(struct attr_record)); + if (err) { + err = -ENOSPC; + ntfs_error(sb, + "Couldn't free space in the MFT record to make attribute list non resident"); + return err; + } + err = ntfs_attrlist_update(base_ni); + if (err) + return err; + goto attr_resize_again; + } + + /* + * Move the attribute to a new mft record, creating an attribute list + * attribute or modifying it if it is already present. + */ + + /* Point search context back to attribute which we need resize. */ + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(attr_ni->type, attr_ni->name, attr_ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (err) { + ntfs_error(sb, "%s: Attribute lookup failed 2", __func__); + goto put_err_out; + } + + /* + * Check whether attribute is already single in this MFT record. + * 8 added for the attribute terminator. + */ + if (le32_to_cpu(ctx->mrec->bytes_in_use) == + le16_to_cpu(ctx->mrec->attrs_offset) + le32_to_cpu(ctx->attr->length) + 8) { + err = -ENOSPC; + ntfs_debug("MFT record is filled with one attribute\n"); + goto put_err_out; + } + + /* Add attribute list if not present. */ + if (!NInoAttrList(base_ni)) { + ntfs_attr_put_search_ctx(ctx); + err = ntfs_inode_add_attrlist(base_ni); + if (err) + return err; + goto attr_resize_again; + } + + /* Allocate new mft record. */ + err = ntfs_mft_record_alloc(base_ni->vol, 0, &ext_ni, base_ni, NULL); + if (err) { + ntfs_error(sb, "Couldn't allocate MFT record"); + goto put_err_out; + } + unmap_mft_record(ext_ni); + + /* Move attribute to it. */ + err = ntfs_attr_record_move_to(ctx, ext_ni); + if (err) { + ntfs_error(sb, "Couldn't move attribute to new MFT record"); + err = -ENOMEM; + goto put_err_out; + } + + err = ntfs_attrlist_update(base_ni); + if (err < 0) + goto put_err_out; + + ntfs_attr_put_search_ctx(ctx); + /* Try to perform resize once again. */ + goto attr_resize_again; + +resize_done: + /* + * Set the inode (and its base inode if it exists) dirty so it is + * written out later. + */ + mark_mft_record_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return 0; + +put_err_out: + ntfs_attr_put_search_ctx(ctx); + return err; +} + +int __ntfs_attr_truncate_vfs(struct ntfs_inode *ni, const s64 newsize, + const s64 i_size) +{ + int err = 0; + + if (newsize < 0 || + (ni->mft_no == FILE_MFT && ni->type == AT_DATA)) { + ntfs_debug("Invalid arguments passed.\n"); + return -EINVAL; + } + + ntfs_debug("Entering for inode 0x%llx, attr 0x%x, size %lld\n", + (unsigned long long)ni->mft_no, ni->type, newsize); + + if (NInoNonResident(ni)) { + if (newsize > i_size) { + down_write(&ni->runlist.lock); + err = ntfs_non_resident_attr_expand(ni, newsize, 0, + NVolDisableSparse(ni->vol) ? + HOLES_NO : HOLES_OK, + false); + up_write(&ni->runlist.lock); + } else + err = ntfs_non_resident_attr_shrink(ni, newsize); + } else + err = ntfs_resident_attr_resize(ni, newsize, 0, + NVolDisableSparse(ni->vol) ? + HOLES_NO : HOLES_OK); + ntfs_debug("Return status %d\n", err); + return err; +} + +int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 prealloc_size) +{ + int err = 0; + + if (newsize < 0 || + (ni->mft_no == FILE_MFT && ni->type == AT_DATA)) { + ntfs_debug("Invalid arguments passed.\n"); + return -EINVAL; + } + + ntfs_debug("Entering for inode 0x%llx, attr 0x%x, size %lld\n", + (unsigned long long)ni->mft_no, ni->type, newsize); + + if (ni->data_size == newsize) { + ntfs_debug("Size is already ok\n"); + return 0; + } + + /* + * Encrypted attributes are not supported. We return access denied, + * which is what Windows NT4 does, too. + */ + if (NInoEncrypted(ni)) { + pr_err("Failed to truncate encrypted attribute\n"); + return -EACCES; + } + + if (NInoNonResident(ni)) { + if (newsize > ni->data_size) + err = ntfs_non_resident_attr_expand(ni, newsize, prealloc_size, + NVolDisableSparse(ni->vol) ? + HOLES_NO : HOLES_OK, true); + } else + err = ntfs_resident_attr_resize(ni, newsize, prealloc_size, + NVolDisableSparse(ni->vol) ? + HOLES_NO : HOLES_OK); + if (!err) + i_size_write(VFS_I(ni), newsize); + ntfs_debug("Return status %d\n", err); + return err; +} + +/* + * ntfs_attr_truncate_i - resize an ntfs attribute + * @ni: open ntfs inode to resize + * @newsize: new size (in bytes) to which to resize the attribute + * @holes: how to create a hole if expanding + * + * Change the size of an open ntfs attribute @na to @newsize bytes. If the + * attribute is made bigger and the attribute is resident the newly + * "allocated" space is cleared and if the attribute is non-resident the + * newly allocated space is marked as not initialised and no real allocation + * on disk is performed. + */ +int ntfs_attr_truncate_i(struct ntfs_inode *ni, const s64 newsize, unsigned int holes) +{ + int err; + + if (newsize < 0 || + (ni->mft_no == FILE_MFT && ni->type == AT_DATA)) { + ntfs_debug("Invalid arguments passed.\n"); + return -EINVAL; + } + + ntfs_debug("Entering for inode 0x%llx, attr 0x%x, size %lld\n", + (unsigned long long)ni->mft_no, ni->type, newsize); + + if (ni->data_size == newsize) { + ntfs_debug("Size is already ok\n"); + return 0; + } + + /* + * Encrypted attributes are not supported. We return access denied, + * which is what Windows NT4 does, too. + */ + if (NInoEncrypted(ni)) { + pr_err("Failed to truncate encrypted attribute\n"); + return -EACCES; + } + + if (NInoCompressed(ni)) { + pr_err("Failed to truncate compressed attribute\n"); + return -EOPNOTSUPP; + } + + if (NInoNonResident(ni)) { + if (newsize > ni->data_size) + err = ntfs_non_resident_attr_expand(ni, newsize, 0, holes, true); + else + err = ntfs_non_resident_attr_shrink(ni, newsize); + } else + err = ntfs_resident_attr_resize(ni, newsize, 0, holes); + ntfs_debug("Return status %d\n", err); + return err; +} + +/* + * Resize an attribute, creating a hole if relevant + */ +int ntfs_attr_truncate(struct ntfs_inode *ni, const s64 newsize) +{ + return ntfs_attr_truncate_i(ni, newsize, + NVolDisableSparse(ni->vol) ? + HOLES_NO : HOLES_OK); +} + +int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start, + s64 *lcn_count, s64 max_clu_count, bool *balloc, bool update_mp, + bool skip_holes) +{ + struct ntfs_volume *vol = ni->vol; + struct ntfs_attr_search_ctx *ctx; + struct runlist_element *rl, *rlc; + s64 vcn = vcn_start, lcn, clu_count; + s64 lcn_seek_from = -1; + int err = 0; + size_t new_rl_count; + + err = ntfs_attr_map_whole_runlist(ni); + if (err) + return err; + + if (NInoAttr(ni)) + ctx = ntfs_attr_get_search_ctx(ni->ext.base_ntfs_ino, NULL); + else + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_error(vol->sb, "%s: Failed to get search context", __func__); + return -ENOMEM; + } + + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, vcn, NULL, 0, ctx); + if (err) { + ntfs_error(vol->sb, + "ntfs_attr_lookup failed, ntfs inode(mft_no : %llu) type : 0x%x, err : %d", + ni->mft_no, ni->type, err); + goto out; + } + + rl = ntfs_attr_find_vcn_nolock(ni, vcn, ctx); + if (IS_ERR(rl)) { + ntfs_error(vol->sb, "Failed to find run after mapping runlist."); + err = PTR_ERR(rl); + goto out; + } + + lcn = ntfs_rl_vcn_to_lcn(rl, vcn); + clu_count = min(max_clu_count, rl->length - (vcn - rl->vcn)); + if (lcn >= LCN_HOLE) { + if (lcn > LCN_DELALLOC || + (lcn == LCN_HOLE && skip_holes)) { + *lcn_start = lcn; + *lcn_count = clu_count; + *balloc = false; + goto out; + } + } else { + WARN_ON(lcn == LCN_RL_NOT_MAPPED); + if (lcn == LCN_ENOENT) + err = -ENOENT; + else + err = -EIO; + goto out; + } + + /* Search backwards to find the best lcn to start seek from. */ + rlc = rl; + while (rlc->vcn) { + rlc--; + if (rlc->lcn >= 0) { + /* + * avoid fragmenting a compressed file + * Windows does not do that, and that may + * not be desirable for files which can + * be updated + */ + if (NInoCompressed(ni)) + lcn_seek_from = rlc->lcn + rlc->length; + else + lcn_seek_from = rlc->lcn + (vcn - rlc->vcn); + break; + } + } + + if (lcn_seek_from == -1) { + /* Backwards search failed, search forwards. */ + rlc = rl; + while (rlc->length) { + rlc++; + if (rlc->lcn >= 0) { + lcn_seek_from = rlc->lcn - (rlc->vcn - vcn); + if (lcn_seek_from < -1) + lcn_seek_from = -1; + break; + } + } + } + + rlc = ntfs_cluster_alloc(vol, vcn, clu_count, lcn_seek_from, DATA_ZONE, + false, true, true); + if (IS_ERR(rlc)) { + err = PTR_ERR(rlc); + goto out; + } + + WARN_ON(rlc->vcn != vcn); + lcn = rlc->lcn; + clu_count = rlc->length; + + rl = ntfs_runlists_merge(&ni->runlist, rlc, 0, &new_rl_count); + if (IS_ERR(rl)) { + ntfs_error(vol->sb, "Failed to merge runlists"); + err = PTR_ERR(rl); + if (ntfs_cluster_free_from_rl(vol, rlc)) + ntfs_error(vol->sb, "Failed to free hot clusters."); + kvfree(rlc); + goto out; + } + ni->runlist.rl = rl; + ni->runlist.count = new_rl_count; + + if (!update_mp) { + u64 free = atomic64_read(&vol->free_clusters) * 100; + + do_div(free, vol->nr_clusters); + if (free <= 5) + update_mp = true; + } + + if (update_mp) { + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_update_mapping_pairs(ni, 0); + if (err) { + int err2; + + err2 = ntfs_cluster_free(ni, vcn, clu_count, ctx); + if (err2 < 0) + ntfs_error(vol->sb, + "Failed to free cluster allocation. Leaving inconstant metadata.\n"); + goto out; + } + } else { + VFS_I(ni)->i_blocks += clu_count << (vol->cluster_size_bits - 9); + NInoSetRunlistDirty(ni); + mark_mft_record_dirty(ni); + } + + *lcn_start = lcn; + *lcn_count = clu_count; + *balloc = true; +out: + ntfs_attr_put_search_ctx(ctx); + return err; +} + +/* + * ntfs_attr_rm - remove attribute from ntfs inode + * @ni: opened ntfs attribute to delete + * + * Remove attribute and all it's extents from ntfs inode. If attribute was non + * resident also free all clusters allocated by attribute. + */ +int ntfs_attr_rm(struct ntfs_inode *ni) +{ + struct ntfs_attr_search_ctx *ctx; + int err = 0, ret = 0; + struct ntfs_inode *base_ni; + struct super_block *sb = ni->vol->sb; + + if (NInoAttr(ni)) + base_ni = ni->ext.base_ntfs_ino; + else + base_ni = ni; + + ntfs_debug("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) ni->mft_no, ni->type); + + /* Free cluster allocation. */ + if (NInoNonResident(ni)) { + struct ntfs_attr_search_ctx *ctx; + + err = ntfs_attr_map_whole_runlist(ni); + if (err) + return err; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_error(sb, "%s: Failed to get search context", __func__); + return -ENOMEM; + } + + ret = ntfs_cluster_free(ni, 0, -1, ctx); + if (ret < 0) + ntfs_error(sb, + "Failed to free cluster allocation. Leaving inconstant metadata.\n"); + ntfs_attr_put_search_ctx(ctx); + } + + /* Search for attribute extents and remove them all. */ + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); + if (!ctx) { + ntfs_error(sb, "%s: Failed to get search context", __func__); + return -ENOMEM; + } + while (!(err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx))) { + err = ntfs_attr_record_rm(ctx); + if (err) { + ntfs_error(sb, + "Failed to remove attribute extent. Leaving inconstant metadata.\n"); + ret = err; + } + ntfs_attr_reinit_search_ctx(ctx); + } + ntfs_attr_put_search_ctx(ctx); + if (err != -ENOENT) { + ntfs_error(sb, "Attribute lookup failed. Probably leaving inconstant metadata.\n"); + ret = err; + } + + return ret; +} + +int ntfs_attr_exist(struct ntfs_inode *ni, const __le32 type, __le16 *name, + u32 name_len) +{ + struct ntfs_attr_search_ctx *ctx; + int ret; + + ntfs_debug("Entering\n"); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_error(ni->vol->sb, "%s: Failed to get search context", + __func__); + return 0; + } + + ret = ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx); + ntfs_attr_put_search_ctx(ctx); + + return !ret; +} + +int ntfs_attr_remove(struct ntfs_inode *ni, const __le32 type, __le16 *name, + u32 name_len) +{ + int err; + struct inode *attr_vi; + struct ntfs_inode *attr_ni; + + ntfs_debug("Entering\n"); + + if (!ni) + return -EINVAL; + + attr_vi = ntfs_attr_iget(VFS_I(ni), type, name, name_len); + if (IS_ERR(attr_vi)) { + err = PTR_ERR(attr_vi); + ntfs_error(ni->vol->sb, "Failed to open attribute 0x%02x of inode 0x%llx", + type, (unsigned long long)ni->mft_no); + return err; + } + attr_ni = NTFS_I(attr_vi); + + err = ntfs_attr_rm(attr_ni); + if (err) + ntfs_error(ni->vol->sb, "Failed to remove attribute 0x%02x of inode 0x%llx", + type, (unsigned long long)ni->mft_no); + iput(attr_vi); + return err; +} + +/* + * ntfs_attr_readall - read the entire data from an ntfs attribute + * @ni: open ntfs inode in which the ntfs attribute resides + * @type: attribute type + * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL + * @name_len: length of attribute @name in Unicode characters (if @name given) + * @data_size: if non-NULL then store here the data size + * + * This function will read the entire content of an ntfs attribute. + * If @name is AT_UNNAMED then look specifically for an unnamed attribute. + * If @name is NULL then the attribute could be either named or not. + * In both those cases @name_len is not used at all. + * + * On success a buffer is allocated with the content of the attribute + * and which needs to be freed when it's not needed anymore. If the + * @data_size parameter is non-NULL then the data size is set there. + */ +void *ntfs_attr_readall(struct ntfs_inode *ni, const __le32 type, + __le16 *name, u32 name_len, s64 *data_size) +{ + struct ntfs_inode *bmp_ni; + struct inode *bmp_vi; + void *data, *ret = NULL; + s64 size; + struct super_block *sb = ni->vol->sb; + + ntfs_debug("Entering\n"); + + bmp_vi = ntfs_attr_iget(VFS_I(ni), type, name, name_len); + if (IS_ERR(bmp_vi)) { + ntfs_debug("ntfs_attr_iget failed"); + goto err_exit; + } + bmp_ni = NTFS_I(bmp_vi); + + if (bmp_ni->data_size > NTFS_ATTR_READALL_MAX_SIZE && + (bmp_ni->type != AT_BITMAP || + bmp_ni->data_size > ((ni->vol->nr_clusters + 7) >> 3))) { + ntfs_error(sb, "Invalid attribute data size"); + goto out; + } + + data = kvmalloc(bmp_ni->data_size, GFP_NOFS); + if (!data) + goto out; + + size = ntfs_inode_attr_pread(VFS_I(bmp_ni), 0, bmp_ni->data_size, + (u8 *)data); + if (size != bmp_ni->data_size) { + ntfs_error(sb, "ntfs_attr_pread failed"); + kvfree(data); + goto out; + } + ret = data; + if (data_size) + *data_size = size; +out: + iput(bmp_vi); +err_exit: + ntfs_debug("\n"); + return ret; +} + +int ntfs_non_resident_attr_insert_range(struct ntfs_inode *ni, s64 start_vcn, s64 len) +{ + struct ntfs_volume *vol = ni->vol; + struct runlist_element *hole_rl, *rl; + struct ntfs_attr_search_ctx *ctx; + int ret; + size_t new_rl_count; + + if (NInoAttr(ni) || ni->type != AT_DATA) + return -EOPNOTSUPP; + if (start_vcn > ntfs_bytes_to_cluster(vol, ni->allocated_size)) + return -EINVAL; + + hole_rl = kmalloc(sizeof(*hole_rl) * 2, GFP_NOFS); + if (!hole_rl) + return -ENOMEM; + hole_rl[0].vcn = start_vcn; + hole_rl[0].lcn = LCN_HOLE; + hole_rl[0].length = len; + hole_rl[1].vcn = start_vcn + len; + hole_rl[1].lcn = LCN_ENOENT; + hole_rl[1].length = 0; + + down_write(&ni->runlist.lock); + ret = ntfs_attr_map_whole_runlist(ni); + if (ret) { + up_write(&ni->runlist.lock); + return ret; + } + + rl = ntfs_rl_find_vcn_nolock(ni->runlist.rl, start_vcn); + if (!rl) { + up_write(&ni->runlist.lock); + kfree(hole_rl); + return -EIO; + } + + rl = ntfs_rl_insert_range(ni->runlist.rl, (int)ni->runlist.count, + hole_rl, 1, &new_rl_count); + if (IS_ERR(rl)) { + up_write(&ni->runlist.lock); + kfree(hole_rl); + return PTR_ERR(rl); + } + ni->runlist.rl = rl; + ni->runlist.count = new_rl_count; + + ni->allocated_size += ntfs_cluster_to_bytes(vol, len); + ni->data_size += ntfs_cluster_to_bytes(vol, len); + if (ntfs_cluster_to_bytes(vol, start_vcn) < ni->initialized_size) + ni->initialized_size += ntfs_cluster_to_bytes(vol, len); + ret = ntfs_attr_update_mapping_pairs(ni, 0); + up_write(&ni->runlist.lock); + if (ret) + return ret; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ret = -ENOMEM; + return ret; + } + + ret = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx); + if (ret) { + ntfs_attr_put_search_ctx(ctx); + return ret; + } + + ctx->attr->data.non_resident.data_size = cpu_to_le64(ni->data_size); + ctx->attr->data.non_resident.initialized_size = cpu_to_le64(ni->initialized_size); + if (ni->type == AT_DATA && ni->name == AT_UNNAMED) + NInoSetFileNameDirty(ni); + mark_mft_record_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return ret; +} + +int ntfs_non_resident_attr_collapse_range(struct ntfs_inode *ni, s64 start_vcn, s64 len) +{ + struct ntfs_volume *vol = ni->vol; + struct runlist_element *punch_rl, *rl; + struct ntfs_attr_search_ctx *ctx = NULL; + s64 end_vcn; + int dst_cnt; + int ret; + size_t new_rl_cnt; + + if (NInoAttr(ni) || ni->type != AT_DATA) + return -EOPNOTSUPP; + + end_vcn = ntfs_bytes_to_cluster(vol, ni->allocated_size); + if (start_vcn >= end_vcn) + return -EINVAL; + + down_write(&ni->runlist.lock); + ret = ntfs_attr_map_whole_runlist(ni); + if (ret) { + up_write(&ni->runlist.lock); + return ret; + } + + len = min(len, end_vcn - start_vcn); + for (rl = ni->runlist.rl, dst_cnt = 0; rl && rl->length; rl++) + dst_cnt++; + rl = ntfs_rl_find_vcn_nolock(ni->runlist.rl, start_vcn); + if (!rl) { + up_write(&ni->runlist.lock); + return -EIO; + } + + rl = ntfs_rl_collapse_range(ni->runlist.rl, dst_cnt + 1, + start_vcn, len, &punch_rl, &new_rl_cnt); + if (IS_ERR(rl)) { + up_write(&ni->runlist.lock); + return PTR_ERR(rl); + } + ni->runlist.rl = rl; + ni->runlist.count = new_rl_cnt; + + ni->allocated_size -= ntfs_cluster_to_bytes(vol, len); + if (ni->data_size > ntfs_cluster_to_bytes(vol, start_vcn)) { + if (ni->data_size > ntfs_cluster_to_bytes(vol, (start_vcn + len))) + ni->data_size -= ntfs_cluster_to_bytes(vol, len); + else + ni->data_size = ntfs_cluster_to_bytes(vol, start_vcn); + } + if (ni->initialized_size > ntfs_cluster_to_bytes(vol, start_vcn)) { + if (ni->initialized_size > + ntfs_cluster_to_bytes(vol, start_vcn + len)) + ni->initialized_size -= ntfs_cluster_to_bytes(vol, len); + else + ni->initialized_size = ntfs_cluster_to_bytes(vol, start_vcn); + } + + if (ni->allocated_size > 0) { + ret = ntfs_attr_update_mapping_pairs(ni, 0); + if (ret) { + up_write(&ni->runlist.lock); + goto out_rl; + } + } + up_write(&ni->runlist.lock); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ret = -ENOMEM; + goto out_rl; + } + + ret = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx); + if (ret) + goto out_ctx; + + ctx->attr->data.non_resident.data_size = cpu_to_le64(ni->data_size); + ctx->attr->data.non_resident.initialized_size = cpu_to_le64(ni->initialized_size); + if (ni->allocated_size == 0) + ntfs_attr_make_resident(ni, ctx); + mark_mft_record_dirty(ctx->ntfs_ino); + + ret = ntfs_cluster_free_from_rl(vol, punch_rl); + if (ret) + ntfs_error(vol->sb, "Freeing of clusters failed"); +out_ctx: + if (ctx) + ntfs_attr_put_search_ctx(ctx); +out_rl: + kvfree(punch_rl); + mark_mft_record_dirty(ni); + return ret; +} + +int ntfs_non_resident_attr_punch_hole(struct ntfs_inode *ni, s64 start_vcn, s64 len) +{ + struct ntfs_volume *vol = ni->vol; + struct runlist_element *punch_rl, *rl; + s64 end_vcn; + int dst_cnt; + int ret; + size_t new_rl_count; + + if (NInoAttr(ni) || ni->type != AT_DATA) + return -EOPNOTSUPP; + + end_vcn = ntfs_bytes_to_cluster(vol, ni->allocated_size); + if (start_vcn >= end_vcn) + return -EINVAL; + + down_write(&ni->runlist.lock); + ret = ntfs_attr_map_whole_runlist(ni); + if (ret) { + up_write(&ni->runlist.lock); + return ret; + } + + len = min(len, end_vcn - start_vcn + 1); + for (rl = ni->runlist.rl, dst_cnt = 0; rl && rl->length; rl++) + dst_cnt++; + rl = ntfs_rl_find_vcn_nolock(ni->runlist.rl, start_vcn); + if (!rl) { + up_write(&ni->runlist.lock); + return -EIO; + } + + rl = ntfs_rl_punch_hole(ni->runlist.rl, dst_cnt + 1, + start_vcn, len, &punch_rl, &new_rl_count); + if (IS_ERR(rl)) { + up_write(&ni->runlist.lock); + return PTR_ERR(rl); + } + ni->runlist.rl = rl; + ni->runlist.count = new_rl_count; + + ret = ntfs_attr_update_mapping_pairs(ni, 0); + up_write(&ni->runlist.lock); + if (ret) { + kvfree(punch_rl); + return ret; + } + + ret = ntfs_cluster_free_from_rl(vol, punch_rl); + if (ret) + ntfs_error(vol->sb, "Freeing of clusters failed"); + + kvfree(punch_rl); + mark_mft_record_dirty(ni); + return ret; +} + +int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bool keep_size) +{ + struct ntfs_volume *vol = ni->vol; + struct mft_record *mrec; + struct ntfs_attr_search_ctx *ctx; + s64 old_data_size; + s64 vcn_start, vcn_end, vcn_uninit, vcn, try_alloc_cnt; + s64 lcn, alloc_cnt; + int err = 0; + struct runlist_element *rl; + bool balloc; + + if (NInoAttr(ni) || ni->type != AT_DATA) + return -EINVAL; + + if (NInoNonResident(ni) && !NInoFullyMapped(ni)) { + down_write(&ni->runlist.lock); + err = ntfs_attr_map_whole_runlist(ni); + up_write(&ni->runlist.lock); + if (err) + return err; + } + + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + mrec = map_mft_record(ni); + if (IS_ERR(mrec)) { + mutex_unlock(&ni->mrec_lock); + return PTR_ERR(mrec); + } + + ctx = ntfs_attr_get_search_ctx(ni, mrec); + if (!ctx) { + err = -ENOMEM; + goto out_unmap; + } + + err = ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx); + if (err) { + err = -EIO; + goto out_unmap; + } + + old_data_size = ni->data_size; + if (start + byte_len > ni->data_size) { + err = ntfs_attr_truncate(ni, start + byte_len); + if (err) + goto out_unmap; + if (keep_size) { + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx); + if (err) { + err = -EIO; + goto out_unmap; + } + ni->data_size = old_data_size; + if (NInoNonResident(ni)) + ctx->attr->data.non_resident.data_size = + cpu_to_le64(old_data_size); + else + ctx->attr->data.resident.value_length = + cpu_to_le32((u32)old_data_size); + mark_mft_record_dirty(ni); + } + } + + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(ni); + mutex_unlock(&ni->mrec_lock); + + if (!NInoNonResident(ni)) + goto out; + + vcn_start = (s64)ntfs_bytes_to_cluster(vol, start); + vcn_end = (s64)ntfs_bytes_to_cluster(vol, + round_up(start + byte_len, vol->cluster_size)); + vcn_uninit = (s64)ntfs_bytes_to_cluster(vol, + round_up(ni->initialized_size, vol->cluster_size)); + vcn_uninit = min_t(s64, vcn_uninit, vcn_end); + + /* + * we have to allocate clusters for holes and delayed within initialized_size, + * and zero out the clusters only for the holes. + */ + vcn = vcn_start; + while (vcn < vcn_uninit) { + down_read(&ni->runlist.lock); + rl = ntfs_attr_find_vcn_nolock(ni, vcn, NULL); + up_read(&ni->runlist.lock); + if (IS_ERR(rl)) { + err = PTR_ERR(rl); + goto out; + } + + if (rl->lcn > 0) { + vcn += rl->length - (vcn - rl->vcn); + } else if (rl->lcn == LCN_DELALLOC || rl->lcn == LCN_HOLE) { + try_alloc_cnt = min(rl->length - (vcn - rl->vcn), + vcn_uninit - vcn); + + if (rl->lcn == LCN_DELALLOC) { + vcn += try_alloc_cnt; + continue; + } + + while (try_alloc_cnt > 0) { + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + down_write(&ni->runlist.lock); + err = ntfs_attr_map_cluster(ni, vcn, &lcn, &alloc_cnt, + try_alloc_cnt, &balloc, false, false); + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + if (err) + goto out; + + err = ntfs_dio_zero_range(VFS_I(ni), + lcn << vol->cluster_size_bits, + alloc_cnt << vol->cluster_size_bits); + if (err > 0) + goto out; + + if (signal_pending(current)) + goto out; + + vcn += alloc_cnt; + try_alloc_cnt -= alloc_cnt; + } + } else { + err = -EIO; + goto out; + } + } + + /* allocate clusters outside of initialized_size */ + try_alloc_cnt = vcn_end - vcn; + while (try_alloc_cnt > 0) { + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + down_write(&ni->runlist.lock); + err = ntfs_attr_map_cluster(ni, vcn, &lcn, &alloc_cnt, + try_alloc_cnt, &balloc, false, false); + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + if (err || signal_pending(current)) + goto out; + + vcn += alloc_cnt; + try_alloc_cnt -= alloc_cnt; + cond_resched(); + } + + if (NInoRunlistDirty(ni)) { + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + down_write(&ni->runlist.lock); + err = ntfs_attr_update_mapping_pairs(ni, 0); + if (err) + ntfs_error(ni->vol->sb, "Updating mapping pairs failed"); + else + NInoClearRunlistDirty(ni); + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + } + return err; +out_unmap: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(ni); + mutex_unlock(&ni->mrec_lock); +out: + return err >= 0 ? 0 : err; +} diff --git a/fs/ntfs/attrib.h b/fs/ntfs/attrib.h new file mode 100644 index 000000000000..f7acc7986b09 --- /dev/null +++ b/fs/ntfs/attrib.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for attribute handling in NTFS Linux kernel driver. + * + * Copyright (c) 2001-2005 Anton Altaparmakov + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#ifndef _LINUX_NTFS_ATTRIB_H +#define _LINUX_NTFS_ATTRIB_H + +#include "ntfs.h" +#include "dir.h" + +extern __le16 AT_UNNAMED[]; + +/* + * ntfs_attr_search_ctx - used in attribute search functions + * @mrec: buffer containing mft record to search + * @mapped_mrec: true if @mrec was mapped by the search functions + * @attr: attribute record in @mrec where to begin/continue search + * @is_first: if true ntfs_attr_lookup() begins search with @attr, else after + * @ntfs_ino: Inode owning this attribute search + * @al_entry: Current attribute list entry + * @base_ntfs_ino: Base inode + * @mapped_base_mrec: true if @base_mrec was mapped by the search + * @base_attr: Base attribute record pointer + * + * Structure must be initialized to zero before the first call to one of the + * attribute search functions. Initialize @mrec to point to the mft record to + * search, and @attr to point to the first attribute within @mrec (not necessary + * if calling the _first() functions), and set @is_first to 'true' (not necessary + * if calling the _first() functions). + * + * If @is_first is 'true', the search begins with @attr. If @is_first is 'false', + * the search begins after @attr. This is so that, after the first call to one + * of the search attribute functions, we can call the function again, without + * any modification of the search context, to automagically get the next + * matching attribute. + */ +struct ntfs_attr_search_ctx { + struct mft_record *mrec; + bool mapped_mrec; + struct attr_record *attr; + bool is_first; + struct ntfs_inode *ntfs_ino; + struct attr_list_entry *al_entry; + struct ntfs_inode *base_ntfs_ino; + struct mft_record *base_mrec; + bool mapped_base_mrec; + struct attr_record *base_attr; +}; + +enum { /* ways of processing holes when expanding */ + HOLES_NO, + HOLES_OK, +}; + +int ntfs_map_runlist_nolock(struct ntfs_inode *ni, s64 vcn, + struct ntfs_attr_search_ctx *ctx); +int ntfs_map_runlist(struct ntfs_inode *ni, s64 vcn); +s64 ntfs_attr_vcn_to_lcn_nolock(struct ntfs_inode *ni, const s64 vcn, + const bool write_locked); +struct runlist_element *ntfs_attr_find_vcn_nolock(struct ntfs_inode *ni, + const s64 vcn, struct ntfs_attr_search_ctx *ctx); +struct runlist_element *__ntfs_attr_find_vcn_nolock(struct runlist *runlist, + const s64 vcn); +int ntfs_attr_map_whole_runlist(struct ntfs_inode *ni); +int ntfs_attr_lookup(const __le32 type, const __le16 *name, + const u32 name_len, const u32 ic, + const s64 lowest_vcn, const u8 *val, const u32 val_len, + struct ntfs_attr_search_ctx *ctx); +int load_attribute_list(struct ntfs_inode *base_ni, + u8 *al_start, const s64 size); + +static inline s64 ntfs_attr_size(const struct attr_record *a) +{ + if (!a->non_resident) + return (s64)le32_to_cpu(a->data.resident.value_length); + return le64_to_cpu(a->data.non_resident.data_size); +} + +void ntfs_attr_reinit_search_ctx(struct ntfs_attr_search_ctx *ctx); +struct ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(struct ntfs_inode *ni, + struct mft_record *mrec); +void ntfs_attr_put_search_ctx(struct ntfs_attr_search_ctx *ctx); +int ntfs_attr_size_bounds_check(const struct ntfs_volume *vol, + const __le32 type, const s64 size); +int ntfs_attr_can_be_resident(const struct ntfs_volume *vol, + const __le32 type); +int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start, + s64 *lcn_count, s64 max_clu_count, bool *balloc, bool update_mp, bool skip_holes); +int ntfs_attr_record_resize(struct mft_record *m, struct attr_record *a, u32 new_size); +int ntfs_resident_attr_value_resize(struct mft_record *m, struct attr_record *a, + const u32 new_size); +int ntfs_attr_make_non_resident(struct ntfs_inode *ni, const u32 data_size); +int ntfs_attr_set(struct ntfs_inode *ni, const s64 ofs, const s64 cnt, + const u8 val); +int ntfs_attr_set_initialized_size(struct ntfs_inode *ni, loff_t new_size); +int ntfs_attr_open(struct ntfs_inode *ni, const __le32 type, + __le16 *name, u32 name_len); +void ntfs_attr_close(struct ntfs_inode *n); +int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bool keep_size); +int ntfs_non_resident_attr_insert_range(struct ntfs_inode *ni, s64 start_vcn, s64 len); +int ntfs_non_resident_attr_collapse_range(struct ntfs_inode *ni, s64 start_vcn, s64 len); +int ntfs_non_resident_attr_punch_hole(struct ntfs_inode *ni, s64 start_vcn, s64 len); +int __ntfs_attr_truncate_vfs(struct ntfs_inode *ni, const s64 newsize, + const s64 i_size); +int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 prealloc_size); +int ntfs_attr_truncate_i(struct ntfs_inode *ni, const s64 newsize, unsigned int holes); +int ntfs_attr_truncate(struct ntfs_inode *ni, const s64 newsize); +int ntfs_attr_rm(struct ntfs_inode *ni); +int ntfs_attr_exist(struct ntfs_inode *ni, const __le32 type, __le16 *name, + u32 name_len); +int ntfs_attr_remove(struct ntfs_inode *ni, const __le32 type, __le16 *name, + u32 name_len); +int ntfs_attr_record_rm(struct ntfs_attr_search_ctx *ctx); +int ntfs_attr_record_move_to(struct ntfs_attr_search_ctx *ctx, struct ntfs_inode *ni); +int ntfs_attr_add(struct ntfs_inode *ni, __le32 type, + __le16 *name, u8 name_len, u8 *val, s64 size); +int ntfs_attr_record_move_away(struct ntfs_attr_search_ctx *ctx, int extra); +char *ntfs_attr_name_get(const struct ntfs_volume *vol, const __le16 *uname, + const int uname_len); +void ntfs_attr_name_free(unsigned char **name); +void *ntfs_attr_readall(struct ntfs_inode *ni, const __le32 type, + __le16 *name, u32 name_len, s64 *data_size); +int ntfs_resident_attr_record_add(struct ntfs_inode *ni, __le32 type, + __le16 *name, u8 name_len, u8 *val, u32 size, + __le16 flags); +int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn); +struct runlist_element *ntfs_attr_vcn_to_rl(struct ntfs_inode *ni, s64 vcn, s64 *lcn); + +/* + * ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode + * @ctx: initialised attribute search context + * + * Syntactic sugar for walking attributes in an inode. + * + * Return 0 on success and -1 on error with errno set to the error code from + * ntfs_attr_lookup(). + * + * Example: When you want to enumerate all attributes in an open ntfs inode + * @ni, you can simply do: + * + * int err; + * struct ntfs_attr_search_ctx *ctx = ntfs_attr_get_search_ctx(ni, NULL); + * if (!ctx) + * // Error code is in errno. Handle this case. + * while (!(err = ntfs_attrs_walk(ctx))) { + * struct attr_record *attr = ctx->attr; + * // attr now contains the next attribute. Do whatever you want + * // with it and then just continue with the while loop. + * } + * if (err && errno != ENOENT) + * // Ooops. An error occurred! You should handle this case. + * // Now finished with all attributes in the inode. + */ +static inline int ntfs_attrs_walk(struct ntfs_attr_search_ctx *ctx) +{ + return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, + NULL, 0, ctx); +} +#endif /* _LINUX_NTFS_ATTRIB_H */ diff --git a/fs/ntfs/attrlist.c b/fs/ntfs/attrlist.c new file mode 100644 index 000000000000..bd501e8a628c --- /dev/null +++ b/fs/ntfs/attrlist.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Attribute list attribute handling code. + * Part of this file is based on code from the NTFS-3G. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2006 Szabolcs Szakacsits + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#include "mft.h" +#include "attrib.h" +#include "attrlist.h" + +/* + * ntfs_attrlist_need - check whether inode need attribute list + * @ni: opened ntfs inode for which perform check + * + * Check whether all are attributes belong to one MFT record, in that case + * attribute list is not needed. + * + * Return 1 if inode need attribute list, 0 if not, or -errno on error. + */ +int ntfs_attrlist_need(struct ntfs_inode *ni) +{ + struct attr_list_entry *ale; + + if (!ni) { + ntfs_debug("Invalid arguments.\n"); + return -EINVAL; + } + ntfs_debug("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + + if (!NInoAttrList(ni)) { + ntfs_debug("Inode haven't got attribute list.\n"); + return -EINVAL; + } + + if (!ni->attr_list) { + ntfs_debug("Corrupt in-memory struct.\n"); + return -EINVAL; + } + + ale = (struct attr_list_entry *)ni->attr_list; + while ((u8 *)ale < ni->attr_list + ni->attr_list_size) { + if (MREF_LE(ale->mft_reference) != ni->mft_no) + return 1; + ale = (struct attr_list_entry *)((u8 *)ale + le16_to_cpu(ale->length)); + } + return 0; +} + +int ntfs_attrlist_update(struct ntfs_inode *base_ni) +{ + struct inode *attr_vi; + struct ntfs_inode *attr_ni; + int err; + + attr_vi = ntfs_attr_iget(VFS_I(base_ni), AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (IS_ERR(attr_vi)) { + err = PTR_ERR(attr_vi); + return err; + } + attr_ni = NTFS_I(attr_vi); + + err = ntfs_attr_truncate_i(attr_ni, base_ni->attr_list_size, HOLES_NO); + if (err == -ENOSPC && attr_ni->mft_no == FILE_MFT) { + err = ntfs_attr_truncate(attr_ni, 0); + if (err || ntfs_attr_truncate_i(attr_ni, base_ni->attr_list_size, HOLES_NO) != 0) { + iput(attr_vi); + ntfs_error(base_ni->vol->sb, + "Failed to truncate attribute list of inode %#llx", + (long long)base_ni->mft_no); + return -EIO; + } + } else if (err) { + iput(attr_vi); + ntfs_error(base_ni->vol->sb, + "Failed to truncate attribute list of inode %#llx", + (long long)base_ni->mft_no); + return -EIO; + } + + i_size_write(attr_vi, base_ni->attr_list_size); + + if (NInoNonResident(attr_ni) && !NInoAttrListNonResident(base_ni)) + NInoSetAttrListNonResident(base_ni); + + if (ntfs_inode_attr_pwrite(attr_vi, 0, base_ni->attr_list_size, + base_ni->attr_list, false) != + base_ni->attr_list_size) { + iput(attr_vi); + ntfs_error(base_ni->vol->sb, + "Failed to write attribute list of inode %#llx", + (long long)base_ni->mft_no); + return -EIO; + } + + NInoSetAttrListDirty(base_ni); + iput(attr_vi); + return 0; +} + +/* + * ntfs_attrlist_entry_add - add an attribute list attribute entry + * @ni: opened ntfs inode, which contains that attribute + * @attr: attribute record to add to attribute list + * + * Return 0 on success and -errno on error. + */ +int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr) +{ + struct attr_list_entry *ale; + __le64 mref; + struct ntfs_attr_search_ctx *ctx; + u8 *new_al; + int entry_len, entry_offset, err; + struct mft_record *ni_mrec; + u8 *old_al; + + ntfs_debug("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) ni->mft_no, + (unsigned int) le32_to_cpu(attr->type)); + + if (!ni || !attr) { + ntfs_debug("Invalid arguments.\n"); + return -EINVAL; + } + + ni_mrec = map_mft_record(ni); + if (IS_ERR(ni_mrec)) { + ntfs_debug("Invalid arguments.\n"); + return -EIO; + } + + mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni_mrec->sequence_number)); + unmap_mft_record(ni); + + if (ni->nr_extents == -1) + ni = ni->ext.base_ntfs_ino; + + if (!NInoAttrList(ni)) { + ntfs_debug("Attribute list isn't present.\n"); + return -ENOENT; + } + + /* Determine size and allocate memory for new attribute list. */ + entry_len = (sizeof(struct attr_list_entry) + sizeof(__le16) * + attr->name_length + 7) & ~7; + new_al = kvzalloc(ni->attr_list_size + entry_len, GFP_NOFS); + if (!new_al) + return -ENOMEM; + + /* Find place for the new entry. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = -ENOMEM; + ntfs_error(ni->vol->sb, "Failed to get search context"); + goto err_out; + } + + err = ntfs_attr_lookup(attr->type, (attr->name_length) ? (__le16 *) + ((u8 *)attr + le16_to_cpu(attr->name_offset)) : + AT_UNNAMED, attr->name_length, CASE_SENSITIVE, + (attr->non_resident) ? le64_to_cpu(attr->data.non_resident.lowest_vcn) : + 0, (attr->non_resident) ? NULL : ((u8 *)attr + + le16_to_cpu(attr->data.resident.value_offset)), (attr->non_resident) ? + 0 : le32_to_cpu(attr->data.resident.value_length), ctx); + if (!err) { + /* Found some extent, check it to be before new extent. */ + if (ctx->al_entry->lowest_vcn == attr->data.non_resident.lowest_vcn) { + err = -EEXIST; + ntfs_debug("Such attribute already present in the attribute list.\n"); + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + /* Add new entry after this extent. */ + ale = (struct attr_list_entry *)((u8 *)ctx->al_entry + + le16_to_cpu(ctx->al_entry->length)); + } else { + /* Check for real errors. */ + if (err != -ENOENT) { + ntfs_debug("Attribute lookup failed.\n"); + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + /* No previous extents found. */ + ale = ctx->al_entry; + } + /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ + ntfs_attr_put_search_ctx(ctx); + + /* Determine new entry offset. */ + entry_offset = ((u8 *)ale - ni->attr_list); + /* Set pointer to new entry. */ + ale = (struct attr_list_entry *)(new_al + entry_offset); + memset(ale, 0, entry_len); + /* Form new entry. */ + ale->type = attr->type; + ale->length = cpu_to_le16(entry_len); + ale->name_length = attr->name_length; + ale->name_offset = offsetof(struct attr_list_entry, name); + if (attr->non_resident) + ale->lowest_vcn = attr->data.non_resident.lowest_vcn; + else + ale->lowest_vcn = 0; + ale->mft_reference = mref; + ale->instance = attr->instance; + memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), + attr->name_length * sizeof(__le16)); + + /* Copy entries from old attribute list to new. */ + memcpy(new_al, ni->attr_list, entry_offset); + memcpy(new_al + entry_offset + entry_len, ni->attr_list + + entry_offset, ni->attr_list_size - entry_offset); + + /* Set new runlist. */ + old_al = ni->attr_list; + ni->attr_list = new_al; + ni->attr_list_size = ni->attr_list_size + entry_len; + + err = ntfs_attrlist_update(ni); + if (err) { + ni->attr_list = old_al; + ni->attr_list_size -= entry_len; + goto err_out; + } + kvfree(old_al); + return 0; +err_out: + kvfree(new_al); + return err; +} + +/* + * ntfs_attrlist_entry_rm - remove an attribute list attribute entry + * @ctx: attribute search context describing the attribute list entry + * + * Remove the attribute list entry @ctx->al_entry from the attribute list. + * + * Return 0 on success and -errno on error. + */ +int ntfs_attrlist_entry_rm(struct ntfs_attr_search_ctx *ctx) +{ + u8 *new_al; + int new_al_len; + struct ntfs_inode *base_ni; + struct attr_list_entry *ale; + + if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { + ntfs_debug("Invalid arguments.\n"); + return -EINVAL; + } + + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + ale = ctx->al_entry; + + ntfs_debug("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n", + (long long)ctx->ntfs_ino->mft_no, + (unsigned int)le32_to_cpu(ctx->al_entry->type), + (long long)le64_to_cpu(ctx->al_entry->lowest_vcn)); + + if (!NInoAttrList(base_ni)) { + ntfs_debug("Attribute list isn't present.\n"); + return -ENOENT; + } + + /* Allocate memory for new attribute list. */ + new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length); + new_al = kvzalloc(new_al_len, GFP_NOFS); + if (!new_al) + return -ENOMEM; + + /* Copy entries from old attribute list to new. */ + memcpy(new_al, base_ni->attr_list, (u8 *)ale - base_ni->attr_list); + memcpy(new_al + ((u8 *)ale - base_ni->attr_list), (u8 *)ale + le16_to_cpu( + ale->length), new_al_len - ((u8 *)ale - base_ni->attr_list)); + + /* Set new runlist. */ + kvfree(base_ni->attr_list); + base_ni->attr_list = new_al; + base_ni->attr_list_size = new_al_len; + + return ntfs_attrlist_update(base_ni); +} diff --git a/fs/ntfs/attrlist.h b/fs/ntfs/attrlist.h new file mode 100644 index 000000000000..1892a3934d3a --- /dev/null +++ b/fs/ntfs/attrlist.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Exports for attribute list attribute handling. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2004 Yura Pakhuchiy + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#ifndef _NTFS_ATTRLIST_H +#define _NTFS_ATTRLIST_H + +#include "attrib.h" + +int ntfs_attrlist_need(struct ntfs_inode *ni); +int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr); +int ntfs_attrlist_entry_rm(struct ntfs_attr_search_ctx *ctx); +int ntfs_attrlist_update(struct ntfs_inode *base_ni); + +#endif /* defined _NTFS_ATTRLIST_H */ diff --git a/fs/ntfs/bdev-io.c b/fs/ntfs/bdev-io.c new file mode 100644 index 000000000000..67e65c88d681 --- /dev/null +++ b/fs/ntfs/bdev-io.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS block device I/O. + * + * Copyright (c) 2026 LG Electronics Co., Ltd. + */ + +#include + +#include "ntfs.h" + +/* + * ntfs_bdev_read - Read data directly from block device using bio + * @bdev: block device to read from + * @data: destination buffer + * @start: starting byte offset on the block device + * @size: number of bytes to read + * + * Reads @size bytes starting from byte offset @start directly from the block + * device using one or more BIOs. This function bypasses the page cache + * completely and performs synchronous I/O with REQ_META | REQ_SYNC flags set. + * + * The @start offset must be sector-aligned (512 bytes). If it is not aligned, + * the function will return -EINVAL. + * + * If the destination buffer @data is not a vmalloc address, it falls back + * to the more efficient bdev_rw_virt() helper. + * + * Return: 0 on success, negative error code on failure. + */ +int ntfs_bdev_read(struct block_device *bdev, char *data, loff_t start, size_t size) +{ + unsigned int done = 0, added; + int error; + struct bio *bio; + enum req_op op; + sector_t sector = start >> SECTOR_SHIFT; + + if (start & (SECTOR_SIZE - 1)) + return -EINVAL; + + op = REQ_OP_READ | REQ_META | REQ_SYNC; + if (!is_vmalloc_addr(data)) + return bdev_rw_virt(bdev, sector, data, size, op); + + bio = bio_alloc(bdev, + bio_max_segs(DIV_ROUND_UP(size, PAGE_SIZE)), + op, GFP_KERNEL); + bio->bi_iter.bi_sector = sector; + + do { + added = bio_add_vmalloc_chunk(bio, data + done, size - done); + if (!added) { + struct bio *prev = bio; + + bio = bio_alloc(prev->bi_bdev, + bio_max_segs(DIV_ROUND_UP(size - done, PAGE_SIZE)), + prev->bi_opf, GFP_KERNEL); + bio->bi_iter.bi_sector = bio_end_sector(prev); + bio_chain(prev, bio); + submit_bio(prev); + } + done += added; + } while (done < size); + + error = submit_bio_wait(bio); + bio_put(bio); + + if (op == REQ_OP_READ) + invalidate_kernel_vmap_range(data, size); + return error; +} + +/* + * ntfs_bdev_write - Update block device contents via page cache + * @sb: super block of the mounted NTFS filesystem + * @buf: source buffer containing data to write + * @start: starting byte offset on the block device + * @size: number of bytes to write + * + * Writes @size bytes from @buf to the block device (sb->s_bdev) starting + * at byte offset @start. The write is performed entirely through the page + * cache of the block device's address space. + */ +int ntfs_bdev_write(struct super_block *sb, void *buf, loff_t start, size_t size) +{ + pgoff_t idx, idx_end; + loff_t offset, end = start + size; + u32 from, to, buf_off = 0; + struct folio *folio; + + idx = start >> PAGE_SHIFT; + idx_end = end >> PAGE_SHIFT; + from = start & ~PAGE_MASK; + + if (idx == idx_end) + idx_end++; + + for (; idx < idx_end; idx++, from = 0) { + folio = read_mapping_folio(sb->s_bdev->bd_mapping, idx, NULL); + if (IS_ERR(folio)) { + ntfs_error(sb, "Unable to read %ld page", idx); + return PTR_ERR(folio); + } + + offset = (loff_t)idx << PAGE_SHIFT; + to = min_t(u32, end - offset, PAGE_SIZE); + + memcpy_to_folio(folio, from, buf + buf_off, to); + buf_off += to; + folio_mark_uptodate(folio); + folio_mark_dirty(folio); + folio_put(folio); + } + + return 0; +} diff --git a/fs/ntfs/bitmap.c b/fs/ntfs/bitmap.c new file mode 100644 index 000000000000..b1436b3151b9 --- /dev/null +++ b/fs/ntfs/bitmap.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel bitmap handling. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#include +#include + +#include "bitmap.h" +#include "ntfs.h" + +int ntfs_trim_fs(struct ntfs_volume *vol, struct fstrim_range *range) +{ + size_t buf_clusters; + pgoff_t index, start_index, end_index; + struct file_ra_state *ra; + struct folio *folio; + unsigned long *bitmap; + char *kaddr; + u64 end, trimmed = 0, start_buf, end_buf, end_cluster; + u64 start_cluster = ntfs_bytes_to_cluster(vol, range->start); + u32 dq = bdev_discard_granularity(vol->sb->s_bdev); + int ret = 0; + + if (!dq) + dq = vol->cluster_size; + + if (start_cluster >= vol->nr_clusters) + return -EINVAL; + + if (range->len == (u64)-1) + end_cluster = vol->nr_clusters; + else { + end_cluster = ntfs_bytes_to_cluster(vol, + (range->start + range->len + vol->cluster_size - 1)); + if (end_cluster > vol->nr_clusters) + end_cluster = vol->nr_clusters; + } + + ra = kzalloc(sizeof(*ra), GFP_NOFS); + if (!ra) + return -ENOMEM; + + buf_clusters = PAGE_SIZE * 8; + start_index = start_cluster >> 15; + end_index = (end_cluster + buf_clusters - 1) >> 15; + + for (index = start_index; index < end_index; index++) { + folio = ntfs_get_locked_folio(vol->lcnbmp_ino->i_mapping, + index, end_index, ra); + if (IS_ERR(folio)) { + ret = PTR_ERR(folio); + goto out_free; + } + + kaddr = kmap_local_folio(folio, 0); + bitmap = (unsigned long *)kaddr; + + start_buf = max_t(u64, index * buf_clusters, start_cluster); + end_buf = min_t(u64, (index + 1) * buf_clusters, end_cluster); + + end = start_buf; + while (end < end_buf) { + u64 aligned_start, aligned_count; + u64 start = find_next_zero_bit(bitmap, end_buf - start_buf, + end - start_buf) + start_buf; + if (start >= end_buf) + break; + + end = find_next_bit(bitmap, end_buf - start_buf, + start - start_buf) + start_buf; + + aligned_start = ALIGN(ntfs_cluster_to_bytes(vol, start), dq); + aligned_count = + ALIGN_DOWN(ntfs_cluster_to_bytes(vol, end - start), dq); + if (aligned_count >= range->minlen) { + ret = blkdev_issue_discard(vol->sb->s_bdev, aligned_start >> 9, + aligned_count >> 9, GFP_NOFS); + if (ret) + goto out_unmap; + trimmed += aligned_count; + } + } + +out_unmap: + kunmap_local(kaddr); + folio_unlock(folio); + folio_put(folio); + + if (ret) + goto out_free; + } + + range->len = trimmed; + +out_free: + kfree(ra); + return ret; +} + +/* + * __ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value + * @vi: vfs inode describing the bitmap + * @start_bit: first bit to set + * @count: number of bits to set + * @value: value to set the bits to (i.e. 0 or 1) + * @is_rollback: if 'true' this is a rollback operation + * + * Set @count bits starting at bit @start_bit in the bitmap described by the + * vfs inode @vi to @value, where @value is either 0 or 1. + * + * @is_rollback should always be 'false', it is for internal use to rollback + * errors. You probably want to use ntfs_bitmap_set_bits_in_run() instead. + * + * Return 0 on success and -errno on error. + */ +int __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit, + const s64 count, const u8 value, const bool is_rollback) +{ + s64 cnt = count; + pgoff_t index, end_index; + struct address_space *mapping; + struct folio *folio; + u8 *kaddr; + int pos, len, err; + u8 bit; + struct ntfs_inode *ni = NTFS_I(vi); + struct ntfs_volume *vol = ni->vol; + + ntfs_debug("Entering for i_ino 0x%llx, start_bit 0x%llx, count 0x%llx, value %u.%s", + ni->mft_no, (unsigned long long)start_bit, + (unsigned long long)cnt, (unsigned int)value, + is_rollback ? " (rollback)" : ""); + + if (start_bit < 0 || cnt < 0 || value > 1) + return -EINVAL; + + /* + * Calculate the indices for the pages containing the first and last + * bits, i.e. @start_bit and @start_bit + @cnt - 1, respectively. + */ + index = start_bit >> (3 + PAGE_SHIFT); + end_index = (start_bit + cnt - 1) >> (3 + PAGE_SHIFT); + + /* Get the page containing the first bit (@start_bit). */ + mapping = vi->i_mapping; + folio = read_mapping_folio(mapping, index, NULL); + if (IS_ERR(folio)) { + if (!is_rollback) + ntfs_error(vi->i_sb, + "Failed to map first page (error %li), aborting.", + PTR_ERR(folio)); + return PTR_ERR(folio); + } + + folio_lock(folio); + kaddr = kmap_local_folio(folio, 0); + + /* Set @pos to the position of the byte containing @start_bit. */ + pos = (start_bit >> 3) & ~PAGE_MASK; + + /* Calculate the position of @start_bit in the first byte. */ + bit = start_bit & 7; + + /* If the first byte is partial, modify the appropriate bits in it. */ + if (bit) { + u8 *byte = kaddr + pos; + + if (ni->mft_no == FILE_Bitmap) + ntfs_set_lcn_empty_bits(vol, index, value, min_t(s64, 8 - bit, cnt)); + while ((bit & 7) && cnt) { + cnt--; + if (value) + *byte |= 1 << bit++; + else + *byte &= ~(1 << bit++); + } + /* If we are done, unmap the page and return success. */ + if (!cnt) + goto done; + + /* Update @pos to the new position. */ + pos++; + } + /* + * Depending on @value, modify all remaining whole bytes in the page up + * to @cnt. + */ + len = min_t(s64, cnt >> 3, PAGE_SIZE - pos); + memset(kaddr + pos, value ? 0xff : 0, len); + cnt -= len << 3; + if (ni->mft_no == FILE_Bitmap) + ntfs_set_lcn_empty_bits(vol, index, value, len << 3); + + /* Update @len to point to the first not-done byte in the page. */ + if (cnt < 8) + len += pos; + + /* If we are not in the last page, deal with all subsequent pages. */ + while (index < end_index) { + if (cnt <= 0) { + err = -EIO; + goto rollback; + } + + /* Update @index and get the next folio. */ + folio_mark_dirty(folio); + folio_unlock(folio); + kunmap_local(kaddr); + folio_put(folio); + folio = read_mapping_folio(mapping, ++index, NULL); + if (IS_ERR(folio)) { + ntfs_error(vi->i_sb, + "Failed to map subsequent page (error %li), aborting.", + PTR_ERR(folio)); + err = PTR_ERR(folio); + goto rollback; + } + + folio_lock(folio); + kaddr = kmap_local_folio(folio, 0); + /* + * Depending on @value, modify all remaining whole bytes in the + * page up to @cnt. + */ + len = min_t(s64, cnt >> 3, PAGE_SIZE); + memset(kaddr, value ? 0xff : 0, len); + cnt -= len << 3; + if (ni->mft_no == FILE_Bitmap) + ntfs_set_lcn_empty_bits(vol, index, value, len << 3); + } + /* + * The currently mapped page is the last one. If the last byte is + * partial, modify the appropriate bits in it. Note, @len is the + * position of the last byte inside the page. + */ + if (cnt) { + u8 *byte; + + WARN_ON(cnt > 7); + + bit = cnt; + byte = kaddr + len; + if (ni->mft_no == FILE_Bitmap) + ntfs_set_lcn_empty_bits(vol, index, value, bit); + while (bit--) { + if (value) + *byte |= 1 << bit; + else + *byte &= ~(1 << bit); + } + } +done: + /* We are done. Unmap the folio and return success. */ + folio_mark_dirty(folio); + folio_unlock(folio); + kunmap_local(kaddr); + folio_put(folio); + ntfs_debug("Done."); + return 0; +rollback: + /* + * Current state: + * - no pages are mapped + * - @count - @cnt is the number of bits that have been modified + */ + if (is_rollback) + return err; + if (count != cnt) + pos = __ntfs_bitmap_set_bits_in_run(vi, start_bit, count - cnt, + value ? 0 : 1, true); + else + pos = 0; + if (!pos) { + /* Rollback was successful. */ + ntfs_error(vi->i_sb, + "Failed to map subsequent page (error %i), aborting.", + err); + } else { + /* Rollback failed. */ + ntfs_error(vi->i_sb, + "Failed to map subsequent page (error %i) and rollback failed (error %i). Aborting and leaving inconsistent metadata. Unmount and run chkdsk.", + err, pos); + NVolSetErrors(NTFS_SB(vi->i_sb)); + } + return err; +} diff --git a/fs/ntfs/bitmap.h b/fs/ntfs/bitmap.h new file mode 100644 index 000000000000..31177fe61da6 --- /dev/null +++ b/fs/ntfs/bitmap.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for NTFS kernel bitmap handling. + * + * Copyright (c) 2004 Anton Altaparmakov + */ + +#ifndef _LINUX_NTFS_BITMAP_H +#define _LINUX_NTFS_BITMAP_H + +#include + +#include "volume.h" + +int ntfs_trim_fs(struct ntfs_volume *vol, struct fstrim_range *range); +int __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit, + const s64 count, const u8 value, const bool is_rollback); + +/* + * ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value + * @vi: vfs inode describing the bitmap + * @start_bit: first bit to set + * @count: number of bits to set + * @value: value to set the bits to (i.e. 0 or 1) + * + * Set @count bits starting at bit @start_bit in the bitmap described by the + * vfs inode @vi to @value, where @value is either 0 or 1. + * + * Return 0 on success and -errno on error. + */ +static inline int ntfs_bitmap_set_bits_in_run(struct inode *vi, + const s64 start_bit, const s64 count, const u8 value) +{ + return __ntfs_bitmap_set_bits_in_run(vi, start_bit, count, value, + false); +} + +/* + * ntfs_bitmap_set_run - set a run of bits in a bitmap + * @vi: vfs inode describing the bitmap + * @start_bit: first bit to set + * @count: number of bits to set + * + * Set @count bits starting at bit @start_bit in the bitmap described by the + * vfs inode @vi. + * + * Return 0 on success and -errno on error. + */ +static inline int ntfs_bitmap_set_run(struct inode *vi, const s64 start_bit, + const s64 count) +{ + return ntfs_bitmap_set_bits_in_run(vi, start_bit, count, 1); +} + +/* + * ntfs_bitmap_clear_run - clear a run of bits in a bitmap + * @vi: vfs inode describing the bitmap + * @start_bit: first bit to clear + * @count: number of bits to clear + * + * Clear @count bits starting at bit @start_bit in the bitmap described by the + * vfs inode @vi. + * + * Return 0 on success and -errno on error. + */ +static inline int ntfs_bitmap_clear_run(struct inode *vi, const s64 start_bit, + const s64 count) +{ + return ntfs_bitmap_set_bits_in_run(vi, start_bit, count, 0); +} + +/* + * ntfs_bitmap_set_bit - set a bit in a bitmap + * @vi: vfs inode describing the bitmap + * @bit: bit to set + * + * Set bit @bit in the bitmap described by the vfs inode @vi. + * + * Return 0 on success and -errno on error. + */ +static inline int ntfs_bitmap_set_bit(struct inode *vi, const s64 bit) +{ + return ntfs_bitmap_set_run(vi, bit, 1); +} + +/* + * ntfs_bitmap_clear_bit - clear a bit in a bitmap + * @vi: vfs inode describing the bitmap + * @bit: bit to clear + * + * Clear bit @bit in the bitmap described by the vfs inode @vi. + * + * Return 0 on success and -errno on error. + */ +static inline int ntfs_bitmap_clear_bit(struct inode *vi, const s64 bit) +{ + return ntfs_bitmap_clear_run(vi, bit, 1); +} + +#endif /* defined _LINUX_NTFS_BITMAP_H */ diff --git a/fs/ntfs/collate.c b/fs/ntfs/collate.c new file mode 100644 index 000000000000..77e34038902d --- /dev/null +++ b/fs/ntfs/collate.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel collation handling. + * + * Copyright (c) 2004 Anton Altaparmakov + * + * Part of this file is based on code from the NTFS-3G. + * and is copyrighted by the respective authors below: + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + */ + +#include "collate.h" +#include "debug.h" +#include "ntfs.h" + +#include + +static int ntfs_collate_binary(struct ntfs_volume *vol, + const void *data1, const u32 data1_len, + const void *data2, const u32 data2_len) +{ + int rc; + + rc = memcmp(data1, data2, min(data1_len, data2_len)); + if (!rc && (data1_len != data2_len)) { + if (data1_len < data2_len) + rc = -1; + else + rc = 1; + } + return rc; +} + +static int ntfs_collate_ntofs_ulong(struct ntfs_volume *vol, + const void *data1, const u32 data1_len, + const void *data2, const u32 data2_len) +{ + int rc; + u32 d1 = le32_to_cpup(data1), d2 = le32_to_cpup(data2); + + if (data1_len != data2_len || data1_len != 4) + return -EINVAL; + + if (d1 < d2) + rc = -1; + else { + if (d1 == d2) + rc = 0; + else + rc = 1; + } + return rc; +} + +/* + * ntfs_collate_ntofs_ulongs - Which of two le32 arrays should be listed first + * @vol: ntfs volume + * @data1: first ulong array to collate + * @data1_len: length in bytes of @data1 + * @data2: second ulong array to collate + * @data2_len: length in bytes of @data2 + * + * Returns: -1, 0 or 1 depending of how the arrays compare + */ +static int ntfs_collate_ntofs_ulongs(struct ntfs_volume *vol, + const void *data1, const u32 data1_len, + const void *data2, const u32 data2_len) +{ + int len; + const __le32 *p1 = data1, *p2 = data2; + u32 d1, d2; + + if (data1_len != data2_len || data1_len & 3) { + ntfs_error(vol->sb, "data1_len or data2_len not valid\n"); + return -1; + } + + len = data1_len; + do { + d1 = le32_to_cpup(p1); + p1++; + d2 = le32_to_cpup(p2); + p2++; + } while (d1 == d2 && (len -= 4) > 0); + return cmp_int(d1, d2); +} + +/* + * ntfs_collate_file_name - Which of two filenames should be listed first + * @vol: ntfs volume + * @data1: first filename to collate + * @data1_len: length in bytes of @data1(unused) + * @data2: second filename to collate + * @data2_len: length in bytes of @data2(unused) + */ +static int ntfs_collate_file_name(struct ntfs_volume *vol, + const void *data1, const u32 data1_len, + const void *data2, const u32 data2_len) +{ + int rc; + + rc = ntfs_file_compare_values(data1, data2, -EINVAL, + IGNORE_CASE, vol->upcase, vol->upcase_len); + if (!rc) + rc = ntfs_file_compare_values(data1, data2, + -EINVAL, CASE_SENSITIVE, vol->upcase, vol->upcase_len); + return rc; +} + +/* + * ntfs_collate - collate two data items using a specified collation rule + * @vol: ntfs volume to which the data items belong + * @cr: collation rule to use when comparing the items + * @data1: first data item to collate + * @data1_len: length in bytes of @data1 + * @data2: second data item to collate + * @data2_len: length in bytes of @data2 + * + * Collate the two data items @data1 and @data2 using the collation rule @cr + * and return -1, 0, ir 1 if @data1 is found, respectively, to collate before, + * to match, or to collate after @data2. return -EINVAL if an error occurred. + */ +int ntfs_collate(struct ntfs_volume *vol, __le32 cr, + const void *data1, const u32 data1_len, + const void *data2, const u32 data2_len) +{ + switch (le32_to_cpu(cr)) { + case le32_to_cpu(COLLATION_BINARY): + return ntfs_collate_binary(vol, data1, data1_len, + data2, data2_len); + case le32_to_cpu(COLLATION_FILE_NAME): + return ntfs_collate_file_name(vol, data1, data1_len, + data2, data2_len); + case le32_to_cpu(COLLATION_NTOFS_ULONG): + return ntfs_collate_ntofs_ulong(vol, data1, data1_len, + data2, data2_len); + case le32_to_cpu(COLLATION_NTOFS_ULONGS): + return ntfs_collate_ntofs_ulongs(vol, data1, data1_len, + data2, data2_len); + default: + ntfs_error(vol->sb, "Unknown collation rule 0x%x", + le32_to_cpu(cr)); + return -EINVAL; + } +} diff --git a/fs/ntfs/collate.h b/fs/ntfs/collate.h new file mode 100644 index 000000000000..8ad1f04be675 --- /dev/null +++ b/fs/ntfs/collate.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for NTFS kernel collation handling. + * + * Copyright (c) 2004 Anton Altaparmakov + * + * Part of this file is based on code from the NTFS-3G. + * and is copyrighted by the respective authors below: + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + */ + +#ifndef _LINUX_NTFS_COLLATE_H +#define _LINUX_NTFS_COLLATE_H + +#include "volume.h" + +static inline bool ntfs_is_collation_rule_supported(__le32 cr) +{ + int i; + + if (unlikely(cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG && + cr != COLLATION_FILE_NAME) && cr != COLLATION_NTOFS_ULONGS) + return false; + i = le32_to_cpu(cr); + if (likely(((i >= 0) && (i <= 0x02)) || + ((i >= 0x10) && (i <= 0x13)))) + return true; + return false; +} + +int ntfs_collate(struct ntfs_volume *vol, __le32 cr, + const void *data1, const u32 data1_len, + const void *data2, const u32 data2_len); + +#endif /* _LINUX_NTFS_COLLATE_H */ diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c new file mode 100644 index 000000000000..76bd806b41ed --- /dev/null +++ b/fs/ntfs/compress.c @@ -0,0 +1,1578 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel compressed attributes handling. + * + * Copyright (c) 2001-2004 Anton Altaparmakov + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. + * + * Part of this file is based on code from the NTFS-3G. + * and is copyrighted by the respective authors below: + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2006 Szabolcs Szakacsits + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2009-2014 Jean-Pierre Andre + * Copyright (c) 2014 Eric Biggers + */ + +#include +#include +#include +#include + +#include "attrib.h" +#include "inode.h" +#include "debug.h" +#include "ntfs.h" +#include "lcnalloc.h" +#include "mft.h" + +/* + * Constants used in the compression code + */ +enum { + /* Token types and access mask. */ + NTFS_SYMBOL_TOKEN = 0, + NTFS_PHRASE_TOKEN = 1, + NTFS_TOKEN_MASK = 1, + + /* Compression sub-block constants. */ + NTFS_SB_SIZE_MASK = 0x0fff, + NTFS_SB_SIZE = 0x1000, + NTFS_SB_IS_COMPRESSED = 0x8000, + + /* + * The maximum compression block size is by definition 16 * the cluster + * size, with the maximum supported cluster size being 4kiB. Thus the + * maximum compression buffer size is 64kiB, so we use this when + * initializing the compression buffer. + */ + NTFS_MAX_CB_SIZE = 64 * 1024, +}; + +/* + * ntfs_compression_buffer - one buffer for the decompression engine + */ +static u8 *ntfs_compression_buffer; + +/* + * ntfs_cb_lock - mutex lock which protects ntfs_compression_buffer + */ +static DEFINE_MUTEX(ntfs_cb_lock); + +/* + * allocate_compression_buffers - allocate the decompression buffers + * + * Caller has to hold the ntfs_lock mutex. + * + * Return 0 on success or -ENOMEM if the allocations failed. + */ +int allocate_compression_buffers(void) +{ + if (ntfs_compression_buffer) + return 0; + + ntfs_compression_buffer = vmalloc(NTFS_MAX_CB_SIZE); + if (!ntfs_compression_buffer) + return -ENOMEM; + return 0; +} + +/* + * free_compression_buffers - free the decompression buffers + * + * Caller has to hold the ntfs_lock mutex. + */ +void free_compression_buffers(void) +{ + mutex_lock(&ntfs_cb_lock); + if (!ntfs_compression_buffer) { + mutex_unlock(&ntfs_cb_lock); + return; + } + + vfree(ntfs_compression_buffer); + ntfs_compression_buffer = NULL; + mutex_unlock(&ntfs_cb_lock); +} + +/* + * zero_partial_compressed_page - zero out of bounds compressed page region + * @page: page to zero + * @initialized_size: initialized size of the attribute + */ +static void zero_partial_compressed_page(struct page *page, + const s64 initialized_size) +{ + u8 *kp = page_address(page); + unsigned int kp_ofs; + + ntfs_debug("Zeroing page region outside initialized size."); + if (((s64)page->__folio_index << PAGE_SHIFT) >= initialized_size) { + clear_page(kp); + return; + } + kp_ofs = initialized_size & ~PAGE_MASK; + memset(kp + kp_ofs, 0, PAGE_SIZE - kp_ofs); +} + +/* + * handle_bounds_compressed_page - test for&handle out of bounds compressed page + * @page: page to check and handle + * @i_size: file size + * @initialized_size: initialized size of the attribute + */ +static inline void handle_bounds_compressed_page(struct page *page, + const loff_t i_size, const s64 initialized_size) +{ + if ((page->__folio_index >= (initialized_size >> PAGE_SHIFT)) && + (initialized_size < i_size)) + zero_partial_compressed_page(page, initialized_size); +} + +/* + * ntfs_decompress - decompress a compression block into an array of pages + * @dest_pages: destination array of pages + * @completed_pages: scratch space to track completed pages + * @dest_index: current index into @dest_pages (IN/OUT) + * @dest_ofs: current offset within @dest_pages[@dest_index] (IN/OUT) + * @dest_max_index: maximum index into @dest_pages (IN) + * @dest_max_ofs: maximum offset within @dest_pages[@dest_max_index] (IN) + * @xpage: the target page (-1 if none) (IN) + * @xpage_done: set to 1 if xpage was completed successfully (IN/OUT) + * @cb_start: compression block to decompress (IN) + * @cb_size: size of compression block @cb_start in bytes (IN) + * @i_size: file size when we started the read (IN) + * @initialized_size: initialized file size when we started the read (IN) + * + * The caller must have disabled preemption. ntfs_decompress() reenables it when + * the critical section is finished. + * + * This decompresses the compression block @cb_start into the array of + * destination pages @dest_pages starting at index @dest_index into @dest_pages + * and at offset @dest_pos into the page @dest_pages[@dest_index]. + * + * When the page @dest_pages[@xpage] is completed, @xpage_done is set to 1. + * If xpage is -1 or @xpage has not been completed, @xpage_done is not modified. + * + * @cb_start is a pointer to the compression block which needs decompressing + * and @cb_size is the size of @cb_start in bytes (8-64kiB). + * + * Return 0 if success or -EOVERFLOW on error in the compressed stream. + * @xpage_done indicates whether the target page (@dest_pages[@xpage]) was + * completed during the decompression of the compression block (@cb_start). + * + * Warning: This function *REQUIRES* PAGE_SIZE >= 4096 or it will blow up + * unpredicatbly! You have been warned! + * + * Note to hackers: This function may not sleep until it has finished accessing + * the compression block @cb_start as it is a per-CPU buffer. + */ +static int ntfs_decompress(struct page *dest_pages[], int completed_pages[], + int *dest_index, int *dest_ofs, const int dest_max_index, + const int dest_max_ofs, const int xpage, char *xpage_done, + u8 *const cb_start, const u32 cb_size, const loff_t i_size, + const s64 initialized_size) +{ + /* + * Pointers into the compressed data, i.e. the compression block (cb), + * and the therein contained sub-blocks (sb). + */ + u8 *cb_end = cb_start + cb_size; /* End of cb. */ + u8 *cb = cb_start; /* Current position in cb. */ + u8 *cb_sb_start = cb; /* Beginning of the current sb in the cb. */ + u8 *cb_sb_end; /* End of current sb / beginning of next sb. */ + + /* Variables for uncompressed data / destination. */ + struct page *dp; /* Current destination page being worked on. */ + u8 *dp_addr; /* Current pointer into dp. */ + u8 *dp_sb_start; /* Start of current sub-block in dp. */ + u8 *dp_sb_end; /* End of current sb in dp (dp_sb_start + NTFS_SB_SIZE). */ + u16 do_sb_start; /* @dest_ofs when starting this sub-block. */ + u16 do_sb_end; /* @dest_ofs of end of this sb (do_sb_start + NTFS_SB_SIZE). */ + + /* Variables for tag and token parsing. */ + u8 tag; /* Current tag. */ + int token; /* Loop counter for the eight tokens in tag. */ + int nr_completed_pages = 0; + + /* Default error code. */ + int err = -EOVERFLOW; + + ntfs_debug("Entering, cb_size = 0x%x.", cb_size); +do_next_sb: + ntfs_debug("Beginning sub-block at offset = 0x%zx in the cb.", + cb - cb_start); + /* + * Have we reached the end of the compression block or the end of the + * decompressed data? The latter can happen for example if the current + * position in the compression block is one byte before its end so the + * first two checks do not detect it. + */ + if (cb == cb_end || !le16_to_cpup((__le16 *)cb) || + (*dest_index == dest_max_index && + *dest_ofs == dest_max_ofs)) { + int i; + + ntfs_debug("Completed. Returning success (0)."); + err = 0; +return_error: + /* We can sleep from now on, so we drop lock. */ + mutex_unlock(&ntfs_cb_lock); + /* Second stage: finalize completed pages. */ + if (nr_completed_pages > 0) { + for (i = 0; i < nr_completed_pages; i++) { + int di = completed_pages[i]; + + dp = dest_pages[di]; + /* + * If we are outside the initialized size, zero + * the out of bounds page range. + */ + handle_bounds_compressed_page(dp, i_size, + initialized_size); + flush_dcache_page(dp); + kunmap_local(page_address(dp)); + SetPageUptodate(dp); + unlock_page(dp); + if (di == xpage) + *xpage_done = 1; + else + put_page(dp); + dest_pages[di] = NULL; + } + } + return err; + } + + /* Setup offsets for the current sub-block destination. */ + do_sb_start = *dest_ofs; + do_sb_end = do_sb_start + NTFS_SB_SIZE; + + /* Check that we are still within allowed boundaries. */ + if (*dest_index == dest_max_index && do_sb_end > dest_max_ofs) + goto return_overflow; + + /* Does the minimum size of a compressed sb overflow valid range? */ + if (cb + 6 > cb_end) + goto return_overflow; + + /* Setup the current sub-block source pointers and validate range. */ + cb_sb_start = cb; + cb_sb_end = cb_sb_start + (le16_to_cpup((__le16 *)cb) & NTFS_SB_SIZE_MASK) + + 3; + if (cb_sb_end > cb_end) + goto return_overflow; + + /* Get the current destination page. */ + dp = dest_pages[*dest_index]; + if (!dp) { + /* No page present. Skip decompression of this sub-block. */ + cb = cb_sb_end; + + /* Advance destination position to next sub-block. */ + *dest_ofs = (*dest_ofs + NTFS_SB_SIZE) & ~PAGE_MASK; + if (!*dest_ofs && (++*dest_index > dest_max_index)) + goto return_overflow; + goto do_next_sb; + } + + /* We have a valid destination page. Setup the destination pointers. */ + dp_addr = (u8 *)page_address(dp) + do_sb_start; + + /* Now, we are ready to process the current sub-block (sb). */ + if (!(le16_to_cpup((__le16 *)cb) & NTFS_SB_IS_COMPRESSED)) { + ntfs_debug("Found uncompressed sub-block."); + /* This sb is not compressed, just copy it into destination. */ + + /* Advance source position to first data byte. */ + cb += 2; + + /* An uncompressed sb must be full size. */ + if (cb_sb_end - cb != NTFS_SB_SIZE) + goto return_overflow; + + /* Copy the block and advance the source position. */ + memcpy(dp_addr, cb, NTFS_SB_SIZE); + cb += NTFS_SB_SIZE; + + /* Advance destination position to next sub-block. */ + *dest_ofs += NTFS_SB_SIZE; + *dest_ofs &= ~PAGE_MASK; + if (!(*dest_ofs)) { +finalize_page: + /* + * First stage: add current page index to array of + * completed pages. + */ + completed_pages[nr_completed_pages++] = *dest_index; + if (++*dest_index > dest_max_index) + goto return_overflow; + } + goto do_next_sb; + } + ntfs_debug("Found compressed sub-block."); + /* This sb is compressed, decompress it into destination. */ + + /* Setup destination pointers. */ + dp_sb_start = dp_addr; + dp_sb_end = dp_sb_start + NTFS_SB_SIZE; + + /* Forward to the first tag in the sub-block. */ + cb += 2; +do_next_tag: + if (cb == cb_sb_end) { + /* Check if the decompressed sub-block was not full-length. */ + if (dp_addr < dp_sb_end) { + int nr_bytes = do_sb_end - *dest_ofs; + + ntfs_debug("Filling incomplete sub-block with zeroes."); + /* Zero remainder and update destination position. */ + memset(dp_addr, 0, nr_bytes); + *dest_ofs += nr_bytes; + } + /* We have finished the current sub-block. */ + *dest_ofs &= ~PAGE_MASK; + if (!(*dest_ofs)) + goto finalize_page; + goto do_next_sb; + } + + /* Check we are still in range. */ + if (cb > cb_sb_end || dp_addr > dp_sb_end) + goto return_overflow; + + /* Get the next tag and advance to first token. */ + tag = *cb++; + + /* Parse the eight tokens described by the tag. */ + for (token = 0; token < 8; token++, tag >>= 1) { + register u16 i; + u16 lg, pt, length, max_non_overlap; + u8 *dp_back_addr; + + /* Check if we are done / still in range. */ + if (cb >= cb_sb_end || dp_addr > dp_sb_end) + break; + + /* Determine token type and parse appropriately.*/ + if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) { + /* + * We have a symbol token, copy the symbol across, and + * advance the source and destination positions. + */ + *dp_addr++ = *cb++; + ++*dest_ofs; + + /* Continue with the next token. */ + continue; + } + + /* + * We have a phrase token. Make sure it is not the first tag in + * the sb as this is illegal and would confuse the code below. + */ + if (dp_addr == dp_sb_start) + goto return_overflow; + + /* + * Determine the number of bytes to go back (p) and the number + * of bytes to copy (l). We use an optimized algorithm in which + * we first calculate log2(current destination position in sb), + * which allows determination of l and p in O(1) rather than + * O(n). We just need an arch-optimized log2() function now. + */ + lg = 0; + for (i = *dest_ofs - do_sb_start - 1; i >= 0x10; i >>= 1) + lg++; + + /* Get the phrase token into i. */ + pt = le16_to_cpup((__le16 *)cb); + + /* + * Calculate starting position of the byte sequence in + * the destination using the fact that p = (pt >> (12 - lg)) + 1 + * and make sure we don't go too far back. + */ + dp_back_addr = dp_addr - (pt >> (12 - lg)) - 1; + if (dp_back_addr < dp_sb_start) + goto return_overflow; + + /* Now calculate the length of the byte sequence. */ + length = (pt & (0xfff >> lg)) + 3; + + /* Advance destination position and verify it is in range. */ + *dest_ofs += length; + if (*dest_ofs > do_sb_end) + goto return_overflow; + + /* The number of non-overlapping bytes. */ + max_non_overlap = dp_addr - dp_back_addr; + + if (length <= max_non_overlap) { + /* The byte sequence doesn't overlap, just copy it. */ + memcpy(dp_addr, dp_back_addr, length); + + /* Advance destination pointer. */ + dp_addr += length; + } else { + /* + * The byte sequence does overlap, copy non-overlapping + * part and then do a slow byte by byte copy for the + * overlapping part. Also, advance the destination + * pointer. + */ + memcpy(dp_addr, dp_back_addr, max_non_overlap); + dp_addr += max_non_overlap; + dp_back_addr += max_non_overlap; + length -= max_non_overlap; + while (length--) + *dp_addr++ = *dp_back_addr++; + } + + /* Advance source position and continue with the next token. */ + cb += 2; + } + + /* No tokens left in the current tag. Continue with the next tag. */ + goto do_next_tag; + +return_overflow: + ntfs_error(NULL, "Failed. Returning -EOVERFLOW."); + goto return_error; +} + +/* + * ntfs_read_compressed_block - read a compressed block into the page cache + * @folio: locked folio in the compression block(s) we need to read + * + * When we are called the page has already been verified to be locked and the + * attribute is known to be non-resident, not encrypted, but compressed. + * + * 1. Determine which compression block(s) @page is in. + * 2. Get hold of all pages corresponding to this/these compression block(s). + * 3. Read the (first) compression block. + * 4. Decompress it into the corresponding pages. + * 5. Throw the compressed data away and proceed to 3. for the next compression + * block or return success if no more compression blocks left. + * + * Warning: We have to be careful what we do about existing pages. They might + * have been written to so that we would lose data if we were to just overwrite + * them with the out-of-date uncompressed data. + */ +int ntfs_read_compressed_block(struct folio *folio) +{ + struct page *page = &folio->page; + loff_t i_size; + s64 initialized_size; + struct address_space *mapping = page->mapping; + struct ntfs_inode *ni = NTFS_I(mapping->host); + struct ntfs_volume *vol = ni->vol; + struct super_block *sb = vol->sb; + struct runlist_element *rl; + unsigned long flags; + u8 *cb, *cb_pos, *cb_end; + unsigned long offset, index = page->__folio_index; + u32 cb_size = ni->itype.compressed.block_size; + u64 cb_size_mask = cb_size - 1UL; + s64 vcn; + s64 lcn; + /* The first wanted vcn (minimum alignment is PAGE_SIZE). */ + s64 start_vcn = (((s64)index << PAGE_SHIFT) & ~cb_size_mask) >> + vol->cluster_size_bits; + /* + * The first vcn after the last wanted vcn (minimum alignment is again + * PAGE_SIZE. + */ + s64 end_vcn = ((((s64)(index + 1UL) << PAGE_SHIFT) + cb_size - 1) + & ~cb_size_mask) >> vol->cluster_size_bits; + /* Number of compression blocks (cbs) in the wanted vcn range. */ + unsigned int nr_cbs = ntfs_cluster_to_bytes(vol, end_vcn - start_vcn) >> + ni->itype.compressed.block_size_bits; + /* + * Number of pages required to store the uncompressed data from all + * compression blocks (cbs) overlapping @page. Due to alignment + * guarantees of start_vcn and end_vcn, no need to round up here. + */ + unsigned int nr_pages = ntfs_cluster_to_pidx(vol, end_vcn - start_vcn); + unsigned int xpage, max_page, cur_page, cur_ofs, i, page_ofs, page_index; + unsigned int cb_clusters, cb_max_ofs; + int cb_max_page, err = 0; + struct page **pages; + int *completed_pages; + unsigned char xpage_done = 0; + struct page *lpage; + + ntfs_debug("Entering, page->index = 0x%lx, cb_size = 0x%x, nr_pages = %i.", + index, cb_size, nr_pages); + /* + * Bad things happen if we get here for anything that is not an + * unnamed $DATA attribute. + */ + if (ni->type != AT_DATA || ni->name_len) { + unlock_page(page); + return -EIO; + } + + pages = kmalloc_array(nr_pages, sizeof(struct page *), GFP_NOFS); + completed_pages = kmalloc_array(nr_pages + 1, sizeof(int), GFP_NOFS); + + if (unlikely(!pages || !completed_pages)) { + kfree(pages); + kfree(completed_pages); + unlock_page(page); + ntfs_error(vol->sb, "Failed to allocate internal buffers."); + return -ENOMEM; + } + + /* + * We have already been given one page, this is the one we must do. + * Once again, the alignment guarantees keep it simple. + */ + offset = ntfs_cluster_to_pidx(vol, start_vcn); + xpage = index - offset; + pages[xpage] = page; + /* + * The remaining pages need to be allocated and inserted into the page + * cache, alignment guarantees keep all the below much simpler. (-8 + */ + read_lock_irqsave(&ni->size_lock, flags); + i_size = i_size_read(VFS_I(ni)); + initialized_size = ni->initialized_size; + read_unlock_irqrestore(&ni->size_lock, flags); + max_page = ((i_size + PAGE_SIZE - 1) >> PAGE_SHIFT) - + offset; + /* Is the page fully outside i_size? (truncate in progress) */ + if (xpage >= max_page) { + kfree(pages); + kfree(completed_pages); + zero_user_segments(page, 0, PAGE_SIZE, 0, 0); + ntfs_debug("Compressed read outside i_size - truncated?"); + SetPageUptodate(page); + unlock_page(page); + return 0; + } + if (nr_pages < max_page) + max_page = nr_pages; + + for (i = 0; i < max_page; i++, offset++) { + if (i != xpage) + pages[i] = grab_cache_page_nowait(mapping, offset); + page = pages[i]; + if (page) { + /* + * We only (re)read the page if it isn't already read + * in and/or dirty or we would be losing data or at + * least wasting our time. + */ + if (!PageDirty(page) && (!PageUptodate(page))) { + kmap_local_page(page); + continue; + } + unlock_page(page); + put_page(page); + pages[i] = NULL; + } + } + + /* + * We have the runlist, and all the destination pages we need to fill. + * Now read the first compression block. + */ + cur_page = 0; + cur_ofs = 0; + cb_clusters = ni->itype.compressed.block_clusters; +do_next_cb: + nr_cbs--; + + mutex_lock(&ntfs_cb_lock); + if (!ntfs_compression_buffer) + if (allocate_compression_buffers()) { + mutex_unlock(&ntfs_cb_lock); + goto err_out; + } + + + cb = ntfs_compression_buffer; + cb_pos = cb; + cb_end = cb + cb_size; + + rl = NULL; + for (vcn = start_vcn, start_vcn += cb_clusters; vcn < start_vcn; + vcn++) { + bool is_retry = false; + + if (!rl) { +lock_retry_remap: + down_read(&ni->runlist.lock); + rl = ni->runlist.rl; + } + if (likely(rl != NULL)) { + /* Seek to element containing target vcn. */ + while (rl->length && rl[1].vcn <= vcn) + rl++; + lcn = ntfs_rl_vcn_to_lcn(rl, vcn); + } else + lcn = LCN_RL_NOT_MAPPED; + ntfs_debug("Reading vcn = 0x%llx, lcn = 0x%llx.", + (unsigned long long)vcn, + (unsigned long long)lcn); + if (lcn < 0) { + /* + * When we reach the first sparse cluster we have + * finished with the cb. + */ + if (lcn == LCN_HOLE) + break; + if (is_retry || lcn != LCN_RL_NOT_MAPPED) { + mutex_unlock(&ntfs_cb_lock); + goto rl_err; + } + is_retry = true; + /* + * Attempt to map runlist, dropping lock for the + * duration. + */ + up_read(&ni->runlist.lock); + if (!ntfs_map_runlist(ni, vcn)) + goto lock_retry_remap; + mutex_unlock(&ntfs_cb_lock); + goto map_rl_err; + } + + page_ofs = ntfs_cluster_to_poff(vol, lcn); + page_index = ntfs_cluster_to_pidx(vol, lcn); + + lpage = read_mapping_page(sb->s_bdev->bd_mapping, + page_index, NULL); + if (IS_ERR(lpage)) { + err = PTR_ERR(lpage); + mutex_unlock(&ntfs_cb_lock); + goto read_err; + } + + lock_page(lpage); + memcpy(cb_pos, page_address(lpage) + page_ofs, + vol->cluster_size); + unlock_page(lpage); + put_page(lpage); + cb_pos += vol->cluster_size; + } + + /* Release the lock if we took it. */ + if (rl) + up_read(&ni->runlist.lock); + + /* Just a precaution. */ + if (cb_pos + 2 <= cb + cb_size) + *(u16 *)cb_pos = 0; + + /* Reset cb_pos back to the beginning. */ + cb_pos = cb; + + /* We now have both source (if present) and destination. */ + ntfs_debug("Successfully read the compression block."); + + /* The last page and maximum offset within it for the current cb. */ + cb_max_page = (cur_page << PAGE_SHIFT) + cur_ofs + cb_size; + cb_max_ofs = cb_max_page & ~PAGE_MASK; + cb_max_page >>= PAGE_SHIFT; + + /* Catch end of file inside a compression block. */ + if (cb_max_page > max_page) + cb_max_page = max_page; + + if (vcn == start_vcn - cb_clusters) { + /* Sparse cb, zero out page range overlapping the cb. */ + ntfs_debug("Found sparse compression block."); + /* We can sleep from now on, so we drop lock. */ + mutex_unlock(&ntfs_cb_lock); + if (cb_max_ofs) + cb_max_page--; + for (; cur_page < cb_max_page; cur_page++) { + page = pages[cur_page]; + if (page) { + if (likely(!cur_ofs)) + clear_page(page_address(page)); + else + memset(page_address(page) + cur_ofs, 0, + PAGE_SIZE - + cur_ofs); + flush_dcache_page(page); + kunmap_local(page_address(page)); + SetPageUptodate(page); + unlock_page(page); + if (cur_page == xpage) + xpage_done = 1; + else + put_page(page); + pages[cur_page] = NULL; + } + cb_pos += PAGE_SIZE - cur_ofs; + cur_ofs = 0; + if (cb_pos >= cb_end) + break; + } + /* If we have a partial final page, deal with it now. */ + if (cb_max_ofs && cb_pos < cb_end) { + page = pages[cur_page]; + if (page) + memset(page_address(page) + cur_ofs, 0, + cb_max_ofs - cur_ofs); + /* + * No need to update cb_pos at this stage: + * cb_pos += cb_max_ofs - cur_ofs; + */ + cur_ofs = cb_max_ofs; + } + } else if (vcn == start_vcn) { + /* We can't sleep so we need two stages. */ + unsigned int cur2_page = cur_page; + unsigned int cur_ofs2 = cur_ofs; + u8 *cb_pos2 = cb_pos; + + ntfs_debug("Found uncompressed compression block."); + /* Uncompressed cb, copy it to the destination pages. */ + if (cb_max_ofs) + cb_max_page--; + /* First stage: copy data into destination pages. */ + for (; cur_page < cb_max_page; cur_page++) { + page = pages[cur_page]; + if (page) + memcpy(page_address(page) + cur_ofs, cb_pos, + PAGE_SIZE - cur_ofs); + cb_pos += PAGE_SIZE - cur_ofs; + cur_ofs = 0; + if (cb_pos >= cb_end) + break; + } + /* If we have a partial final page, deal with it now. */ + if (cb_max_ofs && cb_pos < cb_end) { + page = pages[cur_page]; + if (page) + memcpy(page_address(page) + cur_ofs, cb_pos, + cb_max_ofs - cur_ofs); + cb_pos += cb_max_ofs - cur_ofs; + cur_ofs = cb_max_ofs; + } + /* We can sleep from now on, so drop lock. */ + mutex_unlock(&ntfs_cb_lock); + /* Second stage: finalize pages. */ + for (; cur2_page < cb_max_page; cur2_page++) { + page = pages[cur2_page]; + if (page) { + /* + * If we are outside the initialized size, zero + * the out of bounds page range. + */ + handle_bounds_compressed_page(page, i_size, + initialized_size); + flush_dcache_page(page); + kunmap_local(page_address(page)); + SetPageUptodate(page); + unlock_page(page); + if (cur2_page == xpage) + xpage_done = 1; + else + put_page(page); + pages[cur2_page] = NULL; + } + cb_pos2 += PAGE_SIZE - cur_ofs2; + cur_ofs2 = 0; + if (cb_pos2 >= cb_end) + break; + } + } else { + /* Compressed cb, decompress it into the destination page(s). */ + unsigned int prev_cur_page = cur_page; + + ntfs_debug("Found compressed compression block."); + err = ntfs_decompress(pages, completed_pages, &cur_page, + &cur_ofs, cb_max_page, cb_max_ofs, xpage, + &xpage_done, cb_pos, cb_size - (cb_pos - cb), + i_size, initialized_size); + /* + * We can sleep from now on, lock already dropped by + * ntfs_decompress(). + */ + if (err) { + ntfs_error(vol->sb, + "ntfs_decompress() failed in inode 0x%llx with error code %i. Skipping this compression block.", + ni->mft_no, -err); + /* Release the unfinished pages. */ + for (; prev_cur_page < cur_page; prev_cur_page++) { + page = pages[prev_cur_page]; + if (page) { + flush_dcache_page(page); + kunmap_local(page_address(page)); + unlock_page(page); + if (prev_cur_page != xpage) + put_page(page); + pages[prev_cur_page] = NULL; + } + } + } + } + + /* Do we have more work to do? */ + if (nr_cbs) + goto do_next_cb; + + /* Clean up if we have any pages left. Should never happen. */ + for (cur_page = 0; cur_page < max_page; cur_page++) { + page = pages[cur_page]; + if (page) { + ntfs_error(vol->sb, + "Still have pages left! Terminating them with extreme prejudice. Inode 0x%llx, page index 0x%lx.", + ni->mft_no, page->__folio_index); + flush_dcache_page(page); + kunmap_local(page_address(page)); + unlock_page(page); + if (cur_page != xpage) + put_page(page); + pages[cur_page] = NULL; + } + } + + /* We no longer need the list of pages. */ + kfree(pages); + kfree(completed_pages); + + /* If we have completed the requested page, we return success. */ + if (likely(xpage_done)) + return 0; + + ntfs_debug("Failed. Returning error code %s.", err == -EOVERFLOW ? + "EOVERFLOW" : (!err ? "EIO" : "unknown error")); + return err < 0 ? err : -EIO; + +map_rl_err: + ntfs_error(vol->sb, "ntfs_map_runlist() failed. Cannot read compression block."); + goto err_out; + +rl_err: + up_read(&ni->runlist.lock); + ntfs_error(vol->sb, "ntfs_rl_vcn_to_lcn() failed. Cannot read compression block."); + goto err_out; + +read_err: + up_read(&ni->runlist.lock); + ntfs_error(vol->sb, "IO error while reading compressed data."); + +err_out: + for (i = cur_page; i < max_page; i++) { + page = pages[i]; + if (page) { + flush_dcache_page(page); + kunmap_local(page_address(page)); + unlock_page(page); + if (i != xpage) + put_page(page); + } + } + kfree(pages); + kfree(completed_pages); + return -EIO; +} + +/* + * Match length at or above which ntfs_best_match() will stop searching for + * longer matches. + */ +#define NICE_MATCH_LEN 18 + +/* + * Maximum number of potential matches that ntfs_best_match() will consider at + * each position. + */ +#define MAX_SEARCH_DEPTH 24 + +/* log base 2 of the number of entries in the hash table for match-finding. */ +#define HASH_SHIFT 14 + +/* + * Constant for the multiplicative hash function. These hashing constants + * are used solely for the match-finding algorithm during compression. + * They are NOT part of the on-disk format. The decompressor does not + * utilize this hash. + */ +#define HASH_MULTIPLIER 0x1E35A7BD + +struct compress_context { + const unsigned char *inbuf; + int bufsize; + int size; + int rel; + int mxsz; + s16 head[1 << HASH_SHIFT]; + s16 prev[NTFS_SB_SIZE]; +}; + +/* + * Hash the next 3-byte sequence in the input buffer + */ +static inline unsigned int ntfs_hash(const u8 *p) +{ + u32 str; + u32 hash; + + /* + * Unaligned access allowed, and little endian CPU. + * Callers ensure that at least 4 (not 3) bytes are remaining. + */ + str = *(const u32 *)p & 0xFFFFFF; + hash = str * HASH_MULTIPLIER; + + /* High bits are more random than the low bits. */ + return hash >> (32 - HASH_SHIFT); +} + +/* + * Search for the longest sequence matching current position + * + * A hash table, each entry of which points to a chain of sequence + * positions sharing the corresponding hash code, is maintained to speed up + * searching for matches. To maintain the hash table, either + * ntfs_best_match() or ntfs_skip_position() has to be called for each + * consecutive position. + * + * This function is heavily used; it has to be optimized carefully. + * + * This function sets pctx->size and pctx->rel to the length and offset, + * respectively, of the longest match found. + * + * The minimum match length is assumed to be 3, and the maximum match + * length is assumed to be pctx->mxsz. If this function produces + * pctx->size < 3, then no match was found. + * + * Note: for the following reasons, this function is not guaranteed to find + * *the* longest match up to pctx->mxsz: + * + * (1) If this function finds a match of NICE_MATCH_LEN bytes or greater, + * it ends early because a match this long is good enough and it's not + * worth spending more time searching. + * + * (2) If this function considers MAX_SEARCH_DEPTH matches with a single + * position, it ends early and returns the longest match found so far. + * This saves a lot of time on degenerate inputs. + */ +static void ntfs_best_match(struct compress_context *pctx, const int i, + int best_len) +{ + const u8 * const inbuf = pctx->inbuf; + const u8 * const strptr = &inbuf[i]; /* String we're matching against */ + s16 * const prev = pctx->prev; + const int max_len = min(pctx->bufsize - i, pctx->mxsz); + const int nice_len = min(NICE_MATCH_LEN, max_len); + int depth_remaining = MAX_SEARCH_DEPTH; + const u8 *best_matchptr = strptr; + unsigned int hash; + s16 cur_match; + const u8 *matchptr; + int len; + + if (max_len < 4) + goto out; + + /* Insert the current sequence into the appropriate hash chain. */ + hash = ntfs_hash(strptr); + cur_match = pctx->head[hash]; + prev[i] = cur_match; + pctx->head[hash] = i; + + if (best_len >= max_len) { + /* + * Lazy match is being attempted, but there aren't enough length + * bits remaining to code a longer match. + */ + goto out; + } + + /* Search the appropriate hash chain for matches. */ + + for (; cur_match >= 0 && depth_remaining--; cur_match = prev[cur_match]) { + matchptr = &inbuf[cur_match]; + + /* + * Considering the potential match at 'matchptr': is it longer + * than 'best_len'? + * + * The bytes at index 'best_len' are the most likely to differ, + * so check them first. + * + * The bytes at indices 'best_len - 1' and '0' are less + * important to check separately. But doing so still gives a + * slight performance improvement, at least on x86_64, probably + * because they create separate branches for the CPU to predict + * independently of the branches in the main comparison loops. + */ + if (matchptr[best_len] != strptr[best_len] || + matchptr[best_len - 1] != strptr[best_len - 1] || + matchptr[0] != strptr[0]) + goto next_match; + + for (len = 1; len < best_len - 1; len++) + if (matchptr[len] != strptr[len]) + goto next_match; + + /* + * The match is the longest found so far --- + * at least 'best_len' + 1 bytes. Continue extending it. + */ + + best_matchptr = matchptr; + + do { + if (++best_len >= nice_len) { + /* + * 'nice_len' reached; don't waste time + * searching for longer matches. Extend the + * match as far as possible and terminate the + * search. + */ + while (best_len < max_len && + (best_matchptr[best_len] == + strptr[best_len])) + best_len++; + goto out; + } + } while (best_matchptr[best_len] == strptr[best_len]); + + /* Found a longer match, but 'nice_len' not yet reached. */ + +next_match: + /* Continue to next match in the chain. */ + ; + } + + /* + * Reached end of chain, or ended early due to reaching the maximum + * search depth. + */ + +out: + /* Return the longest match we were able to find. */ + pctx->size = best_len; + pctx->rel = best_matchptr - strptr; /* given as a negative number! */ +} + +/* + * Advance the match-finder, but don't search for matches. + */ +static void ntfs_skip_position(struct compress_context *pctx, const int i) +{ + unsigned int hash; + + if (pctx->bufsize - i < 4) + return; + + /* Insert the current sequence into the appropriate hash chain. */ + hash = ntfs_hash(pctx->inbuf + i); + pctx->prev[i] = pctx->head[hash]; + pctx->head[hash] = i; +} + +/* + * Compress a 4096-byte block + * + * Returns a header of two bytes followed by the compressed data. + * If compression is not effective, the header and an uncompressed + * block is returned. + * + * Note : two bytes may be output before output buffer overflow + * is detected, so a 4100-bytes output buffer must be reserved. + * + * Returns the size of the compressed block, including the + * header (minimal size is 2, maximum size is 4098) + * 0 if an error has been met. + */ +static unsigned int ntfs_compress_block(const char *inbuf, const int bufsize, + char *outbuf) +{ + struct compress_context *pctx; + int i; /* current position */ + int j; /* end of best match from current position */ + int k; /* end of best match from next position */ + int offs; /* offset to best match */ + int bp; /* bits to store offset */ + int bp_cur; /* saved bits to store offset at current position */ + int mxoff; /* max match offset : 1 << bp */ + unsigned int xout; + unsigned int q; /* aggregated offset and size */ + int have_match; /* do we have a match at the current position? */ + char *ptag; /* location reserved for a tag */ + int tag; /* current value of tag */ + int ntag; /* count of bits still undefined in tag */ + + pctx = kvzalloc(sizeof(struct compress_context), GFP_NOFS); + if (!pctx) + return -ENOMEM; + + /* + * All hash chains start as empty. The special value '-1' indicates the + * end of each hash chain. + */ + memset(pctx->head, 0xFF, sizeof(pctx->head)); + + pctx->inbuf = (const unsigned char *)inbuf; + pctx->bufsize = bufsize; + xout = 2; + i = 0; + bp = 4; + mxoff = 1 << bp; + pctx->mxsz = (1 << (16 - bp)) + 2; + have_match = 0; + tag = 0; + ntag = 8; + ptag = &outbuf[xout++]; + + while ((i < bufsize) && (xout < (NTFS_SB_SIZE + 2))) { + + /* + * This implementation uses "lazy" parsing: it always chooses + * the longest match, unless the match at the next position is + * longer. This is the same strategy used by the high + * compression modes of zlib. + */ + if (!have_match) { + /* + * Find the longest match at the current position. But + * first adjust the maximum match length if needed. + * (This loop might need to run more than one time in + * the case that we just output a long match.) + */ + while (mxoff < i) { + bp++; + mxoff <<= 1; + pctx->mxsz = (pctx->mxsz + 2) >> 1; + } + ntfs_best_match(pctx, i, 2); + } + + if (pctx->size >= 3) { + /* Found a match at the current position. */ + j = i + pctx->size; + bp_cur = bp; + offs = pctx->rel; + + if (pctx->size >= NICE_MATCH_LEN) { + /* Choose long matches immediately. */ + q = (~offs << (16 - bp_cur)) + (j - i - 3); + outbuf[xout++] = q & 255; + outbuf[xout++] = (q >> 8) & 255; + tag |= (1 << (8 - ntag)); + + if (j == bufsize) { + /* + * Shortcut if the match extends to the + * end of the buffer. + */ + i = j; + --ntag; + break; + } + i += 1; + do { + ntfs_skip_position(pctx, i); + } while (++i != j); + have_match = 0; + } else { + /* + * Check for a longer match at the next + * position. + */ + + /* + * Doesn't need to be while() since we just + * adjusted the maximum match length at the + * previous position. + */ + if (mxoff < i + 1) { + bp++; + mxoff <<= 1; + pctx->mxsz = (pctx->mxsz + 2) >> 1; + } + ntfs_best_match(pctx, i + 1, pctx->size); + k = i + 1 + pctx->size; + + if (k > (j + 1)) { + /* + * Next match is longer. + * Output a literal. + */ + outbuf[xout++] = inbuf[i++]; + have_match = 1; + } else { + /* + * Next match isn't longer. + * Output the current match. + */ + q = (~offs << (16 - bp_cur)) + + (j - i - 3); + outbuf[xout++] = q & 255; + outbuf[xout++] = (q >> 8) & 255; + tag |= (1 << (8 - ntag)); + + /* + * The minimum match length is 3, and + * we've run two bytes through the + * matchfinder already. So the minimum + * number of positions we need to skip + * is 1. + */ + i += 2; + do { + ntfs_skip_position(pctx, i); + } while (++i != j); + have_match = 0; + } + } + } else { + /* No match at current position. Output a literal. */ + outbuf[xout++] = inbuf[i++]; + have_match = 0; + } + + /* Store the tag if fully used. */ + if (!--ntag) { + *ptag = tag; + ntag = 8; + ptag = &outbuf[xout++]; + tag = 0; + } + } + + /* Store the last tag if partially used. */ + if (ntag == 8) + xout--; + else + *ptag = tag; + + /* Determine whether to store the data compressed or uncompressed. */ + if ((i >= bufsize) && (xout < (NTFS_SB_SIZE + 2))) { + /* Compressed. */ + outbuf[0] = (xout - 3) & 255; + outbuf[1] = 0xb0 + (((xout - 3) >> 8) & 15); + } else { + /* Uncompressed. */ + memcpy(&outbuf[2], inbuf, bufsize); + if (bufsize < NTFS_SB_SIZE) + memset(&outbuf[bufsize + 2], 0, NTFS_SB_SIZE - bufsize); + outbuf[0] = 0xff; + outbuf[1] = 0x3f; + xout = NTFS_SB_SIZE + 2; + } + + /* + * Free the compression context and return the total number of bytes + * written to 'outbuf'. + */ + kvfree(pctx); + return xout; +} + +static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages, + int pages_per_cb) +{ + struct ntfs_volume *vol = ni->vol; + char *outbuf = NULL, *pbuf, *inbuf; + u32 compsz, p, insz = pages_per_cb << PAGE_SHIFT; + s32 rounded, bio_size; + unsigned int sz, bsz; + bool fail = false, allzeroes; + /* a single compressed zero */ + static char onezero[] = {0x01, 0xb0, 0x00, 0x00}; + /* a couple of compressed zeroes */ + static char twozeroes[] = {0x02, 0xb0, 0x00, 0x00, 0x00}; + /* more compressed zeroes, to be followed by some count */ + static char morezeroes[] = {0x03, 0xb0, 0x02, 0x00}; + struct page **pages_disk = NULL, *pg; + s64 bio_lcn; + struct runlist_element *rlc, *rl; + int i, err; + int pages_count = (round_up(ni->itype.compressed.block_size + 2 * + (ni->itype.compressed.block_size / NTFS_SB_SIZE) + 2, PAGE_SIZE)) / PAGE_SIZE; + size_t new_rl_count; + struct bio *bio = NULL; + loff_t new_length; + s64 new_vcn; + + inbuf = vmap(pages, pages_per_cb, VM_MAP, PAGE_KERNEL_RO); + if (!inbuf) + return -ENOMEM; + + /* may need 2 extra bytes per block and 2 more bytes */ + pages_disk = kcalloc(pages_count, sizeof(struct page *), GFP_NOFS); + if (!pages_disk) { + vunmap(inbuf); + return -ENOMEM; + } + + for (i = 0; i < pages_count; i++) { + pg = alloc_page(GFP_KERNEL); + if (!pg) { + err = -ENOMEM; + goto out; + } + pages_disk[i] = pg; + lock_page(pg); + kmap_local_page(pg); + } + + outbuf = vmap(pages_disk, pages_count, VM_MAP, PAGE_KERNEL); + if (!outbuf) { + err = -ENOMEM; + goto out; + } + + compsz = 0; + allzeroes = true; + for (p = 0; (p < insz) && !fail; p += NTFS_SB_SIZE) { + if ((p + NTFS_SB_SIZE) < insz) + bsz = NTFS_SB_SIZE; + else + bsz = insz - p; + pbuf = &outbuf[compsz]; + sz = ntfs_compress_block(&inbuf[p], bsz, pbuf); + /* fail if all the clusters (or more) are needed */ + if (!sz || ((compsz + sz + vol->cluster_size + 2) > + ni->itype.compressed.block_size)) + fail = true; + else { + if (allzeroes) { + /* check whether this is all zeroes */ + switch (sz) { + case 4: + allzeroes = !memcmp(pbuf, onezero, 4); + break; + case 5: + allzeroes = !memcmp(pbuf, twozeroes, 5); + break; + case 6: + allzeroes = !memcmp(pbuf, morezeroes, 4); + break; + default: + allzeroes = false; + break; + } + } + compsz += sz; + } + } + + if (!fail && !allzeroes) { + outbuf[compsz++] = 0; + outbuf[compsz++] = 0; + rounded = ((compsz - 1) | (vol->cluster_size - 1)) + 1; + memset(&outbuf[compsz], 0, rounded - compsz); + bio_size = rounded; + pages = pages_disk; + } else if (allzeroes) { + err = 0; + goto out; + } else { + bio_size = insz; + } + + new_vcn = ntfs_bytes_to_cluster(vol, + pos & ~((loff_t)ni->itype.compressed.block_size - 1)); + new_length = ntfs_bytes_to_cluster(vol, round_up(bio_size, vol->cluster_size)); + + err = ntfs_non_resident_attr_punch_hole(ni, new_vcn, ni->itype.compressed.block_clusters); + if (err < 0) + goto out; + + rlc = ntfs_cluster_alloc(vol, new_vcn, new_length, -1, DATA_ZONE, + false, true, true); + if (IS_ERR(rlc)) { + err = PTR_ERR(rlc); + goto out; + } + + bio_lcn = rlc->lcn; + down_write(&ni->runlist.lock); + rl = ntfs_runlists_merge(&ni->runlist, rlc, 0, &new_rl_count); + if (IS_ERR(rl)) { + up_write(&ni->runlist.lock); + ntfs_error(vol->sb, "Failed to merge runlists"); + err = PTR_ERR(rl); + if (ntfs_cluster_free_from_rl(vol, rlc)) + ntfs_error(vol->sb, "Failed to free hot clusters."); + kvfree(rlc); + goto out; + } + + ni->runlist.count = new_rl_count; + ni->runlist.rl = rl; + + err = ntfs_attr_update_mapping_pairs(ni, 0); + up_write(&ni->runlist.lock); + if (err) { + err = -EIO; + goto out; + } + + i = 0; + while (bio_size > 0) { + int page_size; + + if (bio_size >= PAGE_SIZE) { + page_size = PAGE_SIZE; + bio_size -= PAGE_SIZE; + } else { + page_size = bio_size; + bio_size = 0; + } + +setup_bio: + if (!bio) { + bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, + GFP_NOIO); + bio->bi_iter.bi_sector = + ntfs_bytes_to_sector(vol, + ntfs_cluster_to_bytes(vol, bio_lcn + i)); + } + + if (!bio_add_page(bio, pages[i], page_size, 0)) { + err = submit_bio_wait(bio); + bio_put(bio); + if (err) + goto out; + bio = NULL; + goto setup_bio; + } + i++; + } + + err = submit_bio_wait(bio); + bio_put(bio); +out: + vunmap(outbuf); + for (i = 0; i < pages_count; i++) { + pg = pages_disk[i]; + if (pg) { + kunmap_local(page_address(pg)); + unlock_page(pg); + put_page(pg); + } + } + kfree(pages_disk); + vunmap(inbuf); + NInoSetFileNameDirty(ni); + mark_mft_record_dirty(ni); + + return err; +} + +int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count, + struct iov_iter *from) +{ + struct folio *folio; + struct page **pages = NULL, *page; + int pages_per_cb = ni->itype.compressed.block_size >> PAGE_SHIFT; + int cb_size = ni->itype.compressed.block_size, cb_off, err = 0; + int i, ip; + size_t written = 0; + struct address_space *mapping = VFS_I(ni)->i_mapping; + + if (NInoCompressed(ni) && pos + count > ni->allocated_size) { + int err; + loff_t end = pos + count; + + err = ntfs_attr_expand(ni, end, + round_up(end, ni->itype.compressed.block_size)); + if (err) + return err; + } + + pages = kmalloc_array(pages_per_cb, sizeof(struct page *), GFP_NOFS); + if (!pages) + return -ENOMEM; + + while (count) { + pgoff_t index; + size_t copied, bytes; + int off; + + off = pos & (cb_size - 1); + bytes = cb_size - off; + if (bytes > count) + bytes = count; + + cb_off = pos & ~(cb_size - 1); + index = cb_off >> PAGE_SHIFT; + + if (unlikely(fault_in_iov_iter_readable(from, bytes))) { + err = -EFAULT; + goto out; + } + + for (i = 0; i < pages_per_cb; i++) { + folio = read_mapping_folio(mapping, index + i, NULL); + if (IS_ERR(folio)) { + for (ip = 0; ip < i; ip++) { + folio_unlock(page_folio(pages[ip])); + folio_put(page_folio(pages[ip])); + } + err = PTR_ERR(folio); + goto out; + } + + folio_lock(folio); + pages[i] = folio_page(folio, 0); + } + + WARN_ON(!bytes); + copied = 0; + ip = off >> PAGE_SHIFT; + off = offset_in_page(pos); + + for (;;) { + size_t cp, tail = PAGE_SIZE - off; + + page = pages[ip]; + cp = copy_folio_from_iter_atomic(page_folio(page), off, + min(tail, bytes), from); + flush_dcache_page(page); + + copied += cp; + bytes -= cp; + if (!bytes || !cp) + break; + + if (cp < tail) { + off += cp; + } else { + ip++; + off = 0; + } + } + + err = ntfs_write_cb(ni, pos, pages, pages_per_cb); + + for (i = 0; i < pages_per_cb; i++) { + folio = page_folio(pages[i]); + if (i < ip) { + folio_clear_dirty(folio); + folio_mark_uptodate(folio); + } + folio_unlock(folio); + folio_put(folio); + } + + if (err) + goto out; + + cond_resched(); + pos += copied; + written += copied; + count = iov_iter_count(from); + } + +out: + kfree(pages); + if (err < 0) + written = err; + + return written; +} diff --git a/fs/ntfs/debug.c b/fs/ntfs/debug.c new file mode 100644 index 000000000000..f241ebc97d37 --- /dev/null +++ b/fs/ntfs/debug.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel debug support. + * + * Copyright (c) 2001-2004 Anton Altaparmakov + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include "debug.h" + +/* + * __ntfs_warning - output a warning to the syslog + * @function: name of function outputting the warning + * @sb: super block of mounted ntfs filesystem + * @fmt: warning string containing format specifications + * @...: a variable number of arguments specified in @fmt + * + * Outputs a warning to the syslog for the mounted ntfs filesystem described + * by @sb. + * + * @fmt and the corresponding @... is printf style format string containing + * the warning string and the corresponding format arguments, respectively. + * + * @function is the name of the function from which __ntfs_warning is being + * called. + * + * Note, you should be using debug.h::ntfs_warning(@sb, @fmt, @...) instead + * as this provides the @function parameter automatically. + */ +void __ntfs_warning(const char *function, const struct super_block *sb, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + int flen = 0; + + if (function) + flen = strlen(function); + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; +#ifdef DEBUG + if (sb) + pr_warn("(device %s): %s(): %pV\n", + sb->s_id, flen ? function : "", &vaf); + else + pr_warn("%s(): %pV\n", flen ? function : "", &vaf); +#else + if (sb) + pr_warn_ratelimited("(device %s): %s(): %pV\n", + sb->s_id, flen ? function : "", &vaf); + else + pr_warn_ratelimited("%s(): %pV\n", flen ? function : "", &vaf); +#endif + va_end(args); +} + +/* + * __ntfs_error - output an error to the syslog + * @function: name of function outputting the error + * @sb: super block of mounted ntfs filesystem + * @fmt: error string containing format specifications + * @...: a variable number of arguments specified in @fmt + * + * Outputs an error to the syslog for the mounted ntfs filesystem described + * by @sb. + * + * @fmt and the corresponding @... is printf style format string containing + * the error string and the corresponding format arguments, respectively. + * + * @function is the name of the function from which __ntfs_error is being + * called. + * + * Note, you should be using debug.h::ntfs_error(@sb, @fmt, @...) instead + * as this provides the @function parameter automatically. + */ +void __ntfs_error(const char *function, struct super_block *sb, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + int flen = 0; + + if (function) + flen = strlen(function); + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; +#ifdef DEBUG + if (sb) + pr_err("(device %s): %s(): %pV\n", + sb->s_id, flen ? function : "", &vaf); + else + pr_err("%s(): %pV\n", flen ? function : "", &vaf); +#else + if (sb) + pr_err_ratelimited("(device %s): %s(): %pV\n", + sb->s_id, flen ? function : "", &vaf); + else + pr_err_ratelimited("%s(): %pV\n", flen ? function : "", &vaf); +#endif + va_end(args); + + if (sb) + ntfs_handle_error(sb); +} + +#ifdef DEBUG + +/* If 1, output debug messages, and if 0, don't. */ +int debug_msgs; + +void __ntfs_debug(const char *file, int line, const char *function, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + int flen = 0; + + if (!debug_msgs) + return; + if (function) + flen = strlen(function); + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + pr_debug("(%s, %d): %s(): %pV", file, line, flen ? function : "", &vaf); + va_end(args); +} + +/* Dump a runlist. Caller has to provide synchronisation for @rl. */ +void ntfs_debug_dump_runlist(const struct runlist_element *rl) +{ + int i; + const char *lcn_str[5] = { "LCN_DELALLOC ", "LCN_HOLE ", + "LCN_RL_NOT_MAPPED", "LCN_ENOENT ", + "LCN_unknown " }; + + if (!debug_msgs) + return; + pr_debug("Dumping runlist (values in hex):\n"); + if (!rl) { + pr_debug("Run list not present.\n"); + return; + } + pr_debug("VCN LCN Run length\n"); + for (i = 0; ; i++) { + s64 lcn = (rl + i)->lcn; + + if (lcn < 0) { + int index = -lcn - 1; + + if (index > -LCN_ENOENT - 1) + index = 3; + pr_debug("%-16Lx %s %-16Lx%s\n", + (long long)(rl + i)->vcn, lcn_str[index], + (long long)(rl + i)->length, + (rl + i)->length ? "" : + " (runlist end)"); + } else + pr_debug("%-16Lx %-16Lx %-16Lx%s\n", + (long long)(rl + i)->vcn, + (long long)(rl + i)->lcn, + (long long)(rl + i)->length, + (rl + i)->length ? "" : + " (runlist end)"); + if (!(rl + i)->length) + break; + } +} + +#endif diff --git a/fs/ntfs/debug.h b/fs/ntfs/debug.h new file mode 100644 index 000000000000..d8831d492d7d --- /dev/null +++ b/fs/ntfs/debug.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * NTFS kernel debug support. + * + * Copyright (c) 2001-2004 Anton Altaparmakov + */ + +#ifndef _LINUX_NTFS_DEBUG_H +#define _LINUX_NTFS_DEBUG_H + +#include + +#include "runlist.h" + +#ifdef DEBUG + +extern int debug_msgs; + +extern __printf(4, 5) +void __ntfs_debug(const char *file, int line, const char *function, + const char *format, ...); +/* + * ntfs_debug - write a debug level message to syslog + * @f: a printf format string containing the message + * @...: the variables to substitute into @f + * + * ntfs_debug() writes a DEBUG level message to the syslog but only if the + * driver was compiled with -DDEBUG. Otherwise, the call turns into a NOP. + */ +#define ntfs_debug(f, a...) \ + __ntfs_debug(__FILE__, __LINE__, __func__, f, ##a) + +void ntfs_debug_dump_runlist(const struct runlist_element *rl); + +#else /* !DEBUG */ + +#define ntfs_debug(fmt, ...) \ +do { \ + if (0) \ + no_printk(fmt, ##__VA_ARGS__); \ +} while (0) + +#define ntfs_debug_dump_runlist(rl) \ +do { \ + if (0) \ + (void)rl; \ +} while (0) + +#endif /* !DEBUG */ + +extern __printf(3, 4) +void __ntfs_warning(const char *function, const struct super_block *sb, + const char *fmt, ...); +#define ntfs_warning(sb, f, a...) __ntfs_warning(__func__, sb, f, ##a) + +extern __printf(3, 4) +void __ntfs_error(const char *function, struct super_block *sb, + const char *fmt, ...); +#define ntfs_error(sb, f, a...) __ntfs_error(__func__, sb, f, ##a) + +void ntfs_handle_error(struct super_block *sb); + +#endif /* _LINUX_NTFS_DEBUG_H */ diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c new file mode 100644 index 000000000000..20f5c7074bdd --- /dev/null +++ b/fs/ntfs/dir.c @@ -0,0 +1,1245 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel directory operations. + * + * Copyright (c) 2001-2007 Anton Altaparmakov + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#include + +#include "dir.h" +#include "mft.h" +#include "ntfs.h" +#include "index.h" +#include "reparse.h" + +#include + +/* + * The little endian Unicode string $I30 as a global constant. + */ +__le16 I30[5] = { cpu_to_le16('$'), cpu_to_le16('I'), + cpu_to_le16('3'), cpu_to_le16('0'), 0 }; + +/* + * ntfs_lookup_inode_by_name - find an inode in a directory given its name + * @dir_ni: ntfs inode of the directory in which to search for the name + * @uname: Unicode name for which to search in the directory + * @uname_len: length of the name @uname in Unicode characters + * @res: return the found file name if necessary (see below) + * + * Look for an inode with name @uname in the directory with inode @dir_ni. + * ntfs_lookup_inode_by_name() walks the contents of the directory looking for + * the Unicode name. If the name is found in the directory, the corresponding + * inode number (>= 0) is returned as a mft reference in cpu format, i.e. it + * is a 64-bit number containing the sequence number. + * + * On error, a negative value is returned corresponding to the error code. In + * particular if the inode is not found -ENOENT is returned. Note that you + * can't just check the return value for being negative, you have to check the + * inode number for being negative which you can extract using MREC(return + * value). + * + * Note, @uname_len does not include the (optional) terminating NULL character. + * + * Note, we look for a case sensitive match first but we also look for a case + * insensitive match at the same time. If we find a case insensitive match, we + * save that for the case that we don't find an exact match, where we return + * the case insensitive match and setup @res (which we allocate!) with the mft + * reference, the file name type, length and with a copy of the little endian + * Unicode file name itself. If we match a file name which is in the DOS name + * space, we only return the mft reference and file name type in @res. + * ntfs_lookup() then uses this to find the long file name in the inode itself. + * This is to avoid polluting the dcache with short file names. We want them to + * work but we don't care for how quickly one can access them. This also fixes + * the dcache aliasing issues. + * + * Locking: - Caller must hold i_mutex on the directory. + * - Each page cache page in the index allocation mapping must be + * locked whilst being accessed otherwise we may find a corrupt + * page due to it being under ->writepage at the moment which + * applies the mst protection fixups before writing out and then + * removes them again after the write is complete after which it + * unlocks the page. + */ +u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, + const int uname_len, struct ntfs_name **res) +{ + struct ntfs_volume *vol = dir_ni->vol; + struct super_block *sb = vol->sb; + struct inode *ia_vi = NULL; + struct mft_record *m; + struct index_root *ir; + struct index_entry *ie; + struct index_block *ia; + u8 *index_end; + u64 mref; + struct ntfs_attr_search_ctx *ctx; + int err, rc; + s64 vcn, old_vcn; + struct address_space *ia_mapping; + struct folio *folio; + u8 *kaddr = NULL; + struct ntfs_name *name = NULL; + + /* Get hold of the mft record for the directory. */ + m = map_mft_record(dir_ni); + if (IS_ERR(m)) { + ntfs_error(sb, "map_mft_record() failed with error code %ld.", + -PTR_ERR(m)); + return ERR_MREF(PTR_ERR(m)); + } + ctx = ntfs_attr_get_search_ctx(dir_ni, m); + if (unlikely(!ctx)) { + err = -ENOMEM; + goto err_out; + } + /* Find the index root attribute in the mft record. */ + err = ntfs_attr_lookup(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0, NULL, + 0, ctx); + if (unlikely(err)) { + if (err == -ENOENT) { + ntfs_error(sb, + "Index root attribute missing in directory inode 0x%llx.", + dir_ni->mft_no); + err = -EIO; + } + goto err_out; + } + /* Get to the index root value (it's been verified in read_inode). */ + ir = (struct index_root *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)); + index_end = (u8 *)&ir->index + le32_to_cpu(ir->index.index_length); + /* The first index entry. */ + ie = (struct index_entry *)((u8 *)&ir->index + + le32_to_cpu(ir->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (;; ie = (struct index_entry *)((u8 *)ie + le16_to_cpu(ie->length))) { + /* Bounds checks. */ + if ((u8 *)ie < (u8 *)ctx->mrec || + (u8 *)ie + sizeof(struct index_entry_header) > index_end || + (u8 *)ie + sizeof(struct index_entry_header) + le16_to_cpu(ie->key_length) > + index_end || (u8 *)ie + le16_to_cpu(ie->length) > index_end) + goto dir_err_out; + /* + * The last entry cannot contain a name. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ie->flags & INDEX_ENTRY_END) + break; + /* Key length should not be zero if it is not last entry. */ + if (!ie->key_length) + goto dir_err_out; + /* Check the consistency of an index entry */ + if (ntfs_index_entry_inconsistent(NULL, vol, ie, COLLATION_FILE_NAME, + dir_ni->mft_no)) + goto dir_err_out; + /* + * We perform a case sensitive comparison and if that matches + * we are done and return the mft reference of the inode (i.e. + * the inode number together with the sequence number for + * consistency checking). We convert it to cpu format before + * returning. + */ + if (ntfs_are_names_equal(uname, uname_len, + (__le16 *)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + CASE_SENSITIVE, vol->upcase, vol->upcase_len)) { +found_it: + /* + * We have a perfect match, so we don't need to care + * about having matched imperfectly before, so we can + * free name and set *res to NULL. + * However, if the perfect match is a short file name, + * we need to signal this through *res, so that + * ntfs_lookup() can fix dcache aliasing issues. + * As an optimization we just reuse an existing + * allocation of *res. + */ + if (ie->key.file_name.file_name_type == FILE_NAME_DOS) { + if (!name) { + name = kmalloc(sizeof(struct ntfs_name), + GFP_NOFS); + if (!name) { + err = -ENOMEM; + goto err_out; + } + } + name->mref = le64_to_cpu( + ie->data.dir.indexed_file); + name->type = FILE_NAME_DOS; + name->len = 0; + *res = name; + } else { + kfree(name); + *res = NULL; + } + mref = le64_to_cpu(ie->data.dir.indexed_file); + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(dir_ni); + return mref; + } + /* + * For a case insensitive mount, we also perform a case + * insensitive comparison (provided the file name is not in the + * POSIX namespace). If the comparison matches, and the name is + * in the WIN32 namespace, we cache the filename in *res so + * that the caller, ntfs_lookup(), can work on it. If the + * comparison matches, and the name is in the DOS namespace, we + * only cache the mft reference and the file name type (we set + * the name length to zero for simplicity). + */ + if ((!NVolCaseSensitive(vol) || + ie->key.file_name.file_name_type == FILE_NAME_DOS) && + ntfs_are_names_equal(uname, uname_len, + (__le16 *)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + IGNORE_CASE, vol->upcase, + vol->upcase_len)) { + int name_size = sizeof(struct ntfs_name); + u8 type = ie->key.file_name.file_name_type; + u8 len = ie->key.file_name.file_name_length; + + /* Only one case insensitive matching name allowed. */ + if (name) { + ntfs_error(sb, + "Found already allocated name in phase 1. Please run chkdsk"); + goto dir_err_out; + } + + if (type != FILE_NAME_DOS) + name_size += len * sizeof(__le16); + name = kmalloc(name_size, GFP_NOFS); + if (!name) { + err = -ENOMEM; + goto err_out; + } + name->mref = le64_to_cpu(ie->data.dir.indexed_file); + name->type = type; + if (type != FILE_NAME_DOS) { + name->len = len; + memcpy(name->name, ie->key.file_name.file_name, + len * sizeof(__le16)); + } else + name->len = 0; + *res = name; + } + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_collate_names(uname, uname_len, + (__le16 *)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, 1, + IGNORE_CASE, vol->upcase, vol->upcase_len); + /* + * If uname collates before the name of the current entry, there + * is definitely no such name in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + /* The names are not equal, continue the search. */ + if (rc) + continue; + /* + * Names match with case insensitive comparison, now try the + * case sensitive comparison, which is required for proper + * collation. + */ + rc = ntfs_collate_names(uname, uname_len, + (__le16 *)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, 1, + CASE_SENSITIVE, vol->upcase, vol->upcase_len); + if (rc == -1) + break; + if (rc) + continue; + /* + * Perfect match, this will never happen as the + * ntfs_are_names_equal() call will have gotten a match but we + * still treat it correctly. + */ + goto found_it; + } + /* + * We have finished with this index without success. Check for the + * presence of a child node and if not present return -ENOENT, unless + * we have got a matching name cached in name in which case return the + * mft reference associated with it. + */ + if (!(ie->flags & INDEX_ENTRY_NODE)) { + if (name) { + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(dir_ni); + return name->mref; + } + ntfs_debug("Entry not found."); + err = -ENOENT; + goto err_out; + } /* Child node present, descend into it. */ + + /* Get the starting vcn of the index_block holding the child node. */ + vcn = le64_to_cpup((__le64 *)((u8 *)ie + le16_to_cpu(ie->length) - 8)); + + /* + * We are done with the index root and the mft record. Release them, + * otherwise we deadlock with read_mapping_folio(). + */ + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(dir_ni); + m = NULL; + ctx = NULL; + + ia_vi = ntfs_index_iget(VFS_I(dir_ni), I30, 4); + if (IS_ERR(ia_vi)) { + err = PTR_ERR(ia_vi); + goto err_out; + } + + ia_mapping = ia_vi->i_mapping; +descend_into_child_node: + /* + * Convert vcn to index into the index allocation attribute in units + * of PAGE_SIZE and map the page cache page, reading it from + * disk if necessary. + */ + folio = read_mapping_folio(ia_mapping, vcn << + dir_ni->itype.index.vcn_size_bits >> PAGE_SHIFT, NULL); + if (IS_ERR(folio)) { + ntfs_error(sb, "Failed to map directory index page, error %ld.", + -PTR_ERR(folio)); + err = PTR_ERR(folio); + goto err_out; + } + + folio_lock(folio); + kaddr = kmalloc(PAGE_SIZE, GFP_NOFS); + if (!kaddr) { + err = -ENOMEM; + folio_unlock(folio); + folio_put(folio); + goto unm_err_out; + } + + memcpy_from_folio(kaddr, folio, 0, PAGE_SIZE); + post_read_mst_fixup((struct ntfs_record *)kaddr, PAGE_SIZE); + folio_unlock(folio); + folio_put(folio); +fast_descend_into_child_node: + /* Get to the index allocation block. */ + ia = (struct index_block *)(kaddr + ((vcn << + dir_ni->itype.index.vcn_size_bits) & ~PAGE_MASK)); + /* Bounds checks. */ + if ((u8 *)ia < kaddr || (u8 *)ia > kaddr + PAGE_SIZE) { + ntfs_error(sb, + "Out of bounds check failed. Corrupt directory inode 0x%llx or driver bug.", + dir_ni->mft_no); + goto unm_err_out; + } + /* Catch multi sector transfer fixup errors. */ + if (unlikely(!ntfs_is_indx_record(ia->magic))) { + ntfs_error(sb, + "Directory index record with vcn 0x%llx is corrupt. Corrupt inode 0x%llx. Run chkdsk.", + vcn, dir_ni->mft_no); + goto unm_err_out; + } + if (le64_to_cpu(ia->index_block_vcn) != vcn) { + ntfs_error(sb, + "Actual VCN (0x%llx) of index buffer is different from expected VCN (0x%llx). Directory inode 0x%llx is corrupt or driver bug.", + le64_to_cpu(ia->index_block_vcn), + vcn, dir_ni->mft_no); + goto unm_err_out; + } + if (le32_to_cpu(ia->index.allocated_size) + 0x18 != + dir_ni->itype.index.block_size) { + ntfs_error(sb, + "Index buffer (VCN 0x%llx) of directory inode 0x%llx has a size (%u) differing from the directory specified size (%u). Directory inode is corrupt or driver bug.", + vcn, dir_ni->mft_no, + le32_to_cpu(ia->index.allocated_size) + 0x18, + dir_ni->itype.index.block_size); + goto unm_err_out; + } + index_end = (u8 *)ia + dir_ni->itype.index.block_size; + if (index_end > kaddr + PAGE_SIZE) { + ntfs_error(sb, + "Index buffer (VCN 0x%llx) of directory inode 0x%llx crosses page boundary. Impossible! Cannot access! This is probably a bug in the driver.", + vcn, dir_ni->mft_no); + goto unm_err_out; + } + index_end = (u8 *)&ia->index + le32_to_cpu(ia->index.index_length); + if (index_end > (u8 *)ia + dir_ni->itype.index.block_size) { + ntfs_error(sb, + "Size of index buffer (VCN 0x%llx) of directory inode 0x%llx exceeds maximum size.", + vcn, dir_ni->mft_no); + goto unm_err_out; + } + /* The first index entry. */ + ie = (struct index_entry *)((u8 *)&ia->index + + le32_to_cpu(ia->index.entries_offset)); + /* + * Iterate similar to above big loop but applied to index buffer, thus + * loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (;; ie = (struct index_entry *)((u8 *)ie + le16_to_cpu(ie->length))) { + /* Bounds checks. */ + if ((u8 *)ie < (u8 *)ia || + (u8 *)ie + sizeof(struct index_entry_header) > index_end || + (u8 *)ie + sizeof(struct index_entry_header) + le16_to_cpu(ie->key_length) > + index_end || (u8 *)ie + le16_to_cpu(ie->length) > index_end) { + ntfs_error(sb, "Index entry out of bounds in directory inode 0x%llx.", + dir_ni->mft_no); + goto unm_err_out; + } + /* + * The last entry cannot contain a name. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ie->flags & INDEX_ENTRY_END) + break; + /* Key length should not be zero if it is not last entry. */ + if (!ie->key_length) + goto unm_err_out; + /* Check the consistency of an index entry */ + if (ntfs_index_entry_inconsistent(NULL, vol, ie, COLLATION_FILE_NAME, + dir_ni->mft_no)) + goto unm_err_out; + /* + * We perform a case sensitive comparison and if that matches + * we are done and return the mft reference of the inode (i.e. + * the inode number together with the sequence number for + * consistency checking). We convert it to cpu format before + * returning. + */ + if (ntfs_are_names_equal(uname, uname_len, + (__le16 *)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + CASE_SENSITIVE, vol->upcase, vol->upcase_len)) { +found_it2: + /* + * We have a perfect match, so we don't need to care + * about having matched imperfectly before, so we can + * free name and set *res to NULL. + * However, if the perfect match is a short file name, + * we need to signal this through *res, so that + * ntfs_lookup() can fix dcache aliasing issues. + * As an optimization we just reuse an existing + * allocation of *res. + */ + if (ie->key.file_name.file_name_type == FILE_NAME_DOS) { + if (!name) { + name = kmalloc(sizeof(struct ntfs_name), + GFP_NOFS); + if (!name) { + err = -ENOMEM; + goto unm_err_out; + } + } + name->mref = le64_to_cpu( + ie->data.dir.indexed_file); + name->type = FILE_NAME_DOS; + name->len = 0; + *res = name; + } else { + kfree(name); + *res = NULL; + } + mref = le64_to_cpu(ie->data.dir.indexed_file); + kfree(kaddr); + iput(ia_vi); + return mref; + } + /* + * For a case insensitive mount, we also perform a case + * insensitive comparison (provided the file name is not in the + * POSIX namespace). If the comparison matches, and the name is + * in the WIN32 namespace, we cache the filename in *res so + * that the caller, ntfs_lookup(), can work on it. If the + * comparison matches, and the name is in the DOS namespace, we + * only cache the mft reference and the file name type (we set + * the name length to zero for simplicity). + */ + if ((!NVolCaseSensitive(vol) || + ie->key.file_name.file_name_type == FILE_NAME_DOS) && + ntfs_are_names_equal(uname, uname_len, + (__le16 *)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + IGNORE_CASE, vol->upcase, + vol->upcase_len)) { + int name_size = sizeof(struct ntfs_name); + u8 type = ie->key.file_name.file_name_type; + u8 len = ie->key.file_name.file_name_length; + + /* Only one case insensitive matching name allowed. */ + if (name) { + ntfs_error(sb, + "Found already allocated name in phase 2. Please run chkdsk"); + kfree(kaddr); + goto dir_err_out; + } + + if (type != FILE_NAME_DOS) + name_size += len * sizeof(__le16); + name = kmalloc(name_size, GFP_NOFS); + if (!name) { + err = -ENOMEM; + goto unm_err_out; + } + name->mref = le64_to_cpu(ie->data.dir.indexed_file); + name->type = type; + if (type != FILE_NAME_DOS) { + name->len = len; + memcpy(name->name, ie->key.file_name.file_name, + len * sizeof(__le16)); + } else + name->len = 0; + *res = name; + } + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_collate_names(uname, uname_len, + (__le16 *)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, 1, + IGNORE_CASE, vol->upcase, vol->upcase_len); + /* + * If uname collates before the name of the current entry, there + * is definitely no such name in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + /* The names are not equal, continue the search. */ + if (rc) + continue; + /* + * Names match with case insensitive comparison, now try the + * case sensitive comparison, which is required for proper + * collation. + */ + rc = ntfs_collate_names(uname, uname_len, + (__le16 *)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, 1, + CASE_SENSITIVE, vol->upcase, vol->upcase_len); + if (rc == -1) + break; + if (rc) + continue; + /* + * Perfect match, this will never happen as the + * ntfs_are_names_equal() call will have gotten a match but we + * still treat it correctly. + */ + goto found_it2; + } + /* + * We have finished with this index buffer without success. Check for + * the presence of a child node. + */ + if (ie->flags & INDEX_ENTRY_NODE) { + if ((ia->index.flags & NODE_MASK) == LEAF_NODE) { + ntfs_error(sb, + "Index entry with child node found in a leaf node in directory inode 0x%llx.", + dir_ni->mft_no); + goto unm_err_out; + } + /* Child node present, descend into it. */ + old_vcn = vcn; + vcn = le64_to_cpup((__le64 *)((u8 *)ie + + le16_to_cpu(ie->length) - 8)); + if (vcn >= 0) { + /* + * If vcn is in the same page cache page as old_vcn we + * recycle the mapped page. + */ + if (ntfs_cluster_to_pidx(vol, old_vcn) == + ntfs_cluster_to_pidx(vol, vcn)) + goto fast_descend_into_child_node; + kfree(kaddr); + kaddr = NULL; + goto descend_into_child_node; + } + ntfs_error(sb, "Negative child node vcn in directory inode 0x%llx.", + dir_ni->mft_no); + goto unm_err_out; + } + /* + * No child node present, return -ENOENT, unless we have got a matching + * name cached in name in which case return the mft reference + * associated with it. + */ + if (name) { + kfree(kaddr); + iput(ia_vi); + return name->mref; + } + ntfs_debug("Entry not found."); + err = -ENOENT; +unm_err_out: + kfree(kaddr); +err_out: + if (!err) + err = -EIO; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (m) + unmap_mft_record(dir_ni); + kfree(name); + *res = NULL; + if (!IS_ERR_OR_NULL(ia_vi)) + iput(ia_vi); + return ERR_MREF(err); +dir_err_out: + ntfs_error(sb, "Corrupt directory. Aborting lookup."); + goto err_out; +} + +/* + * ntfs_filldir - ntfs specific filldir method + * @vol: current ntfs volume + * @ndir: ntfs inode of current directory + * @ia_page: page in which the index allocation buffer @ie is in resides + * @ie: current index entry + * @name: buffer to use for the converted name + * @actor: what to feed the entries to + * + * Convert the Unicode @name to the loaded NLS and pass it to the @filldir + * callback. + * + * If @ia_page is not NULL it is the locked page containing the index + * allocation block containing the index entry @ie. + * + * Note, we drop (and then reacquire) the page lock on @ia_page across the + * @filldir() call otherwise we would deadlock with NFSd when it calls ->lookup + * since ntfs_lookup() will lock the same page. As an optimization, we do not + * retake the lock if we are returning a non-zero value as ntfs_readdir() + * would need to drop the lock immediately anyway. + */ +static inline int ntfs_filldir(struct ntfs_volume *vol, + struct ntfs_inode *ndir, struct page *ia_page, struct index_entry *ie, + u8 *name, struct dir_context *actor) +{ + unsigned long mref; + int name_len; + unsigned int dt_type; + u8 name_type; + + name_type = ie->key.file_name.file_name_type; + if (name_type == FILE_NAME_DOS) { + ntfs_debug("Skipping DOS name space entry."); + return 0; + } + if (MREF_LE(ie->data.dir.indexed_file) == FILE_root) { + ntfs_debug("Skipping root directory self reference entry."); + return 0; + } + if (MREF_LE(ie->data.dir.indexed_file) < FILE_first_user && + !NVolShowSystemFiles(vol)) { + ntfs_debug("Skipping system file."); + return 0; + } + if (!NVolShowHiddenFiles(vol) && + (ie->key.file_name.file_attributes & FILE_ATTR_HIDDEN)) { + ntfs_debug("Skipping hidden file."); + return 0; + } + + name_len = ntfs_ucstonls(vol, (__le16 *)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, &name, + NTFS_MAX_NAME_LEN * NLS_MAX_CHARSET_SIZE + 1); + if (name_len <= 0) { + ntfs_warning(vol->sb, "Skipping unrepresentable inode 0x%llx.", + (long long)MREF_LE(ie->data.dir.indexed_file)); + return 0; + } + + mref = MREF_LE(ie->data.dir.indexed_file); + if (ie->key.file_name.file_attributes & + FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT) + dt_type = DT_DIR; + else if (ie->key.file_name.file_attributes & FILE_ATTR_REPARSE_POINT) + dt_type = ntfs_reparse_tag_dt_types(vol, mref); + else + dt_type = DT_REG; + + /* + * Drop the page lock otherwise we deadlock with NFS when it calls + * ->lookup since ntfs_lookup() will lock the same page. + */ + if (ia_page) + unlock_page(ia_page); + ntfs_debug("Calling filldir for %s with len %i, fpos 0x%llx, inode 0x%lx, DT_%s.", + name, name_len, actor->pos, mref, dt_type == DT_DIR ? "DIR" : "REG"); + if (!dir_emit(actor, name, name_len, mref, dt_type)) + return 1; + /* Relock the page but not if we are aborting ->readdir. */ + if (ia_page) + lock_page(ia_page); + return 0; +} + +struct ntfs_file_private { + void *key; + __le16 key_length; + bool end_in_iterate; + loff_t curr_pos; +}; + +struct ntfs_index_ra { + unsigned long start_index; + unsigned int count; + struct rb_node rb_node; +}; + +static void ntfs_insert_rb(struct ntfs_index_ra *nir, struct rb_root *root) +{ + struct rb_node **new = &root->rb_node, *parent = NULL; + struct ntfs_index_ra *cnir; + + while (*new) { + parent = *new; + cnir = rb_entry(parent, struct ntfs_index_ra, rb_node); + if (nir->start_index < cnir->start_index) + new = &parent->rb_left; + else if (nir->start_index >= cnir->start_index + cnir->count) + new = &parent->rb_right; + else { + pr_err("nir start index : %ld, count : %d, cnir start_index : %ld, count : %d\n", + nir->start_index, nir->count, cnir->start_index, cnir->count); + return; + } + } + + rb_link_node(&nir->rb_node, parent, new); + rb_insert_color(&nir->rb_node, root); +} + +static int ntfs_ia_blocks_readahead(struct ntfs_inode *ia_ni, loff_t pos) +{ + unsigned long dir_start_index, dir_end_index; + struct inode *ia_vi = VFS_I(ia_ni); + struct file_ra_state *dir_ra; + + dir_end_index = (i_size_read(ia_vi) + PAGE_SIZE - 1) >> PAGE_SHIFT; + dir_start_index = (pos + PAGE_SIZE - 1) >> PAGE_SHIFT; + + if (dir_start_index >= dir_end_index) + return 0; + + dir_ra = kzalloc(sizeof(*dir_ra), GFP_NOFS); + if (!dir_ra) + return -ENOMEM; + + file_ra_state_init(dir_ra, ia_vi->i_mapping); + dir_end_index = (i_size_read(ia_vi) + PAGE_SIZE - 1) >> PAGE_SHIFT; + dir_start_index = (pos + PAGE_SIZE - 1) >> PAGE_SHIFT; + dir_ra->ra_pages = dir_end_index - dir_start_index; + page_cache_sync_readahead(ia_vi->i_mapping, dir_ra, NULL, + dir_start_index, dir_end_index - dir_start_index); + kfree(dir_ra); + + return 0; +} + +static int ntfs_readdir(struct file *file, struct dir_context *actor) +{ + struct inode *vdir = file_inode(file); + struct super_block *sb = vdir->i_sb; + struct ntfs_inode *ndir = NTFS_I(vdir); + struct ntfs_volume *vol = NTFS_SB(sb); + struct ntfs_attr_search_ctx *ctx = NULL; + struct ntfs_index_context *ictx = NULL; + u8 *name; + struct index_root *ir; + struct index_entry *next = NULL; + struct ntfs_file_private *private = NULL; + int err = 0; + loff_t ie_pos = 2; /* initialize it with dot and dotdot size */ + struct ntfs_index_ra *nir = NULL; + unsigned long index; + struct rb_root ra_root = RB_ROOT; + struct file_ra_state *ra; + + ntfs_debug("Entering for inode 0x%llx, fpos 0x%llx.", + ndir->mft_no, actor->pos); + + if (file->private_data) { + private = file->private_data; + + if (actor->pos != private->curr_pos) { + /* + * If actor->pos is different from the previous passed + * one, Discard the private->key and fill dirent buffer + * with linear lookup. + */ + kfree(private->key); + private->key = NULL; + private->end_in_iterate = false; + } else if (private->end_in_iterate) { + kfree(private->key); + kfree(file->private_data); + file->private_data = NULL; + return 0; + } + } + + /* Emulate . and .. for all directories. */ + if (!dir_emit_dots(file, actor)) + return 0; + + /* + * Allocate a buffer to store the current name being processed + * converted to format determined by current NLS. + */ + name = kmalloc(NTFS_MAX_NAME_LEN * NLS_MAX_CHARSET_SIZE + 1, GFP_NOFS); + if (unlikely(!name)) + return -ENOMEM; + + mutex_lock_nested(&ndir->mrec_lock, NTFS_INODE_MUTEX_PARENT); + ictx = ntfs_index_ctx_get(ndir, I30, 4); + if (!ictx) { + kfree(name); + mutex_unlock(&ndir->mrec_lock); + return -ENOMEM; + } + + ra = kzalloc(sizeof(struct file_ra_state), GFP_NOFS); + if (!ra) { + kfree(name); + ntfs_index_ctx_put(ictx); + mutex_unlock(&ndir->mrec_lock); + return -ENOMEM; + } + file_ra_state_init(ra, vol->mft_ino->i_mapping); + + if (private && private->key) { + /* + * Find index witk private->key using ntfs_index_lookup() + * instead of linear index lookup. + */ + err = ntfs_index_lookup(private->key, + le16_to_cpu(private->key_length), + ictx); + if (!err) { + next = ictx->entry; + /* + * Update ie_pos with private->curr_pos + * to make next d_off of dirent correct. + */ + ie_pos = private->curr_pos; + + if (actor->pos > vol->mft_record_size && ictx->ia_ni) { + err = ntfs_ia_blocks_readahead(ictx->ia_ni, actor->pos); + if (err) + goto out; + } + + goto nextdir; + } else { + goto out; + } + } else if (!private) { + private = kzalloc(sizeof(struct ntfs_file_private), GFP_KERNEL); + if (!private) { + err = -ENOMEM; + goto out; + } + file->private_data = private; + } + + ctx = ntfs_attr_get_search_ctx(ndir, NULL); + if (!ctx) { + err = -ENOMEM; + goto out; + } + + /* Find the index root attribute in the mft record. */ + if (ntfs_attr_lookup(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0, NULL, 0, + ctx)) { + ntfs_error(sb, "Index root attribute missing in directory inode %llu", + ndir->mft_no); + ntfs_attr_put_search_ctx(ctx); + err = -ENOMEM; + goto out; + } + + /* Get to the index root value. */ + ir = (struct index_root *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)); + + ictx->ir = ir; + ictx->actx = ctx; + ictx->parent_vcn[ictx->pindex] = VCN_INDEX_ROOT_PARENT; + ictx->is_in_root = true; + ictx->parent_pos[ictx->pindex] = 0; + + ictx->block_size = le32_to_cpu(ir->index_block_size); + if (ictx->block_size < NTFS_BLOCK_SIZE) { + ntfs_error(sb, "Index block size (%d) is smaller than the sector size (%d)", + ictx->block_size, NTFS_BLOCK_SIZE); + err = -EIO; + goto out; + } + + if (vol->cluster_size <= ictx->block_size) + ictx->vcn_size_bits = vol->cluster_size_bits; + else + ictx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS; + + /* The first index entry. */ + next = (struct index_entry *)((u8 *)&ir->index + + le32_to_cpu(ir->index.entries_offset)); + + if (next->flags & INDEX_ENTRY_NODE) { + ictx->ia_ni = ntfs_ia_open(ictx, ictx->idx_ni); + if (!ictx->ia_ni) { + err = -EINVAL; + goto out; + } + + err = ntfs_ia_blocks_readahead(ictx->ia_ni, actor->pos); + if (err) + goto out; + } + + if (next->flags & INDEX_ENTRY_NODE) { + next = ntfs_index_walk_down(next, ictx); + if (IS_ERR(next)) { + err = PTR_ERR(next); + goto out; + } + } + + if (next && !(next->flags & INDEX_ENTRY_END)) + goto nextdir; + + while (1) { + next = ntfs_index_next(next, ictx); + if (IS_ERR(next)) { + err = PTR_ERR(next); + goto out; + } + if (!next) + break; +nextdir: + /* Check the consistency of an index entry */ + if (ntfs_index_entry_inconsistent(ictx, vol, next, COLLATION_FILE_NAME, + ndir->mft_no)) { + err = -EIO; + goto out; + } + + if (ie_pos < actor->pos) { + ie_pos += le16_to_cpu(next->length); + continue; + } + + actor->pos = ie_pos; + + index = ntfs_mft_no_to_pidx(vol, + MREF_LE(next->data.dir.indexed_file)); + if (nir) { + struct ntfs_index_ra *cnir; + struct rb_node *node = ra_root.rb_node; + + if (nir->start_index <= index && + index < nir->start_index + nir->count) { + /* No behavior */ + goto filldir; + } + + while (node) { + cnir = rb_entry(node, struct ntfs_index_ra, rb_node); + if (cnir->start_index <= index && + index < cnir->start_index + cnir->count) { + goto filldir; + } else if (cnir->start_index + cnir->count == index) { + cnir->count++; + goto filldir; + } else if (!cnir->start_index && cnir->start_index - 1 == index) { + cnir->start_index = index; + goto filldir; + } + + if (index < cnir->start_index) + node = node->rb_left; + else if (index >= cnir->start_index + cnir->count) + node = node->rb_right; + } + + if (nir->start_index + nir->count == index) { + nir->count++; + } else if (!nir->start_index && nir->start_index - 1 == index) { + nir->start_index = index; + } else if (nir->count > 2) { + ntfs_insert_rb(nir, &ra_root); + nir = NULL; + } else { + nir->start_index = index; + nir->count = 1; + } + } + + if (!nir) { + nir = kzalloc(sizeof(struct ntfs_index_ra), GFP_KERNEL); + if (nir) { + nir->start_index = index; + nir->count = 1; + } + } + +filldir: + /* Submit the name to the filldir callback. */ + err = ntfs_filldir(vol, ndir, NULL, next, name, actor); + if (err) { + /* + * Store index key value to file private_data to start + * from current index offset on next round. + */ + private = file->private_data; + kfree(private->key); + private->key = kmalloc(le16_to_cpu(next->key_length), GFP_KERNEL); + if (!private->key) { + err = -ENOMEM; + goto out; + } + + memcpy(private->key, &next->key.file_name, le16_to_cpu(next->key_length)); + private->key_length = next->key_length; + break; + } + ie_pos += le16_to_cpu(next->length); + } + + if (!err) + private->end_in_iterate = true; + else + err = 0; + + private->curr_pos = actor->pos = ie_pos; +out: + while (!RB_EMPTY_ROOT(&ra_root)) { + struct ntfs_index_ra *cnir; + struct rb_node *node; + + node = rb_first(&ra_root); + cnir = rb_entry(node, struct ntfs_index_ra, rb_node); + ra->ra_pages = cnir->count; + page_cache_sync_readahead(vol->mft_ino->i_mapping, ra, NULL, + cnir->start_index, cnir->count); + rb_erase(node, &ra_root); + kfree(cnir); + } + + if (err) { + if (private) { + private->curr_pos = actor->pos; + private->end_in_iterate = true; + } + err = 0; + } + ntfs_index_ctx_put(ictx); + kfree(name); + kfree(nir); + kfree(ra); + mutex_unlock(&ndir->mrec_lock); + return err; +} + +int ntfs_check_empty_dir(struct ntfs_inode *ni, struct mft_record *ni_mrec) +{ + struct ntfs_attr_search_ctx *ctx; + int ret = 0; + + if (!(ni_mrec->flags & MFT_RECORD_IS_DIRECTORY)) + return 0; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_error(ni->vol->sb, "Failed to get search context"); + return -ENOMEM; + } + + /* Find the index root attribute in the mft record. */ + ret = ntfs_attr_lookup(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0, NULL, + 0, ctx); + if (ret) { + ntfs_error(ni->vol->sb, "Index root attribute missing in directory inode %llu", + ni->mft_no); + ntfs_attr_put_search_ctx(ctx); + return ret; + } + + /* Non-empty directory? */ + if (le32_to_cpu(ctx->attr->data.resident.value_length) != + sizeof(struct index_root) + sizeof(struct index_entry_header)) { + /* Both ENOTEMPTY and EEXIST are ok. We use the more common. */ + ret = -ENOTEMPTY; + ntfs_debug("Directory is not empty\n"); + } + + ntfs_attr_put_search_ctx(ctx); + + return ret; +} + +/* + * ntfs_dir_open - called when an inode is about to be opened + * @vi: inode to be opened + * @filp: file structure describing the inode + * + * Limit directory size to the page cache limit on architectures where unsigned + * long is 32-bits. This is the most we can do for now without overflowing the + * page cache page index. Doing it this way means we don't run into problems + * because of existing too large directories. It would be better to allow the + * user to read the accessible part of the directory but I doubt very much + * anyone is going to hit this check on a 32-bit architecture, so there is no + * point in adding the extra complexity required to support this. + * + * On 64-bit architectures, the check is hopefully optimized away by the + * compiler. + */ +static int ntfs_dir_open(struct inode *vi, struct file *filp) +{ + if (sizeof(unsigned long) < 8) { + if (i_size_read(vi) > MAX_LFS_FILESIZE) + return -EFBIG; + } + return 0; +} + +static int ntfs_dir_release(struct inode *vi, struct file *filp) +{ + if (filp->private_data) { + kfree(((struct ntfs_file_private *)filp->private_data)->key); + kfree(filp->private_data); + filp->private_data = NULL; + } + return 0; +} + +/* + * ntfs_dir_fsync - sync a directory to disk + * @filp: file describing the directory to be synced + * @start: start offset to be synced + * @end: end offset to be synced + * @datasync: if non-zero only flush user data and not metadata + * + * Data integrity sync of a directory to disk. Used for fsync, fdatasync, and + * msync system calls. This function is based on file.c::ntfs_file_fsync(). + * + * Write the mft record and all associated extent mft records as well as the + * $INDEX_ALLOCATION and $BITMAP attributes and then sync the block device. + * + * If @datasync is true, we do not wait on the inode(s) to be written out + * but we always wait on the page cache pages to be written out. + * + * Note: In the past @filp could be NULL so we ignore it as we don't need it + * anyway. + * + * Locking: Caller must hold i_mutex on the inode. + */ +static int ntfs_dir_fsync(struct file *filp, loff_t start, loff_t end, + int datasync) +{ + struct inode *bmp_vi, *vi = filp->f_mapping->host; + struct ntfs_volume *vol = NTFS_I(vi)->vol; + struct ntfs_inode *ni = NTFS_I(vi); + struct ntfs_attr_search_ctx *ctx; + struct inode *parent_vi, *ia_vi; + int err, ret; + struct ntfs_attr na; + + ntfs_debug("Entering for inode 0x%llx.", ni->mft_no); + + if (NVolShutdown(vol)) + return -EIO; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -ENOMEM; + + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL_CHILD); + while (!(err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx))) { + struct file_name_attr *fn = (struct file_name_attr *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)); + + if (MREF_LE(fn->parent_directory) == ni->mft_no) + continue; + + parent_vi = ntfs_iget(vi->i_sb, MREF_LE(fn->parent_directory)); + if (IS_ERR(parent_vi)) + continue; + mutex_lock_nested(&NTFS_I(parent_vi)->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + ia_vi = ntfs_index_iget(parent_vi, I30, 4); + mutex_unlock(&NTFS_I(parent_vi)->mrec_lock); + if (IS_ERR(ia_vi)) { + iput(parent_vi); + continue; + } + write_inode_now(ia_vi, 1); + iput(ia_vi); + write_inode_now(parent_vi, 1); + iput(parent_vi); + } + mutex_unlock(&ni->mrec_lock); + ntfs_attr_put_search_ctx(ctx); + + err = file_write_and_wait_range(filp, start, end); + if (err) + return err; + inode_lock(vi); + + /* If the bitmap attribute inode is in memory sync it, too. */ + na.mft_no = vi->i_ino; + na.type = AT_BITMAP; + na.name = I30; + na.name_len = 4; + bmp_vi = ilookup5(vi->i_sb, vi->i_ino, ntfs_test_inode, &na); + if (bmp_vi) { + write_inode_now(bmp_vi, !datasync); + iput(bmp_vi); + } + ret = __ntfs_write_inode(vi, 1); + + write_inode_now(vi, !datasync); + + write_inode_now(vol->mftbmp_ino, 1); + down_write(&vol->lcnbmp_lock); + write_inode_now(vol->lcnbmp_ino, 1); + up_write(&vol->lcnbmp_lock); + write_inode_now(vol->mft_ino, 1); + + err = sync_blockdev(vi->i_sb->s_bdev); + if (unlikely(err && !ret)) + ret = err; + if (likely(!ret)) + ntfs_debug("Done."); + else + ntfs_warning(vi->i_sb, + "Failed to f%ssync inode 0x%llx. Error %u.", + datasync ? "data" : "", ni->mft_no, -ret); + inode_unlock(vi); + return ret; +} + +const struct file_operations ntfs_dir_ops = { + .llseek = generic_file_llseek, /* Seek inside directory. */ + .read = generic_read_dir, /* Return -EISDIR. */ + .iterate_shared = ntfs_readdir, /* Read directory contents. */ + .fsync = ntfs_dir_fsync, /* Sync a directory to disk. */ + .open = ntfs_dir_open, /* Open directory. */ + .release = ntfs_dir_release, + .unlocked_ioctl = ntfs_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ntfs_compat_ioctl, +#endif + .setlease = generic_setlease, +}; diff --git a/fs/ntfs/dir.h b/fs/ntfs/dir.h new file mode 100644 index 000000000000..a38c6b071678 --- /dev/null +++ b/fs/ntfs/dir.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for directory handling in NTFS Linux kernel driver. + * + * Copyright (c) 2002-2004 Anton Altaparmakov + */ + +#ifndef _LINUX_NTFS_DIR_H +#define _LINUX_NTFS_DIR_H + +#include "inode.h" + +/* + * ntfs_name is used to return the file name to the caller of + * ntfs_lookup_inode_by_name() in order for the caller (namei.c::ntfs_lookup()) + * to be able to deal with dcache aliasing issues. + */ +struct ntfs_name { + u64 mref; + u8 type; + u8 len; + __le16 name[]; +} __packed; + +/* The little endian Unicode string $I30 as a global constant. */ +extern __le16 I30[5]; + +u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, + const __le16 *uname, const int uname_len, struct ntfs_name **res); +int ntfs_check_empty_dir(struct ntfs_inode *ni, struct mft_record *ni_mrec); + +#endif /* _LINUX_NTFS_FS_DIR_H */ diff --git a/fs/ntfs/ea.c b/fs/ntfs/ea.c new file mode 100644 index 000000000000..c4a4a3e3e599 --- /dev/null +++ b/fs/ntfs/ea.c @@ -0,0 +1,957 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Pocessing of EA's + * + * Part of this file is based on code from the NTFS-3G. + * + * Copyright (c) 2014-2021 Jean-Pierre Andre + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "layout.h" +#include "attrib.h" +#include "index.h" +#include "dir.h" +#include "ea.h" + +static int ntfs_write_ea(struct ntfs_inode *ni, __le32 type, char *value, s64 ea_off, + s64 ea_size, bool need_truncate) +{ + struct inode *ea_vi; + int err = 0; + s64 written; + + ea_vi = ntfs_attr_iget(VFS_I(ni), type, AT_UNNAMED, 0); + if (IS_ERR(ea_vi)) + return PTR_ERR(ea_vi); + + written = ntfs_inode_attr_pwrite(ea_vi, ea_off, ea_size, value, false); + if (written != ea_size) + err = -EIO; + else { + struct ntfs_inode *ea_ni = NTFS_I(ea_vi); + + if (need_truncate && ea_ni->data_size > ea_off + ea_size) + ntfs_attr_truncate(ea_ni, ea_off + ea_size); + mark_mft_record_dirty(ni); + } + + iput(ea_vi); + return err; +} + +static int ntfs_ea_lookup(char *ea_buf, s64 ea_buf_size, const char *name, + int name_len, s64 *ea_offset, s64 *ea_size) +{ + const struct ea_attr *p_ea; + size_t actual_size; + loff_t offset, p_ea_size; + unsigned int next; + + if (ea_buf_size < sizeof(struct ea_attr)) + goto out; + + offset = 0; + do { + p_ea = (const struct ea_attr *)&ea_buf[offset]; + next = le32_to_cpu(p_ea->next_entry_offset); + p_ea_size = next ? next : (ea_buf_size - offset); + + if (p_ea_size < sizeof(struct ea_attr) || + offset + p_ea_size > ea_buf_size) + break; + + if ((s64)p_ea->ea_name_length + 1 > + p_ea_size - offsetof(struct ea_attr, ea_name)) + break; + + actual_size = ALIGN(struct_size(p_ea, ea_name, 1 + p_ea->ea_name_length + + le16_to_cpu(p_ea->ea_value_length)), 4); + if (actual_size > p_ea_size) + break; + + if (p_ea->ea_name_length == name_len && + !memcmp(p_ea->ea_name, name, name_len)) { + *ea_offset = offset; + *ea_size = next ? next : actual_size; + + if (ea_buf_size < *ea_offset + *ea_size) + goto out; + + return 0; + } + offset += next; + } while (next > 0 && offset < ea_buf_size); + +out: + return -ENOENT; +} + +/* + * Return the existing EA + * + * The EA_INFORMATION is not examined and the consistency of the + * existing EA is not checked. + * + * If successful, the full attribute is returned unchanged + * and its size is returned. + * If the designated buffer is too small, the needed size is + * returned, and the buffer is left unchanged. + * If there is an error, a negative value is returned and errno + * is set according to the error. + */ +static int ntfs_get_ea(struct inode *inode, const char *name, size_t name_len, + void *buffer, size_t size) +{ + struct ntfs_inode *ni = NTFS_I(inode); + const struct ea_attr *p_ea; + char *ea_buf; + s64 ea_off, ea_size, all_ea_size, ea_info_size; + int err; + u32 ea_info_qlen; + u16 ea_value_len; + struct ea_information *p_ea_info; + + if (!NInoHasEA(ni)) + return -ENODATA; + + p_ea_info = ntfs_attr_readall(ni, AT_EA_INFORMATION, NULL, 0, + &ea_info_size); + if (!p_ea_info || ea_info_size != sizeof(struct ea_information)) { + kvfree(p_ea_info); + return -ENODATA; + } + + ea_info_qlen = le32_to_cpu(p_ea_info->ea_query_length); + kvfree(p_ea_info); + + ea_buf = ntfs_attr_readall(ni, AT_EA, NULL, 0, &all_ea_size); + if (!ea_buf) + return -ENODATA; + + if (ea_info_qlen > all_ea_size) { + err = -EIO; + goto free_ea_buf; + } + + err = ntfs_ea_lookup(ea_buf, ea_info_qlen, name, name_len, &ea_off, + &ea_size); + if (!err) { + p_ea = (struct ea_attr *)&ea_buf[ea_off]; + ea_value_len = le16_to_cpu(p_ea->ea_value_length); + if (!buffer) { + kvfree(ea_buf); + return ea_value_len; + } + + if (ea_value_len > size) { + err = -ERANGE; + goto free_ea_buf; + } + + memcpy(buffer, &p_ea->ea_name[p_ea->ea_name_length + 1], + ea_value_len); + kvfree(ea_buf); + return ea_value_len; + } + + err = -ENODATA; +free_ea_buf: + kvfree(ea_buf); + return err; +} + +static inline int ea_packed_size(const struct ea_attr *p_ea) +{ + /* + * 4 bytes for header (flags and lengths) + name length + 1 + + * value length. + */ + return 5 + p_ea->ea_name_length + le16_to_cpu(p_ea->ea_value_length); +} + +/* + * Set a new EA, and set EA_INFORMATION accordingly + * + * This is roughly the same as ZwSetEaFile() on Windows, however + * the "offset to next" of the last EA should not be cleared. + * + * Consistency of the new EA is first checked. + * + * EA_INFORMATION is set first, and it is restored to its former + * state if setting EA fails. + */ +static int ntfs_set_ea(struct inode *inode, const char *name, size_t name_len, + const void *value, size_t val_size, int flags, + __le16 *packed_ea_size) +{ + struct ntfs_inode *ni = NTFS_I(inode); + struct ea_information *p_ea_info = NULL; + int ea_packed, err = 0; + struct ea_attr *p_ea; + u32 ea_info_qsize = 0; + char *ea_buf = NULL; + size_t new_ea_size = ALIGN(struct_size(p_ea, ea_name, 1 + name_len + val_size), 4); + s64 ea_off, ea_info_size, all_ea_size, ea_size; + + if (name_len > 255) + return -ENAMETOOLONG; + + if (ntfs_attr_exist(ni, AT_EA_INFORMATION, AT_UNNAMED, 0)) { + p_ea_info = ntfs_attr_readall(ni, AT_EA_INFORMATION, NULL, 0, + &ea_info_size); + if (!p_ea_info || ea_info_size != sizeof(struct ea_information)) + goto out; + + ea_buf = ntfs_attr_readall(ni, AT_EA, NULL, 0, &all_ea_size); + if (!ea_buf) { + ea_info_qsize = 0; + kvfree(p_ea_info); + goto create_ea_info; + } + + ea_info_qsize = le32_to_cpu(p_ea_info->ea_query_length); + } else { +create_ea_info: + p_ea_info = kzalloc(sizeof(struct ea_information), GFP_NOFS); + if (!p_ea_info) + return -ENOMEM; + + ea_info_qsize = 0; + err = ntfs_attr_add(ni, AT_EA_INFORMATION, AT_UNNAMED, 0, + (char *)p_ea_info, sizeof(struct ea_information)); + if (err) + goto out; + + if (ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)) { + err = ntfs_attr_remove(ni, AT_EA, AT_UNNAMED, 0); + if (err) + goto out; + } + + goto alloc_new_ea; + } + + if (ea_info_qsize > all_ea_size) { + err = -EIO; + goto out; + } + + err = ntfs_ea_lookup(ea_buf, ea_info_qsize, name, name_len, &ea_off, + &ea_size); + if (ea_info_qsize && !err) { + if (flags & XATTR_CREATE) { + err = -EEXIST; + goto out; + } + + p_ea = (struct ea_attr *)(ea_buf + ea_off); + + if (val_size && + le16_to_cpu(p_ea->ea_value_length) == val_size && + !memcmp(p_ea->ea_name + p_ea->ea_name_length + 1, value, + val_size)) + goto out; + + le16_add_cpu(&p_ea_info->ea_length, 0 - ea_packed_size(p_ea)); + + if (p_ea->flags & NEED_EA) + le16_add_cpu(&p_ea_info->need_ea_count, -1); + + memmove((char *)p_ea, (char *)p_ea + ea_size, ea_info_qsize - (ea_off + ea_size)); + ea_info_qsize -= ea_size; + p_ea_info->ea_query_length = cpu_to_le32(ea_info_qsize); + + err = ntfs_write_ea(ni, AT_EA_INFORMATION, (char *)p_ea_info, 0, + sizeof(struct ea_information), false); + if (err) + goto out; + + err = ntfs_write_ea(ni, AT_EA, ea_buf, 0, ea_info_qsize, true); + if (err) + goto out; + + if ((flags & XATTR_REPLACE) && !val_size) { + /* Remove xattr. */ + goto out; + } + } else { + if (flags & XATTR_REPLACE) { + err = -ENODATA; + goto out; + } + } + kvfree(ea_buf); + +alloc_new_ea: + ea_buf = kzalloc(new_ea_size, GFP_NOFS); + if (!ea_buf) { + err = -ENOMEM; + goto out; + } + + /* + * EA and REPARSE_POINT compatibility not checked any more, + * required by Windows 10, but having both may lead to + * problems with earlier versions. + */ + p_ea = (struct ea_attr *)ea_buf; + memcpy(p_ea->ea_name, name, name_len); + p_ea->ea_name_length = name_len; + p_ea->ea_name[name_len] = 0; + memcpy(p_ea->ea_name + name_len + 1, value, val_size); + p_ea->ea_value_length = cpu_to_le16(val_size); + p_ea->next_entry_offset = cpu_to_le32(new_ea_size); + + ea_packed = le16_to_cpu(p_ea_info->ea_length) + ea_packed_size(p_ea); + p_ea_info->ea_length = cpu_to_le16(ea_packed); + p_ea_info->ea_query_length = cpu_to_le32(ea_info_qsize + new_ea_size); + + if (ea_packed > 0xffff || + ntfs_attr_size_bounds_check(ni->vol, AT_EA, new_ea_size)) { + err = -EFBIG; + goto out; + } + + /* + * no EA or EA_INFORMATION : add them + */ + if (!ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)) { + err = ntfs_attr_add(ni, AT_EA, AT_UNNAMED, 0, (char *)p_ea, + new_ea_size); + if (err) + goto out; + } else { + err = ntfs_write_ea(ni, AT_EA, (char *)p_ea, ea_info_qsize, + new_ea_size, false); + if (err) + goto out; + } + + err = ntfs_write_ea(ni, AT_EA_INFORMATION, (char *)p_ea_info, 0, + sizeof(struct ea_information), false); + if (err) + goto out; + + if (packed_ea_size) + *packed_ea_size = p_ea_info->ea_length; + mark_mft_record_dirty(ni); +out: + if (ea_info_qsize > 0) + NInoSetHasEA(ni); + else + NInoClearHasEA(ni); + + kvfree(ea_buf); + kvfree(p_ea_info); + + return err; +} + +/* + * Check for the presence of an EA "$LXDEV" (used by WSL) + * and return its value as a device address + */ +int ntfs_ea_get_wsl_inode(struct inode *inode, dev_t *rdevp, unsigned int flags) +{ + int err; + __le32 v; + + if (!(flags & NTFS_VOL_UID)) { + /* Load uid to lxuid EA */ + err = ntfs_get_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &v, + sizeof(v)); + if (err < 0) + return err; + if (err != sizeof(v)) + return -EIO; + i_uid_write(inode, le32_to_cpu(v)); + } + + if (!(flags & NTFS_VOL_GID)) { + /* Load gid to lxgid EA */ + err = ntfs_get_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &v, + sizeof(v)); + if (err < 0) + return err; + if (err != sizeof(v)) + return -EIO; + i_gid_write(inode, le32_to_cpu(v)); + } + + /* Load mode to lxmod EA */ + err = ntfs_get_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &v, sizeof(v)); + if (err == sizeof(v)) { + inode->i_mode = le32_to_cpu(v); + } else { + /* Everyone gets all permissions. */ + inode->i_mode |= 0777; + } + + /* Load mode to lxdev EA */ + err = ntfs_get_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1, &v, sizeof(v)); + if (err == sizeof(v)) + *rdevp = le32_to_cpu(v); + err = 0; + + return err; +} + +int ntfs_ea_set_wsl_inode(struct inode *inode, dev_t rdev, __le16 *ea_size, + unsigned int flags) +{ + __le32 v; + int err = 0; + + if (ea_size) + *ea_size = 0; + + if (flags & NTFS_EA_UID) { + /* Store uid to lxuid EA */ + v = cpu_to_le32(i_uid_read(inode)); + err = ntfs_set_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &v, + sizeof(v), 0, ea_size); + if (err) + return err; + } + + if (flags & NTFS_EA_GID) { + /* Store gid to lxgid EA */ + v = cpu_to_le32(i_gid_read(inode)); + err = ntfs_set_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &v, + sizeof(v), 0, ea_size); + if (err) + return err; + } + + if (flags & NTFS_EA_MODE) { + /* Store mode to lxmod EA */ + v = cpu_to_le32(inode->i_mode); + err = ntfs_set_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &v, + sizeof(v), 0, ea_size); + if (err) + return err; + } + + if (rdev) { + v = cpu_to_le32(rdev); + err = ntfs_set_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1, &v, sizeof(v), + 0, ea_size); + } + + return err; +} + +ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size) +{ + struct inode *inode = d_inode(dentry); + struct ntfs_inode *ni = NTFS_I(inode); + const struct ea_attr *p_ea; + s64 offset, ea_buf_size, ea_info_size; + s64 ea_size; + u32 next; + int err = 0; + u32 ea_info_qsize; + char *ea_buf = NULL; + ssize_t ret = 0; + struct ea_information *ea_info; + + if (!NInoHasEA(ni)) + return 0; + + mutex_lock(&NTFS_I(inode)->mrec_lock); + ea_info = ntfs_attr_readall(ni, AT_EA_INFORMATION, NULL, 0, + &ea_info_size); + if (!ea_info || ea_info_size != sizeof(struct ea_information)) + goto out; + + ea_info_qsize = le32_to_cpu(ea_info->ea_query_length); + + ea_buf = ntfs_attr_readall(ni, AT_EA, NULL, 0, &ea_buf_size); + if (!ea_buf) + goto out; + + if (ea_info_qsize > ea_buf_size || ea_info_qsize == 0) + goto out; + + if (ea_info_qsize < sizeof(struct ea_attr)) { + err = -EIO; + goto out; + } + + offset = 0; + do { + p_ea = (const struct ea_attr *)&ea_buf[offset]; + next = le32_to_cpu(p_ea->next_entry_offset); + ea_size = next ? next : (ea_info_qsize - offset); + + if (ea_size < sizeof(struct ea_attr) || + offset + ea_size > ea_info_qsize) { + err = -EIO; + goto out; + } + + if ((int)p_ea->ea_name_length + 1 > + ea_size - offsetof(struct ea_attr, ea_name)) { + err = -EIO; + goto out; + } + + if (buffer) { + if (ret + p_ea->ea_name_length + 1 > size) { + err = -ERANGE; + goto out; + } + + memcpy(buffer + ret, p_ea->ea_name, p_ea->ea_name_length); + buffer[ret + p_ea->ea_name_length] = 0; + } + + ret += p_ea->ea_name_length + 1; + offset += ea_size; + } while (next > 0 && offset < ea_info_qsize); + +out: + mutex_unlock(&NTFS_I(inode)->mrec_lock); + kvfree(ea_info); + kvfree(ea_buf); + + return err ? err : ret; +} + +// clang-format off +#define SYSTEM_DOS_ATTRIB "system.dos_attrib" +#define SYSTEM_NTFS_ATTRIB "system.ntfs_attrib" +#define SYSTEM_NTFS_ATTRIB_BE "system.ntfs_attrib_be" +// clang-format on + +static int ntfs_getxattr(const struct xattr_handler *handler, + struct dentry *unused, struct inode *inode, const char *name, + void *buffer, size_t size) +{ + struct ntfs_inode *ni = NTFS_I(inode); + int err; + + if (NVolShutdown(ni->vol)) + return -EIO; + + if (!strcmp(name, SYSTEM_DOS_ATTRIB)) { + if (!buffer) { + err = sizeof(u8); + } else if (size < sizeof(u8)) { + err = -ENODATA; + } else { + err = sizeof(u8); + *(u8 *)buffer = (u8)(le32_to_cpu(ni->flags) & 0x3F); + } + goto out; + } + + if (!strcmp(name, SYSTEM_NTFS_ATTRIB) || + !strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) { + if (!buffer) { + err = sizeof(u32); + } else if (size < sizeof(u32)) { + err = -ENODATA; + } else { + err = sizeof(u32); + *(u32 *)buffer = le32_to_cpu(ni->flags); + if (!strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) + *(__be32 *)buffer = cpu_to_be32(*(u32 *)buffer); + } + goto out; + } + + mutex_lock(&ni->mrec_lock); + err = ntfs_get_ea(inode, name, strlen(name), buffer, size); + mutex_unlock(&ni->mrec_lock); + +out: + return err; +} + +static int ntfs_new_attr_flags(struct ntfs_inode *ni, __le32 fattr) +{ + struct ntfs_attr_search_ctx *ctx; + struct mft_record *m; + struct attr_record *a; + __le16 new_aflags; + int mp_size, mp_ofs, name_ofs, arec_size, err; + + m = map_mft_record(ni); + if (IS_ERR(m)) + return PTR_ERR(m); + + ctx = ntfs_attr_get_search_ctx(ni, m); + if (!ctx) { + err = -ENOMEM; + goto err_out; + } + + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (err) { + err = -EINVAL; + goto err_out; + } + + a = ctx->attr; + new_aflags = ctx->attr->flags; + + if (fattr & FILE_ATTR_SPARSE_FILE) + new_aflags |= ATTR_IS_SPARSE; + else + new_aflags &= ~ATTR_IS_SPARSE; + + if (fattr & FILE_ATTR_COMPRESSED) + new_aflags |= ATTR_IS_COMPRESSED; + else + new_aflags &= ~ATTR_IS_COMPRESSED; + + if (new_aflags == a->flags) + return 0; + + if ((new_aflags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) == + (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) { + pr_err("file can't be sparsed and compressed\n"); + err = -EOPNOTSUPP; + goto err_out; + } + + if (!a->non_resident) + goto out; + + if (a->data.non_resident.data_size) { + pr_err("Can't change sparsed/compressed for non-empty file\n"); + err = -EOPNOTSUPP; + goto err_out; + } + + if (new_aflags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) + name_ofs = (offsetof(struct attr_record, + data.non_resident.compressed_size) + + sizeof(a->data.non_resident.compressed_size) + 7) & ~7; + else + name_ofs = (offsetof(struct attr_record, + data.non_resident.compressed_size) + 7) & ~7; + + mp_size = ntfs_get_size_for_mapping_pairs(ni->vol, ni->runlist.rl, 0, -1, -1); + if (unlikely(mp_size < 0)) { + err = mp_size; + ntfs_debug("Failed to get size for mapping pairs array, error code %i.\n", err); + goto err_out; + } + + mp_ofs = (name_ofs + a->name_length * sizeof(__le16) + 7) & ~7; + arec_size = (mp_ofs + mp_size + 7) & ~7; + + err = ntfs_attr_record_resize(m, a, arec_size); + if (unlikely(err)) + goto err_out; + + if (new_aflags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) { + a->data.non_resident.compression_unit = 0; + if (new_aflags & ATTR_IS_COMPRESSED || ni->vol->major_ver < 3) + a->data.non_resident.compression_unit = 4; + a->data.non_resident.compressed_size = 0; + ni->itype.compressed.size = 0; + if (a->data.non_resident.compression_unit) { + ni->itype.compressed.block_size = 1U << + (a->data.non_resident.compression_unit + + ni->vol->cluster_size_bits); + ni->itype.compressed.block_size_bits = + ffs(ni->itype.compressed.block_size) - + 1; + ni->itype.compressed.block_clusters = 1U << + a->data.non_resident.compression_unit; + } else { + ni->itype.compressed.block_size = 0; + ni->itype.compressed.block_size_bits = 0; + ni->itype.compressed.block_clusters = 0; + } + + if (new_aflags & ATTR_IS_SPARSE) { + NInoSetSparse(ni); + ni->flags |= FILE_ATTR_SPARSE_FILE; + } + + if (new_aflags & ATTR_IS_COMPRESSED) { + NInoSetCompressed(ni); + ni->flags |= FILE_ATTR_COMPRESSED; + } + } else { + ni->flags &= ~(FILE_ATTR_SPARSE_FILE | FILE_ATTR_COMPRESSED); + a->data.non_resident.compression_unit = 0; + NInoClearSparse(ni); + NInoClearCompressed(ni); + } + + a->name_offset = cpu_to_le16(name_ofs); + a->data.non_resident.mapping_pairs_offset = cpu_to_le16(mp_ofs); + +out: + a->flags = new_aflags; + mark_mft_record_dirty(ctx->ntfs_ino); +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(ni); + return err; +} + +static int ntfs_setxattr(const struct xattr_handler *handler, + struct mnt_idmap *idmap, struct dentry *unused, + struct inode *inode, const char *name, const void *value, + size_t size, int flags) +{ + struct ntfs_inode *ni = NTFS_I(inode); + int err; + __le32 fattr; + + if (NVolShutdown(ni->vol)) + return -EIO; + + if (!strcmp(name, SYSTEM_DOS_ATTRIB)) { + if (sizeof(u8) != size) { + err = -EINVAL; + goto out; + } + fattr = cpu_to_le32(*(u8 *)value); + goto set_fattr; + } + + if (!strcmp(name, SYSTEM_NTFS_ATTRIB) || + !strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) { + if (size != sizeof(u32)) { + err = -EINVAL; + goto out; + } + if (!strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) + fattr = cpu_to_le32(be32_to_cpu(*(__be32 *)value)); + else + fattr = cpu_to_le32(*(u32 *)value); + + if (S_ISREG(inode->i_mode)) { + mutex_lock(&ni->mrec_lock); + err = ntfs_new_attr_flags(ni, fattr); + mutex_unlock(&ni->mrec_lock); + if (err) + goto out; + } + +set_fattr: + if (S_ISDIR(inode->i_mode)) + fattr |= FILE_ATTR_DIRECTORY; + else + fattr &= ~FILE_ATTR_DIRECTORY; + + if (ni->flags != fattr) { + ni->flags = fattr; + if (fattr & FILE_ATTR_READONLY) + inode->i_mode &= ~0222; + else + inode->i_mode |= 0222; + NInoSetFileNameDirty(ni); + mark_inode_dirty(inode); + } + err = 0; + goto out; + } + + mutex_lock(&ni->mrec_lock); + err = ntfs_set_ea(inode, name, strlen(name), value, size, flags, NULL); + mutex_unlock(&ni->mrec_lock); + +out: + inode_set_ctime_current(inode); + mark_inode_dirty(inode); + return err; +} + +static bool ntfs_xattr_user_list(struct dentry *dentry) +{ + return true; +} + +// clang-format off +static const struct xattr_handler ntfs_other_xattr_handler = { + .prefix = "", + .get = ntfs_getxattr, + .set = ntfs_setxattr, + .list = ntfs_xattr_user_list, +}; + +const struct xattr_handler * const ntfs_xattr_handlers[] = { + &ntfs_other_xattr_handler, + NULL, +}; +// clang-format on + +#ifdef CONFIG_NTFS_FS_POSIX_ACL +struct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, + int type) +{ + struct inode *inode = d_inode(dentry); + struct ntfs_inode *ni = NTFS_I(inode); + const char *name; + size_t name_len; + struct posix_acl *acl; + int err; + void *buf; + + buf = kmalloc(PATH_MAX, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + /* Possible values of 'type' was already checked above. */ + if (type == ACL_TYPE_ACCESS) { + name = XATTR_NAME_POSIX_ACL_ACCESS; + name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1; + } else { + name = XATTR_NAME_POSIX_ACL_DEFAULT; + name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1; + } + + mutex_lock(&ni->mrec_lock); + err = ntfs_get_ea(inode, name, name_len, buf, PATH_MAX); + mutex_unlock(&ni->mrec_lock); + + /* Translate extended attribute to acl. */ + if (err >= 0) + acl = posix_acl_from_xattr(&init_user_ns, buf, err); + else if (err == -ENODATA) + acl = NULL; + else + acl = ERR_PTR(err); + + if (!IS_ERR(acl)) + set_cached_acl(inode, type, acl); + + kfree(buf); + + return acl; +} + +static noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap, + struct inode *inode, struct posix_acl *acl, + int type, bool init_acl) +{ + const char *name; + size_t size, name_len; + void *value; + int err; + int flags; + umode_t mode; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + + mode = inode->i_mode; + switch (type) { + case ACL_TYPE_ACCESS: + /* Do not change i_mode if we are in init_acl */ + if (acl && !init_acl) { + err = posix_acl_update_mode(idmap, inode, &mode, &acl); + if (err) + return err; + } + name = XATTR_NAME_POSIX_ACL_ACCESS; + name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1; + break; + + case ACL_TYPE_DEFAULT: + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + name = XATTR_NAME_POSIX_ACL_DEFAULT; + name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1; + break; + + default: + return -EINVAL; + } + + if (!acl) { + /* Remove xattr if it can be presented via mode. */ + size = 0; + value = NULL; + flags = XATTR_REPLACE; + } else { + value = posix_acl_to_xattr(&init_user_ns, acl, &size, GFP_NOFS); + if (!value) + return -ENOMEM; + flags = 0; + } + + mutex_lock(&NTFS_I(inode)->mrec_lock); + err = ntfs_set_ea(inode, name, name_len, value, size, flags, NULL); + mutex_unlock(&NTFS_I(inode)->mrec_lock); + if (err == -ENODATA && !size) + err = 0; /* Removing non existed xattr. */ + if (!err) { + __le16 ea_size = 0; + umode_t old_mode = inode->i_mode; + + inode->i_mode = mode; + mutex_lock(&NTFS_I(inode)->mrec_lock); + err = ntfs_ea_set_wsl_inode(inode, 0, &ea_size, NTFS_EA_MODE); + if (err) { + ntfs_set_ea(inode, name, name_len, NULL, 0, + XATTR_REPLACE, NULL); + mutex_unlock(&NTFS_I(inode)->mrec_lock); + inode->i_mode = old_mode; + goto out; + } + mutex_unlock(&NTFS_I(inode)->mrec_lock); + + set_cached_acl(inode, type, acl); + inode_set_ctime_current(inode); + mark_inode_dirty(inode); + } + +out: + kfree(value); + + return err; +} + +int ntfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, + struct posix_acl *acl, int type) +{ + return ntfs_set_acl_ex(idmap, d_inode(dentry), acl, type, false); +} + +int ntfs_init_acl(struct mnt_idmap *idmap, struct inode *inode, + struct inode *dir) +{ + struct posix_acl *default_acl, *acl; + int err; + + err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); + if (err) + return err; + + if (default_acl) { + err = ntfs_set_acl_ex(idmap, inode, default_acl, + ACL_TYPE_DEFAULT, true); + posix_acl_release(default_acl); + } else { + inode->i_default_acl = NULL; + } + + if (acl) { + if (!err) + err = ntfs_set_acl_ex(idmap, inode, acl, + ACL_TYPE_ACCESS, true); + posix_acl_release(acl); + } else { + inode->i_acl = NULL; + } + + return err; +} +#endif diff --git a/fs/ntfs/ea.h b/fs/ntfs/ea.h new file mode 100644 index 000000000000..1f63bd55e057 --- /dev/null +++ b/fs/ntfs/ea.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _LINUX_NTFS_EA_H +#define _LINUX_NTFS_EA_H + +#define NTFS_EA_UID BIT(1) +#define NTFS_EA_GID BIT(2) +#define NTFS_EA_MODE BIT(3) + +extern const struct xattr_handler *const ntfs_xattr_handlers[]; + +int ntfs_ea_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode, dev_t dev); +int ntfs_ea_get_wsl_inode(struct inode *inode, dev_t *rdevp, unsigned int flags); +int ntfs_ea_set_wsl_inode(struct inode *inode, dev_t rdev, __le16 *ea_size, + unsigned int flags); +ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size); + +#ifdef CONFIG_NTFS_FS_POSIX_ACL +struct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, + int type); +int ntfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, + struct posix_acl *acl, int type); +int ntfs_init_acl(struct mnt_idmap *idmap, struct inode *inode, + struct inode *dir); +#else +#define ntfs_get_acl NULL +#define ntfs_set_acl NULL +#endif + +#endif /* _LINUX_NTFS_EA_H */ diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c new file mode 100644 index 000000000000..e8bea22b81a7 --- /dev/null +++ b/fs/ntfs/file.c @@ -0,0 +1,1160 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel file operations. + * + * Copyright (c) 2001-2015 Anton Altaparmakov and Tuxera Inc. + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lcnalloc.h" +#include "ntfs.h" +#include "reparse.h" +#include "ea.h" +#include "iomap.h" +#include "bitmap.h" + +#include + +/* + * ntfs_file_open - called when an inode is about to be opened + * @vi: inode to be opened + * @filp: file structure describing the inode + * + * Limit file size to the page cache limit on architectures where unsigned long + * is 32-bits. This is the most we can do for now without overflowing the page + * cache page index. Doing it this way means we don't run into problems because + * of existing too large files. It would be better to allow the user to read + * the beginning of the file but I doubt very much anyone is going to hit this + * check on a 32-bit architecture, so there is no point in adding the extra + * complexity required to support this. + * + * On 64-bit architectures, the check is hopefully optimized away by the + * compiler. + * + * After the check passes, just call generic_file_open() to do its work. + */ +static int ntfs_file_open(struct inode *vi, struct file *filp) +{ + struct ntfs_inode *ni = NTFS_I(vi); + + if (NVolShutdown(ni->vol)) + return -EIO; + + if (sizeof(unsigned long) < 8) { + if (i_size_read(vi) > MAX_LFS_FILESIZE) + return -EOVERFLOW; + } + + filp->f_mode |= FMODE_NOWAIT | FMODE_CAN_ODIRECT; + + return generic_file_open(vi, filp); +} + +/* + * Trim preallocated space on file release. + * + * When the preallo_size mount option is set (default 64KB), writes extend + * allocated_size and runlist in units of preallocated size to reduce + * runlist merge overhead for small writes. This can leave + * allocated_size > data_size if not all preallocated space is used. + * + * We perform the trim here because ->release() is called only when + * the file is no longer open. At this point, no further writes can occur, + * so it is safe to reclaim the unused preallocated space. + * + * Returns 0 on success, or negative error on failure. + */ +static int ntfs_trim_prealloc(struct inode *vi) +{ + struct ntfs_inode *ni = NTFS_I(vi); + struct ntfs_volume *vol = ni->vol; + struct runlist_element *rl; + s64 aligned_data_size; + s64 vcn_ds, vcn_tr; + ssize_t rc; + int err = 0; + + inode_lock(vi); + mutex_lock(&ni->mrec_lock); + down_write(&ni->runlist.lock); + + aligned_data_size = round_up(ni->data_size, vol->cluster_size); + if (aligned_data_size >= ni->allocated_size) + goto out_unlock; + + vcn_ds = ntfs_bytes_to_cluster(vol, aligned_data_size); + vcn_tr = -1; + rc = ni->runlist.count - 2; + rl = ni->runlist.rl; + + while (rc >= 0 && rl[rc].lcn == LCN_HOLE && vcn_ds <= rl[rc].vcn) { + vcn_tr = rl[rc].vcn; + rc--; + } + + if (vcn_tr >= 0) { + err = ntfs_rl_truncate_nolock(vol, &ni->runlist, vcn_tr); + if (err) { + kvfree(ni->runlist.rl); + ni->runlist.rl = NULL; + ntfs_error(vol->sb, "Preallocated block rollback failed"); + } else { + ni->allocated_size = ntfs_cluster_to_bytes(vol, vcn_tr); + err = ntfs_attr_update_mapping_pairs(ni, 0); + if (err) + ntfs_error(vol->sb, + "Failed to rollback mapping pairs for prealloc"); + } + } + +out_unlock: + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + inode_unlock(vi); + + return err; +} + +static int ntfs_file_release(struct inode *vi, struct file *filp) +{ + if (!NInoCompressed(NTFS_I(vi))) + return ntfs_trim_prealloc(vi); + + return 0; +} + +/* + * ntfs_file_fsync - sync a file to disk + * @filp: file to be synced + * @start: start offset to be synced + * @end: end offset to be synced + * @datasync: if non-zero only flush user data and not metadata + * + * Data integrity sync of a file to disk. Used for fsync, fdatasync, and msync + * system calls. This function is inspired by fs/buffer.c::file_fsync(). + * + * If @datasync is false, write the mft record and all associated extent mft + * records as well as the $DATA attribute and then sync the block device. + * + * If @datasync is true and the attribute is non-resident, we skip the writing + * of the mft record and all associated extent mft records (this might still + * happen due to the write_inode_now() call). + * + * Also, if @datasync is true, we do not wait on the inode to be written out + * but we always wait on the page cache pages to be written out. + */ +static int ntfs_file_fsync(struct file *filp, loff_t start, loff_t end, + int datasync) +{ + struct inode *vi = filp->f_mapping->host; + struct ntfs_inode *ni = NTFS_I(vi); + struct ntfs_volume *vol = ni->vol; + int err, ret = 0; + struct inode *parent_vi, *ia_vi; + struct ntfs_attr_search_ctx *ctx; + + ntfs_debug("Entering for inode 0x%llx.", ni->mft_no); + + if (NVolShutdown(vol)) + return -EIO; + + err = file_write_and_wait_range(filp, start, end); + if (err) + return err; + + if (!datasync || !NInoNonResident(NTFS_I(vi))) + ret = __ntfs_write_inode(vi, 1); + write_inode_now(vi, !datasync); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -ENOMEM; + + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL_CHILD); + while (!(err = ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx))) { + if (ctx->attr->type == AT_FILE_NAME) { + struct file_name_attr *fn = (struct file_name_attr *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)); + + parent_vi = ntfs_iget(vi->i_sb, MREF_LE(fn->parent_directory)); + if (IS_ERR(parent_vi)) + continue; + mutex_lock_nested(&NTFS_I(parent_vi)->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + ia_vi = ntfs_index_iget(parent_vi, I30, 4); + mutex_unlock(&NTFS_I(parent_vi)->mrec_lock); + if (IS_ERR(ia_vi)) { + iput(parent_vi); + continue; + } + write_inode_now(ia_vi, 1); + iput(ia_vi); + write_inode_now(parent_vi, 1); + iput(parent_vi); + } else if (ctx->attr->non_resident) { + struct inode *attr_vi; + __le16 *name; + + name = (__le16 *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->name_offset)); + if (ctx->attr->type == AT_DATA && ctx->attr->name_length == 0) + continue; + + attr_vi = ntfs_attr_iget(vi, ctx->attr->type, + name, ctx->attr->name_length); + if (IS_ERR(attr_vi)) + continue; + spin_lock(&attr_vi->i_lock); + if (inode_state_read_once(attr_vi) & I_DIRTY_PAGES) { + spin_unlock(&attr_vi->i_lock); + filemap_write_and_wait(attr_vi->i_mapping); + } else + spin_unlock(&attr_vi->i_lock); + iput(attr_vi); + } + } + mutex_unlock(&ni->mrec_lock); + ntfs_attr_put_search_ctx(ctx); + + write_inode_now(vol->mftbmp_ino, 1); + down_write(&vol->lcnbmp_lock); + write_inode_now(vol->lcnbmp_ino, 1); + up_write(&vol->lcnbmp_lock); + write_inode_now(vol->mft_ino, 1); + + /* + * NOTE: If we were to use mapping->private_list (see ext2 and + * fs/buffer.c) for dirty blocks then we could optimize the below to be + * sync_mapping_buffers(vi->i_mapping). + */ + err = sync_blockdev(vi->i_sb->s_bdev); + if (unlikely(err && !ret)) + ret = err; + if (likely(!ret)) + ntfs_debug("Done."); + else + ntfs_warning(vi->i_sb, + "Failed to f%ssync inode 0x%llx. Error %u.", + datasync ? "data" : "", ni->mft_no, -ret); + if (!ret) + blkdev_issue_flush(vi->i_sb->s_bdev); + return ret; +} + +static int ntfs_setattr_size(struct inode *vi, struct iattr *attr) +{ + struct ntfs_inode *ni = NTFS_I(vi); + int err; + loff_t old_size = vi->i_size; + + if (NInoCompressed(ni) || NInoEncrypted(ni)) { + ntfs_warning(vi->i_sb, + "Changes in inode size are not supported yet for %s files, ignoring.", + NInoCompressed(ni) ? "compressed" : "encrypted"); + return -EOPNOTSUPP; + } + + err = inode_newsize_ok(vi, attr->ia_size); + if (err) + return err; + + inode_dio_wait(vi); + truncate_setsize(vi, attr->ia_size); + err = ntfs_truncate_vfs(vi, attr->ia_size, old_size); + if (err) { + i_size_write(vi, old_size); + return err; + } + + if (NInoNonResident(ni) && attr->ia_size > old_size && + old_size % PAGE_SIZE != 0) { + loff_t len = min_t(loff_t, + round_up(old_size, PAGE_SIZE) - old_size, + attr->ia_size - old_size); + err = iomap_zero_range(vi, old_size, len, + NULL, &ntfs_seek_iomap_ops, + &ntfs_iomap_folio_ops, NULL); + } + + return err; +} + +/* + * ntfs_setattr + * + * Called from notify_change() when an attribute is being changed. + * + * NOTE: Changes in inode size are not supported yet for compressed or + * encrypted files. + */ +int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr) +{ + struct inode *vi = d_inode(dentry); + int err; + unsigned int ia_valid = attr->ia_valid; + struct ntfs_inode *ni = NTFS_I(vi); + struct ntfs_volume *vol = ni->vol; + + if (NVolShutdown(vol)) + return -EIO; + + err = setattr_prepare(idmap, dentry, attr); + if (err) + goto out; + + if (!(vol->vol_flags & VOLUME_IS_DIRTY)) + ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); + + if (ia_valid & ATTR_SIZE) { + err = ntfs_setattr_size(vi, attr); + if (err) + goto out; + + ia_valid |= ATTR_MTIME | ATTR_CTIME; + } + + setattr_copy(idmap, vi, attr); + + if (vol->sb->s_flags & SB_POSIXACL && !S_ISLNK(vi->i_mode)) { + err = posix_acl_chmod(idmap, dentry, vi->i_mode); + if (err) + goto out; + } + + if (0222 & vi->i_mode) + ni->flags &= ~FILE_ATTR_READONLY; + else + ni->flags |= FILE_ATTR_READONLY; + + if (ia_valid & (ATTR_UID | ATTR_GID | ATTR_MODE)) { + unsigned int flags = 0; + + if (ia_valid & ATTR_UID) + flags |= NTFS_EA_UID; + if (ia_valid & ATTR_GID) + flags |= NTFS_EA_GID; + if (ia_valid & ATTR_MODE) + flags |= NTFS_EA_MODE; + + if (S_ISDIR(vi->i_mode)) + vi->i_mode &= ~vol->dmask; + else + vi->i_mode &= ~vol->fmask; + + mutex_lock(&ni->mrec_lock); + ntfs_ea_set_wsl_inode(vi, 0, NULL, flags); + mutex_unlock(&ni->mrec_lock); + } + + mark_inode_dirty(vi); +out: + return err; +} + +int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path, + struct kstat *stat, unsigned int request_mask, + unsigned int query_flags) +{ + struct inode *inode = d_backing_inode(path->dentry); + struct ntfs_inode *ni = NTFS_I(inode); + + generic_fillattr(idmap, request_mask, inode, stat); + + stat->blksize = NTFS_SB(inode->i_sb)->cluster_size; + stat->blocks = (((u64)NTFS_I(inode)->i_dealloc_clusters << + NTFS_SB(inode->i_sb)->cluster_size_bits) >> 9) + inode->i_blocks; + stat->result_mask |= STATX_BTIME; + stat->btime = NTFS_I(inode)->i_crtime; + + if (NInoCompressed(ni)) + stat->attributes |= STATX_ATTR_COMPRESSED; + + if (NInoEncrypted(ni)) + stat->attributes |= STATX_ATTR_ENCRYPTED; + + if (inode->i_flags & S_IMMUTABLE) + stat->attributes |= STATX_ATTR_IMMUTABLE; + + if (inode->i_flags & S_APPEND) + stat->attributes |= STATX_ATTR_APPEND; + + stat->attributes_mask |= STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED | + STATX_ATTR_IMMUTABLE | STATX_ATTR_APPEND; + + /* + * If it's a compressed or encrypted file, NTFS currently + * does not support DIO. For normal files, we report the bdev + * logical block size. + */ + if (request_mask & STATX_DIOALIGN && S_ISREG(inode->i_mode)) { + unsigned int align = + bdev_logical_block_size(inode->i_sb->s_bdev); + + stat->result_mask |= STATX_DIOALIGN; + if (!NInoCompressed(ni) && !NInoEncrypted(ni)) { + stat->dio_mem_align = align; + stat->dio_offset_align = align; + } + } + + return 0; +} + +static loff_t ntfs_file_llseek(struct file *file, loff_t offset, int whence) +{ + struct inode *inode = file->f_mapping->host; + + switch (whence) { + case SEEK_HOLE: + inode_lock_shared(inode); + offset = iomap_seek_hole(inode, offset, &ntfs_seek_iomap_ops); + inode_unlock_shared(inode); + break; + case SEEK_DATA: + inode_lock_shared(inode); + offset = iomap_seek_data(inode, offset, &ntfs_seek_iomap_ops); + inode_unlock_shared(inode); + break; + default: + return generic_file_llseek_size(file, offset, whence, + inode->i_sb->s_maxbytes, + i_size_read(inode)); + } + if (offset < 0) + return offset; + return vfs_setpos(file, offset, inode->i_sb->s_maxbytes); +} + +static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *to) +{ + struct inode *vi = file_inode(iocb->ki_filp); + struct super_block *sb = vi->i_sb; + ssize_t ret; + + if (NVolShutdown(NTFS_SB(sb))) + return -EIO; + + if (NInoCompressed(NTFS_I(vi)) && iocb->ki_flags & IOCB_DIRECT) + return -EOPNOTSUPP; + + inode_lock_shared(vi); + + if (iocb->ki_flags & IOCB_DIRECT) { + size_t count = iov_iter_count(to); + + if ((iocb->ki_pos | count) & (sb->s_blocksize - 1)) { + ret = -EINVAL; + goto inode_unlock; + } + + file_accessed(iocb->ki_filp); + ret = iomap_dio_rw(iocb, to, &ntfs_read_iomap_ops, NULL, 0, + NULL, 0); + } else { + ret = generic_file_read_iter(iocb, to); + } + +inode_unlock: + inode_unlock_shared(vi); + + return ret; +} + +static int ntfs_file_write_dio_end_io(struct kiocb *iocb, ssize_t size, + int error, unsigned int flags) +{ + struct inode *inode = file_inode(iocb->ki_filp); + + if (error) + return error; + + if (size) { + if (i_size_read(inode) < iocb->ki_pos + size) { + i_size_write(inode, iocb->ki_pos + size); + mark_inode_dirty(inode); + } + } + + return 0; +} + +static const struct iomap_dio_ops ntfs_write_dio_ops = { + .end_io = ntfs_file_write_dio_end_io, +}; + +static ssize_t ntfs_dio_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + ssize_t ret; + + ret = iomap_dio_rw(iocb, from, &ntfs_dio_iomap_ops, + &ntfs_write_dio_ops, 0, NULL, 0); + if (ret == -ENOTBLK) + ret = 0; + else if (ret < 0) + goto out; + + if (iov_iter_count(from)) { + loff_t offset, end; + ssize_t written; + int ret2; + + offset = iocb->ki_pos; + iocb->ki_flags &= ~IOCB_DIRECT; + written = iomap_file_buffered_write(iocb, from, + &ntfs_write_iomap_ops, &ntfs_iomap_folio_ops, + NULL); + if (written < 0) { + ret = written; + goto out; + } + + ret += written; + end = iocb->ki_pos + written - 1; + ret2 = filemap_write_and_wait_range(iocb->ki_filp->f_mapping, + offset, end); + if (ret2) { + ret = -EIO; + goto out; + } + invalidate_mapping_pages(iocb->ki_filp->f_mapping, + offset >> PAGE_SHIFT, + end >> PAGE_SHIFT); + } + +out: + return ret; +} + +static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + struct file *file = iocb->ki_filp; + struct inode *vi = file->f_mapping->host; + struct ntfs_inode *ni = NTFS_I(vi); + struct ntfs_volume *vol = ni->vol; + ssize_t ret; + ssize_t count; + loff_t pos; + int err; + loff_t old_data_size, old_init_size; + + if (NVolShutdown(vol)) + return -EIO; + + if (NInoEncrypted(ni)) { + ntfs_error(vi->i_sb, "Writing for %s files is not supported yet", + NInoCompressed(ni) ? "Compressed" : "Encrypted"); + return -EOPNOTSUPP; + } + + if (NInoCompressed(ni) && iocb->ki_flags & IOCB_DIRECT) + return -EOPNOTSUPP; + + if (iocb->ki_flags & IOCB_NOWAIT) { + if (!inode_trylock(vi)) + return -EAGAIN; + } else + inode_lock(vi); + + ret = generic_write_checks(iocb, from); + if (ret <= 0) + goto out_lock; + + err = file_modified(iocb->ki_filp); + if (err) { + ret = err; + goto out_lock; + } + + if (!(vol->vol_flags & VOLUME_IS_DIRTY)) + ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); + + pos = iocb->ki_pos; + count = ret; + + old_data_size = ni->data_size; + old_init_size = ni->initialized_size; + + if (NInoNonResident(ni) && NInoCompressed(ni)) { + ret = ntfs_compress_write(ni, pos, count, from); + if (ret > 0) + iocb->ki_pos += ret; + goto out; + } + + if (NInoNonResident(ni) && iocb->ki_flags & IOCB_DIRECT) + ret = ntfs_dio_write_iter(iocb, from); + else + ret = iomap_file_buffered_write(iocb, from, &ntfs_write_iomap_ops, + &ntfs_iomap_folio_ops, NULL); +out: + if (ret < 0 && ret != -EIOCBQUEUED) { + if (ni->initialized_size != old_init_size) { + mutex_lock(&ni->mrec_lock); + ntfs_attr_set_initialized_size(ni, old_init_size); + mutex_unlock(&ni->mrec_lock); + } + if (ni->data_size != old_data_size) { + truncate_setsize(vi, old_data_size); + ntfs_attr_truncate(ni, old_data_size); + } + } +out_lock: + inode_unlock(vi); + if (ret > 0) + ret = generic_write_sync(iocb, ret); + return ret; +} + +static vm_fault_t ntfs_filemap_page_mkwrite(struct vm_fault *vmf) +{ + struct inode *inode = file_inode(vmf->vma->vm_file); + vm_fault_t ret; + + sb_start_pagefault(inode->i_sb); + file_update_time(vmf->vma->vm_file); + + ret = iomap_page_mkwrite(vmf, &ntfs_page_mkwrite_iomap_ops, NULL); + sb_end_pagefault(inode->i_sb); + return ret; +} + +static const struct vm_operations_struct ntfs_file_vm_ops = { + .fault = filemap_fault, + .map_pages = filemap_map_pages, + .page_mkwrite = ntfs_filemap_page_mkwrite, +}; + +static int ntfs_file_mmap_prepare(struct vm_area_desc *desc) +{ + struct file *file = desc->file; + struct inode *inode = file_inode(file); + + if (NVolShutdown(NTFS_SB(file->f_mapping->host->i_sb))) + return -EIO; + + if (NInoCompressed(NTFS_I(inode))) + return -EOPNOTSUPP; + + if (vma_desc_test_all(desc, VMA_SHARED_BIT, VMA_MAYWRITE_BIT)) { + struct inode *inode = file_inode(file); + loff_t from, to; + int err; + + from = ((loff_t)desc->pgoff << PAGE_SHIFT); + to = min_t(loff_t, i_size_read(inode), + from + desc->end - desc->start); + + if (NTFS_I(inode)->initialized_size < to) { + err = ntfs_extend_initialized_size(inode, to, to, false); + if (err) + return err; + } + } + + + file_accessed(file); + desc->vm_ops = &ntfs_file_vm_ops; + return 0; +} + +static int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + u64 start, u64 len) +{ + return iomap_fiemap(inode, fieinfo, start, len, &ntfs_read_iomap_ops); +} + +static const char *ntfs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) +{ + if (!NTFS_I(inode)->target) + return ERR_PTR(-EINVAL); + + return NTFS_I(inode)->target; +} + +static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, unsigned int flags) +{ + if (NVolShutdown(NTFS_SB(in->f_mapping->host->i_sb))) + return -EIO; + + return filemap_splice_read(in, ppos, pipe, len, flags); +} + +static int ntfs_ioctl_shutdown(struct super_block *sb, unsigned long arg) +{ + u32 flags; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (get_user(flags, (__u32 __user *)arg)) + return -EFAULT; + + return ntfs_force_shutdown(sb, flags); +} + +static int ntfs_ioctl_get_volume_label(struct file *filp, unsigned long arg) +{ + struct ntfs_volume *vol = NTFS_SB(file_inode(filp)->i_sb); + char __user *buf = (char __user *)arg; + + if (!vol->volume_label) { + if (copy_to_user(buf, "", 1)) + return -EFAULT; + } else if (copy_to_user(buf, vol->volume_label, + MIN(FSLABEL_MAX, strlen(vol->volume_label) + 1))) + return -EFAULT; + return 0; +} + +static int ntfs_ioctl_set_volume_label(struct file *filp, unsigned long arg) +{ + struct ntfs_volume *vol = NTFS_SB(file_inode(filp)->i_sb); + char *label; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + label = strndup_user((const char __user *)arg, FSLABEL_MAX); + if (IS_ERR(label)) + return PTR_ERR(label); + + ret = mnt_want_write_file(filp); + if (ret) + goto out; + + ret = ntfs_write_volume_label(vol, label); + mnt_drop_write_file(filp); +out: + kfree(label); + return ret; +} + +static int ntfs_ioctl_fitrim(struct ntfs_volume *vol, unsigned long arg) +{ + struct fstrim_range __user *user_range; + struct fstrim_range range; + struct block_device *dev; + int err; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + dev = vol->sb->s_bdev; + if (!bdev_max_discard_sectors(dev)) + return -EOPNOTSUPP; + + user_range = (struct fstrim_range __user *)arg; + if (copy_from_user(&range, user_range, sizeof(range))) + return -EFAULT; + + if (range.len == 0) + return -EINVAL; + + if (range.len < vol->cluster_size) + return -EINVAL; + + range.minlen = max_t(u32, range.minlen, bdev_discard_granularity(dev)); + + err = ntfs_trim_fs(vol, &range); + if (err < 0) + return err; + + if (copy_to_user(user_range, &range, sizeof(range))) + return -EFAULT; + + return 0; +} + +long ntfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case FS_IOC_SHUTDOWN: + return ntfs_ioctl_shutdown(file_inode(filp)->i_sb, arg); + case FS_IOC_GETFSLABEL: + return ntfs_ioctl_get_volume_label(filp, arg); + case FS_IOC_SETFSLABEL: + return ntfs_ioctl_set_volume_label(filp, arg); + case FITRIM: + return ntfs_ioctl_fitrim(NTFS_SB(file_inode(filp)->i_sb), arg); + default: + return -ENOTTY; + } +} + +#ifdef CONFIG_COMPAT +long ntfs_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + return ntfs_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static int ntfs_allocate_range(struct ntfs_inode *ni, int mode, loff_t offset, + loff_t len) +{ + struct inode *vi = VFS_I(ni); + struct ntfs_volume *vol = ni->vol; + s64 need_space; + loff_t old_size, new_size; + s64 start_vcn, end_vcn; + int err; + + old_size = i_size_read(vi); + new_size = max_t(loff_t, old_size, offset + len); + start_vcn = ntfs_bytes_to_cluster(vol, offset); + end_vcn = ntfs_bytes_to_cluster(vol, offset + len - 1) + 1; + + err = inode_newsize_ok(vi, new_size); + if (err) + goto out; + + need_space = ntfs_bytes_to_cluster(vol, ni->allocated_size); + if (need_space > start_vcn) + need_space = end_vcn - need_space; + else + need_space = end_vcn - start_vcn; + if (need_space > 0 && + need_space > (atomic64_read(&vol->free_clusters) - + atomic64_read(&vol->dirty_clusters))) { + err = -ENOSPC; + goto out; + } + + err = ntfs_attr_fallocate(ni, offset, len, + mode & FALLOC_FL_KEEP_SIZE ? true : false); + + if (!(mode & FALLOC_FL_KEEP_SIZE) && new_size != old_size) + i_size_write(vi, ni->data_size); +out: + return err; +} + +static int ntfs_punch_hole(struct ntfs_inode *ni, int mode, loff_t offset, + loff_t len) +{ + struct ntfs_volume *vol = ni->vol; + struct inode *vi = VFS_I(ni); + loff_t end_offset; + s64 start_vcn, end_vcn; + int err = 0; + + loff_t offset_down = round_down(offset, max_t(unsigned int, + vol->cluster_size, PAGE_SIZE)); + + if (NVolDisableSparse(vol)) { + err = -EOPNOTSUPP; + goto out; + } + + if (offset >= ni->data_size) + goto out; + + if (offset + len > ni->data_size) + end_offset = ni->data_size; + else + end_offset = offset + len; + + err = filemap_write_and_wait_range(vi->i_mapping, offset_down, LLONG_MAX); + if (err) + goto out; + truncate_pagecache(vi, offset_down); + + start_vcn = ntfs_bytes_to_cluster(vol, offset); + end_vcn = ntfs_bytes_to_cluster(vol, end_offset - 1) + 1; + + if (offset & vol->cluster_size_mask) { + if (offset < ni->initialized_size) { + loff_t to; + + to = min_t(loff_t, + ntfs_cluster_to_bytes(vol, start_vcn + 1), + end_offset); + err = iomap_zero_range(vi, offset, to - offset, + NULL, &ntfs_seek_iomap_ops, + &ntfs_iomap_folio_ops, NULL); + if (err < 0) + goto out; + } + if (end_vcn - start_vcn == 1) + goto out; + start_vcn++; + } + + if (end_offset & vol->cluster_size_mask) { + loff_t from; + + from = ntfs_cluster_to_bytes(vol, end_vcn - 1); + if (from < ni->initialized_size) { + err = iomap_zero_range(vi, from, end_offset - from, + NULL, &ntfs_seek_iomap_ops, + &ntfs_iomap_folio_ops, NULL); + if (err < 0) + goto out; + } + if (end_vcn - start_vcn == 1) + goto out; + end_vcn--; + } + + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + err = ntfs_non_resident_attr_punch_hole(ni, start_vcn, + end_vcn - start_vcn); + mutex_unlock(&ni->mrec_lock); +out: + return err; +} + +static int ntfs_collapse_range(struct ntfs_inode *ni, loff_t offset, loff_t len) +{ + struct ntfs_volume *vol = ni->vol; + struct inode *vi = VFS_I(ni); + loff_t old_size, new_size; + s64 start_vcn, end_vcn; + int err; + + loff_t offset_down = round_down(offset, + max_t(unsigned long, vol->cluster_size, PAGE_SIZE)); + + if ((offset & vol->cluster_size_mask) || + (len & vol->cluster_size_mask) || + offset >= ni->allocated_size) { + err = -EINVAL; + goto out; + } + + old_size = i_size_read(vi); + start_vcn = ntfs_bytes_to_cluster(vol, offset); + end_vcn = ntfs_bytes_to_cluster(vol, offset + len - 1) + 1; + + if (ntfs_cluster_to_bytes(vol, end_vcn) > ni->allocated_size) + end_vcn = (round_up(ni->allocated_size - 1, + vol->cluster_size) >> vol->cluster_size_bits) + 1; + new_size = old_size - ntfs_cluster_to_bytes(vol, end_vcn - start_vcn); + if (new_size < 0) + new_size = 0; + err = filemap_write_and_wait_range(vi->i_mapping, + offset_down, LLONG_MAX); + if (err) + goto out; + + truncate_pagecache(vi, offset_down); + + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + err = ntfs_non_resident_attr_collapse_range(ni, start_vcn, + end_vcn - start_vcn); + mutex_unlock(&ni->mrec_lock); + + if (new_size != old_size) + i_size_write(vi, ni->data_size); +out: + return err; +} + +static int ntfs_insert_range(struct ntfs_inode *ni, loff_t offset, loff_t len) +{ + struct ntfs_volume *vol = ni->vol; + struct inode *vi = VFS_I(ni); + loff_t offset_down = round_down(offset, + max_t(unsigned long, vol->cluster_size, PAGE_SIZE)); + loff_t alloc_size, end_offset = offset + len; + loff_t old_size, new_size; + s64 start_vcn, end_vcn; + int err; + + if (NVolDisableSparse(vol)) { + err = -EOPNOTSUPP; + goto out; + } + + if ((offset & vol->cluster_size_mask) || + (len & vol->cluster_size_mask) || + offset >= ni->allocated_size) { + err = -EINVAL; + goto out; + } + + old_size = i_size_read(vi); + start_vcn = ntfs_bytes_to_cluster(vol, offset); + end_vcn = ntfs_bytes_to_cluster(vol, end_offset - 1) + 1; + + new_size = old_size + ntfs_cluster_to_bytes(vol, end_vcn - start_vcn); + alloc_size = ni->allocated_size + + ntfs_cluster_to_bytes(vol, end_vcn - start_vcn); + if (alloc_size < 0) { + err = -EFBIG; + goto out; + } + err = inode_newsize_ok(vi, alloc_size); + if (err) + goto out; + + err = filemap_write_and_wait_range(vi->i_mapping, + offset_down, LLONG_MAX); + if (err) + goto out; + + truncate_pagecache(vi, offset_down); + + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + err = ntfs_non_resident_attr_insert_range(ni, start_vcn, + end_vcn - start_vcn); + mutex_unlock(&ni->mrec_lock); + + if (new_size != old_size) + i_size_write(vi, ni->data_size); +out: + return err; +} + +#define NTFS_FALLOC_FL_SUPPORTED \ + (FALLOC_FL_ALLOCATE_RANGE | FALLOC_FL_KEEP_SIZE | \ + FALLOC_FL_INSERT_RANGE | FALLOC_FL_PUNCH_HOLE | \ + FALLOC_FL_COLLAPSE_RANGE) + +static long ntfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) +{ + struct inode *vi = file_inode(file); + struct ntfs_inode *ni = NTFS_I(vi); + struct ntfs_volume *vol = ni->vol; + int err = 0; + loff_t old_size; + bool map_locked = false; + + if (mode & ~(NTFS_FALLOC_FL_SUPPORTED)) + return -EOPNOTSUPP; + + if (!NVolFreeClusterKnown(vol)) + wait_event(vol->free_waitq, NVolFreeClusterKnown(vol)); + + if ((ni->vol->mft_zone_end - ni->vol->mft_zone_start) == 0) + return -ENOSPC; + + if (NInoNonResident(ni) && !NInoFullyMapped(ni)) { + down_write(&ni->runlist.lock); + err = ntfs_attr_map_whole_runlist(ni); + up_write(&ni->runlist.lock); + if (err) + return err; + } + + if (!(vol->vol_flags & VOLUME_IS_DIRTY)) { + err = ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); + if (err) + return err; + } + + old_size = i_size_read(vi); + + inode_lock(vi); + if (NInoCompressed(ni) || NInoEncrypted(ni)) { + err = -EOPNOTSUPP; + goto out; + } + + inode_dio_wait(vi); + if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE | + FALLOC_FL_INSERT_RANGE)) { + filemap_invalidate_lock(vi->i_mapping); + map_locked = true; + } + + switch (mode & FALLOC_FL_MODE_MASK) { + case FALLOC_FL_ALLOCATE_RANGE: + case FALLOC_FL_KEEP_SIZE: + err = ntfs_allocate_range(ni, mode, offset, len); + break; + case FALLOC_FL_PUNCH_HOLE: + err = ntfs_punch_hole(ni, mode, offset, len); + break; + case FALLOC_FL_COLLAPSE_RANGE: + err = ntfs_collapse_range(ni, offset, len); + break; + case FALLOC_FL_INSERT_RANGE: + err = ntfs_insert_range(ni, offset, len); + break; + default: + err = -EOPNOTSUPP; + } + + if (err) + goto out; + + err = file_modified(file); +out: + if (map_locked) + filemap_invalidate_unlock(vi->i_mapping); + if (!err) { + if (mode == 0 && NInoNonResident(ni) && + offset > old_size && old_size % PAGE_SIZE != 0) { + loff_t len = min_t(loff_t, + round_up(old_size, PAGE_SIZE) - old_size, + offset - old_size); + err = iomap_zero_range(vi, old_size, len, NULL, + &ntfs_seek_iomap_ops, + &ntfs_iomap_folio_ops, NULL); + } + NInoSetFileNameDirty(ni); + inode_set_mtime_to_ts(vi, inode_set_ctime_current(vi)); + mark_inode_dirty(vi); + } + + inode_unlock(vi); + return err; +} + +const struct file_operations ntfs_file_ops = { + .llseek = ntfs_file_llseek, + .read_iter = ntfs_file_read_iter, + .write_iter = ntfs_file_write_iter, + .fsync = ntfs_file_fsync, + .mmap_prepare = ntfs_file_mmap_prepare, + .open = ntfs_file_open, + .release = ntfs_file_release, + .splice_read = ntfs_file_splice_read, + .splice_write = iter_file_splice_write, + .unlocked_ioctl = ntfs_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ntfs_compat_ioctl, +#endif + .fallocate = ntfs_fallocate, + .setlease = generic_setlease, +}; + +const struct inode_operations ntfs_file_inode_ops = { + .setattr = ntfs_setattr, + .getattr = ntfs_getattr, + .listxattr = ntfs_listxattr, + .get_acl = ntfs_get_acl, + .set_acl = ntfs_set_acl, + .fiemap = ntfs_fiemap, +}; + +const struct inode_operations ntfs_symlink_inode_operations = { + .get_link = ntfs_get_link, + .setattr = ntfs_setattr, + .listxattr = ntfs_listxattr, +}; + +const struct inode_operations ntfs_special_inode_operations = { + .setattr = ntfs_setattr, + .getattr = ntfs_getattr, + .listxattr = ntfs_listxattr, + .get_acl = ntfs_get_acl, + .set_acl = ntfs_set_acl, +}; + +const struct file_operations ntfs_empty_file_ops = {}; + +const struct inode_operations ntfs_empty_inode_ops = {}; diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c new file mode 100644 index 000000000000..a547bdcfa456 --- /dev/null +++ b/fs/ntfs/index.c @@ -0,0 +1,2126 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel index handling. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2025 LG Electronics Co., Ltd. + * + * Part of this file is based on code from the NTFS-3G. + * and is copyrighted by the respective authors below: + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2005-2008 Szabolcs Szakacsits + * Copyright (c) 2007-2021 Jean-Pierre Andre + */ + +#include "collate.h" +#include "index.h" +#include "ntfs.h" +#include "attrlist.h" + +/* + * ntfs_index_entry_inconsistent - Check the consistency of an index entry + * + * Make sure data and key do not overflow from entry. + * As a side effect, an entry with zero length is rejected. + * This entry must be a full one (no INDEX_ENTRY_END flag), and its + * length must have been checked beforehand to not overflow from the + * index record. + */ +int ntfs_index_entry_inconsistent(struct ntfs_index_context *icx, + struct ntfs_volume *vol, const struct index_entry *ie, + __le32 collation_rule, u64 inum) +{ + if (icx) { + struct index_header *ih; + u8 *ie_start, *ie_end; + + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + if ((le32_to_cpu(ih->index_length) > le32_to_cpu(ih->allocated_size)) || + (le32_to_cpu(ih->index_length) > icx->block_size)) { + ntfs_error(vol->sb, "%s Index entry(0x%p)'s length is too big.", + icx->is_in_root ? "Index root" : "Index block", + (u8 *)icx->entry); + return -EINVAL; + } + + ie_start = (u8 *)ih + le32_to_cpu(ih->entries_offset); + ie_end = (u8 *)ih + le32_to_cpu(ih->index_length); + + if (ie_start > (u8 *)ie || + ie_end <= (u8 *)ie + le16_to_cpu(ie->length) || + le16_to_cpu(ie->length) > le32_to_cpu(ih->allocated_size) || + le16_to_cpu(ie->length) > icx->block_size) { + ntfs_error(vol->sb, "Index entry(0x%p) is out of range from %s", + (u8 *)icx->entry, + icx->is_in_root ? "index root" : "index block"); + return -EIO; + } + } + + if (ie->key_length && + ((le16_to_cpu(ie->key_length) + offsetof(struct index_entry, key)) > + le16_to_cpu(ie->length))) { + ntfs_error(vol->sb, "Overflow from index entry in inode %lld\n", + (long long)inum); + return -EIO; + + } else { + if (collation_rule == COLLATION_FILE_NAME) { + if ((offsetof(struct index_entry, key.file_name.file_name) + + ie->key.file_name.file_name_length * sizeof(__le16)) > + le16_to_cpu(ie->length)) { + ntfs_error(vol->sb, + "File name overflow from index entry in inode %lld\n", + (long long)inum); + return -EIO; + } + } else { + if (ie->data.vi.data_length && + ((le16_to_cpu(ie->data.vi.data_offset) + + le16_to_cpu(ie->data.vi.data_length)) > + le16_to_cpu(ie->length))) { + ntfs_error(vol->sb, + "Data overflow from index entry in inode %lld\n", + (long long)inum); + return -EIO; + } + } + } + + return 0; +} + +/* + * ntfs_index_entry_mark_dirty - mark an index entry dirty + * @ictx: ntfs index context describing the index entry + * + * Mark the index entry described by the index entry context @ictx dirty. + * + * If the index entry is in the index root attribute, simply mark the inode + * containing the index root attribute dirty. This ensures the mftrecord, and + * hence the index root attribute, will be written out to disk later. + * + * If the index entry is in an index block belonging to the index allocation + * attribute, set ib_dirty to true, thus index block will be updated during + * ntfs_index_ctx_put. + */ +void ntfs_index_entry_mark_dirty(struct ntfs_index_context *ictx) +{ + if (ictx->is_in_root) + mark_mft_record_dirty(ictx->actx->ntfs_ino); + else if (ictx->ib) + ictx->ib_dirty = true; +} + +static s64 ntfs_ib_vcn_to_pos(struct ntfs_index_context *icx, s64 vcn) +{ + return vcn << icx->vcn_size_bits; +} + +static s64 ntfs_ib_pos_to_vcn(struct ntfs_index_context *icx, s64 pos) +{ + return pos >> icx->vcn_size_bits; +} + +static int ntfs_ib_write(struct ntfs_index_context *icx, struct index_block *ib) +{ + s64 ret, vcn = le64_to_cpu(ib->index_block_vcn); + + ntfs_debug("vcn: %lld\n", vcn); + + ret = pre_write_mst_fixup((struct ntfs_record *)ib, icx->block_size); + if (ret) + return -EIO; + + ret = ntfs_inode_attr_pwrite(VFS_I(icx->ia_ni), + ntfs_ib_vcn_to_pos(icx, vcn), icx->block_size, + (u8 *)ib, icx->sync_write); + if (ret != icx->block_size) { + ntfs_debug("Failed to write index block %lld, inode %llu", + vcn, (unsigned long long)icx->idx_ni->mft_no); + return ret; + } + + return 0; +} + +static int ntfs_icx_ib_write(struct ntfs_index_context *icx) +{ + int err; + + err = ntfs_ib_write(icx, icx->ib); + if (err) + return err; + + icx->ib_dirty = false; + + return 0; +} + +int ntfs_icx_ib_sync_write(struct ntfs_index_context *icx) +{ + int ret; + + if (icx->ib_dirty == false) + return 0; + + icx->sync_write = true; + + ret = ntfs_ib_write(icx, icx->ib); + if (!ret) { + kvfree(icx->ib); + icx->ib = NULL; + icx->ib_dirty = false; + } else { + post_write_mst_fixup((struct ntfs_record *)icx->ib); + icx->sync_write = false; + } + + return ret; +} + +/* + * ntfs_index_ctx_get - allocate and initialize a new index context + * @ni: ntfs inode with which to initialize the context + * @name: name of the which context describes + * @name_len: length of the index name + * + * Allocate a new index context, initialize it with @ni and return it. + * Return NULL if allocation failed. + */ +struct ntfs_index_context *ntfs_index_ctx_get(struct ntfs_inode *ni, + __le16 *name, u32 name_len) +{ + struct ntfs_index_context *icx; + + ntfs_debug("Entering\n"); + + if (!ni) + return NULL; + + if (ni->nr_extents == -1) + ni = ni->ext.base_ntfs_ino; + + icx = kmem_cache_alloc(ntfs_index_ctx_cache, GFP_NOFS); + if (icx) + *icx = (struct ntfs_index_context) { + .idx_ni = ni, + .name = name, + .name_len = name_len, + }; + return icx; +} + +static void ntfs_index_ctx_free(struct ntfs_index_context *icx) +{ + ntfs_debug("Entering\n"); + + if (icx->actx) { + ntfs_attr_put_search_ctx(icx->actx); + icx->actx = NULL; + } + + if (!icx->is_in_root) { + if (icx->ib_dirty) + ntfs_ib_write(icx, icx->ib); + kvfree(icx->ib); + icx->ib = NULL; + } + + if (icx->ia_ni) { + iput(VFS_I(icx->ia_ni)); + icx->ia_ni = NULL; + } +} + +/* + * ntfs_index_ctx_put - release an index context + * @icx: index context to free + * + * Release the index context @icx, releasing all associated resources. + */ +void ntfs_index_ctx_put(struct ntfs_index_context *icx) +{ + ntfs_index_ctx_free(icx); + kmem_cache_free(ntfs_index_ctx_cache, icx); +} + +/* + * ntfs_index_ctx_reinit - reinitialize an index context + * @icx: index context to reinitialize + * + * Reinitialize the index context @icx so it can be used for ntfs_index_lookup. + */ +void ntfs_index_ctx_reinit(struct ntfs_index_context *icx) +{ + ntfs_debug("Entering\n"); + + ntfs_index_ctx_free(icx); + + *icx = (struct ntfs_index_context) { + .idx_ni = icx->idx_ni, + .name = icx->name, + .name_len = icx->name_len, + }; +} + +static __le64 *ntfs_ie_get_vcn_addr(struct index_entry *ie) +{ + return (__le64 *)((u8 *)ie + le16_to_cpu(ie->length) - sizeof(s64)); +} + +/* + * Get the subnode vcn to which the index entry refers. + */ +static s64 ntfs_ie_get_vcn(struct index_entry *ie) +{ + return le64_to_cpup(ntfs_ie_get_vcn_addr(ie)); +} + +static struct index_entry *ntfs_ie_get_first(struct index_header *ih) +{ + return (struct index_entry *)((u8 *)ih + le32_to_cpu(ih->entries_offset)); +} + +static struct index_entry *ntfs_ie_get_next(struct index_entry *ie) +{ + return (struct index_entry *)((char *)ie + le16_to_cpu(ie->length)); +} + +static u8 *ntfs_ie_get_end(struct index_header *ih) +{ + return (u8 *)ih + le32_to_cpu(ih->index_length); +} + +static int ntfs_ie_end(struct index_entry *ie) +{ + return ie->flags & INDEX_ENTRY_END || !ie->length; +} + +/* + * Find the last entry in the index block + */ +static struct index_entry *ntfs_ie_get_last(struct index_entry *ie, char *ies_end) +{ + ntfs_debug("Entering\n"); + + while ((char *)ie < ies_end && !ntfs_ie_end(ie)) + ie = ntfs_ie_get_next(ie); + + return ie; +} + +static struct index_entry *ntfs_ie_get_by_pos(struct index_header *ih, int pos) +{ + struct index_entry *ie; + + ntfs_debug("pos: %d\n", pos); + + ie = ntfs_ie_get_first(ih); + + while (pos-- > 0) + ie = ntfs_ie_get_next(ie); + + return ie; +} + +static struct index_entry *ntfs_ie_prev(struct index_header *ih, struct index_entry *ie) +{ + struct index_entry *ie_prev = NULL; + struct index_entry *tmp; + + ntfs_debug("Entering\n"); + + tmp = ntfs_ie_get_first(ih); + + while (tmp != ie) { + ie_prev = tmp; + tmp = ntfs_ie_get_next(tmp); + } + + return ie_prev; +} + +static int ntfs_ih_numof_entries(struct index_header *ih) +{ + int n; + struct index_entry *ie; + u8 *end; + + ntfs_debug("Entering\n"); + + end = ntfs_ie_get_end(ih); + ie = ntfs_ie_get_first(ih); + for (n = 0; !ntfs_ie_end(ie) && (u8 *)ie < end; n++) + ie = ntfs_ie_get_next(ie); + return n; +} + +static int ntfs_ih_one_entry(struct index_header *ih) +{ + return (ntfs_ih_numof_entries(ih) == 1); +} + +static int ntfs_ih_zero_entry(struct index_header *ih) +{ + return (ntfs_ih_numof_entries(ih) == 0); +} + +static void ntfs_ie_delete(struct index_header *ih, struct index_entry *ie) +{ + u32 new_size; + + ntfs_debug("Entering\n"); + + new_size = le32_to_cpu(ih->index_length) - le16_to_cpu(ie->length); + ih->index_length = cpu_to_le32(new_size); + memmove(ie, (u8 *)ie + le16_to_cpu(ie->length), + new_size - ((u8 *)ie - (u8 *)ih)); +} + +static void ntfs_ie_set_vcn(struct index_entry *ie, s64 vcn) +{ + *ntfs_ie_get_vcn_addr(ie) = cpu_to_le64(vcn); +} + +/* + * Insert @ie index entry at @pos entry. Used @ih values should be ok already. + */ +static void ntfs_ie_insert(struct index_header *ih, struct index_entry *ie, + struct index_entry *pos) +{ + int ie_size = le16_to_cpu(ie->length); + + ntfs_debug("Entering\n"); + + ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) + ie_size); + memmove((u8 *)pos + ie_size, pos, + le32_to_cpu(ih->index_length) - ((u8 *)pos - (u8 *)ih) - ie_size); + memcpy(pos, ie, ie_size); +} + +static struct index_entry *ntfs_ie_dup(struct index_entry *ie) +{ + ntfs_debug("Entering\n"); + + return kmemdup(ie, le16_to_cpu(ie->length), GFP_NOFS); +} + +static struct index_entry *ntfs_ie_dup_novcn(struct index_entry *ie) +{ + struct index_entry *dup; + int size = le16_to_cpu(ie->length); + + ntfs_debug("Entering\n"); + + if (ie->flags & INDEX_ENTRY_NODE) + size -= sizeof(s64); + + dup = kmemdup(ie, size, GFP_NOFS); + if (dup) { + dup->flags &= ~INDEX_ENTRY_NODE; + dup->length = cpu_to_le16(size); + } + return dup; +} + +/* + * Check the consistency of an index block + * + * Make sure the index block does not overflow from the index record. + * The size of block is assumed to have been checked to be what is + * defined in the index root. + * + * Returns 0 if no error was found -1 otherwise (with errno unchanged) + * + * |<--->| offsetof(struct index_block, index) + * | |<--->| sizeof(struct index_header) + * | | | + * | | | seq index entries unused + * |=====|=====|=====|===========================|==============| + * | | | | | + * | |<--------->| entries_offset | | + * | |<---------------- index_length ------->| | + * | |<--------------------- allocated_size --------------->| + * |<--------------------------- block_size ------------------->| + * + * size(struct index_header) <= ent_offset < ind_length <= alloc_size < bk_size + */ +static int ntfs_index_block_inconsistent(struct ntfs_index_context *icx, + struct index_block *ib, s64 vcn) +{ + u32 ib_size = (unsigned int)le32_to_cpu(ib->index.allocated_size) + + offsetof(struct index_block, index); + struct super_block *sb = icx->idx_ni->vol->sb; + unsigned long long inum = icx->idx_ni->mft_no; + + ntfs_debug("Entering\n"); + + if (!ntfs_is_indx_record(ib->magic)) { + + ntfs_error(sb, "Corrupt index block signature: vcn %lld inode %llu\n", + vcn, (unsigned long long)icx->idx_ni->mft_no); + return -1; + } + + if (le64_to_cpu(ib->index_block_vcn) != vcn) { + ntfs_error(sb, + "Corrupt index block: s64 (%lld) is different from expected s64 (%lld) in inode %llu\n", + (long long)le64_to_cpu(ib->index_block_vcn), + vcn, inum); + return -1; + } + + if (ib_size != icx->block_size) { + ntfs_error(sb, + "Corrupt index block : s64 (%lld) of inode %llu has a size (%u) differing from the index specified size (%u)\n", + vcn, inum, ib_size, icx->block_size); + return -1; + } + + if (le32_to_cpu(ib->index.entries_offset) < sizeof(struct index_header)) { + ntfs_error(sb, "Invalid index entry offset in inode %lld\n", inum); + return -1; + } + if (le32_to_cpu(ib->index.index_length) <= + le32_to_cpu(ib->index.entries_offset)) { + ntfs_error(sb, "No space for index entries in inode %lld\n", inum); + return -1; + } + if (le32_to_cpu(ib->index.allocated_size) < + le32_to_cpu(ib->index.index_length)) { + ntfs_error(sb, "Index entries overflow in inode %lld\n", inum); + return -1; + } + + return 0; +} + +static struct index_root *ntfs_ir_lookup(struct ntfs_inode *ni, __le16 *name, + u32 name_len, struct ntfs_attr_search_ctx **ctx) +{ + struct attr_record *a; + struct index_root *ir = NULL; + + ntfs_debug("Entering\n"); + *ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!*ctx) { + ntfs_error(ni->vol->sb, "%s, Failed to get search context", __func__); + return NULL; + } + + if (ntfs_attr_lookup(AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE, + 0, NULL, 0, *ctx)) { + ntfs_error(ni->vol->sb, "Failed to lookup $INDEX_ROOT"); + goto err_out; + } + + a = (*ctx)->attr; + if (a->non_resident) { + ntfs_error(ni->vol->sb, "Non-resident $INDEX_ROOT detected"); + goto err_out; + } + + ir = (struct index_root *)((char *)a + le16_to_cpu(a->data.resident.value_offset)); +err_out: + if (!ir) { + ntfs_attr_put_search_ctx(*ctx); + *ctx = NULL; + } + return ir; +} + +static struct index_root *ntfs_ir_lookup2(struct ntfs_inode *ni, __le16 *name, u32 len) +{ + struct ntfs_attr_search_ctx *ctx; + struct index_root *ir; + + ir = ntfs_ir_lookup(ni, name, len, &ctx); + if (ir) + ntfs_attr_put_search_ctx(ctx); + return ir; +} + +/* + * Find a key in the index block. + */ +static int ntfs_ie_lookup(const void *key, const u32 key_len, + struct ntfs_index_context *icx, struct index_header *ih, + s64 *vcn, struct index_entry **ie_out) +{ + struct index_entry *ie; + u8 *index_end; + int rc, item = 0; + + ntfs_debug("Entering\n"); + + index_end = ntfs_ie_get_end(ih); + + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (ie = ntfs_ie_get_first(ih); ; ie = ntfs_ie_get_next(ie)) { + /* Bounds checks. */ + if ((u8 *)ie + sizeof(struct index_entry_header) > index_end || + (u8 *)ie + le16_to_cpu(ie->length) > index_end) { + ntfs_error(icx->idx_ni->vol->sb, + "Index entry out of bounds in inode %llu.\n", + (unsigned long long)icx->idx_ni->mft_no); + return -ERANGE; + } + + /* + * The last entry cannot contain a key. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ntfs_ie_end(ie)) + break; + + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_collate(icx->idx_ni->vol, icx->cr, key, key_len, &ie->key, + le16_to_cpu(ie->key_length)); + if (rc == -EINVAL) { + ntfs_error(icx->idx_ni->vol->sb, + "Collation error. Perhaps a filename contains invalid characters?\n"); + return -ERANGE; + } + /* + * If @key collates before the key of the current entry, there + * is definitely no such key in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + + if (!rc) { + *ie_out = ie; + icx->parent_pos[icx->pindex] = item; + return 0; + } + + item++; + } + /* + * We have finished with this index block without success. Check for the + * presence of a child node and if not present return with errno ENOENT, + * otherwise we will keep searching in another index block. + */ + if (!(ie->flags & INDEX_ENTRY_NODE)) { + ntfs_debug("Index entry wasn't found.\n"); + *ie_out = ie; + return -ENOENT; + } + + /* Get the starting vcn of the index_block holding the child node. */ + *vcn = ntfs_ie_get_vcn(ie); + if (*vcn < 0) { + ntfs_error(icx->idx_ni->vol->sb, "Negative vcn in inode %llu\n", + (unsigned long long)icx->idx_ni->mft_no); + return -EINVAL; + } + + ntfs_debug("Parent entry number %d\n", item); + icx->parent_pos[icx->pindex] = item; + + return -EAGAIN; +} + +struct ntfs_inode *ntfs_ia_open(struct ntfs_index_context *icx, struct ntfs_inode *ni) +{ + struct inode *ia_vi; + + ia_vi = ntfs_index_iget(VFS_I(ni), icx->name, icx->name_len); + if (IS_ERR(ia_vi)) { + ntfs_error(icx->idx_ni->vol->sb, + "Failed to open index allocation of inode %llu", + (unsigned long long)ni->mft_no); + return NULL; + } + + return NTFS_I(ia_vi); +} + +static int ntfs_ib_read(struct ntfs_index_context *icx, s64 vcn, struct index_block *dst) +{ + s64 pos, ret; + + ntfs_debug("vcn: %lld\n", vcn); + + pos = ntfs_ib_vcn_to_pos(icx, vcn); + + ret = ntfs_inode_attr_pread(VFS_I(icx->ia_ni), pos, icx->block_size, (u8 *)dst); + if (ret != icx->block_size) { + if (ret == -1) + ntfs_error(icx->idx_ni->vol->sb, "Failed to read index block"); + else + ntfs_error(icx->idx_ni->vol->sb, + "Failed to read full index block at %lld\n", pos); + return -1; + } + + post_read_mst_fixup((struct ntfs_record *)((u8 *)dst), icx->block_size); + if (ntfs_index_block_inconsistent(icx, dst, vcn)) + return -1; + + return 0; +} + +static int ntfs_icx_parent_inc(struct ntfs_index_context *icx) +{ + icx->pindex++; + if (icx->pindex >= MAX_PARENT_VCN) { + ntfs_error(icx->idx_ni->vol->sb, "Index is over %d level deep", MAX_PARENT_VCN); + return -EOPNOTSUPP; + } + return 0; +} + +static int ntfs_icx_parent_dec(struct ntfs_index_context *icx) +{ + icx->pindex--; + if (icx->pindex < 0) { + ntfs_error(icx->idx_ni->vol->sb, "Corrupt index pointer (%d)", icx->pindex); + return -EINVAL; + } + return 0; +} + +/* + * ntfs_index_lookup - find a key in an index and return its index entry + * @key: key for which to search in the index + * @key_len: length of @key in bytes + * @icx: context describing the index and the returned entry + * + * Before calling ntfs_index_lookup(), @icx must have been obtained from a + * call to ntfs_index_ctx_get(). + * + * Look for the @key in the index specified by the index lookup context @icx. + * ntfs_index_lookup() walks the contents of the index looking for the @key. + * + * If the @key is found in the index, 0 is returned and @icx is setup to + * describe the index entry containing the matching @key. @icx->entry is the + * index entry and @icx->data and @icx->data_len are the index entry data and + * its length in bytes, respectively. + * + * If the @key is not found in the index, -ENOENT is returned and + * @icx is setup to describe the index entry whose key collates immediately + * after the search @key, i.e. this is the position in the index at which + * an index entry with a key of @key would need to be inserted. + * + * When finished with the entry and its data, call ntfs_index_ctx_put() to free + * the context and other associated resources. + * + * If the index entry was modified, call ntfs_index_entry_mark_dirty() before + * the call to ntfs_index_ctx_put() to ensure that the changes are written + * to disk. + */ +int ntfs_index_lookup(const void *key, const u32 key_len, struct ntfs_index_context *icx) +{ + s64 old_vcn, vcn; + struct ntfs_inode *ni = icx->idx_ni; + struct super_block *sb = ni->vol->sb; + struct index_root *ir; + struct index_entry *ie; + struct index_block *ib = NULL; + int err = 0; + + ntfs_debug("Entering\n"); + + if (!key) { + ntfs_error(sb, "key: %p key_len: %d", key, key_len); + return -EINVAL; + } + + ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &icx->actx); + if (!ir) + return -EIO; + + icx->block_size = le32_to_cpu(ir->index_block_size); + if (icx->block_size < NTFS_BLOCK_SIZE) { + err = -EINVAL; + ntfs_error(sb, + "Index block size (%d) is smaller than the sector size (%d)", + icx->block_size, NTFS_BLOCK_SIZE); + goto err_out; + } + + if (ni->vol->cluster_size <= icx->block_size) + icx->vcn_size_bits = ni->vol->cluster_size_bits; + else + icx->vcn_size_bits = ni->vol->sector_size_bits; + + icx->cr = ir->collation_rule; + if (!ntfs_is_collation_rule_supported(icx->cr)) { + err = -EOPNOTSUPP; + ntfs_error(sb, "Unknown collation rule 0x%x", + (unsigned int)le32_to_cpu(icx->cr)); + goto err_out; + } + + old_vcn = VCN_INDEX_ROOT_PARENT; + err = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); + if (err == -ERANGE || err == -EINVAL) + goto err_out; + + icx->ir = ir; + if (err != -EAGAIN) { + icx->is_in_root = true; + icx->parent_vcn[icx->pindex] = old_vcn; + goto done; + } + + /* Child node present, descend into it. */ + icx->ia_ni = ntfs_ia_open(icx, ni); + if (!icx->ia_ni) { + err = -ENOENT; + goto err_out; + } + + ib = kvzalloc(icx->block_size, GFP_NOFS); + if (!ib) { + err = -ENOMEM; + goto err_out; + } + +descend_into_child_node: + icx->parent_vcn[icx->pindex] = old_vcn; + if (ntfs_icx_parent_inc(icx)) { + err = -EIO; + goto err_out; + } + old_vcn = vcn; + + ntfs_debug("Descend into node with s64 %lld.\n", vcn); + + if (ntfs_ib_read(icx, vcn, ib)) { + err = -EIO; + goto err_out; + } + err = ntfs_ie_lookup(key, key_len, icx, &ib->index, &vcn, &ie); + if (err != -EAGAIN) { + if (err == -EINVAL || err == -ERANGE) + goto err_out; + + icx->is_in_root = false; + icx->ib = ib; + icx->parent_vcn[icx->pindex] = vcn; + goto done; + } + + if ((ib->index.flags & NODE_MASK) == LEAF_NODE) { + ntfs_error(icx->idx_ni->vol->sb, + "Index entry with child node found in a leaf node in inode 0x%llx.\n", + (unsigned long long)ni->mft_no); + goto err_out; + } + + goto descend_into_child_node; +err_out: + if (icx->actx) { + ntfs_attr_put_search_ctx(icx->actx); + icx->actx = NULL; + } + kvfree(ib); + if (!err) + err = -EIO; + return err; +done: + icx->entry = ie; + icx->data = (u8 *)ie + offsetof(struct index_entry, key); + icx->data_len = le16_to_cpu(ie->key_length); + ntfs_debug("Done.\n"); + return err; + +} + +static struct index_block *ntfs_ib_alloc(s64 ib_vcn, u32 ib_size, + u8 node_type) +{ + struct index_block *ib; + int ih_size = sizeof(struct index_header); + + ntfs_debug("Entering ib_vcn = %lld ib_size = %u\n", ib_vcn, ib_size); + + ib = kvzalloc(ib_size, GFP_NOFS); + if (!ib) + return NULL; + + ib->magic = magic_INDX; + ib->usa_ofs = cpu_to_le16(sizeof(struct index_block)); + ib->usa_count = cpu_to_le16(ib_size / NTFS_BLOCK_SIZE + 1); + /* Set USN to 1 */ + *(__le16 *)((char *)ib + le16_to_cpu(ib->usa_ofs)) = cpu_to_le16(1); + ib->lsn = 0; + ib->index_block_vcn = cpu_to_le64(ib_vcn); + ib->index.entries_offset = cpu_to_le32((ih_size + + le16_to_cpu(ib->usa_count) * 2 + 7) & ~7); + ib->index.index_length = 0; + ib->index.allocated_size = cpu_to_le32(ib_size - + (sizeof(struct index_block) - ih_size)); + ib->index.flags = node_type; + + return ib; +} + +/* + * Find the median by going through all the entries + */ +static struct index_entry *ntfs_ie_get_median(struct index_header *ih) +{ + struct index_entry *ie, *ie_start; + u8 *ie_end; + int i = 0, median; + + ntfs_debug("Entering\n"); + + ie = ie_start = ntfs_ie_get_first(ih); + ie_end = (u8 *)ntfs_ie_get_end(ih); + + while ((u8 *)ie < ie_end && !ntfs_ie_end(ie)) { + ie = ntfs_ie_get_next(ie); + i++; + } + /* + * NOTE: this could be also the entry at the half of the index block. + */ + median = i / 2 - 1; + + ntfs_debug("Entries: %d median: %d\n", i, median); + + for (i = 0, ie = ie_start; i <= median; i++) + ie = ntfs_ie_get_next(ie); + + return ie; +} + +static u64 ntfs_ibm_vcn_to_pos(struct ntfs_index_context *icx, s64 vcn) +{ + u64 pos = ntfs_ib_vcn_to_pos(icx, vcn); + + do_div(pos, icx->block_size); + return pos; +} + +static s64 ntfs_ibm_pos_to_vcn(struct ntfs_index_context *icx, s64 pos) +{ + return ntfs_ib_pos_to_vcn(icx, pos * icx->block_size); +} + +static int ntfs_ibm_add(struct ntfs_index_context *icx) +{ + u8 bmp[8]; + + ntfs_debug("Entering\n"); + + if (ntfs_attr_exist(icx->idx_ni, AT_BITMAP, icx->name, icx->name_len)) + return 0; + /* + * AT_BITMAP must be at least 8 bytes. + */ + memset(bmp, 0, sizeof(bmp)); + if (ntfs_attr_add(icx->idx_ni, AT_BITMAP, icx->name, icx->name_len, + bmp, sizeof(bmp))) { + ntfs_error(icx->idx_ni->vol->sb, "Failed to add AT_BITMAP"); + return -EINVAL; + } + + return 0; +} + +static int ntfs_ibm_modify(struct ntfs_index_context *icx, s64 vcn, int set) +{ + u8 byte; + u64 pos = ntfs_ibm_vcn_to_pos(icx, vcn); + u32 bpos = pos / 8; + u32 bit = 1 << (pos % 8); + struct ntfs_inode *bmp_ni; + struct inode *bmp_vi; + int ret = 0; + + ntfs_debug("%s vcn: %lld\n", set ? "set" : "clear", vcn); + + bmp_vi = ntfs_attr_iget(VFS_I(icx->idx_ni), AT_BITMAP, icx->name, icx->name_len); + if (IS_ERR(bmp_vi)) { + ntfs_error(icx->idx_ni->vol->sb, "Failed to open $BITMAP attribute"); + return PTR_ERR(bmp_vi); + } + + bmp_ni = NTFS_I(bmp_vi); + + if (set) { + if (bmp_ni->data_size < bpos + 1) { + ret = ntfs_attr_truncate(bmp_ni, (bmp_ni->data_size + 8) & ~7); + if (ret) { + ntfs_error(icx->idx_ni->vol->sb, "Failed to truncate AT_BITMAP"); + goto err; + } + i_size_write(bmp_vi, (loff_t)bmp_ni->data_size); + } + } + + if (ntfs_inode_attr_pread(bmp_vi, bpos, 1, &byte) != 1) { + ret = -EIO; + ntfs_error(icx->idx_ni->vol->sb, "Failed to read $BITMAP"); + goto err; + } + + if (set) + byte |= bit; + else + byte &= ~bit; + + if (ntfs_inode_attr_pwrite(bmp_vi, bpos, 1, &byte, false) != 1) { + ret = -EIO; + ntfs_error(icx->idx_ni->vol->sb, "Failed to write $Bitmap"); + goto err; + } + +err: + iput(bmp_vi); + return ret; +} + +static int ntfs_ibm_set(struct ntfs_index_context *icx, s64 vcn) +{ + return ntfs_ibm_modify(icx, vcn, 1); +} + +static int ntfs_ibm_clear(struct ntfs_index_context *icx, s64 vcn) +{ + return ntfs_ibm_modify(icx, vcn, 0); +} + +static s64 ntfs_ibm_get_free(struct ntfs_index_context *icx) +{ + u8 *bm; + int bit; + s64 vcn, byte, size; + + ntfs_debug("Entering\n"); + + bm = ntfs_attr_readall(icx->idx_ni, AT_BITMAP, icx->name, icx->name_len, + &size); + if (!bm) + return (s64)-1; + + for (byte = 0; byte < size; byte++) { + if (bm[byte] == 255) + continue; + + for (bit = 0; bit < 8; bit++) { + if (!(bm[byte] & (1 << bit))) { + vcn = ntfs_ibm_pos_to_vcn(icx, byte * 8 + bit); + goto out; + } + } + } + + vcn = ntfs_ibm_pos_to_vcn(icx, size * 8); +out: + ntfs_debug("allocated vcn: %lld\n", vcn); + + if (ntfs_ibm_set(icx, vcn)) + vcn = (s64)-1; + + kvfree(bm); + return vcn; +} + +static struct index_block *ntfs_ir_to_ib(struct index_root *ir, s64 ib_vcn) +{ + struct index_block *ib; + struct index_entry *ie_last; + char *ies_start, *ies_end; + int i; + + ntfs_debug("Entering\n"); + + ib = ntfs_ib_alloc(ib_vcn, le32_to_cpu(ir->index_block_size), LEAF_NODE); + if (!ib) + return NULL; + + ies_start = (char *)ntfs_ie_get_first(&ir->index); + ies_end = (char *)ntfs_ie_get_end(&ir->index); + ie_last = ntfs_ie_get_last((struct index_entry *)ies_start, ies_end); + /* + * Copy all entries, including the termination entry + * as well, which can never have any data. + */ + i = (char *)ie_last - ies_start + le16_to_cpu(ie_last->length); + memcpy(ntfs_ie_get_first(&ib->index), ies_start, i); + + ib->index.flags = ir->index.flags; + ib->index.index_length = cpu_to_le32(i + + le32_to_cpu(ib->index.entries_offset)); + return ib; +} + +static void ntfs_ir_nill(struct index_root *ir) +{ + struct index_entry *ie_last; + char *ies_start, *ies_end; + + ntfs_debug("Entering\n"); + + ies_start = (char *)ntfs_ie_get_first(&ir->index); + ies_end = (char *)ntfs_ie_get_end(&ir->index); + ie_last = ntfs_ie_get_last((struct index_entry *)ies_start, ies_end); + /* + * Move the index root termination entry forward + */ + if ((char *)ie_last > ies_start) { + memmove((char *)ntfs_ie_get_first(&ir->index), + (char *)ie_last, le16_to_cpu(ie_last->length)); + ie_last = (struct index_entry *)ies_start; + } +} + +static int ntfs_ib_copy_tail(struct ntfs_index_context *icx, struct index_block *src, + struct index_entry *median, s64 new_vcn) +{ + u8 *ies_end; + struct index_entry *ie_head; /* first entry after the median */ + int tail_size, ret; + struct index_block *dst; + + ntfs_debug("Entering\n"); + + dst = ntfs_ib_alloc(new_vcn, icx->block_size, + src->index.flags & NODE_MASK); + if (!dst) + return -ENOMEM; + + ie_head = ntfs_ie_get_next(median); + + ies_end = (u8 *)ntfs_ie_get_end(&src->index); + tail_size = ies_end - (u8 *)ie_head; + memcpy(ntfs_ie_get_first(&dst->index), ie_head, tail_size); + + dst->index.index_length = cpu_to_le32(tail_size + + le32_to_cpu(dst->index.entries_offset)); + ret = ntfs_ib_write(icx, dst); + + kvfree(dst); + return ret; +} + +static int ntfs_ib_cut_tail(struct ntfs_index_context *icx, struct index_block *ib, + struct index_entry *ie) +{ + char *ies_start, *ies_end; + struct index_entry *ie_last; + int ret; + + ntfs_debug("Entering\n"); + + ies_start = (char *)ntfs_ie_get_first(&ib->index); + ies_end = (char *)ntfs_ie_get_end(&ib->index); + + ie_last = ntfs_ie_get_last((struct index_entry *)ies_start, ies_end); + if (ie_last->flags & INDEX_ENTRY_NODE) + ntfs_ie_set_vcn(ie_last, ntfs_ie_get_vcn(ie)); + + unsafe_memcpy(ie, ie_last, le16_to_cpu(ie_last->length), + /* alloc is larger than ie_last->length, see ntfs_ie_get_last() */); + + ib->index.index_length = cpu_to_le32(((char *)ie - ies_start) + + le16_to_cpu(ie->length) + le32_to_cpu(ib->index.entries_offset)); + + ret = ntfs_ib_write(icx, ib); + return ret; +} + +static int ntfs_ia_add(struct ntfs_index_context *icx) +{ + int ret; + + ntfs_debug("Entering\n"); + + ret = ntfs_ibm_add(icx); + if (ret) + return ret; + + if (!ntfs_attr_exist(icx->idx_ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len)) { + ret = ntfs_attr_add(icx->idx_ni, AT_INDEX_ALLOCATION, icx->name, + icx->name_len, NULL, 0); + if (ret) { + ntfs_error(icx->idx_ni->vol->sb, "Failed to add AT_INDEX_ALLOCATION"); + return ret; + } + } + + icx->ia_ni = ntfs_ia_open(icx, icx->idx_ni); + if (!icx->ia_ni) + return -ENOENT; + + return 0; +} + +static int ntfs_ir_reparent(struct ntfs_index_context *icx) +{ + struct ntfs_attr_search_ctx *ctx = NULL; + struct index_root *ir; + struct index_entry *ie; + struct index_block *ib = NULL; + s64 new_ib_vcn; + int ix_root_size; + int ret = 0; + + ntfs_debug("Entering\n"); + + ir = ntfs_ir_lookup2(icx->idx_ni, icx->name, icx->name_len); + if (!ir) { + ret = -ENOENT; + goto out; + } + + if ((ir->index.flags & NODE_MASK) == SMALL_INDEX) { + ret = ntfs_ia_add(icx); + if (ret) + goto out; + } + + new_ib_vcn = ntfs_ibm_get_free(icx); + if (new_ib_vcn < 0) { + ret = -EINVAL; + goto out; + } + + ir = ntfs_ir_lookup2(icx->idx_ni, icx->name, icx->name_len); + if (!ir) { + ret = -ENOENT; + goto clear_bmp; + } + + ib = ntfs_ir_to_ib(ir, new_ib_vcn); + if (ib == NULL) { + ret = -EIO; + ntfs_error(icx->idx_ni->vol->sb, "Failed to move index root to index block"); + goto clear_bmp; + } + + ret = ntfs_ib_write(icx, ib); + if (ret) + goto clear_bmp; + +retry: + ir = ntfs_ir_lookup(icx->idx_ni, icx->name, icx->name_len, &ctx); + if (!ir) { + ret = -ENOENT; + goto clear_bmp; + } + + ntfs_ir_nill(ir); + + ie = ntfs_ie_get_first(&ir->index); + ie->flags |= INDEX_ENTRY_NODE; + ie->length = cpu_to_le16(sizeof(struct index_entry_header) + sizeof(s64)); + + ir->index.flags = LARGE_INDEX; + NInoSetIndexAllocPresent(icx->idx_ni); + ir->index.index_length = cpu_to_le32(le32_to_cpu(ir->index.entries_offset) + + le16_to_cpu(ie->length)); + ir->index.allocated_size = ir->index.index_length; + + ix_root_size = sizeof(struct index_root) - sizeof(struct index_header) + + le32_to_cpu(ir->index.allocated_size); + ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size); + if (ret) { + /* + * When there is no space to build a non-resident + * index, we may have to move the root to an extent + */ + if ((ret == -ENOSPC) && (ctx->al_entry || !ntfs_inode_add_attrlist(icx->idx_ni))) { + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + ir = ntfs_ir_lookup(icx->idx_ni, icx->name, icx->name_len, &ctx); + if (ir && !ntfs_attr_record_move_away(ctx, ix_root_size - + le32_to_cpu(ctx->attr->data.resident.value_length))) { + if (ntfs_attrlist_update(ctx->base_ntfs_ino ? + ctx->base_ntfs_ino : ctx->ntfs_ino)) + goto clear_bmp; + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + goto retry; + } + } + goto clear_bmp; + } else { + icx->idx_ni->data_size = icx->idx_ni->initialized_size = ix_root_size; + icx->idx_ni->allocated_size = (ix_root_size + 7) & ~7; + } + ntfs_ie_set_vcn(ie, new_ib_vcn); + +err_out: + kvfree(ib); + if (ctx) + ntfs_attr_put_search_ctx(ctx); +out: + return ret; +clear_bmp: + ntfs_ibm_clear(icx, new_ib_vcn); + goto err_out; +} + +/* + * ntfs_ir_truncate - Truncate index root attribute + * @icx: index context + * @data_size: new data size for the index root + */ +static int ntfs_ir_truncate(struct ntfs_index_context *icx, int data_size) +{ + int ret; + + ntfs_debug("Entering\n"); + + /* + * INDEX_ROOT must be resident and its entries can be moved to + * struct index_block, so ENOSPC isn't a real error. + */ + ret = ntfs_attr_truncate(icx->idx_ni, data_size + offsetof(struct index_root, index)); + if (!ret) { + i_size_write(VFS_I(icx->idx_ni), icx->idx_ni->initialized_size); + icx->ir = ntfs_ir_lookup2(icx->idx_ni, icx->name, icx->name_len); + if (!icx->ir) + return -ENOENT; + + icx->ir->index.allocated_size = cpu_to_le32(data_size); + } else if (ret != -ENOSPC) + ntfs_error(icx->idx_ni->vol->sb, "Failed to truncate INDEX_ROOT"); + + return ret; +} + +/* + * ntfs_ir_make_space - Make more space for the index root attribute + * @icx: index context + * @data_size: required data size for the index root + */ +static int ntfs_ir_make_space(struct ntfs_index_context *icx, int data_size) +{ + int ret; + + ntfs_debug("Entering\n"); + + ret = ntfs_ir_truncate(icx, data_size); + if (ret == -ENOSPC) { + ret = ntfs_ir_reparent(icx); + if (!ret) + ret = -EAGAIN; + else + ntfs_error(icx->idx_ni->vol->sb, "Failed to modify INDEX_ROOT"); + } + + return ret; +} + +/* + * NOTE: 'ie' must be a copy of a real index entry. + */ +static int ntfs_ie_add_vcn(struct index_entry **ie) +{ + struct index_entry *p, *old = *ie; + + old->length = cpu_to_le16(le16_to_cpu(old->length) + sizeof(s64)); + p = krealloc(old, le16_to_cpu(old->length), GFP_NOFS); + if (!p) + return -ENOMEM; + + p->flags |= INDEX_ENTRY_NODE; + *ie = p; + return 0; +} + +static int ntfs_ih_insert(struct index_header *ih, struct index_entry *orig_ie, s64 new_vcn, + int pos) +{ + struct index_entry *ie_node, *ie; + int ret = 0; + s64 old_vcn; + + ntfs_debug("Entering\n"); + ie = ntfs_ie_dup(orig_ie); + if (!ie) + return -ENOMEM; + + if (!(ie->flags & INDEX_ENTRY_NODE)) { + ret = ntfs_ie_add_vcn(&ie); + if (ret) + goto out; + } + + ie_node = ntfs_ie_get_by_pos(ih, pos); + old_vcn = ntfs_ie_get_vcn(ie_node); + ntfs_ie_set_vcn(ie_node, new_vcn); + + ntfs_ie_insert(ih, ie, ie_node); + ntfs_ie_set_vcn(ie_node, old_vcn); +out: + kfree(ie); + return ret; +} + +static s64 ntfs_icx_parent_vcn(struct ntfs_index_context *icx) +{ + return icx->parent_vcn[icx->pindex]; +} + +static s64 ntfs_icx_parent_pos(struct ntfs_index_context *icx) +{ + return icx->parent_pos[icx->pindex]; +} + +static int ntfs_ir_insert_median(struct ntfs_index_context *icx, struct index_entry *median, + s64 new_vcn) +{ + u32 new_size; + int ret; + + ntfs_debug("Entering\n"); + + icx->ir = ntfs_ir_lookup2(icx->idx_ni, icx->name, icx->name_len); + if (!icx->ir) + return -ENOENT; + + new_size = le32_to_cpu(icx->ir->index.index_length) + + le16_to_cpu(median->length); + if (!(median->flags & INDEX_ENTRY_NODE)) + new_size += sizeof(s64); + + ret = ntfs_ir_make_space(icx, new_size); + if (ret) + return ret; + + icx->ir = ntfs_ir_lookup2(icx->idx_ni, icx->name, icx->name_len); + if (!icx->ir) + return -ENOENT; + + return ntfs_ih_insert(&icx->ir->index, median, new_vcn, + ntfs_icx_parent_pos(icx)); +} + +static int ntfs_ib_split(struct ntfs_index_context *icx, struct index_block *ib); + +struct split_info { + struct list_head entry; + s64 new_vcn; + struct index_block *ib; +}; + +static int ntfs_ib_insert(struct ntfs_index_context *icx, struct index_entry *ie, s64 new_vcn, + struct split_info *si) +{ + struct index_block *ib; + u32 idx_size, allocated_size; + int err; + s64 old_vcn; + + ntfs_debug("Entering\n"); + + ib = kvzalloc(icx->block_size, GFP_NOFS); + if (!ib) + return -ENOMEM; + + old_vcn = ntfs_icx_parent_vcn(icx); + + err = ntfs_ib_read(icx, old_vcn, ib); + if (err) + goto err_out; + + idx_size = le32_to_cpu(ib->index.index_length); + allocated_size = le32_to_cpu(ib->index.allocated_size); + if (idx_size + le16_to_cpu(ie->length) + sizeof(s64) > allocated_size) { + si->ib = ib; + si->new_vcn = new_vcn; + return -EAGAIN; + } + + err = ntfs_ih_insert(&ib->index, ie, new_vcn, ntfs_icx_parent_pos(icx)); + if (err) + goto err_out; + + err = ntfs_ib_write(icx, ib); + +err_out: + kvfree(ib); + return err; +} + +/* + * ntfs_ib_split - Split an index block + * @icx: index context + * @ib: index block to split + */ +static int ntfs_ib_split(struct ntfs_index_context *icx, struct index_block *ib) +{ + struct index_entry *median; + s64 new_vcn; + int ret; + struct split_info *si; + LIST_HEAD(ntfs_cut_tail_list); + + ntfs_debug("Entering\n"); + +resplit: + ret = ntfs_icx_parent_dec(icx); + if (ret) + goto out; + + median = ntfs_ie_get_median(&ib->index); + new_vcn = ntfs_ibm_get_free(icx); + if (new_vcn < 0) { + ret = -EINVAL; + goto out; + } + + ret = ntfs_ib_copy_tail(icx, ib, median, new_vcn); + if (ret) { + ntfs_ibm_clear(icx, new_vcn); + goto out; + } + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) { + ret = ntfs_ir_insert_median(icx, median, new_vcn); + if (ret) { + ntfs_ibm_clear(icx, new_vcn); + goto out; + } + } else { + si = kzalloc(sizeof(struct split_info), GFP_NOFS); + if (!si) { + ntfs_ibm_clear(icx, new_vcn); + ret = -ENOMEM; + goto out; + } + + ret = ntfs_ib_insert(icx, median, new_vcn, si); + if (ret == -EAGAIN) { + list_add_tail(&si->entry, &ntfs_cut_tail_list); + ib = si->ib; + goto resplit; + } else if (ret) { + kvfree(si->ib); + kfree(si); + ntfs_ibm_clear(icx, new_vcn); + goto out; + } + kfree(si); + } + + ret = ntfs_ib_cut_tail(icx, ib, median); + +out: + while (!list_empty(&ntfs_cut_tail_list)) { + si = list_last_entry(&ntfs_cut_tail_list, struct split_info, entry); + ntfs_ibm_clear(icx, si->new_vcn); + kvfree(si->ib); + list_del(&si->entry); + kfree(si); + if (!ret) + ret = -EAGAIN; + } + + return ret; +} + +int ntfs_ie_add(struct ntfs_index_context *icx, struct index_entry *ie) +{ + struct index_header *ih; + int allocated_size, new_size; + int ret; + + while (1) { + ret = ntfs_index_lookup(&ie->key, le16_to_cpu(ie->key_length), icx); + if (!ret) { + ret = -EEXIST; + ntfs_error(icx->idx_ni->vol->sb, "Index already have such entry"); + goto err_out; + } + if (ret != -ENOENT) { + ntfs_error(icx->idx_ni->vol->sb, "Failed to find place for new entry"); + goto err_out; + } + ret = 0; + + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + allocated_size = le32_to_cpu(ih->allocated_size); + new_size = le32_to_cpu(ih->index_length) + le16_to_cpu(ie->length); + + if (new_size <= allocated_size) + break; + + ntfs_debug("index block sizes: allocated: %d needed: %d\n", + allocated_size, new_size); + + if (icx->is_in_root) + ret = ntfs_ir_make_space(icx, new_size); + else + ret = ntfs_ib_split(icx, icx->ib); + if (ret && ret != -EAGAIN) + goto err_out; + + mark_mft_record_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); + } + + ntfs_ie_insert(ih, ie, icx->entry); + ntfs_index_entry_mark_dirty(icx); + +err_out: + ntfs_debug("%s\n", ret ? "Failed" : "Done"); + return ret; +} + +/* + * ntfs_index_add_filename - add filename to directory index + * @ni: ntfs inode describing directory to which index add filename + * @fn: FILE_NAME attribute to add + * @mref: reference of the inode which @fn describes + */ +int ntfs_index_add_filename(struct ntfs_inode *ni, struct file_name_attr *fn, u64 mref) +{ + struct index_entry *ie; + struct ntfs_index_context *icx; + int fn_size, ie_size, err; + + ntfs_debug("Entering\n"); + + if (!ni || !fn) + return -EINVAL; + + fn_size = (fn->file_name_length * sizeof(__le16)) + + sizeof(struct file_name_attr); + ie_size = (sizeof(struct index_entry_header) + fn_size + 7) & ~7; + + ie = kzalloc(ie_size, GFP_NOFS); + if (!ie) + return -ENOMEM; + + ie->data.dir.indexed_file = cpu_to_le64(mref); + ie->length = cpu_to_le16(ie_size); + ie->key_length = cpu_to_le16(fn_size); + + unsafe_memcpy(&ie->key, fn, fn_size, + /* "fn_size" was correctly calculated above */); + + icx = ntfs_index_ctx_get(ni, I30, 4); + if (!icx) { + err = -ENOMEM; + goto out; + } + + err = ntfs_ie_add(icx, ie); + ntfs_index_ctx_put(icx); +out: + kfree(ie); + return err; +} + +static int ntfs_ih_takeout(struct ntfs_index_context *icx, struct index_header *ih, + struct index_entry *ie, struct index_block *ib) +{ + struct index_entry *ie_roam; + int freed_space; + bool full; + int ret = 0; + + ntfs_debug("Entering\n"); + + full = ih->index_length == ih->allocated_size; + ie_roam = ntfs_ie_dup_novcn(ie); + if (!ie_roam) + return -ENOMEM; + + ntfs_ie_delete(ih, ie); + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) { + /* + * Recover the space which may have been freed + * while deleting an entry from root index + */ + freed_space = le32_to_cpu(ih->allocated_size) - + le32_to_cpu(ih->index_length); + if (full && (freed_space > 0) && !(freed_space & 7)) { + ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); + /* do nothing if truncation fails */ + } + + mark_mft_record_dirty(icx->actx->ntfs_ino); + } else { + ret = ntfs_ib_write(icx, ib); + if (ret) + goto out; + } + + ntfs_index_ctx_reinit(icx); + + ret = ntfs_ie_add(icx, ie_roam); +out: + kfree(ie_roam); + return ret; +} + +/* + * Used if an empty index block to be deleted has END entry as the parent + * in the INDEX_ROOT which is the only one there. + */ +static void ntfs_ir_leafify(struct ntfs_index_context *icx, struct index_header *ih) +{ + struct index_entry *ie; + + ntfs_debug("Entering\n"); + + ie = ntfs_ie_get_first(ih); + ie->flags &= ~INDEX_ENTRY_NODE; + ie->length = cpu_to_le16(le16_to_cpu(ie->length) - sizeof(s64)); + + ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) - sizeof(s64)); + ih->flags &= ~LARGE_INDEX; + NInoClearIndexAllocPresent(icx->idx_ni); + + /* Not fatal error */ + ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); +} + +/* + * Used if an empty index block to be deleted has END entry as the parent + * in the INDEX_ROOT which is not the only one there. + */ +static int ntfs_ih_reparent_end(struct ntfs_index_context *icx, struct index_header *ih, + struct index_block *ib) +{ + struct index_entry *ie, *ie_prev; + + ntfs_debug("Entering\n"); + + ie = ntfs_ie_get_by_pos(ih, ntfs_icx_parent_pos(icx)); + ie_prev = ntfs_ie_prev(ih, ie); + if (!ie_prev) + return -EIO; + ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(ie_prev)); + + return ntfs_ih_takeout(icx, ih, ie_prev, ib); +} + +static int ntfs_index_rm_leaf(struct ntfs_index_context *icx) +{ + struct index_block *ib = NULL; + struct index_header *parent_ih; + struct index_entry *ie; + int ret; + + ntfs_debug("pindex: %d\n", icx->pindex); + + ret = ntfs_icx_parent_dec(icx); + if (ret) + return ret; + + ret = ntfs_ibm_clear(icx, icx->parent_vcn[icx->pindex + 1]); + if (ret) + return ret; + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + parent_ih = &icx->ir->index; + else { + ib = kvzalloc(icx->block_size, GFP_NOFS); + if (!ib) + return -ENOMEM; + + ret = ntfs_ib_read(icx, ntfs_icx_parent_vcn(icx), ib); + if (ret) + goto out; + + parent_ih = &ib->index; + } + + ie = ntfs_ie_get_by_pos(parent_ih, ntfs_icx_parent_pos(icx)); + if (!ntfs_ie_end(ie)) { + ret = ntfs_ih_takeout(icx, parent_ih, ie, ib); + goto out; + } + + if (ntfs_ih_zero_entry(parent_ih)) { + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) { + ntfs_ir_leafify(icx, parent_ih); + goto out; + } + + ret = ntfs_index_rm_leaf(icx); + goto out; + } + + ret = ntfs_ih_reparent_end(icx, parent_ih, ib); +out: + kvfree(ib); + return ret; +} + +static int ntfs_index_rm_node(struct ntfs_index_context *icx) +{ + int entry_pos, pindex; + s64 vcn; + struct index_block *ib = NULL; + struct index_entry *ie_succ, *ie, *entry = icx->entry; + struct index_header *ih; + u32 new_size; + int delta, ret; + + ntfs_debug("Entering\n"); + + if (!icx->ia_ni) { + icx->ia_ni = ntfs_ia_open(icx, icx->idx_ni); + if (!icx->ia_ni) + return -EINVAL; + } + + ib = kvzalloc(icx->block_size, GFP_NOFS); + if (!ib) + return -ENOMEM; + + ie_succ = ntfs_ie_get_next(icx->entry); + entry_pos = icx->parent_pos[icx->pindex]++; + pindex = icx->pindex; +descend: + vcn = ntfs_ie_get_vcn(ie_succ); + ret = ntfs_ib_read(icx, vcn, ib); + if (ret) + goto out; + + ie_succ = ntfs_ie_get_first(&ib->index); + + ret = ntfs_icx_parent_inc(icx); + if (ret) + goto out; + + icx->parent_vcn[icx->pindex] = vcn; + icx->parent_pos[icx->pindex] = 0; + + if ((ib->index.flags & NODE_MASK) == INDEX_NODE) + goto descend; + + if (ntfs_ih_zero_entry(&ib->index)) { + ret = -EIO; + ntfs_error(icx->idx_ni->vol->sb, "Empty index block"); + goto out; + } + + ie = ntfs_ie_dup(ie_succ); + if (!ie) { + ret = -ENOMEM; + goto out; + } + + ret = ntfs_ie_add_vcn(&ie); + if (ret) + goto out2; + + ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(icx->entry)); + + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + delta = le16_to_cpu(ie->length) - le16_to_cpu(icx->entry->length); + new_size = le32_to_cpu(ih->index_length) + delta; + if (delta > 0) { + if (icx->is_in_root) { + ret = ntfs_ir_make_space(icx, new_size); + if (ret != 0) + goto out2; + + ih = &icx->ir->index; + entry = ntfs_ie_get_by_pos(ih, entry_pos); + + } else if (new_size > le32_to_cpu(ih->allocated_size)) { + icx->pindex = pindex; + ret = ntfs_ib_split(icx, icx->ib); + if (!ret) + ret = -EAGAIN; + goto out2; + } + } + + ntfs_ie_delete(ih, entry); + ntfs_ie_insert(ih, ie, entry); + + if (icx->is_in_root) + ret = ntfs_ir_truncate(icx, new_size); + else + ret = ntfs_icx_ib_write(icx); + if (ret) + goto out2; + + ntfs_ie_delete(&ib->index, ie_succ); + + if (ntfs_ih_zero_entry(&ib->index)) + ret = ntfs_index_rm_leaf(icx); + else + ret = ntfs_ib_write(icx, ib); + +out2: + kfree(ie); +out: + kvfree(ib); + return ret; +} + +/* + * ntfs_index_rm - remove entry from the index + * @icx: index context describing entry to delete + * + * Delete entry described by @icx from the index. Index context is always + * reinitialized after use of this function, so it can be used for index + * lookup once again. + */ +int ntfs_index_rm(struct ntfs_index_context *icx) +{ + struct index_header *ih; + int ret = 0; + + ntfs_debug("Entering\n"); + + if (!icx || (!icx->ib && !icx->ir) || ntfs_ie_end(icx->entry)) { + ret = -EINVAL; + goto err_out; + } + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + if (icx->entry->flags & INDEX_ENTRY_NODE) { + ret = ntfs_index_rm_node(icx); + if (ret) + goto err_out; + } else if (icx->is_in_root || !ntfs_ih_one_entry(ih)) { + ntfs_ie_delete(ih, icx->entry); + + if (icx->is_in_root) + ret = ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); + else + ret = ntfs_icx_ib_write(icx); + if (ret) + goto err_out; + } else { + ret = ntfs_index_rm_leaf(icx); + if (ret) + goto err_out; + } + + return 0; +err_out: + return ret; +} + +int ntfs_index_remove(struct ntfs_inode *dir_ni, const void *key, const u32 keylen) +{ + int ret = 0; + struct ntfs_index_context *icx; + + icx = ntfs_index_ctx_get(dir_ni, I30, 4); + if (!icx) + return -EINVAL; + + while (1) { + ret = ntfs_index_lookup(key, keylen, icx); + if (ret) + goto err_out; + + ret = ntfs_index_rm(icx); + if (ret && ret != -EAGAIN) + goto err_out; + else if (!ret) + break; + + mark_mft_record_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); + } + + mark_mft_record_dirty(icx->actx->ntfs_ino); + + ntfs_index_ctx_put(icx); + return 0; +err_out: + ntfs_index_ctx_put(icx); + ntfs_error(dir_ni->vol->sb, "Delete failed"); + return ret; +} + +/* + * ntfs_index_walk_down - walk down the index tree (leaf bound) + * until there are no subnode in the first index entry returns + * the entry at the bottom left in subnode + */ +struct index_entry *ntfs_index_walk_down(struct index_entry *ie, struct ntfs_index_context *ictx) +{ + struct index_entry *entry; + struct index_block *ib; + s64 vcn; + + entry = ie; + do { + vcn = ntfs_ie_get_vcn(entry); + if (ictx->is_in_root) { + ib = kvzalloc(ictx->block_size, GFP_NOFS); + if (!ib) + return ERR_PTR(-ENOMEM); + /* down from level zero */ + ictx->ir = NULL; + ictx->ib = ib; + ictx->pindex = 1; + ictx->is_in_root = false; + } else { + /* down from non-zero level */ + ictx->pindex++; + } + + ictx->parent_pos[ictx->pindex] = 0; + ictx->parent_vcn[ictx->pindex] = vcn; + if (!ntfs_ib_read(ictx, vcn, ictx->ib)) { + ictx->entry = ntfs_ie_get_first(&ictx->ib->index); + entry = ictx->entry; + } else + entry = ERR_PTR(-EIO); + } while (!IS_ERR(entry) && (entry->flags & INDEX_ENTRY_NODE)); + + return entry; +} + +/* + * ntfs_index_walk_up - walk up the index tree (root bound) until + * there is a valid data entry in parent returns the parent entry + * or NULL if no more parent. + * @ie: current index entry + * @ictx: index context + */ +static struct index_entry *ntfs_index_walk_up(struct index_entry *ie, + struct ntfs_index_context *ictx) +{ + struct index_entry *entry = ie; + s64 vcn; + + if (ictx->pindex <= 0) + return NULL; + + do { + ictx->pindex--; + if (!ictx->pindex) { + /* we have reached the root */ + kfree(ictx->ib); + ictx->ib = NULL; + ictx->is_in_root = true; + /* a new search context is to be allocated */ + if (ictx->actx) + ntfs_attr_put_search_ctx(ictx->actx); + ictx->ir = ntfs_ir_lookup(ictx->idx_ni, ictx->name, + ictx->name_len, &ictx->actx); + if (ictx->ir) + entry = ntfs_ie_get_by_pos( + &ictx->ir->index, + ictx->parent_pos[ictx->pindex]); + else + entry = NULL; + } else { + /* up into non-root node */ + vcn = ictx->parent_vcn[ictx->pindex]; + if (!ntfs_ib_read(ictx, vcn, ictx->ib)) { + entry = ntfs_ie_get_by_pos( + &ictx->ib->index, + ictx->parent_pos[ictx->pindex]); + } else + entry = NULL; + } + ictx->entry = entry; + } while (entry && (ictx->pindex > 0) && + (entry->flags & INDEX_ENTRY_END)); + return entry; +} + +/* + * ntfs_index_next - get next entry in an index according to collating sequence. + * Returns next entry or NULL if none. + * + * Sample layout : + * + * +---+---+---+---+---+---+---+---+ n ptrs to subnodes + * | | | 10| 25| 33| | | | n-1 keys in between + * +---+---+---+---+---+---+---+---+ no key in last entry + * | A | A + * | | | +-------------------------------+ + * +--------------------------+ | +-----+ | + * | +--+ | | + * V | V | + * +---+---+---+---+---+---+---+---+ | +---+---+---+---+---+---+---+---+ + * | 11| 12| 13| 14| 15| 16| 17| | | | 26| 27| 28| 29| 30| 31| 32| | + * +---+---+---+---+---+---+---+---+ | +---+---+---+---+---+---+---+---+ + * | | + * +-----------------------+ | + * | | + * +---+---+---+---+---+---+---+---+ + * | 18| 19| 20| 21| 22| 23| 24| | + * +---+---+---+---+---+---+---+---+ + * + * @ie: current index entry + * @ictx: index context + */ +struct index_entry *ntfs_index_next(struct index_entry *ie, struct ntfs_index_context *ictx) +{ + struct index_entry *next; + __le16 flags; + + /* + * lookup() may have returned an invalid node + * when searching for a partial key + * if this happens, walk up + */ + if (ie->flags & INDEX_ENTRY_END) + next = ntfs_index_walk_up(ie, ictx); + else { + /* + * get next entry in same node + * there is always one after any entry with data + */ + next = (struct index_entry *)((char *)ie + le16_to_cpu(ie->length)); + ++ictx->parent_pos[ictx->pindex]; + flags = next->flags; + + /* walk down if it has a subnode */ + if (flags & INDEX_ENTRY_NODE) { + if (!ictx->ia_ni) { + ictx->ia_ni = ntfs_ia_open(ictx, ictx->idx_ni); + if (!ictx->ia_ni) + return ERR_PTR(-EIO); + } + + next = ntfs_index_walk_down(next, ictx); + if (IS_ERR(next)) + return next; + } else { + + /* walk up it has no subnode, nor data */ + if (flags & INDEX_ENTRY_END) + next = ntfs_index_walk_up(next, ictx); + } + } + + /* return NULL if stuck at end of a block */ + if (next && (next->flags & INDEX_ENTRY_END)) + next = NULL; + + return next; +} diff --git a/fs/ntfs/index.h b/fs/ntfs/index.h new file mode 100644 index 000000000000..e68d6fabaf9f --- /dev/null +++ b/fs/ntfs/index.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for NTFS kernel index handling. + * + * Copyright (c) 2004 Anton Altaparmakov + */ + +#ifndef _LINUX_NTFS_INDEX_H +#define _LINUX_NTFS_INDEX_H + +#include + +#include "attrib.h" +#include "mft.h" + +#define VCN_INDEX_ROOT_PARENT ((s64)-2) + +#define MAX_PARENT_VCN 32 + +/* + * @idx_ni: index inode containing the @entry described by this context + * @name: Unicode name of the indexed attribute + * (usually $I30 for directories) + * @name_len: length of @name in Unicode characters + * @entry: index entry (points into @ir or @ia) + * @cr: creation time of the entry (for sorting/validation) + * @data: index entry data (points into @entry) + * @data_len: length in bytes of @data + * @is_in_root: 'true' if @entry is in @ir and 'false' if it is in @ia + * @ir: index root if @is_in_root and NULL otherwise + * @actx: attribute search context if @is_in_root and NULL otherwise + * @ib: index block header (valid when @is_in_root is 'false') + * @ia_ni: index allocation inode (extent inode) for @ia + * @parent_pos: array of parent entry positions in the B-tree nodes + * @parent_vcn: VCNs of parent index blocks in the B-tree traversal + * @pindex: current depth (number of parent nodes) in the traversal + * (maximum is MAX_PARENT_VCN) + * @ib_dirty: true if the current index block (@ia/@ib) was modified + * @block_size: size of index blocks in bytes (from $INDEX_ROOT or $Boot) + * @vcn_size_bits: log2(cluster size) + * @sync_write: true if synchronous writeback is requested for this context + * + * @idx_ni is the index inode this context belongs to. + * + * @entry is the index entry described by this context. @data and @data_len + * are the index entry data and its length in bytes, respectively. @data + * simply points into @entry. This is probably what the user is interested in. + * + * If @is_in_root is 'true', @entry is in the index root attribute @ir described + * by the attribute search context @actx and the base inode @base_ni. @ia and + * @page are NULL in this case. + * + * If @is_in_root is 'false', @entry is in the index allocation attribute and @ia + * and @page point to the index allocation block and the mapped, locked page it + * is in, respectively. @ir, @actx and @base_ni are NULL in this case. + * + * To obtain a context call ntfs_index_ctx_get(). + * + * We use this context to allow ntfs_index_lookup() to return the found index + * @entry and its @data without having to allocate a buffer and copy the @entry + * and/or its @data into it. + * + * When finished with the @entry and its @data, call ntfs_index_ctx_put() to + * free the context and other associated resources. + * + * If the index entry was modified, ntfs_index_entry_mark_dirty() + * or ntfs_index_entry_write() before the call to ntfs_index_ctx_put() to + * ensure that the changes are written to disk. + */ +struct ntfs_index_context { + struct ntfs_inode *idx_ni; + __le16 *name; + u32 name_len; + struct index_entry *entry; + __le32 cr; + void *data; + u16 data_len; + bool is_in_root; + struct index_root *ir; + struct ntfs_attr_search_ctx *actx; + struct index_block *ib; + struct ntfs_inode *ia_ni; + int parent_pos[MAX_PARENT_VCN]; + s64 parent_vcn[MAX_PARENT_VCN]; + int pindex; + bool ib_dirty; + u32 block_size; + u8 vcn_size_bits; + bool sync_write; +}; + +int ntfs_index_entry_inconsistent(struct ntfs_index_context *icx, struct ntfs_volume *vol, + const struct index_entry *ie, __le32 collation_rule, u64 inum); +struct ntfs_index_context *ntfs_index_ctx_get(struct ntfs_inode *ni, __le16 *name, + u32 name_len); +void ntfs_index_ctx_put(struct ntfs_index_context *ictx); +int ntfs_index_lookup(const void *key, const u32 key_len, + struct ntfs_index_context *ictx); + +void ntfs_index_entry_mark_dirty(struct ntfs_index_context *ictx); +int ntfs_index_add_filename(struct ntfs_inode *ni, struct file_name_attr *fn, u64 mref); +int ntfs_index_remove(struct ntfs_inode *ni, const void *key, const u32 keylen); +struct ntfs_inode *ntfs_ia_open(struct ntfs_index_context *icx, struct ntfs_inode *ni); +struct index_entry *ntfs_index_walk_down(struct index_entry *ie, struct ntfs_index_context *ictx); +struct index_entry *ntfs_index_next(struct index_entry *ie, struct ntfs_index_context *ictx); +int ntfs_index_rm(struct ntfs_index_context *icx); +void ntfs_index_ctx_reinit(struct ntfs_index_context *icx); +int ntfs_ie_add(struct ntfs_index_context *icx, struct index_entry *ie); +int ntfs_icx_ib_sync_write(struct ntfs_index_context *icx); + +#endif /* _LINUX_NTFS_INDEX_H */ diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c new file mode 100644 index 000000000000..16890d411194 --- /dev/null +++ b/fs/ntfs/inode.c @@ -0,0 +1,3787 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel inode handling. + * + * Copyright (c) 2001-2014 Anton Altaparmakov and Tuxera Inc. + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#include +#include + +#include "lcnalloc.h" +#include "time.h" +#include "ntfs.h" +#include "index.h" +#include "attrlist.h" +#include "reparse.h" +#include "ea.h" +#include "attrib.h" +#include "iomap.h" +#include "object_id.h" + +/* + * ntfs_test_inode - compare two (possibly fake) inodes for equality + * @vi: vfs inode which to test + * @data: data which is being tested with + * + * Compare the ntfs attribute embedded in the ntfs specific part of the vfs + * inode @vi for equality with the ntfs attribute @data. + * + * If searching for the normal file/directory inode, set @na->type to AT_UNUSED. + * @na->name and @na->name_len are then ignored. + * + * Return 1 if the attributes match and 0 if not. + * + * NOTE: This function runs with the inode_hash_lock spin lock held so it is not + * allowed to sleep. + */ +int ntfs_test_inode(struct inode *vi, void *data) +{ + struct ntfs_attr *na = data; + struct ntfs_inode *ni = NTFS_I(vi); + + if (vi->i_ino != na->mft_no) + return 0; + + /* If !NInoAttr(ni), @vi is a normal file or directory inode. */ + if (likely(!NInoAttr(ni))) { + /* If not looking for a normal inode this is a mismatch. */ + if (unlikely(na->type != AT_UNUSED)) + return 0; + } else { + /* A fake inode describing an attribute. */ + if (ni->type != na->type) + return 0; + if (ni->name_len != na->name_len) + return 0; + if (na->name_len && memcmp(ni->name, na->name, + na->name_len * sizeof(__le16))) + return 0; + if (!ni->ext.base_ntfs_ino) + return 0; + } + + /* Match! */ + return 1; +} + +/* + * ntfs_init_locked_inode - initialize an inode + * @vi: vfs inode to initialize + * @data: data which to initialize @vi to + * + * Initialize the vfs inode @vi with the values from the ntfs attribute @data in + * order to enable ntfs_test_inode() to do its work. + * + * If initializing the normal file/directory inode, set @na->type to AT_UNUSED. + * In that case, @na->name and @na->name_len should be set to NULL and 0, + * respectively. Although that is not strictly necessary as + * ntfs_read_locked_inode() will fill them in later. + * + * Return 0 on success and error. + * + * NOTE: This function runs with the inode->i_lock spin lock held so it is not + * allowed to sleep. (Hence the GFP_ATOMIC allocation.) + */ +static int ntfs_init_locked_inode(struct inode *vi, void *data) +{ + struct ntfs_attr *na = data; + struct ntfs_inode *ni = NTFS_I(vi); + + vi->i_ino = (unsigned long)na->mft_no; + + if (na->type == AT_INDEX_ALLOCATION) + NInoSetMstProtected(ni); + else + ni->type = na->type; + + ni->name = na->name; + ni->name_len = na->name_len; + ni->folio = NULL; + atomic_set(&ni->count, 1); + + /* If initializing a normal inode, we are done. */ + if (likely(na->type == AT_UNUSED)) + return 0; + + /* It is a fake inode. */ + NInoSetAttr(ni); + + /* + * We have I30 global constant as an optimization as it is the name + * in >99.9% of named attributes! The other <0.1% incur a GFP_ATOMIC + * allocation but that is ok. And most attributes are unnamed anyway, + * thus the fraction of named attributes with name != I30 is actually + * absolutely tiny. + */ + if (na->name_len && na->name != I30) { + unsigned int i; + + i = na->name_len * sizeof(__le16); + ni->name = kmalloc(i + sizeof(__le16), GFP_ATOMIC); + if (!ni->name) + return -ENOMEM; + memcpy(ni->name, na->name, i); + ni->name[na->name_len] = 0; + } + return 0; +} + +static int ntfs_read_locked_inode(struct inode *vi); +static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi); +static int ntfs_read_locked_index_inode(struct inode *base_vi, + struct inode *vi); + +/* + * ntfs_iget - obtain a struct inode corresponding to a specific normal inode + * @sb: super block of mounted volume + * @mft_no: mft record number / inode number to obtain + * + * Obtain the struct inode corresponding to a specific normal inode (i.e. a + * file or directory). + * + * If the inode is in the cache, it is just returned with an increased + * reference count. Otherwise, a new struct inode is allocated and initialized, + * and finally ntfs_read_locked_inode() is called to read in the inode and + * fill in the remainder of the inode structure. + * + * Return the struct inode on success. Check the return value with IS_ERR() and + * if true, the function failed and the error code is obtained from PTR_ERR(). + */ +struct inode *ntfs_iget(struct super_block *sb, u64 mft_no) +{ + struct inode *vi; + int err; + struct ntfs_attr na; + + na.mft_no = mft_no; + na.type = AT_UNUSED; + na.name = NULL; + na.name_len = 0; + + vi = iget5_locked(sb, mft_no, ntfs_test_inode, + ntfs_init_locked_inode, &na); + if (unlikely(!vi)) + return ERR_PTR(-ENOMEM); + + err = 0; + + /* If this is a freshly allocated inode, need to read it now. */ + if (inode_state_read_once(vi) & I_NEW) { + err = ntfs_read_locked_inode(vi); + unlock_new_inode(vi); + } + /* + * There is no point in keeping bad inodes around if the failure was + * due to ENOMEM. We want to be able to retry again later. + */ + if (unlikely(err == -ENOMEM)) { + iput(vi); + vi = ERR_PTR(err); + } + return vi; +} + +/* + * ntfs_attr_iget - obtain a struct inode corresponding to an attribute + * @base_vi: vfs base inode containing the attribute + * @type: attribute type + * @name: Unicode name of the attribute (NULL if unnamed) + * @name_len: length of @name in Unicode characters (0 if unnamed) + * + * Obtain the (fake) struct inode corresponding to the attribute specified by + * @type, @name, and @name_len, which is present in the base mft record + * specified by the vfs inode @base_vi. + * + * If the attribute inode is in the cache, it is just returned with an + * increased reference count. Otherwise, a new struct inode is allocated and + * initialized, and finally ntfs_read_locked_attr_inode() is called to read the + * attribute and fill in the inode structure. + * + * Note, for index allocation attributes, you need to use ntfs_index_iget() + * instead of ntfs_attr_iget() as working with indices is a lot more complex. + * + * Return the struct inode of the attribute inode on success. Check the return + * value with IS_ERR() and if true, the function failed and the error code is + * obtained from PTR_ERR(). + */ +struct inode *ntfs_attr_iget(struct inode *base_vi, __le32 type, + __le16 *name, u32 name_len) +{ + struct inode *vi; + int err; + struct ntfs_attr na; + + /* Make sure no one calls ntfs_attr_iget() for indices. */ + WARN_ON(type == AT_INDEX_ALLOCATION); + + na.mft_no = base_vi->i_ino; + na.type = type; + na.name = name; + na.name_len = name_len; + + vi = iget5_locked(base_vi->i_sb, na.mft_no, ntfs_test_inode, + ntfs_init_locked_inode, &na); + if (unlikely(!vi)) + return ERR_PTR(-ENOMEM); + err = 0; + + /* If this is a freshly allocated inode, need to read it now. */ + if (inode_state_read_once(vi) & I_NEW) { + err = ntfs_read_locked_attr_inode(base_vi, vi); + unlock_new_inode(vi); + } + /* + * There is no point in keeping bad attribute inodes around. This also + * simplifies things in that we never need to check for bad attribute + * inodes elsewhere. + */ + if (unlikely(err)) { + iput(vi); + vi = ERR_PTR(err); + } + return vi; +} + +/* + * ntfs_index_iget - obtain a struct inode corresponding to an index + * @base_vi: vfs base inode containing the index related attributes + * @name: Unicode name of the index + * @name_len: length of @name in Unicode characters + * + * Obtain the (fake) struct inode corresponding to the index specified by @name + * and @name_len, which is present in the base mft record specified by the vfs + * inode @base_vi. + * + * If the index inode is in the cache, it is just returned with an increased + * reference count. Otherwise, a new struct inode is allocated and + * initialized, and finally ntfs_read_locked_index_inode() is called to read + * the index related attributes and fill in the inode structure. + * + * Return the struct inode of the index inode on success. Check the return + * value with IS_ERR() and if true, the function failed and the error code is + * obtained from PTR_ERR(). + */ +struct inode *ntfs_index_iget(struct inode *base_vi, __le16 *name, + u32 name_len) +{ + struct inode *vi; + int err; + struct ntfs_attr na; + + na.mft_no = base_vi->i_ino; + na.type = AT_INDEX_ALLOCATION; + na.name = name; + na.name_len = name_len; + + vi = iget5_locked(base_vi->i_sb, na.mft_no, ntfs_test_inode, + ntfs_init_locked_inode, &na); + if (unlikely(!vi)) + return ERR_PTR(-ENOMEM); + + err = 0; + + /* If this is a freshly allocated inode, need to read it now. */ + if (inode_state_read_once(vi) & I_NEW) { + err = ntfs_read_locked_index_inode(base_vi, vi); + unlock_new_inode(vi); + } + /* + * There is no point in keeping bad index inodes around. This also + * simplifies things in that we never need to check for bad index + * inodes elsewhere. + */ + if (unlikely(err)) { + iput(vi); + vi = ERR_PTR(err); + } + return vi; +} + +struct inode *ntfs_alloc_big_inode(struct super_block *sb) +{ + struct ntfs_inode *ni; + + ntfs_debug("Entering."); + ni = alloc_inode_sb(sb, ntfs_big_inode_cache, GFP_NOFS); + if (likely(ni != NULL)) { + ni->state = 0; + ni->type = 0; + ni->mft_no = 0; + return VFS_I(ni); + } + ntfs_error(sb, "Allocation of NTFS big inode structure failed."); + return NULL; +} + +void ntfs_free_big_inode(struct inode *inode) +{ + kmem_cache_free(ntfs_big_inode_cache, NTFS_I(inode)); +} + +static int ntfs_non_resident_dealloc_clusters(struct ntfs_inode *ni) +{ + struct super_block *sb = ni->vol->sb; + struct ntfs_attr_search_ctx *actx; + int err = 0; + + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (!actx) + return -ENOMEM; + WARN_ON(actx->mrec->link_count != 0); + + /** + * ntfs_truncate_vfs cannot be called in evict() context due + * to some limitations, which are the @ni vfs inode is marked + * with I_FREEING, and etc. + */ + if (NInoRunlistDirty(ni)) { + err = ntfs_cluster_free_from_rl(ni->vol, ni->runlist.rl); + if (err) + ntfs_error(sb, + "Failed to free clusters. Leaving inconsistent metadata.\n"); + } + + while ((err = ntfs_attrs_walk(actx)) == 0) { + if (actx->attr->non_resident && + (!NInoRunlistDirty(ni) || actx->attr->type != AT_DATA)) { + struct runlist_element *rl; + size_t new_rl_count; + + rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr, NULL, + &new_rl_count); + if (IS_ERR(rl)) { + err = PTR_ERR(rl); + ntfs_error(sb, + "Failed to decompress runlist. Leaving inconsistent metadata.\n"); + continue; + } + + err = ntfs_cluster_free_from_rl(ni->vol, rl); + if (err) + ntfs_error(sb, + "Failed to free attribute clusters. Leaving inconsistent metadata.\n"); + kvfree(rl); + } + } + + ntfs_release_dirty_clusters(ni->vol, ni->i_dealloc_clusters); + ntfs_attr_put_search_ctx(actx); + return err; +} + +int ntfs_drop_big_inode(struct inode *inode) +{ + struct ntfs_inode *ni = NTFS_I(inode); + + if (!inode_unhashed(inode) && inode_state_read_once(inode) & I_SYNC) { + if (ni->type == AT_DATA || ni->type == AT_INDEX_ALLOCATION) { + if (!inode->i_nlink) { + struct ntfs_inode *ni = NTFS_I(inode); + + if (ni->data_size == 0) + return 0; + + /* To avoid evict_inode call simultaneously */ + atomic_inc(&inode->i_count); + spin_unlock(&inode->i_lock); + + truncate_setsize(VFS_I(ni), 0); + ntfs_truncate_vfs(VFS_I(ni), 0, 1); + + sb_start_intwrite(inode->i_sb); + i_size_write(inode, 0); + ni->allocated_size = ni->initialized_size = ni->data_size = 0; + + truncate_inode_pages_final(inode->i_mapping); + sb_end_intwrite(inode->i_sb); + + spin_lock(&inode->i_lock); + atomic_dec(&inode->i_count); + } + } + return 0; + } + + return inode_generic_drop(inode); +} + +static inline struct ntfs_inode *ntfs_alloc_extent_inode(void) +{ + struct ntfs_inode *ni; + + ntfs_debug("Entering."); + ni = kmem_cache_alloc(ntfs_inode_cache, GFP_NOFS); + if (likely(ni != NULL)) { + ni->state = 0; + return ni; + } + ntfs_error(NULL, "Allocation of NTFS inode structure failed."); + return NULL; +} + +static void ntfs_destroy_extent_inode(struct ntfs_inode *ni) +{ + ntfs_debug("Entering."); + + if (!atomic_dec_and_test(&ni->count)) + WARN_ON(1); + if (ni->folio) + folio_put(ni->folio); + kfree(ni->mrec); + kmem_cache_free(ntfs_inode_cache, ni); +} + +static struct lock_class_key attr_inode_mrec_lock_class; +static struct lock_class_key attr_list_inode_mrec_lock_class; + +/* + * The attribute runlist lock has separate locking rules from the + * normal runlist lock, so split the two lock-classes: + */ +static struct lock_class_key attr_list_rl_lock_class; + +/* + * __ntfs_init_inode - initialize ntfs specific part of an inode + * @sb: super block of mounted volume + * @ni: freshly allocated ntfs inode which to initialize + * + * Initialize an ntfs inode to defaults. + * + * NOTE: ni->mft_no, ni->state, ni->type, ni->name, and ni->name_len are left + * untouched. Make sure to initialize them elsewhere. + */ +void __ntfs_init_inode(struct super_block *sb, struct ntfs_inode *ni) +{ + ntfs_debug("Entering."); + rwlock_init(&ni->size_lock); + ni->initialized_size = ni->allocated_size = 0; + ni->seq_no = 0; + atomic_set(&ni->count, 1); + ni->vol = NTFS_SB(sb); + ntfs_init_runlist(&ni->runlist); + mutex_init(&ni->mrec_lock); + if (ni->type == AT_ATTRIBUTE_LIST) { + lockdep_set_class(&ni->mrec_lock, + &attr_list_inode_mrec_lock_class); + lockdep_set_class(&ni->runlist.lock, + &attr_list_rl_lock_class); + } else if (NInoAttr(ni)) { + lockdep_set_class(&ni->mrec_lock, + &attr_inode_mrec_lock_class); + } + + ni->folio = NULL; + ni->folio_ofs = 0; + ni->mrec = NULL; + ni->attr_list_size = 0; + ni->attr_list = NULL; + ni->itype.index.block_size = 0; + ni->itype.index.vcn_size = 0; + ni->itype.index.collation_rule = 0; + ni->itype.index.block_size_bits = 0; + ni->itype.index.vcn_size_bits = 0; + mutex_init(&ni->extent_lock); + ni->nr_extents = 0; + ni->ext.base_ntfs_ino = NULL; + ni->flags = 0; + ni->mft_lcn[0] = LCN_RL_NOT_MAPPED; + ni->mft_lcn_count = 0; + ni->target = NULL; + ni->i_dealloc_clusters = 0; +} + +/* + * Extent inodes get MFT-mapped in a nested way, while the base inode + * is still mapped. Teach this nesting to the lock validator by creating + * a separate class for nested inode's mrec_lock's: + */ +static struct lock_class_key extent_inode_mrec_lock_key; + +inline struct ntfs_inode *ntfs_new_extent_inode(struct super_block *sb, + u64 mft_no) +{ + struct ntfs_inode *ni = ntfs_alloc_extent_inode(); + + ntfs_debug("Entering."); + if (likely(ni != NULL)) { + __ntfs_init_inode(sb, ni); + lockdep_set_class(&ni->mrec_lock, &extent_inode_mrec_lock_key); + ni->mft_no = mft_no; + ni->type = AT_UNUSED; + ni->name = NULL; + ni->name_len = 0; + } + return ni; +} + +/* + * ntfs_is_extended_system_file - check if a file is in the $Extend directory + * @ctx: initialized attribute search context + * + * Search all file name attributes in the inode described by the attribute + * search context @ctx and check if any of the names are in the $Extend system + * directory. + * + * Return values: + * 3: file is $ObjId in $Extend directory + * 2: file is $Reparse in $Extend directory + * 1: file is in $Extend directory + * 0: file is not in $Extend directory + * -errno: failed to determine if the file is in the $Extend directory + */ +static int ntfs_is_extended_system_file(struct ntfs_attr_search_ctx *ctx) +{ + int nr_links, err; + + /* Restart search. */ + ntfs_attr_reinit_search_ctx(ctx); + + /* Get number of hard links. */ + nr_links = le16_to_cpu(ctx->mrec->link_count); + + /* Loop through all hard links. */ + while (!(err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, + ctx))) { + struct file_name_attr *file_name_attr; + struct attr_record *attr = ctx->attr; + u8 *p, *p2; + + nr_links--; + /* + * Maximum sanity checking as we are called on an inode that + * we suspect might be corrupt. + */ + p = (u8 *)attr + le32_to_cpu(attr->length); + if (p < (u8 *)ctx->mrec || (u8 *)p > (u8 *)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_in_use)) { +err_corrupt_attr: + ntfs_error(ctx->ntfs_ino->vol->sb, + "Corrupt file name attribute. You should run chkdsk."); + return -EIO; + } + if (attr->non_resident) { + ntfs_error(ctx->ntfs_ino->vol->sb, + "Non-resident file name. You should run chkdsk."); + return -EIO; + } + if (attr->flags) { + ntfs_error(ctx->ntfs_ino->vol->sb, + "File name with invalid flags. You should run chkdsk."); + return -EIO; + } + if (!(attr->data.resident.flags & RESIDENT_ATTR_IS_INDEXED)) { + ntfs_error(ctx->ntfs_ino->vol->sb, + "Unindexed file name. You should run chkdsk."); + return -EIO; + } + file_name_attr = (struct file_name_attr *)((u8 *)attr + + le16_to_cpu(attr->data.resident.value_offset)); + p2 = (u8 *)file_name_attr + le32_to_cpu(attr->data.resident.value_length); + if (p2 < (u8 *)attr || p2 > p) + goto err_corrupt_attr; + /* This attribute is ok, but is it in the $Extend directory? */ + if (MREF_LE(file_name_attr->parent_directory) == FILE_Extend) { + unsigned char *s; + + s = ntfs_attr_name_get(ctx->ntfs_ino->vol, + file_name_attr->file_name, + file_name_attr->file_name_length); + if (!s) + return 1; + if (!strcmp("$Reparse", s)) { + ntfs_attr_name_free(&s); + return 2; /* it's reparse point file */ + } + if (!strcmp("$ObjId", s)) { + ntfs_attr_name_free(&s); + return 3; /* it's object id file */ + } + ntfs_attr_name_free(&s); + return 1; /* YES, it's an extended system file. */ + } + } + if (unlikely(err != -ENOENT)) + return err; + if (unlikely(nr_links)) { + ntfs_error(ctx->ntfs_ino->vol->sb, + "Inode hard link count doesn't match number of name attributes. You should run chkdsk."); + return -EIO; + } + return 0; /* NO, it is not an extended system file. */ +} + +static struct lock_class_key ntfs_dir_inval_lock_key; + +void ntfs_set_vfs_operations(struct inode *inode, mode_t mode, dev_t dev) +{ + if (S_ISDIR(mode)) { + if (!NInoAttr(NTFS_I(inode))) { + inode->i_op = &ntfs_dir_inode_ops; + inode->i_fop = &ntfs_dir_ops; + } + inode->i_mapping->a_ops = &ntfs_aops; + lockdep_set_class(&inode->i_mapping->invalidate_lock, + &ntfs_dir_inval_lock_key); + } else if (S_ISLNK(mode)) { + inode->i_op = &ntfs_symlink_inode_operations; + inode->i_mapping->a_ops = &ntfs_aops; + } else if (S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) { + inode->i_op = &ntfs_special_inode_operations; + init_special_inode(inode, inode->i_mode, dev); + } else { + if (!NInoAttr(NTFS_I(inode))) { + inode->i_op = &ntfs_file_inode_ops; + inode->i_fop = &ntfs_file_ops; + } + if (inode->i_ino == FILE_MFT) + inode->i_mapping->a_ops = &ntfs_mft_aops; + else + inode->i_mapping->a_ops = &ntfs_aops; + } +} + +/* + * ntfs_read_locked_inode - read an inode from its device + * @vi: inode to read + * + * ntfs_read_locked_inode() is called from ntfs_iget() to read the inode + * described by @vi into memory from the device. + * + * The only fields in @vi that we need to/can look at when the function is + * called are i_sb, pointing to the mounted device's super block, and i_ino, + * the number of the inode to load. + * + * ntfs_read_locked_inode() maps, pins and locks the mft record number i_ino + * for reading and sets up the necessary @vi fields as well as initializing + * the ntfs inode. + * + * Q: What locks are held when the function is called? + * A: i_state has I_NEW set, hence the inode is locked, also + * i_count is set to 1, so it is not going to go away + * i_flags is set to 0 and we have no business touching it. Only an ioctl() + * is allowed to write to them. We should of course be honouring them but + * we need to do that using the IS_* macros defined in include/linux/fs.h. + * In any case ntfs_read_locked_inode() has nothing to do with i_flags. + * + * Return 0 on success and -errno on error. + */ +static int ntfs_read_locked_inode(struct inode *vi) +{ + struct ntfs_volume *vol = NTFS_SB(vi->i_sb); + struct ntfs_inode *ni = NTFS_I(vi); + struct mft_record *m; + struct attr_record *a; + struct standard_information *si; + struct ntfs_attr_search_ctx *ctx; + int err = 0; + __le16 *name = I30; + unsigned int name_len = 4, flags = 0; + int extend_sys = 0; + dev_t dev = 0; + bool vol_err = true; + + ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no); + + if (uid_valid(vol->uid)) { + vi->i_uid = vol->uid; + flags |= NTFS_VOL_UID; + } else + vi->i_uid = GLOBAL_ROOT_UID; + + if (gid_valid(vol->gid)) { + vi->i_gid = vol->gid; + flags |= NTFS_VOL_GID; + } else + vi->i_gid = GLOBAL_ROOT_GID; + + vi->i_mode = 0777; + + /* + * Initialize the ntfs specific part of @vi special casing + * FILE_MFT which we need to do at mount time. + */ + if (vi->i_ino != FILE_MFT) + ntfs_init_big_inode(vi); + + m = map_mft_record(ni); + if (IS_ERR(m)) { + err = PTR_ERR(m); + goto err_out; + } + + ctx = ntfs_attr_get_search_ctx(ni, m); + if (!ctx) { + err = -ENOMEM; + goto unm_err_out; + } + + if (!(m->flags & MFT_RECORD_IN_USE)) { + err = -ENOENT; + vol_err = false; + goto unm_err_out; + } + + if (m->base_mft_record) { + ntfs_error(vi->i_sb, "Inode is an extent inode!"); + goto unm_err_out; + } + + /* Transfer information from mft record into vfs and ntfs inodes. */ + vi->i_generation = ni->seq_no = le16_to_cpu(m->sequence_number); + + if (le16_to_cpu(m->link_count) < 1) { + ntfs_error(vi->i_sb, "Inode link count is 0!"); + goto unm_err_out; + } + set_nlink(vi, le16_to_cpu(m->link_count)); + + /* If read-only, no one gets write permissions. */ + if (IS_RDONLY(vi)) + vi->i_mode &= ~0222; + + /* + * Find the standard information attribute in the mft record. At this + * stage we haven't setup the attribute list stuff yet, so this could + * in fact fail if the standard information is in an extent record, but + * I don't think this actually ever happens. + */ + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(AT_STANDARD_INFORMATION, NULL, 0, 0, 0, NULL, 0, + ctx); + if (unlikely(err)) { + if (err == -ENOENT) + ntfs_error(vi->i_sb, "$STANDARD_INFORMATION attribute is missing."); + goto unm_err_out; + } + a = ctx->attr; + /* Get the standard information attribute value. */ + si = (struct standard_information *)((u8 *)a + + le16_to_cpu(a->data.resident.value_offset)); + + /* Transfer information from the standard information into vi. */ + /* + * Note: The i_?times do not quite map perfectly onto the NTFS times, + * but they are close enough, and in the end it doesn't really matter + * that much... + */ + /* + * mtime is the last change of the data within the file. Not changed + * when only metadata is changed, e.g. a rename doesn't affect mtime. + */ + ni->i_crtime = ntfs2utc(si->creation_time); + + inode_set_mtime_to_ts(vi, ntfs2utc(si->last_data_change_time)); + /* + * ctime is the last change of the metadata of the file. This obviously + * always changes, when mtime is changed. ctime can be changed on its + * own, mtime is then not changed, e.g. when a file is renamed. + */ + inode_set_ctime_to_ts(vi, ntfs2utc(si->last_mft_change_time)); + /* + * Last access to the data within the file. Not changed during a rename + * for example but changed whenever the file is written to. + */ + inode_set_atime_to_ts(vi, ntfs2utc(si->last_access_time)); + ni->flags = si->file_attributes; + + /* Find the attribute list attribute if present. */ + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx); + if (err) { + if (unlikely(err != -ENOENT)) { + ntfs_error(vi->i_sb, "Failed to lookup attribute list attribute."); + goto unm_err_out; + } + } else { + if (vi->i_ino == FILE_MFT) + goto skip_attr_list_load; + ntfs_debug("Attribute list found in inode 0x%llx.", ni->mft_no); + NInoSetAttrList(ni); + a = ctx->attr; + if (a->flags & ATTR_COMPRESSION_MASK) { + ntfs_error(vi->i_sb, + "Attribute list attribute is compressed."); + goto unm_err_out; + } + if (a->flags & ATTR_IS_ENCRYPTED || + a->flags & ATTR_IS_SPARSE) { + if (a->non_resident) { + ntfs_error(vi->i_sb, + "Non-resident attribute list attribute is encrypted/sparse."); + goto unm_err_out; + } + ntfs_warning(vi->i_sb, + "Resident attribute list attribute in inode 0x%llx is marked encrypted/sparse which is not true. However, Windows allows this and chkdsk does not detect or correct it so we will just ignore the invalid flags and pretend they are not set.", + ni->mft_no); + } + /* Now allocate memory for the attribute list. */ + ni->attr_list_size = (u32)ntfs_attr_size(a); + if (!ni->attr_list_size) { + ntfs_error(vi->i_sb, "Attr_list_size is zero"); + goto unm_err_out; + } + ni->attr_list = kvzalloc(ni->attr_list_size, GFP_NOFS); + if (!ni->attr_list) { + ntfs_error(vi->i_sb, + "Not enough memory to allocate buffer for attribute list."); + err = -ENOMEM; + goto unm_err_out; + } + if (a->non_resident) { + NInoSetAttrListNonResident(ni); + if (a->data.non_resident.lowest_vcn) { + ntfs_error(vi->i_sb, "Attribute list has non zero lowest_vcn."); + goto unm_err_out; + } + + /* Now load the attribute list. */ + err = load_attribute_list(ni, ni->attr_list, ni->attr_list_size); + if (err) { + ntfs_error(vi->i_sb, "Failed to load attribute list attribute."); + goto unm_err_out; + } + } else /* if (!a->non_resident) */ { + /* Now copy the attribute list. */ + memcpy(ni->attr_list, (u8 *)a + le16_to_cpu( + a->data.resident.value_offset), + le32_to_cpu( + a->data.resident.value_length)); + } + } +skip_attr_list_load: + err = ntfs_attr_lookup(AT_EA_INFORMATION, NULL, 0, 0, 0, NULL, 0, ctx); + if (!err) { + NInoSetHasEA(ni); + ntfs_ea_get_wsl_inode(vi, &dev, flags); + } + + if (m->flags & MFT_RECORD_IS_DIRECTORY) { + vi->i_mode |= S_IFDIR; + /* + * Apply the directory permissions mask set in the mount + * options. + */ + vi->i_mode &= ~vol->dmask; + /* Things break without this kludge! */ + if (vi->i_nlink > 1) + set_nlink(vi, 1); + } else { + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + unsigned int mode; + + mode = ntfs_make_symlink(ni); + if (mode) + vi->i_mode |= mode; + else { + vi->i_mode &= ~S_IFLNK; + vi->i_mode |= S_IFREG; + } + } else + vi->i_mode |= S_IFREG; + /* Apply the file permissions mask set in the mount options. */ + vi->i_mode &= ~vol->fmask; + } + + /* + * If an attribute list is present we now have the attribute list value + * in ntfs_ino->attr_list and it is ntfs_ino->attr_list_size bytes. + */ + if (S_ISDIR(vi->i_mode)) { + struct index_root *ir; + u8 *ir_end, *index_end; + +view_index_meta: + /* It is a directory, find index root attribute. */ + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx); + if (unlikely(err)) { + if (err == -ENOENT) + ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is missing."); + goto unm_err_out; + } + a = ctx->attr; + /* Set up the state. */ + if (unlikely(a->non_resident)) { + ntfs_error(vol->sb, + "$INDEX_ROOT attribute is not resident."); + goto unm_err_out; + } + /* Ensure the attribute name is placed before the value. */ + if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >= + le16_to_cpu(a->data.resident.value_offset)))) { + ntfs_error(vol->sb, + "$INDEX_ROOT attribute name is placed after the attribute value."); + goto unm_err_out; + } + /* + * Compressed/encrypted index root just means that the newly + * created files in that directory should be created compressed/ + * encrypted. However index root cannot be both compressed and + * encrypted. + */ + if (a->flags & ATTR_COMPRESSION_MASK) { + NInoSetCompressed(ni); + ni->flags |= FILE_ATTR_COMPRESSED; + } + if (a->flags & ATTR_IS_ENCRYPTED) { + if (a->flags & ATTR_COMPRESSION_MASK) { + ntfs_error(vi->i_sb, "Found encrypted and compressed attribute."); + goto unm_err_out; + } + NInoSetEncrypted(ni); + ni->flags |= FILE_ATTR_ENCRYPTED; + } + if (a->flags & ATTR_IS_SPARSE) { + NInoSetSparse(ni); + ni->flags |= FILE_ATTR_SPARSE_FILE; + } + ir = (struct index_root *)((u8 *)a + + le16_to_cpu(a->data.resident.value_offset)); + ir_end = (u8 *)ir + le32_to_cpu(a->data.resident.value_length); + index_end = (u8 *)&ir->index + + le32_to_cpu(ir->index.index_length); + if (index_end > ir_end) { + ntfs_error(vi->i_sb, "Directory index is corrupt."); + goto unm_err_out; + } + + if (extend_sys) { + if (ir->type) { + ntfs_error(vi->i_sb, "Indexed attribute is not zero."); + goto unm_err_out; + } + } else { + if (ir->type != AT_FILE_NAME) { + ntfs_error(vi->i_sb, "Indexed attribute is not $FILE_NAME."); + goto unm_err_out; + } + + if (ir->collation_rule != COLLATION_FILE_NAME) { + ntfs_error(vi->i_sb, + "Index collation rule is not COLLATION_FILE_NAME."); + goto unm_err_out; + } + } + + ni->itype.index.collation_rule = ir->collation_rule; + ni->itype.index.block_size = le32_to_cpu(ir->index_block_size); + if (ni->itype.index.block_size & + (ni->itype.index.block_size - 1)) { + ntfs_error(vi->i_sb, "Index block size (%u) is not a power of two.", + ni->itype.index.block_size); + goto unm_err_out; + } + if (ni->itype.index.block_size > PAGE_SIZE) { + ntfs_error(vi->i_sb, + "Index block size (%u) > PAGE_SIZE (%ld) is not supported.", + ni->itype.index.block_size, + PAGE_SIZE); + err = -EOPNOTSUPP; + goto unm_err_out; + } + if (ni->itype.index.block_size < NTFS_BLOCK_SIZE) { + ntfs_error(vi->i_sb, + "Index block size (%u) < NTFS_BLOCK_SIZE (%i) is not supported.", + ni->itype.index.block_size, + NTFS_BLOCK_SIZE); + err = -EOPNOTSUPP; + goto unm_err_out; + } + ni->itype.index.block_size_bits = + ffs(ni->itype.index.block_size) - 1; + /* Determine the size of a vcn in the directory index. */ + if (vol->cluster_size <= ni->itype.index.block_size) { + ni->itype.index.vcn_size = vol->cluster_size; + ni->itype.index.vcn_size_bits = vol->cluster_size_bits; + } else { + ni->itype.index.vcn_size = vol->sector_size; + ni->itype.index.vcn_size_bits = vol->sector_size_bits; + } + + /* Setup the index allocation attribute, even if not present. */ + ni->type = AT_INDEX_ROOT; + ni->name = name; + ni->name_len = name_len; + vi->i_size = ni->initialized_size = ni->data_size = + le32_to_cpu(a->data.resident.value_length); + ni->allocated_size = (ni->data_size + 7) & ~7; + /* We are done with the mft record, so we release it. */ + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(ni); + m = NULL; + ctx = NULL; + /* Setup the operations for this inode. */ + ntfs_set_vfs_operations(vi, S_IFDIR, 0); + if (ir->index.flags & LARGE_INDEX) + NInoSetIndexAllocPresent(ni); + } else { + /* It is a file. */ + ntfs_attr_reinit_search_ctx(ctx); + + /* Setup the data attribute, even if not present. */ + ni->type = AT_DATA; + ni->name = AT_UNNAMED; + ni->name_len = 0; + + /* Find first extent of the unnamed data attribute. */ + err = ntfs_attr_lookup(AT_DATA, NULL, 0, 0, 0, NULL, 0, ctx); + if (unlikely(err)) { + vi->i_size = ni->initialized_size = + ni->allocated_size = 0; + if (err != -ENOENT) { + ntfs_error(vi->i_sb, "Failed to lookup $DATA attribute."); + goto unm_err_out; + } + /* + * FILE_Secure does not have an unnamed $DATA + * attribute, so we special case it here. + */ + if (vi->i_ino == FILE_Secure) + goto no_data_attr_special_case; + /* + * Most if not all the system files in the $Extend + * system directory do not have unnamed data + * attributes so we need to check if the parent + * directory of the file is FILE_Extend and if it is + * ignore this error. To do this we need to get the + * name of this inode from the mft record as the name + * contains the back reference to the parent directory. + */ + extend_sys = ntfs_is_extended_system_file(ctx); + if (extend_sys > 0) { + if (m->flags & MFT_RECORD_IS_VIEW_INDEX) { + if (extend_sys == 2) { + name = reparse_index_name; + name_len = 2; + goto view_index_meta; + } else if (extend_sys == 3) { + name = objid_index_name; + name_len = 2; + goto view_index_meta; + } + } + goto no_data_attr_special_case; + } + + err = extend_sys; + ntfs_error(vi->i_sb, "$DATA attribute is missing, err : %d", err); + goto unm_err_out; + } + a = ctx->attr; + /* Setup the state. */ + if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) { + if (a->flags & ATTR_COMPRESSION_MASK) { + NInoSetCompressed(ni); + ni->flags |= FILE_ATTR_COMPRESSED; + if (vol->cluster_size > 4096) { + ntfs_error(vi->i_sb, + "Found compressed data but compression is disabled due to cluster size (%i) > 4kiB.", + vol->cluster_size); + goto unm_err_out; + } + if ((a->flags & ATTR_COMPRESSION_MASK) + != ATTR_IS_COMPRESSED) { + ntfs_error(vi->i_sb, + "Found unknown compression method or corrupt file."); + goto unm_err_out; + } + } + if (a->flags & ATTR_IS_SPARSE) { + NInoSetSparse(ni); + ni->flags |= FILE_ATTR_SPARSE_FILE; + } + } + if (a->flags & ATTR_IS_ENCRYPTED) { + if (NInoCompressed(ni)) { + ntfs_error(vi->i_sb, "Found encrypted and compressed data."); + goto unm_err_out; + } + NInoSetEncrypted(ni); + ni->flags |= FILE_ATTR_ENCRYPTED; + } + if (a->non_resident) { + NInoSetNonResident(ni); + if (NInoCompressed(ni) || NInoSparse(ni)) { + if (NInoCompressed(ni) && + a->data.non_resident.compression_unit != 4) { + ntfs_error(vi->i_sb, + "Found non-standard compression unit (%u instead of 4). Cannot handle this.", + a->data.non_resident.compression_unit); + err = -EOPNOTSUPP; + goto unm_err_out; + } + + if (NInoSparse(ni) && + a->data.non_resident.compression_unit && + a->data.non_resident.compression_unit != + vol->sparse_compression_unit) { + ntfs_error(vi->i_sb, + "Found non-standard compression unit (%u instead of 0 or %d). Cannot handle this.", + a->data.non_resident.compression_unit, + vol->sparse_compression_unit); + err = -EOPNOTSUPP; + goto unm_err_out; + } + + + if (a->data.non_resident.compression_unit) { + ni->itype.compressed.block_size = 1U << + (a->data.non_resident.compression_unit + + vol->cluster_size_bits); + ni->itype.compressed.block_size_bits = + ffs(ni->itype.compressed.block_size) - 1; + ni->itype.compressed.block_clusters = + 1U << a->data.non_resident.compression_unit; + } else { + ni->itype.compressed.block_size = 0; + ni->itype.compressed.block_size_bits = + 0; + ni->itype.compressed.block_clusters = + 0; + } + ni->itype.compressed.size = le64_to_cpu( + a->data.non_resident.compressed_size); + } + if (a->data.non_resident.lowest_vcn) { + ntfs_error(vi->i_sb, + "First extent of $DATA attribute has non zero lowest_vcn."); + goto unm_err_out; + } + vi->i_size = ni->data_size = le64_to_cpu(a->data.non_resident.data_size); + ni->initialized_size = le64_to_cpu(a->data.non_resident.initialized_size); + ni->allocated_size = le64_to_cpu(a->data.non_resident.allocated_size); + } else { /* Resident attribute. */ + vi->i_size = ni->data_size = ni->initialized_size = le32_to_cpu( + a->data.resident.value_length); + ni->allocated_size = le32_to_cpu(a->length) - + le16_to_cpu( + a->data.resident.value_offset); + if (vi->i_size > ni->allocated_size) { + ntfs_error(vi->i_sb, + "Resident data attribute is corrupt (size exceeds allocation)."); + goto unm_err_out; + } + } +no_data_attr_special_case: + /* We are done with the mft record, so we release it. */ + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(ni); + m = NULL; + ctx = NULL; + /* Setup the operations for this inode. */ + ntfs_set_vfs_operations(vi, vi->i_mode, dev); + } + + if (NVolSysImmutable(vol) && (ni->flags & FILE_ATTR_SYSTEM) && + !S_ISFIFO(vi->i_mode) && !S_ISSOCK(vi->i_mode) && !S_ISLNK(vi->i_mode)) + vi->i_flags |= S_IMMUTABLE; + + /* + * The number of 512-byte blocks used on disk (for stat). This is in so + * far inaccurate as it doesn't account for any named streams or other + * special non-resident attributes, but that is how Windows works, too, + * so we are at least consistent with Windows, if not entirely + * consistent with the Linux Way. Doing it the Linux Way would cause a + * significant slowdown as it would involve iterating over all + * attributes in the mft record and adding the allocated/compressed + * sizes of all non-resident attributes present to give us the Linux + * correct size that should go into i_blocks (after division by 512). + */ + if (S_ISREG(vi->i_mode) && (NInoCompressed(ni) || NInoSparse(ni))) + vi->i_blocks = ni->itype.compressed.size >> 9; + else + vi->i_blocks = ni->allocated_size >> 9; + + ntfs_debug("Done."); + return 0; +unm_err_out: + if (!err) + err = -EIO; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (m) + unmap_mft_record(ni); +err_out: + if (err != -EOPNOTSUPP && err != -ENOMEM && vol_err == true) { + ntfs_error(vol->sb, + "Failed with error code %i. Marking corrupt inode 0x%llx as bad. Run chkdsk.", + err, ni->mft_no); + NVolSetErrors(vol); + } + return err; +} + +/* + * ntfs_read_locked_attr_inode - read an attribute inode from its base inode + * @base_vi: base inode + * @vi: attribute inode to read + * + * ntfs_read_locked_attr_inode() is called from ntfs_attr_iget() to read the + * attribute inode described by @vi into memory from the base mft record + * described by @base_ni. + * + * ntfs_read_locked_attr_inode() maps, pins and locks the base inode for + * reading and looks up the attribute described by @vi before setting up the + * necessary fields in @vi as well as initializing the ntfs inode. + * + * Q: What locks are held when the function is called? + * A: i_state has I_NEW set, hence the inode is locked, also + * i_count is set to 1, so it is not going to go away + * + * Return 0 on success and -errno on error. + * + * Note this cannot be called for AT_INDEX_ALLOCATION. + */ +static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) +{ + struct ntfs_volume *vol = NTFS_SB(vi->i_sb); + struct ntfs_inode *ni = NTFS_I(vi), *base_ni = NTFS_I(base_vi); + struct mft_record *m; + struct attr_record *a; + struct ntfs_attr_search_ctx *ctx; + int err = 0; + + ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no); + + ntfs_init_big_inode(vi); + + /* Just mirror the values from the base inode. */ + vi->i_uid = base_vi->i_uid; + vi->i_gid = base_vi->i_gid; + set_nlink(vi, base_vi->i_nlink); + inode_set_mtime_to_ts(vi, inode_get_mtime(base_vi)); + inode_set_ctime_to_ts(vi, inode_get_ctime(base_vi)); + inode_set_atime_to_ts(vi, inode_get_atime(base_vi)); + vi->i_generation = ni->seq_no = base_ni->seq_no; + + /* Set inode type to zero but preserve permissions. */ + vi->i_mode = base_vi->i_mode & ~S_IFMT; + + m = map_mft_record(base_ni); + if (IS_ERR(m)) { + err = PTR_ERR(m); + goto err_out; + } + ctx = ntfs_attr_get_search_ctx(base_ni, m); + if (!ctx) { + err = -ENOMEM; + goto unm_err_out; + } + /* Find the attribute. */ + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) + goto unm_err_out; + a = ctx->attr; + if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) { + if (a->flags & ATTR_COMPRESSION_MASK) { + NInoSetCompressed(ni); + ni->flags |= FILE_ATTR_COMPRESSED; + if ((ni->type != AT_DATA) || (ni->type == AT_DATA && + ni->name_len)) { + ntfs_error(vi->i_sb, + "Found compressed non-data or named data attribute."); + goto unm_err_out; + } + if (vol->cluster_size > 4096) { + ntfs_error(vi->i_sb, + "Found compressed attribute but compression is disabled due to cluster size (%i) > 4kiB.", + vol->cluster_size); + goto unm_err_out; + } + if ((a->flags & ATTR_COMPRESSION_MASK) != + ATTR_IS_COMPRESSED) { + ntfs_error(vi->i_sb, "Found unknown compression method."); + goto unm_err_out; + } + } + /* + * The compressed/sparse flag set in an index root just means + * to compress all files. + */ + if (NInoMstProtected(ni) && ni->type != AT_INDEX_ROOT) { + ntfs_error(vi->i_sb, + "Found mst protected attribute but the attribute is %s.", + NInoCompressed(ni) ? "compressed" : "sparse"); + goto unm_err_out; + } + if (a->flags & ATTR_IS_SPARSE) { + NInoSetSparse(ni); + ni->flags |= FILE_ATTR_SPARSE_FILE; + } + } + if (a->flags & ATTR_IS_ENCRYPTED) { + if (NInoCompressed(ni)) { + ntfs_error(vi->i_sb, "Found encrypted and compressed data."); + goto unm_err_out; + } + /* + * The encryption flag set in an index root just means to + * encrypt all files. + */ + if (NInoMstProtected(ni) && ni->type != AT_INDEX_ROOT) { + ntfs_error(vi->i_sb, + "Found mst protected attribute but the attribute is encrypted."); + goto unm_err_out; + } + if (ni->type != AT_DATA) { + ntfs_error(vi->i_sb, + "Found encrypted non-data attribute."); + goto unm_err_out; + } + NInoSetEncrypted(ni); + ni->flags |= FILE_ATTR_ENCRYPTED; + } + if (!a->non_resident) { + /* Ensure the attribute name is placed before the value. */ + if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >= + le16_to_cpu(a->data.resident.value_offset)))) { + ntfs_error(vol->sb, + "Attribute name is placed after the attribute value."); + goto unm_err_out; + } + if (NInoMstProtected(ni)) { + ntfs_error(vi->i_sb, + "Found mst protected attribute but the attribute is resident."); + goto unm_err_out; + } + vi->i_size = ni->initialized_size = ni->data_size = le32_to_cpu( + a->data.resident.value_length); + ni->allocated_size = le32_to_cpu(a->length) - + le16_to_cpu(a->data.resident.value_offset); + if (vi->i_size > ni->allocated_size) { + ntfs_error(vi->i_sb, + "Resident attribute is corrupt (size exceeds allocation)."); + goto unm_err_out; + } + } else { + NInoSetNonResident(ni); + /* + * Ensure the attribute name is placed before the mapping pairs + * array. + */ + if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >= + le16_to_cpu( + a->data.non_resident.mapping_pairs_offset)))) { + ntfs_error(vol->sb, + "Attribute name is placed after the mapping pairs array."); + goto unm_err_out; + } + if (NInoCompressed(ni) || NInoSparse(ni)) { + if (NInoCompressed(ni) && a->data.non_resident.compression_unit != 4) { + ntfs_error(vi->i_sb, + "Found non-standard compression unit (%u instead of 4). Cannot handle this.", + a->data.non_resident.compression_unit); + err = -EOPNOTSUPP; + goto unm_err_out; + } + if (a->data.non_resident.compression_unit) { + ni->itype.compressed.block_size = 1U << + (a->data.non_resident.compression_unit + + vol->cluster_size_bits); + ni->itype.compressed.block_size_bits = + ffs(ni->itype.compressed.block_size) - 1; + ni->itype.compressed.block_clusters = 1U << + a->data.non_resident.compression_unit; + } else { + ni->itype.compressed.block_size = 0; + ni->itype.compressed.block_size_bits = 0; + ni->itype.compressed.block_clusters = 0; + } + ni->itype.compressed.size = le64_to_cpu( + a->data.non_resident.compressed_size); + } + if (a->data.non_resident.lowest_vcn) { + ntfs_error(vi->i_sb, "First extent of attribute has non-zero lowest_vcn."); + goto unm_err_out; + } + vi->i_size = ni->data_size = le64_to_cpu(a->data.non_resident.data_size); + ni->initialized_size = le64_to_cpu(a->data.non_resident.initialized_size); + ni->allocated_size = le64_to_cpu(a->data.non_resident.allocated_size); + } + vi->i_mapping->a_ops = &ntfs_aops; + if ((NInoCompressed(ni) || NInoSparse(ni)) && ni->type != AT_INDEX_ROOT) + vi->i_blocks = ni->itype.compressed.size >> 9; + else + vi->i_blocks = ni->allocated_size >> 9; + /* + * Make sure the base inode does not go away and attach it to the + * attribute inode. + */ + if (!igrab(base_vi)) { + err = -ENOENT; + goto unm_err_out; + } + ni->ext.base_ntfs_ino = base_ni; + ni->nr_extents = -1; + + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(base_ni); + + ntfs_debug("Done."); + return 0; + +unm_err_out: + if (!err) + err = -EIO; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(base_ni); +err_out: + if (err != -ENOENT) + ntfs_error(vol->sb, + "Failed with error code %i while reading attribute inode (mft_no 0x%llx, type 0x%x, name_len %i). Marking corrupt inode and base inode 0x%llx as bad. Run chkdsk.", + err, ni->mft_no, ni->type, ni->name_len, + base_ni->mft_no); + if (err != -ENOENT && err != -ENOMEM) + NVolSetErrors(vol); + return err; +} + +/* + * ntfs_read_locked_index_inode - read an index inode from its base inode + * @base_vi: base inode + * @vi: index inode to read + * + * ntfs_read_locked_index_inode() is called from ntfs_index_iget() to read the + * index inode described by @vi into memory from the base mft record described + * by @base_ni. + * + * ntfs_read_locked_index_inode() maps, pins and locks the base inode for + * reading and looks up the attributes relating to the index described by @vi + * before setting up the necessary fields in @vi as well as initializing the + * ntfs inode. + * + * Note, index inodes are essentially attribute inodes (NInoAttr() is true) + * with the attribute type set to AT_INDEX_ALLOCATION. Apart from that, they + * are setup like directory inodes since directories are a special case of + * indices ao they need to be treated in much the same way. Most importantly, + * for small indices the index allocation attribute might not actually exist. + * However, the index root attribute always exists but this does not need to + * have an inode associated with it and this is why we define a new inode type + * index. Also, like for directories, we need to have an attribute inode for + * the bitmap attribute corresponding to the index allocation attribute and we + * can store this in the appropriate field of the inode, just like we do for + * normal directory inodes. + * + * Q: What locks are held when the function is called? + * A: i_state has I_NEW set, hence the inode is locked, also + * i_count is set to 1, so it is not going to go away + * + * Return 0 on success and -errno on error. + */ +static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) +{ + loff_t bvi_size; + struct ntfs_volume *vol = NTFS_SB(vi->i_sb); + struct ntfs_inode *ni = NTFS_I(vi), *base_ni = NTFS_I(base_vi), *bni; + struct inode *bvi; + struct mft_record *m; + struct attr_record *a; + struct ntfs_attr_search_ctx *ctx; + struct index_root *ir; + u8 *ir_end, *index_end; + int err = 0; + + ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no); + lockdep_assert_held(&base_ni->mrec_lock); + + ntfs_init_big_inode(vi); + /* Just mirror the values from the base inode. */ + vi->i_uid = base_vi->i_uid; + vi->i_gid = base_vi->i_gid; + set_nlink(vi, base_vi->i_nlink); + inode_set_mtime_to_ts(vi, inode_get_mtime(base_vi)); + inode_set_ctime_to_ts(vi, inode_get_ctime(base_vi)); + inode_set_atime_to_ts(vi, inode_get_atime(base_vi)); + vi->i_generation = ni->seq_no = base_ni->seq_no; + /* Set inode type to zero but preserve permissions. */ + vi->i_mode = base_vi->i_mode & ~S_IFMT; + /* Map the mft record for the base inode. */ + m = map_mft_record(base_ni); + if (IS_ERR(m)) { + err = PTR_ERR(m); + goto err_out; + } + ctx = ntfs_attr_get_search_ctx(base_ni, m); + if (!ctx) { + err = -ENOMEM; + goto unm_err_out; + } + /* Find the index root attribute. */ + err = ntfs_attr_lookup(AT_INDEX_ROOT, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) { + if (err == -ENOENT) + ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is missing."); + goto unm_err_out; + } + a = ctx->attr; + /* Set up the state. */ + if (unlikely(a->non_resident)) { + ntfs_error(vol->sb, "$INDEX_ROOT attribute is not resident."); + goto unm_err_out; + } + /* Ensure the attribute name is placed before the value. */ + if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >= + le16_to_cpu(a->data.resident.value_offset)))) { + ntfs_error(vol->sb, + "$INDEX_ROOT attribute name is placed after the attribute value."); + goto unm_err_out; + } + + ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset)); + ir_end = (u8 *)ir + le32_to_cpu(a->data.resident.value_length); + index_end = (u8 *)&ir->index + le32_to_cpu(ir->index.index_length); + if (index_end > ir_end) { + ntfs_error(vi->i_sb, "Index is corrupt."); + goto unm_err_out; + } + + ni->itype.index.collation_rule = ir->collation_rule; + ntfs_debug("Index collation rule is 0x%x.", + le32_to_cpu(ir->collation_rule)); + ni->itype.index.block_size = le32_to_cpu(ir->index_block_size); + if (!is_power_of_2(ni->itype.index.block_size)) { + ntfs_error(vi->i_sb, "Index block size (%u) is not a power of two.", + ni->itype.index.block_size); + goto unm_err_out; + } + if (ni->itype.index.block_size > PAGE_SIZE) { + ntfs_error(vi->i_sb, "Index block size (%u) > PAGE_SIZE (%ld) is not supported.", + ni->itype.index.block_size, PAGE_SIZE); + err = -EOPNOTSUPP; + goto unm_err_out; + } + if (ni->itype.index.block_size < NTFS_BLOCK_SIZE) { + ntfs_error(vi->i_sb, + "Index block size (%u) < NTFS_BLOCK_SIZE (%i) is not supported.", + ni->itype.index.block_size, NTFS_BLOCK_SIZE); + err = -EOPNOTSUPP; + goto unm_err_out; + } + ni->itype.index.block_size_bits = ffs(ni->itype.index.block_size) - 1; + /* Determine the size of a vcn in the index. */ + if (vol->cluster_size <= ni->itype.index.block_size) { + ni->itype.index.vcn_size = vol->cluster_size; + ni->itype.index.vcn_size_bits = vol->cluster_size_bits; + } else { + ni->itype.index.vcn_size = vol->sector_size; + ni->itype.index.vcn_size_bits = vol->sector_size_bits; + } + + /* Find index allocation attribute. */ + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(AT_INDEX_ALLOCATION, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) { + if (err == -ENOENT) { + /* No index allocation. */ + vi->i_size = ni->initialized_size = ni->allocated_size = 0; + /* We are done with the mft record, so we release it. */ + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(base_ni); + m = NULL; + ctx = NULL; + goto skip_large_index_stuff; + } else + ntfs_error(vi->i_sb, "Failed to lookup $INDEX_ALLOCATION attribute."); + goto unm_err_out; + } + NInoSetIndexAllocPresent(ni); + NInoSetNonResident(ni); + ni->type = AT_INDEX_ALLOCATION; + + a = ctx->attr; + if (!a->non_resident) { + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is resident."); + goto unm_err_out; + } + /* + * Ensure the attribute name is placed before the mapping pairs array. + */ + if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >= + le16_to_cpu(a->data.non_resident.mapping_pairs_offset)))) { + ntfs_error(vol->sb, + "$INDEX_ALLOCATION attribute name is placed after the mapping pairs array."); + goto unm_err_out; + } + if (a->flags & ATTR_IS_ENCRYPTED) { + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is encrypted."); + goto unm_err_out; + } + if (a->flags & ATTR_IS_SPARSE) { + ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is sparse."); + goto unm_err_out; + } + if (a->flags & ATTR_COMPRESSION_MASK) { + ntfs_error(vi->i_sb, + "$INDEX_ALLOCATION attribute is compressed."); + goto unm_err_out; + } + if (a->data.non_resident.lowest_vcn) { + ntfs_error(vi->i_sb, + "First extent of $INDEX_ALLOCATION attribute has non zero lowest_vcn."); + goto unm_err_out; + } + vi->i_size = ni->data_size = le64_to_cpu(a->data.non_resident.data_size); + ni->initialized_size = le64_to_cpu(a->data.non_resident.initialized_size); + ni->allocated_size = le64_to_cpu(a->data.non_resident.allocated_size); + /* + * We are done with the mft record, so we release it. Otherwise + * we would deadlock in ntfs_attr_iget(). + */ + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(base_ni); + m = NULL; + ctx = NULL; + /* Get the index bitmap attribute inode. */ + bvi = ntfs_attr_iget(base_vi, AT_BITMAP, ni->name, ni->name_len); + if (IS_ERR(bvi)) { + ntfs_error(vi->i_sb, "Failed to get bitmap attribute."); + err = PTR_ERR(bvi); + goto unm_err_out; + } + bni = NTFS_I(bvi); + if (NInoCompressed(bni) || NInoEncrypted(bni) || + NInoSparse(bni)) { + ntfs_error(vi->i_sb, + "$BITMAP attribute is compressed and/or encrypted and/or sparse."); + goto iput_unm_err_out; + } + /* Consistency check bitmap size vs. index allocation size. */ + bvi_size = i_size_read(bvi); + if ((bvi_size << 3) < (vi->i_size >> ni->itype.index.block_size_bits)) { + ntfs_error(vi->i_sb, + "Index bitmap too small (0x%llx) for index allocation (0x%llx).", + bvi_size << 3, vi->i_size); + goto iput_unm_err_out; + } + iput(bvi); +skip_large_index_stuff: + /* Setup the operations for this index inode. */ + ntfs_set_vfs_operations(vi, S_IFDIR, 0); + vi->i_blocks = ni->allocated_size >> 9; + /* + * Make sure the base inode doesn't go away and attach it to the + * index inode. + */ + if (!igrab(base_vi)) + goto unm_err_out; + ni->ext.base_ntfs_ino = base_ni; + ni->nr_extents = -1; + + ntfs_debug("Done."); + return 0; +iput_unm_err_out: + iput(bvi); +unm_err_out: + if (!err) + err = -EIO; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (m) + unmap_mft_record(base_ni); +err_out: + ntfs_error(vi->i_sb, + "Failed with error code %i while reading index inode (mft_no 0x%llx, name_len %i.", + err, ni->mft_no, ni->name_len); + if (err != -EOPNOTSUPP && err != -ENOMEM) + NVolSetErrors(vol); + return err; +} + +/* + * load_attribute_list_mount - load an attribute list into memory + * @vol: ntfs volume from which to read + * @rl: runlist of the attribute list + * @al_start: destination buffer + * @size: size of the destination buffer in bytes + * @initialized_size: initialized size of the attribute list + * + * Walk the runlist @rl and load all clusters from it copying them into + * the linear buffer @al. The maximum number of bytes copied to @al is @size + * bytes. Note, @size does not need to be a multiple of the cluster size. If + * @initialized_size is less than @size, the region in @al between + * @initialized_size and @size will be zeroed and not read from disk. + * + * Return 0 on success or -errno on error. + */ +static int load_attribute_list_mount(struct ntfs_volume *vol, + struct runlist_element *rl, u8 *al_start, const s64 size, + const s64 initialized_size) +{ + s64 lcn; + u8 *al = al_start; + u8 *al_end = al + initialized_size; + struct super_block *sb; + int err = 0; + loff_t rl_byte_off, rl_byte_len; + + ntfs_debug("Entering."); + if (!vol || !rl || !al || size <= 0 || initialized_size < 0 || + initialized_size > size) + return -EINVAL; + if (!initialized_size) { + memset(al, 0, size); + return 0; + } + sb = vol->sb; + + /* Read all clusters specified by the runlist one run at a time. */ + while (rl->length) { + lcn = ntfs_rl_vcn_to_lcn(rl, rl->vcn); + ntfs_debug("Reading vcn = 0x%llx, lcn = 0x%llx.", + (unsigned long long)rl->vcn, + (unsigned long long)lcn); + /* The attribute list cannot be sparse. */ + if (lcn < 0) { + ntfs_error(sb, "ntfs_rl_vcn_to_lcn() failed. Cannot read attribute list."); + return -EIO; + } + + rl_byte_off = ntfs_cluster_to_bytes(vol, lcn); + rl_byte_len = ntfs_cluster_to_bytes(vol, rl->length); + + if (al + rl_byte_len > al_end) + rl_byte_len = al_end - al; + + err = ntfs_bdev_read(sb->s_bdev, al, rl_byte_off, + round_up(rl_byte_len, SECTOR_SIZE)); + if (err) { + ntfs_error(sb, "Cannot read attribute list."); + return -EIO; + } + + if (al + rl_byte_len >= al_end) { + if (initialized_size < size) + goto initialize; + goto done; + } + + al += rl_byte_len; + rl++; + } + if (initialized_size < size) { +initialize: + memset(al_start + initialized_size, 0, size - initialized_size); + } +done: + return err; +} + +/* + * The MFT inode has special locking, so teach the lock validator + * about this by splitting off the locking rules of the MFT from + * the locking rules of other inodes. The MFT inode can never be + * accessed from the VFS side (or even internally), only by the + * map_mft functions. + */ +static struct lock_class_key mft_ni_runlist_lock_key, mft_ni_mrec_lock_key; + +/* + * ntfs_read_inode_mount - special read_inode for mount time use only + * @vi: inode to read + * + * Read inode FILE_MFT at mount time, only called with super_block lock + * held from within the read_super() code path. + * + * This function exists because when it is called the page cache for $MFT/$DATA + * is not initialized and hence we cannot get at the contents of mft records + * by calling map_mft_record*(). + * + * Further it needs to cope with the circular references problem, i.e. cannot + * load any attributes other than $ATTRIBUTE_LIST until $DATA is loaded, because + * we do not know where the other extent mft records are yet and again, because + * we cannot call map_mft_record*() yet. Obviously this applies only when an + * attribute list is actually present in $MFT inode. + * + * We solve these problems by starting with the $DATA attribute before anything + * else and iterating using ntfs_attr_lookup($DATA) over all extents. As each + * extent is found, we ntfs_mapping_pairs_decompress() including the implied + * ntfs_runlists_merge(). Each step of the iteration necessarily provides + * sufficient information for the next step to complete. + * + * This should work but there are two possible pit falls (see inline comments + * below), but only time will tell if they are real pits or just smoke... + */ +int ntfs_read_inode_mount(struct inode *vi) +{ + s64 next_vcn, last_vcn, highest_vcn; + struct super_block *sb = vi->i_sb; + struct ntfs_volume *vol = NTFS_SB(sb); + struct ntfs_inode *ni = NTFS_I(vi); + struct mft_record *m = NULL; + struct attr_record *a; + struct ntfs_attr_search_ctx *ctx; + unsigned int i, nr_blocks; + int err; + size_t new_rl_count; + + ntfs_debug("Entering."); + + /* Initialize the ntfs specific part of @vi. */ + ntfs_init_big_inode(vi); + + + /* Setup the data attribute. It is special as it is mst protected. */ + NInoSetNonResident(ni); + NInoSetMstProtected(ni); + NInoSetSparseDisabled(ni); + ni->type = AT_DATA; + ni->name = AT_UNNAMED; + ni->name_len = 0; + /* + * This sets up our little cheat allowing us to reuse the async read io + * completion handler for directories. + */ + ni->itype.index.block_size = vol->mft_record_size; + ni->itype.index.block_size_bits = vol->mft_record_size_bits; + + /* Very important! Needed to be able to call map_mft_record*(). */ + vol->mft_ino = vi; + + /* Allocate enough memory to read the first mft record. */ + if (vol->mft_record_size > 64 * 1024) { + ntfs_error(sb, "Unsupported mft record size %i (max 64kiB).", + vol->mft_record_size); + goto err_out; + } + + i = vol->mft_record_size; + if (i < sb->s_blocksize) + i = sb->s_blocksize; + + m = kzalloc(i, GFP_NOFS); + if (!m) { + ntfs_error(sb, "Failed to allocate buffer for $MFT record 0."); + goto err_out; + } + + /* Determine the first block of the $MFT/$DATA attribute. */ + nr_blocks = ntfs_bytes_to_sector(vol, vol->mft_record_size); + if (!nr_blocks) + nr_blocks = 1; + + /* Load $MFT/$DATA's first mft record. */ + err = ntfs_bdev_read(sb->s_bdev, (char *)m, + ntfs_cluster_to_bytes(vol, vol->mft_lcn), i); + if (err) { + ntfs_error(sb, "Device read failed."); + goto err_out; + } + + if (le32_to_cpu(m->bytes_allocated) != vol->mft_record_size) { + ntfs_error(sb, "Incorrect mft record size %u in superblock, should be %u.", + le32_to_cpu(m->bytes_allocated), vol->mft_record_size); + goto err_out; + } + + /* Apply the mst fixups. */ + if (post_read_mst_fixup((struct ntfs_record *)m, vol->mft_record_size)) { + ntfs_error(sb, "MST fixup failed. $MFT is corrupt."); + goto err_out; + } + + if (ntfs_mft_record_check(vol, m, FILE_MFT)) { + ntfs_error(sb, "ntfs_mft_record_check failed. $MFT is corrupt."); + goto err_out; + } + + /* Need this to sanity check attribute list references to $MFT. */ + vi->i_generation = ni->seq_no = le16_to_cpu(m->sequence_number); + + /* Provides read_folio() for map_mft_record(). */ + vi->i_mapping->a_ops = &ntfs_mft_aops; + + ctx = ntfs_attr_get_search_ctx(ni, m); + if (!ctx) { + err = -ENOMEM; + goto err_out; + } + + /* Find the attribute list attribute if present. */ + err = ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx); + if (err) { + if (unlikely(err != -ENOENT)) { + ntfs_error(sb, + "Failed to lookup attribute list attribute. You should run chkdsk."); + goto put_err_out; + } + } else /* if (!err) */ { + struct attr_list_entry *al_entry, *next_al_entry; + u8 *al_end; + static const char *es = " Not allowed. $MFT is corrupt. You should run chkdsk."; + + ntfs_debug("Attribute list attribute found in $MFT."); + NInoSetAttrList(ni); + a = ctx->attr; + if (a->flags & ATTR_COMPRESSION_MASK) { + ntfs_error(sb, + "Attribute list attribute is compressed.%s", + es); + goto put_err_out; + } + if (a->flags & ATTR_IS_ENCRYPTED || + a->flags & ATTR_IS_SPARSE) { + if (a->non_resident) { + ntfs_error(sb, + "Non-resident attribute list attribute is encrypted/sparse.%s", + es); + goto put_err_out; + } + ntfs_warning(sb, + "Resident attribute list attribute in $MFT system file is marked encrypted/sparse which is not true. However, Windows allows this and chkdsk does not detect or correct it so we will just ignore the invalid flags and pretend they are not set."); + } + /* Now allocate memory for the attribute list. */ + ni->attr_list_size = (u32)ntfs_attr_size(a); + if (!ni->attr_list_size) { + ntfs_error(sb, "Attr_list_size is zero"); + goto put_err_out; + } + ni->attr_list = kvzalloc(round_up(ni->attr_list_size, SECTOR_SIZE), + GFP_NOFS); + if (!ni->attr_list) { + ntfs_error(sb, "Not enough memory to allocate buffer for attribute list."); + goto put_err_out; + } + if (a->non_resident) { + struct runlist_element *rl; + size_t new_rl_count; + + NInoSetAttrListNonResident(ni); + if (a->data.non_resident.lowest_vcn) { + ntfs_error(sb, + "Attribute list has non zero lowest_vcn. $MFT is corrupt. You should run chkdsk."); + goto put_err_out; + } + + rl = ntfs_mapping_pairs_decompress(vol, a, NULL, &new_rl_count); + if (IS_ERR(rl)) { + err = PTR_ERR(rl); + ntfs_error(sb, + "Mapping pairs decompression failed with error code %i.", + -err); + goto put_err_out; + } + + err = load_attribute_list_mount(vol, rl, ni->attr_list, ni->attr_list_size, + le64_to_cpu(a->data.non_resident.initialized_size)); + kvfree(rl); + if (err) { + ntfs_error(sb, + "Failed to load attribute list with error code %i.", + -err); + goto put_err_out; + } + } else /* if (!ctx.attr->non_resident) */ { + /* Now copy the attribute list. */ + memcpy(ni->attr_list, (u8 *)a + le16_to_cpu( + a->data.resident.value_offset), + le32_to_cpu(a->data.resident.value_length)); + } + /* The attribute list is now setup in memory. */ + al_entry = (struct attr_list_entry *)ni->attr_list; + al_end = (u8 *)al_entry + ni->attr_list_size; + for (;; al_entry = next_al_entry) { + /* Out of bounds check. */ + if ((u8 *)al_entry < ni->attr_list || + (u8 *)al_entry > al_end) + goto em_put_err_out; + /* Catch the end of the attribute list. */ + if ((u8 *)al_entry == al_end) + goto em_put_err_out; + if (!al_entry->length) + goto em_put_err_out; + if ((u8 *)al_entry + 6 > al_end || + (u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end) + goto em_put_err_out; + next_al_entry = (struct attr_list_entry *)((u8 *)al_entry + + le16_to_cpu(al_entry->length)); + if (le32_to_cpu(al_entry->type) > le32_to_cpu(AT_DATA)) + goto em_put_err_out; + if (al_entry->type != AT_DATA) + continue; + /* We want an unnamed attribute. */ + if (al_entry->name_length) + goto em_put_err_out; + /* Want the first entry, i.e. lowest_vcn == 0. */ + if (al_entry->lowest_vcn) + goto em_put_err_out; + /* First entry has to be in the base mft record. */ + if (MREF_LE(al_entry->mft_reference) != vi->i_ino) { + /* MFT references do not match, logic fails. */ + ntfs_error(sb, + "BUG: The first $DATA extent of $MFT is not in the base mft record."); + goto put_err_out; + } else { + /* Sequence numbers must match. */ + if (MSEQNO_LE(al_entry->mft_reference) != + ni->seq_no) + goto em_put_err_out; + /* Got it. All is ok. We can stop now. */ + break; + } + } + } + + ntfs_attr_reinit_search_ctx(ctx); + + /* Now load all attribute extents. */ + a = NULL; + next_vcn = last_vcn = highest_vcn = 0; + while (!(err = ntfs_attr_lookup(AT_DATA, NULL, 0, 0, next_vcn, NULL, 0, + ctx))) { + struct runlist_element *nrl; + + /* Cache the current attribute. */ + a = ctx->attr; + /* $MFT must be non-resident. */ + if (!a->non_resident) { + ntfs_error(sb, + "$MFT must be non-resident but a resident extent was found. $MFT is corrupt. Run chkdsk."); + goto put_err_out; + } + /* $MFT must be uncompressed and unencrypted. */ + if (a->flags & ATTR_COMPRESSION_MASK || + a->flags & ATTR_IS_ENCRYPTED || + a->flags & ATTR_IS_SPARSE) { + ntfs_error(sb, + "$MFT must be uncompressed, non-sparse, and unencrypted but a compressed/sparse/encrypted extent was found. $MFT is corrupt. Run chkdsk."); + goto put_err_out; + } + /* + * Decompress the mapping pairs array of this extent and merge + * the result into the existing runlist. No need for locking + * as we have exclusive access to the inode at this time and we + * are a mount in progress task, too. + */ + nrl = ntfs_mapping_pairs_decompress(vol, a, &ni->runlist, + &new_rl_count); + if (IS_ERR(nrl)) { + ntfs_error(sb, + "ntfs_mapping_pairs_decompress() failed with error code %ld.", + PTR_ERR(nrl)); + goto put_err_out; + } + ni->runlist.rl = nrl; + ni->runlist.count = new_rl_count; + + /* Are we in the first extent? */ + if (!next_vcn) { + if (a->data.non_resident.lowest_vcn) { + ntfs_error(sb, + "First extent of $DATA attribute has non zero lowest_vcn. $MFT is corrupt. You should run chkdsk."); + goto put_err_out; + } + /* Get the last vcn in the $DATA attribute. */ + last_vcn = ntfs_bytes_to_cluster(vol, + le64_to_cpu(a->data.non_resident.allocated_size)); + /* Fill in the inode size. */ + vi->i_size = le64_to_cpu(a->data.non_resident.data_size); + ni->initialized_size = le64_to_cpu(a->data.non_resident.initialized_size); + ni->allocated_size = le64_to_cpu(a->data.non_resident.allocated_size); + /* + * Verify the number of mft records does not exceed + * 2^32 - 1. + */ + if ((vi->i_size >> vol->mft_record_size_bits) >= + (1ULL << 32)) { + ntfs_error(sb, "$MFT is too big! Aborting."); + goto put_err_out; + } + /* + * We have got the first extent of the runlist for + * $MFT which means it is now relatively safe to call + * the normal ntfs_read_inode() function. + * Complete reading the inode, this will actually + * re-read the mft record for $MFT, this time entering + * it into the page cache with which we complete the + * kick start of the volume. It should be safe to do + * this now as the first extent of $MFT/$DATA is + * already known and we would hope that we don't need + * further extents in order to find the other + * attributes belonging to $MFT. Only time will tell if + * this is really the case. If not we will have to play + * magic at this point, possibly duplicating a lot of + * ntfs_read_inode() at this point. We will need to + * ensure we do enough of its work to be able to call + * ntfs_read_inode() on extents of $MFT/$DATA. But lets + * hope this never happens... + */ + err = ntfs_read_locked_inode(vi); + if (err) { + ntfs_error(sb, "ntfs_read_inode() of $MFT failed.\n"); + ntfs_attr_put_search_ctx(ctx); + /* Revert to the safe super operations. */ + kfree(m); + return -1; + } + /* + * Re-initialize some specifics about $MFT's inode as + * ntfs_read_inode() will have set up the default ones. + */ + /* Set uid and gid to root. */ + vi->i_uid = GLOBAL_ROOT_UID; + vi->i_gid = GLOBAL_ROOT_GID; + /* Regular file. No access for anyone. */ + vi->i_mode = S_IFREG; + /* No VFS initiated operations allowed for $MFT. */ + vi->i_op = &ntfs_empty_inode_ops; + vi->i_fop = &ntfs_empty_file_ops; + } + + /* Get the lowest vcn for the next extent. */ + highest_vcn = le64_to_cpu(a->data.non_resident.highest_vcn); + next_vcn = highest_vcn + 1; + + /* Only one extent or error, which we catch below. */ + if (next_vcn <= 0) + break; + + /* Avoid endless loops due to corruption. */ + if (next_vcn < le64_to_cpu(a->data.non_resident.lowest_vcn)) { + ntfs_error(sb, "$MFT has corrupt attribute list attribute. Run chkdsk."); + goto put_err_out; + } + } + if (err != -ENOENT) { + ntfs_error(sb, "Failed to lookup $MFT/$DATA attribute extent. Run chkdsk.\n"); + goto put_err_out; + } + if (!a) { + ntfs_error(sb, "$MFT/$DATA attribute not found. $MFT is corrupt. Run chkdsk."); + goto put_err_out; + } + if (highest_vcn && highest_vcn != last_vcn - 1) { + ntfs_error(sb, "Failed to load the complete runlist for $MFT/$DATA. Run chkdsk."); + ntfs_debug("highest_vcn = 0x%llx, last_vcn - 1 = 0x%llx", + (unsigned long long)highest_vcn, + (unsigned long long)last_vcn - 1); + goto put_err_out; + } + ntfs_attr_put_search_ctx(ctx); + ntfs_debug("Done."); + kfree(m); + + /* + * Split the locking rules of the MFT inode from the + * locking rules of other inodes: + */ + lockdep_set_class(&ni->runlist.lock, &mft_ni_runlist_lock_key); + lockdep_set_class(&ni->mrec_lock, &mft_ni_mrec_lock_key); + + return 0; + +em_put_err_out: + ntfs_error(sb, + "Couldn't find first extent of $DATA attribute in attribute list. $MFT is corrupt. Run chkdsk."); +put_err_out: + ntfs_attr_put_search_ctx(ctx); +err_out: + ntfs_error(sb, "Failed. Marking inode as bad."); + kfree(m); + return -1; +} + +static void __ntfs_clear_inode(struct ntfs_inode *ni) +{ + /* Free all alocated memory. */ + if (NInoNonResident(ni) && ni->runlist.rl) { + kvfree(ni->runlist.rl); + ni->runlist.rl = NULL; + } + + if (ni->attr_list) { + kvfree(ni->attr_list); + ni->attr_list = NULL; + } + + if (ni->name_len && ni->name != I30 && + ni->name != reparse_index_name && + ni->name != objid_index_name) { + WARN_ON(!ni->name); + kfree(ni->name); + } +} + +void ntfs_clear_extent_inode(struct ntfs_inode *ni) +{ + ntfs_debug("Entering for inode 0x%llx.", ni->mft_no); + + WARN_ON(NInoAttr(ni)); + WARN_ON(ni->nr_extents != -1); + + __ntfs_clear_inode(ni); + ntfs_destroy_extent_inode(ni); +} + +static int ntfs_delete_base_inode(struct ntfs_inode *ni) +{ + struct super_block *sb = ni->vol->sb; + int err; + + if (NInoAttr(ni) || ni->nr_extents == -1) + return 0; + + err = ntfs_non_resident_dealloc_clusters(ni); + + /* + * Deallocate extent mft records and free extent inodes. + * No need to lock as no one else has a reference. + */ + while (ni->nr_extents) { + err = ntfs_mft_record_free(ni->vol, *(ni->ext.extent_ntfs_inos)); + if (err) + ntfs_error(sb, + "Failed to free extent MFT record. Leaving inconsistent metadata.\n"); + ntfs_inode_close(*(ni->ext.extent_ntfs_inos)); + } + + /* Deallocate base mft record */ + err = ntfs_mft_record_free(ni->vol, ni); + if (err) + ntfs_error(sb, "Failed to free base MFT record. Leaving inconsistent metadata.\n"); + return err; +} + +/* + * ntfs_evict_big_inode - clean up the ntfs specific part of an inode + * @vi: vfs inode pending annihilation + * + * When the VFS is going to remove an inode from memory, ntfs_clear_big_inode() + * is called, which deallocates all memory belonging to the NTFS specific part + * of the inode and returns. + * + * If the MFT record is dirty, we commit it before doing anything else. + */ +void ntfs_evict_big_inode(struct inode *vi) +{ + struct ntfs_inode *ni = NTFS_I(vi); + + truncate_inode_pages_final(&vi->i_data); + + if (!vi->i_nlink) { + if (!NInoAttr(ni)) { + /* Never called with extent inodes */ + WARN_ON(ni->nr_extents == -1); + ntfs_delete_base_inode(ni); + } + goto release; + } + + if (NInoDirty(ni)) { + /* Committing the inode also commits all extent inodes. */ + ntfs_commit_inode(vi); + + if (NInoDirty(ni)) { + ntfs_debug("Failed to commit dirty inode 0x%llx. Losing data!", + ni->mft_no); + NInoClearAttrListDirty(ni); + NInoClearDirty(ni); + } + } + + /* No need to lock at this stage as no one else has a reference. */ + if (ni->nr_extents > 0) { + int i; + + for (i = 0; i < ni->nr_extents; i++) { + if (ni->ext.extent_ntfs_inos[i]) + ntfs_clear_extent_inode(ni->ext.extent_ntfs_inos[i]); + } + ni->nr_extents = 0; + kvfree(ni->ext.extent_ntfs_inos); + } + +release: + clear_inode(vi); + __ntfs_clear_inode(ni); + + if (NInoAttr(ni)) { + /* Release the base inode if we are holding it. */ + if (ni->nr_extents == -1) { + iput(VFS_I(ni->ext.base_ntfs_ino)); + ni->nr_extents = 0; + ni->ext.base_ntfs_ino = NULL; + } + } + + if (!atomic_dec_and_test(&ni->count)) + WARN_ON(1); + if (ni->folio) + folio_put(ni->folio); + kfree(ni->mrec); + kvfree(ni->target); +} + +/* + * ntfs_show_options - show mount options in /proc/mounts + * @sf: seq_file in which to write our mount options + * @root: root of the mounted tree whose mount options to display + * + * Called by the VFS once for each mounted ntfs volume when someone reads + * /proc/mounts in order to display the NTFS specific mount options of each + * mount. The mount options of fs specified by @root are written to the seq file + * @sf and success is returned. + */ +int ntfs_show_options(struct seq_file *sf, struct dentry *root) +{ + struct ntfs_volume *vol = NTFS_SB(root->d_sb); + int i; + + if (uid_valid(vol->uid)) + seq_printf(sf, ",uid=%i", from_kuid_munged(&init_user_ns, vol->uid)); + if (gid_valid(vol->gid)) + seq_printf(sf, ",gid=%i", from_kgid_munged(&init_user_ns, vol->gid)); + if (vol->fmask == vol->dmask) + seq_printf(sf, ",umask=0%o", vol->fmask); + else { + seq_printf(sf, ",fmask=0%o", vol->fmask); + seq_printf(sf, ",dmask=0%o", vol->dmask); + } + seq_printf(sf, ",iocharset=%s", vol->nls_map->charset); + if (NVolCaseSensitive(vol)) + seq_puts(sf, ",case_sensitive"); + else + seq_puts(sf, ",nocase"); + if (NVolShowSystemFiles(vol)) + seq_puts(sf, ",show_sys_files,showmeta"); + for (i = 0; on_errors_arr[i].val; i++) { + if (on_errors_arr[i].val == vol->on_errors) + seq_printf(sf, ",errors=%s", on_errors_arr[i].str); + } + seq_printf(sf, ",mft_zone_multiplier=%i", vol->mft_zone_multiplier); + if (NVolSysImmutable(vol)) + seq_puts(sf, ",sys_immutable"); + if (!NVolShowHiddenFiles(vol)) + seq_puts(sf, ",nohidden"); + if (NVolHideDotFiles(vol)) + seq_puts(sf, ",hide_dot_files"); + if (NVolCheckWindowsNames(vol)) + seq_puts(sf, ",windows_names"); + if (NVolDiscard(vol)) + seq_puts(sf, ",discard"); + if (NVolDisableSparse(vol)) + seq_puts(sf, ",disable_sparse"); + if (vol->sb->s_flags & SB_POSIXACL) + seq_puts(sf, ",acl"); + return 0; +} + +int ntfs_extend_initialized_size(struct inode *vi, const loff_t offset, + const loff_t new_size, bool bsync) +{ + struct ntfs_inode *ni = NTFS_I(vi); + loff_t old_init_size; + unsigned long flags; + int err; + + read_lock_irqsave(&ni->size_lock, flags); + old_init_size = ni->initialized_size; + read_unlock_irqrestore(&ni->size_lock, flags); + + if (!NInoNonResident(ni)) + return -EINVAL; + if (old_init_size >= new_size) + return 0; + + err = ntfs_attr_map_whole_runlist(ni); + if (err) + return err; + + if (!NInoCompressed(ni) && old_init_size < offset) { + err = iomap_zero_range(vi, old_init_size, + offset - old_init_size, + NULL, &ntfs_seek_iomap_ops, + &ntfs_iomap_folio_ops, NULL); + if (err) + return err; + if (bsync) + err = filemap_write_and_wait_range(vi->i_mapping, + old_init_size, + offset - 1); + } + + + mutex_lock(&ni->mrec_lock); + err = ntfs_attr_set_initialized_size(ni, new_size); + mutex_unlock(&ni->mrec_lock); + if (err) + truncate_setsize(vi, old_init_size); + return err; +} + +int ntfs_truncate_vfs(struct inode *vi, loff_t new_size, loff_t i_size) +{ + struct ntfs_inode *ni = NTFS_I(vi); + int err; + + mutex_lock(&ni->mrec_lock); + err = __ntfs_attr_truncate_vfs(ni, new_size, i_size); + mutex_unlock(&ni->mrec_lock); + if (err < 0) + return err; + + inode_set_mtime_to_ts(vi, inode_set_ctime_current(vi)); + return 0; +} + +/* + * ntfs_inode_sync_standard_information - update standard information attribute + * @vi: inode to update standard information + * @m: mft record + * + * Return 0 on success or -errno on error. + */ +static int ntfs_inode_sync_standard_information(struct inode *vi, struct mft_record *m) +{ + struct ntfs_inode *ni = NTFS_I(vi); + struct ntfs_attr_search_ctx *ctx; + struct standard_information *si; + __le64 nt; + int err = 0; + bool modified = false; + + /* Update the access times in the standard information attribute. */ + ctx = ntfs_attr_get_search_ctx(ni, m); + if (unlikely(!ctx)) + return -ENOMEM; + err = ntfs_attr_lookup(AT_STANDARD_INFORMATION, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) { + ntfs_attr_put_search_ctx(ctx); + return err; + } + si = (struct standard_information *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)); + if (si->file_attributes != ni->flags) { + si->file_attributes = ni->flags; + modified = true; + } + + /* Update the creation times if they have changed. */ + nt = utc2ntfs(ni->i_crtime); + if (si->creation_time != nt) { + ntfs_debug("Updating creation time for inode 0x%llx: old = 0x%llx, new = 0x%llx", + ni->mft_no, le64_to_cpu(si->creation_time), + le64_to_cpu(nt)); + si->creation_time = nt; + modified = true; + } + + /* Update the access times if they have changed. */ + nt = utc2ntfs(inode_get_mtime(vi)); + if (si->last_data_change_time != nt) { + ntfs_debug("Updating mtime for inode 0x%llx: old = 0x%llx, new = 0x%llx", + ni->mft_no, le64_to_cpu(si->last_data_change_time), + le64_to_cpu(nt)); + si->last_data_change_time = nt; + modified = true; + } + + nt = utc2ntfs(inode_get_ctime(vi)); + if (si->last_mft_change_time != nt) { + ntfs_debug("Updating ctime for inode 0x%llx: old = 0x%llx, new = 0x%llx", + ni->mft_no, le64_to_cpu(si->last_mft_change_time), + le64_to_cpu(nt)); + si->last_mft_change_time = nt; + modified = true; + } + nt = utc2ntfs(inode_get_atime(vi)); + if (si->last_access_time != nt) { + ntfs_debug("Updating atime for inode 0x%llx: old = 0x%llx, new = 0x%llx", + ni->mft_no, + le64_to_cpu(si->last_access_time), + le64_to_cpu(nt)); + si->last_access_time = nt; + modified = true; + } + + /* + * If we just modified the standard information attribute we need to + * mark the mft record it is in dirty. We do this manually so that + * mark_inode_dirty() is not called which would redirty the inode and + * hence result in an infinite loop of trying to write the inode. + * There is no need to mark the base inode nor the base mft record + * dirty, since we are going to write this mft record below in any case + * and the base mft record may actually not have been modified so it + * might not need to be written out. + * NOTE: It is not a problem when the inode for $MFT itself is being + * written out as ntfs_mft_mark_dirty() will only set I_DIRTY_PAGES + * on the $MFT inode and hence ntfs_write_inode() will not be + * re-invoked because of it which in turn is ok since the dirtied mft + * record will be cleaned and written out to disk below, i.e. before + * this function returns. + */ + if (modified) + NInoSetDirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + + return err; +} + +/* + * ntfs_inode_sync_filename - update FILE_NAME attributes + * @ni: ntfs inode to update FILE_NAME attributes + * + * Update all FILE_NAME attributes for inode @ni in the index. + * + * Return 0 on success or error. + */ +int ntfs_inode_sync_filename(struct ntfs_inode *ni) +{ + struct inode *index_vi; + struct super_block *sb = VFS_I(ni)->i_sb; + struct ntfs_attr_search_ctx *ctx = NULL; + struct ntfs_index_context *ictx; + struct ntfs_inode *index_ni; + struct file_name_attr *fn; + struct file_name_attr *fnx; + struct reparse_point *rpp; + __le32 reparse_tag; + int err = 0; + unsigned long flags; + + ntfs_debug("Entering for inode %llu\n", ni->mft_no); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -ENOMEM; + + /* Collect the reparse tag, if any */ + reparse_tag = cpu_to_le32(0); + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + if (!ntfs_attr_lookup(AT_REPARSE_POINT, NULL, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + rpp = (struct reparse_point *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)); + reparse_tag = rpp->reparse_tag; + } + ntfs_attr_reinit_search_ctx(ctx); + } + + /* Walk through all FILE_NAME attributes and update them. */ + while (!(err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx))) { + fn = (struct file_name_attr *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)); + if (MREF_LE(fn->parent_directory) == ni->mft_no) + continue; + + index_vi = ntfs_iget(sb, MREF_LE(fn->parent_directory)); + if (IS_ERR(index_vi)) { + ntfs_error(sb, "Failed to open inode %lld with index", + (long long)MREF_LE(fn->parent_directory)); + continue; + } + + index_ni = NTFS_I(index_vi); + + mutex_lock_nested(&index_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + if (NInoBeingDeleted(ni)) { + iput(index_vi); + mutex_unlock(&index_ni->mrec_lock); + continue; + } + + ictx = ntfs_index_ctx_get(index_ni, I30, 4); + if (!ictx) { + ntfs_error(sb, "Failed to get index ctx, inode %llu", + index_ni->mft_no); + iput(index_vi); + mutex_unlock(&index_ni->mrec_lock); + continue; + } + + err = ntfs_index_lookup(fn, sizeof(struct file_name_attr), ictx); + if (err) { + ntfs_debug("Index lookup failed, inode %llu", + index_ni->mft_no); + ntfs_index_ctx_put(ictx); + iput(index_vi); + mutex_unlock(&index_ni->mrec_lock); + continue; + } + /* Update flags and file size. */ + fnx = (struct file_name_attr *)ictx->data; + fnx->file_attributes = + (fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS) | + (ni->flags & FILE_ATTR_VALID_FLAGS); + if (ctx->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fnx->data_size = fnx->allocated_size = 0; + else { + read_lock_irqsave(&ni->size_lock, flags); + if (NInoSparse(ni) || NInoCompressed(ni)) + fnx->allocated_size = cpu_to_le64(ni->itype.compressed.size); + else + fnx->allocated_size = cpu_to_le64(ni->allocated_size); + fnx->data_size = cpu_to_le64(ni->data_size); + + /* + * The file name record has also to be fixed if some + * attribute update implied the unnamed data to be + * made non-resident + */ + fn->allocated_size = fnx->allocated_size; + fn->data_size = fnx->data_size; + read_unlock_irqrestore(&ni->size_lock, flags); + } + + /* update or clear the reparse tag in the index */ + fnx->type.rp.reparse_point_tag = reparse_tag; + fnx->creation_time = fn->creation_time; + fnx->last_data_change_time = fn->last_data_change_time; + fnx->last_mft_change_time = fn->last_mft_change_time; + fnx->last_access_time = fn->last_access_time; + ntfs_index_entry_mark_dirty(ictx); + ntfs_icx_ib_sync_write(ictx); + NInoSetDirty(ctx->ntfs_ino); + ntfs_index_ctx_put(ictx); + mutex_unlock(&index_ni->mrec_lock); + iput(index_vi); + } + /* Check for real error occurred. */ + if (err != -ENOENT) { + ntfs_error(sb, "Attribute lookup failed, err : %d, inode %llu", err, + ni->mft_no); + } else + err = 0; + + ntfs_attr_put_search_ctx(ctx); + return err; +} + +int ntfs_get_block_mft_record(struct ntfs_inode *mft_ni, struct ntfs_inode *ni) +{ + s64 vcn; + struct runlist_element *rl; + + if (ni->mft_lcn[0] != LCN_RL_NOT_MAPPED) + return 0; + + vcn = (s64)ni->mft_no << mft_ni->vol->mft_record_size_bits >> + mft_ni->vol->cluster_size_bits; + + rl = mft_ni->runlist.rl; + if (!rl) { + ntfs_error(mft_ni->vol->sb, "$MFT runlist is not present"); + return -EIO; + } + + /* Seek to element containing target vcn. */ + while (rl->length && rl[1].vcn <= vcn) + rl++; + ni->mft_lcn[0] = ntfs_rl_vcn_to_lcn(rl, vcn); + ni->mft_lcn_count = 1; + + if (mft_ni->vol->cluster_size < mft_ni->vol->mft_record_size && + (rl->length - (vcn - rl->vcn)) <= 1) { + rl++; + ni->mft_lcn[1] = ntfs_rl_vcn_to_lcn(rl, vcn + 1); + ni->mft_lcn_count++; + } + return 0; +} + +/* + * __ntfs_write_inode - write out a dirty inode + * @vi: inode to write out + * @sync: if true, write out synchronously + * + * Write out a dirty inode to disk including any extent inodes if present. + * + * If @sync is true, commit the inode to disk and wait for io completion. This + * is done using write_mft_record(). + * + * If @sync is false, just schedule the write to happen but do not wait for i/o + * completion. + * + * Return 0 on success and -errno on error. + */ +int __ntfs_write_inode(struct inode *vi, int sync) +{ + struct ntfs_inode *ni = NTFS_I(vi); + struct ntfs_inode *mft_ni = NTFS_I(ni->vol->mft_ino); + struct mft_record *m; + int err = 0; + bool need_iput = false; + + ntfs_debug("Entering for %sinode 0x%llx.", NInoAttr(ni) ? "attr " : "", + ni->mft_no); + + if (NVolShutdown(ni->vol)) + return -EIO; + + /* + * Dirty attribute inodes are written via their real inodes so just + * clean them here. Access time updates are taken care off when the + * real inode is written. + */ + if (NInoAttr(ni) || ni->nr_extents == -1) { + NInoClearDirty(ni); + ntfs_debug("Done."); + return 0; + } + + /* igrab prevents vi from being evicted while mrec_lock is hold. */ + if (igrab(vi) != NULL) + need_iput = true; + + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + /* Map, pin, and lock the mft record belonging to the inode. */ + m = map_mft_record(ni); + if (IS_ERR(m)) { + mutex_unlock(&ni->mrec_lock); + err = PTR_ERR(m); + goto err_out; + } + + if (NInoNonResident(ni) && NInoRunlistDirty(ni)) { + down_write(&ni->runlist.lock); + err = ntfs_attr_update_mapping_pairs(ni, 0); + if (!err) + NInoClearRunlistDirty(ni); + up_write(&ni->runlist.lock); + } + + err = ntfs_inode_sync_standard_information(vi, m); + if (err) + goto unm_err_out; + + /* + * when being umounted and inodes are evicted, write_inode() + * is called with all inodes being marked with I_FREEING. + * then ntfs_inode_sync_filename() waits infinitly because + * of ntfs_iget. This situation happens only where sync_filesysem() + * from umount fails because of a disk unplug and etc. + * the absent of SB_ACTIVE means umounting. + */ + if ((vi->i_sb->s_flags & SB_ACTIVE) && NInoTestClearFileNameDirty(ni)) + ntfs_inode_sync_filename(ni); + + /* Now the access times are updated, write the base mft record. */ + if (NInoDirty(ni)) { + down_read(&mft_ni->runlist.lock); + err = ntfs_get_block_mft_record(mft_ni, ni); + up_read(&mft_ni->runlist.lock); + if (err) + goto unm_err_out; + + err = write_mft_record(ni, m, sync); + if (err) + ntfs_error(vi->i_sb, "write_mft_record failed, err : %d\n", err); + } + unmap_mft_record(ni); + + /* Map any unmapped extent mft records with LCNs. */ + down_read(&mft_ni->runlist.lock); + mutex_lock(&ni->extent_lock); + if (ni->nr_extents > 0) { + int i; + + for (i = 0; i < ni->nr_extents; i++) { + err = ntfs_get_block_mft_record(mft_ni, + ni->ext.extent_ntfs_inos[i]); + if (err) { + mutex_unlock(&ni->extent_lock); + up_read(&mft_ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + goto err_out; + } + } + } + mutex_unlock(&ni->extent_lock); + up_read(&mft_ni->runlist.lock); + + /* Write all attached extent mft records. */ + mutex_lock(&ni->extent_lock); + if (ni->nr_extents > 0) { + struct ntfs_inode **extent_nis = ni->ext.extent_ntfs_inos; + int i; + + ntfs_debug("Writing %i extent inodes.", ni->nr_extents); + for (i = 0; i < ni->nr_extents; i++) { + struct ntfs_inode *tni = extent_nis[i]; + + if (NInoDirty(tni)) { + struct mft_record *tm; + int ret; + + mutex_lock(&tni->mrec_lock); + tm = map_mft_record(tni); + if (IS_ERR(tm)) { + mutex_unlock(&tni->mrec_lock); + if (!err || err == -ENOMEM) + err = PTR_ERR(tm); + continue; + } + + ret = write_mft_record(tni, tm, sync); + unmap_mft_record(tni); + mutex_unlock(&tni->mrec_lock); + + if (unlikely(ret)) { + if (!err || err == -ENOMEM) + err = ret; + } + } + } + } + mutex_unlock(&ni->extent_lock); + mutex_unlock(&ni->mrec_lock); + + if (unlikely(err)) + goto err_out; + if (need_iput) + iput(vi); + ntfs_debug("Done."); + return 0; +unm_err_out: + unmap_mft_record(ni); + mutex_unlock(&ni->mrec_lock); +err_out: + if (err == -ENOMEM) + mark_inode_dirty(vi); + else { + ntfs_error(vi->i_sb, "Failed (error %i): Run chkdsk.", -err); + NVolSetErrors(ni->vol); + } + if (need_iput) + iput(vi); + return err; +} + +/* + * ntfs_extent_inode_open - load an extent inode and attach it to its base + * @base_ni: base ntfs inode + * @mref: mft reference of the extent inode to load (in little endian) + * + * First check if the extent inode @mref is already attached to the base ntfs + * inode @base_ni, and if so, return a pointer to the attached extent inode. + * + * If the extent inode is not already attached to the base inode, allocate an + * ntfs_inode structure and initialize it for the given inode @mref. @mref + * specifies the inode number / mft record to read, including the sequence + * number, which can be 0 if no sequence number checking is to be performed. + * + * Then, allocate a buffer for the mft record, read the mft record from the + * volume @base_ni->vol, and attach it to the ntfs_inode structure (->mrec). + * The mft record is mst deprotected and sanity checked for validity and we + * abort if deprotection or checks fail. + * + * Finally attach the ntfs inode to its base inode @base_ni and return a + * pointer to the ntfs_inode structure on success or NULL on error, with errno + * set to the error code. + * + * Note, extent inodes are never closed directly. They are automatically + * disposed off by the closing of the base inode. + */ +static struct ntfs_inode *ntfs_extent_inode_open(struct ntfs_inode *base_ni, + const __le64 mref) +{ + u64 mft_no = MREF_LE(mref); + struct ntfs_inode *ni = NULL; + struct ntfs_inode **extent_nis; + int i; + struct mft_record *ni_mrec; + struct super_block *sb; + + if (!base_ni) + return NULL; + + sb = base_ni->vol->sb; + ntfs_debug("Opening extent inode %llu (base mft record %llu).\n", + mft_no, base_ni->mft_no); + + /* Is the extent inode already open and attached to the base inode? */ + if (base_ni->nr_extents > 0) { + extent_nis = base_ni->ext.extent_ntfs_inos; + for (i = 0; i < base_ni->nr_extents; i++) { + u16 seq_no; + + ni = extent_nis[i]; + if (mft_no != ni->mft_no) + continue; + ni_mrec = map_mft_record(ni); + if (IS_ERR(ni_mrec)) { + ntfs_error(sb, "failed to map mft record for %llu", + ni->mft_no); + goto out; + } + /* Verify the sequence number if given. */ + seq_no = MSEQNO_LE(mref); + if (seq_no && + seq_no != le16_to_cpu(ni_mrec->sequence_number)) { + ntfs_error(sb, "Found stale extent mft reference mft=%llu", + ni->mft_no); + unmap_mft_record(ni); + goto out; + } + unmap_mft_record(ni); + goto out; + } + } + /* Wasn't there, we need to load the extent inode. */ + ni = ntfs_new_extent_inode(base_ni->vol->sb, mft_no); + if (!ni) + goto out; + + ni->seq_no = (u16)MSEQNO_LE(mref); + ni->nr_extents = -1; + ni->ext.base_ntfs_ino = base_ni; + /* Attach extent inode to base inode, reallocating memory if needed. */ + if (!(base_ni->nr_extents & 3)) { + i = (base_ni->nr_extents + 4) * sizeof(struct ntfs_inode *); + + extent_nis = kvzalloc(i, GFP_NOFS); + if (!extent_nis) + goto err_out; + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->ext.extent_ntfs_inos, + i - 4 * sizeof(struct ntfs_inode *)); + kvfree(base_ni->ext.extent_ntfs_inos); + } + base_ni->ext.extent_ntfs_inos = extent_nis; + } + base_ni->ext.extent_ntfs_inos[base_ni->nr_extents++] = ni; + +out: + ntfs_debug("\n"); + return ni; +err_out: + ntfs_destroy_ext_inode(ni); + ni = NULL; + goto out; +} + +/* + * ntfs_inode_attach_all_extents - attach all extents for target inode + * @ni: opened ntfs inode for which perform attach + * + * Return 0 on success and error. + */ +int ntfs_inode_attach_all_extents(struct ntfs_inode *ni) +{ + struct attr_list_entry *ale; + u64 prev_attached = 0; + + if (!ni) { + ntfs_debug("Invalid arguments.\n"); + return -EINVAL; + } + + if (NInoAttr(ni)) + ni = ni->ext.base_ntfs_ino; + + ntfs_debug("Entering for inode 0x%llx.\n", ni->mft_no); + + /* Inode haven't got attribute list, thus nothing to attach. */ + if (!NInoAttrList(ni)) + return 0; + + if (!ni->attr_list) { + ntfs_debug("Corrupt in-memory struct.\n"); + return -EINVAL; + } + + /* Walk through attribute list and attach all extents. */ + ale = (struct attr_list_entry *)ni->attr_list; + while ((u8 *)ale < ni->attr_list + ni->attr_list_size) { + if (ni->mft_no != MREF_LE(ale->mft_reference) && + prev_attached != MREF_LE(ale->mft_reference)) { + if (!ntfs_extent_inode_open(ni, ale->mft_reference)) { + ntfs_debug("Couldn't attach extent inode.\n"); + return -1; + } + prev_attached = MREF_LE(ale->mft_reference); + } + ale = (struct attr_list_entry *)((u8 *)ale + le16_to_cpu(ale->length)); + } + return 0; +} + +/* + * ntfs_inode_add_attrlist - add attribute list to inode and fill it + * @ni: opened ntfs inode to which add attribute list + * + * Return 0 on success or error. + */ +int ntfs_inode_add_attrlist(struct ntfs_inode *ni) +{ + int err; + struct ntfs_attr_search_ctx *ctx; + u8 *al = NULL, *aln; + int al_len = 0; + struct attr_list_entry *ale = NULL; + struct mft_record *ni_mrec; + u32 attr_al_len; + + if (!ni) + return -EINVAL; + + ntfs_debug("inode %llu\n", ni->mft_no); + + if (NInoAttrList(ni) || ni->nr_extents) { + ntfs_error(ni->vol->sb, "Inode already has attribute list"); + return -EEXIST; + } + + ni_mrec = map_mft_record(ni); + if (IS_ERR(ni_mrec)) + return -EIO; + + /* Form attribute list. */ + ctx = ntfs_attr_get_search_ctx(ni, ni_mrec); + if (!ctx) { + err = -ENOMEM; + goto err_out; + } + + /* Walk through all attributes. */ + while (!(err = ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx))) { + int ale_size; + + if (ctx->attr->type == AT_ATTRIBUTE_LIST) { + err = -EIO; + ntfs_error(ni->vol->sb, "Attribute list already present"); + goto put_err_out; + } + + ale_size = (sizeof(struct attr_list_entry) + sizeof(__le16) * + ctx->attr->name_length + 7) & ~7; + al_len += ale_size; + + aln = kvrealloc(al, al_len, GFP_NOFS); + if (!aln) { + err = -ENOMEM; + ntfs_error(ni->vol->sb, "Failed to realloc %d bytes", al_len); + goto put_err_out; + } + ale = (struct attr_list_entry *)(aln + ((u8 *)ale - al)); + al = aln; + + memset(ale, 0, ale_size); + + /* Add attribute to attribute list. */ + ale->type = ctx->attr->type; + ale->length = cpu_to_le16((sizeof(struct attr_list_entry) + + sizeof(__le16) * ctx->attr->name_length + 7) & ~7); + ale->name_length = ctx->attr->name_length; + ale->name_offset = (u8 *)ale->name - (u8 *)ale; + if (ctx->attr->non_resident) + ale->lowest_vcn = + ctx->attr->data.non_resident.lowest_vcn; + else + ale->lowest_vcn = 0; + ale->mft_reference = MK_LE_MREF(ni->mft_no, + le16_to_cpu(ni_mrec->sequence_number)); + ale->instance = ctx->attr->instance; + memcpy(ale->name, (u8 *)ctx->attr + + le16_to_cpu(ctx->attr->name_offset), + ctx->attr->name_length * sizeof(__le16)); + ale = (struct attr_list_entry *)(al + al_len); + } + + /* Check for real error occurred. */ + if (err != -ENOENT) { + ntfs_error(ni->vol->sb, "%s: Attribute lookup failed, inode %llu", + __func__, ni->mft_no); + goto put_err_out; + } + + /* Set in-memory attribute list. */ + ni->attr_list = al; + ni->attr_list_size = al_len; + NInoSetAttrList(ni); + + attr_al_len = offsetof(struct attr_record, data.resident.reserved) + 1 + + ((al_len + 7) & ~7); + /* Free space if there is not enough it for $ATTRIBUTE_LIST. */ + if (le32_to_cpu(ni_mrec->bytes_allocated) - + le32_to_cpu(ni_mrec->bytes_in_use) < attr_al_len) { + if (ntfs_inode_free_space(ni, (int)attr_al_len)) { + /* Failed to free space. */ + err = -ENOSPC; + ntfs_error(ni->vol->sb, "Failed to free space for attrlist"); + goto rollback; + } + } + + /* Add $ATTRIBUTE_LIST to mft record. */ + err = ntfs_resident_attr_record_add(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, + NULL, al_len, 0); + if (err < 0) { + ntfs_error(ni->vol->sb, "Couldn't add $ATTRIBUTE_LIST to MFT"); + goto rollback; + } + + err = ntfs_attrlist_update(ni); + if (err < 0) + goto remove_attrlist_record; + + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(ni); + return 0; + +remove_attrlist_record: + /* Prevent ntfs_attr_recorm_rm from freeing attribute list. */ + ni->attr_list = NULL; + NInoClearAttrList(ni); + /* Remove $ATTRIBUTE_LIST record. */ + ntfs_attr_reinit_search_ctx(ctx); + if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (ntfs_attr_record_rm(ctx)) + ntfs_error(ni->vol->sb, "Rollback failed to remove attrlist"); + } else { + ntfs_error(ni->vol->sb, "Rollback failed to find attrlist"); + } + + /* Setup back in-memory runlist. */ + ni->attr_list = al; + ni->attr_list_size = al_len; + NInoSetAttrList(ni); +rollback: + /* + * Scan attribute list for attributes that placed not in the base MFT + * record and move them to it. + */ + ntfs_attr_reinit_search_ctx(ctx); + ale = (struct attr_list_entry *)al; + while ((u8 *)ale < al + al_len) { + if (MREF_LE(ale->mft_reference) != ni->mft_no) { + if (!ntfs_attr_lookup(ale->type, ale->name, + ale->name_length, + CASE_SENSITIVE, + le64_to_cpu(ale->lowest_vcn), + NULL, 0, ctx)) { + if (ntfs_attr_record_move_to(ctx, ni)) + ntfs_error(ni->vol->sb, + "Rollback failed to move attribute"); + } else { + ntfs_error(ni->vol->sb, "Rollback failed to find attr"); + } + ntfs_attr_reinit_search_ctx(ctx); + } + ale = (struct attr_list_entry *)((u8 *)ale + le16_to_cpu(ale->length)); + } + + /* Remove in-memory attribute list. */ + ni->attr_list = NULL; + ni->attr_list_size = 0; + NInoClearAttrList(ni); + NInoClearAttrListDirty(ni); +put_err_out: + ntfs_attr_put_search_ctx(ctx); +err_out: + kvfree(al); + unmap_mft_record(ni); + return err; +} + +/* + * ntfs_inode_close - close an ntfs inode and free all associated memory + * @ni: ntfs inode to close + * + * Make sure the ntfs inode @ni is clean. + * + * If the ntfs inode @ni is a base inode, close all associated extent inodes, + * then deallocate all memory attached to it, and finally free the ntfs inode + * structure itself. + * + * If it is an extent inode, we disconnect it from its base inode before we + * destroy it. + * + * It is OK to pass NULL to this function, it is just noop in this case. + * + * Return 0 on success or error. + */ +int ntfs_inode_close(struct ntfs_inode *ni) +{ + int err = -1; + struct ntfs_inode **tmp_nis; + struct ntfs_inode *base_ni; + s32 i; + + if (!ni) + return 0; + + ntfs_debug("Entering for inode %llu\n", ni->mft_no); + + /* Is this a base inode with mapped extent inodes? */ + /* + * If the inode is an extent inode, disconnect it from the + * base inode before destroying it. + */ + base_ni = ni->ext.base_ntfs_ino; + tmp_nis = base_ni->ext.extent_ntfs_inos; + if (!tmp_nis) + goto out; + for (i = 0; i < base_ni->nr_extents; ++i) { + if (tmp_nis[i] != ni) + continue; + /* Found it. Disconnect. */ + memmove(tmp_nis + i, tmp_nis + i + 1, + (base_ni->nr_extents - i - 1) * + sizeof(struct ntfs_inode *)); + /* Buffer should be for multiple of four extents. */ + if ((--base_ni->nr_extents) & 3) + break; + /* + * ElectricFence is unhappy with realloc(x,0) as free(x) + * thus we explicitly separate these two cases. + */ + if (base_ni->nr_extents) { + /* Resize the memory buffer. */ + tmp_nis = kvrealloc(tmp_nis, base_ni->nr_extents * + sizeof(struct ntfs_inode *), GFP_NOFS); + /* Ignore errors, they don't really matter. */ + if (tmp_nis) + base_ni->ext.extent_ntfs_inos = tmp_nis; + } else if (tmp_nis) { + kvfree(tmp_nis); + base_ni->ext.extent_ntfs_inos = NULL; + } + break; + } + +out: + if (NInoDirty(ni)) + ntfs_error(ni->vol->sb, "Releasing dirty inode %llu!\n", + ni->mft_no); + if (NInoAttrList(ni) && ni->attr_list) + kvfree(ni->attr_list); + ntfs_destroy_ext_inode(ni); + err = 0; + ntfs_debug("\n"); + return err; +} + +void ntfs_destroy_ext_inode(struct ntfs_inode *ni) +{ + ntfs_debug("Entering."); + if (ni == NULL) + return; + + ntfs_attr_close(ni); + + if (NInoDirty(ni)) + ntfs_error(ni->vol->sb, "Releasing dirty ext inode %llu!\n", + ni->mft_no); + if (NInoAttrList(ni) && ni->attr_list) + kvfree(ni->attr_list); + kfree(ni->mrec); + kmem_cache_free(ntfs_inode_cache, ni); +} + +static struct ntfs_inode *ntfs_inode_base(struct ntfs_inode *ni) +{ + if (ni->nr_extents == -1) + return ni->ext.base_ntfs_ino; + return ni; +} + +static int ntfs_attr_position(__le32 type, struct ntfs_attr_search_ctx *ctx) +{ + int err; + + err = ntfs_attr_lookup(type, NULL, 0, CASE_SENSITIVE, 0, NULL, + 0, ctx); + if (err) { + __le32 atype; + + if (err != -ENOENT) + return err; + + atype = ctx->attr->type; + if (atype == AT_END) + return -ENOSPC; + + /* + * if ntfs_external_attr_lookup return -ENOENT, ctx->al_entry + * could point to an attribute in an extent mft record, but + * ctx->attr and ctx->ntfs_ino always points to an attibute in + * a base mft record. + */ + if (ctx->al_entry && + MREF_LE(ctx->al_entry->mft_reference) != ctx->ntfs_ino->mft_no) { + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_lookup(atype, NULL, 0, CASE_SENSITIVE, 0, NULL, + 0, ctx); + if (err) + return err; + } + } + return 0; +} + +/* + * ntfs_inode_free_space - free space in the MFT record of inode + * @ni: ntfs inode in which MFT record free space + * @size: amount of space needed to free + * + * Return 0 on success or error. + */ +int ntfs_inode_free_space(struct ntfs_inode *ni, int size) +{ + struct ntfs_attr_search_ctx *ctx; + int freed, err; + struct mft_record *ni_mrec; + struct super_block *sb; + + if (!ni || size < 0) + return -EINVAL; + ntfs_debug("Entering for inode %llu, size %d\n", ni->mft_no, size); + + sb = ni->vol->sb; + ni_mrec = map_mft_record(ni); + if (IS_ERR(ni_mrec)) + return -EIO; + + freed = (le32_to_cpu(ni_mrec->bytes_allocated) - + le32_to_cpu(ni_mrec->bytes_in_use)); + + unmap_mft_record(ni); + + if (size <= freed) + return 0; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_error(sb, "%s, Failed to get search context", __func__); + return -ENOMEM; + } + + /* + * Chkdsk complain if $STANDARD_INFORMATION is not in the base MFT + * record. + * + * Also we can't move $ATTRIBUTE_LIST from base MFT_RECORD, so position + * search context on first attribute after $STANDARD_INFORMATION and + * $ATTRIBUTE_LIST. + * + * Why we reposition instead of simply skip this attributes during + * enumeration? Because in case we have got only in-memory attribute + * list ntfs_attr_lookup will fail when it will try to find + * $ATTRIBUTE_LIST. + */ + err = ntfs_attr_position(AT_FILE_NAME, ctx); + if (err) + goto put_err_out; + + while (1) { + int record_size; + + /* + * Check whether attribute is from different MFT record. If so, + * find next, because we don't need such. + */ + while (ctx->ntfs_ino->mft_no != ni->mft_no) { +retry: + err = ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx); + if (err) { + if (err != -ENOENT) + ntfs_error(sb, "Attr lookup failed #2"); + else if (ctx->attr->type == AT_END) + err = -ENOSPC; + else + err = 0; + + if (err) + goto put_err_out; + } + } + + if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && + ctx->attr->type == AT_DATA) + goto retry; + + if (ctx->attr->type == AT_INDEX_ROOT) + goto retry; + + record_size = le32_to_cpu(ctx->attr->length); + + /* Move away attribute. */ + err = ntfs_attr_record_move_away(ctx, 0); + if (err) { + ntfs_error(sb, "Failed to move out attribute #2"); + break; + } + freed += record_size; + + /* Check whether we done. */ + if (size <= freed) { + ntfs_attr_put_search_ctx(ctx); + return 0; + } + + /* + * Reposition to first attribute after $STANDARD_INFORMATION and + * $ATTRIBUTE_LIST (see comments upwards). + */ + ntfs_attr_reinit_search_ctx(ctx); + err = ntfs_attr_position(AT_FILE_NAME, ctx); + if (err) + break; + } +put_err_out: + ntfs_attr_put_search_ctx(ctx); + if (err == -ENOSPC) + ntfs_debug("No attributes left that can be moved out.\n"); + return err; +} + +s64 ntfs_inode_attr_pread(struct inode *vi, s64 pos, s64 count, u8 *buf) +{ + struct address_space *mapping = vi->i_mapping; + struct folio *folio; + struct ntfs_inode *ni = NTFS_I(vi); + s64 isize; + u32 attr_len, total = 0, offset; + pgoff_t index; + int err = 0; + + WARN_ON(!NInoAttr(ni)); + if (!count) + return 0; + + mutex_lock(&ni->mrec_lock); + isize = i_size_read(vi); + if (pos > isize) { + mutex_unlock(&ni->mrec_lock); + return -EINVAL; + } + if (pos + count > isize) + count = isize - pos; + + if (!NInoNonResident(ni)) { + struct ntfs_attr_search_ctx *ctx; + u8 *attr; + + ctx = ntfs_attr_get_search_ctx(ni->ext.base_ntfs_ino, NULL); + if (!ctx) { + ntfs_error(vi->i_sb, "Failed to get attr search ctx"); + err = -ENOMEM; + mutex_unlock(&ni->mrec_lock); + goto out; + } + + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx); + if (err) { + ntfs_error(vi->i_sb, "Failed to look up attr %#x", ni->type); + ntfs_attr_put_search_ctx(ctx); + mutex_unlock(&ni->mrec_lock); + goto out; + } + + attr = (u8 *)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset); + memcpy(buf, (u8 *)attr + pos, count); + ntfs_attr_put_search_ctx(ctx); + mutex_unlock(&ni->mrec_lock); + return count; + } + mutex_unlock(&ni->mrec_lock); + + index = pos >> PAGE_SHIFT; + do { + /* Update @index and get the next folio. */ + folio = read_mapping_folio(mapping, index, NULL); + if (IS_ERR(folio)) + break; + + offset = offset_in_folio(folio, pos); + attr_len = min_t(size_t, (size_t)count, folio_size(folio) - offset); + + folio_lock(folio); + memcpy_from_folio(buf, folio, offset, attr_len); + folio_unlock(folio); + folio_put(folio); + + total += attr_len; + buf += attr_len; + pos += attr_len; + count -= attr_len; + index++; + } while (count); +out: + return err ? (s64)err : total; +} + +static inline int ntfs_enlarge_attribute(struct inode *vi, s64 pos, s64 count, + struct ntfs_attr_search_ctx *ctx) +{ + struct ntfs_inode *ni = NTFS_I(vi); + struct super_block *sb = vi->i_sb; + int ret; + + if (pos + count <= ni->initialized_size) + return 0; + + if (NInoEncrypted(ni) && NInoNonResident(ni)) + return -EACCES; + + if (NInoCompressed(ni)) + return -EOPNOTSUPP; + + if (pos + count > ni->data_size) { + if (ntfs_attr_truncate(ni, pos + count)) { + ntfs_debug("Failed to truncate attribute"); + return -1; + } + + ntfs_attr_reinit_search_ctx(ctx); + ret = ntfs_attr_lookup(ni->type, + ni->name, ni->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx); + if (ret) { + ntfs_error(sb, "Failed to look up attr %#x", ni->type); + return ret; + } + } + + if (!NInoNonResident(ni)) { + if (likely(i_size_read(vi) < ni->data_size)) + i_size_write(vi, ni->data_size); + return 0; + } + + if (pos + count > ni->initialized_size) { + ctx->attr->data.non_resident.initialized_size = cpu_to_le64(pos + count); + mark_mft_record_dirty(ctx->ntfs_ino); + ni->initialized_size = pos + count; + if (i_size_read(vi) < ni->initialized_size) + i_size_write(vi, ni->initialized_size); + } + return 0; +} + +static s64 __ntfs_inode_resident_attr_pwrite(struct inode *vi, + s64 pos, s64 count, u8 *buf, + struct ntfs_attr_search_ctx *ctx) +{ + struct ntfs_inode *ni = NTFS_I(vi); + struct folio *folio; + struct address_space *mapping = vi->i_mapping; + u8 *addr; + int err = 0; + + WARN_ON(NInoNonResident(ni)); + if (pos + count > PAGE_SIZE) { + ntfs_error(vi->i_sb, "Out of write into resident attr %#x", ni->type); + return -EINVAL; + } + + /* Copy to mft record page */ + addr = (u8 *)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset); + memcpy(addr + pos, buf, count); + mark_mft_record_dirty(ctx->ntfs_ino); + + /* Keep the first page clean and uptodate */ + folio = __filemap_get_folio(mapping, 0, FGP_WRITEBEGIN | FGP_NOFS, + mapping_gfp_mask(mapping)); + if (IS_ERR(folio)) { + err = PTR_ERR(folio); + ntfs_error(vi->i_sb, "Failed to read a page 0 for attr %#x: %d", + ni->type, err); + goto out; + } + if (!folio_test_uptodate(folio)) + folio_fill_tail(folio, 0, addr, + le32_to_cpu(ctx->attr->data.resident.value_length)); + else + memcpy_to_folio(folio, offset_in_folio(folio, pos), buf, count); + folio_mark_uptodate(folio); + folio_unlock(folio); + folio_put(folio); +out: + return err ? err : count; +} + +static s64 __ntfs_inode_non_resident_attr_pwrite(struct inode *vi, + s64 pos, s64 count, u8 *buf, + struct ntfs_attr_search_ctx *ctx, + bool sync) +{ + struct ntfs_inode *ni = NTFS_I(vi); + struct address_space *mapping = vi->i_mapping; + struct folio *folio; + pgoff_t index; + unsigned long offset, length; + size_t attr_len; + s64 ret = 0, written = 0; + + WARN_ON(!NInoNonResident(ni)); + + index = pos >> PAGE_SHIFT; + while (count) { + if (count == PAGE_SIZE) { + folio = __filemap_get_folio(vi->i_mapping, index, + FGP_CREAT | FGP_LOCK, + mapping_gfp_mask(mapping)); + if (IS_ERR(folio)) { + ret = -ENOMEM; + break; + } + } else { + folio = read_mapping_folio(mapping, index, NULL); + if (IS_ERR(folio)) { + ret = PTR_ERR(folio); + ntfs_error(vi->i_sb, "Failed to read a page %lu for attr %#x: %ld", + index, ni->type, PTR_ERR(folio)); + break; + } + + folio_lock(folio); + } + + if (count == PAGE_SIZE) { + offset = 0; + attr_len = count; + } else { + offset = offset_in_folio(folio, pos); + attr_len = min_t(size_t, (size_t)count, folio_size(folio) - offset); + } + memcpy_to_folio(folio, offset, buf, attr_len); + + if (sync) { + struct ntfs_volume *vol = ni->vol; + s64 lcn, lcn_count; + unsigned int lcn_folio_off = 0; + struct bio *bio; + u64 rl_length = 0; + s64 vcn; + struct runlist_element *rl; + + lcn_count = max_t(s64, 1, ntfs_bytes_to_cluster(vol, attr_len)); + vcn = ntfs_pidx_to_cluster(vol, folio->index); + + do { + down_write(&ni->runlist.lock); + rl = ntfs_attr_vcn_to_rl(ni, vcn, &lcn); + if (IS_ERR(rl)) { + ret = PTR_ERR(rl); + up_write(&ni->runlist.lock); + goto err_unlock_folio; + } + + rl_length = rl->length - (vcn - rl->vcn); + if (rl_length < lcn_count) { + lcn_count -= rl_length; + } else { + rl_length = lcn_count; + lcn_count = 0; + } + up_write(&ni->runlist.lock); + + if (vol->cluster_size_bits > PAGE_SHIFT) { + lcn_folio_off = folio->index << PAGE_SHIFT; + lcn_folio_off &= vol->cluster_size_mask; + } + + bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, + GFP_NOIO); + bio->bi_iter.bi_sector = + ntfs_bytes_to_sector(vol, + ntfs_cluster_to_bytes(vol, lcn) + + lcn_folio_off); + + length = min_t(unsigned long, + ntfs_cluster_to_bytes(vol, rl_length), + folio_size(folio)); + if (!bio_add_folio(bio, folio, length, offset)) { + ret = -EIO; + bio_put(bio); + goto err_unlock_folio; + } + + submit_bio_wait(bio); + bio_put(bio); + vcn += rl_length; + offset += length; + } while (lcn_count != 0); + + folio_mark_uptodate(folio); + } else { + folio_mark_uptodate(folio); + folio_mark_dirty(folio); + } +err_unlock_folio: + folio_unlock(folio); + folio_put(folio); + + if (ret) + break; + + written += attr_len; + buf += attr_len; + pos += attr_len; + count -= attr_len; + index++; + + cond_resched(); + } + + return ret ? ret : written; +} + +s64 ntfs_inode_attr_pwrite(struct inode *vi, s64 pos, s64 count, u8 *buf, bool sync) +{ + struct ntfs_inode *ni = NTFS_I(vi); + struct ntfs_attr_search_ctx *ctx; + s64 ret; + + WARN_ON(!NInoAttr(ni)); + + ctx = ntfs_attr_get_search_ctx(ni->ext.base_ntfs_ino, NULL); + if (!ctx) { + ntfs_error(vi->i_sb, "Failed to get attr search ctx"); + return -ENOMEM; + } + + ret = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx); + if (ret) { + ntfs_attr_put_search_ctx(ctx); + ntfs_error(vi->i_sb, "Failed to look up attr %#x", ni->type); + return ret; + } + + mutex_lock(&ni->mrec_lock); + ret = ntfs_enlarge_attribute(vi, pos, count, ctx); + mutex_unlock(&ni->mrec_lock); + if (ret) + goto out; + + if (NInoNonResident(ni)) + ret = __ntfs_inode_non_resident_attr_pwrite(vi, pos, count, buf, ctx, sync); + else + ret = __ntfs_inode_resident_attr_pwrite(vi, pos, count, buf, ctx); +out: + ntfs_attr_put_search_ctx(ctx); + return ret; +} + +struct folio *ntfs_get_locked_folio(struct address_space *mapping, + pgoff_t index, pgoff_t end_index, struct file_ra_state *ra) +{ + struct folio *folio; + + folio = filemap_lock_folio(mapping, index); + if (IS_ERR(folio)) { + if (PTR_ERR(folio) != -ENOENT) + return folio; + + page_cache_sync_readahead(mapping, ra, NULL, index, + end_index - index); + folio = read_mapping_folio(mapping, index, NULL); + if (!IS_ERR(folio)) + folio_lock(folio); + } + + return folio; +} diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h new file mode 100644 index 000000000000..67942b97fac6 --- /dev/null +++ b/fs/ntfs/inode.h @@ -0,0 +1,358 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for inode structures NTFS Linux kernel driver. + * + * Copyright (c) 2001-2007 Anton Altaparmakov + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#ifndef _LINUX_NTFS_INODE_H +#define _LINUX_NTFS_INODE_H + +#include "debug.h" + +enum ntfs_inode_mutex_lock_class { + NTFS_INODE_MUTEX_PARENT, + NTFS_INODE_MUTEX_NORMAL, + NTFS_INODE_MUTEX_NORMAL_CHILD, + NTFS_INODE_MUTEX_PARENT_2, + NTFS_INODE_MUTEX_NORMAL_2, + NTFS_EXTEND_MUTEX_PARENT, + NTFS_EA_MUTEX_NORMAL +}; + +/* + * The NTFS in-memory inode structure. It is just used as an extension to the + * fields already provided in the VFS inode. + * @size_lock: Lock serializing access to inode sizes. + * @state: NTFS specific flags describing this inode. + * @flags: Flags describing the file. (Copy from STANDARD_INFORMATION). + * @mft_no: Number of the mft record / inode. + * @seq_no: Sequence number of the mft record. + * @count: Inode reference count for book keeping. + * @vol: Pointer to the ntfs volume of this inode. + * + * If NInoAttr() is true, the below fields describe the attribute which + * this fake inode belongs to. The actual inode of this attribute is + * pointed to by base_ntfs_ino and nr_extents is always set to -1 (see + * below). For real inodes, we also set the type (AT_DATA for files and + * AT_INDEX_ALLOCATION for directories), with the name = NULL and + * name_len = 0 for files and name = I30 (global constant) and + * name_len = 4 for directories. + * @type: Attribute type of this fake inode. + * @name: Attribute name of this fake inode. + * @name_len: Attribute name length of this fake inode. + * @runlist: If state has the NI_NonResident bit set, the runlist of + * the unnamed data attribute (if a file) or of the index allocation + * attribute (directory) or of the attribute described by the fake inode + * (if NInoAttr()). If runlist.rl is NULL, the runlist has not been read + * in yet or has been unmapped. If NI_NonResident is clear, the attribute + * is resident (file and fake inode) or there is no $I30 index allocation + * attribute (small directory). In the latter case runlist.rl is always + * NULL. + * @data_size: Copy from the attribute record. + * @initialized_size: Copy from the attribute record. + * @allocated_size: Copy from the attribute record. + * @i_crtime: File Creation time. + * @mrec: MFT record + * @mrec_lock: Lock for serializing access to the mft record belonging to + * this inode. + * @folio: The folio containing the mft record of the inode. + * @folio_ofs: Offset into the folio at which the mft record begins. + * @mft_lcn: Number containing the mft record. + * @mft_lcn_count: Number of clusters per mft record. + * + * Attribute list support (only for use by the attribute lookup + * functions). Setup during read_inode for all inodes with attribute + * lists. Only valid if NI_AttrList is set in state. + * @attr_list_size: Length of attribute list value in bytes. + * @attr_list: Attribute list value itself. + * + * It is a directory, $MFT, or an index inode. + * @block_size: Size of an index block. + * @vcn_size: Size of a vcn in this index. + * @collation_rule: The collation rule for the index. + * @block_size_bits: Log2 of the above. + * @vcn_size_bits: Log2 of the above. + * + * It is a compressed/sparse file/attribute inode. + * @size: Copy of compressed_size from $DATA. + * @block_size: Size of a compression block (cb). + * @block_size_bits: Log2 of the size of a cb. + * @block_clusters: Number of clusters per cb. + * @extent_lock: Lock for accessing/modifying the below. + * @nr_extents: For a base mft record, the number of attached extent inodes + * (0 if none), for extent records and for fake inodes describing an + * attribute this is -1. + * + * This union is only used if nr_extents != 0. + * @extent_ntfs_inos: For nr_extents > 0, array of the ntfs inodes of + * the extent mft records belonging to this base inode which have been + * loaded. + * @base_ntfs_ino: For nr_extents == -1, the ntfs inode of the base mft + * record. For fake inodes, the real (base) inode to which the attribute + * belongs. + * @i_dealloc_clusters: delayed allocated clusters. + * @target: symlink buffer. + */ +struct ntfs_inode { + rwlock_t size_lock; + unsigned long state; + __le32 flags; + u64 mft_no; + u16 seq_no; + atomic_t count; + struct ntfs_volume *vol; + __le32 type; + __le16 *name; + u32 name_len; + struct runlist runlist; + s64 data_size; + s64 initialized_size; + s64 allocated_size; + struct timespec64 i_crtime; + void *mrec; + struct mutex mrec_lock; + struct folio *folio; + int folio_ofs; + s64 mft_lcn[2]; + unsigned int mft_lcn_count; + u32 attr_list_size; + u8 *attr_list; + union { + struct { + u32 block_size; + u32 vcn_size; + __le32 collation_rule; + u8 block_size_bits; + u8 vcn_size_bits; + } index; + struct { + s64 size; + u32 block_size; + u8 block_size_bits; + u8 block_clusters; + } compressed; + } itype; + struct mutex extent_lock; + s32 nr_extents; + union { + struct ntfs_inode **extent_ntfs_inos; + struct ntfs_inode *base_ntfs_ino; + } ext; + unsigned int i_dealloc_clusters; + char *target; +}; + +/* + * Defined bits for the state field in the ntfs_inode structure. + * (f) = files only, (d) = directories only, (a) = attributes/fake inodes only + * + * NI_Dirty Mft record needs to be written to disk. + * NI_AttrListDirty Mft record contains an attribute list. + * NI_AttrList Mft record contains an attribute list. + * NI_AttrListNonResident Attribute list is non-resident. Implies + * NI_AttrList is set. + * NI_Attr 1: Fake inode for attribute i/o. + * 0: Real inode or extent inode. + * NI_MstProtected Attribute is protected by MST fixups. + * NI_NonResident Unnamed data attr is non-resident (f) + * Attribute is non-resident (a). + * NI_IndexAllocPresent $I30 index alloc attr is present (d). + * NI_Compressed Unnamed data attr is compressed (f). + * Create compressed files by default (d). + * Attribute is compressed (a). + * NI_Encrypted Unnamed data attr is encrypted (f). + * Create encrypted files by default (d). + * Attribute is encrypted (a). + * NI_Sparse Unnamed data attr is sparse (f). + * Create sparse files by default (d). + * Attribute is sparse (a). + * NI_SparseDisabled May not create sparse regions. + * NI_FullyMapped Runlist is fully mapped. + * NI_FileNameDirty FILE_NAME attributes need to be updated. + * NI_BeingDeleted ntfs inode is being delated. + * NI_BeingCreated ntfs inode is being created. + * NI_HasEA ntfs inode has EA attribute. + * NI_RunlistDirty runlist need to be updated. + */ +enum { + NI_Dirty, + NI_AttrListDirty, + NI_AttrList, + NI_AttrListNonResident, + NI_Attr, + NI_MstProtected, + NI_NonResident, + NI_IndexAllocPresent, + NI_Compressed, + NI_Encrypted, + NI_Sparse, + NI_SparseDisabled, + NI_FullyMapped, + NI_FileNameDirty, + NI_BeingDeleted, + NI_BeingCreated, + NI_HasEA, + NI_RunlistDirty, +}; + +/* + * NOTE: We should be adding dirty mft records to a list somewhere and they + * should be independent of the (ntfs/vfs) inode structure so that an inode can + * be removed but the record can be left dirty for syncing later. + */ + +/* + * Macro tricks to expand the NInoFoo(), NInoSetFoo(), and NInoClearFoo() + * functions. + */ +#define NINO_FNS(flag) \ +static inline int NIno##flag(struct ntfs_inode *ni) \ +{ \ + return test_bit(NI_##flag, &(ni)->state); \ +} \ +static inline void NInoSet##flag(struct ntfs_inode *ni) \ +{ \ + set_bit(NI_##flag, &(ni)->state); \ +} \ +static inline void NInoClear##flag(struct ntfs_inode *ni) \ +{ \ + clear_bit(NI_##flag, &(ni)->state); \ +} + +/* + * As above for NInoTestSetFoo() and NInoTestClearFoo(). + */ +#define TAS_NINO_FNS(flag) \ +static inline int NInoTestSet##flag(struct ntfs_inode *ni) \ +{ \ + return test_and_set_bit(NI_##flag, &(ni)->state); \ +} \ +static inline int NInoTestClear##flag(struct ntfs_inode *ni) \ +{ \ + return test_and_clear_bit(NI_##flag, &(ni)->state); \ +} + +/* Emit the ntfs inode bitops functions. */ +NINO_FNS(Dirty) +TAS_NINO_FNS(Dirty) +NINO_FNS(AttrList) +NINO_FNS(AttrListDirty) +NINO_FNS(AttrListNonResident) +NINO_FNS(Attr) +NINO_FNS(MstProtected) +NINO_FNS(NonResident) +NINO_FNS(IndexAllocPresent) +NINO_FNS(Compressed) +NINO_FNS(Encrypted) +NINO_FNS(Sparse) +NINO_FNS(SparseDisabled) +NINO_FNS(FullyMapped) +NINO_FNS(FileNameDirty) +TAS_NINO_FNS(FileNameDirty) +NINO_FNS(BeingDeleted) +NINO_FNS(HasEA) +NINO_FNS(RunlistDirty) + +/* + * The full structure containing a ntfs_inode and a vfs struct inode. Used for + * all real and fake inodes but not for extent inodes which lack the vfs struct + * inode. + */ +struct big_ntfs_inode { + struct ntfs_inode ntfs_inode; + struct inode vfs_inode; /* The vfs inode structure. */ +}; + +/* + * NTFS_I - return the ntfs inode given a vfs inode + * @inode: VFS inode + * + * NTFS_I() returns the ntfs inode associated with the VFS @inode. + */ +static inline struct ntfs_inode *NTFS_I(struct inode *inode) +{ + return &container_of(inode, struct big_ntfs_inode, vfs_inode)->ntfs_inode; +} + +static inline struct inode *VFS_I(struct ntfs_inode *ni) +{ + return &container_of(ni, struct big_ntfs_inode, ntfs_inode)->vfs_inode; +} + +/* + * ntfs_attr - ntfs in memory attribute structure + * + * This structure exists only to provide a small structure for the + * ntfs_{attr_}iget()/ntfs_test_inode()/ntfs_init_locked_inode() mechanism. + * + * NOTE: Elements are ordered by size to make the structure as compact as + * possible on all architectures. + */ +struct ntfs_attr { + u64 mft_no; + __le16 *name; + u32 name_len; + __le32 type; + unsigned long state; +}; + +int ntfs_test_inode(struct inode *vi, void *data); +struct inode *ntfs_iget(struct super_block *sb, u64 mft_no); +struct inode *ntfs_attr_iget(struct inode *base_vi, __le32 type, + __le16 *name, u32 name_len); +struct inode *ntfs_index_iget(struct inode *base_vi, __le16 *name, + u32 name_len); +struct inode *ntfs_alloc_big_inode(struct super_block *sb); +void ntfs_free_big_inode(struct inode *inode); +int ntfs_drop_big_inode(struct inode *inode); +void ntfs_evict_big_inode(struct inode *vi); +void __ntfs_init_inode(struct super_block *sb, struct ntfs_inode *ni); + +static inline void ntfs_init_big_inode(struct inode *vi) +{ + struct ntfs_inode *ni = NTFS_I(vi); + + ntfs_debug("Entering."); + __ntfs_init_inode(vi->i_sb, ni); + ni->mft_no = vi->i_ino; +} + +struct ntfs_inode *ntfs_new_extent_inode(struct super_block *sb, u64 mft_no); +void ntfs_clear_extent_inode(struct ntfs_inode *ni); +int ntfs_read_inode_mount(struct inode *vi); +int ntfs_show_options(struct seq_file *sf, struct dentry *root); +int ntfs_truncate_vfs(struct inode *vi, loff_t new_size, loff_t i_size); + +int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr); +int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path, + struct kstat *stat, unsigned int request_mask, + unsigned int query_flags); + +int ntfs_get_block_mft_record(struct ntfs_inode *mft_ni, struct ntfs_inode *ni); +int __ntfs_write_inode(struct inode *vi, int sync); +int ntfs_inode_attach_all_extents(struct ntfs_inode *ni); +int ntfs_inode_add_attrlist(struct ntfs_inode *ni); +void ntfs_destroy_ext_inode(struct ntfs_inode *ni); +int ntfs_inode_free_space(struct ntfs_inode *ni, int size); +s64 ntfs_inode_attr_pread(struct inode *vi, s64 pos, s64 count, u8 *buf); +s64 ntfs_inode_attr_pwrite(struct inode *vi, s64 pos, s64 count, u8 *buf, + bool sync); +int ntfs_inode_close(struct ntfs_inode *ni); + +static inline void ntfs_commit_inode(struct inode *vi) +{ + __ntfs_write_inode(vi, 1); +} + +int ntfs_inode_sync_filename(struct ntfs_inode *ni); +int ntfs_extend_initialized_size(struct inode *vi, const loff_t offset, + const loff_t new_size, bool bsync); +void ntfs_set_vfs_operations(struct inode *inode, mode_t mode, dev_t dev); +struct folio *ntfs_get_locked_folio(struct address_space *mapping, + pgoff_t index, pgoff_t end_index, struct file_ra_state *ra); + +#endif /* _LINUX_NTFS_INODE_H */ diff --git a/fs/ntfs/iomap.c b/fs/ntfs/iomap.c new file mode 100644 index 000000000000..dc7d8c893a69 --- /dev/null +++ b/fs/ntfs/iomap.c @@ -0,0 +1,882 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * iomap callack functions + * + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#include + +#include "attrib.h" +#include "mft.h" +#include "ntfs.h" +#include "iomap.h" + +static void ntfs_iomap_put_folio_non_resident(struct inode *inode, loff_t pos, + unsigned int len, struct folio *folio) +{ + struct ntfs_inode *ni = NTFS_I(inode); + unsigned long sector_size = 1UL << inode->i_blkbits; + loff_t start_down, end_up, init; + + start_down = round_down(pos, sector_size); + end_up = (pos + len - 1) | (sector_size - 1); + init = ni->initialized_size; + + if (init >= start_down && init <= end_up) { + if (init < pos) { + loff_t offset = offset_in_folio(folio, pos + len); + + if (offset == 0) + offset = folio_size(folio); + folio_zero_segments(folio, + offset_in_folio(folio, init), + offset_in_folio(folio, pos), + offset, + folio_size(folio)); + + } else { + loff_t offset = max_t(loff_t, pos + len, init); + + offset = offset_in_folio(folio, offset); + if (offset == 0) + offset = folio_size(folio); + folio_zero_segment(folio, + offset, + folio_size(folio)); + } + } else if (init <= pos) { + loff_t offset = 0, offset2 = offset_in_folio(folio, pos + len); + + if ((init >> folio_shift(folio)) == (pos >> folio_shift(folio))) + offset = offset_in_folio(folio, init); + if (offset2 == 0) + offset2 = folio_size(folio); + folio_zero_segments(folio, + offset, + offset_in_folio(folio, pos), + offset2, + folio_size(folio)); + } + folio_unlock(folio); + folio_put(folio); +} + +/* + * iomap_zero_range is called for an area beyond the initialized size, + * garbage values can be read, so zeroing out is needed. + */ +static void ntfs_iomap_put_folio(struct inode *inode, loff_t pos, + unsigned int len, struct folio *folio) +{ + if (NInoNonResident(NTFS_I(inode))) + return ntfs_iomap_put_folio_non_resident(inode, pos, + len, folio); + folio_unlock(folio); + folio_put(folio); +} + +const struct iomap_write_ops ntfs_iomap_folio_ops = { + .put_folio = ntfs_iomap_put_folio, +}; + +static int ntfs_read_iomap_begin_resident(struct inode *inode, loff_t offset, loff_t length, + unsigned int flags, struct iomap *iomap) +{ + struct ntfs_inode *base_ni, *ni = NTFS_I(inode); + struct ntfs_attr_search_ctx *ctx; + loff_t i_size; + u32 attr_len; + int err = 0; + char *kattr; + struct page *ipage; + + if (NInoAttr(ni)) + base_ni = ni->ext.base_ntfs_ino; + else + base_ni = ni; + + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); + if (!ctx) { + err = -ENOMEM; + goto out; + } + + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) + goto out; + + attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); + if (unlikely(attr_len > ni->initialized_size)) + attr_len = ni->initialized_size; + i_size = i_size_read(inode); + + if (unlikely(attr_len > i_size)) { + /* Race with shrinking truncate. */ + attr_len = i_size; + } + + if (offset >= attr_len) { + if (flags & IOMAP_REPORT) + err = -ENOENT; + else { + iomap->type = IOMAP_HOLE; + iomap->offset = offset; + iomap->length = length; + } + goto out; + } + + kattr = (u8 *)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset); + + ipage = alloc_page(GFP_NOFS | __GFP_ZERO); + if (!ipage) { + err = -ENOMEM; + goto out; + } + + memcpy(page_address(ipage), kattr, attr_len); + iomap->type = IOMAP_INLINE; + iomap->inline_data = page_address(ipage); + iomap->offset = 0; + iomap->length = attr_len; + iomap->private = ipage; + +out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + + return err; +} + +/* + * ntfs_read_iomap_begin_non_resident - map non-resident NTFS file data + * @inode: inode to map + * @offset: file offset to map + * @length: length of mapping + * @flags: IOMAP flags + * @iomap: iomap structure to fill + * @need_unwritten: true if UNWRITTEN extent type is needed + * + * Map a range of a non-resident NTFS file to an iomap extent. + * + * NTFS UNWRITTEN extent handling: + * ================================ + * The concept of an unwritten extent in NTFS is slightly different from + * that of other filesystems. NTFS conceptually manages only a single + * continuous unwritten region, which is strictly defined based on + * initialized_size. + * + * File offset layout: + * 0 initialized_size i_size(EOF) + * |----------#0----------|----------#1----------|----------#2----------| + * | Actual data | Pre-allocated | Pre-allocated | + * | (user written) | (within initialized) | (initialized ~ EOF) | + * |----------------------|----------------------|----------------------| + * MAPPED MAPPED UNWRITTEN (conditionally) + * + * Region #0: User-written data, initialized and valid. + * Region #1: Pre-allocated within initialized_size, must be zero-initialized + * by the filesystem before exposure to userspace. + * Region #2: Pre-allocated beyond initialized_size, does not need initialization. + * + * The @need_unwritten parameter controls whether region #2 is mapped as + * IOMAP_UNWRITTEN or IOMAP_MAPPED: + * - For seek operations (SEEK_DATA/SEEK_HOLE): IOMAP_MAPPED is needed to + * prevent iomap_seek_data from incorrectly interpreting pre-allocated + * space as a hole. Since NTFS does not support multiple unwritten extents, + * all pre-allocated regions should be treated as data, not holes. + * - For zero_range operations: IOMAP_MAPPED is needed to be zeroed out. + * + * Return: 0 on success, negative error code on failure. + */ +static int ntfs_read_iomap_begin_non_resident(struct inode *inode, loff_t offset, + loff_t length, unsigned int flags, struct iomap *iomap, + bool need_unwritten) +{ + struct ntfs_inode *ni = NTFS_I(inode); + s64 vcn; + s64 lcn; + struct runlist_element *rl; + struct ntfs_volume *vol = ni->vol; + loff_t vcn_ofs; + loff_t rl_length; + + vcn = ntfs_bytes_to_cluster(vol, offset); + vcn_ofs = ntfs_bytes_to_cluster_off(vol, offset); + + down_write(&ni->runlist.lock); + rl = ntfs_attr_vcn_to_rl(ni, vcn, &lcn); + if (IS_ERR(rl)) { + up_write(&ni->runlist.lock); + return PTR_ERR(rl); + } + + if (flags & IOMAP_REPORT) { + if (lcn < LCN_HOLE) { + up_write(&ni->runlist.lock); + return -ENOENT; + } + } else if (lcn < LCN_ENOENT) { + up_write(&ni->runlist.lock); + return -EINVAL; + } + + iomap->bdev = inode->i_sb->s_bdev; + iomap->offset = offset; + + if (lcn <= LCN_DELALLOC) { + if (lcn == LCN_DELALLOC) + iomap->type = IOMAP_DELALLOC; + else + iomap->type = IOMAP_HOLE; + iomap->addr = IOMAP_NULL_ADDR; + } else { + if (need_unwritten && offset >= ni->initialized_size) + iomap->type = IOMAP_UNWRITTEN; + else + iomap->type = IOMAP_MAPPED; + iomap->addr = ntfs_cluster_to_bytes(vol, lcn) + vcn_ofs; + } + + rl_length = ntfs_cluster_to_bytes(vol, rl->length - (vcn - rl->vcn)); + + if (rl_length == 0 && rl->lcn > LCN_DELALLOC) { + ntfs_error(inode->i_sb, + "runlist(vcn : %lld, length : %lld, lcn : %lld) is corrupted\n", + rl->vcn, rl->length, rl->lcn); + up_write(&ni->runlist.lock); + return -EIO; + } + + if (rl_length && length > rl_length - vcn_ofs) + iomap->length = rl_length - vcn_ofs; + else + iomap->length = length; + up_write(&ni->runlist.lock); + + if (!(flags & IOMAP_ZERO) && + iomap->type == IOMAP_MAPPED && + iomap->offset < ni->initialized_size && + iomap->offset + iomap->length > ni->initialized_size) { + iomap->length = round_up(ni->initialized_size, 1 << inode->i_blkbits) - + iomap->offset; + } + iomap->flags |= IOMAP_F_MERGED; + + return 0; +} + +static int __ntfs_read_iomap_begin(struct inode *inode, loff_t offset, loff_t length, + unsigned int flags, struct iomap *iomap, struct iomap *srcmap, + bool need_unwritten) +{ + if (NInoNonResident(NTFS_I(inode))) + return ntfs_read_iomap_begin_non_resident(inode, offset, length, + flags, iomap, need_unwritten); + return ntfs_read_iomap_begin_resident(inode, offset, length, + flags, iomap); +} + +static int ntfs_read_iomap_begin(struct inode *inode, loff_t offset, loff_t length, + unsigned int flags, struct iomap *iomap, struct iomap *srcmap) +{ + return __ntfs_read_iomap_begin(inode, offset, length, flags, iomap, + srcmap, true); +} + +static int ntfs_read_iomap_end(struct inode *inode, loff_t pos, loff_t length, + ssize_t written, unsigned int flags, struct iomap *iomap) +{ + if (iomap->type == IOMAP_INLINE) { + struct page *ipage = iomap->private; + + put_page(ipage); + } + + return written; +} + +const struct iomap_ops ntfs_read_iomap_ops = { + .iomap_begin = ntfs_read_iomap_begin, + .iomap_end = ntfs_read_iomap_end, +}; + +/* + * Check that the cached iomap still matches the NTFS runlist before + * iomap_zero_range() is called. if the runlist changes while iomap is + * iterating a cached iomap, iomap_zero_range() may overwrite folios + * that have been already written with valid data. + */ +static bool ntfs_iomap_valid(struct inode *inode, const struct iomap *iomap) +{ + struct ntfs_inode *ni = NTFS_I(inode); + struct runlist_element *rl; + s64 vcn, lcn; + + if (!NInoNonResident(ni)) + return false; + + vcn = iomap->offset >> ni->vol->cluster_size_bits; + + down_read(&ni->runlist.lock); + rl = __ntfs_attr_find_vcn_nolock(&ni->runlist, vcn); + if (IS_ERR(rl)) { + up_read(&ni->runlist.lock); + return false; + } + lcn = ntfs_rl_vcn_to_lcn(rl, vcn); + up_read(&ni->runlist.lock); + return lcn == LCN_DELALLOC; +} + +static const struct iomap_write_ops ntfs_zero_iomap_folio_ops = { + .put_folio = ntfs_iomap_put_folio, + .iomap_valid = ntfs_iomap_valid, +}; + +static int ntfs_seek_iomap_begin(struct inode *inode, loff_t offset, loff_t length, + unsigned int flags, struct iomap *iomap, struct iomap *srcmap) +{ + return __ntfs_read_iomap_begin(inode, offset, length, flags, iomap, + srcmap, false); +} + +static int ntfs_zero_read_iomap_end(struct inode *inode, loff_t pos, loff_t length, + ssize_t written, unsigned int flags, struct iomap *iomap) +{ + if ((flags & IOMAP_ZERO) && (iomap->flags & IOMAP_F_STALE)) + return -EPERM; + return written; +} + +static const struct iomap_ops ntfs_zero_read_iomap_ops = { + .iomap_begin = ntfs_seek_iomap_begin, + .iomap_end = ntfs_zero_read_iomap_end, +}; + +const struct iomap_ops ntfs_seek_iomap_ops = { + .iomap_begin = ntfs_seek_iomap_begin, + .iomap_end = ntfs_read_iomap_end, +}; + +int ntfs_dio_zero_range(struct inode *inode, loff_t offset, loff_t length) +{ + if ((offset | length) & (SECTOR_SIZE - 1)) + return -EINVAL; + + return blkdev_issue_zeroout(inode->i_sb->s_bdev, + offset >> SECTOR_SHIFT, + length >> SECTOR_SHIFT, + GFP_NOFS, + BLKDEV_ZERO_NOUNMAP); +} + +static int ntfs_zero_range(struct inode *inode, loff_t offset, loff_t length) +{ + return iomap_zero_range(inode, + offset, length, + NULL, + &ntfs_zero_read_iomap_ops, + &ntfs_zero_iomap_folio_ops, + NULL); +} + +static int ntfs_write_simple_iomap_begin_non_resident(struct inode *inode, loff_t offset, + loff_t length, struct iomap *iomap) +{ + struct ntfs_inode *ni = NTFS_I(inode); + struct ntfs_volume *vol = ni->vol; + loff_t vcn_ofs, rl_length; + struct runlist_element *rl, *rlc; + bool is_retry = false; + int err = 0; + s64 vcn, lcn; + s64 max_clu_count = + ntfs_bytes_to_cluster(vol, round_up(length, vol->cluster_size)); + + vcn = ntfs_bytes_to_cluster(vol, offset); + vcn_ofs = ntfs_bytes_to_cluster_off(vol, offset); + + down_read(&ni->runlist.lock); + rl = ni->runlist.rl; + if (!rl) { + up_read(&ni->runlist.lock); + err = ntfs_map_runlist(ni, vcn); + if (err) { + mutex_unlock(&ni->mrec_lock); + return -ENOENT; + } + down_read(&ni->runlist.lock); + rl = ni->runlist.rl; + } + up_read(&ni->runlist.lock); + + down_write(&ni->runlist.lock); +remap_rl: + /* Seek to element containing target vcn. */ + rl = __ntfs_attr_find_vcn_nolock(&ni->runlist, vcn); + if (IS_ERR(rl)) { + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + return -EIO; + } + lcn = ntfs_rl_vcn_to_lcn(rl, vcn); + + if (lcn <= LCN_RL_NOT_MAPPED && is_retry == false) { + is_retry = true; + if (!ntfs_map_runlist_nolock(ni, vcn, NULL)) { + rl = ni->runlist.rl; + goto remap_rl; + } + } + + max_clu_count = min(max_clu_count, rl->length - (vcn - rl->vcn)); + if (max_clu_count == 0) { + ntfs_error(inode->i_sb, + "runlist(vcn : %lld, length : %lld) is corrupted\n", + rl->vcn, rl->length); + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + return -EIO; + } + + iomap->bdev = inode->i_sb->s_bdev; + iomap->offset = offset; + + if (lcn <= LCN_DELALLOC) { + if (lcn < LCN_DELALLOC) { + max_clu_count = + ntfs_available_clusters_count(vol, max_clu_count); + if (max_clu_count < 0) { + err = max_clu_count; + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + return err; + } + } + + iomap->type = IOMAP_DELALLOC; + iomap->addr = IOMAP_NULL_ADDR; + + if (lcn <= LCN_HOLE) { + size_t new_rl_count; + + rlc = kmalloc(sizeof(struct runlist_element) * 2, + GFP_NOFS); + if (!rlc) { + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + return -ENOMEM; + } + + rlc->vcn = vcn; + rlc->lcn = LCN_DELALLOC; + rlc->length = max_clu_count; + + rlc[1].vcn = vcn + max_clu_count; + rlc[1].lcn = LCN_RL_NOT_MAPPED; + rlc[1].length = 0; + + rl = ntfs_runlists_merge(&ni->runlist, rlc, 0, + &new_rl_count); + if (IS_ERR(rl)) { + ntfs_error(vol->sb, "Failed to merge runlists"); + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + kvfree(rlc); + return PTR_ERR(rl); + } + + ni->runlist.rl = rl; + ni->runlist.count = new_rl_count; + ni->i_dealloc_clusters += max_clu_count; + } + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + + if (lcn < LCN_DELALLOC) + ntfs_hold_dirty_clusters(vol, max_clu_count); + + rl_length = ntfs_cluster_to_bytes(vol, max_clu_count); + if (length > rl_length - vcn_ofs) + iomap->length = rl_length - vcn_ofs; + else + iomap->length = length; + + iomap->flags = IOMAP_F_NEW; + if (lcn <= LCN_HOLE) { + loff_t end = offset + length; + + if (vcn_ofs || ((vol->cluster_size > iomap->length) && + end < ni->initialized_size)) { + loff_t z_start, z_end; + + z_start = vcn << vol->cluster_size_bits; + z_end = min_t(loff_t, z_start + vol->cluster_size, + i_size_read(inode)); + if (z_end > z_start) + err = ntfs_zero_range(inode, + z_start, + z_end - z_start); + } + if ((!err || err == -EPERM) && + max_clu_count > 1 && + (iomap->length & vol->cluster_size_mask && + end < ni->initialized_size)) { + loff_t z_start, z_end; + + z_start = (vcn + max_clu_count - 1) << + vol->cluster_size_bits; + z_end = min_t(loff_t, z_start + vol->cluster_size, + i_size_read(inode)); + if (z_end > z_start) + err = ntfs_zero_range(inode, + z_start, + z_end - z_start); + } + + if (err == -EPERM) + err = 0; + if (err) { + ntfs_release_dirty_clusters(vol, max_clu_count); + return err; + } + } + } else { + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + + iomap->type = IOMAP_MAPPED; + iomap->addr = ntfs_cluster_to_bytes(vol, lcn) + vcn_ofs; + + rl_length = ntfs_cluster_to_bytes(vol, max_clu_count); + if (length > rl_length - vcn_ofs) + iomap->length = rl_length - vcn_ofs; + else + iomap->length = length; + } + + return 0; +} + +#define NTFS_IOMAP_FLAGS_BEGIN BIT(1) +#define NTFS_IOMAP_FLAGS_DIO BIT(2) +#define NTFS_IOMAP_FLAGS_MKWRITE BIT(3) +#define NTFS_IOMAP_FLAGS_WRITEBACK BIT(4) + +static int ntfs_write_da_iomap_begin_non_resident(struct inode *inode, + loff_t offset, loff_t length, unsigned int flags, + struct iomap *iomap, int ntfs_iomap_flags) +{ + struct ntfs_inode *ni = NTFS_I(inode); + struct ntfs_volume *vol = ni->vol; + loff_t vcn_ofs, rl_length; + s64 vcn, start_lcn, lcn_count; + bool balloc = false, update_mp; + int err; + s64 max_clu_count = + ntfs_bytes_to_cluster(vol, round_up(length, vol->cluster_size)); + + vcn = ntfs_bytes_to_cluster(vol, offset); + vcn_ofs = ntfs_bytes_to_cluster_off(vol, offset); + + update_mp = ntfs_iomap_flags & (NTFS_IOMAP_FLAGS_DIO | NTFS_IOMAP_FLAGS_MKWRITE) || + NInoAttr(ni) || ni->mft_no < FILE_first_user; + down_write(&ni->runlist.lock); + err = ntfs_attr_map_cluster(ni, vcn, &start_lcn, &lcn_count, + max_clu_count, &balloc, update_mp, + ntfs_iomap_flags & NTFS_IOMAP_FLAGS_WRITEBACK); + up_write(&ni->runlist.lock); + mutex_unlock(&ni->mrec_lock); + if (err) { + ni->i_dealloc_clusters = 0; + return err; + } + + iomap->bdev = inode->i_sb->s_bdev; + iomap->offset = offset; + + rl_length = ntfs_cluster_to_bytes(vol, lcn_count); + if (length > rl_length - vcn_ofs) + iomap->length = rl_length - vcn_ofs; + else + iomap->length = length; + + if (start_lcn == LCN_HOLE) + iomap->type = IOMAP_HOLE; + else + iomap->type = IOMAP_MAPPED; + if (balloc == true) + iomap->flags = IOMAP_F_NEW; + + iomap->addr = ntfs_cluster_to_bytes(vol, start_lcn) + vcn_ofs; + + if (balloc == true) { + if (flags & IOMAP_DIRECT || + ntfs_iomap_flags & NTFS_IOMAP_FLAGS_MKWRITE) { + loff_t end = offset + length; + + if (vcn_ofs || ((vol->cluster_size > iomap->length) && + end < ni->initialized_size)) + err = ntfs_dio_zero_range(inode, + start_lcn << + vol->cluster_size_bits, + vol->cluster_size); + if (!err && lcn_count > 1 && + (iomap->length & vol->cluster_size_mask && + end < ni->initialized_size)) + err = ntfs_dio_zero_range(inode, + (start_lcn + lcn_count - 1) << + vol->cluster_size_bits, + vol->cluster_size); + } else { + if (lcn_count > ni->i_dealloc_clusters) + ni->i_dealloc_clusters = 0; + else + ni->i_dealloc_clusters -= lcn_count; + } + if (err < 0) + return err; + } + + if (ntfs_iomap_flags & NTFS_IOMAP_FLAGS_MKWRITE && + iomap->offset + iomap->length > ni->initialized_size) { + err = ntfs_attr_set_initialized_size(ni, iomap->offset + + iomap->length); + } + + return err; +} + +static int ntfs_write_iomap_begin_resident(struct inode *inode, loff_t offset, + struct iomap *iomap) +{ + struct ntfs_inode *ni = NTFS_I(inode); + struct attr_record *a; + struct ntfs_attr_search_ctx *ctx; + u32 attr_len; + int err = 0; + char *kattr; + struct page *ipage; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = -ENOMEM; + goto out; + } + + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (err) { + if (err == -ENOENT) + err = -EIO; + goto out; + } + + a = ctx->attr; + /* The total length of the attribute value. */ + attr_len = le32_to_cpu(a->data.resident.value_length); + kattr = (u8 *)a + le16_to_cpu(a->data.resident.value_offset); + + ipage = alloc_page(GFP_NOFS | __GFP_ZERO); + if (!ipage) { + err = -ENOMEM; + goto out; + } + + memcpy(page_address(ipage), kattr, attr_len); + iomap->type = IOMAP_INLINE; + iomap->inline_data = page_address(ipage); + iomap->offset = 0; + /* iomap requires there is only one INLINE_DATA extent */ + iomap->length = attr_len; + iomap->private = ipage; + +out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + mutex_unlock(&ni->mrec_lock); + return err; +} + +static int ntfs_write_iomap_begin_non_resident(struct inode *inode, loff_t offset, + loff_t length, unsigned int flags, + struct iomap *iomap, int ntfs_iomap_flags) +{ + struct ntfs_inode *ni = NTFS_I(inode); + + if (ntfs_iomap_flags & (NTFS_IOMAP_FLAGS_BEGIN | NTFS_IOMAP_FLAGS_DIO) && + offset + length > ni->initialized_size) { + int ret; + + ret = ntfs_extend_initialized_size(inode, offset, + offset + length, + ntfs_iomap_flags & + NTFS_IOMAP_FLAGS_DIO); + if (ret < 0) + return ret; + } + + mutex_lock(&ni->mrec_lock); + if (ntfs_iomap_flags & NTFS_IOMAP_FLAGS_BEGIN) + return ntfs_write_simple_iomap_begin_non_resident(inode, offset, + length, iomap); + else + return ntfs_write_da_iomap_begin_non_resident(inode, + offset, length, + flags, iomap, + ntfs_iomap_flags); +} + +static int __ntfs_write_iomap_begin(struct inode *inode, loff_t offset, + loff_t length, unsigned int flags, + struct iomap *iomap, int ntfs_iomap_flags) +{ + struct ntfs_inode *ni = NTFS_I(inode); + loff_t end = offset + length; + + if (NVolShutdown(ni->vol)) + return -EIO; + + if (ntfs_iomap_flags & (NTFS_IOMAP_FLAGS_BEGIN | NTFS_IOMAP_FLAGS_DIO) && + end > ni->data_size) { + struct ntfs_volume *vol = ni->vol; + int ret; + + mutex_lock(&ni->mrec_lock); + if (end > ni->allocated_size && + end < ni->allocated_size + vol->preallocated_size) + ret = ntfs_attr_expand(ni, end, + ni->allocated_size + vol->preallocated_size); + else + ret = ntfs_attr_expand(ni, end, 0); + mutex_unlock(&ni->mrec_lock); + if (ret) + return ret; + } + + if (!NInoNonResident(ni)) { + mutex_lock(&ni->mrec_lock); + return ntfs_write_iomap_begin_resident(inode, offset, iomap); + } + return ntfs_write_iomap_begin_non_resident(inode, offset, length, flags, + iomap, ntfs_iomap_flags); +} + +static int ntfs_write_iomap_begin(struct inode *inode, loff_t offset, + loff_t length, unsigned int flags, + struct iomap *iomap, struct iomap *srcmap) +{ + return __ntfs_write_iomap_begin(inode, offset, length, flags, iomap, + NTFS_IOMAP_FLAGS_BEGIN); +} + +static int ntfs_write_iomap_end_resident(struct inode *inode, loff_t pos, + loff_t length, ssize_t written, + unsigned int flags, struct iomap *iomap) +{ + struct ntfs_inode *ni = NTFS_I(inode); + struct ntfs_attr_search_ctx *ctx; + u32 attr_len; + int err; + char *kattr; + struct page *ipage = iomap->private; + + mutex_lock(&ni->mrec_lock); + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + written = -ENOMEM; + goto err_out; + } + + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (err) { + if (err == -ENOENT) + err = -EIO; + written = err; + goto err_out; + } + + /* The total length of the attribute value. */ + attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); + if (pos >= attr_len || pos + written > attr_len) + goto err_out; + + kattr = (u8 *)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset); + memcpy(kattr + pos, iomap_inline_data(iomap, pos), written); + mark_mft_record_dirty(ctx->ntfs_ino); +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + put_page(ipage); + mutex_unlock(&ni->mrec_lock); + return written; + +} + +static int ntfs_write_iomap_end(struct inode *inode, loff_t pos, loff_t length, + ssize_t written, unsigned int flags, + struct iomap *iomap) +{ + if (iomap->type == IOMAP_INLINE) + return ntfs_write_iomap_end_resident(inode, pos, length, + written, flags, iomap); + return written; +} + +const struct iomap_ops ntfs_write_iomap_ops = { + .iomap_begin = ntfs_write_iomap_begin, + .iomap_end = ntfs_write_iomap_end, +}; + +static int ntfs_page_mkwrite_iomap_begin(struct inode *inode, loff_t offset, + loff_t length, unsigned int flags, + struct iomap *iomap, struct iomap *srcmap) +{ + return __ntfs_write_iomap_begin(inode, offset, length, flags, iomap, + NTFS_IOMAP_FLAGS_MKWRITE); +} + +const struct iomap_ops ntfs_page_mkwrite_iomap_ops = { + .iomap_begin = ntfs_page_mkwrite_iomap_begin, + .iomap_end = ntfs_write_iomap_end, +}; + +static int ntfs_dio_iomap_begin(struct inode *inode, loff_t offset, + loff_t length, unsigned int flags, + struct iomap *iomap, struct iomap *srcmap) +{ + return __ntfs_write_iomap_begin(inode, offset, length, flags, iomap, + NTFS_IOMAP_FLAGS_DIO); +} + +const struct iomap_ops ntfs_dio_iomap_ops = { + .iomap_begin = ntfs_dio_iomap_begin, + .iomap_end = ntfs_write_iomap_end, +}; + +static ssize_t ntfs_writeback_range(struct iomap_writepage_ctx *wpc, + struct folio *folio, u64 offset, unsigned int len, u64 end_pos) +{ + if (offset < wpc->iomap.offset || + offset >= wpc->iomap.offset + wpc->iomap.length) { + int error; + + error = __ntfs_write_iomap_begin(wpc->inode, offset, + NTFS_I(wpc->inode)->allocated_size - offset, + IOMAP_WRITE, &wpc->iomap, + NTFS_IOMAP_FLAGS_WRITEBACK); + if (error) + return error; + } + + return iomap_add_to_ioend(wpc, folio, offset, end_pos, len); +} + +const struct iomap_writeback_ops ntfs_writeback_ops = { + .writeback_range = ntfs_writeback_range, + .writeback_submit = iomap_ioend_writeback_submit, +}; diff --git a/fs/ntfs/iomap.h b/fs/ntfs/iomap.h new file mode 100644 index 000000000000..3abc1d493e91 --- /dev/null +++ b/fs/ntfs/iomap.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#ifndef _LINUX_NTFS_IOMAP_H +#define _LINUX_NTFS_IOMAP_H + +#include +#include + +#include "volume.h" +#include "inode.h" + +extern const struct iomap_ops ntfs_write_iomap_ops; +extern const struct iomap_ops ntfs_read_iomap_ops; +extern const struct iomap_ops ntfs_seek_iomap_ops; +extern const struct iomap_ops ntfs_page_mkwrite_iomap_ops; +extern const struct iomap_ops ntfs_dio_iomap_ops; +extern const struct iomap_writeback_ops ntfs_writeback_ops; +extern const struct iomap_write_ops ntfs_iomap_folio_ops; +extern int ntfs_dio_zero_range(struct inode *inode, loff_t offset, loff_t length); +#endif /* _LINUX_NTFS_IOMAP_H */ diff --git a/fs/ntfs/layout.h b/fs/ntfs/layout.h new file mode 100644 index 000000000000..d94f914e830f --- /dev/null +++ b/fs/ntfs/layout.h @@ -0,0 +1,2346 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * All NTFS associated on-disk structures. + * + * Copyright (c) 2001-2005 Anton Altaparmakov + * Copyright (c) 2002 Richard Russon + */ + +#ifndef _LINUX_NTFS_LAYOUT_H +#define _LINUX_NTFS_LAYOUT_H + +#include +#include +#include +#include + +/* The NTFS oem_id "NTFS " */ +#define magicNTFS cpu_to_le64(0x202020205346544eULL) + +/* + * Location of bootsector on partition: + * The standard NTFS_BOOT_SECTOR is on sector 0 of the partition. + * On NT4 and above there is one backup copy of the boot sector to + * be found on the last sector of the partition (not normally accessible + * from within Windows as the bootsector contained number of sectors + * value is one less than the actual value!). + * On versions of NT 3.51 and earlier, the backup copy was located at + * number of sectors/2 (integer divide), i.e. in the middle of the volume. + */ + +/* + * BIOS parameter block (bpb) structure. + * + * @bytes_per_sector: Size of a sector in bytes (usually 512). + * Matches the logical sector size of the underlying device. + * @sectors_per_cluster: Size of a cluster in sectors (NTFS cluster size / sector size). + * @reserved_sectors: Number of reserved sectors at the beginning of the volume. + * Always set to 0 in NTFS. + * @fats: Number of FAT tables. + * Always 0 in NTFS (no FAT tables exist). + * @root_entries: Number of entries in the root directory. + * Always 0 in NTFS. + * @sectors: Total number of sectors on the volume. + * Always 0 in NTFS (use @large_sectors instead). + * @media_type: Media descriptor byte. + * 0xF8 for hard disk (fixed media) in NTFS. + * @sectors_per_fat: Number of sectors per FAT table. + * Always 0 in NTFS. + * @sectors_per_track: Number of sectors per track. + * Irrelevant for NTFS. + * @heads: Number of heads (CHS geometry). + * Irrelevant for NTFS. + * @hidden_sectors: Number of hidden sectors before the start of the partition. + * Always 0 in NTFS boot sector. + * @large_sectors: Total number of sectors on the volume. + */ +struct bios_parameter_block { + __le16 bytes_per_sector; + u8 sectors_per_cluster; + __le16 reserved_sectors; + u8 fats; + __le16 root_entries; + __le16 sectors; + u8 media_type; + __le16 sectors_per_fat; + __le16 sectors_per_track; + __le16 heads; + __le32 hidden_sectors; + __le32 large_sectors; +} __packed; + +/* + * NTFS boot sector structure. + * + * @jump: 3-byte jump instruction to boot code (irrelevant for NTFS). + * Typically 0xEB 0x52 0x90 or similar. + * @oem_id: OEM identifier string (8 bytes). + * Always "NTFS " (with trailing spaces) in NTFS volumes. + * @bpb: Legacy BIOS Parameter Block (see struct bios_parameter_block). + * Mostly zeroed or set to fixed values for NTFS compatibility. + * @unused: 4 bytes, reserved/unused. + * NTFS disk editors show it as: + * - physical_drive (0x80 for fixed disk) + * - current_head (0) + * - extended_boot_signature (0x80 or 0x28) + * - unused (0) + * Always zero in practice for NTFS. + * @number_of_sectors: Number of sectors in volume. Gives maximum volume + * size of 2^63 sectors. Assuming standard sector + * size of 512 bytes, the maximum byte size is + * approx. 4.7x10^21 bytes. (-; + * @mft_lcn: Logical cluster number (LCN) of the $MFT data attribute. + * Location of the Master File Table. + * @mftmirr_lcn: LCN of the $MFTMirr (first 3-4 MFT records copy). + * Mirror for boot-time recovery. + * @clusters_per_mft_record: + * Size of each MFT record in clusters. + * @reserved0: 3 bytes, reserved/zero. + * @clusters_per_index_record: + * Size of each index block/record in clusters. + * @reserved1: 3 bytes, reserved/zero. + * @volume_serial_number: + * 64-bit volume serial number. + * Used for identification (irrelevant for NTFS operation). + * @checksum: 32-bit checksum of the boot sector (excluding this field). + * Used to detect boot sector corruption. + * @bootstrap: 426 bytes of bootstrap code. + * Irrelevant for NTFS (contains x86 boot loader stub). + * @end_of_sector_marker: + * 2-byte end-of-sector signature. + * Always 0xAA55 (little-endian magic number). + */ +struct ntfs_boot_sector { + u8 jump[3]; + __le64 oem_id; + struct bios_parameter_block bpb; + u8 unused[4]; + __le64 number_of_sectors; + __le64 mft_lcn; + __le64 mftmirr_lcn; + s8 clusters_per_mft_record; + u8 reserved0[3]; + s8 clusters_per_index_record; + u8 reserved1[3]; + __le64 volume_serial_number; + __le32 checksum; + u8 bootstrap[426]; + __le16 end_of_sector_marker; +} __packed; + +static_assert(sizeof(struct ntfs_boot_sector) == 512); + +/* + * Magic identifiers present at the beginning of all ntfs record containing + * records (like mft records for example). + * + * magic_FILE: MFT entry header ("FILE" in ASCII). + * Used in $MFT/$DATA for all master file table records. + * magic_INDX: Index buffer header ("INDX" in ASCII). + * Used in $INDEX_ALLOCATION attributes (directories, $I30 indexes). + * magic_HOLE: Hole marker ("HOLE" in ASCII). + * Introduced in NTFS 3.0+, used for sparse/hole regions in some contexts. + * magic_RSTR: Restart page header ("RSTR" in ASCII). + * Used in LogFile for restart pages (transaction log recovery). + * magic_RCRD: Log record page header ("RCRD" in ASCII). + * Used in LogFile for individual log record pages. + * magic_CHKD: Chkdsk modified marker ("CHKD" in ASCII). + * Set by chkdsk when it modifies a record; indicates repair was done. + * magic_BAAD: Bad record marker ("BAAD" in ASCII). + * Indicates a multi-sector transfer failure was detected. + * The record is corrupted/unusable; often set during I/O errors. + * magic_empty: Empty/uninitialized page marker (0xffffffff). + * Used in LogFile when a page is filled with 0xff bytes + * and has not yet been initialized. Must be formatted before use. + */ +enum { + magic_FILE = cpu_to_le32(0x454c4946), + magic_INDX = cpu_to_le32(0x58444e49), + magic_HOLE = cpu_to_le32(0x454c4f48), + magic_RSTR = cpu_to_le32(0x52545352), + magic_RCRD = cpu_to_le32(0x44524352), + magic_CHKD = cpu_to_le32(0x444b4843), + magic_BAAD = cpu_to_le32(0x44414142), + magic_empty = cpu_to_le32(0xffffffff) +}; + +/* + * Generic magic comparison macros. Finally found a use for the ## preprocessor + * operator! (-8 + */ + +static inline bool __ntfs_is_magic(__le32 x, __le32 r) +{ + return (x == r); +} +#define ntfs_is_magic(x, m) __ntfs_is_magic(x, magic_##m) + +static inline bool __ntfs_is_magicp(__le32 *p, __le32 r) +{ + return (*p == r); +} +#define ntfs_is_magicp(p, m) __ntfs_is_magicp(p, magic_##m) + +/* + * Specialised magic comparison macros for the NTFS_RECORD_TYPEs defined above. + */ +#define ntfs_is_file_record(x) (ntfs_is_magic(x, FILE)) +#define ntfs_is_file_recordp(p) (ntfs_is_magicp(p, FILE)) +#define ntfs_is_mft_record(x) (ntfs_is_file_record(x)) +#define ntfs_is_mft_recordp(p) (ntfs_is_file_recordp(p)) +#define ntfs_is_indx_record(x) (ntfs_is_magic(x, INDX)) +#define ntfs_is_indx_recordp(p) (ntfs_is_magicp(p, INDX)) +#define ntfs_is_hole_record(x) (ntfs_is_magic(x, HOLE)) +#define ntfs_is_hole_recordp(p) (ntfs_is_magicp(p, HOLE)) + +#define ntfs_is_rstr_record(x) (ntfs_is_magic(x, RSTR)) +#define ntfs_is_rstr_recordp(p) (ntfs_is_magicp(p, RSTR)) +#define ntfs_is_rcrd_record(x) (ntfs_is_magic(x, RCRD)) +#define ntfs_is_rcrd_recordp(p) (ntfs_is_magicp(p, RCRD)) + +#define ntfs_is_chkd_record(x) (ntfs_is_magic(x, CHKD)) +#define ntfs_is_chkd_recordp(p) (ntfs_is_magicp(p, CHKD)) + +#define ntfs_is_baad_record(x) (ntfs_is_magic(x, BAAD)) +#define ntfs_is_baad_recordp(p) (ntfs_is_magicp(p, BAAD)) + +#define ntfs_is_empty_record(x) (ntfs_is_magic(x, empty)) +#define ntfs_is_empty_recordp(p) (ntfs_is_magicp(p, empty)) + +/* + * struct ntfs_record - Common header for all multi-sector protected NTFS records + * + * @magic: 4-byte magic identifier for the record type and/or status. + * Common values are defined in the magic_* enum (FILE, INDX, RSTR, + * RCRD, CHKD, BAAD, HOLE, empty). + * - "FILE" = MFT record + * - "INDX" = Index allocation block + * - "BAAD" = Record corrupted (multi-sector fixup failed) + * - 0xffffffff = Uninitialized/empty page + * @usa_ofs: Offset (in bytes) from the start of this record to the Update + * Sequence Array (USA). + * The USA is located at record + usa_ofs. + * @usa_count: Number of 16-bit entries in the USA array (including the Update + * Sequence Number itself). + * - Number of fixup locations = usa_count - 1 + * - Each fixup location is a 16-bit value in the record that needs + * protection against torn writes. + * + * The Update Sequence Array (usa) is an array of the __le16 values which belong + * to the end of each sector protected by the update sequence record in which + * this array is contained. Note that the first entry is the Update Sequence + * Number (usn), a cyclic counter of how many times the protected record has + * been written to disk. The values 0 and -1 (ie. 0xffff) are not used. All + * last le16's of each sector have to be equal to the usn (during reading) or + * are set to it (during writing). If they are not, an incomplete multi sector + * transfer has occurred when the data was written. + * The maximum size for the update sequence array is fixed to: + * maximum size = usa_ofs + (usa_count * 2) = 510 bytes + * The 510 bytes comes from the fact that the last __le16 in the array has to + * (obviously) finish before the last __le16 of the first 512-byte sector. + * This formula can be used as a consistency check in that usa_ofs + + * (usa_count * 2) has to be less than or equal to 510. + */ +struct ntfs_record { + __le32 magic; + __le16 usa_ofs; + __le16 usa_count; +} __packed; + +/* + * System files mft record numbers. All these files are always marked as used + * in the bitmap attribute of the mft; presumably in order to avoid accidental + * allocation for random other mft records. Also, the sequence number for each + * of the system files is always equal to their mft record number and it is + * never modified. + * + * FILE_MFT: Master File Table (MFT) itself. + * Data attribute contains all MFT entries; + * Bitmap attribute tracks which records are in use (bit==1). + * FILE_MFTMirr: MFT mirror: copy of the first four (or more) MFT records + * in its data attribute. + * If cluster size > 4 KiB, copies first N records where + * N = cluster_size / mft_record_size. + * FILE_LogFile: Journaling log (LogFile) in data attribute. + * Used for transaction logging and recovery. + * FILE_Volume: Volume information and name. + * Contains $VolumeName (label) and $VolumeInformation + * (flags, NTFS version). Windows calls this the volume DASD. + * FILE_AttrDef: Attribute definitions array in data attribute. + * Defines all possible attribute types and their properties. + * FILE_root: Root directory ($Root). + * The top-level directory of the filesystem. + * FILE_Bitmap: Cluster allocation bitmap ($Bitmap) in data attribute. + * Tracks free/used clusters (LCNs) on the volume. + * FILE_Boot: Boot sector ($Boot) in data attribute. + * Always located at cluster 0; contains BPB and NTFS parameters. + * FILE_BadClus: Bad cluster list ($BadClus) in non-resident data attribute. + * Marks all known bad clusters. + * FILE_Secure: Security descriptors ($Secure). + * Contains shared $SDS (security descriptors) and two indexes + * ($SDH, $SII). Introduced in Windows 2000. + * Before that, it was called $Quota but was unused. + * FILE_UpCase: Uppercase table ($UpCase) in data attribute. + * Maps all 65536 Unicode characters to their uppercase forms. + * FILE_Extend: System directory ($Extend). + * Contains additional system files ($ObjId, $Quota, $Reparse, + * $UsnJrnl, etc.). Introduced in NTFS 3.0 (Windows 2000). + * FILE_reserved12: Reserved for future use (MFT records 12–15). + * FILE_reserved13: Reserved. + * FILE_reserved14: Reserved. + * FILE_reserved15: Reserved. + * FILE_first_user: First possible user-created file MFT record number. + * Used as a boundary to distinguish system files from user files. + */ +enum { + FILE_MFT = 0, + FILE_MFTMirr = 1, + FILE_LogFile = 2, + FILE_Volume = 3, + FILE_AttrDef = 4, + FILE_root = 5, + FILE_Bitmap = 6, + FILE_Boot = 7, + FILE_BadClus = 8, + FILE_Secure = 9, + FILE_UpCase = 10, + FILE_Extend = 11, + FILE_reserved12 = 12, + FILE_reserved13 = 13, + FILE_reserved14 = 14, + FILE_reserved15 = 15, + FILE_first_user = 16, +}; + +/* + * enum - Flags for MFT record header + * + * These are the so far known MFT_RECORD_* flags (16-bit) which contain + * information about the mft record in which they are present. + * + * MFT_RECORD_IN_USE: This MFT record is allocated and in use. + * (bit set = record is valid/used; clear = free) + * MFT_RECORD_IS_DIRECTORY: This MFT record represents a directory. + * (Used to quickly distinguish files from directories) + * MFT_RECORD_IS_4: Indicates the record is a special "record 4" type. + * (Rarely used; related to NTFS internal special cases, + * often for $AttrDef or early system files) + * MFT_RECORD_IS_VIEW_INDEX: This MFT record is used as a view index. + * (Specific to NTFS indexed views or object ID indexes) + * MFT_REC_SPACE_FILLER: Dummy value to force the enum to be 16-bit wide. + * (Not a real flag; just a sentinel to ensure the type + * is __le16 and no higher bits are accidentally used) + */ +enum { + MFT_RECORD_IN_USE = cpu_to_le16(0x0001), + MFT_RECORD_IS_DIRECTORY = cpu_to_le16(0x0002), + MFT_RECORD_IS_4 = cpu_to_le16(0x0004), + MFT_RECORD_IS_VIEW_INDEX = cpu_to_le16(0x0008), + MFT_REC_SPACE_FILLER = cpu_to_le16(0xffff), /*Just to make flags 16-bit.*/ +} __packed; + +/* + * mft references (aka file references or file record segment references) are + * used whenever a structure needs to refer to a record in the mft. + * + * A reference consists of a 48-bit index into the mft and a 16-bit sequence + * number used to detect stale references. + * + * For error reporting purposes we treat the 48-bit index as a signed quantity. + * + * The sequence number is a circular counter (skipping 0) describing how many + * times the referenced mft record has been (re)used. This has to match the + * sequence number of the mft record being referenced, otherwise the reference + * is considered stale and removed. + * + * If the sequence number is zero it is assumed that no sequence number + * consistency checking should be performed. + */ + +/* + * Define two unpacking macros to get to the reference (MREF) and + * sequence number (MSEQNO) respectively. + * The _LE versions are to be applied on little endian MFT_REFs. + * Note: The _LE versions will return a CPU endian formatted value! + */ +#define MFT_REF_MASK_CPU 0x0000ffffffffffffULL +#define MFT_REF_MASK_LE cpu_to_le64(MFT_REF_MASK_CPU) + +#define MK_MREF(m, s) ((u64)(((u64)(s) << 48) | \ + ((u64)(m) & MFT_REF_MASK_CPU))) +#define MK_LE_MREF(m, s) cpu_to_le64(MK_MREF(m, s)) + +#define MREF(x) ((unsigned long)((x) & MFT_REF_MASK_CPU)) +#define MSEQNO(x) ((u16)(((x) >> 48) & 0xffff)) +#define MREF_LE(x) ((unsigned long)(le64_to_cpu(x) & MFT_REF_MASK_CPU)) +#define MREF_INO(x) ((unsigned long)MREF_LE(x)) +#define MSEQNO_LE(x) ((u16)((le64_to_cpu(x) >> 48) & 0xffff)) + +#define IS_ERR_MREF(x) (((x) & 0x0000800000000000ULL) ? true : false) +#define ERR_MREF(x) ((u64)((s64)(x))) +#define MREF_ERR(x) ((int)((s64)(x))) + +/* + * struct mft_record - NTFS Master File Table (MFT) record header + * + * The mft record header present at the beginning of every record in the mft. + * This is followed by a sequence of variable length attribute records which + * is terminated by an attribute of type AT_END which is a truncated attribute + * in that it only consists of the attribute type code AT_END and none of the + * other members of the attribute structure are present. + * + * magic: Record magic ("FILE" for valid MFT entries). + * See ntfs_record magic enum for other values. + * usa_ofs: Offset to Update Sequence Array (see ntfs_record). + * usa_count: Number of entries in USA (see ntfs_record). + * lsn: Log sequence number (LSN) from LogFile. + * Incremented on every modification to this record. + * sequence_number: Reuse count of this MFT record slot. + * Incremented (skipping zero) when the file is deleted. + * Zero means never reused or special case. + * Part of MFT reference (together with record number). + * link_count: Number of hard links (directory entries) to this file. + * Only meaningful in base MFT records. + * When deleting a directory entry: + * - If link_count == 1, delete the whole file + * - Else remove only the $FILE_NAME attribute and decrement + * attrs_offset: Byte offset from start of MFT record to first attribute. + * Must be 8-byte aligned. + * flags: Bit array of MFT_RECORD_* flags (see MFT_RECORD_IN_USE enum). + * MFT_RECORD_IN_USE cleared when record is freed/deleted. + * bytes_in_use: Number of bytes actually used in this MFT record. + * Must be 8-byte aligned. + * Includes header + all attributes + padding. + * bytes_allocated: Total allocated size of this MFT record. + * Usually equal to MFT record size (1024 bytes or cluster size). + * base_mft_record: MFT reference to the base record. + * 0 for base records. + * Non-zero for extension records → points to base record + * containing the $ATTRIBUTE_LIST that describes this extension. + * next_attr_instance: Next attribute instance number to assign. + * Incremented after each use. + * Reset to 0 when MFT record is reused. + * First instance is always 0. + * reserved: Reserved for alignment (NTFS 3.1+). + * mft_record_number: This MFT record's number (index in $MFT). + * Only present in NTFS 3.1+ (Windows XP and above). + */ +struct mft_record { + __le32 magic; + __le16 usa_ofs; + __le16 usa_count; + + __le64 lsn; + __le16 sequence_number; + __le16 link_count; + __le16 attrs_offset; + __le16 flags; + __le32 bytes_in_use; + __le32 bytes_allocated; + __le64 base_mft_record; + __le16 next_attr_instance; + __le16 reserved; + __le32 mft_record_number; +} __packed; + +static_assert(sizeof(struct mft_record) == 48); + +/**x + * struct mft_record_old - Old NTFS MFT record header (pre-NTFS 3.1 / Windows XP) + * + * This is the older version of the MFT record header used in NTFS versions + * prior to 3.1 (Windows XP and later). It lacks the additional fields + * @reserved and @mft_record_number that were added in NTFS 3.1+. + * + * @magic: Record magic ("FILE" for valid MFT entries). + * See ntfs_record magic enum for other values. + * @usa_ofs: Offset to Update Sequence Array (see ntfs_record). + * @usa_count: Number of entries in USA (see ntfs_record). + * @lsn: Log sequence number (LSN) from LogFile. + * Incremented on every modification to this record. + * @sequence_number: Reuse count of this MFT record slot. + * Incremented (skipping zero) when the file is deleted. + * Zero means never reused or special case. + * Part of MFT reference (together with record number). + * @link_count: Number of hard links (directory entries) to this file. + * Only meaningful in base MFT records. + * When deleting a directory entry: + * - If link_count == 1, delete the whole file + * - Else remove only the $FILE_NAME attribute and decrement + * @attrs_offset: Byte offset from start of MFT record to first attribute. + * Must be 8-byte aligned. + * @flags: Bit array of MFT_RECORD_* flags (see MFT_RECORD_IN_USE enum). + * MFT_RECORD_IN_USE cleared when record is freed/deleted. + * @bytes_in_use: Number of bytes actually used in this MFT record. + * Must be 8-byte aligned. + * Includes header + all attributes + padding. + * @bytes_allocated: Total allocated size of this MFT record. + * Usually equal to MFT record size (1024 bytes or cluster size). + * @base_mft_record: MFT reference to the base record. + * 0 for base records. + * Non-zero for extension records → points to base record + * containing the $ATTRIBUTE_LIST that describes this extension. + * @next_attr_instance: Next attribute instance number to assign. + * Incremented after each use. + * Reset to 0 when MFT record is reused. + * First instance is always 0. + */ +struct mft_record_old { + __le32 magic; + __le16 usa_ofs; + __le16 usa_count; + + __le64 lsn; + __le16 sequence_number; + __le16 link_count; + __le16 attrs_offset; + __le16 flags; + __le32 bytes_in_use; + __le32 bytes_allocated; + __le64 base_mft_record; + __le16 next_attr_instance; +} __packed; + +static_assert(sizeof(struct mft_record_old) == 42); + +/* + * System defined attributes (32-bit). Each attribute type has a corresponding + * attribute name (Unicode string of maximum 64 character length) as described + * by the attribute definitions present in the data attribute of the $AttrDef + * system file. On NTFS 3.0 volumes the names are just as the types are named + * in the below defines exchanging AT_ for the dollar sign ($). If that is not + * a revealing choice of symbol I do not know what is... (-; + */ +enum { + AT_UNUSED = cpu_to_le32(0), + AT_STANDARD_INFORMATION = cpu_to_le32(0x10), + AT_ATTRIBUTE_LIST = cpu_to_le32(0x20), + AT_FILE_NAME = cpu_to_le32(0x30), + AT_OBJECT_ID = cpu_to_le32(0x40), + AT_SECURITY_DESCRIPTOR = cpu_to_le32(0x50), + AT_VOLUME_NAME = cpu_to_le32(0x60), + AT_VOLUME_INFORMATION = cpu_to_le32(0x70), + AT_DATA = cpu_to_le32(0x80), + AT_INDEX_ROOT = cpu_to_le32(0x90), + AT_INDEX_ALLOCATION = cpu_to_le32(0xa0), + AT_BITMAP = cpu_to_le32(0xb0), + AT_REPARSE_POINT = cpu_to_le32(0xc0), + AT_EA_INFORMATION = cpu_to_le32(0xd0), + AT_EA = cpu_to_le32(0xe0), + AT_PROPERTY_SET = cpu_to_le32(0xf0), + AT_LOGGED_UTILITY_STREAM = cpu_to_le32(0x100), + AT_FIRST_USER_DEFINED_ATTRIBUTE = cpu_to_le32(0x1000), + AT_END = cpu_to_le32(0xffffffff) +}; + +/* + * The collation rules for sorting views/indexes/etc (32-bit). + * + * COLLATION_BINARY - Collate by binary compare where the first byte is most + * significant. + * COLLATION_UNICODE_STRING - Collate Unicode strings by comparing their binary + * Unicode values, except that when a character can be uppercased, the + * upper case value collates before the lower case one. + * COLLATION_FILE_NAME - Collate file names as Unicode strings. The collation + * is done very much like COLLATION_UNICODE_STRING. In fact I have no idea + * what the difference is. Perhaps the difference is that file names + * would treat some special characters in an odd way (see + * unistr.c::ntfs_collate_names() and unistr.c::legal_ansi_char_array[] + * for what I mean but COLLATION_UNICODE_STRING would not give any special + * treatment to any characters at all, but this is speculation. + * COLLATION_NTOFS_ULONG - Sorting is done according to ascending __le32 key + * values. E.g. used for $SII index in FILE_Secure, which sorts by + * security_id (le32). + * COLLATION_NTOFS_SID - Sorting is done according to ascending SID values. + * E.g. used for $O index in FILE_Extend/$Quota. + * COLLATION_NTOFS_SECURITY_HASH - Sorting is done first by ascending hash + * values and second by ascending security_id values. E.g. used for $SDH + * index in FILE_Secure. + * COLLATION_NTOFS_ULONGS - Sorting is done according to a sequence of ascending + * __le32 key values. E.g. used for $O index in FILE_Extend/$ObjId, which + * sorts by object_id (16-byte), by splitting up the object_id in four + * __le32 values and using them as individual keys. E.g. take the following + * two security_ids, stored as follows on disk: + * 1st: a1 61 65 b7 65 7b d4 11 9e 3d 00 e0 81 10 42 59 + * 2nd: 38 14 37 d2 d2 f3 d4 11 a5 21 c8 6b 79 b1 97 45 + * To compare them, they are split into four __le32 values each, like so: + * 1st: 0xb76561a1 0x11d47b65 0xe0003d9e 0x59421081 + * 2nd: 0xd2371438 0x11d4f3d2 0x6bc821a5 0x4597b179 + * Now, it is apparent why the 2nd object_id collates after the 1st: the + * first __le32 value of the 1st object_id is less than the first __le32 of + * the 2nd object_id. If the first __le32 values of both object_ids were + * equal then the second __le32 values would be compared, etc. + */ +enum { + COLLATION_BINARY = cpu_to_le32(0x00), + COLLATION_FILE_NAME = cpu_to_le32(0x01), + COLLATION_UNICODE_STRING = cpu_to_le32(0x02), + COLLATION_NTOFS_ULONG = cpu_to_le32(0x10), + COLLATION_NTOFS_SID = cpu_to_le32(0x11), + COLLATION_NTOFS_SECURITY_HASH = cpu_to_le32(0x12), + COLLATION_NTOFS_ULONGS = cpu_to_le32(0x13), +}; + +/* + * enum - Attribute definition flags + * + * The flags (32-bit) describing attribute properties in the attribute + * definition structure. + * The INDEXABLE flag is fairly certainly correct as only the file + * name attribute has this flag set and this is the only attribute indexed in + * NT4. + * + * ATTR_DEF_INDEXABLE: Attribute can be indexed. + * (Used for creating indexes like $I30, $SDH, etc.) + * ATTR_DEF_MULTIPLE: Attribute type can be present multiple times + * in the MFT record of an inode. + * (e.g., multiple $FILE_NAME, $DATA streams) + * ATTR_DEF_NOT_ZERO: Attribute value must contain at least one non-zero byte. + * (Prevents empty or all-zero values) + * ATTR_DEF_INDEXED_UNIQUE: Attribute must be indexed and the value must be unique + * for this attribute type across all MFT records of an inode. + * (e.g., security descriptor IDs in $Secure) + * ATTR_DEF_NAMED_UNIQUE: Attribute must be named and the name must be unique + * for this attribute type across all MFT records of an inode. + * (e.g., named $DATA streams or alternate data streams) + * ATTR_DEF_RESIDENT: Attribute must be resident (stored in MFT record). + * (Cannot be non-resident/sparse/compressed) + * ATTR_DEF_ALWAYS_LOG: Always log modifications to this attribute in LogFile, + * regardless of whether it is resident or non-resident. + * Without this flag, modifications are logged only if resident. + * (Used for critical metadata attributes) + */ +enum { + ATTR_DEF_INDEXABLE = cpu_to_le32(0x02), + ATTR_DEF_MULTIPLE = cpu_to_le32(0x04), + ATTR_DEF_NOT_ZERO = cpu_to_le32(0x08), + ATTR_DEF_INDEXED_UNIQUE = cpu_to_le32(0x10), + ATTR_DEF_NAMED_UNIQUE = cpu_to_le32(0x20), + ATTR_DEF_RESIDENT = cpu_to_le32(0x40), + ATTR_DEF_ALWAYS_LOG = cpu_to_le32(0x80), +}; + +/* + * struct attr_def - Attribute definition entry ($AttrDef array) + * + * The data attribute of FILE_AttrDef contains a sequence of attribute + * definitions for the NTFS volume. With this, it is supposed to be safe for an + * older NTFS driver to mount a volume containing a newer NTFS version without + * damaging it (that's the theory. In practice it's: not damaging it too much). + * Entries are sorted by attribute type. The flags describe whether the + * attribute can be resident/non-resident and possibly other things, but the + * actual bits are unknown. + * + * @name: Unicode (UTF-16LE) name of the attribute (e.g. "$DATA", "$FILE_NAME"). + * Zero-terminated string, maximum 0x40 characters (128 bytes). + * Used for human-readable display and debugging. + * @type: Attribute type code (ATTR_TYPE_* constants). + * Defines which attribute this entry describes. + * @display_rule: Default display rule (usually 0; rarely used in modern NTFS). + * Controls how the attribute is displayed in tools (legacy). + * @collation_rule: Default collation rule for indexing this attribute. + * Determines sort order when indexed (e.g. CASE_SENSITIVE, UNICODE). + * Used in $I30, $SDH, $SII, etc. + * @flags: Bit array of attribute constraints (ATTR_DEF_* flags). + * See ATTR_DEF_INDEXABLE, ATTR_DEF_MULTIPLE, etc. + * Defines whether the attribute can be indexed, multiple, resident-only, etc. + * @min_size: Optional minimum size of the attribute value (in bytes). + * 0 means no minimum enforced. + * @max_size: Maximum allowed size of the attribute value (in bytes). + */ +struct attr_def { + __le16 name[0x40]; + __le32 type; + __le32 display_rule; + __le32 collation_rule; + __le32 flags; + __le64 min_size; + __le64 max_size; +} __packed; + +static_assert(sizeof(struct attr_def) == 160); + +/* + * enum - Attribute flags (16-bit) for non-resident attributes + * + * ATTR_IS_COMPRESSED: Attribute is compressed. + * If set, data is compressed using the method in + * ATTR_COMPRESSION_MASK. + * ATTR_COMPRESSION_MASK: Mask for compression method. + * Valid values are defined in NTFS compression types + * (e.g., 0x02 = LZNT1, etc.). + * Also serves as the first illegal value for method. + * ATTR_IS_ENCRYPTED: Attribute is encrypted. + * Data is encrypted using EFS (Encrypting File System). + * ATTR_IS_SPARSE: Attribute is sparse. + * Contains holes (unallocated regions) that read as zeros. + */ +enum { + ATTR_IS_COMPRESSED = cpu_to_le16(0x0001), + ATTR_COMPRESSION_MASK = cpu_to_le16(0x00ff), + ATTR_IS_ENCRYPTED = cpu_to_le16(0x4000), + ATTR_IS_SPARSE = cpu_to_le16(0x8000), +} __packed; + +/* + * Attribute compression. + * + * Only the data attribute is ever compressed in the current ntfs driver in + * Windows. Further, compression is only applied when the data attribute is + * non-resident. Finally, to use compression, the maximum allowed cluster size + * on a volume is 4kib. + * + * The compression method is based on independently compressing blocks of X + * clusters, where X is determined from the compression_unit value found in the + * non-resident attribute record header (more precisely: X = 2^compression_unit + * clusters). On Windows NT/2k, X always is 16 clusters (compression_unit = 4). + * + * There are three different cases of how a compression block of X clusters + * can be stored: + * + * 1) The data in the block is all zero (a sparse block): + * This is stored as a sparse block in the runlist, i.e. the runlist + * entry has length = X and lcn = -1. The mapping pairs array actually + * uses a delta_lcn value length of 0, i.e. delta_lcn is not present at + * all, which is then interpreted by the driver as lcn = -1. + * NOTE: Even uncompressed files can be sparse on NTFS 3.0 volumes, then + * the same principles apply as above, except that the length is not + * restricted to being any particular value. + * + * 2) The data in the block is not compressed: + * This happens when compression doesn't reduce the size of the block + * in clusters. I.e. if compression has a small effect so that the + * compressed data still occupies X clusters, then the uncompressed data + * is stored in the block. + * This case is recognised by the fact that the runlist entry has + * length = X and lcn >= 0. The mapping pairs array stores this as + * normal with a run length of X and some specific delta_lcn, i.e. + * delta_lcn has to be present. + * + * 3) The data in the block is compressed: + * The common case. This case is recognised by the fact that the run + * list entry has length L < X and lcn >= 0. The mapping pairs array + * stores this as normal with a run length of X and some specific + * delta_lcn, i.e. delta_lcn has to be present. This runlist entry is + * immediately followed by a sparse entry with length = X - L and + * lcn = -1. The latter entry is to make up the vcn counting to the + * full compression block size X. + * + * In fact, life is more complicated because adjacent entries of the same type + * can be coalesced. This means that one has to keep track of the number of + * clusters handled and work on a basis of X clusters at a time being one + * block. An example: if length L > X this means that this particular runlist + * entry contains a block of length X and part of one or more blocks of length + * L - X. Another example: if length L < X, this does not necessarily mean that + * the block is compressed as it might be that the lcn changes inside the block + * and hence the following runlist entry describes the continuation of the + * potentially compressed block. The block would be compressed if the + * following runlist entry describes at least X - L sparse clusters, thus + * making up the compression block length as described in point 3 above. (Of + * course, there can be several runlist entries with small lengths so that the + * sparse entry does not follow the first data containing entry with + * length < X.) + * + * NOTE: At the end of the compressed attribute value, there most likely is not + * just the right amount of data to make up a compression block, thus this data + * is not even attempted to be compressed. It is just stored as is, unless + * the number of clusters it occupies is reduced when compressed in which case + * it is stored as a compressed compression block, complete with sparse + * clusters at the end. + */ + +/* + * enum - Flags for resident attributes (8-bit) + * + * RESIDENT_ATTR_IS_INDEXED: Attribute is referenced in an index. + * (e.g., part of an index key or entry) + * Has implications for deletion and modification: + * - Cannot be freely removed if indexed + * - Index must be updated when value changes + * - Used for attributes like $FILE_NAME in directories + */ +enum { + RESIDENT_ATTR_IS_INDEXED = 0x01, +} __packed; + +/* + * struct attr_record - NTFS attribute record header + * + * Common header for both resident and non-resident attributes. + * Always aligned to an 8-byte boundary on disk. + * Located at attrs_offset in the MFT record (see struct mft_record). + * + * @type: 32-bit attribute type (ATTR_TYPE_* constants). + * Identifies the attribute + * (e.g. 0x10 = $STANDARD_INFORMATION). + * @length: Total byte size of this attribute record (resident). + * 8-byte aligned; used to locate the next attribute. + * @non_resident: 0 = resident attribute + * 1 = non-resident attribute + * @name_length: Number of Unicode characters in the attribute name. + * 0 if unnamed (most system attributes are unnamed). + * @name_offset: Byte offset from start of attribute record to the name. + * 8-byte aligned; when creating, place at end of header. + * @flags: Attribute flags (see ATTR_IS_COMPRESSED, + * ATTR_IS_ENCRYPTED, etc.). + * For resident: see RESIDENT_ATTR_* flags. + * @instance: Unique instance number within this MFT record. + * Incremented via next_attr_instance; unique per record. + * + * Resident attributes (when @non_resident == 0): + * @data.resident.value_length: Byte size of the attribute value. + * @data.resident.value_offset: Byte offset from start of attribute + * record to the value data. + * 8-byte aligned if name present. + * @data.resident.flags: Resident-specific flags + * @data.resident.reserved: Reserved/alignment to 8 bytes. + * + * Non-resident attributes (when @non_resident == 1): + * @data.non_resident.lowest_vcn: Lowest valid VCN in this extent. + * Usually 0 unless attribute list is used. + * @data.non_resident.highest_vcn: Highest valid VCN in this extent. + * -1 for zero-length, 0 for single extent. + * @data.non_resident.mapping_pairs_offset: + * Byte offset to mapping pairs array + * (VCN → LCN mappings). + * 8-byte aligned when creating. + * @data.non_resident.compression_unit: + * Log2 of clusters per compression unit. + * 0 = not compressed. + * WinNT4 used 4; sparse files use 0 + * on XP SP2+. + * @data.non_resident.reserved: 5 bytes for 8-byte alignment. + * @data.non_resident.allocated_size: + * Allocated disk space in bytes. + * For compressed: logical allocated size. + * @data.non_resident.data_size: Logical attribute value size in bytes. + * Can be larger than allocated_size if + * compressed/sparse. + * @data.non_resident.initialized_size: + * Initialized portion size in bytes. + * Usually equals data_size. + * @data.non_resident.compressed_size: + * Compressed on-disk size in bytes. + * Only present when compressed or sparse. + * Actual disk usage. + */ +struct attr_record { + __le32 type; + __le32 length; + u8 non_resident; + u8 name_length; + __le16 name_offset; + __le16 flags; + __le16 instance; + union { + struct { + __le32 value_length; + __le16 value_offset; + u8 flags; + s8 reserved; + } __packed resident; + struct { + __le64 lowest_vcn; + __le64 highest_vcn; + __le16 mapping_pairs_offset; + u8 compression_unit; + u8 reserved[5]; + __le64 allocated_size; + __le64 data_size; + __le64 initialized_size; + __le64 compressed_size; + } __packed non_resident; + } __packed data; +} __packed; + +/* + * enum - NTFS file attribute flags (32-bit) + * + * File attribute flags (32-bit) appearing in the file_attributes fields of the + * STANDARD_INFORMATION attribute of MFT_RECORDs and the FILENAME_ATTR + * attributes of MFT_RECORDs and directory index entries. + * + * All of the below flags appear in the directory index entries but only some + * appear in the STANDARD_INFORMATION attribute whilst only some others appear + * in the FILENAME_ATTR attribute of MFT_RECORDs. Unless otherwise stated the + * flags appear in all of the above. + * + * FILE_ATTR_READONLY: File is read-only. + * FILE_ATTR_HIDDEN: File is hidden (not shown by default). + * FILE_ATTR_SYSTEM: System file (protected by OS). + * FILE_ATTR_DIRECTORY: Directory flag (reserved in NT; use MFT flag instead). + * FILE_ATTR_ARCHIVE: File needs archiving (backup flag). + * FILE_ATTR_DEVICE: Device file (rarely used). + * FILE_ATTR_NORMAL: Normal file (no special attributes). + * FILE_ATTR_TEMPORARY: Temporary file (delete on close). + * FILE_ATTR_SPARSE_FILE: Sparse file (contains holes). + * FILE_ATTR_REPARSE_POINT: Reparse point (junction, symlink, mount point). + * FILE_ATTR_COMPRESSED: File is compressed. + * FILE_ATTR_OFFLINE: File data is offline (not locally available). + * FILE_ATTR_NOT_CONTENT_INDEXED: + * File is excluded from content indexing. + * FILE_ATTR_ENCRYPTED: File is encrypted (EFS). + * FILE_ATTR_VALID_FLAGS: Mask of all valid flags for reading. + * FILE_ATTR_VALID_SET_FLAGS: Mask of flags that can be set by user. + * FILE_ATTRIBUTE_RECALL_ON_OPEN: + * Recall data on open (cloud/HSM related). + * FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT: + * $FILE_NAME has duplicate index entry. + * FILE_ATTR_DUP_VIEW_INDEX_PRESENT: + * Duplicate view index present (object ID, quota, etc.). + */ +enum { + FILE_ATTR_READONLY = cpu_to_le32(0x00000001), + FILE_ATTR_HIDDEN = cpu_to_le32(0x00000002), + FILE_ATTR_SYSTEM = cpu_to_le32(0x00000004), + /* Old DOS volid. Unused in NT. = cpu_to_le32(0x00000008), */ + FILE_ATTR_DIRECTORY = cpu_to_le32(0x00000010), + FILE_ATTR_ARCHIVE = cpu_to_le32(0x00000020), + FILE_ATTR_DEVICE = cpu_to_le32(0x00000040), + FILE_ATTR_NORMAL = cpu_to_le32(0x00000080), + + FILE_ATTR_TEMPORARY = cpu_to_le32(0x00000100), + FILE_ATTR_SPARSE_FILE = cpu_to_le32(0x00000200), + FILE_ATTR_REPARSE_POINT = cpu_to_le32(0x00000400), + FILE_ATTR_COMPRESSED = cpu_to_le32(0x00000800), + + FILE_ATTR_OFFLINE = cpu_to_le32(0x00001000), + FILE_ATTR_NOT_CONTENT_INDEXED = cpu_to_le32(0x00002000), + FILE_ATTR_ENCRYPTED = cpu_to_le32(0x00004000), + + FILE_ATTR_VALID_FLAGS = cpu_to_le32(0x00007fb7), + FILE_ATTR_VALID_SET_FLAGS = cpu_to_le32(0x000031a7), + FILE_ATTRIBUTE_RECALL_ON_OPEN = cpu_to_le32(0x00040000), + FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT = cpu_to_le32(0x10000000), + FILE_ATTR_DUP_VIEW_INDEX_PRESENT = cpu_to_le32(0x20000000), +}; + +/* + * NOTE on times in NTFS: All times are in MS standard time format, i.e. they + * are the number of 100-nanosecond intervals since 1st January 1601, 00:00:00 + * universal coordinated time (UTC). (In Linux time starts 1st January 1970, + * 00:00:00 UTC and is stored as the number of 1-second intervals since then.) + */ + +/* + * struct standard_information - $STANDARD_INFORMATION attribute content + * + * NOTE: Always resident. + * NOTE: Present in all base file records on a volume. + * NOTE: There is conflicting information about the meaning of each of the time + * fields but the meaning as defined below has been verified to be + * correct by practical experimentation on Windows NT4 SP6a and is hence + * assumed to be the one and only correct interpretation. + * + * @creation_time: File creation time (NTFS timestamp). + * Updated on filename change(?). + * @last_data_change_time: Last modification time of data streams. + * @last_mft_change_time: Last modification time of this MFT record. + * @last_access_time: Last access time (approximate). + * Not updated on read-only volumes; can be disabled. + * @file_attributes: File attribute flags (FILE_ATTR_* bits). + * + * Union (version-specific fields): + * @ver.v1.reserved12: 12 bytes reserved/alignment (NTFS 1.2 only). + * + * @ver.v3 (NTFS 3.x / Windows 2000+): + * @maximum_versions: Max allowed file versions (0 = disabled). + * @version_number: Current version number (0 if disabled). + * @class_id: Class ID (from bidirectional index?). + * @owner_id: Owner ID (maps to $Quota via $Q index). + * @security_id: Security ID (maps to $Secure $SII/$SDS). + * @quota_charged: Quota charge in bytes (0 if quotas disabled). + * @usn: Last USN from $UsnJrnl (0 if disabled). + */ +struct standard_information { + __le64 creation_time; + __le64 last_data_change_time; + __le64 last_mft_change_time; + __le64 last_access_time; + __le32 file_attributes; + union { + struct { + u8 reserved12[12]; + } __packed v1; + struct { + __le32 maximum_versions; + __le32 version_number; + __le32 class_id; + __le32 owner_id; + __le32 security_id; + __le64 quota_charged; + __le64 usn; + } __packed v3; + } __packed ver; +} __packed; + +/* + * struct attr_list_entry - Entry in $ATTRIBUTE_LIST attribute. + * + * @type: Attribute type code (ATTR_TYPE_*). + * @length: Byte size of this entry (8-byte aligned). + * @name_length: Unicode char count of attribute name (0 if unnamed). + * @name_offset: Byte offset from start of entry to name (always set). + * @lowest_vcn: Lowest VCN of this attribute extent (usually 0). + * Signed value; non-zero when attribute spans extents. + * @mft_reference: MFT record reference holding this attribute extent. + * @instance: Attribute instance number (if lowest_vcn == 0); else 0. + * @name: Variable Unicode name (use @name_offset when reading). + * + * - Can be either resident or non-resident. + * - Value consists of a sequence of variable length, 8-byte aligned, + * ATTR_LIST_ENTRY records. + * - The list is not terminated by anything at all! The only way to know when + * the end is reached is to keep track of the current offset and compare it to + * the attribute value size. + * - The attribute list attribute contains one entry for each attribute of + * the file in which the list is located, except for the list attribute + * itself. The list is sorted: first by attribute type, second by attribute + * name (if present), third by instance number. The extents of one + * non-resident attribute (if present) immediately follow after the initial + * extent. They are ordered by lowest_vcn and have their instance set to zero. + * It is not allowed to have two attributes with all sorting keys equal. + * - Further restrictions: + * - If not resident, the vcn to lcn mapping array has to fit inside the + * base mft record. + * - The attribute list attribute value has a maximum size of 256kb. This + * is imposed by the Windows cache manager. + * - Attribute lists are only used when the attributes of mft record do not + * fit inside the mft record despite all attributes (that can be made + * non-resident) having been made non-resident. This can happen e.g. when: + * - File has a large number of hard links (lots of file name + * attributes present). + * - The mapping pairs array of some non-resident attribute becomes so + * large due to fragmentation that it overflows the mft record. + * - The security descriptor is very complex (not applicable to + * NTFS 3.0 volumes). + * - There are many named streams. + */ +struct attr_list_entry { + __le32 type; + __le16 length; + u8 name_length; + u8 name_offset; + __le64 lowest_vcn; + __le64 mft_reference; + __le16 instance; + __le16 name[]; +} __packed; + +/* + * The maximum allowed length for a file name. + */ +#define MAXIMUM_FILE_NAME_LENGTH 255 + +/* + * enum - Possible namespaces for filenames in ntfs (8-bit). + * + * FILE_NAME_POSIX POSIX namespace (case sensitive, most permissive). + * Allows all Unicode except '\0' and '/'. + * WinNT/2k/2003 default utilities ignore case + * differences. SFU (Services For Unix) enables true + * case sensitivity. + * SFU restricts some chars: '"', '/', '<', '>', '\'. + * FILE_NAME_WIN32 Standard WinNT/2k long filename namespace + * (case insensitive). + * Disallows '\0', '"', '*', '/', ':', '<', '>', '?', + * '\', '|'. Names cannot end with '.' or space. + * FILE_NAME_DOS DOS 8.3 namespace (uppercase only). + * Allows 8-bit chars > space except '"', '*', '+', + * ',', '/', ':', ';', '<', '=', '>', '?', '\'. + * FILE_NAME_WIN32_AND_DOS + * Win32 and DOS names are identical (single record). + * Value 0x03 indicates both are stored in one entry. + */ +enum { + FILE_NAME_POSIX = 0x00, + FILE_NAME_WIN32 = 0x01, + FILE_NAME_DOS = 0x02, + FILE_NAME_WIN32_AND_DOS = 0x03, +} __packed; + +/* + * struct file_name_attr - $FILE_NAME attribute content + * + * NOTE: Always resident. + * NOTE: All fields, except the parent_directory, are only updated when the + * filename is changed. Until then, they just become out of sync with + * reality and the more up to date values are present in the standard + * information attribute. + * NOTE: There is conflicting information about the meaning of each of the time + * fields but the meaning as defined below has been verified to be + * correct by practical experimentation on Windows NT4 SP6a and is hence + * assumed to be the one and only correct interpretation. + * + * @parent_directory: MFT reference to parent directory. + * @creation_time: File creation time (NTFS timestamp). + * @last_data_change_time: + * Last data modification time. + * @last_mft_change_time: + * Last MFT record modification time. + * @last_access_time: Last access time (approximate; may not + * update always). + * @allocated_size: On-disk allocated size for unnamed $DATA. + * Equals compressed_size if compressed/sparse. + * 0 for directories or no $DATA. + * Multiple of cluster size. + * @data_size: Logical size of unnamed $DATA. + * 0 for directories or no $DATA. + * @file_attributes: File attribute flags (FILE_ATTR_* bits). + * @type.ea.packed_ea_size: + * Size needed to pack EAs (if present). + * @type.ea.reserved: Alignment padding. + * @type.rp.reparse_point_tag: + * Reparse point type (if reparse point, no EAs). + * @file_name_length: Length of filename in Unicode characters. + * @file_name_type: Namespace (FILE_NAME_POSIX, WIN32, DOS, etc.). + * @file_name: Variable-length Unicode filename. + */ +struct file_name_attr { + __le64 parent_directory; + __le64 creation_time; + __le64 last_data_change_time; + __le64 last_mft_change_time; + __le64 last_access_time; + __le64 allocated_size; + __le64 data_size; + __le32 file_attributes; + union { + struct { + __le16 packed_ea_size; + __le16 reserved; + } __packed ea; + struct { + __le32 reparse_point_tag; + } __packed rp; + } __packed type; + u8 file_name_length; + u8 file_name_type; + __le16 file_name[]; +} __packed; + +/* + * struct guid - Globally Unique Identifier (GUID) structure + * + * GUID structures store globally unique identifiers (GUID). A GUID is a + * 128-bit value consisting of one group of eight hexadecimal digits, followed + * by three groups of four hexadecimal digits each, followed by one group of + * twelve hexadecimal digits. GUIDs are Microsoft's implementation of the + * distributed computing environment (DCE) universally unique identifier (UUID). + * Example of a GUID: + * 1F010768-5A73-BC91-0010A52216A7 + * + * @data1: First 32 bits (first 8 hex digits). + * @data2: Next 16 bits (first group of 4 hex digits). + * @data3: Next 16 bits (second group of 4 hex digits). + * @data4: Final 64 bits (third group of 4 + last 12 hex digits). + * data4[0-1]: third group; data4[2-7]: remaining part. + */ +struct guid { + __le32 data1; + __le16 data2; + __le16 data3; + u8 data4[8]; +} __packed; + +/* + * struct object_id_attr - $OBJECT_ID attribute content (NTFS 3.0+) + * + * NOTE: Always resident. + * + * @object_id: Unique 128-bit GUID assigned to the file. + * Core identifier; always present. + * + * Optional extended info (union; total value size 16–64 bytes): + * @extended_info.birth_volume_id: + * Birth volume GUID (where file was first created). + * @extended_info.birth_object_id: + * Birth object GUID (original ID before copy/move). + * @extended_info.domain_id: + * Domain GUID (usually zero; reserved). + */ +struct object_id_attr { + struct guid object_id; + union { + struct { + struct guid birth_volume_id; + struct guid birth_object_id; + struct guid domain_id; + } __packed; + u8 extended_info[48]; + } __packed; +} __packed; + +/* + * enum - RIDs (Relative Identifiers) in Windows/NTFS security + * + * These relative identifiers (RIDs) are used with the above identifier + * authorities to make up universal well-known SIDs. + * + * SECURITY_NULL_RID S-1-0 (Null authority) + * SECURITY_WORLD_RID S-1-1 (World/Everyone) + * SECURITY_LOCAL_RID S-1-2 (Local) + * SECURITY_CREATOR_OWNER_RID S-1-3-0 (Creator Owner) + * SECURITY_CREATOR_GROUP_RID S-1-3-1 (Creator Group) + * SECURITY_CREATOR_OWNER_SERVER_RID S-1-3-2 (Server Creator Owner) + * SECURITY_CREATOR_GROUP_SERVER_RID S-1-3-3 (Server Creator Group) + * SECURITY_DIALUP_RID S-1-5-1 (Dialup) + * SECURITY_NETWORK_RID S-1-5-2 (Network) + * SECURITY_BATCH_RID S-1-5-3 (Batch) + * SECURITY_INTERACTIVE_RID S-1-5-4 (Interactive) + * SECURITY_SERVICE_RID S-1-5-6 (Service) + * SECURITY_ANONYMOUS_LOGON_RID S-1-5-7 (Anonymous Logon) + * SECURITY_PROXY_RID S-1-5-8 (Proxy) + * SECURITY_ENTERPRISE_CONTROLLERS_RID S-1-5-9 (Enterprise DCs) + * SECURITY_SERVER_LOGON_RID S-1-5-9 (Server Logon alias) + * SECURITY_PRINCIPAL_SELF_RID S-1-5-10 (Self/PrincipalSelf) + * SECURITY_AUTHENTICATED_USER_RID S-1-5-11 (Authenticated Users) + * SECURITY_RESTRICTED_CODE_RID S-1-5-12 (Restricted Code) + * SECURITY_TERMINAL_SERVER_RID S-1-5-13 (Terminal Server) + * SECURITY_LOGON_IDS_RID S-1-5-5 (Logon session IDs base) + * SECURITY_LOCAL_SYSTEM_RID S-1-5-18 (Local System) + * SECURITY_NT_NON_UNIQUE S-1-5-21 (NT non-unique authority) + * SECURITY_BUILTIN_DOMAIN_RID S-1-5-32 (Built-in domain) + * + * Built-in domain relative RIDs (S-1-5-32-...): + * Users: + * DOMAIN_USER_RID_ADMIN Administrator + * DOMAIN_USER_RID_GUEST Guest + * DOMAIN_USER_RID_KRBTGT krbtgt (Kerberos ticket-granting) + * + * Groups: + * DOMAIN_GROUP_RID_ADMINS Administrators + * DOMAIN_GROUP_RID_USERS Users + * DOMAIN_GROUP_RID_GUESTS Guests + * DOMAIN_GROUP_RID_COMPUTERS Computers + * DOMAIN_GROUP_RID_CONTROLLERS Domain Controllers + * DOMAIN_GROUP_RID_CERT_ADMINS Cert Publishers + * DOMAIN_GROUP_RID_SCHEMA_ADMINS Schema Admins + * DOMAIN_GROUP_RID_ENTERPRISE_ADMINS Enterprise Admins + * DOMAIN_GROUP_RID_POLICY_ADMINS Policy Admins (if present) + * + * Aliases: + * DOMAIN_ALIAS_RID_ADMINS Administrators alias + * DOMAIN_ALIAS_RID_USERS Users alias + * DOMAIN_ALIAS_RID_GUESTS Guests alias + * DOMAIN_ALIAS_RID_POWER_USERS Power Users + * DOMAIN_ALIAS_RID_ACCOUNT_OPS Account Operators + * DOMAIN_ALIAS_RID_SYSTEM_OPS Server Operators + * DOMAIN_ALIAS_RID_PRINT_OPS Print Operators + * DOMAIN_ALIAS_RID_BACKUP_OPS Backup Operators + * DOMAIN_ALIAS_RID_REPLICATOR Replicator + * DOMAIN_ALIAS_RID_RAS_SERVERS RAS Servers + * DOMAIN_ALIAS_RID_PREW2KCOMPACCESS Pre-Windows 2000 Compatible Access + * + * Note: The relative identifier (RID) refers to the portion of a SID, which + * identifies a user or group in relation to the authority that issued the SID. + * For example, the universal well-known SID Creator Owner ID (S-1-3-0) is + * made up of the identifier authority SECURITY_CREATOR_SID_AUTHORITY (3) and + * the relative identifier SECURITY_CREATOR_OWNER_RID (0). + */ +enum { /* Identifier authority. */ + SECURITY_NULL_RID = 0, /* S-1-0 */ + SECURITY_WORLD_RID = 0, /* S-1-1 */ + SECURITY_LOCAL_RID = 0, /* S-1-2 */ + + SECURITY_CREATOR_OWNER_RID = 0, /* S-1-3 */ + SECURITY_CREATOR_GROUP_RID = 1, /* S-1-3 */ + + SECURITY_CREATOR_OWNER_SERVER_RID = 2, /* S-1-3 */ + SECURITY_CREATOR_GROUP_SERVER_RID = 3, /* S-1-3 */ + + SECURITY_DIALUP_RID = 1, + SECURITY_NETWORK_RID = 2, + SECURITY_BATCH_RID = 3, + SECURITY_INTERACTIVE_RID = 4, + SECURITY_SERVICE_RID = 6, + SECURITY_ANONYMOUS_LOGON_RID = 7, + SECURITY_PROXY_RID = 8, + SECURITY_ENTERPRISE_CONTROLLERS_RID = 9, + SECURITY_SERVER_LOGON_RID = 9, + SECURITY_PRINCIPAL_SELF_RID = 0xa, + SECURITY_AUTHENTICATED_USER_RID = 0xb, + SECURITY_RESTRICTED_CODE_RID = 0xc, + SECURITY_TERMINAL_SERVER_RID = 0xd, + + SECURITY_LOGON_IDS_RID = 5, + SECURITY_LOGON_IDS_RID_COUNT = 3, + + SECURITY_LOCAL_SYSTEM_RID = 0x12, + + SECURITY_NT_NON_UNIQUE = 0x15, + + SECURITY_BUILTIN_DOMAIN_RID = 0x20, + + /* + * Well-known domain relative sub-authority values (RIDs). + */ + + /* Users. */ + DOMAIN_USER_RID_ADMIN = 0x1f4, + DOMAIN_USER_RID_GUEST = 0x1f5, + DOMAIN_USER_RID_KRBTGT = 0x1f6, + + /* Groups. */ + DOMAIN_GROUP_RID_ADMINS = 0x200, + DOMAIN_GROUP_RID_USERS = 0x201, + DOMAIN_GROUP_RID_GUESTS = 0x202, + DOMAIN_GROUP_RID_COMPUTERS = 0x203, + DOMAIN_GROUP_RID_CONTROLLERS = 0x204, + DOMAIN_GROUP_RID_CERT_ADMINS = 0x205, + DOMAIN_GROUP_RID_SCHEMA_ADMINS = 0x206, + DOMAIN_GROUP_RID_ENTERPRISE_ADMINS = 0x207, + DOMAIN_GROUP_RID_POLICY_ADMINS = 0x208, + + /* Aliases. */ + DOMAIN_ALIAS_RID_ADMINS = 0x220, + DOMAIN_ALIAS_RID_USERS = 0x221, + DOMAIN_ALIAS_RID_GUESTS = 0x222, + DOMAIN_ALIAS_RID_POWER_USERS = 0x223, + + DOMAIN_ALIAS_RID_ACCOUNT_OPS = 0x224, + DOMAIN_ALIAS_RID_SYSTEM_OPS = 0x225, + DOMAIN_ALIAS_RID_PRINT_OPS = 0x226, + DOMAIN_ALIAS_RID_BACKUP_OPS = 0x227, + + DOMAIN_ALIAS_RID_REPLICATOR = 0x228, + DOMAIN_ALIAS_RID_RAS_SERVERS = 0x229, + DOMAIN_ALIAS_RID_PREW2KCOMPACCESS = 0x22a, +}; + +/* + * The universal well-known SIDs: + * + * NULL_SID S-1-0-0 + * WORLD_SID S-1-1-0 + * LOCAL_SID S-1-2-0 + * CREATOR_OWNER_SID S-1-3-0 + * CREATOR_GROUP_SID S-1-3-1 + * CREATOR_OWNER_SERVER_SID S-1-3-2 + * CREATOR_GROUP_SERVER_SID S-1-3-3 + * + * (Non-unique IDs) S-1-4 + * + * NT well-known SIDs: + * + * NT_AUTHORITY_SID S-1-5 + * DIALUP_SID S-1-5-1 + * + * NETWORD_SID S-1-5-2 + * BATCH_SID S-1-5-3 + * INTERACTIVE_SID S-1-5-4 + * SERVICE_SID S-1-5-6 + * ANONYMOUS_LOGON_SID S-1-5-7 (aka null logon session) + * PROXY_SID S-1-5-8 + * SERVER_LOGON_SID S-1-5-9 (aka domain controller account) + * SELF_SID S-1-5-10 (self RID) + * AUTHENTICATED_USER_SID S-1-5-11 + * RESTRICTED_CODE_SID S-1-5-12 (running restricted code) + * TERMINAL_SERVER_SID S-1-5-13 (running on terminal server) + * + * (Logon IDs) S-1-5-5-X-Y + * + * (NT non-unique IDs) S-1-5-0x15-... + * + * (Built-in domain) S-1-5-0x20 + */ + +/* + * struct ntfs_sid - Security Identifier (SID) structure + * + * @revision: SID revision level (usually 1). + * @sub_authority_count: Number of sub-authorities (1 or more). + * @identifier_authority: + * 48-bit identifier authority (S-1-x-...). + * @parts.high_part: high 16 bits. + * @parts.low_part: low 32 bits. + * @value: raw 6-byte array. + * @sub_authority: Variable array of 32-bit RIDs. + * At least one; defines the SID relative to authority. + * + * The SID structure is a variable-length structure used to uniquely identify + * users or groups. SID stands for security identifier. + * + * The standard textual representation of the SID is of the form: + * S-R-I-S-S... + * Where: + * - The first "S" is the literal character 'S' identifying the following + * digits as a SID. + * - R is the revision level of the SID expressed as a sequence of digits + * either in decimal or hexadecimal (if the later, prefixed by "0x"). + * - I is the 48-bit identifier_authority, expressed as digits as R above. + * - S... is one or more sub_authority values, expressed as digits as above. + * + * Example SID; the domain-relative SID of the local Administrators group on + * Windows NT/2k: + * S-1-5-32-544 + * This translates to a SID with: + * revision = 1, + * sub_authority_count = 2, + * identifier_authority = {0,0,0,0,0,5}, // SECURITY_NT_AUTHORITY + * sub_authority[0] = 32, // SECURITY_BUILTIN_DOMAIN_RID + * sub_authority[1] = 544 // DOMAIN_ALIAS_RID_ADMINS + */ +struct ntfs_sid { + u8 revision; + u8 sub_authority_count; + union { + struct { + u16 high_part; + u32 low_part; + } __packed parts; + u8 value[6]; + } identifier_authority; + __le32 sub_authority[]; +} __packed; + +/* + * enum - Predefined ACE types (8-bit) for NTFS security descriptors + * + * ACCESS_MIN_MS_ACE_TYPE: Minimum MS ACE type (0). + * ACCESS_ALLOWED_ACE_TYPE: Allow access (standard ACE). + * ACCESS_DENIED_ACE_TYPE: Deny access (standard ACE). + * SYSTEM_AUDIT_ACE_TYPE: Audit successful/failed access. + * SYSTEM_ALARM_ACE_TYPE: Alarm on access (not in Win2k+). + * ACCESS_MAX_MS_V2_ACE_TYPE: Max for V2 ACE types. + * ACCESS_ALLOWED_COMPOUND_ACE_TYPE: + * Compound ACE (legacy). + * ACCESS_MAX_MS_V3_ACE_TYPE: Max for V3 ACE types. + * ACCESS_MIN_MS_OBJECT_ACE_TYPE: Min for object ACE types (Win2k+). + * ACCESS_ALLOWED_OBJECT_ACE_TYPE: Allow with object-specific rights. + * ACCESS_DENIED_OBJECT_ACE_TYPE: Deny with object-specific rights. + * SYSTEM_AUDIT_OBJECT_ACE_TYPE: Audit with object-specific rights. + * SYSTEM_ALARM_OBJECT_ACE_TYPE: Alarm with object-specific rights. + * ACCESS_MAX_MS_OBJECT_ACE_TYPE: Max for object ACE types. + * ACCESS_MAX_MS_V4_ACE_TYPE: Max for V4 ACE types. + * ACCESS_MAX_MS_ACE_TYPE: Overall max ACE type (WinNT/2k). + */ +enum { + ACCESS_MIN_MS_ACE_TYPE = 0, + ACCESS_ALLOWED_ACE_TYPE = 0, + ACCESS_DENIED_ACE_TYPE = 1, + SYSTEM_AUDIT_ACE_TYPE = 2, + SYSTEM_ALARM_ACE_TYPE = 3, + ACCESS_MAX_MS_V2_ACE_TYPE = 3, + + ACCESS_ALLOWED_COMPOUND_ACE_TYPE = 4, + ACCESS_MAX_MS_V3_ACE_TYPE = 4, + ACCESS_MIN_MS_OBJECT_ACE_TYPE = 5, + ACCESS_ALLOWED_OBJECT_ACE_TYPE = 5, + ACCESS_DENIED_OBJECT_ACE_TYPE = 6, + SYSTEM_AUDIT_OBJECT_ACE_TYPE = 7, + SYSTEM_ALARM_OBJECT_ACE_TYPE = 8, + ACCESS_MAX_MS_OBJECT_ACE_TYPE = 8, + + ACCESS_MAX_MS_V4_ACE_TYPE = 8, + ACCESS_MAX_MS_ACE_TYPE = 8, +} __packed; + +/* + * enum - ACE inheritance and audit flags (8-bit) + * + * OBJECT_INHERIT_ACE: Object inherit (files inherit this ACE). + * CONTAINER_INHERIT_ACE: Container inherit (subdirectories inherit). + * NO_PROPAGATE_INHERIT_ACE: No propagation (stop inheritance after this level). + * INHERIT_ONLY_ACE: Inherit only (not applied to current object). + * INHERITED_ACE: ACE was inherited (Win2k+ only). + * VALID_INHERIT_FLAGS: Mask of all valid inheritance flags (0x1f). + * SUCCESSFUL_ACCESS_ACE_FLAG: Audit successful access (system audit ACE). + * FAILED_ACCESS_ACE_FLAG: Audit failed access (system audit ACE). + * + * SUCCESSFUL_ACCESS_ACE_FLAG is only used with system audit and alarm ACE + * types to indicate that a message is generated (in Windows!) for successful + * accesses. + * + * FAILED_ACCESS_ACE_FLAG is only used with system audit and alarm ACE types + * to indicate that a message is generated (in Windows!) for failed accesses. + */ +enum { + OBJECT_INHERIT_ACE = 0x01, + CONTAINER_INHERIT_ACE = 0x02, + NO_PROPAGATE_INHERIT_ACE = 0x04, + INHERIT_ONLY_ACE = 0x08, + INHERITED_ACE = 0x10, + VALID_INHERIT_FLAGS = 0x1f, + SUCCESSFUL_ACCESS_ACE_FLAG = 0x40, + FAILED_ACCESS_ACE_FLAG = 0x80, +} __packed; + +/* + * enum - NTFS access rights masks (32-bit) + * + * FILE_READ_DATA / FILE_LIST_DIRECTORY: Read file data / list dir contents. + * FILE_WRITE_DATA / FILE_ADD_FILE: Write file data / create file in dir. + * FILE_APPEND_DATA / FILE_ADD_SUBDIRECTORY: Append data / create subdir. + * FILE_READ_EA: Read extended attributes. + * FILE_WRITE_EA: Write extended attributes. + * FILE_EXECUTE / FILE_TRAVERSE: Execute file / traverse dir. + * FILE_DELETE_CHILD: Delete children in dir. + * FILE_READ_ATTRIBUTES: Read attributes. + * FILE_WRITE_ATTRIBUTES: Write attributes. + * + * Standard rights (object-independent): + * DELETE: Delete object. + * READ_CONTROL: Read security descriptor/owner. + * WRITE_DAC: Modify DACL. + * WRITE_OWNER: Change owner. + * SYNCHRONIZE: Wait on object signal state. + * + * Combinations: + * STANDARD_RIGHTS_READ / WRITE / EXECUTE: Aliases for READ_CONTROL. + * STANDARD_RIGHTS_REQUIRED: DELETE + READ_CONTROL + + * WRITE_DAC + WRITE_OWNER. + * STANDARD_RIGHTS_ALL: Above + SYNCHRONIZE. + * + * System/access types: + * ACCESS_SYSTEM_SECURITY: Access system ACL. + * MAXIMUM_ALLOWED: Maximum allowed access. + * + * Generic rights (high bits, map to specific/standard): + * GENERIC_ALL: Full access. + * GENERIC_EXECUTE: Execute/traverse. + * GENERIC_WRITE: Write (append, attrs, data, EA, etc.). + * GENERIC_READ: Read (attrs, data, EA, etc.). + * + * The specific rights (bits 0 to 15). These depend on the type of the object + * being secured by the ACE. + */ +enum { + FILE_READ_DATA = cpu_to_le32(0x00000001), + FILE_LIST_DIRECTORY = cpu_to_le32(0x00000001), + FILE_WRITE_DATA = cpu_to_le32(0x00000002), + FILE_ADD_FILE = cpu_to_le32(0x00000002), + FILE_APPEND_DATA = cpu_to_le32(0x00000004), + FILE_ADD_SUBDIRECTORY = cpu_to_le32(0x00000004), + FILE_READ_EA = cpu_to_le32(0x00000008), + FILE_WRITE_EA = cpu_to_le32(0x00000010), + FILE_EXECUTE = cpu_to_le32(0x00000020), + FILE_TRAVERSE = cpu_to_le32(0x00000020), + FILE_DELETE_CHILD = cpu_to_le32(0x00000040), + FILE_READ_ATTRIBUTES = cpu_to_le32(0x00000080), + FILE_WRITE_ATTRIBUTES = cpu_to_le32(0x00000100), + DELETE = cpu_to_le32(0x00010000), + READ_CONTROL = cpu_to_le32(0x00020000), + WRITE_DAC = cpu_to_le32(0x00040000), + WRITE_OWNER = cpu_to_le32(0x00080000), + SYNCHRONIZE = cpu_to_le32(0x00100000), + STANDARD_RIGHTS_READ = cpu_to_le32(0x00020000), + STANDARD_RIGHTS_WRITE = cpu_to_le32(0x00020000), + STANDARD_RIGHTS_EXECUTE = cpu_to_le32(0x00020000), + STANDARD_RIGHTS_REQUIRED = cpu_to_le32(0x000f0000), + STANDARD_RIGHTS_ALL = cpu_to_le32(0x001f0000), + ACCESS_SYSTEM_SECURITY = cpu_to_le32(0x01000000), + MAXIMUM_ALLOWED = cpu_to_le32(0x02000000), + GENERIC_ALL = cpu_to_le32(0x10000000), + GENERIC_EXECUTE = cpu_to_le32(0x20000000), + GENERIC_WRITE = cpu_to_le32(0x40000000), + GENERIC_READ = cpu_to_le32(0x80000000), +}; + +/* + * struct ntfs_ace - Access Control Entry (ACE) structure + * + * @type: ACE type (ACCESS_ALLOWED_ACE_TYPE, ACCESS_DENIED_ACE_TYPE, etc.). + * @flags: Inheritance and audit flags (OBJECT_INHERIT_ACE, etc.). + * @size: Total byte size of this ACE (header + SID + variable data). + * @mask: Access rights mask (FILE_READ_DATA, DELETE, GENERIC_ALL, etc.). + * @sid: Security Identifier (SID) this ACE applies to. + */ +struct ntfs_ace { + u8 type; + u8 flags; + __le16 size; + __le32 mask; + struct ntfs_sid sid; +} __packed; + +/* + * The object ACE flags (32-bit). + */ +enum { + ACE_OBJECT_TYPE_PRESENT = cpu_to_le32(1), + ACE_INHERITED_OBJECT_TYPE_PRESENT = cpu_to_le32(2), +}; + +/* + * struct ntfs_acl - NTFS Access Control List (ACL) header + * + * An ACL is an access-control list (ACL). + * An ACL starts with an ACL header structure, which specifies the size of + * the ACL and the number of ACEs it contains. The ACL header is followed by + * zero or more access control entries (ACEs). The ACL as well as each ACE + * are aligned on 4-byte boundaries. + * + * @revision: ACL revision level (usually 2 or 4). + * @alignment1: Padding/alignment byte (zero). + * @size: Total allocated size in bytes (header + all ACEs + + * free space). + * @ace_count: Number of ACE entries following the header. + * @alignment2: Padding/alignment (zero). + */ +struct ntfs_acl { + u8 revision; + u8 alignment1; + __le16 size; + __le16 ace_count; + __le16 alignment2; +} __packed; + +static_assert(sizeof(struct ntfs_acl) == 8); + +/* + * The security descriptor control flags (16-bit). + * + * SE_OWNER_DEFAULTED - This boolean flag, when set, indicates that the SID + * pointed to by the Owner field was provided by a defaulting mechanism + * rather than explicitly provided by the original provider of the + * security descriptor. This may affect the treatment of the SID with + * respect to inheritance of an owner. + * + * SE_GROUP_DEFAULTED - This boolean flag, when set, indicates that the SID in + * the Group field was provided by a defaulting mechanism rather than + * explicitly provided by the original provider of the security + * descriptor. This may affect the treatment of the SID with respect to + * inheritance of a primary group. + * + * SE_DACL_PRESENT - This boolean flag, when set, indicates that the security + * descriptor contains a discretionary ACL. If this flag is set and the + * Dacl field of the SECURITY_DESCRIPTOR is null, then a null ACL is + * explicitly being specified. + * + * SE_DACL_DEFAULTED - This boolean flag, when set, indicates that the ACL + * pointed to by the Dacl field was provided by a defaulting mechanism + * rather than explicitly provided by the original provider of the + * security descriptor. This may affect the treatment of the ACL with + * respect to inheritance of an ACL. This flag is ignored if the + * DaclPresent flag is not set. + * + * SE_SACL_PRESENT - This boolean flag, when set, indicates that the security + * descriptor contains a system ACL pointed to by the Sacl field. If this + * flag is set and the Sacl field of the SECURITY_DESCRIPTOR is null, then + * an empty (but present) ACL is being specified. + * + * SE_SACL_DEFAULTED - This boolean flag, when set, indicates that the ACL + * pointed to by the Sacl field was provided by a defaulting mechanism + * rather than explicitly provided by the original provider of the + * security descriptor. This may affect the treatment of the ACL with + * respect to inheritance of an ACL. This flag is ignored if the + * SaclPresent flag is not set. + * + * SE_SELF_RELATIVE - This boolean flag, when set, indicates that the security + * descriptor is in self-relative form. In this form, all fields of the + * security descriptor are contiguous in memory and all pointer fields are + * expressed as offsets from the beginning of the security descriptor. + */ +enum { + SE_OWNER_DEFAULTED = cpu_to_le16(0x0001), + SE_GROUP_DEFAULTED = cpu_to_le16(0x0002), + SE_DACL_PRESENT = cpu_to_le16(0x0004), + SE_DACL_DEFAULTED = cpu_to_le16(0x0008), + + SE_SACL_PRESENT = cpu_to_le16(0x0010), + SE_SACL_DEFAULTED = cpu_to_le16(0x0020), + + SE_DACL_AUTO_INHERIT_REQ = cpu_to_le16(0x0100), + SE_SACL_AUTO_INHERIT_REQ = cpu_to_le16(0x0200), + SE_DACL_AUTO_INHERITED = cpu_to_le16(0x0400), + SE_SACL_AUTO_INHERITED = cpu_to_le16(0x0800), + + SE_DACL_PROTECTED = cpu_to_le16(0x1000), + SE_SACL_PROTECTED = cpu_to_le16(0x2000), + SE_RM_CONTROL_VALID = cpu_to_le16(0x4000), + SE_SELF_RELATIVE = cpu_to_le16(0x8000) +} __packed; + +/* + * struct security_descriptor_relative - Relative security descriptor + * + * Self-relative security descriptor. Contains the owner and group SIDs as well + * as the sacl and dacl ACLs inside the security descriptor itself. + * + * @revision: Security descriptor revision (usually 1). + * @alignment: Padding/alignment byte (zero). + * @control: Control flags (SE_OWNER_DEFAULTED, SE_DACL_PRESENT, + * SE_SACL_PRESENT, SE_SACL_AUTO_INHERITED, etc.). + * @owner: Byte offset to owner SID (from start of descriptor). + * 0 if no owner SID present. + * @group: Byte offset to primary group SID. + * 0 if no group SID present. + * @sacl: Byte offset to System ACL (SACL). + * Valid only if SE_SACL_PRESENT in @control. + * 0 means NULL SACL. + * @dacl: Byte offset to Discretionary ACL (DACL). + * Valid only if SE_DACL_PRESENT in @control. + * 0 means NULL DACL (full access granted). + */ +struct security_descriptor_relative { + u8 revision; + u8 alignment; + __le16 control; + __le32 owner; + __le32 group; + __le32 sacl; + __le32 dacl; +} __packed; + +static_assert(sizeof(struct security_descriptor_relative) == 20); + +/* + * On NTFS 3.0+, all security descriptors are stored in FILE_Secure. Only one + * referenced instance of each unique security descriptor is stored. + * + * FILE_Secure contains no unnamed data attribute, i.e. it has zero length. It + * does, however, contain two indexes ($SDH and $SII) as well as a named data + * stream ($SDS). + * + * Every unique security descriptor is assigned a unique security identifier + * (security_id, not to be confused with a SID). The security_id is unique for + * the NTFS volume and is used as an index into the $SII index, which maps + * security_ids to the security descriptor's storage location within the $SDS + * data attribute. The $SII index is sorted by ascending security_id. + * + * A simple hash is computed from each security descriptor. This hash is used + * as an index into the $SDH index, which maps security descriptor hashes to + * the security descriptor's storage location within the $SDS data attribute. + * The $SDH index is sorted by security descriptor hash and is stored in a B+ + * tree. When searching $SDH (with the intent of determining whether or not a + * new security descriptor is already present in the $SDS data stream), if a + * matching hash is found, but the security descriptors do not match, the + * search in the $SDH index is continued, searching for a next matching hash. + * + * When a precise match is found, the security_id coresponding to the security + * descriptor in the $SDS attribute is read from the found $SDH index entry and + * is stored in the $STANDARD_INFORMATION attribute of the file/directory to + * which the security descriptor is being applied. The $STANDARD_INFORMATION + * attribute is present in all base mft records (i.e. in all files and + * directories). + * + * If a match is not found, the security descriptor is assigned a new unique + * security_id and is added to the $SDS data attribute. Then, entries + * referencing the this security descriptor in the $SDS data attribute are + * added to the $SDH and $SII indexes. + * + * Note: Entries are never deleted from FILE_Secure, even if nothing + * references an entry any more. + */ + +/* + * struct sii_index_key - Key for $SII index in $Secure file + * + * The index entry key used in the $SII index. The collation type is + * COLLATION_NTOFS_ULONG. + * + * @security_id: 32-bit security identifier. + * Unique ID assigned to a security descriptor. + */ +struct sii_index_key { + __le32 security_id; +} __packed; + +/* + * struct sdh_index_key - Key for $SDH index in $Secure file + * + * The index entry key used in the $SDH index. The keys are sorted first by + * hash and then by security_id. The collation rule is + * COLLATION_NTOFS_SECURITY_HASH. + * + * @hash: 32-bit hash of the security descriptor. + * Used for quick collision checks and indexing. + * @security_id: 32-bit security identifier. + * Unique ID assigned to the descriptor. + */ +struct sdh_index_key { + __le32 hash; + __le32 security_id; +} __packed; + +/* + * enum - NTFS volume flags (16-bit) + * + * These flags are stored in $VolumeInformation attribute. + * They indicate volume state and required actions. + * + * VOLUME_IS_DIRTY: Volume is dirty (needs chkdsk). + * VOLUME_RESIZE_LOG_FILE: Resize LogFile on next mount. + * VOLUME_UPGRADE_ON_MOUNT: Upgrade volume on mount (old NTFS). + * VOLUME_MOUNTED_ON_NT4: Mounted on NT4 (compatibility flag). + * VOLUME_DELETE_USN_UNDERWAY: USN journal deletion in progress. + * VOLUME_REPAIR_OBJECT_ID: Repair $ObjId on next mount. + * VOLUME_CHKDSK_UNDERWAY: Chkdsk is running. + * VOLUME_MODIFIED_BY_CHKDSK: Modified by chkdsk. + * VOLUME_FLAGS_MASK: Mask of all valid flags (0xc03f). + * VOLUME_MUST_MOUNT_RO_MASK: Flags forcing read-only mount (0xc027). + * If any set, mount read-only. + */ +enum { + VOLUME_IS_DIRTY = cpu_to_le16(0x0001), + VOLUME_RESIZE_LOG_FILE = cpu_to_le16(0x0002), + VOLUME_UPGRADE_ON_MOUNT = cpu_to_le16(0x0004), + VOLUME_MOUNTED_ON_NT4 = cpu_to_le16(0x0008), + + VOLUME_DELETE_USN_UNDERWAY = cpu_to_le16(0x0010), + VOLUME_REPAIR_OBJECT_ID = cpu_to_le16(0x0020), + + VOLUME_CHKDSK_UNDERWAY = cpu_to_le16(0x4000), + VOLUME_MODIFIED_BY_CHKDSK = cpu_to_le16(0x8000), + + VOLUME_FLAGS_MASK = cpu_to_le16(0xc03f), + + VOLUME_MUST_MOUNT_RO_MASK = cpu_to_le16(0xc027), +} __packed; + +/* + * struct volume_information - $VOLUME_INFORMATION (0x70) + * + * @reserved: Reserved 64-bit field (currently unused). + * @major_ver: Major NTFS version number (e.g., 3 for NTFS 3.1). + * @minor_ver: Minor NTFS version number (e.g., 1 for NTFS 3.1). + * @flags: Volume flags (VOLUME_IS_DIRTY, VOLUME_CHKDSK_UNDERWAY, etc.). + * See volume flags enum for details. + * + * NOTE: Always resident. + * NOTE: Present only in FILE_Volume. + * NOTE: Windows 2000 uses NTFS 3.0 while Windows NT4 service pack 6a uses + * NTFS 1.2. I haven't personally seen other values yet. + */ +struct volume_information { + __le64 reserved; + u8 major_ver; + u8 minor_ver; + __le16 flags; +} __packed; + +/* + * enum - Index header flags + * + * These flags are stored in the index header (INDEX_HEADER.flags) for both + * index root ($INDEX_ROOT) and index allocation blocks ($INDEX_ALLOCATION). + * + * For index root ($INDEX_ROOT attribute): + * SMALL_INDEX: Index fits entirely in root attribute (no $INDEX_ALLOCATION). + * LARGE_INDEX: Index too large for root; $INDEX_ALLOCATION present. + * + * For index blocks ($INDEX_ALLOCATION): + * LEAF_NODE: Leaf node (no child nodes; contains actual entries). + * INDEX_NODE: Internal node (indexes other nodes; contains keys/pointers). + * + * NODE_MASK: Mask to extract node type bits (0x01). + */ +enum { + SMALL_INDEX = 0, + LARGE_INDEX = 1, + LEAF_NODE = 0, + INDEX_NODE = 1, + NODE_MASK = 1, +} __packed; + +/* + * struct index_header - Common header for index root and index blocks + * + * entries_offset: Byte offset to first INDEX_ENTRY (8-byte aligned). + * index_length: Bytes used by index entries (8-byte aligned). + * From entries_offset to end of used data. + * allocated_size: Total allocated bytes for this index block. + * Fixed size in index allocation; dynamic in root. + * flags: Index flags (SMALL_INDEX, LARGE_INDEX, LEAF_NODE, etc.). + * See INDEX_HEADER_FLAGS enum. + * reserved: 3 bytes reserved/padding (zero, 8-byte aligned). + * + * This is the header for indexes, describing the INDEX_ENTRY records, which + * follow the index_header. Together the index header and the index entries + * make up a complete index. + * + * IMPORTANT NOTE: The offset, length and size structure members are counted + * relative to the start of the index header structure and not relative to the + * start of the index root or index allocation structures themselves. + * + * For the index root attribute, the above two numbers are always + * equal, as the attribute is resident and it is resized as needed. In + * the case of the index allocation attribute the attribute is not + * resident and hence the allocated_size is a fixed value and must + * equal the index_block_size specified by the INDEX_ROOT attribute + * corresponding to the INDEX_ALLOCATION attribute this INDEX_BLOCK + * belongs to. + */ +struct index_header { + __le32 entries_offset; + __le32 index_length; + __le32 allocated_size; + u8 flags; + u8 reserved[3]; +} __packed; + +/* + * struct index_root - $INDEX_ROOT attribute (0x90). + * + * @type: Indexed attribute type ($FILE_NAME for dirs, + * 0 for view indexes). + * @collation_rule: Collation rule for sorting entries + * (COLLATION_FILE_NAME for $FILE_NAME). + * @index_block_size: Size of each index block in bytes + * (in $INDEX_ALLOCATION). + * @clusters_per_index_block: + * Clusters per index block (or log2(bytes) + * if < cluster). + * Power of 2; used for encoding block size. + * @reserved: 3 bytes reserved/alignment (zero). + * @index: Index header for root entries (entries follow + * immediately). + * + * NOTE: Always resident. + * + * This is followed by a sequence of index entries (INDEX_ENTRY structures) + * as described by the index header. + * + * When a directory is small enough to fit inside the index root then this + * is the only attribute describing the directory. When the directory is too + * large to fit in the index root, on the other hand, two additional attributes + * are present: an index allocation attribute, containing sub-nodes of the B+ + * directory tree (see below), and a bitmap attribute, describing which virtual + * cluster numbers (vcns) in the index allocation attribute are in use by an + * index block. + * + * NOTE: The root directory (FILE_root) contains an entry for itself. Other + * directories do not contain entries for themselves, though. + */ +struct index_root { + __le32 type; + __le32 collation_rule; + __le32 index_block_size; + u8 clusters_per_index_block; + u8 reserved[3]; + struct index_header index; +} __packed; + +/* + * struct index_block - Index allocation (0xa0). + * + * @magic: Magic value "INDX" (see magic_INDX). + * @usa_ofs: Offset to Update Sequence Array (see ntfs_record). + * @usa_count: Number of USA entries (see ntfs_record). + * @lsn: Log sequence number of last modification. + * @index_block_vcn: VCN of this index block. + * Units: clusters if cluster_size <= index_block_size; + * sectors otherwise. + * @index: Index header describing entries in this block. + * + * When creating the index block, we place the update sequence array at this + * offset, i.e. before we start with the index entries. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading use the data from the ntfs record header. + * + * NOTE: Always non-resident (doesn't make sense to be resident anyway!). + * + * This is an array of index blocks. Each index block starts with an + * index_block structure containing an index header, followed by a sequence of + * index entries (INDEX_ENTRY structures), as described by the struct index_header. + */ +struct index_block { + __le32 magic; + __le16 usa_ofs; + __le16 usa_count; + __le64 lsn; + __le64 index_block_vcn; + struct index_header index; +} __packed; + +static_assert(sizeof(struct index_block) == 40); + +/* + * struct reparse_index_key - Key for $R reparse index in $Extend/$Reparse + * + * @reparse_tag: Reparse point type (including flags, REPARSE_TAG_*). + * @file_id: MFT record number of the file with $REPARSE_POINT + * attribute. + * + * The system file FILE_Extend/$Reparse contains an index named $R listing + * all reparse points on the volume. The index entry keys are as defined + * below. Note, that there is no index data associated with the index entries. + * + * The index entries are sorted by the index key file_id. The collation rule is + * COLLATION_NTOFS_ULONGS. + */ +struct reparse_index_key { + __le32 reparse_tag; + __le64 file_id; +} __packed; + +/* + * enum - Quota entry flags (32-bit) in $Quota/$Q + * + * These flags are stored in quota control entries ($Quota file). + * They control quota tracking, limits, and state. + * + * User quota flags (mask 0x00000007): + * @QUOTA_FLAG_DEFAULT_LIMITS: Use default limits. + * @QUOTA_FLAG_LIMIT_REACHED: Quota limit reached. + * @QUOTA_FLAG_ID_DELETED: Quota ID deleted. + * @QUOTA_FLAG_USER_MASK: Mask for user quota flags (0x00000007). + * + * Default entry flags (owner_id = QUOTA_DEFAULTS_ID): + * @QUOTA_FLAG_TRACKING_ENABLED: Quota tracking enabled. + * @QUOTA_FLAG_ENFORCEMENT_ENABLED: Quota enforcement enabled. + * @QUOTA_FLAG_TRACKING_REQUESTED: Tracking requested (pending). + * @QUOTA_FLAG_LOG_THRESHOLD: Log when threshold reached. + * @QUOTA_FLAG_LOG_LIMIT: Log when limit reached. + * @QUOTA_FLAG_OUT_OF_DATE: Quota data out of date. + * @QUOTA_FLAG_CORRUPT: Quota entry corrupt. + * @QUOTA_FLAG_PENDING_DELETES: Pending quota deletes. + * + */ +enum { + QUOTA_FLAG_DEFAULT_LIMITS = cpu_to_le32(0x00000001), + QUOTA_FLAG_LIMIT_REACHED = cpu_to_le32(0x00000002), + QUOTA_FLAG_ID_DELETED = cpu_to_le32(0x00000004), + + QUOTA_FLAG_USER_MASK = cpu_to_le32(0x00000007), + QUOTA_FLAG_TRACKING_ENABLED = cpu_to_le32(0x00000010), + QUOTA_FLAG_ENFORCEMENT_ENABLED = cpu_to_le32(0x00000020), + QUOTA_FLAG_TRACKING_REQUESTED = cpu_to_le32(0x00000040), + QUOTA_FLAG_LOG_THRESHOLD = cpu_to_le32(0x00000080), + + QUOTA_FLAG_LOG_LIMIT = cpu_to_le32(0x00000100), + QUOTA_FLAG_OUT_OF_DATE = cpu_to_le32(0x00000200), + QUOTA_FLAG_CORRUPT = cpu_to_le32(0x00000400), + QUOTA_FLAG_PENDING_DELETES = cpu_to_le32(0x00000800), +}; + +/* + * struct quota_control_entry - Quota entry in $Quota/$Q + * + * @version: Currently 2. + * @flags: Quota flags (QUOTA_FLAG_* bits). + * @bytes_used: Current quota usage in bytes. + * @change_time: Last modification time (NTFS timestamp). + * @threshold: Soft quota limit (-1 = unlimited). + * @limit: Hard quota limit (-1 = unlimited). + * @exceeded_time: Time soft quota has been exceeded. + * @sid: SID of user/object (zero for defaults entry). + * + * The system file FILE_Extend/$Quota contains two indexes $O and $Q. Quotas + * are on a per volume and per user basis. + * + * The $Q index contains one entry for each existing user_id on the volume. The + * index key is the user_id of the user/group owning this quota control entry, + * i.e. the key is the owner_id. The user_id of the owner of a file, i.e. the + * owner_id, is found in the standard information attribute. The collation rule + * for $Q is COLLATION_NTOFS_ULONG. + * + * The $O index contains one entry for each user/group who has been assigned + * a quota on that volume. The index key holds the SID of the user_id the + * entry belongs to, i.e. the owner_id. The collation rule for $O is + * COLLATION_NTOFS_SID. + * + * The $O index entry data is the user_id of the user corresponding to the SID. + * This user_id is used as an index into $Q to find the quota control entry + * associated with the SID. + * + * The $Q index entry data is the quota control entry and is defined below. + */ +struct quota_control_entry { + __le32 version; + __le32 flags; + __le64 bytes_used; + __le64 change_time; + __le64 threshold; + __le64 limit; + __le64 exceeded_time; + struct ntfs_sid sid; +} __packed; + +/* + * Predefined owner_id values (32-bit). + */ +enum { + QUOTA_INVALID_ID = cpu_to_le32(0x00000000), + QUOTA_DEFAULTS_ID = cpu_to_le32(0x00000001), + QUOTA_FIRST_USER_ID = cpu_to_le32(0x00000100), +}; + +/* + * Current constants for quota control entries. + */ +enum { + /* Current version. */ + QUOTA_VERSION = 2, +}; + +/* + * enum - Index entry flags (16-bit) + * + * These flags are in INDEX_ENTRY.flags (after key data). + * They describe entry type and status in index blocks/root. + * + * @INDEX_ENTRY_NODE: Entry points to a sub-node (index block VCN). + * (Not a leaf entry; internal node reference.) + * i.e. a reference to an index block in form of + * a virtual cluster number + * @INDEX_ENTRY_END: Last entry in index block/root. + * Does not represent a real file; can point to sub-node. + * @INDEX_ENTRY_SPACE_FILLER: + * Dummy value to force enum to 16-bit width. + */ +enum { + INDEX_ENTRY_NODE = cpu_to_le16(1), + INDEX_ENTRY_END = cpu_to_le16(2), + INDEX_ENTRY_SPACE_FILLER = cpu_to_le16(0xffff), +} __packed; + +/* + * struct index_entry_header - Common header for all NTFS index entries + * + * This is the fixed header at the start of every INDEX_ENTRY in index + * blocks or index root. It is followed by the variable key, data, and + * sub-node VCN. + * + * Union @data: + * - When INDEX_ENTRY_END is not set: + * @data.dir.indexed_file: MFT reference of the file described by + * this entry. Used in directory indexes ($I30). + * - When INDEX_ENTRY_END is set or for view indexes: + * @data.vi.data_offset: Byte offset from end of this header to + * entry data. + * @data.vi.data_length: Length of data in bytes. + * @data.vi.reservedV: Reserved (zero). + * + * @length: Total byte size of this index entry + * (multiple of 8 bytes). + * @key_length: Byte size of the key (not multiple of 8 bytes). + * Key follows the header immediately. + * @flags: Bit field of INDEX_ENTRY_* flags (INDEX_ENTRY_NODE, etc.). + * @reserved: Reserved/padding (zero; align to 8 bytes). + */ +struct index_entry_header { + union { + struct { + __le64 indexed_file; + } __packed dir; + struct { + __le16 data_offset; + __le16 data_length; + __le32 reservedV; + } __packed vi; + } __packed data; + __le16 length; + __le16 key_length; + __le16 flags; + __le16 reserved; +} __packed; + +static_assert(sizeof(struct index_entry_header) == 16); + +/* + * struct index_entry - NTFS index entry structure + * + * This is an index entry. A sequence of such entries follows each index_header + * structure. Together they make up a complete index. The index follows either + * an index root attribute or an index allocation attribute. + * + * Union @data (valid when INDEX_ENTRY_END not set): + * @data.dir.indexed_file: MFT ref of file (for directory indexes). + * @data.vi.data_offset: Offset to data after key. + * @data.vi.data_length: Length of data in bytes. + * @data.vi.reservedV: Reserved (zero). + * + * Fields: + * @length: Total byte size of entry (multiple of 8 bytes). + * @key_length: Byte size of key (not multiple of 8). + * @flags: INDEX_ENTRY_* flags (NODE, END, etc.). + * @reserved: Reserved/padding (zero). + * + * Union @key (valid when INDEX_ENTRY_END not set) + * The key of the indexed attribute. NOTE: Only present + * if INDEX_ENTRY_END bit in flags is not set. NOTE: On + * NTFS versions before 3.0 the only valid key is the + * struct file_name_attr. On NTFS 3.0+ the following + * additional index keys are defined: + * @key.file_name: $FILE_NAME attr (for $I30 directory indexes). + * @key.sii: $SII key (for $Secure $SII index). + * @key.sdh: $SDH key (for $Secure $SDH index). + * @key.object_id: GUID (for $ObjId $O index). + * @key.reparse: Reparse tag + file ID (for $Reparse $R). + * @key.sid: SID (for $Quota $O index). + * @key.owner_id: User ID (for $Quota $Q index). + * + * The (optional) index data is inserted here when creating. + * __le64 vcn; If INDEX_ENTRY_NODE bit in flags is set, the last + * eight bytes of this index entry contain the virtual + * cluster number of the index block that holds the + * entries immediately preceding the current entry (the + * vcn references the corresponding cluster in the data + * of the non-resident index allocation attribute). If + * the key_length is zero, then the vcn immediately + * follows the INDEX_ENTRY_HEADER. Regardless of + * key_length, the address of the 8-byte boundary + * aligned vcn of INDEX_ENTRY{_HEADER} *ie is given by + * (char*)ie + le16_to_cpu(ie*)->length) - sizeof(VCN), + * where sizeof(VCN) can be hardcoded as 8 if wanted. + * + * NOTE: Before NTFS 3.0 only filename attributes were indexed. + */ +struct index_entry { + union { + struct { + __le64 indexed_file; + } __packed dir; + struct { + __le16 data_offset; + __le16 data_length; + __le32 reservedV; + } __packed vi; + } __packed data; + __le16 length; + __le16 key_length; + __le16 flags; + __le16 reserved; + union { + struct file_name_attr file_name; + struct sii_index_key sii; + struct sdh_index_key sdh; + struct guid object_id; + struct reparse_index_key reparse; + struct ntfs_sid sid; + __le32 owner_id; + } __packed key; +} __packed; + +/* + * The reparse point tag defines the type of the reparse point. It also + * includes several flags, which further describe the reparse point. + * + * The reparse point tag is an unsigned 32-bit value divided in three parts: + * + * 1. The least significant 16 bits (i.e. bits 0 to 15) specify the type of + * the reparse point. + * 2. The 12 bits after this (i.e. bits 16 to 27) are reserved for future use. + * 3. The most significant four bits are flags describing the reparse point. + * They are defined as follows: + * bit 28: Directory bit. If set, the directory is not a surrogate + * and can be used the usual way. + * bit 29: Name surrogate bit. If set, the filename is an alias for + * another object in the system. + * bit 30: High-latency bit. If set, accessing the first byte of data will + * be slow. (E.g. the data is stored on a tape drive.) + * bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User + * defined tags have to use zero here. + * 4. Moreover, on Windows 10 : + * Some flags may be used in bits 12 to 15 to further describe the + * reparse point. + */ +enum { + IO_REPARSE_TAG_DIRECTORY = cpu_to_le32(0x10000000), + IO_REPARSE_TAG_IS_ALIAS = cpu_to_le32(0x20000000), + IO_REPARSE_TAG_IS_HIGH_LATENCY = cpu_to_le32(0x40000000), + IO_REPARSE_TAG_IS_MICROSOFT = cpu_to_le32(0x80000000), + + IO_REPARSE_TAG_RESERVED_ZERO = cpu_to_le32(0x00000000), + IO_REPARSE_TAG_RESERVED_ONE = cpu_to_le32(0x00000001), + IO_REPARSE_TAG_RESERVED_RANGE = cpu_to_le32(0x00000001), + + IO_REPARSE_TAG_CSV = cpu_to_le32(0x80000009), + IO_REPARSE_TAG_DEDUP = cpu_to_le32(0x80000013), + IO_REPARSE_TAG_DFS = cpu_to_le32(0x8000000A), + IO_REPARSE_TAG_DFSR = cpu_to_le32(0x80000012), + IO_REPARSE_TAG_HSM = cpu_to_le32(0xC0000004), + IO_REPARSE_TAG_HSM2 = cpu_to_le32(0x80000006), + IO_REPARSE_TAG_MOUNT_POINT = cpu_to_le32(0xA0000003), + IO_REPARSE_TAG_NFS = cpu_to_le32(0x80000014), + IO_REPARSE_TAG_SIS = cpu_to_le32(0x80000007), + IO_REPARSE_TAG_SYMLINK = cpu_to_le32(0xA000000C), + IO_REPARSE_TAG_WIM = cpu_to_le32(0x80000008), + IO_REPARSE_TAG_DFM = cpu_to_le32(0x80000016), + IO_REPARSE_TAG_WOF = cpu_to_le32(0x80000017), + IO_REPARSE_TAG_WCI = cpu_to_le32(0x80000018), + IO_REPARSE_TAG_CLOUD = cpu_to_le32(0x9000001A), + IO_REPARSE_TAG_APPEXECLINK = cpu_to_le32(0x8000001B), + IO_REPARSE_TAG_GVFS = cpu_to_le32(0x9000001C), + IO_REPARSE_TAG_LX_SYMLINK = cpu_to_le32(0xA000001D), + IO_REPARSE_TAG_AF_UNIX = cpu_to_le32(0x80000023), + IO_REPARSE_TAG_LX_FIFO = cpu_to_le32(0x80000024), + IO_REPARSE_TAG_LX_CHR = cpu_to_le32(0x80000025), + IO_REPARSE_TAG_LX_BLK = cpu_to_le32(0x80000026), + + IO_REPARSE_TAG_VALID_VALUES = cpu_to_le32(0xf000ffff), + IO_REPARSE_PLUGIN_SELECT = cpu_to_le32(0xffff0fff), +}; + +/* + * struct reparse_point - $REPARSE_POINT attribute content (0xc0)\ + * + * @reparse_tag: Reparse point type (with flags; REPARSE_TAG_*). + * @reparse_data_length: Byte size of @reparse_data. + * @reserved: Reserved/padding (zero; 8-byte alignment). + * @reparse_data: Variable reparse data (meaning depends on @reparse_tag). + * - Symbolic link/junction: struct reparse_symlink + * - Mount point: similar symlink structure + * - Other tags: vendor-specific or extended data + * + * NOTE: Can be resident or non-resident. + */ +struct reparse_point { + __le32 reparse_tag; + __le16 reparse_data_length; + __le16 reserved; + u8 reparse_data[]; +} __packed; + +/* + * struct ea_information - $EA_INFORMATION attribute content (0xd0) + * + * @ea_length: Byte size of packed EAs. + * @need_ea_count: Number of EAs with NEED_EA bit set. + * @ea_query_length: Byte size needed to unpack/query EAs via ZwQueryEaFile(). + * (Unpacked format size.) + * + * NOTE: Always resident. (Is this true???) + */ +struct ea_information { + __le16 ea_length; + __le16 need_ea_count; + __le32 ea_query_length; +} __packed; + +/* + * enum - Extended attribute flags (8-bit) + * + * These flags are stored in the EA header of each extended attribute + * (in $EA attribute, type 0xe0). + * + * @NEED_EA: If set, the file cannot be properly interpreted + * without understanding its associated EAs. + * (Critical EA; applications must process it.) + */ +enum { + NEED_EA = 0x80 +} __packed; + +/* + * struct ea_attr - Extended attribute (EA) entry (0xe0) + * + * @next_entry_offset: Byte offset to the next EA_ATTR entry. + * (From start of current entry.) + * @flags: EA flags (NEED_EA = 0x80 if critical). + * @ea_name_length: Length of @ea_name in bytes (excluding '\0'). + * @ea_value_length: Byte size of the EA value. + * @ea_name: ASCII name of the EA (zero-terminated). + * Value immediately follows the name. + * u8 ea_value[]; The value of the EA. Immediately follows the name. + * + * This is one variable-length record in the $EA attribute value. + * The attribute can be resident or non-resident. + * Sequence of these entries forms the packed EA list. + * + * NOTE: Can be resident or non-resident. + */ +struct ea_attr { + __le32 next_entry_offset; + u8 flags; + u8 ea_name_length; + __le16 ea_value_length; + u8 ea_name[]; +} __packed; + +#endif /* _LINUX_NTFS_LAYOUT_H */ diff --git a/fs/ntfs/lcnalloc.c b/fs/ntfs/lcnalloc.c new file mode 100644 index 000000000000..835a041023a2 --- /dev/null +++ b/fs/ntfs/lcnalloc.c @@ -0,0 +1,1049 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Cluster (de)allocation code. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2025 LG Electronics Co., Ltd. + * + * Part of this file is based on code from the NTFS-3G. + * and is copyrighted by the respective authors below: + * Copyright (c) 2002-2004 Anton Altaparmakov + * Copyright (c) 2004 Yura Pakhuchiy + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * Copyright (c) 2008-2009 Jean-Pierre Andre + */ + +#include + +#include "lcnalloc.h" +#include "bitmap.h" +#include "ntfs.h" + +/* + * ntfs_cluster_free_from_rl_nolock - free clusters from runlist + * @vol: mounted ntfs volume on which to free the clusters + * @rl: runlist describing the clusters to free + * + * Free all the clusters described by the runlist @rl on the volume @vol. In + * the case of an error being returned, at least some of the clusters were not + * freed. + * + * Return 0 on success and -errno on error. + * + * Locking: - The volume lcn bitmap must be locked for writing on entry and is + * left locked on return. + */ +int ntfs_cluster_free_from_rl_nolock(struct ntfs_volume *vol, + const struct runlist_element *rl) +{ + struct inode *lcnbmp_vi = vol->lcnbmp_ino; + int ret = 0; + s64 nr_freed = 0; + + ntfs_debug("Entering."); + if (!rl) + return 0; + + if (!NVolFreeClusterKnown(vol)) + wait_event(vol->free_waitq, NVolFreeClusterKnown(vol)); + + for (; rl->length; rl++) { + int err; + + if (rl->lcn < 0) + continue; + err = ntfs_bitmap_clear_run(lcnbmp_vi, rl->lcn, rl->length); + if (unlikely(err && (!ret || ret == -ENOMEM) && ret != err)) + ret = err; + else + nr_freed += rl->length; + } + ntfs_inc_free_clusters(vol, nr_freed); + ntfs_debug("Done."); + return ret; +} + +static s64 max_empty_bit_range(unsigned char *buf, int size) +{ + int i, j, run = 0; + int max_range = 0; + s64 start_pos = -1; + + ntfs_debug("Entering\n"); + + i = 0; + while (i < size) { + switch (*buf) { + case 0: + do { + buf++; + run += 8; + i++; + } while ((i < size) && !*buf); + break; + case 255: + if (run > max_range) { + max_range = run; + start_pos = (s64)i * 8 - run; + } + run = 0; + do { + buf++; + i++; + } while ((i < size) && (*buf == 255)); + break; + default: + for (j = 0; j < 8; j++) { + int bit = *buf & (1 << j); + + if (bit) { + if (run > max_range) { + max_range = run; + start_pos = (s64)i * 8 + (j - run); + } + run = 0; + } else + run++; + } + i++; + buf++; + } + } + + if (run > max_range) + start_pos = (s64)i * 8 - run; + + return start_pos; +} + +/* + * ntfs_cluster_alloc - allocate clusters on an ntfs volume + * @vol: mounted ntfs volume on which to allocate clusters + * @start_vcn: vcn of the first allocated cluster + * @count: number of clusters to allocate + * @start_lcn: starting lcn at which to allocate the clusters or -1 if none + * @zone: zone from which to allocate (MFT_ZONE or DATA_ZONE) + * @is_extension: if true, the caller is extending an attribute + * @is_contig: if true, require contiguous allocation + * @is_dealloc: if true, the allocation is for deallocation purposes + * + * Allocate @count clusters preferably starting at cluster @start_lcn or at the + * current allocator position if @start_lcn is -1, on the mounted ntfs volume + * @vol. @zone is either DATA_ZONE for allocation of normal clusters or + * MFT_ZONE for allocation of clusters for the master file table, i.e. the + * $MFT/$DATA attribute. + * + * @start_vcn specifies the vcn of the first allocated cluster. This makes + * merging the resulting runlist with the old runlist easier. + * + * If @is_extension is 'true', the caller is allocating clusters to extend an + * attribute and if it is 'false', the caller is allocating clusters to fill a + * hole in an attribute. Practically the difference is that if @is_extension + * is 'true' the returned runlist will be terminated with LCN_ENOENT and if + * @is_extension is 'false' the runlist will be terminated with + * LCN_RL_NOT_MAPPED. + * + * You need to check the return value with IS_ERR(). If this is false, the + * function was successful and the return value is a runlist describing the + * allocated cluster(s). If IS_ERR() is true, the function failed and + * PTR_ERR() gives you the error code. + * + * Notes on the allocation algorithm + * ================================= + * + * There are two data zones. First is the area between the end of the mft zone + * and the end of the volume, and second is the area between the start of the + * volume and the start of the mft zone. On unmodified/standard NTFS 1.x + * volumes, the second data zone does not exist due to the mft zone being + * expanded to cover the start of the volume in order to reserve space for the + * mft bitmap attribute. + * + * This is not the prettiest function but the complexity stems from the need of + * implementing the mft vs data zoned approach and from the fact that we have + * access to the lcn bitmap in portions of up to 8192 bytes at a time, so we + * need to cope with crossing over boundaries of two buffers. Further, the + * fact that the allocator allows for caller supplied hints as to the location + * of where allocation should begin and the fact that the allocator keeps track + * of where in the data zones the next natural allocation should occur, + * contribute to the complexity of the function. But it should all be + * worthwhile, because this allocator should: 1) be a full implementation of + * the MFT zone approach used by Windows NT, 2) cause reduction in + * fragmentation, and 3) be speedy in allocations (the code is not optimized + * for speed, but the algorithm is, so further speed improvements are probably + * possible). + * + * Locking: - The volume lcn bitmap must be unlocked on entry and is unlocked + * on return. + * - This function takes the volume lcn bitmap lock for writing and + * modifies the bitmap contents. + * + * Return: Runlist describing the allocated cluster(s) on success, error pointer + * on failure. + */ +struct runlist_element *ntfs_cluster_alloc(struct ntfs_volume *vol, const s64 start_vcn, + const s64 count, const s64 start_lcn, + const int zone, + const bool is_extension, + const bool is_contig, + const bool is_dealloc) +{ + s64 zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn; + s64 prev_lcn = 0, prev_run_len = 0, mft_zone_size; + s64 clusters, free_clusters; + loff_t i_size; + struct inode *lcnbmp_vi; + struct runlist_element *rl = NULL; + struct address_space *mapping; + struct folio *folio = NULL; + u8 *buf = NULL, *byte; + int err = 0, rlpos, rlsize, buf_size, pg_off; + u8 pass, done_zones, search_zone, need_writeback = 0, bit; + unsigned int memalloc_flags; + u8 has_guess, used_zone_pos; + pgoff_t index; + + ntfs_debug("Entering for start_vcn 0x%llx, count 0x%llx, start_lcn 0x%llx, zone %s_ZONE.", + start_vcn, count, start_lcn, + zone == MFT_ZONE ? "MFT" : "DATA"); + + lcnbmp_vi = vol->lcnbmp_ino; + if (start_vcn < 0 || start_lcn < LCN_HOLE || + zone < FIRST_ZONE || zone > LAST_ZONE) + return ERR_PTR(-EINVAL); + + /* Return NULL if @count is zero. */ + if (count < 0 || !count) + return ERR_PTR(-EINVAL); + + memalloc_flags = memalloc_nofs_save(); + + if (!NVolFreeClusterKnown(vol)) + wait_event(vol->free_waitq, NVolFreeClusterKnown(vol)); + free_clusters = atomic64_read(&vol->free_clusters); + + /* Take the lcnbmp lock for writing. */ + down_write(&vol->lcnbmp_lock); + if (is_dealloc == false) + free_clusters -= atomic64_read(&vol->dirty_clusters); + + if (free_clusters < count) { + err = -ENOSPC; + goto out_restore; + } + + /* + * If no specific @start_lcn was requested, use the current data zone + * position, otherwise use the requested @start_lcn but make sure it + * lies outside the mft zone. Also set done_zones to 0 (no zones done) + * and pass depending on whether we are starting inside a zone (1) or + * at the beginning of a zone (2). If requesting from the MFT_ZONE, + * we either start at the current position within the mft zone or at + * the specified position. If the latter is out of bounds then we start + * at the beginning of the MFT_ZONE. + */ + done_zones = 0; + pass = 1; + /* + * zone_start and zone_end are the current search range. search_zone + * is 1 for mft zone, 2 for data zone 1 (end of mft zone till end of + * volume) and 4 for data zone 2 (start of volume till start of mft + * zone). + */ + has_guess = 1; + zone_start = start_lcn; + + if (zone_start < 0) { + if (zone == DATA_ZONE) + zone_start = vol->data1_zone_pos; + else + zone_start = vol->mft_zone_pos; + if (!zone_start) { + /* + * Zone starts at beginning of volume which means a + * single pass is sufficient. + */ + pass = 2; + } + has_guess = 0; + } + + used_zone_pos = has_guess ? 0 : 1; + + if (!zone_start || zone_start == vol->mft_zone_start || + zone_start == vol->mft_zone_end) + pass = 2; + + if (zone_start < vol->mft_zone_start) { + zone_end = vol->mft_zone_start; + search_zone = 4; + /* Skip searching the mft zone. */ + done_zones |= 1; + } else if (zone_start < vol->mft_zone_end) { + zone_end = vol->mft_zone_end; + search_zone = 1; + } else { + zone_end = vol->nr_clusters; + search_zone = 2; + /* Skip searching the mft zone. */ + done_zones |= 1; + } + + /* + * bmp_pos is the current bit position inside the bitmap. We use + * bmp_initial_pos to determine whether or not to do a zone switch. + */ + bmp_pos = bmp_initial_pos = zone_start; + + /* Loop until all clusters are allocated, i.e. clusters == 0. */ + clusters = count; + rlpos = rlsize = 0; + mapping = lcnbmp_vi->i_mapping; + i_size = i_size_read(lcnbmp_vi); + while (1) { + ntfs_debug("Start of outer while loop: done_zones 0x%x, search_zone %i, pass %i, zone_start 0x%llx, zone_end 0x%llx, bmp_initial_pos 0x%llx, bmp_pos 0x%llx, rlpos %i, rlsize %i.", + done_zones, search_zone, pass, + zone_start, zone_end, bmp_initial_pos, + bmp_pos, rlpos, rlsize); + /* Loop until we run out of free clusters. */ + last_read_pos = bmp_pos >> 3; + ntfs_debug("last_read_pos 0x%llx.", last_read_pos); + if (last_read_pos >= i_size) { + ntfs_debug("End of attribute reached. Skipping to zone_pass_done."); + goto zone_pass_done; + } + if (likely(folio)) { + if (need_writeback) { + ntfs_debug("Marking page dirty."); + folio_mark_dirty(folio); + need_writeback = 0; + } + folio_unlock(folio); + kunmap_local(buf); + folio_put(folio); + folio = NULL; + } + + index = last_read_pos >> PAGE_SHIFT; + pg_off = last_read_pos & ~PAGE_MASK; + buf_size = PAGE_SIZE - pg_off; + if (unlikely(last_read_pos + buf_size > i_size)) + buf_size = i_size - last_read_pos; + buf_size <<= 3; + lcn = bmp_pos & 7; + bmp_pos &= ~(s64)7; + + if (vol->lcn_empty_bits_per_page[index] == 0) + goto next_bmp_pos; + + folio = read_mapping_folio(mapping, index, NULL); + if (IS_ERR(folio)) { + err = PTR_ERR(folio); + ntfs_error(vol->sb, "Failed to map page."); + goto out; + } + + folio_lock(folio); + buf = kmap_local_folio(folio, 0) + pg_off; + ntfs_debug("Before inner while loop: buf_size %i, lcn 0x%llx, bmp_pos 0x%llx, need_writeback %i.", + buf_size, lcn, bmp_pos, need_writeback); + while (lcn < buf_size && lcn + bmp_pos < zone_end) { + byte = buf + (lcn >> 3); + ntfs_debug("In inner while loop: buf_size %i, lcn 0x%llx, bmp_pos 0x%llx, need_writeback %i, byte ofs 0x%x, *byte 0x%x.", + buf_size, lcn, bmp_pos, need_writeback, + (unsigned int)(lcn >> 3), + (unsigned int)*byte); + bit = 1 << (lcn & 7); + ntfs_debug("bit 0x%x.", bit); + + if (has_guess) { + if (*byte & bit) { + if (is_contig == true && prev_run_len > 0) + goto done; + + has_guess = 0; + break; + } + } else { + lcn = max_empty_bit_range(buf, buf_size >> 3); + if (lcn < 0) + break; + has_guess = 1; + continue; + } + /* + * Allocate more memory if needed, including space for + * the terminator element. + * kvzalloc() operates on whole pages only. + */ + if ((rlpos + 2) * sizeof(*rl) > rlsize) { + struct runlist_element *rl2; + + ntfs_debug("Reallocating memory."); + if (!rl) + ntfs_debug("First free bit is at s64 0x%llx.", + lcn + bmp_pos); + rl2 = kvzalloc(rlsize + PAGE_SIZE, GFP_NOFS); + if (unlikely(!rl2)) { + err = -ENOMEM; + ntfs_error(vol->sb, "Failed to allocate memory."); + goto out; + } + memcpy(rl2, rl, rlsize); + kvfree(rl); + rl = rl2; + rlsize += PAGE_SIZE; + ntfs_debug("Reallocated memory, rlsize 0x%x.", + rlsize); + } + /* Allocate the bitmap bit. */ + *byte |= bit; + /* We need to write this bitmap page to disk. */ + need_writeback = 1; + ntfs_debug("*byte 0x%x, need_writeback is set.", + (unsigned int)*byte); + ntfs_dec_free_clusters(vol, 1); + ntfs_set_lcn_empty_bits(vol, index, 1, 1); + + /* + * Coalesce with previous run if adjacent LCNs. + * Otherwise, append a new run. + */ + ntfs_debug("Adding run (lcn 0x%llx, len 0x%llx), prev_lcn 0x%llx, lcn 0x%llx, bmp_pos 0x%llx, prev_run_len 0x%llx, rlpos %i.", + lcn + bmp_pos, 1ULL, prev_lcn, + lcn, bmp_pos, prev_run_len, rlpos); + if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { + ntfs_debug("Coalescing to run (lcn 0x%llx, len 0x%llx).", + rl[rlpos - 1].lcn, + rl[rlpos - 1].length); + rl[rlpos - 1].length = ++prev_run_len; + ntfs_debug("Run now (lcn 0x%llx, len 0x%llx), prev_run_len 0x%llx.", + rl[rlpos - 1].lcn, + rl[rlpos - 1].length, + prev_run_len); + } else { + if (likely(rlpos)) { + ntfs_debug("Adding new run, (previous run lcn 0x%llx, len 0x%llx).", + rl[rlpos - 1].lcn, rl[rlpos - 1].length); + rl[rlpos].vcn = rl[rlpos - 1].vcn + + prev_run_len; + } else { + ntfs_debug("Adding new run, is first run."); + rl[rlpos].vcn = start_vcn; + } + rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; + rl[rlpos].length = prev_run_len = 1; + rlpos++; + } + /* Done? */ + if (!--clusters) { + s64 tc; +done: + if (!used_zone_pos) + goto out; + /* + * Update the current zone position. Positions + * of already scanned zones have been updated + * during the respective zone switches. + */ + tc = lcn + bmp_pos + 1; + ntfs_debug("Done. Updating current zone position, tc 0x%llx, search_zone %i.", + tc, search_zone); + switch (search_zone) { + case 1: + ntfs_debug("Before checks, vol->mft_zone_pos 0x%llx.", + vol->mft_zone_pos); + if (tc >= vol->mft_zone_end) { + vol->mft_zone_pos = + vol->mft_lcn; + if (!vol->mft_zone_end) + vol->mft_zone_pos = 0; + } else if ((bmp_initial_pos >= + vol->mft_zone_pos || + tc > vol->mft_zone_pos) + && tc >= vol->mft_lcn) + vol->mft_zone_pos = tc; + ntfs_debug("After checks, vol->mft_zone_pos 0x%llx.", + vol->mft_zone_pos); + break; + case 2: + ntfs_debug("Before checks, vol->data1_zone_pos 0x%llx.", + vol->data1_zone_pos); + if (tc >= vol->nr_clusters) + vol->data1_zone_pos = + vol->mft_zone_end; + else if ((bmp_initial_pos >= + vol->data1_zone_pos || + tc > vol->data1_zone_pos) + && tc >= vol->mft_zone_end) + vol->data1_zone_pos = tc; + ntfs_debug("After checks, vol->data1_zone_pos 0x%llx.", + vol->data1_zone_pos); + break; + case 4: + ntfs_debug("Before checks, vol->data2_zone_pos 0x%llx.", + vol->data2_zone_pos); + if (tc >= vol->mft_zone_start) + vol->data2_zone_pos = 0; + else if (bmp_initial_pos >= + vol->data2_zone_pos || + tc > vol->data2_zone_pos) + vol->data2_zone_pos = tc; + ntfs_debug("After checks, vol->data2_zone_pos 0x%llx.", + vol->data2_zone_pos); + break; + default: + WARN_ON(1); + } + ntfs_debug("Finished. Going to out."); + goto out; + } + lcn++; + } + + if (!used_zone_pos) { + used_zone_pos = 1; + if (search_zone == 1) + zone_start = vol->mft_zone_pos; + else if (search_zone == 2) + zone_start = vol->data1_zone_pos; + else + zone_start = vol->data2_zone_pos; + + if (!zone_start || zone_start == vol->mft_zone_start || + zone_start == vol->mft_zone_end) + pass = 2; + bmp_pos = zone_start; + } else { +next_bmp_pos: + bmp_pos += buf_size; + } + + ntfs_debug("After inner while loop: buf_size 0x%x, lcn 0x%llx, bmp_pos 0x%llx, need_writeback %i.", + buf_size, lcn, bmp_pos, need_writeback); + if (bmp_pos < zone_end) { + ntfs_debug("Continuing outer while loop, bmp_pos 0x%llx, zone_end 0x%llx.", + bmp_pos, zone_end); + continue; + } +zone_pass_done: /* Finished with the current zone pass. */ + ntfs_debug("At zone_pass_done, pass %i.", pass); + if (pass == 1) { + /* + * Now do pass 2, scanning the first part of the zone + * we omitted in pass 1. + */ + pass = 2; + zone_end = zone_start; + switch (search_zone) { + case 1: /* mft_zone */ + zone_start = vol->mft_zone_start; + break; + case 2: /* data1_zone */ + zone_start = vol->mft_zone_end; + break; + case 4: /* data2_zone */ + zone_start = 0; + break; + default: + WARN_ON(1); + } + /* Sanity check. */ + if (zone_end < zone_start) + zone_end = zone_start; + bmp_pos = zone_start; + ntfs_debug("Continuing outer while loop, pass 2, zone_start 0x%llx, zone_end 0x%llx, bmp_pos 0x%llx.", + zone_start, zone_end, bmp_pos); + continue; + } /* pass == 2 */ +done_zones_check: + ntfs_debug("At done_zones_check, search_zone %i, done_zones before 0x%x, done_zones after 0x%x.", + search_zone, done_zones, + done_zones | search_zone); + done_zones |= search_zone; + if (done_zones < 7) { + ntfs_debug("Switching zone."); + /* Now switch to the next zone we haven't done yet. */ + pass = 1; + switch (search_zone) { + case 1: + ntfs_debug("Switching from mft zone to data1 zone."); + /* Update mft zone position. */ + if (rlpos && used_zone_pos) { + s64 tc; + + ntfs_debug("Before checks, vol->mft_zone_pos 0x%llx.", + vol->mft_zone_pos); + tc = rl[rlpos - 1].lcn + + rl[rlpos - 1].length; + if (tc >= vol->mft_zone_end) { + vol->mft_zone_pos = + vol->mft_lcn; + if (!vol->mft_zone_end) + vol->mft_zone_pos = 0; + } else if ((bmp_initial_pos >= + vol->mft_zone_pos || + tc > vol->mft_zone_pos) + && tc >= vol->mft_lcn) + vol->mft_zone_pos = tc; + ntfs_debug("After checks, vol->mft_zone_pos 0x%llx.", + vol->mft_zone_pos); + } + /* Switch from mft zone to data1 zone. */ +switch_to_data1_zone: search_zone = 2; + zone_start = bmp_initial_pos = + vol->data1_zone_pos; + zone_end = vol->nr_clusters; + if (zone_start == vol->mft_zone_end) + pass = 2; + if (zone_start >= zone_end) { + vol->data1_zone_pos = zone_start = + vol->mft_zone_end; + pass = 2; + } + break; + case 2: + ntfs_debug("Switching from data1 zone to data2 zone."); + /* Update data1 zone position. */ + if (rlpos && used_zone_pos) { + s64 tc; + + ntfs_debug("Before checks, vol->data1_zone_pos 0x%llx.", + vol->data1_zone_pos); + tc = rl[rlpos - 1].lcn + + rl[rlpos - 1].length; + if (tc >= vol->nr_clusters) + vol->data1_zone_pos = + vol->mft_zone_end; + else if ((bmp_initial_pos >= + vol->data1_zone_pos || + tc > vol->data1_zone_pos) + && tc >= vol->mft_zone_end) + vol->data1_zone_pos = tc; + ntfs_debug("After checks, vol->data1_zone_pos 0x%llx.", + vol->data1_zone_pos); + } + /* Switch from data1 zone to data2 zone. */ + search_zone = 4; + zone_start = bmp_initial_pos = + vol->data2_zone_pos; + zone_end = vol->mft_zone_start; + if (!zone_start) + pass = 2; + if (zone_start >= zone_end) { + vol->data2_zone_pos = zone_start = + bmp_initial_pos = 0; + pass = 2; + } + break; + case 4: + ntfs_debug("Switching from data2 zone to data1 zone."); + /* Update data2 zone position. */ + if (rlpos && used_zone_pos) { + s64 tc; + + ntfs_debug("Before checks, vol->data2_zone_pos 0x%llx.", + vol->data2_zone_pos); + tc = rl[rlpos - 1].lcn + + rl[rlpos - 1].length; + if (tc >= vol->mft_zone_start) + vol->data2_zone_pos = 0; + else if (bmp_initial_pos >= + vol->data2_zone_pos || + tc > vol->data2_zone_pos) + vol->data2_zone_pos = tc; + ntfs_debug("After checks, vol->data2_zone_pos 0x%llx.", + vol->data2_zone_pos); + } + /* Switch from data2 zone to data1 zone. */ + goto switch_to_data1_zone; + default: + WARN_ON(1); + } + ntfs_debug("After zone switch, search_zone %i, pass %i, bmp_initial_pos 0x%llx, zone_start 0x%llx, zone_end 0x%llx.", + search_zone, pass, + bmp_initial_pos, + zone_start, + zone_end); + bmp_pos = zone_start; + if (zone_start == zone_end) { + ntfs_debug("Empty zone, going to done_zones_check."); + /* Empty zone. Don't bother searching it. */ + goto done_zones_check; + } + ntfs_debug("Continuing outer while loop."); + continue; + } /* done_zones == 7 */ + ntfs_debug("All zones are finished."); + /* + * All zones are finished! If DATA_ZONE, shrink mft zone. If + * MFT_ZONE, we have really run out of space. + */ + mft_zone_size = vol->mft_zone_end - vol->mft_zone_start; + ntfs_debug("vol->mft_zone_start 0x%llx, vol->mft_zone_end 0x%llx, mft_zone_size 0x%llx.", + vol->mft_zone_start, vol->mft_zone_end, + mft_zone_size); + if (zone == MFT_ZONE || mft_zone_size <= 0) { + ntfs_debug("No free clusters left, going to out."); + /* Really no more space left on device. */ + err = -ENOSPC; + goto out; + } /* zone == DATA_ZONE && mft_zone_size > 0 */ + ntfs_debug("Shrinking mft zone."); + zone_end = vol->mft_zone_end; + mft_zone_size >>= 1; + if (mft_zone_size > 0) + vol->mft_zone_end = vol->mft_zone_start + mft_zone_size; + else /* mft zone and data2 zone no longer exist. */ + vol->data2_zone_pos = vol->mft_zone_start = + vol->mft_zone_end = 0; + if (vol->mft_zone_pos >= vol->mft_zone_end) { + vol->mft_zone_pos = vol->mft_lcn; + if (!vol->mft_zone_end) + vol->mft_zone_pos = 0; + } + bmp_pos = zone_start = bmp_initial_pos = + vol->data1_zone_pos = vol->mft_zone_end; + search_zone = 2; + pass = 2; + done_zones &= ~2; + ntfs_debug("After shrinking mft zone, mft_zone_size 0x%llx, vol->mft_zone_start 0x%llx, vol->mft_zone_end 0x%llx, vol->mft_zone_pos 0x%llx, search_zone 2, pass 2, dones_zones 0x%x, zone_start 0x%llx, zone_end 0x%llx, vol->data1_zone_pos 0x%llx, continuing outer while loop.", + mft_zone_size, vol->mft_zone_start, + vol->mft_zone_end, vol->mft_zone_pos, + done_zones, zone_start, zone_end, + vol->data1_zone_pos); + } + ntfs_debug("After outer while loop."); +out: + ntfs_debug("At out."); + /* Add runlist terminator element. */ + if (likely(rl)) { + rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; + rl[rlpos].lcn = is_extension ? LCN_ENOENT : LCN_RL_NOT_MAPPED; + rl[rlpos].length = 0; + } + if (!IS_ERR_OR_NULL(folio)) { + if (need_writeback) { + ntfs_debug("Marking page dirty."); + folio_mark_dirty(folio); + need_writeback = 0; + } + folio_unlock(folio); + kunmap_local(buf); + folio_put(folio); + } + if (likely(!err)) { + if (!rl) { + err = -EIO; + goto out_restore; + } + if (is_dealloc == true) + ntfs_release_dirty_clusters(vol, rl->length); + ntfs_debug("Done."); + goto out_restore; + } + if (err != -ENOSPC) + ntfs_error(vol->sb, + "Failed to allocate clusters, aborting (error %i).", + err); + if (rl) { + int err2; + + if (err == -ENOSPC) + ntfs_debug("Not enough space to complete allocation, err -ENOSPC, first free lcn 0x%llx, could allocate up to 0x%llx clusters.", + rl[0].lcn, count - clusters); + /* Deallocate all allocated clusters. */ + ntfs_debug("Attempting rollback..."); + err2 = ntfs_cluster_free_from_rl_nolock(vol, rl); + if (err2) { + ntfs_error(vol->sb, + "Failed to rollback (error %i). Leaving inconsistent metadata! Unmount and run chkdsk.", + err2); + NVolSetErrors(vol); + } + /* Free the runlist. */ + kvfree(rl); + } else if (err == -ENOSPC) + ntfs_debug("No space left at all, err = -ENOSPC, first free lcn = 0x%llx.", + vol->data1_zone_pos); + atomic64_set(&vol->dirty_clusters, 0); + +out_restore: + up_write(&vol->lcnbmp_lock); + memalloc_nofs_restore(memalloc_flags); + + return err < 0 ? ERR_PTR(err) : rl; +} + +/* + * __ntfs_cluster_free - free clusters on an ntfs volume + * @ni: ntfs inode whose runlist describes the clusters to free + * @start_vcn: vcn in the runlist of @ni at which to start freeing clusters + * @count: number of clusters to free or -1 for all clusters + * @ctx: active attribute search context if present or NULL if not + * @is_rollback: true if this is a rollback operation + * + * Free @count clusters starting at the cluster @start_vcn in the runlist + * described by the vfs inode @ni. + * + * If @count is -1, all clusters from @start_vcn to the end of the runlist are + * deallocated. Thus, to completely free all clusters in a runlist, use + * @start_vcn = 0 and @count = -1. + * + * If @ctx is specified, it is an active search context of @ni and its base mft + * record. This is needed when __ntfs_cluster_free() encounters unmapped + * runlist fragments and allows their mapping. If you do not have the mft + * record mapped, you can specify @ctx as NULL and __ntfs_cluster_free() will + * perform the necessary mapping and unmapping. + * + * Note, __ntfs_cluster_free() saves the state of @ctx on entry and restores it + * before returning. Thus, @ctx will be left pointing to the same attribute on + * return as on entry. However, the actual pointers in @ctx may point to + * different memory locations on return, so you must remember to reset any + * cached pointers from the @ctx, i.e. after the call to __ntfs_cluster_free(), + * you will probably want to do: + * m = ctx->mrec; + * a = ctx->attr; + * Assuming you cache ctx->attr in a variable @a of type attr_record * and that + * you cache ctx->mrec in a variable @m of type struct mft_record *. + * + * @is_rollback should always be 'false', it is for internal use to rollback + * errors. You probably want to use ntfs_cluster_free() instead. + * + * Note, __ntfs_cluster_free() does not modify the runlist, so you have to + * remove from the runlist or mark sparse the freed runs later. + * + * Return the number of deallocated clusters (not counting sparse ones) on + * success and -errno on error. + * + * WARNING: If @ctx is supplied, regardless of whether success or failure is + * returned, you need to check IS_ERR(@ctx->mrec) and if 'true' the @ctx + * is no longer valid, i.e. you need to either call + * ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it. + * In that case PTR_ERR(@ctx->mrec) will give you the error code for + * why the mapping of the old inode failed. + * + * Locking: - The runlist described by @ni must be locked for writing on entry + * and is locked on return. Note the runlist may be modified when + * needed runlist fragments need to be mapped. + * - The volume lcn bitmap must be unlocked on entry and is unlocked + * on return. + * - This function takes the volume lcn bitmap lock for writing and + * modifies the bitmap contents. + * - If @ctx is NULL, the base mft record of @ni must not be mapped on + * entry and it will be left unmapped on return. + * - If @ctx is not NULL, the base mft record must be mapped on entry + * and it will be left mapped on return. + */ +s64 __ntfs_cluster_free(struct ntfs_inode *ni, const s64 start_vcn, s64 count, + struct ntfs_attr_search_ctx *ctx, const bool is_rollback) +{ + s64 delta, to_free, total_freed, real_freed; + struct ntfs_volume *vol; + struct inode *lcnbmp_vi; + struct runlist_element *rl; + int err; + unsigned int memalloc_flags; + + ntfs_debug("Entering for i_ino 0x%llx, start_vcn 0x%llx, count 0x%llx.%s", + ni->mft_no, start_vcn, count, + is_rollback ? " (rollback)" : ""); + vol = ni->vol; + lcnbmp_vi = vol->lcnbmp_ino; + if (start_vcn < 0 || count < -1) + return -EINVAL; + + if (!NVolFreeClusterKnown(vol)) + wait_event(vol->free_waitq, NVolFreeClusterKnown(vol)); + + /* + * Lock the lcn bitmap for writing but only if not rolling back. We + * must hold the lock all the way including through rollback otherwise + * rollback is not possible because once we have cleared a bit and + * dropped the lock, anyone could have set the bit again, thus + * allocating the cluster for another use. + */ + if (likely(!is_rollback)) { + memalloc_flags = memalloc_nofs_save(); + down_write(&vol->lcnbmp_lock); + } + + total_freed = real_freed = 0; + + rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, ctx); + if (IS_ERR(rl)) { + err = PTR_ERR(rl); + if (err == -ENOENT) { + if (likely(!is_rollback)) { + up_write(&vol->lcnbmp_lock); + memalloc_nofs_restore(memalloc_flags); + } + return 0; + } + + if (!is_rollback) + ntfs_error(vol->sb, + "Failed to find first runlist element (error %d), aborting.", + err); + goto err_out; + } + if (unlikely(rl->lcn < LCN_HOLE)) { + if (!is_rollback) + ntfs_error(vol->sb, "First runlist element has invalid lcn, aborting."); + err = -EIO; + goto err_out; + } + /* Find the starting cluster inside the run that needs freeing. */ + delta = start_vcn - rl->vcn; + + /* The number of clusters in this run that need freeing. */ + to_free = rl->length - delta; + if (count >= 0 && to_free > count) + to_free = count; + + if (likely(rl->lcn >= 0)) { + /* Do the actual freeing of the clusters in this run. */ + err = ntfs_bitmap_set_bits_in_run(lcnbmp_vi, rl->lcn + delta, + to_free, likely(!is_rollback) ? 0 : 1); + if (unlikely(err)) { + if (!is_rollback) + ntfs_error(vol->sb, + "Failed to clear first run (error %i), aborting.", + err); + goto err_out; + } + /* We have freed @to_free real clusters. */ + real_freed = to_free; + } + /* Go to the next run and adjust the number of clusters left to free. */ + ++rl; + if (count >= 0) + count -= to_free; + + /* Keep track of the total "freed" clusters, including sparse ones. */ + total_freed = to_free; + /* + * Loop over the remaining runs, using @count as a capping value, and + * free them. + */ + for (; rl->length && count != 0; ++rl) { + if (unlikely(rl->lcn < LCN_HOLE)) { + s64 vcn; + + /* Attempt to map runlist. */ + vcn = rl->vcn; + rl = ntfs_attr_find_vcn_nolock(ni, vcn, ctx); + if (IS_ERR(rl)) { + err = PTR_ERR(rl); + if (!is_rollback) + ntfs_error(vol->sb, + "Failed to map runlist fragment or failed to find subsequent runlist element."); + goto err_out; + } + if (unlikely(rl->lcn < LCN_HOLE)) { + if (!is_rollback) + ntfs_error(vol->sb, + "Runlist element has invalid lcn (0x%llx).", + rl->lcn); + err = -EIO; + goto err_out; + } + } + /* The number of clusters in this run that need freeing. */ + to_free = rl->length; + if (count >= 0 && to_free > count) + to_free = count; + + if (likely(rl->lcn >= 0)) { + /* Do the actual freeing of the clusters in the run. */ + err = ntfs_bitmap_set_bits_in_run(lcnbmp_vi, rl->lcn, + to_free, likely(!is_rollback) ? 0 : 1); + if (unlikely(err)) { + if (!is_rollback) + ntfs_error(vol->sb, "Failed to clear subsequent run."); + goto err_out; + } + /* We have freed @to_free real clusters. */ + real_freed += to_free; + } + /* Adjust the number of clusters left to free. */ + if (count >= 0) + count -= to_free; + + /* Update the total done clusters. */ + total_freed += to_free; + } + ntfs_inc_free_clusters(vol, real_freed); + if (likely(!is_rollback)) { + up_write(&vol->lcnbmp_lock); + memalloc_nofs_restore(memalloc_flags); + } + + WARN_ON(count > 0); + + if (NVolDiscard(vol) && !is_rollback) { + s64 total_discarded = 0, rl_off; + u32 gran = bdev_discard_granularity(vol->sb->s_bdev); + + rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, ctx); + if (IS_ERR(rl)) + return real_freed; + rl_off = start_vcn - rl->vcn; + while (rl->length && total_discarded < total_freed) { + s64 to_discard = rl->length - rl_off; + + if (to_discard + total_discarded > total_freed) + to_discard = total_freed - total_discarded; + if (rl->lcn >= 0) { + sector_t start_sector, end_sector; + int ret; + + start_sector = ALIGN(NTFS_CLU_TO_B(vol, rl->lcn + rl_off), + gran) >> SECTOR_SHIFT; + end_sector = ALIGN_DOWN(NTFS_CLU_TO_B(vol, + rl->lcn + rl_off + to_discard), + gran) >> SECTOR_SHIFT; + if (start_sector < end_sector) { + ret = blkdev_issue_discard(vol->sb->s_bdev, start_sector, + end_sector - start_sector, + GFP_NOFS); + if (ret) + break; + } + } + + total_discarded += to_discard; + ++rl; + rl_off = 0; + } + } + + /* We are done. Return the number of actually freed clusters. */ + ntfs_debug("Done."); + return real_freed; +err_out: + if (is_rollback) + return err; + /* If no real clusters were freed, no need to rollback. */ + if (!real_freed) { + up_write(&vol->lcnbmp_lock); + memalloc_nofs_restore(memalloc_flags); + return err; + } + /* + * Attempt to rollback and if that succeeds just return the error code. + * If rollback fails, set the volume errors flag, emit an error + * message, and return the error code. + */ + delta = __ntfs_cluster_free(ni, start_vcn, total_freed, ctx, true); + if (delta < 0) { + ntfs_error(vol->sb, + "Failed to rollback (error %i). Leaving inconsistent metadata! Unmount and run chkdsk.", + (int)delta); + NVolSetErrors(vol); + } + ntfs_dec_free_clusters(vol, delta); + up_write(&vol->lcnbmp_lock); + memalloc_nofs_restore(memalloc_flags); + ntfs_error(vol->sb, "Aborting (error %i).", err); + return err; +} diff --git a/fs/ntfs/lcnalloc.h b/fs/ntfs/lcnalloc.h new file mode 100644 index 000000000000..d9ac622e4658 --- /dev/null +++ b/fs/ntfs/lcnalloc.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Exports for NTFS kernel cluster (de)allocation. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + */ + +#ifndef _LINUX_NTFS_LCNALLOC_H +#define _LINUX_NTFS_LCNALLOC_H + +#include + +#include "attrib.h" + +/* + * enum zone_type - Zone identifiers for cluster allocation policy + * + * FIRST_ZONE For sanity checking. + * MFT_ZONE Allocate from $MFT zone. + * DATA_ZONE Allocate from $DATA zone. + * LAST_ZONE For sanity checking. + */ +enum { + FIRST_ZONE = 0, + MFT_ZONE = 0, + DATA_ZONE = 1, + LAST_ZONE = 1, +}; + +struct runlist_element *ntfs_cluster_alloc(struct ntfs_volume *vol, + const s64 start_vcn, const s64 count, const s64 start_lcn, + const int zone, + const bool is_extension, + const bool is_contig, + const bool is_dealloc); +s64 __ntfs_cluster_free(struct ntfs_inode *ni, const s64 start_vcn, + s64 count, struct ntfs_attr_search_ctx *ctx, const bool is_rollback); + +/* + * ntfs_cluster_free - free clusters on an ntfs volume + * @ni: ntfs inode whose runlist describes the clusters to free + * @start_vcn: vcn in the runlist of @ni at which to start freeing clusters + * @count: number of clusters to free or -1 for all clusters + * @ctx: active attribute search context if present or NULL if not + * + * Free @count clusters starting at the cluster @start_vcn in the runlist + * described by the ntfs inode @ni. + * + * If @count is -1, all clusters from @start_vcn to the end of the runlist are + * deallocated. Thus, to completely free all clusters in a runlist, use + * @start_vcn = 0 and @count = -1. + * + * If @ctx is specified, it is an active search context of @ni and its base mft + * record. This is needed when ntfs_cluster_free() encounters unmapped runlist + * fragments and allows their mapping. If you do not have the mft record + * mapped, you can specify @ctx as NULL and ntfs_cluster_free() will perform + * the necessary mapping and unmapping. + * + * Note, ntfs_cluster_free() saves the state of @ctx on entry and restores it + * before returning. Thus, @ctx will be left pointing to the same attribute on + * return as on entry. However, the actual pointers in @ctx may point to + * different memory locations on return, so you must remember to reset any + * cached pointers from the @ctx, i.e. after the call to ntfs_cluster_free(), + * you will probably want to do: + * m = ctx->mrec; + * a = ctx->attr; + * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that + * you cache ctx->mrec in a variable @m of type MFT_RECORD *. + * + * Note, ntfs_cluster_free() does not modify the runlist, so you have to remove + * from the runlist or mark sparse the freed runs later. + * + * Return the number of deallocated clusters (not counting sparse ones) on + * success and -errno on error. + * + * WARNING: If @ctx is supplied, regardless of whether success or failure is + * returned, you need to check IS_ERR(@ctx->mrec) and if 'true' the @ctx + * is no longer valid, i.e. you need to either call + * ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it. + * In that case PTR_ERR(@ctx->mrec) will give you the error code for + * why the mapping of the old inode failed. + * + * Locking: - The runlist described by @ni must be locked for writing on entry + * and is locked on return. Note the runlist may be modified when + * needed runlist fragments need to be mapped. + * - The volume lcn bitmap must be unlocked on entry and is unlocked + * on return. + * - This function takes the volume lcn bitmap lock for writing and + * modifies the bitmap contents. + * - If @ctx is NULL, the base mft record of @ni must not be mapped on + * entry and it will be left unmapped on return. + * - If @ctx is not NULL, the base mft record must be mapped on entry + * and it will be left mapped on return. + */ +static inline s64 ntfs_cluster_free(struct ntfs_inode *ni, const s64 start_vcn, + s64 count, struct ntfs_attr_search_ctx *ctx) +{ + return __ntfs_cluster_free(ni, start_vcn, count, ctx, false); +} + +int ntfs_cluster_free_from_rl_nolock(struct ntfs_volume *vol, + const struct runlist_element *rl); + +/* + * ntfs_cluster_free_from_rl - free clusters from runlist + * @vol: mounted ntfs volume on which to free the clusters + * @rl: runlist describing the clusters to free + * + * Free all the clusters described by the runlist @rl on the volume @vol. In + * the case of an error being returned, at least some of the clusters were not + * freed. + * + * Return 0 on success and -errno on error. + * + * Locking: - This function takes the volume lcn bitmap lock for writing and + * modifies the bitmap contents. + * - The caller must have locked the runlist @rl for reading or + * writing. + */ +static inline int ntfs_cluster_free_from_rl(struct ntfs_volume *vol, + const struct runlist_element *rl) +{ + int ret; + unsigned int memalloc_flags; + + memalloc_flags = memalloc_nofs_save(); + down_write(&vol->lcnbmp_lock); + ret = ntfs_cluster_free_from_rl_nolock(vol, rl); + up_write(&vol->lcnbmp_lock); + memalloc_nofs_restore(memalloc_flags); + return ret; +} + +#endif /* defined _LINUX_NTFS_LCNALLOC_H */ diff --git a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c new file mode 100644 index 000000000000..3f8d1640f1d5 --- /dev/null +++ b/fs/ntfs/logfile.c @@ -0,0 +1,778 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel journal handling. + * + * Copyright (c) 2002-2007 Anton Altaparmakov + */ + +#include + +#include "attrib.h" +#include "logfile.h" +#include "ntfs.h" + +/* + * ntfs_check_restart_page_header - check the page header for consistency + * @vi: LogFile inode to which the restart page header belongs + * @rp: restart page header to check + * @pos: position in @vi at which the restart page header resides + * + * Check the restart page header @rp for consistency and return 'true' if it is + * consistent and 'false' otherwise. + * + * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not + * require the full restart page. + */ +static bool ntfs_check_restart_page_header(struct inode *vi, + struct restart_page_header *rp, s64 pos) +{ + u32 logfile_system_page_size, logfile_log_page_size; + u16 ra_ofs, usa_count, usa_ofs, usa_end = 0; + bool have_usa = true; + + ntfs_debug("Entering."); + /* + * If the system or log page sizes are smaller than the ntfs block size + * or either is not a power of 2 we cannot handle this log file. + */ + logfile_system_page_size = le32_to_cpu(rp->system_page_size); + logfile_log_page_size = le32_to_cpu(rp->log_page_size); + if (logfile_system_page_size < NTFS_BLOCK_SIZE || + logfile_log_page_size < NTFS_BLOCK_SIZE || + logfile_system_page_size & + (logfile_system_page_size - 1) || + !is_power_of_2(logfile_log_page_size)) { + ntfs_error(vi->i_sb, "LogFile uses unsupported page size."); + return false; + } + /* + * We must be either at !pos (1st restart page) or at pos = system page + * size (2nd restart page). + */ + if (pos && pos != logfile_system_page_size) { + ntfs_error(vi->i_sb, "Found restart area in incorrect position in LogFile."); + return false; + } + /* We only know how to handle version 1.1. */ + if (le16_to_cpu(rp->major_ver) != 1 || + le16_to_cpu(rp->minor_ver) != 1) { + ntfs_error(vi->i_sb, + "LogFile version %i.%i is not supported. (This driver supports version 1.1 only.)", + (int)le16_to_cpu(rp->major_ver), + (int)le16_to_cpu(rp->minor_ver)); + return false; + } + /* + * If chkdsk has been run the restart page may not be protected by an + * update sequence array. + */ + if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) { + have_usa = false; + goto skip_usa_checks; + } + /* Verify the size of the update sequence array. */ + usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS); + if (usa_count != le16_to_cpu(rp->usa_count)) { + ntfs_error(vi->i_sb, + "LogFile restart page specifies inconsistent update sequence array count."); + return false; + } + /* Verify the position of the update sequence array. */ + usa_ofs = le16_to_cpu(rp->usa_ofs); + usa_end = usa_ofs + usa_count * sizeof(u16); + if (usa_ofs < sizeof(struct restart_page_header) || + usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { + ntfs_error(vi->i_sb, + "LogFile restart page specifies inconsistent update sequence array offset."); + return false; + } +skip_usa_checks: + /* + * Verify the position of the restart area. It must be: + * - aligned to 8-byte boundary, + * - after the update sequence array, and + * - within the system page size. + */ + ra_ofs = le16_to_cpu(rp->restart_area_offset); + if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : + ra_ofs < sizeof(struct restart_page_header)) || + ra_ofs > logfile_system_page_size) { + ntfs_error(vi->i_sb, + "LogFile restart page specifies inconsistent restart area offset."); + return false; + } + /* + * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn + * set. + */ + if (!ntfs_is_chkd_record(rp->magic) && le64_to_cpu(rp->chkdsk_lsn)) { + ntfs_error(vi->i_sb, + "LogFile restart page is not modified by chkdsk but a chkdsk LSN is specified."); + return false; + } + ntfs_debug("Done."); + return true; +} + +/* + * ntfs_check_restart_area - check the restart area for consistency + * @vi: LogFile inode to which the restart page belongs + * @rp: restart page whose restart area to check + * + * Check the restart area of the restart page @rp for consistency and return + * 'true' if it is consistent and 'false' otherwise. + * + * This function assumes that the restart page header has already been + * consistency checked. + * + * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not + * require the full restart page. + */ +static bool ntfs_check_restart_area(struct inode *vi, struct restart_page_header *rp) +{ + u64 file_size; + struct restart_area *ra; + u16 ra_ofs, ra_len, ca_ofs; + u8 fs_bits; + + ntfs_debug("Entering."); + ra_ofs = le16_to_cpu(rp->restart_area_offset); + ra = (struct restart_area *)((u8 *)rp + ra_ofs); + /* + * Everything before ra->file_size must be before the first word + * protected by an update sequence number. This ensures that it is + * safe to access ra->client_array_offset. + */ + if (ra_ofs + offsetof(struct restart_area, file_size) > + NTFS_BLOCK_SIZE - sizeof(u16)) { + ntfs_error(vi->i_sb, + "LogFile restart area specifies inconsistent file offset."); + return false; + } + /* + * Now that we can access ra->client_array_offset, make sure everything + * up to the log client array is before the first word protected by an + * update sequence number. This ensures we can access all of the + * restart area elements safely. Also, the client array offset must be + * aligned to an 8-byte boundary. + */ + ca_ofs = le16_to_cpu(ra->client_array_offset); + if (((ca_ofs + 7) & ~7) != ca_ofs || + ra_ofs + ca_ofs > NTFS_BLOCK_SIZE - sizeof(u16)) { + ntfs_error(vi->i_sb, + "LogFile restart area specifies inconsistent client array offset."); + return false; + } + /* + * The restart area must end within the system page size both when + * calculated manually and as specified by ra->restart_area_length. + * Also, the calculated length must not exceed the specified length. + */ + ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * + sizeof(struct log_client_record); + if (ra_ofs + ra_len > le32_to_cpu(rp->system_page_size) || + ra_ofs + le16_to_cpu(ra->restart_area_length) > + le32_to_cpu(rp->system_page_size) || + ra_len > le16_to_cpu(ra->restart_area_length)) { + ntfs_error(vi->i_sb, + "LogFile restart area is out of bounds of the system page size specified by the restart page header and/or the specified restart area length is inconsistent."); + return false; + } + /* + * The ra->client_free_list and ra->client_in_use_list must be either + * LOGFILE_NO_CLIENT or less than ra->log_clients or they are + * overflowing the client array. + */ + if ((ra->client_free_list != LOGFILE_NO_CLIENT && + le16_to_cpu(ra->client_free_list) >= + le16_to_cpu(ra->log_clients)) || + (ra->client_in_use_list != LOGFILE_NO_CLIENT && + le16_to_cpu(ra->client_in_use_list) >= + le16_to_cpu(ra->log_clients))) { + ntfs_error(vi->i_sb, + "LogFile restart area specifies overflowing client free and/or in use lists."); + return false; + } + /* + * Check ra->seq_number_bits against ra->file_size for consistency. + * We cannot just use ffs() because the file size is not a power of 2. + */ + file_size = le64_to_cpu(ra->file_size); + fs_bits = 0; + while (file_size) { + file_size >>= 1; + fs_bits++; + } + if (le32_to_cpu(ra->seq_number_bits) != 67 - fs_bits) { + ntfs_error(vi->i_sb, + "LogFile restart area specifies inconsistent sequence number bits."); + return false; + } + /* The log record header length must be a multiple of 8. */ + if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) != + le16_to_cpu(ra->log_record_header_length)) { + ntfs_error(vi->i_sb, + "LogFile restart area specifies inconsistent log record header length."); + return false; + } + /* Dito for the log page data offset. */ + if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) != + le16_to_cpu(ra->log_page_data_offset)) { + ntfs_error(vi->i_sb, + "LogFile restart area specifies inconsistent log page data offset."); + return false; + } + ntfs_debug("Done."); + return true; +} + +/* + * ntfs_check_log_client_array - check the log client array for consistency + * @vi: LogFile inode to which the restart page belongs + * @rp: restart page whose log client array to check + * + * Check the log client array of the restart page @rp for consistency and + * return 'true' if it is consistent and 'false' otherwise. + * + * This function assumes that the restart page header and the restart area have + * already been consistency checked. + * + * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this + * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full + * restart page and the page must be multi sector transfer deprotected. + */ +static bool ntfs_check_log_client_array(struct inode *vi, + struct restart_page_header *rp) +{ + struct restart_area *ra; + struct log_client_record *ca, *cr; + u16 nr_clients, idx; + bool in_free_list, idx_is_first; + + ntfs_debug("Entering."); + ra = (struct restart_area *)((u8 *)rp + le16_to_cpu(rp->restart_area_offset)); + ca = (struct log_client_record *)((u8 *)ra + + le16_to_cpu(ra->client_array_offset)); + /* + * Check the ra->client_free_list first and then check the + * ra->client_in_use_list. Check each of the log client records in + * each of the lists and check that the array does not overflow the + * ra->log_clients value. Also keep track of the number of records + * visited as there cannot be more than ra->log_clients records and + * that way we detect eventual loops in within a list. + */ + nr_clients = le16_to_cpu(ra->log_clients); + idx = le16_to_cpu(ra->client_free_list); + in_free_list = true; +check_list: + for (idx_is_first = true; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, + idx = le16_to_cpu(cr->next_client)) { + if (!nr_clients || idx >= le16_to_cpu(ra->log_clients)) + goto err_out; + /* Set @cr to the current log client record. */ + cr = ca + idx; + /* The first log client record must not have a prev_client. */ + if (idx_is_first) { + if (cr->prev_client != LOGFILE_NO_CLIENT) + goto err_out; + idx_is_first = false; + } + } + /* Switch to and check the in use list if we just did the free list. */ + if (in_free_list) { + in_free_list = false; + idx = le16_to_cpu(ra->client_in_use_list); + goto check_list; + } + ntfs_debug("Done."); + return true; +err_out: + ntfs_error(vi->i_sb, "LogFile log client array is corrupt."); + return false; +} + +/* + * ntfs_check_and_load_restart_page - check the restart page for consistency + * @vi: LogFile inode to which the restart page belongs + * @rp: restart page to check + * @pos: position in @vi at which the restart page resides + * @wrp: [OUT] copy of the multi sector transfer deprotected restart page + * @lsn: [OUT] set to the current logfile lsn on success + * + * Check the restart page @rp for consistency and return 0 if it is consistent + * and -errno otherwise. The restart page may have been modified by chkdsk in + * which case its magic is CHKD instead of RSTR. + * + * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not + * require the full restart page. + * + * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a + * copy of the complete multi sector transfer deprotected page. On failure, + * *@wrp is undefined. + * + * Simillarly, if @lsn is not NULL, on success *@lsn will be set to the current + * logfile lsn according to this restart page. On failure, *@lsn is undefined. + * + * The following error codes are defined: + * -EINVAL - The restart page is inconsistent. + * -ENOMEM - Not enough memory to load the restart page. + * -EIO - Failed to reading from LogFile. + */ +static int ntfs_check_and_load_restart_page(struct inode *vi, + struct restart_page_header *rp, s64 pos, struct restart_page_header **wrp, + s64 *lsn) +{ + struct restart_area *ra; + struct restart_page_header *trp; + int size, err; + + ntfs_debug("Entering."); + /* Check the restart page header for consistency. */ + if (!ntfs_check_restart_page_header(vi, rp, pos)) { + /* Error output already done inside the function. */ + return -EINVAL; + } + /* Check the restart area for consistency. */ + if (!ntfs_check_restart_area(vi, rp)) { + /* Error output already done inside the function. */ + return -EINVAL; + } + ra = (struct restart_area *)((u8 *)rp + le16_to_cpu(rp->restart_area_offset)); + /* + * Allocate a buffer to store the whole restart page so we can multi + * sector transfer deprotect it. + */ + trp = kvzalloc(le32_to_cpu(rp->system_page_size), GFP_NOFS); + if (!trp) { + ntfs_error(vi->i_sb, "Failed to allocate memory for LogFile restart page buffer."); + return -ENOMEM; + } + /* + * Read the whole of the restart page into the buffer. If it fits + * completely inside @rp, just copy it from there. Otherwise map all + * the required pages and copy the data from them. + */ + size = PAGE_SIZE - (pos & ~PAGE_MASK); + if (size >= le32_to_cpu(rp->system_page_size)) { + memcpy(trp, rp, le32_to_cpu(rp->system_page_size)); + } else { + pgoff_t idx; + struct folio *folio; + int have_read, to_read; + + /* First copy what we already have in @rp. */ + memcpy(trp, rp, size); + /* Copy the remaining data one page at a time. */ + have_read = size; + to_read = le32_to_cpu(rp->system_page_size) - size; + idx = (pos + size) >> PAGE_SHIFT; + do { + folio = read_mapping_folio(vi->i_mapping, idx, NULL); + if (IS_ERR(folio)) { + ntfs_error(vi->i_sb, "Error mapping LogFile page (index %lu).", + idx); + err = PTR_ERR(folio); + if (err != -EIO && err != -ENOMEM) + err = -EIO; + goto err_out; + } + size = min_t(int, to_read, PAGE_SIZE); + memcpy((u8 *)trp + have_read, folio_address(folio), size); + folio_put(folio); + have_read += size; + to_read -= size; + idx++; + } while (to_read > 0); + } + /* + * Perform the multi sector transfer deprotection on the buffer if the + * restart page is protected. + */ + if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) && + post_read_mst_fixup((struct ntfs_record *)trp, le32_to_cpu(rp->system_page_size))) { + /* + * A multi sector transfer error was detected. We only need to + * abort if the restart page contents exceed the multi sector + * transfer fixup of the first sector. + */ + if (le16_to_cpu(rp->restart_area_offset) + + le16_to_cpu(ra->restart_area_length) > + NTFS_BLOCK_SIZE - sizeof(u16)) { + ntfs_error(vi->i_sb, + "Multi sector transfer error detected in LogFile restart page."); + err = -EINVAL; + goto err_out; + } + } + /* + * If the restart page is modified by chkdsk or there are no active + * logfile clients, the logfile is consistent. Otherwise, need to + * check the log client records for consistency, too. + */ + err = 0; + if (ntfs_is_rstr_record(rp->magic) && + ra->client_in_use_list != LOGFILE_NO_CLIENT) { + if (!ntfs_check_log_client_array(vi, trp)) { + err = -EINVAL; + goto err_out; + } + } + if (lsn) { + if (ntfs_is_rstr_record(rp->magic)) + *lsn = le64_to_cpu(ra->current_lsn); + else /* if (ntfs_is_chkd_record(rp->magic)) */ + *lsn = le64_to_cpu(rp->chkdsk_lsn); + } + ntfs_debug("Done."); + if (wrp) + *wrp = trp; + else { +err_out: + kvfree(trp); + } + return err; +} + +/* + * ntfs_check_logfile - check the journal for consistency + * @log_vi: struct inode of loaded journal LogFile to check + * @rp: [OUT] on success this is a copy of the current restart page + * + * Check the LogFile journal for consistency and return 'true' if it is + * consistent and 'false' if not. On success, the current restart page is + * returned in *@rp. Caller must call kvfree(*@rp) when finished with it. + * + * At present we only check the two restart pages and ignore the log record + * pages. + * + * Note that the MstProtected flag is not set on the LogFile inode and hence + * when reading pages they are not deprotected. This is because we do not know + * if the LogFile was created on a system with a different page size to ours + * yet and mst deprotection would fail if our page size is smaller. + */ +bool ntfs_check_logfile(struct inode *log_vi, struct restart_page_header **rp) +{ + s64 size, pos; + s64 rstr1_lsn, rstr2_lsn; + struct ntfs_volume *vol = NTFS_SB(log_vi->i_sb); + struct address_space *mapping = log_vi->i_mapping; + struct folio *folio = NULL; + u8 *kaddr = NULL; + struct restart_page_header *rstr1_ph = NULL; + struct restart_page_header *rstr2_ph = NULL; + int log_page_size, err; + bool logfile_is_empty = true; + u8 log_page_bits; + + ntfs_debug("Entering."); + /* An empty LogFile must have been clean before it got emptied. */ + if (NVolLogFileEmpty(vol)) + goto is_empty; + size = i_size_read(log_vi); + /* Make sure the file doesn't exceed the maximum allowed size. */ + if (size > MaxLogFileSize) + size = MaxLogFileSize; + /* + * Truncate size to a multiple of the page cache size or the default + * log page size if the page cache size is between the default log page + * log page size if the page cache size is between the default log page + * size and twice that. + */ + if (DefaultLogPageSize <= PAGE_SIZE && + DefaultLogPageSize * 2 <= PAGE_SIZE) + log_page_size = DefaultLogPageSize; + else + log_page_size = PAGE_SIZE; + /* + * Use ntfs_ffs() instead of ffs() to enable the compiler to + * optimize log_page_size and log_page_bits into constants. + */ + log_page_bits = ntfs_ffs(log_page_size) - 1; + size &= ~(s64)(log_page_size - 1); + /* + * Ensure the log file is big enough to store at least the two restart + * pages and the minimum number of log record pages. + */ + if (size < log_page_size * 2 || (size - log_page_size * 2) >> + log_page_bits < MinLogRecordPages) { + ntfs_error(vol->sb, "LogFile is too small."); + return false; + } + /* + * Read through the file looking for a restart page. Since the restart + * page header is at the beginning of a page we only need to search at + * what could be the beginning of a page (for each page size) rather + * than scanning the whole file byte by byte. If all potential places + * contain empty and uninitialzed records, the log file can be assumed + * to be empty. + */ + for (pos = 0; pos < size; pos <<= 1) { + pgoff_t idx = pos >> PAGE_SHIFT; + + if (!folio || folio->index != idx) { + if (folio) { + kunmap_local(kaddr); + folio_put(folio); + } + folio = read_mapping_folio(mapping, idx, NULL); + if (IS_ERR(folio)) { + ntfs_error(vol->sb, "Error mapping LogFile page (index %lu).", + idx); + goto err_out; + } + } + kaddr = (u8 *)kmap_local_folio(folio, 0) + (pos & ~PAGE_MASK); + /* + * A non-empty block means the logfile is not empty while an + * empty block after a non-empty block has been encountered + * means we are done. + */ + if (!ntfs_is_empty_recordp((__le32 *)kaddr)) + logfile_is_empty = false; + else if (!logfile_is_empty) + break; + /* + * A log record page means there cannot be a restart page after + * this so no need to continue searching. + */ + if (ntfs_is_rcrd_recordp((__le32 *)kaddr)) + break; + /* If not a (modified by chkdsk) restart page, continue. */ + if (!ntfs_is_rstr_recordp((__le32 *)kaddr) && + !ntfs_is_chkd_recordp((__le32 *)kaddr)) { + if (!pos) + pos = NTFS_BLOCK_SIZE >> 1; + continue; + } + /* + * Check the (modified by chkdsk) restart page for consistency + * and get a copy of the complete multi sector transfer + * deprotected restart page. + */ + err = ntfs_check_and_load_restart_page(log_vi, + (struct restart_page_header *)kaddr, pos, + !rstr1_ph ? &rstr1_ph : &rstr2_ph, + !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); + if (!err) { + /* + * If we have now found the first (modified by chkdsk) + * restart page, continue looking for the second one. + */ + if (!pos) { + pos = NTFS_BLOCK_SIZE >> 1; + continue; + } + /* + * We have now found the second (modified by chkdsk) + * restart page, so we can stop looking. + */ + break; + } + /* + * Error output already done inside the function. Note, we do + * not abort if the restart page was invalid as we might still + * find a valid one further in the file. + */ + if (err != -EINVAL) { + kunmap_local(kaddr); + folio_put(folio); + goto err_out; + } + /* Continue looking. */ + if (!pos) + pos = NTFS_BLOCK_SIZE >> 1; + } + if (folio) { + kunmap_local(kaddr); + folio_put(folio); + } + if (logfile_is_empty) { + NVolSetLogFileEmpty(vol); +is_empty: + ntfs_debug("Done. (LogFile is empty.)"); + return true; + } + if (!rstr1_ph) { + ntfs_error(vol->sb, + "Did not find any restart pages in LogFile and it was not empty."); + return false; + } + /* If both restart pages were found, use the more recent one. */ + if (rstr2_ph) { + /* + * If the second restart area is more recent, switch to it. + * Otherwise just throw it away. + */ + if (rstr2_lsn > rstr1_lsn) { + ntfs_debug("Using second restart page as it is more recent."); + kvfree(rstr1_ph); + rstr1_ph = rstr2_ph; + /* rstr1_lsn = rstr2_lsn; */ + } else { + ntfs_debug("Using first restart page as it is more recent."); + kvfree(rstr2_ph); + } + rstr2_ph = NULL; + } + /* All consistency checks passed. */ + if (rp) + *rp = rstr1_ph; + else + kvfree(rstr1_ph); + ntfs_debug("Done."); + return true; +err_out: + if (rstr1_ph) + kvfree(rstr1_ph); + return false; +} + +/* + * ntfs_empty_logfile - empty the contents of the LogFile journal + * @log_vi: struct inode of loaded journal LogFile to empty + * + * Empty the contents of the LogFile journal @log_vi and return 'true' on + * success and 'false' on error. + * + * This function assumes that the LogFile journal has already been consistency + * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean() + * has been used to ensure that the LogFile is clean. + */ +bool ntfs_empty_logfile(struct inode *log_vi) +{ + s64 vcn, end_vcn; + struct ntfs_inode *log_ni = NTFS_I(log_vi); + struct ntfs_volume *vol = log_ni->vol; + struct super_block *sb = vol->sb; + struct runlist_element *rl; + unsigned long flags; + int err; + bool should_wait = true; + char *empty_buf = NULL; + struct file_ra_state *ra = NULL; + + ntfs_debug("Entering."); + if (NVolLogFileEmpty(vol)) { + ntfs_debug("Done."); + return true; + } + + /* + * We cannot use ntfs_attr_set() because we may be still in the middle + * of a mount operation. Thus we do the emptying by hand by first + * zapping the page cache pages for the LogFile/DATA attribute and + * then emptying each of the buffers in each of the clusters specified + * by the runlist by hand. + */ + vcn = 0; + read_lock_irqsave(&log_ni->size_lock, flags); + end_vcn = (log_ni->initialized_size + vol->cluster_size_mask) >> + vol->cluster_size_bits; + read_unlock_irqrestore(&log_ni->size_lock, flags); + truncate_inode_pages(log_vi->i_mapping, 0); + down_write(&log_ni->runlist.lock); + rl = log_ni->runlist.rl; + if (unlikely(!rl || vcn < rl->vcn || !rl->length)) { +map_vcn: + err = ntfs_map_runlist_nolock(log_ni, vcn, NULL); + if (err) { + ntfs_error(sb, "Failed to map runlist fragment (error %d).", -err); + goto err; + } + rl = log_ni->runlist.rl; + } + /* Seek to the runlist element containing @vcn. */ + while (rl->length && vcn >= rl[1].vcn) + rl++; + + err = -ENOMEM; + empty_buf = kvzalloc(vol->cluster_size, GFP_NOFS); + if (!empty_buf) + goto err; + + memset(empty_buf, 0xff, vol->cluster_size); + + ra = kzalloc(sizeof(*ra), GFP_NOFS); + if (!ra) + goto err; + + file_ra_state_init(ra, sb->s_bdev->bd_mapping); + do { + s64 lcn; + loff_t start, end; + s64 len; + + /* + * If this run is not mapped map it now and start again as the + * runlist will have been updated. + */ + lcn = rl->lcn; + if (unlikely(lcn == LCN_RL_NOT_MAPPED)) { + vcn = rl->vcn; + kvfree(empty_buf); + goto map_vcn; + } + /* If this run is not valid abort with an error. */ + if (unlikely(!rl->length || lcn < LCN_HOLE)) + goto rl_err; + /* Skip holes. */ + if (lcn == LCN_HOLE) + continue; + start = NTFS_CLU_TO_B(vol, lcn); + len = rl->length; + if (rl[1].vcn > end_vcn) + len = end_vcn - rl->vcn; + end = NTFS_CLU_TO_B(vol, lcn + len); + + page_cache_sync_readahead(sb->s_bdev->bd_mapping, ra, NULL, + start >> PAGE_SHIFT, (end - start) >> PAGE_SHIFT); + + do { + err = ntfs_bdev_write(sb, empty_buf, start, + vol->cluster_size); + if (err) { + ntfs_error(sb, "ntfs_dev_write failed, err : %d\n", err); + goto io_err; + } + + /* + * Submit the buffer and wait for i/o to complete but + * only for the first buffer so we do not miss really + * serious i/o errors. Once the first buffer has + * completed ignore errors afterwards as we can assume + * that if one buffer worked all of them will work. + */ + if (should_wait) { + should_wait = false; + err = filemap_write_and_wait_range(sb->s_bdev->bd_mapping, + start, start + vol->cluster_size); + if (err) + goto io_err; + } + start += vol->cluster_size; + } while (start < end); + } while ((++rl)->vcn < end_vcn); + up_write(&log_ni->runlist.lock); + kfree(empty_buf); + kfree(ra); + truncate_inode_pages(log_vi->i_mapping, 0); + /* Set the flag so we do not have to do it again on remount. */ + NVolSetLogFileEmpty(vol); + ntfs_debug("Done."); + return true; +io_err: + ntfs_error(sb, "Failed to write buffer. Unmount and run chkdsk."); + goto dirty_err; +rl_err: + ntfs_error(sb, "Runlist is corrupt. Unmount and run chkdsk."); +dirty_err: + NVolSetErrors(vol); + err = -EIO; +err: + kvfree(empty_buf); + kfree(ra); + up_write(&log_ni->runlist.lock); + ntfs_error(sb, "Failed to fill LogFile with 0xff bytes (error %d).", + -err); + return false; +} diff --git a/fs/ntfs/logfile.h b/fs/ntfs/logfile.h new file mode 100644 index 000000000000..dadb56fd0b18 --- /dev/null +++ b/fs/ntfs/logfile.h @@ -0,0 +1,245 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for NTFS kernel journal (LogFile) handling. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + */ + +#ifndef _LINUX_NTFS_LOGFILE_H +#define _LINUX_NTFS_LOGFILE_H + +#include "layout.h" + +/* + * Journal (LogFile) organization: + * + * Two restart areas present in the first two pages (restart pages, one restart + * area in each page). When the volume is dismounted they should be identical, + * except for the update sequence array which usually has a different update + * sequence number. + * + * These are followed by log records organized in pages headed by a log record + * header going up to log file size. Not all pages contain log records when a + * volume is first formatted, but as the volume ages, all records will be used. + * When the log file fills up, the records at the beginning are purged (by + * modifying the oldest_lsn to a higher value presumably) and writing begins + * at the beginning of the file. Effectively, the log file is viewed as a + * circular entity. + * + * NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept + * versions <= 1.x, including 0.-1. (Yes, that is a minus one in there!) We + * probably only want to support 1.1 as this seems to be the current version + * and we don't know how that differs from the older versions. The only + * exception is if the journal is clean as marked by the two restart pages + * then it doesn't matter whether we are on an earlier version. We can just + * reinitialize the logfile and start again with version 1.1. + */ + +/* Some LogFile related constants. */ +#define MaxLogFileSize 0x100000000ULL +#define DefaultLogPageSize 4096 +#define MinLogRecordPages 48 + +/* + * Log file restart page header (begins the restart area). + * + * @magic: The magic is "RSTR". + * @usa_ofs: See ntfs_record struct definition in layout.h. When creating, + * set this to be immediately after this header structure (without any + * alignment). + * @usa_count: See ntfs_record struct definition in layout.h. + * @chkdsk_lsn: The last log file sequence number found by chkdsk. Only + * used when the magic is changed to "CHKD". Otherwise this is zero. + * @system_page_size: Byte size of system pages when the log file was + * created, has to be >= 512 and a power of 2. Use this to calculate + * the required size of the usa (usa_count) and add it to usa_ofs. Then + * verify that the result is less than the value of + * the restart_area_offset. + * @log_page_size: Byte size of log file pages, has to be >= 512 and + * a power of 2. The default is 4096 and is used when the system page + * size is between 4096 and 8192. Otherwise this is set to the system + * page size instead. + * @restart_area_offset: Byte offset from the start of this header to + * the RESTART_AREA. Value has to be aligned to 8-byte boundary. When + * creating, set this to be after the usa. + * @minor_ver: Log file minor version. Only check if major version is 1. + * @major_ver: Log file major version. We only support version 1.1. + */ +struct restart_page_header { + __le32 magic; + __le16 usa_ofs; + __le16 usa_count; + __le64 chkdsk_lsn; + __le32 system_page_size; + __le32 log_page_size; + __le16 restart_area_offset; + __le16 minor_ver; + __le16 major_ver; +} __packed; + +/* + * Constant for the log client indices meaning that there are no client records + * in this particular client array. Also inside the client records themselves, + * this means that there are no client records preceding or following this one. + */ +#define LOGFILE_NO_CLIENT cpu_to_le16(0xffff) +#define LOGFILE_NO_CLIENT_CPU 0xffff + +/* + * These are the so far known RESTART_AREA_* flags (16-bit) which contain + * information about the log file in which they are present. + * gcc: Force enum bit width to 16. + */ +enum { + RESTART_VOLUME_IS_CLEAN = cpu_to_le16(0x0002), + RESTART_SPACE_FILLER = cpu_to_le16(0xffff), +} __packed; + +/* + * Log file restart area record. The offset of this record is found by adding + * the offset of the RESTART_PAGE_HEADER to the restart_area_offset value found + * in it. See notes at restart_area_offset above. + * + * @current_lsn: The current, i.e. last LSN inside the log when + * the restart area was last written. This happens often but what is + * the interval? Is it just fixed time or is it every time a check point + * is written or somethine else? On create set to 0. + * @log_clients: Number of log client records in the array of log client + * records which follows this restart area. Must be 1. + * @client_free_list: The index of the first free log client record in + * the array of log client records. LOGFILE_NO_CLIENT means that there + * are no free log client records in the array. If != LOGFILE_NO_CLIENT, + * check that log_clients > client_free_list. On Win2k and presumably + * earlier, on a clean volume this is != LOGFILE_NO_CLIENT, and it should + * be 0, i.e. the first (and only) client record is free and thus + * the logfile is closed and hence clean. A dirty volume would have left + * the logfile open and hence this would be LOGFILE_NO_CLIENT. On WinXP + * and presumably later, the logfile is always open, even on clean + * shutdown so this should always be LOGFILE_NO_CLIENT. + * @client_in_use_list: The index of the first in-use log client record in + * the array of log client records. LOGFILE_NO_CLIENT means that there + * are no in-use log client records in the array. + * If != LOGFILE_NO_CLIENT check that log_clients > client_in_use_list. + * On Win2k and presumably earlier, on a clean volume this is + * LOGFILE_NO_CLIENT, i.e. there are no client records in use and thus + * the logfile is closed and hence clean. A dirty volume would have left + * the logfile open and hence this would be != LOGFILE_NO_CLIENT, and it + * should be 0, i.e. the first (and only) client record is in use. On + * WinXP and presumably later, the logfile is always open, even on clean + * shutdown so this should always be 0. + * @flags: Flags modifying LFS behaviour. On Win2k and presumably earlier + * this is always 0. On WinXP and presumably later, if the logfile was + * shutdown cleanly, the second bit, RESTART_VOLUME_IS_CLEAN, is set. + * This bit is cleared when the volume is mounted by WinXP and set when + * the volume is dismounted, thus if the logfile is dirty, this bit is + * clear. Thus we don't need to check the Windows version to determine + * if the logfile is clean. Instead if the logfile is closed, we know + * it must be clean. If it is open and this bit is set, we also know + * it must be clean. If on the other hand the logfile is open and this + * bit is clear, we can be almost certain that the logfile is dirty. + * @seq_number_bits: How many bits to use for the sequence number. This + * is calculated as 67 - the number of bits required to store the logfile + * size in bytes and this can be used in with the specified file_size as + * a consistency check. + * @restart_area_length: Length of the restart area including the client + * array. Following checks required if version matches. Otherwise, + * skip them. restart_area_offset + restart_area_length has to be + * <= system_page_size. Also, restart_area_length has to be >= + * client_array_offset + (log_clients * sizeof(log client record)). + * @client_array_offset: Offset from the start of this record to the first + * log client record if versions are matched. When creating, set this + * to be after this restart area structure, aligned to 8-bytes boundary. + * If the versions do not match, this is ignored and the offset is + * assumed to be (sizeof(RESTART_AREA) + 7) & ~7, i.e. rounded up to + * first 8-byte boundary. Either way, client_array_offset has to be + * aligned to an 8-byte boundary. Also, restart_area_offset + + * client_array_offset has to be <= 510. Finally, client_array_offset + + * (log_clients * sizeof(log client record)) has to be <= system_page_size. + * On Win2k and presumably earlier, this is 0x30, i.e. immediately + * following this record. On WinXP and presumably later, this is 0x40, + * i.e. there are 16 extra bytes between this record and the client + * array. This probably means that the RESTART_AREA record is actually + * bigger in WinXP and later. + * @file_size: Usable byte size of the log file. + * If the restart_area_offset + the offset of the file_size are > 510 + * then corruption has occurred. This is the very first check when + * starting with the restart_area as if it fails it means that some of + * the above values will be corrupted by the multi sector transfer + * protection. The file_size has to be rounded down to be a multiple + * of the log_page_size in the RESTART_PAGE_HEADER and then it has to be + * at least big enough to store the two restart pages and 48 (0x30) log + * record pages. + * @last_lsn_data_length: Length of data of last LSN, not including the log + * record header. On create set to 0. + * @log_record_header_length: Byte size of the log record header. If the + * version matches then check that the value of log_record_header_length + * is a multiple of 8, i.e. (log_record_header_length + 7) & ~7 == + * log_record_header_length. When creating set it to + * sizeof(LOG_RECORD_HEADER), aligned to 8 bytes. + * @log_page_data_offset: Offset to the start of data in a log record page. + * Must be a multiple of 8. On create set it to immediately after + * the update sequence array of the log record page. + * @restart_log_open_count: A counter that gets incremented every time + * the logfile is restarted which happens at mount time when the logfile + * is opened. When creating set to a random value. Win2k sets it to + * the low 32 bits of the current system time in NTFS format (see time.h). + * @reserved: Reserved/alignment to 8-byte boundary. + */ +struct restart_area { + __le64 current_lsn; + __le16 log_clients; + __le16 client_free_list; + __le16 client_in_use_list; + __le16 flags; + __le32 seq_number_bits; + __le16 restart_area_length; + __le16 client_array_offset; + __le64 file_size; + __le32 last_lsn_data_length; + __le16 log_record_header_length; + __le16 log_page_data_offset; + __le32 restart_log_open_count; + __le32 reserved; +} __packed; + +/* + * Log client record. The offset of this record is found by adding the offset + * of the RESTART_AREA to the client_array_offset value found in it. + * + * @oldest_lsn: Oldest LSN needed by this client. On create set to 0. + * @client_restart_lsn: LSN at which this client needs to restart + * the volume, i.e. the current position within the log file. + * At present, if clean this should = current_lsn in restart area but it + * probably also = current_lsn when dirty most of the time. + * At create set to 0. + * @prev_client: The offset to the previous log client record in the array + * of log client records. LOGFILE_NO_CLIENT means there is no previous + * client record, i.e. this is the first one. This is always + * LOGFILE_NO_CLIENT. + * @next_client: The offset to the next log client record in the array of + * log client records. LOGFILE_NO_CLIENT means there are no next client + * records, i.e. this is the last one. This is always LOGFILE_NO_CLIENT. + * @seq_number: On Win2k and presumably earlier, this is set to zero every + * time the logfile is restarted and it is incremented when the logfile + * is closed at dismount time. Thus it is 0 when dirty and 1 when clean. + * On WinXP and presumably later, this is always 0. + * @reserved[6]: Reserved/alignment. + * @client_name_length: Length of client name in bytes. Should always be 8. + * @client_name[64]: Name of the client in Unicode. Should always be "NTFS" + * with the remaining bytes set to 0. + */ +struct log_client_record { + __le64 oldest_lsn; + __le64 client_restart_lsn; + __le16 prev_client; + __le16 next_client; + __le16 seq_number; + u8 reserved[6]; + __le32 client_name_length; + __le16 client_name[64]; +} __packed; + +bool ntfs_check_logfile(struct inode *log_vi, + struct restart_page_header **rp); +bool ntfs_empty_logfile(struct inode *log_vi); +#endif /* _LINUX_NTFS_LOGFILE_H */ diff --git a/fs/ntfs/malloc.h b/fs/ntfs/malloc.h new file mode 100644 index 000000000000..7068425735f1 --- /dev/null +++ b/fs/ntfs/malloc.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * malloc.h - NTFS kernel memory handling. Part of the Linux-NTFS project. + * + * Copyright (c) 2001-2005 Anton Altaparmakov + */ + +#ifndef _LINUX_NTFS_MALLOC_H +#define _LINUX_NTFS_MALLOC_H + +#include +#include +#include + +/** + * __ntfs_malloc - allocate memory in multiples of pages + * @size: number of bytes to allocate + * @gfp_mask: extra flags for the allocator + * + * Internal function. You probably want ntfs_malloc_nofs()... + * + * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and + * returns a pointer to the allocated memory. + * + * If there was insufficient memory to complete the request, return NULL. + * Depending on @gfp_mask the allocation may be guaranteed to succeed. + */ +static inline void *__ntfs_malloc(unsigned long size, gfp_t gfp_mask) +{ + if (likely(size <= PAGE_SIZE)) { + BUG_ON(!size); + /* kmalloc() has per-CPU caches so is faster for now. */ + return kmalloc(PAGE_SIZE, gfp_mask & ~__GFP_HIGHMEM); + /* return (void *)__get_free_page(gfp_mask); */ + } + if (likely((size >> PAGE_SHIFT) < totalram_pages())) + return __vmalloc(size, gfp_mask); + return NULL; +} + +/** + * ntfs_malloc_nofs - allocate memory in multiples of pages + * @size: number of bytes to allocate + * + * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and + * returns a pointer to the allocated memory. + * + * If there was insufficient memory to complete the request, return NULL. + */ +static inline void *ntfs_malloc_nofs(unsigned long size) +{ + return __ntfs_malloc(size, GFP_NOFS | __GFP_HIGHMEM); +} + +/** + * ntfs_malloc_nofs_nofail - allocate memory in multiples of pages + * @size: number of bytes to allocate + * + * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and + * returns a pointer to the allocated memory. + * + * This function guarantees that the allocation will succeed. It will sleep + * for as long as it takes to complete the allocation. + * + * If there was insufficient memory to complete the request, return NULL. + */ +static inline void *ntfs_malloc_nofs_nofail(unsigned long size) +{ + return __ntfs_malloc(size, GFP_NOFS | __GFP_HIGHMEM | __GFP_NOFAIL); +} + +static inline void ntfs_free(void *addr) +{ + kvfree(addr); +} + +#endif /* _LINUX_NTFS_MALLOC_H */ diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c new file mode 100644 index 000000000000..7d989267a82b --- /dev/null +++ b/fs/ntfs/mft.c @@ -0,0 +1,2925 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel mft record operations. + * Part of this file is based on code from the NTFS-3G. + * + * Copyright (c) 2001-2012 Anton Altaparmakov and Tuxera Inc. + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "bitmap.h" +#include "lcnalloc.h" +#include "mft.h" +#include "ntfs.h" + +/* + * ntfs_mft_record_check - Check the consistency of an MFT record + * + * Make sure its general fields are safe, then examine all its + * attributes and apply generic checks to them. + * + * Returns 0 if the checks are successful. If not, return -EIO. + */ +int ntfs_mft_record_check(const struct ntfs_volume *vol, struct mft_record *m, + u64 mft_no) +{ + struct attr_record *a; + struct super_block *sb = vol->sb; + + if (!ntfs_is_file_record(m->magic)) { + ntfs_error(sb, "Record %llu has no FILE magic (0x%x)\n", + mft_no, le32_to_cpu(*(__le32 *)m)); + goto err_out; + } + + if (le16_to_cpu(m->usa_ofs) & 0x1 || + (vol->mft_record_size >> NTFS_BLOCK_SIZE_BITS) + 1 != le16_to_cpu(m->usa_count) || + le16_to_cpu(m->usa_ofs) + le16_to_cpu(m->usa_count) * 2 > vol->mft_record_size) { + ntfs_error(sb, "Record %llu has corrupt fix-up values fields\n", + mft_no); + goto err_out; + } + + if (le32_to_cpu(m->bytes_allocated) != vol->mft_record_size) { + ntfs_error(sb, "Record %llu has corrupt allocation size (%u <> %u)\n", + mft_no, vol->mft_record_size, + le32_to_cpu(m->bytes_allocated)); + goto err_out; + } + + if (le32_to_cpu(m->bytes_in_use) > vol->mft_record_size) { + ntfs_error(sb, "Record %llu has corrupt in-use size (%u > %u)\n", + mft_no, le32_to_cpu(m->bytes_in_use), + vol->mft_record_size); + goto err_out; + } + + if (le16_to_cpu(m->attrs_offset) & 7) { + ntfs_error(sb, "Attributes badly aligned in record %llu\n", + mft_no); + goto err_out; + } + + a = (struct attr_record *)((char *)m + le16_to_cpu(m->attrs_offset)); + if ((char *)a < (char *)m || (char *)a > (char *)m + vol->mft_record_size) { + ntfs_error(sb, "Record %llu is corrupt\n", mft_no); + goto err_out; + } + + return 0; + +err_out: + return -EIO; +} + +/* + * map_mft_record_folio - map the folio in which a specific mft record resides + * @ni: ntfs inode whose mft record page to map + * + * This maps the folio in which the mft record of the ntfs inode @ni is + * situated. + * + * This allocates a new buffer (@ni->mrec), copies the MFT record data from + * the mapped folio into this buffer, and applies the MST (Multi Sector + * Transfer) fixups on the copy. + * + * The folio is pinned (referenced) in @ni->folio to ensure the data remains + * valid in the page cache, but the returned pointer is the allocated copy. + * + * Return: A pointer to the allocated and fixed-up mft record (@ni->mrec). + * The return value needs to be checked with IS_ERR(). If it is true, + * PTR_ERR() contains the negative error code. + */ +static inline struct mft_record *map_mft_record_folio(struct ntfs_inode *ni) +{ + loff_t i_size; + struct ntfs_volume *vol = ni->vol; + struct inode *mft_vi = vol->mft_ino; + struct folio *folio; + unsigned long index, end_index; + unsigned int ofs; + + WARN_ON(ni->folio); + /* + * The index into the page cache and the offset within the page cache + * page of the wanted mft record. + */ + index = NTFS_MFT_NR_TO_PIDX(vol, ni->mft_no); + ofs = NTFS_MFT_NR_TO_POFS(vol, ni->mft_no); + + i_size = i_size_read(mft_vi); + /* The maximum valid index into the page cache for $MFT's data. */ + end_index = i_size >> PAGE_SHIFT; + + /* If the wanted index is out of bounds the mft record doesn't exist. */ + if (unlikely(index >= end_index)) { + if (index > end_index || (i_size & ~PAGE_MASK) < ofs + + vol->mft_record_size) { + folio = ERR_PTR(-ENOENT); + ntfs_error(vol->sb, + "Attempt to read mft record 0x%llx, which is beyond the end of the mft. This is probably a bug in the ntfs driver.", + ni->mft_no); + goto err_out; + } + } + + /* Read, map, and pin the folio. */ + folio = read_mapping_folio(mft_vi->i_mapping, index, NULL); + if (!IS_ERR(folio)) { + u8 *addr; + + ni->mrec = kmalloc(vol->mft_record_size, GFP_NOFS); + if (!ni->mrec) { + folio_put(folio); + folio = ERR_PTR(-ENOMEM); + goto err_out; + } + + addr = kmap_local_folio(folio, 0); + memcpy(ni->mrec, addr + ofs, vol->mft_record_size); + post_read_mst_fixup((struct ntfs_record *)ni->mrec, vol->mft_record_size); + + /* Catch multi sector transfer fixup errors. */ + if (!ntfs_mft_record_check(vol, (struct mft_record *)ni->mrec, ni->mft_no)) { + kunmap_local(addr); + ni->folio = folio; + ni->folio_ofs = ofs; + return ni->mrec; + } + kunmap_local(addr); + folio_put(folio); + kfree(ni->mrec); + ni->mrec = NULL; + folio = ERR_PTR(-EIO); + NVolSetErrors(vol); + } +err_out: + ni->folio = NULL; + ni->folio_ofs = 0; + return (struct mft_record *)folio; +} + +/* + * map_mft_record - map and pin an mft record + * @ni: ntfs inode whose MFT record to map + * + * This function ensures the MFT record for the given inode is mapped and + * accessible. + * + * It increments the reference count of the ntfs inode. If the record is + * already mapped (@ni->folio is set), it returns the cached record + * immediately. + * + * Otherwise, it calls map_mft_record_folio() to read the folio from disk + * (if necessary via read_mapping_folio), allocate a buffer, and copy the + * record data. + * + * Return: A pointer to the mft record. You need to check the returned + * pointer with IS_ERR(). + */ +struct mft_record *map_mft_record(struct ntfs_inode *ni) +{ + struct mft_record *m; + + if (!ni) + return ERR_PTR(-EINVAL); + + ntfs_debug("Entering for mft_no 0x%llx.", ni->mft_no); + + /* Make sure the ntfs inode doesn't go away. */ + atomic_inc(&ni->count); + + if (ni->folio) + return (struct mft_record *)ni->mrec; + + m = map_mft_record_folio(ni); + if (!IS_ERR(m)) + return m; + + atomic_dec(&ni->count); + ntfs_error(ni->vol->sb, "Failed with error code %lu.", -PTR_ERR(m)); + return m; +} + +/* + * unmap_mft_record - release a reference to a mapped mft record + * @ni: ntfs inode whose MFT record to unmap + * + * This decrements the reference count of the ntfs inode. + * + * It releases the caller's hold on the inode. If the reference count indicates + * that there are still other users (count > 1), the function returns + * immediately, keeping the resources (folio and mrec buffer) pinned for + * those users. + * + * NOTE: If caller has modified the mft record, it is imperative to set the mft + * record dirty BEFORE calling unmap_mft_record(). + */ +void unmap_mft_record(struct ntfs_inode *ni) +{ + struct folio *folio; + + if (!ni) + return; + + ntfs_debug("Entering for mft_no 0x%llx.", ni->mft_no); + + folio = ni->folio; + if (atomic_dec_return(&ni->count) > 1) + return; + WARN_ON(!folio); +} + +/* + * map_extent_mft_record - load an extent inode and attach it to its base + * @base_ni: base ntfs inode + * @mref: mft reference of the extent inode to load + * @ntfs_ino: on successful return, pointer to the struct ntfs_inode structure + * + * Load the extent mft record @mref and attach it to its base inode @base_ni. + * Return the mapped extent mft record if IS_ERR(result) is false. Otherwise + * PTR_ERR(result) gives the negative error code. + * + * On successful return, @ntfs_ino contains a pointer to the ntfs_inode + * structure of the mapped extent inode. + */ +struct mft_record *map_extent_mft_record(struct ntfs_inode *base_ni, u64 mref, + struct ntfs_inode **ntfs_ino) +{ + struct mft_record *m; + struct ntfs_inode *ni = NULL; + struct ntfs_inode **extent_nis = NULL; + int i; + u64 mft_no = MREF(mref); + u16 seq_no = MSEQNO(mref); + bool destroy_ni = false; + + ntfs_debug("Mapping extent mft record 0x%llx (base mft record 0x%llx).", + mft_no, base_ni->mft_no); + /* Make sure the base ntfs inode doesn't go away. */ + atomic_inc(&base_ni->count); + /* + * Check if this extent inode has already been added to the base inode, + * in which case just return it. If not found, add it to the base + * inode before returning it. + */ +retry: + mutex_lock(&base_ni->extent_lock); + if (base_ni->nr_extents > 0) { + extent_nis = base_ni->ext.extent_ntfs_inos; + for (i = 0; i < base_ni->nr_extents; i++) { + if (mft_no != extent_nis[i]->mft_no) + continue; + ni = extent_nis[i]; + /* Make sure the ntfs inode doesn't go away. */ + atomic_inc(&ni->count); + break; + } + } + if (likely(ni != NULL)) { + mutex_unlock(&base_ni->extent_lock); + atomic_dec(&base_ni->count); + /* We found the record; just have to map and return it. */ + m = map_mft_record(ni); + /* map_mft_record() has incremented this on success. */ + atomic_dec(&ni->count); + if (!IS_ERR(m)) { + /* Verify the sequence number. */ + if (likely(le16_to_cpu(m->sequence_number) == seq_no)) { + ntfs_debug("Done 1."); + *ntfs_ino = ni; + return m; + } + unmap_mft_record(ni); + ntfs_error(base_ni->vol->sb, + "Found stale extent mft reference! Corrupt filesystem. Run chkdsk."); + return ERR_PTR(-EIO); + } +map_err_out: + ntfs_error(base_ni->vol->sb, + "Failed to map extent mft record, error code %ld.", + -PTR_ERR(m)); + return m; + } + mutex_unlock(&base_ni->extent_lock); + + /* Record wasn't there. Get a new ntfs inode and initialize it. */ + ni = ntfs_new_extent_inode(base_ni->vol->sb, mft_no); + if (unlikely(!ni)) { + atomic_dec(&base_ni->count); + return ERR_PTR(-ENOMEM); + } + ni->vol = base_ni->vol; + ni->seq_no = seq_no; + ni->nr_extents = -1; + ni->ext.base_ntfs_ino = base_ni; + /* Now map the record. */ + m = map_mft_record(ni); + if (IS_ERR(m)) { + atomic_dec(&base_ni->count); + ntfs_clear_extent_inode(ni); + goto map_err_out; + } + /* Verify the sequence number if it is present. */ + if (seq_no && (le16_to_cpu(m->sequence_number) != seq_no)) { + ntfs_error(base_ni->vol->sb, + "Found stale extent mft reference! Corrupt filesystem. Run chkdsk."); + destroy_ni = true; + m = ERR_PTR(-EIO); + goto unm_nolock_err_out; + } + + mutex_lock(&base_ni->extent_lock); + for (i = 0; i < base_ni->nr_extents; i++) { + if (mft_no == extent_nis[i]->mft_no) { + mutex_unlock(&base_ni->extent_lock); + ntfs_clear_extent_inode(ni); + goto retry; + } + } + /* Attach extent inode to base inode, reallocating memory if needed. */ + if (!(base_ni->nr_extents & 3)) { + struct ntfs_inode **tmp; + int new_size = (base_ni->nr_extents + 4) * sizeof(struct ntfs_inode *); + + tmp = kvzalloc(new_size, GFP_NOFS); + if (unlikely(!tmp)) { + ntfs_error(base_ni->vol->sb, "Failed to allocate internal buffer."); + destroy_ni = true; + m = ERR_PTR(-ENOMEM); + goto unm_err_out; + } + if (base_ni->nr_extents) { + WARN_ON(!base_ni->ext.extent_ntfs_inos); + memcpy(tmp, base_ni->ext.extent_ntfs_inos, new_size - + 4 * sizeof(struct ntfs_inode *)); + kvfree(base_ni->ext.extent_ntfs_inos); + } + base_ni->ext.extent_ntfs_inos = tmp; + } + base_ni->ext.extent_ntfs_inos[base_ni->nr_extents++] = ni; + mutex_unlock(&base_ni->extent_lock); + atomic_dec(&base_ni->count); + ntfs_debug("Done 2."); + *ntfs_ino = ni; + return m; +unm_err_out: + mutex_unlock(&base_ni->extent_lock); +unm_nolock_err_out: + unmap_mft_record(ni); + atomic_dec(&base_ni->count); + /* + * If the extent inode was not attached to the base inode we need to + * release it or we will leak memory. + */ + if (destroy_ni) + ntfs_clear_extent_inode(ni); + return m; +} + +/* + * __mark_mft_record_dirty - mark the base vfs inode dirty + * @ni: ntfs inode describing the mapped mft record + * + * Internal function. Users should call mark_mft_record_dirty() instead. + * + * This function determines the base ntfs inode (in case @ni is an extent + * inode) and marks the corresponding VFS inode dirty. + * + * NOTE: We only set I_DIRTY_DATASYNC (and not I_DIRTY_PAGES) + * on the base vfs inode, because even though file data may have been modified, + * it is dirty in the inode meta data rather than the data page cache of the + * inode, and thus there are no data pages that need writing out. Therefore, a + * full mark_inode_dirty() is overkill. A mark_inode_dirty_sync(), on the + * other hand, is not sufficient, because ->write_inode needs to be called even + * in case of fdatasync. This needs to happen or the file data would not + * necessarily hit the device synchronously, even though the vfs inode has the + * O_SYNC flag set. Also, I_DIRTY_DATASYNC simply "feels" better than just + * I_DIRTY_SYNC, since the file data has not actually hit the block device yet, + * which is not what I_DIRTY_SYNC on its own would suggest. + */ +void __mark_mft_record_dirty(struct ntfs_inode *ni) +{ + struct ntfs_inode *base_ni; + + ntfs_debug("Entering for inode 0x%llx.", ni->mft_no); + WARN_ON(NInoAttr(ni)); + /* Determine the base vfs inode and mark it dirty, too. */ + if (likely(ni->nr_extents >= 0)) + base_ni = ni; + else + base_ni = ni->ext.base_ntfs_ino; + __mark_inode_dirty(VFS_I(base_ni), I_DIRTY_DATASYNC); +} + +/* + * ntfs_bio_end_io - bio completion callback for MFT record writes + * + * Decrements the folio reference count that was incremented before + * submit_bio(). This prevents a race condition where umount could + * evict the inode and release the folio while I/O is still in flight, + * potentially causing data corruption or use-after-free. + */ +static void ntfs_bio_end_io(struct bio *bio) +{ + if (bio->bi_private) + folio_put((struct folio *)bio->bi_private); + bio_put(bio); +} + +/* + * ntfs_sync_mft_mirror - synchronize an mft record to the mft mirror + * @vol: ntfs volume on which the mft record to synchronize resides + * @mft_no: mft record number of mft record to synchronize + * @m: mapped, mst protected (extent) mft record to synchronize + * + * Write the mapped, mst protected (extent) mft record @m with mft record + * number @mft_no to the mft mirror ($MFTMirr) of the ntfs volume @vol. + * + * On success return 0. On error return -errno and set the volume errors flag + * in the ntfs volume @vol. + * + * NOTE: We always perform synchronous i/o. + */ +int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no, + struct mft_record *m) +{ + u8 *kmirr = NULL; + struct folio *folio; + unsigned int folio_ofs, lcn_folio_off = 0; + int err = 0; + struct bio *bio; + + ntfs_debug("Entering for inode 0x%llx.", mft_no); + + if (unlikely(!vol->mftmirr_ino)) { + /* This could happen during umount... */ + err = -EIO; + goto err_out; + } + /* Get the page containing the mirror copy of the mft record @m. */ + folio = read_mapping_folio(vol->mftmirr_ino->i_mapping, + NTFS_MFT_NR_TO_PIDX(vol, mft_no), NULL); + if (IS_ERR(folio)) { + ntfs_error(vol->sb, "Failed to map mft mirror page."); + err = PTR_ERR(folio); + goto err_out; + } + + folio_lock(folio); + folio_clear_uptodate(folio); + /* Offset of the mft mirror record inside the page. */ + folio_ofs = NTFS_MFT_NR_TO_POFS(vol, mft_no); + /* The address in the page of the mirror copy of the mft record @m. */ + kmirr = kmap_local_folio(folio, 0) + folio_ofs; + /* Copy the mst protected mft record to the mirror. */ + memcpy(kmirr, m, vol->mft_record_size); + + if (vol->cluster_size_bits > PAGE_SHIFT) { + lcn_folio_off = folio->index << PAGE_SHIFT; + lcn_folio_off &= vol->cluster_size_mask; + } + + bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO); + bio->bi_iter.bi_sector = + NTFS_B_TO_SECTOR(vol, NTFS_CLU_TO_B(vol, vol->mftmirr_lcn) + + lcn_folio_off + folio_ofs); + + if (!bio_add_folio(bio, folio, vol->mft_record_size, folio_ofs)) { + err = -EIO; + bio_put(bio); + goto unlock_folio; + } + + bio->bi_end_io = ntfs_bio_end_io; + submit_bio(bio); + /* Current state: all buffers are clean, unlocked, and uptodate. */ + folio_mark_uptodate(folio); + +unlock_folio: + folio_unlock(folio); + kunmap_local(kmirr); + folio_put(folio); + if (likely(!err)) { + ntfs_debug("Done."); + } else { + ntfs_error(vol->sb, "I/O error while writing mft mirror record 0x%llx!", mft_no); +err_out: + ntfs_error(vol->sb, + "Failed to synchronize $MFTMirr (error code %i). Volume will be left marked dirty on umount. Run chkdsk on the partition after umounting to correct this.", + err); + NVolSetErrors(vol); + } + return err; +} + +/* + * write_mft_record_nolock - write out a mapped (extent) mft record + * @ni: ntfs inode describing the mapped (extent) mft record + * @m: mapped (extent) mft record to write + * @sync: if true, wait for i/o completion + * + * Write the mapped (extent) mft record @m described by the (regular or extent) + * ntfs inode @ni to backing store. If the mft record @m has a counterpart in + * the mft mirror, that is also updated. + * + * We only write the mft record if the ntfs inode @ni is dirty. + * + * On success, clean the mft record and return 0. + * On error (specifically ENOMEM), we redirty the record so it can be retried. + * For other errors, we mark the volume with errors. + */ +int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int sync) +{ + struct ntfs_volume *vol = ni->vol; + struct folio *folio = ni->folio; + int err = 0, i = 0; + u8 *kaddr; + struct mft_record *fixup_m; + struct bio *bio; + unsigned int offset = 0, folio_size; + + ntfs_debug("Entering for inode 0x%llx.", ni->mft_no); + + WARN_ON(NInoAttr(ni)); + WARN_ON(!folio_test_locked(folio)); + + /* + * If the struct ntfs_inode is clean no need to do anything. If it is dirty, + * mark it as clean now so that it can be redirtied later on if needed. + * There is no danger of races since the caller is holding the locks + * for the mft record @m and the page it is in. + */ + if (!NInoTestClearDirty(ni)) + goto done; + + kaddr = kmap_local_folio(folio, 0); + fixup_m = (struct mft_record *)(kaddr + ni->folio_ofs); + memcpy(fixup_m, m, vol->mft_record_size); + + /* Apply the mst protection fixups. */ + err = pre_write_mst_fixup((struct ntfs_record *)fixup_m, vol->mft_record_size); + if (err) { + ntfs_error(vol->sb, "Failed to apply mst fixups!"); + goto err_out; + } + + folio_size = vol->mft_record_size / ni->mft_lcn_count; + while (i < ni->mft_lcn_count) { + unsigned int clu_off; + + clu_off = (unsigned int)((s64)ni->mft_no * vol->mft_record_size + offset) & + vol->cluster_size_mask; + + bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO); + bio->bi_iter.bi_sector = + NTFS_B_TO_SECTOR(vol, NTFS_CLU_TO_B(vol, ni->mft_lcn[i]) + + clu_off); + + if (!bio_add_folio(bio, folio, folio_size, + ni->folio_ofs + offset)) { + err = -EIO; + goto put_bio_out; + } + + /* Synchronize the mft mirror now if not @sync. */ + if (!sync && ni->mft_no < vol->mftmirr_size) + ntfs_sync_mft_mirror(vol, ni->mft_no, fixup_m); + + folio_get(folio); + bio->bi_private = folio; + bio->bi_end_io = ntfs_bio_end_io; + submit_bio(bio); + offset += vol->cluster_size; + i++; + } + + /* If @sync, now synchronize the mft mirror. */ + if (sync && ni->mft_no < vol->mftmirr_size) + ntfs_sync_mft_mirror(vol, ni->mft_no, fixup_m); + kunmap_local(kaddr); + if (unlikely(err)) { + /* I/O error during writing. This is really bad! */ + ntfs_error(vol->sb, + "I/O error while writing mft record 0x%llx! Marking base inode as bad. You should unmount the volume and run chkdsk.", + ni->mft_no); + goto err_out; + } +done: + ntfs_debug("Done."); + return 0; +put_bio_out: + bio_put(bio); +err_out: + /* + * Current state: all buffers are clean, unlocked, and uptodate. + * The caller should mark the base inode as bad so that no more i/o + * happens. ->drop_inode() will still be invoked so all extent inodes + * and other allocated memory will be freed. + */ + if (err == -ENOMEM) { + ntfs_error(vol->sb, + "Not enough memory to write mft record. Redirtying so the write is retried later."); + mark_mft_record_dirty(ni); + err = 0; + } else + NVolSetErrors(vol); + return err; +} + +static int ntfs_test_inode_wb(struct inode *vi, u64 ino, void *data) +{ + struct ntfs_attr *na = data; + + if (!ntfs_test_inode(vi, na)) + return 0; + + /* + * Without this, ntfs_write_mst_block() could call iput_final() + * , and ntfs_evict_big_inode() could try to unlink this inode + * and the contex could be blocked infinitly in map_mft_record(). + */ + if (NInoBeingDeleted(NTFS_I(vi))) { + na->state = NI_BeingDeleted; + return -1; + } + + /* + * This condition can prevent ntfs_write_mst_block() + * from applying/undo fixups while ntfs_create() being + * called + */ + spin_lock(&vi->i_lock); + if (inode_state_read_once(vi) & I_CREATING) { + spin_unlock(&vi->i_lock); + na->state = NI_BeingCreated; + return -1; + } + spin_unlock(&vi->i_lock); + + return igrab(vi) ? 1 : -1; +} + +/* + * ntfs_may_write_mft_record - check if an mft record may be written out + * @vol: [IN] ntfs volume on which the mft record to check resides + * @mft_no: [IN] mft record number of the mft record to check + * @m: [IN] mapped mft record to check + * @locked_ni: [OUT] caller has to unlock this ntfs inode if one is returned + * @ref_vi: [OUT] caller has to drop this vfs inode if one is returned + * + * Check if the mapped (base or extent) mft record @m with mft record number + * @mft_no belonging to the ntfs volume @vol may be written out. If necessary + * and possible the ntfs inode of the mft record is locked and the base vfs + * inode is pinned. The locked ntfs inode is then returned in @locked_ni. The + * caller is responsible for unlocking the ntfs inode and unpinning the base + * vfs inode. + * + * To avoid deadlock when the caller holds a folio lock, if the function + * returns @ref_vi it defers dropping the vfs inode reference by returning + * it in @ref_vi instead of calling iput() directly. The caller must call + * iput() on @ref_vi after releasing the folio lock. + * + * Return 'true' if the mft record may be written out and 'false' if not. + * + * The caller has locked the page and cleared the uptodate flag on it which + * means that we can safely write out any dirty mft records that do not have + * their inodes in icache as determined by find_inode_nowait(). + * + * Here is a description of the tests we perform: + * + * If the inode is found in icache we know the mft record must be a base mft + * record. If it is dirty, we do not write it and return 'false' as the vfs + * inode write paths will result in the access times being updated which would + * cause the base mft record to be redirtied and written out again. + * + * If the inode is in icache and not dirty, we attempt to lock the mft record + * and if we find the lock was already taken, it is not safe to write the mft + * record and we return 'false'. + * + * If we manage to obtain the lock we have exclusive access to the mft record, + * which also allows us safe writeout of the mft record. We then set + * @locked_ni to the locked ntfs inode and return 'true'. + * + * Note we cannot just lock the mft record and sleep while waiting for the lock + * because this would deadlock due to lock reversal. + * + * If the inode is not in icache we need to perform further checks. + * + * If the mft record is not a FILE record or it is a base mft record, we can + * safely write it and return 'true'. + * + * We now know the mft record is an extent mft record. We check if the inode + * corresponding to its base mft record is in icache. If it is not, we cannot + * safely determine the state of the extent inode, so we return 'false'. + * + * We now have the base inode for the extent mft record. We check if it has an + * ntfs inode for the extent mft record attached. If not, it is safe to write + * the extent mft record and we return 'true'. + * + * If the extent inode is attached, we check if it is dirty. If so, we return + * 'false' (letting the standard write_inode path handle it). + * + * If it is not dirty, we attempt to lock the extent mft record. If the lock + * was already taken, it is not safe to write and we return 'false'. + * + * If we manage to obtain the lock we have exclusive access to the extent mft + * record. We set @locked_ni to the now locked ntfs inode and return 'true'. + */ +static bool ntfs_may_write_mft_record(struct ntfs_volume *vol, const u64 mft_no, + const struct mft_record *m, struct ntfs_inode **locked_ni, + struct inode **ref_vi) +{ + struct super_block *sb = vol->sb; + struct inode *mft_vi = vol->mft_ino; + struct inode *vi; + struct ntfs_inode *ni, *eni, **extent_nis; + int i; + struct ntfs_attr na = {0}; + + ntfs_debug("Entering for inode 0x%llx.", mft_no); + /* + * Normally we do not return a locked inode so set @locked_ni to NULL. + */ + *locked_ni = NULL; + *ref_vi = NULL; + + /* + * Check if the inode corresponding to this mft record is in the VFS + * inode cache and obtain a reference to it if it is. + */ + ntfs_debug("Looking for inode 0x%llx in icache.", mft_no); + na.mft_no = mft_no; + na.type = AT_UNUSED; + /* + * Optimize inode 0, i.e. $MFT itself, since we have it in memory and + * we get here for it rather often. + */ + if (!mft_no) { + /* Balance the below iput(). */ + vi = igrab(mft_vi); + WARN_ON(vi != mft_vi); + } else { + /* + * Have to use find_inode_nowait() since ilookup5_nowait() + * waits for inode with I_FREEING, which causes ntfs to deadlock + * when inodes are unlinked concurrently + */ + vi = find_inode_nowait(sb, mft_no, ntfs_test_inode_wb, &na); + if (na.state == NI_BeingDeleted || na.state == NI_BeingCreated) + return false; + } + if (vi) { + ntfs_debug("Base inode 0x%llx is in icache.", mft_no); + /* The inode is in icache. */ + ni = NTFS_I(vi); + /* Take a reference to the ntfs inode. */ + atomic_inc(&ni->count); + /* If the inode is dirty, do not write this record. */ + if (NInoDirty(ni)) { + ntfs_debug("Inode 0x%llx is dirty, do not write it.", + mft_no); + atomic_dec(&ni->count); + *ref_vi = vi; + return false; + } + ntfs_debug("Inode 0x%llx is not dirty.", mft_no); + /* The inode is not dirty, try to take the mft record lock. */ + if (unlikely(!mutex_trylock(&ni->mrec_lock))) { + ntfs_debug("Mft record 0x%llx is already locked, do not write it.", mft_no); + atomic_dec(&ni->count); + *ref_vi = vi; + return false; + } + ntfs_debug("Managed to lock mft record 0x%llx, write it.", + mft_no); + /* + * The write has to occur while we hold the mft record lock so + * return the locked ntfs inode. + */ + *locked_ni = ni; + return true; + } + ntfs_debug("Inode 0x%llx is not in icache.", mft_no); + /* The inode is not in icache. */ + /* Write the record if it is not a mft record (type "FILE"). */ + if (!ntfs_is_mft_record(m->magic)) { + ntfs_debug("Mft record 0x%llx is not a FILE record, write it.", + mft_no); + return true; + } + /* Write the mft record if it is a base inode. */ + if (!m->base_mft_record) { + ntfs_debug("Mft record 0x%llx is a base record, write it.", + mft_no); + return true; + } + /* + * This is an extent mft record. Check if the inode corresponding to + * its base mft record is in icache and obtain a reference to it if it + * is. + */ + na.mft_no = MREF_LE(m->base_mft_record); + na.state = 0; + ntfs_debug("Mft record 0x%llx is an extent record. Looking for base inode 0x%llx in icache.", + mft_no, na.mft_no); + if (!na.mft_no) { + /* Balance the below iput(). */ + vi = igrab(mft_vi); + WARN_ON(vi != mft_vi); + } else { + vi = find_inode_nowait(sb, mft_no, ntfs_test_inode_wb, &na); + if (na.state == NI_BeingDeleted || na.state == NI_BeingCreated) + return false; + } + + if (!vi) + return false; + ntfs_debug("Base inode 0x%llx is in icache.", na.mft_no); + /* + * The base inode is in icache. Check if it has the extent inode + * corresponding to this extent mft record attached. + */ + ni = NTFS_I(vi); + mutex_lock(&ni->extent_lock); + if (ni->nr_extents <= 0) { + /* + * The base inode has no attached extent inodes, write this + * extent mft record. + */ + mutex_unlock(&ni->extent_lock); + *ref_vi = vi; + ntfs_debug("Base inode 0x%llx has no attached extent inodes, write the extent record.", + na.mft_no); + return true; + } + /* Iterate over the attached extent inodes. */ + extent_nis = ni->ext.extent_ntfs_inos; + for (eni = NULL, i = 0; i < ni->nr_extents; ++i) { + if (mft_no == extent_nis[i]->mft_no) { + /* + * Found the extent inode corresponding to this extent + * mft record. + */ + eni = extent_nis[i]; + break; + } + } + /* + * If the extent inode was not attached to the base inode, write this + * extent mft record. + */ + if (!eni) { + mutex_unlock(&ni->extent_lock); + *ref_vi = vi; + ntfs_debug("Extent inode 0x%llx is not attached to its base inode 0x%llx, write the extent record.", + mft_no, na.mft_no); + return true; + } + ntfs_debug("Extent inode 0x%llx is attached to its base inode 0x%llx.", + mft_no, na.mft_no); + /* Take a reference to the extent ntfs inode. */ + atomic_inc(&eni->count); + mutex_unlock(&ni->extent_lock); + + /* if extent inode is dirty, write_inode will write it */ + if (NInoDirty(eni)) { + atomic_dec(&eni->count); + *ref_vi = vi; + return false; + } + + /* + * Found the extent inode coresponding to this extent mft record. + * Try to take the mft record lock. + */ + if (unlikely(!mutex_trylock(&eni->mrec_lock))) { + atomic_dec(&eni->count); + *ref_vi = vi; + ntfs_debug("Extent mft record 0x%llx is already locked, do not write it.", + mft_no); + return false; + } + ntfs_debug("Managed to lock extent mft record 0x%llx, write it.", + mft_no); + /* + * The write has to occur while we hold the mft record lock so return + * the locked extent ntfs inode. + */ + *locked_ni = eni; + return true; +} + +static const char *es = " Leaving inconsistent metadata. Unmount and run chkdsk."; + +#define RESERVED_MFT_RECORDS 64 + +/* + * ntfs_mft_bitmap_find_and_alloc_free_rec_nolock - see name + * @vol: volume on which to search for a free mft record + * @base_ni: open base inode if allocating an extent mft record or NULL + * + * Search for a free mft record in the mft bitmap attribute on the ntfs volume + * @vol. + * + * If @base_ni is NULL start the search at the default allocator position. + * + * If @base_ni is not NULL start the search at the mft record after the base + * mft record @base_ni. + * + * Return the free mft record on success and -errno on error. An error code of + * -ENOSPC means that there are no free mft records in the currently + * initialized mft bitmap. + * + * Locking: Caller must hold vol->mftbmp_lock for writing. + */ +static s64 ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(struct ntfs_volume *vol, + struct ntfs_inode *base_ni) +{ + s64 pass_end, ll, data_pos, pass_start, ofs, bit; + unsigned long flags; + struct address_space *mftbmp_mapping; + u8 *buf = NULL, *byte; + struct folio *folio; + unsigned int folio_ofs, size; + u8 pass, b; + + ntfs_debug("Searching for free mft record in the currently initialized mft bitmap."); + mftbmp_mapping = vol->mftbmp_ino->i_mapping; + /* + * Set the end of the pass making sure we do not overflow the mft + * bitmap. + */ + read_lock_irqsave(&NTFS_I(vol->mft_ino)->size_lock, flags); + pass_end = NTFS_I(vol->mft_ino)->allocated_size >> + vol->mft_record_size_bits; + read_unlock_irqrestore(&NTFS_I(vol->mft_ino)->size_lock, flags); + read_lock_irqsave(&NTFS_I(vol->mftbmp_ino)->size_lock, flags); + ll = NTFS_I(vol->mftbmp_ino)->initialized_size << 3; + read_unlock_irqrestore(&NTFS_I(vol->mftbmp_ino)->size_lock, flags); + if (pass_end > ll) + pass_end = ll; + pass = 1; + if (!base_ni) + data_pos = vol->mft_data_pos; + else + data_pos = base_ni->mft_no + 1; + if (data_pos < RESERVED_MFT_RECORDS) + data_pos = RESERVED_MFT_RECORDS; + if (data_pos >= pass_end) { + data_pos = RESERVED_MFT_RECORDS; + pass = 2; + /* This happens on a freshly formatted volume. */ + if (data_pos >= pass_end) + return -ENOSPC; + } + + if (base_ni && base_ni->mft_no == FILE_MFT) { + data_pos = 0; + pass = 2; + } + + pass_start = data_pos; + ntfs_debug("Starting bitmap search: pass %u, pass_start 0x%llx, pass_end 0x%llx, data_pos 0x%llx.", + pass, pass_start, pass_end, data_pos); + /* Loop until a free mft record is found. */ + for (; pass <= 2;) { + /* Cap size to pass_end. */ + ofs = data_pos >> 3; + folio_ofs = ofs & ~PAGE_MASK; + size = PAGE_SIZE - folio_ofs; + ll = ((pass_end + 7) >> 3) - ofs; + if (size > ll) + size = ll; + size <<= 3; + /* + * If we are still within the active pass, search the next page + * for a zero bit. + */ + if (size) { + folio = read_mapping_folio(mftbmp_mapping, + ofs >> PAGE_SHIFT, NULL); + if (IS_ERR(folio)) { + ntfs_error(vol->sb, "Failed to read mft bitmap, aborting."); + return PTR_ERR(folio); + } + folio_lock(folio); + buf = (u8 *)kmap_local_folio(folio, 0) + folio_ofs; + bit = data_pos & 7; + data_pos &= ~7ull; + ntfs_debug("Before inner for loop: size 0x%x, data_pos 0x%llx, bit 0x%llx", + size, data_pos, bit); + for (; bit < size && data_pos + bit < pass_end; + bit &= ~7ull, bit += 8) { + /* + * If we're extending $MFT and running out of the first + * mft record (base record) then give up searching since + * no guarantee that the found record will be accessible. + */ + if (base_ni && base_ni->mft_no == FILE_MFT && bit > 400) { + folio_unlock(folio); + kunmap_local(buf); + folio_put(folio); + return -ENOSPC; + } + + byte = buf + (bit >> 3); + if (*byte == 0xff) + continue; + b = ffz((unsigned long)*byte); + if (b < 8 && b >= (bit & 7)) { + ll = data_pos + (bit & ~7ull) + b; + if (unlikely(ll > (1ll << 32))) { + folio_unlock(folio); + kunmap_local(buf); + folio_put(folio); + return -ENOSPC; + } + *byte |= 1 << b; + folio_mark_dirty(folio); + folio_unlock(folio); + kunmap_local(buf); + folio_put(folio); + ntfs_debug("Done. (Found and allocated mft record 0x%llx.)", + ll); + return ll; + } + } + ntfs_debug("After inner for loop: size 0x%x, data_pos 0x%llx, bit 0x%llx", + size, data_pos, bit); + data_pos += size; + folio_unlock(folio); + kunmap_local(buf); + folio_put(folio); + /* + * If the end of the pass has not been reached yet, + * continue searching the mft bitmap for a zero bit. + */ + if (data_pos < pass_end) + continue; + } + /* Do the next pass. */ + if (++pass == 2) { + /* + * Starting the second pass, in which we scan the first + * part of the zone which we omitted earlier. + */ + pass_end = pass_start; + data_pos = pass_start = RESERVED_MFT_RECORDS; + ntfs_debug("pass %i, pass_start 0x%llx, pass_end 0x%llx.", + pass, pass_start, pass_end); + if (data_pos >= pass_end) + break; + } + } + /* No free mft records in currently initialized mft bitmap. */ + ntfs_debug("Done. (No free mft records left in currently initialized mft bitmap.)"); + return -ENOSPC; +} + +static int ntfs_mft_attr_extend(struct ntfs_inode *ni) +{ + int ret = 0; + struct ntfs_inode *base_ni; + + if (NInoAttr(ni)) + base_ni = ni->ext.base_ntfs_ino; + else + base_ni = ni; + + if (!NInoAttrList(base_ni)) { + ret = ntfs_inode_add_attrlist(base_ni); + if (ret) { + pr_err("Can not add attrlist\n"); + goto out; + } else { + ret = -EAGAIN; + goto out; + } + } + + ret = ntfs_attr_update_mapping_pairs(ni, 0); + if (ret) + pr_err("MP update failed\n"); + +out: + return ret; +} + +/* + * ntfs_mft_bitmap_extend_allocation_nolock - extend mft bitmap by a cluster + * @vol: volume on which to extend the mft bitmap attribute + * + * Extend the mft bitmap attribute on the ntfs volume @vol by one cluster. + * + * Note: Only changes allocated_size, i.e. does not touch initialized_size or + * data_size. + * + * Return 0 on success and -errno on error. + * + * Locking: - Caller must hold vol->mftbmp_lock for writing. + * - This function takes NTFS_I(vol->mftbmp_ino)->runlist.lock for + * writing and releases it before returning. + * - This function takes vol->lcnbmp_lock for writing and releases it + * before returning. + */ +static int ntfs_mft_bitmap_extend_allocation_nolock(struct ntfs_volume *vol) +{ + s64 lcn; + s64 ll; + unsigned long flags; + struct folio *folio; + struct ntfs_inode *mft_ni, *mftbmp_ni; + struct runlist_element *rl, *rl2 = NULL; + struct ntfs_attr_search_ctx *ctx = NULL; + struct mft_record *mrec; + struct attr_record *a = NULL; + int ret, mp_size; + u32 old_alen = 0; + u8 *b, tb; + struct { + u8 added_cluster:1; + u8 added_run:1; + u8 mp_rebuilt:1; + u8 mp_extended:1; + } status = { 0, 0, 0, 0 }; + size_t new_rl_count; + + ntfs_debug("Extending mft bitmap allocation."); + mft_ni = NTFS_I(vol->mft_ino); + mftbmp_ni = NTFS_I(vol->mftbmp_ino); + /* + * Determine the last lcn of the mft bitmap. The allocated size of the + * mft bitmap cannot be zero so we are ok to do this. + */ + down_write(&mftbmp_ni->runlist.lock); + read_lock_irqsave(&mftbmp_ni->size_lock, flags); + ll = mftbmp_ni->allocated_size; + read_unlock_irqrestore(&mftbmp_ni->size_lock, flags); + rl = ntfs_attr_find_vcn_nolock(mftbmp_ni, + NTFS_B_TO_CLU(vol, ll - 1), NULL); + if (IS_ERR(rl) || unlikely(!rl->length || rl->lcn < 0)) { + up_write(&mftbmp_ni->runlist.lock); + ntfs_error(vol->sb, + "Failed to determine last allocated cluster of mft bitmap attribute."); + if (!IS_ERR(rl)) + ret = -EIO; + else + ret = PTR_ERR(rl); + return ret; + } + lcn = rl->lcn + rl->length; + ntfs_debug("Last lcn of mft bitmap attribute is 0x%llx.", + (long long)lcn); + /* + * Attempt to get the cluster following the last allocated cluster by + * hand as it may be in the MFT zone so the allocator would not give it + * to us. + */ + ll = lcn >> 3; + folio = read_mapping_folio(vol->lcnbmp_ino->i_mapping, + ll >> PAGE_SHIFT, NULL); + if (IS_ERR(folio)) { + up_write(&mftbmp_ni->runlist.lock); + ntfs_error(vol->sb, "Failed to read from lcn bitmap."); + return PTR_ERR(folio); + } + + down_write(&vol->lcnbmp_lock); + folio_lock(folio); + b = (u8 *)kmap_local_folio(folio, 0) + (ll & ~PAGE_MASK); + tb = 1 << (lcn & 7ull); + if (*b != 0xff && !(*b & tb)) { + /* Next cluster is free, allocate it. */ + *b |= tb; + folio_mark_dirty(folio); + folio_unlock(folio); + kunmap_local(b); + folio_put(folio); + up_write(&vol->lcnbmp_lock); + /* Update the mft bitmap runlist. */ + rl->length++; + rl[1].vcn++; + status.added_cluster = 1; + ntfs_debug("Appending one cluster to mft bitmap."); + } else { + folio_unlock(folio); + kunmap_local(b); + folio_put(folio); + up_write(&vol->lcnbmp_lock); + /* Allocate a cluster from the DATA_ZONE. */ + rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE, + true, false, false); + if (IS_ERR(rl2)) { + up_write(&mftbmp_ni->runlist.lock); + ntfs_error(vol->sb, + "Failed to allocate a cluster for the mft bitmap."); + return PTR_ERR(rl2); + } + rl = ntfs_runlists_merge(&mftbmp_ni->runlist, rl2, 0, &new_rl_count); + if (IS_ERR(rl)) { + up_write(&mftbmp_ni->runlist.lock); + ntfs_error(vol->sb, "Failed to merge runlists for mft bitmap."); + if (ntfs_cluster_free_from_rl(vol, rl2)) { + ntfs_error(vol->sb, "Failed to deallocate allocated cluster.%s", + es); + NVolSetErrors(vol); + } + kvfree(rl2); + return PTR_ERR(rl); + } + mftbmp_ni->runlist.rl = rl; + mftbmp_ni->runlist.count = new_rl_count; + status.added_run = 1; + ntfs_debug("Adding one run to mft bitmap."); + /* Find the last run in the new runlist. */ + for (; rl[1].length; rl++) + ; + } + /* + * Update the attribute record as well. Note: @rl is the last + * (non-terminator) runlist element of mft bitmap. + */ + mrec = map_mft_record(mft_ni); + if (IS_ERR(mrec)) { + ntfs_error(vol->sb, "Failed to map mft record."); + ret = PTR_ERR(mrec); + goto undo_alloc; + } + ctx = ntfs_attr_get_search_ctx(mft_ni, mrec); + if (unlikely(!ctx)) { + ntfs_error(vol->sb, "Failed to get search context."); + ret = -ENOMEM; + goto undo_alloc; + } + ret = ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name, + mftbmp_ni->name_len, CASE_SENSITIVE, rl[1].vcn, NULL, + 0, ctx); + if (unlikely(ret)) { + ntfs_error(vol->sb, + "Failed to find last attribute extent of mft bitmap attribute."); + if (ret == -ENOENT) + ret = -EIO; + goto undo_alloc; + } + a = ctx->attr; + ll = le64_to_cpu(a->data.non_resident.lowest_vcn); + /* Search back for the previous last allocated cluster of mft bitmap. */ + for (rl2 = rl; rl2 > mftbmp_ni->runlist.rl; rl2--) { + if (ll >= rl2->vcn) + break; + } + WARN_ON(ll < rl2->vcn); + WARN_ON(ll >= rl2->vcn + rl2->length); + /* Get the size for the new mapping pairs array for this extent. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, -1, -1); + if (unlikely(mp_size <= 0)) { + ntfs_error(vol->sb, + "Get size for mapping pairs failed for mft bitmap attribute extent."); + ret = mp_size; + if (!ret) + ret = -EIO; + goto undo_alloc; + } + /* Expand the attribute record if necessary. */ + old_alen = le32_to_cpu(a->length); + ret = ntfs_attr_record_resize(ctx->mrec, a, mp_size + + le16_to_cpu(a->data.non_resident.mapping_pairs_offset)); + if (unlikely(ret)) { + ret = ntfs_mft_attr_extend(mftbmp_ni); + if (!ret) + goto extended_ok; + if (ret != -EAGAIN) + status.mp_extended = 1; + goto undo_alloc; + } + status.mp_rebuilt = 1; + /* Generate the mapping pairs array directly into the attr record. */ + ret = ntfs_mapping_pairs_build(vol, (u8 *)a + + le16_to_cpu(a->data.non_resident.mapping_pairs_offset), + mp_size, rl2, ll, -1, NULL, NULL, NULL); + if (unlikely(ret)) { + ntfs_error(vol->sb, + "Failed to build mapping pairs array for mft bitmap attribute."); + goto undo_alloc; + } + /* Update the highest_vcn. */ + a->data.non_resident.highest_vcn = cpu_to_le64(rl[1].vcn - 1); + /* + * We now have extended the mft bitmap allocated_size by one cluster. + * Reflect this in the struct ntfs_inode structure and the attribute record. + */ + if (a->data.non_resident.lowest_vcn) { + /* + * We are not in the first attribute extent, switch to it, but + * first ensure the changes will make it to disk later. + */ + mark_mft_record_dirty(ctx->ntfs_ino); +extended_ok: + ntfs_attr_reinit_search_ctx(ctx); + ret = ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name, + mftbmp_ni->name_len, CASE_SENSITIVE, 0, NULL, + 0, ctx); + if (unlikely(ret)) { + ntfs_error(vol->sb, + "Failed to find first attribute extent of mft bitmap attribute."); + goto restore_undo_alloc; + } + a = ctx->attr; + } + + write_lock_irqsave(&mftbmp_ni->size_lock, flags); + mftbmp_ni->allocated_size += vol->cluster_size; + a->data.non_resident.allocated_size = + cpu_to_le64(mftbmp_ni->allocated_size); + write_unlock_irqrestore(&mftbmp_ni->size_lock, flags); + /* Ensure the changes make it to disk. */ + mark_mft_record_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(mft_ni); + up_write(&mftbmp_ni->runlist.lock); + ntfs_debug("Done."); + return 0; + +restore_undo_alloc: + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name, + mftbmp_ni->name_len, CASE_SENSITIVE, rl[1].vcn, NULL, + 0, ctx)) { + ntfs_error(vol->sb, + "Failed to find last attribute extent of mft bitmap attribute.%s", es); + write_lock_irqsave(&mftbmp_ni->size_lock, flags); + mftbmp_ni->allocated_size += vol->cluster_size; + write_unlock_irqrestore(&mftbmp_ni->size_lock, flags); + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(mft_ni); + up_write(&mftbmp_ni->runlist.lock); + /* + * The only thing that is now wrong is ->allocated_size of the + * base attribute extent which chkdsk should be able to fix. + */ + NVolSetErrors(vol); + return ret; + } + a = ctx->attr; + a->data.non_resident.highest_vcn = cpu_to_le64(rl[1].vcn - 2); +undo_alloc: + if (status.added_cluster) { + /* Truncate the last run in the runlist by one cluster. */ + rl->length--; + rl[1].vcn--; + } else if (status.added_run) { + lcn = rl->lcn; + /* Remove the last run from the runlist. */ + rl->lcn = rl[1].lcn; + rl->length = 0; + mftbmp_ni->runlist.count--; + } + /* Deallocate the cluster. */ + down_write(&vol->lcnbmp_lock); + if (ntfs_bitmap_clear_bit(vol->lcnbmp_ino, lcn)) { + ntfs_error(vol->sb, "Failed to free allocated cluster.%s", es); + NVolSetErrors(vol); + } else + ntfs_inc_free_clusters(vol, 1); + up_write(&vol->lcnbmp_lock); + if (status.mp_rebuilt) { + if (ntfs_mapping_pairs_build(vol, (u8 *)a + le16_to_cpu( + a->data.non_resident.mapping_pairs_offset), + old_alen - le16_to_cpu( + a->data.non_resident.mapping_pairs_offset), + rl2, ll, -1, NULL, NULL, NULL)) { + ntfs_error(vol->sb, "Failed to restore mapping pairs array.%s", es); + NVolSetErrors(vol); + } + if (ntfs_attr_record_resize(ctx->mrec, a, old_alen)) { + ntfs_error(vol->sb, "Failed to restore attribute record.%s", es); + NVolSetErrors(vol); + } + mark_mft_record_dirty(ctx->ntfs_ino); + } else if (status.mp_extended && ntfs_attr_update_mapping_pairs(mftbmp_ni, 0)) { + ntfs_error(vol->sb, "Failed to restore mapping pairs.%s", es); + NVolSetErrors(vol); + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (!IS_ERR(mrec)) + unmap_mft_record(mft_ni); + up_write(&mftbmp_ni->runlist.lock); + return ret; +} + +/* + * ntfs_mft_bitmap_extend_initialized_nolock - extend mftbmp initialized data + * @vol: volume on which to extend the mft bitmap attribute + * + * Extend the initialized portion of the mft bitmap attribute on the ntfs + * volume @vol by 8 bytes. + * + * Note: Only changes initialized_size and data_size, i.e. requires that + * allocated_size is big enough to fit the new initialized_size. + * + * Return 0 on success and -error on error. + * + * Locking: Caller must hold vol->mftbmp_lock for writing. + */ +static int ntfs_mft_bitmap_extend_initialized_nolock(struct ntfs_volume *vol) +{ + s64 old_data_size, old_initialized_size; + unsigned long flags; + struct inode *mftbmp_vi; + struct ntfs_inode *mft_ni, *mftbmp_ni; + struct ntfs_attr_search_ctx *ctx; + struct mft_record *mrec; + struct attr_record *a; + int ret; + + ntfs_debug("Extending mft bitmap initialized (and data) size."); + mft_ni = NTFS_I(vol->mft_ino); + mftbmp_vi = vol->mftbmp_ino; + mftbmp_ni = NTFS_I(mftbmp_vi); + /* Get the attribute record. */ + mrec = map_mft_record(mft_ni); + if (IS_ERR(mrec)) { + ntfs_error(vol->sb, "Failed to map mft record."); + return PTR_ERR(mrec); + } + ctx = ntfs_attr_get_search_ctx(mft_ni, mrec); + if (unlikely(!ctx)) { + ntfs_error(vol->sb, "Failed to get search context."); + ret = -ENOMEM; + goto unm_err_out; + } + ret = ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name, + mftbmp_ni->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(ret)) { + ntfs_error(vol->sb, + "Failed to find first attribute extent of mft bitmap attribute."); + if (ret == -ENOENT) + ret = -EIO; + goto put_err_out; + } + a = ctx->attr; + write_lock_irqsave(&mftbmp_ni->size_lock, flags); + old_data_size = i_size_read(mftbmp_vi); + old_initialized_size = mftbmp_ni->initialized_size; + /* + * We can simply update the initialized_size before filling the space + * with zeroes because the caller is holding the mft bitmap lock for + * writing which ensures that no one else is trying to access the data. + */ + mftbmp_ni->initialized_size += 8; + a->data.non_resident.initialized_size = + cpu_to_le64(mftbmp_ni->initialized_size); + if (mftbmp_ni->initialized_size > old_data_size) { + i_size_write(mftbmp_vi, mftbmp_ni->initialized_size); + a->data.non_resident.data_size = + cpu_to_le64(mftbmp_ni->initialized_size); + } + write_unlock_irqrestore(&mftbmp_ni->size_lock, flags); + /* Ensure the changes make it to disk. */ + mark_mft_record_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(mft_ni); + /* Initialize the mft bitmap attribute value with zeroes. */ + ret = ntfs_attr_set(mftbmp_ni, old_initialized_size, 8, 0); + if (likely(!ret)) { + ntfs_debug("Done. (Wrote eight initialized bytes to mft bitmap."); + ntfs_inc_free_mft_records(vol, 8 * 8); + return 0; + } + ntfs_error(vol->sb, "Failed to write to mft bitmap."); + /* Try to recover from the error. */ + mrec = map_mft_record(mft_ni); + if (IS_ERR(mrec)) { + ntfs_error(vol->sb, "Failed to map mft record.%s", es); + NVolSetErrors(vol); + return ret; + } + ctx = ntfs_attr_get_search_ctx(mft_ni, mrec); + if (unlikely(!ctx)) { + ntfs_error(vol->sb, "Failed to get search context.%s", es); + NVolSetErrors(vol); + goto unm_err_out; + } + if (ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name, + mftbmp_ni->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_error(vol->sb, + "Failed to find first attribute extent of mft bitmap attribute.%s", es); + NVolSetErrors(vol); +put_err_out: + ntfs_attr_put_search_ctx(ctx); +unm_err_out: + unmap_mft_record(mft_ni); + goto err_out; + } + a = ctx->attr; + write_lock_irqsave(&mftbmp_ni->size_lock, flags); + mftbmp_ni->initialized_size = old_initialized_size; + a->data.non_resident.initialized_size = + cpu_to_le64(old_initialized_size); + if (i_size_read(mftbmp_vi) != old_data_size) { + i_size_write(mftbmp_vi, old_data_size); + a->data.non_resident.data_size = cpu_to_le64(old_data_size); + } + write_unlock_irqrestore(&mftbmp_ni->size_lock, flags); + mark_mft_record_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(mft_ni); +#ifdef DEBUG + read_lock_irqsave(&mftbmp_ni->size_lock, flags); + ntfs_debug("Restored status of mftbmp: allocated_size 0x%llx, data_size 0x%llx, initialized_size 0x%llx.", + mftbmp_ni->allocated_size, i_size_read(mftbmp_vi), + mftbmp_ni->initialized_size); + read_unlock_irqrestore(&mftbmp_ni->size_lock, flags); +#endif /* DEBUG */ +err_out: + return ret; +} + +/* + * ntfs_mft_data_extend_allocation_nolock - extend mft data attribute + * @vol: volume on which to extend the mft data attribute + * + * Extend the mft data attribute on the ntfs volume @vol by 16 mft records + * worth of clusters or if not enough space for this by one mft record worth + * of clusters. + * + * Note: Only changes allocated_size, i.e. does not touch initialized_size or + * data_size. + * + * Return 0 on success and -errno on error. + * + * Locking: - Caller must hold vol->mftbmp_lock for writing. + * - This function takes NTFS_I(vol->mft_ino)->runlist.lock for + * writing and releases it before returning. + * - This function calls functions which take vol->lcnbmp_lock for + * writing and release it before returning. + */ +static int ntfs_mft_data_extend_allocation_nolock(struct ntfs_volume *vol) +{ + s64 lcn; + s64 old_last_vcn; + s64 min_nr, nr, ll; + unsigned long flags; + struct ntfs_inode *mft_ni; + struct runlist_element *rl, *rl2; + struct ntfs_attr_search_ctx *ctx = NULL; + struct mft_record *mrec; + struct attr_record *a = NULL; + int ret, mp_size; + u32 old_alen = 0; + bool mp_rebuilt = false, mp_extended = false; + size_t new_rl_count; + + ntfs_debug("Extending mft data allocation."); + mft_ni = NTFS_I(vol->mft_ino); + /* + * Determine the preferred allocation location, i.e. the last lcn of + * the mft data attribute. The allocated size of the mft data + * attribute cannot be zero so we are ok to do this. + */ + down_write(&mft_ni->runlist.lock); + read_lock_irqsave(&mft_ni->size_lock, flags); + ll = mft_ni->allocated_size; + read_unlock_irqrestore(&mft_ni->size_lock, flags); + rl = ntfs_attr_find_vcn_nolock(mft_ni, + NTFS_B_TO_CLU(vol, ll - 1), NULL); + if (IS_ERR(rl) || unlikely(!rl->length || rl->lcn < 0)) { + up_write(&mft_ni->runlist.lock); + ntfs_error(vol->sb, + "Failed to determine last allocated cluster of mft data attribute."); + if (!IS_ERR(rl)) + ret = -EIO; + else + ret = PTR_ERR(rl); + return ret; + } + lcn = rl->lcn + rl->length; + ntfs_debug("Last lcn of mft data attribute is 0x%llx.", lcn); + /* Minimum allocation is one mft record worth of clusters. */ + min_nr = NTFS_B_TO_CLU(vol, vol->mft_record_size); + if (!min_nr) + min_nr = 1; + /* Want to allocate 16 mft records worth of clusters. */ + nr = vol->mft_record_size << 4 >> vol->cluster_size_bits; + if (!nr) + nr = min_nr; + /* Ensure we do not go above 2^32-1 mft records. */ + read_lock_irqsave(&mft_ni->size_lock, flags); + ll = mft_ni->allocated_size; + read_unlock_irqrestore(&mft_ni->size_lock, flags); + if (unlikely((ll + NTFS_CLU_TO_B(vol, nr)) >> + vol->mft_record_size_bits >= (1ll << 32))) { + nr = min_nr; + if (unlikely((ll + NTFS_CLU_TO_B(vol, nr)) >> + vol->mft_record_size_bits >= (1ll << 32))) { + ntfs_warning(vol->sb, + "Cannot allocate mft record because the maximum number of inodes (2^32) has already been reached."); + up_write(&mft_ni->runlist.lock); + return -ENOSPC; + } + } + ntfs_debug("Trying mft data allocation with %s cluster count %lli.", + nr > min_nr ? "default" : "minimal", (long long)nr); + old_last_vcn = rl[1].vcn; + /* + * We can release the mft_ni runlist lock, Because this function is + * the only one that expends $MFT data attribute and is called with + * mft_ni->mrec_lock. + * This is required for the lock order, vol->lcnbmp_lock => + * mft_ni->runlist.lock. + */ + up_write(&mft_ni->runlist.lock); + + do { + rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE, + true, false, false); + if (!IS_ERR(rl2)) + break; + if (PTR_ERR(rl2) != -ENOSPC || nr == min_nr) { + ntfs_error(vol->sb, + "Failed to allocate the minimal number of clusters (%lli) for the mft data attribute.", + nr); + return PTR_ERR(rl2); + } + /* + * There is not enough space to do the allocation, but there + * might be enough space to do a minimal allocation so try that + * before failing. + */ + nr = min_nr; + ntfs_debug("Retrying mft data allocation with minimal cluster count %lli.", nr); + } while (1); + + down_write(&mft_ni->runlist.lock); + rl = ntfs_runlists_merge(&mft_ni->runlist, rl2, 0, &new_rl_count); + if (IS_ERR(rl)) { + up_write(&mft_ni->runlist.lock); + ntfs_error(vol->sb, "Failed to merge runlists for mft data attribute."); + if (ntfs_cluster_free_from_rl(vol, rl2)) { + ntfs_error(vol->sb, + "Failed to deallocate clusters from the mft data attribute.%s", es); + NVolSetErrors(vol); + } + kvfree(rl2); + return PTR_ERR(rl); + } + mft_ni->runlist.rl = rl; + mft_ni->runlist.count = new_rl_count; + ntfs_debug("Allocated %lli clusters.", (long long)nr); + /* Find the last run in the new runlist. */ + for (; rl[1].length; rl++) + ; + up_write(&mft_ni->runlist.lock); + + /* Update the attribute record as well. */ + mrec = map_mft_record(mft_ni); + if (IS_ERR(mrec)) { + ntfs_error(vol->sb, "Failed to map mft record."); + ret = PTR_ERR(mrec); + down_write(&mft_ni->runlist.lock); + goto undo_alloc; + } + ctx = ntfs_attr_get_search_ctx(mft_ni, mrec); + if (unlikely(!ctx)) { + ntfs_error(vol->sb, "Failed to get search context."); + ret = -ENOMEM; + goto undo_alloc; + } + ret = ntfs_attr_lookup(mft_ni->type, mft_ni->name, mft_ni->name_len, + CASE_SENSITIVE, rl[1].vcn, NULL, 0, ctx); + if (unlikely(ret)) { + ntfs_error(vol->sb, "Failed to find last attribute extent of mft data attribute."); + if (ret == -ENOENT) + ret = -EIO; + goto undo_alloc; + } + a = ctx->attr; + ll = le64_to_cpu(a->data.non_resident.lowest_vcn); + + down_write(&mft_ni->runlist.lock); + /* Search back for the previous last allocated cluster of mft bitmap. */ + for (rl2 = rl; rl2 > mft_ni->runlist.rl; rl2--) { + if (ll >= rl2->vcn) + break; + } + WARN_ON(ll < rl2->vcn); + WARN_ON(ll >= rl2->vcn + rl2->length); + /* Get the size for the new mapping pairs array for this extent. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, -1, -1); + if (unlikely(mp_size <= 0)) { + ntfs_error(vol->sb, + "Get size for mapping pairs failed for mft data attribute extent."); + ret = mp_size; + if (!ret) + ret = -EIO; + up_write(&mft_ni->runlist.lock); + goto undo_alloc; + } + up_write(&mft_ni->runlist.lock); + + /* Expand the attribute record if necessary. */ + old_alen = le32_to_cpu(a->length); + ret = ntfs_attr_record_resize(ctx->mrec, a, mp_size + + le16_to_cpu(a->data.non_resident.mapping_pairs_offset)); + if (unlikely(ret)) { + ret = ntfs_mft_attr_extend(mft_ni); + if (!ret) + goto extended_ok; + if (ret != -EAGAIN) + mp_extended = true; + goto undo_alloc; + } + mp_rebuilt = true; + /* Generate the mapping pairs array directly into the attr record. */ + ret = ntfs_mapping_pairs_build(vol, (u8 *)a + + le16_to_cpu(a->data.non_resident.mapping_pairs_offset), + mp_size, rl2, ll, -1, NULL, NULL, NULL); + if (unlikely(ret)) { + ntfs_error(vol->sb, "Failed to build mapping pairs array of mft data attribute."); + goto undo_alloc; + } + /* Update the highest_vcn. */ + a->data.non_resident.highest_vcn = cpu_to_le64(rl[1].vcn - 1); + /* + * We now have extended the mft data allocated_size by nr clusters. + * Reflect this in the struct ntfs_inode structure and the attribute record. + * @rl is the last (non-terminator) runlist element of mft data + * attribute. + */ + if (a->data.non_resident.lowest_vcn) { + /* + * We are not in the first attribute extent, switch to it, but + * first ensure the changes will make it to disk later. + */ + mark_mft_record_dirty(ctx->ntfs_ino); +extended_ok: + ntfs_attr_reinit_search_ctx(ctx); + ret = ntfs_attr_lookup(mft_ni->type, mft_ni->name, + mft_ni->name_len, CASE_SENSITIVE, 0, NULL, 0, + ctx); + if (unlikely(ret)) { + ntfs_error(vol->sb, + "Failed to find first attribute extent of mft data attribute."); + goto restore_undo_alloc; + } + a = ctx->attr; + } + + write_lock_irqsave(&mft_ni->size_lock, flags); + mft_ni->allocated_size += NTFS_CLU_TO_B(vol, nr); + a->data.non_resident.allocated_size = + cpu_to_le64(mft_ni->allocated_size); + write_unlock_irqrestore(&mft_ni->size_lock, flags); + /* Ensure the changes make it to disk. */ + mark_mft_record_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(mft_ni); + ntfs_debug("Done."); + return 0; +restore_undo_alloc: + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mft_ni->type, mft_ni->name, mft_ni->name_len, + CASE_SENSITIVE, rl[1].vcn, NULL, 0, ctx)) { + ntfs_error(vol->sb, + "Failed to find last attribute extent of mft data attribute.%s", es); + write_lock_irqsave(&mft_ni->size_lock, flags); + mft_ni->allocated_size += NTFS_CLU_TO_B(vol, nr); + write_unlock_irqrestore(&mft_ni->size_lock, flags); + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(mft_ni); + up_write(&mft_ni->runlist.lock); + /* + * The only thing that is now wrong is ->allocated_size of the + * base attribute extent which chkdsk should be able to fix. + */ + NVolSetErrors(vol); + return ret; + } + ctx->attr->data.non_resident.highest_vcn = + cpu_to_le64(old_last_vcn - 1); +undo_alloc: + if (ntfs_cluster_free(mft_ni, old_last_vcn, -1, ctx) < 0) { + ntfs_error(vol->sb, "Failed to free clusters from mft data attribute.%s", es); + NVolSetErrors(vol); + } + + if (ntfs_rl_truncate_nolock(vol, &mft_ni->runlist, old_last_vcn)) { + ntfs_error(vol->sb, "Failed to truncate mft data attribute runlist.%s", es); + NVolSetErrors(vol); + } + if (mp_extended && ntfs_attr_update_mapping_pairs(mft_ni, 0)) { + ntfs_error(vol->sb, "Failed to restore mapping pairs.%s", + es); + NVolSetErrors(vol); + } + if (ctx) { + a = ctx->attr; + if (mp_rebuilt && !IS_ERR(ctx->mrec)) { + if (ntfs_mapping_pairs_build(vol, (u8 *)a + le16_to_cpu( + a->data.non_resident.mapping_pairs_offset), + old_alen - le16_to_cpu( + a->data.non_resident.mapping_pairs_offset), + rl2, ll, -1, NULL, NULL, NULL)) { + ntfs_error(vol->sb, "Failed to restore mapping pairs array.%s", es); + NVolSetErrors(vol); + } + if (ntfs_attr_record_resize(ctx->mrec, a, old_alen)) { + ntfs_error(vol->sb, "Failed to restore attribute record.%s", es); + NVolSetErrors(vol); + } + mark_mft_record_dirty(ctx->ntfs_ino); + } else if (IS_ERR(ctx->mrec)) { + ntfs_error(vol->sb, "Failed to restore attribute search context.%s", es); + NVolSetErrors(vol); + } + ntfs_attr_put_search_ctx(ctx); + } + if (!IS_ERR(mrec)) + unmap_mft_record(mft_ni); + return ret; +} + +/* + * ntfs_mft_record_layout - layout an mft record into a memory buffer + * @vol: volume to which the mft record will belong + * @mft_no: mft reference specifying the mft record number + * @m: destination buffer of size >= @vol->mft_record_size bytes + * + * Layout an empty, unused mft record with the mft record number @mft_no into + * the buffer @m. The volume @vol is needed because the mft record structure + * was modified in NTFS 3.1 so we need to know which volume version this mft + * record will be used on. + * + * Return 0 on success and -errno on error. + */ +static int ntfs_mft_record_layout(const struct ntfs_volume *vol, const s64 mft_no, + struct mft_record *m) +{ + struct attr_record *a; + + ntfs_debug("Entering for mft record 0x%llx.", (long long)mft_no); + if (mft_no >= (1ll << 32)) { + ntfs_error(vol->sb, "Mft record number 0x%llx exceeds maximum of 2^32.", + (long long)mft_no); + return -ERANGE; + } + /* Start by clearing the whole mft record to gives us a clean slate. */ + memset(m, 0, vol->mft_record_size); + /* Aligned to 2-byte boundary. */ + if (vol->major_ver < 3 || (vol->major_ver == 3 && !vol->minor_ver)) + m->usa_ofs = cpu_to_le16((sizeof(struct mft_record_old) + 1) & ~1); + else { + m->usa_ofs = cpu_to_le16((sizeof(struct mft_record) + 1) & ~1); + /* + * Set the NTFS 3.1+ specific fields while we know that the + * volume version is 3.1+. + */ + m->reserved = 0; + m->mft_record_number = cpu_to_le32((u32)mft_no); + } + m->magic = magic_FILE; + if (vol->mft_record_size >= NTFS_BLOCK_SIZE) + m->usa_count = cpu_to_le16(vol->mft_record_size / + NTFS_BLOCK_SIZE + 1); + else { + m->usa_count = cpu_to_le16(1); + ntfs_warning(vol->sb, + "Sector size is bigger than mft record size. Setting usa_count to 1. If chkdsk reports this as corruption"); + } + /* Set the update sequence number to 1. */ + *(__le16 *)((u8 *)m + le16_to_cpu(m->usa_ofs)) = cpu_to_le16(1); + m->lsn = 0; + m->sequence_number = cpu_to_le16(1); + m->link_count = 0; + /* + * Place the attributes straight after the update sequence array, + * aligned to 8-byte boundary. + */ + m->attrs_offset = cpu_to_le16((le16_to_cpu(m->usa_ofs) + + (le16_to_cpu(m->usa_count) << 1) + 7) & ~7); + m->flags = 0; + /* + * Using attrs_offset plus eight bytes (for the termination attribute). + * attrs_offset is already aligned to 8-byte boundary, so no need to + * align again. + */ + m->bytes_in_use = cpu_to_le32(le16_to_cpu(m->attrs_offset) + 8); + m->bytes_allocated = cpu_to_le32(vol->mft_record_size); + m->base_mft_record = 0; + m->next_attr_instance = 0; + /* Add the termination attribute. */ + a = (struct attr_record *)((u8 *)m + le16_to_cpu(m->attrs_offset)); + a->type = AT_END; + a->length = 0; + ntfs_debug("Done."); + return 0; +} + +/* + * ntfs_mft_record_format - format an mft record on an ntfs volume + * @vol: volume on which to format the mft record + * @mft_no: mft record number to format + * + * Format the mft record @mft_no in $MFT/$DATA, i.e. lay out an empty, unused + * mft record into the appropriate place of the mft data attribute. This is + * used when extending the mft data attribute. + * + * Return 0 on success and -errno on error. + */ +static int ntfs_mft_record_format(const struct ntfs_volume *vol, const s64 mft_no) +{ + loff_t i_size; + struct inode *mft_vi = vol->mft_ino; + struct folio *folio; + struct mft_record *m; + pgoff_t index, end_index; + unsigned int ofs; + int err; + + ntfs_debug("Entering for mft record 0x%llx.", (long long)mft_no); + /* + * The index into the page cache and the offset within the page cache + * page of the wanted mft record. + */ + index = NTFS_MFT_NR_TO_PIDX(vol, mft_no); + ofs = NTFS_MFT_NR_TO_POFS(vol, mft_no); + /* The maximum valid index into the page cache for $MFT's data. */ + i_size = i_size_read(mft_vi); + end_index = i_size >> PAGE_SHIFT; + if (unlikely(index >= end_index)) { + if (unlikely(index > end_index || + ofs + vol->mft_record_size > (i_size & ~PAGE_MASK))) { + ntfs_error(vol->sb, "Tried to format non-existing mft record 0x%llx.", + (long long)mft_no); + return -ENOENT; + } + } + + /* Read, map, and pin the folio containing the mft record. */ + folio = read_mapping_folio(mft_vi->i_mapping, index, NULL); + if (IS_ERR(folio)) { + ntfs_error(vol->sb, "Failed to map page containing mft record to format 0x%llx.", + (long long)mft_no); + return PTR_ERR(folio); + } + folio_lock(folio); + folio_clear_uptodate(folio); + m = (struct mft_record *)((u8 *)kmap_local_folio(folio, 0) + ofs); + err = ntfs_mft_record_layout(vol, mft_no, m); + if (unlikely(err)) { + ntfs_error(vol->sb, "Failed to layout mft record 0x%llx.", + (long long)mft_no); + folio_mark_uptodate(folio); + folio_unlock(folio); + kunmap_local(m); + folio_put(folio); + return err; + } + pre_write_mst_fixup((struct ntfs_record *)m, vol->mft_record_size); + folio_mark_uptodate(folio); + /* + * Make sure the mft record is written out to disk. We could use + * ilookup5() to check if an inode is in icache and so on but this is + * unnecessary as ntfs_writepage() will write the dirty record anyway. + */ + ntfs_mft_mark_dirty(folio); + folio_unlock(folio); + kunmap_local(m); + folio_put(folio); + ntfs_debug("Done."); + return 0; +} + +/* + * ntfs_mft_record_alloc - allocate an mft record on an ntfs volume + * @vol: [IN] volume on which to allocate the mft record + * @mode: [IN] mode if want a file or directory, i.e. base inode or 0 + * @ni: [OUT] on success, set to the allocated ntfs inode + * @base_ni: [IN] open base inode if allocating an extent mft record or NULL + * @ni_mrec: [OUT] on successful return this is the mapped mft record + * + * Allocate an mft record in $MFT/$DATA of an open ntfs volume @vol. + * + * If @base_ni is NULL make the mft record a base mft record, i.e. a file or + * direvctory inode, and allocate it at the default allocator position. In + * this case @mode is the file mode as given to us by the caller. We in + * particular use @mode to distinguish whether a file or a directory is being + * created (S_IFDIR(mode) and S_IFREG(mode), respectively). + * + * If @base_ni is not NULL make the allocated mft record an extent record, + * allocate it starting at the mft record after the base mft record and attach + * the allocated and opened ntfs inode to the base inode @base_ni. In this + * case @mode must be 0 as it is meaningless for extent inodes. + * + * You need to check the return value with IS_ERR(). If false, the function + * was successful and the return value is the now opened ntfs inode of the + * allocated mft record. *@mrec is then set to the allocated, mapped, pinned, + * and locked mft record. If IS_ERR() is true, the function failed and the + * error code is obtained from PTR_ERR(return value). *@mrec is undefined in + * this case. + * + * Allocation strategy: + * + * To find a free mft record, we scan the mft bitmap for a zero bit. To + * optimize this we start scanning at the place specified by @base_ni or if + * @base_ni is NULL we start where we last stopped and we perform wrap around + * when we reach the end. Note, we do not try to allocate mft records below + * number 64 because numbers 0 to 15 are the defined system files anyway and 16 + * to 64 are special in that they are used for storing extension mft records + * for the $DATA attribute of $MFT. This is required to avoid the possibility + * of creating a runlist with a circular dependency which once written to disk + * can never be read in again. Windows will only use records 16 to 24 for + * normal files if the volume is completely out of space. We never use them + * which means that when the volume is really out of space we cannot create any + * more files while Windows can still create up to 8 small files. We can start + * doing this at some later time, it does not matter much for now. + * + * When scanning the mft bitmap, we only search up to the last allocated mft + * record. If there are no free records left in the range 64 to number of + * allocated mft records, then we extend the $MFT/$DATA attribute in order to + * create free mft records. We extend the allocated size of $MFT/$DATA by 16 + * records at a time or one cluster, if cluster size is above 16kiB. If there + * is not sufficient space to do this, we try to extend by a single mft record + * or one cluster, if cluster size is above the mft record size. + * + * No matter how many mft records we allocate, we initialize only the first + * allocated mft record, incrementing mft data size and initialized size + * accordingly, open an struct ntfs_inode for it and return it to the caller, unless + * there are less than 64 mft records, in which case we allocate and initialize + * mft records until we reach record 64 which we consider as the first free mft + * record for use by normal files. + * + * If during any stage we overflow the initialized data in the mft bitmap, we + * extend the initialized size (and data size) by 8 bytes, allocating another + * cluster if required. The bitmap data size has to be at least equal to the + * number of mft records in the mft, but it can be bigger, in which case the + * superfluous bits are padded with zeroes. + * + * Thus, when we return successfully (IS_ERR() is false), we will have: + * - initialized / extended the mft bitmap if necessary, + * - initialized / extended the mft data if necessary, + * - set the bit corresponding to the mft record being allocated in the + * mft bitmap, + * - opened an struct ntfs_inode for the allocated mft record, and we will have + * - returned the struct ntfs_inode as well as the allocated mapped, pinned, and + * locked mft record. + * + * On error, the volume will be left in a consistent state and no record will + * be allocated. If rolling back a partial operation fails, we may leave some + * inconsistent metadata in which case we set NVolErrors() so the volume is + * left dirty when unmounted. + * + * Note, this function cannot make use of most of the normal functions, like + * for example for attribute resizing, etc, because when the run list overflows + * the base mft record and an attribute list is used, it is very important that + * the extension mft records used to store the $DATA attribute of $MFT can be + * reached without having to read the information contained inside them, as + * this would make it impossible to find them in the first place after the + * volume is unmounted. $MFT/$BITMAP probably does not need to follow this + * rule because the bitmap is not essential for finding the mft records, but on + * the other hand, handling the bitmap in this special way would make life + * easier because otherwise there might be circular invocations of functions + * when reading the bitmap. + */ +int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode, + struct ntfs_inode **ni, struct ntfs_inode *base_ni, + struct mft_record **ni_mrec) +{ + s64 ll, bit, old_data_initialized, old_data_size; + unsigned long flags; + struct folio *folio; + struct ntfs_inode *mft_ni, *mftbmp_ni; + struct ntfs_attr_search_ctx *ctx; + struct mft_record *m = NULL; + struct attr_record *a; + pgoff_t index; + unsigned int ofs; + int err; + __le16 seq_no, usn; + bool record_formatted = false; + unsigned int memalloc_flags; + + if (base_ni && *ni) + return -EINVAL; + + /* @mode and @base_ni are mutually exclusive. */ + if (mode && base_ni) + return -EINVAL; + + if (base_ni) + ntfs_debug("Entering (allocating an extent mft record for base mft record 0x%llx).", + (long long)base_ni->mft_no); + else + ntfs_debug("Entering (allocating a base mft record)."); + + memalloc_flags = memalloc_nofs_save(); + + mft_ni = NTFS_I(vol->mft_ino); + if (!base_ni || base_ni->mft_no != FILE_MFT) + mutex_lock(&mft_ni->mrec_lock); + mftbmp_ni = NTFS_I(vol->mftbmp_ino); +search_free_rec: + if (!base_ni || base_ni->mft_no != FILE_MFT) + down_write(&vol->mftbmp_lock); + bit = ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(vol, base_ni); + if (bit >= 0) { + ntfs_debug("Found and allocated free record (#1), bit 0x%llx.", + (long long)bit); + goto have_alloc_rec; + } + if (bit != -ENOSPC) { + if (!base_ni || base_ni->mft_no != FILE_MFT) { + up_write(&vol->mftbmp_lock); + mutex_unlock(&mft_ni->mrec_lock); + } + memalloc_nofs_restore(memalloc_flags); + return bit; + } + + if (base_ni && base_ni->mft_no == FILE_MFT) { + memalloc_nofs_restore(memalloc_flags); + return bit; + } + + /* + * No free mft records left. If the mft bitmap already covers more + * than the currently used mft records, the next records are all free, + * so we can simply allocate the first unused mft record. + * Note: We also have to make sure that the mft bitmap at least covers + * the first 24 mft records as they are special and whilst they may not + * be in use, we do not allocate from them. + */ + read_lock_irqsave(&mft_ni->size_lock, flags); + ll = mft_ni->initialized_size >> vol->mft_record_size_bits; + read_unlock_irqrestore(&mft_ni->size_lock, flags); + read_lock_irqsave(&mftbmp_ni->size_lock, flags); + old_data_initialized = mftbmp_ni->initialized_size; + read_unlock_irqrestore(&mftbmp_ni->size_lock, flags); + if (old_data_initialized << 3 > ll && + old_data_initialized > RESERVED_MFT_RECORDS / 8) { + bit = ll; + if (bit < RESERVED_MFT_RECORDS) + bit = RESERVED_MFT_RECORDS; + if (unlikely(bit >= (1ll << 32))) + goto max_err_out; + ntfs_debug("Found free record (#2), bit 0x%llx.", + (long long)bit); + goto found_free_rec; + } + /* + * The mft bitmap needs to be expanded until it covers the first unused + * mft record that we can allocate. + * Note: The smallest mft record we allocate is mft record 24. + */ + bit = old_data_initialized << 3; + if (unlikely(bit >= (1ll << 32))) + goto max_err_out; + read_lock_irqsave(&mftbmp_ni->size_lock, flags); + old_data_size = mftbmp_ni->allocated_size; + ntfs_debug("Status of mftbmp before extension: allocated_size 0x%llx, data_size 0x%llx, initialized_size 0x%llx.", + old_data_size, i_size_read(vol->mftbmp_ino), + old_data_initialized); + read_unlock_irqrestore(&mftbmp_ni->size_lock, flags); + if (old_data_initialized + 8 > old_data_size) { + /* Need to extend bitmap by one more cluster. */ + ntfs_debug("mftbmp: initialized_size + 8 > allocated_size."); + err = ntfs_mft_bitmap_extend_allocation_nolock(vol); + if (err == -EAGAIN) + err = ntfs_mft_bitmap_extend_allocation_nolock(vol); + + if (unlikely(err)) { + if (!base_ni || base_ni->mft_no != FILE_MFT) + up_write(&vol->mftbmp_lock); + goto err_out; + } +#ifdef DEBUG + read_lock_irqsave(&mftbmp_ni->size_lock, flags); + ntfs_debug("Status of mftbmp after allocation extension: allocated_size 0x%llx, data_size 0x%llx, initialized_size 0x%llx.", + mftbmp_ni->allocated_size, + i_size_read(vol->mftbmp_ino), + mftbmp_ni->initialized_size); + read_unlock_irqrestore(&mftbmp_ni->size_lock, flags); +#endif /* DEBUG */ + } + /* + * We now have sufficient allocated space, extend the initialized_size + * as well as the data_size if necessary and fill the new space with + * zeroes. + */ + err = ntfs_mft_bitmap_extend_initialized_nolock(vol); + if (unlikely(err)) { + if (!base_ni || base_ni->mft_no != FILE_MFT) + up_write(&vol->mftbmp_lock); + goto err_out; + } +#ifdef DEBUG + read_lock_irqsave(&mftbmp_ni->size_lock, flags); + ntfs_debug("Status of mftbmp after initialized extension: allocated_size 0x%llx, data_size 0x%llx, initialized_size 0x%llx.", + mftbmp_ni->allocated_size, + i_size_read(vol->mftbmp_ino), + mftbmp_ni->initialized_size); + read_unlock_irqrestore(&mftbmp_ni->size_lock, flags); +#endif /* DEBUG */ + ntfs_debug("Found free record (#3), bit 0x%llx.", (long long)bit); +found_free_rec: + /* @bit is the found free mft record, allocate it in the mft bitmap. */ + ntfs_debug("At found_free_rec."); + err = ntfs_bitmap_set_bit(vol->mftbmp_ino, bit); + if (unlikely(err)) { + ntfs_error(vol->sb, "Failed to allocate bit in mft bitmap."); + if (!base_ni || base_ni->mft_no != FILE_MFT) + up_write(&vol->mftbmp_lock); + goto err_out; + } + ntfs_debug("Set bit 0x%llx in mft bitmap.", (long long)bit); +have_alloc_rec: + /* + * The mft bitmap is now uptodate. Deal with mft data attribute now. + * Note, we keep hold of the mft bitmap lock for writing until all + * modifications to the mft data attribute are complete, too, as they + * will impact decisions for mft bitmap and mft record allocation done + * by a parallel allocation and if the lock is not maintained a + * parallel allocation could allocate the same mft record as this one. + */ + ll = (bit + 1) << vol->mft_record_size_bits; + read_lock_irqsave(&mft_ni->size_lock, flags); + old_data_initialized = mft_ni->initialized_size; + read_unlock_irqrestore(&mft_ni->size_lock, flags); + if (ll <= old_data_initialized) { + ntfs_debug("Allocated mft record already initialized."); + goto mft_rec_already_initialized; + } + ntfs_debug("Initializing allocated mft record."); + /* + * The mft record is outside the initialized data. Extend the mft data + * attribute until it covers the allocated record. The loop is only + * actually traversed more than once when a freshly formatted volume is + * first written to so it optimizes away nicely in the common case. + */ + if (!base_ni || base_ni->mft_no != FILE_MFT) { + read_lock_irqsave(&mft_ni->size_lock, flags); + ntfs_debug("Status of mft data before extension: allocated_size 0x%llx, data_size 0x%llx, initialized_size 0x%llx.", + mft_ni->allocated_size, i_size_read(vol->mft_ino), + mft_ni->initialized_size); + while (ll > mft_ni->allocated_size) { + read_unlock_irqrestore(&mft_ni->size_lock, flags); + err = ntfs_mft_data_extend_allocation_nolock(vol); + if (err == -EAGAIN) + err = ntfs_mft_data_extend_allocation_nolock(vol); + + if (unlikely(err)) { + ntfs_error(vol->sb, "Failed to extend mft data allocation."); + goto undo_mftbmp_alloc_nolock; + } + read_lock_irqsave(&mft_ni->size_lock, flags); + ntfs_debug("Status of mft data after allocation extension: allocated_size 0x%llx, data_size 0x%llx, initialized_size 0x%llx.", + mft_ni->allocated_size, i_size_read(vol->mft_ino), + mft_ni->initialized_size); + } + read_unlock_irqrestore(&mft_ni->size_lock, flags); + } else if (ll > mft_ni->allocated_size) { + err = -ENOSPC; + goto undo_mftbmp_alloc_nolock; + } + /* + * Extend mft data initialized size (and data size of course) to reach + * the allocated mft record, formatting the mft records allong the way. + * Note: We only modify the struct ntfs_inode structure as that is all that is + * needed by ntfs_mft_record_format(). We will update the attribute + * record itself in one fell swoop later on. + */ + write_lock_irqsave(&mft_ni->size_lock, flags); + old_data_initialized = mft_ni->initialized_size; + old_data_size = vol->mft_ino->i_size; + while (ll > mft_ni->initialized_size) { + s64 new_initialized_size, mft_no; + + new_initialized_size = mft_ni->initialized_size + + vol->mft_record_size; + mft_no = mft_ni->initialized_size >> vol->mft_record_size_bits; + if (new_initialized_size > i_size_read(vol->mft_ino)) + i_size_write(vol->mft_ino, new_initialized_size); + write_unlock_irqrestore(&mft_ni->size_lock, flags); + ntfs_debug("Initializing mft record 0x%llx.", + (long long)mft_no); + err = ntfs_mft_record_format(vol, mft_no); + if (unlikely(err)) { + ntfs_error(vol->sb, "Failed to format mft record."); + goto undo_data_init; + } + write_lock_irqsave(&mft_ni->size_lock, flags); + mft_ni->initialized_size = new_initialized_size; + } + write_unlock_irqrestore(&mft_ni->size_lock, flags); + record_formatted = true; + /* Update the mft data attribute record to reflect the new sizes. */ + m = map_mft_record(mft_ni); + if (IS_ERR(m)) { + ntfs_error(vol->sb, "Failed to map mft record."); + err = PTR_ERR(m); + goto undo_data_init; + } + ctx = ntfs_attr_get_search_ctx(mft_ni, m); + if (unlikely(!ctx)) { + ntfs_error(vol->sb, "Failed to get search context."); + err = -ENOMEM; + unmap_mft_record(mft_ni); + goto undo_data_init; + } + err = ntfs_attr_lookup(mft_ni->type, mft_ni->name, mft_ni->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx); + if (unlikely(err)) { + ntfs_error(vol->sb, "Failed to find first attribute extent of mft data attribute."); + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(mft_ni); + goto undo_data_init; + } + a = ctx->attr; + read_lock_irqsave(&mft_ni->size_lock, flags); + a->data.non_resident.initialized_size = + cpu_to_le64(mft_ni->initialized_size); + a->data.non_resident.data_size = + cpu_to_le64(i_size_read(vol->mft_ino)); + read_unlock_irqrestore(&mft_ni->size_lock, flags); + /* Ensure the changes make it to disk. */ + mark_mft_record_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(mft_ni); + read_lock_irqsave(&mft_ni->size_lock, flags); + ntfs_debug("Status of mft data after mft record initialization: allocated_size 0x%llx, data_size 0x%llx, initialized_size 0x%llx.", + mft_ni->allocated_size, i_size_read(vol->mft_ino), + mft_ni->initialized_size); + WARN_ON(i_size_read(vol->mft_ino) > mft_ni->allocated_size); + WARN_ON(mft_ni->initialized_size > i_size_read(vol->mft_ino)); + read_unlock_irqrestore(&mft_ni->size_lock, flags); +mft_rec_already_initialized: + /* + * We can finally drop the mft bitmap lock as the mft data attribute + * has been fully updated. The only disparity left is that the + * allocated mft record still needs to be marked as in use to match the + * set bit in the mft bitmap but this is actually not a problem since + * this mft record is not referenced from anywhere yet and the fact + * that it is allocated in the mft bitmap means that no-one will try to + * allocate it either. + */ + if (!base_ni || base_ni->mft_no != FILE_MFT) + up_write(&vol->mftbmp_lock); + /* + * We now have allocated and initialized the mft record. Calculate the + * index of and the offset within the page cache page the record is in. + */ + index = NTFS_MFT_NR_TO_PIDX(vol, bit); + ofs = NTFS_MFT_NR_TO_POFS(vol, bit); + /* Read, map, and pin the folio containing the mft record. */ + folio = read_mapping_folio(vol->mft_ino->i_mapping, index, NULL); + if (IS_ERR(folio)) { + ntfs_error(vol->sb, "Failed to map page containing allocated mft record 0x%llx.", + bit); + err = PTR_ERR(folio); + goto undo_mftbmp_alloc; + } + folio_lock(folio); + folio_clear_uptodate(folio); + m = (struct mft_record *)((u8 *)kmap_local_folio(folio, 0) + ofs); + /* If we just formatted the mft record no need to do it again. */ + if (!record_formatted) { + /* Sanity check that the mft record is really not in use. */ + if (ntfs_is_file_record(m->magic) && + (m->flags & MFT_RECORD_IN_USE)) { + ntfs_warning(vol->sb, + "Mft record 0x%llx was marked free in mft bitmap but is marked used itself. Unmount and run chkdsk.", + bit); + folio_mark_uptodate(folio); + folio_unlock(folio); + kunmap_local(m); + folio_put(folio); + NVolSetErrors(vol); + goto search_free_rec; + } + /* + * We need to (re-)format the mft record, preserving the + * sequence number if it is not zero as well as the update + * sequence number if it is not zero or -1 (0xffff). This + * means we do not need to care whether or not something went + * wrong with the previous mft record. + */ + seq_no = m->sequence_number; + usn = *(__le16 *)((u8 *)m + le16_to_cpu(m->usa_ofs)); + err = ntfs_mft_record_layout(vol, bit, m); + if (unlikely(err)) { + ntfs_error(vol->sb, "Failed to layout allocated mft record 0x%llx.", + bit); + folio_mark_uptodate(folio); + folio_unlock(folio); + kunmap_local(m); + folio_put(folio); + goto undo_mftbmp_alloc; + } + if (seq_no) + m->sequence_number = seq_no; + if (usn && le16_to_cpu(usn) != 0xffff) + *(__le16 *)((u8 *)m + le16_to_cpu(m->usa_ofs)) = usn; + pre_write_mst_fixup((struct ntfs_record *)m, vol->mft_record_size); + } + /* Set the mft record itself in use. */ + m->flags |= MFT_RECORD_IN_USE; + if (S_ISDIR(mode)) + m->flags |= MFT_RECORD_IS_DIRECTORY; + folio_mark_uptodate(folio); + if (base_ni) { + struct mft_record *m_tmp; + + /* + * Setup the base mft record in the extent mft record. This + * completes initialization of the allocated extent mft record + * and we can simply use it with map_extent_mft_record(). + */ + m->base_mft_record = MK_LE_MREF(base_ni->mft_no, + base_ni->seq_no); + /* + * Allocate an extent inode structure for the new mft record, + * attach it to the base inode @base_ni and map, pin, and lock + * its, i.e. the allocated, mft record. + */ + m_tmp = map_extent_mft_record(base_ni, + MK_MREF(bit, le16_to_cpu(m->sequence_number)), + ni); + if (IS_ERR(m_tmp)) { + ntfs_error(vol->sb, "Failed to map allocated extent mft record 0x%llx.", + bit); + err = PTR_ERR(m_tmp); + /* Set the mft record itself not in use. */ + m->flags &= cpu_to_le16( + ~le16_to_cpu(MFT_RECORD_IN_USE)); + /* Make sure the mft record is written out to disk. */ + ntfs_mft_mark_dirty(folio); + folio_unlock(folio); + kunmap_local(m); + folio_put(folio); + goto undo_mftbmp_alloc; + } + + /* + * Make sure the allocated mft record is written out to disk. + * No need to set the inode dirty because the caller is going + * to do that anyway after finishing with the new extent mft + * record (e.g. at a minimum a new attribute will be added to + * the mft record. + */ + ntfs_mft_mark_dirty(folio); + folio_unlock(folio); + /* + * Need to unmap the page since map_extent_mft_record() mapped + * it as well so we have it mapped twice at the moment. + */ + kunmap_local(m); + folio_put(folio); + } else { + /* + * Manually map, pin, and lock the mft record as we already + * have its page mapped and it is very easy to do. + */ + (*ni)->seq_no = le16_to_cpu(m->sequence_number); + /* + * Make sure the allocated mft record is written out to disk. + * NOTE: We do not set the ntfs inode dirty because this would + * fail in ntfs_write_inode() because the inode does not have a + * standard information attribute yet. Also, there is no need + * to set the inode dirty because the caller is going to do + * that anyway after finishing with the new mft record (e.g. at + * a minimum some new attributes will be added to the mft + * record. + */ + + (*ni)->mrec = kmalloc(vol->mft_record_size, GFP_NOFS); + if (!(*ni)->mrec) { + folio_unlock(folio); + kunmap_local(m); + folio_put(folio); + err = -ENOMEM; + goto undo_mftbmp_alloc; + } + + memcpy((*ni)->mrec, m, vol->mft_record_size); + post_read_mst_fixup((struct ntfs_record *)(*ni)->mrec, vol->mft_record_size); + ntfs_mft_mark_dirty(folio); + folio_unlock(folio); + (*ni)->folio = folio; + (*ni)->folio_ofs = ofs; + atomic_inc(&(*ni)->count); + /* Update the default mft allocation position. */ + vol->mft_data_pos = bit + 1; + } + if (!base_ni || base_ni->mft_no != FILE_MFT) + mutex_unlock(&mft_ni->mrec_lock); + memalloc_nofs_restore(memalloc_flags); + + /* + * Return the opened, allocated inode of the allocated mft record as + * well as the mapped, pinned, and locked mft record. + */ + ntfs_debug("Returning opened, allocated %sinode 0x%llx.", + base_ni ? "extent " : "", bit); + (*ni)->mft_no = bit; + if (ni_mrec) + *ni_mrec = (*ni)->mrec; + ntfs_dec_free_mft_records(vol, 1); + return 0; +undo_data_init: + write_lock_irqsave(&mft_ni->size_lock, flags); + mft_ni->initialized_size = old_data_initialized; + i_size_write(vol->mft_ino, old_data_size); + write_unlock_irqrestore(&mft_ni->size_lock, flags); + goto undo_mftbmp_alloc_nolock; +undo_mftbmp_alloc: + if (!base_ni || base_ni->mft_no != FILE_MFT) + down_write(&vol->mftbmp_lock); +undo_mftbmp_alloc_nolock: + if (ntfs_bitmap_clear_bit(vol->mftbmp_ino, bit)) { + ntfs_error(vol->sb, "Failed to clear bit in mft bitmap.%s", es); + NVolSetErrors(vol); + } + if (!base_ni || base_ni->mft_no != FILE_MFT) + up_write(&vol->mftbmp_lock); +err_out: + if (!base_ni || base_ni->mft_no != FILE_MFT) + mutex_unlock(&mft_ni->mrec_lock); + memalloc_nofs_restore(memalloc_flags); + return err; +max_err_out: + ntfs_warning(vol->sb, + "Cannot allocate mft record because the maximum number of inodes (2^32) has already been reached."); + if (!base_ni || base_ni->mft_no != FILE_MFT) { + up_write(&vol->mftbmp_lock); + mutex_unlock(&mft_ni->mrec_lock); + } + memalloc_nofs_restore(memalloc_flags); + return -ENOSPC; +} + +/* + * ntfs_mft_record_free - free an mft record on an ntfs volume + * @vol: volume on which to free the mft record + * @ni: open ntfs inode of the mft record to free + * + * Free the mft record of the open inode @ni on the mounted ntfs volume @vol. + * Note that this function calls ntfs_inode_close() internally and hence you + * cannot use the pointer @ni any more after this function returns success. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni) +{ + u64 mft_no; + int err; + u16 seq_no; + __le16 old_seq_no; + struct mft_record *ni_mrec; + unsigned int memalloc_flags; + struct ntfs_inode *base_ni; + + if (!vol || !ni) + return -EINVAL; + + ntfs_debug("Entering for inode 0x%llx.\n", (long long)ni->mft_no); + + ni_mrec = map_mft_record(ni); + if (IS_ERR(ni_mrec)) + return -EIO; + + /* Cache the mft reference for later. */ + mft_no = ni->mft_no; + + /* Mark the mft record as not in use. */ + ni_mrec->flags &= ~MFT_RECORD_IN_USE; + + /* Increment the sequence number, skipping zero, if it is not zero. */ + old_seq_no = ni_mrec->sequence_number; + seq_no = le16_to_cpu(old_seq_no); + if (seq_no == 0xffff) + seq_no = 1; + else if (seq_no) + seq_no++; + ni_mrec->sequence_number = cpu_to_le16(seq_no); + + down_read(&NTFS_I(vol->mft_ino)->runlist.lock); + err = ntfs_get_block_mft_record(NTFS_I(vol->mft_ino), ni); + up_read(&NTFS_I(vol->mft_ino)->runlist.lock); + if (err) { + unmap_mft_record(ni); + return err; + } + + /* + * Set the ntfs inode dirty and write it out. We do not need to worry + * about the base inode here since whatever caused the extent mft + * record to be freed is guaranteed to do it already. + */ + NInoSetDirty(ni); + err = write_mft_record(ni, ni_mrec, 0); + if (err) + goto sync_rollback; + + if (likely(ni->nr_extents >= 0)) + base_ni = ni; + else + base_ni = ni->ext.base_ntfs_ino; + + /* Clear the bit in the $MFT/$BITMAP corresponding to this record. */ + memalloc_flags = memalloc_nofs_save(); + if (base_ni->mft_no != FILE_MFT) + down_write(&vol->mftbmp_lock); + err = ntfs_bitmap_clear_bit(vol->mftbmp_ino, mft_no); + if (base_ni->mft_no != FILE_MFT) + up_write(&vol->mftbmp_lock); + memalloc_nofs_restore(memalloc_flags); + if (err) + goto bitmap_rollback; + + unmap_mft_record(ni); + ntfs_inc_free_mft_records(vol, 1); + return 0; + + /* Rollback what we did... */ +bitmap_rollback: + memalloc_flags = memalloc_nofs_save(); + if (base_ni->mft_no != FILE_MFT) + down_write(&vol->mftbmp_lock); + if (ntfs_bitmap_set_bit(vol->mftbmp_ino, mft_no)) + ntfs_error(vol->sb, "ntfs_bitmap_set_bit failed in bitmap_rollback\n"); + if (base_ni->mft_no != FILE_MFT) + up_write(&vol->mftbmp_lock); + memalloc_nofs_restore(memalloc_flags); +sync_rollback: + ntfs_error(vol->sb, + "Eeek! Rollback failed in %s. Leaving inconsistent metadata!\n", __func__); + ni_mrec->flags |= MFT_RECORD_IN_USE; + ni_mrec->sequence_number = old_seq_no; + NInoSetDirty(ni); + write_mft_record(ni, ni_mrec, 0); + unmap_mft_record(ni); + return err; +} + +static s64 lcn_from_index(struct ntfs_volume *vol, struct ntfs_inode *ni, + unsigned long index) +{ + s64 vcn; + s64 lcn; + + vcn = ntfs_pidx_to_cluster(vol, index); + + down_read(&ni->runlist.lock); + lcn = ntfs_attr_vcn_to_lcn_nolock(ni, vcn, false); + up_read(&ni->runlist.lock); + + return lcn; +} + +/* + * ntfs_write_mft_block - Write back a folio containing MFT records + * @folio: The folio to write back (contains one or more MFT records) + * @wbc: Writeback control structure + * + * This function is called as part of the address_space_operations + * .writepages implementation for the $MFT inode (or $MFTMirr). + * It handles writing one folio (normally 4KiB page) worth of MFT records + * to the underlying block device. + * + * Return: 0 on success, or -errno on error. + */ +static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *wbc) +{ + struct address_space *mapping = folio->mapping; + struct inode *vi = mapping->host; + struct ntfs_inode *ni = NTFS_I(vi); + struct ntfs_volume *vol = ni->vol; + u8 *kaddr; + struct ntfs_inode **locked_nis __free(kfree) = kmalloc_array(PAGE_SIZE / NTFS_BLOCK_SIZE, + sizeof(struct ntfs_inode *), GFP_NOFS); + int nr_locked_nis = 0, err = 0, mft_ofs, prev_mft_ofs; + struct inode **ref_inos __free(kfree) = kmalloc_array(PAGE_SIZE / NTFS_BLOCK_SIZE, + sizeof(struct inode *), GFP_NOFS); + int nr_ref_inos = 0; + struct bio *bio = NULL; + u64 mft_no; + struct ntfs_inode *tni; + s64 lcn; + s64 vcn = ntfs_pidx_to_cluster(vol, folio->index); + s64 end_vcn = ntfs_bytes_to_cluster(vol, ni->allocated_size); + unsigned int folio_sz; + struct runlist_element *rl = NULL; + loff_t i_size = i_size_read(vi); + + ntfs_debug("Entering for inode 0x%llx, attribute type 0x%x, folio index 0x%lx.", + ni->mft_no, ni->type, folio->index); + + if (!locked_nis || !ref_inos) + return -ENOMEM; + + /* We have to zero every time due to mmap-at-end-of-file. */ + if (folio->index >= (i_size >> folio_shift(folio))) + /* The page straddles i_size. */ + folio_zero_segment(folio, + offset_in_folio(folio, i_size), + folio_size(folio)); + + lcn = lcn_from_index(vol, ni, folio->index); + if (lcn <= LCN_HOLE) { + folio_start_writeback(folio); + folio_unlock(folio); + folio_end_writeback(folio); + return -EIO; + } + + /* Map folio so we can access its contents. */ + kaddr = kmap_local_folio(folio, 0); + /* Clear the page uptodate flag whilst the mst fixups are applied. */ + folio_clear_uptodate(folio); + + for (mft_ofs = 0; mft_ofs < PAGE_SIZE && vcn < end_vcn; + mft_ofs += vol->mft_record_size) { + /* Get the mft record number. */ + mft_no = (((s64)folio->index << PAGE_SHIFT) + mft_ofs) >> + vol->mft_record_size_bits; + vcn = ntfs_mft_no_to_cluster(vol, mft_no); + /* Check whether to write this mft record. */ + tni = NULL; + if (ntfs_may_write_mft_record(vol, mft_no, + (struct mft_record *)(kaddr + mft_ofs), + &tni, &ref_inos[nr_ref_inos])) { + unsigned int mft_record_off = 0; + s64 vcn_off = vcn; + + /* + * Skip $MFT extent mft records and let them being written + * by writeback to avioid deadlocks. the $MFT runlist + * lock must be taken before $MFT extent mrec_lock is taken. + */ + if (tni && tni->nr_extents < 0 && + tni->ext.base_ntfs_ino == NTFS_I(vol->mft_ino)) { + mutex_unlock(&tni->mrec_lock); + atomic_dec(&tni->count); + iput(vol->mft_ino); + continue; + } + + /* + * The record should be written. If a locked ntfs + * inode was returned, add it to the array of locked + * ntfs inodes. + */ + if (tni) + locked_nis[nr_locked_nis++] = tni; + else if (ref_inos[nr_ref_inos]) + nr_ref_inos++; + + if (bio && (mft_ofs != prev_mft_ofs + vol->mft_record_size)) { +flush_bio: + bio->bi_end_io = ntfs_bio_end_io; + submit_bio(bio); + bio = NULL; + } + + if (vol->cluster_size < folio_size(folio)) { + down_write(&ni->runlist.lock); + rl = ntfs_attr_vcn_to_rl(ni, vcn_off, &lcn); + up_write(&ni->runlist.lock); + if (IS_ERR(rl) || lcn < 0) { + err = -EIO; + goto unm_done; + } + + if (bio && + (bio_end_sector(bio) >> (vol->cluster_size_bits - 9)) != + lcn) { + bio->bi_end_io = ntfs_bio_end_io; + submit_bio(bio); + bio = NULL; + } + } + + if (!bio) { + unsigned int off; + + off = ((mft_no << vol->mft_record_size_bits) + + mft_record_off) & vol->cluster_size_mask; + + bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, + GFP_NOIO); + bio->bi_iter.bi_sector = + ntfs_bytes_to_sector(vol, + ntfs_cluster_to_bytes(vol, lcn) + off); + } + + if (vol->cluster_size == NTFS_BLOCK_SIZE && + (mft_record_off || + (rl && rl->length - (vcn_off - rl->vcn) == 1) || + mft_ofs + NTFS_BLOCK_SIZE >= PAGE_SIZE)) + folio_sz = NTFS_BLOCK_SIZE; + else + folio_sz = vol->mft_record_size; + if (!bio_add_folio(bio, folio, folio_sz, + mft_ofs + mft_record_off)) { + err = -EIO; + bio_put(bio); + goto unm_done; + } + mft_record_off += folio_sz; + + if (mft_record_off != vol->mft_record_size) { + vcn_off++; + goto flush_bio; + } + prev_mft_ofs = mft_ofs; + + if (mft_no < vol->mftmirr_size) + ntfs_sync_mft_mirror(vol, mft_no, + (struct mft_record *)(kaddr + mft_ofs)); + } else if (ref_inos[nr_ref_inos]) + nr_ref_inos++; + } + + if (bio) { + bio->bi_end_io = ntfs_bio_end_io; + submit_bio(bio); + } +unm_done: + folio_mark_uptodate(folio); + kunmap_local(kaddr); + + folio_start_writeback(folio); + folio_unlock(folio); + folio_end_writeback(folio); + + /* Unlock any locked inodes. */ + while (nr_locked_nis-- > 0) { + struct ntfs_inode *base_tni; + + tni = locked_nis[nr_locked_nis]; + mutex_unlock(&tni->mrec_lock); + + /* Get the base inode. */ + mutex_lock(&tni->extent_lock); + if (tni->nr_extents >= 0) + base_tni = tni; + else + base_tni = tni->ext.base_ntfs_ino; + mutex_unlock(&tni->extent_lock); + ntfs_debug("Unlocking %s inode 0x%llx.", + tni == base_tni ? "base" : "extent", + tni->mft_no); + atomic_dec(&tni->count); + iput(VFS_I(base_tni)); + } + + /* Dropping deferred references */ + while (nr_ref_inos-- > 0) { + if (ref_inos[nr_ref_inos]) + iput(ref_inos[nr_ref_inos]); + } + + if (unlikely(err && err != -ENOMEM)) + NVolSetErrors(vol); + if (likely(!err)) + ntfs_debug("Done."); + return err; +} + +/* + * ntfs_mft_writepages - Write back dirty folios for the $MFT inode + * @mapping: address space of the $MFT inode + * @wbc: writeback control + * + * Writeback iterator for MFT records. Iterates over dirty folios and + * delegates actual writing to ntfs_write_mft_block() for each folio. + * Called from the address_space_operations .writepages vector of the + * $MFT inode. + * + * Returns 0 on success, or the first error encountered. + */ +int ntfs_mft_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct folio *folio = NULL; + int error; + + if (NVolShutdown(NTFS_I(mapping->host)->vol)) + return -EIO; + + while ((folio = writeback_iter(mapping, wbc, folio, &error))) + error = ntfs_write_mft_block(folio, wbc); + return error; +} + +void ntfs_mft_mark_dirty(struct folio *folio) +{ + iomap_dirty_folio(folio->mapping, folio); +} diff --git a/fs/ntfs/mft.h b/fs/ntfs/mft.h new file mode 100644 index 000000000000..75a51a98d0f6 --- /dev/null +++ b/fs/ntfs/mft.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for mft record handling in NTFS Linux kernel driver. + * + * Copyright (c) 2001-2004 Anton Altaparmakov + */ + +#ifndef _LINUX_NTFS_MFT_H +#define _LINUX_NTFS_MFT_H + +#include +#include + +#include "inode.h" + +struct mft_record *map_mft_record(struct ntfs_inode *ni); +void unmap_mft_record(struct ntfs_inode *ni); +struct mft_record *map_extent_mft_record(struct ntfs_inode *base_ni, u64 mref, + struct ntfs_inode **ntfs_ino); + +static inline void unmap_extent_mft_record(struct ntfs_inode *ni) +{ + unmap_mft_record(ni); +} + +void __mark_mft_record_dirty(struct ntfs_inode *ni); + +/* + * mark_mft_record_dirty - set the mft record and the page containing it dirty + * @ni: ntfs inode describing the mapped mft record + * + * Set the mapped (extent) mft record of the (base or extent) ntfs inode @ni, + * as well as the page containing the mft record, dirty. Also, mark the base + * vfs inode dirty. This ensures that any changes to the mft record are + * written out to disk. + * + * NOTE: Do not do anything if the mft record is already marked dirty. + */ +static inline void mark_mft_record_dirty(struct ntfs_inode *ni) +{ + if (!NInoTestSetDirty(ni)) + __mark_mft_record_dirty(ni); +} + +int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no, + struct mft_record *m); +int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int sync); + +/* + * write_mft_record - write out a mapped (extent) mft record + * @ni: ntfs inode describing the mapped (extent) mft record + * @m: mapped (extent) mft record to write + * @sync: if true, wait for i/o completion + * + * This is just a wrapper for write_mft_record_nolock() (see mft.c), which + * locks the page for the duration of the write. This ensures that there are + * no race conditions between writing the mft record via the dirty inode code + * paths and via the page cache write back code paths or between writing + * neighbouring mft records residing in the same page. + * + * Locking the page also serializes us against ->read_folio() if the page is not + * uptodate. + * + * On success, clean the mft record and return 0. On error, leave the mft + * record dirty and return -errno. + */ +static inline int write_mft_record(struct ntfs_inode *ni, struct mft_record *m, int sync) +{ + struct folio *folio = ni->folio; + int err; + + folio_lock(folio); + err = write_mft_record_nolock(ni, m, sync); + folio_unlock(folio); + + return err; +} + +int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode, + struct ntfs_inode **ni, struct ntfs_inode *base_ni, + struct mft_record **ni_mrec); +int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni); +int ntfs_mft_records_write(const struct ntfs_volume *vol, const u64 mref, + const s64 count, struct mft_record *b); +int ntfs_mft_record_check(const struct ntfs_volume *vol, struct mft_record *m, + u64 mft_no); +int ntfs_mft_writepages(struct address_space *mapping, + struct writeback_control *wbc); +void ntfs_mft_mark_dirty(struct folio *folio); + +#endif /* _LINUX_NTFS_MFT_H */ diff --git a/fs/ntfs/mst.c b/fs/ntfs/mst.c new file mode 100644 index 000000000000..7f9faad924ad --- /dev/null +++ b/fs/ntfs/mst.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS multi sector transfer protection handling code. + * + * Copyright (c) 2001-2004 Anton Altaparmakov + */ + +#include + +#include "ntfs.h" + +/* + * post_read_mst_fixup - deprotect multi sector transfer protected data + * @b: pointer to the data to deprotect + * @size: size in bytes of @b + * + * Perform the necessary post read multi sector transfer fixup and detect the + * presence of incomplete multi sector transfers. - In that case, overwrite the + * magic of the ntfs record header being processed with "BAAD" (in memory only!) + * and abort processing. + * + * Return 0 on success and -EINVAL on error ("BAAD" magic will be present). + * + * NOTE: We consider the absence / invalidity of an update sequence array to + * mean that the structure is not protected at all and hence doesn't need to + * be fixed up. Thus, we return success and not failure in this case. This is + * in contrast to pre_write_mst_fixup(), see below. + */ +int post_read_mst_fixup(struct ntfs_record *b, const u32 size) +{ + u16 usa_ofs, usa_count, usn; + u16 *usa_pos, *data_pos; + + /* Setup the variables. */ + usa_ofs = le16_to_cpu(b->usa_ofs); + /* Decrement usa_count to get number of fixups. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + /* Size and alignment checks. */ + if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || + usa_ofs + (usa_count * 2) > size || + (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) + return 0; + /* Position of usn in update sequence array. */ + usa_pos = (u16 *)b + usa_ofs/sizeof(u16); + /* + * The update sequence number which has to be equal to each of the + * u16 values before they are fixed up. Note no need to care for + * endianness since we are comparing and moving data for on disk + * structures which means the data is consistent. - If it is + * consistenty the wrong endianness it doesn't make any difference. + */ + usn = *usa_pos; + /* + * Position in protected data of first u16 that needs fixing up. + */ + data_pos = (u16 *)b + NTFS_BLOCK_SIZE / sizeof(u16) - 1; + /* + * Check for incomplete multi sector transfer(s). + */ + while (usa_count--) { + if (*data_pos != usn) { + struct mft_record *m = (struct mft_record *)b; + + pr_err_ratelimited("ntfs: Incomplete multi sector transfer detected! (Record magic : 0x%x, mft number : 0x%x, base mft number : 0x%lx, mft in use : %d, data : 0x%x, usn 0x%x)\n", + le32_to_cpu(m->magic), le32_to_cpu(m->mft_record_number), + MREF_LE(m->base_mft_record), m->flags & MFT_RECORD_IN_USE, + *data_pos, usn); + /* + * Incomplete multi sector transfer detected! )-: + * Set the magic to "BAAD" and return failure. + * Note that magic_BAAD is already converted to le32. + */ + b->magic = magic_BAAD; + return -EINVAL; + } + data_pos += NTFS_BLOCK_SIZE / sizeof(u16); + } + /* Re-setup the variables. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + data_pos = (u16 *)b + NTFS_BLOCK_SIZE / sizeof(u16) - 1; + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment position in usa and restore original data from + * the usa into the data buffer. + */ + *data_pos = *(++usa_pos); + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + return 0; +} + +/* + * pre_write_mst_fixup - apply multi sector transfer protection + * @b: pointer to the data to protect + * @size: size in bytes of @b + * + * Perform the necessary pre write multi sector transfer fixup on the data + * pointer to by @b of @size. + * + * Return 0 if fixup applied (success) or -EINVAL if no fixup was performed + * (assumed not needed). This is in contrast to post_read_mst_fixup() above. + * + * NOTE: We consider the absence / invalidity of an update sequence array to + * mean that the structure is not subject to protection and hence doesn't need + * to be fixed up. This means that you have to create a valid update sequence + * array header in the ntfs record before calling this function, otherwise it + * will fail (the header needs to contain the position of the update sequence + * array together with the number of elements in the array). You also need to + * initialise the update sequence number before calling this function + * otherwise a random word will be used (whatever was in the record at that + * position at that time). + */ +int pre_write_mst_fixup(struct ntfs_record *b, const u32 size) +{ + __le16 *usa_pos, *data_pos; + u16 usa_ofs, usa_count, usn; + __le16 le_usn; + + /* Sanity check + only fixup if it makes sense. */ + if (!b || ntfs_is_baad_record(b->magic) || + ntfs_is_hole_record(b->magic)) + return -EINVAL; + /* Setup the variables. */ + usa_ofs = le16_to_cpu(b->usa_ofs); + /* Decrement usa_count to get number of fixups. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + /* Size and alignment checks. */ + if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || + usa_ofs + (usa_count * 2) > size || + (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) + return -EINVAL; + /* Position of usn in update sequence array. */ + usa_pos = (__le16 *)((u8 *)b + usa_ofs); + /* + * Cyclically increment the update sequence number + * (skipping 0 and -1, i.e. 0xffff). + */ + usn = le16_to_cpup(usa_pos) + 1; + if (usn == 0xffff || !usn) + usn = 1; + le_usn = cpu_to_le16(usn); + *usa_pos = le_usn; + /* Position in data of first u16 that needs fixing up. */ + data_pos = (__le16 *)b + NTFS_BLOCK_SIZE/sizeof(__le16) - 1; + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment the position in the usa and save the + * original data from the data buffer into the usa. + */ + *(++usa_pos) = *data_pos; + /* Apply fixup to data. */ + *data_pos = le_usn; + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE / sizeof(__le16); + } + return 0; +} + +/* + * post_write_mst_fixup - fast deprotect multi sector transfer protected data + * @b: pointer to the data to deprotect + * + * Perform the necessary post write multi sector transfer fixup, not checking + * for any errors, because we assume we have just used pre_write_mst_fixup(), + * thus the data will be fine or we would never have gotten here. + */ +void post_write_mst_fixup(struct ntfs_record *b) +{ + __le16 *usa_pos, *data_pos; + + u16 usa_ofs = le16_to_cpu(b->usa_ofs); + u16 usa_count = le16_to_cpu(b->usa_count) - 1; + + /* Position of usn in update sequence array. */ + usa_pos = (__le16 *)b + usa_ofs/sizeof(__le16); + + /* Position in protected data of first u16 that needs fixing up. */ + data_pos = (__le16 *)b + NTFS_BLOCK_SIZE/sizeof(__le16) - 1; + + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment position in usa and restore original data from + * the usa into the data buffer. + */ + *data_pos = *(++usa_pos); + + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(__le16); + } +} diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c new file mode 100644 index 000000000000..96c450e62efc --- /dev/null +++ b/fs/ntfs/namei.c @@ -0,0 +1,1693 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel directory inode operations. + * + * Copyright (c) 2001-2006 Anton Altaparmakov + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#include +#include + +#include "ntfs.h" +#include "time.h" +#include "index.h" +#include "reparse.h" +#include "object_id.h" +#include "ea.h" + +static const __le16 aux_name_le[3] = { + cpu_to_le16('A'), cpu_to_le16('U'), cpu_to_le16('X') +}; + +static const __le16 con_name_le[3] = { + cpu_to_le16('C'), cpu_to_le16('O'), cpu_to_le16('N') +}; + +static const __le16 com_name_le[3] = { + cpu_to_le16('C'), cpu_to_le16('O'), cpu_to_le16('M') +}; + +static const __le16 lpt_name_le[3] = { + cpu_to_le16('L'), cpu_to_le16('P'), cpu_to_le16('T') +}; + +static const __le16 nul_name_le[3] = { + cpu_to_le16('N'), cpu_to_le16('U'), cpu_to_le16('L') +}; + +static const __le16 prn_name_le[3] = { + cpu_to_le16('P'), cpu_to_le16('R'), cpu_to_le16('N') +}; + +static inline int ntfs_check_bad_char(const __le16 *wc, unsigned int wc_len) +{ + int i; + + for (i = 0; i < wc_len; i++) { + u16 c = le16_to_cpu(wc[i]); + + if (c < 0x0020 || + c == 0x0022 || c == 0x002A || c == 0x002F || + c == 0x003A || c == 0x003C || c == 0x003E || + c == 0x003F || c == 0x005C || c == 0x007C) + return -EINVAL; + } + + return 0; +} + +static int ntfs_check_bad_windows_name(struct ntfs_volume *vol, + const __le16 *wc, + unsigned int wc_len) +{ + if (ntfs_check_bad_char(wc, wc_len)) + return -EINVAL; + + if (!NVolCheckWindowsNames(vol)) + return 0; + + /* Check for trailing space or dot. */ + if (wc_len > 0 && + (wc[wc_len - 1] == cpu_to_le16(' ') || + wc[wc_len - 1] == cpu_to_le16('.'))) + return -EINVAL; + + if (wc_len == 3 || (wc_len > 3 && wc[3] == cpu_to_le16('.'))) { + __le16 *upcase = vol->upcase; + u32 size = vol->upcase_len; + + if (ntfs_are_names_equal(wc, 3, aux_name_le, 3, IGNORE_CASE, upcase, size) || + ntfs_are_names_equal(wc, 3, con_name_le, 3, IGNORE_CASE, upcase, size) || + ntfs_are_names_equal(wc, 3, nul_name_le, 3, IGNORE_CASE, upcase, size) || + ntfs_are_names_equal(wc, 3, prn_name_le, 3, IGNORE_CASE, upcase, size)) + return -EINVAL; + } + + if (wc_len == 4 || (wc_len > 4 && wc[4] == cpu_to_le16('.'))) { + __le16 *upcase = vol->upcase; + u32 size = vol->upcase_len, port; + + if (ntfs_are_names_equal(wc, 3, com_name_le, 3, IGNORE_CASE, upcase, size) || + ntfs_are_names_equal(wc, 3, lpt_name_le, 3, IGNORE_CASE, upcase, size)) { + port = le16_to_cpu(wc[3]); + if (port >= '1' && port <= '9') + return -EINVAL; + } + } + return 0; +} + +/* + * ntfs_lookup - find the inode represented by a dentry in a directory inode + * @dir_ino: directory inode in which to look for the inode + * @dent: dentry representing the inode to look for + * @flags: lookup flags + * + * In short, ntfs_lookup() looks for the inode represented by the dentry @dent + * in the directory inode @dir_ino and if found attaches the inode to the + * dentry @dent. + * + * In more detail, the dentry @dent specifies which inode to look for by + * supplying the name of the inode in @dent->d_name.name. ntfs_lookup() + * converts the name to Unicode and walks the contents of the directory inode + * @dir_ino looking for the converted Unicode name. If the name is found in the + * directory, the corresponding inode is loaded by calling ntfs_iget() on its + * inode number and the inode is associated with the dentry @dent via a call to + * d_splice_alias(). + * + * If the name is not found in the directory, a NULL inode is inserted into the + * dentry @dent via a call to d_add(). The dentry is then termed a negative + * dentry. + * + * Only if an actual error occurs, do we return an error via ERR_PTR(). + * + * In order to handle the case insensitivity issues of NTFS with regards to the + * dcache and the dcache requiring only one dentry per directory, we deal with + * dentry aliases that only differ in case in ->ntfs_lookup() while maintaining + * a case sensitive dcache. This means that we get the full benefit of dcache + * speed when the file/directory is looked up with the same case as returned by + * ->ntfs_readdir() but that a lookup for any other case (or for the short file + * name) will not find anything in dcache and will enter ->ntfs_lookup() + * instead, where we search the directory for a fully matching file name + * (including case) and if that is not found, we search for a file name that + * matches with different case and if that has non-POSIX semantics we return + * that. We actually do only one search (case sensitive) and keep tabs on + * whether we have found a case insensitive match in the process. + * + * To simplify matters for us, we do not treat the short vs long filenames as + * two hard links but instead if the lookup matches a short filename, we + * return the dentry for the corresponding long filename instead. + * + * There are three cases we need to distinguish here: + * + * 1) @dent perfectly matches (i.e. including case) a directory entry with a + * file name in the WIN32 or POSIX namespaces. In this case + * ntfs_lookup_inode_by_name() will return with name set to NULL and we + * just d_splice_alias() @dent. + * 2) @dent matches (not including case) a directory entry with a file name in + * the WIN32 namespace. In this case ntfs_lookup_inode_by_name() will return + * with name set to point to a kmalloc()ed ntfs_name structure containing + * the properly cased little endian Unicode name. We convert the name to the + * current NLS code page, search if a dentry with this name already exists + * and if so return that instead of @dent. At this point things are + * complicated by the possibility of 'disconnected' dentries due to NFS + * which we deal with appropriately (see the code comments). The VFS will + * then destroy the old @dent and use the one we returned. If a dentry is + * not found, we allocate a new one, d_splice_alias() it, and return it as + * above. + * 3) @dent matches either perfectly or not (i.e. we don't care about case) a + * directory entry with a file name in the DOS namespace. In this case + * ntfs_lookup_inode_by_name() will return with name set to point to a + * kmalloc()ed ntfs_name structure containing the mft reference (cpu endian) + * of the inode. We use the mft reference to read the inode and to find the + * file name in the WIN32 namespace corresponding to the matched short file + * name. We then convert the name to the current NLS code page, and proceed + * searching for a dentry with this name, etc, as in case 2), above. + * + * Locking: Caller must hold i_mutex on the directory. + */ +static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, + unsigned int flags) +{ + struct ntfs_volume *vol = NTFS_SB(dir_ino->i_sb); + struct inode *dent_inode; + __le16 *uname; + struct ntfs_name *name = NULL; + u64 mref; + unsigned long dent_ino; + int uname_len; + + ntfs_debug("Looking up %pd in directory inode 0x%llx.", + dent, NTFS_I(dir_ino)->mft_no); + /* Convert the name of the dentry to Unicode. */ + uname_len = ntfs_nlstoucs(vol, dent->d_name.name, dent->d_name.len, + &uname, NTFS_MAX_NAME_LEN); + if (uname_len < 0) { + if (uname_len != -ENAMETOOLONG) + ntfs_debug("Failed to convert name to Unicode."); + return ERR_PTR(uname_len); + } + mutex_lock(&NTFS_I(dir_ino)->mrec_lock); + mref = ntfs_lookup_inode_by_name(NTFS_I(dir_ino), uname, uname_len, + &name); + mutex_unlock(&NTFS_I(dir_ino)->mrec_lock); + kmem_cache_free(ntfs_name_cache, uname); + if (!IS_ERR_MREF(mref)) { + dent_ino = MREF(mref); + ntfs_debug("Found inode 0x%lx. Calling ntfs_iget.", dent_ino); + dent_inode = ntfs_iget(vol->sb, dent_ino); + if (!IS_ERR(dent_inode)) { + /* Consistency check. */ + if (MSEQNO(mref) == NTFS_I(dent_inode)->seq_no || + dent_ino == FILE_MFT) { + /* Perfect WIN32/POSIX match. -- Case 1. */ + if (!name) { + ntfs_debug("Done. (Case 1.)"); + return d_splice_alias(dent_inode, dent); + } + /* + * We are too indented. Handle imperfect + * matches and short file names further below. + */ + goto handle_name; + } + ntfs_error(vol->sb, + "Found stale reference to inode 0x%lx (reference sequence number = 0x%x, inode sequence number = 0x%x), returning -EIO. Run chkdsk.", + dent_ino, MSEQNO(mref), + NTFS_I(dent_inode)->seq_no); + iput(dent_inode); + dent_inode = ERR_PTR(-EIO); + } else + ntfs_error(vol->sb, "ntfs_iget(0x%lx) failed with error code %li.", + dent_ino, PTR_ERR(dent_inode)); + kfree(name); + /* Return the error code. */ + return ERR_CAST(dent_inode); + } + kfree(name); + /* It is guaranteed that @name is no longer allocated at this point. */ + if (MREF_ERR(mref) == -ENOENT) { + ntfs_debug("Entry was not found, adding negative dentry."); + /* The dcache will handle negative entries. */ + d_add(dent, NULL); + ntfs_debug("Done."); + return NULL; + } + ntfs_error(vol->sb, "ntfs_lookup_ino_by_name() failed with error code %i.", + -MREF_ERR(mref)); + return ERR_PTR(MREF_ERR(mref)); +handle_name: + { + struct mft_record *m; + struct ntfs_attr_search_ctx *ctx; + struct ntfs_inode *ni = NTFS_I(dent_inode); + int err; + struct qstr nls_name; + + nls_name.name = NULL; + if (name->type != FILE_NAME_DOS) { /* Case 2. */ + ntfs_debug("Case 2."); + nls_name.len = (unsigned int)ntfs_ucstonls(vol, + (__le16 *)&name->name, name->len, + (unsigned char **)&nls_name.name, 0); + kfree(name); + } else /* if (name->type == FILE_NAME_DOS) */ { /* Case 3. */ + struct file_name_attr *fn; + + ntfs_debug("Case 3."); + kfree(name); + + /* Find the WIN32 name corresponding to the matched DOS name. */ + ni = NTFS_I(dent_inode); + m = map_mft_record(ni); + if (IS_ERR(m)) { + err = PTR_ERR(m); + m = NULL; + ctx = NULL; + goto err_out; + } + ctx = ntfs_attr_get_search_ctx(ni, m); + if (unlikely(!ctx)) { + err = -ENOMEM; + goto err_out; + } + do { + struct attr_record *a; + + err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, + NULL, 0, ctx); + if (unlikely(err)) { + ntfs_error(vol->sb, + "Inode corrupt: No WIN32 namespace counterpart to DOS file name. Run chkdsk."); + if (err == -ENOENT) + err = -EIO; + goto err_out; + } + /* Consistency checks. */ + a = ctx->attr; + if (a->non_resident || a->flags) + goto eio_err_out; + fn = (struct file_name_attr *)((u8 *)ctx->attr + le16_to_cpu( + ctx->attr->data.resident.value_offset)); + } while (fn->file_name_type != FILE_NAME_WIN32); + + /* Convert the found WIN32 name to current NLS code page. */ + nls_name.len = (unsigned int)ntfs_ucstonls(vol, + (__le16 *)&fn->file_name, fn->file_name_length, + (unsigned char **)&nls_name.name, 0); + + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(ni); + } + m = NULL; + ctx = NULL; + + /* Check if a conversion error occurred. */ + if ((int)nls_name.len < 0) { + err = (int)nls_name.len; + goto err_out; + } + nls_name.hash = full_name_hash(dent, nls_name.name, nls_name.len); + + dent = d_add_ci(dent, dent_inode, &nls_name); + kfree(nls_name.name); + return dent; + +eio_err_out: + ntfs_error(vol->sb, "Illegal file name attribute. Run chkdsk."); + err = -EIO; +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (m) + unmap_mft_record(ni); + iput(dent_inode); + ntfs_error(vol->sb, "Failed, returning error code %i.", err); + return ERR_PTR(err); + } +} + +static int ntfs_sd_add_everyone(struct ntfs_inode *ni) +{ + struct security_descriptor_relative *sd; + struct ntfs_acl *acl; + struct ntfs_ace *ace; + struct ntfs_sid *sid; + int ret, sd_len; + + /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */ + /* + * Calculate security descriptor length. We have 2 sub-authorities in + * owner and group SIDs, So add 8 bytes to every SID. + */ + sd_len = sizeof(struct security_descriptor_relative) + 2 * + (sizeof(struct ntfs_sid) + 8) + sizeof(struct ntfs_acl) + + sizeof(struct ntfs_ace) + 4; + sd = kmalloc(sd_len, GFP_NOFS); + if (!sd) + return -1; + + sd->revision = 1; + sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE; + + sid = (struct ntfs_sid *)((u8 *)sd + sizeof(struct security_descriptor_relative)); + sid->revision = 1; + sid->sub_authority_count = 2; + sid->sub_authority[0] = cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + sid->identifier_authority.value[5] = 5; + sd->owner = cpu_to_le32((u8 *)sid - (u8 *)sd); + + sid = (struct ntfs_sid *)((u8 *)sid + sizeof(struct ntfs_sid) + 8); + sid->revision = 1; + sid->sub_authority_count = 2; + sid->sub_authority[0] = cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + sid->identifier_authority.value[5] = 5; + sd->group = cpu_to_le32((u8 *)sid - (u8 *)sd); + + acl = (struct ntfs_acl *)((u8 *)sid + sizeof(struct ntfs_sid) + 8); + acl->revision = 2; + acl->size = cpu_to_le16(sizeof(struct ntfs_acl) + sizeof(struct ntfs_ace) + 4); + acl->ace_count = cpu_to_le16(1); + sd->dacl = cpu_to_le32((u8 *)acl - (u8 *)sd); + + ace = (struct ntfs_ace *)((u8 *)acl + sizeof(struct ntfs_acl)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + ace->size = cpu_to_le16(sizeof(struct ntfs_ace) + 4); + ace->mask = cpu_to_le32(0x1f01ff); + ace->sid.revision = 1; + ace->sid.sub_authority_count = 1; + ace->sid.sub_authority[0] = 0; + ace->sid.identifier_authority.value[5] = 1; + + ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8 *)sd, + sd_len); + if (ret) + ntfs_error(ni->vol->sb, "Failed to add SECURITY_DESCRIPTOR\n"); + + kfree(sd); + return ret; +} + +static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *dir, + __le16 *name, u8 name_len, mode_t mode, dev_t dev, + __le16 *target, int target_len) +{ + struct ntfs_inode *dir_ni = NTFS_I(dir); + struct ntfs_volume *vol = dir_ni->vol; + struct ntfs_inode *ni; + bool rollback_data = false, rollback_sd = false, rollback_reparse = false; + struct file_name_attr *fn = NULL; + struct standard_information *si = NULL; + int err = 0, fn_len, si_len; + struct inode *vi; + struct mft_record *ni_mrec, *dni_mrec; + struct super_block *sb = dir_ni->vol->sb; + __le64 parent_mft_ref; + u64 child_mft_ref; + __le16 ea_size; + + vi = new_inode(vol->sb); + if (!vi) + return ERR_PTR(-ENOMEM); + + ntfs_init_big_inode(vi); + ni = NTFS_I(vi); + ni->vol = dir_ni->vol; + ni->name_len = 0; + ni->name = NULL; + + /* + * Set the appropriate mode, attribute type, and name. For + * directories, also setup the index values to the defaults. + */ + if (S_ISDIR(mode)) { + mode &= ~vol->dmask; + + NInoSetMstProtected(ni); + ni->itype.index.block_size = 4096; + ni->itype.index.block_size_bits = ntfs_ffs(4096) - 1; + ni->itype.index.collation_rule = COLLATION_FILE_NAME; + if (vol->cluster_size <= ni->itype.index.block_size) { + ni->itype.index.vcn_size = vol->cluster_size; + ni->itype.index.vcn_size_bits = + vol->cluster_size_bits; + } else { + ni->itype.index.vcn_size = vol->sector_size; + ni->itype.index.vcn_size_bits = + vol->sector_size_bits; + } + } else { + mode &= ~vol->fmask; + } + + if (IS_RDONLY(vi)) + mode &= ~0222; + + inode_init_owner(idmap, vi, dir, mode); + + mode = vi->i_mode; + +#ifdef CONFIG_NTFS_FS_POSIX_ACL + if (!S_ISLNK(mode) && (sb->s_flags & SB_POSIXACL)) { + err = ntfs_init_acl(idmap, vi, dir); + if (err) + goto err_out; + } else +#endif + { + vi->i_flags |= S_NOSEC; + } + + if (uid_valid(vol->uid)) + vi->i_uid = vol->uid; + + if (gid_valid(vol->gid)) + vi->i_gid = vol->gid; + + /* + * Set the file size to 0, the ntfs inode sizes are set to 0 by + * the call to ntfs_init_big_inode() below. + */ + vi->i_size = 0; + vi->i_blocks = 0; + + inode_inc_iversion(vi); + + simple_inode_init_ts(vi); + ni->i_crtime = inode_get_ctime(vi); + + inode_set_mtime_to_ts(dir, ni->i_crtime); + inode_set_ctime_to_ts(dir, ni->i_crtime); + mark_inode_dirty(dir); + + err = ntfs_mft_record_alloc(dir_ni->vol, mode, &ni, NULL, + &ni_mrec); + if (err) { + iput(vi); + return ERR_PTR(err); + } + + /* + * Prevent iget and writeback from finding this inode. + * Caller must call d_instantiate_new instead of d_instantiate. + */ + spin_lock(&vi->i_lock); + inode_state_set(vi, I_NEW | I_CREATING); + spin_unlock(&vi->i_lock); + + /* Add the inode to the inode hash for the superblock. */ + vi->i_ino = (unsigned long)ni->mft_no; + inode_set_iversion(vi, 1); + insert_inode_hash(vi); + + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + mutex_lock_nested(&dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + if (NInoBeingDeleted(dir_ni)) { + err = -ENOENT; + goto err_out; + } + + dni_mrec = map_mft_record(dir_ni); + if (IS_ERR(dni_mrec)) { + ntfs_error(dir_ni->vol->sb, "failed to map mft record for file 0x%llx.\n", + dir_ni->mft_no); + err = -EIO; + goto err_out; + } + parent_mft_ref = MK_LE_MREF(dir_ni->mft_no, + le16_to_cpu(dni_mrec->sequence_number)); + unmap_mft_record(dir_ni); + + /* + * Create STANDARD_INFORMATION attribute. Write STANDARD_INFORMATION + * version 1.2, windows will upgrade it to version 3 if needed. + */ + si_len = offsetof(struct standard_information, file_attributes) + + sizeof(__le32) + 12; + si = kzalloc(si_len, GFP_NOFS); + if (!si) { + err = -ENOMEM; + goto err_out; + } + + si->creation_time = si->last_data_change_time = utc2ntfs(ni->i_crtime); + si->last_mft_change_time = si->last_access_time = si->creation_time; + + if (!S_ISREG(mode) && !S_ISDIR(mode)) + si->file_attributes = FILE_ATTR_SYSTEM; + + /* Add STANDARD_INFORMATION to inode. */ + err = ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, (u8 *)si, + si_len); + if (err) { + ntfs_error(sb, "Failed to add STANDARD_INFORMATION attribute.\n"); + goto err_out; + } + + err = ntfs_sd_add_everyone(ni); + if (err) + goto err_out; + rollback_sd = true; + + if (S_ISDIR(mode)) { + struct index_root *ir = NULL; + struct index_entry *ie; + int ir_len, index_len; + + /* Create struct index_root attribute. */ + index_len = sizeof(struct index_header) + sizeof(struct index_entry_header); + ir_len = offsetof(struct index_root, index) + index_len; + ir = kzalloc(ir_len, GFP_NOFS); + if (!ir) { + err = -ENOMEM; + goto err_out; + } + ir->type = AT_FILE_NAME; + ir->collation_rule = COLLATION_FILE_NAME; + ir->index_block_size = cpu_to_le32(ni->vol->index_record_size); + if (ni->vol->cluster_size <= ni->vol->index_record_size) + ir->clusters_per_index_block = + NTFS_B_TO_CLU(vol, ni->vol->index_record_size); + else + ir->clusters_per_index_block = + ni->vol->index_record_size >> ni->vol->sector_size_bits; + ir->index.entries_offset = cpu_to_le32(sizeof(struct index_header)); + ir->index.index_length = cpu_to_le32(index_len); + ir->index.allocated_size = cpu_to_le32(index_len); + ie = (struct index_entry *)((u8 *)ir + sizeof(struct index_root)); + ie->length = cpu_to_le16(sizeof(struct index_entry_header)); + ie->key_length = 0; + ie->flags = INDEX_ENTRY_END; + + /* Add struct index_root attribute to inode. */ + err = ntfs_attr_add(ni, AT_INDEX_ROOT, I30, 4, (u8 *)ir, ir_len); + if (err) { + kfree(ir); + ntfs_error(vi->i_sb, "Failed to add struct index_root attribute.\n"); + goto err_out; + } + kfree(ir); + err = ntfs_attr_open(ni, AT_INDEX_ROOT, I30, 4); + if (err) + goto err_out; + } else { + /* Add DATA attribute to inode. */ + err = ntfs_attr_add(ni, AT_DATA, AT_UNNAMED, 0, NULL, 0); + if (err) { + ntfs_error(dir_ni->vol->sb, "Failed to add DATA attribute.\n"); + goto err_out; + } + rollback_data = true; + + err = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (err) + goto err_out; + + if (S_ISLNK(mode)) { + err = ntfs_reparse_set_wsl_symlink(ni, target, target_len); + if (!err) + rollback_reparse = true; + } else if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISSOCK(mode) || + S_ISFIFO(mode)) { + si->file_attributes = FILE_ATTRIBUTE_RECALL_ON_OPEN; + ni->flags = FILE_ATTRIBUTE_RECALL_ON_OPEN; + err = ntfs_reparse_set_wsl_not_symlink(ni, mode); + if (!err) + rollback_reparse = true; + } + if (err) + goto err_out; + } + + err = ntfs_ea_set_wsl_inode(vi, dev, &ea_size, + NTFS_EA_UID | NTFS_EA_GID | NTFS_EA_MODE); + if (err) + goto err_out; + + /* Create FILE_NAME attribute. */ + fn_len = sizeof(struct file_name_attr) + name_len * sizeof(__le16); + fn = kzalloc(fn_len, GFP_NOFS); + if (!fn) { + err = -ENOMEM; + goto err_out; + } + + fn->file_attributes |= ni->flags; + fn->parent_directory = parent_mft_ref; + fn->file_name_length = name_len; + fn->file_name_type = FILE_NAME_POSIX; + fn->type.ea.packed_ea_size = ea_size; + if (S_ISDIR(mode)) { + fn->file_attributes = FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT; + fn->allocated_size = fn->data_size = 0; + } else { + fn->data_size = cpu_to_le64(ni->data_size); + fn->allocated_size = cpu_to_le64(ni->allocated_size); + } + if (!S_ISREG(mode) && !S_ISDIR(mode)) { + fn->file_attributes = FILE_ATTR_SYSTEM; + if (rollback_reparse) + fn->file_attributes |= FILE_ATTR_REPARSE_POINT; + } + if (NVolHideDotFiles(vol) && name_len > 0 && name[0] == cpu_to_le16('.')) + fn->file_attributes |= FILE_ATTR_HIDDEN; + fn->creation_time = fn->last_data_change_time = utc2ntfs(ni->i_crtime); + fn->last_mft_change_time = fn->last_access_time = fn->creation_time; + memcpy(fn->file_name, name, name_len * sizeof(__le16)); + + /* Add FILE_NAME attribute to inode. */ + err = ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8 *)fn, fn_len); + if (err) { + ntfs_error(sb, "Failed to add FILE_NAME attribute.\n"); + goto err_out; + } + + child_mft_ref = MK_MREF(ni->mft_no, + le16_to_cpu(ni_mrec->sequence_number)); + /* Set hard links count and directory flag. */ + ni_mrec->link_count = cpu_to_le16(1); + mark_mft_record_dirty(ni); + + /* Add FILE_NAME attribute to index. */ + err = ntfs_index_add_filename(dir_ni, fn, child_mft_ref); + if (err) { + ntfs_debug("Failed to add entry to the index"); + goto err_out; + } + + unmap_mft_record(ni); + mutex_unlock(&dir_ni->mrec_lock); + mutex_unlock(&ni->mrec_lock); + + ni->flags = fn->file_attributes; + /* Set the sequence number. */ + vi->i_generation = ni->seq_no; + set_nlink(vi, 1); + ntfs_set_vfs_operations(vi, mode, dev); + + /* Done! */ + kfree(fn); + kfree(si); + ntfs_debug("Done.\n"); + return ni; + +err_out: + if (rollback_sd) + ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); + + if (rollback_data) + ntfs_attr_remove(ni, AT_DATA, AT_UNNAMED, 0); + + if (rollback_reparse) + ntfs_delete_reparse_index(ni); + /* + * Free extent MFT records (should not exist any with current + * ntfs_create implementation, but for any case if something will be + * changed in the future). + */ + while (ni->nr_extents != 0) { + int err2; + + err2 = ntfs_mft_record_free(ni->vol, *(ni->ext.extent_ntfs_inos)); + if (err2) + ntfs_error(sb, + "Failed to free extent MFT record. Leaving inconsistent metadata.\n"); + ntfs_inode_close(*(ni->ext.extent_ntfs_inos)); + } + if (ntfs_mft_record_free(ni->vol, ni)) + ntfs_error(sb, + "Failed to free MFT record. Leaving inconsistent metadata. Run chkdsk.\n"); + unmap_mft_record(ni); + kfree(fn); + kfree(si); + + mutex_unlock(&dir_ni->mrec_lock); + mutex_unlock(&ni->mrec_lock); + + remove_inode_hash(vi); + discard_new_inode(vi); + return ERR_PTR(err); +} + +static int ntfs_create(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode, bool excl) +{ + struct ntfs_volume *vol = NTFS_SB(dir->i_sb); + struct ntfs_inode *ni; + __le16 *uname; + int uname_len, err; + + if (NVolShutdown(vol)) + return -EIO; + + uname_len = ntfs_nlstoucs(vol, dentry->d_name.name, dentry->d_name.len, + &uname, NTFS_MAX_NAME_LEN); + if (uname_len < 0) { + if (uname_len != -ENAMETOOLONG) + ntfs_error(vol->sb, "Failed to convert name to unicode."); + return uname_len; + } + + err = ntfs_check_bad_windows_name(vol, uname, uname_len); + if (err) { + kmem_cache_free(ntfs_name_cache, uname); + return err; + } + + if (!(vol->vol_flags & VOLUME_IS_DIRTY)) + ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); + + ni = __ntfs_create(idmap, dir, uname, uname_len, S_IFREG | mode, 0, NULL, 0); + kmem_cache_free(ntfs_name_cache, uname); + if (IS_ERR(ni)) + return PTR_ERR(ni); + + d_instantiate_new(dentry, VFS_I(ni)); + + return 0; +} + +static int ntfs_check_unlinkable_dir(struct ntfs_attr_search_ctx *ctx, struct file_name_attr *fn) +{ + int link_count; + int ret; + struct ntfs_inode *ni = ctx->base_ntfs_ino ? ctx->base_ntfs_ino : ctx->ntfs_ino; + struct mft_record *ni_mrec = ctx->base_mrec ? ctx->base_mrec : ctx->mrec; + + ret = ntfs_check_empty_dir(ni, ni_mrec); + if (!ret || ret != -ENOTEMPTY) + return ret; + + link_count = le16_to_cpu(ni_mrec->link_count); + /* + * Directory is non-empty, so we can unlink only if there is more than + * one "real" hard link, i.e. links aren't different DOS and WIN32 names + */ + if ((link_count == 1) || + (link_count == 2 && fn->file_name_type == FILE_NAME_DOS)) { + ret = -ENOTEMPTY; + ntfs_debug("Non-empty directory without hard links\n"); + goto no_hardlink; + } + + ret = 0; +no_hardlink: + return ret; +} + +static int ntfs_test_inode_attr(struct inode *vi, void *data) +{ + struct ntfs_inode *ni = NTFS_I(vi); + u64 mft_no = (u64)(uintptr_t)data; + + if (ni->mft_no != mft_no) + return 0; + if (NInoAttr(ni) || ni->nr_extents == -1) + return 1; + else + return 0; +} + +/* + * ntfs_delete - delete file or directory from ntfs volume + * @ni: ntfs inode for object to delte + * @dir_ni: ntfs inode for directory in which delete object + * @name: unicode name of the object to delete + * @name_len: length of the name in unicode characters + * @need_lock: whether mrec lock is needed or not + * + * Delete the specified name from the directory index @dir_ni and decrement + * the link count of the target inode @ni. + * + * Return 0 on success and -errno on error. + */ +static int ntfs_delete(struct ntfs_inode *ni, struct ntfs_inode *dir_ni, + __le16 *name, u8 name_len, bool need_lock) +{ + struct ntfs_attr_search_ctx *actx = NULL; + struct file_name_attr *fn = NULL; + bool looking_for_dos_name = false, looking_for_win32_name = false; + bool case_sensitive_match = true; + int err = 0; + struct mft_record *ni_mrec; + struct super_block *sb; + bool link_count_zero = false; + + ntfs_debug("Entering.\n"); + + if (need_lock == true) { + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + mutex_lock_nested(&dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + } + + sb = dir_ni->vol->sb; + + if (ni->nr_extents == -1) + ni = ni->ext.base_ntfs_ino; + if (dir_ni->nr_extents == -1) + dir_ni = dir_ni->ext.base_ntfs_ino; + /* + * Search for FILE_NAME attribute with such name. If it's in POSIX or + * WIN32_AND_DOS namespace, then simply remove it from index and inode. + * If filename in DOS or in WIN32 namespace, then remove DOS name first, + * only then remove WIN32 name. + */ + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (!actx) { + ntfs_error(sb, "%s, Failed to get search context", __func__); + if (need_lock) { + mutex_unlock(&dir_ni->mrec_lock); + mutex_unlock(&ni->mrec_lock); + } + return -ENOMEM; + } +search: + while ((err = ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, actx)) == 0) { +#ifdef DEBUG + unsigned char *s; +#endif + bool case_sensitive = IGNORE_CASE; + + fn = (struct file_name_attr *)((u8 *)actx->attr + + le16_to_cpu(actx->attr->data.resident.value_offset)); +#ifdef DEBUG + s = ntfs_attr_name_get(ni->vol, fn->file_name, fn->file_name_length); + ntfs_debug("name: '%s' type: %d dos: %d win32: %d case: %d\n", + s, fn->file_name_type, + looking_for_dos_name, looking_for_win32_name, + case_sensitive_match); + ntfs_attr_name_free(&s); +#endif + if (looking_for_dos_name) { + if (fn->file_name_type == FILE_NAME_DOS) + break; + continue; + } + if (looking_for_win32_name) { + if (fn->file_name_type == FILE_NAME_WIN32) + break; + continue; + } + + /* Ignore hard links from other directories */ + if (dir_ni->mft_no != MREF_LE(fn->parent_directory)) { + ntfs_debug("MFT record numbers don't match (%llu != %lu)\n", + dir_ni->mft_no, + MREF_LE(fn->parent_directory)); + continue; + } + + if (fn->file_name_type == FILE_NAME_POSIX || case_sensitive_match) + case_sensitive = CASE_SENSITIVE; + + if (ntfs_names_are_equal(fn->file_name, fn->file_name_length, + name, name_len, case_sensitive, + ni->vol->upcase, ni->vol->upcase_len)) { + if (fn->file_name_type == FILE_NAME_WIN32) { + looking_for_dos_name = true; + ntfs_attr_reinit_search_ctx(actx); + continue; + } + if (fn->file_name_type == FILE_NAME_DOS) + looking_for_dos_name = true; + break; + } + } + if (err) { + /* + * If case sensitive search failed, then try once again + * ignoring case. + */ + if (err == -ENOENT && case_sensitive_match) { + case_sensitive_match = false; + ntfs_attr_reinit_search_ctx(actx); + goto search; + } + goto err_out; + } + + err = ntfs_check_unlinkable_dir(actx, fn); + if (err) + goto err_out; + + err = ntfs_index_remove(dir_ni, fn, le32_to_cpu(actx->attr->data.resident.value_length)); + if (err) + goto err_out; + + err = ntfs_attr_record_rm(actx); + if (err) + goto err_out; + + ni_mrec = actx->base_mrec ? actx->base_mrec : actx->mrec; + ni_mrec->link_count = cpu_to_le16(le16_to_cpu(ni_mrec->link_count) - 1); + if (!S_ISDIR(VFS_I(ni)->i_mode)) + drop_nlink(VFS_I(ni)); + + mark_mft_record_dirty(ni); + if (looking_for_dos_name) { + looking_for_dos_name = false; + looking_for_win32_name = true; + ntfs_attr_reinit_search_ctx(actx); + goto search; + } + + /* + * For directories, Drop VFS nlink only when mft record link count + * becomes zero. Because we fixes VFS nlink to 1 for directories. + */ + if (S_ISDIR(VFS_I(ni)->i_mode) && !le16_to_cpu(ni_mrec->link_count)) + drop_nlink(VFS_I(ni)); + + /* + * If hard link count is not equal to zero then we are done. In other + * case there are no reference to this inode left, so we should free all + * non-resident attributes and mark all MFT record as not in use. + */ + if (ni_mrec->link_count == 0) { + NInoSetBeingDeleted(ni); + ntfs_delete_reparse_index(ni); + ntfs_delete_object_id_index(ni); + link_count_zero = true; + } + + ntfs_attr_put_search_ctx(actx); + if (need_lock == true) { + mutex_unlock(&dir_ni->mrec_lock); + mutex_unlock(&ni->mrec_lock); + } + + /* + * If hard link count is not equal to zero then we are done. In other + * case there are no reference to this inode left, so we should free all + * non-resident attributes and mark all MFT record as not in use. + */ + if (link_count_zero == true) { + struct inode *attr_vi; + + while ((attr_vi = ilookup5(sb, ni->mft_no, ntfs_test_inode_attr, + (void *)(uintptr_t)ni->mft_no)) != NULL) { + clear_nlink(attr_vi); + iput(attr_vi); + } + } + ntfs_debug("Done.\n"); + return 0; +err_out: + ntfs_attr_put_search_ctx(actx); + if (need_lock) { + mutex_unlock(&dir_ni->mrec_lock); + mutex_unlock(&ni->mrec_lock); + } + return err; +} + +static int ntfs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *vi = dentry->d_inode; + struct super_block *sb = dir->i_sb; + struct ntfs_volume *vol = NTFS_SB(sb); + int err = 0; + struct ntfs_inode *ni = NTFS_I(vi); + __le16 *uname = NULL; + int uname_len; + + if (NVolShutdown(vol)) + return -EIO; + + uname_len = ntfs_nlstoucs(vol, dentry->d_name.name, dentry->d_name.len, + &uname, NTFS_MAX_NAME_LEN); + if (uname_len < 0) { + if (uname_len != -ENAMETOOLONG) + ntfs_error(sb, "Failed to convert name to Unicode."); + return -ENOMEM; + } + + err = ntfs_check_bad_windows_name(vol, uname, uname_len); + if (err) { + kmem_cache_free(ntfs_name_cache, uname); + return err; + } + + if (!(vol->vol_flags & VOLUME_IS_DIRTY)) + ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); + + err = ntfs_delete(ni, NTFS_I(dir), uname, uname_len, true); + if (err) + goto out; + + inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); + mark_inode_dirty(dir); + inode_set_ctime_to_ts(vi, inode_get_ctime(dir)); + if (vi->i_nlink) + mark_inode_dirty(vi); +out: + kmem_cache_free(ntfs_name_cache, uname); + return err; +} + +static struct dentry *ntfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode) +{ + struct super_block *sb = dir->i_sb; + struct ntfs_volume *vol = NTFS_SB(sb); + int err = 0; + struct ntfs_inode *ni; + __le16 *uname; + int uname_len; + + if (NVolShutdown(vol)) + return ERR_PTR(-EIO); + + uname_len = ntfs_nlstoucs(vol, dentry->d_name.name, dentry->d_name.len, + &uname, NTFS_MAX_NAME_LEN); + if (uname_len < 0) { + if (uname_len != -ENAMETOOLONG) + ntfs_error(sb, "Failed to convert name to unicode."); + return ERR_PTR(-ENOMEM); + } + + err = ntfs_check_bad_windows_name(vol, uname, uname_len); + if (err) { + kmem_cache_free(ntfs_name_cache, uname); + return ERR_PTR(err); + } + + if (!(vol->vol_flags & VOLUME_IS_DIRTY)) + ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); + + ni = __ntfs_create(idmap, dir, uname, uname_len, S_IFDIR | mode, 0, NULL, 0); + kmem_cache_free(ntfs_name_cache, uname); + if (IS_ERR(ni)) { + err = PTR_ERR(ni); + return ERR_PTR(err); + } + + d_instantiate_new(dentry, VFS_I(ni)); + return NULL; +} + +static int ntfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *vi = dentry->d_inode; + struct super_block *sb = dir->i_sb; + struct ntfs_volume *vol = NTFS_SB(sb); + int err = 0; + struct ntfs_inode *ni; + __le16 *uname = NULL; + int uname_len; + + if (NVolShutdown(vol)) + return -EIO; + + ni = NTFS_I(vi); + uname_len = ntfs_nlstoucs(vol, dentry->d_name.name, dentry->d_name.len, + &uname, NTFS_MAX_NAME_LEN); + if (uname_len < 0) { + if (uname_len != -ENAMETOOLONG) + ntfs_error(sb, "Failed to convert name to unicode."); + return -ENOMEM; + } + + err = ntfs_check_bad_windows_name(vol, uname, uname_len); + if (err) { + kmem_cache_free(ntfs_name_cache, uname); + return err; + } + + if (!(vol->vol_flags & VOLUME_IS_DIRTY)) + ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); + + err = ntfs_delete(ni, NTFS_I(dir), uname, uname_len, true); + if (err) + goto out; + + inode_set_mtime_to_ts(vi, inode_set_atime_to_ts(vi, current_time(vi))); +out: + kmem_cache_free(ntfs_name_cache, uname); + return err; +} + +/* + * __ntfs_link - create hard link for file or directory + * @ni: ntfs inode for object to create hard link + * @dir_ni: ntfs inode for directory in which new link should be placed + * @name: unicode name of the new link + * @name_len: length of the name in unicode characters + * + * Create a new hard link. This involves adding an entry to the directory + * index and adding a new FILE_NAME attribute to the target inode. + * + * Return 0 on success and -errno on error. + */ +static int __ntfs_link(struct ntfs_inode *ni, struct ntfs_inode *dir_ni, + __le16 *name, u8 name_len) +{ + struct super_block *sb; + struct inode *vi = VFS_I(ni); + struct file_name_attr *fn = NULL; + int fn_len, err = 0; + struct mft_record *dir_mrec = NULL, *ni_mrec = NULL; + + ntfs_debug("Entering.\n"); + + sb = dir_ni->vol->sb; + if (NInoBeingDeleted(dir_ni) || NInoBeingDeleted(ni)) + return -ENOENT; + + ni_mrec = map_mft_record(ni); + if (IS_ERR(ni_mrec)) { + err = -EIO; + goto err_out; + } + + if (le16_to_cpu(ni_mrec->link_count) == 0) { + err = -ENOENT; + goto err_out; + } + + /* Create FILE_NAME attribute. */ + fn_len = sizeof(struct file_name_attr) + name_len * sizeof(__le16); + + fn = kzalloc(fn_len, GFP_NOFS); + if (!fn) { + err = -ENOMEM; + goto err_out; + } + + dir_mrec = map_mft_record(dir_ni); + if (IS_ERR(dir_mrec)) { + err = -EIO; + goto err_out; + } + + fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, + le16_to_cpu(dir_mrec->sequence_number)); + unmap_mft_record(dir_ni); + fn->file_name_length = name_len; + fn->file_name_type = FILE_NAME_POSIX; + fn->file_attributes = ni->flags; + if (ni_mrec->flags & MFT_RECORD_IS_DIRECTORY) { + fn->file_attributes |= FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT; + fn->allocated_size = fn->data_size = 0; + } else { + if (NInoSparse(ni) || NInoCompressed(ni)) + fn->allocated_size = + cpu_to_le64(ni->itype.compressed.size); + else + fn->allocated_size = cpu_to_le64(ni->allocated_size); + fn->data_size = cpu_to_le64(ni->data_size); + } + if (NVolHideDotFiles(dir_ni->vol) && name_len > 0 && name[0] == cpu_to_le16('.')) + fn->file_attributes |= FILE_ATTR_HIDDEN; + + fn->creation_time = utc2ntfs(ni->i_crtime); + fn->last_data_change_time = utc2ntfs(inode_get_mtime(vi)); + fn->last_mft_change_time = utc2ntfs(inode_get_ctime(vi)); + fn->last_access_time = utc2ntfs(inode_get_atime(vi)); + memcpy(fn->file_name, name, name_len * sizeof(__le16)); + + /* Add FILE_NAME attribute to index. */ + err = ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, + le16_to_cpu(ni_mrec->sequence_number))); + if (err) { + ntfs_error(sb, "Failed to add filename to the index"); + goto err_out; + } + /* Add FILE_NAME attribute to inode. */ + err = ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8 *)fn, fn_len); + if (err) { + ntfs_error(sb, "Failed to add FILE_NAME attribute.\n"); + /* Try to remove just added attribute from index. */ + if (ntfs_index_remove(dir_ni, fn, fn_len)) + goto rollback_failed; + goto err_out; + } + /* Increment hard links count. */ + ni_mrec->link_count = cpu_to_le16(le16_to_cpu(ni_mrec->link_count) + 1); + if (!S_ISDIR(vi->i_mode)) + inc_nlink(VFS_I(ni)); + + /* Done! */ + mark_mft_record_dirty(ni); + kfree(fn); + unmap_mft_record(ni); + + ntfs_debug("Done.\n"); + + return 0; +rollback_failed: + ntfs_error(sb, "Rollback failed. Leaving inconsistent metadata.\n"); +err_out: + kfree(fn); + if (!IS_ERR_OR_NULL(ni_mrec)) + unmap_mft_record(ni); + return err; +} + +static int ntfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, + struct dentry *old_dentry, struct inode *new_dir, + struct dentry *new_dentry, unsigned int flags) +{ + struct inode *old_inode, *new_inode = NULL; + int err = 0; + int is_dir; + struct super_block *sb = old_dir->i_sb; + __le16 *uname_new = NULL; + __le16 *uname_old = NULL; + int new_name_len; + int old_name_len; + struct ntfs_volume *vol = NTFS_SB(sb); + struct ntfs_inode *old_ni, *new_ni = NULL; + struct ntfs_inode *old_dir_ni = NTFS_I(old_dir), *new_dir_ni = NTFS_I(new_dir); + + if (NVolShutdown(old_dir_ni->vol)) + return -EIO; + + if (flags & (RENAME_EXCHANGE | RENAME_WHITEOUT)) + return -EINVAL; + + new_name_len = ntfs_nlstoucs(NTFS_I(new_dir)->vol, new_dentry->d_name.name, + new_dentry->d_name.len, &uname_new, + NTFS_MAX_NAME_LEN); + if (new_name_len < 0) { + if (new_name_len != -ENAMETOOLONG) + ntfs_error(sb, "Failed to convert name to unicode."); + return -ENOMEM; + } + + err = ntfs_check_bad_windows_name(vol, uname_new, new_name_len); + if (err) { + kmem_cache_free(ntfs_name_cache, uname_new); + return err; + } + + old_name_len = ntfs_nlstoucs(NTFS_I(old_dir)->vol, old_dentry->d_name.name, + old_dentry->d_name.len, &uname_old, + NTFS_MAX_NAME_LEN); + if (old_name_len < 0) { + kmem_cache_free(ntfs_name_cache, uname_new); + if (old_name_len != -ENAMETOOLONG) + ntfs_error(sb, "Failed to convert name to unicode."); + return -ENOMEM; + } + + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; + old_ni = NTFS_I(old_inode); + + if (!(vol->vol_flags & VOLUME_IS_DIRTY)) + ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); + + mutex_lock_nested(&old_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + + if (NInoBeingDeleted(old_ni) || NInoBeingDeleted(old_dir_ni)) { + err = -ENOENT; + goto unlock_old; + } + + is_dir = S_ISDIR(old_inode->i_mode); + + if (new_inode) { + new_ni = NTFS_I(new_inode); + mutex_lock_nested(&new_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL_2); + if (old_dir != new_dir) { + mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2); + if (NInoBeingDeleted(new_dir_ni)) { + err = -ENOENT; + goto err_out; + } + } + + if (NInoBeingDeleted(new_ni)) { + err = -ENOENT; + goto err_out; + } + + if (is_dir) { + struct mft_record *ni_mrec; + + ni_mrec = map_mft_record(NTFS_I(new_inode)); + if (IS_ERR(ni_mrec)) { + err = -EIO; + goto err_out; + } + err = ntfs_check_empty_dir(NTFS_I(new_inode), ni_mrec); + unmap_mft_record(NTFS_I(new_inode)); + if (err) + goto err_out; + } + + err = ntfs_delete(new_ni, new_dir_ni, uname_new, new_name_len, false); + if (err) + goto err_out; + } else { + if (old_dir != new_dir) { + mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2); + if (NInoBeingDeleted(new_dir_ni)) { + err = -ENOENT; + goto err_out; + } + } + } + + err = __ntfs_link(old_ni, new_dir_ni, uname_new, new_name_len); + if (err) + goto err_out; + + err = ntfs_delete(old_ni, old_dir_ni, uname_old, old_name_len, false); + if (err) { + int err2; + + ntfs_error(sb, "Failed to delete old ntfs inode(%llu) in old dir, err : %d\n", + old_ni->mft_no, err); + err2 = ntfs_delete(old_ni, new_dir_ni, uname_new, new_name_len, false); + if (err2) + ntfs_error(sb, "Failed to delete old ntfs inode in new dir, err : %d\n", + err2); + goto err_out; + } + + simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry); + mark_inode_dirty(old_inode); + mark_inode_dirty(old_dir); + if (old_dir != new_dir) + mark_inode_dirty(new_dir); + if (new_inode) + mark_inode_dirty(old_inode); + + inode_inc_iversion(new_dir); + +err_out: + if (old_dir != new_dir) + mutex_unlock(&new_dir_ni->mrec_lock); + if (new_inode) + mutex_unlock(&new_ni->mrec_lock); + +unlock_old: + mutex_unlock(&old_dir_ni->mrec_lock); + mutex_unlock(&old_ni->mrec_lock); + if (uname_new) + kmem_cache_free(ntfs_name_cache, uname_new); + if (uname_old) + kmem_cache_free(ntfs_name_cache, uname_old); + + return err; +} + +static int ntfs_symlink(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, const char *symname) +{ + struct super_block *sb = dir->i_sb; + struct ntfs_volume *vol = NTFS_SB(sb); + struct inode *vi; + int err = 0; + struct ntfs_inode *ni; + __le16 *usrc; + __le16 *utarget; + int usrc_len; + int utarget_len; + int symlen = strlen(symname); + + if (NVolShutdown(vol)) + return -EIO; + + usrc_len = ntfs_nlstoucs(vol, dentry->d_name.name, + dentry->d_name.len, &usrc, NTFS_MAX_NAME_LEN); + if (usrc_len < 0) { + if (usrc_len != -ENAMETOOLONG) + ntfs_error(sb, "Failed to convert name to Unicode."); + err = -ENOMEM; + goto out; + } + + err = ntfs_check_bad_windows_name(vol, usrc, usrc_len); + if (err) { + kmem_cache_free(ntfs_name_cache, usrc); + goto out; + } + + utarget_len = ntfs_nlstoucs(vol, symname, symlen, &utarget, + PATH_MAX); + if (utarget_len < 0) { + if (utarget_len != -ENAMETOOLONG) + ntfs_error(sb, "Failed to convert target name to Unicode."); + err = -ENOMEM; + kmem_cache_free(ntfs_name_cache, usrc); + goto out; + } + + if (!(vol->vol_flags & VOLUME_IS_DIRTY)) + ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); + + ni = __ntfs_create(idmap, dir, usrc, usrc_len, S_IFLNK | 0777, 0, + utarget, utarget_len); + kmem_cache_free(ntfs_name_cache, usrc); + kvfree(utarget); + if (IS_ERR(ni)) { + err = PTR_ERR(ni); + goto out; + } + + vi = VFS_I(ni); + vi->i_size = symlen; + d_instantiate_new(dentry, vi); +out: + return err; +} + +static int ntfs_mknod(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode, dev_t rdev) +{ + struct super_block *sb = dir->i_sb; + struct ntfs_volume *vol = NTFS_SB(sb); + int err = 0; + struct ntfs_inode *ni; + __le16 *uname = NULL; + int uname_len; + + if (NVolShutdown(vol)) + return -EIO; + + uname_len = ntfs_nlstoucs(vol, dentry->d_name.name, + dentry->d_name.len, &uname, NTFS_MAX_NAME_LEN); + if (uname_len < 0) { + if (uname_len != -ENAMETOOLONG) + ntfs_error(sb, "Failed to convert name to Unicode."); + return -ENOMEM; + } + + err = ntfs_check_bad_windows_name(vol, uname, uname_len); + if (err) { + kmem_cache_free(ntfs_name_cache, uname); + return err; + } + + if (!(vol->vol_flags & VOLUME_IS_DIRTY)) + ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); + + switch (mode & S_IFMT) { + case S_IFCHR: + case S_IFBLK: + ni = __ntfs_create(idmap, dir, uname, uname_len, + mode, rdev, NULL, 0); + break; + default: + ni = __ntfs_create(idmap, dir, uname, uname_len, + mode, 0, NULL, 0); + } + + kmem_cache_free(ntfs_name_cache, uname); + if (IS_ERR(ni)) { + err = PTR_ERR(ni); + goto out; + } + + d_instantiate_new(dentry, VFS_I(ni)); +out: + return err; +} + +static int ntfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + struct inode *vi = old_dentry->d_inode; + struct super_block *sb = vi->i_sb; + struct ntfs_volume *vol = NTFS_SB(sb); + __le16 *uname = NULL; + int uname_len; + int err; + struct ntfs_inode *ni = NTFS_I(vi), *dir_ni = NTFS_I(dir); + + if (NVolShutdown(vol)) + return -EIO; + + uname_len = ntfs_nlstoucs(vol, dentry->d_name.name, + dentry->d_name.len, &uname, NTFS_MAX_NAME_LEN); + if (uname_len < 0) { + if (uname_len != -ENAMETOOLONG) + ntfs_error(sb, "Failed to convert name to unicode."); + err = -ENOMEM; + goto out; + } + + if (!(vol->vol_flags & VOLUME_IS_DIRTY)) + ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); + + ihold(vi); + mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL); + mutex_lock_nested(&dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT); + err = __ntfs_link(NTFS_I(vi), NTFS_I(dir), uname, uname_len); + if (err) { + mutex_unlock(&dir_ni->mrec_lock); + mutex_unlock(&ni->mrec_lock); + iput(vi); + pr_err("failed to create link, err = %d\n", err); + goto out; + } + + inode_inc_iversion(dir); + simple_inode_init_ts(dir); + + inode_inc_iversion(vi); + simple_inode_init_ts(vi); + + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + d_instantiate(dentry, vi); + mutex_unlock(&dir_ni->mrec_lock); + mutex_unlock(&ni->mrec_lock); + +out: + kfree(uname); + return err; +} + +/* + * Inode operations for directories. + */ +const struct inode_operations ntfs_dir_inode_ops = { + .lookup = ntfs_lookup, /* VFS: Lookup directory. */ + .create = ntfs_create, + .unlink = ntfs_unlink, + .mkdir = ntfs_mkdir, + .rmdir = ntfs_rmdir, + .rename = ntfs_rename, + .get_acl = ntfs_get_acl, + .set_acl = ntfs_set_acl, + .listxattr = ntfs_listxattr, + .setattr = ntfs_setattr, + .getattr = ntfs_getattr, + .symlink = ntfs_symlink, + .mknod = ntfs_mknod, + .link = ntfs_link, +}; + +/* + * ntfs_get_parent - find the dentry of the parent of a given directory dentry + * @child_dent: dentry of the directory whose parent directory to find + * + * Find the dentry for the parent directory of the directory specified by the + * dentry @child_dent. This function is called from + * fs/exportfs/expfs.c::find_exported_dentry() which in turn is called from the + * default ->decode_fh() which is export_decode_fh() in the same file. + * + * Note: ntfs_get_parent() is called with @d_inode(child_dent)->i_mutex down. + * + * Return the dentry of the parent directory on success or the error code on + * error (IS_ERR() is true). + */ +static struct dentry *ntfs_get_parent(struct dentry *child_dent) +{ + struct inode *vi = d_inode(child_dent); + struct ntfs_inode *ni = NTFS_I(vi); + struct mft_record *mrec; + struct ntfs_attr_search_ctx *ctx; + struct attr_record *attr; + struct file_name_attr *fn; + unsigned long parent_ino; + int err; + + ntfs_debug("Entering for inode 0x%llx.", ni->mft_no); + /* Get the mft record of the inode belonging to the child dentry. */ + mrec = map_mft_record(ni); + if (IS_ERR(mrec)) + return ERR_CAST(mrec); + /* Find the first file name attribute in the mft record. */ + ctx = ntfs_attr_get_search_ctx(ni, mrec); + if (unlikely(!ctx)) { + unmap_mft_record(ni); + return ERR_PTR(-ENOMEM); + } +try_next: + err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL, + 0, ctx); + if (unlikely(err)) { + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(ni); + if (err == -ENOENT) + ntfs_error(vi->i_sb, + "Inode 0x%llx does not have a file name attribute. Run chkdsk.", + ni->mft_no); + return ERR_PTR(err); + } + attr = ctx->attr; + if (unlikely(attr->non_resident)) + goto try_next; + fn = (struct file_name_attr *)((u8 *)attr + + le16_to_cpu(attr->data.resident.value_offset)); + if (unlikely((u8 *)fn + le32_to_cpu(attr->data.resident.value_length) > + (u8 *)attr + le32_to_cpu(attr->length))) + goto try_next; + /* Get the inode number of the parent directory. */ + parent_ino = MREF_LE(fn->parent_directory); + /* Release the search context and the mft record of the child. */ + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(ni); + + return d_obtain_alias(ntfs_iget(vi->i_sb, parent_ino)); +} + +static struct inode *ntfs_nfs_get_inode(struct super_block *sb, + u64 ino, u32 generation) +{ + struct inode *inode; + + inode = ntfs_iget(sb, ino); + if (!IS_ERR(inode)) { + if (inode->i_generation != generation) { + iput(inode); + inode = ERR_PTR(-ESTALE); + } + } + + return inode; +} + +static struct dentry *ntfs_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + ntfs_nfs_get_inode); +} + +static struct dentry *ntfs_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + ntfs_nfs_get_inode); +} + +/* + * Export operations allowing NFS exporting of mounted NTFS partitions. + */ +const struct export_operations ntfs_export_ops = { + .encode_fh = generic_encode_ino32_fh, + .get_parent = ntfs_get_parent, /* Find the parent of a given directory. */ + .fh_to_dentry = ntfs_fh_to_dentry, + .fh_to_parent = ntfs_fh_to_parent, +}; diff --git a/fs/ntfs/ntfs.h b/fs/ntfs/ntfs.h new file mode 100644 index 000000000000..45064dbcc2e4 --- /dev/null +++ b/fs/ntfs/ntfs.h @@ -0,0 +1,294 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for NTFS Linux kernel driver. + * + * Copyright (c) 2001-2014 Anton Altaparmakov and Tuxera Inc. + * Copyright (C) 2002 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#ifndef _LINUX_NTFS_H +#define _LINUX_NTFS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "volume.h" +#include "layout.h" +#include "inode.h" + +#ifdef pr_fmt +#undef pr_fmt +#endif + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* + * Default pre-allocation size is optimize runlist merge overhead + * with small chunk size. + */ +#define NTFS_DEF_PREALLOC_SIZE 65536 + +/* + * The log2 of the standard number of clusters per compression block. + * A value of 4 corresponds to 16 clusters (1 << 4), which is the + * default chunk size used by NTFS LZNT1 compression. + */ +#define STANDARD_COMPRESSION_UNIT 4 + +/* + * The maximum cluster size (4KB) allowed for compression to be enabled. + * By design, NTFS does not support compression on volumes where the + * cluster size exceeds 4096 bytes. + */ +#define MAX_COMPRESSION_CLUSTER_SIZE 4096 + +#define NTFS_B_TO_CLU(vol, b) ((b) >> (vol)->cluster_size_bits) +#define NTFS_CLU_TO_B(vol, clu) ((u64)(clu) << (vol)->cluster_size_bits) +#define NTFS_B_TO_CLU_OFS(vol, clu) ((u64)(clu) & (vol)->cluster_size_mask) + +#define NTFS_MFT_NR_TO_CLU(vol, mft_no) (((u64)mft_no << (vol)->mft_record_size_bits) >> \ + (vol)->cluster_size_bits) +#define NTFS_MFT_NR_TO_PIDX(vol, mft_no) (mft_no >> (PAGE_SHIFT - \ + (vol)->mft_record_size_bits)) +#define NTFS_MFT_NR_TO_POFS(vol, mft_no) (((u64)mft_no << (vol)->mft_record_size_bits) & \ + ~PAGE_MASK) + +#define NTFS_PIDX_TO_BLK(vol, idx) (((u64)idx << PAGE_SHIFT) >> \ + ((vol)->sb)->s_blocksize_bits) +#define NTFS_PIDX_TO_CLU(vol, idx) (((u64)idx << PAGE_SHIFT) >> \ + (vol)->cluster_size_bits) +#define NTFS_CLU_TO_PIDX(vol, clu) (((u64)(clu) << (vol)->cluster_size_bits) >> \ + PAGE_SHIFT) +#define NTFS_CLU_TO_POFS(vol, clu) (((u64)(clu) << (vol)->cluster_size_bits) & \ + ~PAGE_MASK) + +#define NTFS_B_TO_SECTOR(vol, b) ((b) >> ((vol)->sb)->s_blocksize_bits) + +enum { + NTFS_BLOCK_SIZE = 512, + NTFS_BLOCK_SIZE_BITS = 9, + NTFS_SB_MAGIC = 0x5346544e, /* 'NTFS' */ + NTFS_MAX_NAME_LEN = 255, + NTFS_MAX_LABEL_LEN = 128, +}; + +enum { + CASE_SENSITIVE = 0, + IGNORE_CASE = 1, +}; + +/* + * Conversion helpers for NTFS units. + */ + +/* Convert bytes to cluster count */ +static inline u64 ntfs_bytes_to_cluster(const struct ntfs_volume *vol, + s64 bytes) +{ + return bytes >> vol->cluster_size_bits; +} + +/* Convert cluster count to bytes */ +static inline u64 ntfs_cluster_to_bytes(const struct ntfs_volume *vol, + u64 clusters) +{ + return clusters << vol->cluster_size_bits; +} + +/* Get the byte offset within a cluster from a linear byte address */ +static inline u64 ntfs_bytes_to_cluster_off(const struct ntfs_volume *vol, + u64 bytes) +{ + return bytes & vol->cluster_size_mask; +} + +/* Calculate the physical cluster number containing a specific MFT record. */ +static inline u64 ntfs_mft_no_to_cluster(const struct ntfs_volume *vol, + unsigned long mft_no) +{ + return ((u64)mft_no << vol->mft_record_size_bits) >> + vol->cluster_size_bits; +} + +/* Calculate the folio index where the MFT record resides. */ +static inline pgoff_t ntfs_mft_no_to_pidx(const struct ntfs_volume *vol, + unsigned long mft_no) +{ + return mft_no >> (PAGE_SHIFT - vol->mft_record_size_bits); +} + +/* Calculate the byte offset within a folio for an MFT record. */ +static inline u64 ntfs_mft_no_to_poff(const struct ntfs_volume *vol, + unsigned long mft_no) +{ + return ((u64)mft_no << vol->mft_record_size_bits) & ~PAGE_MASK; +} + +/* Convert folio index to cluster number. */ +static inline u64 ntfs_pidx_to_cluster(const struct ntfs_volume *vol, + pgoff_t idx) +{ + return ((u64)idx << PAGE_SHIFT) >> vol->cluster_size_bits; +} + +/* Convert cluster number to folio index. */ +static inline pgoff_t ntfs_cluster_to_pidx(const struct ntfs_volume *vol, + u64 clu) +{ + return (clu << vol->cluster_size_bits) >> PAGE_SHIFT; +} + +/* Get the byte offset within a folio from a cluster number */ +static inline u64 ntfs_cluster_to_poff(const struct ntfs_volume *vol, + u64 clu) +{ + return (clu << vol->cluster_size_bits) & ~PAGE_MASK; +} + +/* Convert byte offset to sector (block) number. */ +static inline sector_t ntfs_bytes_to_sector(const struct ntfs_volume *vol, + u64 bytes) +{ + return bytes >> vol->sb->s_blocksize_bits; +} + +/* Global variables. */ + +/* Slab caches (from super.c). */ +extern struct kmem_cache *ntfs_name_cache; +extern struct kmem_cache *ntfs_inode_cache; +extern struct kmem_cache *ntfs_big_inode_cache; +extern struct kmem_cache *ntfs_attr_ctx_cache; +extern struct kmem_cache *ntfs_index_ctx_cache; + +/* The various operations structs defined throughout the driver files. */ +extern const struct address_space_operations ntfs_aops; +extern const struct address_space_operations ntfs_mft_aops; + +extern const struct file_operations ntfs_file_ops; +extern const struct inode_operations ntfs_file_inode_ops; +extern const struct inode_operations ntfs_symlink_inode_operations; +extern const struct inode_operations ntfs_special_inode_operations; + +extern const struct file_operations ntfs_dir_ops; +extern const struct inode_operations ntfs_dir_inode_ops; + +extern const struct file_operations ntfs_empty_file_ops; +extern const struct inode_operations ntfs_empty_inode_ops; + +extern const struct export_operations ntfs_export_ops; + +/* + * NTFS_SB - return the ntfs volume given a vfs super block + * @sb: VFS super block + * + * NTFS_SB() returns the ntfs volume associated with the VFS super block @sb. + */ +static inline struct ntfs_volume *NTFS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +/* Declarations of functions and global variables. */ + +/* From fs/ntfs/compress.c */ +int ntfs_read_compressed_block(struct folio *folio); +int allocate_compression_buffers(void); +void free_compression_buffers(void); +int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count, + struct iov_iter *from); + +/* From fs/ntfs/super.c */ +#define default_upcase_len 0x10000 +extern struct mutex ntfs_lock; + +struct option_t { + int val; + char *str; +}; +extern const struct option_t on_errors_arr[]; +int ntfs_set_volume_flags(struct ntfs_volume *vol, __le16 flags); +int ntfs_clear_volume_flags(struct ntfs_volume *vol, __le16 flags); +int ntfs_write_volume_label(struct ntfs_volume *vol, char *label); + +/* From fs/ntfs/mst.c */ +int post_read_mst_fixup(struct ntfs_record *b, const u32 size); +int pre_write_mst_fixup(struct ntfs_record *b, const u32 size); +void post_write_mst_fixup(struct ntfs_record *b); + +/* From fs/ntfs/unistr.c */ +bool ntfs_are_names_equal(const __le16 *s1, size_t s1_len, + const __le16 *s2, size_t s2_len, + const u32 ic, + const __le16 *upcase, const u32 upcase_size); +int ntfs_collate_names(const __le16 *name1, const u32 name1_len, + const __le16 *name2, const u32 name2_len, + const int err_val, const u32 ic, + const __le16 *upcase, const u32 upcase_len); +int ntfs_ucsncmp(const __le16 *s1, const __le16 *s2, size_t n); +int ntfs_ucsncasecmp(const __le16 *s1, const __le16 *s2, size_t n, + const __le16 *upcase, const u32 upcase_size); +int ntfs_file_compare_values(const struct file_name_attr *file_name_attr1, + const struct file_name_attr *file_name_attr2, + const int err_val, const u32 ic, + const __le16 *upcase, const u32 upcase_len); +int ntfs_nlstoucs(const struct ntfs_volume *vol, const char *ins, + const int ins_len, __le16 **outs, int max_name_len); +int ntfs_ucstonls(const struct ntfs_volume *vol, const __le16 *ins, + const int ins_len, unsigned char **outs, int outs_len); +__le16 *ntfs_ucsndup(const __le16 *s, u32 maxlen); +bool ntfs_names_are_equal(const __le16 *s1, size_t s1_len, + const __le16 *s2, size_t s2_len, + const u32 ic, + const __le16 *upcase, const u32 upcase_size); +int ntfs_force_shutdown(struct super_block *sb, u32 flags); +long ntfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +long ntfs_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg); +#endif + +/* From fs/ntfs/upcase.c */ +__le16 *generate_default_upcase(void); + +static inline int ntfs_ffs(int x) +{ + int r = 1; + + if (!x) + return 0; + if (!(x & 0xffff)) { + x >>= 16; + r += 16; + } + if (!(x & 0xff)) { + x >>= 8; + r += 8; + } + if (!(x & 0xf)) { + x >>= 4; + r += 4; + } + if (!(x & 3)) { + x >>= 2; + r += 2; + } + if (!(x & 1)) + r += 1; + return r; +} + +/* From fs/ntfs/bdev-io.c */ +int ntfs_bdev_read(struct block_device *bdev, char *data, loff_t start, size_t size); +int ntfs_bdev_write(struct super_block *sb, void *buf, loff_t start, size_t size); + +#endif /* _LINUX_NTFS_H */ diff --git a/fs/ntfs/object_id.c b/fs/ntfs/object_id.c new file mode 100644 index 000000000000..89002c3759df --- /dev/null +++ b/fs/ntfs/object_id.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Pocessing of object ids + * + * Part of this file is based on code from the NTFS-3G. + * + * Copyright (c) 2009-2019 Jean-Pierre Andre + * Copyright (c) 2026 LG Electronics Co., Ltd. + */ + +#include "ntfs.h" +#include "index.h" +#include "object_id.h" + +struct object_id_index_key { + union { + u32 alignment; + struct guid guid; + } object_id; +} __packed; + +struct object_id_index_data { + __le64 file_id; + struct guid birth_volume_id; + struct guid birth_object_id; + struct guid domain_id; +} __packed; + +/* Index entry in $Extend/$ObjId */ +struct object_id_index { + struct index_entry_header header; + struct object_id_index_key key; + struct object_id_index_data data; +} __packed; + +__le16 objid_index_name[] = {cpu_to_le16('$'), cpu_to_le16('O'), 0}; + +/* + * open_object_id_index - Open the $Extend/$ObjId file and its index + * @vol: NTFS volume structure + * + * Opens the $ObjId system file and retrieves its index context. + * + * Return: The index context if opened successfully, or NULL if an error + * occurred. + */ +static struct ntfs_index_context *open_object_id_index(struct ntfs_volume *vol) +{ + struct inode *dir_vi, *vi; + struct ntfs_inode *dir_ni; + struct ntfs_index_context *xo = NULL; + struct ntfs_name *name = NULL; + u64 mref; + int uname_len; + __le16 *uname; + + uname_len = ntfs_nlstoucs(vol, "$ObjId", 6, &uname, + NTFS_MAX_NAME_LEN); + if (uname_len < 0) + return NULL; + + /* do not use path_name_to inode - could reopen root */ + dir_vi = ntfs_iget(vol->sb, FILE_Extend); + if (IS_ERR(dir_vi)) { + kmem_cache_free(ntfs_name_cache, uname); + return NULL; + } + dir_ni = NTFS_I(dir_vi); + + mutex_lock_nested(&dir_ni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); + mref = ntfs_lookup_inode_by_name(dir_ni, uname, uname_len, &name); + mutex_unlock(&dir_ni->mrec_lock); + kfree(name); + kmem_cache_free(ntfs_name_cache, uname); + if (IS_ERR_MREF(mref)) + goto put_dir_vi; + + vi = ntfs_iget(vol->sb, MREF(mref)); + if (IS_ERR(vi)) + goto put_dir_vi; + + xo = ntfs_index_ctx_get(NTFS_I(vi), objid_index_name, 2); + if (!xo) + iput(vi); +put_dir_vi: + iput(dir_vi); + return xo; +} + + +/* + * remove_object_id_index - Remove an object id index entry if attribute present + * @ni: NTFS inode structure containing the attribute + * @xo: Index context for the object id index + * + * Reads the existing object ID attribute and removes it from the index. + * + * Return: 0 on success, or a negative error code on failure. + */ +static int remove_object_id_index(struct ntfs_inode *ni, struct ntfs_index_context *xo) +{ + struct object_id_index_key key = {0}; + s64 size; + + if (ni->data_size == 0) + return -ENODATA; + + /* read the existing object id attribute */ + size = ntfs_inode_attr_pread(VFS_I(ni), 0, sizeof(struct guid), + (char *)&key); + if (size != sizeof(struct guid)) + return -ENODATA; + + if (!ntfs_index_lookup(&key, sizeof(struct object_id_index_key), xo)) + return ntfs_index_rm(xo); + + return 0; +} + +/* + * ntfs_delete_object_id_index - Delete an object_id index entry + * @ni: NTFS inode structure + * + * Opens the object ID index and removes the entry corresponding to the inode. + * + * Return: 0 on success, or a negative error code on failure. + */ +int ntfs_delete_object_id_index(struct ntfs_inode *ni) +{ + struct ntfs_index_context *xo; + struct ntfs_inode *xoni; + struct inode *attr_vi; + int ret = 0; + + attr_vi = ntfs_attr_iget(VFS_I(ni), AT_OBJECT_ID, AT_UNNAMED, 0); + if (IS_ERR(attr_vi)) + return PTR_ERR(attr_vi); + + /* + * read the existing object id and un-index it + */ + xo = open_object_id_index(ni->vol); + if (xo) { + xoni = xo->idx_ni; + mutex_lock_nested(&xoni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); + ret = remove_object_id_index(NTFS_I(attr_vi), xo); + if (!ret) { + ntfs_index_entry_mark_dirty(xo); + mark_mft_record_dirty(xoni); + } + ntfs_index_ctx_put(xo); + mutex_unlock(&xoni->mrec_lock); + iput(VFS_I(xoni)); + } + + iput(attr_vi); + return ret; +} diff --git a/fs/ntfs/object_id.h b/fs/ntfs/object_id.h new file mode 100644 index 000000000000..e4b8e529d5bc --- /dev/null +++ b/fs/ntfs/object_id.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2008-2021 Jean-Pierre Andre + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#ifndef _LINUX_NTFS_OBJECT_ID_H +#define _LINUX_NTFS_OBJECT_ID_H + +extern __le16 objid_index_name[]; + +int ntfs_delete_object_id_index(struct ntfs_inode *ni); + +#endif /* _LINUX_NTFS_OBJECT_ID_H */ diff --git a/fs/ntfs/quota.c b/fs/ntfs/quota.c new file mode 100644 index 000000000000..b443243b58fb --- /dev/null +++ b/fs/ntfs/quota.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel quota ($Quota) handling. + * + * Copyright (c) 2004 Anton Altaparmakov + */ + +#include "index.h" +#include "quota.h" +#include "debug.h" +#include "ntfs.h" + +/* + * ntfs_mark_quotas_out_of_date - mark the quotas out of date on an ntfs volume + * @vol: ntfs volume on which to mark the quotas out of date + * + * Mark the quotas out of date on the ntfs volume @vol and return 'true' on + * success and 'false' on error. + */ +bool ntfs_mark_quotas_out_of_date(struct ntfs_volume *vol) +{ + struct ntfs_index_context *ictx; + struct quota_control_entry *qce; + const __le32 qid = QUOTA_DEFAULTS_ID; + int err; + + ntfs_debug("Entering."); + if (NVolQuotaOutOfDate(vol)) + goto done; + if (!vol->quota_ino || !vol->quota_q_ino) { + ntfs_error(vol->sb, "Quota inodes are not open."); + return false; + } + inode_lock(vol->quota_q_ino); + ictx = ntfs_index_ctx_get(NTFS_I(vol->quota_q_ino), I30, 4); + if (!ictx) { + ntfs_error(vol->sb, "Failed to get index context."); + goto err_out; + } + err = ntfs_index_lookup(&qid, sizeof(qid), ictx); + if (err) { + if (err == -ENOENT) + ntfs_error(vol->sb, "Quota defaults entry is not present."); + else + ntfs_error(vol->sb, "Lookup of quota defaults entry failed."); + goto err_out; + } + if (ictx->data_len < offsetof(struct quota_control_entry, sid)) { + ntfs_error(vol->sb, "Quota defaults entry size is invalid. Run chkdsk."); + goto err_out; + } + qce = (struct quota_control_entry *)ictx->data; + if (le32_to_cpu(qce->version) != QUOTA_VERSION) { + ntfs_error(vol->sb, + "Quota defaults entry version 0x%x is not supported.", + le32_to_cpu(qce->version)); + goto err_out; + } + ntfs_debug("Quota defaults flags = 0x%x.", le32_to_cpu(qce->flags)); + /* If quotas are already marked out of date, no need to do anything. */ + if (qce->flags & QUOTA_FLAG_OUT_OF_DATE) + goto set_done; + /* + * If quota tracking is neither requested, nor enabled and there are no + * pending deletes, no need to mark the quotas out of date. + */ + if (!(qce->flags & (QUOTA_FLAG_TRACKING_ENABLED | + QUOTA_FLAG_TRACKING_REQUESTED | + QUOTA_FLAG_PENDING_DELETES))) + goto set_done; + /* + * Set the QUOTA_FLAG_OUT_OF_DATE bit thus marking quotas out of date. + * This is verified on WinXP to be sufficient to cause windows to + * rescan the volume on boot and update all quota entries. + */ + qce->flags |= QUOTA_FLAG_OUT_OF_DATE; + /* Ensure the modified flags are written to disk. */ + ntfs_index_entry_mark_dirty(ictx); +set_done: + ntfs_index_ctx_put(ictx); + inode_unlock(vol->quota_q_ino); + /* + * We set the flag so we do not try to mark the quotas out of date + * again on remount. + */ + NVolSetQuotaOutOfDate(vol); +done: + ntfs_debug("Done."); + return true; +err_out: + if (ictx) + ntfs_index_ctx_put(ictx); + inode_unlock(vol->quota_q_ino); + return false; +} diff --git a/fs/ntfs/quota.h b/fs/ntfs/quota.h new file mode 100644 index 000000000000..4b7322661a32 --- /dev/null +++ b/fs/ntfs/quota.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for NTFS kernel quota ($Quota) handling. + * + * Copyright (c) 2004 Anton Altaparmakov + */ + +#ifndef _LINUX_NTFS_QUOTA_H +#define _LINUX_NTFS_QUOTA_H + +#include "volume.h" + +bool ntfs_mark_quotas_out_of_date(struct ntfs_volume *vol); + +#endif /* _LINUX_NTFS_QUOTA_H */ diff --git a/fs/ntfs/reparse.c b/fs/ntfs/reparse.c new file mode 100644 index 000000000000..74713716813f --- /dev/null +++ b/fs/ntfs/reparse.c @@ -0,0 +1,574 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Processing of reparse points + * + * Part of this file is based on code from the NTFS-3G. + * + * Copyright (c) 2008-2021 Jean-Pierre Andre + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#include "ntfs.h" +#include "layout.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "volume.h" +#include "mft.h" +#include "index.h" +#include "lcnalloc.h" +#include "reparse.h" + +struct wsl_link_reparse_data { + __le32 type; + char link[]; +}; + +/* Index entry in $Extend/$Reparse */ +struct reparse_index { + struct index_entry_header header; + struct reparse_index_key key; + __le32 filling; +}; + +__le16 reparse_index_name[] = {cpu_to_le16('$'), cpu_to_le16('R'), 0}; + + +/* + * Check if the reparse point attribute buffer is valid. + * Returns true if valid, false otherwise. + */ +static bool ntfs_is_valid_reparse_buffer(struct ntfs_inode *ni, + const struct reparse_point *reparse_attr, size_t size) +{ + size_t expected; + + if (!ni || !reparse_attr) + return false; + + /* Minimum size must cover reparse_point header */ + if (size < sizeof(struct reparse_point)) + return false; + + /* Reserved zero tag is invalid */ + if (reparse_attr->reparse_tag == IO_REPARSE_TAG_RESERVED_ZERO) + return false; + + /* Calculate expected total size */ + expected = sizeof(struct reparse_point) + + le16_to_cpu(reparse_attr->reparse_data_length); + + /* Add GUID size for non-Microsoft tags */ + if (!(reparse_attr->reparse_tag & IO_REPARSE_TAG_IS_MICROSOFT)) + expected += sizeof(struct guid); + + /* Buffer must exactly match the expected size */ + return expected == size; +} + +/* + * Do some sanity checks on reparse data + * + * Microsoft reparse points have an 8-byte header whereas + * non-Microsoft reparse points have a 24-byte header. In each case, + * 'reparse_data_length' must equal the number of non-header bytes. + * + * If the reparse data looks like a junction point or symbolic + * link, more checks can be done. + */ +static bool valid_reparse_data(struct ntfs_inode *ni, + const struct reparse_point *reparse_attr, size_t size) +{ + const struct wsl_link_reparse_data *wsl_reparse_data = + (const struct wsl_link_reparse_data *)reparse_attr->reparse_data; + unsigned int data_len = le16_to_cpu(reparse_attr->reparse_data_length); + + if (ntfs_is_valid_reparse_buffer(ni, reparse_attr, size) == false) + return false; + + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_LX_SYMLINK: + if (data_len <= sizeof(wsl_reparse_data->type) || + wsl_reparse_data->type != cpu_to_le32(2)) + return false; + break; + case IO_REPARSE_TAG_AF_UNIX: + case IO_REPARSE_TAG_LX_FIFO: + case IO_REPARSE_TAG_LX_CHR: + case IO_REPARSE_TAG_LX_BLK: + if (data_len || !(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN)) + return false; + } + + return true; +} + +static unsigned int ntfs_reparse_tag_mode(struct reparse_point *reparse_attr) +{ + unsigned int mode = 0; + + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_SYMLINK: + case IO_REPARSE_TAG_LX_SYMLINK: + mode = S_IFLNK; + break; + case IO_REPARSE_TAG_AF_UNIX: + mode = S_IFSOCK; + break; + case IO_REPARSE_TAG_LX_FIFO: + mode = S_IFIFO; + break; + case IO_REPARSE_TAG_LX_CHR: + mode = S_IFCHR; + break; + case IO_REPARSE_TAG_LX_BLK: + mode = S_IFBLK; + } + + return mode; +} + +/* + * Get the target for symbolic link + */ +unsigned int ntfs_make_symlink(struct ntfs_inode *ni) +{ + s64 attr_size = 0; + unsigned int lth; + struct reparse_point *reparse_attr; + struct wsl_link_reparse_data *wsl_link_data; + unsigned int mode = 0; + + reparse_attr = ntfs_attr_readall(ni, AT_REPARSE_POINT, NULL, 0, + &attr_size); + if (reparse_attr && attr_size && + valid_reparse_data(ni, reparse_attr, attr_size)) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_LX_SYMLINK: + wsl_link_data = + (struct wsl_link_reparse_data *)reparse_attr->reparse_data; + if (wsl_link_data->type == cpu_to_le32(2)) { + lth = le16_to_cpu(reparse_attr->reparse_data_length) - + sizeof(wsl_link_data->type); + ni->target = kvzalloc(lth + 1, GFP_NOFS); + if (ni->target) { + memcpy(ni->target, wsl_link_data->link, lth); + ni->target[lth] = 0; + mode = ntfs_reparse_tag_mode(reparse_attr); + } + } + break; + default: + mode = ntfs_reparse_tag_mode(reparse_attr); + } + } else + ni->flags &= ~FILE_ATTR_REPARSE_POINT; + + if (reparse_attr) + kvfree(reparse_attr); + + return mode; +} + +unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mref) +{ + s64 attr_size = 0; + struct reparse_point *reparse_attr; + unsigned int dt_type = DT_UNKNOWN; + struct inode *vi; + + vi = ntfs_iget(vol->sb, mref); + if (IS_ERR(vi)) + return PTR_ERR(vi); + + reparse_attr = (struct reparse_point *)ntfs_attr_readall(NTFS_I(vi), + AT_REPARSE_POINT, NULL, 0, &attr_size); + + if (reparse_attr && attr_size) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_SYMLINK: + case IO_REPARSE_TAG_LX_SYMLINK: + dt_type = DT_LNK; + break; + case IO_REPARSE_TAG_AF_UNIX: + dt_type = DT_SOCK; + break; + case IO_REPARSE_TAG_LX_FIFO: + dt_type = DT_FIFO; + break; + case IO_REPARSE_TAG_LX_CHR: + dt_type = DT_CHR; + break; + case IO_REPARSE_TAG_LX_BLK: + dt_type = DT_BLK; + } + } + + if (reparse_attr) + kvfree(reparse_attr); + + iput(vi); + return dt_type; +} + +/* + * Set the index for new reparse data + */ +static int set_reparse_index(struct ntfs_inode *ni, struct ntfs_index_context *xr, + __le32 reparse_tag) +{ + struct reparse_index indx; + u64 file_id_cpu; + __le64 file_id; + + file_id_cpu = MK_MREF(ni->mft_no, ni->seq_no); + file_id = cpu_to_le64(file_id_cpu); + indx.header.data.vi.data_offset = + cpu_to_le16(sizeof(struct index_entry_header) + sizeof(struct reparse_index_key)); + indx.header.data.vi.data_length = 0; + indx.header.data.vi.reservedV = 0; + indx.header.length = cpu_to_le16(sizeof(struct reparse_index)); + indx.header.key_length = cpu_to_le16(sizeof(struct reparse_index_key)); + indx.header.flags = 0; + indx.header.reserved = 0; + indx.key.reparse_tag = reparse_tag; + /* danger on processors which require proper alignment! */ + memcpy(&indx.key.file_id, &file_id, 8); + indx.filling = 0; + ntfs_index_ctx_reinit(xr); + + return ntfs_ie_add(xr, (struct index_entry *)&indx); +} + +/* + * Remove a reparse data index entry if attribute present + */ +static int remove_reparse_index(struct inode *rp, struct ntfs_index_context *xr, + __le32 *preparse_tag) +{ + struct reparse_index_key key; + u64 file_id_cpu; + __le64 file_id; + s64 size; + struct ntfs_inode *ni = NTFS_I(rp); + int err = 0, ret = ni->data_size; + + if (ni->data_size == 0) + return 0; + + /* read the existing reparse_tag */ + size = ntfs_inode_attr_pread(rp, 0, 4, (char *)preparse_tag); + if (size != 4) + return -ENODATA; + + file_id_cpu = MK_MREF(ni->mft_no, ni->seq_no); + file_id = cpu_to_le64(file_id_cpu); + key.reparse_tag = *preparse_tag; + /* danger on processors which require proper alignment! */ + memcpy(&key.file_id, &file_id, 8); + if (!ntfs_index_lookup(&key, sizeof(struct reparse_index_key), xr)) { + err = ntfs_index_rm(xr); + if (err) + ret = err; + } + return ret; +} + +/* + * Open the $Extend/$Reparse file and its index + */ +static struct ntfs_index_context *open_reparse_index(struct ntfs_volume *vol) +{ + struct ntfs_index_context *xr = NULL; + u64 mref; + __le16 *uname; + struct ntfs_name *name = NULL; + int uname_len; + struct inode *vi, *dir_vi; + + /* do not use path_name_to inode - could reopen root */ + dir_vi = ntfs_iget(vol->sb, FILE_Extend); + if (IS_ERR(dir_vi)) + return NULL; + + uname_len = ntfs_nlstoucs(vol, "$Reparse", 8, &uname, + NTFS_MAX_NAME_LEN); + if (uname_len < 0) { + iput(dir_vi); + return NULL; + } + + mutex_lock_nested(&NTFS_I(dir_vi)->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); + mref = ntfs_lookup_inode_by_name(NTFS_I(dir_vi), uname, uname_len, + &name); + mutex_unlock(&NTFS_I(dir_vi)->mrec_lock); + kfree(name); + kmem_cache_free(ntfs_name_cache, uname); + if (IS_ERR_MREF(mref)) + goto put_dir_vi; + + vi = ntfs_iget(vol->sb, MREF(mref)); + if (IS_ERR(vi)) + goto put_dir_vi; + + xr = ntfs_index_ctx_get(NTFS_I(vi), reparse_index_name, 2); + if (!xr) + iput(vi); +put_dir_vi: + iput(dir_vi); + return xr; +} + + +/* + * Update the reparse data and index + * + * The reparse data attribute should have been created, and + * an existing index is expected if there is an existing value. + * + */ +static int update_reparse_data(struct ntfs_inode *ni, struct ntfs_index_context *xr, + char *value, size_t size) +{ + struct inode *rp_inode; + int err = 0; + s64 written; + int oldsize; + __le32 reparse_tag; + struct ntfs_inode *rp_ni; + + rp_inode = ntfs_attr_iget(VFS_I(ni), AT_REPARSE_POINT, AT_UNNAMED, 0); + if (IS_ERR(rp_inode)) + return -EINVAL; + rp_ni = NTFS_I(rp_inode); + + /* remove the existing reparse data */ + oldsize = remove_reparse_index(rp_inode, xr, &reparse_tag); + if (oldsize < 0) { + err = oldsize; + goto put_rp_inode; + } + + /* overwrite value if any */ + written = ntfs_inode_attr_pwrite(rp_inode, 0, size, value, false); + if (written != size) { + ntfs_error(ni->vol->sb, "Failed to update reparse data\n"); + err = -EIO; + goto put_rp_inode; + } + + if (set_reparse_index(ni, xr, ((const struct reparse_point *)value)->reparse_tag) && + oldsize > 0) { + /* + * If cannot index, try to remove the reparse + * data and log the error. There will be an + * inconsistency if removal fails. + */ + ntfs_attr_rm(rp_ni); + ntfs_error(ni->vol->sb, + "Failed to index reparse data. Possible corruption.\n"); + } + + mark_mft_record_dirty(ni); +put_rp_inode: + iput(rp_inode); + + return err; +} + +/* + * Delete a reparse index entry + */ +int ntfs_delete_reparse_index(struct ntfs_inode *ni) +{ + struct inode *vi; + struct ntfs_index_context *xr; + struct ntfs_inode *xrni; + __le32 reparse_tag; + int err = 0; + + if (!(ni->flags & FILE_ATTR_REPARSE_POINT)) + return 0; + + vi = ntfs_attr_iget(VFS_I(ni), AT_REPARSE_POINT, AT_UNNAMED, 0); + if (IS_ERR(vi)) + return PTR_ERR(vi); + + /* + * read the existing reparse data (the tag is enough) + * and un-index it + */ + xr = open_reparse_index(ni->vol); + if (xr) { + xrni = xr->idx_ni; + mutex_lock_nested(&xrni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); + err = remove_reparse_index(vi, xr, &reparse_tag); + if (err < 0) { + ntfs_index_ctx_put(xr); + mutex_unlock(&xrni->mrec_lock); + iput(VFS_I(xrni)); + goto out; + } + mark_mft_record_dirty(xrni); + ntfs_index_ctx_put(xr); + mutex_unlock(&xrni->mrec_lock); + iput(VFS_I(xrni)); + } + + ni->flags &= ~FILE_ATTR_REPARSE_POINT; + NInoSetFileNameDirty(ni); + mark_mft_record_dirty(ni); + +out: + iput(vi); + return err; +} + +/* + * Set the reparse data from an extended attribute + */ +static int ntfs_set_ntfs_reparse_data(struct ntfs_inode *ni, char *value, size_t size) +{ + int err = 0; + struct ntfs_inode *xrni; + struct ntfs_index_context *xr; + + if (!ni) + return -EINVAL; + + /* + * reparse data compatibily with EA is not checked + * any more, it is required by Windows 10, but may + * lead to problems with earlier versions. + */ + if (valid_reparse_data(ni, (const struct reparse_point *)value, size) == false) + return -EINVAL; + + xr = open_reparse_index(ni->vol); + if (!xr) + return -EINVAL; + xrni = xr->idx_ni; + + if (!ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED, 0)) { + struct reparse_point rp = {0, }; + + /* + * no reparse data attribute : add one, + * apparently, this does not feed the new value in + * Note : NTFS version must be >= 3 + */ + if (ni->vol->major_ver < 3) { + err = -EOPNOTSUPP; + ntfs_index_ctx_put(xr); + goto out; + } + + err = ntfs_attr_add(ni, AT_REPARSE_POINT, AT_UNNAMED, 0, (u8 *)&rp, sizeof(rp)); + if (err) { + ntfs_index_ctx_put(xr); + goto out; + } + ni->flags |= FILE_ATTR_REPARSE_POINT; + NInoSetFileNameDirty(ni); + mark_mft_record_dirty(ni); + } + + /* update value and index */ + mutex_lock_nested(&xrni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); + err = update_reparse_data(ni, xr, value, size); + if (err) { + ni->flags &= ~FILE_ATTR_REPARSE_POINT; + NInoSetFileNameDirty(ni); + mark_mft_record_dirty(ni); + } + ntfs_index_ctx_put(xr); + mutex_unlock(&xrni->mrec_lock); + +out: + if (!err) + mark_mft_record_dirty(xrni); + iput(VFS_I(xrni)); + + return err; +} + +/* + * Set reparse data for a WSL type symlink + */ +int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni, + const __le16 *target, int target_len) +{ + int err = 0; + int len; + int reparse_len; + unsigned char *utarget = NULL; + struct reparse_point *reparse; + struct wsl_link_reparse_data *data; + + len = ntfs_ucstonls(ni->vol, target, target_len, &utarget, 0); + if (len <= 0) + return -EINVAL; + + reparse_len = sizeof(struct reparse_point) + sizeof(data->type) + len; + reparse = kvzalloc(reparse_len, GFP_NOFS); + if (!reparse) { + err = -ENOMEM; + kfree(utarget); + } else { + data = (struct wsl_link_reparse_data *)reparse->reparse_data; + reparse->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK; + reparse->reparse_data_length = + cpu_to_le16(sizeof(data->type) + len); + reparse->reserved = 0; + data->type = cpu_to_le32(2); + memcpy(data->link, utarget, len); + err = ntfs_set_ntfs_reparse_data(ni, + (char *)reparse, reparse_len); + kvfree(reparse); + if (!err) + ni->target = utarget; + else + kfree(utarget); + } + return err; +} + +/* + * Set reparse data for a WSL special file other than a symlink + * (socket, fifo, character or block device) + */ +int ntfs_reparse_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode) +{ + int err; + int len; + int reparse_len; + __le32 reparse_tag; + struct reparse_point *reparse; + + len = 0; + if (S_ISSOCK(mode)) + reparse_tag = IO_REPARSE_TAG_AF_UNIX; + else if (S_ISFIFO(mode)) + reparse_tag = IO_REPARSE_TAG_LX_FIFO; + else if (S_ISCHR(mode)) + reparse_tag = IO_REPARSE_TAG_LX_CHR; + else if (S_ISBLK(mode)) + reparse_tag = IO_REPARSE_TAG_LX_BLK; + else + return -EOPNOTSUPP; + + reparse_len = sizeof(struct reparse_point) + len; + reparse = kvzalloc(reparse_len, GFP_NOFS); + if (!reparse) + err = -ENOMEM; + else { + reparse->reparse_tag = reparse_tag; + reparse->reparse_data_length = cpu_to_le16(len); + reparse->reserved = cpu_to_le16(0); + err = ntfs_set_ntfs_reparse_data(ni, (char *)reparse, + reparse_len); + kvfree(reparse); + } + + return err; +} diff --git a/fs/ntfs/reparse.h b/fs/ntfs/reparse.h new file mode 100644 index 000000000000..28da40257f2a --- /dev/null +++ b/fs/ntfs/reparse.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2008-2021 Jean-Pierre Andre + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#ifndef _LINUX_NTFS_REPARSE_H +#define _LINUX_NTFS_REPARSE_H + +extern __le16 reparse_index_name[]; + +unsigned int ntfs_make_symlink(struct ntfs_inode *ni); +unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mref); +int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni, + const __le16 *target, int target_len); +int ntfs_reparse_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode); +int ntfs_delete_reparse_index(struct ntfs_inode *ni); +int ntfs_remove_ntfs_reparse_data(struct ntfs_inode *ni); + +#endif /* _LINUX_NTFS_REPARSE_H */ diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c new file mode 100644 index 000000000000..da21dbeaaf66 --- /dev/null +++ b/fs/ntfs/runlist.c @@ -0,0 +1,2084 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS runlist handling code. + * + * Copyright (c) 2001-2007 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. + * + * Part of this file is based on code from the NTFS-3G. + * and is copyrighted by the respective authors below: + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004 Yura Pakhuchiy + * Copyright (c) 2007-2022 Jean-Pierre Andre + */ + +#include + +#include "ntfs.h" +#include "attrib.h" + +/* + * ntfs_rl_mm - runlist memmove + * @base: base runlist array + * @dst: destination index in @base + * @src: source index in @base + * @size: number of elements to move + * + * It is up to the caller to serialize access to the runlist @base. + */ +static inline void ntfs_rl_mm(struct runlist_element *base, int dst, int src, int size) +{ + if (likely((dst != src) && (size > 0))) + memmove(base + dst, base + src, size * sizeof(*base)); +} + +/* + * ntfs_rl_mc - runlist memory copy + * @dstbase: destination runlist array + * @dst: destination index in @dstbase + * @srcbase: source runlist array + * @src: source index in @srcbase + * @size: number of elements to copy + * + * It is up to the caller to serialize access to the runlists @dstbase and + * @srcbase. + */ +static inline void ntfs_rl_mc(struct runlist_element *dstbase, int dst, + struct runlist_element *srcbase, int src, int size) +{ + if (likely(size > 0)) + memcpy(dstbase + dst, srcbase + src, size * sizeof(*dstbase)); +} + +/* + * ntfs_rl_realloc - Reallocate memory for runlists + * @rl: original runlist + * @old_size: number of runlist elements in the original runlist @rl + * @new_size: number of runlist elements we need space for + * + * As the runlists grow, more memory will be required. To prevent the + * kernel having to allocate and reallocate large numbers of small bits of + * memory, this function returns an entire page of memory. + * + * It is up to the caller to serialize access to the runlist @rl. + * + * N.B. If the new allocation doesn't require a different number of pages in + * memory, the function will return the original pointer. + * + * On success, return a pointer to the newly allocated, or recycled, memory. + * On error, return -errno. + */ +struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl, + int old_size, int new_size) +{ + struct runlist_element *new_rl; + + old_size = old_size * sizeof(*rl); + new_size = new_size * sizeof(*rl); + if (old_size == new_size) + return rl; + + new_rl = kvzalloc(new_size, GFP_NOFS); + if (unlikely(!new_rl)) + return ERR_PTR(-ENOMEM); + + if (likely(rl != NULL)) { + if (unlikely(old_size > new_size)) + old_size = new_size; + memcpy(new_rl, rl, old_size); + kvfree(rl); + } + return new_rl; +} + +/* + * ntfs_rl_realloc_nofail - Reallocate memory for runlists + * @rl: original runlist + * @old_size: number of runlist elements in the original runlist @rl + * @new_size: number of runlist elements we need space for + * + * As the runlists grow, more memory will be required. To prevent the + * kernel having to allocate and reallocate large numbers of small bits of + * memory, this function returns an entire page of memory. + * + * This function guarantees that the allocation will succeed. It will sleep + * for as long as it takes to complete the allocation. + * + * It is up to the caller to serialize access to the runlist @rl. + * + * N.B. If the new allocation doesn't require a different number of pages in + * memory, the function will return the original pointer. + * + * On success, return a pointer to the newly allocated, or recycled, memory. + * On error, return -errno. + */ +static inline struct runlist_element *ntfs_rl_realloc_nofail(struct runlist_element *rl, + int old_size, int new_size) +{ + struct runlist_element *new_rl; + + old_size = old_size * sizeof(*rl); + new_size = new_size * sizeof(*rl); + if (old_size == new_size) + return rl; + + new_rl = kvmalloc(new_size, GFP_NOFS | __GFP_NOFAIL); + if (likely(rl != NULL)) { + if (unlikely(old_size > new_size)) + old_size = new_size; + memcpy(new_rl, rl, old_size); + kvfree(rl); + } + return new_rl; +} + +/* + * ntfs_are_rl_mergeable - test if two runlists can be joined together + * @dst: original runlist + * @src: new runlist to test for mergeability with @dst + * + * Test if two runlists can be joined together. For this, their VCNs and LCNs + * must be adjacent. + * + * It is up to the caller to serialize access to the runlists @dst and @src. + * + * Return: true Success, the runlists can be merged. + * false Failure, the runlists cannot be merged. + */ +static inline bool ntfs_are_rl_mergeable(struct runlist_element *dst, + struct runlist_element *src) +{ + /* We can merge unmapped regions even if they are misaligned. */ + if ((dst->lcn == LCN_RL_NOT_MAPPED) && (src->lcn == LCN_RL_NOT_MAPPED)) + return true; + /* If the runs are misaligned, we cannot merge them. */ + if ((dst->vcn + dst->length) != src->vcn) + return false; + /* If both runs are non-sparse and contiguous, we can merge them. */ + if ((dst->lcn >= 0) && (src->lcn >= 0) && + ((dst->lcn + dst->length) == src->lcn)) + return true; + /* If we are merging two holes, we can merge them. */ + if ((dst->lcn == LCN_HOLE) && (src->lcn == LCN_HOLE)) + return true; + /* If we are merging two dealloc, we can merge them. */ + if ((dst->lcn == LCN_DELALLOC) && (src->lcn == LCN_DELALLOC)) + return true; + /* Cannot merge. */ + return false; +} + +/* + * __ntfs_rl_merge - merge two runlists without testing if they can be merged + * @dst: original, destination runlist + * @src: new runlist to merge with @dst + * + * Merge the two runlists, writing into the destination runlist @dst. The + * caller must make sure the runlists can be merged or this will corrupt the + * destination runlist. + * + * It is up to the caller to serialize access to the runlists @dst and @src. + */ +static inline void __ntfs_rl_merge(struct runlist_element *dst, struct runlist_element *src) +{ + dst->length += src->length; +} + +/* + * ntfs_rl_append - append a runlist after a given element + * @dst: destination runlist to append to + * @dsize: number of elements in @dst + * @src: source runlist to append from + * @ssize: number of elements in @src + * @loc: index in @dst after which to append @src + * @new_size: on success, set to the new combined size + * + * Append the runlist @src after element @loc in @dst. Merge the right end of + * the new runlist, if necessary. Adjust the size of the hole before the + * appended runlist. + * + * It is up to the caller to serialize access to the runlists @dst and @src. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return -errno. Both runlists are left unmodified. + */ +static inline struct runlist_element *ntfs_rl_append(struct runlist_element *dst, + int dsize, struct runlist_element *src, int ssize, int loc, + size_t *new_size) +{ + bool right = false; /* Right end of @src needs merging. */ + int marker; /* End of the inserted runs. */ + + /* First, check if the right hand end needs merging. */ + if ((loc + 1) < dsize) + right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1); + + /* Space required: @dst size + @src size, less one if we merged. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - right); + if (IS_ERR(dst)) + return dst; + + *new_size = dsize + ssize - right; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ + + /* First, merge the right hand end, if necessary. */ + if (right) + __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); + + /* First run after the @src runs that have been inserted. */ + marker = loc + ssize + 1; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, loc + 1 + right, dsize - (loc + 1 + right)); + ntfs_rl_mc(dst, loc + 1, src, 0, ssize); + + /* Adjust the size of the preceding hole. */ + dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; + + /* We may have changed the length of the file, so fix the end marker */ + if (dst[marker].lcn == LCN_ENOENT) + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; + + return dst; +} + +/* + * ntfs_rl_insert - insert a runlist into another + * @dst: destination runlist to insert into + * @dsize: number of elements in @dst + * @src: source runlist to insert from + * @ssize: number of elements in @src + * @loc: index in @dst at which to insert @src + * @new_size: on success, set to the new combined size + * + * Insert the runlist @src before element @loc in the runlist @dst. Merge the + * left end of the new runlist, if necessary. Adjust the size of the hole + * after the inserted runlist. + * + * It is up to the caller to serialize access to the runlists @dst and @src. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return -errno. Both runlists are left unmodified. + */ +static inline struct runlist_element *ntfs_rl_insert(struct runlist_element *dst, + int dsize, struct runlist_element *src, int ssize, int loc, + size_t *new_size) +{ + bool left = false; /* Left end of @src needs merging. */ + bool disc = false; /* Discontinuity between @dst and @src. */ + int marker; /* End of the inserted runs. */ + + /* + * disc => Discontinuity between the end of @dst and the start of @src. + * This means we might need to insert a "not mapped" run. + */ + if (loc == 0) + disc = (src[0].vcn > 0); + else { + s64 merged_length; + + left = ntfs_are_rl_mergeable(dst + loc - 1, src); + + merged_length = dst[loc - 1].length; + if (left) + merged_length += src->length; + + disc = (src[0].vcn > dst[loc - 1].vcn + merged_length); + } + /* + * Space required: @dst size + @src size, less one if we merged, plus + * one if there was a discontinuity. + */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc); + if (IS_ERR(dst)) + return dst; + + *new_size = dsize + ssize - left + disc; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlist. + */ + if (left) + __ntfs_rl_merge(dst + loc - 1, src); + /* + * First run after the @src runs that have been inserted. + * Nominally, @marker equals @loc + @ssize, i.e. location + number of + * runs in @src. However, if @left, then the first run in @src has + * been merged with one in @dst. And if @disc, then @dst and @src do + * not meet and we need an extra run to fill the gap. + */ + marker = loc + ssize - left + disc; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, loc, dsize - loc); + ntfs_rl_mc(dst, loc + disc, src, left, ssize - left); + + /* Adjust the VCN of the first run after the insertion... */ + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; + /* ... and the length. */ + if (dst[marker].lcn == LCN_HOLE || dst[marker].lcn == LCN_RL_NOT_MAPPED || + dst[marker].lcn == LCN_DELALLOC) + dst[marker].length = dst[marker + 1].vcn - dst[marker].vcn; + + /* Writing beyond the end of the file and there is a discontinuity. */ + if (disc) { + if (loc > 0) { + dst[loc].vcn = dst[loc - 1].vcn + dst[loc - 1].length; + dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; + } else { + dst[loc].vcn = 0; + dst[loc].length = dst[loc + 1].vcn; + } + dst[loc].lcn = LCN_RL_NOT_MAPPED; + } + return dst; +} + +/* + * ntfs_rl_replace - overwrite a runlist element with another runlist + * @dst: destination runlist to replace in + * @dsize: number of elements in @dst + * @src: source runlist to replace with + * @ssize: number of elements in @src + * @loc: index in @dst to replace + * @new_size: on success, set to the new combined size + * + * Replace the runlist element @dst at @loc with @src. Merge the left and + * right ends of the inserted runlist, if necessary. + * + * It is up to the caller to serialize access to the runlists @dst and @src. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return -errno. Both runlists are left unmodified. + */ +static inline struct runlist_element *ntfs_rl_replace(struct runlist_element *dst, + int dsize, struct runlist_element *src, int ssize, int loc, + size_t *new_size) +{ + int delta; + bool left = false; /* Left end of @src needs merging. */ + bool right = false; /* Right end of @src needs merging. */ + int tail; /* Start of tail of @dst. */ + int marker; /* End of the inserted runs. */ + + /* First, see if the left and right ends need merging. */ + if ((loc + 1) < dsize) + right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1); + if (loc > 0) + left = ntfs_are_rl_mergeable(dst + loc - 1, src); + /* + * Allocate some space. We will need less if the left, right, or both + * ends get merged. The -1 accounts for the run being replaced. + */ + delta = ssize - 1 - left - right; + if (delta > 0) { + dst = ntfs_rl_realloc(dst, dsize, dsize + delta); + if (IS_ERR(dst)) + return dst; + } + + *new_size = dsize + delta; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ + + /* First, merge the left and right ends, if necessary. */ + if (right) + __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); + if (left) + __ntfs_rl_merge(dst + loc - 1, src); + /* + * Offset of the tail of @dst. This needs to be moved out of the way + * to make space for the runs to be copied from @src, i.e. the first + * run of the tail of @dst. + * Nominally, @tail equals @loc + 1, i.e. location, skipping the + * replaced run. However, if @right, then one of @dst's runs is + * already merged into @src. + */ + tail = loc + right + 1; + /* + * First run after the @src runs that have been inserted, i.e. where + * the tail of @dst needs to be moved to. + * Nominally, @marker equals @loc + @ssize, i.e. location + number of + * runs in @src. However, if @left, then the first run in @src has + * been merged with one in @dst. + */ + marker = loc + ssize - left; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, tail, dsize - tail); + ntfs_rl_mc(dst, loc, src, left, ssize - left); + + /* We may have changed the length of the file, so fix the end marker. */ + if (dsize - tail > 0 && dst[marker].lcn == LCN_ENOENT) + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; + return dst; +} + +/* + * ntfs_rl_split - insert a runlist into the centre of a hole + * @dst: destination runlist with a hole + * @dsize: number of elements in @dst + * @src: source runlist to insert + * @ssize: number of elements in @src + * @loc: index in @dst of the hole to split + * @new_size: on success, set to the new combined size + * + * Split the runlist @dst at @loc into two and insert @new in between the two + * fragments. No merging of runlists is necessary. Adjust the size of the + * holes either side. + * + * It is up to the caller to serialize access to the runlists @dst and @src. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return -errno. Both runlists are left unmodified. + */ +static inline struct runlist_element *ntfs_rl_split(struct runlist_element *dst, int dsize, + struct runlist_element *src, int ssize, int loc, + size_t *new_size) +{ + /* Space required: @dst size + @src size + one new hole. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize + 1); + if (IS_ERR(dst)) + return dst; + + *new_size = dsize + ssize + 1; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, loc + 1 + ssize, loc, dsize - loc); + ntfs_rl_mc(dst, loc + 1, src, 0, ssize); + + /* Adjust the size of the holes either size of @src. */ + dst[loc].length = dst[loc+1].vcn - dst[loc].vcn; + dst[loc+ssize+1].vcn = dst[loc+ssize].vcn + dst[loc+ssize].length; + dst[loc+ssize+1].length = dst[loc+ssize+2].vcn - dst[loc+ssize+1].vcn; + + return dst; +} + +/* + * ntfs_runlists_merge - merge two runlists into one + * @d_runlist: destination runlist structure to merge into + * @srl: source runlist to merge from + * @s_rl_count: number of elements in @srl (0 to auto-detect) + * @new_rl_count: on success, set to the new combined runlist size + * + * First we sanity check the two runlists @srl and @drl to make sure that they + * are sensible and can be merged. The runlist @srl must be either after the + * runlist @drl or completely within a hole (or unmapped region) in @drl. + * + * It is up to the caller to serialize access to the runlists @drl and @srl. + * + * Merging of runlists is necessary in two cases: + * 1. When attribute lists are used and a further extent is being mapped. + * 2. When new clusters are allocated to fill a hole or extend a file. + * + * There are four possible ways @srl can be merged. It can: + * - be inserted at the beginning of a hole, + * - split the hole in two and be inserted between the two fragments, + * - be appended at the end of a hole, or it can + * - replace the whole hole. + * It can also be appended to the end of the runlist, which is just a variant + * of the insert case. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @drl and @srl are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return -errno. Both runlists are left unmodified. + */ +struct runlist_element *ntfs_runlists_merge(struct runlist *d_runlist, + struct runlist_element *srl, size_t s_rl_count, + size_t *new_rl_count) +{ + int di, si; /* Current index into @[ds]rl. */ + int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */ + int dins; /* Index into @drl at which to insert @srl. */ + int dend, send; /* Last index into @[ds]rl. */ + int dfinal, sfinal; /* The last index into @[ds]rl with lcn >= LCN_HOLE. */ + int marker = 0; + s64 marker_vcn = 0; + struct runlist_element *drl = d_runlist->rl, *rl; + +#ifdef DEBUG + ntfs_debug("dst:"); + ntfs_debug_dump_runlist(drl); + ntfs_debug("src:"); + ntfs_debug_dump_runlist(srl); +#endif + + /* Check for silly calling... */ + if (unlikely(!srl)) + return drl; + if (IS_ERR(srl) || IS_ERR(drl)) + return ERR_PTR(-EINVAL); + + if (s_rl_count == 0) { + for (; srl[s_rl_count].length; s_rl_count++) + ; + s_rl_count++; + } + + /* Check for the case where the first mapping is being done now. */ + if (unlikely(!drl)) { + drl = srl; + /* Complete the source runlist if necessary. */ + if (unlikely(drl[0].vcn)) { + /* Scan to the end of the source runlist. */ + drl = ntfs_rl_realloc(drl, s_rl_count, s_rl_count + 1); + if (IS_ERR(drl)) + return drl; + /* Insert start element at the front of the runlist. */ + ntfs_rl_mm(drl, 1, 0, s_rl_count); + drl[0].vcn = 0; + drl[0].lcn = LCN_RL_NOT_MAPPED; + drl[0].length = drl[1].vcn; + s_rl_count++; + } + + *new_rl_count = s_rl_count; + goto finished; + } + + if (d_runlist->count < 1 || s_rl_count < 2) + return ERR_PTR(-EINVAL); + + si = di = 0; + + /* Skip any unmapped start element(s) in the source runlist. */ + while (srl[si].length && srl[si].lcn < LCN_HOLE) + si++; + + /* Can't have an entirely unmapped source runlist. */ + WARN_ON(!srl[si].length); + + /* Record the starting points. */ + sstart = si; + + /* + * Skip forward in @drl until we reach the position where @srl needs to + * be inserted. If we reach the end of @drl, @srl just needs to be + * appended to @drl. + */ + rl = __ntfs_attr_find_vcn_nolock(d_runlist, srl[sstart].vcn); + if (IS_ERR(rl)) + di = (int)d_runlist->count - 1; + else + di = (int)(rl - d_runlist->rl); + dins = di; + + /* Sanity check for illegal overlaps. */ + if ((drl[di].vcn == srl[si].vcn) && (drl[di].lcn >= 0) && + (srl[si].lcn >= 0)) { + ntfs_error(NULL, "Run lists overlap. Cannot merge!"); + return ERR_PTR(-ERANGE); + } + + /* Scan to the end of both runlists in order to know their sizes. */ + send = (int)s_rl_count - 1; + dend = (int)d_runlist->count - 1; + + if (srl[send].lcn == LCN_ENOENT) + marker_vcn = srl[marker = send].vcn; + + /* Scan to the last element with lcn >= LCN_HOLE. */ + for (sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal--) + ; + for (dfinal = dend; dfinal >= 0 && drl[dfinal].lcn < LCN_HOLE; dfinal--) + ; + + { + bool start; + bool finish; + int ds = dend + 1; /* Number of elements in drl & srl */ + int ss = sfinal - sstart + 1; + + start = ((drl[dins].lcn < LCN_RL_NOT_MAPPED) || /* End of file */ + (drl[dins].vcn == srl[sstart].vcn)); /* Start of hole */ + finish = ((drl[dins].lcn >= LCN_RL_NOT_MAPPED) && /* End of file */ + ((drl[dins].vcn + drl[dins].length) <= /* End of hole */ + (srl[send - 1].vcn + srl[send - 1].length))); + + /* Or we will lose an end marker. */ + if (finish && !drl[dins].length) + ss++; + if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn)) + finish = false; + + if (start) { + if (finish) + drl = ntfs_rl_replace(drl, ds, srl + sstart, ss, dins, new_rl_count); + else + drl = ntfs_rl_insert(drl, ds, srl + sstart, ss, dins, new_rl_count); + } else { + if (finish) + drl = ntfs_rl_append(drl, ds, srl + sstart, ss, dins, new_rl_count); + else + drl = ntfs_rl_split(drl, ds, srl + sstart, ss, dins, new_rl_count); + } + if (IS_ERR(drl)) { + ntfs_error(NULL, "Merge failed."); + return drl; + } + kvfree(srl); + if (marker) { + ntfs_debug("Triggering marker code."); + for (ds = dend; drl[ds].length; ds++) + ; + /* We only need to care if @srl ended after @drl. */ + if (drl[ds].vcn <= marker_vcn) { + int slots = 0; + + if (drl[ds].vcn == marker_vcn) { + ntfs_debug("Old marker = 0x%llx, replacing with LCN_ENOENT.", + drl[ds].lcn); + drl[ds].lcn = LCN_ENOENT; + goto finished; + } + /* + * We need to create an unmapped runlist element in + * @drl or extend an existing one before adding the + * ENOENT terminator. + */ + if (drl[ds].lcn == LCN_ENOENT) { + ds--; + slots = 1; + } + if (drl[ds].lcn != LCN_RL_NOT_MAPPED) { + /* Add an unmapped runlist element. */ + if (!slots) { + drl = ntfs_rl_realloc_nofail(drl, ds, + ds + 2); + slots = 2; + *new_rl_count += 2; + } + ds++; + /* Need to set vcn if it isn't set already. */ + if (slots != 1) + drl[ds].vcn = drl[ds - 1].vcn + + drl[ds - 1].length; + drl[ds].lcn = LCN_RL_NOT_MAPPED; + /* We now used up a slot. */ + slots--; + } + drl[ds].length = marker_vcn - drl[ds].vcn; + /* Finally add the ENOENT terminator. */ + ds++; + if (!slots) { + drl = ntfs_rl_realloc_nofail(drl, ds, ds + 1); + *new_rl_count += 1; + } + drl[ds].vcn = marker_vcn; + drl[ds].lcn = LCN_ENOENT; + drl[ds].length = (s64)0; + } + } + } + +finished: + /* The merge was completed successfully. */ + ntfs_debug("Merged runlist:"); + ntfs_debug_dump_runlist(drl); + return drl; +} + +/* + * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist + * @vol: ntfs volume + * @attr: attribute record whose mapping pairs to decompress + * @old_runlist: optional runlist to merge the decompressed runlist into + * @new_rl_count: on success, set to the new runlist size + * + * It is up to the caller to serialize access to the runlist @old_rl. + * + * Decompress the attribute @attr's mapping pairs array into a runlist. On + * success, return the decompressed runlist. + * + * If @old_rl is not NULL, decompressed runlist is inserted into the + * appropriate place in @old_rl and the resultant, combined runlist is + * returned. The original @old_rl is deallocated. + * + * On error, return -errno. @old_rl is left unmodified in that case. + */ +struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume *vol, + const struct attr_record *attr, struct runlist *old_runlist, + size_t *new_rl_count) +{ + s64 vcn; /* Current vcn. */ + s64 lcn; /* Current lcn. */ + s64 deltaxcn; /* Change in [vl]cn. */ + struct runlist_element *rl, *new_rl; /* The output runlist. */ + u8 *buf; /* Current position in mapping pairs array. */ + u8 *attr_end; /* End of attribute. */ + int rlsize; /* Size of runlist buffer. */ + u16 rlpos; /* Current runlist position in units of struct runlist_elements. */ + u8 b; /* Current byte offset in buf. */ + u64 lowest_vcn; /* Raw on-disk lowest_vcn. */ + +#ifdef DEBUG + /* Make sure attr exists and is non-resident. */ + if (!attr || !attr->non_resident) { + ntfs_error(vol->sb, "Invalid arguments."); + return ERR_PTR(-EINVAL); + } +#endif + lowest_vcn = le64_to_cpu(attr->data.non_resident.lowest_vcn); + /* Validate lowest_vcn from on-disk metadata to ensure it is sane. */ + if (overflows_type(lowest_vcn, vcn)) { + ntfs_error(vol->sb, "Invalid lowest_vcn in mapping pairs."); + return ERR_PTR(-EIO); + } + /* Start at vcn = lowest_vcn and lcn 0. */ + vcn = lowest_vcn; + lcn = 0; + /* Get start of the mapping pairs array. */ + buf = (u8 *)attr + + le16_to_cpu(attr->data.non_resident.mapping_pairs_offset); + attr_end = (u8 *)attr + le32_to_cpu(attr->length); + if (unlikely(buf < (u8 *)attr || buf > attr_end)) { + ntfs_error(vol->sb, "Corrupt attribute."); + return ERR_PTR(-EIO); + } + + /* Current position in runlist array. */ + rlpos = 0; + /* Allocate first page and set current runlist size to one page. */ + rl = kvzalloc(rlsize = PAGE_SIZE, GFP_NOFS); + if (unlikely(!rl)) + return ERR_PTR(-ENOMEM); + /* Insert unmapped starting element if necessary. */ + if (vcn) { + rl->vcn = 0; + rl->lcn = LCN_RL_NOT_MAPPED; + rl->length = vcn; + rlpos++; + } + while (buf < attr_end && *buf) { + /* + * Allocate more memory if needed, including space for the + * not-mapped and terminator elements. kvzalloc() + * operates on whole pages only. + */ + if (((rlpos + 3) * sizeof(*rl)) > rlsize) { + struct runlist_element *rl2; + + rl2 = kvzalloc(rlsize + PAGE_SIZE, GFP_NOFS); + if (unlikely(!rl2)) { + kvfree(rl); + return ERR_PTR(-ENOMEM); + } + memcpy(rl2, rl, rlsize); + kvfree(rl); + rl = rl2; + rlsize += PAGE_SIZE; + } + /* Enter the current vcn into the current runlist element. */ + rl[rlpos].vcn = vcn; + /* + * Get the change in vcn, i.e. the run length in clusters. + * Doing it this way ensures that we signextend negative values. + * A negative run length doesn't make any sense, but hey, I + * didn't make up the NTFS specs and Windows NT4 treats the run + * length as a signed value so that's how it is... + */ + b = *buf & 0xf; + if (b) { + if (unlikely(buf + b > attr_end)) + goto io_error; + for (deltaxcn = (s8)buf[b--]; b; b--) + deltaxcn = (deltaxcn << 8) + buf[b]; + } else { /* The length entry is compulsory. */ + ntfs_error(vol->sb, "Missing length entry in mapping pairs array."); + deltaxcn = (s64)-1; + } + /* + * Assume a negative length to indicate data corruption and + * hence clean-up and return NULL. + */ + if (unlikely(deltaxcn < 0)) { + ntfs_error(vol->sb, "Invalid length in mapping pairs array."); + goto err_out; + } + /* + * Enter the current run length into the current runlist + * element. + */ + rl[rlpos].length = deltaxcn; + /* + * Increment the current vcn by the current run length. + * Guard against s64 overflow from a crafted mapping + * pairs array to preserve the monotonically-increasing + * vcn invariant. + */ + if (unlikely(check_add_overflow(vcn, deltaxcn, &vcn))) { + ntfs_error(vol->sb, "VCN overflow in mapping pairs array."); + goto err_out; + } + + /* + * There might be no lcn change at all, as is the case for + * sparse clusters on NTFS 3.0+, in which case we set the lcn + * to LCN_HOLE. + */ + if (!(*buf & 0xf0)) + rl[rlpos].lcn = LCN_HOLE; + else { + /* Get the lcn change which really can be negative. */ + u8 b2 = *buf & 0xf; + + b = b2 + ((*buf >> 4) & 0xf); + if (buf + b > attr_end) + goto io_error; + for (deltaxcn = (s8)buf[b--]; b > b2; b--) + deltaxcn = (deltaxcn << 8) + buf[b]; + /* Change the current lcn to its new value. */ + lcn += deltaxcn; +#ifdef DEBUG + /* + * On NTFS 1.2-, apparently can have lcn == -1 to + * indicate a hole. But we haven't verified ourselves + * whether it is really the lcn or the deltaxcn that is + * -1. So if either is found give us a message so we + * can investigate it further! + */ + if (vol->major_ver < 3) { + if (unlikely(deltaxcn == -1)) + ntfs_error(vol->sb, "lcn delta == -1"); + if (unlikely(lcn == -1)) + ntfs_error(vol->sb, "lcn == -1"); + } +#endif + /* Check lcn is not below -1. */ + if (unlikely(lcn < -1)) { + ntfs_error(vol->sb, "Invalid s64 < -1 in mapping pairs array."); + goto err_out; + } + + /* chkdsk accepts zero-sized runs only for holes */ + if ((lcn != -1) && !rl[rlpos].length) { + ntfs_error(vol->sb, + "Invalid zero-sized data run(lcn : %lld).\n", + lcn); + goto err_out; + } + + /* Enter the current lcn into the runlist element. */ + rl[rlpos].lcn = lcn; + } + /* Get to the next runlist element, skipping zero-sized holes */ + if (rl[rlpos].length) + rlpos++; + /* Increment the buffer position to the next mapping pair. */ + buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1; + } + if (unlikely(buf >= attr_end)) + goto io_error; + /* + * If there is a highest_vcn specified, it must be equal to the final + * vcn in the runlist - 1, or something has gone badly wrong. + */ + deltaxcn = le64_to_cpu(attr->data.non_resident.highest_vcn); + if (unlikely(deltaxcn && vcn - 1 != deltaxcn)) { +mpa_err: + ntfs_error(vol->sb, "Corrupt mapping pairs array in non-resident attribute."); + goto err_out; + } + /* Setup not mapped runlist element if this is the base extent. */ + if (!attr->data.non_resident.lowest_vcn) { + s64 max_cluster; + + max_cluster = ((le64_to_cpu(attr->data.non_resident.allocated_size) + + vol->cluster_size - 1) >> + vol->cluster_size_bits) - 1; + /* + * A highest_vcn of zero means this is a single extent + * attribute so simply terminate the runlist with LCN_ENOENT). + */ + if (deltaxcn) { + /* + * If there is a difference between the highest_vcn and + * the highest cluster, the runlist is either corrupt + * or, more likely, there are more extents following + * this one. + */ + if (deltaxcn < max_cluster) { + ntfs_debug("More extents to follow; deltaxcn = 0x%llx, max_cluster = 0x%llx", + deltaxcn, max_cluster); + rl[rlpos].vcn = vcn; + vcn += rl[rlpos].length = max_cluster - + deltaxcn; + rl[rlpos].lcn = LCN_RL_NOT_MAPPED; + rlpos++; + } else if (unlikely(deltaxcn > max_cluster)) { + ntfs_error(vol->sb, + "Corrupt attribute. deltaxcn = 0x%llx, max_cluster = 0x%llx", + deltaxcn, max_cluster); + goto mpa_err; + } + } + rl[rlpos].lcn = LCN_ENOENT; + } else /* Not the base extent. There may be more extents to follow. */ + rl[rlpos].lcn = LCN_RL_NOT_MAPPED; + + /* Setup terminating runlist element. */ + rl[rlpos].vcn = vcn; + rl[rlpos].length = (s64)0; + /* If no existing runlist was specified, we are done. */ + if (!old_runlist || !old_runlist->rl) { + *new_rl_count = rlpos + 1; + ntfs_debug("Mapping pairs array successfully decompressed:"); + ntfs_debug_dump_runlist(rl); + return rl; + } + /* Now combine the new and old runlists checking for overlaps. */ + new_rl = ntfs_runlists_merge(old_runlist, rl, rlpos + 1, new_rl_count); + if (!IS_ERR(new_rl)) + return new_rl; + kvfree(rl); + ntfs_error(vol->sb, "Failed to merge runlists."); + return new_rl; +io_error: + ntfs_error(vol->sb, "Corrupt attribute."); +err_out: + kvfree(rl); + return ERR_PTR(-EIO); +} + +/* + * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist + * @rl: runlist to use for conversion + * @vcn: vcn to convert + * + * Convert the virtual cluster number @vcn of an attribute into a logical + * cluster number (lcn) of a device using the runlist @rl to map vcns to their + * corresponding lcns. + * + * It is up to the caller to serialize access to the runlist @rl. + * + * Since lcns must be >= 0, we use negative return codes with special meaning: + * + * Return code Meaning / Description + * ================================================== + * LCN_HOLE Hole / not allocated on disk. + * LCN_RL_NOT_MAPPED This is part of the runlist which has not been + * inserted into the runlist yet. + * LCN_ENOENT There is no such vcn in the attribute. + * + * Locking: - The caller must have locked the runlist (for reading or writing). + * - This function does not touch the lock, nor does it modify the + * runlist. + */ +s64 ntfs_rl_vcn_to_lcn(const struct runlist_element *rl, const s64 vcn) +{ + int i; + + /* + * If rl is NULL, assume that we have found an unmapped runlist. The + * caller can then attempt to map it and fail appropriately if + * necessary. + */ + if (unlikely(!rl)) + return LCN_RL_NOT_MAPPED; + + /* Catch out of lower bounds vcn. */ + if (unlikely(vcn < rl[0].vcn)) + return LCN_ENOENT; + + for (i = 0; likely(rl[i].length); i++) { + if (vcn < rl[i+1].vcn) { + if (likely(rl[i].lcn >= 0)) + return rl[i].lcn + (vcn - rl[i].vcn); + return rl[i].lcn; + } + } + /* + * The terminator element is setup to the correct value, i.e. one of + * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT. + */ + if (likely(rl[i].lcn < 0)) + return rl[i].lcn; + /* Just in case... We could replace this with BUG() some day. */ + return LCN_ENOENT; +} + +/* + * ntfs_rl_find_vcn_nolock - find a vcn in a runlist + * @rl: runlist to search + * @vcn: vcn to find + * + * Find the virtual cluster number @vcn in the runlist @rl and return the + * address of the runlist element containing the @vcn on success. + * + * Return NULL if @rl is NULL or @vcn is in an unmapped part/out of bounds of + * the runlist. + * + * Locking: The runlist must be locked on entry. + */ +struct runlist_element *ntfs_rl_find_vcn_nolock(struct runlist_element *rl, const s64 vcn) +{ + if (unlikely(!rl || vcn < rl[0].vcn)) + return NULL; + while (likely(rl->length)) { + if (unlikely(vcn < rl[1].vcn)) { + if (likely(rl->lcn >= LCN_HOLE)) + return rl; + return NULL; + } + rl++; + } + if (likely(rl->lcn == LCN_ENOENT)) + return rl; + return NULL; +} + +/* + * ntfs_get_nr_significant_bytes - get number of bytes needed to store a number + * @n: number for which to get the number of bytes for + * + * Return the number of bytes required to store @n unambiguously as + * a signed number. + * + * This is used in the context of the mapping pairs array to determine how + * many bytes will be needed in the array to store a given logical cluster + * number (lcn) or a specific run length. + * + * Return the number of bytes written. This function cannot fail. + */ +static inline int ntfs_get_nr_significant_bytes(const s64 n) +{ + s64 l = n; + int i; + s8 j; + + i = 0; + do { + l >>= 8; + i++; + } while (l != 0 && l != -1); + j = (n >> 8 * (i - 1)) & 0xff; + /* If the sign bit is wrong, we need an extra byte. */ + if ((n < 0 && j >= 0) || (n > 0 && j < 0)) + i++; + return i; +} + +/* + * ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array + * @vol: ntfs volume + * @rl: runlist to calculate the mapping pairs array size for + * @first_vcn: first vcn which to include in the mapping pairs array + * @last_vcn: last vcn which to include in the mapping pairs array + * @max_mp_size: maximum size to return (0 or less means unlimited) + * + * Walk the locked runlist @rl and calculate the size in bytes of the mapping + * pairs array corresponding to the runlist @rl, starting at vcn @first_vcn and + * finishing with vcn @last_vcn. + * + * A @last_vcn of -1 means end of runlist and in that case the size of the + * mapping pairs array corresponding to the runlist starting at vcn @first_vcn + * and finishing at the end of the runlist is determined. + * + * This for example allows us to allocate a buffer of the right size when + * building the mapping pairs array. + * + * If @rl is NULL, just return 1 (for the single terminator byte). + * + * Return the calculated size in bytes on success. On error, return -errno. + */ +int ntfs_get_size_for_mapping_pairs(const struct ntfs_volume *vol, + const struct runlist_element *rl, const s64 first_vcn, + const s64 last_vcn, int max_mp_size) +{ + s64 prev_lcn; + int rls; + bool the_end = false; + + if (first_vcn < 0 || last_vcn < -1) + return -EINVAL; + + if (last_vcn >= 0 && first_vcn > last_vcn) + return -EINVAL; + + if (!rl) { + WARN_ON(first_vcn); + WARN_ON(last_vcn > 0); + return 1; + } + if (max_mp_size <= 0) + max_mp_size = INT_MAX; + /* Skip to runlist element containing @first_vcn. */ + while (rl->length && first_vcn >= rl[1].vcn) + rl++; + if (unlikely((!rl->length && first_vcn > rl->vcn) || + first_vcn < rl->vcn)) + return -EINVAL; + prev_lcn = 0; + /* Always need the termining zero byte. */ + rls = 1; + /* Do the first partial run if present. */ + if (first_vcn > rl->vcn) { + s64 delta, length = rl->length; + + /* We know rl->length != 0 already. */ + if (unlikely(length < 0 || rl->lcn < LCN_HOLE)) + goto err_out; + /* + * If @stop_vcn is given and finishes inside this run, cap the + * run length. + */ + if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) { + s64 s1 = last_vcn + 1; + + if (unlikely(rl[1].vcn > s1)) + length = s1 - rl->vcn; + the_end = true; + } + delta = first_vcn - rl->vcn; + /* Header byte + length. */ + rls += 1 + ntfs_get_nr_significant_bytes(length - delta); + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just store the lcn. + * Note: this assumes that on NTFS 1.2-, holes are stored with + * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). + */ + if (likely(rl->lcn >= 0 || vol->major_ver < 3)) { + prev_lcn = rl->lcn; + if (likely(rl->lcn >= 0)) + prev_lcn += delta; + /* Change in lcn. */ + rls += ntfs_get_nr_significant_bytes(prev_lcn); + } + /* Go to next runlist element. */ + rl++; + } + /* Do the full runs. */ + for (; rl->length && !the_end; rl++) { + s64 length = rl->length; + + if (unlikely(length < 0 || rl->lcn < LCN_HOLE)) + goto err_out; + /* + * If @stop_vcn is given and finishes inside this run, cap the + * run length. + */ + if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) { + s64 s1 = last_vcn + 1; + + if (unlikely(rl[1].vcn > s1)) + length = s1 - rl->vcn; + the_end = true; + } + /* Header byte + length. */ + rls += 1 + ntfs_get_nr_significant_bytes(length); + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just store the lcn. + * Note: this assumes that on NTFS 1.2-, holes are stored with + * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). + */ + if (likely(rl->lcn >= 0 || vol->major_ver < 3)) { + /* Change in lcn. */ + rls += ntfs_get_nr_significant_bytes(rl->lcn - + prev_lcn); + prev_lcn = rl->lcn; + } + + if (rls > max_mp_size) + break; + } + return rls; +err_out: + if (rl->lcn == LCN_RL_NOT_MAPPED) + rls = -EINVAL; + else + rls = -EIO; + return rls; +} + +/* + * ntfs_write_significant_bytes - write the significant bytes of a number + * @dst: destination buffer to write to + * @dst_max: pointer to last byte of destination buffer for bounds checking + * @n: number whose significant bytes to write + * + * Store in @dst, the minimum bytes of the number @n which are required to + * identify @n unambiguously as a signed number, taking care not to exceed + * @dest_max, the maximum position within @dst to which we are allowed to + * write. + * + * This is used when building the mapping pairs array of a runlist to compress + * a given logical cluster number (lcn) or a specific run length to the minimum + * size possible. + * + * Return the number of bytes written on success. On error, i.e. the + * destination buffer @dst is too small, return -ENOSPC. + */ +static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max, + const s64 n) +{ + s64 l = n; + int i; + s8 j; + + i = 0; + do { + if (unlikely(dst > dst_max)) + goto err_out; + *dst++ = l & 0xffll; + l >>= 8; + i++; + } while (l != 0 && l != -1); + j = (n >> 8 * (i - 1)) & 0xff; + /* If the sign bit is wrong, we need an extra byte. */ + if (n < 0 && j >= 0) { + if (unlikely(dst > dst_max)) + goto err_out; + i++; + *dst = (s8)-1; + } else if (n > 0 && j < 0) { + if (unlikely(dst > dst_max)) + goto err_out; + i++; + *dst = (s8)0; + } + return i; +err_out: + return -ENOSPC; +} + +/* + * ntfs_mapping_pairs_build - build the mapping pairs array from a runlist + * @vol: ntfs volume + * @dst: destination buffer to build mapping pairs array into + * @dst_len: size of @dst in bytes + * @rl: runlist to build the mapping pairs array from + * @first_vcn: first vcn which to include in the mapping pairs array + * @last_vcn: last vcn which to include in the mapping pairs array + * @stop_vcn: on return, set to the first vcn outside the destination buffer + * @stop_rl: on return, set to the runlist element where encoding stopped + * @de_cluster_count: on return, set to the number of clusters encoded + * + * Create the mapping pairs array from the locked runlist @rl, starting at vcn + * @first_vcn and finishing with vcn @last_vcn and save the array in @dst. + * @dst_len is the size of @dst in bytes and it should be at least equal to the + * value obtained by calling ntfs_get_size_for_mapping_pairs(). + * + * A @last_vcn of -1 means end of runlist and in that case the mapping pairs + * array corresponding to the runlist starting at vcn @first_vcn and finishing + * at the end of the runlist is created. + * + * If @rl is NULL, just write a single terminator byte to @dst. + * + * On success or -ENOSPC error, if @stop_vcn is not NULL, *@stop_vcn is set to + * the first vcn outside the destination buffer. Note that on error, @dst has + * been filled with all the mapping pairs that will fit, thus it can be treated + * as partial success, in that a new attribute extent needs to be created or + * the next extent has to be used and the mapping pairs build has to be + * continued with @first_vcn set to *@stop_vcn. + * + * Return 0 on success and -errno on error. The following error codes are + * defined: + * -EINVAL - Run list contains unmapped elements. Make sure to only pass + * fully mapped runlists to this function. + * -EIO - The runlist is corrupt. + * -ENOSPC - The destination buffer is too small. + * + * Locking: @rl must be locked on entry (either for reading or writing), it + * remains locked throughout, and is left locked upon return. + */ +int ntfs_mapping_pairs_build(const struct ntfs_volume *vol, s8 *dst, + const int dst_len, const struct runlist_element *rl, + const s64 first_vcn, const s64 last_vcn, s64 *const stop_vcn, + struct runlist_element **stop_rl, unsigned int *de_cluster_count) +{ + s64 prev_lcn; + s8 *dst_max, *dst_next; + int err = -ENOSPC; + bool the_end = false; + s8 len_len, lcn_len; + unsigned int de_cnt = 0; + + if (first_vcn < 0 || last_vcn < -1 || dst_len < 1) + return -EINVAL; + if (last_vcn >= 0 && first_vcn > last_vcn) + return -EINVAL; + + if (!rl) { + WARN_ON(first_vcn || last_vcn > 0); + if (stop_vcn) + *stop_vcn = 0; + /* Terminator byte. */ + *dst = 0; + return 0; + } + /* Skip to runlist element containing @first_vcn. */ + while (rl->length && first_vcn >= rl[1].vcn) + rl++; + if (unlikely((!rl->length && first_vcn > rl->vcn) || + first_vcn < rl->vcn)) + return -EINVAL; + /* + * @dst_max is used for bounds checking in + * ntfs_write_significant_bytes(). + */ + dst_max = dst + dst_len - 1; + prev_lcn = 0; + /* Do the first partial run if present. */ + if (first_vcn > rl->vcn) { + s64 delta, length = rl->length; + + /* We know rl->length != 0 already. */ + if (unlikely(length < 0 || rl->lcn < LCN_HOLE)) + goto err_out; + /* + * If @stop_vcn is given and finishes inside this run, cap the + * run length. + */ + if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) { + s64 s1 = last_vcn + 1; + + if (unlikely(rl[1].vcn > s1)) + length = s1 - rl->vcn; + the_end = true; + } + delta = first_vcn - rl->vcn; + /* Write length. */ + len_len = ntfs_write_significant_bytes(dst + 1, dst_max, + length - delta); + if (unlikely(len_len < 0)) + goto size_err; + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just write the lcn + * change. + */ + if (likely(rl->lcn >= 0 || vol->major_ver < 3)) { + prev_lcn = rl->lcn; + if (likely(rl->lcn >= 0)) + prev_lcn += delta; + /* Write change in lcn. */ + lcn_len = ntfs_write_significant_bytes(dst + 1 + + len_len, dst_max, prev_lcn); + if (unlikely(lcn_len < 0)) + goto size_err; + } else + lcn_len = 0; + dst_next = dst + len_len + lcn_len + 1; + if (unlikely(dst_next > dst_max)) + goto size_err; + /* Update header byte. */ + *dst = lcn_len << 4 | len_len; + /* Position at next mapping pairs array element. */ + dst = dst_next; + /* Go to next runlist element. */ + rl++; + } + /* Do the full runs. */ + for (; rl->length && !the_end; rl++) { + s64 length = rl->length; + + if (unlikely(length < 0 || rl->lcn < LCN_HOLE)) + goto err_out; + /* + * If @stop_vcn is given and finishes inside this run, cap the + * run length. + */ + if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) { + s64 s1 = last_vcn + 1; + + if (unlikely(rl[1].vcn > s1)) + length = s1 - rl->vcn; + the_end = true; + } + /* Write length. */ + len_len = ntfs_write_significant_bytes(dst + 1, dst_max, + length); + if (unlikely(len_len < 0)) + goto size_err; + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just write the lcn + * change. + */ + if (likely(rl->lcn >= 0 || vol->major_ver < 3)) { + /* Write change in lcn. */ + lcn_len = ntfs_write_significant_bytes(dst + 1 + + len_len, dst_max, rl->lcn - prev_lcn); + if (unlikely(lcn_len < 0)) + goto size_err; + prev_lcn = rl->lcn; + } else { + if (rl->lcn == LCN_DELALLOC) + de_cnt += rl->length; + lcn_len = 0; + } + dst_next = dst + len_len + lcn_len + 1; + if (unlikely(dst_next > dst_max)) + goto size_err; + /* Update header byte. */ + *dst = lcn_len << 4 | len_len; + /* Position at next mapping pairs array element. */ + dst = dst_next; + } + /* Success. */ + if (de_cluster_count) + *de_cluster_count = de_cnt; + err = 0; +size_err: + /* Set stop vcn. */ + if (stop_vcn) + *stop_vcn = rl->vcn; + if (stop_rl) + *stop_rl = (struct runlist_element *)rl; + /* Add terminator byte. */ + *dst = 0; + return err; +err_out: + if (rl->lcn == LCN_RL_NOT_MAPPED) + err = -EINVAL; + else + err = -EIO; + return err; +} + +/* + * ntfs_rl_truncate_nolock - truncate a runlist starting at a specified vcn + * @vol: ntfs volume (needed for error output) + * @runlist: runlist to truncate + * @new_length: the new length of the runlist in VCNs + * + * Truncate the runlist described by @runlist as well as the memory buffer + * holding the runlist elements to a length of @new_length VCNs. + * + * If @new_length lies within the runlist, the runlist elements with VCNs of + * @new_length and above are discarded. As a special case if @new_length is + * zero, the runlist is discarded and set to NULL. + * + * If @new_length lies beyond the runlist, a sparse runlist element is added to + * the end of the runlist @runlist or if the last runlist element is a sparse + * one already, this is extended. + * + * Note, no checking is done for unmapped runlist elements. It is assumed that + * the caller has mapped any elements that need to be mapped already. + * + * Return 0 on success and -errno on error. + * + * Locking: The caller must hold @runlist->lock for writing. + */ +int ntfs_rl_truncate_nolock(const struct ntfs_volume *vol, struct runlist *const runlist, + const s64 new_length) +{ + struct runlist_element *rl; + int old_size; + + ntfs_debug("Entering for new_length 0x%llx.", (long long)new_length); + + if (!runlist || new_length < 0) + return -EINVAL; + + rl = runlist->rl; + if (new_length < rl->vcn) + return -EINVAL; + + /* Find @new_length in the runlist. */ + while (likely(rl->length && new_length >= rl[1].vcn)) + rl++; + /* + * If not at the end of the runlist we need to shrink it. + * If at the end of the runlist we need to expand it. + */ + if (rl->length) { + struct runlist_element *trl; + bool is_end; + + ntfs_debug("Shrinking runlist."); + /* Determine the runlist size. */ + trl = rl + 1; + while (likely(trl->length)) + trl++; + old_size = trl - runlist->rl + 1; + /* Truncate the run. */ + rl->length = new_length - rl->vcn; + /* + * If a run was partially truncated, make the following runlist + * element a terminator. + */ + is_end = false; + if (rl->length) { + rl++; + if (!rl->length) + is_end = true; + rl->vcn = new_length; + rl->length = 0; + } + rl->lcn = LCN_ENOENT; + runlist->count = rl - runlist->rl + 1; + /* Reallocate memory if necessary. */ + if (!is_end) { + int new_size = rl - runlist->rl + 1; + + rl = ntfs_rl_realloc(runlist->rl, old_size, new_size); + if (IS_ERR(rl)) + ntfs_warning(vol->sb, + "Failed to shrink runlist buffer. This just wastes a bit of memory temporarily so we ignore it and return success."); + else + runlist->rl = rl; + } + } else if (likely(/* !rl->length && */ new_length > rl->vcn)) { + ntfs_debug("Expanding runlist."); + /* + * If there is a previous runlist element and it is a sparse + * one, extend it. Otherwise need to add a new, sparse runlist + * element. + */ + if ((rl > runlist->rl) && ((rl - 1)->lcn == LCN_HOLE)) + (rl - 1)->length = new_length - (rl - 1)->vcn; + else { + /* Determine the runlist size. */ + old_size = rl - runlist->rl + 1; + /* Reallocate memory if necessary. */ + rl = ntfs_rl_realloc(runlist->rl, old_size, + old_size + 1); + if (IS_ERR(rl)) { + ntfs_error(vol->sb, "Failed to expand runlist buffer, aborting."); + return PTR_ERR(rl); + } + runlist->rl = rl; + /* + * Set @rl to the same runlist element in the new + * runlist as before in the old runlist. + */ + rl += old_size - 1; + /* Add a new, sparse runlist element. */ + rl->lcn = LCN_HOLE; + rl->length = new_length - rl->vcn; + /* Add a new terminator runlist element. */ + rl++; + rl->length = 0; + runlist->count = old_size + 1; + } + rl->vcn = new_length; + rl->lcn = LCN_ENOENT; + } else /* if (unlikely(!rl->length && new_length == rl->vcn)) */ { + /* Runlist already has same size as requested. */ + rl->lcn = LCN_ENOENT; + } + ntfs_debug("Done."); + return 0; +} + +/* + * ntfs_rl_sparse - check whether runlist have sparse regions or not. + * @rl: runlist to check + * + * Return 1 if have, 0 if not, -errno on error. + */ +int ntfs_rl_sparse(struct runlist_element *rl) +{ + struct runlist_element *rlc; + + if (!rl) + return -EINVAL; + + for (rlc = rl; rlc->length; rlc++) + if (rlc->lcn < 0) { + if (rlc->lcn != LCN_HOLE && rlc->lcn != LCN_DELALLOC) { + pr_err("%s: bad runlist\n", __func__); + return -EINVAL; + } + return 1; + } + return 0; +} + +/* + * ntfs_rl_get_compressed_size - calculate length of non sparse regions + * @vol: ntfs volume (need for cluster size) + * @rl: runlist to calculate for + * + * Return compressed size or -errno on error. + */ +s64 ntfs_rl_get_compressed_size(struct ntfs_volume *vol, struct runlist_element *rl) +{ + struct runlist_element *rlc; + s64 ret = 0; + + if (!rl) + return -EINVAL; + + for (rlc = rl; rlc->length; rlc++) { + if (rlc->lcn < 0) { + if (rlc->lcn != LCN_HOLE && rlc->lcn != LCN_DELALLOC) { + ntfs_error(vol->sb, "%s: bad runlist, rlc->lcn : %lld", + __func__, rlc->lcn); + return -EINVAL; + } + } else + ret += rlc->length; + } + return NTFS_CLU_TO_B(vol, ret); +} + +static inline bool ntfs_rle_lcn_contiguous(struct runlist_element *left_rle, + struct runlist_element *right_rle) +{ + if (left_rle->lcn > LCN_HOLE && + left_rle->lcn + left_rle->length == right_rle->lcn) + return true; + else if (left_rle->lcn == LCN_HOLE && right_rle->lcn == LCN_HOLE) + return true; + else + return false; +} + +static inline bool ntfs_rle_contain(struct runlist_element *rle, s64 vcn) +{ + if (rle->length > 0 && + vcn >= rle->vcn && vcn < rle->vcn + rle->length) + return true; + else + return false; +} + +struct runlist_element *ntfs_rl_insert_range(struct runlist_element *dst_rl, int dst_cnt, + struct runlist_element *src_rl, int src_cnt, + size_t *new_rl_cnt) +{ + struct runlist_element *i_rl, *new_rl, *src_rl_origin = src_rl; + struct runlist_element dst_rl_split; + s64 start_vcn; + int new_1st_cnt, new_2nd_cnt, new_3rd_cnt, new_cnt; + + if (!dst_rl || !src_rl || !new_rl_cnt) + return ERR_PTR(-EINVAL); + if (dst_cnt <= 0 || src_cnt <= 0) + return ERR_PTR(-EINVAL); + if (!(dst_rl[dst_cnt - 1].lcn == LCN_ENOENT && + dst_rl[dst_cnt - 1].length == 0) || + src_rl[src_cnt - 1].lcn < LCN_HOLE) + return ERR_PTR(-EINVAL); + + start_vcn = src_rl[0].vcn; + + i_rl = ntfs_rl_find_vcn_nolock(dst_rl, start_vcn); + if (!i_rl || + (i_rl->lcn == LCN_ENOENT && i_rl->vcn != start_vcn) || + (i_rl->lcn != LCN_ENOENT && !ntfs_rle_contain(i_rl, start_vcn))) + return ERR_PTR(-EINVAL); + + new_1st_cnt = (int)(i_rl - dst_rl); + if (new_1st_cnt > dst_cnt) + return ERR_PTR(-EINVAL); + new_3rd_cnt = dst_cnt - new_1st_cnt; + if (new_3rd_cnt < 1) + return ERR_PTR(-EINVAL); + + if (i_rl[0].vcn != start_vcn) { + if (i_rl[0].lcn == LCN_HOLE && src_rl[0].lcn == LCN_HOLE) + goto merge_src_rle; + + /* split @i_rl[0] and create @dst_rl_split */ + dst_rl_split.vcn = i_rl[0].vcn; + dst_rl_split.length = start_vcn - i_rl[0].vcn; + dst_rl_split.lcn = i_rl[0].lcn; + + i_rl[0].vcn = start_vcn; + i_rl[0].length -= dst_rl_split.length; + i_rl[0].lcn += dst_rl_split.length; + } else { + struct runlist_element *dst_rle, *src_rle; +merge_src_rle: + + /* not split @i_rl[0] */ + dst_rl_split.lcn = LCN_ENOENT; + + /* merge @src_rl's first run and @i_rl[0]'s left run if possible */ + dst_rle = &dst_rl[new_1st_cnt - 1]; + src_rle = &src_rl[0]; + if (new_1st_cnt > 0 && ntfs_rle_lcn_contiguous(dst_rle, src_rle)) { + WARN_ON(dst_rle->vcn + dst_rle->length != src_rle->vcn); + dst_rle->length += src_rle->length; + src_rl++; + src_cnt--; + } else { + /* merge @src_rl's last run and @i_rl[0]'s right if possible */ + dst_rle = &dst_rl[new_1st_cnt]; + src_rle = &src_rl[src_cnt - 1]; + + if (ntfs_rle_lcn_contiguous(dst_rle, src_rle)) { + dst_rle->length += src_rle->length; + src_cnt--; + } + } + } + + new_2nd_cnt = src_cnt; + new_cnt = new_1st_cnt + new_2nd_cnt + new_3rd_cnt; + new_cnt += dst_rl_split.lcn >= LCN_HOLE ? 1 : 0; + new_rl = kvcalloc(new_cnt, sizeof(*new_rl), GFP_NOFS); + if (!new_rl) + return ERR_PTR(-ENOMEM); + + /* Copy the @dst_rl's first half to @new_rl */ + ntfs_rl_mc(new_rl, 0, dst_rl, 0, new_1st_cnt); + if (dst_rl_split.lcn >= LCN_HOLE) { + ntfs_rl_mc(new_rl, new_1st_cnt, &dst_rl_split, 0, 1); + new_1st_cnt++; + } + /* Copy the @src_rl to @new_rl */ + ntfs_rl_mc(new_rl, new_1st_cnt, src_rl, 0, new_2nd_cnt); + /* Copy the @dst_rl's second half to @new_rl */ + if (new_3rd_cnt >= 1) { + struct runlist_element *rl, *rl_3rd; + int dst_1st_cnt = dst_rl_split.lcn >= LCN_HOLE ? + new_1st_cnt - 1 : new_1st_cnt; + + ntfs_rl_mc(new_rl, new_1st_cnt + new_2nd_cnt, + dst_rl, dst_1st_cnt, new_3rd_cnt); + /* Update vcn of the @dst_rl's second half runs to reflect + * appended @src_rl. + */ + if (new_1st_cnt + new_2nd_cnt == 0) { + rl_3rd = &new_rl[new_1st_cnt + new_2nd_cnt + 1]; + rl = &new_rl[new_1st_cnt + new_2nd_cnt]; + } else { + rl_3rd = &new_rl[new_1st_cnt + new_2nd_cnt]; + rl = &new_rl[new_1st_cnt + new_2nd_cnt - 1]; + } + do { + rl_3rd->vcn = rl->vcn + rl->length; + if (rl_3rd->length <= 0) + break; + rl = rl_3rd; + rl_3rd++; + } while (1); + } + *new_rl_cnt = new_1st_cnt + new_2nd_cnt + new_3rd_cnt; + + kvfree(dst_rl); + kvfree(src_rl_origin); + return new_rl; +} + +struct runlist_element *ntfs_rl_punch_hole(struct runlist_element *dst_rl, int dst_cnt, + s64 start_vcn, s64 len, + struct runlist_element **punch_rl, + size_t *new_rl_cnt) +{ + struct runlist_element *s_rl, *e_rl, *new_rl, *dst_3rd_rl, hole_rl[1]; + s64 end_vcn; + int new_1st_cnt, dst_3rd_cnt, new_cnt, punch_cnt, merge_cnt; + bool begin_split, end_split, one_split_3; + + if (dst_cnt < 2 || + !(dst_rl[dst_cnt - 1].lcn == LCN_ENOENT && + dst_rl[dst_cnt - 1].length == 0)) + return ERR_PTR(-EINVAL); + + end_vcn = min(start_vcn + len - 1, + dst_rl[dst_cnt - 2].vcn + dst_rl[dst_cnt - 2].length - 1); + + s_rl = ntfs_rl_find_vcn_nolock(dst_rl, start_vcn); + if (!s_rl || + s_rl->lcn <= LCN_ENOENT || + !ntfs_rle_contain(s_rl, start_vcn)) + return ERR_PTR(-EINVAL); + + begin_split = s_rl->vcn != start_vcn ? true : false; + + e_rl = ntfs_rl_find_vcn_nolock(dst_rl, end_vcn); + if (!e_rl || + e_rl->lcn <= LCN_ENOENT || + !ntfs_rle_contain(e_rl, end_vcn)) + return ERR_PTR(-EINVAL); + + end_split = e_rl->vcn + e_rl->length - 1 != end_vcn ? true : false; + + /* @s_rl has to be split into left, punched hole, and right */ + one_split_3 = e_rl == s_rl && begin_split && end_split ? true : false; + + punch_cnt = (int)(e_rl - s_rl) + 1; + + *punch_rl = kvcalloc(punch_cnt + 1, sizeof(struct runlist_element), + GFP_NOFS); + if (!*punch_rl) + return ERR_PTR(-ENOMEM); + + new_cnt = dst_cnt - (int)(e_rl - s_rl + 1) + 3; + new_rl = kvcalloc(new_cnt, sizeof(struct runlist_element), GFP_NOFS); + if (!new_rl) { + kvfree(*punch_rl); + *punch_rl = NULL; + return ERR_PTR(-ENOMEM); + } + + new_1st_cnt = (int)(s_rl - dst_rl) + 1; + ntfs_rl_mc(*punch_rl, 0, dst_rl, new_1st_cnt - 1, punch_cnt); + + (*punch_rl)[punch_cnt].lcn = LCN_ENOENT; + (*punch_rl)[punch_cnt].length = 0; + + if (!begin_split) + new_1st_cnt--; + dst_3rd_rl = e_rl; + dst_3rd_cnt = (int)(&dst_rl[dst_cnt - 1] - e_rl) + 1; + if (!end_split) { + dst_3rd_rl++; + dst_3rd_cnt--; + } + + /* Copy the 1st part of @dst_rl into @new_rl */ + ntfs_rl_mc(new_rl, 0, dst_rl, 0, new_1st_cnt); + if (begin_split) { + /* the @e_rl has to be splited and copied into the last of @new_rl + * and the first of @punch_rl + */ + s64 first_cnt = start_vcn - dst_rl[new_1st_cnt - 1].vcn; + + if (new_1st_cnt) + new_rl[new_1st_cnt - 1].length = first_cnt; + + (*punch_rl)[0].vcn = start_vcn; + (*punch_rl)[0].length -= first_cnt; + if ((*punch_rl)[0].lcn > LCN_HOLE) + (*punch_rl)[0].lcn += first_cnt; + } + + /* Copy a hole into @new_rl */ + hole_rl[0].vcn = start_vcn; + hole_rl[0].length = (s64)len; + hole_rl[0].lcn = LCN_HOLE; + ntfs_rl_mc(new_rl, new_1st_cnt, hole_rl, 0, 1); + + /* Copy the 3rd part of @dst_rl into @new_rl */ + ntfs_rl_mc(new_rl, new_1st_cnt + 1, dst_3rd_rl, 0, dst_3rd_cnt); + if (end_split) { + /* the @e_rl has to be splited and copied into the first of + * @new_rl and the last of @punch_rl + */ + s64 first_cnt = end_vcn - dst_3rd_rl[0].vcn + 1; + + new_rl[new_1st_cnt + 1].vcn = end_vcn + 1; + new_rl[new_1st_cnt + 1].length -= first_cnt; + if (new_rl[new_1st_cnt + 1].lcn > LCN_HOLE) + new_rl[new_1st_cnt + 1].lcn += first_cnt; + + if (one_split_3) + (*punch_rl)[punch_cnt - 1].length -= + new_rl[new_1st_cnt + 1].length; + else + (*punch_rl)[punch_cnt - 1].length = first_cnt; + } + + /* Merge left and hole, or hole and right in @new_rl, if left or right + * consists of holes. + */ + merge_cnt = 0; + if (new_1st_cnt > 0 && new_rl[new_1st_cnt - 1].lcn == LCN_HOLE) { + /* Merge right and hole */ + s_rl = &new_rl[new_1st_cnt - 1]; + s_rl->length += s_rl[1].length; + merge_cnt = 1; + /* Merge left and right */ + if (new_1st_cnt + 1 < new_cnt && + new_rl[new_1st_cnt + 1].lcn == LCN_HOLE) { + s_rl->length += s_rl[2].length; + merge_cnt++; + } + } else if (new_1st_cnt + 1 < new_cnt && + new_rl[new_1st_cnt + 1].lcn == LCN_HOLE) { + /* Merge left and hole */ + s_rl = &new_rl[new_1st_cnt]; + s_rl->length += s_rl[1].length; + merge_cnt = 1; + } + if (merge_cnt) { + struct runlist_element *d_rl, *src_rl; + + d_rl = s_rl + 1; + src_rl = s_rl + 1 + merge_cnt; + ntfs_rl_mm(new_rl, (int)(d_rl - new_rl), (int)(src_rl - new_rl), + (int)(&new_rl[new_cnt - 1] - src_rl) + 1); + } + + (*punch_rl)[punch_cnt].vcn = (*punch_rl)[punch_cnt - 1].vcn + + (*punch_rl)[punch_cnt - 1].length; + + /* punch_cnt elements of dst are replaced with one hole */ + *new_rl_cnt = dst_cnt - (punch_cnt - (int)begin_split - (int)end_split) + + 1 - merge_cnt; + kvfree(dst_rl); + return new_rl; +} + +struct runlist_element *ntfs_rl_collapse_range(struct runlist_element *dst_rl, int dst_cnt, + s64 start_vcn, s64 len, + struct runlist_element **punch_rl, + size_t *new_rl_cnt) +{ + struct runlist_element *s_rl, *e_rl, *new_rl, *dst_3rd_rl; + s64 end_vcn; + int new_1st_cnt, dst_3rd_cnt, new_cnt, punch_cnt, merge_cnt, i; + bool begin_split, end_split, one_split_3; + + if (dst_cnt < 2 || + !(dst_rl[dst_cnt - 1].lcn == LCN_ENOENT && + dst_rl[dst_cnt - 1].length == 0)) + return ERR_PTR(-EINVAL); + + end_vcn = min(start_vcn + len - 1, + dst_rl[dst_cnt - 1].vcn - 1); + + s_rl = ntfs_rl_find_vcn_nolock(dst_rl, start_vcn); + if (!s_rl || + s_rl->lcn <= LCN_ENOENT || + !ntfs_rle_contain(s_rl, start_vcn)) + return ERR_PTR(-EINVAL); + + begin_split = s_rl->vcn != start_vcn ? true : false; + + e_rl = ntfs_rl_find_vcn_nolock(dst_rl, end_vcn); + if (!e_rl || + e_rl->lcn <= LCN_ENOENT || + !ntfs_rle_contain(e_rl, end_vcn)) + return ERR_PTR(-EINVAL); + + end_split = e_rl->vcn + e_rl->length - 1 != end_vcn ? true : false; + + /* @s_rl has to be split into left, collapsed, and right */ + one_split_3 = e_rl == s_rl && begin_split && end_split ? true : false; + + punch_cnt = (int)(e_rl - s_rl) + 1; + *punch_rl = kvcalloc(punch_cnt + 1, sizeof(struct runlist_element), + GFP_NOFS); + if (!*punch_rl) + return ERR_PTR(-ENOMEM); + + new_cnt = dst_cnt - (int)(e_rl - s_rl + 1) + 3; + new_rl = kvcalloc(new_cnt, sizeof(struct runlist_element), GFP_NOFS); + if (!new_rl) { + kvfree(*punch_rl); + *punch_rl = NULL; + return ERR_PTR(-ENOMEM); + } + + new_1st_cnt = (int)(s_rl - dst_rl) + 1; + ntfs_rl_mc(*punch_rl, 0, dst_rl, new_1st_cnt - 1, punch_cnt); + (*punch_rl)[punch_cnt].lcn = LCN_ENOENT; + (*punch_rl)[punch_cnt].length = 0; + + if (!begin_split) + new_1st_cnt--; + dst_3rd_rl = e_rl; + dst_3rd_cnt = (int)(&dst_rl[dst_cnt - 1] - e_rl) + 1; + if (!end_split) { + dst_3rd_rl++; + dst_3rd_cnt--; + } + + /* Copy the 1st part of @dst_rl into @new_rl */ + ntfs_rl_mc(new_rl, 0, dst_rl, 0, new_1st_cnt); + if (begin_split) { + /* the @e_rl has to be splited and copied into the last of @new_rl + * and the first of @punch_rl + */ + s64 first_cnt = start_vcn - dst_rl[new_1st_cnt - 1].vcn; + + new_rl[new_1st_cnt - 1].length = first_cnt; + + (*punch_rl)[0].vcn = start_vcn; + (*punch_rl)[0].length -= first_cnt; + if ((*punch_rl)[0].lcn > LCN_HOLE) + (*punch_rl)[0].lcn += first_cnt; + } + + /* Copy the 3rd part of @dst_rl into @new_rl */ + ntfs_rl_mc(new_rl, new_1st_cnt, dst_3rd_rl, 0, dst_3rd_cnt); + if (end_split) { + /* the @e_rl has to be splited and copied into the first of + * @new_rl and the last of @punch_rl + */ + s64 first_cnt = end_vcn - dst_3rd_rl[0].vcn + 1; + + new_rl[new_1st_cnt].vcn = end_vcn + 1; + new_rl[new_1st_cnt].length -= first_cnt; + if (new_rl[new_1st_cnt].lcn > LCN_HOLE) + new_rl[new_1st_cnt].lcn += first_cnt; + + if (one_split_3) + (*punch_rl)[punch_cnt - 1].length -= + new_rl[new_1st_cnt].length; + else + (*punch_rl)[punch_cnt - 1].length = first_cnt; + } + + /* Adjust vcn */ + if (new_1st_cnt == 0) + new_rl[new_1st_cnt].vcn = 0; + for (i = new_1st_cnt == 0 ? 1 : new_1st_cnt; new_rl[i].length; i++) + new_rl[i].vcn = new_rl[i - 1].vcn + new_rl[i - 1].length; + new_rl[i].vcn = new_rl[i - 1].vcn + new_rl[i - 1].length; + + /* Merge left and hole, or hole and right in @new_rl, if left or right + * consists of holes. + */ + merge_cnt = 0; + i = new_1st_cnt == 0 ? 1 : new_1st_cnt; + if (ntfs_rle_lcn_contiguous(&new_rl[i - 1], &new_rl[i])) { + /* Merge right and left */ + s_rl = &new_rl[new_1st_cnt - 1]; + s_rl->length += s_rl[1].length; + merge_cnt = 1; + } + if (merge_cnt) { + struct runlist_element *d_rl, *src_rl; + + d_rl = s_rl + 1; + src_rl = s_rl + 1 + merge_cnt; + ntfs_rl_mm(new_rl, (int)(d_rl - new_rl), (int)(src_rl - new_rl), + (int)(&new_rl[new_cnt - 1] - src_rl) + 1); + } + + (*punch_rl)[punch_cnt].vcn = (*punch_rl)[punch_cnt - 1].vcn + + (*punch_rl)[punch_cnt - 1].length; + + /* punch_cnt elements of dst are extracted */ + *new_rl_cnt = dst_cnt - (punch_cnt - (int)begin_split - (int)end_split) - + merge_cnt; + + kvfree(dst_rl); + return new_rl; +} diff --git a/fs/ntfs/runlist.h b/fs/ntfs/runlist.h new file mode 100644 index 000000000000..cc92e09fbf68 --- /dev/null +++ b/fs/ntfs/runlist.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for runlist handling in NTFS Linux kernel driver. + * + * Copyright (c) 2001-2005 Anton Altaparmakov + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#ifndef _LINUX_NTFS_RUNLIST_H +#define _LINUX_NTFS_RUNLIST_H + +#include "volume.h" + +/* + * runlist_element - in memory vcn to lcn mapping array element + * @vcn: starting vcn of the current array element + * @lcn: starting lcn of the current array element + * @length: length in clusters of the current array element + * + * The last vcn (in fact the last vcn + 1) is reached when length == 0. + * + * When lcn == -1 this means that the count vcns starting at vcn are not + * physically allocated (i.e. this is a hole / data is sparse). + * + * In memory vcn to lcn mapping structure element. + * @vcn: vcn = Starting virtual cluster number. + * @lcn: lcn = Starting logical cluster number. + * @length: Run length in clusters. + */ +struct runlist_element { + s64 vcn; + s64 lcn; + s64 length; +}; + +/* + * runlist - in memory vcn to lcn mapping array including a read/write lock + * @rl: pointer to an array of runlist elements + * @lock: read/write spinlock for serializing access to @rl + * @rl_hint: hint/cache pointing to the last accessed runlist element + */ +struct runlist { + struct runlist_element *rl; + struct rw_semaphore lock; + size_t count; + int rl_hint; +}; + +static inline void ntfs_init_runlist(struct runlist *rl) +{ + rl->rl = NULL; + init_rwsem(&rl->lock); + rl->count = 0; + rl->rl_hint = -1; +} + +enum { + LCN_DELALLOC = -1, + LCN_HOLE = -2, + LCN_RL_NOT_MAPPED = -3, + LCN_ENOENT = -4, + LCN_ENOMEM = -5, + LCN_EIO = -6, + LCN_EINVAL = -7, +}; + +struct runlist_element *ntfs_runlists_merge(struct runlist *d_runlist, + struct runlist_element *srl, size_t s_rl_count, + size_t *new_rl_count); +struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume *vol, + const struct attr_record *attr, struct runlist *old_runlist, + size_t *new_rl_count); +s64 ntfs_rl_vcn_to_lcn(const struct runlist_element *rl, const s64 vcn); +struct runlist_element *ntfs_rl_find_vcn_nolock(struct runlist_element *rl, const s64 vcn); +int ntfs_get_size_for_mapping_pairs(const struct ntfs_volume *vol, + const struct runlist_element *rl, const s64 first_vcn, + const s64 last_vcn, int max_mp_size); +int ntfs_mapping_pairs_build(const struct ntfs_volume *vol, s8 *dst, + const int dst_len, const struct runlist_element *rl, + const s64 first_vcn, const s64 last_vcn, s64 *const stop_vcn, + struct runlist_element **stop_rl, unsigned int *de_cluster_count); +int ntfs_rl_truncate_nolock(const struct ntfs_volume *vol, + struct runlist *const runlist, const s64 new_length); +int ntfs_rl_sparse(struct runlist_element *rl); +s64 ntfs_rl_get_compressed_size(struct ntfs_volume *vol, struct runlist_element *rl); +struct runlist_element *ntfs_rl_insert_range(struct runlist_element *dst_rl, int dst_cnt, + struct runlist_element *src_rl, int src_cnt, size_t *new_cnt); +struct runlist_element *ntfs_rl_punch_hole(struct runlist_element *dst_rl, int dst_cnt, + s64 start_vcn, s64 len, struct runlist_element **punch_rl, + size_t *new_rl_cnt); +struct runlist_element *ntfs_rl_collapse_range(struct runlist_element *dst_rl, int dst_cnt, + s64 start_vcn, s64 len, struct runlist_element **punch_rl, + size_t *new_rl_cnt); +struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl, int old_size, + int new_size); +#endif /* _LINUX_NTFS_RUNLIST_H */ diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c new file mode 100644 index 000000000000..22dc7865eca7 --- /dev/null +++ b/fs/ntfs/super.c @@ -0,0 +1,2765 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS kernel super block handling. + * + * Copyright (c) 2001-2012 Anton Altaparmakov and Tuxera Inc. + * Copyright (c) 2001,2002 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#include /* For bdev_logical_block_size(). */ +#include +#include +#include +#include +#include +#include + +#include "sysctl.h" +#include "logfile.h" +#include "quota.h" +#include "index.h" +#include "ntfs.h" +#include "ea.h" +#include "volume.h" + +/* A global default upcase table and a corresponding reference count. */ +static __le16 *default_upcase; +static unsigned long ntfs_nr_upcase_users; + +static struct workqueue_struct *ntfs_wq; + +/* Error constants/strings used in inode.c::ntfs_show_options(). */ +enum { + /* One of these must be present, default is ON_ERRORS_CONTINUE. */ + ON_ERRORS_PANIC = 0x01, + ON_ERRORS_REMOUNT_RO = 0x02, + ON_ERRORS_CONTINUE = 0x04, +}; + +static const struct constant_table ntfs_param_enums[] = { + { "panic", ON_ERRORS_PANIC }, + { "remount-ro", ON_ERRORS_REMOUNT_RO }, + { "continue", ON_ERRORS_CONTINUE }, + {} +}; + +enum { + Opt_uid, + Opt_gid, + Opt_umask, + Opt_dmask, + Opt_fmask, + Opt_errors, + Opt_nls, + Opt_charset, + Opt_show_sys_files, + Opt_show_meta, + Opt_case_sensitive, + Opt_disable_sparse, + Opt_sparse, + Opt_mft_zone_multiplier, + Opt_preallocated_size, + Opt_sys_immutable, + Opt_nohidden, + Opt_hide_dot_files, + Opt_check_windows_names, + Opt_acl, + Opt_discard, + Opt_nocase, +}; + +static const struct fs_parameter_spec ntfs_parameters[] = { + fsparam_u32("uid", Opt_uid), + fsparam_u32("gid", Opt_gid), + fsparam_u32oct("umask", Opt_umask), + fsparam_u32oct("dmask", Opt_dmask), + fsparam_u32oct("fmask", Opt_fmask), + fsparam_string("nls", Opt_nls), + fsparam_string("iocharset", Opt_charset), + fsparam_enum("errors", Opt_errors, ntfs_param_enums), + fsparam_flag("show_sys_files", Opt_show_sys_files), + fsparam_flag("showmeta", Opt_show_meta), + fsparam_flag("case_sensitive", Opt_case_sensitive), + fsparam_flag("disable_sparse", Opt_disable_sparse), + fsparam_s32("mft_zone_multiplier", Opt_mft_zone_multiplier), + fsparam_u64("preallocated_size", Opt_preallocated_size), + fsparam_flag("sys_immutable", Opt_sys_immutable), + fsparam_flag("nohidden", Opt_nohidden), + fsparam_flag("hide_dot_files", Opt_hide_dot_files), + fsparam_flag("windows_names", Opt_check_windows_names), + fsparam_flag("acl", Opt_acl), + fsparam_flag("discard", Opt_discard), + fsparam_flag("sparse", Opt_sparse), + fsparam_flag("nocase", Opt_nocase), + {} +}; + +static int ntfs_parse_param(struct fs_context *fc, struct fs_parameter *param) +{ + struct ntfs_volume *vol = fc->s_fs_info; + struct fs_parse_result result; + int opt; + + opt = fs_parse(fc, ntfs_parameters, param, &result); + if (opt < 0) + return opt; + + switch (opt) { + case Opt_uid: + vol->uid = make_kuid(current_user_ns(), result.uint_32); + break; + case Opt_gid: + vol->gid = make_kgid(current_user_ns(), result.uint_32); + break; + case Opt_umask: + vol->fmask = vol->dmask = result.uint_32; + break; + case Opt_dmask: + vol->dmask = result.uint_32; + break; + case Opt_fmask: + vol->fmask = result.uint_32; + break; + case Opt_errors: + vol->on_errors = result.uint_32; + break; + case Opt_nls: + case Opt_charset: + if (vol->nls_map) + unload_nls(vol->nls_map); + vol->nls_map = load_nls(param->string); + if (!vol->nls_map) { + ntfs_error(vol->sb, "Failed to load NLS table '%s'.", + param->string); + return -EINVAL; + } + break; + case Opt_mft_zone_multiplier: + if (vol->mft_zone_multiplier && vol->mft_zone_multiplier != + result.int_32) { + ntfs_error(vol->sb, "Cannot change mft_zone_multiplier on remount."); + return -EINVAL; + } + if (result.int_32 < 1 || result.int_32 > 4) { + ntfs_error(vol->sb, + "Invalid mft_zone_multiplier. Using default value, i.e. 1."); + vol->mft_zone_multiplier = 1; + } else + vol->mft_zone_multiplier = result.int_32; + break; + case Opt_show_sys_files: + case Opt_show_meta: + if (result.boolean) + NVolSetShowSystemFiles(vol); + else + NVolClearShowSystemFiles(vol); + break; + case Opt_case_sensitive: + if (result.boolean) + NVolSetCaseSensitive(vol); + else + NVolClearCaseSensitive(vol); + break; + case Opt_nocase: + if (result.boolean) + NVolClearCaseSensitive(vol); + else + NVolSetCaseSensitive(vol); + break; + case Opt_preallocated_size: + vol->preallocated_size = (loff_t)result.uint_64; + break; + case Opt_sys_immutable: + if (result.boolean) + NVolSetSysImmutable(vol); + else + NVolClearSysImmutable(vol); + break; + case Opt_nohidden: + if (result.boolean) + NVolClearShowHiddenFiles(vol); + else + NVolSetShowHiddenFiles(vol); + break; + case Opt_hide_dot_files: + if (result.boolean) + NVolSetHideDotFiles(vol); + else + NVolClearHideDotFiles(vol); + break; + case Opt_check_windows_names: + if (result.boolean) + NVolSetCheckWindowsNames(vol); + else + NVolClearCheckWindowsNames(vol); + break; + case Opt_acl: +#ifdef CONFIG_NTFS_FS_POSIX_ACL + if (result.boolean) + fc->sb_flags |= SB_POSIXACL; + else + fc->sb_flags &= ~SB_POSIXACL; + break; +#else + return -EINVAL; +#endif + case Opt_discard: + if (result.boolean) + NVolSetDiscard(vol); + else + NVolClearDiscard(vol); + break; + case Opt_disable_sparse: + if (result.boolean) + NVolSetDisableSparse(vol); + else + NVolClearDisableSparse(vol); + break; + case Opt_sparse: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ntfs_reconfigure(struct fs_context *fc) +{ + struct super_block *sb = fc->root->d_sb; + struct ntfs_volume *vol = NTFS_SB(sb); + + ntfs_debug("Entering with remount"); + + sync_filesystem(sb); + + /* + * For the read-write compiled driver, if we are remounting read-write, + * make sure there are no volume errors and that no unsupported volume + * flags are set. Also, empty the logfile journal as it would become + * stale as soon as something is written to the volume and mark the + * volume dirty so that chkdsk is run if the volume is not umounted + * cleanly. Finally, mark the quotas out of date so Windows rescans + * the volume on boot and updates them. + * + * When remounting read-only, mark the volume clean if no volume errors + * have occurred. + */ + if (sb_rdonly(sb) && !(fc->sb_flags & SB_RDONLY)) { + static const char *es = ". Cannot remount read-write."; + + /* Remounting read-write. */ + if (NVolErrors(vol)) { + ntfs_error(sb, "Volume has errors and is read-only%s", + es); + return -EROFS; + } + if (vol->vol_flags & VOLUME_IS_DIRTY) { + ntfs_error(sb, "Volume is dirty and read-only%s", es); + return -EROFS; + } + if (vol->vol_flags & VOLUME_MODIFIED_BY_CHKDSK) { + ntfs_error(sb, "Volume has been modified by chkdsk and is read-only%s", es); + return -EROFS; + } + if (vol->vol_flags & VOLUME_MUST_MOUNT_RO_MASK) { + ntfs_error(sb, "Volume has unsupported flags set (0x%x) and is read-only%s", + le16_to_cpu(vol->vol_flags), es); + return -EROFS; + } + if (vol->logfile_ino && !ntfs_empty_logfile(vol->logfile_ino)) { + ntfs_error(sb, "Failed to empty journal LogFile%s", + es); + NVolSetErrors(vol); + return -EROFS; + } + if (!ntfs_mark_quotas_out_of_date(vol)) { + ntfs_error(sb, "Failed to mark quotas out of date%s", + es); + NVolSetErrors(vol); + return -EROFS; + } + } else if (!sb_rdonly(sb) && (fc->sb_flags & SB_RDONLY)) { + /* Remounting read-only. */ + if (!NVolErrors(vol)) { + if (ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) + ntfs_warning(sb, + "Failed to clear dirty bit in volume information flags. Run chkdsk."); + } + } + + ntfs_debug("Done."); + return 0; +} + +const struct option_t on_errors_arr[] = { + { ON_ERRORS_PANIC, "panic" }, + { ON_ERRORS_REMOUNT_RO, "remount-ro", }, + { ON_ERRORS_CONTINUE, "continue", }, + { 0, NULL } +}; + +void ntfs_handle_error(struct super_block *sb) +{ + struct ntfs_volume *vol = NTFS_SB(sb); + + if (sb_rdonly(sb)) + return; + + if (vol->on_errors == ON_ERRORS_REMOUNT_RO) { + sb->s_flags |= SB_RDONLY; + pr_crit("(device %s): Filesystem has been set read-only\n", + sb->s_id); + } else if (vol->on_errors == ON_ERRORS_PANIC) { + panic("ntfs: (device %s): panic from previous error\n", + sb->s_id); + } else if (vol->on_errors == ON_ERRORS_CONTINUE) { + if (errseq_check(&sb->s_wb_err, vol->wb_err) == -ENODEV) { + NVolSetShutdown(vol); + vol->wb_err = sb->s_wb_err; + } + } +} + +/* + * ntfs_write_volume_flags - write new flags to the volume information flags + * @vol: ntfs volume on which to modify the flags + * @flags: new flags value for the volume information flags + * + * Internal function. You probably want to use ntfs_{set,clear}_volume_flags() + * instead (see below). + * + * Replace the volume information flags on the volume @vol with the value + * supplied in @flags. Note, this overwrites the volume information flags, so + * make sure to combine the flags you want to modify with the old flags and use + * the result when calling ntfs_write_volume_flags(). + * + * Return 0 on success and -errno on error. + */ +static int ntfs_write_volume_flags(struct ntfs_volume *vol, const __le16 flags) +{ + struct ntfs_inode *ni = NTFS_I(vol->vol_ino); + struct volume_information *vi; + struct ntfs_attr_search_ctx *ctx; + int err; + + ntfs_debug("Entering, old flags = 0x%x, new flags = 0x%x.", + le16_to_cpu(vol->vol_flags), le16_to_cpu(flags)); + mutex_lock(&ni->mrec_lock); + if (vol->vol_flags == flags) + goto done; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = -ENOMEM; + goto put_unm_err_out; + } + + err = ntfs_attr_lookup(AT_VOLUME_INFORMATION, NULL, 0, 0, 0, NULL, 0, + ctx); + if (err) + goto put_unm_err_out; + + vi = (struct volume_information *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)); + vol->vol_flags = vi->flags = flags; + mark_mft_record_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); +done: + mutex_unlock(&ni->mrec_lock); + ntfs_debug("Done."); + return 0; +put_unm_err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + mutex_unlock(&ni->mrec_lock); + ntfs_error(vol->sb, "Failed with error code %i.", -err); + return err; +} + +/* + * ntfs_set_volume_flags - set bits in the volume information flags + * @vol: ntfs volume on which to modify the flags + * @flags: flags to set on the volume + * + * Set the bits in @flags in the volume information flags on the volume @vol. + * + * Return 0 on success and -errno on error. + */ +int ntfs_set_volume_flags(struct ntfs_volume *vol, __le16 flags) +{ + flags &= VOLUME_FLAGS_MASK; + return ntfs_write_volume_flags(vol, vol->vol_flags | flags); +} + +/* + * ntfs_clear_volume_flags - clear bits in the volume information flags + * @vol: ntfs volume on which to modify the flags + * @flags: flags to clear on the volume + * + * Clear the bits in @flags in the volume information flags on the volume @vol. + * + * Return 0 on success and -errno on error. + */ +int ntfs_clear_volume_flags(struct ntfs_volume *vol, __le16 flags) +{ + flags &= VOLUME_FLAGS_MASK; + flags = vol->vol_flags & cpu_to_le16(~le16_to_cpu(flags)); + return ntfs_write_volume_flags(vol, flags); +} + +int ntfs_write_volume_label(struct ntfs_volume *vol, char *label) +{ + struct ntfs_inode *vol_ni = NTFS_I(vol->vol_ino); + struct ntfs_attr_search_ctx *ctx; + __le16 *uname; + int uname_len, ret; + + uname_len = ntfs_nlstoucs(vol, label, strlen(label), + &uname, FSLABEL_MAX); + if (uname_len < 0) { + ntfs_error(vol->sb, + "Failed to convert volume label '%s' to Unicode.", + label); + return uname_len; + } + + if (uname_len > NTFS_MAX_LABEL_LEN) { + ntfs_error(vol->sb, + "Volume label is too long (max %d characters).", + NTFS_MAX_LABEL_LEN); + kvfree(uname); + return -EINVAL; + } + + mutex_lock(&vol_ni->mrec_lock); + ctx = ntfs_attr_get_search_ctx(vol_ni, NULL); + if (!ctx) { + ret = -ENOMEM; + goto out; + } + + if (!ntfs_attr_lookup(AT_VOLUME_NAME, NULL, 0, 0, 0, NULL, 0, + ctx)) + ntfs_attr_record_rm(ctx); + ntfs_attr_put_search_ctx(ctx); + + ret = ntfs_resident_attr_record_add(vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0, + (u8 *)uname, uname_len * sizeof(__le16), 0); +out: + mutex_unlock(&vol_ni->mrec_lock); + kvfree(uname); + mark_inode_dirty_sync(vol->vol_ino); + + if (ret >= 0) { + kfree(vol->volume_label); + vol->volume_label = kstrdup(label, GFP_KERNEL); + ret = 0; + } + return ret; +} + +/* + * is_boot_sector_ntfs - check whether a boot sector is a valid NTFS boot sector + * @sb: Super block of the device to which @b belongs. + * @b: Boot sector of device @sb to check. + * @silent: If 'true', all output will be silenced. + * + * is_boot_sector_ntfs() checks whether the boot sector @b is a valid NTFS boot + * sector. Returns 'true' if it is valid and 'false' if not. + * + * @sb is only needed for warning/error output, i.e. it can be NULL when silent + * is 'true'. + */ +static bool is_boot_sector_ntfs(const struct super_block *sb, + const struct ntfs_boot_sector *b, const bool silent) +{ + /* + * Check that checksum == sum of u32 values from b to the checksum + * field. If checksum is zero, no checking is done. We will work when + * the checksum test fails, since some utilities update the boot sector + * ignoring the checksum which leaves the checksum out-of-date. We + * report a warning if this is the case. + */ + if ((void *)b < (void *)&b->checksum && b->checksum && !silent) { + __le32 *u; + u32 i; + + for (i = 0, u = (__le32 *)b; u < (__le32 *)(&b->checksum); ++u) + i += le32_to_cpup(u); + if (le32_to_cpu(b->checksum) != i) + ntfs_warning(sb, "Invalid boot sector checksum."); + } + /* Check OEMidentifier is "NTFS " */ + if (b->oem_id != magicNTFS) + goto not_ntfs; + /* Check bytes per sector value is between 256 and 4096. */ + if (le16_to_cpu(b->bpb.bytes_per_sector) < 0x100 || + le16_to_cpu(b->bpb.bytes_per_sector) > 0x1000) + goto not_ntfs; + /* + * Check sectors per cluster value is valid and the cluster size + * is not above the maximum (2MB). + */ + if (b->bpb.sectors_per_cluster > 0x80 && + b->bpb.sectors_per_cluster < 0xf4) + goto not_ntfs; + + /* Check reserved/unused fields are really zero. */ + if (le16_to_cpu(b->bpb.reserved_sectors) || + le16_to_cpu(b->bpb.root_entries) || + le16_to_cpu(b->bpb.sectors) || + le16_to_cpu(b->bpb.sectors_per_fat) || + le32_to_cpu(b->bpb.large_sectors) || b->bpb.fats) + goto not_ntfs; + /* Check clusters per file mft record value is valid. */ + if ((u8)b->clusters_per_mft_record < 0xe1 || + (u8)b->clusters_per_mft_record > 0xf7) + switch (b->clusters_per_mft_record) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: + break; + default: + goto not_ntfs; + } + /* Check clusters per index block value is valid. */ + if ((u8)b->clusters_per_index_record < 0xe1 || + (u8)b->clusters_per_index_record > 0xf7) + switch (b->clusters_per_index_record) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: + break; + default: + goto not_ntfs; + } + /* + * Check for valid end of sector marker. We will work without it, but + * many BIOSes will refuse to boot from a bootsector if the magic is + * incorrect, so we emit a warning. + */ + if (!silent && b->end_of_sector_marker != cpu_to_le16(0xaa55)) + ntfs_warning(sb, "Invalid end of sector marker."); + return true; +not_ntfs: + return false; +} + +/* + * read_ntfs_boot_sector - read the NTFS boot sector of a device + * @sb: super block of device to read the boot sector from + * @silent: if true, suppress all output + * + * Reads the boot sector from the device and validates it. + */ +static char *read_ntfs_boot_sector(struct super_block *sb, + const int silent) +{ + char *boot_sector; + + boot_sector = kzalloc(PAGE_SIZE, GFP_NOFS); + if (!boot_sector) + return NULL; + + if (ntfs_bdev_read(sb->s_bdev, boot_sector, 0, PAGE_SIZE)) { + if (!silent) + ntfs_error(sb, "Unable to read primary boot sector."); + kfree(boot_sector); + return NULL; + } + + if (!is_boot_sector_ntfs(sb, (struct ntfs_boot_sector *)boot_sector, + silent)) { + if (!silent) + ntfs_error(sb, "Primary boot sector is invalid."); + kfree(boot_sector); + return NULL; + } + + return boot_sector; +} + +/* + * parse_ntfs_boot_sector - parse the boot sector and store the data in @vol + * @vol: volume structure to initialise with data from boot sector + * @b: boot sector to parse + * + * Parse the ntfs boot sector @b and store all imporant information therein in + * the ntfs super block @vol. Return 'true' on success and 'false' on error. + */ +static bool parse_ntfs_boot_sector(struct ntfs_volume *vol, + const struct ntfs_boot_sector *b) +{ + unsigned int sectors_per_cluster, sectors_per_cluster_bits, nr_hidden_sects; + int clusters_per_mft_record, clusters_per_index_record; + s64 ll; + + vol->sector_size = le16_to_cpu(b->bpb.bytes_per_sector); + vol->sector_size_bits = ffs(vol->sector_size) - 1; + ntfs_debug("vol->sector_size = %i (0x%x)", vol->sector_size, + vol->sector_size); + ntfs_debug("vol->sector_size_bits = %i (0x%x)", vol->sector_size_bits, + vol->sector_size_bits); + if (vol->sector_size < vol->sb->s_blocksize) { + ntfs_error(vol->sb, + "Sector size (%i) is smaller than the device block size (%lu). This is not supported.", + vol->sector_size, vol->sb->s_blocksize); + return false; + } + + if (b->bpb.sectors_per_cluster >= 0xf4) + sectors_per_cluster = 1U << -(s8)b->bpb.sectors_per_cluster; + else + sectors_per_cluster = b->bpb.sectors_per_cluster; + ntfs_debug("sectors_per_cluster = 0x%x", b->bpb.sectors_per_cluster); + sectors_per_cluster_bits = ffs(sectors_per_cluster) - 1; + ntfs_debug("sectors_per_cluster_bits = 0x%x", + sectors_per_cluster_bits); + nr_hidden_sects = le32_to_cpu(b->bpb.hidden_sectors); + ntfs_debug("number of hidden sectors = 0x%x", nr_hidden_sects); + vol->cluster_size = vol->sector_size << sectors_per_cluster_bits; + vol->cluster_size_mask = vol->cluster_size - 1; + vol->cluster_size_bits = ffs(vol->cluster_size) - 1; + ntfs_debug("vol->cluster_size = %i (0x%x)", vol->cluster_size, + vol->cluster_size); + ntfs_debug("vol->cluster_size_mask = 0x%x", vol->cluster_size_mask); + ntfs_debug("vol->cluster_size_bits = %i", vol->cluster_size_bits); + if (vol->cluster_size < vol->sector_size) { + ntfs_error(vol->sb, + "Cluster size (%i) is smaller than the sector size (%i). This is not supported.", + vol->cluster_size, vol->sector_size); + return false; + } + clusters_per_mft_record = b->clusters_per_mft_record; + ntfs_debug("clusters_per_mft_record = %i (0x%x)", + clusters_per_mft_record, clusters_per_mft_record); + if (clusters_per_mft_record > 0) + vol->mft_record_size = vol->cluster_size << + (ffs(clusters_per_mft_record) - 1); + else + /* + * When mft_record_size < cluster_size, clusters_per_mft_record + * = -log2(mft_record_size) bytes. mft_record_size normaly is + * 1024 bytes, which is encoded as 0xF6 (-10 in decimal). + */ + vol->mft_record_size = 1 << -clusters_per_mft_record; + vol->mft_record_size_mask = vol->mft_record_size - 1; + vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; + ntfs_debug("vol->mft_record_size = %i (0x%x)", vol->mft_record_size, + vol->mft_record_size); + ntfs_debug("vol->mft_record_size_mask = 0x%x", + vol->mft_record_size_mask); + ntfs_debug("vol->mft_record_size_bits = %i (0x%x)", + vol->mft_record_size_bits, vol->mft_record_size_bits); + /* + * We cannot support mft record sizes above the PAGE_SIZE since + * we store $MFT/$DATA, the table of mft records in the page cache. + */ + if (vol->mft_record_size > PAGE_SIZE) { + ntfs_error(vol->sb, + "Mft record size (%i) exceeds the PAGE_SIZE on your system (%lu). This is not supported.", + vol->mft_record_size, PAGE_SIZE); + return false; + } + /* We cannot support mft record sizes below the sector size. */ + if (vol->mft_record_size < vol->sector_size) { + ntfs_warning(vol->sb, "Mft record size (%i) is smaller than the sector size (%i).", + vol->mft_record_size, vol->sector_size); + } + clusters_per_index_record = b->clusters_per_index_record; + ntfs_debug("clusters_per_index_record = %i (0x%x)", + clusters_per_index_record, clusters_per_index_record); + if (clusters_per_index_record > 0) + vol->index_record_size = vol->cluster_size << + (ffs(clusters_per_index_record) - 1); + else + /* + * When index_record_size < cluster_size, + * clusters_per_index_record = -log2(index_record_size) bytes. + * index_record_size normaly equals 4096 bytes, which is + * encoded as 0xF4 (-12 in decimal). + */ + vol->index_record_size = 1 << -clusters_per_index_record; + vol->index_record_size_mask = vol->index_record_size - 1; + vol->index_record_size_bits = ffs(vol->index_record_size) - 1; + ntfs_debug("vol->index_record_size = %i (0x%x)", + vol->index_record_size, vol->index_record_size); + ntfs_debug("vol->index_record_size_mask = 0x%x", + vol->index_record_size_mask); + ntfs_debug("vol->index_record_size_bits = %i (0x%x)", + vol->index_record_size_bits, + vol->index_record_size_bits); + /* We cannot support index record sizes below the sector size. */ + if (vol->index_record_size < vol->sector_size) { + ntfs_error(vol->sb, + "Index record size (%i) is smaller than the sector size (%i). This is not supported.", + vol->index_record_size, vol->sector_size); + return false; + } + /* + * Get the size of the volume in clusters and check for 64-bit-ness. + * Windows currently only uses 32 bits to save the clusters so we do + * the same as it is much faster on 32-bit CPUs. + */ + ll = le64_to_cpu(b->number_of_sectors) >> sectors_per_cluster_bits; + if ((u64)ll >= 1ULL << 32) { + ntfs_error(vol->sb, "Cannot handle 64-bit clusters."); + return false; + } + vol->nr_clusters = ll; + ntfs_debug("vol->nr_clusters = 0x%llx", vol->nr_clusters); + ll = le64_to_cpu(b->mft_lcn); + if (ll >= vol->nr_clusters) { + ntfs_error(vol->sb, "MFT LCN (%lli, 0x%llx) is beyond end of volume. Weird.", + ll, ll); + return false; + } + vol->mft_lcn = ll; + ntfs_debug("vol->mft_lcn = 0x%llx", vol->mft_lcn); + ll = le64_to_cpu(b->mftmirr_lcn); + if (ll >= vol->nr_clusters) { + ntfs_error(vol->sb, "MFTMirr LCN (%lli, 0x%llx) is beyond end of volume. Weird.", + ll, ll); + return false; + } + vol->mftmirr_lcn = ll; + ntfs_debug("vol->mftmirr_lcn = 0x%llx", vol->mftmirr_lcn); + /* + * Work out the size of the mft mirror in number of mft records. If the + * cluster size is less than or equal to the size taken by four mft + * records, the mft mirror stores the first four mft records. If the + * cluster size is bigger than the size taken by four mft records, the + * mft mirror contains as many mft records as will fit into one + * cluster. + */ + if (vol->cluster_size <= (4 << vol->mft_record_size_bits)) + vol->mftmirr_size = 4; + else + vol->mftmirr_size = vol->cluster_size >> + vol->mft_record_size_bits; + ntfs_debug("vol->mftmirr_size = %i", vol->mftmirr_size); + vol->serial_no = le64_to_cpu(b->volume_serial_number); + ntfs_debug("vol->serial_no = 0x%llx", vol->serial_no); + + vol->sparse_compression_unit = 4; + if (vol->cluster_size > 4096) { + switch (vol->cluster_size) { + case 65536: + vol->sparse_compression_unit = 0; + break; + case 32768: + vol->sparse_compression_unit = 1; + break; + case 16384: + vol->sparse_compression_unit = 2; + break; + case 8192: + vol->sparse_compression_unit = 3; + break; + } + } + + return true; +} + +/* + * ntfs_setup_allocators - initialize the cluster and mft allocators + * @vol: volume structure for which to setup the allocators + * + * Setup the cluster (lcn) and mft allocators to the starting values. + */ +static void ntfs_setup_allocators(struct ntfs_volume *vol) +{ + s64 mft_zone_size, mft_lcn; + + ntfs_debug("vol->mft_zone_multiplier = 0x%x", + vol->mft_zone_multiplier); + /* Determine the size of the MFT zone. */ + mft_zone_size = vol->nr_clusters; + switch (vol->mft_zone_multiplier) { /* % of volume size in clusters */ + case 4: + mft_zone_size >>= 1; /* 50% */ + break; + case 3: + mft_zone_size = (mft_zone_size + + (mft_zone_size >> 1)) >> 2; /* 37.5% */ + break; + case 2: + mft_zone_size >>= 2; /* 25% */ + break; + /* case 1: */ + default: + mft_zone_size >>= 3; /* 12.5% */ + break; + } + /* Setup the mft zone. */ + vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn; + ntfs_debug("vol->mft_zone_pos = 0x%llx", vol->mft_zone_pos); + /* + * Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs + * source) and if the actual mft_lcn is in the expected place or even + * further to the front of the volume, extend the mft_zone to cover the + * beginning of the volume as well. This is in order to protect the + * area reserved for the mft bitmap as well within the mft_zone itself. + * On non-standard volumes we do not protect it as the overhead would + * be higher than the speed increase we would get by doing it. + */ + mft_lcn = NTFS_B_TO_CLU(vol, 8192 + 2 * vol->cluster_size - 1); + if (mft_lcn * vol->cluster_size < 16 * 1024) + mft_lcn = (16 * 1024 + vol->cluster_size - 1) >> + vol->cluster_size_bits; + if (vol->mft_zone_start <= mft_lcn) + vol->mft_zone_start = 0; + ntfs_debug("vol->mft_zone_start = 0x%llx", vol->mft_zone_start); + /* + * Need to cap the mft zone on non-standard volumes so that it does + * not point outside the boundaries of the volume. We do this by + * halving the zone size until we are inside the volume. + */ + vol->mft_zone_end = vol->mft_lcn + mft_zone_size; + while (vol->mft_zone_end >= vol->nr_clusters) { + mft_zone_size >>= 1; + vol->mft_zone_end = vol->mft_lcn + mft_zone_size; + } + ntfs_debug("vol->mft_zone_end = 0x%llx", vol->mft_zone_end); + /* + * Set the current position within each data zone to the start of the + * respective zone. + */ + vol->data1_zone_pos = vol->mft_zone_end; + ntfs_debug("vol->data1_zone_pos = 0x%llx", vol->data1_zone_pos); + vol->data2_zone_pos = 0; + ntfs_debug("vol->data2_zone_pos = 0x%llx", vol->data2_zone_pos); + + /* Set the mft data allocation position to mft record 24. */ + vol->mft_data_pos = 24; + ntfs_debug("vol->mft_data_pos = 0x%llx", vol->mft_data_pos); +} + +static struct lock_class_key mftmirr_runlist_lock_key, + mftmirr_mrec_lock_key; +/* + * load_and_init_mft_mirror - load and setup the mft mirror inode for a volume + * @vol: ntfs super block describing device whose mft mirror to load + * + * Return 'true' on success or 'false' on error. + */ +static bool load_and_init_mft_mirror(struct ntfs_volume *vol) +{ + struct inode *tmp_ino; + struct ntfs_inode *tmp_ni; + + ntfs_debug("Entering."); + /* Get mft mirror inode. */ + tmp_ino = ntfs_iget(vol->sb, FILE_MFTMirr); + if (IS_ERR(tmp_ino)) { + if (!IS_ERR(tmp_ino)) + iput(tmp_ino); + /* Caller will display error message. */ + return false; + } + lockdep_set_class(&NTFS_I(tmp_ino)->runlist.lock, + &mftmirr_runlist_lock_key); + lockdep_set_class(&NTFS_I(tmp_ino)->mrec_lock, + &mftmirr_mrec_lock_key); + /* + * Re-initialize some specifics about $MFTMirr's inode as + * ntfs_read_inode() will have set up the default ones. + */ + /* Set uid and gid to root. */ + tmp_ino->i_uid = GLOBAL_ROOT_UID; + tmp_ino->i_gid = GLOBAL_ROOT_GID; + /* Regular file. No access for anyone. */ + tmp_ino->i_mode = S_IFREG; + /* No VFS initiated operations allowed for $MFTMirr. */ + tmp_ino->i_op = &ntfs_empty_inode_ops; + tmp_ino->i_fop = &ntfs_empty_file_ops; + /* Put in our special address space operations. */ + tmp_ino->i_mapping->a_ops = &ntfs_aops; + tmp_ni = NTFS_I(tmp_ino); + /* The $MFTMirr, like the $MFT is multi sector transfer protected. */ + NInoSetMstProtected(tmp_ni); + NInoSetSparseDisabled(tmp_ni); + /* + * Set up our little cheat allowing us to reuse the async read io + * completion handler for directories. + */ + tmp_ni->itype.index.block_size = vol->mft_record_size; + tmp_ni->itype.index.block_size_bits = vol->mft_record_size_bits; + vol->mftmirr_ino = tmp_ino; + ntfs_debug("Done."); + return true; +} + +/* + * check_mft_mirror - compare contents of the mft mirror with the mft + * @vol: ntfs super block describing device whose mft mirror to check + * + * Return 'true' on success or 'false' on error. + * + * Note, this function also results in the mft mirror runlist being completely + * mapped into memory. The mft mirror write code requires this and will BUG() + * should it find an unmapped runlist element. + */ +static bool check_mft_mirror(struct ntfs_volume *vol) +{ + struct super_block *sb = vol->sb; + struct ntfs_inode *mirr_ni; + struct folio *mft_folio = NULL, *mirr_folio = NULL; + u8 *kmft = NULL, *kmirr = NULL; + struct runlist_element *rl, rl2[2]; + pgoff_t index; + int mrecs_per_page, i; + + ntfs_debug("Entering."); + /* Compare contents of $MFT and $MFTMirr. */ + mrecs_per_page = PAGE_SIZE / vol->mft_record_size; + index = i = 0; + do { + u32 bytes; + + /* Switch pages if necessary. */ + if (!(i % mrecs_per_page)) { + if (index) { + kunmap_local(kmirr); + folio_put(mirr_folio); + kunmap_local(kmft); + folio_put(mft_folio); + } + /* Get the $MFT page. */ + mft_folio = read_mapping_folio(vol->mft_ino->i_mapping, + index, NULL); + if (IS_ERR(mft_folio)) { + ntfs_error(sb, "Failed to read $MFT."); + return false; + } + kmft = kmap_local_folio(mft_folio, 0); + /* Get the $MFTMirr page. */ + mirr_folio = read_mapping_folio(vol->mftmirr_ino->i_mapping, + index, NULL); + if (IS_ERR(mirr_folio)) { + ntfs_error(sb, "Failed to read $MFTMirr."); + goto mft_unmap_out; + } + kmirr = kmap_local_folio(mirr_folio, 0); + ++index; + } + + /* Do not check the record if it is not in use. */ + if (((struct mft_record *)kmft)->flags & MFT_RECORD_IN_USE) { + /* Make sure the record is ok. */ + if (ntfs_is_baad_recordp((__le32 *)kmft)) { + ntfs_error(sb, + "Incomplete multi sector transfer detected in mft record %i.", + i); +mm_unmap_out: + kunmap_local(kmirr); + folio_put(mirr_folio); +mft_unmap_out: + kunmap_local(kmft); + folio_put(mft_folio); + return false; + } + } + /* Do not check the mirror record if it is not in use. */ + if (((struct mft_record *)kmirr)->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_recordp((__le32 *)kmirr)) { + ntfs_error(sb, + "Incomplete multi sector transfer detected in mft mirror record %i.", + i); + goto mm_unmap_out; + } + } + /* Get the amount of data in the current record. */ + bytes = le32_to_cpu(((struct mft_record *)kmft)->bytes_in_use); + if (bytes < sizeof(struct mft_record_old) || + bytes > vol->mft_record_size || + ntfs_is_baad_recordp((__le32 *)kmft)) { + bytes = le32_to_cpu(((struct mft_record *)kmirr)->bytes_in_use); + if (bytes < sizeof(struct mft_record_old) || + bytes > vol->mft_record_size || + ntfs_is_baad_recordp((__le32 *)kmirr)) + bytes = vol->mft_record_size; + } + kmft += vol->mft_record_size; + kmirr += vol->mft_record_size; + } while (++i < vol->mftmirr_size); + /* Release the last folios. */ + kunmap_local(kmirr); + folio_put(mirr_folio); + kunmap_local(kmft); + folio_put(mft_folio); + + /* Construct the mft mirror runlist by hand. */ + rl2[0].vcn = 0; + rl2[0].lcn = vol->mftmirr_lcn; + rl2[0].length = NTFS_B_TO_CLU(vol, vol->mftmirr_size * vol->mft_record_size + + vol->cluster_size - 1); + rl2[1].vcn = rl2[0].length; + rl2[1].lcn = LCN_ENOENT; + rl2[1].length = 0; + /* + * Because we have just read all of the mft mirror, we know we have + * mapped the full runlist for it. + */ + mirr_ni = NTFS_I(vol->mftmirr_ino); + down_read(&mirr_ni->runlist.lock); + rl = mirr_ni->runlist.rl; + /* Compare the two runlists. They must be identical. */ + i = 0; + do { + if (rl2[i].vcn != rl[i].vcn || rl2[i].lcn != rl[i].lcn || + rl2[i].length != rl[i].length) { + ntfs_error(sb, "$MFTMirr location mismatch. Run chkdsk."); + up_read(&mirr_ni->runlist.lock); + return false; + } + } while (rl2[i++].length); + up_read(&mirr_ni->runlist.lock); + ntfs_debug("Done."); + return true; +} + +/* + * load_and_check_logfile - load and check the logfile inode for a volume + * @vol: ntfs volume to load the logfile for + * @rp: on success, set to the restart page header + * + * Return 0 on success or errno on error. + */ +static int load_and_check_logfile(struct ntfs_volume *vol, + struct restart_page_header **rp) +{ + struct inode *tmp_ino; + int err = 0; + + ntfs_debug("Entering."); + tmp_ino = ntfs_iget(vol->sb, FILE_LogFile); + if (IS_ERR(tmp_ino)) { + if (!IS_ERR(tmp_ino)) + iput(tmp_ino); + /* Caller will display error message. */ + return -ENOENT; + } + if (!ntfs_check_logfile(tmp_ino, rp)) + err = -EINVAL; + NInoSetSparseDisabled(NTFS_I(tmp_ino)); + vol->logfile_ino = tmp_ino; + ntfs_debug("Done."); + return err; +} + +#define NTFS_HIBERFIL_HEADER_SIZE 4096 + +/* + * check_windows_hibernation_status - check if Windows is suspended on a volume + * @vol: ntfs super block of device to check + * + * Check if Windows is hibernated on the ntfs volume @vol. This is done by + * looking for the file hiberfil.sys in the root directory of the volume. If + * the file is not present Windows is definitely not suspended. + * + * If hiberfil.sys exists and is less than 4kiB in size it means Windows is + * definitely suspended (this volume is not the system volume). Caveat: on a + * system with many volumes it is possible that the < 4kiB check is bogus but + * for now this should do fine. + * + * If hiberfil.sys exists and is larger than 4kiB in size, we need to read the + * hiberfil header (which is the first 4kiB). If this begins with "hibr", + * Windows is definitely suspended. If it is completely full of zeroes, + * Windows is definitely not hibernated. Any other case is treated as if + * Windows is suspended. This caters for the above mentioned caveat of a + * system with many volumes where no "hibr" magic would be present and there is + * no zero header. + * + * Return 0 if Windows is not hibernated on the volume, >0 if Windows is + * hibernated on the volume, and -errno on error. + */ +static int check_windows_hibernation_status(struct ntfs_volume *vol) +{ + static const __le16 hiberfil[13] = { cpu_to_le16('h'), + cpu_to_le16('i'), cpu_to_le16('b'), + cpu_to_le16('e'), cpu_to_le16('r'), + cpu_to_le16('f'), cpu_to_le16('i'), + cpu_to_le16('l'), cpu_to_le16('.'), + cpu_to_le16('s'), cpu_to_le16('y'), + cpu_to_le16('s'), 0 }; + u64 mref; + struct inode *vi; + struct folio *folio; + u32 *kaddr, *kend, *start_addr = NULL; + struct ntfs_name *name = NULL; + int ret = 1; + + ntfs_debug("Entering."); + /* + * Find the inode number for the hibernation file by looking up the + * filename hiberfil.sys in the root directory. + */ + inode_lock(vol->root_ino); + mref = ntfs_lookup_inode_by_name(NTFS_I(vol->root_ino), hiberfil, 12, + &name); + inode_unlock(vol->root_ino); + kfree(name); + if (IS_ERR_MREF(mref)) { + ret = MREF_ERR(mref); + /* If the file does not exist, Windows is not hibernated. */ + if (ret == -ENOENT) { + ntfs_debug("hiberfil.sys not present. Windows is not hibernated on the volume."); + return 0; + } + /* A real error occurred. */ + ntfs_error(vol->sb, "Failed to find inode number for hiberfil.sys."); + return ret; + } + /* Get the inode. */ + vi = ntfs_iget(vol->sb, MREF(mref)); + if (IS_ERR(vi)) { + if (!IS_ERR(vi)) + iput(vi); + ntfs_error(vol->sb, "Failed to load hiberfil.sys."); + return IS_ERR(vi) ? PTR_ERR(vi) : -EIO; + } + if (unlikely(i_size_read(vi) < NTFS_HIBERFIL_HEADER_SIZE)) { + ntfs_debug("hiberfil.sys is smaller than 4kiB (0x%llx). Windows is hibernated on the volume. This is not the system volume.", + i_size_read(vi)); + goto iput_out; + } + + folio = read_mapping_folio(vi->i_mapping, 0, NULL); + if (IS_ERR(folio)) { + ntfs_error(vol->sb, "Failed to read from hiberfil.sys."); + ret = PTR_ERR(folio); + goto iput_out; + } + start_addr = (u32 *)kmap_local_folio(folio, 0); + kaddr = start_addr; + if (*(__le32 *)kaddr == cpu_to_le32(0x72626968)/*'hibr'*/) { + ntfs_debug("Magic \"hibr\" found in hiberfil.sys. Windows is hibernated on the volume. This is the system volume."); + goto unm_iput_out; + } + kend = kaddr + NTFS_HIBERFIL_HEADER_SIZE/sizeof(*kaddr); + do { + if (unlikely(*kaddr)) { + ntfs_debug("hiberfil.sys is larger than 4kiB (0x%llx), does not contain the \"hibr\" magic, and does not have a zero header. Windows is hibernated on the volume. This is not the system volume.", + i_size_read(vi)); + goto unm_iput_out; + } + } while (++kaddr < kend); + ntfs_debug("hiberfil.sys contains a zero header. Windows is not hibernated on the volume. This is the system volume."); + ret = 0; +unm_iput_out: + kunmap_local(start_addr); + folio_put(folio); +iput_out: + iput(vi); + return ret; +} + +/* + * load_and_init_quota - load and setup the quota file for a volume if present + * @vol: ntfs super block describing device whose quota file to load + * + * Return 'true' on success or 'false' on error. If $Quota is not present, we + * leave vol->quota_ino as NULL and return success. + */ +static bool load_and_init_quota(struct ntfs_volume *vol) +{ + static const __le16 Quota[7] = { cpu_to_le16('$'), + cpu_to_le16('Q'), cpu_to_le16('u'), + cpu_to_le16('o'), cpu_to_le16('t'), + cpu_to_le16('a'), 0 }; + static __le16 Q[3] = { cpu_to_le16('$'), + cpu_to_le16('Q'), 0 }; + struct ntfs_name *name = NULL; + u64 mref; + struct inode *tmp_ino; + + ntfs_debug("Entering."); + /* + * Find the inode number for the quota file by looking up the filename + * $Quota in the extended system files directory $Extend. + */ + inode_lock(vol->extend_ino); + mref = ntfs_lookup_inode_by_name(NTFS_I(vol->extend_ino), Quota, 6, + &name); + inode_unlock(vol->extend_ino); + kfree(name); + if (IS_ERR_MREF(mref)) { + /* + * If the file does not exist, quotas are disabled and have + * never been enabled on this volume, just return success. + */ + if (MREF_ERR(mref) == -ENOENT) { + ntfs_debug("$Quota not present. Volume does not have quotas enabled."); + /* + * No need to try to set quotas out of date if they are + * not enabled. + */ + NVolSetQuotaOutOfDate(vol); + return true; + } + /* A real error occurred. */ + ntfs_error(vol->sb, "Failed to find inode number for $Quota."); + return false; + } + /* Get the inode. */ + tmp_ino = ntfs_iget(vol->sb, MREF(mref)); + if (IS_ERR(tmp_ino)) { + if (!IS_ERR(tmp_ino)) + iput(tmp_ino); + ntfs_error(vol->sb, "Failed to load $Quota."); + return false; + } + vol->quota_ino = tmp_ino; + /* Get the $Q index allocation attribute. */ + tmp_ino = ntfs_index_iget(vol->quota_ino, Q, 2); + if (IS_ERR(tmp_ino)) { + ntfs_error(vol->sb, "Failed to load $Quota/$Q index."); + return false; + } + vol->quota_q_ino = tmp_ino; + ntfs_debug("Done."); + return true; +} + +/* + * load_and_init_attrdef - load the attribute definitions table for a volume + * @vol: ntfs super block describing device whose attrdef to load + * + * Return 'true' on success or 'false' on error. + */ +static bool load_and_init_attrdef(struct ntfs_volume *vol) +{ + loff_t i_size; + struct super_block *sb = vol->sb; + struct inode *ino; + struct folio *folio; + u8 *addr; + pgoff_t index, max_index; + unsigned int size; + + ntfs_debug("Entering."); + /* Read attrdef table and setup vol->attrdef and vol->attrdef_size. */ + ino = ntfs_iget(sb, FILE_AttrDef); + if (IS_ERR(ino)) { + if (!IS_ERR(ino)) + iput(ino); + goto failed; + } + NInoSetSparseDisabled(NTFS_I(ino)); + /* The size of FILE_AttrDef must be above 0 and fit inside 31 bits. */ + i_size = i_size_read(ino); + if (i_size <= 0 || i_size > 0x7fffffff) + goto iput_failed; + vol->attrdef = kvzalloc(i_size, GFP_NOFS); + if (!vol->attrdef) + goto iput_failed; + index = 0; + max_index = i_size >> PAGE_SHIFT; + size = PAGE_SIZE; + while (index < max_index) { + /* Read the attrdef table and copy it into the linear buffer. */ +read_partial_attrdef_page: + folio = read_mapping_folio(ino->i_mapping, index, NULL); + if (IS_ERR(folio)) + goto free_iput_failed; + addr = kmap_local_folio(folio, 0); + memcpy((u8 *)vol->attrdef + (index++ << PAGE_SHIFT), + addr, size); + kunmap_local(addr); + folio_put(folio); + } + if (size == PAGE_SIZE) { + size = i_size & ~PAGE_MASK; + if (size) + goto read_partial_attrdef_page; + } + vol->attrdef_size = i_size; + ntfs_debug("Read %llu bytes from $AttrDef.", i_size); + iput(ino); + return true; +free_iput_failed: + kvfree(vol->attrdef); + vol->attrdef = NULL; +iput_failed: + iput(ino); +failed: + ntfs_error(sb, "Failed to initialize attribute definition table."); + return false; +} + +/* + * load_and_init_upcase - load the upcase table for an ntfs volume + * @vol: ntfs super block describing device whose upcase to load + * + * Return 'true' on success or 'false' on error. + */ +static bool load_and_init_upcase(struct ntfs_volume *vol) +{ + loff_t i_size; + struct super_block *sb = vol->sb; + struct inode *ino; + struct folio *folio; + u8 *addr; + pgoff_t index, max_index; + unsigned int size; + int i, max; + + ntfs_debug("Entering."); + /* Read upcase table and setup vol->upcase and vol->upcase_len. */ + ino = ntfs_iget(sb, FILE_UpCase); + if (IS_ERR(ino)) { + if (!IS_ERR(ino)) + iput(ino); + goto upcase_failed; + } + /* + * The upcase size must not be above 64k Unicode characters, must not + * be zero and must be a multiple of sizeof(__le16). + */ + i_size = i_size_read(ino); + if (!i_size || i_size & (sizeof(__le16) - 1) || + i_size > 64ULL * 1024 * sizeof(__le16)) + goto iput_upcase_failed; + vol->upcase = kvzalloc(i_size, GFP_NOFS); + if (!vol->upcase) + goto iput_upcase_failed; + index = 0; + max_index = i_size >> PAGE_SHIFT; + size = PAGE_SIZE; + while (index < max_index) { + /* Read the upcase table and copy it into the linear buffer. */ +read_partial_upcase_page: + folio = read_mapping_folio(ino->i_mapping, index, NULL); + if (IS_ERR(folio)) + goto iput_upcase_failed; + addr = kmap_local_folio(folio, 0); + memcpy((char *)vol->upcase + (index++ << PAGE_SHIFT), + addr, size); + kunmap_local(addr); + folio_put(folio); + } + if (size == PAGE_SIZE) { + size = i_size & ~PAGE_MASK; + if (size) + goto read_partial_upcase_page; + } + vol->upcase_len = i_size >> sizeof(unsigned char); + ntfs_debug("Read %llu bytes from $UpCase (expected %zu bytes).", + i_size, 64 * 1024 * sizeof(__le16)); + iput(ino); + mutex_lock(&ntfs_lock); + if (!default_upcase) { + ntfs_debug("Using volume specified $UpCase since default is not present."); + mutex_unlock(&ntfs_lock); + return true; + } + max = default_upcase_len; + if (max > vol->upcase_len) + max = vol->upcase_len; + for (i = 0; i < max; i++) + if (vol->upcase[i] != default_upcase[i]) + break; + if (i == max) { + kvfree(vol->upcase); + vol->upcase = default_upcase; + vol->upcase_len = max; + ntfs_nr_upcase_users++; + mutex_unlock(&ntfs_lock); + ntfs_debug("Volume specified $UpCase matches default. Using default."); + return true; + } + mutex_unlock(&ntfs_lock); + ntfs_debug("Using volume specified $UpCase since it does not match the default."); + return true; +iput_upcase_failed: + iput(ino); + kvfree(vol->upcase); + vol->upcase = NULL; +upcase_failed: + mutex_lock(&ntfs_lock); + if (default_upcase) { + vol->upcase = default_upcase; + vol->upcase_len = default_upcase_len; + ntfs_nr_upcase_users++; + mutex_unlock(&ntfs_lock); + ntfs_error(sb, "Failed to load $UpCase from the volume. Using default."); + return true; + } + mutex_unlock(&ntfs_lock); + ntfs_error(sb, "Failed to initialize upcase table."); + return false; +} + +/* + * The lcn and mft bitmap inodes are NTFS-internal inodes with + * their own special locking rules: + */ +static struct lock_class_key + lcnbmp_runlist_lock_key, lcnbmp_mrec_lock_key, + mftbmp_runlist_lock_key, mftbmp_mrec_lock_key; + +/* + * load_system_files - open the system files using normal functions + * @vol: ntfs super block describing device whose system files to load + * + * Open the system files with normal access functions and complete setting up + * the ntfs super block @vol. + * + * Return 'true' on success or 'false' on error. + */ +static bool load_system_files(struct ntfs_volume *vol) +{ + struct super_block *sb = vol->sb; + struct mft_record *m; + struct volume_information *vi; + struct ntfs_attr_search_ctx *ctx; + struct restart_page_header *rp; + int err; + + ntfs_debug("Entering."); + /* Get mft mirror inode compare the contents of $MFT and $MFTMirr. */ + if (!load_and_init_mft_mirror(vol) || !check_mft_mirror(vol)) { + /* If a read-write mount, convert it to a read-only mount. */ + if (!sb_rdonly(sb) && vol->on_errors == ON_ERRORS_REMOUNT_RO) { + static const char *es1 = "Failed to load $MFTMirr"; + static const char *es2 = "$MFTMirr does not match $MFT"; + static const char *es3 = ". Run ntfsck and/or chkdsk."; + + sb->s_flags |= SB_RDONLY; + ntfs_error(sb, "%s. Mounting read-only%s", + !vol->mftmirr_ino ? es1 : es2, es3); + } + NVolSetErrors(vol); + } + /* Get mft bitmap attribute inode. */ + vol->mftbmp_ino = ntfs_attr_iget(vol->mft_ino, AT_BITMAP, NULL, 0); + if (IS_ERR(vol->mftbmp_ino)) { + ntfs_error(sb, "Failed to load $MFT/$BITMAP attribute."); + goto iput_mirr_err_out; + } + lockdep_set_class(&NTFS_I(vol->mftbmp_ino)->runlist.lock, + &mftbmp_runlist_lock_key); + lockdep_set_class(&NTFS_I(vol->mftbmp_ino)->mrec_lock, + &mftbmp_mrec_lock_key); + /* Read upcase table and setup @vol->upcase and @vol->upcase_len. */ + if (!load_and_init_upcase(vol)) + goto iput_mftbmp_err_out; + /* + * Read attribute definitions table and setup @vol->attrdef and + * @vol->attrdef_size. + */ + if (!load_and_init_attrdef(vol)) + goto iput_upcase_err_out; + /* + * Get the cluster allocation bitmap inode and verify the size, no + * need for any locking at this stage as we are already running + * exclusively as we are mount in progress task. + */ + vol->lcnbmp_ino = ntfs_iget(sb, FILE_Bitmap); + if (IS_ERR(vol->lcnbmp_ino)) { + if (!IS_ERR(vol->lcnbmp_ino)) + iput(vol->lcnbmp_ino); + goto bitmap_failed; + } + lockdep_set_class(&NTFS_I(vol->lcnbmp_ino)->runlist.lock, + &lcnbmp_runlist_lock_key); + lockdep_set_class(&NTFS_I(vol->lcnbmp_ino)->mrec_lock, + &lcnbmp_mrec_lock_key); + + NInoSetSparseDisabled(NTFS_I(vol->lcnbmp_ino)); + if ((vol->nr_clusters + 7) >> 3 > i_size_read(vol->lcnbmp_ino)) { + iput(vol->lcnbmp_ino); +bitmap_failed: + ntfs_error(sb, "Failed to load $Bitmap."); + goto iput_attrdef_err_out; + } + /* + * Get the volume inode and setup our cache of the volume flags and + * version. + */ + vol->vol_ino = ntfs_iget(sb, FILE_Volume); + if (IS_ERR(vol->vol_ino)) { + if (!IS_ERR(vol->vol_ino)) + iput(vol->vol_ino); +volume_failed: + ntfs_error(sb, "Failed to load $Volume."); + goto iput_lcnbmp_err_out; + } + m = map_mft_record(NTFS_I(vol->vol_ino)); + if (IS_ERR(m)) { +iput_volume_failed: + iput(vol->vol_ino); + goto volume_failed; + } + + ctx = ntfs_attr_get_search_ctx(NTFS_I(vol->vol_ino), m); + if (!ctx) { + ntfs_error(sb, "Failed to get attribute search context."); + goto get_ctx_vol_failed; + } + + if (!ntfs_attr_lookup(AT_VOLUME_NAME, NULL, 0, 0, 0, NULL, 0, ctx) && + !ctx->attr->non_resident && + !(ctx->attr->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) && + le32_to_cpu(ctx->attr->data.resident.value_length) > 0) { + err = ntfs_ucstonls(vol, (__le16 *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)), + le32_to_cpu(ctx->attr->data.resident.value_length) / 2, + &vol->volume_label, NTFS_MAX_LABEL_LEN); + if (err < 0) + vol->volume_label = NULL; + } + + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, NULL, 0, 0, 0, NULL, 0, + ctx) || ctx->attr->non_resident || ctx->attr->flags) { + ntfs_attr_put_search_ctx(ctx); +get_ctx_vol_failed: + unmap_mft_record(NTFS_I(vol->vol_ino)); + goto iput_volume_failed; + } + vi = (struct volume_information *)((char *)ctx->attr + + le16_to_cpu(ctx->attr->data.resident.value_offset)); + /* Copy the volume flags and version to the struct ntfs_volume structure. */ + vol->vol_flags = vi->flags; + vol->major_ver = vi->major_ver; + vol->minor_ver = vi->minor_ver; + ntfs_attr_put_search_ctx(ctx); + unmap_mft_record(NTFS_I(vol->vol_ino)); + pr_info("volume version %i.%i, dev %s, cluster size %d\n", + vol->major_ver, vol->minor_ver, sb->s_id, vol->cluster_size); + + /* Make sure that no unsupported volume flags are set. */ + if (vol->vol_flags & VOLUME_MUST_MOUNT_RO_MASK) { + static const char *es1a = "Volume is dirty"; + static const char *es1b = "Volume has been modified by chkdsk"; + static const char *es1c = "Volume has unsupported flags set"; + static const char *es2a = ". Run chkdsk and mount in Windows."; + static const char *es2b = ". Mount in Windows."; + const char *es1, *es2; + + es2 = es2a; + if (vol->vol_flags & VOLUME_IS_DIRTY) + es1 = es1a; + else if (vol->vol_flags & VOLUME_MODIFIED_BY_CHKDSK) { + es1 = es1b; + es2 = es2b; + } else { + es1 = es1c; + ntfs_warning(sb, "Unsupported volume flags 0x%x encountered.", + (unsigned int)le16_to_cpu(vol->vol_flags)); + } + /* If a read-write mount, convert it to a read-only mount. */ + if (!sb_rdonly(sb) && vol->on_errors == ON_ERRORS_REMOUNT_RO) { + sb->s_flags |= SB_RDONLY; + ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); + } + /* + * Do not set NVolErrors() because ntfs_remount() re-checks the + * flags which we need to do in case any flags have changed. + */ + } + /* + * Get the inode for the logfile, check it and determine if the volume + * was shutdown cleanly. + */ + rp = NULL; + err = load_and_check_logfile(vol, &rp); + if (err) { + /* If a read-write mount, convert it to a read-only mount. */ + if (!sb_rdonly(sb) && vol->on_errors == ON_ERRORS_REMOUNT_RO) { + sb->s_flags |= SB_RDONLY; + ntfs_error(sb, "Failed to load LogFile. Mounting read-only."); + } + NVolSetErrors(vol); + } + + kvfree(rp); + /* Get the root directory inode so we can do path lookups. */ + vol->root_ino = ntfs_iget(sb, FILE_root); + if (IS_ERR(vol->root_ino)) { + if (!IS_ERR(vol->root_ino)) + iput(vol->root_ino); + ntfs_error(sb, "Failed to load root directory."); + goto iput_logfile_err_out; + } + /* + * Check if Windows is suspended to disk on the target volume. If it + * is hibernated, we must not write *anything* to the disk so set + * NVolErrors() without setting the dirty volume flag and mount + * read-only. This will prevent read-write remounting and it will also + * prevent all writes. + */ + err = check_windows_hibernation_status(vol); + if (unlikely(err)) { + static const char *es1a = "Failed to determine if Windows is hibernated"; + static const char *es1b = "Windows is hibernated"; + static const char *es2 = ". Run chkdsk."; + const char *es1; + + es1 = err < 0 ? es1a : es1b; + /* If a read-write mount, convert it to a read-only mount. */ + if (!sb_rdonly(sb) && vol->on_errors == ON_ERRORS_REMOUNT_RO) { + sb->s_flags |= SB_RDONLY; + ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); + } + NVolSetErrors(vol); + } + + /* If (still) a read-write mount, empty the logfile. */ + if (!sb_rdonly(sb) && + vol->logfile_ino && !ntfs_empty_logfile(vol->logfile_ino) && + vol->on_errors == ON_ERRORS_REMOUNT_RO) { + static const char *es1 = "Failed to empty LogFile"; + static const char *es2 = ". Mount in Windows."; + + /* Convert to a read-only mount. */ + ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); + sb->s_flags |= SB_RDONLY; + NVolSetErrors(vol); + } + /* If on NTFS versions before 3.0, we are done. */ + if (unlikely(vol->major_ver < 3)) + return true; + /* NTFS 3.0+ specific initialization. */ + /* Get the security descriptors inode. */ + vol->secure_ino = ntfs_iget(sb, FILE_Secure); + if (IS_ERR(vol->secure_ino)) { + if (!IS_ERR(vol->secure_ino)) + iput(vol->secure_ino); + ntfs_error(sb, "Failed to load $Secure."); + goto iput_root_err_out; + } + /* Get the extended system files' directory inode. */ + vol->extend_ino = ntfs_iget(sb, FILE_Extend); + if (IS_ERR(vol->extend_ino) || + !S_ISDIR(vol->extend_ino->i_mode)) { + if (!IS_ERR(vol->extend_ino)) + iput(vol->extend_ino); + ntfs_error(sb, "Failed to load $Extend."); + goto iput_sec_err_out; + } + /* Find the quota file, load it if present, and set it up. */ + if (!load_and_init_quota(vol) && + vol->on_errors == ON_ERRORS_REMOUNT_RO) { + static const char *es1 = "Failed to load $Quota"; + static const char *es2 = ". Run chkdsk."; + + sb->s_flags |= SB_RDONLY; + ntfs_error(sb, "%s. Mounting read-only%s", es1, es2); + /* This will prevent a read-write remount. */ + NVolSetErrors(vol); + } + + return true; + +iput_sec_err_out: + iput(vol->secure_ino); +iput_root_err_out: + iput(vol->root_ino); +iput_logfile_err_out: + if (vol->logfile_ino) + iput(vol->logfile_ino); + iput(vol->vol_ino); +iput_lcnbmp_err_out: + iput(vol->lcnbmp_ino); +iput_attrdef_err_out: + vol->attrdef_size = 0; + if (vol->attrdef) { + kvfree(vol->attrdef); + vol->attrdef = NULL; + } +iput_upcase_err_out: + vol->upcase_len = 0; + mutex_lock(&ntfs_lock); + if (vol->upcase == default_upcase) { + ntfs_nr_upcase_users--; + vol->upcase = NULL; + } + mutex_unlock(&ntfs_lock); + if (vol->upcase) { + kvfree(vol->upcase); + vol->upcase = NULL; + } +iput_mftbmp_err_out: + iput(vol->mftbmp_ino); +iput_mirr_err_out: + iput(vol->mftmirr_ino); + return false; +} + +static void ntfs_volume_free(struct ntfs_volume *vol) +{ + /* Throw away the table of attribute definitions. */ + vol->attrdef_size = 0; + if (vol->attrdef) { + kvfree(vol->attrdef); + vol->attrdef = NULL; + } + vol->upcase_len = 0; + /* + * Destroy the global default upcase table if necessary. Also decrease + * the number of upcase users if we are a user. + */ + mutex_lock(&ntfs_lock); + if (vol->upcase == default_upcase) { + ntfs_nr_upcase_users--; + vol->upcase = NULL; + } + + if (!ntfs_nr_upcase_users && default_upcase) { + kvfree(default_upcase); + default_upcase = NULL; + } + + free_compression_buffers(); + + mutex_unlock(&ntfs_lock); + if (vol->upcase) { + kvfree(vol->upcase); + vol->upcase = NULL; + } + + unload_nls(vol->nls_map); + + if (vol->lcn_empty_bits_per_page) + kvfree(vol->lcn_empty_bits_per_page); + kfree(vol->volume_label); + kfree(vol); +} + +/* + * ntfs_put_super - called by the vfs to unmount a volume + * @sb: vfs superblock of volume to unmount + */ +static void ntfs_put_super(struct super_block *sb) +{ + struct ntfs_volume *vol = NTFS_SB(sb); + + pr_info("Entering %s, dev %s\n", __func__, sb->s_id); + + cancel_work_sync(&vol->precalc_work); + + /* + * Commit all inodes while they are still open in case some of them + * cause others to be dirtied. + */ + ntfs_commit_inode(vol->vol_ino); + + /* NTFS 3.0+ specific. */ + if (vol->major_ver >= 3) { + if (vol->quota_q_ino) + ntfs_commit_inode(vol->quota_q_ino); + if (vol->quota_ino) + ntfs_commit_inode(vol->quota_ino); + if (vol->extend_ino) + ntfs_commit_inode(vol->extend_ino); + if (vol->secure_ino) + ntfs_commit_inode(vol->secure_ino); + } + + ntfs_commit_inode(vol->root_ino); + + ntfs_commit_inode(vol->lcnbmp_ino); + + /* + * the GFP_NOFS scope is not needed because ntfs_commit_inode + * does nothing + */ + ntfs_commit_inode(vol->mftbmp_ino); + + if (vol->logfile_ino) + ntfs_commit_inode(vol->logfile_ino); + + if (vol->mftmirr_ino) + ntfs_commit_inode(vol->mftmirr_ino); + ntfs_commit_inode(vol->mft_ino); + + /* + * If a read-write mount and no volume errors have occurred, mark the + * volume clean. Also, re-commit all affected inodes. + */ + if (!sb_rdonly(sb)) { + if (!NVolErrors(vol)) { + if (ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) + ntfs_warning(sb, + "Failed to clear dirty bit in volume information flags. Run chkdsk."); + ntfs_commit_inode(vol->vol_ino); + ntfs_commit_inode(vol->root_ino); + if (vol->mftmirr_ino) + ntfs_commit_inode(vol->mftmirr_ino); + ntfs_commit_inode(vol->mft_ino); + } else { + ntfs_warning(sb, + "Volume has errors. Leaving volume marked dirty. Run chkdsk."); + } + } + + iput(vol->vol_ino); + vol->vol_ino = NULL; + + /* NTFS 3.0+ specific clean up. */ + if (vol->major_ver >= 3) { + if (vol->quota_q_ino) { + iput(vol->quota_q_ino); + vol->quota_q_ino = NULL; + } + if (vol->quota_ino) { + iput(vol->quota_ino); + vol->quota_ino = NULL; + } + if (vol->extend_ino) { + iput(vol->extend_ino); + vol->extend_ino = NULL; + } + if (vol->secure_ino) { + iput(vol->secure_ino); + vol->secure_ino = NULL; + } + } + + iput(vol->root_ino); + vol->root_ino = NULL; + + iput(vol->lcnbmp_ino); + vol->lcnbmp_ino = NULL; + + iput(vol->mftbmp_ino); + vol->mftbmp_ino = NULL; + + if (vol->logfile_ino) { + iput(vol->logfile_ino); + vol->logfile_ino = NULL; + } + if (vol->mftmirr_ino) { + /* Re-commit the mft mirror and mft just in case. */ + ntfs_commit_inode(vol->mftmirr_ino); + ntfs_commit_inode(vol->mft_ino); + iput(vol->mftmirr_ino); + vol->mftmirr_ino = NULL; + } + /* + * We should have no dirty inodes left, due to + * mft.c::ntfs_mft_writepage() cleaning all the dirty pages as + * the underlying mft records are written out and cleaned. + */ + ntfs_commit_inode(vol->mft_ino); + write_inode_now(vol->mft_ino, 1); + + iput(vol->mft_ino); + vol->mft_ino = NULL; + blkdev_issue_flush(sb->s_bdev); + + ntfs_volume_free(vol); +} + +int ntfs_force_shutdown(struct super_block *sb, u32 flags) +{ + struct ntfs_volume *vol = NTFS_SB(sb); + int ret; + + if (NVolShutdown(vol)) + return 0; + + switch (flags) { + case FS_SHUTDOWN_FLAGS_DEFAULT: + case FS_SHUTDOWN_FLAGS_LOGFLUSH: + ret = bdev_freeze(sb->s_bdev); + if (ret) + return ret; + bdev_thaw(sb->s_bdev); + NVolSetShutdown(vol); + break; + case FS_SHUTDOWN_FLAGS_NOLOGFLUSH: + NVolSetShutdown(vol); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void ntfs_shutdown(struct super_block *sb) +{ + ntfs_force_shutdown(sb, FS_SHUTDOWN_FLAGS_NOLOGFLUSH); + +} + +static int ntfs_sync_fs(struct super_block *sb, int wait) +{ + struct ntfs_volume *vol = NTFS_SB(sb); + int err = 0; + + if (NVolShutdown(vol)) + return -EIO; + + if (!wait) + return 0; + + /* If there are some dirty buffers in the bdev inode */ + if (ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) { + ntfs_warning(sb, "Failed to clear dirty bit in volume information flags. Run chkdsk."); + err = -EIO; + } + sync_inodes_sb(sb); + sync_blockdev(sb->s_bdev); + blkdev_issue_flush(sb->s_bdev); + return err; +} + +/* + * get_nr_free_clusters - return the number of free clusters on a volume + * @vol: ntfs volume for which to obtain free cluster count + * + * Calculate the number of free clusters on the mounted NTFS volume @vol. We + * actually calculate the number of clusters in use instead because this + * allows us to not care about partial pages as these will be just zero filled + * and hence not be counted as allocated clusters. + * + * The only particularity is that clusters beyond the end of the logical ntfs + * volume will be marked as allocated to prevent errors which means we have to + * discount those at the end. This is important as the cluster bitmap always + * has a size in multiples of 8 bytes, i.e. up to 63 clusters could be outside + * the logical volume and marked in use when they are not as they do not exist. + * + * If any pages cannot be read we assume all clusters in the erroring pages are + * in use. This means we return an underestimate on errors which is better than + * an overestimate. + */ +s64 get_nr_free_clusters(struct ntfs_volume *vol) +{ + s64 nr_free = vol->nr_clusters; + u32 nr_used; + struct address_space *mapping = vol->lcnbmp_ino->i_mapping; + struct folio *folio; + pgoff_t index, max_index; + struct file_ra_state *ra; + + ntfs_debug("Entering."); + /* Serialize accesses to the cluster bitmap. */ + + if (NVolFreeClusterKnown(vol)) + return atomic64_read(&vol->free_clusters); + + ra = kzalloc(sizeof(*ra), GFP_NOFS); + if (!ra) + return 0; + + file_ra_state_init(ra, mapping); + + /* + * Convert the number of bits into bytes rounded up, then convert into + * multiples of PAGE_SIZE, rounding up so that if we have one + * full and one partial page max_index = 2. + */ + max_index = (((vol->nr_clusters + 7) >> 3) + PAGE_SIZE - 1) >> + PAGE_SHIFT; + /* Use multiples of 4 bytes, thus max_size is PAGE_SIZE / 4. */ + ntfs_debug("Reading $Bitmap, max_index = 0x%lx, max_size = 0x%lx.", + max_index, PAGE_SIZE / 4); + for (index = 0; index < max_index; index++) { + unsigned long *kaddr; + + /* + * Get folio from page cache, getting it from backing store + * if necessary, and increment the use count. + */ + folio = ntfs_get_locked_folio(mapping, index, max_index, ra); + + /* Ignore pages which errored synchronously. */ + if (IS_ERR(folio)) { + ntfs_debug("Skipping page (index 0x%lx).", index); + nr_free -= PAGE_SIZE * 8; + vol->lcn_empty_bits_per_page[index] = 0; + continue; + } + + kaddr = kmap_local_folio(folio, 0); + /* + * Subtract the number of set bits. If this + * is the last page and it is partial we don't really care as + * it just means we do a little extra work but it won't affect + * the result as all out of range bytes are set to zero by + * ntfs_readpage(). + */ + nr_used = bitmap_weight(kaddr, PAGE_SIZE * BITS_PER_BYTE); + nr_free -= nr_used; + vol->lcn_empty_bits_per_page[index] = PAGE_SIZE * BITS_PER_BYTE - nr_used; + kunmap_local(kaddr); + folio_unlock(folio); + folio_put(folio); + } + ntfs_debug("Finished reading $Bitmap, last index = 0x%lx.", index - 1); + /* + * Fixup for eventual bits outside logical ntfs volume (see function + * description above). + */ + if (vol->nr_clusters & 63) + nr_free += 64 - (vol->nr_clusters & 63); + + /* If errors occurred we may well have gone below zero, fix this. */ + if (nr_free < 0) + nr_free = 0; + else + atomic64_set(&vol->free_clusters, nr_free); + + kfree(ra); + NVolSetFreeClusterKnown(vol); + wake_up_all(&vol->free_waitq); + ntfs_debug("Exiting."); + return nr_free; +} + +/* + * @nr_clusters is the number of clusters requested for allocation. + * + * Return the number of clusters available for allocation within + * the range of @nr_clusters, which is counts that considered + * for delayed allocation. + */ +s64 ntfs_available_clusters_count(struct ntfs_volume *vol, s64 nr_clusters) +{ + s64 free_clusters; + + /* wait event */ + if (!NVolFreeClusterKnown(vol)) + wait_event(vol->free_waitq, NVolFreeClusterKnown(vol)); + + free_clusters = atomic64_read(&vol->free_clusters) - + atomic64_read(&vol->dirty_clusters); + if (free_clusters <= 0) + return -ENOSPC; + else if (free_clusters < nr_clusters) + nr_clusters = free_clusters; + + return nr_clusters; +} + +/* + * __get_nr_free_mft_records - return the number of free inodes on a volume + * @vol: ntfs volume for which to obtain free inode count + * @nr_free: number of mft records in filesystem + * @max_index: maximum number of pages containing set bits + * + * Calculate the number of free mft records (inodes) on the mounted NTFS + * volume @vol. We actually calculate the number of mft records in use instead + * because this allows us to not care about partial pages as these will be just + * zero filled and hence not be counted as allocated mft record. + * + * If any pages cannot be read we assume all mft records in the erroring pages + * are in use. This means we return an underestimate on errors which is better + * than an overestimate. + * + * NOTE: Caller must hold mftbmp_lock rw_semaphore for reading or writing. + */ +static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol, + s64 nr_free, const pgoff_t max_index) +{ + struct address_space *mapping = vol->mftbmp_ino->i_mapping; + struct folio *folio; + pgoff_t index; + struct file_ra_state *ra; + + ntfs_debug("Entering."); + + ra = kzalloc(sizeof(*ra), GFP_NOFS); + if (!ra) + return 0; + + file_ra_state_init(ra, mapping); + + /* Use multiples of 4 bytes, thus max_size is PAGE_SIZE / 4. */ + ntfs_debug("Reading $MFT/$BITMAP, max_index = 0x%lx, max_size = 0x%lx.", + max_index, PAGE_SIZE / 4); + for (index = 0; index < max_index; index++) { + unsigned long *kaddr; + + /* + * Get folio from page cache, getting it from backing store + * if necessary, and increment the use count. + */ + folio = ntfs_get_locked_folio(mapping, index, max_index, ra); + + /* Ignore pages which errored synchronously. */ + if (IS_ERR(folio)) { + ntfs_debug("read_mapping_page() error. Skipping page (index 0x%lx).", + index); + nr_free -= PAGE_SIZE * 8; + continue; + } + + kaddr = kmap_local_folio(folio, 0); + /* + * Subtract the number of set bits. If this + * is the last page and it is partial we don't really care as + * it just means we do a little extra work but it won't affect + * the result as all out of range bytes are set to zero by + * ntfs_readpage(). + */ + nr_free -= bitmap_weight(kaddr, + PAGE_SIZE * BITS_PER_BYTE); + kunmap_local(kaddr); + folio_unlock(folio); + folio_put(folio); + } + ntfs_debug("Finished reading $MFT/$BITMAP, last index = 0x%lx.", + index - 1); + /* If errors occurred we may well have gone below zero, fix this. */ + if (nr_free < 0) + nr_free = 0; + else + atomic64_set(&vol->free_mft_records, nr_free); + + kfree(ra); + ntfs_debug("Exiting."); + return nr_free; +} + +/* + * ntfs_statfs - return information about mounted NTFS volume + * @dentry: dentry from mounted volume + * @sfs: statfs structure in which to return the information + * + * Return information about the mounted NTFS volume @dentry in the statfs structure + * pointed to by @sfs (this is initialized with zeros before ntfs_statfs is + * called). We interpret the values to be correct of the moment in time at + * which we are called. Most values are variable otherwise and this isn't just + * the free values but the totals as well. For example we can increase the + * total number of file nodes if we run out and we can keep doing this until + * there is no more space on the volume left at all. + * + * Called from vfs_statfs which is used to handle the statfs, fstatfs, and + * ustat system calls. + * + * Return 0 on success or -errno on error. + */ +static int ntfs_statfs(struct dentry *dentry, struct kstatfs *sfs) +{ + struct super_block *sb = dentry->d_sb; + s64 size; + struct ntfs_volume *vol = NTFS_SB(sb); + struct ntfs_inode *mft_ni = NTFS_I(vol->mft_ino); + unsigned long flags; + + ntfs_debug("Entering."); + /* Type of filesystem. */ + sfs->f_type = NTFS_SB_MAGIC; + /* Optimal transfer block size. */ + sfs->f_bsize = vol->cluster_size; + /* Fundamental file system block size, used as the unit. */ + sfs->f_frsize = vol->cluster_size; + + /* + * Total data blocks in filesystem in units of f_bsize and since + * inodes are also stored in data blocs ($MFT is a file) this is just + * the total clusters. + */ + sfs->f_blocks = vol->nr_clusters; + + /* wait event */ + if (!NVolFreeClusterKnown(vol)) + wait_event(vol->free_waitq, NVolFreeClusterKnown(vol)); + + /* Free data blocks in filesystem in units of f_bsize. */ + size = atomic64_read(&vol->free_clusters) - + atomic64_read(&vol->dirty_clusters); + if (size < 0LL) + size = 0LL; + + /* Free blocks avail to non-superuser, same as above on NTFS. */ + sfs->f_bavail = sfs->f_bfree = size; + + /* Number of inodes in filesystem (at this point in time). */ + read_lock_irqsave(&mft_ni->size_lock, flags); + sfs->f_files = i_size_read(vol->mft_ino) >> vol->mft_record_size_bits; + read_unlock_irqrestore(&mft_ni->size_lock, flags); + + /* Free inodes in fs (based on current total count). */ + sfs->f_ffree = atomic64_read(&vol->free_mft_records); + + /* + * File system id. This is extremely *nix flavour dependent and even + * within Linux itself all fs do their own thing. I interpret this to + * mean a unique id associated with the mounted fs and not the id + * associated with the filesystem driver, the latter is already given + * by the filesystem type in sfs->f_type. Thus we use the 64-bit + * volume serial number splitting it into two 32-bit parts. We enter + * the least significant 32-bits in f_fsid[0] and the most significant + * 32-bits in f_fsid[1]. + */ + sfs->f_fsid = u64_to_fsid(vol->serial_no); + /* Maximum length of filenames. */ + sfs->f_namelen = NTFS_MAX_NAME_LEN; + + return 0; +} + +static int ntfs_write_inode(struct inode *vi, struct writeback_control *wbc) +{ + return __ntfs_write_inode(vi, wbc->sync_mode == WB_SYNC_ALL); +} + +/* + * The complete super operations. + */ +static const struct super_operations ntfs_sops = { + .alloc_inode = ntfs_alloc_big_inode, /* VFS: Allocate new inode. */ + .free_inode = ntfs_free_big_inode, /* VFS: Deallocate inode. */ + .drop_inode = ntfs_drop_big_inode, + .write_inode = ntfs_write_inode, /* VFS: Write dirty inode to disk. */ + .put_super = ntfs_put_super, /* Syscall: umount. */ + .shutdown = ntfs_shutdown, + .sync_fs = ntfs_sync_fs, /* Syscall: sync. */ + .statfs = ntfs_statfs, /* Syscall: statfs */ + .evict_inode = ntfs_evict_big_inode, + .show_options = ntfs_show_options, /* Show mount options in proc. */ +}; + +static void precalc_free_clusters(struct work_struct *work) +{ + struct ntfs_volume *vol = container_of(work, struct ntfs_volume, precalc_work); + s64 nr_free; + + nr_free = get_nr_free_clusters(vol); + + ntfs_debug("pre-calculate free clusters(%lld) using workqueue", + nr_free); +} + +static struct lock_class_key ntfs_mft_inval_lock_key; + +/* + * ntfs_fill_super - mount an ntfs filesystem + * @sb: super block of the device to mount + * @fc: filesystem context containing mount options + * + * ntfs_fill_super() is called by the VFS to mount the device described by @sb + * with the mount otions in @data with the NTFS filesystem. + * + * If @silent is true, remain silent even if errors are detected. This is used + * during bootup, when the kernel tries to mount the root filesystem with all + * registered filesystems one after the other until one succeeds. This implies + * that all filesystems except the correct one will quite correctly and + * expectedly return an error, but nobody wants to see error messages when in + * fact this is what is supposed to happen. + */ +static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) +{ + char *boot; + struct inode *tmp_ino; + int blocksize, result; + pgoff_t lcn_bit_pages; + struct ntfs_volume *vol = NTFS_SB(sb); + int silent = fc->sb_flags & SB_SILENT; + + vol->sb = sb; + + /* + * We do a pretty difficult piece of bootstrap by reading the + * MFT (and other metadata) from disk into memory. We'll only + * release this metadata during umount, so the locking patterns + * observed during bootstrap do not count. So turn off the + * observation of locking patterns (strictly for this context + * only) while mounting NTFS. [The validator is still active + * otherwise, even for this context: it will for example record + * lock class registrations.] + */ + lockdep_off(); + ntfs_debug("Entering."); + + if (vol->nls_map && !strcmp(vol->nls_map->charset, "utf8")) + vol->nls_utf8 = true; + if (NVolDisableSparse(vol)) + vol->preallocated_size = 0; + + if (NVolDiscard(vol) && !bdev_max_discard_sectors(sb->s_bdev)) { + ntfs_warning( + sb, + "Discard requested but device does not support discard. Discard disabled."); + NVolClearDiscard(vol); + } + + /* We support sector sizes up to the PAGE_SIZE. */ + if (bdev_logical_block_size(sb->s_bdev) > PAGE_SIZE) { + if (!silent) + ntfs_error(sb, + "Device has unsupported sector size (%i). The maximum supported sector size on this architecture is %lu bytes.", + bdev_logical_block_size(sb->s_bdev), + PAGE_SIZE); + goto err_out_now; + } + + /* + * Setup the device access block size to NTFS_BLOCK_SIZE or the hard + * sector size, whichever is bigger. + */ + blocksize = sb_min_blocksize(sb, NTFS_BLOCK_SIZE); + if (blocksize < NTFS_BLOCK_SIZE) { + if (!silent) + ntfs_error(sb, "Unable to set device block size."); + goto err_out_now; + } + + ntfs_debug("Set device block size to %i bytes (block size bits %i).", + blocksize, sb->s_blocksize_bits); + /* Determine the size of the device in units of block_size bytes. */ + if (!bdev_nr_bytes(sb->s_bdev)) { + if (!silent) + ntfs_error(sb, "Unable to determine device size."); + goto err_out_now; + } + vol->nr_blocks = bdev_nr_bytes(sb->s_bdev) >> + sb->s_blocksize_bits; + /* Read the boot sector and return unlocked buffer head to it. */ + boot = read_ntfs_boot_sector(sb, silent); + if (!boot) { + if (!silent) + ntfs_error(sb, "Not an NTFS volume."); + goto err_out_now; + } + /* + * Extract the data from the boot sector and setup the ntfs volume + * using it. + */ + result = parse_ntfs_boot_sector(vol, (struct ntfs_boot_sector *)boot); + kfree(boot); + if (!result) { + if (!silent) + ntfs_error(sb, "Unsupported NTFS filesystem."); + goto err_out_now; + } + + if (vol->sector_size > blocksize) { + blocksize = sb_set_blocksize(sb, vol->sector_size); + if (blocksize != vol->sector_size) { + if (!silent) + ntfs_error(sb, + "Unable to set device block size to sector size (%i).", + vol->sector_size); + goto err_out_now; + } + vol->nr_blocks = bdev_nr_bytes(sb->s_bdev) >> + sb->s_blocksize_bits; + ntfs_debug("Changed device block size to %i bytes (block size bits %i) to match volume sector size.", + blocksize, sb->s_blocksize_bits); + } + /* Initialize the cluster and mft allocators. */ + ntfs_setup_allocators(vol); + /* Setup remaining fields in the super block. */ + sb->s_magic = NTFS_SB_MAGIC; + /* + * Ntfs allows 63 bits for the file size, i.e. correct would be: + * sb->s_maxbytes = ~0ULL >> 1; + * But the kernel uses a long as the page cache page index which on + * 32-bit architectures is only 32-bits. MAX_LFS_FILESIZE is kernel + * defined to the maximum the page cache page index can cope with + * without overflowing the index or to 2^63 - 1, whichever is smaller. + */ + sb->s_maxbytes = MAX_LFS_FILESIZE; + /* Ntfs measures time in 100ns intervals. */ + sb->s_time_gran = 100; + + sb->s_xattr = ntfs_xattr_handlers; + /* + * Now load the metadata required for the page cache and our address + * space operations to function. We do this by setting up a specialised + * read_inode method and then just calling the normal iget() to obtain + * the inode for $MFT which is sufficient to allow our normal inode + * operations and associated address space operations to function. + */ + sb->s_op = &ntfs_sops; + tmp_ino = new_inode(sb); + if (!tmp_ino) { + if (!silent) + ntfs_error(sb, "Failed to load essential metadata."); + goto err_out_now; + } + + tmp_ino->i_ino = FILE_MFT; + insert_inode_hash(tmp_ino); + if (ntfs_read_inode_mount(tmp_ino) < 0) { + if (!silent) + ntfs_error(sb, "Failed to load essential metadata."); + goto iput_tmp_ino_err_out_now; + } + lockdep_set_class(&tmp_ino->i_mapping->invalidate_lock, + &ntfs_mft_inval_lock_key); + + mutex_lock(&ntfs_lock); + + /* + * Generate the global default upcase table if necessary. Also + * temporarily increment the number of upcase users to avoid race + * conditions with concurrent (u)mounts. + */ + if (!default_upcase) + default_upcase = generate_default_upcase(); + ntfs_nr_upcase_users++; + mutex_unlock(&ntfs_lock); + + lcn_bit_pages = (((vol->nr_clusters + 7) >> 3) + PAGE_SIZE - 1) >> PAGE_SHIFT; + vol->lcn_empty_bits_per_page = kvmalloc_array(lcn_bit_pages, sizeof(unsigned int), + GFP_KERNEL); + if (!vol->lcn_empty_bits_per_page) { + ntfs_error(sb, + "Unable to allocate pages for storing LCN empty bit counts\n"); + goto unl_upcase_iput_tmp_ino_err_out_now; + } + + /* + * From now on, ignore @silent parameter. If we fail below this line, + * it will be due to a corrupt fs or a system error, so we report it. + */ + /* + * Open the system files with normal access functions and complete + * setting up the ntfs super block. + */ + if (!load_system_files(vol)) { + ntfs_error(sb, "Failed to load system files."); + goto unl_upcase_iput_tmp_ino_err_out_now; + } + + /* We grab a reference, simulating an ntfs_iget(). */ + ihold(vol->root_ino); + sb->s_root = d_make_root(vol->root_ino); + if (sb->s_root) { + s64 nr_records; + + ntfs_debug("Exiting, status successful."); + + /* Release the default upcase if it has no users. */ + mutex_lock(&ntfs_lock); + if (!--ntfs_nr_upcase_users && default_upcase) { + kvfree(default_upcase); + default_upcase = NULL; + } + mutex_unlock(&ntfs_lock); + sb->s_export_op = &ntfs_export_ops; + lockdep_on(); + + nr_records = __get_nr_free_mft_records(vol, + i_size_read(vol->mft_ino) >> vol->mft_record_size_bits, + ((((NTFS_I(vol->mft_ino)->initialized_size >> + vol->mft_record_size_bits) + + 7) >> 3) + PAGE_SIZE - 1) >> PAGE_SHIFT); + ntfs_debug("Free mft records(%lld)", nr_records); + + init_waitqueue_head(&vol->free_waitq); + INIT_WORK(&vol->precalc_work, precalc_free_clusters); + queue_work(ntfs_wq, &vol->precalc_work); + return 0; + } + ntfs_error(sb, "Failed to allocate root directory."); + /* Clean up after the successful load_system_files() call from above. */ + iput(vol->vol_ino); + vol->vol_ino = NULL; + /* NTFS 3.0+ specific clean up. */ + if (vol->major_ver >= 3) { + if (vol->quota_q_ino) { + iput(vol->quota_q_ino); + vol->quota_q_ino = NULL; + } + if (vol->quota_ino) { + iput(vol->quota_ino); + vol->quota_ino = NULL; + } + if (vol->extend_ino) { + iput(vol->extend_ino); + vol->extend_ino = NULL; + } + if (vol->secure_ino) { + iput(vol->secure_ino); + vol->secure_ino = NULL; + } + } + iput(vol->root_ino); + vol->root_ino = NULL; + iput(vol->lcnbmp_ino); + vol->lcnbmp_ino = NULL; + iput(vol->mftbmp_ino); + vol->mftbmp_ino = NULL; + if (vol->logfile_ino) { + iput(vol->logfile_ino); + vol->logfile_ino = NULL; + } + if (vol->mftmirr_ino) { + iput(vol->mftmirr_ino); + vol->mftmirr_ino = NULL; + } + /* Throw away the table of attribute definitions. */ + vol->attrdef_size = 0; + if (vol->attrdef) { + kvfree(vol->attrdef); + vol->attrdef = NULL; + } + vol->upcase_len = 0; + mutex_lock(&ntfs_lock); + if (vol->upcase == default_upcase) { + ntfs_nr_upcase_users--; + vol->upcase = NULL; + } + mutex_unlock(&ntfs_lock); + if (vol->upcase) { + kvfree(vol->upcase); + vol->upcase = NULL; + } + if (vol->nls_map) { + unload_nls(vol->nls_map); + vol->nls_map = NULL; + } + /* Error exit code path. */ +unl_upcase_iput_tmp_ino_err_out_now: + if (vol->lcn_empty_bits_per_page) + kvfree(vol->lcn_empty_bits_per_page); + /* + * Decrease the number of upcase users and destroy the global default + * upcase table if necessary. + */ + mutex_lock(&ntfs_lock); + if (!--ntfs_nr_upcase_users && default_upcase) { + kvfree(default_upcase); + default_upcase = NULL; + } + + mutex_unlock(&ntfs_lock); +iput_tmp_ino_err_out_now: + iput(tmp_ino); + if (vol->mft_ino && vol->mft_ino != tmp_ino) + iput(vol->mft_ino); + vol->mft_ino = NULL; + /* Errors at this stage are irrelevant. */ +err_out_now: + sb->s_fs_info = NULL; + kfree(vol); + ntfs_debug("Failed, returning -EINVAL."); + lockdep_on(); + return -EINVAL; +} + +/* + * This is a slab cache to optimize allocations and deallocations of Unicode + * strings of the maximum length allowed by NTFS, which is NTFS_MAX_NAME_LEN + * (255) Unicode characters + a terminating NULL Unicode character. + */ +struct kmem_cache *ntfs_name_cache; + +/* Slab caches for efficient allocation/deallocation of inodes. */ +struct kmem_cache *ntfs_inode_cache; +struct kmem_cache *ntfs_big_inode_cache; + +/* Init once constructor for the inode slab cache. */ +static void ntfs_big_inode_init_once(void *foo) +{ + struct ntfs_inode *ni = foo; + + inode_init_once(VFS_I(ni)); +} + +/* + * Slab caches to optimize allocations and deallocations of attribute search + * contexts and index contexts, respectively. + */ +struct kmem_cache *ntfs_attr_ctx_cache; +struct kmem_cache *ntfs_index_ctx_cache; + +/* Driver wide mutex. */ +DEFINE_MUTEX(ntfs_lock); + +static int ntfs_get_tree(struct fs_context *fc) +{ + return get_tree_bdev(fc, ntfs_fill_super); +} + +static void ntfs_free_fs_context(struct fs_context *fc) +{ + struct ntfs_volume *vol = fc->s_fs_info; + + if (vol) + ntfs_volume_free(vol); +} + +static const struct fs_context_operations ntfs_context_ops = { + .parse_param = ntfs_parse_param, + .get_tree = ntfs_get_tree, + .free = ntfs_free_fs_context, + .reconfigure = ntfs_reconfigure, +}; + +static int ntfs_init_fs_context(struct fs_context *fc) +{ + struct ntfs_volume *vol; + + /* Allocate a new struct ntfs_volume and place it in sb->s_fs_info. */ + vol = kmalloc(sizeof(struct ntfs_volume), GFP_NOFS); + if (!vol) + return -ENOMEM; + + /* Initialize struct ntfs_volume structure. */ + *vol = (struct ntfs_volume) { + .uid = INVALID_UID, + .gid = INVALID_GID, + .fmask = 0, + .dmask = 0, + .mft_zone_multiplier = 1, + .on_errors = ON_ERRORS_CONTINUE, + .nls_map = load_nls_default(), + .preallocated_size = NTFS_DEF_PREALLOC_SIZE, + }; + + NVolSetShowHiddenFiles(vol); + NVolSetCaseSensitive(vol); + init_rwsem(&vol->mftbmp_lock); + init_rwsem(&vol->lcnbmp_lock); + + fc->s_fs_info = vol; + fc->ops = &ntfs_context_ops; + return 0; +} + +static struct file_system_type ntfs_fs_type = { + .owner = THIS_MODULE, + .name = "ntfs", + .init_fs_context = ntfs_init_fs_context, + .parameters = ntfs_parameters, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, +}; +MODULE_ALIAS_FS("ntfs"); + +static int ntfs_workqueue_init(void) +{ + ntfs_wq = alloc_workqueue("ntfs-bg-io", 0, 0); + if (!ntfs_wq) + return -ENOMEM; + return 0; +} + +static void ntfs_workqueue_destroy(void) +{ + destroy_workqueue(ntfs_wq); + ntfs_wq = NULL; +} + +/* Stable names for the slab caches. */ +static const char ntfs_index_ctx_cache_name[] = "ntfs_index_ctx_cache"; +static const char ntfs_attr_ctx_cache_name[] = "ntfs_attr_ctx_cache"; +static const char ntfs_name_cache_name[] = "ntfs_name_cache"; +static const char ntfs_inode_cache_name[] = "ntfs_inode_cache"; +static const char ntfs_big_inode_cache_name[] = "ntfs_big_inode_cache"; + +static int __init init_ntfs_fs(void) +{ + int err = 0; + + err = ntfs_workqueue_init(); + if (err) { + pr_crit("Failed to register workqueue!\n"); + return err; + } + + ntfs_index_ctx_cache = kmem_cache_create(ntfs_index_ctx_cache_name, + sizeof(struct ntfs_index_context), 0 /* offset */, + SLAB_HWCACHE_ALIGN, NULL /* ctor */); + if (!ntfs_index_ctx_cache) { + pr_crit("Failed to create %s!\n", ntfs_index_ctx_cache_name); + goto ictx_err_out; + } + ntfs_attr_ctx_cache = kmem_cache_create(ntfs_attr_ctx_cache_name, + sizeof(struct ntfs_attr_search_ctx), 0 /* offset */, + SLAB_HWCACHE_ALIGN, NULL /* ctor */); + if (!ntfs_attr_ctx_cache) { + pr_crit("NTFS: Failed to create %s!\n", + ntfs_attr_ctx_cache_name); + goto actx_err_out; + } + + ntfs_name_cache = kmem_cache_create(ntfs_name_cache_name, + (NTFS_MAX_NAME_LEN+2) * sizeof(__le16), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!ntfs_name_cache) { + pr_crit("Failed to create %s!\n", ntfs_name_cache_name); + goto name_err_out; + } + + ntfs_inode_cache = kmem_cache_create(ntfs_inode_cache_name, + sizeof(struct ntfs_inode), 0, SLAB_RECLAIM_ACCOUNT, NULL); + if (!ntfs_inode_cache) { + pr_crit("Failed to create %s!\n", ntfs_inode_cache_name); + goto inode_err_out; + } + + ntfs_big_inode_cache = kmem_cache_create(ntfs_big_inode_cache_name, + sizeof(struct big_ntfs_inode), 0, SLAB_HWCACHE_ALIGN | + SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT, + ntfs_big_inode_init_once); + if (!ntfs_big_inode_cache) { + pr_crit("Failed to create %s!\n", ntfs_big_inode_cache_name); + goto big_inode_err_out; + } + + /* Register the ntfs sysctls. */ + err = ntfs_sysctl(1); + if (err) { + pr_crit("Failed to register NTFS sysctls!\n"); + goto sysctl_err_out; + } + + err = register_filesystem(&ntfs_fs_type); + if (!err) { + ntfs_debug("NTFS driver registered successfully."); + return 0; /* Success! */ + } + pr_crit("Failed to register NTFS filesystem driver!\n"); + + /* Unregister the ntfs sysctls. */ + ntfs_sysctl(0); +sysctl_err_out: + kmem_cache_destroy(ntfs_big_inode_cache); +big_inode_err_out: + kmem_cache_destroy(ntfs_inode_cache); +inode_err_out: + kmem_cache_destroy(ntfs_name_cache); +name_err_out: + kmem_cache_destroy(ntfs_attr_ctx_cache); +actx_err_out: + kmem_cache_destroy(ntfs_index_ctx_cache); +ictx_err_out: + if (!err) { + pr_crit("Aborting NTFS filesystem driver registration...\n"); + err = -ENOMEM; + } + return err; +} + +static void __exit exit_ntfs_fs(void) +{ + ntfs_debug("Unregistering NTFS driver."); + + unregister_filesystem(&ntfs_fs_type); + + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + kmem_cache_destroy(ntfs_big_inode_cache); + kmem_cache_destroy(ntfs_inode_cache); + kmem_cache_destroy(ntfs_name_cache); + kmem_cache_destroy(ntfs_attr_ctx_cache); + kmem_cache_destroy(ntfs_index_ctx_cache); + ntfs_workqueue_destroy(); + /* Unregister the ntfs sysctls. */ + ntfs_sysctl(0); +} + +module_init(init_ntfs_fs); +module_exit(exit_ntfs_fs); + +MODULE_AUTHOR("Anton Altaparmakov "); /* Original read-only NTFS driver */ +MODULE_AUTHOR("Namjae Jeon "); /* Add write, iomap and various features */ +MODULE_DESCRIPTION("NTFS read-write filesystem driver"); +MODULE_LICENSE("GPL"); +#ifdef DEBUG +module_param(debug_msgs, uint, 0); +MODULE_PARM_DESC(debug_msgs, "Enable debug messages."); +#endif diff --git a/fs/ntfs/sysctl.c b/fs/ntfs/sysctl.c new file mode 100644 index 000000000000..8c5c1b7cd483 --- /dev/null +++ b/fs/ntfs/sysctl.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Code for sysctl handling in NTFS Linux kernel driver. + * + * Copyright (C) 1997 Martin von Löwis, Régis Duchesne + * Copyright (c) 2002-2005 Anton Altaparmakov + */ + +#ifdef DEBUG + +#include + +#ifdef CONFIG_SYSCTL + +#include +#include + +#include "sysctl.h" +#include "debug.h" + +/* Definition of the ntfs sysctl. */ +static const struct ctl_table ntfs_sysctls[] = { + { + .procname = "ntfs-debug", + .data = &debug_msgs, /* Data pointer and size. */ + .maxlen = sizeof(debug_msgs), + .mode = 0644, /* Mode, proc handler. */ + .proc_handler = proc_dointvec + }, +}; + +/* Storage for the sysctls header. */ +static struct ctl_table_header *sysctls_root_table; + +/* + * ntfs_sysctl - add or remove the debug sysctl + * @add: add (1) or remove (0) the sysctl + * + * Add or remove the debug sysctl. Return 0 on success or -errno on error. + */ +int ntfs_sysctl(int add) +{ + if (add) { + sysctls_root_table = register_sysctl("fs/ntfs", ntfs_sysctls); + if (!sysctls_root_table) + return -ENOMEM; + } else { + unregister_sysctl_table(sysctls_root_table); + sysctls_root_table = NULL; + } + return 0; +} +#endif /* CONFIG_SYSCTL */ +#endif /* DEBUG */ diff --git a/fs/ntfs/sysctl.h b/fs/ntfs/sysctl.h new file mode 100644 index 000000000000..037127c994bf --- /dev/null +++ b/fs/ntfs/sysctl.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for sysctl handling in NTFS Linux kernel driver. + * + * Copyright (C) 1997 Martin von Löwis, Régis Duchesne + * Copyright (c) 2002-2004 Anton Altaparmakov + */ + +#ifndef _LINUX_NTFS_SYSCTL_H +#define _LINUX_NTFS_SYSCTL_H + + +#if defined(DEBUG) && defined(CONFIG_SYSCTL) + +int ntfs_sysctl(int add); + +#else + +/* Just return success. */ +static inline int ntfs_sysctl(int add) +{ + return 0; +} + +#endif /* DEBUG && CONFIG_SYSCTL */ +#endif /* _LINUX_NTFS_SYSCTL_H */ diff --git a/fs/ntfs/time.h b/fs/ntfs/time.h new file mode 100644 index 000000000000..aeed195bdcda --- /dev/null +++ b/fs/ntfs/time.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * NTFS time conversion functions. + * + * Copyright (c) 2001-2005 Anton Altaparmakov + */ + +#ifndef _LINUX_NTFS_TIME_H +#define _LINUX_NTFS_TIME_H + +#include +#include /* For do_div(). */ + +#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600) + +/* + * utc2ntfs - convert Linux UTC time to NTFS time + * @ts: Linux UTC time to convert to NTFS time + * + * Convert the Linux UTC time @ts to its corresponding NTFS time and return + * that in little endian format. + * + * Linux stores time in a struct timespec64 consisting of a time64_t tv_sec + * and a long tv_nsec where tv_sec is the number of 1-second intervals since + * 1st January 1970, 00:00:00 UTC and tv_nsec is the number of 1-nano-second + * intervals since the value of tv_sec. + * + * NTFS uses Microsoft's standard time format which is stored in a s64 and is + * measured as the number of 100-nano-second intervals since 1st January 1601, + * 00:00:00 UTC. + */ +static inline __le64 utc2ntfs(const struct timespec64 ts) +{ + /* + * Convert the seconds to 100ns intervals, add the nano-seconds + * converted to 100ns intervals, and then add the NTFS time offset. + */ + return cpu_to_le64((u64)(ts.tv_sec + NTFS_TIME_OFFSET) * 10000000 + + ts.tv_nsec / 100); +} + +/* + * get_current_ntfs_time - get the current time in little endian NTFS format + * + * Get the current time from the Linux kernel, convert it to its corresponding + * NTFS time and return that in little endian format. + */ +static inline __le64 get_current_ntfs_time(void) +{ + struct timespec64 ts; + + ktime_get_coarse_real_ts64(&ts); + return utc2ntfs(ts); +} + +/* + * ntfs2utc - convert NTFS time to Linux time + * @time: NTFS time (little endian) to convert to Linux UTC + * + * Convert the little endian NTFS time @time to its corresponding Linux UTC + * time and return that in cpu format. + * + * Linux stores time in a struct timespec64 consisting of a time64_t tv_sec + * and a long tv_nsec where tv_sec is the number of 1-second intervals since + * 1st January 1970, 00:00:00 UTC and tv_nsec is the number of 1-nano-second + * intervals since the value of tv_sec. + * + * NTFS uses Microsoft's standard time format which is stored in a s64 and is + * measured as the number of 100 nano-second intervals since 1st January 1601, + * 00:00:00 UTC. + */ +static inline struct timespec64 ntfs2utc(const __le64 time) +{ + struct timespec64 ts; + s32 t32; + + /* Subtract the NTFS time offset. */ + s64 t = le64_to_cpu(time) - NTFS_TIME_OFFSET * 10000000; + /* + * Convert the time to 1-second intervals and the remainder to + * 1-nano-second intervals. + */ + ts.tv_sec = div_s64_rem(t, 10000000, &t32); + ts.tv_nsec = t32 * 100; + return ts; +} +#endif /* _LINUX_NTFS_TIME_H */ diff --git a/fs/ntfs/unistr.c b/fs/ntfs/unistr.c new file mode 100644 index 000000000000..7f11a2825527 --- /dev/null +++ b/fs/ntfs/unistr.c @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS Unicode string handling. + * + * Copyright (c) 2001-2006 Anton Altaparmakov + */ + +#include "ntfs.h" + +/* + * IMPORTANT + * ========= + * + * All these routines assume that the Unicode characters are in little endian + * encoding inside the strings!!! + */ + +/* + * This is used by the name collation functions to quickly determine what + * characters are (in)valid. + */ +static const u8 legal_ansi_char_array[0x40] = { + 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + + 0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00, + + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18, +}; + +/* + * ntfs_are_names_equal - compare two Unicode names for equality + * @s1: name to compare to @s2 + * @s1_len: length in Unicode characters of @s1 + * @s2: name to compare to @s1 + * @s2_len: length in Unicode characters of @s2 + * @ic: ignore case bool + * @upcase: upcase table (only if @ic == IGNORE_CASE) + * @upcase_size: length in Unicode characters of @upcase (if present) + * + * Compare the names @s1 and @s2 and return 'true' (1) if the names are + * identical, or 'false' (0) if they are not identical. If @ic is IGNORE_CASE, + * the @upcase table is used to performa a case insensitive comparison. + */ +bool ntfs_are_names_equal(const __le16 *s1, size_t s1_len, + const __le16 *s2, size_t s2_len, const u32 ic, + const __le16 *upcase, const u32 upcase_size) +{ + if (s1_len != s2_len) + return false; + if (ic == CASE_SENSITIVE) + return !ntfs_ucsncmp(s1, s2, s1_len); + return !ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size); +} + +/* + * ntfs_collate_names - collate two Unicode names + * @name1: first Unicode name to compare + * @name1_len: first Unicode name length + * @name2: second Unicode name to compare + * @name2_len: second Unicode name length + * @err_val: if @name1 contains an invalid character return this value + * @ic: either CASE_SENSITIVE or IGNORE_CASE + * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) + * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE) + * + * ntfs_collate_names collates two Unicode names and returns: + * + * -1 if the first name collates before the second one, + * 0 if the names match, + * 1 if the second name collates before the first one, or + * @err_val if an invalid character is found in @name1 during the comparison. + * + * The following characters are considered invalid: '"', '*', '<', '>' and '?'. + */ +int ntfs_collate_names(const __le16 *name1, const u32 name1_len, + const __le16 *name2, const u32 name2_len, + const int err_val, const u32 ic, + const __le16 *upcase, const u32 upcase_len) +{ + u32 cnt, min_len; + u16 c1, c2; + + min_len = name1_len; + if (name1_len > name2_len) + min_len = name2_len; + for (cnt = 0; cnt < min_len; ++cnt) { + c1 = le16_to_cpu(*name1++); + c2 = le16_to_cpu(*name2++); + if (ic) { + if (c1 < upcase_len) + c1 = le16_to_cpu(upcase[c1]); + if (c2 < upcase_len) + c2 = le16_to_cpu(upcase[c2]); + } + if (c1 < 64 && legal_ansi_char_array[c1] & 8) + return err_val; + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } + if (name1_len < name2_len) + return -1; + if (name1_len == name2_len) + return 0; + /* name1_len > name2_len */ + c1 = le16_to_cpu(*name1); + if (c1 < 64 && legal_ansi_char_array[c1] & 8) + return err_val; + return 1; +} + +/* + * ntfs_ucsncmp - compare two little endian Unicode strings + * @s1: first string + * @s2: second string + * @n: maximum unicode characters to compare + * + * Compare the first @n characters of the Unicode strings @s1 and @s2, + * The strings in little endian format and appropriate le16_to_cpu() + * conversion is performed on non-little endian machines. + * + * The function returns an integer less than, equal to, or greater than zero + * if @s1 (or the first @n Unicode characters thereof) is found, respectively, + * to be less than, to match, or be greater than @s2. + */ +int ntfs_ucsncmp(const __le16 *s1, const __le16 *s2, size_t n) +{ + u16 c1, c2; + size_t i; + + for (i = 0; i < n; ++i) { + c1 = le16_to_cpu(s1[i]); + c2 = le16_to_cpu(s2[i]); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (!c1) + break; + } + return 0; +} + +/* + * ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring case + * @s1: first string + * @s2: second string + * @n: maximum unicode characters to compare + * @upcase: upcase table + * @upcase_size: upcase table size in Unicode characters + * + * Compare the first @n characters of the Unicode strings @s1 and @s2, + * ignoring case. The strings in little endian format and appropriate + * le16_to_cpu() conversion is performed on non-little endian machines. + * + * Each character is uppercased using the @upcase table before the comparison. + * + * The function returns an integer less than, equal to, or greater than zero + * if @s1 (or the first @n Unicode characters thereof) is found, respectively, + * to be less than, to match, or be greater than @s2. + */ +int ntfs_ucsncasecmp(const __le16 *s1, const __le16 *s2, size_t n, + const __le16 *upcase, const u32 upcase_size) +{ + size_t i; + u16 c1, c2; + + for (i = 0; i < n; ++i) { + c1 = le16_to_cpu(s1[i]); + if (c1 < upcase_size) + c1 = le16_to_cpu(upcase[c1]); + c2 = le16_to_cpu(s2[i]); + if (c2 < upcase_size) + c2 = le16_to_cpu(upcase[c2]); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (!c1) + break; + } + return 0; +} + +int ntfs_file_compare_values(const struct file_name_attr *file_name_attr1, + const struct file_name_attr *file_name_attr2, + const int err_val, const u32 ic, + const __le16 *upcase, const u32 upcase_len) +{ + return ntfs_collate_names((__le16 *)&file_name_attr1->file_name, + file_name_attr1->file_name_length, + (__le16 *)&file_name_attr2->file_name, + file_name_attr2->file_name_length, + err_val, ic, upcase, upcase_len); +} + +/* + * ntfs_nlstoucs - convert NLS string to little endian Unicode string + * @vol: ntfs volume which we are working with + * @ins: input NLS string buffer + * @ins_len: length of input string in bytes + * @outs: on return contains the allocated output Unicode string buffer + * @max_name_len: maximum number of Unicode characters allowed for the output name + * + * Convert the input string @ins, which is in whatever format the loaded NLS + * map dictates, into a little endian, 2-byte Unicode string. + * + * This function allocates the string and the caller is responsible for + * calling kmem_cache_free(ntfs_name_cache, *@outs); when finished with it. + * + * On success the function returns the number of Unicode characters written to + * the output string *@outs (>= 0), not counting the terminating Unicode NULL + * character. *@outs is set to the allocated output string buffer. + * + * On error, a negative number corresponding to the error code is returned. In + * that case the output string is not allocated. Both *@outs and *@outs_len + * are then undefined. + * + * This might look a bit odd due to fast path optimization... + */ +int ntfs_nlstoucs(const struct ntfs_volume *vol, const char *ins, + const int ins_len, __le16 **outs, int max_name_len) +{ + struct nls_table *nls = vol->nls_map; + __le16 *ucs; + wchar_t wc; + int i, o, wc_len; + + /* We do not trust outside sources. */ + if (likely(ins)) { + if (max_name_len > NTFS_MAX_NAME_LEN) + ucs = kvmalloc((max_name_len + 2) * sizeof(__le16), + GFP_NOFS | __GFP_ZERO); + else + ucs = kmem_cache_alloc(ntfs_name_cache, GFP_NOFS); + if (likely(ucs)) { + if (vol->nls_utf8) { + o = utf8s_to_utf16s(ins, ins_len, + UTF16_LITTLE_ENDIAN, + (wchar_t *)ucs, + max_name_len + 2); + if (o < 0 || o > max_name_len) { + wc_len = o; + goto name_err; + } + } else { + for (i = o = 0; i < ins_len; i += wc_len) { + wc_len = nls->char2uni(ins + i, ins_len - i, + &wc); + if (likely(wc_len >= 0 && + o < max_name_len)) { + if (likely(wc)) { + ucs[o++] = cpu_to_le16(wc); + continue; + } /* else if (!wc) */ + break; + } + + goto name_err; + } + } + ucs[o] = 0; + *outs = ucs; + return o; + } /* else if (!ucs) */ + ntfs_debug("Failed to allocate buffer for converted name from ntfs_name_cache."); + return -ENOMEM; + } /* else if (!ins) */ + ntfs_error(vol->sb, "Received NULL pointer."); + return -EINVAL; +name_err: + if (max_name_len > NTFS_MAX_NAME_LEN) + kvfree(ucs); + else + kmem_cache_free(ntfs_name_cache, ucs); + if (wc_len < 0) { + ntfs_debug("Name using character set %s contains characters that cannot be converted to Unicode.", + nls->charset); + i = -EILSEQ; + } else { + ntfs_debug("Name is too long (maximum length for a name on NTFS is %d Unicode characters.", + max_name_len); + i = -ENAMETOOLONG; + } + return i; +} + +/* + * ntfs_ucstonls - convert little endian Unicode string to NLS string + * @vol: ntfs volume which we are working with + * @ins: input Unicode string buffer + * @ins_len: length of input string in Unicode characters + * @outs: on return contains the (allocated) output NLS string buffer + * @outs_len: length of output string buffer in bytes + * + * Convert the input little endian, 2-byte Unicode string @ins, of length + * @ins_len into the string format dictated by the loaded NLS. + * + * If *@outs is NULL, this function allocates the string and the caller is + * responsible for calling kfree(*@outs); when finished with it. In this case + * @outs_len is ignored and can be 0. + * + * On success the function returns the number of bytes written to the output + * string *@outs (>= 0), not counting the terminating NULL byte. If the output + * string buffer was allocated, *@outs is set to it. + * + * On error, a negative number corresponding to the error code is returned. In + * that case the output string is not allocated. The contents of *@outs are + * then undefined. + * + * This might look a bit odd due to fast path optimization... + */ +int ntfs_ucstonls(const struct ntfs_volume *vol, const __le16 *ins, + const int ins_len, unsigned char **outs, int outs_len) +{ + struct nls_table *nls = vol->nls_map; + unsigned char *ns; + int i, o, ns_len, wc; + + /* We don't trust outside sources. */ + if (ins) { + ns = *outs; + ns_len = outs_len; + if (ns && !ns_len) { + wc = -ENAMETOOLONG; + goto conversion_err; + } + if (!ns) { + ns_len = ins_len * NLS_MAX_CHARSET_SIZE; + ns = kmalloc(ns_len + 1, GFP_NOFS); + if (!ns) + goto mem_err_out; + } + + if (vol->nls_utf8) { + o = utf16s_to_utf8s((const wchar_t *)ins, ins_len, + UTF16_LITTLE_ENDIAN, ns, ns_len); + if (o >= ns_len) { + wc = -ENAMETOOLONG; + goto conversion_err; + } + goto done; + } + + for (i = o = 0; i < ins_len; i++) { +retry: + wc = nls->uni2char(le16_to_cpu(ins[i]), ns + o, + ns_len - o); + if (wc > 0) { + o += wc; + continue; + } else if (!wc) + break; + else if (wc == -ENAMETOOLONG && ns != *outs) { + unsigned char *tc; + /* Grow in multiples of 64 bytes. */ + tc = kmalloc((ns_len + 64) & + ~63, GFP_NOFS); + if (tc) { + memcpy(tc, ns, ns_len); + ns_len = ((ns_len + 64) & ~63) - 1; + kfree(ns); + ns = tc; + goto retry; + } /* No memory so goto conversion_error; */ + } /* wc < 0, real error. */ + goto conversion_err; + } +done: + ns[o] = 0; + *outs = ns; + return o; + } /* else (!ins) */ + ntfs_error(vol->sb, "Received NULL pointer."); + return -EINVAL; +conversion_err: + ntfs_error(vol->sb, + "Unicode name contains characters that cannot be converted to character set %s. You might want to try to use the mount option nls=utf8.", + nls->charset); + if (ns != *outs) + kfree(ns); + if (wc != -ENAMETOOLONG) + wc = -EILSEQ; + return wc; +mem_err_out: + ntfs_error(vol->sb, "Failed to allocate name!"); + return -ENOMEM; +} + +/* + * ntfs_ucsnlen - determine the length of a little endian Unicode string + * @s: pointer to Unicode string + * @maxlen: maximum length of string @s + * + * Return the number of Unicode characters in the little endian Unicode + * string @s up to a maximum of maxlen Unicode characters, not including + * the terminating (__le16)'\0'. If there is no (__le16)'\0' between @s + * and @s + @maxlen, @maxlen is returned. + * + * This function never looks beyond @s + @maxlen. + */ +static u32 ntfs_ucsnlen(const __le16 *s, u32 maxlen) +{ + u32 i; + + for (i = 0; i < maxlen; i++) { + if (!le16_to_cpu(s[i])) + break; + } + return i; +} + +/* + * ntfs_ucsndup - duplicate little endian Unicode string + * @s: pointer to Unicode string + * @maxlen: maximum length of string @s + * + * Return a pointer to a new little endian Unicode string which is a duplicate + * of the string s. Memory for the new string is obtained with kmalloc, + * and can be freed with kfree. + * + * A maximum of @maxlen Unicode characters are copied and a terminating + * (__le16)'\0' little endian Unicode character is added. + * + * This function never looks beyond @s + @maxlen. + * + * Return a pointer to the new little endian Unicode string on success and NULL + * on failure with errno set to the error code. + */ +__le16 *ntfs_ucsndup(const __le16 *s, u32 maxlen) +{ + __le16 *dst; + u32 len; + + len = ntfs_ucsnlen(s, maxlen); + dst = kmalloc((len + 1) * sizeof(__le16), GFP_NOFS); + if (dst) { + memcpy(dst, s, len * sizeof(__le16)); + dst[len] = cpu_to_le16(L'\0'); + } + return dst; +} + +/* + * ntfs_names_are_equal - compare two Unicode names for equality + * @s1: name to compare to @s2 + * @s1_len: length in Unicode characters of @s1 + * @s2: name to compare to @s1 + * @s2_len: length in Unicode characters of @s2 + * @ic: ignore case bool + * @upcase: upcase table (only if @ic == IGNORE_CASE) + * @upcase_size: length in Unicode characters of @upcase (if present) + * + * Compare the names @s1 and @s2 and return TRUE (1) if the names are + * identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CASE, + * the @upcase table is used to perform a case insensitive comparison. + */ +bool ntfs_names_are_equal(const __le16 *s1, size_t s1_len, + const __le16 *s2, size_t s2_len, + const u32 ic, + const __le16 *upcase, const u32 upcase_size) +{ + if (s1_len != s2_len) + return false; + if (!s1_len) + return true; + if (ic == CASE_SENSITIVE) + return ntfs_ucsncmp(s1, s2, s1_len) ? false : true; + return ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? false : true; +} diff --git a/fs/ntfs/upcase.c b/fs/ntfs/upcase.c new file mode 100644 index 000000000000..4b954470883f --- /dev/null +++ b/fs/ntfs/upcase.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Generate the full NTFS Unicode upcase table in little endian. + * + * Copyright (c) 2001 Richard Russon + * Copyright (c) 2001-2006 Anton Altaparmakov + */ + +#include "ntfs.h" + +__le16 *generate_default_upcase(void) +{ + static const int uc_run_table[][3] = { /* Start, End, Add */ + {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, + {0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86}, + {0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100}, + {0x0256, 0x0258, -205}, {0x1F00, 0x1F08, 8}, {0x1F78, 0x1F7A, 128}, + {0x028A, 0x028C, -217}, {0x1F10, 0x1F16, 8}, {0x1F7A, 0x1F7C, 112}, + {0x03AC, 0x03AD, -38}, {0x1F20, 0x1F28, 8}, {0x1F7C, 0x1F7E, 126}, + {0x03AD, 0x03B0, -37}, {0x1F30, 0x1F38, 8}, {0x1FB0, 0x1FB2, 8}, + {0x03B1, 0x03C2, -32}, {0x1F40, 0x1F46, 8}, {0x1FD0, 0x1FD2, 8}, + {0x03C2, 0x03C3, -31}, {0x1F51, 0x1F52, 8}, {0x1FE0, 0x1FE2, 8}, + {0x03C3, 0x03CC, -32}, {0x1F53, 0x1F54, 8}, {0x1FE5, 0x1FE6, 7}, + {0x03CC, 0x03CD, -64}, {0x1F55, 0x1F56, 8}, {0x2170, 0x2180, -16}, + {0x03CD, 0x03CF, -63}, {0x1F57, 0x1F58, 8}, {0x24D0, 0x24EA, -26}, + {0x0430, 0x0450, -32}, {0x1F60, 0x1F68, 8}, {0xFF41, 0xFF5B, -32}, + {0} + }; + + static const int uc_dup_table[][2] = { /* Start, End */ + {0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC}, + {0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB}, + {0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5}, + {0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9}, + {0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95}, + {0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9}, + {0} + }; + + static const int uc_word_table[][2] = { /* Offset, Value */ + {0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196}, + {0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C}, + {0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D}, + {0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F}, + {0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9}, + {0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE}, + {0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7}, + {0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197}, + {0} + }; + + int i, r; + __le16 *uc; + + uc = kvcalloc(default_upcase_len, sizeof(__le16), GFP_NOFS); + if (!uc) + return uc; + /* Generate the little endian Unicode upcase table used by ntfs. */ + for (i = 0; i < default_upcase_len; i++) + uc[i] = cpu_to_le16(i); + for (r = 0; uc_run_table[r][0]; r++) + for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++) + le16_add_cpu(&uc[i], uc_run_table[r][2]); + for (r = 0; uc_dup_table[r][0]; r++) + for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2) + le16_add_cpu(&uc[i + 1], -1); + for (r = 0; uc_word_table[r][0]; r++) + uc[uc_word_table[r][0]] = cpu_to_le16(uc_word_table[r][1]); + return uc; +} diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h new file mode 100644 index 000000000000..af41427ec622 --- /dev/null +++ b/fs/ntfs/volume.h @@ -0,0 +1,296 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Defines for volume structures in NTFS Linux kernel driver. + * + * Copyright (c) 2001-2006 Anton Altaparmakov + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2025 LG Electronics Co., Ltd. + */ + +#ifndef _LINUX_NTFS_VOLUME_H +#define _LINUX_NTFS_VOLUME_H + +#include +#include +#include +#include +#include +#include + +#include "layout.h" + +#define NTFS_VOL_UID BIT(1) +#define NTFS_VOL_GID BIT(2) + +/* + * The NTFS in memory super block structure. + * + * @sb: Pointer back to the super_block. + * @nr_blocks: Number of sb->s_blocksize bytes sized blocks on the device. + * @flags: Miscellaneous flags, see below. + * @uid: uid that files will be mounted as. + * @gid: gid that files will be mounted as. + * @fmask: The mask for file permissions. + * @dmask: The mask for directory permissions. + * @mft_zone_multiplier: Initial mft zone multiplier. + * @on_errors: What to do on filesystem errors. + * @wb_err: Writeback error tracking. + * @sector_size: in bytes + * @sector_size_bits: log2(sector_size) + * @cluster_size: in bytes + * @cluster_size_mask: cluster_size - 1 + * @cluster_size_bits: log2(cluster_size) + * @mft_record_size: in bytes + * @mft_record_size_mask: mft_record_size - 1 + * @mft_record_size_bits: log2(mft_record_size) + * @index_record_size: in bytes + * @index_record_size_mask: index_record_size - 1 + * @index_record_size_bits: log2(index_record_size) + * @nr_clusters: Volume size in clusters == number of bits in lcn bitmap. + * @mft_lcn: Cluster location of mft data. + * @mftmirr_lcn: Cluster location of copy of mft. + * @serial_no: The volume serial number. + * @upcase_len: Number of entries in upcase[]. + * @upcase: The upcase table. + * @attrdef_size: Size of the attribute definition table in bytes. + * @attrdef: Table of attribute definitions. Obtained from FILE_AttrDef. + * @mft_data_pos: Mft record number at which to allocate the next mft record. + * @mft_zone_start: First cluster of the mft zone. + * @mft_zone_end: First cluster beyond the mft zone. + * @mft_zone_pos: Current position in the mft zone. + * @data1_zone_pos: Current position in the first data zone. + * @data2_zone_pos: Current position in the second data zone. + * @mft_ino: The VFS inode of $MFT. + * @mftbmp_ino: Attribute inode for $MFT/$BITMAP. + * @mftbmp_lock: Lock for serializing accesses to the mft record bitmap. + * @mftmirr_ino: The VFS inode of $MFTMirr. + * @mftmirr_size: Size of mft mirror in mft records. + * @logfile_ino: The VFS inode of LogFile. + * @lcnbmp_ino: The VFS inode of $Bitmap. + * @lcnbmp_lock: Lock for serializing accesses to the cluster bitmap + * @vol_ino: The VFS inode of $Volume. + * @vol_flags: Volume flags. + * @major_ver: Ntfs major version of volume. + * @minor_ver: Ntfs minor version of volume. + * @volume_label: volume label. + * @root_ino: The VFS inode of the root directory. + * @secure_ino: The VFS inode of $Secure (NTFS3.0+ only, otherwise NULL). + * @extend_ino: The VFS inode of $Extend (NTFS3.0+ only, otherwise NULL). + * @quota_ino: The VFS inode of $Quota. + * @quota_q_ino: Attribute inode for $Quota/$Q. + * @nls_map: NLS (National Language Support) table. + * @nls_utf8: NLS table for UTF-8. + * @free_waitq: Wait queue for threads waiting for free clusters or MFT records. + * @free_clusters: Track the number of free clusters. + * @free_mft_records: Track the free mft records. + * @dirty_clusters: Number of clusters that are dirty. + * @sparse_compression_unit: Size of compression/sparse unit in clusters. + * @lcn_empty_bits_per_page: Number of empty bits per page in the LCN bitmap. + * @precalc_work: Work structure for background pre-calculation tasks. + * @preallocated_size: reallocation size (in bytes). + */ +struct ntfs_volume { + struct super_block *sb; + s64 nr_blocks; + unsigned long flags; + kuid_t uid; + kgid_t gid; + umode_t fmask; + umode_t dmask; + u8 mft_zone_multiplier; + u8 on_errors; + errseq_t wb_err; + u16 sector_size; + u8 sector_size_bits; + u32 cluster_size; + u32 cluster_size_mask; + u8 cluster_size_bits; + u32 mft_record_size; + u32 mft_record_size_mask; + u8 mft_record_size_bits; + u32 index_record_size; + u32 index_record_size_mask; + u8 index_record_size_bits; + s64 nr_clusters; + s64 mft_lcn; + s64 mftmirr_lcn; + u64 serial_no; + u32 upcase_len; + __le16 *upcase; + s32 attrdef_size; + struct attr_def *attrdef; + s64 mft_data_pos; + s64 mft_zone_start; + s64 mft_zone_end; + s64 mft_zone_pos; + s64 data1_zone_pos; + s64 data2_zone_pos; + struct inode *mft_ino; + struct inode *mftbmp_ino; + struct rw_semaphore mftbmp_lock; + struct inode *mftmirr_ino; + int mftmirr_size; + struct inode *logfile_ino; + struct inode *lcnbmp_ino; + struct rw_semaphore lcnbmp_lock; + struct inode *vol_ino; + __le16 vol_flags; + u8 major_ver; + u8 minor_ver; + unsigned char *volume_label; + struct inode *root_ino; + struct inode *secure_ino; + struct inode *extend_ino; + struct inode *quota_ino; + struct inode *quota_q_ino; + struct nls_table *nls_map; + bool nls_utf8; + wait_queue_head_t free_waitq; + atomic64_t free_clusters; + atomic64_t free_mft_records; + atomic64_t dirty_clusters; + u8 sparse_compression_unit; + unsigned int *lcn_empty_bits_per_page; + struct work_struct precalc_work; + loff_t preallocated_size; +}; + +/* + * Defined bits for the flags field in the ntfs_volume structure. + * + * NV_Errors Volume has errors, prevent remount rw. + * NV_ShowSystemFiles Return system files in ntfs_readdir(). + * NV_CaseSensitive Treat file names as case sensitive and + * create filenames in the POSIX namespace. + * Otherwise be case insensitive but still + * create file names in POSIX namespace. + * NV_LogFileEmpty LogFile journal is empty. + * NV_QuotaOutOfDate Quota is out of date. + * NV_UsnJrnlStamped UsnJrnl has been stamped. + * NV_ReadOnly Volume is mounted read-only. + * NV_Compression Volume supports compression. + * NV_FreeClusterKnown Free cluster count is known and up-to-date. + * NV_Shutdown Volume is in shutdown state + * NV_SysImmutable Protect system files from deletion. + * NV_ShowHiddenFiles Return hidden files in ntfs_readdir(). + * NV_HideDotFiles Hide names beginning with a dot ("."). + * NV_CheckWindowsNames Refuse creation/rename of files with + * Windows-reserved names (CON, AUX, NUL, COM1, + * LPT1, etc.) or invalid characters. + * + * NV_Discard Issue discard/TRIM commands for freed clusters. + * NV_DisableSparse Disable creation of sparse regions. + */ +enum { + NV_Errors, + NV_ShowSystemFiles, + NV_CaseSensitive, + NV_LogFileEmpty, + NV_QuotaOutOfDate, + NV_UsnJrnlStamped, + NV_ReadOnly, + NV_Compression, + NV_FreeClusterKnown, + NV_Shutdown, + NV_SysImmutable, + NV_ShowHiddenFiles, + NV_HideDotFiles, + NV_CheckWindowsNames, + NV_Discard, + NV_DisableSparse, +}; + +/* + * Macro tricks to expand the NVolFoo(), NVolSetFoo(), and NVolClearFoo() + * functions. + */ +#define DEFINE_NVOL_BIT_OPS(flag) \ +static inline int NVol##flag(struct ntfs_volume *vol) \ +{ \ + return test_bit(NV_##flag, &(vol)->flags); \ +} \ +static inline void NVolSet##flag(struct ntfs_volume *vol) \ +{ \ + set_bit(NV_##flag, &(vol)->flags); \ +} \ +static inline void NVolClear##flag(struct ntfs_volume *vol) \ +{ \ + clear_bit(NV_##flag, &(vol)->flags); \ +} + +/* Emit the ntfs volume bitops functions. */ +DEFINE_NVOL_BIT_OPS(Errors) +DEFINE_NVOL_BIT_OPS(ShowSystemFiles) +DEFINE_NVOL_BIT_OPS(CaseSensitive) +DEFINE_NVOL_BIT_OPS(LogFileEmpty) +DEFINE_NVOL_BIT_OPS(QuotaOutOfDate) +DEFINE_NVOL_BIT_OPS(UsnJrnlStamped) +DEFINE_NVOL_BIT_OPS(ReadOnly) +DEFINE_NVOL_BIT_OPS(Compression) +DEFINE_NVOL_BIT_OPS(FreeClusterKnown) +DEFINE_NVOL_BIT_OPS(Shutdown) +DEFINE_NVOL_BIT_OPS(SysImmutable) +DEFINE_NVOL_BIT_OPS(ShowHiddenFiles) +DEFINE_NVOL_BIT_OPS(HideDotFiles) +DEFINE_NVOL_BIT_OPS(CheckWindowsNames) +DEFINE_NVOL_BIT_OPS(Discard) +DEFINE_NVOL_BIT_OPS(DisableSparse) + +static inline void ntfs_inc_free_clusters(struct ntfs_volume *vol, s64 nr) +{ + if (!NVolFreeClusterKnown(vol)) + wait_event(vol->free_waitq, NVolFreeClusterKnown(vol)); + atomic64_add(nr, &vol->free_clusters); +} + +static inline void ntfs_dec_free_clusters(struct ntfs_volume *vol, s64 nr) +{ + if (!NVolFreeClusterKnown(vol)) + wait_event(vol->free_waitq, NVolFreeClusterKnown(vol)); + atomic64_sub(nr, &vol->free_clusters); +} + +static inline void ntfs_inc_free_mft_records(struct ntfs_volume *vol, s64 nr) +{ + if (!NVolFreeClusterKnown(vol)) + return; + + atomic64_add(nr, &vol->free_mft_records); +} + +static inline void ntfs_dec_free_mft_records(struct ntfs_volume *vol, s64 nr) +{ + if (!NVolFreeClusterKnown(vol)) + return; + + atomic64_sub(nr, &vol->free_mft_records); +} + +static inline void ntfs_set_lcn_empty_bits(struct ntfs_volume *vol, unsigned long index, + u8 val, unsigned int count) +{ + if (!NVolFreeClusterKnown(vol)) + wait_event(vol->free_waitq, NVolFreeClusterKnown(vol)); + + if (val) + vol->lcn_empty_bits_per_page[index] -= count; + else + vol->lcn_empty_bits_per_page[index] += count; +} + +static __always_inline void ntfs_hold_dirty_clusters(struct ntfs_volume *vol, s64 nr_clusters) +{ + atomic64_add(nr_clusters, &vol->dirty_clusters); +} + +static __always_inline void ntfs_release_dirty_clusters(struct ntfs_volume *vol, s64 nr_clusters) +{ + if (atomic64_read(&vol->dirty_clusters) < nr_clusters) + atomic64_set(&vol->dirty_clusters, 0); + else + atomic64_sub(nr_clusters, &vol->dirty_clusters); +} + +s64 ntfs_available_clusters_count(struct ntfs_volume *vol, s64 nr_clusters); +s64 get_nr_free_clusters(struct ntfs_volume *vol); +#endif /* _LINUX_NTFS_VOLUME_H */ diff --git a/fs/ntfs3/Kconfig b/fs/ntfs3/Kconfig index 7bc31d69f680..876dbc613ae6 100644 --- a/fs/ntfs3/Kconfig +++ b/fs/ntfs3/Kconfig @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config NTFS3_FS tristate "NTFS Read-Write file system support" + depends on !NTFS_FS || m select BUFFER_HEAD select NLS select LEGACY_DIRECT_IO @@ -46,12 +47,3 @@ config NTFS3_FS_POSIX_ACL NOTE: this is linux only feature. Windows will ignore these ACLs. If you don't know what Access Control Lists are, say N. - -config NTFS_FS - tristate "NTFS file system support" - select NTFS3_FS - select BUFFER_HEAD - select NLS - help - This config option is here only for backward compatibility. NTFS - filesystem is now handled by the NTFS3 driver. diff --git a/fs/ntfs3/Makefile b/fs/ntfs3/Makefile index 279701b62bbe..53bf2c17ac28 100644 --- a/fs/ntfs3/Makefile +++ b/fs/ntfs3/Makefile @@ -3,6 +3,26 @@ # Makefile for the ntfs3 filesystem support. # +# Subset of W=1 warnings +subdir-ccflags-y += -Wextra -Wunused -Wno-unused-parameter +subdir-ccflags-y += -Wmissing-declarations +subdir-ccflags-y += -Wmissing-format-attribute +subdir-ccflags-y += -Wmissing-prototypes +subdir-ccflags-y += -Wold-style-definition +subdir-ccflags-y += -Wmissing-include-dirs +condflags := \ + $(call cc-option, -Wunused-but-set-variable) \ + $(call cc-option, -Wunused-const-variable) \ + $(call cc-option, -Wpacked-not-aligned) \ + $(call cc-option, -Wstringop-truncation) \ + $(call cc-option, -Wmaybe-uninitialized) +subdir-ccflags-y += $(condflags) +# The following turn off the warnings enabled by -Wextra +subdir-ccflags-y += -Wno-missing-field-initializers +subdir-ccflags-y += -Wno-sign-compare +subdir-ccflags-y += -Wno-type-limits +subdir-ccflags-y += -Wno-shift-negative-value + # to check robot warnings ccflags-y += -Wint-to-pointer-cast \ $(call cc-option,-Wunused-but-set-variable,-Wunused-const-variable) \ diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 6cb9bc5d605c..e61c5bf7e27e 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -173,7 +173,7 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, if (err == -ENOSPC && pre) { pre = 0; - if (*pre_alloc) + if (pre_alloc) *pre_alloc = 0; continue; } @@ -1152,6 +1152,21 @@ again: if (err) goto out; } + + if (vcn0 < svcn || evcn1 <= vcn0) { + struct ATTRIB *attr2; + + attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL, + 0, &vcn0, &mi); + if (!attr2) { + err = -EINVAL; + goto out; + } + err = attr_load_runs(attr2, ni, run, NULL); + if (err) + goto out; + } + da = false; /* no delalloc for compressed file. */ } @@ -1576,6 +1591,12 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, u64 from = vbo[i] & ~(u64)(PAGE_SIZE - 1); u64 to = min(from + PAGE_SIZE, wof_size); + if (from >= wof_size) { + _ntfs_bad_inode(&ni->vfs_inode); + err = -EINVAL; + goto out1; + } + err = attr_load_runs_range(ni, ATTR_DATA, WOF_NAME, ARRAY_SIZE(WOF_NAME), run, from, to); diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c index 4652a56ad105..d99ab086ef6f 100644 --- a/fs/ntfs3/dir.c +++ b/fs/ntfs3/dir.c @@ -676,14 +676,4 @@ const struct file_operations ntfs_dir_operations = { #endif .setlease = generic_setlease, }; - -#if IS_ENABLED(CONFIG_NTFS_FS) -const struct file_operations ntfs_legacy_dir_operations = { - .llseek = generic_file_llseek, - .read = generic_read_dir, - .iterate_shared = ntfs_readdir, - .open = ntfs_file_open, - .setlease = generic_setlease, -}; -#endif // clang-format on diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index fb07afef84d0..b041639ab406 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -1566,15 +1566,4 @@ const struct file_operations ntfs_file_operations = { .release = ntfs_file_release, .setlease = generic_setlease, }; - -#if IS_ENABLED(CONFIG_NTFS_FS) -const struct file_operations ntfs_legacy_file_operations = { - .llseek = generic_file_llseek, - .read_iter = ntfs_file_read_iter, - .splice_read = ntfs_file_splice_read, - .open = ntfs_file_open, - .release = ntfs_file_release, - .setlease = generic_setlease, -}; -#endif // clang-format on diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 2e901d073fe9..7b035da63c12 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -1852,27 +1852,31 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, return REPARSE_LINK; } -static struct page *ntfs_lock_new_page(struct address_space *mapping, - pgoff_t index, gfp_t gfp) +static struct folio *ntfs_lock_new_page(struct address_space *mapping, + pgoff_t index, gfp_t gfp) { - struct folio *folio = __filemap_get_folio( - mapping, index, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp); - struct page *page; + struct folio *folio = __filemap_get_folio(mapping, index, + FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp); if (IS_ERR(folio)) - return ERR_CAST(folio); + return folio; - if (!folio_test_uptodate(folio)) - return folio_file_page(folio, index); + if (!folio_test_uptodate(folio)) { + struct page *page = folio_file_page(folio, index); + + if (IS_ERR(page)) + return ERR_CAST(page); + return page_folio(page); + } /* Use a temporary page to avoid data corruption */ folio_unlock(folio); folio_put(folio); - page = alloc_page(gfp); - if (!page) + folio = folio_alloc(gfp, 0); + if (!folio) return ERR_PTR(-ENOMEM); - __SetPageLocked(page); - return page; + __folio_set_locked(folio); + return folio; } /* @@ -1894,6 +1898,7 @@ int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio) u32 i, idx, frame_size, pages_per_frame; gfp_t gfp_mask; struct page *pg; + struct folio *f; if (vbo >= i_size_read(&ni->vfs_inode)) { folio_zero_range(folio, 0, folio_size(folio)); @@ -1929,12 +1934,12 @@ int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio) if (i == idx) continue; - pg = ntfs_lock_new_page(mapping, index, gfp_mask); - if (IS_ERR(pg)) { - err = PTR_ERR(pg); + f = ntfs_lock_new_page(mapping, index, gfp_mask); + if (IS_ERR(f)) { + err = PTR_ERR(f); goto out1; } - pages[i] = pg; + pages[i] = &f->page; } ni_lock(ni); @@ -2023,18 +2028,18 @@ int ni_decompress_file(struct ntfs_inode *ni) } for (i = 0; i < pages_per_frame; i++, index++) { - struct page *pg; + struct folio *f; - pg = ntfs_lock_new_page(mapping, index, gfp_mask); - if (IS_ERR(pg)) { + f = ntfs_lock_new_page(mapping, index, gfp_mask); + if (IS_ERR(f)) { while (i--) { unlock_page(pages[i]); put_page(pages[i]); } - err = PTR_ERR(pg); + err = PTR_ERR(f); goto out; } - pages[i] = pg; + pages[i] = &f->page; } err = ni_read_frame(ni, vbo, pages, pages_per_frame, 1); @@ -3262,7 +3267,7 @@ int ni_allocate_da_blocks(struct ntfs_inode *ni) */ int ni_allocate_da_blocks_locked(struct ntfs_inode *ni) { - int err; + int err = 0; if (!ni->file.run_da.count) return 0; diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 272e45276143..acfa18b84401 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -45,10 +45,10 @@ struct CLIENT_REC { __le16 seq_num; // 0x14: u8 align[6]; // 0x16: __le32 name_bytes; // 0x1C: In bytes. - __le16 name[32]; // 0x20: Name of client. + __le16 name[64]; // 0x20: Name of client. }; -static_assert(sizeof(struct CLIENT_REC) == 0x60); +static_assert(sizeof(struct CLIENT_REC) == 0xa0); /* Two copies of these will exist at the beginning of the log file */ struct RESTART_AREA { @@ -2791,13 +2791,14 @@ static inline bool check_file_record(const struct MFT_REC *rec, u16 fn = le16_to_cpu(rec->rhdr.fix_num); u16 ao = le16_to_cpu(rec->attr_off); u32 rs = sbi->record_size; + u32 used = le32_to_cpu(rec->used); /* Check the file record header for consistency. */ if (rec->rhdr.sign != NTFS_FILE_SIGNATURE || fo > (SECTOR_SIZE - ((rs >> SECTOR_SHIFT) + 1) * sizeof(short)) || (fn - 1) * SECTOR_SIZE != rs || ao < MFTRECORD_FIXUP_OFFSET_1 || ao > sbi->record_size - SIZEOF_RESIDENT || !is_rec_inuse(rec) || - le32_to_cpu(rec->total) != rs) { + le32_to_cpu(rec->total) != rs || used > rs || used < ao) { return false; } @@ -2809,6 +2810,15 @@ static inline bool check_file_record(const struct MFT_REC *rec, return false; } + /* + * The do_action() handlers compute memmove lengths as + * "rec->used - ", which underflows when + * rec->used is smaller than the attribute walk reached. At this + * point attr is the ATTR_END marker; rec->used must cover it. + */ + if (used < PtrOffset(rec, attr) + sizeof(attr->type)) + return false; + return true; } diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index 0df2aa81d884..d0434756029b 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -1440,8 +1440,8 @@ int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr, u16 fo = le16_to_cpu(rhdr->fix_off); u16 fn = le16_to_cpu(rhdr->fix_num); u32 idx; - __le16 *fixup; - __le16 sample; + __le16 *fixup = NULL; + __le16 sample = cpu_to_le16(-1u); if ((fo & 1) || fo + fn * sizeof(short) > SECTOR_SIZE || !fn-- || fn * SECTOR_SIZE > bytes) { diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 97f06c26fe1a..5344b29b0577 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -714,10 +714,10 @@ static bool fnd_is_empty(struct ntfs_fnd *fnd) */ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, const struct INDEX_HDR *hdr, const void *key, - size_t key_len, const void *ctx, int *diff) + size_t key_len, const void *ctx, int *diff, + NTFS_CMP_FUNC cmp) { struct NTFS_DE *e, *found = NULL; - NTFS_CMP_FUNC cmp = indx->cmp; int min_idx = 0, mid_idx, max_idx = 0; int diff2; int table_size = 8; @@ -727,9 +727,6 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, u32 total = le32_to_cpu(hdr->total); u16 offs[128]; - if (unlikely(!cmp)) - return NULL; - fill_table: if (end > total) return NULL; @@ -800,7 +797,8 @@ binary_search: static struct NTFS_DE *hdr_insert_de(const struct ntfs_index *indx, struct INDEX_HDR *hdr, const struct NTFS_DE *de, - struct NTFS_DE *before, const void *ctx) + struct NTFS_DE *before, const void *ctx, + NTFS_CMP_FUNC cmp) { int diff; size_t off = PtrOffset(hdr, before); @@ -823,7 +821,7 @@ static struct NTFS_DE *hdr_insert_de(const struct ntfs_index *indx, } /* No insert point is applied. Get it manually. */ before = hdr_find_e(indx, hdr, de + 1, le16_to_cpu(de->key_size), ctx, - &diff); + &diff, cmp); if (!before) return NULL; off = PtrOffset(hdr, before); @@ -915,10 +913,6 @@ int indx_init(struct ntfs_index *indx, struct ntfs_sb_info *sbi, init_rwsem(&indx->run_lock); - indx->cmp = get_cmp_func(root); - if (!indx->cmp) - goto out; - return 0; out: @@ -1141,6 +1135,7 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, int err; struct NTFS_DE *e; struct indx_node *node; + NTFS_CMP_FUNC cmp; if (!root) root = indx_get_root(&ni->dir, ni, NULL, NULL); @@ -1150,10 +1145,16 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, return -EINVAL; } + cmp = get_cmp_func(root); + if (unlikely(!cmp)) { + WARN_ON_ONCE(1); + return -EINVAL; + } + /* Check cache. */ e = fnd->level ? fnd->de[fnd->level - 1] : fnd->root_de; if (e && !de_is_last(e) && - !(*indx->cmp)(key, key_len, e + 1, le16_to_cpu(e->key_size), ctx)) { + !(*cmp)(key, key_len, e + 1, le16_to_cpu(e->key_size), ctx)) { *entry = e; *diff = 0; return 0; @@ -1163,7 +1164,7 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, fnd_clear(fnd); /* Lookup entry that is <= to the search value. */ - e = hdr_find_e(indx, &root->ihdr, key, key_len, ctx, diff); + e = hdr_find_e(indx, &root->ihdr, key, key_len, ctx, diff, cmp); if (!e) return -EINVAL; @@ -1183,7 +1184,7 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, /* Lookup entry that is <= to the search value. */ e = hdr_find_e(indx, &node->index->ihdr, key, key_len, ctx, - diff); + diff, cmp); if (!e) { put_indx_node(node); return -EINVAL; @@ -1481,6 +1482,7 @@ out1: run_deallocate(sbi, &run, false); out: + run_close(&run); return err; } @@ -1585,7 +1587,7 @@ out1: static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, const struct NTFS_DE *new_de, struct NTFS_DE *root_de, const void *ctx, - struct ntfs_fnd *fnd, bool undo) + struct ntfs_fnd *fnd, bool undo, NTFS_CMP_FUNC cmp) { int err = 0; struct NTFS_DE *e, *e0, *re; @@ -1626,7 +1628,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, if ((undo || asize + ds_root < sbi->max_bytes_per_attr) && mi_resize_attr(mi, attr, ds_root)) { hdr->total = cpu_to_le32(hdr_total + ds_root); - e = hdr_insert_de(indx, hdr, new_de, root_de, ctx); + e = hdr_insert_de(indx, hdr, new_de, root_de, ctx, cmp); WARN_ON(!e); fnd_clear(fnd); fnd->root_de = e; @@ -1767,7 +1769,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, * Now root is a parent for new index buffer. * Insert NewEntry a new buffer. */ - e = hdr_insert_de(indx, hdr, new_de, NULL, ctx); + e = hdr_insert_de(indx, hdr, new_de, NULL, ctx, cmp); if (!e) { err = -EINVAL; goto out_put_n; @@ -1797,7 +1799,7 @@ out_free_root: static int indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, struct INDEX_ROOT *root, const struct NTFS_DE *new_de, - const void *ctx, int level, struct ntfs_fnd *fnd) + const void *ctx, int level, struct ntfs_fnd *fnd, NTFS_CMP_FUNC cmp) { int err; const struct NTFS_DE *sp; @@ -1814,7 +1816,7 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, /* Try the most easy case. */ e = fnd->level - 1 == level ? fnd->de[level] : NULL; - e = hdr_insert_de(indx, hdr1, new_de, e, ctx); + e = hdr_insert_de(indx, hdr1, new_de, e, ctx, cmp); fnd->de[level] = e; if (e) { /* Just write updated index into disk. */ @@ -1891,12 +1893,12 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, * (depending on sp <=> new_de). */ hdr_insert_de(indx, - (*indx->cmp)(new_de + 1, le16_to_cpu(new_de->key_size), + (*cmp)(new_de + 1, le16_to_cpu(new_de->key_size), up_e + 1, le16_to_cpu(up_e->key_size), ctx) < 0 ? hdr2 : hdr1, - new_de, NULL, ctx); + new_de, NULL, ctx, cmp); indx_mark_used(indx, ni, new_vbn >> indx->idx2vbn_bits); @@ -1911,14 +1913,14 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, */ if (!level) { /* Insert in root. */ - err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd, 0); + err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd, 0, cmp); } else { /* * The target buffer's parent is another index buffer. * TODO: Remove recursion. */ err = indx_insert_into_buffer(indx, ni, root, up_e, ctx, - level - 1, fnd); + level - 1, fnd, cmp); } if (err) { @@ -1952,6 +1954,7 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, struct NTFS_DE *e; struct ntfs_fnd *fnd_a = NULL; struct INDEX_ROOT *root; + NTFS_CMP_FUNC cmp; if (!fnd) { fnd_a = fnd_get(); @@ -1968,6 +1971,12 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, goto out; } + cmp = get_cmp_func(root); + if (unlikely(!cmp)) { + WARN_ON_ONCE(1); + return -EINVAL; + } + if (fnd_is_empty(fnd)) { /* * Find the spot the tree where we want to @@ -1991,13 +2000,13 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, * new entry into it. */ err = indx_insert_into_root(indx, ni, new_de, fnd->root_de, ctx, - fnd, undo); + fnd, undo, cmp); } else { /* * Found a leaf buffer, so we'll insert the new entry into it. */ err = indx_insert_into_buffer(indx, ni, root, new_de, ctx, - fnd->level - 1, fnd); + fnd->level - 1, fnd, cmp); } indx->version += 1; @@ -2291,6 +2300,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, u32 e_size, root_size, new_root_size; size_t trim_bit; const struct INDEX_NAMES *in; + NTFS_CMP_FUNC cmp; fnd = fnd_get(); if (!fnd) { @@ -2310,6 +2320,12 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, goto out; } + cmp = get_cmp_func(root); + if (unlikely(!cmp)) { + WARN_ON_ONCE(1); + return -EINVAL; + } + /* Locate the entry to remove. */ err = indx_find(indx, ni, root, key, key_len, ctx, &diff, &e, fnd); if (err) @@ -2376,9 +2392,9 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, err = level ? indx_insert_into_buffer(indx, ni, root, re, ctx, fnd->level - 1, - fnd) : + fnd, cmp) : indx_insert_into_root(indx, ni, re, e, - ctx, fnd, 0); + ctx, fnd, 0, cmp); kfree(re); if (err) @@ -2673,6 +2689,7 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, struct INDEX_ROOT *root; struct mft_inode *mi; struct ntfs_index *indx = &ni->dir; + NTFS_CMP_FUNC cmp; fnd = fnd_get(); if (!fnd) @@ -2684,6 +2701,12 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, goto out; } + cmp = get_cmp_func(root); + if (unlikely(!cmp)) { + WARN_ON_ONCE(1); + return -EINVAL; + } + /* Find entry in directory. */ err = indx_find(indx, ni, root, fname, fname_full_size(fname), sbi, &diff, &e, fnd); diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 7f2ed0f26686..42af1abe17f8 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -432,6 +432,11 @@ end_enum: ni->mi.dirty = true; } + if (!links) { + err = -EINVAL; + goto out; + } + set_nlink(inode, links); if (S_ISDIR(mode)) { @@ -443,9 +448,7 @@ end_enum: * Usually a hard links to directories are disabled. */ inode->i_op = &ntfs_dir_inode_operations; - inode->i_fop = unlikely(is_legacy_ntfs(sb)) ? - &ntfs_legacy_dir_operations : - &ntfs_dir_operations; + inode->i_fop = &ntfs_dir_operations; ni->i_valid = 0; } else if (S_ISLNK(mode)) { ni->std_fa &= ~FILE_ATTRIBUTE_DIRECTORY; @@ -455,9 +458,7 @@ end_enum: } else if (S_ISREG(mode)) { ni->std_fa &= ~FILE_ATTRIBUTE_DIRECTORY; inode->i_op = &ntfs_file_inode_operations; - inode->i_fop = unlikely(is_legacy_ntfs(sb)) ? - &ntfs_legacy_file_operations : - &ntfs_file_operations; + inode->i_fop = &ntfs_file_operations; inode->i_mapping->a_ops = is_compressed(ni) ? &ntfs_aops_cmpr : &ntfs_aops; if (ino != MFT_REC_MFT) @@ -777,6 +778,11 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, return err; } + if (!clen) { + /* broken file? */ + return -EINVAL; + } + if (lcn == EOF_LCN) { /* request out of file. */ if (flags & IOMAP_REPORT) { @@ -810,11 +816,6 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, return 0; } - if (!clen) { - /* broken file? */ - return -EINVAL; - } - iomap->bdev = inode->i_sb->s_bdev; iomap->offset = offset; iomap->length = ((loff_t)clen << cluster_bits) - off; @@ -1601,9 +1602,7 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, if (S_ISDIR(mode)) { inode->i_op = &ntfs_dir_inode_operations; - inode->i_fop = unlikely(is_legacy_ntfs(sb)) ? - &ntfs_legacy_dir_operations : - &ntfs_dir_operations; + inode->i_fop = &ntfs_dir_operations; } else if (S_ISLNK(mode)) { inode->i_op = &ntfs_link_inode_operations; inode->i_fop = NULL; @@ -1612,9 +1611,7 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, inode_nohighmem(inode); } else if (S_ISREG(mode)) { inode->i_op = &ntfs_file_inode_operations; - inode->i_fop = unlikely(is_legacy_ntfs(sb)) ? - &ntfs_legacy_file_operations : - &ntfs_file_operations; + inode->i_fop = &ntfs_file_operations; inode->i_mapping->a_ops = is_compressed(ni) ? &ntfs_aops_cmpr : &ntfs_aops; init_rwsem(&ni->file.run_lock); diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index daf5a1f47275..bbf3b6a1dcbe 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -196,9 +196,6 @@ struct ntfs_index { struct rw_semaphore run_lock; size_t version; /* increment each change */ - /*TODO: Remove 'cmp'. */ - NTFS_CMP_FUNC cmp; - u8 index_bits; // log2(root->index_block_size) u8 idx2vbn_bits; // log2(root->index_block_clst) u8 vbn2vbo_bits; // index_block_size < cluster? 9 : cluster_bits @@ -530,7 +527,6 @@ struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni, struct ntfs_fnd *fnd); bool dir_is_empty(struct inode *dir); extern const struct file_operations ntfs_dir_operations; -extern const struct file_operations ntfs_legacy_dir_operations; /* Globals from file.c */ int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path, @@ -546,7 +542,6 @@ long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg); extern const struct inode_operations ntfs_special_inode_operations; extern const struct inode_operations ntfs_file_inode_operations; extern const struct file_operations ntfs_file_operations; -extern const struct file_operations ntfs_legacy_file_operations; /* Globals from frecord.c */ void ni_remove_mi(struct ntfs_inode *ni, struct mft_inode *mi); @@ -1250,13 +1245,4 @@ static inline void le64_sub_cpu(__le64 *var, u64 val) *var = cpu_to_le64(le64_to_cpu(*var) - val); } -#if IS_ENABLED(CONFIG_NTFS_FS) -bool is_legacy_ntfs(struct super_block *sb); -#else -static inline bool is_legacy_ntfs(struct super_block *sb) -{ - return false; -} -#endif - #endif /* _LINUX_NTFS3_NTFS_FS_H */ diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index c0324cdc174d..1ce7d92fb274 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -1008,6 +1008,9 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, if (size_size > sizeof(len)) return -EINVAL; + if (run_buf + size_size > run_last) + return -EINVAL; + len = run_unpack_s64(run_buf, size_size, 0); /* Skip size_size. */ run_buf += size_size; @@ -1020,6 +1023,9 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, else if (offset_size <= sizeof(s64)) { s64 dlcn; + if (run_buf + offset_size > run_last) + return -EINVAL; + /* Initial value of dlcn is -1 or 0. */ dlcn = (run_buf[offset_size - 1] & 0x80) ? (s64)-1 : 0; dlcn = run_unpack_s64(run_buf, offset_size, dlcn); @@ -1059,9 +1065,15 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, return -EOPNOTSUPP; } #endif - if (lcn != SPARSE_LCN64 && lcn + len > sbi->used.bitmap.nbits) { - /* LCN range is out of volume. */ - return -EINVAL; + if (lcn != SPARSE_LCN64) { + u64 lcn_end; + + if (check_add_overflow(lcn, len, &lcn_end)) + return -EINVAL; + if (lcn_end > sbi->used.bitmap.nbits) { + /* LCN range is out of volume. */ + return -EINVAL; + } } if (!run) diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c index 51aa008e126a..004f59937559 100644 --- a/fs/ntfs3/super.c +++ b/fs/ntfs3/super.c @@ -434,12 +434,6 @@ static int ntfs_fs_reconfigure(struct fs_context *fc) struct ntfs_mount_options *new_opts = fc->fs_private; int ro_rw; - /* If ntfs3 is used as legacy ntfs enforce read-only mode. */ - if (is_legacy_ntfs(sb)) { - fc->sb_flags |= SB_RDONLY; - goto out; - } - ro_rw = sb_rdonly(sb) && !(fc->sb_flags & SB_RDONLY); if (ro_rw && (sbi->flags & NTFS_FLAGS_NEED_REPLAY)) { errorf(fc, @@ -466,7 +460,6 @@ static int ntfs_fs_reconfigure(struct fs_context *fc) return -EINVAL; } -out: sync_filesystem(sb); swap(sbi->options, fc->fs_private); @@ -1339,8 +1332,13 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) le32_to_cpu(attr->res.data_size) >> 1, UTF16_LITTLE_ENDIAN, sbi->volume.label, sizeof(sbi->volume.label)); - if (err < 0) + if (err < 0) { sbi->volume.label[0] = 0; + } else if (err >= sizeof(sbi->volume.label)) { + sbi->volume.label[sizeof(sbi->volume.label) - 1] = 0; + } else { + sbi->volume.label[err] = 0; + } } else { /* Should we break mounting here? */ //err = -EINVAL; @@ -1426,16 +1424,47 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) tt = inode->i_size >> sbi->record_bits; sbi->mft.next_free = MFT_REC_USER; - err = wnd_init(&sbi->mft.bitmap, sb, tt); - if (err) - goto put_inode_out; - err = ni_load_all_mi(ni); if (err) { ntfs_err(sb, "Failed to load $MFT's subrecords (%d).", err); goto put_inode_out; } + /* Merge MFT bitmap runs from extent records loaded by ni_load_all_mi. */ + { + struct ATTRIB *a = NULL; + struct ATTR_LIST_ENTRY *le = NULL; + + while ((a = ni_enum_attr_ex(ni, a, &le, NULL))) { + CLST svcn, evcn; + u16 roff; + + if (a->type != ATTR_BITMAP || !a->non_res) + continue; + + svcn = le64_to_cpu(a->nres.svcn); + if (!svcn) + continue; /* Base record runs already loaded. */ + + evcn = le64_to_cpu(a->nres.evcn); + roff = le16_to_cpu(a->nres.run_off); + + err = run_unpack_ex(&sbi->mft.bitmap.run, sbi, + MFT_REC_MFT, svcn, evcn, svcn, + Add2Ptr(a, roff), + le32_to_cpu(a->size) - roff); + if (err < 0) { + ntfs_err(sb, "Failed to unpack $MFT bitmap extent (%d).", err); + goto put_inode_out; + } + err = 0; + } + } + + err = wnd_init(&sbi->mft.bitmap, sb, tt); + if (err) + goto put_inode_out; + sbi->mft.ni = ni; /* Load $Bitmap. */ @@ -1673,7 +1702,7 @@ load_root: sb->s_root = d_make_root(inode); if (!sb->s_root) { err = -ENOMEM; - goto put_inode_out; + goto out; } if (boot2) { @@ -1699,8 +1728,6 @@ load_root: ntfs_create_procdir(sb); - if (is_legacy_ntfs(sb)) - sb->s_flags |= SB_RDONLY; return 0; put_inode_out: @@ -1823,7 +1850,7 @@ static const struct fs_context_operations ntfs_context_ops = { * This will called when mount/remount. We will first initialize * options so that if remount we can use just that. */ -static int __ntfs_init_fs_context(struct fs_context *fc) +static int ntfs_init_fs_context(struct fs_context *fc) { struct ntfs_mount_options *opts; struct ntfs_sb_info *sbi; @@ -1877,11 +1904,6 @@ free_opts: return -ENOMEM; } -static int ntfs_init_fs_context(struct fs_context *fc) -{ - return __ntfs_init_fs_context(fc); -} - static void ntfs3_kill_sb(struct super_block *sb) { struct ntfs_sb_info *sbi = sb->s_fs_info; @@ -1903,47 +1925,6 @@ static struct file_system_type ntfs_fs_type = { .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, }; -#if IS_ENABLED(CONFIG_NTFS_FS) -static int ntfs_legacy_init_fs_context(struct fs_context *fc) -{ - int ret; - - ret = __ntfs_init_fs_context(fc); - /* If ntfs3 is used as legacy ntfs enforce read-only mode. */ - fc->sb_flags |= SB_RDONLY; - return ret; -} - -static struct file_system_type ntfs_legacy_fs_type = { - .owner = THIS_MODULE, - .name = "ntfs", - .init_fs_context = ntfs_legacy_init_fs_context, - .parameters = ntfs_fs_parameters, - .kill_sb = ntfs3_kill_sb, - .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, -}; -MODULE_ALIAS_FS("ntfs"); - -static inline void register_as_ntfs_legacy(void) -{ - int err = register_filesystem(&ntfs_legacy_fs_type); - if (err) - pr_warn("ntfs3: Failed to register legacy ntfs filesystem driver: %d\n", err); -} - -static inline void unregister_as_ntfs_legacy(void) -{ - unregister_filesystem(&ntfs_legacy_fs_type); -} -bool is_legacy_ntfs(struct super_block *sb) -{ - return sb->s_type == &ntfs_legacy_fs_type; -} -#else -static inline void register_as_ntfs_legacy(void) {} -static inline void unregister_as_ntfs_legacy(void) {} -#endif - // clang-format on static int __init init_ntfs_fs(void) @@ -1972,7 +1953,6 @@ static int __init init_ntfs_fs(void) goto out1; } - register_as_ntfs_legacy(); err = register_filesystem(&ntfs_fs_type); if (err) goto out; @@ -1992,7 +1972,6 @@ static void __exit exit_ntfs_fs(void) rcu_barrier(); kmem_cache_destroy(ntfs_inode_cachep); unregister_filesystem(&ntfs_fs_type); - unregister_as_ntfs_legacy(); ntfs3_exit_bitmap(); ntfs_remove_proc_root(); } diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index 3fffda784892..9eeac0ab2b71 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -1031,7 +1031,7 @@ void ntfs_get_wsl_perm(struct inode *inode) i_gid_write(inode, (gid_t)le32_to_cpu(value[1])); inode->i_mode = le32_to_cpu(value[2]); - if (ntfs_get_ea(inode, "$LXDEV", sizeof("$$LXDEV") - 1, + if (ntfs_get_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1, &value[0], sizeof(value), &sz) == sizeof(value[0])) { inode->i_rdev = le32_to_cpu(value[0]); diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index d40f5d205bce..6e5fd3f12a84 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -917,11 +917,32 @@ static int ocfs2_validate_extent_block(struct super_block *sb, goto bail; } - if (le32_to_cpu(eb->h_fs_generation) != OCFS2_SB(sb)->fs_generation) + if (le32_to_cpu(eb->h_fs_generation) != OCFS2_SB(sb)->fs_generation) { rc = ocfs2_error(sb, "Extent block #%llu has an invalid h_fs_generation of #%u\n", (unsigned long long)bh->b_blocknr, le32_to_cpu(eb->h_fs_generation)); + goto bail; + } + + if (le16_to_cpu(eb->h_list.l_count) != ocfs2_extent_recs_per_eb(sb)) { + rc = ocfs2_error(sb, + "Extent block #%llu has invalid l_count %u (expected %u)\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(eb->h_list.l_count), + ocfs2_extent_recs_per_eb(sb)); + goto bail; + } + + if (le16_to_cpu(eb->h_list.l_next_free_rec) > le16_to_cpu(eb->h_list.l_count)) { + rc = ocfs2_error(sb, + "Extent block #%llu has invalid l_next_free_rec %u (l_count %u)\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(eb->h_list.l_next_free_rec), + le16_to_cpu(eb->h_list.l_count)); + goto bail; + } + bail: return rc; } @@ -1857,18 +1878,6 @@ static int __ocfs2_find_path(struct ocfs2_caching_info *ci, eb = (struct ocfs2_extent_block *) bh->b_data; el = &eb->h_list; - if (le16_to_cpu(el->l_next_free_rec) > - le16_to_cpu(el->l_count)) { - ocfs2_error(ocfs2_metadata_cache_get_super(ci), - "Owner %llu has bad count in extent list at block %llu (next free=%u, count=%u)\n", - (unsigned long long)ocfs2_metadata_cache_owner(ci), - (unsigned long long)bh->b_blocknr, - le16_to_cpu(el->l_next_free_rec), - le16_to_cpu(el->l_count)); - ret = -EROFS; - goto out; - } - if (func) func(data, bh); } diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index c7ad912ec7a0..6ec198bdab12 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -37,6 +37,8 @@ #include "namei.h" #include "sysfile.h" +#define OCFS2_DIO_MARK_EXTENT_BATCH 200 + static int ocfs2_symlink_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { @@ -2277,7 +2279,7 @@ static int ocfs2_dio_end_io_write(struct inode *inode, struct ocfs2_alloc_context *meta_ac = NULL; handle_t *handle = NULL; loff_t end = offset + bytes; - int ret = 0, credits = 0; + int ret = 0, credits = 0, batch = 0; ocfs2_init_dealloc_ctxt(&dealloc); @@ -2295,19 +2297,6 @@ static int ocfs2_dio_end_io_write(struct inode *inode, } down_write(&oi->ip_alloc_sem); - - /* Delete orphan before acquire i_rwsem. */ - if (dwc->dw_orphaned) { - BUG_ON(dwc->dw_writer_pid != task_pid_nr(current)); - - end = end > i_size_read(inode) ? end : 0; - - ret = ocfs2_del_inode_from_orphan(osb, inode, di_bh, - !!end, end); - if (ret < 0) - mlog_errno(ret); - } - di = (struct ocfs2_dinode *)di_bh->b_data; ocfs2_init_dinode_extent_tree(&et, INODE_CACHE(inode), di_bh); @@ -2327,24 +2316,25 @@ static int ocfs2_dio_end_io_write(struct inode *inode, credits = ocfs2_calc_extend_credits(inode->i_sb, &di->id2.i_list); - handle = ocfs2_start_trans(osb, credits); - if (IS_ERR(handle)) { - ret = PTR_ERR(handle); - mlog_errno(ret); - goto unlock; - } - ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), di_bh, - OCFS2_JOURNAL_ACCESS_WRITE); - if (ret) { - mlog_errno(ret); - goto commit; - } - list_for_each_entry(ue, &dwc->dw_zero_list, ue_node) { + if (!handle) { + handle = ocfs2_start_trans(osb, credits); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto unlock; + } + ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), di_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto commit; + } + } ret = ocfs2_assure_trans_credits(handle, credits); if (ret < 0) { mlog_errno(ret); - break; + goto commit; } ret = ocfs2_mark_extent_written(inode, &et, handle, ue->ue_cpos, 1, @@ -2352,19 +2342,44 @@ static int ocfs2_dio_end_io_write(struct inode *inode, meta_ac, &dealloc); if (ret < 0) { mlog_errno(ret); - break; + goto commit; + } + + if (++batch == OCFS2_DIO_MARK_EXTENT_BATCH) { + ocfs2_commit_trans(osb, handle); + handle = NULL; + batch = 0; } } if (end > i_size_read(inode)) { + if (!handle) { + handle = ocfs2_start_trans(osb, credits); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto unlock; + } + } ret = ocfs2_set_inode_size(handle, inode, di_bh, end); if (ret < 0) mlog_errno(ret); } + commit: - ocfs2_commit_trans(osb, handle); + if (handle) + ocfs2_commit_trans(osb, handle); unlock: up_write(&oi->ip_alloc_sem); + + /* everything looks good, let's start the cleanup */ + if (!ret && dwc->dw_orphaned) { + BUG_ON(dwc->dw_writer_pid != task_pid_nr(current)); + + ret = ocfs2_del_inode_from_orphan(osb, inode, di_bh, 0, 0); + if (ret < 0) + mlog_errno(ret); + } ocfs2_inode_unlock(inode, 1); brelse(di_bh); out: diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index fe1949578336..d12784aaaa4b 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -1488,33 +1488,45 @@ static struct o2hb_region *to_o2hb_region(struct config_item *item) return item ? container_of(item, struct o2hb_region, hr_item) : NULL; } -/* drop_item only drops its ref after killing the thread, nothing should - * be using the region anymore. this has to clean up any state that - * attributes might have built up. */ -static void o2hb_region_release(struct config_item *item) +static void o2hb_unmap_slot_data(struct o2hb_region *reg) { int i; struct page *page; - struct o2hb_region *reg = to_o2hb_region(item); - - mlog(ML_HEARTBEAT, "hb region release (%pg)\n", reg_bdev(reg)); - - kfree(reg->hr_tmp_block); if (reg->hr_slot_data) { for (i = 0; i < reg->hr_num_pages; i++) { page = reg->hr_slot_data[i]; - if (page) + if (page) { __free_page(page); + reg->hr_slot_data[i] = NULL; + } } kfree(reg->hr_slot_data); + reg->hr_slot_data = NULL; } + kfree(reg->hr_slots); + reg->hr_slots = NULL; + + kfree(reg->hr_tmp_block); + reg->hr_tmp_block = NULL; +} + +/* drop_item only drops its ref after killing the thread, nothing should + * be using the region anymore. this has to clean up any state that + * attributes might have built up. + */ +static void o2hb_region_release(struct config_item *item) +{ + struct o2hb_region *reg = to_o2hb_region(item); + + mlog(ML_HEARTBEAT, "hb region release (%pg)\n", reg_bdev(reg)); + + o2hb_unmap_slot_data(reg); + if (reg->hr_bdev_file) fput(reg->hr_bdev_file); - kfree(reg->hr_slots); - debugfs_remove_recursive(reg->hr_debug_dir); kfree(reg->hr_db_livenodes); kfree(reg->hr_db_regnum); @@ -1667,6 +1679,7 @@ static void o2hb_init_region_params(struct o2hb_region *reg) static int o2hb_map_slot_data(struct o2hb_region *reg) { int i, j; + int ret = -ENOMEM; unsigned int last_slot; unsigned int spp = reg->hr_slots_per_page; struct page *page; @@ -1674,14 +1687,14 @@ static int o2hb_map_slot_data(struct o2hb_region *reg) struct o2hb_disk_slot *slot; reg->hr_tmp_block = kmalloc(reg->hr_block_bytes, GFP_KERNEL); - if (reg->hr_tmp_block == NULL) - return -ENOMEM; + if (!reg->hr_tmp_block) + goto out; reg->hr_slots = kzalloc_objs(struct o2hb_disk_slot, reg->hr_blocks); - if (reg->hr_slots == NULL) - return -ENOMEM; + if (!reg->hr_slots) + goto out; - for(i = 0; i < reg->hr_blocks; i++) { + for (i = 0; i < reg->hr_blocks; i++) { slot = ®->hr_slots[i]; slot->ds_node_num = i; INIT_LIST_HEAD(&slot->ds_live_item); @@ -1695,12 +1708,12 @@ static int o2hb_map_slot_data(struct o2hb_region *reg) reg->hr_slot_data = kzalloc_objs(struct page *, reg->hr_num_pages); if (!reg->hr_slot_data) - return -ENOMEM; + goto out; - for(i = 0; i < reg->hr_num_pages; i++) { + for (i = 0; i < reg->hr_num_pages; i++) { page = alloc_page(GFP_KERNEL); if (!page) - return -ENOMEM; + goto out; reg->hr_slot_data[i] = page; @@ -1720,6 +1733,10 @@ static int o2hb_map_slot_data(struct o2hb_region *reg) } return 0; + +out: + o2hb_unmap_slot_data(reg); + return ret; } /* Read in all the slots available and populate the tracking @@ -1809,9 +1826,11 @@ static ssize_t o2hb_region_dev_store(struct config_item *item, "blocksize %u incorrect for device, expected %d", reg->hr_block_bytes, sectsize); ret = -EINVAL; - goto out3; + goto out; } + reg->hr_aborted_start = 0; + reg->hr_node_deleted = 0; o2hb_init_region_params(reg); /* Generation of zero is invalid */ @@ -1823,13 +1842,13 @@ static ssize_t o2hb_region_dev_store(struct config_item *item, ret = o2hb_map_slot_data(reg); if (ret) { mlog_errno(ret); - goto out3; + goto out; } ret = o2hb_populate_slot_data(reg); if (ret) { mlog_errno(ret); - goto out3; + goto out; } INIT_DELAYED_WORK(®->hr_write_timeout_work, o2hb_write_timeout); @@ -1860,7 +1879,7 @@ static ssize_t o2hb_region_dev_store(struct config_item *item, if (IS_ERR(hb_task)) { ret = PTR_ERR(hb_task); mlog_errno(ret); - goto out3; + goto out; } spin_lock(&o2hb_live_lock); @@ -1877,12 +1896,12 @@ static ssize_t o2hb_region_dev_store(struct config_item *item, if (reg->hr_aborted_start) { ret = -EIO; - goto out3; + goto out; } if (reg->hr_node_deleted) { ret = -EINVAL; - goto out3; + goto out; } /* Ok, we were woken. Make sure it wasn't by drop_item() */ @@ -1901,8 +1920,18 @@ static ssize_t o2hb_region_dev_store(struct config_item *item, printk(KERN_NOTICE "o2hb: Heartbeat started on region %s (%pg)\n", config_item_name(®->hr_item), reg_bdev(reg)); -out3: +out: if (ret < 0) { + spin_lock(&o2hb_live_lock); + hb_task = reg->hr_task; + reg->hr_task = NULL; + spin_unlock(&o2hb_live_lock); + + if (hb_task) + kthread_stop(hb_task); + + o2hb_unmap_slot_data(reg); + fput(reg->hr_bdev_file); reg->hr_bdev_file = NULL; } diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c index c4ba968e778b..e06774fd89d8 100644 --- a/fs/ocfs2/dcache.c +++ b/fs/ocfs2/dcache.c @@ -145,7 +145,7 @@ struct dentry *ocfs2_find_local_alias(struct inode *inode, struct dentry *dentry; spin_lock(&inode->i_lock); - hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { + for_each_alias(dentry, inode) { spin_lock(&dentry->d_lock); if (ocfs2_match_dentry(dentry, parent_blkno, skip_unhashed)) { trace_ocfs2_find_local_alias(dentry->d_name.len, diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index b82fe4431eb1..8e6b03238327 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -593,7 +593,7 @@ static int ocfs2_validate_dx_root(struct super_block *sb, mlog(ML_ERROR, "Checksum failed for dir index root block %llu\n", (unsigned long long)bh->b_blocknr); - return ret; + goto bail; } if (!OCFS2_IS_VALID_DX_ROOT(dx_root)) { @@ -601,8 +601,32 @@ static int ocfs2_validate_dx_root(struct super_block *sb, "Dir Index Root # %llu has bad signature %.*s\n", (unsigned long long)le64_to_cpu(dx_root->dr_blkno), 7, dx_root->dr_signature); + goto bail; } + if (!(dx_root->dr_flags & OCFS2_DX_FLAG_INLINE)) { + struct ocfs2_extent_list *el = &dx_root->dr_list; + + if (le16_to_cpu(el->l_count) != ocfs2_extent_recs_per_dx_root(sb)) { + ret = ocfs2_error(sb, + "Dir Index Root # %llu has invalid l_count %u (expected %u)\n", + (unsigned long long)le64_to_cpu(dx_root->dr_blkno), + le16_to_cpu(el->l_count), + ocfs2_extent_recs_per_dx_root(sb)); + goto bail; + } + + if (le16_to_cpu(el->l_next_free_rec) > le16_to_cpu(el->l_count)) { + ret = ocfs2_error(sb, + "Dir Index Root # %llu has invalid l_next_free_rec %u (l_count %u)\n", + (unsigned long long)le64_to_cpu(dx_root->dr_blkno), + le16_to_cpu(el->l_next_free_rec), + le16_to_cpu(el->l_count)); + goto bail; + } + } + +bail: return ret; } @@ -791,14 +815,6 @@ static int ocfs2_dx_dir_lookup_rec(struct inode *inode, struct ocfs2_extent_block *eb; struct ocfs2_extent_rec *rec = NULL; - if (le16_to_cpu(el->l_count) != - ocfs2_extent_recs_per_dx_root(inode->i_sb)) { - ret = ocfs2_error(inode->i_sb, - "Inode %llu has invalid extent list length %u\n", - inode->i_ino, le16_to_cpu(el->l_count)); - goto out; - } - if (el->l_tree_depth) { ret = ocfs2_find_leaf(INODE_CACHE(inode), el, major_hash, &eb_bh); @@ -819,14 +835,6 @@ static int ocfs2_dx_dir_lookup_rec(struct inode *inode, } } - if (le16_to_cpu(el->l_next_free_rec) == 0) { - ret = ocfs2_error(inode->i_sb, - "Inode %llu has empty extent list at depth %u\n", - inode->i_ino, - le16_to_cpu(el->l_tree_depth)); - goto out; - } - found = 0; for (i = le16_to_cpu(el->l_next_free_rec) - 1; i >= 0; i--) { rec = &el->l_recs[i]; @@ -839,10 +847,9 @@ static int ocfs2_dx_dir_lookup_rec(struct inode *inode, if (!found) { ret = ocfs2_error(inode->i_sb, - "Inode %llu has bad extent record (%u, %u, 0) in btree\n", - inode->i_ino, - le32_to_cpu(rec->e_cpos), - ocfs2_rec_clusters(el, rec)); + "Inode %llu has no extent record for hash %u in btree (next_free_rec %u)\n", + inode->i_ino, major_hash, + le16_to_cpu(el->l_next_free_rec)); goto out; } diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index 70ca79e4bdc3..dc9da9133c8e 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -980,6 +980,14 @@ static int dlm_match_regions(struct dlm_ctxt *dlm, goto bail; } + if (qr->qr_numregions > O2NM_MAX_REGIONS) { + mlog(ML_ERROR, "Domain %s: Joining node %d has invalid " + "number of heartbeat regions %u\n", + qr->qr_domain, qr->qr_node, qr->qr_numregions); + status = -EINVAL; + goto bail; + } + r = remote; for (i = 0; i < qr->qr_numregions; ++i) { mlog(0, "Region %.*s\n", O2HB_MAX_REGION_NAME_LEN, r); @@ -994,7 +1002,7 @@ static int dlm_match_regions(struct dlm_ctxt *dlm, for (i = 0; i < localnr; ++i) { foundit = 0; r = remote; - for (j = 0; j <= qr->qr_numregions; ++j) { + for (j = 0; j < qr->qr_numregions; ++j) { if (!memcmp(l, r, O2HB_MAX_REGION_NAME_LEN)) { foundit = 1; break; diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c index eb62724bbe9b..93eff38fdadd 100644 --- a/fs/ocfs2/dlm/dlmmaster.c +++ b/fs/ocfs2/dlm/dlmmaster.c @@ -930,7 +930,6 @@ redo_request: if (blocked) goto wait; - ret = -EINVAL; dlm_node_iter_init(mle->vote_map, &iter); while ((nodenum = dlm_node_iter_next(&iter)) >= 0) { ret = dlm_do_master_request(res, mle, nodenum); diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c index bfed0fb35f9b..cbe59d231666 100644 --- a/fs/ocfs2/ioctl.c +++ b/fs/ocfs2/ioctl.c @@ -441,13 +441,16 @@ static int ocfs2_info_freefrag_scan_chain(struct ocfs2_super *osb, struct buffer_head *bh = NULL; struct ocfs2_group_desc *bg = NULL; - unsigned int max_bits, num_clusters; + unsigned int max_bits, max_bitmap_bits, num_clusters; unsigned int offset = 0, cluster, chunk; unsigned int chunk_free, last_chunksize = 0; if (!le32_to_cpu(rec->c_free)) goto bail; + max_bitmap_bits = 8 * ocfs2_group_bitmap_size(osb->sb, 0, + osb->s_feature_incompat); + do { if (!bg) blkno = le64_to_cpu(rec->c_blkno); @@ -479,6 +482,19 @@ static int ocfs2_info_freefrag_scan_chain(struct ocfs2_super *osb, continue; max_bits = le16_to_cpu(bg->bg_bits); + + /* + * Non-coherent scans read raw blocks and do not get the + * bg_bits validation from + * ocfs2_read_group_descriptor(). + */ + if (max_bits > max_bitmap_bits) { + mlog(ML_ERROR, + "Group desc #%llu has %u bits, max bitmap bits %u\n", + (unsigned long long)blkno, max_bits, max_bitmap_bits); + max_bits = max_bitmap_bits; + } + offset = 0; for (chunk = 0; chunk < chunks_in_group; chunk++) { diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 4c86a9d46870..f9bf3bac085d 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -899,8 +899,13 @@ bail: static int ocfs2_journal_submit_inode_data_buffers(struct jbd2_inode *jinode) { - return filemap_fdatawrite_range(jinode->i_vfs_inode->i_mapping, - jinode->i_dirty_start, jinode->i_dirty_end); + struct address_space *mapping = jinode->i_vfs_inode->i_mapping; + loff_t range_start, range_end; + + if (!jbd2_jinode_get_dirty_range(jinode, &range_start, &range_end)) + return 0; + + return filemap_fdatawrite_range(mapping, range_start, range_end); } int ocfs2_journal_init(struct ocfs2_super *osb, int *dirty) diff --git a/fs/ocfs2/mmap.c b/fs/ocfs2/mmap.c index 50e2faf64c19..6c570157caf1 100644 --- a/fs/ocfs2/mmap.c +++ b/fs/ocfs2/mmap.c @@ -30,7 +30,8 @@ static vm_fault_t ocfs2_fault(struct vm_fault *vmf) { - struct vm_area_struct *vma = vmf->vma; + unsigned long long ip_blkno = + OCFS2_I(file_inode(vmf->vma->vm_file))->ip_blkno; sigset_t oldset; vm_fault_t ret; @@ -38,11 +39,9 @@ static vm_fault_t ocfs2_fault(struct vm_fault *vmf) ret = filemap_fault(vmf); ocfs2_unblock_signals(&oldset); - trace_ocfs2_fault(OCFS2_I(vma->vm_file->f_mapping->host)->ip_blkno, - vma, vmf->page, vmf->pgoff); + trace_ocfs2_fault(ip_blkno, vmf->page, vmf->pgoff); return ret; } - static vm_fault_t __ocfs2_page_mkwrite(struct file *file, struct buffer_head *di_bh, struct folio *folio) { diff --git a/fs/ocfs2/ocfs2_trace.h b/fs/ocfs2/ocfs2_trace.h index 4b32fb5658ad..6c2c97a9804f 100644 --- a/fs/ocfs2/ocfs2_trace.h +++ b/fs/ocfs2/ocfs2_trace.h @@ -1246,22 +1246,20 @@ TRACE_EVENT(ocfs2_write_end_inline, TRACE_EVENT(ocfs2_fault, TP_PROTO(unsigned long long ino, - void *area, void *page, unsigned long pgoff), - TP_ARGS(ino, area, page, pgoff), + void *page, unsigned long pgoff), + TP_ARGS(ino, page, pgoff), TP_STRUCT__entry( __field(unsigned long long, ino) - __field(void *, area) __field(void *, page) __field(unsigned long, pgoff) ), TP_fast_assign( __entry->ino = ino; - __entry->area = area; __entry->page = page; __entry->pgoff = pgoff; ), - TP_printk("%llu %p %p %lu", - __entry->ino, __entry->area, __entry->page, __entry->pgoff) + TP_printk("%llu %p %lu", + __entry->ino, __entry->page, __entry->pgoff) ); /* End of trace events for fs/ocfs2/mmap.c. */ diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c index e85b1ccf81be..77b8f0363e94 100644 --- a/fs/ocfs2/quota_global.c +++ b/fs/ocfs2/quota_global.c @@ -311,11 +311,25 @@ int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex) spin_unlock(&dq_data_lock); if (ex) { inode_lock(oinfo->dqi_gqinode); - down_write(&OCFS2_I(oinfo->dqi_gqinode)->ip_alloc_sem); + if (!down_write_trylock(&OCFS2_I(oinfo->dqi_gqinode)->ip_alloc_sem)) { + inode_unlock(oinfo->dqi_gqinode); + status = -EBUSY; + goto bail; + } } else { down_read(&OCFS2_I(oinfo->dqi_gqinode)->ip_alloc_sem); } return 0; + +bail: + /* does a similar job as ocfs2_unlock_global_qf */ + ocfs2_inode_unlock(oinfo->dqi_gqinode, ex); + brelse(oinfo->dqi_gqi_bh); + spin_lock(&dq_data_lock); + if (!--oinfo->dqi_gqi_count) + oinfo->dqi_gqi_bh = NULL; + spin_unlock(&dq_data_lock); + return status; } void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex) diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index 269b0f27d567..12cbb4fccda0 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c @@ -1224,7 +1224,9 @@ int ocfs2_create_local_dquot(struct dquot *dquot) int status; u64 pcount; - down_write(&OCFS2_I(lqinode)->ip_alloc_sem); + if (!down_write_trylock(&OCFS2_I(lqinode)->ip_alloc_sem)) + return -EBUSY; + chunk = ocfs2_find_free_entry(sb, type, &offset); if (!chunk) { chunk = ocfs2_extend_local_quota_file(sb, type, &offset); diff --git a/fs/ocfs2/resize.c b/fs/ocfs2/resize.c index ac3ec2c21119..6375d5035972 100644 --- a/fs/ocfs2/resize.c +++ b/fs/ocfs2/resize.c @@ -303,9 +303,13 @@ int ocfs2_group_extend(struct inode * inode, int new_clusters) fe = (struct ocfs2_dinode *)main_bm_bh->b_data; - /* main_bm_bh is validated by inode read inside ocfs2_inode_lock(), - * so any corruption is a code bug. */ - BUG_ON(!OCFS2_IS_VALID_DINODE(fe)); + /* JBD-managed buffers can bypass validation, so treat this as corruption. */ + if (!OCFS2_IS_VALID_DINODE(fe)) { + ret = ocfs2_error(main_bm_inode->i_sb, + "Invalid dinode #%llu\n", + (unsigned long long)OCFS2_I(main_bm_inode)->ip_blkno); + goto out_unlock; + } if (le16_to_cpu(fe->id2.i_chain.cl_cpg) != ocfs2_group_bitmap_size(osb->sb, 0, @@ -504,14 +508,14 @@ int ocfs2_group_add(struct inode *inode, struct ocfs2_new_group_input *input) goto out_unlock; } - ocfs2_set_new_buffer_uptodate(INODE_CACHE(inode), group_bh); - ret = ocfs2_verify_group_and_input(main_bm_inode, fe, input, group_bh); if (ret) { mlog_errno(ret); goto out_free_group_bh; } + ocfs2_set_new_buffer_uptodate(INODE_CACHE(main_bm_inode), group_bh); + trace_ocfs2_group_add((unsigned long long)input->group, input->chain, input->clusters, input->frees); @@ -519,7 +523,7 @@ int ocfs2_group_add(struct inode *inode, struct ocfs2_new_group_input *input) if (IS_ERR(handle)) { mlog_errno(PTR_ERR(handle)); ret = -EINVAL; - goto out_free_group_bh; + goto out_remove_cache; } cl_bpc = le16_to_cpu(fe->id2.i_chain.cl_bpc); @@ -573,9 +577,11 @@ int ocfs2_group_add(struct inode *inode, struct ocfs2_new_group_input *input) out_commit: ocfs2_commit_trans(osb, handle); -out_free_group_bh: +out_remove_cache: if (ret < 0) - ocfs2_remove_from_cache(INODE_CACHE(inode), group_bh); + ocfs2_remove_from_cache(INODE_CACHE(main_bm_inode), group_bh); + +out_free_group_bh: brelse(group_bh); out_unlock: diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index bb98bd51338e..d284e0e37252 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -197,6 +197,31 @@ static int ocfs2_validate_gd_self(struct super_block *sb, 8 * le16_to_cpu(gd->bg_size)); } + /* + * For discontiguous block groups, validate the on-disk extent list + * against the maximum number of extent records that can physically + * fit in a single block. + */ + if (ocfs2_gd_is_discontig(gd)) { + u16 max_recs = ocfs2_extent_recs_per_gd(sb); + u16 l_count = le16_to_cpu(gd->bg_list.l_count); + u16 l_next_free_rec = le16_to_cpu(gd->bg_list.l_next_free_rec); + + if (l_count != max_recs) { + do_error("Group descriptor #%llu bad discontig l_count %u expected %u\n", + (unsigned long long)bh->b_blocknr, + l_count, + max_recs); + } + + if (l_next_free_rec > l_count) { + do_error("Group descriptor #%llu bad discontig l_next_free_rec %u max %u\n", + (unsigned long long)bh->b_blocknr, + l_next_free_rec, + l_count); + } + } + return 0; } diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index d7c58fd7d438..b875f01c9756 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -2124,7 +2124,7 @@ static int ocfs2_initialize_super(struct super_block *sb, osb->osb_cluster_stack[0] = '\0'; } - get_random_bytes(&osb->s_next_generation, sizeof(u32)); + osb->s_next_generation = get_random_u32(); /* * FIXME diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 4d55ad963ac5..86cfd4c2adf9 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -911,8 +911,8 @@ static int ocfs2_xattr_list_entry(struct super_block *sb, total_len = prefix_len + name_len + 1; *result += total_len; - /* we are just looking for how big our buffer needs to be */ - if (!size) + /* No buffer means we are only looking for the required size. */ + if (!buffer) return 0; if (*result > size) diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c index 9e8a2a9e5229..7143b64b5b25 100644 --- a/fs/orangefs/inode.c +++ b/fs/orangefs/inode.c @@ -224,6 +224,8 @@ static void orangefs_readahead(struct readahead_control *rac) loff_t new_start = readahead_pos(rac); int ret; size_t new_len = 0; + size_t this_size; + size_t remaining; loff_t bytes_remaining = inode->i_size - readahead_pos(rac); loff_t pages_remaining = bytes_remaining / PAGE_SIZE; @@ -239,17 +241,33 @@ static void orangefs_readahead(struct readahead_control *rac) offset = readahead_pos(rac); i_pages = &rac->mapping->i_pages; - iov_iter_xarray(&iter, ITER_DEST, i_pages, offset, readahead_length(rac)); + iov_iter_xarray(&iter, ITER_DEST, i_pages, + offset, readahead_length(rac)); - /* read in the pages. */ - if ((ret = wait_for_direct_io(ORANGEFS_IO_READ, inode, - &offset, &iter, readahead_length(rac), - inode->i_size, NULL, NULL, rac->file)) < 0) - gossip_debug(GOSSIP_FILE_DEBUG, - "%s: wait_for_direct_io failed. \n", __func__); - else - ret = 0; + remaining = readahead_length(rac); + while (remaining) { + if (remaining > 4194304) + this_size = 4194304; + else + this_size = remaining; + /* read in the pages. */ + if ((ret = wait_for_direct_io(ORANGEFS_IO_READ, inode, + &offset, &iter, this_size, + inode->i_size, NULL, NULL, rac->file)) < 0) { + gossip_debug(GOSSIP_FILE_DEBUG, + "%s: wait_for_direct_io failed. :%d: \n", + __func__, ret); + goto cleanup; + } else { + ret = 0; + } + + remaining -= this_size; + offset += this_size; + } + +cleanup: /* clean up. */ while ((folio = readahead_folio(rac))) { if (!ret) diff --git a/fs/orangefs/orangefs-bufmap.c b/fs/orangefs/orangefs-bufmap.c index 5dd2708cce94..a557e1cf7436 100644 --- a/fs/orangefs/orangefs-bufmap.c +++ b/fs/orangefs/orangefs-bufmap.c @@ -139,9 +139,15 @@ static int get(struct slot_map *m) /* used to describe mapped buffers */ struct orangefs_bufmap_desc { void __user *uaddr; /* user space address pointer */ - struct page **page_array; /* array of mapped pages */ - int array_count; /* size of above arrays */ - struct list_head list_link; + struct folio **folio_array; + /* + * folio_offsets could be needed when userspace sets custom + * sizes in user_desc, or when folios aren't all backed by + * 2MB THPs. + */ + size_t *folio_offsets; + int folio_count; + bool is_two_2mib_chunks; }; static struct orangefs_bufmap { @@ -150,8 +156,10 @@ static struct orangefs_bufmap { int desc_count; int total_size; int page_count; + int folio_count; struct page **page_array; + struct folio **folio_array; struct orangefs_bufmap_desc *desc_array; /* array to track usage of buffer descriptors */ @@ -174,6 +182,17 @@ orangefs_bufmap_unmap(struct orangefs_bufmap *bufmap) static void orangefs_bufmap_free(struct orangefs_bufmap *bufmap) { + int i; + + if (!bufmap) + return; + + for (i = 0; i < bufmap->desc_count; i++) { + kfree(bufmap->desc_array[i].folio_array); + kfree(bufmap->desc_array[i].folio_offsets); + bufmap->desc_array[i].folio_array = NULL; + bufmap->desc_array[i].folio_offsets = NULL; + } kfree(bufmap->page_array); kfree(bufmap->desc_array); bitmap_free(bufmap->buffer_index_array); @@ -213,8 +232,10 @@ orangefs_bufmap_alloc(struct ORANGEFS_dev_map_desc *user_desc) bufmap->desc_count = user_desc->count; bufmap->desc_size = user_desc->size; bufmap->desc_shift = ilog2(bufmap->desc_size); + bufmap->page_count = bufmap->total_size / PAGE_SIZE; - bufmap->buffer_index_array = bitmap_zalloc(bufmap->desc_count, GFP_KERNEL); + bufmap->buffer_index_array = + bitmap_zalloc(bufmap->desc_count, GFP_KERNEL); if (!bufmap->buffer_index_array) goto out_free_bufmap; @@ -223,16 +244,21 @@ orangefs_bufmap_alloc(struct ORANGEFS_dev_map_desc *user_desc) if (!bufmap->desc_array) goto out_free_index_array; - bufmap->page_count = bufmap->total_size / PAGE_SIZE; - /* allocate storage to track our page mappings */ bufmap->page_array = kzalloc_objs(struct page *, bufmap->page_count); if (!bufmap->page_array) goto out_free_desc_array; + /* allocate folio array. */ + bufmap->folio_array = kzalloc_objs(struct folio *, bufmap->page_count); + if (!bufmap->folio_array) + goto out_free_page_array; + return bufmap; +out_free_page_array: + kfree(bufmap->page_array); out_free_desc_array: kfree(bufmap->desc_array); out_free_index_array: @@ -243,16 +269,65 @@ out: return NULL; } -static int -orangefs_bufmap_map(struct orangefs_bufmap *bufmap, - struct ORANGEFS_dev_map_desc *user_desc) +static int orangefs_bufmap_group_folios(struct orangefs_bufmap *bufmap) +{ + int i = 0; + int f = 0; + int k; + int num_pages; + struct page *page; + struct folio *folio; + + while (i < bufmap->page_count) { + page = bufmap->page_array[i]; + folio = page_folio(page); + num_pages = folio_nr_pages(folio); + gossip_debug(GOSSIP_BUFMAP_DEBUG, + "%s: i:%d: num_pages:%d: \n", __func__, i, num_pages); + + for (k = 1; k < num_pages; k++) { + if (bufmap->page_array[i + k] != folio_page(folio, k)) { + gossip_err("%s: bad match, i:%d: k:%d:\n", + __func__, i, k); + return -EINVAL; + } + } + + bufmap->folio_array[f++] = folio; + i += num_pages; + } + + bufmap->folio_count = f; + pr_info("%s: Grouped %d folios from %d pages.\n", + __func__, + bufmap->folio_count, + bufmap->page_count); + return 0; +} + +static int orangefs_bufmap_map(struct orangefs_bufmap *bufmap, + struct ORANGEFS_dev_map_desc *user_desc) { int pages_per_desc = bufmap->desc_size / PAGE_SIZE; - int offset = 0, ret, i; + int ret; + int i; + int j; + int current_folio; + int desc_pages_needed; + int desc_folio_count; + int remaining_pages; + int need_avail_min; + int pages_assigned_to_this_desc; + int allocated_descs = 0; + size_t current_offset; + size_t adjust_offset; + struct folio *folio; /* map the pages */ ret = pin_user_pages_fast((unsigned long)user_desc->ptr, - bufmap->page_count, FOLL_WRITE, bufmap->page_array); + bufmap->page_count, + FOLL_WRITE, + bufmap->page_array); if (ret < 0) return ret; @@ -260,7 +335,6 @@ orangefs_bufmap_map(struct orangefs_bufmap *bufmap, if (ret != bufmap->page_count) { gossip_err("orangefs error: asked for %d pages, only got %d.\n", bufmap->page_count, ret); - for (i = 0; i < ret; i++) unpin_user_page(bufmap->page_array[i]); return -ENOMEM; @@ -275,16 +349,120 @@ orangefs_bufmap_map(struct orangefs_bufmap *bufmap, for (i = 0; i < bufmap->page_count; i++) flush_dcache_page(bufmap->page_array[i]); - /* build a list of available descriptors */ - for (offset = 0, i = 0; i < bufmap->desc_count; i++) { - bufmap->desc_array[i].page_array = &bufmap->page_array[offset]; - bufmap->desc_array[i].array_count = pages_per_desc; + /* + * Group pages into folios. + */ + ret = orangefs_bufmap_group_folios(bufmap); + if (ret) + goto unpin; + + pr_info("%s: desc_size=%d bytes (%d pages per desc), total folios=%d\n", + __func__, bufmap->desc_size, pages_per_desc, + bufmap->folio_count); + + current_folio = 0; + remaining_pages = 0; + current_offset = 0; + for (i = 0; i < bufmap->desc_count; i++) { + desc_pages_needed = pages_per_desc; + desc_folio_count = 0; + pages_assigned_to_this_desc = 0; + bufmap->desc_array[i].is_two_2mib_chunks = false; + + /* + * We hope there was enough memory that each desc is + * covered by two THPs/folios, if not we want to keep on + * working even if there's only one page per folio. + */ + bufmap->desc_array[i].folio_array = + kzalloc_objs(struct folio *, pages_per_desc); + if (!bufmap->desc_array[i].folio_array) { + ret = -ENOMEM; + goto unpin; + } + + bufmap->desc_array[i].folio_offsets = + kzalloc_objs(size_t, pages_per_desc); + if (!bufmap->desc_array[i].folio_offsets) { + ret = -ENOMEM; + kfree(bufmap->desc_array[i].folio_array); + bufmap->desc_array[i].folio_array = NULL; + goto unpin; + } + bufmap->desc_array[i].uaddr = - (user_desc->ptr + (i * pages_per_desc * PAGE_SIZE)); - offset += pages_per_desc; + user_desc->ptr + (size_t)i * bufmap->desc_size; + + /* + * Accumulate folios until desc is full. + */ + while (desc_pages_needed > 0) { + if (remaining_pages == 0) { + /* shouldn't happen. */ + if (current_folio >= bufmap->folio_count) { + ret = -EINVAL; + goto unpin; + } + folio = bufmap->folio_array[current_folio++]; + remaining_pages = folio_nr_pages(folio); + current_offset = 0; + } else { + folio = bufmap->folio_array[current_folio - 1]; + } + + need_avail_min = + min(desc_pages_needed, remaining_pages); + adjust_offset = need_avail_min * PAGE_SIZE; + + bufmap->desc_array[i].folio_array[desc_folio_count] = + folio; + bufmap->desc_array[i].folio_offsets[desc_folio_count] = + current_offset; + desc_folio_count++; + pages_assigned_to_this_desc += need_avail_min; + desc_pages_needed -= need_avail_min; + remaining_pages -= need_avail_min; + current_offset += adjust_offset; + } + + /* Detect optimal case: two 2MiB folios per 4MiB slot. */ + if (desc_folio_count == 2 && + folio_nr_pages(bufmap->desc_array[i].folio_array[0]) == 512 && + folio_nr_pages(bufmap->desc_array[i].folio_array[1]) == 512) { + bufmap->desc_array[i].is_two_2mib_chunks = true; + gossip_debug(GOSSIP_BUFMAP_DEBUG, "%s: descriptor :%d: " + "optimal folio/page ratio.\n", __func__, i); + } + + bufmap->desc_array[i].folio_count = desc_folio_count; + gossip_debug(GOSSIP_BUFMAP_DEBUG, + " descriptor %d: folio_count=%d, " + "pages_assigned=%d (should be %d)\n", + i, desc_folio_count, pages_assigned_to_this_desc, + pages_per_desc); + + allocated_descs = i + 1; } return 0; +unpin: + /* + * rollback any allocations we got so far... + * Memory pressure, like in generic/340, led me + * to write the rollback this way. + */ + for (j = 0; j < allocated_descs; j++) { + if (bufmap->desc_array[j].folio_array) { + kfree(bufmap->desc_array[j].folio_array); + bufmap->desc_array[j].folio_array = NULL; + } + if (bufmap->desc_array[j].folio_offsets) { + kfree(bufmap->desc_array[j].folio_offsets); + bufmap->desc_array[j].folio_offsets = NULL; + } + } + unpin_user_pages(bufmap->page_array, bufmap->page_count); + return ret; } /* @@ -292,6 +470,8 @@ orangefs_bufmap_map(struct orangefs_bufmap *bufmap, * * initializes the mapped buffer interface * + * user_desc is the parameters provided by userspace for the bufmap. + * * returns 0 on success, -errno on failure */ int orangefs_bufmap_initialize(struct ORANGEFS_dev_map_desc *user_desc) @@ -300,8 +480,8 @@ int orangefs_bufmap_initialize(struct ORANGEFS_dev_map_desc *user_desc) int ret = -EINVAL; gossip_debug(GOSSIP_BUFMAP_DEBUG, - "orangefs_bufmap_initialize: called (ptr (" - "%p) sz (%d) cnt(%d).\n", + "%s: called (ptr (" "%p) sz (%d) cnt(%d).\n", + __func__, user_desc->ptr, user_desc->size, user_desc->count); @@ -371,7 +551,7 @@ int orangefs_bufmap_initialize(struct ORANGEFS_dev_map_desc *user_desc) spin_unlock(&orangefs_bufmap_lock); gossip_debug(GOSSIP_BUFMAP_DEBUG, - "orangefs_bufmap_initialize: exiting normally\n"); + "%s: exiting normally\n", __func__); return 0; out_unmap_bufmap: @@ -471,22 +651,89 @@ int orangefs_bufmap_copy_from_iovec(struct iov_iter *iter, size_t size) { struct orangefs_bufmap_desc *to; - int i; - - gossip_debug(GOSSIP_BUFMAP_DEBUG, - "%s: buffer_index:%d: size:%zu:\n", - __func__, buffer_index, size); + size_t remaining = size; + int folio_index = 0; + struct folio *folio; + size_t folio_offset; + size_t folio_avail; + size_t copy_amount; + size_t copied; + void *kaddr; + size_t half; + size_t first; + size_t second; to = &__orangefs_bufmap->desc_array[buffer_index]; - for (i = 0; size; i++) { - struct page *page = to->page_array[i]; - size_t n = size; - if (n > PAGE_SIZE) - n = PAGE_SIZE; - if (copy_page_from_iter(page, 0, n, iter) != n) + + /* shouldn't happen... */ + if (size > 4194304) + pr_info("%s: size:%zu\n", __func__, size); + + gossip_debug(GOSSIP_BUFMAP_DEBUG, + "%s: buffer_index:%d size:%zu folio_count:%d\n", + __func__, + buffer_index, + size, + to->folio_count); + + /* Fast path: exactly two 2 MiB folios */ + if (to->is_two_2mib_chunks && size <= 4194304) { + gossip_debug(GOSSIP_BUFMAP_DEBUG, + "%s: fastpath hit.\n", __func__); + half = 2097152; /* 2 MiB */ + first = min(size, half); + second = (size > half) ? size - half : 0; + + /* First 2 MiB chunk */ + kaddr = kmap_local_folio(to->folio_array[0], 0); + copied = copy_from_iter(kaddr, first, iter); + kunmap_local(kaddr); + if (copied != first) return -EFAULT; - size -= n; + + if (second == 0) + return 0; + + /* Second 2 MiB chunk */ + kaddr = kmap_local_folio(to->folio_array[1], 0); + copied = copy_from_iter(kaddr, second, iter); + kunmap_local(kaddr); + if (copied != second) + return -EFAULT; + + return 0; } + + while (remaining > 0) { + + if (unlikely(folio_index >= to->folio_count || + to->folio_array[folio_index] == NULL)) { + gossip_err("%s: " + "folio_index:%d: >= folio_count:%d: " + "(size %zu, buffer %d)\n", + __func__, + folio_index, + to->folio_count, + size, + buffer_index); + return -EFAULT; + } + + folio = to->folio_array[folio_index]; + folio_offset = to->folio_offsets[folio_index]; + folio_avail = folio_nr_pages(folio) * PAGE_SIZE - folio_offset; + copy_amount = min(remaining, folio_avail); + kaddr = kmap_local_folio(folio, folio_offset); + copied = copy_from_iter(kaddr, copy_amount, iter); + kunmap_local(kaddr); + + if (copied != copy_amount) + return -EFAULT; + + remaining -= copied; + folio_index++; + } + return 0; } @@ -499,23 +746,91 @@ int orangefs_bufmap_copy_to_iovec(struct iov_iter *iter, size_t size) { struct orangefs_bufmap_desc *from; - int i; + size_t remaining = size; + int folio_index = 0; + struct folio *folio; + size_t folio_offset; + size_t folio_avail; + size_t copy_amount; + size_t copied; + void *kaddr; + size_t half; + size_t first; + size_t second; from = &__orangefs_bufmap->desc_array[buffer_index]; + + /* shouldn't happen... */ + if (size > 4194304) + pr_info("%s: size:%zu\n", __func__, size); + gossip_debug(GOSSIP_BUFMAP_DEBUG, - "%s: buffer_index:%d: size:%zu:\n", - __func__, buffer_index, size); + "%s: buffer_index:%d size:%zu folio_count:%d\n", + __func__, + buffer_index, + size, + from->folio_count); + /* Fast path: exactly two 2 MiB folios */ + if (from->is_two_2mib_chunks && size <= 4194304) { + gossip_debug(GOSSIP_BUFMAP_DEBUG, + "%s: fastpath hit.\n", __func__); + half = 2097152; /* 2 MiB */ + first = min(size, half); + second = (size > half) ? size - half : 0; + void *kaddr; + size_t copied; - for (i = 0; size; i++) { - struct page *page = from->page_array[i]; - size_t n = size; - if (n > PAGE_SIZE) - n = PAGE_SIZE; - n = copy_page_to_iter(page, 0, n, iter); - if (!n) + /* First 2 MiB chunk */ + kaddr = kmap_local_folio(from->folio_array[0], 0); + copied = copy_to_iter(kaddr, first, iter); + kunmap_local(kaddr); + if (copied != first) return -EFAULT; - size -= n; + + if (second == 0) + return 0; + + /* Second 2 MiB chunk */ + kaddr = kmap_local_folio(from->folio_array[1], 0); + copied = copy_to_iter(kaddr, second, iter); + kunmap_local(kaddr); + if (copied != second) + return -EFAULT; + + return 0; } + + while (remaining > 0) { + + if (unlikely(folio_index >= from->folio_count || + from->folio_array[folio_index] == NULL)) { + gossip_err("%s: " + "folio_index:%d: >= folio_count:%d: " + "(size %zu, buffer %d)\n", + __func__, + folio_index, + from->folio_count, + size, + buffer_index); + return -EFAULT; + } + + folio = from->folio_array[folio_index]; + folio_offset = from->folio_offsets[folio_index]; + folio_avail = folio_nr_pages(folio) * PAGE_SIZE - folio_offset; + copy_amount = min(remaining, folio_avail); + + kaddr = kmap_local_folio(folio, folio_offset); + copied = copy_to_iter(kaddr, copy_amount, iter); + kunmap_local(kaddr); + + if (copied != copy_amount) + return -EFAULT; + + remaining -= copied; + folio_index++; + } + return 0; } diff --git a/fs/orangefs/orangefs-cache.c b/fs/orangefs/orangefs-cache.c index e75e173a9186..0bdb99e89744 100644 --- a/fs/orangefs/orangefs-cache.c +++ b/fs/orangefs/orangefs-cache.c @@ -19,10 +19,14 @@ static struct kmem_cache *op_cache; int op_cache_initialize(void) { - op_cache = kmem_cache_create("orangefs_op_cache", + op_cache = kmem_cache_create_usercopy("orangefs_op_cache", sizeof(struct orangefs_kernel_op_s), 0, 0, + offsetof(struct orangefs_kernel_op_s, tag), + offsetof(struct orangefs_kernel_op_s, upcall) + + sizeof(struct orangefs_upcall_s) - + offsetof(struct orangefs_kernel_op_s, tag), NULL); if (!op_cache) { diff --git a/fs/orangefs/orangefs-debugfs.c b/fs/orangefs/orangefs-debugfs.c index e82b934ed074..69bd73a2b556 100644 --- a/fs/orangefs/orangefs-debugfs.c +++ b/fs/orangefs/orangefs-debugfs.c @@ -238,12 +238,10 @@ void orangefs_debugfs_init(int debug_mask) static void orangefs_kernel_debug_init(void) { static char k_buffer[ORANGEFS_MAX_DEBUG_STRING_LEN] = { }; - size_t len = strlen(kernel_debug_string); + size_t len = + strscpy(k_buffer, kernel_debug_string, sizeof(k_buffer) - 1); - gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: start\n", __func__); - - if (len + 1 < ORANGEFS_MAX_DEBUG_STRING_LEN) { - memcpy(k_buffer, kernel_debug_string, len); + if (len > 0) { k_buffer[len] = '\n'; k_buffer[len + 1] = '\0'; } else { @@ -339,12 +337,10 @@ static int help_show(struct seq_file *m, void *v) static void orangefs_client_debug_init(void) { static char c_buffer[ORANGEFS_MAX_DEBUG_STRING_LEN] = { }; - size_t len = strlen(client_debug_string); + size_t len = + strscpy(c_buffer, client_debug_string, sizeof(c_buffer) - 1); - gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: start\n", __func__); - - if (len + 1 < ORANGEFS_MAX_DEBUG_STRING_LEN) { - memcpy(c_buffer, client_debug_string, len); + if (len > 0) { c_buffer[len] = '\n'; c_buffer[len + 1] = '\0'; } else { @@ -443,7 +439,7 @@ static ssize_t orangefs_debug_write(struct file *file, count = ORANGEFS_MAX_DEBUG_STRING_LEN; } - buf = memdup_user_nul(ubuf, count - 1); + buf = memdup_user_nul(ubuf, count); if (IS_ERR(buf)) { gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: memdup_user_nul failed!\n", @@ -452,6 +448,7 @@ static ssize_t orangefs_debug_write(struct file *file, buf = NULL; goto out; } + strim(buf); /* * Map the keyword string from userspace into a valid debug mask. @@ -873,9 +870,10 @@ out: */ static void debug_string_to_mask(char *debug_string, void *mask, int type) { - char *unchecked_keyword; int i; char *strsep_fodder = kstrdup(debug_string, GFP_KERNEL); + char *trimmed; + char *token; char *original_pointer; int element_count = 0; struct client_debug_mask *c_mask = NULL; @@ -893,18 +891,17 @@ static void debug_string_to_mask(char *debug_string, void *mask, int type) } original_pointer = strsep_fodder; - while ((unchecked_keyword = strsep(&strsep_fodder, ","))) - if (strlen(unchecked_keyword)) { + while ((token = strsep(&strsep_fodder, ",")) != NULL) { + trimmed = strim(token); + if (*trimmed) { for (i = 0; i < element_count; i++) if (type) - do_c_mask(i, - unchecked_keyword, - &c_mask); + do_c_mask(i, trimmed, &c_mask); else - do_k_mask(i, - unchecked_keyword, - &k_mask); + do_k_mask(i, trimmed, &k_mask); + } + } kfree(original_pointer); } diff --git a/fs/orangefs/xattr.c b/fs/orangefs/xattr.c index 1b372189cd10..b6d116302de4 100644 --- a/fs/orangefs/xattr.c +++ b/fs/orangefs/xattr.c @@ -188,6 +188,10 @@ ssize_t orangefs_inode_getxattr(struct inode *inode, const char *name, * Length returned includes null terminator. */ length = new_op->downcall.resp.getxattr.val_sz; + if (length < 0 || length > ORANGEFS_MAX_XATTR_VALUELEN) { + ret = -EIO; + goto out_release_op; + } /* * Just return the length of the queried attribute. diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 5ef48d22bdf9..a033743dbf51 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -919,7 +919,7 @@ static void ovl_drop_nlink(struct dentry *dentry) /* Try to find another, hashed alias */ spin_lock(&inode->i_lock); - hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) { + for_each_alias(alias, inode) { if (alias != dentry && !d_unhashed(alias)) break; } diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 7b86a6bac644..b41f4788e4f0 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -1354,7 +1354,7 @@ int ovl_ensure_verity_loaded(const struct path *datapath) struct inode *inode = d_inode(datapath->dentry); struct file *filp; - if (!fsverity_active(inode) && IS_VERITY(inode)) { + if (IS_VERITY(inode) && fsverity_get_info(inode) == NULL) { /* * If this inode was not yet opened, the verity info hasn't been * loaded yet, so we need to do that here to force it into memory. diff --git a/fs/proc/array.c b/fs/proc/array.c index f447e734612a..90fb0c6b5f99 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -280,7 +280,7 @@ static inline void task_sig(struct seq_file *m, struct task_struct *p) blocked = p->blocked; collect_sigign_sigcatch(p, &ignored, &caught); num_threads = get_nr_threads(p); - rcu_read_lock(); /* FIXME: is this correct? */ + rcu_read_lock(); qsize = get_rlimit_value(task_ucounts(p), UCOUNT_RLIMIT_SIGPENDING); rcu_read_unlock(); qlim = task_rlimit(p, RLIMIT_SIGPENDING); diff --git a/fs/smb/Kconfig b/fs/smb/Kconfig index b4b2cfdc2a6b..e549e189ee6a 100644 --- a/fs/smb/Kconfig +++ b/fs/smb/Kconfig @@ -4,7 +4,7 @@ source "fs/smb/client/Kconfig" source "fs/smb/server/Kconfig" -source "fs/smb/common/smbdirect/Kconfig" +source "fs/smb/smbdirect/Kconfig" config SMBFS tristate diff --git a/fs/smb/Makefile b/fs/smb/Makefile index 9a1bf59a1a65..353b1c2eefc4 100644 --- a/fs/smb/Makefile +++ b/fs/smb/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_SMBFS) += common/ +obj-$(CONFIG_SMBDIRECT) += smbdirect/ obj-$(CONFIG_CIFS) += client/ obj-$(CONFIG_SMB_SERVER) += server/ diff --git a/fs/smb/client/Kconfig b/fs/smb/client/Kconfig index 63831242fddf..2b7db5fb0fd9 100644 --- a/fs/smb/client/Kconfig +++ b/fs/smb/client/Kconfig @@ -5,11 +5,11 @@ config CIFS select NLS select NLS_UCS2_UTILS select CRYPTO - select CRYPTO_CMAC select CRYPTO_AEAD2 select CRYPTO_CCM select CRYPTO_GCM select CRYPTO_AES + select CRYPTO_LIB_AES_CBC_MACS select CRYPTO_LIB_ARC4 select CRYPTO_LIB_MD5 select CRYPTO_LIB_SHA256 @@ -182,7 +182,7 @@ config CIFS_SMB_DIRECT bool "SMB Direct support" depends on CIFS && INFINIBAND && INFINIBAND_ADDR_TRANS depends on CIFS=m || INFINIBAND=y - select SMB_COMMON_SMBDIRECT + select SMBDIRECT help Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. SMB Direct allows transferring SMB packets over RDMA. If unsure, diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index 04bb95091f49..88d5e9a32f28 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -286,6 +286,14 @@ replay_again: &rqst[0], &oplock, &oparms, utf16_path); if (rc) goto oshr_free; + + if (oplock != SMB2_OPLOCK_LEVEL_II) { + rc = -EINVAL; + cifs_dbg(FYI, "%s: Oplock level %d not suitable for cached directory\n", + __func__, oplock); + goto oshr_free; + } + smb2_set_next_command(tcon, &rqst[0]); memset(&qi_iov, 0, sizeof(qi_iov)); @@ -593,7 +601,7 @@ done: * Invalidate all cached dirs when a TCON has been reset * due to a session loss. */ -void invalidate_all_cached_dirs(struct cifs_tcon *tcon) +void invalidate_all_cached_dirs(struct cifs_tcon *tcon, bool sync) { struct cached_fids *cfids = tcon->cfids; struct cached_fid *cfid, *q; @@ -625,7 +633,8 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon) /* run laundromat unconditionally now as there might have been previously queued work */ mod_delayed_work(cfid_put_wq, &cfids->laundromat_work, 0); - flush_delayed_work(&cfids->laundromat_work); + if (sync) + flush_delayed_work(&cfids->laundromat_work); } static void diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h index 19d5592512e4..fc756836da95 100644 --- a/fs/smb/client/cached_dir.h +++ b/fs/smb/client/cached_dir.h @@ -90,7 +90,7 @@ void close_cached_dir(struct cached_fid *cfid); void drop_cached_dir_by_name(const unsigned int xid, struct cifs_tcon *tcon, const char *name, struct cifs_sb_info *cifs_sb); void close_all_cached_dirs(struct cifs_sb_info *cifs_sb); -void invalidate_all_cached_dirs(struct cifs_tcon *tcon); +void invalidate_all_cached_dirs(struct cifs_tcon *tcon, bool sync); bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]); #endif /* _CACHED_DIR_H */ diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c index 0691d2a3e04b..4ed4f55a0bb7 100644 --- a/fs/smb/client/cifs_debug.c +++ b/fs/smb/client/cifs_debug.c @@ -306,6 +306,9 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v) LIST_HEAD(entry); seq_puts(m, "# Version:1\n"); +#ifdef CONFIG_CIFS_DEBUG + seq_puts(m, "# Write 0 to this file to drop all cached directory entries\n"); +#endif /* CONFIG_CIFS_DEBUG */ seq_puts(m, "# Format:\n"); seq_puts(m, "# \n"); @@ -353,6 +356,51 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v) return 0; } +#ifdef CONFIG_CIFS_DEBUG +static int cifs_debug_dirs_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifs_debug_dirs_proc_show, NULL); +} + +/* Drop all cached directory entries across all CIFS mounts. */ +static ssize_t cifs_debug_dirs_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + int rc, v; + + rc = kstrtoint_from_user(buffer, count, 10, &v); + if (rc) + return rc; + + if (v == 0) { + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + if (cifs_ses_exiting(ses)) + continue; + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) + invalidate_all_cached_dirs(tcon, false); + } + } + spin_unlock(&cifs_tcp_ses_lock); + } + + return count; +} + +static const struct proc_ops cifs_debug_dirs_proc_ops = { + .proc_open = cifs_debug_dirs_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = cifs_debug_dirs_proc_write, +}; +#endif /* CONFIG_CIFS_DEBUG */ + static __always_inline const char *compression_alg_str(__le16 alg) { switch (alg) { @@ -885,9 +933,11 @@ cifs_proc_init(void) proc_create_single("open_files", 0400, proc_fs_cifs, cifs_debug_files_proc_show); - proc_create_single("open_dirs", 0400, proc_fs_cifs, - cifs_debug_dirs_proc_show); - +#ifdef CONFIG_CIFS_DEBUG + proc_create("open_dirs", 0600, proc_fs_cifs, &cifs_debug_dirs_proc_ops); +#else /* CONFIG_CIFS_DEBUG */ + proc_create_single("open_dirs", 0400, proc_fs_cifs, cifs_debug_dirs_proc_show); +#endif /* !CONFIG_CIFS_DEBUG */ proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_ops); proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_ops); proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_ops); diff --git a/fs/smb/client/cifs_unicode.c b/fs/smb/client/cifs_unicode.c index e2edc207cef2..4a8a591f4bca 100644 --- a/fs/smb/client/cifs_unicode.c +++ b/fs/smb/client/cifs_unicode.c @@ -6,6 +6,7 @@ */ #include #include +#include #include "cifs_fs_sb.h" #include "cifs_unicode.h" #include "cifsglob.h" diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index c920039d733c..786dbbc43c5b 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -758,6 +758,77 @@ static void dump_ace(struct smb_ace *pace, char *end_of_acl) } #endif +static int validate_dacl(struct smb_acl *pdacl, char *end_of_acl) +{ + int i, ace_hdr_size, ace_size, min_ace_size; + u16 dacl_size, num_aces; + char *acl_base, *end_of_dacl; + struct smb_ace *pace; + + if (!pdacl) + return 0; + + if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl)) { + cifs_dbg(VFS, "ACL too small to parse DACL\n"); + return -EINVAL; + } + + dacl_size = le16_to_cpu(pdacl->size); + if (dacl_size < sizeof(struct smb_acl) || + end_of_acl < (char *)pdacl + dacl_size) { + cifs_dbg(VFS, "ACL too small to parse DACL\n"); + return -EINVAL; + } + + num_aces = le16_to_cpu(pdacl->num_aces); + if (!num_aces) + return 0; + + ace_hdr_size = offsetof(struct smb_ace, sid) + + offsetof(struct smb_sid, sub_auth); + min_ace_size = ace_hdr_size + sizeof(__le32); + if (num_aces > (dacl_size - sizeof(struct smb_acl)) / min_ace_size) { + cifs_dbg(VFS, "ACL too small to parse DACL\n"); + return -EINVAL; + } + + end_of_dacl = (char *)pdacl + dacl_size; + acl_base = (char *)pdacl; + ace_size = sizeof(struct smb_acl); + + for (i = 0; i < num_aces; ++i) { + if (end_of_dacl - acl_base < ace_size) { + cifs_dbg(VFS, "ACL too small to parse ACE\n"); + return -EINVAL; + } + + pace = (struct smb_ace *)(acl_base + ace_size); + acl_base = (char *)pace; + + if (end_of_dacl - acl_base < ace_hdr_size || + pace->sid.num_subauth == 0 || + pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) { + cifs_dbg(VFS, "ACL too small to parse ACE\n"); + return -EINVAL; + } + + ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth; + if (end_of_dacl - acl_base < ace_size || + le16_to_cpu(pace->size) < ace_size) { + cifs_dbg(VFS, "ACL too small to parse ACE\n"); + return -EINVAL; + } + + ace_size = le16_to_cpu(pace->size); + if (end_of_dacl - acl_base < ace_size) { + cifs_dbg(VFS, "ACL too small to parse ACE\n"); + return -EINVAL; + } + } + + return 0; +} + static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, struct smb_sid *pownersid, struct smb_sid *pgrpsid, struct cifs_fattr *fattr, bool mode_from_special_sid) @@ -777,12 +848,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, return; } - /* validate that we do not go past end of acl */ - if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) || - end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { - cifs_dbg(VFS, "ACL too small to parse DACL\n"); + if (validate_dacl(pdacl, end_of_acl)) return; - } cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n", le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), @@ -800,37 +867,19 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, if (num_aces > 0) { umode_t denied_mode = 0; - if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) / - (offsetof(struct smb_ace, sid) + - offsetof(struct smb_sid, sub_auth) + sizeof(__le16))) - return; - ppace = kmalloc_objs(struct smb_ace *, num_aces); if (!ppace) return; for (i = 0; i < num_aces; ++i) { - if (end_of_acl - acl_base < acl_size) - break; - ppace[i] = (struct smb_ace *) (acl_base + acl_size); - acl_base = (char *)ppace[i]; - acl_size = offsetof(struct smb_ace, sid) + - offsetof(struct smb_sid, sub_auth); - - if (end_of_acl - acl_base < acl_size || - ppace[i]->sid.num_subauth == 0 || - ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || - (end_of_acl - acl_base < - acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || - (le16_to_cpu(ppace[i]->size) < - acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) - break; #ifdef CONFIG_CIFS_DEBUG2 - dump_ace(ppace[i], end_of_acl); + dump_ace(ppace[i], + (char *)pdacl + le16_to_cpu(pdacl->size)); #endif if (mode_from_special_sid && + ppace[i]->sid.num_subauth >= 3 && (compare_sids(&(ppace[i]->sid), &sid_unix_NFS_mode) == 0)) { /* @@ -870,6 +919,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, (void *)ppace[i], sizeof(struct smb_ace)); */ + acl_base = (char *)ppace[i]; acl_size = le16_to_cpu(ppace[i]->size); } @@ -1214,6 +1264,17 @@ static int parse_sid(struct smb_sid *psid, char *end_of_acl) return 0; } +static bool dacl_offset_valid(unsigned int acl_len, __u32 dacloffset) +{ + if (acl_len < sizeof(struct smb_acl)) + return false; + + if (dacloffset < sizeof(struct smb_ntsd)) + return false; + + return dacloffset <= acl_len - sizeof(struct smb_acl); +} + /* Convert CIFS ACL to POSIX form */ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, @@ -1234,7 +1295,6 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, group_sid_ptr = (struct smb_sid *)((char *)pntsd + le32_to_cpu(pntsd->gsidoffset)); dacloffset = le32_to_cpu(pntsd->dacloffset); - dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), le32_to_cpu(pntsd->gsidoffset), @@ -1265,11 +1325,18 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, return rc; } - if (dacloffset) + if (dacloffset) { + if (!dacl_offset_valid(acl_len, dacloffset)) { + cifs_dbg(VFS, "Server returned illegal DACL offset\n"); + return -EINVAL; + } + + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr, fattr, get_mode_from_special_sid); - else + } else { cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */ + } return rc; } @@ -1292,11 +1359,15 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd, dacloffset = le32_to_cpu(pntsd->dacloffset); if (dacloffset) { - dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); - if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) { - cifs_dbg(VFS, "Server returned illegal ACL size\n"); + if (!dacl_offset_valid(secdesclen, dacloffset)) { + cifs_dbg(VFS, "Server returned illegal DACL offset\n"); return -EINVAL; } + + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); + rc = validate_dacl(dacl_ptr, end_of_acl); + if (rc) + return rc; } owner_sid_ptr = (struct smb_sid *)((char *)pntsd + @@ -1661,7 +1732,19 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, nsecdesclen = sizeof(struct smb_ntsd) + (sizeof(struct smb_sid) * 2); dacloffset = le32_to_cpu(pntsd->dacloffset); if (dacloffset) { + if (!dacl_offset_valid(secdesclen, dacloffset)) { + cifs_dbg(VFS, "Server returned illegal DACL offset\n"); + rc = -EINVAL; + goto id_mode_to_cifs_acl_exit; + } + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); + rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen); + if (rc) { + kfree(pntsd); + cifs_put_tlink(tlink); + return rc; + } if (mode_from_sid) nsecdesclen += le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace); @@ -1677,7 +1760,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, * descriptor parameters, and security descriptor itself */ nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN); - pnntsd = kmalloc(nsecdesclen, GFP_KERNEL); + pnntsd = kzalloc(nsecdesclen, GFP_KERNEL); if (!pnntsd) { kfree(pntsd); cifs_put_tlink(tlink); @@ -1697,6 +1780,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag); cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc); } +id_mode_to_cifs_acl_exit: cifs_put_tlink(tlink); kfree(pnntsd); diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c index 3d731f3af235..34804e9842a8 100644 --- a/fs/smb/client/cifsencrypt.c +++ b/fs/smb/client/cifsencrypt.c @@ -22,49 +22,33 @@ #include #include #include +#include #include #include #include -static int cifs_sig_update(struct cifs_calc_sig_ctx *ctx, - const u8 *data, size_t len) -{ - if (ctx->md5) { - md5_update(ctx->md5, data, len); - return 0; - } - if (ctx->hmac) { - hmac_sha256_update(ctx->hmac, data, len); - return 0; - } - return crypto_shash_update(ctx->shash, data, len); -} - -static int cifs_sig_final(struct cifs_calc_sig_ctx *ctx, u8 *out) -{ - if (ctx->md5) { - md5_final(ctx->md5, out); - return 0; - } - if (ctx->hmac) { - hmac_sha256_final(ctx->hmac, out); - return 0; - } - return crypto_shash_final(ctx->shash, out); -} - static size_t cifs_sig_step(void *iter_base, size_t progress, size_t len, void *priv, void *priv2) { struct cifs_calc_sig_ctx *ctx = priv; - int ret, *pret = priv2; - ret = cifs_sig_update(ctx, iter_base, len); - if (ret < 0) { - *pret = ret; - return len; - } - return 0; + if (ctx->md5) + md5_update(ctx->md5, iter_base, len); + else if (ctx->hmac) + hmac_sha256_update(ctx->hmac, iter_base, len); + else + aes_cmac_update(ctx->cmac, iter_base, len); + return 0; /* Return value is length *not* processed, i.e. 0. */ +} + +static void cifs_sig_final(struct cifs_calc_sig_ctx *ctx, u8 *out) +{ + if (ctx->md5) + md5_final(ctx->md5, out); + else if (ctx->hmac) + hmac_sha256_final(ctx->hmac, out); + else + aes_cmac_final(ctx->cmac, out); } /* @@ -75,9 +59,8 @@ static int cifs_sig_iter(const struct iov_iter *iter, size_t maxsize, { struct iov_iter tmp_iter = *iter; size_t did; - int err; - did = iterate_and_advance_kernel(&tmp_iter, maxsize, ctx, &err, + did = iterate_and_advance_kernel(&tmp_iter, maxsize, ctx, NULL, cifs_sig_step); if (did != maxsize) return smb_EIO2(smb_eio_trace_sig_iter, did, maxsize); @@ -108,11 +91,8 @@ int __cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, if (rc < 0) return rc; - rc = cifs_sig_final(ctx, signature); - if (rc) - cifs_dbg(VFS, "%s: Could not generate hash\n", __func__); - - return rc; + cifs_sig_final(ctx, signature); + return 0; } /* Build a proper attribute value/target info pairs blob. @@ -523,8 +503,6 @@ calc_seckey(struct cifs_ses *ses) void cifs_crypto_secmech_release(struct TCP_Server_Info *server) { - cifs_free_hash(&server->secmech.aes_cmac); - if (server->secmech.enc) { crypto_free_aead(server->secmech.enc); server->secmech.enc = NULL; diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 2025739f070a..9f76b0347fa9 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -127,43 +127,6 @@ atomic64_t cifs_dircache_bytes_used = ATOMIC64_INIT(0); atomic_t cifs_sillycounter; atomic_t cifs_tmpcounter; -/* - * Write-only module parameter to drop all cached directory entries across - * all CIFS mounts. Echo a non-zero value to trigger. - */ -static void cifs_drop_all_dir_caches(void) -{ - struct TCP_Server_Info *server; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - if (cifs_ses_exiting(ses)) - continue; - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) - invalidate_all_cached_dirs(tcon); - } - } - spin_unlock(&cifs_tcp_ses_lock); -} - -static int cifs_param_set_drop_dir_cache(const char *val, const struct kernel_param *kp) -{ - bool bv; - int rc = kstrtobool(val, &bv); - - if (rc) - return rc; - if (bv) - cifs_drop_all_dir_caches(); - return 0; -} - -module_param_call(drop_dir_cache, cifs_param_set_drop_dir_cache, NULL, NULL, 0200); -MODULE_PARM_DESC(drop_dir_cache, "Write 1 to drop all cached directory entries across all CIFS mounts"); - #ifdef CONFIG_CIFS_STATS2 unsigned int slow_rsp_threshold = 1; module_param(slow_rsp_threshold, uint, 0644); @@ -2160,7 +2123,6 @@ MODULE_DESCRIPTION MODULE_VERSION(CIFS_VERSION); MODULE_SOFTDEP("nls"); MODULE_SOFTDEP("aes"); -MODULE_SOFTDEP("cmac"); MODULE_SOFTDEP("aead2"); MODULE_SOFTDEP("ccm"); MODULE_SOFTDEP("gcm"); diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h index 18f9f93a01b4..c455b15f2778 100644 --- a/fs/smb/client/cifsfs.h +++ b/fs/smb/client/cifsfs.h @@ -10,6 +10,7 @@ #define _CIFSFS_H #include +#include #define ROOT_I 2 @@ -149,23 +150,17 @@ struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type, int flags, char *cifs_silly_fullpath(struct dentry *dentry); -#define CIFS_TMPNAME_PREFIX ".__smbfile_tmp" -#define CIFS_TMPNAME_PREFIX_LEN ((int)sizeof(CIFS_TMPNAME_PREFIX) - 1) -#define CIFS_TMPNAME_COUNTER_LEN ((int)sizeof(cifs_tmpcounter) * 2) -#define CIFS_TMPNAME_LEN \ - (CIFS_TMPNAME_PREFIX_LEN + CIFS_TMPNAME_COUNTER_LEN) +#define CIFS_TMPNAME_PREFIX ".__smbfile_tmp" +#define CIFS_TMPNAME_LEN (DNAME_INLINE_LEN - 1) -#define CIFS_SILLYNAME_PREFIX ".__smbfile_silly" -#define CIFS_SILLYNAME_PREFIX_LEN ((int)sizeof(CIFS_SILLYNAME_PREFIX) - 1) -#define CIFS_SILLYNAME_COUNTER_LEN ((int)sizeof(cifs_sillycounter) * 2) -#define CIFS_SILLYNAME_LEN \ - (CIFS_SILLYNAME_PREFIX_LEN + CIFS_SILLYNAME_COUNTER_LEN) +#define CIFS_SILLYNAME_PREFIX ".__smbfile_silly" +#define CIFS_SILLYNAME_LEN (DNAME_INLINE_LEN - 1) #ifdef CONFIG_CIFS_NFSD_EXPORT extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ /* when changing internal version - update following two lines at same time */ -#define SMB3_PRODUCT_BUILD 59 -#define CIFS_VERSION "2.59" +#define SMB3_PRODUCT_BUILD 60 +#define CIFS_VERSION "2.60" #endif /* _CIFSFS_H */ diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index ccfde157d3be..82e0adc1dabd 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -23,7 +23,6 @@ #include #include "cifs_fs_sb.h" #include "cifsacl.h" -#include #include #include "../common/smbglob.h" #include "../common/smb2pdu.h" @@ -221,10 +220,8 @@ struct session_key { char *response; }; -/* crypto hashing related structure/fields, not specific to a sec mech */ +/* encryption related structure/fields, not specific to a sec mech */ struct cifs_secmech { - struct shash_desc *aes_cmac; /* block-cipher based MAC function, for SMB3 signatures */ - struct crypto_aead *enc; /* smb3 encryption AEAD TFM (AES-CCM and AES-GCM) */ struct crypto_aead *dec; /* smb3 decryption AEAD TFM (AES-CCM and AES-GCM) */ }; @@ -2324,7 +2321,7 @@ static inline void mid_execute_callback(struct TCP_Server_Info *server, struct cifs_calc_sig_ctx { struct md5_ctx *md5; struct hmac_sha256_ctx *hmac; - struct shash_desc *shash; + struct aes_cmac_ctx *cmac; }; #define CIFS_RECONN_DELAY_SECS 30 diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h index c24c50d732e6..79d891f7df1a 100644 --- a/fs/smb/client/cifsproto.h +++ b/fs/smb/client/cifsproto.h @@ -89,7 +89,6 @@ int cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid); char *smb3_fs_context_fullpath(const struct smb3_fs_context *ctx, char dirsep); int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx); -int smb3_parse_opt(const char *options, const char *key, char **val); int cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs); bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs); int cifs_discard_remaining_data(struct TCP_Server_Info *server); @@ -351,9 +350,6 @@ int __cifs_calc_signature(struct smb_rqst *rqst, enum securityEnum cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested); -int cifs_alloc_hash(const char *name, struct shash_desc **sdesc); -void cifs_free_hash(struct shash_desc **sdesc); - int cifs_try_adding_channels(struct cifs_ses *ses); int smb3_update_ses_channels(struct cifs_ses *ses, struct TCP_Server_Info *server, diff --git a/fs/smb/client/compress.c b/fs/smb/client/compress.c index 3d1e73f5d9af..be9023f841e6 100644 --- a/fs/smb/client/compress.c +++ b/fs/smb/client/compress.c @@ -329,11 +329,7 @@ int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq, compress_s goto err_free; } - /* - * This is just overprovisioning, as the algorithm will error out if @dst reaches 7/8 - * of @slen. - */ - dlen = slen; + dlen = lz77_compressed_alloc_size(slen); dst = kvzalloc(dlen, GFP_KERNEL); if (!dst) { ret = -ENOMEM; diff --git a/fs/smb/client/compress/lz77.c b/fs/smb/client/compress/lz77.c index 96e8a8057a77..7365d0f97396 100644 --- a/fs/smb/client/compress/lz77.c +++ b/fs/smb/client/compress/lz77.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2024, SUSE LLC + * Copyright (C) 2024-2026, SUSE LLC * * Authors: Enzo Matsumiya * @@ -15,19 +15,41 @@ /* * Compression parameters. + * + * LZ77_MATCH_MAX_DIST: Farthest back a match can be from current position (can be 1 - 8K). + * LZ77_HASH_LOG: + * LZ77_HASH_SIZE: ilog2 hash size (recommended to be 13 - 18, default 15 (hash size + * 32k)). + * LZ77_RSTEP_SIZE: Number of bytes to read from input buffer for hashing and initial + * match check (default 4 bytes, this effectivelly makes this the min + * match len). + * LZ77_MSTEP_SIZE: Number of bytes to extend-compare a found match (default 8 bytes). + * LZ77_SKIP_TRIGGER: ilog2 value for adaptive skipping, i.e. to progressively skip input + * bytes when we can't find matches. Default is 4. + * Higher values (>0) will decrease compression time, but will result + * in worse compression ratio. Lower values will give better + * compression ratio (more matches found), but will increase time. */ -#define LZ77_MATCH_MIN_LEN 4 -#define LZ77_MATCH_MIN_DIST 1 -#define LZ77_MATCH_MAX_DIST SZ_1K +#define LZ77_MATCH_MAX_DIST SZ_8K #define LZ77_HASH_LOG 15 #define LZ77_HASH_SIZE (1 << LZ77_HASH_LOG) -#define LZ77_STEP_SIZE sizeof(u64) +#define LZ77_RSTEP_SIZE sizeof(u32) +#define LZ77_MSTEP_SIZE sizeof(u64) +#define LZ77_SKIP_TRIGGER 4 + +#define LZ77_PREFETCH(ptr) __builtin_prefetch((ptr), 0, 3) +#define LZ77_FLAG_MAX 32 static __always_inline u8 lz77_read8(const u8 *ptr) { return get_unaligned(ptr); } +static __always_inline u32 lz77_read32(const u32 *ptr) +{ + return get_unaligned(ptr); +} + static __always_inline u64 lz77_read64(const u64 *ptr) { return get_unaligned(ptr); @@ -48,17 +70,17 @@ static __always_inline void lz77_write32(u32 *ptr, u32 v) put_unaligned_le32(v, ptr); } -static __always_inline u32 lz77_match_len(const void *wnd, const void *cur, const void *end) +static __always_inline u32 lz77_match_len(const void *match, const void *cur, const void *end) { const void *start = cur; - u64 diff; /* Safe for a do/while because otherwise we wouldn't reach here from the main loop. */ do { - diff = lz77_read64(cur) ^ lz77_read64(wnd); + const u64 diff = lz77_read64(cur) ^ lz77_read64(match); + if (!diff) { - cur += LZ77_STEP_SIZE; - wnd += LZ77_STEP_SIZE; + cur += LZ77_MSTEP_SIZE; + match += LZ77_MSTEP_SIZE; continue; } @@ -67,15 +89,31 @@ static __always_inline u32 lz77_match_len(const void *wnd, const void *cur, cons cur += count_trailing_zeros(diff) >> 3; return (cur - start); - } while (likely(cur + LZ77_STEP_SIZE < end)); + } while (likely(cur + LZ77_MSTEP_SIZE <= end)); - while (cur < end && lz77_read8(cur++) == lz77_read8(wnd++)) - ; + /* Fallback to byte-by-byte comparison for last <8 bytes. */ + while (cur < end && lz77_read8(cur) == lz77_read8(match)) { + cur++; + match++; + } return (cur - start); } -static __always_inline void *lz77_write_match(void *dst, void **nib, u32 dist, u32 len) +/** + * lz77_encode_match() - Match encoding. + * @dst: compressed buffer + * @nib: pointer to an address in @dst + * @dist: match distance + * @len: match length + * + * Assumes all args were previously checked. + * + * Return: @dst advanced to new position + * + * Ref: MS-XCA 2.3.4 "Plain LZ77 Compression Algorithm Details" - "Processing" + */ +static __always_inline void *lz77_encode_match(void *dst, void **nib, u16 dist, u32 len) { len -= 3; dist--; @@ -84,12 +122,12 @@ static __always_inline void *lz77_write_match(void *dst, void **nib, u32 dist, u if (len < 7) { lz77_write16(dst, dist + len); - return dst + 2; + return dst + sizeof(u16); } dist |= 7; lz77_write16(dst, dist); - dst += 2; + dst += sizeof(u16); len -= 7; if (!*nib) { @@ -119,113 +157,175 @@ static __always_inline void *lz77_write_match(void *dst, void **nib, u32 dist, u if (len <= 0xffff) { lz77_write16(dst, len); - return dst + 2; + return dst + sizeof(u16); } lz77_write16(dst, 0); - dst += 2; + dst += sizeof(u16); lz77_write32(dst, len); - return dst + 4; + return dst + sizeof(u32); } -noinline int lz77_compress(const void *src, u32 slen, void *dst, u32 *dlen) +/** + * lz77_encode_literals() - Literals encoding. + * @start: where to start copying literals (uncompressed buffer) + * @end: when to stop copying (uncompressed buffer) + * @dst: compressed buffer + * @f: pointer to current flag value + * @fc: pointer to current flag count + * @fp: pointer to current flag address + * + * Batch copy literals from @start to @dst, updating flag values accordingly. + * Assumes all args were previously checked. + * + * Return: @dst advanced to new position + * + * MS-XCA 2.3.4 "Plain LZ77 Compression Algorithm Details" - "Processing" + */ +static __always_inline void *lz77_encode_literals(const void *start, const void *end, void *dst, + long *f, u32 *fc, void **fp) { - const void *srcp, *end; - void *dstp, *nib, *flag_pos; - u32 flag_count = 0; - long flag = 0; - u64 *htable; + if (start >= end) + return dst; - srcp = src; - end = src + slen; + do { + const u32 len = umin(end - start, LZ77_FLAG_MAX - *fc); + + memcpy(dst, start, len); + + dst += len; + start += len; + + *f <<= len; + *fc += len; + if (*fc == LZ77_FLAG_MAX) { + lz77_write32(*fp, *f); + *fc = 0; + *fp = dst; + dst += sizeof(u32); + } + } while (start < end); + + return dst; +} + +static __always_inline u32 lz77_hash(const u32 v) +{ + return ((v ^ 0x9E3779B9) * 0x85EBCA6B) >> (32 - LZ77_HASH_LOG); +} + +noinline int lz77_compress(const void *src, const u32 slen, void *dst, u32 *dlen) +{ + const void *srcp, *rlim, *end, *anchor; + u32 *htable, hash, flag_count = 0; + void *dstp, *nib, *flag_pos; + long flag = 0; + + /* This is probably a bug, so throw a warning. */ + if (WARN_ON_ONCE(*dlen < lz77_compressed_alloc_size(slen))) + return -EINVAL; + + srcp = anchor = src; + end = srcp + slen; /* absolute end */ + rlim = end - LZ77_MSTEP_SIZE; /* read limit (for lz77_match_len()) */ dstp = dst; - nib = NULL; flag_pos = dstp; - dstp += 4; + dstp += sizeof(u32); + nib = NULL; htable = kvcalloc(LZ77_HASH_SIZE, sizeof(*htable), GFP_KERNEL); if (!htable) return -ENOMEM; - /* Main loop. */ + LZ77_PREFETCH(srcp + LZ77_RSTEP_SIZE); + + /* + * Adjust @srcp so we don't get a false positive match on first iteration. + * Then prepare hash for first loop iteration (don't advance @srcp again). + */ + hash = lz77_hash(lz77_read32(srcp++)); + htable[hash] = 0; + hash = lz77_hash(lz77_read32(srcp)); + + /* + * Main loop. + * + * @dlen is >= lz77_compressed_alloc_size(), so run without bound-checking @dstp. + * + * This code was crafted in a way to best utilise fetch-decode-execute CPU flow. + * Any attempt to optimize it, or even organize it, can lead to huge performance loss. + */ do { - u32 dist, len = 0; - const void *wnd; - u64 hash; + const void *match, *next = srcp; + u32 len, step = 1, skip = 1U << LZ77_SKIP_TRIGGER; - hash = ((lz77_read64(srcp) << 24) * 889523592379ULL) >> (64 - LZ77_HASH_LOG); - wnd = src + htable[hash]; - htable[hash] = srcp - src; - dist = srcp - wnd; + /* Match finding (hot path -- don't change the read/check/write order). */ + do { + const u32 cur_hash = hash; - if (dist && dist < LZ77_MATCH_MAX_DIST) - len = lz77_match_len(wnd, srcp, end); + srcp = next; + next += step; - if (len < LZ77_MATCH_MIN_LEN) { - lz77_write8(dstp, lz77_read8(srcp)); + /* + * Adaptive skipping. + * + * Increment @step every (1 << LZ77_SKIP_TRIGGER, 16 in our case) bytes + * without a match. + * Reset to 1 when a match is found. + */ + step = (skip++ >> LZ77_SKIP_TRIGGER); + if (unlikely(next > rlim)) + goto out; - dstp++; - srcp++; - - flag <<= 1; - flag_count++; - if (flag_count == 32) { - lz77_write32(flag_pos, flag); - flag_count = 0; - flag_pos = dstp; - dstp += 4; - } - - continue; - } + hash = lz77_hash(lz77_read32(next)); + match = src + htable[cur_hash]; + htable[cur_hash] = srcp - src; + } while (likely(match + LZ77_MATCH_MAX_DIST < srcp) || + lz77_read32(match) != lz77_read32(srcp)); /* - * Bail out if @dstp reached >= 7/8 of @slen -- already compressed badly, not worth - * going further. + * Match found. Warm/cold path; begin parsing @srcp and writing to @dstp: + * - flush literals + * - compute match length (*) + * - encode match + * + * (*) Current minimum match length is defined by the memory read size above, so + * here we already know that we have 4 matching bytes, but it's just faster to + * redundantly compute it again in lz77_match_len() than to adjust pointers/len. */ - if (unlikely(dstp - dst >= slen - (slen >> 3))) { - *dlen = slen; - goto out; - } - - dstp = lz77_write_match(dstp, &nib, dist, len); + dstp = lz77_encode_literals(anchor, srcp, dstp, &flag, &flag_count, &flag_pos); + len = lz77_match_len(match, srcp, end); + dstp = lz77_encode_match(dstp, &nib, srcp - match, len); srcp += len; + anchor = srcp; + + LZ77_PREFETCH(srcp); flag = (flag << 1) | 1; flag_count++; - if (flag_count == 32) { + if (flag_count == LZ77_FLAG_MAX) { lz77_write32(flag_pos, flag); flag_count = 0; flag_pos = dstp; - dstp += 4; + dstp += sizeof(u32); } - } while (likely(srcp + LZ77_STEP_SIZE < end)); - while (srcp < end) { - u32 c = umin(end - srcp, 32 - flag_count); + if (unlikely(srcp > rlim)) + break; - memcpy(dstp, srcp, c); + /* Prepare for next loop. */ + hash = lz77_hash(lz77_read32(srcp)); + } while (srcp < end); +out: + dstp = lz77_encode_literals(anchor, end, dstp, &flag, &flag_count, &flag_pos); - dstp += c; - srcp += c; - - flag <<= c; - flag_count += c; - if (flag_count == 32) { - lz77_write32(flag_pos, flag); - flag_count = 0; - flag_pos = dstp; - dstp += 4; - } - } - - flag <<= (32 - flag_count); - flag |= (1 << (32 - flag_count)) - 1; + flag_count = LZ77_FLAG_MAX - flag_count; + flag <<= flag_count; + flag |= (1UL << flag_count) - 1; lz77_write32(flag_pos, flag); *dlen = dstp - dst; -out: kvfree(htable); if (*dlen < slen) diff --git a/fs/smb/client/compress/lz77.h b/fs/smb/client/compress/lz77.h index cdcb191b48a2..4e570846aefa 100644 --- a/fs/smb/client/compress/lz77.h +++ b/fs/smb/client/compress/lz77.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2024, SUSE LLC + * Copyright (C) 2024-2026, SUSE LLC * * Authors: Enzo Matsumiya * @@ -11,5 +11,33 @@ #include -int lz77_compress(const void *src, u32 slen, void *dst, u32 *dlen); +/** + * lz77_compressed_alloc_size() - Compute compressed buffer size. + * @size: uncompressed (src) size + * + * Compute allocation size for the compressed buffer based on uncompressed size. + * Accounts for metadata and overprovision for the worst case scenario. + * + * LZ77 metadata is a 4-byte flag that is written: + * - on dst begin (pos 0) + * - every 32 literals or matches + * - on end-of-stream (possibly, if last write was another flag) + * + * Worst case scenario is an all-literal compression, which means: + * metadata bytes = 4 + ((@size / 32) * 4) + 4, or, simplified, (@size >> 3) + 8 + * + * The worst case scenario rarely happens, but such overprovisioning also allows lz77_compress() + * main loop to run without ever bound checking dst, which is a huge perf improvement, while also + * being safe when compression goes bad. + * + * Return: required (*) allocation size for compressed buffer. + * + * (*) checked once in the beginning of lz77_compress() + */ +static __always_inline u32 lz77_compressed_alloc_size(const u32 size) +{ + return size + (size >> 3) + 8; +} + +int lz77_compress(const void *src, const u32 slen, void *dst, u32 *dlen); #endif /* _SMB_COMPRESS_LZ77_H */ diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index ca1bc67eb23b..dcde25da468d 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -3609,7 +3609,6 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx) server = mnt_ctx->server; ctx = mnt_ctx->fs_ctx; cifs_sb = mnt_ctx->cifs_sb; - sbflags = cifs_sb_flags(cifs_sb); /* search for existing tcon to this server share */ tcon = cifs_get_tcon(mnt_ctx->ses, ctx); @@ -3624,9 +3623,10 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx) * path (i.e., do not remap / and \ and do not map any special characters) */ if (tcon->posix_extensions) { - sbflags |= CIFS_MOUNT_POSIX_PATHS; - sbflags &= ~(CIFS_MOUNT_MAP_SFM_CHR | - CIFS_MOUNT_MAP_SPECIAL_CHR); + atomic_or(CIFS_MOUNT_POSIX_PATHS, &cifs_sb->mnt_cifs_flags); + atomic_andnot(CIFS_MOUNT_MAP_SFM_CHR | + CIFS_MOUNT_MAP_SPECIAL_CHR, + &cifs_sb->mnt_cifs_flags); } #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY @@ -3650,6 +3650,7 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx) #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ tcon->unix_ext = 0; /* server does not support them */ + sbflags = cifs_sb_flags(cifs_sb); /* do not care if a following call succeed - informational */ if (!tcon->pipe && server->ops->qfs_tcon) { server->ops->qfs_tcon(mnt_ctx->xid, tcon, cifs_sb); @@ -3674,7 +3675,6 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx) out: mnt_ctx->tcon = tcon; - atomic_set(&cifs_sb->mnt_cifs_flags, sbflags); return rc; } diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c index 6ea1ae7f7a46..e4295a5b55b3 100644 --- a/fs/smb/client/dir.c +++ b/fs/smb/client/dir.c @@ -1056,9 +1056,9 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, { struct dentry *dentry = file->f_path.dentry; struct cifs_sb_info *cifs_sb = CIFS_SB(dir); + size_t namesize = CIFS_TMPNAME_LEN + 1; char *path __free(kfree) = NULL, *name; unsigned int oflags = file->f_flags; - size_t size = CIFS_TMPNAME_LEN + 1; int retries = 0, max_retries = 16; struct TCP_Server_Info *server; struct cifs_pending_open open; @@ -1070,6 +1070,7 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, struct inode *inode; unsigned int xid; __u32 oplock; + int namelen; int rc; if (unlikely(cifs_forced_shutdown(cifs_sb))) @@ -1093,7 +1094,7 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, server->ops->new_lease_key(&fid); cifs_add_pending_open(&fid, tlink, &open); - path = alloc_parent_path(dentry, size - 1); + path = alloc_parent_path(dentry, namesize - 1); if (IS_ERR(path)) { cifs_del_pending_open(&open); rc = PTR_ERR(path); @@ -1103,16 +1104,22 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, name = path + strlen(path); do { - scnprintf(name, size, - CIFS_TMPNAME_PREFIX "%0*x", - CIFS_TMPNAME_COUNTER_LEN, - atomic_inc_return(&cifs_tmpcounter)); + /* Append tmpfile name to @path */ + namelen = scnprintf(name, namesize, CIFS_TMPNAME_PREFIX "%x", + atomic_inc_return(&cifs_tmpcounter)); rc = __cifs_do_create(dir, dentry, path, xid, tlink, oflags, mode, &oplock, &fid, NULL, &inode); if (!rc) { + rc = d_mark_tmpfile_name(file, &QSTR_LEN(name, namelen)); + if (rc) { + cifs_dbg(VFS | ONCE, "%s: failed to set filename in dentry: %d\n", + __func__, rc); + rc = -EISDIR; + iput(inode); + goto err_open; + } set_nlink(inode, 0); mark_inode_dirty(inode); - d_mark_tmpfile_name(file, &QSTR_LEN(name, size - 1)); d_instantiate(dentry, inode); break; } @@ -1168,9 +1175,7 @@ char *cifs_silly_fullpath(struct dentry *dentry) do { dput(sdentry); - scnprintf(name, namesize, - CIFS_SILLYNAME_PREFIX "%0*x", - CIFS_SILLYNAME_COUNTER_LEN, + scnprintf(name, namesize, CIFS_SILLYNAME_PREFIX "%x", atomic_inc_return(&cifs_sillycounter)); sdentry = lookup_noperm(&QSTR(name), dentry->d_parent); if (IS_ERR(sdentry)) diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index ebafc7994cb4..664a2c223089 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -393,7 +393,7 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon) } spin_unlock(&tcon->open_file_lock); - invalidate_all_cached_dirs(tcon); + invalidate_all_cached_dirs(tcon, true); spin_lock(&tcon->tc_lock); if (tcon->status == TID_IN_FILES_INVALIDATE) tcon->status = TID_NEED_TCON; @@ -1082,6 +1082,9 @@ int cifs_open(struct inode *inode, struct file *file) rc = cfile ? 0 : -ENOENT; } if (rc == 0) { + trace_smb3_open_cached(xid, tcon->tid, tcon->ses->Suid, + cfile->fid.persistent_fid, + file->f_flags, cfile->f_flags); file->private_data = cfile; spin_lock(&CIFS_I(inode)->deferred_lock); cifs_del_deferred_close(cfile); @@ -1441,6 +1444,7 @@ int cifs_close(struct inode *inode, struct file *file) struct cifsInodeInfo *cinode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_deferred_close *dclose; + struct cifs_tcon *tcon; cifs_fscache_unuse_inode_cookie(inode, file->f_mode & FMODE_WRITE); @@ -1467,6 +1471,10 @@ int cifs_close(struct inode *inode, struct file *file) cifsFileInfo_get(cfile); } else { /* Deferred close for files */ + tcon = tlink_tcon(cfile->tlink); + trace_smb3_close_cached(tcon->tid, tcon->ses->Suid, + cfile->fid.persistent_fid, + cifs_sb->ctx->closetimeo); queue_delayed_work(deferredclose_wq, &cfile->deferred, cifs_sb->ctx->closetimeo); cfile->deferred_close_scheduled = true; @@ -1630,6 +1638,9 @@ cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, continue; if (conf_lock) *conf_lock = li; + trace_smb3_lock_conflict(cfile->fid.persistent_fid, + offset, length, type, + li->offset, li->length, li->type, li->pid); return true; } return false; @@ -1711,7 +1722,7 @@ cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock) */ static int cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock, - bool wait) + bool wait, unsigned int xid) { struct cifsLockInfo *conf_lock; struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); @@ -1726,7 +1737,13 @@ try_again: lock->type, lock->flags, &conf_lock, CIFS_LOCK_OP); if (!exist && cinode->can_cache_brlcks) { + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + list_add_tail(&lock->llist, &cfile->llist->locks); + trace_smb3_lock_cached(xid, cfile->fid.persistent_fid, + tcon->tid, tcon->ses->Suid, + lock->offset, lock->length, + lock->type, 1, 0); up_write(&cinode->lock_sem); return rc; } @@ -2341,7 +2358,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, if (!lock) return -ENOMEM; - rc = cifs_lock_add_if(cfile, lock, wait_flag); + rc = cifs_lock_add_if(cfile, lock, wait_flag, xid); if (rc < 0) { kfree(lock); return rc; diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c index b9544eb0381b..4c8b1b9ade8b 100644 --- a/fs/smb/client/fs_context.c +++ b/fs/smb/client/fs_context.c @@ -536,37 +536,6 @@ cifs_parse_smb_version(struct fs_context *fc, char *value, struct smb3_fs_contex return 0; } -int smb3_parse_opt(const char *options, const char *key, char **val) -{ - int rc = -ENOENT; - char *opts, *orig, *p; - - orig = opts = kstrdup(options, GFP_KERNEL); - if (!opts) - return -ENOMEM; - - while ((p = strsep(&opts, ","))) { - char *nval; - - if (!*p) - continue; - if (strncasecmp(p, key, strlen(key))) - continue; - nval = strchr(p, '='); - if (nval) { - if (nval == p) - continue; - *nval++ = 0; - *val = kstrdup(nval, GFP_KERNEL); - rc = !*val ? -ENOMEM : 0; - goto out; - } - } -out: - kfree(orig); - return rc; -} - /* * Remove duplicate path delimiters. Windows is supposed to do that * but there are some bugs that prevent rename from working if there are @@ -767,7 +736,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, static int smb3_fs_context_parse_monolithic(struct fs_context *fc, void *data); static int smb3_get_tree(struct fs_context *fc); -static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channels); +static void smb3_sync_ses_chan_max(struct cifs_ses *ses, size_t max_channels); static int smb3_reconfigure(struct fs_context *fc); static const struct fs_context_operations smb3_fs_context_ops = { @@ -1041,25 +1010,34 @@ do { \ int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) { + char *password = NULL, *password2 = NULL; + if (ses->password && cifs_sb->ctx->password && strcmp(ses->password, cifs_sb->ctx->password)) { - kfree_sensitive(cifs_sb->ctx->password); - cifs_sb->ctx->password = kstrdup(ses->password, GFP_KERNEL); - if (!cifs_sb->ctx->password) + password = kstrdup(ses->password, GFP_KERNEL); + if (!password) return -ENOMEM; } if (ses->password2 && cifs_sb->ctx->password2 && strcmp(ses->password2, cifs_sb->ctx->password2)) { - kfree_sensitive(cifs_sb->ctx->password2); - cifs_sb->ctx->password2 = kstrdup(ses->password2, GFP_KERNEL); - if (!cifs_sb->ctx->password2) { - kfree_sensitive(cifs_sb->ctx->password); - cifs_sb->ctx->password = NULL; + password2 = kstrdup(ses->password2, GFP_KERNEL); + if (!password2) { + kfree_sensitive(password); return -ENOMEM; } } + + if (password) { + kfree_sensitive(cifs_sb->ctx->password); + cifs_sb->ctx->password = password; + } + if (password2) { + kfree_sensitive(cifs_sb->ctx->password2); + cifs_sb->ctx->password2 = password2; + } + return 0; } @@ -1072,7 +1050,7 @@ int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_se * with the session's channel lock. This should be called whenever the maximum * allowed channels for a session changes (e.g., after a remount or reconfigure). */ -static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channels) +static void smb3_sync_ses_chan_max(struct cifs_ses *ses, size_t max_channels) { spin_lock(&ses->chan_lock); ses->chan_max = max_channels; @@ -1082,12 +1060,15 @@ static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channe static int smb3_reconfigure(struct fs_context *fc) { struct smb3_fs_context *ctx = smb3_fc2context(fc); + struct smb3_fs_context *new_ctx = NULL; + struct smb3_fs_context *old_ctx = NULL; struct dentry *root = fc->root; struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses; unsigned int rsize = ctx->rsize, wsize = ctx->wsize; char *new_password = NULL, *new_password2 = NULL; bool need_recon = false; + bool need_mchan_update; int rc; if (ses->expired_pwd) @@ -1097,6 +1078,16 @@ static int smb3_reconfigure(struct fs_context *fc) if (rc) return rc; + old_ctx = kzalloc_obj(*old_ctx); + if (!old_ctx) + return -ENOMEM; + + rc = smb3_fs_context_dup(old_ctx, cifs_sb->ctx); + if (rc) { + kfree(old_ctx); + return rc; + } + /* * We can not change UNC/username/password/domainname/ * workstation_name/nodename/iocharset @@ -1106,16 +1097,22 @@ static int smb3_reconfigure(struct fs_context *fc) STEAL_STRING(cifs_sb, ctx, UNC); STEAL_STRING(cifs_sb, ctx, source); STEAL_STRING(cifs_sb, ctx, username); + STEAL_STRING(cifs_sb, ctx, domainname); + STEAL_STRING(cifs_sb, ctx, nodename); + STEAL_STRING(cifs_sb, ctx, iocharset); - if (need_recon == false) + if (!need_recon) { STEAL_STRING_SENSITIVE(cifs_sb, ctx, password); - else { + } else { if (ctx->password) { new_password = kstrdup(ctx->password, GFP_KERNEL); - if (!new_password) - return -ENOMEM; - } else + if (!new_password) { + rc = -ENOMEM; + goto restore_ctx; + } + } else { STEAL_STRING_SENSITIVE(cifs_sb, ctx, password); + } } /* @@ -1125,11 +1122,29 @@ static int smb3_reconfigure(struct fs_context *fc) if (ctx->password2) { new_password2 = kstrdup(ctx->password2, GFP_KERNEL); if (!new_password2) { - kfree_sensitive(new_password); - return -ENOMEM; + rc = -ENOMEM; + goto restore_ctx; } - } else + } else { STEAL_STRING_SENSITIVE(cifs_sb, ctx, password2); + } + + /* if rsize or wsize not passed in on remount, use previous values */ + ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize; + ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize; + + new_ctx = kzalloc_obj(*new_ctx); + if (!new_ctx) { + rc = -ENOMEM; + goto restore_ctx; + } + + rc = smb3_fs_context_dup(new_ctx, ctx); + if (rc) + goto restore_ctx; + + need_mchan_update = ctx->multichannel != cifs_sb->ctx->multichannel || + ctx->max_channels != cifs_sb->ctx->max_channels; /* * we may update the passwords in the ses struct below. Make sure we do @@ -1140,54 +1155,55 @@ static int smb3_reconfigure(struct fs_context *fc) /* * smb2_reconnect may swap password and password2 in case session setup * failed. First get ctx passwords in sync with ses passwords. It should - * be okay to do this even if this function were to return an error at a - * later stage + * be done before committing new passwords. */ rc = smb3_sync_session_ctx_passwords(cifs_sb, ses); if (rc) { mutex_unlock(&ses->session_mutex); - kfree_sensitive(new_password); - kfree_sensitive(new_password2); - return rc; - } - - /* - * now that allocations for passwords are done, commit them - */ - if (new_password) { - kfree_sensitive(ses->password); - ses->password = new_password; - } - if (new_password2) { - kfree_sensitive(ses->password2); - ses->password2 = new_password2; + goto cleanup_new_ctx; } /* * If multichannel or max_channels has changed, update the session's channels accordingly. * This may add or remove channels to match the new configuration. */ - if ((ctx->multichannel != cifs_sb->ctx->multichannel) || - (ctx->max_channels != cifs_sb->ctx->max_channels)) { - - /* Synchronize ses->chan_max with the new mount context */ - smb3_sync_ses_chan_max(ses, ctx->max_channels); - /* Now update the session's channels to match the new configuration */ + if (need_mchan_update) { /* Prevent concurrent scaling operations */ spin_lock(&ses->ses_lock); if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) { spin_unlock(&ses->ses_lock); mutex_unlock(&ses->session_mutex); - return -EINVAL; + rc = -EINVAL; + goto cleanup_new_ctx; } ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS; spin_unlock(&ses->ses_lock); + } + + /* + * Commit session passwords before any channel work so newly added + * channels authenticate with the new credentials. + */ + if (new_password) { + kfree_sensitive(ses->password); + ses->password = new_password; + new_password = NULL; + } + if (new_password2) { + kfree_sensitive(ses->password2); + ses->password2 = new_password2; + new_password2 = NULL; + } + + if (need_mchan_update) { + /* Synchronize ses->chan_max with the new mount context */ + smb3_sync_ses_chan_max(ses, ctx->max_channels); mutex_unlock(&ses->session_mutex); - rc = smb3_update_ses_channels(ses, ses->server, - false /* from_reconnect */, - false /* disable_mchan */); + smb3_update_ses_channels(ses, ses->server, + false /* from_reconnect */, + false /* disable_mchan */); /* Clear scaling flag after operation */ spin_lock(&ses->ses_lock); @@ -1197,22 +1213,30 @@ static int smb3_reconfigure(struct fs_context *fc) mutex_unlock(&ses->session_mutex); } - STEAL_STRING(cifs_sb, ctx, domainname); - STEAL_STRING(cifs_sb, ctx, nodename); - STEAL_STRING(cifs_sb, ctx, iocharset); - - /* if rsize or wsize not passed in on remount, use previous values */ - ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize; - ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize; - smb3_cleanup_fs_context_contents(cifs_sb->ctx); - rc = smb3_fs_context_dup(cifs_sb->ctx, ctx); + memcpy(cifs_sb->ctx, new_ctx, sizeof(*new_ctx)); + kfree(new_ctx); + new_ctx = NULL; + smb3_cleanup_fs_context(old_ctx); + old_ctx = NULL; smb3_update_mnt_flags(cifs_sb); #ifdef CONFIG_CIFS_DFS_UPCALL if (!rc) rc = dfs_cache_remount_fs(cifs_sb); #endif + return rc; + +cleanup_new_ctx: + smb3_cleanup_fs_context_contents(new_ctx); +restore_ctx: + kfree(new_ctx); + kfree_sensitive(new_password); + kfree_sensitive(new_password2); + smb3_cleanup_fs_context_contents(cifs_sb->ctx); + memcpy(cifs_sb->ctx, old_ctx, sizeof(*old_ctx)); + kfree(old_ctx); + return rc; } diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 24040909d184..16a5310155d5 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -1595,7 +1595,7 @@ inode_has_hashed_dentries(struct inode *inode) struct dentry *dentry; spin_lock(&inode->i_lock); - hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { + for_each_alias(dentry, inode) { if (!d_unhashed(dentry) || IS_ROOT(dentry)) { spin_unlock(&inode->i_lock); return true; diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c index 9afab3237e54..17408bb8ab65 100644 --- a/fs/smb/client/ioctl.c +++ b/fs/smb/client/ioctl.c @@ -296,7 +296,7 @@ search_end: break; case SMB2_ENCRYPTION_AES256_CCM: case SMB2_ENCRYPTION_AES256_GCM: - out.session_key_length = CIFS_SESS_KEY_SIZE; + out.session_key_length = ses->auth_key.len; out.server_in_key_length = out.server_out_key_length = SMB3_GCM256_CRYPTKEY_SIZE; break; default: diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index 2aff1cab6c31..0c54b9b79a2c 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -785,63 +785,6 @@ parse_DFS_referrals_exit: return rc; } -/** - * cifs_alloc_hash - allocate hash and hash context together - * @name: The name of the crypto hash algo - * @sdesc: SHASH descriptor where to put the pointer to the hash TFM - * - * The caller has to make sure @sdesc is initialized to either NULL or - * a valid context. It can be freed via cifs_free_hash(). - */ -int -cifs_alloc_hash(const char *name, struct shash_desc **sdesc) -{ - int rc = 0; - struct crypto_shash *alg = NULL; - - if (*sdesc) - return 0; - - alg = crypto_alloc_shash(name, 0, 0); - if (IS_ERR(alg)) { - cifs_dbg(VFS, "Could not allocate shash TFM '%s'\n", name); - rc = PTR_ERR(alg); - *sdesc = NULL; - return rc; - } - - *sdesc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(alg), GFP_KERNEL); - if (*sdesc == NULL) { - cifs_dbg(VFS, "no memory left to allocate shash TFM '%s'\n", name); - crypto_free_shash(alg); - return -ENOMEM; - } - - (*sdesc)->tfm = alg; - return 0; -} - -/** - * cifs_free_hash - free hash and hash context together - * @sdesc: Where to find the pointer to the hash TFM - * - * Freeing a NULL descriptor is safe. - */ -void -cifs_free_hash(struct shash_desc **sdesc) -{ - if (unlikely(!sdesc) || !*sdesc) - return; - - if ((*sdesc)->tfm) { - crypto_free_shash((*sdesc)->tfm); - (*sdesc)->tfm = NULL; - } - - kfree_sensitive(*sdesc); - *sdesc = NULL; -} - void extract_unc_hostname(const char *unc, const char **h, size_t *len) { const char *end; diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c index 698bd27119ae..de2012cc9cf3 100644 --- a/fs/smb/client/sess.c +++ b/fs/smb/client/sess.c @@ -595,17 +595,6 @@ cifs_ses_add_channel(struct cifs_ses *ses, spin_unlock(&ses->chan_lock); mutex_lock(&ses->session_mutex); - /* - * We need to allocate the server crypto now as we will need - * to sign packets before we generate the channel signing key - * (we sign with the session key) - */ - rc = smb3_crypto_shash_allocate(chan->server); - if (rc) { - cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); - mutex_unlock(&ses->session_mutex); - goto out; - } rc = cifs_negotiate_protocol(xid, ses, chan->server); if (!rc) diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c index 9694117050a6..e198e3dda917 100644 --- a/fs/smb/client/smb1ops.c +++ b/fs/smb/client/smb1ops.c @@ -49,7 +49,6 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, if (!CIFSSMBQFSUnixInfo(xid, tcon)) { __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - unsigned int sbflags; cifs_dbg(FYI, "unix caps which server supports %lld\n", cap); /* @@ -76,29 +75,27 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) cifs_dbg(VFS, "per-share encryption not supported yet\n"); - if (cifs_sb) - sbflags = cifs_sb_flags(cifs_sb); - cap &= CIFS_UNIX_CAP_MASK; if (ctx && ctx->no_psx_acl) cap &= ~CIFS_UNIX_POSIX_ACL_CAP; else if (CIFS_UNIX_POSIX_ACL_CAP & cap) { cifs_dbg(FYI, "negotiated posix acl support\n"); - if (cifs_sb) - sbflags |= CIFS_MOUNT_POSIXACL; + if (cifs_sb) { + atomic_or(CIFS_MOUNT_POSIXACL, + &cifs_sb->mnt_cifs_flags); + } } if (ctx && ctx->posix_paths == 0) cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { cifs_dbg(FYI, "negotiate posix pathnames\n"); - if (cifs_sb) - sbflags |= CIFS_MOUNT_POSIX_PATHS; + if (cifs_sb) { + atomic_or(CIFS_MOUNT_POSIX_PATHS, + &cifs_sb->mnt_cifs_flags); + } } - if (cifs_sb) - atomic_set(&cifs_sb->mnt_cifs_flags, sbflags); - cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap); #ifdef CONFIG_CIFS_DEBUG2 if (cap & CIFS_UNIX_FCNTL_CAP) diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c index b292aa94a593..6860eff31693 100644 --- a/fs/smb/client/smb2file.c +++ b/fs/smb/client/smb2file.c @@ -49,6 +49,9 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov) __func__, le32_to_cpu(p->ErrorId)); len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8); + if (len > end - ((u8 *)p + sizeof(*p))) + return ERR_PTR(-EINVAL); + p = (struct smb2_error_context_rsp *)(p->ErrorContextData + len); } } else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) && diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index c6dd282fc3a9..6c9c229b91f6 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -111,7 +111,7 @@ static int check_wsl_eas(struct kvec *rsp_iov) u32 outlen, next; u16 vlen; u8 nlen; - u8 *end; + u8 *ea_end, *iov_end; outlen = le32_to_cpu(rsp->OutputBufferLength); if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE || @@ -120,15 +120,19 @@ static int check_wsl_eas(struct kvec *rsp_iov) ea = (void *)((u8 *)rsp_iov->iov_base + le16_to_cpu(rsp->OutputBufferOffset)); - end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len; + ea_end = (u8 *)ea + outlen; + iov_end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len; + if (ea_end > iov_end) + return -EINVAL; + for (;;) { - if ((u8 *)ea > end - sizeof(*ea)) + if ((u8 *)ea > ea_end - sizeof(*ea)) return -EINVAL; nlen = ea->ea_name_length; vlen = le16_to_cpu(ea->ea_value_length); if (nlen != SMB2_WSL_XATTR_NAME_LEN || - (u8 *)ea->ea_data + nlen + 1 + vlen > end) + (u8 *)ea->ea_data + nlen + 1 + vlen > ea_end) return -EINVAL; switch (vlen) { @@ -230,7 +234,7 @@ replay_again: num_rqst = 0; server = cifs_pick_channel(ses); - vars = kzalloc_obj(*vars, GFP_ATOMIC); + vars = kzalloc_obj(*vars, GFP_KERNEL); if (vars == NULL) { rc = -ENOMEM; goto out; diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c index 973fce3c959c..2a7355ce1a07 100644 --- a/fs/smb/client/smb2misc.c +++ b/fs/smb/client/smb2misc.c @@ -241,7 +241,8 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, if (len != calc_len) { /* create failed on symlink */ if (command == SMB2_CREATE_HE && - shdr->Status == STATUS_STOPPED_ON_SYMLINK) + shdr->Status == STATUS_STOPPED_ON_SYMLINK && + len > calc_len) return 0; /* Windows 7 server returns 24 bytes more */ if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 509fcea28a42..3738204984f5 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -111,10 +111,21 @@ smb2_add_credits(struct TCP_Server_Info *server, cifs_trace_rw_credits_zero_in_flight); } server->in_flight--; + + /* + * Rebalance credits when an op drains in_flight. For session setup, + * do this only when the total accumulated credits are high enough (>2) + * so that a newly established secondary channel can reserve credits for + * echoes and oplocks. We expect this to happen at the end of the final + * session setup response. + */ if (server->in_flight == 0 && ((optype & CIFS_OP_MASK) != CIFS_NEG_OP) && ((optype & CIFS_OP_MASK) != CIFS_SESS_OP)) rc = change_conf(server); + else if (server->in_flight == 0 && + ((optype & CIFS_OP_MASK) == CIFS_SESS_OP) && *val > 2) + rc = change_conf(server); /* * Sometimes server returns 0 credits on oplock break ack - we need to * rebalance credits in this case. @@ -1783,6 +1794,12 @@ replay_again: qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length) qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength); + if (qi.input_buffer_length > 0 && + struct_size(qi_rsp, Buffer, qi.input_buffer_length) > + rsp_iov[1].iov_len) { + rc = -EFAULT; + goto out; + } if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length, sizeof(qi.input_buffer_length))) { @@ -4704,6 +4721,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, { unsigned int data_offset; unsigned int data_len; + unsigned int end_off; unsigned int cur_off; unsigned int cur_page_idx; unsigned int pad_len; @@ -4819,7 +4837,8 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, } rdata->got_bytes = buffer_len; - } else if (buf_len >= data_offset + data_len) { + } else if (!check_add_overflow(data_offset, data_len, &end_off) && + buf_len >= end_off) { /* read response payload is in buf */ WARN_ONCE(buffer, "read data can be either in buf or in buffer"); copied = copy_to_iter(buf + data_offset, data_len, &rdata->subreq.io_iter); @@ -4943,6 +4962,14 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, goto free_dw; server->total_read += rc; + if (le32_to_cpu(tr_hdr->OriginalMessageSize) < + server->vals->read_rsp_size) { + cifs_server_dbg(VFS, "OriginalMessageSize %u too small for read response (%zu)\n", + le32_to_cpu(tr_hdr->OriginalMessageSize), + server->vals->read_rsp_size); + rc = -EINVAL; + goto discard_data; + } len = le32_to_cpu(tr_hdr->OriginalMessageSize) - server->vals->read_rsp_size; dw->len = len; diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 957aca2222b5..995fcdd30681 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -1713,17 +1713,30 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) is_binding = (ses->ses_status == SES_GOOD); spin_unlock(&ses->ses_lock); + /* + * Per MS-SMB2 3.2.5.3, Session.SessionKey is the first 16 bytes of the + * GSS cryptographic key, right-padded with zero bytes if shorter. + * Allocate at least SMB2_NTLMV2_SESSKEY_SIZE bytes (zeroed) so the KDF + * input buffer is always valid for HMAC-SHA256 even with deprecated + * Kerberos enctypes that return a short session key. + */ + if (unlikely(msg->sesskey_len < SMB2_NTLMV2_SESSKEY_SIZE)) + cifs_dbg(VFS, + "short GSS session key (%u bytes); zero-padding per MS-SMB2 3.2.5.3\n", + msg->sesskey_len); + kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = kmemdup(msg->data, - msg->sesskey_len, - GFP_KERNEL); + ses->auth_key.len = max_t(unsigned int, msg->sesskey_len, + SMB2_NTLMV2_SESSKEY_SIZE); + ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL); if (!ses->auth_key.response) { cifs_dbg(VFS, "%s: can't allocate (%u bytes) memory\n", - __func__, msg->sesskey_len); + __func__, ses->auth_key.len); + ses->auth_key.len = 0; rc = -ENOMEM; goto out_put_spnego_key; } - ses->auth_key.len = msg->sesskey_len; + memcpy(ses->auth_key.response, msg->data, msg->sesskey_len); sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; sess_data->iov[1].iov_len = msg->secblob_len; @@ -2257,7 +2270,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) } spin_unlock(&ses->chan_lock); - invalidate_all_cached_dirs(tcon); + invalidate_all_cached_dirs(tcon, true); rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, server, (void **) &req, @@ -3043,7 +3056,8 @@ replay_again: } trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, - CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES); + CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES, + rsp->OplockLevel); SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId); @@ -3320,9 +3334,6 @@ replay_again: goto creat_exit; } else if (rsp == NULL) /* unlikely to happen, but safer to check */ goto creat_exit; - else - trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, - oparms->create_options, oparms->desired_access); atomic_inc(&tcon->num_remote_opens); oparms->fid->persistent_fid = rsp->PersistentFileId; @@ -3347,6 +3358,10 @@ replay_again: rc = smb2_parse_contexts(server, &rsp_iov, &oparms->fid->epoch, oparms->fid->lease_key, oplock, buf, posix); + + trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, + oparms->create_options, oparms->desired_access, + *oplock); creat_exit: SMB2_open_free(&rqst); free_rsp_buf(resp_buftype, rsp); @@ -6272,6 +6287,11 @@ replay_again: smb2_set_replay(server, &rqst); } + trace_smb3_lock_enter(xid, persist_fid, tcon->tid, tcon->ses->Suid, + le64_to_cpu(buf[0].Offset), + le64_to_cpu(buf[0].Length), + le32_to_cpu(buf[0].Flags), num_lock, 0); + rc = cifs_send_recv(xid, tcon->ses, server, &rqst, &resp_buf_type, flags, &rsp_iov); @@ -6280,7 +6300,15 @@ replay_again: cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc); cifs_stats_fail_inc(tcon, SMB2_LOCK_HE); trace_smb3_lock_err(xid, persist_fid, tcon->tid, - tcon->ses->Suid, rc); + tcon->ses->Suid, + le64_to_cpu(buf[0].Offset), + le64_to_cpu(buf[0].Length), + le32_to_cpu(buf[0].Flags), num_lock, rc); + } else { + trace_smb3_lock_done(xid, persist_fid, tcon->tid, tcon->ses->Suid, + le64_to_cpu(buf[0].Offset), + le64_to_cpu(buf[0].Length), + le32_to_cpu(buf[0].Flags), num_lock, 0); } if (is_replayable_error(rc) && diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h index 5f74475ba9d1..1ceb95b907e6 100644 --- a/fs/smb/client/smb2proto.h +++ b/fs/smb/client/smb2proto.h @@ -257,7 +257,6 @@ int smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, char *data); void smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, struct kstatfs *kst); -int smb3_crypto_shash_allocate(struct TCP_Server_Info *server); void smb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server, struct kvec *iov, int nvec); diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c index 81be2b226e26..e8eeff9e50d6 100644 --- a/fs/smb/client/smb2transport.c +++ b/fs/smb/client/smb2transport.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include "cifsglob.h" @@ -28,14 +29,6 @@ #include "../common/smb2status.h" #include "smb2glob.h" -int -smb3_crypto_shash_allocate(struct TCP_Server_Info *server) -{ - struct cifs_secmech *p = &server->secmech; - - return cifs_alloc_hash("cmac(aes)", &p->aes_cmac); -} - static int smb3_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) { @@ -211,8 +204,7 @@ smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid) } static int -smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, - bool allocate_crypto) +smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) { int rc; unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; @@ -258,14 +250,14 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, return rc; } -static int generate_key(struct cifs_ses *ses, struct kvec label, - struct kvec context, __u8 *key, unsigned int key_size) +static void generate_key(struct cifs_ses *ses, struct kvec label, + struct kvec context, __u8 *key, unsigned int key_size, + unsigned int full_key_size) { unsigned char zero = 0x0; __u8 i[4] = {0, 0, 0, 1}; __u8 L128[4] = {0, 0, 0, 128}; __u8 L256[4] = {0, 0, 1, 0}; - int rc = 0; unsigned char prfhash[SMB2_HMACSHA256_SIZE]; struct TCP_Server_Info *server = ses->server; struct hmac_sha256_ctx hmac_ctx; @@ -273,14 +265,8 @@ static int generate_key(struct cifs_ses *ses, struct kvec label, memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); memset(key, 0x0, key_size); - rc = smb3_crypto_shash_allocate(server); - if (rc) { - cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); - return rc; - } - hmac_sha256_init_usingrawkey(&hmac_ctx, ses->auth_key.response, - SMB2_NTLMV2_SESSKEY_SIZE); + full_key_size); hmac_sha256_update(&hmac_ctx, i, 4); hmac_sha256_update(&hmac_ctx, label.iov_base, label.iov_len); hmac_sha256_update(&hmac_ctx, &zero, 1); @@ -295,7 +281,6 @@ static int generate_key(struct cifs_ses *ses, struct kvec label, hmac_sha256_final(&hmac_ctx, prfhash); memcpy(key, prfhash, key_size); - return 0; } struct derivation { @@ -314,7 +299,7 @@ generate_smb3signingkey(struct cifs_ses *ses, struct TCP_Server_Info *server, const struct derivation_triplet *ptriplet) { - int rc; + unsigned int full_key_size = SMB2_NTLMV2_SESSKEY_SIZE; bool is_binding = false; int chan_index = 0; @@ -345,19 +330,26 @@ generate_smb3signingkey(struct cifs_ses *ses, */ if (is_binding) { - rc = generate_key(ses, ptriplet->signing.label, - ptriplet->signing.context, - ses->chans[chan_index].signkey, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; + generate_key(ses, ptriplet->signing.label, + ptriplet->signing.context, + ses->chans[chan_index].signkey, SMB3_SIGN_KEY_SIZE, + SMB2_NTLMV2_SESSKEY_SIZE); } else { - rc = generate_key(ses, ptriplet->signing.label, - ptriplet->signing.context, - ses->smb3signingkey, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; + generate_key(ses, ptriplet->signing.label, + ptriplet->signing.context, ses->smb3signingkey, + SMB3_SIGN_KEY_SIZE, SMB2_NTLMV2_SESSKEY_SIZE); + + /* + * Per MS-SMB2 3.2.5.3.1, signing key always uses Session.SessionKey + * (first 16 bytes). Encryption/decryption keys use + * Session.FullSessionKey when dialect is 3.1.1 and cipher is + * AES-256-CCM or AES-256-GCM, otherwise Session.SessionKey. + */ + + if (server->dialect == SMB311_PROT_ID && + (server->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + full_key_size = ses->auth_key.len; /* safe to access primary channel, since it will never go away */ spin_lock(&ses->chan_lock); @@ -365,18 +357,15 @@ generate_smb3signingkey(struct cifs_ses *ses, SMB3_SIGN_KEY_SIZE); spin_unlock(&ses->chan_lock); - rc = generate_key(ses, ptriplet->encryption.label, - ptriplet->encryption.context, - ses->smb3encryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; - rc = generate_key(ses, ptriplet->decryption.label, - ptriplet->decryption.context, - ses->smb3decryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; + generate_key(ses, ptriplet->encryption.label, + ptriplet->encryption.context, + ses->smb3encryptionkey, SMB3_ENC_DEC_KEY_SIZE, + full_key_size); + + generate_key(ses, ptriplet->decryption.label, + ptriplet->decryption.context, + ses->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE, + full_key_size); } #ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS @@ -389,7 +378,7 @@ generate_smb3signingkey(struct cifs_ses *ses, &ses->Suid); cifs_dbg(VFS, "Cipher type %d\n", server->cipher_type); cifs_dbg(VFS, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); + (int)ses->auth_key.len, ses->auth_key.response); cifs_dbg(VFS, "Signing Key %*ph\n", SMB3_SIGN_KEY_SIZE, ses->smb3signingkey); if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || @@ -405,7 +394,7 @@ generate_smb3signingkey(struct cifs_ses *ses, SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3decryptionkey); } #endif - return rc; + return 0; } int @@ -467,19 +456,19 @@ generate_smb311signingkey(struct cifs_ses *ses, } static int -smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, - bool allocate_crypto) +smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) { int rc; unsigned char smb3_signature[SMB2_CMACAES_SIZE]; struct kvec *iov = rqst->rq_iov; struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; - struct shash_desc *shash = NULL; + struct aes_cmac_key cmac_key; + struct aes_cmac_ctx cmac_ctx; struct smb_rqst drqst; u8 key[SMB3_SIGN_KEY_SIZE]; if (server->vals->protocol_id <= SMB21_PROT_ID) - return smb2_calc_signature(rqst, server, allocate_crypto); + return smb2_calc_signature(rqst, server); rc = smb3_get_sign_key(le64_to_cpu(shdr->SessionId), server, key); if (unlikely(rc)) { @@ -487,33 +476,16 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, return rc; } - if (allocate_crypto) { - rc = cifs_alloc_hash("cmac(aes)", &shash); - if (rc) - return rc; - } else { - shash = server->secmech.aes_cmac; - } - memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); - rc = crypto_shash_setkey(shash->tfm, key, SMB2_CMACAES_SIZE); + rc = aes_cmac_preparekey(&cmac_key, key, SMB2_CMACAES_SIZE); if (rc) { cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); - goto out; + return rc; } - /* - * we already allocate aes_cmac when we init smb3 signing key, - * so unlike smb2 case we do not have to check here if secmech are - * initialized - */ - rc = crypto_shash_init(shash); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__); - goto out; - } + aes_cmac_init(&cmac_ctx, &cmac_key); /* * For SMB2+, __cifs_calc_signature() expects to sign only the actual @@ -524,26 +496,16 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, */ drqst = *rqst; if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { - rc = crypto_shash_update(shash, iov[0].iov_base, - iov[0].iov_len); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with payload\n", - __func__); - goto out; - } + aes_cmac_update(&cmac_ctx, iov[0].iov_base, iov[0].iov_len); drqst.rq_iov++; drqst.rq_nvec--; } rc = __cifs_calc_signature( &drqst, server, smb3_signature, - &(struct cifs_calc_sig_ctx){ .shash = shash }); + &(struct cifs_calc_sig_ctx){ .cmac = &cmac_ctx }); if (!rc) memcpy(shdr->Signature, smb3_signature, SMB2_SIGNATURE_SIZE); - -out: - if (allocate_crypto) - cifs_free_hash(&shash); return rc; } @@ -577,7 +539,7 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) return 0; } - return smb3_calc_signature(rqst, server, false); + return smb3_calc_signature(rqst, server); } int @@ -613,7 +575,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE); - rc = smb3_calc_signature(rqst, server, true); + rc = smb3_calc_signature(rqst, server); if (rc) return rc; diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c index 9e67adcdc7d3..563ef488a225 100644 --- a/fs/smb/client/smbdirect.c +++ b/fs/smb/client/smbdirect.c @@ -9,7 +9,6 @@ #include "cifs_debug.h" #include "cifsproto.h" #include "smb2proto.h" -#include "../common/smbdirect/smbdirect_public.h" /* Port numbers for SMBD transport */ #define SMB_PORT 445 @@ -558,3 +557,5 @@ void smbd_debug_proc_show(struct TCP_Server_Info *server, struct seq_file *m) server->rdma_readwrite_threshold, m); } + +MODULE_IMPORT_NS("SMBDIRECT"); diff --git a/fs/smb/client/smbdirect.h b/fs/smb/client/smbdirect.h index 0017d5b2de44..be205ec02077 100644 --- a/fs/smb/client/smbdirect.h +++ b/fs/smb/client/smbdirect.h @@ -12,7 +12,7 @@ #include "cifsglob.h" -#include "../common/smbdirect/smbdirect.h" +#include extern int rdma_readwrite_threshold; extern int smbd_max_frmr_depth; diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h index acfbb63086ea..b99ec5a417fa 100644 --- a/fs/smb/client/trace.h +++ b/fs/smb/client/trace.h @@ -670,9 +670,100 @@ DEFINE_EVENT(smb3_fd_err_class, smb3_##name, \ TP_ARGS(xid, fid, tid, sesid, rc)) DEFINE_SMB3_FD_ERR_EVENT(flush_err); -DEFINE_SMB3_FD_ERR_EVENT(lock_err); DEFINE_SMB3_FD_ERR_EVENT(close_err); +DECLARE_EVENT_CLASS(smb3_lock_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + __u64 offset, + __u64 len, + __u32 flags, + __u32 num_lock, + int rc), + TP_ARGS(xid, fid, tid, sesid, offset, len, flags, num_lock, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, offset) + __field(__u64, len) + __field(__u32, flags) + __field(__u32, num_lock) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->offset = offset; + __entry->len = len; + __entry->flags = flags; + __entry->num_lock = num_lock; + __entry->rc = rc; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%llx flags=0x%x num_lock=%u rc=%d", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->offset, __entry->len, __entry->flags, __entry->num_lock, + __entry->rc) +) + +#define DEFINE_SMB3_LOCK_EVENT(name) \ +DEFINE_EVENT(smb3_lock_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + __u64 offset, \ + __u64 len, \ + __u32 flags, \ + __u32 num_lock, \ + int rc), \ + TP_ARGS(xid, fid, tid, sesid, offset, len, flags, num_lock, rc)) + +DEFINE_SMB3_LOCK_EVENT(lock_enter); +DEFINE_SMB3_LOCK_EVENT(lock_done); +DEFINE_SMB3_LOCK_EVENT(lock_err); +DEFINE_SMB3_LOCK_EVENT(lock_cached); + +TRACE_EVENT(smb3_lock_conflict, + TP_PROTO(__u64 fid, + __u64 req_offset, + __u64 req_len, + __u8 req_type, + __u64 conf_offset, + __u64 conf_len, + __u16 conf_type, + __u32 conf_pid), + TP_ARGS(fid, req_offset, req_len, req_type, conf_offset, conf_len, conf_type, conf_pid), + TP_STRUCT__entry( + __field(__u64, fid) + __field(__u64, req_offset) + __field(__u64, req_len) + __field(__u8, req_type) + __field(__u64, conf_offset) + __field(__u64, conf_len) + __field(__u16, conf_type) + __field(__u32, conf_pid) + ), + TP_fast_assign( + __entry->fid = fid; + __entry->req_offset = req_offset; + __entry->req_len = req_len; + __entry->req_type = req_type; + __entry->conf_offset = conf_offset; + __entry->conf_len = conf_len; + __entry->conf_type = conf_type; + __entry->conf_pid = conf_pid; + ), + TP_printk("fid=0x%llx req=[0x%llx:0x%llx] type=0x%x conflicts with [0x%llx:0x%llx] type=0x%x pid=%u", + __entry->fid, __entry->req_offset, __entry->req_len, __entry->req_type, + __entry->conf_offset, __entry->conf_len, __entry->conf_type, __entry->conf_pid) +); + /* * For handle based query/set info calls */ @@ -1230,8 +1321,9 @@ DECLARE_EVENT_CLASS(smb3_open_done_class, __u32 tid, __u64 sesid, int create_options, - int desired_access), - TP_ARGS(xid, fid, tid, sesid, create_options, desired_access), + int desired_access, + __u8 oplock), + TP_ARGS(xid, fid, tid, sesid, create_options, desired_access, oplock), TP_STRUCT__entry( __field(unsigned int, xid) __field(__u64, fid) @@ -1239,6 +1331,7 @@ DECLARE_EVENT_CLASS(smb3_open_done_class, __field(__u64, sesid) __field(int, create_options) __field(int, desired_access) + __field(__u8, oplock) ), TP_fast_assign( __entry->xid = xid; @@ -1247,10 +1340,11 @@ DECLARE_EVENT_CLASS(smb3_open_done_class, __entry->sesid = sesid; __entry->create_options = create_options; __entry->desired_access = desired_access; + __entry->oplock = oplock; ), - TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx cr_opts=0x%x des_access=0x%x", + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx cr_opts=0x%x des_access=0x%x oplock=0x%x", __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->create_options, __entry->desired_access) + __entry->create_options, __entry->desired_access, __entry->oplock) ) #define DEFINE_SMB3_OPEN_DONE_EVENT(name) \ @@ -1260,12 +1354,64 @@ DEFINE_EVENT(smb3_open_done_class, smb3_##name, \ __u32 tid, \ __u64 sesid, \ int create_options, \ - int desired_access), \ - TP_ARGS(xid, fid, tid, sesid, create_options, desired_access)) + int desired_access, \ + __u8 oplock), \ + TP_ARGS(xid, fid, tid, sesid, create_options, desired_access, oplock)) DEFINE_SMB3_OPEN_DONE_EVENT(open_done); DEFINE_SMB3_OPEN_DONE_EVENT(posix_mkdir_done); +TRACE_EVENT(smb3_open_cached, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid, + __u64 fid, + unsigned int oflags, + unsigned int cflags), + TP_ARGS(xid, tid, sesid, fid, oflags, cflags), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, fid) + __field(unsigned int, oflags) + __field(unsigned int, cflags) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->fid = fid; + __entry->oflags = oflags; + __entry->cflags = cflags; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx oflags=0x%x cflags=0x%x", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->oflags, __entry->cflags) +); + +TRACE_EVENT(smb3_close_cached, + TP_PROTO(__u32 tid, + __u64 sesid, + __u64 fid, + unsigned long delay_jiffies), + TP_ARGS(tid, sesid, fid, delay_jiffies), + TP_STRUCT__entry( + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, fid) + __field(unsigned long, delay_jiffies) + ), + TP_fast_assign( + __entry->tid = tid; + __entry->sesid = sesid; + __entry->fid = fid; + __entry->delay_jiffies = delay_jiffies; + ), + TP_printk("sid=0x%llx tid=0x%x fid=0x%llx delay_jiffies=%lu", + __entry->sesid, __entry->tid, __entry->fid, __entry->delay_jiffies) +); + DECLARE_EVENT_CLASS(smb3_lease_done_class, TP_PROTO(__u32 lease_state, diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index 05f8099047e1..fdf4e50c27ce 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -1158,7 +1158,7 @@ int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) { int length, len; - unsigned int data_offset, data_len; + unsigned int data_offset, data_len, end_off; struct cifs_io_subrequest *rdata = mid->callback_data; char *buf = server->smallbuf; unsigned int buflen = server->pdu_size; @@ -1256,11 +1256,14 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) use_rdma_mr = rdata->mr; #endif data_len = server->ops->read_data_length(buf, use_rdma_mr); - if (!use_rdma_mr && (data_offset + data_len > buflen)) { - /* data_len is corrupt -- discard frame */ - rdata->result = smb_EIO2(smb_eio_trace_read_rsp_malformed, - data_offset + data_len, buflen); - return cifs_readv_discard(server, mid); + if (!use_rdma_mr) { + if (check_add_overflow(data_offset, data_len, &end_off) || + end_off > buflen) { + /* data_len is corrupt -- discard frame */ + rdata->result = smb_EIO2(smb_eio_trace_read_rsp_malformed, + end_off, buflen); + return cifs_readv_discard(server, mid); + } } #ifdef CONFIG_CIFS_SMB_DIRECT diff --git a/fs/smb/common/Makefile b/fs/smb/common/Makefile index e6ee65c31b5d..9e0730a385fb 100644 --- a/fs/smb/common/Makefile +++ b/fs/smb/common/Makefile @@ -4,4 +4,3 @@ # obj-$(CONFIG_SMBFS) += cifs_md4.o -obj-$(CONFIG_SMB_COMMON_SMBDIRECT) += smbdirect/ diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h index b4ccddca9256..bc3012cc295d 100644 --- a/fs/smb/common/fscc.h +++ b/fs/smb/common/fscc.h @@ -260,12 +260,12 @@ typedef struct { char FileName[]; } __packed FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */ -/* See MS-FSCC 2.4.13 */ +/* See MS-FSCC 2.4.14 */ struct smb2_file_eof_info { /* encoding of request for level 10 */ __le64 EndOfFile; /* new end of file value */ } __packed; /* level 20 Set */ -/* See MS-FSCC 2.4.14 */ +/* See MS-FSCC 2.4.15 */ typedef struct { __le32 NextEntryOffset; __u32 FileIndex; diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h index 85a4248d4f29..aeb0a245c532 100644 --- a/fs/smb/common/smb2pdu.h +++ b/fs/smb/common/smb2pdu.h @@ -510,6 +510,8 @@ struct smb2_encryption_neg_context { /* Pattern scanning algorithm See MS-SMB2 3.1.4.4.1 */ #define SMB3_COMPRESS_PATTERN cpu_to_le16(0x0004) /* Pattern_V1 */ #define SMB3_COMPRESS_LZ4 cpu_to_le16(0x0005) +/* Account for NONE for easier array indexing */ +#define SMB3_COMPRESS_MAX_ALGS 6 /* Compression Flags */ #define SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE cpu_to_le32(0x00000000) @@ -1564,6 +1566,10 @@ struct validate_negotiate_info_rsp { #define FILE_STANDARD_LINK_INFORMATION 54 #define FILE_ID_INFORMATION 59 #define FILE_ID_EXTD_DIRECTORY_INFORMATION 60 /* also for QUERY_DIR */ +#define FileId64ExtdDirectoryInformation 78 /* also for QUERY_DIR */ +#define FileId64ExtdBothDirectoryInformation 79 /* also for QUERY_DIR */ +#define FileIdAllExtdDirectoryInformation 80 /* also for QUERY_DIR */ +#define FileIdAllExtdBothDirectoryInformation 81 /* also for QUERY_DIR */ /* Used for Query Info and Find File POSIX Info for SMB3.1.1 and SMB1 */ #define SMB_FIND_FILE_POSIX_INFO 0x064 diff --git a/fs/smb/common/smbdirect/Makefile b/fs/smb/common/smbdirect/Makefile deleted file mode 100644 index 423f533e1002..000000000000 --- a/fs/smb/common/smbdirect/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# -# Makefile for smbdirect support -# - -obj-$(CONFIG_SMB_COMMON_SMBDIRECT) += smbdirect.o - -smbdirect-y := \ - smbdirect_socket.o \ - smbdirect_connection.o \ - smbdirect_mr.o \ - smbdirect_rw.o \ - smbdirect_debug.o \ - smbdirect_connect.o \ - smbdirect_listen.o \ - smbdirect_accept.o \ - smbdirect_devices.o \ - smbdirect_main.o diff --git a/fs/smb/common/smbdirect/smbdirect.h b/fs/smb/common/smbdirect/smbdirect.h deleted file mode 100644 index bbab5f7f7cc9..000000000000 --- a/fs/smb/common/smbdirect/smbdirect.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2025 Stefan Metzmacher - */ - -#ifndef __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__ -#define __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__ - -#include - -/* SMB-DIRECT buffer descriptor V1 structure [MS-SMBD] 2.2.3.1 */ -struct smbdirect_buffer_descriptor_v1 { - __le64 offset; - __le32 token; - __le32 length; -} __packed; - -/* - * Connection parameters mostly from [MS-SMBD] 3.1.1.1 - * - * These are setup and negotiated at the beginning of a - * connection and remain constant unless explicitly changed. - * - * Some values are important for the upper layer. - */ -struct smbdirect_socket_parameters { - __u64 flags; -#define SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB ((__u64)0x1) -#define SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW ((__u64)0x2) - __u32 resolve_addr_timeout_msec; - __u32 resolve_route_timeout_msec; - __u32 rdma_connect_timeout_msec; - __u32 negotiate_timeout_msec; - __u16 initiator_depth; /* limited to U8_MAX */ - __u16 responder_resources; /* limited to U8_MAX */ - __u16 recv_credit_max; - __u16 send_credit_target; - __u32 max_send_size; - __u32 max_fragmented_send_size; - __u32 max_recv_size; - __u32 max_fragmented_recv_size; - __u32 max_read_write_size; - __u32 max_frmr_depth; - __u32 keepalive_interval_msec; - __u32 keepalive_timeout_msec; -} __packed; - -#define SMBDIRECT_FLAG_PORT_RANGE_MASK ( \ - SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB | \ - SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW) - -#endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__ */ diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig index 37387410e5bb..08d8b7a965a6 100644 --- a/fs/smb/server/Kconfig +++ b/fs/smb/server/Kconfig @@ -7,13 +7,13 @@ config SMB_SERVER select NLS_UTF8 select NLS_UCS2_UTILS select CRYPTO + select CRYPTO_LIB_AES_CBC_MACS select CRYPTO_LIB_ARC4 select CRYPTO_LIB_DES select CRYPTO_LIB_MD5 select CRYPTO_LIB_SHA256 select CRYPTO_LIB_SHA512 select CRYPTO_LIB_UTILS - select CRYPTO_CMAC select CRYPTO_AEAD2 select CRYPTO_CCM select CRYPTO_GCM @@ -49,7 +49,7 @@ config SMB_SERVER_SMBDIRECT bool "Support for SMB Direct protocol" depends on SMB_SERVER && INFINIBAND && INFINIBAND_ADDR_TRANS depends on SMB_SERVER=m || INFINIBAND=y - select SMB_COMMON_SMBDIRECT + select SMBDIRECT default n help diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index 7d0691f7263f..e99409fa721c 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -11,8 +11,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -490,46 +490,21 @@ void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, * @sig: signature value generated for client request packet * */ -int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig) +void ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) { - struct ksmbd_crypto_ctx *ctx; - int rc, i; + struct aes_cmac_key cmac_key; + struct aes_cmac_ctx cmac_ctx; + int i; - ctx = ksmbd_crypto_ctx_find_cmacaes(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc cmac\n"); - return -ENOMEM; - } + /* This cannot fail, since we always pass a valid key length. */ + static_assert(SMB2_CMACAES_SIZE == AES_KEYSIZE_128); + aes_cmac_preparekey(&cmac_key, key, SMB2_CMACAES_SIZE); - rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx), - key, - SMB2_CMACAES_SIZE); - if (rc) - goto out; - - rc = crypto_shash_init(CRYPTO_CMACAES(ctx)); - if (rc) { - ksmbd_debug(AUTH, "cmaces init error %d\n", rc); - goto out; - } - - for (i = 0; i < n_vec; i++) { - rc = crypto_shash_update(CRYPTO_CMACAES(ctx), - iov[i].iov_base, - iov[i].iov_len); - if (rc) { - ksmbd_debug(AUTH, "cmaces update error %d\n", rc); - goto out; - } - } - - rc = crypto_shash_final(CRYPTO_CMACAES(ctx), sig); - if (rc) - ksmbd_debug(AUTH, "cmaces generation error %d\n", rc); -out: - ksmbd_release_crypto_ctx(ctx); - return rc; + aes_cmac_init(&cmac_ctx, &cmac_key); + for (i = 0; i < n_vec; i++) + aes_cmac_update(&cmac_ctx, iov[i].iov_base, iov[i].iov_len); + aes_cmac_final(&cmac_ctx, sig); } struct derivation { diff --git a/fs/smb/server/auth.h b/fs/smb/server/auth.h index 6d351d61b0e5..5767aabc63c9 100644 --- a/fs/smb/server/auth.h +++ b/fs/smb/server/auth.h @@ -54,8 +54,8 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, int in_len, char *out_blob, int *out_len); void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, int n_vec, char *sig); -int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig); +void ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, struct ksmbd_conn *conn); int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index a26899d12df1..8347495dbc62 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -79,6 +79,85 @@ static int create_proc_clients(void) { return 0; } static void delete_proc_clients(void) {} #endif +static struct workqueue_struct *ksmbd_conn_wq; + +int ksmbd_conn_wq_init(void) +{ + ksmbd_conn_wq = alloc_workqueue("ksmbd-conn-release", + WQ_UNBOUND | WQ_MEM_RECLAIM, 0); + if (!ksmbd_conn_wq) + return -ENOMEM; + return 0; +} + +void ksmbd_conn_wq_destroy(void) +{ + if (ksmbd_conn_wq) { + destroy_workqueue(ksmbd_conn_wq); + ksmbd_conn_wq = NULL; + } +} + +/* + * __ksmbd_conn_release_work() - perform the final, once-per-struct cleanup + * of a ksmbd_conn whose refcount has just dropped to zero. + * + * This is the common release path used by ksmbd_conn_put() for the embedded + * state that outlives the connection thread: async_ida and the attached + * transport (which owns the socket and iov for TCP). Called from a workqueue + * so that sleep-allowed teardown (sock_release -> tcp_close -> + * lock_sock_nested) never runs from an RCU softirq callback (free_opinfo_rcu) + * or any other non-sleeping putter context. + */ +static void __ksmbd_conn_release_work(struct work_struct *work) +{ + struct ksmbd_conn *conn = + container_of(work, struct ksmbd_conn, release_work); + + ida_destroy(&conn->async_ida); + conn->transport->ops->free_transport(conn->transport); + kfree(conn); +} + +/** + * ksmbd_conn_get() - take a reference on @conn and return it. + * + * @conn: connection instance to get a reference to + * + * Returns @conn unchanged so callers can write + * "fp->conn = ksmbd_conn_get(work->conn);" in one expression. Returns NULL + * if @conn is NULL. + */ +struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn) +{ + if (!conn) + return NULL; + + atomic_inc(&conn->refcnt); + return conn; +} + +/** + * ksmbd_conn_put() - drop a reference and, if it was the last, queue the + * release onto ksmbd_conn_wq so it runs from process context. + * + * @conn: connection instance to put a reference to + * + * Callable from any context including RCU softirq callbacks and non-sleeping + * locks; the actual release is deferred to the workqueue. ksmbd_conn_wq is + * created in ksmbd_server_init() before any conn can be allocated and is + * destroyed in ksmbd_server_exit() after rcu_barrier(), so it is always + * non-NULL while a conn reference is held. + */ +void ksmbd_conn_put(struct ksmbd_conn *conn) +{ + if (!conn) + return; + + if (atomic_dec_and_test(&conn->refcnt)) + queue_work(ksmbd_conn_wq, &conn->release_work); +} + /** * ksmbd_conn_free() - free resources of the connection instance * @@ -93,14 +172,19 @@ void ksmbd_conn_free(struct ksmbd_conn *conn) hash_del(&conn->hlist); up_write(&conn_list_lock); + /* + * request_buf / preauth_info / mechToken are only ever accessed by the + * connection handler thread that owns @conn. ksmbd_conn_free() is + * called from the transport free_transport() path when that thread is + * exiting, so it is safe to release them unconditionally even when + * ksmbd_conn_put() below is not the final putter (oplock / ksmbd_file + * holders only retain the conn pointer, not these per-thread buffers). + */ xa_destroy(&conn->sessions); kvfree(conn->request_buf); kfree(conn->preauth_info); kfree(conn->mechToken); - if (atomic_dec_and_test(&conn->refcnt)) { - conn->transport->ops->free_transport(conn->transport); - kfree(conn); - } + ksmbd_conn_put(conn); } /** @@ -127,6 +211,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) conn->um = ERR_PTR(-EOPNOTSUPP); if (IS_ERR(conn->um)) conn->um = NULL; + INIT_WORK(&conn->release_work, __ksmbd_conn_release_work); atomic_set(&conn->req_running, 0); atomic_set(&conn->r_count, 0); atomic_set(&conn->refcnt, 1); @@ -237,7 +322,7 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id) { struct ksmbd_conn *conn; int rc, retry_count = 0, max_timeout = 120; - int rcount = 1, bkt; + int rcount, bkt; retry_idle: if (retry_count >= max_timeout) @@ -246,8 +331,7 @@ retry_idle: down_read(&conn_list_lock); hash_for_each(conn_list, bkt, conn, hlist) { if (conn->binding || xa_load(&conn->sessions, sess_id)) { - if (conn == curr_conn) - rcount = 2; + rcount = (conn == curr_conn) ? 2 : 1; if (atomic_read(&conn->req_running) >= rcount) { rc = wait_event_timeout(conn->req_running_q, atomic_read(&conn->req_running) < rcount, @@ -504,8 +588,7 @@ void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn) if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) wake_up(&conn->r_count_q); - if (atomic_dec_and_test(&conn->refcnt)) - kfree(conn); + ksmbd_conn_put(conn); } int ksmbd_conn_transport_init(void) @@ -532,24 +615,54 @@ out: static void stop_sessions(void) { - struct ksmbd_conn *conn; + struct ksmbd_conn *conn, *target; struct ksmbd_transport *t; + bool any; int bkt; + /* + * Serialised via init_lock; no concurrent stop_sessions() can + * touch conn->stop_called, so writing it under the read lock is + * safe. + */ again: + target = NULL; + any = false; down_read(&conn_list_lock); hash_for_each(conn_list, bkt, conn, hlist) { - t = conn->transport; - ksmbd_conn_set_exiting(conn); - if (t->ops->shutdown) { - up_read(&conn_list_lock); - t->ops->shutdown(t); - down_read(&conn_list_lock); - } + any = true; + if (conn->stop_called) + continue; + atomic_inc(&conn->refcnt); + conn->stop_called = true; + /* + * Mark the connection EXITING while still holding the + * read lock so the selection and the status transition + * happen together. Do not regress a connection that has + * already advanced to RELEASING on its own (e.g. the + * handler exited its receive loop for an unrelated + * reason). + */ + if (READ_ONCE(conn->status) != KSMBD_SESS_RELEASING) + ksmbd_conn_set_exiting(conn); + target = conn; + break; } up_read(&conn_list_lock); - if (!hash_empty(conn_list)) { + if (target) { + t = target->transport; + if (t->ops->shutdown) + t->ops->shutdown(t); + if (atomic_dec_and_test(&target->refcnt)) { + ida_destroy(&target->async_ida); + t->ops->free_transport(t); + kfree(target); + } + goto again; + } + + if (any) { msleep(100); goto again; } diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index ae21a1bd4c70..e074be942582 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "smb_common.h" #include "ksmbd_work.h" @@ -49,6 +50,7 @@ struct ksmbd_conn { struct mutex srv_mutex; int status; unsigned int cli_cap; + bool stop_called; union { __be32 inet_addr; #if IS_ENABLED(CONFIG_IPV6) @@ -119,6 +121,7 @@ struct ksmbd_conn { bool binding; atomic_t refcnt; bool is_aapl; + struct work_struct release_work; }; struct ksmbd_conn_ops { @@ -163,6 +166,10 @@ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn); int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id); struct ksmbd_conn *ksmbd_conn_alloc(void); void ksmbd_conn_free(struct ksmbd_conn *conn); +struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn); +void ksmbd_conn_put(struct ksmbd_conn *conn); +int ksmbd_conn_wq_init(void); +void ksmbd_conn_wq_destroy(void); bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); int ksmbd_conn_write(struct ksmbd_work *work); int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, diff --git a/fs/smb/server/crypto_ctx.c b/fs/smb/server/crypto_ctx.c index 8fd9713b00b7..2fe7d3300480 100644 --- a/fs/smb/server/crypto_ctx.c +++ b/fs/smb/server/crypto_ctx.c @@ -28,14 +28,6 @@ static inline void free_aead(struct crypto_aead *aead) crypto_free_aead(aead); } -static void free_shash(struct shash_desc *shash) -{ - if (shash) { - crypto_free_shash(shash->tfm); - kfree(shash); - } -} - static struct crypto_aead *alloc_aead(int id) { struct crypto_aead *tfm = NULL; @@ -60,37 +52,10 @@ static struct crypto_aead *alloc_aead(int id) return tfm; } -static struct shash_desc *alloc_shash_desc(int id) -{ - struct crypto_shash *tfm = NULL; - struct shash_desc *shash; - - switch (id) { - case CRYPTO_SHASH_CMACAES: - tfm = crypto_alloc_shash("cmac(aes)", 0, 0); - break; - default: - return NULL; - } - - if (IS_ERR(tfm)) - return NULL; - - shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), - KSMBD_DEFAULT_GFP); - if (!shash) - crypto_free_shash(tfm); - else - shash->tfm = tfm; - return shash; -} - static void ctx_free(struct ksmbd_crypto_ctx *ctx) { int i; - for (i = 0; i < CRYPTO_SHASH_MAX; i++) - free_shash(ctx->desc[i]); for (i = 0; i < CRYPTO_AEAD_MAX; i++) free_aead(ctx->ccmaes[i]); kfree(ctx); @@ -153,29 +118,6 @@ void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx) ctx_free(ctx); } -static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id) -{ - struct ksmbd_crypto_ctx *ctx; - - if (id >= CRYPTO_SHASH_MAX) - return NULL; - - ctx = ksmbd_find_crypto_ctx(); - if (ctx->desc[id]) - return ctx; - - ctx->desc[id] = alloc_shash_desc(id); - if (ctx->desc[id]) - return ctx; - ksmbd_release_crypto_ctx(ctx); - return NULL; -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); -} - static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id) { struct ksmbd_crypto_ctx *ctx; diff --git a/fs/smb/server/crypto_ctx.h b/fs/smb/server/crypto_ctx.h index 27fd553d10aa..b22c6e086f03 100644 --- a/fs/smb/server/crypto_ctx.h +++ b/fs/smb/server/crypto_ctx.h @@ -6,14 +6,8 @@ #ifndef __CRYPTO_CTX_H__ #define __CRYPTO_CTX_H__ -#include #include -enum { - CRYPTO_SHASH_CMACAES = 0, - CRYPTO_SHASH_MAX, -}; - enum { CRYPTO_AEAD_AES_GCM = 16, CRYPTO_AEAD_AES_CCM, @@ -23,19 +17,13 @@ enum { struct ksmbd_crypto_ctx { struct list_head list; - struct shash_desc *desc[CRYPTO_SHASH_MAX]; struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX]; }; -#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) - -#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) - #define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) #define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); void ksmbd_crypto_destroy(void); diff --git a/fs/smb/server/mgmt/share_config.c b/fs/smb/server/mgmt/share_config.c index 53f44ff4d376..6f97f8d39657 100644 --- a/fs/smb/server/mgmt/share_config.c +++ b/fs/smb/server/mgmt/share_config.c @@ -167,7 +167,10 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work, share->path = kstrndup(ksmbd_share_config_path(resp), path_len, KSMBD_DEFAULT_GFP); - if (share->path) { + if (!share->path) { + ret = -ENOMEM; + } else { + ret = 0; share->path_sz = strlen(share->path); while (share->path_sz > 1 && share->path[share->path_sz - 1] == '/') @@ -179,9 +182,10 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work, share->force_directory_mode = resp->force_directory_mode; share->force_uid = resp->force_uid; share->force_gid = resp->force_gid; - ret = parse_veto_list(share, - KSMBD_SHARE_CONFIG_VETO_LIST(resp), - resp->veto_list_sz); + if (!ret) + ret = parse_veto_list(share, + KSMBD_SHARE_CONFIG_VETO_LIST(resp), + resp->veto_list_sz); if (!ret && share->path) { if (__ksmbd_override_fsids(work, share)) { kill_share(share); diff --git a/fs/smb/server/mgmt/user_config.c b/fs/smb/server/mgmt/user_config.c index a3183fe5c536..cf45841d9d1b 100644 --- a/fs/smb/server/mgmt/user_config.c +++ b/fs/smb/server/mgmt/user_config.c @@ -56,12 +56,6 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp, goto err_free; if (resp_ext) { - if (resp_ext->ngroups > NGROUPS_MAX) { - pr_err("ngroups(%u) from login response exceeds max groups(%d)\n", - resp_ext->ngroups, NGROUPS_MAX); - goto err_free; - } - user->sgid = kmemdup(resp_ext->____payload, resp_ext->ngroups * sizeof(gid_t), KSMBD_DEFAULT_GFP); diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index a86589408835..de58aed76cb4 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -391,6 +391,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) free_channel_list(sess); kfree(sess->Preauth_HashValue); ksmbd_release_id(&session_ida, sess->id); + ida_destroy(&sess->tree_conn_ida); kfree(sess); } @@ -547,8 +548,13 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, struct ksmbd_session *sess; sess = ksmbd_session_lookup(conn, id); - if (!sess && conn->binding) + if (!sess && conn->binding) { sess = ksmbd_session_lookup_slowpath(id); + if (sess && !xa_load(&sess->ksmbd_chann_list, (long)conn)) { + ksmbd_user_session_put(sess); + sess = NULL; + } + } if (sess && sess->state != SMB2_SESSION_VALID) { ksmbd_user_session_put(sess); sess = NULL; @@ -665,6 +671,8 @@ static struct ksmbd_session *__session_create(int protocol) if (!sess) return NULL; + ida_init(&sess->tree_conn_ida); + if (ksmbd_init_file_table(&sess->file_table)) goto error; @@ -684,8 +692,6 @@ static struct ksmbd_session *__session_create(int protocol) if (ret) goto error; - ida_init(&sess->tree_conn_ida); - down_write(&sessions_table_lock); hash_add(sessions_table, &sess->hlist, sess->id); up_write(&sessions_table_lock); diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index cd3f28b0e7cb..8feca02ddbf2 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -30,7 +30,6 @@ static DEFINE_RWLOCK(lease_list_lock); static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, u64 id, __u16 Tid) { - struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; struct oplock_info *opinfo; @@ -39,7 +38,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, return NULL; opinfo->sess = sess; - opinfo->conn = conn; + opinfo->conn = ksmbd_conn_get(work->conn); opinfo->level = SMB2_OPLOCK_LEVEL_NONE; opinfo->op_state = OPLOCK_STATE_NONE; opinfo->pending_break = 0; @@ -50,7 +49,6 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, init_waitqueue_head(&opinfo->oplock_brk); atomic_set(&opinfo->refcount, 1); atomic_set(&opinfo->breaking_cnt, 0); - atomic_inc(&opinfo->conn->refcnt); return opinfo; } @@ -132,8 +130,7 @@ static void __free_opinfo(struct oplock_info *opinfo) { if (opinfo->is_lease) free_lease(opinfo); - if (opinfo->conn && atomic_dec_and_test(&opinfo->conn->refcnt)) - kfree(opinfo->conn); + ksmbd_conn_put(opinfo->conn); kfree(opinfo); } diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index d8893079abdb..5d799b2d4c62 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -596,8 +596,14 @@ static int __init ksmbd_server_init(void) if (ret) goto err_crypto_destroy; + ret = ksmbd_conn_wq_init(); + if (ret) + goto err_workqueue_destroy; + return 0; +err_workqueue_destroy: + ksmbd_workqueue_destroy(); err_crypto_destroy: ksmbd_crypto_destroy(); err_release_inode_hash: @@ -623,6 +629,12 @@ static void __exit ksmbd_server_exit(void) { ksmbd_server_shutdown(); rcu_barrier(); + /* + * ksmbd_conn_put() defers the final release onto ksmbd_conn_wq, + * so drain it after rcu_barrier() has fired any pending RCU + * callbacks that may have queued a release. + */ + ksmbd_conn_wq_destroy(); ksmbd_release_inode_hash(); } @@ -631,7 +643,6 @@ MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); MODULE_LICENSE("GPL"); MODULE_SOFTDEP("pre: nls"); MODULE_SOFTDEP("pre: aes"); -MODULE_SOFTDEP("pre: cmac"); MODULE_SOFTDEP("pre: aead2"); MODULE_SOFTDEP("pre: ccm"); MODULE_SOFTDEP("pre: gcm"); diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index ee32e61b6d3c..62d4399a993d 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2844,6 +2844,8 @@ static int parse_durable_handle_context(struct ksmbd_work *work, dh_info->reconnected = true; goto out; } + ksmbd_put_durable_fd(dh_info->fp); + dh_info->fp = NULL; } if ((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) || @@ -3014,29 +3016,23 @@ int smb2_open(struct ksmbd_work *work) if (dh_info.reconnected == true) { rc = smb2_check_durable_oplock(conn, share, dh_info.fp, lc, sess->user, name); - if (rc) { - ksmbd_put_durable_fd(dh_info.fp); + if (rc) goto err_out2; - } rc = ksmbd_reopen_durable_fd(work, dh_info.fp); - if (rc) { - ksmbd_put_durable_fd(dh_info.fp); + if (rc) goto err_out2; - } fp = dh_info.fp; if (ksmbd_override_fsids(work)) { rc = -ENOMEM; - ksmbd_put_durable_fd(dh_info.fp); goto err_out2; } file_info = FILE_OPENED; rc = ksmbd_vfs_getattr(&fp->filp->f_path, &stat); - ksmbd_put_durable_fd(fp); if (rc) goto err_out2; @@ -3061,7 +3057,7 @@ int smb2_open(struct ksmbd_work *work) } else { if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && req->CreateOptions & FILE_RANDOM_ACCESS_LE) - req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE); + req->CreateOptions &= ~FILE_SEQUENTIAL_ONLY_LE; if (req->CreateOptions & (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION | @@ -3075,7 +3071,7 @@ int smb2_open(struct ksmbd_work *work) rc = -EINVAL; goto err_out2; } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) { - req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); + req->CreateOptions &= ~FILE_NO_COMPRESSION_LE; } } } @@ -3771,8 +3767,10 @@ err_out1: err_out2: if (!rc) { - ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED); - rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); + rc = ksmbd_update_fstate(&work->sess->file_table, fp, + FP_INITED); + if (!rc) + rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); } if (rc) { if (rc == -EINVAL) @@ -3806,6 +3804,9 @@ err_out2: ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status); } + if (dh_info.reconnected) + ksmbd_put_durable_fd(dh_info.fp); + kfree(name); kfree(lc); @@ -3947,7 +3948,13 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, goto free_conv_name; } - struct_sz = readdir_info_level_struct_sz(info_level) + conv_len; + struct_sz = readdir_info_level_struct_sz(info_level); + if (struct_sz == -EOPNOTSUPP) { + rc = -EINVAL; + goto free_conv_name; + } + + struct_sz += conv_len; next_entry_offset = ALIGN(struct_sz, KSMBD_DIR_INFO_ALIGNMENT); d_info->last_entry_off_align = next_entry_offset - struct_sz; @@ -4821,6 +4828,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, /* align next xattr entry at 4 byte bundary */ alignment_bytes = ((next_offset + 3) & ~3) - next_offset; if (alignment_bytes) { + if (buf_free_len < alignment_bytes) + break; memset(ptr, '\0', alignment_bytes); ptr += alignment_bytes; next_offset += alignment_bytes; @@ -5745,20 +5754,8 @@ static int smb2_get_info_sec(struct ksmbd_work *work, ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", addition_info); - pntsd = kzalloc(ALIGN(sizeof(struct smb_ntsd), 8), - KSMBD_DEFAULT_GFP); - if (!pntsd) - return -ENOMEM; - - pntsd->revision = cpu_to_le16(1); - pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); - pntsd->osidoffset = 0; - pntsd->gsidoffset = 0; - pntsd->sacloffset = 0; - pntsd->dacloffset = 0; - - secdesclen = sizeof(struct smb_ntsd); - goto iov_pin; + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + return -EINVAL; } if (work->next_smb2_rcv_hdr_off) { @@ -5825,7 +5822,6 @@ release_acl: if (rc) goto err_out; -iov_pin: rsp->OutputBufferLength = cpu_to_le32(secdesclen); rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), rsp, work->response_buf); @@ -7492,7 +7488,12 @@ int smb2_lock(struct ksmbd_work *work) lock_ele = req->locks; ksmbd_debug(SMB, "lock count is %d\n", lock_count); - if (!lock_count) { + /* + * Cap lock_count at 64. The MS-SMB2 spec defines Open.LockSequenceArray + * as exactly 64 entries so 64 is the intended ceiling. No real workload + * comes close to this in a single request. + */ + if (!lock_count || lock_count > 64) { err = -EINVAL; goto out2; } @@ -9067,8 +9068,7 @@ int smb3_check_sign_req(struct ksmbd_work *work) iov[0].iov_base = (char *)&hdr->ProtocolId; iov[0].iov_len = len; - if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature)) - return 0; + ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature); if (crypto_memneq(signature, signature_req, SMB2_SIGNATURE_SIZE)) { pr_err("bad smb2 signature\n"); @@ -9119,9 +9119,8 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) iov = &work->iov[work->iov_idx]; } - if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, - signature)) - memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); + ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature); + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); } /** diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 061a305bf9c8..c1d1f34581d6 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -596,6 +596,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, struct smb_sid *sid; struct smb_ace *ntace; int i, j; + u16 ace_sz; if (!fattr->cf_acls) goto posix_default_acl; @@ -640,8 +641,10 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, flags = 0x03; ntace = (struct smb_ace *)((char *)pndace + *size); - *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, + ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, pace->e_perm, 0777); + if (check_add_overflow(*size, ace_sz, size)) + break; (*num_aces)++; if (pace->e_tag == ACL_USER) ntace->access_req |= @@ -650,8 +653,10 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, if (S_ISDIR(fattr->cf_mode) && (pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) { ntace = (struct smb_ace *)((char *)pndace + *size); - *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, + ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x03, pace->e_perm, 0777); + if (check_add_overflow(*size, ace_sz, size)) + break; (*num_aces)++; if (pace->e_tag == ACL_USER) ntace->access_req |= @@ -691,8 +696,10 @@ posix_default_acl: } ntace = (struct smb_ace *)((char *)pndace + *size); - *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, + ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, pace->e_perm, 0777); + if (check_add_overflow(*size, ace_sz, size)) + break; (*num_aces)++; if (pace->e_tag == ACL_USER) ntace->access_req |= @@ -728,7 +735,8 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, break; memcpy((char *)pndace + size, ntace, nt_ace_size); - size += nt_ace_size; + if (check_add_overflow(size, nt_ace_size, &size)) + break; aces_size -= nt_ace_size; ntace = (struct smb_ace *)((char *)ntace + nt_ace_size); num_aces++; @@ -1060,7 +1068,26 @@ static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, ace->flags = flags; ace->access_req = access_req; smb_copy_sid(&ace->sid, sid); - ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4)); + ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + + (ace->sid.num_subauth * 4)); +} + +static int smb_append_inherited_ace(struct smb_ace **ace, int *nt_size, + u16 *ace_cnt, const struct smb_sid *sid, + u8 type, u8 flags, __le32 access_req) +{ + int ace_size; + + smb_set_ace(*ace, sid, type, flags, access_req); + ace_size = le16_to_cpu((*ace)->size); + /* pdacl->size is __le16 and includes struct smb_acl. */ + if (check_add_overflow(*nt_size, ace_size, nt_size) || + *nt_size > U16_MAX - (int)sizeof(struct smb_acl)) + return -EINVAL; + + (*ace_cnt)++; + *ace = (struct smb_ace *)((char *)*ace + ace_size); + return 0; } int smb_inherit_dacl(struct ksmbd_conn *conn, @@ -1106,8 +1133,24 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, goto free_parent_pntsd; } - aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, - KSMBD_DEFAULT_GFP); + aces_size = pdacl_size - sizeof(struct smb_acl); + + /* + * Validate num_aces against the DACL payload before allocating. + * Each ACE must be at least as large as its fixed-size header + * (up to the SID base), so num_aces cannot exceed the payload + * divided by the minimum ACE size. This mirrors the existing + * check in parse_dacl(). + */ + if (num_aces > aces_size / (offsetof(struct smb_ace, sid) + + offsetof(struct smb_sid, sub_auth) + + sizeof(__le16))) { + rc = -EINVAL; + goto free_parent_pntsd; + } + + aces_base = kmalloc_array(num_aces * 2, sizeof(struct smb_ace), + KSMBD_DEFAULT_GFP); if (!aces_base) { rc = -ENOMEM; goto free_parent_pntsd; @@ -1116,7 +1159,6 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, aces = (struct smb_ace *)aces_base; parent_aces = (struct smb_ace *)((char *)parent_pdacl + sizeof(struct smb_acl)); - aces_size = acl_len - sizeof(struct smb_acl); if (pntsd_type & DACL_AUTO_INHERITED) inherited_flags = INHERITED_ACE; @@ -1124,11 +1166,20 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, for (i = 0; i < num_aces; i++) { int pace_size; - if (offsetof(struct smb_ace, access_req) > aces_size) + if (aces_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE) break; pace_size = le16_to_cpu(parent_aces->size); - if (pace_size > aces_size) + if (pace_size > aces_size || + pace_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE) + break; + + if (parent_aces->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || + pace_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE + + sizeof(__le32) * parent_aces->sid.num_subauth) break; aces_size -= pace_size; @@ -1160,22 +1211,24 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, } if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) { - smb_set_ace(aces, psid, parent_aces->type, inherited_flags, - parent_aces->access_req); - nt_size += le16_to_cpu(aces->size); - ace_cnt++; - aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); + rc = smb_append_inherited_ace(&aces, &nt_size, &ace_cnt, + psid, parent_aces->type, + inherited_flags, + parent_aces->access_req); + if (rc) + goto free_aces_base; flags |= INHERIT_ONLY_ACE; psid = creator; } else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) { psid = &parent_aces->sid; } - smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags, - parent_aces->access_req); - nt_size += le16_to_cpu(aces->size); - aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); - ace_cnt++; + rc = smb_append_inherited_ace(&aces, &nt_size, &ace_cnt, psid, + parent_aces->type, + flags | inherited_flags, + parent_aces->access_req); + if (rc) + goto free_aces_base; pass: parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size); } @@ -1185,7 +1238,7 @@ pass: struct smb_acl *pdacl; struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL; int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; - int pntsd_alloc_size; + size_t pntsd_alloc_size; if (parent_pntsd->osidoffset) { powner_sid = (struct smb_sid *)((char *)parent_pntsd + @@ -1198,8 +1251,19 @@ pass: pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4); } - pntsd_alloc_size = sizeof(struct smb_ntsd) + powner_sid_size + - pgroup_sid_size + sizeof(struct smb_acl) + nt_size; + if (check_add_overflow(sizeof(struct smb_ntsd), + (size_t)powner_sid_size, + &pntsd_alloc_size) || + check_add_overflow(pntsd_alloc_size, + (size_t)pgroup_sid_size, + &pntsd_alloc_size) || + check_add_overflow(pntsd_alloc_size, sizeof(struct smb_acl), + &pntsd_alloc_size) || + check_add_overflow(pntsd_alloc_size, (size_t)nt_size, + &pntsd_alloc_size)) { + rc = -EINVAL; + goto free_aces_base; + } pntsd = kzalloc(pntsd_alloc_size, KSMBD_DEFAULT_GFP); if (!pntsd) { @@ -1342,10 +1406,13 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); aces_size = acl_size - sizeof(struct smb_acl); for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) { - if (offsetof(struct smb_ace, access_req) > aces_size) + if (offsetof(struct smb_ace, sid) + + aces_size < CIFS_SID_BASE_SIZE) break; ace_size = le16_to_cpu(ace->size); - if (ace_size > aces_size) + if (ace_size > aces_size || + ace_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE) break; aces_size -= ace_size; granted |= le32_to_cpu(ace->access_req); @@ -1360,13 +1427,19 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); aces_size = acl_size - sizeof(struct smb_acl); for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) { - if (offsetof(struct smb_ace, access_req) > aces_size) + if (offsetof(struct smb_ace, sid) + + aces_size < CIFS_SID_BASE_SIZE) break; ace_size = le16_to_cpu(ace->size); - if (ace_size > aces_size) + if (ace_size > aces_size || + ace_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE) break; aces_size -= ace_size; + if (ace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) + break; + if (!compare_sids(&sid, &ace->sid) || !compare_sids(&sid_unix_NFS_mode, &ace->sid)) { found = 1; diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c index f7aa427a06fe..0c581b9624d3 100644 --- a/fs/smb/server/transport_ipc.c +++ b/fs/smb/server/transport_ipc.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "vfs_cache.h" #include "transport_ipc.h" @@ -496,7 +497,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry) { struct ksmbd_rpc_command *resp = entry->response; - msg_sz = sizeof(struct ksmbd_rpc_command) + resp->payload_sz; + if (check_add_overflow(sizeof(struct ksmbd_rpc_command), + resp->payload_sz, &msg_sz)) + return -EINVAL; break; } case KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST: @@ -515,8 +518,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry) if (resp->payload_sz < resp->veto_list_sz) return -EINVAL; - msg_sz = sizeof(struct ksmbd_share_config_response) + - resp->payload_sz; + if (check_add_overflow(sizeof(struct ksmbd_share_config_response), + resp->payload_sz, &msg_sz)) + return -EINVAL; } break; } @@ -525,6 +529,12 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry) struct ksmbd_login_response_ext *resp = entry->response; if (resp->ngroups) { + if (resp->ngroups < 0 || + resp->ngroups > NGROUPS_MAX) { + pr_err("ngroups(%d) from login response exceeds max groups(%d)\n", + resp->ngroups, NGROUPS_MAX); + return -EINVAL; + } msg_sz = sizeof(struct ksmbd_login_response_ext) + resp->ngroups * sizeof(gid_t); } diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c index 706a2c897948..b6d63ff8a8a3 100644 --- a/fs/smb/server/transport_rdma.c +++ b/fs/smb/server/transport_rdma.c @@ -18,7 +18,6 @@ #include "smb_common.h" #include "../common/smb2status.h" #include "transport_rdma.h" -#include "../common/smbdirect/smbdirect_public.h" #define SMB_DIRECT_PORT_IWARP 5445 @@ -540,3 +539,5 @@ static const struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { .rdma_write = smb_direct_rdma_write, .free_transport = smb_direct_free_transport, }; + +MODULE_IMPORT_NS("SMBDIRECT"); diff --git a/fs/smb/server/transport_rdma.h b/fs/smb/server/transport_rdma.h index 05352dc47f95..8b78917a1795 100644 --- a/fs/smb/server/transport_rdma.h +++ b/fs/smb/server/transport_rdma.h @@ -25,6 +25,6 @@ static inline void init_smbd_max_io_size(unsigned int sz) { } static inline unsigned int get_smbd_max_read_write_size(struct ksmbd_transport *kt) { return 0; } #endif -#include "../common/smbdirect/smbdirect.h" +#include #endif /* __KSMBD_TRANSPORT_RDMA_H__ */ diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c index 7e29b06820e2..13b711ea575d 100644 --- a/fs/smb/server/transport_tcp.c +++ b/fs/smb/server/transport_tcp.c @@ -183,6 +183,8 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk) t = alloc_transport(client_sk); if (!t) { sock_release(client_sk); + if (server_conf.max_connections) + atomic_dec(&active_num_conn); return -ENOMEM; } @@ -279,7 +281,7 @@ static int ksmbd_kthread_fn(void *p) skip_max_ip_conns_limit: if (server_conf.max_connections && - atomic_inc_return(&active_num_conn) >= server_conf.max_connections) { + atomic_inc_return(&active_num_conn) > server_conf.max_connections) { pr_info_ratelimited("Limit the maximum number of connections(%u)\n", atomic_read(&active_num_conn)); atomic_dec(&active_num_conn); diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 3551f01a3fa0..354c4d8a1cfb 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -418,6 +418,14 @@ static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) return; idr_remove(global_ft.idr, fp->persistent_id); + /* + * Clear persistent_id so a later __ksmbd_close_fd() that runs from a + * delayed putter (e.g. when a concurrent ksmbd_lookup_fd_inode() + * walker held the final reference) does not re-issue idr_remove() on + * an id that idr_alloc_cyclic() may have already handed out to a new + * durable handle. + */ + fp->persistent_id = KSMBD_NO_FID; } static void ksmbd_remove_durable_fd(struct ksmbd_file *fp) @@ -431,13 +439,13 @@ static void ksmbd_remove_durable_fd(struct ksmbd_file *fp) static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) { - if (!has_file_id(fp->volatile_id)) - return; - down_write(&fp->f_ci->m_lock); list_del_init(&fp->node); up_write(&fp->f_ci->m_lock); + if (!has_file_id(fp->volatile_id)) + return; + write_lock(&ft->lock); idr_remove(ft->idr, fp->volatile_id); write_unlock(&ft->lock); @@ -475,6 +483,17 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) kfree(smb_lock); } + /* + * Drop fp's strong reference on conn (taken in ksmbd_open_fd() / + * ksmbd_reopen_durable_fd()). Durable fps that reached the + * scavenger have already had fp->conn cleared by session_fd_check(), + * in which case there is nothing to drop here. + */ + if (fp->conn) { + ksmbd_conn_put(fp->conn); + fp->conn = NULL; + } + if (ksmbd_stream_fd(fp)) kfree(fp->stream.name); kfree(fp->owner.name); @@ -510,6 +529,20 @@ static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp) { + /* + * Detached durable fp -- session_fd_check() cleared fp->conn at + * preserve, so this fp is no longer tracked by any conn's + * stats.open_files_count. This happens when + * ksmbd_scavenger_dispose_dh() hands the final close off to an + * m_fp_list walker (e.g. ksmbd_lookup_fd_inode()) whose work->conn + * is unrelated to the conn that originally opened the handle; close + * via the NULL-ft path so we do not underflow that unrelated + * counter. + */ + if (!fp->conn) { + __ksmbd_close_fd(NULL, fp); + return; + } __ksmbd_close_fd(&work->sess->file_table, fp); atomic_dec(&work->conn->stats.open_files_count); } @@ -752,7 +785,14 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) atomic_set(&fp->refcount, 1); fp->filp = filp; - fp->conn = work->conn; + /* + * fp owns a strong reference on fp->conn for as long as fp->conn is + * non-NULL, so session_fd_check() and __ksmbd_close_fd() never + * dereference a dangling pointer. Paired with ksmbd_conn_put() in + * session_fd_check() (durable preserve), in __ksmbd_close_fd() + * (final close), and on the error paths below. + */ + fp->conn = ksmbd_conn_get(work->conn); fp->tcon = work->tcon; fp->volatile_id = KSMBD_NO_FID; fp->persistent_id = KSMBD_NO_FID; @@ -774,19 +814,64 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) return fp; err_out: + /* fp->conn was set and refcounted before every branch here. */ + ksmbd_conn_put(fp->conn); kmem_cache_free(filp_cache, fp); return ERR_PTR(ret); } -void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, - unsigned int state) +/** + * ksmbd_update_fstate() - update an fp state under the file-table lock + * @ft: file table that publishes @fp's volatile id + * @fp: file pointer to update + * @state: new state + * + * Return: 0 on success. The FP_NEW -> FP_INITED transition is special: + * -ENOENT if teardown already unpublished @fp by advancing the state or + * clearing the volatile id. Other state updates preserve the historical + * fire-and-forget behavior. + */ +int ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, + unsigned int state) { + int ret; + if (!fp) - return; + return -ENOENT; write_lock(&ft->lock); - fp->f_state = state; + if (state == FP_INITED && + (fp->f_state != FP_NEW || !has_file_id(fp->volatile_id))) { + ret = -ENOENT; + } else { + fp->f_state = state; + ret = 0; + } write_unlock(&ft->lock); + + return ret; +} + +/* + * ksmbd_mark_fp_closed() - mark fp closed under ft->lock and return how many + * refs the teardown path owns. + * + * FP_INITED has a normal idr-owned reference, so teardown owns both that + * reference and the transient lookup reference. FP_NEW is still owned by the + * in-flight opener/reopener, which will drop the original reference after + * ksmbd_update_fstate(..., FP_INITED) observes the cleared volatile id. + * FP_CLOSED on entry means an earlier ksmbd_close_fd() already consumed the + * idr-owned ref. + */ +static int ksmbd_mark_fp_closed(struct ksmbd_file *fp) +{ + if (fp->f_state == FP_INITED) { + set_close_state_blocked_works(fp); + fp->f_state = FP_CLOSED; + return 2; + } + + return 1; } static int @@ -794,7 +879,8 @@ __close_file_table_ids(struct ksmbd_session *sess, struct ksmbd_tree_connect *tcon, bool (*skip)(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp, - struct ksmbd_user *user)) + struct ksmbd_user *user), + bool skip_preserves_fp) { struct ksmbd_file_table *ft = &sess->file_table; struct ksmbd_file *fp; @@ -802,32 +888,120 @@ __close_file_table_ids(struct ksmbd_session *sess, int num = 0; while (1) { + int n_to_drop; + write_lock(&ft->lock); fp = idr_get_next(ft->idr, &id); if (!fp) { write_unlock(&ft->lock); break; } - - if (skip(tcon, fp, sess->user) || - !atomic_dec_and_test(&fp->refcount)) { + if (!atomic_inc_not_zero(&fp->refcount)) { id++; write_unlock(&ft->lock); continue; } - set_close_state_blocked_works(fp); - idr_remove(ft->idr, fp->volatile_id); - fp->volatile_id = KSMBD_NO_FID; - write_unlock(&ft->lock); + if (skip_preserves_fp) { + /* + * Session teardown: skip() is session_fd_check(), + * which may sleep and mutates fp->conn / fp->tcon / + * fp->volatile_id when it chooses to preserve fp + * for durable reconnect. Unpublish fp from the + * session idr here, under ft->lock, so that + * __ksmbd_lookup_fd() through this session cannot + * grant a new ksmbd_fp_get() reference to an fp + * whose fields are about to be rewritten outside + * the lock. Durable reconnect still reaches fp via + * global_ft. + */ + idr_remove(ft->idr, id); + fp->volatile_id = KSMBD_NO_FID; + write_unlock(&ft->lock); + if (skip(tcon, fp, sess->user)) { + /* + * session_fd_check() has converted fp to + * durable-preserve state and cleared its + * per-conn fields. fp is already unpublished + * above; the original idr-owned ref keeps it + * alive for the durable scavenger. Drop only + * the transient ref. atomic_dec() is safe -- + * atomic_inc_not_zero() succeeded on a + * positive value and we added one more, so + * refcount cannot be zero here. + */ + atomic_dec(&fp->refcount); + id++; + continue; + } + + /* + * Keep the close-state decision under the same lock + * observed by ksmbd_update_fstate(), which is how an + * in-flight FP_NEW opener learns that teardown has + * cleared its volatile id. + */ + write_lock(&ft->lock); + n_to_drop = ksmbd_mark_fp_closed(fp); + write_unlock(&ft->lock); + } else { + /* + * Tree teardown: skip() is tree_conn_fd_check(), a + * cheap pointer compare that doesn't sleep and has + * no side effects, so keep the skip decision plus + * the unpublish-and-mark-closed sequence atomic + * under ft->lock. fps belonging to other tree + * connects (skip() == true) stay fully published in + * the session idr with no lock window. + */ + if (skip(tcon, fp, sess->user)) { + atomic_dec(&fp->refcount); + write_unlock(&ft->lock); + id++; + continue; + } + idr_remove(ft->idr, id); + fp->volatile_id = KSMBD_NO_FID; + n_to_drop = ksmbd_mark_fp_closed(fp); + write_unlock(&ft->lock); + } + + /* + * fp->volatile_id is already cleared to prevent stale idr + * removal from a deferred final close. Remove fp from + * m_fp_list here because __ksmbd_remove_fd() will skip the + * list unlink when volatile_id is KSMBD_NO_FID. + */ down_write(&fp->f_ci->m_lock); list_del_init(&fp->node); up_write(&fp->f_ci->m_lock); - __ksmbd_close_fd(ft, fp); - - num++; + /* + * Drop the references this iteration owns: + * + * n_to_drop == 2: we observed FP_INITED and committed + * the FP_CLOSED transition ourselves, so we own the + * transient (+1) and the still-intact idr-owned ref. + * + * n_to_drop == 1: either a prior ksmbd_close_fd() + * already consumed the idr-owned ref, or fp was still + * FP_NEW and the in-flight opener/reopener must keep + * the original reference until ksmbd_update_fstate() + * observes the cleared volatile id. + * + * If we end up as the final putter, finalize fp and + * account the open_files_count decrement via the caller's + * atomic_sub(num, ...). Otherwise the remaining user's + * ksmbd_fd_put() reaches __put_fd_final(), which does its + * own atomic_dec(&open_files_count), so we must not count + * this fp here -- doing so would double-decrement the + * connection-wide counter. + */ + if (atomic_sub_and_test(n_to_drop, &fp->refcount)) { + __ksmbd_close_fd(NULL, fp); + num++; + } id++; } @@ -881,24 +1055,37 @@ static bool ksmbd_durable_scavenger_alive(void) return true; } -static void ksmbd_scavenger_dispose_dh(struct list_head *head) +static void ksmbd_scavenger_dispose_dh(struct ksmbd_file *fp) { - while (!list_empty(head)) { - struct ksmbd_file *fp; + /* + * Durable-preserved fp can remain linked on f_ci->m_fp_list for + * share-mode checks. Unlink it before final close; fp->node is not + * available as a scavenger-private list node because re-adding it to + * another list corrupts m_fp_list. + */ + down_write(&fp->f_ci->m_lock); + list_del_init(&fp->node); + up_write(&fp->f_ci->m_lock); - fp = list_first_entry(head, struct ksmbd_file, node); - list_del_init(&fp->node); + /* + * Drop both the durable lifetime reference and the transient reference + * taken by the scavenger under global_ft.lock. If a concurrent + * ksmbd_lookup_fd_inode() (or any other m_fp_list walker) snatched fp + * before the unlink above, that holder owns the final close via + * ksmbd_fd_put() -> __ksmbd_close_fd(). Otherwise the scavenger is + * the last putter and finalises fp here. + */ + if (atomic_sub_and_test(2, &fp->refcount)) __ksmbd_close_fd(NULL, fp); - } } static int ksmbd_durable_scavenger(void *dummy) { struct ksmbd_file *fp = NULL; + struct ksmbd_file *expired_fp; unsigned int id; unsigned int min_timeout = 1; bool found_fp_timeout; - LIST_HEAD(scavenger_list); unsigned long remaining_jiffies; __module_get(THIS_MODULE); @@ -908,8 +1095,6 @@ static int ksmbd_durable_scavenger(void *dummy) if (try_to_freeze()) continue; - found_fp_timeout = false; - remaining_jiffies = wait_event_timeout(dh_wq, ksmbd_durable_scavenger_alive() == false, __msecs_to_jiffies(min_timeout)); @@ -918,23 +1103,39 @@ static int ksmbd_durable_scavenger(void *dummy) else min_timeout = DURABLE_HANDLE_MAX_TIMEOUT; - write_lock(&global_ft.lock); - idr_for_each_entry(global_ft.idr, fp, id) { - if (!fp->durable_timeout) - continue; + do { + expired_fp = NULL; + found_fp_timeout = false; - if (atomic_read(&fp->refcount) > 1 || - fp->conn) - continue; - - found_fp_timeout = true; - if (fp->durable_scavenger_timeout <= - jiffies_to_msecs(jiffies)) { - __ksmbd_remove_durable_fd(fp); - list_add(&fp->node, &scavenger_list); - } else { + write_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { unsigned long durable_timeout; + if (!fp->durable_timeout) + continue; + + if (atomic_read(&fp->refcount) > 1 || + fp->conn) + continue; + + found_fp_timeout = true; + if (fp->durable_scavenger_timeout <= + jiffies_to_msecs(jiffies)) { + __ksmbd_remove_durable_fd(fp); + /* + * Take a transient reference so fp + * cannot be freed by an in-flight + * ksmbd_lookup_fd_inode() that found + * it through f_ci->m_fp_list while we + * drop global_ft.lock and reach the + * m_fp_list unlink in + * ksmbd_scavenger_dispose_dh(). + */ + atomic_inc(&fp->refcount); + expired_fp = fp; + break; + } + durable_timeout = fp->durable_scavenger_timeout - jiffies_to_msecs(jiffies); @@ -942,10 +1143,11 @@ static int ksmbd_durable_scavenger(void *dummy) if (min_timeout > durable_timeout) min_timeout = durable_timeout; } - } - write_unlock(&global_ft.lock); + write_unlock(&global_ft.lock); - ksmbd_scavenger_dispose_dh(&scavenger_list); + if (expired_fp) + ksmbd_scavenger_dispose_dh(expired_fp); + } while (expired_fp); if (found_fp_timeout == false) break; @@ -1062,25 +1264,35 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, if (!is_reconnectable(fp)) return false; + if (fp->f_state != FP_INITED) + return false; + + if (WARN_ON_ONCE(!fp->conn)) + return false; + if (ksmbd_vfs_copy_durable_owner(fp, user)) return false; + /* + * fp owns a strong reference on fp->conn (taken in ksmbd_open_fd() + * / ksmbd_reopen_durable_fd()), so conn stays valid for the whole + * body of this function regardless of any op->conn puts below. + */ conn = fp->conn; ci = fp->f_ci; down_write(&ci->m_lock); list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) { if (op->conn != conn) continue; - if (op->conn && atomic_dec_and_test(&op->conn->refcnt)) - kfree(op->conn); + ksmbd_conn_put(op->conn); op->conn = NULL; } up_write(&ci->m_lock); list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - spin_lock(&fp->conn->llist_lock); + spin_lock(&conn->llist_lock); list_del_init(&smb_lock->clist); - spin_unlock(&fp->conn->llist_lock); + spin_unlock(&conn->llist_lock); } fp->conn = NULL; @@ -1091,6 +1303,8 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, fp->durable_scavenger_timeout = jiffies_to_msecs(jiffies) + fp->durable_timeout; + /* Drop fp's own reference on conn. */ + ksmbd_conn_put(conn); return true; } @@ -1098,7 +1312,8 @@ void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) { int num = __close_file_table_ids(work->sess, work->tcon, - tree_conn_fd_check); + tree_conn_fd_check, + false); atomic_sub(num, &work->conn->stats.open_files_count); } @@ -1107,7 +1322,8 @@ void ksmbd_close_session_fds(struct ksmbd_work *work) { int num = __close_file_table_ids(work->sess, work->tcon, - session_fd_check); + session_fd_check, + true); atomic_sub(num, &work->conn->stats.open_files_count); } @@ -1178,15 +1394,27 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) old_f_state = fp->f_state; fp->f_state = FP_NEW; + + /* + * Initialize fp's connection binding before publishing fp into the + * session's file table. If __open_id() is ordered first, a + * concurrent teardown that iterates the table can observe a valid + * volatile_id with fp->conn == NULL and preserve a + * partially-initialized fp. fp owns a strong reference on the new + * conn (see ksmbd_open_fd()); undo it on __open_id() failure. + */ + fp->conn = ksmbd_conn_get(conn); + fp->tcon = work->tcon; + __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); if (!has_file_id(fp->volatile_id)) { + fp->conn = NULL; + fp->tcon = NULL; + ksmbd_conn_put(conn); fp->f_state = old_f_state; return -EBADF; } - fp->conn = conn; - fp->tcon = work->tcon; - list_for_each_entry(smb_lock, &fp->lock_list, flist) { spin_lock(&conn->llist_lock); list_add_tail(&smb_lock->clist, &conn->lock_list); @@ -1198,8 +1426,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) { if (op->conn) continue; - op->conn = fp->conn; - atomic_inc(&op->conn->refcnt); + op->conn = ksmbd_conn_get(fp->conn); } up_write(&ci->m_lock); @@ -1228,7 +1455,7 @@ void ksmbd_destroy_file_table(struct ksmbd_session *sess) if (!ft->idr) return; - __close_file_table_ids(sess, NULL, session_fd_check); + __close_file_table_ids(sess, NULL, session_fd_check, true); idr_destroy(ft->idr); kfree(ft->idr); ft->idr = NULL; diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 866f32c10d4d..e6871266a94b 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -172,8 +172,8 @@ int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); int ksmbd_init_global_file_table(void); void ksmbd_free_global_file_table(void); void ksmbd_set_fd_limit(unsigned long limit); -void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, - unsigned int state); +int ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, + unsigned int state); bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp, struct ksmbd_user *user); diff --git a/fs/smb/common/smbdirect/Kconfig b/fs/smb/smbdirect/Kconfig similarity index 86% rename from fs/smb/common/smbdirect/Kconfig rename to fs/smb/smbdirect/Kconfig index a46a2e6ec87a..2bf1b3350f7d 100644 --- a/fs/smb/common/smbdirect/Kconfig +++ b/fs/smb/smbdirect/Kconfig @@ -2,7 +2,7 @@ # # smbdirect configuration -config SMB_COMMON_SMBDIRECT +config SMBDIRECT def_tristate n depends on INFINIBAND && INFINIBAND_ADDR_TRANS depends on m || INFINIBAND=y diff --git a/fs/smb/smbdirect/Makefile b/fs/smb/smbdirect/Makefile new file mode 100644 index 000000000000..80d6025984d1 --- /dev/null +++ b/fs/smb/smbdirect/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Makefile for smbdirect support +# + +obj-$(CONFIG_SMBDIRECT) += smbdirect.o + +smbdirect-y := \ + socket.o \ + connection.o \ + mr.o \ + rw.o \ + debug.o \ + connect.o \ + listen.o \ + accept.o \ + devices.o \ + main.o diff --git a/fs/smb/common/smbdirect/smbdirect_accept.c b/fs/smb/smbdirect/accept.c similarity index 99% rename from fs/smb/common/smbdirect/smbdirect_accept.c rename to fs/smb/smbdirect/accept.c index d6d5e6a3f5de..529740005838 100644 --- a/fs/smb/common/smbdirect/smbdirect_accept.c +++ b/fs/smb/smbdirect/accept.c @@ -5,9 +5,9 @@ * Copyright (c) 2025, Stefan Metzmacher */ -#include "smbdirect_internal.h" +#include "internal.h" #include -#include "../../common/smb2status.h" +#include "../common/smb2status.h" static int smbdirect_accept_rdma_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event); @@ -854,4 +854,4 @@ struct smbdirect_socket *smbdirect_socket_accept(struct smbdirect_socket *lsc, return nsc; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_accept); +EXPORT_SYMBOL_GPL(smbdirect_socket_accept); diff --git a/fs/smb/common/smbdirect/smbdirect_connect.c b/fs/smb/smbdirect/connect.c similarity index 99% rename from fs/smb/common/smbdirect/smbdirect_connect.c rename to fs/smb/smbdirect/connect.c index 2b54f79dba43..cd726b399afe 100644 --- a/fs/smb/common/smbdirect/smbdirect_connect.c +++ b/fs/smb/smbdirect/connect.c @@ -3,8 +3,8 @@ * Copyright (c) 2012,2016,2017,2025 Stefan Metzmacher */ -#include "smbdirect_internal.h" -#include "../../common/smb2status.h" +#include "internal.h" +#include "../common/smb2status.h" static int smbdirect_connect_setup_connection(struct smbdirect_socket *sc); static int smbdirect_connect_resolve_addr(struct smbdirect_socket *sc, @@ -60,7 +60,7 @@ int smbdirect_connect(struct smbdirect_socket *sc, const struct sockaddr *dst) */ return 0; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connect); +EXPORT_SYMBOL_GPL(smbdirect_connect); static int smbdirect_connect_setup_connection(struct smbdirect_socket *sc) { @@ -922,4 +922,4 @@ int smbdirect_connect_sync(struct smbdirect_socket *sc, return 0; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connect_sync); +EXPORT_SYMBOL_GPL(smbdirect_connect_sync); diff --git a/fs/smb/common/smbdirect/smbdirect_connection.c b/fs/smb/smbdirect/connection.c similarity index 99% rename from fs/smb/common/smbdirect/smbdirect_connection.c rename to fs/smb/smbdirect/connection.c index 7e4921b9538c..8adf58097534 100644 --- a/fs/smb/common/smbdirect/smbdirect_connection.c +++ b/fs/smb/smbdirect/connection.c @@ -4,7 +4,7 @@ * Copyright (c) 2025, Stefan Metzmacher */ -#include "smbdirect_internal.h" +#include "internal.h" #include struct smbdirect_map_sges { @@ -706,7 +706,7 @@ bool smbdirect_connection_is_connected(struct smbdirect_socket *sc) return false; return true; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_is_connected); +EXPORT_SYMBOL_GPL(smbdirect_connection_is_connected); int smbdirect_connection_wait_for_connected(struct smbdirect_socket *sc) { @@ -779,7 +779,7 @@ int smbdirect_connection_wait_for_connected(struct smbdirect_socket *sc) return 0; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_wait_for_connected); +EXPORT_SYMBOL_GPL(smbdirect_connection_wait_for_connected); void smbdirect_connection_idle_timer_work(struct work_struct *work) { @@ -958,7 +958,7 @@ release_credit: return ret; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_send_batch_flush); +EXPORT_SYMBOL_GPL(smbdirect_connection_send_batch_flush); struct smbdirect_send_batch * smbdirect_init_send_batch_storage(struct smbdirect_send_batch_storage *storage, @@ -976,7 +976,7 @@ smbdirect_init_send_batch_storage(struct smbdirect_send_batch_storage *storage, return batch; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_init_send_batch_storage); +EXPORT_SYMBOL_GPL(smbdirect_init_send_batch_storage); static int smbdirect_connection_wait_for_send_bcredit(struct smbdirect_socket *sc, struct smbdirect_send_batch *batch) @@ -1263,7 +1263,7 @@ lcredit_failed: bcredit_failed: return ret; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_send_single_iter); +EXPORT_SYMBOL_GPL(smbdirect_connection_send_single_iter); int smbdirect_connection_send_wait_zero_pending(struct smbdirect_socket *sc) { @@ -1288,7 +1288,7 @@ int smbdirect_connection_send_wait_zero_pending(struct smbdirect_socket *sc) return 0; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_send_wait_zero_pending); +EXPORT_SYMBOL_GPL(smbdirect_connection_send_wait_zero_pending); int smbdirect_connection_send_iter(struct smbdirect_socket *sc, struct iov_iter *iter, @@ -1373,7 +1373,7 @@ int smbdirect_connection_send_iter(struct smbdirect_socket *sc, return total_count; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_send_iter); +EXPORT_SYMBOL_GPL(smbdirect_connection_send_iter); static void smbdirect_connection_send_io_done(struct ib_cq *cq, struct ib_wc *wc) { @@ -1937,7 +1937,7 @@ read_rfc1002_done: goto again; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_recvmsg); +EXPORT_SYMBOL_GPL(smbdirect_connection_recvmsg); static bool smbdirect_map_sges_single_page(struct smbdirect_map_sges *state, struct page *page, size_t off, size_t len) @@ -2168,7 +2168,7 @@ static ssize_t smbdirect_map_sges_from_iter(struct iov_iter *iter, size_t len, if (ret < 0) { while (state->num_sge > before) { - struct ib_sge *sge = &state->sge[state->num_sge--]; + struct ib_sge *sge = &state->sge[--state->num_sge]; ib_dma_unmap_page(state->device, sge->addr, diff --git a/fs/smb/common/smbdirect/smbdirect_debug.c b/fs/smb/smbdirect/debug.c similarity index 96% rename from fs/smb/common/smbdirect/smbdirect_debug.c rename to fs/smb/smbdirect/debug.c index d8664fd7f71a..05ba7c8d165e 100644 --- a/fs/smb/common/smbdirect/smbdirect_debug.c +++ b/fs/smb/smbdirect/debug.c @@ -4,7 +4,7 @@ * Copyright (c) 2025, Stefan Metzmacher */ -#include "smbdirect_internal.h" +#include "internal.h" #include void smbdirect_connection_legacy_debug_proc_show(struct smbdirect_socket *sc, @@ -85,4 +85,4 @@ void smbdirect_connection_legacy_debug_proc_show(struct smbdirect_socket *sc, atomic_read(&sc->mr_io.ready.count), atomic_read(&sc->mr_io.used.count)); } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_legacy_debug_proc_show); +EXPORT_SYMBOL_GPL(smbdirect_connection_legacy_debug_proc_show); diff --git a/fs/smb/common/smbdirect/smbdirect_devices.c b/fs/smb/smbdirect/devices.c similarity index 98% rename from fs/smb/common/smbdirect/smbdirect_devices.c rename to fs/smb/smbdirect/devices.c index aaab99e9c045..7adacbdfe12e 100644 --- a/fs/smb/common/smbdirect/smbdirect_devices.c +++ b/fs/smb/smbdirect/devices.c @@ -5,7 +5,7 @@ * Copyright (c) 2025 Stefan Metzmacher */ -#include "smbdirect_internal.h" +#include "internal.h" static u8 smbdirect_ib_device_rdma_capable_node_type(struct ib_device *ib_dev) { @@ -238,7 +238,7 @@ u8 smbdirect_netdev_rdma_capable_node_type(struct net_device *netdev) return RDMA_NODE_UNSPECIFIED; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_netdev_rdma_capable_node_type); +EXPORT_SYMBOL_GPL(smbdirect_netdev_rdma_capable_node_type); __init int smbdirect_devices_init(void) { diff --git a/fs/smb/common/smbdirect/smbdirect_internal.h b/fs/smb/smbdirect/internal.h similarity index 97% rename from fs/smb/common/smbdirect/smbdirect_internal.h rename to fs/smb/smbdirect/internal.h index 30a1b8643657..e9959e6dc13a 100644 --- a/fs/smb/common/smbdirect/smbdirect_internal.h +++ b/fs/smb/smbdirect/internal.h @@ -6,11 +6,11 @@ #ifndef __FS_SMB_COMMON_SMBDIRECT_INTERNAL_H__ #define __FS_SMB_COMMON_SMBDIRECT_INTERNAL_H__ +#define DEFAULT_SYMBOL_NAMESPACE "SMBDIRECT" #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include "smbdirect.h" -#include "smbdirect_pdu.h" -#include "smbdirect_public.h" +#include +#include "pdu.h" #include @@ -34,7 +34,7 @@ struct smbdirect_module_state { extern struct smbdirect_module_state smbdirect_globals; -#include "smbdirect_socket.h" +#include "socket.h" struct smbdirect_device { struct list_head list; diff --git a/fs/smb/common/smbdirect/smbdirect_listen.c b/fs/smb/smbdirect/listen.c similarity index 99% rename from fs/smb/common/smbdirect/smbdirect_listen.c rename to fs/smb/smbdirect/listen.c index 05c7902e7020..2f78bcaedbf8 100644 --- a/fs/smb/common/smbdirect/smbdirect_listen.c +++ b/fs/smb/smbdirect/listen.c @@ -5,7 +5,7 @@ * Copyright (c) 2025, Stefan Metzmacher */ -#include "smbdirect_internal.h" +#include "internal.h" static int smbdirect_listen_rdma_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event); @@ -90,7 +90,7 @@ int smbdirect_socket_listen(struct smbdirect_socket *sc, int backlog) */ return 0; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_listen); +EXPORT_SYMBOL_GPL(smbdirect_socket_listen); static int smbdirect_new_rdma_event_handler(struct rdma_cm_id *new_id, struct rdma_cm_event *event) diff --git a/fs/smb/common/smbdirect/smbdirect_main.c b/fs/smb/smbdirect/main.c similarity index 99% rename from fs/smb/common/smbdirect/smbdirect_main.c rename to fs/smb/smbdirect/main.c index fe6e8d93c34c..606732fefb69 100644 --- a/fs/smb/common/smbdirect/smbdirect_main.c +++ b/fs/smb/smbdirect/main.c @@ -3,7 +3,7 @@ * Copyright (c) 2025, Stefan Metzmacher */ -#include "smbdirect_internal.h" +#include "internal.h" #include struct smbdirect_module_state smbdirect_globals = { diff --git a/fs/smb/common/smbdirect/smbdirect_mr.c b/fs/smb/smbdirect/mr.c similarity index 95% rename from fs/smb/common/smbdirect/smbdirect_mr.c rename to fs/smb/smbdirect/mr.c index fa9be8089925..15c6363a2f97 100644 --- a/fs/smb/common/smbdirect/smbdirect_mr.c +++ b/fs/smb/smbdirect/mr.c @@ -4,7 +4,7 @@ * Copyright (c) 2025, Stefan Metzmacher */ -#include "smbdirect_internal.h" +#include "internal.h" /* * Allocate MRs used for RDMA read/write @@ -269,7 +269,7 @@ smbdirect_connection_register_mr_io(struct smbdirect_socket *sc, { const struct smbdirect_socket_parameters *sp = &sc->parameters; struct smbdirect_mr_io *mr; - int ret, num_pages; + int ret, num_pages, num_mapped; struct ib_reg_wr *reg_wr; num_pages = iov_iter_npages(iter, sp->max_frmr_depth + 1); @@ -300,19 +300,22 @@ smbdirect_connection_register_mr_io(struct smbdirect_socket *sc, num_pages, iov_iter_count(iter), sp->max_frmr_depth); smbdirect_iter_to_sgt(iter, &mr->sgt, sp->max_frmr_depth); - ret = ib_dma_map_sg(sc->ib.dev, mr->sgt.sgl, mr->sgt.nents, mr->dir); - if (!ret) { + num_mapped = ib_dma_map_sg(sc->ib.dev, mr->sgt.sgl, mr->sgt.nents, mr->dir); + if (!num_mapped) { smbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR, - "ib_dma_map_sg num_pages=%u dir=%x ret=%d (%1pe)\n", - num_pages, mr->dir, ret, SMBDIRECT_DEBUG_ERR_PTR(ret)); + "ib_dma_map_sg num_pages=%u dir=%x num_mapped=%d\n", + num_pages, mr->dir, num_mapped); + ret = -EIO; goto dma_map_error; } - ret = ib_map_mr_sg(mr->mr, mr->sgt.sgl, mr->sgt.nents, NULL, PAGE_SIZE); - if (ret != mr->sgt.nents) { + ret = ib_map_mr_sg(mr->mr, mr->sgt.sgl, num_mapped, NULL, PAGE_SIZE); + if (ret != num_mapped) { smbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR, - "ib_map_mr_sg failed ret = %d nents = %u\n", - ret, mr->sgt.nents); + "ib_map_mr_sg failed ret = %d num_mapped = %u\n", + ret, num_mapped); + if (ret >= 0) + ret = -EIO; goto map_mr_error; } @@ -380,7 +383,7 @@ dma_map_error: mutex_unlock(&mr->mutex); return NULL; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_register_mr_io); +EXPORT_SYMBOL_GPL(smbdirect_connection_register_mr_io); void smbdirect_mr_io_fill_buffer_descriptor(struct smbdirect_mr_io *mr, struct smbdirect_buffer_descriptor_v1 *v1) @@ -397,7 +400,7 @@ void smbdirect_mr_io_fill_buffer_descriptor(struct smbdirect_mr_io *mr, } mutex_unlock(&mr->mutex); } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_mr_io_fill_buffer_descriptor); +EXPORT_SYMBOL_GPL(smbdirect_mr_io_fill_buffer_descriptor); /* * Deregister a MR after I/O is done @@ -490,4 +493,4 @@ put_kref: if (!kref_put(&mr->kref, smbdirect_mr_io_free_locked)) mutex_unlock(&mr->mutex); } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_deregister_mr_io); +EXPORT_SYMBOL_GPL(smbdirect_connection_deregister_mr_io); diff --git a/fs/smb/common/smbdirect/smbdirect_pdu.h b/fs/smb/smbdirect/pdu.h similarity index 100% rename from fs/smb/common/smbdirect/smbdirect_pdu.h rename to fs/smb/smbdirect/pdu.h diff --git a/fs/smb/common/smbdirect/smbdirect_rw.c b/fs/smb/smbdirect/rw.c similarity index 98% rename from fs/smb/common/smbdirect/smbdirect_rw.c rename to fs/smb/smbdirect/rw.c index 3b2eb8c48efc..6fe38042cfb9 100644 --- a/fs/smb/common/smbdirect/smbdirect_rw.c +++ b/fs/smb/smbdirect/rw.c @@ -5,7 +5,7 @@ * Copyright (c) 2025, Stefan Metzmacher */ -#include "smbdirect_internal.h" +#include "internal.h" static int smbdirect_connection_wait_for_rw_credits(struct smbdirect_socket *sc, int credits) @@ -252,4 +252,4 @@ free_msg: kfree(msg); goto out; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_rdma_xmit); +EXPORT_SYMBOL_GPL(smbdirect_connection_rdma_xmit); diff --git a/fs/smb/common/smbdirect/smbdirect_socket.c b/fs/smb/smbdirect/socket.c similarity index 96% rename from fs/smb/common/smbdirect/smbdirect_socket.c rename to fs/smb/smbdirect/socket.c index 9153e1dbf53d..39cca7219c4d 100644 --- a/fs/smb/common/smbdirect/smbdirect_socket.c +++ b/fs/smb/smbdirect/socket.c @@ -4,7 +4,7 @@ * Copyright (c) 2025, Stefan Metzmacher */ -#include "smbdirect_internal.h" +#include "internal.h" bool smbdirect_frwr_is_supported(const struct ib_device_attr *attrs) { @@ -20,7 +20,7 @@ bool smbdirect_frwr_is_supported(const struct ib_device_attr *attrs) return false; return true; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_frwr_is_supported); +EXPORT_SYMBOL_GPL(smbdirect_frwr_is_supported); static void smbdirect_socket_cleanup_work(struct work_struct *work); @@ -107,7 +107,7 @@ init_failed: alloc_failed: return ret; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_create_kern); +EXPORT_SYMBOL_GPL(smbdirect_socket_create_kern); int smbdirect_socket_init_accepting(struct rdma_cm_id *id, struct smbdirect_socket *sc) { @@ -148,7 +148,7 @@ init_failed: alloc_failed: return ret; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_create_accepting); +EXPORT_SYMBOL_GPL(smbdirect_socket_create_accepting); int smbdirect_socket_set_initial_parameters(struct smbdirect_socket *sc, const struct smbdirect_socket_parameters *sp) @@ -189,14 +189,14 @@ int smbdirect_socket_set_initial_parameters(struct smbdirect_socket *sc, return 0; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_set_initial_parameters); +EXPORT_SYMBOL_GPL(smbdirect_socket_set_initial_parameters); const struct smbdirect_socket_parameters * smbdirect_socket_get_current_parameters(struct smbdirect_socket *sc) { return &sc->parameters; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_get_current_parameters); +EXPORT_SYMBOL_GPL(smbdirect_socket_get_current_parameters); int smbdirect_socket_set_kernel_settings(struct smbdirect_socket *sc, enum ib_poll_context poll_ctx, @@ -220,7 +220,7 @@ int smbdirect_socket_set_kernel_settings(struct smbdirect_socket *sc, return 0; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_set_kernel_settings); +EXPORT_SYMBOL_GPL(smbdirect_socket_set_kernel_settings); void smbdirect_socket_set_logging(struct smbdirect_socket *sc, void *private_ptr, @@ -240,7 +240,7 @@ void smbdirect_socket_set_logging(struct smbdirect_socket *sc, sc->logging.needed = needed; sc->logging.vaprintf = vaprintf; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_set_logging); +EXPORT_SYMBOL_GPL(smbdirect_socket_set_logging); static void smbdirect_socket_wake_up_all(struct smbdirect_socket *sc) { @@ -663,13 +663,13 @@ int smbdirect_socket_bind(struct smbdirect_socket *sc, struct sockaddr *addr) return 0; } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_bind); +EXPORT_SYMBOL_GPL(smbdirect_socket_bind); void smbdirect_socket_shutdown(struct smbdirect_socket *sc) { smbdirect_socket_schedule_cleanup_lvl(sc, SMBDIRECT_LOG_INFO, -ESHUTDOWN); } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_shutdown); +EXPORT_SYMBOL_GPL(smbdirect_socket_shutdown); static void smbdirect_socket_release_disconnect(struct kref *kref) { @@ -712,7 +712,7 @@ void smbdirect_socket_release(struct smbdirect_socket *sc) */ kref_put(&sc->refs.destroy, smbdirect_socket_release_destroy); } -__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_release); +EXPORT_SYMBOL_GPL(smbdirect_socket_release); int smbdirect_socket_wait_for_credits(struct smbdirect_socket *sc, enum smbdirect_socket_status expected_status, diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/smbdirect/socket.h similarity index 100% rename from fs/smb/common/smbdirect/smbdirect_socket.h rename to fs/smb/smbdirect/socket.h diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index b3edae0578c0..182e54e575ee 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -517,8 +517,11 @@ static int sysfs_group_attrs_change_owner(struct kobject *kobj, struct attribute *const *attr; for (i = 0, attr = grp->attrs; *attr; i++, attr++) { - if (grp->is_visible) { - mode = grp->is_visible(kobj, *attr, i); + if (grp->is_visible || grp->is_visible_const) { + if (grp->is_visible) + mode = grp->is_visible(kobj, *attr, i); + else + mode = grp->is_visible_const(kobj, *attr, i); if (mode & SYSFS_GROUP_INVISIBLE) break; if (!mode) diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index 81df94038f2e..26b6453de30e 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -244,6 +244,8 @@ static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t { struct eventfs_inode *ei_child; + lockdep_assert_held(&eventfs_mutex); + /* Update events// */ if (WARN_ON_ONCE(level > 3)) return; @@ -706,7 +708,7 @@ struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode scoped_guard(mutex, &eventfs_mutex) { if (!parent->is_freed) - list_add_tail(&ei->list, &parent->children); + list_add_tail_rcu(&ei->list, &parent->children); } /* Was the parent freed? */ if (list_empty(&ei->list)) { @@ -886,3 +888,15 @@ void eventfs_remove_events_dir(struct eventfs_inode *ei) d_invalidate(dentry); d_make_discardable(dentry); } + +int eventfs_remount_lock(void) +{ + mutex_lock(&eventfs_mutex); + return srcu_read_lock(&eventfs_srcu); +} + +void eventfs_remount_unlock(int srcu_idx) +{ + srcu_read_unlock(&eventfs_srcu, srcu_idx); + mutex_unlock(&eventfs_mutex); +} diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index 03f768536fd5..f3d6188a3b7b 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -313,6 +313,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount) struct inode *inode = d_inode(sb->s_root); struct tracefs_inode *ti; bool update_uid, update_gid; + int srcu_idx; umode_t tmp_mode; /* @@ -337,6 +338,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount) update_uid = fsi->opts & BIT(Opt_uid); update_gid = fsi->opts & BIT(Opt_gid); + srcu_idx = eventfs_remount_lock(); rcu_read_lock(); list_for_each_entry_rcu(ti, &tracefs_inodes, list) { if (update_uid) { @@ -358,6 +360,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount) eventfs_remount(ti, update_uid, update_gid); } rcu_read_unlock(); + eventfs_remount_unlock(srcu_idx); } return 0; @@ -403,7 +406,7 @@ static int tracefs_drop_inode(struct inode *inode) * This inode is being freed and cannot be used for * eventfs. Clear the flag so that it doesn't call into * eventfs during the remount flag updates. The eventfs_inode - * gets freed after an RCU cycle, so the content will still + * gets freed after an SRCU cycle, so the content will still * be safe if the iteration is going on now. */ ti->flags &= ~TRACEFS_EVENT_INODE; diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h index d83c2a25f288..a4a7f8431aff 100644 --- a/fs/tracefs/internal.h +++ b/fs/tracefs/internal.h @@ -76,4 +76,7 @@ struct inode *tracefs_get_inode(struct super_block *sb); void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid); void eventfs_d_release(struct dentry *dentry); +int eventfs_remount_lock(void); +void eventfs_remount_unlock(int srcu_idx); + #endif /* _TRACEFS_INTERNAL_H */ diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index 0bf08b7755b8..933c79b5cd6b 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c @@ -109,7 +109,6 @@ static int data_nodes_cmp(void *priv, const struct list_head *a, struct ubifs_info *c = priv; struct ubifs_scan_node *sa, *sb; - cond_resched(); if (a == b) return 0; @@ -153,7 +152,6 @@ static int nondata_nodes_cmp(void *priv, const struct list_head *a, struct ubifs_info *c = priv; struct ubifs_scan_node *sa, *sb; - cond_resched(); if (a == b) return 0; diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index a9a568f4a868..263045e05cf1 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -305,7 +305,6 @@ static int replay_entries_cmp(void *priv, const struct list_head *a, struct ubifs_info *c = priv; struct replay_entry *ra, *rb; - cond_resched(); if (a == b) return 0; diff --git a/fs/udf/misc.c b/fs/udf/misc.c index 0788593b6a1d..6928e378fbbd 100644 --- a/fs/udf/misc.c +++ b/fs/udf/misc.c @@ -230,8 +230,12 @@ struct buffer_head *udf_read_tagged(struct super_block *sb, uint32_t block, } /* Verify the descriptor CRC */ - if (le16_to_cpu(tag_p->descCRCLength) + sizeof(struct tag) > sb->s_blocksize || - le16_to_cpu(tag_p->descCRC) == crc_itu_t(0, + if (le16_to_cpu(tag_p->descCRCLength) + sizeof(struct tag) > sb->s_blocksize) { + udf_err(sb, "block %u: CRC length %u exceeds block size\n", + block, le16_to_cpu(tag_p->descCRCLength)); + goto error_out; + } + if (le16_to_cpu(tag_p->descCRC) == crc_itu_t(0, bh->b_data + sizeof(struct tag), le16_to_cpu(tag_p->descCRCLength))) return bh; diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index bdc84e5219cd..4b53dc4a3266 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -1238,8 +1238,6 @@ static __always_inline int validate_unaligned_range( return -EINVAL; if (!len) return -EINVAL; - if (start < mmap_min_addr) - return -EINVAL; if (start >= task_size) return -EINVAL; if (len > task_size - start) diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c index 80ba94f51e5c..aecbab61014c 100644 --- a/fs/xfs/libxfs/xfs_dir2_data.c +++ b/fs/xfs/libxfs/xfs_dir2_data.c @@ -382,6 +382,7 @@ xfs_dir3_data_write_verify( struct xfs_mount *mp = bp->b_mount; struct xfs_buf_log_item *bip = bp->b_log_item; struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr; + struct xfs_dir3_data_hdr *datahdr3 = bp->b_addr; xfs_failaddr_t fa; fa = xfs_dir3_data_verify(bp); @@ -396,6 +397,11 @@ xfs_dir3_data_write_verify( if (bip) hdr3->lsn = cpu_to_be64(bip->bli_item.li_lsn); + /* + * Zero padding that may be stale from old kernels. + */ + datahdr3->pad = 0; + xfs_buf_update_cksum(bp, XFS_DIR3_DATA_CRC_OFF); } @@ -728,7 +734,6 @@ xfs_dir3_data_init( struct xfs_dir2_data_unused *dup; struct xfs_dir2_data_free *bf; int error; - int i; /* * Get the buffer set up for the block. @@ -741,13 +746,16 @@ xfs_dir3_data_init( xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DIR_DATA_BUF); /* - * Initialize the header. + * Initialize the whole directory header region to zero + * so that all padding, bestfree entries, and any + * future header fields are clean. */ hdr = bp->b_addr; + memset(hdr, 0, geo->data_entry_offset); + if (xfs_has_crc(mp)) { struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr; - memset(hdr3, 0, sizeof(*hdr3)); hdr3->magic = cpu_to_be32(XFS_DIR3_DATA_MAGIC); hdr3->blkno = cpu_to_be64(xfs_buf_daddr(bp)); hdr3->owner = cpu_to_be64(args->owner); @@ -759,10 +767,6 @@ xfs_dir3_data_init( bf = xfs_dir2_data_bestfree_p(mp, hdr); bf[0].offset = cpu_to_be16(geo->data_entry_offset); bf[0].length = cpu_to_be16(geo->blksize - geo->data_entry_offset); - for (i = 1; i < XFS_DIR2_DATA_FD_COUNT; i++) { - bf[i].length = 0; - bf[i].offset = 0; - } /* * Set up an unused entry for the block's body. diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c index 40c7f0ff6cf3..0ec6ccd8b4dc 100644 --- a/fs/xfs/libxfs/xfs_refcount.c +++ b/fs/xfs/libxfs/xfs_refcount.c @@ -1414,8 +1414,7 @@ xfs_refcount_finish_one( if (rcur == NULL) { struct xfs_perag *pag = to_perag(ri->ri_group); - error = xfs_alloc_read_agf(pag, tp, - XFS_ALLOC_FLAG_FREEING, &agbp); + error = xfs_alloc_read_agf(pag, tp, 0, &agbp); if (error) return error; diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c index 20e63069088b..3d40cb0b2496 100644 --- a/fs/xfs/scrub/common.c +++ b/fs/xfs/scrub/common.c @@ -251,6 +251,17 @@ xchk_ino_set_preen( trace_xchk_ino_preen(sc, ino, __return_address); } +/* Record a block indexed by a file fork that could be optimized. */ +void +xchk_fblock_set_preen( + struct xfs_scrub *sc, + int whichfork, + xfs_fileoff_t offset) +{ + sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN; + trace_xchk_fblock_preen(sc, whichfork, offset, __return_address); +} + /* Record something being wrong with the filesystem primary superblock. */ void xchk_set_corrupt( diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h index f2ecc68538f0..b494d747c008 100644 --- a/fs/xfs/scrub/common.h +++ b/fs/xfs/scrub/common.h @@ -25,6 +25,8 @@ bool xchk_fblock_xref_process_error(struct xfs_scrub *sc, void xchk_block_set_preen(struct xfs_scrub *sc, struct xfs_buf *bp); void xchk_ino_set_preen(struct xfs_scrub *sc, xfs_ino_t ino); +void xchk_fblock_set_preen(struct xfs_scrub *sc, + int whichfork, xfs_fileoff_t offset); void xchk_set_corrupt(struct xfs_scrub *sc); void xchk_block_set_corrupt(struct xfs_scrub *sc, diff --git a/fs/xfs/scrub/dabtree.c b/fs/xfs/scrub/dabtree.c index 1a71d36898b1..c2d6ad59d03e 100644 --- a/fs/xfs/scrub/dabtree.c +++ b/fs/xfs/scrub/dabtree.c @@ -454,7 +454,12 @@ xchk_da_btree_block( } } - /* XXX: Check hdr3.pad32 once we know how to fix it. */ + if (xfs_has_crc(ip->i_mount)) { + struct xfs_da3_node_hdr *nodehdr3 = blk->bp->b_addr; + + if (nodehdr3->__pad32) + xchk_da_set_preen(ds, level); + } break; default: xchk_da_set_corrupt(ds, level); diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c index e09724cd3725..09715a4aa154 100644 --- a/fs/xfs/scrub/dir.c +++ b/fs/xfs/scrub/dir.c @@ -492,7 +492,12 @@ xchk_directory_data_bestfree( goto out; xchk_buffer_recheck(sc, bp); - /* XXX: Check xfs_dir3_data_hdr.pad is zero once we start setting it. */ + if (xfs_has_crc(sc->mp)) { + struct xfs_dir3_data_hdr *hdr3 = bp->b_addr; + + if (hdr3->pad) + xchk_fblock_set_preen(sc, XFS_DATA_FORK, lblk); + } if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) goto out_buf; diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index beaa26ec62da..9978ac1422fc 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -699,12 +699,6 @@ xfs_create( */ error = xfs_trans_alloc_icreate(mp, tres, udqp, gdqp, pdqp, resblks, &tp); - if (error == -ENOSPC) { - /* flush outstanding delalloc blocks and retry */ - xfs_flush_inodes(mp); - error = xfs_trans_alloc_icreate(mp, tres, udqp, gdqp, pdqp, - resblks, &tp); - } if (error) goto out_parent; diff --git a/fs/xfs/xfs_notify_failure.c b/fs/xfs/xfs_notify_failure.c index 64c8afb935c2..b994ff15d5e4 100644 --- a/fs/xfs/xfs_notify_failure.c +++ b/fs/xfs/xfs_notify_failure.c @@ -350,7 +350,7 @@ xfs_dax_notify_dev_failure( /* * Shutdown fs from a force umount in pre-remove case which won't fail, * so errors can be ignored. Otherwise, shutdown the filesystem with - * CORRUPT flag if error occured or notify.want_shutdown was set during + * CORRUPT flag if error occurred or notify.want_shutdown was set during * RMAP querying. */ if (mf_flags & MF_MEM_PRE_REMOVE) diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index bcc470f56e46..148cc32449c1 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -1199,10 +1199,21 @@ xfs_trans_alloc_icreate( { struct xfs_trans *tp; bool retried = false; + bool flushed = false; int error; retry: error = xfs_trans_alloc(mp, resv, dblocks, 0, 0, &tp); + if (error == -ENOSPC && !flushed) { + /* + * Flush all delalloc blocks to reclaim space from speculative + * preallocation. This is similar to the quota retry below + * but targets FS-wide ENOSPC. + */ + xfs_flush_inodes(mp); + flushed = true; + goto retry; + } if (error) return error; diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c index a851b98143c0..5e297b75a85f 100644 --- a/fs/xfs/xfs_zone_alloc.c +++ b/fs/xfs/xfs_zone_alloc.c @@ -1170,7 +1170,7 @@ xfs_calc_open_zones( if (bdev_open_zones && bdev_open_zones < mp->m_max_open_zones) { mp->m_max_open_zones = bdev_open_zones; - xfs_info(mp, "limiting open zones to %u due to hardware limit.\n", + xfs_info(mp, "limiting open zones to %u due to hardware limit.", bdev_open_zones); } @@ -1217,7 +1217,7 @@ xfs_alloc_zone_info( return zi; out_free_bitmaps: - while (--i > 0) + while (--i >= 0) kvfree(zi->zi_used_bucket_bitmap[i]); kfree(zi); return NULL; diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c index fedcc47048af..c8a1d5c0332c 100644 --- a/fs/xfs/xfs_zone_gc.c +++ b/fs/xfs/xfs_zone_gc.c @@ -1221,7 +1221,7 @@ out_put_oz: if (data->oz) xfs_open_zone_put(data->oz); out_free_gc_data: - kfree(data); + xfs_zone_gc_data_free(data); return error; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index b701b5f972cb..c41d9a7565cf 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -17,6 +17,8 @@ #include #include +struct notifier_block; + struct acpi_handle_list { u32 count; acpi_handle *handles; diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 4e15583e0d25..f72e00517eb3 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -1386,6 +1386,12 @@ enum acpi_einj_command_status { #define ACPI_EINJ_CXL_MEM_FATAL (1<<17) #define ACPI_EINJ_VENDOR_DEFINED (1<<31) +/* EINJV2 error types from EINJV2_GET_ERROR_TYPE (ACPI 6.6) */ + +#define ACPI_EINJV2_PROCESSOR (1) +#define ACPI_EINJV2_MEMORY (1<<1) +#define ACPI_EINJV2_PCIE (1<<2) + /******************************************************************************* * * ERST - Error Record Serialization Table (ACPI 4.0) diff --git a/include/asm-generic/Kbuild b/include/asm-generic/Kbuild index 9aff61e7b8f2..2c53a1e0b760 100644 --- a/include/asm-generic/Kbuild +++ b/include/asm-generic/Kbuild @@ -65,4 +65,3 @@ mandatory-y += vermagic.h mandatory-y += vga.h mandatory-y += video.h mandatory-y += word-at-a-time.h -mandatory-y += xor.h diff --git a/include/asm-generic/kprobes.h b/include/asm-generic/kprobes.h index 060eab094e5a..5290a2b2e15a 100644 --- a/include/asm-generic/kprobes.h +++ b/include/asm-generic/kprobes.h @@ -14,7 +14,7 @@ static unsigned long __used \ _kbl_addr_##fname = (unsigned long)fname; # define NOKPROBE_SYMBOL(fname) __NOKPROBE_SYMBOL(fname) /* Use this to forbid a kprobes attach on very low level functions */ -# define __kprobes __section(".kprobes.text") +# define __kprobes notrace __section(".kprobes.text") # define nokprobe_inline __always_inline #else # define NOKPROBE_SYMBOL(fname) diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index d37b68238c97..bf601d67cecb 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -347,6 +347,8 @@ bool hv_result_needs_memory(u64 status); int hv_deposit_memory_node(int node, u64 partition_id, u64 status); int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages); int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id); +int hv_call_notify_all_processors_started(void); +bool hv_lp_exists(u32 lp_index); int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags); #else /* CONFIG_MSHV_ROOT */ @@ -366,6 +368,14 @@ static inline int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id) { return -EOPNOTSUPP; } +static inline int hv_call_notify_all_processors_started(void) +{ + return -EOPNOTSUPP; +} +static inline bool hv_lp_exists(u32 lp_index) +{ + return false; +} static inline int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags) { return -EOPNOTSUPP; diff --git a/include/asm-generic/xor.h b/include/asm-generic/xor.h deleted file mode 100644 index 44509d48fca2..000000000000 --- a/include/asm-generic/xor.h +++ /dev/null @@ -1,738 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * include/asm-generic/xor.h - * - * Generic optimized RAID-5 checksumming functions. - */ - -#include - -static void -xor_8regs_2(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2) -{ - long lines = bytes / (sizeof (long)) / 8; - - do { - p1[0] ^= p2[0]; - p1[1] ^= p2[1]; - p1[2] ^= p2[2]; - p1[3] ^= p2[3]; - p1[4] ^= p2[4]; - p1[5] ^= p2[5]; - p1[6] ^= p2[6]; - p1[7] ^= p2[7]; - p1 += 8; - p2 += 8; - } while (--lines > 0); -} - -static void -xor_8regs_3(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3) -{ - long lines = bytes / (sizeof (long)) / 8; - - do { - p1[0] ^= p2[0] ^ p3[0]; - p1[1] ^= p2[1] ^ p3[1]; - p1[2] ^= p2[2] ^ p3[2]; - p1[3] ^= p2[3] ^ p3[3]; - p1[4] ^= p2[4] ^ p3[4]; - p1[5] ^= p2[5] ^ p3[5]; - p1[6] ^= p2[6] ^ p3[6]; - p1[7] ^= p2[7] ^ p3[7]; - p1 += 8; - p2 += 8; - p3 += 8; - } while (--lines > 0); -} - -static void -xor_8regs_4(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4) -{ - long lines = bytes / (sizeof (long)) / 8; - - do { - p1[0] ^= p2[0] ^ p3[0] ^ p4[0]; - p1[1] ^= p2[1] ^ p3[1] ^ p4[1]; - p1[2] ^= p2[2] ^ p3[2] ^ p4[2]; - p1[3] ^= p2[3] ^ p3[3] ^ p4[3]; - p1[4] ^= p2[4] ^ p3[4] ^ p4[4]; - p1[5] ^= p2[5] ^ p3[5] ^ p4[5]; - p1[6] ^= p2[6] ^ p3[6] ^ p4[6]; - p1[7] ^= p2[7] ^ p3[7] ^ p4[7]; - p1 += 8; - p2 += 8; - p3 += 8; - p4 += 8; - } while (--lines > 0); -} - -static void -xor_8regs_5(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4, - const unsigned long * __restrict p5) -{ - long lines = bytes / (sizeof (long)) / 8; - - do { - p1[0] ^= p2[0] ^ p3[0] ^ p4[0] ^ p5[0]; - p1[1] ^= p2[1] ^ p3[1] ^ p4[1] ^ p5[1]; - p1[2] ^= p2[2] ^ p3[2] ^ p4[2] ^ p5[2]; - p1[3] ^= p2[3] ^ p3[3] ^ p4[3] ^ p5[3]; - p1[4] ^= p2[4] ^ p3[4] ^ p4[4] ^ p5[4]; - p1[5] ^= p2[5] ^ p3[5] ^ p4[5] ^ p5[5]; - p1[6] ^= p2[6] ^ p3[6] ^ p4[6] ^ p5[6]; - p1[7] ^= p2[7] ^ p3[7] ^ p4[7] ^ p5[7]; - p1 += 8; - p2 += 8; - p3 += 8; - p4 += 8; - p5 += 8; - } while (--lines > 0); -} - -static void -xor_32regs_2(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2) -{ - long lines = bytes / (sizeof (long)) / 8; - - do { - register long d0, d1, d2, d3, d4, d5, d6, d7; - d0 = p1[0]; /* Pull the stuff into registers */ - d1 = p1[1]; /* ... in bursts, if possible. */ - d2 = p1[2]; - d3 = p1[3]; - d4 = p1[4]; - d5 = p1[5]; - d6 = p1[6]; - d7 = p1[7]; - d0 ^= p2[0]; - d1 ^= p2[1]; - d2 ^= p2[2]; - d3 ^= p2[3]; - d4 ^= p2[4]; - d5 ^= p2[5]; - d6 ^= p2[6]; - d7 ^= p2[7]; - p1[0] = d0; /* Store the result (in bursts) */ - p1[1] = d1; - p1[2] = d2; - p1[3] = d3; - p1[4] = d4; - p1[5] = d5; - p1[6] = d6; - p1[7] = d7; - p1 += 8; - p2 += 8; - } while (--lines > 0); -} - -static void -xor_32regs_3(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3) -{ - long lines = bytes / (sizeof (long)) / 8; - - do { - register long d0, d1, d2, d3, d4, d5, d6, d7; - d0 = p1[0]; /* Pull the stuff into registers */ - d1 = p1[1]; /* ... in bursts, if possible. */ - d2 = p1[2]; - d3 = p1[3]; - d4 = p1[4]; - d5 = p1[5]; - d6 = p1[6]; - d7 = p1[7]; - d0 ^= p2[0]; - d1 ^= p2[1]; - d2 ^= p2[2]; - d3 ^= p2[3]; - d4 ^= p2[4]; - d5 ^= p2[5]; - d6 ^= p2[6]; - d7 ^= p2[7]; - d0 ^= p3[0]; - d1 ^= p3[1]; - d2 ^= p3[2]; - d3 ^= p3[3]; - d4 ^= p3[4]; - d5 ^= p3[5]; - d6 ^= p3[6]; - d7 ^= p3[7]; - p1[0] = d0; /* Store the result (in bursts) */ - p1[1] = d1; - p1[2] = d2; - p1[3] = d3; - p1[4] = d4; - p1[5] = d5; - p1[6] = d6; - p1[7] = d7; - p1 += 8; - p2 += 8; - p3 += 8; - } while (--lines > 0); -} - -static void -xor_32regs_4(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4) -{ - long lines = bytes / (sizeof (long)) / 8; - - do { - register long d0, d1, d2, d3, d4, d5, d6, d7; - d0 = p1[0]; /* Pull the stuff into registers */ - d1 = p1[1]; /* ... in bursts, if possible. */ - d2 = p1[2]; - d3 = p1[3]; - d4 = p1[4]; - d5 = p1[5]; - d6 = p1[6]; - d7 = p1[7]; - d0 ^= p2[0]; - d1 ^= p2[1]; - d2 ^= p2[2]; - d3 ^= p2[3]; - d4 ^= p2[4]; - d5 ^= p2[5]; - d6 ^= p2[6]; - d7 ^= p2[7]; - d0 ^= p3[0]; - d1 ^= p3[1]; - d2 ^= p3[2]; - d3 ^= p3[3]; - d4 ^= p3[4]; - d5 ^= p3[5]; - d6 ^= p3[6]; - d7 ^= p3[7]; - d0 ^= p4[0]; - d1 ^= p4[1]; - d2 ^= p4[2]; - d3 ^= p4[3]; - d4 ^= p4[4]; - d5 ^= p4[5]; - d6 ^= p4[6]; - d7 ^= p4[7]; - p1[0] = d0; /* Store the result (in bursts) */ - p1[1] = d1; - p1[2] = d2; - p1[3] = d3; - p1[4] = d4; - p1[5] = d5; - p1[6] = d6; - p1[7] = d7; - p1 += 8; - p2 += 8; - p3 += 8; - p4 += 8; - } while (--lines > 0); -} - -static void -xor_32regs_5(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4, - const unsigned long * __restrict p5) -{ - long lines = bytes / (sizeof (long)) / 8; - - do { - register long d0, d1, d2, d3, d4, d5, d6, d7; - d0 = p1[0]; /* Pull the stuff into registers */ - d1 = p1[1]; /* ... in bursts, if possible. */ - d2 = p1[2]; - d3 = p1[3]; - d4 = p1[4]; - d5 = p1[5]; - d6 = p1[6]; - d7 = p1[7]; - d0 ^= p2[0]; - d1 ^= p2[1]; - d2 ^= p2[2]; - d3 ^= p2[3]; - d4 ^= p2[4]; - d5 ^= p2[5]; - d6 ^= p2[6]; - d7 ^= p2[7]; - d0 ^= p3[0]; - d1 ^= p3[1]; - d2 ^= p3[2]; - d3 ^= p3[3]; - d4 ^= p3[4]; - d5 ^= p3[5]; - d6 ^= p3[6]; - d7 ^= p3[7]; - d0 ^= p4[0]; - d1 ^= p4[1]; - d2 ^= p4[2]; - d3 ^= p4[3]; - d4 ^= p4[4]; - d5 ^= p4[5]; - d6 ^= p4[6]; - d7 ^= p4[7]; - d0 ^= p5[0]; - d1 ^= p5[1]; - d2 ^= p5[2]; - d3 ^= p5[3]; - d4 ^= p5[4]; - d5 ^= p5[5]; - d6 ^= p5[6]; - d7 ^= p5[7]; - p1[0] = d0; /* Store the result (in bursts) */ - p1[1] = d1; - p1[2] = d2; - p1[3] = d3; - p1[4] = d4; - p1[5] = d5; - p1[6] = d6; - p1[7] = d7; - p1 += 8; - p2 += 8; - p3 += 8; - p4 += 8; - p5 += 8; - } while (--lines > 0); -} - -static void -xor_8regs_p_2(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2) -{ - long lines = bytes / (sizeof (long)) / 8 - 1; - prefetchw(p1); - prefetch(p2); - - do { - prefetchw(p1+8); - prefetch(p2+8); - once_more: - p1[0] ^= p2[0]; - p1[1] ^= p2[1]; - p1[2] ^= p2[2]; - p1[3] ^= p2[3]; - p1[4] ^= p2[4]; - p1[5] ^= p2[5]; - p1[6] ^= p2[6]; - p1[7] ^= p2[7]; - p1 += 8; - p2 += 8; - } while (--lines > 0); - if (lines == 0) - goto once_more; -} - -static void -xor_8regs_p_3(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3) -{ - long lines = bytes / (sizeof (long)) / 8 - 1; - prefetchw(p1); - prefetch(p2); - prefetch(p3); - - do { - prefetchw(p1+8); - prefetch(p2+8); - prefetch(p3+8); - once_more: - p1[0] ^= p2[0] ^ p3[0]; - p1[1] ^= p2[1] ^ p3[1]; - p1[2] ^= p2[2] ^ p3[2]; - p1[3] ^= p2[3] ^ p3[3]; - p1[4] ^= p2[4] ^ p3[4]; - p1[5] ^= p2[5] ^ p3[5]; - p1[6] ^= p2[6] ^ p3[6]; - p1[7] ^= p2[7] ^ p3[7]; - p1 += 8; - p2 += 8; - p3 += 8; - } while (--lines > 0); - if (lines == 0) - goto once_more; -} - -static void -xor_8regs_p_4(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4) -{ - long lines = bytes / (sizeof (long)) / 8 - 1; - - prefetchw(p1); - prefetch(p2); - prefetch(p3); - prefetch(p4); - - do { - prefetchw(p1+8); - prefetch(p2+8); - prefetch(p3+8); - prefetch(p4+8); - once_more: - p1[0] ^= p2[0] ^ p3[0] ^ p4[0]; - p1[1] ^= p2[1] ^ p3[1] ^ p4[1]; - p1[2] ^= p2[2] ^ p3[2] ^ p4[2]; - p1[3] ^= p2[3] ^ p3[3] ^ p4[3]; - p1[4] ^= p2[4] ^ p3[4] ^ p4[4]; - p1[5] ^= p2[5] ^ p3[5] ^ p4[5]; - p1[6] ^= p2[6] ^ p3[6] ^ p4[6]; - p1[7] ^= p2[7] ^ p3[7] ^ p4[7]; - p1 += 8; - p2 += 8; - p3 += 8; - p4 += 8; - } while (--lines > 0); - if (lines == 0) - goto once_more; -} - -static void -xor_8regs_p_5(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4, - const unsigned long * __restrict p5) -{ - long lines = bytes / (sizeof (long)) / 8 - 1; - - prefetchw(p1); - prefetch(p2); - prefetch(p3); - prefetch(p4); - prefetch(p5); - - do { - prefetchw(p1+8); - prefetch(p2+8); - prefetch(p3+8); - prefetch(p4+8); - prefetch(p5+8); - once_more: - p1[0] ^= p2[0] ^ p3[0] ^ p4[0] ^ p5[0]; - p1[1] ^= p2[1] ^ p3[1] ^ p4[1] ^ p5[1]; - p1[2] ^= p2[2] ^ p3[2] ^ p4[2] ^ p5[2]; - p1[3] ^= p2[3] ^ p3[3] ^ p4[3] ^ p5[3]; - p1[4] ^= p2[4] ^ p3[4] ^ p4[4] ^ p5[4]; - p1[5] ^= p2[5] ^ p3[5] ^ p4[5] ^ p5[5]; - p1[6] ^= p2[6] ^ p3[6] ^ p4[6] ^ p5[6]; - p1[7] ^= p2[7] ^ p3[7] ^ p4[7] ^ p5[7]; - p1 += 8; - p2 += 8; - p3 += 8; - p4 += 8; - p5 += 8; - } while (--lines > 0); - if (lines == 0) - goto once_more; -} - -static void -xor_32regs_p_2(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2) -{ - long lines = bytes / (sizeof (long)) / 8 - 1; - - prefetchw(p1); - prefetch(p2); - - do { - register long d0, d1, d2, d3, d4, d5, d6, d7; - - prefetchw(p1+8); - prefetch(p2+8); - once_more: - d0 = p1[0]; /* Pull the stuff into registers */ - d1 = p1[1]; /* ... in bursts, if possible. */ - d2 = p1[2]; - d3 = p1[3]; - d4 = p1[4]; - d5 = p1[5]; - d6 = p1[6]; - d7 = p1[7]; - d0 ^= p2[0]; - d1 ^= p2[1]; - d2 ^= p2[2]; - d3 ^= p2[3]; - d4 ^= p2[4]; - d5 ^= p2[5]; - d6 ^= p2[6]; - d7 ^= p2[7]; - p1[0] = d0; /* Store the result (in bursts) */ - p1[1] = d1; - p1[2] = d2; - p1[3] = d3; - p1[4] = d4; - p1[5] = d5; - p1[6] = d6; - p1[7] = d7; - p1 += 8; - p2 += 8; - } while (--lines > 0); - if (lines == 0) - goto once_more; -} - -static void -xor_32regs_p_3(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3) -{ - long lines = bytes / (sizeof (long)) / 8 - 1; - - prefetchw(p1); - prefetch(p2); - prefetch(p3); - - do { - register long d0, d1, d2, d3, d4, d5, d6, d7; - - prefetchw(p1+8); - prefetch(p2+8); - prefetch(p3+8); - once_more: - d0 = p1[0]; /* Pull the stuff into registers */ - d1 = p1[1]; /* ... in bursts, if possible. */ - d2 = p1[2]; - d3 = p1[3]; - d4 = p1[4]; - d5 = p1[5]; - d6 = p1[6]; - d7 = p1[7]; - d0 ^= p2[0]; - d1 ^= p2[1]; - d2 ^= p2[2]; - d3 ^= p2[3]; - d4 ^= p2[4]; - d5 ^= p2[5]; - d6 ^= p2[6]; - d7 ^= p2[7]; - d0 ^= p3[0]; - d1 ^= p3[1]; - d2 ^= p3[2]; - d3 ^= p3[3]; - d4 ^= p3[4]; - d5 ^= p3[5]; - d6 ^= p3[6]; - d7 ^= p3[7]; - p1[0] = d0; /* Store the result (in bursts) */ - p1[1] = d1; - p1[2] = d2; - p1[3] = d3; - p1[4] = d4; - p1[5] = d5; - p1[6] = d6; - p1[7] = d7; - p1 += 8; - p2 += 8; - p3 += 8; - } while (--lines > 0); - if (lines == 0) - goto once_more; -} - -static void -xor_32regs_p_4(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4) -{ - long lines = bytes / (sizeof (long)) / 8 - 1; - - prefetchw(p1); - prefetch(p2); - prefetch(p3); - prefetch(p4); - - do { - register long d0, d1, d2, d3, d4, d5, d6, d7; - - prefetchw(p1+8); - prefetch(p2+8); - prefetch(p3+8); - prefetch(p4+8); - once_more: - d0 = p1[0]; /* Pull the stuff into registers */ - d1 = p1[1]; /* ... in bursts, if possible. */ - d2 = p1[2]; - d3 = p1[3]; - d4 = p1[4]; - d5 = p1[5]; - d6 = p1[6]; - d7 = p1[7]; - d0 ^= p2[0]; - d1 ^= p2[1]; - d2 ^= p2[2]; - d3 ^= p2[3]; - d4 ^= p2[4]; - d5 ^= p2[5]; - d6 ^= p2[6]; - d7 ^= p2[7]; - d0 ^= p3[0]; - d1 ^= p3[1]; - d2 ^= p3[2]; - d3 ^= p3[3]; - d4 ^= p3[4]; - d5 ^= p3[5]; - d6 ^= p3[6]; - d7 ^= p3[7]; - d0 ^= p4[0]; - d1 ^= p4[1]; - d2 ^= p4[2]; - d3 ^= p4[3]; - d4 ^= p4[4]; - d5 ^= p4[5]; - d6 ^= p4[6]; - d7 ^= p4[7]; - p1[0] = d0; /* Store the result (in bursts) */ - p1[1] = d1; - p1[2] = d2; - p1[3] = d3; - p1[4] = d4; - p1[5] = d5; - p1[6] = d6; - p1[7] = d7; - p1 += 8; - p2 += 8; - p3 += 8; - p4 += 8; - } while (--lines > 0); - if (lines == 0) - goto once_more; -} - -static void -xor_32regs_p_5(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4, - const unsigned long * __restrict p5) -{ - long lines = bytes / (sizeof (long)) / 8 - 1; - - prefetchw(p1); - prefetch(p2); - prefetch(p3); - prefetch(p4); - prefetch(p5); - - do { - register long d0, d1, d2, d3, d4, d5, d6, d7; - - prefetchw(p1+8); - prefetch(p2+8); - prefetch(p3+8); - prefetch(p4+8); - prefetch(p5+8); - once_more: - d0 = p1[0]; /* Pull the stuff into registers */ - d1 = p1[1]; /* ... in bursts, if possible. */ - d2 = p1[2]; - d3 = p1[3]; - d4 = p1[4]; - d5 = p1[5]; - d6 = p1[6]; - d7 = p1[7]; - d0 ^= p2[0]; - d1 ^= p2[1]; - d2 ^= p2[2]; - d3 ^= p2[3]; - d4 ^= p2[4]; - d5 ^= p2[5]; - d6 ^= p2[6]; - d7 ^= p2[7]; - d0 ^= p3[0]; - d1 ^= p3[1]; - d2 ^= p3[2]; - d3 ^= p3[3]; - d4 ^= p3[4]; - d5 ^= p3[5]; - d6 ^= p3[6]; - d7 ^= p3[7]; - d0 ^= p4[0]; - d1 ^= p4[1]; - d2 ^= p4[2]; - d3 ^= p4[3]; - d4 ^= p4[4]; - d5 ^= p4[5]; - d6 ^= p4[6]; - d7 ^= p4[7]; - d0 ^= p5[0]; - d1 ^= p5[1]; - d2 ^= p5[2]; - d3 ^= p5[3]; - d4 ^= p5[4]; - d5 ^= p5[5]; - d6 ^= p5[6]; - d7 ^= p5[7]; - p1[0] = d0; /* Store the result (in bursts) */ - p1[1] = d1; - p1[2] = d2; - p1[3] = d3; - p1[4] = d4; - p1[5] = d5; - p1[6] = d6; - p1[7] = d7; - p1 += 8; - p2 += 8; - p3 += 8; - p4 += 8; - p5 += 8; - } while (--lines > 0); - if (lines == 0) - goto once_more; -} - -static struct xor_block_template xor_block_8regs = { - .name = "8regs", - .do_2 = xor_8regs_2, - .do_3 = xor_8regs_3, - .do_4 = xor_8regs_4, - .do_5 = xor_8regs_5, -}; - -static struct xor_block_template xor_block_32regs = { - .name = "32regs", - .do_2 = xor_32regs_2, - .do_3 = xor_32regs_3, - .do_4 = xor_32regs_4, - .do_5 = xor_32regs_5, -}; - -static struct xor_block_template xor_block_8regs_p __maybe_unused = { - .name = "8regs_prefetch", - .do_2 = xor_8regs_p_2, - .do_3 = xor_8regs_p_3, - .do_4 = xor_8regs_p_4, - .do_5 = xor_8regs_p_5, -}; - -static struct xor_block_template xor_block_32regs_p __maybe_unused = { - .name = "32regs_prefetch", - .do_2 = xor_32regs_p_2, - .do_3 = xor_32regs_p_3, - .do_4 = xor_32regs_p_4, - .do_5 = xor_32regs_p_5, -}; - -#define XOR_TRY_TEMPLATES \ - do { \ - xor_speed(&xor_block_8regs); \ - xor_speed(&xor_block_8regs_p); \ - xor_speed(&xor_block_32regs); \ - xor_speed(&xor_block_32regs_p); \ - } while (0) diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h new file mode 100644 index 000000000000..fa7269154620 --- /dev/null +++ b/include/cxl/cxl.h @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2020 Intel Corporation. */ +/* Copyright(c) 2026 Advanced Micro Devices, Inc. */ + +#ifndef __CXL_CXL_H__ +#define __CXL_CXL_H__ + +#include +#include +#include + +/** + * enum cxl_devtype - delineate type-2 from a generic type-3 device + * @CXL_DEVTYPE_DEVMEM: Vendor specific CXL Type-2 device implementing HDM-D or + * HDM-DB, no requirement that this device implements a + * mailbox, or other memory-device-standard manageability + * flows. + * @CXL_DEVTYPE_CLASSMEM: Common class definition of a CXL Type-3 device with + * HDM-H and class-mandatory memory device registers + */ +enum cxl_devtype { + CXL_DEVTYPE_DEVMEM, + CXL_DEVTYPE_CLASSMEM, +}; + +struct device; + +/* + * Using struct_group() allows for per register-block-type helper routines, + * without requiring block-type agnostic code to include the prefix. + */ +struct cxl_regs { + /* + * Common set of CXL Component register block base pointers + * @hdm_decoder: CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure + * @ras: CXL 2.0 8.2.5.9 CXL RAS Capability Structure + */ + struct_group_tagged(cxl_component_regs, component, + void __iomem *hdm_decoder; + void __iomem *ras; + ); + /* + * Common set of CXL Device register block base pointers + * @status: CXL 2.0 8.2.8.3 Device Status Registers + * @mbox: CXL 2.0 8.2.8.4 Mailbox Registers + * @memdev: CXL 2.0 8.2.8.5 Memory Device Registers + */ + struct_group_tagged(cxl_device_regs, device_regs, + void __iomem *status, *mbox, *memdev; + ); + + struct_group_tagged(cxl_pmu_regs, pmu_regs, + void __iomem *pmu; + ); + + /* + * RCH downstream port specific RAS register + * @aer: CXL 3.0 8.2.1.1 RCH Downstream Port RCRB + */ + struct_group_tagged(cxl_rch_regs, rch_regs, + void __iomem *dport_aer; + ); + + /* + * RCD upstream port specific PCIe cap register + * @pcie_cap: CXL 3.0 8.2.1.2 RCD Upstream Port RCRB + */ + struct_group_tagged(cxl_rcd_regs, rcd_regs, + void __iomem *rcd_pcie_cap; + ); +}; + +struct cxl_reg_map { + bool valid; + int id; + unsigned long offset; + unsigned long size; +}; + +struct cxl_component_reg_map { + struct cxl_reg_map hdm_decoder; + struct cxl_reg_map ras; +}; + +struct cxl_device_reg_map { + struct cxl_reg_map status; + struct cxl_reg_map mbox; + struct cxl_reg_map memdev; +}; + +struct cxl_pmu_reg_map { + struct cxl_reg_map pmu; +}; + +/** + * struct cxl_register_map - DVSEC harvested register block mapping parameters + * @host: device for devm operations and logging + * @base: virtual base of the register-block-BAR + @block_offset + * @resource: physical resource base of the register block + * @max_size: maximum mapping size to perform register search + * @reg_type: see enum cxl_regloc_type + * @component_map: cxl_reg_map for component registers + * @device_map: cxl_reg_maps for device registers + * @pmu_map: cxl_reg_maps for CXL Performance Monitoring Units + */ +struct cxl_register_map { + struct device *host; + void __iomem *base; + resource_size_t resource; + resource_size_t max_size; + u8 reg_type; + union { + struct cxl_component_reg_map component_map; + struct cxl_device_reg_map device_map; + struct cxl_pmu_reg_map pmu_map; + }; +}; + +/** + * struct cxl_dpa_perf - DPA performance property entry + * @dpa_range: range for DPA address + * @coord: QoS performance data (i.e. latency, bandwidth) + * @cdat_coord: raw QoS performance data from CDAT + * @qos_class: QoS Class cookies + */ +struct cxl_dpa_perf { + struct range dpa_range; + struct access_coordinate coord[ACCESS_COORDINATE_MAX]; + struct access_coordinate cdat_coord[ACCESS_COORDINATE_MAX]; + int qos_class; +}; + +enum cxl_partition_mode { + CXL_PARTMODE_RAM, + CXL_PARTMODE_PMEM, +}; + +/** + * struct cxl_dpa_partition - DPA partition descriptor + * @res: shortcut to the partition in the DPA resource tree (cxlds->dpa_res) + * @perf: performance attributes of the partition from CDAT + * @mode: operation mode for the DPA capacity, e.g. ram, pmem, dynamic... + */ +struct cxl_dpa_partition { + struct resource res; + struct cxl_dpa_perf perf; + enum cxl_partition_mode mode; +}; + +#define CXL_NR_PARTITIONS_MAX 2 + +/** + * struct cxl_dev_state - The driver device state + * + * cxl_dev_state represents the CXL driver/device state. It provides an + * interface to mailbox commands as well as some cached data about the device. + * Currently only memory devices are represented. + * + * @dev: The device associated with this CXL state + * @cxlmd: The device representing the CXL.mem capabilities of @dev + * @reg_map: component and ras register mapping parameters + * @regs: Parsed register blocks + * @cxl_dvsec: Offset to the PCIe device DVSEC + * @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH) + * @media_ready: Indicate whether the device media is usable + * @dpa_res: Overall DPA resource tree for the device + * @part: DPA partition array + * @nr_partitions: Number of DPA partitions + * @serial: PCIe Device Serial Number + * @type: Generic Memory Class device or Vendor Specific Memory device + * @cxl_mbox: CXL mailbox context + * @cxlfs: CXL features context + */ +struct cxl_dev_state { + /* public for Type2 drivers */ + struct device *dev; + struct cxl_memdev *cxlmd; + + /* private for Type2 drivers */ + struct cxl_register_map reg_map; + struct cxl_device_regs regs; + int cxl_dvsec; + bool rcd; + bool media_ready; + struct resource dpa_res; + struct cxl_dpa_partition part[CXL_NR_PARTITIONS_MAX]; + unsigned int nr_partitions; + u64 serial; + enum cxl_devtype type; + struct cxl_mailbox cxl_mbox; +#ifdef CONFIG_CXL_FEATURES + struct cxl_features_state *cxlfs; +#endif +}; + +struct cxl_dev_state *_devm_cxl_dev_state_create(struct device *dev, + enum cxl_devtype type, + u64 serial, u16 dvsec, + size_t size, bool has_mbox); + +/** + * cxl_dev_state_create - safely create and cast a cxl dev state embedded in a + * driver specific struct. + * + * @parent: device behind the request + * @type: CXL device type + * @serial: device identification + * @dvsec: dvsec capability offset + * @drv_struct: driver struct embedding a cxl_dev_state struct + * @member: name of the struct cxl_dev_state member in drv_struct + * @mbox: true if mailbox supported + * + * Returns a pointer to the drv_struct allocated and embedding a cxl_dev_state + * struct initialized. + * + * Introduced for Type2 driver support. + */ +#define devm_cxl_dev_state_create(parent, type, serial, dvsec, drv_struct, member, mbox) \ + ({ \ + static_assert(__same_type(struct cxl_dev_state, \ + ((drv_struct *)NULL)->member)); \ + static_assert(offsetof(drv_struct, member) == 0); \ + (drv_struct *)_devm_cxl_dev_state_create(parent, type, serial, dvsec, \ + sizeof(drv_struct), mbox); \ + }) +#endif /* __CXL_CXL_H__ */ diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index f03cd199aee7..8883290cd014 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -1101,7 +1101,8 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); for_each_if ((__state)->colorops[__i].ptr && \ ((colorop) = (__state)->colorops[__i].ptr, \ (void)(colorop) /* Only to avoid unused-but-set-variable warning */, \ - (new_colorop_state) = (__state)->colorops[__i].new_state, 1)) + (new_colorop_state) = (__state)->colorops[__i].new_state,\ + (void)(new_colorop_state) /* Only to avoid unused-but-set-variable warning */, 1)) /** * for_each_oldnew_plane_in_state - iterate over all planes in an atomic update diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index af8b92d2d5b7..f83f28cae207 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -693,6 +693,39 @@ enum drm_bus_flags { DRM_BUS_FLAG_SHARP_SIGNALS = BIT(8), }; +/** + * struct drm_amd_vsdb_info - AMD-specific VSDB information + * + * This structure holds information parsed from the AMD Vendor-Specific Data + * Block (VSDB) version 3. + */ +struct drm_amd_vsdb_info { + /** + * @version: Version of the Vendor-Specific Data Block (VSDB) + */ + u8 version; + + /** + * @replay_mode: Panel Replay supported + */ + bool replay_mode; + + /** + * @panel_type: Panel technology type + */ + u8 panel_type; + + /** + * @luminance_range1: Luminance for max back light + */ + struct drm_luminance_range_info luminance_range1; + + /** + * @luminance_range2: Luminance for min back light + */ + struct drm_luminance_range_info luminance_range2; +}; + /** * struct drm_display_info - runtime data about the connected sink * @@ -883,6 +916,11 @@ struct drm_display_info { * Defaults to CEC_PHYS_ADDR_INVALID (0xffff). */ u16 source_physical_address; + + /** + * @amd_vsdb: AMD-specific VSDB information. + */ + struct drm_amd_vsdb_info amd_vsdb; }; int drm_display_info_set_bus_formats(struct drm_display_info *info, diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index bf391903443d..0c5e5ed7b5e7 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -273,6 +273,12 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper); int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper); bool drm_fb_helper_gem_is_fb(const struct drm_fb_helper *fb_helper, const struct drm_gem_object *obj); +#else +static inline bool drm_fb_helper_gem_is_fb(const struct drm_fb_helper *fb_helper, + const struct drm_gem_object *obj) +{ + return false; +} #endif #endif diff --git a/include/drm/intel/intel_lb_mei_interface.h b/include/drm/intel/intel_lb_mei_interface.h index 0850738a30fc..7f533ac7cc10 100644 --- a/include/drm/intel/intel_lb_mei_interface.h +++ b/include/drm/intel/intel_lb_mei_interface.h @@ -6,6 +6,7 @@ #ifndef _INTEL_LB_MEI_INTERFACE_H_ #define _INTEL_LB_MEI_INTERFACE_H_ +#include #include struct device; @@ -21,9 +22,11 @@ struct device; /** * enum intel_lb_type - enum to determine late binding payload type * @INTEL_LB_TYPE_FAN_CONTROL: Fan controller configuration + * @INTEL_LB_TYPE_OCODE: Ocode firmware */ enum intel_lb_type { INTEL_LB_TYPE_FAN_CONTROL = 1, + INTEL_LB_TYPE_OCODE = 3, }; /** @@ -36,16 +39,46 @@ enum intel_lb_type { * @INTEL_LB_STATUS_INVALID_SIGNATURE: Payload has an invalid or untrusted signature * @INTEL_LB_STATUS_INVALID_PAYLOAD: Payload contents are not accepted by firmware * @INTEL_LB_STATUS_TIMEOUT: Operation timed out before completion + * @INTEL_LB_STATUS_BUFFER_TOO_SMALL: Buffer provided is smaller when expected + * @INTEL_LB_STATUS_INTERNAL_ERROR: Internal firmware error + * @INTEL_LB_STATUS_INVALID_FPT_TABLE: Invalid firmware format table + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_VERIFICATION_ERROR: Error in signature verification + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_CPD: Invalid CPD + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_FW_VERSION_MISMATCH: Firmware version mismatch + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_MANIFEST: Invalid firmware manifest + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_HASH: Wrong hash in signature + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_BINDING_TYPE_MISMATCH: Wrong firmware type provided + * @INTEL_LB_STATUS_SIGNED_PAYLOAD_HANDLE_SVN_FAILED: SVN check failed + * @INTEL_LB_STATUS_DESTINATION_MBOX_FAILURE: Failed to send datat to destination + * @INTEL_LB_STATUS_MISSING_LOADING_PATCH: No loading patch found + * @INTEL_LB_STATUS_INVALID_COMMAND: Invalid command number + * @INTEL_LB_STATUS_INVALID_HECI_HEADER: Invalid transport header + * @INTEL_LB_STATUS_IP_ERROR_START: Base for internal errors */ enum intel_lb_status { - INTEL_LB_STATUS_SUCCESS = 0, - INTEL_LB_STATUS_4ID_MISMATCH = 1, - INTEL_LB_STATUS_ARB_FAILURE = 2, - INTEL_LB_STATUS_GENERAL_ERROR = 3, - INTEL_LB_STATUS_INVALID_PARAMS = 4, - INTEL_LB_STATUS_INVALID_SIGNATURE = 5, - INTEL_LB_STATUS_INVALID_PAYLOAD = 6, - INTEL_LB_STATUS_TIMEOUT = 7, + INTEL_LB_STATUS_SUCCESS = 0, + INTEL_LB_STATUS_4ID_MISMATCH = 1, + INTEL_LB_STATUS_ARB_FAILURE = 2, + INTEL_LB_STATUS_GENERAL_ERROR = 3, + INTEL_LB_STATUS_INVALID_PARAMS = 4, + INTEL_LB_STATUS_INVALID_SIGNATURE = 5, + INTEL_LB_STATUS_INVALID_PAYLOAD = 6, + INTEL_LB_STATUS_TIMEOUT = 7, + INTEL_LB_STATUS_BUFFER_TOO_SMALL = 8, + INTEL_LB_STATUS_INTERNAL_ERROR = 9, + INTEL_LB_STATUS_INVALID_FPT_TABLE = 10, + INTEL_LB_STATUS_SIGNED_PAYLOAD_VERIFICATION_ERROR = 11, + INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_CPD = 12, + INTEL_LB_STATUS_SIGNED_PAYLOAD_FW_VERSION_MISMATCH = 13, + INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_MANIFEST = 14, + INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_HASH = 15, + INTEL_LB_STATUS_SIGNED_PAYLOAD_BINDING_TYPE_MISMATCH = 16, + INTEL_LB_STATUS_SIGNED_PAYLOAD_HANDLE_SVN_FAILED = 17, + INTEL_LB_STATUS_DESTINATION_MBOX_FAILURE = 18, + INTEL_LB_STATUS_MISSING_LOADING_PATCH = 19, + INTEL_LB_STATUS_INVALID_COMMAND = 20, + INTEL_LB_STATUS_INVALID_HECI_HEADER = 21, + INTEL_LB_STATUS_IP_ERROR_START = BIT(31), }; /** @@ -62,7 +95,7 @@ struct intel_lb_component_ops { * @payload_size: Payload buffer size in bytes * * Return: 0 success, negative errno value on transport failure, - * positive status returned by firmware + * positive error status returned by firmware */ int (*push_payload)(struct device *dev, u32 type, u32 flags, const void *payload, size_t payload_size); diff --git a/include/drm/ttm/ttm_resource.h b/include/drm/ttm/ttm_resource.h index 33e80f30b8b8..a5d386583fb6 100644 --- a/include/drm/ttm/ttm_resource.h +++ b/include/drm/ttm/ttm_resource.h @@ -448,6 +448,8 @@ void ttm_resource_add_bulk_move(struct ttm_resource *res, struct ttm_buffer_object *bo); void ttm_resource_del_bulk_move(struct ttm_resource *res, struct ttm_buffer_object *bo); +void ttm_resource_del_bulk_move_unevictable(struct ttm_resource *res, + struct ttm_buffer_object *bo); void ttm_resource_move_to_lru_tail(struct ttm_resource *res); void ttm_resource_init(struct ttm_buffer_object *bo, diff --git a/include/dt-bindings/arm/qcom,ids.h b/include/dt-bindings/arm/qcom,ids.h index 8776844e0eeb..336f7bb7188a 100644 --- a/include/dt-bindings/arm/qcom,ids.h +++ b/include/dt-bindings/arm/qcom,ids.h @@ -245,6 +245,7 @@ #define QCOM_ID_IPQ5000 503 #define QCOM_ID_IPQ0509 504 #define QCOM_ID_IPQ0518 505 +#define QCOM_ID_SM7450 506 #define QCOM_ID_SM6375 507 #define QCOM_ID_IPQ9514 510 #define QCOM_ID_IPQ9550 511 @@ -260,10 +261,12 @@ #define QCOM_ID_SM8475 530 #define QCOM_ID_SM8475P 531 #define QCOM_ID_SA8255P 532 +#define QCOM_ID_SA8650P 533 #define QCOM_ID_SA8775P 534 #define QCOM_ID_QRU1000 539 #define QCOM_ID_SM8475_2 540 #define QCOM_ID_QDU1000 545 +#define QCOM_ID_SM7450P 547 #define QCOM_ID_X1E80100 555 #define QCOM_ID_SM8650 557 #define QCOM_ID_SM4450 568 @@ -294,6 +297,13 @@ #define QCOM_ID_QCS8275 675 #define QCOM_ID_QCS9075 676 #define QCOM_ID_QCS615 680 +#define QCOM_ID_CQ7790M 731 +#define QCOM_ID_CQ7790S 732 +#define QCOM_ID_IPQ5200 765 +#define QCOM_ID_IPQ5210 766 +#define QCOM_ID_QCF2200 767 +#define QCOM_ID_QCF3200 768 +#define QCOM_ID_QCF3210 769 /* * The board type and revision information, used by Qualcomm bootloaders and diff --git a/include/dt-bindings/clock/axis,artpec9-clk.h b/include/dt-bindings/clock/axis,artpec9-clk.h new file mode 100644 index 000000000000..c6787be8d686 --- /dev/null +++ b/include/dt-bindings/clock/axis,artpec9-clk.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * https://www.samsung.com + * Copyright (c) 2025 Axis Communications AB. + * https://www.axis.com + * + * Device Tree binding constants for ARTPEC-9 clock controller. + */ + +#ifndef _DT_BINDINGS_CLOCK_ARTPEC9_H +#define _DT_BINDINGS_CLOCK_ARTPEC9_H + +/* CMU_CMU */ +#define CLK_FOUT_SHARED0_PLL 1 +#define CLK_DOUT_SHARED0_DIV2 2 +#define CLK_DOUT_SHARED0_DIV3 3 +#define CLK_DOUT_SHARED0_DIV4 4 +#define CLK_FOUT_SHARED1_PLL 5 +#define CLK_DOUT_SHARED1_DIV2 6 +#define CLK_DOUT_SHARED1_DIV3 7 +#define CLK_DOUT_SHARED1_DIV4 8 +#define CLK_FOUT_AUDIO_PLL 9 +#define CLK_DOUT_CMU_ADD 10 +#define CLK_DOUT_CMU_BUS 11 +#define CLK_DOUT_CMU_CDC_CORE 12 +#define CLK_DOUT_CMU_CORE_MAIN 13 +#define CLK_DOUT_CMU_CPUCL_SWITCH 14 +#define CLK_DOUT_CMU_DLP_CORE 15 +#define CLK_DOUT_CMU_FSYS0_BUS 16 +#define CLK_DOUT_CMU_FSYS0_IP 17 +#define CLK_DOUT_CMU_FSYS1_BUS 18 +#define CLK_DOUT_CMU_FSYS1_SCAN0 19 +#define CLK_DOUT_CMU_FSYS1_SCAN1 20 +#define CLK_DOUT_CMU_GPU_3D 21 +#define CLK_DOUT_CMU_GPU_2D 22 +#define CLK_DOUT_CMU_IMEM_ACLK 23 +#define CLK_DOUT_CMU_IMEM_CA5 24 +#define CLK_DOUT_CMU_IMEM_JPEG 25 +#define CLK_DOUT_CMU_IMEM_SSS 26 +#define CLK_DOUT_CMU_IPA_CORE 27 +#define CLK_DOUT_CMU_LCPU 28 +#define CLK_DOUT_CMU_MIF_SWITCH 29 +#define CLK_DOUT_CMU_MIF_BUSP 30 +#define CLK_DOUT_CMU_PERI_DISP 31 +#define CLK_DOUT_CMU_PERI_IP 32 +#define CLK_DOUT_CMU_RSP_CORE 33 +#define CLK_DOUT_CMU_TRFM 34 +#define CLK_DOUT_CMU_VIO_CORE_L 35 +#define CLK_DOUT_CMU_VIO_CORE 36 +#define CLK_DOUT_CMU_VIP0 37 +#define CLK_DOUT_CMU_VIP1 38 +#define CLK_DOUT_CMU_VPP_CORE 39 +#define CLK_DOUT_CMU_VIO_AUDIO 40 + +/* CMU_BUS */ +#define CLK_MOUT_BUS_ACLK_USER 1 + +/* CMU_CORE */ +#define CLK_MOUT_CORE_ACLK_USER 1 + +/* CMU_CPUCL */ +#define CLK_FOUT_CPUCL_PLL0 1 +#define CLK_MOUT_CPUCL_PLL0 2 +#define CLK_FOUT_CPUCL_PLL1 3 +#define CLK_MOUT_CPUCL_PLL_SCU 4 +#define CLK_MOUT_CPUCL_SWITCH_SCU_USER 5 +#define CLK_MOUT_CPUCL_SWITCH_USER 6 +#define CLK_DOUT_CPUCL_CPU 7 +#define CLK_DOUT_CPUCL_CLUSTER_PERIPHCLK 8 +#define CLK_DOUT_CPUCL_CLUSTER_GICCLK 9 +#define CLK_DOUT_CPUCL_CLUSTER_PCLK 10 +#define CLK_DOUT_CPUCL_CMUREF 11 +#define CLK_DOUT_CPUCL_CLUSTER_ATCLK 12 +#define CLK_DOUT_CPUCL_CLUSTER_SCU 13 +#define CLK_DOUT_CPUCL_DBG 14 +#define CLK_GOUT_CPUCL_SHORTSTOP 15 +#define CLK_GOUT_CPUCL_CLUSTER_CPU 16 +#define CLK_GOUT_CPUCL_CSSYS_IPCLKPORT_ATCLK 17 +#define CLK_GOUT_CPUCL_CSSYS_IPCLKPORT_PCLKDBG 18 + +/* CMU_FSYS0 */ +#define CLK_MOUT_FSYS0_BUS_USER 1 +#define CLK_MOUT_FSYS0_IP_USER 2 +#define CLK_MOUT_FSYS0_MAIN_USER 3 +#define CLK_DOUT_FSYS0_125 4 +#define CLK_DOUT_FSYS0_ADC 5 +#define CLK_DOUT_FSYS0_BUS_300 6 +#define CLK_DOUT_FSYS0_EQOS0 7 +#define CLK_DOUT_FSYS0_EQOS1 8 +#define CLK_DOUT_FSYS0_MMC_CARD0 9 +#define CLK_DOUT_FSYS0_MMC_CARD1 10 +#define CLK_DOUT_FSYS0_MMC_CARD2 11 +#define CLK_DOUT_FSYS0_QSPI 12 +#define CLK_DOUT_FSYS0_SFMC_NAND 13 +#define CLK_GOUT_FSYS0_EQOS_TOP0_IPCLKPORT_ACLK_I 14 +#define CLK_GOUT_FSYS0_EQOS_TOP0_IPCLKPORT_CLK_CSR_I 15 +#define CLK_GOUT_FSYS0_EQOS_TOP0_IPCLKPORT_I_RGMII_PHASE_CLK_250 16 +#define CLK_GOUT_FSYS0_EQOS_TOP0_IPCLKPORT_I_RGMII_TXCLK 17 +#define CLK_GOUT_FSYS0_EQOS_TOP1_IPCLKPORT_I_RGMII_PHASE_CLK_250 18 +#define CLK_GOUT_FSYS0_EQOS_TOP1_IPCLKPORT_I_RGMII_TXCLK 19 +#define CLK_GOUT_FSYS0_EQOS_TOP1_IPCLKPORT_ACLK_I 20 +#define CLK_GOUT_FSYS0_EQOS_TOP1_IPCLKPORT_CLK_CSR_I 21 +#define CLK_GOUT_FSYS0_I3C0_IPCLKPORT_I_APB_S_PCLK 22 +#define CLK_GOUT_FSYS0_I3C0_IPCLKPORT_I_CORE_CLK 23 +#define CLK_GOUT_FSYS0_I3C0_IPCLKPORT_I_DMA_CLK 24 +#define CLK_GOUT_FSYS0_I3C0_IPCLKPORT_I_HDR_TX_CLK 25 +#define CLK_GOUT_FSYS0_I3C1_IPCLKPORT_I_APB_S_PCLK 26 +#define CLK_GOUT_FSYS0_I3C1_IPCLKPORT_I_CORE_CLK 27 +#define CLK_GOUT_FSYS0_I3C1_IPCLKPORT_I_DMA_CLK 28 +#define CLK_GOUT_FSYS0_I3C1_IPCLKPORT_I_HDR_TX_CLK 29 +#define CLK_GOUT_FSYS0_MMC0_IPCLKPORT_SDCLKIN 30 +#define CLK_GOUT_FSYS0_MMC1_IPCLKPORT_SDCLKIN 31 +#define CLK_GOUT_FSYS0_MMC2_IPCLKPORT_SDCLKIN 32 +#define CLK_GOUT_FSYS0_QSPI_IPCLKPORT_HCLK 33 +#define CLK_GOUT_FSYS0_QSPI_IPCLKPORT_SSI_CLK 34 +#define CLK_GOUT_FSYS0_SFMC_IPCLKPORT_I_ACLK_NAND 35 +#define CLK_GOUT_FSYS0_I2C0_IPCLKPORT_I_PCLK 36 +#define CLK_GOUT_FSYS0_I2C1_IPCLKPORT_I_PCLK 37 +#define CLK_GOUT_FSYS0_MMC0_IPCLKPORT_I_ACLK 38 +#define CLK_GOUT_FSYS0_MMC1_IPCLKPORT_I_ACLK 39 +#define CLK_GOUT_FSYS0_MMC2_IPCLKPORT_I_ACLK 40 +#define CLK_GOUT_FSYS0_PWM_IPCLKPORT_I_PCLK_S0 41 + +/* CMU_FSYS1 */ +#define CLK_FOUT_FSYS1_PLL 1 +#define CLK_MOUT_FSYS1_SCAN0_USER 2 +#define CLK_MOUT_FSYS1_SCAN1_USER 3 +#define CLK_MOUT_FSYS1_BUS_USER 4 +#define CLK_DOUT_FSYS1_200 5 +#define CLK_DOUT_FSYS1_BUS_300 6 +#define CLK_DOUT_FSYS1_OTP_MEM 7 +#define CLK_DOUT_FSYS1_PCIE_PHY_REFCLK_SYSPLL 8 +#define CLK_GOUT_FSYS1_IPCLKPORT_PCIE_PHY_APB2CR_PCLK_100 9 +#define CLK_GOUT_FSYS1_UART0_PCLK 10 +#define CLK_GOUT_FSYS1_UART0_SCLK_UART 11 +#define CLK_GOUT_FSYS1_IPCLKPORT_PCIE_PHY_APB2CR_PCLK_300 12 +#define CLK_GOUT_FSYS1_IPCLKPORT_PCIE_SUB_CON_X1_DBI_ACLK_SOC 13 +#define CLK_GOUT_FSYS1_IPCLKPORT_PCIE_SUB_CON_X1_MSTR_ACLK_SOC 14 +#define CLK_GOUT_FSYS1_IPCLKPORT_PCIE_SUB_CON_X1_SLV_ACLK_SOC 15 +#define CLK_GOUT_FSYS1_IPCLKPORT_PCIE_SUB_CON_X2_DBI_ACLK_SOC 16 +#define CLK_GOUT_FSYS1_IPCLKPORT_PCIE_SUB_CON_X2_MSTR_ACLK_SOC 17 +#define CLK_GOUT_FSYS1_IPCLKPORT_PCIE_SUB_CON_X2_SLV_ACLK_SOC 18 +#define CLK_GOUT_FSYS1_USB20DRD_IPCLKPORT_ACLK_PHYCTRL_20 19 +#define CLK_GOUT_FSYS1_USB20DRD_IPCLKPORT_BUS_CLK_EARLY 20 +#define CLK_GOUT_FSYS1_XHB_AHBBR_FSYS1_IPCLKPORT_CLK 21 +#define CLK_GOUT_FSYS1_XHB_USB_IPCLKPORT_CLK 22 + +/* CMU_IMEM */ +#define CLK_MOUT_IMEM_ACLK_USER 1 +#define CLK_MOUT_IMEM_CA5_USER 2 +#define CLK_MOUT_IMEM_SSS_USER 3 +#define CLK_MOUT_IMEM_JPEG_USER 4 +#define CLK_DOUT_IMEM_PCLK 5 +#define CLK_GOUT_IMEM_CA5_0_IPCLKPORT_ATCLK 6 +#define CLK_GOUT_IMEM_CA5_0_IPCLKPORT_CLKIN 7 +#define CLK_GOUT_IMEM_CA5_0_IPCLKPORT_PCLK_DBG 8 +#define CLK_GOUT_IMEM_CA5_1_IPCLKPORT_ATCLK 9 +#define CLK_GOUT_IMEM_CA5_1_IPCLKPORT_CLKIN 10 +#define CLK_GOUT_IMEM_CA5_1_IPCLKPORT_PCLK_DBG 11 +#define CLK_GOUT_IMEM_MCT0_PCLK 12 +#define CLK_GOUT_IMEM_MCT1_PCLK 13 +#define CLK_GOUT_IMEM_MCT2_PCLK 14 +#define CLK_GOUT_IMEM_MCT3_PCLK 15 +#define CLK_GOUT_IMEM_PCLK_TMU0_APBIF 16 + +/* CMU_PERI */ +#define CLK_MOUT_PERI_IP_USER 1 +#define CLK_MOUT_PERI_DISP_USER 2 +#define CLK_DOUT_PERI_125 3 +#define CLK_DOUT_PERI_PCLK 4 +#define CLK_DOUT_PERI_SPI 5 +#define CLK_DOUT_PERI_UART1 6 +#define CLK_DOUT_PERI_UART2 7 +#define CLK_GOUT_PERI_DMA4DSIM_IPCLKPORT_CLK_APB_CLK 8 +#define CLK_GOUT_PERI_DMA4DSIM_IPCLKPORT_CLK_AXI_CLK 9 +#define CLK_GOUT_PERI_I3C2_IPCLKPORT_I_APB_S_PCLK 10 +#define CLK_GOUT_PERI_I3C2_IPCLKPORT_I_CORE_CLK 11 +#define CLK_GOUT_PERI_I3C2_IPCLKPORT_I_DMA_CLK 12 +#define CLK_GOUT_PERI_I3C2_IPCLKPORT_I_HDR_TX_CLK 13 +#define CLK_GOUT_PERI_I3C3_IPCLKPORT_I_APB_S_PCLK 14 +#define CLK_GOUT_PERI_I3C3_IPCLKPORT_I_CORE_CLK 15 +#define CLK_GOUT_PERI_I3C3_IPCLKPORT_I_DMA_CLK 16 +#define CLK_GOUT_PERI_I3C3_IPCLKPORT_I_HDR_TX_CLK 17 +#define CLK_GOUT_PERI_APB_ASYNC_DSIM_IPCLKPORT_PCLKS 18 +#define CLK_GOUT_PERI_I2C2_IPCLKPORT_I_PCLK 19 +#define CLK_GOUT_PERI_I2C3_IPCLKPORT_I_PCLK 20 +#define CLK_GOUT_PERI_SPI0_PCLK 21 +#define CLK_GOUT_PERI_SPI0_SCLK_SPI 22 +#define CLK_GOUT_PERI_UART1_PCLK 23 +#define CLK_GOUT_PERI_UART1_SCLK_UART 24 +#define CLK_GOUT_PERI_UART2_PCLK 25 +#define CLK_GOUT_PERI_UART2_SCLK_UART 26 + +#endif /* _DT_BINDINGS_CLOCK_ARTPEC9_H */ diff --git a/include/dt-bindings/clock/bt1-ccu.h b/include/dt-bindings/clock/bt1-ccu.h deleted file mode 100644 index 5f166d27a00a..000000000000 --- a/include/dt-bindings/clock/bt1-ccu.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC - * - * Baikal-T1 CCU clock indices - */ -#ifndef __DT_BINDINGS_CLOCK_BT1_CCU_H -#define __DT_BINDINGS_CLOCK_BT1_CCU_H - -#define CCU_CPU_PLL 0 -#define CCU_SATA_PLL 1 -#define CCU_DDR_PLL 2 -#define CCU_PCIE_PLL 3 -#define CCU_ETH_PLL 4 - -#define CCU_AXI_MAIN_CLK 0 -#define CCU_AXI_DDR_CLK 1 -#define CCU_AXI_SATA_CLK 2 -#define CCU_AXI_GMAC0_CLK 3 -#define CCU_AXI_GMAC1_CLK 4 -#define CCU_AXI_XGMAC_CLK 5 -#define CCU_AXI_PCIE_M_CLK 6 -#define CCU_AXI_PCIE_S_CLK 7 -#define CCU_AXI_USB_CLK 8 -#define CCU_AXI_HWA_CLK 9 -#define CCU_AXI_SRAM_CLK 10 - -#define CCU_SYS_SATA_REF_CLK 0 -#define CCU_SYS_APB_CLK 1 -#define CCU_SYS_GMAC0_TX_CLK 2 -#define CCU_SYS_GMAC0_PTP_CLK 3 -#define CCU_SYS_GMAC1_TX_CLK 4 -#define CCU_SYS_GMAC1_PTP_CLK 5 -#define CCU_SYS_XGMAC_REF_CLK 6 -#define CCU_SYS_XGMAC_PTP_CLK 7 -#define CCU_SYS_USB_CLK 8 -#define CCU_SYS_PVT_CLK 9 -#define CCU_SYS_HWA_CLK 10 -#define CCU_SYS_UART_CLK 11 -#define CCU_SYS_I2C1_CLK 12 -#define CCU_SYS_I2C2_CLK 13 -#define CCU_SYS_GPIO_CLK 14 -#define CCU_SYS_TIMER0_CLK 15 -#define CCU_SYS_TIMER1_CLK 16 -#define CCU_SYS_TIMER2_CLK 17 -#define CCU_SYS_WDT_CLK 18 - -#endif /* __DT_BINDINGS_CLOCK_BT1_CCU_H */ diff --git a/include/dt-bindings/clock/econet,en751221-scu.h b/include/dt-bindings/clock/econet,en751221-scu.h new file mode 100644 index 000000000000..318ec8a4670e --- /dev/null +++ b/include/dt-bindings/clock/econet,en751221-scu.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + +#ifndef _DT_BINDINGS_CLOCK_ECONET_EN751221_SCU_H_ +#define _DT_BINDINGS_CLOCK_ECONET_EN751221_SCU_H_ + +#define EN751221_CLK_PCIE 0 +#define EN751221_CLK_SPI 1 +#define EN751221_CLK_BUS 2 +#define EN751221_CLK_CPU 3 +#define EN751221_CLK_GSW 4 + +#endif /* _DT_BINDINGS_CLOCK_ECONET_EN751221_SCU_H_ */ diff --git a/include/dt-bindings/clock/eswin,eic7700-clock.h b/include/dt-bindings/clock/eswin,eic7700-clock.h new file mode 100644 index 000000000000..d7ef697d0f7a --- /dev/null +++ b/include/dt-bindings/clock/eswin,eic7700-clock.h @@ -0,0 +1,285 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * Device Tree binding constants for EIC7700 clock controller. + * + * Authors: + * Yifeng Huang + * Xuyang Dong + */ + +#ifndef _DT_BINDINGS_ESWIN_EIC7700_CLOCK_H_ +#define _DT_BINDINGS_ESWIN_EIC7700_CLOCK_H_ + +#define EIC7700_CLK_XTAL_32K 0 +#define EIC7700_CLK_PLL_CPU 1 +#define EIC7700_CLK_SPLL0_FOUT1 2 +#define EIC7700_CLK_SPLL0_FOUT2 3 +#define EIC7700_CLK_SPLL0_FOUT3 4 +#define EIC7700_CLK_SPLL1_FOUT1 5 +#define EIC7700_CLK_SPLL1_FOUT2 6 +#define EIC7700_CLK_SPLL1_FOUT3 7 +#define EIC7700_CLK_SPLL2_FOUT1 8 +#define EIC7700_CLK_SPLL2_FOUT2 9 +#define EIC7700_CLK_SPLL2_FOUT3 10 +#define EIC7700_CLK_VPLL_FOUT1 11 +#define EIC7700_CLK_VPLL_FOUT2 12 +#define EIC7700_CLK_VPLL_FOUT3 13 +#define EIC7700_CLK_APLL_FOUT1 14 +#define EIC7700_CLK_APLL_FOUT2 15 +#define EIC7700_CLK_APLL_FOUT3 16 +#define EIC7700_CLK_EXT_MCLK 17 +#define EIC7700_CLK_LPDDR_REF_BAK 18 +#define EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE 19 +#define EIC7700_CLK_MUX_CPU_ACLK_2MUX1_GFREE 20 +#define EIC7700_CLK_MUX_DSP_ACLK_ROOT_2MUX1_GFREE 21 +#define EIC7700_CLK_MUX_D2D_ACLK_ROOT_2MUX1_GFREE 22 +#define EIC7700_CLK_MUX_MSHCORE_ROOT_3MUX1_0 23 +#define EIC7700_CLK_MUX_MSHCORE_ROOT_3MUX1_1 24 +#define EIC7700_CLK_MUX_MSHCORE_ROOT_3MUX1_2 25 +#define EIC7700_CLK_MUX_NPU_LLCLK_3MUX1_GFREE 26 +#define EIC7700_CLK_MUX_NPU_CORE_3MUX1_GFREE 27 +#define EIC7700_CLK_MUX_VI_ACLK_ROOT_2MUX1_GFREE 28 +#define EIC7700_CLK_MUX_VI_DVP_ROOT_2MUX1_GFREE 29 +#define EIC7700_CLK_MUX_VI_DIG_ISP_ROOT_2MUX1_GFREE 30 +#define EIC7700_CLK_MUX_VO_ACLK_ROOT_2MUX1_GFREE 31 +#define EIC7700_CLK_MUX_VO_PIXEL_ROOT_2MUX1 32 +#define EIC7700_CLK_MUX_VCDEC_ROOT_2MUX1_GFREE 33 +#define EIC7700_CLK_MUX_VCACLK_ROOT_2MUX1_GFREE 34 +#define EIC7700_CLK_MUX_SATA_PHY_2MUX1 35 +#define EIC7700_CLK_MUX_BOOTSPI_CLK_2MUX1_GFREE 36 +#define EIC7700_CLK_MUX_SCPU_CORE_CLK_2MUX1_GFREE 37 +#define EIC7700_CLK_MUX_LPCPU_CORE_CLK_2MUX1_GFREE 38 +#define EIC7700_CLK_MUX_VO_MCLK_2MUX_EXT_MCLK 39 +#define EIC7700_CLK_MUX_SYSCFG_CLK_ROOT_2MUX1_GFREE 40 +#define EIC7700_CLK_MUX_AONDMA_AXI2MUX1_GFREE 41 +#define EIC7700_CLK_MUX_RMII_REF_2MUX 42 +#define EIC7700_CLK_MUX_ETH_CORE_2MUX1 43 +#define EIC7700_CLK_MUX_VI_DW_ROOT_2MUX1 44 +#define EIC7700_CLK_MUX_NPU_E31_3MUX1_GFREE 45 +#define EIC7700_CLK_MUX_DDR_ACLK_ROOT_2MUX1_GFREE 46 +#define EIC7700_CLK_DIV_SYS_CFG_DYNM 47 +#define EIC7700_CLK_DIV_NOC_NSP_DYNM 48 +#define EIC7700_CLK_DIV_BOOTSPI_DYNM 49 +#define EIC7700_CLK_DIV_SCPU_CORE_DYNM 50 +#define EIC7700_CLK_DIV_LPCPU_CORE_DYNM 51 +#define EIC7700_CLK_DIV_GPU_ACLK_DYNM 52 +#define EIC7700_CLK_DIV_DSP_ACLK_DYNM 53 +#define EIC7700_CLK_DIV_D2D_ACLK_DYNM 54 +#define EIC7700_CLK_DIV_HSP_ACLK_DYNM 55 +#define EIC7700_CLK_DIV_ETH_TXCLK_DYNM_0 56 +#define EIC7700_CLK_DIV_ETH_TXCLK_DYNM_1 57 +#define EIC7700_CLK_DIV_MSHC_CORE_DYNM_0 58 +#define EIC7700_CLK_DIV_MSHC_CORE_DYNM_1 59 +#define EIC7700_CLK_DIV_MSHC_CORE_DYNM_2 60 +#define EIC7700_CLK_DIV_PCIE_ACLK_DYNM 61 +#define EIC7700_CLK_DIV_NPU_ACLK_DYNM 62 +#define EIC7700_CLK_DIV_NPU_LLC_SRC0_DYNM 63 +#define EIC7700_CLK_DIV_NPU_LLC_SRC1_DYNM 64 +#define EIC7700_CLK_DIV_NPU_CORECLK_DYNM 65 +#define EIC7700_CLK_DIV_VI_ACLK_DYNM 66 +#define EIC7700_CLK_DIV_VI_DVP_DYNM 67 +#define EIC7700_CLK_DIV_VI_DIG_ISP_DYNM 68 +#define EIC7700_CLK_DIV_VI_SHUTTER_DYNM_0 69 +#define EIC7700_CLK_DIV_VI_SHUTTER_DYNM_1 70 +#define EIC7700_CLK_DIV_VI_SHUTTER_DYNM_2 71 +#define EIC7700_CLK_DIV_VI_SHUTTER_DYNM_3 72 +#define EIC7700_CLK_DIV_VI_SHUTTER_DYNM_4 73 +#define EIC7700_CLK_DIV_VI_SHUTTER_DYNM_5 74 +#define EIC7700_CLK_DIV_VO_ACLK_DYNM 75 +#define EIC7700_CLK_DIV_IESMCLK_DYNM 76 +#define EIC7700_CLK_DIV_VO_PIXEL_DYNM 77 +#define EIC7700_CLK_DIV_VO_MCLK_DYNM 78 +#define EIC7700_CLK_DIV_VC_ACLK_DYNM 79 +#define EIC7700_CLK_DIV_JD_DYNM 80 +#define EIC7700_CLK_DIV_JE_DYNM 81 +#define EIC7700_CLK_DIV_VE_DYNM 82 +#define EIC7700_CLK_DIV_VD_DYNM 83 +#define EIC7700_CLK_DIV_G2D_DYNM 84 +#define EIC7700_CLK_DIV_AONDMA_AXI_DYNM 85 +#define EIC7700_CLK_DIV_CRYPTO_DYNM 86 +#define EIC7700_CLK_DIV_VI_DW_DYNM 87 +#define EIC7700_CLK_DIV_NPU_E31_DYNM 88 +#define EIC7700_CLK_DIV_SATA_PHY_REF_DYNM 89 +#define EIC7700_CLK_DIV_DSP_0_ACLK_DYNM 90 +#define EIC7700_CLK_DIV_DSP_1_ACLK_DYNM 91 +#define EIC7700_CLK_DIV_DSP_2_ACLK_DYNM 92 +#define EIC7700_CLK_DIV_DSP_3_ACLK_DYNM 93 +#define EIC7700_CLK_DIV_DDR_ACLK_DYNM 94 +#define EIC7700_CLK_DIV_AON_RTC_DYNM 95 +#define EIC7700_CLK_DIV_U84_RTC_TOGGLE_DYNM 96 +#define EIC7700_CLK_DIV_VO_CEC_DYNM 97 +#define EIC7700_CLK_GATE_CPU_EXT_SRC_CORE_CLK_0 98 +#define EIC7700_CLK_GATE_CPU_EXT_SRC_CORE_CLK_1 99 +#define EIC7700_CLK_GATE_CPU_EXT_SRC_CORE_CLK_2 100 +#define EIC7700_CLK_GATE_CPU_EXT_SRC_CORE_CLK_3 101 +#define EIC7700_CLK_GATE_CPU_TRACE_CLK_0 102 +#define EIC7700_CLK_GATE_CPU_TRACE_CLK_1 103 +#define EIC7700_CLK_GATE_CPU_TRACE_CLK_2 104 +#define EIC7700_CLK_GATE_CPU_TRACE_CLK_3 105 +#define EIC7700_CLK_GATE_CPU_TRACE_COM_CLK 106 +#define EIC7700_CLK_GATE_SPLL0_FOUT2 107 +#define EIC7700_CLK_GATE_NOC_NSP_CLK 108 +#define EIC7700_CLK_GATE_BOOTSPI 109 +#define EIC7700_CLK_GATE_BOOTSPI_CFG 110 +#define EIC7700_CLK_GATE_SCPU_CORE 111 +#define EIC7700_CLK_GATE_SCPU_BUS 112 +#define EIC7700_CLK_GATE_LPCPU_CORE 113 +#define EIC7700_CLK_GATE_LPCPU_BUS 114 +#define EIC7700_CLK_GATE_GPU_ACLK 115 +#define EIC7700_CLK_GATE_GPU_GRAY_CLK 116 +#define EIC7700_CLK_GATE_GPU_CFG_CLK 117 +#define EIC7700_CLK_GATE_DSPT_ACLK 118 +#define EIC7700_CLK_GATE_DSPT_CFG_CLK 119 +#define EIC7700_CLK_GATE_D2D_ACLK 120 +#define EIC7700_CLK_GATE_D2D_CFG_CLK 121 +#define EIC7700_CLK_GATE_TCU_ACLK 122 +#define EIC7700_CLK_GATE_TCU_CFG_CLK 123 +#define EIC7700_CLK_GATE_DDRT_CFG_CLK 124 +#define EIC7700_CLK_GATE_DDRT0_P0_ACLK 125 +#define EIC7700_CLK_GATE_DDRT0_P1_ACLK 126 +#define EIC7700_CLK_GATE_DDRT0_P2_ACLK 127 +#define EIC7700_CLK_GATE_DDRT0_P3_ACLK 128 +#define EIC7700_CLK_GATE_DDRT0_P4_ACLK 129 +#define EIC7700_CLK_GATE_DDRT1_P0_ACLK 130 +#define EIC7700_CLK_GATE_DDRT1_P1_ACLK 131 +#define EIC7700_CLK_GATE_DDRT1_P2_ACLK 132 +#define EIC7700_CLK_GATE_DDRT1_P3_ACLK 133 +#define EIC7700_CLK_GATE_DDRT1_P4_ACLK 134 +#define EIC7700_CLK_GATE_TIMER_CLK_0 135 +#define EIC7700_CLK_GATE_TIMER_CLK_1 136 +#define EIC7700_CLK_GATE_TIMER_CLK_2 137 +#define EIC7700_CLK_GATE_TIMER_CLK_3 138 +#define EIC7700_CLK_GATE_TIMER_PCLK_0 139 +#define EIC7700_CLK_GATE_TIMER_PCLK_1 140 +#define EIC7700_CLK_GATE_TIMER_PCLK_2 141 +#define EIC7700_CLK_GATE_TIMER_PCLK_3 142 +#define EIC7700_CLK_GATE_TIMER3_CLK8 143 +#define EIC7700_CLK_GATE_PCIET_ACLK 144 +#define EIC7700_CLK_GATE_PCIET_CFG_CLK 145 +#define EIC7700_CLK_GATE_PCIET_CR_CLK 146 +#define EIC7700_CLK_GATE_PCIET_AUX_CLK 147 +#define EIC7700_CLK_GATE_NPU_ACLK 148 +#define EIC7700_CLK_GATE_NPU_CFG_CLK 149 +#define EIC7700_CLK_GATE_NPU_LLC_ACLK 150 +#define EIC7700_CLK_GATE_NPU_CLK 151 +#define EIC7700_CLK_GATE_NPU_E31_CLK 152 +#define EIC7700_CLK_GATE_VI_ACLK 153 +#define EIC7700_CLK_GATE_VI_DVP_CLK 154 +#define EIC7700_CLK_GATE_VI_CFG_CLK 155 +#define EIC7700_CLK_GATE_VI_DIG_DW_CLK 156 +#define EIC7700_CLK_GATE_VI_DIG_ISP_CLK 157 +#define EIC7700_CLK_GATE_VI_SHUTTER_0 158 +#define EIC7700_CLK_GATE_VI_SHUTTER_1 159 +#define EIC7700_CLK_GATE_VI_SHUTTER_2 160 +#define EIC7700_CLK_GATE_VI_SHUTTER_3 161 +#define EIC7700_CLK_GATE_VI_SHUTTER_4 162 +#define EIC7700_CLK_GATE_VI_SHUTTER_5 163 +#define EIC7700_CLK_GATE_VI_PHY_TXCLKESC 164 +#define EIC7700_CLK_GATE_VI_PHY_CFG 165 +#define EIC7700_CLK_GATE_VO_ACLK 166 +#define EIC7700_CLK_GATE_VO_CFG_CLK 167 +#define EIC7700_CLK_GATE_VO_HDMI_IESMCLK 168 +#define EIC7700_CLK_GATE_VO_PIXEL_CLK 169 +#define EIC7700_CLK_GATE_VO_I2S_MCLK 170 +#define EIC7700_CLK_GATE_HSP_CFG_CLK 171 +#define EIC7700_CLK_GATE_VC_ACLK 172 +#define EIC7700_CLK_GATE_VC_CFG_CLK 173 +#define EIC7700_CLK_GATE_VC_JE_CLK 174 +#define EIC7700_CLK_GATE_VC_JD_CLK 175 +#define EIC7700_CLK_GATE_VC_VE_CLK 176 +#define EIC7700_CLK_GATE_VC_VD_CLK 177 +#define EIC7700_CLK_GATE_G2D_CFG_CLK 178 +#define EIC7700_CLK_GATE_G2D_CLK 179 +#define EIC7700_CLK_GATE_G2D_ACLK 180 +#define EIC7700_CLK_GATE_AONDMA_CFG 181 +#define EIC7700_CLK_GATE_AONDMA_ACLK 182 +#define EIC7700_CLK_GATE_AON_ACLK 183 +#define EIC7700_CLK_GATE_HSP_SATA_RBC_CLK 184 +#define EIC7700_CLK_GATE_VO_CR_CLK 185 +#define EIC7700_CLK_GATE_HSP_ACLK 186 +#define EIC7700_CLK_GATE_HSP_SATA_OOB_CLK 187 +#define EIC7700_CLK_GATE_RTC_CFG 188 +#define EIC7700_CLK_GATE_RTC 189 +#define EIC7700_CLK_GATE_HSP_MSHC0_CORE_CLK 190 +#define EIC7700_CLK_GATE_HSP_MSHC1_CORE_CLK 191 +#define EIC7700_CLK_GATE_HSP_MSHC2_CORE_CLK 192 +#define EIC7700_CLK_GATE_HSP_ETH0_CORE_CLK 193 +#define EIC7700_CLK_GATE_HSP_ETH1_CORE_CLK 194 +#define EIC7700_CLK_GATE_HSP_RMII_REF_0 195 +#define EIC7700_CLK_GATE_HSP_RMII_REF_1 196 +#define EIC7700_CLK_GATE_PKA_CFG 197 +#define EIC7700_CLK_GATE_SPACC_CFG 198 +#define EIC7700_CLK_GATE_CRYPTO 199 +#define EIC7700_CLK_GATE_TRNG_CFG 200 +#define EIC7700_CLK_GATE_OTP_CFG 201 +#define EIC7700_CLK_GATE_MAILBOX_0 202 +#define EIC7700_CLK_GATE_MAILBOX_1 203 +#define EIC7700_CLK_GATE_MAILBOX_2 204 +#define EIC7700_CLK_GATE_MAILBOX_3 205 +#define EIC7700_CLK_GATE_MAILBOX_4 206 +#define EIC7700_CLK_GATE_MAILBOX_5 207 +#define EIC7700_CLK_GATE_MAILBOX_6 208 +#define EIC7700_CLK_GATE_MAILBOX_7 209 +#define EIC7700_CLK_GATE_MAILBOX_8 210 +#define EIC7700_CLK_GATE_MAILBOX_9 211 +#define EIC7700_CLK_GATE_MAILBOX_10 212 +#define EIC7700_CLK_GATE_MAILBOX_11 213 +#define EIC7700_CLK_GATE_MAILBOX_12 214 +#define EIC7700_CLK_GATE_MAILBOX_13 215 +#define EIC7700_CLK_GATE_MAILBOX_14 216 +#define EIC7700_CLK_GATE_MAILBOX_15 217 +#define EIC7700_CLK_GATE_LSP_I2C0_PCLK 218 +#define EIC7700_CLK_GATE_LSP_I2C1_PCLK 219 +#define EIC7700_CLK_GATE_LSP_I2C2_PCLK 220 +#define EIC7700_CLK_GATE_LSP_I2C3_PCLK 221 +#define EIC7700_CLK_GATE_LSP_I2C4_PCLK 222 +#define EIC7700_CLK_GATE_LSP_I2C5_PCLK 223 +#define EIC7700_CLK_GATE_LSP_I2C6_PCLK 224 +#define EIC7700_CLK_GATE_LSP_I2C7_PCLK 225 +#define EIC7700_CLK_GATE_LSP_I2C8_PCLK 226 +#define EIC7700_CLK_GATE_LSP_I2C9_PCLK 227 +#define EIC7700_CLK_GATE_LSP_WDT0_PCLK 228 +#define EIC7700_CLK_GATE_LSP_WDT1_PCLK 229 +#define EIC7700_CLK_GATE_LSP_WDT2_PCLK 230 +#define EIC7700_CLK_GATE_LSP_WDT3_PCLK 231 +#define EIC7700_CLK_GATE_LSP_SSI0_PCLK 232 +#define EIC7700_CLK_GATE_LSP_SSI1_PCLK 233 +#define EIC7700_CLK_GATE_LSP_PVT_PCLK 234 +#define EIC7700_CLK_GATE_AON_I2C0_PCLK 235 +#define EIC7700_CLK_GATE_AON_I2C1_PCLK 236 +#define EIC7700_CLK_GATE_LSP_UART0_PCLK 237 +#define EIC7700_CLK_GATE_LSP_UART1_PCLK 238 +#define EIC7700_CLK_GATE_LSP_UART2_PCLK 239 +#define EIC7700_CLK_GATE_LSP_UART3_PCLK 240 +#define EIC7700_CLK_GATE_LSP_UART4_PCLK 241 +#define EIC7700_CLK_GATE_LSP_TIMER_PCLK 242 +#define EIC7700_CLK_GATE_LSP_FAN_PCLK 243 +#define EIC7700_CLK_GATE_LSP_PVT0_CLK 244 +#define EIC7700_CLK_GATE_LSP_PVT1_CLK 245 +#define EIC7700_CLK_GATE_VC_JE_PCLK 246 +#define EIC7700_CLK_GATE_VC_JD_PCLK 247 +#define EIC7700_CLK_GATE_VC_VE_PCLK 248 +#define EIC7700_CLK_GATE_VC_VD_PCLK 249 +#define EIC7700_CLK_GATE_VC_MON_PCLK 250 +#define EIC7700_CLK_GATE_HSP_DMA0_CLK 251 +#define EIC7700_CLK_GATE_HSP_DMA0_CLK_TEST 252 +#define EIC7700_CLK_FIXED_FACTOR_CPU_DIV2 253 +#define EIC7700_CLK_FIXED_FACTOR_CLK_1M_DIV24 254 +#define EIC7700_CLK_FIXED_FACTOR_MIPI_TXESC_DIV10 255 +#define EIC7700_CLK_FIXED_FACTOR_U84_CORE_LP_DIV2 256 +#define EIC7700_CLK_FIXED_FACTOR_SCPU_BUS_DIV2 257 +#define EIC7700_CLK_FIXED_FACTOR_LPCPU_BUS_DIV2 258 +#define EIC7700_CLK_FIXED_FACTOR_PCIE_CR_DIV2 259 +#define EIC7700_CLK_FIXED_FACTOR_PCIE_AUX_DIV4 260 +#define EIC7700_CLK_FIXED_FACTOR_PVT_DIV20 261 +#define EIC7700_CLK_FIXED_FACTOR_HSP_RMII_REF_DIV6 262 +#define EIC7700_CLK_DIV_NOC_WDREF_DYNM 263 +#define EIC7700_CLK_GATE_DDR0_TRACE 264 +#define EIC7700_CLK_GATE_DDR1_TRACE 265 +#define EIC7700_CLK_GATE_RNOC_NSP 266 +#define EIC7700_CLK_GATE_NOC_WDREF 267 + +#endif /* _DT_BINDINGS_ESWIN_EIC7700_CLOCK_H_ */ diff --git a/include/dt-bindings/clock/exynos850.h b/include/dt-bindings/clock/exynos850.h index 80dacda57229..95285589615a 100644 --- a/include/dt-bindings/clock/exynos850.h +++ b/include/dt-bindings/clock/exynos850.h @@ -126,6 +126,7 @@ #define CLK_GOUT_GPIO_ALIVE_PCLK 22 #define CLK_GOUT_PMU_ALIVE_PCLK 23 #define CLK_GOUT_SYSREG_APM_PCLK 24 +#define CLK_GOUT_MAILBOX_APM_AP_PCLK 25 /* CMU_AUD */ #define CLK_DOUT_AUD_AUDIF 1 diff --git a/include/dt-bindings/clock/mobileye,eyeq6lplus-clk.h b/include/dt-bindings/clock/mobileye,eyeq6lplus-clk.h new file mode 100644 index 000000000000..20d84ee24ad5 --- /dev/null +++ b/include/dt-bindings/clock/mobileye,eyeq6lplus-clk.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (C) 2025 Mobileye Vision Technologies Ltd. + */ + +#ifndef _DT_BINDINGS_CLOCK_MOBILEYE_EYEQ6LPLUS_CLK_H +#define _DT_BINDINGS_CLOCK_MOBILEYE_EYEQ6LPLUS_CLK_H + +#define EQ6LPC_PLL_CPU 0 +#define EQ6LPC_PLL_DDR 1 +#define EQ6LPC_PLL_PER 2 +#define EQ6LPC_PLL_VDI 3 +#define EQ6LPC_PLL_ACC 4 + +#define EQ6LPC_CPU_OCC 5 + +#define EQ6LPC_ACC_VDI 6 +#define EQ6LPC_ACC_OCC 7 +#define EQ6LPC_ACC_FCMU 8 + +#define EQ6LPC_DDR_OCC 9 + +#define EQ6LPC_PER_OCC 10 +#define EQ6LPC_PER_I2C_SER 11 +#define EQ6LPC_PER_PCLK 12 +#define EQ6LPC_PER_TSU 13 +#define EQ6LPC_PER_OSPI 14 +#define EQ6LPC_PER_GPIO 15 +#define EQ6LPC_PER_TIMER 16 +#define EQ6LPC_PER_I2C 17 +#define EQ6LPC_PER_UART 18 +#define EQ6LPC_PER_SPI 19 +#define EQ6LPC_PER_PERIPH 20 + +#define EQ6LPC_VDI_OCC 21 + +#endif diff --git a/include/dt-bindings/clock/qcom,dispcc-sc7180.h b/include/dt-bindings/clock/qcom,dispcc-sc7180.h index b9b51617a335..070510306074 100644 --- a/include/dt-bindings/clock/qcom,dispcc-sc7180.h +++ b/include/dt-bindings/clock/qcom,dispcc-sc7180.h @@ -6,6 +6,7 @@ #ifndef _DT_BINDINGS_CLK_QCOM_DISP_CC_SC7180_H #define _DT_BINDINGS_CLK_QCOM_DISP_CC_SC7180_H +/* Clocks */ #define DISP_CC_PLL0 0 #define DISP_CC_PLL0_OUT_EVEN 1 #define DISP_CC_MDSS_AHB_CLK 2 @@ -40,7 +41,11 @@ #define DISP_CC_MDSS_VSYNC_CLK_SRC 31 #define DISP_CC_XO_CLK 32 -/* DISP_CC GDSCR */ +/* Resets */ +#define DISP_CC_MDSS_CORE_BCR 0 +#define DISP_CC_MDSS_RSCC_BCR 1 + +/* GDSCs */ #define MDSS_GDSC 0 #endif diff --git a/include/dt-bindings/clock/qcom,dispcc-sm6125.h b/include/dt-bindings/clock/qcom,dispcc-sm6125.h index 4ff974f4fcc3..f58b85d2c814 100644 --- a/include/dt-bindings/clock/qcom,dispcc-sm6125.h +++ b/include/dt-bindings/clock/qcom,dispcc-sm6125.h @@ -6,6 +6,7 @@ #ifndef _DT_BINDINGS_CLK_QCOM_DISP_CC_SM6125_H #define _DT_BINDINGS_CLK_QCOM_DISP_CC_SM6125_H +/* Clocks */ #define DISP_CC_PLL0 0 #define DISP_CC_MDSS_AHB_CLK 1 #define DISP_CC_MDSS_AHB_CLK_SRC 2 @@ -35,7 +36,10 @@ #define DISP_CC_MDSS_VSYNC_CLK_SRC 26 #define DISP_CC_XO_CLK 27 -/* DISP_CC GDSCR */ +/* Resets */ +#define DISP_CC_MDSS_CORE_BCR 0 + +/* GDSCs */ #define MDSS_GDSC 0 #endif diff --git a/include/dt-bindings/clock/qcom,eliza-dispcc.h b/include/dt-bindings/clock/qcom,eliza-dispcc.h new file mode 100644 index 000000000000..30c6d856fa98 --- /dev/null +++ b/include/dt-bindings/clock/qcom,eliza-dispcc.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_ELIZA_DISP_CC_H +#define _DT_BINDINGS_CLK_QCOM_ELIZA_DISP_CC_H + +/* DISP_CC clocks */ +#define DISP_CC_PLL0 0 +#define DISP_CC_PLL1 1 +#define DISP_CC_PLL2 2 +#define DISP_CC_ESYNC0_CLK 3 +#define DISP_CC_ESYNC0_CLK_SRC 4 +#define DISP_CC_ESYNC1_CLK 5 +#define DISP_CC_ESYNC1_CLK_SRC 6 +#define DISP_CC_MDSS_ACCU_SHIFT_CLK 7 +#define DISP_CC_MDSS_AHB1_CLK 8 +#define DISP_CC_MDSS_AHB_CLK 9 +#define DISP_CC_MDSS_AHB_CLK_SRC 10 +#define DISP_CC_MDSS_BYTE0_CLK 11 +#define DISP_CC_MDSS_BYTE0_CLK_SRC 12 +#define DISP_CC_MDSS_BYTE0_DIV_CLK_SRC 13 +#define DISP_CC_MDSS_BYTE0_INTF_CLK 14 +#define DISP_CC_MDSS_BYTE1_CLK 15 +#define DISP_CC_MDSS_BYTE1_CLK_SRC 16 +#define DISP_CC_MDSS_BYTE1_DIV_CLK_SRC 17 +#define DISP_CC_MDSS_BYTE1_INTF_CLK 18 +#define DISP_CC_MDSS_DPTX0_AUX_CLK 19 +#define DISP_CC_MDSS_DPTX0_AUX_CLK_SRC 20 +#define DISP_CC_MDSS_DPTX0_CRYPTO_CLK 21 +#define DISP_CC_MDSS_DPTX0_LINK_CLK 22 +#define DISP_CC_MDSS_DPTX0_LINK_CLK_SRC 23 +#define DISP_CC_MDSS_DPTX0_LINK_DIV_CLK_SRC 24 +#define DISP_CC_MDSS_DPTX0_LINK_INTF_CLK 25 +#define DISP_CC_MDSS_DPTX0_PIXEL0_CLK 26 +#define DISP_CC_MDSS_DPTX0_PIXEL0_CLK_SRC 27 +#define DISP_CC_MDSS_DPTX0_PIXEL1_CLK 28 +#define DISP_CC_MDSS_DPTX0_PIXEL1_CLK_SRC 29 +#define DISP_CC_MDSS_DPTX0_USB_ROUTER_LINK_INTF_CLK 30 +#define DISP_CC_MDSS_DPTX1_AUX_CLK 31 +#define DISP_CC_MDSS_DPTX1_AUX_CLK_SRC 32 +#define DISP_CC_MDSS_DPTX1_CRYPTO_CLK 33 +#define DISP_CC_MDSS_DPTX1_LINK_CLK 34 +#define DISP_CC_MDSS_DPTX1_LINK_CLK_SRC 35 +#define DISP_CC_MDSS_DPTX1_LINK_DIV_CLK_SRC 36 +#define DISP_CC_MDSS_DPTX1_LINK_INTF_CLK 37 +#define DISP_CC_MDSS_DPTX1_PIXEL0_CLK 38 +#define DISP_CC_MDSS_DPTX1_PIXEL0_CLK_SRC 39 +#define DISP_CC_MDSS_DPTX1_PIXEL1_CLK 40 +#define DISP_CC_MDSS_DPTX1_PIXEL1_CLK_SRC 41 +#define DISP_CC_MDSS_DPTX1_USB_ROUTER_LINK_INTF_CLK 42 +#define DISP_CC_MDSS_DPTX2_AUX_CLK 43 +#define DISP_CC_MDSS_DPTX2_AUX_CLK_SRC 44 +#define DISP_CC_MDSS_DPTX2_CRYPTO_CLK 45 +#define DISP_CC_MDSS_DPTX2_LINK_CLK 46 +#define DISP_CC_MDSS_DPTX2_LINK_CLK_SRC 47 +#define DISP_CC_MDSS_DPTX2_LINK_DIV_CLK_SRC 48 +#define DISP_CC_MDSS_DPTX2_LINK_INTF_CLK 49 +#define DISP_CC_MDSS_DPTX2_PIXEL0_CLK 50 +#define DISP_CC_MDSS_DPTX2_PIXEL0_CLK_SRC 51 +#define DISP_CC_MDSS_DPTX2_PIXEL1_CLK 52 +#define DISP_CC_MDSS_DPTX2_PIXEL1_CLK_SRC 53 +#define DISP_CC_MDSS_DPTX3_AUX_CLK 54 +#define DISP_CC_MDSS_DPTX3_AUX_CLK_SRC 55 +#define DISP_CC_MDSS_DPTX3_CRYPTO_CLK 56 +#define DISP_CC_MDSS_DPTX3_LINK_CLK 57 +#define DISP_CC_MDSS_DPTX3_LINK_CLK_SRC 58 +#define DISP_CC_MDSS_DPTX3_LINK_DIV_CLK_SRC 59 +#define DISP_CC_MDSS_DPTX3_LINK_INTF_CLK 60 +#define DISP_CC_MDSS_DPTX3_PIXEL0_CLK 61 +#define DISP_CC_MDSS_DPTX3_PIXEL0_CLK_SRC 62 +#define DISP_CC_MDSS_ESC0_CLK 63 +#define DISP_CC_MDSS_ESC0_CLK_SRC 64 +#define DISP_CC_MDSS_ESC1_CLK 65 +#define DISP_CC_MDSS_ESC1_CLK_SRC 66 +#define DISP_CC_MDSS_HDMI_AHBM_CLK 67 +#define DISP_CC_MDSS_HDMI_APP_CLK 68 +#define DISP_CC_MDSS_HDMI_APP_CLK_SRC 69 +#define DISP_CC_MDSS_HDMI_CRYPTO_CLK 70 +#define DISP_CC_MDSS_HDMI_INTF_CLK 71 +#define DISP_CC_MDSS_HDMI_PCLK_CLK 72 +#define DISP_CC_MDSS_HDMI_PCLK_CLK_SRC 73 +#define DISP_CC_MDSS_HDMI_PCLK_DIV_CLK_SRC 74 +#define DISP_CC_MDSS_MDP1_CLK 75 +#define DISP_CC_MDSS_MDP_CLK 76 +#define DISP_CC_MDSS_MDP_CLK_SRC 77 +#define DISP_CC_MDSS_MDP_LUT1_CLK 78 +#define DISP_CC_MDSS_MDP_LUT_CLK 79 +#define DISP_CC_MDSS_NON_GDSC_AHB_CLK 80 +#define DISP_CC_MDSS_PCLK0_CLK 81 +#define DISP_CC_MDSS_PCLK0_CLK_SRC 82 +#define DISP_CC_MDSS_PCLK1_CLK 83 +#define DISP_CC_MDSS_PCLK1_CLK_SRC 84 +#define DISP_CC_MDSS_PCLK2_CLK 85 +#define DISP_CC_MDSS_PCLK2_CLK_SRC 86 +#define DISP_CC_MDSS_RSCC_AHB_CLK 87 +#define DISP_CC_MDSS_RSCC_VSYNC_CLK 88 +#define DISP_CC_MDSS_VSYNC1_CLK 89 +#define DISP_CC_MDSS_VSYNC_CLK 90 +#define DISP_CC_MDSS_VSYNC_CLK_SRC 91 +#define DISP_CC_OSC_CLK 92 +#define DISP_CC_OSC_CLK_SRC 93 +#define DISP_CC_SLEEP_CLK 94 +#define DISP_CC_SLEEP_CLK_SRC 95 +#define DISP_CC_XO_CLK 96 +#define DISP_CC_XO_CLK_SRC 97 + +/* DISP_CC resets */ +#define DISP_CC_MDSS_CORE_BCR 0 +#define DISP_CC_MDSS_CORE_INT2_BCR 1 +#define DISP_CC_MDSS_RSCC_BCR 2 + +/* DISP_CC GDSCR */ +#define MDSS_GDSC 0 +#define MDSS_INT2_GDSC 1 + +#endif diff --git a/include/dt-bindings/clock/qcom,eliza-gcc.h b/include/dt-bindings/clock/qcom,eliza-gcc.h new file mode 100644 index 000000000000..4d27b329ae99 --- /dev/null +++ b/include/dt-bindings/clock/qcom,eliza-gcc.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_GCC_ELIZA_H +#define _DT_BINDINGS_CLK_QCOM_GCC_ELIZA_H + +/* GCC clocks */ +#define GCC_AGGRE_NOC_PCIE_AXI_CLK 0 +#define GCC_AGGRE_UFS_PHY_AXI_CLK 1 +#define GCC_AGGRE_USB3_PRIM_AXI_CLK 2 +#define GCC_BOOT_ROM_AHB_CLK 3 +#define GCC_CAM_BIST_MCLK_AHB_CLK 4 +#define GCC_CAMERA_AHB_CLK 5 +#define GCC_CAMERA_HF_AXI_CLK 6 +#define GCC_CAMERA_SF_AXI_CLK 7 +#define GCC_CAMERA_XO_CLK 8 +#define GCC_CFG_NOC_PCIE_ANOC_AHB_CLK 9 +#define GCC_CFG_NOC_USB3_PRIM_AXI_CLK 10 +#define GCC_CNOC_PCIE_SF_AXI_CLK 11 +#define GCC_DDRSS_GPU_AXI_CLK 12 +#define GCC_DDRSS_PCIE_SF_QTB_CLK 13 +#define GCC_DISP_AHB_CLK 14 +#define GCC_DISP_HF_AXI_CLK 15 +#define GCC_GP1_CLK 16 +#define GCC_GP1_CLK_SRC 17 +#define GCC_GP2_CLK 18 +#define GCC_GP2_CLK_SRC 19 +#define GCC_GP3_CLK 20 +#define GCC_GP3_CLK_SRC 21 +#define GCC_GPLL0 22 +#define GCC_GPLL0_OUT_EVEN 23 +#define GCC_GPLL4 24 +#define GCC_GPLL7 25 +#define GCC_GPLL8 26 +#define GCC_GPLL9 27 +#define GCC_GPU_CFG_AHB_CLK 28 +#define GCC_GPU_GEMNOC_GFX_CLK 29 +#define GCC_GPU_GPLL0_CPH_CLK_SRC 30 +#define GCC_GPU_GPLL0_DIV_CPH_CLK_SRC 31 +#define GCC_GPU_SMMU_VOTE_CLK 32 +#define GCC_MMU_TCU_VOTE_CLK 33 +#define GCC_PCIE_0_AUX_CLK 34 +#define GCC_PCIE_0_AUX_CLK_SRC 35 +#define GCC_PCIE_0_CFG_AHB_CLK 36 +#define GCC_PCIE_0_MSTR_AXI_CLK 37 +#define GCC_PCIE_0_PHY_RCHNG_CLK 38 +#define GCC_PCIE_0_PHY_RCHNG_CLK_SRC 39 +#define GCC_PCIE_0_PIPE_CLK 40 +#define GCC_PCIE_0_PIPE_CLK_SRC 41 +#define GCC_PCIE_0_PIPE_DIV2_CLK 42 +#define GCC_PCIE_0_PIPE_DIV2_CLK_SRC 43 +#define GCC_PCIE_0_SLV_AXI_CLK 44 +#define GCC_PCIE_0_SLV_Q2A_AXI_CLK 45 +#define GCC_PCIE_1_AUX_CLK 46 +#define GCC_PCIE_1_AUX_CLK_SRC 47 +#define GCC_PCIE_1_CFG_AHB_CLK 48 +#define GCC_PCIE_1_MSTR_AXI_CLK 49 +#define GCC_PCIE_1_PHY_RCHNG_CLK 50 +#define GCC_PCIE_1_PHY_RCHNG_CLK_SRC 51 +#define GCC_PCIE_1_PIPE_CLK 52 +#define GCC_PCIE_1_PIPE_CLK_SRC 53 +#define GCC_PCIE_1_PIPE_DIV2_CLK 54 +#define GCC_PCIE_1_PIPE_DIV2_CLK_SRC 55 +#define GCC_PCIE_1_SLV_AXI_CLK 56 +#define GCC_PCIE_1_SLV_Q2A_AXI_CLK 57 +#define GCC_PCIE_RSCC_CFG_AHB_CLK 58 +#define GCC_PCIE_RSCC_XO_CLK 59 +#define GCC_PDM2_CLK 60 +#define GCC_PDM2_CLK_SRC 61 +#define GCC_PDM_AHB_CLK 62 +#define GCC_PDM_XO4_CLK 63 +#define GCC_QMIP_CAMERA_CMD_AHB_CLK 64 +#define GCC_QMIP_CAMERA_NRT_AHB_CLK 65 +#define GCC_QMIP_CAMERA_RT_AHB_CLK 66 +#define GCC_QMIP_GPU_AHB_CLK 67 +#define GCC_QMIP_PCIE_AHB_CLK 68 +#define GCC_QMIP_VIDEO_V_CPU_AHB_CLK 69 +#define GCC_QMIP_VIDEO_VCODEC_AHB_CLK 70 +#define GCC_QUPV3_WRAP1_CORE_2X_CLK 71 +#define GCC_QUPV3_WRAP1_CORE_CLK 72 +#define GCC_QUPV3_WRAP1_QSPI_REF_CLK 73 +#define GCC_QUPV3_WRAP1_QSPI_REF_CLK_SRC 74 +#define GCC_QUPV3_WRAP1_S0_CLK 75 +#define GCC_QUPV3_WRAP1_S0_CLK_SRC 76 +#define GCC_QUPV3_WRAP1_S1_CLK 77 +#define GCC_QUPV3_WRAP1_S1_CLK_SRC 78 +#define GCC_QUPV3_WRAP1_S2_CLK 79 +#define GCC_QUPV3_WRAP1_S2_CLK_SRC 80 +#define GCC_QUPV3_WRAP1_S3_CLK 81 +#define GCC_QUPV3_WRAP1_S3_CLK_SRC 82 +#define GCC_QUPV3_WRAP1_S4_CLK 83 +#define GCC_QUPV3_WRAP1_S4_CLK_SRC 84 +#define GCC_QUPV3_WRAP1_S5_CLK 85 +#define GCC_QUPV3_WRAP1_S5_CLK_SRC 86 +#define GCC_QUPV3_WRAP1_S6_CLK 87 +#define GCC_QUPV3_WRAP1_S6_CLK_SRC 88 +#define GCC_QUPV3_WRAP1_S7_CLK 89 +#define GCC_QUPV3_WRAP1_S7_CLK_SRC 90 +#define GCC_QUPV3_WRAP2_CORE_2X_CLK 91 +#define GCC_QUPV3_WRAP2_CORE_CLK 92 +#define GCC_QUPV3_WRAP2_S0_CLK 93 +#define GCC_QUPV3_WRAP2_S0_CLK_SRC 94 +#define GCC_QUPV3_WRAP2_S1_CLK 95 +#define GCC_QUPV3_WRAP2_S1_CLK_SRC 96 +#define GCC_QUPV3_WRAP2_S2_CLK 97 +#define GCC_QUPV3_WRAP2_S2_CLK_SRC 98 +#define GCC_QUPV3_WRAP2_S3_CLK 99 +#define GCC_QUPV3_WRAP2_S3_CLK_SRC 100 +#define GCC_QUPV3_WRAP2_S4_CLK 101 +#define GCC_QUPV3_WRAP2_S4_CLK_SRC 102 +#define GCC_QUPV3_WRAP2_S5_CLK 103 +#define GCC_QUPV3_WRAP2_S5_CLK_SRC 104 +#define GCC_QUPV3_WRAP2_S6_CLK 105 +#define GCC_QUPV3_WRAP2_S6_CLK_SRC 106 +#define GCC_QUPV3_WRAP2_S7_CLK 107 +#define GCC_QUPV3_WRAP2_S7_CLK_SRC 108 +#define GCC_QUPV3_WRAP_1_M_AHB_CLK 109 +#define GCC_QUPV3_WRAP_1_S_AHB_CLK 110 +#define GCC_QUPV3_WRAP_2_M_AHB_CLK 111 +#define GCC_QUPV3_WRAP_2_S_AHB_CLK 112 +#define GCC_SDCC1_AHB_CLK 113 +#define GCC_SDCC1_APPS_CLK 114 +#define GCC_SDCC1_APPS_CLK_SRC 115 +#define GCC_SDCC1_ICE_CORE_CLK 116 +#define GCC_SDCC1_ICE_CORE_CLK_SRC 117 +#define GCC_SDCC2_AHB_CLK 118 +#define GCC_SDCC2_APPS_CLK 119 +#define GCC_SDCC2_APPS_CLK_SRC 120 +#define GCC_UFS_PHY_AHB_CLK 121 +#define GCC_UFS_PHY_AXI_CLK 122 +#define GCC_UFS_PHY_AXI_CLK_SRC 123 +#define GCC_UFS_PHY_ICE_CORE_CLK 124 +#define GCC_UFS_PHY_ICE_CORE_CLK_SRC 125 +#define GCC_UFS_PHY_PHY_AUX_CLK 126 +#define GCC_UFS_PHY_PHY_AUX_CLK_SRC 127 +#define GCC_UFS_PHY_RX_SYMBOL_0_CLK 128 +#define GCC_UFS_PHY_RX_SYMBOL_0_CLK_SRC 129 +#define GCC_UFS_PHY_RX_SYMBOL_1_CLK 130 +#define GCC_UFS_PHY_RX_SYMBOL_1_CLK_SRC 131 +#define GCC_UFS_PHY_TX_SYMBOL_0_CLK 132 +#define GCC_UFS_PHY_TX_SYMBOL_0_CLK_SRC 133 +#define GCC_UFS_PHY_UNIPRO_CORE_CLK 134 +#define GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC 135 +#define GCC_USB30_PRIM_ATB_CLK 136 +#define GCC_USB30_PRIM_MASTER_CLK 137 +#define GCC_USB30_PRIM_MASTER_CLK_SRC 138 +#define GCC_USB30_PRIM_MOCK_UTMI_CLK 139 +#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 140 +#define GCC_USB30_PRIM_MOCK_UTMI_POSTDIV_CLK_SRC 141 +#define GCC_USB30_PRIM_SLEEP_CLK 142 +#define GCC_USB3_PRIM_PHY_AUX_CLK 143 +#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 144 +#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 145 +#define GCC_USB3_PRIM_PHY_PIPE_CLK 146 +#define GCC_USB3_PRIM_PHY_PIPE_CLK_SRC 147 +#define GCC_VIDEO_AHB_CLK 148 +#define GCC_VIDEO_AXI0_CLK 149 +#define GCC_VIDEO_AXI1_CLK 150 +#define GCC_VIDEO_XO_CLK 151 + +/* GCC power domains */ +#define GCC_PCIE_0_GDSC 0 +#define GCC_PCIE_0_PHY_GDSC 1 +#define GCC_PCIE_1_GDSC 2 +#define GCC_PCIE_1_PHY_GDSC 3 +#define GCC_UFS_MEM_PHY_GDSC 4 +#define GCC_UFS_PHY_GDSC 5 +#define GCC_USB30_PRIM_GDSC 6 +#define GCC_USB3_PHY_GDSC 7 + +/* GCC resets */ +#define GCC_CAMERA_BCR 0 +#define GCC_DISPLAY_BCR 1 +#define GCC_GPU_BCR 2 +#define GCC_PCIE_0_BCR 3 +#define GCC_PCIE_0_LINK_DOWN_BCR 4 +#define GCC_PCIE_0_NOCSR_COM_PHY_BCR 5 +#define GCC_PCIE_0_PHY_BCR 6 +#define GCC_PCIE_0_PHY_NOCSR_COM_PHY_BCR 7 +#define GCC_PCIE_1_BCR 8 +#define GCC_PCIE_1_LINK_DOWN_BCR 9 +#define GCC_PCIE_1_NOCSR_COM_PHY_BCR 10 +#define GCC_PCIE_1_PHY_BCR 11 +#define GCC_PCIE_1_PHY_NOCSR_COM_PHY_BCR 12 +#define GCC_PCIE_PHY_BCR 13 +#define GCC_PCIE_PHY_CFG_AHB_BCR 14 +#define GCC_PCIE_PHY_COM_BCR 15 +#define GCC_PCIE_RSCC_BCR 16 +#define GCC_PDM_BCR 17 +#define GCC_QUPV3_WRAPPER_1_BCR 18 +#define GCC_QUPV3_WRAPPER_2_BCR 19 +#define GCC_QUSB2PHY_PRIM_BCR 20 +#define GCC_QUSB2PHY_SEC_BCR 21 +#define GCC_SDCC1_BCR 22 +#define GCC_SDCC2_BCR 23 +#define GCC_UFS_PHY_BCR 24 +#define GCC_USB30_PRIM_BCR 25 +#define GCC_USB3_DP_PHY_PRIM_BCR 26 +#define GCC_USB3_DP_PHY_SEC_BCR 27 +#define GCC_USB3_PHY_PRIM_BCR 28 +#define GCC_USB3_PHY_SEC_BCR 29 +#define GCC_USB3PHY_PHY_PRIM_BCR 30 +#define GCC_USB3PHY_PHY_SEC_BCR 31 +#define GCC_VIDEO_AXI0_CLK_ARES 32 +#define GCC_VIDEO_AXI1_CLK_ARES 33 +#define GCC_VIDEO_BCR 34 + +#endif diff --git a/include/dt-bindings/clock/qcom,eliza-tcsr.h b/include/dt-bindings/clock/qcom,eliza-tcsr.h new file mode 100644 index 000000000000..aeb5e2b1a47b --- /dev/null +++ b/include/dt-bindings/clock/qcom,eliza-tcsr.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_TCSR_CC_ELIZA_H +#define _DT_BINDINGS_CLK_QCOM_TCSR_CC_ELIZA_H + +/* TCSR_CC clocks */ +#define TCSR_HDMI_CLKREF_EN 0 +#define TCSR_PCIE_0_CLKREF_EN 1 +#define TCSR_PCIE_1_CLKREF_EN 2 +#define TCSR_UFS_CLKREF_EN 3 +#define TCSR_USB2_CLKREF_EN 4 +#define TCSR_USB3_CLKREF_EN 5 + +#endif diff --git a/include/dt-bindings/clock/qcom,gcc-sc8180x.h b/include/dt-bindings/clock/qcom,gcc-sc8180x.h index b9d8438a15ff..9ed7b794aacc 100644 --- a/include/dt-bindings/clock/qcom,gcc-sc8180x.h +++ b/include/dt-bindings/clock/qcom,gcc-sc8180x.h @@ -322,5 +322,10 @@ #define USB30_MP_GDSC 8 #define USB30_PRIM_GDSC 9 #define USB30_SEC_GDSC 10 +#define HLOS1_VOTE_MMNOC_MMU_TBU_HF0_GDSC 11 +#define HLOS1_VOTE_MMNOC_MMU_TBU_HF1_GDSC 12 +#define HLOS1_VOTE_MMNOC_MMU_TBU_SF_GDSC 13 +#define HLOS1_VOTE_TURING_MMU_TBU0_GDSC 14 +#define HLOS1_VOTE_TURING_MMU_TBU1_GDSC 15 #endif diff --git a/include/dt-bindings/clock/qcom,glymur-gcc.h b/include/dt-bindings/clock/qcom,glymur-gcc.h index 10c12b8c51c3..6907653c7992 100644 --- a/include/dt-bindings/clock/qcom,glymur-gcc.h +++ b/include/dt-bindings/clock/qcom,glymur-gcc.h @@ -574,5 +574,6 @@ #define GCC_VIDEO_AXI0_CLK_ARES 89 #define GCC_VIDEO_AXI1_CLK_ARES 90 #define GCC_VIDEO_BCR 91 +#define GCC_VIDEO_AXI0C_CLK_ARES 92 #endif diff --git a/include/dt-bindings/clock/qcom,glymur-gpucc.h b/include/dt-bindings/clock/qcom,glymur-gpucc.h new file mode 100644 index 000000000000..35f5abb848fd --- /dev/null +++ b/include/dt-bindings/clock/qcom,glymur-gpucc.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2025, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_GPU_CC_GLYMUR_H +#define _DT_BINDINGS_CLK_QCOM_GPU_CC_GLYMUR_H + +/* GPU_CC clocks */ +#define GPU_CC_AHB_CLK 0 +#define GPU_CC_CB_CLK 1 +#define GPU_CC_CX_ACCU_SHIFT_CLK 2 +#define GPU_CC_CX_FF_CLK 3 +#define GPU_CC_CX_GMU_CLK 4 +#define GPU_CC_CXO_AON_CLK 5 +#define GPU_CC_CXO_CLK 6 +#define GPU_CC_DEMET_CLK 7 +#define GPU_CC_DPM_CLK 8 +#define GPU_CC_FF_CLK_SRC 9 +#define GPU_CC_FREQ_MEASURE_CLK 10 +#define GPU_CC_GMU_CLK_SRC 11 +#define GPU_CC_GPU_SMMU_VOTE_CLK 12 +#define GPU_CC_GX_ACCU_SHIFT_CLK 13 +#define GPU_CC_GX_ACD_AHB_FF_CLK 14 +#define GPU_CC_GX_AHB_FF_CLK 15 +#define GPU_CC_GX_GMU_CLK 16 +#define GPU_CC_GX_RCG_AHB_FF_CLK 17 +#define GPU_CC_HUB_AON_CLK 18 +#define GPU_CC_HUB_CLK_SRC 19 +#define GPU_CC_HUB_CX_INT_CLK 20 +#define GPU_CC_HUB_DIV_CLK_SRC 21 +#define GPU_CC_MEMNOC_GFX_CLK 22 +#define GPU_CC_PLL0 23 +#define GPU_CC_PLL0_OUT_EVEN 24 +#define GPU_CC_RSCC_HUB_AON_CLK 25 +#define GPU_CC_RSCC_XO_AON_CLK 26 +#define GPU_CC_SLEEP_CLK 27 + +/* GPU_CC power domains */ +#define GPU_CC_CX_GDSC 0 + +/* GPU_CC resets */ +#define GPU_CC_CB_BCR 0 +#define GPU_CC_CX_BCR 1 +#define GPU_CC_FAST_HUB_BCR 2 +#define GPU_CC_FF_BCR 3 +#define GPU_CC_GMU_BCR 4 +#define GPU_CC_GX_BCR 5 +#define GPU_CC_XO_BCR 6 + +#endif diff --git a/include/dt-bindings/clock/qcom,glymur-videocc.h b/include/dt-bindings/clock/qcom,glymur-videocc.h new file mode 100644 index 000000000000..98c0debef8fa --- /dev/null +++ b/include/dt-bindings/clock/qcom,glymur-videocc.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_VIDEO_CC_GLYMUR_H +#define _DT_BINDINGS_CLK_QCOM_VIDEO_CC_GLYMUR_H + +/* VIDEO_CC clocks */ +#define VIDEO_CC_AHB_CLK 0 +#define VIDEO_CC_AHB_CLK_SRC 1 +#define VIDEO_CC_MVS0_CLK 2 +#define VIDEO_CC_MVS0_CLK_SRC 3 +#define VIDEO_CC_MVS0_DIV_CLK_SRC 4 +#define VIDEO_CC_MVS0_FREERUN_CLK 5 +#define VIDEO_CC_MVS0_SHIFT_CLK 6 +#define VIDEO_CC_MVS0C_CLK 7 +#define VIDEO_CC_MVS0C_DIV2_DIV_CLK_SRC 8 +#define VIDEO_CC_MVS0C_FREERUN_CLK 9 +#define VIDEO_CC_MVS0C_SHIFT_CLK 10 +#define VIDEO_CC_MVS1_CLK 11 +#define VIDEO_CC_MVS1_DIV_CLK_SRC 12 +#define VIDEO_CC_MVS1_FREERUN_CLK 13 +#define VIDEO_CC_MVS1_SHIFT_CLK 14 +#define VIDEO_CC_PLL0 15 +#define VIDEO_CC_SLEEP_CLK 16 +#define VIDEO_CC_SLEEP_CLK_SRC 17 +#define VIDEO_CC_XO_CLK 18 +#define VIDEO_CC_XO_CLK_SRC 19 + +/* VIDEO_CC power domains */ +#define VIDEO_CC_MVS0_GDSC 0 +#define VIDEO_CC_MVS0C_GDSC 1 +#define VIDEO_CC_MVS1_GDSC 2 + +/* VIDEO_CC resets */ +#define VIDEO_CC_INTERFACE_BCR 0 +#define VIDEO_CC_MVS0_BCR 1 +#define VIDEO_CC_MVS0C_BCR 2 +#define VIDEO_CC_MVS0C_FREERUN_CLK_ARES 3 +#define VIDEO_CC_MVS0_FREERUN_CLK_ARES 4 +#define VIDEO_CC_MVS1_FREERUN_CLK_ARES 5 +#define VIDEO_CC_XO_CLK_ARES 6 +#define VIDEO_CC_MVS1_BCR 7 +#endif diff --git a/include/dt-bindings/clock/qcom,ipq5210-gcc.h b/include/dt-bindings/clock/qcom,ipq5210-gcc.h new file mode 100644 index 000000000000..84116f34ee4d --- /dev/null +++ b/include/dt-bindings/clock/qcom,ipq5210-gcc.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLOCK_IPQ_GCC_IPQ5210_H +#define _DT_BINDINGS_CLOCK_IPQ_GCC_IPQ5210_H + +#define GCC_ADSS_PWM_CLK 0 +#define GCC_ADSS_PWM_CLK_SRC 1 +#define GCC_CMN_12GPLL_AHB_CLK 2 +#define GCC_CMN_12GPLL_SYS_CLK 3 +#define GCC_CNOC_LPASS_CFG_CLK 4 +#define GCC_CNOC_PCIE0_1LANE_S_CLK 5 +#define GCC_CNOC_PCIE1_2LANE_S_CLK 6 +#define GCC_CNOC_USB_CLK 7 +#define GCC_GEPHY_SYS_CLK 8 +#define GCC_LPASS_AXIM_CLK_SRC 9 +#define GCC_LPASS_CORE_AXIM_CLK 10 +#define GCC_LPASS_SWAY_CLK 11 +#define GCC_LPASS_SWAY_CLK_SRC 12 +#define GCC_MDIO_AHB_CLK 13 +#define GCC_MDIO_GEPHY_AHB_CLK 14 +#define GCC_NSS_TS_CLK 15 +#define GCC_NSS_TS_CLK_SRC 16 +#define GCC_NSSCC_CLK 17 +#define GCC_NSSCFG_CLK 18 +#define GCC_NSSNOC_ATB_CLK 19 +#define GCC_NSSNOC_MEMNOC_1_CLK 20 +#define GCC_NSSNOC_MEMNOC_BFDCD_CLK_SRC 21 +#define GCC_NSSNOC_MEMNOC_CLK 22 +#define GCC_NSSNOC_MEMNOC_DIV_CLK_SRC 23 +#define GCC_NSSNOC_NSSCC_CLK 24 +#define GCC_NSSNOC_PCNOC_1_CLK 25 +#define GCC_NSSNOC_QOSGEN_REF_CLK 26 +#define GCC_NSSNOC_SNOC_1_CLK 27 +#define GCC_NSSNOC_SNOC_CLK 28 +#define GCC_NSSNOC_TIMEOUT_REF_CLK 29 +#define GCC_NSSNOC_XO_DCD_CLK 30 +#define GCC_PCIE0_AHB_CLK 31 +#define GCC_PCIE0_AUX_CLK 32 +#define GCC_PCIE0_AXI_M_CLK 33 +#define GCC_PCIE0_AXI_M_CLK_SRC 34 +#define GCC_PCIE0_AXI_S_BRIDGE_CLK 35 +#define GCC_PCIE0_AXI_S_CLK 36 +#define GCC_PCIE0_AXI_S_CLK_SRC 37 +#define GCC_PCIE0_PIPE_CLK 38 +#define GCC_PCIE0_PIPE_CLK_SRC 39 +#define GCC_PCIE0_RCHNG_CLK 40 +#define GCC_PCIE0_RCHNG_CLK_SRC 41 +#define GCC_PCIE1_AHB_CLK 42 +#define GCC_PCIE1_AUX_CLK 43 +#define GCC_PCIE1_AXI_M_CLK 44 +#define GCC_PCIE1_AXI_M_CLK_SRC 45 +#define GCC_PCIE1_AXI_S_BRIDGE_CLK 46 +#define GCC_PCIE1_AXI_S_CLK 47 +#define GCC_PCIE1_AXI_S_CLK_SRC 48 +#define GCC_PCIE1_PIPE_CLK 49 +#define GCC_PCIE1_PIPE_CLK_SRC 50 +#define GCC_PCIE1_RCHNG_CLK 51 +#define GCC_PCIE1_RCHNG_CLK_SRC 52 +#define GCC_PCIE_AUX_CLK_SRC 53 +#define GCC_PCNOC_BFDCD_CLK_SRC 54 +#define GCC_PON_APB_CLK 55 +#define GCC_PON_TM_CLK 56 +#define GCC_PON_TM2X_CLK 57 +#define GCC_PON_TM2X_CLK_SRC 58 +#define GCC_QDSS_AT_CLK 59 +#define GCC_QDSS_AT_CLK_SRC 60 +#define GCC_QDSS_DAP_CLK 61 +#define GCC_QDSS_TSCTR_CLK_SRC 62 +#define GCC_QPIC_AHB_CLK 63 +#define GCC_QPIC_CLK 64 +#define GCC_QPIC_CLK_SRC 65 +#define GCC_QPIC_IO_MACRO_CLK 66 +#define GCC_QPIC_IO_MACRO_CLK_SRC 67 +#define GCC_QRNG_AHB_CLK 68 +#define GCC_QUPV3_AHB_MST_CLK 69 +#define GCC_QUPV3_AHB_SLV_CLK 70 +#define GCC_QUPV3_WRAP_SE0_CLK 71 +#define GCC_QUPV3_WRAP_SE0_CLK_SRC 72 +#define GCC_QUPV3_WRAP_SE1_CLK 73 +#define GCC_QUPV3_WRAP_SE1_CLK_SRC 74 +#define GCC_QUPV3_WRAP_SE2_CLK 75 +#define GCC_QUPV3_WRAP_SE2_CLK_SRC 76 +#define GCC_QUPV3_WRAP_SE3_CLK 77 +#define GCC_QUPV3_WRAP_SE3_CLK_SRC 78 +#define GCC_QUPV3_WRAP_SE4_CLK 79 +#define GCC_QUPV3_WRAP_SE4_CLK_SRC 80 +#define GCC_QUPV3_WRAP_SE5_CLK 81 +#define GCC_QUPV3_WRAP_SE5_CLK_SRC 82 +#define GCC_SDCC1_AHB_CLK 83 +#define GCC_SDCC1_APPS_CLK 84 +#define GCC_SDCC1_APPS_CLK_SRC 85 +#define GCC_SDCC1_ICE_CORE_CLK 86 +#define GCC_SDCC1_ICE_CORE_CLK_SRC 87 +#define GCC_SLEEP_CLK_SRC 88 +#define GCC_SNOC_LPASS_CLK 89 +#define GCC_SNOC_PCIE0_AXI_M_CLK 90 +#define GCC_SNOC_PCIE1_AXI_M_CLK 91 +#define GCC_SYSTEM_NOC_BFDCD_CLK_SRC 92 +#define GCC_UNIPHY0_AHB_CLK 93 +#define GCC_UNIPHY0_SYS_CLK 94 +#define GCC_UNIPHY1_AHB_CLK 95 +#define GCC_UNIPHY1_SYS_CLK 96 +#define GCC_UNIPHY2_AHB_CLK 97 +#define GCC_UNIPHY2_SYS_CLK 98 +#define GCC_UNIPHY_SYS_CLK_SRC 99 +#define GCC_USB0_AUX_CLK 100 +#define GCC_USB0_AUX_CLK_SRC 101 +#define GCC_USB0_MASTER_CLK 102 +#define GCC_USB0_MASTER_CLK_SRC 103 +#define GCC_USB0_MOCK_UTMI_CLK 104 +#define GCC_USB0_MOCK_UTMI_CLK_SRC 105 +#define GCC_USB0_MOCK_UTMI_DIV_CLK_SRC 106 +#define GCC_USB0_PHY_CFG_AHB_CLK 107 +#define GCC_USB0_PIPE_CLK 108 +#define GCC_USB0_PIPE_CLK_SRC 109 +#define GCC_USB0_SLEEP_CLK 110 +#define GCC_XO_CLK_SRC 111 +#define GPLL0_MAIN 112 +#define GPLL0 113 +#define GPLL2_MAIN 114 +#define GPLL2 115 +#define GPLL4_MAIN 116 +#endif diff --git a/include/dt-bindings/clock/qcom,ipq6018-cmn-pll.h b/include/dt-bindings/clock/qcom,ipq6018-cmn-pll.h new file mode 100644 index 000000000000..28d325beb073 --- /dev/null +++ b/include/dt-bindings/clock/qcom,ipq6018-cmn-pll.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_IPQ6018_CMN_PLL_H +#define _DT_BINDINGS_CLK_QCOM_IPQ6018_CMN_PLL_H + +/* CMN PLL core clock. */ +#define IPQ6018_CMN_PLL_CLK 0 + +/* The output clocks from CMN PLL of IPQ6018. */ +#define IPQ6018_BIAS_PLL_CC_CLK 1 +#define IPQ6018_BIAS_PLL_NSS_NOC_CLK 2 +#endif diff --git a/include/dt-bindings/clock/qcom,ipq8074-cmn-pll.h b/include/dt-bindings/clock/qcom,ipq8074-cmn-pll.h new file mode 100644 index 000000000000..354258a481c2 --- /dev/null +++ b/include/dt-bindings/clock/qcom,ipq8074-cmn-pll.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_IPQ8074_CMN_PLL_H +#define _DT_BINDINGS_CLK_QCOM_IPQ8074_CMN_PLL_H + +/* CMN PLL core clock. */ +#define IPQ8074_CMN_PLL_CLK 0 + +/* The output clocks from CMN PLL of IPQ8074. */ +#define IPQ8074_BIAS_PLL_CC_CLK 1 +#define IPQ8074_BIAS_PLL_NSS_NOC_CLK 2 +#endif diff --git a/include/dt-bindings/clock/qcom,nord-gcc.h b/include/dt-bindings/clock/qcom,nord-gcc.h new file mode 100644 index 000000000000..8fbde162c859 --- /dev/null +++ b/include/dt-bindings/clock/qcom,nord-gcc.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_GCC_NORD_H +#define _DT_BINDINGS_CLK_QCOM_GCC_NORD_H + +/* GCC clocks */ +#define GCC_BOOT_ROM_AHB_CLK 0 +#define GCC_GP1_CLK 1 +#define GCC_GP1_CLK_SRC 2 +#define GCC_GP2_CLK 3 +#define GCC_GP2_CLK_SRC 4 +#define GCC_GPLL0 5 +#define GCC_GPLL0_OUT_EVEN 6 +#define GCC_MMU_0_TCU_VOTE_CLK 7 +#define GCC_PCIE_A_AUX_CLK 8 +#define GCC_PCIE_A_AUX_CLK_SRC 9 +#define GCC_PCIE_A_CFG_AHB_CLK 10 +#define GCC_PCIE_A_DTI_QTC_CLK 11 +#define GCC_PCIE_A_MSTR_AXI_CLK 12 +#define GCC_PCIE_A_PHY_AUX_CLK 13 +#define GCC_PCIE_A_PHY_AUX_CLK_SRC 14 +#define GCC_PCIE_A_PHY_RCHNG_CLK 15 +#define GCC_PCIE_A_PHY_RCHNG_CLK_SRC 16 +#define GCC_PCIE_A_PIPE_CLK 17 +#define GCC_PCIE_A_PIPE_CLK_SRC 18 +#define GCC_PCIE_A_SLV_AXI_CLK 19 +#define GCC_PCIE_A_SLV_Q2A_AXI_CLK 20 +#define GCC_PCIE_B_AUX_CLK 21 +#define GCC_PCIE_B_AUX_CLK_SRC 22 +#define GCC_PCIE_B_CFG_AHB_CLK 23 +#define GCC_PCIE_B_DTI_QTC_CLK 24 +#define GCC_PCIE_B_MSTR_AXI_CLK 25 +#define GCC_PCIE_B_PHY_AUX_CLK 26 +#define GCC_PCIE_B_PHY_AUX_CLK_SRC 27 +#define GCC_PCIE_B_PHY_RCHNG_CLK 28 +#define GCC_PCIE_B_PHY_RCHNG_CLK_SRC 29 +#define GCC_PCIE_B_PIPE_CLK 30 +#define GCC_PCIE_B_PIPE_CLK_SRC 31 +#define GCC_PCIE_B_SLV_AXI_CLK 32 +#define GCC_PCIE_B_SLV_Q2A_AXI_CLK 33 +#define GCC_PCIE_C_AUX_CLK 34 +#define GCC_PCIE_C_AUX_CLK_SRC 35 +#define GCC_PCIE_C_CFG_AHB_CLK 36 +#define GCC_PCIE_C_DTI_QTC_CLK 37 +#define GCC_PCIE_C_MSTR_AXI_CLK 38 +#define GCC_PCIE_C_PHY_AUX_CLK 39 +#define GCC_PCIE_C_PHY_AUX_CLK_SRC 40 +#define GCC_PCIE_C_PHY_RCHNG_CLK 41 +#define GCC_PCIE_C_PHY_RCHNG_CLK_SRC 42 +#define GCC_PCIE_C_PIPE_CLK 43 +#define GCC_PCIE_C_PIPE_CLK_SRC 44 +#define GCC_PCIE_C_SLV_AXI_CLK 45 +#define GCC_PCIE_C_SLV_Q2A_AXI_CLK 46 +#define GCC_PCIE_D_AUX_CLK 47 +#define GCC_PCIE_D_AUX_CLK_SRC 48 +#define GCC_PCIE_D_CFG_AHB_CLK 49 +#define GCC_PCIE_D_DTI_QTC_CLK 50 +#define GCC_PCIE_D_MSTR_AXI_CLK 51 +#define GCC_PCIE_D_PHY_AUX_CLK 52 +#define GCC_PCIE_D_PHY_AUX_CLK_SRC 53 +#define GCC_PCIE_D_PHY_RCHNG_CLK 54 +#define GCC_PCIE_D_PHY_RCHNG_CLK_SRC 55 +#define GCC_PCIE_D_PIPE_CLK 56 +#define GCC_PCIE_D_PIPE_CLK_SRC 57 +#define GCC_PCIE_D_SLV_AXI_CLK 58 +#define GCC_PCIE_D_SLV_Q2A_AXI_CLK 59 +#define GCC_PCIE_LINK_AHB_CLK 60 +#define GCC_PCIE_LINK_XO_CLK 61 +#define GCC_PCIE_NOC_ASYNC_BRIDGE_CLK 62 +#define GCC_PCIE_NOC_CNOC_SF_QX_CLK 63 +#define GCC_PCIE_NOC_M_CFG_CLK 64 +#define GCC_PCIE_NOC_M_PDB_CLK 65 +#define GCC_PCIE_NOC_MSTR_AXI_CLK 66 +#define GCC_PCIE_NOC_PWRCTL_CLK 67 +#define GCC_PCIE_NOC_QOSGEN_EXTREF_CLK 68 +#define GCC_PCIE_NOC_REFGEN_CLK 69 +#define GCC_PCIE_NOC_REFGEN_CLK_SRC 70 +#define GCC_PCIE_NOC_S_CFG_CLK 71 +#define GCC_PCIE_NOC_S_PDB_CLK 72 +#define GCC_PCIE_NOC_SAFETY_CLK 73 +#define GCC_PCIE_NOC_SAFETY_CLK_SRC 74 +#define GCC_PCIE_NOC_SLAVE_AXI_CLK 75 +#define GCC_PCIE_NOC_TSCTR_CLK 76 +#define GCC_PCIE_NOC_XO_CLK 77 +#define GCC_PDM2_CLK 78 +#define GCC_PDM2_CLK_SRC 79 +#define GCC_PDM_AHB_CLK 80 +#define GCC_PDM_XO4_CLK 81 +#define GCC_QUPV3_WRAP3_CORE_2X_CLK 82 +#define GCC_QUPV3_WRAP3_CORE_CLK 83 +#define GCC_QUPV3_WRAP3_M_CLK 84 +#define GCC_QUPV3_WRAP3_QSPI_REF_CLK 85 +#define GCC_QUPV3_WRAP3_QSPI_REF_CLK_SRC 86 +#define GCC_QUPV3_WRAP3_S0_CLK 87 +#define GCC_QUPV3_WRAP3_S0_CLK_SRC 88 +#define GCC_QUPV3_WRAP3_S_AHB_CLK 89 +#define GCC_SMMU_PCIE_QTC_VOTE_CLK 90 + +/* GCC power domains */ +#define GCC_PCIE_A_GDSC 0 +#define GCC_PCIE_A_PHY_GDSC 1 +#define GCC_PCIE_B_GDSC 2 +#define GCC_PCIE_B_PHY_GDSC 3 +#define GCC_PCIE_C_GDSC 4 +#define GCC_PCIE_C_PHY_GDSC 5 +#define GCC_PCIE_D_GDSC 6 +#define GCC_PCIE_D_PHY_GDSC 7 +#define GCC_PCIE_NOC_GDSC 8 + +/* GCC resets */ +#define GCC_PCIE_A_BCR 0 +#define GCC_PCIE_A_LINK_DOWN_BCR 1 +#define GCC_PCIE_A_NOCSR_COM_PHY_BCR 2 +#define GCC_PCIE_A_PHY_BCR 3 +#define GCC_PCIE_A_PHY_CFG_AHB_BCR 4 +#define GCC_PCIE_A_PHY_COM_BCR 5 +#define GCC_PCIE_A_PHY_NOCSR_COM_PHY_BCR 6 +#define GCC_PCIE_B_BCR 7 +#define GCC_PCIE_B_LINK_DOWN_BCR 8 +#define GCC_PCIE_B_NOCSR_COM_PHY_BCR 9 +#define GCC_PCIE_B_PHY_BCR 10 +#define GCC_PCIE_B_PHY_CFG_AHB_BCR 11 +#define GCC_PCIE_B_PHY_COM_BCR 12 +#define GCC_PCIE_B_PHY_NOCSR_COM_PHY_BCR 13 +#define GCC_PCIE_C_BCR 14 +#define GCC_PCIE_C_LINK_DOWN_BCR 15 +#define GCC_PCIE_C_NOCSR_COM_PHY_BCR 16 +#define GCC_PCIE_C_PHY_BCR 17 +#define GCC_PCIE_C_PHY_CFG_AHB_BCR 18 +#define GCC_PCIE_C_PHY_COM_BCR 19 +#define GCC_PCIE_C_PHY_NOCSR_COM_PHY_BCR 20 +#define GCC_PCIE_D_BCR 21 +#define GCC_PCIE_D_LINK_DOWN_BCR 22 +#define GCC_PCIE_D_NOCSR_COM_PHY_BCR 23 +#define GCC_PCIE_D_PHY_BCR 24 +#define GCC_PCIE_D_PHY_CFG_AHB_BCR 25 +#define GCC_PCIE_D_PHY_COM_BCR 26 +#define GCC_PCIE_D_PHY_NOCSR_COM_PHY_BCR 27 +#define GCC_PCIE_NOC_BCR 28 +#define GCC_PDM_BCR 29 +#define GCC_QUPV3_WRAPPER_3_BCR 30 +#define GCC_TCSR_PCIE_BCR 31 + +#endif diff --git a/include/dt-bindings/clock/qcom,nord-negcc.h b/include/dt-bindings/clock/qcom,nord-negcc.h new file mode 100644 index 000000000000..95f333d8e1aa --- /dev/null +++ b/include/dt-bindings/clock/qcom,nord-negcc.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_NE_GCC_NORD_H +#define _DT_BINDINGS_CLK_QCOM_NE_GCC_NORD_H + +/* NE_GCC clocks */ +#define NE_GCC_AGGRE_NOC_UFS_PHY_AXI_CLK 0 +#define NE_GCC_AGGRE_NOC_USB2_AXI_CLK 1 +#define NE_GCC_AGGRE_NOC_USB3_PRIM_AXI_CLK 2 +#define NE_GCC_AGGRE_NOC_USB3_SEC_AXI_CLK 3 +#define NE_GCC_AHB2PHY_CLK 4 +#define NE_GCC_CNOC_USB2_AXI_CLK 5 +#define NE_GCC_CNOC_USB3_PRIM_AXI_CLK 6 +#define NE_GCC_CNOC_USB3_SEC_AXI_CLK 7 +#define NE_GCC_FRQ_MEASURE_REF_CLK 8 +#define NE_GCC_GP1_CLK 9 +#define NE_GCC_GP1_CLK_SRC 10 +#define NE_GCC_GP2_CLK 11 +#define NE_GCC_GP2_CLK_SRC 12 +#define NE_GCC_GPLL0 13 +#define NE_GCC_GPLL0_OUT_EVEN 14 +#define NE_GCC_GPLL2 15 +#define NE_GCC_GPU_2_CFG_CLK 16 +#define NE_GCC_GPU_2_GPLL0_CLK_SRC 17 +#define NE_GCC_GPU_2_GPLL0_DIV_CLK_SRC 18 +#define NE_GCC_GPU_2_HSCNOC_GFX_CLK 19 +#define NE_GCC_GPU_2_SMMU_VOTE_CLK 20 +#define NE_GCC_QUPV3_WRAP2_CORE_2X_CLK 21 +#define NE_GCC_QUPV3_WRAP2_CORE_CLK 22 +#define NE_GCC_QUPV3_WRAP2_M_AHB_CLK 23 +#define NE_GCC_QUPV3_WRAP2_S0_CLK 24 +#define NE_GCC_QUPV3_WRAP2_S0_CLK_SRC 25 +#define NE_GCC_QUPV3_WRAP2_S1_CLK 26 +#define NE_GCC_QUPV3_WRAP2_S1_CLK_SRC 27 +#define NE_GCC_QUPV3_WRAP2_S2_CLK 28 +#define NE_GCC_QUPV3_WRAP2_S2_CLK_SRC 29 +#define NE_GCC_QUPV3_WRAP2_S3_CLK 30 +#define NE_GCC_QUPV3_WRAP2_S3_CLK_SRC 31 +#define NE_GCC_QUPV3_WRAP2_S4_CLK 32 +#define NE_GCC_QUPV3_WRAP2_S4_CLK_SRC 33 +#define NE_GCC_QUPV3_WRAP2_S5_CLK 34 +#define NE_GCC_QUPV3_WRAP2_S5_CLK_SRC 35 +#define NE_GCC_QUPV3_WRAP2_S6_CLK 36 +#define NE_GCC_QUPV3_WRAP2_S6_CLK_SRC 37 +#define NE_GCC_QUPV3_WRAP2_S_AHB_CLK 38 +#define NE_GCC_SDCC4_APPS_CLK 39 +#define NE_GCC_SDCC4_APPS_CLK_SRC 40 +#define NE_GCC_SDCC4_AXI_CLK 41 +#define NE_GCC_UFS_PHY_AHB_CLK 42 +#define NE_GCC_UFS_PHY_AXI_CLK 43 +#define NE_GCC_UFS_PHY_AXI_CLK_SRC 44 +#define NE_GCC_UFS_PHY_ICE_CORE_CLK 45 +#define NE_GCC_UFS_PHY_ICE_CORE_CLK_SRC 46 +#define NE_GCC_UFS_PHY_PHY_AUX_CLK 47 +#define NE_GCC_UFS_PHY_PHY_AUX_CLK_SRC 48 +#define NE_GCC_UFS_PHY_RX_SYMBOL_0_CLK 49 +#define NE_GCC_UFS_PHY_RX_SYMBOL_0_CLK_SRC 50 +#define NE_GCC_UFS_PHY_RX_SYMBOL_1_CLK 51 +#define NE_GCC_UFS_PHY_RX_SYMBOL_1_CLK_SRC 52 +#define NE_GCC_UFS_PHY_TX_SYMBOL_0_CLK 53 +#define NE_GCC_UFS_PHY_TX_SYMBOL_0_CLK_SRC 54 +#define NE_GCC_UFS_PHY_UNIPRO_CORE_CLK 55 +#define NE_GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC 56 +#define NE_GCC_USB20_MASTER_CLK 57 +#define NE_GCC_USB20_MASTER_CLK_SRC 58 +#define NE_GCC_USB20_MOCK_UTMI_CLK 59 +#define NE_GCC_USB20_MOCK_UTMI_CLK_SRC 60 +#define NE_GCC_USB20_MOCK_UTMI_POSTDIV_CLK_SRC 61 +#define NE_GCC_USB20_SLEEP_CLK 62 +#define NE_GCC_USB31_PRIM_ATB_CLK 63 +#define NE_GCC_USB31_PRIM_EUD_AHB_CLK 64 +#define NE_GCC_USB31_PRIM_MASTER_CLK 65 +#define NE_GCC_USB31_PRIM_MASTER_CLK_SRC 66 +#define NE_GCC_USB31_PRIM_MOCK_UTMI_CLK 67 +#define NE_GCC_USB31_PRIM_MOCK_UTMI_CLK_SRC 68 +#define NE_GCC_USB31_PRIM_MOCK_UTMI_POSTDIV_CLK_SRC 69 +#define NE_GCC_USB31_PRIM_SLEEP_CLK 70 +#define NE_GCC_USB31_SEC_ATB_CLK 71 +#define NE_GCC_USB31_SEC_EUD_AHB_CLK 72 +#define NE_GCC_USB31_SEC_MASTER_CLK 73 +#define NE_GCC_USB31_SEC_MASTER_CLK_SRC 74 +#define NE_GCC_USB31_SEC_MOCK_UTMI_CLK 75 +#define NE_GCC_USB31_SEC_MOCK_UTMI_CLK_SRC 76 +#define NE_GCC_USB31_SEC_MOCK_UTMI_POSTDIV_CLK_SRC 77 +#define NE_GCC_USB31_SEC_SLEEP_CLK 78 +#define NE_GCC_USB3_PRIM_PHY_AUX_CLK 79 +#define NE_GCC_USB3_PRIM_PHY_AUX_CLK_SRC 80 +#define NE_GCC_USB3_PRIM_PHY_COM_AUX_CLK 81 +#define NE_GCC_USB3_PRIM_PHY_PIPE_CLK 82 +#define NE_GCC_USB3_PRIM_PHY_PIPE_CLK_SRC 83 +#define NE_GCC_USB3_SEC_PHY_AUX_CLK 84 +#define NE_GCC_USB3_SEC_PHY_AUX_CLK_SRC 85 +#define NE_GCC_USB3_SEC_PHY_COM_AUX_CLK 86 +#define NE_GCC_USB3_SEC_PHY_PIPE_CLK 87 +#define NE_GCC_USB3_SEC_PHY_PIPE_CLK_SRC 88 + +/* NE_GCC power domains */ +#define NE_GCC_UFS_MEM_PHY_GDSC 0 +#define NE_GCC_UFS_PHY_GDSC 1 +#define NE_GCC_USB20_PRIM_GDSC 2 +#define NE_GCC_USB31_PRIM_GDSC 3 +#define NE_GCC_USB31_SEC_GDSC 4 +#define NE_GCC_USB3_PHY_GDSC 5 +#define NE_GCC_USB3_SEC_PHY_GDSC 6 + +/* NE_GCC resets */ +#define NE_GCC_GPU_2_BCR 0 +#define NE_GCC_QUPV3_WRAPPER_2_BCR 1 +#define NE_GCC_SDCC4_BCR 2 +#define NE_GCC_UFS_PHY_BCR 3 +#define NE_GCC_USB20_PRIM_BCR 4 +#define NE_GCC_USB31_PRIM_BCR 5 +#define NE_GCC_USB31_SEC_BCR 6 +#define NE_GCC_USB3_DP_PHY_PRIM_BCR 7 +#define NE_GCC_USB3_DP_PHY_SEC_BCR 8 +#define NE_GCC_USB3_PHY_PRIM_BCR 9 +#define NE_GCC_USB3_PHY_SEC_BCR 10 +#define NE_GCC_USB3PHY_PHY_PRIM_BCR 11 +#define NE_GCC_USB3PHY_PHY_SEC_BCR 12 + +#endif diff --git a/include/dt-bindings/clock/qcom,nord-nwgcc.h b/include/dt-bindings/clock/qcom,nord-nwgcc.h new file mode 100644 index 000000000000..b6253dd2aa85 --- /dev/null +++ b/include/dt-bindings/clock/qcom,nord-nwgcc.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_NW_GCC_NORD_H +#define _DT_BINDINGS_CLK_QCOM_NW_GCC_NORD_H + +/* NW_GCC clocks */ +#define NW_GCC_ACMU_MUX_CLK 0 +#define NW_GCC_CAMERA_AHB_CLK 1 +#define NW_GCC_CAMERA_HF_AXI_CLK 2 +#define NW_GCC_CAMERA_SF_AXI_CLK 3 +#define NW_GCC_CAMERA_TRIG_CLK 4 +#define NW_GCC_CAMERA_XO_CLK 5 +#define NW_GCC_DISP_0_AHB_CLK 6 +#define NW_GCC_DISP_0_HF_AXI_CLK 7 +#define NW_GCC_DISP_0_TRIG_CLK 8 +#define NW_GCC_DISP_1_AHB_CLK 9 +#define NW_GCC_DISP_1_HF_AXI_CLK 10 +#define NW_GCC_DISP_1_TRIG_CLK 11 +#define NW_GCC_DPRX0_AXI_HF_CLK 12 +#define NW_GCC_DPRX0_CFG_AHB_CLK 13 +#define NW_GCC_DPRX1_AXI_HF_CLK 14 +#define NW_GCC_DPRX1_CFG_AHB_CLK 15 +#define NW_GCC_EVA_AHB_CLK 16 +#define NW_GCC_EVA_AXI0_CLK 17 +#define NW_GCC_EVA_AXI0C_CLK 18 +#define NW_GCC_EVA_TRIG_CLK 19 +#define NW_GCC_EVA_XO_CLK 20 +#define NW_GCC_FRQ_MEASURE_REF_CLK 21 +#define NW_GCC_GP1_CLK 22 +#define NW_GCC_GP1_CLK_SRC 23 +#define NW_GCC_GP2_CLK 24 +#define NW_GCC_GP2_CLK_SRC 25 +#define NW_GCC_GPLL0 26 +#define NW_GCC_GPLL0_OUT_EVEN 27 +#define NW_GCC_GPU_2_CFG_AHB_CLK 28 +#define NW_GCC_GPU_2_GPLL0_CLK_SRC 29 +#define NW_GCC_GPU_2_GPLL0_DIV_CLK_SRC 30 +#define NW_GCC_GPU_2_HSCNOC_GFX_CLK 31 +#define NW_GCC_GPU_CFG_AHB_CLK 32 +#define NW_GCC_GPU_GPLL0_CLK_SRC 33 +#define NW_GCC_GPU_GPLL0_DIV_CLK_SRC 34 +#define NW_GCC_GPU_HSCNOC_GFX_CLK 35 +#define NW_GCC_GPU_SMMU_VOTE_CLK 36 +#define NW_GCC_HSCNOC_GPU_2_AXI_CLK 37 +#define NW_GCC_HSCNOC_GPU_AXI_CLK 38 +#define NW_GCC_MMU_1_TCU_VOTE_CLK 39 +#define NW_GCC_VIDEO_AHB_CLK 40 +#define NW_GCC_VIDEO_AXI0_CLK 41 +#define NW_GCC_VIDEO_AXI0C_CLK 42 +#define NW_GCC_VIDEO_AXI1_CLK 43 +#define NW_GCC_VIDEO_XO_CLK 44 + +/* NW_GCC power domains */ + +/* NW_GCC resets */ +#define NW_GCC_CAMERA_BCR 0 +#define NW_GCC_DISPLAY_0_BCR 1 +#define NW_GCC_DISPLAY_1_BCR 2 +#define NW_GCC_DPRX0_BCR 3 +#define NW_GCC_DPRX1_BCR 4 +#define NW_GCC_EVA_BCR 5 +#define NW_GCC_GPU_2_BCR 6 +#define NW_GCC_GPU_BCR 7 +#define NW_GCC_VIDEO_BCR 8 + +#endif diff --git a/include/dt-bindings/clock/qcom,nord-segcc.h b/include/dt-bindings/clock/qcom,nord-segcc.h new file mode 100644 index 000000000000..f0f7422af692 --- /dev/null +++ b/include/dt-bindings/clock/qcom,nord-segcc.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_SE_GCC_NORD_H +#define _DT_BINDINGS_CLK_QCOM_SE_GCC_NORD_H + +/* SE_GCC clocks */ +#define SE_GCC_EEE_EMAC0_CLK 0 +#define SE_GCC_EEE_EMAC0_CLK_SRC 1 +#define SE_GCC_EEE_EMAC1_CLK 2 +#define SE_GCC_EEE_EMAC1_CLK_SRC 3 +#define SE_GCC_EMAC0_AXI_CLK 4 +#define SE_GCC_EMAC0_CC_SGMIIPHY_RX_CLK 5 +#define SE_GCC_EMAC0_CC_SGMIIPHY_TX_CLK 6 +#define SE_GCC_EMAC0_PHY_AUX_CLK 7 +#define SE_GCC_EMAC0_PHY_AUX_CLK_SRC 8 +#define SE_GCC_EMAC0_PTP_CLK 9 +#define SE_GCC_EMAC0_PTP_CLK_SRC 10 +#define SE_GCC_EMAC0_RGMII_CLK 11 +#define SE_GCC_EMAC0_RGMII_CLK_SRC 12 +#define SE_GCC_EMAC0_RPCS_RX_CLK 13 +#define SE_GCC_EMAC0_RPCS_TX_CLK 14 +#define SE_GCC_EMAC0_XGXS_RX_CLK 15 +#define SE_GCC_EMAC0_XGXS_TX_CLK 16 +#define SE_GCC_EMAC1_AXI_CLK 17 +#define SE_GCC_EMAC1_CC_SGMIIPHY_RX_CLK 18 +#define SE_GCC_EMAC1_CC_SGMIIPHY_TX_CLK 19 +#define SE_GCC_EMAC1_PHY_AUX_CLK 20 +#define SE_GCC_EMAC1_PHY_AUX_CLK_SRC 21 +#define SE_GCC_EMAC1_PTP_CLK 22 +#define SE_GCC_EMAC1_PTP_CLK_SRC 23 +#define SE_GCC_EMAC1_RGMII_CLK 24 +#define SE_GCC_EMAC1_RGMII_CLK_SRC 25 +#define SE_GCC_EMAC1_RPCS_RX_CLK 26 +#define SE_GCC_EMAC1_RPCS_TX_CLK 27 +#define SE_GCC_EMAC1_XGXS_RX_CLK 28 +#define SE_GCC_EMAC1_XGXS_TX_CLK 29 +#define SE_GCC_FRQ_MEASURE_REF_CLK 30 +#define SE_GCC_GP1_CLK 31 +#define SE_GCC_GP1_CLK_SRC 32 +#define SE_GCC_GP2_CLK 33 +#define SE_GCC_GP2_CLK_SRC 34 +#define SE_GCC_GPLL0 35 +#define SE_GCC_GPLL0_OUT_EVEN 36 +#define SE_GCC_GPLL2 37 +#define SE_GCC_GPLL4 38 +#define SE_GCC_GPLL5 39 +#define SE_GCC_MMU_2_TCU_VOTE_CLK 40 +#define SE_GCC_QUPV3_WRAP0_CORE_2X_CLK 41 +#define SE_GCC_QUPV3_WRAP0_CORE_CLK 42 +#define SE_GCC_QUPV3_WRAP0_M_AHB_CLK 43 +#define SE_GCC_QUPV3_WRAP0_S0_CLK 44 +#define SE_GCC_QUPV3_WRAP0_S0_CLK_SRC 45 +#define SE_GCC_QUPV3_WRAP0_S1_CLK 46 +#define SE_GCC_QUPV3_WRAP0_S1_CLK_SRC 47 +#define SE_GCC_QUPV3_WRAP0_S2_CLK 48 +#define SE_GCC_QUPV3_WRAP0_S2_CLK_SRC 49 +#define SE_GCC_QUPV3_WRAP0_S3_CLK 50 +#define SE_GCC_QUPV3_WRAP0_S3_CLK_SRC 51 +#define SE_GCC_QUPV3_WRAP0_S4_CLK 52 +#define SE_GCC_QUPV3_WRAP0_S4_CLK_SRC 53 +#define SE_GCC_QUPV3_WRAP0_S5_CLK 54 +#define SE_GCC_QUPV3_WRAP0_S5_CLK_SRC 55 +#define SE_GCC_QUPV3_WRAP0_S6_CLK 56 +#define SE_GCC_QUPV3_WRAP0_S6_CLK_SRC 57 +#define SE_GCC_QUPV3_WRAP0_S_AHB_CLK 58 +#define SE_GCC_QUPV3_WRAP1_CORE_2X_CLK 59 +#define SE_GCC_QUPV3_WRAP1_CORE_CLK 60 +#define SE_GCC_QUPV3_WRAP1_M_AHB_CLK 61 +#define SE_GCC_QUPV3_WRAP1_S0_CLK 62 +#define SE_GCC_QUPV3_WRAP1_S0_CLK_SRC 63 +#define SE_GCC_QUPV3_WRAP1_S1_CLK 64 +#define SE_GCC_QUPV3_WRAP1_S1_CLK_SRC 65 +#define SE_GCC_QUPV3_WRAP1_S2_CLK 66 +#define SE_GCC_QUPV3_WRAP1_S2_CLK_SRC 67 +#define SE_GCC_QUPV3_WRAP1_S3_CLK 68 +#define SE_GCC_QUPV3_WRAP1_S3_CLK_SRC 69 +#define SE_GCC_QUPV3_WRAP1_S4_CLK 70 +#define SE_GCC_QUPV3_WRAP1_S4_CLK_SRC 71 +#define SE_GCC_QUPV3_WRAP1_S5_CLK 72 +#define SE_GCC_QUPV3_WRAP1_S5_CLK_SRC 73 +#define SE_GCC_QUPV3_WRAP1_S6_CLK 74 +#define SE_GCC_QUPV3_WRAP1_S6_CLK_SRC 75 +#define SE_GCC_QUPV3_WRAP1_S_AHB_CLK 76 + +/* SE_GCC power domains */ +#define SE_GCC_EMAC0_GDSC 0 +#define SE_GCC_EMAC1_GDSC 1 + +/* SE_GCC resets */ +#define SE_GCC_EMAC0_BCR 0 +#define SE_GCC_EMAC1_BCR 1 +#define SE_GCC_QUPV3_WRAPPER_0_BCR 2 +#define SE_GCC_QUPV3_WRAPPER_1_BCR 3 + +#endif diff --git a/include/dt-bindings/clock/qcom,nord-tcsrcc.h b/include/dt-bindings/clock/qcom,nord-tcsrcc.h new file mode 100644 index 000000000000..3f0e2ff7acc7 --- /dev/null +++ b/include/dt-bindings/clock/qcom,nord-tcsrcc.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_TCSR_CC_NORD_H +#define _DT_BINDINGS_CLK_QCOM_TCSR_CC_NORD_H + +/* TCSR_CC clocks */ +#define TCSR_DP_RX_0_CLKREF_EN 0 +#define TCSR_DP_RX_1_CLKREF_EN 1 +#define TCSR_DP_TX_0_CLKREF_EN 2 +#define TCSR_DP_TX_1_CLKREF_EN 3 +#define TCSR_DP_TX_2_CLKREF_EN 4 +#define TCSR_DP_TX_3_CLKREF_EN 5 +#define TCSR_PCIE_CLKREF_EN 6 +#define TCSR_UFS_CLKREF_EN 7 +#define TCSR_USB2_0_CLKREF_EN 8 +#define TCSR_USB2_1_CLKREF_EN 9 +#define TCSR_USB2_2_CLKREF_EN 10 +#define TCSR_USB3_0_CLKREF_EN 11 +#define TCSR_USB3_1_CLKREF_EN 12 +#define TCSR_UX_SGMII_0_CLKREF_EN 13 +#define TCSR_UX_SGMII_1_CLKREF_EN 14 + +#endif diff --git a/include/dt-bindings/clock/qcom,sm6115-dispcc.h b/include/dt-bindings/clock/qcom,sm6115-dispcc.h index d1a6c45b5029..ab8d312ade37 100644 --- a/include/dt-bindings/clock/qcom,sm6115-dispcc.h +++ b/include/dt-bindings/clock/qcom,sm6115-dispcc.h @@ -6,7 +6,7 @@ #ifndef _DT_BINDINGS_CLK_QCOM_DISP_CC_SM6115_H #define _DT_BINDINGS_CLK_QCOM_DISP_CC_SM6115_H -/* DISP_CC clocks */ +/* Clocks */ #define DISP_CC_PLL0 0 #define DISP_CC_PLL0_OUT_MAIN 1 #define DISP_CC_MDSS_AHB_CLK 2 @@ -30,7 +30,10 @@ #define DISP_CC_SLEEP_CLK 20 #define DISP_CC_SLEEP_CLK_SRC 21 -/* DISP_CC GDSCR */ +/* Resets */ +#define DISP_CC_MDSS_CORE_BCR 0 + +/* GDSCs */ #define MDSS_GDSC 0 #endif diff --git a/include/dt-bindings/clock/qcom,sm8750-gpucc.h b/include/dt-bindings/clock/qcom,sm8750-gpucc.h new file mode 100644 index 000000000000..e2143d905fec --- /dev/null +++ b/include/dt-bindings/clock/qcom,sm8750-gpucc.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#ifndef _DT_BINDINGS_CLK_QCOM_GPU_CC_SM8750_H +#define _DT_BINDINGS_CLK_QCOM_GPU_CC_SM8750_H + +/* GPU_CC clocks */ +#define GPU_CC_AHB_CLK 0 +#define GPU_CC_CB_CLK 1 +#define GPU_CC_CX_ACCU_SHIFT_CLK 2 +#define GPU_CC_CX_FF_CLK 3 +#define GPU_CC_CX_GMU_CLK 4 +#define GPU_CC_CXO_AON_CLK 5 +#define GPU_CC_CXO_CLK 6 +#define GPU_CC_DEMET_CLK 7 +#define GPU_CC_DPM_CLK 8 +#define GPU_CC_FF_CLK_SRC 9 +#define GPU_CC_FREQ_MEASURE_CLK 10 +#define GPU_CC_GMU_CLK_SRC 11 +#define GPU_CC_GX_ACCU_SHIFT_CLK 12 +#define GPU_CC_GX_ACD_AHB_FF_CLK 13 +#define GPU_CC_GX_AHB_FF_CLK 14 +#define GPU_CC_GX_GMU_CLK 15 +#define GPU_CC_GX_RCG_AHB_FF_CLK 16 +#define GPU_CC_HLOS1_VOTE_GPU_SMMU_CLK 17 +#define GPU_CC_HUB_AON_CLK 18 +#define GPU_CC_HUB_CLK_SRC 19 +#define GPU_CC_HUB_CX_INT_CLK 20 +#define GPU_CC_HUB_DIV_CLK_SRC 21 +#define GPU_CC_MEMNOC_GFX_CLK 22 +#define GPU_CC_PLL0 23 +#define GPU_CC_PLL0_OUT_EVEN 24 +#define GPU_CC_RSCC_HUB_AON_CLK 25 +#define GPU_CC_RSCC_XO_AON_CLK 26 +#define GPU_CC_SLEEP_CLK 27 + +/* GPU_CC power domains */ +#define GPU_CC_CX_GDSC 0 + +/* GPU_CC resets */ +#define GPU_CC_GPU_CC_CB_BCR 0 +#define GPU_CC_GPU_CC_CX_BCR 1 +#define GPU_CC_GPU_CC_FAST_HUB_BCR 2 +#define GPU_CC_GPU_CC_FF_BCR 3 +#define GPU_CC_GPU_CC_GMU_BCR 4 +#define GPU_CC_GPU_CC_GX_BCR 5 +#define GPU_CC_GPU_CC_XO_BCR 6 + +#endif diff --git a/include/dt-bindings/clock/renesas,r9a08g046-cpg.h b/include/dt-bindings/clock/renesas,r9a08g046-cpg.h new file mode 100644 index 000000000000..018b0a1e4340 --- /dev/null +++ b/include/dt-bindings/clock/renesas,r9a08g046-cpg.h @@ -0,0 +1,342 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + * + * Copyright (C) 2026 Renesas Electronics Corp. + */ +#ifndef __DT_BINDINGS_CLOCK_RENESAS_R9A08G046_CPG_H__ +#define __DT_BINDINGS_CLOCK_RENESAS_R9A08G046_CPG_H__ + +#include + +/* R9A08G046 CPG Core Clocks */ +#define R9A08G046_CLK_I 0 +#define R9A08G046_CLK_IC0 1 +#define R9A08G046_CLK_IC1 2 +#define R9A08G046_CLK_IC2 3 +#define R9A08G046_CLK_IC3 4 +#define R9A08G046_CLK_P0 5 +#define R9A08G046_CLK_P1 6 +#define R9A08G046_CLK_P2 7 +#define R9A08G046_CLK_P3 8 +#define R9A08G046_CLK_P4 9 +#define R9A08G046_CLK_P5 10 +#define R9A08G046_CLK_P6 11 +#define R9A08G046_CLK_P7 12 +#define R9A08G046_CLK_P8 13 +#define R9A08G046_CLK_P9 14 +#define R9A08G046_CLK_P10 15 +#define R9A08G046_CLK_P13 16 +#define R9A08G046_CLK_P14 17 +#define R9A08G046_CLK_P15 18 +#define R9A08G046_CLK_P16 19 +#define R9A08G046_CLK_P17 20 +#define R9A08G046_CLK_P18 21 +#define R9A08G046_CLK_P19 22 +#define R9A08G046_CLK_P20 23 +#define R9A08G046_CLK_M0 24 +#define R9A08G046_CLK_M1 25 +#define R9A08G046_CLK_M2 26 +#define R9A08G046_CLK_M3 27 +#define R9A08G046_CLK_M4 28 +#define R9A08G046_CLK_M5 29 +#define R9A08G046_CLK_M6 30 +#define R9A08G046_CLK_AT 31 +#define R9A08G046_CLK_B 32 +#define R9A08G046_CLK_ETHTX01 33 +#define R9A08G046_CLK_ETHTX02 34 +#define R9A08G046_CLK_ETHRX01 35 +#define R9A08G046_CLK_ETHRX02 36 +#define R9A08G046_CLK_ETHRM0 37 +#define R9A08G046_CLK_ETHTX11 38 +#define R9A08G046_CLK_ETHTX12 39 +#define R9A08G046_CLK_ETHRX11 40 +#define R9A08G046_CLK_ETHRX12 41 +#define R9A08G046_CLK_ETHRM1 42 +#define R9A08G046_CLK_G 43 +#define R9A08G046_CLK_HP 44 +#define R9A08G046_CLK_SD0 45 +#define R9A08G046_CLK_SD1 46 +#define R9A08G046_CLK_SD2 47 +#define R9A08G046_CLK_SPI0 48 +#define R9A08G046_CLK_SPI1 49 +#define R9A08G046_CLK_S0 50 +#define R9A08G046_CLK_SWD 51 +#define R9A08G046_OSCCLK 52 +#define R9A08G046_OSCCLK2 53 +#define R9A08G046_MIPI_DSI_PLLCLK 54 +#define R9A08G046_USB_SCLK 55 + +/* R9A08G046 Module Clocks */ +#define R9A08G046_CA55_SCLK 0 +#define R9A08G046_CA55_PCLK 1 +#define R9A08G046_CA55_ATCLK 2 +#define R9A08G046_CA55_GICCLK 3 +#define R9A08G046_CA55_PERICLK 4 +#define R9A08G046_CA55_ACLK 5 +#define R9A08G046_CA55_TSCLK 6 +#define R9A08G046_CA55_CORECLK0 7 +#define R9A08G046_CA55_CORECLK1 8 +#define R9A08G046_CA55_CORECLK2 9 +#define R9A08G046_CA55_CORECLK3 10 +#define R9A08G046_SRAM_ACPU_ACLK0 11 +#define R9A08G046_SRAM_ACPU_ACLK1 12 +#define R9A08G046_SRAM_ACPU_ACLK2 13 +#define R9A08G046_GIC600_GICCLK 14 +#define R9A08G046_IA55_CLK 15 +#define R9A08G046_IA55_PCLK 16 +#define R9A08G046_MHU_PCLK 17 +#define R9A08G046_SYC_CNT_CLK 18 +#define R9A08G046_DMAC_ACLK 19 +#define R9A08G046_DMAC_PCLK 20 +#define R9A08G046_OSTM0_PCLK 21 +#define R9A08G046_OSTM1_PCLK 22 +#define R9A08G046_OSTM2_PCLK 23 +#define R9A08G046_MTU_X_MCK_MTU3 24 +#define R9A08G046_POE3_CLKM_POE 25 +#define R9A08G046_GPT_PCLK 26 +#define R9A08G046_POEG_A_CLKP 27 +#define R9A08G046_POEG_B_CLKP 28 +#define R9A08G046_POEG_C_CLKP 29 +#define R9A08G046_POEG_D_CLKP 30 +#define R9A08G046_WDT0_PCLK 31 +#define R9A08G046_WDT0_CLK 32 +#define R9A08G046_WDT1_PCLK 33 +#define R9A08G046_WDT1_CLK 34 +#define R9A08G046_WDT2_PCLK 35 +#define R9A08G046_WDT2_CLK 36 +#define R9A08G046_XSPI_HCLK 37 +#define R9A08G046_XSPI_ACLK 38 +#define R9A08G046_XSPI_CLK 39 +#define R9A08G046_XSPI_CLKX2 40 +#define R9A08G046_SDHI0_IMCLK 41 +#define R9A08G046_SDHI0_IMCLK2 42 +#define R9A08G046_SDHI0_CLK_HS 43 +#define R9A08G046_SDHI0_IACLKS 44 +#define R9A08G046_SDHI0_IACLKM 45 +#define R9A08G046_SDHI1_IMCLK 46 +#define R9A08G046_SDHI1_IMCLK2 47 +#define R9A08G046_SDHI1_CLK_HS 48 +#define R9A08G046_SDHI1_IACLKS 49 +#define R9A08G046_SDHI1_IACLKM 50 +#define R9A08G046_SDHI2_IMCLK 51 +#define R9A08G046_SDHI2_IMCLK2 52 +#define R9A08G046_SDHI2_CLK_HS 53 +#define R9A08G046_SDHI2_IACLKS 54 +#define R9A08G046_SDHI2_IACLKM 55 +#define R9A08G046_GE3D_CLK 56 +#define R9A08G046_GE3D_AXI_CLK 57 +#define R9A08G046_GE3D_ACE_CLK 58 +#define R9A08G046_ISU_ACLK 59 +#define R9A08G046_ISU_PCLK 60 +#define R9A08G046_H264_CLK_A 61 +#define R9A08G046_H264_CLK_P 62 +#define R9A08G046_CRU_SYSCLK 63 +#define R9A08G046_CRU_VCLK 64 +#define R9A08G046_CRU_PCLK 65 +#define R9A08G046_CRU_ACLK 66 +#define R9A08G046_MIPI_DSI_SYSCLK 67 +#define R9A08G046_MIPI_DSI_ACLK 68 +#define R9A08G046_MIPI_DSI_PCLK 69 +#define R9A08G046_MIPI_DSI_VCLK 70 +#define R9A08G046_MIPI_DSI_LPCLK 71 +#define R9A08G046_LVDS_PLLCLK 72 +#define R9A08G046_LVDS_CLK_DOT0 73 +#define R9A08G046_LCDC_CLK_A 74 +#define R9A08G046_LCDC_CLK_D 75 +#define R9A08G046_LCDC_CLK_P 76 +#define R9A08G046_SSI0_PCLK2 77 +#define R9A08G046_SSI0_PCLK_SFR 78 +#define R9A08G046_SSI1_PCLK2 79 +#define R9A08G046_SSI1_PCLK_SFR 80 +#define R9A08G046_SSI2_PCLK2 81 +#define R9A08G046_SSI2_PCLK_SFR 82 +#define R9A08G046_SSI3_PCLK2 83 +#define R9A08G046_SSI3_PCLK_SFR 84 +#define R9A08G046_USB_U2H0_HCLK 85 +#define R9A08G046_USB_U2H1_HCLK 86 +#define R9A08G046_USB_U2P0_EXR_CPUCLK 87 +#define R9A08G046_USB_U2P1_EXR_CPUCLK 88 +#define R9A08G046_USB_PCLK 89 +#define R9A08G046_ETH0_CLK_AXI 90 +#define R9A08G046_ETH0_CLK_CHI 91 +#define R9A08G046_ETH0_CLK_TX_I 92 +#define R9A08G046_ETH0_CLK_RX_I 93 +#define R9A08G046_ETH0_CLK_TX_180_I 94 +#define R9A08G046_ETH0_CLK_RX_180_I 95 +#define R9A08G046_ETH0_CLK_RMII_I 96 +#define R9A08G046_ETH0_CLK_PTP_REF_I 97 +#define R9A08G046_ETH0_CLK_TX_I_RMII 98 +#define R9A08G046_ETH0_CLK_RX_I_RMII 99 +#define R9A08G046_ETH1_CLK_AXI 100 +#define R9A08G046_ETH1_CLK_CHI 101 +#define R9A08G046_ETH1_CLK_TX_I 102 +#define R9A08G046_ETH1_CLK_RX_I 103 +#define R9A08G046_ETH1_CLK_TX_180_I 104 +#define R9A08G046_ETH1_CLK_RX_180_I 105 +#define R9A08G046_ETH1_CLK_RMII_I 106 +#define R9A08G046_ETH1_CLK_PTP_REF_I 107 +#define R9A08G046_ETH1_CLK_TX_I_RMII 108 +#define R9A08G046_ETH1_CLK_RX_I_RMII 109 +#define R9A08G046_I2C0_PCLK 110 +#define R9A08G046_I2C1_PCLK 111 +#define R9A08G046_I2C2_PCLK 112 +#define R9A08G046_I2C3_PCLK 113 +#define R9A08G046_SCIF0_CLK_PCK 114 +#define R9A08G046_SCIF1_CLK_PCK 115 +#define R9A08G046_SCIF2_CLK_PCK 116 +#define R9A08G046_SCIF3_CLK_PCK 117 +#define R9A08G046_SCIF4_CLK_PCK 118 +#define R9A08G046_SCIF5_CLK_PCK 119 +#define R9A08G046_RSCI0_PCLK 120 +#define R9A08G046_RSCI0_TCLK 121 +#define R9A08G046_RSCI1_PCLK 122 +#define R9A08G046_RSCI1_TCLK 123 +#define R9A08G046_RSCI2_PCLK 124 +#define R9A08G046_RSCI2_TCLK 125 +#define R9A08G046_RSCI3_PCLK 126 +#define R9A08G046_RSCI3_TCLK 127 +#define R9A08G046_RSPI0_PCLK 128 +#define R9A08G046_RSPI0_TCLK 129 +#define R9A08G046_RSPI1_PCLK 130 +#define R9A08G046_RSPI1_TCLK 131 +#define R9A08G046_RSPI2_PCLK 132 +#define R9A08G046_RSPI2_TCLK 133 +#define R9A08G046_CANFD_PCLK 134 +#define R9A08G046_CANFD_CLK_RAM 135 +#define R9A08G046_GPIO_HCLK 136 +#define R9A08G046_ADC0_ADCLK 137 +#define R9A08G046_ADC0_PCLK 138 +#define R9A08G046_ADC1_ADCLK 139 +#define R9A08G046_ADC1_PCLK 140 +#define R9A08G046_TSU_PCLK 141 +#define R9A08G046_PDM_PCLK 142 +#define R9A08G046_PDM_CCLK 143 +#define R9A08G046_PCI_ACLK 144 +#define R9A08G046_PCI_CLKL1PM 145 +#define R9A08G046_PCI_CLK_PMU 146 +#define R9A08G046_SPDIF_PCLK 147 +#define R9A08G046_I3C_TCLK 148 +#define R9A08G046_I3C_PCLK 149 +#define R9A08G046_VBAT_BCLK 150 +#define R9A08G046_BSC_X_BCK_BSC 151 + +/* R9A08G046 Resets */ +#define R9A08G046_CA55_RST0_0 0 +#define R9A08G046_CA55_RST0_1 1 +#define R9A08G046_CA55_RST0_2 2 +#define R9A08G046_CA55_RST0_3 3 +#define R9A08G046_CA55_RST4_0 4 +#define R9A08G046_CA55_RST4_1 5 +#define R9A08G046_CA55_RST4_2 6 +#define R9A08G046_CA55_RST4_3 7 +#define R9A08G046_CA55_RST8 8 +#define R9A08G046_CA55_RST9 9 +#define R9A08G046_CA55_RST10 10 +#define R9A08G046_CA55_RST11 11 +#define R9A08G046_CA55_RST12 12 +#define R9A08G046_CA55_RST13 13 +#define R9A08G046_CA55_RST14 14 +#define R9A08G046_CA55_RST15 15 +#define R9A08G046_CA55_RST16 16 +#define R9A08G046_SRAM_ACPU_ARESETN0 17 +#define R9A08G046_SRAM_ACPU_ARESETN1 18 +#define R9A08G046_SRAM_ACPU_ARESETN2 19 +#define R9A08G046_GIC600_GICRESET_N 20 +#define R9A08G046_GIC600_DBG_GICRESET_N 21 +#define R9A08G046_IA55_RESETN 22 +#define R9A08G046_MHU_RESETN 23 +#define R9A08G046_SYC_RESETN 24 +#define R9A08G046_DMAC_ARESETN 25 +#define R9A08G046_DMAC_RST_ASYNC 26 +#define R9A08G046_GTM0_PRESETZ 27 +#define R9A08G046_GTM1_PRESETZ 28 +#define R9A08G046_GTM2_PRESETZ 29 +#define R9A08G046_MTU_X_PRESET_MTU3 30 +#define R9A08G046_POE3_RST_M_REG 31 +#define R9A08G046_GPT_RST_C 32 +#define R9A08G046_POEG_A_RST 33 +#define R9A08G046_POEG_B_RST 34 +#define R9A08G046_POEG_C_RST 35 +#define R9A08G046_POEG_D_RST 36 +#define R9A08G046_WDT0_PRESETN 37 +#define R9A08G046_WDT1_PRESETN 38 +#define R9A08G046_WDT2_PRESETN 39 +#define R9A08G046_XSPI_HRESETN 40 +#define R9A08G046_XSPI_ARESETN 41 +#define R9A08G046_SDHI0_IXRST 42 +#define R9A08G046_SDHI1_IXRST 43 +#define R9A08G046_SDHI2_IXRST 44 +#define R9A08G046_SDHI0_IXRSTAXIM 45 +#define R9A08G046_SDHI0_IXRSTAXIS 46 +#define R9A08G046_SDHI1_IXRSTAXIM 47 +#define R9A08G046_SDHI1_IXRSTAXIS 48 +#define R9A08G046_SDHI2_IXRSTAXIM 49 +#define R9A08G046_SDHI2_IXRSTAXIS 50 +#define R9A08G046_GE3D_RESETN 51 +#define R9A08G046_GE3D_AXI_RESETN 52 +#define R9A08G046_GE3D_ACE_RESETN 53 +#define R9A08G046_ISU_ARESETN 54 +#define R9A08G046_ISU_PRESETN 55 +#define R9A08G046_H264_X_RESET_VCP 56 +#define R9A08G046_H264_CP_PRESET_P 57 +#define R9A08G046_CRU_CMN_RSTB 58 +#define R9A08G046_CRU_PRESETN 59 +#define R9A08G046_CRU_ARESETN 60 +#define R9A08G046_MIPI_DSI_CMN_RSTB 61 +#define R9A08G046_MIPI_DSI_ARESET_N 62 +#define R9A08G046_MIPI_DSI_PRESET_N 63 +#define R9A08G046_LCDC_RESET_N 64 +#define R9A08G046_SSI0_RST_M2_REG 65 +#define R9A08G046_SSI1_RST_M2_REG 66 +#define R9A08G046_SSI2_RST_M2_REG 67 +#define R9A08G046_SSI3_RST_M2_REG 68 +#define R9A08G046_USB_U2H0_HRESETN 69 +#define R9A08G046_USB_U2H1_HRESETN 70 +#define R9A08G046_USB_U2P0_EXL_SYSRST 71 +#define R9A08G046_USB_PRESETN 72 +#define R9A08G046_USB_U2P1_EXL_SYSRST 73 +#define R9A08G046_ETH0_ARESET_N 74 +#define R9A08G046_ETH1_ARESET_N 75 +#define R9A08G046_I2C0_MRST 76 +#define R9A08G046_I2C1_MRST 77 +#define R9A08G046_I2C2_MRST 78 +#define R9A08G046_I2C3_MRST 79 +#define R9A08G046_SCIF0_RST_SYSTEM_N 80 +#define R9A08G046_SCIF1_RST_SYSTEM_N 81 +#define R9A08G046_SCIF2_RST_SYSTEM_N 82 +#define R9A08G046_SCIF3_RST_SYSTEM_N 83 +#define R9A08G046_SCIF4_RST_SYSTEM_N 84 +#define R9A08G046_SCIF5_RST_SYSTEM_N 85 +#define R9A08G046_RSPI0_PRESETN 86 +#define R9A08G046_RSPI1_PRESETN 87 +#define R9A08G046_RSPI2_PRESETN 88 +#define R9A08G046_RSPI0_TRESETN 89 +#define R9A08G046_RSPI1_TRESETN 90 +#define R9A08G046_RSPI2_TRESETN 91 +#define R9A08G046_CANFD_RSTP_N 92 +#define R9A08G046_CANFD_RSTC_N 93 +#define R9A08G046_GPIO_RSTN 94 +#define R9A08G046_GPIO_PORT_RESETN 95 +#define R9A08G046_GPIO_SPARE_RESETN 96 +#define R9A08G046_ADC0_PRESETN 97 +#define R9A08G046_ADC0_ADRST_N 98 +#define R9A08G046_ADC1_PRESETN 99 +#define R9A08G046_ADC1_ADRST_N 100 +#define R9A08G046_TSU_PRESETN 101 +#define R9A08G046_PDM_PRESETN 102 +#define R9A08G046_PCI_ARESETN 103 +#define R9A08G046_SPDIF_RST 104 +#define R9A08G046_I3C_TRESETN 105 +#define R9A08G046_I3C_PRESETN 106 +#define R9A08G046_VBAT_BRESETN 107 +#define R9A08G046_RSCI0_PRESETN 108 +#define R9A08G046_RSCI1_PRESETN 109 +#define R9A08G046_RSCI2_PRESETN 110 +#define R9A08G046_RSCI3_PRESETN 111 +#define R9A08G046_RSCI0_TRESETN 112 +#define R9A08G046_RSCI1_TRESETN 113 +#define R9A08G046_RSCI2_TRESETN 114 +#define R9A08G046_RSCI3_TRESETN 115 +#define R9A08G046_LVDS_RESET_N 116 +#define R9A08G046_BSC_X_PRESET_BSC 117 + +#endif /* __DT_BINDINGS_CLOCK_RENESAS_R9A08G046_CPG_H__ */ diff --git a/include/dt-bindings/clock/rockchip,rv1103b-cru.h b/include/dt-bindings/clock/rockchip,rv1103b-cru.h new file mode 100644 index 000000000000..35afdee7e961 --- /dev/null +++ b/include/dt-bindings/clock/rockchip,rv1103b-cru.h @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ +/* + * Copyright (c) 2024 Rockchip Electronics Co. Ltd. + * Author: Elaine Zhang + */ + +#ifndef _DT_BINDINGS_CLK_ROCKCHIP_RV1103B_H +#define _DT_BINDINGS_CLK_ROCKCHIP_RV1103B_H + +#define PLL_GPLL 0 +#define ARMCLK 1 +#define PLL_DPLL 2 +#define XIN_OSC0_HALF 3 +#define CLK_GPLL_DIV24 4 +#define CLK_GPLL_DIV12 5 +#define CLK_GPLL_DIV6 6 +#define CLK_GPLL_DIV4 7 +#define CLK_GPLL_DIV3 8 +#define CLK_GPLL_DIV2P5 9 +#define CLK_GPLL_DIV2 10 +#define CLK_UART0_SRC 11 +#define CLK_UART1_SRC 12 +#define CLK_UART2_SRC 13 +#define CLK_UART0_FRAC 14 +#define CLK_UART1_FRAC 15 +#define CLK_UART2_FRAC 16 +#define CLK_SAI_SRC 17 +#define CLK_SAI_FRAC 18 +#define LSCLK_NPU_SRC 19 +#define CLK_NPU_SRC 20 +#define ACLK_VEPU_SRC 21 +#define CLK_VEPU_SRC 22 +#define ACLK_VI_SRC 23 +#define CLK_ISP_SRC 24 +#define DCLK_VICAP 25 +#define CCLK_EMMC 26 +#define CCLK_SDMMC0 27 +#define SCLK_SFC_2X 28 +#define LSCLK_PERI_SRC 29 +#define ACLK_PERI_SRC 30 +#define HCLK_HPMCU 31 +#define SCLK_UART0 32 +#define SCLK_UART1 33 +#define SCLK_UART2 34 +#define CLK_I2C_PMU 35 +#define CLK_I2C_PERI 36 +#define CLK_SPI0 37 +#define CLK_PWM0_SRC 38 +#define CLK_PWM1 39 +#define CLK_PWM2 40 +#define DCLK_DECOM_SRC 41 +#define CCLK_SDMMC1 42 +#define CLK_CORE_CRYPTO 43 +#define CLK_PKA_CRYPTO 44 +#define CLK_CORE_RGA 45 +#define MCLK_SAI_SRC 46 +#define CLK_FREQ_PWM0_SRC 47 +#define CLK_COUNTER_PWM0_SRC 48 +#define PCLK_TOP_ROOT 49 +#define CLK_REF_MIPI0 50 +#define CLK_MIPI0_OUT2IO 51 +#define CLK_REF_MIPI1 52 +#define CLK_MIPI1_OUT2IO 53 +#define MCLK_SAI_OUT2IO 54 +#define ACLK_NPU_ROOT 55 +#define HCLK_RKNN 56 +#define ACLK_RKNN 57 +#define LSCLK_VEPU_ROOT 58 +#define HCLK_VEPU 59 +#define ACLK_VEPU 60 +#define CLK_CORE_VEPU 61 +#define PCLK_IOC_VCCIO3 62 +#define PCLK_ACODEC 63 +#define PCLK_USBPHY 64 +#define LSCLK_VI_100M 65 +#define LSCLK_VI_ROOT 66 +#define HCLK_ISP 67 +#define ACLK_ISP 68 +#define CLK_CORE_ISP 69 +#define ACLK_VICAP 70 +#define HCLK_VICAP 71 +#define ISP0CLK_VICAP 72 +#define PCLK_CSI2HOST0 73 +#define PCLK_CSI2HOST1 74 +#define HCLK_EMMC 75 +#define HCLK_SFC 76 +#define HCLK_SFC_XIP 77 +#define HCLK_SDMMC0 78 +#define PCLK_CSIPHY 79 +#define PCLK_GPIO1 80 +#define DBCLK_GPIO1 81 +#define PCLK_IOC_VCCIO47 82 +#define LSCLK_DDR_ROOT 83 +#define CLK_TIMER_DDRMON 84 +#define LSCLK_PMU_ROOT 85 +#define PCLK_PMU 86 +#define XIN_RC_DIV 87 +#define CLK_32K 88 +#define PCLK_PMU_GPIO0 89 +#define DBCLK_PMU_GPIO0 90 +#define CLK_DDR_FAIL_SAFE 91 +#define PCLK_PMU_HP_TIMER 92 +#define CLK_PMU_32K_HP_TIMER 93 +#define PCLK_PWM0 94 +#define CLK_PWM0 95 +#define CLK_OSC_PWM0 96 +#define CLK_RC_PWM0 97 +#define CLK_FREQ_PWM0 98 +#define CLK_COUNTER_PWM0 99 +#define PCLK_I2C0 100 +#define CLK_I2C0 101 +#define PCLK_UART0 102 +#define PCLK_IOC_PMUIO0 103 +#define CLK_REFOUT 104 +#define CLK_PREROLL 105 +#define CLK_PREROLL_32K 106 +#define CLK_LPMCU_PMU 107 +#define PCLK_SPI2AHB 108 +#define HCLK_SPI2AHB 109 +#define SCLK_SPI2AHB 110 +#define PCLK_WDT_LPMCU 111 +#define TCLK_WDT_LPMCU 112 +#define HCLK_SFC_PMU1 113 +#define HCLK_SFC_XIP_PMU1 114 +#define SCLK_SFC_2X_PMU1 115 +#define CLK_LPMCU 116 +#define CLK_LPMCU_RTC 117 +#define PCLK_LPMCU_MAILBOX 118 +#define PCLK_IOC_PMUIO1 119 +#define PCLK_CRU_PMU1 120 +#define PCLK_PERI_ROOT 121 +#define PCLK_RTC_ROOT 122 +#define CLK_TIMER_ROOT 123 +#define PCLK_TIMER 124 +#define CLK_TIMER0 125 +#define CLK_TIMER1 126 +#define CLK_TIMER2 127 +#define CLK_TIMER3 128 +#define CLK_TIMER4 129 +#define CLK_TIMER5 130 +#define PCLK_STIMER 131 +#define CLK_STIMER0 132 +#define CLK_STIMER1 133 +#define PCLK_WDT_NS 134 +#define TCLK_WDT_NS 135 +#define PCLK_WDT_S 136 +#define TCLK_WDT_S 137 +#define PCLK_WDT_HPMCU 138 +#define TCLK_WDT_HPMCU 139 +#define PCLK_I2C1 140 +#define CLK_I2C1 141 +#define PCLK_I2C2 142 +#define CLK_I2C2 143 +#define PCLK_I2C3 144 +#define CLK_I2C3 145 +#define PCLK_I2C4 146 +#define CLK_I2C4 147 +#define PCLK_SPI0 148 +#define PCLK_PWM1 149 +#define CLK_OSC_PWM1 150 +#define PCLK_PWM2 151 +#define CLK_OSC_PWM2 152 +#define PCLK_UART2 153 +#define PCLK_UART1 154 +#define ACLK_RKDMA 155 +#define PCLK_TSADC 156 +#define CLK_TSADC 157 +#define CLK_TSADC_TSEN 158 +#define PCLK_SARADC 159 +#define CLK_SARADC 160 +#define PCLK_GPIO2 161 +#define DBCLK_GPIO2 162 +#define PCLK_IOC_VCCIO6 163 +#define ACLK_USBOTG 164 +#define CLK_REF_USBOTG 165 +#define HCLK_SDMMC1 166 +#define HCLK_SAI 167 +#define MCLK_SAI 168 +#define ACLK_CRYPTO 169 +#define HCLK_CRYPTO 170 +#define HCLK_RK_RNG_NS 171 +#define HCLK_RK_RNG_S 172 +#define PCLK_OTPC_NS 173 +#define CLK_OTPC_ROOT_NS 174 +#define CLK_SBPI_OTPC_NS 175 +#define CLK_USER_OTPC_NS 176 +#define PCLK_OTPC_S 177 +#define CLK_OTPC_ROOT_S 178 +#define CLK_SBPI_OTPC_S 179 +#define CLK_USER_OTPC_S 180 +#define CLK_OTPC_ARB 181 +#define PCLK_OTP_MASK 182 +#define HCLK_RGA 183 +#define ACLK_RGA 184 +#define ACLK_MAC 185 +#define PCLK_MAC 186 +#define CLK_MACPHY 187 +#define ACLK_SPINLOCK 188 +#define HCLK_CACHE 189 +#define PCLK_HPMCU_MAILBOX 190 +#define PCLK_HPMCU_INTMUX 191 +#define CLK_HPMCU 192 +#define CLK_HPMCU_RTC 193 +#define DCLK_DECOM 194 +#define ACLK_DECOM 195 +#define PCLK_DECOM 196 +#define ACLK_SYS_SRAM 197 +#define PCLK_DMA2DDR 198 +#define ACLK_DMA2DDR 199 +#define PCLK_DCF 200 +#define ACLK_DCF 201 +#define MCLK_ACODEC_TX 202 +#define SCLK_UART0_SRC 203 +#define SCLK_UART1_SRC 204 +#define SCLK_UART2_SRC 205 +#define XIN_RC_SRC 206 +#define CLK_UTMI_USBOTG 207 +#define CLK_REF_USBPHY 208 + +#endif // _DT_BINDINGS_CLK_ROCKCHIP_RV1103B_H diff --git a/include/dt-bindings/clock/samsung,exynosautov920.h b/include/dt-bindings/clock/samsung,exynosautov920.h index 06dec27a8c77..f2628c220b22 100644 --- a/include/dt-bindings/clock/samsung,exynosautov920.h +++ b/include/dt-bindings/clock/samsung,exynosautov920.h @@ -309,4 +309,10 @@ #define CLK_MOUT_MFD_NOC_USER 1 #define CLK_DOUT_MFD_NOCP 2 +/* CMU_G3D */ +#define FOUT_PLL_G3D 1 +#define CLK_MOUT_G3D_NOC 2 +#define CLK_MOUT_G3D_SWITCH_USER 3 +#define CLK_MOUT_G3D_NOCP_USER 4 + #endif /* _DT_BINDINGS_CLOCK_EXYNOSAUTOV920_H */ diff --git a/include/dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h b/include/dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h new file mode 100644 index 000000000000..c1c875e016f8 --- /dev/null +++ b/include/dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Tenstorrent Atlantis PRCM Clock and Reset Indices + * + * Copyright (c) 2026 Tenstorrent + */ + +#ifndef _DT_BINDINGS_ATLANTIS_PRCM_RCPU_H +#define _DT_BINDINGS_ATLANTIS_PRCM_RCPU_H + +/* + * RCPU Domain Clock IDs + */ +#define CLK_RCPU_PLL 0 +#define CLK_RCPU_ROOT 1 +#define CLK_RCPU_DIV2 2 +#define CLK_RCPU_DIV4 3 +#define CLK_RCPU_RTC 4 +#define CLK_SMNDMA0_ACLK 5 +#define CLK_SMNDMA1_ACLK 6 +#define CLK_WDT0_PCLK 7 +#define CLK_WDT1_PCLK 8 +#define CLK_TIMER_PCLK 9 +#define CLK_PVTC_PCLK 10 +#define CLK_PMU_PCLK 11 +#define CLK_MAILBOX_HCLK 12 +#define CLK_SEC_SPACC_HCLK 13 +#define CLK_SEC_OTP_HCLK 14 +#define CLK_TRNG_PCLK 15 +#define CLK_SEC_CRC_HCLK 16 +#define CLK_SMN_HCLK 17 +#define CLK_AHB0_HCLK 18 +#define CLK_SMN_PCLK 19 +#define CLK_SMN_CLK 20 +#define CLK_SCRATCHPAD_CLK 21 +#define CLK_RCPU_CORE_CLK 22 +#define CLK_RCPU_ROM_CLK 23 +#define CLK_OTP_LOAD_CLK 24 +#define CLK_NOC_PLL 25 +#define CLK_NOCC_CLK 26 +#define CLK_NOCC_DIV2 27 +#define CLK_NOCC_DIV4 28 +#define CLK_NOCC_RTC 29 +#define CLK_NOCC_CAN 30 +#define CLK_QSPI_SCLK 31 +#define CLK_QSPI_HCLK 32 +#define CLK_I2C0_PCLK 33 +#define CLK_I2C1_PCLK 34 +#define CLK_I2C2_PCLK 35 +#define CLK_I2C3_PCLK 36 +#define CLK_I2C4_PCLK 37 +#define CLK_UART0_PCLK 38 +#define CLK_UART1_PCLK 39 +#define CLK_UART2_PCLK 40 +#define CLK_UART3_PCLK 41 +#define CLK_UART4_PCLK 42 +#define CLK_SPI0_PCLK 43 +#define CLK_SPI1_PCLK 44 +#define CLK_SPI2_PCLK 45 +#define CLK_SPI3_PCLK 46 +#define CLK_GPIO_PCLK 47 +#define CLK_CAN0_HCLK 48 +#define CLK_CAN0_CLK 49 +#define CLK_CAN1_HCLK 50 +#define CLK_CAN1_CLK 51 +#define CLK_CAN0_TIMER_CLK 52 +#define CLK_CAN1_TIMER_CLK 53 + +/* RCPU domain reset */ +#define RST_SMNDMA0 0 +#define RST_SMNDMA1 1 +#define RST_WDT0 2 +#define RST_WDT1 3 +#define RST_TMR 4 +#define RST_PVTC 5 +#define RST_PMU 6 +#define RST_MAILBOX 7 +#define RST_SPACC 8 +#define RST_OTP 9 +#define RST_TRNG 10 +#define RST_CRC 11 +#define RST_QSPI 12 +#define RST_I2C0 13 +#define RST_I2C1 14 +#define RST_I2C2 15 +#define RST_I2C3 16 +#define RST_I2C4 17 +#define RST_UART0 18 +#define RST_UART1 19 +#define RST_UART2 20 +#define RST_UART3 21 +#define RST_UART4 22 +#define RST_SPI0 23 +#define RST_SPI1 24 +#define RST_SPI2 25 +#define RST_SPI3 26 +#define RST_GPIO 27 +#define RST_CAN0 28 +#define RST_CAN1 29 +#define RST_I2S0 30 +#define RST_I2S1 31 + +#endif /* _DT_BINDINGS_ATLANTIS_PRCM_RCPU_H */ diff --git a/include/dt-bindings/clock/vf610-clock.h b/include/dt-bindings/clock/vf610-clock.h index 373644e46747..5d94bd561a2e 100644 --- a/include/dt-bindings/clock/vf610-clock.h +++ b/include/dt-bindings/clock/vf610-clock.h @@ -197,6 +197,10 @@ #define VF610_CLK_TCON1 188 #define VF610_CLK_CAAM 189 #define VF610_CLK_CRC 190 -#define VF610_CLK_END 191 +#define VF610_CLK_ESW 191 +#define VF610_CLK_ESW_MAC_TAB0 192 +#define VF610_CLK_ESW_MAC_TAB1 193 +#define VF610_CLK_ESW_MAC_TAB2 194 +#define VF610_CLK_ESW_MAC_TAB3 195 #endif /* __DT_BINDINGS_CLOCK_VF610_H */ diff --git a/include/dt-bindings/interconnect/qcom,eliza-rpmh.h b/include/dt-bindings/interconnect/qcom,eliza-rpmh.h new file mode 100644 index 000000000000..95db2fe647de --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,eliza-rpmh.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_ELIZA_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_ELIZA_H + +#define MASTER_QSPI_0 0 +#define MASTER_QUP_1 1 +#define MASTER_UFS_MEM 2 +#define MASTER_USB3_0 3 +#define SLAVE_A1NOC_SNOC 4 + +#define MASTER_QUP_2 0 +#define MASTER_CRYPTO 1 +#define MASTER_IPA 2 +#define MASTER_SOCCP_AGGR_NOC 3 +#define MASTER_QDSS_ETR 4 +#define MASTER_QDSS_ETR_1 5 +#define MASTER_SDCC_1 6 +#define MASTER_SDCC_2 7 +#define SLAVE_A2NOC_SNOC 8 + +#define MASTER_QUP_CORE_1 0 +#define MASTER_QUP_CORE_2 1 +#define SLAVE_QUP_CORE_1 2 +#define SLAVE_QUP_CORE_2 3 + +#define MASTER_CNOC_CFG 0 +#define SLAVE_AHB2PHY_SOUTH 1 +#define SLAVE_AHB2PHY_NORTH 2 +#define SLAVE_CAMERA_CFG 3 +#define SLAVE_CLK_CTL 4 +#define SLAVE_CRYPTO_0_CFG 5 +#define SLAVE_DISPLAY_CFG 6 +#define SLAVE_GFX3D_CFG 7 +#define SLAVE_I3C_IBI0_CFG 8 +#define SLAVE_I3C_IBI1_CFG 9 +#define SLAVE_IMEM_CFG 10 +#define SLAVE_CNOC_MSS 11 +#define SLAVE_PCIE_0_CFG 12 +#define SLAVE_PRNG 13 +#define SLAVE_QDSS_CFG 14 +#define SLAVE_QSPI_0 15 +#define SLAVE_QUP_1 16 +#define SLAVE_QUP_2 17 +#define SLAVE_SDCC_2 18 +#define SLAVE_TCSR 19 +#define SLAVE_TLMM 20 +#define SLAVE_UFS_MEM_CFG 21 +#define SLAVE_USB3_0 22 +#define SLAVE_VENUS_CFG 23 +#define SLAVE_VSENSE_CTRL_CFG 24 +#define SLAVE_CNOC_MNOC_HF_CFG 25 +#define SLAVE_CNOC_MNOC_SF_CFG 26 +#define SLAVE_PCIE_ANOC_CFG 27 +#define SLAVE_QDSS_STM 28 +#define SLAVE_TCU 29 + +#define MASTER_GEM_NOC_CNOC 0 +#define MASTER_GEM_NOC_PCIE_SNOC 1 +#define SLAVE_AOSS 2 +#define SLAVE_IPA_CFG 3 +#define SLAVE_IPC_ROUTER_CFG 4 +#define SLAVE_SOCCP 5 +#define SLAVE_TME_CFG 6 +#define SLAVE_APPSS 7 +#define SLAVE_CNOC_CFG 8 +#define SLAVE_DDRSS_CFG 9 +#define SLAVE_BOOT_IMEM 10 +#define SLAVE_IMEM 11 +#define SLAVE_BOOT_IMEM_2 12 +#define SLAVE_SERVICE_CNOC 13 +#define SLAVE_PCIE_0 14 +#define SLAVE_PCIE_1 15 + +#define MASTER_GPU_TCU 0 +#define MASTER_SYS_TCU 1 +#define MASTER_APPSS_PROC 2 +#define MASTER_GFX3D 3 +#define MASTER_LPASS_GEM_NOC 4 +#define MASTER_MSS_PROC 5 +#define MASTER_MNOC_HF_MEM_NOC 6 +#define MASTER_MNOC_SF_MEM_NOC 7 +#define MASTER_COMPUTE_NOC 8 +#define MASTER_ANOC_PCIE_GEM_NOC 9 +#define MASTER_SNOC_SF_MEM_NOC 10 +#define MASTER_WLAN_Q6 11 +#define MASTER_GIC 12 +#define SLAVE_GEM_NOC_CNOC 13 +#define SLAVE_LLCC 14 +#define SLAVE_MEM_NOC_PCIE_SNOC 15 + +#define MASTER_LPIAON_NOC 0 +#define SLAVE_LPASS_GEM_NOC 1 + +#define MASTER_LPASS_LPINOC 0 +#define SLAVE_LPIAON_NOC_LPASS_AG_NOC 1 + +#define MASTER_LPASS_PROC 0 +#define SLAVE_LPICX_NOC_LPIAON_NOC 1 + +#define MASTER_LLCC 0 +#define SLAVE_EBI1 1 + +#define MASTER_CAMNOC_NRT_ICP_SF 0 +#define MASTER_CAMNOC_RT_CDM_SF 1 +#define MASTER_CAMNOC_SF 2 +#define MASTER_VIDEO_MVP 3 +#define MASTER_VIDEO_V_PROC 4 +#define MASTER_CNOC_MNOC_SF_CFG 5 +#define MASTER_CAMNOC_HF 6 +#define MASTER_MDP 7 +#define MASTER_CNOC_MNOC_HF_CFG 8 +#define SLAVE_MNOC_SF_MEM_NOC 9 +#define SLAVE_SERVICE_MNOC_SF 10 +#define SLAVE_MNOC_HF_MEM_NOC 11 +#define SLAVE_SERVICE_MNOC_HF 12 + +#define MASTER_CDSP_PROC 0 +#define SLAVE_CDSP_MEM_NOC 1 + +#define MASTER_PCIE_ANOC_CFG 0 +#define MASTER_PCIE_0 1 +#define MASTER_PCIE_1 2 +#define SLAVE_ANOC_PCIE_GEM_NOC 3 +#define SLAVE_SERVICE_PCIE_ANOC 4 + +#define MASTER_A1NOC_SNOC 0 +#define MASTER_A2NOC_SNOC 1 +#define MASTER_CNOC_SNOC 2 +#define MASTER_NSINOC_SNOC 3 +#define SLAVE_SNOC_GEM_NOC_SF 4 + +#endif diff --git a/include/dt-bindings/reset/cix,sky1-s5-system-control.h b/include/dt-bindings/reset/cix,sky1-s5-system-control.h new file mode 100644 index 000000000000..808bbcbe0c98 --- /dev/null +++ b/include/dt-bindings/reset/cix,sky1-s5-system-control.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* Author: Jerry Zhu */ +#ifndef DT_BINDING_RESET_CIX_SKY1_S5_SYSTEM_CONTROL_H +#define DT_BINDING_RESET_CIX_SKY1_S5_SYSTEM_CONTROL_H + +/* reset for csu_pm */ +#define SKY1_CSU_PM_RESET_N 0 +#define SKY1_SENSORFUSION_RESET_N 1 +#define SKY1_SENSORFUSION_NOC_RESET_N 2 + +/* reset group0 for s0 domain modules */ +#define SKY1_DDRC_RESET_N 3 +#define SKY1_GIC_RESET_N 4 +#define SKY1_CI700_RESET_N 5 +#define SKY1_SYS_NI700_RESET_N 6 +#define SKY1_MM_NI700_RESET_N 7 +#define SKY1_PCIE_NI700_RESET_N 8 +#define SKY1_GPU_RESET_N 9 +#define SKY1_NPUTOP_RESET_N 10 +#define SKY1_NPUCORE0_RESET_N 11 +#define SKY1_NPUCORE1_RESET_N 12 +#define SKY1_NPUCORE2_RESET_N 13 +#define SKY1_VPU_RESET_N 14 +#define SKY1_ISP_SRESET_N 15 +#define SKY1_ISP_ARESET_N 16 +#define SKY1_ISP_HRESET_N 17 +#define SKY1_ISP_GDCRESET_N 18 +#define SKY1_DPU_RESET0_N 19 +#define SKY1_DPU_RESET1_N 20 +#define SKY1_DPU_RESET2_N 21 +#define SKY1_DPU_RESET3_N 22 +#define SKY1_DPU_RESET4_N 23 +#define SKY1_DP_RESET0_N 24 +#define SKY1_DP_RESET1_N 25 +#define SKY1_DP_RESET2_N 26 +#define SKY1_DP_RESET3_N 27 +#define SKY1_DP_RESET4_N 28 +#define SKY1_DP_PHY_RST_N 29 + +/* reset group1 for s0 domain modules */ +#define SKY1_AUDIO_HIFI5_RESET_N 30 +#define SKY1_AUDIO_HIFI5_NOC_RESET_N 31 +#define SKY1_CSIDPHY_PRST0_N 32 +#define SKY1_CSIDPHY_CMNRST0_N 33 +#define SKY1_CSI0_RST_N 34 +#define SKY1_CSIDPHY_PRST1_N 35 +#define SKY1_CSIDPHY_CMNRST1_N 36 +#define SKY1_CSI1_RST_N 37 +#define SKY1_CSI2_RST_N 38 +#define SKY1_CSI3_RST_N 39 +#define SKY1_CSIBRDGE0_RST_N 40 +#define SKY1_CSIBRDGE1_RST_N 41 +#define SKY1_CSIBRDGE2_RST_N 42 +#define SKY1_CSIBRDGE3_RST_N 43 +#define SKY1_GMAC0_RST_N 44 +#define SKY1_GMAC1_RST_N 45 +#define SKY1_PCIE0_RESET_N 46 +#define SKY1_PCIE1_RESET_N 47 +#define SKY1_PCIE2_RESET_N 48 +#define SKY1_PCIE3_RESET_N 49 +#define SKY1_PCIE4_RESET_N 50 + +/* reset group1 for usb phys */ +#define SKY1_USB_DP_PHY0_PRST_N 51 +#define SKY1_USB_DP_PHY1_PRST_N 52 +#define SKY1_USB_DP_PHY2_PRST_N 53 +#define SKY1_USB_DP_PHY3_PRST_N 54 +#define SKY1_USB_DP_PHY0_RST_N 55 +#define SKY1_USB_DP_PHY1_RST_N 56 +#define SKY1_USB_DP_PHY2_RST_N 57 +#define SKY1_USB_DP_PHY3_RST_N 58 +#define SKY1_USBPHY_SS_PST_N 59 +#define SKY1_USBPHY_SS_RST_N 60 +#define SKY1_USBPHY_HS0_PRST_N 61 +#define SKY1_USBPHY_HS1_PRST_N 62 +#define SKY1_USBPHY_HS2_PRST_N 63 +#define SKY1_USBPHY_HS3_PRST_N 64 +#define SKY1_USBPHY_HS4_PRST_N 65 +#define SKY1_USBPHY_HS5_PRST_N 66 +#define SKY1_USBPHY_HS6_PRST_N 67 +#define SKY1_USBPHY_HS7_PRST_N 68 +#define SKY1_USBPHY_HS8_PRST_N 69 +#define SKY1_USBPHY_HS9_PRST_N 70 + +/* reset group1 for usb controllers */ +#define SKY1_USBC_SS0_PRST_N 71 +#define SKY1_USBC_SS1_PRST_N 72 +#define SKY1_USBC_SS2_PRST_N 73 +#define SKY1_USBC_SS3_PRST_N 74 +#define SKY1_USBC_SS4_PRST_N 75 +#define SKY1_USBC_SS5_PRST_N 76 +#define SKY1_USBC_SS0_RST_N 77 +#define SKY1_USBC_SS1_RST_N 78 +#define SKY1_USBC_SS2_RST_N 79 +#define SKY1_USBC_SS3_RST_N 80 +#define SKY1_USBC_SS4_RST_N 81 +#define SKY1_USBC_SS5_RST_N 82 +#define SKY1_USBC_HS0_PRST_N 83 +#define SKY1_USBC_HS1_PRST_N 84 +#define SKY1_USBC_HS2_PRST_N 85 +#define SKY1_USBC_HS3_PRST_N 86 +#define SKY1_USBC_HS0_RST_N 87 +#define SKY1_USBC_HS1_RST_N 88 +#define SKY1_USBC_HS2_RST_N 89 +#define SKY1_USBC_HS3_RST_N 90 + +/* reset group0 for rcsu */ +#define SKY1_AUDIO_RCSU_RESET_N 91 +#define SKY1_CI700_RCSU_RESET_N 92 +#define SKY1_CSI_RCSU0_RESET_N 93 +#define SKY1_CSI_RCSU1_RESET_N 94 +#define SKY1_CSU_PM_RCSU_RESET_N 95 +#define SKY1_DDR_BROADCAST_RCSU_RESET_N 96 +#define SKY1_DDR_CTRL_RCSU_0_RESET_N 97 +#define SKY1_DDR_CTRL_RCSU_1_RESET_N 98 +#define SKY1_DDR_CTRL_RCSU_2_RESET_N 99 +#define SKY1_DDR_CTRL_RCSU_3_RESET_N 100 +#define SKY1_DDR_TZC400_RCSU_0_RESET_N 101 +#define SKY1_DDR_TZC400_RCSU_1_RESET_N 102 +#define SKY1_DDR_TZC400_RCSU_2_RESET_N 103 +#define SKY1_DDR_TZC400_RCSU_3_RESET_N 104 +#define SKY1_DP0_RCSU_RESET_N 105 +#define SKY1_DP1_RCSU_RESET_N 106 +#define SKY1_DP2_RCSU_RESET_N 107 +#define SKY1_DP3_RCSU_RESET_N 108 +#define SKY1_DP4_RCSU_RESET_N 109 +#define SKY1_DPU0_RCSU_RESET_N 110 +#define SKY1_DPU1_RCSU_RESET_N 111 +#define SKY1_DPU2_RCSU_RESET_N 112 +#define SKY1_DPU3_RCSU_RESET_N 113 +#define SKY1_DPU4_RCSU_RESET_N 114 +#define SKY1_DSU_RCSU_RESET_N 115 +#define SKY1_FCH_RCSU_RESET_N 116 +#define SKY1_GICD_RCSU_RESET_N 117 +#define SKY1_GMAC_RCSU_RESET_N 118 +#define SKY1_GPU_RCSU_RESET_N 119 +#define SKY1_ISP_RCSU0_RESET_N 120 +#define SKY1_ISP_RCSU1_RESET_N 121 +#define SKY1_NI700_MMHUB_RCSU_RESET_N 122 + +/* reset group1 for rcsu */ +#define SKY1_NPU_RCSU_RESET_N 123 +#define SKY1_NI700_PCIE_RCSU_RESET_N 124 +#define SKY1_PCIE_X421_RCSU_RESET_N 125 +#define SKY1_PCIE_X8_RCSU_RESET_N 126 +#define SKY1_SF_RCSU_RESET_N 127 +#define SKY1_RCSU_SMMU_MMHUB_RESET_N 128 +#define SKY1_RCSU_SMMU_PCIEHUB_RESET_N 129 +#define SKY1_RCSU_SYSHUB_RESET_N 130 +#define SKY1_NI700_SMN_RCSU_RESET_N 131 +#define SKY1_NI700_SYSHUB_RCSU_RESET_N 132 +#define SKY1_RCSU_USB2_HOST0_RESET_N 133 +#define SKY1_RCSU_USB2_HOST1_RESET_N 134 +#define SKY1_RCSU_USB2_HOST2_RESET_N 135 +#define SKY1_RCSU_USB2_HOST3_RESET_N 136 +#define SKY1_RCSU_USB3_TYPEA_DRD_RESET_N 137 +#define SKY1_RCSU_USB3_TYPEC_DRD_RESET_N 138 +#define SKY1_RCSU_USB3_TYPEC_HOST0_RESET_N 139 +#define SKY1_RCSU_USB3_TYPEC_HOST1_RESET_N 140 +#define SKY1_RCSU_USB3_TYPEC_HOST2_RESET_N 141 +#define SKY1_VPU_RCSU_RESET_N 142 + +#endif diff --git a/include/dt-bindings/reset/cix,sky1-system-control.h b/include/dt-bindings/reset/cix,sky1-system-control.h new file mode 100644 index 000000000000..7a16fc4ef3b5 --- /dev/null +++ b/include/dt-bindings/reset/cix,sky1-system-control.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* Author: Jerry Zhu */ +#ifndef DT_BINDING_RESET_CIX_SKY1_SYSTEM_CONTROL_H +#define DT_BINDING_RESET_CIX_SKY1_SYSTEM_CONTROL_H + +/* func reset for sky1 fch */ +#define SW_I3C0_RST_FUNC_G_N 0 +#define SW_I3C0_RST_FUNC_I_N 1 +#define SW_I3C1_RST_FUNC_G_N 2 +#define SW_I3C1_RST_FUNC_I_N 3 +#define SW_UART0_RST_FUNC_N 4 +#define SW_UART1_RST_FUNC_N 5 +#define SW_UART2_RST_FUNC_N 6 +#define SW_UART3_RST_FUNC_N 7 +#define SW_TIMER_RST_FUNC_N 8 + +/* apb reset for sky1 fch */ +#define SW_I3C0_RST_APB_N 9 +#define SW_I3C1_RST_APB_N 10 +#define SW_DMA_RST_AXI_N 11 +#define SW_UART0_RST_APB_N 12 +#define SW_UART1_RST_APB_N 13 +#define SW_UART2_RST_APB_N 14 +#define SW_UART3_RST_APB_N 15 +#define SW_SPI0_RST_APB_N 16 +#define SW_SPI1_RST_APB_N 17 +#define SW_I2C0_RST_APB_N 18 +#define SW_I2C1_RST_APB_N 19 +#define SW_I2C2_RST_APB_N 20 +#define SW_I2C3_RST_APB_N 21 +#define SW_I2C4_RST_APB_N 22 +#define SW_I2C5_RST_APB_N 23 +#define SW_I2C6_RST_APB_N 24 +#define SW_I2C7_RST_APB_N 25 +#define SW_GPIO_RST_APB_N 26 + +/* fch rst for xspi */ +#define SW_XSPI_REG_RST_N 27 +#define SW_XSPI_SYS_RST_N 28 + +#endif diff --git a/include/dt-bindings/reset/econet,en751221-scu.h b/include/dt-bindings/reset/econet,en751221-scu.h new file mode 100644 index 000000000000..bad499d4d50a --- /dev/null +++ b/include/dt-bindings/reset/econet,en751221-scu.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + +#ifndef __DT_BINDINGS_RESET_CONTROLLER_ECONET_EN751221_H_ +#define __DT_BINDINGS_RESET_CONTROLLER_ECONET_EN751221_H_ + +#define EN751221_XPON_PHY_RST 0 +#define EN751221_PCM1_ZSI_ISI_RST 1 +#define EN751221_FE_QDMA1_RST 2 +#define EN751221_FE_QDMA2_RST 3 +#define EN751221_FE_UNZIP_RST 4 +#define EN751221_PCM2_RST 5 +#define EN751221_PTM_MAC_RST 6 +#define EN751221_CRYPTO_RST 7 +#define EN751221_SAR_RST 8 +#define EN751221_TIMER_RST 9 +#define EN751221_INTC_RST 10 +#define EN751221_BONDING_RST 11 +#define EN751221_PCM1_RST 12 +#define EN751221_UART_RST 13 +#define EN751221_GPIO_RST 14 +#define EN751221_GDMA_RST 15 +#define EN751221_I2C_MASTER_RST 16 +#define EN751221_PCM2_ZSI_ISI_RST 17 +#define EN751221_SFC_RST 18 +#define EN751221_UART2_RST 19 +#define EN751221_GDMP_RST 20 +#define EN751221_FE_RST 21 +#define EN751221_USB_HOST_P0_RST 22 +#define EN751221_GSW_RST 23 +#define EN751221_SFC2_PCM_RST 24 +#define EN751221_PCIE0_RST 25 +#define EN751221_PCIE1_RST 26 +#define EN751221_CPU_TIMER_RST 27 +#define EN751221_PCIE_HB_RST 28 +#define EN751221_SIMIF_RST 29 +#define EN751221_XPON_MAC_RST 30 +#define EN751221_GFAST_RST 31 +#define EN751221_CPU_TIMER2_RST 32 +#define EN751221_UART3_RST 33 +#define EN751221_UART4_RST 34 +#define EN751221_UART5_RST 35 +#define EN751221_I2C2_RST 36 +#define EN751221_XSI_MAC_RST 37 +#define EN751221_XSI_PHY_RST 38 +#define EN751221_DMT_RST 39 +#define EN751221_USB_PHY_P0_RST 40 +#define EN751221_USB_PHY_P1_RST 41 + +#endif /* __DT_BINDINGS_RESET_CONTROLLER_ECONET_EN751221_H_ */ diff --git a/include/dt-bindings/reset/qcom,ipq5210-gcc.h b/include/dt-bindings/reset/qcom,ipq5210-gcc.h new file mode 100644 index 000000000000..09890a09087c --- /dev/null +++ b/include/dt-bindings/reset/qcom,ipq5210-gcc.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_RESET_IPQ_GCC_IPQ5210_H +#define _DT_BINDINGS_RESET_IPQ_GCC_IPQ5210_H + +#define GCC_ADSS_BCR 0 +#define GCC_ADSS_PWM_ARES 1 +#define GCC_APC0_VOLTAGE_DROOP_DETECTOR_BCR 2 +#define GCC_APC0_VOLTAGE_DROOP_DETECTOR_GPLL0_ARES 3 +#define GCC_APSS_AHB_ARES 4 +#define GCC_APSS_ATB_ARES 5 +#define GCC_APSS_AXI_ARES 6 +#define GCC_APSS_TS_ARES 7 +#define GCC_BOOT_ROM_AHB_ARES 8 +#define GCC_BOOT_ROM_BCR 9 +#define GCC_GEPHY_BCR 10 +#define GCC_GEPHY_SYS_ARES 11 +#define GCC_GP1_ARES 12 +#define GCC_GP2_ARES 13 +#define GCC_GP3_ARES 14 +#define GCC_MDIO_AHB_ARES 15 +#define GCC_MDIO_BCR 16 +#define GCC_MDIO_GEPHY_AHB_ARES 17 +#define GCC_NSS_BCR 18 +#define GCC_NSS_TS_ARES 19 +#define GCC_NSSCC_ARES 20 +#define GCC_NSSCFG_ARES 21 +#define GCC_NSSNOC_ATB_ARES 22 +#define GCC_NSSNOC_MEMNOC_1_ARES 23 +#define GCC_NSSNOC_MEMNOC_ARES 24 +#define GCC_NSSNOC_NSSCC_ARES 25 +#define GCC_NSSNOC_PCNOC_1_ARES 26 +#define GCC_NSSNOC_QOSGEN_REF_ARES 27 +#define GCC_NSSNOC_SNOC_1_ARES 28 +#define GCC_NSSNOC_SNOC_ARES 29 +#define GCC_NSSNOC_TIMEOUT_REF_ARES 30 +#define GCC_NSSNOC_XO_DCD_ARES 31 +#define GCC_PCIE0_AHB_ARES 32 +#define GCC_PCIE0_AUX_ARES 33 +#define GCC_PCIE0_AXI_M_ARES 34 +#define GCC_PCIE0_AXI_S_BRIDGE_ARES 35 +#define GCC_PCIE0_AXI_S_ARES 36 +#define GCC_PCIE0_BCR 37 +#define GCC_PCIE0_LINK_DOWN_BCR 38 +#define GCC_PCIE0_PHY_BCR 39 +#define GCC_PCIE0_PIPE_ARES 40 +#define GCC_PCIE0PHY_PHY_BCR 41 +#define GCC_PCIE1_AHB_ARES 42 +#define GCC_PCIE1_AUX_ARES 43 +#define GCC_PCIE1_AXI_M_ARES 44 +#define GCC_PCIE1_AXI_S_BRIDGE_ARES 45 +#define GCC_PCIE1_AXI_S_ARES 46 +#define GCC_PCIE1_BCR 47 +#define GCC_PCIE1_LINK_DOWN_BCR 48 +#define GCC_PCIE1_PHY_BCR 49 +#define GCC_PCIE1_PIPE_ARES 50 +#define GCC_PCIE1PHY_PHY_BCR 51 +#define GCC_QRNG_AHB_ARES 52 +#define GCC_QRNG_BCR 53 +#define GCC_QUPV3_2X_CORE_ARES 54 +#define GCC_QUPV3_AHB_MST_ARES 55 +#define GCC_QUPV3_AHB_SLV_ARES 56 +#define GCC_QUPV3_BCR 57 +#define GCC_QUPV3_CORE_ARES 58 +#define GCC_QUPV3_WRAP_SE0_ARES 59 +#define GCC_QUPV3_WRAP_SE0_BCR 60 +#define GCC_QUPV3_WRAP_SE1_ARES 61 +#define GCC_QUPV3_WRAP_SE1_BCR 62 +#define GCC_QUPV3_WRAP_SE2_ARES 63 +#define GCC_QUPV3_WRAP_SE2_BCR 64 +#define GCC_QUPV3_WRAP_SE3_ARES 65 +#define GCC_QUPV3_WRAP_SE3_BCR 66 +#define GCC_QUPV3_WRAP_SE4_ARES 67 +#define GCC_QUPV3_WRAP_SE4_BCR 68 +#define GCC_QUPV3_WRAP_SE5_ARES 69 +#define GCC_QUPV3_WRAP_SE5_BCR 70 +#define GCC_QUSB2_0_PHY_BCR 71 +#define GCC_SDCC1_AHB_ARES 72 +#define GCC_SDCC1_APPS_ARES 73 +#define GCC_SDCC1_ICE_CORE_ARES 74 +#define GCC_SDCC_BCR 75 +#define GCC_TLMM_AHB_ARES 76 +#define GCC_TLMM_ARES 77 +#define GCC_TLMM_BCR 78 +#define GCC_UNIPHY0_AHB_ARES 79 +#define GCC_UNIPHY0_BCR 80 +#define GCC_UNIPHY0_SYS_ARES 81 +#define GCC_UNIPHY1_AHB_ARES 82 +#define GCC_UNIPHY1_BCR 83 +#define GCC_UNIPHY1_SYS_ARES 84 +#define GCC_UNIPHY2_AHB_ARES 85 +#define GCC_UNIPHY2_BCR 86 +#define GCC_UNIPHY2_SYS_ARES 87 +#define GCC_USB0_AUX_ARES 88 +#define GCC_USB0_MASTER_ARES 89 +#define GCC_USB0_MOCK_UTMI_ARES 90 +#define GCC_USB0_PHY_BCR 91 +#define GCC_USB0_PHY_CFG_AHB_ARES 92 +#define GCC_USB0_PIPE_ARES 93 +#define GCC_USB0_SLEEP_ARES 94 +#define GCC_USB3PHY_0_PHY_BCR 95 +#define GCC_USB_BCR 96 +#define GCC_PCIE0_PIPE_RESET 97 +#define GCC_PCIE0_CORE_STICKY_RESET 98 +#define GCC_PCIE0_AXI_S_STICKY_RESET 99 +#define GCC_PCIE0_AXI_S_RESET 100 +#define GCC_PCIE0_AXI_M_STICKY_RESET 101 +#define GCC_PCIE0_AXI_M_RESET 102 +#define GCC_PCIE0_AUX_RESET 103 +#define GCC_PCIE0_AHB_RESET 104 +#define GCC_PCIE1_PIPE_RESET 105 +#define GCC_PCIE1_CORE_STICKY_RESET 106 +#define GCC_PCIE1_AXI_S_STICKY_RESET 107 +#define GCC_PCIE1_AXI_S_RESET 108 +#define GCC_PCIE1_AXI_M_STICKY_RESET 109 +#define GCC_PCIE1_AXI_M_RESET 110 +#define GCC_PCIE1_AUX_RESET 111 +#define GCC_PCIE1_AHB_RESET 112 +#define GCC_UNIPHY0_XPCS_ARES 113 +#define GCC_UNIPHY1_XPCS_ARES 114 +#define GCC_UNIPHY2_XPCS_ARES 115 +#define GCC_QDSS_BCR 116 + +#endif diff --git a/include/dt-bindings/usb/pd.h b/include/dt-bindings/usb/pd.h index e6526b138174..1e64a1f563f9 100644 --- a/include/dt-bindings/usb/pd.h +++ b/include/dt-bindings/usb/pd.h @@ -60,6 +60,7 @@ PDO_VAR_MAX_VOLT(max_mv) | PDO_VAR_MAX_CURR(max_ma)) #define APDO_TYPE_PPS 0 +#define APDO_TYPE_SPR_AVS 2 #define PDO_APDO_TYPE_SHIFT 28 /* Only valid value currently is 0x0 - PPS */ #define PDO_APDO_TYPE_MASK 0x3 @@ -85,6 +86,23 @@ PDO_PPS_APDO_MIN_VOLT(min_mv) | PDO_PPS_APDO_MAX_VOLT(max_mv) | \ PDO_PPS_APDO_MAX_CURR(max_ma)) +#define PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR_SHIFT 10 /* 10mA units */ +#define PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR_SHIFT 0 /* 10mA units */ +#define PDO_SPR_AVS_APDO_MAX_CURR_MASK 0x3ff + +#define PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR(max_cur_9v_to_15v_ma) \ + ((((max_cur_9v_to_15v_ma) / 10) & PDO_SPR_AVS_APDO_MAX_CURR_MASK) << \ + PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR_SHIFT) + +#define PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR(max_cur_15v_to_20v_ma) \ + ((((max_cur_15v_to_20v_ma) / 10) & PDO_SPR_AVS_APDO_MAX_CURR_MASK) << \ + PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR_SHIFT) + +#define PDO_SPR_AVS_SNK_APDO(max_cur_9v_to_15v_ma, max_cur_15v_to_20v_ma) \ + (PDO_TYPE(PDO_TYPE_APDO) | PDO_APDO_TYPE(APDO_TYPE_SPR_AVS) | \ + PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR(max_cur_9v_to_15v_ma) | \ + PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR(max_cur_15v_to_20v_ma)) + /* * Based on "Table 6-14 Fixed Supply PDO - Sink" of "USB Power Delivery Specification Revision 3.0, * Version 1.2" @@ -465,4 +483,22 @@ | ((vbm) & 0x3) << 15 | (curr) << 14 | ((vbi) & 0x3f) << 7 \ | ((gi) & 0x3f) << 1 | (ct)) +/* + * Sink Load Characteristics + * ------------------------- + * <15> :: Can tolerate vbus voltage droop + * <11:14> :: Duty cycle in 5% increments when bits 4:0 are non-zero + * <10:5> :: Overload period in 20ms when bits 4:0 are non-zero + * <4:0> :: Percent overload in 10% increments. Values higher than 25 are + * clipped to 250% + */ +#define SINK_LOAD_CHAR(vdroop, duty_cycle, period, percent_ol) \ + (((vdroop) & 0x1) << 15 | ((duty_cycle) & 0xf) << 11 | \ + ((period) & 0x3f) << 5 | ((percent_ol) & 0x1f)) + +/* Compliance */ +#define COMPLIANCE_LPS (1 << 0) +#define COMPLIANCE_PS1 (1 << 1) +#define COMPLIANCE_PS2 (1 << 2) + #endif /* __DT_POWER_DELIVERY_H */ diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h index f9600f87186a..6a4e8b9d570f 100644 --- a/include/hyperv/hvgdk_mini.h +++ b/include/hyperv/hvgdk_mini.h @@ -435,6 +435,7 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_ASSIST_PAGE */ /* HV_CALL_CODE */ #define HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE 0x0002 #define HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST 0x0003 +#define HVCALL_GET_LOGICAL_PROCESSOR_RUN_TIME 0x0004 #define HVCALL_NOTIFY_LONG_SPIN_WAIT 0x0008 #define HVCALL_SEND_IPI 0x000b #define HVCALL_ENABLE_VP_VTL 0x000f diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h index 091c03e26046..b4cb2fa26e9b 100644 --- a/include/hyperv/hvhdk_mini.h +++ b/include/hyperv/hvhdk_mini.h @@ -362,6 +362,7 @@ union hv_partition_event_input { enum hv_partition_event { HV_PARTITION_EVENT_ROOT_CRASHDUMP = 2, + HV_PARTITION_ALL_LOGICAL_PROCESSORS_STARTED = 4, }; struct hv_input_notify_partition_event { @@ -369,6 +370,17 @@ struct hv_input_notify_partition_event { union hv_partition_event_input input; } __packed; +struct hv_input_get_logical_processor_run_time { + u32 lp_index; +} __packed; + +struct hv_output_get_logical_processor_run_time { + u64 global_time; + u64 local_run_time; + u64 rsvdz0; + u64 hypervisor_time; +} __packed; + struct hv_lp_startup_status { u64 hv_status; u64 substatus1; diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index 7310841f4512..bf8cc9589bd0 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -10,6 +10,8 @@ #include #include +#include + enum kvm_arch_timers { TIMER_PTIMER, TIMER_VTIMER, @@ -47,7 +49,7 @@ struct arch_timer_vm_data { u64 poffset; /* The PPI for each timer, global to the VM */ - u8 ppi[NR_KVM_TIMERS]; + u32 ppi[NR_KVM_TIMERS]; }; struct arch_timer_context { @@ -130,6 +132,10 @@ void kvm_timer_init_vhe(void); #define timer_vm_data(ctx) (&(timer_context_to_vcpu(ctx)->kvm->arch.timer_data)) #define timer_irq(ctx) (timer_vm_data(ctx)->ppi[arch_timer_ctx_index(ctx)]) +#define get_vgic_ppi(k, i) (((k)->arch.vgic.vgic_model != KVM_DEV_TYPE_ARM_VGIC_V5) ? \ + (i) : (FIELD_PREP(GICV5_HWIRQ_ID, i) | \ + FIELD_PREP(GICV5_HWIRQ_TYPE, GICV5_HWIRQ_TYPE_PPI))) + u64 kvm_arm_timer_read_sysreg(struct kvm_vcpu *vcpu, enum kvm_arch_timers tmr, enum kvm_arch_timer_regs treg); diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 96754b51b411..0a36a3d5c894 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -12,6 +12,9 @@ #define KVM_ARMV8_PMU_MAX_COUNTERS 32 +/* PPI #23 - architecturally specified for GICv5 */ +#define KVM_ARMV8_PMU_GICV5_IRQ 0x20000017 + #if IS_ENABLED(CONFIG_HW_PERF_EVENTS) && IS_ENABLED(CONFIG_KVM) struct kvm_pmc { u8 idx; /* index into the pmu->pmc array */ @@ -38,7 +41,7 @@ struct arm_pmu_entry { }; bool kvm_supports_guest_pmuv3(void); -#define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num >= VGIC_NR_SGIS) +#define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num != 0) u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx); void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val); void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu, u64 select_idx, u64 val); diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index f2eafc65bbf4..1388dc6028a9 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -19,7 +19,9 @@ #include #include +#include +#define VGIC_V5_MAX_CPUS 512 #define VGIC_V3_MAX_CPUS 512 #define VGIC_V2_MAX_CPUS 8 #define VGIC_NR_IRQS_LEGACY 256 @@ -31,9 +33,96 @@ #define VGIC_MIN_LPI 8192 #define KVM_IRQCHIP_NUM_PINS (1020 - 32) -#define irq_is_ppi(irq) ((irq) >= VGIC_NR_SGIS && (irq) < VGIC_NR_PRIVATE_IRQS) -#define irq_is_spi(irq) ((irq) >= VGIC_NR_PRIVATE_IRQS && \ - (irq) <= VGIC_MAX_SPI) +/* + * GICv5 supports 128 PPIs, but only the first 64 are architected. We only + * support the timers and PMU in KVM, both of which are architected. Rather than + * handling twice the state, we instead opt to only support the architected set + * in KVM for now. At a future stage, this can be bumped up to 128, if required. + */ +#define VGIC_V5_NR_PRIVATE_IRQS 64 + +#define is_v5_type(t, i) (FIELD_GET(GICV5_HWIRQ_TYPE, (i)) == (t)) + +#define __irq_is_sgi(t, i) \ + ({ \ + bool __ret; \ + \ + switch (t) { \ + case KVM_DEV_TYPE_ARM_VGIC_V5: \ + __ret = false; \ + break; \ + default: \ + __ret = (i) < VGIC_NR_SGIS; \ + } \ + \ + __ret; \ + }) + +#define __irq_is_ppi(t, i) \ + ({ \ + bool __ret; \ + \ + switch (t) { \ + case KVM_DEV_TYPE_ARM_VGIC_V5: \ + __ret = is_v5_type(GICV5_HWIRQ_TYPE_PPI, (i)); \ + break; \ + default: \ + __ret = (i) >= VGIC_NR_SGIS; \ + __ret &= (i) < VGIC_NR_PRIVATE_IRQS; \ + } \ + \ + __ret; \ + }) + +#define __irq_is_spi(t, i) \ + ({ \ + bool __ret; \ + \ + switch (t) { \ + case KVM_DEV_TYPE_ARM_VGIC_V5: \ + __ret = is_v5_type(GICV5_HWIRQ_TYPE_SPI, (i)); \ + break; \ + default: \ + __ret = (i) <= VGIC_MAX_SPI; \ + __ret &= (i) >= VGIC_NR_PRIVATE_IRQS; \ + } \ + \ + __ret; \ + }) + +#define __irq_is_lpi(t, i) \ + ({ \ + bool __ret; \ + \ + switch (t) { \ + case KVM_DEV_TYPE_ARM_VGIC_V5: \ + __ret = is_v5_type(GICV5_HWIRQ_TYPE_LPI, (i)); \ + break; \ + default: \ + __ret = (i) >= 8192; \ + } \ + \ + __ret; \ + }) + +#define irq_is_sgi(k, i) __irq_is_sgi((k)->arch.vgic.vgic_model, i) +#define irq_is_ppi(k, i) __irq_is_ppi((k)->arch.vgic.vgic_model, i) +#define irq_is_spi(k, i) __irq_is_spi((k)->arch.vgic.vgic_model, i) +#define irq_is_lpi(k, i) __irq_is_lpi((k)->arch.vgic.vgic_model, i) + +#define irq_is_private(k, i) (irq_is_ppi(k, i) || irq_is_sgi(k, i)) + +#define vgic_v5_get_hwirq_id(x) FIELD_GET(GICV5_HWIRQ_ID, (x)) +#define vgic_v5_set_hwirq_id(x) FIELD_PREP(GICV5_HWIRQ_ID, (x)) + +#define __vgic_v5_set_type(t) (FIELD_PREP(GICV5_HWIRQ_TYPE, GICV5_HWIRQ_TYPE_##t)) +#define vgic_v5_make_ppi(x) (__vgic_v5_set_type(PPI) | vgic_v5_set_hwirq_id(x)) +#define vgic_v5_make_spi(x) (__vgic_v5_set_type(SPI) | vgic_v5_set_hwirq_id(x)) +#define vgic_v5_make_lpi(x) (__vgic_v5_set_type(LPI) | vgic_v5_set_hwirq_id(x)) + +#define __vgic_is_v(k, v) ((k)->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V##v) +#define vgic_is_v3(k) (__vgic_is_v(k, 3)) +#define vgic_is_v5(k) (__vgic_is_v(k, 5)) enum vgic_type { VGIC_V2, /* Good ol' GICv2 */ @@ -101,6 +190,8 @@ enum vgic_irq_config { VGIC_CONFIG_LEVEL }; +struct vgic_irq; + /* * Per-irq ops overriding some common behavious. * @@ -119,6 +210,19 @@ struct irq_ops { * peaking into the physical GIC. */ bool (*get_input_level)(int vintid); + + /* + * Function pointer to override the queuing of an IRQ. + */ + bool (*queue_irq_unlock)(struct kvm *kvm, struct vgic_irq *irq, + unsigned long flags) __releases(&irq->irq_lock); + + /* + * Callback function pointer to either enable or disable direct + * injection for a mapped interrupt. + */ + void (*set_direct_injection)(struct kvm_vcpu *vcpu, + struct vgic_irq *irq, bool direct); }; struct vgic_irq { @@ -238,6 +342,26 @@ struct vgic_redist_region { struct list_head list; }; +struct vgic_v5_vm { + /* + * We only expose a subset of PPIs to the guest. This subset is a + * combination of the PPIs that are actually implemented and what we + * actually choose to expose. + */ + DECLARE_BITMAP(vgic_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS); + + /* A mask of the PPIs that are exposed for userspace to drive. */ + DECLARE_BITMAP(userspace_ppis, VGIC_V5_NR_PRIVATE_IRQS); + + /* + * The HMR itself is handled by the hardware, but we still need to have + * a mask that we can use when merging in pending state (only the state + * of Edge PPIs is merged back in from the guest an the HMR provides a + * convenient way to do that). + */ + DECLARE_BITMAP(vgic_ppi_hmr, VGIC_V5_NR_PRIVATE_IRQS); +}; + struct vgic_dist { bool in_kernel; bool ready; @@ -310,6 +434,11 @@ struct vgic_dist { * else. */ struct its_vm its_vm; + + /* + * GICv5 per-VM data. + */ + struct vgic_v5_vm gicv5_vm; }; struct vgic_v2_cpu_if { @@ -340,11 +469,40 @@ struct vgic_v3_cpu_if { unsigned int used_lrs; }; +struct vgic_v5_cpu_if { + u64 vgic_apr; + u64 vgic_vmcr; + + /* PPI register state */ + DECLARE_BITMAP(vgic_ppi_dvir, VGIC_V5_NR_PRIVATE_IRQS); + DECLARE_BITMAP(vgic_ppi_activer, VGIC_V5_NR_PRIVATE_IRQS); + DECLARE_BITMAP(vgic_ppi_enabler, VGIC_V5_NR_PRIVATE_IRQS); + /* We have one byte (of which 5 bits are used) per PPI for priority */ + u64 vgic_ppi_priorityr[VGIC_V5_NR_PRIVATE_IRQS / 8]; + + /* + * The ICSR is re-used across host and guest, and hence it needs to be + * saved/restored. Only one copy is required as the host should block + * preemption between executing GIC CDRCFG and acccessing the + * ICC_ICSR_EL1. A guest, of course, can never guarantee this, and hence + * it is the hyp's responsibility to keep the state constistent. + */ + u64 vgic_icsr; + + struct gicv5_vpe gicv5_vpe; +}; + +/* What PPI capabilities does a GICv5 host have */ +struct vgic_v5_ppi_caps { + DECLARE_BITMAP(impl_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS); +}; + struct vgic_cpu { /* CPU vif control registers for world switch */ union { struct vgic_v2_cpu_if vgic_v2; struct vgic_v3_cpu_if vgic_v3; + struct vgic_v5_cpu_if vgic_v5; }; struct vgic_irq *private_irqs; @@ -392,13 +550,17 @@ int kvm_vgic_create(struct kvm *kvm, u32 type); void kvm_vgic_destroy(struct kvm *kvm); void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu); int kvm_vgic_map_resources(struct kvm *kvm); +void kvm_vgic_finalize_idregs(struct kvm *kvm); int kvm_vgic_hyp_init(void); void kvm_vgic_init_cpu_hardware(void); int kvm_vgic_inject_irq(struct kvm *kvm, struct kvm_vcpu *vcpu, unsigned int intid, bool level, void *owner); +void kvm_vgic_set_irq_ops(struct kvm_vcpu *vcpu, u32 vintid, + struct irq_ops *ops); +void kvm_vgic_clear_irq_ops(struct kvm_vcpu *vcpu, u32 vintid); int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq, - u32 vintid, struct irq_ops *ops); + u32 vintid); int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid); int kvm_vgic_get_map(struct kvm_vcpu *vcpu, unsigned int vintid); bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int vintid); @@ -414,8 +576,20 @@ u64 vgic_v3_get_misr(struct kvm_vcpu *vcpu); #define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel)) #define vgic_initialized(k) ((k)->arch.vgic.initialized) -#define vgic_valid_spi(k, i) (((i) >= VGIC_NR_PRIVATE_IRQS) && \ - ((i) < (k)->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS)) +#define vgic_valid_spi(k, i) \ + ({ \ + bool __ret = irq_is_spi(k, i); \ + \ + switch ((k)->arch.vgic.vgic_model) { \ + case KVM_DEV_TYPE_ARM_VGIC_V5: \ + __ret &= FIELD_GET(GICV5_HWIRQ_ID, i) < (k)->arch.vgic.nr_spis; \ + break; \ + default: \ + __ret &= (i) < ((k)->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS); \ + } \ + \ + __ret; \ + }) bool kvm_vcpu_has_pending_irqs(struct kvm_vcpu *vcpu); void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu); @@ -455,6 +629,11 @@ int vgic_v4_load(struct kvm_vcpu *vcpu); void vgic_v4_commit(struct kvm_vcpu *vcpu); int vgic_v4_put(struct kvm_vcpu *vcpu); +int vgic_v5_finalize_ppi_state(struct kvm *kvm); +bool vgic_v5_ppi_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq, + unsigned long flags); +void vgic_v5_set_ppi_dvi(struct kvm_vcpu *vcpu, struct vgic_irq *irq, bool dvi); + bool vgic_state_is_nested(struct kvm_vcpu *vcpu); /* CPU HP callbacks */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index bfacb9475aac..67effb91fa98 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -959,6 +959,12 @@ static inline int acpi_table_parse(char *id, return -ENODEV; } +static inline int acpi_get_cpu_uid(unsigned int cpu, u32 *uid) +{ + *uid = cpu; + return 0; +} + static inline int acpi_nvs_register(__u64 start, __u64 size) { return 0; diff --git a/include/linux/alloc_tag.h b/include/linux/alloc_tag.h index d40ac39bfbe8..02de2ede560f 100644 --- a/include/linux/alloc_tag.h +++ b/include/linux/alloc_tag.h @@ -163,9 +163,11 @@ static inline void alloc_tag_sub_check(union codetag_ref *ref) { WARN_ONCE(ref && !ref->ct, "alloc_tag was not set\n"); } +void alloc_tag_add_early_pfn(unsigned long pfn); #else static inline void alloc_tag_add_check(union codetag_ref *ref, struct alloc_tag *tag) {} static inline void alloc_tag_sub_check(union codetag_ref *ref) {} +static inline void alloc_tag_add_early_pfn(unsigned long pfn) {} #endif /* Caller should verify both ref and tag to be valid */ diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 50b47eba7d01..e7195750d21b 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -105,6 +105,12 @@ ARM_SMCCC_SMC_32, \ 0, 0x3fff) +/* C1-Pro erratum 4193714: SME DVMSync early acknowledgement */ +#define ARM_SMCCC_CPU_WORKAROUND_4193714 \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_CPU, 0x10) + #define ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID \ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ ARM_SMCCC_SMC_32, \ diff --git a/include/linux/bio.h b/include/linux/bio.h index 97d747320b35..dc17780d6c1e 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -475,7 +475,8 @@ void __bio_release_pages(struct bio *bio, bool mark_dirty); extern void bio_set_pages_dirty(struct bio *bio); extern void bio_check_pages_dirty(struct bio *bio); -int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter, size_t maxlen); +int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter, size_t maxlen, + size_t minsize); void bio_iov_iter_unbounce(struct bio *bio, bool is_error, bool mark_dirty); extern void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h b/include/linux/bnxt/ulp.h similarity index 86% rename from drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h rename to include/linux/bnxt/ulp.h index 3c5b8a53f715..0851ad3394b0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h +++ b/include/linux/bnxt/ulp.h @@ -10,6 +10,8 @@ #ifndef BNXT_ULP_H #define BNXT_ULP_H +#include + #define BNXT_MIN_ROCE_CP_RINGS 2 #define BNXT_MIN_ROCE_STAT_CTXS 1 @@ -20,6 +22,18 @@ struct hwrm_async_event_cmpl; struct bnxt; +enum bnxt_auxdev_type { + BNXT_AUXDEV_RDMA = 0, + BNXT_AUXDEV_FWCTL, + __BNXT_AUXDEV_MAX +}; + +struct bnxt_aux_priv { + struct auxiliary_device aux_dev; + struct bnxt_en_dev *edev; + int id; +}; + struct bnxt_msix_entry { u32 vector; u32 ring_idx; @@ -110,19 +124,21 @@ void bnxt_set_ulp_stat_ctxs(struct bnxt *bp, int num_ctxs); int bnxt_get_ulp_stat_ctxs_in_use(struct bnxt *bp); void bnxt_set_dflt_ulp_stat_ctxs(struct bnxt *bp); void bnxt_ulp_stop(struct bnxt *bp); -void bnxt_ulp_start(struct bnxt *bp, int err); +void bnxt_ulp_start(struct bnxt *bp); void bnxt_ulp_sriov_cfg(struct bnxt *bp, int num_vfs); void bnxt_ulp_irq_stop(struct bnxt *bp); void bnxt_ulp_irq_restart(struct bnxt *bp, int err); void bnxt_ulp_async_events(struct bnxt *bp, struct hwrm_async_event_cmpl *cmpl); -void bnxt_rdma_aux_device_uninit(struct bnxt *bp); -void bnxt_rdma_aux_device_del(struct bnxt *bp); -void bnxt_rdma_aux_device_add(struct bnxt *bp); -void bnxt_rdma_aux_device_init(struct bnxt *bp); +void bnxt_aux_devices_uninit(struct bnxt *bp); +void bnxt_aux_devices_del(struct bnxt *bp); +void bnxt_aux_devices_add(struct bnxt *bp); +void bnxt_aux_devices_init(struct bnxt *bp); int bnxt_register_dev(struct bnxt_en_dev *edev, struct bnxt_ulp_ops *ulp_ops, void *handle); void bnxt_unregister_dev(struct bnxt_en_dev *edev); int bnxt_send_msg(struct bnxt_en_dev *edev, struct bnxt_fw_msg *fw_msg); void bnxt_register_async_events(struct bnxt_en_dev *edev, unsigned long *events_bmap, u16 max_id); +int bnxt_auxdev_id_alloc(struct bnxt *bp); +void bnxt_auxdev_id_free(struct bnxt *bp, int id); #endif diff --git a/include/linux/bootconfig.h b/include/linux/bootconfig.h index 25df9260d206..692a5acc2ffc 100644 --- a/include/linux/bootconfig.h +++ b/include/linux/bootconfig.h @@ -36,9 +36,9 @@ bool __init cmdline_has_extra_options(void); * The checksum will be used with the BOOTCONFIG_MAGIC and the size for * embedding the bootconfig in the initrd image. */ -static inline __init uint32_t xbc_calc_checksum(void *data, uint32_t size) +static inline __init uint32_t xbc_calc_checksum(const void *data, uint32_t size) { - unsigned char *p = data; + const unsigned char *p = data; uint32_t ret = 0; while (size--) @@ -66,7 +66,7 @@ struct xbc_node { /* Node tree access raw APIs */ struct xbc_node * __init xbc_root_node(void); -int __init xbc_node_index(struct xbc_node *node); +uint16_t __init xbc_node_index(struct xbc_node *node); struct xbc_node * __init xbc_node_get_parent(struct xbc_node *node); struct xbc_node * __init xbc_node_get_child(struct xbc_node *node); struct xbc_node * __init xbc_node_get_next(struct xbc_node *node); diff --git a/include/linux/bootmem_info.h b/include/linux/bootmem_info.h index 4c506e76a808..492ceeb1cdf8 100644 --- a/include/linux/bootmem_info.h +++ b/include/linux/bootmem_info.h @@ -44,10 +44,6 @@ static inline void free_bootmem_page(struct page *page) { enum bootmem_type type = bootmem_type(page); - /* - * The reserve_bootmem_region sets the reserved flag on bootmem - * pages. - */ VM_BUG_ON_PAGE(page_ref_count(page) != 2, page); if (type == SECTION_INFO || type == MIX_SECTION_INFO) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 0136a108d083..c323b3e027fe 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -617,6 +618,8 @@ void bpf_rb_root_free(const struct btf_field *field, void *rb_root, struct bpf_spin_lock *spin_lock); u64 bpf_arena_get_kern_vm_start(struct bpf_arena *arena); u64 bpf_arena_get_user_vm_start(struct bpf_arena *arena); +u64 bpf_arena_map_kern_vm_start(struct bpf_map *map); +struct bpf_map *bpf_prog_arena(struct bpf_prog *prog); int bpf_obj_name_cpy(char *dst, const char *src, unsigned int size); struct bpf_offload_dev; @@ -678,6 +681,8 @@ int bpf_dynptr_from_file_sleepable(struct file *file, u32 flags, void *bpf_arena_alloc_pages_non_sleepable(void *p__map, void *addr__ign, u32 page_cnt, int node_id, u64 flags); void bpf_arena_free_pages_non_sleepable(void *p__map, void *ptr__ign, u32 page_cnt); +void *bpf_arena_alloc_pages_sleepable(void *p__map, void *addr__ign, u32 page_cnt, int node_id, + u64 flags); #else static inline void *bpf_arena_alloc_pages_non_sleepable(void *p__map, void *addr__ign, u32 page_cnt, int node_id, u64 flags) @@ -688,6 +693,12 @@ static inline void *bpf_arena_alloc_pages_non_sleepable(void *p__map, void *addr static inline void bpf_arena_free_pages_non_sleepable(void *p__map, void *ptr__ign, u32 page_cnt) { } + +static inline void *bpf_arena_alloc_pages_sleepable(void *p__map, void *addr__ign, u32 page_cnt, + int node_id, u64 flags) +{ + return NULL; +} #endif extern const struct bpf_map_ops bpf_map_offload_ops; @@ -1541,6 +1552,8 @@ bool bpf_has_frame_pointer(unsigned long ip); int bpf_jit_charge_modmem(u32 size); void bpf_jit_uncharge_modmem(u32 size); bool bpf_prog_has_trampoline(const struct bpf_prog *prog); +bool bpf_insn_is_indirect_target(const struct bpf_verifier_env *env, const struct bpf_prog *prog, + int insn_idx); #else static inline int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr, @@ -2120,6 +2133,9 @@ int bpf_prog_assoc_struct_ops(struct bpf_prog *prog, struct bpf_map *map); void bpf_prog_disassoc_struct_ops(struct bpf_prog *prog); void *bpf_prog_get_assoc_struct_ops(const struct bpf_prog_aux *aux); u32 bpf_struct_ops_id(const void *kdata); +int bpf_struct_ops_for_each_prog(const void *kdata, + int (*cb)(struct bpf_prog *prog, void *data), + void *data); #ifdef CONFIG_NET /* Define it here to avoid the use of forward declaration */ @@ -3723,6 +3739,7 @@ extern const struct bpf_func_proto bpf_for_each_map_elem_proto; extern const struct bpf_func_proto bpf_btf_find_by_name_kind_proto; extern const struct bpf_func_proto bpf_sk_setsockopt_proto; extern const struct bpf_func_proto bpf_sk_getsockopt_proto; +extern const struct bpf_func_proto bpf_sk_setsockopt_nodelay_proto; extern const struct bpf_func_proto bpf_unlocked_sk_setsockopt_proto; extern const struct bpf_func_proto bpf_unlocked_sk_getsockopt_proto; extern const struct bpf_func_proto bpf_find_vma_proto; diff --git a/include/linux/bpf_defs.h b/include/linux/bpf_defs.h new file mode 100644 index 000000000000..2185cd3966d4 --- /dev/null +++ b/include/linux/bpf_defs.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Subset of bpf.h declarations, split out so files that need only these + * declarations can avoid bpf.h's full include cost. + */ +#ifndef _LINUX_BPF_DEFS_H +#define _LINUX_BPF_DEFS_H + +#ifdef CONFIG_BPF_SYSCALL +bool bpf_arena_handle_page_fault(unsigned long addr, bool is_write, unsigned long fault_ip); +#else +static inline bool bpf_arena_handle_page_fault(unsigned long addr, bool is_write, + unsigned long fault_ip) +{ + return false; +} +#endif + +#endif /* _LINUX_BPF_DEFS_H */ diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 53e8664cb566..b148f816f25b 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -630,16 +630,17 @@ struct bpf_insn_aux_data { /* below fields are initialized once */ unsigned int orig_idx; /* original instruction index */ - bool jmp_point; - bool prune_point; + u32 jmp_point:1; + u32 prune_point:1; /* ensure we check state equivalence and save state checkpoint and * this instruction, regardless of any heuristics */ - bool force_checkpoint; + u32 force_checkpoint:1; /* true if instruction is a call to a helper function that * accepts callback function as a parameter. */ - bool calls_callback; + u32 calls_callback:1; + u32 indirect_target:1; /* if it is an indirect jump target */ /* * CFG strongly connected component this instruction belongs to, * zero if it is a singleton SCC. diff --git a/drivers/bus/stm32_firewall.h b/include/linux/bus/stm32_firewall.h similarity index 100% rename from drivers/bus/stm32_firewall.h rename to include/linux/bus/stm32_firewall.h diff --git a/include/linux/bus/stm32_firewall_device.h b/include/linux/bus/stm32_firewall_device.h index eaa7a3f54450..6c878f3ca86f 100644 --- a/include/linux/bus/stm32_firewall_device.h +++ b/include/linux/bus/stm32_firewall_device.h @@ -112,6 +112,25 @@ int stm32_firewall_grant_access_by_id(struct stm32_firewall *firewall, u32 subsy */ void stm32_firewall_release_access_by_id(struct stm32_firewall *firewall, u32 subsystem_id); +/** + * stm32_firewall_get_grant_all_access - Allocate and get all the firewall(s) associated to given + * device. Then, try to grant access rights for each element. + * This function is basically a helper function that wraps + * both stm32_firewall_get_firewall() and + * stm32_firewall_grant_access() on all firewall references of + * a device along with the allocation of the array. + * Realease access using stm32_firewall_release_access* APIs + * when done. + * + * @dev: Device performing the checks + * @firewall: Pointer to the array of firewall references to be allocated + * @nb_firewall: Number of allocated elements in @firewall + * + * Returns 0 on success, or appropriate errno code if error occurred. + */ +int stm32_firewall_get_grant_all_access(struct device *dev, struct stm32_firewall **firewall, + int *nb_firewall); + #else /* CONFIG_STM32_FIREWALL */ static inline int stm32_firewall_get_firewall(struct device_node *np, @@ -141,5 +160,12 @@ static inline void stm32_firewall_release_access_by_id(struct stm32_firewall *fi { } +static inline int stm32_firewall_get_grant_all_access(struct device *dev, + struct stm32_firewall **firewall, + int *nb_firewall) +{ + return -ENODEV; +} + #endif /* CONFIG_STM32_FIREWALL */ #endif /* STM32_FIREWALL_DEVICE_H */ diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h index b907e6c2307d..260d7968cf72 100644 --- a/include/linux/cdrom.h +++ b/include/linux/cdrom.h @@ -108,6 +108,7 @@ int cdrom_ioctl(struct cdrom_device_info *cdi, struct block_device *bdev, extern unsigned int cdrom_check_events(struct cdrom_device_info *cdi, unsigned int clearing); +extern void cdrom_probe_write_features(struct cdrom_device_info *cdi); extern int register_cdrom(struct gendisk *disk, struct cdrom_device_info *cdi); extern void unregister_cdrom(struct cdrom_device_info *cdi); diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index f42563739d2e..50a784da7a81 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -611,8 +611,8 @@ struct cgroup { /* used to wait for offlining of csses */ wait_queue_head_t offline_waitq; - /* used by cgroup_rmdir() to wait for dying tasks to leave */ - wait_queue_head_t dying_populated_waitq; + /* defers killing csses after removal until cgroup is depopulated */ + struct work_struct finish_destroy_work; /* used to schedule release agent */ struct work_struct release_agent_work; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 630705a47129..b01a38fef8cf 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -136,10 +136,6 @@ struct clk_duty { * 0. Returns the calculated rate. Optional, but recommended - if * this op is not set then clock rate will be initialized to 0. * - * @round_rate: Given a target rate as input, returns the closest rate actually - * supported by the clock. The parent rate is an input/output - * parameter. - * * @determine_rate: Given a target rate as input, returns the closest rate * actually supported by the clock, and optionally the parent clock * that should be used to provide the clock rate. @@ -163,13 +159,13 @@ struct clk_duty { * * @set_rate: Change the rate of this clock. The requested rate is specified * by the second argument, which should typically be the return - * of .round_rate call. The third argument gives the parent rate - * which is likely helpful for most .set_rate implementation. + * of .determine_rate call. The third argument gives the parent + * rate which is likely helpful for most .set_rate implementation. * Returns 0 on success, -EERROR otherwise. * * @set_rate_and_parent: Change the rate and the parent of this clock. The * requested rate is specified by the second argument, which - * should typically be the return of .round_rate call. The + * should typically be the return of clk_round_rate() call. The * third argument gives the parent rate which is likely helpful * for most .set_rate_and_parent implementation. The fourth * argument gives the parent index. This callback is optional (and @@ -244,8 +240,6 @@ struct clk_ops { void (*restore_context)(struct clk_hw *hw); unsigned long (*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate); - long (*round_rate)(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate); int (*determine_rate)(struct clk_hw *hw, struct clk_rate_request *req); int (*set_parent)(struct clk_hw *hw, u8 index); @@ -679,7 +673,7 @@ struct clk_div_table { * @lock: register lock * * Clock with an adjustable divider affecting its output frequency. Implements - * .recalc_rate, .set_rate and .round_rate + * .recalc_rate, .set_rate and .determine_rate * * @flags: * CLK_DIVIDER_ONE_BASED - by default the divisor is the value read from the @@ -739,14 +733,6 @@ extern const struct clk_ops clk_divider_ro_ops; unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate, unsigned int val, const struct clk_div_table *table, unsigned long flags, unsigned long width); -long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent, - unsigned long rate, unsigned long *prate, - const struct clk_div_table *table, - u8 width, unsigned long flags); -long divider_ro_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent, - unsigned long rate, unsigned long *prate, - const struct clk_div_table *table, u8 width, - unsigned long flags, unsigned int val); int divider_determine_rate(struct clk_hw *hw, struct clk_rate_request *req, const struct clk_div_table *table, u8 width, unsigned long flags); @@ -947,6 +933,26 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, (parent_hw), NULL, (flags), (reg), \ (shift), (width), (clk_divider_flags), \ NULL, (lock)) +/** + * devm_clk_hw_register_divider_parent_data - register a divider clock with the + * clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_data: parent clk data + * @flags: framework-specific flags + * @reg: register address to adjust divider + * @shift: number of bits to shift the bitfield + * @width: width of the bitfield + * @clk_divider_flags: divider-specific flags for this clock + * @lock: shared register lock for this clock + */ +#define devm_clk_hw_register_divider_parent_data(dev, name, parent_data, \ + flags, reg, shift, width, \ + clk_divider_flags, lock) \ + __devm_clk_hw_register_divider((dev), NULL, (name), NULL, NULL, \ + (parent_data), (flags), (reg), (shift), \ + (width), (clk_divider_flags), NULL, \ + (lock)) /** * devm_clk_hw_register_divider_table - register a table based divider clock * with the clock framework (devres variant) @@ -1126,7 +1132,7 @@ void of_fixed_factor_clk_setup(struct device_node *node); * * Clock with a fixed multiplier and divider. The output frequency is the * parent clock rate divided by div and multiplied by mult. - * Implements .recalc_rate, .set_rate, .round_rate and .recalc_accuracy + * Implements .recalc_rate, .set_rate, .determine_rate and .recalc_accuracy * * Flags: * * CLK_FIXED_FACTOR_FIXED_ACCURACY - Use the value in @acc instead of the @@ -1254,7 +1260,7 @@ void clk_hw_unregister_fractional_divider(struct clk_hw *hw); * @lock: register lock * * Clock with an adjustable multiplier affecting its output frequency. - * Implements .recalc_rate, .set_rate and .round_rate + * Implements .recalc_rate, .set_rate and .determine_rate * * @flags: * CLK_MULTIPLIER_ZERO_BYPASS - By default, the multiplier is the value read @@ -1437,26 +1443,6 @@ static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src) dst->core = src->core; } -static inline long divider_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate, - const struct clk_div_table *table, - u8 width, unsigned long flags) -{ - return divider_round_rate_parent(hw, clk_hw_get_parent(hw), - rate, prate, table, width, flags); -} - -static inline long divider_ro_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate, - const struct clk_div_table *table, - u8 width, unsigned long flags, - unsigned int val) -{ - return divider_ro_round_rate_parent(hw, clk_hw_get_parent(hw), - rate, prate, table, width, flags, - val); -} - /* * FIXME clock api without lock protection */ diff --git a/include/linux/cma.h b/include/linux/cma.h index d0793eaaadaa..8555d38a97b1 100644 --- a/include/linux/cma.h +++ b/include/linux/cma.h @@ -61,14 +61,4 @@ extern int cma_for_each_area(int (*it)(struct cma *cma, void *data), void *data) extern bool cma_intersects(struct cma *cma, unsigned long start, unsigned long end); extern void cma_reserve_pages_on_error(struct cma *cma); - -#ifdef CONFIG_DMA_CMA -extern bool cma_skip_dt_default_reserved_mem(void); -#else -static inline bool cma_skip_dt_default_reserved_mem(void) -{ - return false; -} -#endif - #endif diff --git a/include/linux/comedi/comedi_isadma.h b/include/linux/comedi/comedi_isadma.h index 9d2b12db7e6e..7514ce222fa6 100644 --- a/include/linux/comedi/comedi_isadma.h +++ b/include/linux/comedi/comedi_isadma.h @@ -48,11 +48,11 @@ struct comedi_isadma_desc { */ struct comedi_isadma { struct device *dev; - struct comedi_isadma_desc *desc; int n_desc; int cur_dma; unsigned int chan; unsigned int chan2; + struct comedi_isadma_desc desc[] __counted_by(n_desc); }; #if IS_ENABLED(CONFIG_ISA_DMA_API) diff --git a/include/linux/comedi/comedidev.h b/include/linux/comedi/comedidev.h index 35fdc41845ce..577a08f37aee 100644 --- a/include/linux/comedi/comedidev.h +++ b/include/linux/comedi/comedidev.h @@ -1026,10 +1026,55 @@ int comedi_load_firmware(struct comedi_device *dev, struct device *hw_dev, unsigned long context), unsigned long context); -int __comedi_request_region(struct comedi_device *dev, - unsigned long start, unsigned long len); -int comedi_request_region(struct comedi_device *dev, - unsigned long start, unsigned long len); +int __comedi_check_request_region(struct comedi_device *dev, + unsigned long start, unsigned long len, + unsigned long minstart, unsigned long maxend, + unsigned long minalign); +int comedi_check_request_region(struct comedi_device *dev, + unsigned long start, unsigned long len, + unsigned long minstart, unsigned long maxend, + unsigned long minalign); + +/** + * __comedi_request_region() - Request an I/O region for a legacy driver + * @dev: COMEDI device. + * @start: Base address of the I/O region. + * @len: Length of the I/O region. + * + * Requests the specified I/O port region which must start at a non-zero + * address. + * + * Returns 0 on success, -EINVAL if @start is 0, or -EIO if the request + * fails. + */ +static inline int __comedi_request_region(struct comedi_device *dev, + unsigned long start, + unsigned long len) +{ + return __comedi_check_request_region(dev, start, len, 0, ~0ul, 1); +} + +/** + * comedi_request_region() - Request an I/O region for a legacy driver + * @dev: COMEDI device. + * @start: Base address of the I/O region. + * @len: Length of the I/O region. + * + * Requests the specified I/O port region which must start at a non-zero + * address. + * + * On success, @dev->iobase is set to the base address of the region and + * @dev->iolen is set to its length. + * + * Returns 0 on success, -EINVAL if @start is 0, or -EIO if the request + * fails. + */ +static inline int comedi_request_region(struct comedi_device *dev, + unsigned long start, unsigned long len) +{ + return comedi_check_request_region(dev, start, len, 0, ~0ul, 1); +} + void comedi_legacy_detach(struct comedi_device *dev); int comedi_auto_config(struct device *hardware_device, diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 2b48be97fcd0..2131febebee9 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -306,24 +306,19 @@ struct coresight_device { * coresight_dev_list - Mapping for devices to "name" index for device * names. * + * @node: Node on the global device index list. * @nr_idx: Number of entries already allocated. * @pfx: Prefix pattern for device name. * @fwnode_list: Array of fwnode_handles associated with each allocated * index, upto nr_idx entries. */ struct coresight_dev_list { + struct list_head node; int nr_idx; - const char *pfx; + char *pfx; struct fwnode_handle **fwnode_list; }; -#define DEFINE_CORESIGHT_DEVLIST(var, dev_pfx) \ -static struct coresight_dev_list (var) = { \ - .pfx = dev_pfx, \ - .nr_idx = 0, \ - .fwnode_list = NULL, \ -} - #define to_coresight_device(d) container_of(d, struct coresight_device, dev) /** @@ -663,8 +658,7 @@ void coresight_clear_self_claim_tag(struct csdev_access *csa); void coresight_clear_self_claim_tag_unlocked(struct csdev_access *csa); void coresight_disclaim_device(struct coresight_device *csdev); void coresight_disclaim_device_unlocked(struct coresight_device *csdev); -char *coresight_alloc_device_name(struct coresight_dev_list *devs, - struct device *dev); +char *coresight_alloc_device_name(const char *prefix, struct device *dev); bool coresight_loses_context_with_cpu(struct device *dev); diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h index d35726d6a415..c1dee3f971a9 100644 --- a/include/linux/crash_core.h +++ b/include/linux/crash_core.h @@ -34,13 +34,6 @@ static inline void arch_kexec_protect_crashkres(void) { } static inline void arch_kexec_unprotect_crashkres(void) { } #endif -#ifdef CONFIG_CRASH_DM_CRYPT -int crash_load_dm_crypt_keys(struct kimage *image); -ssize_t dm_crypt_keys_read(char *buf, size_t count, u64 *ppos); -#else -static inline int crash_load_dm_crypt_keys(struct kimage *image) {return 0; } -#endif - #ifndef arch_crash_handle_hotplug_event static inline void arch_crash_handle_hotplug_event(struct kimage *image, void *arg) { } #endif @@ -96,4 +89,11 @@ static inline void crash_save_cpu(struct pt_regs *regs, int cpu) {}; static inline int kimage_crash_copy_vmcoreinfo(struct kimage *image) { return 0; }; #endif /* CONFIG_CRASH_DUMP*/ +#ifdef CONFIG_CRASH_DM_CRYPT +int crash_load_dm_crypt_keys(struct kimage *image); +ssize_t dm_crypt_keys_read(char *buf, size_t count, u64 *ppos); +#else +static inline int crash_load_dm_crypt_keys(struct kimage *image) {return 0; } +#endif + #endif /* LINUX_CRASH_CORE_H */ diff --git a/include/linux/damon.h b/include/linux/damon.h index d9a3babbafc1..f2cdb7c3f5e6 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -818,9 +818,11 @@ struct damon_ctx { /* lists of &struct damon_call_control */ struct list_head call_controls; + bool call_controls_obsolete; struct mutex call_controls_lock; struct damos_walk_control *walk_control; + bool walk_control_obsolete; struct mutex walk_control_lock; /* diff --git a/include/linux/dax.h b/include/linux/dax.h index 10a7cc79aea5..fe6c3ded1b50 100644 --- a/include/linux/dax.h +++ b/include/linux/dax.h @@ -54,6 +54,7 @@ struct dax_device *alloc_dax(void *private, const struct dax_operations *ops); void *dax_holder(struct dax_device *dax_dev); void put_dax(struct dax_device *dax_dev); void kill_dax(struct dax_device *dax_dev); +struct dax_device *dax_dev_get(dev_t devt); void dax_write_cache(struct dax_device *dax_dev, bool wc); bool dax_write_cache_enabled(struct dax_device *dax_dev); bool dax_synchronous(struct dax_device *dax_dev); @@ -130,7 +131,6 @@ int dax_add_host(struct dax_device *dax_dev, struct gendisk *disk); void dax_remove_host(struct gendisk *disk); struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev, u64 *start_off, void *holder, const struct dax_holder_operations *ops); -void fs_put_dax(struct dax_device *dax_dev, void *holder); #else static inline int dax_add_host(struct dax_device *dax_dev, struct gendisk *disk) { @@ -145,14 +145,15 @@ static inline struct dax_device *fs_dax_get_by_bdev(struct block_device *bdev, { return NULL; } -static inline void fs_put_dax(struct dax_device *dax_dev, void *holder) -{ -} #endif /* CONFIG_BLOCK && CONFIG_FS_DAX */ #if IS_ENABLED(CONFIG_FS_DAX) +void fs_put_dax(struct dax_device *dax_dev, void *holder); +int fs_dax_get(struct dax_device *dax_dev, void *holder, + const struct dax_holder_operations *hops); int dax_writeback_mapping_range(struct address_space *mapping, struct dax_device *dax_dev, struct writeback_control *wbc); +int dax_folio_reset_order(struct folio *folio); struct page *dax_layout_busy_page(struct address_space *mapping); struct page *dax_layout_busy_page_range(struct address_space *mapping, loff_t start, loff_t end); @@ -163,6 +164,15 @@ dax_entry_t dax_lock_mapping_entry(struct address_space *mapping, void dax_unlock_mapping_entry(struct address_space *mapping, unsigned long index, dax_entry_t cookie); #else +static inline void fs_put_dax(struct dax_device *dax_dev, void *holder) +{ +} + +static inline int fs_dax_get(struct dax_device *dax_dev, void *holder, + const struct dax_holder_operations *hops) +{ + return -EOPNOTSUPP; +} static inline struct page *dax_layout_busy_page(struct address_space *mapping) { return NULL; @@ -242,6 +252,7 @@ static inline void dax_break_layout_final(struct inode *inode) bool dax_alive(struct dax_device *dax_dev); void *dax_get_private(struct dax_device *dax_dev); +int dax_set_ops(struct dax_device *dax_dev, const struct dax_operations *ops); long dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff, long nr_pages, enum dax_access_mode mode, void **kaddr, unsigned long *pfn); size_t dax_copy_from_iter(struct dax_device *dax_dev, pgoff_t pgoff, void *addr, diff --git a/include/linux/dcache.h b/include/linux/dcache.h index f60819dcfebd..2577c05f84ec 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -88,6 +88,7 @@ union shortname_store { #define d_lock d_lockref.lock #define d_iname d_shortname.string +struct completion_list; struct dentry { /* RCU lookup touched fields */ @@ -122,13 +123,24 @@ struct dentry { struct hlist_node d_sib; /* child of parent list */ struct hlist_head d_children; /* our children */ /* - * d_alias and d_rcu can share memory + * the following members can share memory - their uses are + * mutually exclusive. */ union { - struct hlist_node d_alias; /* inode alias list */ - struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */ + /* positives: inode alias list */ + struct hlist_node d_alias; + /* in-lookup ones (all negative, live): hash chain */ + struct hlist_bl_node d_in_lookup_hash; + /* killed ones: (already negative) used to schedule freeing */ struct rcu_head d_rcu; - } d_u; + /* + * live non-in-lookup negatives: used if shrink_dcache_tree() + * races with eviction by another thread and needs to wait for + * this dentry to get killed . Remains NULL for almost all + * negative dentries. + */ + struct completion_list *waiters; + }; }; /* @@ -264,7 +276,7 @@ extern void d_invalidate(struct dentry *); extern struct dentry * d_make_root(struct inode *); extern void d_mark_tmpfile(struct file *, struct inode *); -void d_mark_tmpfile_name(struct file *file, const struct qstr *name); +int d_mark_tmpfile_name(struct file *file, const struct qstr *name); extern void d_tmpfile(struct file *, struct inode *); extern struct dentry *d_find_alias(struct inode *); @@ -616,4 +628,8 @@ void set_default_d_op(struct super_block *, const struct dentry_operations *); struct dentry *d_make_persistent(struct dentry *, struct inode *); void d_make_discardable(struct dentry *dentry); +/* inode->i_lock must be held over that */ +#define for_each_alias(dentry, inode) \ + hlist_for_each_entry(dentry, &(inode)->i_dentry, d_alias) + #endif /* __LINUX_DCACHE_H */ diff --git a/include/linux/device.h b/include/linux/device.h index f0d52e1a6e07..9c8fde6a3d86 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -189,6 +189,22 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, #define DEVICE_ATTR_ADMIN_RW(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RW_MODE(_name, 0600) +/** + * DEVICE_ATTR_RW_NAMED - Define a read-write device attribute with a sysfs name + * that differs from the function name. + * @_name: Attribute function preface + * @_attrname: Attribute name as it wil be exposed in the sysfs. + * + * Like DEVICE_ATTR_RW(), but allows for reusing names under separate paths in + * the same driver. + */ +#define DEVICE_ATTR_RW_NAMED(_name, _attrname) \ + struct device_attribute dev_attr_##_name = { \ + .attr = { .name = _attrname, .mode = 0644 }, \ + .show = _name##_show, \ + .store = _name##_store, \ + } + /** * DEVICE_ATTR_RO - Define a readable device attribute. * @_name: Attribute name. @@ -207,6 +223,21 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, #define DEVICE_ATTR_ADMIN_RO(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RO_MODE(_name, 0400) +/** + * DEVICE_ATTR_RO_NAMED - Define a read-only device attribute with a sysfs name + * that differs from the function name. + * @_name: Attribute function preface + * @_attrname: Attribute name as it wil be exposed in the sysfs. + * + * Like DEVICE_ATTR_RO(), but allows for reusing names under separate paths in + * the same driver. + */ +#define DEVICE_ATTR_RO_NAMED(_name, _attrname) \ + struct device_attribute dev_attr_##_name = { \ + .attr = { .name = _attrname, .mode = 0444 }, \ + .show = _name##_show, \ + } + /** * DEVICE_ATTR_WO - Define an admin-only writable device attribute. * @_name: Attribute name. @@ -216,6 +247,21 @@ ssize_t device_show_string(struct device *dev, struct device_attribute *attr, #define DEVICE_ATTR_WO(_name) \ struct device_attribute dev_attr_##_name = __ATTR_WO(_name) +/** + * DEVICE_ATTR_WO_NAMED - Define a read-only device attribute with a sysfs name + * that differs from the function name. + * @_name: Attribute function preface + * @_attrname: Attribute name as it wil be exposed in the sysfs. + * + * Like DEVICE_ATTR_WO(), but allows for reusing names under separate paths in + * the same driver. + */ +#define DEVICE_ATTR_WO_NAMED(_name, _attrname) \ + struct device_attribute dev_attr_##_name = { \ + .attr = { .name = _attrname, .mode = 0200 }, \ + .store = _name##_store, \ + } + /** * DEVICE_ULONG_ATTR - Define a device attribute backed by an unsigned long. * @_name: Attribute name. @@ -458,6 +504,22 @@ struct device_physical_location { bool lid; }; +/** + * enum struct_device_flags - Flags in struct device + * + * Each flag should have a set of accessor functions created via + * __create_dev_flag_accessors() for each access. + * + * @DEV_FLAG_READY_TO_PROBE: If set then device_add() has finished enough + * initialization that probe could be called. + * @DEV_FLAG_COUNT: Number of defined struct_device_flags. + */ +enum struct_device_flags { + DEV_FLAG_READY_TO_PROBE = 0, + + DEV_FLAG_COUNT +}; + /** * struct device - The basic device structure * @parent: The device's "parent" device, the device to which it is attached. @@ -553,6 +615,7 @@ struct device_physical_location { * @dma_skip_sync: DMA sync operations can be skipped for coherent buffers. * @dma_iommu: Device is using default IOMMU implementation for DMA and * doesn't rely on dma_ops structure. + * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify. * * At the lowest level, every device in a Linux system is represented by an * instance of struct device. The device structure contains the information @@ -675,8 +738,36 @@ struct device { #ifdef CONFIG_IOMMU_DMA bool dma_iommu:1; #endif + + DECLARE_BITMAP(flags, DEV_FLAG_COUNT); }; +#define __create_dev_flag_accessors(accessor_name, flag_name) \ +static inline bool dev_##accessor_name(const struct device *dev) \ +{ \ + return test_bit(flag_name, dev->flags); \ +} \ +static inline void dev_set_##accessor_name(struct device *dev) \ +{ \ + set_bit(flag_name, dev->flags); \ +} \ +static inline void dev_clear_##accessor_name(struct device *dev) \ +{ \ + clear_bit(flag_name, dev->flags); \ +} \ +static inline void dev_assign_##accessor_name(struct device *dev, bool value) \ +{ \ + assign_bit(flag_name, dev->flags, value); \ +} \ +static inline bool dev_test_and_set_##accessor_name(struct device *dev) \ +{ \ + return test_and_set_bit(flag_name, dev->flags); \ +} + +__create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE); + +#undef __create_dev_flag_accessors + /** * struct device_link - Device link representation. * @supplier: The device on the supplier end of the link. diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 166933b82e27..d1203da56fc5 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -322,13 +322,13 @@ struct dma_buf { * @vmapping_counter: * * Used internally to refcnt the vmaps returned by dma_buf_vmap(). - * Protected by @lock. + * Protected by @resv. */ unsigned vmapping_counter; /** * @vmap_ptr: - * The current vmap ptr if @vmapping_counter > 0. Protected by @lock. + * The current vmap ptr if @vmapping_counter > 0. Protected by @resv. */ struct iosys_map vmap_ptr; diff --git a/include/linux/dma-buf/heaps/cma.h b/include/linux/dma-buf/heaps/cma.h deleted file mode 100644 index e751479e21e7..000000000000 --- a/include/linux/dma-buf/heaps/cma.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef DMA_BUF_HEAP_CMA_H_ -#define DMA_BUF_HEAP_CMA_H_ - -struct cma; - -#ifdef CONFIG_DMABUF_HEAPS_CMA -int dma_heap_cma_register_heap(struct cma *cma); -#else -static inline int dma_heap_cma_register_heap(struct cma *cma) -{ - return 0; -} -#endif // CONFIG_DMABUF_HEAPS_CMA - -#endif // DMA_BUF_HEAP_CMA_H_ diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h index 60b63756df82..6a1832a73cad 100644 --- a/include/linux/dma-map-ops.h +++ b/include/linux/dma-map-ops.h @@ -91,14 +91,8 @@ static inline void set_dma_ops(struct device *dev, #endif /* CONFIG_ARCH_HAS_DMA_OPS */ #ifdef CONFIG_DMA_CMA -extern struct cma *dma_contiguous_default_area; - -static inline struct cma *dev_get_cma_area(struct device *dev) -{ - if (dev && dev->cma_area) - return dev->cma_area; - return dma_contiguous_default_area; -} +struct cma *dev_get_cma_area(struct device *dev); +struct cma *dma_contiguous_get_area_by_idx(unsigned int idx); void dma_contiguous_reserve(phys_addr_t addr_limit); int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, @@ -117,6 +111,10 @@ static inline struct cma *dev_get_cma_area(struct device *dev) { return NULL; } +static inline struct cma *dma_contiguous_get_area_by_idx(unsigned int idx) +{ + return NULL; +} static inline void dma_contiguous_reserve(phys_addr_t limit) { } @@ -147,9 +145,6 @@ static inline void dma_free_contiguous(struct device *dev, struct page *page, { __free_pages(page, get_order(size)); } -static inline void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size) -{ -} #endif /* CONFIG_DMA_CMA*/ #ifdef CONFIG_DMA_DECLARE_COHERENT @@ -361,6 +356,12 @@ static inline void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, } #endif /* ARCH_HAS_SYNC_DMA_FOR_CPU */ +#ifndef CONFIG_ARCH_HAS_BATCHED_DMA_SYNC +static inline void arch_sync_dma_flush(void) +{ +} +#endif + #ifdef CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL void arch_sync_dma_for_cpu_all(void); #else diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 99ef042ecdb4..db8ab24a54f4 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -9,7 +9,7 @@ #include #include -/** +/* * List of possible attributes associated with a DMA mapping. The semantics * of each attribute should be defined in Documentation/core-api/dma-attributes.rst. */ @@ -92,6 +92,16 @@ * flushing. */ #define DMA_ATTR_REQUIRE_COHERENT (1UL << 12) +/* + * DMA_ATTR_CC_SHARED: Indicates the DMA mapping is shared (decrypted) for + * confidential computing guests. For normal system memory the caller must have + * called set_memory_decrypted(), and pgprot_decrypted must be used when + * creating CPU PTEs for the mapping. The same shared semantic may be passed + * to the vIOMMU when it sets up the IOPTE. For MMIO use together with + * DMA_ATTR_MMIO to indicate shared MMIO. Unless DMA_ATTR_MMIO is provided + * a struct page is required. + */ +#define DMA_ATTR_CC_SHARED (1UL << 13) /* * A dma_addr_t can hold any valid DMA or bus address for the platform. It can diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h index 270b5458aecf..1fafd5b0e315 100644 --- a/include/linux/dma/edma.h +++ b/include/linux/dma/edma.h @@ -73,6 +73,8 @@ enum dw_edma_chip_flags { * @ll_region_rd: DMA descriptor link list memory for read channel * @dt_region_wr: DMA data memory for write channel * @dt_region_rd: DMA data memory for read channel + * @db_irq: Virtual IRQ dedicated to interrupt emulation + * @db_offset: Offset from DMA register base * @mf: DMA register map format * @dw: struct dw_edma that is filled by dw_edma_probe() */ @@ -94,9 +96,14 @@ struct dw_edma_chip { struct dw_edma_region dt_region_wr[EDMA_MAX_WR_CH]; struct dw_edma_region dt_region_rd[EDMA_MAX_RD_CH]; + /* interrupt emulation */ + int db_irq; + resource_size_t db_offset; + enum dw_edma_map_format mf; struct dw_edma *dw; + bool cfg_non_ll; }; /* Export to the platform drivers */ diff --git a/include/linux/dma/qcom-gpi-dma.h b/include/linux/dma/qcom-gpi-dma.h index 6680dd1a43c6..332be28427e4 100644 --- a/include/linux/dma/qcom-gpi-dma.h +++ b/include/linux/dma/qcom-gpi-dma.h @@ -8,6 +8,9 @@ /** * enum spi_transfer_cmd - spi transfer commands + * @SPI_TX: SPI peripheral TX command + * @SPI_RX: SPI peripheral RX command + * @SPI_DUPLEX: SPI peripheral Duplex command */ enum spi_transfer_cmd { SPI_TX = 1, @@ -64,7 +67,7 @@ enum i2c_op { * @set_config: set peripheral config * @rx_len: receive length for buffer * @op: i2c cmd - * @muli-msg: is part of multi i2c r-w msgs + * @multi_msg: is part of multi i2c r-w msgs */ struct gpi_i2c_config { u8 set_config; diff --git a/include/linux/dma/ti-cppi5.h b/include/linux/dma/ti-cppi5.h index c53c0f6e3b1a..3fe19b75ddf7 100644 --- a/include/linux/dma/ti-cppi5.h +++ b/include/linux/dma/ti-cppi5.h @@ -16,8 +16,8 @@ * struct cppi5_desc_hdr_t - Descriptor header, present in all types of * descriptors * @pkt_info0: Packet info word 0 (n/a in Buffer desc) - * @pkt_info0: Packet info word 1 (n/a in Buffer desc) - * @pkt_info0: Packet info word 2 (n/a in Buffer desc) + * @pkt_info1: Packet info word 1 (n/a in Buffer desc) + * @pkt_info2: Packet info word 2 (n/a in Buffer desc) * @src_dst_tag: Packet info word 3 (n/a in Buffer desc) */ struct cppi5_desc_hdr_t { @@ -35,7 +35,7 @@ struct cppi5_desc_hdr_t { * @buf_info1: word 8: Buffer valid data length * @org_buf_len: word 9: Original buffer length * @org_buf_ptr: word 10/11: Original buffer pointer - * @epib[0]: Extended Packet Info Data (optional, 4 words), and/or + * @epib: Extended Packet Info Data (optional, 4 words), and/or * Protocol Specific Data (optional, 0-128 bytes in * multiples of 4), and/or * Other Software Data (0-N bytes, optional) @@ -132,7 +132,7 @@ struct cppi5_desc_epib_t { /** * struct cppi5_monolithic_desc_t - Monolithic-mode packet descriptor * @hdr: Descriptor header - * @epib[0]: Extended Packet Info Data (optional, 4 words), and/or + * @epib: Extended Packet Info Data (optional, 4 words), and/or * Protocol Specific Data (optional, 0-128 bytes in * multiples of 4), and/or * Other Software Data (0-N bytes, optional) @@ -179,7 +179,7 @@ static inline void cppi5_desc_dump(void *desc, u32 size) * cppi5_desc_is_tdcm - check if the paddr indicates Teardown Complete Message * @paddr: Physical address of the packet popped from the ring * - * Returns true if the address indicates TDCM + * Returns: true if the address indicates TDCM */ static inline bool cppi5_desc_is_tdcm(dma_addr_t paddr) { @@ -190,7 +190,7 @@ static inline bool cppi5_desc_is_tdcm(dma_addr_t paddr) * cppi5_desc_get_type - get descriptor type * @desc_hdr: packet descriptor/TR header * - * Returns descriptor type: + * Returns: descriptor type: * CPPI5_INFO0_DESC_TYPE_VAL_HOST * CPPI5_INFO0_DESC_TYPE_VAL_MONO * CPPI5_INFO0_DESC_TYPE_VAL_TR @@ -205,7 +205,7 @@ static inline u32 cppi5_desc_get_type(struct cppi5_desc_hdr_t *desc_hdr) * cppi5_desc_get_errflags - get Error Flags from Desc * @desc_hdr: packet/TR descriptor header * - * Returns Error Flags from Packet/TR Descriptor + * Returns: Error Flags from Packet/TR Descriptor */ static inline u32 cppi5_desc_get_errflags(struct cppi5_desc_hdr_t *desc_hdr) { @@ -307,7 +307,7 @@ static inline void cppi5_desc_set_tags_ids(struct cppi5_desc_hdr_t *desc_hdr, * @psdata_size: PSDATA size * @sw_data_size: SWDATA size * - * Returns required Host Packet Descriptor size + * Returns: required Host Packet Descriptor size * 0 - if PSDATA > CPPI5_INFO0_HDESC_PSDATA_MAX_SIZE */ static inline u32 cppi5_hdesc_calc_size(bool epib, u32 psdata_size, @@ -381,6 +381,8 @@ cppi5_hdesc_update_psdata_size(struct cppi5_host_desc_t *desc, u32 psdata_size) /** * cppi5_hdesc_get_psdata_size - get PSdata size in bytes * @desc: Host packet descriptor + * + * Returns: PSdata size in bytes */ static inline u32 cppi5_hdesc_get_psdata_size(struct cppi5_host_desc_t *desc) { @@ -398,7 +400,7 @@ static inline u32 cppi5_hdesc_get_psdata_size(struct cppi5_host_desc_t *desc) * cppi5_hdesc_get_pktlen - get Packet Length from HDesc * @desc: Host packet descriptor * - * Returns Packet Length from Host Packet Descriptor + * Returns: Packet Length from Host Packet Descriptor */ static inline u32 cppi5_hdesc_get_pktlen(struct cppi5_host_desc_t *desc) { @@ -408,6 +410,7 @@ static inline u32 cppi5_hdesc_get_pktlen(struct cppi5_host_desc_t *desc) /** * cppi5_hdesc_set_pktlen - set Packet Length in HDesc * @desc: Host packet descriptor + * @pkt_len: Packet length to set */ static inline void cppi5_hdesc_set_pktlen(struct cppi5_host_desc_t *desc, u32 pkt_len) @@ -420,7 +423,7 @@ static inline void cppi5_hdesc_set_pktlen(struct cppi5_host_desc_t *desc, * cppi5_hdesc_get_psflags - get Protocol Specific Flags from HDesc * @desc: Host packet descriptor * - * Returns Protocol Specific Flags from Host Packet Descriptor + * Returns: Protocol Specific Flags from Host Packet Descriptor */ static inline u32 cppi5_hdesc_get_psflags(struct cppi5_host_desc_t *desc) { @@ -431,6 +434,7 @@ static inline u32 cppi5_hdesc_get_psflags(struct cppi5_host_desc_t *desc) /** * cppi5_hdesc_set_psflags - set Protocol Specific Flags in HDesc * @desc: Host packet descriptor + * @ps_flags: Protocol Specific flags to set */ static inline void cppi5_hdesc_set_psflags(struct cppi5_host_desc_t *desc, u32 ps_flags) @@ -442,8 +446,10 @@ static inline void cppi5_hdesc_set_psflags(struct cppi5_host_desc_t *desc, } /** - * cppi5_hdesc_get_errflags - get Packet Type from HDesc + * cppi5_hdesc_get_pkttype - get Packet Type from HDesc * @desc: Host packet descriptor + * + * Returns: Packet type */ static inline u32 cppi5_hdesc_get_pkttype(struct cppi5_host_desc_t *desc) { @@ -452,7 +458,7 @@ static inline u32 cppi5_hdesc_get_pkttype(struct cppi5_host_desc_t *desc) } /** - * cppi5_hdesc_get_errflags - set Packet Type in HDesc + * cppi5_hdesc_set_pkttype - set Packet Type in HDesc * @desc: Host packet descriptor * @pkt_type: Packet Type */ @@ -501,7 +507,7 @@ static inline void cppi5_hdesc_reset_to_original(struct cppi5_host_desc_t *desc) /** * cppi5_hdesc_link_hbdesc - link Host Buffer Descriptor to HDesc * @desc: Host Packet Descriptor - * @buf_desc: Host Buffer Descriptor physical address + * @hbuf_desc: Host Buffer Descriptor physical address * * add and link Host Buffer Descriptor to HDesc */ @@ -527,7 +533,7 @@ static inline void cppi5_hdesc_reset_hbdesc(struct cppi5_host_desc_t *desc) * cppi5_hdesc_epib_present - check if EPIB present * @desc_hdr: packet descriptor/TR header * - * Returns true if EPIB present in the packet + * Returns: true if EPIB present in the packet */ static inline bool cppi5_hdesc_epib_present(struct cppi5_desc_hdr_t *desc_hdr) { @@ -538,7 +544,7 @@ static inline bool cppi5_hdesc_epib_present(struct cppi5_desc_hdr_t *desc_hdr) * cppi5_hdesc_get_psdata - Get pointer on PSDATA * @desc: Host packet descriptor * - * Returns pointer on PSDATA in HDesc. + * Returns: pointer on PSDATA in HDesc. * NULL - if ps_data placed at the start of data buffer. */ static inline void *cppi5_hdesc_get_psdata(struct cppi5_host_desc_t *desc) @@ -568,7 +574,7 @@ static inline void *cppi5_hdesc_get_psdata(struct cppi5_host_desc_t *desc) * cppi5_hdesc_get_swdata - Get pointer on swdata * @desc: Host packet descriptor * - * Returns pointer on SWDATA in HDesc. + * Returns: pointer on SWDATA in HDesc. * NOTE. It's caller responsibility to be sure hdesc actually has swdata. */ static inline void *cppi5_hdesc_get_swdata(struct cppi5_host_desc_t *desc) @@ -648,6 +654,7 @@ enum cppi5_tr_types { CPPI5_TR_TYPE11, /* type12-14: Reserved */ CPPI5_TR_TYPE15 = 15, + /* private: */ CPPI5_TR_TYPE_MAX }; @@ -673,6 +680,7 @@ enum cppi5_tr_event_size { CPPI5_TR_EVENT_SIZE_ICNT1_DEC, CPPI5_TR_EVENT_SIZE_ICNT2_DEC, CPPI5_TR_EVENT_SIZE_ICNT3_DEC, + /* private: */ CPPI5_TR_EVENT_SIZE_MAX }; @@ -690,6 +698,7 @@ enum cppi5_tr_trigger { CPPI5_TR_TRIGGER_GLOBAL0, CPPI5_TR_TRIGGER_GLOBAL1, CPPI5_TR_TRIGGER_LOCAL_EVENT, + /* private: */ CPPI5_TR_TRIGGER_MAX }; @@ -711,6 +720,7 @@ enum cppi5_tr_trigger_type { CPPI5_TR_TRIGGER_TYPE_ICNT2_DEC, CPPI5_TR_TRIGGER_TYPE_ICNT3_DEC, CPPI5_TR_TRIGGER_TYPE_ALL, + /* private: */ CPPI5_TR_TRIGGER_TYPE_MAX }; @@ -815,7 +825,7 @@ struct cppi5_tr_type3_t { * destination * @dicnt1: Total loop iteration count for level 1 for destination * @dicnt2: Total loop iteration count for level 2 for destination - * @sicnt3: Total loop iteration count for level 3 (outermost) for + * @dicnt3: Total loop iteration count for level 3 (outermost) for * destination */ struct cppi5_tr_type15_t { @@ -887,6 +897,7 @@ enum cppi5_tr_resp_status_type { CPPI5_TR_RESPONSE_STATUS_UNSUPPORTED_ERR, CPPI5_TR_RESPONSE_STATUS_TRANSFER_EXCEPTION, CPPI5_TR_RESPONSE_STATUS__TEARDOWN_FLUSH, + /* private: */ CPPI5_TR_RESPONSE_STATUS_MAX }; @@ -903,6 +914,7 @@ enum cppi5_tr_resp_status_submission { CPPI5_TR_RESPONSE_STATUS_SUBMISSION_ICNT0, CPPI5_TR_RESPONSE_STATUS_SUBMISSION_FIFO_FULL, CPPI5_TR_RESPONSE_STATUS_SUBMISSION_OWN, + /* private: */ CPPI5_TR_RESPONSE_STATUS_SUBMISSION_MAX }; @@ -931,6 +943,7 @@ enum cppi5_tr_resp_status_unsupported { CPPI5_TR_RESPONSE_STATUS_UNSUPPORTED_DFMT, CPPI5_TR_RESPONSE_STATUS_UNSUPPORTED_SECTR, CPPI5_TR_RESPONSE_STATUS_UNSUPPORTED_AMODE_SPECIFIC, + /* private: */ CPPI5_TR_RESPONSE_STATUS_UNSUPPORTED_MAX }; @@ -939,7 +952,7 @@ enum cppi5_tr_resp_status_unsupported { * @tr_count: number of TR records * @tr_size: Nominal size of TR record (max) [16, 32, 64, 128] * - * Returns required TR Descriptor size + * Returns: required TR Descriptor size */ static inline size_t cppi5_trdesc_calc_size(u32 tr_count, u32 tr_size) { @@ -955,7 +968,7 @@ static inline size_t cppi5_trdesc_calc_size(u32 tr_count, u32 tr_size) /** * cppi5_trdesc_init - Init TR Descriptor - * @desc: TR Descriptor + * @desc_hdr: TR Descriptor * @tr_count: number of TR records * @tr_size: Nominal size of TR record (max) [16, 32, 64, 128] * @reload_idx: Absolute index to jump to on the 2nd and following passes @@ -1044,7 +1057,7 @@ static inline void cppi5_tr_set_trigger(cppi5_tr_flags_t *flags, } /** - * cppi5_tr_cflag_set - Update the Configuration specific flags + * cppi5_tr_csf_set - Update the Configuration specific flags * @flags: Pointer to the TR's flags * @csf: Configuration specific flags * diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 99efe2b9b4ea..b3d251c9734e 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -996,7 +996,8 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single( * @vecs: The array of DMA vectors that should be transferred * @nents: The number of DMA vectors in the array * @dir: Specifies the direction of the data transfer - * @flags: DMA engine flags + * @flags: DMA engine flags - DMA_PREP_REPEAT can be used to mark a cyclic + * DMA transfer */ static inline struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec( struct dma_chan *chan, const struct dma_vec *vecs, size_t nents, diff --git a/include/linux/dpll.h b/include/linux/dpll.h index b7277a8b484d..f8037f1ab20b 100644 --- a/include/linux/dpll.h +++ b/include/linux/dpll.h @@ -286,6 +286,7 @@ int dpll_pin_ref_sync_pair_add(struct dpll_pin *pin, int dpll_device_change_ntf(struct dpll_device *dpll); +int __dpll_pin_change_ntf(struct dpll_pin *pin); int dpll_pin_change_ntf(struct dpll_pin *pin); int register_dpll_notifier(struct notifier_block *nb); diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h index e04d67e999a1..416a3352261f 100644 --- a/include/linux/entry-common.h +++ b/include/linux/entry-common.h @@ -20,31 +20,21 @@ /* * SYSCALL_WORK flags handled in syscall_enter_from_user_mode() */ -#ifndef ARCH_SYSCALL_WORK_ENTER -# define ARCH_SYSCALL_WORK_ENTER (0) -#endif - -/* - * SYSCALL_WORK flags handled in syscall_exit_to_user_mode() - */ -#ifndef ARCH_SYSCALL_WORK_EXIT -# define ARCH_SYSCALL_WORK_EXIT (0) -#endif - #define SYSCALL_WORK_ENTER (SYSCALL_WORK_SECCOMP | \ SYSCALL_WORK_SYSCALL_TRACEPOINT | \ SYSCALL_WORK_SYSCALL_TRACE | \ SYSCALL_WORK_SYSCALL_EMU | \ SYSCALL_WORK_SYSCALL_AUDIT | \ SYSCALL_WORK_SYSCALL_USER_DISPATCH | \ - SYSCALL_WORK_SYSCALL_RSEQ_SLICE | \ - ARCH_SYSCALL_WORK_ENTER) + SYSCALL_WORK_SYSCALL_RSEQ_SLICE) +/* + * SYSCALL_WORK flags handled in syscall_exit_to_user_mode() + */ #define SYSCALL_WORK_EXIT (SYSCALL_WORK_SYSCALL_TRACEPOINT | \ SYSCALL_WORK_SYSCALL_TRACE | \ SYSCALL_WORK_SYSCALL_AUDIT | \ SYSCALL_WORK_SYSCALL_USER_DISPATCH | \ - SYSCALL_WORK_SYSCALL_EXIT_TRAP | \ - ARCH_SYSCALL_WORK_EXIT) + SYSCALL_WORK_SYSCALL_EXIT_TRAP) /** * arch_ptrace_report_syscall_entry - Architecture specific ptrace_report_syscall_entry() wrapper diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index ea9ca0e4172a..728fb5dee5ed 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -39,12 +39,16 @@ static inline void eventpoll_release(struct file *file) { /* - * Fast check to avoid the get/release of the semaphore. Since - * we're doing this outside the semaphore lock, it might return - * false negatives, but we don't care. It'll help in 99.99% of cases - * to avoid the semaphore lock. False positives simply cannot happen - * because the file in on the way to be removed and nobody ( but - * eventpoll ) has still a reference to this file. + * Fast check to skip the slow path in the common case where the + * file was never attached to an epoll. Safe without file->f_lock + * because every f_ep writer excludes a concurrent __fput() on + * @file: + * - ep_insert() requires the file alive (refcount > 0); + * - ep_remove() holds @file pinned via epi_fget() across the + * write; + * - eventpoll_release_file() runs from __fput() itself. + * We are in __fput() here, so none of those can race us: a NULL + * observation truly means no epoll path has work left on @file. */ if (likely(!READ_ONCE(file->f_ep))) return; diff --git a/include/linux/evm.h b/include/linux/evm.h index ddece4a6b25d..913f4573b203 100644 --- a/include/linux/evm.h +++ b/include/linux/evm.h @@ -18,6 +18,8 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry, const char *xattr_name, void *xattr_value, size_t xattr_value_len); +int evm_fix_hmac(struct dentry *dentry, const char *xattr_name, + const char *xattr_value, size_t xattr_value_len); int evm_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, struct xattr *xattrs, int *xattr_count); @@ -51,6 +53,12 @@ static inline enum integrity_status evm_verifyxattr(struct dentry *dentry, { return INTEGRITY_UNKNOWN; } + +static inline int evm_fix_hmac(struct dentry *dentry, const char *xattr_name, + const char *xattr_value, size_t xattr_value_len) +{ + return -EOPNOTSUPP; +} #endif static inline int evm_inode_init_security(struct inode *inode, struct inode *dir, diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index dc41722fcc9d..829a59399dac 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -80,6 +80,9 @@ enum stop_cp_reason { STOP_CP_REASON_NO_SEGMENT, STOP_CP_REASON_CORRUPTED_FREE_BITMAP, STOP_CP_REASON_CORRUPTED_NID, + STOP_CP_REASON_READ_META, + STOP_CP_REASON_READ_NODE, + STOP_CP_REASON_READ_DATA, STOP_CP_REASON_MAX, }; diff --git a/include/linux/filelock.h b/include/linux/filelock.h index d2c9740e26a8..5f0a2fb31450 100644 --- a/include/linux/filelock.h +++ b/include/linux/filelock.h @@ -50,6 +50,7 @@ struct lease_manager_operations { void (*lm_setup)(struct file_lease *, void **); bool (*lm_breaker_owns_lease)(struct file_lease *); int (*lm_open_conflict)(struct file *, int); + bool (*lm_breaker_timedout)(struct file_lease *fl); }; struct lock_manager { diff --git a/include/linux/filter.h b/include/linux/filter.h index f552170eacf4..1ec6d5ba64cc 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -1108,6 +1108,8 @@ sk_filter_reason(struct sock *sk, struct sk_buff *skb) return sk_filter_trim_cap(sk, skb, 1); } +struct bpf_prog *__bpf_prog_select_runtime(struct bpf_verifier_env *env, struct bpf_prog *fp, + int *err); struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err); void bpf_prog_free(struct bpf_prog *fp); @@ -1153,7 +1155,7 @@ u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); ((u64 (*)(u64, u64, u64, u64, u64, const struct bpf_insn *)) \ (void *)__bpf_call_base) -struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog); +struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog); void bpf_jit_compile(struct bpf_prog *prog); bool bpf_jit_needs_zext(void); bool bpf_jit_inlines_helper_call(s32 imm); @@ -1184,6 +1186,31 @@ static inline bool bpf_dump_raw_ok(const struct cred *cred) struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, const struct bpf_insn *patch, u32 len); + +#ifdef CONFIG_BPF_SYSCALL +struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off, + const struct bpf_insn *patch, u32 len); +struct bpf_insn_aux_data *bpf_dup_insn_aux_data(struct bpf_verifier_env *env); +void bpf_restore_insn_aux_data(struct bpf_verifier_env *env, + struct bpf_insn_aux_data *orig_insn_aux); +#else +static inline struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off, + const struct bpf_insn *patch, u32 len) +{ + return ERR_PTR(-ENOTSUPP); +} + +static inline struct bpf_insn_aux_data *bpf_dup_insn_aux_data(struct bpf_verifier_env *env) +{ + return NULL; +} + +static inline void bpf_restore_insn_aux_data(struct bpf_verifier_env *env, + struct bpf_insn_aux_data *orig_insn_aux) +{ +} +#endif /* CONFIG_BPF_SYSCALL */ + int bpf_remove_insns(struct bpf_prog *prog, u32 off, u32 cnt); static inline bool xdp_return_frame_no_direct(void) @@ -1310,9 +1337,14 @@ int bpf_jit_get_func_addr(const struct bpf_prog *prog, const char *bpf_jit_get_prog_name(struct bpf_prog *prog); -struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *fp); +struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bpf_prog *prog); void bpf_jit_prog_release_other(struct bpf_prog *fp, struct bpf_prog *fp_other); +static inline bool bpf_prog_need_blind(const struct bpf_prog *prog) +{ + return prog->blinding_requested && !prog->blinded; +} + static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen, u32 pass, void *image) { @@ -1451,6 +1483,20 @@ static inline void bpf_prog_kallsyms_del(struct bpf_prog *fp) { } +static inline bool bpf_prog_need_blind(const struct bpf_prog *prog) +{ + return false; +} + +static inline +struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bpf_prog *prog) +{ + return prog; +} + +static inline void bpf_jit_prog_release_other(struct bpf_prog *fp, struct bpf_prog *fp_other) +{ +} #endif /* CONFIG_BPF_JIT */ void bpf_prog_kallsyms_del_all(struct bpf_prog *fp); diff --git a/include/linux/firmware/samsung/exynos-acpm-protocol.h b/include/linux/firmware/samsung/exynos-acpm-protocol.h index 2091da965a5a..13f17dc4443b 100644 --- a/include/linux/firmware/samsung/exynos-acpm-protocol.h +++ b/include/linux/firmware/samsung/exynos-acpm-protocol.h @@ -14,30 +14,24 @@ struct acpm_handle; struct device_node; struct acpm_dvfs_ops { - int (*set_rate)(const struct acpm_handle *handle, - unsigned int acpm_chan_id, unsigned int clk_id, - unsigned long rate); - unsigned long (*get_rate)(const struct acpm_handle *handle, + int (*set_rate)(struct acpm_handle *handle, unsigned int acpm_chan_id, + unsigned int clk_id, unsigned long rate); + unsigned long (*get_rate)(struct acpm_handle *handle, unsigned int acpm_chan_id, unsigned int clk_id); }; struct acpm_pmic_ops { - int (*read_reg)(const struct acpm_handle *handle, - unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, - u8 *buf); - int (*bulk_read)(const struct acpm_handle *handle, - unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, - u8 count, u8 *buf); - int (*write_reg)(const struct acpm_handle *handle, - unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, - u8 value); - int (*bulk_write)(const struct acpm_handle *handle, - unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, - u8 count, const u8 *buf); - int (*update_reg)(const struct acpm_handle *handle, - unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, - u8 value, u8 mask); + int (*read_reg)(struct acpm_handle *handle, unsigned int acpm_chan_id, + u8 type, u8 reg, u8 chan, u8 *buf); + int (*bulk_read)(struct acpm_handle *handle, unsigned int acpm_chan_id, + u8 type, u8 reg, u8 chan, u8 count, u8 *buf); + int (*write_reg)(struct acpm_handle *handle, unsigned int acpm_chan_id, + u8 type, u8 reg, u8 chan, u8 value); + int (*bulk_write)(struct acpm_handle *handle, unsigned int acpm_chan_id, + u8 type, u8 reg, u8 chan, u8 count, const u8 *buf); + int (*update_reg)(struct acpm_handle *handle, unsigned int acpm_chan_id, + u8 type, u8 reg, u8 chan, u8 value, u8 mask); }; struct acpm_ops { @@ -56,12 +50,12 @@ struct acpm_handle { struct device; #if IS_ENABLED(CONFIG_EXYNOS_ACPM_PROTOCOL) -const struct acpm_handle *devm_acpm_get_by_node(struct device *dev, - struct device_node *np); +struct acpm_handle *devm_acpm_get_by_node(struct device *dev, + struct device_node *np); #else -static inline const struct acpm_handle *devm_acpm_get_by_node(struct device *dev, - struct device_node *np) +static inline struct acpm_handle *devm_acpm_get_by_node(struct device *dev, + struct device_node *np) { return NULL; } diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h index 0a3bcd1718f3..be1b38c981d4 100644 --- a/include/linux/fprobe.h +++ b/include/linux/fprobe.h @@ -94,6 +94,7 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num); int register_fprobe_syms(struct fprobe *fp, const char **syms, int num); int unregister_fprobe(struct fprobe *fp); +int unregister_fprobe_async(struct fprobe *fp); bool fprobe_is_registered(struct fprobe *fp); int fprobe_count_ips_from_filter(const char *filter, const char *notfilter); #else @@ -113,6 +114,10 @@ static inline int unregister_fprobe(struct fprobe *fp) { return -EOPNOTSUPP; } +static inline int unregister_fprobe_async(struct fprobe *fp) +{ + return -EOPNOTSUPP; +} static inline bool fprobe_is_registered(struct fprobe *fp) { return false; diff --git a/include/linux/fs.h b/include/linux/fs.h index e1d257e6da68..11559c513dfb 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2062,20 +2062,13 @@ void compat_set_desc_from_vma(struct vm_area_desc *desc, const struct file *file const struct vm_area_struct *vma); int __compat_vma_mmap(struct vm_area_desc *desc, struct vm_area_struct *vma); int compat_vma_mmap(struct file *file, struct vm_area_struct *vma); -int __vma_check_mmap_hook(struct vm_area_struct *vma); static inline int vfs_mmap(struct file *file, struct vm_area_struct *vma) { - int err; - if (file->f_op->mmap_prepare) return compat_vma_mmap(file, vma); - err = file->f_op->mmap(file, vma); - if (err) - return err; - - return __vma_check_mmap_hook(vma); + return file->f_op->mmap(file, vma); } static inline int vfs_mmap_prepare(struct file *file, struct vm_area_desc *desc) diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h index 916dc4fe7de3..83a449b4d6ec 100644 --- a/include/linux/fsl/ntmp.h +++ b/include/linux/fsl/ntmp.h @@ -31,6 +31,12 @@ struct netc_tbl_vers { u8 rsst_ver; }; +struct netc_swcbd { + void *buf; + dma_addr_t dma; + size_t size; +}; + struct netc_cbdr { struct device *dev; struct netc_cbdr_regs regs; @@ -44,9 +50,10 @@ struct netc_cbdr { void *addr_base_align; dma_addr_t dma_base; dma_addr_t dma_base_align; + struct netc_swcbd *swcbd; /* Serialize the order of command BD ring */ - spinlock_t ring_lock; + struct mutex ring_lock; }; struct ntmp_user { diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 95985400d3d8..e5cde39d6e85 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -915,6 +915,7 @@ extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group, unsigned int obj_type); extern void fsnotify_get_mark(struct fsnotify_mark *mark); extern void fsnotify_put_mark(struct fsnotify_mark *mark); +struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark); extern void fsnotify_finish_user_wait(struct fsnotify_iter_info *iter_info); extern bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info); diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index c242fe49af4c..28b30c6f1031 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -31,7 +31,7 @@ #define ARCH_SUPPORTS_FTRACE_OPS 0 #endif -#ifdef CONFIG_TRACING +#ifdef CONFIG_TRACER_SNAPSHOT extern void ftrace_boot_snapshot(void); #else static inline void ftrace_boot_snapshot(void) { } diff --git a/include/linux/hdlcdrv.h b/include/linux/hdlcdrv.h deleted file mode 100644 index 5d70c3f98f5b..000000000000 --- a/include/linux/hdlcdrv.h +++ /dev/null @@ -1,276 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * hdlcdrv.h -- HDLC packet radio network driver. - * The Linux soundcard driver for 1200 baud and 9600 baud packet radio - * (C) 1996-1998 by Thomas Sailer, HB9JNX/AE4WA - */ -#ifndef _HDLCDRV_H -#define _HDLCDRV_H - - -#include -#include -#include -#include - -#define HDLCDRV_MAGIC 0x5ac6e778 -#define HDLCDRV_HDLCBUFFER 32 /* should be a power of 2 for speed reasons */ -#define HDLCDRV_BITBUFFER 256 /* should be a power of 2 for speed reasons */ -#undef HDLCDRV_LOOPBACK /* define for HDLC debugging purposes */ -#define HDLCDRV_DEBUG - -/* maximum packet length, excluding CRC */ -#define HDLCDRV_MAXFLEN 400 - - -struct hdlcdrv_hdlcbuffer { - spinlock_t lock; - unsigned rd, wr; - unsigned short buf[HDLCDRV_HDLCBUFFER]; -}; - -#ifdef HDLCDRV_DEBUG -struct hdlcdrv_bitbuffer { - unsigned int rd; - unsigned int wr; - unsigned int shreg; - unsigned char buffer[HDLCDRV_BITBUFFER]; -}; - -static inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf, - unsigned int bit) -{ - unsigned char new; - - new = buf->shreg & 1; - buf->shreg >>= 1; - buf->shreg |= (!!bit) << 7; - if (new) { - buf->buffer[buf->wr] = buf->shreg; - buf->wr = (buf->wr+1) % sizeof(buf->buffer); - buf->shreg = 0x80; - } -} - -static inline void hdlcdrv_add_bitbuffer_word(struct hdlcdrv_bitbuffer *buf, - unsigned int bits) -{ - buf->buffer[buf->wr] = bits & 0xff; - buf->wr = (buf->wr+1) % sizeof(buf->buffer); - buf->buffer[buf->wr] = (bits >> 8) & 0xff; - buf->wr = (buf->wr+1) % sizeof(buf->buffer); - -} -#endif /* HDLCDRV_DEBUG */ - -/* -------------------------------------------------------------------- */ -/* - * Information that need to be kept for each driver. - */ - -struct hdlcdrv_ops { - /* - * first some informations needed by the hdlcdrv routines - */ - const char *drvname; - const char *drvinfo; - /* - * the routines called by the hdlcdrv routines - */ - int (*open)(struct net_device *); - int (*close)(struct net_device *); - int (*ioctl)(struct net_device *, void __user *, - struct hdlcdrv_ioctl *, int); -}; - -struct hdlcdrv_state { - int magic; - int opened; - - const struct hdlcdrv_ops *ops; - - struct { - int bitrate; - } par; - - struct hdlcdrv_pttoutput { - int dma2; - int seriobase; - int pariobase; - int midiiobase; - unsigned int flags; - } ptt_out; - - struct hdlcdrv_channel_params ch_params; - - struct hdlcdrv_hdlcrx { - struct hdlcdrv_hdlcbuffer hbuf; - unsigned long in_hdlc_rx; - /* 0 = sync hunt, != 0 receiving */ - int rx_state; - unsigned int bitstream; - unsigned int bitbuf; - int numbits; - unsigned char dcd; - - int len; - unsigned char *bp; - unsigned char buffer[HDLCDRV_MAXFLEN+2]; - } hdlcrx; - - struct hdlcdrv_hdlctx { - struct hdlcdrv_hdlcbuffer hbuf; - unsigned long in_hdlc_tx; - /* - * 0 = send flags - * 1 = send txtail (flags) - * 2 = send packet - */ - int tx_state; - int numflags; - unsigned int bitstream; - unsigned char ptt; - int calibrate; - int slotcnt; - - unsigned int bitbuf; - int numbits; - - int len; - unsigned char *bp; - unsigned char buffer[HDLCDRV_MAXFLEN+2]; - } hdlctx; - -#ifdef HDLCDRV_DEBUG - struct hdlcdrv_bitbuffer bitbuf_channel; - struct hdlcdrv_bitbuffer bitbuf_hdlc; -#endif /* HDLCDRV_DEBUG */ - - int ptt_keyed; - - /* queued skb for transmission */ - struct sk_buff *skb; -}; - - -/* -------------------------------------------------------------------- */ - -static inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&hb->lock, flags); - ret = !((HDLCDRV_HDLCBUFFER - 1 + hb->rd - hb->wr) % HDLCDRV_HDLCBUFFER); - spin_unlock_irqrestore(&hb->lock, flags); - return ret; -} - -/* -------------------------------------------------------------------- */ - -static inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&hb->lock, flags); - ret = (hb->rd == hb->wr); - spin_unlock_irqrestore(&hb->lock, flags); - return ret; -} - -/* -------------------------------------------------------------------- */ - -static inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb) -{ - unsigned long flags; - unsigned short val; - unsigned newr; - - spin_lock_irqsave(&hb->lock, flags); - if (hb->rd == hb->wr) - val = 0; - else { - newr = (hb->rd+1) % HDLCDRV_HDLCBUFFER; - val = hb->buf[hb->rd]; - hb->rd = newr; - } - spin_unlock_irqrestore(&hb->lock, flags); - return val; -} - -/* -------------------------------------------------------------------- */ - -static inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb, - unsigned short val) -{ - unsigned newp; - unsigned long flags; - - spin_lock_irqsave(&hb->lock, flags); - newp = (hb->wr+1) % HDLCDRV_HDLCBUFFER; - if (newp != hb->rd) { - hb->buf[hb->wr] = val & 0xffff; - hb->wr = newp; - } - spin_unlock_irqrestore(&hb->lock, flags); -} - -/* -------------------------------------------------------------------- */ - -static inline void hdlcdrv_putbits(struct hdlcdrv_state *s, unsigned int bits) -{ - hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, bits); -} - -static inline unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s) -{ - unsigned int ret; - - if (hdlcdrv_hbuf_empty(&s->hdlctx.hbuf)) { - if (s->hdlctx.calibrate > 0) - s->hdlctx.calibrate--; - else - s->hdlctx.ptt = 0; - ret = 0; - } else - ret = hdlcdrv_hbuf_get(&s->hdlctx.hbuf); -#ifdef HDLCDRV_LOOPBACK - hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, ret); -#endif /* HDLCDRV_LOOPBACK */ - return ret; -} - -static inline void hdlcdrv_channelbit(struct hdlcdrv_state *s, unsigned int bit) -{ -#ifdef HDLCDRV_DEBUG - hdlcdrv_add_bitbuffer(&s->bitbuf_channel, bit); -#endif /* HDLCDRV_DEBUG */ -} - -static inline void hdlcdrv_setdcd(struct hdlcdrv_state *s, int dcd) -{ - s->hdlcrx.dcd = !!dcd; -} - -static inline int hdlcdrv_ptt(struct hdlcdrv_state *s) -{ - return s->hdlctx.ptt || (s->hdlctx.calibrate > 0); -} - -/* -------------------------------------------------------------------- */ - -void hdlcdrv_receiver(struct net_device *, struct hdlcdrv_state *); -void hdlcdrv_transmitter(struct net_device *, struct hdlcdrv_state *); -void hdlcdrv_arbitrate(struct net_device *, struct hdlcdrv_state *); -struct net_device *hdlcdrv_register(const struct hdlcdrv_ops *ops, - unsigned int privsize, const char *ifname, - unsigned int baseaddr, unsigned int irq, - unsigned int dma); -void hdlcdrv_unregister(struct net_device *dev); - -/* -------------------------------------------------------------------- */ - - - -#endif /* _HDLCDRV_H */ diff --git a/include/linux/hid.h b/include/linux/hid.h index 31324609af4d..bfb9859f391e 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -634,6 +634,38 @@ enum hid_battery_status { HID_BATTERY_REPORTED, /* Device sent unsolicited battery strength report */ }; +/** + * struct hid_battery - represents a single battery power supply + * @dev: pointer to the parent hid_device + * @ps: the power supply instance + * @min: minimum battery value from HID descriptor + * @max: maximum battery value from HID descriptor + * @report_type: HID report type (input/feature) + * @report_id: HID report ID for this battery + * @charge_status: current charging status + * @status: battery reporting status + * @capacity: current battery capacity (0-100) + * @avoid_query: if true, avoid querying battery (e.g., for stylus) + * @present: if true, battery is present (may be dynamic) + * @ratelimit_time: rate limiting for battery reports + * @list: list node for linking into hid_device's battery list + */ +struct hid_battery { + struct hid_device *dev; + struct power_supply *ps; + __s32 min; + __s32 max; + __s32 report_type; + __s32 report_id; + __s32 charge_status; + enum hid_battery_status status; + __s32 capacity; + bool avoid_query; + bool present; + ktime_t ratelimit_time; + struct list_head list; +}; + struct hid_driver; struct hid_ll_driver; @@ -670,20 +702,10 @@ struct hid_device { #ifdef CONFIG_HID_BATTERY_STRENGTH /* * Power supply information for HID devices which report - * battery strength. power_supply was successfully registered if - * battery is non-NULL. + * battery strength. Each battery is tracked separately in the + * batteries list. */ - struct power_supply *battery; - __s32 battery_capacity; - __s32 battery_min; - __s32 battery_max; - __s32 battery_report_type; - __s32 battery_report_id; - __s32 battery_charge_status; - enum hid_battery_status battery_status; - bool battery_avoid_query; - bool battery_present; - ktime_t battery_ratelimit_time; + struct list_head batteries; #endif unsigned long status; /* see STAT flags above */ @@ -699,6 +721,7 @@ struct hid_device { char name[128]; /* Device name */ char phys[64]; /* Device physical location */ char uniq[64]; /* Device unique identifier (serial #) */ + u64 firmware_version; /* Firmware version */ void *driver_data; @@ -744,6 +767,15 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data) dev_set_drvdata(&hdev->dev, data); } +#ifdef CONFIG_HID_BATTERY_STRENGTH +static inline struct hid_battery *hid_get_battery(struct hid_device *hdev) +{ + if (list_empty(&hdev->batteries)) + return NULL; + return list_first_entry(&hdev->batteries, struct hid_battery, list); +} +#endif + #define HID_GLOBAL_STACK_SIZE 4 #define HID_COLLECTION_STACK_SIZE 4 @@ -998,6 +1030,8 @@ struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_ty int hid_set_field(struct hid_field *, unsigned, __s32); int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, int interrupt); +int hid_safe_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, + size_t bufsize, u32 size, int interrupt); struct hid_field *hidinput_get_led_field(struct hid_device *hid); unsigned int hidinput_count_leds(struct hid_device *hid); __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code); @@ -1266,8 +1300,8 @@ static inline u32 hid_report_len(struct hid_report *report) return DIV_ROUND_UP(report->size, 8) + (report->id > 0); } -int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, - int interrupt); +int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, + size_t bufsize, u32 size, int interrupt); /* HID quirks API */ unsigned long hid_lookup_quirk(const struct hid_device *hdev); diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index a2e47dbcf82c..19fffa4574a4 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -72,8 +72,8 @@ struct hid_ops { int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len, u64 source, bool from_bpf); int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type, - u8 *data, u32 size, int interrupt, u64 source, bool from_bpf, - bool lock_already_taken); + u8 *data, size_t bufsize, u32 size, int interrupt, u64 source, + bool from_bpf, bool lock_already_taken); struct module *owner; const struct bus_type *bus_type; }; @@ -200,7 +200,8 @@ struct hid_bpf { #ifdef CONFIG_HID_BPF u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, - u32 *size, int interrupt, u64 source, bool from_bpf); + size_t *buf_size, u32 *size, int interrupt, u64 source, + bool from_bpf); int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, u32 size, enum hid_report_type rtype, @@ -215,8 +216,11 @@ int hid_bpf_device_init(struct hid_device *hid); const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size); #else /* CONFIG_HID_BPF */ static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, - u8 *data, u32 *size, int interrupt, - u64 source, bool from_bpf) { return data; } + u8 *data, size_t *buf_size, u32 *size, + int interrupt, u64 source, bool from_bpf) +{ + return data; +} static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, unsigned char reportnum, u8 *buf, u32 size, enum hid_report_type rtype, diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h index 6ca92bff02c6..ea6bef9b6012 100644 --- a/include/linux/hsi/hsi.h +++ b/include/linux/hsi/hsi.h @@ -271,7 +271,7 @@ struct hsi_controller { struct module *owner; unsigned int id; unsigned int num_ports; - struct hsi_port **port; + struct hsi_port *port[] __counted_by(num_ports); }; #define to_hsi_controller(dev) container_of(dev, struct hsi_controller, device) diff --git a/include/linux/hwspinlock.h b/include/linux/hwspinlock.h index f35b42e8c5de..74b91244fe0e 100644 --- a/include/linux/hwspinlock.h +++ b/include/linux/hwspinlock.h @@ -25,34 +25,6 @@ struct hwspinlock; struct hwspinlock_device; struct hwspinlock_ops; -/** - * struct hwspinlock_pdata - platform data for hwspinlock drivers - * @base_id: base id for this hwspinlock device - * - * hwspinlock devices provide system-wide hardware locks that are used - * by remote processors that have no other way to achieve synchronization. - * - * To achieve that, each physical lock must have a system-wide id number - * that is agreed upon, otherwise remote processors can't possibly assume - * they're using the same hardware lock. - * - * Usually boards have a single hwspinlock device, which provides several - * hwspinlocks, and in this case, they can be trivially numbered 0 to - * (num-of-locks - 1). - * - * In case boards have several hwspinlocks devices, a different base id - * should be used for each hwspinlock device (they can't all use 0 as - * a starting id!). - * - * This platform data structure should be used to provide the base id - * for each device (which is trivially 0 when only a single hwspinlock - * device exists). It can be shared between different platforms, hence - * its location. - */ -struct hwspinlock_pdata { - int base_id; -}; - #ifdef CONFIG_HWSPINLOCK int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev, diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index a26fb8e7cedf..964f1be8150c 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1304,6 +1304,8 @@ static inline void *hv_get_drvdata(struct hv_device *dev) struct device *hv_get_vmbus_root_device(void); +bool hv_vmbus_exists(void); + struct hv_ring_buffer_debug_info { u32 current_interrupt_mask; u32 current_read_index; diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index e6272f9c5e42..20cc16ea4e5a 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -147,11 +147,13 @@ extern __be16 vlan_dev_vlan_proto(const struct net_device *dev); * @priority: skb priority * @vlan_qos: vlan priority: (skb->priority << 13) & 0xE000 * @next: pointer to next struct + * @rcu: used for deferred freeing of mapping nodes */ struct vlan_priority_tci_mapping { u32 priority; u16 vlan_qos; - struct vlan_priority_tci_mapping *next; + struct vlan_priority_tci_mapping __rcu *next; + struct rcu_head rcu; }; struct proc_dir_entry; @@ -177,7 +179,7 @@ struct vlan_dev_priv { unsigned int nr_ingress_mappings; u32 ingress_priority_map[8]; unsigned int nr_egress_mappings; - struct vlan_priority_tci_mapping *egress_priority_map[16]; + struct vlan_priority_tci_mapping __rcu *egress_priority_map[16]; __be16 vlan_proto; u16 vlan_id; @@ -209,19 +211,24 @@ static inline u16 vlan_dev_get_egress_qos_mask(struct net_device *dev, u32 skprio) { struct vlan_priority_tci_mapping *mp; + u16 vlan_qos = 0; - smp_rmb(); /* coupled with smp_wmb() in vlan_dev_set_egress_priority() */ + rcu_read_lock(); - mp = vlan_dev_priv(dev)->egress_priority_map[(skprio & 0xF)]; + mp = rcu_dereference(vlan_dev_priv(dev)->egress_priority_map[skprio & 0xF]); while (mp) { if (mp->priority == skprio) { - return mp->vlan_qos; /* This should already be shifted - * to mask correctly with the - * VLAN's TCI */ + vlan_qos = READ_ONCE(mp->vlan_qos); + break; } - mp = mp->next; + mp = rcu_dereference(mp->next); } - return 0; + rcu_read_unlock(); + + /* This should already be shifted to mask correctly with + * the VLAN's TCI. + */ + return vlan_qos; } extern bool vlan_do_receive(struct sk_buff **skb); diff --git a/include/linux/iio/adc/qcom-adc5-gen3-common.h b/include/linux/iio/adc/qcom-adc5-gen3-common.h new file mode 100644 index 000000000000..6303eaa6640b --- /dev/null +++ b/include/linux/iio/adc/qcom-adc5-gen3-common.h @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * Code used in the main and auxiliary Qualcomm PMIC voltage ADCs + * of type ADC5 Gen3. + */ + +#ifndef QCOM_ADC5_GEN3_COMMON_H +#define QCOM_ADC5_GEN3_COMMON_H + +#include +#include +#include +#include +#include +#include +#include + +#define ADC5_GEN3_HS 0x45 +#define ADC5_GEN3_HS_BUSY BIT(7) +#define ADC5_GEN3_HS_READY BIT(0) + +#define ADC5_GEN3_STATUS1 0x46 +#define ADC5_GEN3_STATUS1_CONV_FAULT BIT(7) +#define ADC5_GEN3_STATUS1_THR_CROSS BIT(6) +#define ADC5_GEN3_STATUS1_EOC BIT(0) + +#define ADC5_GEN3_TM_EN_STS 0x47 +#define ADC5_GEN3_TM_HIGH_STS 0x48 +#define ADC5_GEN3_TM_LOW_STS 0x49 + +#define ADC5_GEN3_EOC_STS 0x4a +#define ADC5_GEN3_EOC_CHAN_0 BIT(0) + +#define ADC5_GEN3_EOC_CLR 0x4b +#define ADC5_GEN3_TM_HIGH_STS_CLR 0x4c +#define ADC5_GEN3_TM_LOW_STS_CLR 0x4d +#define ADC5_GEN3_CONV_ERR_CLR 0x4e +#define ADC5_GEN3_CONV_ERR_CLR_REQ BIT(0) + +#define ADC5_GEN3_SID 0x4f +#define ADC5_GEN3_SID_MASK GENMASK(3, 0) + +#define ADC5_GEN3_PERPH_CH 0x50 +#define ADC5_GEN3_CHAN_CONV_REQ BIT(7) + +#define ADC5_GEN3_TIMER_SEL 0x51 +#define ADC5_GEN3_TIME_IMMEDIATE 0x1 + +#define ADC5_GEN3_DIG_PARAM 0x52 +#define ADC5_GEN3_DIG_PARAM_CAL_SEL_MASK GENMASK(5, 4) +#define ADC5_GEN3_DIG_PARAM_DEC_RATIO_SEL_MASK GENMASK(3, 2) + +#define ADC5_GEN3_FAST_AVG 0x53 +#define ADC5_GEN3_FAST_AVG_CTL_EN BIT(7) +#define ADC5_GEN3_FAST_AVG_CTL_SAMPLES_MASK GENMASK(2, 0) + +#define ADC5_GEN3_ADC_CH_SEL_CTL 0x54 +#define ADC5_GEN3_DELAY_CTL 0x55 +#define ADC5_GEN3_HW_SETTLE_DELAY_MASK GENMASK(3, 0) + +#define ADC5_GEN3_CH_EN 0x56 +#define ADC5_GEN3_HIGH_THR_INT_EN BIT(1) +#define ADC5_GEN3_LOW_THR_INT_EN BIT(0) + +#define ADC5_GEN3_LOW_THR0 0x57 +#define ADC5_GEN3_LOW_THR1 0x58 +#define ADC5_GEN3_HIGH_THR0 0x59 +#define ADC5_GEN3_HIGH_THR1 0x5a + +#define ADC5_GEN3_CH_DATA0(channel) (0x5c + (channel) * 2) +#define ADC5_GEN3_CH_DATA1(channel) (0x5d + (channel) * 2) + +#define ADC5_GEN3_CONV_REQ 0xe5 +#define ADC5_GEN3_CONV_REQ_REQ BIT(0) + +#define ADC5_GEN3_VIRTUAL_SID_MASK GENMASK(15, 8) +#define ADC5_GEN3_CHANNEL_MASK GENMASK(7, 0) +#define ADC5_GEN3_V_CHAN(x) \ + (FIELD_PREP(ADC5_GEN3_VIRTUAL_SID_MASK, (x).sid) | (x).channel) + +/* ADC channels for PMIC5 Gen3 */ +#define ADC5_GEN3_REF_GND 0x00 +#define ADC5_GEN3_1P25VREF 0x01 +#define ADC5_GEN3_DIE_TEMP 0x03 +#define ADC5_GEN3_USB_SNS_V_16 0x11 +#define ADC5_GEN3_VIN_DIV16_MUX 0x12 +#define ADC5_GEN3_VPH_PWR 0x8e +#define ADC5_GEN3_VBAT_SNS_QBG 0x8f +/* 100k pull-up channels */ +#define ADC5_GEN3_AMUX1_THM_100K_PU 0x44 +#define ADC5_GEN3_AMUX2_THM_100K_PU 0x45 +#define ADC5_GEN3_AMUX3_THM_100K_PU 0x46 +#define ADC5_GEN3_AMUX4_THM_100K_PU 0x47 +#define ADC5_GEN3_AMUX5_THM_100K_PU 0x48 +#define ADC5_GEN3_AMUX6_THM_100K_PU 0x49 +#define ADC5_GEN3_AMUX1_GPIO_100K_PU 0x4a +#define ADC5_GEN3_AMUX2_GPIO_100K_PU 0x4b +#define ADC5_GEN3_AMUX3_GPIO_100K_PU 0x4c +#define ADC5_GEN3_AMUX4_GPIO_100K_PU 0x4d + +#define ADC5_MAX_CHANNEL 0xc0 + +enum adc5_cal_method { + ADC5_NO_CAL = 0, + ADC5_RATIOMETRIC_CAL, + ADC5_ABSOLUTE_CAL, +}; + +enum adc5_time_select { + MEAS_INT_DISABLE = 0, + MEAS_INT_IMMEDIATE, + MEAS_INT_50MS, + MEAS_INT_100MS, + MEAS_INT_1S, + MEAS_INT_NONE, +}; + +/** + * struct adc5_sdam_data - data per SDAM allocated for adc usage + * @base_addr: base address for the ADC SDAM peripheral. + * @irq_name: ADC IRQ name. + * @irq: ADC IRQ number. + */ +struct adc5_sdam_data { + u16 base_addr; + const char *irq_name; + int irq; +}; + +/** + * struct adc5_device_data - Top-level ADC device data + * @regmap: ADC peripheral register map field. + * @base: array of SDAM data. + * @num_sdams: number of ADC SDAM peripherals. + */ +struct adc5_device_data { + struct regmap *regmap; + struct adc5_sdam_data *base; + int num_sdams; +}; + +/** + * struct adc5_channel_common_prop - ADC channel properties (common to ADC and TM). + * @channel: channel number, refer to the channel list. + * @cal_method: calibration method. + * @decimation: sampling rate supported for the channel. + * @sid: ID of PMIC owning the channel. + * @label: Channel name used in device tree. + * @prescale: channel scaling performed on the input signal. + * @hw_settle_time_us: the time between AMUX being configured and the + * start of conversion in uS. + * @avg_samples: ability to provide single result from the ADC + * that is an average of multiple measurements. + * @scale_fn_type: Represents the scaling function to convert voltage + * physical units desired by the client for the channel. + */ +struct adc5_channel_common_prop { + unsigned int channel; + enum adc5_cal_method cal_method; + unsigned int decimation; + unsigned int sid; + const char *label; + unsigned int prescale; + unsigned int hw_settle_time_us; + unsigned int avg_samples; + enum vadc_scale_fn_type scale_fn_type; +}; + +/** + * struct tm5_aux_dev_wrapper - wrapper structure around TM auxiliary device + * @aux_dev: TM auxiliary device structure. + * @dev_data: Top-level ADC device data. + * @tm_props: Array of common ADC channel properties for TM channels. + * @n_tm_channels: number of TM channels. + */ +struct tm5_aux_dev_wrapper { + struct auxiliary_device aux_dev; + struct adc5_device_data *dev_data; + struct adc5_channel_common_prop *tm_props; + unsigned int n_tm_channels; +}; + +int adc5_gen3_read(struct adc5_device_data *adc, unsigned int sdam_index, + u16 offset, u8 *data, int len); + +int adc5_gen3_write(struct adc5_device_data *adc, unsigned int sdam_index, + u16 offset, u8 *data, int len); + +int adc5_gen3_poll_wait_hs(struct adc5_device_data *adc, + unsigned int sdam_index); + +void adc5_gen3_update_dig_param(struct adc5_channel_common_prop *prop, + u8 *data); + +int adc5_gen3_status_clear(struct adc5_device_data *adc, + int sdam_index, u16 offset, u8 *val, int len); + +void adc5_gen3_mutex_lock(struct device *dev); +void adc5_gen3_mutex_unlock(struct device *dev); +int adc5_gen3_get_scaled_reading(struct device *dev, + struct adc5_channel_common_prop *common_props, + int *val); +int adc5_gen3_therm_code_to_temp(struct device *dev, + struct adc5_channel_common_prop *common_props, + u16 code, int *val); +void adc5_gen3_register_tm_event_notifier(struct device *dev, + void (*handler)(struct auxiliary_device *)); + +#endif /* QCOM_ADC5_GEN3_COMMON_H */ diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h index 7f815f3fed6a..4d15c2a9802c 100644 --- a/include/linux/iio/backend.h +++ b/include/linux/iio/backend.h @@ -84,6 +84,27 @@ enum iio_backend_filter_type { IIO_BACKEND_FILTER_TYPE_MAX }; +/** + * enum iio_backend_capabilities - Backend capabilities + * Backend capabilities can be used by frontends to check if a given + * functionality is supported by the backend. This is useful for frontend + * devices which are expected to work with alternative backend + * implementations. Capabilities are loosely coupled with operations, + * meaning that a capability requires certain operations to be implemented + * by the backend. A capability might be mapped to a single operation or + * multiple operations. + * + * @IIO_BACKEND_CAP_CALIBRATION: Backend supports digital interface + * calibration. Calibration procedure is device specific. + * @IIO_BACKEND_CAP_BUFFER: Support for IIO buffer interface. + * @IIO_BACKEND_CAP_ENABLE: Backend can be explicitly enabled/disabled. + */ +enum iio_backend_capabilities { + IIO_BACKEND_CAP_CALIBRATION = BIT(0), + IIO_BACKEND_CAP_BUFFER = BIT(1), + IIO_BACKEND_CAP_ENABLE = BIT(2), +}; + /** * struct iio_backend_ops - operations structure for an iio_backend * @enable: Enable backend. @@ -179,10 +200,12 @@ struct iio_backend_ops { * struct iio_backend_info - info structure for an iio_backend * @name: Backend name. * @ops: Backend operations. + * @caps: Backend capabilities. (bitmask of enum iio_backend_capabilities). */ struct iio_backend_info { const char *name; const struct iio_backend_ops *ops; + u32 caps; }; int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan); @@ -235,6 +258,7 @@ int iio_backend_read_raw(struct iio_backend *back, long mask); int iio_backend_extend_chan_spec(struct iio_backend *back, struct iio_chan_spec *chan); +bool iio_backend_has_caps(struct iio_backend *back, u32 caps); void *iio_backend_get_priv(const struct iio_backend *conv); struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name); struct iio_backend *devm_iio_backend_fwnode_get(struct device *dev, diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index f9ae5cdd884f..1ba496f0fea5 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -160,12 +160,12 @@ struct st_sensor_int_drdy { /** * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt - * struct int1 - data-ready configuration register for INT1 pin. - * struct int2 - data-ready configuration register for INT2 pin. + * @int1: data-ready configuration register for INT1 pin. + * @int2: data-ready configuration register for INT2 pin. * @addr_ihl: address to enable/disable active low on the INT lines. * @mask_ihl: mask to enable/disable active low on the INT lines. - * struct stat_drdy - status register of DRDY (data ready) interrupt. - * struct ig1 - represents the Interrupt Generator 1 of sensors. + * @stat_drdy: status register of DRDY (data ready) interrupt. + * @ig1: represents the Interrupt Generator 1 of sensors. * @en_addr: address of the enable ig1 register. * @en_mask: mask to write the on/off value for enable. */ @@ -190,6 +190,7 @@ struct st_sensor_data_ready_irq { * @wai_addr: The address of WhoAmI register. * @sensors_supported: List of supported sensors by struct itself. * @ch: IIO channels for the sensor. + * @num_ch: Number of IIO channels in @ch * @odr: Output data rate register and ODR list available. * @pw: Power register of the sensor. * @enable_axis: Enable one or more axis of the sensor. @@ -228,7 +229,7 @@ struct st_sensor_settings { * @regmap: Pointer to specific sensor regmap configuration. * @enabled: Status of the sensor (false->off, true->on). * @odr: Output data rate of the sensor [Hz]. - * num_data_channels: Number of data channels used in buffer. + * @num_data_channels: Number of data channels used in buffer. * @drdy_int_pin: Redirect DRDY on pin 1 (1) or pin 2 (2). * @int_pin_open_drain: Set the interrupt/DRDY to open drain. * @irq: the IRQ number. diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 34eebad12d2c..4e3099defc1d 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -21,6 +21,7 @@ enum iio_event_info { IIO_EV_INFO_TAP2_MIN_DELAY, IIO_EV_INFO_RUNNING_PERIOD, IIO_EV_INFO_RUNNING_COUNT, + IIO_EV_INFO_SCALE, }; #define IIO_VAL_INT 1 diff --git a/include/linux/ima.h b/include/linux/ima.h index abf8923f8fc5..8e08baf16c2f 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -11,6 +11,7 @@ #include #include #include +#include #include struct linux_binprm; @@ -73,14 +74,8 @@ int ima_validate_range(phys_addr_t phys, size_t size); #endif #ifdef CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT -extern bool arch_ima_get_secureboot(void); extern const char * const *arch_get_ima_policy(void); #else -static inline bool arch_ima_get_secureboot(void) -{ - return false; -} - static inline const char * const *arch_get_ima_policy(void) { return NULL; diff --git a/include/linux/input.h b/include/linux/input.h index 7d7cb0593a63..06ca62328db1 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -517,6 +517,10 @@ INPUT_GENERATE_ABS_ACCESSORS(res, resolution) int input_scancode_to_scalar(const struct input_keymap_entry *ke, unsigned int *scancode); +int input_default_setkeycode(struct input_dev *dev, + const struct input_keymap_entry *ke, + unsigned int *old_keycode); + int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke); int input_set_keycode(struct input_dev *dev, const struct input_keymap_entry *ke); diff --git a/include/linux/intel_tpmi.h b/include/linux/intel_tpmi.h index 94c06bf214fb..15f02422e9ca 100644 --- a/include/linux/intel_tpmi.h +++ b/include/linux/intel_tpmi.h @@ -28,6 +28,12 @@ enum intel_tpmi_id { TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */ }; +#define TPMI_CORE_INIT 0 +#define TPMI_CORE_EXIT 1 + +int tpmi_register_notifier(struct notifier_block *nb); +int tpmi_unregister_notifier(struct notifier_block *nb); + struct oobmsm_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev); struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index); int tpmi_get_resource_count(struct auxiliary_device *auxdev); diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 1a0f357c2427..1fe5665a9d02 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -29,9 +29,15 @@ #define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) #define TABLE_OFFSET_SHIFT 3 +struct device; struct pci_dev; struct resource; +enum intel_vsec_disc_source { + INTEL_VSEC_DISC_PCI, /* PCI, default */ + INTEL_VSEC_DISC_ACPI, /* ACPI */ +}; + enum intel_vsec_id { VSEC_ID_TELEMETRY = 2, VSEC_ID_WATCHER = 3, @@ -82,14 +88,14 @@ enum intel_vsec_quirks { * struct pmt_callbacks - Callback infrastructure for PMT devices * @read_telem: when specified, called by client driver to access PMT * data (instead of direct copy). - * * pdev: PCI device reference for the callback's use + * * dev: device reference for the callback's use * * guid: ID of data to acccss * * data: buffer for the data to be copied * * off: offset into the requested buffer * * count: size of buffer */ struct pmt_callbacks { - int (*read_telem)(struct pci_dev *pdev, u32 guid, u64 *data, loff_t off, u32 count); + int (*read_telem)(struct device *dev, u32 guid, u64 *data, loff_t off, u32 count); }; struct vsec_feature_dependency { @@ -102,6 +108,10 @@ struct vsec_feature_dependency { * @parent: parent device in the auxbus chain * @headers: list of headers to define the PMT client devices to create * @deps: array of feature dependencies + * @acpi_disc: ACPI discovery tables, each entry is two QWORDs + * in little-endian format as defined by the PMT ACPI spec. + * Valid only when @provider == INTEL_VSEC_DISC_ACPI. + * @src: source of discovery table data * @priv_data: private data, usable by parent devices, currently a callback * @caps: bitmask of PMT capabilities for the given headers * @quirks: bitmask of VSEC device quirks @@ -112,6 +122,8 @@ struct intel_vsec_platform_info { struct device *parent; struct intel_vsec_header **headers; const struct vsec_feature_dependency *deps; + u32 (*acpi_disc)[4]; + enum intel_vsec_disc_source src; void *priv_data; unsigned long caps; unsigned long quirks; @@ -122,8 +134,13 @@ struct intel_vsec_platform_info { /** * struct intel_vsec_device - Auxbus specific device information * @auxdev: auxbus device struct for auxbus access - * @pcidev: pci device associated with the device - * @resource: any resources shared by the parent + * @dev: struct device associated with the device + * @resource: PCI discovery resources (BAR windows), one per discovery + * instance. Valid only when @src == INTEL_VSEC_DISC_PCI + * @acpi_disc: ACPI discovery tables, each entry is two QWORDs + * in little-endian format as defined by the PMT ACPI spec. + * Valid only when @src == INTEL_VSEC_DISC_ACPI. + * @src: source of discovery table data * @ida: id reference * @num_resources: number of resources * @id: xarray id @@ -135,8 +152,10 @@ struct intel_vsec_platform_info { */ struct intel_vsec_device { struct auxiliary_device auxdev; - struct pci_dev *pcidev; + struct device *dev; struct resource *resource; + u32 (*acpi_disc)[4]; + enum intel_vsec_disc_source src; struct ida *ida; int num_resources; int id; /* xa */ @@ -184,7 +203,7 @@ struct pmt_feature_group { struct telemetry_region regions[]; }; -int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, +int intel_vsec_add_aux(struct device *parent, struct intel_vsec_device *intel_vsec_dev, const char *name); @@ -199,14 +218,14 @@ static inline struct intel_vsec_device *auxdev_to_ivdev(struct auxiliary_device } #if IS_ENABLED(CONFIG_INTEL_VSEC) -int intel_vsec_register(struct pci_dev *pdev, - struct intel_vsec_platform_info *info); +int intel_vsec_register(struct device *dev, + const struct intel_vsec_platform_info *info); int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, struct intel_vsec_device *vsec_dev); struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev); #else -static inline int intel_vsec_register(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) +static inline int intel_vsec_register(struct device *dev, + const struct intel_vsec_platform_info *info) { return -ENODEV; } diff --git a/include/linux/irq-entry-common.h b/include/linux/irq-entry-common.h index 167fba7dbf04..1fabf0f5ea8e 100644 --- a/include/linux/irq-entry-common.h +++ b/include/linux/irq-entry-common.h @@ -218,14 +218,6 @@ static __always_inline void __exit_to_user_mode_validate(void) lockdep_sys_exit(); } -/* Temporary workaround to keep ARM64 alive */ -static __always_inline void exit_to_user_mode_prepare_legacy(struct pt_regs *regs) -{ - __exit_to_user_mode_prepare(regs, EXIT_TO_USER_MODE_WORK); - rseq_exit_to_user_mode_legacy(); - __exit_to_user_mode_validate(); -} - /** * syscall_exit_to_user_mode_prepare - call exit_to_user_mode_loop() if required * @regs: Pointer to pt_regs on entry stack diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h index b78488df6c98..f78787e654f4 100644 --- a/include/linux/irqchip/arm-gic-v5.h +++ b/include/linux/irqchip/arm-gic-v5.h @@ -24,6 +24,28 @@ #define GICV5_HWIRQ_TYPE_LPI UL(0x2) #define GICV5_HWIRQ_TYPE_SPI UL(0x3) +/* + * Architected PPIs + */ +#define GICV5_ARCH_PPI_S_DB_PPI 0x0 +#define GICV5_ARCH_PPI_RL_DB_PPI 0x1 +#define GICV5_ARCH_PPI_NS_DB_PPI 0x2 +#define GICV5_ARCH_PPI_SW_PPI 0x3 +#define GICV5_ARCH_PPI_HACDBSIRQ 0xf +#define GICV5_ARCH_PPI_CNTHVS 0x13 +#define GICV5_ARCH_PPI_CNTHPS 0x14 +#define GICV5_ARCH_PPI_PMBIRQ 0x15 +#define GICV5_ARCH_PPI_COMMIRQ 0x16 +#define GICV5_ARCH_PPI_PMUIRQ 0x17 +#define GICV5_ARCH_PPI_CTIIRQ 0x18 +#define GICV5_ARCH_PPI_GICMNT 0x19 +#define GICV5_ARCH_PPI_CNTHP 0x1a +#define GICV5_ARCH_PPI_CNTV 0x1b +#define GICV5_ARCH_PPI_CNTHV 0x1c +#define GICV5_ARCH_PPI_CNTPS 0x1d +#define GICV5_ARCH_PPI_CNTP 0x1e +#define GICV5_ARCH_PPI_TRBIRQ 0x1f + /* * Tables attributes */ @@ -365,6 +387,11 @@ int gicv5_spi_irq_set_type(struct irq_data *d, unsigned int type); int gicv5_irs_iste_alloc(u32 lpi); void gicv5_irs_syncr(void); +/* Embedded in kvm.arch */ +struct gicv5_vpe { + bool resident; +}; + struct gicv5_its_devtab_cfg { union { struct { @@ -398,9 +425,6 @@ struct gicv5_its_itt_cfg { void gicv5_init_lpis(u32 max); void gicv5_deinit_lpis(void); -int gicv5_alloc_lpi(void); -void gicv5_free_lpi(u32 lpi); - void __init gicv5_its_of_probe(struct device_node *parent); void __init gicv5_its_acpi_probe(void); #endif diff --git a/include/linux/isdn/capilli.h b/include/linux/isdn/capilli.h deleted file mode 100644 index 12be09b6883b..000000000000 --- a/include/linux/isdn/capilli.h +++ /dev/null @@ -1,95 +0,0 @@ -/* $Id: capilli.h,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ - * - * Kernel CAPI 2.0 Driver Interface for Linux - * - * Copyright 1999 by Carsten Paeth - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#ifndef __CAPILLI_H__ -#define __CAPILLI_H__ - -#include -#include -#include -#include - -typedef struct capiloaddatapart { - int user; /* data in userspace ? */ - int len; - unsigned char *data; -} capiloaddatapart; - -typedef struct capiloaddata { - capiloaddatapart firmware; - capiloaddatapart configuration; -} capiloaddata; - -typedef struct capicardparams { - unsigned int port; - unsigned irq; - int cardtype; - int cardnr; - unsigned int membase; -} capicardparams; - -struct capi_ctr { - /* filled in before calling attach_capi_ctr */ - struct module *owner; - void *driverdata; /* driver specific */ - char name[32]; /* name of controller */ - char *driver_name; /* name of driver */ - int (*load_firmware)(struct capi_ctr *, capiloaddata *); - void (*reset_ctr)(struct capi_ctr *); - void (*register_appl)(struct capi_ctr *, u16 appl, - capi_register_params *); - void (*release_appl)(struct capi_ctr *, u16 appl); - u16 (*send_message)(struct capi_ctr *, struct sk_buff *skb); - - char *(*procinfo)(struct capi_ctr *); - int (*proc_show)(struct seq_file *, void *); - - /* filled in before calling ready callback */ - u8 manu[CAPI_MANUFACTURER_LEN]; /* CAPI_GET_MANUFACTURER */ - capi_version version; /* CAPI_GET_VERSION */ - capi_profile profile; /* CAPI_GET_PROFILE */ - u8 serial[CAPI_SERIAL_LEN]; /* CAPI_GET_SERIAL */ - - /* management information for kcapi */ - - unsigned long nrecvctlpkt; - unsigned long nrecvdatapkt; - unsigned long nsentctlpkt; - unsigned long nsentdatapkt; - - int cnr; /* controller number */ - unsigned short state; /* controller state */ - int blocked; /* output blocked */ - int traceflag; /* capi trace */ - - struct proc_dir_entry *procent; - char procfn[128]; -}; - -int attach_capi_ctr(struct capi_ctr *); -int detach_capi_ctr(struct capi_ctr *); - -void capi_ctr_ready(struct capi_ctr * card); -void capi_ctr_down(struct capi_ctr * card); -void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *skb); - -// --------------------------------------------------------------------------- -// needed for AVM capi drivers - -struct capi_driver { - char name[32]; /* driver name */ - char revision[32]; - - /* management information for kcapi */ - struct list_head list; -}; - -#endif /* __CAPILLI_H__ */ diff --git a/include/linux/isdn/capiutil.h b/include/linux/isdn/capiutil.h deleted file mode 100644 index 953fd500dff7..000000000000 --- a/include/linux/isdn/capiutil.h +++ /dev/null @@ -1,60 +0,0 @@ -/* $Id: capiutil.h,v 1.5.6.2 2001/09/23 22:24:33 kai Exp $ - * - * CAPI 2.0 defines & types - * - * From CAPI 2.0 Development Kit AVM 1995 (msg.c) - * Rewritten for Linux 1996 by Carsten Paeth - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#ifndef __CAPIUTIL_H__ -#define __CAPIUTIL_H__ - -#include - -#define CAPIMSG_BASELEN 8 -#define CAPIMSG_U8(m, off) (m[off]) -#define CAPIMSG_U16(m, off) (m[off]|(m[(off)+1]<<8)) -#define CAPIMSG_U32(m, off) (m[off]|(m[(off)+1]<<8)|(m[(off)+2]<<16)|(m[(off)+3]<<24)) -#define CAPIMSG_LEN(m) CAPIMSG_U16(m,0) -#define CAPIMSG_APPID(m) CAPIMSG_U16(m,2) -#define CAPIMSG_COMMAND(m) CAPIMSG_U8(m,4) -#define CAPIMSG_SUBCOMMAND(m) CAPIMSG_U8(m,5) -#define CAPIMSG_CMD(m) (((m[4])<<8)|(m[5])) -#define CAPIMSG_MSGID(m) CAPIMSG_U16(m,6) -#define CAPIMSG_CONTROLLER(m) (m[8] & 0x7f) -#define CAPIMSG_CONTROL(m) CAPIMSG_U32(m, 8) -#define CAPIMSG_NCCI(m) CAPIMSG_CONTROL(m) -#define CAPIMSG_DATALEN(m) CAPIMSG_U16(m,16) /* DATA_B3_REQ */ - -static inline void capimsg_setu8(void *m, int off, __u8 val) -{ - ((__u8 *)m)[off] = val; -} - -static inline void capimsg_setu16(void *m, int off, __u16 val) -{ - ((__u8 *)m)[off] = val & 0xff; - ((__u8 *)m)[off+1] = (val >> 8) & 0xff; -} - -static inline void capimsg_setu32(void *m, int off, __u32 val) -{ - ((__u8 *)m)[off] = val & 0xff; - ((__u8 *)m)[off+1] = (val >> 8) & 0xff; - ((__u8 *)m)[off+2] = (val >> 16) & 0xff; - ((__u8 *)m)[off+3] = (val >> 24) & 0xff; -} - -#define CAPIMSG_SETLEN(m, len) capimsg_setu16(m, 0, len) -#define CAPIMSG_SETAPPID(m, applid) capimsg_setu16(m, 2, applid) -#define CAPIMSG_SETCOMMAND(m,cmd) capimsg_setu8(m, 4, cmd) -#define CAPIMSG_SETSUBCOMMAND(m, cmd) capimsg_setu8(m, 5, cmd) -#define CAPIMSG_SETMSGID(m, msgid) capimsg_setu16(m, 6, msgid) -#define CAPIMSG_SETCONTROL(m, contr) capimsg_setu32(m, 8, contr) -#define CAPIMSG_SETDATALEN(m, len) capimsg_setu16(m, 16, len) - -#endif /* __CAPIUTIL_H__ */ diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index a53a00d36228..7e785aa6d35d 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -429,22 +429,46 @@ struct jbd2_inode { unsigned long i_flags; /** - * @i_dirty_start: + * @i_dirty_start_page: + * + * Dirty range start in PAGE_SIZE units. + * + * The dirty range is empty if @i_dirty_start_page is greater than or + * equal to @i_dirty_end_page. * - * Offset in bytes where the dirty range for this inode starts. * [j_list_lock] */ - loff_t i_dirty_start; + pgoff_t i_dirty_start_page; /** - * @i_dirty_end: + * @i_dirty_end_page: * - * Inclusive offset in bytes where the dirty range for this inode - * ends. [j_list_lock] + * Dirty range end in PAGE_SIZE units (exclusive). + * + * [j_list_lock] */ - loff_t i_dirty_end; + pgoff_t i_dirty_end_page; }; +/* + * Lockless readers treat start_page >= end_page as an empty range. + * Writers publish a new non-empty range by storing i_dirty_end_page before + * i_dirty_start_page. + */ +static inline bool jbd2_jinode_get_dirty_range(const struct jbd2_inode *jinode, + loff_t *start, loff_t *end) +{ + pgoff_t start_page = READ_ONCE(jinode->i_dirty_start_page); + pgoff_t end_page = READ_ONCE(jinode->i_dirty_end_page); + + if (start_page >= end_page) + return false; + + *start = (loff_t)start_page << PAGE_SHIFT; + *end = ((loff_t)end_page << PAGE_SHIFT) - 1; + return true; +} + struct jbd2_revoke_table_s; /** diff --git a/include/linux/kernelcapi.h b/include/linux/kernelcapi.h deleted file mode 100644 index 94ba42bf9da1..000000000000 --- a/include/linux/kernelcapi.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * $Id: kernelcapi.h,v 1.8.6.2 2001/02/07 11:31:31 kai Exp $ - * - * Kernel CAPI 2.0 Interface for Linux - * - * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) - * - */ -#ifndef __KERNELCAPI_H__ -#define __KERNELCAPI_H__ - -#include -#include -#include -#include -#include - -#define CAPI_NOERROR 0x0000 - -#define CAPI_TOOMANYAPPLS 0x1001 -#define CAPI_LOGBLKSIZETOSMALL 0x1002 -#define CAPI_BUFFEXECEEDS64K 0x1003 -#define CAPI_MSGBUFSIZETOOSMALL 0x1004 -#define CAPI_ANZLOGCONNNOTSUPPORTED 0x1005 -#define CAPI_REGRESERVED 0x1006 -#define CAPI_REGBUSY 0x1007 -#define CAPI_REGOSRESOURCEERR 0x1008 -#define CAPI_REGNOTINSTALLED 0x1009 -#define CAPI_REGCTRLERNOTSUPPORTEXTEQUIP 0x100a -#define CAPI_REGCTRLERONLYSUPPORTEXTEQUIP 0x100b - -#define CAPI_ILLAPPNR 0x1101 -#define CAPI_ILLCMDORSUBCMDORMSGTOSMALL 0x1102 -#define CAPI_SENDQUEUEFULL 0x1103 -#define CAPI_RECEIVEQUEUEEMPTY 0x1104 -#define CAPI_RECEIVEOVERFLOW 0x1105 -#define CAPI_UNKNOWNNOTPAR 0x1106 -#define CAPI_MSGBUSY 0x1107 -#define CAPI_MSGOSRESOURCEERR 0x1108 -#define CAPI_MSGNOTINSTALLED 0x1109 -#define CAPI_MSGCTRLERNOTSUPPORTEXTEQUIP 0x110a -#define CAPI_MSGCTRLERONLYSUPPORTEXTEQUIP 0x110b - -#endif /* __KERNELCAPI_H__ */ diff --git a/include/linux/kexec_handover.h b/include/linux/kexec_handover.h index ac4129d1d741..8968c56d2d73 100644 --- a/include/linux/kexec_handover.h +++ b/include/linux/kexec_handover.h @@ -32,9 +32,9 @@ void kho_restore_free(void *mem); struct folio *kho_restore_folio(phys_addr_t phys); struct page *kho_restore_pages(phys_addr_t phys, unsigned long nr_pages); void *kho_restore_vmalloc(const struct kho_vmalloc *preservation); -int kho_add_subtree(const char *name, void *fdt); -void kho_remove_subtree(void *fdt); -int kho_retrieve_subtree(const char *name, phys_addr_t *phys); +int kho_add_subtree(const char *name, void *blob, size_t size); +void kho_remove_subtree(void *blob); +int kho_retrieve_subtree(const char *name, phys_addr_t *phys, size_t *size); void kho_memory_init(void); @@ -97,14 +97,15 @@ static inline void *kho_restore_vmalloc(const struct kho_vmalloc *preservation) return NULL; } -static inline int kho_add_subtree(const char *name, void *fdt) +static inline int kho_add_subtree(const char *name, void *blob, size_t size) { return -EOPNOTSUPP; } -static inline void kho_remove_subtree(void *fdt) { } +static inline void kho_remove_subtree(void *blob) { } -static inline int kho_retrieve_subtree(const char *name, phys_addr_t *phys) +static inline int kho_retrieve_subtree(const char *name, phys_addr_t *phys, + size_t *size) { return -EOPNOTSUPP; } diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h index 22b3f3839f30..6c46591a2eac 100644 --- a/include/linux/kgdb.h +++ b/include/linux/kgdb.h @@ -202,9 +202,10 @@ extern void kgdb_call_nmi_hook(void *ignored); * * On SMP systems, we need to get the attention of the other CPUs * and get them into a known state. This should do what is needed - * to get the other CPUs to call kgdb_wait(). Note that on some arches, - * the NMI approach is not used for rounding up all the CPUs. Normally - * those architectures can just not implement this and get the default. + * to get the other CPUs to call kgdb_handle_exception(). Note that + * on some arches, the NMI approach is not used for rounding up all + * the CPUs. Normally those architectures can just not implement + * this and get the default. * * On non-SMP systems, this is not called. */ diff --git a/include/linux/kho/abi/kexec_handover.h b/include/linux/kho/abi/kexec_handover.h index 6b7d8ef550f9..7e847a2339b0 100644 --- a/include/linux/kho/abi/kexec_handover.h +++ b/include/linux/kho/abi/kexec_handover.h @@ -41,25 +41,28 @@ * restore the preserved data.:: * * / { - * compatible = "kho-v2"; + * compatible = "kho-v3"; * * preserved-memory-map = <0x...>; * * { * preserved-data = <0x...>; + * blob-size = <0x...>; * }; * * { * preserved-data = <0x...>; + * blob-size = <0x...>; * }; * ... ... * { * preserved-data = <0x...>; + * blob-size = <0x...>; * }; * }; * * Root KHO Node (/): - * - compatible: "kho-v2" + * - compatible: "kho-v3" * * Indentifies the overall KHO ABI version. * @@ -78,16 +81,25 @@ * * Physical address pointing to a subnode data blob that is also * being preserved. + * + * - blob-size: u64 + * + * Size in bytes of the preserved data blob. This is needed because + * blobs may use arbitrary formats (not just FDT), so the size + * cannot be determined from the blob content alone. */ /* The compatible string for the KHO FDT root node. */ -#define KHO_FDT_COMPATIBLE "kho-v2" +#define KHO_FDT_COMPATIBLE "kho-v3" /* The FDT property for the preserved memory map. */ #define KHO_FDT_MEMORY_MAP_PROP_NAME "preserved-memory-map" /* The FDT property for preserved data blobs. */ -#define KHO_FDT_SUB_TREE_PROP_NAME "preserved-data" +#define KHO_SUB_TREE_PROP_NAME "preserved-data" + +/* The FDT property for the size of preserved data blobs. */ +#define KHO_SUB_TREE_SIZE_PROP_NAME "blob-size" /** * DOC: Kexec Handover ABI for vmalloc Preservation diff --git a/include/linux/kho/abi/kexec_metadata.h b/include/linux/kho/abi/kexec_metadata.h new file mode 100644 index 000000000000..e9e3f7e38a7c --- /dev/null +++ b/include/linux/kho/abi/kexec_metadata.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/** + * DOC: Kexec Metadata ABI + * + * The "kexec-metadata" subtree stores optional metadata about the kexec chain. + * It is registered via kho_add_subtree(), keeping it independent from the core + * KHO ABI. This allows the metadata format to evolve without affecting other + * KHO consumers. + * + * The metadata is stored as a plain C struct rather than FDT format for + * simplicity and direct field access. + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + * Copyright (c) 2026 Breno Leitao + */ + +#ifndef _LINUX_KHO_ABI_KEXEC_METADATA_H +#define _LINUX_KHO_ABI_KEXEC_METADATA_H + +#include +#include + +#define KHO_KEXEC_METADATA_VERSION 1 + +/** + * struct kho_kexec_metadata - Kexec metadata passed between kernels + * @version: ABI version of this struct (must be first field) + * @previous_release: Kernel version string that initiated the kexec + * @kexec_count: Number of kexec boots since last cold boot + * + * This structure is preserved across kexec and allows the new kernel to + * identify which kernel it was booted from and how many kexec reboots + * have occurred. + * + * __NEW_UTS_LEN is part of uABI, so it safe to use it in here. + */ +struct kho_kexec_metadata { + u32 version; + char previous_release[__NEW_UTS_LEN + 1]; + u32 kexec_count; +} __packed; + +#define KHO_METADATA_NODE_NAME "kexec-metadata" + +#endif /* _LINUX_KHO_ABI_KEXEC_METADATA_H */ diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 6b76e7a6f4c2..4c14aee1fb06 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -318,7 +318,8 @@ static inline bool kvm_vcpu_can_poll(ktime_t cur, ktime_t stop) struct kvm_mmio_fragment { gpa_t gpa; void *data; - unsigned len; + u64 val; + unsigned int len; }; struct kvm_vcpu { @@ -1029,6 +1030,13 @@ static inline struct kvm_vcpu *kvm_get_vcpu_by_id(struct kvm *kvm, int id) return NULL; } +static inline bool kvm_is_vcpu_creation_in_progress(struct kvm *kvm) +{ + lockdep_assert_held(&kvm->lock); + + return kvm->created_vcpus != atomic_read(&kvm->online_vcpus); +} + void kvm_destroy_vcpus(struct kvm *kvm); int kvm_trylock_all_vcpus(struct kvm *kvm); @@ -1627,6 +1635,13 @@ static inline void kvm_create_vcpu_debugfs(struct kvm_vcpu *vcpu) {} #endif #ifdef CONFIG_KVM_GENERIC_HARDWARE_ENABLING +/* + * kvm_arch_shutdown() is invoked immediately prior to forcefully disabling + * hardware virtualization on all CPUs via IPI function calls (in preparation + * for shutdown or reboot), e.g. to allow arch code to prepare for disabling + * virtualization while KVM may be actively running vCPUs. + */ +void kvm_arch_shutdown(void); /* * kvm_arch_{enable,disable}_virtualization() are called on one CPU, under * kvm_usage_lock, immediately after/before 0=>1 and 1=>0 transitions of @@ -2300,7 +2315,6 @@ static inline bool kvm_check_request(int req, struct kvm_vcpu *vcpu) #ifdef CONFIG_KVM_GENERIC_HARDWARE_ENABLING extern bool enable_virt_at_load; -extern bool kvm_rebooting; #endif extern unsigned int halt_poll_ns; @@ -2366,6 +2380,7 @@ void kvm_unregister_device_ops(u32 type); extern struct kvm_device_ops kvm_mpic_ops; extern struct kvm_device_ops kvm_arm_vgic_v2_ops; extern struct kvm_device_ops kvm_arm_vgic_v3_ops; +extern struct kvm_device_ops kvm_arm_vgic_v5_ops; #ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT @@ -2594,12 +2609,4 @@ long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu, struct kvm_pre_fault_memory *range); #endif -#ifdef CONFIG_KVM_GENERIC_HARDWARE_ENABLING -int kvm_enable_virtualization(void); -void kvm_disable_virtualization(void); -#else -static inline int kvm_enable_virtualization(void) { return 0; } -static inline void kvm_disable_virtualization(void) { } -#endif - #endif diff --git a/include/linux/linear_range.h b/include/linux/linear_range.h index 2e4f4c3539c0..0f3037f1a94f 100644 --- a/include/linux/linear_range.h +++ b/include/linux/linear_range.h @@ -57,5 +57,8 @@ void linear_range_get_selector_within(const struct linear_range *r, int linear_range_get_selector_low_array(const struct linear_range *r, int ranges, unsigned int val, unsigned int *selector, bool *found); +int linear_range_get_selector_high_array(const struct linear_range *r, + int ranges, unsigned int val, + unsigned int *selector, bool *found); #endif diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h index dd11fdc76a5f..30c5a39ff9e9 100644 --- a/include/linux/liveupdate.h +++ b/include/linux/liveupdate.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,7 @@ struct liveupdate_file_op_args { * finish, in order to do successful finish calls for all * resources in the session. * @finish: Required. Final cleanup in the new kernel. + * @get_id: Optional. Returns a unique identifier for the file. * @owner: Module reference * * All operations (except can_preserve) receive a pointer to a @@ -78,6 +80,7 @@ struct liveupdate_file_ops { int (*retrieve)(struct liveupdate_file_op_args *args); bool (*can_finish)(struct liveupdate_file_op_args *args); void (*finish)(struct liveupdate_file_op_args *args); + unsigned long (*get_id)(struct file *file); struct module *owner; }; @@ -228,12 +231,12 @@ bool liveupdate_enabled(void); int liveupdate_reboot(void); int liveupdate_register_file_handler(struct liveupdate_file_handler *fh); -int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh); +void liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh); int liveupdate_register_flb(struct liveupdate_file_handler *fh, struct liveupdate_flb *flb); -int liveupdate_unregister_flb(struct liveupdate_file_handler *fh, - struct liveupdate_flb *flb); +void liveupdate_unregister_flb(struct liveupdate_file_handler *fh, + struct liveupdate_flb *flb); int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp); int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp); @@ -255,9 +258,8 @@ static inline int liveupdate_register_file_handler(struct liveupdate_file_handle return -EOPNOTSUPP; } -static inline int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh) +static inline void liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh) { - return -EOPNOTSUPP; } static inline int liveupdate_register_flb(struct liveupdate_file_handler *fh, @@ -266,10 +268,9 @@ static inline int liveupdate_register_flb(struct liveupdate_file_handler *fh, return -EOPNOTSUPP; } -static inline int liveupdate_unregister_flb(struct liveupdate_file_handler *fh, - struct liveupdate_flb *flb) +static inline void liveupdate_unregister_flb(struct liveupdate_file_handler *fh, + struct liveupdate_flb *flb) { - return -EOPNOTSUPP; } static inline int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h index c53c81242e72..b614e0deea72 100644 --- a/include/linux/lockd/bind.h +++ b/include/linux/lockd/bind.h @@ -10,27 +10,20 @@ #ifndef LINUX_LOCKD_BIND_H #define LINUX_LOCKD_BIND_H -#include -/* need xdr-encoded error codes too, so... */ -#include -#ifdef CONFIG_LOCKD_V4 -#include -#endif - -/* Dummy declarations */ +struct file_lock; +struct nfs_fh; struct svc_rqst; struct rpc_task; struct rpc_clnt; +struct super_block; /* * This is the set of functions for lockd->nfsd communication */ struct nlmsvc_binding { - __be32 (*fopen)(struct svc_rqst *, - struct nfs_fh *, - struct file **, - int mode); - void (*fclose)(struct file *); + int (*fopen)(struct svc_rqst *rqstp, struct nfs_fh *f, + struct file **filp, int flags); + void (*fclose)(struct file *filp); }; extern const struct nlmsvc_binding *nlmsvc_ops; @@ -58,6 +51,7 @@ struct nlmclnt_initdata { extern struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init); extern void nlmclnt_done(struct nlm_host *host); extern struct rpc_clnt *nlmclnt_rpc_clnt(struct nlm_host *host); +extern void nlmclnt_shutdown_rpc_clnt(struct nlm_host *host); /* * NLM client operations provide a means to modify RPC processing of NLM @@ -82,4 +76,10 @@ extern int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl, vo extern int lockd_up(struct net *net, const struct cred *cred); extern void lockd_down(struct net *net); +/* + * Cluster failover support + */ +int nlmsvc_unlock_all_by_sb(struct super_block *sb); +int nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr); + #endif /* LINUX_LOCKD_BIND_H */ diff --git a/include/linux/lockd/debug.h b/include/linux/lockd/debug.h deleted file mode 100644 index eede2ab5246f..000000000000 --- a/include/linux/lockd/debug.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * linux/include/linux/lockd/debug.h - * - * Debugging stuff. - * - * Copyright (C) 1996 Olaf Kirch - */ - -#ifndef LINUX_LOCKD_DEBUG_H -#define LINUX_LOCKD_DEBUG_H - -#include - -/* - * Enable lockd debugging. - * Requires RPC_DEBUG. - */ -#undef ifdebug -#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) -# define ifdebug(flag) if (unlikely(nlm_debug & NLMDBG_##flag)) -#else -# define ifdebug(flag) if (0) -#endif - -/* - * Debug flags - */ -#define NLMDBG_SVC 0x0001 -#define NLMDBG_CLIENT 0x0002 -#define NLMDBG_CLNTLOCK 0x0004 -#define NLMDBG_SVCLOCK 0x0008 -#define NLMDBG_MONITOR 0x0010 -#define NLMDBG_CLNTSUBS 0x0020 -#define NLMDBG_SVCSUBS 0x0040 -#define NLMDBG_HOSTCACHE 0x0080 -#define NLMDBG_XDR 0x0100 -#define NLMDBG_ALL 0x7fff - -#endif /* LINUX_LOCKD_DEBUG_H */ diff --git a/include/linux/lockd/xdr4.h b/include/linux/lockd/xdr4.h deleted file mode 100644 index 72831e35dca3..000000000000 --- a/include/linux/lockd/xdr4.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * linux/include/linux/lockd/xdr4.h - * - * XDR types for the NLM protocol - * - * Copyright (C) 1996 Olaf Kirch - */ - -#ifndef LOCKD_XDR4_H -#define LOCKD_XDR4_H - -#include -#include -#include -#include - -/* error codes new to NLMv4 */ -#define nlm4_deadlock cpu_to_be32(NLM_DEADLCK) -#define nlm4_rofs cpu_to_be32(NLM_ROFS) -#define nlm4_stale_fh cpu_to_be32(NLM_STALE_FH) -#define nlm4_fbig cpu_to_be32(NLM_FBIG) -#define nlm4_failed cpu_to_be32(NLM_FAILED) - -void nlm4svc_set_file_lock_range(struct file_lock *fl, u64 off, u64 len); -bool nlm4svc_decode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlm4svc_decode_testargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlm4svc_decode_lockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlm4svc_decode_cancargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlm4svc_decode_unlockargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlm4svc_decode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlm4svc_decode_reboot(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlm4svc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlm4svc_decode_notify(struct svc_rqst *rqstp, struct xdr_stream *xdr); - -bool nlm4svc_encode_testres(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlm4svc_encode_res(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlm4svc_encode_void(struct svc_rqst *rqstp, struct xdr_stream *xdr); -bool nlm4svc_encode_shareres(struct svc_rqst *rqstp, struct xdr_stream *xdr); - -extern const struct rpc_version nlm_version4; - -#endif /* LOCKD_XDR4_H */ diff --git a/include/linux/mISDNdsp.h b/include/linux/mISDNdsp.h deleted file mode 100644 index 00758f45fddc..000000000000 --- a/include/linux/mISDNdsp.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __mISDNdsp_H__ -#define __mISDNdsp_H__ - -struct mISDN_dsp_element_arg { - char *name; - char *def; - char *desc; -}; - -struct mISDN_dsp_element { - char *name; - void *(*new)(const char *arg); - void (*free)(void *p); - void (*process_tx)(void *p, unsigned char *data, int len); - void (*process_rx)(void *p, unsigned char *data, int len, - unsigned int txlen); - int num_args; - struct mISDN_dsp_element_arg - *args; -}; - -extern int mISDN_dsp_element_register(struct mISDN_dsp_element *elem); -extern void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem); - -struct dsp_features { - int hfc_id; /* unique id to identify the chip (or -1) */ - int hfc_dtmf; /* set if HFCmulti card supports dtmf */ - int hfc_conf; /* set if HFCmulti card supports conferences */ - int hfc_loops; /* set if card supports tone loops */ - int hfc_echocanhw; /* set if card supports echocancelation*/ - int pcm_id; /* unique id to identify the pcm bus (or -1) */ - int pcm_slots; /* number of slots on the pcm bus */ - int pcm_banks; /* number of IO banks of pcm bus */ - int unclocked; /* data is not clocked (has jitter/loss) */ - int unordered; /* data is unordered (packets have index) */ -}; - -#endif - diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h deleted file mode 100644 index ef4f8eb02eac..000000000000 --- a/include/linux/mISDNhw.h +++ /dev/null @@ -1,192 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * - * Author Karsten Keil - * - * Basic declarations for the mISDN HW channels - * - * Copyright 2008 by Karsten Keil - */ - -#ifndef MISDNHW_H -#define MISDNHW_H -#include -#include - -/* - * HW DEBUG 0xHHHHGGGG - * H - hardware driver specific bits - * G - for all drivers - */ - -#define DEBUG_HW 0x00000001 -#define DEBUG_HW_OPEN 0x00000002 -#define DEBUG_HW_DCHANNEL 0x00000100 -#define DEBUG_HW_DFIFO 0x00000200 -#define DEBUG_HW_BCHANNEL 0x00001000 -#define DEBUG_HW_BFIFO 0x00002000 - -#define MAX_DFRAME_LEN_L1 300 -#define MAX_MON_FRAME 32 -#define MAX_LOG_SPACE 2048 -#define MISDN_COPY_SIZE 32 - -/* channel->Flags bit field */ -#define FLG_TX_BUSY 0 /* tx_buf in use */ -#define FLG_TX_NEXT 1 /* next_skb in use */ -#define FLG_L1_BUSY 2 /* L1 is permanent busy */ -#define FLG_L2_ACTIVATED 3 /* activated from L2 */ -#define FLG_OPEN 5 /* channel is in use */ -#define FLG_ACTIVE 6 /* channel is activated */ -#define FLG_BUSY_TIMER 7 -/* channel type */ -#define FLG_DCHANNEL 8 /* channel is D-channel */ -#define FLG_BCHANNEL 9 /* channel is B-channel */ -#define FLG_ECHANNEL 10 /* channel is E-channel */ -#define FLG_TRANSPARENT 12 /* channel use transparent data */ -#define FLG_HDLC 13 /* channel use hdlc data */ -#define FLG_L2DATA 14 /* channel use L2 DATA primitivs */ -#define FLG_ORIGIN 15 /* channel is on origin site */ -/* channel specific stuff */ -#define FLG_FILLEMPTY 16 /* fill fifo on first frame (empty) */ -/* arcofi specific */ -#define FLG_ARCOFI_TIMER 17 -#define FLG_ARCOFI_ERROR 18 -/* isar specific */ -#define FLG_INITIALIZED 17 -#define FLG_DLEETX 18 -#define FLG_LASTDLE 19 -#define FLG_FIRST 20 -#define FLG_LASTDATA 21 -#define FLG_NMD_DATA 22 -#define FLG_FTI_RUN 23 -#define FLG_LL_OK 24 -#define FLG_LL_CONN 25 -#define FLG_DTMFSEND 26 -#define FLG_TX_EMPTY 27 -/* stop sending received data upstream */ -#define FLG_RX_OFF 28 -/* workq events */ -#define FLG_RECVQUEUE 30 -#define FLG_PHCHANGE 31 - -#define schedule_event(s, ev) do { \ - test_and_set_bit(ev, &((s)->Flags)); \ - schedule_work(&((s)->workq)); \ - } while (0) - -struct dchannel { - struct mISDNdevice dev; - u_long Flags; - struct work_struct workq; - void (*phfunc) (struct dchannel *); - u_int state; - void *l1; - void *hw; - int slot; /* multiport card channel slot */ - struct timer_list timer; - /* receive data */ - struct sk_buff *rx_skb; - int maxlen; - /* send data */ - struct sk_buff_head squeue; - struct sk_buff_head rqueue; - struct sk_buff *tx_skb; - int tx_idx; - int debug; - /* statistics */ - int err_crc; - int err_tx; - int err_rx; -}; - -typedef int (dchannel_l1callback)(struct dchannel *, u_int); -extern int create_l1(struct dchannel *, dchannel_l1callback *); - -/* private L1 commands */ -#define INFO0 0x8002 -#define INFO1 0x8102 -#define INFO2 0x8202 -#define INFO3_P8 0x8302 -#define INFO3_P10 0x8402 -#define INFO4_P8 0x8502 -#define INFO4_P10 0x8602 -#define LOSTFRAMING 0x8702 -#define ANYSIGNAL 0x8802 -#define HW_POWERDOWN 0x8902 -#define HW_RESET_REQ 0x8a02 -#define HW_POWERUP_REQ 0x8b02 -#define HW_DEACT_REQ 0x8c02 -#define HW_ACTIVATE_REQ 0x8e02 -#define HW_D_NOBLOCKED 0x8f02 -#define HW_RESET_IND 0x9002 -#define HW_POWERUP_IND 0x9102 -#define HW_DEACT_IND 0x9202 -#define HW_ACTIVATE_IND 0x9302 -#define HW_DEACT_CNF 0x9402 -#define HW_TESTLOOP 0x9502 -#define HW_TESTRX_RAW 0x9602 -#define HW_TESTRX_HDLC 0x9702 -#define HW_TESTRX_OFF 0x9802 -#define HW_TIMER3_IND 0x9902 -#define HW_TIMER3_VALUE 0x9a00 -#define HW_TIMER3_VMASK 0x00FF - -struct layer1; -extern int l1_event(struct layer1 *, u_int); - -#define MISDN_BCH_FILL_SIZE 4 - -struct bchannel { - struct mISDNchannel ch; - int nr; - u_long Flags; - struct work_struct workq; - u_int state; - void *hw; - int slot; /* multiport card channel slot */ - struct timer_list timer; - /* receive data */ - u8 fill[MISDN_BCH_FILL_SIZE]; - struct sk_buff *rx_skb; - unsigned short maxlen; - unsigned short init_maxlen; /* initial value */ - unsigned short next_maxlen; /* pending value */ - unsigned short minlen; /* for transparent data */ - unsigned short init_minlen; /* initial value */ - unsigned short next_minlen; /* pending value */ - /* send data */ - struct sk_buff *next_skb; - struct sk_buff *tx_skb; - struct sk_buff_head rqueue; - int rcount; - int tx_idx; - int debug; - /* statistics */ - int err_crc; - int err_tx; - int err_rx; - int dropcnt; -}; - -extern int mISDN_initdchannel(struct dchannel *, int, void *); -extern int mISDN_initbchannel(struct bchannel *, unsigned short, - unsigned short); -extern int mISDN_freedchannel(struct dchannel *); -extern void mISDN_clear_bchannel(struct bchannel *); -extern void mISDN_freebchannel(struct bchannel *); -extern int mISDN_ctrl_bchannel(struct bchannel *, struct mISDN_ctrl_req *); -extern void queue_ch_frame(struct mISDNchannel *, u_int, - int, struct sk_buff *); -extern int dchannel_senddata(struct dchannel *, struct sk_buff *); -extern int bchannel_senddata(struct bchannel *, struct sk_buff *); -extern int bchannel_get_rxbuf(struct bchannel *, int); -extern void recv_Dchannel(struct dchannel *); -extern void recv_Echannel(struct dchannel *, struct dchannel *); -extern void recv_Bchannel(struct bchannel *, unsigned int, bool); -extern void recv_Dchannel_skb(struct dchannel *, struct sk_buff *); -extern void recv_Bchannel_skb(struct bchannel *, struct sk_buff *); -extern int get_next_bframe(struct bchannel *); -extern int get_next_dframe(struct dchannel *); - -#endif diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h deleted file mode 100644 index 7aab4a769736..000000000000 --- a/include/linux/mISDNif.h +++ /dev/null @@ -1,603 +0,0 @@ -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - * - * This code is free software; you can redistribute it and/or modify - * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE - * version 2.1 as published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU LESSER GENERAL PUBLIC LICENSE for more details. - * - */ - -#ifndef mISDNIF_H -#define mISDNIF_H - -#include -#include -#include - -/* - * ABI Version 32 bit - * - * <8 bit> Major version - * - changed if any interface become backwards incompatible - * - * <8 bit> Minor version - * - changed if any interface is extended but backwards compatible - * - * <16 bit> Release number - * - should be incremented on every checkin - */ -#define MISDN_MAJOR_VERSION 1 -#define MISDN_MINOR_VERSION 1 -#define MISDN_RELEASE 29 - -/* primitives for information exchange - * generell format - * <16 bit 0 > - * <8 bit command> - * BIT 8 = 1 LAYER private - * BIT 7 = 1 answer - * BIT 6 = 1 DATA - * <8 bit target layer mask> - * - * Layer = 00 is reserved for general commands - Layer = 01 L2 -> HW - Layer = 02 HW -> L2 - Layer = 04 L3 -> L2 - Layer = 08 L2 -> L3 - * Layer = FF is reserved for broadcast commands - */ - -#define MISDN_CMDMASK 0xff00 -#define MISDN_LAYERMASK 0x00ff - -/* generell commands */ -#define OPEN_CHANNEL 0x0100 -#define CLOSE_CHANNEL 0x0200 -#define CONTROL_CHANNEL 0x0300 -#define CHECK_DATA 0x0400 - -/* layer 2 -> layer 1 */ -#define PH_ACTIVATE_REQ 0x0101 -#define PH_DEACTIVATE_REQ 0x0201 -#define PH_DATA_REQ 0x2001 -#define MPH_ACTIVATE_REQ 0x0501 -#define MPH_DEACTIVATE_REQ 0x0601 -#define MPH_INFORMATION_REQ 0x0701 -#define PH_CONTROL_REQ 0x0801 - -/* layer 1 -> layer 2 */ -#define PH_ACTIVATE_IND 0x0102 -#define PH_ACTIVATE_CNF 0x4102 -#define PH_DEACTIVATE_IND 0x0202 -#define PH_DEACTIVATE_CNF 0x4202 -#define PH_DATA_IND 0x2002 -#define PH_DATA_E_IND 0x3002 -#define MPH_ACTIVATE_IND 0x0502 -#define MPH_DEACTIVATE_IND 0x0602 -#define MPH_INFORMATION_IND 0x0702 -#define PH_DATA_CNF 0x6002 -#define PH_CONTROL_IND 0x0802 -#define PH_CONTROL_CNF 0x4802 - -/* layer 3 -> layer 2 */ -#define DL_ESTABLISH_REQ 0x1004 -#define DL_RELEASE_REQ 0x1104 -#define DL_DATA_REQ 0x3004 -#define DL_UNITDATA_REQ 0x3104 -#define DL_INFORMATION_REQ 0x0004 - -/* layer 2 -> layer 3 */ -#define DL_ESTABLISH_IND 0x1008 -#define DL_ESTABLISH_CNF 0x5008 -#define DL_RELEASE_IND 0x1108 -#define DL_RELEASE_CNF 0x5108 -#define DL_DATA_IND 0x3008 -#define DL_UNITDATA_IND 0x3108 -#define DL_INFORMATION_IND 0x0008 - -/* intern layer 2 management */ -#define MDL_ASSIGN_REQ 0x1804 -#define MDL_ASSIGN_IND 0x1904 -#define MDL_REMOVE_REQ 0x1A04 -#define MDL_REMOVE_IND 0x1B04 -#define MDL_STATUS_UP_IND 0x1C04 -#define MDL_STATUS_DOWN_IND 0x1D04 -#define MDL_STATUS_UI_IND 0x1E04 -#define MDL_ERROR_IND 0x1F04 -#define MDL_ERROR_RSP 0x5F04 - -/* intern layer 2 */ -#define DL_TIMER200_IND 0x7004 -#define DL_TIMER203_IND 0x7304 -#define DL_INTERN_MSG 0x7804 - -/* DL_INFORMATION_IND types */ -#define DL_INFO_L2_CONNECT 0x0001 -#define DL_INFO_L2_REMOVED 0x0002 - -/* PH_CONTROL types */ -/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */ -#define DTMF_TONE_VAL 0x2000 -#define DTMF_TONE_MASK 0x007F -#define DTMF_TONE_START 0x2100 -#define DTMF_TONE_STOP 0x2200 -#define DTMF_HFC_COEF 0x4000 -#define DSP_CONF_JOIN 0x2403 -#define DSP_CONF_SPLIT 0x2404 -#define DSP_RECEIVE_OFF 0x2405 -#define DSP_RECEIVE_ON 0x2406 -#define DSP_ECHO_ON 0x2407 -#define DSP_ECHO_OFF 0x2408 -#define DSP_MIX_ON 0x2409 -#define DSP_MIX_OFF 0x240a -#define DSP_DELAY 0x240b -#define DSP_JITTER 0x240c -#define DSP_TXDATA_ON 0x240d -#define DSP_TXDATA_OFF 0x240e -#define DSP_TX_DEJITTER 0x240f -#define DSP_TX_DEJ_OFF 0x2410 -#define DSP_TONE_PATT_ON 0x2411 -#define DSP_TONE_PATT_OFF 0x2412 -#define DSP_VOL_CHANGE_TX 0x2413 -#define DSP_VOL_CHANGE_RX 0x2414 -#define DSP_BF_ENABLE_KEY 0x2415 -#define DSP_BF_DISABLE 0x2416 -#define DSP_BF_ACCEPT 0x2416 -#define DSP_BF_REJECT 0x2417 -#define DSP_PIPELINE_CFG 0x2418 -#define HFC_VOL_CHANGE_TX 0x2601 -#define HFC_VOL_CHANGE_RX 0x2602 -#define HFC_SPL_LOOP_ON 0x2603 -#define HFC_SPL_LOOP_OFF 0x2604 -/* for T30 FAX and analog modem */ -#define HW_MOD_FRM 0x4000 -#define HW_MOD_FRH 0x4001 -#define HW_MOD_FTM 0x4002 -#define HW_MOD_FTH 0x4003 -#define HW_MOD_FTS 0x4004 -#define HW_MOD_CONNECT 0x4010 -#define HW_MOD_OK 0x4011 -#define HW_MOD_NOCARR 0x4012 -#define HW_MOD_FCERROR 0x4013 -#define HW_MOD_READY 0x4014 -#define HW_MOD_LASTDATA 0x4015 - -/* DSP_TONE_PATT_ON parameter */ -#define TONE_OFF 0x0000 -#define TONE_GERMAN_DIALTONE 0x0001 -#define TONE_GERMAN_OLDDIALTONE 0x0002 -#define TONE_AMERICAN_DIALTONE 0x0003 -#define TONE_GERMAN_DIALPBX 0x0004 -#define TONE_GERMAN_OLDDIALPBX 0x0005 -#define TONE_AMERICAN_DIALPBX 0x0006 -#define TONE_GERMAN_RINGING 0x0007 -#define TONE_GERMAN_OLDRINGING 0x0008 -#define TONE_AMERICAN_RINGPBX 0x000b -#define TONE_GERMAN_RINGPBX 0x000c -#define TONE_GERMAN_OLDRINGPBX 0x000d -#define TONE_AMERICAN_RINGING 0x000e -#define TONE_GERMAN_BUSY 0x000f -#define TONE_GERMAN_OLDBUSY 0x0010 -#define TONE_AMERICAN_BUSY 0x0011 -#define TONE_GERMAN_HANGUP 0x0012 -#define TONE_GERMAN_OLDHANGUP 0x0013 -#define TONE_AMERICAN_HANGUP 0x0014 -#define TONE_SPECIAL_INFO 0x0015 -#define TONE_GERMAN_GASSENBESETZT 0x0016 -#define TONE_GERMAN_AUFSCHALTTON 0x0016 - -/* MPH_INFORMATION_IND */ -#define L1_SIGNAL_LOS_OFF 0x0010 -#define L1_SIGNAL_LOS_ON 0x0011 -#define L1_SIGNAL_AIS_OFF 0x0012 -#define L1_SIGNAL_AIS_ON 0x0013 -#define L1_SIGNAL_RDI_OFF 0x0014 -#define L1_SIGNAL_RDI_ON 0x0015 -#define L1_SIGNAL_SLIP_RX 0x0020 -#define L1_SIGNAL_SLIP_TX 0x0021 - -/* - * protocol ids - * D channel 1-31 - * B channel 33 - 63 - */ - -#define ISDN_P_NONE 0 -#define ISDN_P_BASE 0 -#define ISDN_P_TE_S0 0x01 -#define ISDN_P_NT_S0 0x02 -#define ISDN_P_TE_E1 0x03 -#define ISDN_P_NT_E1 0x04 -#define ISDN_P_TE_UP0 0x05 -#define ISDN_P_NT_UP0 0x06 - -#define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \ - (p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE)) -#define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \ - (p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT)) -#define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0)) -#define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1)) -#define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0)) - - -#define ISDN_P_LAPD_TE 0x10 -#define ISDN_P_LAPD_NT 0x11 - -#define ISDN_P_B_MASK 0x1f -#define ISDN_P_B_START 0x20 - -#define ISDN_P_B_RAW 0x21 -#define ISDN_P_B_HDLC 0x22 -#define ISDN_P_B_X75SLP 0x23 -#define ISDN_P_B_L2DTMF 0x24 -#define ISDN_P_B_L2DSP 0x25 -#define ISDN_P_B_L2DSPHDLC 0x26 -#define ISDN_P_B_T30_FAX 0x27 -#define ISDN_P_B_MODEM_ASYNC 0x28 - -#define OPTION_L2_PMX 1 -#define OPTION_L2_PTP 2 -#define OPTION_L2_FIXEDTEI 3 -#define OPTION_L2_CLEANUP 4 -#define OPTION_L1_HOLD 5 - -/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */ -#define MISDN_MAX_IDLEN 20 - -struct mISDNhead { - unsigned int prim; - unsigned int id; -} __packed; - -#define MISDN_HEADER_LEN sizeof(struct mISDNhead) -#define MAX_DATA_SIZE 2048 -#define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN) -#define MAX_DFRAME_LEN 260 - -#define MISDN_ID_ADDR_MASK 0xFFFF -#define MISDN_ID_TEI_MASK 0xFF00 -#define MISDN_ID_SAPI_MASK 0x00FF -#define MISDN_ID_TEI_ANY 0x7F00 - -#define MISDN_ID_ANY 0xFFFF -#define MISDN_ID_NONE 0xFFFE - -#define GROUP_TEI 127 -#define TEI_SAPI 63 -#define CTRL_SAPI 0 - -#define MISDN_MAX_CHANNEL 127 -#define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3) - -#define SOL_MISDN 0 - -struct sockaddr_mISDN { - sa_family_t family; - unsigned char dev; - unsigned char channel; - unsigned char sapi; - unsigned char tei; -}; - -struct mISDNversion { - unsigned char major; - unsigned char minor; - unsigned short release; -}; - -struct mISDN_devinfo { - u_int id; - u_int Dprotocols; - u_int Bprotocols; - u_int protocol; - u_char channelmap[MISDN_CHMAP_SIZE]; - u_int nrbchan; - char name[MISDN_MAX_IDLEN]; -}; - -struct mISDN_devrename { - u_int id; - char name[MISDN_MAX_IDLEN]; /* new name */ -}; - -/* MPH_INFORMATION_REQ payload */ -struct ph_info_ch { - __u32 protocol; - __u64 Flags; -}; - -struct ph_info_dch { - struct ph_info_ch ch; - __u16 state; - __u16 num_bch; -}; - -struct ph_info { - struct ph_info_dch dch; - struct ph_info_ch bch[]; -}; - -/* timer device ioctl */ -#define IMADDTIMER _IOR('I', 64, int) -#define IMDELTIMER _IOR('I', 65, int) - -/* socket ioctls */ -#define IMGETVERSION _IOR('I', 66, int) -#define IMGETCOUNT _IOR('I', 67, int) -#define IMGETDEVINFO _IOR('I', 68, int) -#define IMCTRLREQ _IOR('I', 69, int) -#define IMCLEAR_L2 _IOR('I', 70, int) -#define IMSETDEVNAME _IOR('I', 71, struct mISDN_devrename) -#define IMHOLD_L1 _IOR('I', 72, int) - -static inline int -test_channelmap(u_int nr, u_char *map) -{ - if (nr <= MISDN_MAX_CHANNEL) - return map[nr >> 3] & (1 << (nr & 7)); - else - return 0; -} - -static inline void -set_channelmap(u_int nr, u_char *map) -{ - map[nr >> 3] |= (1 << (nr & 7)); -} - -static inline void -clear_channelmap(u_int nr, u_char *map) -{ - map[nr >> 3] &= ~(1 << (nr & 7)); -} - -/* CONTROL_CHANNEL parameters */ -#define MISDN_CTRL_GETOP 0x0000 -#define MISDN_CTRL_LOOP 0x0001 -#define MISDN_CTRL_CONNECT 0x0002 -#define MISDN_CTRL_DISCONNECT 0x0004 -#define MISDN_CTRL_RX_BUFFER 0x0008 -#define MISDN_CTRL_PCMCONNECT 0x0010 -#define MISDN_CTRL_PCMDISCONNECT 0x0020 -#define MISDN_CTRL_SETPEER 0x0040 -#define MISDN_CTRL_UNSETPEER 0x0080 -#define MISDN_CTRL_RX_OFF 0x0100 -#define MISDN_CTRL_FILL_EMPTY 0x0200 -#define MISDN_CTRL_GETPEER 0x0400 -#define MISDN_CTRL_L1_TIMER3 0x0800 -#define MISDN_CTRL_HW_FEATURES_OP 0x2000 -#define MISDN_CTRL_HW_FEATURES 0x2001 -#define MISDN_CTRL_HFC_OP 0x4000 -#define MISDN_CTRL_HFC_PCM_CONN 0x4001 -#define MISDN_CTRL_HFC_PCM_DISC 0x4002 -#define MISDN_CTRL_HFC_CONF_JOIN 0x4003 -#define MISDN_CTRL_HFC_CONF_SPLIT 0x4004 -#define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005 -#define MISDN_CTRL_HFC_RECEIVE_ON 0x4006 -#define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007 -#define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008 -#define MISDN_CTRL_HFC_WD_INIT 0x4009 -#define MISDN_CTRL_HFC_WD_RESET 0x400A - -/* special RX buffer value for MISDN_CTRL_RX_BUFFER request.p1 is the minimum - * buffer size request.p2 the maximum. Using MISDN_CTRL_RX_SIZE_IGNORE will - * not change the value, but still read back the actual stetting. - */ -#define MISDN_CTRL_RX_SIZE_IGNORE -1 - -/* socket options */ -#define MISDN_TIME_STAMP 0x0001 - -struct mISDN_ctrl_req { - int op; - int channel; - int p1; - int p2; -}; - -/* muxer options */ -#define MISDN_OPT_ALL 1 -#define MISDN_OPT_TEIMGR 2 - -#ifdef __KERNEL__ -#include -#include -#include -#include -#include - -#define DEBUG_CORE 0x000000ff -#define DEBUG_CORE_FUNC 0x00000002 -#define DEBUG_SOCKET 0x00000004 -#define DEBUG_MANAGER 0x00000008 -#define DEBUG_SEND_ERR 0x00000010 -#define DEBUG_MSG_THREAD 0x00000020 -#define DEBUG_QUEUE_FUNC 0x00000040 -#define DEBUG_L1 0x0000ff00 -#define DEBUG_L1_FSM 0x00000200 -#define DEBUG_L2 0x00ff0000 -#define DEBUG_L2_FSM 0x00020000 -#define DEBUG_L2_CTRL 0x00040000 -#define DEBUG_L2_RECV 0x00080000 -#define DEBUG_L2_TEI 0x00100000 -#define DEBUG_L2_TEIFSM 0x00200000 -#define DEBUG_TIMER 0x01000000 -#define DEBUG_CLOCK 0x02000000 - -#define mISDN_HEAD_P(s) ((struct mISDNhead *)&s->cb[0]) -#define mISDN_HEAD_PRIM(s) (((struct mISDNhead *)&s->cb[0])->prim) -#define mISDN_HEAD_ID(s) (((struct mISDNhead *)&s->cb[0])->id) - -/* socket states */ -#define MISDN_OPEN 1 -#define MISDN_BOUND 2 -#define MISDN_CLOSED 3 - -struct mISDNchannel; -struct mISDNdevice; -struct mISDNstack; -struct mISDNclock; - -struct channel_req { - u_int protocol; - struct sockaddr_mISDN adr; - struct mISDNchannel *ch; -}; - -typedef int (ctrl_func_t)(struct mISDNchannel *, u_int, void *); -typedef int (send_func_t)(struct mISDNchannel *, struct sk_buff *); -typedef int (create_func_t)(struct channel_req *); - -struct Bprotocol { - struct list_head list; - char *name; - u_int Bprotocols; - create_func_t *create; -}; - -struct mISDNchannel { - struct list_head list; - u_int protocol; - u_int nr; - u_long opt; - u_int addr; - struct mISDNstack *st; - struct mISDNchannel *peer; - send_func_t *send; - send_func_t *recv; - ctrl_func_t *ctrl; -}; - -struct mISDN_sock_list { - struct hlist_head head; - rwlock_t lock; -}; - -struct mISDN_sock { - struct sock sk; - struct mISDNchannel ch; - u_int cmask; - struct mISDNdevice *dev; -}; - - - -struct mISDNdevice { - struct mISDNchannel D; - u_int id; - u_int Dprotocols; - u_int Bprotocols; - u_int nrbchan; - u_char channelmap[MISDN_CHMAP_SIZE]; - struct list_head bchannels; - struct mISDNchannel *teimgr; - struct device dev; -}; - -struct mISDNstack { - u_long status; - struct mISDNdevice *dev; - struct task_struct *thread; - struct completion *notify; - wait_queue_head_t workq; - struct sk_buff_head msgq; - struct list_head layer2; - struct mISDNchannel *layer1; - struct mISDNchannel own; - struct mutex lmutex; /* protect lists */ - struct mISDN_sock_list l1sock; -#ifdef MISDN_MSG_STATS - u_int msg_cnt; - u_int sleep_cnt; - u_int stopped_cnt; -#endif -}; - -typedef int (clockctl_func_t)(void *, int); - -struct mISDNclock { - struct list_head list; - char name[64]; - int pri; - clockctl_func_t *ctl; - void *priv; -}; - -/* global alloc/queue functions */ - -static inline struct sk_buff * -mI_alloc_skb(unsigned int len, gfp_t gfp_mask) -{ - struct sk_buff *skb; - - skb = alloc_skb(len + MISDN_HEADER_LEN, gfp_mask); - if (likely(skb)) - skb_reserve(skb, MISDN_HEADER_LEN); - return skb; -} - -static inline struct sk_buff * -_alloc_mISDN_skb(u_int prim, u_int id, u_int len, void *dp, gfp_t gfp_mask) -{ - struct sk_buff *skb = mI_alloc_skb(len, gfp_mask); - struct mISDNhead *hh; - - if (!skb) - return NULL; - if (len) - skb_put_data(skb, dp, len); - hh = mISDN_HEAD_P(skb); - hh->prim = prim; - hh->id = id; - return skb; -} - -static inline void -_queue_data(struct mISDNchannel *ch, u_int prim, - u_int id, u_int len, void *dp, gfp_t gfp_mask) -{ - struct sk_buff *skb; - - if (!ch->peer) - return; - skb = _alloc_mISDN_skb(prim, id, len, dp, gfp_mask); - if (!skb) - return; - if (ch->recv(ch->peer, skb)) - dev_kfree_skb(skb); -} - -/* global register/unregister functions */ - -extern int mISDN_register_device(struct mISDNdevice *, - struct device *parent, char *name); -extern void mISDN_unregister_device(struct mISDNdevice *); -extern int mISDN_register_Bprotocol(struct Bprotocol *); -extern void mISDN_unregister_Bprotocol(struct Bprotocol *); -extern struct mISDNclock *mISDN_register_clock(char *, int, clockctl_func_t *, - void *); -extern void mISDN_unregister_clock(struct mISDNclock *); - -static inline struct mISDNdevice *dev_to_mISDN(const struct device *dev) -{ - if (dev) - return dev_get_drvdata(dev); - else - return NULL; -} - -extern void set_channel_address(struct mISDNchannel *, u_int, u_int); -extern void mISDN_clock_update(struct mISDNclock *, int, ktime_t *); -extern unsigned short mISDN_clock_get(void); -extern const char *mISDNDevName4ch(struct mISDNchannel *); - -#endif /* __KERNEL__ */ -#endif /* mISDNIF_H */ diff --git a/include/linux/mailbox_client.h b/include/linux/mailbox_client.h index c6eea9afb943..e5997120f45c 100644 --- a/include/linux/mailbox_client.h +++ b/include/linux/mailbox_client.h @@ -45,6 +45,7 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg); int mbox_flush(struct mbox_chan *chan, unsigned long timeout); void mbox_client_txdone(struct mbox_chan *chan, int r); /* atomic */ bool mbox_client_peek_data(struct mbox_chan *chan); /* atomic */ +unsigned int mbox_chan_tx_slots_available(struct mbox_chan *chan); /* atomic */ void mbox_free_channel(struct mbox_chan *chan); /* may sleep */ #endif /* __MAILBOX_CLIENT_H */ diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h index 80a427c7ca29..dc93287a2a01 100644 --- a/include/linux/mailbox_controller.h +++ b/include/linux/mailbox_controller.h @@ -3,6 +3,7 @@ #ifndef __MAILBOX_CONTROLLER_H #define __MAILBOX_CONTROLLER_H +#include #include #include #include @@ -11,6 +12,13 @@ struct mbox_chan; +/* Sentinel value distinguishing "no active request" from "NULL message data" */ +#define MBOX_NO_MSG ((void *)-1) + +#define MBOX_TXDONE_BY_IRQ BIT(0) /* controller has remote RTR irq */ +#define MBOX_TXDONE_BY_POLL BIT(1) /* controller can read status of last TX */ +#define MBOX_TXDONE_BY_ACK BIT(2) /* S/W ACK received by Client ticks the TX */ + /** * struct mbox_chan_ops - methods to control mailbox channels * @send_data: The API asks the MBOX controller driver, in atomic @@ -54,10 +62,10 @@ struct mbox_chan_ops { /** * struct mbox_controller - Controller of a class of communication channels - * @dev: Device backing this controller - * @ops: Operators that work on each communication chan - * @chans: Array of channels - * @num_chans: Number of channels in the 'chans' array. + * @dev: Device backing this controller. Required. + * @ops: Operators that work on each communication chan. Required. + * @chans: Array of channels. Required. + * @num_chans: Number of channels in the 'chans' array. Required. * @txdone_irq: Indicates if the controller can report to API when * the last transmitted data was read by the remote. * Eg, if it has some TX ACK irq. @@ -70,6 +78,7 @@ struct mbox_chan_ops { * @of_xlate: Controller driver specific mapping of channel via DT * @poll_hrt: API private. hrtimer used to poll for TXDONE on all * channels. + * @poll_hrt_lock: API private. Lock protecting access to poll_hrt. * @node: API private. To hook into list of controllers. */ struct mbox_controller { diff --git a/include/linux/maple_tree.h b/include/linux/maple_tree.h index 0c464eade1d6..4a5631906aff 100644 --- a/include/linux/maple_tree.h +++ b/include/linux/maple_tree.h @@ -4,7 +4,7 @@ /* * Maple Tree - An RCU-safe adaptive tree for storing ranges * Copyright (c) 2018-2022 Oracle - * Authors: Liam R. Howlett + * Authors: Liam R. Howlett * Matthew Wilcox */ diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h index a82755e1fc40..5bdbd9e1d460 100644 --- a/include/linux/mei_cl_bus.h +++ b/include/linux/mei_cl_bus.h @@ -112,6 +112,7 @@ int mei_cldev_register_rx_cb(struct mei_cl_device *cldev, mei_cldev_cb_t rx_cb); int mei_cldev_register_notif_cb(struct mei_cl_device *cldev, mei_cldev_cb_t notif_cb); +const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev); u8 mei_cldev_ver(const struct mei_cl_device *cldev); size_t mei_cldev_mtu(const struct mei_cl_device *cldev); diff --git a/include/linux/memblock.h b/include/linux/memblock.h index 9eac4f268359..b0f750d22a7b 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -173,8 +173,6 @@ void __next_mem_range_rev(u64 *idx, int nid, enum memblock_flags flags, struct memblock_type *type_b, phys_addr_t *out_start, phys_addr_t *out_end, int *out_nid); -void memblock_free_late(phys_addr_t base, phys_addr_t size); - #ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP static inline void __next_physmem_range(u64 *idx, struct memblock_type *type, phys_addr_t *out_start, diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 5173a9f16721..dc3fa687759b 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -115,6 +115,16 @@ struct mem_cgroup_per_node { unsigned long lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS]; struct mem_cgroup_reclaim_iter iter; + /* + * objcg is wiped out as a part of the objcg repaprenting process. + * orig_objcg preserves a pointer (and a reference) to the original + * objcg until the end of live of memcg. + */ + struct obj_cgroup __rcu *objcg; + struct obj_cgroup *orig_objcg; + /* list of inherited objcgs, protected by objcg_lock */ + struct list_head objcg_list; + #ifdef CONFIG_MEMCG_NMI_SAFETY_REQUIRES_ATOMIC /* slab stats for nmi context */ atomic_t slab_reclaimable; @@ -179,6 +189,7 @@ struct obj_cgroup { struct list_head list; /* protected by objcg_lock */ struct rcu_head rcu; }; + bool is_root; }; /* @@ -257,15 +268,6 @@ struct mem_cgroup { seqlock_t socket_pressure_seqlock; #endif int kmemcg_id; - /* - * memcg->objcg is wiped out as a part of the objcg repaprenting - * process. memcg->orig_objcg preserves a pointer (and a reference) - * to the original objcg until the end of live of memcg. - */ - struct obj_cgroup __rcu *objcg; - struct obj_cgroup *orig_objcg; - /* list of inherited objcgs, protected by objcg_lock */ - struct list_head objcg_list; struct memcg_vmstats_percpu __percpu *vmstats_percpu; @@ -367,9 +369,6 @@ enum objext_flags { #define OBJEXTS_FLAGS_MASK (__NR_OBJEXTS_FLAGS - 1) #ifdef CONFIG_MEMCG - -static inline bool folio_memcg_kmem(struct folio *folio); - /* * After the initialization objcg->memcg is always pointing at * a valid memcg, but can be atomically swapped to the parent memcg. @@ -383,43 +382,19 @@ static inline struct mem_cgroup *obj_cgroup_memcg(struct obj_cgroup *objcg) } /* - * __folio_memcg - Get the memory cgroup associated with a non-kmem folio - * @folio: Pointer to the folio. - * - * Returns a pointer to the memory cgroup associated with the folio, - * or NULL. This function assumes that the folio is known to have a - * proper memory cgroup pointer. It's not safe to call this function - * against some type of folios, e.g. slab folios or ex-slab folios or - * kmem folios. - */ -static inline struct mem_cgroup *__folio_memcg(struct folio *folio) -{ - unsigned long memcg_data = folio->memcg_data; - - VM_BUG_ON_FOLIO(folio_test_slab(folio), folio); - VM_BUG_ON_FOLIO(memcg_data & MEMCG_DATA_OBJEXTS, folio); - VM_BUG_ON_FOLIO(memcg_data & MEMCG_DATA_KMEM, folio); - - return (struct mem_cgroup *)(memcg_data & ~OBJEXTS_FLAGS_MASK); -} - -/* - * __folio_objcg - get the object cgroup associated with a kmem folio. + * folio_objcg - get the object cgroup associated with a folio. * @folio: Pointer to the folio. * * Returns a pointer to the object cgroup associated with the folio, * or NULL. This function assumes that the folio is known to have a - * proper object cgroup pointer. It's not safe to call this function - * against some type of folios, e.g. slab folios or ex-slab folios or - * LRU folios. + * proper object cgroup pointer. */ -static inline struct obj_cgroup *__folio_objcg(struct folio *folio) +static inline struct obj_cgroup *folio_objcg(struct folio *folio) { unsigned long memcg_data = folio->memcg_data; VM_BUG_ON_FOLIO(folio_test_slab(folio), folio); VM_BUG_ON_FOLIO(memcg_data & MEMCG_DATA_OBJEXTS, folio); - VM_BUG_ON_FOLIO(!(memcg_data & MEMCG_DATA_KMEM), folio); return (struct obj_cgroup *)(memcg_data & ~OBJEXTS_FLAGS_MASK); } @@ -433,21 +408,30 @@ static inline struct obj_cgroup *__folio_objcg(struct folio *folio) * proper memory cgroup pointer. It's not safe to call this function * against some type of folios, e.g. slab folios or ex-slab folios. * - * For a non-kmem folio any of the following ensures folio and memcg binding - * stability: + * For a folio any of the following ensures folio and objcg binding stability: * * - the folio lock * - LRU isolation * - exclusive reference * - * For a kmem folio a caller should hold an rcu read lock to protect memcg - * associated with a kmem folio from being released. + * Based on the stable binding of folio and objcg, for a folio any of the + * following ensures folio and memcg binding stability: + * + * - cgroup_mutex + * - the lruvec lock + * + * If the caller only want to ensure that the page counters of memcg are + * updated correctly, ensure that the binding stability of folio and objcg + * is sufficient. + * + * Note: The caller should hold an rcu read lock or cgroup_mutex to protect + * memcg associated with a folio from being released. */ static inline struct mem_cgroup *folio_memcg(struct folio *folio) { - if (folio_memcg_kmem(folio)) - return obj_cgroup_memcg(__folio_objcg(folio)); - return __folio_memcg(folio); + struct obj_cgroup *objcg = folio_objcg(folio); + + return objcg ? obj_cgroup_memcg(objcg) : NULL; } /* @@ -471,15 +455,10 @@ static inline bool folio_memcg_charged(struct folio *folio) * has an associated memory cgroup pointer or an object cgroups vector or * an object cgroup. * - * For a non-kmem folio any of the following ensures folio and memcg binding - * stability: + * The page and objcg or memcg binding rules can refer to folio_memcg(). * - * - the folio lock - * - LRU isolation - * - exclusive reference - * - * For a kmem folio a caller should hold an rcu read lock to protect memcg - * associated with a kmem folio from being released. + * A caller should hold an rcu read lock to protect memcg associated with a + * page from being released. */ static inline struct mem_cgroup *folio_memcg_check(struct folio *folio) { @@ -488,18 +467,14 @@ static inline struct mem_cgroup *folio_memcg_check(struct folio *folio) * for slabs, READ_ONCE() should be used here. */ unsigned long memcg_data = READ_ONCE(folio->memcg_data); + struct obj_cgroup *objcg; if (memcg_data & MEMCG_DATA_OBJEXTS) return NULL; - if (memcg_data & MEMCG_DATA_KMEM) { - struct obj_cgroup *objcg; + objcg = (void *)(memcg_data & ~OBJEXTS_FLAGS_MASK); - objcg = (void *)(memcg_data & ~OBJEXTS_FLAGS_MASK); - return obj_cgroup_memcg(objcg); - } - - return (struct mem_cgroup *)(memcg_data & ~OBJEXTS_FLAGS_MASK); + return objcg ? obj_cgroup_memcg(objcg) : NULL; } static inline struct mem_cgroup *page_memcg_check(struct page *page) @@ -548,6 +523,11 @@ static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg) return (memcg == root_mem_cgroup); } +static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg) +{ + return objcg->is_root; +} + static inline bool mem_cgroup_disabled(void) { return !cgroup_subsys_enabled(memory_cgrp_subsys); @@ -735,7 +715,15 @@ out: * folio_lruvec - return lruvec for isolating/putting an LRU folio * @folio: Pointer to the folio. * - * This function relies on folio->mem_cgroup being stable. + * Call with rcu_read_lock() held to ensure the lifetime of the returned lruvec. + * Note that this alone will NOT guarantee the stability of the folio->lruvec + * association; the folio can be reparented to an ancestor if this races with + * cgroup deletion. + * + * Use folio_lruvec_lock() to ensure both lifetime and stability of the binding. + * Once a lruvec is locked, folio_lruvec() can be called on other folios, and + * their binding is stable if the returned lruvec matches the one the caller has + * locked. Useful for lock batching. */ static inline struct lruvec *folio_lruvec(struct folio *folio) { @@ -758,15 +746,6 @@ struct lruvec *folio_lruvec_lock_irq(struct folio *folio); struct lruvec *folio_lruvec_lock_irqsave(struct folio *folio, unsigned long *flags); -#ifdef CONFIG_DEBUG_VM -void lruvec_memcg_debug(struct lruvec *lruvec, struct folio *folio); -#else -static inline -void lruvec_memcg_debug(struct lruvec *lruvec, struct folio *folio) -{ -} -#endif - static inline struct mem_cgroup *mem_cgroup_from_css(struct cgroup_subsys_state *css){ return css ? container_of(css, struct mem_cgroup, css) : NULL; @@ -774,23 +753,26 @@ struct mem_cgroup *mem_cgroup_from_css(struct cgroup_subsys_state *css){ static inline bool obj_cgroup_tryget(struct obj_cgroup *objcg) { + if (obj_cgroup_is_root(objcg)) + return true; return percpu_ref_tryget(&objcg->refcnt); } -static inline void obj_cgroup_get(struct obj_cgroup *objcg) -{ - percpu_ref_get(&objcg->refcnt); -} - static inline void obj_cgroup_get_many(struct obj_cgroup *objcg, unsigned long nr) { - percpu_ref_get_many(&objcg->refcnt, nr); + if (!obj_cgroup_is_root(objcg)) + percpu_ref_get_many(&objcg->refcnt, nr); +} + +static inline void obj_cgroup_get(struct obj_cgroup *objcg) +{ + obj_cgroup_get_many(objcg, 1); } static inline void obj_cgroup_put(struct obj_cgroup *objcg) { - if (objcg) + if (objcg && !obj_cgroup_is_root(objcg)) percpu_ref_put(&objcg->refcnt); } @@ -885,7 +867,7 @@ static inline bool mm_match_cgroup(struct mm_struct *mm, return match; } -struct cgroup_subsys_state *mem_cgroup_css_from_folio(struct folio *folio); +struct cgroup_subsys_state *get_mem_cgroup_css_from_folio(struct folio *folio); ino_t page_cgroup_ino(struct page *page); static inline bool mem_cgroup_online(struct mem_cgroup *memcg) @@ -896,7 +878,7 @@ static inline bool mem_cgroup_online(struct mem_cgroup *memcg) } void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru, - int zid, int nr_pages); + int zid, long nr_pages); static inline unsigned long mem_cgroup_get_zone_lru_size(struct lruvec *lruvec, @@ -966,10 +948,15 @@ void count_memcg_events(struct mem_cgroup *memcg, enum vm_event_item idx, static inline void count_memcg_folio_events(struct folio *folio, enum vm_event_item idx, unsigned long nr) { - struct mem_cgroup *memcg = folio_memcg(folio); + struct mem_cgroup *memcg; - if (memcg) - count_memcg_events(memcg, idx, nr); + if (!folio_memcg_charged(folio)) + return; + + rcu_read_lock(); + memcg = folio_memcg(folio); + count_memcg_events(memcg, idx, nr); + rcu_read_unlock(); } static inline void count_memcg_events_mm(struct mm_struct *mm, @@ -1087,6 +1074,11 @@ static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg) return true; } +static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg) +{ + return true; +} + static inline bool mem_cgroup_disabled(void) { return true; @@ -1179,11 +1171,6 @@ static inline struct lruvec *folio_lruvec(struct folio *folio) return &pgdat->__lruvec; } -static inline -void lruvec_memcg_debug(struct lruvec *lruvec, struct folio *folio) -{ -} - static inline struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg) { return NULL; @@ -1242,6 +1229,7 @@ static inline struct lruvec *folio_lruvec_lock(struct folio *folio) { struct pglist_data *pgdat = folio_pgdat(folio); + rcu_read_lock(); spin_lock(&pgdat->__lruvec.lru_lock); return &pgdat->__lruvec; } @@ -1250,6 +1238,7 @@ static inline struct lruvec *folio_lruvec_lock_irq(struct folio *folio) { struct pglist_data *pgdat = folio_pgdat(folio); + rcu_read_lock(); spin_lock_irq(&pgdat->__lruvec.lru_lock); return &pgdat->__lruvec; } @@ -1259,6 +1248,7 @@ static inline struct lruvec *folio_lruvec_lock_irqsave(struct folio *folio, { struct pglist_data *pgdat = folio_pgdat(folio); + rcu_read_lock(); spin_lock_irqsave(&pgdat->__lruvec.lru_lock, *flagsp); return &pgdat->__lruvec; } @@ -1479,20 +1469,28 @@ static inline struct lruvec *parent_lruvec(struct lruvec *lruvec) return mem_cgroup_lruvec(memcg, lruvec_pgdat(lruvec)); } -static inline void unlock_page_lruvec(struct lruvec *lruvec) +static inline void lruvec_lock_irq(struct lruvec *lruvec) +{ + rcu_read_lock(); + spin_lock_irq(&lruvec->lru_lock); +} + +static inline void lruvec_unlock(struct lruvec *lruvec) { spin_unlock(&lruvec->lru_lock); + rcu_read_unlock(); } -static inline void unlock_page_lruvec_irq(struct lruvec *lruvec) +static inline void lruvec_unlock_irq(struct lruvec *lruvec) { spin_unlock_irq(&lruvec->lru_lock); + rcu_read_unlock(); } -static inline void unlock_page_lruvec_irqrestore(struct lruvec *lruvec, - unsigned long flags) +static inline void lruvec_unlock_irqrestore(struct lruvec *lruvec, unsigned long flags) { spin_unlock_irqrestore(&lruvec->lru_lock, flags); + rcu_read_unlock(); } /* Test requires a stable folio->memcg binding, see folio_memcg() */ @@ -1511,7 +1509,7 @@ static inline struct lruvec *folio_lruvec_relock_irq(struct folio *folio, if (folio_matches_lruvec(folio, locked_lruvec)) return locked_lruvec; - unlock_page_lruvec_irq(locked_lruvec); + lruvec_unlock_irq(locked_lruvec); } return folio_lruvec_lock_irq(folio); @@ -1525,7 +1523,7 @@ static inline void folio_lruvec_relock_irqsave(struct folio *folio, if (folio_matches_lruvec(folio, *lruvecp)) return; - unlock_page_lruvec_irqrestore(*lruvecp, *flags); + lruvec_unlock_irqrestore(*lruvecp, *flags); } *lruvecp = folio_lruvec_lock_irqsave(folio, flags); @@ -1549,9 +1547,14 @@ static inline void mem_cgroup_track_foreign_dirty(struct folio *folio, if (mem_cgroup_disabled()) return; + if (!folio_memcg_charged(folio)) + return; + + rcu_read_lock(); memcg = folio_memcg(folio); - if (unlikely(memcg && &memcg->css != wb->memcg_css)) + if (unlikely(&memcg->css != wb->memcg_css)) mem_cgroup_track_foreign_dirty_slowpath(folio, wb); + rcu_read_unlock(); } void mem_cgroup_flush_foreign(struct bdi_writeback *wb); diff --git a/include/linux/mfd/atmel-hlcdc.h b/include/linux/mfd/atmel-hlcdc.h index 80d675a03b39..07c2081867fd 100644 --- a/include/linux/mfd/atmel-hlcdc.h +++ b/include/linux/mfd/atmel-hlcdc.h @@ -75,6 +75,7 @@ */ struct atmel_hlcdc { struct regmap *regmap; + struct clk *lvds_pll_clk; struct clk *periph_clk; struct clk *sys_clk; struct clk *slow_clk; diff --git a/include/linux/mfd/bcm2835-pm.h b/include/linux/mfd/bcm2835-pm.h index f70a810c55f7..d2e17ab1dbfc 100644 --- a/include/linux/mfd/bcm2835-pm.h +++ b/include/linux/mfd/bcm2835-pm.h @@ -5,11 +5,18 @@ #include +enum bcm2835_soc { + BCM2835_PM_SOC_BCM2835, + BCM2835_PM_SOC_BCM2711, + BCM2835_PM_SOC_BCM2712, +}; + struct bcm2835_pm { struct device *dev; void __iomem *base; void __iomem *asb; void __iomem *rpivid_asb; + enum bcm2835_soc soc; }; #endif /* BCM2835_MFD_PM_H */ diff --git a/include/linux/mfd/cgbc.h b/include/linux/mfd/cgbc.h index badbec4c7033..91f501e76c8f 100644 --- a/include/linux/mfd/cgbc.h +++ b/include/linux/mfd/cgbc.h @@ -26,8 +26,8 @@ struct cgbc_version { * @io_cmd: Pointer to the command IO memory * @session: Session id returned by the Board Controller * @dev: Pointer to kernel device structure - * @cgbc_version: Board Controller version structure - * @mutex: Board Controller mutex + * @version: Board Controller version structure + * @lock: Board Controller mutex */ struct cgbc_device_data { void __iomem *io_session; diff --git a/include/linux/mfd/kempld.h b/include/linux/mfd/kempld.h index 2dbd80abfd1d..5d75071eaaea 100644 --- a/include/linux/mfd/kempld.h +++ b/include/linux/mfd/kempld.h @@ -98,10 +98,10 @@ struct kempld_device_data { /** * struct kempld_platform_data - PLD hardware configuration structure * @pld_clock: PLD clock frequency - * @gpio_base GPIO base pin number + * @gpio_base: GPIO base pin number * @ioresource: IO addresses of the PLD - * @get_mutex: PLD specific get_mutex callback - * @release_mutex: PLD specific release_mutex callback + * @get_hardware_mutex: PLD specific get_mutex callback + * @release_hardware_mutex: PLD specific release_mutex callback * @get_info: PLD specific get_info callback * @register_cells: PLD specific register_cells callback */ diff --git a/include/linux/mfd/lpc_ich.h b/include/linux/mfd/lpc_ich.h index 1fbda1f8967d..1819aa743c5c 100644 --- a/include/linux/mfd/lpc_ich.h +++ b/include/linux/mfd/lpc_ich.h @@ -37,4 +37,6 @@ struct lpc_ich_info { u8 use_gpio; }; +extern const struct software_node lpc_ich_gpio_swnode; + #endif diff --git a/include/linux/mfd/max77759.h b/include/linux/mfd/max77759.h index c6face34e385..ec19be952877 100644 --- a/include/linux/mfd/max77759.h +++ b/include/linux/mfd/max77759.h @@ -59,35 +59,65 @@ #define MAX77759_MAXQ_REG_AP_DATAIN0 0xb1 #define MAX77759_MAXQ_REG_UIC_SWRST 0xe0 -#define MAX77759_CHGR_REG_CHG_INT 0xb0 -#define MAX77759_CHGR_REG_CHG_INT2 0xb1 -#define MAX77759_CHGR_REG_CHG_INT_MASK 0xb2 -#define MAX77759_CHGR_REG_CHG_INT2_MASK 0xb3 -#define MAX77759_CHGR_REG_CHG_INT_OK 0xb4 -#define MAX77759_CHGR_REG_CHG_DETAILS_00 0xb5 -#define MAX77759_CHGR_REG_CHG_DETAILS_01 0xb6 -#define MAX77759_CHGR_REG_CHG_DETAILS_02 0xb7 -#define MAX77759_CHGR_REG_CHG_DETAILS_03 0xb8 -#define MAX77759_CHGR_REG_CHG_CNFG_00 0xb9 -#define MAX77759_CHGR_REG_CHG_CNFG_01 0xba -#define MAX77759_CHGR_REG_CHG_CNFG_02 0xbb -#define MAX77759_CHGR_REG_CHG_CNFG_03 0xbc -#define MAX77759_CHGR_REG_CHG_CNFG_04 0xbd -#define MAX77759_CHGR_REG_CHG_CNFG_05 0xbe -#define MAX77759_CHGR_REG_CHG_CNFG_06 0xbf -#define MAX77759_CHGR_REG_CHG_CNFG_07 0xc0 -#define MAX77759_CHGR_REG_CHG_CNFG_08 0xc1 -#define MAX77759_CHGR_REG_CHG_CNFG_09 0xc2 -#define MAX77759_CHGR_REG_CHG_CNFG_10 0xc3 -#define MAX77759_CHGR_REG_CHG_CNFG_11 0xc4 -#define MAX77759_CHGR_REG_CHG_CNFG_12 0xc5 -#define MAX77759_CHGR_REG_CHG_CNFG_13 0xc6 -#define MAX77759_CHGR_REG_CHG_CNFG_14 0xc7 -#define MAX77759_CHGR_REG_CHG_CNFG_15 0xc8 -#define MAX77759_CHGR_REG_CHG_CNFG_16 0xc9 -#define MAX77759_CHGR_REG_CHG_CNFG_17 0xca -#define MAX77759_CHGR_REG_CHG_CNFG_18 0xcb -#define MAX77759_CHGR_REG_CHG_CNFG_19 0xcc +#define MAX77759_CHGR_REG_CHG_INT 0xb0 +#define MAX77759_CHGR_REG_CHG_INT_AICL BIT(7) +#define MAX77759_CHGR_REG_CHG_INT_CHGIN BIT(6) +#define MAX77759_CHGR_REG_CHG_INT_WCIN BIT(5) +#define MAX77759_CHGR_REG_CHG_INT_CHG BIT(4) +#define MAX77759_CHGR_REG_CHG_INT_BAT BIT(3) +#define MAX77759_CHGR_REG_CHG_INT_INLIM BIT(2) +#define MAX77759_CHGR_REG_CHG_INT_THM2 BIT(1) +#define MAX77759_CHGR_REG_CHG_INT_BYP BIT(0) +#define MAX77759_CHGR_REG_CHG_INT2 0xb1 +#define MAX77759_CHGR_REG_CHG_INT2_INSEL BIT(7) +#define MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO1 BIT(6) +#define MAX77759_CHGR_REG_CHG_INT2_SYS_UVLO2 BIT(5) +#define MAX77759_CHGR_REG_CHG_INT2_BAT_OILO BIT(4) +#define MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CC BIT(3) +#define MAX77759_CHGR_REG_CHG_INT2_CHG_STA_CV BIT(2) +#define MAX77759_CHGR_REG_CHG_INT2_CHG_STA_TO BIT(1) +#define MAX77759_CHGR_REG_CHG_INT2_CHG_STA_DONE BIT(0) +#define MAX77759_CHGR_REG_CHG_INT_MASK 0xb2 +#define MAX77759_CHGR_REG_CHG_INT2_MASK 0xb3 +#define MAX77759_CHGR_REG_CHG_INT_OK 0xb4 +#define MAX77759_CHGR_REG_CHG_DETAILS_00 0xb5 +#define MAX77759_CHGR_REG_CHG_DETAILS_00_CHGIN_DTLS GENMASK(6, 5) +#define MAX77759_CHGR_REG_CHG_DETAILS_01 0xb6 +#define MAX77759_CHGR_REG_CHG_DETAILS_01_BAT_DTLS GENMASK(6, 4) +#define MAX77759_CHGR_REG_CHG_DETAILS_01_CHG_DTLS GENMASK(3, 0) +#define MAX77759_CHGR_REG_CHG_DETAILS_02 0xb7 +#define MAX77759_CHGR_REG_CHG_DETAILS_02_CHGIN_STS BIT(5) +#define MAX77759_CHGR_REG_CHG_DETAILS_03 0xb8 +#define MAX77759_CHGR_REG_CHG_CNFG_00 0xb9 +#define MAX77759_CHGR_REG_CHG_CNFG_00_MODE GENMASK(3, 0) +#define MAX77759_CHGR_REG_CHG_CNFG_01 0xba +#define MAX77759_CHGR_REG_CHG_CNFG_02 0xbb +#define MAX77759_CHGR_REG_CHG_CNFG_02_CHGCC GENMASK(5, 0) +#define MAX77759_CHGR_REG_CHG_CNFG_03 0xbc +#define MAX77759_CHGR_REG_CHG_CNFG_04 0xbd +#define MAX77759_CHGR_REG_CHG_CNFG_04_CHG_CV_PRM GENMASK(5, 0) +#define MAX77759_CHGR_REG_CHG_CNFG_05 0xbe +#define MAX77759_CHGR_REG_CHG_CNFG_06 0xbf +#define MAX77759_CHGR_REG_CHG_CNFG_06_CHGPROT GENMASK(3, 2) +#define MAX77759_CHGR_REG_CHG_CNFG_07 0xc0 +#define MAX77759_CHGR_REG_CHG_CNFG_08 0xc1 +#define MAX77759_CHGR_REG_CHG_CNFG_09 0xc2 +#define MAX77759_CHGR_REG_CHG_CNFG_09_CHGIN_ILIM GENMASK(6, 0) +#define MAX77759_CHGR_REG_CHG_CNFG_10 0xc3 +#define MAX77759_CHGR_REG_CHG_CNFG_11 0xc4 +#define MAX77759_CHGR_REG_CHG_CNFG_12 0xc5 +/* Wireless Charging input channel select */ +#define MAX77759_CHGR_REG_CHG_CNFG_12_WCINSEL BIT(6) +/* CHGIN/USB input channel select */ +#define MAX77759_CHGR_REG_CHG_CNFG_12_CHGINSEL BIT(5) +#define MAX77759_CHGR_REG_CHG_CNFG_13 0xc6 +#define MAX77759_CHGR_REG_CHG_CNFG_14 0xc7 +#define MAX77759_CHGR_REG_CHG_CNFG_15 0xc8 +#define MAX77759_CHGR_REG_CHG_CNFG_16 0xc9 +#define MAX77759_CHGR_REG_CHG_CNFG_17 0xca +#define MAX77759_CHGR_REG_CHG_CNFG_18 0xcb +#define MAX77759_CHGR_REG_CHG_CNFG_18_WDTEN BIT(0) +#define MAX77759_CHGR_REG_CHG_CNFG_19 0xcc /* MaxQ opcodes for max77759_maxq_command() */ #define MAX77759_MAXQ_OPCODE_MAXLENGTH (MAX77759_MAXQ_REG_AP_DATAOUT32 - \ @@ -101,6 +131,84 @@ #define MAX77759_MAXQ_OPCODE_USER_SPACE_READ 0x81 #define MAX77759_MAXQ_OPCODE_USER_SPACE_WRITE 0x82 +/** + * enum max77759_chgr_chgin_dtls_status - Charger Input Status + * @MAX77759_CHGR_CHGIN_DTLS_VBUS_UNDERVOLTAGE: + * Charger input voltage (Vchgin) < Under Voltage Threshold (Vuvlo) + * @MAX77759_CHGR_CHGIN_DTLS_VBUS_MARGINAL_VOLTAGE: + * Vchgin > Vuvlo and Vchgin < (Battery Voltage (Vbatt) + system voltage (Vsys)) + * @MAX77759_CHGR_CHGIN_DTLS_VBUS_OVERVOLTAGE: + * Vchgin > Over Voltage threshold (Vovlo) + * @MAX77759_CHGR_CHGIN_DTLS_VBUS_VALID: + * Vchgin > Vuvlo, Vchgin < Vovlo and Vchgin > (Vsys + Vbatt) + */ +enum max77759_chgr_chgin_dtls_status { + MAX77759_CHGR_CHGIN_DTLS_VBUS_UNDERVOLTAGE, + MAX77759_CHGR_CHGIN_DTLS_VBUS_MARGINAL_VOLTAGE, + MAX77759_CHGR_CHGIN_DTLS_VBUS_OVERVOLTAGE, + MAX77759_CHGR_CHGIN_DTLS_VBUS_VALID, +}; + +/** + * enum max77759_chgr_bat_dtls_states - Battery Details + * @MAX77759_CHGR_BAT_DTLS_NO_BATT_CHG_SUSP: No battery and the charger suspended + * @MAX77759_CHGR_BAT_DTLS_DEAD_BATTERY: Vbatt < Vtrickle + * @MAX77759_CHGR_BAT_DTLS_BAT_CHG_TIMER_FAULT: Charging suspended due to timer fault + * @MAX77759_CHGR_BAT_DTLS_BAT_OKAY: Battery okay and Vbatt > Min Sys Voltage (Vsysmin) + * @MAX77759_CHGR_BAT_DTLS_BAT_UNDERVOLTAGE: Battery is okay. Vtrickle < Vbatt < Vsysmin + * @MAX77759_CHGR_BAT_DTLS_BAT_OVERVOLTAGE: Battery voltage > Overvoltage threshold + * @MAX77759_CHGR_BAT_DTLS_BAT_OVERCURRENT: Battery current exceeds overcurrent threshold + * @MAX77759_CHGR_BAT_DTLS_BAT_ONLY_MODE: Battery only mode and battery level not available + */ +enum max77759_chgr_bat_dtls_states { + MAX77759_CHGR_BAT_DTLS_NO_BATT_CHG_SUSP, + MAX77759_CHGR_BAT_DTLS_DEAD_BATTERY, + MAX77759_CHGR_BAT_DTLS_BAT_CHG_TIMER_FAULT, + MAX77759_CHGR_BAT_DTLS_BAT_OKAY, + MAX77759_CHGR_BAT_DTLS_BAT_UNDERVOLTAGE, + MAX77759_CHGR_BAT_DTLS_BAT_OVERVOLTAGE, + MAX77759_CHGR_BAT_DTLS_BAT_OVERCURRENT, + MAX77759_CHGR_BAT_DTLS_BAT_ONLY_MODE, +}; + +/** + * enum max77759_chgr_chg_dtls_states - Charger Details + * @MAX77759_CHGR_CHG_DTLS_PREQUAL: Charger in prequalification mode + * @MAX77759_CHGR_CHG_DTLS_CC: Charger in fast charge const curr mode + * @MAX77759_CHGR_CHG_DTLS_CV: Charger in fast charge const voltage mode + * @MAX77759_CHGR_CHG_DTLS_TO: Charger is in top off mode + * @MAX77759_CHGR_CHG_DTLS_DONE: Charger is done + * @MAX77759_CHGR_CHG_DTLS_RSVD_1: Reserved + * @MAX77759_CHGR_CHG_DTLS_TIMER_FAULT: Charger is in timer fault mode + * @MAX77759_CHGR_CHG_DTLS_SUSP_BATT_THM: Charger is suspended as battery removal detected + * @MAX77759_CHGR_CHG_DTLS_OFF: Charger is off. Input invalid or charger disabled + * @MAX77759_CHGR_CHG_DTLS_RSVD_2: Reserved + * @MAX77759_CHGR_CHG_DTLS_RSVD_3: Reserved + * @MAX77759_CHGR_CHG_DTLS_OFF_WDOG_TIMER: Charger is off as watchdog timer expired + * @MAX77759_CHGR_CHG_DTLS_SUSP_JEITA: Charger is in JEITA control mode + */ +enum max77759_chgr_chg_dtls_states { + MAX77759_CHGR_CHG_DTLS_PREQUAL, + MAX77759_CHGR_CHG_DTLS_CC, + MAX77759_CHGR_CHG_DTLS_CV, + MAX77759_CHGR_CHG_DTLS_TO, + MAX77759_CHGR_CHG_DTLS_DONE, + MAX77759_CHGR_CHG_DTLS_RSVD_1, + MAX77759_CHGR_CHG_DTLS_TIMER_FAULT, + MAX77759_CHGR_CHG_DTLS_SUSP_BATT_THM, + MAX77759_CHGR_CHG_DTLS_OFF, + MAX77759_CHGR_CHG_DTLS_RSVD_2, + MAX77759_CHGR_CHG_DTLS_RSVD_3, + MAX77759_CHGR_CHG_DTLS_OFF_WDOG_TIMER, + MAX77759_CHGR_CHG_DTLS_SUSP_JEITA, +}; + +enum max77759_chgr_mode { + MAX77759_CHGR_MODE_OFF, + MAX77759_CHGR_MODE_CHG_BUCK_ON = 0x5, + MAX77759_CHGR_MODE_OTG_BOOST_ON = 0xA, +}; + /** * struct max77759 - core max77759 internal data structure * diff --git a/include/linux/mfd/mt6397/core.h b/include/linux/mfd/mt6397/core.h index b774c3a4bb62..340fc72e22aa 100644 --- a/include/linux/mfd/mt6397/core.h +++ b/include/linux/mfd/mt6397/core.h @@ -12,9 +12,9 @@ enum chip_id { MT6323_CHIP_ID = 0x23, - MT6328_CHIP_ID = 0x30, - MT6331_CHIP_ID = 0x20, - MT6332_CHIP_ID = 0x20, + MT6328_CHIP_ID = 0x28, + MT6331_CHIP_ID = 0x31, + MT6332_CHIP_ID = 0x32, MT6357_CHIP_ID = 0x57, MT6358_CHIP_ID = 0x58, MT6359_CHIP_ID = 0x59, diff --git a/include/linux/mfd/rsmu.h b/include/linux/mfd/rsmu.h index 0379aa207428..2f27386a7122 100644 --- a/include/linux/mfd/rsmu.h +++ b/include/linux/mfd/rsmu.h @@ -19,7 +19,6 @@ enum rsmu_type { }; /** - * * struct rsmu_ddata - device data structure for sub devices. * * @dev: i2c/spi device. diff --git a/include/linux/mfd/si476x-core.h b/include/linux/mfd/si476x-core.h index dd95c37ca134..e913b2cdf77d 100644 --- a/include/linux/mfd/si476x-core.h +++ b/include/linux/mfd/si476x-core.h @@ -77,6 +77,7 @@ enum si476x_power_state { * underlying "core" device which all the MFD cell-devices use. * * @client: Actual I2C client used to transfer commands to the chip. + * @regmap: Regmap for accessing the device registers * @chip_id: Last digit of the chip model(E.g. "1" for SI4761) * @cells: MFD cell devices created by this driver. * @cmd_lock: Mutex used to serialize all the requests to the core @@ -100,16 +101,18 @@ enum si476x_power_state { * @stc: Similar to @cts, but for the STC bit of the status value. * @power_up_parameters: Parameters used as argument for POWER_UP * command when the device is started. - * @state: Current power state of the device. - * @supplues: Structure containing handles to all power supplies used + * @power_state: Current power state of the device. + * @supplies: Structure containing handles to all power supplies used * by the device (NULL ones are ignored). * @gpio_reset: GPIO pin connectet to the RSTB pin of the chip. * @pinmux: Chip's configurable pins configuration. * @diversity_mode: Chips role when functioning in diversity mode. + * @is_alive: Chip is initialized and active. * @status_monitor: Polling worker used in polling use case scenarion * (when IRQ is not avalible). * @revision: Chip's running firmware revision number(Used for correct * command set support). + * @rds_fifo_depth: RDS FIFO size: 20 for IRQ mode or 5 for polling mode. */ struct si476x_core { @@ -166,6 +169,7 @@ static inline struct si476x_core *i2c_mfd_cell_to_core(struct device *dev) /** * si476x_core_lock() - lock the core device to get an exclusive access * to it. + * @core: Core device structure */ static inline void si476x_core_lock(struct si476x_core *core) { @@ -175,6 +179,7 @@ static inline void si476x_core_lock(struct si476x_core *core) /** * si476x_core_unlock() - unlock the core device to relinquish an * exclusive access to it. + * @core: Core device structure */ static inline void si476x_core_unlock(struct si476x_core *core) { @@ -246,9 +251,10 @@ static inline int si476x_to_v4l2(struct si476x_core *core, u16 freq) * struct si476x_func_info - structure containing result of the * FUNC_INFO command. * + * @firmware: Firmware version numbers. * @firmware.major: Firmware major number. * @firmware.minor[...]: Firmware minor numbers. - * @patch_id: + * @patch_id: Firmware patch level. * @func: Mode tuner is working in. */ struct si476x_func_info { @@ -318,8 +324,9 @@ enum si476x_smoothmetrics { * @tp: Current channel's TP flag. * @pty: Current channel's PTY code. * @pi: Current channel's PI code. - * @rdsfifoused: Number of blocks remaining in the RDS FIFO (0 if - * empty). + * @rdsfifoused: Number of blocks remaining in the RDS FIFO (0 if empty). + * @ble: + * @rds: RDS data descriptor */ struct si476x_rds_status_report { bool rdstpptyint, rdspiint, rdssyncint, rdsfifoint; diff --git a/include/linux/mhi.h b/include/linux/mhi.h index 88ccb3e14f48..fb3ba639f4f8 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -85,17 +85,33 @@ enum mhi_ch_type { MHI_CH_TYPE_INBOUND_COALESCED = 3, }; +/** + * struct mhi_buf - MHI Buffer description + * @buf: Virtual address of the buffer + * @name: Buffer label. For offload channel, configurations name must be: + * ECA - Event context array data + * CCA - Channel context array data + * @dma_addr: IOMMU address of the buffer + * @len: # of bytes + */ +struct mhi_buf { + void *buf; + const char *name; + dma_addr_t dma_addr; + size_t len; +}; + /** * struct image_info - Firmware and RDDM table * @mhi_buf: Buffer for firmware and RDDM table * @entries: # of entries in table */ struct image_info { - struct mhi_buf *mhi_buf; /* private: from internal.h */ struct bhi_vec_entry *bhi_vec; /* public: */ u32 entries; + struct mhi_buf mhi_buf[] __counted_by(entries); }; /** @@ -488,22 +504,6 @@ struct mhi_result { int transaction_status; }; -/** - * struct mhi_buf - MHI Buffer description - * @buf: Virtual address of the buffer - * @name: Buffer label. For offload channel, configurations name must be: - * ECA - Event context array data - * CCA - Channel context array data - * @dma_addr: IOMMU address of the buffer - * @len: # of bytes - */ -struct mhi_buf { - void *buf; - const char *name; - dma_addr_t dma_addr; - size_t len; -}; - /** * struct mhi_driver - Structure representing a MHI client driver * @probe: CB function for client driver probe function diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index e1ded9cf0f70..04b96c5abb57 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -705,23 +705,12 @@ struct mlx5_st; enum { MLX5_PROF_MASK_QP_SIZE = (u64)1 << 0, - MLX5_PROF_MASK_MR_CACHE = (u64)1 << 1, -}; - -enum { - MKEY_CACHE_LAST_STD_ENTRY = 20, - MLX5_IMR_KSM_CACHE_ENTRY, - MAX_MKEY_CACHE_ENTRIES }; struct mlx5_profile { u64 mask; u8 log_max_qp; u8 num_cmd_caches; - struct { - int size; - int limit; - } mr_cache[MAX_MKEY_CACHE_ENTRIES]; }; struct mlx5_hca_cap { diff --git a/include/linux/mm.h b/include/linux/mm.h index 8260e28205e9..af23453e9dbd 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -758,6 +758,8 @@ struct vm_fault { */ }; +struct vm_uffd_ops; + /* * These are the virtual MM functions - opening of an area, closing and * unmapping it (needed to keep files on disk up-to-date etc), pointer @@ -865,6 +867,9 @@ struct vm_operations_struct { struct page *(*find_normal_page)(struct vm_area_struct *vma, unsigned long addr); #endif /* CONFIG_FIND_NORMAL_PAGE */ +#ifdef CONFIG_USERFAULTFD + const struct vm_uffd_ops *uffd_ops; +#endif }; #ifdef CONFIG_NUMA_BALANCING @@ -3928,9 +3933,6 @@ extern unsigned long free_reserved_area(void *start, void *end, extern void adjust_managed_page_count(struct page *page, long count); -extern void reserve_bootmem_region(phys_addr_t start, - phys_addr_t end, int nid); - /* Free the reserved page into the buddy system, so it gets managed. */ void free_reserved_page(struct page *page); @@ -4389,7 +4391,7 @@ static inline void mmap_action_map_kernel_pages_full(struct vm_area_desc *desc, int mmap_action_prepare(struct vm_area_desc *desc); int mmap_action_complete(struct vm_area_struct *vma, - struct mmap_action *action); + struct mmap_action *action, bool is_compat); /* Look up the first VMA which exactly match the interval vm_start ... vm_end */ static inline struct vm_area_struct *find_exact_vma(struct mm_struct *mm, diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h index 7fc2ced00f8f..a171070e15f0 100644 --- a/include/linux/mm_inline.h +++ b/include/linux/mm_inline.h @@ -348,6 +348,8 @@ void lruvec_add_folio(struct lruvec *lruvec, struct folio *folio) { enum lru_list lru = folio_lru_list(folio); + VM_WARN_ON_ONCE_FOLIO(!folio_matches_lruvec(folio, lruvec), folio); + if (lru_gen_add_folio(lruvec, folio, false)) return; @@ -362,6 +364,8 @@ void lruvec_add_folio_tail(struct lruvec *lruvec, struct folio *folio) { enum lru_list lru = folio_lru_list(folio); + VM_WARN_ON_ONCE_FOLIO(!folio_matches_lruvec(folio, lruvec), folio); + if (lru_gen_add_folio(lruvec, folio, true)) return; @@ -376,6 +380,8 @@ void lruvec_del_folio(struct lruvec *lruvec, struct folio *folio) { enum lru_list lru = folio_lru_list(folio); + VM_WARN_ON_ONCE_FOLIO(!folio_matches_lruvec(folio, lruvec), folio); + if (lru_gen_del_folio(lruvec, folio, false)) return; diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 3bcdda226a91..9adb2ad21da5 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -694,6 +694,9 @@ void lru_gen_online_memcg(struct mem_cgroup *memcg); void lru_gen_offline_memcg(struct mem_cgroup *memcg); void lru_gen_release_memcg(struct mem_cgroup *memcg); void lru_gen_soft_reclaim(struct mem_cgroup *memcg, int nid); +void max_lru_gen_memcg(struct mem_cgroup *memcg, int nid); +bool recheck_lru_gen_max_memcg(struct mem_cgroup *memcg, int nid); +void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid); #else /* !CONFIG_LRU_GEN */ @@ -735,6 +738,20 @@ static inline void lru_gen_soft_reclaim(struct mem_cgroup *memcg, int nid) { } +static inline void max_lru_gen_memcg(struct mem_cgroup *memcg, int nid) +{ +} + +static inline bool recheck_lru_gen_max_memcg(struct mem_cgroup *memcg, int nid) +{ + return true; +} + +static inline +void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid) +{ +} + #endif /* CONFIG_LRU_GEN */ struct lruvec { @@ -2053,21 +2070,16 @@ static inline struct mem_section *__nr_to_section(unsigned long nr) extern size_t mem_section_usage_size(void); /* - * We use the lower bits of the mem_map pointer to store - * a little bit of information. The pointer is calculated - * as mem_map - section_nr_to_pfn(pnum). The result is - * aligned to the minimum alignment of the two values: - * 1. All mem_map arrays are page-aligned. - * 2. section_nr_to_pfn() always clears PFN_SECTION_SHIFT - * lowest bits. PFN_SECTION_SHIFT is arch-specific - * (equal SECTION_SIZE_BITS - PAGE_SHIFT), and the - * worst combination is powerpc with 256k pages, - * which results in PFN_SECTION_SHIFT equal 6. - * To sum it up, at least 6 bits are available on all architectures. - * However, we can exceed 6 bits on some other architectures except - * powerpc (e.g. 15 bits are available on x86_64, 13 bits are available - * with the worst case of 64K pages on arm64) if we make sure the - * exceeded bit is not applicable to powerpc. + * We use the lower bits of the mem_map pointer to store a little bit of + * information. The pointer is calculated as mem_map - section_nr_to_pfn(). + * The result is aligned to the minimum alignment of the two values: + * + * 1. All mem_map arrays are page-aligned. + * 2. section_nr_to_pfn() always clears PFN_SECTION_SHIFT lowest bits. + * + * We always expect a single section to cover full pages. Therefore, + * we can safely assume that PFN_SECTION_SHIFT is large enough to + * accommodate SECTION_MAP_LAST_BIT. We use BUILD_BUG_ON() to ensure this. */ enum { SECTION_MARKED_PRESENT_BIT, diff --git a/include/linux/mroute_base.h b/include/linux/mroute_base.h index cf3374580f74..5d75cc5b057e 100644 --- a/include/linux/mroute_base.h +++ b/include/linux/mroute_base.h @@ -226,6 +226,7 @@ struct mr_table_ops { /** * struct mr_table - a multicast routing table + * @work: used for table destruction * @list: entry within a list of multicast routing tables * @net: net where this table belongs * @ops: protocol specific operations @@ -243,6 +244,7 @@ struct mr_table_ops { * @mroute_reg_vif_num: PIM-device vif index */ struct mr_table { + struct rcu_work work; struct list_head list; possible_net_t net; struct mr_table_ops ops; @@ -274,6 +276,7 @@ void vif_device_init(struct vif_device *v, unsigned short flags, unsigned short get_iflink_mask); +void mr_table_free(struct mr_table *mrt); struct mr_table * mr_table_alloc(struct net *net, u32 id, struct mr_table_ops *ops, diff --git a/include/linux/mtd/concat.h b/include/linux/mtd/concat.h index d6f653e07426..f8d4d6ac1fc1 100644 --- a/include/linux/mtd/concat.h +++ b/include/linux/mtd/concat.h @@ -9,6 +9,18 @@ #define MTD_CONCAT_H +/* + * Our storage structure: + * Subdev points to an array of pointers to struct mtd_info objects + * which is allocated along with this structure + * + */ +struct mtd_concat { + struct mtd_info mtd; + int num_subdev; + struct mtd_info *subdev[]; +}; + struct mtd_info *mtd_concat_create( struct mtd_info *subdev[], /* subdevices to concatenate */ int num_devs, /* number of subdevices */ @@ -16,5 +28,54 @@ struct mtd_info *mtd_concat_create( void mtd_concat_destroy(struct mtd_info *mtd); -#endif +/** + * mtd_virt_concat_node_create - Create a component for concatenation + * + * Returns a positive number representing the no. of devices found for + * concatenation, or a negative error code. + * + * List all the devices for concatenations found in DT and create a + * component for concatenation. + */ +int mtd_virt_concat_node_create(void); +/** + * mtd_virt_concat_add - add mtd_info object to the list of subdevices for concatenation + * @mtd: pointer to new MTD device info structure + * + * Returns true if the mtd_info object is added successfully else returns false. + * + * The mtd_info object is added to the list of subdevices for concatenation. + * It returns true if a match is found, and false if all subdevices have + * already been added or if the mtd_info object does not match any of the + * intended MTD devices. + */ +bool mtd_virt_concat_add(struct mtd_info *mtd); + +/** + * mtd_virt_concat_create_join - Create and register the concatenated MTD device + * + * Returns 0 on succes, or a negative error code. + * + * Creates and registers the concatenated MTD device + */ +int mtd_virt_concat_create_join(void); + +/** + * mtd_virt_concat_destroy - Remove the concat that includes a specific mtd device + * as one of its components. + * @mtd: pointer to MTD device info structure. + * + * Returns 0 on succes, or a negative error code. + * + * If the mtd_info object is part of a concatenated device, all other MTD devices + * within that concat are registered individually. The concatenated device is then + * removed, along with its concatenation component. + * + */ +int mtd_virt_concat_destroy(struct mtd_info *mtd); + +void mtd_virt_concat_destroy_joins(void); +void mtd_virt_concat_destroy_items(void); + +#endif diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 6a024cf1c53a..782984ba3a20 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -290,6 +290,12 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) +#define SPINAND_PAGE_READ_PACKED_8D_8D_0_OP(addr) \ + SPI_MEM_OP(SPI_MEM_DTR_OP_PACKED_CMD(0x13, addr >> 16, 8), \ + SPI_MEM_DTR_OP_ADDR(2, addr & 0xffff, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + #define SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(addr, ndummy, buf, len, freq) \ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9d, 8), \ SPI_MEM_DTR_OP_ADDR(2, addr, 8), \ @@ -477,11 +483,13 @@ struct spinand_ecc_info { const struct mtd_ooblayout_ops *ooblayout; }; -#define SPINAND_HAS_QE_BIT BIT(0) -#define SPINAND_HAS_CR_FEAT_BIT BIT(1) +/* SPI NAND flags */ +#define SPINAND_HAS_QE_BIT BIT(0) +#define SPINAND_HAS_CR_FEAT_BIT BIT(1) #define SPINAND_HAS_PROG_PLANE_SELECT_BIT BIT(2) #define SPINAND_HAS_READ_PLANE_SELECT_BIT BIT(3) #define SPINAND_NO_RAW_ACCESS BIT(4) +#define SPINAND_ODTR_PACKED_PAGE_READ BIT(5) /** * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine structure diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 7969fcdd5ac4..0e1e581efc5a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -162,7 +162,7 @@ static inline bool dev_xmit_complete(int rc) #if defined(CONFIG_HYPERV_NET) # define LL_MAX_HEADER 128 -#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) +#elif defined(CONFIG_WLAN) # if defined(CONFIG_MAC80211_MESH) # define LL_MAX_HEADER 128 # else @@ -1119,6 +1119,16 @@ struct netdev_net_notifier { * This function is called device changes address list filtering. * If driver handles unicast address filtering, it should set * IFF_UNICAST_FLT in its priv_flags. + * Cannot sleep, called with netif_addr_lock_bh held. + * Deprecated in favor of ndo_set_rx_mode_async. + * + * void (*ndo_set_rx_mode_async)(struct net_device *dev, + * struct netdev_hw_addr_list *uc, + * struct netdev_hw_addr_list *mc); + * Async version of ndo_set_rx_mode which runs in process context + * with rtnl_lock and netdev_lock_ops(dev) held. The uc/mc parameters + * are snapshots of the address lists - iterate with + * netdev_hw_addr_list_for_each(ha, uc). * * int (*ndo_set_mac_address)(struct net_device *dev, void *addr); * This function is called when the Media Access Control address @@ -1439,6 +1449,10 @@ struct net_device_ops { void (*ndo_change_rx_flags)(struct net_device *dev, int flags); void (*ndo_set_rx_mode)(struct net_device *dev); + void (*ndo_set_rx_mode_async)( + struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc); int (*ndo_set_mac_address)(struct net_device *dev, void *addr); int (*ndo_validate_addr)(struct net_device *dev); @@ -1903,6 +1917,9 @@ enum netdev_reg_state { * has been enabled due to the need to listen to * additional unicast addresses in a device that * does not implement ndo_set_rx_mode() + * @rx_mode_node: List entry for rx_mode work processing + * @rx_mode_tracker: Refcount tracker for rx_mode work + * @rx_mode_addr_cache: Recycled snapshot entries for rx_mode work * @uc: unicast mac addresses * @mc: multicast mac addresses * @dev_addrs: list of device hw addresses @@ -2294,6 +2311,9 @@ struct net_device { unsigned int promiscuity; unsigned int allmulti; bool uc_promisc; + struct list_head rx_mode_node; + netdevice_tracker rx_mode_tracker; + struct netdev_hw_addr_list rx_mode_addr_cache; #ifdef CONFIG_LOCKDEP unsigned char nested_level; #endif @@ -2316,9 +2336,6 @@ struct net_device { #if IS_ENABLED(CONFIG_ATALK) void *atalk_ptr; #endif -#if IS_ENABLED(CONFIG_AX25) - struct ax25_dev __rcu *ax25_ptr; -#endif #if IS_ENABLED(CONFIG_CFG80211) struct wireless_dev *ieee80211_ptr; #endif @@ -5004,6 +5021,14 @@ void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list, int (*unsync)(struct net_device *, const unsigned char *)); void __hw_addr_init(struct netdev_hw_addr_list *list); +void __hw_addr_flush(struct netdev_hw_addr_list *list); +int __hw_addr_list_snapshot(struct netdev_hw_addr_list *snap, + const struct netdev_hw_addr_list *list, + int addr_len, struct netdev_hw_addr_list *cache); +void __hw_addr_list_reconcile(struct netdev_hw_addr_list *real_list, + struct netdev_hw_addr_list *work, + struct netdev_hw_addr_list *ref, int addr_len, + struct netdev_hw_addr_list *cache); /* Functions used for device addresses handling */ void dev_addr_mod(struct net_device *dev, unsigned int offset, diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 77c778d84d4c..5a1c5c336fa4 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -146,6 +146,9 @@ struct xt_match { /* Called when user tries to insert an entry of this type. */ int (*checkentry)(const struct xt_mtchk_param *); + /* Called to validate hooks based on the match configuration. */ + int (*check_hooks)(const struct xt_mtchk_param *); + /* Called when entry of this type deleted. */ void (*destroy)(const struct xt_mtdtor_param *); #ifdef CONFIG_NETFILTER_XTABLES_COMPAT @@ -187,6 +190,9 @@ struct xt_target { /* Should return 0 on success or an error code otherwise (-Exxxx). */ int (*checkentry)(const struct xt_tgchk_param *); + /* Called to validate hooks based on the target configuration. */ + int (*check_hooks)(const struct xt_tgchk_param *); + /* Called when entry of this type deleted. */ void (*destroy)(const struct xt_tgdtor_param *); #ifdef CONFIG_NETFILTER_XTABLES_COMPAT @@ -279,8 +285,10 @@ bool xt_find_jump_offset(const unsigned int *offsets, int xt_check_proc_name(const char *name, unsigned int size); +int xt_check_hooks_match(struct xt_mtchk_param *par); int xt_check_match(struct xt_mtchk_param *, unsigned int size, u16 proto, bool inv_proto); +int xt_check_hooks_target(struct xt_tgchk_param *par); int xt_check_target(struct xt_tgchk_param *, unsigned int size, u16 proto, bool inv_proto); @@ -297,9 +305,11 @@ struct xt_counters *xt_counters_alloc(unsigned int counters); struct xt_table *xt_register_table(struct net *net, const struct xt_table *table, + const struct nf_hook_ops *template_ops, struct xt_table_info *bootstrap, struct xt_table_info *newinfo); -void *xt_unregister_table(struct xt_table *table); +void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); +struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name); struct xt_table_info *xt_replace_table(struct xt_table *table, unsigned int num_counters, diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h index a40aaf645fa4..05631a25e622 100644 --- a/include/linux/netfilter_arp/arp_tables.h +++ b/include/linux/netfilter_arp/arp_tables.h @@ -53,7 +53,6 @@ int arpt_register_table(struct net *net, const struct xt_table *table, const struct arpt_replace *repl, const struct nf_hook_ops *ops); void arpt_unregister_table(struct net *net, const char *name); -void arpt_unregister_table_pre_exit(struct net *net, const char *name); extern unsigned int arpt_do_table(void *priv, struct sk_buff *skb, const struct nf_hook_state *state); diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h index 132b0e4a6d4d..13593391d605 100644 --- a/include/linux/netfilter_ipv4/ip_tables.h +++ b/include/linux/netfilter_ipv4/ip_tables.h @@ -26,7 +26,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, const struct ipt_replace *repl, const struct nf_hook_ops *ops); -void ipt_unregister_table_pre_exit(struct net *net, const char *name); void ipt_unregister_table_exit(struct net *net, const char *name); /* Standard entry. */ diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h index 8b8885a73c76..c6d5b927830d 100644 --- a/include/linux/netfilter_ipv6/ip6_tables.h +++ b/include/linux/netfilter_ipv6/ip6_tables.h @@ -27,7 +27,6 @@ extern void *ip6t_alloc_initial_table(const struct xt_table *); int ip6t_register_table(struct net *net, const struct xt_table *table, const struct ip6t_replace *repl, const struct nf_hook_ops *ops); -void ip6t_unregister_table_pre_exit(struct net *net, const char *name); void ip6t_unregister_table_exit(struct net *net, const char *name); extern unsigned int ip6t_do_table(void *priv, struct sk_buff *skb, const struct nf_hook_state *state); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 8dd79a3f3d66..4623262da3c0 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -109,6 +109,7 @@ struct nfs_open_context { #define NFS_CONTEXT_BAD (2) #define NFS_CONTEXT_UNLOCK (3) #define NFS_CONTEXT_FILE_OPEN (4) +#define NFS_CONTEXT_WRITE_SYNC (5) struct nfs4_threshold *mdsthreshold; struct list_head list; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index ff1f12aa73d2..fcbd21b5685f 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1611,12 +1611,15 @@ struct nfs42_listxattrsres { struct nfs42_removexattrargs { struct nfs4_sequence_args seq_args; struct nfs_fh *fh; + const u32 *bitmask; const char *xattr_name; }; struct nfs42_removexattrres { struct nfs4_sequence_res seq_res; struct nfs4_change_info cinfo; + struct nfs_fattr *fattr; + const struct nfs_server *server; }; #endif /* CONFIG_NFS_V4_2 */ diff --git a/include/linux/nmi.h b/include/linux/nmi.h index 207156f2143c..bc1162895f35 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -21,6 +21,7 @@ void lockup_detector_soft_poweroff(void); extern int watchdog_user_enabled; extern int watchdog_thresh; extern unsigned long watchdog_enabled; +extern int watchdog_hardlockup_miss_thresh; extern struct cpumask watchdog_cpumask; extern unsigned long *watchdog_cpumask_bits; diff --git a/include/linux/nstree.h b/include/linux/nstree.h index 175e4625bfa6..5b64d4572881 100644 --- a/include/linux/nstree.h +++ b/include/linux/nstree.h @@ -61,7 +61,7 @@ static inline void __ns_tree_add(struct ns_common *ns, struct ns_tree_root *ns_t /** * ns_tree_add_raw - Add a namespace to a namespace - * @ns: Namespace to add + * @__ns: Namespace to add * * This function adds a namespace to the appropriate namespace tree * without assigning a id. @@ -70,7 +70,7 @@ static inline void __ns_tree_add(struct ns_common *ns, struct ns_tree_root *ns_t /** * ns_tree_add - Add a namespace to a namespace tree - * @ns: Namespace to add + * @__ns: Namespace to add * * This function assigns a new id to the namespace and adds it to the * appropriate namespace tree and list. @@ -81,7 +81,7 @@ static inline void __ns_tree_add(struct ns_common *ns, struct ns_tree_root *ns_t /** * ns_tree_remove - Remove a namespace from a namespace tree - * @ns: Namespace to remove + * @__ns: Namespace to remove * * This function removes a namespace from the appropriate namespace * tree and list. diff --git a/include/linux/nvme-auth.h b/include/linux/nvme-auth.h index 682f81046345..d674d8ab26e6 100644 --- a/include/linux/nvme-auth.h +++ b/include/linux/nvme-auth.h @@ -49,9 +49,9 @@ int nvme_auth_augmented_challenge(u8 hmac_id, const u8 *skey, size_t skey_len, int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, u8 dh_gid); int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm, u8 *host_key, size_t host_key_len); -int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm, - const u8 *ctrl_key, size_t ctrl_key_len, - u8 *sess_key, size_t sess_key_len); +int nvme_auth_gen_session_key(struct crypto_kpp *dh_tfm, + const u8 *public_key, size_t public_key_len, + u8 *sess_key, size_t sess_key_len, u8 hash_id); int nvme_auth_generate_psk(u8 hmac_id, const u8 *skey, size_t skey_len, const u8 *c1, const u8 *c2, size_t hash_len, u8 **ret_psk, size_t *ret_len); diff --git a/include/linux/of.h b/include/linux/of.h index 2b95777f16f6..959786f8f196 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -410,7 +410,7 @@ extern int of_alias_get_id(const struct device_node *np, const char *stem); extern int of_alias_get_highest_id(const char *stem); bool of_machine_compatible_match(const char *const *compats); -bool of_machine_device_match(const struct of_device_id *matches); +const struct of_device_id *of_machine_get_match(const struct of_device_id *matches); const void *of_machine_get_match_data(const struct of_device_id *matches); /** @@ -880,9 +880,9 @@ static inline bool of_machine_compatible_match(const char *const *compats) return false; } -static inline bool of_machine_device_match(const struct of_device_id *matches) +static inline const struct of_device_id *of_machine_get_match(const struct of_device_id *matches) { - return false; + return NULL; } static inline const void * @@ -990,6 +990,11 @@ static inline int of_numa_init(void) } #endif +static inline bool of_machine_device_match(const struct of_device_id *matches) +{ + return of_machine_get_match(matches) != NULL; +} + static inline struct device_node *of_find_matching_node( struct device_node *from, const struct of_device_id *matches) diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index fd706cdf255c..16b08234d03b 100644 --- a/include/linux/of_dma.h +++ b/include/linux/of_dma.h @@ -38,6 +38,26 @@ extern int of_dma_controller_register(struct device_node *np, void *data); extern void of_dma_controller_free(struct device_node *np); +static void __of_dma_controller_free(void *np) +{ + of_dma_controller_free(np); +} + +static inline int +devm_of_dma_controller_register(struct device *dev, struct device_node *np, + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *), + void *data) +{ + int ret; + + ret = of_dma_controller_register(np, of_dma_xlate, data); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, __of_dma_controller_free, np); +} + extern int of_dma_router_register(struct device_node *np, void *(*of_dma_route_allocate) (struct of_phandle_args *, struct of_dma *), @@ -64,6 +84,15 @@ static inline void of_dma_controller_free(struct device_node *np) { } +static inline int +devm_of_dma_controller_register(struct device *dev, struct device_node *np, + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *), + void *data) +{ + return -ENODEV; +} + static inline int of_dma_router_register(struct device_node *np, void *(*of_dma_route_allocate) (struct of_phandle_args *, struct of_dma *), diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h index f573423359f4..e8b20b29fa68 100644 --- a/include/linux/of_reserved_mem.h +++ b/include/linux/of_reserved_mem.h @@ -11,7 +11,6 @@ struct resource; struct reserved_mem { const char *name; - unsigned long fdt_node; const struct reserved_mem_ops *ops; phys_addr_t base; phys_addr_t size; @@ -19,18 +18,20 @@ struct reserved_mem { }; struct reserved_mem_ops { + int (*node_validate)(unsigned long fdt_node, phys_addr_t *align); + int (*node_fixup)(unsigned long fdt_node, phys_addr_t base, + phys_addr_t size); + int (*node_init)(unsigned long fdt_node, struct reserved_mem *rmem); int (*device_init)(struct reserved_mem *rmem, struct device *dev); void (*device_release)(struct reserved_mem *rmem, struct device *dev); }; -typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem); - #ifdef CONFIG_OF_RESERVED_MEM -#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ - _OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn) +#define RESERVEDMEM_OF_DECLARE(name, compat, ops) \ + _OF_DECLARE(reservedmem, name, compat, ops, struct reserved_mem_ops *) int of_reserved_mem_device_init_by_idx(struct device *dev, struct device_node *np, int idx); @@ -48,8 +49,9 @@ int of_reserved_mem_region_count(const struct device_node *np); #else -#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ - _OF_DECLARE_STUB(reservedmem, name, compat, init, reservedmem_of_init_fn) +#define RESERVEDMEM_OF_DECLARE(name, compat, ops) \ + _OF_DECLARE_STUB(reservedmem, name, compat, ops, \ + struct reserved_mem_ops *) static inline int of_reserved_mem_device_init_by_idx(struct device *dev, struct device_node *np, int idx) diff --git a/include/linux/pgalloc_tag.h b/include/linux/pgalloc_tag.h index 38a82d65e58e..951d33362268 100644 --- a/include/linux/pgalloc_tag.h +++ b/include/linux/pgalloc_tag.h @@ -181,7 +181,7 @@ static inline struct alloc_tag *__pgalloc_tag_get(struct page *page) if (get_page_tag_ref(page, &ref, &handle)) { alloc_tag_sub_check(&ref); - if (ref.ct) + if (ref.ct && !is_codetag_empty(&ref)) tag = ct_to_alloc_tag(ref.ct); put_page_tag_ref(handle); } diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h index cdd68ed3ae1a..b5739bb99fc1 100644 --- a/include/linux/pgtable.h +++ b/include/linux/pgtable.h @@ -1036,6 +1036,31 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addres } #endif +#ifndef ptep_try_set +/** + * ptep_try_set - atomically set an empty kernel PTE + * @ptep: page table entry + * @new_pte: value to install + * + * Atomically set *@ptep to @new_pte iff *@ptep is pte_none(). Return true on + * success, false if the slot was already populated or the arch has no + * implementation. + * + * For special kernel page tables only - never user page tables. The caller must + * prevent concurrent teardown of @ptep and must accept that other writers may + * race. Concurrent clearers must use ptep_get_and_clear() so racing accesses + * agree on the outcome. + * + * Architectures opt in by providing a cmpxchg-based override and defining + * ptep_try_set as an identity macro. The generic stub returns false, which is + * correct for callers that fall through to oops on failure. + */ +static inline bool ptep_try_set(pte_t *ptep, pte_t new_pte) +{ + return false; +} +#endif + #ifndef wrprotect_ptes /** * wrprotect_ptes - Write-protect PTEs that map consecutive pages of the same diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h index 89277808ea61..a5d4b2d8633a 100644 --- a/include/linux/pinctrl/pinconf-generic.h +++ b/include/linux/pinctrl/pinconf-generic.h @@ -83,6 +83,8 @@ struct pinctrl_map; * schmitt-trigger mode is disabled. * @PIN_CONFIG_INPUT_SCHMITT_UV: this will configure an input pin to run in * schmitt-trigger mode. The argument is in uV. + * @PIN_CONFIG_INPUT_VOLTAGE_UV: this will configure the input voltage level of + * the pin. The argument is specified in microvolts. * @PIN_CONFIG_MODE_LOW_POWER: this will configure the pin for low power * operation, if several modes of operation are supported these can be * passed in the argument on a custom form, else just use argument 1 @@ -115,7 +117,7 @@ struct pinctrl_map; * @PIN_CONFIG_SKEW_DELAY_INPUT_PS: if the pin has independent values for the * programmable skew rate (on inputs) and latch delay (on outputs), then * this parameter specifies the clock skew only. The argument is in ps. - * @PIN_CONFIG_SKEW_DELAY_OUPUT_PS: if the pin has independent values for the + * @PIN_CONFIG_SKEW_DELAY_OUTPUT_PS: if the pin has independent values for the * programmable skew rate (on inputs) and latch delay (on outputs), then * this parameter specifies the latch delay only. The argument is in ps. * @PIN_CONFIG_SLEEP_HARDWARE_STATE: indicate this is sleep related state. @@ -145,6 +147,7 @@ enum pin_config_param { PIN_CONFIG_INPUT_SCHMITT, PIN_CONFIG_INPUT_SCHMITT_ENABLE, PIN_CONFIG_INPUT_SCHMITT_UV, + PIN_CONFIG_INPUT_VOLTAGE_UV, PIN_CONFIG_MODE_LOW_POWER, PIN_CONFIG_MODE_PWM, PIN_CONFIG_LEVEL, diff --git a/include/linux/platform_data/apds990x.h b/include/linux/platform_data/apds990x.h index 94dfbaa365e1..37684f68c04f 100644 --- a/include/linux/platform_data/apds990x.h +++ b/include/linux/platform_data/apds990x.h @@ -31,7 +31,6 @@ * itself. If the GA is zero, driver will use uncovered sensor default values * format: decimal value * APDS_PARAM_SCALE except df which is plain integer. */ -#define APDS_PARAM_SCALE 4096 struct apds990x_chip_factors { int ga; int cf1; @@ -40,11 +39,12 @@ struct apds990x_chip_factors { int irf2; int df; }; +#define APDS_PARAM_SCALE 4096 /** * struct apds990x_platform_data - platform data for apsd990x.c driver * @cf: chip factor data - * @pddrive: IR-led driving current + * @pdrive: IR-led driving current * @ppcount: number of IR pulses used for proximity estimation * @setup_resources: interrupt line setup call back function * @release_resources: interrupt line release call back function diff --git a/include/linux/platform_data/dma-mcf-edma.h b/include/linux/platform_data/dma-mcf-edma.h index d718ccfa3421..0b31af66a1ac 100644 --- a/include/linux/platform_data/dma-mcf-edma.h +++ b/include/linux/platform_data/dma-mcf-edma.h @@ -26,8 +26,9 @@ bool mcf_edma_filter_fn(struct dma_chan *chan, void *param); /** * struct mcf_edma_platform_data - platform specific data for eDMA engine * - * @ver The eDMA module version. - * @dma_channels The number of eDMA channels. + * @dma_channels: The number of eDMA channels. + * @slave_map: Slave device map + * @slavecnt: Number of entries in @slave_map */ struct mcf_edma_platform_data { int dma_channels; diff --git a/include/linux/platform_data/tsl2772.h b/include/linux/platform_data/tsl2772.h index f8ade15a35e2..f042e82b39c3 100644 --- a/include/linux/platform_data/tsl2772.h +++ b/include/linux/platform_data/tsl2772.h @@ -61,9 +61,9 @@ struct tsl2772_lux { * @prox_pulse_count: Number if proximity emitter pulses. * @prox_max_samples_cal: The number of samples that are taken when performing * a proximity calibration. - * @prox_diode Which diode(s) to use for driving the external + * @prox_diode: Which diode(s) to use for driving the external * LED(s) for proximity sensing. - * @prox_power The amount of power to use for the external LED(s). + * @prox_power: The amount of power to use for the external LED(s). */ struct tsl2772_settings { int als_time; diff --git a/include/linux/platform_data/voltage-omap.h b/include/linux/platform_data/voltage-omap.h index 6d74e507dbd2..2b48f2b0135d 100644 --- a/include/linux/platform_data/voltage-omap.h +++ b/include/linux/platform_data/voltage-omap.h @@ -10,14 +10,14 @@ /** * struct omap_volt_data - Omap voltage specific data. - * @voltage_nominal: The possible voltage value in uV + * @volt_nominal: The possible voltage value in uV * @sr_efuse_offs: The offset of the efuse register(from system * control module base address) from where to read * the n-target value for the smartreflex module. * @sr_errminlimit: Error min limit value for smartreflex. This value * differs at differnet opp and thus is linked * with voltage. - * @vp_errorgain: Error gain value for the voltage processor. This + * @vp_errgain: Error gain value for the voltage processor. This * field also differs according to the voltage/opp. */ struct omap_volt_data { diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h index dbe745dc88d5..93f1e1fe09b4 100644 --- a/include/linux/platform_data/x86/int3472.h +++ b/include/linux/platform_data/x86/int3472.h @@ -23,6 +23,7 @@ /* PMIC GPIO Types */ #define INT3472_GPIO_TYPE_RESET 0x00 #define INT3472_GPIO_TYPE_POWERDOWN 0x01 +#define INT3472_GPIO_TYPE_STROBE 0x02 #define INT3472_GPIO_TYPE_POWER_ENABLE 0x0b #define INT3472_GPIO_TYPE_CLK_ENABLE 0x0c #define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d @@ -32,6 +33,7 @@ #define INT3472_PDEV_MAX_NAME_LEN 23 #define INT3472_MAX_SENSOR_GPIOS 3 +#define INT3472_MAX_LEDS 2 #define INT3472_MAX_REGULATORS 3 /* E.g. "dovdd\0" */ @@ -122,16 +124,17 @@ struct int3472_discrete_device { u8 imgclk_index; } clock; - struct int3472_pled { + struct int3472_led { struct led_classdev classdev; struct led_lookup_data lookup; char name[INT3472_LED_MAX_NAME_LEN]; struct gpio_desc *gpio; - } pled; + } leds[INT3472_MAX_LEDS]; struct int3472_discrete_quirks quirks; unsigned int ngpios; /* how many GPIOs have we seen */ + unsigned int n_leds; /* how many LEDs have we registered */ unsigned int n_sensor_gpios; /* how many have we mapped to sensor */ unsigned int n_regulator_gpios; /* how many have we mapped to a regulator */ struct gpiod_lookup_table gpios; @@ -161,7 +164,8 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, const char *second_sensor); void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); -int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gpio_desc *gpio); -void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472); +int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, + const char *con_id); +void skl_int3472_unregister_leds(struct int3472_discrete_device *int3472); #endif diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index c417abd2ab70..d5b08313cf11 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -17,6 +17,7 @@ #define MAX17042_DEFAULT_VMAX (4500) /* LiHV cell max */ #define MAX17042_DEFAULT_TEMP_MIN (0) /* For sys without temp sensor */ #define MAX17042_DEFAULT_TEMP_MAX (700) /* 70 degrees Celcius */ +#define MAX17042_DEFAULT_TASK_PERIOD (5760) /* Consider RepCap which is less then 10 units below FullCAP full */ #define MAX17042_FULL_THRESHOLD 10 @@ -105,7 +106,7 @@ enum max17042_register { MAX17042_OCV = 0xEE, - MAX17042_OCVInternal = 0xFB, /* MAX17055 VFOCV */ + MAX17042_OCVInternal = 0xFB, /* MAX17055/77759 VFOCV */ MAX17042_VFSOC = 0xFF, }; @@ -156,7 +157,7 @@ enum max17055_register { MAX17055_AtAvCap = 0xDF, }; -/* Registers specific to max17047/50/55 */ +/* Registers specific to max17047/50/55/77759 */ enum max17047_register { MAX17047_QRTbl00 = 0x12, MAX17047_FullSOCThr = 0x13, @@ -167,12 +168,32 @@ enum max17047_register { MAX17047_QRTbl30 = 0x42, }; +enum max77759_register { + MAX77759_AvgTA0 = 0x26, + MAX77759_AtTTF = 0x33, + MAX77759_Tconvert = 0x34, + MAX77759_AvgCurrent0 = 0x3B, + MAX77759_THMHOT = 0x40, + MAX77759_CTESample = 0x41, + MAX77759_ISys = 0x43, + MAX77759_AvgVCell0 = 0x44, + MAX77759_RlxSOC = 0x47, + MAX77759_AvgISys = 0x4B, + MAX77759_QH0 = 0x4C, + MAX77759_MixAtFull = 0x4F, + MAX77759_VSys = 0xB1, + MAX77759_TAlrtTh2 = 0xB2, + MAX77759_VByp = 0xB3, + MAX77759_IIn = 0xD0, +}; + enum max170xx_chip_type { MAXIM_DEVICE_TYPE_UNKNOWN = 0, MAXIM_DEVICE_TYPE_MAX17042, MAXIM_DEVICE_TYPE_MAX17047, MAXIM_DEVICE_TYPE_MAX17050, MAXIM_DEVICE_TYPE_MAX17055, + MAXIM_DEVICE_TYPE_MAX77759, MAXIM_DEVICE_TYPE_NUM }; diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 360ffdf272da..7a5e4c3242a0 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -210,6 +210,9 @@ enum power_supply_usb_type { POWER_SUPPLY_USB_TYPE_PD, /* Power Delivery Port */ POWER_SUPPLY_USB_TYPE_PD_DRP, /* PD Dual Role Port */ POWER_SUPPLY_USB_TYPE_PD_PPS, /* PD Programmable Power Supply */ + /* PD Standard Power Range Adjustable Voltage Supply */ + POWER_SUPPLY_USB_TYPE_PD_SPR_AVS, + POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS, /* Supports both PD PPS + SPR AVS */ POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */ }; diff --git a/include/linux/ppp_defs.h b/include/linux/ppp_defs.h index b7e57fdbd413..b1d1f46d7d3b 100644 --- a/include/linux/ppp_defs.h +++ b/include/linux/ppp_defs.h @@ -8,6 +8,7 @@ #define _PPP_DEFS_H_ #include +#include #include #define PPP_FCS(fcs, c) crc_ccitt_byte(fcs, c) @@ -25,4 +26,19 @@ static inline bool ppp_proto_is_valid(u16 proto) return !!((proto & 0x0101) == 0x0001); } +/** + * ppp_skb_is_compressed_proto - checks if PPP protocol in a skb is compressed + * @skb: skb to check + * + * Check if the PPP protocol field is compressed (the least significant + * bit of the most significant octet is 1). skb->data must point to the PPP + * protocol header. + * + * Return: Whether the PPP protocol field is compressed. + */ +static inline bool ppp_skb_is_compressed_proto(const struct sk_buff *skb) +{ + return unlikely(skb->data[0] & 0x01); +} + #endif /* _PPP_DEFS_H_ */ diff --git a/include/linux/printk.h b/include/linux/printk.h index 54e3c621fec3..f594c1266bfd 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -815,7 +815,8 @@ static inline void print_hex_dump_devel(const char *prefix_str, int prefix_type, #endif /** - * print_hex_dump_bytes - shorthand form of print_hex_dump() with default params + * print_hex_dump_bytes - shorthand form of print_hex_dump_debug() with default + * params * @prefix_str: string to prefix each line with; * caller supplies trailing spaces for alignment if desired * @prefix_type: controls whether prefix of an offset, address, or none @@ -823,7 +824,7 @@ static inline void print_hex_dump_devel(const char *prefix_str, int prefix_type, * @buf: data blob to dump * @len: number of bytes in the @buf * - * Calls print_hex_dump(), with log level of KERN_DEBUG, + * Calls print_hex_dump_debug(), with log level of KERN_DEBUG, * rowsize of 16, groupsize of 1, and ASCII output included. */ #define print_hex_dump_bytes(prefix_str, prefix_type, buf, len) \ diff --git a/include/linux/raid/xor.h b/include/linux/raid/xor.h index 51b811b62322..870558c9d36e 100644 --- a/include/linux/raid/xor.h +++ b/include/linux/raid/xor.h @@ -2,29 +2,6 @@ #ifndef _XOR_H #define _XOR_H -#define MAX_XOR_BLOCKS 4 +void xor_gen(void *dest, void **srcs, unsigned int src_cnt, unsigned int bytes); -extern void xor_blocks(unsigned int count, unsigned int bytes, - void *dest, void **srcs); - -struct xor_block_template { - struct xor_block_template *next; - const char *name; - int speed; - void (*do_2)(unsigned long, unsigned long * __restrict, - const unsigned long * __restrict); - void (*do_3)(unsigned long, unsigned long * __restrict, - const unsigned long * __restrict, - const unsigned long * __restrict); - void (*do_4)(unsigned long, unsigned long * __restrict, - const unsigned long * __restrict, - const unsigned long * __restrict, - const unsigned long * __restrict); - void (*do_5)(unsigned long, unsigned long * __restrict, - const unsigned long * __restrict, - const unsigned long * __restrict, - const unsigned long * __restrict, - const unsigned long * __restrict); -}; - -#endif +#endif /* _XOR_H */ diff --git a/include/linux/rculist.h b/include/linux/rculist.h index 2abba7552605..e3bc44225692 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -261,6 +261,35 @@ static inline void list_replace_rcu(struct list_head *old, old->prev = LIST_POISON2; } +static inline void __list_splice_rcu(struct list_head *list, + struct list_head *prev, + struct list_head *next) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + + last->next = next; + first->prev = prev; + next->prev = last; + rcu_assign_pointer(list_next_rcu(prev), first); +} + +/** + * list_splice_rcu - splice a non-RCU list into an RCU-protected list, + * designed for stacks. + * @list: the non RCU-protected list to splice + * @head: the place in the existing RCU-protected list to splice + * + * The list pointed to by @head can be RCU-read traversed concurrently with + * this function. + */ +static inline void list_splice_rcu(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice_rcu(list, head, head->next); +} + /** * __list_splice_init_rcu - join an RCU-protected list into an existing list. * @list: the RCU-protected list to splice diff --git a/include/linux/remoteproc/mtk_scp.h b/include/linux/remoteproc/mtk_scp.h index 344ff41c22c7..4070537d6542 100644 --- a/include/linux/remoteproc/mtk_scp.h +++ b/include/linux/remoteproc/mtk_scp.h @@ -58,7 +58,7 @@ int scp_ipi_register(struct mtk_scp *scp, u32 id, scp_ipi_handler_t handler, void *priv); void scp_ipi_unregister(struct mtk_scp *scp, u32 id); -int scp_ipi_send(struct mtk_scp *scp, u32 id, void *buf, unsigned int len, +int scp_ipi_send(struct mtk_scp *scp, u32 id, const void *buf, unsigned int len, unsigned int wait); unsigned int scp_get_vdec_hw_capa(struct mtk_scp *scp); diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h index 46514cb1b9e0..52a5a4e81f18 100644 --- a/include/linux/reset-controller.h +++ b/include/linux/reset-controller.h @@ -3,7 +3,10 @@ #define _LINUX_RESET_CONTROLLER_H_ #include +#include +struct fwnode_handle; +struct fwnode_reference_args; struct reset_controller_dev; /** @@ -35,14 +38,16 @@ struct of_phandle_args; * @reset_control_head: head of internal list of requested reset controls * @dev: corresponding driver model device struct * @of_node: corresponding device tree node as phandle target - * @of_args: for reset-gpios controllers: corresponding phandle args with - * of_node and GPIO number complementing of_node; either this or - * of_node should be present * @of_reset_n_cells: number of cells in reset line specifiers * @of_xlate: translation function to translate from specifier as found in the - * device tree to id as given to the reset control ops, defaults - * to :c:func:`of_reset_simple_xlate`. + * device tree to id as given to the reset control ops + * @fwnode: firmware node associated with this device + * @fwnode_reset_n_cells: number of cells in reset line specifiers + * @fwnode_xlate: translation function to translate from firmware specifier to + * id as given to the reset control ops, defaults to + * :c:func:`fwnode_reset_simple_xlate` * @nr_resets: number of reset controls in this reset controller device + * @lock: protects the reset control list from concurrent access */ struct reset_controller_dev { const struct reset_control_ops *ops; @@ -51,11 +56,15 @@ struct reset_controller_dev { struct list_head reset_control_head; struct device *dev; struct device_node *of_node; - const struct of_phandle_args *of_args; int of_reset_n_cells; int (*of_xlate)(struct reset_controller_dev *rcdev, const struct of_phandle_args *reset_spec); + struct fwnode_handle *fwnode; + int fwnode_reset_n_cells; + int (*fwnode_xlate)(struct reset_controller_dev *rcdev, + const struct fwnode_reference_args *reset_spec); unsigned int nr_resets; + struct mutex lock; }; #if IS_ENABLED(CONFIG_RESET_CONTROLLER) diff --git a/include/linux/reset.h b/include/linux/reset.h index 44f9e3415f92..9c391cf0c822 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -5,10 +5,12 @@ #include #include #include +#include #include struct device; struct device_node; +struct fwnode_handle; struct reset_control; /** @@ -84,7 +86,7 @@ int reset_control_bulk_deassert(int num_rstcs, struct reset_control_bulk_data *r int reset_control_bulk_acquire(int num_rstcs, struct reset_control_bulk_data *rstcs); void reset_control_bulk_release(int num_rstcs, struct reset_control_bulk_data *rstcs); -struct reset_control *__of_reset_control_get(struct device_node *node, +struct reset_control *__fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int index, enum reset_control_flags flags); struct reset_control *__reset_control_get(struct device *dev, const char *id, int index, enum reset_control_flags flags); @@ -103,7 +105,8 @@ int __devm_reset_control_bulk_get(struct device *dev, int num_rstcs, struct reset_control *devm_reset_control_array_get(struct device *dev, enum reset_control_flags flags); -struct reset_control *of_reset_control_array_get(struct device_node *np, enum reset_control_flags); +struct reset_control *fwnode_reset_control_array_get(struct fwnode_handle *fwnode, + enum reset_control_flags); int reset_control_get_count(struct device *dev); @@ -152,8 +155,8 @@ static inline int __device_reset(struct device *dev, bool optional) return optional ? 0 : -ENOTSUPP; } -static inline struct reset_control *__of_reset_control_get( - struct device_node *node, +static inline struct reset_control *__fwnode_reset_control_get( + struct fwnode_handle *fwnode, const char *id, int index, enum reset_control_flags flags) { bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; @@ -242,7 +245,7 @@ devm_reset_control_array_get(struct device *dev, enum reset_control_flags flags) } static inline struct reset_control * -of_reset_control_array_get(struct device_node *np, enum reset_control_flags flags) +fwnode_reset_control_array_get(struct fwnode_handle *fwnode, enum reset_control_flags flags) { bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; @@ -500,7 +503,8 @@ reset_control_bulk_get_optional_shared(struct device *dev, int num_rstcs, static inline struct reset_control *of_reset_control_get_exclusive( struct device_node *node, const char *id) { - return __of_reset_control_get(node, id, 0, RESET_CONTROL_EXCLUSIVE); + return __fwnode_reset_control_get(of_fwnode_handle(node), id, 0, + RESET_CONTROL_EXCLUSIVE); } /** @@ -520,7 +524,8 @@ static inline struct reset_control *of_reset_control_get_exclusive( static inline struct reset_control *of_reset_control_get_optional_exclusive( struct device_node *node, const char *id) { - return __of_reset_control_get(node, id, 0, RESET_CONTROL_OPTIONAL_EXCLUSIVE); + return __fwnode_reset_control_get(of_fwnode_handle(node), id, 0, + RESET_CONTROL_OPTIONAL_EXCLUSIVE); } /** @@ -545,7 +550,8 @@ static inline struct reset_control *of_reset_control_get_optional_exclusive( static inline struct reset_control *of_reset_control_get_shared( struct device_node *node, const char *id) { - return __of_reset_control_get(node, id, 0, RESET_CONTROL_SHARED); + return __fwnode_reset_control_get(of_fwnode_handle(node), id, 0, + RESET_CONTROL_SHARED); } /** @@ -562,7 +568,8 @@ static inline struct reset_control *of_reset_control_get_shared( static inline struct reset_control *of_reset_control_get_exclusive_by_index( struct device_node *node, int index) { - return __of_reset_control_get(node, NULL, index, RESET_CONTROL_EXCLUSIVE); + return __fwnode_reset_control_get(of_fwnode_handle(node), NULL, index, + RESET_CONTROL_EXCLUSIVE); } /** @@ -590,7 +597,8 @@ static inline struct reset_control *of_reset_control_get_exclusive_by_index( static inline struct reset_control *of_reset_control_get_shared_by_index( struct device_node *node, int index) { - return __of_reset_control_get(node, NULL, index, RESET_CONTROL_SHARED); + return __fwnode_reset_control_get(of_fwnode_handle(node), NULL, index, + RESET_CONTROL_SHARED); } /** @@ -1032,30 +1040,35 @@ devm_reset_control_array_get_optional_shared(struct device *dev) static inline struct reset_control * of_reset_control_array_get_exclusive(struct device_node *node) { - return of_reset_control_array_get(node, RESET_CONTROL_EXCLUSIVE); + return fwnode_reset_control_array_get(of_fwnode_handle(node), + RESET_CONTROL_EXCLUSIVE); } static inline struct reset_control * of_reset_control_array_get_exclusive_released(struct device_node *node) { - return of_reset_control_array_get(node, RESET_CONTROL_EXCLUSIVE_RELEASED); + return fwnode_reset_control_array_get(of_fwnode_handle(node), + RESET_CONTROL_EXCLUSIVE_RELEASED); } static inline struct reset_control * of_reset_control_array_get_shared(struct device_node *node) { - return of_reset_control_array_get(node, RESET_CONTROL_SHARED); + return fwnode_reset_control_array_get(of_fwnode_handle(node), + RESET_CONTROL_SHARED); } static inline struct reset_control * of_reset_control_array_get_optional_exclusive(struct device_node *node) { - return of_reset_control_array_get(node, RESET_CONTROL_OPTIONAL_EXCLUSIVE); + return fwnode_reset_control_array_get(of_fwnode_handle(node), + RESET_CONTROL_OPTIONAL_EXCLUSIVE); } static inline struct reset_control * of_reset_control_array_get_optional_shared(struct device_node *node) { - return of_reset_control_array_get(node, RESET_CONTROL_OPTIONAL_SHARED); + return fwnode_reset_control_array_get(of_fwnode_handle(node), + RESET_CONTROL_OPTIONAL_SHARED); } #endif diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index fb7ab9165645..83266ce14642 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -182,11 +182,11 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *, rpmsg_rx_cb_t cb, void *priv, struct rpmsg_channel_info chinfo); -int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len); -int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); +int rpmsg_send(struct rpmsg_endpoint *ept, const void *data, int len); +int rpmsg_sendto(struct rpmsg_endpoint *ept, const void *data, int len, u32 dst); -int rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len); -int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); +int rpmsg_trysend(struct rpmsg_endpoint *ept, const void *data, int len); +int rpmsg_trysendto(struct rpmsg_endpoint *ept, const void *data, int len, u32 dst); __poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait); @@ -249,7 +249,7 @@ static inline struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev return NULL; } -static inline int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len) +static inline int rpmsg_send(struct rpmsg_endpoint *ept, const void *data, int len) { /* This shouldn't be possible */ WARN_ON(1); @@ -257,7 +257,7 @@ static inline int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len) return -ENXIO; } -static inline int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, +static inline int rpmsg_sendto(struct rpmsg_endpoint *ept, const void *data, int len, u32 dst) { /* This shouldn't be possible */ @@ -267,7 +267,8 @@ static inline int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, } -static inline int rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len) +static inline int rpmsg_trysend(struct rpmsg_endpoint *ept, const void *data, + int len) { /* This shouldn't be possible */ WARN_ON(1); @@ -275,7 +276,7 @@ static inline int rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len) return -ENXIO; } -static inline int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, +static inline int rpmsg_trysendto(struct rpmsg_endpoint *ept, const void *data, int len, u32 dst) { /* This shouldn't be possible */ diff --git a/include/linux/rpmsg/mtk_rpmsg.h b/include/linux/rpmsg/mtk_rpmsg.h index 363b60178040..badcbc89917f 100644 --- a/include/linux/rpmsg/mtk_rpmsg.h +++ b/include/linux/rpmsg/mtk_rpmsg.h @@ -25,7 +25,7 @@ struct mtk_rpmsg_info { ipi_handler_t handler, void *priv); void (*unregister_ipi)(struct platform_device *pdev, u32 id); int (*send_ipi)(struct platform_device *pdev, u32 id, - void *buf, unsigned int len, unsigned int wait); + const void *buf, unsigned int len, unsigned int wait); int ns_ipi_id; }; diff --git a/include/linux/rseq.h b/include/linux/rseq.h index b9d62fc2140d..7ef79b25e714 100644 --- a/include/linux/rseq.h +++ b/include/linux/rseq.h @@ -9,6 +9,11 @@ void __rseq_handle_slowpath(struct pt_regs *regs); +static __always_inline bool rseq_v2(struct task_struct *t) +{ + return IS_ENABLED(CONFIG_GENERIC_IRQ_ENTRY) && likely(t->rseq.event.has_rseq > 1); +} + /* Invoked from resume_user_mode_work() */ static inline void rseq_handle_slowpath(struct pt_regs *regs) { @@ -16,8 +21,7 @@ static inline void rseq_handle_slowpath(struct pt_regs *regs) if (current->rseq.event.slowpath) __rseq_handle_slowpath(regs); } else { - /* '&' is intentional to spare one conditional branch */ - if (current->rseq.event.sched_switch & current->rseq.event.has_rseq) + if (current->rseq.event.sched_switch && current->rseq.event.has_rseq) __rseq_handle_slowpath(regs); } } @@ -30,9 +34,9 @@ void __rseq_signal_deliver(int sig, struct pt_regs *regs); */ static inline void rseq_signal_deliver(struct ksignal *ksig, struct pt_regs *regs) { - if (IS_ENABLED(CONFIG_GENERIC_IRQ_ENTRY)) { - /* '&' is intentional to spare one conditional branch */ - if (current->rseq.event.has_rseq & current->rseq.event.user_irq) + if (rseq_v2(current)) { + /* has_rseq is implied in rseq_v2() */ + if (current->rseq.event.user_irq) __rseq_signal_deliver(ksig->sig, regs); } else { if (current->rseq.event.has_rseq) @@ -50,15 +54,22 @@ static __always_inline void rseq_sched_switch_event(struct task_struct *t) { struct rseq_event *ev = &t->rseq.event; - if (IS_ENABLED(CONFIG_GENERIC_IRQ_ENTRY)) { + /* + * Only apply the user_irq optimization for RSEQ ABI V2 registrations. + * Legacy users like TCMalloc rely on the original ABI V1 behaviour + * which updates IDs on every context swtich. + */ + if (rseq_v2(t)) { /* - * Avoid a boat load of conditionals by using simple logic - * to determine whether NOTIFY_RESUME needs to be raised. + * Avoid a boat load of conditionals by using simple logic to + * determine whether TIF_NOTIFY_RESUME or TIF_RSEQ needs to be + * raised. * - * It's required when the CPU or MM CID has changed or - * the entry was from user space. + * It's required when the CPU or MM CID has changed or the entry + * was via interrupt from user space. ev->has_rseq does not have + * to be evaluated here because rseq_v2() implies has_rseq. */ - bool raise = (ev->user_irq | ev->ids_changed) & ev->has_rseq; + bool raise = ev->user_irq | ev->ids_changed; if (raise) { ev->sched_switch = true; @@ -66,6 +77,7 @@ static __always_inline void rseq_sched_switch_event(struct task_struct *t) } } else { if (ev->has_rseq) { + t->rseq.event.ids_changed = true; t->rseq.event.sched_switch = true; rseq_raise_notify_resume(t); } @@ -119,6 +131,8 @@ static inline void rseq_virt_userspace_exit(void) static inline void rseq_reset(struct task_struct *t) { + /* Protect against preemption and membarrier IPI */ + guard(irqsave)(); memset(&t->rseq, 0, sizeof(t->rseq)); t->rseq.ids.cpu_id = RSEQ_CPU_ID_UNINITIALIZED; } @@ -159,6 +173,7 @@ static inline unsigned int rseq_alloc_align(void) } #else /* CONFIG_RSEQ */ +static inline bool rseq_v2(struct task_struct *t) { return false; } static inline void rseq_handle_slowpath(struct pt_regs *regs) { } static inline void rseq_signal_deliver(struct ksignal *ksig, struct pt_regs *regs) { } static inline void rseq_sched_switch_event(struct task_struct *t) { } diff --git a/include/linux/rseq_entry.h b/include/linux/rseq_entry.h index f11ebd34f8b9..63bc72086e75 100644 --- a/include/linux/rseq_entry.h +++ b/include/linux/rseq_entry.h @@ -111,6 +111,20 @@ static __always_inline void rseq_slice_clear_grant(struct task_struct *t) t->rseq.slice.state.granted = false; } +/* + * Open coded, so it can be invoked within a user access region. + * + * This clears the user space state of the time slice extensions field only when + * the task has registered the optimized RSEQ_ABI V2. Some legacy registrations, + * e.g. TCMalloc, have conflicting non-ABI fields in struct RSEQ, which would be + * overwritten by an unconditional write. + */ +#define rseq_slice_clear_user(rseq, efault) \ +do { \ + if (rseq_slice_extension_enabled()) \ + unsafe_put_user(0U, &rseq->slice_ctrl.all, efault); \ +} while (0) + static __always_inline bool __rseq_grant_slice_extension(bool work_pending) { struct task_struct *curr = current; @@ -230,10 +244,10 @@ static __always_inline bool rseq_slice_extension_enabled(void) { return false; } static __always_inline bool rseq_arm_slice_extension_timer(void) { return false; } static __always_inline void rseq_slice_clear_grant(struct task_struct *t) { } static __always_inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask) { return false; } +#define rseq_slice_clear_user(rseq, efault) do { } while (0) #endif /* !CONFIG_RSEQ_SLICE_EXTENSION */ bool rseq_debug_update_user_cs(struct task_struct *t, struct pt_regs *regs, unsigned long csaddr); -bool rseq_debug_validate_ids(struct task_struct *t); static __always_inline void rseq_note_user_irq_entry(void) { @@ -353,43 +367,6 @@ efault: return false; } -/* - * On debug kernels validate that user space did not mess with it if the - * debug branch is enabled. - */ -bool rseq_debug_validate_ids(struct task_struct *t) -{ - struct rseq __user *rseq = t->rseq.usrptr; - u32 cpu_id, uval, node_id; - - /* - * On the first exit after registering the rseq region CPU ID is - * RSEQ_CPU_ID_UNINITIALIZED and node_id in user space is 0! - */ - node_id = t->rseq.ids.cpu_id != RSEQ_CPU_ID_UNINITIALIZED ? - cpu_to_node(t->rseq.ids.cpu_id) : 0; - - scoped_user_read_access(rseq, efault) { - unsafe_get_user(cpu_id, &rseq->cpu_id_start, efault); - if (cpu_id != t->rseq.ids.cpu_id) - goto die; - unsafe_get_user(uval, &rseq->cpu_id, efault); - if (uval != cpu_id) - goto die; - unsafe_get_user(uval, &rseq->node_id, efault); - if (uval != node_id) - goto die; - unsafe_get_user(uval, &rseq->mm_cid, efault); - if (uval != t->rseq.ids.mm_cid) - goto die; - } - return true; -die: - t->rseq.event.fatal = true; -efault: - return false; -} - #endif /* RSEQ_BUILD_SLOW_PATH */ /* @@ -499,37 +476,50 @@ efault: * faults in task context are fatal too. */ static rseq_inline -bool rseq_set_ids_get_csaddr(struct task_struct *t, struct rseq_ids *ids, - u32 node_id, u64 *csaddr) +bool rseq_set_ids_get_csaddr(struct task_struct *t, struct rseq_ids *ids, u64 *csaddr) { struct rseq __user *rseq = t->rseq.usrptr; - if (static_branch_unlikely(&rseq_debug_enabled)) { - if (!rseq_debug_validate_ids(t)) - return false; - } - scoped_user_rw_access(rseq, efault) { + /* Validate the R/O fields for debug and optimized mode */ + if (static_branch_unlikely(&rseq_debug_enabled) || rseq_v2(t)) { + u32 cpu_id, uval; + + unsafe_get_user(cpu_id, &rseq->cpu_id_start, efault); + if (cpu_id != t->rseq.ids.cpu_id) + goto die; + unsafe_get_user(uval, &rseq->cpu_id, efault); + if (uval != cpu_id) + goto die; + unsafe_get_user(uval, &rseq->node_id, efault); + if (uval != t->rseq.ids.node_id) + goto die; + unsafe_get_user(uval, &rseq->mm_cid, efault); + if (uval != t->rseq.ids.mm_cid) + goto die; + } + unsafe_put_user(ids->cpu_id, &rseq->cpu_id_start, efault); unsafe_put_user(ids->cpu_id, &rseq->cpu_id, efault); - unsafe_put_user(node_id, &rseq->node_id, efault); + unsafe_put_user(ids->node_id, &rseq->node_id, efault); unsafe_put_user(ids->mm_cid, &rseq->mm_cid, efault); if (csaddr) unsafe_get_user(*csaddr, &rseq->rseq_cs, efault); - /* Open coded, so it's in the same user access region */ - if (rseq_slice_extension_enabled()) { - /* Unconditionally clear it, no point in conditionals */ - unsafe_put_user(0U, &rseq->slice_ctrl.all, efault); - } + /* RSEQ ABI V2 only operations */ + if (rseq_v2(t)) + rseq_slice_clear_user(rseq, efault); } rseq_slice_clear_grant(t); /* Cache the new values */ - t->rseq.ids.cpu_cid = ids->cpu_cid; + t->rseq.ids = *ids; rseq_stat_inc(rseq_stats.ids); rseq_trace_update(t, ids); return true; + +die: + t->rseq.event.fatal = true; efault: return false; } @@ -539,11 +529,11 @@ efault: * is in a critical section. */ static rseq_inline bool rseq_update_usr(struct task_struct *t, struct pt_regs *regs, - struct rseq_ids *ids, u32 node_id) + struct rseq_ids *ids) { u64 csaddr; - if (!rseq_set_ids_get_csaddr(t, ids, node_id, &csaddr)) + if (!rseq_set_ids_get_csaddr(t, ids, &csaddr)) return false; /* @@ -612,6 +602,14 @@ static __always_inline bool rseq_exit_user_update(struct pt_regs *regs, struct t * interrupts disabled */ guard(pagefault)(); + /* + * This optimization is only valid when the task registered for the + * optimized RSEQ_ABI_V2 variant. Some legacy users rely on the original + * RSEQ implementation behaviour which unconditionally updated the IDs. + * rseq_sched_switch_event() ensures that legacy registrations always + * have both sched_switch and ids_changed set, which is compatible with + * the historical TIF_NOTIFY_RESUME behaviour. + */ if (likely(!t->rseq.event.ids_changed)) { struct rseq __user *rseq = t->rseq.usrptr; /* @@ -623,11 +621,9 @@ static __always_inline bool rseq_exit_user_update(struct pt_regs *regs, struct t scoped_user_rw_access(rseq, efault) { unsafe_get_user(csaddr, &rseq->rseq_cs, efault); - /* Open coded, so it's in the same user access region */ - if (rseq_slice_extension_enabled()) { - /* Unconditionally clear it, no point in conditionals */ - unsafe_put_user(0U, &rseq->slice_ctrl.all, efault); - } + /* RSEQ ABI V2 only operations */ + if (rseq_v2(t)) + rseq_slice_clear_user(rseq, efault); } rseq_slice_clear_grant(t); @@ -640,12 +636,12 @@ static __always_inline bool rseq_exit_user_update(struct pt_regs *regs, struct t } struct rseq_ids ids = { - .cpu_id = task_cpu(t), - .mm_cid = task_mm_cid(t), + .cpu_id = task_cpu(t), + .mm_cid = task_mm_cid(t), + .node_id = cpu_to_node(ids.cpu_id), }; - u32 node_id = cpu_to_node(ids.cpu_id); - return rseq_update_usr(t, regs, &ids, node_id); + return rseq_update_usr(t, regs, &ids); efault: return false; } @@ -753,24 +749,6 @@ static __always_inline void rseq_irqentry_exit_to_user_mode(void) ev->events = 0; } -/* Required to keep ARM64 working */ -static __always_inline void rseq_exit_to_user_mode_legacy(void) -{ - struct rseq_event *ev = ¤t->rseq.event; - - rseq_stat_inc(rseq_stats.exit); - - if (static_branch_unlikely(&rseq_debug_enabled)) - WARN_ON_ONCE(ev->sched_switch); - - /* - * Ensure that event (especially user_irq) is cleared when the - * interrupt did not result in a schedule and therefore the - * rseq processing did not clear it. - */ - ev->events = 0; -} - void __rseq_debug_syscall_return(struct pt_regs *regs); static __always_inline void rseq_debug_syscall_return(struct pt_regs *regs) @@ -786,7 +764,6 @@ static inline bool rseq_exit_to_user_mode_restart(struct pt_regs *regs, unsigned } static inline void rseq_syscall_exit_to_user_mode(void) { } static inline void rseq_irqentry_exit_to_user_mode(void) { } -static inline void rseq_exit_to_user_mode_legacy(void) { } static inline void rseq_debug_syscall_return(struct pt_regs *regs) { } static inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask) { return false; } #endif /* !CONFIG_RSEQ */ diff --git a/include/linux/rseq_types.h b/include/linux/rseq_types.h index 0b42045988db..85739a63e85e 100644 --- a/include/linux/rseq_types.h +++ b/include/linux/rseq_types.h @@ -9,6 +9,12 @@ #ifdef CONFIG_RSEQ struct rseq; +/* + * rseq_event::has_rseq contains the ABI version number so preserving it + * in AND operations requires a mask. + */ +#define RSEQ_HAS_RSEQ_VERSION_MASK 0xff + /** * struct rseq_event - Storage for rseq related event management * @all: Compound to initialize and clear the data efficiently @@ -17,7 +23,8 @@ struct rseq; * exit to user * @ids_changed: Indicator that IDs need to be updated * @user_irq: True on interrupt entry from user mode - * @has_rseq: True if the task has a rseq pointer installed + * @has_rseq: Greater than 0 if the task has a rseq pointer installed. + * Contains the RSEQ version number * @error: Compound error code for the slow path to analyze * @fatal: User space data corrupted or invalid * @slowpath: Indicator that slow path processing via TIF_NOTIFY_RESUME @@ -59,8 +66,9 @@ struct rseq_event { * compiler emit a single compare on 64-bit * @cpu_id: The CPU ID which was written last to user space * @mm_cid: The MM CID which was written last to user space + * @node_id: The node ID which was written last to user space * - * @cpu_id and @mm_cid are updated when the data is written to user space. + * @cpu_id, @mm_cid and @node_id are updated when the data is written to user space. */ struct rseq_ids { union { @@ -70,6 +78,7 @@ struct rseq_ids { u32 mm_cid; }; }; + u32 node_id; }; /** diff --git a/include/linux/scc.h b/include/linux/scc.h deleted file mode 100644 index 745eabd17c10..000000000000 --- a/include/linux/scc.h +++ /dev/null @@ -1,86 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* $Id: scc.h,v 1.29 1997/04/02 14:56:45 jreuter Exp jreuter $ */ -#ifndef _SCC_H -#define _SCC_H - -#include - - -enum {TX_OFF, TX_ON}; /* command for scc_key_trx() */ - -/* Vector masks in RR2B */ - -#define VECTOR_MASK 0x06 -#define TXINT 0x00 -#define EXINT 0x02 -#define RXINT 0x04 -#define SPINT 0x06 - -#ifdef CONFIG_SCC_DELAY -#define Inb(port) inb_p(port) -#define Outb(port, val) outb_p(val, port) -#else -#define Inb(port) inb(port) -#define Outb(port, val) outb(val, port) -#endif - -/* SCC channel control structure for KISS */ - -struct scc_kiss { - unsigned char txdelay; /* Transmit Delay 10 ms/cnt */ - unsigned char persist; /* Persistence (0-255) as a % */ - unsigned char slottime; /* Delay to wait on persistence hit */ - unsigned char tailtime; /* Delay after last byte written */ - unsigned char fulldup; /* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */ - unsigned char waittime; /* Waittime before any transmit attempt */ - unsigned int maxkeyup; /* Maximum time to transmit (seconds) */ - unsigned int mintime; /* Minimal offtime after MAXKEYUP timeout (seconds) */ - unsigned int idletime; /* Maximum idle time in ALWAYS KEYED mode (seconds) */ - unsigned int maxdefer; /* Timer for CSMA channel busy limit */ - unsigned char tx_inhibit; /* Transmit is not allowed when set */ - unsigned char group; /* Group ID for AX.25 TX interlocking */ - unsigned char mode; /* 'normal' or 'hwctrl' mode (unused) */ - unsigned char softdcd; /* Use DPLL instead of DCD pin for carrier detect */ -}; - - -/* SCC channel structure */ - -struct scc_channel { - int init; /* channel exists? */ - - struct net_device *dev; /* link to device control structure */ - struct net_device_stats dev_stat;/* device statistics */ - - char brand; /* manufacturer of the board */ - long clock; /* used clock */ - - io_port ctrl; /* I/O address of CONTROL register */ - io_port data; /* I/O address of DATA register */ - io_port special; /* I/O address of special function port */ - int irq; /* Number of Interrupt */ - - char option; - char enhanced; /* Enhanced SCC support */ - - unsigned char wreg[16]; /* Copy of last written value in WRx */ - unsigned char status; /* Copy of R0 at last external interrupt */ - unsigned char dcd; /* DCD status */ - - struct scc_kiss kiss; /* control structure for KISS params */ - struct scc_stat stat; /* statistical information */ - struct scc_modem modem; /* modem information */ - - struct sk_buff_head tx_queue; /* next tx buffer */ - struct sk_buff *rx_buff; /* pointer to frame currently received */ - struct sk_buff *tx_buff; /* pointer to frame currently transmitted */ - - /* Timer */ - struct timer_list tx_t; /* tx timer for this channel */ - struct timer_list tx_wdog; /* tx watchdogs */ - - /* Channel lock */ - spinlock_t lock; /* Channel guard lock */ -}; - -#endif /* defined(_SCC_H) */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 004e6d56a499..ee06cba5c6f5 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1002,6 +1002,9 @@ struct task_struct { unsigned sched_rt_mutex:1; #endif + /* Save user-dumpable when mm goes away */ + unsigned user_dumpable:1; + /* Bit to tell TOMOYO we're in execve(): */ unsigned in_execve:1; unsigned in_iowait:1; @@ -1535,7 +1538,7 @@ struct task_struct { /* Used by memcontrol for targeted memcg charge: */ struct mem_cgroup *active_memcg; - /* Cache for current->cgroups->memcg->objcg lookups: */ + /* Cache for current->cgroups->memcg->nodeinfo[nid]->objcg lookups: */ struct obj_cgroup *objcg; #endif diff --git a/include/linux/sched/deadline.h b/include/linux/sched/deadline.h index 1198138cb839..273538200a44 100644 --- a/include/linux/sched/deadline.h +++ b/include/linux/sched/deadline.h @@ -33,6 +33,15 @@ struct root_domain; extern void dl_add_task_root_domain(struct task_struct *p); extern void dl_clear_root_domain(struct root_domain *rd); extern void dl_clear_root_domain_cpu(int cpu); +/* + * Return whether moving DL task @p to @new_mask requires moving DL + * bandwidth accounting between root domains. This helper is specific to + * DL bandwidth move accounting semantics and is shared by + * cpuset_can_attach() and set_cpus_allowed_dl() so both paths use the + * same source root-domain test. + */ +extern bool dl_task_needs_bw_move(struct task_struct *p, + const struct cpumask *new_mask); extern u64 dl_cookie; extern bool dl_bw_visited(int cpu, u64 cookie); diff --git a/include/linux/sched/isolation.h b/include/linux/sched/isolation.h index dc3975ff1b2e..cf0fd03dd7a2 100644 --- a/include/linux/sched/isolation.h +++ b/include/linux/sched/isolation.h @@ -20,6 +20,11 @@ enum hk_type { HK_TYPE_KERNEL_NOISE, HK_TYPE_MAX, + /* + * HK_TYPE_KTHREAD is now an alias of HK_TYPE_DOMAIN + */ + HK_TYPE_KTHREAD = HK_TYPE_DOMAIN, + /* * The following housekeeping types are only set by the nohz_full * boot commandline option. So they can share the same value. @@ -29,7 +34,6 @@ enum hk_type { HK_TYPE_RCU = HK_TYPE_KERNEL_NOISE, HK_TYPE_MISC = HK_TYPE_KERNEL_NOISE, HK_TYPE_WQ = HK_TYPE_KERNEL_NOISE, - HK_TYPE_KTHREAD = HK_TYPE_KERNEL_NOISE }; #ifdef CONFIG_CPU_ISOLATION diff --git a/include/linux/secure_boot.h b/include/linux/secure_boot.h new file mode 100644 index 000000000000..d17e92351567 --- /dev/null +++ b/include/linux/secure_boot.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2026 Red Hat, Inc. All Rights Reserved. + * + * Author: Coiby Xu + */ + +#ifndef _LINUX_SECURE_BOOT_H +#define _LINUX_SECURE_BOOT_H + +#include + +#ifdef CONFIG_HAVE_ARCH_GET_SECUREBOOT +/* + * Returns true if the platform secure boot is enabled. + * Returns false if disabled or not supported. + */ +bool arch_get_secureboot(void); +#else +static inline bool arch_get_secureboot(void) { return false; } +#endif + +#endif /* _LINUX_SECURE_BOOT_H */ diff --git a/include/linux/serdev.h b/include/linux/serdev.h index 188c0ba62d50..b6c3d957ec15 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -37,8 +37,8 @@ struct serdev_device_ops { * @nr: Device number on serdev bus. * @ctrl: serdev controller managing this device. * @ops: Device operations. - * @write_comp Completion used by serdev_device_write() internally - * @write_lock Lock to serialize access when writing data + * @write_comp: Completion used by serdev_device_write() internally + * @write_lock: Lock to serialize access when writing data */ struct serdev_device { struct device dev; @@ -57,6 +57,7 @@ struct serdev_device { * structure. * @probe: binds this driver to a serdev device. * @remove: unbinds this driver from the serdev device. + * @shutdown: shut down this serdev device. */ struct serdev_device_driver { struct device_driver driver; @@ -120,7 +121,7 @@ static inline void serdev_device_set_drvdata(struct serdev_device *serdev, void /** * serdev_device_put() - decrement serdev device refcount - * @serdev serdev device. + * @serdev: serdev device. */ static inline void serdev_device_put(struct serdev_device *serdev) { @@ -148,7 +149,7 @@ static inline void serdev_controller_set_drvdata(struct serdev_controller *ctrl, /** * serdev_controller_put() - decrement controller refcount - * @ctrl serdev controller. + * @ctrl: serdev controller. */ static inline void serdev_controller_put(struct serdev_controller *ctrl) { diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index f6a2d3402d76..93a0ba872ebe 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -221,20 +221,6 @@ static inline pgoff_t shmem_fallocend(struct inode *inode, pgoff_t eof) extern bool shmem_charge(struct inode *inode, long pages); -#ifdef CONFIG_USERFAULTFD -#ifdef CONFIG_SHMEM -extern int shmem_mfill_atomic_pte(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, - unsigned long src_addr, - uffd_flags_t flags, - struct folio **foliop); -#else /* !CONFIG_SHMEM */ -#define shmem_mfill_atomic_pte(dst_pmd, dst_vma, dst_addr, \ - src_addr, flags, foliop) ({ BUG(); 0; }) -#endif /* CONFIG_SHMEM */ -#endif /* CONFIG_USERFAULTFD */ - /* * Used space is stored as unsigned 64-bit value in bytes but * quota core supports only signed 64-bit values so use that diff --git a/include/linux/slab.h b/include/linux/slab.h index 15a60b501b95..2b5ab488e96b 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -1234,6 +1234,9 @@ void *kvrealloc_node_align_noprof(const void *p, size_t size, unsigned long alig extern void kvfree(const void *addr); DEFINE_FREE(kvfree, void *, if (!IS_ERR_OR_NULL(_T)) kvfree(_T)) +extern void kvfree_atomic(const void *addr); +DEFINE_FREE(kvfree_atomic, void *, if (!IS_ERR_OR_NULL(_T)) kvfree_atomic(_T)) + extern void kvfree_sensitive(const void *addr, size_t len); unsigned int kmem_cache_size(struct kmem_cache *s); diff --git a/fs/smb/common/smbdirect/smbdirect_public.h b/include/linux/smbdirect.h similarity index 76% rename from fs/smb/common/smbdirect/smbdirect_public.h rename to include/linux/smbdirect.h index 50088155e7c3..97f5ba730fa7 100644 --- a/fs/smb/common/smbdirect/smbdirect_public.h +++ b/include/linux/smbdirect.h @@ -3,18 +3,56 @@ * Copyright (C) 2025, Stefan Metzmacher */ -#ifndef __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_PUBLIC_H__ -#define __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_PUBLIC_H__ +#ifndef __LINUX_SMBDIRECT_H__ +#define __LINUX_SMBDIRECT_H__ -struct smbdirect_buffer_descriptor_v1; -struct smbdirect_socket_parameters; +#include + +/* SMB-DIRECT buffer descriptor V1 structure [MS-SMBD] 2.2.3.1 */ +struct smbdirect_buffer_descriptor_v1 { + __le64 offset; + __le32 token; + __le32 length; +} __packed; + +/* + * Connection parameters mostly from [MS-SMBD] 3.1.1.1 + * + * These are setup and negotiated at the beginning of a + * connection and remain constant unless explicitly changed. + * + * Some values are important for the upper layer. + */ +struct smbdirect_socket_parameters { + __u64 flags; +#define SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB ((__u64)0x1) +#define SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW ((__u64)0x2) + __u32 resolve_addr_timeout_msec; + __u32 resolve_route_timeout_msec; + __u32 rdma_connect_timeout_msec; + __u32 negotiate_timeout_msec; + __u16 initiator_depth; /* limited to U8_MAX */ + __u16 responder_resources; /* limited to U8_MAX */ + __u16 recv_credit_max; + __u16 send_credit_target; + __u32 max_send_size; + __u32 max_fragmented_send_size; + __u32 max_recv_size; + __u32 max_fragmented_recv_size; + __u32 max_read_write_size; + __u32 max_frmr_depth; + __u32 keepalive_interval_msec; + __u32 keepalive_timeout_msec; +} __packed; + +#define SMBDIRECT_FLAG_PORT_RANGE_MASK ( \ + SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB | \ + SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW) struct smbdirect_socket; struct smbdirect_send_batch; struct smbdirect_mr_io; -#define __SMBDIRECT_EXPORT_SYMBOL__(__sym) EXPORT_SYMBOL_FOR_MODULES(__sym, "cifs,ksmbd") - #include u8 smbdirect_netdev_rdma_capable_node_type(struct net_device *netdev); @@ -145,4 +183,4 @@ void smbdirect_connection_legacy_debug_proc_show(struct smbdirect_socket *sc, unsigned int rdma_readwrite_threshold, struct seq_file *m); -#endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_PUBLIC_H__ */ +#endif /* __LINUX_SMBDIRECT_H__ */ diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h index 6e1b1202e818..58fa1df96347 100644 --- a/include/linux/soc/qcom/apr.h +++ b/include/linux/soc/qcom/apr.h @@ -191,7 +191,7 @@ int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt); gpr_port_t *gpr_alloc_port(gpr_device_t *gdev, struct device *dev, gpr_port_cb cb, void *priv); void gpr_free_port(gpr_port_t *port); -int gpr_send_port_pkt(gpr_port_t *port, struct gpr_pkt *pkt); -int gpr_send_pkt(gpr_device_t *gdev, struct gpr_pkt *pkt); +int gpr_send_port_pkt(gpr_port_t *port, const struct gpr_pkt *pkt); +int gpr_send_pkt(gpr_device_t *gdev, const struct gpr_pkt *pkt); #endif /* __QCOM_APR_H_ */ diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h index 8243ab3a12a8..227125d84318 100644 --- a/include/linux/soc/qcom/llcc-qcom.h +++ b/include/linux/soc/qcom/llcc-qcom.h @@ -91,10 +91,12 @@ * struct llcc_slice_desc - Cache slice descriptor * @slice_id: llcc slice id * @slice_size: Size allocated for the llcc slice + * @refcount: Atomic counter to track activate/deactivate calls */ struct llcc_slice_desc { u32 slice_id; size_t slice_size; + refcount_t refcount; }; /** @@ -152,11 +154,10 @@ struct llcc_edac_reg_offset { * @edac_reg_offset: Offset of the LLCC EDAC registers * @lock: mutex associated with each slice * @cfg_size: size of the config data table - * @max_slices: max slices as read from device tree * @num_banks: Number of llcc banks - * @bitmap: Bit map to track the active slice ids * @ecc_irq: interrupt for llcc cache error detection and reporting * @ecc_irq_configured: 'True' if firmware has already configured the irq propagation + * @desc: Array pointer of pre-allocated LLCC slice descriptors * @version: Indicates the LLCC version */ struct llcc_drv_data { @@ -167,12 +168,11 @@ struct llcc_drv_data { const struct llcc_edac_reg_offset *edac_reg_offset; struct mutex lock; u32 cfg_size; - u32 max_slices; u32 num_banks; - unsigned long *bitmap; int ecc_irq; bool ecc_irq_configured; u32 version; + struct llcc_slice_desc *desc; }; #if IS_ENABLED(CONFIG_QCOM_LLCC) diff --git a/include/linux/soc/qcom/qmi.h b/include/linux/soc/qcom/qmi.h index 291cdc7ef49c..b9dcb437a0be 100644 --- a/include/linux/soc/qcom/qmi.h +++ b/include/linux/soc/qcom/qmi.h @@ -92,6 +92,18 @@ struct qmi_elem_info { #define QMI_ERR_INCOMPATIBLE_STATE_V01 90 #define QMI_ERR_NOT_SUPPORTED_V01 94 +/* + * Enumerate the IDs of the QMI services + */ +#define QMI_SERVICE_ID_TEST 0x0f /* 15 */ +#define QMI_SERVICE_ID_SSCTL 0x2b /* 43 */ +#define QMI_SERVICE_ID_IPA 0x31 /* 49 */ +#define QMI_SERVICE_ID_SERVREG_LOC 0x40 /* 64 */ +#define QMI_SERVICE_ID_SERVREG_NOTIF 0x42 /* 66 */ +#define QMI_SERVICE_ID_WLFW 0x45 /* 69 */ +#define QMI_SERVICE_ID_SLIMBUS 0x301 /* 769 */ +#define QMI_SERVICE_ID_USB_AUDIO_STREAM 0x41d /* 1053 */ + /** * struct qmi_response_type_v01 - common response header (decoded) * @result: result of the transaction diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index f052e241736c..f5d0e2341261 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -74,4 +74,29 @@ static inline bool qcom_ubwc_get_ubwc_mode(const struct qcom_ubwc_cfg_data *cfg) return ret; } +/* + * This is the best guess, based on the MDSS driver, which worked so far. + */ +static inline bool qcom_ubwc_min_acc_length_64b(const struct qcom_ubwc_cfg_data *cfg) +{ + return cfg->ubwc_enc_version == UBWC_1_0 && + (cfg->ubwc_dec_version == UBWC_2_0 || + cfg->ubwc_dec_version == UBWC_3_0); +} + +static inline bool qcom_ubwc_macrotile_mode(const struct qcom_ubwc_cfg_data *cfg) +{ + return cfg->macrotile_mode; +} + +static inline bool qcom_ubwc_bank_spread(const struct qcom_ubwc_cfg_data *cfg) +{ + return cfg->ubwc_bank_spread; +} + +static inline u32 qcom_ubwc_swizzle(const struct qcom_ubwc_cfg_data *cfg) +{ + return cfg->ubwc_swizzle; +} + #endif /* __QCOM_UBWC_H__ */ diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index f462717acf20..6147eb1fb210 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -532,7 +532,7 @@ struct sdw_slave_intr_status { }; /** - * sdw_reg_bank - SoundWire register banks + * enum sdw_reg_bank - SoundWire register banks * @SDW_BANK0: Soundwire register bank 0 * @SDW_BANK1: Soundwire register bank 1 */ @@ -751,7 +751,7 @@ struct sdw_port_params { * struct sdw_transport_params: Data Port Transport Parameters * * @blk_grp_ctrl_valid: Port implements block group control - * @num: Port number + * @port_num: Port number * @blk_grp_ctrl: Block group control value * @sample_interval: Sample interval * @offset1: Blockoffset of the payload data @@ -782,7 +782,7 @@ struct sdw_transport_params { /** * struct sdw_enable_ch: Enable/disable Data Port channel * - * @num: Port number + * @port_num: Port number * @ch_mask: Active channel mask * @enable: Enable (true) /disable (false) channel */ @@ -885,7 +885,7 @@ void sdw_bus_master_delete(struct sdw_bus *bus); void sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay); /** - * sdw_port_config: Master or Slave Port configuration + * struct sdw_port_config: Master or Slave Port configuration * * @num: Port number * @ch_mask: channels mask for port @@ -896,7 +896,7 @@ struct sdw_port_config { }; /** - * sdw_stream_config: Master or Slave stream configuration + * struct sdw_stream_config: Master or Slave stream configuration * * @frame_rate: Audio frame rate of the stream, in Hz * @ch_count: Channel count of the stream @@ -913,7 +913,7 @@ struct sdw_stream_config { }; /** - * sdw_stream_state: Stream states + * enum sdw_stream_state: Stream states * * @SDW_STREAM_ALLOCATED: New stream allocated. * @SDW_STREAM_CONFIGURED: Stream configured @@ -934,7 +934,7 @@ enum sdw_stream_state { }; /** - * sdw_stream_params: Stream parameters + * struct sdw_stream_params: Stream parameters * * @rate: Sampling frequency, in Hz * @ch_count: Number of channels @@ -947,7 +947,7 @@ struct sdw_stream_params { }; /** - * sdw_stream_runtime: Runtime stream parameters + * struct sdw_stream_runtime: Runtime stream parameters * * @name: SoundWire stream name * @params: Stream parameters @@ -983,7 +983,7 @@ struct sdw_stream_runtime { * @defer_msg: Defer message * @params: Current bus parameters * @stream_refcount: number of streams currently using this bus - * @btp_stream_refcount: number of BTP streams currently using this bus (should + * @bpt_stream_refcount: number of BTP streams currently using this bus (should * be zero or one, multiple streams per link is not supported). * @bpt_stream: pointer stored to handle BTP streams. * @ops: Master callback ops diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h index fe31773d5210..470360a2723c 100644 --- a/include/linux/soundwire/sdw_amd.h +++ b/include/linux/soundwire/sdw_amd.h @@ -66,8 +66,10 @@ struct sdw_amd_dai_runtime { * @status: peripheral devices status array * @num_din_ports: number of input ports * @num_dout_ports: number of output ports + * @max_ports: total number of input ports and output ports * @cols_index: Column index in frame shape * @rows_index: Rows index in frame shape + * @port_offset_map: dynamic array to map port block offset * @instance: SoundWire manager instance * @quirks: SoundWire manager quirks * @wake_en_mask: wake enable mask per SoundWire manager @@ -92,10 +94,12 @@ struct amd_sdw_manager { int num_din_ports; int num_dout_ports; + int max_ports; int cols_index; int rows_index; + int *port_offset_map; u32 instance; u32 quirks; u32 wake_en_mask; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 7587b1c5d7ec..79513f5941cc 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -701,7 +701,7 @@ struct spi_controller { int (*transfer)(struct spi_device *spi, struct spi_message *mesg); - /* Called on release() to free memory provided by spi_controller */ + /* Called on deregistration to free memory provided by spi_controller */ void (*cleanup)(struct spi_device *spi); /* @@ -1019,7 +1019,7 @@ struct spi_res { * this value may have changed compared to what was requested, depending * on the available snapshotting resolution (DMA transfer, * @ptp_sts_supported is false, etc). - * @ptp_sts_word_post: See @ptp_sts_word_post. The two can be equal (meaning + * @ptp_sts_word_post: See @ptp_sts_word_pre. The two can be equal (meaning * that a single byte should be snapshotted). * If the core takes care of the timestamp (if @ptp_sts_supported is false * for this controller), it will set @ptp_sts_word_pre to 0, and diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h index 72820503514c..01011113d226 100644 --- a/include/linux/stop_machine.h +++ b/include/linux/stop_machine.h @@ -99,7 +99,7 @@ static inline void print_stop_info(const char *log_lvl, struct task_struct *task * stop_machine: freeze the machine on all CPUs and run this function * @fn: the function to run * @data: the data ptr to pass to @fn() - * @cpus: the cpus to run @fn() on (NULL = run on each online CPU) + * @cpus: the cpus to run @fn() on (NULL = one unspecified online CPU) * * Description: This causes a thread to be scheduled on every CPU, which * will run with interrupts disabled. Each CPU specified by @cpus will @@ -133,7 +133,7 @@ int stop_machine(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus); * stop_machine_cpuslocked: freeze the machine on all CPUs and run this function * @fn: the function to run * @data: the data ptr to pass to @fn() - * @cpus: the cpus to run @fn() on (NULL = run on each online CPU) + * @cpus: the cpus to run @fn() on (NULL = one unspecified online CPU) * * Same as above. Avoids nested calls to cpus_read_lock(). * diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index e783132e481f..b1e595c2615b 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -16,6 +16,7 @@ #include #include #include +#include /* * Each cache requires: @@ -112,7 +113,11 @@ struct cache_detail { int entries; /* fields for communication over channel */ - struct list_head queue; + struct list_head requests; + struct list_head readers; + spinlock_t queue_lock; + wait_queue_head_t queue_wait; + u64 next_seqno; atomic_t writers; /* how many time is /channel open */ time64_t last_close; /* if no writers, when did last close */ diff --git a/include/linux/sunrpc/debug.h b/include/linux/sunrpc/debug.h index eb4bd62df319..ab61bed2f7af 100644 --- a/include/linux/sunrpc/debug.h +++ b/include/linux/sunrpc/debug.h @@ -38,6 +38,8 @@ extern unsigned int nlm_debug; do { \ ifdebug(fac) \ __sunrpc_printk(fmt, ##__VA_ARGS__); \ + else \ + no_printk(fmt, ##__VA_ARGS__); \ } while (0) # define dfprintk_rcu(fac, fmt, ...) \ @@ -46,15 +48,15 @@ do { \ rcu_read_lock(); \ __sunrpc_printk(fmt, ##__VA_ARGS__); \ rcu_read_unlock(); \ + } else { \ + no_printk(fmt, ##__VA_ARGS__); \ } \ } while (0) -# define RPC_IFDEBUG(x) x #else # define ifdebug(fac) if (0) -# define dfprintk(fac, fmt, ...) do {} while (0) -# define dfprintk_rcu(fac, fmt, ...) do {} while (0) -# define RPC_IFDEBUG(x) +# define dfprintk(fac, fmt, ...) no_printk(fmt, ##__VA_ARGS__) +# define dfprintk_rcu(fac, fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif /* diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index ccba79ebf893..0dbdf3722537 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -95,10 +95,7 @@ struct rpc_task { int tk_rpc_status; /* Result of last RPC operation */ unsigned short tk_flags; /* misc flags */ unsigned short tk_timeouts; /* maj timeouts */ - -#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) || IS_ENABLED(CONFIG_TRACEPOINTS) unsigned short tk_pid; /* debugging aid */ -#endif unsigned char tk_priority : 2,/* Task priority */ tk_garb_retry : 2, tk_cred_retry : 2; diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index a11acf5cd63b..4be6204f6630 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -134,25 +134,37 @@ enum { extern u32 svc_max_payload(const struct svc_rqst *rqstp); /* - * RPC Requests and replies are stored in one or more pages. - * We maintain an array of pages for each server thread. - * Requests are copied into these pages as they arrive. Remaining - * pages are available to write the reply into. + * RPC Call and Reply messages each have their own page array. + * rq_pages holds the incoming Call message; rq_respages holds + * the outgoing Reply message. Both arrays are sized to + * svc_serv_maxpages() entries and are allocated dynamically. * - * Pages are sent using ->sendmsg with MSG_SPLICE_PAGES so each server thread - * needs to allocate more to replace those used in sending. To help keep track - * of these pages we have a receive list where all pages initialy live, and a - * send list where pages are moved to when there are to be part of a reply. + * Pages are sent using ->sendmsg with MSG_SPLICE_PAGES so each + * server thread needs to allocate more to replace those used in + * sending. * - * We use xdr_buf for holding responses as it fits well with NFS - * read responses (that have a header, and some data pages, and possibly - * a tail) and means we can share some client side routines. + * rq_pages request page contract: * - * The xdr_buf.head kvec always points to the first page in the rq_*pages - * list. The xdr_buf.pages pointer points to the second page on that - * list. xdr_buf.tail points to the end of the first page. - * This assumes that the non-page part of an rpc reply will fit - * in a page - NFSd ensures this. lockd also has no trouble. + * Transport receive paths that move request data pages out of + * rq_pages -- TCP multi-fragment reassembly (svc_tcp_save_pages) + * and RDMA Read I/O (svc_rdma_clear_rqst_pages) -- NULL those + * entries to prevent svc_rqst_release_pages() from freeing pages + * still in transport use, and set rq_pages_nfree to the count. + * svc_alloc_arg() refills only that many rq_pages entries. + * + * For rq_respages, svc_rqst_release_pages() NULLs entries in + * [rq_respages, rq_next_page) after each RPC. svc_alloc_arg() + * refills only that range. + * + * xdr_buf holds responses; the structure fits NFS read responses + * (header, data pages, optional tail) and enables sharing of + * client-side routines. + * + * The xdr_buf.head kvec always points to the first page in the + * rq_*pages list. The xdr_buf.pages pointer points to the second + * page on that list. xdr_buf.tail points to the end of the first + * page. This assumes that the non-page part of an rpc reply will + * fit in a page - NFSd ensures this. lockd also has no trouble. */ /** @@ -162,10 +174,10 @@ extern u32 svc_max_payload(const struct svc_rqst *rqstp); * Returns a count of pages or vectors that can hold the maximum * size RPC message for @serv. * - * Each request/reply pair can have at most one "payload", plus two - * pages, one for the request, and one for the reply. - * nfsd_splice_actor() might need an extra page when a READ payload - * is not page-aligned. + * Each page array can hold at most one payload plus two + * overhead pages (one for the RPC header, one for tail data). + * nfsd_splice_actor() might need an extra page when a READ + * payload is not page-aligned. */ static inline unsigned long svc_serv_maxpages(const struct svc_serv *serv) { @@ -175,6 +187,9 @@ static inline unsigned long svc_serv_maxpages(const struct svc_serv *serv) /* * The context of a single thread, including the request currently being * processed. + * + * RPC programs are free to use rq_private to stash thread-local information. + * The sunrpc layer will not access it. */ struct svc_rqst { struct list_head rq_all; /* all threads list */ @@ -201,11 +216,12 @@ struct svc_rqst { struct xdr_stream rq_res_stream; struct folio *rq_scratch_folio; struct xdr_buf rq_res; - unsigned long rq_maxpages; /* num of entries in rq_pages */ - struct page * *rq_pages; - struct page * *rq_respages; /* points into rq_pages */ + unsigned long rq_maxpages; /* entries per page array */ + unsigned long rq_pages_nfree; /* rq_pages entries NULLed by transport */ + struct page * *rq_pages; /* Call buffer pages */ + struct page * *rq_respages; /* Reply buffer pages */ struct page * *rq_next_page; /* next reply page to use */ - struct page * *rq_page_end; /* one past the last page */ + struct page * *rq_page_end; /* one past the last reply page */ struct folio_batch rq_fbatch; struct bio_vec *rq_bvec; @@ -215,7 +231,6 @@ struct svc_rqst { u32 rq_vers; /* program version */ u32 rq_proc; /* procedure number */ u32 rq_prot; /* IP protocol */ - int rq_cachetype; /* catering to nfsd */ unsigned long rq_flags; /* flags field */ ktime_t rq_qtime; /* enqueue time */ @@ -251,7 +266,7 @@ struct svc_rqst { unsigned long bc_to_initval; unsigned int bc_to_retries; unsigned int rq_status_counter; /* RPC processing counter */ - void **rq_lease_breaker; /* The v4 client breaking a lease */ + void *rq_private; /* For use by the service thread */ }; /* bits for rq_flags */ @@ -483,6 +498,21 @@ int svc_generic_rpcbind_set(struct net *net, #define RPC_MAX_ADDRBUFLEN (63U) +/** + * svc_rqst_page_release - release a page associated with an RPC transaction + * @rqstp: RPC transaction context + * @page: page to release + * + * Released pages are batched and freed together, reducing + * allocator pressure under heavy RPC workloads. + */ +static inline void svc_rqst_page_release(struct svc_rqst *rqstp, + struct page *page) +{ + if (!folio_batch_add(&rqstp->rq_fbatch, page_folio(page))) + __folio_batch_release(&rqstp->rq_fbatch); +} + /* * When we want to reduce the size of the reserved space in the response * buffer, we need to take into account the size of any checksum data that diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 57f4fd94166a..df6e08aaad57 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -84,6 +84,9 @@ struct svcxprt_rdma { atomic_t sc_sq_avail; /* SQEs ready to be consumed */ unsigned int sc_sq_depth; /* Depth of SQ */ + atomic_t sc_sq_ticket_head; /* Next ticket to issue */ + atomic_t sc_sq_ticket_tail; /* Ticket currently serving */ + wait_queue_head_t sc_sq_ticket_wait; /* Ticket ordering waitlist */ __be32 sc_fc_credits; /* Forward credits */ u32 sc_max_requests; /* Max requests */ u32 sc_max_bc_requests;/* Backward credits */ @@ -213,6 +216,7 @@ struct svc_rdma_recv_ctxt { */ struct svc_rdma_write_info { struct svcxprt_rdma *wi_rdma; + struct list_head wi_list; const struct svc_rdma_chunk *wi_chunk; @@ -241,7 +245,10 @@ struct svc_rdma_send_ctxt { struct ib_cqe sc_cqe; struct xdr_buf sc_hdrbuf; struct xdr_stream sc_stream; + + struct list_head sc_write_info_list; struct svc_rdma_write_info sc_reply_info; + void *sc_xprt_buf; int sc_page_count; int sc_cur_sge_no; @@ -274,11 +281,14 @@ extern void svc_rdma_cc_init(struct svcxprt_rdma *rdma, extern void svc_rdma_cc_release(struct svcxprt_rdma *rdma, struct svc_rdma_chunk_ctxt *cc, enum dma_data_direction dir); +extern void svc_rdma_write_chunk_release(struct svcxprt_rdma *rdma, + struct svc_rdma_send_ctxt *ctxt); extern void svc_rdma_reply_chunk_release(struct svcxprt_rdma *rdma, struct svc_rdma_send_ctxt *ctxt); -extern int svc_rdma_send_write_list(struct svcxprt_rdma *rdma, - const struct svc_rdma_recv_ctxt *rctxt, - const struct xdr_buf *xdr); +extern int svc_rdma_prepare_write_list(struct svcxprt_rdma *rdma, + const struct svc_rdma_recv_ctxt *rctxt, + struct svc_rdma_send_ctxt *sctxt, + const struct xdr_buf *xdr); extern int svc_rdma_prepare_reply_chunk(struct svcxprt_rdma *rdma, const struct svc_rdma_pcl *write_pcl, const struct svc_rdma_pcl *reply_pcl, @@ -306,6 +316,13 @@ extern void svc_rdma_send_error_msg(struct svcxprt_rdma *rdma, struct svc_rdma_recv_ctxt *rctxt, int status); extern void svc_rdma_wake_send_waiters(struct svcxprt_rdma *rdma, int avail); +extern int svc_rdma_sq_wait(struct svcxprt_rdma *rdma, + const struct rpc_rdma_cid *cid, int sqecount); +extern int svc_rdma_post_send_err(struct svcxprt_rdma *rdma, + const struct rpc_rdma_cid *cid, + const struct ib_send_wr *bad_wr, + const struct ib_send_wr *first_wr, + int sqecount, int ret); extern int svc_rdma_sendto(struct svc_rqst *); extern int svc_rdma_result_payload(struct svc_rqst *rqstp, unsigned int offset, unsigned int length); diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 152597750f55..b639a6fafcbc 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -290,7 +290,7 @@ xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen) /** * xdr_set_scratch_folio - Attach a scratch buffer for decoding data * @xdr: pointer to xdr_stream struct - * @page: an anonymous folio + * @folio: an anonymous folio * * See xdr_set_scratch_buffer(). */ @@ -330,7 +330,7 @@ static inline void xdr_commit_encode(struct xdr_stream *xdr) * xdr_stream_remaining - Return the number of bytes remaining in the stream * @xdr: pointer to struct xdr_stream * - * Return value: + * Returns: * Number of bytes remaining in @xdr before xdr->end */ static inline size_t @@ -350,7 +350,7 @@ ssize_t xdr_stream_encode_opaque_auth(struct xdr_stream *xdr, u32 flavor, * xdr_align_size - Calculate padded size of an object * @n: Size of an object being XDR encoded (in bytes) * - * Return value: + * Returns: * Size (in bytes) of the object including xdr padding */ static inline size_t @@ -368,7 +368,7 @@ xdr_align_size(size_t n) * This implementation avoids the need for conditional * branches or modulo division. * - * Return value: + * Returns: * Size (in bytes) of the needed XDR pad */ static inline size_t xdr_pad_size(size_t n) @@ -380,7 +380,7 @@ static inline size_t xdr_pad_size(size_t n) * xdr_stream_encode_item_present - Encode a "present" list item * @xdr: pointer to xdr_stream * - * Return values: + * Returns: * On success, returns length in bytes of XDR buffer consumed * %-EMSGSIZE on XDR buffer overflow */ @@ -399,7 +399,7 @@ static inline ssize_t xdr_stream_encode_item_present(struct xdr_stream *xdr) * xdr_stream_encode_item_absent - Encode a "not present" list item * @xdr: pointer to xdr_stream * - * Return values: + * Returns: * On success, returns length in bytes of XDR buffer consumed * %-EMSGSIZE on XDR buffer overflow */ @@ -419,7 +419,7 @@ static inline int xdr_stream_encode_item_absent(struct xdr_stream *xdr) * @p: address in a buffer into which to encode * @n: boolean value to encode * - * Return value: + * Returns: * Address of item following the encoded boolean */ static inline __be32 *xdr_encode_bool(__be32 *p, u32 n) @@ -433,7 +433,7 @@ static inline __be32 *xdr_encode_bool(__be32 *p, u32 n) * @xdr: pointer to xdr_stream * @n: boolean value to encode * - * Return values: + * Returns: * On success, returns length in bytes of XDR buffer consumed * %-EMSGSIZE on XDR buffer overflow */ @@ -453,7 +453,7 @@ static inline int xdr_stream_encode_bool(struct xdr_stream *xdr, __u32 n) * @xdr: pointer to xdr_stream * @n: integer to encode * - * Return values: + * Returns: * On success, returns length in bytes of XDR buffer consumed * %-EMSGSIZE on XDR buffer overflow */ @@ -474,7 +474,7 @@ xdr_stream_encode_u32(struct xdr_stream *xdr, __u32 n) * @xdr: pointer to xdr_stream * @n: integer to encode * - * Return values: + * Returns: * On success, returns length in bytes of XDR buffer consumed * %-EMSGSIZE on XDR buffer overflow */ @@ -495,7 +495,7 @@ xdr_stream_encode_be32(struct xdr_stream *xdr, __be32 n) * @xdr: pointer to xdr_stream * @n: 64-bit integer to encode * - * Return values: + * Returns: * On success, returns length in bytes of XDR buffer consumed * %-EMSGSIZE on XDR buffer overflow */ @@ -517,7 +517,7 @@ xdr_stream_encode_u64(struct xdr_stream *xdr, __u64 n) * @ptr: pointer to void pointer * @len: size of object * - * Return values: + * Returns: * On success, returns length in bytes of XDR buffer consumed * %-EMSGSIZE on XDR buffer overflow */ @@ -542,7 +542,7 @@ xdr_stream_encode_opaque_inline(struct xdr_stream *xdr, void **ptr, size_t len) * @ptr: pointer to opaque data object * @len: size of object pointed to by @ptr * - * Return values: + * Returns: * On success, returns length in bytes of XDR buffer consumed * %-EMSGSIZE on XDR buffer overflow */ @@ -563,7 +563,7 @@ xdr_stream_encode_opaque_fixed(struct xdr_stream *xdr, const void *ptr, size_t l * @ptr: pointer to opaque data object * @len: size of object pointed to by @ptr * - * Return values: + * Returns: * On success, returns length in bytes of XDR buffer consumed * %-EMSGSIZE on XDR buffer overflow */ @@ -585,7 +585,7 @@ xdr_stream_encode_opaque(struct xdr_stream *xdr, const void *ptr, size_t len) * @array: array of integers * @array_size: number of elements in @array * - * Return values: + * Returns: * On success, returns length in bytes of XDR buffer consumed * %-EMSGSIZE on XDR buffer overflow */ @@ -608,7 +608,7 @@ xdr_stream_encode_uint32_array(struct xdr_stream *xdr, * xdr_item_is_absent - symbolically handle XDR discriminators * @p: pointer to undecoded discriminator * - * Return values: + * Returns: * %true if the following XDR item is absent * %false if the following XDR item is present */ @@ -621,7 +621,7 @@ static inline bool xdr_item_is_absent(const __be32 *p) * xdr_item_is_present - symbolically handle XDR discriminators * @p: pointer to undecoded discriminator * - * Return values: + * Returns: * %true if the following XDR item is present * %false if the following XDR item is absent */ @@ -635,7 +635,7 @@ static inline bool xdr_item_is_present(const __be32 *p) * @xdr: pointer to xdr_stream * @ptr: pointer to a u32 in which to store the result * - * Return values: + * Returns: * %0 on success * %-EBADMSG on XDR buffer overflow */ @@ -656,7 +656,7 @@ xdr_stream_decode_bool(struct xdr_stream *xdr, __u32 *ptr) * @xdr: pointer to xdr_stream * @ptr: location to store integer * - * Return values: + * Returns: * %0 on success * %-EBADMSG on XDR buffer overflow */ @@ -677,7 +677,7 @@ xdr_stream_decode_u32(struct xdr_stream *xdr, __u32 *ptr) * @xdr: pointer to xdr_stream * @ptr: location to store integer * - * Return values: + * Returns: * %0 on success * %-EBADMSG on XDR buffer overflow */ @@ -698,7 +698,7 @@ xdr_stream_decode_be32(struct xdr_stream *xdr, __be32 *ptr) * @xdr: pointer to xdr_stream * @ptr: location to store 64-bit integer * - * Return values: + * Returns: * %0 on success * %-EBADMSG on XDR buffer overflow */ @@ -720,7 +720,7 @@ xdr_stream_decode_u64(struct xdr_stream *xdr, __u64 *ptr) * @ptr: location to store data * @len: size of buffer pointed to by @ptr * - * Return values: + * Returns: * %0 on success * %-EBADMSG on XDR buffer overflow */ @@ -746,7 +746,7 @@ xdr_stream_decode_opaque_fixed(struct xdr_stream *xdr, void *ptr, size_t len) * on @xdr. It is therefore expected that the object it points to should * be processed immediately. * - * Return values: + * Returns: * On success, returns size of object stored in *@ptr * %-EBADMSG on XDR buffer overflow * %-EMSGSIZE if the size of the object would exceed @maxlen @@ -777,7 +777,7 @@ xdr_stream_decode_opaque_inline(struct xdr_stream *xdr, void **ptr, size_t maxle * @array: location to store the integer array or NULL * @array_size: number of elements to store * - * Return values: + * Returns: * On success, returns number of elements stored in @array * %-EBADMSG on XDR buffer overflow * %-EMSGSIZE if the size of the array exceeds @array_size diff --git a/include/linux/sunrpc/xdrgen/nlm4.h b/include/linux/sunrpc/xdrgen/nlm4.h new file mode 100644 index 000000000000..e95e8f105624 --- /dev/null +++ b/include/linux/sunrpc/xdrgen/nlm4.h @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Generated by xdrgen. Manual edits will be lost. */ +/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm4.x */ +/* XDR specification modification time: Thu Dec 25 13:10:19 2025 */ + +#ifndef _LINUX_XDRGEN_NLM4_DEF_H +#define _LINUX_XDRGEN_NLM4_DEF_H + +#include +#include + +enum { LM_MAXSTRLEN = 1024 }; + +enum { LM_MAXNAMELEN = 1025 }; + +enum { MAXNETOBJ_SZ = 1024 }; + +typedef opaque netobj; + +enum fsh4_mode { + fsm_DN = 0, + fsm_DR = 1, + fsm_DW = 2, + fsm_DRW = 3, +}; + +typedef enum fsh4_mode fsh4_mode; + +enum fsh4_access { + fsa_NONE = 0, + fsa_R = 1, + fsa_W = 2, + fsa_RW = 3, +}; + +typedef enum fsh4_access fsh4_access; + +enum { SM_MAXSTRLEN = 1024 }; + +typedef u64 uint64; + +typedef s64 int64; + +typedef u32 uint32; + +typedef s32 int32; + +enum nlm4_stats { + NLM4_GRANTED = 0, + NLM4_DENIED = 1, + NLM4_DENIED_NOLOCKS = 2, + NLM4_BLOCKED = 3, + NLM4_DENIED_GRACE_PERIOD = 4, + NLM4_DEADLCK = 5, + NLM4_ROFS = 6, + NLM4_STALE_FH = 7, + NLM4_FBIG = 8, + NLM4_FAILED = 9, +}; + +typedef __be32 nlm4_stats; + +struct nlm4_holder { + bool exclusive; + int32 svid; + netobj oh; + uint64 l_offset; + uint64 l_len; +}; + +struct nlm4_testrply { + nlm4_stats stat; + union { + struct nlm4_holder holder; + } u; +}; + +struct nlm4_stat { + nlm4_stats stat; +}; + +struct nlm4_res { + netobj cookie; + struct nlm4_stat stat; +}; + +struct nlm4_testres { + netobj cookie; + struct nlm4_testrply stat; +}; + +struct nlm4_lock { + string caller_name; + netobj fh; + netobj oh; + int32 svid; + uint64 l_offset; + uint64 l_len; +}; + +struct nlm4_lockargs { + netobj cookie; + bool block; + bool exclusive; + struct nlm4_lock alock; + bool reclaim; + int32 state; +}; + +struct nlm4_cancargs { + netobj cookie; + bool block; + bool exclusive; + struct nlm4_lock alock; +}; + +struct nlm4_testargs { + netobj cookie; + bool exclusive; + struct nlm4_lock alock; +}; + +struct nlm4_unlockargs { + netobj cookie; + struct nlm4_lock alock; +}; + +struct nlm4_share { + string caller_name; + netobj fh; + netobj oh; + fsh4_mode mode; + fsh4_access access; +}; + +struct nlm4_shareargs { + netobj cookie; + struct nlm4_share share; + bool reclaim; +}; + +struct nlm4_shareres { + netobj cookie; + nlm4_stats stat; + int32 sequence; +}; + +struct nlm4_notify { + string name; + int32 state; +}; + +enum { SM_PRIV_SIZE = 16 }; + +struct nlm4_notifyargs { + struct nlm4_notify notify; + u8 private[SM_PRIV_SIZE]; +}; + +enum { + NLMPROC4_NULL = 0, + NLMPROC4_TEST = 1, + NLMPROC4_LOCK = 2, + NLMPROC4_CANCEL = 3, + NLMPROC4_UNLOCK = 4, + NLMPROC4_GRANTED = 5, + NLMPROC4_TEST_MSG = 6, + NLMPROC4_LOCK_MSG = 7, + NLMPROC4_CANCEL_MSG = 8, + NLMPROC4_UNLOCK_MSG = 9, + NLMPROC4_GRANTED_MSG = 10, + NLMPROC4_TEST_RES = 11, + NLMPROC4_LOCK_RES = 12, + NLMPROC4_CANCEL_RES = 13, + NLMPROC4_UNLOCK_RES = 14, + NLMPROC4_GRANTED_RES = 15, + NLMPROC4_SM_NOTIFY = 16, + NLMPROC4_SHARE = 20, + NLMPROC4_UNSHARE = 21, + NLMPROC4_NM_LOCK = 22, + NLMPROC4_FREE_ALL = 23, +}; + +#ifndef NLM4_PROG +#define NLM4_PROG (100021) +#endif + +#define NLM4_netobj_sz (XDR_unsigned_int + XDR_QUADLEN(MAXNETOBJ_SZ)) +#define NLM4_fsh4_mode_sz (XDR_int) +#define NLM4_fsh4_access_sz (XDR_int) +#define NLM4_uint64_sz \ + (XDR_unsigned_hyper) +#define NLM4_int64_sz \ + (XDR_hyper) +#define NLM4_uint32_sz \ + (XDR_unsigned_long) +#define NLM4_int32_sz \ + (XDR_long) +#define NLM4_nlm4_stats_sz (XDR_int) +#define NLM4_nlm4_holder_sz \ + (XDR_bool + NLM4_int32_sz + NLM4_netobj_sz + NLM4_uint64_sz + NLM4_uint64_sz) +#define NLM4_nlm4_testrply_sz \ + (NLM4_nlm4_stats_sz + NLM4_nlm4_holder_sz) +#define NLM4_nlm4_stat_sz \ + (NLM4_nlm4_stats_sz) +#define NLM4_nlm4_res_sz \ + (NLM4_netobj_sz + NLM4_nlm4_stat_sz) +#define NLM4_nlm4_testres_sz \ + (NLM4_netobj_sz + NLM4_nlm4_testrply_sz) +#define NLM4_nlm4_lock_sz \ + (XDR_unsigned_int + XDR_QUADLEN(LM_MAXSTRLEN) + NLM4_netobj_sz + NLM4_netobj_sz + NLM4_int32_sz + NLM4_uint64_sz + NLM4_uint64_sz) +#define NLM4_nlm4_lockargs_sz \ + (NLM4_netobj_sz + XDR_bool + XDR_bool + NLM4_nlm4_lock_sz + XDR_bool + NLM4_int32_sz) +#define NLM4_nlm4_cancargs_sz \ + (NLM4_netobj_sz + XDR_bool + XDR_bool + NLM4_nlm4_lock_sz) +#define NLM4_nlm4_testargs_sz \ + (NLM4_netobj_sz + XDR_bool + NLM4_nlm4_lock_sz) +#define NLM4_nlm4_unlockargs_sz \ + (NLM4_netobj_sz + NLM4_nlm4_lock_sz) +#define NLM4_nlm4_share_sz \ + (XDR_unsigned_int + XDR_QUADLEN(LM_MAXSTRLEN) + NLM4_netobj_sz + NLM4_netobj_sz + NLM4_fsh4_mode_sz + NLM4_fsh4_access_sz) +#define NLM4_nlm4_shareargs_sz \ + (NLM4_netobj_sz + NLM4_nlm4_share_sz + XDR_bool) +#define NLM4_nlm4_shareres_sz \ + (NLM4_netobj_sz + NLM4_nlm4_stats_sz + NLM4_int32_sz) +#define NLM4_nlm4_notify_sz \ + (XDR_unsigned_int + XDR_QUADLEN(LM_MAXNAMELEN) + NLM4_int32_sz) +#define NLM4_nlm4_notifyargs_sz \ + (NLM4_nlm4_notify_sz + XDR_QUADLEN(SM_PRIV_SIZE)) +#define NLM4_MAX_ARGS_SZ \ + (NLM4_nlm4_lockargs_sz) + +#endif /* _LINUX_XDRGEN_NLM4_DEF_H */ diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index f46d1fb8f71a..a82045804d34 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -404,6 +404,8 @@ struct rpc_xprt * xprt_alloc(struct net *net, size_t size, unsigned int max_req); void xprt_free(struct rpc_xprt *); void xprt_add_backlog(struct rpc_xprt *xprt, struct rpc_task *task); +void xprt_add_backlog_noncongested(struct rpc_xprt *xprt, + struct rpc_task *task); bool xprt_wake_up_backlog(struct rpc_xprt *xprt, struct rpc_rqst *req); void xprt_cleanup_ids(void); diff --git a/include/linux/swap.h b/include/linux/swap.h index 4b1f13b5bbad..7a09df6977a5 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -310,8 +310,7 @@ extern unsigned long totalreserve_pages; /* linux/mm/swap.c */ void lru_note_cost_unlock_irq(struct lruvec *lruvec, bool file, - unsigned int nr_io, unsigned int nr_rotated) - __releases(lruvec->lru_lock); + unsigned int nr_io, unsigned int nr_rotated); void lru_note_cost_refault(struct folio *); void folio_add_lru(struct folio *); void folio_add_lru_vma(struct folio *, struct vm_area_struct *); @@ -353,6 +352,7 @@ extern void swap_setup(void); extern unsigned long zone_reclaimable_pages(struct zone *zone); extern unsigned long try_to_free_pages(struct zonelist *zonelist, int order, gfp_t gfp_mask, nodemask_t *mask); +unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru, int zone_idx); #define MEMCG_RECLAIM_MAY_SWAP (1 << 1) #define MEMCG_RECLAIM_PROACTIVE (1 << 2) @@ -547,6 +547,8 @@ static inline int mem_cgroup_swappiness(struct mem_cgroup *memcg) return READ_ONCE(memcg->swappiness); } + +void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid); #else static inline int mem_cgroup_swappiness(struct mem_cgroup *mem) { @@ -611,5 +613,24 @@ static inline bool mem_cgroup_swap_full(struct folio *folio) } #endif +/* for_each_managed_zone_pgdat - helper macro to iterate over all managed zones in a pgdat up to + * and including the specified highidx + * @zone: The current zone in the iterator + * @pgdat: The pgdat which node_zones are being iterated + * @idx: The index variable + * @highidx: The index of the highest zone to return + * + * This macro iterates through all managed zones up to and including the specified highidx. + * The zone iterator enters an invalid state after macro call and must be reinitialized + * before it can be used again. + */ +#define for_each_managed_zone_pgdat(zone, pgdat, idx, highidx) \ + for ((idx) = 0, (zone) = (pgdat)->node_zones; \ + (idx) <= (highidx); \ + (idx)++, (zone)++) \ + if (!managed_zone(zone)) \ + continue; \ + else + #endif /* __KERNEL__*/ #endif /* _LINUX_SWAP_H */ diff --git a/include/linux/t10-pi.h b/include/linux/t10-pi.h index 2c59fe3efcd4..b6c2496866ea 100644 --- a/include/linux/t10-pi.h +++ b/include/linux/t10-pi.h @@ -4,6 +4,7 @@ #include #include +#include /* * A T10 PI-capable target device can be formatted with different @@ -25,6 +26,16 @@ enum t10_dif_type { T10_PI_TYPE3_PROTECTION = 0x3, }; +static inline u64 full_pi_ref_tag(const struct request *rq) +{ + unsigned int shift = ilog2(queue_logical_block_size(rq->q)); + + if (IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) && + rq->q->limits.integrity.interval_exp) + shift = rq->q->limits.integrity.interval_exp; + return blk_rq_pos(rq) >> (shift - SECTOR_SHIFT); +} + /* * T10 Protection Information tuple. */ @@ -39,12 +50,7 @@ struct t10_pi_tuple { static inline u32 t10_pi_ref_tag(struct request *rq) { - unsigned int shift = ilog2(queue_logical_block_size(rq->q)); - - if (IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) && - rq->q->limits.integrity.interval_exp) - shift = rq->q->limits.integrity.interval_exp; - return blk_rq_pos(rq) >> (shift - SECTOR_SHIFT) & 0xffffffff; + return lower_32_bits(full_pi_ref_tag(rq)); } struct crc64_pi_tuple { @@ -64,12 +70,7 @@ static inline u64 lower_48_bits(u64 n) static inline u64 ext_pi_ref_tag(struct request *rq) { - unsigned int shift = ilog2(queue_logical_block_size(rq->q)); - - if (IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY) && - rq->q->limits.integrity.interval_exp) - shift = rq->q->limits.integrity.interval_exp; - return lower_48_bits(blk_rq_pos(rq) >> (shift - SECTOR_SHIFT)); + return lower_48_bits(full_pi_ref_tag(rq)); } #endif diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h index ee5f0bd41f43..f993d5118edd 100644 --- a/include/linux/tee_core.h +++ b/include/linux/tee_core.h @@ -50,7 +50,7 @@ enum tee_dma_heap_id { * @dev: embedded basic device structure * @cdev: embedded cdev * @num_users: number of active users of this device - * @c_no_user: completion used when unregistering the device + * @c_no_users: completion used when unregistering the device * @mutex: mutex protecting @num_users and @idr * @idr: register of user space shared memory objects allocated or * registered on this device @@ -132,6 +132,7 @@ struct tee_driver_ops { /* Size for TEE revision string buffer used by get_tee_revision(). */ #define TEE_REVISION_STR_SIZE 128 +#define TEE_DESC_PRIVILEGED 0x1 /** * struct tee_desc - Describes the TEE driver to the subsystem * @name: name of driver @@ -139,7 +140,6 @@ struct tee_driver_ops { * @owner: module providing the driver * @flags: Extra properties of driver, defined by TEE_DESC_* below */ -#define TEE_DESC_PRIVILEGED 0x1 struct tee_desc { const char *name; const struct tee_driver_ops *ops; @@ -187,7 +187,7 @@ struct tee_protmem_pool_ops { * Allocates a new struct tee_device instance. The device is * removed by tee_device_unregister(). * - * @returns a pointer to a 'struct tee_device' or an ERR_PTR on failure + * @returns: a pointer to a 'struct tee_device' or an ERR_PTR on failure */ struct tee_device *tee_device_alloc(const struct tee_desc *teedesc, struct device *dev, @@ -201,7 +201,7 @@ struct tee_device *tee_device_alloc(const struct tee_desc *teedesc, * tee_device_unregister() need to be called to remove the @teedev if * this function fails. * - * @returns < 0 on failure + * @returns: < 0 on failure */ int tee_device_register(struct tee_device *teedev); @@ -254,14 +254,14 @@ void tee_device_set_dev_groups(struct tee_device *teedev, * tee_session_calc_client_uuid() - Calculates client UUID for session * @uuid: Resulting UUID * @connection_method: Connection method for session (TEE_IOCTL_LOGIN_*) - * @connectuon_data: Connection data for opening session + * @connection_data: Connection data for opening session * * Based on connection method calculates UUIDv5 based client UUID. * * For group based logins verifies that calling process has specified * credentials. * - * @return < 0 on failure + * @returns: < 0 on failure */ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, const u8 connection_data[TEE_IOCTL_UUID_LEN]); @@ -295,7 +295,7 @@ struct tee_shm_pool_ops { * @paddr: Physical address of start of pool * @size: Size in bytes of the pool * - * @returns pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure. + * @returns: pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure. */ struct tee_shm_pool *tee_shm_pool_alloc_res_mem(unsigned long vaddr, phys_addr_t paddr, size_t size, @@ -318,14 +318,16 @@ static inline void tee_shm_pool_free(struct tee_shm_pool *pool) * @paddr: Physical address of start of pool * @size: Size in bytes of the pool * - * @returns pointer to a 'struct tee_protmem_pool' or an ERR_PTR on failure. + * @returns: pointer to a 'struct tee_protmem_pool' or an ERR_PTR on failure. */ struct tee_protmem_pool *tee_protmem_static_pool_alloc(phys_addr_t paddr, size_t size); /** * tee_get_drvdata() - Return driver_data pointer - * @returns the driver_data pointer supplied to tee_register(). + * @teedev: Pointer to the tee_device + * + * @returns: the driver_data pointer supplied to tee_register(). */ void *tee_get_drvdata(struct tee_device *teedev); @@ -334,7 +336,7 @@ void *tee_get_drvdata(struct tee_device *teedev); * TEE driver * @ctx: The TEE context for shared memory allocation * @size: Shared memory allocation size - * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure + * @returns: a pointer to 'struct tee_shm' on success or an ERR_PTR on failure */ struct tee_shm *tee_shm_alloc_priv_buf(struct tee_context *ctx, size_t size); @@ -354,7 +356,7 @@ void tee_dyn_shm_free_helper(struct tee_shm *shm, /** * tee_shm_is_dynamic() - Check if shared memory object is of the dynamic kind * @shm: Shared memory handle - * @returns true if object is dynamic shared memory + * @returns: true if object is dynamic shared memory */ static inline bool tee_shm_is_dynamic(struct tee_shm *shm) { @@ -370,7 +372,7 @@ void tee_shm_put(struct tee_shm *shm); /** * tee_shm_get_id() - Get id of a shared memory object * @shm: Shared memory handle - * @returns id + * @returns: id */ static inline int tee_shm_get_id(struct tee_shm *shm) { @@ -382,7 +384,7 @@ static inline int tee_shm_get_id(struct tee_shm *shm) * count * @ctx: Context owning the shared memory * @id: Id of shared memory object - * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure + * @returns: a pointer to 'struct tee_shm' on success or an ERR_PTR on failure */ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id); @@ -402,7 +404,7 @@ static inline bool tee_param_is_memref(struct tee_param *param) * teedev_open() - Open a struct tee_device * @teedev: Device to open * - * @return a pointer to struct tee_context on success or an ERR_PTR on failure. + * @returns: pointer to struct tee_context on success or an ERR_PTR on failure. */ struct tee_context *teedev_open(struct tee_device *teedev); diff --git a/include/linux/tpm_eventlog.h b/include/linux/tpm_eventlog.h index 891368e82558..aff8ea2fa98e 100644 --- a/include/linux/tpm_eventlog.h +++ b/include/linux/tpm_eventlog.h @@ -131,11 +131,16 @@ struct tcg_algorithm_info { }; #ifndef TPM_MEMREMAP -#define TPM_MEMREMAP(start, size) NULL +static inline void *TPM_MEMREMAP(unsigned long start, size_t size) +{ + return NULL; +} #endif #ifndef TPM_MEMUNMAP -#define TPM_MEMUNMAP(start, size) do{} while(0) +static inline void TPM_MEMUNMAP(void *mapping, size_t size) +{ +} #endif /** diff --git a/include/linux/trace_printk.h b/include/linux/trace_printk.h index bb5874097f24..2670ec7f4262 100644 --- a/include/linux/trace_printk.h +++ b/include/linux/trace_printk.h @@ -107,7 +107,6 @@ do { \ __trace_printk(_THIS_IP_, fmt, ##args); \ } while (0) -extern __printf(2, 3) int __trace_bprintk(unsigned long ip, const char *fmt, ...); extern __printf(2, 3) diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 1d7f29f5e901..763eea4d80d8 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -202,7 +202,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) #define TP_CONDITION(args...) args /* - * Individual subsystem my have a separate configuration to + * Individual subsystem may have a separate configuration to * enable their tracepoints. By default, this file will create * the tracepoints if CONFIG_TRACEPOINTS is defined. If a subsystem * wants to be able to disable its tracepoints from being created @@ -314,6 +314,10 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) WARN_ONCE(!rcu_is_watching(), \ "RCU not watching for tracepoint"); \ } \ + } \ + static inline void trace_call__##name(proto) \ + { \ + __do_trace_##name(args); \ } #define __DECLARE_TRACE_SYSCALL(name, proto, args, data_proto) \ @@ -333,6 +337,11 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) WARN_ONCE(!rcu_is_watching(), \ "RCU not watching for tracepoint"); \ } \ + } \ + static inline void trace_call__##name(proto) \ + { \ + might_fault(); \ + __do_trace_##name(args); \ } /* @@ -418,6 +427,8 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) #define __DECLARE_TRACE_COMMON(name, proto, args, data_proto) \ static inline void trace_##name(proto) \ { } \ + static inline void trace_call__##name(proto) \ + { } \ static inline int \ register_trace_##name(void (*probe)(data_proto), \ void *data) \ diff --git a/include/linux/tty_buffer.h b/include/linux/tty_buffer.h index 31125e3be3c5..48adcb0e8ff3 100644 --- a/include/linux/tty_buffer.h +++ b/include/linux/tty_buffer.h @@ -34,6 +34,7 @@ static inline u8 *flag_buf_ptr(struct tty_buffer *b, unsigned int ofs) struct tty_bufhead { struct tty_buffer *head; /* Queue head */ + struct workqueue_struct *flip_wq; struct work_struct work; struct mutex lock; atomic_t priority; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 188ee9b768eb..1f2896e56e77 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -69,6 +69,10 @@ struct serial_struct; * Do not create numbered ``/dev`` nodes. For example, create * ``/dev/ttyprintk`` and not ``/dev/ttyprintk0``. Applicable only when a * driver for a single tty device is being allocated. + * + * @TTY_DRIVER_NO_WORKQUEUE: + * Do not create workqueue when tty_register_driver(). Whenever set, flip + * buffer workqueue can be set by tty_port_link_wq() for every port. */ enum tty_driver_flag { TTY_DRIVER_INSTALLED = BIT(0), @@ -79,6 +83,7 @@ enum tty_driver_flag { TTY_DRIVER_HARDWARE_BREAK = BIT(5), TTY_DRIVER_DYNAMIC_ALLOC = BIT(6), TTY_DRIVER_UNNUMBERED_NODE = BIT(7), + TTY_DRIVER_NO_WORKQUEUE = BIT(8), }; enum tty_driver_type { @@ -506,6 +511,7 @@ struct tty_operations { * @flags: tty driver flags (%TTY_DRIVER_) * @proc_entry: proc fs entry, used internally * @other: driver of the linked tty; only used for the PTY driver + * @flip_wq: workqueue to queue flip buffer work on * @ttys: array of active &struct tty_struct, set by tty_standard_install() * @ports: array of &struct tty_port; can be set during initialization by * tty_port_link_device() and similar @@ -539,6 +545,7 @@ struct tty_driver { unsigned long flags; struct proc_dir_entry *proc_entry; struct tty_driver *other; + struct workqueue_struct *flip_wq; /* * Pointer to the tty data structures diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index c5cccc3fc1e8..d227a58e3e49 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -266,7 +266,7 @@ struct tty_ldisc_ops { }; struct tty_ldisc { - struct tty_ldisc_ops *ops; + const struct tty_ldisc_ops *ops; struct tty_struct *tty; }; @@ -281,8 +281,8 @@ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *); void tty_ldisc_flush(struct tty_struct *tty); -int tty_register_ldisc(struct tty_ldisc_ops *new_ldisc); -void tty_unregister_ldisc(struct tty_ldisc_ops *ldisc); +int tty_register_ldisc(const struct tty_ldisc_ops *new_ldisc); +void tty_unregister_ldisc(const struct tty_ldisc_ops *ldisc); int tty_set_ldisc(struct tty_struct *tty, int disc); #endif /* _LINUX_TTY_LDISC_H */ diff --git a/include/linux/tty_port.h b/include/linux/tty_port.h index 660c254f1efe..d2a7882c0b58 100644 --- a/include/linux/tty_port.h +++ b/include/linux/tty_port.h @@ -138,6 +138,7 @@ struct tty_port { kernel */ void tty_port_init(struct tty_port *port); +void tty_port_link_wq(struct tty_port *port, struct workqueue_struct *flip_wq); void tty_port_link_device(struct tty_port *port, struct tty_driver *driver, unsigned index); struct device *tty_port_register_device(struct tty_port *port, @@ -165,6 +166,18 @@ static inline struct tty_port *tty_port_get(struct tty_port *port) return NULL; } +/* + * Never overwrite the workqueue set by tty_port_link_wq(). + * No effect when %TTY_DRIVER_NO_WORKQUEUE is set, as driver->flip_wq is + * %NULL. + */ +static inline void tty_port_link_driver_wq(struct tty_port *port, + struct tty_driver *driver) +{ + if (!port->buf.flip_wq) + tty_port_link_wq(port, driver->flip_wq); +} + /* If the cts flow control is enabled, return true. */ static inline bool tty_port_cts_enabled(const struct tty_port *port) { diff --git a/include/linux/usb.h b/include/linux/usb.h index 4aab20015851..25a203ac7a7e 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -55,7 +55,8 @@ struct ep_device; * @eusb2_isoc_ep_comp: eUSB2 isoc companion descriptor for this endpoint * @urb_list: urbs queued to this endpoint; maintained by usbcore * @hcpriv: for use by HCD; typically holds hardware dma queue head (QH) - * with one or more transfer descriptors (TDs) per urb + * with one or more transfer descriptors (TDs) per urb; must be preserved + * by core while BW is allocated for the endpoint * @ep_dev: ep_device for sysfs info * @extra: descriptors following this endpoint in the configuration * @extralen: how many bytes of "extra" are valid @@ -2081,6 +2082,8 @@ static inline int usb_translate_errors(int error_code) case -ENODEV: case -EOPNOTSUPP: return error_code; + case -ENOSPC: + return -EBUSY; default: return -EIO; } diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 4ac082a63173..97ef37a1ff4a 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -118,8 +118,8 @@ struct cdc_ncm_ctx { u32 timer_interval; u32 max_ndp_size; - u8 is_ndp16; - u8 filtering_supported; + bool is_ndp16; + bool filtering_supported; union { struct usb_cdc_ncm_ndp16 *delayed_ndp16; struct usb_cdc_ncm_ndp32 *delayed_ndp32; diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index 6ccd1b2af993..337a5485af7c 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -34,7 +34,8 @@ enum pd_ctrl_msg_type { PD_CTRL_FR_SWAP = 19, PD_CTRL_GET_PPS_STATUS = 20, PD_CTRL_GET_COUNTRY_CODES = 21, - /* 22-23 Reserved */ + PD_CTRL_GET_SINK_CAP_EXT = 22, + /* 23 Reserved */ PD_CTRL_GET_REVISION = 24, /* 25-31 Reserved */ }; @@ -72,7 +73,8 @@ enum pd_ext_msg_type { PD_EXT_PPS_STATUS = 12, PD_EXT_COUNTRY_INFO = 13, PD_EXT_COUNTRY_CODES = 14, - /* 15-31 Reserved */ + PD_EXT_SINK_CAP_EXT = 15, + /* 16-31 Reserved */ }; #define PD_REV10 0x0 @@ -205,6 +207,72 @@ struct pd_message { }; } __packed; +/* + * count_chunked_data_objs - Helper to calculate number of Data Objects on a 4 + * byte boundary. + * @size: Size of data block for extended message. Should *not* include extended + * header size. + */ +static inline u8 count_chunked_data_objs(u32 size) +{ + size += offsetof(struct pd_chunked_ext_message_data, data); + return ((size / 4) + (size % 4 ? 1 : 0)); +} + +/* Sink Caps Extended Data Block Version */ +#define SKEDB_VER_1_0 1 + +/* Sink Caps Extended Sink Modes */ +#define SINK_MODE_PPS BIT(0) +#define SINK_MODE_VBUS BIT(1) +#define SINK_MODE_AC_SUPPLY BIT(2) +#define SINK_MODE_BATT BIT(3) +#define SINK_MODE_BATT_UL BIT(4) /* Unlimited battery power supply */ +#define SINK_MODE_AVS BIT(5) + +/** + * struct sink_caps_ext_msg - Sink extended capability PD message + * @vid: Vendor ID + * @pid: Product ID + * @xid: Value assigned by USB-IF for product + * @fw: Firmware version + * @hw: Hardware version + * @skedb_ver: Sink Caps Extended Data Block (SKEDB) Version + * @load_step: Indicates the load step slew rate. + * @load_char: Sink overload characteristics + * @compliance: Types of sources the sink has been tested & certified on + * @touch_temp: Indicates the IEC standard to which the touch temperature + * conforms to (if applicable). + * @batt_info: Indicates number batteries and hot swappable ports + * @modes: Charging caps & power sources supported + * @spr_min_pdp: Sink Minimum PDP for SPR mode + * @spr_op_pdp: Sink Operational PDP for SPR mode + * @spr_max_pdp: Sink Maximum PDP for SPR mode + * @epr_min_pdp: Sink Minimum PDP for EPR mode + * @epr_op_pdp: Sink Operational PDP for EPR mode + * @epr_max_pdp: Sink Maximum PDP for EPR mode + */ +struct sink_caps_ext_msg { + __le16 vid; + __le16 pid; + __le32 xid; + u8 fw; + u8 hw; + u8 skedb_ver; + u8 load_step; + __le16 load_char; + u8 compliance; + u8 touch_temp; + u8 batt_info; + u8 modes; + u8 spr_min_pdp; + u8 spr_op_pdp; + u8 spr_max_pdp; + u8 epr_min_pdp; + u8 epr_op_pdp; + u8 epr_max_pdp; +} __packed; + /* PDO: Power Data Object */ #define PDO_MAX_OBJECTS 7 @@ -329,6 +397,32 @@ enum pd_apdo_type { #define PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR GENMASK(19, 10) /* 10mA unit */ #define PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR GENMASK(9, 0) /* 10mA unit */ +/* SPR AVS has two different current ranges 9V - 15V, 15V - 20V */ +#define SPR_AVS_TIER1_MIN_VOLT_MV 9000 +#define SPR_AVS_TIER1_MAX_VOLT_MV 15000 +#define SPR_AVS_TIER2_MAX_VOLT_MV 20000 + +#define SPR_AVS_AVS_SMALL_STEP_V 1 +/* vAvsStep - 100mv */ +#define SPR_AVS_VOLT_MV_STEP 100 +/* SPR AVS RDO Operating Current is in 50mA step */ +#define RDO_SPR_AVS_CURR_MA_STEP 50 +/* SPR AVS RDO Output voltage is in 25mV step */ +#define RDO_SPR_AVS_OUT_VOLT_MV_STEP 25 + +#define RDO_SPR_AVS_VOLT GENMASK(20, 9) +#define RDO_SPR_AVS_CURR GENMASK(6, 0) + +#define RDO_SPR_AVS_OUT_VOLT(mv) \ + FIELD_PREP(RDO_SPR_AVS_VOLT, ((mv) / RDO_SPR_AVS_OUT_VOLT_MV_STEP)) + +#define RDO_SPR_AVS_OP_CURR(ma) \ + FIELD_PREP(RDO_SPR_AVS_CURR, ((ma) / RDO_SPR_AVS_CURR_MA_STEP)) + +#define RDO_AVS(idx, out_mv, op_ma, flags) \ + (RDO_OBJ(idx) | (flags) | \ + RDO_SPR_AVS_OUT_VOLT(out_mv) | RDO_SPR_AVS_OP_CURR(op_ma)) + static inline enum pd_pdo_type pdo_type(u32 pdo) { return (pdo >> PDO_TYPE_SHIFT) & PDO_TYPE_MASK; @@ -339,6 +433,11 @@ static inline unsigned int pdo_fixed_voltage(u32 pdo) return ((pdo >> PDO_FIXED_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; } +static inline unsigned int pdo_fixed_current(u32 pdo) +{ + return ((pdo >> PDO_FIXED_CURR_SHIFT) & PDO_CURR_MASK) * 10; +} + static inline unsigned int pdo_min_voltage(u32 pdo) { return ((pdo >> PDO_VAR_MIN_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; @@ -582,6 +681,11 @@ static inline unsigned int rdo_max_power(u32 rdo) #define PD_P_SNK_STDBY_MW 2500 /* 2500 mW */ +#define PD_I_SNK_STBY_MA 500 /* 500 mA */ + +#define PD_T_AVS_SRC_TRANS_SMALL 50 /* 50 ms */ +#define PD_T_AVS_SRC_TRANS_LARGE 700 /* 700 ms */ + #if IS_ENABLED(CONFIG_TYPEC) struct usb_power_delivery; diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index b22e659f81ba..93079450bba0 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -31,7 +31,7 @@ enum typec_cc_polarity { /* Time to wait for TCPC to complete transmit */ #define PD_T_TCPC_TX_TIMEOUT 100 /* in ms */ #define PD_ROLE_SWAP_TIMEOUT (MSEC_PER_SEC * 10) -#define PD_PPS_CTRL_TIMEOUT (MSEC_PER_SEC * 10) +#define PD_AUG_PSY_CTRL_TIMEOUT (MSEC_PER_SEC * 10) enum tcpm_transmit_status { TCPC_TX_SUCCESS = 0, diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index 0513d333b797..b90cc5cfff8d 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -26,6 +26,9 @@ struct typec_altmode_ops; * @mode: Index of the Mode * @vdo: VDO returned by Discover Modes USB PD command * @active: Tells has the mode been entered or not + * @priority: Priority used by the automatic alternate mode selection process + * @mode_selection: Whether entry to this alternate mode is managed by the + * automatic alternate mode selection process or by the specific driver * @desc: Optional human readable description of the mode * @ops: Operations vector from the driver * @cable_ops: Cable operations vector from the driver. diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index d83e349900a3..d2920f98ab86 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -83,6 +83,39 @@ struct userfaultfd_ctx { extern vm_fault_t handle_userfault(struct vm_fault *vmf, unsigned long reason); +/* VMA userfaultfd operations */ +struct vm_uffd_ops { + /* Checks if a VMA can support userfaultfd */ + bool (*can_userfault)(struct vm_area_struct *vma, vm_flags_t vm_flags); + /* + * Called to resolve UFFDIO_CONTINUE request. + * Should return the folio found at pgoff in the VMA's pagecache if it + * exists or ERR_PTR otherwise. + * The returned folio is locked and with reference held. + */ + struct folio *(*get_folio_noalloc)(struct inode *inode, pgoff_t pgoff); + /* + * Called during resolution of UFFDIO_COPY request. + * Should allocate and return a folio or NULL if allocation fails. + */ + struct folio *(*alloc_folio)(struct vm_area_struct *vma, + unsigned long addr); + /* + * Called during resolution of UFFDIO_COPY request. + * Should only be called with a folio returned by alloc_folio() above. + * The folio will be set to locked. + * Returns 0 on success, error code on failure. + */ + int (*filemap_add)(struct folio *folio, struct vm_area_struct *vma, + unsigned long addr); + /* + * Called during resolution of UFFDIO_COPY request on the error + * handling path. + * Should revert the operation of ->filemap_add(). + */ + void (*filemap_remove)(struct folio *folio, struct vm_area_struct *vma); +}; + /* A combined operation mode + behavior flags. */ typedef unsigned int __bitwise uffd_flags_t; @@ -114,11 +147,6 @@ static inline uffd_flags_t uffd_flags_set_mode(uffd_flags_t flags, enum mfill_at /* Flags controlling behavior. These behavior changes are mode-independent. */ #define MFILL_ATOMIC_WP MFILL_ATOMIC_FLAG(0) -extern int mfill_atomic_install_pte(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, struct page *page, - bool newly_allocated, uffd_flags_t flags); - extern ssize_t mfill_atomic_copy(struct userfaultfd_ctx *ctx, unsigned long dst_start, unsigned long src_start, unsigned long len, uffd_flags_t flags); @@ -211,39 +239,8 @@ static inline bool userfaultfd_armed(struct vm_area_struct *vma) return vma->vm_flags & __VM_UFFD_FLAGS; } -static inline bool vma_can_userfault(struct vm_area_struct *vma, - vm_flags_t vm_flags, - bool wp_async) -{ - vm_flags &= __VM_UFFD_FLAGS; - - if (vma->vm_flags & VM_DROPPABLE) - return false; - - if ((vm_flags & VM_UFFD_MINOR) && - (!is_vm_hugetlb_page(vma) && !vma_is_shmem(vma))) - return false; - - /* - * If wp async enabled, and WP is the only mode enabled, allow any - * memory type. - */ - if (wp_async && (vm_flags == VM_UFFD_WP)) - return true; - - /* - * If user requested uffd-wp but not enabled pte markers for - * uffd-wp, then shmem & hugetlbfs are not supported but only - * anonymous. - */ - if (!uffd_supports_wp_marker() && (vm_flags & VM_UFFD_WP) && - !vma_is_anonymous(vma)) - return false; - - /* By default, allow any of anon|shmem|hugetlb */ - return vma_is_anonymous(vma) || is_vm_hugetlb_page(vma) || - vma_is_shmem(vma); -} +bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags, + bool wp_async); static inline bool vma_has_uffd_without_event_remap(struct vm_area_struct *vma) { diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h index 2ebba746c18f..89165b769e5c 100644 --- a/include/linux/vfio_pci_core.h +++ b/include/linux/vfio_pci_core.h @@ -21,7 +21,7 @@ #define VFIO_PCI_CORE_H #define VFIO_PCI_OFFSET_SHIFT 40 -#define VFIO_PCI_OFFSET_TO_INDEX(off) (off >> VFIO_PCI_OFFSET_SHIFT) +#define VFIO_PCI_OFFSET_TO_INDEX(off) ((u64)(off) >> VFIO_PCI_OFFSET_SHIFT) #define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT) #define VFIO_PCI_OFFSET_MASK (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1) diff --git a/include/linux/virtio_caif.h b/include/linux/virtio_caif.h deleted file mode 100644 index ea722479510c..000000000000 --- a/include/linux/virtio_caif.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) ST-Ericsson AB 2012 - * Author: Sjur Brændeland - * - * This header is BSD licensed so - * anyone can use the definitions to implement compatible remote processors - */ - -#ifndef VIRTIO_CAIF_H -#define VIRTIO_CAIF_H - -#include -struct virtio_caif_transf_config { - __virtio16 headroom; - __virtio16 tailroom; - __virtio32 mtu; - u8 reserved[4]; -}; - -struct virtio_caif_config { - struct virtio_caif_transf_config uplink, downlink; - u8 reserved[8]; -}; -#endif diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 14fb644e1701..d723e4b1cafb 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -64,9 +64,13 @@ ssize_t wmi_string_from_utf8s(struct wmi_string *str, size_t max_chars, const u8 size_t src_length); int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, - const struct wmi_buffer *in, struct wmi_buffer *out); + const struct wmi_buffer *in, struct wmi_buffer *out, size_t min_size); -int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out); +int wmidev_invoke_procedure(struct wmi_device *wdev, u8 instance, u32 method_id, + const struct wmi_buffer *in); + +int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out, + size_t min_size); int wmidev_set_block(struct wmi_device *wdev, u8 instance, const struct wmi_buffer *in); @@ -83,7 +87,7 @@ u8 wmidev_instance_count(struct wmi_device *wdev); * struct wmi_driver - WMI driver structure * @driver: Driver model structure * @id_table: List of WMI GUIDs supported by this driver - * @no_notify_data: Driver supports WMI events which provide no event data + * @min_event_size: Minimum event payload size supported by this driver * @no_singleton: Driver can be instantiated multiple times * @probe: Callback for device binding * @remove: Callback for device unbinding @@ -93,11 +97,14 @@ u8 wmidev_instance_count(struct wmi_device *wdev); * * This represents WMI drivers which handle WMI devices. The data inside the buffer * passed to the @notify_new callback is guaranteed to be aligned on a 8-byte boundary. + * The minimum supported size for said buffer can be specified using @min_event_size. + * WMI drivers that still use the deprecated @notify callback can still set @min_event_size + * to 0 in order to signal that they support WMI events which provide no event data. */ struct wmi_driver { struct device_driver driver; const struct wmi_device_id *id_table; - bool no_notify_data; + size_t min_event_size; bool no_singleton; int (*probe)(struct wmi_device *wdev, const void *context); diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index ab6cb70ca1a5..6177624539b3 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -534,8 +534,10 @@ alloc_workqueue_noprof(const char *fmt, unsigned int flags, int max_active, ...) * Pointer to the allocated workqueue on success, %NULL on failure. */ __printf(2, 5) struct workqueue_struct * -devm_alloc_workqueue(struct device *dev, const char *fmt, unsigned int flags, - int max_active, ...); +devm_alloc_workqueue_noprof(struct device *dev, const char *fmt, + unsigned int flags, int max_active, ...); +#define devm_alloc_workqueue(...) \ + alloc_hooks(devm_alloc_workqueue_noprof(__VA_ARGS__)) #ifdef CONFIG_LOCKDEP /** diff --git a/include/linux/yam.h b/include/linux/yam.h deleted file mode 100644 index a29b04fa1e66..000000000000 --- a/include/linux/yam.h +++ /dev/null @@ -1,67 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/*****************************************************************************/ - -/* - * yam.h -- YAM radio modem driver. - * - * Copyright (C) 1998 Frederic Rible F1OAT (frible@teaser.fr) - * Adapted from baycom.c driver written by Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - */ - -/*****************************************************************************/ - -#define SIOCYAMRESERVED (0) -#define SIOCYAMSCFG (1) /* Set configuration */ -#define SIOCYAMGCFG (2) /* Get configuration */ -#define SIOCYAMSMCS (3) /* Set mcs data */ - -#define YAM_IOBASE (1 << 0) -#define YAM_IRQ (1 << 1) -#define YAM_BITRATE (1 << 2) /* Bit rate of radio port ->57600 */ -#define YAM_MODE (1 << 3) /* 0=simplex 1=duplex 2=duplex+tempo */ -#define YAM_HOLDDLY (1 << 4) /* duplex tempo (sec) */ -#define YAM_TXDELAY (1 << 5) /* Tx Delay (ms) */ -#define YAM_TXTAIL (1 << 6) /* Tx Tail (ms) */ -#define YAM_PERSIST (1 << 7) /* Persist (ms) */ -#define YAM_SLOTTIME (1 << 8) /* Slottime (ms) */ -#define YAM_BAUDRATE (1 << 9) /* Baud rate of rs232 port ->115200 */ - -#define YAM_MAXBITRATE 57600 -#define YAM_MAXBAUDRATE 115200 -#define YAM_MAXMODE 2 -#define YAM_MAXHOLDDLY 99 -#define YAM_MAXTXDELAY 999 -#define YAM_MAXTXTAIL 999 -#define YAM_MAXPERSIST 255 -#define YAM_MAXSLOTTIME 999 - -#define YAM_FPGA_SIZE 5302 - -struct yamcfg { - unsigned int mask; /* Mask of commands */ - unsigned int iobase; /* IO Base of COM port */ - unsigned int irq; /* IRQ of COM port */ - unsigned int bitrate; /* Bit rate of radio port */ - unsigned int baudrate; /* Baud rate of the RS232 port */ - unsigned int txdelay; /* TxDelay */ - unsigned int txtail; /* TxTail */ - unsigned int persist; /* Persistence */ - unsigned int slottime; /* Slottime */ - unsigned int mode; /* mode 0 (simp), 1(Dupl), 2(Dupl+delay) */ - unsigned int holddly; /* PTT delay in FullDuplex 2 mode */ -}; - -struct yamdrv_ioctl_cfg { - int cmd; - struct yamcfg cfg; -}; - -struct yamdrv_ioctl_mcs { - int cmd; - unsigned int bitrate; - unsigned char bits[YAM_FPGA_SIZE]; -}; diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index 60cad0d200a4..fd7a034b8278 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -24,6 +24,8 @@ * @P9_DEBUG_PKT: packet marshalling/unmarshalling * @P9_DEBUG_FSC: FS-cache tracing * @P9_DEBUG_VPKT: Verbose packet debugging (full packet dump) + * @P9_DEBUG_CACHE: cache operations tracing + * @P9_DEBUG_MMAP: memory-mapped I/O tracing * * These flags are passed at mount time to turn on various levels of * verbosity and tracing which will be output to the system logs. @@ -68,13 +70,39 @@ void _p9_debug(enum p9_debug_flags level, const char *func, * @P9_RSYMLINK: make symlink response * @P9_TMKNOD: create a special file object request * @P9_RMKNOD: create a special file object response + * @P9_TLOPEN: open a file for I/O (9P2000.L) + * @P9_RLOPEN: response with qid and iounit (9P2000.L) * @P9_TLCREATE: prepare a handle for I/O on an new file for 9P2000.L * @P9_RLCREATE: response with file access information for 9P2000.L * @P9_TRENAME: rename request * @P9_RRENAME: rename response - * @P9_TMKDIR: create a directory request - * @P9_RMKDIR: create a directory response - * @P9_TVERSION: version handshake request + * @P9_TREADLINK: read symbolic link target (9P2000.L) + * @P9_RREADLINK: response with symbolic link target (9P2000.L) + * @P9_TGETATTR: get file attributes request (9P2000.L) + * @P9_RGETATTR: get file attributes response (9P2000.L) + * @P9_TSETATTR: set file attributes request (9P2000.L) + * @P9_RSETATTR: set file attributes response (9P2000.L) + * @P9_TXATTRWALK: prepare to read/list extended attributes (9P2000.L) + * @P9_RXATTRWALK: response with extended attribute size (9P2000.L) + * @P9_TXATTRCREATE: prepare to set extended attribute (9P2000.L) + * @P9_RXATTRCREATE: set extended attribute response (9P2000.L) + * @P9_TREADDIR: read directory entries request (9P2000.L) + * @P9_RREADDIR: read directory entries response (9P2000.L) + * @P9_TFSYNC: flush cached file data to storage request (9P2000.L) + * @P9_RFSYNC: flush cached file data to storage response (9P2000.L) + * @P9_TLOCK: acquire or release a POSIX record lock (9P2000.L) + * @P9_RLOCK: POSIX record lock response (9P2000.L) + * @P9_TGETLOCK: test for existence of POSIX record lock (9P2000.L) + * @P9_RGETLOCK: POSIX record lock test response (9P2000.L) + * @P9_TLINK: create a hard link (9P2000.L) + * @P9_RLINK: hard link response (9P2000.L) + * @P9_TRENAMEAT: safely rename across directories (9P2000.L) + * @P9_RRENAMEAT: rename response (9P2000.L) + * @P9_TUNLINKAT: unlink a file or directory (9P2000.L) + * @P9_RUNLINKAT: unlink response (9P2000.L) + * @P9_TMKDIR: create a directory request (9P2000.L) + * @P9_RMKDIR: create a directory response (9P2000.L) + * @P9_TVERSION: negotiate protocol version and message size * @P9_RVERSION: version handshake response * @P9_TAUTH: request to establish authentication channel * @P9_RAUTH: response with authentication information @@ -194,6 +222,10 @@ enum p9_msg_t { * @P9_ORCLOSE: remove the file when the file is closed * @P9_OAPPEND: open the file and seek to the end * @P9_OEXCL: only create a file, do not open it + * @P9L_MODE_MASK: mask for protocol mode bits (client-side only) + * @P9L_DIRECT: disable client-side caching for this file + * @P9L_NOWRITECACHE: disable write caching for this file + * @P9L_LOOSE: enable loose cache consistency * * 9P open modes differ slightly from Posix standard modes. * In particular, there are extra modes which specify different diff --git a/include/net/Space.h b/include/net/Space.h index ef42629f4258..6a0b6674d930 100644 --- a/include/net/Space.h +++ b/include/net/Space.h @@ -3,10 +3,5 @@ * ethernet adaptor have the name "eth[0123...]". */ -struct net_device *ultra_probe(int unit); -struct net_device *wd_probe(int unit); struct net_device *ne_probe(int unit); -struct net_device *smc_init(int unit); struct net_device *cs89x0_probe(int unit); -struct net_device *tc515_probe(int unit); -struct net_device *lance_probe(int unit); diff --git a/include/net/atmclip.h b/include/net/atmclip.h deleted file mode 100644 index 70e350e0db3d..000000000000 --- a/include/net/atmclip.h +++ /dev/null @@ -1,53 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* net/atm/atmarp.h - RFC1577 ATM ARP */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - - -#ifndef _ATMCLIP_H -#define _ATMCLIP_H - -#include -#include -#include -#include -#include -#include - - -#define CLIP_VCC(vcc) ((struct clip_vcc *) ((vcc)->user_back)) - -struct sk_buff; - -struct clip_vcc { - struct atm_vcc *vcc; /* VCC descriptor */ - struct atmarp_entry *entry; /* ATMARP table entry, NULL if IP addr. - isn't known yet */ - int xoff; /* 1 if send buffer is full */ - unsigned char encap; /* 0: NULL, 1: LLC/SNAP */ - unsigned long last_use; /* last send or receive operation */ - unsigned long idle_timeout; /* keep open idle for so many jiffies*/ - void (*old_push)(struct atm_vcc *vcc,struct sk_buff *skb); - /* keep old push fn for chaining */ - void (*old_pop)(struct atm_vcc *vcc,struct sk_buff *skb); - /* keep old pop fn for chaining */ - struct clip_vcc *next; /* next VCC */ -}; - - -struct atmarp_entry { - struct clip_vcc *vccs; /* active VCCs; NULL if resolution is - pending */ - unsigned long expires; /* entry expiration time */ - struct neighbour *neigh; /* neighbour back-pointer */ -}; - -#define PRIV(dev) ((struct clip_priv *) netdev_priv(dev)) - -struct clip_priv { - int number; /* for convenience ... */ - spinlock_t xoff_lock; /* ensures that pop is atomic (SMP) */ - struct net_device *next; /* next CLIP interface */ -}; - -#endif diff --git a/include/net/ax25.h b/include/net/ax25.h index 9fc6a6657266..6b2f518facdb 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -1,480 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* - * Declarations of AX.25 type objects. - * - * Alan Cox (GW4PTS) 10/11/93 - */ #ifndef _AX25_H -#define _AX25_H +#define _AX25_H #include -#include -#include -#include -#include -#include -#include -#include -#include -#define AX25_T1CLAMPLO 1 -#define AX25_T1CLAMPHI (30 * HZ) - -#define AX25_BPQ_HEADER_LEN 16 -#define AX25_KISS_HEADER_LEN 1 - -#define AX25_HEADER_LEN 17 -#define AX25_ADDR_LEN 7 -#define AX25_DIGI_HEADER_LEN (AX25_MAX_DIGIS * AX25_ADDR_LEN) -#define AX25_MAX_HEADER_LEN (AX25_HEADER_LEN + AX25_DIGI_HEADER_LEN) - -/* AX.25 Protocol IDs */ -#define AX25_P_ROSE 0x01 -#define AX25_P_VJCOMP 0x06 /* Compressed TCP/IP packet */ - /* Van Jacobsen (RFC 1144) */ -#define AX25_P_VJUNCOMP 0x07 /* Uncompressed TCP/IP packet */ - /* Van Jacobsen (RFC 1144) */ -#define AX25_P_SEGMENT 0x08 /* Segmentation fragment */ -#define AX25_P_TEXNET 0xc3 /* TEXTNET datagram protocol */ -#define AX25_P_LQ 0xc4 /* Link Quality Protocol */ -#define AX25_P_ATALK 0xca /* Appletalk */ -#define AX25_P_ATALK_ARP 0xcb /* Appletalk ARP */ -#define AX25_P_IP 0xcc /* ARPA Internet Protocol */ -#define AX25_P_ARP 0xcd /* ARPA Address Resolution */ -#define AX25_P_FLEXNET 0xce /* FlexNet */ -#define AX25_P_NETROM 0xcf /* NET/ROM */ -#define AX25_P_TEXT 0xF0 /* No layer 3 protocol impl. */ - -/* AX.25 Segment control values */ -#define AX25_SEG_REM 0x7F -#define AX25_SEG_FIRST 0x80 - -#define AX25_CBIT 0x80 /* Command/Response bit */ -#define AX25_EBIT 0x01 /* HDLC Address Extension bit */ -#define AX25_HBIT 0x80 /* Has been repeated bit */ - -#define AX25_SSSID_SPARE 0x60 /* Unused bits in SSID for standard AX.25 */ -#define AX25_ESSID_SPARE 0x20 /* Unused bits in SSID for extended AX.25 */ -#define AX25_DAMA_FLAG 0x20 /* Well, it is *NOT* unused! (dl1bke 951121 */ - -#define AX25_COND_ACK_PENDING 0x01 -#define AX25_COND_REJECT 0x02 -#define AX25_COND_PEER_RX_BUSY 0x04 -#define AX25_COND_OWN_RX_BUSY 0x08 -#define AX25_COND_DAMA_MODE 0x10 - -#ifndef _LINUX_NETDEVICE_H -#include -#endif - -/* Upper sub-layer (LAPB) definitions */ - -/* Control field templates */ -#define AX25_I 0x00 /* Information frames */ -#define AX25_S 0x01 /* Supervisory frames */ -#define AX25_RR 0x01 /* Receiver ready */ -#define AX25_RNR 0x05 /* Receiver not ready */ -#define AX25_REJ 0x09 /* Reject */ -#define AX25_U 0x03 /* Unnumbered frames */ -#define AX25_SABM 0x2f /* Set Asynchronous Balanced Mode */ -#define AX25_SABME 0x6f /* Set Asynchronous Balanced Mode Extended */ -#define AX25_DISC 0x43 /* Disconnect */ -#define AX25_DM 0x0f /* Disconnected mode */ -#define AX25_UA 0x63 /* Unnumbered acknowledge */ -#define AX25_FRMR 0x87 /* Frame reject */ -#define AX25_UI 0x03 /* Unnumbered information */ -#define AX25_XID 0xaf /* Exchange information */ -#define AX25_TEST 0xe3 /* Test */ - -#define AX25_PF 0x10 /* Poll/final bit for standard AX.25 */ -#define AX25_EPF 0x01 /* Poll/final bit for extended AX.25 */ - -#define AX25_ILLEGAL 0x100 /* Impossible to be a real frame type */ - -#define AX25_POLLOFF 0 -#define AX25_POLLON 1 - -/* AX25 L2 C-bit */ -#define AX25_COMMAND 1 -#define AX25_RESPONSE 2 - -/* Define Link State constants. */ - -enum { - AX25_STATE_0, /* Listening */ - AX25_STATE_1, /* SABM sent */ - AX25_STATE_2, /* DISC sent */ - AX25_STATE_3, /* Established */ - AX25_STATE_4 /* Recovery */ -}; - -#define AX25_MODULUS 8 /* Standard AX.25 modulus */ -#define AX25_EMODULUS 128 /* Extended AX.25 modulus */ - -enum { - AX25_PROTO_STD_SIMPLEX, - AX25_PROTO_STD_DUPLEX, -#ifdef CONFIG_AX25_DAMA_SLAVE - AX25_PROTO_DAMA_SLAVE, -#endif - __AX25_PROTO_MAX, - AX25_PROTO_MAX = __AX25_PROTO_MAX -1 -}; - -enum { - AX25_VALUES_IPDEFMODE, /* 0=DG 1=VC */ - AX25_VALUES_AXDEFMODE, /* 0=Normal 1=Extended Seq Nos */ - AX25_VALUES_BACKOFF, /* 0=None 1=Linear 2=Exponential */ - AX25_VALUES_CONMODE, /* Allow connected modes - 0=No 1=no "PID text" 2=all PIDs */ - AX25_VALUES_WINDOW, /* Default window size for standard AX.25 */ - AX25_VALUES_EWINDOW, /* Default window size for extended AX.25 */ - AX25_VALUES_T1, /* Default T1 timeout value */ - AX25_VALUES_T2, /* Default T2 timeout value */ - AX25_VALUES_T3, /* Default T3 timeout value */ - AX25_VALUES_IDLE, /* Connected mode idle timer */ - AX25_VALUES_N2, /* Default N2 value */ - AX25_VALUES_PACLEN, /* AX.25 MTU */ - AX25_VALUES_PROTOCOL, /* Std AX.25, DAMA Slave */ -#ifdef CONFIG_AX25_DAMA_SLAVE - AX25_VALUES_DS_TIMEOUT, /* DAMA Slave timeout */ -#endif - AX25_MAX_VALUES /* THIS MUST REMAIN THE LAST ENTRY OF THIS LIST */ -}; - -#define AX25_DEF_IPDEFMODE 0 /* Datagram */ -#define AX25_DEF_AXDEFMODE 0 /* Normal */ -#define AX25_DEF_BACKOFF 1 /* Linear backoff */ -#define AX25_DEF_CONMODE 2 /* Connected mode allowed */ -#define AX25_DEF_WINDOW 2 /* Window=2 */ -#define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */ -#define AX25_DEF_T1 10000 /* T1=10s */ -#define AX25_DEF_T2 3000 /* T2=3s */ -#define AX25_DEF_T3 300000 /* T3=300s */ -#define AX25_DEF_N2 10 /* N2=10 */ -#define AX25_DEF_IDLE 0 /* Idle=None */ -#define AX25_DEF_PACLEN 256 /* Paclen=256 */ -#define AX25_DEF_PROTOCOL AX25_PROTO_STD_SIMPLEX /* Standard AX.25 */ -#define AX25_DEF_DS_TIMEOUT 180000 /* DAMA timeout 3 minutes */ - -typedef struct ax25_uid_assoc { - struct hlist_node uid_node; - refcount_t refcount; - kuid_t uid; - ax25_address call; -} ax25_uid_assoc; - -#define ax25_uid_for_each(__ax25, list) \ - hlist_for_each_entry(__ax25, list, uid_node) - -#define ax25_uid_hold(ax25) \ - refcount_inc(&((ax25)->refcount)) - -static inline void ax25_uid_put(ax25_uid_assoc *assoc) -{ - if (refcount_dec_and_test(&assoc->refcount)) { - kfree(assoc); - } -} - -typedef struct { - ax25_address calls[AX25_MAX_DIGIS]; - unsigned char repeated[AX25_MAX_DIGIS]; - unsigned char ndigi; - signed char lastrepeat; -} ax25_digi; - -typedef struct ax25_route { - struct ax25_route *next; - ax25_address callsign; - struct net_device *dev; - ax25_digi *digipeat; - char ip_mode; -} ax25_route; - -void __ax25_put_route(ax25_route *ax25_rt); - -extern rwlock_t ax25_route_lock; - -static inline void ax25_route_lock_use(void) -{ - read_lock(&ax25_route_lock); -} - -static inline void ax25_route_lock_unuse(void) -{ - read_unlock(&ax25_route_lock); -} - -typedef struct { - char slave; /* slave_mode? */ - struct timer_list slave_timer; /* timeout timer */ - unsigned short slave_timeout; /* when? */ -} ax25_dama_info; - -typedef struct ax25_dev { - struct list_head list; - - struct net_device *dev; - netdevice_tracker dev_tracker; - - struct net_device *forward; - struct ctl_table_header *sysheader; - int values[AX25_MAX_VALUES]; -#ifdef CONFIG_AX25_DAMA_SLAVE - ax25_dama_info dama; -#endif - refcount_t refcount; - bool device_up; - struct rcu_head rcu; -} ax25_dev; - -typedef struct ax25_cb { - struct hlist_node ax25_node; - ax25_address source_addr, dest_addr; - ax25_digi *digipeat; - ax25_dev *ax25_dev; - netdevice_tracker dev_tracker; - unsigned char iamdigi; - unsigned char state, modulus, pidincl; - unsigned short vs, vr, va; - unsigned char condition, backoff; - unsigned char n2, n2count; - struct timer_list t1timer, t2timer, t3timer, idletimer; - unsigned long t1, t2, t3, idle, rtt; - unsigned short paclen, fragno, fraglen; - struct sk_buff_head write_queue; - struct sk_buff_head reseq_queue; - struct sk_buff_head ack_queue; - struct sk_buff_head frag_queue; - unsigned char window; - struct timer_list timer, dtimer; - struct sock *sk; /* Backlink to socket */ - refcount_t refcount; -} ax25_cb; - -struct ax25_sock { - struct sock sk; - struct ax25_cb *cb; -}; - -#define ax25_sk(ptr) container_of_const(ptr, struct ax25_sock, sk) - -static inline struct ax25_cb *sk_to_ax25(const struct sock *sk) -{ - return ax25_sk(sk)->cb; -} - -#define ax25_for_each(__ax25, list) \ - hlist_for_each_entry(__ax25, list, ax25_node) - -#define ax25_cb_hold(__ax25) \ - refcount_inc(&((__ax25)->refcount)) - -static __inline__ void ax25_cb_put(ax25_cb *ax25) -{ - if (refcount_dec_and_test(&ax25->refcount)) { - kfree(ax25->digipeat); - kfree(ax25); - } -} - -static inline void ax25_dev_hold(ax25_dev *ax25_dev) -{ - refcount_inc(&ax25_dev->refcount); -} - -static inline void ax25_dev_put(ax25_dev *ax25_dev) -{ - if (refcount_dec_and_test(&ax25_dev->refcount)) - kfree_rcu(ax25_dev, rcu); -} -static inline __be16 ax25_type_trans(struct sk_buff *skb, struct net_device *dev) -{ - skb->dev = dev; - skb_reset_mac_header(skb); - skb->pkt_type = PACKET_HOST; - return htons(ETH_P_AX25); -} - -/* af_ax25.c */ -extern struct hlist_head ax25_list; -extern spinlock_t ax25_list_lock; -void ax25_cb_add(ax25_cb *); -struct sock *ax25_find_listener(ax25_address *, int, struct net_device *, int); -struct sock *ax25_get_socket(ax25_address *, ax25_address *, int); -ax25_cb *ax25_find_cb(const ax25_address *, ax25_address *, ax25_digi *, - struct net_device *); -void ax25_send_to_raw(ax25_address *, struct sk_buff *, int); -void ax25_destroy_socket(ax25_cb *); -ax25_cb * __must_check ax25_create_cb(void); -void ax25_fillin_cb(ax25_cb *, ax25_dev *); -struct sock *ax25_make_new(struct sock *, struct ax25_dev *); - -/* ax25_addr.c */ -extern const ax25_address ax25_bcast; -extern const ax25_address ax25_defaddr; -extern const ax25_address null_ax25_address; -char *ax2asc(char *buf, const ax25_address *); -void asc2ax(ax25_address *addr, const char *callsign); -int ax25cmp(const ax25_address *, const ax25_address *); -int ax25digicmp(const ax25_digi *, const ax25_digi *); -const unsigned char *ax25_addr_parse(const unsigned char *, int, - ax25_address *, ax25_address *, ax25_digi *, int *, int *); -int ax25_addr_build(unsigned char *, const ax25_address *, - const ax25_address *, const ax25_digi *, int, int); -int ax25_addr_size(const ax25_digi *); -void ax25_digi_invert(const ax25_digi *, ax25_digi *); - -/* ax25_dev.c */ -extern spinlock_t ax25_dev_lock; - -#if IS_ENABLED(CONFIG_AX25) -static inline ax25_dev *ax25_dev_ax25dev(const struct net_device *dev) -{ - return rcu_dereference_rtnl(dev->ax25_ptr); -} -#endif - -ax25_dev *ax25_addr_ax25dev(ax25_address *); -void ax25_dev_device_up(struct net_device *); -void ax25_dev_device_down(struct net_device *); -int ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *); -struct net_device *ax25_fwd_dev(struct net_device *); -void ax25_dev_free(void); - -/* ax25_ds_in.c */ -int ax25_ds_frame_in(ax25_cb *, struct sk_buff *, int); - -/* ax25_ds_subr.c */ -void ax25_ds_nr_error_recovery(ax25_cb *); -void ax25_ds_enquiry_response(ax25_cb *); -void ax25_ds_establish_data_link(ax25_cb *); -void ax25_dev_dama_off(ax25_dev *); -void ax25_dama_on(ax25_cb *); -void ax25_dama_off(ax25_cb *); - -/* ax25_ds_timer.c */ -void ax25_ds_setup_timer(ax25_dev *); -void ax25_ds_set_timer(ax25_dev *); -void ax25_ds_del_timer(ax25_dev *); -void ax25_ds_timer(ax25_cb *); -void ax25_ds_t1_timeout(ax25_cb *); -void ax25_ds_heartbeat_expiry(ax25_cb *); -void ax25_ds_t3timer_expiry(ax25_cb *); -void ax25_ds_idletimer_expiry(ax25_cb *); - -/* ax25_iface.c */ - -struct ax25_protocol { - struct ax25_protocol *next; - unsigned int pid; - int (*func)(struct sk_buff *, ax25_cb *); -}; - -void ax25_register_pid(struct ax25_protocol *ap); -void ax25_protocol_release(unsigned int); - -struct ax25_linkfail { - struct hlist_node lf_node; - void (*func)(ax25_cb *, int); -}; - -void ax25_linkfail_register(struct ax25_linkfail *lf); -void ax25_linkfail_release(struct ax25_linkfail *lf); -int __must_check ax25_listen_register(const ax25_address *, - struct net_device *); -void ax25_listen_release(const ax25_address *, struct net_device *); -int(*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *); -int ax25_listen_mine(const ax25_address *, struct net_device *); -void ax25_link_failed(ax25_cb *, int); -int ax25_protocol_is_registered(unsigned int); - -/* ax25_in.c */ -int ax25_rx_iframe(ax25_cb *, struct sk_buff *); -int ax25_kiss_rcv(struct sk_buff *, struct net_device *, struct packet_type *, - struct net_device *); - -/* ax25_ip.c */ -netdev_tx_t ax25_ip_xmit(struct sk_buff *skb); -extern const struct header_ops ax25_header_ops; - -/* ax25_out.c */ -ax25_cb *ax25_send_frame(struct sk_buff *, int, const ax25_address *, - ax25_address *, ax25_digi *, struct net_device *); -void ax25_output(ax25_cb *, int, struct sk_buff *); -void ax25_kick(ax25_cb *); -void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int); -void ax25_queue_xmit(struct sk_buff *skb, struct net_device *dev); -int ax25_check_iframes_acked(ax25_cb *, unsigned short); - -/* ax25_route.c */ -void ax25_rt_device_down(struct net_device *); -int ax25_rt_ioctl(unsigned int, void __user *); -extern const struct seq_operations ax25_rt_seqops; -ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev); -struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, - ax25_address *, ax25_digi *); -void ax25_rt_free(void); - -/* ax25_std_in.c */ -int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int); - -/* ax25_std_subr.c */ -void ax25_std_nr_error_recovery(ax25_cb *); -void ax25_std_establish_data_link(ax25_cb *); -void ax25_std_transmit_enquiry(ax25_cb *); -void ax25_std_enquiry_response(ax25_cb *); -void ax25_std_timeout_response(ax25_cb *); - -/* ax25_std_timer.c */ -void ax25_std_heartbeat_expiry(ax25_cb *); -void ax25_std_t1timer_expiry(ax25_cb *); -void ax25_std_t2timer_expiry(ax25_cb *); -void ax25_std_t3timer_expiry(ax25_cb *); -void ax25_std_idletimer_expiry(ax25_cb *); - -/* ax25_subr.c */ -void ax25_clear_queues(ax25_cb *); -void ax25_frames_acked(ax25_cb *, unsigned short); -void ax25_requeue_frames(ax25_cb *); -int ax25_validate_nr(ax25_cb *, unsigned short); -int ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *); -void ax25_send_control(ax25_cb *, int, int, int); -void ax25_return_dm(struct net_device *, ax25_address *, ax25_address *, - ax25_digi *); -void ax25_calculate_t1(ax25_cb *); -void ax25_calculate_rtt(ax25_cb *); -void ax25_disconnect(ax25_cb *, int); - -/* ax25_timer.c */ -void ax25_setup_timers(ax25_cb *); -void ax25_start_heartbeat(ax25_cb *); -void ax25_start_t1timer(ax25_cb *); -void ax25_start_t2timer(ax25_cb *); -void ax25_start_t3timer(ax25_cb *); -void ax25_start_idletimer(ax25_cb *); -void ax25_stop_heartbeat(ax25_cb *); -void ax25_stop_t1timer(ax25_cb *); -void ax25_stop_t2timer(ax25_cb *); -void ax25_stop_t3timer(ax25_cb *); -void ax25_stop_idletimer(ax25_cb *); -int ax25_t1timer_running(ax25_cb *); -unsigned long ax25_display_timer(struct timer_list *); - -/* ax25_uid.c */ -extern int ax25_uid_policy; -ax25_uid_assoc *ax25_findbyuid(kuid_t); -int __must_check ax25_uid_ioctl(int, struct sockaddr_ax25 *); -extern const struct seq_operations ax25_uid_seqops; -void ax25_uid_free(void); - -/* sysctl_net_ax25.c */ -#ifdef CONFIG_SYSCTL -int ax25_register_dev_sysctl(ax25_dev *ax25_dev); -void ax25_unregister_dev_sysctl(ax25_dev *ax25_dev); -#else -static inline int ax25_register_dev_sysctl(ax25_dev *ax25_dev) { return 0; } -static inline void ax25_unregister_dev_sysctl(ax25_dev *ax25_dev) {} -#endif /* CONFIG_SYSCTL */ +#define AX25_ADDR_LEN 7 +#define AX25_P_IP 0xCC #endif diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index a7bffb908c1e..aa600fbf9a53 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -2495,7 +2495,7 @@ void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle, bdaddr_t *bdaddr, u8 addr_type); int hci_abort_conn(struct hci_conn *conn, u8 reason); -u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, +void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, __u8 ltk[16], __u8 key_size); diff --git a/include/net/bond_3ad.h b/include/net/bond_3ad.h index c92d4a976246..05572c19e14b 100644 --- a/include/net/bond_3ad.h +++ b/include/net/bond_3ad.h @@ -243,7 +243,7 @@ typedef struct port { churn_state_t sm_churn_actor_state; churn_state_t sm_churn_partner_state; struct slave *slave; /* pointer to the bond slave that this port belongs to */ - struct aggregator *aggregator; /* pointer to an aggregator that this port related to */ + struct aggregator __rcu *aggregator; /* pointer to an aggregator that this port related to */ struct port *next_port_in_aggregator; /* Next port on the linked list of the parent aggregator */ u32 transaction_id; /* continuous number for identification of Marker PDU's; */ struct lacpdu lacpdu; /* the lacpdu that will be sent for this port */ diff --git a/include/net/caif/caif_dev.h b/include/net/caif/caif_dev.h deleted file mode 100644 index b655d8666f55..000000000000 --- a/include/net/caif/caif_dev.h +++ /dev/null @@ -1,128 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CAIF_DEV_H_ -#define CAIF_DEV_H_ - -#include -#include -#include -#include -#include -#include - -/** - * struct caif_param - CAIF parameters. - * @size: Length of data - * @data: Binary Data Blob - */ -struct caif_param { - u16 size; - u8 data[256]; -}; - -/** - * struct caif_connect_request - Request data for CAIF channel setup. - * @protocol: Type of CAIF protocol to use (at, datagram etc) - * @sockaddr: Socket address to connect. - * @priority: Priority of the connection. - * @link_selector: Link selector (high bandwidth or low latency) - * @ifindex: kernel index of the interface. - * @param: Connect Request parameters (CAIF_SO_REQ_PARAM). - * - * This struct is used when connecting a CAIF channel. - * It contains all CAIF channel configuration options. - */ -struct caif_connect_request { - enum caif_protocol_type protocol; - struct sockaddr_caif sockaddr; - enum caif_channel_priority priority; - enum caif_link_selector link_selector; - int ifindex; - struct caif_param param; -}; - -/** - * caif_connect_client - Connect a client to CAIF Core Stack. - * @config: Channel setup parameters, specifying what address - * to connect on the Modem. - * @client_layer: User implementation of client layer. This layer - * MUST have receive and control callback functions - * implemented. - * @ifindex: Link layer interface index used for this connection. - * @headroom: Head room needed by CAIF protocol. - * @tailroom: Tail room needed by CAIF protocol. - * - * This function connects a CAIF channel. The Client must implement - * the struct cflayer. This layer represents the Client layer and holds - * receive functions and control callback functions. Control callback - * function will receive information about connect/disconnect responses, - * flow control etc (see enum caif_control). - * E.g. CAIF Socket will call this function for each socket it connects - * and have one client_layer instance for each socket. - */ -int caif_connect_client(struct net *net, - struct caif_connect_request *conn_req, - struct cflayer *client_layer, int *ifindex, - int *headroom, int *tailroom); - -/** - * caif_disconnect_client - Disconnects a client from the CAIF stack. - * - * @client_layer: Client layer to be disconnected. - */ -int caif_disconnect_client(struct net *net, struct cflayer *client_layer); - - -/** - * caif_client_register_refcnt - register ref-count functions provided by client. - * - * @adapt_layer: Client layer using CAIF Stack. - * @hold: Function provided by client layer increasing ref-count - * @put: Function provided by client layer decreasing ref-count - * - * Client of the CAIF Stack must register functions for reference counting. - * These functions are called by the CAIF Stack for every upstream packet, - * and must therefore be implemented efficiently. - * - * Client should call caif_free_client when reference count degrease to zero. - */ - -void caif_client_register_refcnt(struct cflayer *adapt_layer, - void (*hold)(struct cflayer *lyr), - void (*put)(struct cflayer *lyr)); -/** - * caif_free_client - Free memory used to manage the client in the CAIF Stack. - * - * @client_layer: Client layer to be removed. - * - * This function must be called from client layer in order to free memory. - * Caller must guarantee that no packets are in flight upstream when calling - * this function. - */ -void caif_free_client(struct cflayer *adap_layer); - -/** - * struct caif_enroll_dev - Enroll a net-device as a CAIF Link layer - * @dev: Network device to enroll. - * @caifdev: Configuration information from CAIF Link Layer - * @link_support: Link layer support layer - * @head_room: Head room needed by link support layer - * @layer: Lowest layer in CAIF stack - * @rcv_fun: Receive function for CAIF stack. - * - * This function enroll a CAIF link layer into CAIF Stack and - * expects the interface to be able to handle CAIF payload. - * The link_support layer is used to add any Link Layer specific - * framing. - */ -int caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev, - struct cflayer *link_support, int head_room, - struct cflayer **layer, int (**rcv_func)( - struct sk_buff *, struct net_device *, - struct packet_type *, struct net_device *)); - -#endif /* CAIF_DEV_H_ */ diff --git a/include/net/caif/caif_device.h b/include/net/caif/caif_device.h deleted file mode 100644 index 91d1fd5b44a4..000000000000 --- a/include/net/caif/caif_device.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CAIF_DEVICE_H_ -#define CAIF_DEVICE_H_ -#include -#include -#include -#include -#include - -/** - * struct caif_dev_common - data shared between CAIF drivers and stack. - * @flowctrl: Flow Control callback function. This function is - * supplied by CAIF Core Stack and is used by CAIF - * Link Layer to send flow-stop to CAIF Core. - * The flow information will be distributed to all - * clients of CAIF. - * - * @link_select: Profile of device, either high-bandwidth or - * low-latency. This member is set by CAIF Link - * Layer Device in order to indicate if this device - * is a high bandwidth or low latency device. - * - * @use_frag: CAIF Frames may be fragmented. - * Is set by CAIF Link Layer in order to indicate if the - * interface receives fragmented frames that must be - * assembled by CAIF Core Layer. - * - * @use_fcs: Indicate if Frame CheckSum (fcs) is used. - * Is set if the physical interface is - * using Frame Checksum on the CAIF Frames. - * - * @use_stx: Indicate STart of frame eXtension (stx) in use. - * Is set if the CAIF Link Layer expects - * CAIF Frames to start with the STX byte. - * - * This structure is shared between the CAIF drivers and the CAIF stack. - * It is used by the device to register its behavior. - * CAIF Core layer must set the member flowctrl in order to supply - * CAIF Link Layer with the flow control function. - * - */ - struct caif_dev_common { - void (*flowctrl)(struct net_device *net, int on); - enum caif_link_selector link_select; - int use_frag; - int use_fcs; - int use_stx; -}; - -#endif /* CAIF_DEVICE_H_ */ diff --git a/include/net/caif/caif_layer.h b/include/net/caif/caif_layer.h deleted file mode 100644 index 053e7c6a6a66..000000000000 --- a/include/net/caif/caif_layer.h +++ /dev/null @@ -1,277 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CAIF_LAYER_H_ -#define CAIF_LAYER_H_ - -#include - -struct cflayer; -struct cfpkt; -struct caif_payload_info; - -#define CAIF_LAYER_NAME_SZ 16 - -/** - * caif_assert() - Assert function for CAIF. - * @assert: expression to evaluate. - * - * This function will print a error message and a do WARN_ON if the - * assertion fails. Normally this will do a stack up at the current location. - */ -#define caif_assert(assert) \ -do { \ - if (!(assert)) { \ - pr_err("caif:Assert detected:'%s'\n", #assert); \ - WARN_ON(!(assert)); \ - } \ -} while (0) - -/** - * enum caif_ctrlcmd - CAIF Stack Control Signaling sent in layer.ctrlcmd(). - * - * @CAIF_CTRLCMD_FLOW_OFF_IND: Flow Control is OFF, transmit function - * should stop sending data - * - * @CAIF_CTRLCMD_FLOW_ON_IND: Flow Control is ON, transmit function - * can start sending data - * - * @CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: Remote end modem has decided to close - * down channel - * - * @CAIF_CTRLCMD_INIT_RSP: Called initially when the layer below - * has finished initialization - * - * @CAIF_CTRLCMD_DEINIT_RSP: Called when de-initialization is - * complete - * - * @CAIF_CTRLCMD_INIT_FAIL_RSP: Called if initialization fails - * - * @_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: CAIF Link layer temporarily cannot - * send more packets. - * @_CAIF_CTRLCMD_PHYIF_FLOW_ON_IND: Called if CAIF Link layer is able - * to send packets again. - * @_CAIF_CTRLCMD_PHYIF_DOWN_IND: Called if CAIF Link layer is going - * down. - * - * These commands are sent upwards in the CAIF stack to the CAIF Client. - * They are used for signaling originating from the modem or CAIF Link Layer. - * These are either responses (*_RSP) or events (*_IND). - */ -enum caif_ctrlcmd { - CAIF_CTRLCMD_FLOW_OFF_IND, - CAIF_CTRLCMD_FLOW_ON_IND, - CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, - CAIF_CTRLCMD_INIT_RSP, - CAIF_CTRLCMD_DEINIT_RSP, - CAIF_CTRLCMD_INIT_FAIL_RSP, - _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, - _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND, - _CAIF_CTRLCMD_PHYIF_DOWN_IND, -}; - -/** - * enum caif_modemcmd - Modem Control Signaling, sent from CAIF Client - * to the CAIF Link Layer or modem. - * - * @CAIF_MODEMCMD_FLOW_ON_REQ: Flow Control is ON, transmit function - * can start sending data. - * - * @CAIF_MODEMCMD_FLOW_OFF_REQ: Flow Control is OFF, transmit function - * should stop sending data. - * - * @_CAIF_MODEMCMD_PHYIF_USEFULL: Notify physical layer that it is in use - * - * @_CAIF_MODEMCMD_PHYIF_USELESS: Notify physical layer that it is - * no longer in use. - * - * These are requests sent 'downwards' in the stack. - * Flow ON, OFF can be indicated to the modem. - */ -enum caif_modemcmd { - CAIF_MODEMCMD_FLOW_ON_REQ = 0, - CAIF_MODEMCMD_FLOW_OFF_REQ = 1, - _CAIF_MODEMCMD_PHYIF_USEFULL = 3, - _CAIF_MODEMCMD_PHYIF_USELESS = 4 -}; - -/** - * enum caif_direction - CAIF Packet Direction. - * Indicate if a packet is to be sent out or to be received in. - * @CAIF_DIR_IN: Incoming packet received. - * @CAIF_DIR_OUT: Outgoing packet to be transmitted. - */ -enum caif_direction { - CAIF_DIR_IN = 0, - CAIF_DIR_OUT = 1 -}; - -/** - * struct cflayer - CAIF Stack layer. - * Defines the framework for the CAIF Core Stack. - * @up: Pointer up to the layer above. - * @dn: Pointer down to the layer below. - * @node: List node used when layer participate in a list. - * @receive: Packet receive function. - * @transmit: Packet transmit function. - * @ctrlcmd: Used for control signalling upwards in the stack. - * @modemcmd: Used for control signaling downwards in the stack. - * @id: The identity of this layer - * @name: Name of the layer. - * - * This structure defines the layered structure in CAIF. - * - * It defines CAIF layering structure, used by all CAIF Layers and the - * layers interfacing CAIF. - * - * In order to integrate with CAIF an adaptation layer on top of the CAIF stack - * and PHY layer below the CAIF stack - * must be implemented. These layer must follow the design principles below. - * - * Principles for layering of protocol layers: - * - All layers must use this structure. If embedding it, then place this - * structure first in the layer specific structure. - * - * - Each layer should not depend on any others layer's private data. - * - * - In order to send data upwards do - * layer->up->receive(layer->up, packet); - * - * - In order to send data downwards do - * layer->dn->transmit(layer->dn, info, packet); - */ -struct cflayer { - struct cflayer *up; - struct cflayer *dn; - struct list_head node; - - /* - * receive() - Receive Function (non-blocking). - * Contract: Each layer must implement a receive function passing the - * CAIF packets upwards in the stack. - * Packet handling rules: - * - The CAIF packet (cfpkt) ownership is passed to the - * called receive function. This means that the - * packet cannot be accessed after passing it to the - * above layer using up->receive(). - * - * - If parsing of the packet fails, the packet must be - * destroyed and negative error code returned - * from the function. - * EXCEPTION: If the framing layer (cffrml) returns - * -EILSEQ, the packet is not freed. - * - * - If parsing succeeds (and above layers return OK) then - * the function must return a value >= 0. - * - * Returns result < 0 indicates an error, 0 or positive value - * indicates success. - * - * @layr: Pointer to the current layer the receive function is - * implemented for (this pointer). - * @cfpkt: Pointer to CaifPacket to be handled. - */ - int (*receive)(struct cflayer *layr, struct cfpkt *cfpkt); - - /* - * transmit() - Transmit Function (non-blocking). - * Contract: Each layer must implement a transmit function passing the - * CAIF packet downwards in the stack. - * Packet handling rules: - * - The CAIF packet (cfpkt) ownership is passed to the - * transmit function. This means that the packet - * cannot be accessed after passing it to the below - * layer using dn->transmit(). - * - * - Upon error the packet ownership is still passed on, - * so the packet shall be freed where error is detected. - * Callers of the transmit function shall not free packets, - * but errors shall be returned. - * - * - Return value less than zero means error, zero or - * greater than zero means OK. - * - * Returns result < 0 indicates an error, 0 or positive value - * indicates success. - * - * @layr: Pointer to the current layer the receive function - * isimplemented for (this pointer). - * @cfpkt: Pointer to CaifPacket to be handled. - */ - int (*transmit) (struct cflayer *layr, struct cfpkt *cfpkt); - - /* - * cttrlcmd() - Control Function upwards in CAIF Stack (non-blocking). - * Used for signaling responses (CAIF_CTRLCMD_*_RSP) - * and asynchronous events from the modem (CAIF_CTRLCMD_*_IND) - * - * @layr: Pointer to the current layer the receive function - * is implemented for (this pointer). - * @ctrl: Control Command. - */ - void (*ctrlcmd) (struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid); - - /* - * modemctrl() - Control Function used for controlling the modem. - * Used to signal down-wards in the CAIF stack. - * Returns 0 on success, < 0 upon failure. - * - * @layr: Pointer to the current layer the receive function - * is implemented for (this pointer). - * @ctrl: Control Command. - */ - int (*modemcmd) (struct cflayer *layr, enum caif_modemcmd ctrl); - - unsigned int id; - char name[CAIF_LAYER_NAME_SZ]; -}; - -/** - * layer_set_up() - Set the up pointer for a specified layer. - * @layr: Layer where up pointer shall be set. - * @above: Layer above. - */ -#define layer_set_up(layr, above) ((layr)->up = (struct cflayer *)(above)) - -/** - * layer_set_dn() - Set the down pointer for a specified layer. - * @layr: Layer where down pointer shall be set. - * @below: Layer below. - */ -#define layer_set_dn(layr, below) ((layr)->dn = (struct cflayer *)(below)) - -/** - * struct dev_info - Physical Device info information about physical layer. - * @dev: Pointer to native physical device. - * @id: Physical ID of the physical connection used by the - * logical CAIF connection. Used by service layers to - * identify their physical id to Caif MUX (CFMUXL)so - * that the MUX can add the correct physical ID to the - * packet. - */ -struct dev_info { - void *dev; - unsigned int id; -}; - -/** - * struct caif_payload_info - Payload information embedded in packet (sk_buff). - * - * @dev_info: Information about the receiving device. - * - * @hdr_len: Header length, used to align pay load on 32bit boundary. - * - * @channel_id: Channel ID of the logical CAIF connection. - * Used by mux to insert channel id into the caif packet. - */ -struct caif_payload_info { - struct dev_info *dev_info; - unsigned short hdr_len; - unsigned short channel_id; -}; - -#endif /* CAIF_LAYER_H_ */ diff --git a/include/net/caif/cfcnfg.h b/include/net/caif/cfcnfg.h deleted file mode 100644 index 8819ff4db35a..000000000000 --- a/include/net/caif/cfcnfg.h +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFCNFG_H_ -#define CFCNFG_H_ -#include -#include -#include -#include - -struct cfcnfg; - -/** - * enum cfcnfg_phy_preference - Physical preference HW Abstraction - * - * @CFPHYPREF_UNSPECIFIED: Default physical interface - * - * @CFPHYPREF_LOW_LAT: Default physical interface for low-latency - * traffic - * @CFPHYPREF_HIGH_BW: Default physical interface for high-bandwidth - * traffic - * @CFPHYPREF_LOOP: TEST only Loopback interface simulating modem - * responses. - * - */ -enum cfcnfg_phy_preference { - CFPHYPREF_UNSPECIFIED, - CFPHYPREF_LOW_LAT, - CFPHYPREF_HIGH_BW, - CFPHYPREF_LOOP -}; - -/** - * cfcnfg_create() - Get the CAIF configuration object given network. - * @net: Network for the CAIF configuration object. - */ -struct cfcnfg *get_cfcnfg(struct net *net); - -/** - * cfcnfg_create() - Create the CAIF configuration object. - */ -struct cfcnfg *cfcnfg_create(void); - -/** - * cfcnfg_remove() - Remove the CFCNFG object - * @cfg: config object - */ -void cfcnfg_remove(struct cfcnfg *cfg); - -/** - * cfcnfg_add_phy_layer() - Adds a physical layer to the CAIF stack. - * @cnfg: Pointer to a CAIF configuration object, created by - * cfcnfg_create(). - * @dev: Pointer to link layer device - * @phy_layer: Specify the physical layer. The transmit function - * MUST be set in the structure. - * @pref: The phy (link layer) preference. - * @link_support: Protocol implementation for link layer specific protocol. - * @fcs: Specify if checksum is used in CAIF Framing Layer. - * @head_room: Head space needed by link specific protocol. - */ -int -cfcnfg_add_phy_layer(struct cfcnfg *cnfg, - struct net_device *dev, struct cflayer *phy_layer, - enum cfcnfg_phy_preference pref, - struct cflayer *link_support, - bool fcs, int head_room); - -/** - * cfcnfg_del_phy_layer - Deletes an phy layer from the CAIF stack. - * - * @cnfg: Pointer to a CAIF configuration object, created by - * cfcnfg_create(). - * @phy_layer: Adaptation layer to be removed. - */ -int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer); - -/** - * cfcnfg_set_phy_state() - Set the state of the physical interface device. - * @cnfg: Configuration object - * @phy_layer: Physical Layer representation - * @up: State of device - */ -int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer, - bool up); - -#endif /* CFCNFG_H_ */ diff --git a/include/net/caif/cfctrl.h b/include/net/caif/cfctrl.h deleted file mode 100644 index 86d17315c8a1..000000000000 --- a/include/net/caif/cfctrl.h +++ /dev/null @@ -1,130 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFCTRL_H_ -#define CFCTRL_H_ -#include -#include - -/* CAIF Control packet commands */ -enum cfctrl_cmd { - CFCTRL_CMD_LINK_SETUP = 0, - CFCTRL_CMD_LINK_DESTROY = 1, - CFCTRL_CMD_LINK_ERR = 2, - CFCTRL_CMD_ENUM = 3, - CFCTRL_CMD_SLEEP = 4, - CFCTRL_CMD_WAKE = 5, - CFCTRL_CMD_LINK_RECONF = 6, - CFCTRL_CMD_START_REASON = 7, - CFCTRL_CMD_RADIO_SET = 8, - CFCTRL_CMD_MODEM_SET = 9, - CFCTRL_CMD_MASK = 0xf -}; - -/* Channel types */ -enum cfctrl_srv { - CFCTRL_SRV_DECM = 0, - CFCTRL_SRV_VEI = 1, - CFCTRL_SRV_VIDEO = 2, - CFCTRL_SRV_DBG = 3, - CFCTRL_SRV_DATAGRAM = 4, - CFCTRL_SRV_RFM = 5, - CFCTRL_SRV_UTIL = 6, - CFCTRL_SRV_MASK = 0xf -}; - -#define CFCTRL_RSP_BIT 0x20 -#define CFCTRL_ERR_BIT 0x10 - -struct cfctrl_rsp { - void (*linksetup_rsp)(struct cflayer *layer, u8 linkid, - enum cfctrl_srv serv, u8 phyid, - struct cflayer *adapt_layer); - void (*linkdestroy_rsp)(struct cflayer *layer, u8 linkid); - void (*linkerror_ind)(void); - void (*enum_rsp)(void); - void (*sleep_rsp)(void); - void (*wake_rsp)(void); - void (*restart_rsp)(void); - void (*radioset_rsp)(void); - void (*reject_rsp)(struct cflayer *layer, u8 linkid, - struct cflayer *client_layer); -}; - -/* Link Setup Parameters for CAIF-Links. */ -struct cfctrl_link_param { - enum cfctrl_srv linktype;/* (T3,T0) Type of Channel */ - u8 priority; /* (P4,P0) Priority of the channel */ - u8 phyid; /* (U2-U0) Physical interface to connect */ - u8 endpoint; /* (E1,E0) Endpoint for data channels */ - u8 chtype; /* (H1,H0) Channel-Type, applies to - * VEI, DEBUG */ - union { - struct { - u8 connid; /* (D7,D0) Video LinkId */ - } video; - - struct { - u32 connid; /* (N31,Ngit0) Connection ID used - * for Datagram */ - } datagram; - - struct { - u32 connid; /* Connection ID used for RFM */ - char volume[20]; /* Volume to mount for RFM */ - } rfm; /* Configuration for RFM */ - - struct { - u16 fifosize_kb; /* Psock FIFO size in KB */ - u16 fifosize_bufs; /* Psock # signal buffers */ - char name[16]; /* Name of the PSOCK service */ - u8 params[255]; /* Link setup Parameters> */ - u16 paramlen; /* Length of Link Setup - * Parameters */ - } utility; /* Configuration for Utility Links (Psock) */ - } u; -}; - -/* This structure is used internally in CFCTRL */ -struct cfctrl_request_info { - int sequence_no; - enum cfctrl_cmd cmd; - u8 channel_id; - struct cfctrl_link_param param; - struct cflayer *client_layer; - struct list_head list; -}; - -struct cfctrl { - struct cfsrvl serv; - struct cfctrl_rsp res; - atomic_t req_seq_no; - atomic_t rsp_seq_no; - struct list_head list; - /* Protects from simultaneous access to first_req list */ - spinlock_t info_list_lock; -#ifndef CAIF_NO_LOOP - u8 loop_linkid; - int loop_linkused[256]; - /* Protects simultaneous access to loop_linkid and loop_linkused */ - spinlock_t loop_linkid_lock; -#endif - -}; - -void cfctrl_enum_req(struct cflayer *cfctrl, u8 physlinkid); -int cfctrl_linkup_request(struct cflayer *cfctrl, - struct cfctrl_link_param *param, - struct cflayer *user_layer); -int cfctrl_linkdown_req(struct cflayer *cfctrl, u8 linkid, - struct cflayer *client); - -struct cflayer *cfctrl_create(void); -struct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer); -int cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer); -void cfctrl_remove(struct cflayer *layr); - -#endif /* CFCTRL_H_ */ diff --git a/include/net/caif/cffrml.h b/include/net/caif/cffrml.h deleted file mode 100644 index 1ab8a80ede4d..000000000000 --- a/include/net/caif/cffrml.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFFRML_H_ -#define CFFRML_H_ -#include -#include - -struct cffrml; -struct cflayer *cffrml_create(u16 phyid, bool use_fcs); -void cffrml_free(struct cflayer *layr); -void cffrml_set_uplayer(struct cflayer *this, struct cflayer *up); -void cffrml_set_dnlayer(struct cflayer *this, struct cflayer *dn); -void cffrml_put(struct cflayer *layr); -void cffrml_hold(struct cflayer *layr); -int cffrml_refcnt_read(struct cflayer *layr); - -#endif /* CFFRML_H_ */ diff --git a/include/net/caif/cfmuxl.h b/include/net/caif/cfmuxl.h deleted file mode 100644 index 92ccb2648309..000000000000 --- a/include/net/caif/cfmuxl.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFMUXL_H_ -#define CFMUXL_H_ -#include - -struct cfsrvl; -struct cffrml; - -struct cflayer *cfmuxl_create(void); -int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid); -struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid); -int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *up, u8 phyid); -struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 linkid); - -#endif /* CFMUXL_H_ */ diff --git a/include/net/caif/cfpkt.h b/include/net/caif/cfpkt.h deleted file mode 100644 index acf664227d96..000000000000 --- a/include/net/caif/cfpkt.h +++ /dev/null @@ -1,232 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFPKT_H_ -#define CFPKT_H_ -#include -#include -struct cfpkt; - -/* Create a CAIF packet. - * len: Length of packet to be created - * @return New packet. - */ -struct cfpkt *cfpkt_create(u16 len); - -/* - * Destroy a CAIF Packet. - * pkt Packet to be destroyed. - */ -void cfpkt_destroy(struct cfpkt *pkt); - -/* - * Extract header from packet. - * - * pkt Packet to extract header data from. - * data Pointer to copy the header data into. - * len Length of head data to copy. - * @return zero on success and error code upon failure - */ -int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len); - -static inline u8 cfpkt_extr_head_u8(struct cfpkt *pkt) -{ - u8 tmp; - - cfpkt_extr_head(pkt, &tmp, 1); - - return tmp; -} - -static inline u16 cfpkt_extr_head_u16(struct cfpkt *pkt) -{ - __le16 tmp; - - cfpkt_extr_head(pkt, &tmp, 2); - - return le16_to_cpu(tmp); -} - -static inline u32 cfpkt_extr_head_u32(struct cfpkt *pkt) -{ - __le32 tmp; - - cfpkt_extr_head(pkt, &tmp, 4); - - return le32_to_cpu(tmp); -} - -/* - * Peek header from packet. - * Reads data from packet without changing packet. - * - * pkt Packet to extract header data from. - * data Pointer to copy the header data into. - * len Length of head data to copy. - * @return zero on success and error code upon failure - */ -int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len); - -/* - * Extract header from trailer (end of packet). - * - * pkt Packet to extract header data from. - * data Pointer to copy the trailer data into. - * len Length of header data to copy. - * @return zero on success and error code upon failure - */ -int cfpkt_extr_trail(struct cfpkt *pkt, void *data, u16 len); - -/* - * Add header to packet. - * - * - * pkt Packet to add header data to. - * data Pointer to data to copy into the header. - * len Length of header data to copy. - * @return zero on success and error code upon failure - */ -int cfpkt_add_head(struct cfpkt *pkt, const void *data, u16 len); - -/* - * Add trailer to packet. - * - * - * pkt Packet to add trailer data to. - * data Pointer to data to copy into the trailer. - * len Length of trailer data to copy. - * @return zero on success and error code upon failure - */ -int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len); - -/* - * Pad trailer on packet. - * Moves data pointer in packet, no content copied. - * - * pkt Packet in which to pad trailer. - * len Length of padding to add. - * @return zero on success and error code upon failure - */ -int cfpkt_pad_trail(struct cfpkt *pkt, u16 len); - -/* - * Add a single byte to packet body (tail). - * - * pkt Packet in which to add byte. - * data Byte to add. - * @return zero on success and error code upon failure - */ -int cfpkt_addbdy(struct cfpkt *pkt, const u8 data); - -/* - * Add a data to packet body (tail). - * - * pkt Packet in which to add data. - * data Pointer to data to copy into the packet body. - * len Length of data to add. - * @return zero on success and error code upon failure - */ -int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len); - -/* - * Checks whether there are more data to process in packet. - * pkt Packet to check. - * @return true if more data are available in packet false otherwise - */ -bool cfpkt_more(struct cfpkt *pkt); - -/* - * Checks whether the packet is erroneous, - * i.e. if it has been attempted to extract more data than available in packet - * or writing more data than has been allocated in cfpkt_create(). - * pkt Packet to check. - * @return true on error false otherwise - */ -bool cfpkt_erroneous(struct cfpkt *pkt); - -/* - * Get the packet length. - * pkt Packet to get length from. - * @return Number of bytes in packet. - */ -u16 cfpkt_getlen(struct cfpkt *pkt); - -/* - * Set the packet length, by adjusting the trailer pointer according to length. - * pkt Packet to set length. - * len Packet length. - * @return Number of bytes in packet. - */ -int cfpkt_setlen(struct cfpkt *pkt, u16 len); - -/* - * cfpkt_append - Appends a packet's data to another packet. - * dstpkt: Packet to append data into, WILL BE FREED BY THIS FUNCTION - * addpkt: Packet to be appended and automatically released, - * WILL BE FREED BY THIS FUNCTION. - * expectlen: Packet's expected total length. This should be considered - * as a hint. - * NB: Input packets will be destroyed after appending and cannot be used - * after calling this function. - * @return The new appended packet. - */ -struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, struct cfpkt *addpkt, - u16 expectlen); - -/* - * cfpkt_split - Split a packet into two packets at the specified split point. - * pkt: Packet to be split (will contain the first part of the data on exit) - * pos: Position to split packet in two parts. - * @return The new packet, containing the second part of the data. - */ -struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos); - -/* - * Iteration function, iterates the packet buffers from start to end. - * - * Checksum iteration function used to iterate buffers - * (we may have packets consisting of a chain of buffers) - * pkt: Packet to calculate checksum for - * iter_func: Function pointer to iteration function - * chks: Checksum calculated so far. - * buf: Pointer to the buffer to checksum - * len: Length of buf. - * data: Initial checksum value. - * @return Checksum of buffer. - */ - -int cfpkt_iterate(struct cfpkt *pkt, - u16 (*iter_func)(u16 chks, void *buf, u16 len), - u16 data); - -/* Map from a "native" packet (e.g. Linux Socket Buffer) to a CAIF packet. - * dir - Direction indicating whether this packet is to be sent or received. - * nativepkt - The native packet to be transformed to a CAIF packet - * @return The mapped CAIF Packet CFPKT. - */ -struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt); - -/* Map from a CAIF packet to a "native" packet (e.g. Linux Socket Buffer). - * pkt - The CAIF packet to be transformed into a "native" packet. - * @return The native packet transformed from a CAIF packet. - */ -void *cfpkt_tonative(struct cfpkt *pkt); - -/* - * Returns packet information for a packet. - * pkt Packet to get info from; - * @return Packet information - */ -struct caif_payload_info *cfpkt_info(struct cfpkt *pkt); - -/** cfpkt_set_prio - set priority for a CAIF packet. - * - * @pkt: The CAIF packet to be adjusted. - * @prio: one of TC_PRIO_ constants. - */ -void cfpkt_set_prio(struct cfpkt *pkt, int prio); - -#endif /* CFPKT_H_ */ diff --git a/include/net/caif/cfserl.h b/include/net/caif/cfserl.h deleted file mode 100644 index 67cce8757175..000000000000 --- a/include/net/caif/cfserl.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFSERL_H_ -#define CFSERL_H_ -#include - -struct cflayer *cfserl_create(int instance, bool use_stx); -void cfserl_release(struct cflayer *layer); -#endif diff --git a/include/net/caif/cfsrvl.h b/include/net/caif/cfsrvl.h deleted file mode 100644 index a000dc45f966..000000000000 --- a/include/net/caif/cfsrvl.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#ifndef CFSRVL_H_ -#define CFSRVL_H_ -#include -#include -#include -#include -#include - -struct cfsrvl { - struct cflayer layer; - bool open; - bool phy_flow_on; - bool modem_flow_on; - bool supports_flowctrl; - void (*release)(struct cflayer *layer); - struct dev_info dev_info; - void (*hold)(struct cflayer *lyr); - void (*put)(struct cflayer *lyr); - struct rcu_head rcu; -}; - -struct cflayer *cfvei_create(u8 linkid, struct dev_info *dev_info); -struct cflayer *cfdgml_create(u8 linkid, struct dev_info *dev_info); -struct cflayer *cfutill_create(u8 linkid, struct dev_info *dev_info); -struct cflayer *cfvidl_create(u8 linkid, struct dev_info *dev_info); -struct cflayer *cfrfml_create(u8 linkid, struct dev_info *dev_info, - int mtu_size); -struct cflayer *cfdbgl_create(u8 linkid, struct dev_info *dev_info); - -bool cfsrvl_phyid_match(struct cflayer *layer, int phyid); - -void cfsrvl_init(struct cfsrvl *service, - u8 channel_id, - struct dev_info *dev_info, - bool supports_flowctrl); -bool cfsrvl_ready(struct cfsrvl *service, int *err); - -static inline void cfsrvl_get(struct cflayer *layr) -{ - struct cfsrvl *s = container_of(layr, struct cfsrvl, layer); - if (layr == NULL || layr->up == NULL || s->hold == NULL) - return; - - s->hold(layr->up); -} - -static inline void cfsrvl_put(struct cflayer *layr) -{ - struct cfsrvl *s = container_of(layr, struct cfsrvl, layer); - if (layr == NULL || layr->up == NULL || s->hold == NULL) - return; - - s->put(layr->up); -} -#endif /* CFSRVL_H_ */ diff --git a/include/net/dropreason-core.h b/include/net/dropreason-core.h index e0ca3904ff8e..2f312d1f67d6 100644 --- a/include/net/dropreason-core.h +++ b/include/net/dropreason-core.h @@ -99,6 +99,7 @@ FN(FRAG_TOO_FAR) \ FN(TCP_MINTTL) \ FN(IPV6_BAD_EXTHDR) \ + FN(IPV6_TOO_MANY_EXTHDRS) \ FN(IPV6_NDISC_FRAG) \ FN(IPV6_NDISC_HOP_LIMIT) \ FN(IPV6_NDISC_BAD_CODE) \ @@ -494,6 +495,11 @@ enum skb_drop_reason { SKB_DROP_REASON_TCP_MINTTL, /** @SKB_DROP_REASON_IPV6_BAD_EXTHDR: Bad IPv6 extension header. */ SKB_DROP_REASON_IPV6_BAD_EXTHDR, + /** + * @SKB_DROP_REASON_IPV6_TOO_MANY_EXTHDRS: Number of IPv6 extension + * headers in the packet exceeds IP6_MAX_EXT_HDRS_CNT. + */ + SKB_DROP_REASON_IPV6_TOO_MANY_EXTHDRS, /** @SKB_DROP_REASON_IPV6_NDISC_FRAG: invalid frag (suppress_frag_ndisc). */ SKB_DROP_REASON_IPV6_NDISC_FRAG, /** @SKB_DROP_REASON_IPV6_NDISC_HOP_LIMIT: invalid hop limit. */ diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 7b84f2cef8b1..d70510ac31ab 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -489,8 +489,10 @@ genlmsg_multicast_netns_filtered(const struct genl_family *family, netlink_filter_fn filter, void *filter_data) { - if (WARN_ON_ONCE(group >= family->n_mcgrps)) + if (WARN_ON_ONCE(group >= family->n_mcgrps)) { + nlmsg_free(skb); return -EINVAL; + } group = family->mcgrp_offset + group; return nlmsg_multicast_filtered(net->genl_sock, skb, portid, group, flags, filter, filter_data); diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 72d325c81313..02762ce73a0c 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -491,6 +491,7 @@ struct ip_vs_est_kt_data { DECLARE_BITMAP(avail, IPVS_EST_NTICKS); /* tick has space for ests */ unsigned long est_timer; /* estimation timer (jiffies) */ struct ip_vs_stats *calc_stats; /* Used for calculation */ + int needed; /* task is needed */ int tick_len[IPVS_EST_NTICKS]; /* est count */ int id; /* ktid per netns */ int chain_max; /* max ests per tick chain */ @@ -1411,7 +1412,7 @@ static inline int sysctl_run_estimation(struct netns_ipvs *ipvs) return ipvs->sysctl_run_estimation; } -static inline const struct cpumask *sysctl_est_cpulist(struct netns_ipvs *ipvs) +static inline const struct cpumask *__sysctl_est_cpulist(struct netns_ipvs *ipvs) { if (ipvs->est_cpulist_valid) return ipvs->sysctl_est_cpulist; @@ -1529,7 +1530,7 @@ static inline int sysctl_run_estimation(struct netns_ipvs *ipvs) return 1; } -static inline const struct cpumask *sysctl_est_cpulist(struct netns_ipvs *ipvs) +static inline const struct cpumask *__sysctl_est_cpulist(struct netns_ipvs *ipvs) { return housekeeping_cpumask(HK_TYPE_KTHREAD); } @@ -1564,6 +1565,18 @@ static inline int sysctl_svc_lfactor(struct netns_ipvs *ipvs) return READ_ONCE(ipvs->sysctl_svc_lfactor); } +static inline bool sysctl_est_cpulist_empty(struct netns_ipvs *ipvs) +{ + guard(rcu)(); + return cpumask_empty(__sysctl_est_cpulist(ipvs)); +} + +static inline unsigned int sysctl_est_cpulist_weight(struct netns_ipvs *ipvs) +{ + guard(rcu)(); + return cpumask_weight(__sysctl_est_cpulist(ipvs)); +} + /* IPVS core functions * (from ip_vs_core.c) */ @@ -1884,18 +1897,26 @@ int ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats); void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats); void ip_vs_zero_estimator(struct ip_vs_stats *stats); void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats); -void ip_vs_est_reload_start(struct netns_ipvs *ipvs); +void ip_vs_est_reload_start(struct netns_ipvs *ipvs, bool restart); int ip_vs_est_kthread_start(struct netns_ipvs *ipvs, struct ip_vs_est_kt_data *kd); void ip_vs_est_kthread_stop(struct ip_vs_est_kt_data *kd); +static inline void ip_vs_stop_estimator_tot_stats(struct netns_ipvs *ipvs) +{ +#ifdef CONFIG_SYSCTL + ip_vs_stop_estimator(ipvs, &ipvs->tot_stats->s); + ipvs->tot_stats->s.est.ktid = -2; +#endif +} + static inline void ip_vs_est_stopped_recalc(struct netns_ipvs *ipvs) { #ifdef CONFIG_SYSCTL /* Stop tasks while cpulist is empty or if disabled with flag */ ipvs->est_stopped = !sysctl_run_estimation(ipvs) || (ipvs->est_cpulist_valid && - cpumask_empty(sysctl_est_cpulist(ipvs))); + sysctl_est_cpulist_empty(ipvs)); #endif } @@ -1911,7 +1932,7 @@ static inline bool ip_vs_est_stopped(struct netns_ipvs *ipvs) static inline int ip_vs_est_max_threads(struct netns_ipvs *ipvs) { unsigned int limit = IPVS_EST_CPU_KTHREADS * - cpumask_weight(sysctl_est_cpulist(ipvs)); + sysctl_est_cpulist_weight(ipvs); return max(1U, limit); } diff --git a/include/net/ipv6.h b/include/net/ipv6.h index d042afe7a245..1dec81faff28 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -90,6 +90,9 @@ struct ip_tunnel_info; #define IP6_DEFAULT_MAX_DST_OPTS_LEN INT_MAX /* No limit */ #define IP6_DEFAULT_MAX_HBH_OPTS_LEN INT_MAX /* No limit */ +/* Hard limit on traversed IPv6 extension headers */ +#define IP6_MAX_EXT_HDRS_CNT 12 + /* * Addr type * diff --git a/include/net/macsec.h b/include/net/macsec.h index bc7de5b53e54..d962093ee923 100644 --- a/include/net/macsec.h +++ b/include/net/macsec.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -123,6 +124,7 @@ struct macsec_dev_stats { * @key: key structure * @ssci: short secure channel identifier * @stats: per-SA stats + * @destroy_work: deferred work to free the SA in process context after RCU grace period */ struct macsec_rx_sa { struct macsec_key key; @@ -136,7 +138,7 @@ struct macsec_rx_sa { bool active; struct macsec_rx_sa_stats __percpu *stats; struct macsec_rx_sc *sc; - struct rcu_head rcu; + struct rcu_work destroy_work; }; struct pcpu_rx_sc_stats { @@ -174,6 +176,7 @@ struct macsec_rx_sc { * @key: key structure * @ssci: short secure channel identifier * @stats: per-SA stats + * @destroy_work: deferred work to free the SA in process context after RCU grace period */ struct macsec_tx_sa { struct macsec_key key; @@ -186,7 +189,7 @@ struct macsec_tx_sa { refcount_t refcnt; bool active; struct macsec_tx_sa_stats __percpu *stats; - struct rcu_head rcu; + struct rcu_work destroy_work; }; /** diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h index 7fe3a1b61b2d..6d836060976a 100644 --- a/include/net/mana/gdma.h +++ b/include/net/mana/gdma.h @@ -792,6 +792,7 @@ enum gdma_mr_access_flags { GDMA_ACCESS_FLAG_REMOTE_READ = BIT_ULL(2), GDMA_ACCESS_FLAG_REMOTE_WRITE = BIT_ULL(3), GDMA_ACCESS_FLAG_REMOTE_ATOMIC = BIT_ULL(4), + GDMA_ACCESS_FLAG_BIND_MW = BIT_ULL(5), }; /* GDMA_CREATE_DMA_REGION */ @@ -884,6 +885,10 @@ enum gdma_mr_type { GDMA_MR_TYPE_ZBVA = 4, /* Device address MRs */ GDMA_MR_TYPE_DM = 5, + /* Memory Window type 1 */ + GDMA_MR_TYPE_MW1 = 6, + /* Memory Window type 2 */ + GDMA_MR_TYPE_MW2 = 7, }; struct gdma_create_mr_params { diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h index 96d21cbbdee2..8f721cd4e4a7 100644 --- a/include/net/mana/mana.h +++ b/include/net/mana/mana.h @@ -573,6 +573,7 @@ struct mana_port_context { netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev); int mana_config_rss(struct mana_port_context *ac, enum TRI_STATE rx, bool update_hash, bool update_tab); +int mana_disable_vport_rx(struct mana_port_context *apc); int mana_alloc_queues(struct net_device *ndev); int mana_attach(struct net_device *ndev); diff --git a/include/net/mana/shm_channel.h b/include/net/mana/shm_channel.h index 5199b41497ff..dbabcfb95daf 100644 --- a/include/net/mana/shm_channel.h +++ b/include/net/mana/shm_channel.h @@ -4,6 +4,12 @@ #ifndef _SHM_CHANNEL_H #define _SHM_CHANNEL_H +#define SMC_APERTURE_BITS 256 +#define SMC_BASIC_UNIT (sizeof(u32)) +#define SMC_APERTURE_DWORDS (SMC_APERTURE_BITS / (SMC_BASIC_UNIT * 8)) +#define SMC_LAST_DWORD (SMC_APERTURE_DWORDS - 1) +#define SMC_APERTURE_SIZE (SMC_APERTURE_BITS / 8) + struct shm_channel { struct device *dev; void __iomem *base; diff --git a/include/net/mctp.h b/include/net/mctp.h index e1e0a69afdce..d8bf9074110d 100644 --- a/include/net/mctp.h +++ b/include/net/mctp.h @@ -26,6 +26,9 @@ struct mctp_hdr { #define MCTP_VER_MIN 1 #define MCTP_VER_MAX 1 +/* Definitions for ver field */ +#define MCTP_HDR_VER_MASK GENMASK(3, 0) + /* Definitions for flags_seq_tag field */ #define MCTP_HDR_FLAG_SOM BIT(7) #define MCTP_HDR_FLAG_EOM BIT(6) diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index e9a8350e7ccf..80f50fd0f7ad 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -45,9 +45,12 @@ struct nf_conntrack_expect { void (*expectfn)(struct nf_conn *new, struct nf_conntrack_expect *this); - /* Helper to assign to new connection */ + /* Helper that created this expectation */ struct nf_conntrack_helper __rcu *helper; + /* Helper to assign to new connection */ + struct nf_conntrack_helper __rcu *assign_helper; + /* The conntrack of the master connection */ struct nf_conn *master; diff --git a/include/net/netfilter/nf_dup_netdev.h b/include/net/netfilter/nf_dup_netdev.h index b175d271aec9..609bcf422a9b 100644 --- a/include/net/netfilter/nf_dup_netdev.h +++ b/include/net/netfilter/nf_dup_netdev.h @@ -3,10 +3,23 @@ #define _NF_DUP_NETDEV_H_ #include +#include +#include void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif); void nf_fwd_netdev_egress(const struct nft_pktinfo *pkt, int oif); +#define NF_RECURSION_LIMIT 2 + +static inline u8 *nf_get_nf_dup_skb_recursion(void) +{ +#ifndef CONFIG_PREEMPT_RT + return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion); +#else + return ¤t->net_xmit.nf_dup_skb_recursion; +#endif +} + struct nft_offload_ctx; struct nft_flow_rule; diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index b09c11c048d5..7b23b245a5a8 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -148,9 +148,10 @@ struct flow_offload_tuple { /* All members above are keys for lookups, see flow_offload_hash(). */ struct { } __hash; - u8 dir:2, + u16 dir:2, xmit_type:3, encap_num:2, + needs_gso_segment:1, tun_num:2, in_vlan_ingress:2; u16 mtu; @@ -232,6 +233,7 @@ struct nf_flow_route { u32 hw_ifindex; u8 h_source[ETH_ALEN]; u8 h_dest[ETH_ALEN]; + u8 needs_gso_segment:1; } out; enum flow_offload_xmit_type xmit_type; } tuple[FLOW_OFFLOAD_DIR_MAX]; diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 2c0173d9309c..cff7b773e972 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -1204,12 +1204,15 @@ struct nft_stats { struct u64_stats_sync syncp; }; +#define NFT_HOOK_REMOVE (1 << 0) + struct nft_hook { struct list_head list; struct list_head ops_list; struct rcu_head rcu; char ifname[IFNAMSIZ]; u8 ifnamelen; + u8 flags; }; struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook, @@ -1664,6 +1667,16 @@ struct nft_trans { u8 put_net:1; }; +/** + * struct nft_trans_hook - nf_tables hook update in transaction + * @list: used internally + * @hook: struct nft_hook with the device hook + */ +struct nft_trans_hook { + struct list_head list; + struct nft_hook *hook; +}; + /** * struct nft_trans_binding - nf_tables object with binding support in transaction * @nft_trans: base structure, MUST be first member diff --git a/include/net/netmem.h b/include/net/netmem.h index 507b74c9f52d..78fe51e5756b 100644 --- a/include/net/netmem.h +++ b/include/net/netmem.h @@ -127,6 +127,21 @@ static inline unsigned int net_iov_idx(const struct net_iov *niov) return niov - net_iov_owner(niov)->niovs; } +/* Initialize a niov: stamp the owning area, the memory provider type, + * and the page_type "no type" sentinel expected by the page-type API + * (see PAGE_TYPE_OPS in ) so that + * page_pool_set_pp_info() can later call __SetPageNetpp() on a niov + * cast to struct page. + */ +static inline void net_iov_init(struct net_iov *niov, + struct net_iov_area *owner, + enum net_iov_type type) +{ + niov->owner = owner; + niov->type = type; + niov->page_type = UINT_MAX; +} + /* netmem */ /** diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 80ccd4dda8e0..6e27c56514df 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -275,7 +275,7 @@ struct netns_ipv4 { #ifdef CONFIG_IP_MROUTE #ifndef CONFIG_IP_MROUTE_MULTIPLE_TABLES - struct mr_table *mrt; + struct mr_table __rcu *mrt; #else struct list_head mr_tables; struct fib_rules_ops *mr_rules_ops; diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 499e4288170f..875916d60bfe 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -119,6 +119,7 @@ struct netns_ipv6 { struct fib_notifier_ops *notifier_ops; struct fib_notifier_ops *ip6mr_notifier_ops; atomic_t ipmr_seq; + int flowlabel_count; struct { struct hlist_head head; spinlock_t lock; diff --git a/include/net/netrom.h b/include/net/netrom.h deleted file mode 100644 index f0565a5987d1..000000000000 --- a/include/net/netrom.h +++ /dev/null @@ -1,273 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Declarations of NET/ROM type objects. - * - * Jonathan Naylor G4KLX 9/4/95 - */ - -#ifndef _NETROM_H -#define _NETROM_H - -#include -#include -#include -#include -#include -#include -#include - -#define NR_NETWORK_LEN 15 -#define NR_TRANSPORT_LEN 5 - -#define NR_PROTO_IP 0x0C - -#define NR_PROTOEXT 0x00 -#define NR_CONNREQ 0x01 -#define NR_CONNACK 0x02 -#define NR_DISCREQ 0x03 -#define NR_DISCACK 0x04 -#define NR_INFO 0x05 -#define NR_INFOACK 0x06 -#define NR_RESET 0x07 - -#define NR_CHOKE_FLAG 0x80 -#define NR_NAK_FLAG 0x40 -#define NR_MORE_FLAG 0x20 - -/* Define Link State constants. */ -enum { - NR_STATE_0, - NR_STATE_1, - NR_STATE_2, - NR_STATE_3 -}; - -#define NR_COND_ACK_PENDING 0x01 -#define NR_COND_REJECT 0x02 -#define NR_COND_PEER_RX_BUSY 0x04 -#define NR_COND_OWN_RX_BUSY 0x08 - -#define NR_DEFAULT_T1 120000 /* Outstanding frames - 120 seconds */ -#define NR_DEFAULT_T2 5000 /* Response delay - 5 seconds */ -#define NR_DEFAULT_N2 3 /* Number of Retries - 3 */ -#define NR_DEFAULT_T4 180000 /* Busy Delay - 180 seconds */ -#define NR_DEFAULT_IDLE 0 /* No Activity Timeout - none */ -#define NR_DEFAULT_WINDOW 4 /* Default Window Size - 4 */ -#define NR_DEFAULT_OBS 6 /* Default Obsolescence Count - 6 */ -#define NR_DEFAULT_QUAL 10 /* Default Neighbour Quality - 10 */ -#define NR_DEFAULT_TTL 16 /* Default Time To Live - 16 */ -#define NR_DEFAULT_ROUTING 1 /* Is routing enabled ? */ -#define NR_DEFAULT_FAILS 2 /* Link fails until route fails */ -#define NR_DEFAULT_RESET 0 /* Sent / accept reset cmds? */ - -#define NR_MODULUS 256 -#define NR_MAX_WINDOW_SIZE 127 /* Maximum Window Allowable - 127 */ -#define NR_MAX_PACKET_SIZE 236 /* Maximum Packet Length - 236 */ - -struct nr_sock { - struct sock sock; - ax25_address user_addr, source_addr, dest_addr; - struct net_device *device; - unsigned char my_index, my_id; - unsigned char your_index, your_id; - unsigned char state, condition, bpqext, window; - unsigned short vs, vr, va, vl; - unsigned char n2, n2count; - unsigned long t1, t2, t4, idle; - unsigned short fraglen; - struct timer_list t1timer; - struct timer_list t2timer; - struct timer_list t4timer; - struct timer_list idletimer; - struct sk_buff_head ack_queue; - struct sk_buff_head reseq_queue; - struct sk_buff_head frag_queue; -}; - -#define nr_sk(sk) ((struct nr_sock *)(sk)) - -struct nr_neigh { - struct hlist_node neigh_node; - ax25_address callsign; - ax25_digi *digipeat; - ax25_cb *ax25; - struct net_device *dev; - unsigned char quality; - unsigned char locked; - unsigned short count; - unsigned int number; - unsigned char failed; - refcount_t refcount; -}; - -struct nr_route { - unsigned char quality; - unsigned char obs_count; - struct nr_neigh *neighbour; -}; - -struct nr_node { - struct hlist_node node_node; - ax25_address callsign; - char mnemonic[7]; - unsigned char which; - unsigned char count; - struct nr_route routes[3]; - refcount_t refcount; - spinlock_t node_lock; -}; - -/********************************************************************* - * nr_node & nr_neigh lists, refcounting and locking - *********************************************************************/ - -#define nr_node_hold(__nr_node) \ - refcount_inc(&((__nr_node)->refcount)) - -static __inline__ void nr_node_put(struct nr_node *nr_node) -{ - if (refcount_dec_and_test(&nr_node->refcount)) { - kfree(nr_node); - } -} - -#define nr_neigh_hold(__nr_neigh) \ - refcount_inc(&((__nr_neigh)->refcount)) - -static __inline__ void nr_neigh_put(struct nr_neigh *nr_neigh) -{ - if (refcount_dec_and_test(&nr_neigh->refcount)) { - if (nr_neigh->ax25) - ax25_cb_put(nr_neigh->ax25); - kfree(nr_neigh->digipeat); - kfree(nr_neigh); - } -} - -/* nr_node_lock and nr_node_unlock also hold/put the node's refcounter. - */ -static __inline__ void nr_node_lock(struct nr_node *nr_node) -{ - nr_node_hold(nr_node); - spin_lock_bh(&nr_node->node_lock); -} - -static __inline__ void nr_node_unlock(struct nr_node *nr_node) -{ - spin_unlock_bh(&nr_node->node_lock); - nr_node_put(nr_node); -} - -#define nr_neigh_for_each(__nr_neigh, list) \ - hlist_for_each_entry(__nr_neigh, list, neigh_node) - -#define nr_neigh_for_each_safe(__nr_neigh, node2, list) \ - hlist_for_each_entry_safe(__nr_neigh, node2, list, neigh_node) - -#define nr_node_for_each(__nr_node, list) \ - hlist_for_each_entry(__nr_node, list, node_node) - -#define nr_node_for_each_safe(__nr_node, node2, list) \ - hlist_for_each_entry_safe(__nr_node, node2, list, node_node) - - -/*********************************************************************/ - -/* af_netrom.c */ -extern int sysctl_netrom_default_path_quality; -extern int sysctl_netrom_obsolescence_count_initialiser; -extern int sysctl_netrom_network_ttl_initialiser; -extern int sysctl_netrom_transport_timeout; -extern int sysctl_netrom_transport_maximum_tries; -extern int sysctl_netrom_transport_acknowledge_delay; -extern int sysctl_netrom_transport_busy_delay; -extern int sysctl_netrom_transport_requested_window_size; -extern int sysctl_netrom_transport_no_activity_timeout; -extern int sysctl_netrom_routing_control; -extern int sysctl_netrom_link_fails_count; -extern int sysctl_netrom_reset_circuit; - -int nr_rx_frame(struct sk_buff *, struct net_device *); -void nr_destroy_socket(struct sock *); - -/* nr_dev.c */ -int nr_rx_ip(struct sk_buff *, struct net_device *); -void nr_setup(struct net_device *); - -/* nr_in.c */ -int nr_process_rx_frame(struct sock *, struct sk_buff *); - -/* nr_loopback.c */ -void nr_loopback_init(void); -void nr_loopback_clear(void); -int nr_loopback_queue(struct sk_buff *); - -/* nr_out.c */ -void nr_output(struct sock *, struct sk_buff *); -void nr_send_nak_frame(struct sock *); -void nr_kick(struct sock *); -void nr_transmit_buffer(struct sock *, struct sk_buff *); -void nr_establish_data_link(struct sock *); -void nr_enquiry_response(struct sock *); -void nr_check_iframes_acked(struct sock *, unsigned short); - -/* nr_route.c */ -void nr_rt_device_down(struct net_device *); -struct net_device *nr_dev_first(void); -struct net_device *nr_dev_get(ax25_address *); -int nr_rt_ioctl(unsigned int, void __user *); -void nr_link_failed(ax25_cb *, int); -int nr_route_frame(struct sk_buff *, ax25_cb *); -extern const struct seq_operations nr_node_seqops; -extern const struct seq_operations nr_neigh_seqops; -void nr_rt_free(void); - -/* nr_subr.c */ -void nr_clear_queues(struct sock *); -void nr_frames_acked(struct sock *, unsigned short); -void nr_requeue_frames(struct sock *); -int nr_validate_nr(struct sock *, unsigned short); -int nr_in_rx_window(struct sock *, unsigned short); -void nr_write_internal(struct sock *, int); - -void __nr_transmit_reply(struct sk_buff *skb, int mine, unsigned char cmdflags); - -/* - * This routine is called when a Connect Acknowledge with the Choke Flag - * set is needed to refuse a connection. - */ -#define nr_transmit_refusal(skb, mine) \ -do { \ - __nr_transmit_reply((skb), (mine), NR_CONNACK | NR_CHOKE_FLAG); \ -} while (0) - -/* - * This routine is called when we don't have a circuit matching an incoming - * NET/ROM packet. This is an G8PZT Xrouter extension. - */ -#define nr_transmit_reset(skb, mine) \ -do { \ - __nr_transmit_reply((skb), (mine), NR_RESET); \ -} while (0) - -void nr_disconnect(struct sock *, int); - -/* nr_timer.c */ -void nr_init_timers(struct sock *sk); -void nr_start_heartbeat(struct sock *); -void nr_start_t1timer(struct sock *); -void nr_start_t2timer(struct sock *); -void nr_start_t4timer(struct sock *); -void nr_start_idletimer(struct sock *); -void nr_stop_heartbeat(struct sock *); -void nr_stop_t1timer(struct sock *); -void nr_stop_t2timer(struct sock *); -void nr_stop_t4timer(struct sock *); -void nr_stop_idletimer(struct sock *); -int nr_t1timer_running(struct sock *); - -/* sysctl_net_netrom.c */ -int nr_register_sysctl(void); -void nr_unregister_sysctl(void); - -#endif diff --git a/include/net/nsh.h b/include/net/nsh.h index 16a751093896..15a26c590815 100644 --- a/include/net/nsh.h +++ b/include/net/nsh.h @@ -247,10 +247,10 @@ struct nshhdr { #define NSH_M_TYPE1_LEN 24 /* NSH header maximum Length. */ -#define NSH_HDR_MAX_LEN 256 +#define NSH_HDR_MAX_LEN ((NSH_LEN_MASK >> NSH_LEN_SHIFT) * 4) /* NSH context headers maximum Length. */ -#define NSH_CTX_HDRS_MAX_LEN 248 +#define NSH_CTX_HDRS_MAX_LEN (NSH_HDR_MAX_LEN - NSH_BASE_HDR_LEN) static inline struct nshhdr *nsh_hdr(struct sk_buff *skb) { diff --git a/include/net/pie.h b/include/net/pie.h index 01cbc66825a4..1f3db0c35514 100644 --- a/include/net/pie.h +++ b/include/net/pie.h @@ -104,7 +104,7 @@ static inline void pie_vars_init(struct pie_vars *vars) vars->dq_tstamp = DTIME_INVALID; vars->accu_prob = 0; vars->dq_count = DQCOUNT_INVALID; - vars->avg_dq_rate = 0; + WRITE_ONCE(vars->avg_dq_rate, 0); } static inline struct pie_skb_cb *get_pie_cb(const struct sk_buff *skb) diff --git a/include/net/rose.h b/include/net/rose.h index 2b5491bbf39a..41bfcb224f0b 100644 --- a/include/net/rose.h +++ b/include/net/rose.h @@ -1,266 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* - * Declarations of Rose type objects. - * - * Jonathan Naylor G4KLX 25/8/96 - */ - #ifndef _ROSE_H -#define _ROSE_H +#define _ROSE_H -#include -#include -#include -#include - -#define ROSE_ADDR_LEN 5 - -#define ROSE_MIN_LEN 3 - -#define ROSE_CALL_REQ_ADDR_LEN_OFF 3 -#define ROSE_CALL_REQ_ADDR_LEN_VAL 0xAA /* each address is 10 digits */ -#define ROSE_CALL_REQ_DEST_ADDR_OFF 4 -#define ROSE_CALL_REQ_SRC_ADDR_OFF 9 -#define ROSE_CALL_REQ_FACILITIES_OFF 14 - -#define ROSE_GFI 0x10 -#define ROSE_Q_BIT 0x80 -#define ROSE_D_BIT 0x40 -#define ROSE_M_BIT 0x10 - -#define ROSE_CALL_REQUEST 0x0B -#define ROSE_CALL_ACCEPTED 0x0F -#define ROSE_CLEAR_REQUEST 0x13 -#define ROSE_CLEAR_CONFIRMATION 0x17 -#define ROSE_DATA 0x00 -#define ROSE_INTERRUPT 0x23 -#define ROSE_INTERRUPT_CONFIRMATION 0x27 -#define ROSE_RR 0x01 -#define ROSE_RNR 0x05 -#define ROSE_REJ 0x09 -#define ROSE_RESET_REQUEST 0x1B -#define ROSE_RESET_CONFIRMATION 0x1F -#define ROSE_REGISTRATION_REQUEST 0xF3 -#define ROSE_REGISTRATION_CONFIRMATION 0xF7 -#define ROSE_RESTART_REQUEST 0xFB -#define ROSE_RESTART_CONFIRMATION 0xFF -#define ROSE_DIAGNOSTIC 0xF1 -#define ROSE_ILLEGAL 0xFD - -/* Define Link State constants. */ - -enum { - ROSE_STATE_0, /* Ready */ - ROSE_STATE_1, /* Awaiting Call Accepted */ - ROSE_STATE_2, /* Awaiting Clear Confirmation */ - ROSE_STATE_3, /* Data Transfer */ - ROSE_STATE_4, /* Awaiting Reset Confirmation */ - ROSE_STATE_5 /* Deferred Call Acceptance */ -}; - -#define ROSE_DEFAULT_T0 180000 /* Default T10 T20 value */ -#define ROSE_DEFAULT_T1 200000 /* Default T11 T21 value */ -#define ROSE_DEFAULT_T2 180000 /* Default T12 T22 value */ -#define ROSE_DEFAULT_T3 180000 /* Default T13 T23 value */ -#define ROSE_DEFAULT_HB 5000 /* Default Holdback value */ -#define ROSE_DEFAULT_IDLE 0 /* No Activity Timeout - none */ -#define ROSE_DEFAULT_ROUTING 1 /* Default routing flag */ -#define ROSE_DEFAULT_FAIL_TIMEOUT 120000 /* Time until link considered usable */ -#define ROSE_DEFAULT_MAXVC 50 /* Maximum number of VCs per neighbour */ -#define ROSE_DEFAULT_WINDOW_SIZE 7 /* Default window size */ - -#define ROSE_MODULUS 8 -#define ROSE_MAX_PACKET_SIZE 251 /* Maximum packet size */ - -#define ROSE_COND_ACK_PENDING 0x01 -#define ROSE_COND_PEER_RX_BUSY 0x02 -#define ROSE_COND_OWN_RX_BUSY 0x04 - -#define FAC_NATIONAL 0x00 -#define FAC_CCITT 0x0F - -#define FAC_NATIONAL_RAND 0x7F -#define FAC_NATIONAL_FLAGS 0x3F -#define FAC_NATIONAL_DEST_DIGI 0xE9 -#define FAC_NATIONAL_SRC_DIGI 0xEB -#define FAC_NATIONAL_FAIL_CALL 0xED -#define FAC_NATIONAL_FAIL_ADD 0xEE -#define FAC_NATIONAL_DIGIS 0xEF - -#define FAC_CCITT_DEST_NSAP 0xC9 -#define FAC_CCITT_SRC_NSAP 0xCB - -struct rose_neigh { - struct rose_neigh *next; - ax25_address callsign; - ax25_digi *digipeat; - ax25_cb *ax25; - struct net_device *dev; - unsigned short count; - refcount_t use; - unsigned int number; - char restarted; - char dce_mode; - char loopback; - struct sk_buff_head queue; - struct timer_list t0timer; - struct timer_list ftimer; -}; - -struct rose_node { - struct rose_node *next; - rose_address address; - unsigned short mask; - unsigned char count; - char loopback; - struct rose_neigh *neighbour[3]; -}; - -struct rose_route { - struct rose_route *next; - unsigned int lci1, lci2; - rose_address src_addr, dest_addr; - ax25_address src_call, dest_call; - struct rose_neigh *neigh1, *neigh2; - unsigned int rand; -}; - -struct rose_sock { - struct sock sock; - rose_address source_addr, dest_addr; - ax25_address source_call, dest_call; - unsigned char source_ndigis, dest_ndigis; - ax25_address source_digis[ROSE_MAX_DIGIS]; - ax25_address dest_digis[ROSE_MAX_DIGIS]; - struct rose_neigh *neighbour; - struct net_device *device; - netdevice_tracker dev_tracker; - unsigned int lci, rand; - unsigned char state, condition, qbitincl, defer; - unsigned char cause, diagnostic; - unsigned short vs, vr, va, vl; - unsigned long t1, t2, t3, hb, idle; -#ifdef M_BIT - unsigned short fraglen; - struct sk_buff_head frag_queue; -#endif - struct sk_buff_head ack_queue; - struct rose_facilities_struct facilities; - struct timer_list timer; - struct timer_list idletimer; -}; - -#define rose_sk(sk) ((struct rose_sock *)(sk)) - -static inline void rose_neigh_hold(struct rose_neigh *rose_neigh) -{ - refcount_inc(&rose_neigh->use); -} - -static inline void rose_neigh_put(struct rose_neigh *rose_neigh) -{ - if (refcount_dec_and_test(&rose_neigh->use)) { - if (rose_neigh->ax25) - ax25_cb_put(rose_neigh->ax25); - kfree(rose_neigh->digipeat); - kfree(rose_neigh); - } -} - -/* af_rose.c */ -extern ax25_address rose_callsign; -extern int sysctl_rose_restart_request_timeout; -extern int sysctl_rose_call_request_timeout; -extern int sysctl_rose_reset_request_timeout; -extern int sysctl_rose_clear_request_timeout; -extern int sysctl_rose_no_activity_timeout; -extern int sysctl_rose_ack_hold_back_timeout; -extern int sysctl_rose_routing_control; -extern int sysctl_rose_link_fail_timeout; -extern int sysctl_rose_maximum_vcs; -extern int sysctl_rose_window_size; - -int rosecmp(const rose_address *, const rose_address *); -int rosecmpm(const rose_address *, const rose_address *, unsigned short); -char *rose2asc(char *buf, const rose_address *); -struct sock *rose_find_socket(unsigned int, struct rose_neigh *); -void rose_kill_by_neigh(struct rose_neigh *); -unsigned int rose_new_lci(struct rose_neigh *); -int rose_rx_call_request(struct sk_buff *, struct net_device *, - struct rose_neigh *, unsigned int); -void rose_destroy_socket(struct sock *); - -/* rose_dev.c */ -void rose_setup(struct net_device *); - -/* rose_in.c */ -int rose_process_rx_frame(struct sock *, struct sk_buff *); - -/* rose_link.c */ -void rose_start_ftimer(struct rose_neigh *); -void rose_stop_ftimer(struct rose_neigh *); -void rose_stop_t0timer(struct rose_neigh *); -int rose_ftimer_running(struct rose_neigh *); -void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *, - unsigned short); -void rose_transmit_clear_request(struct rose_neigh *, unsigned int, - unsigned char, unsigned char); -void rose_transmit_link(struct sk_buff *, struct rose_neigh *); - -/* rose_loopback.c */ -void rose_loopback_init(void); -void rose_loopback_clear(void); -int rose_loopback_queue(struct sk_buff *, struct rose_neigh *); - -/* rose_out.c */ -void rose_kick(struct sock *); -void rose_enquiry_response(struct sock *); - -/* rose_route.c */ -extern struct rose_neigh *rose_loopback_neigh; -extern const struct seq_operations rose_neigh_seqops; -extern const struct seq_operations rose_node_seqops; -extern struct seq_operations rose_route_seqops; - -void rose_add_loopback_neigh(void); -int __must_check rose_add_loopback_node(const rose_address *); -void rose_del_loopback_node(const rose_address *); -void rose_rt_device_down(struct net_device *); -void rose_link_device_down(struct net_device *); -struct net_device *rose_dev_first(void); -struct net_device *rose_dev_get(rose_address *); -struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *); -struct rose_neigh *rose_get_neigh(rose_address *, unsigned char *, - unsigned char *, int); -int rose_rt_ioctl(unsigned int, void __user *); -void rose_link_failed(ax25_cb *, int); -int rose_route_frame(struct sk_buff *, ax25_cb *); -void rose_rt_free(void); - -/* rose_subr.c */ -void rose_clear_queues(struct sock *); -void rose_frames_acked(struct sock *, unsigned short); -void rose_requeue_frames(struct sock *); -int rose_validate_nr(struct sock *, unsigned short); -void rose_write_internal(struct sock *, int); -int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *); -int rose_parse_facilities(unsigned char *, unsigned int, - struct rose_facilities_struct *); -void rose_disconnect(struct sock *, int, int, int); - -/* rose_timer.c */ -void rose_start_heartbeat(struct sock *); -void rose_start_t1timer(struct sock *); -void rose_start_t2timer(struct sock *); -void rose_start_t3timer(struct sock *); -void rose_start_hbtimer(struct sock *); -void rose_start_idletimer(struct sock *); -void rose_stop_heartbeat(struct sock *); -void rose_stop_timer(struct sock *); -void rose_stop_idletimer(struct sock *); - -/* sysctl_net_rose.c */ -void rose_register_sysctl(void); -void rose_unregister_sysctl(void); +#define ROSE_ADDR_LEN 5 #endif diff --git a/include/net/tcp.h b/include/net/tcp.h index dfa52ceefd23..ecbadcb3a744 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1513,7 +1513,7 @@ static inline u32 tcp_snd_cwnd(const struct tcp_sock *tp) static inline void tcp_snd_cwnd_set(struct tcp_sock *tp, u32 val) { WARN_ON_ONCE((int)val <= 0); - tp->snd_cwnd = val; + WRITE_ONCE(tp->snd_cwnd, val); } static inline bool tcp_in_slow_start(const struct tcp_sock *tp) @@ -2208,10 +2208,14 @@ static inline void tcp_chrono_set(struct tcp_sock *tp, const enum tcp_chrono new const u32 now = tcp_jiffies32; enum tcp_chrono old = tp->chrono_type; + /* Following WRITE_ONCE()s pair with READ_ONCE()s in + * tcp_get_info_chrono_stats(). + */ if (old > TCP_CHRONO_UNSPEC) - tp->chrono_stat[old - 1] += now - tp->chrono_start; - tp->chrono_start = now; - tp->chrono_type = new; + WRITE_ONCE(tp->chrono_stat[old - 1], + tp->chrono_stat[old - 1] + now - tp->chrono_start); + WRITE_ONCE(tp->chrono_start, now); + WRITE_ONCE(tp->chrono_type, new); } static inline void tcp_chrono_start(struct sock *sk, const enum tcp_chrono type) diff --git a/include/net/tcp_ecn.h b/include/net/tcp_ecn.h index e9a933641636..865d5c5a7718 100644 --- a/include/net/tcp_ecn.h +++ b/include/net/tcp_ecn.h @@ -181,7 +181,7 @@ static inline void tcp_accecn_third_ack(struct sock *sk, tcp_accecn_validate_syn_feedback(sk, ace, sent_ect)) { if ((tcp_accecn_extract_syn_ect(ace) == INET_ECN_CE) && !tp->delivered_ce) - tp->delivered_ce++; + WRITE_ONCE(tp->delivered_ce, 1); } break; } diff --git a/include/rdma/frmr_pools.h b/include/rdma/frmr_pools.h new file mode 100644 index 000000000000..af1b88801fa4 --- /dev/null +++ b/include/rdma/frmr_pools.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB + * + * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#ifndef FRMR_POOLS_H +#define FRMR_POOLS_H + +#include +#include + +struct ib_device; +struct ib_mr; + +struct ib_frmr_key { + u64 vendor_key; + /* A pool with non-zero kernel_vendor_key is a kernel-only pool. */ + u64 kernel_vendor_key; + size_t num_dma_blocks; + int access_flags; + u8 ats:1; +}; + +struct ib_frmr_pool_ops { + int (*create_frmrs)(struct ib_device *device, struct ib_frmr_key *key, + u32 *handles, u32 count); + void (*destroy_frmrs)(struct ib_device *device, u32 *handles, + u32 count); + int (*build_key)(struct ib_device *device, const struct ib_frmr_key *in, + struct ib_frmr_key *out); +}; + +int ib_frmr_pools_init(struct ib_device *device, + const struct ib_frmr_pool_ops *pool_ops); +void ib_frmr_pools_cleanup(struct ib_device *device); +int ib_frmr_pool_pop(struct ib_device *device, struct ib_mr *mr); +int ib_frmr_pool_push(struct ib_device *device, struct ib_mr *mr); + +#endif /* FRMR_POOLS_H */ diff --git a/include/rdma/ib_cache.h b/include/rdma/ib_cache.h index 2bf09b594d10..eed46d966e40 100644 --- a/include/rdma/ib_cache.h +++ b/include/rdma/ib_cache.h @@ -34,7 +34,7 @@ struct net_device *rdma_read_gid_attr_ndev_rcu(const struct ib_gid_attr *attr); /** * ib_get_cached_pkey - Returns a cached PKey table entry - * @device: The device to query. + * @device_handle: The device to query. * @port_num: The port number of the device to query. * @index: The index into the cached PKey table to query. * @pkey: The PKey value found at the specified index. @@ -80,7 +80,7 @@ int ib_get_cached_lmc(struct ib_device *device, * ib_get_cached_port_state - Returns a cached port state table entry * @device: The device to query. * @port_num: The port number of the device to query. - * @port_state: port_state for the specified port for that device. + * @port_active: port_state for the specified port for that device. * * ib_get_cached_port_state() fetches the specified port_state table entry stored in * the local software cache. diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h index 0a8e092c0ea8..2ad52cc1d52b 100644 --- a/include/rdma/ib_umem.h +++ b/include/rdma/ib_umem.h @@ -7,13 +7,9 @@ #ifndef IB_UMEM_H #define IB_UMEM_H -#include #include -#include -#include -struct ib_ucontext; -struct ib_umem_odp; +struct ib_device; struct dma_buf_attach_ops; struct ib_umem { @@ -22,6 +18,7 @@ struct ib_umem { u64 iova; size_t length; unsigned long address; + unsigned long dma_attrs; u32 writable : 1; u32 is_odp : 1; u32 is_dmabuf : 1; @@ -36,6 +33,7 @@ struct ib_umem_dmabuf { struct scatterlist *last_sg; unsigned long first_sg_offset; unsigned long last_sg_trim; + void (*pinned_revoke)(void *priv); void *private; u8 pinned : 1; u8 revoked : 1; @@ -75,37 +73,6 @@ static inline size_t ib_umem_num_pages(struct ib_umem *umem) { return ib_umem_num_dma_blocks(umem, PAGE_SIZE); } - -static inline void __rdma_umem_block_iter_start(struct ib_block_iter *biter, - struct ib_umem *umem, - unsigned long pgsz) -{ - __rdma_block_iter_start(biter, umem->sgt_append.sgt.sgl, - umem->sgt_append.sgt.nents, pgsz); - biter->__sg_advance = ib_umem_offset(umem) & ~(pgsz - 1); - biter->__sg_numblocks = ib_umem_num_dma_blocks(umem, pgsz); -} - -static inline bool __rdma_umem_block_iter_next(struct ib_block_iter *biter) -{ - return __rdma_block_iter_next(biter) && biter->__sg_numblocks--; -} - -/** - * rdma_umem_for_each_dma_block - iterate over contiguous DMA blocks of the umem - * @umem: umem to iterate over - * @pgsz: Page size to split the list into - * - * pgsz must be <= PAGE_SIZE or computed by ib_umem_find_best_pgsz(). The - * returned DMA blocks will be aligned to pgsz and span the range: - * ALIGN_DOWN(umem->address, pgsz) to ALIGN(umem->address + umem->length, pgsz) - * - * Performs exactly ib_umem_num_dma_blocks() iterations. - */ -#define rdma_umem_for_each_dma_block(umem, biter, pgsz) \ - for (__rdma_umem_block_iter_start(biter, umem, pgsz); \ - __rdma_umem_block_iter_next(biter);) - #ifdef CONFIG_INFINIBAND_USER_MEM struct ib_umem *ib_umem_get(struct ib_device *device, unsigned long addr, @@ -121,7 +88,7 @@ unsigned long ib_umem_find_best_pgsz(struct ib_umem *umem, * ib_umem_find_best_pgoff - Find best HW page size * * @umem: umem struct - * @pgsz_bitmap bitmap of HW supported page sizes + * @pgsz_bitmap: bitmap of HW supported page sizes * @pgoff_bitmask: Mask of bits that can be represented with an offset * * This is very similar to ib_umem_find_best_pgsz() except instead of accepting @@ -134,6 +101,9 @@ unsigned long ib_umem_find_best_pgsz(struct ib_umem *umem, * * If the pgoff_bitmask requires either alignment in the low bit or an * unavailable page size for the high bits, this function returns 0. + * + * Returns: best HW page size for the parameters or 0 if none available + * for the given parameters. */ static inline unsigned long ib_umem_find_best_pgoff(struct ib_umem *umem, unsigned long pgsz_bitmap, @@ -169,6 +139,12 @@ struct ib_umem_dmabuf *ib_umem_dmabuf_get_pinned(struct ib_device *device, size_t size, int fd, int access); struct ib_umem_dmabuf * +ib_umem_dmabuf_get_pinned_revocable_and_lock(struct ib_device *device, + unsigned long offset, size_t size, + int fd, int access); +void ib_umem_dmabuf_set_revoke_locked(struct ib_umem_dmabuf *umem_dmabuf, + void (*revoke)(void *priv), void *priv); +struct ib_umem_dmabuf * ib_umem_dmabuf_get_pinned_with_dma_device(struct ib_device *device, struct device *dma_device, unsigned long offset, size_t size, @@ -176,6 +152,8 @@ ib_umem_dmabuf_get_pinned_with_dma_device(struct ib_device *device, int ib_umem_dmabuf_map_pages(struct ib_umem_dmabuf *umem_dmabuf); void ib_umem_dmabuf_unmap_pages(struct ib_umem_dmabuf *umem_dmabuf); void ib_umem_dmabuf_release(struct ib_umem_dmabuf *umem_dmabuf); +void ib_umem_dmabuf_revoke_lock(struct ib_umem_dmabuf *umem_dmabuf); +void ib_umem_dmabuf_revoke_unlock(struct ib_umem_dmabuf *umem_dmabuf); void ib_umem_dmabuf_revoke(struct ib_umem_dmabuf *umem_dmabuf); #else /* CONFIG_INFINIBAND_USER_MEM */ @@ -221,6 +199,18 @@ ib_umem_dmabuf_get_pinned(struct ib_device *device, unsigned long offset, return ERR_PTR(-EOPNOTSUPP); } +static inline struct ib_umem_dmabuf * +ib_umem_dmabuf_get_pinned_revocable_and_lock(struct ib_device *device, + unsigned long offset, size_t size, + int fd, int access) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void +ib_umem_dmabuf_set_revoke_locked(struct ib_umem_dmabuf *umem_dmabuf, + void (*revoke)(void *priv), void *priv) {} + static inline struct ib_umem_dmabuf * ib_umem_dmabuf_get_pinned_with_dma_device(struct ib_device *device, struct device *dma_device, @@ -236,6 +226,8 @@ static inline int ib_umem_dmabuf_map_pages(struct ib_umem_dmabuf *umem_dmabuf) } static inline void ib_umem_dmabuf_unmap_pages(struct ib_umem_dmabuf *umem_dmabuf) { } static inline void ib_umem_dmabuf_release(struct ib_umem_dmabuf *umem_dmabuf) { } +static inline void ib_umem_dmabuf_revoke_lock(struct ib_umem_dmabuf *umem_dmabuf) {} +static inline void ib_umem_dmabuf_revoke_unlock(struct ib_umem_dmabuf *umem_dmabuf) {} static inline void ib_umem_dmabuf_revoke(struct ib_umem_dmabuf *umem_dmabuf) {} #endif /* CONFIG_INFINIBAND_USER_MEM */ diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 3f3827e1c711..9dd76f489a0b 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #define IB_FW_VERSION_NAME_MAX ETHTOOL_FWVERS_LEN @@ -1576,6 +1577,93 @@ struct ib_uobject { const struct uverbs_api_object *uapi_object; }; +/** + * struct ib_udata - Driver request/response data from userspace + * @inbuf: Pointer to request data from userspace + * @outbuf: Pointer to response buffer in userspace + * @inlen: Length of request data + * @outlen: Length of response buffer + * + * struct ib_udata is used to hold the driver data request and response + * structures defined in the uapi. They follow these rules for forwards and + * backwards compatibility: + * + * 1) Userspace can provide a longer request so long as the trailing part the + * kernel doesn't understand is all zeros. + * + * This provides a degree of safety if userspace wrongly tries to use a new + * feature the kernel does not understand with some non-zero value. + * + * It allows a simpler rdma-core implementation because the library can + * simply always use the latest structs for the request, even if they are + * bigger. It simply has to avoid using the new members if they are not + * supported/required. + * + * 2) Userspace can provide a shorter request; the kernel will zero-pad it out + * to fill the storage. The newer kernel should understand that older + * userspace will provide 0 to new fields. The kernel has three options to + * enable new request fields: + * + * - Input comp_mask that says the field is supported + * - Look for non-zero values + * - Check if the udata->inlen size covers the field + * + * This also corrects any bugs related to not filling in request structures + * as the new helper always fully writes to the struct. + * + * 3) Userspace can provide a shorter or longer response struct. If shorter, + * the kernel reply is truncated. The kernel should be designed to not write + * to new reply fields unless userspace has affirmatively requested them. + * + * If the user buffer is longer, the kernel will zero-fill it. + * + * Userspace has three options to enable new response fields: + * + * - Output comp_mask that says the field is supported + * - Look for non-zero values + * - Infer the output must be valid because the request contents demand it + * and old kernels will fail the request + * + * The following helper functions implement these semantics: + * + * ib_copy_validate_udata_in() - Checks the minimum length, and zero trailing:: + * + * struct driver_create_cq_req req; + * int err; + * + * err = ib_copy_validate_udata_in(udata, req, end_member); + * if (err) + * return err; + * + * The third argument specifies the last member of the struct in the first + * kernel version that introduced it, establishing the minimum required size. + * + * ib_copy_validate_udata_in_cm() - The above but also validate a + * comp_mask member only has supported bits set:: + * + * err = ib_copy_validate_udata_in_cm(udata, req, first_version_last_member, + * DRIVER_CREATE_CQ_MASK_FEATURE_A | + * DRIVER_CREATE_CQ_MASK_FEATURE_B); + * + * ib_respond_udata() - Implements the response rules:: + * + * struct driver_create_cq_resp resp = {}; + * + * resp.some_field = value; + * return ib_respond_udata(udata, resp); + * + * ib_is_udata_in_empty() - Used instead of ib_copy_validate_udata_in() if the + * driver does not have a request structure:: + * + * ret = ib_is_udata_in_empty(udata); + * if (ret) + * return ret; + * + * Similarly ib_respond_empty_udata() is used instead of ib_respond_udata() if + * the driver does not have a response structure:: + * + * return ib_respond_empty_udata(udata); + */ struct ib_udata { const void __user *inbuf; void __user *outbuf; @@ -1650,6 +1738,7 @@ struct ib_cq { u8 interrupt:1; u8 shared:1; unsigned int comp_vector; + struct ib_umem *umem; /* * Implementation details of the RDMA core, don't use in drivers: @@ -1904,6 +1993,11 @@ struct ib_mr { struct ib_dm *dm; struct ib_sig_attrs *sig_attrs; /* only for IB_MR_TYPE_INTEGRITY MRs */ struct ib_dmah *dmah; + struct { + struct ib_frmr_pool *pool; + struct ib_frmr_key key; + u32 handle; + } frmr; /* * Implementation details of the RDMA core, don't use in drivers: */ @@ -2267,7 +2361,6 @@ struct ib_port_data { /* rdma netdev type - specifies protocol type */ enum rdma_netdev_t { - RDMA_NETDEV_OPA_VNIC, RDMA_NETDEV_IPOIB, }; @@ -2281,11 +2374,6 @@ struct rdma_netdev { u32 port_num; int mtu; - /* - * cleanup function must be specified. - * FIXME: This is only used for OPA_VNIC and that usage should be - * removed too. - */ void (*free_rdma_netdev)(struct net_device *netdev); /* control functions */ @@ -2387,6 +2475,12 @@ struct ib_device_ops { enum rdma_driver_id driver_id; u32 uverbs_abi_ver; unsigned int uverbs_no_driver_id_binding:1; + /* + * Indicates the driver checks every op accepting a udata for the + * correct size on input and always handles the output using the udata + * helpers. + */ + unsigned int uverbs_robust_udata:1; /* * NOTE: New drivers should not make use of device_group; instead new @@ -2419,8 +2513,6 @@ struct ib_device_ops { int (*modify_device)(struct ib_device *device, int device_modify_mask, struct ib_device_modify *device_modify); void (*get_dev_fw_str)(struct ib_device *device, char *str); - const struct cpumask *(*get_vector_affinity)(struct ib_device *ibdev, - int comp_vector); int (*query_port)(struct ib_device *device, u32 port_num, struct ib_port_attr *port_attr); int (*query_port_speed)(struct ib_device *device, u32 port_num, @@ -2537,13 +2629,13 @@ struct ib_device_ops { int (*destroy_qp)(struct ib_qp *qp, struct ib_udata *udata); int (*create_cq)(struct ib_cq *cq, const struct ib_cq_init_attr *attr, struct uverbs_attr_bundle *attrs); - int (*create_cq_umem)(struct ib_cq *cq, + int (*create_user_cq)(struct ib_cq *cq, const struct ib_cq_init_attr *attr, - struct ib_umem *umem, struct uverbs_attr_bundle *attrs); int (*modify_cq)(struct ib_cq *cq, u16 cq_count, u16 cq_period); int (*destroy_cq)(struct ib_cq *cq, struct ib_udata *udata); - int (*resize_cq)(struct ib_cq *cq, int cqe, struct ib_udata *udata); + int (*resize_user_cq)(struct ib_cq *cq, unsigned int cqe, + struct ib_udata *udata); /* * pre_destroy_cq - Prevent a cq from generating any new work * completions, but not free any kernel resources @@ -2907,6 +2999,8 @@ struct ib_device { struct list_head subdev_list; enum rdma_nl_name_assign_type name_assign_type; + + struct ib_frmr_pools *frmr_pools; }; static inline void *rdma_zalloc_obj(struct ib_device *dev, size_t size, @@ -2959,22 +3053,6 @@ struct ib_client { u8 no_kverbs_req:1; }; -/* - * IB block DMA iterator - * - * Iterates the DMA-mapped SGL in contiguous memory blocks aligned - * to a HW supported page size. - */ -struct ib_block_iter { - /* internal states */ - struct scatterlist *__sg; /* sg holding the current aligned block */ - dma_addr_t __dma_addr; /* unaligned DMA address of this block */ - size_t __sg_numblocks; /* ib_umem_num_dma_blocks() */ - unsigned int __sg_nents; /* number of SG entries */ - unsigned int __sg_advance; /* number of bytes to advance in sg in next step */ - unsigned int __pg_bit; /* alignment of current block */ -}; - struct ib_device *_ib_alloc_device(size_t size, struct net *net); #define ib_alloc_device(drv_struct, member) \ container_of(_ib_alloc_device(sizeof(struct drv_struct) + \ @@ -3003,38 +3081,6 @@ void ib_unregister_device_queued(struct ib_device *ib_dev); int ib_register_client (struct ib_client *client); void ib_unregister_client(struct ib_client *client); -void __rdma_block_iter_start(struct ib_block_iter *biter, - struct scatterlist *sglist, - unsigned int nents, - unsigned long pgsz); -bool __rdma_block_iter_next(struct ib_block_iter *biter); - -/** - * rdma_block_iter_dma_address - get the aligned dma address of the current - * block held by the block iterator. - * @biter: block iterator holding the memory block - */ -static inline dma_addr_t -rdma_block_iter_dma_address(struct ib_block_iter *biter) -{ - return biter->__dma_addr & ~(BIT_ULL(biter->__pg_bit) - 1); -} - -/** - * rdma_for_each_block - iterate over contiguous memory blocks of the sg list - * @sglist: sglist to iterate over - * @biter: block iterator holding the memory block - * @nents: maximum number of sg entries to iterate over - * @pgsz: best HW supported page size to use - * - * Callers may use rdma_block_iter_dma_address() to get each - * blocks aligned DMA address. - */ -#define rdma_for_each_block(sglist, biter, nents, pgsz) \ - for (__rdma_block_iter_start(biter, sglist, nents, \ - pgsz); \ - __rdma_block_iter_next(biter);) - /** * ib_get_client_data - Get IB client context * @device:Device to get context for @@ -4058,15 +4104,6 @@ struct ib_cq *__ib_create_cq(struct ib_device *device, #define ib_create_cq(device, cmp_hndlr, evt_hndlr, cq_ctxt, cq_attr) \ __ib_create_cq((device), (cmp_hndlr), (evt_hndlr), (cq_ctxt), (cq_attr), KBUILD_MODNAME) -/** - * ib_resize_cq - Modifies the capacity of the CQ. - * @cq: The CQ to resize. - * @cqe: The minimum size of the CQ. - * - * Users can examine the cq structure to determine the actual CQ size. - */ -int ib_resize_cq(struct ib_cq *cq, int cqe); - /** * rdma_set_cq_moderation - Modifies moderation params of the CQ * @cq: The CQ to modify. @@ -4874,27 +4911,6 @@ static inline __be16 ib_lid_be16(u32 lid) return cpu_to_be16((u16)lid); } -/** - * ib_get_vector_affinity - Get the affinity mappings of a given completion - * vector - * @device: the rdma device - * @comp_vector: index of completion vector - * - * Returns NULL on failure, otherwise a corresponding cpu map of the - * completion vector (returns all-cpus map if the device driver doesn't - * implement get_vector_affinity). - */ -static inline const struct cpumask * -ib_get_vector_affinity(struct ib_device *device, int comp_vector) -{ - if (comp_vector < 0 || comp_vector >= device->num_comp_vectors || - !device->ops.get_vector_affinity) - return NULL; - - return device->ops.get_vector_affinity(device, comp_vector); - -} - /** * rdma_roce_rescan_device - Rescan all of the network devices in the system * and add their gids, as needed, to the relevant RoCE devices. diff --git a/include/rdma/iter.h b/include/rdma/iter.h new file mode 100644 index 000000000000..19d64ef04ba9 --- /dev/null +++ b/include/rdma/iter.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. */ + +#ifndef _RDMA_ITER_H_ +#define _RDMA_ITER_H_ + +#include +#include + +/** + * IB block DMA iterator + * + * Iterates the DMA-mapped SGL in contiguous memory blocks aligned + * to a HW supported page size. + */ +struct ib_block_iter { + /* internal states */ + struct scatterlist *__sg; /* sg holding the current aligned block */ + dma_addr_t __dma_addr; /* unaligned DMA address of this block */ + size_t __sg_numblocks; /* ib_umem_num_dma_blocks() */ + unsigned int __sg_nents; /* number of SG entries */ + unsigned int __sg_advance; /* number of bytes to advance in sg in next step */ + unsigned int __pg_bit; /* alignment of current block */ +}; + +void __rdma_block_iter_start(struct ib_block_iter *biter, + struct scatterlist *sglist, + unsigned int nents, + unsigned long pgsz); +bool __rdma_block_iter_next(struct ib_block_iter *biter); + +/** + * rdma_block_iter_dma_address - get the aligned dma address of the current + * block held by the block iterator. + * @biter: block iterator holding the memory block + */ +static inline dma_addr_t +rdma_block_iter_dma_address(struct ib_block_iter *biter) +{ + return biter->__dma_addr & ~(BIT_ULL(biter->__pg_bit) - 1); +} + +/** + * rdma_for_each_block - iterate over contiguous memory blocks of the sg list + * @sglist: sglist to iterate over + * @biter: block iterator holding the memory block + * @nents: maximum number of sg entries to iterate over + * @pgsz: best HW supported page size to use + * + * Callers may use rdma_block_iter_dma_address() to get each + * blocks aligned DMA address. + */ +#define rdma_for_each_block(sglist, biter, nents, pgsz) \ + for (__rdma_block_iter_start(biter, sglist, nents, \ + pgsz); \ + __rdma_block_iter_next(biter);) + +static inline void __rdma_umem_block_iter_start(struct ib_block_iter *biter, + struct ib_umem *umem, + unsigned long pgsz) +{ + __rdma_block_iter_start(biter, umem->sgt_append.sgt.sgl, + umem->sgt_append.sgt.nents, pgsz); + biter->__sg_advance = ib_umem_offset(umem) & ~(pgsz - 1); + biter->__sg_numblocks = ib_umem_num_dma_blocks(umem, pgsz); +} + +static inline bool __rdma_umem_block_iter_next(struct ib_block_iter *biter) +{ + return __rdma_block_iter_next(biter) && biter->__sg_numblocks--; +} + +/** + * rdma_umem_for_each_dma_block - iterate over contiguous DMA blocks of the umem + * @umem: umem to iterate over + * @pgsz: Page size to split the list into + * + * pgsz must be <= PAGE_SIZE or computed by ib_umem_find_best_pgsz(). The + * returned DMA blocks will be aligned to pgsz and span the range: + * ALIGN_DOWN(umem->address, pgsz) to ALIGN(umem->address + umem->length, pgsz) + * + * Performs exactly ib_umem_num_dma_blocks() iterations. + */ +#define rdma_umem_for_each_dma_block(umem, biter, pgsz) \ + for (__rdma_umem_block_iter_start(biter, umem, pgsz); \ + __rdma_umem_block_iter_next(biter);) + +#endif /* _RDMA_ITER_H_ */ diff --git a/include/rdma/iw_cm.h b/include/rdma/iw_cm.h index 2b22f153ef63..57b33edd9ce7 100644 --- a/include/rdma/iw_cm.h +++ b/include/rdma/iw_cm.h @@ -33,8 +33,8 @@ struct iw_cm_event { }; /** - * iw_cm_handler - Function to be called by the IW CM when delivering events - * to the client. + * typedef iw_cm_handler - Function to be called by the IW CM when delivering + * events to the client. * * @cm_id: The IW CM identifier associated with the event. * @event: Pointer to the event structure. @@ -43,9 +43,9 @@ typedef int (*iw_cm_handler)(struct iw_cm_id *cm_id, struct iw_cm_event *event); /** - * iw_event_handler - Function called by the provider when delivering provider - * events to the IW CM. Returns either 0 indicating the event was processed - * or -errno if the event could not be processed. + * typedef iw_event_handler - Function called by the provider when delivering + * provider events to the IW CM. Returns either 0 indicating the event was + * processed or -errno if the event could not be processed. * * @cm_id: The IW CM identifier associated with the event. * @event: Pointer to the event structure. @@ -97,7 +97,7 @@ enum iw_flags { * iw_create_cm_id - Create an IW CM identifier. * * @device: The IB device on which to create the IW CM identier. - * @event_handler: User callback invoked to report events associated with the + * @cm_handler: User callback invoked to report events associated with the * returned IW CM identifier. * @context: User specified context associated with the id. */ @@ -147,7 +147,7 @@ int iw_cm_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param); * iw_cm_reject - Reject an incoming connection request. * * @cm_id: Connection identifier associated with the request. - * @private_daa: Pointer to data to deliver to the remote peer as part of the + * @private_data: Pointer to data to deliver to the remote peer as part of the * reject message. * @private_data_len: The number of bytes in the private_data parameter. * diff --git a/include/rdma/opa_port_info.h b/include/rdma/opa_port_info.h index 73bcac90a048..fb66d3a1dfa9 100644 --- a/include/rdma/opa_port_info.h +++ b/include/rdma/opa_port_info.h @@ -93,9 +93,11 @@ #define OPA_LINKINIT_QUARANTINED (9 << 4) #define OPA_LINKINIT_INSUFIC_CAPABILITY (10 << 4) -#define OPA_LINK_SPEED_NOP 0x0000 /* Reserved (1-5 Gbps) */ -#define OPA_LINK_SPEED_12_5G 0x0001 /* 12.5 Gbps */ -#define OPA_LINK_SPEED_25G 0x0002 /* 25.78125? Gbps (EDR) */ +#define OPA_LINK_SPEED_NOP 0x0000 /* no change */ +#define OPA_LINK_SPEED_12_5G 0x0001 /* 12.5 Gbps */ +#define OPA_LINK_SPEED_25G 0x0002 /* 25.78125 Gbps */ +#define OPA_LINK_SPEED_50G 0x0004 /* 53.125 Gbps */ +#define OPA_LINK_SPEED_100G 0x0008 /* 106.25 Gbps */ #define OPA_LINK_WIDTH_1X 0x0001 #define OPA_LINK_WIDTH_2X 0x0002 diff --git a/include/rdma/opa_vnic.h b/include/rdma/opa_vnic.h deleted file mode 100644 index d297f084001a..000000000000 --- a/include/rdma/opa_vnic.h +++ /dev/null @@ -1,96 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ -/* - * Copyright(c) 2017 - 2020 Intel Corporation. - */ - -#ifndef _OPA_VNIC_H -#define _OPA_VNIC_H - -/* - * This file contains Intel Omni-Path (OPA) Virtual Network Interface - * Controller (VNIC) specific declarations. - */ - -#include - -/* 16 header bytes + 2 reserved bytes */ -#define OPA_VNIC_L2_HDR_LEN (16 + 2) - -#define OPA_VNIC_L4_HDR_LEN 2 - -#define OPA_VNIC_HDR_LEN (OPA_VNIC_L2_HDR_LEN + \ - OPA_VNIC_L4_HDR_LEN) - -#define OPA_VNIC_L4_ETHR 0x78 - -#define OPA_VNIC_ICRC_LEN 4 -#define OPA_VNIC_TAIL_LEN 1 -#define OPA_VNIC_ICRC_TAIL_LEN (OPA_VNIC_ICRC_LEN + OPA_VNIC_TAIL_LEN) - -#define OPA_VNIC_SKB_MDATA_LEN 4 -#define OPA_VNIC_SKB_MDATA_ENCAP_ERR 0x1 - -/* opa vnic rdma netdev's private data structure */ -struct opa_vnic_rdma_netdev { - struct rdma_netdev rn; /* keep this first */ - /* followed by device private data */ - char *dev_priv[]; -}; - -static inline void *opa_vnic_priv(const struct net_device *dev) -{ - struct rdma_netdev *rn = netdev_priv(dev); - - return rn->clnt_priv; -} - -static inline void *opa_vnic_dev_priv(const struct net_device *dev) -{ - struct opa_vnic_rdma_netdev *oparn = netdev_priv(dev); - - return oparn->dev_priv; -} - -/* opa_vnic skb meta data structure */ -struct opa_vnic_skb_mdata { - u8 vl; - u8 entropy; - u8 flags; - u8 rsvd; -} __packed; - -/* OPA VNIC group statistics */ -struct opa_vnic_grp_stats { - u64 unicast; - u64 mcastbcast; - u64 untagged; - u64 vlan; - u64 s_64; - u64 s_65_127; - u64 s_128_255; - u64 s_256_511; - u64 s_512_1023; - u64 s_1024_1518; - u64 s_1519_max; -}; - -struct opa_vnic_stats { - /* standard netdev statistics */ - struct rtnl_link_stats64 netstats; - - /* OPA VNIC statistics */ - struct opa_vnic_grp_stats tx_grp; - struct opa_vnic_grp_stats rx_grp; - u64 tx_dlid_zero; - u64 tx_drop_state; - u64 rx_drop_state; - u64 rx_runt; - u64 rx_oversize; -}; - -static inline bool rdma_cap_opa_vnic(struct ib_device *device) -{ - return !!(device->attrs.kernel_cap_flags & IBK_RDMA_NETDEV_OPA); -} - -#endif /* _OPA_VNIC_H */ diff --git a/include/rdma/rdma_netlink.h b/include/rdma/rdma_netlink.h index 326deaf56d5d..2fd1358ea57d 100644 --- a/include/rdma/rdma_netlink.h +++ b/include/rdma/rdma_netlink.h @@ -5,6 +5,7 @@ #include #include +#include struct ib_device; @@ -126,6 +127,7 @@ struct rdma_link_ops { struct list_head list; const char *type; int (*newlink)(const char *ibdev_name, struct net_device *ndev); + int (*dellink)(struct ib_device *dev); }; void rdma_link_register(struct rdma_link_ops *ops); diff --git a/include/rdma/rdma_vt.h b/include/rdma/rdma_vt.h index c429d6ddb129..7d8de561f71b 100644 --- a/include/rdma/rdma_vt.h +++ b/include/rdma/rdma_vt.h @@ -149,6 +149,7 @@ struct rvt_driver_params { /* User context */ struct rvt_ucontext { struct ib_ucontext ibucontext; + void *priv; }; /* Protection domain */ @@ -359,6 +360,15 @@ struct rvt_driver_provided { /* Get and return CPU to pin CQ processing thread */ int (*comp_vect_cpu_lookup)(struct rvt_dev_info *rdi, int comp_vect); + + /* allocate a ucontext */ + int (*alloc_ucontext)(struct ib_ucontext *uctx, struct ib_udata *udata); + + /* deallocate a ucontext */ + void (*dealloc_ucontext)(struct ib_ucontext *context); + + /* driver mmap */ + int (*mmap)(struct ib_ucontext *context, struct vm_area_struct *vma); }; struct rvt_dev_info { diff --git a/include/rdma/restrack.h b/include/rdma/restrack.h index 8a9bcf77dace..451f99e3717d 100644 --- a/include/rdma/restrack.h +++ b/include/rdma/restrack.h @@ -87,11 +87,11 @@ struct rdma_restrack_entry { * query stage. */ u8 no_track : 1; - /* + /** * @kref: Protect destroy of the resource */ struct kref kref; - /* + /** * @comp: Signal that all consumers of resource are completed their work */ struct completion comp; diff --git a/include/rdma/uverbs_ioctl.h b/include/rdma/uverbs_ioctl.h index e6c0de227fad..e2af17da3e32 100644 --- a/include/rdma/uverbs_ioctl.h +++ b/include/rdma/uverbs_ioctl.h @@ -667,6 +667,8 @@ rdma_udata_to_uverbs_attr_bundle(struct ib_udata *udata) (udata ? container_of(rdma_udata_to_uverbs_attr_bundle(udata)->context, \ drv_dev_struct, member) : (drv_dev_struct *)NULL) +struct ib_device *rdma_udata_to_dev(struct ib_udata *udata); + #define IS_UVERBS_COPY_ERR(_ret) ((_ret) && (_ret) != -ENOENT) static inline const struct uverbs_attr *uverbs_attr_get(const struct uverbs_attr_bundle *attrs_bundle, @@ -895,6 +897,10 @@ int _uverbs_get_const_unsigned(u64 *to, size_t idx, u64 upper_bound, u64 *def_val); int uverbs_copy_to_struct_or_zero(const struct uverbs_attr_bundle *bundle, size_t idx, const void *from, size_t size); + +int _ib_copy_validate_udata_in(struct ib_udata *udata, void *req, + size_t kernel_size, size_t minimum_size); +int _ib_respond_udata(struct ib_udata *udata, const void *src, size_t len); #else static inline int uverbs_get_flags64(u64 *to, const struct uverbs_attr_bundle *attrs_bundle, @@ -951,6 +957,19 @@ _uverbs_get_const_unsigned(u64 *to, { return -EINVAL; } + +static inline int _ib_copy_validate_udata_in(struct ib_udata *udata, void *req, + size_t kernel_size, + size_t minimum_size) +{ + return -EINVAL; +} + +static inline int _ib_respond_udata(struct ib_udata *udata, const void *src, + size_t len) +{ + return -EINVAL; +} #endif #define uverbs_get_const_signed(_to, _attrs_bundle, _idx) \ @@ -1016,4 +1035,86 @@ uverbs_get_raw_fd(int *to, const struct uverbs_attr_bundle *attrs_bundle, return uverbs_get_const_signed(to, attrs_bundle, idx); } +/** + * ib_copy_validate_udata_in - Copy and validate that the request structure is + * compatible with this kernel + * @_udata: The system calls ib_udata struct + * @_req: The name of an on-stack structure that holds the driver data + * @_end_member: The member in the struct that is the original end of struct + * from the first kernel to introduce it. + * + * Check that the udata input request struct is properly formed for this kernel. + * Then copy it into req + */ +#define ib_copy_validate_udata_in(_udata, _req, _end_member) \ + _ib_copy_validate_udata_in(_udata, &(_req), sizeof(_req), \ + offsetofend(typeof(_req), _end_member)) + +int _ib_copy_validate_udata_cm_fail(struct ib_udata *udata, u64 req_cm, + u64 valid_cm); + +/** + * ib_copy_validate_udata_in_cm - Copy the req structure and check the comp_mask + * @_udata: The system calls ib_udata struct + * @_req: The name of an on-stack structure that holds the driver data + * @_end_member: The member in the struct that is the original end of struct + * from the first kernel to introduce it. + * @_valid_cm: A bitmask of bits permitted in the comp_mask_field. + * + * Check that the udata input request struct is properly formed for this kernel. + * Then copy it into req + */ +#define ib_copy_validate_udata_in_cm(_udata, _req, _end_member, _valid_cm) \ + ({ \ + typeof((_req).comp_mask) __valid_cm = _valid_cm; \ + int ret = \ + ib_copy_validate_udata_in(_udata, _req, _end_member); \ + if (!ret && ((_req).comp_mask & ~__valid_cm)) \ + ret = _ib_copy_validate_udata_cm_fail( \ + _udata, (_req).comp_mask, __valid_cm); \ + ret; \ + }) + +/** + * ib_is_udata_in_empty - Check if the udata input buffer is all zeros + * @udata: The system calls ib_udata struct + * + * This should be used if the driver does not currently define a driver data + * struct. Returns 0 if the buffer is empty or all zeros, -EOPNOTSUPP if + * non-zero data is present, or a negative error code on failure. + */ +static inline int ib_is_udata_in_empty(struct ib_udata *udata) +{ + if (!udata || udata->inlen == 0) + return 0; + return _ib_copy_validate_udata_in(udata, NULL, 0, 0); +} + +/** + * ib_respond_udata - Copy a driver data response to userspace + * @_udata: The system calls ib_udata struct + * @_rep: Kernel buffer containing the response driver data on the stack + * + * Copy driver data response structures back to userspace in a way that + * is forwards and backwards compatible. Longer kernel structs are truncated, + * userspace has made some kind of error if it needed the truncated information. + * Shorter structs are zero padded. + */ +#define ib_respond_udata(_udata, _rep) \ + _ib_respond_udata(_udata, &(_rep), sizeof(_rep)) + +/** + * ib_respond_empty_udata - Zero fill the response buffer to userspace + * @_udata: The system calls ib_udata struct + * + * Used when there is no driver response data to return. Provides forward + * compatability by zeroing any buffer the user may have provided. + */ +static inline int ib_respond_empty_udata(struct ib_udata *udata) +{ + if (udata && udata->outlen && clear_user(udata->outbuf, udata->outlen)) + return -EFAULT; + return 0; +} + #endif diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index e76f5744941b..163f23c92b41 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -62,10 +62,6 @@ enum discover_event { /* ---------- Expander Devices ---------- */ -#define to_dom_device(_obj) container_of(_obj, struct domain_device, dev_obj) -#define to_dev_attr(_attr) container_of(_attr, struct domain_dev_attribute,\ - attr) - enum routing_attribute { DIRECT_ROUTING, SUBTRACTIVE_ROUTING, diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index d32f5841f4f8..9c2a7bbe5891 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -571,6 +571,7 @@ void scsi_put_internal_cmd(struct scsi_cmnd *scmd); extern void sdev_disable_disk_events(struct scsi_device *sdev); extern void sdev_enable_disk_events(struct scsi_device *sdev); extern int scsi_vpd_lun_id(struct scsi_device *, char *, size_t); +extern int scsi_vpd_lun_serial(struct scsi_device *, char *, size_t); extern int scsi_vpd_tpg_id(struct scsi_device *, int *); #ifdef CONFIG_PM diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index f6e12565a81d..7e2011830ba4 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -660,6 +660,10 @@ struct Scsi_Host { */ unsigned nr_hw_queues; unsigned nr_maps; + + /* Asynchronous scan in progress */ + bool async_scan __guarded_by(&scan_mutex); + unsigned active_mode:2; /* @@ -678,9 +682,6 @@ struct Scsi_Host { /* Task mgmt function in progress */ unsigned tmf_in_progress:1; - /* Asynchronous scan in progress */ - unsigned async_scan:1; - /* Don't resume host in EH */ unsigned eh_noresume:1; diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h index dc0789c20333..5c64b3e02211 100644 --- a/include/soc/tegra/bpmp-abi.h +++ b/include/soc/tegra/bpmp-abi.h @@ -1,6 +1,6 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* - * Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2025, NVIDIA CORPORATION. All rights reserved. */ #ifndef ABI_BPMP_ABI_H @@ -74,6 +74,7 @@ /** * @ingroup MRQ_Format + * * Request an answer from the peer. * This should be set in mrq_request::flags for all requests targetted * at BPMP. For requests originating in BPMP, this flag is optional except @@ -85,6 +86,7 @@ /** * @ingroup MRQ_Format + * * Ring the sender's doorbell when responding. This should be set unless * the sender wants to poll the underlying communications layer directly. * @@ -94,7 +96,9 @@ /** * @ingroup MRQ_Format - * CRC present + * + * This is set in mrq_request::flags for requests that have CRC present and + * correspondingly in mrq_response::flags for responses that have CRC present. */ #define BPMP_MAIL_CRC_PRESENT (1U << 2U) @@ -127,91 +131,319 @@ struct mrq_request { * crc16, xid and length fields are present when set. * Some platform configurations, especially when targeted to applications requiring * functional safety, mandate this option being set or otherwise will respond with - * -BPMP_EBADMSG and ignore the request. + * -#BPMP_EBADMSG and ignore the request. * * **xid** is a transaction ID. * * Only used when #BPMP_MAIL_CRC_PRESENT is set. * * **payload_length** of the message expressed in bytes without the size of this header. - * See table below for minimum accepted payload lengths for each MRQ. - * Note: For DMCE communication, this field expresses the length as a multiple of 4 bytes - * rather than bytes. + * See tables below for minimum accepted payload lengths for each MRQ. * * Only used when #BPMP_MAIL_CRC_PRESENT is set. * - * | MRQ | CMD | minimum payload length - * | -------------------- | ------------------------------------ | ------------------------------------------ | - * | MRQ_PING | | 4 | - * | MRQ_THREADED_PING | | 4 | - * | MRQ_RESET | any | 8 | - * | MRQ_I2C | | 12 + cmd_i2c_xfer_request.data_size | - * | MRQ_CLK | CMD_CLK_GET_RATE | 4 | - * | MRQ_CLK | CMD_CLK_SET_RATE | 16 | - * | MRQ_CLK | CMD_CLK_ROUND_RATE | 16 | - * | MRQ_CLK | CMD_CLK_GET_PARENT | 4 | - * | MRQ_CLK | CMD_CLK_SET_PARENT | 8 | - * | MRQ_CLK | CMD_CLK_ENABLE | 4 | - * | MRQ_CLK | CMD_CLK_DISABLE | 4 | - * | MRQ_CLK | CMD_CLK_IS_ENABLED | 4 | - * | MRQ_CLK | CMD_CLK_GET_ALL_INFO | 4 | - * | MRQ_CLK | CMD_CLK_GET_MAX_CLK_ID | 4 | - * | MRQ_CLK | CMD_CLK_GET_FMAX_AT_VMIN | 4 | - * | MRQ_QUERY_ABI | | 4 | - * | MRQ_PG | CMD_PG_QUERY_ABI | 12 | - * | MRQ_PG | CMD_PG_SET_STATE | 12 | - * | MRQ_PG | CMD_PG_GET_STATE | 8 | - * | MRQ_PG | CMD_PG_GET_NAME | 8 | - * | MRQ_PG | CMD_PG_GET_MAX_ID | 8 | - * | MRQ_THERMAL | CMD_THERMAL_QUERY_ABI | 8 | - * | MRQ_THERMAL | CMD_THERMAL_GET_TEMP | 8 | - * | MRQ_THERMAL | CMD_THERMAL_SET_TRIP | 20 | - * | MRQ_THERMAL | CMD_THERMAL_GET_NUM_ZONES | 4 | - * | MRQ_THERMAL | CMD_THERMAL_GET_THERMTRIP | 8 | - * | MRQ_CPU_VHINT | | 8 | - * | MRQ_ABI_RATCHET | | 2 | - * | MRQ_EMC_DVFS_LATENCY | | 8 | - * | MRQ_EMC_DVFS_EMCHUB | | 8 | - * | MRQ_EMC_DISP_RFL | | 4 | - * | MRQ_BWMGR | CMD_BWMGR_QUERY_ABI | 8 | - * | MRQ_BWMGR | CMD_BWMGR_CALC_RATE | 8 + 8 * bwmgr_rate_req.num_iso_clients | - * | MRQ_ISO_CLIENT | CMD_ISO_CLIENT_QUERY_ABI | 8 | - * | MRQ_ISO_CLIENT | CMD_ISO_CLIENT_CALCULATE_LA | 16 | - * | MRQ_ISO_CLIENT | CMD_ISO_CLIENT_SET_LA | 16 | - * | MRQ_ISO_CLIENT | CMD_ISO_CLIENT_GET_MAX_BW | 8 | - * | MRQ_CPU_NDIV_LIMITS | | 4 | - * | MRQ_CPU_AUTO_CC3 | | 4 | - * | MRQ_RINGBUF_CONSOLE | CMD_RINGBUF_CONSOLE_QUERY_ABI | 8 | - * | MRQ_RINGBUF_CONSOLE | CMD_RINGBUF_CONSOLE_READ | 5 | - * | MRQ_RINGBUF_CONSOLE | CMD_RINGBUF_CONSOLE_WRITE | 5 + cmd_ringbuf_console_write_req.len | - * | MRQ_RINGBUF_CONSOLE | CMD_RINGBUF_CONSOLE_GET_FIFO | 4 | - * | MRQ_STRAP | STRAP_SET | 12 | - * | MRQ_UPHY | CMD_UPHY_PCIE_LANE_MARGIN_CONTROL | 24 | - * | MRQ_UPHY | CMD_UPHY_PCIE_LANE_MARGIN_STATUS | 4 | - * | MRQ_UPHY | CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT | 5 | - * | MRQ_UPHY | CMD_UPHY_PCIE_CONTROLLER_STATE | 6 | - * | MRQ_UPHY | CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF | 5 | - * | MRQ_FMON | CMD_FMON_GEAR_CLAMP | 16 | - * | MRQ_FMON | CMD_FMON_GEAR_FREE | 4 | - * | MRQ_FMON | CMD_FMON_GEAR_GET | 4 | - * | MRQ_FMON | CMD_FMON_FAULT_STS_GET | 8 | - * | MRQ_EC | CMD_EC_STATUS_EX_GET | 12 | - * | MRQ_QUERY_FW_TAG | | 0 | - * | MRQ_DEBUG | CMD_DEBUG_OPEN_RO | 4 + length of cmd_debug_fopen_request.name | - * | MRQ_DEBUG | CMD_DEBUG_OPEN_WO | 4 + length of cmd_debug_fopen_request.name | - * | MRQ_DEBUG | CMD_DEBUG_READ | 8 | - * | MRQ_DEBUG | CMD_DEBUG_WRITE | 12 + cmd_debug_fwrite_request.datalen | - * | MRQ_DEBUG | CMD_DEBUG_CLOSE | 8 | - * | MRQ_TELEMETRY | | 8 | - * | MRQ_PWR_LIMIT | CMD_PWR_LIMIT_QUERY_ABI | 8 | - * | MRQ_PWR_LIMIT | CMD_PWR_LIMIT_SET | 20 | - * | MRQ_PWR_LIMIT | CMD_PWR_LIMIT_GET | 16 | - * | MRQ_PWR_LIMIT | CMD_PWR_LIMIT_CURR_CAP | 8 | - * | MRQ_GEARS | | 0 | - * | MRQ_BWMGR_INT | CMD_BWMGR_INT_QUERY_ABI | 8 | - * | MRQ_BWMGR_INT | CMD_BWMGR_INT_CALC_AND_SET | 16 | - * | MRQ_BWMGR_INT | CMD_BWMGR_INT_CAP_SET | 8 | - * | MRQ_OC_STATUS | | 0 | + * | MRQ | Sub-command | Minimum payload length + * | --------------------- | ------------------------------------ | ------------------------------------------------------- | + * | #MRQ_PING | - | 4 | + * | #MRQ_THREADED_PING | - | 4 | + * | #MRQ_RESET | any | 8 | + * | #MRQ_I2C | - | 12 + cmd_i2c_xfer_request.data_size | + * | #MRQ_CLK | #CMD_CLK_GET_RATE | 4 | + * | #MRQ_CLK | #CMD_CLK_SET_RATE | 16 | + * | #MRQ_CLK | #CMD_CLK_ROUND_RATE | 16 | + * | #MRQ_CLK | #CMD_CLK_GET_PARENT | 4 | + * | #MRQ_CLK | #CMD_CLK_SET_PARENT | 8 | + * | #MRQ_CLK | #CMD_CLK_ENABLE | 4 | + * | #MRQ_CLK | #CMD_CLK_DISABLE | 4 | + * | #MRQ_CLK | #CMD_CLK_IS_ENABLED | 4 | + * | #MRQ_CLK | #CMD_CLK_GET_ALL_INFO | 4 | + * | #MRQ_CLK | #CMD_CLK_GET_MAX_CLK_ID | 4 | + * | #MRQ_CLK | #CMD_CLK_GET_FMAX_AT_VMIN | 4 | + * | #MRQ_QUERY_ABI | - | 4 | + * | #MRQ_PG | #CMD_PG_QUERY_ABI | 12 | + * | #MRQ_PG | #CMD_PG_SET_STATE | 12 | + * | #MRQ_PG | #CMD_PG_GET_STATE | 8 | + * | #MRQ_PG | #CMD_PG_GET_NAME | 8 | + * | #MRQ_PG | #CMD_PG_GET_MAX_ID | 8 | + * | #MRQ_THERMAL | #CMD_THERMAL_QUERY_ABI | 8 | + * | #MRQ_THERMAL | #CMD_THERMAL_GET_TEMP | 8 | + * | #MRQ_THERMAL | #CMD_THERMAL_GET_NUM_ZONES | 4 | + * | #MRQ_THERMAL | #CMD_THERMAL_GET_THERMTRIP | 8 | + * | #MRQ_ABI_RATCHET | - | 2 | + * | #MRQ_EMC_DVFS_LATENCY | - | 8 | + * | #MRQ_QUERY_FW_TAG | - | 0 | + * | #MRQ_DEBUG | #CMD_DEBUG_OPEN_RO | 4 + length of cmd_debug_fopen_request.name | + * | #MRQ_DEBUG | #CMD_DEBUG_OPEN_WO | 4 + length of cmd_debug_fopen_request.name | + * | #MRQ_DEBUG | #CMD_DEBUG_READ | 8 | + * | #MRQ_DEBUG | #CMD_DEBUG_WRITE | 12 + cmd_debug_fwrite_request.datalen | + * | #MRQ_DEBUG | #CMD_DEBUG_CLOSE | 8 | + * + * @cond (bpmp_t186) + * The following additional MRQ is supported on T186 -platform: + * + * | MRQ | Sub-command | Minimum payload length | + * | --------------------- | ------------------------------------- | ------------------------------------- | + * | #MRQ_CPU_VHINT | - | 8 | + * | #MRQ_THERMAL | #CMD_THERMAL_SET_TRIP | 20 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_QUERY_ABI | 8 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_READ | 5 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_WRITE | 5 + cmd_ringbuf_console_write_req.len | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_GET_FIFO | 4 | + * @endcond + * + * @cond (bpmp_t194) + * The following additional MRQs are supported on T194 -platform: + * + * | MRQ | Sub-command | Minimum payload length | + * | --------------------- | ------------------------------------- | ------------------------------------- | + * | #MRQ_CPU_NDIV_LIMITS | - | 4 | + * | #MRQ_STRAP | #STRAP_SET | 12 | + * | #MRQ_CPU_AUTO_CC3 | - | 4 | + * | #MRQ_EC | #CMD_EC_STATUS_EX_GET | 12 | + * | #MRQ_FMON | #CMD_FMON_GEAR_CLAMP | 16 | + * | #MRQ_FMON | #CMD_FMON_GEAR_FREE | 4 | + * | #MRQ_FMON | #CMD_FMON_GEAR_GET | 4 | + * | #MRQ_FMON | #CMD_FMON_FAULT_STS_GET | 8 | + * | #MRQ_THERMAL | #CMD_THERMAL_SET_TRIP | 20 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_QUERY_ABI | 8 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_READ | 5 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_WRITE | 5 + cmd_ringbuf_console_write_req.len | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_GET_FIFO | 4 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_LANE_MARGIN_CONTROL | 24 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_LANE_MARGIN_STATUS | 4 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT | 5 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_CONTROLLER_STATE | 6 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF | 5 | + * @endcond + * + * @cond (bpmp_safe && bpmp_t234) + * The following additional MRQ is supported on functional-safety + * builds for the T234 platform: + * + * | MRQ | Sub-command | Minimum payload length | + * | --------------------- | ------------------------------------- | ------------------------------------- | + * | #MRQ_CPU_NDIV_LIMITS | - | 4 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_QUERY_ABI | 8 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_READ | 5 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_WRITE | 5 + cmd_ringbuf_console_write_req.len | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_GET_FIFO | 4 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_LANE_MARGIN_CONTROL | 24 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_LANE_MARGIN_STATUS | 4 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT | 5 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_CONTROLLER_STATE | 6 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF | 5 | + * | #MRQ_FMON | #CMD_FMON_GEAR_CLAMP | 16 | + * | #MRQ_FMON | #CMD_FMON_GEAR_FREE | 4 | + * | #MRQ_FMON | #CMD_FMON_GEAR_GET | 4 | + * | #MRQ_FMON | #CMD_FMON_FAULT_STS_GET | 8 | + * | #MRQ_EMC_DVFS_EMCHUB | - | 8 | + * | #MRQ_EMC_DISP_RFL | - | 4 | + * + * @endcond + * + * @cond (!bpmp_safe && bpmp_t234) + * + * The following additional MRQs are supported on non-functional-safety + * builds for the T234 and T238 -platforms: + * + * | MRQ | Sub-command | Minimum payload length | + * | --------------------- | ------------------------------------- | --------------------------------------------------- | + * | #MRQ_CPU_NDIV_LIMITS | - | 4 | + * | #MRQ_STRAP | #STRAP_SET | 12 | + * | #MRQ_THERMAL | #CMD_THERMAL_SET_TRIP | 20 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_QUERY_ABI | 8 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_READ | 5 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_WRITE | 5 + cmd_ringbuf_console_write_req.len | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_GET_FIFO | 4 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_LANE_MARGIN_CONTROL | 24 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_LANE_MARGIN_STATUS | 4 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT | 5 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_CONTROLLER_STATE | 6 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF | 5 | + * | #MRQ_FMON | #CMD_FMON_GEAR_CLAMP | 16 | + * | #MRQ_FMON | #CMD_FMON_GEAR_FREE | 4 | + * | #MRQ_FMON | #CMD_FMON_GEAR_GET | 4 | + * | #MRQ_FMON | #CMD_FMON_FAULT_STS_GET | 8 | + * | #MRQ_EMC_DVFS_EMCHUB | - | 8 | + * | #MRQ_EMC_DISP_RFL | - | 4 | + * | #MRQ_BWMGR | #CMD_BWMGR_QUERY_ABI | 8 | + * | #MRQ_BWMGR | #CMD_BWMGR_CALC_RATE | 8 + 8 * cmd_bwmgr_calc_rate_request.num_iso_clients | + * | #MRQ_ISO_CLIENT | #CMD_ISO_CLIENT_QUERY_ABI | 8 | + * | #MRQ_ISO_CLIENT | #CMD_ISO_CLIENT_CALCULATE_LA | 16 | + * | #MRQ_ISO_CLIENT | #CMD_ISO_CLIENT_SET_LA | 16 | + * | #MRQ_ISO_CLIENT | #CMD_ISO_CLIENT_GET_MAX_BW | 8 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_QUERY_ABI | 8 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_CALC_AND_SET | 16 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_CAP_SET | 8 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_GET_LAST_REQUEST | 9 | + * | #MRQ_OC_STATUS | - | 0 | + * @endcond + * + * @cond bpmp_t238 + * The following additional MRQs are supported on T238 platform: + * + * | MRQ | Sub-command | Minimum payload length | + * | --------------------- | ------------------------------------- | --------------------------------------------------- | + * | #MRQ_CPU_NDIV_LIMITS | - | 4 | + * | #MRQ_STRAP | #STRAP_SET | 12 | + * | #MRQ_THERMAL | #CMD_THERMAL_SET_TRIP | 20 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_QUERY_ABI | 8 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_READ | 5 | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_WRITE | 5 + cmd_ringbuf_console_write_req.len | + * | #MRQ_RINGBUF_CONSOLE | #CMD_RINGBUF_CONSOLE_GET_FIFO | 4 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_LANE_MARGIN_CONTROL | 24 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_LANE_MARGIN_STATUS | 4 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT | 5 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_CONTROLLER_STATE | 6 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF | 5 | + * | #MRQ_FMON | #CMD_FMON_GEAR_CLAMP | 16 | + * | #MRQ_FMON | #CMD_FMON_GEAR_FREE | 4 | + * | #MRQ_FMON | #CMD_FMON_GEAR_GET | 4 | + * | #MRQ_FMON | #CMD_FMON_FAULT_STS_GET | 8 | + * | #MRQ_EMC_DVFS_EMCHUB | - | 8 | + * | #MRQ_EMC_DISP_RFL | - | 4 | + * | #MRQ_BWMGR | #CMD_BWMGR_QUERY_ABI | 8 | + * | #MRQ_BWMGR | #CMD_BWMGR_CALC_RATE | 8 + 8 * cmd_bwmgr_calc_rate_request.num_iso_clients | + * | #MRQ_ISO_CLIENT | #CMD_ISO_CLIENT_QUERY_ABI | 8 | + * | #MRQ_ISO_CLIENT | #CMD_ISO_CLIENT_CALCULATE_LA | 16 | + * | #MRQ_ISO_CLIENT | #CMD_ISO_CLIENT_SET_LA | 16 | + * | #MRQ_ISO_CLIENT | #CMD_ISO_CLIENT_GET_MAX_BW | 8 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_QUERY_ABI | 8 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_CALC_AND_SET | 16 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_CAP_SET | 8 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_GET_LAST_REQUEST | 9 | + * | #MRQ_OC_STATUS | - | 0 | + * | #MRQ_THROTTLE | #CMD_THROTTLE_SET_OC_CONFIG | 5 | + * @endcond + * + * @cond (bpmp_th500) + * The following additional MRQs are supported on TH500 -platform: + * + * | MRQ | Sub-command | Minimum payload length | + * | -------------------- | ------------------------------------- | ---------------------- | + * | #MRQ_CPU_NDIV_LIMITS | - | 4 | + * | #MRQ_THERMAL | #CMD_THERMAL_SET_TRIP | 20 | + * | #MRQ_STRAP | #STRAP_SET | 12 | + * | #MRQ_SHUTDOWN | - | 4 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_LANE_MARGIN_CONTROL | 24 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_LANE_MARGIN_STATUS | 4 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT | 5 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_CONTROLLER_STATE | 6 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF | 5 | + * | #MRQ_UPHY | #CMD_UPHY_PCIE_CONFIG_VDM | 3 | + * | #MRQ_TELEMETRY | - | 8 | + * | #MRQ_PWR_LIMIT | #CMD_PWR_LIMIT_QUERY_ABI | 8 | + * | #MRQ_PWR_LIMIT | #CMD_PWR_LIMIT_SET | 20 | + * | #MRQ_PWR_LIMIT | #CMD_PWR_LIMIT_GET | 16 | + * | #MRQ_PWR_LIMIT | #CMD_PWR_LIMIT_CURR_CAP | 8 | + * | #MRQ_GEARS | - | 0 | + * | #MRQ_C2C | #CMD_C2C_QUERY_ABI | 8 | + * | #MRQ_C2C | #CMD_C2C_START_INITIALIZATION | 5 | + * | #MRQ_C2C | #CMD_C2C_GET_STATUS | 4 | + * | #MRQ_C2C | #CMD_C2C_HOTRESET_PREP | 5 | + * | #MRQ_C2C | #CMD_C2C_START_HOTRESET | 5 | + * | #MRQ_THROTTLE | #CMD_THROTTLE_QUERY_ABI | 4 | + * | #MRQ_THROTTLE | #CMD_THROTTLE_GET_CHIPTHROT_STATUS | 4 | + * | #MRQ_PWRMODEL | #CMD_PWRMODEL_QUERY_ABI | 8 | + * | #MRQ_PWRMODEL | #CMD_PWRMODEL_PWR_GET | 16 | + * | #MRQ_PWR_CNTRL | #CMD_PWR_CNTRL_QUERY_ABI | 8 | + * | #MRQ_PWR_CNTRL | #CMD_PWR_CNTRL_BYPASS_SET | 12 | + * | #MRQ_PWR_CNTRL | #CMD_PWR_CNTRL_BYPASS_GET | 8 | + * @endcond + * + * @cond (bpmp_tb500) + * The following additional MRQs are supported on TB500 -platform: + * + * | MRQ | Sub-command | Minimum payload length | + * | -------------------- | ---------------------------------------- | ---------------------- | + * | #MRQ_PWR_LIMIT | #CMD_PWR_LIMIT_QUERY_ABI | 8 | + * | #MRQ_PWR_LIMIT | #CMD_PWR_LIMIT_SET | 20 | + * | #MRQ_PWR_LIMIT | #CMD_PWR_LIMIT_GET | 16 | + * | #MRQ_PWR_LIMIT | #CMD_PWR_LIMIT_CURR_CAP | 8 | + * | #MRQ_TELEMETRY_EX | #CMD_TELEMETRY_EX_QUERY_ABI | 8 | + * | #MRQ_TELEMETRY_EX | #CMD_TELEMETRY_EX_BASE_SZ_GET | 12 | + * | #MRQ_THROTTLE | #CMD_THROTTLE_GET_CHIPTHROT_STATUS | 4 | + * | #MRQ_C2C | #CMD_C2C_QUERY_ABI | 8 | + * | #MRQ_C2C | #CMD_C2C_START_INITIALIZATION | 5 | + * | #MRQ_C2C | #CMD_C2C_GET_STATUS | 4 | + * | #MRQ_C2C | #CMD_C2C_HOTRESET_PREP | 5 | + * | #MRQ_C2C | #CMD_C2C_START_HOTRESET | 5 | + * | MRQ_HWPM | CMD_HWPM_QUERY_ABI | 4 | + * | MRQ_HWPM | CMD_HWPM_IPMU_SET_TRIGGERS | 120 | + * | MRQ_HWPM | CMD_HWPM_IPMU_SET_PAYLOADS_SHIFTS | 120 | + * | MRQ_HWPM | CMD_HWPM_IPMU_GET_MAX_PAYLOADS | 0 | + * | MRQ_HWPM | CMD_HWPM_NVTHERM_SET_SAMPLE_RATE | 4 | + * | MRQ_HWPM | CMD_HWPM_NVTHERM_SET_BUBBLE_INTERVAL | 4 | + * | MRQ_HWPM | CMD_HWPM_NVTHERM_SET_FLEX_CHANNELS | 120 | + * | MRQ_HWPM | CMD_HWPM_ISENSE_GET_SENSOR_NAME | 4 | + * | MRQ_HWPM | CMD_HWPM_ISENSE_GET_SENSOR_CHANNEL | 4 | + * | MRQ_HWPM | CMD_HWPM_ISENSE_GET_SENSOR_SCALE_FACTOR | 4 | + * | MRQ_HWPM | CMD_HWPM_ISENSE_GET_SENSOR_OFFSET | 4 | + * | MRQ_HWPM | CMD_HWPM_ISENSE_GET_SUM_BLOCK_NAME | 4 | + * | MRQ_HWPM | CMD_HWPM_ISENSE_GET_SUM_BLOCK_INPUTS | 4 | + * | MRQ_DVFS | CMD_DVFS_QUERY_ABI | 4 | + * | MRQ_DVFS | CMD_DVFS_SET_CTRL_STATE | 8 | + * | MRQ_DVFS | CMD_DVFS_SET_MGR_STATE | 8 | + * | MRQ_PPP_PROFILE | CMD_PPP_PROFILE_QUERY_ABI | 8 | + * | MRQ_PPP_PROFILE | CMD_PPP_PROFILE_QUERY_MASKS | 8 | + * | MRQ_PPP_PROFILE | CMD_PPP_CORE_QUERY_CPU_MASK | 8 | + * | MRQ_PPP_PROFILE | CMD_PPP_AVAILABLE_QUERY | 4 | + * @endcond + * + * @cond (bpmp_safe && bpmp_t264) + * The following additional MRQ is supported on functional-safety + * builds for the T264 platform: + * + * | MRQ | Sub-command | Minimum payload length | + * | -------------------- | --------------------------------- | ---------------------- | + * | #MRQ_CPU_NDIV_LIMITS | - | 4 | + * | #MRQ_STRAP | #STRAP_SET | 12 | + * | #MRQ_SHUTDOWN | - | 4 | + * | #MRQ_FMON | #CMD_FMON_GEAR_CLAMP | 16 | + * | #MRQ_FMON | #CMD_FMON_GEAR_FREE | 4 | + * | #MRQ_FMON | #CMD_FMON_GEAR_GET | 4 | + * | #MRQ_FMON | #CMD_FMON_FAULT_STS_GET | 8 | + * | #MRQ_PCIE | #CMD_PCIE_EP_CONTROLLER_INIT | 5 | + * | #MRQ_PCIE | #CMD_PCIE_EP_CONTROLLER_OFF | 5 | + * | #MRQ_CR7 | #CMD_CR7_ENTRY | 12 | + * | #MRQ_CR7 | #CMD_CR7_EXIT | 12 | + * | #MRQ_SLC | #CMD_SLC_QUERY_ABI | 8 | + * | #MRQ_SLC | #CMD_SLC_BYPASS_SET | 8 | + * | #MRQ_SLC | #CMD_SLC_BYPASS_GET | 4 | + * @endcond + * + * @cond (!bpmp_safe && bpmp_t264) + * The following additional MRQs are supported on non-functional-safety + * builds for the T264 -platform: + * + * | MRQ | Sub-command | Minimum payload length | + * | -------------------- | --------------------------------- | ---------------------- | + * | #MRQ_CPU_NDIV_LIMITS | - | 4 | + * | #MRQ_STRAP | #STRAP_SET | 12 | + * | #MRQ_SHUTDOWN | - | 4 | + * | #MRQ_FMON | #CMD_FMON_GEAR_CLAMP | 16 | + * | #MRQ_FMON | #CMD_FMON_GEAR_FREE | 4 | + * | #MRQ_FMON | #CMD_FMON_GEAR_GET | 4 | + * | #MRQ_FMON | #CMD_FMON_FAULT_STS_GET | 8 | + * | #MRQ_OC_STATUS | - | 0 | + * | #MRQ_PCIE | #CMD_PCIE_EP_CONTROLLER_INIT | 5 | + * | #MRQ_PCIE | #CMD_PCIE_EP_CONTROLLER_OFF | 5 | + * | #MRQ_PCIE | #CMD_PCIE_RP_CONTROLLER_OFF | 5 | + * | #MRQ_CR7 | #CMD_CR7_ENTRY | 12 | + * | #MRQ_CR7 | #CMD_CR7_EXIT | 12 | + * | #MRQ_SLC | #CMD_SLC_QUERY_ABI | 8 | + * | #MRQ_SLC | #CMD_SLC_BYPASS_SET | 8 | + * | #MRQ_SLC | #CMD_SLC_BYPASS_GET | 4 | + * | #MRQ_ISO_CLIENT | #CMD_ISO_CLIENT_QUERY_ABI | 8 | + * | #MRQ_ISO_CLIENT | #CMD_ISO_CLIENT_CALCULATE_LA | 16 | + * | #MRQ_ISO_CLIENT | #CMD_ISO_CLIENT_SET_LA | 16 | + * | #MRQ_ISO_CLIENT | #CMD_ISO_CLIENT_GET_MAX_BW | 8 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_QUERY_ABI | 8 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_CALC_AND_SET | 16 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_CAP_SET | 8 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_CURR_AVAILABLE_BW | 8 | + * | #MRQ_BWMGR_INT | #CMD_BWMGR_INT_GET_LAST_REQUEST | 9 | + * @endcond * * **crc16** * @@ -220,7 +452,7 @@ struct mrq_request { * including this header. However the crc16 field is considered to be set to 0 when * calculating the CRC. Only used when #BPMP_MAIL_CRC_PRESENT is set. If * #BPMP_MAIL_CRC_PRESENT is set and this field does not match the CRC as - * calculated by BPMP, -BPMP_EBADMSG will be returned and the request will + * calculated by BPMP, -#BPMP_EBADMSG will be returned and the request will * be ignored. See code snippet below on how to calculate the CRC. * * @code @@ -322,6 +554,9 @@ struct mrq_response { #define MRQ_CPU_VHINT 28U #define MRQ_ABI_RATCHET 29U #define MRQ_EMC_DVFS_LATENCY 31U +//adoc: tag::bpmp_dmce_mrq_shutdown[] +#define MRQ_SHUTDOWN 49U +//adoc: end::bpmp_dmce_mrq_shutdown[] #define MRQ_RINGBUF_CONSOLE 65U #define MRQ_PG 66U #define MRQ_CPU_NDIV_LIMITS 67U @@ -341,48 +576,31 @@ struct mrq_response { #define MRQ_GEARS 82U #define MRQ_BWMGR_INT 83U #define MRQ_OC_STATUS 84U - -/** @cond DEPRECATED */ -#define MRQ_RESERVED_2 2U -#define MRQ_RESERVED_3 3U -#define MRQ_RESERVED_4 4U -#define MRQ_RESERVED_5 5U -#define MRQ_RESERVED_6 6U -#define MRQ_RESERVED_7 7U -#define MRQ_RESERVED_8 8U -#define MRQ_RESERVED_10 10U -#define MRQ_RESERVED_11 11U -#define MRQ_RESERVED_12 12U -#define MRQ_RESERVED_13 13U -#define MRQ_RESERVED_14 14U -#define MRQ_RESERVED_15 15U -#define MRQ_RESERVED_16 16U -#define MRQ_RESERVED_17 17U -#define MRQ_RESERVED_18 18U -#define MRQ_RESERVED_24 24U -#define MRQ_RESERVED_25 25U -#define MRQ_RESERVED_26 26U -#define MRQ_RESERVED_30 30U -#define MRQ_RESERVED_64 64U -#define MRQ_RESERVED_74 74U -/** @endcond DEPRECATED */ - -/** @} */ +#define MRQ_C2C 85U +#define MRQ_THROTTLE 86U +#define MRQ_PWRMODEL 87U +#define MRQ_PCIE 88U +#define MRQ_PWR_CNTRL 89U +#define MRQ_CR7 90U +#define MRQ_SLC 91U +#define MRQ_TELEMETRY_EX 92U +#define MRQ_HWPM 93U +#define MRQ_DVFS 94U +#define MRQ_PPP_PROFILE 95U /** - * @ingroup MRQ_Codes * @brief Maximum MRQ code to be sent by CPU software to * BPMP. Subject to change in future */ -#define MAX_CPU_MRQ_ID 84U +#define MAX_CPU_MRQ_ID 95U + +/** @} */ /** * @addtogroup MRQ_Payloads * @{ * @defgroup Ping Ping * @defgroup Query_Tag Query Tag - * @defgroup Module Loadable Modules - * @defgroup Trace Trace * @defgroup Debugfs Debug File System * @defgroup Reset Reset * @defgroup I2C I2C @@ -390,6 +608,7 @@ struct mrq_response { * @defgroup ABI_info ABI Info * @defgroup Powergating Power Gating * @defgroup Thermal Thermal + * @defgroup Throttle Throttle * @defgroup OC_status OC status * @defgroup Vhint CPU Voltage hint * @defgroup EMC EMC @@ -405,7 +624,22 @@ struct mrq_response { * @defgroup Telemetry Telemetry * @defgroup Pwrlimit PWR_LIMIT * @defgroup Gears Gears + * @defgroup Shutdown Shutdown * @defgroup BWMGR_INT Bandwidth Manager Integrated + * @defgroup C2C C2C + * @defgroup Pwrmodel Power Model + * @defgroup Pwrcntrl Power Controllers + * @cond bpmp_t264 + * * @defgroup PCIE PCIE + * * @defgroup CR7 CR7 + * * @defgroup Slc Slc + * @endcond + * @cond bpmp_tb500 + * * @defgroup Telemetry_ex Telemetry Expanded + * * @defgroup HWPM Hardware Performance Monitoring + * * @defgroup DVFS Dynamic Voltage and Frequency Scaling + * * @defgroup PPP power/performance profiles + * @endcond * @} MRQ_Payloads */ @@ -414,7 +648,6 @@ struct mrq_response { * @def MRQ_PING * @brief A simple ping * - * * Platforms: All * * Initiators: Any * * Targets: Any * * Request Payload: @ref mrq_ping_request @@ -424,7 +657,6 @@ struct mrq_response { * @def MRQ_THREADED_PING * @brief A deeper ping * - * * Platforms: All * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_ping_request @@ -441,8 +673,8 @@ struct mrq_response { * @brief Request with #MRQ_PING * * Used by the sender of an #MRQ_PING message to request a pong from - * recipient. The response from the recipient is computed based on - * #challenge. + * recipient. The response from the recipient is computed based on the + * mrq_ping_request::challenge -value. */ struct mrq_ping_request { /** @brief Arbitrarily chosen value */ @@ -470,7 +702,7 @@ struct mrq_ping_response { * * @deprecated Use #MRQ_QUERY_FW_TAG instead. * - * * Platforms: All + * @details * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_query_tag_request @@ -483,7 +715,7 @@ struct mrq_ping_response { * @brief Request with #MRQ_QUERY_TAG * * @deprecated This structure will be removed in future version. - * Use MRQ_QUERY_FW_TAG instead. + * Use #MRQ_QUERY_FW_TAG instead. */ struct mrq_query_tag_request { /** @brief Base address to store the firmware tag */ @@ -496,7 +728,6 @@ struct mrq_query_tag_request { * @def MRQ_QUERY_FW_TAG * @brief Query BPMP firmware's tag (i.e. unique identifier) * - * * Platforms: All * * Initiators: Any * * Targets: BPMP * * Request Payload: N/A @@ -510,10 +741,9 @@ struct mrq_query_tag_request { * * Sent in response to #MRQ_QUERY_FW_TAG message. #tag contains the unique * identifier for the version of firmware issuing the reply. - * */ struct mrq_query_fw_tag_response { - /** @brief Array to store tag information */ + /** @brief Array to store tag information */ uint8_t tag[32]; } BPMP_ABI_PACKED; @@ -532,9 +762,8 @@ struct mrq_threaded_ping_response { * @def MRQ_DEBUGFS * @brief Interact with BPMP's debugfs file nodes * - * @deprecated use MRQ_DEBUG instead. + * @deprecated Use #MRQ_DEBUG instead. * - * * Platforms: T186, T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_debugfs_request @@ -626,9 +855,9 @@ struct cmd_debugfs_dumpdir_response { /** * @ingroup Debugfs - * @brief Request with #MRQ_DEBUGFS. + * @brief Request with #MRQ_DEBUG. * - * The sender of an MRQ_DEBUGFS message uses #cmd to specify a debugfs + * The sender of an MRQ_DEBUG message uses #cmd to specify a debugfs * command to execute. Legal commands are the values of @ref * mrq_debugfs_commands. Each command requires a specific additional * payload of data. @@ -676,16 +905,15 @@ struct mrq_debugfs_response { /** * @ingroup MRQ_Codes * @def MRQ_DEBUG - * @brief Interact with BPMP's debugfs file nodes. Use message payload + * @brief Interact with BPMP-FW debugfs file nodes. Use message payload * for exchanging data. This is functionally equivalent to - * @ref MRQ_DEBUGFS. But the way in which data is exchanged is different. - * When software running on CPU tries to read a debugfs file, + * the deprecated MRQ_DEBUGFS but the way in which data is exchanged is + * different. When software running on CPU tries to read a debugfs file, * the file path and read data will be stored in message payload. * Since the message payload size is limited, a debugfs file * transaction might require multiple frames of data exchanged * between BPMP and CPU until the transaction completes. * - * * Platforms: T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_debug_request @@ -694,17 +922,34 @@ struct mrq_debugfs_response { /** @ingroup Debugfs */ enum mrq_debug_commands { - /** @brief Open required file for read operation */ + /** + * @brief Open file represented by the path in + * cmd_debug_fopen_request::name for read operation + */ CMD_DEBUG_OPEN_RO = 0, - /** @brief Open required file for write operation */ + /** + * @brief Open file represented by the path in + * cmd_debug_fopen_request::name for write operation + */ CMD_DEBUG_OPEN_WO = 1, - /** @brief Perform read */ + /** + * @brief Perform read on a previously opened file handle represented + * by the cmd_debug_fread_request::fd -value. + */ CMD_DEBUG_READ = 2, - /** @brief Perform write */ + /** + * @brief Perform write on a previously opened file handle represented + * by the cmd_debug_fwrite_request::fd -value. + */ CMD_DEBUG_WRITE = 3, - /** @brief Close file */ + /** + * @brief Close previously opened file handle. + */ CMD_DEBUG_CLOSE = 4, - /** @brief Not a command */ + /** + * @brief Not a command, represents maximum number of supported + * sub-commands + */ CMD_DEBUG_MAX }; @@ -727,35 +972,38 @@ enum mrq_debug_commands { /** * @ingroup Debugfs - * @brief Parameters for CMD_DEBUG_OPEN command + * @brief Parameters for #CMD_DEBUG_OPEN_RO and #CMD_DEBUG_OPEN_WO -commands */ struct cmd_debug_fopen_request { - /** @brief File name - Null-terminated string with maximum - * length @ref DEBUG_FNAME_MAX_SZ + /** + * @brief File name - Null-terminated string with maximum + * length including the terminator defined by the + * #DEBUG_FNAME_MAX_SZ -preprocessor constant. */ char name[DEBUG_FNAME_MAX_SZ]; } BPMP_ABI_PACKED; /** * @ingroup Debugfs - * @brief Response data for CMD_DEBUG_OPEN_RO/WO command + * @brief Response data for #CMD_DEBUG_OPEN_RO and #CMD_DEBUG_OPEN_WO commands */ struct cmd_debug_fopen_response { /** @brief Identifier for file access */ uint32_t fd; /** @brief Data length. File data size for READ command. - * Maximum allowed length for WRITE command + * Maximum allowed length for WRITE command */ uint32_t datalen; } BPMP_ABI_PACKED; /** * @ingroup Debugfs - * @brief Parameters for CMD_DEBUG_READ command + * @brief Parameters for #CMD_DEBUG_READ command */ struct cmd_debug_fread_request { - /** @brief File access identifier received in response - * to CMD_DEBUG_OPEN_RO request + /** + * @brief File access identifier received in response + * to #CMD_DEBUG_OPEN_RO request */ uint32_t fd; } BPMP_ABI_PACKED; @@ -770,7 +1018,7 @@ struct cmd_debug_fread_request { /** * @ingroup Debugfs - * @brief Response data for CMD_DEBUG_READ command + * @brief Response data for #CMD_DEBUG_READ command */ struct cmd_debug_fread_response { /** @brief Size of data provided in this response in bytes */ @@ -789,11 +1037,11 @@ struct cmd_debug_fread_response { /** * @ingroup Debugfs - * @brief Parameters for CMD_DEBUG_WRITE command + * @brief Parameters for #CMD_DEBUG_WRITE command */ struct cmd_debug_fwrite_request { /** @brief File access identifier received in response - * to CMD_DEBUG_OPEN_RO request + * to prior #CMD_DEBUG_OPEN_RO -request */ uint32_t fd; /** @brief Size of write data in bytes */ @@ -804,11 +1052,12 @@ struct cmd_debug_fwrite_request { /** * @ingroup Debugfs - * @brief Parameters for CMD_DEBUG_CLOSE command + * @brief Parameters for #CMD_DEBUG_CLOSE command */ struct cmd_debug_fclose_request { - /** @brief File access identifier received in response - * to CMD_DEBUG_OPEN_RO request + /** + * @brief File access identifier received in prior response + * to #CMD_DEBUG_OPEN_RO or #CMD_DEBUG_OPEN_WO -request. */ uint32_t fd; } BPMP_ABI_PACKED; @@ -817,30 +1066,34 @@ struct cmd_debug_fclose_request { * @ingroup Debugfs * @brief Request with #MRQ_DEBUG. * - * The sender of an MRQ_DEBUG message uses #cmd to specify a debugfs - * command to execute. Legal commands are the values of @ref - * mrq_debug_commands. Each command requires a specific additional - * payload of data. + * The sender of an #MRQ_DEBUG message uses mrq_debug_request::cmd to specify + * which debugfs sub-command to execute. Legal sub-commands are the values + * specified in the @ref mrq_debug_commands -enumeration. Each sub-command + * requires a specific additional payload of data according to the following + * table: * - * |command |payload| - * |-------------------|-------| - * |CMD_DEBUG_OPEN_RO |fop | - * |CMD_DEBUG_OPEN_WO |fop | - * |CMD_DEBUG_READ |frd | - * |CMD_DEBUG_WRITE |fwr | - * |CMD_DEBUG_CLOSE |fcl | + * |Sub-command |Payload structure | + * |--------------------|---------------------------| + * |#CMD_DEBUG_OPEN_RO |cmd_debug_fopen_request | + * |#CMD_DEBUG_OPEN_WO |cmd_debug_fopen_request | + * |#CMD_DEBUG_READ |cmd_debug_fread_request | + * |#CMD_DEBUG_WRITE |cmd_debug_fwrite_request | + * |#CMD_DEBUG_CLOSE |cmd_debug_fclose_request | */ struct mrq_debug_request { - /** @brief Sub-command (@ref mrq_debug_commands) */ + /** @brief Sub-command identifier from @ref mrq_debug_commands */ uint32_t cmd; union { - /** @brief Request payload for CMD_DEBUG_OPEN_RO/WO command */ + /** + * @brief Request payload for #CMD_DEBUG_OPEN_RO and + * #CMD_DEBUG_OPEN_WO sub-commands + */ struct cmd_debug_fopen_request fop; - /** @brief Request payload for CMD_DEBUG_READ command */ + /** @brief Request payload for #CMD_DEBUG_READ sub-command */ struct cmd_debug_fread_request frd; - /** @brief Request payload for CMD_DEBUG_WRITE command */ + /** @brief Request payload for #CMD_DEBUG_WRITE sub-command */ struct cmd_debug_fwrite_request fwr; - /** @brief Request payload for CMD_DEBUG_CLOSE command */ + /** @brief Request payload for #CMD_DEBUG_CLOSE sub-command */ struct cmd_debug_fclose_request fcl; } BPMP_UNION_ANON; } BPMP_ABI_PACKED; @@ -850,9 +1103,12 @@ struct mrq_debug_request { */ struct mrq_debug_response { union { - /** @brief Response data for CMD_DEBUG_OPEN_RO/WO command */ + /** + * @brief Response data for the #CMD_DEBUG_OPEN_RO and + * #CMD_DEBUG_OPEN_WO sub-commands + */ struct cmd_debug_fopen_response fop; - /** @brief Response data for CMD_DEBUG_READ command */ + /** @brief Response data for the #CMD_DEBUG_READ sub-command */ struct cmd_debug_fread_response frd; } BPMP_UNION_ANON; } BPMP_ABI_PACKED; @@ -862,7 +1118,6 @@ struct mrq_debug_response { * @def MRQ_RESET * @brief Reset an IP block * - * * Platforms: T186, T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_reset_request @@ -872,39 +1127,46 @@ struct mrq_debug_response { * @{ */ +/** + * @brief Sub-command identifiers for #MRQ_RESET + */ enum mrq_reset_commands { /** * @brief Assert module reset * - * mrq_response::err is 0 if the operation was successful, or @n - * -#BPMP_EINVAL if mrq_reset_request::reset_id is invalid @n - * -#BPMP_EACCES if mrq master is not an owner of target domain reset @n - * -#BPMP_ENOTSUP if target domain h/w state does not allow reset + * mrq_response::err is + * * 0 if the operation was successful + * * -#BPMP_EINVAL if mrq_reset_request::reset_id is invalid + * * -#BPMP_EACCES if mrq master is not an owner of target domain reset + * * -#BPMP_ENOTSUP if target domain h/w state does not allow reset */ CMD_RESET_ASSERT = 1, /** * @brief Deassert module reset * - * mrq_response::err is 0 if the operation was successful, or @n - * -#BPMP_EINVAL if mrq_reset_request::reset_id is invalid @n - * -#BPMP_EACCES if mrq master is not an owner of target domain reset @n - * -#BPMP_ENOTSUP if target domain h/w state does not allow reset + * mrq_response::err is + * * 0 if the operation was successful + * * -#BPMP_EINVAL if mrq_reset_request::reset_id is invalid + * * -#BPMP_EACCES if mrq master is not an owner of target domain reset + * * -#BPMP_ENOTSUP if target domain h/w state does not allow reset */ CMD_RESET_DEASSERT = 2, /** * @brief Assert and deassert the module reset * - * mrq_response::err is 0 if the operation was successful, or @n - * -#BPMP_EINVAL if mrq_reset_request::reset_id is invalid @n - * -#BPMP_EACCES if mrq master is not an owner of target domain reset @n - * -#BPMP_ENOTSUP if target domain h/w state does not allow reset + * mrq_response::err is + * * 0 if the operation was successful + * * -#BPMP_EINVAL if mrq_reset_request::reset_id is invalid + * * -#BPMP_EACCES if mrq master is not an owner of target domain reset + * * -#BPMP_ENOTSUP if target domain h/w state does not allow reset */ CMD_RESET_MODULE = 3, /** * @brief Get the highest reset ID * - * mrq_response::err is 0 if the operation was successful, or @n - * -#BPMP_ENODEV if no reset domains are supported (number of IDs is 0) + * mrq_response::err is + * * 0 if the operation was successful + * * -#BPMP_ENODEV if no reset domains are supported (number of IDs is 0) */ CMD_RESET_GET_MAX_ID = 4, @@ -913,15 +1175,15 @@ enum mrq_reset_commands { }; /** - * @brief Request with MRQ_RESET + * @brief Request with #MRQ_RESET * * Used by the sender of an #MRQ_RESET message to request BPMP to - * assert or or deassert a given reset line. + * assert or deassert a given reset line. */ struct mrq_reset_request { - /** @brief Reset action to perform (@ref mrq_reset_commands) */ + /** @brief Reset action to perform, from @ref mrq_reset_commands */ uint32_t cmd; - /** @brief Id of the reset to affected */ + /** @brief ID of the reset to affected, from @ref bpmp_reset_ids */ uint32_t reset_id; } BPMP_ABI_PACKED; @@ -940,7 +1202,7 @@ struct cmd_reset_get_max_id_response { * * Each sub-command supported by @ref mrq_reset_request may return * sub-command-specific data. Some do and some do not as indicated - * in the following table + * in the following table: * * | sub-command | payload | * |----------------------|------------------| @@ -962,7 +1224,6 @@ struct mrq_reset_response { * @def MRQ_I2C * @brief Issue an i2c transaction * - * * Platforms: T186, T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_i2c_request @@ -971,19 +1232,60 @@ struct mrq_reset_response { * @addtogroup I2C * @{ */ + +/** + * @brief Size of the cmd_i2c_xfer_request::data_buf -member array in bytes. + */ #define TEGRA_I2C_IPC_MAX_IN_BUF_SIZE (MSG_DATA_MIN_SZ - 12U) + +/** + * @brief Size of the cmd_i2c_xfer_response::data_buf -member array in bytes. + */ #define TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE (MSG_DATA_MIN_SZ - 4U) +/** + * @defgroup seriali2c_flags I2C flags + * + * @brief I2C transaction modifier flags for each transaction segment + * in #MRQ_I2C subcommand CMD_I2C_XFER + */ + +/** + * @addtogroup seriali2c_flags + * @{ + */ + +/** @brief when set, use 10-bit I2C slave address */ #define SERIALI2C_TEN 0x0010U +/** @brief when set, perform a Read transaction */ #define SERIALI2C_RD 0x0001U -#define SERIALI2C_STOP 0x8000U +/** + * @brief when set, no repeated START is issued between the segments + * of transaction. This flag is ignored for the first segment as any + * transaction always starts with a START condition + */ #define SERIALI2C_NOSTART 0x4000U -#define SERIALI2C_REV_DIR_ADDR 0x2000U +/** + * @brief when set, a no-ACK from slave device is ignored and treated + * always as success + */ #define SERIALI2C_IGNORE_NAK 0x1000U +/** @} seriali2c_flags */ + +/** brief Unused flag. Retained for backwards compatibility. */ +#define SERIALI2C_STOP 0x8000U +/** brief Unused flag. Retained for backwards compatibility. */ +#define SERIALI2C_REV_DIR_ADDR 0x2000U +/** brief Unused flag. Retained for backwards compatibility. */ #define SERIALI2C_NO_RD_ACK 0x0800U +/** brief Unused flag. Retained for backwards compatibility. */ #define SERIALI2C_RECV_LEN 0x0400U -enum { +/** + * @brief Supported I2C sub-command identifiers + */ +enum mrq_i2c_commands { + /** @brief Perform an I2C transaction */ CMD_I2C_XFER = 1 }; @@ -1005,7 +1307,7 @@ enum { struct serial_i2c_request { /** @brief I2C slave address */ uint16_t addr; - /** @brief Bitmask of SERIALI2C_ flags */ + /** @brief Bitmask of @ref seriali2c_flags */ uint16_t flags; /** @brief Length of I2C transaction in bytes */ uint16_t len; @@ -1020,13 +1322,13 @@ struct cmd_i2c_xfer_request { /** * @brief Tegra PWR_I2C bus identifier * - * @cond (bpmp_t234 || bpmp_t239 || bpmp_t194) + * @cond (bpmp_t186 || bpmp_t194 || bpmp_t234 || bpmp_t238 || bpmp_t264) * Must be set to 5. - * @endcond (bpmp_t234 || bpmp_t239 || bpmp_t194) - * @cond bpmp_th500 - * Must be set to 1. - * @endcond bpmp_th500 + * @endcond * + * @cond (bpmp_th500) + * Must be set to 1. + * @endcond */ uint32_t bus_id; @@ -1047,7 +1349,7 @@ struct cmd_i2c_xfer_request { struct cmd_i2c_xfer_response { /** @brief Count of valid bytes in #data_buf*/ uint32_t data_size; - /** @brief I2c read data */ + /** @brief I2C read data */ uint8_t data_buf[TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE]; } BPMP_ABI_PACKED; @@ -1064,16 +1366,19 @@ struct mrq_i2c_request { /** * @brief Response to #MRQ_I2C * - * mrq_response:err is - * 0: Success - * -#BPMP_EBADCMD: if mrq_i2c_request::cmd is other than 1 - * -#BPMP_EINVAL: if cmd_i2c_xfer_request does not contain correctly formatted request - * -#BPMP_ENODEV: if cmd_i2c_xfer_request::bus_id is not supported by BPMP - * -#BPMP_EACCES: if i2c transaction is not allowed due to firewall rules - * -#BPMP_ETIMEDOUT: if i2c transaction times out - * -#BPMP_ENXIO: if i2c slave device does not reply with ACK to the transaction - * -#BPMP_EAGAIN: if ARB_LOST condition is detected by the i2c controller - * -#BPMP_EIO: any other i2c controller error code than NO_ACK or ARB_LOST + * mrq_response::err value for this response is defined as: + * + * | Value | Description | + * |--------------------|---------------------------------------------------------------------| + * | 0 | Success | + * | -#BPMP_EBADCMD | mrq_i2c_request::cmd is other than 1 | + * | -#BPMP_EINVAL | cmd_i2c_xfer_request does not contain correctly formatted request | + * | -#BPMP_ENODEV | cmd_i2c_xfer_request::bus_id is not supported by BPMP | + * | -#BPMP_EACCES | I2C transaction is not allowed due to firewall rules | + * | -#BPMP_ETIMEDOUT | I2C transaction times out | + * | -#BPMP_ENXIO | I2C slave device does not reply with ACK to the transaction | + * | -#BPMP_EAGAIN | ARB_LOST condition is detected by the I2C controller | + * | -#BPMP_EIO | Any other I2C controller error code than NO_ACK or ARB_LOST | */ struct mrq_i2c_response { struct cmd_i2c_xfer_response xfer; @@ -1086,7 +1391,6 @@ struct mrq_i2c_response { * @def MRQ_CLK * @brief Perform a clock operation * - * * Platforms: T186, T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_clk_request @@ -1095,205 +1399,354 @@ struct mrq_i2c_response { * @addtogroup Clocks * @{ */ -enum { + +/** + * @brief Sub-command identifiers for #MRQ_CLK + */ +enum mrq_clk_commands { + /** Get clock rate */ CMD_CLK_GET_RATE = 1, + + /** Set clock rate */ CMD_CLK_SET_RATE = 2, + + /** Get attainable clock rate closer to a given rate */ CMD_CLK_ROUND_RATE = 3, + + /** Get parent clock identifier for a given clock */ CMD_CLK_GET_PARENT = 4, + + /** Change clock parent */ CMD_CLK_SET_PARENT = 5, + + /** Get clock enable status */ CMD_CLK_IS_ENABLED = 6, + + /** Enable a clock */ CMD_CLK_ENABLE = 7, + + /** Disable a clock */ CMD_CLK_DISABLE = 8, -/** @cond DEPRECATED */ - CMD_CLK_PROPERTIES = 9, - CMD_CLK_POSSIBLE_PARENTS = 10, - CMD_CLK_NUM_POSSIBLE_PARENTS = 11, - CMD_CLK_GET_POSSIBLE_PARENT = 12, - CMD_CLK_RESET_REFCOUNTS = 13, -/** @endcond DEPRECATED */ + + /** Get all information about a clock */ CMD_CLK_GET_ALL_INFO = 14, + + /** Get largest supported clock identifier */ CMD_CLK_GET_MAX_CLK_ID = 15, + + /** Get clock maximum rate at VMIN */ CMD_CLK_GET_FMAX_AT_VMIN = 16, + + /** Largest supported #MRQ_CLK sub-command identifier + 1 */ CMD_CLK_MAX, }; +/** + * Flag bit set in cmd_clk_get_all_info_response::flags -field when clock + * supports changing of the parent clock at runtime. + */ #define BPMP_CLK_HAS_MUX (1U << 0U) + +/** + * Flag bit set in cmd_clk_get_all_info_response::flags -field when clock + * supports changing the clock rate at runtime. + */ #define BPMP_CLK_HAS_SET_RATE (1U << 1U) + +/** + * Flag bit set in cmd_clk_get_all_info_response::flags -field when clock is a + * root clock without visible parents. + */ #define BPMP_CLK_IS_ROOT (1U << 2U) + #define BPMP_CLK_IS_VAR_ROOT (1U << 3U) + /** * @brief Protection against rate and parent changes * - * #MRQ_CLK command #CMD_CLK_SET_RATE or #MRQ_CLK command #CMD_CLK_SET_PARENT will return - * -#BPMP_EACCES. + * #MRQ_CLK command #CMD_CLK_SET_RATE or #MRQ_CLK command #CMD_CLK_SET_PARENT + * will return -#BPMP_EACCES. */ #define BPMP_CLK_RATE_PARENT_CHANGE_DENIED (1U << 30) /** * @brief Protection against state changes * - * #MRQ_CLK command #CMD_CLK_ENABLE or #MRQ_CLK command #CMD_CLK_DISABLE will return - * -#BPMP_EACCES. + * #MRQ_CLK command #CMD_CLK_ENABLE or #MRQ_CLK command #CMD_CLK_DISABLE + * will return -#BPMP_EACCES. */ #define BPMP_CLK_STATE_CHANGE_DENIED (1U << 31) +/** + * Size of the cmd_clk_get_all_info_response::name -array in number + * of elements. + */ #define MRQ_CLK_NAME_MAXLEN 40U + +/** + * @brief Maximum number of elements in parent_id arrays of clock info responses. + */ #define MRQ_CLK_MAX_PARENTS 16U -/** @private */ +/** + * @brief Request payload for #MRQ_CLK sub-command #CMD_CLK_GET_RATE + * + * This structure is an empty placeholder for future expansion of this + * sub-command. + */ struct cmd_clk_get_rate_request { BPMP_ABI_EMPTY } BPMP_ABI_PACKED; +/** + * @brief Response payload for #MRQ_CLK sub-command #CMD_CLK_GET_RATE + */ struct cmd_clk_get_rate_response { + /** + * Current rate of the given clock in Hz if mrq_response::err is 0 to + * indicate successful #CMD_CLK_GET_RATE -request. + */ int64_t rate; } BPMP_ABI_PACKED; +/** + * @brief Request payload for #MRQ_CLK sub-command #CMD_CLK_SET_RATE + */ struct cmd_clk_set_rate_request { + /** Unused / reserved field. */ int32_t unused; + + /** Requested rate of the clock in Hz. */ int64_t rate; } BPMP_ABI_PACKED; +/** + * @brief Response payload for #MRQ_CLK sub-command #CMD_CLK_SET_RATE + */ struct cmd_clk_set_rate_response { + /** + * If request was successful (mrq_response::err is 0), set to the new + * rate of the given clock in Hz. + */ int64_t rate; } BPMP_ABI_PACKED; +/** + * @brief Request payload for #MRQ_CLK sub-command #CMD_CLK_ROUND_RATE + */ struct cmd_clk_round_rate_request { + /** Unused / reserved field. */ int32_t unused; + + /** Target rate for the clock */ int64_t rate; } BPMP_ABI_PACKED; +/** + * @brief Response payload for #MRQ_CLK sub-command #CMD_CLK_ROUND_RATE + */ struct cmd_clk_round_rate_response { + /** + * The attainable rate if request was successful + * (mrq_response::err is 0). + */ int64_t rate; } BPMP_ABI_PACKED; -/** @private */ +/** + * @brief Request payload for #MRQ_CLK sub-command #CMD_CLK_GET_PARENT + * + * This structure is an empty placeholder for future expansion of this + * sub-command. + */ struct cmd_clk_get_parent_request { BPMP_ABI_EMPTY } BPMP_ABI_PACKED; +/** + * @brief Response payload for #MRQ_CLK sub-command #CMD_CLK_GET_PARENT + */ struct cmd_clk_get_parent_response { + /** + * The clock identifier of the parent clock if request was successful + * (mrq_response::err is 0). + */ uint32_t parent_id; } BPMP_ABI_PACKED; +/** + * @brief Request payload for #MRQ_CLK sub-command #CMD_CLK_SET_PARENT + */ struct cmd_clk_set_parent_request { + /** + * The clock identifier of the new parent clock. + */ uint32_t parent_id; } BPMP_ABI_PACKED; +/** + * @brief Response payload for #MRQ_CLK sub-command #CMD_CLK_SET_PARENT + */ struct cmd_clk_set_parent_response { + /** + * The clock identifier of the new parent clock if request was + * successful (mrq_response::err is 0). + */ uint32_t parent_id; } BPMP_ABI_PACKED; -/** @private */ +/** + * @brief Request payload for #CMD_CLK_IS_ENABLED -sub-command + * + * This structure is an empty placeholder for future expansion of this + * sub-command. + */ struct cmd_clk_is_enabled_request { BPMP_ABI_EMPTY } BPMP_ABI_PACKED; /** - * @brief Response data to #MRQ_CLK sub-command CMD_CLK_IS_ENABLED + * @brief Response payload for #MRQ_CLK sub-command #CMD_CLK_IS_ENABLED */ struct cmd_clk_is_enabled_response { /** * @brief The state of the clock that has been successfully - * requested with CMD_CLK_ENABLE or CMD_CLK_DISABLE by the + * requested with #CMD_CLK_ENABLE or #CMD_CLK_DISABLE by the * master invoking the command earlier. * * The state may not reflect the physical state of the clock * if there are some other masters requesting it to be - * enabled. + * enabled. Valid values: * - * Value 0 is disabled, all other values indicate enabled. + * * Value 0: The clock is disabled, + * * Value 1: The clock is enabled. */ int32_t state; } BPMP_ABI_PACKED; -/** @private */ +/** + * @brief Request payload for #MRQ_CLK sub-command #CMD_CLK_ENABLE + * + * This structure is an empty placeholder for future expansion of this + * sub-command. + */ struct cmd_clk_enable_request { BPMP_ABI_EMPTY } BPMP_ABI_PACKED; -/** @private */ +/** + * @brief Response payload for #MRQ_CLK sub-command #CMD_CLK_ENABLE + * + * This structure is an empty placeholder for future expansion of this + * sub-command. + */ struct cmd_clk_enable_response { BPMP_ABI_EMPTY } BPMP_ABI_PACKED; -/** @private */ +/** + * @brief Request payload for #MRQ_CLK sub-command #CMD_CLK_DISABLE + * + * This structure is an empty placeholder for future expansion of this + * sub-command. + */ struct cmd_clk_disable_request { BPMP_ABI_EMPTY } BPMP_ABI_PACKED; -/** @private */ +/** + * @brief Response payload for #MRQ_CLK sub-command #CMD_CLK_DISABLE + * + * This structure is an empty placeholder for future expansion of this + * sub-command. + */ struct cmd_clk_disable_response { BPMP_ABI_EMPTY } BPMP_ABI_PACKED; -/** @cond DEPRECATED */ -/** @private */ -struct cmd_clk_properties_request { - BPMP_ABI_EMPTY -} BPMP_ABI_PACKED; - -/** @todo flags need to be spelled out here */ -struct cmd_clk_properties_response { - uint32_t flags; -} BPMP_ABI_PACKED; - -/** @private */ -struct cmd_clk_possible_parents_request { - BPMP_ABI_EMPTY -} BPMP_ABI_PACKED; - -struct cmd_clk_possible_parents_response { - uint8_t num_parents; - uint8_t reserved[3]; - uint32_t parent_id[MRQ_CLK_MAX_PARENTS]; -} BPMP_ABI_PACKED; - -/** @private */ -struct cmd_clk_num_possible_parents_request { - BPMP_ABI_EMPTY -} BPMP_ABI_PACKED; - -struct cmd_clk_num_possible_parents_response { - uint8_t num_parents; -} BPMP_ABI_PACKED; - -struct cmd_clk_get_possible_parent_request { - uint8_t parent_idx; -} BPMP_ABI_PACKED; - -struct cmd_clk_get_possible_parent_response { - uint32_t parent_id; -} BPMP_ABI_PACKED; -/** @endcond DEPRECATED */ - -/** @private */ +/** + * @brief Request payload for #MRQ_CLK sub-command #CMD_CLK_GET_ALL_INFO + * + * This structure is an empty placeholder for future expansion of this + * sub-command. + */ struct cmd_clk_get_all_info_request { BPMP_ABI_EMPTY } BPMP_ABI_PACKED; + +/** + * @brief Response payload for #MRQ_CLK sub-command #CMD_CLK_GET_ALL_INFO + * + * The values in the response are only set and valid if request status in + * mrq_response::err is 0. + */ struct cmd_clk_get_all_info_response { + /** + * State / informational flags for the clock: + * + * | Flag bit | Description | + * |------------------------|------------------------------------------| + * | #BPMP_CLK_IS_ROOT | Clock is a root clock. | + * | #BPMP_CLK_HAS_MUX | Clock supports changing of parent clock. | + * | #BPMP_CLK_HAS_SET_RATE | Clock supports changing clock rate. | + */ uint32_t flags; + + /** + * Current parent clock identifier. + */ uint32_t parent; + + /** + * Array of possible parent clock identifiers. + */ uint32_t parents[MRQ_CLK_MAX_PARENTS]; + + /** + * Number of identifiers in the #parents -array. + */ uint8_t num_parents; + + /** + * Friendly name of the clock, truncated to fit the array + * and null-terminated. + */ uint8_t name[MRQ_CLK_NAME_MAXLEN]; } BPMP_ABI_PACKED; -/** @private */ + +/** + * @brief Request payload for #MRQ_CLK sub-command #CMD_CLK_GET_MAX_CLK_ID + * + * This structure is an empty placeholder for future expansion of this + * sub-command. + */ struct cmd_clk_get_max_clk_id_request { BPMP_ABI_EMPTY } BPMP_ABI_PACKED; +/** + * @brief Response payload for #MRQ_CLK sub-command #CMD_CLK_GET_MAX_CLK_ID + */ struct cmd_clk_get_max_clk_id_response { + /** @brief Largest supported clock identifier. */ uint32_t max_id; } BPMP_ABI_PACKED; -/** @private */ +/** + * @brief Request payload for #MRQ_CLK sub-command #CMD_CLK_GET_FMAX_AT_VMIN + * + * This structure is an empty placeholder for future expansion of this + * sub-command. + */ struct cmd_clk_get_fmax_at_vmin_request { BPMP_ABI_EMPTY } BPMP_ABI_PACKED; +/** + * @brief Response payload for #MRQ_CLK sub-command #CMD_CLK_GET_FMAX_AT_VMIN + */ struct cmd_clk_get_fmax_at_vmin_response { int64_t rate; } BPMP_ABI_PACKED; @@ -1308,38 +1761,26 @@ struct cmd_clk_get_fmax_at_vmin_response { * require no additional data. Others have a sub-command specific * payload * - * |sub-command |payload | - * |----------------------------|-----------------------| - * |CMD_CLK_GET_RATE |- | - * |CMD_CLK_SET_RATE |clk_set_rate | - * |CMD_CLK_ROUND_RATE |clk_round_rate | - * |CMD_CLK_GET_PARENT |- | - * |CMD_CLK_SET_PARENT |clk_set_parent | - * |CMD_CLK_IS_ENABLED |- | - * |CMD_CLK_ENABLE |- | - * |CMD_CLK_DISABLE |- | - * |CMD_CLK_GET_ALL_INFO |- | - * |CMD_CLK_GET_MAX_CLK_ID |- | - * |CMD_CLK_GET_FMAX_AT_VMIN |- - * | - * + * |Sub-command |Payload | + * |----------------------------|-----------------------------| + * |#CMD_CLK_GET_RATE |- | + * |#CMD_CLK_SET_RATE |#cmd_clk_set_rate_request | + * |#CMD_CLK_ROUND_RATE |#cmd_clk_round_rate_request | + * |#CMD_CLK_GET_PARENT |- | + * |#CMD_CLK_SET_PARENT |#cmd_clk_set_parent_request | + * |#CMD_CLK_IS_ENABLED |- | + * |#CMD_CLK_ENABLE |- | + * |#CMD_CLK_DISABLE |- | + * |#CMD_CLK_GET_ALL_INFO |- | + * |#CMD_CLK_GET_MAX_CLK_ID |- | + * |#CMD_CLK_GET_FMAX_AT_VMIN |- | */ -/** @cond DEPRECATED - * - * Older versions of firmware also supported following sub-commands: - * |CMD_CLK_PROPERTIES |- | - * |CMD_CLK_POSSIBLE_PARENTS |- | - * |CMD_CLK_NUM_POSSIBLE_PARENTS|- | - * |CMD_CLK_GET_POSSIBLE_PARENT |clk_get_possible_parent| - * |CMD_CLK_RESET_REFCOUNTS |- | - * - * @endcond DEPRECATED */ - struct mrq_clk_request { /** @brief Sub-command and clock id concatenated to 32-bit word. - * - bits[31..24] is the sub-cmd. - * - bits[23..0] is the clock id + * + * - bits[31..24] is the sub-command ID from @ref mrq_clk_commands. + * - bits[23..0] is the clock identifier from @ref bpmp_clock_ids. */ uint32_t cmd_and_id; @@ -1357,15 +1798,6 @@ struct mrq_clk_request { struct cmd_clk_disable_request clk_disable; /** @private */ struct cmd_clk_is_enabled_request clk_is_enabled; - /** @cond DEPRECATED */ - /** @private */ - struct cmd_clk_properties_request clk_properties; - /** @private */ - struct cmd_clk_possible_parents_request clk_possible_parents; - /** @private */ - struct cmd_clk_num_possible_parents_request clk_num_possible_parents; - struct cmd_clk_get_possible_parent_request clk_get_possible_parent; - /** @endcond DEPRECATED */ /** @private */ struct cmd_clk_get_all_info_request clk_get_all_info; /** @private */ @@ -1381,35 +1813,24 @@ struct mrq_clk_request { * * Each sub-command supported by @ref mrq_clk_request may return * sub-command-specific data. Some do and some do not as indicated in - * the following table + * the following table: * - * |sub-command |payload | - * |----------------------------|------------------------| - * |CMD_CLK_GET_RATE |clk_get_rate | - * |CMD_CLK_SET_RATE |clk_set_rate | - * |CMD_CLK_ROUND_RATE |clk_round_rate | - * |CMD_CLK_GET_PARENT |clk_get_parent | - * |CMD_CLK_SET_PARENT |clk_set_parent | - * |CMD_CLK_IS_ENABLED |clk_is_enabled | - * |CMD_CLK_ENABLE |- | - * |CMD_CLK_DISABLE |- | - * |CMD_CLK_GET_ALL_INFO |clk_get_all_info | - * |CMD_CLK_GET_MAX_CLK_ID |clk_get_max_id | - * |CMD_CLK_GET_FMAX_AT_VMIN |clk_get_fmax_at_vmin | + * |Sub-command |Payload | + * |----------------------------|-----------------------------------| + * |#CMD_CLK_GET_RATE |#cmd_clk_get_rate_response | + * |#CMD_CLK_SET_RATE |#cmd_clk_set_rate_response | + * |#CMD_CLK_ROUND_RATE |#cmd_clk_round_rate_response | + * |#CMD_CLK_GET_PARENT |#cmd_clk_get_parent_response | + * |#CMD_CLK_SET_PARENT |#cmd_clk_set_parent_response | + * |#CMD_CLK_IS_ENABLED |#cmd_clk_is_enabled_response | + * |#CMD_CLK_ENABLE |- | + * |#CMD_CLK_DISABLE |- | + * |#CMD_CLK_GET_ALL_INFO |#cmd_clk_get_all_info_response | + * |#CMD_CLK_GET_MAX_CLK_ID |#cmd_clk_get_max_clk_id_response | + * |#CMD_CLK_GET_FMAX_AT_VMIN |#cmd_clk_get_fmax_at_vmin_response | * */ -/** @cond DEPRECATED - * - * Older versions of firmware also supported following sub-commands: - * |CMD_CLK_PROPERTIES |clk_properties | - * |CMD_CLK_POSSIBLE_PARENTS |clk_possible_parents | - * |CMD_CLK_NUM_POSSIBLE_PARENTS|clk_num_possible_parents| - * |CMD_CLK_GET_POSSIBLE_PARENT |clk_get_possible_parents| - * |CMD_CLK_RESET_REFCOUNTS |- | - * - * @endcond DEPRECATED */ - struct mrq_clk_response { union { struct cmd_clk_get_rate_response clk_get_rate; @@ -1422,12 +1843,6 @@ struct mrq_clk_response { /** @private */ struct cmd_clk_disable_response clk_disable; struct cmd_clk_is_enabled_response clk_is_enabled; - /** @cond DEPRECATED */ - struct cmd_clk_properties_response clk_properties; - struct cmd_clk_possible_parents_response clk_possible_parents; - struct cmd_clk_num_possible_parents_response clk_num_possible_parents; - struct cmd_clk_get_possible_parent_response clk_get_possible_parent; - /** @endcond DEPRECATED */ struct cmd_clk_get_all_info_response clk_get_all_info; struct cmd_clk_get_max_clk_id_response clk_get_max_clk_id; struct cmd_clk_get_fmax_at_vmin_response clk_get_fmax_at_vmin; @@ -1441,7 +1856,6 @@ struct mrq_clk_response { * @def MRQ_QUERY_ABI * @brief Check if an MRQ is implemented * - * * Platforms: All * * Initiators: Any * * Targets: Any except DMCE * * Request Payload: @ref mrq_query_abi_request @@ -1450,7 +1864,7 @@ struct mrq_clk_response { /** * @ingroup ABI_info - * @brief Request with MRQ_QUERY_ABI + * @brief Request with #MRQ_QUERY_ABI * * Used by #MRQ_QUERY_ABI call to check if MRQ code #mrq is supported * by the recipient. @@ -1468,7 +1882,11 @@ struct mrq_query_abi_request { * successful, not that the MRQ itself is supported! */ struct mrq_query_abi_response { - /** @brief 0 if queried MRQ is supported. Else, -#BPMP_ENODEV */ + /** + * This response field is set to: + * - 0 if queried MRQ is supported, or + * - -#BPMP_ENODEV if queried MRQ is not supported + */ int32_t status; } BPMP_ABI_PACKED; @@ -1476,9 +1894,7 @@ struct mrq_query_abi_response { * * @ingroup MRQ_Codes * @def MRQ_PG - * @brief Control power-gating state of a partition. In contrast to - * MRQ_PG_UPDATE_STATE, operations that change the power partition - * state are NOT reference counted + * @brief Control power-gating state of a partition. * * @cond (bpmp_t194 || bpmp_t186) * @note On T194 and earlier BPMP-FW forcefully turns off some partitions as @@ -1486,9 +1902,8 @@ struct mrq_query_abi_response { * Therefore, it is recommended to power off all domains via MRQ_PG prior to SC7 * entry. * See @ref bpmp_pdomain_ids for further detail. - * @endcond (bpmp_t194 || bpmp_t186) + * @endcond * - * * Platforms: T186, T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_pg_request @@ -1497,6 +1912,10 @@ struct mrq_query_abi_response { * @addtogroup Powergating * @{ */ + +/** + * @brief Sub-command identifiers for #MRQ_PG -command. + */ enum mrq_pg_cmd { /** * @brief Check whether the BPMP driver supports the specified @@ -1512,9 +1931,14 @@ enum mrq_pg_cmd { * possible values for power domains are defined in enum * pg_states * - * mrq_response:err is - * 0: Success - * -#BPMP_EINVAL: Invalid request parameters + * mrq_response:err for this sub-command is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------------------------------------ | + * | 0 | Request was successful. | + * | -#BPMP_EINVAL | Invalid request parameters were provided. | + * | -#BPMP_EACCES | Permission denied or always-off partition was attempted to be turned on. | + * | Any other <0 | Internal error while performing the operation. | */ CMD_PG_SET_STATE = 1, @@ -1523,18 +1947,26 @@ enum mrq_pg_cmd { * possible values for power domains are defined in enum * pg_states * - * mrq_response:err is - * 0: Success - * -#BPMP_EINVAL: Invalid request parameters + * mrq_response:err for this sub-command is defined as: + * + * | Value | Description | + * | -------------- | ---------------------------------------------- | + * | 0 | Request was successful. | + * | -#BPMP_EINVAL | Invalid request parameters were provided. | + * | Any other <0 | Internal error while performing the operation. | */ CMD_PG_GET_STATE = 2, /** * @brief Get the name string of specified power domain id. * - * mrq_response:err is - * 0: Success - * -#BPMP_EINVAL: Invalid request parameters + * mrq_response:err for this sub-command is defined as: + * + * | Value | Description | + * | -------------- | ---------------------------------------------- | + * | 0 | Request was successful. | + * | -#BPMP_EINVAL | Invalid request parameters were provided. | + * | Any other <0 | Internal error while performing the operation. | */ CMD_PG_GET_NAME = 3, @@ -1543,20 +1975,29 @@ enum mrq_pg_cmd { * @brief Get the highest power domain id in the system. Not * all IDs between 0 and max_id are valid IDs. * - * mrq_response:err is - * 0: Success - * -#BPMP_EINVAL: Invalid request parameters + * mrq_response:err for this sub-command is defined as: + * + * | Value | Description | + * | -------------- | ---------------------------------------------- | + * | 0 | Request was successful. | + * | -#BPMP_EINVAL | Invalid request parameters were provided. | + * | Any other <0 | Internal error while performing the operation. | */ CMD_PG_GET_MAX_ID = 4, }; #define MRQ_PG_NAME_MAXLEN 40 +/** + * @brief State value for the cmd_pg_set_state_request::state -field. + */ enum pg_states { /** @brief Power domain is OFF */ PG_STATE_OFF = 0, /** @brief Power domain is ON */ PG_STATE_ON = 1, + + /** @cond bpmp_t186 */ /** * @brief a legacy state where power domain and the clock * associated to the domain are ON. @@ -1564,40 +2005,51 @@ enum pg_states { * deprecated. */ PG_STATE_RUNNING = 2, + /** @endcond */ }; struct cmd_pg_query_abi_request { - /** @ref mrq_pg_cmd */ + /** #MRQ_PG sub-command identifier from @ref mrq_pg_cmd */ uint32_t type; } BPMP_ABI_PACKED; struct cmd_pg_set_state_request { - /** @ref pg_states */ + /** One of the state values from @ref pg_states */ uint32_t state; } BPMP_ABI_PACKED; /** - * @brief Response data to #MRQ_PG sub command #CMD_PG_GET_STATE + * @brief Response payload for the #MRQ_PG sub-command #CMD_PG_GET_STATE */ struct cmd_pg_get_state_response { /** * @brief The state of the power partition that has been - * succesfuly requested by the master earlier using #MRQ_PG + * successfully requested by the master earlier using #MRQ_PG * command #CMD_PG_SET_STATE. * * The state may not reflect the physical state of the power * partition if there are some other masters requesting it to * be enabled. * - * See @ref pg_states for possible values + * See @ref pg_states for possible values. */ uint32_t state; } BPMP_ABI_PACKED; +/** + * @brief Response payload for the #MRQ_PG sub-command #CMD_PG_GET_NAME + */ struct cmd_pg_get_name_response { + /** + * @brief On successful response contains the null-terminated + * friendly name of the requested power-domain. + */ uint8_t name[MRQ_PG_NAME_MAXLEN]; } BPMP_ABI_PACKED; +/** + * @brief Response payload for the #MRQ_PG sub-command #CMD_PG_GET_MAX_ID + */ struct cmd_pg_get_max_id_response { uint32_t max_id; } BPMP_ABI_PACKED; @@ -1606,22 +2058,28 @@ struct cmd_pg_get_max_id_response { * @brief Request with #MRQ_PG * * Used by the sender of an #MRQ_PG message to control power - * partitions. The pg_request is split into several sub-commands. Some - * sub-commands require no additional data. Others have a sub-command - * specific payload + * partitions. The expected payload depends on the sub-command identifier. + * Some sub-commands require no additional data while others have a sub-command + * specific payload: * - * |sub-command |payload | - * |----------------------------|-----------------------| - * |CMD_PG_QUERY_ABI | query_abi | - * |CMD_PG_SET_STATE | set_state | - * |CMD_PG_GET_STATE | - | - * |CMD_PG_GET_NAME | - | - * |CMD_PG_GET_MAX_ID | - | + * |Sub-command |Payload | + * |----------------------------|---------------------------| + * |#CMD_PG_QUERY_ABI | #cmd_pg_query_abi_request | + * |#CMD_PG_SET_STATE | #cmd_pg_set_state_request | + * |#CMD_PG_GET_STATE | - | + * |#CMD_PG_GET_NAME | - | + * |#CMD_PG_GET_MAX_ID | - | * */ struct mrq_pg_request { + /** @brief Sub-command identifier from @ref mrq_pg_cmd. */ uint32_t cmd; + + /** + * @brief Power-domain identifier + */ uint32_t id; + union { struct cmd_pg_query_abi_request query_abi; struct cmd_pg_set_state_request set_state; @@ -1629,19 +2087,18 @@ struct mrq_pg_request { } BPMP_ABI_PACKED; /** - * @brief Response to MRQ_PG + * @brief Response to #MRQ_PG * - * Each sub-command supported by @ref mrq_pg_request may return - * sub-command-specific data. Some do and some do not as indicated in - * the following table + * Some of the #MRQ_PG sub-commands return a sub-command -specific payload + * as specified in the following table: * - * |sub-command |payload | - * |----------------------------|-----------------------| - * |CMD_PG_QUERY_ABI | - | - * |CMD_PG_SET_STATE | - | - * |CMD_PG_GET_STATE | get_state | - * |CMD_PG_GET_NAME | get_name | - * |CMD_PG_GET_MAX_ID | get_max_id | + * |Sub-command |Payload | + * |--------------------|------------------------------| + * |#CMD_PG_QUERY_ABI | - | + * |#CMD_PG_SET_STATE | - | + * |#CMD_PG_GET_STATE | #cmd_pg_get_state_response | + * |#CMD_PG_GET_NAME | #cmd_pg_get_name_response | + * |#CMD_PG_GET_MAX_ID | #cmd_pg_get_max_id_response | */ struct mrq_pg_response { union { @@ -1658,11 +2115,10 @@ struct mrq_pg_response { * @def MRQ_THERMAL * @brief Interact with BPMP thermal framework * - * * Platforms: T186, T194 * * Initiators: Any * * Targets: Any - * * Request Payload: TODO - * * Response Payload: TODO + * * Request Payload: #mrq_thermal_host_to_bpmp_request + * * Response Payload: #mrq_thermal_bpmp_to_host_response * * @addtogroup Thermal * @@ -1686,10 +2142,14 @@ struct mrq_pg_response { * payload of @ref mrq_thermal_bpmp_to_host_request. * @{ */ + +/** + * @brief Sub-command identifiers for Host->BPMP #MRQ_THERMAL -command. + */ enum mrq_thermal_host_to_bpmp_cmd { /** - * @brief Check whether the BPMP driver supports the specified - * request type. + * @brief Check whether BPMP-FW supports the specified + * #MRQ_THERMAL sub-command. * * Host needs to supply request parameters. * @@ -1703,31 +2163,44 @@ enum mrq_thermal_host_to_bpmp_cmd { * * Host needs to supply request parameters. * - * mrq_response::err is - * * 0: Temperature query succeeded. - * * -#BPMP_EINVAL: Invalid request parameters. - * * -#BPMP_ENOENT: No driver registered for thermal zone.. - * * -#BPMP_EFAULT: Problem reading temperature measurement. + * mrq_response::err value for this sub-command is: + * + * | Value | Description | + * | -------------- | ----------------------------------------- | + * | 0 | Temperature query succeeded. | + * | -#BPMP_EINVAL | Invalid request parameters. | + * | -#BPMP_ENOENT | No driver registered for thermal zone. | + * | -#BPMP_EFAULT | Problem reading temperature measurement. | */ CMD_THERMAL_GET_TEMP = 1, /** + * @cond (!bpmp_safe && !bpmp_t264) * @brief Enable or disable and set the lower and upper * thermal limits for a thermal trip point. Each zone has * one trip point. * * Host needs to supply request parameters. Once the * temperature hits a trip point, the BPMP will send a message - * to the CPU having MRQ=MRQ_THERMAL and - * type=CMD_THERMAL_HOST_TRIP_REACHED + * to the CPU having MRQ command identifier equal to #MRQ_THERMAL and + * sub-command identifier equal to #CMD_THERMAL_HOST_TRIP_REACHED. * - * mrq_response::err is - * * 0: Trip successfully set. - * * -#BPMP_EINVAL: Invalid request parameters. - * * -#BPMP_ENOENT: No driver registered for thermal zone. - * * -#BPMP_EFAULT: Problem setting trip point. + * If #CMD_THERMAL_SET_TRIP -sub-command is issued for a + * thermal zone that is currently power gated and unable to + * report temperature, a temperature of -256C is used as + * temperature for evaluation of the trip. + * + * mrq_response::err for this sub-command is defined as: + * + * | Value | Description | + * | --------------- | -------------------------------------- | + * | 0 | Trip successfully set. | + * | -#BPMP_EINVAL | Invalid request parameters. | + * | -#BPMP_ENOENT | No driver registered for thermal zone. | + * | -#BPMP_EFAULT | Problem setting trip point. | */ CMD_THERMAL_SET_TRIP = 2, + /** @endcond */ /** * @brief Get the number of supported thermal zones. @@ -1739,135 +2212,153 @@ enum mrq_thermal_host_to_bpmp_cmd { CMD_THERMAL_GET_NUM_ZONES = 3, /** - * @brief Get the thermtrip of the specified zone. + * @brief Get the thermal trip value of the specified zone. * * Host needs to supply request parameters. * - * mrq_response::err is - * * 0: Valid zone information returned. - * * -#BPMP_EINVAL: Invalid request parameters. - * * -#BPMP_ENOENT: No driver registered for thermal zone. - * * -#BPMP_ERANGE if thermtrip is invalid or disabled. - * * -#BPMP_EFAULT: Problem reading zone information. + * mrq_response::err for this sub-command is defined as: + * + * | Value | Description | + * | --------------- | -------------------------------------- | + * | 0 | Valid zone information returned. | + * | -#BPMP_EINVAL | Invalid request parameters. | + * | -#BPMP_ENOENT | No driver registered for thermal zone. | + * | -#BPMP_ERANGE | Thermal trip is invalid or disabled. | + * | -#BPMP_EFAULT | Problem reading zone information. | */ CMD_THERMAL_GET_THERMTRIP = 4, - /** @brief: number of supported host-to-bpmp commands. May - * increase in future + /** + * @brief Number of supported host-to-bpmp commands. */ CMD_THERMAL_HOST_TO_BPMP_NUM }; +/** + * @brief Sub-command identifiers for BPMP->host #MRQ_THERMAL -command + */ enum mrq_thermal_bpmp_to_host_cmd { /** * @brief Indication that the temperature for a zone has - * exceeded the range indicated in the thermal trip point - * for the zone. + * exceeded the range indicated in the thermal trip point + * for the zone. * - * BPMP needs to supply request parameters. Host only needs to + * BPMP-FW needs to supply request parameters. Host only needs to * acknowledge. */ CMD_THERMAL_HOST_TRIP_REACHED = 100, - /** @brief: number of supported bpmp-to-host commands. May - * increase in future + /** + * @brief: Number of supported bpmp-to-host commands. May + * increase in future. */ CMD_THERMAL_BPMP_TO_HOST_NUM }; -/* - * Host->BPMP request data for request type CMD_THERMAL_QUERY_ABI - * - * zone: Request type for which to check existence. +/** + * Host->BPMP request payload for the #CMD_THERMAL_QUERY_ABI sub-command */ struct cmd_thermal_query_abi_request { + /** + * Request type for which to check whether supported by BPMP-FW. + * + * Valid identifiers are available at #mrq_thermal_host_to_bpmp_cmd + */ uint32_t type; } BPMP_ABI_PACKED; -/* - * Host->BPMP request data for request type CMD_THERMAL_GET_TEMP - * - * zone: Number of thermal zone. +/** + * Host->BPMP request payload for the #CMD_THERMAL_GET_TEMP sub-command */ struct cmd_thermal_get_temp_request { + /** Thermal zone identifier from @ref bpmp_thermal_ids. */ uint32_t zone; } BPMP_ABI_PACKED; -/* - * BPMP->Host reply data for request CMD_THERMAL_GET_TEMP +/** + * BPMP->Host response payload for the #CMD_THERMAL_GET_TEMP sub-command. * - * error: 0 if request succeeded. - * -BPMP_EINVAL if request parameters were invalid. - * -BPMP_ENOENT if no driver was registered for the specified thermal zone. - * -BPMP_EFAULT for other thermal zone driver errors. - * temp: Current temperature in millicelsius. + * mrq_response::err is defined as: + * + * | Value | Description | + * | ------------- | -------------------------------------------------------- | + * | 0 | Request succeeded. | + * | -#BPMP_EINVAL | Request parameters were invalid. | + * | -#BPMP_ENOENT | No driver was registered for the specified thermal zone. | + * | -#BPMP_EFAULT | For other BPMP-FW internal thermal zone driver errors. | */ struct cmd_thermal_get_temp_response { + /** @brief Current temperature in millicelsius. */ int32_t temp; } BPMP_ABI_PACKED; -/* - * Host->BPMP request data for request type CMD_THERMAL_SET_TRIP +/** + * @cond (!bpmp_safe && !bpmp_t264) * - * zone: Number of thermal zone. - * low: Temperature of lower trip point in millicelsius - * high: Temperature of upper trip point in millicelsius - * enabled: 1 to enable trip point, 0 to disable trip point + * Host->BPMP request payload for the #CMD_THERMAL_SET_TRIP sub-command. */ struct cmd_thermal_set_trip_request { + /** @brief Thermal zone identifier from @ref bpmp_thermal_ids. */ uint32_t zone; + /** @brief Temperature of lower trip point in millicelsius */ int32_t low; + /** @brief Temperature of upper trip point in millicelsius */ int32_t high; + /** 1 to enable trip point, 0 to disable trip point */ uint32_t enabled; } BPMP_ABI_PACKED; -/* - * BPMP->Host request data for request type CMD_THERMAL_HOST_TRIP_REACHED - * - * zone: Number of thermal zone where trip point was reached. +/** + * BPMP->Host request payload for the #CMD_THERMAL_HOST_TRIP_REACHED sub-command. */ struct cmd_thermal_host_trip_reached_request { + /** + * @brief ID of the thermal zone where trip point was reached, + * from @ref bpmp_thermal_ids. + */ uint32_t zone; } BPMP_ABI_PACKED; +/** @endcond */ -/* - * BPMP->Host reply data for request type CMD_THERMAL_GET_NUM_ZONES - * - * num: Number of supported thermal zones. The thermal zones are indexed - * starting from zero. +/** + * BPMP->Host response payload for the #CMD_THERMAL_GET_NUM_ZONES sub-command. */ struct cmd_thermal_get_num_zones_response { + /** + * @brief Number of supported thermal zones. + * + * The thermal zones are indexed starting from zero. + */ uint32_t num; } BPMP_ABI_PACKED; -/* - * Host->BPMP request data for request type CMD_THERMAL_GET_THERMTRIP - * - * zone: Number of thermal zone. +/** + * Host->BPMP request payload for the #CMD_THERMAL_GET_THERMTRIP sub-command. */ struct cmd_thermal_get_thermtrip_request { + /** @brief Thermal zone identifier from @ref bpmp_thermal_ids. */ uint32_t zone; } BPMP_ABI_PACKED; -/* - * BPMP->Host reply data for request CMD_THERMAL_GET_THERMTRIP - * - * thermtrip: HW shutdown temperature in millicelsius. +/** + * BPMP->Host response payload for the #CMD_THERMAL_GET_THERMTRIP sub-command. */ struct cmd_thermal_get_thermtrip_response { + /** @brief HW shutdown temperature in millicelsius. */ int32_t thermtrip; } BPMP_ABI_PACKED; -/* - * Host->BPMP request data. +/** + * Host->BPMP #MRQ_THERMAL request payload. * - * Reply type is union mrq_thermal_bpmp_to_host_response. - * - * type: Type of request. Values listed in enum mrq_thermal_type. - * data: Request type specific parameters. + * Response payload type is #mrq_thermal_bpmp_to_host_response. */ struct mrq_thermal_host_to_bpmp_request { + /** + * Request sub-command identifier from @ref mrq_thermal_host_to_bpmp_cmd. + */ uint32_t type; + union { struct cmd_thermal_query_abi_request query_abi; struct cmd_thermal_get_temp_request get_temp; @@ -1876,21 +2367,22 @@ struct mrq_thermal_host_to_bpmp_request { } BPMP_UNION_ANON; } BPMP_ABI_PACKED; -/* - * BPMP->Host request data. - * - * type: Type of request. Values listed in enum mrq_thermal_type. - * data: Request type specific parameters. +/** + * @brief Request payload for the BPMP->Host #MRQ_THERMAL command. */ struct mrq_thermal_bpmp_to_host_request { + /** + * Request sub-command identifier from @ref mrq_thermal_bpmp_to_host_cmd. + */ uint32_t type; + union { struct cmd_thermal_host_trip_reached_request host_trip_reached; } BPMP_UNION_ANON; } BPMP_ABI_PACKED; -/* - * Data in reply to a Host->BPMP request. +/** + * @brief Response payload for the Host->BPMP #MRQ_THERMAL command. */ union mrq_thermal_bpmp_to_host_response { struct cmd_thermal_get_temp_response get_temp; @@ -1900,13 +2392,11 @@ union mrq_thermal_bpmp_to_host_response { /** @} Thermal */ -/** +/** @cond (!bpmp_safe && (bpmp_t234 || bpmp_t238 || bpmp_t264)) * @ingroup MRQ_Codes * @def MRQ_OC_STATUS - * @brief Query over current status + * @brief Query overcurrent status * - * * Platforms: T234 - * @cond bpmp_t234 * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: N/A @@ -1916,33 +2406,224 @@ union mrq_thermal_bpmp_to_host_response { * @{ */ +/** + * @brief Size of the mrq_oc_status_response::throt_en and + * mrq_oc_status_response::event_cnt -arrays. + */ #define OC_STATUS_MAX_SIZE 24U -/* - * @brief Response to #MRQ_OC_STATUS - * - * throt_en: Value for each OC alarm where zero signifies throttle is - * disabled, and non-zero throttle is enabled. - * event_cnt: Total number of OC events for each OC alarm. +/** + * @brief Response payload for the #MRQ_OC_STATUS -command. * * mrq_response::err is 0 if the operation was successful and * -#BPMP_ENODEV otherwise. */ struct mrq_oc_status_response { + /** + * @brief Value for each overcurrent alarm where zero signifies + * throttle is disabled, and non-zero throttle is enabled. + */ uint8_t throt_en[OC_STATUS_MAX_SIZE]; + + /** + * @brief Total number of overcurrent events for each overcurrent alarm. + */ uint32_t event_cnt[OC_STATUS_MAX_SIZE]; } BPMP_ABI_PACKED; /** @} OC_status */ -/** @endcond bpmp_t234 */ +/** @endcond */ + +/** @cond (bpmp_th500 || bpmp_tb500 || bpmp_t238) + * @ingroup MRQ_Codes + * @def MRQ_THROTTLE + * @brief Overcurrent throttling + * + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_throttle_request + * * Response Payload: @ref mrq_throttle_response + * @addtogroup Throttle + * @{ + */ +enum mrq_throttle_cmd { + /** + * @brief Check whether the BPMP-FW supports the specified + * #MRQ_THROTTLE sub-command. + * + * mrq_response::err is 0 if the specified request is + * supported and -#BPMP_ENODEV otherwise. + */ + CMD_THROTTLE_QUERY_ABI = 0, + + /** + * @cond (bpmp_th500 || bpmp_tb500) + * @brief query chipthrot status + * + * mrq_response:err is defined as: + * + * | Value | Description | + * |----------------|--------------------------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENODEV | CMD_THROTTLE_GET_CHIPTHROT_STATUS is not supported by BPMP-FW| + */ + CMD_THROTTLE_GET_CHIPTHROT_STATUS = 1, + /** @endcond */ + + /** + * @cond bpmp_t238 + * @brief program OC throttle configuration + * + * mrq_response:err is defined as: + * + * | Value | Description | + * |----------------|--------------------------------------------------------------| + * | 0 | Success | + * | -#BPMP_EINVAL | ID out of range or alarm for this ID not enabled at boot | + * | -#BPMP_ENODEV | CMD_THROTTLE_SET_OC_CONFIG is not supported by BPMP-FW | + */ + CMD_THROTTLE_SET_OC_CONFIG = 2, + /** @endcond */ +}; /** + * @brief Request payload for #MRQ_THROTTLE sub-command #CMD_THROTTLE_QUERY_ABI + */ +struct cmd_throttle_query_abi_request { + uint32_t cmd; /**< @ref mrq_throttle_cmd */ +} BPMP_ABI_PACKED; + +/** + * @cond bpmp_th500 + * @brief Response payload for #MRQ_THROTTLE sub-command + * #CMD_THROTTLE_GET_CHIPTHROT_STATUS + * + * Bit-mask of all h/w throttling actions that have been engaged since + * last invocation of this command + * Bit 0...11 : HW throttling status of the thermal zones. + * Bit 12...23 : Reserved for future thermal zone events. + * Bit 24...25 : HW throttling status of the Over current Alarms OC1 & OC2. + * Bit 26...31 : Reserved for future Over current alarm events. + * Bit 32...63 : Reserved for future use. + * @endcond + * @cond bpmp_tb500 + * @brief Response payload for #MRQ_THROTTLE sub-command + * #CMD_THROTTLE_GET_CHIPTHROT_STATUS + * + * Bit-mask of all h/w throttling actions that have been engaged since + * last invocation of this command + * Bit 0 : HW throttling status of the TB500C_TJ_MAX thermal zone. + * Bit 1...63 : Reserved for future use. + * @endcond + * @cond (bpmp_th500 || bpmp_tb500) + */ +struct cmd_throttle_get_chipthrot_status_response { + uint64_t status; +} BPMP_ABI_PACKED; +/** @endcond */ + +/** + * @cond bpmp_t238 + * @brief Request payload for #MRQ_THROTTLE sub-command + * #CMD_THROTTLE_SET_OC_CONFIG + * + * Only alarms that have been configured as enabled in BPMP-DTB at boot can + * be reconfigured with this MRQ. + */ +struct cmd_throttle_set_oc_config_request { + /** @brief valid OC alarm ID from @ref bpmp_soctherm_edp_oc_ids */ + uint32_t id; + /** @brief Throttling enable/disable + * + * Set to 1 to enable throttling, or 0 to disable. Other values are + * disallowed. + */ + uint8_t en_throttle; +} BPMP_ABI_PACKED; +/** @endcond */ + +/** + * @brief Request payload for the #MRQ_THROTTLE -command + * + * | Sub-command | Request payload | + * |------------------------------------|----------------------------------| + * | #CMD_THROTTLE_QUERY_ABI | #cmd_throttle_query_abi_request | + * + * @cond bpmp_th500 + * The following additional sub-commands are supported on TH500 platforms: + * | Sub-command | Request payload | + * |------------------------------------|----------------------------------| + * | #CMD_THROTTLE_GET_CHIPTHROT_STATUS | - | + * @endcond + * + * @cond bpmp_tb500 + * The following additional sub-commands are supported on TB500 platforms: + * | Sub-command | Request payload | + * |------------------------------------|----------------------------------| + * | #CMD_THROTTLE_GET_CHIPTHROT_STATUS | - | + * @endcond + * + * @cond bpmp_t238 + * The following additional sub-commands are supported on T238 platforms: + * | Sub-command | Request payload | + * |------------------------------------|-------------------------------------| + * | #CMD_THROTTLE_SET_OC_CONFIG | #cmd_throttle_set_oc_config_request | + * @endcond + */ +struct mrq_throttle_request { + uint32_t cmd; + union { + struct cmd_throttle_query_abi_request throttle_query_abi_req; + /** @cond bpmp_t238 */ + struct cmd_throttle_set_oc_config_request throttle_set_oc_config_req; + /** @endcond */ + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** + * @brief Response payload for the #MRQ_THROTTLE -command. + * + * | Sub-command | Response payload | + * |------------------------------------|--------------------------------------------| + * | #CMD_THROTTLE_QUERY_ABI | - | + * + * @cond bpmp_th500 + * The following additional sub-commands are supported on TH500 platforms: + * | Sub-command | Response payload | + * |------------------------------------|--------------------------------------------| + * | #CMD_THROTTLE_GET_CHIPTHROT_STATUS | #cmd_throttle_get_chipthrot_status_response| + * @endcond + * + * @cond bpmp_tb500 + * The following additional sub-commands are supported on TB500 platforms: + * | Sub-command | Response payload | + * |------------------------------------|--------------------------------------------| + * | #CMD_THROTTLE_GET_CHIPTHROT_STATUS | #cmd_throttle_get_chipthrot_status_response| + * @endcond + * + * @cond bpmp_t238 + * The following additional sub-commands are supported on T238 platforms: + * | Sub-command | Response payload | + * |------------------------------------|--------------------------------------------| + * | #CMD_THROTTLE_SET_OC_CONFIG | - | + * @endcond + */ +struct mrq_throttle_response { + union { + /** @cond (bpmp_th500 || bpmp_tb500) */ + struct cmd_throttle_get_chipthrot_status_response throttle_get_chipthrot_status_resp; + /** @endcond */ + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; +/** @} Throttle */ +/** @endcond */ + + +/** @cond bpmp_t186 * @ingroup MRQ_Codes * @def MRQ_CPU_VHINT * @brief Query CPU voltage hint data * - * * Platforms: T186 - * @cond bpmp_t186 * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_cpu_vhint_request @@ -1995,14 +2676,13 @@ struct cpu_vhint_data { } BPMP_ABI_PACKED; /** @} Vhint */ -/** @endcond bpmp_t186 */ +/** @endcond */ /** * @ingroup MRQ_Codes * @def MRQ_ABI_RATCHET * @brief ABI ratchet value query * - * * Platforms: T186, T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_abi_ratchet_request @@ -2014,7 +2694,7 @@ struct cpu_vhint_data { /** * @brief An ABI compatibility mechanism * - * BPMP_ABI_RATCHET_VALUE may increase for various reasons in a future + * #BPMP_ABI_RATCHET_VALUE may increase for various reasons in a future * revision of this header file. * 1. That future revision deprecates some MRQ * 2. That future revision introduces a breaking change to an existing @@ -2051,11 +2731,11 @@ struct mrq_abi_ratchet_request { * * If #ratchet is less than the requester's #BPMP_ABI_RATCHET_VALUE, * the requster must either interoperate with BPMP according to an ABI - * header version with BPMP_ABI_RATCHET_VALUE = ratchet or cease + * header version with #BPMP_ABI_RATCHET_VALUE = ratchet or cease * communication with BPMP. * * If mrq_response::err is 0 and ratchet is greater than or equal to the - * requester's BPMP_ABI_RATCHET_VALUE, the requester should continue + * requester's #BPMP_ABI_RATCHET_VALUE, the requester should continue * normal operation. */ struct mrq_abi_ratchet_response { @@ -2070,7 +2750,9 @@ struct mrq_abi_ratchet_response { * @def MRQ_EMC_DVFS_LATENCY * @brief Query frequency dependent EMC DVFS latency * - * * Platforms: T186, T194, T234 + * On T264 and onwards, this MRQ service is available only when + * BPMP-FW has valid DRAM timing table passed by earlier boot stages. + * * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: N/A @@ -2094,7 +2776,11 @@ struct emc_dvfs_latency { * @brief Response to #MRQ_EMC_DVFS_LATENCY */ struct mrq_emc_dvfs_latency_response { - /** @brief The number valid entries in #pairs */ + /** + * @brief The number valid entries in #pairs + * + * Valid range is [0, #EMC_DVFS_LATENCY_MAX_SIZE] + */ uint32_t num_pairs; /** @brief EMC DVFS node information */ struct emc_dvfs_latency pairs[EMC_DVFS_LATENCY_MAX_SIZE]; @@ -2102,13 +2788,11 @@ struct mrq_emc_dvfs_latency_response { /** @} EMC */ -/** +/** @cond (bpmp_t234) * @ingroup MRQ_Codes * @def MRQ_EMC_DVFS_EMCHUB * @brief Query EMC HUB frequencies * - * * Platforms: T234 onwards - * @cond (bpmp_t234 || bpmp_t239 || bpmp_th500) * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: N/A @@ -2139,15 +2823,13 @@ struct mrq_emc_dvfs_emchub_response { } BPMP_ABI_PACKED; /** @} EMC */ -/** @endcond (bpmp_t234 || bpmp_t239 || bpmp_th500) */ +/** @endcond */ -/** +/** @cond (bpmp_t234) * @ingroup MRQ_Codes * @def MRQ_EMC_DISP_RFL * @brief Set EMC display RFL handshake mode of operations * - * * Platforms: T234 onwards - * @cond (bpmp_t234 || bpmp_t239 || bpmp_th500) * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_emc_disp_rfl_request @@ -2157,6 +2839,9 @@ struct mrq_emc_dvfs_emchub_response { * @{ */ +/** + * @brief Allowed mode values for the mrq_emc_disp_rfl_request::mode -field. + */ enum mrq_emc_disp_rfl_mode { /** @brief EMC display RFL handshake disabled */ EMC_DISP_RFL_MODE_DISABLED = 0, @@ -2171,65 +2856,75 @@ enum mrq_emc_disp_rfl_mode { * Used by the sender of an #MRQ_EMC_DISP_RFL message to * request the mode of EMC display RFL handshake. * - * mrq_response::err is - * * 0: RFL mode is set successfully - * * -#BPMP_EINVAL: invalid mode requested - * * -#BPMP_ENOSYS: RFL handshake is not supported - * * -#BPMP_EACCES: Permission denied - * * -#BPMP_ENODEV: if disp rfl mrq is not supported by BPMP-FW + * mrq_response::err for this request is defined as: + * + * | Value | Description | + * | -------------- | --------------------------------------------- | + * | 0 | RFL mode is set successfully. | + * | -#BPMP_EINVAL | Invalid mode requested. | + * | -#BPMP_ENOSYS | RFL handshake is not supported. | + * | -#BPMP_EACCES | Permission denied. | + * | -#BPMP_ENODEV | if disp rfl mrq is not supported by BPMP-FW. | */ struct mrq_emc_disp_rfl_request { - /** @brief EMC display RFL mode (@ref mrq_emc_disp_rfl_mode) */ + /** @brief EMC display RFL mode from @ref mrq_emc_disp_rfl_mode */ uint32_t mode; } BPMP_ABI_PACKED; /** @} EMC */ -/** @endcond (bpmp_t234 || bpmp_t239 || bpmp_th500) */ +/** @endcond */ -/** +/** @cond (!bpmp_safe && (bpmp_t234 || bpmp_t238)) * @ingroup MRQ_Codes * @def MRQ_BWMGR - * @brief bwmgr requests + * @brief Bandwidth manager (BWMGR) commands * - * * Platforms: T234 onwards - * @cond (bpmp_t234 || bpmp_t239 || bpmp_th500) * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_bwmgr_request * * Response Payload: @ref mrq_bwmgr_response * * @addtogroup BWMGR - * * @{ */ +/** + * @brief Sub-command identifiers for #MRQ_BWMGR + */ enum mrq_bwmgr_cmd { /** - * @brief Check whether the BPMP driver supports the specified - * request type + * @brief Check whether BPMP-FW supports the specified + * #MRQ_BWMGR sub-command. * - * mrq_response::err is 0 if the specified request is - * supported and -#BPMP_ENODEV otherwise. + * mrq_response::err is defined to be: + * + * | Value | Description + * |----------------|---------------------------- + * | 0 | Specified sub-command is supported. + * | -#BPMP_ENODEV | Specified sub-command is not supported. */ CMD_BWMGR_QUERY_ABI = 0, /** - * @brief Determine dram rate to satisfy iso/niso bw requests + * @brief Determine DRAM rate to satisfy ISO/NISO bandwidth requests * - * mrq_response::err is - * * 0: calc_rate succeeded. - * * -#BPMP_EINVAL: Invalid request parameters. - * * -#BPMP_ENOTSUP: Requested bw is not available. + * mrq_response::err is defined to be: + * + * | Value | Description + * |----------------|---------------------------- + * | 0 | Rate calculation succeeded. + * | -#BPMP_EINVAL | Invalid request parameters. + * | -#BPMP_ENOTSUP | Requested bandwidth is not available. + * | <0 | Any other internal error. */ CMD_BWMGR_CALC_RATE = 1 }; -/* - * request data for request type CMD_BWMGR_QUERY_ABI - * - * type: Request type for which to check existence. +/** + * @brief Request payload for #MRQ_BWMGR sub-command #CMD_BWMGR_QUERY_ABI */ struct cmd_bwmgr_query_abi_request { + /** @brief Sub-command identifier from @ref mrq_bwmgr_cmd. */ uint32_t type; } BPMP_ABI_PACKED; @@ -2237,47 +2932,56 @@ struct cmd_bwmgr_query_abi_request { * @brief Used by @ref cmd_bwmgr_calc_rate_request */ struct iso_req { - /* @brief bwmgr client ID @ref bpmp_bwmgr_ids */ + /** @brief BWMGR client ID from @ref bpmp_bwmgr_ids */ uint32_t id; - /* @brief bw in kBps requested by client */ + /** @brief Bandwidth in kBps requested by client */ uint32_t iso_bw; } BPMP_ABI_PACKED; +/** + * @brief Size of the cmd_bwmgr_calc_rate_request::isobw_reqs -array. + */ #define MAX_ISO_CLIENTS 13U -/* - * request data for request type CMD_BWMGR_CALC_RATE + +/** + * @brief Request payload for #MRQ_BWMGR sub-command #CMD_BWMGR_CALC_RATE */ struct cmd_bwmgr_calc_rate_request { - /* @brief total bw in kBps requested by all niso clients */ + /** @brief Total bandwidth in kBps requested by all NISO clients. */ uint32_t sum_niso_bw; - /* @brief The number of iso clients */ + /** @brief The number of ISO client requests in #isobw_reqs -array */ uint32_t num_iso_clients; - /* @brief iso_req information */ + /** @brief iso_req information */ struct iso_req isobw_reqs[MAX_ISO_CLIENTS]; } BPMP_ABI_PACKED; -/* - * response data for request type CMD_BWMGR_CALC_RATE - * - * iso_rate_min: min dram data clk rate in kHz to satisfy all iso bw reqs - * total_rate_min: min dram data clk rate in kHz to satisfy all bw reqs +/** + * @brief Response payload for #MRQ_BWMGR sub-command #CMD_BWMGR_CALC_RATE */ struct cmd_bwmgr_calc_rate_response { + /** + * @brief Minimum DRAM data clock rate in kHz to satisfy all ISO client + * bandwidth requests. + */ uint32_t iso_rate_min; + + /** + * @brief Minimum DRAM data clock rate in kHz to satisfy all + * bandwidth requests. + */ uint32_t total_rate_min; } BPMP_ABI_PACKED; -/* - * @brief Request with #MRQ_BWMGR - * - * - * |sub-command |payload | - * |----------------------------|------------------------------| - * |CMD_BWMGR_QUERY_ABI | cmd_bwmgr_query_abi_request | - * |CMD_BWMGR_CALC_RATE | cmd_bwmgr_calc_rate_request | +/** + * @brief Request payload for the #MRQ_BWMGR -command. * + * |Sub-command |Payload | + * |----------------------|-----------------------------| + * |#CMD_BWMGR_QUERY_ABI |#cmd_bwmgr_query_abi_request | + * |#CMD_BWMGR_CALC_RATE |#cmd_bwmgr_calc_rate_request | */ struct mrq_bwmgr_request { + /** @brief Sub-command identifier from @ref mrq_bwmgr_cmd. */ uint32_t cmd; union { struct cmd_bwmgr_query_abi_request query_abi; @@ -2285,12 +2989,12 @@ struct mrq_bwmgr_request { } BPMP_UNION_ANON; } BPMP_ABI_PACKED; -/* - * @brief Response to MRQ_BWMGR +/** + * @brief Response payload for the #MRQ_BWMGR -command. * - * |sub-command |payload | - * |----------------------------|------------------------------| - * |CMD_BWMGR_CALC_RATE | cmd_bwmgr_calc_rate_response | + * |Sub-command |Payload | + * |----------------------|------------------------------| + * |#CMD_BWMGR_CALC_RATE |#cmd_bwmgr_calc_rate_response | */ struct mrq_bwmgr_response { union { @@ -2299,15 +3003,13 @@ struct mrq_bwmgr_response { } BPMP_ABI_PACKED; /** @} BWMGR */ -/** @endcond (bpmp_t234 || bpmp_t239 || bpmp_th500) */ +/** @endcond */ -/** +/** @cond (!bpmp_safe && (bpmp_t234 || bpmp_t238 || bpmp_t264)) * @ingroup MRQ_Codes * @def MRQ_BWMGR_INT - * @brief bpmp-integrated bwmgr requests + * @brief BPMP-FW integrated BWMGR requests * - * * Platforms: T234 onwards - * @cond (bpmp_t234 || bpmp_t239 || bpmp_th500) * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_bwmgr_int_request @@ -2317,10 +3019,13 @@ struct mrq_bwmgr_response { * @{ */ +/** + * @brief Sub-command identifiers for #MRQ_BWMGR_INT + */ enum mrq_bwmgr_int_cmd { /** * @brief Check whether the BPMP-FW supports the specified - * request type + * sub-command. * * mrq_response::err is 0 if the specified request is * supported and -#BPMP_ENODEV otherwise. @@ -2328,36 +3033,64 @@ enum mrq_bwmgr_int_cmd { CMD_BWMGR_INT_QUERY_ABI = 1, /** - * @brief Determine and set dram rate to satisfy iso/niso bw request + * @brief Determine and set DRAM rate to satisfy ISO/NISO bandwidth requests. * - * mrq_response::err is - * * 0: request succeeded. - * * -#BPMP_EINVAL: Invalid request parameters. - * set_frequency in @ref cmd_bwmgr_int_calc_and_set_response - * will not be set. - * * -#BPMP_ENOTSUP: Requested bw is not available. - * set_frequency in @ref cmd_bwmgr_int_calc_and_set_response - * will be current dram-clk rate. + * mrq_response::err is defined as: + * + * |Value |Description | + * |-----------------|----------------------------------------------------------------------------------------------------------------| + * |0 |Request succeeded. | + * |-#BPMP_EINVAL |Invalid request parameters, cmd_bwmgr_int_calc_and_set_response::rate is not set. | + * |-#BPMP_ENOTSUP |Requested bandwidth is not available, cmd_bwmgr_int_calc_and_set_response::rate is the current DRAM clock rate. | + * |<0 |Any other internal error. | */ CMD_BWMGR_INT_CALC_AND_SET = 2, /** - * @brief Set a max DRAM frequency for the bandwidth-manager + * @brief Set a max DRAM frequency for the bandwidth manager. * - * mrq_response::err is - * * 0: request succeeded. - * * -#BPMP_ENOTSUP: Requested cap frequency is not possible. + * mrq_response::err is defined as: + * + * |Value |Description | + * |-----------------|------------------------------------------| + * |0 |Request succeeded. | + * |-#BPMP_ENOTSUP |Requested cap frequency is not possible. | + * |<0 |Any other internal error. | */ - CMD_BWMGR_INT_CAP_SET = 3 + CMD_BWMGR_INT_CAP_SET = 3, + + /** + * @brief Obtain the maximum amount of bandwidth currently allocatable + * to the requesting client. + * + * mrq_response::err is defined as: + * + * |Value |Description | + * |-----------------|------------------------------------------| + * |0 |Request succeeded. | + * |-#BPMP_EINVAL |Invalid request parameters. | + * |<0 |Any other internal error. | + */ + CMD_BWMGR_INT_CURR_AVAILABLE_BW = 4, + /** + * @brief Get the last request made by the client. + * + * mrq_response::err is defined as: + * + * |Value |Description | + * |-----------------|------------------------------------------| + * |0 |Request succeeded. | + * |-#BPMP_EINVAL |Invalid request parameters. | + * |<0 |Any other internal error. | + */ + CMD_BWMGR_INT_GET_LAST_REQUEST = 5, }; -/* - * request structure for request type CMD_BWMGR_QUERY_ABI - * - * type: Request type for which to check existence. +/** + * @brief Request payload for #MRQ_BWMGR_INT sub-command #CMD_BWMGR_INT_QUERY_ABI */ struct cmd_bwmgr_int_query_abi_request { - /* @brief request type determined by @ref mrq_bwmgr_int_cmd */ + /** @brief Sub-command identifier from @ref mrq_bwmgr_int_cmd. */ uint32_t type; } BPMP_ABI_PACKED; @@ -2373,87 +3106,168 @@ struct cmd_bwmgr_int_query_abi_request { /** @} bwmgr_int_unit_type */ -/* - * request data for request type CMD_BWMGR_INT_CALC_AND_SET +/** + * @brief Request payload for #MRQ_BWMGR_INT sub-command #CMD_BWMGR_INT_CALC_AND_SET */ struct cmd_bwmgr_int_calc_and_set_request { - /* @brief bwmgr client ID @ref bpmp_bwmgr_ids */ + /** @brief BWGMR client ID from @ref bpmp_bwmgr_ids */ uint32_t client_id; - /* @brief average niso bw usage in kBps requested by client. */ + /** @brief Average NISO bandwidth usage in kBps requested by client. */ uint32_t niso_bw; - /* - * @brief average iso bw usage in kBps requested by client. - * Value is ignored if client is niso. Determined by client_id. + /** + * @brief Average ISO bandwidth usage in kBps requested by client. + * + * Value is ignored if client is NISO as determined by #client_id. */ uint32_t iso_bw; - /* - * @brief memory clock floor requested by client. - * Unit determined by floor_unit. + /** + * @brief Memory clock floor requested by client, unit of the value + * is determined by #floor_unit -field. */ uint32_t mc_floor; - /* - * @brief toggle to determine the unit-type of floor value. - * See @ref bwmgr_int_unit_type definitions for unit-type mappings. + /** + * @brief Value set to determine the unit of the #mc_floor value: + * + * | Value | Unit | + * |-----------------------|----------------------| + * | #BWMGR_INT_UNIT_KBPS | Kilobytes per second | + * | #BWMGR_INT_UNIT_KHZ | Kilohertz | */ uint8_t floor_unit; } BPMP_ABI_PACKED; -struct cmd_bwmgr_int_cap_set_request { - /* @brief requested cap frequency in Hz. */ - uint64_t rate; -} BPMP_ABI_PACKED; - -/* - * response data for request type CMD_BWMGR_CALC_AND_SET +/** + * @brief Response payload for #MRQ_BWMGR_INT sub-command #CMD_BWMGR_INT_CALC_AND_SET */ struct cmd_bwmgr_int_calc_and_set_response { - /* @brief current set memory clock frequency in Hz */ + /** @brief Currently set memory clock frequency in Hz */ uint64_t rate; } BPMP_ABI_PACKED; -/* - * @brief Request with #MRQ_BWMGR_INT +/** + * @brief Request payload for #MRQ_BWMGR_INT sub-command #CMD_BWMGR_INT_CAP_SET + */ +struct cmd_bwmgr_int_cap_set_request { + /** @brief Requested cap frequency in Hz. */ + uint64_t rate; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for #MRQ_BWMGR_INT sub-command #CMD_BWMGR_INT_CURR_AVAILABLE_BW + */ +struct cmd_bwmgr_int_curr_available_bw_request { + /** @brief BWMGR client ID from @ref bpmp_bwmgr_ids */ + uint32_t id; +} BPMP_ABI_PACKED; + +/** + * @brief Response payload for #MRQ_BWMGR_INT sub-command #CMD_BWMGR_INT_CURR_AVAILABLE_BW + */ +struct cmd_bwmgr_int_curr_available_bw_response { + /** @brief Current cap frequency in KHz. */ + uint64_t cap_rate; + /** @brief Currently available bandwidth for the requesting client + * to allocate in KBps. + */ + uint64_t available_bw; +} BPMP_ABI_PACKED; + +struct cmd_bwmgr_int_get_last_request_request { + /** @brief BWMGR client ID from @ref bpmp_bwmgr_ids */ + uint32_t id; + /** + * @brief Value set to determine the unit of the returned mc_floor value: + * + * | Value | Unit | + * |-----------------------|----------------------| + * | #BWMGR_INT_UNIT_KBPS | Kilobytes per second | + * | #BWMGR_INT_UNIT_KHZ | Kilohertz | + */ + uint8_t floor_unit; +} BPMP_ABI_PACKED; + +struct cmd_bwmgr_int_get_last_request_response { + /** @brief BWGMR client ID from @ref bpmp_bwmgr_ids */ + uint32_t client_id; + /** @brief Average NISO bandwidth usage in kBps requested by client. */ + uint32_t niso_bw; + /** + * @brief Average ISO bandwidth usage in kBps requested by client. + */ + uint32_t iso_bw; + /** + * @brief Memory clock floor requested by client, unit of the value + * is determined by #floor_unit -field. + */ + uint32_t mc_floor; + /** + * @brief Value set to determine the unit of the #mc_floor value: + * + * | Value | Unit | + * |-----------------------|----------------------| + * | #BWMGR_INT_UNIT_KBPS | Kilobytes per second | + * | #BWMGR_INT_UNIT_KHZ | Kilohertz | + */ + uint8_t floor_unit; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for the #MRQ_BWMGR_INT -command. * + * |Sub-command |Payload | + * |---------------------------------|-----------------------------------------| + * |#CMD_BWMGR_INT_QUERY_ABI |#cmd_bwmgr_int_query_abi_request | + * |#CMD_BWMGR_INT_CALC_AND_SET |#cmd_bwmgr_int_calc_and_set_request | + * |#CMD_BWMGR_INT_CAP_SET |#cmd_bwmgr_int_cap_set_request | + * |#CMD_BWMGR_INT_GET_LAST_REQUEST |#cmd_bwmgr_int_get_last_request_request | * - * |sub-command |payload | - * |----------------------------|-----------------------------------| - * |CMD_BWMGR_INT_QUERY_ABI | cmd_bwmgr_int_query_abi_request | - * |CMD_BWMGR_INT_CALC_AND_SET | cmd_bwmgr_int_calc_and_set_request| - * |CMD_BWMGR_INT_CAP_SET | cmd_bwmgr_int_cap_set_request | + * The following additional sub-commands are supported on T264 platforms: * + * |Sub-command |Payload | + * |---------------------------------|-----------------------------------------| + * |#CMD_BWMGR_INT_CURR_AVAILABLE_BW |#cmd_bwmgr_int_curr_available_bw_request | */ struct mrq_bwmgr_int_request { + /** @brief Sub-command identifier from @ref mrq_bwmgr_int_cmd. */ uint32_t cmd; union { struct cmd_bwmgr_int_query_abi_request query_abi; struct cmd_bwmgr_int_calc_and_set_request bwmgr_calc_set_req; struct cmd_bwmgr_int_cap_set_request bwmgr_cap_set_req; + struct cmd_bwmgr_int_curr_available_bw_request bwmgr_curr_available_bw_req; + struct cmd_bwmgr_int_get_last_request_request bwmgr_get_last_request_req; } BPMP_UNION_ANON; } BPMP_ABI_PACKED; -/* - * @brief Response to MRQ_BWMGR_INT +/** + * @brief Response payload for the #MRQ_BWMGR_INT -command. * - * |sub-command |payload | - * |----------------------------|---------------------------------------| - * |CMD_BWMGR_INT_CALC_AND_SET | cmd_bwmgr_int_calc_and_set_response | + * |Sub-command |Payload | + * |---------------------------------|------------------------------------------| + * |#CMD_BWMGR_INT_CALC_AND_SET |#cmd_bwmgr_int_calc_and_set_response | + * |#CMD_BWMGR_INT_GET_LAST_REQUEST |#cmd_bwmgr_int_get_last_request_response | + * + * The following additional sub-commands are supported on T264 platforms: + * |Sub-command |Payload | + * |---------------------------------|------------------------------------------| + * |#CMD_BWMGR_INT_CURR_AVAILABLE_BW |#cmd_bwmgr_int_curr_available_bw_response | */ struct mrq_bwmgr_int_response { union { struct cmd_bwmgr_int_calc_and_set_response bwmgr_calc_set_resp; + struct cmd_bwmgr_int_curr_available_bw_response bwmgr_curr_available_bw_resp; + struct cmd_bwmgr_int_get_last_request_response bwmgr_get_last_request_resp; } BPMP_UNION_ANON; } BPMP_ABI_PACKED; /** @} BWMGR_INT */ -/** @endcond (bpmp_t234 || bpmp_t239 || bpmp_th500) */ +/** @endcond */ -/** +/** @cond (!bpmp_safe && (bpmp_t234 || bpmp_t238 || bpmp_t264)) * @ingroup MRQ_Codes * @def MRQ_ISO_CLIENT * @brief ISO client requests * - * * Platforms: T234 onwards - * @cond (bpmp_t234 || bpmp_t239 || bpmp_th500) * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_iso_client_request @@ -2463,148 +3277,178 @@ struct mrq_bwmgr_int_response { * @{ */ +/** + * @brief Sub-command identifiers for #MRQ_ISO_CLIENT. + */ enum mrq_iso_client_cmd { /** - * @brief Check whether the BPMP driver supports the specified - * request type + * @brief Check whether BPMP-FW supports a specified + * #MRQ_ISO_CLIENT sub-command. * * mrq_response::err is 0 if the specified request is * supported and -#BPMP_ENODEV otherwise. */ CMD_ISO_CLIENT_QUERY_ABI = 0, - /* - * @brief check for legal LA for the iso client. Without programming - * LA MC registers, calculate and ensure that legal LA is possible for - * iso bw requested by the ISO client. + /** + * @brief Determine legal LA for ISO client. * - * mrq_response::err is - * * 0: check la succeeded. - * * -#BPMP_EINVAL: Invalid request parameters. - * * -#BPMP_EFAULT: Legal LA is not possible for client requested iso_bw + * Without programming LA MC registers, calculate and ensure that + * a legal LA is possible for the ISO bandwidth requested by the + * ISO client. + * + * mrq_response::err for this sub-command is defined as: + * + * | Value | Description | + * |---------------|--------------------------------------------------------------| + * | 0 | Request successful and legal LA is possible. | + * | -#BPMP_EINVAL | Invalid request parameters. | + * | -#BPMP_EFAULT | Legal LA is not possible for client requested ISO bandwidth. | + * | <0 | Any other internal error. | */ CMD_ISO_CLIENT_CALCULATE_LA = 1, - /* - * @brief set LA for the iso client. Calculate and program the LA/PTSA - * MC registers corresponding to the client making bw request + /** + * @brief Set LA for ISO client. * - * mrq_response::err is - * * 0: set la succeeded. - * * -#BPMP_EINVAL: Invalid request parameters. - * * -#BPMP_EFAULT: Failed to calculate or program MC registers. + * Calculate and program the LA/PTSA MC registers corresponding to the + * ISO client making the bandwidth request. + * + * mrq_response::err for this sub-command is defined as: + * + * | Value | Description | + * |---------------|----------------------------------------------| + * | 0 | Setting LA succeeded. | + * | -#BPMP_EINVAL | Invalid request parameters. | + * | -#BPMP_EFAULT | Failed to calculate or program MC registers. | + * | <0 | Any other internal error. | */ CMD_ISO_CLIENT_SET_LA = 2, - /* - * @brief Get max possible bw for iso client + /** + * @brief Get maximum possible bandwidth for ISO client. * - * mrq_response::err is - * * 0: get_max_bw succeeded. - * * -#BPMP_EINVAL: Invalid request parameters. + * mrq_response::err for this sub-command is defined as: + * + * | Value | Description | + * |---------------|----------------------------------------------| + * | 0 | Operation successful. | + * | -#BPMP_EINVAL | Invalid request parameters. | + * | <0 | Any other internal error. | */ CMD_ISO_CLIENT_GET_MAX_BW = 3 }; -/* - * request data for request type CMD_ISO_CLIENT_QUERY_ABI - * - * type: Request type for which to check existence. +/** + * @brief Request payload for #MRQ_ISO_CLIENT sub-command #CMD_ISO_CLIENT_QUERY_ABI */ struct cmd_iso_client_query_abi_request { + /** + * @brief Sub-command identifier from @ref mrq_iso_client_cmd + * for which to check existence. + */ uint32_t type; } BPMP_ABI_PACKED; -/* - * request data for request type CMD_ISO_CLIENT_CALCULATE_LA - * - * id: client ID in @ref bpmp_bwmgr_ids - * bw: bw requested in kBps by client ID. - * init_bw_floor: initial dram_bw_floor in kBps passed by client ID. - * ISO client will perform mempool allocation and DVFS buffering based - * on this dram_bw_floor. +/** + * @brief Request payload #MRQ_ISO_CLIENT sub-command #CMD_ISO_CLIENT_CALCULATE_LA */ struct cmd_iso_client_calculate_la_request { + /** @brief BWMGR client ID from @ref bpmp_bwmgr_ids */ uint32_t id; + /** @brief Bandwidth requested in kBps for the client specified in #id. */ uint32_t bw; + /** + * @brief Initial DRAM bandwidth floor in kBps for the ISO client specified in #id. + * + * ISO client will perform mempool allocation and DVFS buffering based + * on this value. + */ uint32_t init_bw_floor; } BPMP_ABI_PACKED; -/* - * request data for request type CMD_ISO_CLIENT_SET_LA - * - * id: client ID in @ref bpmp_bwmgr_ids - * bw: bw requested in kBps by client ID. - * final_bw_floor: final dram_bw_floor in kBps. - * Sometimes the initial dram_bw_floor passed by ISO client may need to be - * updated by considering higher dram freq's. This is the final dram_bw_floor - * used to calculate and program MC registers. +/** + * @brief Response payload for #MRQ_ISO_CLIENT sub-command #CMD_ISO_CLIENT_CALCULATE_LA + */ +struct cmd_iso_client_calculate_la_response { + /** @brief Minimum DRAM rate in kHz at which a legal LA is possible */ + uint32_t la_rate_floor; + /** + * Minimum DRAM frequency in kHz required to satisfy this clients + * ISO bandwidth request, assuming all other ISO clients are inactive. + */ + uint32_t iso_client_only_rate; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for #MRQ_ISO_CLIENT sub-command #CMD_ISO_CLIENT_SET_LA */ struct cmd_iso_client_set_la_request { + /** @brief BMWGR client ID from @ref bpmp_bwmgr_ids */ uint32_t id; + /** @brief Bandwidth requested in kBps for the client specified in #id. */ uint32_t bw; + /** + * @brief Final DRAM bandwidth floor in kBps. + * + * Sometimes the initial cmd_iso_client_calculate_la_request::dram_bw_floor + * passed by ISO client may need to be updated by considering higher + * DRAM frequencies. This is the final DRAM bandwidth floor value used + * to calculate and program MC registers. + */ uint32_t final_bw_floor; } BPMP_ABI_PACKED; -/* - * request data for request type CMD_ISO_CLIENT_GET_MAX_BW - * - * id: client ID in @ref bpmp_bwmgr_ids +/** + * @brief Request payload for #MRQ_ISO_CLIENT sub-command #CMD_ISO_CLIENT_GET_MAX_BW */ struct cmd_iso_client_get_max_bw_request { + /** @brief BWMGR client ID from @ref bpmp_bwmgr_ids */ uint32_t id; } BPMP_ABI_PACKED; -/* - * response data for request type CMD_ISO_CLIENT_CALCULATE_LA - * - * la_rate_floor: minimum dram_rate_floor in kHz at which a legal la is possible - * iso_client_only_rate: Minimum dram freq in kHz required to satisfy this clients - * iso bw request, assuming all other iso clients are inactive - */ -struct cmd_iso_client_calculate_la_response { - uint32_t la_rate_floor; - uint32_t iso_client_only_rate; -} BPMP_ABI_PACKED; - /** * @brief Used by @ref cmd_iso_client_get_max_bw_response */ struct iso_max_bw { - /* @brief dram frequency in kHz */ + /** @brief dram frequency in kHz */ uint32_t freq; - /* @brief max possible iso-bw in kBps */ + /** @brief max possible iso-bw in kBps */ uint32_t iso_bw; } BPMP_ABI_PACKED; +/** + * @brief Size of the cmd_iso_client_get_max_bw_response::pairs -array. + */ #define ISO_MAX_BW_MAX_SIZE 14U -/* - * response data for request type CMD_ISO_CLIENT_GET_MAX_BW + +/** + * @brief Response payload for #MRQ_ISO_CLIENT sub-command #CMD_ISO_CLIENT_GET_MAX_BW */ struct cmd_iso_client_get_max_bw_response { - /* @brief The number valid entries in iso_max_bw pairs */ + /** @brief The number valid entries in iso_max_bw pairs */ uint32_t num_pairs; - /* @brief max ISOBW information */ + /** @brief max ISOBW information */ struct iso_max_bw pairs[ISO_MAX_BW_MAX_SIZE]; } BPMP_ABI_PACKED; /** - * @brief Request with #MRQ_ISO_CLIENT + * @brief Request payload for #MRQ_ISO_CLIENT command. * - * Used by the sender of an #MRQ_ISO_CLIENT message. - * - * |sub-command |payload | - * |------------------------------------ |----------------------------------------| - * |CMD_ISO_CLIENT_QUERY_ABI |cmd_iso_client_query_abi_request | - * |CMD_ISO_CLIENT_CALCULATE_LA |cmd_iso_client_calculate_la_request | - * |CMD_ISO_CLIENT_SET_LA |cmd_iso_client_set_la_request | - * |CMD_ISO_CLIENT_GET_MAX_BW |cmd_iso_client_get_max_bw_request | + * Each #MRQ_ISO_CLIENT -command is expected to include a sub-command specific + * payload as defined in table below: * + * |Sub-command |Request payload | + * |-----------------------------|--------------------------------------| + * |#CMD_ISO_CLIENT_QUERY_ABI |#cmd_iso_client_query_abi_request | + * |#CMD_ISO_CLIENT_CALCULATE_LA |#cmd_iso_client_calculate_la_request | + * |#CMD_ISO_CLIENT_SET_LA |#cmd_iso_client_set_la_request | + * |#CMD_ISO_CLIENT_GET_MAX_BW |#cmd_iso_client_get_max_bw_request | */ - struct mrq_iso_client_request { - /* Type of request. Values listed in enum mrq_iso_client_cmd */ + /** @brief Sub-command identifier from @ref mrq_iso_client_cmd. */ uint32_t cmd; + union { struct cmd_iso_client_query_abi_request query_abi; struct cmd_iso_client_calculate_la_request calculate_la_req; @@ -2614,20 +3458,20 @@ struct mrq_iso_client_request { } BPMP_ABI_PACKED; /** - * @brief Response to MRQ_ISO_CLIENT + * @brief Response payload for #MRQ_ISO_CLIENT command. * - * Each sub-command supported by @ref mrq_iso_client_request may return - * sub-command-specific data. Some do and some do not as indicated in - * the following table + * Some of the sub-commands supported by #MRQ_ISO_CLIENT may return + * a sub-command -specific payload in the MRQ response as defined in table + * below: * - * |sub-command |payload | - * |---------------------------- |------------------------------------| - * |CMD_ISO_CLIENT_CALCULATE_LA |cmd_iso_client_calculate_la_response| - * |CMD_ISO_CLIENT_SET_LA |N/A | - * |CMD_ISO_CLIENT_GET_MAX_BW |cmd_iso_client_get_max_bw_response | + * |Sub-command |Response payload | + * |---------------------------- |--------------------------------------| + * |#CMD_ISO_CLIENT_QUERY_ABI |- | + * |#CMD_ISO_CLIENT_CALCULATE_LA |#cmd_iso_client_calculate_la_response | + * |#CMD_ISO_CLIENT_SET_LA |- | + * |#CMD_ISO_CLIENT_GET_MAX_BW |#cmd_iso_client_get_max_bw_response | * */ - struct mrq_iso_client_response { union { struct cmd_iso_client_calculate_la_response calculate_la_resp; @@ -2636,15 +3480,13 @@ struct mrq_iso_client_response { } BPMP_ABI_PACKED; /** @} ISO_CLIENT */ -/** @endcond (bpmp_t234 || bpmp_t239 || bpmp_th500) */ +/** @endcond */ -/** +/** @cond (!bpmp_t186) * @ingroup MRQ_Codes * @def MRQ_CPU_NDIV_LIMITS - * @brief CPU freq. limits in ndiv + * @brief Return CPU cluster NDIV limits * - * * Platforms: T194 onwards - * @cond (bpmp_t194 || bpmp_t234 || bpmp_t239 || bpmp_th500) * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_cpu_ndiv_limits_request @@ -2654,10 +3496,10 @@ struct mrq_iso_client_response { */ /** - * @brief Request for ndiv limits of a cluster + * @brief Request payload for the #MRQ_CPU_NDIV_LIMITS -command */ struct mrq_cpu_ndiv_limits_request { - /** @brief Enum cluster_id */ + /** @brief Logical CPU cluster identifier */ uint32_t cluster_id; } BPMP_ABI_PACKED; @@ -2678,15 +3520,14 @@ struct mrq_cpu_ndiv_limits_response { } BPMP_ABI_PACKED; /** @} CPU */ -/** @endcond (bpmp_t194 || bpmp_t234 || bpmp_t239 || bpmp_th500) */ +/** @endcond */ -/** + +/** @cond (bpmp_t194) * @ingroup MRQ_Codes * @def MRQ_CPU_AUTO_CC3 * @brief Query CPU cluster auto-CC3 configuration * - * * Platforms: T194 - * @cond bpmp_t194 * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_cpu_auto_cc3_request @@ -2702,15 +3543,15 @@ struct mrq_cpu_ndiv_limits_response { */ /** - * @brief Request for auto-CC3 configuration of a cluster + * @brief Request payload for the #MRQ_CPU_AUTO_CC3 -command */ struct mrq_cpu_auto_cc3_request { - /** @brief Enum cluster_id (logical cluster id, known to CCPLEX s/w) */ + /** @brief Logical CPU cluster ID */ uint32_t cluster_id; } BPMP_ABI_PACKED; /** - * @brief Response to #MRQ_CPU_AUTO_CC3 + * @brief Response payload for the #MRQ_CPU_AUTO_CC3 -command */ struct mrq_cpu_auto_cc3_response { /** @@ -2724,9 +3565,9 @@ struct mrq_cpu_auto_cc3_response { } BPMP_ABI_PACKED; /** @} CC3 */ -/** @endcond bpmp_t194 */ +/** @endcond */ -/** +/** @cond (bpmp_t186 || bpmp_t194 || bpmp_t234) * @ingroup MRQ_Codes * @def MRQ_RINGBUF_CONSOLE * @brief A ring buffer debug console for BPMP @@ -2811,7 +3652,9 @@ struct cmd_ringbuf_console_query_abi_resp { */ struct cmd_ringbuf_console_read_req { /** - * @brief Number of bytes requested to be read from the BPMP TX buffer + * @brief Number of bytes requested to be read from the BPMP TX buffer. + * + * Valid range is [0, #MRQ_RINGBUF_CONSOLE_MAX_READ_LEN] */ uint8_t len; } BPMP_ABI_PACKED; @@ -2823,7 +3666,11 @@ struct cmd_ringbuf_console_read_req { struct cmd_ringbuf_console_read_resp { /** @brief The actual data read from the BPMP TX buffer */ uint8_t data[MRQ_RINGBUF_CONSOLE_MAX_READ_LEN]; - /** @brief Number of bytes in cmd_ringbuf_console_read_resp::data */ + /** + * @brief Number of bytes in cmd_ringbuf_console_read_resp::data + * + * Valid range is [0, #MRQ_RINGBUF_CONSOLE_MAX_WRITE_LEN] + */ uint8_t len; } BPMP_ABI_PACKED; @@ -2904,14 +3751,13 @@ union mrq_ringbuf_console_bpmp_to_host_response { } BPMP_ABI_PACKED; /** @} RingbufConsole */ +/** @endcond */ -/** +/** @cond (!bpmp_t186 && !(bpmp_safe && bpmp_t234)) * @ingroup MRQ_Codes * @def MRQ_STRAP * @brief Set a strap value controlled by BPMP * - * * Platforms: T194 onwards - * @cond (bpmp_t194 || bpmp_t234 || bpmp_t239 || bpmp_th500) * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_strap_request @@ -2925,10 +3771,14 @@ union mrq_ringbuf_console_bpmp_to_host_response { * deasserted. * * BPMP owns registers which act as straps to various units. It - * exposes limited control of those straps via #MRQ_STRAP. + * exposes limited control of those registers via #MRQ_STRAP. * * @{ */ + +/** + * @brief Sub-command identifiers for the #MRQ_STRAP -command. + */ enum mrq_strap_cmd { /** @private */ STRAP_RESERVED = 0, @@ -2937,27 +3787,31 @@ enum mrq_strap_cmd { }; /** - * @brief Request with #MRQ_STRAP + * @brief Request payload for the #MRQ_STRAP -command. */ struct mrq_strap_request { - /** @brief @ref mrq_strap_cmd */ + /** @brief Sub-command identifier from @ref mrq_strap_cmd */ uint32_t cmd; - /** @brief Strap ID from @ref Strap_Identifiers */ + /** + * @if (bpmp_t234 || bpmp_th500 || bpmp_t264) + * @brief Strap ID from @ref bpmp_strap_ids + * @else + * @brief Strap ID (undefined) + * @endif + */ uint32_t id; - /** @brief Desired value for strap (if cmd is #STRAP_SET) */ + /** @brief Desired value for strap (if #cmd is #STRAP_SET) */ uint32_t value; } BPMP_ABI_PACKED; /** @} Strap */ -/** @endcond (bpmp_t194 || bpmp_t234 || bpmp_t239 || bpmp_th500) */ +/** @endcond */ -/** +/** @cond (bpmp_t194 || bpmp_t234 || bpmp_th500) * @ingroup MRQ_Codes * @def MRQ_UPHY * @brief Perform a UPHY operation * - * * Platforms: T194 onwards - * @cond (bpmp_t194 || bpmp_t234 || bpmp_t239 || bpmp_th500) * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_uphy_request @@ -2966,90 +3820,215 @@ struct mrq_strap_request { * @addtogroup UPHY * @{ */ -enum { + +/** + * @brief Sub-command identifiers for #MRQ_UPHY. + */ +enum mrq_uphy_cmd { + /** @brief Trigger PCIE lane margining procedure. */ CMD_UPHY_PCIE_LANE_MARGIN_CONTROL = 1, + /** @brief Return PCIE lane margining status. */ CMD_UPHY_PCIE_LANE_MARGIN_STATUS = 2, + /** @brief Initialize PCIE EP PLL controller. */ CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT = 3, + /** @brief Set state of the PCIE RP/EP controller. */ CMD_UPHY_PCIE_CONTROLLER_STATE = 4, + /** @brief Disable PCIE EP PLL controller. */ CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF = 5, + + /** + * @cond bpmp_t238 + * @brief Initialize and enable UPHY display port. + */ CMD_UPHY_DISPLAY_PORT_INIT = 6, + /** @brief Disable UPHY display port. */ CMD_UPHY_DISPLAY_PORT_OFF = 7, + /** @brief Trigger sequence to restore XUSB DYN lanes during SC7 exit. */ CMD_UPHY_XUSB_DYN_LANES_RESTORE = 8, + /** @endcond */ + + /** + * @cond bpmp_th500 + * @brief Perform UPHY Lane EOM scan. + */ + CMD_UPHY_LANE_EOM_SCAN = 9, + /** @brief Config PCIe VDM with a given BDF ID. */ + CMD_UPHY_PCIE_CONFIG_VDM = 10, + /** @endcond */ + CMD_UPHY_MAX, }; +/** + * @brief Request payload for #MRQ_UPHY sub-command #CMD_UPHY_PCIE_LANE_MARGIN_CONTROL. + */ struct cmd_uphy_margin_control_request { - /** @brief Enable margin */ + /** + * @brief Enable margin. + * + * Valid values: + * * Value 0 disables margin, + * * Value 1 enables margin. + */ int32_t en; - /** @brief Clear the number of error and sections */ + /** + * @brief Clear the number of error and sections. + * + * Valid values: + * + * * Value 0: Skip clear, + * * Value 1: Perform clear. + */ int32_t clr; - /** @brief Set x offset (1's complement) for left/right margin type (y should be 0) */ + /** + * @brief Set x offset (1's complement) for left/right margin type (y should be 0). + * + * Valid range is [0, 127] + */ uint32_t x; - /** @brief Set y offset (1's complement) for left/right margin type (x should be 0) */ + /** + * @brief Set y offset (1's complement) for left/right margin type (x should be 0) + * + * Valid range is [0, 63] + */ uint32_t y; - /** @brief Set number of bit blocks for each margin section */ + /** + * @brief Set number of bit blocks for each margin section. + * + * Valid range is [0, 15] + */ uint32_t nblks; } BPMP_ABI_PACKED; +/** + * @brief Response payload for #MRQ_UPHY sub-command #CMD_UPHY_PCIE_LANE_MARGIN_STATUS. + */ struct cmd_uphy_margin_status_response { /** @brief Number of errors observed */ uint32_t status; } BPMP_ABI_PACKED; +/** + * @brief Request payload for #MRQ_UPHY sub-command #CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT. + */ struct cmd_uphy_ep_controller_pll_init_request { - /** @brief EP controller number, T194 valid: 0, 4, 5; T234 valid: 5, 6, 7, 10; T239 valid: 0 */ + /** @brief EP controller number, T194 valid: 0, 4, 5; T234 valid: 5, 6, 7, 10; T238 valid: 0 */ uint8_t ep_controller; } BPMP_ABI_PACKED; +/** + * @brief Request payload for #MRQ_UPHY sub-command #CMD_UPHY_PCIE_CONTROLLER_STATE. + */ struct cmd_uphy_pcie_controller_state_request { - /** @brief PCIE controller number, T194 valid: 0-4; T234 valid: 0-10; T239 valid: 0-3 */ + /** @brief PCIE controller number, T194 valid: 0-4; T234 valid: 0-10; T238 valid: 0-3 */ uint8_t pcie_controller; + /** @brief Nonzero value to enable controller, zero value to disable */ uint8_t enable; } BPMP_ABI_PACKED; +/** + * @brief Request payload for #MRQ_UPHY sub-command #CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF. + */ struct cmd_uphy_ep_controller_pll_off_request { - /** @brief EP controller number, T194 valid: 0, 4, 5; T234 valid: 5, 6, 7, 10; T239 valid: 0 */ + /** @brief EP controller number, T194 valid: 0, 4, 5; T234 valid: 5, 6, 7, 10; T238 valid: 0 */ uint8_t ep_controller; } BPMP_ABI_PACKED; +/** + * @cond bpmp_t238 + * @brief Request payload for #MRQ_UPHY sub-command #CMD_UPHY_DISPLAY_PORT_INIT. + */ struct cmd_uphy_display_port_init_request { - /** @brief DisplayPort link rate, T239 valid: 1620, 2700, 5400, 8100, 2160, 2430, 3240, 4320, 6750 */ + /** @brief DisplayPort link rate, T238 valid: 1620, 2700, 5400, 8100, 2160, 2430, 3240, 4320, 6750 */ uint16_t link_rate; /** @brief 1: lane 0; 2: lane 1; 3: lane 0 and 1 */ uint16_t lanes_bitmap; } BPMP_ABI_PACKED; +/** + * @brief Request payload for #MRQ_UPHY sub-command #CMD_UPHY_XUSB_DYN_LANES_RESTORE. + */ struct cmd_uphy_xusb_dyn_lanes_restore_request { /** @brief 1: lane 0; 2: lane 1; 3: lane 0 and 1 */ uint16_t lanes_bitmap; } BPMP_ABI_PACKED; +/** @endcond */ + +/** + * @cond bpmp_th500 + * @brief Request payload for #MRQ_UPHY sub-command #CMD_UPHY_LANE_EOM_SCAN + */ +struct cmd_uphy_lane_eom_scan_request { + /** @brief UPHY brick number, valid: 0-5 */ + uint32_t brick; + /** @brief UPHY lane number, valid: 0-15 for UPHY0-UPHY3, 0-1 for UPHY4-UPHY5 */ + uint32_t lane; + /** @brief Perform EOM for PCIE GEN5 link: 1 for yes, 0 for no. */ + uint32_t pcie_gen5; +} BPMP_ABI_PACKED; + +/** + * @brief Response payload for #MRQ_UPHY sub-command #CMD_UPHY_LANE_EOM_SCAN + */ +struct cmd_uphy_lane_eom_scan_response { + uint32_t data; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for #MRQ_UPHY sub-command #CMD_UPHY_PCIE_CONFIG_VDM + */ +struct cmd_uphy_pcie_config_vdm_request { + uint8_t pcie_controller; + /** + * @brief Bus/Dev/Func ID to be programmed for VDM. + * + * - bits[15..8] Bus + * - bits[7..3] Dev + * - bit [2..0] Func + */ + uint16_t bdf; +} BPMP_ABI_PACKED; +/** @endcond */ /** * @ingroup UPHY - * @brief Request with #MRQ_UPHY + * @brief Request payload for the #MRQ_UPHY -command. * * Used by the sender of an #MRQ_UPHY message to control UPHY. * The uphy_request is split into several sub-commands. CMD_UPHY_PCIE_LANE_MARGIN_STATUS * requires no additional data. Others have a sub-command specific payload. Below table * shows sub-commands with their corresponding payload data. * - * |sub-command |payload | - * |------------------------------------ |----------------------------------------| - * |CMD_UPHY_PCIE_LANE_MARGIN_CONTROL |uphy_set_margin_control | - * |CMD_UPHY_PCIE_LANE_MARGIN_STATUS | | - * |CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT |cmd_uphy_ep_controller_pll_init_request | - * |CMD_UPHY_PCIE_CONTROLLER_STATE |cmd_uphy_pcie_controller_state_request | - * |CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF |cmd_uphy_ep_controller_pll_off_request | - * |CMD_UPHY_PCIE_DISPLAY_PORT_INIT |cmd_uphy_display_port_init_request | - * |CMD_UPHY_PCIE_DISPLAY_PORT_OFF | | - * |CMD_UPHY_XUSB_DYN_LANES_RESTORE |cmd_uphy_xusb_dyn_lanes_restore_request | + * |sub-command |payload | + * |--------------------------------------|-----------------------------------------| + * |#CMD_UPHY_PCIE_LANE_MARGIN_CONTROL |#cmd_uphy_margin_control_request | + * |#CMD_UPHY_PCIE_LANE_MARGIN_STATUS |- | + * |#CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT |#cmd_uphy_ep_controller_pll_init_request | + * |#CMD_UPHY_PCIE_CONTROLLER_STATE |#cmd_uphy_pcie_controller_state_request | + * |#CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF |#cmd_uphy_ep_controller_pll_off_request | * + * @cond bpmp_t238 + * The following additional sub-commands are supported on T238 platforms: + * + * |sub-command |payload | + * |--------------------------------------|-----------------------------------------| + * |#CMD_UPHY_DISPLAY_PORT_INIT |#cmd_uphy_display_port_init_request | + * |#CMD_UPHY_DISPLAY_PORT_OFF |- | + * |#CMD_UPHY_XUSB_DYN_LANES_RESTORE |#cmd_uphy_xusb_dyn_lanes_restore_request | + * @endcond + * + * @cond bpmp_th500 + * The following additional sub-commands are supported on TH500 platforms: + * |sub-command |payload | + * |--------------------------------------|-----------------------------------------| + * |#CMD_UPHY_LANE_EOM_SCAN |#cmd_uphy_lane_eom_scan_request | + * |#CMD_UPHY_PCIE_CONFIG_VDM |#cmd_uphy_pcie_config_vdm_request | + * @endcond */ - struct mrq_uphy_request { /** @brief Lane number. */ uint16_t lane; - /** @brief Sub-command id. */ + /** @brief Sub-command ID from @ref mrq_uphy_cmd. */ uint16_t cmd; union { @@ -3057,53 +4036,68 @@ struct mrq_uphy_request { struct cmd_uphy_ep_controller_pll_init_request ep_ctrlr_pll_init; struct cmd_uphy_pcie_controller_state_request controller_state; struct cmd_uphy_ep_controller_pll_off_request ep_ctrlr_pll_off; + /** @cond bpmp_t238 */ struct cmd_uphy_display_port_init_request display_port_init; struct cmd_uphy_xusb_dyn_lanes_restore_request xusb_dyn_lanes_restore; + /** @endcond */ + /** @cond bpmp_th500 */ + struct cmd_uphy_lane_eom_scan_request lane_eom_scan; + struct cmd_uphy_pcie_config_vdm_request pcie_vdm; + /** @endcond */ } BPMP_UNION_ANON; } BPMP_ABI_PACKED; /** * @ingroup UPHY - * @brief Response to MRQ_UPHY + * @brief Response payload for the #MRQ_UPHY -command. * * Each sub-command supported by @ref mrq_uphy_request may return * sub-command-specific data. Some do and some do not as indicated in * the following table * - * |sub-command |payload | - * |---------------------------- |------------------------| - * |CMD_UPHY_PCIE_LANE_MARGIN_CONTROL | | - * |CMD_UPHY_PCIE_LANE_MARGIN_STATUS |uphy_get_margin_status | + * |sub-command |payload | + * |-----------------------------------|---------------------------------| + * |#CMD_UPHY_PCIE_LANE_MARGIN_CONTROL |- | + * |#CMD_UPHY_PCIE_LANE_MARGIN_STATUS |#cmd_uphy_margin_status_response | * + * @cond bpmp_th500 + * The following additional sub-commands are supported on TH500 platforms: + * |sub-command |payload | + * |-----------------------------------|---------------------------------| + * |#CMD_UPHY_LANE_EOM_SCAN |#cmd_uphy_lane_eom_scan_response | + * |#CMD_UPHY_PCIE_CONFIG_VDM |- | + * @endcond */ - struct mrq_uphy_response { union { struct cmd_uphy_margin_status_response uphy_get_margin_status; + /** @cond bpmp_th500 */ + struct cmd_uphy_lane_eom_scan_response eom_status; + /** @endcond */ } BPMP_UNION_ANON; } BPMP_ABI_PACKED; /** @} UPHY */ -/** @endcond (bpmp_t194 || bpmp_t234 || bpmp_t239 || bpmp_th500) */ +/** @endcond */ -/** +/** @cond (bpmp_t194 || bpmp_t234 || bpmp_t238 || bpmp_t264) * @ingroup MRQ_Codes * @def MRQ_FMON - * @brief Perform a frequency monitor configuration operations + * @brief Perform a frequency monitor configuration operation * - * * Platforms: T194 onwards - * @cond (bpmp_t194 || bpmp_t234 || bpmp_t239 || bpmp_th500) * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_fmon_request * * Response Payload: @ref mrq_fmon_response - * @endcond (bpmp_t194 || bpmp_t234 || bpmp_t239 || bpmp_th500) * * @addtogroup FMON * @{ - * @cond (bpmp_t194 || bpmp_t234) */ -enum { + +/** + * @brief Sub-command identifiers for #MRQ_FMON + */ +enum mrq_fmon_cmd { /** * @brief Clamp FMON configuration to specified rate. * @@ -3111,62 +4105,80 @@ enum { * clamped, FMON configuration is preserved when clock rate * and/or state is changed. * - * mrq_response::err is 0 if the operation was successful, or @n - * -#BPMP_EACCES: FMON access error @n - * -#BPMP_EBADCMD if subcommand is not supported @n - * -#BPMP_EBADSLT: clamp FMON on cluster with auto-CC3 enabled @n - * -#BPMP_EBUSY: fmon is already clamped at different rate @n - * -#BPMP_EFAULT: self-diagnostic error @n - * -#BPMP_EINVAL: invalid FMON configuration @n - * -#BPMP_EOPNOTSUPP: not in production mode @n - * -#BPMP_ENODEV: invalid clk_id @n - * -#BPMP_ENOENT: no calibration data, uninitialized @n - * -#BPMP_ENOTSUP: avfs config not set @n - * -#BPMP_ENOSYS: clamp FMON on cluster clock w/ no NAFLL @n - * -#BPMP_ETIMEDOUT: operation timed out @n + * mrq_response::err for this sub-command is defined to be: + * + * | Value | Description | + * |-------------------|---------------------------------------------------| + * | 0 | Operation was successful. | + * | -#BPMP_EBADCMD | Subcommand is not supported. | + * | -#BPMP_EACCES | FMON access error. | + * | -#BPMP_EBADSLT | Clamp FMON on cluster with auto-CC3 enabled. | + * | -#BPMP_EBUSY | FMON is already clamped at different rate. | + * | -#BPMP_EFAULT | Self-diagnostic error detected. | + * | -#BPMP_EINVAL | Invalid FMON configuration. | + * | -#BPMP_EOPNOTSUPP | Not in production mode. | + * | -#BPMP_ENODEV | Invalid clock ID in mrq_fmon_request::cmd_and_id. | + * | -#BPMP_ENOENT | No calibration data, uninitialized. | + * | -#BPMP_ENOTSUP | AVFS config not set. | + * | -#BPMP_ENOSYS | Clamp FMON on cluster clock w/ no NAFLL. | + * | -#BPMP_ETIMEDOUT | Operation timed out. | */ CMD_FMON_GEAR_CLAMP = 1, + /** * @brief Release clamped FMON configuration. * * Allow FMON configuration to follow monitored clock rate * and/or state changes. * - * mrq_response::err is 0 if the operation was successful, or @n - * -#BPMP_EBADCMD if subcommand is not supported @n - * -#BPMP_ENODEV: invalid clk_id @n - * -#BPMP_ENOENT: no calibration data, uninitialized @n - * -#BPMP_ENOTSUP: avfs config not set @n - * -#BPMP_EOPNOTSUPP: not in production mode @n + * mrq_response::err for this sub-command is defined to be: + * + * | Value | Description | + * |-------------------|---------------------------------------------------| + * | 0 | Operation was successful. | + * | -#BPMP_EBADCMD | Subcommand is not supported. | + * | -#BPMP_ENODEV | Invalid clock ID in mrq_fmon_request::cmd_and_id. | + * | -#BPMP_ENOENT | No calibration data, uninitialized. | + * | -#BPMP_ENOTSUP | AVFS config not set. | + * | -#BPMP_EOPNOTSUPP | Not in production mode. | */ CMD_FMON_GEAR_FREE = 2, + /** - * @brief Return rate FMON is clamped at, or 0 if FMON is not - * clamped. + * @brief Return rate FMON is clamped at, or 0 if FMON is not clamped. * - * Inherently racy, since clamp state can be changed - * concurrently. Useful for testing. + * Inherently racy, since clamp state can be changed concurrently, + * only provided and useful for testing purposes. * - * mrq_response::err is 0 if the operation was successful, or @n - * -#BPMP_EBADCMD if subcommand is not supported @n - * -#BPMP_ENODEV: invalid clk_id @n - * -#BPMP_ENOENT: no calibration data, uninitialized @n - * -#BPMP_ENOTSUP: avfs config not set @n - * -#BPMP_EOPNOTSUPP: not in production mode @n + * mrq_response::err for this sub-command is defined to be: + * + * | Value | Description | + * |-------------------|---------------------------------------------------| + * | 0 | Operation was successful. | + * | -#BPMP_EBADCMD | Subcommand is not supported. | + * | -#BPMP_ENODEV | Invalid clock ID in mrq_fmon_request::cmd_and_id. | + * | -#BPMP_ENOENT | No calibration data, uninitialized. | + * | -#BPMP_ENOTSUP | AVFS config not set. | + * | -#BPMP_EOPNOTSUPP | Not in production mode. | */ CMD_FMON_GEAR_GET = 3, + /** * @brief Return current status of FMON faults detected by FMON - * h/w or s/w since last invocation of this command. - * Clears fault status. + * HW or SW since last invocation of this sub-command. + * Clears fault status. * - * mrq_response::err is 0 if the operation was successful, or @n - * -#BPMP_EBADCMD if subcommand is not supported @n - * -#BPMP_EINVAL: invalid fault type @n - * -#BPMP_ENODEV: invalid clk_id @n - * -#BPMP_ENOENT: no calibration data, uninitialized @n - * -#BPMP_ENOTSUP: avfs config not set @n - * -#BPMP_EOPNOTSUPP: not in production mode @n + * mrq_response::err for this sub-command is defined to be: + * + * | Value | Description | + * |-------------------|---------------------------------------------------| + * | 0 | Operation was successful. | + * | -#BPMP_EBADCMD | Subcommand is not supported. | + * | -#BPMP_ENODEV | Invalid clock ID in mrq_fmon_request::cmd_and_id. | + * | -#BPMP_ENOENT | No calibration data, uninitialized. | + * | -#BPMP_ENOTSUP | AVFS config not set. | + * | -#BPMP_EOPNOTSUPP | Not in production mode. | + * | -#BPMP_EINVAL | Invalid fault type. | */ CMD_FMON_FAULT_STS_GET = 4, }; @@ -3177,25 +4189,30 @@ enum { */ #define CMD_FMON_NUM 4 -/** @endcond DEPRECATED */ +/** @endcond */ /** - * @defgroup fmon_fault_type FMON fault type + * @defgroup fmon_fault_type FMON fault types * @addtogroup fmon_fault_type * @{ */ -/** @brief All detected FMON faults (h/w or s/w) */ +/** @brief All detected FMON faults (HW or SW) */ #define FMON_FAULT_TYPE_ALL 0U -/** @brief FMON faults detected by h/w */ +/** @brief FMON faults detected by HW */ #define FMON_FAULT_TYPE_HW 1U -/** @brief FMON faults detected by s/w */ +/** @brief FMON faults detected by SW */ #define FMON_FAULT_TYPE_SW 2U /** @} fmon_fault_type */ - +/** + * @brief Request payload for #MRQ_FMON sub-command #CMD_FMON_GEAR_CLAMP. + */ struct cmd_fmon_gear_clamp_request { + /** @brief Unused / reserved */ int32_t unused; + + /** @brief Target rate in Hz. Valid range for the rate is [1, INT64_MAX] */ int64_t rate; } BPMP_ABI_PACKED; @@ -3219,40 +4236,63 @@ struct cmd_fmon_gear_get_request { BPMP_ABI_EMPTY } BPMP_ABI_PACKED; +/** + * @brief Response payload for #MRQ_FMON sub-command #CMD_FMON_GEAR_GET. + */ struct cmd_fmon_gear_get_response { int64_t rate; } BPMP_ABI_PACKED; +/** + * @brief Request payload for #MRQ_FMON sub-command #CMD_FMON_FAULT_STS_GET + */ struct cmd_fmon_fault_sts_get_request { - uint32_t fault_type; /**< @ref fmon_fault_type */ + /** + * @brief Which fault types to return in response: + * + * | Value | Description | + * |----------------------|-----------------------------------------| + * | #FMON_FAULT_TYPE_ALL | Return all detected faults (HW and SW). | + * | #FMON_FAULT_TYPE_HW | Return only HW detected faults. | + * | #FMON_FAULT_TYPE_SW | Return only SW detected faults. | + */ + uint32_t fault_type; } BPMP_ABI_PACKED; +/** + * @brief Response payload for #MRQ_FMON sub-command #CMD_FMON_FAULT_STS_GET + */ struct cmd_fmon_fault_sts_get_response { + /** + * Bitmask of detected HW / SW specific faults, or 0 if no faults have + * been detected since last invocation of #CMD_FMON_FAULT_STS_GET. + */ uint32_t fault_sts; } BPMP_ABI_PACKED; /** * @ingroup FMON - * @brief Request with #MRQ_FMON + * @brief Request payload for the #MRQ_FMON -command. * * Used by the sender of an #MRQ_FMON message to configure clock * frequency monitors. The FMON request is split into several - * sub-commands. Some sub-commands require no additional data. - * Others have a sub-command specific payload + * sub-commands. Sub-command specific payloads are defined in + * the following table: * - * |sub-command |payload | - * |----------------------------|-----------------------| - * |CMD_FMON_GEAR_CLAMP |fmon_gear_clamp | - * |CMD_FMON_GEAR_FREE |- | - * |CMD_FMON_GEAR_GET |- | - * |CMD_FMON_FAULT_STS_GET |fmon_fault_sts_get | + * |Sub-command |Payload | + * |------------------------|--------------------------------| + * |#CMD_FMON_GEAR_CLAMP |#cmd_fmon_gear_clamp_request | + * |#CMD_FMON_GEAR_FREE |- | + * |#CMD_FMON_GEAR_GET |- | + * |#CMD_FMON_FAULT_STS_GET |#cmd_fmon_fault_sts_get_request | * */ struct mrq_fmon_request { - /** @brief Sub-command and clock id concatenated to 32-bit word. - * - bits[31..24] is the sub-cmd. - * - bits[23..0] is monitored clock id used to select target - * FMON + /** + * @brief Sub-command and clock id concatenated to 32-bit word. + * + * - bits[31..24] -> Sub-command identifier from @ref mrq_fmon_cmd. + * - bits[23..0] -> Monitored clock identifier used to select target FMON. */ uint32_t cmd_and_id; @@ -3268,20 +4308,19 @@ struct mrq_fmon_request { /** * @ingroup FMON - * @brief Response to MRQ_FMON + * @brief Response payload for the #MRQ_FMON -command. * * Each sub-command supported by @ref mrq_fmon_request may * return sub-command-specific data as indicated below. * - * |sub-command |payload | - * |----------------------------|------------------------| - * |CMD_FMON_GEAR_CLAMP |- | - * |CMD_FMON_GEAR_FREE |- | - * |CMD_FMON_GEAR_GET |fmon_gear_get | - * |CMD_FMON_FAULT_STS_GET |fmon_fault_sts_get | + * |Sub-command |Payload | + * |------------------------|---------------------------------| + * |#CMD_FMON_GEAR_CLAMP |- | + * |#CMD_FMON_GEAR_FREE |- | + * |#CMD_FMON_GEAR_GET |#cmd_fmon_gear_get_response | + * |#CMD_FMON_FAULT_STS_GET |#cmd_fmon_fault_sts_get_response | * */ - struct mrq_fmon_response { union { /** @private */ @@ -3293,17 +4332,15 @@ struct mrq_fmon_response { } BPMP_UNION_ANON; } BPMP_ABI_PACKED; -/** @endcond (bpmp_t194 || bpmp_t234) */ /** @} FMON */ +/** @endcond */ -/** +/** @cond (bpmp_t194) * @ingroup MRQ_Codes * @def MRQ_EC * @brief Provide status information on faults reported by Error * Collator (EC) to HSM. * - * * Platforms: T194 - * @cond bpmp_t194 * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_ec_request @@ -3311,10 +4348,8 @@ struct mrq_fmon_response { * * @note This MRQ ABI is under construction, and subject to change * - * @endcond bpmp_t194 * @addtogroup EC * @{ - * @cond bpmp_t194 */ enum { /** @@ -3325,7 +4360,7 @@ enum { * -#BPMP_ENODEV if target EC is not owned by BPMP @n * -#BPMP_EACCES if target EC power domain is turned off @n * -#BPMP_EBADCMD if subcommand is not supported - * @endcond DEPRECATED + * @endcond */ CMD_EC_STATUS_GET = 1, /* deprecated */ @@ -3572,7 +4607,7 @@ struct cmd_ec_status_get_response { /** @brief EC error descriptors */ union ec_err_desc error_descs[EC_ERR_STATUS_DESC_MAX_NUM]; } BPMP_ABI_PACKED; -/** @endcond DEPRECATED */ +/** @endcond */ struct cmd_ec_status_ex_get_response { /** @brief Target EC id (the same id received with request). */ @@ -3610,7 +4645,7 @@ struct cmd_ec_status_ex_get_response { * |sub-command |payload | * |----------------------------|-----------------------| * |@ref CMD_EC_STATUS_GET |ec_status_get | - * @endcond DEPRECATED + * @endcond * * |sub-command |payload | * |----------------------------|-----------------------| @@ -3638,7 +4673,7 @@ struct mrq_ec_request { * |sub-command |payload | * |----------------------------|------------------------| * |@ref CMD_EC_STATUS_GET |ec_status_get | - * @endcond DEPRECATED + * @endcond * * |sub-command |payload | * |----------------------------|------------------------| @@ -3652,22 +4687,20 @@ struct mrq_ec_response { * @cond DEPRECATED */ struct cmd_ec_status_get_response ec_status_get; - /** @endcond DEPRECATED */ + /** @endcond */ struct cmd_ec_status_ex_get_response ec_status_ex_get; } BPMP_UNION_ANON; } BPMP_ABI_PACKED; -/** @endcond bpmp_t194 */ /** @} EC */ +/** @endcond */ -/** +/** @cond (bpmp_th500) * @ingroup MRQ_Codes * @def MRQ_TELEMETRY * @brief Get address of memory buffer refreshed with recently sampled * telemetry data * - * * Platforms: TH500 onwards - * @cond bpmp_th500 * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: N/A @@ -3677,14 +4710,17 @@ struct mrq_ec_response { */ /** - * @brief Response to #MRQ_TELEMETRY + * @brief Response payload for the #MRQ_TELEMETRY -command * - * mrq_response::err is - * * 0: Telemetry data is available at returned address - * * -#BPMP_EACCES: MRQ master is not allowed to request buffer refresh - * * -#BPMP_ENAVAIL: Telemetry buffer cannot be refreshed via this MRQ channel - * * -#BPMP_ENOTSUP: Telemetry buffer is not supported by BPMP-FW - * * -#BPMP_ENODEV: Telemetry mrq is not supported by BPMP-FW + * mrq_response::err is defined as: + * + * | Value | Description | + * |-----------------|------------------------------------------------------------| + * | 0 | Telemetry data is available at returned address. | + * | -#BPMP_EACCES | MRQ master is not allowed to request buffer refresh. | + * | -#BPMP_ENAVAIL | Telemetry buffer cannot be refreshed via this MRQ channel. | + * | -#BPMP_ENOTSUP | Telemetry buffer is not supported by BPMP-FW. | + * | -#BPMP_ENODEV | Telemetry MRQ is not supported by BPMP-FW. | */ struct mrq_telemetry_response { /** @brief Physical address of telemetry data buffer */ @@ -3692,15 +4728,112 @@ struct mrq_telemetry_response { } BPMP_ABI_PACKED; /** @} Telemetry */ -/** @endcond bpmp_th500 */ +/** @endcond */ + +/** @cond (bpmp_tb500) + * @ingroup MRQ_Codes + * @def MRQ_TELEMETRY_EX + * @brief Get telemetry configuration settings. + * + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_telemetry_ex_request + * * Response Payload: @ref mrq_telemetry_ex_response + * + * @addtogroup Telemetry_ex + * @{ + */ /** + * @brief Sub-command identifiers for #MRQ_TELEMETRY_EX. + */ +enum mrq_telemetry_ex_cmd { + /** + * @brief Check whether the BPMP-FW supports the specified + * #MRQ_TELEMETRY_EX sub-command. + * + * mrq_response::err is 0 if the specified request is + * supported and -#BPMP_ENODEV otherwise. + */ + CMD_TELEMETRY_EX_QUERY_ABI = 0, + + /** + * @brief Get telemetry buffer base address and data size + * + * mrq_response:err is defined as: + * + * | Value | Description | + * |----------------|------------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_TELEMETRY_EX is not supported by BPMP-FW. | + */ + CMD_TELEMETRY_EX_BASE_SZ_GET = 1, +}; + +/** + * @brief Request data for #MRQ_TELEMETRY_EX sub-command + * #CMD_TELEMETRY_EX_QUERY_ABI + */ +struct cmd_telemetry_ex_query_abi_request { + /** @brief Sub-command identifier from @ref mrq_telemetry_ex_cmd */ + uint32_t cmd_code; +} BPMP_ABI_PACKED; + +/** + * @brief Response payload for #MRQ_TELEMETRY_EX sub-command + * #CMD_TELEMETRY_EX_BASE_SZ_GET + */ +struct cmd_telemetry_ex_base_sz_get_response { + /** + * @brief Physical address of telemetry data buffer + * + * 0 if no buffer is allocated for the initiator sending MRQ. + */ + uint64_t buf_base_addr; + /** @brief Telemetry data size in bytes */ + uint32_t buf_size; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for the #MRQ_TELEMETRY_EX -command + * + * | Sub-command | Request payload | + * |-------------------------------|----------------------------------------| + * | #CMD_TELEMETRY_EX_QUERY_ABI | #cmd_telemetry_ex_query_abi_request | + * | #CMD_TELEMETRY_EX_BASE_SZ_GET | - | + */ +struct mrq_telemetry_ex_request { + /** @brief Sub-command ID from @ref mrq_telemetry_ex_cmd. */ + uint32_t cmd; + union { + struct cmd_telemetry_ex_query_abi_request + telemetry_ex_query_abi_req; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** + * @brief Response payload for the #MRQ_TELEMETRY_EX -command. + * + * | Sub-command | Response payload | + * |-------------------------------|----------------------------------------| + * | #CMD_TELEMETRY_EX_QUERY_ABI | - | + * | #CMD_TELEMETRY_EX_BASE_SZ_GET | #cmd_telemetry_ex_base_sz_get_response | + */ +struct mrq_telemetry_ex_response { + union { + struct cmd_telemetry_ex_base_sz_get_response + telemetry_ex_base_sz_get_rsp; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** @} Telemetry_ex */ +/** @endcond */ + +/** @cond (bpmp_th500 || bpmp_tb500) * @ingroup MRQ_Codes * @def MRQ_PWR_LIMIT * @brief Control power limits. * - * * Platforms: TH500 onwards - * @cond bpmp_th500 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_pwr_limit_request @@ -3709,10 +4842,14 @@ struct mrq_telemetry_response { * @addtogroup Pwrlimit * @{ */ + +/** + * @brief Sub-command identifiers for #MRQ_PWR_LIMIT. + */ enum mrq_pwr_limit_cmd { /** * @brief Check whether the BPMP-FW supports the specified - * command + * #MRQ_PWR_LIMIT sub-command. * * mrq_response::err is 0 if the specified request is * supported and -#BPMP_ENODEV otherwise. @@ -3722,31 +4859,43 @@ enum mrq_pwr_limit_cmd { /** * @brief Set power limit * - * mrq_response:err is - * * 0: Success - * * -#BPMP_ENODEV: Pwr limit mrq is not supported by BPMP-FW - * * -#BPMP_ENAVAIL: Invalid request parameters - * * -#BPMP_EACCES: Request is not accepted + * mrq_response:err is defined as: + * + * | Value | Description | + * |----------------|---------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_PWR_LIMIT is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + * | -#BPMP_EACCES | Request is not accepted. | */ CMD_PWR_LIMIT_SET = 1, /** * @brief Get power limit setting * - * mrq_response:err is - * * 0: Success - * * -#BPMP_ENODEV: Pwr limit mrq is not supported by BPMP-FW - * * -#BPMP_ENAVAIL: Invalid request parameters + * mrq_response:err is defined as: + * + * | Value | Description | + * |----------------|---------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_PWR_LIMIT is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | */ CMD_PWR_LIMIT_GET = 2, /** - * @brief Get current power cap + * @brief Get current aggregated power cap * - * mrq_response:err is - * * 0: Success - * * -#BPMP_ENODEV: Pwr limit mrq is not supported by BPMP-FW - * * -#BPMP_ENAVAIL: Invalid request parameters + * Get currently applied power cap for the specified limit id + * aggregated across all limit sources and types. + * + * mrq_response:err is defined as: + * + * | Value | Description | + * |----------------|---------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_PWR_LIMIT is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | */ CMD_PWR_LIMIT_CURR_CAP = 3, }; @@ -3761,7 +4910,7 @@ enum mrq_pwr_limit_cmd { #define PWR_LIMIT_TYPE_BOUND_MAX 1U /** @brief Limit value specifies minimum possible target cap */ #define PWR_LIMIT_TYPE_BOUND_MIN 2U -/** @brief Number of limit types supported by mrq interface */ +/** @brief Number of limit types supported by #MRQ_PWR_LIMIT command */ #define PWR_LIMIT_TYPE_NUM 3U /** @} bpmp_pwr_limit_type */ @@ -3770,7 +4919,8 @@ enum mrq_pwr_limit_cmd { * @brief Request data for #MRQ_PWR_LIMIT command CMD_PWR_LIMIT_QUERY_ABI */ struct cmd_pwr_limit_query_abi_request { - uint32_t cmd_code; /**< @ref mrq_pwr_limit_cmd */ + /** @brief Sub-command identifier from @ref mrq_pwr_limit_cmd */ + uint32_t cmd_code; } BPMP_ABI_PACKED; /** @@ -3782,56 +4932,66 @@ struct cmd_pwr_limit_query_abi_request { * is ignored by the arbitration (i.e., indicates "no limit set"). */ struct cmd_pwr_limit_set_request { - uint32_t limit_id; /**< @ref bpmp_pwr_limit_id */ + /** @brief Power limit identifier from @ref bpmp_pwr_limit_id */ + uint32_t limit_id; + /** @brief Power limit source identifier from @ref bpmp_pwr_limit_src */ uint32_t limit_src; /**< @ref bpmp_pwr_limit_src */ - uint32_t limit_type; /**< @ref bpmp_pwr_limit_type */ + /** @brief Power limit type from @ref bpmp_pwr_limit_type */ + uint32_t limit_type; + /** @brief New power limit value */ uint32_t limit_setting; } BPMP_ABI_PACKED; /** - * @brief Request data for #MRQ_PWR_LIMIT command CMD_PWR_LIMIT_GET + * @brief Request payload for #MRQ_PWR_LIMIT sub-command #CMD_PWR_LIMIT_GET * * Get previously set from specified source specified limit value of specified * type. */ struct cmd_pwr_limit_get_request { - uint32_t limit_id; /**< @ref bpmp_pwr_limit_id */ + /** @brief Power limit identifier from @ref bpmp_pwr_limit_id */ + uint32_t limit_id; + /** @brief Power limit source identifier from @ref bpmp_pwr_limit_src */ uint32_t limit_src; /**< @ref bpmp_pwr_limit_src */ - uint32_t limit_type; /**< @ref bpmp_pwr_limit_type */ + /** @brief Power limit type from @ref bpmp_pwr_limit_type */ + uint32_t limit_type; } BPMP_ABI_PACKED; /** - * @brief Response data for #MRQ_PWR_LIMIT command CMD_PWR_LIMIT_GET + * @brief Response payload for #MRQ_PWR_LIMIT sub-command #CMD_PWR_LIMIT_GET */ struct cmd_pwr_limit_get_response { + /** @brief Power limit value */ uint32_t limit_setting; } BPMP_ABI_PACKED; /** - * @brief Request data for #MRQ_PWR_LIMIT command CMD_PWR_LIMIT_CURR_CAP + * @brief Request payload for #MRQ_PWR_LIMIT sub-command #CMD_PWR_LIMIT_CURR_CAP * * For specified limit get current power cap aggregated from all sources. */ struct cmd_pwr_limit_curr_cap_request { - uint32_t limit_id; /**< @ref bpmp_pwr_limit_id */ + /** @brief Power limit identifier from @ref bpmp_pwr_limit_id */ + uint32_t limit_id; } BPMP_ABI_PACKED; /** - * @brief Response data for #MRQ_PWR_LIMIT command CMD_PWR_LIMIT_CURR_CAP + * @brief Response payload for #MRQ_PWR_LIMIT sub-command #CMD_PWR_LIMIT_CURR_CAP */ struct cmd_pwr_limit_curr_cap_response { + /** @brief Current power cap value */ uint32_t curr_cap; } BPMP_ABI_PACKED; /** - * @brief Request with #MRQ_PWR_LIMIT + * @brief Request payload for the #MRQ_PWR_LIMIT -command * - * |sub-command |payload | - * |----------------------------|---------------------------------| - * |CMD_PWR_LIMIT_QUERY_ABI | cmd_pwr_limit_query_abi_request | - * |CMD_PWR_LIMIT_SET | cmd_pwr_limit_set_request | - * |CMD_PWR_LIMIT_GET | cmd_pwr_limit_get_request | - * |CMD_PWR_LIMIT_CURR_CAP | cmd_pwr_limit_curr_cap_request | + * | Sub-command | Request payload | + * |--------------------------|----------------------------------| + * | #CMD_PWR_LIMIT_QUERY_ABI | #cmd_pwr_limit_query_abi_request | + * | #CMD_PWR_LIMIT_SET | #cmd_pwr_limit_set_request | + * | #CMD_PWR_LIMIT_GET | #cmd_pwr_limit_get_request | + * | #CMD_PWR_LIMIT_CURR_CAP | #cmd_pwr_limit_curr_cap_request | */ struct mrq_pwr_limit_request { uint32_t cmd; @@ -3844,14 +5004,14 @@ struct mrq_pwr_limit_request { } BPMP_ABI_PACKED; /** - * @brief Response to MRQ_PWR_LIMIT + * @brief Response payload for the #MRQ_PWR_LIMIT -command. * - * |sub-command |payload | - * |----------------------------|---------------------------------| - * |CMD_PWR_LIMIT_QUERY_ABI | - | - * |CMD_PWR_LIMIT_SET | - | - * |CMD_PWR_LIMIT_GET | cmd_pwr_limit_get_response | - * |CMD_PWR_LIMIT_CURR_CAP | cmd_pwr_limit_curr_cap_response | + * | Sub-command | Response payload | + * |--------------------------|----------------------------------| + * | #CMD_PWR_LIMIT_QUERY_ABI | - | + * | #CMD_PWR_LIMIT_SET | - | + * | #CMD_PWR_LIMIT_GET | #cmd_pwr_limit_get_response | + * | #CMD_PWR_LIMIT_CURR_CAP | #cmd_pwr_limit_curr_cap_response | */ struct mrq_pwr_limit_response { union { @@ -3860,17 +5020,395 @@ struct mrq_pwr_limit_response { } BPMP_UNION_ANON; } BPMP_ABI_PACKED; -/** @} PwrLimit */ -/** @endcond bpmp_th500 */ +/** @} Pwrlimit */ +/** @endcond */ + +/** @cond (bpmp_th500) + * @ingroup MRQ_Codes + * @def MRQ_PWRMODEL + * @brief Retrieve power evaluted by SoC power model. + * + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_pwrmodel_request + * * Response Payload: @ref mrq_pwrmodel_response + * + * @addtogroup Pwrmodel + * @{ + */ + /** + * @brief Sub-command identifiers for #MRQ_PWRMODEL. + */ +enum mrq_pwrmodel_cmd { + /** + * @brief Check whether the BPMP-FW supports the specified + * #MRQ_PWRMODEL sub-command. + * + * mrq_response::err is 0 if the specified request is + * supported and -#BPMP_ENODEV otherwise. + */ + CMD_PWRMODEL_QUERY_ABI = 0, + + /** + * @brief Get power model output power + * + * mrq_response:err is defined as: + * + * | Value | Description | + * |----------------|---------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_PWRMODEL is not supported by BPMP-FW. | + * | -#BPMP_ERANGE | Power model calculation overflow. | + */ + CMD_PWRMODEL_PWR_GET = 1, +}; + +/** + * @brief Request data for #MRQ_PWRMODEL sub-command #CMD_PWRMODEL_QUERY_ABI + */ +struct cmd_pwrmodel_query_abi_request { + /** @brief Sub-command identifier from @ref mrq_pwrmodel_cmd */ + uint32_t cmd_code; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for #MRQ_PWRMODEL sub-command #CMD_PWRMODEL_PWR_GET + * + * Rerieve power evaluated by power model for specified work-load factor, + * temperature, and cpu iso frequency for all cores. + */ +struct cmd_pwrmodel_pwr_get_request { + /** @brief Unitless work load factor to evaluate power model at */ + uint32_t work_load_factor; + /** @brief CPU frequency in kHz to evaluate power model at */ + uint32_t cpu_frequency; + /** @brief Temperature in mC to evaluate power model at */ + int32_t temperature; +} BPMP_ABI_PACKED; + +/** + * @brief Response payload for #MRQ_PWRMODEL sub-command #CMD_PWRMODEL_PWR_GET + */ +struct cmd_pwrmodel_pwr_get_response { + /** @brief Power model output in mW */ + uint32_t power; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for the #MRQ_PWRMODEL -command + * + * | Sub-command | Request payload | + * |--------------------------|----------------------------------| + * | #CMD_PWRMODEL_QUERY_ABI | #cmd_pwrmodel_query_abi_request | + * | #CMD_PWRMODEL_PWR_GET | #cmd_pwrmodel_pwr_get_request | + */ +struct mrq_pwrmodel_request { + uint32_t cmd; + union { + struct cmd_pwrmodel_query_abi_request pwrmodel_query_abi_req; + struct cmd_pwrmodel_pwr_get_request pwrmodel_pwr_get_req; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** + * @brief Response payload for the #MRQ_PWRMODEL -command. + * + * | Sub-command | Response payload | + * |--------------------------|----------------------------------| + * | #CMD_PWRMODEL_QUERY_ABI | - | + * | #CMD_PWRMODEL_PWR_GET | #cmd_pwrmodel_pwr_get_response | + */ +struct mrq_pwrmodel_response { + union { + struct cmd_pwrmodel_pwr_get_response pwrmodel_pwr_get_rsp; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** @} Pwrmodel */ +/** @endcond */ + + +/** @cond (bpmp_th500) + * @ingroup MRQ_Codes + * @def MRQ_PWR_CNTRL + * @brief Configure power controllers. + * + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_pwr_cntrl_request + * * Response Payload: @ref mrq_pwr_cntrl_response + * + * @addtogroup Pwrcntrl + * @{ + */ + +/** + * @brief Sub-command identifiers for #MRQ_PWR_CNTRL. + */ +enum mrq_pwr_cntrl_cmd { + /** + * @brief Check whether the BPMP-FW supports the specified + * #MRQ_PWR_CNTRL sub-command. + * + * mrq_response::err is 0 if the specified request is + * supported and -#BPMP_ENODEV otherwise. + */ + CMD_PWR_CNTRL_QUERY_ABI = 0, + + /** + * @brief Switch power controller to/out of bypass mode + * + * mrq_response:err is defined as: + * + * | Value | Description | + * |----------------|---------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_PWR_CNTRL is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + * | -#BPMP_ENOTSUP | Bypass mode is not supported. | + */ + CMD_PWR_CNTRL_BYPASS_SET = 1, + + /** + * @brief Get power controller bypass mode status + * + * mrq_response:err is defined as: + * + * | Value | Description | + * |----------------|---------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_PWR_CNTRL is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_PWR_CNTRL_BYPASS_GET = 2, +}; + +/** + * @brief Request data for #MRQ_PWR_CNTRL sub-command #CMD_PWR_CNTRL_QUERY_ABI + */ +struct cmd_pwr_cntrl_query_abi_request { + /** @brief Sub-command identifier from @ref mrq_pwr_cntrl_cmd */ + uint32_t cmd_code; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_PWR_CNTRL sub-command #CMD_PWR_CNTRL_BYPASS_SET + * + * Switch specified power controller to / out of bypass mode provided such + * mode is supported by the controller. + */ +struct cmd_pwr_cntrl_bypass_set_request { + /** @brief Power controller identifier from @ref bpmp_pwr_cntrl_id */ + uint32_t cntrl_id; + /** + * @brief Bypass setting. + * + * Valid values: + * + * * 1 to enter bypass mode, + * * 0 to exit bypass mode. + */ + uint32_t bypass_setting; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_PWR_CNTRL sub-command #CMD_PWR_CNTRL_BYPASS_GET + * + * Get bypass mode status of the specified power controller. + */ +struct cmd_pwr_cntrl_bypass_get_request { + /** @brief Power controller identifier from @ref bpmp_pwr_cntrl_id */ + uint32_t cntrl_id; +} BPMP_ABI_PACKED; + +/** + * @brief Response data for #MRQ_PWR_CNTRL sub-command #CMD_PWR_CNTRL_BYPASS_GET + * + * Get current bypass mode status if such mode is supported by the controller. + * Otherwise, return "out of bypass" . + */ +struct cmd_pwr_cntrl_bypass_get_response { + /** + * @brief Bypass mode status: 1 controller is in bypass, + * 0 controller is out of bypass. + */ + uint32_t bypass_status; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for the #MRQ_PWR_CNTRL -command + * + * | Sub-command | Request payload | + * |---------------------------|-----------------------------------| + * | #CMD_PWR_CNTRL_QUERY_ABI | #cmd_pwr_cntrl_query_abi_request | + * | #CMD_PWR_CNTRL_BYPASS_SET | #cmd_pwr_cntrl_bypass_set_request | + * | #CMD_PWR_CNTRL_BYPASS_GET | #cmd_pwr_cntrl_bypass_get_request | + */ +struct mrq_pwr_cntrl_request { + uint32_t cmd; + union { + struct cmd_pwr_cntrl_query_abi_request pwr_cntrl_query_abi_req; + struct cmd_pwr_cntrl_bypass_set_request pwr_cntrl_bypass_set_req; + struct cmd_pwr_cntrl_bypass_get_request pwr_cntrl_bypass_get_req; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** + * @brief Response payload for the #MRQ_PWR_CNTRL -command. + * + * | Sub-command | Response payload | + * |---------------------------|-----------------------------------| + * | #CMD_PWR_CNTRL_QUERY_ABI | - | + * | #CMD_PWR_CNTRL_BYPASS_SET | - | + * | #CMD_PWR_CNTRL_BYPASS_GET | #cmd_pwr_cntrl_bypass_get_response| + */ +struct mrq_pwr_cntrl_response { + union { + struct cmd_pwr_cntrl_bypass_get_response pwr_cntrl_bypass_get_rsp; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** @} Pwrcntrl */ +/** @endcond */ + + +/** @cond (bpmp_t264) + * @ingroup MRQ_Codes + * @def MRQ_SLC + * @brief Configure SLC state. + * + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_slc_request + * * Response Payload: @ref mrq_slc_response + * + * @addtogroup Slc + * @{ + */ + +/** + * @brief Sub-command identifiers for #MRQ_SLC. + */ +enum mrq_slc_cmd { + /** + * @brief Check whether the BPMP-FW supports the specified + * #MRQ_SLC sub-command. + * + * mrq_response::err is 0 if the specified request is + * supported and -#BPMP_ENODEV otherwise. + */ + CMD_SLC_QUERY_ABI = 0, + + /** + * @brief Switch SLC to/out of bypass mode + * + * mrq_response:err is defined as: + * + * | Value | Description | + * |----------------|---------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_SLC is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + * | -#BPMP_ENOTSUP | Bypass mode is not supported. | + */ + CMD_SLC_BYPASS_SET = 1, + + /** + * @brief Get SLC bypass mode status + * + * mrq_response:err is defined as: + * + * | Value | Description | + * |----------------|---------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_SLC is not supported by BPMP-FW. | + */ + CMD_SLC_BYPASS_GET = 2, +}; + +/** + * @brief Request data for #MRQ_SLC sub-command #CMD_SLC_QUERY_ABI + */ +struct cmd_slc_query_abi_request { + /** @brief Sub-command identifier from @ref mrq_slc_cmd */ + uint32_t cmd_code; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_SLC sub-command #CMD_SLC_BYPASS_SET + * + * Switch SLC to / out of bypass mode provided such + * mode is supported by the SLC. + */ +struct cmd_slc_bypass_set_request { + /** + * @brief Bypass setting. + * + * Valid values: + * + * * 1 to enter bypass mode, + * * 0 to exit bypass mode. + */ + uint32_t bypass_setting; +} BPMP_ABI_PACKED; + +/** + * @brief Response data for #MRQ_SLC sub-command #CMD_SLC_BYPASS_GET + * + * Get current bypass mode status if such mode is supported by the SLC. + * Otherwise, return "out of bypass" . + */ +struct cmd_slc_bypass_get_response { + /** + * @brief Bypass mode status: 1 SLC is in bypass, + * 0 SLC is out of bypass. + */ + uint32_t bypass_status; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for the #MRQ_SLC -command + * + * | Sub-command | Request payload | + * |---------------------------|-----------------------------------| + * | #CMD_SLC_QUERY_ABI | #cmd_slc_query_abi_request | + * | #CMD_SLC_BYPASS_SET | #cmd_slc_bypass_set_request | + * | #CMD_SLC_BYPASS_GET | - | + */ +struct mrq_slc_request { + uint32_t cmd; + union { + struct cmd_slc_query_abi_request slc_query_abi_req; + struct cmd_slc_bypass_set_request slc_bypass_set_req; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** + * @brief Response payload for the #MRQ_SLC -command. + * + * | Sub-command | Response payload | + * |---------------------------|-----------------------------------| + * | #CMD_SLC_QUERY_ABI | - | + * | #CMD_SLC_BYPASS_SET | - | + * | #CMD_SLC_BYPASS_GET | #cmd_slc_bypass_get_response | + */ +struct mrq_slc_response { + union { + struct cmd_slc_bypass_get_response slc_bypass_get_rsp; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** @} Slc */ +/** @endcond */ + +/** @cond (bpmp_th500) * @ingroup MRQ_Codes * @def MRQ_GEARS * @brief Get thresholds for NDIV offset switching * - * * Platforms: TH500 onwards - * @cond bpmp_th500 * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: N/A @@ -3905,11 +5443,1248 @@ struct mrq_pwr_limit_response { */ struct mrq_gears_response { /** @brief number of online CPUs for each gear */ - uint32_t ncpu[16]; + uint32_t ncpu[8]; + /** @brief ndiv offset for each gear */ + uint32_t ndiv_offset[8]; + /** @brief voltage below which gears are disabled */ + uint32_t uv_threshold; } BPMP_ABI_PACKED; /** @} Gears */ -/** @endcond bpmp_th500 */ +/** @endcond */ + + +/** + * @ingroup MRQ_Codes + * @def MRQ_SHUTDOWN + * @brief System shutdown + * + * This message indicates system shutdown or reboot request. BPMP will + * initiate system shutdown/reboot after receiving this message, it + * may include turning off some rails in sequence and programming + * PMIC. + * + * * Initiators: CPU_S, MCE + * * Targets: BPMP + * * Request Payload: @ref mrq_shutdown_request + * * Response Payload: N/A + * @addtogroup Shutdown + * @{ + */ + +/** + * @brief Request with #MRQ_SHUTDOWN + */ +struct mrq_shutdown_request { + /** + * @brief Shutdown state ID + * + * Legal values: + * * 0 - Power off + * * 1 - Reboot + * @cond bpmp_t264 + * * 2 - Suspend + * @endcond + */ + uint32_t state; +} BPMP_ABI_PACKED; + +/** @} Shutdown */ + +/** @cond (bpmp_th500 || bpmp_tb500) + * @defgroup bpmp_c2c_status C2C link status + * @addtogroup bpmp_c2c_status + * @{ + */ +/** @brief initial status code */ +#define BPMP_C2C_STATUS_INIT_NOT_STARTED 0 +/** @brief Invalid speedo code */ +#define BPMP_C2C_STATUS_C2C_INVALID_SPEEDO_CODE 7 +/** @brief Invalid frequency */ +#define BPMP_C2C_STATUS_C2C_INVALID_FREQ 8 +/** @brief Invalid link */ +#define BPMP_C2C_STATUS_C2C_INVALID_LINK 9 +/** @brief refpll lock polling times out - partition 0 */ +#define BPMP_C2C_STATUS_C2C0_REFPLL_FAIL 10 +/** @brief refpll lock polling times out - partition 1 */ +#define BPMP_C2C_STATUS_C2C1_REFPLL_FAIL 11 +/** @brief PLL cal times out - partition 0 */ +#define BPMP_C2C_STATUS_C2C0_PLLCAL_FAIL 12 +/** @brief PLL cal times out - partition 1 */ +#define BPMP_C2C_STATUS_C2C1_PLLCAL_FAIL 13 +/** @brief clock detection times out - partition 0 */ +#define BPMP_C2C_STATUS_C2C0_CLKDET_FAIL 14 +/** @brief clock detection times out - partition 1 */ +#define BPMP_C2C_STATUS_C2C1_CLKDET_FAIL 15 +/** @brief Final trainings fail partition 0 */ +#define BPMP_C2C_STATUS_C2C0_TR_FAIL 16 +/** @brief Final trainings fail partition 1 */ +#define BPMP_C2C_STATUS_C2C1_TR_FAIL 17 +/** @brief C2C FW init done */ +#define NV_GFW_GLOBAL_DEVINIT_C2C_STATUS_C2C_FW_INIT_DONE 20 +/** @brief C2C FW init failed partition 0 */ +#define NV_GFW_GLOBAL_DEVINIT_C2C_STATUS_C2C0_FW_INIT_FAIL 21 +/** @brief C2C FW init failed partition 1 */ +#define NV_GFW_GLOBAL_DEVINIT_C2C_STATUS_C2C1_FW_INIT_FAIL 22 +/** @brief no failure seen, c2c init was successful */ +#define BPMP_C2C_STATUS_C2C_LINK_TRAIN_PASS 255 +/** @} bpmp_c2c_status */ + +/** + * @ingroup MRQ_Codes + * @def MRQ_C2C + * @brief Control C2C partitions initialization. + * + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_c2c_request + * * Response Payload: @ref mrq_c2c_response + * + * @addtogroup C2C + * @{ + */ +enum mrq_c2c_cmd { + /** + * @brief Check whether the BPMP driver supports the specified request + * type + * + * mrq_response:: err is 0 if the specified request is supported and + * -#BPMP_ENODEV otherwise + */ + CMD_C2C_QUERY_ABI = 0, + + /** + * @brief Start C2C initialization + * + * mrq_response:err is + * * 0: Success + * * -#BPMP_ENODEV: MRQ_C2C is not supported by BPMP-FW + * * -#BPMP_ENAVAIL: Invalid request parameters + * * -#BPMP_EACCES: Request is not accepted + */ + CMD_C2C_START_INITIALIZATION = 1, + + /** + * @brief Command to query current C2C training status + * + * This command will return the result of the latest C2C re-training that is initiated with + * MRQ_C2C.CMD_C2C_START_INITIALIZATION or MRQ_C2C.CMD_C2C_START_HOTRESET calls. + * If no training has been initiated yet, the command will return code BPMP_C2C_STATUS_INIT_NOT_STARTED. + * + * mrq_response:err is + * * 0: Success + * * -#BPMP_ENODEV: MRQ_C2C is not supported by BPMP-FW + * * -#BPMP_EACCES: Request is not accepted + */ + CMD_C2C_GET_STATUS = 2, + /** + * @brief C2C hot-reset precondition + * + * mrq_response:err is + * * 0: Success + * * -#BPMP_ENODEV: MRQ_C2C is not supported by BPMP-FW + * * -#BPMP_ENAVAIL: Invalid request parameters + * * -#BPMP_EACCES: Request is not accepted + */ + CMD_C2C_HOTRESET_PREP = 3, + /** + * @brief Start C2C hot-reset + * + * mrq_response:err is + * * 0: Success + * * -#BPMP_ENODEV: MRQ_C2C is not supported by BPMP-FW + * * -#BPMP_ENAVAIL: Invalid request parameters + * * -#BPMP_EACCES: Request is not accepted + */ + CMD_C2C_START_HOTRESET = 4, + + CMD_C2C_MAX +}; + +/** + * @brief Request data for #MRQ_C2C command CMD_C2C_QUERY_ABI + */ +struct cmd_c2c_query_abi_request { + /** @brief Command identifier to be queried */ + uint32_t cmd; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_C2C command CMD_C2C_START_INITIALIZATION + */ +struct cmd_c2c_start_init_request { + /** @brief 1: partition 0; 2: partition 1; 3: partition 0 and 1; */ + uint8_t partitions; +} BPMP_ABI_PACKED; + +/** + * @brief Response data for #MRQ_C2C command CMD_C2C_START_INITIALIZATION + */ +struct cmd_c2c_start_init_response { + /** @brief Refer to @ref bpmp_c2c_status */ + uint8_t status; +} BPMP_ABI_PACKED; + +/** + * @brief Response data for #MRQ_C2C command CMD_C2C_GET_STATUS + */ +struct cmd_c2c_get_status_response { + /** @brief Refer to @ref bpmp_c2c_status */ + uint8_t status; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_C2C command CMD_C2C_HOTRESET_PREP + */ +struct cmd_c2c_hotreset_prep_request { + /** @brief 1: partition 0; 2: partition 1; 3: partition 0 and 1; */ + uint8_t partitions; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_C2C command CMD_C2C_START_HOTRESET + */ +struct cmd_c2c_start_hotreset_request { + /** @brief 1: partition 0; 2: partition 1; 3: partition 0 and 1; */ + uint8_t partitions; +} BPMP_ABI_PACKED; + +/** + * @brief Response data for #MRQ_C2C command CMD_C2C_START_HOTRESET + */ +struct cmd_c2c_start_hotreset_response { + /** @brief Refer to @ref bpmp_c2c_status */ + uint8_t status; +} BPMP_ABI_PACKED; + +/** + * @brief Request with #MRQ_C2C + * + * |sub-command |payload | + * |-----------------------------|-------------------------------| + * |CMD_C2C_QUERY_ABI |cmd_c2c_query_abi_request | + * |CMD_C2C_START_INITIALIZATION |cmd_c2c_start_init_request | + * |CMD_C2C_GET_STATUS | | + * |CMD_C2C_HOTRESET_PREP |cmd_c2c_hotreset_prep_request | + * |CMD_C2C_START_HOTRESET |cmd_c2c_start_hotreset_request | + + */ +struct mrq_c2c_request { + uint32_t cmd; + union { + struct cmd_c2c_query_abi_request c2c_query_abi_req; + struct cmd_c2c_start_init_request c2c_start_init_req; + struct cmd_c2c_hotreset_prep_request c2c_hotreset_prep_req; + struct cmd_c2c_start_hotreset_request c2c_start_hotreset_req; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** + * @brief Response to MRQ_C2C + * + * |sub-command |payload | + * |-----------------------------|--------------------------------| + * |CMD_C2C_QUERY_ABI | | + * |CMD_C2C_START_INITIALIZATION |cmd_c2c_start_init_response | + * |CMD_C2C_GET_STATUS |cmd_c2c_get_status_response | + * |CMD_C2C_HOTRESET_PREP | | + * |CMD_C2C_START_HOTRESET |cmd_c2c_start_hotreset_response | + */ +struct mrq_c2c_response { + union { + struct cmd_c2c_start_init_response c2c_start_init_resp; + struct cmd_c2c_get_status_response c2c_get_status_resp; + struct cmd_c2c_start_hotreset_response c2c_start_hotreset_resp; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; +/** @} */ +/** @endcond */ + + +/** @cond (bpmp_t264) + * @ingroup MRQ_Codes + * @def MRQ_PCIE + * @brief Perform a PCIE operation + * + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_pcie_request + * + * @addtogroup PCIE + * @{ + */ + +/** + * @brief Sub-command identifiers for #MRQ_PCIE. + */ +enum mrq_pcie_cmd { + /** @brief Initialize PCIE EP controller. */ + CMD_PCIE_EP_CONTROLLER_INIT = 0, + /** @brief Disable PCIE EP controller. */ + CMD_PCIE_EP_CONTROLLER_OFF = 1, + + /** @brief Disable PCIE RP controller. */ + CMD_PCIE_RP_CONTROLLER_OFF = 100, + + CMD_PCIE_MAX, +}; + +/** + * @brief Request payload for #MRQ_PCIE sub-command #CMD_PCIE_EP_CONTROLLER_INIT. + */ +struct cmd_pcie_ep_controller_init_request { + /** + * @brief PCIe EP controller number. + * Valid entries for T264 are 2, 4 and 5. + */ + uint8_t ep_controller; + /** + * @brief PCIe EP function programming interface code. + * Valid range in HW is [0, 0xFFU], BPMP-FW programs the input value without any check. + * It is up to the requester to send valid input as documented in "PCI CODE AND ID + * ASSIGNMENT SPECIFICATION". + */ + uint8_t progif_code; + /** + * @brief PCIe EP function sub-class code. + * Valid range in HW is [0, 0xFFU], BPMP-FW programs the input value without any check. + * It is up to the requester to send valid input as documented in "PCI CODE AND ID + * ASSIGNMENT SPECIFICATION". + */ + uint8_t subclass_code; + /** + * @brief PCIe EP function base class code. + * Valid range in HW is [0, 0xFFU], BPMP-FW programs the input value without any check. + * It is up to the requester to send valid input as documented in "PCI CODE AND ID + * ASSIGNMENT SPECIFICATION". + */ + uint8_t baseclass_code; + /** + * @brief PCIe EP function device id. + * Valid range is [0, 0x7FU], only LSB 7 bits are writable in 16-bit PCI device id. + * Valid range check is done on input value and returns -BPMP_EINVAL on failure. + */ + uint8_t deviceid; + /** + * @brief PCIe EP EP BAR1 size. + * Valid range is [6U, 16U], which translate to [64MB, 64GB] size. + * Valid range check is done on input value and returns -BPMP_EINVAL on failure. + */ + uint8_t bar1_size; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for #MRQ_PCIE sub-command #CMD_PCIE_EP_CONTROLLER_OFF. + */ +struct cmd_pcie_ep_controller_off_request { + /** @brief EP controller number, T264 valid: 2, 4, 5. */ + uint8_t ep_controller; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for #MRQ_PCIE sub-command #CMD_PCIE_RP_CONTROLLER_OFF. + */ +struct cmd_pcie_rp_controller_off_request { + /** @brief RP controller number, T264 valid: 1-5 */ + uint8_t rp_controller; +} BPMP_ABI_PACKED; + +/** + * @ingroup PCIE + * @brief Request payload for the #MRQ_PCIE command. + * + * Used by the sender of an #MRQ_PCIE message to control PCIE. + * Below table shows sub-commands with their corresponding payload data. + * + * |sub-command |payload | + * |--------------------------------------|-----------------------------------------| + * |#CMD_PCIE_EP_CONTROLLER_INIT |#cmd_pcie_ep_controller_init_request | + * |#CMD_PCIE_EP_CONTROLLER_OFF |#cmd_pcie_ep_controller_off_request | + * + * @cond (!bpmp_safe) + * + * The following additional MRQs are supported on non-functional-safety + * builds: + * |sub-command |payload | + * |--------------------------------------|-----------------------------------------| + * |#CMD_PCIE_RP_CONTROLLER_OFF |#cmd_pcie_rp_controller_off_request | + * + * @endcond + * + */ +struct mrq_pcie_request { + /** @brief Sub-command ID from @ref mrq_pcie_cmd. */ + uint32_t cmd; + + union { + struct cmd_pcie_ep_controller_init_request ep_ctrlr_init; + struct cmd_pcie_ep_controller_off_request ep_ctrlr_off; + /** @cond (!bpmp_safe) */ + struct cmd_pcie_rp_controller_off_request rp_ctrlr_off; + /** @endcond */ + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** @} PCIE */ +/** @endcond */ + +/** @cond (bpmp_t264) + * @ingroup MRQ_Codes + * @def MRQ_CR7 + * @brief Perform a CR7 operation + * + * * Initiators: CPU_S + * * Targets: BPMP + * * Request Payload: @ref mrq_cr7_request + * + * @addtogroup CR7 + * @{ + */ + +/** + * @brief Payload for #MRQ_CR7 + * 2 fields for future parameters are provided. These must be 0 currently. + */ +struct cmd_cr7_request { + uint32_t fld0; + uint32_t fld1; +} BPMP_ABI_PACKED; + +struct cmd_cr7_query_abi_request { + /** #MRQ_CR7 sub-command identifier from @ref mrq_cr7_cmd */ + uint32_t type; +} BPMP_ABI_PACKED; + +/** + * @brief Sub-command identifiers for #MRQ_CR7. + */ +enum mrq_cr7_cmd { + /** + * @brief Check whether the BPMP driver supports the specified request + * type + * + * mrq_response:: err is 0 if the specified request is supported and + * -#BPMP_ENODEV otherwise + */ + CMD_CR7_QUERY_ABI = 0, + + /** @brief Enter CR7 state on the package BPMP-FW is running on. */ + CMD_CR7_ENTRY = 1, + /** @brief Exit CR7 state on the package BPMP-FW is running on. */ + CMD_CR7_EXIT = 2, + + CMD_CR7_MAX, +}; + +/** + * @ingroup CR7 + * @brief #MRQ_CR7 structure + * + * |Sub-command |Payload | + * |----------------------------|---------------------------| + * |#CMD_CR7_QUERY_ABI | #cmd_cr7_query_abi_request| + * |#CMD_CR7_ENTRY | #cmd_cr7_request | + * |#CMD_CR7_EXIT | #cmd_cr7_request | + + */ +struct mrq_cr7_request { + /** @brief Sub-command ID from @ref mrq_cr7_cmd. */ + uint32_t cmd; + union { + struct cmd_cr7_query_abi_request query_abi; + struct cmd_cr7_request cr7_request; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** @} CR7 */ +/** @endcond */ + +/** @cond (bpmp_tb500) + * @ingroup MRQ_Codes + * @def MRQ_HWPM + * @brief Configure and query HWPM functionality + * + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_hwpm_request + * * Response Payload: @ref mrq_hwpm_response + * + * @addtogroup HWPM + * @{ + */ + +/** + * @brief Sub-command identifiers for #MRQ_HWPM. + */ +enum mrq_hwpm_cmd { + /** + * @brief Check whether the BPMP-FW supports the specified + * #MRQ_HWPM sub-command. + * + * mrq_response:err is 0 if the specified request is + * supported and -#BPMP_ENODEV otherwise. + */ + CMD_HWPM_QUERY_ABI = 1, + + /** + * @brief Configure IPMU triggers + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_HWPM is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_HWPM_IPMU_SET_TRIGGERS = 2, + + /** + * @brief Configure IPMU payloads and shifts + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_HWPM is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_HWPM_IPMU_SET_PAYLOADS_SHIFTS = 3, + + /** + * @brief Get maximum number of payloads + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_HWPM is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + */ + CMD_HWPM_IPMU_GET_MAX_PAYLOADS = 4, + + /** + * @brief Configure NVTHERM sample rate + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_HWPM is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_HWPM_NVTHERM_SET_SAMPLE_RATE = 5, + + /** + * @brief Set NVTHERM bubble interval + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_HWPM is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_HWPM_NVTHERM_SET_BUBBLE_INTERVAL = 6, + + /** + * @brief Configure NVTHERM DG flexible channels + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_HWPM is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_HWPM_NVTHERM_SET_FLEX_CHANNELS = 7, + + /** + * @brief Get ISENSE sensor name + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_HWPM is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_HWPM_ISENSE_GET_SENSOR_NAME = 8, + + /** + * @brief Get ISENSE sensor channel + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_HWPM is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_HWPM_ISENSE_GET_SENSOR_CHANNEL = 9, + + /** + * @brief Get ISENSE sensor scale factor + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_HWPM is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_HWPM_ISENSE_GET_SENSOR_SCALE_FACTOR = 10, + + /** + * @brief Get ISENSE sensor offset + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_HWPM is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + * | -#BPMP_ENODATA | No sensor offset. | + */ + CMD_HWPM_ISENSE_GET_SENSOR_OFFSET = 11, + + /** + * @brief Get ISENSE sum block name + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_HWPM is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_HWPM_ISENSE_GET_SUM_BLOCK_NAME = 12, + + /** + * @brief Get ISENSE sum input sensor IDs + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_HWPM is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_HWPM_ISENSE_GET_SUM_BLOCK_INPUTS = 13, + + /** + * @brief Largest supported #MRQ_HWPM sub-command identifier + 1 + */ + CMD_HWPM_MAX, +}; + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_QUERY_ABI + */ +struct cmd_hwpm_query_abi_req { + /** @brief Sub-command identifier from @ref mrq_hwpm_cmd */ + uint32_t cmd_code; +} BPMP_ABI_PACKED; + +/** + * @brief Maximum array length for IPMU trigger bitmask + */ +#define HWPM_IPMU_TRIGGER_ARR_LEN 28U + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_IPMU_SET_TRIGGERS + */ +struct cmd_hwpm_ipmu_set_triggers_req { + /** @brief IPMU physical ID + * + * @note Valid range from [0, MAX_CPU_CORES), see @ref bpmp_hwpm_core_config + */ + uint32_t ipmu_phys_id; + /** @brief Trigger bitmask, see @ref bpmp_ipmu_trigger_ids + * + * @note Setting a trigger bit will cause the associated trigger to + * generate an output packet from IPMU to the HWPM perfmux. + * @note Up to a maximum possible 896 triggers + */ + uint32_t triggers[HWPM_IPMU_TRIGGER_ARR_LEN]; +} BPMP_ABI_PACKED; + +/** + * @brief Array length for IPMU payload bitmask + */ +#define HWPM_IPMU_PAYLOAD_ARR_LEN 26U + +/** + * @brief Array length for IPMU payload shift bitmask + */ +#define HWPM_IPMU_SHIFT_ARR_LEN 2U + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_IPMU_SET_PAYLOADS_SHIFTS + */ +struct cmd_hwpm_ipmu_set_payloads_shifts_req { + /** @brief IPMU physical ID + * + * @note Valid range from [0, MAX_CPU_CORES), see @ref bpmp_hwpm_core_config + */ + uint32_t ipmu_phys_id; + /** @brief Payload bitmask, see @ref bpmp_ipmu_payload_ids + * + * @note Setting a payload bit will add the associated payload to the + * IPMU output packet. + * @note The maximum number of payloads is platform dependent, + * @see #CMD_HWPM_IPMU_GET_MAX_PAYLOADS + * @note To disable IPMU streaming on this instance, set all payload bits to 0. + * @note Up to a maximum of 832 available payloads + */ + uint32_t payloads[HWPM_IPMU_PAYLOAD_ARR_LEN]; + /** + * @brief Payload shift mask + * + * @note Setting the i-th shift bit will right-shift the + * i-th enabled payload by 1 bit. + * @note Up to a maximum of 64 simultaneous emitted payloads + */ + uint32_t shifts[HWPM_IPMU_SHIFT_ARR_LEN]; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_IPMU_GET_MAX_PAYLOADS + */ +struct cmd_hwpm_ipmu_get_max_payloads_req { + BPMP_ABI_EMPTY +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_NVTHERM_SET_SAMPLE_RATE + */ +struct cmd_hwpm_nvtherm_set_sample_rate_req { + /** @brief Sample rate in microseconds + * + * @note Requesting a sample rate of 0 will disable NVTHERM streaming. + */ + uint32_t sample_rate; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_NVTHERM_SET_BUBBLE_INTERVAL + */ +struct cmd_hwpm_nvtherm_set_bubble_interval_req { + /** @brief Bubble interval in microseconds */ + uint32_t bubble_interval; +} BPMP_ABI_PACKED; + +/** + * @brief Maximum array length for NVTHERM flexible channel bitmask + */ +#define HWPM_NVTHERM_FLEX_CHANNEL_ARR_LEN 29U + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_NVTHERM_SET_FLEX_CHANNELS + */ +struct cmd_hwpm_nvtherm_set_flex_channels_req { + /** @brief NVTHERM flexible channel bitmask + * + * @see #bpmp_nvtherm_flex_channel_ids + * + * @note Up to a maximum of 928 flexible channels + */ + uint32_t channels[HWPM_NVTHERM_FLEX_CHANNEL_ARR_LEN]; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_ISENSE_GET_SENSOR_NAME + */ +struct cmd_hwpm_isense_get_sensor_name_req { + /** @brief Sensor ID from @ref bpmp_isense_sensor_ids */ + uint32_t sensor_id; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_ISENSE_GET_SENSOR_CHANNEL + */ +struct cmd_hwpm_isense_get_sensor_channel_req { + /** @brief Sensor ID from @ref bpmp_isense_sensor_ids */ + uint32_t sensor_id; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_ISENSE_GET_SENSOR_SCALE_FACTOR + */ +struct cmd_hwpm_isense_get_sensor_scale_factor_req { + /** @brief Sensor ID from @ref bpmp_isense_sensor_ids */ + uint32_t sensor_id; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_ISENSE_GET_SENSOR_OFFSET + */ +struct cmd_hwpm_isense_get_sensor_offset_req { + /** @brief Sensor ID from @ref bpmp_isense_sensor_ids */ + uint32_t sensor_id; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_ISENSE_GET_SUM_BLOCK_NAME + */ +struct cmd_hwpm_isense_get_sum_block_name_req { + /** @brief Sum block index */ + uint32_t sum_block_index; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_HWPM sub-command #CMD_HWPM_ISENSE_GET_SUM_BLOCK_INPUTS + */ +struct cmd_hwpm_isense_get_sum_block_inputs_req { + /** @brief Sum block index */ + uint32_t sum_block_index; +} BPMP_ABI_PACKED; + +/** + * @brief Response data for #MRQ_HWPM sub-command #CMD_HWPM_IPMU_GET_MAX_PAYLOADS + */ +struct cmd_hwpm_ipmu_get_max_payloads_resp { + /** @brief Maximum number of payloads */ + uint32_t max_payloads; +} BPMP_ABI_PACKED; + +/** + * @brief Maximum array length for ISENSE sensor name + */ +#define HWPM_ISENSE_SENSOR_MAX_NAME_LEN 64U + +/** + * @brief Response data for #MRQ_HWPM sub-command #CMD_HWPM_ISENSE_GET_SENSOR_NAME + */ +struct cmd_hwpm_isense_get_sensor_name_resp { + /** @brief Sensor name */ + char sensor_name[HWPM_ISENSE_SENSOR_MAX_NAME_LEN]; +} BPMP_ABI_PACKED; + +/** + * @brief Response data for #MRQ_HWPM sub-command #CMD_HWPM_ISENSE_GET_SENSOR_CHANNEL + */ +struct cmd_hwpm_isense_get_sensor_channel_resp { + /** @brief Physical channel index */ + uint32_t channel_index; +} BPMP_ABI_PACKED; + +/** + * @brief Response data for #MRQ_HWPM sub-command #CMD_HWPM_ISENSE_GET_SENSOR_SCALE_FACTOR + */ +struct cmd_hwpm_isense_get_sensor_scale_factor_resp { + /** @brief Scale factor */ + float scale_factor; +} BPMP_ABI_PACKED; + +/** + * @brief Response data for #MRQ_HWPM sub-command #CMD_HWPM_ISENSE_GET_SENSOR_OFFSET + */ +struct cmd_hwpm_isense_get_sensor_offset_resp { + /** @brief Offset sensor ID */ + uint32_t offset_sensor_id; +} BPMP_ABI_PACKED; + +/** + * @brief Maximum array length for ISENSE sum name + */ +#define HWPM_ISENSE_SUM_BLOCK_MAX_NAME_LEN 64U + +/** + * @brief Response data for #MRQ_HWPM sub-command #CMD_HWPM_ISENSE_GET_SUM_BLOCK_NAME + */ +struct cmd_hwpm_isense_get_sum_block_name_resp { + /** @brief Sum block name */ + char sum_block_name[HWPM_ISENSE_SUM_BLOCK_MAX_NAME_LEN]; +} BPMP_ABI_PACKED; + +/** + * @brief Maximum array length for ISENSE sum block input sensor IDs + */ +#define HWPM_ISENSE_SUM_BLOCK_INPUTS_MAX 16U + +/** + * @brief Response data for #MRQ_HWPM sub-command #CMD_HWPM_ISENSE_GET_SUM_BLOCK_INPUTS + */ +struct cmd_hwpm_isense_get_sum_block_inputs_resp { + /** @brief Input channel indices; negative if no input is applied */ + int32_t input_channel_idx[HWPM_ISENSE_SUM_BLOCK_INPUTS_MAX]; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for the #MRQ_HWPM -command + * + * | Sub-command | Request payload | + * | ---------------------------------------- | -------------------------------------------- | + * | #CMD_HWPM_QUERY_ABI | #cmd_hwpm_query_abi_req | + * | #CMD_HWPM_IPMU_SET_TRIGGERS | #cmd_hwpm_ipmu_set_triggers_req | + * | #CMD_HWPM_IPMU_SET_PAYLOADS_SHIFTS | #cmd_hwpm_ipmu_set_payloads_shifts_req | + * | #CMD_HWPM_IPMU_GET_MAX_PAYLOADS | #cmd_hwpm_ipmu_get_max_payloads_req | + * | #CMD_HWPM_NVTHERM_SET_SAMPLE_RATE | #cmd_hwpm_nvtherm_set_sample_rate_req | + * | #CMD_HWPM_NVTHERM_SET_BUBBLE_INTERVAL | #cmd_hwpm_nvtherm_set_bubble_interval_req | + * | #CMD_HWPM_NVTHERM_SET_FLEX_CHANNELS | #cmd_hwpm_nvtherm_set_flex_channels_req | + * | #CMD_HWPM_ISENSE_GET_SENSOR_CHANNEL | #cmd_hwpm_isense_get_sensor_channel_req | + * | #CMD_HWPM_ISENSE_GET_SENSOR_SCALE_FACTOR | #cmd_hwpm_isense_get_sensor_scale_factor_req | + * | #CMD_HWPM_ISENSE_GET_SENSOR_OFFSET | #cmd_hwpm_isense_get_sensor_offset_req | + * | #CMD_HWPM_ISENSE_GET_SUM_BLOCK_NAME | #cmd_hwpm_isense_get_sum_block_name_req | + * | #CMD_HWPM_ISENSE_GET_SUM_BLOCK_INPUTS | #cmd_hwpm_isense_get_sum_block_inputs_req | + */ +struct mrq_hwpm_request { + uint32_t cmd; + union { + struct cmd_hwpm_query_abi_req query_abi; + struct cmd_hwpm_ipmu_set_triggers_req ipmu_set_triggers; + struct cmd_hwpm_ipmu_set_payloads_shifts_req ipmu_set_payloads_shifts; + struct cmd_hwpm_ipmu_get_max_payloads_req ipmu_get_max_payloads; + struct cmd_hwpm_nvtherm_set_sample_rate_req nvtherm_set_sample_rate; + struct cmd_hwpm_nvtherm_set_bubble_interval_req nvtherm_set_bubble_interval; + struct cmd_hwpm_nvtherm_set_flex_channels_req nvtherm_set_flex_channels; + struct cmd_hwpm_isense_get_sensor_name_req isense_get_sensor_name; + struct cmd_hwpm_isense_get_sensor_channel_req isense_get_sensor_channel; + struct cmd_hwpm_isense_get_sensor_scale_factor_req isense_get_sensor_scale_factor; + struct cmd_hwpm_isense_get_sensor_offset_req isense_get_sensor_offset; + struct cmd_hwpm_isense_get_sum_block_name_req isense_get_sum_block_name; + struct cmd_hwpm_isense_get_sum_block_inputs_req isense_get_sum_block_inputs; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** + * @brief Response payload for the #MRQ_HWPM -command + * + * | Sub-command | Response payload | + * | ---------------------------------------- | --------------------------------------------- | + * | #CMD_HWPM_QUERY_ABI | - | + * | #CMD_HWPM_IPMU_SET_TRIGGERS | - | + * | #CMD_HWPM_IPMU_SET_PAYLOADS_SHIFTS | - | + * | #CMD_HWPM_IPMU_GET_MAX_PAYLOADS | #cmd_hwpm_ipmu_get_max_payloads_resp | + * | #CMD_HWPM_NVTHERM_SET_SAMPLE_RATE | - | + * | #CMD_HWPM_NVTHERM_SET_BUBBLE_INTERVAL | - | + * | #CMD_HWPM_NVTHERM_SET_FLEX_CHANNELS | - | + * | #CMD_HWPM_ISENSE_GET_SENSOR_NAME | #cmd_hwpm_isense_get_sensor_name_resp | + * | #CMD_HWPM_ISENSE_GET_SENSOR_CHANNEL | #cmd_hwpm_isense_get_sensor_channel_resp | + * | #CMD_HWPM_ISENSE_GET_SENSOR_SCALE_FACTOR | #cmd_hwpm_isense_get_sensor_scale_factor_resp | + * | #CMD_HWPM_ISENSE_GET_SENSOR_OFFSET | #cmd_hwpm_isense_get_sensor_offset_resp | + * | #CMD_HWPM_ISENSE_GET_SUM_BLOCK_NAME | #cmd_hwpm_isense_get_sum_block_name_resp | + * | #CMD_HWPM_ISENSE_GET_SUM_BLOCK_INPUTS | #cmd_hwpm_isense_get_sum_block_inputs_resp | + */ +struct mrq_hwpm_response { + uint32_t err; + union { + struct cmd_hwpm_ipmu_get_max_payloads_resp ipmu_get_max_payloads; + struct cmd_hwpm_isense_get_sensor_name_resp isense_get_sensor_name; + struct cmd_hwpm_isense_get_sensor_channel_resp isense_get_sensor_channel; + struct cmd_hwpm_isense_get_sensor_scale_factor_resp isense_get_sensor_scale_factor; + struct cmd_hwpm_isense_get_sensor_offset_resp isense_get_sensor_offset; + struct cmd_hwpm_isense_get_sum_block_name_resp isense_get_sum_block_name; + struct cmd_hwpm_isense_get_sum_block_inputs_resp isense_get_sum_block_inputs; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** @} HWPM */ +/** @endcond */ + +/** @cond (bpmp_tb500) + * @ingroup MRQ_Codes + * @def MRQ_DVFS + * @brief Configure DVFS functionality + * + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_dvfs_request + * + * @addtogroup DVFS + * @{ + */ + +/** + * @brief Sub-command identifiers for #MRQ_DVFS. + */ +enum mrq_dvfs_cmd { + /** + * @brief Check whether the BPMP-FW supports the specified + * #MRQ_DVFS sub-command. + * + * mrq_response:err is 0 if the specified request is + * supported and -#BPMP_ENODEV otherwise. + */ + CMD_DVFS_QUERY_ABI = 1, + + /** + * @brief Configure DVFS controller + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_DVFS is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_DVFS_SET_CTRL_STATE = 2, + + /** + * @brief Configure DVFS manager + * + * mrq_response:err is defined as: + * + * | Value | Description | + * | -------------- | ------------------------------------------- | + * | 0 | Success | + * | -#BPMP_ENODEV | #MRQ_DVFS is not supported by BPMP-FW. | + * | -#BPMP_ENOTSUP | Subcommand is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_DVFS_SET_MGR_STATE = 3, + + /** + * @brief Largest supported #MRQ_DVFS sub-command identifier + 1 + */ + CMD_DVFS_MAX, +}; + +/** + * @brief Request data for #MRQ_DVFS sub-command #CMD_DVFS_QUERY_ABI + */ +struct cmd_dvfs_query_abi_req { + /** @brief Sub-command identifier from @ref mrq_dvfs_cmd */ + uint32_t cmd_code; +} BPMP_ABI_PACKED; + +struct cmd_dvfs_set_ctrl_state_req { + /** @brief Controller ID from @ref bpmp_dvfs_ctrl_ids */ + uint32_t ctrl_id; + /** @brief Controller enable state */ + uint32_t enable; +} BPMP_ABI_PACKED; + +struct cmd_dvfs_set_mgr_state_req { + /** @brief Manager ID from @ref bpmp_dvfs_mgr_ids */ + uint32_t mgr_id; + /** @brief Manager enable state */ + uint32_t enable; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for the #MRQ_DVFS -command + * + * | Sub-command | Request payload | + * | ---------------------------------------- | -------------------------------------------- | + * | #CMD_DVFS_QUERY_ABI | #cmd_dvfs_query_abi_req | + * | #CMD_DVFS_SET_CTRL_STATE | #cmd_dvfs_set_ctrl_state_req | + * | #CMD_DVFS_SET_MGR_STATE | #cmd_dvfs_set_mgr_state_req | + */ +struct mrq_dvfs_request { + uint32_t cmd; + union { + struct cmd_dvfs_query_abi_req query_abi; + struct cmd_dvfs_set_ctrl_state_req set_ctrl_state; + struct cmd_dvfs_set_mgr_state_req set_mgr_state; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** @} DVFS */ +/** @endcond */ + +/** @cond (bpmp_tb500) + * @ingroup MRQ_Codes + * @def MRQ_PPP_PROFILE + * @brief Get power/performance profile configuration settings. + * + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_ppp_profile_request + * * Response Payload: @ref mrq_ppp_profile_response + * + * @addtogroup PPP + * @{ + */ + +/** + * @brief Sub-command identifiers for #MRQ_PPP_PROFILE. + */ +enum mrq_ppp_profile_cmd { + /** + * @brief Check whether the BPMP-FW supports the specified + * #MRQ_PPP_PROFILE sub-command. + * + * mrq_ppp_profile_response:err is 0 if the specified request is + * supported and -#BPMP_ENOTSUP otherwise. + */ + + CMD_PPP_PROFILE_QUERY_ABI = 0, + /** + * @brief Query the BPMP for the CPU core and SLC slice configuration associated + * with a given Power/Performance Profile (PPP). + * + * mrq_ppp_profile_response:err is defined as: + * + * | Value | Description | + * |----------------|------------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENOTSUP | #MRQ_PPP_PROFILE is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_PPP_PROFILE_QUERY_MASKS = 1, + /** + * @brief Query BPMP for the CPU mask corresponding to a requested + * number of active CPU cores. + * + * mrq_ppp_profile_response:err is defined as: + * + * | Value | Description | + * |----------------|------------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENOTSUP | #MRQ_PPP_PROFILE is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_PPP_CORE_QUERY_CPU_MASK = 2, + /** + * @brief Query BPMP-FW for the currently available Power/Performance Profiles. + * + * mrq_ppp_profile_response:err is defined as: + * + * | Value | Description | + * |----------------|------------------------------------------------| + * | 0 | Success | + * | -#BPMP_ENOTSUP | #MRQ_PPP_PROFILE is not supported by BPMP-FW. | + * | -#BPMP_EINVAL | Invalid request parameters. | + */ + CMD_PPP_AVAILABLE_QUERY = 3, +}; + +/** + * @brief Request data for #MRQ_PPP_PROFILE sub-command + * #CMD_PPP_PROFILE_QUERY_ABI + */ +struct cmd_ppp_profile_query_abi_req { + /** @brief Sub-command identifier from @ref mrq_ppp_profile_cmd */ + uint32_t cmd_code; +} BPMP_ABI_PACKED; + +/** + * @brief Response data for #MRQ_PPP_PROFILE sub-command + * #CMD_PPP_AVAILABLE_QUERY + */ +struct cmd_ppp_available_query_resp { + /** + * @brief Bitmask of available profiles. + * Bit N = 1 ⇒ profile N is available + */ + uint32_t avail_ppp_mask; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_PPP_PROFILE sub-command + * #CMD_PPP_PROFILE_QUERY_MASKS + */ +struct cmd_ppp_profile_query_masks_req { + /** @brief power/perf profile identifier */ + uint32_t profile_id; +} BPMP_ABI_PACKED; + +/** + * @brief Response payload for #MRQ_PPP_PROFILE sub-command + * #CMD_PPP_PROFILE_QUERY_MASKS + */ +struct cmd_ppp_profile_query_masks_resp { + /** @brief Enabled cores in this profile */ + uint32_t num_active_cores; + /** @brief Enabled SLC slices in this profile */ + uint32_t num_active_slcs; + /** @brief Number of valid words in active_core_masks array */ + uint32_t max_num_core_words; + /** @brief Number of valid words in active_slc_masks array */ + uint32_t max_num_slc_words; + /** @brief Enabled cores bit mask (bit N = 1 => core N enabled) */ + uint32_t active_core_masks[8]; + /** @brief Enabled SLC slices bit mask (bit N = 1 => SLC slice N enabled) */ + uint32_t active_slc_masks[8]; +} BPMP_ABI_PACKED; + +/** + * @brief Request data for #MRQ_PPP_PROFILE sub-command + * #CMD_PPP_CORE_QUERY_CPU_MASK + */ +struct cmd_ppp_core_query_cpu_mask_req { + /** @brief Requested number of active cores */ + uint32_t num_cores; +} BPMP_ABI_PACKED; + +/** + * @brief Response data for #MRQ_PPP_PROFILE sub-command + * #CMD_PPP_CORE_QUERY_CPU_MASK + */ +struct cmd_ppp_core_query_cpu_mask_resp { + /** @brief Number of valid words in active_core_masks array */ + uint32_t max_num_words; + /** @brief Enabled CPU core bitmask (bit N = 1 ⇒ core N enabled) */ + uint32_t active_core_masks[8]; +} BPMP_ABI_PACKED; + +/** + * @brief Request payload for the #MRQ_PPP_PROFILE -command + * + * | Sub-command | Request payload | + * |-------------------------------|----------------------------------------| + * | #CMD_PPP_PROFILE_QUERY_ABI | #cmd_ppp_profile_query_abi_req | + * | #CMD_PPP_PROFILE_QUERY_MASKS | #cmd_ppp_profile_query_masks_req | + * | #CMD_PPP_CORE_QUERY_CPU_MASK | #cmd_ppp_core_query_cpu_mask_req | + * | #CMD_PPP_AVAILABLE_QUERY | - | + */ +struct mrq_ppp_profile_request { + /** @brief Sub-command ID from @ref mrq_ppp_profile_cmd. */ + uint32_t cmd; + union { + struct cmd_ppp_profile_query_abi_req query_abi; + struct cmd_ppp_profile_query_masks_req ppp_profile_masks_req; + struct cmd_ppp_core_query_cpu_mask_req ppp_core_mask_req; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** + * @brief Response payload for the #MRQ_PPP_PROFILE -command. + * + * | Sub-command | Response payload | + * |-------------------------------|----------------------------------------| + * | #CMD_PPP_PROFILE_QUERY_ABI | - | + * | #CMD_PPP_PROFILE_QUERY_MASKS | #cmd_ppp_profile_query_masks_resp | + * | #CMD_PPP_CORE_QUERY_CPU_MASK | #cmd_ppp_core_query_cpu_mask_resp | + * | #CMD_PPP_AVAILABLE_QUERY | #cmd_ppp_available_query_resp | + */ +struct mrq_ppp_profile_response { + uint32_t err; + union { + struct cmd_ppp_profile_query_masks_resp ppp_profile_masks_resp; + struct cmd_ppp_core_query_cpu_mask_resp ppp_core_mask_resp; + struct cmd_ppp_available_query_resp ppp_avail_query_resp; + } BPMP_UNION_ANON; +} BPMP_ABI_PACKED; + +/** @} PPP */ +/** @endcond */ /** * @addtogroup Error_Codes @@ -3953,6 +6728,8 @@ struct mrq_gears_response { #define BPMP_ENOSYS 38 /** @brief Invalid slot */ #define BPMP_EBADSLT 57 +/** @brief No data */ +#define BPMP_ENODATA 61 /** @brief Invalid message */ #define BPMP_EBADMSG 77 /** @brief Operation not supported */ diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h index f5e4ac5b8cce..a33582590a3b 100644 --- a/include/soc/tegra/bpmp.h +++ b/include/soc/tegra/bpmp.h @@ -127,6 +127,7 @@ struct tegra_bpmp_message { #if IS_ENABLED(CONFIG_TEGRA_BPMP) struct tegra_bpmp *tegra_bpmp_get(struct device *dev); +struct tegra_bpmp *tegra_bpmp_get_with_id(struct device *dev, unsigned int *id); void tegra_bpmp_put(struct tegra_bpmp *bpmp); int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp, struct tegra_bpmp_message *msg); @@ -143,21 +144,31 @@ bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq); #else static inline struct tegra_bpmp *tegra_bpmp_get(struct device *dev) { - return ERR_PTR(-ENOTSUPP); + return ERR_PTR(-ENODEV); } + +static inline struct tegra_bpmp *tegra_bpmp_get_with_id(struct device *dev, + unsigned int *id) +{ + return ERR_PTR(-ENODEV); +} + static inline void tegra_bpmp_put(struct tegra_bpmp *bpmp) { } + static inline int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp, struct tegra_bpmp_message *msg) { - return -ENOTSUPP; + return -ENODEV; } + static inline int tegra_bpmp_transfer(struct tegra_bpmp *bpmp, struct tegra_bpmp_message *msg) { - return -ENOTSUPP; + return -ENODEV; } + static inline void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, int code, const void *data, size_t size) @@ -169,8 +180,9 @@ static inline int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, tegra_bpmp_mrq_handler_t handler, void *data) { - return -ENOTSUPP; + return -ENODEV; } + static inline void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, void *data) { diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h index 6ee4c59db620..e6da035d1306 100644 --- a/include/soc/tegra/mc.h +++ b/include/soc/tegra/mc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2014 NVIDIA Corporation + * Copyright (C) 2014-2026 NVIDIA Corporation */ #ifndef __SOC_TEGRA_MC_H__ @@ -10,10 +10,11 @@ #include #include #include +#include #include #include -#include #include +#include struct clk; struct device; @@ -164,10 +165,31 @@ struct tegra_mc_ops { int (*probe)(struct tegra_mc *mc); void (*remove)(struct tegra_mc *mc); int (*resume)(struct tegra_mc *mc); - irqreturn_t (*handle_irq)(int irq, void *data); int (*probe_device)(struct tegra_mc *mc, struct device *dev); }; +struct tegra_mc_regs { + unsigned int cfg_channel_enable; + unsigned int err_status; + unsigned int err_add; + unsigned int err_add_hi; + unsigned int err_vpr_status; + unsigned int err_vpr_add; + unsigned int err_sec_status; + unsigned int err_sec_add; + unsigned int err_mts_status; + unsigned int err_mts_add; + unsigned int err_gen_co_status; + unsigned int err_gen_co_add; + unsigned int err_route_status; + unsigned int err_route_add; +}; + +struct tegra_mc_intmask { + u32 reg; + u32 mask; +}; + struct tegra_mc_soc { const struct tegra_mc_client *clients; unsigned int num_clients; @@ -185,7 +207,6 @@ struct tegra_mc_soc { const struct tegra_smmu_soc *smmu; - u32 intmask; u32 ch_intmask; u32 global_intstatus_channel_shift; bool has_addr_hi_reg; @@ -196,6 +217,14 @@ struct tegra_mc_soc { const struct tegra_mc_icc_ops *icc_ops; const struct tegra_mc_ops *ops; + const struct tegra_mc_regs *regs; + + const irq_handler_t *handle_irq; + unsigned int num_interrupts; + unsigned int mc_addr_hi_mask; + unsigned int mc_err_status_type_mask; + const struct tegra_mc_intmask *intmasks; + unsigned int num_intmasks; }; struct tegra_mc { @@ -206,7 +235,6 @@ struct tegra_mc { void __iomem *bcast_ch_regs; void __iomem **ch_regs; struct clk *clk; - int irq; const struct tegra_mc_soc *soc; unsigned long tick; @@ -256,4 +284,6 @@ tegra_mc_get_carveout_info(struct tegra_mc *mc, unsigned int id, } #endif +extern const struct tegra_mc_regs tegra20_mc_regs; + #endif /* __SOC_TEGRA_MC_H__ */ diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index e847cf51878c..95296bb4a33a 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -131,6 +131,7 @@ enum audio_device { TAS5827, TAS5828, TAS5830, + TAS5832, TAS_OTHERS, }; diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index b62d5fcce950..9a0e9f9e1ec4 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -111,6 +111,15 @@ /* Peripheral Device Text Identification Information */ #define PD_TEXT_ID_INFO_LEN 256 +enum target_compl_type { + /* Use the fabric driver's default completion type */ + TARGET_FABRIC_DEFAULT_COMPL, + /* Complete from the backend calling context */ + TARGET_DIRECT_COMPL, + /* Defer completion to the LIO workqueue */ + TARGET_QUEUE_COMPL, +}; + enum target_submit_type { /* Use the fabric driver's default submission type */ TARGET_FABRIC_DEFAULT_SUBMIT, @@ -741,6 +750,7 @@ struct se_dev_attrib { u32 atomic_granularity; u32 atomic_max_with_boundary; u32 atomic_max_boundary; + u8 complete_type; u8 submit_type; struct se_device *da_dev; struct config_group da_group; diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index 3378ff9ee271..e9039e73d058 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h @@ -118,15 +118,21 @@ struct target_core_fabric_ops { * its entirety before a command is aborted. */ unsigned int write_pending_must_be_called:1; + /* + * Set this if the driver does not require calling queue_data_in + * queue_status and check_stop_free from a worker thread when + * completing successful commands. + */ + unsigned int direct_compl_supp:1; /* * Set this if the driver supports submitting commands to the backend * from target_submit/target_submit_cmd. */ unsigned int direct_submit_supp:1; - /* - * Set this to a target_submit_type value. - */ + /* Set this to a target_submit_type value. */ u8 default_submit_type; + /* Set this to the target_compl_type value. */ + u8 default_compl_type; }; int target_register_template(const struct target_core_fabric_ops *fo); diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 8ad7a2d76c1d..ec1df8b94517 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -771,10 +771,8 @@ TRACE_EVENT(btrfs_sync_file, TP_fast_assign( struct dentry *dentry = file_dentry(file); struct inode *inode = file_inode(file); - struct dentry *parent = dget_parent(dentry); - struct inode *parent_inode = d_inode(parent); + struct inode *parent_inode = d_inode(dentry->d_parent); - dput(parent); TP_fast_assign_fsid(btrfs_sb(inode->i_sb)); __entry->ino = btrfs_ino(BTRFS_I(inode)); __entry->parent = btrfs_ino(BTRFS_I(parent_inode)); diff --git a/include/trace/events/dma.h b/include/trace/events/dma.h index 63597b004424..31c9ddf72c9d 100644 --- a/include/trace/events/dma.h +++ b/include/trace/events/dma.h @@ -34,7 +34,8 @@ TRACE_DEFINE_ENUM(DMA_NONE); { DMA_ATTR_PRIVILEGED, "PRIVILEGED" }, \ { DMA_ATTR_MMIO, "MMIO" }, \ { DMA_ATTR_DEBUGGING_IGNORE_CACHELINES, "CACHELINES_OVERLAP" }, \ - { DMA_ATTR_REQUIRE_COHERENT, "REQUIRE_COHERENT" }) + { DMA_ATTR_REQUIRE_COHERENT, "REQUIRE_COHERENT" }, \ + { DMA_ATTR_CC_SHARED, "CC_SHARED" }) DECLARE_EVENT_CLASS(dma_map, TP_PROTO(struct device *dev, phys_addr_t phys_addr, dma_addr_t dma_addr, diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 05a46908acd9..b5188d2671d7 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -2116,9 +2116,10 @@ DEFINE_EVENT(f2fs_zip_end, f2fs_decompress_pages_end, #ifdef CONFIG_F2FS_IOSTAT TRACE_EVENT(f2fs_iostat, - TP_PROTO(struct f2fs_sb_info *sbi, unsigned long long *iostat), + TP_PROTO(struct f2fs_sb_info *sbi, unsigned long long *iostat, + unsigned long long *read_folio_count), - TP_ARGS(sbi, iostat), + TP_ARGS(sbi, iostat, read_folio_count), TP_STRUCT__entry( __field(dev_t, dev) @@ -2150,6 +2151,7 @@ TRACE_EVENT(f2fs_iostat, __field(unsigned long long, fs_mrio) __field(unsigned long long, fs_discard) __field(unsigned long long, fs_reset_zone) + __array(unsigned long long, read_folio_count, 11) ), TP_fast_assign( @@ -2182,6 +2184,9 @@ TRACE_EVENT(f2fs_iostat, __entry->fs_mrio = iostat[FS_META_READ_IO]; __entry->fs_discard = iostat[FS_DISCARD_IO]; __entry->fs_reset_zone = iostat[FS_ZONE_RESET_IO]; + memset(__entry->read_folio_count, 0, sizeof(__entry->read_folio_count)); + memcpy(__entry->read_folio_count, read_folio_count, + sizeof(unsigned long long) * min_t(int, NR_PAGE_ORDERS, 11)); ), TP_printk("dev = (%d,%d), " @@ -2194,7 +2199,9 @@ TRACE_EVENT(f2fs_iostat, "app [read=%llu (direct=%llu, buffered=%llu), mapped=%llu], " "compr(buffered=%llu, mapped=%llu)], " "fs [data=%llu, (gc_data=%llu, cdata=%llu), " - "node=%llu, meta=%llu]", + "node=%llu, meta=%llu], " + "read_folio_count [0=%llu, 1=%llu, 2=%llu, 3=%llu, 4=%llu, " + "5=%llu, 6=%llu, 7=%llu, 8=%llu, 9=%llu, 10=%llu]", show_dev(__entry->dev), __entry->app_wio, __entry->app_dio, __entry->app_bio, __entry->app_mio, __entry->app_bcdio, __entry->app_mcdio, __entry->fs_dio, __entry->fs_cdio, @@ -2205,7 +2212,13 @@ TRACE_EVENT(f2fs_iostat, __entry->app_rio, __entry->app_drio, __entry->app_brio, __entry->app_mrio, __entry->app_bcrio, __entry->app_mcrio, __entry->fs_drio, __entry->fs_gdrio, - __entry->fs_cdrio, __entry->fs_nrio, __entry->fs_mrio) + __entry->fs_cdrio, __entry->fs_nrio, __entry->fs_mrio, + __entry->read_folio_count[0], __entry->read_folio_count[1], + __entry->read_folio_count[2], __entry->read_folio_count[3], + __entry->read_folio_count[4], __entry->read_folio_count[5], + __entry->read_folio_count[6], __entry->read_folio_count[7], + __entry->read_folio_count[8], __entry->read_folio_count[9], + __entry->read_folio_count[10]) ); #ifndef __F2FS_IOSTAT_LATENCY_TYPE diff --git a/include/trace/events/ipi.h b/include/trace/events/ipi.h index 9912f0ded81d..fae4f8eac411 100644 --- a/include/trace/events/ipi.h +++ b/include/trace/events/ipi.h @@ -68,16 +68,16 @@ TRACE_EVENT(ipi_raise, TP_ARGS(mask, reason), TP_STRUCT__entry( - __bitmask(target_cpus, nr_cpumask_bits) + __cpumask(target_cpus) __field(const char *, reason) ), TP_fast_assign( - __assign_bitmask(target_cpus, cpumask_bits(mask), nr_cpumask_bits); + __assign_cpumask(target_cpus, cpumask_bits(mask)); __entry->reason = reason; ), - TP_printk("target_mask=%s (%s)", __get_bitmask(target_cpus), __entry->reason) + TP_printk("target_mask=%s (%s)", __get_cpumask(target_cpus), __entry->reason) ); DECLARE_EVENT_CLASS(ipi_handler, diff --git a/include/trace/events/memcg.h b/include/trace/events/memcg.h index dfe2f51019b4..51b62c5931fc 100644 --- a/include/trace/events/memcg.h +++ b/include/trace/events/memcg.h @@ -11,14 +11,14 @@ DECLARE_EVENT_CLASS(memcg_rstat_stats, - TP_PROTO(struct mem_cgroup *memcg, int item, int val), + TP_PROTO(struct mem_cgroup *memcg, int item, long val), TP_ARGS(memcg, item, val), TP_STRUCT__entry( __field(u64, id) __field(int, item) - __field(int, val) + __field(long, val) ), TP_fast_assign( @@ -27,20 +27,20 @@ DECLARE_EVENT_CLASS(memcg_rstat_stats, __entry->val = val; ), - TP_printk("memcg_id=%llu item=%d val=%d", + TP_printk("memcg_id=%llu item=%d val=%ld", __entry->id, __entry->item, __entry->val) ); DEFINE_EVENT(memcg_rstat_stats, mod_memcg_state, - TP_PROTO(struct mem_cgroup *memcg, int item, int val), + TP_PROTO(struct mem_cgroup *memcg, int item, long val), TP_ARGS(memcg, item, val) ); DEFINE_EVENT(memcg_rstat_stats, mod_memcg_lruvec_state, - TP_PROTO(struct mem_cgroup *memcg, int item, int val), + TP_PROTO(struct mem_cgroup *memcg, int item, long val), TP_ARGS(memcg, item, val) ); diff --git a/include/trace/events/net.h b/include/trace/events/net.h index fdd9ad474ce3..dbc2c5598e35 100644 --- a/include/trace/events/net.h +++ b/include/trace/events/net.h @@ -10,6 +10,7 @@ #include #include #include +#include TRACE_EVENT(net_dev_start_xmit, @@ -208,7 +209,8 @@ DECLARE_EVENT_CLASS(net_dev_rx_verbose_template, TP_fast_assign( __assign_str(name); #ifdef CONFIG_NET_RX_BUSY_POLL - __entry->napi_id = skb->napi_id; + __entry->napi_id = napi_id_valid(skb->napi_id) ? + skb->napi_id : 0; #else __entry->napi_id = 0; #endif diff --git a/include/trace/events/rpcrdma.h b/include/trace/events/rpcrdma.h index e6a72646c507..b79913048e1a 100644 --- a/include/trace/events/rpcrdma.h +++ b/include/trace/events/rpcrdma.h @@ -392,10 +392,10 @@ DECLARE_EVENT_CLASS(xprtrdma_rdch_event, const struct rpc_task *task, unsigned int pos, struct rpcrdma_mr *mr, - int nsegs + bool is_last ), - TP_ARGS(task, pos, mr, nsegs), + TP_ARGS(task, pos, mr, is_last), TP_STRUCT__entry( __field(unsigned int, task_id) @@ -405,7 +405,7 @@ DECLARE_EVENT_CLASS(xprtrdma_rdch_event, __field(u32, handle) __field(u32, length) __field(u64, offset) - __field(int, nsegs) + __field(bool, is_last) ), TP_fast_assign( @@ -416,7 +416,7 @@ DECLARE_EVENT_CLASS(xprtrdma_rdch_event, __entry->handle = mr->mr_handle; __entry->length = mr->mr_length; __entry->offset = mr->mr_offset; - __entry->nsegs = nsegs; + __entry->is_last = is_last; ), TP_printk(SUNRPC_TRACE_TASK_SPECIFIER @@ -424,7 +424,7 @@ DECLARE_EVENT_CLASS(xprtrdma_rdch_event, __entry->task_id, __entry->client_id, __entry->pos, __entry->length, (unsigned long long)__entry->offset, __entry->handle, - __entry->nents < __entry->nsegs ? "more" : "last" + __entry->is_last ? "last" : "more" ) ); @@ -434,18 +434,18 @@ DECLARE_EVENT_CLASS(xprtrdma_rdch_event, const struct rpc_task *task, \ unsigned int pos, \ struct rpcrdma_mr *mr, \ - int nsegs \ + bool is_last \ ), \ - TP_ARGS(task, pos, mr, nsegs)) + TP_ARGS(task, pos, mr, is_last)) DECLARE_EVENT_CLASS(xprtrdma_wrch_event, TP_PROTO( const struct rpc_task *task, struct rpcrdma_mr *mr, - int nsegs + bool is_last ), - TP_ARGS(task, mr, nsegs), + TP_ARGS(task, mr, is_last), TP_STRUCT__entry( __field(unsigned int, task_id) @@ -454,7 +454,7 @@ DECLARE_EVENT_CLASS(xprtrdma_wrch_event, __field(u32, handle) __field(u32, length) __field(u64, offset) - __field(int, nsegs) + __field(bool, is_last) ), TP_fast_assign( @@ -464,7 +464,7 @@ DECLARE_EVENT_CLASS(xprtrdma_wrch_event, __entry->handle = mr->mr_handle; __entry->length = mr->mr_length; __entry->offset = mr->mr_offset; - __entry->nsegs = nsegs; + __entry->is_last = is_last; ), TP_printk(SUNRPC_TRACE_TASK_SPECIFIER @@ -472,7 +472,7 @@ DECLARE_EVENT_CLASS(xprtrdma_wrch_event, __entry->task_id, __entry->client_id, __entry->length, (unsigned long long)__entry->offset, __entry->handle, - __entry->nents < __entry->nsegs ? "more" : "last" + __entry->is_last ? "last" : "more" ) ); @@ -481,9 +481,9 @@ DECLARE_EVENT_CLASS(xprtrdma_wrch_event, TP_PROTO( \ const struct rpc_task *task, \ struct rpcrdma_mr *mr, \ - int nsegs \ + bool is_last \ ), \ - TP_ARGS(task, mr, nsegs)) + TP_ARGS(task, mr, is_last)) TRACE_DEFINE_ENUM(DMA_BIDIRECTIONAL); TRACE_DEFINE_ENUM(DMA_TO_DEVICE); diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 578b8038b211..573f2df3a2c9 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -37,6 +37,7 @@ EM(rxkad_abort_1_short_encdata, "rxkad1-short-encdata") \ EM(rxkad_abort_1_short_header, "rxkad1-short-hdr") \ EM(rxkad_abort_2_short_check, "rxkad2-short-check") \ + EM(rxkad_abort_2_crypto_unaligned, "rxkad2-crypto-unaligned") \ EM(rxkad_abort_2_short_data, "rxkad2-short-data") \ EM(rxkad_abort_2_short_header, "rxkad2-short-hdr") \ EM(rxkad_abort_2_short_len, "rxkad2-short-len") \ @@ -161,8 +162,6 @@ E_(rxrpc_call_poke_timer_now, "Timer-now") #define rxrpc_skb_traces \ - EM(rxrpc_skb_eaten_by_unshare, "ETN unshare ") \ - EM(rxrpc_skb_eaten_by_unshare_nomem, "ETN unshar-nm") \ EM(rxrpc_skb_get_call_rx, "GET call-rx ") \ EM(rxrpc_skb_get_conn_secured, "GET conn-secd") \ EM(rxrpc_skb_get_conn_work, "GET conn-work") \ @@ -189,6 +188,7 @@ EM(rxrpc_skb_put_purge, "PUT purge ") \ EM(rxrpc_skb_put_purge_oob, "PUT purge-oob") \ EM(rxrpc_skb_put_response, "PUT response ") \ + EM(rxrpc_skb_put_response_copy, "PUT resp-cpy ") \ EM(rxrpc_skb_put_rotate, "PUT rotate ") \ EM(rxrpc_skb_put_unknown, "PUT unknown ") \ EM(rxrpc_skb_see_conn_work, "SEE conn-work") \ @@ -197,6 +197,7 @@ EM(rxrpc_skb_see_recvmsg_oob, "SEE recvm-oob") \ EM(rxrpc_skb_see_reject, "SEE reject ") \ EM(rxrpc_skb_see_rotate, "SEE rotate ") \ + EM(rxrpc_skb_see_unshare_nomem, "SEE unshar-nm") \ E_(rxrpc_skb_see_version, "SEE version ") #define rxrpc_local_traces \ @@ -284,7 +285,6 @@ EM(rxrpc_conn_put_unidle, "PUT unidle ") \ EM(rxrpc_conn_put_work, "PUT work ") \ EM(rxrpc_conn_queue_challenge, "QUE chall ") \ - EM(rxrpc_conn_queue_retry_work, "QUE retry-wk") \ EM(rxrpc_conn_queue_rx_work, "QUE rx-work ") \ EM(rxrpc_conn_see_new_service_conn, "SEE new-svc ") \ EM(rxrpc_conn_see_reap_service, "SEE reap-svc") \ diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index 750ecce56930..ff855197880d 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -1933,7 +1933,9 @@ TRACE_EVENT(svc_stats_latency, svc_xprt_flag(CONG_CTRL) \ svc_xprt_flag(HANDSHAKE) \ svc_xprt_flag(TLS_SESSION) \ - svc_xprt_flag_end(PEER_AUTH) + svc_xprt_flag(PEER_AUTH) \ + svc_xprt_flag(PEER_VALID) \ + svc_xprt_flag_end(RPCB_UNREG) #undef svc_xprt_flag #undef svc_xprt_flag_end diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index e5cd2b80fd29..bdac0d685a98 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -294,7 +294,10 @@ TRACE_EVENT(track_foreign_dirty, __entry->ino = inode ? inode->i_ino : 0; __entry->memcg_id = wb->memcg_css->id; __entry->cgroup_ino = __trace_wb_assign_cgroup(wb); + + rcu_read_lock(); __entry->page_cgroup_ino = cgroup_ino(folio_memcg(folio)->css.cgroup); + rcu_read_unlock(); ), TP_printk("bdi %s[%llu]: ino=%llu memcg_id=%u cgroup_ino=%llu page_cgroup_ino=%llu", diff --git a/include/uapi/fwctl/bnxt.h b/include/uapi/fwctl/bnxt.h new file mode 100644 index 000000000000..32e0bfb9a836 --- /dev/null +++ b/include/uapi/fwctl/bnxt.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2026, Broadcom Inc + */ + +#ifndef _UAPI_FWCTL_BNXT_H_ +#define _UAPI_FWCTL_BNXT_H_ + +#include + +enum fwctl_bnxt_commands { + FWCTL_BNXT_INLINE_COMMANDS = 0, + FWCTL_BNXT_QUERY_COMMANDS, + FWCTL_BNXT_SEND_COMMANDS, +}; + +/** + * struct fwctl_info_bnxt - ioctl(FWCTL_INFO) out_device_data + * @uctx_caps: The command capabilities driver accepts. + * + * Return basic information about the FW interface available. + */ +struct fwctl_info_bnxt { + __u32 uctx_caps; +}; +#endif diff --git a/include/uapi/fwctl/fwctl.h b/include/uapi/fwctl/fwctl.h index 716ac0eee42d..2d6d4049c205 100644 --- a/include/uapi/fwctl/fwctl.h +++ b/include/uapi/fwctl/fwctl.h @@ -44,6 +44,7 @@ enum fwctl_device_type { FWCTL_DEVICE_TYPE_ERROR = 0, FWCTL_DEVICE_TYPE_MLX5 = 1, FWCTL_DEVICE_TYPE_CXL = 2, + FWCTL_DEVICE_TYPE_BNXT = 3, FWCTL_DEVICE_TYPE_PDS = 4, }; diff --git a/include/uapi/linux/baycom.h b/include/uapi/linux/baycom.h deleted file mode 100644 index 478cb565ae52..000000000000 --- a/include/uapi/linux/baycom.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * The Linux BAYCOM driver for the Baycom serial 1200 baud modem - * and the parallel 9600 baud modem - * (C) 1997-1998 by Thomas Sailer, HB9JNX/AE4WA - */ - -#ifndef _BAYCOM_H -#define _BAYCOM_H - -/* -------------------------------------------------------------------- */ -/* - * structs for the IOCTL commands - */ - -struct baycom_debug_data { - unsigned long debug1; - unsigned long debug2; - long debug3; -}; - -struct baycom_ioctl { - int cmd; - union { - struct baycom_debug_data dbg; - } data; -}; - -/* -------------------------------------------------------------------- */ - -/* - * ioctl values change for baycom - */ -#define BAYCOMCTL_GETDEBUG 0x92 - -/* -------------------------------------------------------------------- */ - -#endif /* _BAYCOM_H */ - -/* --------------------------------------------------------------------- */ diff --git a/include/uapi/linux/caif/caif_socket.h b/include/uapi/linux/caif/caif_socket.h deleted file mode 100644 index d9970bbaa156..000000000000 --- a/include/uapi/linux/caif/caif_socket.h +++ /dev/null @@ -1,195 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* linux/caif_socket.h - * CAIF Definitions for CAIF socket and network layer - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef _LINUX_CAIF_SOCKET_H -#define _LINUX_CAIF_SOCKET_H - -#include -#include - -/** - * enum caif_link_selector - Physical Link Selection. - * @CAIF_LINK_HIGH_BANDW: Physical interface for high-bandwidth - * traffic. - * @CAIF_LINK_LOW_LATENCY: Physical interface for low-latency - * traffic. - * - * CAIF Link Layers can register their link properties. - * This enum is used for choosing between CAIF Link Layers when - * setting up CAIF Channels when multiple CAIF Link Layers exists. - */ -enum caif_link_selector { - CAIF_LINK_HIGH_BANDW, - CAIF_LINK_LOW_LATENCY -}; - -/** - * enum caif_channel_priority - CAIF channel priorities. - * - * @CAIF_PRIO_MIN: Min priority for a channel. - * @CAIF_PRIO_LOW: Low-priority channel. - * @CAIF_PRIO_NORMAL: Normal/default priority level. - * @CAIF_PRIO_HIGH: High priority level - * @CAIF_PRIO_MAX: Max priority for channel - * - * Priority can be set on CAIF Channels in order to - * prioritize between traffic on different CAIF Channels. - * These priority levels are recommended, but the priority value - * is not restricted to the values defined in this enum, any value - * between CAIF_PRIO_MIN and CAIF_PRIO_MAX could be used. - */ -enum caif_channel_priority { - CAIF_PRIO_MIN = 0x01, - CAIF_PRIO_LOW = 0x04, - CAIF_PRIO_NORMAL = 0x0f, - CAIF_PRIO_HIGH = 0x14, - CAIF_PRIO_MAX = 0x1F -}; - -/** - * enum caif_protocol_type - CAIF Channel type. - * @CAIFPROTO_AT: Classic AT channel. - * @CAIFPROTO_DATAGRAM: Datagram channel. - * @CAIFPROTO_DATAGRAM_LOOP: Datagram loopback channel, used for testing. - * @CAIFPROTO_UTIL: Utility (Psock) channel. - * @CAIFPROTO_RFM: Remote File Manager - * @CAIFPROTO_DEBUG: Debug link - * - * This enum defines the CAIF Channel type to be used. This defines - * the service to connect to on the modem. - */ -enum caif_protocol_type { - CAIFPROTO_AT, - CAIFPROTO_DATAGRAM, - CAIFPROTO_DATAGRAM_LOOP, - CAIFPROTO_UTIL, - CAIFPROTO_RFM, - CAIFPROTO_DEBUG, - _CAIFPROTO_MAX -}; -#define CAIFPROTO_MAX _CAIFPROTO_MAX - -/** - * enum caif_at_type - AT Service Endpoint - * @CAIF_ATTYPE_PLAIN: Connects to a plain vanilla AT channel. - */ -enum caif_at_type { - CAIF_ATTYPE_PLAIN = 2 -}; - /** - * enum caif_debug_type - Content selection for debug connection - * @CAIF_DEBUG_TRACE_INTERACTIVE: Connection will contain - * both trace and interactive debug. - * @CAIF_DEBUG_TRACE: Connection contains trace only. - * @CAIF_DEBUG_INTERACTIVE: Connection to interactive debug. - */ -enum caif_debug_type { - CAIF_DEBUG_TRACE_INTERACTIVE = 0, - CAIF_DEBUG_TRACE, - CAIF_DEBUG_INTERACTIVE, -}; - -/** - * enum caif_debug_service - Debug Service Endpoint - * @CAIF_RADIO_DEBUG_SERVICE: Debug service on the Radio sub-system - * @CAIF_APP_DEBUG_SERVICE: Debug for the applications sub-system - */ -enum caif_debug_service { - CAIF_RADIO_DEBUG_SERVICE = 1, - CAIF_APP_DEBUG_SERVICE -}; - -/** - * struct sockaddr_caif - the sockaddr structure for CAIF sockets. - * @family: Address family number, must be AF_CAIF. - * @u: Union of address data 'switched' by family. - * : - * @u.at: Applies when family = CAIFPROTO_AT. - * - * @u.at.type: Type of AT link to set up (enum caif_at_type). - * - * @u.util: Applies when family = CAIFPROTO_UTIL - * - * @u.util.service: Utility service name. - * - * @u.dgm: Applies when family = CAIFPROTO_DATAGRAM - * - * @u.dgm.connection_id: Datagram connection id. - * - * @u.dgm.nsapi: NSAPI of the PDP-Context. - * - * @u.rfm: Applies when family = CAIFPROTO_RFM - * - * @u.rfm.connection_id: Connection ID for RFM. - * - * @u.rfm.volume: Volume to mount. - * - * @u.dbg: Applies when family = CAIFPROTO_DEBUG. - * - * @u.dbg.type: Type of debug connection to set up - * (caif_debug_type). - * - * @u.dbg.service: Service sub-system to connect (caif_debug_service - * Description: - * This structure holds the connect parameters used for setting up a - * CAIF Channel. It defines the service to connect to on the modem. - */ -struct sockaddr_caif { - __kernel_sa_family_t family; - union { - struct { - __u8 type; /* type: enum caif_at_type */ - } at; /* CAIFPROTO_AT */ - struct { - char service[16]; - } util; /* CAIFPROTO_UTIL */ - union { - __u32 connection_id; - __u8 nsapi; - } dgm; /* CAIFPROTO_DATAGRAM(_LOOP)*/ - struct { - __u32 connection_id; - char volume[16]; - } rfm; /* CAIFPROTO_RFM */ - struct { - __u8 type; /* type:enum caif_debug_type */ - __u8 service; /* service:caif_debug_service */ - } dbg; /* CAIFPROTO_DEBUG */ - } u; -}; - -/** - * enum caif_socket_opts - CAIF option values for getsockopt and setsockopt. - * - * @CAIFSO_LINK_SELECT: Selector used if multiple CAIF Link layers are - * available. Either a high bandwidth - * link can be selected (CAIF_LINK_HIGH_BANDW) or - * a low latency link (CAIF_LINK_LOW_LATENCY). - * This option is of type __u32. - * Alternatively SO_BINDTODEVICE can be used. - * - * @CAIFSO_REQ_PARAM: Used to set the request parameters for a - * utility channel. (maximum 256 bytes). This - * option must be set before connecting. - * - * @CAIFSO_RSP_PARAM: Gets the response parameters for a utility - * channel. (maximum 256 bytes). This option - * is valid after a successful connect. - * - * - * This enum defines the CAIF Socket options to be used on a socket - * of type PF_CAIF. - * - */ -enum caif_socket_opts { - CAIFSO_LINK_SELECT = 127, - CAIFSO_REQ_PARAM = 128, - CAIFSO_RSP_PARAM = 129, -}; - -#endif /* _LINUX_CAIF_SOCKET_H */ diff --git a/include/uapi/linux/caif/if_caif.h b/include/uapi/linux/caif/if_caif.h deleted file mode 100644 index 74bca19403fa..000000000000 --- a/include/uapi/linux/caif/if_caif.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef IF_CAIF_H_ -#define IF_CAIF_H_ -#include -#include -#include - -/** - * enum ifla_caif - CAIF NetlinkRT parameters. - * @IFLA_CAIF_IPV4_CONNID: Connection ID for IPv4 PDP Context. - * The type of attribute is NLA_U32. - * @IFLA_CAIF_IPV6_CONNID: Connection ID for IPv6 PDP Context. - * The type of attribute is NLA_U32. - * @IFLA_CAIF_LOOPBACK: If different from zero, device is doing loopback - * The type of attribute is NLA_U8. - * - * When using RT Netlink to create, destroy or configure a CAIF IP interface, - * enum ifla_caif is used to specify the configuration attributes. - */ -enum ifla_caif { - __IFLA_CAIF_UNSPEC, - IFLA_CAIF_IPV4_CONNID, - IFLA_CAIF_IPV6_CONNID, - IFLA_CAIF_LOOPBACK, - __IFLA_CAIF_MAX -}; -#define IFLA_CAIF_MAX (__IFLA_CAIF_MAX-1) - -#endif /*IF_CAIF_H_*/ diff --git a/include/uapi/linux/capi.h b/include/uapi/linux/capi.h deleted file mode 100644 index 31f946f8a88d..000000000000 --- a/include/uapi/linux/capi.h +++ /dev/null @@ -1,134 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* $Id: capi.h,v 1.4.6.1 2001/09/23 22:25:05 kai Exp $ - * - * CAPI 2.0 Interface for Linux - * - * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#ifndef __LINUX_CAPI_H__ -#define __LINUX_CAPI_H__ - -#include -#include -#ifndef __KERNEL__ -#include -#endif - -/* - * CAPI_REGISTER - */ - -typedef struct capi_register_params { /* CAPI_REGISTER */ - __u32 level3cnt; /* No. of simulatneous user data connections */ - __u32 datablkcnt; /* No. of buffered data messages */ - __u32 datablklen; /* Size of buffered data messages */ -} capi_register_params; - -#define CAPI_REGISTER _IOW('C',0x01,struct capi_register_params) - -/* - * CAPI_GET_MANUFACTURER - */ - -#define CAPI_MANUFACTURER_LEN 64 - -#define CAPI_GET_MANUFACTURER _IOWR('C',0x06,int) /* broken: wanted size 64 (CAPI_MANUFACTURER_LEN) */ - -/* - * CAPI_GET_VERSION - */ - -typedef struct capi_version { - __u32 majorversion; - __u32 minorversion; - __u32 majormanuversion; - __u32 minormanuversion; -} capi_version; - -#define CAPI_GET_VERSION _IOWR('C',0x07,struct capi_version) - -/* - * CAPI_GET_SERIAL - */ - -#define CAPI_SERIAL_LEN 8 -#define CAPI_GET_SERIAL _IOWR('C',0x08,int) /* broken: wanted size 8 (CAPI_SERIAL_LEN) */ - -/* - * CAPI_GET_PROFILE - */ - -typedef struct capi_profile { - __u16 ncontroller; /* number of installed controller */ - __u16 nbchannel; /* number of B-Channels */ - __u32 goptions; /* global options */ - __u32 support1; /* B1 protocols support */ - __u32 support2; /* B2 protocols support */ - __u32 support3; /* B3 protocols support */ - __u32 reserved[6]; /* reserved */ - __u32 manu[5]; /* manufacturer specific information */ -} capi_profile; - -#define CAPI_GET_PROFILE _IOWR('C',0x09,struct capi_profile) - -typedef struct capi_manufacturer_cmd { - unsigned long cmd; - void __user *data; -} capi_manufacturer_cmd; - -/* - * CAPI_MANUFACTURER_CMD - */ - -#define CAPI_MANUFACTURER_CMD _IOWR('C',0x20, struct capi_manufacturer_cmd) - -/* - * CAPI_GET_ERRCODE - * capi errcode is set, * if read, write, or ioctl returns EIO, - * ioctl returns errcode directly, and in arg, if != 0 - */ - -#define CAPI_GET_ERRCODE _IOR('C',0x21, __u16) - -/* - * CAPI_INSTALLED - */ -#define CAPI_INSTALLED _IOR('C',0x22, __u16) - - -/* - * member contr is input for - * CAPI_GET_MANUFACTURER, CAPI_GET_VERSION, CAPI_GET_SERIAL - * and CAPI_GET_PROFILE - */ -typedef union capi_ioctl_struct { - __u32 contr; - capi_register_params rparams; - __u8 manufacturer[CAPI_MANUFACTURER_LEN]; - capi_version version; - __u8 serial[CAPI_SERIAL_LEN]; - capi_profile profile; - capi_manufacturer_cmd cmd; - __u16 errcode; -} capi_ioctl_struct; - -/* - * Middleware extension - */ - -#define CAPIFLAG_HIGHJACKING 0x0001 - -#define CAPI_GET_FLAGS _IOR('C',0x23, unsigned) -#define CAPI_SET_FLAGS _IOR('C',0x24, unsigned) -#define CAPI_CLR_FLAGS _IOR('C',0x25, unsigned) - -#define CAPI_NCCI_OPENCOUNT _IOR('C',0x26, unsigned) - -#define CAPI_NCCI_GETUNIT _IOR('C',0x27, unsigned) - -#endif /* __LINUX_CAPI_H__ */ diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 70b2b661f42c..13f71202845e 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -657,4 +657,16 @@ struct procmap_query { __u64 build_id_addr; /* in */ }; +/* + * Shutdown the filesystem. + */ +#define FS_IOC_SHUTDOWN _IOR('X', 125, __u32) + +/* + * Flags for FS_IOC_SHUTDOWN + */ +#define FS_SHUTDOWN_FLAGS_DEFAULT 0x0 +#define FS_SHUTDOWN_FLAGS_LOGFLUSH 0x1 /* flush log but not data*/ +#define FS_SHUTDOWN_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */ + #endif /* _UAPI_LINUX_FS_H */ diff --git a/include/uapi/linux/hdlcdrv.h b/include/uapi/linux/hdlcdrv.h deleted file mode 100644 index 9fe9499403a6..000000000000 --- a/include/uapi/linux/hdlcdrv.h +++ /dev/null @@ -1,111 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * hdlcdrv.h -- HDLC packet radio network driver. - * The Linux soundcard driver for 1200 baud and 9600 baud packet radio - * (C) 1996-1998 by Thomas Sailer, HB9JNX/AE4WA - */ - -#ifndef _UAPI_HDLCDRV_H -#define _UAPI_HDLCDRV_H - -/* -------------------------------------------------------------------- */ -/* - * structs for the IOCTL commands - */ - -struct hdlcdrv_params { - int iobase; - int irq; - int dma; - int dma2; - int seriobase; - int pariobase; - int midiiobase; -}; - -struct hdlcdrv_channel_params { - int tx_delay; /* the transmitter keyup delay in 10ms units */ - int tx_tail; /* the transmitter keyoff delay in 10ms units */ - int slottime; /* the slottime in 10ms; usually 10 = 100ms */ - int ppersist; /* the p-persistence 0..255 */ - int fulldup; /* some driver do not support full duplex, setting */ - /* this just makes them send even if DCD is on */ -}; - -struct hdlcdrv_old_channel_state { - int ptt; - int dcd; - int ptt_keyed; -}; - -struct hdlcdrv_channel_state { - int ptt; - int dcd; - int ptt_keyed; - unsigned long tx_packets; - unsigned long tx_errors; - unsigned long rx_packets; - unsigned long rx_errors; -}; - -struct hdlcdrv_ioctl { - int cmd; - union { - struct hdlcdrv_params mp; - struct hdlcdrv_channel_params cp; - struct hdlcdrv_channel_state cs; - struct hdlcdrv_old_channel_state ocs; - unsigned int calibrate; - unsigned char bits; - char modename[128]; - char drivername[32]; - } data; -}; - -/* -------------------------------------------------------------------- */ - -/* - * ioctl values - */ -#define HDLCDRVCTL_GETMODEMPAR 0 -#define HDLCDRVCTL_SETMODEMPAR 1 -#define HDLCDRVCTL_MODEMPARMASK 2 /* not handled by hdlcdrv */ -#define HDLCDRVCTL_GETCHANNELPAR 10 -#define HDLCDRVCTL_SETCHANNELPAR 11 -#define HDLCDRVCTL_OLDGETSTAT 20 -#define HDLCDRVCTL_CALIBRATE 21 -#define HDLCDRVCTL_GETSTAT 22 - -/* - * these are mainly for debugging purposes - */ -#define HDLCDRVCTL_GETSAMPLES 30 -#define HDLCDRVCTL_GETBITS 31 - -/* - * not handled by hdlcdrv, but by its depending drivers - */ -#define HDLCDRVCTL_GETMODE 40 -#define HDLCDRVCTL_SETMODE 41 -#define HDLCDRVCTL_MODELIST 42 -#define HDLCDRVCTL_DRIVERNAME 43 - -/* - * mask of needed modem parameters, returned by HDLCDRVCTL_MODEMPARMASK - */ -#define HDLCDRV_PARMASK_IOBASE (1<<0) -#define HDLCDRV_PARMASK_IRQ (1<<1) -#define HDLCDRV_PARMASK_DMA (1<<2) -#define HDLCDRV_PARMASK_DMA2 (1<<3) -#define HDLCDRV_PARMASK_SERIOBASE (1<<4) -#define HDLCDRV_PARMASK_PARIOBASE (1<<5) -#define HDLCDRV_PARMASK_MIDIIOBASE (1<<6) - -/* -------------------------------------------------------------------- */ - - -/* -------------------------------------------------------------------- */ - -#endif /* _UAPI_HDLCDRV_H */ - -/* -------------------------------------------------------------------- */ diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index 17ac1b785440..909fb7aea638 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -905,7 +905,8 @@ struct io_uring_buf_reg { __u32 ring_entries; __u16 bgid; __u16 flags; - __u64 resv[3]; + __u32 min_left; + __u32 resv[5]; }; /* argument for IORING_REGISTER_PBUF_STATUS */ diff --git a/include/uapi/linux/isdn/capicmd.h b/include/uapi/linux/isdn/capicmd.h deleted file mode 100644 index 5ec88e7548a9..000000000000 --- a/include/uapi/linux/isdn/capicmd.h +++ /dev/null @@ -1,117 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* $Id: capicmd.h,v 1.2.6.2 2001/09/23 22:24:33 kai Exp $ - * - * CAPI 2.0 Interface for Linux - * - * Copyright 1997 by Carsten Paeth - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#ifndef __CAPICMD_H__ -#define __CAPICMD_H__ - -#define CAPI_MSG_BASELEN 8 -#define CAPI_DATA_B3_REQ_LEN (CAPI_MSG_BASELEN+4+4+2+2+2) -#define CAPI_DATA_B3_RESP_LEN (CAPI_MSG_BASELEN+4+2) -#define CAPI_DISCONNECT_B3_RESP_LEN (CAPI_MSG_BASELEN+4) - -/*----- CAPI commands -----*/ -#define CAPI_ALERT 0x01 -#define CAPI_CONNECT 0x02 -#define CAPI_CONNECT_ACTIVE 0x03 -#define CAPI_CONNECT_B3_ACTIVE 0x83 -#define CAPI_CONNECT_B3 0x82 -#define CAPI_CONNECT_B3_T90_ACTIVE 0x88 -#define CAPI_DATA_B3 0x86 -#define CAPI_DISCONNECT_B3 0x84 -#define CAPI_DISCONNECT 0x04 -#define CAPI_FACILITY 0x80 -#define CAPI_INFO 0x08 -#define CAPI_LISTEN 0x05 -#define CAPI_MANUFACTURER 0xff -#define CAPI_RESET_B3 0x87 -#define CAPI_SELECT_B_PROTOCOL 0x41 - -/*----- CAPI subcommands -----*/ - -#define CAPI_REQ 0x80 -#define CAPI_CONF 0x81 -#define CAPI_IND 0x82 -#define CAPI_RESP 0x83 - -/*----- CAPI combined commands -----*/ - -#define CAPICMD(cmd,subcmd) (((cmd)<<8)|(subcmd)) - -#define CAPI_DISCONNECT_REQ CAPICMD(CAPI_DISCONNECT,CAPI_REQ) -#define CAPI_DISCONNECT_CONF CAPICMD(CAPI_DISCONNECT,CAPI_CONF) -#define CAPI_DISCONNECT_IND CAPICMD(CAPI_DISCONNECT,CAPI_IND) -#define CAPI_DISCONNECT_RESP CAPICMD(CAPI_DISCONNECT,CAPI_RESP) - -#define CAPI_ALERT_REQ CAPICMD(CAPI_ALERT,CAPI_REQ) -#define CAPI_ALERT_CONF CAPICMD(CAPI_ALERT,CAPI_CONF) - -#define CAPI_CONNECT_REQ CAPICMD(CAPI_CONNECT,CAPI_REQ) -#define CAPI_CONNECT_CONF CAPICMD(CAPI_CONNECT,CAPI_CONF) -#define CAPI_CONNECT_IND CAPICMD(CAPI_CONNECT,CAPI_IND) -#define CAPI_CONNECT_RESP CAPICMD(CAPI_CONNECT,CAPI_RESP) - -#define CAPI_CONNECT_ACTIVE_REQ CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_REQ) -#define CAPI_CONNECT_ACTIVE_CONF CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_CONF) -#define CAPI_CONNECT_ACTIVE_IND CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_IND) -#define CAPI_CONNECT_ACTIVE_RESP CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_RESP) - -#define CAPI_SELECT_B_PROTOCOL_REQ CAPICMD(CAPI_SELECT_B_PROTOCOL,CAPI_REQ) -#define CAPI_SELECT_B_PROTOCOL_CONF CAPICMD(CAPI_SELECT_B_PROTOCOL,CAPI_CONF) - -#define CAPI_CONNECT_B3_ACTIVE_REQ CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_REQ) -#define CAPI_CONNECT_B3_ACTIVE_CONF CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_CONF) -#define CAPI_CONNECT_B3_ACTIVE_IND CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_IND) -#define CAPI_CONNECT_B3_ACTIVE_RESP CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_RESP) - -#define CAPI_CONNECT_B3_REQ CAPICMD(CAPI_CONNECT_B3,CAPI_REQ) -#define CAPI_CONNECT_B3_CONF CAPICMD(CAPI_CONNECT_B3,CAPI_CONF) -#define CAPI_CONNECT_B3_IND CAPICMD(CAPI_CONNECT_B3,CAPI_IND) -#define CAPI_CONNECT_B3_RESP CAPICMD(CAPI_CONNECT_B3,CAPI_RESP) - - -#define CAPI_CONNECT_B3_T90_ACTIVE_IND CAPICMD(CAPI_CONNECT_B3_T90_ACTIVE,CAPI_IND) -#define CAPI_CONNECT_B3_T90_ACTIVE_RESP CAPICMD(CAPI_CONNECT_B3_T90_ACTIVE,CAPI_RESP) - -#define CAPI_DATA_B3_REQ CAPICMD(CAPI_DATA_B3,CAPI_REQ) -#define CAPI_DATA_B3_CONF CAPICMD(CAPI_DATA_B3,CAPI_CONF) -#define CAPI_DATA_B3_IND CAPICMD(CAPI_DATA_B3,CAPI_IND) -#define CAPI_DATA_B3_RESP CAPICMD(CAPI_DATA_B3,CAPI_RESP) - -#define CAPI_DISCONNECT_B3_REQ CAPICMD(CAPI_DISCONNECT_B3,CAPI_REQ) -#define CAPI_DISCONNECT_B3_CONF CAPICMD(CAPI_DISCONNECT_B3,CAPI_CONF) -#define CAPI_DISCONNECT_B3_IND CAPICMD(CAPI_DISCONNECT_B3,CAPI_IND) -#define CAPI_DISCONNECT_B3_RESP CAPICMD(CAPI_DISCONNECT_B3,CAPI_RESP) - -#define CAPI_RESET_B3_REQ CAPICMD(CAPI_RESET_B3,CAPI_REQ) -#define CAPI_RESET_B3_CONF CAPICMD(CAPI_RESET_B3,CAPI_CONF) -#define CAPI_RESET_B3_IND CAPICMD(CAPI_RESET_B3,CAPI_IND) -#define CAPI_RESET_B3_RESP CAPICMD(CAPI_RESET_B3,CAPI_RESP) - -#define CAPI_LISTEN_REQ CAPICMD(CAPI_LISTEN,CAPI_REQ) -#define CAPI_LISTEN_CONF CAPICMD(CAPI_LISTEN,CAPI_CONF) - -#define CAPI_MANUFACTURER_REQ CAPICMD(CAPI_MANUFACTURER,CAPI_REQ) -#define CAPI_MANUFACTURER_CONF CAPICMD(CAPI_MANUFACTURER,CAPI_CONF) -#define CAPI_MANUFACTURER_IND CAPICMD(CAPI_MANUFACTURER,CAPI_IND) -#define CAPI_MANUFACTURER_RESP CAPICMD(CAPI_MANUFACTURER,CAPI_RESP) - -#define CAPI_FACILITY_REQ CAPICMD(CAPI_FACILITY,CAPI_REQ) -#define CAPI_FACILITY_CONF CAPICMD(CAPI_FACILITY,CAPI_CONF) -#define CAPI_FACILITY_IND CAPICMD(CAPI_FACILITY,CAPI_IND) -#define CAPI_FACILITY_RESP CAPICMD(CAPI_FACILITY,CAPI_RESP) - -#define CAPI_INFO_REQ CAPICMD(CAPI_INFO,CAPI_REQ) -#define CAPI_INFO_CONF CAPICMD(CAPI_INFO,CAPI_CONF) -#define CAPI_INFO_IND CAPICMD(CAPI_INFO,CAPI_IND) -#define CAPI_INFO_RESP CAPICMD(CAPI_INFO,CAPI_RESP) - -#endif /* __CAPICMD_H__ */ diff --git a/include/uapi/linux/kernelcapi.h b/include/uapi/linux/kernelcapi.h deleted file mode 100644 index 325a856e0e20..000000000000 --- a/include/uapi/linux/kernelcapi.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * $Id: kernelcapi.h,v 1.8.6.2 2001/02/07 11:31:31 kai Exp $ - * - * Kernel CAPI 2.0 Interface for Linux - * - * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) - * - */ - -#ifndef _UAPI__KERNELCAPI_H__ -#define _UAPI__KERNELCAPI_H__ - -#define CAPI_MAXAPPL 240 /* maximum number of applications */ -#define CAPI_MAXCONTR 32 /* maximum number of controller */ -#define CAPI_MAXDATAWINDOW 8 - - -typedef struct kcapi_flagdef { - int contr; - int flag; -} kcapi_flagdef; - -typedef struct kcapi_carddef { - char driver[32]; - unsigned int port; - unsigned irq; - unsigned int membase; - int cardnr; -} kcapi_carddef; - -/* new ioctls >= 10 */ -#define KCAPI_CMD_TRACE 10 -#define KCAPI_CMD_ADDCARD 11 /* OBSOLETE */ - -/* - * flag > 2 => trace also data - * flag & 1 => show trace - */ -#define KCAPI_TRACE_OFF 0 -#define KCAPI_TRACE_SHORT_NO_DATA 1 -#define KCAPI_TRACE_FULL_NO_DATA 2 -#define KCAPI_TRACE_SHORT 3 -#define KCAPI_TRACE_FULL 4 - - - -#endif /* _UAPI__KERNELCAPI_H__ */ diff --git a/include/uapi/linux/keyboard.h b/include/uapi/linux/keyboard.h index 36d230cedf12..48ecb0cefb45 100644 --- a/include/uapi/linux/keyboard.h +++ b/include/uapi/linux/keyboard.h @@ -41,6 +41,7 @@ #define KT_SLOCK 12 #define KT_DEAD2 13 #define KT_BRL 14 +#define KT_CSI 15 /* CSI sequences with modifier support */ #define K(t,v) (((t)<<8)|(v)) #define KTYP(x) ((x) >> 8) @@ -461,5 +462,33 @@ #define NR_BRL 11 +/* KT_CSI keys: value is the CSI parameter number for ESC [ ~ */ +#define K_CSI_HOME K(KT_CSI, 1) /* ESC [ 1 ~ */ +#define K_CSI_INSERT K(KT_CSI, 2) /* ESC [ 2 ~ */ +#define K_CSI_DELETE K(KT_CSI, 3) /* ESC [ 3 ~ */ +#define K_CSI_END K(KT_CSI, 4) /* ESC [ 4 ~ */ +#define K_CSI_PGUP K(KT_CSI, 5) /* ESC [ 5 ~ */ +#define K_CSI_PGDN K(KT_CSI, 6) /* ESC [ 6 ~ */ +#define K_CSI_F1 K(KT_CSI, 11) /* ESC [ 11 ~ */ +#define K_CSI_F2 K(KT_CSI, 12) /* ESC [ 12 ~ */ +#define K_CSI_F3 K(KT_CSI, 13) /* ESC [ 13 ~ */ +#define K_CSI_F4 K(KT_CSI, 14) /* ESC [ 14 ~ */ +#define K_CSI_F5 K(KT_CSI, 15) /* ESC [ 15 ~ */ +#define K_CSI_F6 K(KT_CSI, 17) /* ESC [ 17 ~ */ +#define K_CSI_F7 K(KT_CSI, 18) /* ESC [ 18 ~ */ +#define K_CSI_F8 K(KT_CSI, 19) /* ESC [ 19 ~ */ +#define K_CSI_F9 K(KT_CSI, 20) /* ESC [ 20 ~ */ +#define K_CSI_F10 K(KT_CSI, 21) /* ESC [ 21 ~ */ +#define K_CSI_F11 K(KT_CSI, 23) /* ESC [ 23 ~ */ +#define K_CSI_F12 K(KT_CSI, 24) /* ESC [ 24 ~ */ +#define K_CSI_F13 K(KT_CSI, 25) /* ESC [ 25 ~ */ +#define K_CSI_F14 K(KT_CSI, 26) /* ESC [ 26 ~ */ +#define K_CSI_F15 K(KT_CSI, 28) /* ESC [ 28 ~ */ +#define K_CSI_F16 K(KT_CSI, 29) /* ESC [ 29 ~ */ +#define K_CSI_F17 K(KT_CSI, 31) /* ESC [ 31 ~ */ +#define K_CSI_F18 K(KT_CSI, 32) /* ESC [ 32 ~ */ +#define K_CSI_F19 K(KT_CSI, 33) /* ESC [ 33 ~ */ +#define K_CSI_F20 K(KT_CSI, 34) /* ESC [ 34 ~ */ + #define MAX_DIACR 256 #endif /* _UAPI__LINUX_KEYBOARD_H */ diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 3f0d8d3c3daf..6c8afa2047bf 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -704,6 +704,11 @@ struct kvm_enable_cap { #define KVM_VM_TYPE_ARM_IPA_SIZE_MASK 0xffULL #define KVM_VM_TYPE_ARM_IPA_SIZE(x) \ ((x) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK) + +#define KVM_VM_TYPE_ARM_PROTECTED (1UL << 31) +#define KVM_VM_TYPE_ARM_MASK (KVM_VM_TYPE_ARM_IPA_SIZE_MASK | \ + KVM_VM_TYPE_ARM_PROTECTED) + /* * ioctls for /dev/kvm fds: */ @@ -990,6 +995,7 @@ struct kvm_enable_cap { #define KVM_CAP_ARM_SEA_TO_USER 245 #define KVM_CAP_S390_USER_OPEREXEC 246 #define KVM_CAP_S390_KEYOP 247 +#define KVM_CAP_S390_VSIE_ESAMODE 248 struct kvm_irq_routing_irqchip { __u32 irqchip; @@ -1225,6 +1231,10 @@ enum kvm_device_type { #define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_PCHPIC, #define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC + KVM_DEV_TYPE_LOONGARCH_DMSINTC, +#define KVM_DEV_TYPE_LOONGARCH_DMSINTC KVM_DEV_TYPE_LOONGARCH_DMSINTC + KVM_DEV_TYPE_ARM_VGIC_V5, +#define KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_MAX, diff --git a/include/uapi/linux/map_benchmark.h b/include/uapi/linux/map_benchmark.h index c2d91088a40d..4b17829a9f17 100644 --- a/include/uapi/linux/map_benchmark.h +++ b/include/uapi/linux/map_benchmark.h @@ -17,6 +17,12 @@ #define DMA_MAP_TO_DEVICE 1 #define DMA_MAP_FROM_DEVICE 2 +enum { + DMA_MAP_BENCH_SINGLE_MODE, + DMA_MAP_BENCH_SG_MODE, + DMA_MAP_BENCH_MODE_MAX +}; + struct map_benchmark { __u64 avg_map_100ns; /* average map latency in 100ns */ __u64 map_stddev; /* standard deviation of map latency */ @@ -28,8 +34,11 @@ struct map_benchmark { __u32 dma_bits; /* DMA addressing capability */ __u32 dma_dir; /* DMA data direction */ __u32 dma_trans_ns; /* time for DMA transmission in ns */ - __u32 granule; /* how many PAGE_SIZE will do map/unmap once a time */ - __u8 expansion[76]; /* For future use */ + __u32 granule; /* - SINGLE_MODE: number of pages mapped/unmapped per operation + * - SG_MODE: number of scatterlist entries (each maps one page) + */ + __u8 map_mode; /* the mode of dma map */ + __u8 expansion[75]; /* For future use */ }; #endif /* _UAPI_DMA_BENCHMARK_H */ diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h index e0645a34b55b..32ff92b6342b 100644 --- a/include/uapi/linux/mshv.h +++ b/include/uapi/linux/mshv.h @@ -357,7 +357,7 @@ struct mshv_vtl_sint_post_msg { struct mshv_vtl_ram_disposition { __u64 start_pfn; - __u64 last_pfn; + __u64 last_pfn; /* last_pfn is excluded from the range [start_pfn, last_pfn) */ }; struct mshv_vtl_set_poll_file { diff --git a/include/uapi/linux/netrom.h b/include/uapi/linux/netrom.h deleted file mode 100644 index 7498ea3c3940..000000000000 --- a/include/uapi/linux/netrom.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * These are the public elements of the Linux kernel NET/ROM implementation. - * For kernel AX.25 see the file ax25.h. This file requires ax25.h for the - * definition of the ax25_address structure. - */ - -#ifndef NETROM_KERNEL_H -#define NETROM_KERNEL_H - -#include - -#define NETROM_MTU 236 - -#define NETROM_T1 1 -#define NETROM_T2 2 -#define NETROM_N2 3 -#define NETROM_T4 6 -#define NETROM_IDLE 7 - -#define SIOCNRDECOBS (SIOCPROTOPRIVATE+2) - -struct nr_route_struct { -#define NETROM_NEIGH 0 -#define NETROM_NODE 1 - int type; - ax25_address callsign; - char device[16]; - unsigned int quality; - char mnemonic[7]; - ax25_address neighbour; - unsigned int obs_count; - unsigned int ndigis; - ax25_address digipeaters[AX25_MAX_DIGIS]; -}; - -#endif diff --git a/include/uapi/linux/nfsd/export.h b/include/uapi/linux/nfsd/export.h index a73ca3703abb..de647cf166c3 100644 --- a/include/uapi/linux/nfsd/export.h +++ b/include/uapi/linux/nfsd/export.h @@ -34,7 +34,7 @@ #define NFSEXP_GATHERED_WRITES 0x0020 #define NFSEXP_NOREADDIRPLUS 0x0040 #define NFSEXP_SECURITY_LABEL 0x0080 -/* 0x100 currently unused */ +#define NFSEXP_SIGN_FH 0x0100 #define NFSEXP_NOHIDE 0x0200 #define NFSEXP_NOSUBTREECHECK 0x0400 #define NFSEXP_NOAUTHNLM 0x0800 /* Don't authenticate NLM requests - just trust */ @@ -55,7 +55,7 @@ #define NFSEXP_PNFS 0x20000 /* All flags that we claim to support. (Note we don't support NOACL.) */ -#define NFSEXP_ALLFLAGS 0x3FEFF +#define NFSEXP_ALLFLAGS 0x3FFFF /* The flags that may vary depending on security flavor: */ #define NFSEXP_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \ diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h index e9efbc9e63d8..97c7447f4d14 100644 --- a/include/uapi/linux/nfsd_netlink.h +++ b/include/uapi/linux/nfsd_netlink.h @@ -36,6 +36,7 @@ enum { NFSD_A_SERVER_LEASETIME, NFSD_A_SERVER_SCOPE, NFSD_A_SERVER_MIN_THREADS, + NFSD_A_SERVER_FH_KEY, __NFSD_A_SERVER_MAX, NFSD_A_SERVER_MAX = (__NFSD_A_SERVER_MAX - 1) diff --git a/include/uapi/linux/rose.h b/include/uapi/linux/rose.h deleted file mode 100644 index 19aa4693c8fc..000000000000 --- a/include/uapi/linux/rose.h +++ /dev/null @@ -1,91 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * These are the public elements of the Linux kernel Rose implementation. - * For kernel AX.25 see the file ax25.h. This file requires ax25.h for the - * definition of the ax25_address structure. - */ - -#ifndef ROSE_KERNEL_H -#define ROSE_KERNEL_H - -#include -#include - -#define ROSE_MTU 251 - -#define ROSE_MAX_DIGIS 6 - -#define ROSE_DEFER 1 -#define ROSE_T1 2 -#define ROSE_T2 3 -#define ROSE_T3 4 -#define ROSE_IDLE 5 -#define ROSE_QBITINCL 6 -#define ROSE_HOLDBACK 7 - -#define SIOCRSGCAUSE (SIOCPROTOPRIVATE+0) -#define SIOCRSSCAUSE (SIOCPROTOPRIVATE+1) -#define SIOCRSL2CALL (SIOCPROTOPRIVATE+2) -#define SIOCRSSL2CALL (SIOCPROTOPRIVATE+2) -#define SIOCRSACCEPT (SIOCPROTOPRIVATE+3) -#define SIOCRSCLRRT (SIOCPROTOPRIVATE+4) -#define SIOCRSGL2CALL (SIOCPROTOPRIVATE+5) -#define SIOCRSGFACILITIES (SIOCPROTOPRIVATE+6) - -#define ROSE_DTE_ORIGINATED 0x00 -#define ROSE_NUMBER_BUSY 0x01 -#define ROSE_INVALID_FACILITY 0x03 -#define ROSE_NETWORK_CONGESTION 0x05 -#define ROSE_OUT_OF_ORDER 0x09 -#define ROSE_ACCESS_BARRED 0x0B -#define ROSE_NOT_OBTAINABLE 0x0D -#define ROSE_REMOTE_PROCEDURE 0x11 -#define ROSE_LOCAL_PROCEDURE 0x13 -#define ROSE_SHIP_ABSENT 0x39 - -typedef struct { - char rose_addr[5]; -} rose_address; - -struct sockaddr_rose { - __kernel_sa_family_t srose_family; - rose_address srose_addr; - ax25_address srose_call; - int srose_ndigis; - ax25_address srose_digi; -}; - -struct full_sockaddr_rose { - __kernel_sa_family_t srose_family; - rose_address srose_addr; - ax25_address srose_call; - unsigned int srose_ndigis; - ax25_address srose_digis[ROSE_MAX_DIGIS]; -}; - -struct rose_route_struct { - rose_address address; - unsigned short mask; - ax25_address neighbour; - char device[16]; - unsigned char ndigis; - ax25_address digipeaters[AX25_MAX_DIGIS]; -}; - -struct rose_cause_struct { - unsigned char cause; - unsigned char diagnostic; -}; - -struct rose_facilities_struct { - rose_address source_addr, dest_addr; - ax25_address source_call, dest_call; - unsigned char source_ndigis, dest_ndigis; - ax25_address source_digis[ROSE_MAX_DIGIS]; - ax25_address dest_digis[ROSE_MAX_DIGIS]; - unsigned int rand; - rose_address fail_addr; - ax25_address fail_call; -}; - -#endif diff --git a/include/uapi/linux/rseq.h b/include/uapi/linux/rseq.h index f69344fe6c08..ca6fe1f9d05e 100644 --- a/include/uapi/linux/rseq.h +++ b/include/uapi/linux/rseq.h @@ -28,7 +28,7 @@ enum rseq_cs_flags_bit { RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT_BIT = 0, RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL_BIT = 1, RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT = 2, - /* (3) Intentional gap to put new bits into a separate byte */ + /* (3) Intentional gap to keep new bits separate */ /* User read only feature flags */ RSEQ_CS_FLAG_SLICE_EXT_AVAILABLE_BIT = 4, @@ -161,6 +161,9 @@ struct rseq { * - RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT * - RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL * - RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE + * + * It is now used for feature status advertisement by the kernel. + * See: enum rseq_cs_flags_bit for further information. */ __u32 flags; diff --git a/include/uapi/linux/scc.h b/include/uapi/linux/scc.h deleted file mode 100644 index 947edb17ce9d..000000000000 --- a/include/uapi/linux/scc.h +++ /dev/null @@ -1,174 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* $Id: scc.h,v 1.29 1997/04/02 14:56:45 jreuter Exp jreuter $ */ - -#ifndef _UAPI_SCC_H -#define _UAPI_SCC_H - -#include - -/* selection of hardware types */ - -#define PA0HZP 0x00 /* hardware type for PA0HZP SCC card and compatible */ -#define EAGLE 0x01 /* hardware type for EAGLE card */ -#define PC100 0x02 /* hardware type for PC100 card */ -#define PRIMUS 0x04 /* hardware type for PRIMUS-PC (DG9BL) card */ -#define DRSI 0x08 /* hardware type for DRSI PC*Packet card */ -#define BAYCOM 0x10 /* hardware type for BayCom (U)SCC */ - -/* DEV ioctl() commands */ - -enum SCC_ioctl_cmds { - SIOCSCCRESERVED = SIOCDEVPRIVATE, - SIOCSCCCFG, - SIOCSCCINI, - SIOCSCCCHANINI, - SIOCSCCSMEM, - SIOCSCCGKISS, - SIOCSCCSKISS, - SIOCSCCGSTAT, - SIOCSCCCAL -}; - -/* Device parameter control (from WAMPES) */ - -enum L1_params { - PARAM_DATA, - PARAM_TXDELAY, - PARAM_PERSIST, - PARAM_SLOTTIME, - PARAM_TXTAIL, - PARAM_FULLDUP, - PARAM_SOFTDCD, /* was: PARAM_HW */ - PARAM_MUTE, /* ??? */ - PARAM_DTR, - PARAM_RTS, - PARAM_SPEED, - PARAM_ENDDELAY, /* ??? */ - PARAM_GROUP, - PARAM_IDLE, - PARAM_MIN, - PARAM_MAXKEY, - PARAM_WAIT, - PARAM_MAXDEFER, - PARAM_TX, - PARAM_HWEVENT = 31, - PARAM_RETURN = 255 /* reset kiss mode */ -}; - -/* fulldup parameter */ - -enum FULLDUP_modes { - KISS_DUPLEX_HALF, /* normal CSMA operation */ - KISS_DUPLEX_FULL, /* fullduplex, key down trx after transmission */ - KISS_DUPLEX_LINK, /* fullduplex, key down trx after 'idletime' sec */ - KISS_DUPLEX_OPTIMA /* fullduplex, let the protocol layer control the hw */ -}; - -/* misc. parameters */ - -#define TIMER_OFF 65535U /* to switch off timers */ -#define NO_SUCH_PARAM 65534U /* param not implemented */ - -/* HWEVENT parameter */ - -enum HWEVENT_opts { - HWEV_DCD_ON, - HWEV_DCD_OFF, - HWEV_ALL_SENT -}; - -/* channel grouping */ - -#define RXGROUP 0100 /* if set, only tx when all channels clear */ -#define TXGROUP 0200 /* if set, don't transmit simultaneously */ - -/* Tx/Rx clock sources */ - -enum CLOCK_sources { - CLK_DPLL, /* normal halfduplex operation */ - CLK_EXTERNAL, /* external clocking (G3RUH/DF9IC modems) */ - CLK_DIVIDER, /* Rx = DPLL, Tx = divider (fullduplex with */ - /* modems without clock regeneration */ - CLK_BRG /* experimental fullduplex mode with DPLL/BRG for */ - /* MODEMs without clock recovery */ -}; - -/* Tx state */ - -enum TX_state { - TXS_IDLE, /* Transmitter off, no data pending */ - TXS_BUSY, /* waiting for permission to send / tailtime */ - TXS_ACTIVE, /* Transmitter on, sending data */ - TXS_NEWFRAME, /* reset CRC and send (next) frame */ - TXS_IDLE2, /* Transmitter on, no data pending */ - TXS_WAIT, /* Waiting for Mintime to expire */ - TXS_TIMEOUT /* We had a transmission timeout */ -}; - -typedef unsigned long io_port; /* type definition for an 'io port address' */ - -/* SCC statistical information */ - -struct scc_stat { - long rxints; /* Receiver interrupts */ - long txints; /* Transmitter interrupts */ - long exints; /* External/status interrupts */ - long spints; /* Special receiver interrupts */ - - long txframes; /* Packets sent */ - long rxframes; /* Number of Frames Actually Received */ - long rxerrs; /* CRC Errors */ - long txerrs; /* KISS errors */ - - unsigned int nospace; /* "Out of buffers" */ - unsigned int rx_over; /* Receiver Overruns */ - unsigned int tx_under; /* Transmitter Underruns */ - - unsigned int tx_state; /* Transmitter state */ - int tx_queued; /* tx frames enqueued */ - - unsigned int maxqueue; /* allocated tx_buffers */ - unsigned int bufsize; /* used buffersize */ -}; - -struct scc_modem { - long speed; /* Line speed, bps */ - char clocksrc; /* 0 = DPLL, 1 = external, 2 = divider */ - char nrz; /* NRZ instead of NRZI */ -}; - -struct scc_kiss_cmd { - int command; /* one of the KISS-Commands defined above */ - unsigned param; /* KISS-Param */ -}; - -struct scc_hw_config { - io_port data_a; /* data port channel A */ - io_port ctrl_a; /* control port channel A */ - io_port data_b; /* data port channel B */ - io_port ctrl_b; /* control port channel B */ - io_port vector_latch; /* INTACK-Latch (#) */ - io_port special; /* special function port */ - - int irq; /* irq */ - long clock; /* clock */ - char option; /* command for function port */ - - char brand; /* hardware type */ - char escc; /* use ext. features of a 8580/85180/85280 */ -}; - -/* (#) only one INTACK latch allowed. */ - - -struct scc_mem_config { - unsigned int dummy; - unsigned int bufsize; -}; - -struct scc_calibrate { - unsigned int time; - unsigned char pattern; -}; - -#endif /* _UAPI_SCC_H */ diff --git a/include/uapi/linux/trace_mmap.h b/include/uapi/linux/trace_mmap.h index e8185889a1c8..7e5e3900d39f 100644 --- a/include/uapi/linux/trace_mmap.h +++ b/include/uapi/linux/trace_mmap.h @@ -10,6 +10,7 @@ * @meta_struct_len: Size of this structure. * @subbuf_size: Size of each sub-buffer. * @nr_subbufs: Number of subbfs in the ring-buffer, including the reader. + * @reader: The reader composite info structure * @reader.lost_events: Number of events lost at the time of the reader swap. * @reader.id: subbuf ID of the current reader. ID range [0 : @nr_subbufs - 1] * @reader.read: Number of bytes read on the reader subbuf. diff --git a/include/uapi/linux/um_timetravel.h b/include/uapi/linux/um_timetravel.h index 546a690b0346..fa7c75334f2e 100644 --- a/include/uapi/linux/um_timetravel.h +++ b/include/uapi/linux/um_timetravel.h @@ -56,6 +56,9 @@ enum um_timetravel_shared_mem_fds { * in the control message */ UM_TIMETRAVEL_SHARED_LOGFD, + /** + * @UM_TIMETRAVEL_SHARED_MAX_FDS: number of fds listed here + */ UM_TIMETRAVEL_SHARED_MAX_FDS, }; @@ -242,6 +245,7 @@ union um_timetravel_schedshm_client { __u64 req_time; __u64 name; }; + /* private: */ char reserve[128]; /* reserved for future usage */ }; @@ -264,7 +268,7 @@ union um_timetravel_schedshm_client { * is made by any client. Clients also must update this value when they * insert/update an own request into the shared memory while not running * themselves, and the new request is before than the current value. - * current_time: Current time, can only be set by the client in running state + * @current_time: Current time, can only be set by the client in running state * (indicated by @running_id), though that client may only run until @free_until, * so it must remain smaller than @free_until. * @running_id: The current client in state running, set before a client is diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index 8003243a4937..c3e593378377 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -102,6 +102,8 @@ #define USB_REQ_LOOPBACK_DATA_WRITE 0x15 #define USB_REQ_LOOPBACK_DATA_READ 0x16 #define USB_REQ_SET_INTERFACE_DS 0x17 +#define USB_REQ_AUTH_IN 0x18 +#define USB_REQ_AUTH_OUT 0x19 /* specific requests for USB Power Delivery */ #define USB_REQ_GET_PARTNER_PDO 20 @@ -121,15 +123,17 @@ * are at most sixteen features of each type.) Hubs may also support a * new USB_REQ_TEST_AND_SET_FEATURE to put ports into L1 suspend. */ -#define USB_DEVICE_SELF_POWERED 0 /* (read only) */ -#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */ -#define USB_DEVICE_TEST_MODE 2 /* (wired high speed only) */ -#define USB_DEVICE_BATTERY 2 /* (wireless) */ -#define USB_DEVICE_B_HNP_ENABLE 3 /* (otg) dev may initiate HNP */ -#define USB_DEVICE_WUSB_DEVICE 3 /* (wireless)*/ -#define USB_DEVICE_A_HNP_SUPPORT 4 /* (otg) RH port supports HNP */ -#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */ -#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */ +#define USB_DEVICE_SELF_POWERED 0 /* (read only) */ +#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */ +#define USB_DEVICE_TEST_MODE 2 /* (wired high speed only) */ +#define USB_DEVICE_BATTERY 2 /* (wireless) */ +#define USB_DEVICE_B_HNP_ENABLE 3 /* (otg) dev may initiate HNP */ +#define USB_DEVICE_WUSB_DEVICE 3 /* (wireless)*/ +#define USB_DEVICE_A_HNP_SUPPORT 4 /* (otg) RH port supports HNP */ +#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */ +#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */ + +#define USB_DEVICE_BULK_MAX_PACKET_UPDATE 8 /* (eUSB2v2) bump maxpacket to 1024 */ /* * Test Mode Selectors @@ -1147,6 +1151,17 @@ struct usb_ptm_cap_descriptor { /*-------------------------------------------------------------------------*/ +struct usb_authentication_capability_descriptor { + __u8 bLength; + __u8 bDescriptorType; /* set to USB_DT_DEVICE_CAPABILITY */ + __u8 bmAttributes; + + __u8 bcdProtocolVersion; + __u8 bcdCapability; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + /* USB_DT_WIRELESS_ENDPOINT_COMP: companion descriptor associated with * each endpoint descriptor for a wireless device */ diff --git a/include/uapi/rdma/bnxt_re-abi.h b/include/uapi/rdma/bnxt_re-abi.h index f24edf1c75eb..40955eaba32e 100644 --- a/include/uapi/rdma/bnxt_re-abi.h +++ b/include/uapi/rdma/bnxt_re-abi.h @@ -102,12 +102,17 @@ struct bnxt_re_pd_resp { struct bnxt_re_cq_req { __aligned_u64 cq_va; __aligned_u64 cq_handle; + __aligned_u64 comp_mask; }; -enum bnxt_re_cq_mask { +enum bnxt_re_resp_cq_mask { BNXT_RE_CQ_TOGGLE_PAGE_SUPPORT = 0x1, }; +enum bnxt_re_req_cq_mask { + BNXT_RE_CQ_FIXED_NUM_CQE_ENABLE = 0x1, +}; + struct bnxt_re_cq_resp { __u32 cqid; __u32 tail; @@ -163,6 +168,8 @@ enum bnxt_re_objects { BNXT_RE_OBJECT_ALLOC_PAGE = (1U << UVERBS_ID_NS_SHIFT), BNXT_RE_OBJECT_NOTIFY_DRV, BNXT_RE_OBJECT_GET_TOGGLE_MEM, + BNXT_RE_OBJECT_DBR, + BNXT_RE_OBJECT_DEFAULT_DBR, }; enum bnxt_re_alloc_page_type { @@ -231,4 +238,31 @@ struct bnxt_re_packet_pacing_caps { struct bnxt_re_query_device_ex_resp { struct bnxt_re_packet_pacing_caps packet_pacing_caps; }; + +struct bnxt_re_db_region { + __u32 dpi; + __u32 reserved; + __aligned_u64 umdbr; +}; + +enum bnxt_re_obj_dbr_alloc_attrs { + BNXT_RE_ALLOC_DBR_HANDLE = (1U << UVERBS_ID_NS_SHIFT), + BNXT_RE_ALLOC_DBR_ATTR, + BNXT_RE_ALLOC_DBR_OFFSET, +}; + +enum bnxt_re_obj_dbr_free_attrs { + BNXT_RE_FREE_DBR_HANDLE = (1U << UVERBS_ID_NS_SHIFT), +}; + +enum bnxt_re_obj_default_dbr_attrs { + BNXT_RE_DEFAULT_DBR_ATTR = (1U << UVERBS_ID_NS_SHIFT), +}; + +enum bnxt_re_obj_dpi_methods { + BNXT_RE_METHOD_DBR_ALLOC = (1U << UVERBS_ID_NS_SHIFT), + BNXT_RE_METHOD_DBR_FREE, + BNXT_RE_METHOD_GET_DEFAULT_DBR, +}; + #endif /* __BNXT_RE_UVERBS_ABI_H__*/ diff --git a/include/uapi/rdma/efa-abi.h b/include/uapi/rdma/efa-abi.h index 98b71b9979f8..d5c18f8de182 100644 --- a/include/uapi/rdma/efa-abi.h +++ b/include/uapi/rdma/efa-abi.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ /* - * Copyright 2018-2025 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2026 Amazon.com, Inc. or its affiliates. All rights reserved. */ #ifndef EFA_ABI_USER_H @@ -22,12 +22,12 @@ */ enum { - EFA_ALLOC_UCONTEXT_CMD_COMP_TX_BATCH = 1 << 0, - EFA_ALLOC_UCONTEXT_CMD_COMP_MIN_SQ_WR = 1 << 1, + EFA_ALLOC_UCONTEXT_CMD_SUPP_CAPS_TX_BATCH = 1 << 0, + EFA_ALLOC_UCONTEXT_CMD_SUPP_CAPS_MIN_SQ_WR = 1 << 1, }; struct efa_ibv_alloc_ucontext_cmd { - __u32 comp_mask; + __u32 supported_caps; __u8 reserved_20[4]; }; @@ -44,7 +44,8 @@ struct efa_ibv_alloc_ucontext_resp { __u32 max_llq_size; /* bytes */ __u16 max_tx_batch; /* units of 64 bytes */ __u16 min_sq_wr; - __u8 reserved_a0[4]; + __u16 inline_buf_size_ex; + __u8 reserved_b0[2]; }; struct efa_ibv_alloc_pd_resp { diff --git a/include/uapi/rdma/ib_user_ioctl_verbs.h b/include/uapi/rdma/ib_user_ioctl_verbs.h index 89e6a3f13191..90c5cd8e7753 100644 --- a/include/uapi/rdma/ib_user_ioctl_verbs.h +++ b/include/uapi/rdma/ib_user_ioctl_verbs.h @@ -46,6 +46,7 @@ enum ib_uverbs_core_support { IB_UVERBS_CORE_SUPPORT_OPTIONAL_MR_ACCESS = 1 << 0, + IB_UVERBS_CORE_SUPPORT_ROBUST_UDATA = 1 << 1, }; enum ib_uverbs_access_flags { diff --git a/include/uapi/rdma/mlx5_user_ioctl_cmds.h b/include/uapi/rdma/mlx5_user_ioctl_cmds.h index 18f9fe070213..01a2a050e468 100644 --- a/include/uapi/rdma/mlx5_user_ioctl_cmds.h +++ b/include/uapi/rdma/mlx5_user_ioctl_cmds.h @@ -139,6 +139,7 @@ enum mlx5_ib_var_alloc_attrs { MLX5_IB_ATTR_VAR_OBJ_ALLOC_MMAP_OFFSET, MLX5_IB_ATTR_VAR_OBJ_ALLOC_MMAP_LENGTH, MLX5_IB_ATTR_VAR_OBJ_ALLOC_PAGE_ID, + MLX5_IB_ATTR_VAR_OBJ_ALLOC_FLAGS, }; enum mlx5_ib_var_obj_destroy_attrs { diff --git a/include/uapi/rdma/mlx5_user_ioctl_verbs.h b/include/uapi/rdma/mlx5_user_ioctl_verbs.h index 8f86e79d78a5..ef295b38a1cf 100644 --- a/include/uapi/rdma/mlx5_user_ioctl_verbs.h +++ b/include/uapi/rdma/mlx5_user_ioctl_verbs.h @@ -100,6 +100,10 @@ enum mlx5_ib_uapi_query_port_flags { MLX5_IB_UAPI_QUERY_PORT_ESW_OWNER_VHCA_ID = 1 << 5, }; +enum mlx5_ib_uapi_var_alloc_flags { + MLX5_IB_UAPI_VAR_ALLOC_FLAG_TLP = 1 << 0, +}; + struct mlx5_ib_uapi_reg { __u32 value; __u32 mask; diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h index f41f0228fcd0..aac9782ddc09 100644 --- a/include/uapi/rdma/rdma_netlink.h +++ b/include/uapi/rdma/rdma_netlink.h @@ -308,6 +308,10 @@ enum rdma_nldev_command { RDMA_NLDEV_CMD_MONITOR, + RDMA_NLDEV_CMD_FRMR_POOLS_GET, /* can dump */ + + RDMA_NLDEV_CMD_FRMR_POOLS_SET, + RDMA_NLDEV_NUM_OPS }; @@ -582,6 +586,24 @@ enum rdma_nldev_attr { RDMA_NLDEV_SYS_ATTR_MONITOR_MODE, /* u8 */ RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED, /* u8 */ + + /* + * FRMR Pools attributes + */ + RDMA_NLDEV_ATTR_FRMR_POOLS, /* nested table */ + RDMA_NLDEV_ATTR_FRMR_POOL_ENTRY, /* nested table */ + RDMA_NLDEV_ATTR_FRMR_POOL_KEY, /* nested table */ + RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ATS, /* u8 */ + RDMA_NLDEV_ATTR_FRMR_POOL_KEY_ACCESS_FLAGS, /* u32 */ + RDMA_NLDEV_ATTR_FRMR_POOL_KEY_VENDOR_KEY, /* u64 */ + RDMA_NLDEV_ATTR_FRMR_POOL_KEY_NUM_DMA_BLOCKS, /* u64 */ + RDMA_NLDEV_ATTR_FRMR_POOL_QUEUE_HANDLES, /* u32 */ + RDMA_NLDEV_ATTR_FRMR_POOL_MAX_IN_USE, /* u64 */ + RDMA_NLDEV_ATTR_FRMR_POOL_IN_USE, /* u64 */ + RDMA_NLDEV_ATTR_FRMR_POOLS_AGING_PERIOD, /* u32 */ + RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES, /* u32 */ + RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY, /* u64 */ + /* * Always the end */ diff --git a/include/uapi/scsi/fc/fc_els.h b/include/uapi/scsi/fc/fc_els.h index 019096beb179..dca6a28f4e86 100644 --- a/include/uapi/scsi/fc/fc_els.h +++ b/include/uapi/scsi/fc/fc_els.h @@ -1030,7 +1030,7 @@ struct fc_fn_li_desc { */ __be32 event_count; /* minimum number of event * occurrences during the event - * threshold to caause the LI event + * threshold to cause the LI event */ __be32 pname_count; /* number of portname_list elements */ __be64 pname_list[]; /* list of N_Port_Names accessible diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h index 8563b6648976..cfbc75d8df83 100644 --- a/include/ufs/ufshcd.h +++ b/include/ufs/ufshcd.h @@ -287,6 +287,86 @@ struct ufs_pwr_mode_info { struct ufs_pa_layer_attr info; }; +#define UFS_MAX_LANES 2 + +/** + * struct tx_eqtr_iter - TX Equalization Training iterator + * @preshoot_bitmap: PreShoot bitmap + * @deemphasis_bitmap: DeEmphasis bitmap + * @preshoot: PreShoot value + * @deemphasis: DeEmphasis value + * @fom: Figure-of-Merit read out from RX_FOM + * @is_updated: Flag to indicate if updated since previous iteration + */ +struct tx_eqtr_iter { + unsigned long preshoot_bitmap; + unsigned long deemphasis_bitmap; + u8 preshoot; + u8 deemphasis; + u8 fom[UFS_MAX_LANES]; + bool is_updated; +}; + +/** + * struct ufshcd_tx_eq_settings - TX Equalization settings + * @preshoot: PreShoot value + * @deemphasis: DeEmphasis value + * @fom_val: Figure-of-Merit value read out from RX_FOM (Bit[6:0]) + * @precode_en: Flag to indicate whether need to enable pre-coding + */ +struct ufshcd_tx_eq_settings { + u8 preshoot; + u8 deemphasis; + u8 fom_val; + bool precode_en; +}; + +/** + * struct ufshcd_tx_eqtr_data - Data used during TX Equalization Training procedure + * @host: Optimal TX EQ settings identified for host TX Lanes during TX EQTR + * @device: Optimal TX EQ settings identified for device TX Lanes during TX EQTR + * @host_fom: Host TX EQTR FOM record + * @device_fom: Device TX EQTR FOM record + */ +struct ufshcd_tx_eqtr_data { + struct ufshcd_tx_eq_settings host[UFS_MAX_LANES]; + struct ufshcd_tx_eq_settings device[UFS_MAX_LANES]; + u8 host_fom[UFS_MAX_LANES][TX_HS_NUM_PRESHOOT][TX_HS_NUM_DEEMPHASIS]; + u8 device_fom[UFS_MAX_LANES][TX_HS_NUM_PRESHOOT][TX_HS_NUM_DEEMPHASIS]; +}; + +/** + * struct ufshcd_tx_eqtr_record - TX Equalization Training record + * @host_fom: Host TX EQTR FOM record + * @device_fom: Device TX EQTR FOM record + * @last_record_ts: Timestamp of the most recent TX EQTR record + * @last_record_index: Index of the most recent TX EQTR record + * @saved_adapt_eqtr: Saved Adaptation length setting for TX EQTR + */ +struct ufshcd_tx_eqtr_record { + u8 host_fom[UFS_MAX_LANES][TX_HS_NUM_PRESHOOT][TX_HS_NUM_DEEMPHASIS]; + u8 device_fom[UFS_MAX_LANES][TX_HS_NUM_PRESHOOT][TX_HS_NUM_DEEMPHASIS]; + ktime_t last_record_ts; + u16 last_record_index; + u16 saved_adapt_eqtr; +}; + +/** + * struct ufshcd_tx_eq_params - TX Equalization parameters structure + * @host: TX EQ settings for host TX Lanes + * @device: TX EQ settings for device TX Lanes + * @eqtr_record: Pointer to TX EQTR record + * @is_valid: True if parameter contains valid TX Equalization settings + * @is_applied: True if settings have been applied to UniPro of both sides + */ +struct ufshcd_tx_eq_params { + struct ufshcd_tx_eq_settings host[UFS_MAX_LANES]; + struct ufshcd_tx_eq_settings device[UFS_MAX_LANES]; + struct ufshcd_tx_eqtr_record *eqtr_record; + bool is_valid; + bool is_applied; +}; + /** * struct ufs_hba_variant_ops - variant specific callbacks * @name: variant name @@ -302,11 +382,10 @@ struct ufs_pwr_mode_info { * variant specific Uni-Pro initialization. * @link_startup_notify: called before and after Link startup is carried out * to allow variant specific Uni-Pro initialization. + * @negotiate_pwr_mode: called to negotiate power mode. * @pwr_change_notify: called before and after a power mode change * is carried out to allow vendor spesific capabilities - * to be set. PRE_CHANGE can modify final_params based - * on desired_pwr_mode, but POST_CHANGE must not alter - * the final_params parameter + * to be set. * @setup_xfer_req: called before any transfer request is issued * to set some things * @setup_task_mgmt: called before any task management request is issued @@ -331,6 +410,11 @@ struct ufs_pwr_mode_info { * @config_esi: called to config Event Specific Interrupt * @config_scsi_dev: called to configure SCSI device parameters * @freq_to_gear_speed: called to map clock frequency to the max supported gear speed + * @apply_tx_eqtr_settings: called to apply settings for TX Equalization + * Training settings. + * @get_rx_fom: called to get Figure of Merit (FOM) value. + * @tx_eqtr_notify: called before and after TX Equalization Training procedure + * to allow platform vendor specific configs to take place. */ struct ufs_hba_variant_ops { const char *name; @@ -347,10 +431,12 @@ struct ufs_hba_variant_ops { enum ufs_notify_change_status); int (*link_startup_notify)(struct ufs_hba *, enum ufs_notify_change_status); - int (*pwr_change_notify)(struct ufs_hba *, - enum ufs_notify_change_status status, - const struct ufs_pa_layer_attr *desired_pwr_mode, - struct ufs_pa_layer_attr *final_params); + int (*negotiate_pwr_mode)(struct ufs_hba *hba, + const struct ufs_pa_layer_attr *desired_pwr_mode, + struct ufs_pa_layer_attr *final_params); + int (*pwr_change_notify)(struct ufs_hba *hba, + enum ufs_notify_change_status status, + struct ufs_pa_layer_attr *final_params); void (*setup_xfer_req)(struct ufs_hba *hba, int tag, bool is_scsi_cmd); void (*setup_task_mgmt)(struct ufs_hba *, int, u8); @@ -380,6 +466,17 @@ struct ufs_hba_variant_ops { int (*config_esi)(struct ufs_hba *hba); void (*config_scsi_dev)(struct scsi_device *sdev); u32 (*freq_to_gear_speed)(struct ufs_hba *hba, unsigned long freq); + int (*get_rx_fom)(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + struct tx_eqtr_iter *h_iter, + struct tx_eqtr_iter *d_iter); + int (*apply_tx_eqtr_settings)(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + struct tx_eqtr_iter *h_iter, + struct tx_eqtr_iter *d_iter); + int (*tx_eqtr_notify)(struct ufs_hba *hba, + enum ufs_notify_change_status status, + struct ufs_pa_layer_attr *pwr_mode); }; /* clock gating state */ @@ -528,6 +625,17 @@ enum ufshcd_state { UFSHCD_STATE_ERROR, }; +/** + * enum ufshcd_pmc_policy - Power Mode change policy + * @UFSHCD_PMC_POLICY_DONT_FORCE: Do not force a Power Mode change. + * @UFSHCD_PMC_POLICY_FORCE: Force a Power Mode change even if current Power + * Mode is same as target Power Mode. + */ +enum ufshcd_pmc_policy { + UFSHCD_PMC_POLICY_DONT_FORCE, + UFSHCD_PMC_POLICY_FORCE, +}; + enum ufshcd_quirks { /* Interrupt aggregation support is broken */ UFSHCD_QUIRK_BROKEN_INTR_AGGR = 1 << 0, @@ -690,6 +798,12 @@ enum ufshcd_quirks { * because it causes link startup to become unreliable. */ UFSHCD_QUIRK_PERFORM_LINK_STARTUP_ONCE = 1 << 26, + + /* + * On some platforms, the VCC regulator has a slow ramp-up time. Add a + * delay after enabling VCC to ensure it's stable. + */ + UFSHCD_QUIRK_VCC_ON_DELAY = 1 << 27, }; enum ufshcd_caps { @@ -767,6 +881,13 @@ enum ufshcd_caps { * WriteBooster when scaling the clock down. */ UFSHCD_CAP_WB_WITH_CLK_SCALING = 1 << 12, + + /* + * This capability allows the host controller driver to apply TX + * Equalization settings discovered from UFS attributes, variant + * specific operations and TX Equaliztion Training procedure. + */ + UFSHCD_CAP_TX_EQUALIZATION = 1 << 13, }; struct ufs_hba_variant_params { @@ -881,7 +1002,6 @@ enum ufshcd_mcq_opr { * @saved_uic_err: sticky UIC error mask * @ufs_stats: various error counters * @force_reset: flag to force eh_work perform a full reset - * @force_pmc: flag to force a power mode change * @silence_err_logs: flag to silence error logs * @dev_cmd: ufs device management command information * @last_dme_cmd_tstamp: time stamp of the last completed DME command @@ -943,7 +1063,21 @@ enum ufshcd_mcq_opr { * @critical_health_count: count of critical health exceptions * @dev_lvl_exception_count: count of device level exceptions since last reset * @dev_lvl_exception_id: vendor specific information about the device level exception event. + * @dme_qos_notification: Bitfield of pending DME Quality of Service (QoS) + * events. Bits[3:1] reflect the corresponding bits of UIC DME Error Code + * field within the Host Controller's UECDME register. Bit[0] is a flag + * indicating that the DME QoS Monitor has been reset by the host. + * @dme_qos_sysfs_handle: handle for 'dme_qos_notification' sysfs entry * @rpmbs: list of OP-TEE RPMB devices (one per RPMB region) + * @host_preshoot_cap: a bitfield to indicate supported PreShoot dBs of host's TX lanes, cache of + * host M-PHY TX_HS_PreShoot_Setting_Capability Attribute (ID 0x15) + * @host_deemphasis_cap: a bitfield to indicate supported DeEmphasis dBs of host's TX lanes, cache + * of host M-PHY TX_HS_DeEmphasis_Setting_Capability Attribute (ID 0x12) + * @device_preshoot_cap: a bitfield to indicate supported PreShoot dBs of device's TX lanes, cache + * of device M-PHY TX_HS_PreShoot_Setting_Capability Attribute (ID 0x15) + * @device_deemphasis_cap: a bitfield to indicate supported DeEmphasis dBs of device's TX lanes, + * cache of device M-PHY TX_HS_DeEmphasis_Setting_Capability Attribute (ID 0x12) + * @tx_eq_params: TX Equalization settings */ struct ufs_hba { void __iomem *mmio_base; @@ -1035,7 +1169,6 @@ struct ufs_hba { u32 saved_uic_err; struct ufs_stats ufs_stats; bool force_reset; - bool force_pmc; bool silence_err_logs; /* Device management request data */ @@ -1116,8 +1249,18 @@ struct ufs_hba { int critical_health_count; atomic_t dev_lvl_exception_count; u64 dev_lvl_exception_id; + + atomic_t dme_qos_notification; + struct kernfs_node *dme_qos_sysfs_handle; + u32 vcc_off_delay_us; struct list_head rpmbs; + + u8 host_preshoot_cap; + u8 host_deemphasis_cap; + u8 device_preshoot_cap; + u8 device_deemphasis_cap; + struct ufshcd_tx_eq_params tx_eq_params[UFS_HS_GEAR_MAX]; }; /** @@ -1262,6 +1405,13 @@ static inline bool ufshcd_enable_wb_if_scaling_up(struct ufs_hba *hba) return hba->caps & UFSHCD_CAP_WB_WITH_CLK_SCALING; } +static inline bool ufshcd_is_tx_eq_supported(struct ufs_hba *hba) +{ + return hba->caps & UFSHCD_CAP_TX_EQUALIZATION && + hba->ufs_version >= ufshci_version(5, 0) && + hba->dev_info.wspecversion >= 0x500; +} + #define ufsmcq_writel(hba, val, reg) \ writel((val), (hba)->mcq_base + (reg)) #define ufsmcq_readl(hba, reg) \ @@ -1277,6 +1427,18 @@ static inline bool ufshcd_enable_wb_if_scaling_up(struct ufs_hba *hba) #define ufshcd_readl(hba, reg) \ readl((hba)->mmio_base + (reg)) +static inline const char *ufs_hs_rate_to_str(enum ufs_hs_gear_rate rate) +{ + switch (rate) { + case PA_HS_MODE_A: + return "A"; + case PA_HS_MODE_B: + return "B"; + default: + return "Unknown"; + } +} + /** * ufshcd_rmwl - perform read/modify/write for a controller register * @hba: per adapter instance @@ -1361,9 +1523,16 @@ extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, u8 attr_set, u32 mib_val, u8 peer); extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val, u8 peer); +extern int ufshcd_change_power_mode(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode, + enum ufshcd_pmc_policy pmc_policy); extern int ufshcd_config_pwr_mode(struct ufs_hba *hba, - struct ufs_pa_layer_attr *desired_pwr_mode); + struct ufs_pa_layer_attr *desired_pwr_mode, + enum ufshcd_pmc_policy pmc_policy); extern int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode); +extern int ufshcd_apply_tx_eq_settings(struct ufs_hba *hba, + struct ufshcd_tx_eq_params *params, + u32 gear); /* UIC command interfaces for DME primitives */ #define DME_LOCAL 0 diff --git a/include/ufs/ufshci.h b/include/ufs/ufshci.h index 806fdaf52bd9..9f0fdd850e54 100644 --- a/include/ufs/ufshci.h +++ b/include/ufs/ufshci.h @@ -115,6 +115,7 @@ enum { enum { REG_CQIS = 0x0, REG_CQIE = 0x4, + REG_MCQIACR = 0x8, }; enum { @@ -188,6 +189,7 @@ static inline u32 ufshci_version(u32 major, u32 minor) #define SYSTEM_BUS_FATAL_ERROR 0x20000 #define CRYPTO_ENGINE_FATAL_ERROR 0x40000 #define MCQ_CQ_EVENT_STATUS 0x100000 +#define MCQ_IAG_EVENT_STATUS 0x200000 #define UFSHCD_UIC_HIBERN8_MASK (UIC_HIBERNATE_ENTER |\ UIC_HIBERNATE_EXIT) @@ -271,6 +273,7 @@ enum { /* UECDME - Host UIC Error Code DME 48h */ #define UIC_DME_ERROR 0x80000000 #define UIC_DME_ERROR_CODE_MASK 0x1 +#define UIC_DME_QOS_MASK 0xE /* UTRIACR - Interrupt Aggregation control register - 0x4Ch */ #define INT_AGGR_TIMEOUT_VAL_MASK 0xFF diff --git a/include/ufs/unipro.h b/include/ufs/unipro.h index 59de737490ca..9c168703b104 100644 --- a/include/ufs/unipro.h +++ b/include/ufs/unipro.h @@ -10,6 +10,8 @@ * M-TX Configuration Attributes */ #define TX_HIBERN8TIME_CAPABILITY 0x000F +#define TX_HS_DEEMPHASIS_SETTING_CAP 0x0012 +#define TX_HS_PRESHOOT_SETTING_CAP 0x0015 #define TX_MODE 0x0021 #define TX_HSRATE_SERIES 0x0022 #define TX_HSGEAR 0x0023 @@ -30,6 +32,8 @@ #define TX_LCC_SEQUENCER 0x0032 #define TX_MIN_ACTIVATETIME 0x0033 #define TX_PWM_G6_G7_SYNC_LENGTH 0x0034 +#define TX_HS_DEEMPHASIS_SETTING 0x0037 +#define TX_HS_PRESHOOT_SETTING 0x003B #define TX_REFCLKFREQ 0x00EB #define TX_CFGCLKFREQVAL 0x00EC #define CFGEXTRATTR 0x00F0 @@ -38,6 +42,9 @@ /* * M-RX Configuration Attributes */ +#define RX_HS_G5_ADAPT_INITIAL_CAP 0x0074 +#define RX_HS_G6_ADAPT_INITIAL_CAP 0x007B +#define RX_HS_G6_ADAPT_INITIAL_L0L1L2L3_CAP 0x007D #define RX_HS_G1_SYNC_LENGTH_CAP 0x008B #define RX_HS_G1_PREP_LENGTH_CAP 0x008C #define RX_MIN_ACTIVATETIME_CAPABILITY 0x008F @@ -50,6 +57,7 @@ #define RX_HIBERN8TIME_CAP 0x0092 #define RX_ADV_HIBERN8TIME_CAP 0x0099 #define RX_ADV_MIN_ACTIVATETIME_CAP 0x009A +#define RX_HS_G4_ADAPT_INITIAL_CAP 0x009F #define RX_MODE 0x00A1 #define RX_HSRATE_SERIES 0x00A2 #define RX_HSGEAR 0x00A3 @@ -64,15 +72,32 @@ #define CFGRXCDR8 0x00BA #define CFGRXOVR8 0x00BD #define CFGRXOVR6 0x00BF +#define RX_FOM 0x00C2 #define RXDIRECTCTRL2 0x00C7 #define CFGRXOVR4 0x00E9 #define RX_REFCLKFREQ 0x00EB #define RX_CFGCLKFREQVAL 0x00EC #define CFGWIDEINLN 0x00F0 +#define RX_EYEMON_CAP 0x00F1 +#define RX_EYEMON_TIMING_MAX_STEPS_CAP 0x00F2 +#define RX_EYEMON_TIMING_MAX_OFFSET_CAP 0x00F3 +#define RX_EYEMON_VOLTAGE_MAX_STEPS_CAP 0x00F4 +#define RX_EYEMON_VOLTAGE_MAX_OFFSET_CAP 0x00F5 +#define RX_EYEMON_ENABLE 0x00F6 +#define RX_EYEMON_TIMING_STEPS 0x00F7 +#define RX_EYEMON_VOLTAGE_STEPS 0x00F8 +#define RX_EYEMON_TARGET_TEST_COUNT 0x00F9 +#define RX_EYEMON_TESTED_COUNT 0x00FA +#define RX_EYEMON_ERROR_COUNT 0x00FB +#define RX_EYEMON_START 0x00FC +#define RX_EYEMON_EXTENDED_ERROR_COUNT 0x00FD + #define ENARXDIRECTCFG4 0x00F2 #define ENARXDIRECTCFG3 0x00F3 #define ENARXDIRECTCFG2 0x00F4 +#define RX_EYEMON_NEGATIVE_STEP_BIT BIT(6) +#define RX_EYEMON_EXTENDED_VRANGE_BIT BIT(6) #define is_mphy_tx_attr(attr) (attr < RX_MODE) #define RX_ADV_FINE_GRAN_STEP(x) ((((x) & 0x3) << 1) | 0x1) @@ -99,6 +124,18 @@ #define UNIPRO_CB_OFFSET(x) (0x8000 | x) +#define ADAPT_LENGTH_MASK 0x7F +#define ADAPT_RANGE_BIT BIT(7) +#define IS_ADAPT_RANGE_COARSE(x) ((x) & ADAPT_RANGE_BIT) + +/* Adapt definitions */ +#define ADAPT_LENGTH_MAX 0x91 +#define ADAPT_L0L3_LENGTH_MAX 0x90 +#define ADAPT_L0L1L2L3_LENGTH_MAX 0x8C +#define TADAPT_FACTOR 650 +#define TADAPT_L0L3_FACTOR (1 << 9) +#define TADAPT_L0L1L2L3_FACTOR (1 << 15) + /* * PHY Adapter attributes */ @@ -164,10 +201,26 @@ #define PA_PACPERRORCOUNT 0x15C1 #define PA_PHYTESTCONTROL 0x15C2 #define PA_TXHSG4SYNCLENGTH 0x15D0 +#define PA_PEERRXHSG4ADAPTINITIAL 0x15D3 #define PA_TXHSADAPTTYPE 0x15D4 #define PA_TXHSG5SYNCLENGTH 0x15D6 +#define PA_PEERRXHSG5ADAPTINITIAL 0x15D9 +#define PA_PEERRXHSG6ADAPTREFRESHL0L1L2L3 0x15DE +#define PA_PEERRXHSG6ADAPTINITIALL0L3 0x15DF +#define PA_PEERRXHSG6ADAPTINITIALL0L1L2L3 0x15E0 +#define PA_TXEQG1SETTING 0x15E1 +#define PA_TXEQG2SETTING 0x15E2 +#define PA_TXEQG3SETTING 0x15E3 +#define PA_TXEQG4SETTING 0x15E4 +#define PA_TXEQG5SETTING 0x15E5 +#define PA_TXEQG6SETTING 0x15E6 +#define PA_TXEQTRSETTING 0x15E7 +#define PA_PEERTXEQTRSETTING 0x15E8 +#define PA_PRECODEEN 0x15E9 +#define PA_EQTR_GEAR 0x15EA +#define PA_TXADAPTLENGTH_EQTR 0x15EB -/* Adpat type for PA_TXHSADAPTTYPE attribute */ +/* Adapt type for PA_TXHSADAPTTYPE attribute */ #define PA_REFRESH_ADAPT 0x00 #define PA_INITIAL_ADAPT 0x01 #define PA_NO_ADAPT 0x03 @@ -187,6 +240,88 @@ /* PHY Adapter Protocol Constants */ #define PA_MAXDATALANES 4 +/* + * TX EQTR's minimum TAdapt should not be less than 10us. + * This value is rounded up into the nearest Unit Intervals (UI) + */ +#define TX_EQTR_HS_G4_MIN_T_ADAPT 166400 +#define TX_EQTR_HS_G5_MIN_T_ADAPT 332800 +#define TX_EQTR_HS_G6_MIN_T_ADAPT 262144 + +#define TX_EQTR_HS_G4_ADAPT_DEFAULT 0x88 +#define TX_EQTR_HS_G5_ADAPT_DEFAULT 0x89 +#define TX_EQTR_HS_G6_ADAPT_DEFAULT 0x89 + +#define TX_EQTR_CAP_MASK 0x7F + +#define TX_EQTR_ADAPT_LENGTH_L0L1L2L3_SHIFT 8 +#define TX_EQTR_ADAPT_RESERVED 0xFF + +#define TX_HS_NUM_PRESHOOT 8 +#define TX_HS_NUM_DEEMPHASIS 8 +#define TX_HS_PRESHOOT_SHIFT 4 +#define TX_HS_DEEMPHASIS_SHIFT 4 +#define TX_HS_PRESHOOT_OFFSET 0 +#define TX_HS_DEEMPHASIS_OFFSET 16 + +#define TX_HS_PRESHOOT_LANE_SHIFT(lane) \ + (TX_HS_PRESHOOT_OFFSET + (lane) * TX_HS_PRESHOOT_SHIFT) +#define TX_HS_DEEMPHASIS_LANE_SHIFT(lane) \ + (TX_HS_DEEMPHASIS_OFFSET + (lane) * TX_HS_DEEMPHASIS_SHIFT) + +#define TX_HS_PRESHOOT_BITS(lane, val) \ + ((val) << TX_HS_PRESHOOT_LANE_SHIFT(lane)) +#define TX_HS_DEEMPHASIS_BITS(lane, val) \ + ((val) << TX_HS_DEEMPHASIS_LANE_SHIFT(lane)) + +#define RX_FOM_VALUE_MASK 0x7F +#define RX_FOM_PRECODING_EN_BIT BIT(7) + +#define PRECODEEN_TX_OFFSET 0 +#define PRECODEEN_RX_OFFSET 4 +#define PRECODEEN_TX_BIT(lane) (1 << (PRECODEEN_TX_OFFSET + (lane))) +#define PRECODEEN_RX_BIT(lane) (1 << (PRECODEEN_RX_OFFSET + (lane))) + +enum ufs_tx_eq_preset { + UFS_TX_EQ_PRESET_P0, + UFS_TX_EQ_PRESET_P1, + UFS_TX_EQ_PRESET_P2, + UFS_TX_EQ_PRESET_P3, + UFS_TX_EQ_PRESET_P4, + UFS_TX_EQ_PRESET_P5, + UFS_TX_EQ_PRESET_P6, + UFS_TX_EQ_PRESET_P7, + UFS_TX_EQ_PRESET_MAX, +}; + +enum ufs_tx_hs_preshoot { + UFS_TX_HS_PRESHOOT_DB_0P0, + UFS_TX_HS_PRESHOOT_DB_0P4, + UFS_TX_HS_PRESHOOT_DB_0P8, + UFS_TX_HS_PRESHOOT_DB_1P2, + UFS_TX_HS_PRESHOOT_DB_1P6, + UFS_TX_HS_PRESHOOT_DB_2P5, + UFS_TX_HS_PRESHOOT_DB_3P5, + UFS_TX_HS_PRESHOOT_DB_4P7, +}; + +enum ufs_tx_hs_deemphasis { + UFS_TX_HS_DEEMPHASIS_DB_0P0, + UFS_TX_HS_DEEMPHASIS_DB_0P8, + UFS_TX_HS_DEEMPHASIS_DB_1P6, + UFS_TX_HS_DEEMPHASIS_DB_2P5, + UFS_TX_HS_DEEMPHASIS_DB_3P5, + UFS_TX_HS_DEEMPHASIS_DB_4P7, + UFS_TX_HS_DEEMPHASIS_DB_6P0, + UFS_TX_HS_DEEMPHASIS_DB_7P6, +}; + +enum ufs_eom_eye_mask { + UFS_EOM_EYE_MASK_M, + UFS_EOM_EYE_MASK_L, + UFS_EOM_EYE_MASK_U, +}; + #define DL_FC0ProtectionTimeOutVal_Default 8191 #define DL_TC0ReplayTimeOutVal_Default 65535 #define DL_AFC0ReqTimeOutVal_Default 32767 @@ -198,6 +333,11 @@ #define DME_LocalTC0ReplayTimeOutVal 0xD042 #define DME_LocalAFC0ReqTimeOutVal 0xD043 +enum ufs_op_mode { + LS_MODE = 1, + HS_MODE = 2, +}; + /* PA power modes */ enum ufs_pa_pwr_mode { FAST_MODE = 1, @@ -233,7 +373,9 @@ enum ufs_hs_gear_tag { UFS_HS_G2, /* HS Gear 2 */ UFS_HS_G3, /* HS Gear 3 */ UFS_HS_G4, /* HS Gear 4 */ - UFS_HS_G5 /* HS Gear 5 */ + UFS_HS_G5, /* HS Gear 5 */ + UFS_HS_G6, /* HS Gear 6 */ + UFS_HS_GEAR_MAX = UFS_HS_G6, }; enum ufs_lanes { diff --git a/include/video/imx-ipu-image-convert.h b/include/video/imx-ipu-image-convert.h index 003b3927ede5..6b77968a6a15 100644 --- a/include/video/imx-ipu-image-convert.h +++ b/include/video/imx-ipu-image-convert.h @@ -27,12 +27,13 @@ struct ipu_image_convert_run { int status; + /* private: */ /* internal to image converter, callers don't touch */ struct list_head list; }; /** - * ipu_image_convert_cb_t - conversion callback function prototype + * typedef ipu_image_convert_cb_t - conversion callback function prototype * * @run: the completed conversion run pointer * @ctx: a private context pointer for the callback @@ -60,7 +61,7 @@ void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, * @out: output image format * @rot_mode: rotation mode * - * Returns 0 if the formats and rotation mode meet IPU restrictions, + * Returns: 0 if the formats and rotation mode meet IPU restrictions, * -EINVAL otherwise. */ int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out, @@ -77,11 +78,11 @@ int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out, * @complete: run completion callback * @complete_context: a context pointer for the completion callback * - * Returns an opaque conversion context pointer on success, error pointer + * In V4L2, drivers should call ipu_image_convert_prepare() at streamon. + * + * Returns: an opaque conversion context pointer on success, error pointer * on failure. The input/output formats and rotation mode must already meet * IPU retrictions. - * - * In V4L2, drivers should call ipu_image_convert_prepare() at streamon. */ struct ipu_image_convert_ctx * ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task, @@ -122,6 +123,8 @@ void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx); * In V4L2, drivers should call ipu_image_convert_queue() while * streaming to queue the conversion of a received input buffer. * For example mem2mem devices this would be called in .device_run. + * + * Returns: 0 on success or -errno on error. */ int ipu_image_convert_queue(struct ipu_image_convert_run *run); @@ -155,6 +158,9 @@ void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx); * On successful return the caller can queue more run requests if needed, using * the prepared context in run->ctx. The caller is responsible for unpreparing * the context when no more conversion requests are needed. + * + * Returns: pointer to the created &struct ipu_image_convert_run that has + * been queued on success; an ERR_PTR(errno) on error. */ struct ipu_image_convert_run * ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task, diff --git a/include/video/udlfb.h b/include/video/udlfb.h index 58fb5732831a..ab34790d57ec 100644 --- a/include/video/udlfb.h +++ b/include/video/udlfb.h @@ -56,6 +56,7 @@ struct dlfb_data { spinlock_t damage_lock; struct work_struct damage_work; struct fb_ops ops; + atomic_t mmap_count; /* blit-only rendering path metrics, exposed through sysfs */ atomic_t bytes_rendered; /* raw pixel-bytes driver asked to render */ atomic_t bytes_identical; /* saved effort with backbuffer comparison */ diff --git a/include/xen/arm/interface.h b/include/xen/arm/interface.h index c3eada2642aa..61360b89da40 100644 --- a/include/xen/arm/interface.h +++ b/include/xen/arm/interface.h @@ -30,7 +30,7 @@ #define __HYPERVISOR_platform_op_raw __HYPERVISOR_platform_op -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ /* Explicitly size integers that represent pfns in the interface with * Xen so that we can have one ABI that works for 32 and 64 bit guests. * Note that this means that the xen_pfn_t type may be capable of diff --git a/init/initramfs.c b/init/initramfs.c index d29fe0a5ccfc..58db15fb18fd 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -652,13 +652,6 @@ disable: void __weak __init free_initrd_mem(unsigned long start, unsigned long end) { -#ifdef CONFIG_ARCH_KEEP_MEMBLOCK - unsigned long aligned_start = ALIGN_DOWN(start, PAGE_SIZE); - unsigned long aligned_end = ALIGN(end, PAGE_SIZE); - - memblock_free((void *)aligned_start, aligned_end - aligned_start); -#endif - free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM, "initrd"); } diff --git a/io_uring/alloc_cache.h b/io_uring/alloc_cache.h index 45fcd8b3b824..962b6e2d04cc 100644 --- a/io_uring/alloc_cache.h +++ b/io_uring/alloc_cache.h @@ -64,7 +64,7 @@ static inline void *io_cache_alloc(struct io_alloc_cache *cache, gfp_t gfp) static inline void io_cache_free(struct io_alloc_cache *cache, void *obj) { if (!io_alloc_cache_put(cache, obj)) - kfree(obj); + kvfree(obj); } #endif diff --git a/io_uring/cancel.c b/io_uring/cancel.c index 5e5eb9cfc7cd..4aa3103ba9c3 100644 --- a/io_uring/cancel.c +++ b/io_uring/cancel.c @@ -561,8 +561,8 @@ __cold bool io_uring_try_cancel_requests(struct io_ring_ctx *ctx, ret |= io_waitid_remove_all(ctx, tctx, cancel_all); ret |= io_futex_remove_all(ctx, tctx, cancel_all); ret |= io_uring_try_cancel_uring_cmd(ctx, tctx, cancel_all); - mutex_unlock(&ctx->uring_lock); ret |= io_kill_timeouts(ctx, tctx, cancel_all); + mutex_unlock(&ctx->uring_lock); if (tctx) ret |= io_run_task_work() > 0; else diff --git a/io_uring/eventfd.c b/io_uring/eventfd.c index 3da028500f76..d656cc2a0b9b 100644 --- a/io_uring/eventfd.c +++ b/io_uring/eventfd.c @@ -43,6 +43,7 @@ static void io_eventfd_do_signal(struct rcu_head *rcu) { struct io_ev_fd *ev_fd = container_of(rcu, struct io_ev_fd, rcu); + atomic_andnot(BIT(IO_EVENTFD_OP_SIGNAL_BIT), &ev_fd->ops); eventfd_signal_mask(ev_fd->cq_ev_fd, EPOLL_URING_WAKE); io_eventfd_put(ev_fd); } diff --git a/io_uring/fdinfo.c b/io_uring/fdinfo.c index c2d3e45544bb..001fb542dc11 100644 --- a/io_uring/fdinfo.c +++ b/io_uring/fdinfo.c @@ -190,8 +190,9 @@ static void __io_uring_show_fdinfo(struct io_ring_ctx *ctx, struct seq_file *m) get_task_struct(tsk); rcu_read_unlock(); usec = io_sq_cpu_usec(tsk); + sq_pid = task_pid_nr_ns(tsk, + proc_pid_ns(file_inode(m->file)->i_sb)); put_task_struct(tsk); - sq_pid = sq->task_pid; sq_cpu = sq->sq_cpu; sq_total_time = usec; sq_work_time = sq->work_time; diff --git a/io_uring/futex.c b/io_uring/futex.c index fd503c24b428..9cc1788ef4c6 100644 --- a/io_uring/futex.c +++ b/io_uring/futex.c @@ -159,8 +159,10 @@ static void io_futex_wakev_fn(struct wake_q_head *wake_q, struct futex_q *q) struct io_kiocb *req = q->wake_data; struct io_futexv_data *ifd = req->async_data; - if (!io_futexv_claim(ifd)) + if (!io_futexv_claim(ifd)) { + __futex_wake_mark(q); return; + } if (unlikely(!__futex_wake_mark(q))) return; diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c index 7a9f94a0ce6f..8cc7b47d3089 100644 --- a/io_uring/io-wq.c +++ b/io_uring/io-wq.c @@ -1124,7 +1124,8 @@ static inline void io_wq_remove_pending(struct io_wq *wq, if (io_wq_is_hashed(work) && work == wq->hash_tail[hash]) { if (prev) prev_work = container_of(prev, struct io_wq_work, list); - if (prev_work && io_get_work_hash(prev_work) == hash) + if (prev_work && io_wq_is_hashed(prev_work) && + io_get_work_hash(prev_work) == hash) wq->hash_tail[hash] = prev_work; else wq->hash_tail[hash] = NULL; diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index dd6326dc5f88..036145ee466c 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -686,13 +686,27 @@ static struct io_overflow_cqe *io_alloc_ocqe(struct io_ring_ctx *ctx, return ocqe; } +/* + * Compute queued CQEs for free-space calculation, clamped to cq_entries. + */ +static unsigned int io_cqring_queued(struct io_ring_ctx *ctx) +{ + struct io_rings *rings = io_get_rings(ctx); + int diff; + + diff = (int)(ctx->cached_cq_tail - READ_ONCE(rings->cq.head)); + if (diff >= 0) + return min((unsigned int)diff, ctx->cq_entries); + return 0; +} + /* * Fill an empty dummy CQE, in case alignment is off for posting a 32b CQE * because the ring is a single 16b entry away from wrapping. */ static bool io_fill_nop_cqe(struct io_ring_ctx *ctx, unsigned int off) { - if (__io_cqring_events(ctx) < ctx->cq_entries) { + if (io_cqring_queued(ctx) < ctx->cq_entries) { struct io_uring_cqe *cqe = &ctx->rings->cqes[off]; cqe->user_data = 0; @@ -713,7 +727,7 @@ bool io_cqe_cache_refill(struct io_ring_ctx *ctx, bool overflow, bool cqe32) { struct io_rings *rings = ctx->rings; unsigned int off = ctx->cached_cq_tail & (ctx->cq_entries - 1); - unsigned int free, queued, len; + unsigned int free, len; /* * Posting into the CQ when there are pending overflowed CQEs may break @@ -733,9 +747,7 @@ bool io_cqe_cache_refill(struct io_ring_ctx *ctx, bool overflow, bool cqe32) off = 0; } - /* userspace may cheat modifying the tail, be safe and do min */ - queued = min(__io_cqring_events(ctx), ctx->cq_entries); - free = ctx->cq_entries - queued; + free = ctx->cq_entries - io_cqring_queued(ctx); /* we need a contiguous range, limit based on the current array offset */ len = min(free, ctx->cq_entries - off); if (len < (cqe32 + 1)) @@ -1452,8 +1464,13 @@ struct io_wq_work *io_wq_free_work(struct io_wq_work *work) struct io_kiocb *nxt = NULL; if (req_ref_put_and_test_atomic(req)) { - if (req->flags & IO_REQ_LINK_FLAGS) + if (req->flags & IO_REQ_LINK_FLAGS) { + struct io_ring_ctx *ctx = req->ctx; + + mutex_lock(&ctx->uring_lock); nxt = io_req_find_next(req); + mutex_unlock(&ctx->uring_lock); + } io_free_req(req); } return nxt ? &nxt->work : NULL; @@ -2575,7 +2592,8 @@ struct file *io_uring_ctx_get_file(unsigned int fd, bool registered) return ERR_PTR(-EBADF); if (io_is_uring_fops(file)) return file; - fput(file); + if (!registered) + fput(file); return ERR_PTR(-EOPNOTSUPP); } diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c index 8da2ff798170..63061aa1cab9 100644 --- a/io_uring/kbuf.c +++ b/io_uring/kbuf.c @@ -47,7 +47,7 @@ static bool io_kbuf_inc_commit(struct io_buffer_list *bl, int len) this_len = min_t(u32, len, buf_len); buf_len -= this_len; /* Stop looping for invalid buffer length of 0 */ - if (buf_len || !this_len) { + if (buf_len > bl->min_left_sub_one || !this_len) { WRITE_ONCE(buf->addr, READ_ONCE(buf->addr) + this_len); WRITE_ONCE(buf->len, buf_len); return false; @@ -637,6 +637,10 @@ int io_register_pbuf_ring(struct io_ring_ctx *ctx, void __user *arg) if (reg.ring_entries >= 65536) return -EINVAL; + /* minimum left byte count is a property of incremental buffers */ + if (!(reg.flags & IOU_PBUF_RING_INC) && reg.min_left) + return -EINVAL; + bl = io_buffer_get_list(ctx, reg.bgid); if (bl) { /* if mapped buffer ring OR classic exists, don't allow */ @@ -680,10 +684,11 @@ int io_register_pbuf_ring(struct io_ring_ctx *ctx, void __user *arg) } #endif - bl->nr_entries = reg.ring_entries; bl->mask = reg.ring_entries - 1; bl->flags |= IOBL_BUF_RING; bl->buf_ring = br; + if (reg.min_left) + bl->min_left_sub_one = reg.min_left - 1; if (reg.flags & IOU_PBUF_RING_INC) bl->flags |= IOBL_INC; ret = io_buffer_add_list(ctx, bl, reg.bgid); diff --git a/io_uring/kbuf.h b/io_uring/kbuf.h index bf15e26520d3..401773e1ef80 100644 --- a/io_uring/kbuf.h +++ b/io_uring/kbuf.h @@ -27,12 +27,18 @@ struct io_buffer_list { __u16 bgid; /* below is for ring provided buffers */ - __u16 nr_entries; __u16 head; __u16 mask; __u16 flags; + /* + * minimum required amount to be left to reuse an incrementally + * consumed buffer. If less than this is left at consumption time, + * buffer is done and head is incremented to the next buffer. + */ + __u32 min_left_sub_one; + struct io_mapped_region region; }; diff --git a/io_uring/memmap.c b/io_uring/memmap.c index e6958968975a..4f9b439319c4 100644 --- a/io_uring/memmap.c +++ b/io_uring/memmap.c @@ -366,9 +366,53 @@ unsigned long io_uring_get_unmapped_area(struct file *filp, unsigned long addr, #else /* !CONFIG_MMU */ +/* + * Drop the pages that were initially referenced and added in + * io_uring_mmap(). We cannot have had a mremap() as that isn't supported, + * hence the vma should be identical to the one we initially referenced and + * mapped, and partial unmaps and splitting isn't possible on a file backed + * mapping. + */ +static void io_uring_nommu_vm_close(struct vm_area_struct *vma) +{ + unsigned long index; + + for (index = vma->vm_start; index < vma->vm_end; index += PAGE_SIZE) + put_page(virt_to_page((void *) index)); +} + +static const struct vm_operations_struct io_uring_nommu_vm_ops = { + .close = io_uring_nommu_vm_close, +}; + int io_uring_mmap(struct file *file, struct vm_area_struct *vma) { - return is_nommu_shared_mapping(vma->vm_flags) ? 0 : -EINVAL; + struct io_ring_ctx *ctx = file->private_data; + struct io_mapped_region *region; + unsigned long i; + + if (!is_nommu_shared_mapping(vma->vm_flags)) + return -EINVAL; + + guard(mutex)(&ctx->mmap_lock); + region = io_mmap_get_region(ctx, vma->vm_pgoff); + if (!region || !io_region_is_set(region)) + return -EINVAL; + + if ((vma->vm_end - vma->vm_start) != + (unsigned long) region->nr_pages << PAGE_SHIFT) + return -EINVAL; + + /* + * Pin the pages so io_free_region()'s release_pages() does not + * drop the last reference while this VMA exists. delete_vma() + * in mm/nommu.c calls vma_close() which runs ->close above. + */ + for (i = 0; i < region->nr_pages; i++) + get_page(region->pages[i]); + + vma->vm_ops = &io_uring_nommu_vm_ops; + return 0; } unsigned int io_uring_nommu_mmap_capabilities(struct file *file) diff --git a/io_uring/napi.c b/io_uring/napi.c index 4a10de03e426..bfc771445912 100644 --- a/io_uring/napi.c +++ b/io_uring/napi.c @@ -38,7 +38,8 @@ static inline ktime_t net_to_ktime(unsigned long t) return ns_to_ktime(t << 10); } -int __io_napi_add_id(struct io_ring_ctx *ctx, unsigned int napi_id) +int __io_napi_add_id(struct io_ring_ctx *ctx, unsigned int napi_id, + unsigned int mode) { struct hlist_head *hash_list; struct io_napi_entry *e; @@ -69,6 +70,11 @@ int __io_napi_add_id(struct io_ring_ctx *ctx, unsigned int napi_id) * kfree() */ spin_lock(&ctx->napi_lock); + if (unlikely(READ_ONCE(ctx->napi_track_mode) != mode)) { + spin_unlock(&ctx->napi_lock); + kfree(e); + return -EINVAL; + } if (unlikely(io_napi_hash_find(hash_list, napi_id))) { spin_unlock(&ctx->napi_lock); kfree(e); @@ -196,9 +202,14 @@ __io_napi_do_busy_loop(struct io_ring_ctx *ctx, bool (*loop_end)(void *, unsigned long), void *loop_end_arg) { - if (READ_ONCE(ctx->napi_track_mode) == IO_URING_NAPI_TRACKING_STATIC) + switch (READ_ONCE(ctx->napi_track_mode)) { + case IO_URING_NAPI_TRACKING_STATIC: return static_tracking_do_busy_loop(ctx, loop_end, loop_end_arg); - return dynamic_tracking_do_busy_loop(ctx, loop_end, loop_end_arg); + case IO_URING_NAPI_TRACKING_DYNAMIC: + return dynamic_tracking_do_busy_loop(ctx, loop_end, loop_end_arg); + default: + return false; + } } static void io_napi_blocking_busy_loop(struct io_ring_ctx *ctx, @@ -273,11 +284,13 @@ static int io_napi_register_napi(struct io_ring_ctx *ctx, default: return -EINVAL; } - /* clean the napi list for new settings */ + WRITE_ONCE(ctx->napi_track_mode, IO_URING_NAPI_TRACKING_INACTIVE); io_napi_free(ctx); - WRITE_ONCE(ctx->napi_track_mode, napi->op_param); + /* cap NAPI at 10 msec of spin time */ + napi->busy_poll_to = min(10000, napi->busy_poll_to); WRITE_ONCE(ctx->napi_busy_poll_dt, napi->busy_poll_to * NSEC_PER_USEC); WRITE_ONCE(ctx->napi_prefer_busy_poll, !!napi->prefer_busy_poll); + WRITE_ONCE(ctx->napi_track_mode, napi->op_param); return 0; } @@ -313,7 +326,8 @@ int io_register_napi(struct io_ring_ctx *ctx, void __user *arg) case IO_URING_NAPI_STATIC_ADD_ID: if (curr.op_param != IO_URING_NAPI_TRACKING_STATIC) return -EINVAL; - return __io_napi_add_id(ctx, napi.op_param); + return __io_napi_add_id(ctx, napi.op_param, + IO_URING_NAPI_TRACKING_STATIC); case IO_URING_NAPI_STATIC_DEL_ID: if (curr.op_param != IO_URING_NAPI_TRACKING_STATIC) return -EINVAL; @@ -341,9 +355,10 @@ int io_unregister_napi(struct io_ring_ctx *ctx, void __user *arg) if (arg && copy_to_user(arg, &curr, sizeof(curr))) return -EFAULT; + WRITE_ONCE(ctx->napi_track_mode, IO_URING_NAPI_TRACKING_INACTIVE); WRITE_ONCE(ctx->napi_busy_poll_dt, 0); WRITE_ONCE(ctx->napi_prefer_busy_poll, false); - WRITE_ONCE(ctx->napi_track_mode, IO_URING_NAPI_TRACKING_INACTIVE); + io_napi_free(ctx); return 0; } diff --git a/io_uring/napi.h b/io_uring/napi.h index fa742f42e09b..e0aecccc5065 100644 --- a/io_uring/napi.h +++ b/io_uring/napi.h @@ -15,7 +15,8 @@ void io_napi_free(struct io_ring_ctx *ctx); int io_register_napi(struct io_ring_ctx *ctx, void __user *arg); int io_unregister_napi(struct io_ring_ctx *ctx, void __user *arg); -int __io_napi_add_id(struct io_ring_ctx *ctx, unsigned int napi_id); +int __io_napi_add_id(struct io_ring_ctx *ctx, unsigned int napi_id, + unsigned int mode); void __io_napi_busy_loop(struct io_ring_ctx *ctx, struct io_wait_queue *iowq); int io_napi_sqpoll_busy_poll(struct io_ring_ctx *ctx); @@ -43,13 +44,14 @@ static inline void io_napi_add(struct io_kiocb *req) { struct io_ring_ctx *ctx = req->ctx; struct socket *sock; + unsigned int mode = IO_URING_NAPI_TRACKING_DYNAMIC; - if (READ_ONCE(ctx->napi_track_mode) != IO_URING_NAPI_TRACKING_DYNAMIC) + if (READ_ONCE(ctx->napi_track_mode) != mode) return; sock = sock_from_file(req->file); if (sock && sock->sk) - __io_napi_add_id(ctx, READ_ONCE(sock->sk->sk_napi_id)); + __io_napi_add_id(ctx, READ_ONCE(sock->sk->sk_napi_id), mode); } #else diff --git a/io_uring/poll.c b/io_uring/poll.c index 74eef7884159..0204affdc308 100644 --- a/io_uring/poll.c +++ b/io_uring/poll.c @@ -93,7 +93,7 @@ static bool io_poll_get_ownership_slowpath(struct io_kiocb *req) */ static inline bool io_poll_get_ownership(struct io_kiocb *req) { - if (unlikely(atomic_read(&req->poll_refs) >= IO_POLL_REF_BIAS)) + if (unlikely((unsigned int)atomic_read(&req->poll_refs) >= IO_POLL_REF_BIAS)) return io_poll_get_ownership_slowpath(req); return !(atomic_fetch_inc(&req->poll_refs) & IO_POLL_REF_MASK); } @@ -417,8 +417,10 @@ static int io_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, * disable multishot as there is a circular dependency between * CQ posting and triggering the event. */ - if (mask & EPOLL_URING_WAKE) + if (mask & EPOLL_URING_WAKE) { poll->events |= EPOLLONESHOT; + req->apoll_events |= EPOLLONESHOT; + } /* optional, saves extra locking for removal in tw handler */ if (mask && poll->events & EPOLLONESHOT) { diff --git a/io_uring/register.c b/io_uring/register.c index 24e593332d1a..dce5e2f9cf77 100644 --- a/io_uring/register.c +++ b/io_uring/register.c @@ -599,10 +599,20 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) if (tail - old_head > p->sq_entries) goto overflow; for (i = old_head; i < tail; i++) { - unsigned src_head = i & (ctx->sq_entries - 1); - unsigned dst_head = i & (p->sq_entries - 1); + unsigned index, dst_mask, src_mask; + size_t sq_size; - n.sq_sqes[dst_head] = o.sq_sqes[src_head]; + index = i; + sq_size = sizeof(struct io_uring_sqe); + src_mask = ctx->sq_entries - 1; + dst_mask = p->sq_entries - 1; + if (ctx->flags & IORING_SETUP_SQE128) { + index <<= 1; + sq_size <<= 1; + src_mask = (ctx->sq_entries << 1) - 1; + dst_mask = (p->sq_entries << 1) - 1; + } + memcpy(&n.sq_sqes[index & dst_mask], &o.sq_sqes[index & src_mask], sq_size); } WRITE_ONCE(n.rings->sq.head, old_head); WRITE_ONCE(n.rings->sq.tail, tail); @@ -619,10 +629,20 @@ overflow: goto out; } for (i = old_head; i < tail; i++) { - unsigned src_head = i & (ctx->cq_entries - 1); - unsigned dst_head = i & (p->cq_entries - 1); + unsigned index, dst_mask, src_mask; + size_t cq_size; - n.rings->cqes[dst_head] = o.rings->cqes[src_head]; + index = i; + cq_size = sizeof(struct io_uring_cqe); + src_mask = ctx->cq_entries - 1; + dst_mask = p->cq_entries - 1; + if (ctx->flags & IORING_SETUP_CQE32) { + index <<= 1; + cq_size <<= 1; + src_mask = (ctx->cq_entries << 1) - 1; + dst_mask = (p->cq_entries << 1) - 1; + } + memcpy(&n.rings->cqes[index & dst_mask], &o.rings->cqes[index & src_mask], cq_size); } WRITE_ONCE(n.rings->cq.head, old_head); WRITE_ONCE(n.rings->cq.tail, tail); diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c index fd36e0e319a2..650303626be6 100644 --- a/io_uring/rsrc.c +++ b/io_uring/rsrc.c @@ -168,7 +168,7 @@ bool io_rsrc_cache_init(struct io_ring_ctx *ctx) void io_rsrc_cache_free(struct io_ring_ctx *ctx) { io_alloc_cache_free(&ctx->node_cache, kfree); - io_alloc_cache_free(&ctx->imu_cache, kfree); + io_alloc_cache_free(&ctx->imu_cache, kvfree); } static void io_clear_table_tags(struct io_rsrc_data *data) @@ -238,6 +238,9 @@ static int __io_sqe_files_update(struct io_ring_ctx *ctx, continue; i = up->offset + done; + if (i >= ctx->file_table.data.nr) + break; + i = array_index_nospec(i, ctx->file_table.data.nr); if (io_reset_rsrc_node(ctx, &ctx->file_table.data, i)) io_file_bitmap_clear(&ctx->file_table, i); diff --git a/io_uring/rsrc.h b/io_uring/rsrc.h index cff0f8834c35..44e3386f7c1c 100644 --- a/io_uring/rsrc.h +++ b/io_uring/rsrc.h @@ -109,10 +109,15 @@ static inline void io_put_rsrc_node(struct io_ring_ctx *ctx, struct io_rsrc_node } static inline bool io_reset_rsrc_node(struct io_ring_ctx *ctx, - struct io_rsrc_data *data, int index) + struct io_rsrc_data *data, + unsigned int index) { - struct io_rsrc_node *node = data->nodes[index]; + struct io_rsrc_node *node; + if (index >= data->nr) + return false; + index = array_index_nospec(index, data->nr); + node = data->nodes[index]; if (!node) return false; io_put_rsrc_node(ctx, node); diff --git a/io_uring/rw.c b/io_uring/rw.c index 20654deff84d..0c4834645279 100644 --- a/io_uring/rw.c +++ b/io_uring/rw.c @@ -230,7 +230,7 @@ static inline void io_meta_restore(struct io_async_rw *io, struct kiocb *kiocb) } static int io_prep_rw_pi(struct io_kiocb *req, struct io_rw *rw, int ddir, - u64 attr_ptr, u64 attr_type_mask) + u64 attr_ptr) { struct io_uring_attr_pi pi_attr; struct io_async_rw *io; @@ -305,7 +305,7 @@ static int __io_prep_rw(struct io_kiocb *req, const struct io_uring_sqe *sqe, return -EINVAL; attr_ptr = READ_ONCE(sqe->attr_ptr); - return io_prep_rw_pi(req, rw, ddir, attr_ptr, attr_type_mask); + return io_prep_rw_pi(req, rw, ddir, attr_ptr); } return 0; } @@ -580,7 +580,7 @@ void io_req_rw_complete(struct io_tw_req tw_req, io_tw_token_t tw) io_req_io_end(req); if (req->flags & (REQ_F_BUFFER_SELECTED|REQ_F_BUFFER_RING)) - req->cqe.flags |= io_put_kbuf(req, req->cqe.res, NULL); + req->cqe.flags |= io_put_kbuf(req, max(req->cqe.res, 0), NULL); io_req_rw_cleanup(req, 0); io_req_task_complete(tw_req, tw); @@ -1379,7 +1379,7 @@ int io_do_iopoll(struct io_ring_ctx *ctx, bool force_nonspin) list_del(&req->iopoll_node); wq_list_add_tail(&req->comp_list, &ctx->submit_state.compl_reqs); nr_events++; - req->cqe.flags = io_put_kbuf(req, req->cqe.res, NULL); + req->cqe.flags = io_put_kbuf(req, max(req->cqe.res, 0), NULL); if (!io_is_uring_cmd(req)) io_req_rw_cleanup(req, 0); } diff --git a/io_uring/tctx.c b/io_uring/tctx.c index 61533f30494f..6af62ca9baba 100644 --- a/io_uring/tctx.c +++ b/io_uring/tctx.c @@ -146,9 +146,13 @@ int __io_uring_add_tctx_node(struct io_ring_ctx *ctx) if (IS_ERR(tctx)) return PTR_ERR(tctx); - if (ctx->int_flags & IO_RING_F_IOWQ_LIMITS_SET) { - unsigned int limits[2] = { ctx->iowq_limits[0], - ctx->iowq_limits[1], }; + if (data_race(ctx->int_flags) & IO_RING_F_IOWQ_LIMITS_SET) { + unsigned int limits[2]; + + mutex_lock(&ctx->uring_lock); + limits[0] = ctx->iowq_limits[0]; + limits[1] = ctx->iowq_limits[1]; + mutex_unlock(&ctx->uring_lock); ret = io_wq_max_workers(tctx->io_wq, limits); if (ret) @@ -171,7 +175,10 @@ int __io_uring_add_tctx_node(struct io_ring_ctx *ctx) } if (!current->io_uring) { err_free: - io_wq_put_and_exit(tctx->io_wq); + if (tctx->io_wq) { + io_wq_exit_start(tctx->io_wq); + io_wq_put_and_exit(tctx->io_wq); + } percpu_counter_destroy(&tctx->inflight); kfree(tctx); } diff --git a/io_uring/timeout.c b/io_uring/timeout.c index 4cfdfc519770..6353a4d979dc 100644 --- a/io_uring/timeout.c +++ b/io_uring/timeout.c @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -35,6 +36,22 @@ struct io_timeout_rem { bool ltimeout; }; +static clockid_t io_flags_to_clock(unsigned flags) +{ + switch (flags & IORING_TIMEOUT_CLOCK_MASK) { + case IORING_TIMEOUT_BOOTTIME: + return CLOCK_BOOTTIME; + case IORING_TIMEOUT_REALTIME: + return CLOCK_REALTIME; + default: + /* can't happen, vetted at prep time */ + WARN_ON_ONCE(1); + fallthrough; + case 0: + return CLOCK_MONOTONIC; + } +} + static int io_parse_user_time(ktime_t *time, u64 arg, unsigned flags) { struct timespec64 ts; @@ -43,7 +60,7 @@ static int io_parse_user_time(ktime_t *time, u64 arg, unsigned flags) *time = ns_to_ktime(arg); if (*time < 0) return -EINVAL; - return 0; + goto out; } if (get_timespec64(&ts, u64_to_user_ptr(arg))) @@ -51,6 +68,9 @@ static int io_parse_user_time(ktime_t *time, u64 arg, unsigned flags) if (ts.tv_sec < 0 || ts.tv_nsec < 0) return -EINVAL; *time = timespec64_to_ktime(ts); +out: + if (flags & IORING_TIMEOUT_ABS) + *time = timens_ktime_to_host(io_flags_to_clock(flags), *time); return 0; } @@ -264,6 +284,10 @@ static struct io_kiocb *__io_disarm_linked_timeout(struct io_kiocb *req, struct io_timeout *timeout = io_kiocb_to_cmd(link, struct io_timeout); io_remove_next_linked(req); + + /* If this is NULL, then timer already claimed it and will complete it */ + if (!timeout->head) + return NULL; timeout->head = NULL; if (hrtimer_try_to_cancel(&io->timer) != -1) { list_del(&timeout->list); @@ -347,6 +371,14 @@ static void io_req_task_link_timeout(struct io_tw_req tw_req, io_tw_token_t tw) int ret; if (prev) { + /* + * splice the linked timeout out of prev's chain if the regular + * completion path didn't already do it. + */ + if (prev->link == req) + prev->link = req->link; + req->link = NULL; + if (!tw.cancel) { struct io_cancel_data cd = { .ctx = req->ctx, @@ -381,10 +413,10 @@ static enum hrtimer_restart io_link_timeout_fn(struct hrtimer *timer) /* * We don't expect the list to be empty, that will only happen if we - * race with the completion of the linked work. + * race with the completion of the linked work. Splice of prev is + * done in io_req_task_link_timeout(), if needed. */ if (prev) { - io_remove_next_linked(prev); if (!req_ref_inc_not_zero(prev)) prev = NULL; } @@ -399,18 +431,7 @@ static enum hrtimer_restart io_link_timeout_fn(struct hrtimer *timer) static clockid_t io_timeout_get_clock(struct io_timeout_data *data) { - switch (data->flags & IORING_TIMEOUT_CLOCK_MASK) { - case IORING_TIMEOUT_BOOTTIME: - return CLOCK_BOOTTIME; - case IORING_TIMEOUT_REALTIME: - return CLOCK_REALTIME; - default: - /* can't happen, vetted at prep time */ - WARN_ON_ONCE(1); - fallthrough; - case 0: - return CLOCK_MONOTONIC; - } + return io_flags_to_clock(data->flags); } static int io_linked_timeout_update(struct io_ring_ctx *ctx, __u64 user_data, diff --git a/io_uring/tw.c b/io_uring/tw.c index fdff81eebc95..023d5e6bc491 100644 --- a/io_uring/tw.c +++ b/io_uring/tw.c @@ -273,8 +273,18 @@ void io_req_task_work_add_remote(struct io_kiocb *req, unsigned flags) void __cold io_move_task_work_from_local(struct io_ring_ctx *ctx) { - struct llist_node *node = llist_del_all(&ctx->work_llist); + struct llist_node *node; + /* + * Running the work items may utilize ->retry_llist as a means + * for capping the number of task_work entries run at the same + * time. But that list can potentially race with moving the work + * from here, if the task is exiting. As any normal task_work + * running holds ->uring_lock already, just guard this slow path + * with ->uring_lock to avoid racing on ->retry_llist. + */ + guard(mutex)(&ctx->uring_lock); + node = llist_del_all(&ctx->work_llist); __io_fallback_tw(node, false); node = llist_del_all(&ctx->retry_llist); __io_fallback_tw(node, false); diff --git a/io_uring/wait.c b/io_uring/wait.c index 91df86ce0d18..ec01e78a216d 100644 --- a/io_uring/wait.c +++ b/io_uring/wait.c @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -229,7 +230,10 @@ int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags, if (ext_arg->ts_set) { iowq.timeout = timespec64_to_ktime(ext_arg->ts); - if (!(flags & IORING_ENTER_ABS_TIMER)) + if (flags & IORING_ENTER_ABS_TIMER) + iowq.timeout = timens_ktime_to_host(ctx->clockid, + iowq.timeout); + else iowq.timeout = ktime_add(iowq.timeout, start_time); } diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index 9a83d7eb4210..19837e0b5e91 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -396,6 +396,7 @@ static int io_allocate_rbuf_ring(struct io_ring_ctx *ctx, ifq->rq.ring = (struct io_uring *)ptr; ifq->rq.rqes = (struct io_uring_zcrx_rqe *)(ptr + off); + memset(ifq->rq.ring, 0, sizeof(*ifq->rq.ring)); return 0; } @@ -494,10 +495,9 @@ static int io_zcrx_create_area(struct io_zcrx_ifq *ifq, for (i = 0; i < nr_iovs; i++) { struct net_iov *niov = &area->nia.niovs[i]; - niov->owner = &area->nia; + net_iov_init(niov, &area->nia, NET_IOV_IOURING); area->freelist[i] = i; atomic_set(&area->user_refs[i], 0); - niov->type = NET_IOV_IOURING; } if (ifq->dev) { @@ -579,13 +579,13 @@ static void io_zcrx_ifq_free(struct io_zcrx_ifq *ifq) if (ifq->area) io_zcrx_free_area(ifq, ifq->area); - free_uid(ifq->user); if (ifq->mm_account) mmdrop(ifq->mm_account); if (ifq->dev) put_device(ifq->dev); io_free_rbuf_ring(ifq); + free_uid(ifq->user); mutex_destroy(&ifq->pp_lock); kfree(ifq); } @@ -601,6 +601,8 @@ static void io_zcrx_return_niov_freelist(struct net_iov *niov) struct io_zcrx_area *area = io_zcrx_iov_to_area(niov); guard(spinlock_bh)(&area->freelist_lock); + if (WARN_ON_ONCE(area->free_count >= area->nia.num_niovs)) + return; area->freelist[area->free_count++] = net_iov_idx(niov); } diff --git a/kernel/audit.c b/kernel/audit.c index e1d489bc2dff..34dc7cb246ff 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1468,6 +1468,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh, err = audit_list_rules_send(skb, seq); break; case AUDIT_TRIM: + if (audit_enabled == AUDIT_LOCKED) + return -EPERM; audit_trim_trees(); audit_log_common_recv_msg(audit_context(), &ab, AUDIT_CONFIG_CHANGE); @@ -1480,6 +1482,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh, size_t msglen = data_len; char *old, *new; + if (audit_enabled == AUDIT_LOCKED) + return -EPERM; err = -EINVAL; if (msglen < 2 * sizeof(u32)) break; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index ab54fccba215..abdf8da3be93 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2786,7 +2786,7 @@ void __audit_log_capset(const struct cred *new, const struct cred *old) context->capset.pid = task_tgid_nr(current); context->capset.cap.effective = new->cap_effective; - context->capset.cap.inheritable = new->cap_effective; + context->capset.cap.inheritable = new->cap_inheritable; context->capset.cap.permitted = new->cap_permitted; context->capset.cap.ambient = new->cap_ambient; context->type = AUDIT_CAPSET; diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c index 08d008cc471e..1727503b25d8 100644 --- a/kernel/bpf/arena.c +++ b/kernel/bpf/arena.c @@ -53,6 +53,7 @@ struct bpf_arena { u64 user_vm_start; u64 user_vm_end; struct vm_struct *kern_vm; + struct page *scratch_page; struct range_tree rt; /* protects rt */ rqspinlock_t spinlock; @@ -83,6 +84,32 @@ u64 bpf_arena_get_user_vm_start(struct bpf_arena *arena) return arena ? arena->user_vm_start : 0; } +/** + * bpf_arena_map_kern_vm_start - kern_vm_start lookup by struct bpf_map * + * @map: a BPF_MAP_TYPE_ARENA map + * + * Return @map's kern_vm_start. + */ +u64 bpf_arena_map_kern_vm_start(struct bpf_map *map) +{ + return bpf_arena_get_kern_vm_start(container_of(map, struct bpf_arena, map)); +} + +/** + * bpf_prog_arena - return the bpf_map of the arena referenced by @prog + * @prog: a loaded BPF program + * + * The verifier enforces at most one arena per program and stores it in + * prog->aux->arena. Return that arena's underlying bpf_map, or NULL if + * @prog does not reference an arena. + */ +struct bpf_map *bpf_prog_arena(struct bpf_prog *prog) +{ + struct bpf_arena *arena = prog->aux->arena; + + return arena ? &arena->map : NULL; +} + static long arena_map_peek_elem(struct bpf_map *map, void *value) { return -EOPNOTSUPP; @@ -118,6 +145,11 @@ struct apply_range_data { int i; }; +struct clear_range_data { + struct llist_head *free_pages; + struct page *scratch_page; +}; + static int apply_range_set_cb(pte_t *pte, unsigned long addr, void *data) { struct apply_range_data *d = data; @@ -144,33 +176,59 @@ static void flush_vmap_cache(unsigned long start, unsigned long size) flush_cache_vmap(start, start + size); } -static int apply_range_clear_cb(pte_t *pte, unsigned long addr, void *free_pages) +static int apply_range_clear_cb(pte_t *pte, unsigned long addr, void *data) { + struct clear_range_data *d = data; pte_t old_pte; struct page *page; - /* sanity check */ - old_pte = ptep_get(pte); + /* + * Pairs with ptep_try_set() in the kernel-fault scratch installer. + * Both sides must be atomic. + */ + old_pte = ptep_get_and_clear(&init_mm, addr, pte); if (pte_none(old_pte) || !pte_present(old_pte)) - return 0; /* nothing to do */ + return 0; page = pte_page(old_pte); if (WARN_ON_ONCE(!page)) return -EINVAL; - pte_clear(&init_mm, addr, pte); + /* + * Skip the per-arena scratch page. A kernel fault on an unallocated uaddr + * scratches its PTE. A later bpf_arena_free_pages() over that range walks + * here. Without the skip, scratch_page would be freed. + */ + if (page == d->scratch_page) + return 0; - /* Add page to the list so it is freed later */ - if (free_pages) - __llist_add(&page->pcp_llist, free_pages); + __llist_add(&page->pcp_llist, d->free_pages); + return 0; +} +static int apply_range_set_scratch_cb(pte_t *pte, unsigned long addr, void *data) +{ + struct page *scratch_page = data; + + if (!pte_none(ptep_get(pte))) + return 0; + /* + * Best-effort install. ptep_try_set() returns false only if another + * installer (real allocation or concurrent fault) won the cmpxchg. + * Their PTE is already valid, so the access retry succeeds. + * + * No flush_tlb_kernel_range() needed. Stale "not mapped" entries just + * cause one extra re-fault through this same path. + */ + ptep_try_set(pte, mk_pte(scratch_page, PAGE_KERNEL)); return 0; } static int populate_pgtable_except_pte(struct bpf_arena *arena) { + /* Populate intermediates for the recovery range (4 GiB + upper half-guard). */ return apply_to_page_range(&init_mm, bpf_arena_get_kern_vm_start(arena), - KERN_VM_SZ - GUARD_SZ, apply_range_set_cb, NULL); + SZ_4G + GUARD_SZ / 2, apply_range_set_cb, NULL); } static struct bpf_map *arena_map_alloc(union bpf_attr *attr) @@ -221,22 +279,29 @@ static struct bpf_map *arena_map_alloc(union bpf_attr *attr) init_irq_work(&arena->free_irq, arena_free_irq); INIT_WORK(&arena->free_work, arena_free_worker); bpf_map_init_from_attr(&arena->map, attr); + + err = bpf_map_alloc_pages(&arena->map, NUMA_NO_NODE, 1, &arena->scratch_page); + if (err) + goto err_free_arena; + range_tree_init(&arena->rt); err = range_tree_set(&arena->rt, 0, attr->max_entries); - if (err) { - bpf_map_area_free(arena); - goto err; - } + if (err) + goto err_free_scratch; mutex_init(&arena->lock); raw_res_spin_lock_init(&arena->spinlock); err = populate_pgtable_except_pte(arena); - if (err) { - range_tree_destroy(&arena->rt); - bpf_map_area_free(arena); - goto err; - } + if (err) + goto err_destroy_rt; return &arena->map; + +err_destroy_rt: + range_tree_destroy(&arena->rt); +err_free_scratch: + __free_page(arena->scratch_page); +err_free_arena: + bpf_map_area_free(arena); err: free_vm_area(kern_vm); return ERR_PTR(err); @@ -244,6 +309,7 @@ err: static int existing_page_cb(pte_t *ptep, unsigned long addr, void *data) { + struct bpf_arena *arena = data; struct page *page; pte_t pte; @@ -251,6 +317,12 @@ static int existing_page_cb(pte_t *ptep, unsigned long addr, void *data) if (!pte_present(pte)) /* sanity check */ return 0; page = pte_page(pte); + /* + * Skip the scratch page. The walk is page-table-driven, not range-tree-driven, + * so it can visit scratch PTEs at uaddrs the BPF program never allocated. + */ + if (page == arena->scratch_page) + return 0; /* * We do not update pte here: * 1. Nobody should be accessing bpf_arena's range outside of a kernel bug @@ -286,9 +358,10 @@ static void arena_map_free(struct bpf_map *map) * free those pages. */ apply_to_existing_page_range(&init_mm, bpf_arena_get_kern_vm_start(arena), - KERN_VM_SZ - GUARD_SZ, existing_page_cb, NULL); + SZ_4G + GUARD_SZ / 2, existing_page_cb, arena); free_vm_area(arena->kern_vm); range_tree_destroy(&arena->rt); + __free_page(arena->scratch_page); bpf_map_area_free(arena); } @@ -341,6 +414,16 @@ static void arena_vm_open(struct vm_area_struct *vma) refcount_inc(&vml->mmap_count); } +static int arena_vm_may_split(struct vm_area_struct *vma, unsigned long addr) +{ + return -EINVAL; +} + +static int arena_vm_mremap(struct vm_area_struct *vma) +{ + return -EINVAL; +} + static void arena_vm_close(struct vm_area_struct *vma) { struct bpf_map *map = vma->vm_file->private_data; @@ -374,33 +457,37 @@ static vm_fault_t arena_vm_fault(struct vm_fault *vmf) return VM_FAULT_RETRY; page = vmalloc_to_page((void *)kaddr); - if (page) + if (page) { + if (page == arena->scratch_page) + /* BPF triggered scratch here; don't lazy-alloc over it */ + goto out_sigsegv; /* already have a page vmap-ed */ goto out; + } bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg); if (arena->map.map_flags & BPF_F_SEGV_ON_FAULT) /* User space requested to segfault when page is not allocated by bpf prog */ - goto out_unlock_sigsegv; + goto out_sigsegv_memcg; ret = range_tree_clear(&arena->rt, vmf->pgoff, 1); if (ret) - goto out_unlock_sigsegv; + goto out_sigsegv_memcg; struct apply_range_data data = { .pages = &page, .i = 0 }; /* Account into memcg of the process that created bpf_arena */ ret = bpf_map_alloc_pages(map, NUMA_NO_NODE, 1, &page); if (ret) { range_tree_set(&arena->rt, vmf->pgoff, 1); - goto out_unlock_sigsegv; + goto out_sigsegv_memcg; } ret = apply_to_page_range(&init_mm, kaddr, PAGE_SIZE, apply_range_set_cb, &data); if (ret) { range_tree_set(&arena->rt, vmf->pgoff, 1); free_pages_nolock(page, 0); - goto out_unlock_sigsegv; + goto out_sigsegv_memcg; } flush_vmap_cache(kaddr, PAGE_SIZE); bpf_map_memcg_exit(old_memcg, new_memcg); @@ -409,14 +496,17 @@ out: raw_res_spin_unlock_irqrestore(&arena->spinlock, flags); vmf->page = page; return 0; -out_unlock_sigsegv: +out_sigsegv_memcg: bpf_map_memcg_exit(old_memcg, new_memcg); +out_sigsegv: raw_res_spin_unlock_irqrestore(&arena->spinlock, flags); return VM_FAULT_SIGSEGV; } static const struct vm_operations_struct arena_vm_ops = { .open = arena_vm_open, + .may_split = arena_vm_may_split, + .mremap = arena_vm_mremap, .close = arena_vm_close, .fault = arena_vm_fault, }; @@ -486,10 +576,11 @@ static int arena_map_mmap(struct bpf_map *map, struct vm_area_struct *vma) arena->user_vm_end = vma->vm_end; /* * bpf_map_mmap() checks that it's being mmaped as VM_SHARED and - * clears VM_MAYEXEC. Set VM_DONTEXPAND as well to avoid - * potential change of user_vm_start. + * clears VM_MAYEXEC. Set VM_DONTEXPAND to avoid potential change + * of user_vm_start. Set VM_DONTCOPY to prevent arena VMA from + * being copied into the child process on fork. */ - vm_flags_set(vma, VM_DONTEXPAND); + vm_flags_set(vma, VM_DONTEXPAND | VM_DONTCOPY); vma->vm_ops = &arena_vm_ops; return 0; } @@ -498,7 +589,7 @@ static int arena_map_direct_value_addr(const struct bpf_map *map, u64 *imm, u32 { struct bpf_arena *arena = container_of(map, struct bpf_arena, map); - if ((u64)off > arena->user_vm_end - arena->user_vm_start) + if ((u64)off >= arena->user_vm_end - arena->user_vm_start) return -ERANGE; *imm = (unsigned long)arena->user_vm_start; return 0; @@ -549,6 +640,10 @@ static long arena_alloc_pages(struct bpf_arena *arena, long uaddr, long page_cnt u32 uaddr32; int ret, i; + if (node_id != NUMA_NO_NODE && + ((unsigned int)node_id >= nr_node_ids || !node_online(node_id))) + return 0; + if (page_cnt > page_cnt_max) return 0; @@ -668,6 +763,7 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt, struct llist_head free_pages; struct llist_node *pos, *t; struct arena_free_span *s; + struct clear_range_data cdata; unsigned long flags; int ret = 0; @@ -696,9 +792,11 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt, range_tree_set(&arena->rt, pgoff, page_cnt); init_llist_head(&free_pages); + cdata.free_pages = &free_pages; + cdata.scratch_page = arena->scratch_page; /* clear ptes and collect struct pages */ apply_to_existing_page_range(&init_mm, kaddr, page_cnt << PAGE_SHIFT, - apply_range_clear_cb, &free_pages); + apply_range_clear_cb, &cdata); /* drop the lock to do the tlb flush and zap pages */ raw_res_spin_unlock_irqrestore(&arena->spinlock, flags); @@ -788,6 +886,7 @@ static void arena_free_worker(struct work_struct *work) struct arena_free_span *s; u64 arena_vm_start, user_vm_start; struct llist_head free_pages; + struct clear_range_data cdata; struct page *page; unsigned long full_uaddr; long kaddr, page_cnt, pgoff; @@ -801,6 +900,8 @@ static void arena_free_worker(struct work_struct *work) bpf_map_memcg_enter(&arena->map, &old_memcg, &new_memcg); init_llist_head(&free_pages); + cdata.free_pages = &free_pages; + cdata.scratch_page = arena->scratch_page; arena_vm_start = bpf_arena_get_kern_vm_start(arena); user_vm_start = bpf_arena_get_user_vm_start(arena); @@ -813,7 +914,7 @@ static void arena_free_worker(struct work_struct *work) /* clear ptes and collect pages in free_pages llist */ apply_to_existing_page_range(&init_mm, kaddr, page_cnt << PAGE_SHIFT, - apply_range_clear_cb, &free_pages); + apply_range_clear_cb, &cdata); range_tree_set(&arena->rt, pgoff, page_cnt); } @@ -876,6 +977,19 @@ void *bpf_arena_alloc_pages_non_sleepable(void *p__map, void *addr__ign, u32 pag return (void *)arena_alloc_pages(arena, (long)addr__ign, page_cnt, node_id, false); } + +void *bpf_arena_alloc_pages_sleepable(void *p__map, void *addr__ign, u32 page_cnt, + int node_id, u64 flags) +{ + struct bpf_map *map = p__map; + struct bpf_arena *arena = container_of(map, struct bpf_arena, map); + + if (map->map_type != BPF_MAP_TYPE_ARENA || flags || !page_cnt) + return NULL; + + return (void *)arena_alloc_pages(arena, (long)addr__ign, page_cnt, node_id, true); +} + __bpf_kfunc void bpf_arena_free_pages(void *p__map, void *ptr__ign, u32 page_cnt) { struct bpf_map *map = p__map; @@ -928,23 +1042,12 @@ static int __init kfunc_init(void) } late_initcall(kfunc_init); -void bpf_prog_report_arena_violation(bool write, unsigned long addr, unsigned long fault_ip) +static void __bpf_prog_report_arena_violation(struct bpf_prog *prog, bool write, + unsigned long addr, unsigned long fault_ip) { struct bpf_stream_stage ss; - struct bpf_prog *prog; u64 user_vm_start; - /* - * The RCU read lock is held to safely traverse the latch tree, but we - * don't need its protection when accessing the prog, since it will not - * disappear while we are handling the fault. - */ - rcu_read_lock(); - prog = bpf_prog_ksym_find(fault_ip); - rcu_read_unlock(); - if (!prog) - return; - /* Use main prog for stream access */ prog = prog->aux->main_prog_aux->prog; @@ -957,3 +1060,53 @@ void bpf_prog_report_arena_violation(bool write, unsigned long addr, unsigned lo bpf_stream_dump_stack(ss); })); } + +bool bpf_arena_handle_page_fault(unsigned long addr, bool is_write, unsigned long fault_ip) +{ + struct bpf_arena *arena; + struct bpf_prog *prog; + unsigned long kbase; + unsigned long page_addr = addr & PAGE_MASK; + + prog = bpf_prog_find_from_stack(); + if (!prog) + return false; + + arena = prog->aux->arena; + /* a prog not using arena may be on stack, so arena can be NULL */ + if (!arena) + return false; + + kbase = bpf_arena_get_kern_vm_start(arena); + + /* + * Recovery covers the 4 GiB mappable band plus the upper half-guard. + * Lower guard is unreachable from kfuncs; an address there indicates + * a different bug class - leave it to the regular kernel oops path. + */ + if (page_addr < kbase || page_addr >= kbase + SZ_4G + GUARD_SZ / 2) + return false; + + apply_to_page_range(&init_mm, page_addr, PAGE_SIZE, + apply_range_set_scratch_cb, arena->scratch_page); + flush_vmap_cache(page_addr, PAGE_SIZE); + __bpf_prog_report_arena_violation(prog, is_write, page_addr - kbase, fault_ip); + return true; +} + +void bpf_prog_report_arena_violation(bool write, unsigned long addr, unsigned long fault_ip) +{ + struct bpf_prog *prog; + + /* + * The RCU read lock is held to safely traverse the latch tree, but we + * don't need its protection when accessing the prog, since it will not + * disappear while we are handling the fault. + */ + rcu_read_lock(); + prog = bpf_prog_ksym_find(fault_ip); + rcu_read_unlock(); + if (!prog) + return; + __bpf_prog_report_arena_violation(prog, write, addr, fault_ip); +} diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c index 05b366b821c3..5e51c1211673 100644 --- a/kernel/bpf/bpf_struct_ops.c +++ b/kernel/bpf/bpf_struct_ops.c @@ -811,9 +811,6 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, goto reset_unlock; } - /* Poison pointer on error instead of return for backward compatibility */ - bpf_prog_assoc_struct_ops(prog, &st_map->map); - link = kzalloc_obj(*link, GFP_USER); if (!link) { bpf_prog_put(prog); @@ -824,6 +821,9 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, &bpf_struct_ops_link_lops, prog, prog->expected_attach_type); *plink++ = &link->link; + /* Poison pointer on error instead of return for backward compatibility */ + bpf_prog_assoc_struct_ops(prog, &st_map->map); + ksym = kzalloc_obj(*ksym, GFP_USER); if (!ksym) { err = -ENOMEM; @@ -906,6 +906,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, reset_unlock: bpf_struct_ops_map_free_ksyms(st_map); bpf_struct_ops_map_free_image(st_map); + bpf_struct_ops_map_dissoc_progs(st_map); bpf_struct_ops_map_put_progs(st_map); memset(uvalue, 0, map->value_size); memset(kvalue, 0, map->value_size); @@ -1203,6 +1204,42 @@ u32 bpf_struct_ops_id(const void *kdata) } EXPORT_SYMBOL_GPL(bpf_struct_ops_id); +/** + * bpf_struct_ops_for_each_prog - Invoke @cb for each member prog + * @kdata: kernel-side struct_ops vmtable (the @kdata arg to ->reg/->update/->unreg) + * @cb: callback invoked once per member prog; non-zero return stops iteration + * @data: opaque argument passed to @cb + * + * Walks the struct_ops member progs registered on the map containing @kdata. + * Intended for use from struct_ops ->reg() callbacks (and similar) that need to + * inspect the loaded BPF programs (for example to discover maps they reference + * via @prog->aux->used_maps). + * + * Return 0 if iteration completed, otherwise the first non-zero @cb return. + */ +int bpf_struct_ops_for_each_prog(const void *kdata, + int (*cb)(struct bpf_prog *prog, void *data), + void *data) +{ + struct bpf_struct_ops_value *kvalue; + struct bpf_struct_ops_map *st_map; + u32 i; + int ret; + + kvalue = container_of(kdata, struct bpf_struct_ops_value, data); + st_map = container_of(kvalue, struct bpf_struct_ops_map, kvalue); + + for (i = 0; i < st_map->funcs_cnt; i++) { + if (!st_map->links[i]) + continue; + ret = cb(st_map->links[i]->prog, data); + if (ret) + return ret; + } + return 0; +} +EXPORT_SYMBOL_GPL(bpf_struct_ops_for_each_prog); + static bool bpf_struct_ops_valid_to_reg(struct bpf_map *map) { struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map; diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 066b86e7233c..fc3ee67486ce 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1491,24 +1491,11 @@ void bpf_jit_prog_release_other(struct bpf_prog *fp, struct bpf_prog *fp_other) bpf_prog_clone_free(fp_other); } -static void adjust_insn_arrays(struct bpf_prog *prog, u32 off, u32 len) -{ -#ifdef CONFIG_BPF_SYSCALL - struct bpf_map *map; - int i; - - if (len <= 1) - return; - - for (i = 0; i < prog->aux->used_map_cnt; i++) { - map = prog->aux->used_maps[i]; - if (map->map_type == BPF_MAP_TYPE_INSN_ARRAY) - bpf_insn_array_adjust(map, off, len); - } -#endif -} - -struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog) +/* + * Now this function is used only to blind the main prog and must be invoked only when + * bpf_prog_need_blind() returns true. + */ +struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bpf_prog *prog) { struct bpf_insn insn_buff[16], aux[2]; struct bpf_prog *clone, *tmp; @@ -1516,13 +1503,17 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog) struct bpf_insn *insn; int i, rewritten; - if (!prog->blinding_requested || prog->blinded) - return prog; + if (WARN_ON_ONCE(env && env->prog != prog)) + return ERR_PTR(-EINVAL); clone = bpf_prog_clone_create(prog, GFP_USER); if (!clone) return ERR_PTR(-ENOMEM); + /* make sure bpf_patch_insn_data() patches the correct prog */ + if (env) + env->prog = clone; + insn_cnt = clone->len; insn = clone->insnsi; @@ -1550,21 +1541,28 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog) if (!rewritten) continue; - tmp = bpf_patch_insn_single(clone, i, insn_buff, rewritten); - if (IS_ERR(tmp)) { + if (env) + tmp = bpf_patch_insn_data(env, i, insn_buff, rewritten); + else + tmp = bpf_patch_insn_single(clone, i, insn_buff, rewritten); + + if (IS_ERR_OR_NULL(tmp)) { + if (env) + /* restore the original prog */ + env->prog = prog; /* Patching may have repointed aux->prog during * realloc from the original one, so we need to * fix it up here on error. */ bpf_jit_prog_release_other(prog, clone); - return tmp; + return IS_ERR(tmp) ? tmp : ERR_PTR(-ENOMEM); } clone = tmp; insn_delta = rewritten - 1; - /* Instructions arrays must be updated using absolute xlated offsets */ - adjust_insn_arrays(clone, prog->aux->subprog_start + i, rewritten); + if (env) + env->prog = clone; /* Walk new program and skip insns we just inserted. */ insn = clone->insnsi + i + insn_delta; @@ -1575,6 +1573,15 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog) clone->blinded = 1; return clone; } + +bool bpf_insn_is_indirect_target(const struct bpf_verifier_env *env, const struct bpf_prog *prog, + int insn_idx) +{ + if (!env) + return false; + insn_idx += prog->aux->subprog_start; + return env->insn_aux_data[insn_idx].indirect_target; +} #endif /* CONFIG_BPF_JIT */ /* Base function for offset calculation. Needs to go into .text section, @@ -2533,18 +2540,55 @@ static bool bpf_prog_select_interpreter(struct bpf_prog *fp) return select_interpreter; } -/** - * bpf_prog_select_runtime - select exec runtime for BPF program - * @fp: bpf_prog populated with BPF program - * @err: pointer to error variable - * - * Try to JIT eBPF program, if JIT is not available, use interpreter. - * The BPF program will be executed via bpf_prog_run() function. - * - * Return: the &fp argument along with &err set to 0 for success or - * a negative errno code on failure - */ -struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err) +static struct bpf_prog *bpf_prog_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) +{ +#ifdef CONFIG_BPF_JIT + struct bpf_prog *orig_prog; + struct bpf_insn_aux_data *orig_insn_aux; + + if (!bpf_prog_need_blind(prog)) + return bpf_int_jit_compile(env, prog); + + if (env) { + /* + * If env is not NULL, we are called from the end of bpf_check(), at this + * point, only insn_aux_data is used after failure, so it should be restored + * on failure. + */ + orig_insn_aux = bpf_dup_insn_aux_data(env); + if (!orig_insn_aux) + return prog; + } + + orig_prog = prog; + prog = bpf_jit_blind_constants(env, prog); + /* + * If blinding was requested and we failed during blinding, we must fall + * back to the interpreter. + */ + if (IS_ERR(prog)) + goto out_restore; + + prog = bpf_int_jit_compile(env, prog); + if (prog->jited) { + bpf_jit_prog_release_other(prog, orig_prog); + if (env) + vfree(orig_insn_aux); + return prog; + } + + bpf_jit_prog_release_other(orig_prog, prog); + +out_restore: + prog = orig_prog; + if (env) + bpf_restore_insn_aux_data(env, orig_insn_aux); +#endif + return prog; +} + +struct bpf_prog *__bpf_prog_select_runtime(struct bpf_verifier_env *env, struct bpf_prog *fp, + int *err) { /* In case of BPF to BPF calls, verifier did all the prep * work with regards to JITing, etc. @@ -2572,7 +2616,7 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err) if (*err) return fp; - fp = bpf_int_jit_compile(fp); + fp = bpf_prog_jit_compile(env, fp); bpf_prog_jit_attempt_done(fp); if (!fp->jited && jit_needed) { *err = -ENOTSUPP; @@ -2598,6 +2642,22 @@ finalize: return fp; } + +/** + * bpf_prog_select_runtime - select exec runtime for BPF program + * @fp: bpf_prog populated with BPF program + * @err: pointer to error variable + * + * Try to JIT eBPF program, if JIT is not available, use interpreter. + * The BPF program will be executed via bpf_prog_run() function. + * + * Return: the &fp argument along with &err set to 0 for success or + * a negative errno code on failure + */ +struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err) +{ + return __bpf_prog_select_runtime(NULL, fp, err); +} EXPORT_SYMBOL_GPL(bpf_prog_select_runtime); static unsigned int __bpf_prog_ret1(const void *ctx, @@ -3085,7 +3145,7 @@ const struct bpf_func_proto bpf_tail_call_proto = { * It is encouraged to implement bpf_int_jit_compile() instead, so that * eBPF and implicitly also cBPF can get JITed! */ -struct bpf_prog * __weak bpf_int_jit_compile(struct bpf_prog *prog) +struct bpf_prog * __weak bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog) { return prog; } @@ -3290,6 +3350,11 @@ __weak u64 bpf_arena_get_kern_vm_start(struct bpf_arena *arena) { return 0; } +__weak bool bpf_arena_handle_page_fault(unsigned long addr, bool is_write, + unsigned long fault_ip) +{ + return false; +} #ifdef CONFIG_BPF_SYSCALL static int __init bpf_global_ma_init(void) diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c index 67c9b28767e1..fba9e8c00878 100644 --- a/kernel/bpf/fixups.c +++ b/kernel/bpf/fixups.c @@ -183,6 +183,18 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env, data[i].seen = old_seen; data[i].zext_dst = insn_has_def32(insn + i); } + + /* + * The indirect_target flag of the original instruction was moved to the last of the + * new instructions by the above memmove and memset, but the indirect jump target is + * actually the first instruction, so move it back. This also matches with the behavior + * of bpf_insn_array_adjust(), which preserves xlated_off to point to the first new + * instruction. + */ + if (data[off + cnt - 1].indirect_target) { + data[off].indirect_target = 1; + data[off + cnt - 1].indirect_target = 0; + } } static void adjust_subprog_starts(struct bpf_verifier_env *env, u32 off, u32 len) @@ -232,8 +244,8 @@ static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len) } } -static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off, - const struct bpf_insn *patch, u32 len) +struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off, + const struct bpf_insn *patch, u32 len) { struct bpf_prog *new_prog; struct bpf_insn_aux_data *new_data = NULL; @@ -973,7 +985,47 @@ patch_insn_buf: return 0; } -int bpf_jit_subprogs(struct bpf_verifier_env *env) +static u32 *bpf_dup_subprog_starts(struct bpf_verifier_env *env) +{ + u32 *starts = NULL; + + starts = kvmalloc_objs(u32, env->subprog_cnt, GFP_KERNEL_ACCOUNT); + if (starts) { + for (int i = 0; i < env->subprog_cnt; i++) + starts[i] = env->subprog_info[i].start; + } + return starts; +} + +static void bpf_restore_subprog_starts(struct bpf_verifier_env *env, u32 *orig_starts) +{ + for (int i = 0; i < env->subprog_cnt; i++) + env->subprog_info[i].start = orig_starts[i]; + /* restore the start of fake 'exit' subprog as well */ + env->subprog_info[env->subprog_cnt].start = env->prog->len; +} + +struct bpf_insn_aux_data *bpf_dup_insn_aux_data(struct bpf_verifier_env *env) +{ + size_t size; + void *new_aux; + + size = array_size(sizeof(struct bpf_insn_aux_data), env->prog->len); + new_aux = __vmalloc(size, GFP_KERNEL_ACCOUNT); + if (new_aux) + memcpy(new_aux, env->insn_aux_data, size); + return new_aux; +} + +void bpf_restore_insn_aux_data(struct bpf_verifier_env *env, + struct bpf_insn_aux_data *orig_insn_aux) +{ + /* the expanded elements are zero-filled, so no special handling is required */ + vfree(env->insn_aux_data); + env->insn_aux_data = orig_insn_aux; +} + +static int jit_subprogs(struct bpf_verifier_env *env) { struct bpf_prog *prog = env->prog, **func, *tmp; int i, j, subprog_start, subprog_end = 0, len, subprog; @@ -981,10 +1033,6 @@ int bpf_jit_subprogs(struct bpf_verifier_env *env) struct bpf_insn *insn; void *old_bpf_func; int err, num_exentries; - int old_len, subprog_start_adjustment = 0; - - if (env->subprog_cnt <= 1) - return 0; for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) { if (!bpf_pseudo_func(insn) && !bpf_pseudo_call(insn)) @@ -1053,10 +1101,11 @@ int bpf_jit_subprogs(struct bpf_verifier_env *env) goto out_free; func[i]->is_func = 1; func[i]->sleepable = prog->sleepable; + func[i]->blinded = prog->blinded; func[i]->aux->func_idx = i; /* Below members will be freed only at prog->aux */ func[i]->aux->btf = prog->aux->btf; - func[i]->aux->subprog_start = subprog_start + subprog_start_adjustment; + func[i]->aux->subprog_start = subprog_start; func[i]->aux->func_info = prog->aux->func_info; func[i]->aux->func_info_cnt = prog->aux->func_info_cnt; func[i]->aux->poke_tab = prog->aux->poke_tab; @@ -1110,17 +1159,10 @@ int bpf_jit_subprogs(struct bpf_verifier_env *env) func[i]->aux->exception_cb = env->subprog_info[i].is_exception_cb; func[i]->aux->changes_pkt_data = env->subprog_info[i].changes_pkt_data; func[i]->aux->might_sleep = env->subprog_info[i].might_sleep; + func[i]->aux->token = prog->aux->token; if (!i) func[i]->aux->exception_boundary = env->seen_exception; - - /* - * To properly pass the absolute subprog start to jit - * all instruction adjustments should be accumulated - */ - old_len = func[i]->len; - func[i] = bpf_int_jit_compile(func[i]); - subprog_start_adjustment += func[i]->len - old_len; - + func[i] = bpf_int_jit_compile(env, func[i]); if (!func[i]->jited) { err = -ENOTSUPP; goto out_free; @@ -1164,7 +1206,7 @@ int bpf_jit_subprogs(struct bpf_verifier_env *env) } for (i = 0; i < env->subprog_cnt; i++) { old_bpf_func = func[i]->bpf_func; - tmp = bpf_int_jit_compile(func[i]); + tmp = bpf_int_jit_compile(env, func[i]); if (tmp != func[i] || func[i]->bpf_func != old_bpf_func) { verbose(env, "JIT doesn't support bpf-to-bpf calls\n"); err = -ENOTSUPP; @@ -1246,16 +1288,87 @@ out_free: } kfree(func); out_undo_insn: + bpf_prog_jit_attempt_done(prog); + return err; +} + +int bpf_jit_subprogs(struct bpf_verifier_env *env) +{ + int err, i; + bool blinded = false; + struct bpf_insn *insn; + struct bpf_prog *prog, *orig_prog; + struct bpf_insn_aux_data *orig_insn_aux; + u32 *orig_subprog_starts; + + if (env->subprog_cnt <= 1) + return 0; + + prog = orig_prog = env->prog; + if (bpf_prog_need_blind(prog)) { + orig_insn_aux = bpf_dup_insn_aux_data(env); + if (!orig_insn_aux) { + err = -ENOMEM; + goto out_cleanup; + } + orig_subprog_starts = bpf_dup_subprog_starts(env); + if (!orig_subprog_starts) { + vfree(orig_insn_aux); + err = -ENOMEM; + goto out_cleanup; + } + prog = bpf_jit_blind_constants(env, prog); + if (IS_ERR(prog)) { + err = -ENOMEM; + prog = orig_prog; + goto out_restore; + } + blinded = true; + } + + err = jit_subprogs(env); + if (err) + goto out_jit_err; + + if (blinded) { + bpf_jit_prog_release_other(prog, orig_prog); + kvfree(orig_subprog_starts); + vfree(orig_insn_aux); + } + + return 0; + +out_jit_err: + if (blinded) { + bpf_jit_prog_release_other(orig_prog, prog); + /* roll back to the clean original prog */ + prog = env->prog = orig_prog; + goto out_restore; + } else { + if (err != -EFAULT) { + /* + * We will fall back to interpreter mode when err is not -EFAULT, before + * that, insn->off and insn->imm should be restored to their original + * values since they were modified by jit_subprogs. + */ + for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) { + if (!bpf_pseudo_call(insn)) + continue; + insn->off = 0; + insn->imm = env->insn_aux_data[i].call_imm; + } + } + goto out_cleanup; + } + +out_restore: + bpf_restore_subprog_starts(env, orig_subprog_starts); + bpf_restore_insn_aux_data(env, orig_insn_aux); + kvfree(orig_subprog_starts); +out_cleanup: /* cleanup main prog to be interpreted */ prog->jit_requested = 0; prog->blinding_requested = 0; - for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) { - if (!bpf_pseudo_call(insn)) - continue; - insn->off = 0; - insn->imm = env->insn_aux_data[i].call_imm; - } - bpf_prog_jit_attempt_done(prog); return err; } diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index bb95e287b0dc..2bb60200c266 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -845,7 +845,13 @@ int bpf_bprintf_prepare(const char *fmt, u32 fmt_size, const u64 *raw_args, data->buf = buffers->buf; for (i = 0; i < fmt_size; i++) { - if ((!isprint(fmt[i]) && !isspace(fmt[i])) || !isascii(fmt[i])) { + unsigned char c = fmt[i]; + + /* + * Permit bytes >= 0x80 in plain text so UTF-8 literals can pass + * through unchanged, while still rejecting ASCII control bytes. + */ + if (isascii(c) && !isprint(c) && !isspace(c)) { err = -EINVAL; goto out; } @@ -867,6 +873,15 @@ int bpf_bprintf_prepare(const char *fmt, u32 fmt_size, const u64 *raw_args, * always access fmt[i + 1], in the worst case it will be a 0 */ i++; + c = fmt[i]; + /* + * The format parser below only understands ASCII conversion + * specifiers and modifiers, so reject non-ASCII after '%'. + */ + if (!isascii(c)) { + err = -EINVAL; + goto out; + } /* skip optional "[0 +-][num]" width formatting field */ while (fmt[i] == '0' || fmt[i] == '+' || fmt[i] == '-' || diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c index 1fb4c511db5a..58197d73b120 100644 --- a/kernel/bpf/liveness.c +++ b/kernel/bpf/liveness.c @@ -574,7 +574,7 @@ static int print_instances(struct bpf_verifier_env *env) * * precise {frame=N, off=V} -- known absolute frame index and byte offset * | - * offset-imprecise {frame=N, off=OFF_IMPRECISE} + * offset-imprecise {frame=N, cnt=0} * | -- known frame identity, unknown offset * fully-imprecise {frame=ARG_IMPRECISE, mask=bitmask} * -- unknown frame identity; .mask is a @@ -607,8 +607,6 @@ enum arg_track_state { ARG_IMPRECISE = -3, /* lost identity; .mask is arg bitmask */ }; -#define OFF_IMPRECISE S16_MIN /* arg identity known but offset unknown */ - /* Track callee stack slots fp-8 through fp-512 (64 slots of 8 bytes each) */ #define MAX_ARG_SPILL_SLOTS 64 @@ -622,28 +620,6 @@ static bool arg_is_fp(const struct arg_track *at) return at->frame >= 0 || at->frame == ARG_IMPRECISE; } -/* - * Clear all tracked callee stack slots overlapping the byte range - * [off, off+sz-1] where off is a negative FP-relative offset. - */ -static void clear_overlapping_stack_slots(struct arg_track *at_stack, s16 off, u32 sz) -{ - struct arg_track none = { .frame = ARG_NONE }; - - if (off == OFF_IMPRECISE) { - for (int i = 0; i < MAX_ARG_SPILL_SLOTS; i++) - at_stack[i] = none; - return; - } - for (int i = 0; i < MAX_ARG_SPILL_SLOTS; i++) { - int slot_start = -((i + 1) * 8); - int slot_end = slot_start + 8; - - if (slot_start < off + (int)sz && slot_end > off) - at_stack[i] = none; - } -} - static void verbose_arg_track(struct bpf_verifier_env *env, struct arg_track *at) { int i; @@ -863,16 +839,13 @@ static void arg_track_alu64(struct arg_track *dst, const struct arg_track *src) *dst = arg_join_imprecise(*dst, *src); } -static s16 arg_add(s16 off, s64 delta) +static bool arg_add(s16 off, s64 delta, s16 *out) { - s64 res; + s16 d = delta; - if (off == OFF_IMPRECISE) - return OFF_IMPRECISE; - res = (s64)off + delta; - if (res < S16_MIN + 1 || res > S16_MAX) - return OFF_IMPRECISE; - return res; + if (d != delta) + return true; + return check_add_overflow(off, d, out); } static void arg_padd(struct arg_track *at, s64 delta) @@ -882,9 +855,9 @@ static void arg_padd(struct arg_track *at, s64 delta) if (at->off_cnt == 0) return; for (i = 0; i < at->off_cnt; i++) { - s16 new_off = arg_add(at->off[i], delta); + s16 new_off; - if (new_off == OFF_IMPRECISE) { + if (arg_add(at->off[i], delta, &new_off)) { at->off_cnt = 0; return; } @@ -899,8 +872,6 @@ static void arg_padd(struct arg_track *at, s64 delta) */ static int fp_off_to_slot(s16 off) { - if (off == OFF_IMPRECISE) - return -1; if (off >= 0 || off < -(int)(MAX_ARG_SPILL_SLOTS * 8)) return -1; if (off % 8) @@ -930,9 +901,11 @@ static struct arg_track fill_from_stack(struct bpf_insn *insn, return imp; for (i = 0; i < cnt; i++) { - s16 fp_off = arg_add(at_out[reg].off[i], insn->off); - int slot = fp_off_to_slot(fp_off); + s16 fp_off, slot; + if (arg_add(at_out[reg].off[i], insn->off, &fp_off)) + return imp; + slot = fp_off_to_slot(fp_off); if (slot < 0) return imp; result = __arg_track_join(result, at_stack_out[slot]); @@ -968,9 +941,12 @@ static void spill_to_stack(struct bpf_insn *insn, struct arg_track *at_out, return; } for (i = 0; i < cnt; i++) { - s16 fp_off = arg_add(at_out[reg].off[i], insn->off); - int slot = fp_off_to_slot(fp_off); + s16 fp_off; + int slot; + if (arg_add(at_out[reg].off[i], insn->off, &fp_off)) + continue; + slot = fp_off_to_slot(fp_off); if (slot < 0) continue; if (cnt == 1) @@ -980,6 +956,32 @@ static void spill_to_stack(struct bpf_insn *insn, struct arg_track *at_out, } } +/* + * Clear all tracked callee stack slots overlapping the byte range + * [off, off+sz-1] where off is a negative FP-relative offset. + */ +static void clear_overlapping_stack_slots(struct arg_track *at_stack, s16 off, u32 sz, int cnt) +{ + struct arg_track none = { .frame = ARG_NONE }; + + if (cnt == 0) { + for (int i = 0; i < MAX_ARG_SPILL_SLOTS; i++) + at_stack[i] = __arg_track_join(at_stack[i], none); + return; + } + for (int i = 0; i < MAX_ARG_SPILL_SLOTS; i++) { + int slot_start = -((i + 1) * 8); + int slot_end = slot_start + 8; + + if (slot_start < off + (int)sz && slot_end > off) { + if (cnt == 1) + at_stack[i] = none; + else + at_stack[i] = __arg_track_join(at_stack[i], none); + } + } +} + /* * Clear stack slots overlapping all possible FP offsets in @reg. */ @@ -990,18 +992,22 @@ static void clear_stack_for_all_offs(struct bpf_insn *insn, int cnt, i; if (reg == BPF_REG_FP) { - clear_overlapping_stack_slots(at_stack_out, insn->off, sz); + clear_overlapping_stack_slots(at_stack_out, insn->off, sz, 1); return; } cnt = at_out[reg].off_cnt; if (cnt == 0) { - clear_overlapping_stack_slots(at_stack_out, OFF_IMPRECISE, sz); + clear_overlapping_stack_slots(at_stack_out, 0, sz, cnt); return; } for (i = 0; i < cnt; i++) { - s16 fp_off = arg_add(at_out[reg].off[i], insn->off); + s16 fp_off; - clear_overlapping_stack_slots(at_stack_out, fp_off, sz); + if (arg_add(at_out[reg].off[i], insn->off, &fp_off)) { + clear_overlapping_stack_slots(at_stack_out, 0, sz, 0); + break; + } + clear_overlapping_stack_slots(at_stack_out, fp_off, sz, cnt); } } @@ -1042,6 +1048,12 @@ static void arg_track_log(struct bpf_verifier_env *env, struct bpf_insn *insn, i verbose(env, "\n"); } +static bool can_be_local_fp(int depth, int regno, struct arg_track *at) +{ + return regno == BPF_REG_FP || at->frame == depth || + (at->frame == ARG_IMPRECISE && (at->mask & BIT(depth))); +} + /* * Pure dataflow transfer function for arg_track state. * Updates at_out[] based on how the instruction modifies registers. @@ -1111,8 +1123,7 @@ static void arg_track_xfer(struct bpf_verifier_env *env, struct bpf_insn *insn, at_out[r] = none; } else if (class == BPF_LDX) { u32 sz = bpf_size_to_bytes(BPF_SIZE(insn->code)); - bool src_is_local_fp = insn->src_reg == BPF_REG_FP || src->frame == depth || - (src->frame == ARG_IMPRECISE && (src->mask & BIT(depth))); + bool src_is_local_fp = can_be_local_fp(depth, insn->src_reg, src); /* * Reload from callee stack: if src is current-frame FP-derived @@ -1147,7 +1158,7 @@ static void arg_track_xfer(struct bpf_verifier_env *env, struct bpf_insn *insn, bool dst_is_local_fp; /* Track spills to current-frame FP-derived callee stack */ - dst_is_local_fp = insn->dst_reg == BPF_REG_FP || dst->frame == depth; + dst_is_local_fp = can_be_local_fp(depth, insn->dst_reg, dst); if (dst_is_local_fp && BPF_MODE(insn->code) == BPF_MEM) spill_to_stack(insn, at_out, insn->dst_reg, at_stack_out, src, sz); @@ -1166,7 +1177,7 @@ static void arg_track_xfer(struct bpf_verifier_env *env, struct bpf_insn *insn, } } else if (class == BPF_ST && BPF_MODE(insn->code) == BPF_MEM) { u32 sz = bpf_size_to_bytes(BPF_SIZE(insn->code)); - bool dst_is_local_fp = insn->dst_reg == BPF_REG_FP || dst->frame == depth; + bool dst_is_local_fp = can_be_local_fp(depth, insn->dst_reg, dst); /* BPF_ST to FP-derived dst: clear overlapping stack slots */ if (dst_is_local_fp) @@ -1316,8 +1327,7 @@ static int record_load_store_access(struct bpf_verifier_env *env, resolved.off_cnt = ptr->off_cnt; resolved.frame = ptr->frame; for (oi = 0; oi < ptr->off_cnt; oi++) { - resolved.off[oi] = arg_add(ptr->off[oi], insn->off); - if (resolved.off[oi] == OFF_IMPRECISE) { + if (arg_add(ptr->off[oi], insn->off, &resolved.off[oi])) { resolved.off_cnt = 0; break; } @@ -1904,26 +1914,15 @@ int bpf_compute_subprog_arg_access(struct bpf_verifier_env *env) return -ENOMEM; } - instance = call_instance(env, NULL, 0, 0); - if (IS_ERR(instance)) { - err = PTR_ERR(instance); - goto out; - } - err = analyze_subprog(env, NULL, info, instance, callsites); - if (err) - goto out; - /* - * Subprogs and callbacks that don't receive FP-derived arguments - * cannot access ancestor stack frames, so they were skipped during - * the recursive walk above. Async callbacks (timer, workqueue) are - * also not reachable from the main program's call graph. Analyze - * all unvisited subprogs as independent roots at depth 0. + * Analyze every subprog in reverse topological order (callers + * before callees) so that each subprog is analyzed before its + * callees, allowing the recursive walk inside analyze_subprog() + * to naturally reach callees that receive FP-derived args. * - * Use reverse topological order (callers before callees) so that - * each subprog is analyzed before its callees, allowing the - * recursive walk inside analyze_subprog() to naturally - * reach nested callees that also lack FP-derived args. + * Subprogs and callbacks that don't receive FP-derived arguments + * cannot access ancestor stack frames are analyzed independently. + * Async callbacks (timer, workqueue) are handled the same way. */ for (k = env->subprog_cnt - 1; k >= 0; k--) { int sub = env->subprog_topo_order[k]; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b73b25c63073..a3c0214ca934 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3083,10 +3083,6 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) if (err < 0) goto free_used_maps; - prog = bpf_prog_select_runtime(prog, &err); - if (err < 0) - goto free_used_maps; - err = bpf_prog_mark_insn_arrays_ready(prog); if (err < 0) goto free_used_maps; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9e4980128151..69d75515ed3f 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3497,6 +3497,11 @@ static int insn_stack_access_flags(int frameno, int spi) return INSN_F_STACK_ACCESS | (spi << INSN_F_SPI_SHIFT) | frameno; } +static void mark_indirect_target(struct bpf_verifier_env *env, int idx) +{ + env->insn_aux_data[idx].indirect_target = true; +} + #define LR_FRAMENO_BITS 3 #define LR_SPI_BITS 6 #define LR_ENTRY_BITS (LR_SPI_BITS + LR_FRAMENO_BITS + 1) @@ -4544,6 +4549,9 @@ static int map_kptr_match_type(struct bpf_verifier_env *env, int perm_flags; const char *reg_name = ""; + if (base_type(reg->type) != PTR_TO_BTF_ID) + goto bad_type; + if (btf_is_kernel(reg->btf)) { perm_flags = PTR_MAYBE_NULL | PTR_TRUSTED | MEM_RCU; @@ -4556,7 +4564,7 @@ static int map_kptr_match_type(struct bpf_verifier_env *env, perm_flags |= MEM_PERCPU; } - if (base_type(reg->type) != PTR_TO_BTF_ID || (type_flag(reg->type) & ~perm_flags)) + if (type_flag(reg->type) & ~perm_flags) goto bad_type; /* We need to verify reg->type and reg->btf, before accessing reg->btf */ @@ -17545,12 +17553,14 @@ static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *in } for (i = 0; i < n - 1; i++) { + mark_indirect_target(env, env->gotox_tmp_buf->items[i]); other_branch = push_stack(env, env->gotox_tmp_buf->items[i], env->insn_idx, env->cur_state->speculative); if (IS_ERR(other_branch)) return PTR_ERR(other_branch); } env->insn_idx = env->gotox_tmp_buf->items[n-1]; + mark_indirect_target(env, env->insn_idx); return INSN_IDX_UPDATED; } @@ -20155,6 +20165,14 @@ skip_full_check: adjust_btf_func(env); + /* extension progs temporarily inherit the attach_type of their targets + for verification purposes, so set it back to zero before returning + */ + if (env->prog->type == BPF_PROG_TYPE_EXT) + env->prog->expected_attach_type = 0; + + env->prog = __bpf_prog_select_runtime(env, env->prog, &ret); + err_release_maps: if (ret) release_insn_arrays(env); @@ -20166,12 +20184,6 @@ err_release_maps: if (!env->prog->aux->used_btfs) release_btfs(env); - /* extension progs temporarily inherit the attach_type of their targets - for verification purposes, so set it back to zero before returning - */ - if (env->prog->type == BPF_PROG_TYPE_EXT) - env->prog->expected_attach_type = 0; - *prog = env->prog; module_put(env->attach_btf_mod); diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index e51ce4cd3739..6152add0c5eb 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -264,10 +264,12 @@ static void cgroup_finalize_control(struct cgroup *cgrp, int ret); static void css_task_iter_skip(struct css_task_iter *it, struct task_struct *task); static int cgroup_destroy_locked(struct cgroup *cgrp); +static void cgroup_finish_destroy(struct cgroup *cgrp); +static void kill_css_sync(struct cgroup_subsys_state *css); +static void kill_css_finish(struct cgroup_subsys_state *css); static struct cgroup_subsys_state *css_create(struct cgroup *cgrp, struct cgroup_subsys *ss); static void css_release(struct percpu_ref *ref); -static void kill_css(struct cgroup_subsys_state *css); static int cgroup_addrm_files(struct cgroup_subsys_state *css, struct cgroup *cgrp, struct cftype cfts[], bool is_add); @@ -797,6 +799,16 @@ static void cgroup_update_populated(struct cgroup *cgrp, bool populated) if (was_populated == cgroup_is_populated(cgrp)) break; + /* + * Subtree just emptied below an offlined cgrp. Fire deferred + * destroy. The transition is one-shot. + */ + if (was_populated && !css_is_online(&cgrp->self)) { + cgroup_get(cgrp); + WARN_ON_ONCE(!queue_work(cgroup_offline_wq, + &cgrp->finish_destroy_work)); + } + cgroup1_check_for_release(cgrp); TRACE_CGROUP_PATH(notify_populated, cgrp, cgroup_is_populated(cgrp)); @@ -2039,6 +2051,16 @@ static int cgroup_reconfigure(struct fs_context *fc) return 0; } +static void cgroup_finish_destroy_work_fn(struct work_struct *work) +{ + struct cgroup *cgrp = container_of(work, struct cgroup, finish_destroy_work); + + cgroup_lock(); + cgroup_finish_destroy(cgrp); + cgroup_unlock(); + cgroup_put(cgrp); +} + static void init_cgroup_housekeeping(struct cgroup *cgrp) { struct cgroup_subsys *ss; @@ -2065,7 +2087,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) #endif init_waitqueue_head(&cgrp->offline_waitq); - init_waitqueue_head(&cgrp->dying_populated_waitq); + INIT_WORK(&cgrp->finish_destroy_work, cgroup_finish_destroy_work_fn); INIT_WORK(&cgrp->release_agent_work, cgroup1_release_agent); } @@ -3375,7 +3397,8 @@ static void cgroup_apply_control_disable(struct cgroup *cgrp) if (css->parent && !(cgroup_ss_mask(dsct) & (1 << ss->id))) { - kill_css(css); + kill_css_sync(css); + kill_css_finish(css); } else if (!css_visible(css)) { css_clear_dir(css); if (ss->css_reset) @@ -3934,33 +3957,41 @@ static int cgroup_cpu_pressure_show(struct seq_file *seq, void *v) static ssize_t pressure_write(struct kernfs_open_file *of, char *buf, size_t nbytes, enum psi_res res) { - struct cgroup_file_ctx *ctx = of->priv; + struct cgroup_file_ctx *ctx; struct psi_trigger *new; struct cgroup *cgrp; struct psi_group *psi; + ssize_t ret = 0; cgrp = cgroup_kn_lock_live(of->kn, false); if (!cgrp) return -ENODEV; - cgroup_get(cgrp); - cgroup_kn_unlock(of->kn); + ctx = of->priv; + if (!ctx) { + ret = -ENODEV; + goto out_unlock; + } /* Allow only one trigger per file descriptor */ if (ctx->psi.trigger) { - cgroup_put(cgrp); - return -EBUSY; + ret = -EBUSY; + goto out_unlock; } psi = cgroup_psi(cgrp); new = psi_trigger_create(psi, buf, res, of->file, of); if (IS_ERR(new)) { - cgroup_put(cgrp); - return PTR_ERR(new); + ret = PTR_ERR(new); + goto out_unlock; } smp_store_release(&ctx->psi.trigger, new); - cgroup_put(cgrp); + +out_unlock: + cgroup_kn_unlock(of->kn); + if (ret) + return ret; return nbytes; } @@ -5508,7 +5539,7 @@ static struct cftype cgroup_psi_files[] = { * css destruction is four-stage process. * * 1. Destruction starts. Killing of the percpu_ref is initiated. - * Implemented in kill_css(). + * Implemented in kill_css_finish(). * * 2. When the percpu_ref is confirmed to be visible as killed on all CPUs * and thus css_tryget_online() is guaranteed to fail, the css can be @@ -5718,16 +5749,6 @@ static void offline_css(struct cgroup_subsys_state *css) RCU_INIT_POINTER(css->cgroup->subsys[ss->id], NULL); wake_up_all(&css->cgroup->offline_waitq); - - css->cgroup->nr_dying_subsys[ss->id]++; - /* - * Parent css and cgroup cannot be freed until after the freeing - * of child css, see css_free_rwork_fn(). - */ - while ((css = css->parent)) { - css->nr_descendants--; - css->cgroup->nr_dying_subsys[ss->id]++; - } } /** @@ -5997,12 +6018,13 @@ out_unlock: /* * This is called when the refcnt of a css is confirmed to be killed. * css_tryget_online() is now guaranteed to fail. Tell the subsystem to - * initiate destruction and put the css ref from kill_css(). + * initiate destruction and put the css ref from kill_css_finish(). */ static void css_killed_work_fn(struct work_struct *work) { - struct cgroup_subsys_state *css = - container_of(work, struct cgroup_subsys_state, destroy_work); + struct cgroup_subsys_state *css; + + css = container_of(to_rcu_work(work), struct cgroup_subsys_state, destroy_rwork); cgroup_lock(); @@ -6023,22 +6045,21 @@ static void css_killed_ref_fn(struct percpu_ref *ref) container_of(ref, struct cgroup_subsys_state, refcnt); if (atomic_dec_and_test(&css->online_cnt)) { - INIT_WORK(&css->destroy_work, css_killed_work_fn); - queue_work(cgroup_offline_wq, &css->destroy_work); + INIT_RCU_WORK(&css->destroy_rwork, css_killed_work_fn); + queue_rcu_work(cgroup_offline_wq, &css->destroy_rwork); } } /** - * kill_css - destroy a css - * @css: css to destroy + * kill_css_sync - synchronous half of css teardown + * @css: css being killed * - * This function initiates destruction of @css by removing cgroup interface - * files and putting its base reference. ->css_offline() will be invoked - * asynchronously once css_tryget_online() is guaranteed to fail and when - * the reference count reaches zero, @css will be released. + * See cgroup_destroy_locked(). */ -static void kill_css(struct cgroup_subsys_state *css) +static void kill_css_sync(struct cgroup_subsys_state *css) { + struct cgroup_subsys *ss = css->ss; + lockdep_assert_held(&cgroup_mutex); if (css->flags & CSS_DYING) @@ -6058,64 +6079,100 @@ static void kill_css(struct cgroup_subsys_state *css) */ css_clear_dir(css); + css->cgroup->nr_dying_subsys[ss->id]++; /* - * Killing would put the base ref, but we need to keep it alive - * until after ->css_offline(). + * Parent css and cgroup cannot be freed until after the freeing + * of child css, see css_free_rwork_fn(). + */ + while ((css = css->parent)) { + css->nr_descendants--; + css->cgroup->nr_dying_subsys[ss->id]++; + } +} + +/** + * kill_css_finish - deferred half of css teardown + * @css: css being killed + * + * See cgroup_destroy_locked(). + */ +static void kill_css_finish(struct cgroup_subsys_state *css) +{ + lockdep_assert_held(&cgroup_mutex); + + /* + * Skip on re-entry: cgroup_apply_control_disable() may have killed @css + * earlier. cgroup_destroy_locked() can still walk it because + * offline_css() (which NULLs cgrp->subsys[ssid]) runs async. + */ + if (percpu_ref_is_dying(&css->refcnt)) + return; + + /* + * Killing would put the base ref, but we need to keep it alive until + * after ->css_offline(). */ css_get(css); /* - * cgroup core guarantees that, by the time ->css_offline() is - * invoked, no new css reference will be given out via - * css_tryget_online(). We can't simply call percpu_ref_kill() and - * proceed to offlining css's because percpu_ref_kill() doesn't - * guarantee that the ref is seen as killed on all CPUs on return. + * cgroup core guarantees that, by the time ->css_offline() is invoked, + * no new css reference will be given out via css_tryget_online(). We + * can't simply call percpu_ref_kill() and proceed to offlining css's + * because percpu_ref_kill() doesn't guarantee that the ref is seen as + * killed on all CPUs on return. * - * Use percpu_ref_kill_and_confirm() to get notifications as each - * css is confirmed to be seen as killed on all CPUs. + * Use percpu_ref_kill_and_confirm() to get notifications as each css is + * confirmed to be seen as killed on all CPUs. */ percpu_ref_kill_and_confirm(&css->refcnt, css_killed_ref_fn); } /** - * cgroup_destroy_locked - the first stage of cgroup destruction + * cgroup_destroy_locked - destroy @cgrp (called on rmdir) * @cgrp: cgroup to be destroyed * - * css's make use of percpu refcnts whose killing latency shouldn't be - * exposed to userland and are RCU protected. Also, cgroup core needs to - * guarantee that css_tryget_online() won't succeed by the time - * ->css_offline() is invoked. To satisfy all the requirements, - * destruction is implemented in the following two steps. + * Tear down @cgrp on behalf of rmdir. Constraints: * - * s1. Verify @cgrp can be destroyed and mark it dying. Remove all - * userland visible parts and start killing the percpu refcnts of - * css's. Set up so that the next stage will be kicked off once all - * the percpu refcnts are confirmed to be killed. + * - Userspace: rmdir must succeed when cgroup.procs and friends are empty. * - * s2. Invoke ->css_offline(), mark the cgroup dead and proceed with the - * rest of destruction. Once all cgroup references are gone, the - * cgroup is RCU-freed. + * - Kernel: subsystem ->css_offline() must not run while any task in @cgrp's + * subtree is still doing kernel work. A task hidden from cgroup.procs (past + * exit_signals() with signal->live cleared) can still schedule, allocate, and + * consume resources until its final context switch. Dying descendants in the + * subtree can host such tasks too. * - * This function implements s1. After this step, @cgrp is gone as far as - * the userland is concerned and a new cgroup with the same name may be - * created. As cgroup doesn't care about the names internally, this - * doesn't cause any problem. + * - Kernel: css_tryget_online() must fail by the time ->css_offline() runs. + * + * The destruction runs in three parts: + * + * - This function: synchronous user-visible state teardown plus kill_css_sync() + * on each subsystem css. + * + * - cgroup_finish_destroy(): kicks the percpu_ref kill via kill_css_finish() on + * each subsystem css. Fires once @cgrp's subtree is fully drained, either + * inline here or from cgroup_update_populated(). + * + * - The percpu_ref kill chain: css_killed_ref_fn -> css_killed_work_fn -> + * ->css_offline() -> release/free. + * + * Return 0 on success, -EBUSY if a userspace-visible task or an online child + * remains. */ static int cgroup_destroy_locked(struct cgroup *cgrp) - __releases(&cgroup_mutex) __acquires(&cgroup_mutex) { struct cgroup *tcgrp, *parent = cgroup_parent(cgrp); struct cgroup_subsys_state *css; struct cgrp_cset_link *link; + struct css_task_iter it; + struct task_struct *task; int ssid, ret; lockdep_assert_held(&cgroup_mutex); - /* - * Only migration can raise populated from zero and we're already - * holding cgroup_mutex. - */ - if (cgroup_is_populated(cgrp)) + css_task_iter_start(&cgrp->self, 0, &it); + task = css_task_iter_next(&it); + css_task_iter_end(&it); + if (task) return -EBUSY; /* @@ -6139,9 +6196,8 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) link->cset->dead = true; spin_unlock_irq(&css_set_lock); - /* initiate massacre of all css's */ for_each_css(css, ssid, cgrp) - kill_css(css); + kill_css_sync(css); /* clear and remove @cgrp dir, @cgrp has an extra ref on its kn */ css_clear_dir(&cgrp->self); @@ -6172,79 +6228,27 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) /* put the base reference */ percpu_ref_kill(&cgrp->self.refcnt); + if (!cgroup_is_populated(cgrp)) + cgroup_finish_destroy(cgrp); + return 0; }; /** - * cgroup_drain_dying - wait for dying tasks to leave before rmdir - * @cgrp: the cgroup being removed + * cgroup_finish_destroy - deferred half of @cgrp destruction + * @cgrp: cgroup whose subtree just became empty * - * cgroup.procs and cgroup.threads use css_task_iter which filters out - * PF_EXITING tasks so that userspace doesn't see tasks that have already been - * reaped via waitpid(). However, cgroup_has_tasks() - which tests whether the - * cgroup has non-empty css_sets - is only updated when dying tasks pass through - * cgroup_task_dead() in finish_task_switch(). This creates a window where - * cgroup.procs reads empty but cgroup_has_tasks() is still true, making rmdir - * fail with -EBUSY from cgroup_destroy_locked() even though userspace sees no - * tasks. - * - * This function aligns cgroup_has_tasks() with what userspace can observe. If - * cgroup_has_tasks() but the task iterator sees nothing (all remaining tasks are - * PF_EXITING), we wait for cgroup_task_dead() to finish processing them. As the - * window between PF_EXITING and cgroup_task_dead() is short, the wait is brief. - * - * This function only concerns itself with this cgroup's own dying tasks. - * Whether the cgroup has children is cgroup_destroy_locked()'s problem. - * - * Each cgroup_task_dead() kicks the waitqueue via cset->cgrp_links, and we - * retry the full check from scratch. - * - * Must be called with cgroup_mutex held. + * See cgroup_destroy_locked() for the rationale. */ -static int cgroup_drain_dying(struct cgroup *cgrp) - __releases(&cgroup_mutex) __acquires(&cgroup_mutex) +static void cgroup_finish_destroy(struct cgroup *cgrp) { - struct css_task_iter it; - struct task_struct *task; - DEFINE_WAIT(wait); + struct cgroup_subsys_state *css; + int ssid; lockdep_assert_held(&cgroup_mutex); -retry: - if (!cgroup_has_tasks(cgrp)) - return 0; - /* Same iterator as cgroup.threads - if any task is visible, it's busy */ - css_task_iter_start(&cgrp->self, 0, &it); - task = css_task_iter_next(&it); - css_task_iter_end(&it); - - if (task) - return -EBUSY; - - /* - * All remaining tasks are PF_EXITING and will pass through - * cgroup_task_dead() shortly. Wait for a kick and retry. - * - * cgroup_has_tasks() can't transition from false to true while we're - * holding cgroup_mutex, but the true to false transition happens - * under css_set_lock (via cgroup_task_dead()). We must retest and - * prepare_to_wait() under css_set_lock. Otherwise, the transition - * can happen between our first test and prepare_to_wait(), and we - * sleep with no one to wake us. - */ - spin_lock_irq(&css_set_lock); - if (!cgroup_has_tasks(cgrp)) { - spin_unlock_irq(&css_set_lock); - return 0; - } - prepare_to_wait(&cgrp->dying_populated_waitq, &wait, - TASK_UNINTERRUPTIBLE); - spin_unlock_irq(&css_set_lock); - mutex_unlock(&cgroup_mutex); - schedule(); - finish_wait(&cgrp->dying_populated_waitq, &wait); - mutex_lock(&cgroup_mutex); - goto retry; + for_each_css(css, ssid, cgrp) + kill_css_finish(css); } int cgroup_rmdir(struct kernfs_node *kn) @@ -6256,12 +6260,9 @@ int cgroup_rmdir(struct kernfs_node *kn) if (!cgrp) return 0; - ret = cgroup_drain_dying(cgrp); - if (!ret) { - ret = cgroup_destroy_locked(cgrp); - if (!ret) - TRACE_CGROUP_PATH(rmdir, cgrp); - } + ret = cgroup_destroy_locked(cgrp); + if (!ret) + TRACE_CGROUP_PATH(rmdir, cgrp); cgroup_kn_unlock(kn); return ret; @@ -7021,7 +7022,6 @@ void cgroup_task_exit(struct task_struct *tsk) static void do_cgroup_task_dead(struct task_struct *tsk) { - struct cgrp_cset_link *link; struct css_set *cset; unsigned long flags; @@ -7035,11 +7035,6 @@ static void do_cgroup_task_dead(struct task_struct *tsk) if (thread_group_leader(tsk) && atomic_read(&tsk->signal->live)) list_add_tail(&tsk->cg_list, &cset->dying_tasks); - /* kick cgroup_drain_dying() waiters, see cgroup_rmdir() */ - list_for_each_entry(link, &cset->cgrp_links, cgrp_link) - if (waitqueue_active(&link->cgrp->dying_populated_waitq)) - wake_up(&link->cgrp->dying_populated_waitq); - if (dl_task(tsk)) dec_dl_tasks_cs(tsk); diff --git a/kernel/cgroup/cpuset-internal.h b/kernel/cgroup/cpuset-internal.h index fd7d19842ded..f7aaf01f7cd5 100644 --- a/kernel/cgroup/cpuset-internal.h +++ b/kernel/cgroup/cpuset-internal.h @@ -167,7 +167,13 @@ struct cpuset { */ int nr_deadline_tasks; int nr_migrate_dl_tasks; + /* DL bandwidth that needs destination reservation for this attach. */ u64 sum_migrate_dl_bw; + /* + * CPU used for temporary DL bandwidth allocation during attach; + * -1 if no DL bandwidth was allocated in the current attach. + */ + int dl_bw_cpu; /* Invalid partition error code, not lock protected */ enum prs_errcode prs_err; diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 1335e437098e..5c33ab20cc20 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -288,6 +288,7 @@ struct cpuset top_cpuset = { .flags = BIT(CS_CPU_EXCLUSIVE) | BIT(CS_MEM_EXCLUSIVE) | BIT(CS_SCHED_LOAD_BALANCE), .partition_root_state = PRS_ROOT, + .dl_bw_cpu = -1, }; /** @@ -579,6 +580,8 @@ static struct cpuset *dup_or_alloc_cpuset(struct cpuset *cs) if (!trial) return NULL; + trial->dl_bw_cpu = -1; + /* Setup cpumask pointer array */ cpumask_var_t *pmask[4] = { &trial->cpus_allowed, @@ -1715,7 +1718,8 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd, */ if (is_partition_valid(parent)) adding = cpumask_and(tmp->addmask, - xcpus, parent->effective_xcpus); + cs->effective_xcpus, + parent->effective_xcpus); if (old_prs > 0) new_prs = -old_prs; @@ -2980,6 +2984,7 @@ static void reset_migrate_dl_data(struct cpuset *cs) { cs->nr_migrate_dl_tasks = 0; cs->sum_migrate_dl_bw = 0; + cs->dl_bw_cpu = -1; } /* Called by cgroups to determine if a cpuset is usable; cpuset_mutex held */ @@ -2989,7 +2994,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) struct cpuset *cs, *oldcs; struct task_struct *task; bool setsched_check; - int ret; + int cpu, ret; /* used later by cpuset_attach() */ cpuset_attach_old_cs = task_cs(cgroup_taskset_first(tset, &css)); @@ -3034,37 +3039,42 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) } if (dl_task(task)) { + /* + * Count all migrating DL tasks for cpuset task accounting. + * Only tasks that need a root-domain bandwidth move + * contribute to sum_migrate_dl_bw. + */ cs->nr_migrate_dl_tasks++; - cs->sum_migrate_dl_bw += task->dl.dl_bw; + if (dl_task_needs_bw_move(task, cs->effective_cpus)) + cs->sum_migrate_dl_bw += task->dl.dl_bw; } } - if (!cs->nr_migrate_dl_tasks) + if (!cs->sum_migrate_dl_bw) goto out_success; - if (!cpumask_intersects(oldcs->effective_cpus, cs->effective_cpus)) { - int cpu = cpumask_any_and(cpu_active_mask, cs->effective_cpus); - - if (unlikely(cpu >= nr_cpu_ids)) { - reset_migrate_dl_data(cs); - ret = -EINVAL; - goto out_unlock; - } - - ret = dl_bw_alloc(cpu, cs->sum_migrate_dl_bw); - if (ret) { - reset_migrate_dl_data(cs); - goto out_unlock; - } + cpu = cpumask_any_and(cpu_active_mask, cs->effective_cpus); + if (unlikely(cpu >= nr_cpu_ids)) { + ret = -EINVAL; + goto out_unlock; } + ret = dl_bw_alloc(cpu, cs->sum_migrate_dl_bw); + if (ret) + goto out_unlock; + + cs->dl_bw_cpu = cpu; + out_success: /* * Mark attach is in progress. This makes validate_change() fail * changes which zero cpus/mems_allowed. */ cs->attach_in_progress++; + out_unlock: + if (ret) + reset_migrate_dl_data(cs); mutex_unlock(&cpuset_mutex); return ret; } @@ -3080,12 +3090,11 @@ static void cpuset_cancel_attach(struct cgroup_taskset *tset) mutex_lock(&cpuset_mutex); dec_attach_in_progress_locked(cs); - if (cs->nr_migrate_dl_tasks) { - int cpu = cpumask_any(cs->effective_cpus); + if (cs->dl_bw_cpu >= 0) + dl_bw_free(cs->dl_bw_cpu, cs->sum_migrate_dl_bw); - dl_bw_free(cpu, cs->sum_migrate_dl_bw); + if (cs->nr_migrate_dl_tasks) reset_migrate_dl_data(cs); - } mutex_unlock(&cpuset_mutex); } @@ -4171,11 +4180,11 @@ static struct cpuset *nearest_hardwall_ancestor(struct cpuset *cs) * current's mems_allowed, yes. If it's not a __GFP_HARDWALL request and this * node is set in the nearest hardwalled cpuset ancestor to current's cpuset, * yes. If current has access to memory reserves as an oom victim, yes. - * Otherwise, no. + * If the current task is PF_EXITING, yes. Otherwise, no. * * GFP_USER allocations are marked with the __GFP_HARDWALL bit, * and do not allow allocations outside the current tasks cpuset - * unless the task has been OOM killed. + * unless the task has been OOM killed or is exiting. * GFP_KERNEL allocations are not so marked, so can escape to the * nearest enclosing hardwalled ancestor cpuset. * @@ -4189,7 +4198,9 @@ static struct cpuset *nearest_hardwall_ancestor(struct cpuset *cs) * The first call here from mm/page_alloc:get_page_from_freelist() * has __GFP_HARDWALL set in gfp_mask, enforcing hardwall cpusets, * so no allocation on a node outside the cpuset is allowed (unless - * in interrupt, of course). + * in interrupt, of course). The PF_EXITING check must therefore + * come before the __GFP_HARDWALL check, otherwise a dying task + * would be blocked on the fast path. * * The second pass through get_page_from_freelist() doesn't even call * here for GFP_ATOMIC calls. For those calls, the __alloc_pages() @@ -4199,6 +4210,7 @@ static struct cpuset *nearest_hardwall_ancestor(struct cpuset *cs) * in_interrupt - any node ok (current task context irrelevant) * GFP_ATOMIC - any node ok * tsk_is_oom_victim - any node ok + * PF_EXITING - any node ok (let dying task exit quickly) * GFP_KERNEL - any node in enclosing hardwalled cpuset ok * GFP_USER - only nodes in current tasks mems allowed ok. */ @@ -4218,11 +4230,10 @@ bool cpuset_current_node_allowed(int node, gfp_t gfp_mask) */ if (unlikely(tsk_is_oom_victim(current))) return true; - if (gfp_mask & __GFP_HARDWALL) /* If hardwall request, stop here */ - return false; - if (current->flags & PF_EXITING) /* Let dying task have memory */ return true; + if (gfp_mask & __GFP_HARDWALL) /* If hardwall request, stop here */ + return false; /* Not hardwall and node outside mems_allowed: scan up cpusets */ spin_lock_irqsave(&callback_lock, flags); diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c index 1ab1fb47f271..4753a67d0f0f 100644 --- a/kernel/cgroup/dmem.c +++ b/kernel/cgroup/dmem.c @@ -602,6 +602,7 @@ get_cg_pool_unlocked(struct dmemcg_state *cg, struct dmem_cgroup_region *region) pool = NULL; continue; } + pool = ERR_PTR(-ENOMEM); } } diff --git a/kernel/cgroup/rdma.c b/kernel/cgroup/rdma.c index 9967fb25c563..4fdab4cf49e0 100644 --- a/kernel/cgroup/rdma.c +++ b/kernel/cgroup/rdma.c @@ -283,7 +283,7 @@ int rdmacg_try_charge(struct rdma_cgroup **rdmacg, ret = PTR_ERR(rpool); goto err; } else { - new = rpool->resources[index].usage + 1; + new = (s64)rpool->resources[index].usage + 1; if (new > rpool->resources[index].max) { ret = -EAGAIN; goto err; diff --git a/kernel/crash_core.c b/kernel/crash_core.c index 2c1a3791e410..4f21fc3b108b 100644 --- a/kernel/crash_core.c +++ b/kernel/crash_core.c @@ -27,8 +27,6 @@ #include #include -#include - #include "kallsyms_internal.h" #include "kexec_internal.h" diff --git a/kernel/crash_dump_dm_crypt.c b/kernel/crash_dump_dm_crypt.c index a20d4097744a..cb875ddb6ba6 100644 --- a/kernel/crash_dump_dm_crypt.c +++ b/kernel/crash_dump_dm_crypt.c @@ -6,6 +6,7 @@ #include #include #include +#include #define KEY_NUM_MAX 128 /* maximum dm crypt keys */ #define KEY_SIZE_MAX 256 /* maximum dm crypt key size */ @@ -115,7 +116,7 @@ static int restore_dm_crypt_keys_to_thread_keyring(void) addr = dm_crypt_keys_addr; dm_crypt_keys_read((char *)&key_count, sizeof(key_count), &addr); - if (key_count < 0 || key_count > KEY_NUM_MAX) { + if (key_count > KEY_NUM_MAX) { kexec_dprintk("Failed to read the number of dm-crypt keys\n"); return -1; } @@ -139,7 +140,7 @@ static int restore_dm_crypt_keys_to_thread_keyring(void) return 0; } -static int read_key_from_user_keying(struct dm_crypt_key *dm_key) +static int read_key_from_user_keyring(struct dm_crypt_key *dm_key) { const struct user_key_payload *ukp; struct key *key; @@ -189,7 +190,7 @@ static inline struct config_key *to_config_key(struct config_item *item) static ssize_t config_key_description_show(struct config_item *item, char *page) { - return sprintf(page, "%s\n", to_config_key(item)->description); + return sysfs_emit(page, "%s\n", to_config_key(item)->description); } static ssize_t config_key_description_store(struct config_item *item, @@ -265,7 +266,7 @@ static struct config_item *config_keys_make_item(struct config_group *group, static ssize_t config_keys_count_show(struct config_item *item, char *page) { - return sprintf(page, "%d\n", key_count); + return sysfs_emit(page, "%d\n", key_count); } CONFIGFS_ATTR_RO(config_keys_, count); @@ -274,7 +275,7 @@ static bool is_dm_key_reused; static ssize_t config_keys_reuse_show(struct config_item *item, char *page) { - return sprintf(page, "%d\n", is_dm_key_reused); + return sysfs_emit(page, "%d\n", is_dm_key_reused); } static ssize_t config_keys_reuse_store(struct config_item *item, @@ -321,7 +322,7 @@ static bool restore; static ssize_t config_keys_restore_show(struct config_item *item, char *page) { - return sprintf(page, "%d\n", restore); + return sysfs_emit(page, "%d\n", restore); } static ssize_t config_keys_restore_store(struct config_item *item, @@ -387,7 +388,7 @@ static int build_keys_header(void) strscpy(keys_header->keys[i].key_desc, key->description, KEY_DESC_MAX_LEN); - r = read_key_from_user_keying(&keys_header->keys[i]); + r = read_key_from_user_keyring(&keys_header->keys[i]); if (r != 0) { kexec_dprintk("Failed to read key %s\n", keys_header->keys[i].key_desc); @@ -414,14 +415,16 @@ int crash_load_dm_crypt_keys(struct kimage *image) if (key_count <= 0) { kexec_dprintk("No dm-crypt keys\n"); - return -ENOENT; + return 0; } if (!is_dm_key_reused) { image->dm_crypt_keys_addr = 0; r = build_keys_header(); - if (r) + if (r) { + pr_err("Failed to build dm-crypt keys header, ret=%d\n", r); return r; + } } kbuf.buffer = keys_header; @@ -432,6 +435,7 @@ int crash_load_dm_crypt_keys(struct kimage *image) kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; r = kexec_add_buffer(&kbuf); if (r) { + pr_err("Failed to call kexec_add_buffer, ret=%d\n", r); kvfree((void *)kbuf.buffer); return r; } diff --git a/kernel/crash_reserve.c b/kernel/crash_reserve.c index 62e60e0223cf..eee37a11380c 100644 --- a/kernel/crash_reserve.c +++ b/kernel/crash_reserve.c @@ -20,8 +20,6 @@ #include #include -#include - #include "kallsyms_internal.h" #include "kexec_internal.h" diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 0b9495187fba..b276504c1c6b 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -704,7 +704,7 @@ return_normal: if (ks->send_ready) atomic_set(ks->send_ready, 1); - /* Signal the other CPUs to enter kgdb_wait() */ + /* Signal the other CPUs to enter the debug trap handler */ else if ((!kgdb_single_step) && kgdb_do_roundup) kgdb_roundup_cpus(); #endif diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c index f586afd76c80..e271a436d60e 100644 --- a/kernel/debug/gdbstub.c +++ b/kernel/debug/gdbstub.c @@ -517,7 +517,7 @@ static void gdb_get_regs_helper(struct kgdb_state *ks) /* * All threads that don't have debuggerinfo should be * in schedule() sleeping, since all other CPUs - * are in kgdb_wait, and thus have debuggerinfo. + * are in kgdb_cpu_enter(), and thus have debuggerinfo. */ if (local_debuggerinfo) { pt_regs_to_gdb_regs(gdb_regs, local_debuggerinfo); diff --git a/kernel/dma/Kconfig b/kernel/dma/Kconfig index 159900736f25..bfef21b4a9ae 100644 --- a/kernel/dma/Kconfig +++ b/kernel/dma/Kconfig @@ -72,6 +72,9 @@ config ARCH_HAS_DMA_PREP_COHERENT config ARCH_HAS_FORCE_DMA_UNENCRYPTED bool +config ARCH_HAS_BATCHED_DMA_SYNC + bool + # # Select this option if the architecture assumes DMA devices are coherent # by default. diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c index 1147497bc512..bcdc0f76d2e8 100644 --- a/kernel/dma/coherent.c +++ b/kernel/dma/coherent.c @@ -362,17 +362,11 @@ static void rmem_dma_device_release(struct reserved_mem *rmem, dev->dma_mem = NULL; } -static const struct reserved_mem_ops rmem_dma_ops = { - .device_init = rmem_dma_device_init, - .device_release = rmem_dma_device_release, -}; -static int __init rmem_dma_setup(struct reserved_mem *rmem) +static int __init rmem_dma_setup(unsigned long node, struct reserved_mem *rmem) { - unsigned long node = rmem->fdt_node; - if (of_get_flat_dt_prop(node, "reusable", NULL)) - return -EINVAL; + return -ENODEV; #ifdef CONFIG_ARM if (!of_get_flat_dt_prop(node, "no-map", NULL)) { @@ -390,7 +384,6 @@ static int __init rmem_dma_setup(struct reserved_mem *rmem) } #endif - rmem->ops = &rmem_dma_ops; pr_info("Reserved memory: created DMA memory pool at %pa, size %ld MiB\n", &rmem->base, (unsigned long)rmem->size / SZ_1M); return 0; @@ -407,5 +400,11 @@ static int __init dma_init_reserved_memory(void) core_initcall(dma_init_reserved_memory); #endif /* CONFIG_DMA_GLOBAL_POOL */ -RESERVEDMEM_OF_DECLARE(dma, "shared-dma-pool", rmem_dma_setup); +static const struct reserved_mem_ops rmem_dma_ops = { + .node_init = rmem_dma_setup, + .device_init = rmem_dma_device_init, + .device_release = rmem_dma_device_release, +}; + +RESERVEDMEM_OF_DECLARE(dma, "shared-dma-pool", &rmem_dma_ops); #endif diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c index c56004d314dc..03f52bd17120 100644 --- a/kernel/dma/contiguous.c +++ b/kernel/dma/contiguous.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -53,7 +52,38 @@ #define CMA_SIZE_MBYTES 0 #endif -struct cma *dma_contiguous_default_area; +static struct cma *dma_contiguous_areas[MAX_CMA_AREAS]; +static unsigned int dma_contiguous_areas_num; + +static int dma_contiguous_insert_area(struct cma *cma) +{ + if (dma_contiguous_areas_num >= ARRAY_SIZE(dma_contiguous_areas)) + return -EINVAL; + + dma_contiguous_areas[dma_contiguous_areas_num++] = cma; + + return 0; +} + +/** + * dma_contiguous_get_area_by_idx() - Get contiguous area at given index + * @idx: index of the area we query + * + * Queries for the contiguous area located at index @idx. + * + * Returns: + * A pointer to the requested contiguous area, or NULL otherwise. + */ +struct cma *dma_contiguous_get_area_by_idx(unsigned int idx) +{ + if (idx >= dma_contiguous_areas_num) + return NULL; + + return dma_contiguous_areas[idx]; +} +EXPORT_SYMBOL_GPL(dma_contiguous_get_area_by_idx); + +static struct cma *dma_contiguous_default_area; /* * Default global CMA area size can be defined in kernel's .config. @@ -91,15 +121,14 @@ static int __init early_cma(char *p) } early_param("cma", early_cma); -/* - * cma_skip_dt_default_reserved_mem - This is called from the - * reserved_mem framework to detect if the default cma region is being - * set by the "cma=" kernel parameter. - */ -bool __init cma_skip_dt_default_reserved_mem(void) +struct cma *dev_get_cma_area(struct device *dev) { - return size_cmdline != -1; + if (dev && dev->cma_area) + return dev->cma_area; + + return dma_contiguous_default_area; } +EXPORT_SYMBOL_GPL(dev_get_cma_area); #ifdef CONFIG_DMA_NUMA_CMA @@ -264,9 +293,24 @@ void __init dma_contiguous_reserve(phys_addr_t limit) if (ret) return; - ret = dma_heap_cma_register_heap(dma_contiguous_default_area); + /* + * We need to insert the new area in our list to avoid + * any inconsistencies between having the default area + * listed in the DT or not. + * + * The DT case is handled by rmem_cma_setup() and will + * always insert all its areas in our list. However, if + * it didn't run (because OF_RESERVED_MEM isn't set, or + * there's no DT region specified), then we don't have a + * default area yet, and no area in our list. + * + * This block creates the default area in such a case, + * but we also need to insert it in our list to avoid + * having a default area but an empty list. + */ + ret = dma_contiguous_insert_area(dma_contiguous_default_area); if (ret) - pr_warn("Couldn't register default CMA heap."); + pr_warn("Couldn't queue default CMA region for heap creation."); } } @@ -470,47 +514,89 @@ static void rmem_cma_device_release(struct reserved_mem *rmem, dev->cma_area = NULL; } -static const struct reserved_mem_ops rmem_cma_ops = { - .device_init = rmem_cma_device_init, - .device_release = rmem_cma_device_release, -}; - -static int __init rmem_cma_setup(struct reserved_mem *rmem) +static int __init __rmem_cma_verify_node(unsigned long node) { - unsigned long node = rmem->fdt_node; - bool default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL); - struct cma *cma; - int err; - if (!of_get_flat_dt_prop(node, "reusable", NULL) || of_get_flat_dt_prop(node, "no-map", NULL)) - return -EINVAL; + return -ENODEV; + + if (size_cmdline != -1 && + of_get_flat_dt_prop(node, "linux,cma-default", NULL)) { + pr_err("Skipping dt linux,cma-default node in favor for \"cma=\" kernel param.\n"); + return -EBUSY; + } + return 0; +} + +static int __init rmem_cma_validate(unsigned long node, phys_addr_t *align) +{ + int ret = __rmem_cma_verify_node(node); + + if (ret) + return ret; + + if (align) + *align = max_t(phys_addr_t, *align, CMA_MIN_ALIGNMENT_BYTES); + + return 0; +} + +static int __init rmem_cma_fixup(unsigned long node, phys_addr_t base, + phys_addr_t size) +{ + int ret = __rmem_cma_verify_node(node); + + if (ret) + return ret; + + /* Architecture specific contiguous memory fixup. */ + dma_contiguous_early_fixup(base, size); + return 0; +} + +static int __init rmem_cma_setup(unsigned long node, struct reserved_mem *rmem) +{ + bool default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL); + struct cma *cma; + int ret; + + ret = __rmem_cma_verify_node(node); + if (ret) + return ret; if (!IS_ALIGNED(rmem->base | rmem->size, CMA_MIN_ALIGNMENT_BYTES)) { pr_err("Reserved memory: incorrect alignment of CMA region\n"); return -EINVAL; } - err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma); - if (err) { + ret = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma); + if (ret) { pr_err("Reserved memory: unable to setup CMA region\n"); - return err; + return ret; } if (default_cma) dma_contiguous_default_area = cma; - rmem->ops = &rmem_cma_ops; rmem->priv = cma; pr_info("Reserved memory: created CMA memory pool at %pa, size %ld MiB\n", &rmem->base, (unsigned long)rmem->size / SZ_1M); - err = dma_heap_cma_register_heap(cma); - if (err) - pr_warn("Couldn't register CMA heap."); + ret = dma_contiguous_insert_area(cma); + if (ret) + pr_warn("Couldn't store CMA reserved area."); return 0; } -RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup); + +static const struct reserved_mem_ops rmem_cma_ops = { + .node_validate = rmem_cma_validate, + .node_fixup = rmem_cma_fixup, + .node_init = rmem_cma_setup, + .device_init = rmem_cma_device_init, + .device_release = rmem_cma_device_release, +}; + +RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", &rmem_cma_ops); #endif diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c index 8f43a930716d..ec887f443741 100644 --- a/kernel/dma/direct.c +++ b/kernel/dma/direct.c @@ -406,6 +406,8 @@ void dma_direct_sync_sg_for_device(struct device *dev, arch_sync_dma_for_device(paddr, sg->length, dir); } + if (!dev_is_dma_coherent(dev)) + arch_sync_dma_flush(); } #endif @@ -427,8 +429,10 @@ void dma_direct_sync_sg_for_cpu(struct device *dev, swiotlb_sync_single_for_cpu(dev, paddr, sg->length, dir); } - if (!dev_is_dma_coherent(dev)) + if (!dev_is_dma_coherent(dev)) { + arch_sync_dma_flush(); arch_sync_dma_for_cpu_all(); + } } /* @@ -440,14 +444,19 @@ void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sgl, { struct scatterlist *sg; int i; + bool need_sync = false; for_each_sg(sgl, sg, nents, i) { - if (sg_dma_is_bus_address(sg)) + if (sg_dma_is_bus_address(sg)) { sg_dma_unmark_bus_address(sg); - else + } else { + need_sync = true; dma_direct_unmap_phys(dev, sg->dma_address, - sg_dma_len(sg), dir, attrs); + sg_dma_len(sg), dir, attrs, false); + } } + if (need_sync && !dev_is_dma_coherent(dev)) + arch_sync_dma_flush(); } #endif @@ -457,6 +466,7 @@ int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents, struct pci_p2pdma_map_state p2pdma_state = {}; struct scatterlist *sg; int i, ret; + bool need_sync = false; for_each_sg(sgl, sg, nents, i) { switch (pci_p2pdma_state(&p2pdma_state, dev, sg_page(sg))) { @@ -468,8 +478,9 @@ int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents, */ break; case PCI_P2PDMA_MAP_NONE: + need_sync = true; sg->dma_address = dma_direct_map_phys(dev, sg_phys(sg), - sg->length, dir, attrs); + sg->length, dir, attrs, false); if (sg->dma_address == DMA_MAPPING_ERROR) { ret = -EIO; goto out_unmap; @@ -488,6 +499,8 @@ int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents, sg_dma_len(sg) = sg->length; } + if (need_sync && !dev_is_dma_coherent(dev)) + arch_sync_dma_flush(); return nents; out_unmap: diff --git a/kernel/dma/direct.h b/kernel/dma/direct.h index 6184ff303f08..7140c208c123 100644 --- a/kernel/dma/direct.h +++ b/kernel/dma/direct.h @@ -60,17 +60,22 @@ static inline void dma_direct_sync_single_for_device(struct device *dev, swiotlb_sync_single_for_device(dev, paddr, size, dir); - if (!dev_is_dma_coherent(dev)) + if (!dev_is_dma_coherent(dev)) { arch_sync_dma_for_device(paddr, size, dir); + arch_sync_dma_flush(); + } } static inline void dma_direct_sync_single_for_cpu(struct device *dev, - dma_addr_t addr, size_t size, enum dma_data_direction dir) + dma_addr_t addr, size_t size, enum dma_data_direction dir, + bool flush) { phys_addr_t paddr = dma_to_phys(dev, addr); if (!dev_is_dma_coherent(dev)) { arch_sync_dma_for_cpu(paddr, size, dir); + if (flush) + arch_sync_dma_flush(); arch_sync_dma_for_cpu_all(); } @@ -79,21 +84,29 @@ static inline void dma_direct_sync_single_for_cpu(struct device *dev, static inline dma_addr_t dma_direct_map_phys(struct device *dev, phys_addr_t phys, size_t size, enum dma_data_direction dir, - unsigned long attrs) + unsigned long attrs, bool flush) { dma_addr_t dma_addr; if (is_swiotlb_force_bounce(dev)) { - if (attrs & (DMA_ATTR_MMIO | DMA_ATTR_REQUIRE_COHERENT)) - return DMA_MAPPING_ERROR; + if (!(attrs & DMA_ATTR_CC_SHARED)) { + if (attrs & (DMA_ATTR_MMIO | DMA_ATTR_REQUIRE_COHERENT)) + return DMA_MAPPING_ERROR; - return swiotlb_map(dev, phys, size, dir, attrs); + return swiotlb_map(dev, phys, size, dir, attrs); + } + } else if (attrs & DMA_ATTR_CC_SHARED) { + return DMA_MAPPING_ERROR; } if (attrs & DMA_ATTR_MMIO) { dma_addr = phys; if (unlikely(!dma_capable(dev, dma_addr, size, false))) goto err_overflow; + } else if (attrs & DMA_ATTR_CC_SHARED) { + dma_addr = phys_to_dma_unencrypted(dev, phys); + if (unlikely(!dma_capable(dev, dma_addr, size, false))) + goto err_overflow; } else { dma_addr = phys_to_dma(dev, phys); if (unlikely(!dma_capable(dev, dma_addr, size, true)) || @@ -107,8 +120,11 @@ static inline dma_addr_t dma_direct_map_phys(struct device *dev, } if (!dev_is_dma_coherent(dev) && - !(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))) + !(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))) { arch_sync_dma_for_device(phys, size, dir); + if (flush) + arch_sync_dma_flush(); + } return dma_addr; err_overflow: @@ -120,7 +136,8 @@ err_overflow: } static inline void dma_direct_unmap_phys(struct device *dev, dma_addr_t addr, - size_t size, enum dma_data_direction dir, unsigned long attrs) + size_t size, enum dma_data_direction dir, unsigned long attrs, + bool flush) { phys_addr_t phys; @@ -130,7 +147,7 @@ static inline void dma_direct_unmap_phys(struct device *dev, dma_addr_t addr, phys = dma_to_phys(dev, addr); if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) - dma_direct_sync_single_for_cpu(dev, addr, size, dir); + dma_direct_sync_single_for_cpu(dev, addr, size, dir, flush); swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs | DMA_ATTR_SKIP_CPU_SYNC); diff --git a/kernel/dma/map_benchmark.c b/kernel/dma/map_benchmark.c index 0f33b3ea7daf..29eeb5fdf199 100644 --- a/kernel/dma/map_benchmark.c +++ b/kernel/dma/map_benchmark.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -15,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -31,17 +33,219 @@ struct map_benchmark_data { atomic64_t loops; }; -static int map_benchmark_thread(void *data) +struct map_benchmark_ops { + void *(*prepare)(struct map_benchmark_data *map); + void (*unprepare)(void *mparam); + void (*initialize_data)(void *mparam); + int (*do_map)(void *mparam); + void (*do_unmap)(void *mparam); +}; + +struct dma_single_map_param { + struct device *dev; + dma_addr_t addr; + void *xbuf; + u32 npages; + u32 dma_dir; +}; + +static void *dma_single_map_benchmark_prepare(struct map_benchmark_data *map) { - void *buf; - dma_addr_t dma_addr; - struct map_benchmark_data *map = data; - int npages = map->bparam.granule; - u64 size = npages * PAGE_SIZE; + struct dma_single_map_param *params __free(kfree) = kzalloc(sizeof(*params), + GFP_KERNEL); + if (!params) + return NULL; + + params->npages = map->bparam.granule; + params->dma_dir = map->bparam.dma_dir; + params->dev = map->dev; + params->xbuf = alloc_pages_exact(params->npages * PAGE_SIZE, GFP_KERNEL); + if (!params->xbuf) + return NULL; + + return_ptr(params); +} + +static void dma_single_map_benchmark_unprepare(void *mparam) +{ + struct dma_single_map_param *params = mparam; + + free_pages_exact(params->xbuf, params->npages * PAGE_SIZE); + kfree(params); +} + +static void dma_single_map_benchmark_initialize_data(void *mparam) +{ + struct dma_single_map_param *params = mparam; + + /* + * for a non-coherent device, if we don't stain them in the + * cache, this will give an underestimate of the real-world + * overhead of BIDIRECTIONAL or TO_DEVICE mappings; + * 66 means everything goes well! 66 is lucky. + */ + if (params->dma_dir != DMA_FROM_DEVICE) + memset(params->xbuf, 0x66, params->npages * PAGE_SIZE); +} + +static int dma_single_map_benchmark_do_map(void *mparam) +{ + struct dma_single_map_param *params = mparam; + + params->addr = dma_map_single(params->dev, params->xbuf, + params->npages * PAGE_SIZE, params->dma_dir); + if (unlikely(dma_mapping_error(params->dev, params->addr))) { + pr_err("dma_map_single failed on %s\n", dev_name(params->dev)); + return -ENOMEM; + } + + return 0; +} + +static void dma_single_map_benchmark_do_unmap(void *mparam) +{ + struct dma_single_map_param *params = mparam; + + dma_unmap_single(params->dev, params->addr, + params->npages * PAGE_SIZE, params->dma_dir); +} + +static struct map_benchmark_ops dma_single_map_benchmark_ops = { + .prepare = dma_single_map_benchmark_prepare, + .unprepare = dma_single_map_benchmark_unprepare, + .initialize_data = dma_single_map_benchmark_initialize_data, + .do_map = dma_single_map_benchmark_do_map, + .do_unmap = dma_single_map_benchmark_do_unmap, +}; + +struct dma_sg_map_param { + struct sg_table sgt; + struct device *dev; + void **buf; + u32 npages; + u32 dma_dir; +}; + +static void *dma_sg_map_benchmark_prepare(struct map_benchmark_data *map) +{ + struct scatterlist *sg; + int i; + + struct dma_sg_map_param *params = kzalloc(sizeof(*params), GFP_KERNEL); + + if (!params) + return NULL; + /* + * Set the number of scatterlist entries based on the granule. + * In SG mode, 'granule' represents the number of scatterlist entries. + * Each scatterlist entry corresponds to a single page. + */ + params->npages = map->bparam.granule; + params->dma_dir = map->bparam.dma_dir; + params->dev = map->dev; + params->buf = kmalloc_array(params->npages, sizeof(*params->buf), + GFP_KERNEL); + if (!params->buf) + goto out; + + if (sg_alloc_table(¶ms->sgt, params->npages, GFP_KERNEL)) + goto free_buf; + + for_each_sgtable_sg(¶ms->sgt, sg, i) { + params->buf[i] = (void *)__get_free_page(GFP_KERNEL); + if (!params->buf[i]) + goto free_page; + + sg_set_buf(sg, params->buf[i], PAGE_SIZE); + } + + return params; + +free_page: + while (i-- > 0) + free_page((unsigned long)params->buf[i]); + + sg_free_table(¶ms->sgt); +free_buf: + kfree(params->buf); +out: + kfree(params); + return NULL; +} + +static void dma_sg_map_benchmark_unprepare(void *mparam) +{ + struct dma_sg_map_param *params = mparam; + int i; + + for (i = 0; i < params->npages; i++) + free_page((unsigned long)params->buf[i]); + + sg_free_table(¶ms->sgt); + + kfree(params->buf); + kfree(params); +} + +static void dma_sg_map_benchmark_initialize_data(void *mparam) +{ + struct dma_sg_map_param *params = mparam; + struct scatterlist *sg; + int i = 0; + + if (params->dma_dir == DMA_FROM_DEVICE) + return; + + for_each_sgtable_sg(¶ms->sgt, sg, i) + memset(params->buf[i], 0x66, PAGE_SIZE); +} + +static int dma_sg_map_benchmark_do_map(void *mparam) +{ + struct dma_sg_map_param *params = mparam; int ret = 0; - buf = alloc_pages_exact(size, GFP_KERNEL); - if (!buf) + int sg_mapped = dma_map_sg(params->dev, params->sgt.sgl, + params->npages, params->dma_dir); + if (!sg_mapped) { + pr_err("dma_map_sg failed on %s\n", dev_name(params->dev)); + ret = -ENOMEM; + } + + return ret; +} + +static void dma_sg_map_benchmark_do_unmap(void *mparam) +{ + struct dma_sg_map_param *params = mparam; + + dma_unmap_sg(params->dev, params->sgt.sgl, params->npages, + params->dma_dir); +} + +static struct map_benchmark_ops dma_sg_map_benchmark_ops = { + .prepare = dma_sg_map_benchmark_prepare, + .unprepare = dma_sg_map_benchmark_unprepare, + .initialize_data = dma_sg_map_benchmark_initialize_data, + .do_map = dma_sg_map_benchmark_do_map, + .do_unmap = dma_sg_map_benchmark_do_unmap, +}; + +static struct map_benchmark_ops *dma_map_benchmark_ops[DMA_MAP_BENCH_MODE_MAX] = { + [DMA_MAP_BENCH_SINGLE_MODE] = &dma_single_map_benchmark_ops, + [DMA_MAP_BENCH_SG_MODE] = &dma_sg_map_benchmark_ops, +}; + +static int map_benchmark_thread(void *data) +{ + struct map_benchmark_data *map = data; + __u8 map_mode = map->bparam.map_mode; + int ret = 0; + + struct map_benchmark_ops *mb_ops = dma_map_benchmark_ops[map_mode]; + void *mparam = mb_ops->prepare(map); + + if (!mparam) return -ENOMEM; while (!kthread_should_stop()) { @@ -49,23 +253,12 @@ static int map_benchmark_thread(void *data) ktime_t map_stime, map_etime, unmap_stime, unmap_etime; ktime_t map_delta, unmap_delta; - /* - * for a non-coherent device, if we don't stain them in the - * cache, this will give an underestimate of the real-world - * overhead of BIDIRECTIONAL or TO_DEVICE mappings; - * 66 means evertything goes well! 66 is lucky. - */ - if (map->dir != DMA_FROM_DEVICE) - memset(buf, 0x66, size); - + mb_ops->initialize_data(mparam); map_stime = ktime_get(); - dma_addr = dma_map_single(map->dev, buf, size, map->dir); - if (unlikely(dma_mapping_error(map->dev, dma_addr))) { - pr_err("dma_map_single failed on %s\n", - dev_name(map->dev)); - ret = -ENOMEM; + ret = mb_ops->do_map(mparam); + if (ret) goto out; - } + map_etime = ktime_get(); map_delta = ktime_sub(map_etime, map_stime); @@ -73,7 +266,8 @@ static int map_benchmark_thread(void *data) ndelay(map->bparam.dma_trans_ns); unmap_stime = ktime_get(); - dma_unmap_single(map->dev, dma_addr, size, map->dir); + mb_ops->do_unmap(mparam); + unmap_etime = ktime_get(); unmap_delta = ktime_sub(unmap_etime, unmap_stime); @@ -108,7 +302,7 @@ static int map_benchmark_thread(void *data) } out: - free_pages_exact(buf, size); + mb_ops->unprepare(mparam); return ret; } @@ -209,6 +403,12 @@ static long map_benchmark_ioctl(struct file *file, unsigned int cmd, switch (cmd) { case DMA_MAP_BENCHMARK: + if (map->bparam.map_mode < 0 || + map->bparam.map_mode >= DMA_MAP_BENCH_MODE_MAX) { + pr_err("invalid map mode\n"); + return -EINVAL; + } + if (map->bparam.threads == 0 || map->bparam.threads > DMA_MAP_MAX_THREADS) { pr_err("invalid thread number\n"); diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c index 6d3dd0bd3a88..23ed8eb9233e 100644 --- a/kernel/dma/mapping.c +++ b/kernel/dma/mapping.c @@ -157,6 +157,7 @@ dma_addr_t dma_map_phys(struct device *dev, phys_addr_t phys, size_t size, { const struct dma_map_ops *ops = get_dma_ops(dev); bool is_mmio = attrs & DMA_ATTR_MMIO; + bool is_cc_shared = attrs & DMA_ATTR_CC_SHARED; dma_addr_t addr = DMA_MAPPING_ERROR; BUG_ON(!valid_dma_direction(dir)); @@ -168,8 +169,11 @@ dma_addr_t dma_map_phys(struct device *dev, phys_addr_t phys, size_t size, return DMA_MAPPING_ERROR; if (dma_map_direct(dev, ops) || - (!is_mmio && arch_dma_map_phys_direct(dev, phys + size))) - addr = dma_direct_map_phys(dev, phys, size, dir, attrs); + (!is_mmio && !is_cc_shared && + arch_dma_map_phys_direct(dev, phys + size))) + addr = dma_direct_map_phys(dev, phys, size, dir, attrs, true); + else if (is_cc_shared) + return DMA_MAPPING_ERROR; else if (use_dma_iommu(dev)) addr = iommu_dma_map_phys(dev, phys, size, dir, attrs); else if (ops->map_phys) @@ -206,11 +210,16 @@ void dma_unmap_phys(struct device *dev, dma_addr_t addr, size_t size, { const struct dma_map_ops *ops = get_dma_ops(dev); bool is_mmio = attrs & DMA_ATTR_MMIO; + bool is_cc_shared = attrs & DMA_ATTR_CC_SHARED; BUG_ON(!valid_dma_direction(dir)); + if (dma_map_direct(dev, ops) || - (!is_mmio && arch_dma_unmap_phys_direct(dev, addr + size))) - dma_direct_unmap_phys(dev, addr, size, dir, attrs); + (!is_mmio && !is_cc_shared && + arch_dma_unmap_phys_direct(dev, addr + size))) + dma_direct_unmap_phys(dev, addr, size, dir, attrs, true); + else if (is_cc_shared) + return; else if (use_dma_iommu(dev)) iommu_dma_unmap_phys(dev, addr, size, dir, attrs); else if (ops->unmap_phys) @@ -379,7 +388,7 @@ void __dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr, size_t size, BUG_ON(!valid_dma_direction(dir)); if (dma_map_direct(dev, ops)) - dma_direct_sync_single_for_cpu(dev, addr, size, dir); + dma_direct_sync_single_for_cpu(dev, addr, size, dir, true); else if (use_dma_iommu(dev)) iommu_dma_sync_single_for_cpu(dev, addr, size, dir); else if (ops->sync_single_for_cpu) diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index 9fd73700ddcf..1abd3e6146f4 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -547,10 +547,10 @@ void __init swiotlb_exit(void) free_pages(tbl_vaddr, get_order(tbl_size)); free_pages((unsigned long)mem->slots, get_order(slots_size)); } else { - memblock_free_late(__pa(mem->areas), + memblock_free(mem->areas, array_size(sizeof(*mem->areas), mem->nareas)); - memblock_free_late(mem->start, tbl_size); - memblock_free_late(__pa(mem->slots), slots_size); + memblock_phys_free(mem->start, tbl_size); + memblock_free(mem->slots, slots_size); } memset(mem, 0, sizeof(*mem)); @@ -868,6 +868,9 @@ static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size if (orig_addr == INVALID_PHYS_ADDR) return; + if (dir == DMA_FROM_DEVICE && !dev_is_dma_coherent(dev)) + arch_sync_dma_flush(); + /* * It's valid for tlb_offset to be negative. This can happen when the * "offset" returned by swiotlb_align_offset() is non-zero, and the @@ -1612,8 +1615,10 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size, return DMA_MAPPING_ERROR; } - if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) + if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) { arch_sync_dma_for_device(swiotlb_addr, size, dir); + arch_sync_dma_flush(); + } return dma_addr; } @@ -1872,26 +1877,25 @@ static void rmem_swiotlb_device_release(struct reserved_mem *rmem, dev->dma_io_tlb_mem = &io_tlb_default_mem; } -static const struct reserved_mem_ops rmem_swiotlb_ops = { - .device_init = rmem_swiotlb_device_init, - .device_release = rmem_swiotlb_device_release, -}; - -static int __init rmem_swiotlb_setup(struct reserved_mem *rmem) +static int __init rmem_swiotlb_setup(unsigned long node, + struct reserved_mem *rmem) { - unsigned long node = rmem->fdt_node; - if (of_get_flat_dt_prop(node, "reusable", NULL) || of_get_flat_dt_prop(node, "linux,cma-default", NULL) || of_get_flat_dt_prop(node, "linux,dma-default", NULL) || of_get_flat_dt_prop(node, "no-map", NULL)) return -EINVAL; - rmem->ops = &rmem_swiotlb_ops; pr_info("Reserved memory: created restricted DMA pool at %pa, size %ld MiB\n", &rmem->base, (unsigned long)rmem->size / SZ_1M); return 0; } -RESERVEDMEM_OF_DECLARE(dma, "restricted-dma-pool", rmem_swiotlb_setup); +static const struct reserved_mem_ops rmem_swiotlb_ops = { + .node_init = rmem_swiotlb_setup, + .device_init = rmem_swiotlb_device_init, + .device_release = rmem_swiotlb_device_release, +}; + +RESERVEDMEM_OF_DECLARE(dma, "restricted-dma-pool", &rmem_swiotlb_ops); #endif /* CONFIG_DMA_RESTRICTED_POOL */ diff --git a/kernel/events/core.c b/kernel/events/core.c index 6d1f8bad7e1c..7935d5663944 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7006,6 +7006,7 @@ static void perf_mmap_open(struct vm_area_struct *vma) } static void perf_pmu_output_stop(struct perf_event *event); +static void perf_mmap_unaccount(struct vm_area_struct *vma, struct perf_buffer *rb); /* * A buffer can be mmap()ed multiple times; either directly through the same @@ -7021,8 +7022,6 @@ static void perf_mmap_close(struct vm_area_struct *vma) mapped_f unmapped = get_mapped(event, event_unmapped); struct perf_buffer *rb = ring_buffer_get(event); struct user_struct *mmap_user = rb->mmap_user; - int mmap_locked = rb->mmap_locked; - unsigned long size = perf_data_size(rb); bool detach_rest = false; /* FIXIES vs perf_pmu_unregister() */ @@ -7117,11 +7116,7 @@ again: * Aside from that, this buffer is 'fully' detached and unmapped, * undo the VM accounting. */ - - atomic_long_sub((size >> PAGE_SHIFT) + 1 - mmap_locked, - &mmap_user->locked_vm); - atomic64_sub(mmap_locked, &vma->vm_mm->pinned_vm); - free_uid(mmap_user); + perf_mmap_unaccount(vma, rb); out_put: ring_buffer_put(rb); /* could be last */ @@ -7261,6 +7256,15 @@ static void perf_mmap_account(struct vm_area_struct *vma, long user_extra, long atomic64_add(extra, &vma->vm_mm->pinned_vm); } +static void perf_mmap_unaccount(struct vm_area_struct *vma, struct perf_buffer *rb) +{ + struct user_struct *user = rb->mmap_user; + + atomic_long_sub((perf_data_size(rb) >> PAGE_SHIFT) + 1 - rb->mmap_locked, + &user->locked_vm); + atomic64_sub(rb->mmap_locked, &vma->vm_mm->pinned_vm); +} + static int perf_mmap_rb(struct vm_area_struct *vma, struct perf_event *event, unsigned long nr_pages) { @@ -7323,8 +7327,6 @@ static int perf_mmap_rb(struct vm_area_struct *vma, struct perf_event *event, if (!rb) return -ENOMEM; - refcount_set(&rb->mmap_count, 1); - rb->mmap_user = get_current_user(); rb->mmap_locked = extra; ring_buffer_attach(event, rb); @@ -7474,16 +7476,54 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) mapped(event, vma->vm_mm); /* - * Try to map it into the page table. On fail, invoke - * perf_mmap_close() to undo the above, as the callsite expects - * full cleanup in this case and therefore does not invoke - * vmops::close(). + * Try to map it into the page table. On fail undo the above, + * as the callsite expects full cleanup in this case and + * therefore does not invoke vmops::close(). */ ret = map_range(event->rb, vma); - if (ret) - perf_mmap_close(vma); + if (likely(!ret)) + return 0; + + /* Error path */ + + /* + * If this is the first mmap(), then event->mmap_count should + * be stable at 1. It is only modified by: + * perf_mmap_{open,close}() and perf_mmap(). + * + * The former are not possible because this mmap() hasn't been + * successful yet, and the latter is serialized by + * event->mmap_mutex which we still hold (note that mmap_lock + * is not strictly sufficient here, because the event fd can + * be passed to another process through trivial means like + * fork(), leading to concurrent mmap() from different mm). + * + * Make sure to remove event->rb before releasing + * event->mmap_mutex, such that any concurrent mmap() will not + * attempt use this failed buffer. + */ + if (refcount_read(&event->mmap_count) == 1) { + /* + * Minimal perf_mmap_close(); there can't be AUX or + * other events on account of this being the first. + */ + mapped = get_mapped(event, event_unmapped); + if (mapped) + mapped(event, vma->vm_mm); + perf_mmap_unaccount(vma, event->rb); + ring_buffer_attach(event, NULL); /* drops last rb->refcount */ + refcount_set(&event->mmap_count, 0); + return ret; + } + + /* + * Otherwise this is an already existing buffer, and there is + * no race vs first exposure, so fall-through and call + * perf_mmap_close(). + */ } + perf_mmap_close(vma); return ret; } diff --git a/kernel/events/internal.h b/kernel/events/internal.h index d9cc57083091..c03c4f2eea57 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -67,6 +67,7 @@ static inline void rb_free_rcu(struct rcu_head *rcu_head) struct perf_buffer *rb; rb = container_of(rcu_head, struct perf_buffer, rcu_head); + free_uid(rb->mmap_user); rb_free(rb); } diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 3e7de2661417..9fe92161715e 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -340,6 +340,8 @@ ring_buffer_init(struct perf_buffer *rb, long watermark, int flags) rb->paused = 1; mutex_init(&rb->aux_mutex); + rb->mmap_user = get_current_user(); + refcount_set(&rb->mmap_count, 1); } void perf_aux_output_flag(struct perf_output_handle *handle, u64 flags) diff --git a/kernel/exit.c b/kernel/exit.c index 31b714c3a791..f50d73c272d6 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -571,6 +571,7 @@ static void exit_mm(void) */ smp_mb__after_spinlock(); local_irq_disable(); + current->user_dumpable = (get_dumpable(mm) == SUID_DUMP_USER); current->mm = NULL; membarrier_update_current_mm(NULL); enter_lazy_tlb(mm, current); @@ -749,14 +750,12 @@ static void exit_notify(struct task_struct *tsk, int group_dead) tsk->exit_state = EXIT_ZOMBIE; if (unlikely(tsk->ptrace)) { - int sig = thread_group_leader(tsk) && - thread_group_empty(tsk) && - !ptrace_reparented(tsk) ? - tsk->exit_signal : SIGCHLD; + int sig = thread_group_empty(tsk) && !ptrace_reparented(tsk) + ? tsk->exit_signal : SIGCHLD; autoreap = do_notify_parent(tsk, sig); } else if (thread_group_leader(tsk)) { autoreap = thread_group_empty(tsk) && - do_notify_parent(tsk, tsk->exit_signal); + do_notify_parent(tsk, tsk->exit_signal); } else { autoreap = true; /* untraced sub-thread */ @@ -1075,6 +1074,7 @@ void __noreturn make_task_dead(int signr) futex_exit_recursive(tsk); tsk->exit_state = EXIT_DEAD; refcount_inc(&tsk->rcu_users); + preempt_disable(); do_task_dead(); } diff --git a/kernel/fork.c b/kernel/fork.c index fe3821160f9a..5f3fdfdb14c7 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -347,7 +347,7 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node) stack = kasan_reset_tag(vm_area->addr); /* Clear stale pointers from reused stack. */ - memset(stack, 0, THREAD_SIZE); + clear_pages(vm_area->addr, vm_area->nr_pages); tsk->stack_vm_area = vm_area; tsk->stack = stack; @@ -1016,13 +1016,14 @@ free_tsk: __cacheline_aligned_in_smp DEFINE_SPINLOCK(mmlist_lock); -static unsigned long default_dump_filter = MMF_DUMP_FILTER_DEFAULT; +static unsigned long coredump_filter = MMF_DUMP_FILTER_DEFAULT; static int __init coredump_filter_setup(char *s) { - default_dump_filter = - (simple_strtoul(s, NULL, 0) << MMF_DUMP_FILTER_SHIFT) & - MMF_DUMP_FILTER_MASK; + if (kstrtoul(s, 0, &coredump_filter)) + return 0; + coredump_filter <<= MMF_DUMP_FILTER_SHIFT; + coredump_filter &= MMF_DUMP_FILTER_MASK; return 1; } @@ -1108,7 +1109,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, __mm_flags_overwrite_word(mm, mmf_init_legacy_flags(flags)); mm->def_flags = current->mm->def_flags & VM_INIT_DEF_MASK; } else { - __mm_flags_overwrite_word(mm, default_dump_filter); + __mm_flags_overwrite_word(mm, coredump_filter); mm->def_flags = 0; } @@ -1950,9 +1951,11 @@ static void rv_task_fork(struct task_struct *p) static bool need_futex_hash_allocate_default(u64 clone_flags) { - if ((clone_flags & (CLONE_THREAD | CLONE_VM)) != (CLONE_THREAD | CLONE_VM)) - return false; - return true; + /* + * Allocate a default futex hash for any sibling that will + * share the parent's mm, except vfork. + */ + return (clone_flags & (CLONE_VM | CLONE_VFORK)) == CLONE_VM; } /* @@ -2379,10 +2382,6 @@ __latent_entropy struct task_struct *copy_process( if (retval) goto bad_fork_cancel_cgroup; - /* - * Allocate a default futex hash for the user process once the first - * thread spawns. - */ if (need_futex_hash_allocate_default(clone_flags)) { retval = futex_hash_allocate_default(); if (retval) @@ -2435,7 +2434,11 @@ __latent_entropy struct task_struct *copy_process( rseq_fork(p, clone_flags); - /* Don't start children in a dying pid namespace */ + /* + * If zap_pid_ns_processes() was called after alloc_pid(), the new + * child missed SIGKILL. If current is not in the same namespace, + * we can't rely on fatal_signal_pending() below. + */ if (unlikely(!(ns_of_pid(pid)->pid_allocated & PIDNS_ADDING))) { retval = -ENOMEM; goto bad_fork_core_free; @@ -3241,11 +3244,10 @@ int ksys_unshare(unsigned long unshare_flags) new_cred, new_fs); if (err) goto bad_unshare_cleanup_cred; - if (new_cred) { err = set_cred_ucounts(new_cred); if (err) - goto bad_unshare_cleanup_cred; + goto bad_unshare_cleanup_nsproxy; } if (new_fs || new_fd || do_sysvsem || new_cred || new_nsproxy) { @@ -3261,8 +3263,10 @@ int ksys_unshare(unsigned long unshare_flags) shm_init_task(current); } - if (new_nsproxy) + if (new_nsproxy) { switch_task_namespaces(current, new_nsproxy); + new_nsproxy = NULL; + } task_lock(current); @@ -3291,13 +3295,15 @@ int ksys_unshare(unsigned long unshare_flags) perf_event_namespaces(current); +bad_unshare_cleanup_nsproxy: + if (new_nsproxy) + put_nsproxy(new_nsproxy); bad_unshare_cleanup_cred: if (new_cred) put_cred(new_cred); bad_unshare_cleanup_fd: if (new_fd) put_files_struct(new_fd); - bad_unshare_cleanup_fs: if (new_fs) free_fs_struct(new_fs); diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c index d818b4d47f1b..b597cb3d17fc 100644 --- a/kernel/futex/requeue.c +++ b/kernel/futex/requeue.c @@ -319,8 +319,11 @@ futex_proxy_trylock_atomic(u32 __user *pifutex, struct futex_hash_bucket *hb1, return -EINVAL; /* Ensure that this does not race against an early wakeup */ - if (!futex_requeue_pi_prepare(top_waiter, NULL)) + if (!futex_requeue_pi_prepare(top_waiter, NULL)) { + plist_del(&top_waiter->list, &hb1->chain); + futex_hb_waiters_dec(hb1); return -EAGAIN; + } /* * Try to take the lock for top_waiter and set the FUTEX_WAITERS bit @@ -722,10 +725,12 @@ int handle_early_requeue_pi_wakeup(struct futex_hash_bucket *hb, /* * We were woken prior to requeue by a timeout or a signal. - * Unqueue the futex_q and determine which it was. + * Conditionally unqueue the futex_q and determine which it was. */ - plist_del(&q->list, &hb->chain); - futex_hb_waiters_dec(hb); + if (!plist_node_empty(&q->list)) { + plist_del(&q->list, &hb->chain); + futex_hb_waiters_dec(hb); + } /* Handle spurious wakeups gracefully */ ret = -EWOULDBLOCK; diff --git a/kernel/hung_task.c b/kernel/hung_task.c index d2254c91450b..6fcc94ce4ca9 100644 --- a/kernel/hung_task.c +++ b/kernel/hung_task.c @@ -36,7 +36,7 @@ static int __read_mostly sysctl_hung_task_check_count = PID_MAX_LIMIT; /* * Total number of tasks detected as hung since boot: */ -static unsigned long __read_mostly sysctl_hung_task_detect_count; +static atomic_long_t sysctl_hung_task_detect_count = ATOMIC_LONG_INIT(0); /* * Limit number of tasks checked in a batch. @@ -223,37 +223,36 @@ static inline void debug_show_blocker(struct task_struct *task, unsigned long ti } #endif -static void check_hung_task(struct task_struct *t, unsigned long timeout, - unsigned long prev_detect_count) +/** + * hung_task_info - Print diagnostic details for a hung task + * @t: Pointer to the detected hung task. + * @timeout: Timeout threshold for detecting hung tasks + * @this_round_count: Count of hung tasks detected in the current iteration + * + * Print structured information about the specified hung task, if warnings + * are enabled or if the panic batch threshold is exceeded. + */ +static void hung_task_info(struct task_struct *t, unsigned long timeout, + unsigned long this_round_count) { - unsigned long total_hung_task; - - if (!task_is_hung(t, timeout)) - return; - - /* - * This counter tracks the total number of tasks detected as hung - * since boot. - */ - sysctl_hung_task_detect_count++; - - total_hung_task = sysctl_hung_task_detect_count - prev_detect_count; trace_sched_process_hang(t); - if (sysctl_hung_task_panic && total_hung_task >= sysctl_hung_task_panic) { + if (sysctl_hung_task_panic && this_round_count >= sysctl_hung_task_panic) { console_verbose(); hung_task_call_panic = true; } /* - * Ok, the task did not get scheduled for more than 2 minutes, - * complain: + * The given task did not get scheduled for more than + * CONFIG_DEFAULT_HUNG_TASK_TIMEOUT. Therefore, complain + * accordingly */ if (sysctl_hung_task_warnings || hung_task_call_panic) { if (sysctl_hung_task_warnings > 0) sysctl_hung_task_warnings--; - pr_err("INFO: task %s:%d blocked for more than %ld seconds.\n", - t->comm, t->pid, (jiffies - t->last_switch_time) / HZ); + pr_err("INFO: task %s:%d blocked%s for more than %ld seconds.\n", + t->comm, t->pid, t->in_iowait ? " in I/O wait" : "", + (jiffies - t->last_switch_time) / HZ); pr_err(" %s %s %.*s\n", print_tainted(), init_utsname()->release, (int)strcspn(init_utsname()->version, " "), @@ -297,15 +296,14 @@ static bool rcu_lock_break(struct task_struct *g, struct task_struct *t) /* * Check whether a TASK_UNINTERRUPTIBLE does not get woken up for - * a really long time (120 seconds). If that happens, print out - * a warning. + * a really long time. If that happens, print out a warning. */ static void check_hung_uninterruptible_tasks(unsigned long timeout) { int max_count = sysctl_hung_task_check_count; unsigned long last_break = jiffies; struct task_struct *g, *t; - unsigned long prev_detect_count = sysctl_hung_task_detect_count; + unsigned long this_round_count; int need_warning = sysctl_hung_task_warnings; unsigned long si_mask = hung_task_si_mask; @@ -316,10 +314,9 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) if (test_taint(TAINT_DIE) || did_panic) return; - + this_round_count = 0; rcu_read_lock(); for_each_process_thread(g, t) { - if (!max_count--) goto unlock; if (time_after(jiffies, last_break + HUNG_TASK_LOCK_BREAK)) { @@ -328,12 +325,22 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) last_break = jiffies; } - check_hung_task(t, timeout, prev_detect_count); + if (task_is_hung(t, timeout)) { + /* + * Increment the global counter so that userspace could + * start migrating tasks ASAP. But count the current + * round separately because userspace could reset + * the global counter at any time. + */ + atomic_long_inc(&sysctl_hung_task_detect_count); + this_round_count++; + hung_task_info(t, timeout, this_round_count); + } } unlock: rcu_read_unlock(); - if (!(sysctl_hung_task_detect_count - prev_detect_count)) + if (!this_round_count) return; if (need_warning || hung_task_call_panic) { @@ -358,6 +365,46 @@ static long hung_timeout_jiffies(unsigned long last_checked, } #ifdef CONFIG_SYSCTL + +/** + * proc_dohung_task_detect_count - proc handler for hung_task_detect_count + * @table: Pointer to the struct ctl_table definition for this proc entry + * @dir: Flag indicating the operation + * @buffer: User space buffer for data transfer + * @lenp: Pointer to the length of the data being transferred + * @ppos: Pointer to the current file offset + * + * This handler is used for reading the current hung task detection count + * and for resetting it to zero when a write operation is performed using a + * zero value only. + * Return: 0 on success, or a negative error code on failure. + */ +static int proc_dohung_task_detect_count(const struct ctl_table *table, int dir, + void *buffer, size_t *lenp, loff_t *ppos) +{ + unsigned long detect_count; + struct ctl_table proxy_table; + int err; + + proxy_table = *table; + proxy_table.data = &detect_count; + + if (SYSCTL_KERN_TO_USER(dir)) + detect_count = atomic_long_read(&sysctl_hung_task_detect_count); + + err = proc_doulongvec_minmax(&proxy_table, dir, buffer, lenp, ppos); + if (err < 0) + return err; + + if (SYSCTL_USER_TO_KERN(dir)) { + if (detect_count) + return -EINVAL; + atomic_long_set(&sysctl_hung_task_detect_count, 0); + } + + return 0; +} + /* * Process updating of timeout sysctl */ @@ -438,10 +485,9 @@ static const struct ctl_table hung_task_sysctls[] = { }, { .procname = "hung_task_detect_count", - .data = &sysctl_hung_task_detect_count, .maxlen = sizeof(unsigned long), - .mode = 0444, - .proc_handler = proc_doulongvec_minmax, + .mode = 0644, + .proc_handler = proc_dohung_task_detect_count, }, { .procname = "hung_task_sys_info", diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 6c9b1dc4e7d4..b635e3c5d5b6 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -893,7 +894,10 @@ void handle_percpu_irq(struct irq_desc *desc) * * action->percpu_dev_id is a pointer to percpu variables which * contain the real device id for the cpu on which this handler is - * called + * called. + * + * May be used for NMI interrupt lines, and so may be called in IRQ or NMI + * context. */ void handle_percpu_devid_irq(struct irq_desc *desc) { @@ -930,7 +934,8 @@ void handle_percpu_devid_irq(struct irq_desc *desc) enabled ? " and unmasked" : "", irq, cpu); } - add_interrupt_randomness(irq); + if (!in_nmi()) + add_interrupt_randomness(irq); if (chip->irq_eoi) chip->irq_eoi(&desc->irq_data); diff --git a/kernel/irq_work.c b/kernel/irq_work.c index 73f7e1fd4ab4..f7e2dc2c30c6 100644 --- a/kernel/irq_work.c +++ b/kernel/irq_work.c @@ -79,7 +79,7 @@ void __weak arch_irq_work_raise(void) static __always_inline void irq_work_raise(struct irq_work *work) { if (trace_ipi_send_cpu_enabled() && arch_irq_work_has_interrupt()) - trace_ipi_send_cpu(smp_processor_id(), _RET_IP_, work->func); + trace_call__ipi_send_cpu(smp_processor_id(), _RET_IP_, work->func); arch_irq_work_raise(); } @@ -292,6 +292,12 @@ void irq_work_sync(struct irq_work *work) !arch_irq_work_has_interrupt()) { rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work), TASK_UNINTERRUPTIBLE); + /* + * Ensure irq_work_single() does not access @work + * after removing IRQ_WORK_BUSY. It is always + * accessed within a RCU-read section. + */ + synchronize_rcu(); return; } @@ -302,6 +308,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync); static void run_irq_workd(unsigned int cpu) { + guard(rcu)(); irq_work_run_list(this_cpu_ptr(&lazy_list)); } diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c index 2fea396d29b9..a43d2da0fe3e 100644 --- a/kernel/kexec_core.c +++ b/kernel/kexec_core.c @@ -47,7 +47,6 @@ #include #include -#include #include "kexec_internal.h" atomic_t __kexec_lock = ATOMIC_INIT(0); diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index 532f455c5d4f..2592f7ca16e2 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -18,7 +18,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -724,12 +726,13 @@ err_disable_kho: } /** - * kho_add_subtree - record the physical address of a sub FDT in KHO root tree. + * kho_add_subtree - record the physical address of a sub blob in KHO root tree. * @name: name of the sub tree. - * @fdt: the sub tree blob. + * @blob: the sub tree blob. + * @size: size of the blob in bytes. * * Creates a new child node named @name in KHO root FDT and records - * the physical address of @fdt. The pages of @fdt must also be preserved + * the physical address of @blob. The pages of @blob must also be preserved * by KHO for the new kernel to retrieve it after kexec. * * A debugfs blob entry is also created at @@ -738,10 +741,11 @@ err_disable_kho: * * Return: 0 on success, error code on failure */ -int kho_add_subtree(const char *name, void *fdt) +int kho_add_subtree(const char *name, void *blob, size_t size) { - phys_addr_t phys = virt_to_phys(fdt); + phys_addr_t phys = virt_to_phys(blob); void *root_fdt = kho_out.fdt; + u64 size_u64 = size; int err = -ENOMEM; int off, fdt_err; @@ -758,13 +762,24 @@ int kho_add_subtree(const char *name, void *fdt) goto out_pack; } - err = fdt_setprop(root_fdt, off, KHO_FDT_SUB_TREE_PROP_NAME, - &phys, sizeof(phys)); - if (err < 0) - goto out_pack; + fdt_err = fdt_setprop(root_fdt, off, KHO_SUB_TREE_PROP_NAME, + &phys, sizeof(phys)); + if (fdt_err < 0) + goto out_del_node; - WARN_ON_ONCE(kho_debugfs_fdt_add(&kho_out.dbg, name, fdt, false)); + fdt_err = fdt_setprop(root_fdt, off, KHO_SUB_TREE_SIZE_PROP_NAME, + &size_u64, sizeof(size_u64)); + if (fdt_err < 0) + goto out_del_node; + WARN_ON_ONCE(kho_debugfs_blob_add(&kho_out.dbg, name, blob, + size, false)); + + err = 0; + goto out_pack; + +out_del_node: + fdt_del_node(root_fdt, off); out_pack: fdt_pack(root_fdt); @@ -772,9 +787,9 @@ out_pack: } EXPORT_SYMBOL_GPL(kho_add_subtree); -void kho_remove_subtree(void *fdt) +void kho_remove_subtree(void *blob) { - phys_addr_t target_phys = virt_to_phys(fdt); + phys_addr_t target_phys = virt_to_phys(blob); void *root_fdt = kho_out.fdt; int off; int err; @@ -790,13 +805,13 @@ void kho_remove_subtree(void *fdt) const u64 *val; int len; - val = fdt_getprop(root_fdt, off, KHO_FDT_SUB_TREE_PROP_NAME, &len); + val = fdt_getprop(root_fdt, off, KHO_SUB_TREE_PROP_NAME, &len); if (!val || len != sizeof(phys_addr_t)) continue; if ((phys_addr_t)*val == target_phys) { fdt_del_node(root_fdt, off); - kho_debugfs_fdt_remove(&kho_out.dbg, fdt); + kho_debugfs_blob_remove(&kho_out.dbg, blob); break; } } @@ -1260,6 +1275,8 @@ EXPORT_SYMBOL_GPL(kho_restore_free); struct kho_in { phys_addr_t fdt_phys; phys_addr_t scratch_phys; + char previous_release[__NEW_UTS_LEN + 1]; + u32 kexec_count; struct kho_debugfs dbg; }; @@ -1292,16 +1309,17 @@ bool is_kho_boot(void) EXPORT_SYMBOL_GPL(is_kho_boot); /** - * kho_retrieve_subtree - retrieve a preserved sub FDT by its name. - * @name: the name of the sub FDT passed to kho_add_subtree(). - * @phys: if found, the physical address of the sub FDT is stored in @phys. + * kho_retrieve_subtree - retrieve a preserved sub blob by its name. + * @name: the name of the sub blob passed to kho_add_subtree(). + * @phys: if found, the physical address of the sub blob is stored in @phys. + * @size: if not NULL and found, the size of the sub blob is stored in @size. * - * Retrieve a preserved sub FDT named @name and store its physical - * address in @phys. + * Retrieve a preserved sub blob named @name and store its physical + * address in @phys and optionally its size in @size. * * Return: 0 on success, error code on failure */ -int kho_retrieve_subtree(const char *name, phys_addr_t *phys) +int kho_retrieve_subtree(const char *name, phys_addr_t *phys, size_t *size) { const void *fdt = kho_get_fdt(); const u64 *val; @@ -1317,12 +1335,22 @@ int kho_retrieve_subtree(const char *name, phys_addr_t *phys) if (offset < 0) return -ENOENT; - val = fdt_getprop(fdt, offset, KHO_FDT_SUB_TREE_PROP_NAME, &len); + val = fdt_getprop(fdt, offset, KHO_SUB_TREE_PROP_NAME, &len); if (!val || len != sizeof(*val)) return -EINVAL; *phys = (phys_addr_t)*val; + val = fdt_getprop(fdt, offset, KHO_SUB_TREE_SIZE_PROP_NAME, &len); + if (!val || len != sizeof(*val)) { + pr_warn("broken KHO subnode '%s': missing or invalid blob-size property\n", + name); + return -EINVAL; + } + + if (size) + *size = (size_t)*val; + return 0; } EXPORT_SYMBOL_GPL(kho_retrieve_subtree); @@ -1373,6 +1401,96 @@ static __init int kho_out_fdt_setup(void) return err; } +static void __init kho_in_kexec_metadata(void) +{ + struct kho_kexec_metadata *metadata; + phys_addr_t metadata_phys; + size_t blob_size; + int err; + + err = kho_retrieve_subtree(KHO_METADATA_NODE_NAME, &metadata_phys, + &blob_size); + if (err) + /* This is fine, previous kernel didn't export metadata */ + return; + + /* Check that, at least, "version" is present */ + if (blob_size < sizeof(u32)) { + pr_warn("kexec-metadata blob too small (%zu bytes)\n", + blob_size); + return; + } + + metadata = phys_to_virt(metadata_phys); + + if (metadata->version != KHO_KEXEC_METADATA_VERSION) { + pr_warn("kexec-metadata version %u not supported (expected %u)\n", + metadata->version, KHO_KEXEC_METADATA_VERSION); + return; + } + + if (blob_size < sizeof(*metadata)) { + pr_warn("kexec-metadata blob too small for v%u (%zu < %zu)\n", + metadata->version, blob_size, sizeof(*metadata)); + return; + } + + /* + * Copy data to the kernel structure that will persist during + * kernel lifetime. + */ + kho_in.kexec_count = metadata->kexec_count; + strscpy(kho_in.previous_release, metadata->previous_release, + sizeof(kho_in.previous_release)); + + pr_info("exec from: %s (count %u)\n", + kho_in.previous_release, kho_in.kexec_count); +} + +/* + * Create kexec metadata to pass kernel version and boot count to the + * next kernel. This keeps the core KHO ABI minimal and allows the + * metadata format to evolve independently. + */ +static __init int kho_out_kexec_metadata(void) +{ + struct kho_kexec_metadata *metadata; + int err; + + metadata = kho_alloc_preserve(sizeof(*metadata)); + if (IS_ERR(metadata)) + return PTR_ERR(metadata); + + metadata->version = KHO_KEXEC_METADATA_VERSION; + strscpy(metadata->previous_release, init_uts_ns.name.release, + sizeof(metadata->previous_release)); + /* kho_in.kexec_count is set to 0 on cold boot */ + metadata->kexec_count = kho_in.kexec_count + 1; + + err = kho_add_subtree(KHO_METADATA_NODE_NAME, metadata, + sizeof(*metadata)); + if (err) + kho_unpreserve_free(metadata); + + return err; +} + +static int __init kho_kexec_metadata_init(const void *fdt) +{ + int err; + + if (fdt) + kho_in_kexec_metadata(); + + /* Populate kexec metadata for the possible next kexec */ + err = kho_out_kexec_metadata(); + if (err) + pr_warn("failed to initialize kexec-metadata subtree: %d\n", + err); + + return err; +} + static __init int kho_init(void) { struct kho_radix_tree *tree = &kho_out.radix_tree; @@ -1406,6 +1524,10 @@ static __init int kho_init(void) if (err) goto err_free_fdt; + err = kho_kexec_metadata_init(fdt); + if (err) + goto err_free_fdt; + if (fdt) { kho_in_debugfs_init(&kho_in.dbg, fdt); return 0; @@ -1430,8 +1552,9 @@ static __init int kho_init(void) init_cma_reserved_pageblock(pfn_to_page(pfn)); } - WARN_ON_ONCE(kho_debugfs_fdt_add(&kho_out.dbg, "fdt", - kho_out.fdt, true)); + WARN_ON_ONCE(kho_debugfs_blob_add(&kho_out.dbg, "fdt", + kho_out.fdt, + fdt_totalsize(kho_out.fdt), true)); return 0; @@ -1584,7 +1707,7 @@ int kho_fill_kimage(struct kimage *image) int err = 0; struct kexec_buf scratch; - if (!kho_enable) + if (!kho_enable || image->type == KEXEC_TYPE_CRASH) return 0; image->kho.fdt = virt_to_phys(kho_out.fdt); diff --git a/kernel/liveupdate/kexec_handover_debugfs.c b/kernel/liveupdate/kexec_handover_debugfs.c index acf368222682..257ee8a52be6 100644 --- a/kernel/liveupdate/kexec_handover_debugfs.c +++ b/kernel/liveupdate/kexec_handover_debugfs.c @@ -24,8 +24,9 @@ struct fdt_debugfs { struct dentry *file; }; -static int __kho_debugfs_fdt_add(struct list_head *list, struct dentry *dir, - const char *name, const void *fdt) +static int __kho_debugfs_blob_add(struct list_head *list, struct dentry *dir, + const char *name, const void *blob, + size_t size) { struct fdt_debugfs *f; struct dentry *file; @@ -34,8 +35,8 @@ static int __kho_debugfs_fdt_add(struct list_head *list, struct dentry *dir, if (!f) return -ENOMEM; - f->wrapper.data = (void *)fdt; - f->wrapper.size = fdt_totalsize(fdt); + f->wrapper.data = (void *)blob; + f->wrapper.size = size; file = debugfs_create_blob(name, 0400, dir, &f->wrapper); if (IS_ERR(file)) { @@ -49,8 +50,8 @@ static int __kho_debugfs_fdt_add(struct list_head *list, struct dentry *dir, return 0; } -int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name, - const void *fdt, bool root) +int kho_debugfs_blob_add(struct kho_debugfs *dbg, const char *name, + const void *blob, size_t size, bool root) { struct dentry *dir; @@ -59,15 +60,15 @@ int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name, else dir = dbg->sub_fdt_dir; - return __kho_debugfs_fdt_add(&dbg->fdt_list, dir, name, fdt); + return __kho_debugfs_blob_add(&dbg->fdt_list, dir, name, blob, size); } -void kho_debugfs_fdt_remove(struct kho_debugfs *dbg, void *fdt) +void kho_debugfs_blob_remove(struct kho_debugfs *dbg, void *blob) { struct fdt_debugfs *ff; list_for_each_entry(ff, &dbg->fdt_list, list) { - if (ff->wrapper.data == fdt) { + if (ff->wrapper.data == blob) { debugfs_remove(ff->file); list_del(&ff->list); kfree(ff); @@ -113,28 +114,42 @@ __init void kho_in_debugfs_init(struct kho_debugfs *dbg, const void *fdt) goto err_rmdir; } - err = __kho_debugfs_fdt_add(&dbg->fdt_list, dir, "fdt", fdt); + err = __kho_debugfs_blob_add(&dbg->fdt_list, dir, "fdt", fdt, + fdt_totalsize(fdt)); if (err) goto err_rmdir; fdt_for_each_subnode(child, fdt, 0) { int len = 0; const char *name = fdt_get_name(fdt, child, NULL); - const u64 *fdt_phys; + const u64 *blob_phys; + const u64 *blob_size; + void *blob; - fdt_phys = fdt_getprop(fdt, child, KHO_FDT_SUB_TREE_PROP_NAME, &len); - if (!fdt_phys) + blob_phys = fdt_getprop(fdt, child, + KHO_SUB_TREE_PROP_NAME, &len); + if (!blob_phys) continue; - if (len != sizeof(*fdt_phys)) { - pr_warn("node %s prop fdt has invalid length: %d\n", - name, len); + if (len != sizeof(*blob_phys)) { + pr_warn("node %s prop %s has invalid length: %d\n", + name, KHO_SUB_TREE_PROP_NAME, len); continue; } - err = __kho_debugfs_fdt_add(&dbg->fdt_list, sub_fdt_dir, name, - phys_to_virt(*fdt_phys)); + + blob_size = fdt_getprop(fdt, child, + KHO_SUB_TREE_SIZE_PROP_NAME, &len); + if (!blob_size || len != sizeof(*blob_size)) { + pr_warn("node %s missing or invalid %s property\n", + name, KHO_SUB_TREE_SIZE_PROP_NAME); + continue; + } + + blob = phys_to_virt(*blob_phys); + err = __kho_debugfs_blob_add(&dbg->fdt_list, sub_fdt_dir, name, + blob, *blob_size); if (err) { - pr_warn("failed to add fdt %s to debugfs: %pe\n", name, - ERR_PTR(err)); + pr_warn("failed to add blob %s to debugfs: %pe\n", + name, ERR_PTR(err)); continue; } } diff --git a/kernel/liveupdate/kexec_handover_internal.h b/kernel/liveupdate/kexec_handover_internal.h index 9a832a35254c..0399ff107775 100644 --- a/kernel/liveupdate/kexec_handover_internal.h +++ b/kernel/liveupdate/kexec_handover_internal.h @@ -26,18 +26,19 @@ extern unsigned int kho_scratch_cnt; int kho_debugfs_init(void); void kho_in_debugfs_init(struct kho_debugfs *dbg, const void *fdt); int kho_out_debugfs_init(struct kho_debugfs *dbg); -int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name, - const void *fdt, bool root); -void kho_debugfs_fdt_remove(struct kho_debugfs *dbg, void *fdt); +int kho_debugfs_blob_add(struct kho_debugfs *dbg, const char *name, + const void *blob, size_t size, bool root); +void kho_debugfs_blob_remove(struct kho_debugfs *dbg, void *blob); #else static inline int kho_debugfs_init(void) { return 0; } static inline void kho_in_debugfs_init(struct kho_debugfs *dbg, const void *fdt) { } static inline int kho_out_debugfs_init(struct kho_debugfs *dbg) { return 0; } -static inline int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name, - const void *fdt, bool root) { return 0; } -static inline void kho_debugfs_fdt_remove(struct kho_debugfs *dbg, - void *fdt) { } +static inline int kho_debugfs_blob_add(struct kho_debugfs *dbg, + const char *name, const void *blob, + size_t size, bool root) { return 0; } +static inline void kho_debugfs_blob_remove(struct kho_debugfs *dbg, + void *blob) { } #endif /* CONFIG_KEXEC_HANDOVER_DEBUGFS */ #ifdef CONFIG_KEXEC_HANDOVER_DEBUG diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c index 84ac728d63ba..803f51c84275 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,11 @@ static struct { u64 liveupdate_num; } luo_global; +/* + * luo_register_rwlock - Protects registration of file handlers and FLBs. + */ +DECLARE_RWSEM(luo_register_rwlock); + static int __init early_liveupdate_param(char *buf) { return kstrtobool(buf, &luo_global.enabled); @@ -88,7 +94,7 @@ static int __init luo_early_startup(void) } /* Retrieve LUO subtree, and verify its format. */ - err = kho_retrieve_subtree(LUO_FDT_KHO_ENTRY_NAME, &fdt_phys); + err = kho_retrieve_subtree(LUO_FDT_KHO_ENTRY_NAME, &fdt_phys, NULL); if (err) { if (err != -ENOENT) { pr_err("failed to retrieve FDT '%s' from KHO: %pe\n", @@ -172,7 +178,8 @@ static int __init luo_fdt_setup(void) if (err) goto exit_free; - err = kho_add_subtree(LUO_FDT_KHO_ENTRY_NAME, fdt_out); + err = kho_add_subtree(LUO_FDT_KHO_ENTRY_NAME, fdt_out, + fdt_totalsize(fdt_out)); if (err) goto exit_free; luo_global.fdt_out = fdt_out; diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c index 5acee4174bf0..a0a419085e28 100644 --- a/kernel/liveupdate/luo_file.c +++ b/kernel/liveupdate/luo_file.c @@ -108,12 +108,16 @@ #include #include #include +#include #include #include #include "luo_internal.h" static LIST_HEAD(luo_file_handler_list); +/* Keep track of files being preserved by LUO */ +static DEFINE_XARRAY(luo_preserved_files); + /* 2 4K pages, give space for 128 files per file_set */ #define LUO_FILE_PGCNT 2ul #define LUO_FILE_MAX \ @@ -203,6 +207,12 @@ static void luo_free_files_mem(struct luo_file_set *file_set) file_set->files = NULL; } +static unsigned long luo_get_id(struct liveupdate_file_handler *fh, + struct file *file) +{ + return fh->ops->get_id ? fh->ops->get_id(file) : (unsigned long)file; +} + static bool luo_token_is_used(struct luo_file_set *file_set, u64 token) { struct luo_file *iter; @@ -248,6 +258,7 @@ static bool luo_token_is_used(struct luo_file_set *file_set, u64 token) * Context: Can be called from an ioctl handler during normal system operation. * Return: 0 on success. Returns a negative errno on failure: * -EEXIST if the token is already used. + * -EBUSY if the file descriptor is already preserved by another session. * -EBADF if the file descriptor is invalid. * -ENOSPC if the file_set is full. * -ENOENT if no compatible handler is found. @@ -277,20 +288,28 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd) goto err_fput; err = -ENOENT; + down_read(&luo_register_rwlock); list_private_for_each_entry(fh, &luo_file_handler_list, list) { if (fh->ops->can_preserve(fh, file)) { - err = 0; + if (try_module_get(fh->ops->owner)) + err = 0; break; } } + up_read(&luo_register_rwlock); /* err is still -ENOENT if no handler was found */ if (err) goto err_free_files_mem; + err = xa_insert(&luo_preserved_files, luo_get_id(fh, file), + file, GFP_KERNEL); + if (err) + goto err_module_put; + err = luo_flb_file_preserve(fh); if (err) - goto err_free_files_mem; + goto err_erase_xa; luo_file = kzalloc_obj(*luo_file); if (!luo_file) { @@ -320,6 +339,10 @@ err_kfree: kfree(luo_file); err_flb_unpreserve: luo_flb_file_unpreserve(fh); +err_erase_xa: + xa_erase(&luo_preserved_files, luo_get_id(fh, file)); +err_module_put: + module_put(fh->ops->owner); err_free_files_mem: luo_free_files_mem(file_set); err_fput: @@ -362,7 +385,10 @@ void luo_file_unpreserve_files(struct luo_file_set *file_set) args.private_data = luo_file->private_data; luo_file->fh->ops->unpreserve(&args); luo_flb_file_unpreserve(luo_file->fh); + module_put(luo_file->fh->ops->owner); + xa_erase(&luo_preserved_files, + luo_get_id(luo_file->fh, luo_file->file)); list_del(&luo_file->list); file_set->count--; @@ -606,6 +632,11 @@ int luo_retrieve_file(struct luo_file_set *file_set, u64 token, luo_file->file = args.file; /* Get reference so we can keep this file in LUO until finish */ get_file(luo_file->file); + + WARN_ON(xa_insert(&luo_preserved_files, + luo_get_id(luo_file->fh, luo_file->file), + luo_file->file, GFP_KERNEL)); + *filep = luo_file->file; luo_file->retrieve_status = 1; @@ -646,6 +677,7 @@ static void luo_file_finish_one(struct luo_file_set *file_set, luo_file->fh->ops->finish(&args); luo_flb_file_finish(luo_file->fh); + module_put(luo_file->fh->ops->owner); } /** @@ -701,8 +733,11 @@ int luo_file_finish(struct luo_file_set *file_set) luo_file_finish_one(file_set, luo_file); - if (luo_file->file) + if (luo_file->file) { + xa_erase(&luo_preserved_files, + luo_get_id(luo_file->fh, luo_file->file)); fput(luo_file->file); + } list_del(&luo_file->list); file_set->count--; mutex_destroy(&luo_file->mutex); @@ -777,22 +812,28 @@ int luo_file_deserialize(struct luo_file_set *file_set, bool handler_found = false; struct luo_file *luo_file; + down_read(&luo_register_rwlock); list_private_for_each_entry(fh, &luo_file_handler_list, list) { if (!strcmp(fh->compatible, file_ser[i].compatible)) { - handler_found = true; + if (try_module_get(fh->ops->owner)) + handler_found = true; break; } } + up_read(&luo_register_rwlock); if (!handler_found) { - pr_warn("No registered handler for compatible '%s'\n", + pr_warn("No registered handler for compatible '%.*s'\n", + (int)sizeof(file_ser[i].compatible), file_ser[i].compatible); return -ENOENT; } luo_file = kzalloc_obj(*luo_file); - if (!luo_file) + if (!luo_file) { + module_put(fh->ops->owner); return -ENOMEM; + } luo_file->fh = fh; luo_file->file = NULL; @@ -842,41 +883,28 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh) return -EINVAL; } - /* - * Ensure the system is quiescent (no active sessions). - * This prevents registering new handlers while sessions are active or - * while deserialization is in progress. - */ - if (!luo_session_quiesce()) - return -EBUSY; - + down_write(&luo_register_rwlock); /* Check for duplicate compatible strings */ list_private_for_each_entry(fh_iter, &luo_file_handler_list, list) { if (!strcmp(fh_iter->compatible, fh->compatible)) { pr_err("File handler registration failed: Compatible string '%s' already registered.\n", fh->compatible); err = -EEXIST; - goto err_resume; + goto err_unlock; } } - /* Pin the module implementing the handler */ - if (!try_module_get(fh->ops->owner)) { - err = -EAGAIN; - goto err_resume; - } - INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, flb_list)); INIT_LIST_HEAD(&ACCESS_PRIVATE(fh, list)); list_add_tail(&ACCESS_PRIVATE(fh, list), &luo_file_handler_list); - luo_session_resume(); + up_write(&luo_register_rwlock); liveupdate_test_register(fh); return 0; -err_resume: - luo_session_resume(); +err_unlock: + up_write(&luo_register_rwlock); return err; } @@ -886,41 +914,13 @@ err_resume: * * Unregisters the file handler from the liveupdate core. This function * reverses the operations of liveupdate_register_file_handler(). - * - * It ensures safe removal by checking that: - * No live update session is currently in progress. - * No FLB registered with this file handler. - * - * If the unregistration fails, the internal test state is reverted. - * - * Return: 0 Success. -EOPNOTSUPP when live update is not enabled. -EBUSY A live - * update is in progress, can't quiesce live update or FLB is registred with - * this file handler. */ -int liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh) +void liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh) { - int err = -EBUSY; - if (!liveupdate_enabled()) - return -EOPNOTSUPP; - - liveupdate_test_unregister(fh); - - if (!luo_session_quiesce()) - goto err_register; - - if (!list_empty(&ACCESS_PRIVATE(fh, flb_list))) - goto err_resume; + return; + guard(rwsem_write)(&luo_register_rwlock); + luo_flb_unregister_all(fh); list_del(&ACCESS_PRIVATE(fh, list)); - module_put(fh->ops->owner); - luo_session_resume(); - - return 0; - -err_resume: - luo_session_resume(); -err_register: - liveupdate_test_register(fh); - return err; } diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c index f52e8114837e..00f5494812c4 100644 --- a/kernel/liveupdate/luo_flb.c +++ b/kernel/liveupdate/luo_flb.c @@ -89,13 +89,18 @@ struct luo_flb_link { static struct luo_flb_private *luo_flb_get_private(struct liveupdate_flb *flb) { struct luo_flb_private *private = &ACCESS_PRIVATE(flb, private); + static DEFINE_SPINLOCK(luo_flb_init_lock); + if (smp_load_acquire(&private->initialized)) + return private; + + guard(spinlock)(&luo_flb_init_lock); if (!private->initialized) { mutex_init(&private->incoming.lock); mutex_init(&private->outgoing.lock); INIT_LIST_HEAD(&private->list); private->users = 0; - private->initialized = true; + smp_store_release(&private->initialized, true); } return private; @@ -110,10 +115,15 @@ static int luo_flb_file_preserve_one(struct liveupdate_flb *flb) struct liveupdate_flb_op_args args = {0}; int err; + if (!try_module_get(flb->ops->owner)) + return -ENODEV; + args.flb = flb; err = flb->ops->preserve(&args); - if (err) + if (err) { + module_put(flb->ops->owner); return err; + } private->outgoing.data = args.data; private->outgoing.obj = args.obj; } @@ -141,6 +151,7 @@ static void luo_flb_file_unpreserve_one(struct liveupdate_flb *flb) private->outgoing.data = 0; private->outgoing.obj = NULL; + module_put(flb->ops->owner); } } } @@ -176,12 +187,17 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb) if (!found) return -ENOENT; + if (!try_module_get(flb->ops->owner)) + return -ENODEV; + args.flb = flb; args.data = private->incoming.data; err = flb->ops->retrieve(&args); - if (err) + if (err) { + module_put(flb->ops->owner); return err; + } private->incoming.obj = args.obj; private->incoming.retrieved = true; @@ -215,6 +231,7 @@ static void luo_flb_file_finish_one(struct liveupdate_flb *flb) private->incoming.data = 0; private->incoming.obj = NULL; private->incoming.finished = true; + module_put(flb->ops->owner); } } } @@ -240,17 +257,20 @@ int luo_flb_file_preserve(struct liveupdate_file_handler *fh) struct luo_flb_link *iter; int err = 0; + down_read(&luo_register_rwlock); list_for_each_entry(iter, flb_list, list) { err = luo_flb_file_preserve_one(iter->flb); if (err) goto exit_err; } + up_read(&luo_register_rwlock); return 0; exit_err: list_for_each_entry_continue_reverse(iter, flb_list, list) luo_flb_file_unpreserve_one(iter->flb); + up_read(&luo_register_rwlock); return err; } @@ -272,6 +292,7 @@ void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh) struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list); struct luo_flb_link *iter; + guard(rwsem_read)(&luo_register_rwlock); list_for_each_entry_reverse(iter, flb_list, list) luo_flb_file_unpreserve_one(iter->flb); } @@ -292,10 +313,67 @@ void luo_flb_file_finish(struct liveupdate_file_handler *fh) struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list); struct luo_flb_link *iter; + guard(rwsem_read)(&luo_register_rwlock); list_for_each_entry_reverse(iter, flb_list, list) luo_flb_file_finish_one(iter->flb); } +static void luo_flb_unregister_one(struct liveupdate_file_handler *fh, + struct liveupdate_flb *flb) +{ + struct luo_flb_private *private = luo_flb_get_private(flb); + struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list); + struct luo_flb_link *iter; + bool found = false; + + /* Find and remove the link from the file handler's list */ + list_for_each_entry(iter, flb_list, list) { + if (iter->flb == flb) { + list_del(&iter->list); + kfree(iter); + found = true; + break; + } + } + + if (!found) { + pr_warn("Failed to unregister FLB '%s': not found in file handler '%s'\n", + flb->compatible, fh->compatible); + return; + } + + private->users--; + + /* + * If this is the last file-handler with which we are registred, remove + * from the global list. + */ + if (!private->users) { + list_del_init(&private->list); + luo_flb_global.count--; + } +} + +/** + * luo_flb_unregister_all - Unregister all FLBs associated with a file handler. + * @fh: The file handler whose FLBs should be unregistered. + * + * This function iterates through the list of FLBs associated with the given + * file handler and unregisters them all one by one. + */ +void luo_flb_unregister_all(struct liveupdate_file_handler *fh) +{ + struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list); + struct luo_flb_link *iter, *tmp; + + if (!liveupdate_enabled()) + return; + + lockdep_assert_held_write(&luo_register_rwlock); + list_for_each_entry_safe(iter, tmp, flb_list, list) + luo_flb_unregister_one(fh, iter->flb); +} + /** * liveupdate_register_flb - Associate an FLB with a file handler and register it globally. * @fh: The file handler that will now depend on the FLB. @@ -326,7 +404,6 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh, struct luo_flb_link *link __free(kfree) = NULL; struct liveupdate_flb *gflb; struct luo_flb_link *iter; - int err; if (!liveupdate_enabled()) return -EOPNOTSUPP; @@ -347,19 +424,12 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh, if (!link) return -ENOMEM; - /* - * Ensure the system is quiescent (no active sessions). - * This acts as a global lock for registration: no other thread can - * be in this section, and no sessions can be creating/using FDs. - */ - if (!luo_session_quiesce()) - return -EBUSY; + guard(rwsem_write)(&luo_register_rwlock); /* Check that this FLB is not already linked to this file handler */ - err = -EEXIST; list_for_each_entry(iter, flb_list, list) { if (iter->flb == flb) - goto err_resume; + return -EEXIST; } /* @@ -367,25 +437,16 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh, * is registered */ if (!private->users) { - if (WARN_ON(!list_empty(&private->list))) { - err = -EINVAL; - goto err_resume; - } + if (WARN_ON(!list_empty(&private->list))) + return -EINVAL; - if (luo_flb_global.count == LUO_FLB_MAX) { - err = -ENOSPC; - goto err_resume; - } + if (luo_flb_global.count == LUO_FLB_MAX) + return -ENOSPC; /* Check that compatible string is unique in global list */ list_private_for_each_entry(gflb, &luo_flb_global.list, private.list) { if (!strcmp(gflb->compatible, flb->compatible)) - goto err_resume; - } - - if (!try_module_get(flb->ops->owner)) { - err = -EAGAIN; - goto err_resume; + return -EEXIST; } list_add_tail(&private->list, &luo_flb_global.list); @@ -396,13 +457,8 @@ int liveupdate_register_flb(struct liveupdate_file_handler *fh, private->users++; link->flb = flb; list_add_tail(&no_free_ptr(link)->list, flb_list); - luo_session_resume(); return 0; - -err_resume: - luo_session_resume(); - return err; } /** @@ -418,63 +474,17 @@ err_resume: * the FLB is removed from the global registry and the reference to its * owner module (acquired during registration) is released. * - * Context: This function ensures the session is quiesced (no active FDs - * being created) during the update. It is typically called from a - * subsystem's module exit function. - * Return: 0 on success. - * -EOPNOTSUPP if live update is disabled. - * -EBUSY if the live update session is active and cannot be quiesced. - * -ENOENT if the FLB was not found in the file handler's list. + * Context: It is typically called from a subsystem's module exit function. */ -int liveupdate_unregister_flb(struct liveupdate_file_handler *fh, - struct liveupdate_flb *flb) +void liveupdate_unregister_flb(struct liveupdate_file_handler *fh, + struct liveupdate_flb *flb) { - struct luo_flb_private *private = luo_flb_get_private(flb); - struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list); - struct luo_flb_link *iter; - int err = -ENOENT; - if (!liveupdate_enabled()) - return -EOPNOTSUPP; + return; - /* - * Ensure the system is quiescent (no active sessions). - * This acts as a global lock for unregistration. - */ - if (!luo_session_quiesce()) - return -EBUSY; + guard(rwsem_write)(&luo_register_rwlock); - /* Find and remove the link from the file handler's list */ - list_for_each_entry(iter, flb_list, list) { - if (iter->flb == flb) { - list_del(&iter->list); - kfree(iter); - err = 0; - break; - } - } - - if (err) - goto err_resume; - - private->users--; - /* - * If this is the last file-handler with which we are registred, remove - * from the global list, and relese module reference. - */ - if (!private->users) { - list_del_init(&private->list); - luo_flb_global.count--; - module_put(flb->ops->owner); - } - - luo_session_resume(); - - return 0; - -err_resume: - luo_session_resume(); - return err; + luo_flb_unregister_one(fh, flb); } /** @@ -492,7 +502,8 @@ err_resume: * * Return: 0 on success, or a negative errno on failure. -ENODATA means no * incoming FLB data, -ENOENT means specific flb not found in the incoming - * data, and -EOPNOTSUPP when live update is disabled or not configured. + * data, -ENODEV if the FLB's module is unloading, and -EOPNOTSUPP when + * live update is disabled or not configured. */ int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp) { @@ -638,6 +649,7 @@ void luo_flb_serialize(void) struct liveupdate_flb *gflb; int i = 0; + guard(rwsem_read)(&luo_register_rwlock); list_private_for_each_entry(gflb, &luo_flb_global.list, private.list) { struct luo_flb_private *private = luo_flb_get_private(gflb); diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h index 8083d8739b09..875844d7a41d 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -77,14 +77,14 @@ struct luo_session { struct mutex mutex; }; +extern struct rw_semaphore luo_register_rwlock; + int luo_session_create(const char *name, struct file **filep); int luo_session_retrieve(const char *name, struct file **filep); int __init luo_session_setup_outgoing(void *fdt); int __init luo_session_setup_incoming(void *fdt); int luo_session_serialize(void); int luo_session_deserialize(void); -bool luo_session_quiesce(void); -void luo_session_resume(void); int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd); void luo_file_unpreserve_files(struct luo_file_set *file_set); @@ -103,16 +103,15 @@ void luo_file_set_destroy(struct luo_file_set *file_set); int luo_flb_file_preserve(struct liveupdate_file_handler *fh); void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh); void luo_flb_file_finish(struct liveupdate_file_handler *fh); +void luo_flb_unregister_all(struct liveupdate_file_handler *fh); int __init luo_flb_setup_outgoing(void *fdt); int __init luo_flb_setup_incoming(void *fdt); void luo_flb_serialize(void); #ifdef CONFIG_LIVEUPDATE_TEST void liveupdate_test_register(struct liveupdate_file_handler *fh); -void liveupdate_test_unregister(struct liveupdate_file_handler *fh); #else static inline void liveupdate_test_register(struct liveupdate_file_handler *fh) { } -static inline void liveupdate_test_unregister(struct liveupdate_file_handler *fh) { } #endif #endif /* _LINUX_LUO_INTERNAL_H */ diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 25ae704d7787..7a42385dabe2 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -514,11 +514,12 @@ int luo_session_deserialize(void) { struct luo_session_header *sh = &luo_session_global.incoming; static bool is_deserialized; - static int err; + static int saved_err; + int err; /* If has been deserialized, always return the same error code */ if (is_deserialized) - return err; + return saved_err; is_deserialized = true; if (!sh->active) @@ -544,9 +545,11 @@ int luo_session_deserialize(void) session = luo_session_alloc(sh->ser[i].name); if (IS_ERR(session)) { - pr_warn("Failed to allocate session [%s] during deserialization %pe\n", + pr_warn("Failed to allocate session [%.*s] during deserialization %pe\n", + (int)sizeof(sh->ser[i].name), sh->ser[i].name, session); - return PTR_ERR(session); + err = PTR_ERR(session); + goto save_err; } err = luo_session_insert(sh, session); @@ -554,7 +557,7 @@ int luo_session_deserialize(void) pr_warn("Failed to insert session [%s] %pe\n", session->name, ERR_PTR(err)); luo_session_free(session); - return err; + goto save_err; } scoped_guard(mutex, &session->mutex) { @@ -564,7 +567,7 @@ int luo_session_deserialize(void) if (err) { pr_warn("Failed to deserialize files for session [%s] %pe\n", session->name, ERR_PTR(err)); - return err; + goto save_err; } } @@ -573,6 +576,9 @@ int luo_session_deserialize(void) sh->ser = NULL; return 0; +save_err: + saved_err = err; + return err; } int luo_session_serialize(void) @@ -606,46 +612,3 @@ err_undo: return err; } -/** - * luo_session_quiesce - Ensure no active sessions exist and lock session lists. - * - * Acquires exclusive write locks on both incoming and outgoing session lists. - * It then validates no sessions exist in either list. - * - * This mechanism is used during file handler un/registration to ensure that no - * sessions are currently using the handler, and no new sessions can be created - * while un/registration is in progress. - * - * This prevents registering new handlers while sessions are active or - * while deserialization is in progress. - * - * Return: - * true - System is quiescent (0 sessions) and locked. - * false - Active sessions exist. The locks are released internally. - */ -bool luo_session_quiesce(void) -{ - down_write(&luo_session_global.incoming.rwsem); - down_write(&luo_session_global.outgoing.rwsem); - - if (luo_session_global.incoming.count || - luo_session_global.outgoing.count) { - up_write(&luo_session_global.outgoing.rwsem); - up_write(&luo_session_global.incoming.rwsem); - return false; - } - - return true; -} - -/** - * luo_session_resume - Unlock session lists and resume normal activity. - * - * Releases the exclusive locks acquired by a successful call to - * luo_session_quiesce(). - */ -void luo_session_resume(void) -{ - up_write(&luo_session_global.outgoing.rwsem); - up_write(&luo_session_global.incoming.rwsem); -} diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c index 186b463fe326..09534628dc01 100644 --- a/kernel/locking/mutex.c +++ b/kernel/locking/mutex.c @@ -198,27 +198,43 @@ static inline void __mutex_clear_flag(struct mutex *lock, unsigned long flag) } /* - * Add @waiter to a given location in the lock wait_list and set the - * FLAG_WAITERS flag if it's the first waiter. + * Add @waiter to the @lock wait_list and set the FLAG_WAITERS flag if it's + * the first waiter. + * + * When @pos, @waiter is added before the waiter indicated by @pos. Otherwise + * @waiter will be added to the tail of the list. */ static void __mutex_add_waiter(struct mutex *lock, struct mutex_waiter *waiter, - struct mutex_waiter *first) + struct mutex_waiter *pos) __must_hold(&lock->wait_lock) { + struct mutex_waiter *first = lock->first_waiter; + hung_task_set_blocker(lock, BLOCKER_TYPE_MUTEX); debug_mutex_add_waiter(lock, waiter, current); - if (!first) - first = lock->first_waiter; + if (pos) { + /* + * Insert @waiter before @pos. + */ + list_add_tail(&waiter->list, &pos->list); + /* + * If @pos == @first, then @waiter will be the new first. + */ + if (pos == first) + lock->first_waiter = waiter; + return; + } if (first) { list_add_tail(&waiter->list, &first->list); - } else { - INIT_LIST_HEAD(&waiter->list); - lock->first_waiter = waiter; - __mutex_set_flag(lock, MUTEX_FLAG_WAITERS); + return; } + + INIT_LIST_HEAD(&waiter->list); + lock->first_waiter = waiter; + __mutex_set_flag(lock, MUTEX_FLAG_WAITERS); } static void @@ -229,10 +245,8 @@ __mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter) __mutex_clear_flag(lock, MUTEX_FLAGS); lock->first_waiter = NULL; } else { - if (lock->first_waiter == waiter) { - lock->first_waiter = list_first_entry(&waiter->list, - struct mutex_waiter, list); - } + if (lock->first_waiter == waiter) + lock->first_waiter = list_next_entry(waiter, list); list_del(&waiter->list); } diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c index ccaba6148b61..4f386ea6c792 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c @@ -1544,6 +1544,8 @@ static bool rtmutex_spin_on_owner(struct rt_mutex_base *lock, * * Must be called with lock->wait_lock held and interrupts disabled. It must * have just failed to try_to_take_rt_mutex(). + * + * When invoked from rt_mutex_start_proxy_lock() waiter::task != current ! */ static void __sched remove_waiter(struct rt_mutex_base *lock, struct rt_mutex_waiter *waiter) @@ -1551,14 +1553,15 @@ static void __sched remove_waiter(struct rt_mutex_base *lock, { bool is_top_waiter = (waiter == rt_mutex_top_waiter(lock)); struct task_struct *owner = rt_mutex_owner(lock); + struct task_struct *waiter_task = waiter->task; struct rt_mutex_base *next_lock; lockdep_assert_held(&lock->wait_lock); - raw_spin_lock(¤t->pi_lock); - rt_mutex_dequeue(lock, waiter); - current->pi_blocked_on = NULL; - raw_spin_unlock(¤t->pi_lock); + scoped_guard(raw_spinlock, &waiter_task->pi_lock) { + rt_mutex_dequeue(lock, waiter); + waiter_task->pi_blocked_on = NULL; + } /* * Only update priority if the waiter was the highest priority @@ -1594,7 +1597,7 @@ static void __sched remove_waiter(struct rt_mutex_base *lock, raw_spin_unlock_irq(&lock->wait_lock); rt_mutex_adjust_prio_chain(owner, RT_MUTEX_MIN_CHAINWALK, lock, - next_lock, NULL, current); + next_lock, NULL, waiter_task); raw_spin_lock_irq(&lock->wait_lock); } diff --git a/kernel/locking/ww_mutex.h b/kernel/locking/ww_mutex.h index 016f0db892a5..6c12452097e1 100644 --- a/kernel/locking/ww_mutex.h +++ b/kernel/locking/ww_mutex.h @@ -6,6 +6,19 @@ #define MUTEX_WAITER mutex_waiter #define WAIT_LOCK wait_lock +/* + * +--------+ + * | first | + * +--------+ + * | + * v + * +----+ +----+ +----+ + * | W3 | <-> | W1 | <-> | W2 | + * +----+ +----+ +----+ + * ^ ^ + * +---------------------+ + */ + static inline struct mutex_waiter * __ww_waiter_first(struct mutex *lock) __must_hold(&lock->wait_lock) @@ -13,26 +26,43 @@ __ww_waiter_first(struct mutex *lock) return lock->first_waiter; } +/* + * for (cur = __ww_waiter_first(); cur; cur = __ww_waiter_next()) + * + * Should iterate like: W1, W2, W3 + */ static inline struct mutex_waiter * __ww_waiter_next(struct mutex *lock, struct mutex_waiter *w) __must_hold(&lock->wait_lock) { w = list_next_entry(w, list); + /* + * Terminate if the next entry is the first again, that has already + * been observed. + */ if (lock->first_waiter == w) return NULL; return w; } +/* + * for (cur = __ww_waiter_last(); cur; cur = __ww_waiter_prev()) + * + * Should iterate like: W3, W2, W1 + */ static inline struct mutex_waiter * __ww_waiter_prev(struct mutex *lock, struct mutex_waiter *w) __must_hold(&lock->wait_lock) { - w = list_prev_entry(w, list); + /* + * Terminate at the first entry, the previous entry of first is the + * last and that has already been observed. + */ if (lock->first_waiter == w) return NULL; - return w; + return list_prev_entry(w, list); } static inline struct mutex_waiter * diff --git a/kernel/panic.c b/kernel/panic.c index c78600212b6c..20feada5319d 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -801,6 +801,8 @@ EXPORT_SYMBOL(panic); * Documentation/admin-guide/tainted-kernels.rst, including its * small shell script that prints the TAINT_FLAGS_COUNT bits of * /proc/sys/kernel/tainted. + * + * Also, update INIT_TAINT_BUF_MAX below. */ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = { TAINT_FLAG(PROPRIETARY_MODULE, 'P', 'G'), @@ -854,15 +856,54 @@ static void print_tainted_seq(struct seq_buf *s, bool verbose) } } +/* The initial buffer can accommodate all taint flags in verbose + * mode, with some headroom. Once the allocator is available, the + * exact size is allocated dynamically; the initial buffer remains + * as a fallback if allocation fails. + * + * The verbose taint string currently requires up to 327 characters. + */ +#define INIT_TAINT_BUF_MAX 350 + +static char init_taint_buf[INIT_TAINT_BUF_MAX] __initdata; +static char *taint_buf __refdata = init_taint_buf; +static size_t taint_buf_size = INIT_TAINT_BUF_MAX; + +static __init int alloc_taint_buf(void) +{ + int i; + char *buf; + size_t size = 0; + + size += sizeof("Tainted: ") - 1; + for (i = 0; i < TAINT_FLAGS_COUNT; i++) { + size += 2; /* For ", " */ + size += 4; /* For "[%c]=" */ + size += strlen(taint_flags[i].desc); + } + + size += 1; /* For NULL terminator */ + + buf = kmalloc(size, GFP_KERNEL); + + if (!buf) { + panic("Failed to allocate taint string buffer"); + } + + taint_buf = buf; + taint_buf_size = size; + + return 0; +} +postcore_initcall(alloc_taint_buf); + static const char *_print_tainted(bool verbose) { - /* FIXME: what should the size be? */ - static char buf[sizeof(taint_flags)]; struct seq_buf s; BUILD_BUG_ON(ARRAY_SIZE(taint_flags) != TAINT_FLAGS_COUNT); - seq_buf_init(&s, buf, sizeof(buf)); + seq_buf_init(&s, taint_buf, taint_buf_size); print_tainted_seq(&s, verbose); diff --git a/kernel/pid.c b/kernel/pid.c index 677c84e319dd..fd5c2d4aa349 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -131,9 +131,8 @@ void free_pid(struct pid *pid) wake_up_process(READ_ONCE(ns->child_reaper)); break; case PIDNS_ADDING: - /* Handle a fork failure of the first process */ - WARN_ON(ns->child_reaper); - ns->pid_allocated = 0; + /* Only possible if the 1st fork fails */ + WARN_ON(READ_ONCE(ns->child_reaper)); break; } @@ -230,6 +229,10 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid, retried_preload = false; idr_preload(GFP_KERNEL); spin_lock(&pidmap_lock); + /* For the case when the previous attempt to create init failed */ + if (ns->pid_allocated == PIDNS_ADDING) + idr_set_cursor(&ns->idr, 0); + for (tmp = ns, i = ns->level; i >= 0;) { int tid = set_tid[ns->level - i]; @@ -314,6 +317,11 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid, * * This can't be done earlier because we need to preserve other * error conditions. + * + * We need this even if copy_process() does the same check. If two + * or more tasks from parent namespace try to inject a child into a + * dead namespace, one of free_pid() calls from the copy_process() + * error path may try to wakeup the possibly freed ns->child_reaper. */ retval = -ENOMEM; if (unlikely(!(ns->pid_allocated & PIDNS_ADDING))) @@ -341,10 +349,6 @@ out_free: idr_remove(&upid->ns->idr, upid->nr); } - /* On failure to allocate the first pid, reset the state */ - if (ns->pid_allocated == PIDNS_ADDING) - idr_set_cursor(&ns->idr, 0); - spin_unlock(&pidmap_lock); idr_preload_end(); diff --git a/kernel/printk/printk_ringbuffer.c b/kernel/printk/printk_ringbuffer.c index 56c8e3d031f4..85c0c854b3ce 100644 --- a/kernel/printk/printk_ringbuffer.c +++ b/kernel/printk/printk_ringbuffer.c @@ -14,7 +14,7 @@ * * Data Structure * -------------- - * The printk_ringbuffer is made up of 3 internal ringbuffers: + * The printk_ringbuffer is made up of 2 internal ringbuffers: * * desc_ring * A ring of descriptors and their meta data (such as sequence number, @@ -224,7 +224,7 @@ * * prb_rec_init_rd(&r, &info, &text_buf[0], sizeof(text_buf)); * - * prb_for_each_record(0, &test_rb, &seq, &r) { + * prb_for_each_record(0, &test_rb, seq, &r) { * if (info.seq != seq) * pr_warn("lost %llu records\n", info.seq - seq); * @@ -1302,23 +1302,26 @@ static const char *get_data(struct prb_data_ring *data_ring, return NULL; } - /* Sanity check. Data-less blocks were handled earlier. */ - if (WARN_ON_ONCE(!data_check_size(data_ring, *data_size) || !*data_size)) - return NULL; - /* A valid data block will always be aligned to the ID size. */ if (WARN_ON_ONCE(blk_lpos->begin != ALIGN(blk_lpos->begin, sizeof(db->id))) || WARN_ON_ONCE(blk_lpos->next != ALIGN(blk_lpos->next, sizeof(db->id)))) { return NULL; } - /* A valid data block will always have at least an ID. */ - if (WARN_ON_ONCE(*data_size < sizeof(db->id))) + /* + * A regular data block will always have an ID and at least + * 1 byte of data. Data-less blocks were handled earlier. + */ + if (WARN_ON_ONCE(*data_size <= sizeof(db->id))) return NULL; /* Subtract block ID space from size to reflect data size. */ *data_size -= sizeof(db->id); + /* Sanity check the max size of the regular data block. */ + if (WARN_ON_ONCE(!data_check_size(data_ring, *data_size))) + return NULL; + return &db->data[0]; } @@ -1365,7 +1368,7 @@ static struct prb_desc *desc_reopen_last(struct prb_desc_ring *desc_ring, * * WMB from _prb_commit:A to _prb_commit:B * matching - * MB If desc_reopen_last:A to prb_reserve_in_last:A + * MB from desc_reopen_last:A to prb_reserve_in_last:A */ if (!atomic_long_try_cmpxchg(&d->state_var, &prev_state_val, DESC_SV(id, desc_reserved))) { /* LMM(desc_reopen_last:A) */ @@ -1770,9 +1773,9 @@ static void _prb_commit(struct prb_reserved_entry *e, unsigned long state_val) * * Relies on: * - * MB _prb_commit:B to prb_commit:A + * MB from _prb_commit:B to prb_commit:A * matching - * MB desc_reserve:D to desc_make_final:A + * MB from desc_reserve:D to desc_make_final:A */ if (!atomic_long_try_cmpxchg(&d->state_var, &prev_state_val, DESC_SV(e->id, state_val))) { /* LMM(_prb_commit:B) */ @@ -2035,7 +2038,7 @@ u64 prb_first_seq(struct printk_ringbuffer *rb) * * MB from desc_push_tail:B to desc_reserve:F * matching - * RMB prb_first_seq:B to prb_first_seq:A + * RMB from prb_first_seq:B to prb_first_seq:A */ smp_rmb(); /* LMM(prb_first_seq:C) */ } diff --git a/kernel/printk/printk_ringbuffer.h b/kernel/printk/printk_ringbuffer.h index 1651b53ece34..2648da0a68b2 100644 --- a/kernel/printk/printk_ringbuffer.h +++ b/kernel/printk/printk_ringbuffer.h @@ -127,7 +127,7 @@ enum desc_state { }; #define _DATA_SIZE(sz_bits) (1UL << (sz_bits)) -#define _DESCS_COUNT(ct_bits) (1U << (ct_bits)) +#define _DESCS_COUNT(ct_bits) (1UL << (ct_bits)) #define DESC_SV_BITS BITS_PER_LONG #define DESC_FLAGS_SHIFT (DESC_SV_BITS - 2) #define DESC_FLAGS_MASK (3UL << DESC_FLAGS_SHIFT) @@ -388,7 +388,7 @@ for ((s) = from; prb_read_valid(rb, s, r); (s) = (r)->info->seq + 1) * * This is a macro for conveniently iterating over a ringbuffer. * Note that @s may not be the sequence number of the record on each - * iteration. For the sequence number, @r->info->seq should be checked. + * iteration. For the sequence number, @i->seq should be checked. * * Context: Any context. */ diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 68c17daef8d4..130043bfc209 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -272,11 +272,24 @@ static bool ptrace_has_cap(struct user_namespace *ns, unsigned int mode) return ns_capable(ns, CAP_SYS_PTRACE); } +static bool task_still_dumpable(struct task_struct *task, unsigned int mode) +{ + struct mm_struct *mm = task->mm; + if (mm) { + if (get_dumpable(mm) == SUID_DUMP_USER) + return true; + return ptrace_has_cap(mm->user_ns, mode); + } + + if (task->user_dumpable) + return true; + return ptrace_has_cap(&init_user_ns, mode); +} + /* Returns 0 on success, -errno on denial. */ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) { const struct cred *cred = current_cred(), *tcred; - struct mm_struct *mm; kuid_t caller_uid; kgid_t caller_gid; @@ -337,11 +350,8 @@ ok: * Pairs with a write barrier in commit_creds(). */ smp_rmb(); - mm = task->mm; - if (mm && - ((get_dumpable(mm) != SUID_DUMP_USER) && - !ptrace_has_cap(mm->user_ns, mode))) - return -EPERM; + if (!task_still_dumpable(task, mode)) + return -EPERM; return security_ptrace_access_check(task, mode); } diff --git a/kernel/rseq.c b/kernel/rseq.c index 38d3ef540760..e75e3a5e312c 100644 --- a/kernel/rseq.c +++ b/kernel/rseq.c @@ -236,11 +236,6 @@ static int __init rseq_debugfs_init(void) } __initcall(rseq_debugfs_init); -static bool rseq_set_ids(struct task_struct *t, struct rseq_ids *ids, u32 node_id) -{ - return rseq_set_ids_get_csaddr(t, ids, node_id, NULL); -} - static bool rseq_handle_cs(struct task_struct *t, struct pt_regs *regs) { struct rseq __user *urseq = t->rseq.usrptr; @@ -258,14 +253,16 @@ efault: static void rseq_slowpath_update_usr(struct pt_regs *regs) { /* - * Preserve rseq state and user_irq state. The generic entry code - * clears user_irq on the way out, the non-generic entry - * architectures are not having user_irq. + * Preserve has_rseq and user_irq state. The generic entry code clears + * user_irq on the way out, the non-generic entry architectures are not + * setting user_irq. */ - const struct rseq_event evt_mask = { .has_rseq = true, .user_irq = true, }; + const struct rseq_event evt_mask = { + .has_rseq = RSEQ_HAS_RSEQ_VERSION_MASK, + .user_irq = true, + }; struct task_struct *t = current; struct rseq_ids ids; - u32 node_id; bool event; if (unlikely(t->flags & PF_EXITING)) @@ -301,9 +298,9 @@ static void rseq_slowpath_update_usr(struct pt_regs *regs) if (!event) return; - node_id = cpu_to_node(ids.cpu_id); + ids.node_id = cpu_to_node(ids.cpu_id); - if (unlikely(!rseq_update_usr(t, regs, &ids, node_id))) { + if (unlikely(!rseq_update_usr(t, regs, &ids))) { /* * Clear the errors just in case this might survive magically, but * leave the rest intact. @@ -335,8 +332,9 @@ void __rseq_handle_slowpath(struct pt_regs *regs) void __rseq_signal_deliver(int sig, struct pt_regs *regs) { rseq_stat_inc(rseq_stats.signal); + /* - * Don't update IDs, they are handled on exit to user if + * Don't update IDs yet, they are handled on exit to user if * necessary. The important thing is to abort a critical section of * the interrupted context as after this point the instruction * pointer in @regs points to the signal handler. @@ -349,6 +347,13 @@ void __rseq_signal_deliver(int sig, struct pt_regs *regs) current->rseq.event.error = 0; force_sigsegv(sig); } + + /* + * In legacy mode, force the update of IDs before returning to user + * space to stay compatible. + */ + if (!rseq_v2(current)) + rseq_force_update(); } /* @@ -384,19 +389,22 @@ void rseq_syscall(struct pt_regs *regs) static bool rseq_reset_ids(void) { - struct rseq_ids ids = { - .cpu_id = RSEQ_CPU_ID_UNINITIALIZED, - .mm_cid = 0, - }; + struct rseq __user *rseq = current->rseq.usrptr; /* * If this fails, terminate it because this leaves the kernel in * stupid state as exit to user space will try to fixup the ids * again. */ - if (rseq_set_ids(current, &ids, 0)) - return true; + scoped_user_rw_access(rseq, efault) { + unsafe_put_user(0, &rseq->cpu_id_start, efault); + unsafe_put_user(RSEQ_CPU_ID_UNINITIALIZED, &rseq->cpu_id, efault); + unsafe_put_user(0, &rseq->node_id, efault); + unsafe_put_user(0, &rseq->mm_cid, efault); + } + return true; +efault: force_sig(SIGSEGV); return false; } @@ -404,70 +412,29 @@ static bool rseq_reset_ids(void) /* The original rseq structure size (including padding) is 32 bytes. */ #define ORIG_RSEQ_SIZE 32 -/* - * sys_rseq - setup restartable sequences for caller thread. - */ -SYSCALL_DEFINE4(rseq, struct rseq __user *, rseq, u32, rseq_len, int, flags, u32, sig) +static long rseq_register(struct rseq __user * rseq, u32 rseq_len, int flags, u32 sig) { u32 rseqfl = 0; + u8 version = 1; - if (flags & RSEQ_FLAG_UNREGISTER) { - if (flags & ~RSEQ_FLAG_UNREGISTER) - return -EINVAL; - /* Unregister rseq for current thread. */ - if (current->rseq.usrptr != rseq || !current->rseq.usrptr) - return -EINVAL; - if (rseq_len != current->rseq.len) - return -EINVAL; - if (current->rseq.sig != sig) - return -EPERM; - if (!rseq_reset_ids()) - return -EFAULT; - rseq_reset(current); - return 0; - } - - if (unlikely(flags & ~(RSEQ_FLAG_SLICE_EXT_DEFAULT_ON))) - return -EINVAL; - - if (current->rseq.usrptr) { - /* - * If rseq is already registered, check whether - * the provided address differs from the prior - * one. - */ - if (current->rseq.usrptr != rseq || rseq_len != current->rseq.len) - return -EINVAL; - if (current->rseq.sig != sig) - return -EPERM; - /* Already registered. */ - return -EBUSY; - } - - /* - * If there was no rseq previously registered, ensure the provided rseq - * is properly aligned, as communcated to user-space through the ELF - * auxiliary vector AT_RSEQ_ALIGN. If rseq_len is the original rseq - * size, the required alignment is the original struct rseq alignment. - * - * The rseq_len is required to be greater or equal to the original rseq - * size. In order to be valid, rseq_len is either the original rseq size, - * or large enough to contain all supported fields, as communicated to - * user-space through the ELF auxiliary vector AT_RSEQ_FEATURE_SIZE. - */ - if (rseq_len < ORIG_RSEQ_SIZE || - (rseq_len == ORIG_RSEQ_SIZE && !IS_ALIGNED((unsigned long)rseq, ORIG_RSEQ_SIZE)) || - (rseq_len != ORIG_RSEQ_SIZE && (!IS_ALIGNED((unsigned long)rseq, rseq_alloc_align()) || - rseq_len < offsetof(struct rseq, end)))) - return -EINVAL; if (!access_ok(rseq, rseq_len)) return -EFAULT; - if (IS_ENABLED(CONFIG_RSEQ_SLICE_EXTENSION)) { - rseqfl |= RSEQ_CS_FLAG_SLICE_EXT_AVAILABLE; - if (rseq_slice_extension_enabled() && - (flags & RSEQ_FLAG_SLICE_EXT_DEFAULT_ON)) - rseqfl |= RSEQ_CS_FLAG_SLICE_EXT_ENABLED; + /* + * Architectures, which use the generic IRQ entry code (at least) enable + * registrations with a size greater than the original v1 fixed sized + * @rseq_len, which has been validated already to utilize the optimized + * v2 ABI mode which also enables extended RSEQ features beyond MMCID. + */ + if (IS_ENABLED(CONFIG_GENERIC_IRQ_ENTRY) && rseq_len > ORIG_RSEQ_SIZE) + version = 2; + + if (IS_ENABLED(CONFIG_RSEQ_SLICE_EXTENSION) && version > 1) { + if (rseq_slice_extension_enabled()) { + rseqfl |= RSEQ_CS_FLAG_SLICE_EXT_AVAILABLE; + if (flags & RSEQ_FLAG_SLICE_EXT_DEFAULT_ON) + rseqfl |= RSEQ_CS_FLAG_SLICE_EXT_ENABLED; + } } scoped_user_write_access(rseq, efault) { @@ -485,7 +452,15 @@ SYSCALL_DEFINE4(rseq, struct rseq __user *, rseq, u32, rseq_len, int, flags, u32 unsafe_put_user(RSEQ_CPU_ID_UNINITIALIZED, &rseq->cpu_id, efault); unsafe_put_user(0U, &rseq->node_id, efault); unsafe_put_user(0U, &rseq->mm_cid, efault); - unsafe_put_user(0U, &rseq->slice_ctrl.all, efault); + + /* + * All fields past mm_cid are only valid for non-legacy v2 + * registrations. + */ + if (version > 1) { + if (IS_ENABLED(CONFIG_RSEQ_SLICE_EXTENSION)) + unsafe_put_user(0U, &rseq->slice_ctrl.all, efault); + } } /* @@ -501,11 +476,10 @@ SYSCALL_DEFINE4(rseq, struct rseq __user *, rseq, u32, rseq_len, int, flags, u32 #endif /* - * If rseq was previously inactive, and has just been - * registered, ensure the cpu_id_start and cpu_id fields - * are updated before returning to user-space. + * Ensure the cpu_id_start and cpu_id fields are updated before + * returning to user-space. */ - current->rseq.event.has_rseq = true; + current->rseq.event.has_rseq = version; rseq_force_update(); return 0; @@ -513,6 +487,80 @@ efault: return -EFAULT; } +static long rseq_unregister(struct rseq __user * rseq, u32 rseq_len, int flags, u32 sig) +{ + if (flags & ~RSEQ_FLAG_UNREGISTER) + return -EINVAL; + if (current->rseq.usrptr != rseq || !current->rseq.usrptr) + return -EINVAL; + if (rseq_len != current->rseq.len) + return -EINVAL; + if (current->rseq.sig != sig) + return -EPERM; + if (!rseq_reset_ids()) + return -EFAULT; + rseq_reset(current); + return 0; +} + +static long rseq_reregister(struct rseq __user * rseq, u32 rseq_len, u32 sig) +{ + /* + * If rseq is already registered, check whether the provided address + * differs from the prior one. + */ + if (current->rseq.usrptr != rseq || rseq_len != current->rseq.len) + return -EINVAL; + if (current->rseq.sig != sig) + return -EPERM; + /* Already registered. */ + return -EBUSY; +} + +static bool rseq_length_valid(struct rseq __user *rseq, unsigned int rseq_len) +{ + /* + * Ensure the provided rseq is properly aligned, as communicated to + * user-space through the ELF auxiliary vector AT_RSEQ_ALIGN. If + * rseq_len is the original rseq size, the required alignment is the + * original struct rseq alignment. + * + * In order to be valid, rseq_len is either the original rseq size, or + * large enough to contain all supported fields, as communicated to + * user-space through the ELF auxiliary vector AT_RSEQ_FEATURE_SIZE. + */ + if (rseq_len < ORIG_RSEQ_SIZE) + return false; + + if (rseq_len == ORIG_RSEQ_SIZE) + return IS_ALIGNED((unsigned long)rseq, ORIG_RSEQ_SIZE); + + return IS_ALIGNED((unsigned long)rseq, rseq_alloc_align()) && + rseq_len >= offsetof(struct rseq, end); +} + +#define RSEQ_FLAGS_SUPPORTED (RSEQ_FLAG_SLICE_EXT_DEFAULT_ON) + +/* + * sys_rseq - Register or unregister restartable sequences for the caller thread. + */ +SYSCALL_DEFINE4(rseq, struct rseq __user *, rseq, u32, rseq_len, int, flags, u32, sig) +{ + if (flags & RSEQ_FLAG_UNREGISTER) + return rseq_unregister(rseq, rseq_len, flags, sig); + + if (unlikely(flags & ~RSEQ_FLAGS_SUPPORTED)) + return -EINVAL; + + if (current->rseq.usrptr) + return rseq_reregister(rseq, rseq_len, sig); + + if (!rseq_length_valid(rseq, rseq_len)) + return -EINVAL; + + return rseq_register(rseq, rseq_len, flags, sig); +} + #ifdef CONFIG_RSEQ_SLICE_EXTENSION struct slice_timer { struct hrtimer timer; @@ -713,6 +761,8 @@ int rseq_slice_extension_prctl(unsigned long arg2, unsigned long arg3) return -ENOTSUPP; if (!current->rseq.usrptr) return -ENXIO; + if (!rseq_v2(current)) + return -ENOTSUPP; /* No change? */ if (enable == !!current->rseq.slice.state.enabled) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index da20fb6ea25a..b8871449d3c6 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4458,6 +4458,7 @@ static void __sched_fork(u64 clone_flags, struct task_struct *p) p->se.nr_migrations = 0; p->se.vruntime = 0; p->se.vlag = 0; + p->se.rel_deadline = 0; INIT_LIST_HEAD(&p->se.group_node); /* A delayed task cannot be in clone(). */ diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index edca7849b165..7db4c87df83b 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -3107,20 +3107,18 @@ static void task_woken_dl(struct rq *rq, struct task_struct *p) static void set_cpus_allowed_dl(struct task_struct *p, struct affinity_context *ctx) { - struct root_domain *src_rd; struct rq *rq; WARN_ON_ONCE(!dl_task(p)); rq = task_rq(p); - src_rd = rq->rd; /* * Migrating a SCHED_DEADLINE task between exclusive * cpusets (different root_domains) entails a bandwidth * update. We already made space for us in the destination * domain (see cpuset_can_attach()). */ - if (!cpumask_intersects(src_rd->span, ctx->new_mask)) { + if (dl_task_needs_bw_move(p, ctx->new_mask)) { struct dl_bw *src_dl_b; src_dl_b = dl_bw_of(cpu_of(rq)); @@ -3137,6 +3135,15 @@ static void set_cpus_allowed_dl(struct task_struct *p, set_cpus_allowed_common(p, ctx); } +bool dl_task_needs_bw_move(struct task_struct *p, + const struct cpumask *new_mask) +{ + if (!dl_task(p)) + return false; + + return !cpumask_intersects(task_rq(p)->rd->span, new_mask); +} + /* Assumes rq->lock is held */ static void rq_online_dl(struct rq *rq) { diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index 9c458552d14f..295df262ee5a 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -6310,7 +6310,7 @@ static __printf(2, 3) void dump_line(struct seq_buf *s, const char *fmt, ...) vscnprintf(line_buf, sizeof(line_buf), fmt, args); va_end(args); - trace_sched_ext_dump(line_buf); + trace_call__sched_ext_dump(line_buf); } #endif /* @s may be zero sized and seq_buf triggers WARN if so */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 69361c63353a..3ebec186f982 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -847,13 +847,19 @@ static s64 entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se, u64 avrunt * Similarly, check that the entity didn't gain positive lag when DELAY_ZERO * is set. * - * Return true if the lag has been adjusted. + * Return true if the vlag has been modified. Specifically: + * + * se->vlag != avg_vruntime() - se->vruntime + * + * This can be due to clamping in entity_lag() or clamping due to + * sched_delayed. Either way, when vlag is modified and the entity is + * retained, the tree needs to be adjusted. */ static __always_inline bool update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se) { - s64 vlag = entity_lag(cfs_rq, se, avg_vruntime(cfs_rq)); - bool ret; + u64 avruntime = avg_vruntime(cfs_rq); + s64 vlag = entity_lag(cfs_rq, se, avruntime); WARN_ON_ONCE(!se->on_rq); @@ -863,10 +869,9 @@ bool update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se) if (sched_feat(DELAY_ZERO)) vlag = min(vlag, 0); } - ret = (vlag == se->vlag); se->vlag = vlag; - return ret; + return avruntime - vlag != se->vruntime; } /* @@ -877,11 +882,11 @@ bool update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se) * * lag_i >= 0 -> V >= v_i * - * \Sum (v_i - v)*w_i - * V = ------------------ + v + * \Sum (v_i - v0)*w_i + * V = ------------------- + v0 * \Sum w_i * - * lag_i >= 0 -> \Sum (v_i - v)*w_i >= (v_i - v)*(\Sum w_i) + * lag_i >= 0 -> \Sum (v_i - v0)*w_i >= (v_i - v0)*(\Sum w_i) * * Note: using 'avg_vruntime() > se->vruntime' is inaccurate due * to the loss in precision caused by the division. @@ -889,7 +894,7 @@ bool update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se) static int vruntime_eligible(struct cfs_rq *cfs_rq, u64 vruntime) { struct sched_entity *curr = cfs_rq->curr; - s64 avg = cfs_rq->sum_w_vruntime; + s64 key, avg = cfs_rq->sum_w_vruntime; long load = cfs_rq->sum_weight; if (curr && curr->on_rq) { @@ -899,7 +904,36 @@ static int vruntime_eligible(struct cfs_rq *cfs_rq, u64 vruntime) load += weight; } - return avg >= vruntime_op(vruntime, "-", cfs_rq->zero_vruntime) * load; + key = vruntime_op(vruntime, "-", cfs_rq->zero_vruntime); + + /* + * The worst case term for @key includes 'NSEC_TICK * NICE_0_LOAD' + * and @load obviously includes NICE_0_LOAD. NSEC_TICK is around 24 + * bits, while NICE_0_LOAD is 20 on 64bit and 10 otherwise. + * + * This gives that on 64bit the product will be at least 64bit which + * overflows s64, while on 32bit it will only be 44bits and should fit + * comfortably. + */ +#ifdef CONFIG_64BIT +#ifdef CONFIG_ARCH_SUPPORTS_INT128 + /* This often results in simpler code than __builtin_mul_overflow(). */ + return avg >= (__int128)key * load; +#else + s64 rhs; + /* + * On overflow, the sign of key tells us the correct answer: a large + * positive key means vruntime >> V, so not eligible; a large negative + * key means vruntime << V, so eligible. + */ + if (check_mul_overflow(key, load, &rhs)) + return key <= 0; + + return avg >= rhs; +#endif +#else /* 32bit */ + return avg >= key * load; +#endif } int entity_eligible(struct cfs_rq *cfs_rq, struct sched_entity *se) @@ -1099,7 +1133,7 @@ static inline void cancel_protect_slice(struct sched_entity *se) * * Which allows tree pruning through eligibility. */ -static struct sched_entity *__pick_eevdf(struct cfs_rq *cfs_rq, bool protect) +static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq, bool protect) { struct rb_node *node = cfs_rq->tasks_timeline.rb_root.rb_node; struct sched_entity *se = __pick_first_entity(cfs_rq); @@ -1170,11 +1204,6 @@ found: return best; } -static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq) -{ - return __pick_eevdf(cfs_rq, true); -} - struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq) { struct rb_node *last = rb_last(&cfs_rq->tasks_timeline.rb_root); @@ -5749,11 +5778,11 @@ static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags); * 4) do not run the "skip" process, if something else is available */ static struct sched_entity * -pick_next_entity(struct rq *rq, struct cfs_rq *cfs_rq) +pick_next_entity(struct rq *rq, struct cfs_rq *cfs_rq, bool protect) { struct sched_entity *se; - se = pick_eevdf(cfs_rq); + se = pick_eevdf(cfs_rq, protect); if (se->sched_delayed) { dequeue_entities(rq, se, DEQUEUE_SLEEP | DEQUEUE_DELAYED); /* @@ -9027,7 +9056,7 @@ static void wakeup_preempt_fair(struct rq *rq, struct task_struct *p, int wake_f { enum preempt_wakeup_action preempt_action = PREEMPT_WAKEUP_PICK; struct task_struct *donor = rq->donor; - struct sched_entity *se = &donor->se, *pse = &p->se; + struct sched_entity *nse, *se = &donor->se, *pse = &p->se; struct cfs_rq *cfs_rq = task_cfs_rq(donor); int cse_is_idle, pse_is_idle; @@ -9138,12 +9167,19 @@ static void wakeup_preempt_fair(struct rq *rq, struct task_struct *p, int wake_f } pick: - /* - * If @p has become the most eligible task, force preemption. - */ - if (__pick_eevdf(cfs_rq, preempt_action != PREEMPT_WAKEUP_SHORT) == pse) + nse = pick_next_entity(rq, cfs_rq, preempt_action != PREEMPT_WAKEUP_SHORT); + /* If @p has become the most eligible task, force preemption */ + if (nse == pse) goto preempt; + /* + * Because p is enqueued, nse being null can only mean that we + * dequeued a delayed task. If there are still entities queued in + * cfs, check if the next one will be p. + */ + if (!nse && cfs_rq->nr_queued) + goto pick; + if (sched_feat(RUN_TO_PARITY)) update_protect_slice(cfs_rq, se); @@ -9179,7 +9215,7 @@ again: throttled |= check_cfs_rq_runtime(cfs_rq); - se = pick_next_entity(rq, cfs_rq); + se = pick_next_entity(rq, cfs_rq, true); if (!se) goto again; cfs_rq = group_cfs_rq(se); diff --git a/kernel/sched/membarrier.c b/kernel/sched/membarrier.c index 623445603725..226a6329f3e9 100644 --- a/kernel/sched/membarrier.c +++ b/kernel/sched/membarrier.c @@ -199,7 +199,16 @@ static void ipi_rseq(void *info) * is negligible. */ smp_mb(); - rseq_sched_switch_event(current); + /* + * Legacy mode requires that IDs are written and the critical section is + * evaluated. V2 optimized mode handles the critical section and IDs are + * only updated if they change as a consequence of preemption after + * return from this IPI. + */ + if (rseq_v2(current)) + rseq_sched_switch_event(current); + else + rseq_force_update(); } static void ipi_sync_rq_state(void *info) diff --git a/kernel/signal.c b/kernel/signal.c index 21d881b95ffb..2d102e025883 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1000,9 +1000,7 @@ static void complete_signal(int sig, struct task_struct *p, enum pid_type type) * Found a killable thread. If the signal will be fatal, * then start taking the whole group down immediately. */ - if (sig_fatal(p, sig) && - (signal->core_state || !(signal->flags & SIGNAL_GROUP_EXIT)) && - !sigismember(&t->real_blocked, sig) && + if (sig_fatal(p, sig) && !sigismember(&t->real_blocked, sig) && (sig == SIGKILL || !p->ptrace)) { /* * This signal will be fatal to the whole group. @@ -2173,13 +2171,13 @@ bool do_notify_parent(struct task_struct *tsk, int sig) bool autoreap = false; u64 utime, stime; - WARN_ON_ONCE(sig == -1); + if (WARN_ON_ONCE(!valid_signal(sig))) + return false; /* do_notify_parent_cldstop should have been called instead. */ WARN_ON_ONCE(task_is_stopped_or_traced(tsk)); - WARN_ON_ONCE(!tsk->ptrace && - (tsk->group_leader != tsk || !thread_group_empty(tsk))); + WARN_ON_ONCE(!tsk->ptrace && !thread_group_empty(tsk)); /* ptraced, or group-leader without sub-threads */ do_notify_pidfd(tsk); @@ -2259,7 +2257,7 @@ bool do_notify_parent(struct task_struct *tsk, int sig) * Send with __send_signal as si_pid and si_uid are in the * parent's namespaces. */ - if (valid_signal(sig) && sig) + if (sig) __send_signal_locked(sig, &info, tsk->parent, PIDTYPE_TGID, false); __wake_up_parent(tsk, tsk->parent); spin_unlock_irqrestore(&psig->siglock, flags); diff --git a/kernel/smp.c b/kernel/smp.c index 6c77848d91f3..a0bb56bd8dda 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -408,7 +408,7 @@ void __smp_call_single_queue(int cpu, struct llist_node *node) func = CSD_TYPE(csd) == CSD_TYPE_TTWU ? sched_ttwu_pending : csd->func; - trace_csd_queue_cpu(cpu, _RET_IP_, func, csd); + trace_call__csd_queue_cpu(cpu, _RET_IP_, func, csd); } /* diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 0cd680ccc7e5..73bd6a6a7893 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -649,6 +649,7 @@ void taskstats_exit(struct task_struct *tsk, int group_dead) goto err; memcpy(stats, tsk->signal->stats, sizeof(*stats)); + stats->version = TASKSTATS_VERSION; send: send_cpu_listeners(rep_skb, listeners); diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index b4d730604972..5e22697b098d 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -94,6 +94,9 @@ static int __clockevents_switch_state(struct clock_event_device *dev, if (dev->features & CLOCK_EVT_FEAT_DUMMY) return 0; + /* On state transitions clear the forced flag unconditionally */ + dev->next_event_forced = 0; + /* Transition with new state-specific callbacks */ switch (state) { case CLOCK_EVT_STATE_DETACHED: @@ -366,8 +369,10 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, b if (delta > (int64_t)dev->min_delta_ns) { delta = min(delta, (int64_t) dev->max_delta_ns); cycles = ((u64)delta * dev->mult) >> dev->shift; - if (!dev->set_next_event((unsigned long) cycles, dev)) + if (!dev->set_next_event((unsigned long) cycles, dev)) { + dev->next_event_forced = 0; return 0; + } } if (dev->next_event_forced) diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 7e57fa31ee26..115e0bf01276 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -108,6 +108,7 @@ static struct clock_event_device *tick_get_oneshot_wakeup_device(int cpu) static void tick_oneshot_wakeup_handler(struct clock_event_device *wd) { + wd->next_event_forced = 0; /* * If we woke up early and the tick was reprogrammed in the * meantime then this may be spurious but harmless. diff --git a/kernel/time/timer_migration.c b/kernel/time/timer_migration.c index 155eeaea4113..1d0d3a4058d5 100644 --- a/kernel/time/timer_migration.c +++ b/kernel/time/timer_migration.c @@ -1860,19 +1860,37 @@ static int tmigr_setup_groups(unsigned int cpu, unsigned int node, * child to the new parents. So tmigr_active_up() activates the * new parents while walking up from the old root to the new. * - * * It is ensured that @start is active, as this setup path is - * executed in hotplug prepare callback. This is executed by an - * already connected and !idle CPU. Even if all other CPUs go idle, - * the CPU executing the setup will be responsible up to current top - * level group. And the next time it goes inactive, it will release - * the new childmask and parent to subsequent walkers through this - * @child. Therefore propagate active state unconditionally. + * * It is ensured that @start is active, (or on the way to be activated + * by another CPU that woke up before the current one) as this setup path + * is executed in hotplug prepare callback. This is executed by an already + * connected and !idle CPU in the hierarchy. + * + * * The below RmW atomic operation ensures that: + * + * 1) If the old root has been completely activated, the latest state is + * acquired (the below implicit acquire pairs with the implicit release + * from cmpxchg() in tmigr_active_up()). + * + * 2) If the old root is still on the way to be activated, the lagging behind + * CPU performing the activation will acquire the links up to the new root. + * (The below implicit release pairs with the implicit acquire from cmpxchg() + * in tmigr_active_up()). + * + * 3) Every subsequent CPU below the old root will acquire the new links while + * walking through the old root (The below implicit release pairs with the + * implicit acquire from cmpxchg() in either tmigr_active_up()) or + * tmigr_inactive_up(). */ - state.state = atomic_read(&start->migr_state); - WARN_ON_ONCE(!state.active); + state.state = atomic_fetch_or(0, &start->migr_state); WARN_ON_ONCE(!start->parent); - data.childmask = start->groupmask; - __walk_groups_from(tmigr_active_up, &data, start, start->parent); + /* + * If the state of the old root is inactive, another CPU is on its way to activate + * it and propagate to the new root. + */ + if (state.active) { + data.childmask = start->groupmask; + __walk_groups_from(tmigr_active_up, &data, start, start->parent); + } } /* Root update */ diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 3182e1bc1cf7..9b0834134cae 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_TRACING) += trace_seq.o obj-$(CONFIG_TRACING) += trace_stat.o obj-$(CONFIG_TRACING) += trace_printk.o obj-$(CONFIG_TRACING) += trace_pid.o +obj-$(CONFIG_TRACER_SNAPSHOT) += trace_snapshot.o obj-$(CONFIG_TRACING) += pid_list.o obj-$(CONFIG_TRACING_MAP) += tracing_map.o obj-$(CONFIG_PREEMPTIRQ_DELAY_TEST) += preemptirq_delay_test.o @@ -132,18 +133,30 @@ obj-$(CONFIG_TRACE_REMOTE) += trace_remote.o obj-$(CONFIG_SIMPLE_RING_BUFFER) += simple_ring_buffer.o obj-$(CONFIG_TRACE_REMOTE_TEST) += remote_test.o -# # simple_ring_buffer is used by the pKVM hypervisor which does not have access # to all kernel symbols. Fail the build if forbidden symbols are found. -# -UNDEFINED_ALLOWLIST := memset alt_cb_patch_nops __x86 __ubsan __asan __kasan __gcov __aeabi_unwind -UNDEFINED_ALLOWLIST += __stack_chk_fail stackleak_track_stack __ref_stack __sanitizer -UNDEFINED_ALLOWLIST := $(addprefix -e , $(UNDEFINED_ALLOWLIST)) + +# Basic compiler and tooling-generated symbols that can safely be left +# undefined. Ensure KASAN is enabled to avoid logic that may disable +# FORTIFY_SOURCE when KASAN is not enabled. undefsyms_base.o does not +# automatically get KASAN flags because it is not linked into vmlinux. +targets += undefsyms_base.o +KASAN_SANITIZE_undefsyms_base.o := y + +UNDEFINED_ALLOWLIST = __asan __gcov __kasan __kcsan __hwasan __sancov __sanitizer __tsan __ubsan __msan \ + __aeabi_unwind_cpp __s390_indirect_jump __x86_indirect_thunk simple_ring_buffer \ + $(shell $(NM) -u $(obj)/undefsyms_base.o 2>/dev/null | awk '{print $$2}') quiet_cmd_check_undefined = NM $< - cmd_check_undefined = test -z "`$(NM) -u $< | grep -v $(UNDEFINED_ALLOWLIST)`" + cmd_check_undefined = \ + undefsyms=$$($(NM) -u $< | grep -v $(addprefix -e , $(UNDEFINED_ALLOWLIST)) || true); \ + if [ -n "$$undefsyms" ]; then \ + echo "Unexpected symbols in $<:" >&2; \ + echo "$$undefsyms" >&2; \ + false; \ + fi -$(obj)/%.o.checked: $(obj)/%.o FORCE +$(obj)/%.o.checked: $(obj)/%.o $(obj)/undefsyms_base.o FORCE $(call if_changed,check_undefined) always-$(CONFIG_SIMPLE_RING_BUFFER) += simple_ring_buffer.o.checked diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index af7079aa0f36..a02bd258677e 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2384,7 +2384,8 @@ static void bpf_kprobe_multi_link_release(struct bpf_link *link) struct bpf_kprobe_multi_link *kmulti_link; kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); - unregister_fprobe(&kmulti_link->fp); + /* Don't wait for RCU GP here. */ + unregister_fprobe_async(&kmulti_link->fp); kprobe_multi_put_modules(kmulti_link->mods, kmulti_link->mods_cnt); } diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index dcadf1d23b8a..f378613ad120 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -4,6 +4,7 @@ */ #define pr_fmt(fmt) "fprobe: " fmt +#include #include #include #include @@ -78,36 +79,33 @@ static const struct rhashtable_params fprobe_rht_params = { }; /* Node insertion and deletion requires the fprobe_mutex */ -static int insert_fprobe_node(struct fprobe_hlist_node *node) +static int __insert_fprobe_node(struct fprobe_hlist_node *node, struct fprobe *fp) { + int ret; + lockdep_assert_held(&fprobe_mutex); - return rhltable_insert(&fprobe_ip_table, &node->hlist, fprobe_rht_params); + ret = rhltable_insert(&fprobe_ip_table, &node->hlist, fprobe_rht_params); + /* Set the fprobe pointer if insertion was successful. */ + if (!ret) + WRITE_ONCE(node->fp, fp); + return ret; } -/* Return true if there are synonims */ -static bool delete_fprobe_node(struct fprobe_hlist_node *node) +static void __delete_fprobe_node(struct fprobe_hlist_node *node) { lockdep_assert_held(&fprobe_mutex); - bool ret; - /* Avoid double deleting */ + /* Avoid double deleting and non-inserted nodes */ if (READ_ONCE(node->fp) != NULL) { WRITE_ONCE(node->fp, NULL); rhltable_remove(&fprobe_ip_table, &node->hlist, fprobe_rht_params); } - - rcu_read_lock(); - ret = !!rhltable_lookup(&fprobe_ip_table, &node->addr, - fprobe_rht_params); - rcu_read_unlock(); - - return ret; } /* Check existence of the fprobe */ -static bool is_fprobe_still_exist(struct fprobe *fp) +static bool fprobe_registered(struct fprobe *fp) { struct hlist_head *head; struct fprobe_hlist *fph; @@ -120,7 +118,7 @@ static bool is_fprobe_still_exist(struct fprobe *fp) } return false; } -NOKPROBE_SYMBOL(is_fprobe_still_exist); +NOKPROBE_SYMBOL(fprobe_registered); static int add_fprobe_hash(struct fprobe *fp) { @@ -132,9 +130,6 @@ static int add_fprobe_hash(struct fprobe *fp) if (WARN_ON_ONCE(!fph)) return -EINVAL; - if (is_fprobe_still_exist(fp)) - return -EEXIST; - head = &fprobe_table[hash_ptr(fp, FPROBE_HASH_BITS)]; hlist_add_head_rcu(&fp->hlist_array->hlist, head); return 0; @@ -149,7 +144,7 @@ static int del_fprobe_hash(struct fprobe *fp) if (WARN_ON_ONCE(!fph)) return -EINVAL; - if (!is_fprobe_still_exist(fp)) + if (!fprobe_registered(fp)) return -ENOENT; fph->fp = NULL; @@ -255,7 +250,65 @@ static inline int __fprobe_kprobe_handler(unsigned long ip, unsigned long parent return ret; } +static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops, + struct ftrace_regs *fregs); +static void fprobe_return(struct ftrace_graph_ret *trace, + struct fgraph_ops *gops, + struct ftrace_regs *fregs); + +static struct fgraph_ops fprobe_graph_ops = { + .entryfunc = fprobe_fgraph_entry, + .retfunc = fprobe_return, +}; +/* Number of fgraph fprobe nodes */ +static int nr_fgraph_fprobes; +/* Is fprobe_graph_ops registered? */ +static bool fprobe_graph_registered; + +/* Add @addrs to the ftrace filter and register fgraph if needed. */ +static int fprobe_graph_add_ips(unsigned long *addrs, int num) +{ + int ret; + + lockdep_assert_held(&fprobe_mutex); + + ret = ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 0, 0); + if (ret) + return ret; + + if (!fprobe_graph_registered) { + ret = register_ftrace_graph(&fprobe_graph_ops); + if (WARN_ON_ONCE(ret)) { + ftrace_free_filter(&fprobe_graph_ops.ops); + return ret; + } + fprobe_graph_registered = true; + } + return 0; +} + +static void __fprobe_graph_unregister(void) +{ + if (fprobe_graph_registered) { + unregister_ftrace_graph(&fprobe_graph_ops); + ftrace_free_filter(&fprobe_graph_ops.ops); + fprobe_graph_registered = false; + } +} + +/* Remove @addrs from the ftrace filter and unregister fgraph if possible. */ +static void fprobe_graph_remove_ips(unsigned long *addrs, int num) +{ + lockdep_assert_held(&fprobe_mutex); + + if (!nr_fgraph_fprobes) + __fprobe_graph_unregister(); + else if (num) + ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 1, 0); +} + #if defined(CONFIG_DYNAMIC_FTRACE_WITH_ARGS) || defined(CONFIG_DYNAMIC_FTRACE_WITH_REGS) + /* ftrace_ops callback, this processes fprobes which have only entry_handler. */ static void fprobe_ftrace_entry(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct ftrace_regs *fregs) @@ -298,7 +351,10 @@ static struct ftrace_ops fprobe_ftrace_ops = { .func = fprobe_ftrace_entry, .flags = FTRACE_OPS_FL_SAVE_ARGS, }; -static int fprobe_ftrace_active; +/* Number of ftrace fprobe nodes */ +static int nr_ftrace_fprobes; +/* Is fprobe_ftrace_ops registered? */ +static bool fprobe_ftrace_registered; static int fprobe_ftrace_add_ips(unsigned long *addrs, int num) { @@ -310,25 +366,33 @@ static int fprobe_ftrace_add_ips(unsigned long *addrs, int num) if (ret) return ret; - if (!fprobe_ftrace_active) { + if (!fprobe_ftrace_registered) { ret = register_ftrace_function(&fprobe_ftrace_ops); if (ret) { ftrace_free_filter(&fprobe_ftrace_ops); return ret; } + fprobe_ftrace_registered = true; } - fprobe_ftrace_active++; return 0; } +static void __fprobe_ftrace_unregister(void) +{ + if (fprobe_ftrace_registered) { + unregister_ftrace_function(&fprobe_ftrace_ops); + ftrace_free_filter(&fprobe_ftrace_ops); + fprobe_ftrace_registered = false; + } +} + static void fprobe_ftrace_remove_ips(unsigned long *addrs, int num) { lockdep_assert_held(&fprobe_mutex); - fprobe_ftrace_active--; - if (!fprobe_ftrace_active) - unregister_ftrace_function(&fprobe_ftrace_ops); - if (num) + if (!nr_ftrace_fprobes) + __fprobe_ftrace_unregister(); + else if (num) ftrace_set_filter_ips(&fprobe_ftrace_ops, addrs, num, 1, 0); } @@ -337,12 +401,78 @@ static bool fprobe_is_ftrace(struct fprobe *fp) return !fp->exit_handler; } -#ifdef CONFIG_MODULES -static void fprobe_set_ips(unsigned long *ips, unsigned int cnt, int remove, - int reset) +/* Node insertion and deletion requires the fprobe_mutex */ +static int insert_fprobe_node(struct fprobe_hlist_node *node, struct fprobe *fp) { - ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, remove, reset); - ftrace_set_filter_ips(&fprobe_ftrace_ops, ips, cnt, remove, reset); + int ret; + + lockdep_assert_held(&fprobe_mutex); + + ret = __insert_fprobe_node(node, fp); + if (!ret) { + if (fprobe_is_ftrace(fp)) + nr_ftrace_fprobes++; + else + nr_fgraph_fprobes++; + } + + return ret; +} + +static void delete_fprobe_node(struct fprobe_hlist_node *node) +{ + struct fprobe *fp; + + lockdep_assert_held(&fprobe_mutex); + + fp = READ_ONCE(node->fp); + if (fp) { + if (fprobe_is_ftrace(fp)) + nr_ftrace_fprobes--; + else + nr_fgraph_fprobes--; + } + __delete_fprobe_node(node); +} + +static bool fprobe_exists_on_hash(unsigned long ip, bool ftrace) +{ + struct rhlist_head *head, *pos; + struct fprobe_hlist_node *node; + struct fprobe *fp; + + guard(rcu)(); + head = rhltable_lookup(&fprobe_ip_table, &ip, + fprobe_rht_params); + if (!head) + return false; + /* We have to check the same type on the list. */ + rhl_for_each_entry_rcu(node, pos, head, hlist) { + if (node->addr != ip) + break; + fp = READ_ONCE(node->fp); + if (likely(fp)) { + if ((!ftrace && fp->exit_handler) || + (ftrace && !fp->exit_handler)) + return true; + } + } + + return false; +} + +#ifdef CONFIG_MODULES +static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt) +{ + if (!nr_fgraph_fprobes) + __fprobe_graph_unregister(); + else if (cnt) + ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0); + + if (!nr_ftrace_fprobes) + __fprobe_ftrace_unregister(); + else if (cnt) + ftrace_set_filter_ips(&fprobe_ftrace_ops, ips, cnt, 1, 0); } #endif #else @@ -360,11 +490,62 @@ static bool fprobe_is_ftrace(struct fprobe *fp) return false; } -#ifdef CONFIG_MODULES -static void fprobe_set_ips(unsigned long *ips, unsigned int cnt, int remove, - int reset) +/* Node insertion and deletion requires the fprobe_mutex */ +static int insert_fprobe_node(struct fprobe_hlist_node *node, struct fprobe *fp) { - ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, remove, reset); + int ret; + + lockdep_assert_held(&fprobe_mutex); + + ret = __insert_fprobe_node(node, fp); + if (!ret) + nr_fgraph_fprobes++; + + return ret; +} + +static void delete_fprobe_node(struct fprobe_hlist_node *node) +{ + struct fprobe *fp; + + lockdep_assert_held(&fprobe_mutex); + + fp = READ_ONCE(node->fp); + if (fp) + nr_fgraph_fprobes--; + __delete_fprobe_node(node); +} + +static bool fprobe_exists_on_hash(unsigned long ip, bool ftrace __maybe_unused) +{ + struct rhlist_head *head, *pos; + struct fprobe_hlist_node *node; + struct fprobe *fp; + + guard(rcu)(); + head = rhltable_lookup(&fprobe_ip_table, &ip, + fprobe_rht_params); + if (!head) + return false; + /* We only need to check fp is there. */ + rhl_for_each_entry_rcu(node, pos, head, hlist) { + if (node->addr != ip) + break; + fp = READ_ONCE(node->fp); + if (likely(fp)) + return true; + } + + return false; +} + +#ifdef CONFIG_MODULES +static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt) +{ + if (!nr_fgraph_fprobes) + __fprobe_graph_unregister(); + else if (cnt) + ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0); } #endif #endif /* !CONFIG_DYNAMIC_FTRACE_WITH_ARGS && !CONFIG_DYNAMIC_FTRACE_WITH_REGS */ @@ -450,8 +631,6 @@ static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops used += FPROBE_HEADER_SIZE_IN_LONG + size_words; } } - if (used < reserved_words) - memset(fgraph_data + used, 0, reserved_words - used); /* If any exit_handler is set, data must be used. */ return used != 0; @@ -482,7 +661,7 @@ static void fprobe_return(struct ftrace_graph_ret *trace, if (!fp) break; curr += FPROBE_HEADER_SIZE_IN_LONG; - if (is_fprobe_still_exist(fp) && !fprobe_disabled(fp)) { + if (fprobe_registered(fp) && !fprobe_disabled(fp)) { if (WARN_ON_ONCE(curr + size > size_words)) break; fp->exit_handler(fp, trace->func, ret_ip, fregs, @@ -494,51 +673,9 @@ static void fprobe_return(struct ftrace_graph_ret *trace, } NOKPROBE_SYMBOL(fprobe_return); -static struct fgraph_ops fprobe_graph_ops = { - .entryfunc = fprobe_fgraph_entry, - .retfunc = fprobe_return, -}; -static int fprobe_graph_active; - -/* Add @addrs to the ftrace filter and register fgraph if needed. */ -static int fprobe_graph_add_ips(unsigned long *addrs, int num) -{ - int ret; - - lockdep_assert_held(&fprobe_mutex); - - ret = ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 0, 0); - if (ret) - return ret; - - if (!fprobe_graph_active) { - ret = register_ftrace_graph(&fprobe_graph_ops); - if (WARN_ON_ONCE(ret)) { - ftrace_free_filter(&fprobe_graph_ops.ops); - return ret; - } - } - fprobe_graph_active++; - return 0; -} - -/* Remove @addrs from the ftrace filter and unregister fgraph if possible. */ -static void fprobe_graph_remove_ips(unsigned long *addrs, int num) -{ - lockdep_assert_held(&fprobe_mutex); - - fprobe_graph_active--; - /* Q: should we unregister it ? */ - if (!fprobe_graph_active) - unregister_ftrace_graph(&fprobe_graph_ops); - - if (num) - ftrace_set_filter_ips(&fprobe_graph_ops.ops, addrs, num, 1, 0); -} - #ifdef CONFIG_MODULES -#define FPROBE_IPS_BATCH_INIT 8 +#define FPROBE_IPS_BATCH_INIT 128 /* instruction pointer address list */ struct fprobe_addr_list { int index; @@ -546,43 +683,29 @@ struct fprobe_addr_list { unsigned long *addrs; }; -static int fprobe_addr_list_add(struct fprobe_addr_list *alist, unsigned long addr) -{ - unsigned long *addrs; - - /* Previously we failed to expand the list. */ - if (alist->index == alist->size) - return -ENOSPC; - - alist->addrs[alist->index++] = addr; - if (alist->index < alist->size) - return 0; - - /* Expand the address list */ - addrs = kcalloc(alist->size * 2, sizeof(*addrs), GFP_KERNEL); - if (!addrs) - return -ENOMEM; - - memcpy(addrs, alist->addrs, alist->size * sizeof(*addrs)); - alist->size *= 2; - kfree(alist->addrs); - alist->addrs = addrs; - - return 0; -} - -static void fprobe_remove_node_in_module(struct module *mod, struct fprobe_hlist_node *node, +static int fprobe_remove_node_in_module(struct module *mod, struct fprobe_hlist_node *node, struct fprobe_addr_list *alist) { + lockdep_assert_in_rcu_read_lock(); + if (!within_module(node->addr, mod)) - return; - if (delete_fprobe_node(node)) - return; + return 0; + + delete_fprobe_node(node); + /* If no address list is available, we can't track this address. */ + if (!alist->addrs) + return 0; /* - * If failed to update alist, just continue to update hlist. - * Therefore, at list user handler will not hit anymore. + * Don't care the type here, because all fprobes on the same + * address must be removed eventually. */ - fprobe_addr_list_add(alist, node->addr); + if (!rhltable_lookup(&fprobe_ip_table, &node->addr, fprobe_rht_params)) { + alist->addrs[alist->index++] = node->addr; + if (alist->index == alist->size) + return -ENOSPC; + } + + return 0; } /* Handle module unloading to manage fprobe_ip_table. */ @@ -593,29 +716,48 @@ static int fprobe_module_callback(struct notifier_block *nb, struct fprobe_hlist_node *node; struct rhashtable_iter iter; struct module *mod = data; + bool retry; if (val != MODULE_STATE_GOING) return NOTIFY_DONE; alist.addrs = kcalloc(alist.size, sizeof(*alist.addrs), GFP_KERNEL); - /* If failed to alloc memory, we can not remove ips from hash. */ - if (!alist.addrs) - return NOTIFY_DONE; + /* + * If failed to alloc memory, ftrace_ops will not be able to remove ips from + * hash, but we can still remove nodes from fprobe_ip_table, so we can avoid + * the potential wrong callback. So just print a warning here and try to + * continue without address list. + */ + WARN_ONCE(!alist.addrs, + "Failed to allocate memory for fprobe_addr_list, ftrace_ops will not be updated"); mutex_lock(&fprobe_mutex); +again: + retry = false; + alist.index = 0; rhltable_walk_enter(&fprobe_ip_table, &iter); do { rhashtable_walk_start(&iter); while ((node = rhashtable_walk_next(&iter)) && !IS_ERR(node)) - fprobe_remove_node_in_module(mod, node, &alist); + if (fprobe_remove_node_in_module(mod, node, &alist) < 0) { + retry = true; + break; + } rhashtable_walk_stop(&iter); - } while (node == ERR_PTR(-EAGAIN)); + } while (node == ERR_PTR(-EAGAIN) && !retry); rhashtable_walk_exit(&iter); + /* Remove any ips from hash table(s) */ + fprobe_remove_ips(alist.addrs, alist.index); + /* + * If we break rhashtable walk loop except for -EAGAIN, we need + * to restart looping from start for safety. Anyway, this is + * not a hotpath. + */ + if (retry) + goto again; - if (alist.index > 0) - fprobe_set_ips(alist.addrs, alist.index, 1, 0); mutex_unlock(&fprobe_mutex); kfree(alist.addrs); @@ -759,7 +901,6 @@ static int fprobe_init(struct fprobe *fp, unsigned long *addrs, int num) fp->hlist_array = hlist_array; hlist_array->fp = fp; for (i = 0; i < num; i++) { - hlist_array->array[i].fp = fp; addr = ftrace_location(addrs[i]); if (!addr) { fprobe_fail_cleanup(fp); @@ -823,6 +964,8 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter } EXPORT_SYMBOL_GPL(register_fprobe); +static int unregister_fprobe_nolock(struct fprobe *fp); + /** * register_fprobe_ips() - Register fprobe to ftrace by address. * @fp: A fprobe data structure to be registered. @@ -841,35 +984,33 @@ int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num) struct fprobe_hlist *hlist_array; int ret, i; + guard(mutex)(&fprobe_mutex); + if (fprobe_registered(fp)) + return -EEXIST; + ret = fprobe_init(fp, addrs, num); if (ret) return ret; - mutex_lock(&fprobe_mutex); - - hlist_array = fp->hlist_array; if (fprobe_is_ftrace(fp)) ret = fprobe_ftrace_add_ips(addrs, num); else ret = fprobe_graph_add_ips(addrs, num); - - if (!ret) { - add_fprobe_hash(fp); - for (i = 0; i < hlist_array->size; i++) { - ret = insert_fprobe_node(&hlist_array->array[i]); - if (ret) - break; - } - /* fallback on insert error */ - if (ret) { - for (i--; i >= 0; i--) - delete_fprobe_node(&hlist_array->array[i]); - } - } - mutex_unlock(&fprobe_mutex); - - if (ret) + if (ret) { fprobe_fail_cleanup(fp); + return ret; + } + + hlist_array = fp->hlist_array; + ret = add_fprobe_hash(fp); + for (i = 0; i < hlist_array->size && !ret; i++) + ret = insert_fprobe_node(&hlist_array->array[i], fp); + + if (ret) { + unregister_fprobe_nolock(fp); + /* In error case, wait for clean up safely. */ + synchronize_rcu(); + } return ret; } @@ -913,37 +1054,28 @@ bool fprobe_is_registered(struct fprobe *fp) return true; } -/** - * unregister_fprobe() - Unregister fprobe. - * @fp: A fprobe data structure to be unregistered. - * - * Unregister fprobe (and remove ftrace hooks from the function entries). - * - * Return 0 if @fp is unregistered successfully, -errno if not. - */ -int unregister_fprobe(struct fprobe *fp) +static int unregister_fprobe_nolock(struct fprobe *fp) { - struct fprobe_hlist *hlist_array; + struct fprobe_hlist *hlist_array = fp->hlist_array; unsigned long *addrs = NULL; - int ret = 0, i, count; + int i, count; - mutex_lock(&fprobe_mutex); - if (!fp || !is_fprobe_still_exist(fp)) { - ret = -EINVAL; - goto out; - } - - hlist_array = fp->hlist_array; addrs = kcalloc(hlist_array->size, sizeof(unsigned long), GFP_KERNEL); - if (!addrs) { - ret = -ENOMEM; /* TODO: Fallback to one-by-one loop */ - goto out; - } + /* + * This will remove fprobe_hash_node from the hash table even if + * memory allocation fails. However, ftrace_ops will not be updated. + * Anyway, when the last fprobe is unregistered, ftrace_ops is also + * unregistered. + */ + if (!addrs) + pr_warn("Failed to allocate working array. ftrace_ops may not sync.\n"); /* Remove non-synonim ips from table and hash */ count = 0; for (i = 0; i < hlist_array->size; i++) { - if (!delete_fprobe_node(&hlist_array->array[i])) + delete_fprobe_node(&hlist_array->array[i]); + if (addrs && !fprobe_exists_on_hash(hlist_array->array[i].addr, + fprobe_is_ftrace(fp))) addrs[count++] = hlist_array->array[i].addr; } del_fprobe_hash(fp); @@ -955,11 +1087,44 @@ int unregister_fprobe(struct fprobe *fp) kfree_rcu(hlist_array, rcu); fp->hlist_array = NULL; - -out: - mutex_unlock(&fprobe_mutex); - kfree(addrs); + + return 0; +} + +/** + * unregister_fprobe_async() - Unregister fprobe without RCU GP wait + * @fp: A fprobe data structure to be unregistered. + * + * Unregister fprobe (and remove ftrace hooks from the function entries). + * This function will NOT wait until the fprobe is no longer used. + * + * Return 0 if @fp is unregistered successfully, -errno if not. + */ +int unregister_fprobe_async(struct fprobe *fp) +{ + guard(mutex)(&fprobe_mutex); + if (!fp || !fprobe_registered(fp)) + return -EINVAL; + + return unregister_fprobe_nolock(fp); +} + +/** + * unregister_fprobe() - Unregister fprobe with RCU GP wait + * @fp: A fprobe data structure to be unregistered. + * + * Unregister fprobe (and remove ftrace hooks from the function entries). + * This function will block until the fprobe is no longer used. + * + * Return 0 if @fp is unregistered successfully, -errno if not. + */ +int unregister_fprobe(struct fprobe *fp) +{ + int ret = unregister_fprobe_async(fp); + + if (!ret) + synchronize_rcu(); return ret; } EXPORT_SYMBOL_GPL(unregister_fprobe); diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 7eac1472cc57..b2611de3f594 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -6841,7 +6841,8 @@ bool ftrace_filter_param __initdata; static int __init set_ftrace_notrace(char *str) { ftrace_filter_param = true; - strscpy(ftrace_notrace_buf, str, FTRACE_FILTER_SIZE); + trace_append_boot_param(ftrace_notrace_buf, str, ',', + FTRACE_FILTER_SIZE); return 1; } __setup("ftrace_notrace=", set_ftrace_notrace); @@ -6849,7 +6850,8 @@ __setup("ftrace_notrace=", set_ftrace_notrace); static int __init set_ftrace_filter(char *str) { ftrace_filter_param = true; - strscpy(ftrace_filter_buf, str, FTRACE_FILTER_SIZE); + trace_append_boot_param(ftrace_filter_buf, str, ',', + FTRACE_FILTER_SIZE); return 1; } __setup("ftrace_filter=", set_ftrace_filter); @@ -6861,14 +6863,16 @@ static int ftrace_graph_set_hash(struct ftrace_hash *hash, char *buffer); static int __init set_graph_function(char *str) { - strscpy(ftrace_graph_buf, str, FTRACE_FILTER_SIZE); + trace_append_boot_param(ftrace_graph_buf, str, ',', + FTRACE_FILTER_SIZE); return 1; } __setup("ftrace_graph_filter=", set_graph_function); static int __init set_graph_notrace_function(char *str) { - strscpy(ftrace_graph_notrace_buf, str, FTRACE_FILTER_SIZE); + trace_append_boot_param(ftrace_graph_notrace_buf, str, ',', + FTRACE_FILTER_SIZE); return 1; } __setup("ftrace_graph_notrace=", set_graph_notrace_function); diff --git a/kernel/trace/remote_test.c b/kernel/trace/remote_test.c index 6c1b7701ddae..a3e2c9b606eb 100644 --- a/kernel/trace/remote_test.c +++ b/kernel/trace/remote_test.c @@ -110,9 +110,9 @@ static struct trace_buffer_desc *remote_test_load(unsigned long size, void *unus return remote_test_buffer_desc; err_unload: - for_each_ring_buffer_desc(rb_desc, cpu, remote_test_buffer_desc) + for_each_ring_buffer_desc(rb_desc, cpu, desc) remote_test_unload_simple_rb(rb_desc->cpu); - trace_remote_free_buffer(remote_test_buffer_desc); + trace_remote_free_buffer(desc); err_free_desc: kfree(desc); diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index cef49f8871d2..5326924615a4 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -1884,7 +1884,7 @@ static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu) static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) { struct ring_buffer_cpu_meta *meta = cpu_buffer->ring_meta; - struct buffer_page *head_page, *orig_head; + struct buffer_page *head_page, *orig_head, *orig_reader; unsigned long entry_bytes = 0; unsigned long entries = 0; int ret; @@ -1895,16 +1895,17 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) return; orig_head = head_page = cpu_buffer->head_page; + orig_reader = cpu_buffer->reader_page; /* Do the reader page first */ - ret = rb_validate_buffer(cpu_buffer->reader_page->page, cpu_buffer->cpu); + ret = rb_validate_buffer(orig_reader->page, cpu_buffer->cpu); if (ret < 0) { pr_info("Ring buffer reader page is invalid\n"); goto invalid; } entries += ret; - entry_bytes += local_read(&cpu_buffer->reader_page->page->commit); - local_set(&cpu_buffer->reader_page->entries, ret); + entry_bytes += local_read(&orig_reader->page->commit); + local_set(&orig_reader->entries, ret); ts = head_page->page->time_stamp; @@ -2007,8 +2008,8 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) /* Iterate until finding the commit page */ for (i = 0; i < meta->nr_subbufs + 1; i++, rb_inc_page(&head_page)) { - /* Reader page has already been done */ - if (head_page == cpu_buffer->reader_page) + /* The original reader page has already been checked/counted. */ + if (head_page == orig_reader) continue; ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index e9455d46ec16..6eb4d3097a4d 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #include #include @@ -219,14 +218,36 @@ static void ftrace_trace_userstack(struct trace_array *tr, static char bootup_tracer_buf[MAX_TRACER_SIZE] __initdata; static char *default_bootup_tracer; -static bool allocate_snapshot; -static bool snapshot_at_boot; - static char boot_instance_info[COMMAND_LINE_SIZE] __initdata; static int boot_instance_index; -static char boot_snapshot_info[COMMAND_LINE_SIZE] __initdata; -static int boot_snapshot_index; +/* + * Repeated boot parameters, including Bootconfig array expansions, need + * to stay in the delimiter form that the existing parser consumes. + */ +void __init trace_append_boot_param(char *buf, const char *str, char sep, + int size) +{ + int len, needed, str_len; + + if (!*str) + return; + + len = strlen(buf); + str_len = strlen(str); + needed = len + str_len + 1; + + /* For continuation, account for the separator. */ + if (len) + needed++; + if (needed > size) + return; + + if (len) + buf[len++] = sep; + + strscpy(buf + len, str, size - len); +} static int __init set_cmdline_ftrace(char *str) { @@ -276,38 +297,6 @@ static int __init stop_trace_on_warning(char *str) } __setup("traceoff_on_warning", stop_trace_on_warning); -static int __init boot_alloc_snapshot(char *str) -{ - char *slot = boot_snapshot_info + boot_snapshot_index; - int left = sizeof(boot_snapshot_info) - boot_snapshot_index; - int ret; - - if (str[0] == '=') { - str++; - if (strlen(str) >= left) - return -1; - - ret = snprintf(slot, left, "%s\t", str); - boot_snapshot_index += ret; - } else { - allocate_snapshot = true; - /* We also need the main ring buffer expanded */ - trace_set_ring_buffer_expanded(NULL); - } - return 1; -} -__setup("alloc_snapshot", boot_alloc_snapshot); - - -static int __init boot_snapshot(char *str) -{ - snapshot_at_boot = true; - boot_alloc_snapshot(str); - return 1; -} -__setup("ftrace_boot_snapshot", boot_snapshot); - - static int __init boot_instance(char *str) { char *slot = boot_instance_info + boot_instance_index; @@ -329,7 +318,8 @@ static char trace_boot_options_buf[MAX_TRACER_SIZE] __initdata; static int __init set_trace_boot_options(char *str) { - strscpy(trace_boot_options_buf, str, MAX_TRACER_SIZE); + trace_append_boot_param(trace_boot_options_buf, str, ',', + MAX_TRACER_SIZE); return 1; } __setup("trace_options=", set_trace_boot_options); @@ -863,47 +853,6 @@ void tracing_on(void) EXPORT_SYMBOL_GPL(tracing_on); #ifdef CONFIG_TRACER_SNAPSHOT -static void tracing_snapshot_instance_cond(struct trace_array *tr, - void *cond_data) -{ - unsigned long flags; - - if (in_nmi()) { - trace_array_puts(tr, "*** SNAPSHOT CALLED FROM NMI CONTEXT ***\n"); - trace_array_puts(tr, "*** snapshot is being ignored ***\n"); - return; - } - - if (!tr->allocated_snapshot) { - trace_array_puts(tr, "*** SNAPSHOT NOT ALLOCATED ***\n"); - trace_array_puts(tr, "*** stopping trace here! ***\n"); - tracer_tracing_off(tr); - return; - } - - if (tr->mapped) { - trace_array_puts(tr, "*** BUFFER MEMORY MAPPED ***\n"); - trace_array_puts(tr, "*** Can not use snapshot (sorry) ***\n"); - return; - } - - /* Note, snapshot can not be used when the tracer uses it */ - if (tracer_uses_snapshot(tr->current_trace)) { - trace_array_puts(tr, "*** LATENCY TRACER ACTIVE ***\n"); - trace_array_puts(tr, "*** Can not use snapshot (sorry) ***\n"); - return; - } - - local_irq_save(flags); - update_max_tr(tr, current, smp_processor_id(), cond_data); - local_irq_restore(flags); -} - -void tracing_snapshot_instance(struct trace_array *tr) -{ - tracing_snapshot_instance_cond(tr, NULL); -} - /** * tracing_snapshot - take a snapshot of the current buffer. * @@ -926,138 +875,6 @@ void tracing_snapshot(void) } EXPORT_SYMBOL_GPL(tracing_snapshot); -/** - * tracing_snapshot_cond - conditionally take a snapshot of the current buffer. - * @tr: The tracing instance to snapshot - * @cond_data: The data to be tested conditionally, and possibly saved - * - * This is the same as tracing_snapshot() except that the snapshot is - * conditional - the snapshot will only happen if the - * cond_snapshot.update() implementation receiving the cond_data - * returns true, which means that the trace array's cond_snapshot - * update() operation used the cond_data to determine whether the - * snapshot should be taken, and if it was, presumably saved it along - * with the snapshot. - */ -void tracing_snapshot_cond(struct trace_array *tr, void *cond_data) -{ - tracing_snapshot_instance_cond(tr, cond_data); -} -EXPORT_SYMBOL_GPL(tracing_snapshot_cond); - -/** - * tracing_cond_snapshot_data - get the user data associated with a snapshot - * @tr: The tracing instance - * - * When the user enables a conditional snapshot using - * tracing_snapshot_cond_enable(), the user-defined cond_data is saved - * with the snapshot. This accessor is used to retrieve it. - * - * Should not be called from cond_snapshot.update(), since it takes - * the tr->max_lock lock, which the code calling - * cond_snapshot.update() has already done. - * - * Returns the cond_data associated with the trace array's snapshot. - */ -void *tracing_cond_snapshot_data(struct trace_array *tr) -{ - void *cond_data = NULL; - - local_irq_disable(); - arch_spin_lock(&tr->max_lock); - - if (tr->cond_snapshot) - cond_data = tr->cond_snapshot->cond_data; - - arch_spin_unlock(&tr->max_lock); - local_irq_enable(); - - return cond_data; -} -EXPORT_SYMBOL_GPL(tracing_cond_snapshot_data); - -static int resize_buffer_duplicate_size(struct array_buffer *trace_buf, - struct array_buffer *size_buf, int cpu_id); -static void set_buffer_entries(struct array_buffer *buf, unsigned long val); - -int tracing_alloc_snapshot_instance(struct trace_array *tr) -{ - int order; - int ret; - - if (!tr->allocated_snapshot) { - - /* Make the snapshot buffer have the same order as main buffer */ - order = ring_buffer_subbuf_order_get(tr->array_buffer.buffer); - ret = ring_buffer_subbuf_order_set(tr->snapshot_buffer.buffer, order); - if (ret < 0) - return ret; - - /* allocate spare buffer */ - ret = resize_buffer_duplicate_size(&tr->snapshot_buffer, - &tr->array_buffer, RING_BUFFER_ALL_CPUS); - if (ret < 0) - return ret; - - tr->allocated_snapshot = true; - } - - return 0; -} - -static void free_snapshot(struct trace_array *tr) -{ - /* - * We don't free the ring buffer. instead, resize it because - * The max_tr ring buffer has some state (e.g. ring->clock) and - * we want preserve it. - */ - ring_buffer_subbuf_order_set(tr->snapshot_buffer.buffer, 0); - ring_buffer_resize(tr->snapshot_buffer.buffer, 1, RING_BUFFER_ALL_CPUS); - set_buffer_entries(&tr->snapshot_buffer, 1); - tracing_reset_online_cpus(&tr->snapshot_buffer); - tr->allocated_snapshot = false; -} - -static int tracing_arm_snapshot_locked(struct trace_array *tr) -{ - int ret; - - lockdep_assert_held(&trace_types_lock); - - spin_lock(&tr->snapshot_trigger_lock); - if (tr->snapshot == UINT_MAX || tr->mapped) { - spin_unlock(&tr->snapshot_trigger_lock); - return -EBUSY; - } - - tr->snapshot++; - spin_unlock(&tr->snapshot_trigger_lock); - - ret = tracing_alloc_snapshot_instance(tr); - if (ret) { - spin_lock(&tr->snapshot_trigger_lock); - tr->snapshot--; - spin_unlock(&tr->snapshot_trigger_lock); - } - - return ret; -} - -int tracing_arm_snapshot(struct trace_array *tr) -{ - guard(mutex)(&trace_types_lock); - return tracing_arm_snapshot_locked(tr); -} - -void tracing_disarm_snapshot(struct trace_array *tr) -{ - spin_lock(&tr->snapshot_trigger_lock); - if (!WARN_ON(!tr->snapshot)) - tr->snapshot--; - spin_unlock(&tr->snapshot_trigger_lock); -} - /** * tracing_alloc_snapshot - allocate snapshot buffer. * @@ -1078,159 +895,18 @@ int tracing_alloc_snapshot(void) return ret; } -EXPORT_SYMBOL_GPL(tracing_alloc_snapshot); - -/** - * tracing_snapshot_alloc - allocate and take a snapshot of the current buffer. - * - * This is similar to tracing_snapshot(), but it will allocate the - * snapshot buffer if it isn't already allocated. Use this only - * where it is safe to sleep, as the allocation may sleep. - * - * This causes a swap between the snapshot buffer and the current live - * tracing buffer. You can use this to take snapshots of the live - * trace when some condition is triggered, but continue to trace. - */ -void tracing_snapshot_alloc(void) -{ - int ret; - - ret = tracing_alloc_snapshot(); - if (ret < 0) - return; - - tracing_snapshot(); -} -EXPORT_SYMBOL_GPL(tracing_snapshot_alloc); - -/** - * tracing_snapshot_cond_enable - enable conditional snapshot for an instance - * @tr: The tracing instance - * @cond_data: User data to associate with the snapshot - * @update: Implementation of the cond_snapshot update function - * - * Check whether the conditional snapshot for the given instance has - * already been enabled, or if the current tracer is already using a - * snapshot; if so, return -EBUSY, else create a cond_snapshot and - * save the cond_data and update function inside. - * - * Returns 0 if successful, error otherwise. - */ -int tracing_snapshot_cond_enable(struct trace_array *tr, void *cond_data, - cond_update_fn_t update) -{ - struct cond_snapshot *cond_snapshot __free(kfree) = - kzalloc_obj(*cond_snapshot); - int ret; - - if (!cond_snapshot) - return -ENOMEM; - - cond_snapshot->cond_data = cond_data; - cond_snapshot->update = update; - - guard(mutex)(&trace_types_lock); - - if (tracer_uses_snapshot(tr->current_trace)) - return -EBUSY; - - /* - * The cond_snapshot can only change to NULL without the - * trace_types_lock. We don't care if we race with it going - * to NULL, but we want to make sure that it's not set to - * something other than NULL when we get here, which we can - * do safely with only holding the trace_types_lock and not - * having to take the max_lock. - */ - if (tr->cond_snapshot) - return -EBUSY; - - ret = tracing_arm_snapshot_locked(tr); - if (ret) - return ret; - - local_irq_disable(); - arch_spin_lock(&tr->max_lock); - tr->cond_snapshot = no_free_ptr(cond_snapshot); - arch_spin_unlock(&tr->max_lock); - local_irq_enable(); - - return 0; -} -EXPORT_SYMBOL_GPL(tracing_snapshot_cond_enable); - -/** - * tracing_snapshot_cond_disable - disable conditional snapshot for an instance - * @tr: The tracing instance - * - * Check whether the conditional snapshot for the given instance is - * enabled; if so, free the cond_snapshot associated with it, - * otherwise return -EINVAL. - * - * Returns 0 if successful, error otherwise. - */ -int tracing_snapshot_cond_disable(struct trace_array *tr) -{ - int ret = 0; - - local_irq_disable(); - arch_spin_lock(&tr->max_lock); - - if (!tr->cond_snapshot) - ret = -EINVAL; - else { - kfree(tr->cond_snapshot); - tr->cond_snapshot = NULL; - } - - arch_spin_unlock(&tr->max_lock); - local_irq_enable(); - - tracing_disarm_snapshot(tr); - - return ret; -} -EXPORT_SYMBOL_GPL(tracing_snapshot_cond_disable); #else void tracing_snapshot(void) { WARN_ONCE(1, "Snapshot feature not enabled, but internal snapshot used"); } EXPORT_SYMBOL_GPL(tracing_snapshot); -void tracing_snapshot_cond(struct trace_array *tr, void *cond_data) -{ - WARN_ONCE(1, "Snapshot feature not enabled, but internal conditional snapshot used"); -} -EXPORT_SYMBOL_GPL(tracing_snapshot_cond); -int tracing_alloc_snapshot(void) -{ - WARN_ONCE(1, "Snapshot feature not enabled, but snapshot allocation used"); - return -ENODEV; -} -EXPORT_SYMBOL_GPL(tracing_alloc_snapshot); void tracing_snapshot_alloc(void) { /* Give warning */ tracing_snapshot(); } EXPORT_SYMBOL_GPL(tracing_snapshot_alloc); -void *tracing_cond_snapshot_data(struct trace_array *tr) -{ - return NULL; -} -EXPORT_SYMBOL_GPL(tracing_cond_snapshot_data); -int tracing_snapshot_cond_enable(struct trace_array *tr, void *cond_data, cond_update_fn_t update) -{ - return -ENODEV; -} -EXPORT_SYMBOL_GPL(tracing_snapshot_cond_enable); -int tracing_snapshot_cond_disable(struct trace_array *tr) -{ - return false; -} -EXPORT_SYMBOL_GPL(tracing_snapshot_cond_disable); -#define free_snapshot(tr) do { } while (0) -#define tracing_arm_snapshot_locked(tr) ({ -EBUSY; }) #endif /* CONFIG_TRACER_SNAPSHOT */ void tracer_tracing_off(struct trace_array *tr) @@ -1543,206 +1219,6 @@ static ssize_t trace_seq_to_buffer(struct trace_seq *s, void *buf, size_t cnt) unsigned long __read_mostly tracing_thresh; -#ifdef CONFIG_TRACER_MAX_TRACE -#ifdef LATENCY_FS_NOTIFY -static struct workqueue_struct *fsnotify_wq; - -static void latency_fsnotify_workfn(struct work_struct *work) -{ - struct trace_array *tr = container_of(work, struct trace_array, - fsnotify_work); - fsnotify_inode(tr->d_max_latency->d_inode, FS_MODIFY); -} - -static void latency_fsnotify_workfn_irq(struct irq_work *iwork) -{ - struct trace_array *tr = container_of(iwork, struct trace_array, - fsnotify_irqwork); - queue_work(fsnotify_wq, &tr->fsnotify_work); -} - -__init static int latency_fsnotify_init(void) -{ - fsnotify_wq = alloc_workqueue("tr_max_lat_wq", - WQ_UNBOUND | WQ_HIGHPRI, 0); - if (!fsnotify_wq) { - pr_err("Unable to allocate tr_max_lat_wq\n"); - return -ENOMEM; - } - return 0; -} - -late_initcall_sync(latency_fsnotify_init); - -void latency_fsnotify(struct trace_array *tr) -{ - if (!fsnotify_wq) - return; - /* - * We cannot call queue_work(&tr->fsnotify_work) from here because it's - * possible that we are called from __schedule() or do_idle(), which - * could cause a deadlock. - */ - irq_work_queue(&tr->fsnotify_irqwork); -} -#endif /* !LATENCY_FS_NOTIFY */ - -static const struct file_operations tracing_max_lat_fops; - -static void trace_create_maxlat_file(struct trace_array *tr, - struct dentry *d_tracer) -{ -#ifdef LATENCY_FS_NOTIFY - INIT_WORK(&tr->fsnotify_work, latency_fsnotify_workfn); - init_irq_work(&tr->fsnotify_irqwork, latency_fsnotify_workfn_irq); -#endif - tr->d_max_latency = trace_create_file("tracing_max_latency", - TRACE_MODE_WRITE, - d_tracer, tr, - &tracing_max_lat_fops); -} - -/* - * Copy the new maximum trace into the separate maximum-trace - * structure. (this way the maximum trace is permanently saved, - * for later retrieval via /sys/kernel/tracing/tracing_max_latency) - */ -static void -__update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu) -{ - struct array_buffer *trace_buf = &tr->array_buffer; - struct trace_array_cpu *data = per_cpu_ptr(trace_buf->data, cpu); - struct array_buffer *max_buf = &tr->snapshot_buffer; - struct trace_array_cpu *max_data = per_cpu_ptr(max_buf->data, cpu); - - max_buf->cpu = cpu; - max_buf->time_start = data->preempt_timestamp; - - max_data->saved_latency = tr->max_latency; - max_data->critical_start = data->critical_start; - max_data->critical_end = data->critical_end; - - strscpy(max_data->comm, tsk->comm); - max_data->pid = tsk->pid; - /* - * If tsk == current, then use current_uid(), as that does not use - * RCU. The irq tracer can be called out of RCU scope. - */ - if (tsk == current) - max_data->uid = current_uid(); - else - max_data->uid = task_uid(tsk); - - max_data->nice = tsk->static_prio - 20 - MAX_RT_PRIO; - max_data->policy = tsk->policy; - max_data->rt_priority = tsk->rt_priority; - - /* record this tasks comm */ - tracing_record_cmdline(tsk); - latency_fsnotify(tr); -} -#else -static inline void trace_create_maxlat_file(struct trace_array *tr, - struct dentry *d_tracer) { } -static inline void __update_max_tr(struct trace_array *tr, - struct task_struct *tsk, int cpu) { } -#endif /* CONFIG_TRACER_MAX_TRACE */ - -#ifdef CONFIG_TRACER_SNAPSHOT -/** - * update_max_tr - snapshot all trace buffers from global_trace to max_tr - * @tr: tracer - * @tsk: the task with the latency - * @cpu: The cpu that initiated the trace. - * @cond_data: User data associated with a conditional snapshot - * - * Flip the buffers between the @tr and the max_tr and record information - * about which task was the cause of this latency. - */ -void -update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu, - void *cond_data) -{ - if (tr->stop_count) - return; - - WARN_ON_ONCE(!irqs_disabled()); - - if (!tr->allocated_snapshot) { - /* Only the nop tracer should hit this when disabling */ - WARN_ON_ONCE(tr->current_trace != &nop_trace); - return; - } - - arch_spin_lock(&tr->max_lock); - - /* Inherit the recordable setting from array_buffer */ - if (ring_buffer_record_is_set_on(tr->array_buffer.buffer)) - ring_buffer_record_on(tr->snapshot_buffer.buffer); - else - ring_buffer_record_off(tr->snapshot_buffer.buffer); - - if (tr->cond_snapshot && !tr->cond_snapshot->update(tr, cond_data)) { - arch_spin_unlock(&tr->max_lock); - return; - } - - swap(tr->array_buffer.buffer, tr->snapshot_buffer.buffer); - - __update_max_tr(tr, tsk, cpu); - - arch_spin_unlock(&tr->max_lock); - - /* Any waiters on the old snapshot buffer need to wake up */ - ring_buffer_wake_waiters(tr->array_buffer.buffer, RING_BUFFER_ALL_CPUS); -} - -/** - * update_max_tr_single - only copy one trace over, and reset the rest - * @tr: tracer - * @tsk: task with the latency - * @cpu: the cpu of the buffer to copy. - * - * Flip the trace of a single CPU buffer between the @tr and the max_tr. - */ -void -update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu) -{ - int ret; - - if (tr->stop_count) - return; - - WARN_ON_ONCE(!irqs_disabled()); - if (!tr->allocated_snapshot) { - /* Only the nop tracer should hit this when disabling */ - WARN_ON_ONCE(tr->current_trace != &nop_trace); - return; - } - - arch_spin_lock(&tr->max_lock); - - ret = ring_buffer_swap_cpu(tr->snapshot_buffer.buffer, tr->array_buffer.buffer, cpu); - - if (ret == -EBUSY) { - /* - * We failed to swap the buffer due to a commit taking - * place on this CPU. We fail to record, but we reset - * the max trace buffer (no one writes directly to it) - * and flag that it failed. - * Another reason is resize is in progress. - */ - trace_array_printk_buf(tr->snapshot_buffer.buffer, _THIS_IP_, - "Failed to swap buffers due to commit or resize in progress\n"); - } - - WARN_ON_ONCE(ret && ret != -EAGAIN && ret != -EBUSY); - - __update_max_tr(tr, tsk, cpu); - arch_spin_unlock(&tr->max_lock); -} -#endif /* CONFIG_TRACER_SNAPSHOT */ - struct pipe_wait { struct trace_iterator *iter; int wait_index; @@ -2051,7 +1527,7 @@ int __init register_tracer(struct tracer *type) return 0; } -static void tracing_reset_cpu(struct array_buffer *buf, int cpu) +void tracing_reset_cpu(struct array_buffer *buf, int cpu) { struct trace_buffer *buffer = buf->buffer; @@ -3816,50 +3292,6 @@ static void test_ftrace_alive(struct seq_file *m) "# MAY BE MISSING FUNCTION EVENTS\n"); } -#ifdef CONFIG_TRACER_SNAPSHOT -static void show_snapshot_main_help(struct seq_file *m) -{ - seq_puts(m, "# echo 0 > snapshot : Clears and frees snapshot buffer\n" - "# echo 1 > snapshot : Allocates snapshot buffer, if not already allocated.\n" - "# Takes a snapshot of the main buffer.\n" - "# echo 2 > snapshot : Clears snapshot buffer (but does not allocate or free)\n" - "# (Doesn't have to be '2' works with any number that\n" - "# is not a '0' or '1')\n"); -} - -static void show_snapshot_percpu_help(struct seq_file *m) -{ - seq_puts(m, "# echo 0 > snapshot : Invalid for per_cpu snapshot file.\n"); -#ifdef CONFIG_RING_BUFFER_ALLOW_SWAP - seq_puts(m, "# echo 1 > snapshot : Allocates snapshot buffer, if not already allocated.\n" - "# Takes a snapshot of the main buffer for this cpu.\n"); -#else - seq_puts(m, "# echo 1 > snapshot : Not supported with this kernel.\n" - "# Must use main snapshot file to allocate.\n"); -#endif - seq_puts(m, "# echo 2 > snapshot : Clears this cpu's snapshot buffer (but does not allocate)\n" - "# (Doesn't have to be '2' works with any number that\n" - "# is not a '0' or '1')\n"); -} - -static void print_snapshot_help(struct seq_file *m, struct trace_iterator *iter) -{ - if (iter->tr->allocated_snapshot) - seq_puts(m, "#\n# * Snapshot is allocated *\n#\n"); - else - seq_puts(m, "#\n# * Snapshot is freed *\n#\n"); - - seq_puts(m, "# Snapshot commands:\n"); - if (iter->cpu_file == RING_BUFFER_ALL_CPUS) - show_snapshot_main_help(m); - else - show_snapshot_percpu_help(m); -} -#else -/* Should never be called */ -static inline void print_snapshot_help(struct seq_file *m, struct trace_iterator *iter) { } -#endif - static int s_show(struct seq_file *m, void *v) { struct trace_iterator *iter = v; @@ -3908,17 +3340,6 @@ static int s_show(struct seq_file *m, void *v) return 0; } -/* - * Should be used after trace_array_get(), trace_types_lock - * ensures that i_cdev was already initialized. - */ -int tracing_get_cpu(struct inode *inode) -{ - if (inode->i_cdev) /* See trace_create_cpu_file() */ - return (long)inode->i_cdev - 1; - return RING_BUFFER_ALL_CPUS; -} - static const struct seq_operations tracer_seq_ops = { .start = s_start, .next = s_next, @@ -3945,7 +3366,7 @@ static void free_trace_iter_content(struct trace_iterator *iter) free_cpumask_var(iter->started); } -static struct trace_iterator * +struct trace_iterator * __tracing_open(struct inode *inode, struct file *file, bool snapshot) { struct trace_array *tr = inode->i_private; @@ -4111,8 +3532,6 @@ int tracing_open_file_tr(struct inode *inode, struct file *filp) event_file_get(file); } - filp->private_data = inode->i_private; - return 0; } @@ -4132,7 +3551,7 @@ int tracing_single_release_file_tr(struct inode *inode, struct file *filp) return single_release(inode, filp); } -static int tracing_release(struct inode *inode, struct file *file) +int tracing_release(struct inode *inode, struct file *file) { struct trace_array *tr = inode->i_private; struct seq_file *m = file->private_data; @@ -5283,7 +4702,7 @@ int tracer_init(struct tracer *t, struct trace_array *tr) return t->init(tr); } -static void set_buffer_entries(struct array_buffer *buf, unsigned long val) +void trace_set_buffer_entries(struct array_buffer *buf, unsigned long val) { int cpu; @@ -5294,40 +4713,12 @@ static void set_buffer_entries(struct array_buffer *buf, unsigned long val) static void update_buffer_entries(struct array_buffer *buf, int cpu) { if (cpu == RING_BUFFER_ALL_CPUS) { - set_buffer_entries(buf, ring_buffer_size(buf->buffer, 0)); + trace_set_buffer_entries(buf, ring_buffer_size(buf->buffer, 0)); } else { per_cpu_ptr(buf->data, cpu)->entries = ring_buffer_size(buf->buffer, cpu); } } -#ifdef CONFIG_TRACER_SNAPSHOT -/* resize @tr's buffer to the size of @size_tr's entries */ -static int resize_buffer_duplicate_size(struct array_buffer *trace_buf, - struct array_buffer *size_buf, int cpu_id) -{ - int cpu, ret = 0; - - if (cpu_id == RING_BUFFER_ALL_CPUS) { - for_each_tracing_cpu(cpu) { - ret = ring_buffer_resize(trace_buf->buffer, - per_cpu_ptr(size_buf->data, cpu)->entries, cpu); - if (ret < 0) - break; - per_cpu_ptr(trace_buf->data, cpu)->entries = - per_cpu_ptr(size_buf->data, cpu)->entries; - } - } else { - ret = ring_buffer_resize(trace_buf->buffer, - per_cpu_ptr(size_buf->data, cpu_id)->entries, cpu_id); - if (ret == 0) - per_cpu_ptr(trace_buf->data, cpu_id)->entries = - per_cpu_ptr(size_buf->data, cpu_id)->entries; - } - - return ret; -} -#endif /* CONFIG_TRACER_SNAPSHOT */ - static int __tracing_resize_ring_buffer(struct trace_array *tr, unsigned long size, int cpu) { @@ -5750,9 +5141,8 @@ tracing_set_trace_write(struct file *filp, const char __user *ubuf, return ret; } -static ssize_t -tracing_nsecs_read(unsigned long *ptr, char __user *ubuf, - size_t cnt, loff_t *ppos) +ssize_t tracing_nsecs_read(unsigned long *ptr, char __user *ubuf, + size_t cnt, loff_t *ppos) { char buf[64]; int r; @@ -5764,9 +5154,8 @@ tracing_nsecs_read(unsigned long *ptr, char __user *ubuf, return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); } -static ssize_t -tracing_nsecs_write(unsigned long *ptr, const char __user *ubuf, - size_t cnt, loff_t *ppos) +ssize_t tracing_nsecs_write(unsigned long *ptr, const char __user *ubuf, + size_t cnt, loff_t *ppos) { unsigned long val; int ret; @@ -5808,28 +5197,6 @@ tracing_thresh_write(struct file *filp, const char __user *ubuf, return cnt; } -#ifdef CONFIG_TRACER_MAX_TRACE - -static ssize_t -tracing_max_lat_read(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos) -{ - struct trace_array *tr = filp->private_data; - - return tracing_nsecs_read(&tr->max_latency, ubuf, cnt, ppos); -} - -static ssize_t -tracing_max_lat_write(struct file *filp, const char __user *ubuf, - size_t cnt, loff_t *ppos) -{ - struct trace_array *tr = filp->private_data; - - return tracing_nsecs_write(&tr->max_latency, ubuf, cnt, ppos); -} - -#endif - static int open_pipe_on_cpu(struct trace_array *tr, int cpu) { if (cpu == RING_BUFFER_ALL_CPUS) { @@ -7212,194 +6579,6 @@ u64 tracing_event_time_stamp(struct trace_buffer *buffer, struct ring_buffer_eve return ring_buffer_event_time_stamp(buffer, rbe); } -struct ftrace_buffer_info { - struct trace_iterator iter; - void *spare; - unsigned int spare_cpu; - unsigned int spare_size; - unsigned int read; -}; - -#ifdef CONFIG_TRACER_SNAPSHOT -static int tracing_snapshot_open(struct inode *inode, struct file *file) -{ - struct trace_array *tr = inode->i_private; - struct trace_iterator *iter; - struct seq_file *m; - int ret; - - ret = tracing_check_open_get_tr(tr); - if (ret) - return ret; - - if (file->f_mode & FMODE_READ) { - iter = __tracing_open(inode, file, true); - if (IS_ERR(iter)) - ret = PTR_ERR(iter); - } else { - /* Writes still need the seq_file to hold the private data */ - ret = -ENOMEM; - m = kzalloc_obj(*m); - if (!m) - goto out; - iter = kzalloc_obj(*iter); - if (!iter) { - kfree(m); - goto out; - } - ret = 0; - - iter->tr = tr; - iter->array_buffer = &tr->snapshot_buffer; - iter->cpu_file = tracing_get_cpu(inode); - m->private = iter; - file->private_data = m; - } -out: - if (ret < 0) - trace_array_put(tr); - - return ret; -} - -static void tracing_swap_cpu_buffer(void *tr) -{ - update_max_tr_single((struct trace_array *)tr, current, smp_processor_id()); -} - -static ssize_t -tracing_snapshot_write(struct file *filp, const char __user *ubuf, size_t cnt, - loff_t *ppos) -{ - struct seq_file *m = filp->private_data; - struct trace_iterator *iter = m->private; - struct trace_array *tr = iter->tr; - unsigned long val; - int ret; - - ret = tracing_update_buffers(tr); - if (ret < 0) - return ret; - - ret = kstrtoul_from_user(ubuf, cnt, 10, &val); - if (ret) - return ret; - - guard(mutex)(&trace_types_lock); - - if (tracer_uses_snapshot(tr->current_trace)) - return -EBUSY; - - local_irq_disable(); - arch_spin_lock(&tr->max_lock); - if (tr->cond_snapshot) - ret = -EBUSY; - arch_spin_unlock(&tr->max_lock); - local_irq_enable(); - if (ret) - return ret; - - switch (val) { - case 0: - if (iter->cpu_file != RING_BUFFER_ALL_CPUS) - return -EINVAL; - if (tr->allocated_snapshot) - free_snapshot(tr); - break; - case 1: -/* Only allow per-cpu swap if the ring buffer supports it */ -#ifndef CONFIG_RING_BUFFER_ALLOW_SWAP - if (iter->cpu_file != RING_BUFFER_ALL_CPUS) - return -EINVAL; -#endif - if (tr->allocated_snapshot) - ret = resize_buffer_duplicate_size(&tr->snapshot_buffer, - &tr->array_buffer, iter->cpu_file); - - ret = tracing_arm_snapshot_locked(tr); - if (ret) - return ret; - - /* Now, we're going to swap */ - if (iter->cpu_file == RING_BUFFER_ALL_CPUS) { - local_irq_disable(); - update_max_tr(tr, current, smp_processor_id(), NULL); - local_irq_enable(); - } else { - smp_call_function_single(iter->cpu_file, tracing_swap_cpu_buffer, - (void *)tr, 1); - } - tracing_disarm_snapshot(tr); - break; - default: - if (tr->allocated_snapshot) { - if (iter->cpu_file == RING_BUFFER_ALL_CPUS) - tracing_reset_online_cpus(&tr->snapshot_buffer); - else - tracing_reset_cpu(&tr->snapshot_buffer, iter->cpu_file); - } - break; - } - - if (ret >= 0) { - *ppos += cnt; - ret = cnt; - } - - return ret; -} - -static int tracing_snapshot_release(struct inode *inode, struct file *file) -{ - struct seq_file *m = file->private_data; - int ret; - - ret = tracing_release(inode, file); - - if (file->f_mode & FMODE_READ) - return ret; - - /* If write only, the seq_file is just a stub */ - if (m) - kfree(m->private); - kfree(m); - - return 0; -} - -static int tracing_buffers_open(struct inode *inode, struct file *filp); -static ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf, - size_t count, loff_t *ppos); -static int tracing_buffers_release(struct inode *inode, struct file *file); -static ssize_t tracing_buffers_splice_read(struct file *file, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, unsigned int flags); - -static int snapshot_raw_open(struct inode *inode, struct file *filp) -{ - struct ftrace_buffer_info *info; - int ret; - - /* The following checks for tracefs lockdown */ - ret = tracing_buffers_open(inode, filp); - if (ret < 0) - return ret; - - info = filp->private_data; - - if (tracer_uses_snapshot(info->iter.trace)) { - tracing_buffers_release(inode, filp); - return -EBUSY; - } - - info->iter.snapshot = true; - info->iter.array_buffer = &info->iter.tr->snapshot_buffer; - - return ret; -} - -#endif /* CONFIG_TRACER_SNAPSHOT */ - - static const struct file_operations tracing_thresh_fops = { .open = tracing_open_generic, .read = tracing_thresh_read, @@ -7407,16 +6586,6 @@ static const struct file_operations tracing_thresh_fops = { .llseek = generic_file_llseek, }; -#ifdef CONFIG_TRACER_MAX_TRACE -static const struct file_operations tracing_max_lat_fops = { - .open = tracing_open_generic_tr, - .read = tracing_max_lat_read, - .write = tracing_max_lat_write, - .llseek = generic_file_llseek, - .release = tracing_release_generic_tr, -}; -#endif - static const struct file_operations set_tracer_fops = { .open = tracing_open_generic_tr, .read = tracing_set_trace_read, @@ -7503,24 +6672,6 @@ static const struct file_operations last_boot_fops = { .release = tracing_seq_release, }; -#ifdef CONFIG_TRACER_SNAPSHOT -static const struct file_operations snapshot_fops = { - .open = tracing_snapshot_open, - .read = seq_read, - .write = tracing_snapshot_write, - .llseek = tracing_lseek, - .release = tracing_snapshot_release, -}; - -static const struct file_operations snapshot_raw_fops = { - .open = snapshot_raw_open, - .read = tracing_buffers_read, - .release = tracing_buffers_release, - .splice_read = tracing_buffers_splice_read, -}; - -#endif /* CONFIG_TRACER_SNAPSHOT */ - /* * trace_min_max_write - Write a u64 value to a trace_min_max_param struct * @filp: The active open file structure @@ -7880,7 +7031,7 @@ static const struct file_operations tracing_err_log_fops = { .release = tracing_err_log_release, }; -static int tracing_buffers_open(struct inode *inode, struct file *filp) +int tracing_buffers_open(struct inode *inode, struct file *filp) { struct trace_array *tr = inode->i_private; struct ftrace_buffer_info *info; @@ -7928,9 +7079,8 @@ tracing_buffers_poll(struct file *filp, poll_table *poll_table) return trace_poll(iter, filp, poll_table); } -static ssize_t -tracing_buffers_read(struct file *filp, char __user *ubuf, - size_t count, loff_t *ppos) +ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *ppos) { struct ftrace_buffer_info *info = filp->private_data; struct trace_iterator *iter = &info->iter; @@ -8031,7 +7181,7 @@ static int tracing_buffers_flush(struct file *file, fl_owner_t id) return 0; } -static int tracing_buffers_release(struct inode *inode, struct file *file) +int tracing_buffers_release(struct inode *inode, struct file *file) { struct ftrace_buffer_info *info = file->private_data; struct trace_iterator *iter = &info->iter; @@ -8105,10 +7255,9 @@ static void buffer_spd_release(struct splice_pipe_desc *spd, unsigned int i) spd->partial[i].private = 0; } -static ssize_t -tracing_buffers_splice_read(struct file *file, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) +ssize_t tracing_buffers_splice_read(struct file *file, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) { struct ftrace_buffer_info *info = file->private_data; struct trace_iterator *iter = &info->iter; @@ -8262,44 +7411,6 @@ static long tracing_buffers_ioctl(struct file *file, unsigned int cmd, unsigned return 0; } -#ifdef CONFIG_TRACER_SNAPSHOT -static int get_snapshot_map(struct trace_array *tr) -{ - int err = 0; - - /* - * Called with mmap_lock held. lockdep would be unhappy if we would now - * take trace_types_lock. Instead use the specific - * snapshot_trigger_lock. - */ - spin_lock(&tr->snapshot_trigger_lock); - - if (tr->snapshot || tr->mapped == UINT_MAX) - err = -EBUSY; - else - tr->mapped++; - - spin_unlock(&tr->snapshot_trigger_lock); - - /* Wait for update_max_tr() to observe iter->tr->mapped */ - if (tr->mapped == 1) - synchronize_rcu(); - - return err; - -} -static void put_snapshot_map(struct trace_array *tr) -{ - spin_lock(&tr->snapshot_trigger_lock); - if (!WARN_ON(!tr->mapped)) - tr->mapped--; - spin_unlock(&tr->snapshot_trigger_lock); -} -#else -static inline int get_snapshot_map(struct trace_array *tr) { return 0; } -static inline void put_snapshot_map(struct trace_array *tr) { } -#endif - /* * This is called when a VMA is duplicated (e.g., on fork()) to increment * the user_mapped counter without remapping pages. @@ -8480,170 +7591,6 @@ static const struct file_operations tracing_dyn_info_fops = { }; #endif /* CONFIG_DYNAMIC_FTRACE */ -#if defined(CONFIG_TRACER_SNAPSHOT) && defined(CONFIG_DYNAMIC_FTRACE) -static void -ftrace_snapshot(unsigned long ip, unsigned long parent_ip, - struct trace_array *tr, struct ftrace_probe_ops *ops, - void *data) -{ - tracing_snapshot_instance(tr); -} - -static void -ftrace_count_snapshot(unsigned long ip, unsigned long parent_ip, - struct trace_array *tr, struct ftrace_probe_ops *ops, - void *data) -{ - struct ftrace_func_mapper *mapper = data; - long *count = NULL; - - if (mapper) - count = (long *)ftrace_func_mapper_find_ip(mapper, ip); - - if (count) { - - if (*count <= 0) - return; - - (*count)--; - } - - tracing_snapshot_instance(tr); -} - -static int -ftrace_snapshot_print(struct seq_file *m, unsigned long ip, - struct ftrace_probe_ops *ops, void *data) -{ - struct ftrace_func_mapper *mapper = data; - long *count = NULL; - - seq_printf(m, "%ps:", (void *)ip); - - seq_puts(m, "snapshot"); - - if (mapper) - count = (long *)ftrace_func_mapper_find_ip(mapper, ip); - - if (count) - seq_printf(m, ":count=%ld\n", *count); - else - seq_puts(m, ":unlimited\n"); - - return 0; -} - -static int -ftrace_snapshot_init(struct ftrace_probe_ops *ops, struct trace_array *tr, - unsigned long ip, void *init_data, void **data) -{ - struct ftrace_func_mapper *mapper = *data; - - if (!mapper) { - mapper = allocate_ftrace_func_mapper(); - if (!mapper) - return -ENOMEM; - *data = mapper; - } - - return ftrace_func_mapper_add_ip(mapper, ip, init_data); -} - -static void -ftrace_snapshot_free(struct ftrace_probe_ops *ops, struct trace_array *tr, - unsigned long ip, void *data) -{ - struct ftrace_func_mapper *mapper = data; - - if (!ip) { - if (!mapper) - return; - free_ftrace_func_mapper(mapper, NULL); - return; - } - - ftrace_func_mapper_remove_ip(mapper, ip); -} - -static struct ftrace_probe_ops snapshot_probe_ops = { - .func = ftrace_snapshot, - .print = ftrace_snapshot_print, -}; - -static struct ftrace_probe_ops snapshot_count_probe_ops = { - .func = ftrace_count_snapshot, - .print = ftrace_snapshot_print, - .init = ftrace_snapshot_init, - .free = ftrace_snapshot_free, -}; - -static int -ftrace_trace_snapshot_callback(struct trace_array *tr, struct ftrace_hash *hash, - char *glob, char *cmd, char *param, int enable) -{ - struct ftrace_probe_ops *ops; - void *count = (void *)-1; - char *number; - int ret; - - if (!tr) - return -ENODEV; - - /* hash funcs only work with set_ftrace_filter */ - if (!enable) - return -EINVAL; - - ops = param ? &snapshot_count_probe_ops : &snapshot_probe_ops; - - if (glob[0] == '!') { - ret = unregister_ftrace_function_probe_func(glob+1, tr, ops); - if (!ret) - tracing_disarm_snapshot(tr); - - return ret; - } - - if (!param) - goto out_reg; - - number = strsep(¶m, ":"); - - if (!strlen(number)) - goto out_reg; - - /* - * We use the callback data field (which is a pointer) - * as our counter. - */ - ret = kstrtoul(number, 0, (unsigned long *)&count); - if (ret) - return ret; - - out_reg: - ret = tracing_arm_snapshot(tr); - if (ret < 0) - return ret; - - ret = register_ftrace_function_probe(glob, tr, ops, count); - if (ret < 0) - tracing_disarm_snapshot(tr); - - return ret < 0 ? ret : 0; -} - -static struct ftrace_func_command ftrace_snapshot_cmd = { - .name = "snapshot", - .func = ftrace_trace_snapshot_callback, -}; - -static __init int register_snapshot_cmd(void) -{ - return register_ftrace_command(&ftrace_snapshot_cmd); -} -#else -static inline __init int register_snapshot_cmd(void) { return 0; } -#endif /* defined(CONFIG_TRACER_SNAPSHOT) && defined(CONFIG_DYNAMIC_FTRACE) */ - static struct dentry *tracing_get_dentry(struct trace_array *tr) { /* Top directory uses NULL as the parent */ @@ -9436,8 +8383,7 @@ static void setup_trace_scratch(struct trace_array *tr, memset(tscratch, 0, size); } -static int -allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, unsigned long size) +int allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, int size) { enum ring_buffer_flags rb_flags; struct trace_scratch *tscratch; @@ -9476,8 +8422,8 @@ allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, unsigned } /* Allocate the first page for all buffers */ - set_buffer_entries(&tr->array_buffer, - ring_buffer_size(tr->array_buffer.buffer, 0)); + trace_set_buffer_entries(&tr->array_buffer, + ring_buffer_size(tr->array_buffer.buffer, 0)); return 0; } @@ -9500,23 +8446,11 @@ static int allocate_trace_buffers(struct trace_array *tr, unsigned long size) if (ret) return ret; -#ifdef CONFIG_TRACER_SNAPSHOT - /* Fix mapped buffer trace arrays do not have snapshot buffers */ - if (tr->range_addr_start) - return 0; - - ret = allocate_trace_buffer(tr, &tr->snapshot_buffer, - allocate_snapshot ? size : 1); - if (MEM_FAIL(ret, "Failed to allocate trace buffer\n")) { + ret = trace_allocate_snapshot(tr, size); + if (MEM_FAIL(ret, "Failed to allocate trace buffer\n")) free_trace_buffer(&tr->array_buffer); - return -ENOMEM; - } - tr->allocated_snapshot = allocate_snapshot; - allocate_snapshot = false; -#endif - - return 0; + return ret; } static void free_trace_buffers(struct trace_array *tr) @@ -10638,47 +9572,6 @@ ssize_t trace_parse_run_command(struct file *file, const char __user *buffer, return done; } -#ifdef CONFIG_TRACER_SNAPSHOT -__init static bool tr_needs_alloc_snapshot(const char *name) -{ - char *test; - int len = strlen(name); - bool ret; - - if (!boot_snapshot_index) - return false; - - if (strncmp(name, boot_snapshot_info, len) == 0 && - boot_snapshot_info[len] == '\t') - return true; - - test = kmalloc(strlen(name) + 3, GFP_KERNEL); - if (!test) - return false; - - sprintf(test, "\t%s\t", name); - ret = strstr(boot_snapshot_info, test) == NULL; - kfree(test); - return ret; -} - -__init static void do_allocate_snapshot(const char *name) -{ - if (!tr_needs_alloc_snapshot(name)) - return; - - /* - * When allocate_snapshot is set, the next call to - * allocate_trace_buffers() (called by trace_array_get_by_name()) - * will allocate the snapshot buffer. That will also clear - * this flag. - */ - allocate_snapshot = true; -} -#else -static inline void do_allocate_snapshot(const char *name) { } -#endif - __init static int backup_instance_area(const char *backup, unsigned long *addr, phys_addr_t *size) { @@ -10828,8 +9721,7 @@ __init static void enable_instances(void) } } else { /* Only non mapped buffers have snapshot buffers */ - if (IS_ENABLED(CONFIG_TRACER_SNAPSHOT)) - do_allocate_snapshot(name); + do_allocate_snapshot(name); } tr = trace_array_create_systems(name, NULL, addr, size); @@ -11045,24 +9937,6 @@ struct trace_array *trace_get_global_array(void) } #endif -void __init ftrace_boot_snapshot(void) -{ -#ifdef CONFIG_TRACER_SNAPSHOT - struct trace_array *tr; - - if (!snapshot_at_boot) - return; - - list_for_each_entry(tr, &ftrace_trace_arrays, list) { - if (!tr->allocated_snapshot) - continue; - - tracing_snapshot_instance(tr); - trace_array_puts(tr, "** Boot snapshot taken **\n"); - } -#endif -} - void __init early_trace_init(void) { if (tracepoint_printk) { diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index e68f9c2027eb..80fe152af1dd 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -264,6 +264,7 @@ static inline bool still_need_pid_events(int type, struct trace_pid_list *pid_li typedef bool (*cond_update_fn_t)(struct trace_array *tr, void *cond_data); +#ifdef CONFIG_TRACER_SNAPSHOT /** * struct cond_snapshot - conditional snapshot data and callback * @@ -306,6 +307,7 @@ struct cond_snapshot { void *cond_data; cond_update_fn_t update; }; +#endif /* CONFIG_TRACER_SNAPSHOT */ /* * struct trace_func_repeats - used to keep track of the consecutive @@ -691,6 +693,7 @@ void tracing_reset_all_online_cpus(void); void tracing_reset_all_online_cpus_unlocked(void); int tracing_open_generic(struct inode *inode, struct file *filp); int tracing_open_generic_tr(struct inode *inode, struct file *filp); +int tracing_release(struct inode *inode, struct file *file); int tracing_release_generic_tr(struct inode *inode, struct file *file); int tracing_open_file_tr(struct inode *inode, struct file *filp); int tracing_release_file_tr(struct inode *inode, struct file *filp); @@ -700,6 +703,7 @@ void tracer_tracing_on(struct trace_array *tr); void tracer_tracing_off(struct trace_array *tr); void tracer_tracing_disable(struct trace_array *tr); void tracer_tracing_enable(struct trace_array *tr); +int allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, int size); struct dentry *trace_create_file(const char *name, umode_t mode, struct dentry *parent, @@ -711,8 +715,42 @@ struct dentry *trace_create_cpu_file(const char *name, void *data, long cpu, const struct file_operations *fops); -int tracing_get_cpu(struct inode *inode); +struct trace_iterator *__tracing_open(struct inode *inode, struct file *file, + bool snapshot); +int tracing_buffers_open(struct inode *inode, struct file *filp); +ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *ppos); +int tracing_buffers_release(struct inode *inode, struct file *file); +ssize_t tracing_buffers_splice_read(struct file *file, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, unsigned int flags); + +ssize_t tracing_nsecs_read(unsigned long *ptr, char __user *ubuf, + size_t cnt, loff_t *ppos); +ssize_t tracing_nsecs_write(unsigned long *ptr, const char __user *ubuf, + size_t cnt, loff_t *ppos); + +void trace_set_buffer_entries(struct array_buffer *buf, unsigned long val); + +/* + * Should be used after trace_array_get(), trace_types_lock + * ensures that i_cdev was already initialized. + */ +static inline int tracing_get_cpu(struct inode *inode) +{ + if (inode->i_cdev) /* See trace_create_cpu_file() */ + return (long)inode->i_cdev - 1; + return RING_BUFFER_ALL_CPUS; +} +void tracing_reset_cpu(struct array_buffer *buf, int cpu); + +struct ftrace_buffer_info { + struct trace_iterator iter; + void *spare; + unsigned int spare_cpu; + unsigned int spare_size; + unsigned int read; +}; /** * tracer_tracing_is_on_cpu - show real state of ring buffer enabled on for a cpu @@ -829,13 +867,13 @@ void update_max_tr_single(struct trace_array *tr, #if defined(CONFIG_TRACER_MAX_TRACE) && defined(CONFIG_FSNOTIFY) # define LATENCY_FS_NOTIFY #endif +#endif /* CONFIG_TRACER_SNAPSHOT */ #ifdef LATENCY_FS_NOTIFY void latency_fsnotify(struct trace_array *tr); #else static inline void latency_fsnotify(struct trace_array *tr) { } #endif -#endif /* CONFIG_TRACER_SNAPSHOT */ #ifdef CONFIG_STACKTRACE void __trace_stack(struct trace_array *tr, unsigned int trace_ctx, int skip); @@ -851,11 +889,15 @@ static inline bool tracer_uses_snapshot(struct tracer *tracer) { return tracer->use_max_tr; } +void trace_create_maxlat_file(struct trace_array *tr, + struct dentry *d_tracer); #else static inline bool tracer_uses_snapshot(struct tracer *tracer) { return false; } +static inline void trace_create_maxlat_file(struct trace_array *tr, + struct dentry *d_tracer) { } #endif void trace_last_func_repeats(struct trace_array *tr, @@ -885,6 +927,8 @@ extern int DYN_FTRACE_TEST_NAME(void); #define DYN_FTRACE_TEST_NAME2 trace_selftest_dynamic_test_func2 extern int DYN_FTRACE_TEST_NAME2(void); +void __init trace_append_boot_param(char *buf, const char *str, + char sep, int size); extern void trace_set_ring_buffer_expanded(struct trace_array *tr); extern bool tracing_selftest_disabled; @@ -1825,11 +1869,6 @@ extern struct trace_event_file *find_event_file(struct trace_array *tr, const char *system, const char *event); -static inline void *event_file_data(struct file *filp) -{ - return READ_ONCE(file_inode(filp)->i_private); -} - extern struct mutex event_mutex; extern struct list_head ftrace_events; @@ -1850,12 +1889,22 @@ static inline struct trace_event_file *event_file_file(struct file *filp) struct trace_event_file *file; lockdep_assert_held(&event_mutex); - file = READ_ONCE(file_inode(filp)->i_private); + file = file_inode(filp)->i_private; if (!file || file->flags & EVENT_FILE_FL_FREED) return NULL; return file; } +static inline void *event_file_data(struct file *filp) +{ + struct trace_event_file *file; + + lockdep_assert_held(&event_mutex); + file = file_inode(filp)->i_private; + WARN_ON(!file || file->flags & EVENT_FILE_FL_FREED); + return file; +} + extern const struct file_operations event_trigger_fops; extern const struct file_operations event_hist_fops; extern const struct file_operations event_hist_debug_fops; @@ -2158,12 +2207,6 @@ static inline bool event_command_needs_rec(struct event_command *cmd_ops) extern int trace_event_enable_disable(struct trace_event_file *file, int enable, int soft_disable); -extern int tracing_alloc_snapshot(void); -extern void tracing_snapshot_cond(struct trace_array *tr, void *cond_data); -extern int tracing_snapshot_cond_enable(struct trace_array *tr, void *cond_data, cond_update_fn_t update); - -extern int tracing_snapshot_cond_disable(struct trace_array *tr); -extern void *tracing_cond_snapshot_data(struct trace_array *tr); extern const char *__start___trace_bprintk_fmt[]; extern const char *__stop___trace_bprintk_fmt[]; @@ -2251,19 +2294,71 @@ static inline void trace_event_update_all(struct trace_eval_map **map, int len) #endif #ifdef CONFIG_TRACER_SNAPSHOT +extern const struct file_operations snapshot_fops; +extern const struct file_operations snapshot_raw_fops; + +/* Used when creating instances */ +int trace_allocate_snapshot(struct trace_array *tr, int size); + +int tracing_alloc_snapshot(void); +void tracing_snapshot_cond(struct trace_array *tr, void *cond_data); +int tracing_snapshot_cond_enable(struct trace_array *tr, void *cond_data, cond_update_fn_t update); +int tracing_snapshot_cond_disable(struct trace_array *tr); +void *tracing_cond_snapshot_data(struct trace_array *tr); void tracing_snapshot_instance(struct trace_array *tr); int tracing_alloc_snapshot_instance(struct trace_array *tr); +int tracing_arm_snapshot_locked(struct trace_array *tr); int tracing_arm_snapshot(struct trace_array *tr); void tracing_disarm_snapshot(struct trace_array *tr); -#else +void free_snapshot(struct trace_array *tr); +void print_snapshot_help(struct seq_file *m, struct trace_iterator *iter); +int get_snapshot_map(struct trace_array *tr); +void put_snapshot_map(struct trace_array *tr); +int resize_buffer_duplicate_size(struct array_buffer *trace_buf, + struct array_buffer *size_buf, int cpu_id); +__init void do_allocate_snapshot(const char *name); +# ifdef CONFIG_DYNAMIC_FTRACE +__init int register_snapshot_cmd(void); +# else +static inline int register_snapshot_cmd(void) { return 0; } +# endif +#else /* !CONFIG_TRACER_SNAPSHOT */ +static inline int trace_allocate_snapshot(struct trace_array *tr, int size) { return 0; } static inline void tracing_snapshot_instance(struct trace_array *tr) { } static inline int tracing_alloc_snapshot_instance(struct trace_array *tr) { return 0; } +static inline int tracing_arm_snapshot_locked(struct trace_array *tr) { return -EBUSY; } static inline int tracing_arm_snapshot(struct trace_array *tr) { return 0; } static inline void tracing_disarm_snapshot(struct trace_array *tr) { } -#endif +static inline void free_snapshot(struct trace_array *tr) {} +static inline void tracing_snapshot_cond(struct trace_array *tr, void *cond_data) +{ + WARN_ONCE(1, "Snapshot feature not enabled, but internal conditional snapshot used"); +} +static inline void *tracing_cond_snapshot_data(struct trace_array *tr) +{ + return NULL; +} +static inline int tracing_snapshot_cond_enable(struct trace_array *tr, void *cond_data, cond_update_fn_t update) +{ + return -ENODEV; +} +static inline int tracing_snapshot_cond_disable(struct trace_array *tr) +{ + return false; +} +static inline void print_snapshot_help(struct seq_file *m, struct trace_iterator *iter) +{ + /* Should never be called */ + WARN_ONCE(1, "Snapshot print function called without snapshot configured"); +} +static inline int get_snapshot_map(struct trace_array *tr) { return 0; } +static inline void put_snapshot_map(struct trace_array *tr) { } +static inline void do_allocate_snapshot(const char *name) { } +static inline int register_snapshot_cmd(void) { return 0; } +#endif /* CONFIG_TRACER_SNAPSHOT */ #ifdef CONFIG_PREEMPT_TRACER void tracer_preempt_on(unsigned long a0, unsigned long a1); diff --git a/kernel/trace/trace_branch.c b/kernel/trace/trace_branch.c index 6809b370e991..d1564db95a8f 100644 --- a/kernel/trace/trace_branch.c +++ b/kernel/trace/trace_branch.c @@ -373,10 +373,10 @@ __init static int init_annotated_branch_stats(void) int ret; ret = register_stat_tracer(&annotated_branch_stats); - if (!ret) { + if (ret) { printk(KERN_WARNING "Warning: could not register " "annotated branches stats\n"); - return 1; + return ret; } return 0; } @@ -438,10 +438,10 @@ __init static int all_annotated_branch_stats(void) int ret; ret = register_stat_tracer(&all_branch_stats); - if (!ret) { + if (ret) { printk(KERN_WARNING "Warning: could not register " "all branches stats\n"); - return 1; + return ret; } return 0; } diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index aa422dc80ae8..c46e623e7e0d 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1721,7 +1721,7 @@ static int t_show_filters(struct seq_file *m, void *v) len = get_call_len(call); - seq_printf(m, "%s:%s%*.s%s\n", call->class->system, + seq_printf(m, "%s:%s%*s%s\n", call->class->system, trace_event_name(call), len, "", filter->filter_string); return 0; @@ -1753,7 +1753,7 @@ static int t_show_triggers(struct seq_file *m, void *v) len = get_call_len(call); list_for_each_entry_rcu(data, &file->triggers, list) { - seq_printf(m, "%s:%s%*.s", call->class->system, + seq_printf(m, "%s:%s%*s", call->class->system, trace_event_name(call), len, ""); data->cmd_ops->print(m, data); @@ -2187,12 +2187,12 @@ static int trace_format_open(struct inode *inode, struct file *file) static ssize_t event_id_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { - int id = (long)event_file_data(filp); + /* id is directly in i_private and available for inode's lifetime. */ + int id = (long)file_inode(filp)->i_private; char buf[32]; int len; - if (unlikely(!id)) - return -ENODEV; + WARN_ON(!id); len = sprintf(buf, "%d\n", id); @@ -2250,12 +2250,8 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, mutex_lock(&event_mutex); file = event_file_file(filp); - if (file) { - if (file->flags & EVENT_FILE_FL_FREED) - err = -ENODEV; - else - err = apply_event_filter(file, buf); - } + if (file) + err = apply_event_filter(file, buf); mutex_unlock(&event_mutex); kfree(buf); @@ -3687,20 +3683,27 @@ static struct boot_triggers { } bootup_triggers[MAX_BOOT_TRIGGERS]; static char bootup_trigger_buf[COMMAND_LINE_SIZE]; +static int boot_trigger_buf_len; static int nr_boot_triggers; static __init int setup_trace_triggers(char *str) { char *trigger; char *buf; + int len = boot_trigger_buf_len; int i; - strscpy(bootup_trigger_buf, str, COMMAND_LINE_SIZE); + if (len >= COMMAND_LINE_SIZE) + return 1; + + strscpy(bootup_trigger_buf + len, str, COMMAND_LINE_SIZE - len); trace_set_ring_buffer_expanded(NULL); disable_tracing_selftest("running event triggers"); - buf = bootup_trigger_buf; - for (i = 0; i < MAX_BOOT_TRIGGERS; i++) { + buf = bootup_trigger_buf + len; + boot_trigger_buf_len += strlen(buf) + 1; + + for (i = nr_boot_triggers; i < MAX_BOOT_TRIGGERS; i++) { trigger = strsep(&buf, ","); if (!trigger) break; diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 73ea180cad55..0dbbf6cca9bc 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -1361,12 +1361,17 @@ static const char *hist_field_name(struct hist_field *field, field->flags & HIST_FIELD_FL_VAR_REF) { if (field->system) { static char full_name[MAX_FILTER_STR_VAL]; + static char *fmt; + int len; + + fmt = field->flags & HIST_FIELD_FL_VAR_REF ? "%s.%s.$%s" : "%s.%s.%s"; + + len = snprintf(full_name, sizeof(full_name), fmt, + field->system, field->event_name, + field->name); + if (len >= sizeof(full_name)) + return NULL; - strcat(full_name, field->system); - strcat(full_name, "."); - strcat(full_name, field->event_name); - strcat(full_name, "."); - strcat(full_name, field->name); field_name = full_name; } else field_name = field->name; @@ -1740,9 +1745,10 @@ static const char *get_hist_field_flags(struct hist_field *hist_field) static void expr_field_str(struct hist_field *field, char *expr) { - if (field->flags & HIST_FIELD_FL_VAR_REF) - strcat(expr, "$"); - else if (field->flags & HIST_FIELD_FL_CONST) { + if (field->flags & HIST_FIELD_FL_VAR_REF) { + if (!field->system) + strcat(expr, "$"); + } else if (field->flags & HIST_FIELD_FL_CONST) { char str[HIST_CONST_DIGITS_MAX]; snprintf(str, HIST_CONST_DIGITS_MAX, "%llu", field->constant); @@ -5836,8 +5842,6 @@ static int event_hist_open(struct inode *inode, struct file *file) hist_file->file = file; hist_file->last_act = get_hist_hit_count(event_file); - /* Clear private_data to avoid warning in single_open() */ - file->private_data = NULL; ret = single_open(file, hist_show, hist_file); if (ret) { kfree(hist_file); @@ -6126,8 +6130,6 @@ static int event_hist_debug_open(struct inode *inode, struct file *file) if (ret) return ret; - /* Clear private_data to avoid warning in single_open() */ - file->private_data = NULL; ret = single_open(file, hist_debug_show, file); if (ret) tracing_release_file_tr(inode, file); @@ -6158,7 +6160,8 @@ static void hist_field_print(struct seq_file *m, struct hist_field *hist_field) else if (field_name) { if (hist_field->flags & HIST_FIELD_FL_VAR_REF || hist_field->flags & HIST_FIELD_FL_ALIAS) - seq_putc(m, '$'); + if (!hist_field->system) + seq_putc(m, '$'); seq_printf(m, "%s", field_name); } else if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP) seq_puts(m, "common_timestamp"); diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 058724c41c46..a8420e6abb56 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -31,7 +31,8 @@ static char kprobe_boot_events_buf[COMMAND_LINE_SIZE] __initdata; static int __init set_kprobe_boot_events(char *str) { - strscpy(kprobe_boot_events_buf, str, COMMAND_LINE_SIZE); + trace_append_boot_param(kprobe_boot_events_buf, str, ';', + COMMAND_LINE_SIZE); disable_tracing_selftest("running kprobe events"); return 1; diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c index be6cf0bb3c03..75678053b21c 100644 --- a/kernel/trace/trace_osnoise.c +++ b/kernel/trace/trace_osnoise.c @@ -58,6 +58,7 @@ enum osnoise_options_index { OSN_PANIC_ON_STOP, OSN_PREEMPT_DISABLE, OSN_IRQ_DISABLE, + OSN_TIMERLAT_ALIGN, OSN_MAX }; @@ -66,7 +67,8 @@ static const char * const osnoise_options_str[OSN_MAX] = { "OSNOISE_WORKLOAD", "PANIC_ON_STOP", "OSNOISE_PREEMPT_DISABLE", - "OSNOISE_IRQ_DISABLE" }; + "OSNOISE_IRQ_DISABLE", + "TIMERLAT_ALIGN" }; #define OSN_DEFAULT_OPTIONS 0x2 static unsigned long osnoise_options = OSN_DEFAULT_OPTIONS; @@ -250,6 +252,11 @@ struct timerlat_variables { static DEFINE_PER_CPU(struct timerlat_variables, per_cpu_timerlat_var); +/* + * timerlat wake-up offset for next thread with TIMERLAT_ALIGN set. + */ +static atomic64_t align_next; + /* * this_cpu_tmr_var - Return the per-cpu timerlat_variables on its relative CPU */ @@ -268,6 +275,7 @@ static inline void tlat_var_reset(void) /* Synchronize with the timerlat interfaces */ mutex_lock(&interface_lock); + /* * So far, all the values are initialized as 0, so * zeroing the structure is perfect. @@ -278,6 +286,12 @@ static inline void tlat_var_reset(void) hrtimer_cancel(&tlat_var->timer); memset(tlat_var, 0, sizeof(*tlat_var)); } + /* + * Reset also align_next, to be filled by a new offset by the first timerlat + * thread that wakes up, if TIMERLAT_ALIGN is set. + */ + atomic64_set(&align_next, 0); + mutex_unlock(&interface_lock); } #else /* CONFIG_TIMERLAT_TRACER */ @@ -326,6 +340,7 @@ static struct osnoise_data { u64 stop_tracing_total; /* stop trace in the final operation (report/thread) */ #ifdef CONFIG_TIMERLAT_TRACER u64 timerlat_period; /* timerlat period */ + u64 timerlat_align_us; /* timerlat alignment */ u64 print_stack; /* print IRQ stack if total > */ int timerlat_tracer; /* timerlat tracer */ #endif @@ -338,6 +353,7 @@ static struct osnoise_data { #ifdef CONFIG_TIMERLAT_TRACER .print_stack = 0, .timerlat_period = DEFAULT_TIMERLAT_PERIOD, + .timerlat_align_us = 0, .timerlat_tracer = 0, #endif }; @@ -1829,6 +1845,26 @@ static int wait_next_period(struct timerlat_variables *tlat) */ tlat->abs_period = (u64) ktime_to_ns(next_abs_period); + /* + * Align thread in the first cycle on each CPU to the set alignment + * if TIMERLAT_ALIGN is set. + * + * This is done by using an atomic64_t to store the next absolute period. + * The first thread that wakes up will set the atomic64_t to its + * absolute period, and the other threads will increment it by + * the alignment value. + */ + if (test_bit(OSN_TIMERLAT_ALIGN, &osnoise_options) && !tlat->count + && atomic64_cmpxchg_relaxed(&align_next, 0, tlat->abs_period)) { + /* + * A thread has already set align_next, use it and increment it + * to be used by the next thread that wakes up after this one. + */ + tlat->abs_period = atomic64_add_return_relaxed( + osnoise_data.timerlat_align_us * 1000, &align_next); + next_abs_period = ns_to_ktime(tlat->abs_period); + } + /* * If the new abs_period is in the past, skip the activation. */ @@ -2650,6 +2686,17 @@ static struct trace_min_max_param timerlat_period = { .min = &timerlat_min_period, }; +/* + * osnoise/timerlat_align_us: align the first wakeup of all timerlat + * threads to a common boundary (in us). 0 means disabled. + */ +static struct trace_min_max_param timerlat_align_us = { + .lock = &interface_lock, + .val = &osnoise_data.timerlat_align_us, + .max = NULL, + .min = NULL, +}; + static const struct file_operations timerlat_fd_fops = { .open = timerlat_fd_open, .read = timerlat_fd_read, @@ -2746,6 +2793,11 @@ static int init_timerlat_tracefs(struct dentry *top_dir) if (!tmp) return -ENOMEM; + tmp = tracefs_create_file("timerlat_align_us", TRACE_MODE_WRITE, top_dir, + &timerlat_align_us, &trace_min_max_fops); + if (!tmp) + return -ENOMEM; + retval = osnoise_create_cpu_timerlat_fd(top_dir); if (retval) return retval; diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 96e2d22b4364..a5ad76175d10 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -723,12 +723,13 @@ void print_function_args(struct trace_seq *s, unsigned long *args, { const struct btf_param *param; const struct btf_type *t; + const struct btf_enum *enums; const char *param_name; char name[KSYM_NAME_LEN]; unsigned long arg; struct btf *btf; s32 tid, nr = 0; - int a, p, x; + int a, p, x, i; u16 encode; trace_seq_printf(s, "("); @@ -782,6 +783,15 @@ void print_function_args(struct trace_seq *s, unsigned long *args, break; case BTF_KIND_ENUM: trace_seq_printf(s, "%ld", arg); + enums = btf_enum(t); + for (i = 0; i < btf_vlen(t); i++) { + if (arg == enums[i].val) { + trace_seq_printf(s, " [%s]", + btf_name_by_offset(btf, + enums[i].name_off)); + break; + } + } break; default: /* This does not handle complex arguments */ diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c index 5ea5e0d76f00..3ea17af60169 100644 --- a/kernel/trace/trace_printk.c +++ b/kernel/trace/trace_printk.c @@ -197,6 +197,7 @@ struct notifier_block module_trace_bprintk_format_nb = { .notifier_call = module_trace_bprintk_format_notify, }; +__printf(2, 3) int __trace_bprintk(unsigned long ip, const char *fmt, ...) { int ret; diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index e1c73065dae5..e0d3a0da26af 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -1523,6 +1523,12 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, parg->offset = *size; *size += parg->type->size * (parg->count ?: 1); + if (*size > MAX_PROBE_EVENT_SIZE) { + ret = -E2BIG; + trace_probe_log_err(ctx->offset, EVENT_TOO_BIG); + goto fail; + } + if (parg->count) { len = strlen(parg->type->fmttype) + 6; parg->fmt = kmalloc(len, GFP_KERNEL); diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 9fc56c937130..262d8707a3df 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -38,6 +38,7 @@ #define MAX_BTF_ARGS_LEN 128 #define MAX_DENTRY_ARGS_LEN 256 #define MAX_STRING_SIZE PATH_MAX +#define MAX_PROBE_EVENT_SIZE 3072 /* Reserved field names */ #define FIELD_STRING_IP "__probe_ip" @@ -561,7 +562,8 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, C(BAD_TYPE4STR, "This type does not fit for string."),\ C(NEED_STRING_TYPE, "$comm and immediate-string only accepts string type"),\ C(TOO_MANY_ARGS, "Too many arguments are specified"), \ - C(TOO_MANY_EARGS, "Too many entry arguments specified"), + C(TOO_MANY_EARGS, "Too many entry arguments specified"), \ + C(EVENT_TOO_BIG, "Event too big (too many fields?)"), #undef C #define C(a, b) TP_ERR_##a diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c index 0d78e5f5fe98..d6c3f94d67cd 100644 --- a/kernel/trace/trace_remote.c +++ b/kernel/trace/trace_remote.c @@ -282,6 +282,14 @@ static void trace_remote_put(struct trace_remote *remote) trace_remote_try_unload(remote); } +static bool trace_remote_has_cpu(struct trace_remote *remote, int cpu) +{ + if (cpu == RING_BUFFER_ALL_CPUS) + return true; + + return ring_buffer_poll_remote(remote->trace_buffer, cpu) == 0; +} + static void __poll_remote(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); @@ -324,6 +332,10 @@ static int __alloc_ring_buffer_iter(struct trace_remote_iterator *iter, int cpu) iter->rb_iters[cpu] = ring_buffer_read_start(iter->remote->trace_buffer, cpu, GFP_KERNEL); if (!iter->rb_iters[cpu]) { + /* This CPU isn't part of trace_buffer. Skip it */ + if (!trace_remote_has_cpu(iter->remote, cpu)) + continue; + __free_ring_buffer_iter(iter, RING_BUFFER_ALL_CPUS); return -ENOMEM; } @@ -347,10 +359,10 @@ static struct trace_remote_iterator if (ret) return ERR_PTR(ret); - /* Test the CPU */ - ret = ring_buffer_poll_remote(remote->trace_buffer, cpu); - if (ret) + if (!trace_remote_has_cpu(remote, cpu)) { + ret = -ENODEV; goto err; + } iter = kzalloc_obj(*iter); if (iter) { @@ -361,6 +373,7 @@ static struct trace_remote_iterator switch (type) { case TRI_CONSUMING: + ring_buffer_poll_remote(remote->trace_buffer, cpu); INIT_DELAYED_WORK(&iter->poll_work, __poll_remote); schedule_delayed_work(&iter->poll_work, msecs_to_jiffies(remote->poll_ms)); break; @@ -476,6 +489,9 @@ __peek_event(struct trace_remote_iterator *iter, int cpu, u64 *ts, unsigned long return ring_buffer_peek(iter->remote->trace_buffer, cpu, ts, lost_events); case TRI_NONCONSUMING: rb_iter = __get_rb_iter(iter, cpu); + if (!rb_iter) + return NULL; + rb_evt = ring_buffer_iter_peek(rb_iter, ts); if (!rb_evt) return NULL; diff --git a/kernel/trace/trace_snapshot.c b/kernel/trace/trace_snapshot.c new file mode 100644 index 000000000000..07b43c9863a2 --- /dev/null +++ b/kernel/trace/trace_snapshot.c @@ -0,0 +1,1066 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +#include /* COMMAND_LINE_SIZE */ + +#include "trace.h" + +/* Used if snapshot allocated at boot */ +static bool allocate_snapshot; +static bool snapshot_at_boot; + +static char boot_snapshot_info[COMMAND_LINE_SIZE] __initdata; +static int boot_snapshot_index; + +static int __init boot_alloc_snapshot(char *str) +{ + char *slot = boot_snapshot_info + boot_snapshot_index; + int left = sizeof(boot_snapshot_info) - boot_snapshot_index; + int ret; + + if (str[0] == '=') { + str++; + if (strlen(str) >= left) + return -1; + + ret = snprintf(slot, left, "%s\t", str); + boot_snapshot_index += ret; + } else { + allocate_snapshot = true; + /* We also need the main ring buffer expanded */ + trace_set_ring_buffer_expanded(NULL); + } + return 1; +} +__setup("alloc_snapshot", boot_alloc_snapshot); + + +static int __init boot_snapshot(char *str) +{ + snapshot_at_boot = true; + boot_alloc_snapshot(str); + return 1; +} +__setup("ftrace_boot_snapshot", boot_snapshot); +static void tracing_snapshot_instance_cond(struct trace_array *tr, + void *cond_data) +{ + unsigned long flags; + + if (in_nmi()) { + trace_array_puts(tr, "*** SNAPSHOT CALLED FROM NMI CONTEXT ***\n"); + trace_array_puts(tr, "*** snapshot is being ignored ***\n"); + return; + } + + if (!tr->allocated_snapshot) { + trace_array_puts(tr, "*** SNAPSHOT NOT ALLOCATED ***\n"); + trace_array_puts(tr, "*** stopping trace here! ***\n"); + tracer_tracing_off(tr); + return; + } + + if (tr->mapped) { + trace_array_puts(tr, "*** BUFFER MEMORY MAPPED ***\n"); + trace_array_puts(tr, "*** Can not use snapshot (sorry) ***\n"); + return; + } + + /* Note, snapshot can not be used when the tracer uses it */ + if (tracer_uses_snapshot(tr->current_trace)) { + trace_array_puts(tr, "*** LATENCY TRACER ACTIVE ***\n"); + trace_array_puts(tr, "*** Can not use snapshot (sorry) ***\n"); + return; + } + + local_irq_save(flags); + update_max_tr(tr, current, smp_processor_id(), cond_data); + local_irq_restore(flags); +} + +void tracing_snapshot_instance(struct trace_array *tr) +{ + tracing_snapshot_instance_cond(tr, NULL); +} + +/** + * tracing_snapshot_cond - conditionally take a snapshot of the current buffer. + * @tr: The tracing instance to snapshot + * @cond_data: The data to be tested conditionally, and possibly saved + * + * This is the same as tracing_snapshot() except that the snapshot is + * conditional - the snapshot will only happen if the + * cond_snapshot.update() implementation receiving the cond_data + * returns true, which means that the trace array's cond_snapshot + * update() operation used the cond_data to determine whether the + * snapshot should be taken, and if it was, presumably saved it along + * with the snapshot. + */ +void tracing_snapshot_cond(struct trace_array *tr, void *cond_data) +{ + tracing_snapshot_instance_cond(tr, cond_data); +} +EXPORT_SYMBOL_GPL(tracing_snapshot_cond); + +/** + * tracing_cond_snapshot_data - get the user data associated with a snapshot + * @tr: The tracing instance + * + * When the user enables a conditional snapshot using + * tracing_snapshot_cond_enable(), the user-defined cond_data is saved + * with the snapshot. This accessor is used to retrieve it. + * + * Should not be called from cond_snapshot.update(), since it takes + * the tr->max_lock lock, which the code calling + * cond_snapshot.update() has already done. + * + * Returns the cond_data associated with the trace array's snapshot. + */ +void *tracing_cond_snapshot_data(struct trace_array *tr) +{ + void *cond_data = NULL; + + local_irq_disable(); + arch_spin_lock(&tr->max_lock); + + if (tr->cond_snapshot) + cond_data = tr->cond_snapshot->cond_data; + + arch_spin_unlock(&tr->max_lock); + local_irq_enable(); + + return cond_data; +} +EXPORT_SYMBOL_GPL(tracing_cond_snapshot_data); + +/* resize @tr's buffer to the size of @size_tr's entries */ +int resize_buffer_duplicate_size(struct array_buffer *trace_buf, + struct array_buffer *size_buf, int cpu_id) +{ + int cpu, ret = 0; + + if (cpu_id == RING_BUFFER_ALL_CPUS) { + for_each_tracing_cpu(cpu) { + ret = ring_buffer_resize(trace_buf->buffer, + per_cpu_ptr(size_buf->data, cpu)->entries, cpu); + if (ret < 0) + break; + per_cpu_ptr(trace_buf->data, cpu)->entries = + per_cpu_ptr(size_buf->data, cpu)->entries; + } + } else { + ret = ring_buffer_resize(trace_buf->buffer, + per_cpu_ptr(size_buf->data, cpu_id)->entries, cpu_id); + if (ret == 0) + per_cpu_ptr(trace_buf->data, cpu_id)->entries = + per_cpu_ptr(size_buf->data, cpu_id)->entries; + } + + return ret; +} + +int tracing_alloc_snapshot_instance(struct trace_array *tr) +{ + int order; + int ret; + + if (!tr->allocated_snapshot) { + + /* Make the snapshot buffer have the same order as main buffer */ + order = ring_buffer_subbuf_order_get(tr->array_buffer.buffer); + ret = ring_buffer_subbuf_order_set(tr->snapshot_buffer.buffer, order); + if (ret < 0) + return ret; + + /* allocate spare buffer */ + ret = resize_buffer_duplicate_size(&tr->snapshot_buffer, + &tr->array_buffer, RING_BUFFER_ALL_CPUS); + if (ret < 0) + return ret; + + tr->allocated_snapshot = true; + } + + return 0; +} + +void free_snapshot(struct trace_array *tr) +{ + /* + * We don't free the ring buffer. instead, resize it because + * The max_tr ring buffer has some state (e.g. ring->clock) and + * we want preserve it. + */ + ring_buffer_subbuf_order_set(tr->snapshot_buffer.buffer, 0); + ring_buffer_resize(tr->snapshot_buffer.buffer, 1, RING_BUFFER_ALL_CPUS); + trace_set_buffer_entries(&tr->snapshot_buffer, 1); + tracing_reset_online_cpus(&tr->snapshot_buffer); + tr->allocated_snapshot = false; +} + +int tracing_arm_snapshot_locked(struct trace_array *tr) +{ + int ret; + + lockdep_assert_held(&trace_types_lock); + + spin_lock(&tr->snapshot_trigger_lock); + if (tr->snapshot == UINT_MAX || tr->mapped) { + spin_unlock(&tr->snapshot_trigger_lock); + return -EBUSY; + } + + tr->snapshot++; + spin_unlock(&tr->snapshot_trigger_lock); + + ret = tracing_alloc_snapshot_instance(tr); + if (ret) { + spin_lock(&tr->snapshot_trigger_lock); + tr->snapshot--; + spin_unlock(&tr->snapshot_trigger_lock); + } + + return ret; +} + +int tracing_arm_snapshot(struct trace_array *tr) +{ + guard(mutex)(&trace_types_lock); + return tracing_arm_snapshot_locked(tr); +} + +void tracing_disarm_snapshot(struct trace_array *tr) +{ + spin_lock(&tr->snapshot_trigger_lock); + if (!WARN_ON(!tr->snapshot)) + tr->snapshot--; + spin_unlock(&tr->snapshot_trigger_lock); +} + +/** + * tracing_snapshot_alloc - allocate and take a snapshot of the current buffer. + * + * This is similar to tracing_snapshot(), but it will allocate the + * snapshot buffer if it isn't already allocated. Use this only + * where it is safe to sleep, as the allocation may sleep. + * + * This causes a swap between the snapshot buffer and the current live + * tracing buffer. You can use this to take snapshots of the live + * trace when some condition is triggered, but continue to trace. + */ +void tracing_snapshot_alloc(void) +{ + int ret; + + ret = tracing_alloc_snapshot(); + if (ret < 0) + return; + + tracing_snapshot(); +} +EXPORT_SYMBOL_GPL(tracing_snapshot_alloc); + +/** + * tracing_snapshot_cond_enable - enable conditional snapshot for an instance + * @tr: The tracing instance + * @cond_data: User data to associate with the snapshot + * @update: Implementation of the cond_snapshot update function + * + * Check whether the conditional snapshot for the given instance has + * already been enabled, or if the current tracer is already using a + * snapshot; if so, return -EBUSY, else create a cond_snapshot and + * save the cond_data and update function inside. + * + * Returns 0 if successful, error otherwise. + */ +int tracing_snapshot_cond_enable(struct trace_array *tr, void *cond_data, + cond_update_fn_t update) +{ + struct cond_snapshot *cond_snapshot __free(kfree) = + kzalloc_obj(*cond_snapshot); + int ret; + + if (!cond_snapshot) + return -ENOMEM; + + cond_snapshot->cond_data = cond_data; + cond_snapshot->update = update; + + guard(mutex)(&trace_types_lock); + + if (tracer_uses_snapshot(tr->current_trace)) + return -EBUSY; + + /* + * The cond_snapshot can only change to NULL without the + * trace_types_lock. We don't care if we race with it going + * to NULL, but we want to make sure that it's not set to + * something other than NULL when we get here, which we can + * do safely with only holding the trace_types_lock and not + * having to take the max_lock. + */ + if (tr->cond_snapshot) + return -EBUSY; + + ret = tracing_arm_snapshot_locked(tr); + if (ret) + return ret; + + local_irq_disable(); + arch_spin_lock(&tr->max_lock); + tr->cond_snapshot = no_free_ptr(cond_snapshot); + arch_spin_unlock(&tr->max_lock); + local_irq_enable(); + + return 0; +} +EXPORT_SYMBOL_GPL(tracing_snapshot_cond_enable); + +/** + * tracing_snapshot_cond_disable - disable conditional snapshot for an instance + * @tr: The tracing instance + * + * Check whether the conditional snapshot for the given instance is + * enabled; if so, free the cond_snapshot associated with it, + * otherwise return -EINVAL. + * + * Returns 0 if successful, error otherwise. + */ +int tracing_snapshot_cond_disable(struct trace_array *tr) +{ + int ret = 0; + + local_irq_disable(); + arch_spin_lock(&tr->max_lock); + + if (!tr->cond_snapshot) + ret = -EINVAL; + else { + kfree(tr->cond_snapshot); + tr->cond_snapshot = NULL; + } + + arch_spin_unlock(&tr->max_lock); + local_irq_enable(); + + tracing_disarm_snapshot(tr); + + return ret; +} +EXPORT_SYMBOL_GPL(tracing_snapshot_cond_disable); + +#ifdef CONFIG_TRACER_MAX_TRACE +#ifdef LATENCY_FS_NOTIFY +static struct workqueue_struct *fsnotify_wq; + +static void latency_fsnotify_workfn(struct work_struct *work) +{ + struct trace_array *tr = container_of(work, struct trace_array, + fsnotify_work); + fsnotify_inode(tr->d_max_latency->d_inode, FS_MODIFY); +} + +static void latency_fsnotify_workfn_irq(struct irq_work *iwork) +{ + struct trace_array *tr = container_of(iwork, struct trace_array, + fsnotify_irqwork); + queue_work(fsnotify_wq, &tr->fsnotify_work); +} + +__init static int latency_fsnotify_init(void) +{ + fsnotify_wq = alloc_workqueue("tr_max_lat_wq", + WQ_UNBOUND | WQ_HIGHPRI, 0); + if (!fsnotify_wq) { + pr_err("Unable to allocate tr_max_lat_wq\n"); + return -ENOMEM; + } + return 0; +} + +late_initcall_sync(latency_fsnotify_init); + +void latency_fsnotify(struct trace_array *tr) +{ + if (!fsnotify_wq) + return; + /* + * We cannot call queue_work(&tr->fsnotify_work) from here because it's + * possible that we are called from __schedule() or do_idle(), which + * could cause a deadlock. + */ + irq_work_queue(&tr->fsnotify_irqwork); +} +#endif /* LATENCY_FS_NOTIFY */ + +static const struct file_operations tracing_max_lat_fops; + +void trace_create_maxlat_file(struct trace_array *tr, + struct dentry *d_tracer) +{ +#ifdef LATENCY_FS_NOTIFY + INIT_WORK(&tr->fsnotify_work, latency_fsnotify_workfn); + init_irq_work(&tr->fsnotify_irqwork, latency_fsnotify_workfn_irq); +#endif + tr->d_max_latency = trace_create_file("tracing_max_latency", + TRACE_MODE_WRITE, + d_tracer, tr, + &tracing_max_lat_fops); +} + +/* + * Copy the new maximum trace into the separate maximum-trace + * structure. (this way the maximum trace is permanently saved, + * for later retrieval via /sys/kernel/tracing/tracing_max_latency) + */ +static void +__update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu) +{ + struct array_buffer *trace_buf = &tr->array_buffer; + struct trace_array_cpu *data = per_cpu_ptr(trace_buf->data, cpu); + struct array_buffer *max_buf = &tr->snapshot_buffer; + struct trace_array_cpu *max_data = per_cpu_ptr(max_buf->data, cpu); + + max_buf->cpu = cpu; + max_buf->time_start = data->preempt_timestamp; + + max_data->saved_latency = tr->max_latency; + max_data->critical_start = data->critical_start; + max_data->critical_end = data->critical_end; + + strscpy(max_data->comm, tsk->comm); + max_data->pid = tsk->pid; + /* + * If tsk == current, then use current_uid(), as that does not use + * RCU. The irq tracer can be called out of RCU scope. + */ + if (tsk == current) + max_data->uid = current_uid(); + else + max_data->uid = task_uid(tsk); + + max_data->nice = tsk->static_prio - 20 - MAX_RT_PRIO; + max_data->policy = tsk->policy; + max_data->rt_priority = tsk->rt_priority; + + /* record this tasks comm */ + tracing_record_cmdline(tsk); + latency_fsnotify(tr); +} +#else +static inline void __update_max_tr(struct trace_array *tr, + struct task_struct *tsk, int cpu) { } +#endif /* CONFIG_TRACER_MAX_TRACE */ + +/** + * update_max_tr - snapshot all trace buffers from global_trace to max_tr + * @tr: tracer + * @tsk: the task with the latency + * @cpu: The cpu that initiated the trace. + * @cond_data: User data associated with a conditional snapshot + * + * Flip the buffers between the @tr and the max_tr and record information + * about which task was the cause of this latency. + */ +void +update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu, + void *cond_data) +{ + if (tr->stop_count) + return; + + WARN_ON_ONCE(!irqs_disabled()); + + if (!tr->allocated_snapshot) { + /* Only the nop tracer should hit this when disabling */ + WARN_ON_ONCE(tr->current_trace != &nop_trace); + return; + } + + arch_spin_lock(&tr->max_lock); + + /* Inherit the recordable setting from array_buffer */ + if (ring_buffer_record_is_set_on(tr->array_buffer.buffer)) + ring_buffer_record_on(tr->snapshot_buffer.buffer); + else + ring_buffer_record_off(tr->snapshot_buffer.buffer); + + if (tr->cond_snapshot && !tr->cond_snapshot->update(tr, cond_data)) { + arch_spin_unlock(&tr->max_lock); + return; + } + + swap(tr->array_buffer.buffer, tr->snapshot_buffer.buffer); + + __update_max_tr(tr, tsk, cpu); + + arch_spin_unlock(&tr->max_lock); + + /* Any waiters on the old snapshot buffer need to wake up */ + ring_buffer_wake_waiters(tr->array_buffer.buffer, RING_BUFFER_ALL_CPUS); +} + +/** + * update_max_tr_single - only copy one trace over, and reset the rest + * @tr: tracer + * @tsk: task with the latency + * @cpu: the cpu of the buffer to copy. + * + * Flip the trace of a single CPU buffer between the @tr and the max_tr. + */ +void +update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu) +{ + int ret; + + if (tr->stop_count) + return; + + WARN_ON_ONCE(!irqs_disabled()); + if (!tr->allocated_snapshot) { + /* Only the nop tracer should hit this when disabling */ + WARN_ON_ONCE(tr->current_trace != &nop_trace); + return; + } + + arch_spin_lock(&tr->max_lock); + + ret = ring_buffer_swap_cpu(tr->snapshot_buffer.buffer, tr->array_buffer.buffer, cpu); + + if (ret == -EBUSY) { + /* + * We failed to swap the buffer due to a commit taking + * place on this CPU. We fail to record, but we reset + * the max trace buffer (no one writes directly to it) + * and flag that it failed. + * Another reason is resize is in progress. + */ + trace_array_printk_buf(tr->snapshot_buffer.buffer, _THIS_IP_, + "Failed to swap buffers due to commit or resize in progress\n"); + } + + WARN_ON_ONCE(ret && ret != -EAGAIN && ret != -EBUSY); + + __update_max_tr(tr, tsk, cpu); + arch_spin_unlock(&tr->max_lock); +} + +static void show_snapshot_main_help(struct seq_file *m) +{ + seq_puts(m, "# echo 0 > snapshot : Clears and frees snapshot buffer\n" + "# echo 1 > snapshot : Allocates snapshot buffer, if not already allocated.\n" + "# Takes a snapshot of the main buffer.\n" + "# echo 2 > snapshot : Clears snapshot buffer (but does not allocate or free)\n" + "# (Doesn't have to be '2' works with any number that\n" + "# is not a '0' or '1')\n"); +} + +static void show_snapshot_percpu_help(struct seq_file *m) +{ + seq_puts(m, "# echo 0 > snapshot : Invalid for per_cpu snapshot file.\n"); +#ifdef CONFIG_RING_BUFFER_ALLOW_SWAP + seq_puts(m, "# echo 1 > snapshot : Allocates snapshot buffer, if not already allocated.\n" + "# Takes a snapshot of the main buffer for this cpu.\n"); +#else + seq_puts(m, "# echo 1 > snapshot : Not supported with this kernel.\n" + "# Must use main snapshot file to allocate.\n"); +#endif + seq_puts(m, "# echo 2 > snapshot : Clears this cpu's snapshot buffer (but does not allocate)\n" + "# (Doesn't have to be '2' works with any number that\n" + "# is not a '0' or '1')\n"); +} + +void print_snapshot_help(struct seq_file *m, struct trace_iterator *iter) +{ + if (iter->tr->allocated_snapshot) + seq_puts(m, "#\n# * Snapshot is allocated *\n#\n"); + else + seq_puts(m, "#\n# * Snapshot is freed *\n#\n"); + + seq_puts(m, "# Snapshot commands:\n"); + if (iter->cpu_file == RING_BUFFER_ALL_CPUS) + show_snapshot_main_help(m); + else + show_snapshot_percpu_help(m); +} + +static int tracing_snapshot_open(struct inode *inode, struct file *file) +{ + struct trace_array *tr = inode->i_private; + struct trace_iterator *iter; + struct seq_file *m; + int ret; + + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; + + if (file->f_mode & FMODE_READ) { + iter = __tracing_open(inode, file, true); + if (IS_ERR(iter)) + ret = PTR_ERR(iter); + } else { + /* Writes still need the seq_file to hold the private data */ + ret = -ENOMEM; + m = kzalloc_obj(*m); + if (!m) + goto out; + iter = kzalloc_obj(*iter); + if (!iter) { + kfree(m); + goto out; + } + ret = 0; + + iter->tr = tr; + iter->array_buffer = &tr->snapshot_buffer; + iter->cpu_file = tracing_get_cpu(inode); + m->private = iter; + file->private_data = m; + } +out: + if (ret < 0) + trace_array_put(tr); + + return ret; +} + +static void tracing_swap_cpu_buffer(void *tr) +{ + update_max_tr_single((struct trace_array *)tr, current, smp_processor_id()); +} + +static ssize_t +tracing_snapshot_write(struct file *filp, const char __user *ubuf, size_t cnt, + loff_t *ppos) +{ + struct seq_file *m = filp->private_data; + struct trace_iterator *iter = m->private; + struct trace_array *tr = iter->tr; + unsigned long val; + int ret; + + ret = tracing_update_buffers(tr); + if (ret < 0) + return ret; + + ret = kstrtoul_from_user(ubuf, cnt, 10, &val); + if (ret) + return ret; + + guard(mutex)(&trace_types_lock); + + if (tracer_uses_snapshot(tr->current_trace)) + return -EBUSY; + + local_irq_disable(); + arch_spin_lock(&tr->max_lock); + if (tr->cond_snapshot) + ret = -EBUSY; + arch_spin_unlock(&tr->max_lock); + local_irq_enable(); + if (ret) + return ret; + + switch (val) { + case 0: + if (iter->cpu_file != RING_BUFFER_ALL_CPUS) + return -EINVAL; + if (tr->allocated_snapshot) + free_snapshot(tr); + break; + case 1: +/* Only allow per-cpu swap if the ring buffer supports it */ +#ifndef CONFIG_RING_BUFFER_ALLOW_SWAP + if (iter->cpu_file != RING_BUFFER_ALL_CPUS) + return -EINVAL; +#endif + if (tr->allocated_snapshot) + ret = resize_buffer_duplicate_size(&tr->snapshot_buffer, + &tr->array_buffer, iter->cpu_file); + + ret = tracing_arm_snapshot_locked(tr); + if (ret) + return ret; + + /* Now, we're going to swap */ + if (iter->cpu_file == RING_BUFFER_ALL_CPUS) { + local_irq_disable(); + update_max_tr(tr, current, smp_processor_id(), NULL); + local_irq_enable(); + } else { + smp_call_function_single(iter->cpu_file, tracing_swap_cpu_buffer, + (void *)tr, 1); + } + tracing_disarm_snapshot(tr); + break; + default: + if (tr->allocated_snapshot) { + if (iter->cpu_file == RING_BUFFER_ALL_CPUS) + tracing_reset_online_cpus(&tr->snapshot_buffer); + else + tracing_reset_cpu(&tr->snapshot_buffer, iter->cpu_file); + } + break; + } + + if (ret >= 0) { + *ppos += cnt; + ret = cnt; + } + + return ret; +} + +static int tracing_snapshot_release(struct inode *inode, struct file *file) +{ + struct seq_file *m = file->private_data; + int ret; + + ret = tracing_release(inode, file); + + if (file->f_mode & FMODE_READ) + return ret; + + /* If write only, the seq_file is just a stub */ + if (m) + kfree(m->private); + kfree(m); + + return 0; +} + +static int snapshot_raw_open(struct inode *inode, struct file *filp) +{ + struct ftrace_buffer_info *info; + int ret; + + /* The following checks for tracefs lockdown */ + ret = tracing_buffers_open(inode, filp); + if (ret < 0) + return ret; + + info = filp->private_data; + + if (tracer_uses_snapshot(info->iter.trace)) { + tracing_buffers_release(inode, filp); + return -EBUSY; + } + + info->iter.snapshot = true; + info->iter.array_buffer = &info->iter.tr->snapshot_buffer; + + return ret; +} + +const struct file_operations snapshot_fops = { + .open = tracing_snapshot_open, + .read = seq_read, + .write = tracing_snapshot_write, + .llseek = tracing_lseek, + .release = tracing_snapshot_release, +}; + +const struct file_operations snapshot_raw_fops = { + .open = snapshot_raw_open, + .read = tracing_buffers_read, + .release = tracing_buffers_release, + .splice_read = tracing_buffers_splice_read, +}; + +#ifdef CONFIG_TRACER_MAX_TRACE +static ssize_t +tracing_max_lat_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct trace_array *tr = filp->private_data; + + return tracing_nsecs_read(&tr->max_latency, ubuf, cnt, ppos); +} + +static ssize_t +tracing_max_lat_write(struct file *filp, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct trace_array *tr = filp->private_data; + + return tracing_nsecs_write(&tr->max_latency, ubuf, cnt, ppos); +} + +static const struct file_operations tracing_max_lat_fops = { + .open = tracing_open_generic_tr, + .read = tracing_max_lat_read, + .write = tracing_max_lat_write, + .llseek = generic_file_llseek, + .release = tracing_release_generic_tr, +}; +#endif /* CONFIG_TRACER_MAX_TRACE */ + +int get_snapshot_map(struct trace_array *tr) +{ + int err = 0; + + /* + * Called with mmap_lock held. lockdep would be unhappy if we would now + * take trace_types_lock. Instead use the specific + * snapshot_trigger_lock. + */ + spin_lock(&tr->snapshot_trigger_lock); + + if (tr->snapshot || tr->mapped == UINT_MAX) + err = -EBUSY; + else + tr->mapped++; + + spin_unlock(&tr->snapshot_trigger_lock); + + /* Wait for update_max_tr() to observe iter->tr->mapped */ + if (tr->mapped == 1) + synchronize_rcu(); + + return err; + +} + +void put_snapshot_map(struct trace_array *tr) +{ + spin_lock(&tr->snapshot_trigger_lock); + if (!WARN_ON(!tr->mapped)) + tr->mapped--; + spin_unlock(&tr->snapshot_trigger_lock); +} + +#ifdef CONFIG_DYNAMIC_FTRACE +static void +ftrace_snapshot(unsigned long ip, unsigned long parent_ip, + struct trace_array *tr, struct ftrace_probe_ops *ops, + void *data) +{ + tracing_snapshot_instance(tr); +} + +static void +ftrace_count_snapshot(unsigned long ip, unsigned long parent_ip, + struct trace_array *tr, struct ftrace_probe_ops *ops, + void *data) +{ + struct ftrace_func_mapper *mapper = data; + long *count = NULL; + + if (mapper) + count = (long *)ftrace_func_mapper_find_ip(mapper, ip); + + if (count) { + + if (*count <= 0) + return; + + (*count)--; + } + + tracing_snapshot_instance(tr); +} + +static int +ftrace_snapshot_print(struct seq_file *m, unsigned long ip, + struct ftrace_probe_ops *ops, void *data) +{ + struct ftrace_func_mapper *mapper = data; + long *count = NULL; + + seq_printf(m, "%ps:", (void *)ip); + + seq_puts(m, "snapshot"); + + if (mapper) + count = (long *)ftrace_func_mapper_find_ip(mapper, ip); + + if (count) + seq_printf(m, ":count=%ld\n", *count); + else + seq_puts(m, ":unlimited\n"); + + return 0; +} + +static int +ftrace_snapshot_init(struct ftrace_probe_ops *ops, struct trace_array *tr, + unsigned long ip, void *init_data, void **data) +{ + struct ftrace_func_mapper *mapper = *data; + + if (!mapper) { + mapper = allocate_ftrace_func_mapper(); + if (!mapper) + return -ENOMEM; + *data = mapper; + } + + return ftrace_func_mapper_add_ip(mapper, ip, init_data); +} + +static void +ftrace_snapshot_free(struct ftrace_probe_ops *ops, struct trace_array *tr, + unsigned long ip, void *data) +{ + struct ftrace_func_mapper *mapper = data; + + if (!ip) { + if (!mapper) + return; + free_ftrace_func_mapper(mapper, NULL); + return; + } + + ftrace_func_mapper_remove_ip(mapper, ip); +} + +static struct ftrace_probe_ops snapshot_probe_ops = { + .func = ftrace_snapshot, + .print = ftrace_snapshot_print, +}; + +static struct ftrace_probe_ops snapshot_count_probe_ops = { + .func = ftrace_count_snapshot, + .print = ftrace_snapshot_print, + .init = ftrace_snapshot_init, + .free = ftrace_snapshot_free, +}; + +static int +ftrace_trace_snapshot_callback(struct trace_array *tr, struct ftrace_hash *hash, + char *glob, char *cmd, char *param, int enable) +{ + struct ftrace_probe_ops *ops; + void *count = (void *)-1; + char *number; + int ret; + + if (!tr) + return -ENODEV; + + /* hash funcs only work with set_ftrace_filter */ + if (!enable) + return -EINVAL; + + ops = param ? &snapshot_count_probe_ops : &snapshot_probe_ops; + + if (glob[0] == '!') { + ret = unregister_ftrace_function_probe_func(glob+1, tr, ops); + if (!ret) + tracing_disarm_snapshot(tr); + + return ret; + } + + if (!param) + goto out_reg; + + number = strsep(¶m, ":"); + + if (!strlen(number)) + goto out_reg; + + /* + * We use the callback data field (which is a pointer) + * as our counter. + */ + ret = kstrtoul(number, 0, (unsigned long *)&count); + if (ret) + return ret; + + out_reg: + ret = tracing_arm_snapshot(tr); + if (ret < 0) + return ret; + + ret = register_ftrace_function_probe(glob, tr, ops, count); + if (ret < 0) + tracing_disarm_snapshot(tr); + + return ret < 0 ? ret : 0; +} + +static struct ftrace_func_command ftrace_snapshot_cmd = { + .name = "snapshot", + .func = ftrace_trace_snapshot_callback, +}; + +__init int register_snapshot_cmd(void) +{ + return register_ftrace_command(&ftrace_snapshot_cmd); +} +#endif /* CONFIG_DYNAMIC_FTRACE */ + +int trace_allocate_snapshot(struct trace_array *tr, int size) +{ + int ret; + + /* Fix mapped buffer trace arrays do not have snapshot buffers */ + if (tr->range_addr_start) + return 0; + + /* allocate_snapshot can only be true during system boot */ + ret = allocate_trace_buffer(tr, &tr->snapshot_buffer, + allocate_snapshot ? size : 1); + if (ret < 0) + return -ENOMEM; + + tr->allocated_snapshot = allocate_snapshot; + + allocate_snapshot = false; + return 0; +} + +__init static bool tr_needs_alloc_snapshot(const char *name) +{ + char *test; + int len = strlen(name); + bool ret; + + if (!boot_snapshot_index) + return false; + + if (strncmp(name, boot_snapshot_info, len) == 0 && + boot_snapshot_info[len] == '\t') + return true; + + test = kmalloc(strlen(name) + 3, GFP_KERNEL); + if (!test) + return false; + + sprintf(test, "\t%s\t", name); + ret = strstr(boot_snapshot_info, test) == NULL; + kfree(test); + return ret; +} + +__init void do_allocate_snapshot(const char *name) +{ + if (!tr_needs_alloc_snapshot(name)) + return; + + /* + * When allocate_snapshot is set, the next call to + * allocate_trace_buffers() (called by trace_array_get_by_name()) + * will allocate the snapshot buffer. That will also clear + * this flag. + */ + allocate_snapshot = true; +} + +void __init ftrace_boot_snapshot(void) +{ + struct trace_array *tr; + + if (!snapshot_at_boot) + return; + + list_for_each_entry(tr, &ftrace_trace_arrays, list) { + if (!tr->allocated_snapshot) + continue; + + tracing_snapshot_instance(tr); + trace_array_puts(tr, "** Boot snapshot taken **\n"); + } +} diff --git a/kernel/trace/undefsyms_base.c b/kernel/trace/undefsyms_base.c new file mode 100644 index 000000000000..e65baf58e6ff --- /dev/null +++ b/kernel/trace/undefsyms_base.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * simple_ring_buffer is used by the pKVM hypervisor which does not have access + * to all kernel symbols. Whatever is undefined when compiling this file is + * compiler and tooling-generated symbols that can safely be ignored for + * simple_ring_buffer. + */ + +#include +#include +#include + +void undefsyms_base(void *p, int n); + +static char page[PAGE_SIZE] __aligned(PAGE_SIZE); + +void undefsyms_base(void *p, int n) +{ + char buffer[256] = { 0 }; + + u32 u = 0; + memset((char * volatile)page, 8, PAGE_SIZE); + memset((char * volatile)buffer, 8, sizeof(buffer)); + memcpy((void * volatile)p, buffer, sizeof(buffer)); + cmpxchg((u32 * volatile)&u, 0, 8); + WARN_ON(n == 0xdeadbeef); +} diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 91905aa19294..dffef52a807b 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -300,6 +300,8 @@ static int tracepoint_add_func(struct tracepoint *tp, lockdep_is_held(&tracepoints_mutex)); old = func_add(&tp_funcs, func, prio); if (IS_ERR(old)) { + if (tp->ext && tp->ext->unregfunc && !static_key_enabled(&tp->key)) + tp->ext->unregfunc(); WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); return PTR_ERR(old); } diff --git a/kernel/vmcore_info.c b/kernel/vmcore_info.c index 94e4ef75b1b2..8614430ca212 100644 --- a/kernel/vmcore_info.c +++ b/kernel/vmcore_info.c @@ -18,8 +18,6 @@ #include #include -#include - #include "kallsyms_internal.h" #include "kexec_internal.h" diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 7d675781bc91..87dd5e0f6968 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -60,6 +60,13 @@ unsigned long *watchdog_cpumask_bits = cpumask_bits(&watchdog_cpumask); int __read_mostly sysctl_hardlockup_all_cpu_backtrace; # endif /* CONFIG_SMP */ +/* + * Number of consecutive missed interrupts before declaring a lockup. + * Default to 1 (immediate) for NMI/Perf. Buddy will overwrite this to 3. + */ +int __read_mostly watchdog_hardlockup_miss_thresh = 1; +EXPORT_SYMBOL_GPL(watchdog_hardlockup_miss_thresh); + /* * Should we panic when a soft-lockup or hard-lockup occurs: */ @@ -137,6 +144,7 @@ __setup("nmi_watchdog=", hardlockup_panic_setup); static DEFINE_PER_CPU(atomic_t, hrtimer_interrupts); static DEFINE_PER_CPU(int, hrtimer_interrupts_saved); +static DEFINE_PER_CPU(int, hrtimer_interrupts_missed); static DEFINE_PER_CPU(bool, watchdog_hardlockup_warned); static DEFINE_PER_CPU(bool, watchdog_hardlockup_touched); static unsigned long hard_lockup_nmi_warn; @@ -159,21 +167,33 @@ void watchdog_hardlockup_touch_cpu(unsigned int cpu) per_cpu(watchdog_hardlockup_touched, cpu) = true; } -static bool is_hardlockup(unsigned int cpu) +static void watchdog_hardlockup_update_reset(unsigned int cpu) { int hrint = atomic_read(&per_cpu(hrtimer_interrupts, cpu)); - if (per_cpu(hrtimer_interrupts_saved, cpu) == hrint) - return true; - /* * NOTE: we don't need any fancy atomic_t or READ_ONCE/WRITE_ONCE * for hrtimer_interrupts_saved. hrtimer_interrupts_saved is * written/read by a single CPU. */ per_cpu(hrtimer_interrupts_saved, cpu) = hrint; + per_cpu(hrtimer_interrupts_missed, cpu) = 0; +} - return false; +static bool is_hardlockup(unsigned int cpu) +{ + int hrint = atomic_read(&per_cpu(hrtimer_interrupts, cpu)); + + if (per_cpu(hrtimer_interrupts_saved, cpu) != hrint) { + watchdog_hardlockup_update_reset(cpu); + return false; + } + + per_cpu(hrtimer_interrupts_missed, cpu)++; + if (per_cpu(hrtimer_interrupts_missed, cpu) % watchdog_hardlockup_miss_thresh) + return false; + + return true; } static void watchdog_hardlockup_kick(void) @@ -187,8 +207,11 @@ static void watchdog_hardlockup_kick(void) void watchdog_hardlockup_check(unsigned int cpu, struct pt_regs *regs) { int hardlockup_all_cpu_backtrace; + unsigned int this_cpu; + unsigned long flags; if (per_cpu(watchdog_hardlockup_touched, cpu)) { + watchdog_hardlockup_update_reset(cpu); per_cpu(watchdog_hardlockup_touched, cpu) = false; return; } @@ -201,74 +224,73 @@ void watchdog_hardlockup_check(unsigned int cpu, struct pt_regs *regs) * fired multiple times before we overflow'd. If it hasn't * then this is a good indication the cpu is stuck */ - if (is_hardlockup(cpu)) { - unsigned int this_cpu = smp_processor_id(); - unsigned long flags; + if (!is_hardlockup(cpu)) { + per_cpu(watchdog_hardlockup_warned, cpu) = false; + return; + } #ifdef CONFIG_SYSFS - ++hardlockup_count; + ++hardlockup_count; #endif - /* - * A poorly behaving BPF scheduler can trigger hard lockup by - * e.g. putting numerous affinitized tasks in a single queue and - * directing all CPUs at it. The following call can return true - * only once when sched_ext is enabled and will immediately - * abort the BPF scheduler and print out a warning message. - */ - if (scx_hardlockup(cpu)) + /* + * A poorly behaving BPF scheduler can trigger hard lockup by + * e.g. putting numerous affinitized tasks in a single queue and + * directing all CPUs at it. The following call can return true + * only once when sched_ext is enabled and will immediately + * abort the BPF scheduler and print out a warning message. + */ + if (scx_hardlockup(cpu)) + return; + + /* Only print hardlockups once. */ + if (per_cpu(watchdog_hardlockup_warned, cpu)) + return; + + /* + * Prevent multiple hard-lockup reports if one cpu is already + * engaged in dumping all cpu back traces. + */ + if (hardlockup_all_cpu_backtrace) { + if (test_and_set_bit_lock(0, &hard_lockup_nmi_warn)) return; - - /* Only print hardlockups once. */ - if (per_cpu(watchdog_hardlockup_warned, cpu)) - return; - - /* - * Prevent multiple hard-lockup reports if one cpu is already - * engaged in dumping all cpu back traces. - */ - if (hardlockup_all_cpu_backtrace) { - if (test_and_set_bit_lock(0, &hard_lockup_nmi_warn)) - return; - } - - /* - * NOTE: we call printk_cpu_sync_get_irqsave() after printing - * the lockup message. While it would be nice to serialize - * that printout, we really want to make sure that if some - * other CPU somehow locked up while holding the lock associated - * with printk_cpu_sync_get_irqsave() that we can still at least - * get the message about the lockup out. - */ - pr_emerg("CPU%u: Watchdog detected hard LOCKUP on cpu %u\n", this_cpu, cpu); - printk_cpu_sync_get_irqsave(flags); - - print_modules(); - print_irqtrace_events(current); - if (cpu == this_cpu) { - if (regs) - show_regs(regs); - else - dump_stack(); - printk_cpu_sync_put_irqrestore(flags); - } else { - printk_cpu_sync_put_irqrestore(flags); - trigger_single_cpu_backtrace(cpu); - } - - if (hardlockup_all_cpu_backtrace) { - trigger_allbutcpu_cpu_backtrace(cpu); - if (!hardlockup_panic) - clear_bit_unlock(0, &hard_lockup_nmi_warn); - } - - sys_info(hardlockup_si_mask & ~SYS_INFO_ALL_BT); - if (hardlockup_panic) - nmi_panic(regs, "Hard LOCKUP"); - - per_cpu(watchdog_hardlockup_warned, cpu) = true; - } else { - per_cpu(watchdog_hardlockup_warned, cpu) = false; } + + /* + * NOTE: we call printk_cpu_sync_get_irqsave() after printing + * the lockup message. While it would be nice to serialize + * that printout, we really want to make sure that if some + * other CPU somehow locked up while holding the lock associated + * with printk_cpu_sync_get_irqsave() that we can still at least + * get the message about the lockup out. + */ + this_cpu = smp_processor_id(); + pr_emerg("CPU%u: Watchdog detected hard LOCKUP on cpu %u\n", this_cpu, cpu); + printk_cpu_sync_get_irqsave(flags); + + print_modules(); + print_irqtrace_events(current); + if (cpu == this_cpu) { + if (regs) + show_regs(regs); + else + dump_stack(); + printk_cpu_sync_put_irqrestore(flags); + } else { + printk_cpu_sync_put_irqrestore(flags); + trigger_single_cpu_backtrace(cpu); + } + + if (hardlockup_all_cpu_backtrace) { + trigger_allbutcpu_cpu_backtrace(cpu); + if (!hardlockup_panic) + clear_bit_unlock(0, &hard_lockup_nmi_warn); + } + + sys_info(hardlockup_si_mask & ~SYS_INFO_ALL_BT); + if (hardlockup_panic) + nmi_panic(regs, "Hard LOCKUP"); + + per_cpu(watchdog_hardlockup_warned, cpu) = true; } #else /* CONFIG_HARDLOCKUP_DETECTOR_COUNTS_HRTIMER */ diff --git a/kernel/watchdog_buddy.c b/kernel/watchdog_buddy.c index ee754d767c21..3a1e57080c1c 100644 --- a/kernel/watchdog_buddy.c +++ b/kernel/watchdog_buddy.c @@ -21,6 +21,7 @@ static unsigned int watchdog_next_cpu(unsigned int cpu) int __init watchdog_hardlockup_probe(void) { + watchdog_hardlockup_miss_thresh = 3; return 0; } @@ -86,14 +87,6 @@ void watchdog_buddy_check_hardlockup(int hrtimer_interrupts) { unsigned int next_cpu; - /* - * Test for hardlockups every 3 samples. The sample period is - * watchdog_thresh * 2 / 5, so 3 samples gets us back to slightly over - * watchdog_thresh (over by 20%). - */ - if (hrtimer_interrupts % 3 != 0) - return; - /* check for a hardlockup on the next CPU */ next_cpu = watchdog_next_cpu(smp_processor_id()); if (next_cpu >= nr_cpu_ids) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 5f747f241a5f..33b721a9af02 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -2296,6 +2296,18 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) && WARN_ONCE(!is_chained_work(wq), "workqueue: cannot queue %ps on wq %s\n", work->func, wq->name))) { + struct work_offq_data offqd; + + /* + * State on entry: PENDING is set, work is off-queue (no + * insert_work() has run). + * + * Returning without clearing PENDING would leave the work + * in a weird state (PENDING=1, PWQ=0, entry empty) + */ + work_offqd_unpack(&offqd, *work_data_bits(work)); + set_work_pool_and_clear_pending(work, offqd.pool_id, + work_offqd_pack_flags(&offqd)); return; } rcu_read_lock(); @@ -5642,7 +5654,9 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq) ret = apply_workqueue_attrs_locked(wq, unbound_std_wq_attrs[highpri]); } - return ret; + if (ret) + goto enomem; + return 0; enomem: if (wq->cpu_pwq) { @@ -5906,6 +5920,21 @@ err_destroy: return NULL; } +__printf(1, 0) +static struct workqueue_struct *alloc_workqueue_va(const char *fmt, + unsigned int flags, + int max_active, + va_list args) +{ + struct workqueue_struct *wq; + + wq = __alloc_workqueue(fmt, flags, max_active, args); + if (wq) + wq_init_lockdep(wq); + + return wq; +} + __printf(1, 4) struct workqueue_struct *alloc_workqueue_noprof(const char *fmt, unsigned int flags, @@ -5915,12 +5944,8 @@ struct workqueue_struct *alloc_workqueue_noprof(const char *fmt, va_list args; va_start(args, max_active); - wq = __alloc_workqueue(fmt, flags, max_active, args); + wq = alloc_workqueue_va(fmt, flags, max_active, args); va_end(args); - if (!wq) - return NULL; - - wq_init_lockdep(wq); return wq; } @@ -5932,15 +5957,15 @@ static void devm_workqueue_release(void *res) } __printf(2, 5) struct workqueue_struct * -devm_alloc_workqueue(struct device *dev, const char *fmt, unsigned int flags, - int max_active, ...) +devm_alloc_workqueue_noprof(struct device *dev, const char *fmt, + unsigned int flags, int max_active, ...) { struct workqueue_struct *wq; va_list args; int ret; va_start(args, max_active); - wq = alloc_workqueue(fmt, flags, max_active, args); + wq = alloc_workqueue_va(fmt, flags, max_active, args); va_end(args); if (!wq) return NULL; @@ -5951,7 +5976,7 @@ devm_alloc_workqueue(struct device *dev, const char *fmt, unsigned int flags, return wq; } -EXPORT_SYMBOL_GPL(devm_alloc_workqueue); +EXPORT_SYMBOL_GPL(devm_alloc_workqueue_noprof); #ifdef CONFIG_LOCKDEP __printf(1, 5) diff --git a/lib/Kconfig b/lib/Kconfig index 0f2fb9610647..00a9509636c1 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -138,6 +138,7 @@ config TRACE_MMIO_ACCESS source "lib/crc/Kconfig" source "lib/crypto/Kconfig" +source "lib/raid/Kconfig" config XXHASH tristate @@ -625,9 +626,6 @@ config PLDMFW config ASN1_ENCODER tristate -config POLYNOMIAL - tristate - config FIRMWARE_TABLE bool diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 77c3774c1c49..8ff5adcfe1e0 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2526,6 +2526,17 @@ config STRING_HELPERS_KUNIT_TEST depends on KUNIT default KUNIT_ALL_TESTS +config STRING_KUNIT_BENCH + bool "Benchmark string functions at runtime" + depends on STRING_KUNIT_TEST + help + Enable performance measurement for string functions. + + This measures the execution efficiency of string functions + during the KUnit test run. + + If unsure, say N. + config FFS_KUNIT_TEST tristate "KUnit test ffs-family functions at runtime" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/lib/Makefile b/lib/Makefile index 72c90fca6fef..f33a24bf1c19 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -121,7 +121,7 @@ endif obj-$(CONFIG_DEBUG_INFO_REDUCED) += debug_info.o CFLAGS_debug_info.o += $(call cc-option, -femit-struct-debug-detailed=any) -obj-y += math/ crc/ crypto/ tests/ vdso/ +obj-y += math/ crc/ crypto/ tests/ vdso/ raid/ obj-$(CONFIG_GENERIC_IOMAP) += iomap.o obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o @@ -244,8 +244,6 @@ obj-$(CONFIG_MEMREGION) += memregion.o obj-$(CONFIG_STMP_DEVICE) += stmp_device.o obj-$(CONFIG_IRQ_POLL) += irq_poll.o -obj-$(CONFIG_POLYNOMIAL) += polynomial.o - # stackdepot.c should not be instrumented or call instrumented functions. # Prevent the compiler from calling builtins like memcmp() or bcmp() from this # file. diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c index 58991ab09d84..ed1bdcf1f8ab 100644 --- a/lib/alloc_tag.c +++ b/lib/alloc_tag.c @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -758,8 +760,115 @@ static __init bool need_page_alloc_tagging(void) return mem_profiling_support; } +#ifdef CONFIG_MEM_ALLOC_PROFILING_DEBUG +/* + * Track page allocations before page_ext is initialized. + * Some pages are allocated before page_ext becomes available, leaving + * their codetag uninitialized. Track these early PFNs so we can clear + * their codetag refs later to avoid warnings when they are freed. + * + * Early allocations include: + * - Base allocations independent of CPU count + * - Per-CPU allocations (e.g., CPU hotplug callbacks during smp_init, + * such as trace ring buffers, scheduler per-cpu data) + * + * For simplicity, we fix the size to 8192. + * If insufficient, a warning will be triggered to alert the user. + * + * TODO: Replace fixed-size array with dynamic allocation using + * a GFP flag similar to ___GFP_NO_OBJ_EXT to avoid recursion. + */ +#define EARLY_ALLOC_PFN_MAX 8192 + +static unsigned long early_pfns[EARLY_ALLOC_PFN_MAX] __initdata; +static atomic_t early_pfn_count __initdata = ATOMIC_INIT(0); + +static void __init __alloc_tag_add_early_pfn(unsigned long pfn) +{ + int old_idx, new_idx; + + do { + old_idx = atomic_read(&early_pfn_count); + if (old_idx >= EARLY_ALLOC_PFN_MAX) { + pr_warn_once("Early page allocations before page_ext init exceeded EARLY_ALLOC_PFN_MAX (%d)\n", + EARLY_ALLOC_PFN_MAX); + return; + } + new_idx = old_idx + 1; + } while (!atomic_try_cmpxchg(&early_pfn_count, &old_idx, new_idx)); + + early_pfns[old_idx] = pfn; +} + +typedef void alloc_tag_add_func(unsigned long pfn); +static alloc_tag_add_func __rcu *alloc_tag_add_early_pfn_ptr __refdata = + RCU_INITIALIZER(__alloc_tag_add_early_pfn); + +void alloc_tag_add_early_pfn(unsigned long pfn) +{ + alloc_tag_add_func *alloc_tag_add; + + if (static_key_enabled(&mem_profiling_compressed)) + return; + + rcu_read_lock(); + alloc_tag_add = rcu_dereference(alloc_tag_add_early_pfn_ptr); + if (alloc_tag_add) + alloc_tag_add(pfn); + rcu_read_unlock(); +} + +static void __init clear_early_alloc_pfn_tag_refs(void) +{ + unsigned int i; + + if (static_key_enabled(&mem_profiling_compressed)) + return; + + rcu_assign_pointer(alloc_tag_add_early_pfn_ptr, NULL); + /* Make sure we are not racing with __alloc_tag_add_early_pfn() */ + synchronize_rcu(); + + for (i = 0; i < atomic_read(&early_pfn_count); i++) { + unsigned long pfn = early_pfns[i]; + + if (pfn_valid(pfn)) { + struct page *page = pfn_to_page(pfn); + union pgtag_ref_handle handle; + union codetag_ref ref; + + if (get_page_tag_ref(page, &ref, &handle)) { + /* + * An early-allocated page could be freed and reallocated + * after its page_ext is initialized but before we clear it. + * In that case, it already has a valid tag set. + * We should not overwrite that valid tag with CODETAG_EMPTY. + * + * Note: there is still a small race window between checking + * ref.ct and calling set_codetag_empty(). We accept this + * race as it's unlikely and the extra complexity of atomic + * cmpxchg is not worth it for this debug-only code path. + */ + if (ref.ct) { + put_page_tag_ref(handle); + continue; + } + + set_codetag_empty(&ref); + update_page_tag_ref(handle, &ref); + put_page_tag_ref(handle); + } + } + + } +} +#else /* !CONFIG_MEM_ALLOC_PROFILING_DEBUG */ +static inline void __init clear_early_alloc_pfn_tag_refs(void) {} +#endif /* CONFIG_MEM_ALLOC_PROFILING_DEBUG */ + static __init void init_page_alloc_tagging(void) { + clear_early_alloc_pfn_tag_refs(); } struct page_ext_operations page_alloc_tagging_ops = { diff --git a/lib/bch.c b/lib/bch.c index 9561c0828802..c991c71c4cbd 100644 --- a/lib/bch.c +++ b/lib/bch.c @@ -392,7 +392,7 @@ static void compute_syndromes(struct bch_control *bch, uint32_t *ecc, for (j = 0; j < 2*t; j += 2) syn[j] ^= a_pow(bch, (j+1)*(i+s)); - poly ^= (1 << i); + poly ^= (1u << i); } } while (s > 0); @@ -612,7 +612,7 @@ static int find_poly_deg2_roots(struct bch_control *bch, struct gf_poly *poly, while (v) { i = deg(v); r ^= bch->xi_tab[i]; - v ^= (1 << i); + v ^= (1u << i); } /* verify root */ if ((gf_sqr(bch, r)^r) == u) { @@ -1116,7 +1116,7 @@ static void build_mod8_tables(struct bch_control *bch, const uint32_t *g) for (b = 0; b < 4; b++) { /* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */ tab = bch->mod8_tab + (b*256+i)*l; - data = i << (8*b); + data = (unsigned int)i << (8*b); while (data) { d = deg(data); /* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */ diff --git a/lib/bootconfig.c b/lib/bootconfig.c index e88d0221a826..c470b93d5dbc 100644 --- a/lib/bootconfig.c +++ b/lib/bootconfig.c @@ -17,7 +17,9 @@ #include #include #include -#include +#include +#include +#include #include #include @@ -64,14 +66,14 @@ static inline void __init xbc_free_mem(void *addr, size_t size, bool early) if (early) memblock_free(addr, size); else if (addr) - memblock_free_late(__pa(addr), size); + memblock_free(addr, size); } #else /* !__KERNEL__ */ static inline void *xbc_alloc_mem(size_t size) { - return malloc(size); + return calloc(1, size); } static inline void xbc_free_mem(void *addr, size_t size, bool early) @@ -79,6 +81,7 @@ static inline void xbc_free_mem(void *addr, size_t size, bool early) free(addr); } #endif + /** * xbc_get_info() - Get the information of loaded boot config * @node_size: A pointer to store the number of nodes. @@ -112,7 +115,7 @@ static int __init xbc_parse_error(const char *msg, const char *p) * xbc_root_node() - Get the root node of extended boot config * * Return the address of root node of extended boot config. If the - * extended boot config is not initiized, return NULL. + * extended boot config is not initialized, return NULL. */ struct xbc_node * __init xbc_root_node(void) { @@ -128,9 +131,9 @@ struct xbc_node * __init xbc_root_node(void) * * Return the index number of @node in XBC node list. */ -int __init xbc_node_index(struct xbc_node *node) +uint16_t __init xbc_node_index(struct xbc_node *node) { - return node - &xbc_nodes[0]; + return (uint16_t)(node - &xbc_nodes[0]); } /** @@ -180,7 +183,7 @@ struct xbc_node * __init xbc_node_get_next(struct xbc_node *node) */ const char * __init xbc_node_get_data(struct xbc_node *node) { - int offset = node->data & ~XBC_VALUE; + size_t offset = node->data & ~XBC_VALUE; if (WARN_ON(offset >= xbc_data_size)) return NULL; @@ -192,7 +195,7 @@ static bool __init xbc_node_match_prefix(struct xbc_node *node, const char **prefix) { const char *p = xbc_node_get_data(node); - int len = strlen(p); + size_t len = strlen(p); if (strncmp(*prefix, p, len)) return false; @@ -364,7 +367,7 @@ struct xbc_node * __init xbc_node_find_next_leaf(struct xbc_node *root, node = xbc_node_get_parent(node); if (node == root) return NULL; - /* User passed a node which is not uder parent */ + /* User passed a node which is not under parent */ if (WARN_ON(!node)) return NULL; } @@ -407,11 +410,11 @@ const char * __init xbc_node_find_next_key_value(struct xbc_node *root, /* XBC parse and tree build */ -static int __init xbc_init_node(struct xbc_node *node, char *data, uint32_t flag) +static int __init xbc_init_node(struct xbc_node *node, char *data, uint16_t flag) { - unsigned long offset = data - xbc_data; + long offset = data - xbc_data; - if (WARN_ON(offset >= XBC_DATA_MAX)) + if (WARN_ON(offset < 0 || offset >= XBC_DATA_MAX)) return -EINVAL; node->data = (uint16_t)offset | flag; @@ -421,16 +424,17 @@ static int __init xbc_init_node(struct xbc_node *node, char *data, uint32_t flag return 0; } -static struct xbc_node * __init xbc_add_node(char *data, uint32_t flag) +static struct xbc_node * __init xbc_add_node(char *data, uint16_t flag) { struct xbc_node *node; if (xbc_node_num == XBC_NODE_MAX) return NULL; - node = &xbc_nodes[xbc_node_num++]; + node = &xbc_nodes[xbc_node_num]; if (xbc_init_node(node, data, flag) < 0) return NULL; + xbc_node_num++; return node; } @@ -451,7 +455,7 @@ static inline __init struct xbc_node *xbc_last_child(struct xbc_node *node) return node; } -static struct xbc_node * __init __xbc_add_sibling(char *data, uint32_t flag, bool head) +static struct xbc_node * __init __xbc_add_sibling(char *data, uint16_t flag, bool head) { struct xbc_node *sib, *node = xbc_add_node(data, flag); @@ -472,23 +476,24 @@ static struct xbc_node * __init __xbc_add_sibling(char *data, uint32_t flag, boo sib->next = xbc_node_index(node); } } - } else + } else { xbc_parse_error("Too many nodes", data); + } return node; } -static inline struct xbc_node * __init xbc_add_sibling(char *data, uint32_t flag) +static inline struct xbc_node * __init xbc_add_sibling(char *data, uint16_t flag) { return __xbc_add_sibling(data, flag, false); } -static inline struct xbc_node * __init xbc_add_head_sibling(char *data, uint32_t flag) +static inline struct xbc_node * __init xbc_add_head_sibling(char *data, uint16_t flag) { return __xbc_add_sibling(data, flag, true); } -static inline __init struct xbc_node *xbc_add_child(char *data, uint32_t flag) +static inline __init struct xbc_node *xbc_add_child(char *data, uint16_t flag) { struct xbc_node *node = xbc_add_sibling(data, flag); @@ -655,9 +660,9 @@ static int __init __xbc_add_key(char *k) if (unlikely(xbc_node_num == 0)) goto add_node; - if (!last_parent) /* the first level */ + if (!last_parent) { /* the first level */ node = find_match_node(xbc_nodes, k); - else { + } else { child = xbc_node_get_child(last_parent); /* Since the value node is the first child, skip it. */ if (child && xbc_node_is_value(child)) @@ -665,9 +670,9 @@ static int __init __xbc_add_key(char *k) node = find_match_node(child, k); } - if (node) + if (node) { last_parent = node; - else { + } else { add_node: node = xbc_add_child(k, XBC_KEY); if (!node) @@ -798,7 +803,8 @@ static int __init xbc_close_brace(char **k, char *n) static int __init xbc_verify_tree(void) { - int i, depth, len, wlen; + int i, depth; + size_t len, wlen; struct xbc_node *n, *m; /* Brace closing */ @@ -815,10 +821,14 @@ static int __init xbc_verify_tree(void) } for (i = 0; i < xbc_node_num; i++) { - if (xbc_nodes[i].next > xbc_node_num) { + if (xbc_nodes[i].next >= xbc_node_num) { return xbc_parse_error("No closing brace", xbc_node_get_data(xbc_nodes + i)); } + if (xbc_nodes[i].child >= xbc_node_num) { + return xbc_parse_error("Broken child node", + xbc_node_get_data(xbc_nodes + i)); + } } /* Key tree limitation check */ @@ -980,7 +990,6 @@ int __init xbc_init(const char *data, size_t size, const char **emsg, int *epos) _xbc_exit(true); return -ENOMEM; } - memset(xbc_nodes, 0, sizeof(struct xbc_node) * XBC_NODE_MAX); ret = xbc_parse_tree(); if (!ret) @@ -992,8 +1001,9 @@ int __init xbc_init(const char *data, size_t size, const char **emsg, int *epos) if (emsg) *emsg = xbc_err_msg; _xbc_exit(true); - } else + } else { ret = xbc_node_num; + } return ret; } diff --git a/lib/bug.c b/lib/bug.c index aab9e6a40c5f..224f4cfa4aa3 100644 --- a/lib/bug.c +++ b/lib/bug.c @@ -251,7 +251,7 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga if (file) pr_crit("kernel BUG at %s:%u!\n", file, line); else - pr_crit("Kernel BUG at %pB [verbose debug info unavailable]\n", + pr_crit("kernel BUG at %pB [verbose debug info unavailable]\n", (void *)bugaddr); return BUG_TRAP_TYPE_BUG; @@ -260,7 +260,7 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga enum bug_trap_type report_bug_entry(struct bug_entry *bug, struct pt_regs *regs) { enum bug_trap_type ret; - bool rcu = false; + bool rcu; rcu = warn_rcu_enter(); ret = __report_bug(bug, bug_addr(bug), regs); @@ -272,7 +272,7 @@ enum bug_trap_type report_bug_entry(struct bug_entry *bug, struct pt_regs *regs) enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) { enum bug_trap_type ret; - bool rcu = false; + bool rcu; rcu = warn_rcu_enter(); ret = __report_bug(NULL, bugaddr, regs); diff --git a/lib/crc/Kconfig b/lib/crc/Kconfig index 31038c8d111a..f47bb4c706fb 100644 --- a/lib/crc/Kconfig +++ b/lib/crc/Kconfig @@ -65,7 +65,7 @@ config CRC32_ARCH depends on CRC32 && CRC_OPTIMIZATIONS default y if ARM && KERNEL_MODE_NEON default y if ARM64 - default y if LOONGARCH + default y if LOONGARCH && 64BIT default y if MIPS && CPU_MIPSR6 default y if PPC64 && ALTIVEC default y if RISCV && RISCV_ISA_ZBC diff --git a/lib/crypto/mpi/mpicoder.c b/lib/crypto/mpi/mpicoder.c index bf716a03c704..9359a58c29ec 100644 --- a/lib/crypto/mpi/mpicoder.c +++ b/lib/crypto/mpi/mpicoder.c @@ -347,7 +347,7 @@ MPI mpi_read_raw_from_sgl(struct scatterlist *sgl, unsigned int nbytes) lzeros = 0; len = 0; while (nbytes > 0) { - while (len && !*buff) { + while (len && !*buff && lzeros < nbytes) { lzeros++; len--; buff++; diff --git a/lib/decompress_bunzip2.c b/lib/decompress_bunzip2.c index ca736166f100..1288f146661f 100644 --- a/lib/decompress_bunzip2.c +++ b/lib/decompress_bunzip2.c @@ -135,7 +135,7 @@ static unsigned int INIT get_bits(struct bunzip_data *bd, char bits_wanted) } /* Avoid 32-bit overflow (dump bit buffer to top of output) */ if (bd->inbufBitCount >= 24) { - bits = bd->inbufBits&((1 << bd->inbufBitCount)-1); + bits = bd->inbufBits & ((1ULL << bd->inbufBitCount) - 1); bits_wanted -= bd->inbufBitCount; bits <<= bits_wanted; bd->inbufBitCount = 0; @@ -146,7 +146,7 @@ static unsigned int INIT get_bits(struct bunzip_data *bd, char bits_wanted) } /* Calculate result */ bd->inbufBitCount -= bits_wanted; - bits |= (bd->inbufBits >> bd->inbufBitCount)&((1 << bits_wanted)-1); + bits |= (bd->inbufBits >> bd->inbufBitCount) & ((1ULL << bits_wanted) - 1); return bits; } diff --git a/lib/fonts/font_rotate.c b/lib/fonts/font_rotate.c index 065e0fc0667b..275406008823 100644 --- a/lib/fonts/font_rotate.c +++ b/lib/fonts/font_rotate.c @@ -106,7 +106,7 @@ static void __font_glyph_rotate_180(const unsigned char *glyph, for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { if (font_glyph_test_bit(glyph, x, y, bit_pitch)) { - font_glyph_set_bit(out, width - (1 + x + shift), height - (1 + y), + font_glyph_set_bit(out, bit_pitch - 1 - x - shift, height - 1 - y, bit_pitch); } } diff --git a/lib/glob.c b/lib/glob.c index aa57900d2062..7aca76c25bcb 100644 --- a/lib/glob.c +++ b/lib/glob.c @@ -1,5 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) #include #include +#include /* * The only reason this code can be compiled as a module is because the @@ -20,7 +22,7 @@ MODULE_LICENSE("Dual MIT/GPL"); * Pattern metacharacters are ?, *, [ and \. * (And, inside character classes, !, - and ].) * - * This is small and simple implementation intended for device blacklists + * This is a small and simple implementation intended for device denylists * where a string is matched against a number of patterns. Thus, it * does not preprocess the patterns. It is non-recursive, and run-time * is at most quadratic: strlen(@str)*strlen(@pat). @@ -45,7 +47,7 @@ bool __pure glob_match(char const *pat, char const *str) * (no exception for /), it can be easily proved that there's * never a need to backtrack multiple levels. */ - char const *back_pat = NULL, *back_str; + char const *back_pat = NULL, *back_str = NULL; /* * Loop over each token (character or class) in pat, matching @@ -71,7 +73,7 @@ bool __pure glob_match(char const *pat, char const *str) if (c == '\0') /* No possible match */ return false; bool match = false, inverted = (*pat == '!'); - char const *class = pat + inverted; + char const *class = inverted ? pat + 1 : pat; unsigned char a = *class++; /* @@ -94,7 +96,8 @@ bool __pure glob_match(char const *pat, char const *str) class += 2; /* Any special action if a > b? */ } - match |= (a <= c && c <= b); + if (a <= c && c <= b) + match = true; } while ((a = *class++) != ']'); if (match == inverted) diff --git a/lib/inflate.c b/lib/inflate.c index eab886baa1b4..44a7da582baa 100644 --- a/lib/inflate.c +++ b/lib/inflate.c @@ -9,7 +9,7 @@ * based on gzip-1.0.3 * * Nicolas Pitre , 1999/04/14 : - * Little mods for all variable to reside either into rodata or bss segments + * Little mods for all variables to reside either into rodata or bss segments * by marking constant variables with 'const' and initializing all the others * at run-time only. This allows for the kernel uncompressor to run * directly from Flash or ROM memory on embedded systems. @@ -286,7 +286,7 @@ static void free(void *where) the longer codes. The time it costs to decode the longer codes is then traded against the time it takes to make longer tables. - This results of this trade are in the variables lbits and dbits + The results of this trade are in the variables lbits and dbits below. lbits is the number of bits the first level table for literal/ length codes can decode in one step, and dbits is the same thing for the distance codes. Subsequent tables are also less than or equal to @@ -811,6 +811,8 @@ DEBG(""); - ret = 0; out: free(ll); return ret; diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig index 498cc51e493d..94ff8e4089bf 100644 --- a/lib/kunit/Kconfig +++ b/lib/kunit/Kconfig @@ -16,8 +16,9 @@ menuconfig KUNIT if KUNIT config KUNIT_DEBUGFS - bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" if !KUNIT_ALL_TESTS - default KUNIT_ALL_TESTS + bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" + depends on DEBUG_FS + default y help Enable debugfs representation for kunit. Currently this consists of /sys/kernel/debug/kunit//results files for each diff --git a/lib/linear_ranges.c b/lib/linear_ranges.c index a1a7dfa881de..c85583678f6b 100644 --- a/lib/linear_ranges.c +++ b/lib/linear_ranges.c @@ -241,6 +241,42 @@ int linear_range_get_selector_high(const struct linear_range *r, } EXPORT_SYMBOL_GPL(linear_range_get_selector_high); +/** + * linear_range_get_selector_high_array - return linear range selector for value + * @r: pointer to array of linear ranges where selector is looked from + * @ranges: amount of ranges to scan from array + * @val: value for which the selector is searched + * @selector: address where found selector value is updated + * @found: flag to indicate that given value was in the range + * + * Scan array of ranges for selector for which range value matches given + * input value. Value is matching if it is equal or higher than given value + * If given value is found to be in a range scanning is stopped and @found is + * set true. If a range with values greater than given value is found + * but the range min is being greater than given value, then the range's + * lowest selector is updated to @selector and scanning is stopped. + * + * Return: 0 on success, -EINVAL if range array is invalid or does not contain + * range with a value greater or equal to given value + */ +int linear_range_get_selector_high_array(const struct linear_range *r, + int ranges, unsigned int val, + unsigned int *selector, bool *found) +{ + int i; + int ret; + + for (i = 0; i < ranges; i++) { + ret = linear_range_get_selector_high(&r[i], val, selector, + found); + if (!ret) + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(linear_range_get_selector_high_array); + /** * linear_range_get_selector_within - return linear range selector for value * @r: pointer to linear range where selector is looked from diff --git a/lib/list_sort.c b/lib/list_sort.c index a310ecb7ccc0..ff99203f208f 100644 --- a/lib/list_sort.c +++ b/lib/list_sort.c @@ -50,7 +50,6 @@ static void merge_final(void *priv, list_cmp_func_t cmp, struct list_head *head, struct list_head *a, struct list_head *b) { struct list_head *tail = head; - u8 count = 0; for (;;) { /* if equal, take 'a' -- important for sort stability */ @@ -76,15 +75,6 @@ static void merge_final(void *priv, list_cmp_func_t cmp, struct list_head *head, /* Finish linking remainder of list b on to tail */ tail->next = b; do { - /* - * If the merge is highly unbalanced (e.g. the input is - * already sorted), this loop may run many iterations. - * Continue callbacks to the client even though no - * element comparison is needed, so the client's cmp() - * routine can invoke cond_resched() periodically. - */ - if (unlikely(!++count)) - cmp(priv, b, b); b->prev = tail; tail = b; b = b->next; diff --git a/lib/maple_tree.c b/lib/maple_tree.c index d18d7ed9ab67..60ae5e6fc1ee 100644 --- a/lib/maple_tree.c +++ b/lib/maple_tree.c @@ -2,7 +2,7 @@ /* * Maple Tree implementation * Copyright (c) 2018-2022 Oracle Corporation - * Authors: Liam R. Howlett + * Authors: Liam R. Howlett * Matthew Wilcox * Copyright (c) 2023 ByteDance * Author: Peng Zhang diff --git a/lib/math/Kconfig b/lib/math/Kconfig index 0634b428d0cb..0e6d9cffc5d6 100644 --- a/lib/math/Kconfig +++ b/lib/math/Kconfig @@ -5,6 +5,9 @@ config CORDIC This option provides an implementation of the CORDIC algorithm; calculations are in fixed point. Module will be called cordic. +config POLYNOMIAL + tristate + config PRIME_NUMBERS tristate "Simple prime number generator for testing" help diff --git a/lib/math/Makefile b/lib/math/Makefile index d1caba23baa0..9a3850d55b79 100644 --- a/lib/math/Makefile +++ b/lib/math/Makefile @@ -2,6 +2,7 @@ obj-y += div64.o gcd.o lcm.o int_log.o int_pow.o int_sqrt.o reciprocal_div.o obj-$(CONFIG_CORDIC) += cordic.o +obj-$(CONFIG_POLYNOMIAL) += polynomial.o obj-$(CONFIG_PRIME_NUMBERS) += prime_numbers.o obj-$(CONFIG_RATIONAL) += rational.o diff --git a/lib/polynomial.c b/lib/math/polynomial.c similarity index 88% rename from lib/polynomial.c rename to lib/math/polynomial.c index 66d383445fec..f26677cfeeff 100644 --- a/lib/polynomial.c +++ b/lib/math/polynomial.c @@ -10,21 +10,19 @@ * */ -#include +#include +#include #include #include /* - * Originally this was part of drivers/hwmon/bt1-pvt.c. - * There the following conversion is used and should serve as an example here: + * The following conversion is an example: * * The original translation formulae of the temperature (in degrees of Celsius) * to PVT data and vice-versa are following: * - * N = 1.8322e-8*(T^4) + 2.343e-5*(T^3) + 8.7018e-3*(T^2) + 3.9269*(T^1) + - * 1.7204e2 - * T = -1.6743e-11*(N^4) + 8.1542e-8*(N^3) + -1.8201e-4*(N^2) + - * 3.1020e-1*(N^1) - 4.838e1 + * N = 1.8322e-8*(T^4) + 2.343e-5*(T^3) + 8.7018e-3*(T^2) + 3.9269*(T^1) + 1.7204e2 + * T = -1.6743e-11*(N^4) + 8.1542e-8*(N^3) + -1.8201e-4*(N^2) + 3.1020e-1*(N^1) - 4.838e1 * * where T = [-48.380, 147.438]C and N = [0, 1023]. * @@ -35,10 +33,9 @@ * formulae to accept millidegrees of Celsius. Here what they look like after * the alterations: * - * N = (18322e-20*(T^4) + 2343e-13*(T^3) + 87018e-9*(T^2) + 39269e-3*T + - * 17204e2) / 1e4 - * T = -16743e-12*(D^4) + 81542e-9*(D^3) - 182010e-6*(D^2) + 310200e-3*D - - * 48380 + * N = (18322e-20*(T^4) + 2343e-13*(T^3) + 87018e-9*(T^2) + 39269e-3*T + 17204e2) / 1e4 + * T = -16743e-12*(D^4) + 81542e-9*(D^3) - 182010e-6*(D^2) + 310200e-3*D - 48380 + * * where T = [-48380, 147438] mC and N = [0, 1023]. * * static const struct polynomial poly_temp_to_N = { @@ -68,13 +65,13 @@ * polynomial_calc - calculate a polynomial using integer arithmetic * * @poly: pointer to the descriptor of the polynomial - * @data: input value of the polynimal + * @data: input value of the polynomial * * Calculate the result of a polynomial using only integer arithmetic. For * this to work without too much loss of precision the coefficients has to * be altered. This is called factor redistribution. * - * Returns the result of the polynomial calculation. + * Return: the result of the polynomial calculation. */ long polynomial_calc(const struct polynomial *poly, long data) { diff --git a/lib/parser.c b/lib/parser.c index 73e8f8e5be73..62da0ac0d438 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -315,7 +315,7 @@ bool match_wildcard(const char *pattern, const char *str) } } - if (*p == '*') + while (*p == '*') ++p; return !*p; } diff --git a/lib/raid/.kunitconfig b/lib/raid/.kunitconfig new file mode 100644 index 000000000000..351d22ed1954 --- /dev/null +++ b/lib/raid/.kunitconfig @@ -0,0 +1,3 @@ +CONFIG_KUNIT=y +CONFIG_BTRFS_FS=y +CONFIG_XOR_KUNIT_TEST=y diff --git a/lib/raid/Kconfig b/lib/raid/Kconfig new file mode 100644 index 000000000000..5ab2b0a7be4c --- /dev/null +++ b/lib/raid/Kconfig @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0 + +config XOR_BLOCKS + tristate + +# selected by architectures that provide an optimized XOR implementation +config XOR_BLOCKS_ARCH + depends on XOR_BLOCKS + default y if ALPHA + default y if ARM + default y if ARM64 + default y if CPU_HAS_LSX # loongarch + default y if ALTIVEC # powerpc + default y if RISCV_ISA_V + default y if SPARC + default y if S390 + default y if X86_32 + default y if X86_64 + bool + +config XOR_KUNIT_TEST + tristate "KUnit tests for xor_gen" if !KUNIT_ALL_TESTS + depends on KUNIT + depends on XOR_BLOCKS + default KUNIT_ALL_TESTS + help + Unit tests for the XOR library functions. + + This is intended to help people writing architecture-specific + optimized versions. If unsure, say N. diff --git a/lib/raid/Makefile b/lib/raid/Makefile new file mode 100644 index 000000000000..3540fe846dc4 --- /dev/null +++ b/lib/raid/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y += xor/ diff --git a/lib/raid/xor/Makefile b/lib/raid/xor/Makefile new file mode 100644 index 000000000000..4d633dfd5b90 --- /dev/null +++ b/lib/raid/xor/Makefile @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0 + +ccflags-y += -I $(src) + +obj-$(CONFIG_XOR_BLOCKS) += xor.o + +xor-y += xor-core.o +xor-y += xor-8regs.o +xor-y += xor-32regs.o +xor-y += xor-8regs-prefetch.o +xor-y += xor-32regs-prefetch.o + +ifeq ($(CONFIG_XOR_BLOCKS_ARCH),y) +CFLAGS_xor-core.o += -I$(src)/$(SRCARCH) +endif + +xor-$(CONFIG_ALPHA) += alpha/xor.o +xor-$(CONFIG_ARM) += arm/xor.o +ifeq ($(CONFIG_ARM),y) +xor-$(CONFIG_KERNEL_MODE_NEON) += arm/xor-neon.o arm/xor-neon-glue.o +endif +xor-$(CONFIG_ARM64) += arm64/xor-neon.o arm64/xor-neon-glue.o +xor-$(CONFIG_CPU_HAS_LSX) += loongarch/xor_simd.o +xor-$(CONFIG_CPU_HAS_LSX) += loongarch/xor_simd_glue.o +xor-$(CONFIG_ALTIVEC) += powerpc/xor_vmx.o powerpc/xor_vmx_glue.o +xor-$(CONFIG_RISCV_ISA_V) += riscv/xor.o riscv/xor-glue.o +xor-$(CONFIG_SPARC32) += sparc/xor-sparc32.o +xor-$(CONFIG_SPARC64) += sparc/xor-sparc64.o sparc/xor-sparc64-glue.o +xor-$(CONFIG_S390) += s390/xor.o +xor-$(CONFIG_X86_32) += x86/xor-avx.o x86/xor-sse.o x86/xor-mmx.o +xor-$(CONFIG_X86_64) += x86/xor-avx.o x86/xor-sse.o +obj-y += tests/ + +CFLAGS_arm/xor-neon.o += $(CC_FLAGS_FPU) +CFLAGS_REMOVE_arm/xor-neon.o += $(CC_FLAGS_NO_FPU) + +CFLAGS_arm64/xor-neon.o += $(CC_FLAGS_FPU) +CFLAGS_REMOVE_arm64/xor-neon.o += $(CC_FLAGS_NO_FPU) + +CFLAGS_powerpc/xor_vmx.o += -mhard-float -maltivec \ + $(call cc-option,-mabi=altivec) \ + -isystem $(shell $(CC) -print-file-name=include) diff --git a/arch/alpha/include/asm/xor.h b/lib/raid/xor/alpha/xor.c similarity index 95% rename from arch/alpha/include/asm/xor.h rename to lib/raid/xor/alpha/xor.c index e0de0c233ab9..a8f72f2dd3a5 100644 --- a/arch/alpha/include/asm/xor.h +++ b/lib/raid/xor/alpha/xor.c @@ -1,9 +1,9 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * include/asm-alpha/xor.h - * - * Optimized RAID-5 checksumming functions for alpha EV5 and EV6 + * Optimized XOR parity functions for alpha EV5 and EV6 */ +#include "xor_impl.h" +#include "xor_arch.h" extern void xor_alpha_2(unsigned long bytes, unsigned long * __restrict p1, @@ -832,35 +832,17 @@ xor_alpha_prefetch_5: \n\ .end xor_alpha_prefetch_5 \n\ "); -static struct xor_block_template xor_block_alpha = { - .name = "alpha", - .do_2 = xor_alpha_2, - .do_3 = xor_alpha_3, - .do_4 = xor_alpha_4, - .do_5 = xor_alpha_5, +DO_XOR_BLOCKS(alpha, xor_alpha_2, xor_alpha_3, xor_alpha_4, xor_alpha_5); + +struct xor_block_template xor_block_alpha = { + .name = "alpha", + .xor_gen = xor_gen_alpha, }; -static struct xor_block_template xor_block_alpha_prefetch = { - .name = "alpha prefetch", - .do_2 = xor_alpha_prefetch_2, - .do_3 = xor_alpha_prefetch_3, - .do_4 = xor_alpha_prefetch_4, - .do_5 = xor_alpha_prefetch_5, +DO_XOR_BLOCKS(alpha_prefetch, xor_alpha_prefetch_2, xor_alpha_prefetch_3, + xor_alpha_prefetch_4, xor_alpha_prefetch_5); + +struct xor_block_template xor_block_alpha_prefetch = { + .name = "alpha prefetch", + .xor_gen = xor_gen_alpha_prefetch, }; - -/* For grins, also test the generic routines. */ -#include - -#undef XOR_TRY_TEMPLATES -#define XOR_TRY_TEMPLATES \ - do { \ - xor_speed(&xor_block_8regs); \ - xor_speed(&xor_block_32regs); \ - xor_speed(&xor_block_alpha); \ - xor_speed(&xor_block_alpha_prefetch); \ - } while (0) - -/* Force the use of alpha_prefetch if EV6, as it is significantly - faster in the cold cache case. */ -#define XOR_SELECT_TEMPLATE(FASTEST) \ - (implver() == IMPLVER_EV6 ? &xor_block_alpha_prefetch : FASTEST) diff --git a/lib/raid/xor/alpha/xor_arch.h b/lib/raid/xor/alpha/xor_arch.h new file mode 100644 index 000000000000..0dcfea578a48 --- /dev/null +++ b/lib/raid/xor/alpha/xor_arch.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern struct xor_block_template xor_block_alpha; +extern struct xor_block_template xor_block_alpha_prefetch; + +/* + * Force the use of alpha_prefetch if EV6, as it is significantly faster in the + * cold cache case. + */ +static __always_inline void __init arch_xor_init(void) +{ + if (implver() == IMPLVER_EV6) { + xor_force(&xor_block_alpha_prefetch); + } else { + xor_register(&xor_block_8regs); + xor_register(&xor_block_32regs); + xor_register(&xor_block_alpha); + xor_register(&xor_block_alpha_prefetch); + } +} diff --git a/lib/raid/xor/arm/xor-neon-glue.c b/lib/raid/xor/arm/xor-neon-glue.c new file mode 100644 index 000000000000..cea39e019904 --- /dev/null +++ b/lib/raid/xor/arm/xor-neon-glue.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2001 Russell King + */ +#include "xor_impl.h" +#include "xor_arch.h" + +static void xor_gen_neon(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes) +{ + kernel_neon_begin(); + xor_gen_neon_inner(dest, srcs, src_cnt, bytes); + kernel_neon_end(); +} + +struct xor_block_template xor_block_neon = { + .name = "neon", + .xor_gen = xor_gen_neon, +}; diff --git a/arch/arm/lib/xor-neon.c b/lib/raid/xor/arm/xor-neon.c similarity index 53% rename from arch/arm/lib/xor-neon.c rename to lib/raid/xor/arm/xor-neon.c index cf57fca97908..23147e3a7904 100644 --- a/arch/arm/lib/xor-neon.c +++ b/lib/raid/xor/arm/xor-neon.c @@ -1,15 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * linux/arch/arm/lib/xor-neon.c - * * Copyright (C) 2013 Linaro Ltd */ -#include -#include - -MODULE_DESCRIPTION("NEON accelerated XOR implementation"); -MODULE_LICENSE("GPL"); +#include "xor_impl.h" +#include "xor_arch.h" #ifndef __ARM_NEON__ #error You should compile this file with '-march=armv7-a -mfloat-abi=softfp -mfpu=neon' @@ -25,14 +20,7 @@ MODULE_LICENSE("GPL"); #pragma GCC optimize "tree-vectorize" #endif -#pragma GCC diagnostic ignored "-Wunused-variable" -#include +#define NO_TEMPLATE +#include "../xor-8regs.c" -struct xor_block_template const xor_block_neon_inner = { - .name = "__inner_neon__", - .do_2 = xor_8regs_2, - .do_3 = xor_8regs_3, - .do_4 = xor_8regs_4, - .do_5 = xor_8regs_5, -}; -EXPORT_SYMBOL(xor_block_neon_inner); +__DO_XOR_BLOCKS(neon_inner, xor_8regs_2, xor_8regs_3, xor_8regs_4, xor_8regs_5); diff --git a/arch/arm/include/asm/xor.h b/lib/raid/xor/arm/xor.c similarity index 59% rename from arch/arm/include/asm/xor.h rename to lib/raid/xor/arm/xor.c index 934b549905f5..45139b6c55ea 100644 --- a/arch/arm/include/asm/xor.h +++ b/lib/raid/xor/arm/xor.c @@ -1,13 +1,9 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +// SPDX-License-Identifier: GPL-2.0-only /* - * arch/arm/include/asm/xor.h - * * Copyright (C) 2001 Russell King */ -#include -#include -#include -#include +#include "xor_impl.h" +#include "xor_arch.h" #define __XOR(a1, a2) a1 ^= a2 @@ -131,95 +127,10 @@ xor_arm4regs_5(unsigned long bytes, unsigned long * __restrict p1, } while (--lines); } -static struct xor_block_template xor_block_arm4regs = { - .name = "arm4regs", - .do_2 = xor_arm4regs_2, - .do_3 = xor_arm4regs_3, - .do_4 = xor_arm4regs_4, - .do_5 = xor_arm4regs_5, +DO_XOR_BLOCKS(arm4regs, xor_arm4regs_2, xor_arm4regs_3, xor_arm4regs_4, + xor_arm4regs_5); + +struct xor_block_template xor_block_arm4regs = { + .name = "arm4regs", + .xor_gen = xor_gen_arm4regs, }; - -#undef XOR_TRY_TEMPLATES -#define XOR_TRY_TEMPLATES \ - do { \ - xor_speed(&xor_block_arm4regs); \ - xor_speed(&xor_block_8regs); \ - xor_speed(&xor_block_32regs); \ - NEON_TEMPLATES; \ - } while (0) - -#ifdef CONFIG_KERNEL_MODE_NEON - -extern struct xor_block_template const xor_block_neon_inner; - -static void -xor_neon_2(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2) -{ - if (in_interrupt()) { - xor_arm4regs_2(bytes, p1, p2); - } else { - kernel_neon_begin(); - xor_block_neon_inner.do_2(bytes, p1, p2); - kernel_neon_end(); - } -} - -static void -xor_neon_3(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3) -{ - if (in_interrupt()) { - xor_arm4regs_3(bytes, p1, p2, p3); - } else { - kernel_neon_begin(); - xor_block_neon_inner.do_3(bytes, p1, p2, p3); - kernel_neon_end(); - } -} - -static void -xor_neon_4(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4) -{ - if (in_interrupt()) { - xor_arm4regs_4(bytes, p1, p2, p3, p4); - } else { - kernel_neon_begin(); - xor_block_neon_inner.do_4(bytes, p1, p2, p3, p4); - kernel_neon_end(); - } -} - -static void -xor_neon_5(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4, - const unsigned long * __restrict p5) -{ - if (in_interrupt()) { - xor_arm4regs_5(bytes, p1, p2, p3, p4, p5); - } else { - kernel_neon_begin(); - xor_block_neon_inner.do_5(bytes, p1, p2, p3, p4, p5); - kernel_neon_end(); - } -} - -static struct xor_block_template xor_block_neon = { - .name = "neon", - .do_2 = xor_neon_2, - .do_3 = xor_neon_3, - .do_4 = xor_neon_4, - .do_5 = xor_neon_5 -}; - -#define NEON_TEMPLATES \ - do { if (cpu_has_neon()) xor_speed(&xor_block_neon); } while (0) -#else -#define NEON_TEMPLATES -#endif diff --git a/lib/raid/xor/arm/xor_arch.h b/lib/raid/xor/arm/xor_arch.h new file mode 100644 index 000000000000..775ff835df65 --- /dev/null +++ b/lib/raid/xor/arm/xor_arch.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2001 Russell King + */ +#include + +extern struct xor_block_template xor_block_arm4regs; +extern struct xor_block_template xor_block_neon; + +void xor_gen_neon_inner(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes); + +static __always_inline void __init arch_xor_init(void) +{ + xor_register(&xor_block_arm4regs); + xor_register(&xor_block_8regs); + xor_register(&xor_block_32regs); +#ifdef CONFIG_KERNEL_MODE_NEON + if (cpu_has_neon()) + xor_register(&xor_block_neon); +#endif +} diff --git a/lib/raid/xor/arm64/xor-neon-glue.c b/lib/raid/xor/arm64/xor-neon-glue.c new file mode 100644 index 000000000000..f0284f86feb4 --- /dev/null +++ b/lib/raid/xor/arm64/xor-neon-glue.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Authors: Jackie Liu + * Copyright (C) 2018,Tianjin KYLIN Information Technology Co., Ltd. + */ + +#include +#include "xor_impl.h" +#include "xor_arch.h" +#include "xor-neon.h" + +#define XOR_TEMPLATE(_name) \ +static void xor_gen_##_name(void *dest, void **srcs, unsigned int src_cnt, \ + unsigned int bytes) \ +{ \ + scoped_ksimd() \ + xor_gen_##_name##_inner(dest, srcs, src_cnt, bytes); \ +} \ + \ +struct xor_block_template xor_block_##_name = { \ + .name = __stringify(_name), \ + .xor_gen = xor_gen_##_name, \ +}; + +XOR_TEMPLATE(neon); +XOR_TEMPLATE(eor3); diff --git a/arch/arm64/lib/xor-neon.c b/lib/raid/xor/arm64/xor-neon.c similarity index 76% rename from arch/arm64/lib/xor-neon.c rename to lib/raid/xor/arm64/xor-neon.c index 8fffebfa17b2..97ef3cb92496 100644 --- a/arch/arm64/lib/xor-neon.c +++ b/lib/raid/xor/arm64/xor-neon.c @@ -1,17 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * arch/arm64/lib/xor-neon.c - * * Authors: Jackie Liu * Copyright (C) 2018,Tianjin KYLIN Information Technology Co., Ltd. */ -#include -#include +#include #include +#include "xor_impl.h" +#include "xor_arch.h" +#include "xor-neon.h" -static void xor_arm64_neon_2(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2) +static void __xor_neon_2(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2) { uint64_t *dp1 = (uint64_t *)p1; uint64_t *dp2 = (uint64_t *)p2; @@ -37,9 +37,9 @@ static void xor_arm64_neon_2(unsigned long bytes, unsigned long * __restrict p1, } while (--lines > 0); } -static void xor_arm64_neon_3(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3) +static void __xor_neon_3(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3) { uint64_t *dp1 = (uint64_t *)p1; uint64_t *dp2 = (uint64_t *)p2; @@ -73,10 +73,10 @@ static void xor_arm64_neon_3(unsigned long bytes, unsigned long * __restrict p1, } while (--lines > 0); } -static void xor_arm64_neon_4(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4) +static void __xor_neon_4(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3, + const unsigned long * __restrict p4) { uint64_t *dp1 = (uint64_t *)p1; uint64_t *dp2 = (uint64_t *)p2; @@ -118,11 +118,11 @@ static void xor_arm64_neon_4(unsigned long bytes, unsigned long * __restrict p1, } while (--lines > 0); } -static void xor_arm64_neon_5(unsigned long bytes, unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4, - const unsigned long * __restrict p5) +static void __xor_neon_5(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3, + const unsigned long * __restrict p4, + const unsigned long * __restrict p5) { uint64_t *dp1 = (uint64_t *)p1; uint64_t *dp2 = (uint64_t *)p2; @@ -172,14 +172,8 @@ static void xor_arm64_neon_5(unsigned long bytes, unsigned long * __restrict p1, } while (--lines > 0); } -struct xor_block_template xor_block_inner_neon __ro_after_init = { - .name = "__inner_neon__", - .do_2 = xor_arm64_neon_2, - .do_3 = xor_arm64_neon_3, - .do_4 = xor_arm64_neon_4, - .do_5 = xor_arm64_neon_5, -}; -EXPORT_SYMBOL(xor_block_inner_neon); +__DO_XOR_BLOCKS(neon_inner, __xor_neon_2, __xor_neon_3, __xor_neon_4, + __xor_neon_5); static inline uint64x2_t eor3(uint64x2_t p, uint64x2_t q, uint64x2_t r) { @@ -191,10 +185,9 @@ static inline uint64x2_t eor3(uint64x2_t p, uint64x2_t q, uint64x2_t r) return res; } -static void xor_arm64_eor3_3(unsigned long bytes, - unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3) +static void __xor_eor3_3(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3) { uint64_t *dp1 = (uint64_t *)p1; uint64_t *dp2 = (uint64_t *)p2; @@ -226,11 +219,10 @@ static void xor_arm64_eor3_3(unsigned long bytes, } while (--lines > 0); } -static void xor_arm64_eor3_4(unsigned long bytes, - unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4) +static void __xor_eor3_4(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3, + const unsigned long * __restrict p4) { uint64_t *dp1 = (uint64_t *)p1; uint64_t *dp2 = (uint64_t *)p2; @@ -270,12 +262,11 @@ static void xor_arm64_eor3_4(unsigned long bytes, } while (--lines > 0); } -static void xor_arm64_eor3_5(unsigned long bytes, - unsigned long * __restrict p1, - const unsigned long * __restrict p2, - const unsigned long * __restrict p3, - const unsigned long * __restrict p4, - const unsigned long * __restrict p5) +static void __xor_eor3_5(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3, + const unsigned long * __restrict p4, + const unsigned long * __restrict p5) { uint64_t *dp1 = (uint64_t *)p1; uint64_t *dp2 = (uint64_t *)p2; @@ -317,22 +308,5 @@ static void xor_arm64_eor3_5(unsigned long bytes, } while (--lines > 0); } -static int __init xor_neon_init(void) -{ - if (cpu_have_named_feature(SHA3)) { - xor_block_inner_neon.do_3 = xor_arm64_eor3_3; - xor_block_inner_neon.do_4 = xor_arm64_eor3_4; - xor_block_inner_neon.do_5 = xor_arm64_eor3_5; - } - return 0; -} -module_init(xor_neon_init); - -static void __exit xor_neon_exit(void) -{ -} -module_exit(xor_neon_exit); - -MODULE_AUTHOR("Jackie Liu "); -MODULE_DESCRIPTION("ARMv8 XOR Extensions"); -MODULE_LICENSE("GPL"); +__DO_XOR_BLOCKS(eor3_inner, __xor_neon_2, __xor_eor3_3, __xor_eor3_4, + __xor_eor3_5); diff --git a/lib/raid/xor/arm64/xor-neon.h b/lib/raid/xor/arm64/xor-neon.h new file mode 100644 index 000000000000..514699ba8f5f --- /dev/null +++ b/lib/raid/xor/arm64/xor-neon.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +void xor_gen_neon_inner(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes); +void xor_gen_eor3_inner(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes); diff --git a/lib/raid/xor/arm64/xor_arch.h b/lib/raid/xor/arm64/xor_arch.h new file mode 100644 index 000000000000..5dbb40319501 --- /dev/null +++ b/lib/raid/xor/arm64/xor_arch.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Authors: Jackie Liu + * Copyright (C) 2018,Tianjin KYLIN Information Technology Co., Ltd. + */ +#include + +extern struct xor_block_template xor_block_neon; +extern struct xor_block_template xor_block_eor3; + +static __always_inline void __init arch_xor_init(void) +{ + xor_register(&xor_block_8regs); + xor_register(&xor_block_32regs); + if (cpu_has_neon()) { + if (cpu_have_named_feature(SHA3)) + xor_register(&xor_block_eor3); + else + xor_register(&xor_block_neon); + } +} diff --git a/lib/raid/xor/loongarch/xor_arch.h b/lib/raid/xor/loongarch/xor_arch.h new file mode 100644 index 000000000000..fe5e8244fd0e --- /dev/null +++ b/lib/raid/xor/loongarch/xor_arch.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2023 WANG Xuerui + */ +#include + +/* + * For grins, also test the generic routines. + * + * More importantly: it cannot be ruled out at this point of time, that some + * future (maybe reduced) models could run the vector algorithms slower than + * the scalar ones, maybe for errata or micro-op reasons. It may be + * appropriate to revisit this after one or two more uarch generations. + */ + +extern struct xor_block_template xor_block_lsx; +extern struct xor_block_template xor_block_lasx; + +static __always_inline void __init arch_xor_init(void) +{ + xor_register(&xor_block_8regs); + xor_register(&xor_block_8regs_p); + xor_register(&xor_block_32regs); + xor_register(&xor_block_32regs_p); +#ifdef CONFIG_CPU_HAS_LSX + if (cpu_has_lsx) + xor_register(&xor_block_lsx); +#endif +#ifdef CONFIG_CPU_HAS_LASX + if (cpu_has_lasx) + xor_register(&xor_block_lasx); +#endif +} diff --git a/arch/loongarch/lib/xor_simd.c b/lib/raid/xor/loongarch/xor_simd.c similarity index 100% rename from arch/loongarch/lib/xor_simd.c rename to lib/raid/xor/loongarch/xor_simd.c diff --git a/arch/loongarch/lib/xor_simd.h b/lib/raid/xor/loongarch/xor_simd.h similarity index 100% rename from arch/loongarch/lib/xor_simd.h rename to lib/raid/xor/loongarch/xor_simd.h diff --git a/lib/raid/xor/loongarch/xor_simd_glue.c b/lib/raid/xor/loongarch/xor_simd_glue.c new file mode 100644 index 000000000000..7f324d924f87 --- /dev/null +++ b/lib/raid/xor/loongarch/xor_simd_glue.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * LoongArch SIMD XOR operations + * + * Copyright (C) 2023 WANG Xuerui + */ + +#include +#include +#include "xor_impl.h" +#include "xor_arch.h" +#include "xor_simd.h" + +#define MAKE_XOR_GLUES(flavor) \ +DO_XOR_BLOCKS(flavor##_inner, __xor_##flavor##_2, __xor_##flavor##_3, \ + __xor_##flavor##_4, __xor_##flavor##_5); \ + \ +static void xor_gen_##flavor(void *dest, void **srcs, unsigned int src_cnt, \ + unsigned int bytes) \ +{ \ + kernel_fpu_begin(); \ + xor_gen_##flavor##_inner(dest, srcs, src_cnt, bytes); \ + kernel_fpu_end(); \ +} \ + \ +struct xor_block_template xor_block_##flavor = { \ + .name = __stringify(flavor), \ + .xor_gen = xor_gen_##flavor \ +} + +#ifdef CONFIG_CPU_HAS_LSX +MAKE_XOR_GLUES(lsx); +#endif /* CONFIG_CPU_HAS_LSX */ + +#ifdef CONFIG_CPU_HAS_LASX +MAKE_XOR_GLUES(lasx); +#endif /* CONFIG_CPU_HAS_LASX */ diff --git a/arch/loongarch/lib/xor_template.c b/lib/raid/xor/loongarch/xor_template.c similarity index 100% rename from arch/loongarch/lib/xor_template.c rename to lib/raid/xor/loongarch/xor_template.c diff --git a/lib/raid/xor/powerpc/xor_arch.h b/lib/raid/xor/powerpc/xor_arch.h new file mode 100644 index 000000000000..3b00a4a2fd67 --- /dev/null +++ b/lib/raid/xor/powerpc/xor_arch.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Copyright (C) IBM Corporation, 2012 + * + * Author: Anton Blanchard + */ +#include + +extern struct xor_block_template xor_block_altivec; + +static __always_inline void __init arch_xor_init(void) +{ + xor_register(&xor_block_8regs); + xor_register(&xor_block_8regs_p); + xor_register(&xor_block_32regs); + xor_register(&xor_block_32regs_p); +#ifdef CONFIG_ALTIVEC + if (cpu_has_feature(CPU_FTR_ALTIVEC)) + xor_register(&xor_block_altivec); +#endif +} diff --git a/arch/powerpc/lib/xor_vmx.c b/lib/raid/xor/powerpc/xor_vmx.c similarity index 69% rename from arch/powerpc/lib/xor_vmx.c rename to lib/raid/xor/powerpc/xor_vmx.c index aab49d056d18..09bed98c1bc7 100644 --- a/arch/powerpc/lib/xor_vmx.c +++ b/lib/raid/xor/powerpc/xor_vmx.c @@ -10,6 +10,7 @@ * Sparse (as at v0.5.0) gets very, very confused by this file. * Make it a bit simpler for it. */ +#include "xor_impl.h" #if !defined(__CHECKER__) #include #else @@ -49,9 +50,9 @@ typedef vector signed char unative_t; V1##_3 = vec_xor(V1##_3, V2##_3); \ } while (0) -void __xor_altivec_2(unsigned long bytes, - unsigned long * __restrict v1_in, - const unsigned long * __restrict v2_in) +static void __xor_altivec_2(unsigned long bytes, + unsigned long * __restrict v1_in, + const unsigned long * __restrict v2_in) { DEFINE(v1); DEFINE(v2); @@ -68,10 +69,10 @@ void __xor_altivec_2(unsigned long bytes, } while (--lines > 0); } -void __xor_altivec_3(unsigned long bytes, - unsigned long * __restrict v1_in, - const unsigned long * __restrict v2_in, - const unsigned long * __restrict v3_in) +static void __xor_altivec_3(unsigned long bytes, + unsigned long * __restrict v1_in, + const unsigned long * __restrict v2_in, + const unsigned long * __restrict v3_in) { DEFINE(v1); DEFINE(v2); @@ -92,11 +93,11 @@ void __xor_altivec_3(unsigned long bytes, } while (--lines > 0); } -void __xor_altivec_4(unsigned long bytes, - unsigned long * __restrict v1_in, - const unsigned long * __restrict v2_in, - const unsigned long * __restrict v3_in, - const unsigned long * __restrict v4_in) +static void __xor_altivec_4(unsigned long bytes, + unsigned long * __restrict v1_in, + const unsigned long * __restrict v2_in, + const unsigned long * __restrict v3_in, + const unsigned long * __restrict v4_in) { DEFINE(v1); DEFINE(v2); @@ -121,12 +122,12 @@ void __xor_altivec_4(unsigned long bytes, } while (--lines > 0); } -void __xor_altivec_5(unsigned long bytes, - unsigned long * __restrict v1_in, - const unsigned long * __restrict v2_in, - const unsigned long * __restrict v3_in, - const unsigned long * __restrict v4_in, - const unsigned long * __restrict v5_in) +static void __xor_altivec_5(unsigned long bytes, + unsigned long * __restrict v1_in, + const unsigned long * __restrict v2_in, + const unsigned long * __restrict v3_in, + const unsigned long * __restrict v4_in, + const unsigned long * __restrict v5_in) { DEFINE(v1); DEFINE(v2); @@ -154,3 +155,6 @@ void __xor_altivec_5(unsigned long bytes, v5 += 4; } while (--lines > 0); } + +__DO_XOR_BLOCKS(altivec_inner, __xor_altivec_2, __xor_altivec_3, + __xor_altivec_4, __xor_altivec_5); diff --git a/lib/raid/xor/powerpc/xor_vmx.h b/lib/raid/xor/powerpc/xor_vmx.h new file mode 100644 index 000000000000..1d26c1133a86 --- /dev/null +++ b/lib/raid/xor/powerpc/xor_vmx.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Simple interface to link xor_vmx.c and xor_vmx_glue.c + * + * Separating these file ensures that no altivec instructions are run + * outside of the enable/disable altivec block. + */ + +void xor_gen_altivec_inner(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes); diff --git a/lib/raid/xor/powerpc/xor_vmx_glue.c b/lib/raid/xor/powerpc/xor_vmx_glue.c new file mode 100644 index 000000000000..dbfbb5cadc36 --- /dev/null +++ b/lib/raid/xor/powerpc/xor_vmx_glue.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Altivec XOR operations + * + * Copyright 2017 IBM Corp. + */ + +#include +#include +#include +#include "xor_impl.h" +#include "xor_arch.h" +#include "xor_vmx.h" + +static void xor_gen_altivec(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes) +{ + preempt_disable(); + enable_kernel_altivec(); + xor_gen_altivec_inner(dest, srcs, src_cnt, bytes); + disable_kernel_altivec(); + preempt_enable(); +} + +struct xor_block_template xor_block_altivec = { + .name = "altivec", + .xor_gen = xor_gen_altivec, +}; diff --git a/lib/raid/xor/riscv/xor-glue.c b/lib/raid/xor/riscv/xor-glue.c new file mode 100644 index 000000000000..2e4c1b05d998 --- /dev/null +++ b/lib/raid/xor/riscv/xor-glue.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 SiFive + */ + +#include +#include +#include +#include "xor_impl.h" +#include "xor_arch.h" + +DO_XOR_BLOCKS(vector_inner, xor_regs_2_, xor_regs_3_, xor_regs_4_, xor_regs_5_); + +static void xor_gen_vector(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes) +{ + kernel_vector_begin(); + xor_gen_vector_inner(dest, srcs, src_cnt, bytes); + kernel_vector_end(); +} + +struct xor_block_template xor_block_rvv = { + .name = "rvv", + .xor_gen = xor_gen_vector, +}; diff --git a/arch/riscv/lib/xor.S b/lib/raid/xor/riscv/xor.S similarity index 92% rename from arch/riscv/lib/xor.S rename to lib/raid/xor/riscv/xor.S index b28f2430e52f..56fb7fc1e2cd 100644 --- a/arch/riscv/lib/xor.S +++ b/lib/raid/xor/riscv/xor.S @@ -18,7 +18,6 @@ SYM_FUNC_START(xor_regs_2_) bnez a0, xor_regs_2_ ret SYM_FUNC_END(xor_regs_2_) -EXPORT_SYMBOL(xor_regs_2_) SYM_FUNC_START(xor_regs_3_) vsetvli a4, a0, e8, m8, ta, ma @@ -35,7 +34,6 @@ SYM_FUNC_START(xor_regs_3_) bnez a0, xor_regs_3_ ret SYM_FUNC_END(xor_regs_3_) -EXPORT_SYMBOL(xor_regs_3_) SYM_FUNC_START(xor_regs_4_) vsetvli a5, a0, e8, m8, ta, ma @@ -55,7 +53,6 @@ SYM_FUNC_START(xor_regs_4_) bnez a0, xor_regs_4_ ret SYM_FUNC_END(xor_regs_4_) -EXPORT_SYMBOL(xor_regs_4_) SYM_FUNC_START(xor_regs_5_) vsetvli a6, a0, e8, m8, ta, ma @@ -78,4 +75,3 @@ SYM_FUNC_START(xor_regs_5_) bnez a0, xor_regs_5_ ret SYM_FUNC_END(xor_regs_5_) -EXPORT_SYMBOL(xor_regs_5_) diff --git a/lib/raid/xor/riscv/xor_arch.h b/lib/raid/xor/riscv/xor_arch.h new file mode 100644 index 000000000000..9240857d760b --- /dev/null +++ b/lib/raid/xor/riscv/xor_arch.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 SiFive + */ +#include + +extern struct xor_block_template xor_block_rvv; + +static __always_inline void __init arch_xor_init(void) +{ + xor_register(&xor_block_8regs); + xor_register(&xor_block_32regs); +#ifdef CONFIG_RISCV_ISA_V + if (has_vector()) + xor_register(&xor_block_rvv); +#endif +} diff --git a/arch/s390/lib/xor.c b/lib/raid/xor/s390/xor.c similarity index 93% rename from arch/s390/lib/xor.c rename to lib/raid/xor/s390/xor.c index 5363e4c2462d..0c478678a129 100644 --- a/arch/s390/lib/xor.c +++ b/lib/raid/xor/s390/xor.c @@ -7,9 +7,8 @@ */ #include -#include -#include -#include +#include "xor_impl.h" +#include "xor_arch.h" static void xor_xc_2(unsigned long bytes, unsigned long * __restrict p1, const unsigned long * __restrict p2) @@ -126,11 +125,9 @@ static void xor_xc_5(unsigned long bytes, unsigned long * __restrict p1, : : "0", "cc", "memory"); } +DO_XOR_BLOCKS(xc, xor_xc_2, xor_xc_3, xor_xc_4, xor_xc_5); + struct xor_block_template xor_block_xc = { - .name = "xc", - .do_2 = xor_xc_2, - .do_3 = xor_xc_3, - .do_4 = xor_xc_4, - .do_5 = xor_xc_5, + .name = "xc", + .xor_gen = xor_gen_xc, }; -EXPORT_SYMBOL(xor_block_xc); diff --git a/lib/raid/xor/s390/xor_arch.h b/lib/raid/xor/s390/xor_arch.h new file mode 100644 index 000000000000..4a233ed2b97a --- /dev/null +++ b/lib/raid/xor/s390/xor_arch.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Optimited xor routines + * + * Copyright IBM Corp. 2016 + * Author(s): Martin Schwidefsky + */ +extern struct xor_block_template xor_block_xc; + +static __always_inline void __init arch_xor_init(void) +{ + xor_force(&xor_block_xc); +} diff --git a/arch/sparc/include/asm/xor_32.h b/lib/raid/xor/sparc/xor-sparc32.c similarity index 92% rename from arch/sparc/include/asm/xor_32.h rename to lib/raid/xor/sparc/xor-sparc32.c index 0351813cf3af..fb37631e90e6 100644 --- a/arch/sparc/include/asm/xor_32.h +++ b/lib/raid/xor/sparc/xor-sparc32.c @@ -1,16 +1,12 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * include/asm/xor.h - * - * Optimized RAID-5 checksumming functions for 32-bit Sparc. - */ - +// SPDX-License-Identifier: GPL-2.0-or-later /* * High speed xor_block operation for RAID4/5 utilizing the * ldd/std SPARC instructions. * * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz) */ +#include "xor_impl.h" +#include "xor_arch.h" static void sparc_2(unsigned long bytes, unsigned long * __restrict p1, @@ -248,21 +244,9 @@ sparc_5(unsigned long bytes, unsigned long * __restrict p1, } while (--lines > 0); } -static struct xor_block_template xor_block_SPARC = { - .name = "SPARC", - .do_2 = sparc_2, - .do_3 = sparc_3, - .do_4 = sparc_4, - .do_5 = sparc_5, +DO_XOR_BLOCKS(sparc32, sparc_2, sparc_3, sparc_4, sparc_5); + +struct xor_block_template xor_block_SPARC = { + .name = "SPARC", + .xor_gen = xor_gen_sparc32, }; - -/* For grins, also test the generic routines. */ -#include - -#undef XOR_TRY_TEMPLATES -#define XOR_TRY_TEMPLATES \ - do { \ - xor_speed(&xor_block_8regs); \ - xor_speed(&xor_block_32regs); \ - xor_speed(&xor_block_SPARC); \ - } while (0) diff --git a/arch/sparc/include/asm/xor_64.h b/lib/raid/xor/sparc/xor-sparc64-glue.c similarity index 63% rename from arch/sparc/include/asm/xor_64.h rename to lib/raid/xor/sparc/xor-sparc64-glue.c index caaddea8ad79..a8a686e0d258 100644 --- a/arch/sparc/include/asm/xor_64.h +++ b/lib/raid/xor/sparc/xor-sparc64-glue.c @@ -1,7 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * include/asm/xor.h - * * High speed xor_block operation for RAID4/5 utilizing the * UltraSparc Visual Instruction Set and Niagara block-init * twin-load instructions. @@ -10,7 +8,8 @@ * Copyright (C) 2006 David S. Miller */ -#include +#include "xor_impl.h" +#include "xor_arch.h" void xor_vis_2(unsigned long bytes, unsigned long * __restrict p1, const unsigned long * __restrict p2); @@ -29,12 +28,11 @@ void xor_vis_5(unsigned long bytes, unsigned long * __restrict p1, /* XXX Ugh, write cheetah versions... -DaveM */ -static struct xor_block_template xor_block_VIS = { - .name = "VIS", - .do_2 = xor_vis_2, - .do_3 = xor_vis_3, - .do_4 = xor_vis_4, - .do_5 = xor_vis_5, +DO_XOR_BLOCKS(vis, xor_vis_2, xor_vis_3, xor_vis_4, xor_vis_5); + +struct xor_block_template xor_block_VIS = { + .name = "VIS", + .xor_gen = xor_gen_vis, }; void xor_niagara_2(unsigned long bytes, unsigned long * __restrict p1, @@ -52,28 +50,10 @@ void xor_niagara_5(unsigned long bytes, unsigned long * __restrict p1, const unsigned long * __restrict p4, const unsigned long * __restrict p5); -static struct xor_block_template xor_block_niagara = { - .name = "Niagara", - .do_2 = xor_niagara_2, - .do_3 = xor_niagara_3, - .do_4 = xor_niagara_4, - .do_5 = xor_niagara_5, +DO_XOR_BLOCKS(niagara, xor_niagara_2, xor_niagara_3, xor_niagara_4, + xor_niagara_5); + +struct xor_block_template xor_block_niagara = { + .name = "Niagara", + .xor_gen = xor_gen_niagara, }; - -#undef XOR_TRY_TEMPLATES -#define XOR_TRY_TEMPLATES \ - do { \ - xor_speed(&xor_block_VIS); \ - xor_speed(&xor_block_niagara); \ - } while (0) - -/* For VIS for everything except Niagara. */ -#define XOR_SELECT_TEMPLATE(FASTEST) \ - ((tlb_type == hypervisor && \ - (sun4v_chip_type == SUN4V_CHIP_NIAGARA1 || \ - sun4v_chip_type == SUN4V_CHIP_NIAGARA2 || \ - sun4v_chip_type == SUN4V_CHIP_NIAGARA3 || \ - sun4v_chip_type == SUN4V_CHIP_NIAGARA4 || \ - sun4v_chip_type == SUN4V_CHIP_NIAGARA5)) ? \ - &xor_block_niagara : \ - &xor_block_VIS) diff --git a/arch/sparc/lib/xor.S b/lib/raid/xor/sparc/xor-sparc64.S similarity index 98% rename from arch/sparc/lib/xor.S rename to lib/raid/xor/sparc/xor-sparc64.S index 35461e3b2a9b..a7b74d473bd4 100644 --- a/arch/sparc/lib/xor.S +++ b/lib/raid/xor/sparc/xor-sparc64.S @@ -1,7 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * arch/sparc64/lib/xor.S - * * High speed xor_block operation for RAID4/5 utilizing the * UltraSparc Visual Instruction Set and Niagara store-init/twin-load. * @@ -92,7 +90,6 @@ ENTRY(xor_vis_2) retl wr %g0, 0, %fprs ENDPROC(xor_vis_2) -EXPORT_SYMBOL(xor_vis_2) ENTRY(xor_vis_3) rd %fprs, %o5 @@ -159,7 +156,6 @@ ENTRY(xor_vis_3) retl wr %g0, 0, %fprs ENDPROC(xor_vis_3) -EXPORT_SYMBOL(xor_vis_3) ENTRY(xor_vis_4) rd %fprs, %o5 @@ -245,7 +241,6 @@ ENTRY(xor_vis_4) retl wr %g0, 0, %fprs ENDPROC(xor_vis_4) -EXPORT_SYMBOL(xor_vis_4) ENTRY(xor_vis_5) save %sp, -192, %sp @@ -352,7 +347,6 @@ ENTRY(xor_vis_5) ret restore ENDPROC(xor_vis_5) -EXPORT_SYMBOL(xor_vis_5) /* Niagara versions. */ ENTRY(xor_niagara_2) /* %o0=bytes, %o1=dest, %o2=src */ @@ -399,7 +393,6 @@ ENTRY(xor_niagara_2) /* %o0=bytes, %o1=dest, %o2=src */ ret restore ENDPROC(xor_niagara_2) -EXPORT_SYMBOL(xor_niagara_2) ENTRY(xor_niagara_3) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2 */ save %sp, -192, %sp @@ -461,7 +454,6 @@ ENTRY(xor_niagara_3) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2 */ ret restore ENDPROC(xor_niagara_3) -EXPORT_SYMBOL(xor_niagara_3) ENTRY(xor_niagara_4) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2, %o4=src3 */ save %sp, -192, %sp @@ -544,7 +536,6 @@ ENTRY(xor_niagara_4) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2, %o4=src3 */ ret restore ENDPROC(xor_niagara_4) -EXPORT_SYMBOL(xor_niagara_4) ENTRY(xor_niagara_5) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2, %o4=src3, %o5=src4 */ save %sp, -192, %sp @@ -643,4 +634,3 @@ ENTRY(xor_niagara_5) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2, %o4=src3, %o5=s ret restore ENDPROC(xor_niagara_5) -EXPORT_SYMBOL(xor_niagara_5) diff --git a/lib/raid/xor/sparc/xor_arch.h b/lib/raid/xor/sparc/xor_arch.h new file mode 100644 index 000000000000..af288abe4e91 --- /dev/null +++ b/lib/raid/xor/sparc/xor_arch.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz) + * Copyright (C) 2006 David S. Miller + */ +#if defined(__sparc__) && defined(__arch64__) +#include + +extern struct xor_block_template xor_block_VIS; +extern struct xor_block_template xor_block_niagara; + +static __always_inline void __init arch_xor_init(void) +{ + /* Force VIS for everything except Niagara. */ + if (tlb_type == hypervisor && + (sun4v_chip_type == SUN4V_CHIP_NIAGARA1 || + sun4v_chip_type == SUN4V_CHIP_NIAGARA2 || + sun4v_chip_type == SUN4V_CHIP_NIAGARA3 || + sun4v_chip_type == SUN4V_CHIP_NIAGARA4 || + sun4v_chip_type == SUN4V_CHIP_NIAGARA5)) + xor_force(&xor_block_niagara); + else + xor_force(&xor_block_VIS); +} +#else /* sparc64 */ + +extern struct xor_block_template xor_block_SPARC; + +static __always_inline void __init arch_xor_init(void) +{ + xor_register(&xor_block_8regs); + xor_register(&xor_block_32regs); + xor_register(&xor_block_SPARC); +} +#endif /* !sparc64 */ diff --git a/lib/raid/xor/tests/Makefile b/lib/raid/xor/tests/Makefile new file mode 100644 index 000000000000..661e8f6ffd1f --- /dev/null +++ b/lib/raid/xor/tests/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_XOR_KUNIT_TEST) += xor_kunit.o diff --git a/lib/raid/xor/tests/xor_kunit.c b/lib/raid/xor/tests/xor_kunit.c new file mode 100644 index 000000000000..0c2a3a420bf9 --- /dev/null +++ b/lib/raid/xor/tests/xor_kunit.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Unit test the XOR library functions. + * + * Copyright 2024 Google LLC + * Copyright 2026 Christoph Hellwig + * + * Based on the CRC tests by Eric Biggers . + */ +#include +#include +#include +#include +#include + +#define XOR_KUNIT_SEED 42 +#define XOR_KUNIT_MAX_BYTES 16384 +#define XOR_KUNIT_MAX_BUFFERS 64 +#define XOR_KUNIT_NUM_TEST_ITERS 1000 + +static struct rnd_state rng; +static void *test_buffers[XOR_KUNIT_MAX_BUFFERS]; +static void *test_dest; +static void *test_ref; +static size_t test_buflen; + +static u32 rand32(void) +{ + return prandom_u32_state(&rng); +} + +/* Reference implementation using dumb byte-wise XOR */ +static void xor_ref(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes) +{ + unsigned int off, idx; + u8 *d = dest; + + for (off = 0; off < bytes; off++) { + for (idx = 0; idx < src_cnt; idx++) { + u8 *src = srcs[idx]; + + d[off] ^= src[off]; + } + } +} + +/* Generate a random length that is a multiple of 512. */ +static unsigned int random_length(unsigned int max_length) +{ + return round_up((rand32() % max_length) + 1, 512); +} + +/* Generate a random alignment that is a multiple of 64. */ +static unsigned int random_alignment(unsigned int max_alignment) +{ + return ((rand32() % max_alignment) + 1) & ~63; +} + +static void xor_generate_random_data(void) +{ + int i; + + prandom_bytes_state(&rng, test_dest, test_buflen); + memcpy(test_ref, test_dest, test_buflen); + for (i = 0; i < XOR_KUNIT_MAX_BUFFERS; i++) + prandom_bytes_state(&rng, test_buffers[i], test_buflen); +} + +/* Test that xor_gen gives the same result as a reference implementation. */ +static void xor_test(struct kunit *test) +{ + void *aligned_buffers[XOR_KUNIT_MAX_BUFFERS]; + size_t i; + + for (i = 0; i < XOR_KUNIT_NUM_TEST_ITERS; i++) { + unsigned int nr_buffers = + (rand32() % XOR_KUNIT_MAX_BUFFERS) + 1; + unsigned int len = random_length(XOR_KUNIT_MAX_BYTES); + unsigned int max_alignment, align = 0; + void *buffers; + + if (rand32() % 8 == 0) + /* Refresh the data occasionally. */ + xor_generate_random_data(); + + /* + * If we're not using the entire buffer size, inject randomize + * alignment into the buffer. + */ + max_alignment = XOR_KUNIT_MAX_BYTES - len; + if (max_alignment == 0) { + buffers = test_buffers; + } else if (rand32() % 2 == 0) { + /* Use random alignments mod 64 */ + int j; + + for (j = 0; j < nr_buffers; j++) + aligned_buffers[j] = test_buffers[j] + + random_alignment(max_alignment); + buffers = aligned_buffers; + align = random_alignment(max_alignment); + } else { + /* Go up to the guard page, to catch buffer overreads */ + int j; + + align = test_buflen - len; + for (j = 0; j < nr_buffers; j++) + aligned_buffers[j] = test_buffers[j] + align; + buffers = aligned_buffers; + } + + /* + * Compute the XOR, and verify that it equals the XOR computed + * by a simple byte-at-a-time reference implementation. + */ + xor_ref(test_ref + align, buffers, nr_buffers, len); + xor_gen(test_dest + align, buffers, nr_buffers, len); + KUNIT_EXPECT_MEMEQ_MSG(test, test_ref + align, + test_dest + align, len, + "Wrong result with buffers=%u, len=%u, unaligned=%s, at_end=%s", + nr_buffers, len, + str_yes_no(max_alignment), + str_yes_no(align + len == test_buflen)); + } +} + +static struct kunit_case xor_test_cases[] = { + KUNIT_CASE(xor_test), + {}, +}; + +static int xor_suite_init(struct kunit_suite *suite) +{ + int i; + + /* + * Allocate the test buffer using vmalloc() with a page-aligned length + * so that it is immediately followed by a guard page. This allows + * buffer overreads to be detected, even in assembly code. + */ + test_buflen = round_up(XOR_KUNIT_MAX_BYTES, PAGE_SIZE); + test_ref = vmalloc(test_buflen); + if (!test_ref) + return -ENOMEM; + test_dest = vmalloc(test_buflen); + if (!test_dest) + goto out_free_ref; + for (i = 0; i < XOR_KUNIT_MAX_BUFFERS; i++) { + test_buffers[i] = vmalloc(test_buflen); + if (!test_buffers[i]) + goto out_free_buffers; + } + + prandom_seed_state(&rng, XOR_KUNIT_SEED); + xor_generate_random_data(); + return 0; + +out_free_buffers: + while (--i >= 0) + vfree(test_buffers[i]); + vfree(test_dest); +out_free_ref: + vfree(test_ref); + return -ENOMEM; +} + +static void xor_suite_exit(struct kunit_suite *suite) +{ + int i; + + vfree(test_ref); + vfree(test_dest); + for (i = 0; i < XOR_KUNIT_MAX_BUFFERS; i++) + vfree(test_buffers[i]); +} + +static struct kunit_suite xor_test_suite = { + .name = "xor", + .test_cases = xor_test_cases, + .suite_init = xor_suite_init, + .suite_exit = xor_suite_exit, +}; +kunit_test_suite(xor_test_suite); + +MODULE_DESCRIPTION("Unit test for the XOR library functions"); +MODULE_LICENSE("GPL"); diff --git a/lib/raid/xor/um/xor_arch.h b/lib/raid/xor/um/xor_arch.h new file mode 100644 index 000000000000..a33e57a26c5e --- /dev/null +++ b/lib/raid/xor/um/xor_arch.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <../x86/xor_arch.h> diff --git a/arch/x86/include/asm/xor_avx.h b/lib/raid/xor/x86/xor-avx.c similarity index 84% rename from arch/x86/include/asm/xor_avx.h rename to lib/raid/xor/x86/xor-avx.c index 7f81dd5897f4..f7777d7aa269 100644 --- a/arch/x86/include/asm/xor_avx.h +++ b/lib/raid/xor/x86/xor-avx.c @@ -1,18 +1,16 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef _ASM_X86_XOR_AVX_H -#define _ASM_X86_XOR_AVX_H - +// SPDX-License-Identifier: GPL-2.0-only /* - * Optimized RAID-5 checksumming functions for AVX + * Optimized XOR parity functions for AVX * * Copyright (C) 2012 Intel Corporation * Author: Jim Kukunas * * Based on Ingo Molnar and Zach Brown's respective MMX and SSE routines */ - #include #include +#include "xor_impl.h" +#include "xor_arch.h" #define BLOCK4(i) \ BLOCK(32 * i, 0) \ @@ -31,8 +29,6 @@ static void xor_avx_2(unsigned long bytes, unsigned long * __restrict p0, { unsigned long lines = bytes >> 9; - kernel_fpu_begin(); - while (lines--) { #undef BLOCK #define BLOCK(i, reg) \ @@ -49,8 +45,6 @@ do { \ p0 = (unsigned long *)((uintptr_t)p0 + 512); p1 = (unsigned long *)((uintptr_t)p1 + 512); } - - kernel_fpu_end(); } static void xor_avx_3(unsigned long bytes, unsigned long * __restrict p0, @@ -59,8 +53,6 @@ static void xor_avx_3(unsigned long bytes, unsigned long * __restrict p0, { unsigned long lines = bytes >> 9; - kernel_fpu_begin(); - while (lines--) { #undef BLOCK #define BLOCK(i, reg) \ @@ -80,8 +72,6 @@ do { \ p1 = (unsigned long *)((uintptr_t)p1 + 512); p2 = (unsigned long *)((uintptr_t)p2 + 512); } - - kernel_fpu_end(); } static void xor_avx_4(unsigned long bytes, unsigned long * __restrict p0, @@ -91,8 +81,6 @@ static void xor_avx_4(unsigned long bytes, unsigned long * __restrict p0, { unsigned long lines = bytes >> 9; - kernel_fpu_begin(); - while (lines--) { #undef BLOCK #define BLOCK(i, reg) \ @@ -115,8 +103,6 @@ do { \ p2 = (unsigned long *)((uintptr_t)p2 + 512); p3 = (unsigned long *)((uintptr_t)p3 + 512); } - - kernel_fpu_end(); } static void xor_avx_5(unsigned long bytes, unsigned long * __restrict p0, @@ -127,8 +113,6 @@ static void xor_avx_5(unsigned long bytes, unsigned long * __restrict p0, { unsigned long lines = bytes >> 9; - kernel_fpu_begin(); - while (lines--) { #undef BLOCK #define BLOCK(i, reg) \ @@ -154,25 +138,19 @@ do { \ p3 = (unsigned long *)((uintptr_t)p3 + 512); p4 = (unsigned long *)((uintptr_t)p4 + 512); } +} +DO_XOR_BLOCKS(avx_inner, xor_avx_2, xor_avx_3, xor_avx_4, xor_avx_5); + +static void xor_gen_avx(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes) +{ + kernel_fpu_begin(); + xor_gen_avx_inner(dest, srcs, src_cnt, bytes); kernel_fpu_end(); } -static struct xor_block_template xor_block_avx = { - .name = "avx", - .do_2 = xor_avx_2, - .do_3 = xor_avx_3, - .do_4 = xor_avx_4, - .do_5 = xor_avx_5, +struct xor_block_template xor_block_avx = { + .name = "avx", + .xor_gen = xor_gen_avx, }; - -#define AVX_XOR_SPEED \ -do { \ - if (boot_cpu_has(X86_FEATURE_AVX) && boot_cpu_has(X86_FEATURE_OSXSAVE)) \ - xor_speed(&xor_block_avx); \ -} while (0) - -#define AVX_SELECT(FASTEST) \ - (boot_cpu_has(X86_FEATURE_AVX) && boot_cpu_has(X86_FEATURE_OSXSAVE) ? &xor_block_avx : FASTEST) - -#endif diff --git a/arch/x86/include/asm/xor_32.h b/lib/raid/xor/x86/xor-mmx.c similarity index 87% rename from arch/x86/include/asm/xor_32.h rename to lib/raid/xor/x86/xor-mmx.c index 7a6b9474591e..63a8b0444fce 100644 --- a/arch/x86/include/asm/xor_32.h +++ b/lib/raid/xor/x86/xor-mmx.c @@ -1,15 +1,12 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef _ASM_X86_XOR_32_H -#define _ASM_X86_XOR_32_H - +// SPDX-License-Identifier: GPL-2.0-or-later /* - * Optimized RAID-5 checksumming functions for MMX. - */ - -/* - * High-speed RAID5 checksumming functions utilizing MMX instructions. + * Optimized XOR parity functions for MMX. + * * Copyright (C) 1998 Ingo Molnar. */ +#include +#include "xor_impl.h" +#include "xor_arch.h" #define LD(x, y) " movq 8*("#x")(%1), %%mm"#y" ;\n" #define ST(x, y) " movq %%mm"#y", 8*("#x")(%1) ;\n" @@ -18,16 +15,12 @@ #define XO3(x, y) " pxor 8*("#x")(%4), %%mm"#y" ;\n" #define XO4(x, y) " pxor 8*("#x")(%5), %%mm"#y" ;\n" -#include - static void xor_pII_mmx_2(unsigned long bytes, unsigned long * __restrict p1, const unsigned long * __restrict p2) { unsigned long lines = bytes >> 7; - kernel_fpu_begin(); - asm volatile( #undef BLOCK #define BLOCK(i) \ @@ -60,8 +53,6 @@ xor_pII_mmx_2(unsigned long bytes, unsigned long * __restrict p1, "+r" (p1), "+r" (p2) : : "memory"); - - kernel_fpu_end(); } static void @@ -71,8 +62,6 @@ xor_pII_mmx_3(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 7; - kernel_fpu_begin(); - asm volatile( #undef BLOCK #define BLOCK(i) \ @@ -110,8 +99,6 @@ xor_pII_mmx_3(unsigned long bytes, unsigned long * __restrict p1, "+r" (p1), "+r" (p2), "+r" (p3) : : "memory"); - - kernel_fpu_end(); } static void @@ -122,8 +109,6 @@ xor_pII_mmx_4(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 7; - kernel_fpu_begin(); - asm volatile( #undef BLOCK #define BLOCK(i) \ @@ -166,8 +151,6 @@ xor_pII_mmx_4(unsigned long bytes, unsigned long * __restrict p1, "+r" (p1), "+r" (p2), "+r" (p3), "+r" (p4) : : "memory"); - - kernel_fpu_end(); } @@ -180,8 +163,6 @@ xor_pII_mmx_5(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 7; - kernel_fpu_begin(); - /* Make sure GCC forgets anything it knows about p4 or p5, such that it won't pass to the asm volatile below a register that is shared with any other variable. That's @@ -242,8 +223,6 @@ xor_pII_mmx_5(unsigned long bytes, unsigned long * __restrict p1, Clobber them just to be sure nobody does something stupid like assuming they have some legal value. */ asm("" : "=r" (p4), "=r" (p5)); - - kernel_fpu_end(); } #undef LD @@ -260,8 +239,6 @@ xor_p5_mmx_2(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 6; - kernel_fpu_begin(); - asm volatile( " .align 32 ;\n" " 1: ;\n" @@ -298,8 +275,6 @@ xor_p5_mmx_2(unsigned long bytes, unsigned long * __restrict p1, "+r" (p1), "+r" (p2) : : "memory"); - - kernel_fpu_end(); } static void @@ -309,8 +284,6 @@ xor_p5_mmx_3(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 6; - kernel_fpu_begin(); - asm volatile( " .align 32,0x90 ;\n" " 1: ;\n" @@ -356,8 +329,6 @@ xor_p5_mmx_3(unsigned long bytes, unsigned long * __restrict p1, "+r" (p1), "+r" (p2), "+r" (p3) : : "memory" ); - - kernel_fpu_end(); } static void @@ -368,8 +339,6 @@ xor_p5_mmx_4(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 6; - kernel_fpu_begin(); - asm volatile( " .align 32,0x90 ;\n" " 1: ;\n" @@ -424,8 +393,6 @@ xor_p5_mmx_4(unsigned long bytes, unsigned long * __restrict p1, "+r" (p1), "+r" (p2), "+r" (p3), "+r" (p4) : : "memory"); - - kernel_fpu_end(); } static void @@ -437,8 +404,6 @@ xor_p5_mmx_5(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 6; - kernel_fpu_begin(); - /* Make sure GCC forgets anything it knows about p4 or p5, such that it won't pass to the asm volatile below a register that is shared with any other variable. That's @@ -515,59 +480,36 @@ xor_p5_mmx_5(unsigned long bytes, unsigned long * __restrict p1, Clobber them just to be sure nobody does something stupid like assuming they have some legal value. */ asm("" : "=r" (p4), "=r" (p5)); +} +DO_XOR_BLOCKS(pII_mmx_inner, xor_pII_mmx_2, xor_pII_mmx_3, xor_pII_mmx_4, + xor_pII_mmx_5); + +static void xor_gen_pII_mmx(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes) +{ + kernel_fpu_begin(); + xor_gen_pII_mmx_inner(dest, srcs, src_cnt, bytes); kernel_fpu_end(); } -static struct xor_block_template xor_block_pII_mmx = { - .name = "pII_mmx", - .do_2 = xor_pII_mmx_2, - .do_3 = xor_pII_mmx_3, - .do_4 = xor_pII_mmx_4, - .do_5 = xor_pII_mmx_5, +struct xor_block_template xor_block_pII_mmx = { + .name = "pII_mmx", + .xor_gen = xor_gen_pII_mmx, }; -static struct xor_block_template xor_block_p5_mmx = { - .name = "p5_mmx", - .do_2 = xor_p5_mmx_2, - .do_3 = xor_p5_mmx_3, - .do_4 = xor_p5_mmx_4, - .do_5 = xor_p5_mmx_5, +DO_XOR_BLOCKS(p5_mmx_inner, xor_p5_mmx_2, xor_p5_mmx_3, xor_p5_mmx_4, + xor_p5_mmx_5); + +static void xor_gen_p5_mmx(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes) +{ + kernel_fpu_begin(); + xor_gen_p5_mmx_inner(dest, srcs, src_cnt, bytes); + kernel_fpu_end(); +} + +struct xor_block_template xor_block_p5_mmx = { + .name = "p5_mmx", + .xor_gen = xor_gen_p5_mmx, }; - -static struct xor_block_template xor_block_pIII_sse = { - .name = "pIII_sse", - .do_2 = xor_sse_2, - .do_3 = xor_sse_3, - .do_4 = xor_sse_4, - .do_5 = xor_sse_5, -}; - -/* Also try the AVX routines */ -#include - -/* Also try the generic routines. */ -#include - -/* We force the use of the SSE xor block because it can write around L2. - We may also be able to load into the L1 only depending on how the cpu - deals with a load to a line that is being prefetched. */ -#undef XOR_TRY_TEMPLATES -#define XOR_TRY_TEMPLATES \ -do { \ - AVX_XOR_SPEED; \ - if (boot_cpu_has(X86_FEATURE_XMM)) { \ - xor_speed(&xor_block_pIII_sse); \ - xor_speed(&xor_block_sse_pf64); \ - } else if (boot_cpu_has(X86_FEATURE_MMX)) { \ - xor_speed(&xor_block_pII_mmx); \ - xor_speed(&xor_block_p5_mmx); \ - } else { \ - xor_speed(&xor_block_8regs); \ - xor_speed(&xor_block_8regs_p); \ - xor_speed(&xor_block_32regs); \ - xor_speed(&xor_block_32regs_p); \ - } \ -} while (0) - -#endif /* _ASM_X86_XOR_32_H */ diff --git a/arch/x86/include/asm/xor.h b/lib/raid/xor/x86/xor-sse.c similarity index 90% rename from arch/x86/include/asm/xor.h rename to lib/raid/xor/x86/xor-sse.c index 7b0307acc410..c6626ecae6ba 100644 --- a/arch/x86/include/asm/xor.h +++ b/lib/raid/xor/x86/xor-sse.c @@ -1,31 +1,20 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef _ASM_X86_XOR_H -#define _ASM_X86_XOR_H - -/* - * Optimized RAID-5 checksumming functions for SSE. - */ - +// SPDX-License-Identifier: GPL-2.0-or-later /* + * Optimized XOR parity functions for SSE. + * * Cache avoiding checksumming functions utilizing KNI instructions * Copyright (C) 1999 Zach Brown (with obvious credit due Ingo) - */ - -/* + * * Based on * High-speed RAID5 checksumming functions utilizing SSE instructions. * Copyright (C) 1998 Ingo Molnar. - */ - -/* + * * x86-64 changes / gcc fixes from Andi Kleen. * Copyright 2002 Andi Kleen, SuSE Labs. - * - * This hasn't been optimized for the hammer yet, but there are likely - * no advantages to be gotten from x86-64 here anyways. */ - #include +#include "xor_impl.h" +#include "xor_arch.h" #ifdef CONFIG_X86_32 /* reduce register pressure */ @@ -62,8 +51,6 @@ xor_sse_2(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 8; - kernel_fpu_begin(); - asm volatile( #undef BLOCK #define BLOCK(i) \ @@ -104,8 +91,6 @@ xor_sse_2(unsigned long bytes, unsigned long * __restrict p1, [p1] "+r" (p1), [p2] "+r" (p2) : [inc] XOR_CONSTANT_CONSTRAINT (256UL) : "memory"); - - kernel_fpu_end(); } static void @@ -114,8 +99,6 @@ xor_sse_2_pf64(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 8; - kernel_fpu_begin(); - asm volatile( #undef BLOCK #define BLOCK(i) \ @@ -139,8 +122,6 @@ xor_sse_2_pf64(unsigned long bytes, unsigned long * __restrict p1, [p1] "+r" (p1), [p2] "+r" (p2) : [inc] XOR_CONSTANT_CONSTRAINT (256UL) : "memory"); - - kernel_fpu_end(); } static void @@ -150,8 +131,6 @@ xor_sse_3(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 8; - kernel_fpu_begin(); - asm volatile( #undef BLOCK #define BLOCK(i) \ @@ -199,8 +178,6 @@ xor_sse_3(unsigned long bytes, unsigned long * __restrict p1, [p1] "+r" (p1), [p2] "+r" (p2), [p3] "+r" (p3) : [inc] XOR_CONSTANT_CONSTRAINT (256UL) : "memory"); - - kernel_fpu_end(); } static void @@ -210,8 +187,6 @@ xor_sse_3_pf64(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 8; - kernel_fpu_begin(); - asm volatile( #undef BLOCK #define BLOCK(i) \ @@ -237,8 +212,6 @@ xor_sse_3_pf64(unsigned long bytes, unsigned long * __restrict p1, [p1] "+r" (p1), [p2] "+r" (p2), [p3] "+r" (p3) : [inc] XOR_CONSTANT_CONSTRAINT (256UL) : "memory"); - - kernel_fpu_end(); } static void @@ -249,8 +222,6 @@ xor_sse_4(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 8; - kernel_fpu_begin(); - asm volatile( #undef BLOCK #define BLOCK(i) \ @@ -305,8 +276,6 @@ xor_sse_4(unsigned long bytes, unsigned long * __restrict p1, [p2] "+r" (p2), [p3] "+r" (p3), [p4] "+r" (p4) : [inc] XOR_CONSTANT_CONSTRAINT (256UL) : "memory"); - - kernel_fpu_end(); } static void @@ -317,8 +286,6 @@ xor_sse_4_pf64(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 8; - kernel_fpu_begin(); - asm volatile( #undef BLOCK #define BLOCK(i) \ @@ -346,8 +313,6 @@ xor_sse_4_pf64(unsigned long bytes, unsigned long * __restrict p1, [p2] "+r" (p2), [p3] "+r" (p3), [p4] "+r" (p4) : [inc] XOR_CONSTANT_CONSTRAINT (256UL) : "memory"); - - kernel_fpu_end(); } static void @@ -359,8 +324,6 @@ xor_sse_5(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 8; - kernel_fpu_begin(); - asm volatile( #undef BLOCK #define BLOCK(i) \ @@ -422,8 +385,6 @@ xor_sse_5(unsigned long bytes, unsigned long * __restrict p1, [p3] "+r" (p3), [p4] "+r" (p4), [p5] "+r" (p5) : [inc] XOR_CONSTANT_CONSTRAINT (256UL) : "memory"); - - kernel_fpu_end(); } static void @@ -435,8 +396,6 @@ xor_sse_5_pf64(unsigned long bytes, unsigned long * __restrict p1, { unsigned long lines = bytes >> 8; - kernel_fpu_begin(); - asm volatile( #undef BLOCK #define BLOCK(i) \ @@ -466,37 +425,35 @@ xor_sse_5_pf64(unsigned long bytes, unsigned long * __restrict p1, [p3] "+r" (p3), [p4] "+r" (p4), [p5] "+r" (p5) : [inc] XOR_CONSTANT_CONSTRAINT (256UL) : "memory"); +} +DO_XOR_BLOCKS(sse_inner, xor_sse_2, xor_sse_3, xor_sse_4, xor_sse_5); + +static void xor_gen_sse(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes) +{ + kernel_fpu_begin(); + xor_gen_sse_inner(dest, srcs, src_cnt, bytes); kernel_fpu_end(); } -static struct xor_block_template xor_block_sse_pf64 = { - .name = "prefetch64-sse", - .do_2 = xor_sse_2_pf64, - .do_3 = xor_sse_3_pf64, - .do_4 = xor_sse_4_pf64, - .do_5 = xor_sse_5_pf64, +struct xor_block_template xor_block_sse = { + .name = "sse", + .xor_gen = xor_gen_sse, }; -#undef LD -#undef XO1 -#undef XO2 -#undef XO3 -#undef XO4 -#undef ST -#undef NOP -#undef BLK64 -#undef BLOCK +DO_XOR_BLOCKS(sse_pf64_inner, xor_sse_2_pf64, xor_sse_3_pf64, xor_sse_4_pf64, + xor_sse_5_pf64); -#undef XOR_CONSTANT_CONSTRAINT +static void xor_gen_sse_pf64(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes) +{ + kernel_fpu_begin(); + xor_gen_sse_pf64_inner(dest, srcs, src_cnt, bytes); + kernel_fpu_end(); +} -#ifdef CONFIG_X86_32 -# include -#else -# include -#endif - -#define XOR_SELECT_TEMPLATE(FASTEST) \ - AVX_SELECT(FASTEST) - -#endif /* _ASM_X86_XOR_H */ +struct xor_block_template xor_block_sse_pf64 = { + .name = "prefetch64-sse", + .xor_gen = xor_gen_sse_pf64, +}; diff --git a/lib/raid/xor/x86/xor_arch.h b/lib/raid/xor/x86/xor_arch.h new file mode 100644 index 000000000000..99fe85a213c6 --- /dev/null +++ b/lib/raid/xor/x86/xor_arch.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include + +extern struct xor_block_template xor_block_pII_mmx; +extern struct xor_block_template xor_block_p5_mmx; +extern struct xor_block_template xor_block_sse; +extern struct xor_block_template xor_block_sse_pf64; +extern struct xor_block_template xor_block_avx; + +/* + * When SSE is available, use it as it can write around L2. We may also be able + * to load into the L1 only depending on how the cpu deals with a load to a line + * that is being prefetched. + * + * When AVX2 is available, force using it as it is better by all measures. + * + * 32-bit without MMX can fall back to the generic routines. + */ +static __always_inline void __init arch_xor_init(void) +{ + if (boot_cpu_has(X86_FEATURE_AVX) && + boot_cpu_has(X86_FEATURE_OSXSAVE)) { + xor_force(&xor_block_avx); + } else if (IS_ENABLED(CONFIG_X86_64) || boot_cpu_has(X86_FEATURE_XMM)) { + xor_register(&xor_block_sse); + xor_register(&xor_block_sse_pf64); + } else if (boot_cpu_has(X86_FEATURE_MMX)) { + xor_register(&xor_block_pII_mmx); + xor_register(&xor_block_p5_mmx); + } else { + xor_register(&xor_block_8regs); + xor_register(&xor_block_8regs_p); + xor_register(&xor_block_32regs); + xor_register(&xor_block_32regs_p); + } +} diff --git a/lib/raid/xor/xor-32regs-prefetch.c b/lib/raid/xor/xor-32regs-prefetch.c new file mode 100644 index 000000000000..ade2a7d8cbe2 --- /dev/null +++ b/lib/raid/xor/xor-32regs-prefetch.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "xor_impl.h" + +static void +xor_32regs_p_2(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2) +{ + long lines = bytes / (sizeof (long)) / 8 - 1; + + prefetchw(p1); + prefetch(p2); + + do { + register long d0, d1, d2, d3, d4, d5, d6, d7; + + prefetchw(p1+8); + prefetch(p2+8); + once_more: + d0 = p1[0]; /* Pull the stuff into registers */ + d1 = p1[1]; /* ... in bursts, if possible. */ + d2 = p1[2]; + d3 = p1[3]; + d4 = p1[4]; + d5 = p1[5]; + d6 = p1[6]; + d7 = p1[7]; + d0 ^= p2[0]; + d1 ^= p2[1]; + d2 ^= p2[2]; + d3 ^= p2[3]; + d4 ^= p2[4]; + d5 ^= p2[5]; + d6 ^= p2[6]; + d7 ^= p2[7]; + p1[0] = d0; /* Store the result (in bursts) */ + p1[1] = d1; + p1[2] = d2; + p1[3] = d3; + p1[4] = d4; + p1[5] = d5; + p1[6] = d6; + p1[7] = d7; + p1 += 8; + p2 += 8; + } while (--lines > 0); + if (lines == 0) + goto once_more; +} + +static void +xor_32regs_p_3(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3) +{ + long lines = bytes / (sizeof (long)) / 8 - 1; + + prefetchw(p1); + prefetch(p2); + prefetch(p3); + + do { + register long d0, d1, d2, d3, d4, d5, d6, d7; + + prefetchw(p1+8); + prefetch(p2+8); + prefetch(p3+8); + once_more: + d0 = p1[0]; /* Pull the stuff into registers */ + d1 = p1[1]; /* ... in bursts, if possible. */ + d2 = p1[2]; + d3 = p1[3]; + d4 = p1[4]; + d5 = p1[5]; + d6 = p1[6]; + d7 = p1[7]; + d0 ^= p2[0]; + d1 ^= p2[1]; + d2 ^= p2[2]; + d3 ^= p2[3]; + d4 ^= p2[4]; + d5 ^= p2[5]; + d6 ^= p2[6]; + d7 ^= p2[7]; + d0 ^= p3[0]; + d1 ^= p3[1]; + d2 ^= p3[2]; + d3 ^= p3[3]; + d4 ^= p3[4]; + d5 ^= p3[5]; + d6 ^= p3[6]; + d7 ^= p3[7]; + p1[0] = d0; /* Store the result (in bursts) */ + p1[1] = d1; + p1[2] = d2; + p1[3] = d3; + p1[4] = d4; + p1[5] = d5; + p1[6] = d6; + p1[7] = d7; + p1 += 8; + p2 += 8; + p3 += 8; + } while (--lines > 0); + if (lines == 0) + goto once_more; +} + +static void +xor_32regs_p_4(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3, + const unsigned long * __restrict p4) +{ + long lines = bytes / (sizeof (long)) / 8 - 1; + + prefetchw(p1); + prefetch(p2); + prefetch(p3); + prefetch(p4); + + do { + register long d0, d1, d2, d3, d4, d5, d6, d7; + + prefetchw(p1+8); + prefetch(p2+8); + prefetch(p3+8); + prefetch(p4+8); + once_more: + d0 = p1[0]; /* Pull the stuff into registers */ + d1 = p1[1]; /* ... in bursts, if possible. */ + d2 = p1[2]; + d3 = p1[3]; + d4 = p1[4]; + d5 = p1[5]; + d6 = p1[6]; + d7 = p1[7]; + d0 ^= p2[0]; + d1 ^= p2[1]; + d2 ^= p2[2]; + d3 ^= p2[3]; + d4 ^= p2[4]; + d5 ^= p2[5]; + d6 ^= p2[6]; + d7 ^= p2[7]; + d0 ^= p3[0]; + d1 ^= p3[1]; + d2 ^= p3[2]; + d3 ^= p3[3]; + d4 ^= p3[4]; + d5 ^= p3[5]; + d6 ^= p3[6]; + d7 ^= p3[7]; + d0 ^= p4[0]; + d1 ^= p4[1]; + d2 ^= p4[2]; + d3 ^= p4[3]; + d4 ^= p4[4]; + d5 ^= p4[5]; + d6 ^= p4[6]; + d7 ^= p4[7]; + p1[0] = d0; /* Store the result (in bursts) */ + p1[1] = d1; + p1[2] = d2; + p1[3] = d3; + p1[4] = d4; + p1[5] = d5; + p1[6] = d6; + p1[7] = d7; + p1 += 8; + p2 += 8; + p3 += 8; + p4 += 8; + } while (--lines > 0); + if (lines == 0) + goto once_more; +} + +static void +xor_32regs_p_5(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3, + const unsigned long * __restrict p4, + const unsigned long * __restrict p5) +{ + long lines = bytes / (sizeof (long)) / 8 - 1; + + prefetchw(p1); + prefetch(p2); + prefetch(p3); + prefetch(p4); + prefetch(p5); + + do { + register long d0, d1, d2, d3, d4, d5, d6, d7; + + prefetchw(p1+8); + prefetch(p2+8); + prefetch(p3+8); + prefetch(p4+8); + prefetch(p5+8); + once_more: + d0 = p1[0]; /* Pull the stuff into registers */ + d1 = p1[1]; /* ... in bursts, if possible. */ + d2 = p1[2]; + d3 = p1[3]; + d4 = p1[4]; + d5 = p1[5]; + d6 = p1[6]; + d7 = p1[7]; + d0 ^= p2[0]; + d1 ^= p2[1]; + d2 ^= p2[2]; + d3 ^= p2[3]; + d4 ^= p2[4]; + d5 ^= p2[5]; + d6 ^= p2[6]; + d7 ^= p2[7]; + d0 ^= p3[0]; + d1 ^= p3[1]; + d2 ^= p3[2]; + d3 ^= p3[3]; + d4 ^= p3[4]; + d5 ^= p3[5]; + d6 ^= p3[6]; + d7 ^= p3[7]; + d0 ^= p4[0]; + d1 ^= p4[1]; + d2 ^= p4[2]; + d3 ^= p4[3]; + d4 ^= p4[4]; + d5 ^= p4[5]; + d6 ^= p4[6]; + d7 ^= p4[7]; + d0 ^= p5[0]; + d1 ^= p5[1]; + d2 ^= p5[2]; + d3 ^= p5[3]; + d4 ^= p5[4]; + d5 ^= p5[5]; + d6 ^= p5[6]; + d7 ^= p5[7]; + p1[0] = d0; /* Store the result (in bursts) */ + p1[1] = d1; + p1[2] = d2; + p1[3] = d3; + p1[4] = d4; + p1[5] = d5; + p1[6] = d6; + p1[7] = d7; + p1 += 8; + p2 += 8; + p3 += 8; + p4 += 8; + p5 += 8; + } while (--lines > 0); + if (lines == 0) + goto once_more; +} + +DO_XOR_BLOCKS(32regs_p, xor_32regs_p_2, xor_32regs_p_3, xor_32regs_p_4, + xor_32regs_p_5); + +struct xor_block_template xor_block_32regs_p = { + .name = "32regs_prefetch", + .xor_gen = xor_gen_32regs_p, +}; diff --git a/lib/raid/xor/xor-32regs.c b/lib/raid/xor/xor-32regs.c new file mode 100644 index 000000000000..acb4a10d1e95 --- /dev/null +++ b/lib/raid/xor/xor-32regs.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "xor_impl.h" + +static void +xor_32regs_2(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2) +{ + long lines = bytes / (sizeof (long)) / 8; + + do { + register long d0, d1, d2, d3, d4, d5, d6, d7; + d0 = p1[0]; /* Pull the stuff into registers */ + d1 = p1[1]; /* ... in bursts, if possible. */ + d2 = p1[2]; + d3 = p1[3]; + d4 = p1[4]; + d5 = p1[5]; + d6 = p1[6]; + d7 = p1[7]; + d0 ^= p2[0]; + d1 ^= p2[1]; + d2 ^= p2[2]; + d3 ^= p2[3]; + d4 ^= p2[4]; + d5 ^= p2[5]; + d6 ^= p2[6]; + d7 ^= p2[7]; + p1[0] = d0; /* Store the result (in bursts) */ + p1[1] = d1; + p1[2] = d2; + p1[3] = d3; + p1[4] = d4; + p1[5] = d5; + p1[6] = d6; + p1[7] = d7; + p1 += 8; + p2 += 8; + } while (--lines > 0); +} + +static void +xor_32regs_3(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3) +{ + long lines = bytes / (sizeof (long)) / 8; + + do { + register long d0, d1, d2, d3, d4, d5, d6, d7; + d0 = p1[0]; /* Pull the stuff into registers */ + d1 = p1[1]; /* ... in bursts, if possible. */ + d2 = p1[2]; + d3 = p1[3]; + d4 = p1[4]; + d5 = p1[5]; + d6 = p1[6]; + d7 = p1[7]; + d0 ^= p2[0]; + d1 ^= p2[1]; + d2 ^= p2[2]; + d3 ^= p2[3]; + d4 ^= p2[4]; + d5 ^= p2[5]; + d6 ^= p2[6]; + d7 ^= p2[7]; + d0 ^= p3[0]; + d1 ^= p3[1]; + d2 ^= p3[2]; + d3 ^= p3[3]; + d4 ^= p3[4]; + d5 ^= p3[5]; + d6 ^= p3[6]; + d7 ^= p3[7]; + p1[0] = d0; /* Store the result (in bursts) */ + p1[1] = d1; + p1[2] = d2; + p1[3] = d3; + p1[4] = d4; + p1[5] = d5; + p1[6] = d6; + p1[7] = d7; + p1 += 8; + p2 += 8; + p3 += 8; + } while (--lines > 0); +} + +static void +xor_32regs_4(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3, + const unsigned long * __restrict p4) +{ + long lines = bytes / (sizeof (long)) / 8; + + do { + register long d0, d1, d2, d3, d4, d5, d6, d7; + d0 = p1[0]; /* Pull the stuff into registers */ + d1 = p1[1]; /* ... in bursts, if possible. */ + d2 = p1[2]; + d3 = p1[3]; + d4 = p1[4]; + d5 = p1[5]; + d6 = p1[6]; + d7 = p1[7]; + d0 ^= p2[0]; + d1 ^= p2[1]; + d2 ^= p2[2]; + d3 ^= p2[3]; + d4 ^= p2[4]; + d5 ^= p2[5]; + d6 ^= p2[6]; + d7 ^= p2[7]; + d0 ^= p3[0]; + d1 ^= p3[1]; + d2 ^= p3[2]; + d3 ^= p3[3]; + d4 ^= p3[4]; + d5 ^= p3[5]; + d6 ^= p3[6]; + d7 ^= p3[7]; + d0 ^= p4[0]; + d1 ^= p4[1]; + d2 ^= p4[2]; + d3 ^= p4[3]; + d4 ^= p4[4]; + d5 ^= p4[5]; + d6 ^= p4[6]; + d7 ^= p4[7]; + p1[0] = d0; /* Store the result (in bursts) */ + p1[1] = d1; + p1[2] = d2; + p1[3] = d3; + p1[4] = d4; + p1[5] = d5; + p1[6] = d6; + p1[7] = d7; + p1 += 8; + p2 += 8; + p3 += 8; + p4 += 8; + } while (--lines > 0); +} + +static void +xor_32regs_5(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3, + const unsigned long * __restrict p4, + const unsigned long * __restrict p5) +{ + long lines = bytes / (sizeof (long)) / 8; + + do { + register long d0, d1, d2, d3, d4, d5, d6, d7; + d0 = p1[0]; /* Pull the stuff into registers */ + d1 = p1[1]; /* ... in bursts, if possible. */ + d2 = p1[2]; + d3 = p1[3]; + d4 = p1[4]; + d5 = p1[5]; + d6 = p1[6]; + d7 = p1[7]; + d0 ^= p2[0]; + d1 ^= p2[1]; + d2 ^= p2[2]; + d3 ^= p2[3]; + d4 ^= p2[4]; + d5 ^= p2[5]; + d6 ^= p2[6]; + d7 ^= p2[7]; + d0 ^= p3[0]; + d1 ^= p3[1]; + d2 ^= p3[2]; + d3 ^= p3[3]; + d4 ^= p3[4]; + d5 ^= p3[5]; + d6 ^= p3[6]; + d7 ^= p3[7]; + d0 ^= p4[0]; + d1 ^= p4[1]; + d2 ^= p4[2]; + d3 ^= p4[3]; + d4 ^= p4[4]; + d5 ^= p4[5]; + d6 ^= p4[6]; + d7 ^= p4[7]; + d0 ^= p5[0]; + d1 ^= p5[1]; + d2 ^= p5[2]; + d3 ^= p5[3]; + d4 ^= p5[4]; + d5 ^= p5[5]; + d6 ^= p5[6]; + d7 ^= p5[7]; + p1[0] = d0; /* Store the result (in bursts) */ + p1[1] = d1; + p1[2] = d2; + p1[3] = d3; + p1[4] = d4; + p1[5] = d5; + p1[6] = d6; + p1[7] = d7; + p1 += 8; + p2 += 8; + p3 += 8; + p4 += 8; + p5 += 8; + } while (--lines > 0); +} + +DO_XOR_BLOCKS(32regs, xor_32regs_2, xor_32regs_3, xor_32regs_4, xor_32regs_5); + +struct xor_block_template xor_block_32regs = { + .name = "32regs", + .xor_gen = xor_gen_32regs, +}; diff --git a/lib/raid/xor/xor-8regs-prefetch.c b/lib/raid/xor/xor-8regs-prefetch.c new file mode 100644 index 000000000000..451527a951b1 --- /dev/null +++ b/lib/raid/xor/xor-8regs-prefetch.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "xor_impl.h" + +static void +xor_8regs_p_2(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2) +{ + long lines = bytes / (sizeof (long)) / 8 - 1; + prefetchw(p1); + prefetch(p2); + + do { + prefetchw(p1+8); + prefetch(p2+8); + once_more: + p1[0] ^= p2[0]; + p1[1] ^= p2[1]; + p1[2] ^= p2[2]; + p1[3] ^= p2[3]; + p1[4] ^= p2[4]; + p1[5] ^= p2[5]; + p1[6] ^= p2[6]; + p1[7] ^= p2[7]; + p1 += 8; + p2 += 8; + } while (--lines > 0); + if (lines == 0) + goto once_more; +} + +static void +xor_8regs_p_3(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3) +{ + long lines = bytes / (sizeof (long)) / 8 - 1; + prefetchw(p1); + prefetch(p2); + prefetch(p3); + + do { + prefetchw(p1+8); + prefetch(p2+8); + prefetch(p3+8); + once_more: + p1[0] ^= p2[0] ^ p3[0]; + p1[1] ^= p2[1] ^ p3[1]; + p1[2] ^= p2[2] ^ p3[2]; + p1[3] ^= p2[3] ^ p3[3]; + p1[4] ^= p2[4] ^ p3[4]; + p1[5] ^= p2[5] ^ p3[5]; + p1[6] ^= p2[6] ^ p3[6]; + p1[7] ^= p2[7] ^ p3[7]; + p1 += 8; + p2 += 8; + p3 += 8; + } while (--lines > 0); + if (lines == 0) + goto once_more; +} + +static void +xor_8regs_p_4(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3, + const unsigned long * __restrict p4) +{ + long lines = bytes / (sizeof (long)) / 8 - 1; + + prefetchw(p1); + prefetch(p2); + prefetch(p3); + prefetch(p4); + + do { + prefetchw(p1+8); + prefetch(p2+8); + prefetch(p3+8); + prefetch(p4+8); + once_more: + p1[0] ^= p2[0] ^ p3[0] ^ p4[0]; + p1[1] ^= p2[1] ^ p3[1] ^ p4[1]; + p1[2] ^= p2[2] ^ p3[2] ^ p4[2]; + p1[3] ^= p2[3] ^ p3[3] ^ p4[3]; + p1[4] ^= p2[4] ^ p3[4] ^ p4[4]; + p1[5] ^= p2[5] ^ p3[5] ^ p4[5]; + p1[6] ^= p2[6] ^ p3[6] ^ p4[6]; + p1[7] ^= p2[7] ^ p3[7] ^ p4[7]; + p1 += 8; + p2 += 8; + p3 += 8; + p4 += 8; + } while (--lines > 0); + if (lines == 0) + goto once_more; +} + +static void +xor_8regs_p_5(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3, + const unsigned long * __restrict p4, + const unsigned long * __restrict p5) +{ + long lines = bytes / (sizeof (long)) / 8 - 1; + + prefetchw(p1); + prefetch(p2); + prefetch(p3); + prefetch(p4); + prefetch(p5); + + do { + prefetchw(p1+8); + prefetch(p2+8); + prefetch(p3+8); + prefetch(p4+8); + prefetch(p5+8); + once_more: + p1[0] ^= p2[0] ^ p3[0] ^ p4[0] ^ p5[0]; + p1[1] ^= p2[1] ^ p3[1] ^ p4[1] ^ p5[1]; + p1[2] ^= p2[2] ^ p3[2] ^ p4[2] ^ p5[2]; + p1[3] ^= p2[3] ^ p3[3] ^ p4[3] ^ p5[3]; + p1[4] ^= p2[4] ^ p3[4] ^ p4[4] ^ p5[4]; + p1[5] ^= p2[5] ^ p3[5] ^ p4[5] ^ p5[5]; + p1[6] ^= p2[6] ^ p3[6] ^ p4[6] ^ p5[6]; + p1[7] ^= p2[7] ^ p3[7] ^ p4[7] ^ p5[7]; + p1 += 8; + p2 += 8; + p3 += 8; + p4 += 8; + p5 += 8; + } while (--lines > 0); + if (lines == 0) + goto once_more; +} + + +DO_XOR_BLOCKS(8regs_p, xor_8regs_p_2, xor_8regs_p_3, xor_8regs_p_4, + xor_8regs_p_5); + +struct xor_block_template xor_block_8regs_p = { + .name = "8regs_prefetch", + .xor_gen = xor_gen_8regs_p, +}; diff --git a/lib/raid/xor/xor-8regs.c b/lib/raid/xor/xor-8regs.c new file mode 100644 index 000000000000..1edaed8acffe --- /dev/null +++ b/lib/raid/xor/xor-8regs.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "xor_impl.h" + +static void +xor_8regs_2(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2) +{ + long lines = bytes / (sizeof (long)) / 8; + + do { + p1[0] ^= p2[0]; + p1[1] ^= p2[1]; + p1[2] ^= p2[2]; + p1[3] ^= p2[3]; + p1[4] ^= p2[4]; + p1[5] ^= p2[5]; + p1[6] ^= p2[6]; + p1[7] ^= p2[7]; + p1 += 8; + p2 += 8; + } while (--lines > 0); +} + +static void +xor_8regs_3(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3) +{ + long lines = bytes / (sizeof (long)) / 8; + + do { + p1[0] ^= p2[0] ^ p3[0]; + p1[1] ^= p2[1] ^ p3[1]; + p1[2] ^= p2[2] ^ p3[2]; + p1[3] ^= p2[3] ^ p3[3]; + p1[4] ^= p2[4] ^ p3[4]; + p1[5] ^= p2[5] ^ p3[5]; + p1[6] ^= p2[6] ^ p3[6]; + p1[7] ^= p2[7] ^ p3[7]; + p1 += 8; + p2 += 8; + p3 += 8; + } while (--lines > 0); +} + +static void +xor_8regs_4(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3, + const unsigned long * __restrict p4) +{ + long lines = bytes / (sizeof (long)) / 8; + + do { + p1[0] ^= p2[0] ^ p3[0] ^ p4[0]; + p1[1] ^= p2[1] ^ p3[1] ^ p4[1]; + p1[2] ^= p2[2] ^ p3[2] ^ p4[2]; + p1[3] ^= p2[3] ^ p3[3] ^ p4[3]; + p1[4] ^= p2[4] ^ p3[4] ^ p4[4]; + p1[5] ^= p2[5] ^ p3[5] ^ p4[5]; + p1[6] ^= p2[6] ^ p3[6] ^ p4[6]; + p1[7] ^= p2[7] ^ p3[7] ^ p4[7]; + p1 += 8; + p2 += 8; + p3 += 8; + p4 += 8; + } while (--lines > 0); +} + +static void +xor_8regs_5(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, + const unsigned long * __restrict p3, + const unsigned long * __restrict p4, + const unsigned long * __restrict p5) +{ + long lines = bytes / (sizeof (long)) / 8; + + do { + p1[0] ^= p2[0] ^ p3[0] ^ p4[0] ^ p5[0]; + p1[1] ^= p2[1] ^ p3[1] ^ p4[1] ^ p5[1]; + p1[2] ^= p2[2] ^ p3[2] ^ p4[2] ^ p5[2]; + p1[3] ^= p2[3] ^ p3[3] ^ p4[3] ^ p5[3]; + p1[4] ^= p2[4] ^ p3[4] ^ p4[4] ^ p5[4]; + p1[5] ^= p2[5] ^ p3[5] ^ p4[5] ^ p5[5]; + p1[6] ^= p2[6] ^ p3[6] ^ p4[6] ^ p5[6]; + p1[7] ^= p2[7] ^ p3[7] ^ p4[7] ^ p5[7]; + p1 += 8; + p2 += 8; + p3 += 8; + p4 += 8; + p5 += 8; + } while (--lines > 0); +} + +#ifndef NO_TEMPLATE +DO_XOR_BLOCKS(8regs, xor_8regs_2, xor_8regs_3, xor_8regs_4, xor_8regs_5); + +struct xor_block_template xor_block_8regs = { + .name = "8regs", + .xor_gen = xor_gen_8regs, +}; +#endif /* NO_TEMPLATE */ diff --git a/lib/raid/xor/xor-core.c b/lib/raid/xor/xor-core.c new file mode 100644 index 000000000000..bd4e6e434418 --- /dev/null +++ b/lib/raid/xor/xor-core.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 1996, 1997, 1998, 1999, 2000, + * Ingo Molnar, Matti Aarnio, Jakub Jelinek, Richard Henderson. + * + * Dispatch optimized XOR parity functions. + */ + +#include +#include +#include +#include +#include +#include +#include "xor_impl.h" + +DEFINE_STATIC_CALL_NULL(xor_gen_impl, *xor_block_8regs.xor_gen); + +/** + * xor_gen - generate RAID-style XOR information + * @dest: destination vector + * @srcs: source vectors + * @src_cnt: number of source vectors + * @bytes: length in bytes of each vector + * + * Performs bit-wise XOR operation into @dest for each of the @src_cnt vectors + * in @srcs for a length of @bytes bytes. @src_cnt must be non-zero, and the + * memory pointed to by @dest and each member of @srcs must be at least 64-byte + * aligned. @bytes must be non-zero and a multiple of 512. + * + * Note: for typical RAID uses, @dest either needs to be zeroed, or filled with + * the first disk, which then needs to be removed from @srcs. + */ +void xor_gen(void *dest, void **srcs, unsigned int src_cnt, unsigned int bytes) +{ + WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); + WARN_ON_ONCE(bytes == 0); + WARN_ON_ONCE(bytes & 511); + + static_call(xor_gen_impl)(dest, srcs, src_cnt, bytes); +} +EXPORT_SYMBOL(xor_gen); + +/* Set of all registered templates. */ +static struct xor_block_template *__initdata template_list; +static struct xor_block_template *forced_template; + +/** + * xor_register - register a XOR template + * @tmpl: template to register + * + * Register a XOR implementation with the core. Registered implementations + * will be measured by a trivial benchmark, and the fastest one is chosen + * unless an implementation is forced using xor_force(). + */ +void __init xor_register(struct xor_block_template *tmpl) +{ + tmpl->next = template_list; + template_list = tmpl; +} + +/** + * xor_force - force use of a XOR template + * @tmpl: template to register + * + * Register a XOR implementation with the core and force using it. Forcing + * an implementation will make the core ignore any template registered using + * xor_register(), or any previous implementation forced using xor_force(). + */ +void __init xor_force(struct xor_block_template *tmpl) +{ + forced_template = tmpl; +} + +#define BENCH_SIZE 4096 +#define REPS 800U + +static void __init +do_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2) +{ + int speed; + unsigned long reps; + ktime_t min, start, t0; + void *srcs[1] = { b2 }; + + preempt_disable(); + + reps = 0; + t0 = ktime_get(); + /* delay start until time has advanced */ + while ((start = ktime_get()) == t0) + cpu_relax(); + do { + mb(); /* prevent loop optimization */ + tmpl->xor_gen(b1, srcs, 1, BENCH_SIZE); + mb(); + } while (reps++ < REPS || (t0 = ktime_get()) == start); + min = ktime_sub(t0, start); + + preempt_enable(); + + // bytes/ns == GB/s, multiply by 1000 to get MB/s [not MiB/s] + speed = (1000 * reps * BENCH_SIZE) / (unsigned int)ktime_to_ns(min); + tmpl->speed = speed; + + pr_info(" %-16s: %5d MB/sec\n", tmpl->name, speed); +} + +static int __init calibrate_xor_blocks(void) +{ + void *b1, *b2; + struct xor_block_template *f, *fastest; + + if (forced_template) + return 0; + + b1 = (void *) __get_free_pages(GFP_KERNEL, 2); + if (!b1) { + pr_warn("xor: Yikes! No memory available.\n"); + return -ENOMEM; + } + b2 = b1 + 2*PAGE_SIZE + BENCH_SIZE; + + pr_info("xor: measuring software checksum speed\n"); + fastest = template_list; + for (f = template_list; f; f = f->next) { + do_xor_speed(f, b1, b2); + if (f->speed > fastest->speed) + fastest = f; + } + static_call_update(xor_gen_impl, fastest->xor_gen); + pr_info("xor: using function: %s (%d MB/sec)\n", + fastest->name, fastest->speed); + + free_pages((unsigned long)b1, 2); + return 0; +} + +#ifdef CONFIG_XOR_BLOCKS_ARCH +#include "xor_arch.h" /* $SRCARCH/xor_arch.h */ +#else +static void __init arch_xor_init(void) +{ + xor_register(&xor_block_8regs); + xor_register(&xor_block_8regs_p); + xor_register(&xor_block_32regs); + xor_register(&xor_block_32regs_p); +} +#endif /* CONFIG_XOR_BLOCKS_ARCH */ + +static int __init xor_init(void) +{ + arch_xor_init(); + + /* + * If this arch/cpu has a short-circuited selection, don't loop through + * all the possible functions, just use the best one. + */ + if (forced_template) { + pr_info("xor: automatically using best checksumming function %-10s\n", + forced_template->name); + static_call_update(xor_gen_impl, forced_template->xor_gen); + return 0; + } + +#ifdef MODULE + return calibrate_xor_blocks(); +#else + /* + * Pick the first template as the temporary default until calibration + * happens. + */ + static_call_update(xor_gen_impl, template_list->xor_gen); + return 0; +#endif +} + +static __exit void xor_exit(void) +{ +} + +MODULE_DESCRIPTION("RAID-5 checksumming functions"); +MODULE_LICENSE("GPL"); + +/* + * When built-in we must register the default template before md, but we don't + * want calibration to run that early as that would delay the boot process. + */ +#ifndef MODULE +__initcall(calibrate_xor_blocks); +#endif +core_initcall(xor_init); +module_exit(xor_exit); diff --git a/lib/raid/xor/xor_impl.h b/lib/raid/xor/xor_impl.h new file mode 100644 index 000000000000..09ae2916f71e --- /dev/null +++ b/lib/raid/xor/xor_impl.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _XOR_IMPL_H +#define _XOR_IMPL_H + +#include +#include + +struct xor_block_template { + struct xor_block_template *next; + const char *name; + int speed; + void (*xor_gen)(void *dest, void **srcs, unsigned int src_cnt, + unsigned int bytes); +}; + +#define __DO_XOR_BLOCKS(_name, _handle1, _handle2, _handle3, _handle4) \ +void \ +xor_gen_##_name(void *dest, void **srcs, unsigned int src_cnt, \ + unsigned int bytes) \ +{ \ + unsigned int src_off = 0; \ + \ + while (src_cnt > 0) { \ + unsigned int this_cnt = min(src_cnt, 4); \ + \ + if (this_cnt == 1) \ + _handle1(bytes, dest, srcs[src_off]); \ + else if (this_cnt == 2) \ + _handle2(bytes, dest, srcs[src_off], \ + srcs[src_off + 1]); \ + else if (this_cnt == 3) \ + _handle3(bytes, dest, srcs[src_off], \ + srcs[src_off + 1], srcs[src_off + 2]); \ + else \ + _handle4(bytes, dest, srcs[src_off], \ + srcs[src_off + 1], srcs[src_off + 2], \ + srcs[src_off + 3]); \ + \ + src_cnt -= this_cnt; \ + src_off += this_cnt; \ + } \ +} + +#define DO_XOR_BLOCKS(_name, _handle1, _handle2, _handle3, _handle4) \ + static __DO_XOR_BLOCKS(_name, _handle1, _handle2, _handle3, _handle4) + +/* generic implementations */ +extern struct xor_block_template xor_block_8regs; +extern struct xor_block_template xor_block_32regs; +extern struct xor_block_template xor_block_8regs_p; +extern struct xor_block_template xor_block_32regs_p; + +void __init xor_register(struct xor_block_template *tmpl); +void __init xor_force(struct xor_block_template *tmpl); + +#endif /* _XOR_IMPL_H */ diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 7a67ef5b67b6..04b3a808fca9 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -114,6 +114,14 @@ static void bucket_table_free(const struct bucket_table *tbl) kvfree(tbl); } +static void bucket_table_free_atomic(const struct bucket_table *tbl) +{ + if (tbl->nest) + nested_bucket_table_free(tbl); + + kvfree_atomic(tbl); +} + static void bucket_table_free_rcu(struct rcu_head *head) { bucket_table_free(container_of(head, struct bucket_table, rcu)); @@ -496,7 +504,7 @@ static int rhashtable_insert_rehash(struct rhashtable *ht, err = rhashtable_rehash_attach(ht, tbl, new_tbl); if (err) { - bucket_table_free(new_tbl); + bucket_table_free_atomic(new_tbl); if (err == -EEXIST) err = 0; } else @@ -1166,6 +1174,11 @@ static void rhashtable_free_one(struct rhashtable *ht, struct rhash_head *obj, * This function will eventually sleep to wait for an async resize * to complete. The caller is responsible that no further write operations * occurs in parallel. + * + * After cancel_work_sync() has returned, the deferred rehash worker is + * quiesced and, per the contract above, no other concurrent access to the + * rhashtable is possible. The tables are therefore owned exclusively by + * this function and can be walked without ht->mutex held. */ void rhashtable_free_and_destroy(struct rhashtable *ht, void (*free_fn)(void *ptr, void *arg), @@ -1177,8 +1190,15 @@ void rhashtable_free_and_destroy(struct rhashtable *ht, irq_work_sync(&ht->run_irq_work); cancel_work_sync(&ht->run_work); - mutex_lock(&ht->mutex); - tbl = rht_dereference(ht->tbl, ht); + /* + * Do NOT take ht->mutex here. The rehash worker establishes + * ht->mutex -> fs_reclaim via GFP_KERNEL bucket allocation under + * the mutex; callers on the reclaim path (e.g. simple_xattr_ht_free() + * from evict() under the dcache shrinker for shmem/kernfs/pidfs + * inodes) would otherwise close a circular dependency + * fs_reclaim -> ht->mutex. + */ + tbl = rcu_dereference_raw(ht->tbl); restart: if (free_fn) { for (i = 0; i < tbl->size; i++) { @@ -1187,22 +1207,21 @@ restart: cond_resched(); for (pos = rht_ptr_exclusive(rht_bucket(tbl, i)), next = !rht_is_a_nulls(pos) ? - rht_dereference(pos->next, ht) : NULL; + rcu_dereference_raw(pos->next) : NULL; !rht_is_a_nulls(pos); pos = next, next = !rht_is_a_nulls(pos) ? - rht_dereference(pos->next, ht) : NULL) + rcu_dereference_raw(pos->next) : NULL) rhashtable_free_one(ht, pos, free_fn, arg); } } - next_tbl = rht_dereference(tbl->future_tbl, ht); + next_tbl = rcu_dereference_raw(tbl->future_tbl); bucket_table_free(tbl); if (next_tbl) { tbl = next_tbl; goto restart; } - mutex_unlock(&ht->mutex); } EXPORT_SYMBOL_GPL(rhashtable_free_and_destroy); diff --git a/lib/scatterlist.c b/lib/scatterlist.c index d773720d11bf..b7fe91ef35b8 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -1123,8 +1123,7 @@ static ssize_t extract_user_to_sg(struct iov_iter *iter, size_t len, off; /* We decant the page list into the tail of the scatterlist */ - pages = (void *)sgtable->sgl + - array_size(sg_max, sizeof(struct scatterlist)); + pages = (void *)sg + array_size(sg_max, sizeof(struct scatterlist)); pages -= sg_max; do { @@ -1247,7 +1246,7 @@ static ssize_t extract_kvec_to_sg(struct iov_iter *iter, else page = virt_to_page((void *)kaddr); - sg_set_page(sg, page, len, off); + sg_set_page(sg, page, seg, off); sgtable->nents++; sg++; sg_max--; @@ -1256,6 +1255,7 @@ static ssize_t extract_kvec_to_sg(struct iov_iter *iter, kaddr += PAGE_SIZE; off = 0; } while (len > 0 && sg_max > 0); + ret -= len; if (maxsize <= 0 || sg_max == 0) break; @@ -1409,7 +1409,7 @@ ssize_t extract_iter_to_sg(struct iov_iter *iter, size_t maxsize, struct sg_table *sgtable, unsigned int sg_max, iov_iter_extraction_t extraction_flags) { - if (maxsize == 0) + if (maxsize == 0 || sg_max == 0) return 0; switch (iov_iter_type(iter)) { diff --git a/lib/test_hmm.c b/lib/test_hmm.c index 0964d53365e6..213504915737 100644 --- a/lib/test_hmm.c +++ b/lib/test_hmm.c @@ -185,11 +185,73 @@ static int dmirror_fops_open(struct inode *inode, struct file *filp) return 0; } +static void dmirror_device_evict_chunk(struct dmirror_chunk *chunk) +{ + unsigned long start_pfn = chunk->pagemap.range.start >> PAGE_SHIFT; + unsigned long end_pfn = chunk->pagemap.range.end >> PAGE_SHIFT; + unsigned long npages = end_pfn - start_pfn + 1; + unsigned long i; + unsigned long *src_pfns; + unsigned long *dst_pfns; + unsigned int order = 0; + + src_pfns = kvcalloc(npages, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL); + dst_pfns = kvcalloc(npages, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL); + + migrate_device_range(src_pfns, start_pfn, npages); + for (i = 0; i < npages; i++) { + struct page *dpage, *spage; + + spage = migrate_pfn_to_page(src_pfns[i]); + if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE)) + continue; + + if (WARN_ON(!is_device_private_page(spage) && + !is_device_coherent_page(spage))) + continue; + + order = folio_order(page_folio(spage)); + spage = BACKING_PAGE(spage); + if (src_pfns[i] & MIGRATE_PFN_COMPOUND) { + dpage = folio_page(folio_alloc(GFP_HIGHUSER_MOVABLE, + order), 0); + } else { + dpage = alloc_page(GFP_HIGHUSER_MOVABLE | __GFP_NOFAIL); + order = 0; + } + + /* TODO Support splitting here */ + lock_page(dpage); + dst_pfns[i] = migrate_pfn(page_to_pfn(dpage)); + if (src_pfns[i] & MIGRATE_PFN_WRITE) + dst_pfns[i] |= MIGRATE_PFN_WRITE; + if (order) + dst_pfns[i] |= MIGRATE_PFN_COMPOUND; + folio_copy(page_folio(dpage), page_folio(spage)); + } + migrate_device_pages(src_pfns, dst_pfns, npages); + migrate_device_finalize(src_pfns, dst_pfns, npages); + kvfree(src_pfns); + kvfree(dst_pfns); +} + static int dmirror_fops_release(struct inode *inode, struct file *filp) { struct dmirror *dmirror = filp->private_data; + struct dmirror_device *mdevice = dmirror->mdevice; + int i; mmu_interval_notifier_remove(&dmirror->notifier); + + if (mdevice->devmem_chunks) { + for (i = 0; i < mdevice->devmem_count; i++) { + struct dmirror_chunk *devmem = + mdevice->devmem_chunks[i]; + + dmirror_device_evict_chunk(devmem); + } + } + xa_destroy(&dmirror->pt); kfree(dmirror); return 0; @@ -1377,56 +1439,6 @@ static int dmirror_snapshot(struct dmirror *dmirror, return ret; } -static void dmirror_device_evict_chunk(struct dmirror_chunk *chunk) -{ - unsigned long start_pfn = chunk->pagemap.range.start >> PAGE_SHIFT; - unsigned long end_pfn = chunk->pagemap.range.end >> PAGE_SHIFT; - unsigned long npages = end_pfn - start_pfn + 1; - unsigned long i; - unsigned long *src_pfns; - unsigned long *dst_pfns; - unsigned int order = 0; - - src_pfns = kvcalloc(npages, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL); - dst_pfns = kvcalloc(npages, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL); - - migrate_device_range(src_pfns, start_pfn, npages); - for (i = 0; i < npages; i++) { - struct page *dpage, *spage; - - spage = migrate_pfn_to_page(src_pfns[i]); - if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE)) - continue; - - if (WARN_ON(!is_device_private_page(spage) && - !is_device_coherent_page(spage))) - continue; - - order = folio_order(page_folio(spage)); - spage = BACKING_PAGE(spage); - if (src_pfns[i] & MIGRATE_PFN_COMPOUND) { - dpage = folio_page(folio_alloc(GFP_HIGHUSER_MOVABLE, - order), 0); - } else { - dpage = alloc_page(GFP_HIGHUSER_MOVABLE | __GFP_NOFAIL); - order = 0; - } - - /* TODO Support splitting here */ - lock_page(dpage); - dst_pfns[i] = migrate_pfn(page_to_pfn(dpage)); - if (src_pfns[i] & MIGRATE_PFN_WRITE) - dst_pfns[i] |= MIGRATE_PFN_WRITE; - if (order) - dst_pfns[i] |= MIGRATE_PFN_COMPOUND; - folio_copy(page_folio(dpage), page_folio(spage)); - } - migrate_device_pages(src_pfns, dst_pfns, npages); - migrate_device_finalize(src_pfns, dst_pfns, npages); - kvfree(src_pfns); - kvfree(dst_pfns); -} - /* Removes free pages from the free list so they can't be re-allocated */ static void dmirror_remove_free_pages(struct dmirror_chunk *devmem) { @@ -1726,6 +1738,13 @@ static const struct dev_pagemap_ops dmirror_devmem_ops = { .folio_split = dmirror_devmem_folio_split, }; +static void dmirror_device_release(struct device *dev) +{ + struct dmirror_device *mdevice = container_of(dev, struct dmirror_device, device); + + dmirror_device_remove_chunks(mdevice); +} + static int dmirror_device_init(struct dmirror_device *mdevice, int id) { dev_t dev; @@ -1737,6 +1756,8 @@ static int dmirror_device_init(struct dmirror_device *mdevice, int id) cdev_init(&mdevice->cdevice, &dmirror_fops); mdevice->cdevice.owner = THIS_MODULE; + mdevice->device.release = dmirror_device_release; + device_initialize(&mdevice->device); mdevice->device.devt = dev; @@ -1744,12 +1765,16 @@ static int dmirror_device_init(struct dmirror_device *mdevice, int id) if (ret) goto put_device; + /* Build a list of free ZONE_DEVICE struct pages */ + ret = dmirror_allocate_chunk(mdevice, NULL, false); + if (ret) + goto put_device; + ret = cdev_device_add(&mdevice->cdevice, &mdevice->device); if (ret) goto put_device; - /* Build a list of free ZONE_DEVICE struct pages */ - return dmirror_allocate_chunk(mdevice, NULL, false); + return 0; put_device: put_device(&mdevice->device); @@ -1758,7 +1783,6 @@ put_device: static void dmirror_device_remove(struct dmirror_device *mdevice) { - dmirror_device_remove_chunks(mdevice); cdev_device_del(&mdevice->cdevice, &mdevice->device); put_device(&mdevice->device); } diff --git a/lib/test_kho.c b/lib/test_kho.c index 7ef9e4061869..aa6a0956bb8b 100644 --- a/lib/test_kho.c +++ b/lib/test_kho.c @@ -143,7 +143,8 @@ static int kho_test_preserve(struct kho_test_state *state) if (err) goto err_unpreserve_data; - err = kho_add_subtree(KHO_TEST_FDT, folio_address(state->fdt)); + err = kho_add_subtree(KHO_TEST_FDT, folio_address(state->fdt), + fdt_totalsize(folio_address(state->fdt))); if (err) goto err_unpreserve_data; @@ -318,7 +319,7 @@ static int __init kho_test_init(void) if (!kho_is_enabled()) return 0; - err = kho_retrieve_subtree(KHO_TEST_FDT, &fdt_phys); + err = kho_retrieve_subtree(KHO_TEST_FDT, &fdt_phys, NULL); if (!err) { err = kho_test_restore(fdt_phys); if (err) diff --git a/lib/test_maple_tree.c b/lib/test_maple_tree.c index 434d8a2fdd99..b9367c61e8b5 100644 --- a/lib/test_maple_tree.c +++ b/lib/test_maple_tree.c @@ -2,7 +2,7 @@ /* * test_maple_tree.c: Test the maple tree API * Copyright (c) 2018-2022 Oracle Corporation - * Author: Liam R. Howlett + * Author: Liam R. Howlett * * Any tests that only require the interface of the tree. */ @@ -4021,6 +4021,6 @@ static void __exit maple_tree_harvest(void) module_init(maple_tree_seed); module_exit(maple_tree_harvest); -MODULE_AUTHOR("Liam R. Howlett "); +MODULE_AUTHOR("Liam R. Howlett "); MODULE_DESCRIPTION("maple tree API test module"); MODULE_LICENSE("GPL"); diff --git a/lib/tests/Makefile b/lib/tests/Makefile index 05f74edbc62b..7e9c2fa52e35 100644 --- a/lib/tests/Makefile +++ b/lib/tests/Makefile @@ -40,6 +40,8 @@ obj-$(CONFIG_MEMCPY_KUNIT_TEST) += memcpy_kunit.o obj-$(CONFIG_MIN_HEAP_KUNIT_TEST) += min_heap_kunit.o CFLAGS_overflow_kunit.o = $(call cc-disable-warning, tautological-constant-out-of-range-compare) obj-$(CONFIG_OVERFLOW_KUNIT_TEST) += overflow_kunit.o +# GCC < 12.1 can miscompile errptr() test when branch profiling is enabled. +CFLAGS_printf_kunit.o += -DDISABLE_BRANCH_PROFILING obj-$(CONFIG_PRINTF_KUNIT_TEST) += printf_kunit.o obj-$(CONFIG_RANDSTRUCT_KUNIT_TEST) += randstruct_kunit.o obj-$(CONFIG_SCANF_KUNIT_TEST) += scanf_kunit.o diff --git a/lib/tests/kunit_iov_iter.c b/lib/tests/kunit_iov_iter.c index bb847e5010eb..37bd6eb25896 100644 --- a/lib/tests/kunit_iov_iter.c +++ b/lib/tests/kunit_iov_iter.c @@ -13,6 +13,9 @@ #include #include #include +#include +#include +#include #include MODULE_DESCRIPTION("iov_iter testing"); @@ -37,12 +40,12 @@ static const struct kvec_test_range kvec_test_ranges[] = { static inline u8 pattern(unsigned long x) { - return x & 0xff; + return (u8)x + (u8)(x >> 8) + (u8)(x >> 16); } static void iov_kunit_unmap(void *data) { - vunmap(data); + vfree(data); } static void *__init iov_kunit_create_buffer(struct kunit *test, @@ -52,18 +55,27 @@ static void *__init iov_kunit_create_buffer(struct kunit *test, struct page **pages; unsigned long got; void *buffer; + unsigned int i; - pages = kunit_kcalloc(test, npages, sizeof(struct page *), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages); + pages = kzalloc_objs(struct page *, npages, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages); *ppages = pages; got = alloc_pages_bulk(GFP_KERNEL, npages, pages); if (got != npages) { release_pages(pages, got); + kvfree(pages); KUNIT_ASSERT_EQ(test, got, npages); } + /* Make sure that we don't get a physically contiguous buffer. */ + for (i = 0; i < npages / 4; ++i) + swap(pages[i], pages[i + npages / 2]); buffer = vmap(pages, npages, VM_MAP | VM_MAP_PUT_PAGES, PAGE_KERNEL); + if (buffer == NULL) { + release_pages(pages, got); + kvfree(pages); + } KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer); kunit_add_action_or_reset(test, iov_kunit_unmap, buffer); @@ -369,9 +381,6 @@ static void iov_kunit_destroy_folioq(void *data) for (folioq = data; folioq; folioq = next) { next = folioq->next; - for (int i = 0; i < folioq_nr_slots(folioq); i++) - if (folioq_folio(folioq, i)) - folio_put(folioq_folio(folioq, i)); kfree(folioq); } } @@ -1009,6 +1018,202 @@ stop: KUNIT_SUCCEED(test); } +struct iov_kunit_iter_to_sg_data { + struct sg_table *sgt; + u8 *buffer, *scratch; + u8 __user *ubuf; + struct page **pages; + size_t npages; +}; + +static void __init +iov_kunit_iter_unpin_sgt(void *data) +{ + struct sg_table *sgt = data; + + for (unsigned int i = 0; i < sgt->nents; ++i) + unpin_user_page(sg_page(&sgt->sgl[i])); +} + +static void __init +iov_kunit_iter_to_sg_init(struct kunit *test, size_t bufsize, bool user, + struct iov_kunit_iter_to_sg_data *data) +{ + struct page **spages; + struct scatterlist *sg; + unsigned long uaddr; + size_t i; + + data->npages = bufsize / PAGE_SIZE; + sg = kunit_kmalloc_array(test, data->npages, sizeof(*sg), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sg); + sg_init_table(sg, data->npages); + data->sgt = kunit_kzalloc(test, sizeof(*data->sgt), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, data->sgt); + data->sgt->orig_nents = 0; + data->sgt->sgl = sg; + + data->buffer = NULL; + data->ubuf = NULL; + if (user) { + uaddr = kunit_vm_mmap(test, NULL, 0, bufsize, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, 0); + KUNIT_ASSERT_NE(test, uaddr, 0); + data->ubuf = (u8 __user *)uaddr; + for (i = 0; i < bufsize; ++i) + put_user(pattern(i), data->ubuf + i); + } else { + data->buffer = iov_kunit_create_buffer(test, &data->pages, + data->npages); + for (i = 0; i < bufsize; ++i) + data->buffer[i] = pattern(i); + } + data->scratch = iov_kunit_create_buffer(test, &spages, data->npages); + memset(data->scratch, 0, bufsize); +} + +static void __init +iov_kunit_iter_to_sg_check(struct kunit *test, struct iov_iter *iter, + size_t bufsize, + struct iov_kunit_iter_to_sg_data *data) +{ + static const size_t tail = 16 * PAGE_SIZE; + size_t i; + + KUNIT_ASSERT_LT(test, tail, bufsize); + + if (iov_iter_extract_will_pin(iter)) + kunit_add_action_or_reset(test, iov_kunit_iter_unpin_sgt, + data->sgt); + + i = extract_iter_to_sg(iter, bufsize, data->sgt, 0, 0); + KUNIT_ASSERT_EQ(test, i, 0); + KUNIT_ASSERT_EQ(test, data->sgt->nents, 0); + + i = extract_iter_to_sg(iter, bufsize - tail, data->sgt, 1, 0); + KUNIT_ASSERT_LE(test, i, bufsize - tail); + KUNIT_ASSERT_EQ(test, data->sgt->nents, 1); + + i += extract_iter_to_sg(iter, bufsize - tail - i, data->sgt, + data->npages - data->sgt->nents, 0); + KUNIT_ASSERT_EQ(test, i, bufsize - tail); + KUNIT_ASSERT_LE(test, data->sgt->nents, data->npages); + + i += extract_iter_to_sg(iter, tail, data->sgt, + data->npages - data->sgt->nents, 0); + KUNIT_ASSERT_EQ(test, i, bufsize); + KUNIT_ASSERT_LE(test, data->sgt->nents, data->npages); + + sg_mark_end(&data->sgt->sgl[data->sgt->nents - 1]); + + i = sg_copy_to_buffer(data->sgt->sgl, data->sgt->nents, + data->scratch, bufsize); + KUNIT_ASSERT_EQ(test, i, bufsize); + + for (i = 0; i < bufsize; ++i) { + KUNIT_EXPECT_EQ_MSG(test, data->scratch[i], pattern(i), + "at i=%zx", i); + if (data->scratch[i] != pattern(i)) + break; + } + + KUNIT_EXPECT_EQ(test, i, bufsize); +} + +static void __init iov_kunit_iter_to_sg_kvec(struct kunit *test) +{ + struct iov_kunit_iter_to_sg_data data; + struct iov_iter iter; + struct kvec kvec; + size_t bufsize; + + bufsize = 0x100000; + iov_kunit_iter_to_sg_init(test, bufsize, false, &data); + + kvec.iov_base = data.buffer; + kvec.iov_len = bufsize; + iov_iter_kvec(&iter, READ, &kvec, 1, bufsize); + + iov_kunit_iter_to_sg_check(test, &iter, bufsize, &data); +} + +static void __init iov_kunit_iter_to_sg_bvec(struct kunit *test) +{ + struct iov_kunit_iter_to_sg_data data; + struct page *p, *can_merge = NULL; + size_t i, k, bufsize; + struct bio_vec *bvec; + struct iov_iter iter; + + bufsize = 0x100000; + iov_kunit_iter_to_sg_init(test, bufsize, false, &data); + + bvec = kunit_kmalloc_array(test, data.npages, sizeof(*bvec), + GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bvec); + k = 0; + for (i = 0; i < data.npages; ++i) { + p = data.pages[i]; + if (p == can_merge) + bvec[k-1].bv_len += PAGE_SIZE; + else + bvec_set_page(&bvec[k++], p, PAGE_SIZE, 0); + can_merge = p + 1; + } + iov_iter_bvec(&iter, READ, bvec, k, bufsize); + + iov_kunit_iter_to_sg_check(test, &iter, bufsize, &data); +} + +static void __init iov_kunit_iter_to_sg_folioq(struct kunit *test) +{ + struct iov_kunit_iter_to_sg_data data; + struct folio_queue *folioq; + struct iov_iter iter; + size_t bufsize; + + bufsize = 0x100000; + iov_kunit_iter_to_sg_init(test, bufsize, false, &data); + + folioq = iov_kunit_create_folioq(test); + iov_kunit_load_folioq(test, &iter, READ, folioq, data.pages, + data.npages); + + iov_kunit_iter_to_sg_check(test, &iter, bufsize, &data); +} + +static void __init iov_kunit_iter_to_sg_xarray(struct kunit *test) +{ + struct iov_kunit_iter_to_sg_data data; + struct xarray *xarray; + struct iov_iter iter; + size_t bufsize; + + bufsize = 0x100000; + iov_kunit_iter_to_sg_init(test, bufsize, false, &data); + + xarray = iov_kunit_create_xarray(test); + iov_kunit_load_xarray(test, &iter, READ, xarray, data.pages, + data.npages); + + iov_kunit_iter_to_sg_check(test, &iter, bufsize, &data); +} + +static void __init iov_kunit_iter_to_sg_ubuf(struct kunit *test) +{ + struct iov_kunit_iter_to_sg_data data; + struct iov_iter iter; + size_t bufsize; + + bufsize = 0x100000; + iov_kunit_iter_to_sg_init(test, bufsize, true, &data); + + iov_iter_ubuf(&iter, READ, data.ubuf, bufsize); + + iov_kunit_iter_to_sg_check(test, &iter, bufsize, &data); +} + static struct kunit_case __refdata iov_kunit_cases[] = { KUNIT_CASE(iov_kunit_copy_to_kvec), KUNIT_CASE(iov_kunit_copy_from_kvec), @@ -1022,6 +1227,11 @@ static struct kunit_case __refdata iov_kunit_cases[] = { KUNIT_CASE(iov_kunit_extract_pages_bvec), KUNIT_CASE(iov_kunit_extract_pages_folioq), KUNIT_CASE(iov_kunit_extract_pages_xarray), + KUNIT_CASE(iov_kunit_iter_to_sg_kvec), + KUNIT_CASE(iov_kunit_iter_to_sg_bvec), + KUNIT_CASE(iov_kunit_iter_to_sg_folioq), + KUNIT_CASE(iov_kunit_iter_to_sg_xarray), + KUNIT_CASE(iov_kunit_iter_to_sg_ubuf), {} }; diff --git a/lib/tests/liveupdate.c b/lib/tests/liveupdate.c index 496d6ef91a30..e4b0ecbee32f 100644 --- a/lib/tests/liveupdate.c +++ b/lib/tests/liveupdate.c @@ -135,24 +135,6 @@ void liveupdate_test_register(struct liveupdate_file_handler *fh) TEST_NFLBS, fh->compatible); } -void liveupdate_test_unregister(struct liveupdate_file_handler *fh) -{ - int err, i; - - for (i = 0; i < TEST_NFLBS; i++) { - struct liveupdate_flb *flb = &test_flbs[i]; - - err = liveupdate_unregister_flb(fh, flb); - if (err) { - pr_err("Failed to unregister %s %pe\n", - flb->compatible, ERR_PTR(err)); - } - } - - pr_info("Unregistered %d FLBs from file handler: [%s]\n", - TEST_NFLBS, fh->compatible); -} - MODULE_LICENSE("GPL"); MODULE_AUTHOR("Pasha Tatashin "); MODULE_DESCRIPTION("In-kernel test for LUO mechanism"); diff --git a/lib/tests/printf_kunit.c b/lib/tests/printf_kunit.c index f6f21b445ece..bb70b9cddadd 100644 --- a/lib/tests/printf_kunit.c +++ b/lib/tests/printf_kunit.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -437,6 +438,27 @@ ip4(struct kunit *kunittest) static void ip6(struct kunit *kunittest) { + const struct in6_addr addr = { + .s6_addr = { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 } + }; + const struct in6_addr single_zero = { + .s6_addr = { 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 } + }; + struct sockaddr_in6 sa = { + .sin6_family = AF_INET6, + .sin6_port = cpu_to_be16(12345), + .sin6_addr = addr, + }; + + test("00010002000300040005000600070008|0001:0002:0003:0004:0005:0006:0007:0008", + "%pi6|%pI6", &addr, &addr); + test("00010002000300040005000600070008|0001:0002:0003:0004:0005:0006:0007:0008", + "%piS|%pIS", &sa, &sa); + test("1:2:3:4:5:6:7:8", "%pI6c", &addr); + test("1:0:3:4:5:6:7:8", "%pI6c", &single_zero); + test("[1:2:3:4:5:6:7:8]:12345", "%pISpc", &sa); } static void diff --git a/lib/tests/string_kunit.c b/lib/tests/string_kunit.c index f9a8e557ba77..0819ace5b027 100644 --- a/lib/tests/string_kunit.c +++ b/lib/tests/string_kunit.c @@ -6,10 +6,18 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include +#include +#include +#include #include +#include #include #include #include +#include +#include +#include #define STRCMP_LARGE_BUF_LEN 2048 #define STRCMP_CHANGE_POINT 1337 @@ -17,6 +25,12 @@ #define STRCMP_TEST_EXPECT_LOWER(test, fn, ...) KUNIT_EXPECT_LT(test, fn(__VA_ARGS__), 0) #define STRCMP_TEST_EXPECT_GREATER(test, fn, ...) KUNIT_EXPECT_GT(test, fn(__VA_ARGS__), 0) +#define STRING_TEST_MAX_LEN 128 +#define STRING_TEST_MAX_OFFSET 16 + +#define STRING_BENCH_SEED 888 +#define STRING_BENCH_WORKLOAD (1 * MEGA) + static void string_test_memset16(struct kunit *test) { unsigned i, j, k; @@ -104,6 +118,64 @@ static void string_test_memset64(struct kunit *test) } } +static void string_test_strlen(struct kunit *test) +{ + size_t buf_size; + char *buf, *s; + + buf_size = PAGE_ALIGN(STRING_TEST_MAX_LEN + STRING_TEST_MAX_OFFSET + 1); + buf = vmalloc(buf_size); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + + memset(buf, 'A', buf_size); + + for (size_t offset = 0; offset < STRING_TEST_MAX_OFFSET; offset++) { + for (size_t len = 0; len <= STRING_TEST_MAX_LEN; len++) { + s = buf + buf_size - 1 - offset - len; + s[len] = '\0'; + KUNIT_EXPECT_EQ_MSG(test, strlen(s), len, + "offset:%zu len:%zu", offset, len); + s[len] = 'A'; + } + } + + vfree(buf); +} + +static void string_test_strnlen(struct kunit *test) +{ + size_t buf_size; + char *buf, *s; + + buf_size = PAGE_ALIGN(STRING_TEST_MAX_LEN + STRING_TEST_MAX_OFFSET + 1); + buf = vmalloc(buf_size); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + + memset(buf, 'A', buf_size); + + for (size_t offset = 0; offset < STRING_TEST_MAX_OFFSET; offset++) { + for (size_t len = 0; len <= STRING_TEST_MAX_LEN; len++) { + s = buf + buf_size - 1 - offset - len; + s[len] = '\0'; + + if (len > 0) + KUNIT_EXPECT_EQ(test, strnlen(s, len - 1), len - 1); + if (len > 1) + KUNIT_EXPECT_EQ(test, strnlen(s, len - 2), len - 2); + + KUNIT_EXPECT_EQ(test, strnlen(s, len), len); + + KUNIT_EXPECT_EQ(test, strnlen(s, len + 1), len); + KUNIT_EXPECT_EQ(test, strnlen(s, len + 2), len); + KUNIT_EXPECT_EQ(test, strnlen(s, len + 10), len); + + s[len] = 'A'; + } + } + + vfree(buf); +} + static void string_test_strchr(struct kunit *test) { const char *test_string = "abcdefghijkl"; @@ -127,6 +199,36 @@ static void string_test_strchr(struct kunit *test) KUNIT_ASSERT_NULL(test, result); } +static void string_test_strrchr(struct kunit *test) +{ + size_t buf_size; + char *buf, *s; + + buf_size = PAGE_ALIGN(STRING_TEST_MAX_LEN + STRING_TEST_MAX_OFFSET + 1); + buf = vmalloc(buf_size); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + + memset(buf, 'A', buf_size); + + for (size_t offset = 0; offset < STRING_TEST_MAX_OFFSET; offset++) { + for (size_t len = 0; len <= STRING_TEST_MAX_LEN; len++) { + s = buf + buf_size - 1 - offset - len; + s[len] = '\0'; + + KUNIT_EXPECT_PTR_EQ(test, strrchr(s, 'Z'), NULL); + + if (len > 0) + KUNIT_EXPECT_PTR_EQ(test, strrchr(s, 'A'), s + len - 1); + else + KUNIT_EXPECT_PTR_EQ(test, strrchr(s, 'A'), NULL); + + s[len] = 'A'; + } + } + + vfree(buf); +} + static void string_test_strnchr(struct kunit *test) { const char *test_string = "abcdefghijkl"; @@ -614,12 +716,180 @@ static void string_test_strends(struct kunit *test) KUNIT_EXPECT_TRUE(test, strends("", "")); } +#if IS_ENABLED(CONFIG_STRING_KUNIT_BENCH) +/* Target string lengths for benchmarking */ +static const size_t bench_lens[] = { + 0, 1, 7, 8, 16, 31, 64, 127, 512, 1024, 3173, 4096, +}; + +/** + * alloc_max_bench_buffer() - Allocate buffer for the max test case. + * @test: KUnit context for managed allocation. + * @lens: Array of lengths used in the benchmark cases. + * @count: Number of elements in the @lens array. + * @buf_len: [out] Pointer to store the actually allocated buffer + * size (including NUL character). + * + * Return: Pointer to the allocated memory, or NULL on failure. + */ +static void *alloc_max_bench_buffer(struct kunit *test, const size_t *lens, + size_t count, size_t *buf_len) +{ + size_t max_len = 0; + void *buf; + + for (size_t i = 0; i < count; i++) + max_len = max(lens[i], max_len); + + /* Add space for NUL character */ + max_len += 1; + + buf = kunit_kzalloc(test, max_len, GFP_KERNEL); + if (!buf) + return NULL; + + if (buf_len) + *buf_len = max_len; + + return buf; +} + +/** + * fill_random_string() - Populate a buffer with a random NUL-terminated string. + * @buf: Buffer to fill. + * @len: Length of the buffer in bytes. + * + * Fills the buffer with random non-NUL bytes and ensures the string is + * properly NUL-terminated. + */ +static void fill_random_string(char *buf, size_t len) +{ + struct rnd_state state; + + if (!buf || !len) + return; + + /* Use a fixed seed to ensure deterministic benchmark results */ + prandom_seed_state(&state, STRING_BENCH_SEED); + prandom_bytes_state(&state, buf, len); + + /* Replace NUL characters to avoid early string termination */ + for (size_t i = 0; i < len; i++) { + if (buf[i] == '\0') + buf[i] = 0x01; + } + + buf[len - 1] = '\0'; +} + +/** + * STRING_BENCH() - Benchmark string functions. + * @iters: Number of iterations to run. + * @func: Function to benchmark. + * @...: Variable arguments passed to @func. + * + * Disables preemption and measures the total time in nanoseconds to execute + * @func(@__VA_ARGS__) for @iters times, including a small warm-up phase. + * + * Context: Disables preemption during measurement. + * Return: Total execution time in nanoseconds (u64). + */ +#define STRING_BENCH(iters, func, ...) \ +({ \ + /* Volatile function pointer prevents dead code elimination */ \ + typeof(func) (* volatile __func) = (func); \ + size_t __bn_iters = (iters); \ + size_t __bn_warm_iters; \ + u64 __bn_t; \ + \ + /* Use 10% of the given iterations (maximum 50) to warm up */ \ + __bn_warm_iters = max(__bn_iters / 10, 50U); \ + \ + for (size_t __bn_i = 0; __bn_i < __bn_warm_iters; __bn_i++) \ + (void)__func(__VA_ARGS__); \ + \ + preempt_disable(); \ + __bn_t = ktime_get_ns(); \ + for (size_t __bn_i = 0; __bn_i < __bn_iters; __bn_i++) \ + (void)__func(__VA_ARGS__); \ + __bn_t = ktime_get_ns() - __bn_t; \ + preempt_enable(); \ + __bn_t; \ +}) + +/** + * STRING_BENCH_BUF() - Benchmark harness for single-buffer functions. + * @test: KUnit context. + * @buf_name: Local char * variable name to be defined. + * @buf_size: Local size_t variable name to be defined. + * @func: Function to benchmark. + * @...: Extra arguments for @func. + * + * Prepares a randomized, NUL-terminated buffer and iterates through lengths + * in bench_lens, defining @buf_name and @buf_size in each loop. + */ +#define STRING_BENCH_BUF(test, buf_name, buf_size, func, ...) \ +do { \ + size_t _bn_i, _bn_iters, _bn_size = 0; \ + u64 _bn_t, _bn_mbps = 0, _bn_lat = 0; \ + char *_bn_buf; \ + \ + _bn_buf = alloc_max_bench_buffer(test, bench_lens, \ + ARRAY_SIZE(bench_lens), &_bn_size); \ + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, _bn_buf); \ + \ + fill_random_string(_bn_buf, _bn_size); \ + \ + for (_bn_i = 0; _bn_i < ARRAY_SIZE(bench_lens); _bn_i++) { \ + size_t buf_size = bench_lens[_bn_i]; \ + char *buf_name = _bn_buf + _bn_size - buf_size - 1; \ + _bn_iters = STRING_BENCH_WORKLOAD / max(buf_size, 1U); \ + \ + _bn_t = STRING_BENCH(_bn_iters, func, ##__VA_ARGS__); \ + if (_bn_t > 0) { \ + _bn_mbps = (u64)(buf_size) * _bn_iters * \ + (NSEC_PER_SEC / MEGA); \ + _bn_mbps = div64_u64(_bn_mbps, _bn_t); \ + _bn_lat = div64_u64(_bn_t, _bn_iters); \ + } \ + kunit_info(test, "len=%zu: %llu MB/s (%llu ns/call)\n", \ + buf_size, _bn_mbps, _bn_lat); \ + } \ +} while (0) +#else +#define STRING_BENCH_BUF(test, buf_name, buf_size, func, ...) \ + kunit_skip(test, "not enabled") +#endif /* IS_ENABLED(CONFIG_STRING_KUNIT_BENCH) */ + +static void string_bench_strlen(struct kunit *test) +{ + STRING_BENCH_BUF(test, buf, len, strlen, buf); +} + +static void string_bench_strnlen(struct kunit *test) +{ + STRING_BENCH_BUF(test, buf, len, strnlen, buf, len); +} + +static void string_bench_strchr(struct kunit *test) +{ + STRING_BENCH_BUF(test, buf, len, strchr, buf, '\0'); +} + +static void string_bench_strrchr(struct kunit *test) +{ + STRING_BENCH_BUF(test, buf, len, strrchr, buf, '\0'); +} + static struct kunit_case string_test_cases[] = { KUNIT_CASE(string_test_memset16), KUNIT_CASE(string_test_memset32), KUNIT_CASE(string_test_memset64), + KUNIT_CASE(string_test_strlen), + KUNIT_CASE(string_test_strnlen), KUNIT_CASE(string_test_strchr), KUNIT_CASE(string_test_strnchr), + KUNIT_CASE(string_test_strrchr), KUNIT_CASE(string_test_strspn), KUNIT_CASE(string_test_strcmp), KUNIT_CASE(string_test_strcmp_long_strings), @@ -636,6 +906,10 @@ static struct kunit_case string_test_cases[] = { KUNIT_CASE(string_test_strtomem), KUNIT_CASE(string_test_memtostr), KUNIT_CASE(string_test_strends), + KUNIT_CASE(string_bench_strlen), + KUNIT_CASE(string_bench_strnlen), + KUNIT_CASE(string_bench_strchr), + KUNIT_CASE(string_bench_strrchr), {} }; diff --git a/lib/tests/test_kprobes.c b/lib/tests/test_kprobes.c index b7582010125c..06e729e4de05 100644 --- a/lib/tests/test_kprobes.c +++ b/lib/tests/test_kprobes.c @@ -12,6 +12,12 @@ #define div_factor 3 +#define KP_CLEAR(_kp) \ +do { \ + (_kp).addr = NULL; \ + (_kp).flags = 0; \ +} while (0) + static u32 rand1, preh_val, posth_val; static u32 (*target)(u32 value); static u32 (*recursed_target)(u32 value); @@ -125,10 +131,6 @@ static void test_kprobes(struct kunit *test) current_test = test; - /* addr and flags should be cleard for reusing kprobe. */ - kp.addr = NULL; - kp.flags = 0; - KUNIT_EXPECT_EQ(test, 0, register_kprobes(kps, 2)); preh_val = 0; posth_val = 0; @@ -226,9 +228,6 @@ static void test_kretprobes(struct kunit *test) struct kretprobe *rps[2] = {&rp, &rp2}; current_test = test; - /* addr and flags should be cleard for reusing kprobe. */ - rp.kp.addr = NULL; - rp.kp.flags = 0; KUNIT_EXPECT_EQ(test, 0, register_kretprobes(rps, 2)); krph_val = 0; @@ -290,8 +289,6 @@ static void test_stacktrace_on_kretprobe(struct kunit *test) unsigned long myretaddr = (unsigned long)__builtin_return_address(0); current_test = test; - rp3.kp.addr = NULL; - rp3.kp.flags = 0; /* * Run the stacktrace_driver() to record correct return address in @@ -352,8 +349,6 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test) struct kretprobe *rps[2] = {&rp3, &rp4}; current_test = test; - rp3.kp.addr = NULL; - rp3.kp.flags = 0; //KUNIT_ASSERT_NE(test, myretaddr, stacktrace_driver()); @@ -367,6 +362,18 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test) static int kprobes_test_init(struct kunit *test) { + KP_CLEAR(kp); + KP_CLEAR(kp2); + KP_CLEAR(kp_missed); +#ifdef CONFIG_KRETPROBES + KP_CLEAR(rp.kp); + KP_CLEAR(rp2.kp); +#ifdef CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE + KP_CLEAR(rp3.kp); + KP_CLEAR(rp4.kp); +#endif +#endif + target = kprobe_target; target2 = kprobe_target2; recursed_target = kprobe_recursed_target; diff --git a/lib/ts_bm.c b/lib/ts_bm.c index eed5967238c5..676105e84005 100644 --- a/lib/ts_bm.c +++ b/lib/ts_bm.c @@ -163,8 +163,22 @@ static struct ts_config *bm_init(const void *pattern, unsigned int len, struct ts_config *conf; struct ts_bm *bm; int i; - unsigned int prefix_tbl_len = len * sizeof(unsigned int); - size_t priv_size = sizeof(*bm) + len + prefix_tbl_len; + unsigned int prefix_tbl_len; + size_t priv_size; + + /* Zero-length patterns would underflow bm_find()'s initial shift. */ + if (unlikely(!len)) + return ERR_PTR(-EINVAL); + + /* + * bm->pattern is stored immediately after the good_shift[] table. + * Reject lengths that would wrap while sizing either region. + */ + if (unlikely(check_mul_overflow(len, sizeof(*bm->good_shift), + &prefix_tbl_len) || + check_add_overflow(sizeof(*bm), (size_t)len, &priv_size) || + check_add_overflow(priv_size, prefix_tbl_len, &priv_size))) + return ERR_PTR(-EINVAL); conf = alloc_ts_config(priv_size, gfp_mask); if (IS_ERR(conf)) diff --git a/lib/ts_kmp.c b/lib/ts_kmp.c index 5520dc28255a..29466c1803c9 100644 --- a/lib/ts_kmp.c +++ b/lib/ts_kmp.c @@ -94,8 +94,22 @@ static struct ts_config *kmp_init(const void *pattern, unsigned int len, struct ts_config *conf; struct ts_kmp *kmp; int i; - unsigned int prefix_tbl_len = len * sizeof(unsigned int); - size_t priv_size = sizeof(*kmp) + len + prefix_tbl_len; + unsigned int prefix_tbl_len; + size_t priv_size; + + /* Zero-length patterns would make kmp_find() read beyond kmp->pattern. */ + if (unlikely(!len)) + return ERR_PTR(-EINVAL); + + /* + * kmp->pattern is stored immediately after the prefix_tbl[] table. + * Reject lengths that would wrap while sizing either region. + */ + if (unlikely(check_mul_overflow(len, sizeof(*kmp->prefix_tbl), + &prefix_tbl_len) || + check_add_overflow(sizeof(*kmp), (size_t)len, &priv_size) || + check_add_overflow(priv_size, prefix_tbl_len, &priv_size))) + return ERR_PTR(-EINVAL); conf = alloc_ts_config(priv_size, gfp_mask); if (IS_ERR(conf)) diff --git a/lib/uuid.c b/lib/uuid.c index e8543c668dc7..128a51f1879b 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -54,7 +54,7 @@ EXPORT_SYMBOL(generate_random_guid); static void __uuid_gen_common(__u8 b[16]) { get_random_bytes(b, 16); - /* reversion 0b10 */ + /* revision 0b10 */ b[8] = (b[8] & 0x3F) | 0x80; } diff --git a/lib/vdso/gettimeofday.c b/lib/vdso/gettimeofday.c index a5798bd26d20..da224011fafd 100644 --- a/lib/vdso/gettimeofday.c +++ b/lib/vdso/gettimeofday.c @@ -248,11 +248,10 @@ bool do_aux(const struct vdso_time_data *vd, clockid_t clock, struct __kernel_ti vc = &vd->aux_clock_data[idx]; do { - if (vdso_read_begin_timens(vc, &seq)) { + while (vdso_read_begin_timens(vc, &seq)) { + /* Re-read from the real time data page, reload seq by looping */ vd = __arch_get_vdso_u_timens_data(vd); vc = &vd->aux_clock_data[idx]; - /* Re-read from the real time data page */ - continue; } /* Auxclock disabled? */ diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 800b8ac49f53..9f359b31c8d1 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1107,7 +1107,7 @@ char *resource_string(char *buf, char *end, struct resource *res, 2*RSRC_BUF_SIZE + FLAG_BUF_SIZE + RAW_BUF_SIZE)]; char *p = sym, *pend = sym + sizeof(sym); - int decode = (fmt[0] == 'R') ? 1 : 0; + bool decode = fmt[0] == 'R'; const struct printf_spec *specp; if (check_pointer(&buf, end, res, spec)) @@ -1132,7 +1132,7 @@ char *resource_string(char *buf, char *end, struct resource *res, } else { p = string_nocheck(p, pend, "??? ", str_spec); specp = &mem_spec; - decode = 0; + decode = false; } if (decode && res->flags & IORESOURCE_UNSET) { p = string_nocheck(p, pend, "size ", str_spec); diff --git a/mm/Kconfig b/mm/Kconfig index 0a43bb80df4f..e8bf1e9e6ad9 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -570,6 +570,7 @@ config SPLIT_PTE_PTLOCKS depends on !ARM || CPU_CACHE_VIPT depends on !PARISC || PA20 depends on !SPARC32 + depends on !UML config ARCH_ENABLE_SPLIT_PMD_PTLOCK bool diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug index 7638d75b27db..91b3e027b753 100644 --- a/mm/Kconfig.debug +++ b/mm/Kconfig.debug @@ -297,6 +297,17 @@ config DEBUG_KMEMLEAK_AUTO_SCAN If unsure, say Y. +config DEBUG_KMEMLEAK_VERBOSE + bool "Default kmemleak to verbose mode" + depends on DEBUG_KMEMLEAK_AUTO_SCAN + help + Say Y here to have kmemleak print unreferenced object details + (backtrace, hex dump, address) to dmesg when new memory leaks are + detected during automatic scanning. This can also be toggled at + runtime via /sys/module/kmemleak/parameters/verbose. + + If unsure, say N. + config PER_VMA_LOCK_STATS bool "Statistics for per-vma locks" depends on PER_VMA_LOCK diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 7a18fa6c7272..cecbcf9060a6 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -618,12 +618,13 @@ static void cgwb_release_workfn(struct work_struct *work) wb_shutdown(wb); css_put(wb->memcg_css); - css_put(wb->blkcg_css); - mutex_unlock(&wb->bdi->cgwb_release_mutex); /* triggers blkg destruction if no online users left */ blkcg_unpin_online(wb->blkcg_css); + css_put(wb->blkcg_css); + mutex_unlock(&wb->bdi->cgwb_release_mutex); + fprop_local_destroy_percpu(&wb->memcg_completions); spin_lock_irq(&cgwb_lock); diff --git a/mm/cma.c b/mm/cma.c index 15cc0ae76c8e..c7ca567f4c5c 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -52,6 +52,7 @@ const char *cma_get_name(const struct cma *cma) { return cma->name; } +EXPORT_SYMBOL_GPL(cma_get_name); static unsigned long cma_bitmap_aligned_mask(const struct cma *cma, unsigned int align_order) @@ -951,6 +952,7 @@ struct page *cma_alloc(struct cma *cma, unsigned long count, return page; } +EXPORT_SYMBOL_GPL(cma_alloc); static struct cma_memrange *find_cma_memrange(struct cma *cma, const struct page *pages, unsigned long count) @@ -1030,6 +1032,7 @@ bool cma_release(struct cma *cma, const struct page *pages, return true; } +EXPORT_SYMBOL_GPL(cma_release); bool cma_release_frozen(struct cma *cma, const struct page *pages, unsigned long count) diff --git a/mm/compaction.c b/mm/compaction.c index 1e8f8eca318c..3648ce22c807 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -518,6 +518,24 @@ static bool compact_lock_irqsave(spinlock_t *lock, unsigned long *flags, return true; } +static struct lruvec * +compact_folio_lruvec_lock_irqsave(struct folio *folio, unsigned long *flags, + struct compact_control *cc) +{ + struct lruvec *lruvec; + + rcu_read_lock(); +retry: + lruvec = folio_lruvec(folio); + compact_lock_irqsave(&lruvec->lru_lock, flags, cc); + if (unlikely(lruvec_memcg(lruvec) != folio_memcg(folio))) { + spin_unlock_irqrestore(&lruvec->lru_lock, *flags); + goto retry; + } + + return lruvec; +} + /* * Compaction requires the taking of some coarse locks that are potentially * very heavily contended. The lock should be periodically unlocked to avoid @@ -839,7 +857,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, { pg_data_t *pgdat = cc->zone->zone_pgdat; unsigned long nr_scanned = 0, nr_isolated = 0; - struct lruvec *lruvec; + struct lruvec *lruvec = NULL; unsigned long flags = 0; struct lruvec *locked = NULL; struct folio *folio = NULL; @@ -913,7 +931,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, */ if (!(low_pfn % COMPACT_CLUSTER_MAX)) { if (locked) { - unlock_page_lruvec_irqrestore(locked, flags); + lruvec_unlock_irqrestore(locked, flags); locked = NULL; } @@ -964,7 +982,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, } /* for alloc_contig case */ if (locked) { - unlock_page_lruvec_irqrestore(locked, flags); + lruvec_unlock_irqrestore(locked, flags); locked = NULL; } @@ -1053,7 +1071,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, if (unlikely(page_has_movable_ops(page)) && !PageMovableOpsIsolated(page)) { if (locked) { - unlock_page_lruvec_irqrestore(locked, flags); + lruvec_unlock_irqrestore(locked, flags); locked = NULL; } @@ -1153,18 +1171,17 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, if (!folio_test_clear_lru(folio)) goto isolate_fail_put; - lruvec = folio_lruvec(folio); + if (locked) + lruvec = folio_lruvec(folio); /* If we already hold the lock, we can skip some rechecking */ - if (lruvec != locked) { + if (lruvec != locked || !locked) { if (locked) - unlock_page_lruvec_irqrestore(locked, flags); + lruvec_unlock_irqrestore(locked, flags); - compact_lock_irqsave(&lruvec->lru_lock, &flags, cc); + lruvec = compact_folio_lruvec_lock_irqsave(folio, &flags, cc); locked = lruvec; - lruvec_memcg_debug(lruvec, folio); - /* * Try get exclusive access under lock. If marked for * skip, the scan is aborted unless the current context @@ -1226,7 +1243,7 @@ isolate_success_no_list: isolate_fail_put: /* Avoid potential deadlock in freeing page under lru_lock */ if (locked) { - unlock_page_lruvec_irqrestore(locked, flags); + lruvec_unlock_irqrestore(locked, flags); locked = NULL; } folio_put(folio); @@ -1242,7 +1259,7 @@ isolate_fail: */ if (nr_isolated) { if (locked) { - unlock_page_lruvec_irqrestore(locked, flags); + lruvec_unlock_irqrestore(locked, flags); locked = NULL; } putback_movable_pages(&cc->migratepages); @@ -1274,7 +1291,7 @@ isolate_fail: isolate_abort: if (locked) - unlock_page_lruvec_irqrestore(locked, flags); + lruvec_unlock_irqrestore(locked, flags); if (folio) { folio_set_lru(folio); folio_put(folio); diff --git a/mm/damon/core.c b/mm/damon/core.c index db6c67e52d2b..3dbbbfdeff71 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -1477,6 +1477,11 @@ int damon_start(struct damon_ctx **ctxs, int nr_ctxs, bool exclusive) int i; int err = 0; + for (i = 0; i < nr_ctxs; i++) { + if (!is_power_of_2(ctxs[i]->min_region_sz)) + return -EINVAL; + } + mutex_lock(&damon_lock); if ((exclusive && nr_running_ctxs) || (!exclusive && running_exclusive_ctxs)) { @@ -1573,35 +1578,6 @@ int damon_kdamond_pid(struct damon_ctx *ctx) return pid; } -/* - * damon_call_handle_inactive_ctx() - handle DAMON call request that added to - * an inactive context. - * @ctx: The inactive DAMON context. - * @control: Control variable of the call request. - * - * This function is called in a case that @control is added to @ctx but @ctx is - * not running (inactive). See if @ctx handled @control or not, and cleanup - * @control if it was not handled. - * - * Returns 0 if @control was handled by @ctx, negative error code otherwise. - */ -static int damon_call_handle_inactive_ctx( - struct damon_ctx *ctx, struct damon_call_control *control) -{ - struct damon_call_control *c; - - mutex_lock(&ctx->call_controls_lock); - list_for_each_entry(c, &ctx->call_controls, list) { - if (c == control) { - list_del(&control->list); - mutex_unlock(&ctx->call_controls_lock); - return -EINVAL; - } - } - mutex_unlock(&ctx->call_controls_lock); - return 0; -} - /** * damon_call() - Invoke a given function on DAMON worker thread (kdamond). * @ctx: DAMON context to call the function for. @@ -1619,6 +1595,10 @@ static int damon_call_handle_inactive_ctx( * synchronization. The return value of the function will be saved in * &damon_call_control->return_code. * + * Note that this function should be called only after damon_start() with the + * @ctx has succeeded. Otherwise, this function could fall into an indefinite + * wait. + * * Return: 0 on success, negative error code otherwise. */ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control) @@ -1629,10 +1609,12 @@ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control) INIT_LIST_HEAD(&control->list); mutex_lock(&ctx->call_controls_lock); + if (ctx->call_controls_obsolete) { + mutex_unlock(&ctx->call_controls_lock); + return -ECANCELED; + } list_add_tail(&control->list, &ctx->call_controls); mutex_unlock(&ctx->call_controls_lock); - if (!damon_is_running(ctx)) - return damon_call_handle_inactive_ctx(ctx, control); if (control->repeat) return 0; wait_for_completion(&control->completion); @@ -1660,6 +1642,10 @@ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control) * passed at least one &damos->apply_interval_us, kdamond marks the request as * completed so that damos_walk() can wakeup and return. * + * Note that this function should be called only after damon_start() with the + * @ctx has succeeded. Otherwise, this function could fall into an indefinite + * wait. + * * Return: 0 on success, negative error code otherwise. */ int damos_walk(struct damon_ctx *ctx, struct damos_walk_control *control) @@ -1667,19 +1653,16 @@ int damos_walk(struct damon_ctx *ctx, struct damos_walk_control *control) init_completion(&control->completion); control->canceled = false; mutex_lock(&ctx->walk_control_lock); + if (ctx->walk_control_obsolete) { + mutex_unlock(&ctx->walk_control_lock); + return -ECANCELED; + } if (ctx->walk_control) { mutex_unlock(&ctx->walk_control_lock); return -EBUSY; } ctx->walk_control = control; mutex_unlock(&ctx->walk_control_lock); - if (!damon_is_running(ctx)) { - mutex_lock(&ctx->walk_control_lock); - if (ctx->walk_control == control) - ctx->walk_control = NULL; - mutex_unlock(&ctx->walk_control_lock); - return -EINVAL; - } wait_for_completion(&control->completion); if (control->canceled) return -ECANCELED; @@ -2239,12 +2222,24 @@ static inline u64 damos_get_some_mem_psi_total(void) #endif /* CONFIG_PSI */ #ifdef CONFIG_NUMA +static bool invalid_mem_node(int nid) +{ + return nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY); +} + static __kernel_ulong_t damos_get_node_mem_bp( struct damos_quota_goal *goal) { struct sysinfo i; __kernel_ulong_t numerator; + if (invalid_mem_node(goal->nid)) { + if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) + return 0; + else /* DAMOS_QUOTA_NODE_MEM_FREE_BP */ + return 10000; + } + si_meminfo_node(&i, goal->nid); if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP) numerator = i.totalram - i.freeram; @@ -2261,6 +2256,13 @@ static unsigned long damos_get_node_memcg_used_bp( unsigned long used_pages, numerator; struct sysinfo i; + if (invalid_mem_node(goal->nid)) { + if (goal->metric == DAMOS_QUOTA_NODE_MEMCG_USED_BP) + return 0; + else /* DAMOS_QUOTA_NODE_MEMCG_FREE_BP */ + return 10000; + } + memcg = mem_cgroup_get_from_id(goal->memcg_id); if (!memcg) { if (goal->metric == DAMOS_QUOTA_NODE_MEMCG_USED_BP) @@ -2387,7 +2389,8 @@ static void damos_goal_tune_esz_bp_temporal(struct damos_quota *quota) /* * Called only if quota->ms, or quota->sz are set, or quota->goals is not empty */ -static void damos_set_effective_quota(struct damos_quota *quota) +static void damos_set_effective_quota(struct damos_quota *quota, + struct damon_ctx *ctx) { unsigned long throughput; unsigned long esz = ULONG_MAX; @@ -2412,6 +2415,7 @@ static void damos_set_effective_quota(struct damos_quota *quota) else throughput = PAGE_SIZE * 1024; esz = min(throughput * quota->ms, esz); + esz = max(ctx->min_region_sz, esz); } if (quota->sz && quota->sz < esz) @@ -2448,11 +2452,12 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s) /* First charge window */ if (!quota->total_charged_sz && !quota->charged_from) { quota->charged_from = jiffies; - damos_set_effective_quota(quota); + damos_set_effective_quota(quota, c); } /* New charge window starts */ - if (time_after_eq(jiffies, quota->charged_from + + if (!time_in_range_open(jiffies, quota->charged_from, + quota->charged_from + msecs_to_jiffies(quota->reset_interval))) { if (damos_quota_is_set(quota) && quota->charged_sz >= quota->esz) @@ -2462,7 +2467,7 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s) quota->charged_sz = 0; if (trace_damos_esz_enabled()) cached_esz = quota->esz; - damos_set_effective_quota(quota); + damos_set_effective_quota(quota, c); if (trace_damos_esz_enabled() && quota->esz != cached_esz) damos_trace_esz(c, s, quota); } @@ -2510,7 +2515,7 @@ static void damos_trace_stat(struct damon_ctx *c, struct damos *s) break; sidx++; } - trace_damos_stat_after_apply_interval(cidx, sidx, &s->stat); + trace_call__damos_stat_after_apply_interval(cidx, sidx, &s->stat); } static void kdamond_apply_schemes(struct damon_ctx *c) @@ -2952,6 +2957,12 @@ static int kdamond_fn(void *data) pr_debug("kdamond (%d) starts\n", current->pid); + mutex_lock(&ctx->call_controls_lock); + ctx->call_controls_obsolete = false; + mutex_unlock(&ctx->call_controls_lock); + mutex_lock(&ctx->walk_control_lock); + ctx->walk_control_obsolete = false; + mutex_unlock(&ctx->walk_control_lock); complete(&ctx->kdamond_started); kdamond_init_ctx(ctx); @@ -3062,7 +3073,13 @@ done: damon_destroy_targets(ctx); kfree(ctx->regions_score_histogram); + mutex_lock(&ctx->call_controls_lock); + ctx->call_controls_obsolete = true; + mutex_unlock(&ctx->call_controls_lock); kdamond_call(ctx, true); + mutex_lock(&ctx->walk_control_lock); + ctx->walk_control_obsolete = true; + mutex_unlock(&ctx->walk_control_lock); damos_walk_cancel(ctx); pr_debug("kdamond (%d) finishes\n", current->pid); diff --git a/mm/damon/lru_sort.c b/mm/damon/lru_sort.c index 554559d72976..8494040b1ee4 100644 --- a/mm/damon/lru_sort.c +++ b/mm/damon/lru_sort.c @@ -161,15 +161,6 @@ module_param(monitor_region_end, ulong, 0600); */ static unsigned long addr_unit __read_mostly = 1; -/* - * PID of the DAMON thread - * - * If DAMON_LRU_SORT is enabled, this becomes the PID of the worker thread. - * Else, -1. - */ -static int kdamond_pid __read_mostly = -1; -module_param(kdamond_pid, int, 0400); - static struct damos_stat damon_lru_sort_hot_stat; DEFINE_DAMON_MODULES_DAMOS_STATS_PARAMS(damon_lru_sort_hot_stat, lru_sort_tried_hot_regions, lru_sorted_hot_regions, @@ -386,12 +377,8 @@ static int damon_lru_sort_turn(bool on) { int err; - if (!on) { - err = damon_stop(&ctx, 1); - if (!err) - kdamond_pid = -1; - return err; - } + if (!on) + return damon_stop(&ctx, 1); err = damon_lru_sort_apply_parameters(); if (err) @@ -400,9 +387,6 @@ static int damon_lru_sort_turn(bool on) err = damon_start(&ctx, 1, true); if (err) return err; - kdamond_pid = damon_kdamond_pid(ctx); - if (kdamond_pid < 0) - return kdamond_pid; return damon_call(ctx, &call_control); } @@ -430,42 +414,83 @@ module_param_cb(addr_unit, &addr_unit_param_ops, &addr_unit, 0600); MODULE_PARM_DESC(addr_unit, "Scale factor for DAMON_LRU_SORT to ops address conversion (default: 1)"); +static bool damon_lru_sort_enabled(void) +{ + if (!ctx) + return false; + return damon_is_running(ctx); +} + static int damon_lru_sort_enabled_store(const char *val, const struct kernel_param *kp) { - bool is_enabled = enabled; - bool enable; int err; - err = kstrtobool(val, &enable); + err = kstrtobool(val, &enabled); if (err) return err; - if (is_enabled == enable) + if (damon_lru_sort_enabled() == enabled) return 0; /* Called before init function. The function will handle this. */ if (!damon_initialized()) - goto set_param_out; + return 0; - err = damon_lru_sort_turn(enable); - if (err) - return err; + return damon_lru_sort_turn(enabled); +} -set_param_out: - enabled = enable; - return err; +static int damon_lru_sort_enabled_load(char *buffer, + const struct kernel_param *kp) +{ + return sprintf(buffer, "%c\n", damon_lru_sort_enabled() ? 'Y' : 'N'); } static const struct kernel_param_ops enabled_param_ops = { .set = damon_lru_sort_enabled_store, - .get = param_get_bool, + .get = damon_lru_sort_enabled_load, }; module_param_cb(enabled, &enabled_param_ops, &enabled, 0600); MODULE_PARM_DESC(enabled, "Enable or disable DAMON_LRU_SORT (default: disabled)"); +static int damon_lru_sort_kdamond_pid_store(const char *val, + const struct kernel_param *kp) +{ + /* + * kdamond_pid is read-only, but kernel command line could write it. + * Do nothing here. + */ + return 0; +} + +static int damon_lru_sort_kdamond_pid_load(char *buffer, + const struct kernel_param *kp) +{ + int kdamond_pid = -1; + + if (ctx) { + kdamond_pid = damon_kdamond_pid(ctx); + if (kdamond_pid < 0) + kdamond_pid = -1; + } + return sprintf(buffer, "%d\n", kdamond_pid); +} + +static const struct kernel_param_ops kdamond_pid_param_ops = { + .set = damon_lru_sort_kdamond_pid_store, + .get = damon_lru_sort_kdamond_pid_load, +}; + +/* + * PID of the DAMON thread + * + * If DAMON_LRU_SORT is enabled, this becomes the PID of the worker thread. + * Else, -1. + */ +module_param_cb(kdamond_pid, &kdamond_pid_param_ops, NULL, 0400); + static int __init damon_lru_sort_init(void) { int err; diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c index 86da14778658..fe7fce26cf6c 100644 --- a/mm/damon/reclaim.c +++ b/mm/damon/reclaim.c @@ -144,15 +144,6 @@ static unsigned long addr_unit __read_mostly = 1; static bool skip_anon __read_mostly; module_param(skip_anon, bool, 0600); -/* - * PID of the DAMON thread - * - * If DAMON_RECLAIM is enabled, this becomes the PID of the worker thread. - * Else, -1. - */ -static int kdamond_pid __read_mostly = -1; -module_param(kdamond_pid, int, 0400); - static struct damos_stat damon_reclaim_stat; DEFINE_DAMON_MODULES_DAMOS_STATS_PARAMS(damon_reclaim_stat, reclaim_tried_regions, reclaimed_regions, quota_exceeds); @@ -288,12 +279,8 @@ static int damon_reclaim_turn(bool on) { int err; - if (!on) { - err = damon_stop(&ctx, 1); - if (!err) - kdamond_pid = -1; - return err; - } + if (!on) + return damon_stop(&ctx, 1); err = damon_reclaim_apply_parameters(); if (err) @@ -302,9 +289,6 @@ static int damon_reclaim_turn(bool on) err = damon_start(&ctx, 1, true); if (err) return err; - kdamond_pid = damon_kdamond_pid(ctx); - if (kdamond_pid < 0) - return kdamond_pid; return damon_call(ctx, &call_control); } @@ -332,42 +316,83 @@ module_param_cb(addr_unit, &addr_unit_param_ops, &addr_unit, 0600); MODULE_PARM_DESC(addr_unit, "Scale factor for DAMON_RECLAIM to ops address conversion (default: 1)"); +static bool damon_reclaim_enabled(void) +{ + if (!ctx) + return false; + return damon_is_running(ctx); +} + static int damon_reclaim_enabled_store(const char *val, const struct kernel_param *kp) { - bool is_enabled = enabled; - bool enable; int err; - err = kstrtobool(val, &enable); + err = kstrtobool(val, &enabled); if (err) return err; - if (is_enabled == enable) + if (damon_reclaim_enabled() == enabled) return 0; /* Called before init function. The function will handle this. */ if (!damon_initialized()) - goto set_param_out; + return 0; - err = damon_reclaim_turn(enable); - if (err) - return err; + return damon_reclaim_turn(enabled); +} -set_param_out: - enabled = enable; - return err; +static int damon_reclaim_enabled_load(char *buffer, + const struct kernel_param *kp) +{ + return sprintf(buffer, "%c\n", damon_reclaim_enabled() ? 'Y' : 'N'); } static const struct kernel_param_ops enabled_param_ops = { .set = damon_reclaim_enabled_store, - .get = param_get_bool, + .get = damon_reclaim_enabled_load, }; module_param_cb(enabled, &enabled_param_ops, &enabled, 0600); MODULE_PARM_DESC(enabled, "Enable or disable DAMON_RECLAIM (default: disabled)"); +static int damon_reclaim_kdamond_pid_store(const char *val, + const struct kernel_param *kp) +{ + /* + * kdamond_pid is read-only, but kernel command line could write it. + * Do nothing here. + */ + return 0; +} + +static int damon_reclaim_kdamond_pid_load(char *buffer, + const struct kernel_param *kp) +{ + int kdamond_pid = -1; + + if (ctx) { + kdamond_pid = damon_kdamond_pid(ctx); + if (kdamond_pid < 0) + kdamond_pid = -1; + } + return sprintf(buffer, "%d\n", kdamond_pid); +} + +static const struct kernel_param_ops kdamond_pid_param_ops = { + .set = damon_reclaim_kdamond_pid_store, + .get = damon_reclaim_kdamond_pid_load, +}; + +/* + * PID of the DAMON thread + * + * If DAMON_RECLAIM is enabled, this becomes the PID of the worker thread. + * Else, -1. + */ +module_param_cb(kdamond_pid, &kdamond_pid_param_ops, NULL, 0400); + static int __init damon_reclaim_init(void) { int err; diff --git a/mm/damon/stat.c b/mm/damon/stat.c index 60351a719460..3951b762cbdd 100644 --- a/mm/damon/stat.c +++ b/mm/damon/stat.c @@ -19,14 +19,17 @@ static int damon_stat_enabled_store( const char *val, const struct kernel_param *kp); +static int damon_stat_enabled_load(char *buffer, + const struct kernel_param *kp); + static const struct kernel_param_ops enabled_param_ops = { .set = damon_stat_enabled_store, - .get = param_get_bool, + .get = damon_stat_enabled_load, }; static bool enabled __read_mostly = IS_ENABLED( CONFIG_DAMON_STAT_ENABLED_DEFAULT); -module_param_cb(enabled, &enabled_param_ops, &enabled, 0600); +module_param_cb(enabled, &enabled_param_ops, NULL, 0600); MODULE_PARM_DESC(enabled, "Enable of disable DAMON_STAT"); static unsigned long estimated_memory_bandwidth __read_mostly; @@ -255,8 +258,11 @@ static int damon_stat_start(void) if (!damon_stat_context) return -ENOMEM; err = damon_start(&damon_stat_context, 1, true); - if (err) + if (err) { + damon_destroy_ctx(damon_stat_context); + damon_stat_context = NULL; return err; + } damon_stat_last_refresh_jiffies = jiffies; call_control.data = damon_stat_context; @@ -270,17 +276,23 @@ static void damon_stat_stop(void) damon_stat_context = NULL; } +static bool damon_stat_enabled(void) +{ + if (!damon_stat_context) + return false; + return damon_is_running(damon_stat_context); +} + static int damon_stat_enabled_store( const char *val, const struct kernel_param *kp) { - bool is_enabled = enabled; int err; err = kstrtobool(val, &enabled); if (err) return err; - if (is_enabled == enabled) + if (damon_stat_enabled() == enabled) return 0; if (!damon_initialized()) @@ -290,16 +302,17 @@ static int damon_stat_enabled_store( */ return 0; - if (enabled) { - err = damon_stat_start(); - if (err) - enabled = false; - return err; - } + if (enabled) + return damon_stat_start(); damon_stat_stop(); return 0; } +static int damon_stat_enabled_load(char *buffer, const struct kernel_param *kp) +{ + return sprintf(buffer, "%c\n", damon_stat_enabled() ? 'Y' : 'N'); +} + static int __init damon_stat_init(void) { int err = 0; diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 5186966dafb3..245d63808411 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -533,9 +533,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -550,8 +555,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_path = path; + mutex_unlock(&damon_sysfs_lock); return count; } @@ -1187,8 +1197,13 @@ static ssize_t path_show(struct kobject *kobj, { struct damos_sysfs_quota_goal *goal = container_of(kobj, struct damos_sysfs_quota_goal, kobj); + int len; - return sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t path_store(struct kobject *kobj, @@ -1203,8 +1218,13 @@ static ssize_t path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(goal->path); goal->path = path; + mutex_unlock(&damon_sysfs_lock); return count; } diff --git a/mm/filemap.c b/mm/filemap.c index c568d9058ff8..4e636647100c 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -228,7 +228,8 @@ void __filemap_remove_folio(struct folio *folio, void *shadow) page_cache_delete(mapping, folio, shadow); } -void filemap_free_folio(struct address_space *mapping, struct folio *folio) +static void filemap_free_folio(const struct address_space *mapping, + struct folio *folio) { void (*free_folio)(struct folio *); diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 42c983821c03..970e077019b7 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1218,13 +1218,29 @@ retry: static struct deferred_split *folio_split_queue_lock(struct folio *folio) { - return split_queue_lock(folio_nid(folio), folio_memcg(folio)); + struct deferred_split *queue; + + rcu_read_lock(); + queue = split_queue_lock(folio_nid(folio), folio_memcg(folio)); + /* + * The memcg destruction path is acquiring the split queue lock for + * reparenting. Once you have it locked, it's safe to drop the rcu lock. + */ + rcu_read_unlock(); + + return queue; } static struct deferred_split * folio_split_queue_lock_irqsave(struct folio *folio, unsigned long *flags) { - return split_queue_lock_irqsave(folio_nid(folio), folio_memcg(folio), flags); + struct deferred_split *queue; + + rcu_read_lock(); + queue = split_queue_lock_irqsave(folio_nid(folio), folio_memcg(folio), flags); + rcu_read_unlock(); + + return queue; } static inline void split_queue_unlock(struct deferred_split *queue) @@ -3994,7 +4010,7 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n folio_ref_unfreeze(folio, folio_cache_ref_count(folio) + 1); if (do_lru) - unlock_page_lruvec(lruvec); + lruvec_unlock(lruvec); if (ci) swap_cluster_unlock(ci); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 9413ed497be5..f24bf49be047 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -4218,6 +4218,9 @@ static __init int hugetlb_add_param(char *s, int (*setup)(char *)) size_t len; char *p; + if (!s) + return -EINVAL; + if (hugetlb_param_index >= HUGE_MAX_CMDLINE_ARGS) return -EINVAL; @@ -4784,6 +4787,18 @@ static vm_fault_t hugetlb_vm_op_fault(struct vm_fault *vmf) return 0; } +#ifdef CONFIG_USERFAULTFD +static bool hugetlb_can_userfault(struct vm_area_struct *vma, + vm_flags_t vm_flags) +{ + return true; +} + +static const struct vm_uffd_ops hugetlb_uffd_ops = { + .can_userfault = hugetlb_can_userfault, +}; +#endif + /* * When a new function is introduced to vm_operations_struct and added * to hugetlb_vm_ops, please consider adding the function to shm_vm_ops. @@ -4797,6 +4812,9 @@ const struct vm_operations_struct hugetlb_vm_ops = { .close = hugetlb_vm_op_close, .may_split = hugetlb_vm_op_split, .pagesize = hugetlb_vm_op_pagesize, +#ifdef CONFIG_USERFAULTFD + .uffd_ops = &hugetlb_uffd_ops, +#endif }; static pte_t make_huge_pte(struct vm_area_struct *vma, struct folio *folio, diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c index f83ae4998990..7693ccefd0c6 100644 --- a/mm/hugetlb_cma.c +++ b/mm/hugetlb_cma.c @@ -204,6 +204,7 @@ void __init hugetlb_cma_reserve(void) */ per_node = DIV_ROUND_UP(hugetlb_cma_size, nodes_weight(hugetlb_bootmem_nodes)); + per_node = round_up(per_node, PAGE_SIZE << order); pr_info("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n", hugetlb_cma_size / SZ_1M, per_node / SZ_1M); } diff --git a/mm/internal.h b/mm/internal.h index c693646e5b3f..5a2ddcf68e0b 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -557,7 +557,6 @@ unsigned find_lock_entries(struct address_space *mapping, pgoff_t *start, pgoff_t end, struct folio_batch *fbatch, pgoff_t *indices); unsigned find_get_entries(struct address_space *mapping, pgoff_t *start, pgoff_t end, struct folio_batch *fbatch, pgoff_t *indices); -void filemap_free_folio(struct address_space *mapping, struct folio *folio); int truncate_inode_folio(struct address_space *mapping, struct folio *folio); bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end); @@ -1322,7 +1321,17 @@ static inline void vunmap_range_noflush(unsigned long start, unsigned long end) #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT DECLARE_STATIC_KEY_TRUE(deferred_pages); +static inline bool deferred_pages_enabled(void) +{ + return static_branch_unlikely(&deferred_pages); +} + bool __init deferred_grow_zone(struct zone *zone, unsigned int order); +#else +static inline bool deferred_pages_enabled(void) +{ + return false; +} #endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */ void init_deferred_page(unsigned long pfn, int nid); diff --git a/mm/kfence/core.c b/mm/kfence/core.c index 9eba46212edf..655dc5ce3240 100644 --- a/mm/kfence/core.c +++ b/mm/kfence/core.c @@ -736,10 +736,10 @@ static bool __init kfence_init_pool_early(void) * fails for the first page, and therefore expect addr==__kfence_pool in * most failure cases. */ - memblock_free_late(__pa(addr), KFENCE_POOL_SIZE - (addr - (unsigned long)__kfence_pool)); + memblock_free((void *)addr, KFENCE_POOL_SIZE - (addr - (unsigned long)__kfence_pool)); __kfence_pool = NULL; - memblock_free_late(__pa(kfence_metadata_init), KFENCE_METADATA_SIZE); + memblock_free(kfence_metadata_init, KFENCE_METADATA_SIZE); kfence_metadata_init = NULL; return false; diff --git a/mm/kmemleak.c b/mm/kmemleak.c index fa8201e23222..2eff0d6b622b 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -241,7 +241,7 @@ static int kmemleak_skip_disable; /* If there are leaks that can be reported */ static bool kmemleak_found_leaks; -static bool kmemleak_verbose; +static bool kmemleak_verbose = IS_ENABLED(CONFIG_DEBUG_KMEMLEAK_VERBOSE); module_param_named(verbose, kmemleak_verbose, bool, 0600); static void kmemleak_disable(void); diff --git a/mm/memblock.c b/mm/memblock.c index 2505ce8b319c..a6a1c91e276d 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -17,6 +17,7 @@ #include #include #include +#include #ifdef CONFIG_KEXEC_HANDOVER #include @@ -384,26 +385,27 @@ static void __init_memblock memblock_remove_region(struct memblock_type *type, u */ void __init memblock_discard(void) { - phys_addr_t addr, size; + phys_addr_t size; + void *addr; if (memblock.reserved.regions != memblock_reserved_init_regions) { - addr = __pa(memblock.reserved.regions); + addr = memblock.reserved.regions; size = PAGE_ALIGN(sizeof(struct memblock_region) * memblock.reserved.max); if (memblock_reserved_in_slab) - kfree(memblock.reserved.regions); + kfree(addr); else - memblock_free_late(addr, size); + memblock_free(addr, size); } if (memblock.memory.regions != memblock_memory_init_regions) { - addr = __pa(memblock.memory.regions); + addr = memblock.memory.regions; size = PAGE_ALIGN(sizeof(struct memblock_region) * memblock.memory.max); if (memblock_memory_in_slab) - kfree(memblock.memory.regions); + kfree(addr); else - memblock_free_late(addr, size); + memblock_free(addr, size); } memblock_memory = NULL; @@ -893,13 +895,81 @@ int __init_memblock memblock_remove(phys_addr_t base, phys_addr_t size) return memblock_remove_range(&memblock.memory, base, size); } +static unsigned long __free_reserved_area(phys_addr_t start, phys_addr_t end, + int poison) +{ + unsigned long pages = 0, pfn; + + if (deferred_pages_enabled()) { + WARN(1, "Cannot free reserved memory because of deferred initialization of the memory map"); + return 0; + } + + for_each_valid_pfn(pfn, PFN_UP(start), PFN_DOWN(end)) { + struct page *page = pfn_to_page(pfn); + void *direct_map_addr; + + /* + * 'direct_map_addr' might be different from the kernel virtual + * address because some architectures use aliases. + * Going via physical address, pfn_to_page() and page_address() + * ensures that we get a _writeable_ alias for the memset(). + */ + direct_map_addr = page_address(page); + /* + * Perform a kasan-unchecked memset() since this memory + * has not been initialized. + */ + direct_map_addr = kasan_reset_tag(direct_map_addr); + if ((unsigned int)poison <= 0xFF) + memset(direct_map_addr, poison, PAGE_SIZE); + + free_reserved_page(page); + pages++; + } + return pages; +} + +unsigned long free_reserved_area(void *start, void *end, int poison, const char *s) +{ + phys_addr_t start_pa, end_pa; + unsigned long pages; + + /* + * end is the first address past the region and it may be beyond what + * __pa() or __pa_symbol() can handle. + * Use the address included in the range for the conversion and add back + * 1 afterwards. + */ + if (__is_kernel((unsigned long)start)) { + start_pa = __pa_symbol(start); + end_pa = __pa_symbol(end - 1) + 1; + } else { + start_pa = __pa(start); + end_pa = __pa(end - 1) + 1; + } + + if (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK)) { + if (start_pa < end_pa) + memblock_remove_range(&memblock.reserved, + start_pa, end_pa - start_pa); + } + + pages = __free_reserved_area(start_pa, end_pa, poison); + if (pages && s) + pr_info("Freeing %s memory: %ldK\n", s, K(pages)); + + return pages; +} + /** * memblock_free - free boot memory allocation * @ptr: starting address of the boot memory allocation * @size: size of the boot memory block in bytes * * Free boot memory block previously allocated by memblock_alloc_xx() API. - * The freeing memory will not be released to the buddy allocator. + * If called after the buddy allocator is available, the memory is released to + * the buddy allocator. */ void __init_memblock memblock_free(void *ptr, size_t size) { @@ -913,17 +983,24 @@ void __init_memblock memblock_free(void *ptr, size_t size) * @size: size of the boot memory block in bytes * * Free boot memory block previously allocated by memblock_phys_alloc_xx() API. - * The freeing memory will not be released to the buddy allocator. + * If called after the buddy allocator is available, the memory is released to + * the buddy allocator. */ int __init_memblock memblock_phys_free(phys_addr_t base, phys_addr_t size) { phys_addr_t end = base + size - 1; + int ret; memblock_dbg("%s: [%pa-%pa] %pS\n", __func__, &base, &end, (void *)_RET_IP_); kmemleak_free_part_phys(base, size); - return memblock_remove_range(&memblock.reserved, base, size); + ret = memblock_remove_range(&memblock.reserved, base, size); + + if (slab_is_available()) + __free_reserved_area(base, base + size, -1); + + return ret; } int __init_memblock __memblock_reserve(phys_addr_t base, phys_addr_t size, @@ -973,7 +1050,7 @@ __init void memmap_init_kho_scratch_pages(void) /* * Initialize struct pages for free scratch memory. * The struct pages for reserved scratch memory will be set up in - * reserve_bootmem_region() + * memmap_init_reserved_pages() */ __for_each_mem_range(i, &memblock.memory, NULL, NUMA_NO_NODE, MEMBLOCK_KHO_SCRATCH, &start, &end, &nid) { @@ -1766,32 +1843,6 @@ void *__init __memblock_alloc_or_panic(phys_addr_t size, phys_addr_t align, return addr; } -/** - * memblock_free_late - free pages directly to buddy allocator - * @base: phys starting address of the boot memory block - * @size: size of the boot memory block in bytes - * - * This is only useful when the memblock allocator has already been torn - * down, but we are still initializing the system. Pages are released directly - * to the buddy allocator. - */ -void __init memblock_free_late(phys_addr_t base, phys_addr_t size) -{ - phys_addr_t cursor, end; - - end = base + size - 1; - memblock_dbg("%s: [%pa-%pa] %pS\n", - __func__, &base, &end, (void *)_RET_IP_); - kmemleak_free_part_phys(base, size); - cursor = PFN_UP(base); - end = PFN_DOWN(base + size); - - for (; cursor < end; cursor++) { - memblock_free_pages(cursor, 0); - totalram_pages_inc(); - } -} - /* * Remaining API functions */ @@ -2255,6 +2306,31 @@ static unsigned long __init __free_memory_core(phys_addr_t start, return end_pfn - start_pfn; } +/* + * Initialised pages do not have PageReserved set. This function is called + * for each reserved range and marks the pages PageReserved. + * When deferred initialization of struct pages is enabled it also ensures + * that struct pages are properly initialised. + */ +static void __init memmap_init_reserved_range(phys_addr_t start, + phys_addr_t end, int nid) +{ + unsigned long pfn; + + for_each_valid_pfn(pfn, PFN_DOWN(start), PFN_UP(end)) { + struct page *page = pfn_to_page(pfn); + + init_deferred_page(pfn, nid); + + /* + * no need for atomic set_bit because the struct + * page is not visible yet so nobody should + * access it yet. + */ + __SetPageReserved(page); + } +} + static void __init memmap_init_reserved_pages(void) { struct memblock_region *region; @@ -2274,7 +2350,7 @@ repeat: end = start + region->size; if (memblock_is_nomap(region)) - reserve_bootmem_region(start, end, nid); + memmap_init_reserved_range(start, end, nid); memblock_set_node(start, region->size, &memblock.reserved, nid); } @@ -2299,7 +2375,7 @@ repeat: if (!numa_valid_node(nid)) nid = early_pfn_to_nid(PFN_DOWN(start)); - reserve_bootmem_region(start, end, nid); + memmap_init_reserved_range(start, end, nid); } } } @@ -2449,7 +2525,7 @@ int reserve_mem_release_by_name(const char *name) return 0; start = phys_to_virt(map->start); - end = start + map->size - 1; + end = start + map->size; snprintf(buf, sizeof(buf), "reserve_mem:%s", name); free_reserved_area(start, end, 0, buf); map->size = 0; @@ -2525,7 +2601,7 @@ static int __init prepare_kho_fdt(void) if (err) goto err_unpreserve_fdt; - err = kho_add_subtree(MEMBLOCK_KHO_FDT, fdt); + err = kho_add_subtree(MEMBLOCK_KHO_FDT, fdt, fdt_totalsize(fdt)); if (err) goto err_unpreserve_fdt; @@ -2570,7 +2646,7 @@ static void *__init reserve_mem_kho_retrieve_fdt(void) if (fdt) return fdt; - err = kho_retrieve_subtree(MEMBLOCK_KHO_FDT, &fdt_phys); + err = kho_retrieve_subtree(MEMBLOCK_KHO_FDT, &fdt_phys, NULL); if (err) { if (err != -ENOENT) pr_warn("failed to retrieve FDT '%s' from KHO: %d\n", @@ -2657,23 +2733,25 @@ static int __init reserve_mem(char *p) int len; if (!p) - return -EINVAL; + goto err_param; /* Check if there's room for more reserved memory */ - if (reserved_mem_count >= RESERVE_MEM_MAX_ENTRIES) + if (reserved_mem_count >= RESERVE_MEM_MAX_ENTRIES) { + pr_err("reserve_mem: no more room for reserved memory\n"); return -EBUSY; + } oldp = p; size = memparse(p, &p); if (!size || p == oldp) - return -EINVAL; + goto err_param; if (*p != ':') - return -EINVAL; + goto err_param; align = memparse(p+1, &p); if (*p != ':') - return -EINVAL; + goto err_param; /* * memblock_phys_alloc() doesn't like a zero size align, @@ -2687,7 +2765,7 @@ static int __init reserve_mem(char *p) /* name needs to have length but not too big */ if (!len || len >= RESERVE_MEM_NAME_SIZE) - return -EINVAL; + goto err_param; /* Make sure that name has text */ for (p = name; *p; p++) { @@ -2695,11 +2773,13 @@ static int __init reserve_mem(char *p) break; } if (!*p) - return -EINVAL; + goto err_param; /* Make sure the name is not already used */ - if (reserve_mem_find_by_name(name, &start, &tmp)) + if (reserve_mem_find_by_name(name, &start, &tmp)) { + pr_err("reserve_mem: name \"%s\" was already used\n", name); return -EBUSY; + } /* Pick previous allocations up from KHO if available */ if (reserve_mem_kho_revive(name, size, align)) @@ -2707,16 +2787,22 @@ static int __init reserve_mem(char *p) /* TODO: Allocation must be outside of scratch region */ start = memblock_phys_alloc(size, align); - if (!start) + if (!start) { + pr_err("reserve_mem: memblock allocation failed\n"); return -ENOMEM; + } reserved_mem_add(start, size, name); return 1; +err_param: + pr_err("reserve_mem: empty or malformed parameter\n"); + return -EINVAL; } __setup("reserve_mem=", reserve_mem); -#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_ARCH_KEEP_MEMBLOCK) +#ifdef CONFIG_DEBUG_FS +#ifdef CONFIG_ARCH_KEEP_MEMBLOCK static const char * const flagname[] = { [ilog2(MEMBLOCK_HOTPLUG)] = "HOTPLUG", [ilog2(MEMBLOCK_MIRROR)] = "MIRROR", @@ -2763,10 +2849,8 @@ static int memblock_debug_show(struct seq_file *m, void *private) } DEFINE_SHOW_ATTRIBUTE(memblock_debug); -static int __init memblock_init_debugfs(void) +static inline void memblock_debugfs_expose_arrays(struct dentry *root) { - struct dentry *root = debugfs_create_dir("memblock", NULL); - debugfs_create_file("memory", 0444, root, &memblock.memory, &memblock_debug_fops); debugfs_create_file("reserved", 0444, root, @@ -2775,7 +2859,48 @@ static int __init memblock_init_debugfs(void) debugfs_create_file("physmem", 0444, root, &physmem, &memblock_debug_fops); #endif +} +#else + +static inline void memblock_debugfs_expose_arrays(struct dentry *root) { } + +#endif /* CONFIG_ARCH_KEEP_MEMBLOCK */ + +static int memblock_reserve_mem_show(struct seq_file *m, void *private) +{ + struct reserve_mem_table *map; + char txtsz[16]; + + guard(mutex)(&reserve_mem_lock); + for (int i = 0; i < reserved_mem_count; i++) { + map = &reserved_mem_table[i]; + if (!map->size) + continue; + + memset(txtsz, 0, sizeof(txtsz)); + string_get_size(map->size, 1, STRING_UNITS_2, txtsz, sizeof(txtsz)); + seq_printf(m, "%s\t\t(%s)\n", map->name, txtsz); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(memblock_reserve_mem); + +static int __init memblock_init_debugfs(void) +{ + struct dentry *root; + + if (!IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK) && !reserved_mem_count) + return 0; + + root = debugfs_create_dir("memblock", NULL); + + if (reserved_mem_count) + debugfs_create_file("reserve_mem_param", 0444, root, NULL, + &memblock_reserve_mem_fops); + + memblock_debugfs_expose_arrays(root); return 0; } __initcall(memblock_init_debugfs); diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c index 437cd25784fe..433bba9dfe71 100644 --- a/mm/memcontrol-v1.c +++ b/mm/memcontrol-v1.c @@ -613,6 +613,7 @@ void memcg1_commit_charge(struct folio *folio, struct mem_cgroup *memcg) void memcg1_swapout(struct folio *folio, swp_entry_t entry) { struct mem_cgroup *memcg, *swap_memcg; + struct obj_cgroup *objcg; unsigned int nr_entries; VM_BUG_ON_FOLIO(folio_test_lru(folio), folio); @@ -624,12 +625,13 @@ void memcg1_swapout(struct folio *folio, swp_entry_t entry) if (!do_memsw_account()) return; - memcg = folio_memcg(folio); - - VM_WARN_ON_ONCE_FOLIO(!memcg, folio); - if (!memcg) + objcg = folio_objcg(folio); + VM_WARN_ON_ONCE_FOLIO(!objcg, folio); + if (!objcg) return; + rcu_read_lock(); + memcg = obj_cgroup_memcg(objcg); /* * In case the memcg owning these pages has been offlined and doesn't * have an ID allocated to it anymore, charge the closest online @@ -644,7 +646,7 @@ void memcg1_swapout(struct folio *folio, swp_entry_t entry) folio_unqueue_deferred_split(folio); folio->memcg_data = 0; - if (!mem_cgroup_is_root(memcg)) + if (!obj_cgroup_is_root(objcg)) page_counter_uncharge(&memcg->memory, nr_entries); if (memcg != swap_memcg) { @@ -665,7 +667,8 @@ void memcg1_swapout(struct folio *folio, swp_entry_t entry) preempt_enable_nested(); memcg1_check_events(memcg, folio_nid(folio)); - css_put(&memcg->css); + rcu_read_unlock(); + obj_cgroup_put(objcg); } /* @@ -1884,6 +1887,22 @@ static const unsigned int memcg1_events[] = { PGMAJFAULT, }; +void reparent_memcg1_state_local(struct mem_cgroup *memcg, struct mem_cgroup *parent) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(memcg1_stats); i++) + reparent_memcg_state_local(memcg, parent, memcg1_stats[i]); +} + +void reparent_memcg1_lruvec_state_local(struct mem_cgroup *memcg, struct mem_cgroup *parent) +{ + int i; + + for (i = 0; i < NR_LRU_LISTS; i++) + reparent_memcg_lruvec_state_local(memcg, parent, i); +} + void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s) { unsigned long memory, memsw; diff --git a/mm/memcontrol-v1.h b/mm/memcontrol-v1.h index 1b969294ea6a..f92f81108d5e 100644 --- a/mm/memcontrol-v1.h +++ b/mm/memcontrol-v1.h @@ -73,6 +73,13 @@ void memcg1_uncharge_batch(struct mem_cgroup *memcg, unsigned long pgpgout, unsigned long nr_memory, int nid); void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s); +void reparent_memcg1_state_local(struct mem_cgroup *memcg, struct mem_cgroup *parent); +void reparent_memcg1_lruvec_state_local(struct mem_cgroup *memcg, struct mem_cgroup *parent); + +void reparent_memcg_state_local(struct mem_cgroup *memcg, + struct mem_cgroup *parent, int idx); +void reparent_memcg_lruvec_state_local(struct mem_cgroup *memcg, + struct mem_cgroup *parent, int idx); void memcg1_account_kmem(struct mem_cgroup *memcg, int nr_pages); static inline bool memcg1_tcpmem_active(struct mem_cgroup *memcg) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 051b82ebf371..c03d4787d466 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -206,26 +206,100 @@ static struct obj_cgroup *obj_cgroup_alloc(void) return objcg; } -static void memcg_reparent_objcgs(struct mem_cgroup *memcg, - struct mem_cgroup *parent) +static inline struct obj_cgroup *__memcg_reparent_objcgs(struct mem_cgroup *memcg, + struct mem_cgroup *parent, + int nid) { struct obj_cgroup *objcg, *iter; + struct mem_cgroup_per_node *pn = memcg->nodeinfo[nid]; + struct mem_cgroup_per_node *parent_pn = parent->nodeinfo[nid]; - objcg = rcu_replace_pointer(memcg->objcg, NULL, true); - - spin_lock_irq(&objcg_lock); - + objcg = rcu_replace_pointer(pn->objcg, NULL, true); /* 1) Ready to reparent active objcg. */ - list_add(&objcg->list, &memcg->objcg_list); + list_add(&objcg->list, &pn->objcg_list); /* 2) Reparent active objcg and already reparented objcgs to parent. */ - list_for_each_entry(iter, &memcg->objcg_list, list) + list_for_each_entry(iter, &pn->objcg_list, list) WRITE_ONCE(iter->memcg, parent); /* 3) Move already reparented objcgs to the parent's list */ - list_splice(&memcg->objcg_list, &parent->objcg_list); + list_splice(&pn->objcg_list, &parent_pn->objcg_list); + return objcg; +} + +#ifdef CONFIG_MEMCG_V1 +static void __mem_cgroup_flush_stats(struct mem_cgroup *memcg, bool force); + +static inline void reparent_state_local(struct mem_cgroup *memcg, struct mem_cgroup *parent) +{ + if (cgroup_subsys_on_dfl(memory_cgrp_subsys)) + return; + + /* + * Reparent stats exposed non-hierarchically. Flush @memcg's stats first + * to read its stats accurately , and conservatively flush @parent's + * stats after reparenting to avoid hiding a potentially large stat + * update (e.g. from callers of mem_cgroup_flush_stats_ratelimited()). + */ + __mem_cgroup_flush_stats(memcg, true); + + /* The following counts are all non-hierarchical and need to be reparented. */ + reparent_memcg1_state_local(memcg, parent); + reparent_memcg1_lruvec_state_local(memcg, parent); + + __mem_cgroup_flush_stats(parent, true); +} +#else +static inline void reparent_state_local(struct mem_cgroup *memcg, struct mem_cgroup *parent) +{ +} +#endif + +static inline void reparent_locks(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid) +{ + spin_lock_irq(&objcg_lock); + spin_lock_nested(&mem_cgroup_lruvec(memcg, NODE_DATA(nid))->lru_lock, 1); + spin_lock_nested(&mem_cgroup_lruvec(parent, NODE_DATA(nid))->lru_lock, 2); +} + +static inline void reparent_unlocks(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid) +{ + spin_unlock(&mem_cgroup_lruvec(parent, NODE_DATA(nid))->lru_lock); + spin_unlock(&mem_cgroup_lruvec(memcg, NODE_DATA(nid))->lru_lock); spin_unlock_irq(&objcg_lock); +} - percpu_ref_kill(&objcg->refcnt); +static void memcg_reparent_objcgs(struct mem_cgroup *memcg) +{ + struct obj_cgroup *objcg; + struct mem_cgroup *parent = parent_mem_cgroup(memcg); + int nid; + + for_each_node(nid) { +retry: + if (lru_gen_enabled()) + max_lru_gen_memcg(parent, nid); + + reparent_locks(memcg, parent, nid); + + if (lru_gen_enabled()) { + if (!recheck_lru_gen_max_memcg(parent, nid)) { + reparent_unlocks(memcg, parent, nid); + cond_resched(); + goto retry; + } + lru_gen_reparent_memcg(memcg, parent, nid); + } else { + lru_reparent_memcg(memcg, parent, nid); + } + + objcg = __memcg_reparent_objcgs(memcg, parent, nid); + + reparent_unlocks(memcg, parent, nid); + + percpu_ref_kill(&objcg->refcnt); + } + + reparent_state_local(memcg, parent); } /* @@ -241,7 +315,7 @@ DEFINE_STATIC_KEY_FALSE(memcg_bpf_enabled_key); EXPORT_SYMBOL(memcg_bpf_enabled_key); /** - * mem_cgroup_css_from_folio - css of the memcg associated with a folio + * get_mem_cgroup_css_from_folio - acquire a css of the memcg associated with a folio * @folio: folio of interest * * If memcg is bound to the default hierarchy, css of the memcg associated @@ -251,14 +325,16 @@ EXPORT_SYMBOL(memcg_bpf_enabled_key); * If memcg is bound to a traditional hierarchy, the css of root_mem_cgroup * is returned. */ -struct cgroup_subsys_state *mem_cgroup_css_from_folio(struct folio *folio) +struct cgroup_subsys_state *get_mem_cgroup_css_from_folio(struct folio *folio) { - struct mem_cgroup *memcg = folio_memcg(folio); + struct mem_cgroup *memcg; - if (!memcg || !cgroup_subsys_on_dfl(memory_cgrp_subsys)) - memcg = root_mem_cgroup; + if (!cgroup_subsys_on_dfl(memory_cgrp_subsys)) + return &root_mem_cgroup->css; - return &memcg->css; + memcg = get_mem_cgroup_from_folio(folio); + + return memcg ? &memcg->css : &root_mem_cgroup->css; } /** @@ -449,6 +525,30 @@ unsigned long lruvec_page_state_local(struct lruvec *lruvec, return x; } +#ifdef CONFIG_MEMCG_V1 +static void __mod_memcg_lruvec_state(struct mem_cgroup_per_node *pn, + enum node_stat_item idx, long val); + +void reparent_memcg_lruvec_state_local(struct mem_cgroup *memcg, + struct mem_cgroup *parent, int idx) +{ + int nid; + + for_each_node(nid) { + struct lruvec *child_lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); + struct lruvec *parent_lruvec = mem_cgroup_lruvec(parent, NODE_DATA(nid)); + unsigned long value = lruvec_page_state_local(child_lruvec, idx); + struct mem_cgroup_per_node *child_pn, *parent_pn; + + child_pn = container_of(child_lruvec, struct mem_cgroup_per_node, lruvec); + parent_pn = container_of(parent_lruvec, struct mem_cgroup_per_node, lruvec); + + __mod_memcg_lruvec_state(child_pn, idx, -value); + __mod_memcg_lruvec_state(parent_pn, idx, value); + } +} +#endif + /* Subset of vm_event_item to report for memcg event stats */ static const unsigned int memcg_vm_event_stat[] = { #ifdef CONFIG_MEMCG_V1 @@ -508,7 +608,7 @@ static inline int memcg_events_index(enum vm_event_item idx) struct memcg_vmstats_percpu { /* Stats updates since the last flush */ - unsigned int stats_updates; + unsigned long stats_updates; /* Cached pointers for fast iteration in memcg_rstat_updated() */ struct memcg_vmstats_percpu __percpu *parent_pcpu; @@ -539,7 +639,7 @@ struct memcg_vmstats { unsigned long events_pending[NR_MEMCG_EVENTS]; /* Stats updates since the last flush */ - atomic_t stats_updates; + atomic_long_t stats_updates; }; /* @@ -565,16 +665,16 @@ static u64 flush_last_time; static bool memcg_vmstats_needs_flush(struct memcg_vmstats *vmstats) { - return atomic_read(&vmstats->stats_updates) > + return atomic_long_read(&vmstats->stats_updates) > MEMCG_CHARGE_BATCH * num_online_cpus(); } -static inline void memcg_rstat_updated(struct mem_cgroup *memcg, int val, +static inline void memcg_rstat_updated(struct mem_cgroup *memcg, long val, int cpu) { struct memcg_vmstats_percpu __percpu *statc_pcpu; struct memcg_vmstats_percpu *statc; - unsigned int stats_updates; + unsigned long stats_updates; if (!val) return; @@ -597,7 +697,7 @@ static inline void memcg_rstat_updated(struct mem_cgroup *memcg, int val, continue; stats_updates = this_cpu_xchg(statc_pcpu->stats_updates, 0); - atomic_add(stats_updates, &statc->vmstats->stats_updates); + atomic_long_add(stats_updates, &statc->vmstats->stats_updates); } } @@ -605,7 +705,7 @@ static void __mem_cgroup_flush_stats(struct mem_cgroup *memcg, bool force) { bool needs_flush = memcg_vmstats_needs_flush(memcg->vmstats); - trace_memcg_flush_stats(memcg, atomic_read(&memcg->vmstats->stats_updates), + trace_memcg_flush_stats(memcg, atomic_long_read(&memcg->vmstats->stats_updates), force, needs_flush); if (!force && !needs_flush) @@ -684,14 +784,82 @@ static int memcg_page_state_unit(int item); * Normalize the value passed into memcg_rstat_updated() to be in pages. Round * up non-zero sub-page updates to 1 page as zero page updates are ignored. */ -static int memcg_state_val_in_pages(int idx, int val) +static long memcg_state_val_in_pages(int idx, long val) { int unit = memcg_page_state_unit(idx); + long res; if (!val || unit == PAGE_SIZE) return val; - else - return max(val * unit / PAGE_SIZE, 1UL); + + /* Get the absolute value of (val * unit / PAGE_SIZE). */ + res = mult_frac(abs(val), unit, PAGE_SIZE); + /* Round up zero values. */ + res = res ? : 1; + + return val < 0 ? -res : res; +} + +#ifdef CONFIG_MEMCG_V1 +/* + * Used in mod_memcg_state() and mod_memcg_lruvec_state() to avoid race with + * reparenting of non-hierarchical state_locals. + */ +static inline struct mem_cgroup *get_non_dying_memcg_start(struct mem_cgroup *memcg, + bool *rcu_locked) +{ + /* Rebinding can cause this value to be changed at runtime */ + if (cgroup_subsys_on_dfl(memory_cgrp_subsys)) { + *rcu_locked = false; + return memcg; + } + + rcu_read_lock(); + *rcu_locked = true; + + while (memcg_is_dying(memcg)) + memcg = parent_mem_cgroup(memcg); + + return memcg; +} + +static inline void get_non_dying_memcg_end(bool rcu_locked) +{ + if (!rcu_locked) + return; + + rcu_read_unlock(); +} +#else +static inline struct mem_cgroup *get_non_dying_memcg_start(struct mem_cgroup *memcg, + bool *rcu_locked) +{ + return memcg; +} + +static inline void get_non_dying_memcg_end(bool rcu_locked) +{ +} +#endif + +static void __mod_memcg_state(struct mem_cgroup *memcg, + enum memcg_stat_item idx, long val) +{ + int i = memcg_stats_index(idx); + int cpu; + + if (WARN_ONCE(BAD_STAT_IDX(i), "%s: missing stat item %d\n", __func__, idx)) + return; + + cpu = get_cpu(); + + this_cpu_add(memcg->vmstats_percpu->state[i], val); + val = memcg_state_val_in_pages(idx, val); + memcg_rstat_updated(memcg, val, cpu); + + trace_mod_memcg_state(memcg, idx, val); + + put_cpu(); } /** @@ -703,23 +871,14 @@ static int memcg_state_val_in_pages(int idx, int val) void mod_memcg_state(struct mem_cgroup *memcg, enum memcg_stat_item idx, int val) { - int i = memcg_stats_index(idx); - int cpu; + bool rcu_locked = false; if (mem_cgroup_disabled()) return; - if (WARN_ONCE(BAD_STAT_IDX(i), "%s: missing stat item %d\n", __func__, idx)) - return; - - cpu = get_cpu(); - - this_cpu_add(memcg->vmstats_percpu->state[i], val); - val = memcg_state_val_in_pages(idx, val); - memcg_rstat_updated(memcg, val, cpu); - trace_mod_memcg_state(memcg, idx, val); - - put_cpu(); + memcg = get_non_dying_memcg_start(memcg, &rcu_locked); + __mod_memcg_state(memcg, idx, val); + get_non_dying_memcg_end(rcu_locked); } #ifdef CONFIG_MEMCG_V1 @@ -739,23 +898,27 @@ unsigned long memcg_page_state_local(struct mem_cgroup *memcg, int idx) #endif return x; } + +void reparent_memcg_state_local(struct mem_cgroup *memcg, + struct mem_cgroup *parent, int idx) +{ + unsigned long value = memcg_page_state_local(memcg, idx); + + __mod_memcg_state(memcg, idx, -value); + __mod_memcg_state(parent, idx, value); +} #endif -static void mod_memcg_lruvec_state(struct lruvec *lruvec, - enum node_stat_item idx, - int val) +static void __mod_memcg_lruvec_state(struct mem_cgroup_per_node *pn, + enum node_stat_item idx, long val) { - struct mem_cgroup_per_node *pn; - struct mem_cgroup *memcg; + struct mem_cgroup *memcg = pn->memcg; int i = memcg_stats_index(idx); int cpu; if (WARN_ONCE(BAD_STAT_IDX(i), "%s: missing stat item %d\n", __func__, idx)) return; - pn = container_of(lruvec, struct mem_cgroup_per_node, lruvec); - memcg = pn->memcg; - cpu = get_cpu(); /* Update memcg */ @@ -771,6 +934,24 @@ static void mod_memcg_lruvec_state(struct lruvec *lruvec, put_cpu(); } +static void mod_memcg_lruvec_state(struct lruvec *lruvec, + enum node_stat_item idx, + int val) +{ + struct pglist_data *pgdat = lruvec_pgdat(lruvec); + struct mem_cgroup_per_node *pn; + struct mem_cgroup *memcg; + bool rcu_locked = false; + + pn = container_of(lruvec, struct mem_cgroup_per_node, lruvec); + memcg = get_non_dying_memcg_start(pn->memcg, &rcu_locked); + pn = memcg->nodeinfo[pgdat->node_id]; + + __mod_memcg_lruvec_state(pn, idx, val); + + get_non_dying_memcg_end(rcu_locked); +} + /** * mod_lruvec_state - update lruvec memory statistics * @lruvec: the lruvec @@ -991,17 +1172,23 @@ again: /** * get_mem_cgroup_from_folio - Obtain a reference on a given folio's memcg. * @folio: folio from which memcg should be extracted. + * + * See folio_memcg() for folio->objcg/memcg binding rules. */ struct mem_cgroup *get_mem_cgroup_from_folio(struct folio *folio) { - struct mem_cgroup *memcg = folio_memcg(folio); + struct mem_cgroup *memcg; if (mem_cgroup_disabled()) return NULL; + if (!folio_memcg_charged(folio)) + return root_mem_cgroup; + rcu_read_lock(); - if (!memcg || WARN_ON_ONCE(!css_tryget(&memcg->css))) - memcg = root_mem_cgroup; + do { + memcg = folio_memcg(folio); + } while (unlikely(!css_tryget(&memcg->css))); rcu_read_unlock(); return memcg; } @@ -1198,23 +1385,6 @@ void mem_cgroup_scan_tasks(struct mem_cgroup *memcg, } } -#ifdef CONFIG_DEBUG_VM -void lruvec_memcg_debug(struct lruvec *lruvec, struct folio *folio) -{ - struct mem_cgroup *memcg; - - if (mem_cgroup_disabled()) - return; - - memcg = folio_memcg(folio); - - if (!memcg) - VM_BUG_ON_FOLIO(!mem_cgroup_is_root(lruvec_memcg(lruvec)), folio); - else - VM_BUG_ON_FOLIO(lruvec_memcg(lruvec) != memcg, folio); -} -#endif - /** * folio_lruvec_lock - Lock the lruvec for a folio. * @folio: Pointer to the folio. @@ -1224,14 +1394,20 @@ void lruvec_memcg_debug(struct lruvec *lruvec, struct folio *folio) * - folio_test_lru false * - folio frozen (refcount of 0) * - * Return: The lruvec this folio is on with its lock held. + * Return: The lruvec this folio is on with its lock held and rcu read lock held. */ struct lruvec *folio_lruvec_lock(struct folio *folio) { - struct lruvec *lruvec = folio_lruvec(folio); + struct lruvec *lruvec; + rcu_read_lock(); +retry: + lruvec = folio_lruvec(folio); spin_lock(&lruvec->lru_lock); - lruvec_memcg_debug(lruvec, folio); + if (unlikely(lruvec_memcg(lruvec) != folio_memcg(folio))) { + spin_unlock(&lruvec->lru_lock); + goto retry; + } return lruvec; } @@ -1246,14 +1422,20 @@ struct lruvec *folio_lruvec_lock(struct folio *folio) * - folio frozen (refcount of 0) * * Return: The lruvec this folio is on with its lock held and interrupts - * disabled. + * disabled and rcu read lock held. */ struct lruvec *folio_lruvec_lock_irq(struct folio *folio) { - struct lruvec *lruvec = folio_lruvec(folio); + struct lruvec *lruvec; + rcu_read_lock(); +retry: + lruvec = folio_lruvec(folio); spin_lock_irq(&lruvec->lru_lock); - lruvec_memcg_debug(lruvec, folio); + if (unlikely(lruvec_memcg(lruvec) != folio_memcg(folio))) { + spin_unlock_irq(&lruvec->lru_lock); + goto retry; + } return lruvec; } @@ -1269,15 +1451,21 @@ struct lruvec *folio_lruvec_lock_irq(struct folio *folio) * - folio frozen (refcount of 0) * * Return: The lruvec this folio is on with its lock held and interrupts - * disabled. + * disabled and rcu read lock held. */ struct lruvec *folio_lruvec_lock_irqsave(struct folio *folio, unsigned long *flags) { - struct lruvec *lruvec = folio_lruvec(folio); + struct lruvec *lruvec; + rcu_read_lock(); +retry: + lruvec = folio_lruvec(folio); spin_lock_irqsave(&lruvec->lru_lock, *flags); - lruvec_memcg_debug(lruvec, folio); + if (unlikely(lruvec_memcg(lruvec) != folio_memcg(folio))) { + spin_unlock_irqrestore(&lruvec->lru_lock, *flags); + goto retry; + } return lruvec; } @@ -1293,7 +1481,7 @@ struct lruvec *folio_lruvec_lock_irqsave(struct folio *folio, * to or just after a page is removed from an lru list. */ void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru, - int zid, int nr_pages) + int zid, long nr_pages) { struct mem_cgroup_per_node *mz; unsigned long *lru_size; @@ -1310,7 +1498,7 @@ void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru, size = *lru_size; if (WARN_ONCE(size < 0, - "%s(%p, %d, %d): lru_size %ld\n", + "%s(%p, %d, %ld): lru_size %ld\n", __func__, lruvec, lru, nr_pages, size)) { VM_BUG_ON(1); *lru_size = 0; @@ -2581,17 +2769,17 @@ static inline int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, return try_charge_memcg(memcg, gfp_mask, nr_pages); } -static void commit_charge(struct folio *folio, struct mem_cgroup *memcg) +static void commit_charge(struct folio *folio, struct obj_cgroup *objcg) { VM_BUG_ON_FOLIO(folio_memcg_charged(folio), folio); /* - * Any of the following ensures page's memcg stability: + * Any of the following ensures folio's objcg stability: * * - the page lock * - LRU isolation * - exclusive reference */ - folio->memcg_data = (unsigned long)memcg; + folio->memcg_data = (unsigned long)objcg; } #ifdef CONFIG_MEMCG_NMI_SAFETY_REQUIRES_ATOMIC @@ -2693,14 +2881,26 @@ struct mem_cgroup *mem_cgroup_from_virt(void *p) static struct obj_cgroup *__get_obj_cgroup_from_memcg(struct mem_cgroup *memcg) { - struct obj_cgroup *objcg = NULL; + int nid = numa_node_id(); + + for (; memcg; memcg = parent_mem_cgroup(memcg)) { + struct obj_cgroup *objcg = rcu_dereference(memcg->nodeinfo[nid]->objcg); - for (; !mem_cgroup_is_root(memcg); memcg = parent_mem_cgroup(memcg)) { - objcg = rcu_dereference(memcg->objcg); if (likely(objcg && obj_cgroup_tryget(objcg))) - break; - objcg = NULL; + return objcg; } + + return NULL; +} + +static inline struct obj_cgroup *get_obj_cgroup_from_memcg(struct mem_cgroup *memcg) +{ + struct obj_cgroup *objcg; + + rcu_read_lock(); + objcg = __get_obj_cgroup_from_memcg(memcg); + rcu_read_unlock(); + return objcg; } @@ -2759,6 +2959,7 @@ __always_inline struct obj_cgroup *current_obj_cgroup(void) { struct mem_cgroup *memcg; struct obj_cgroup *objcg; + int nid = numa_node_id(); if (IS_ENABLED(CONFIG_MEMCG_NMI_UNSAFE) && in_nmi()) return NULL; @@ -2775,53 +2976,39 @@ __always_inline struct obj_cgroup *current_obj_cgroup(void) * Objcg reference is kept by the task, so it's safe * to use the objcg by the current task. */ - return objcg; + return objcg ? : rcu_dereference_check(root_mem_cgroup->nodeinfo[nid]->objcg, 1); } memcg = this_cpu_read(int_active_memcg); if (unlikely(memcg)) goto from_memcg; - return NULL; + return rcu_dereference_check(root_mem_cgroup->nodeinfo[nid]->objcg, 1); from_memcg: - objcg = NULL; - for (; !mem_cgroup_is_root(memcg); memcg = parent_mem_cgroup(memcg)) { + for (; memcg; memcg = parent_mem_cgroup(memcg)) { /* * Memcg pointer is protected by scope (see set_active_memcg()) * and is pinning the corresponding objcg, so objcg can't go * away and can be used within the scope without any additional * protection. */ - objcg = rcu_dereference_check(memcg->objcg, 1); + objcg = rcu_dereference_check(memcg->nodeinfo[nid]->objcg, 1); if (likely(objcg)) - break; + return objcg; } - return objcg; + return rcu_dereference_check(root_mem_cgroup->nodeinfo[nid]->objcg, 1); } struct obj_cgroup *get_obj_cgroup_from_folio(struct folio *folio) { struct obj_cgroup *objcg; - if (!memcg_kmem_online()) - return NULL; - - if (folio_memcg_kmem(folio)) { - objcg = __folio_objcg(folio); + objcg = folio_objcg(folio); + if (objcg) obj_cgroup_get(objcg); - } else { - struct mem_cgroup *memcg; - rcu_read_lock(); - memcg = __folio_memcg(folio); - if (memcg) - objcg = __get_obj_cgroup_from_memcg(memcg); - else - objcg = NULL; - rcu_read_unlock(); - } return objcg; } @@ -2922,7 +3109,7 @@ int __memcg_kmem_charge_page(struct page *page, gfp_t gfp, int order) int ret = 0; objcg = current_obj_cgroup(); - if (objcg) { + if (objcg && !obj_cgroup_is_root(objcg)) { ret = obj_cgroup_charge_pages(objcg, gfp, 1 << order); if (!ret) { obj_cgroup_get(objcg); @@ -3251,7 +3438,7 @@ bool __memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, * obj_cgroup_get() is used to get a permanent reference. */ objcg = current_obj_cgroup(); - if (!objcg) + if (!objcg || obj_cgroup_is_root(objcg)) return true; /* @@ -3383,33 +3570,20 @@ void folio_split_memcg_refs(struct folio *folio, unsigned old_order, return; new_refs = (1 << (old_order - new_order)) - 1; - css_get_many(&__folio_memcg(folio)->css, new_refs); + obj_cgroup_get_many(folio_objcg(folio), new_refs); } -static int memcg_online_kmem(struct mem_cgroup *memcg) +static void memcg_online_kmem(struct mem_cgroup *memcg) { - struct obj_cgroup *objcg; - if (mem_cgroup_kmem_disabled()) - return 0; + return; if (unlikely(mem_cgroup_is_root(memcg))) - return 0; - - objcg = obj_cgroup_alloc(); - if (!objcg) - return -ENOMEM; - - objcg->memcg = memcg; - rcu_assign_pointer(memcg->objcg, objcg); - obj_cgroup_get(objcg); - memcg->orig_objcg = objcg; + return; static_branch_enable(&memcg_kmem_online_key); memcg->kmemcg_id = memcg->id.id; - - return 0; } static void memcg_offline_kmem(struct mem_cgroup *memcg) @@ -3423,16 +3597,7 @@ static void memcg_offline_kmem(struct mem_cgroup *memcg) return; parent = parent_mem_cgroup(memcg); - if (!parent) - parent = root_mem_cgroup; - memcg_reparent_list_lrus(memcg, parent); - - /* - * Objcg's reparenting must be after list_lru's, make sure list_lru - * helpers won't use parent's list_lru until child is drained. - */ - memcg_reparent_objcgs(memcg, parent); } #ifdef CONFIG_CGROUP_WRITEBACK @@ -3705,8 +3870,6 @@ struct mem_cgroup *mem_cgroup_private_id_get_online(struct mem_cgroup *memcg, un break; } memcg = parent_mem_cgroup(memcg); - if (!memcg) - memcg = root_mem_cgroup; } return memcg; } @@ -3771,6 +3934,8 @@ static bool alloc_mem_cgroup_per_node_info(struct mem_cgroup *memcg, int node) if (!pn->lruvec_stats_percpu) goto fail; + INIT_LIST_HEAD(&pn->objcg_list); + lruvec_init(&pn->lruvec); pn->memcg = memcg; @@ -3785,10 +3950,14 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg) { int node; - obj_cgroup_put(memcg->orig_objcg); + for_each_node(node) { + struct mem_cgroup_per_node *pn = memcg->nodeinfo[node]; + if (!pn) + continue; - for_each_node(node) - free_mem_cgroup_per_node_info(memcg->nodeinfo[node]); + obj_cgroup_put(pn->orig_objcg); + free_mem_cgroup_per_node_info(pn); + } memcg1_free_events(memcg); kfree(memcg->vmstats); free_percpu(memcg->vmstats_percpu); @@ -3859,7 +4028,6 @@ static struct mem_cgroup *mem_cgroup_alloc(struct mem_cgroup *parent) #endif memcg1_memcg_init(memcg); memcg->kmemcg_id = -1; - INIT_LIST_HEAD(&memcg->objcg_list); #ifdef CONFIG_CGROUP_WRITEBACK INIT_LIST_HEAD(&memcg->cgwb_list); for (i = 0; i < MEMCG_CGWB_FRN_CNT; i++) @@ -3935,9 +4103,10 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) static int mem_cgroup_css_online(struct cgroup_subsys_state *css) { struct mem_cgroup *memcg = mem_cgroup_from_css(css); + struct obj_cgroup *objcg; + int nid; - if (memcg_online_kmem(memcg)) - goto remove_id; + memcg_online_kmem(memcg); /* * A memcg must be visible for expand_shrinker_info() @@ -3947,6 +4116,20 @@ static int mem_cgroup_css_online(struct cgroup_subsys_state *css) if (alloc_shrinker_info(memcg)) goto offline_kmem; + for_each_node(nid) { + objcg = obj_cgroup_alloc(); + if (!objcg) + goto free_objcg; + + if (unlikely(mem_cgroup_is_root(memcg))) + objcg->is_root = true; + + objcg->memcg = memcg; + rcu_assign_pointer(memcg->nodeinfo[nid]->objcg, objcg); + obj_cgroup_get(objcg); + memcg->nodeinfo[nid]->orig_objcg = objcg; + } + if (unlikely(mem_cgroup_is_root(memcg)) && !mem_cgroup_disabled()) queue_delayed_work(system_dfl_wq, &stats_flush_dwork, FLUSH_TIME); @@ -3969,9 +4152,27 @@ static int mem_cgroup_css_online(struct cgroup_subsys_state *css) xa_store(&mem_cgroup_private_ids, memcg->id.id, memcg, GFP_KERNEL); return 0; +free_objcg: + for_each_node(nid) { + struct mem_cgroup_per_node *pn = memcg->nodeinfo[nid]; + + objcg = rcu_replace_pointer(pn->objcg, NULL, true); + if (objcg) + percpu_ref_kill(&objcg->refcnt); + + if (pn->orig_objcg) { + obj_cgroup_put(pn->orig_objcg); + /* + * Reset pn->orig_objcg to NULL to prevent + * obj_cgroup_put() from being called again in + * __mem_cgroup_free(). + */ + pn->orig_objcg = NULL; + } + } + free_shrinker_info(memcg); offline_kmem: memcg_offline_kmem(memcg); -remove_id: mem_cgroup_private_id_remove(memcg); return -ENOMEM; } @@ -3989,6 +4190,12 @@ static void mem_cgroup_css_offline(struct cgroup_subsys_state *css) memcg_offline_kmem(memcg); reparent_deferred_split_queue(memcg); + /* + * The reparenting of objcg must be after the reparenting of the + * list_lru and deferred_split_queue above, which ensures that they will + * not mistakenly get the parent list_lru and deferred_split_queue. + */ + memcg_reparent_objcgs(memcg); reparent_shrinker_deferred(memcg); wb_memcg_offline(memcg); lru_gen_offline_memcg(memcg); @@ -4221,8 +4428,8 @@ static void mem_cgroup_css_rstat_flush(struct cgroup_subsys_state *css, int cpu) } WRITE_ONCE(statc->stats_updates, 0); /* We are in a per-cpu loop here, only do the atomic write once */ - if (atomic_read(&memcg->vmstats->stats_updates)) - atomic_set(&memcg->vmstats->stats_updates, 0); + if (atomic_long_read(&memcg->vmstats->stats_updates)) + atomic_long_set(&memcg->vmstats->stats_updates, 0); } static void mem_cgroup_fork(struct task_struct *task) @@ -4799,16 +5006,20 @@ void mem_cgroup_calculate_protection(struct mem_cgroup *root, static int charge_memcg(struct folio *folio, struct mem_cgroup *memcg, gfp_t gfp) { - int ret; + int ret = 0; + struct obj_cgroup *objcg; - ret = try_charge(memcg, gfp, folio_nr_pages(folio)); - if (ret) - goto out; - - css_get(&memcg->css); - commit_charge(folio, memcg); + objcg = get_obj_cgroup_from_memcg(memcg); + /* Do not account at the root objcg level. */ + if (!obj_cgroup_is_root(objcg)) + ret = try_charge_memcg(memcg, gfp, folio_nr_pages(folio)); + if (ret) { + obj_cgroup_put(objcg); + return ret; + } + commit_charge(folio, objcg); memcg1_commit_charge(folio, memcg); -out: + return ret; } @@ -4894,7 +5105,7 @@ int mem_cgroup_swapin_charge_folio(struct folio *folio, struct mm_struct *mm, } struct uncharge_gather { - struct mem_cgroup *memcg; + struct obj_cgroup *objcg; unsigned long nr_memory; unsigned long pgpgout; unsigned long nr_kmem; @@ -4908,58 +5119,52 @@ static inline void uncharge_gather_clear(struct uncharge_gather *ug) static void uncharge_batch(const struct uncharge_gather *ug) { + struct mem_cgroup *memcg; + + rcu_read_lock(); + memcg = obj_cgroup_memcg(ug->objcg); if (ug->nr_memory) { - memcg_uncharge(ug->memcg, ug->nr_memory); + memcg_uncharge(memcg, ug->nr_memory); if (ug->nr_kmem) { - mod_memcg_state(ug->memcg, MEMCG_KMEM, -ug->nr_kmem); - memcg1_account_kmem(ug->memcg, -ug->nr_kmem); + mod_memcg_state(memcg, MEMCG_KMEM, -ug->nr_kmem); + memcg1_account_kmem(memcg, -ug->nr_kmem); } - memcg1_oom_recover(ug->memcg); + memcg1_oom_recover(memcg); } - memcg1_uncharge_batch(ug->memcg, ug->pgpgout, ug->nr_memory, ug->nid); + memcg1_uncharge_batch(memcg, ug->pgpgout, ug->nr_memory, ug->nid); + rcu_read_unlock(); /* drop reference from uncharge_folio */ - css_put(&ug->memcg->css); + obj_cgroup_put(ug->objcg); } static void uncharge_folio(struct folio *folio, struct uncharge_gather *ug) { long nr_pages; - struct mem_cgroup *memcg; struct obj_cgroup *objcg; VM_BUG_ON_FOLIO(folio_test_lru(folio), folio); /* * Nobody should be changing or seriously looking at - * folio memcg or objcg at this point, we have fully - * exclusive access to the folio. + * folio objcg at this point, we have fully exclusive + * access to the folio. */ - if (folio_memcg_kmem(folio)) { - objcg = __folio_objcg(folio); - /* - * This get matches the put at the end of the function and - * kmem pages do not hold memcg references anymore. - */ - memcg = get_mem_cgroup_from_objcg(objcg); - } else { - memcg = __folio_memcg(folio); - } - - if (!memcg) + objcg = folio_objcg(folio); + if (!objcg) return; - if (ug->memcg != memcg) { - if (ug->memcg) { + if (ug->objcg != objcg) { + if (ug->objcg) { uncharge_batch(ug); uncharge_gather_clear(ug); } - ug->memcg = memcg; + ug->objcg = objcg; ug->nid = folio_nid(folio); - /* pairs with css_put in uncharge_batch */ - css_get(&memcg->css); + /* pairs with obj_cgroup_put in uncharge_batch */ + obj_cgroup_get(objcg); } nr_pages = folio_nr_pages(folio); @@ -4967,20 +5172,17 @@ static void uncharge_folio(struct folio *folio, struct uncharge_gather *ug) if (folio_memcg_kmem(folio)) { ug->nr_memory += nr_pages; ug->nr_kmem += nr_pages; - - folio->memcg_data = 0; - obj_cgroup_put(objcg); } else { /* LRU pages aren't accounted at the root level */ - if (!mem_cgroup_is_root(memcg)) + if (!obj_cgroup_is_root(objcg)) ug->nr_memory += nr_pages; ug->pgpgout++; WARN_ON_ONCE(folio_unqueue_deferred_split(folio)); - folio->memcg_data = 0; } - css_put(&memcg->css); + folio->memcg_data = 0; + obj_cgroup_put(objcg); } void __mem_cgroup_uncharge(struct folio *folio) @@ -5004,7 +5206,7 @@ void __mem_cgroup_uncharge_folios(struct folio_batch *folios) uncharge_gather_clear(&ug); for (i = 0; i < folios->nr; i++) uncharge_folio(folios->folios[i], &ug); - if (ug.memcg) + if (ug.objcg) uncharge_batch(&ug); } @@ -5021,6 +5223,7 @@ void __mem_cgroup_uncharge_folios(struct folio_batch *folios) void mem_cgroup_replace_folio(struct folio *old, struct folio *new) { struct mem_cgroup *memcg; + struct obj_cgroup *objcg; long nr_pages = folio_nr_pages(new); VM_BUG_ON_FOLIO(!folio_test_locked(old), old); @@ -5035,21 +5238,24 @@ void mem_cgroup_replace_folio(struct folio *old, struct folio *new) if (folio_memcg_charged(new)) return; - memcg = folio_memcg(old); - VM_WARN_ON_ONCE_FOLIO(!memcg, old); - if (!memcg) + objcg = folio_objcg(old); + VM_WARN_ON_ONCE_FOLIO(!objcg, old); + if (!objcg) return; + rcu_read_lock(); + memcg = obj_cgroup_memcg(objcg); /* Force-charge the new page. The old one will be freed soon */ - if (!mem_cgroup_is_root(memcg)) { + if (!obj_cgroup_is_root(objcg)) { page_counter_charge(&memcg->memory, nr_pages); if (do_memsw_account()) page_counter_charge(&memcg->memsw, nr_pages); } - css_get(&memcg->css); - commit_charge(new, memcg); + obj_cgroup_get(objcg); + commit_charge(new, objcg); memcg1_commit_charge(new, memcg); + rcu_read_unlock(); } /** @@ -5065,7 +5271,7 @@ void mem_cgroup_replace_folio(struct folio *old, struct folio *new) */ void mem_cgroup_migrate(struct folio *old, struct folio *new) { - struct mem_cgroup *memcg; + struct obj_cgroup *objcg; VM_BUG_ON_FOLIO(!folio_test_locked(old), old); VM_BUG_ON_FOLIO(!folio_test_locked(new), new); @@ -5076,18 +5282,18 @@ void mem_cgroup_migrate(struct folio *old, struct folio *new) if (mem_cgroup_disabled()) return; - memcg = folio_memcg(old); + objcg = folio_objcg(old); /* - * Note that it is normal to see !memcg for a hugetlb folio. + * Note that it is normal to see !objcg for a hugetlb folio. * For e.g, it could have been allocated when memory_hugetlb_accounting * was not selected. */ - VM_WARN_ON_ONCE_FOLIO(!folio_test_hugetlb(old) && !memcg, old); - if (!memcg) + VM_WARN_ON_ONCE_FOLIO(!folio_test_hugetlb(old) && !objcg, old); + if (!objcg) return; - /* Transfer the charge and the css ref */ - commit_charge(new, memcg); + /* Transfer the charge and the objcg ref */ + commit_charge(new, objcg); /* Warning should never happen, so don't worry about refcount non-0 */ WARN_ON_ONCE(folio_unqueue_deferred_split(old)); @@ -5270,22 +5476,27 @@ int __mem_cgroup_try_charge_swap(struct folio *folio, swp_entry_t entry) unsigned int nr_pages = folio_nr_pages(folio); struct page_counter *counter; struct mem_cgroup *memcg; + struct obj_cgroup *objcg; if (do_memsw_account()) return 0; - memcg = folio_memcg(folio); - - VM_WARN_ON_ONCE_FOLIO(!memcg, folio); - if (!memcg) + objcg = folio_objcg(folio); + VM_WARN_ON_ONCE_FOLIO(!objcg, folio); + if (!objcg) return 0; + rcu_read_lock(); + memcg = obj_cgroup_memcg(objcg); if (!entry.val) { memcg_memory_event(memcg, MEMCG_SWAP_FAIL); + rcu_read_unlock(); return 0; } memcg = mem_cgroup_private_id_get_online(memcg, nr_pages); + /* memcg is pined by memcg ID. */ + rcu_read_unlock(); if (!mem_cgroup_is_root(memcg) && !page_counter_try_charge(&memcg->swap, nr_pages, &counter)) { @@ -5343,27 +5554,29 @@ long mem_cgroup_get_nr_swap_pages(struct mem_cgroup *memcg) bool mem_cgroup_swap_full(struct folio *folio) { struct mem_cgroup *memcg; + bool ret = false; VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio); if (vm_swap_full()) return true; - if (do_memsw_account()) - return false; + if (do_memsw_account() || !folio_memcg_charged(folio)) + return ret; + rcu_read_lock(); memcg = folio_memcg(folio); - if (!memcg) - return false; - for (; !mem_cgroup_is_root(memcg); memcg = parent_mem_cgroup(memcg)) { unsigned long usage = page_counter_read(&memcg->swap); if (usage * 2 >= READ_ONCE(memcg->swap.high) || - usage * 2 >= READ_ONCE(memcg->swap.max)) - return true; + usage * 2 >= READ_ONCE(memcg->swap.max)) { + ret = true; + break; + } } + rcu_read_unlock(); - return false; + return ret; } static int __init setup_swap_account(char *s) @@ -5559,6 +5772,9 @@ void obj_cgroup_charge_zswap(struct obj_cgroup *objcg, size_t size) if (!cgroup_subsys_on_dfl(memory_cgrp_subsys)) return; + if (obj_cgroup_is_root(objcg)) + return; + VM_WARN_ON_ONCE(!(current->flags & PF_MEMALLOC)); /* PF_MEMALLOC context, charging must succeed */ @@ -5588,6 +5804,9 @@ void obj_cgroup_uncharge_zswap(struct obj_cgroup *objcg, size_t size) if (!cgroup_subsys_on_dfl(memory_cgrp_subsys)) return; + if (obj_cgroup_is_root(objcg)) + return; + obj_cgroup_uncharge(objcg, size); rcu_read_lock(); diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c index bc7f4f045edf..59de210bee5f 100644 --- a/mm/memfd_luo.c +++ b/mm/memfd_luo.c @@ -50,6 +50,11 @@ * memfds are always opened with ``O_RDWR`` and ``O_LARGEFILE``. This property * is maintained. * + * Seals + * File seals set on the memfd are preserved and re-applied on restore. + * Only seals known to this LUO version (see ``MEMFD_LUO_ALL_SEALS``) may + * be present; preservation fails with ``-EOPNOTSUPP`` otherwise. + * * Non-Preserved Properties * ======================== * @@ -61,10 +66,6 @@ * A memfd can be created with the ``MFD_CLOEXEC`` flag that sets the * ``FD_CLOEXEC`` on the file. This flag is not preserved and must be set * again after restore via ``fcntl()``. - * - * Seals - * File seals are not preserved. The file is unsealed on restore and if - * needed, must be sealed again via ``fcntl()``. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -105,7 +106,6 @@ static int memfd_luo_preserve_folios(struct file *file, if (!size) { *nr_foliosp = 0; *out_folios_ser = NULL; - memset(kho_vmalloc, 0, sizeof(*kho_vmalloc)); return 0; } @@ -260,7 +260,7 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args) struct inode *inode = file_inode(args->file); struct memfd_luo_folio_ser *folios_ser; struct memfd_luo_ser *ser; - u64 nr_folios; + u64 nr_folios, inode_size; int err = 0, seals; inode_lock(inode); @@ -286,7 +286,18 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args) } ser->pos = args->file->f_pos; - ser->size = i_size_read(inode); + inode_size = i_size_read(inode); + + /* + * memfd_pin_folios() caps at UINT_MAX folios; refuse larger + * files to avoid silently preserving only a prefix. + */ + if (DIV_ROUND_UP_ULL(inode_size, PAGE_SIZE) > UINT_MAX) { + err = -EFBIG; + goto err_free_ser; + } + + ser->size = inode_size; ser->seals = seals; err = memfd_luo_preserve_folios(args->file, &ser->folios, @@ -410,6 +421,7 @@ static int memfd_luo_retrieve_folios(struct file *file, struct inode *inode = file_inode(file); struct address_space *mapping = inode->i_mapping; struct folio *folio; + long npages, nr_added_pages = 0; int err = -EIO; long i; @@ -427,6 +439,7 @@ static int memfd_luo_retrieve_folios(struct file *file, if (!folio) { pr_err("Unable to restore folio at physical address: %llx\n", phys); + err = -EIO; goto put_folios; } index = pfolio->index; @@ -456,21 +469,26 @@ static int memfd_luo_retrieve_folios(struct file *file, if (flags & MEMFD_LUO_FOLIO_DIRTY) folio_mark_dirty(folio); - err = shmem_inode_acct_blocks(inode, 1); + npages = folio_nr_pages(folio); + err = shmem_inode_acct_blocks(inode, npages); if (err) { - pr_err("shmem: failed to account folio index %ld: %d\n", - i, err); - goto unlock_folio; + pr_err("shmem: failed to account folio index %ld(%ld pages): %d\n", + i, npages, err); + goto remove_from_cache; } - shmem_recalc_inode(inode, 1, 0); + nr_added_pages += npages; folio_add_lru(folio); folio_unlock(folio); folio_put(folio); } + shmem_recalc_inode(inode, nr_added_pages, 0); + return 0; +remove_from_cache: + filemap_remove_folio(folio); unlock_folio: folio_unlock(folio); folio_put(folio); @@ -481,12 +499,19 @@ put_folios: */ for (long j = i + 1; j < nr_folios; j++) { const struct memfd_luo_folio_ser *pfolio = &folios_ser[j]; + phys_addr_t phys; - folio = kho_restore_folio(pfolio->pfn); + if (!pfolio->pfn) + continue; + + phys = PFN_PHYS(pfolio->pfn); + folio = kho_restore_folio(phys); if (folio) folio_put(folio); } + shmem_recalc_inode(inode, nr_added_pages, 0); + return err; } @@ -525,7 +550,7 @@ static int memfd_luo_retrieve(struct liveupdate_file_op_args *args) } vfs_setpos(file, ser->pos, MAX_LFS_FILESIZE); - file->f_inode->i_size = ser->size; + i_size_write(file_inode(file), ser->size); if (ser->nr_folios) { folios_ser = kho_restore_vmalloc(&ser->folios); @@ -560,6 +585,11 @@ static bool memfd_luo_can_preserve(struct liveupdate_file_handler *handler, return shmem_file(file) && !inode->i_nlink; } +static unsigned long memfd_luo_get_id(struct file *file) +{ + return (unsigned long)file_inode(file); +} + static const struct liveupdate_file_ops memfd_luo_file_ops = { .freeze = memfd_luo_freeze, .finish = memfd_luo_finish, @@ -567,6 +597,7 @@ static const struct liveupdate_file_ops memfd_luo_file_ops = { .preserve = memfd_luo_preserve, .unpreserve = memfd_luo_unpreserve, .can_preserve = memfd_luo_can_preserve, + .get_id = memfd_luo_get_id, .owner = THIS_MODULE, }; diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 2e136b738889..4e4421b22b59 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -3706,18 +3706,19 @@ static ssize_t weighted_interleave_auto_store(struct kobject *kobj, new_wi_state->iw_table[i] = 1; mutex_lock(&wi_state_lock); - if (!input) { - old_wi_state = rcu_dereference_protected(wi_state, - lockdep_is_held(&wi_state_lock)); - if (!old_wi_state) - goto update_wi_state; - if (input == old_wi_state->mode_auto) { - mutex_unlock(&wi_state_lock); - return count; - } + old_wi_state = rcu_dereference_protected(wi_state, + lockdep_is_held(&wi_state_lock)); - memcpy(new_wi_state->iw_table, old_wi_state->iw_table, - nr_node_ids * sizeof(u8)); + if (old_wi_state && input == old_wi_state->mode_auto) { + mutex_unlock(&wi_state_lock); + kfree(new_wi_state); + return count; + } + + if (!input) { + if (old_wi_state) + memcpy(new_wi_state->iw_table, old_wi_state->iw_table, + nr_node_ids * sizeof(u8)); goto update_wi_state; } @@ -3787,9 +3788,11 @@ static void wi_state_free(void) } } -static struct kobj_attribute wi_auto_attr = - __ATTR(auto, 0664, weighted_interleave_auto_show, - weighted_interleave_auto_store); +static struct kobj_attribute wi_auto_attr = { + .attr = { .name = "auto", .mode = 0664 }, + .show = weighted_interleave_auto_show, + .store = weighted_interleave_auto_store, +}; static void wi_cleanup(void) { sysfs_remove_file(&wi_group->wi_kobj, &wi_auto_attr.attr); diff --git a/mm/memremap.c b/mm/memremap.c index ac7be07e3361..053842d45cb1 100644 --- a/mm/memremap.c +++ b/mm/memremap.c @@ -454,7 +454,7 @@ void free_zone_device_folio(struct folio *folio) if (WARN_ON_ONCE(!pgmap->ops || !pgmap->ops->folio_free)) break; pgmap->ops->folio_free(folio); - percpu_ref_put_many(&folio->pgmap->ref, nr); + percpu_ref_put_many(&pgmap->ref, nr); break; case MEMORY_DEVICE_GENERIC: diff --git a/mm/migrate.c b/mm/migrate.c index 76142a02192b..8a64291ab5b4 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -672,6 +672,7 @@ static int __folio_migrate_mapping(struct address_space *mapping, struct lruvec *old_lruvec, *new_lruvec; struct mem_cgroup *memcg; + rcu_read_lock(); memcg = folio_memcg(folio); old_lruvec = mem_cgroup_lruvec(memcg, oldzone->zone_pgdat); new_lruvec = mem_cgroup_lruvec(memcg, newzone->zone_pgdat); @@ -699,6 +700,7 @@ static int __folio_migrate_mapping(struct address_space *mapping, mod_lruvec_state(new_lruvec, NR_FILE_DIRTY, nr); __mod_zone_page_state(newzone, NR_ZONE_WRITE_PENDING, nr); } + rcu_read_unlock(); } local_irq_enable(); diff --git a/mm/migrate_device.c b/mm/migrate_device.c index 2912eba575d5..fbfe5715f635 100644 --- a/mm/migrate_device.c +++ b/mm/migrate_device.c @@ -175,12 +175,6 @@ static int migrate_vma_collect_huge_pmd(pmd_t *pmdp, unsigned long start, return migrate_vma_collect_skip(start, end, walk); } - if (softleaf_is_migration(entry)) { - softleaf_entry_wait_on_locked(entry, ptl); - spin_unlock(ptl); - return -EAGAIN; - } - if (softleaf_is_device_private_write(entry)) write = MIGRATE_PFN_WRITE; } else { diff --git a/mm/mlock.c b/mm/mlock.c index fdbd1434a35f..8c227fefa2df 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -205,7 +205,7 @@ static void mlock_folio_batch(struct folio_batch *fbatch) } if (lruvec) - unlock_page_lruvec_irq(lruvec); + lruvec_unlock_irq(lruvec); folios_put(fbatch); } diff --git a/mm/mm_init.c b/mm/mm_init.c index 79f93f2a90cf..f9f8e1af921c 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -783,31 +783,6 @@ void __meminit init_deferred_page(unsigned long pfn, int nid) __init_deferred_page(pfn, nid); } -/* - * Initialised pages do not have PageReserved set. This function is - * called for each range allocated by the bootmem allocator and - * marks the pages PageReserved. The remaining valid pages are later - * sent to the buddy page allocator. - */ -void __meminit reserve_bootmem_region(phys_addr_t start, - phys_addr_t end, int nid) -{ - unsigned long pfn; - - for_each_valid_pfn(pfn, PFN_DOWN(start), PFN_UP(end)) { - struct page *page = pfn_to_page(pfn); - - __init_deferred_page(pfn, nid); - - /* - * no need for atomic set_bit because the struct - * page is not visible yet so nobody should - * access it yet. - */ - __SetPageReserved(page); - } -} - /* If zone is ZONE_MOVABLE but memory is mirrored, it is an overlapped init */ static bool __meminit overlap_memmap_init(unsigned long zone, unsigned long *pfn) diff --git a/mm/mprotect.c b/mm/mprotect.c index 110d47a36d4b..9cbf932b028c 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -117,9 +117,9 @@ static int mprotect_folio_pte_batch(struct folio *folio, pte_t *ptep, } /* Set nr_ptes number of ptes, starting from idx */ -static void prot_commit_flush_ptes(struct vm_area_struct *vma, unsigned long addr, - pte_t *ptep, pte_t oldpte, pte_t ptent, int nr_ptes, - int idx, bool set_write, struct mmu_gather *tlb) +static __always_inline void prot_commit_flush_ptes(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep, pte_t oldpte, pte_t ptent, + int nr_ptes, int idx, bool set_write, struct mmu_gather *tlb) { /* * Advance the position in the batch by idx; note that if idx > 0, @@ -143,7 +143,7 @@ static void prot_commit_flush_ptes(struct vm_area_struct *vma, unsigned long add * !PageAnonExclusive() pages, starting from start_idx. Caller must enforce * that the ptes point to consecutive pages of the same anon large folio. */ -static int page_anon_exclusive_sub_batch(int start_idx, int max_len, +static __always_inline int page_anon_exclusive_sub_batch(int start_idx, int max_len, struct page *first_page, bool expected_anon_exclusive) { int idx; @@ -169,7 +169,7 @@ static int page_anon_exclusive_sub_batch(int start_idx, int max_len, * pte of the batch. Therefore, we must individually check all pages and * retrieve sub-batches. */ -static void commit_anon_folio_batch(struct vm_area_struct *vma, +static __always_inline void commit_anon_folio_batch(struct vm_area_struct *vma, struct folio *folio, struct page *first_page, unsigned long addr, pte_t *ptep, pte_t oldpte, pte_t ptent, int nr_ptes, struct mmu_gather *tlb) { @@ -188,7 +188,7 @@ static void commit_anon_folio_batch(struct vm_area_struct *vma, } } -static void set_write_prot_commit_flush_ptes(struct vm_area_struct *vma, +static __always_inline void set_write_prot_commit_flush_ptes(struct vm_area_struct *vma, struct folio *folio, struct page *page, unsigned long addr, pte_t *ptep, pte_t oldpte, pte_t ptent, int nr_ptes, struct mmu_gather *tlb) { @@ -211,6 +211,111 @@ static void set_write_prot_commit_flush_ptes(struct vm_area_struct *vma, commit_anon_folio_batch(vma, folio, page, addr, ptep, oldpte, ptent, nr_ptes, tlb); } +static long change_softleaf_pte(struct vm_area_struct *vma, + unsigned long addr, pte_t *pte, pte_t oldpte, unsigned long cp_flags) +{ + const bool uffd_wp = cp_flags & MM_CP_UFFD_WP; + const bool uffd_wp_resolve = cp_flags & MM_CP_UFFD_WP_RESOLVE; + softleaf_t entry = softleaf_from_pte(oldpte); + pte_t newpte; + + if (softleaf_is_migration_write(entry)) { + const struct folio *folio = softleaf_to_folio(entry); + + /* + * A protection check is difficult so + * just be safe and disable write + */ + if (folio_test_anon(folio)) + entry = make_readable_exclusive_migration_entry(swp_offset(entry)); + else + entry = make_readable_migration_entry(swp_offset(entry)); + newpte = swp_entry_to_pte(entry); + if (pte_swp_soft_dirty(oldpte)) + newpte = pte_swp_mksoft_dirty(newpte); + } else if (softleaf_is_device_private_write(entry)) { + /* + * We do not preserve soft-dirtiness. See + * copy_nonpresent_pte() for explanation. + */ + entry = make_readable_device_private_entry(swp_offset(entry)); + newpte = swp_entry_to_pte(entry); + if (pte_swp_uffd_wp(oldpte)) + newpte = pte_swp_mkuffd_wp(newpte); + } else if (softleaf_is_marker(entry)) { + /* + * Ignore error swap entries unconditionally, + * because any access should sigbus/sigsegv + * anyway. + */ + if (softleaf_is_poison_marker(entry) || + softleaf_is_guard_marker(entry)) + return 0; + /* + * If this is uffd-wp pte marker and we'd like + * to unprotect it, drop it; the next page + * fault will trigger without uffd trapping. + */ + if (uffd_wp_resolve) { + pte_clear(vma->vm_mm, addr, pte); + return 1; + } + return 0; + } else { + newpte = oldpte; + } + + if (uffd_wp) + newpte = pte_swp_mkuffd_wp(newpte); + else if (uffd_wp_resolve) + newpte = pte_swp_clear_uffd_wp(newpte); + + if (!pte_same(oldpte, newpte)) { + set_pte_at(vma->vm_mm, addr, pte, newpte); + return 1; + } + return 0; +} + +static __always_inline void change_present_ptes(struct mmu_gather *tlb, + struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, + int nr_ptes, unsigned long end, pgprot_t newprot, + struct folio *folio, struct page *page, unsigned long cp_flags) +{ + const bool uffd_wp_resolve = cp_flags & MM_CP_UFFD_WP_RESOLVE; + const bool uffd_wp = cp_flags & MM_CP_UFFD_WP; + pte_t ptent, oldpte; + + oldpte = modify_prot_start_ptes(vma, addr, ptep, nr_ptes); + ptent = pte_modify(oldpte, newprot); + + if (uffd_wp) + ptent = pte_mkuffd_wp(ptent); + else if (uffd_wp_resolve) + ptent = pte_clear_uffd_wp(ptent); + + /* + * In some writable, shared mappings, we might want + * to catch actual write access -- see + * vma_wants_writenotify(). + * + * In all writable, private mappings, we have to + * properly handle COW. + * + * In both cases, we can sometimes still change PTEs + * writable and avoid the write-fault handler, for + * example, if a PTE is already dirty and no other + * COW or special handling is required. + */ + if ((cp_flags & MM_CP_TRY_CHANGE_WRITABLE) && + !pte_write(ptent)) + set_write_prot_commit_flush_ptes(vma, folio, page, + addr, ptep, oldpte, ptent, nr_ptes, tlb); + else + prot_commit_flush_ptes(vma, addr, ptep, oldpte, ptent, + nr_ptes, /* idx = */ 0, /* set_write = */ false, tlb); +} + static long change_pte_range(struct mmu_gather *tlb, struct vm_area_struct *vma, pmd_t *pmd, unsigned long addr, unsigned long end, pgprot_t newprot, unsigned long cp_flags) @@ -221,7 +326,6 @@ static long change_pte_range(struct mmu_gather *tlb, bool is_private_single_threaded; bool prot_numa = cp_flags & MM_CP_PROT_NUMA; bool uffd_wp = cp_flags & MM_CP_UFFD_WP; - bool uffd_wp_resolve = cp_flags & MM_CP_UFFD_WP_RESOLVE; int nr_ptes; tlb_change_page_size(tlb, PAGE_SIZE); @@ -242,7 +346,6 @@ static long change_pte_range(struct mmu_gather *tlb, int max_nr_ptes = (end - addr) >> PAGE_SHIFT; struct folio *folio = NULL; struct page *page; - pte_t ptent; /* Already in the desired state. */ if (prot_numa && pte_protnone(oldpte)) @@ -268,34 +371,20 @@ static long change_pte_range(struct mmu_gather *tlb, nr_ptes = mprotect_folio_pte_batch(folio, pte, oldpte, max_nr_ptes, flags); - oldpte = modify_prot_start_ptes(vma, addr, pte, nr_ptes); - ptent = pte_modify(oldpte, newprot); - - if (uffd_wp) - ptent = pte_mkuffd_wp(ptent); - else if (uffd_wp_resolve) - ptent = pte_clear_uffd_wp(ptent); - /* - * In some writable, shared mappings, we might want - * to catch actual write access -- see - * vma_wants_writenotify(). - * - * In all writable, private mappings, we have to - * properly handle COW. - * - * In both cases, we can sometimes still change PTEs - * writable and avoid the write-fault handler, for - * example, if a PTE is already dirty and no other - * COW or special handling is required. + * Optimize for the small-folio common case by + * special-casing it here. Compiler constant propagation + * plus copious amounts of __always_inline does wonders. */ - if ((cp_flags & MM_CP_TRY_CHANGE_WRITABLE) && - !pte_write(ptent)) - set_write_prot_commit_flush_ptes(vma, folio, page, - addr, pte, oldpte, ptent, nr_ptes, tlb); - else - prot_commit_flush_ptes(vma, addr, pte, oldpte, ptent, - nr_ptes, /* idx = */ 0, /* set_write = */ false, tlb); + if (likely(nr_ptes == 1)) { + change_present_ptes(tlb, vma, addr, pte, 1, + end, newprot, folio, page, cp_flags); + } else { + change_present_ptes(tlb, vma, addr, pte, + nr_ptes, end, newprot, folio, page, + cp_flags); + } + pages += nr_ptes; } else if (pte_none(oldpte)) { /* @@ -317,66 +406,7 @@ static long change_pte_range(struct mmu_gather *tlb, pages++; } } else { - softleaf_t entry = softleaf_from_pte(oldpte); - pte_t newpte; - - if (softleaf_is_migration_write(entry)) { - const struct folio *folio = softleaf_to_folio(entry); - - /* - * A protection check is difficult so - * just be safe and disable write - */ - if (folio_test_anon(folio)) - entry = make_readable_exclusive_migration_entry( - swp_offset(entry)); - else - entry = make_readable_migration_entry(swp_offset(entry)); - newpte = swp_entry_to_pte(entry); - if (pte_swp_soft_dirty(oldpte)) - newpte = pte_swp_mksoft_dirty(newpte); - } else if (softleaf_is_device_private_write(entry)) { - /* - * We do not preserve soft-dirtiness. See - * copy_nonpresent_pte() for explanation. - */ - entry = make_readable_device_private_entry( - swp_offset(entry)); - newpte = swp_entry_to_pte(entry); - if (pte_swp_uffd_wp(oldpte)) - newpte = pte_swp_mkuffd_wp(newpte); - } else if (softleaf_is_marker(entry)) { - /* - * Ignore error swap entries unconditionally, - * because any access should sigbus/sigsegv - * anyway. - */ - if (softleaf_is_poison_marker(entry) || - softleaf_is_guard_marker(entry)) - continue; - /* - * If this is uffd-wp pte marker and we'd like - * to unprotect it, drop it; the next page - * fault will trigger without uffd trapping. - */ - if (uffd_wp_resolve) { - pte_clear(vma->vm_mm, addr, pte); - pages++; - } - continue; - } else { - newpte = oldpte; - } - - if (uffd_wp) - newpte = pte_swp_mkuffd_wp(newpte); - else if (uffd_wp_resolve) - newpte = pte_swp_clear_uffd_wp(newpte); - - if (!pte_same(oldpte, newpte)) { - set_pte_at(vma->vm_mm, addr, pte, newpte); - pages++; - } + pages += change_softleaf_pte(vma, addr, pte, oldpte, cp_flags); } } while (pte += nr_ptes, addr += nr_ptes * PAGE_SIZE, addr != end); lazy_mmu_mode_disable(); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 88cd53d4ba09..833f743f309f 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -1835,7 +1835,9 @@ static int balance_dirty_pages(struct bdi_writeback *wb, balance_domain_limits(mdtc, strictlimit); } - if (nr_dirty > gdtc->bg_thresh && !writeback_in_progress(wb)) + if (!writeback_in_progress(wb) && + (nr_dirty > gdtc->bg_thresh || + (strictlimit && gdtc->wb_dirty > gdtc->wb_bg_thresh))) wb_start_background_writeback(wb); /* @@ -1862,15 +1864,9 @@ free_running: * Unconditionally start background writeback if it's not * already in progress. We need to do this because the global * dirty threshold check above (nr_dirty > gdtc->bg_thresh) - * doesn't account for these cases: - * - * a) strictlimit BDIs: throttling is calculated using per-wb - * thresholds. The per-wb threshold can be exceeded even when - * nr_dirty < gdtc->bg_thresh - * - * b) memcg-based throttling: memcg uses its own dirty count and - * thresholds and can trigger throttling even when global - * nr_dirty < gdtc->bg_thresh + * doesn't account for the memcg-based throttling case. memcg + * uses its own dirty count and thresholds and can trigger + * throttling even when global nr_dirty < gdtc->bg_thresh * * Writeback needs to be started else the writer stalls in the * throttle loop waiting for dirty pages to be written back diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 111b54df8a3c..227d58dc3de6 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -297,11 +297,6 @@ int page_group_by_mobility_disabled __read_mostly; */ DEFINE_STATIC_KEY_TRUE(deferred_pages); -static inline bool deferred_pages_enabled(void) -{ - return static_branch_unlikely(&deferred_pages); -} - /* * deferred_grow_zone() is __init, but it is called from * get_page_from_freelist() during early boot until deferred_pages permanently @@ -314,11 +309,6 @@ _deferred_grow_zone(struct zone *zone, unsigned int order) return deferred_grow_zone(zone, order); } #else -static inline bool deferred_pages_enabled(void) -{ - return false; -} - static inline bool _deferred_grow_zone(struct zone *zone, unsigned int order) { return false; @@ -1252,10 +1242,18 @@ void __pgalloc_tag_add(struct page *page, struct task_struct *task, union pgtag_ref_handle handle; union codetag_ref ref; - if (get_page_tag_ref(page, &ref, &handle)) { + if (likely(get_page_tag_ref(page, &ref, &handle))) { alloc_tag_add(&ref, task->alloc_tag, PAGE_SIZE * nr); update_page_tag_ref(handle, &ref); put_page_tag_ref(handle); + } else { + /* + * page_ext is not available yet, record the pfn so we can + * clear the tag ref later when page_ext is initialized. + */ + alloc_tag_add_early_pfn(page_to_pfn(page)); + if (task->alloc_tag) + alloc_tag_set_inaccurate(task->alloc_tag); } } @@ -6211,42 +6209,6 @@ void adjust_managed_page_count(struct page *page, long count) } EXPORT_SYMBOL(adjust_managed_page_count); -unsigned long free_reserved_area(void *start, void *end, int poison, const char *s) -{ - void *pos; - unsigned long pages = 0; - - start = (void *)PAGE_ALIGN((unsigned long)start); - end = (void *)((unsigned long)end & PAGE_MASK); - for (pos = start; pos < end; pos += PAGE_SIZE, pages++) { - struct page *page = virt_to_page(pos); - void *direct_map_addr; - - /* - * 'direct_map_addr' might be different from 'pos' - * because some architectures' virt_to_page() - * work with aliases. Getting the direct map - * address ensures that we get a _writeable_ - * alias for the memset(). - */ - direct_map_addr = page_address(page); - /* - * Perform a kasan-unchecked memset() since this memory - * has not been initialized. - */ - direct_map_addr = kasan_reset_tag(direct_map_addr); - if ((unsigned int)poison <= 0xFF) - memset(direct_map_addr, poison, PAGE_SIZE); - - free_reserved_page(page); - } - - if (pages && s) - pr_info("Freeing %s memory: %ldK\n", s, K(pages)); - - return pages; -} - void free_reserved_page(struct page *page) { clear_page_tag_ref(page); @@ -7775,6 +7737,11 @@ struct page *alloc_frozen_pages_nolock_noprof(gfp_t gfp_flags, int nid, unsigned */ if (IS_ENABLED(CONFIG_PREEMPT_RT) && (in_nmi() || in_hardirq())) return NULL; + + /* On UP, spin_trylock() always succeeds even when it is locked */ + if (!IS_ENABLED(CONFIG_SMP) && in_nmi()) + return NULL; + if (!pcp_allowed_order(order)) return NULL; diff --git a/mm/page_io.c b/mm/page_io.c index 330abc5ab7b4..70cea9e24d2f 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -276,10 +276,14 @@ int swap_writeout(struct folio *folio, struct swap_iocb **swap_plug) count_mthp_stat(folio_order(folio), MTHP_STAT_ZSWPOUT); goto out_unlock; } + + rcu_read_lock(); if (!mem_cgroup_zswap_writeback_enabled(folio_memcg(folio))) { + rcu_read_unlock(); folio_mark_dirty(folio); return AOP_WRITEPAGE_ACTIVATE; } + rcu_read_unlock(); __swap_writepage(folio, swap_plug); return 0; @@ -307,11 +311,11 @@ static void bio_associate_blkg_from_page(struct bio *bio, struct folio *folio) struct cgroup_subsys_state *css; struct mem_cgroup *memcg; - memcg = folio_memcg(folio); - if (!memcg) + if (!folio_memcg_charged(folio)) return; rcu_read_lock(); + memcg = folio_memcg(folio); css = cgroup_e_css(memcg->css.cgroup, &io_cgrp_subsys); bio_associate_blkg_from_css(bio, css); rcu_read_unlock(); @@ -493,7 +497,7 @@ static void sio_read_complete(struct kiocb *iocb, long ret) folio_mark_uptodate(folio); folio_unlock(folio); } - count_vm_events(PSWPIN, sio->pages); + count_vm_events(PSWPIN, sio->len >> PAGE_SHIFT); } else { for (p = 0; p < sio->pages; p++) { struct folio *folio = page_folio(sio->bvec[p].bv_page); diff --git a/mm/page_table_check.c b/mm/page_table_check.c index 2708c2b3ac1f..53a8997ec043 100644 --- a/mm/page_table_check.c +++ b/mm/page_table_check.c @@ -151,9 +151,8 @@ void __page_table_check_pte_clear(struct mm_struct *mm, unsigned long addr, if (&init_mm == mm) return; - if (pte_user_accessible_page(pte, addr)) { + if (pte_user_accessible_page(mm, addr, pte)) page_table_check_clear(pte_pfn(pte), PAGE_SIZE >> PAGE_SHIFT); - } } EXPORT_SYMBOL(__page_table_check_pte_clear); @@ -163,9 +162,8 @@ void __page_table_check_pmd_clear(struct mm_struct *mm, unsigned long addr, if (&init_mm == mm) return; - if (pmd_user_accessible_page(pmd, addr)) { + if (pmd_user_accessible_page(mm, addr, pmd)) page_table_check_clear(pmd_pfn(pmd), PMD_SIZE >> PAGE_SHIFT); - } } EXPORT_SYMBOL(__page_table_check_pmd_clear); @@ -175,9 +173,8 @@ void __page_table_check_pud_clear(struct mm_struct *mm, unsigned long addr, if (&init_mm == mm) return; - if (pud_user_accessible_page(pud, addr)) { + if (pud_user_accessible_page(mm, addr, pud)) page_table_check_clear(pud_pfn(pud), PUD_SIZE >> PAGE_SHIFT); - } } EXPORT_SYMBOL(__page_table_check_pud_clear); @@ -211,7 +208,7 @@ void __page_table_check_ptes_set(struct mm_struct *mm, unsigned long addr, for (i = 0; i < nr; i++) __page_table_check_pte_clear(mm, addr + PAGE_SIZE * i, ptep_get(ptep + i)); - if (pte_user_accessible_page(pte, addr)) + if (pte_user_accessible_page(mm, addr, pte)) page_table_check_set(pte_pfn(pte), nr, pte_write(pte)); } EXPORT_SYMBOL(__page_table_check_ptes_set); @@ -241,7 +238,7 @@ void __page_table_check_pmds_set(struct mm_struct *mm, unsigned long addr, for (i = 0; i < nr; i++) __page_table_check_pmd_clear(mm, addr + PMD_SIZE * i, *(pmdp + i)); - if (pmd_user_accessible_page(pmd, addr)) + if (pmd_user_accessible_page(mm, addr, pmd)) page_table_check_set(pmd_pfn(pmd), stride * nr, pmd_write(pmd)); } EXPORT_SYMBOL(__page_table_check_pmds_set); @@ -257,7 +254,7 @@ void __page_table_check_puds_set(struct mm_struct *mm, unsigned long addr, for (i = 0; i < nr; i++) __page_table_check_pud_clear(mm, addr + PUD_SIZE * i, *(pudp + i)); - if (pud_user_accessible_page(pud, addr)) + if (pud_user_accessible_page(mm, addr, pud)) page_table_check_set(pud_pfn(pud), stride * nr, pud_write(pud)); } EXPORT_SYMBOL(__page_table_check_puds_set); diff --git a/mm/percpu.c b/mm/percpu.c index a2107bdebf0b..b0676b8054ed 100644 --- a/mm/percpu.c +++ b/mm/percpu.c @@ -1622,7 +1622,7 @@ static bool pcpu_memcg_pre_alloc_hook(size_t size, gfp_t gfp, return true; objcg = current_obj_cgroup(); - if (!objcg) + if (!objcg || obj_cgroup_is_root(objcg)) return true; if (obj_cgroup_charge(objcg, gfp, pcpu_obj_full_size(size))) diff --git a/mm/shmem.c b/mm/shmem.c index 19bf77925fa1..3b5dc21b323c 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3177,119 +3177,99 @@ static struct inode *shmem_get_inode(struct mnt_idmap *idmap, #endif /* CONFIG_TMPFS_QUOTA */ #ifdef CONFIG_USERFAULTFD -int shmem_mfill_atomic_pte(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, - unsigned long src_addr, - uffd_flags_t flags, - struct folio **foliop) +static struct folio *shmem_mfill_folio_alloc(struct vm_area_struct *vma, + unsigned long addr) { - struct inode *inode = file_inode(dst_vma->vm_file); - struct shmem_inode_info *info = SHMEM_I(inode); + struct inode *inode = file_inode(vma->vm_file); struct address_space *mapping = inode->i_mapping; + struct shmem_inode_info *info = SHMEM_I(inode); + pgoff_t pgoff = linear_page_index(vma, addr); gfp_t gfp = mapping_gfp_mask(mapping); - pgoff_t pgoff = linear_page_index(dst_vma, dst_addr); - void *page_kaddr; struct folio *folio; - int ret; - pgoff_t max_off; - if (shmem_inode_acct_blocks(inode, 1)) { - /* - * We may have got a page, returned -ENOENT triggering a retry, - * and now we find ourselves with -ENOMEM. Release the page, to - * avoid a BUG_ON in our caller. - */ - if (unlikely(*foliop)) { - folio_put(*foliop); - *foliop = NULL; - } - return -ENOMEM; + if (unlikely(pgoff >= DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE))) + return NULL; + + folio = shmem_alloc_folio(gfp, 0, info, pgoff); + if (!folio) + return NULL; + + if (mem_cgroup_charge(folio, vma->vm_mm, GFP_KERNEL)) { + folio_put(folio); + return NULL; } - if (!*foliop) { - ret = -ENOMEM; - folio = shmem_alloc_folio(gfp, 0, info, pgoff); - if (!folio) - goto out_unacct_blocks; + return folio; +} - if (uffd_flags_mode_is(flags, MFILL_ATOMIC_COPY)) { - page_kaddr = kmap_local_folio(folio, 0); - /* - * The read mmap_lock is held here. Despite the - * mmap_lock being read recursive a deadlock is still - * possible if a writer has taken a lock. For example: - * - * process A thread 1 takes read lock on own mmap_lock - * process A thread 2 calls mmap, blocks taking write lock - * process B thread 1 takes page fault, read lock on own mmap lock - * process B thread 2 calls mmap, blocks taking write lock - * process A thread 1 blocks taking read lock on process B - * process B thread 1 blocks taking read lock on process A - * - * Disable page faults to prevent potential deadlock - * and retry the copy outside the mmap_lock. - */ - pagefault_disable(); - ret = copy_from_user(page_kaddr, - (const void __user *)src_addr, - PAGE_SIZE); - pagefault_enable(); - kunmap_local(page_kaddr); +static int shmem_mfill_filemap_add(struct folio *folio, + struct vm_area_struct *vma, + unsigned long addr) +{ + struct inode *inode = file_inode(vma->vm_file); + struct address_space *mapping = inode->i_mapping; + pgoff_t pgoff = linear_page_index(vma, addr); + gfp_t gfp = mapping_gfp_mask(mapping); + int err; - /* fallback to copy_from_user outside mmap_lock */ - if (unlikely(ret)) { - *foliop = folio; - ret = -ENOENT; - /* don't free the page */ - goto out_unacct_blocks; - } - - flush_dcache_folio(folio); - } else { /* ZEROPAGE */ - clear_user_highpage(&folio->page, dst_addr); - } - } else { - folio = *foliop; - VM_BUG_ON_FOLIO(folio_test_large(folio), folio); - *foliop = NULL; - } - - VM_BUG_ON(folio_test_locked(folio)); - VM_BUG_ON(folio_test_swapbacked(folio)); __folio_set_locked(folio); __folio_set_swapbacked(folio); - __folio_mark_uptodate(folio); - ret = -EFAULT; - max_off = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); - if (unlikely(pgoff >= max_off)) - goto out_release; + err = shmem_add_to_page_cache(folio, mapping, pgoff, NULL, gfp); + if (err) + goto err_unlock; - ret = mem_cgroup_charge(folio, dst_vma->vm_mm, gfp); - if (ret) - goto out_release; - ret = shmem_add_to_page_cache(folio, mapping, pgoff, NULL, gfp); - if (ret) - goto out_release; - - ret = mfill_atomic_install_pte(dst_pmd, dst_vma, dst_addr, - &folio->page, true, flags); - if (ret) - goto out_delete_from_cache; + if (shmem_inode_acct_blocks(inode, 1)) { + err = -ENOMEM; + goto err_delete_from_cache; + } + folio_add_lru(folio); shmem_recalc_inode(inode, 1, 0); - folio_unlock(folio); + return 0; -out_delete_from_cache: + +err_delete_from_cache: filemap_remove_folio(folio); -out_release: +err_unlock: folio_unlock(folio); - folio_put(folio); -out_unacct_blocks: - shmem_inode_unacct_blocks(inode, 1); - return ret; + return err; } + +static void shmem_mfill_filemap_remove(struct folio *folio, + struct vm_area_struct *vma) +{ + struct inode *inode = file_inode(vma->vm_file); + + filemap_remove_folio(folio); + shmem_recalc_inode(inode, 0, 0); + folio_unlock(folio); +} + +static struct folio *shmem_get_folio_noalloc(struct inode *inode, pgoff_t pgoff) +{ + struct folio *folio; + int err; + + err = shmem_get_folio(inode, pgoff, 0, &folio, SGP_NOALLOC); + if (err) + return ERR_PTR(err); + + return folio; +} + +static bool shmem_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags) +{ + return true; +} + +static const struct vm_uffd_ops shmem_uffd_ops = { + .can_userfault = shmem_can_userfault, + .get_folio_noalloc = shmem_get_folio_noalloc, + .alloc_folio = shmem_mfill_folio_alloc, + .filemap_add = shmem_mfill_filemap_add, + .filemap_remove = shmem_mfill_filemap_remove, +}; #endif /* CONFIG_USERFAULTFD */ #ifdef CONFIG_TMPFS @@ -5325,6 +5305,9 @@ static const struct vm_operations_struct shmem_vm_ops = { .set_policy = shmem_set_policy, .get_policy = shmem_get_policy, #endif +#ifdef CONFIG_USERFAULTFD + .uffd_ops = &shmem_uffd_ops, +#endif }; static const struct vm_operations_struct shmem_anon_vm_ops = { @@ -5334,6 +5317,9 @@ static const struct vm_operations_struct shmem_anon_vm_ops = { .set_policy = shmem_set_policy, .get_policy = shmem_get_policy, #endif +#ifdef CONFIG_USERFAULTFD + .uffd_ops = &shmem_uffd_ops, +#endif }; int shmem_init_fs_context(struct fs_context *fc) diff --git a/mm/shrinker.c b/mm/shrinker.c index c23086bccf4d..76b3f750cf65 100644 --- a/mm/shrinker.c +++ b/mm/shrinker.c @@ -288,14 +288,10 @@ void reparent_shrinker_deferred(struct mem_cgroup *memcg) { int nid, index, offset; long nr; - struct mem_cgroup *parent; + struct mem_cgroup *parent = parent_mem_cgroup(memcg); struct shrinker_info *child_info, *parent_info; struct shrinker_info_unit *child_unit, *parent_unit; - parent = parent_mem_cgroup(memcg); - if (!parent) - parent = root_mem_cgroup; - /* Prevent from concurrent shrinker_info expand */ mutex_lock(&shrinker_mutex); for_each_node(nid) { diff --git a/mm/slub.c b/mm/slub.c index 92362eeb13e5..8f9004536729 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5339,6 +5339,10 @@ void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node) if (IS_ENABLED(CONFIG_PREEMPT_RT) && (in_nmi() || in_hardirq())) return NULL; + /* On UP, spin_trylock() always succeeds even when it is locked */ + if (!IS_ENABLED(CONFIG_SMP) && in_nmi()) + return NULL; + retry: if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) return NULL; @@ -6645,16 +6649,6 @@ __do_krealloc(const void *p, size_t new_size, unsigned long align, gfp_t flags, if (!kasan_check_byte(p)) return NULL; - /* - * If reallocation is not necessary (e. g. the new size is less - * than the current allocated size), the current allocation will be - * preserved unless __GFP_THISNODE is set. In the latter case a new - * allocation on the requested node will be attempted. - */ - if (unlikely(flags & __GFP_THISNODE) && nid != NUMA_NO_NODE && - nid != page_to_nid(virt_to_page(p))) - goto alloc_new; - if (is_kfence_address(p)) { ks = orig_size = kfence_ksize(p); } else { @@ -6673,6 +6667,16 @@ __do_krealloc(const void *p, size_t new_size, unsigned long align, gfp_t flags, } } + /* + * If reallocation is not necessary (e. g. the new size is less + * than the current allocated size), the current allocation will be + * preserved unless __GFP_THISNODE is set. In the latter case a new + * allocation on the requested node will be attempted. + */ + if (unlikely(flags & __GFP_THISNODE) && nid != NUMA_NO_NODE && + nid != page_to_nid(virt_to_page(p))) + goto alloc_new; + /* If the old object doesn't fit, allocate a bigger one */ if (new_size > ks) goto alloc_new; @@ -6707,7 +6711,7 @@ alloc_new: if (ret && p) { /* Disable KASAN checks as the object's redzone is accessed. */ kasan_disable_current(); - memcpy(ret, kasan_reset_tag(p), orig_size ?: ks); + memcpy(ret, kasan_reset_tag(p), min(new_size, (size_t)(orig_size ?: ks))); kasan_enable_current(); } @@ -6878,6 +6882,22 @@ void kvfree(const void *addr) } EXPORT_SYMBOL(kvfree); +/** + * kvfree_atomic() - Free memory. + * @addr: Pointer to allocated memory. + * + * Same as kvfree(), but uses vfree_atomic() for vmalloc + * backed memory. Must not be called from NMI context. + */ +void kvfree_atomic(const void *addr) +{ + if (is_vmalloc_addr(addr)) + vfree_atomic(addr); + else + kfree(addr); +} +EXPORT_SYMBOL(kvfree_atomic); + /** * kvfree_sensitive - Free a data object containing sensitive information. * @addr: address of the data object to be freed. @@ -6941,7 +6961,7 @@ void *kvrealloc_node_align_noprof(const void *p, size_t size, unsigned long alig if (p) { /* We already know that `p` is not a vmalloc address. */ kasan_disable_current(); - memcpy(n, kasan_reset_tag(p), ksize(p)); + memcpy(n, kasan_reset_tag(p), min(size, ksize(p))); kasan_enable_current(); kfree(p); diff --git a/mm/sparse.c b/mm/sparse.c index 007fd52c621e..effdac6b0ab1 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -403,7 +403,6 @@ failed: ms = __nr_to_section(pnum); if (!preinited_vmemmap_section(ms)) ms->section_mem_map = 0; - ms->section_mem_map = 0; } } diff --git a/mm/swap.c b/mm/swap.c index 78b4aa811fc6..5cc44f0de987 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -91,7 +91,7 @@ static void page_cache_release(struct folio *folio) __page_cache_release(folio, &lruvec, &flags); if (lruvec) - unlock_page_lruvec_irqrestore(lruvec, flags); + lruvec_unlock_irqrestore(lruvec, flags); } void __folio_put(struct folio *folio) @@ -175,7 +175,7 @@ static void folio_batch_move_lru(struct folio_batch *fbatch, move_fn_t move_fn) } if (lruvec) - unlock_page_lruvec_irqrestore(lruvec, flags); + lruvec_unlock_irqrestore(lruvec, flags); folios_put(fbatch); } @@ -240,6 +240,7 @@ void folio_rotate_reclaimable(struct folio *folio) void lru_note_cost_unlock_irq(struct lruvec *lruvec, bool file, unsigned int nr_io, unsigned int nr_rotated) __releases(lruvec->lru_lock) + __releases(rcu) { unsigned long cost; @@ -253,6 +254,7 @@ void lru_note_cost_unlock_irq(struct lruvec *lruvec, bool file, cost = nr_io * SWAP_CLUSTER_MAX + nr_rotated; if (!cost) { spin_unlock_irq(&lruvec->lru_lock); + rcu_read_unlock(); return; } @@ -285,8 +287,10 @@ void lru_note_cost_unlock_irq(struct lruvec *lruvec, bool file, spin_unlock_irq(&lruvec->lru_lock); lruvec = parent_lruvec(lruvec); - if (!lruvec) + if (!lruvec) { + rcu_read_unlock(); break; + } spin_lock_irq(&lruvec->lru_lock); } } @@ -349,7 +353,7 @@ void folio_activate(struct folio *folio) lruvec = folio_lruvec_lock_irq(folio); lru_activate(lruvec, folio); - unlock_page_lruvec_irq(lruvec); + lruvec_unlock_irq(lruvec); folio_set_lru(folio); } #endif @@ -412,18 +416,20 @@ static void lru_gen_inc_refs(struct folio *folio) static bool lru_gen_clear_refs(struct folio *folio) { - struct lru_gen_folio *lrugen; int gen = folio_lru_gen(folio); int type = folio_is_file_lru(folio); + unsigned long seq; if (gen < 0) return true; set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS | BIT(PG_workingset), 0); - lrugen = &folio_lruvec(folio)->lrugen; + rcu_read_lock(); + seq = READ_ONCE(folio_lruvec(folio)->lrugen.min_seq[type]); + rcu_read_unlock(); /* whether can do without shuffling under the LRU lock */ - return gen == lru_gen_from_seq(READ_ONCE(lrugen->min_seq[type])); + return gen == lru_gen_from_seq(seq); } #else /* !CONFIG_LRU_GEN */ @@ -963,7 +969,7 @@ void folios_put_refs(struct folio_batch *folios, unsigned int *refs) if (folio_is_zone_device(folio)) { if (lruvec) { - unlock_page_lruvec_irqrestore(lruvec, flags); + lruvec_unlock_irqrestore(lruvec, flags); lruvec = NULL; } if (folio_ref_sub_and_test(folio, nr_refs)) @@ -977,7 +983,7 @@ void folios_put_refs(struct folio_batch *folios, unsigned int *refs) /* hugetlb has its own memcg */ if (folio_test_hugetlb(folio)) { if (lruvec) { - unlock_page_lruvec_irqrestore(lruvec, flags); + lruvec_unlock_irqrestore(lruvec, flags); lruvec = NULL; } free_huge_folio(folio); @@ -991,7 +997,7 @@ void folios_put_refs(struct folio_batch *folios, unsigned int *refs) j++; } if (lruvec) - unlock_page_lruvec_irqrestore(lruvec, flags); + lruvec_unlock_irqrestore(lruvec, flags); if (!j) { folio_batch_reinit(folios); return; @@ -1084,6 +1090,39 @@ void folio_batch_remove_exceptionals(struct folio_batch *fbatch) fbatch->nr = j; } +#ifdef CONFIG_MEMCG +static void lruvec_reparent_lru(struct lruvec *child_lruvec, + struct lruvec *parent_lruvec, + enum lru_list lru, int nid) +{ + int zid; + struct zone *zone; + + if (lru != LRU_UNEVICTABLE) + list_splice_tail_init(&child_lruvec->lists[lru], &parent_lruvec->lists[lru]); + + for_each_managed_zone_pgdat(zone, NODE_DATA(nid), zid, MAX_NR_ZONES - 1) { + unsigned long size = mem_cgroup_get_zone_lru_size(child_lruvec, lru, zid); + + mem_cgroup_update_lru_size(parent_lruvec, lru, zid, size); + } +} + +void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid) +{ + enum lru_list lru; + struct lruvec *child_lruvec, *parent_lruvec; + + child_lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); + parent_lruvec = mem_cgroup_lruvec(parent, NODE_DATA(nid)); + parent_lruvec->anon_cost += child_lruvec->anon_cost; + parent_lruvec->file_cost += child_lruvec->file_cost; + + for_each_lru(lru) + lruvec_reparent_lru(child_lruvec, parent_lruvec, lru, nid); +} +#endif + static const struct ctl_table swap_sysctl_table[] = { { .procname = "page-cluster", diff --git a/mm/truncate.c b/mm/truncate.c index 2931d66c16d0..12cc89f89afc 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -622,6 +622,7 @@ static int folio_launder(struct address_space *mapping, struct folio *folio) int folio_unmap_invalidate(struct address_space *mapping, struct folio *folio, gfp_t gfp) { + void (*free_folio)(struct folio *); int ret; VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio); @@ -648,9 +649,12 @@ int folio_unmap_invalidate(struct address_space *mapping, struct folio *folio, xa_unlock_irq(&mapping->i_pages); if (mapping_shrinkable(mapping)) inode_lru_list_add(mapping->host); + free_folio = mapping->a_ops->free_folio; spin_unlock(&mapping->host->i_lock); - filemap_free_folio(mapping, folio); + if (free_folio) + free_folio(folio); + folio_put_refs(folio, folio_nr_pages(folio)); return 1; failed: xa_unlock_irq(&mapping->i_pages); diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 89879c3ba344..180bad42fc79 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -14,12 +14,61 @@ #include #include #include -#include #include #include #include "internal.h" #include "swap.h" +struct mfill_state { + struct userfaultfd_ctx *ctx; + unsigned long src_start; + unsigned long dst_start; + unsigned long len; + uffd_flags_t flags; + + struct vm_area_struct *vma; + unsigned long src_addr; + unsigned long dst_addr; + pmd_t *pmd; +}; + +static bool anon_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags) +{ + /* anonymous memory does not support MINOR mode */ + if (vm_flags & VM_UFFD_MINOR) + return false; + return true; +} + +static struct folio *anon_alloc_folio(struct vm_area_struct *vma, + unsigned long addr) +{ + struct folio *folio = vma_alloc_folio(GFP_HIGHUSER_MOVABLE, 0, vma, + addr); + + if (!folio) + return NULL; + + if (mem_cgroup_charge(folio, vma->vm_mm, GFP_KERNEL)) { + folio_put(folio); + return NULL; + } + + return folio; +} + +static const struct vm_uffd_ops anon_uffd_ops = { + .can_userfault = anon_can_userfault, + .alloc_folio = anon_alloc_folio, +}; + +static const struct vm_uffd_ops *vma_uffd_ops(struct vm_area_struct *vma) +{ + if (vma_is_anonymous(vma)) + return &anon_uffd_ops; + return vma->vm_ops ? vma->vm_ops->uffd_ops : NULL; +} + static __always_inline bool validate_dst_vma(struct vm_area_struct *dst_vma, unsigned long dst_end) { @@ -143,6 +192,128 @@ static void uffd_mfill_unlock(struct vm_area_struct *vma) } #endif +static void mfill_put_vma(struct mfill_state *state) +{ + if (!state->vma) + return; + + up_read(&state->ctx->map_changing_lock); + uffd_mfill_unlock(state->vma); + state->vma = NULL; +} + +static int mfill_get_vma(struct mfill_state *state) +{ + struct userfaultfd_ctx *ctx = state->ctx; + uffd_flags_t flags = state->flags; + struct vm_area_struct *dst_vma; + const struct vm_uffd_ops *ops; + int err; + + /* + * Make sure the vma is not shared, that the dst range is + * both valid and fully within a single existing vma. + */ + dst_vma = uffd_mfill_lock(ctx->mm, state->dst_start, state->len); + if (IS_ERR(dst_vma)) + return PTR_ERR(dst_vma); + + /* + * If memory mappings are changing because of non-cooperative + * operation (e.g. mremap) running in parallel, bail out and + * request the user to retry later + */ + down_read(&ctx->map_changing_lock); + state->vma = dst_vma; + err = -EAGAIN; + if (atomic_read(&ctx->mmap_changing)) + goto out_unlock; + + err = -EINVAL; + + /* + * shmem_zero_setup is invoked in mmap for MAP_ANONYMOUS|MAP_SHARED but + * it will overwrite vm_ops, so vma_is_anonymous must return false. + */ + if (WARN_ON_ONCE(vma_is_anonymous(dst_vma) && + dst_vma->vm_flags & VM_SHARED)) + goto out_unlock; + + /* + * validate 'mode' now that we know the dst_vma: don't allow + * a wrprotect copy if the userfaultfd didn't register as WP. + */ + if ((flags & MFILL_ATOMIC_WP) && !(dst_vma->vm_flags & VM_UFFD_WP)) + goto out_unlock; + + if (is_vm_hugetlb_page(dst_vma)) + return 0; + + ops = vma_uffd_ops(dst_vma); + if (!ops) + goto out_unlock; + + if (uffd_flags_mode_is(flags, MFILL_ATOMIC_CONTINUE) && + !ops->get_folio_noalloc) + goto out_unlock; + + return 0; + +out_unlock: + mfill_put_vma(state); + return err; +} + +static pmd_t *mm_alloc_pmd(struct mm_struct *mm, unsigned long address) +{ + pgd_t *pgd; + p4d_t *p4d; + pud_t *pud; + + pgd = pgd_offset(mm, address); + p4d = p4d_alloc(mm, pgd, address); + if (!p4d) + return NULL; + pud = pud_alloc(mm, p4d, address); + if (!pud) + return NULL; + /* + * Note that we didn't run this because the pmd was + * missing, the *pmd may be already established and in + * turn it may also be a trans_huge_pmd. + */ + return pmd_alloc(mm, pud, address); +} + +static int mfill_establish_pmd(struct mfill_state *state) +{ + struct mm_struct *dst_mm = state->ctx->mm; + pmd_t *dst_pmd, dst_pmdval; + + dst_pmd = mm_alloc_pmd(dst_mm, state->dst_addr); + if (unlikely(!dst_pmd)) + return -ENOMEM; + + dst_pmdval = pmdp_get_lockless(dst_pmd); + if (unlikely(pmd_none(dst_pmdval)) && + unlikely(__pte_alloc(dst_mm, dst_pmd))) + return -ENOMEM; + + dst_pmdval = pmdp_get_lockless(dst_pmd); + /* + * If the dst_pmd is THP don't override it and just be strict. + * (This includes the case where the PMD used to be THP and + * changed back to none after __pte_alloc().) + */ + if (unlikely(!pmd_present(dst_pmdval) || pmd_leaf(dst_pmdval))) + return -EEXIST; + if (unlikely(pmd_bad(dst_pmdval))) + return -EFAULT; + + state->pmd = dst_pmd; + return 0; +} + /* Check if dst_addr is outside of file's size. Must be called with ptl held. */ static bool mfill_file_over_size(struct vm_area_struct *dst_vma, unsigned long dst_addr) @@ -165,10 +336,10 @@ static bool mfill_file_over_size(struct vm_area_struct *dst_vma, * This function handles both MCOPY_ATOMIC_NORMAL and _CONTINUE for both shmem * and anon, and for both shared and private VMAs. */ -int mfill_atomic_install_pte(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, struct page *page, - bool newly_allocated, uffd_flags_t flags) +static int mfill_atomic_install_pte(pmd_t *dst_pmd, + struct vm_area_struct *dst_vma, + unsigned long dst_addr, struct page *page, + uffd_flags_t flags) { int ret; struct mm_struct *dst_mm = dst_vma->vm_mm; @@ -212,9 +383,6 @@ int mfill_atomic_install_pte(pmd_t *dst_pmd, goto out_unlock; if (page_in_cache) { - /* Usually, cache pages are already added to LRU */ - if (newly_allocated) - folio_add_lru(folio); folio_add_file_rmap_pte(folio, page, dst_vma); } else { folio_add_new_anon_rmap(folio, dst_vma, dst_addr, RMAP_EXCLUSIVE); @@ -229,6 +397,9 @@ int mfill_atomic_install_pte(pmd_t *dst_pmd, set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte); + if (page_in_cache) + folio_unlock(folio); + /* No need to invalidate - it was non-present before */ update_mmu_cache(dst_vma, dst_addr, dst_pte); ret = 0; @@ -238,58 +409,110 @@ out: return ret; } -static int mfill_atomic_pte_copy(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, - unsigned long src_addr, - uffd_flags_t flags, - struct folio **foliop) +static int mfill_copy_folio_locked(struct folio *folio, unsigned long src_addr) { void *kaddr; int ret; + + kaddr = kmap_local_folio(folio, 0); + /* + * The read mmap_lock is held here. Despite the + * mmap_lock being read recursive a deadlock is still + * possible if a writer has taken a lock. For example: + * + * process A thread 1 takes read lock on own mmap_lock + * process A thread 2 calls mmap, blocks taking write lock + * process B thread 1 takes page fault, read lock on own mmap lock + * process B thread 2 calls mmap, blocks taking write lock + * process A thread 1 blocks taking read lock on process B + * process B thread 1 blocks taking read lock on process A + * + * Disable page faults to prevent potential deadlock + * and retry the copy outside the mmap_lock. + */ + pagefault_disable(); + ret = copy_from_user(kaddr, (const void __user *) src_addr, + PAGE_SIZE); + pagefault_enable(); + kunmap_local(kaddr); + + if (ret) + return -EFAULT; + + flush_dcache_folio(folio); + return ret; +} + +static int mfill_copy_folio_retry(struct mfill_state *state, + struct folio *folio) +{ + const struct vm_uffd_ops *orig_ops = vma_uffd_ops(state->vma); + unsigned long src_addr = state->src_addr; + void *kaddr; + int err; + + /* retry copying with mm_lock dropped */ + mfill_put_vma(state); + + kaddr = kmap_local_folio(folio, 0); + err = copy_from_user(kaddr, (const void __user *) src_addr, PAGE_SIZE); + kunmap_local(kaddr); + if (unlikely(err)) + return -EFAULT; + + flush_dcache_folio(folio); + + /* reget VMA and PMD, they could change underneath us */ + err = mfill_get_vma(state); + if (err) + return err; + + /* + * The VMA type may have changed while the lock was dropped + * (e.g. replaced with a hugetlb mapping), making the caller's + * ops pointer stale. + */ + if (vma_uffd_ops(state->vma) != orig_ops) + return -EAGAIN; + + err = mfill_establish_pmd(state); + if (err) + return err; + + return 0; +} + +static int __mfill_atomic_pte(struct mfill_state *state, + const struct vm_uffd_ops *ops) +{ + unsigned long dst_addr = state->dst_addr; + unsigned long src_addr = state->src_addr; + uffd_flags_t flags = state->flags; struct folio *folio; + int ret; - if (!*foliop) { - ret = -ENOMEM; - folio = vma_alloc_folio(GFP_HIGHUSER_MOVABLE, 0, dst_vma, - dst_addr); - if (!folio) - goto out; + folio = ops->alloc_folio(state->vma, state->dst_addr); + if (!folio) + return -ENOMEM; - kaddr = kmap_local_folio(folio, 0); + if (uffd_flags_mode_is(flags, MFILL_ATOMIC_COPY)) { + ret = mfill_copy_folio_locked(folio, src_addr); /* - * The read mmap_lock is held here. Despite the - * mmap_lock being read recursive a deadlock is still - * possible if a writer has taken a lock. For example: - * - * process A thread 1 takes read lock on own mmap_lock - * process A thread 2 calls mmap, blocks taking write lock - * process B thread 1 takes page fault, read lock on own mmap lock - * process B thread 2 calls mmap, blocks taking write lock - * process A thread 1 blocks taking read lock on process B - * process B thread 1 blocks taking read lock on process A - * - * Disable page faults to prevent potential deadlock - * and retry the copy outside the mmap_lock. + * Fallback to copy_from_user outside mmap_lock. + * If retry is successful, mfill_copy_folio_locked() returns + * with locks retaken by mfill_get_vma(). + * If there was an error, we must mfill_put_vma() anyway and it + * will take care of unlocking if needed. */ - pagefault_disable(); - ret = copy_from_user(kaddr, (const void __user *) src_addr, - PAGE_SIZE); - pagefault_enable(); - kunmap_local(kaddr); - - /* fallback to copy_from_user outside mmap_lock */ if (unlikely(ret)) { - ret = -ENOENT; - *foliop = folio; - /* don't free the page */ - goto out; + ret = mfill_copy_folio_retry(state, folio); + if (ret) + goto err_folio_put; } - - flush_dcache_folio(folio); + } else if (uffd_flags_mode_is(flags, MFILL_ATOMIC_ZEROPAGE)) { + clear_user_highpage(&folio->page, state->dst_addr); } else { - folio = *foliop; - *foliop = NULL; + VM_WARN_ONCE(1, "Unknown UFFDIO operation, flags: %x", flags); } /* @@ -299,63 +522,65 @@ static int mfill_atomic_pte_copy(pmd_t *dst_pmd, */ __folio_mark_uptodate(folio); - ret = -ENOMEM; - if (mem_cgroup_charge(folio, dst_vma->vm_mm, GFP_KERNEL)) - goto out_release; + if (ops->filemap_add) { + ret = ops->filemap_add(folio, state->vma, state->dst_addr); + if (ret) + goto err_folio_put; + } - ret = mfill_atomic_install_pte(dst_pmd, dst_vma, dst_addr, - &folio->page, true, flags); + ret = mfill_atomic_install_pte(state->pmd, state->vma, dst_addr, + &folio->page, flags); if (ret) - goto out_release; -out: - return ret; -out_release: - folio_put(folio); - goto out; -} - -static int mfill_atomic_pte_zeroed_folio(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr) -{ - struct folio *folio; - int ret = -ENOMEM; - - folio = vma_alloc_zeroed_movable_folio(dst_vma, dst_addr); - if (!folio) - return ret; - - if (mem_cgroup_charge(folio, dst_vma->vm_mm, GFP_KERNEL)) - goto out_put; - - /* - * The memory barrier inside __folio_mark_uptodate makes sure that - * zeroing out the folio become visible before mapping the page - * using set_pte_at(). See do_anonymous_page(). - */ - __folio_mark_uptodate(folio); - - ret = mfill_atomic_install_pte(dst_pmd, dst_vma, dst_addr, - &folio->page, true, 0); - if (ret) - goto out_put; + goto err_filemap_remove; return 0; -out_put: + +err_filemap_remove: + if (ops->filemap_remove) + ops->filemap_remove(folio, state->vma); +err_folio_put: folio_put(folio); return ret; } -static int mfill_atomic_pte_zeropage(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr) +static int mfill_atomic_pte_copy(struct mfill_state *state) { + const struct vm_uffd_ops *ops = vma_uffd_ops(state->vma); + + /* + * The normal page fault path for a MAP_PRIVATE mapping in a + * file-backed VMA will invoke the fault, fill the hole in the file and + * COW it right away. The result generates plain anonymous memory. + * So when we are asked to fill a hole in a MAP_PRIVATE mapping, we'll + * generate anonymous memory directly without actually filling the + * hole. For the MAP_PRIVATE case the robustness check only happens in + * the pagetable (to verify it's still none) and not in the page cache. + */ + if (!(state->vma->vm_flags & VM_SHARED)) + ops = &anon_uffd_ops; + + return __mfill_atomic_pte(state, ops); +} + +static int mfill_atomic_pte_zeroed_folio(struct mfill_state *state) +{ + const struct vm_uffd_ops *ops = vma_uffd_ops(state->vma); + + return __mfill_atomic_pte(state, ops); +} + +static int mfill_atomic_pte_zeropage(struct mfill_state *state) +{ + struct vm_area_struct *dst_vma = state->vma; + unsigned long dst_addr = state->dst_addr; + pmd_t *dst_pmd = state->pmd; pte_t _dst_pte, *dst_pte; spinlock_t *ptl; int ret; - if (mm_forbids_zeropage(dst_vma->vm_mm)) - return mfill_atomic_pte_zeroed_folio(dst_pmd, dst_vma, dst_addr); + if (mm_forbids_zeropage(dst_vma->vm_mm) || + (dst_vma->vm_flags & VM_SHARED)) + return mfill_atomic_pte_zeroed_folio(state); _dst_pte = pte_mkspecial(pfn_pte(zero_pfn(dst_addr), dst_vma->vm_page_prot)); @@ -381,28 +606,29 @@ out: } /* Handles UFFDIO_CONTINUE for all shmem VMAs (shared or private). */ -static int mfill_atomic_pte_continue(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, - uffd_flags_t flags) +static int mfill_atomic_pte_continue(struct mfill_state *state) { - struct inode *inode = file_inode(dst_vma->vm_file); + struct vm_area_struct *dst_vma = state->vma; + const struct vm_uffd_ops *ops = vma_uffd_ops(dst_vma); + unsigned long dst_addr = state->dst_addr; pgoff_t pgoff = linear_page_index(dst_vma, dst_addr); + struct inode *inode = file_inode(dst_vma->vm_file); + uffd_flags_t flags = state->flags; + pmd_t *dst_pmd = state->pmd; struct folio *folio; struct page *page; int ret; - ret = shmem_get_folio(inode, pgoff, 0, &folio, SGP_NOALLOC); - /* Our caller expects us to return -EFAULT if we failed to find folio */ - if (ret == -ENOENT) - ret = -EFAULT; - if (ret) - goto out; - if (!folio) { - ret = -EFAULT; - goto out; + if (!ops) { + VM_WARN_ONCE(1, "UFFDIO_CONTINUE for unsupported VMA"); + return -EOPNOTSUPP; } + folio = ops->get_folio_noalloc(inode, pgoff); + /* Our caller expects us to return -EFAULT if we failed to find folio */ + if (IS_ERR_OR_NULL(folio)) + return -EFAULT; + page = folio_file_page(folio, pgoff); if (PageHWPoison(page)) { ret = -EIO; @@ -410,30 +636,28 @@ static int mfill_atomic_pte_continue(pmd_t *dst_pmd, } ret = mfill_atomic_install_pte(dst_pmd, dst_vma, dst_addr, - page, false, flags); + page, flags); if (ret) goto out_release; - folio_unlock(folio); - ret = 0; -out: - return ret; + return 0; + out_release: folio_unlock(folio); folio_put(folio); - goto out; + return ret; } /* Handles UFFDIO_POISON for all non-hugetlb VMAs. */ -static int mfill_atomic_pte_poison(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, - uffd_flags_t flags) +static int mfill_atomic_pte_poison(struct mfill_state *state) { - int ret; + struct vm_area_struct *dst_vma = state->vma; struct mm_struct *dst_mm = dst_vma->vm_mm; + unsigned long dst_addr = state->dst_addr; + pmd_t *dst_pmd = state->pmd; pte_t _dst_pte, *dst_pte; spinlock_t *ptl; + int ret; _dst_pte = make_pte_marker(PTE_MARKER_POISONED); ret = -EAGAIN; @@ -462,27 +686,6 @@ out: return ret; } -static pmd_t *mm_alloc_pmd(struct mm_struct *mm, unsigned long address) -{ - pgd_t *pgd; - p4d_t *p4d; - pud_t *pud; - - pgd = pgd_offset(mm, address); - p4d = p4d_alloc(mm, pgd, address); - if (!p4d) - return NULL; - pud = pud_alloc(mm, p4d, address); - if (!pud) - return NULL; - /* - * Note that we didn't run this because the pmd was - * missing, the *pmd may be already established and in - * turn it may also be a trans_huge_pmd. - */ - return pmd_alloc(mm, pud, address); -} - #ifdef CONFIG_HUGETLB_PAGE /* * mfill_atomic processing for HUGETLB vmas. Note that this routine is @@ -657,48 +860,21 @@ extern ssize_t mfill_atomic_hugetlb(struct userfaultfd_ctx *ctx, uffd_flags_t flags); #endif /* CONFIG_HUGETLB_PAGE */ -static __always_inline ssize_t mfill_atomic_pte(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, - unsigned long src_addr, - uffd_flags_t flags, - struct folio **foliop) +static __always_inline ssize_t mfill_atomic_pte(struct mfill_state *state) { - ssize_t err; + uffd_flags_t flags = state->flags; - if (uffd_flags_mode_is(flags, MFILL_ATOMIC_CONTINUE)) { - return mfill_atomic_pte_continue(dst_pmd, dst_vma, - dst_addr, flags); - } else if (uffd_flags_mode_is(flags, MFILL_ATOMIC_POISON)) { - return mfill_atomic_pte_poison(dst_pmd, dst_vma, - dst_addr, flags); - } + if (uffd_flags_mode_is(flags, MFILL_ATOMIC_CONTINUE)) + return mfill_atomic_pte_continue(state); + if (uffd_flags_mode_is(flags, MFILL_ATOMIC_POISON)) + return mfill_atomic_pte_poison(state); + if (uffd_flags_mode_is(flags, MFILL_ATOMIC_COPY)) + return mfill_atomic_pte_copy(state); + if (uffd_flags_mode_is(flags, MFILL_ATOMIC_ZEROPAGE)) + return mfill_atomic_pte_zeropage(state); - /* - * The normal page fault path for a shmem will invoke the - * fault, fill the hole in the file and COW it right away. The - * result generates plain anonymous memory. So when we are - * asked to fill an hole in a MAP_PRIVATE shmem mapping, we'll - * generate anonymous memory directly without actually filling - * the hole. For the MAP_PRIVATE case the robustness check - * only happens in the pagetable (to verify it's still none) - * and not in the radix tree. - */ - if (!(dst_vma->vm_flags & VM_SHARED)) { - if (uffd_flags_mode_is(flags, MFILL_ATOMIC_COPY)) - err = mfill_atomic_pte_copy(dst_pmd, dst_vma, - dst_addr, src_addr, - flags, foliop); - else - err = mfill_atomic_pte_zeropage(dst_pmd, - dst_vma, dst_addr); - } else { - err = shmem_mfill_atomic_pte(dst_pmd, dst_vma, - dst_addr, src_addr, - flags, foliop); - } - - return err; + VM_WARN_ONCE(1, "Unknown UFFDIO operation, flags: %x", flags); + return -EOPNOTSUPP; } static __always_inline ssize_t mfill_atomic(struct userfaultfd_ctx *ctx, @@ -707,13 +883,17 @@ static __always_inline ssize_t mfill_atomic(struct userfaultfd_ctx *ctx, unsigned long len, uffd_flags_t flags) { - struct mm_struct *dst_mm = ctx->mm; - struct vm_area_struct *dst_vma; + struct mfill_state state = (struct mfill_state){ + .ctx = ctx, + .dst_start = dst_start, + .src_start = src_start, + .flags = flags, + .len = len, + .src_addr = src_start, + .dst_addr = dst_start, + }; + long copied = 0; ssize_t err; - pmd_t *dst_pmd; - unsigned long src_addr, dst_addr; - long copied; - struct folio *folio; /* * Sanitize the command parameters: @@ -725,125 +905,35 @@ static __always_inline ssize_t mfill_atomic(struct userfaultfd_ctx *ctx, VM_WARN_ON_ONCE(src_start + len <= src_start); VM_WARN_ON_ONCE(dst_start + len <= dst_start); - src_addr = src_start; - dst_addr = dst_start; - copied = 0; - folio = NULL; -retry: - /* - * Make sure the vma is not shared, that the dst range is - * both valid and fully within a single existing vma. - */ - dst_vma = uffd_mfill_lock(dst_mm, dst_start, len); - if (IS_ERR(dst_vma)) { - err = PTR_ERR(dst_vma); + err = mfill_get_vma(&state); + if (err) goto out; - } - - /* - * If memory mappings are changing because of non-cooperative - * operation (e.g. mremap) running in parallel, bail out and - * request the user to retry later - */ - down_read(&ctx->map_changing_lock); - err = -EAGAIN; - if (atomic_read(&ctx->mmap_changing)) - goto out_unlock; - - err = -EINVAL; - /* - * shmem_zero_setup is invoked in mmap for MAP_ANONYMOUS|MAP_SHARED but - * it will overwrite vm_ops, so vma_is_anonymous must return false. - */ - if (WARN_ON_ONCE(vma_is_anonymous(dst_vma) && - dst_vma->vm_flags & VM_SHARED)) - goto out_unlock; - - /* - * validate 'mode' now that we know the dst_vma: don't allow - * a wrprotect copy if the userfaultfd didn't register as WP. - */ - if ((flags & MFILL_ATOMIC_WP) && !(dst_vma->vm_flags & VM_UFFD_WP)) - goto out_unlock; /* * If this is a HUGETLB vma, pass off to appropriate routine */ - if (is_vm_hugetlb_page(dst_vma)) - return mfill_atomic_hugetlb(ctx, dst_vma, dst_start, + if (is_vm_hugetlb_page(state.vma)) + return mfill_atomic_hugetlb(ctx, state.vma, dst_start, src_start, len, flags); - if (!vma_is_anonymous(dst_vma) && !vma_is_shmem(dst_vma)) - goto out_unlock; - if (!vma_is_shmem(dst_vma) && - uffd_flags_mode_is(flags, MFILL_ATOMIC_CONTINUE)) - goto out_unlock; + while (state.src_addr < src_start + len) { + VM_WARN_ON_ONCE(state.dst_addr >= dst_start + len); - while (src_addr < src_start + len) { - pmd_t dst_pmdval; + err = mfill_establish_pmd(&state); + if (err) + break; - VM_WARN_ON_ONCE(dst_addr >= dst_start + len); - - dst_pmd = mm_alloc_pmd(dst_mm, dst_addr); - if (unlikely(!dst_pmd)) { - err = -ENOMEM; - break; - } - - dst_pmdval = pmdp_get_lockless(dst_pmd); - if (unlikely(pmd_none(dst_pmdval)) && - unlikely(__pte_alloc(dst_mm, dst_pmd))) { - err = -ENOMEM; - break; - } - dst_pmdval = pmdp_get_lockless(dst_pmd); - /* - * If the dst_pmd is THP don't override it and just be strict. - * (This includes the case where the PMD used to be THP and - * changed back to none after __pte_alloc().) - */ - if (unlikely(!pmd_present(dst_pmdval) || - pmd_trans_huge(dst_pmdval))) { - err = -EEXIST; - break; - } - if (unlikely(pmd_bad(dst_pmdval))) { - err = -EFAULT; - break; - } /* * For shmem mappings, khugepaged is allowed to remove page * tables under us; pte_offset_map_lock() will deal with that. */ - err = mfill_atomic_pte(dst_pmd, dst_vma, dst_addr, - src_addr, flags, &folio); + err = mfill_atomic_pte(&state); cond_resched(); - if (unlikely(err == -ENOENT)) { - void *kaddr; - - up_read(&ctx->map_changing_lock); - uffd_mfill_unlock(dst_vma); - VM_WARN_ON_ONCE(!folio); - - kaddr = kmap_local_folio(folio, 0); - err = copy_from_user(kaddr, - (const void __user *) src_addr, - PAGE_SIZE); - kunmap_local(kaddr); - if (unlikely(err)) { - err = -EFAULT; - goto out; - } - flush_dcache_folio(folio); - goto retry; - } else - VM_WARN_ON_ONCE(folio); - if (!err) { - dst_addr += PAGE_SIZE; - src_addr += PAGE_SIZE; + state.dst_addr += PAGE_SIZE; + state.src_addr += PAGE_SIZE; copied += PAGE_SIZE; if (fatal_signal_pending(current)) @@ -853,12 +943,8 @@ retry: break; } -out_unlock: - up_read(&ctx->map_changing_lock); - uffd_mfill_unlock(dst_vma); + mfill_put_vma(&state); out: - if (folio) - folio_put(folio); VM_WARN_ON_ONCE(copied < 0); VM_WARN_ON_ONCE(err > 0); VM_WARN_ON_ONCE(!copied && !err); @@ -1938,6 +2024,38 @@ out: return moved ? moved : err; } +bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags, + bool wp_async) +{ + const struct vm_uffd_ops *ops = vma_uffd_ops(vma); + + if (vma->vm_flags & VM_DROPPABLE) + return false; + + vm_flags &= __VM_UFFD_FLAGS; + + /* + * If WP is the only mode enabled and context is wp async, allow any + * memory type. + */ + if (wp_async && (vm_flags == VM_UFFD_WP)) + return true; + + /* For any other mode reject VMAs that don't implement vm_uffd_ops */ + if (!ops) + return false; + + /* + * If user requested uffd-wp but not enabled pte markers for + * uffd-wp, then only anonymous memory is supported + */ + if (!uffd_supports_wp_marker() && (vm_flags & VM_UFFD_WP) && + !vma_is_anonymous(vma)) + return false; + + return ops->can_userfault(vma, vm_flags); +} + static void userfaultfd_set_vm_flags(struct vm_area_struct *vma, vm_flags_t vm_flags) { diff --git a/mm/util.c b/mm/util.c index f063fd4de1e8..3cc949a0b7ed 100644 --- a/mm/util.c +++ b/mm/util.c @@ -1232,7 +1232,7 @@ int __compat_vma_mmap(struct vm_area_desc *desc, /* Update the VMA from the descriptor. */ compat_set_vma_from_desc(vma, desc); /* Complete any specified mmap actions. */ - return mmap_action_complete(vma, &desc->action); + return mmap_action_complete(vma, &desc->action, /*is_compat=*/true); } EXPORT_SYMBOL(__compat_vma_mmap); @@ -1281,16 +1281,6 @@ int compat_vma_mmap(struct file *file, struct vm_area_struct *vma) } EXPORT_SYMBOL(compat_vma_mmap); -int __vma_check_mmap_hook(struct vm_area_struct *vma) -{ - /* vm_ops->mapped is not valid if mmap() is specified. */ - if (vma->vm_ops && WARN_ON_ONCE(vma->vm_ops->mapped)) - return -EINVAL; - - return 0; -} -EXPORT_SYMBOL(__vma_check_mmap_hook); - static void set_ps_flags(struct page_snapshot *ps, const struct folio *folio, const struct page *page) { @@ -1399,7 +1389,8 @@ static int call_vma_mapped(struct vm_area_struct *vma) } static int mmap_action_finish(struct vm_area_struct *vma, - struct mmap_action *action, int err) + struct mmap_action *action, int err, + bool is_compat) { size_t len; @@ -1410,8 +1401,12 @@ static int mmap_action_finish(struct vm_area_struct *vma, /* do_munmap() might take rmap lock, so release if held. */ maybe_rmap_unlock_action(vma, action); - if (!err) - return 0; + /* + * If this is invoked from the compatibility layer, post-mmap() hook + * logic will handle cleanup for us. + */ + if (!err || is_compat) + return err; /* * If an error occurs, unmap the VMA altogether and return an error. We @@ -1461,13 +1456,15 @@ EXPORT_SYMBOL(mmap_action_prepare); * mmap_action_complete - Execute VMA descriptor action. * @vma: The VMA to perform the action upon. * @action: The action to perform. + * @is_compat: Is this being invoked from the compatibility layer? * * Similar to mmap_action_prepare(). * - * Return: 0 on success, or error, at which point the VMA will be unmapped. + * Return: 0 on success, or error, at which point the VMA will be unmapped if + * !@is_compat. */ int mmap_action_complete(struct vm_area_struct *vma, - struct mmap_action *action) + struct mmap_action *action, bool is_compat) { int err = 0; @@ -1488,7 +1485,7 @@ int mmap_action_complete(struct vm_area_struct *vma, break; } - return mmap_action_finish(vma, action, err); + return mmap_action_finish(vma, action, err, is_compat); } EXPORT_SYMBOL(mmap_action_complete); #else @@ -1510,7 +1507,8 @@ int mmap_action_prepare(struct vm_area_desc *desc) EXPORT_SYMBOL(mmap_action_prepare); int mmap_action_complete(struct vm_area_struct *vma, - struct mmap_action *action) + struct mmap_action *action, + bool is_compat) { int err = 0; @@ -1527,7 +1525,7 @@ int mmap_action_complete(struct vm_area_struct *vma, break; } - return mmap_action_finish(vma, action, err); + return mmap_action_finish(vma, action, err, is_compat); } EXPORT_SYMBOL(mmap_action_complete); #endif diff --git a/mm/vma.c b/mm/vma.c index 377321b48734..d90791b00a7b 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -2780,7 +2780,8 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr, __mmap_complete(&map, vma); if (have_mmap_prepare && allocated_new) { - error = mmap_action_complete(vma, &desc.action); + error = mmap_action_complete(vma, &desc.action, + /*is_compat=*/false); if (error) return error; } diff --git a/mm/vmalloc.c b/mm/vmalloc.c index b31b208f6ecb..c31a8615a832 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -4361,7 +4361,7 @@ need_realloc: return NULL; if (p) { - memcpy(n, p, old_size); + memcpy(n, p, min(size, old_size)); vfree(p); } @@ -5416,6 +5416,7 @@ vmap_node_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) { struct vmap_node *vn; + guard(mutex)(&vmap_purge_lock); for_each_vmap_node(vn) decay_va_pool_node(vn, true); diff --git a/mm/vmscan.c b/mm/vmscan.c index 4bf091b1c8af..bd1b1aa12581 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -269,25 +269,6 @@ static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg) } #endif -/* for_each_managed_zone_pgdat - helper macro to iterate over all managed zones in a pgdat up to - * and including the specified highidx - * @zone: The current zone in the iterator - * @pgdat: The pgdat which node_zones are being iterated - * @idx: The index variable - * @highidx: The index of the highest zone to return - * - * This macro iterates through all managed zones up to and including the specified highidx. - * The zone iterator enters an invalid state after macro call and must be reinitialized - * before it can be used again. - */ -#define for_each_managed_zone_pgdat(zone, pgdat, idx, highidx) \ - for ((idx) = 0, (zone) = (pgdat)->node_zones; \ - (idx) <= (highidx); \ - (idx)++, (zone)++) \ - if (!managed_zone(zone)) \ - continue; \ - else - static void set_task_reclaim_state(struct task_struct *task, struct reclaim_state *rs) { @@ -409,8 +390,7 @@ unsigned long zone_reclaimable_pages(struct zone *zone) * @lru: lru to use * @zone_idx: zones to consider (use MAX_NR_ZONES - 1 for the whole LRU list) */ -static unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru, - int zone_idx) +unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru, int zone_idx) { unsigned long size = 0; int zid; @@ -1831,7 +1811,7 @@ bool folio_isolate_lru(struct folio *folio) folio_get(folio); lruvec = folio_lruvec_lock_irq(folio); lruvec_del_folio(lruvec, folio); - unlock_page_lruvec_irq(lruvec); + lruvec_unlock_irq(lruvec); ret = true; } @@ -1885,24 +1865,27 @@ static bool too_many_isolated(struct pglist_data *pgdat, int file, /* * move_folios_to_lru() moves folios from private @list to appropriate LRU list. * - * Returns the number of pages moved to the given lruvec. + * Returns the number of pages moved to the appropriate lruvec. + * + * Note: The caller must not hold any lruvec lock. */ -static unsigned int move_folios_to_lru(struct lruvec *lruvec, - struct list_head *list) +static unsigned int move_folios_to_lru(struct list_head *list) { int nr_pages, nr_moved = 0; + struct lruvec *lruvec = NULL; struct folio_batch free_folios; folio_batch_init(&free_folios); while (!list_empty(list)) { struct folio *folio = lru_to_folio(list); + lruvec = folio_lruvec_relock_irq(folio, lruvec); VM_BUG_ON_FOLIO(folio_test_lru(folio), folio); list_del(&folio->lru); if (unlikely(!folio_evictable(folio))) { - spin_unlock_irq(&lruvec->lru_lock); + lruvec_unlock_irq(lruvec); folio_putback_lru(folio); - spin_lock_irq(&lruvec->lru_lock); + lruvec = NULL; continue; } @@ -1924,20 +1907,15 @@ static unsigned int move_folios_to_lru(struct lruvec *lruvec, folio_unqueue_deferred_split(folio); if (folio_batch_add(&free_folios, folio) == 0) { - spin_unlock_irq(&lruvec->lru_lock); + lruvec_unlock_irq(lruvec); mem_cgroup_uncharge_folios(&free_folios); free_unref_folios(&free_folios); - spin_lock_irq(&lruvec->lru_lock); + lruvec = NULL; } continue; } - /* - * All pages were isolated from the same lruvec (and isolation - * inhibits memcg migration). - */ - VM_BUG_ON_FOLIO(!folio_matches_lruvec(folio, lruvec), folio); lruvec_add_folio(lruvec, folio); nr_pages = folio_nr_pages(folio); nr_moved += nr_pages; @@ -1945,11 +1923,12 @@ static unsigned int move_folios_to_lru(struct lruvec *lruvec, workingset_age_nonresident(lruvec, nr_pages); } + if (lruvec) + lruvec_unlock_irq(lruvec); + if (free_folios.nr) { - spin_unlock_irq(&lruvec->lru_lock); mem_cgroup_uncharge_folios(&free_folios); free_unref_folios(&free_folios); - spin_lock_irq(&lruvec->lru_lock); } return nr_moved; @@ -1998,7 +1977,7 @@ static unsigned long shrink_inactive_list(unsigned long nr_to_scan, lru_add_drain(); - spin_lock_irq(&lruvec->lru_lock); + lruvec_lock_irq(lruvec); nr_taken = isolate_lru_folios(nr_to_scan, lruvec, &folio_list, &nr_scanned, sc, lru); @@ -2008,7 +1987,7 @@ static unsigned long shrink_inactive_list(unsigned long nr_to_scan, mod_lruvec_state(lruvec, item, nr_scanned); mod_lruvec_state(lruvec, PGSCAN_ANON + file, nr_scanned); - spin_unlock_irq(&lruvec->lru_lock); + lruvec_unlock_irq(lruvec); if (nr_taken == 0) return 0; @@ -2016,16 +1995,16 @@ static unsigned long shrink_inactive_list(unsigned long nr_to_scan, nr_reclaimed = shrink_folio_list(&folio_list, pgdat, sc, &stat, false, lruvec_memcg(lruvec)); - spin_lock_irq(&lruvec->lru_lock); - move_folios_to_lru(lruvec, &folio_list); + move_folios_to_lru(&folio_list); mod_lruvec_state(lruvec, PGDEMOTE_KSWAPD + reclaimer_offset(sc), stat.nr_demoted); - __mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, -nr_taken); + mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, -nr_taken); item = PGSTEAL_KSWAPD + reclaimer_offset(sc); mod_lruvec_state(lruvec, item, nr_reclaimed); mod_lruvec_state(lruvec, PGSTEAL_ANON + file, nr_reclaimed); + lruvec_lock_irq(lruvec); lru_note_cost_unlock_irq(lruvec, file, stat.nr_pageout, nr_scanned - nr_reclaimed); @@ -2104,7 +2083,7 @@ static void shrink_active_list(unsigned long nr_to_scan, lru_add_drain(); - spin_lock_irq(&lruvec->lru_lock); + lruvec_lock_irq(lruvec); nr_taken = isolate_lru_folios(nr_to_scan, lruvec, &l_hold, &nr_scanned, sc, lru); @@ -2113,7 +2092,7 @@ static void shrink_active_list(unsigned long nr_to_scan, mod_lruvec_state(lruvec, PGREFILL, nr_scanned); - spin_unlock_irq(&lruvec->lru_lock); + lruvec_unlock_irq(lruvec); while (!list_empty(&l_hold)) { struct folio *folio; @@ -2162,16 +2141,14 @@ static void shrink_active_list(unsigned long nr_to_scan, /* * Move folios back to the lru list. */ - spin_lock_irq(&lruvec->lru_lock); + nr_activate = move_folios_to_lru(&l_active); + nr_deactivate = move_folios_to_lru(&l_inactive); - nr_activate = move_folios_to_lru(lruvec, &l_active); - nr_deactivate = move_folios_to_lru(lruvec, &l_inactive); - - __count_vm_events(PGDEACTIVATE, nr_deactivate); + count_vm_events(PGDEACTIVATE, nr_deactivate); count_memcg_events(lruvec_memcg(lruvec), PGDEACTIVATE, nr_deactivate); + mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, -nr_taken); - __mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, -nr_taken); - + lruvec_lock_irq(lruvec); lru_note_cost_unlock_irq(lruvec, file, 0, nr_rotated); trace_mm_vmscan_lru_shrink_active(pgdat->node_id, nr_taken, nr_activate, nr_deactivate, nr_rotated, sc->priority, file); @@ -2886,8 +2863,9 @@ static struct mm_struct *get_next_mm(struct lru_gen_mm_walk *walk) return NULL; clear_bit(key, &mm->lru_gen.bitmap); + mmgrab(mm); - return mmget_not_zero(mm) ? mm : NULL; + return mm; } void lru_gen_add_mm(struct mm_struct *mm) @@ -3087,7 +3065,7 @@ done: reset_bloom_filter(mm_state, walk->seq + 1); if (*iter) - mmput_async(*iter); + mmdrop(*iter); *iter = mm; @@ -3442,8 +3420,10 @@ static struct folio *get_pfn_folio(unsigned long pfn, struct mem_cgroup *memcg, if (folio_nid(folio) != pgdat->node_id) return NULL; + rcu_read_lock(); if (folio_memcg(folio) != memcg) - return NULL; + folio = NULL; + rcu_read_unlock(); return folio; } @@ -3803,9 +3783,9 @@ static void walk_mm(struct mm_struct *mm, struct lru_gen_mm_walk *walk) } if (walk->batched) { - spin_lock_irq(&lruvec->lru_lock); + lruvec_lock_irq(lruvec); reset_batch_size(walk); - spin_unlock_irq(&lruvec->lru_lock); + lruvec_unlock_irq(lruvec); } cond_resched(); @@ -3965,7 +3945,7 @@ restart: if (seq < READ_ONCE(lrugen->max_seq)) return false; - spin_lock_irq(&lruvec->lru_lock); + lruvec_lock_irq(lruvec); VM_WARN_ON_ONCE(!seq_is_valid(lruvec)); @@ -3980,7 +3960,7 @@ restart: if (inc_min_seq(lruvec, type, swappiness)) continue; - spin_unlock_irq(&lruvec->lru_lock); + lruvec_unlock_irq(lruvec); cond_resched(); goto restart; } @@ -4015,7 +3995,7 @@ restart: /* make sure preceding modifications appear */ smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1); unlock: - spin_unlock_irq(&lruvec->lru_lock); + lruvec_unlock_irq(lruvec); return success; } @@ -4213,12 +4193,12 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr) unsigned long addr = pvmw->address; struct vm_area_struct *vma = pvmw->vma; struct folio *folio = pfn_folio(pvmw->pfn); - struct mem_cgroup *memcg = folio_memcg(folio); + struct mem_cgroup *memcg; struct pglist_data *pgdat = folio_pgdat(folio); - struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat); - struct lru_gen_mm_state *mm_state = get_mm_state(lruvec); - DEFINE_MAX_SEQ(lruvec); - int gen = lru_gen_from_seq(max_seq); + struct lruvec *lruvec; + struct lru_gen_mm_state *mm_state; + unsigned long max_seq; + int gen; lockdep_assert_held(pvmw->ptl); VM_WARN_ON_ONCE_FOLIO(folio_test_lru(folio), folio); @@ -4253,6 +4233,12 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr) } } + memcg = get_mem_cgroup_from_folio(folio); + lruvec = mem_cgroup_lruvec(memcg, pgdat); + max_seq = READ_ONCE((lruvec)->lrugen.max_seq); + gen = lru_gen_from_seq(max_seq); + mm_state = get_mm_state(lruvec); + lazy_mmu_mode_enable(); pte -= (addr - start) / PAGE_SIZE; @@ -4302,6 +4288,8 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr) if (mm_state && suitable_to_scan(i, young)) update_bloom_filter(mm_state, max_seq, pvmw->pmd); + mem_cgroup_put(memcg); + return true; } @@ -4437,6 +4425,148 @@ void lru_gen_soft_reclaim(struct mem_cgroup *memcg, int nid) lru_gen_rotate_memcg(lruvec, MEMCG_LRU_HEAD); } +bool recheck_lru_gen_max_memcg(struct mem_cgroup *memcg, int nid) +{ + struct lruvec *lruvec = get_lruvec(memcg, nid); + int type; + + for (type = 0; type < ANON_AND_FILE; type++) { + if (get_nr_gens(lruvec, type) != MAX_NR_GENS) + return false; + } + + return true; +} + +static void try_to_inc_max_seq_nowalk(struct mem_cgroup *memcg, + struct lruvec *lruvec) +{ + struct lru_gen_mm_list *mm_list = get_mm_list(memcg); + struct lru_gen_mm_state *mm_state = get_mm_state(lruvec); + int swappiness = mem_cgroup_swappiness(memcg); + DEFINE_MAX_SEQ(lruvec); + bool success = false; + + /* + * We are not iterating the mm_list here, updating mm_state->seq is just + * to make mm walkers work properly. + */ + if (mm_state) { + spin_lock(&mm_list->lock); + VM_WARN_ON_ONCE(mm_state->seq + 1 < max_seq); + if (max_seq > mm_state->seq) { + WRITE_ONCE(mm_state->seq, mm_state->seq + 1); + success = true; + } + spin_unlock(&mm_list->lock); + } else { + success = true; + } + + if (success) + inc_max_seq(lruvec, max_seq, swappiness); +} + +/* + * We need to ensure that the folios of child memcg can be reparented to the + * same gen of the parent memcg, so the gens of the parent memcg needed be + * incremented to the MAX_NR_GENS before reparenting. + */ +void max_lru_gen_memcg(struct mem_cgroup *memcg, int nid) +{ + struct lruvec *lruvec = get_lruvec(memcg, nid); + int type; + + for (type = 0; type < ANON_AND_FILE; type++) { + while (get_nr_gens(lruvec, type) < MAX_NR_GENS) { + try_to_inc_max_seq_nowalk(memcg, lruvec); + cond_resched(); + } + } +} + +/* + * Compared to traditional LRU, MGLRU faces the following challenges: + * + * 1. Each lruvec has between MIN_NR_GENS and MAX_NR_GENS generations, the + * number of generations of the parent and child memcg may be different, + * so we cannot simply transfer MGLRU folios in the child memcg to the + * parent memcg as we did for traditional LRU folios. + * 2. The generation information is stored in folio->flags, but we cannot + * traverse these folios while holding the lru lock, otherwise it may + * cause softlockup. + * 3. In walk_update_folio(), the gen of folio and corresponding lru size + * may be updated, but the folio is not immediately moved to the + * corresponding lru list. Therefore, there may be folios of different + * generations on an LRU list. + * 4. In lru_gen_del_folio(), the generation to which the folio belongs is + * found based on the generation information in folio->flags, and the + * corresponding LRU size will be updated. Therefore, we need to update + * the lru size correctly during reparenting, otherwise the lru size may + * be updated incorrectly in lru_gen_del_folio(). + * + * Finally, we choose a compromise method, which is to splice the lru list in + * the child memcg to the lru list of the same generation in the parent memcg + * during reparenting. + * + * The same generation has different meanings in the parent and child memcg, + * so this compromise method will cause the LRU inversion problem. But as the + * system runs, this problem will be fixed automatically. + */ +static void __lru_gen_reparent_memcg(struct lruvec *child_lruvec, struct lruvec *parent_lruvec, + int zone, int type) +{ + struct lru_gen_folio *child_lrugen, *parent_lrugen; + enum lru_list lru = type * LRU_INACTIVE_FILE; + int i; + + child_lrugen = &child_lruvec->lrugen; + parent_lrugen = &parent_lruvec->lrugen; + + for (i = 0; i < get_nr_gens(child_lruvec, type); i++) { + int gen = lru_gen_from_seq(child_lrugen->max_seq - i); + long nr_pages = child_lrugen->nr_pages[gen][type][zone]; + int child_lru_active = lru_gen_is_active(child_lruvec, gen) ? LRU_ACTIVE : 0; + int parent_lru_active = lru_gen_is_active(parent_lruvec, gen) ? LRU_ACTIVE : 0; + + /* Assuming that child pages are colder than parent pages */ + list_splice_tail_init(&child_lrugen->folios[gen][type][zone], + &parent_lrugen->folios[gen][type][zone]); + + WRITE_ONCE(child_lrugen->nr_pages[gen][type][zone], 0); + WRITE_ONCE(parent_lrugen->nr_pages[gen][type][zone], + parent_lrugen->nr_pages[gen][type][zone] + nr_pages); + + if (lru_gen_is_active(child_lruvec, gen) != lru_gen_is_active(parent_lruvec, gen)) { + __update_lru_size(child_lruvec, lru + child_lru_active, zone, -nr_pages); + __update_lru_size(parent_lruvec, lru + parent_lru_active, zone, nr_pages); + } + } +} + +void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid) +{ + struct lruvec *child_lruvec, *parent_lruvec; + int type, zid; + struct zone *zone; + enum lru_list lru; + + child_lruvec = get_lruvec(memcg, nid); + parent_lruvec = get_lruvec(parent, nid); + + for_each_managed_zone_pgdat(zone, NODE_DATA(nid), zid, MAX_NR_ZONES - 1) + for (type = 0; type < ANON_AND_FILE; type++) + __lru_gen_reparent_memcg(child_lruvec, parent_lruvec, zid, type); + + for_each_lru(lru) { + for_each_managed_zone_pgdat(zone, NODE_DATA(nid), zid, MAX_NR_ZONES - 1) { + unsigned long size = mem_cgroup_get_zone_lru_size(child_lruvec, lru, zid); + + mem_cgroup_update_lru_size(parent_lruvec, lru, zid, size); + } + } +} + #endif /* CONFIG_MEMCG */ /****************************************************************************** @@ -4630,7 +4760,7 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec, static int get_tier_idx(struct lruvec *lruvec, int type) { int tier; - struct ctrl_pos sp, pv; + struct ctrl_pos sp, pv = {}; /* * To leave a margin for fluctuations, use a larger gain factor (2:3). @@ -4649,7 +4779,7 @@ static int get_tier_idx(struct lruvec *lruvec, int type) static int get_type_to_scan(struct lruvec *lruvec, int swappiness) { - struct ctrl_pos sp, pv; + struct ctrl_pos sp, pv = {}; if (swappiness <= MIN_SWAPPINESS + 1) return LRU_GEN_FILE; @@ -4707,7 +4837,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, struct mem_cgroup *memcg = lruvec_memcg(lruvec); struct pglist_data *pgdat = lruvec_pgdat(lruvec); - spin_lock_irq(&lruvec->lru_lock); + lruvec_lock_irq(lruvec); scanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness, &type, &list); @@ -4716,7 +4846,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, if (evictable_min_seq(lrugen->min_seq, swappiness) + MIN_NR_GENS > lrugen->max_seq) scanned = 0; - spin_unlock_irq(&lruvec->lru_lock); + lruvec_unlock_irq(lruvec); if (list_empty(&list)) return scanned; @@ -4749,14 +4879,14 @@ retry: set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS, BIT(PG_active)); } - spin_lock_irq(&lruvec->lru_lock); - - move_folios_to_lru(lruvec, &list); + move_folios_to_lru(&list); walk = current->reclaim_state->mm_walk; if (walk && walk->batched) { walk->lruvec = lruvec; + lruvec_lock_irq(lruvec); reset_batch_size(walk); + lruvec_unlock_irq(lruvec); } mod_lruvec_state(lruvec, PGDEMOTE_KSWAPD + reclaimer_offset(sc), @@ -4766,8 +4896,6 @@ retry: mod_lruvec_state(lruvec, item, reclaimed); mod_lruvec_state(lruvec, PGSTEAL_ANON + type, reclaimed); - spin_unlock_irq(&lruvec->lru_lock); - list_splice_init(&clean, &list); if (!list_empty(&list)) { @@ -4843,10 +4971,6 @@ static bool should_abort_scan(struct lruvec *lruvec, struct scan_control *sc) int i; enum zone_watermarks mark; - /* don't abort memcg reclaim to ensure fairness */ - if (!root_reclaim(sc)) - return false; - if (sc->nr_reclaimed >= max(sc->nr_to_reclaim, compact_gap(sc->order))) return true; @@ -4900,9 +5024,24 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) * If too many file cache in the coldest generation can't be evicted * due to being dirty, wake up the flusher. */ - if (sc->nr.unqueued_dirty && sc->nr.unqueued_dirty == sc->nr.file_taken) + if (sc->nr.unqueued_dirty && sc->nr.unqueued_dirty == sc->nr.file_taken) { + struct pglist_data *pgdat = lruvec_pgdat(lruvec); + wakeup_flusher_threads(WB_REASON_VMSCAN); + /* + * For cgroupv1 dirty throttling is achieved by waking up + * the kernel flusher here and later waiting on folios + * which are in writeback to finish (see shrink_folio_list()). + * + * Flusher may not be able to issue writeback quickly + * enough for cgroupv1 writeback throttling to work + * on a large system. + */ + if (!writeback_throttling_sane(sc)) + reclaim_throttle(pgdat, VMSCAN_THROTTLE_WRITEBACK); + } + /* whether this lruvec should be rotated */ return nr_to_scan < 0; } @@ -5196,7 +5335,7 @@ static void lru_gen_change_state(bool enabled) for_each_node(nid) { struct lruvec *lruvec = get_lruvec(memcg, nid); - spin_lock_irq(&lruvec->lru_lock); + lruvec_lock_irq(lruvec); VM_WARN_ON_ONCE(!seq_is_valid(lruvec)); VM_WARN_ON_ONCE(!state_is_valid(lruvec)); @@ -5204,12 +5343,12 @@ static void lru_gen_change_state(bool enabled) lruvec->lrugen.enabled = enabled; while (!(enabled ? fill_evictable(lruvec) : drain_evictable(lruvec))) { - spin_unlock_irq(&lruvec->lru_lock); + lruvec_unlock_irq(lruvec); cond_resched(); - spin_lock_irq(&lruvec->lru_lock); + lruvec_lock_irq(lruvec); } - spin_unlock_irq(&lruvec->lru_lock); + lruvec_unlock_irq(lruvec); } cond_resched(); @@ -7898,7 +8037,7 @@ void check_move_unevictable_folios(struct folio_batch *fbatch) if (lruvec) { __count_vm_events(UNEVICTABLE_PGRESCUED, pgrescued); __count_vm_events(UNEVICTABLE_PGSCANNED, pgscanned); - unlock_page_lruvec_irq(lruvec); + lruvec_unlock_irq(lruvec); } else if (pgscanned) { count_vm_events(UNEVICTABLE_PGSCANNED, pgscanned); } diff --git a/mm/vmstat.c b/mm/vmstat.c index c360c1b29ac9..f534972f517d 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -2141,7 +2141,7 @@ static void vmstat_shepherd(struct work_struct *w) if (cpu_is_isolated(cpu)) continue; - if (!delayed_work_pending(dw) && need_update(cpu)) + if (!work_busy(&dw->work) && need_update(cpu)) queue_delayed_work_on(cpu, mm_percpu_wq, dw, 0); } diff --git a/mm/workingset.c b/mm/workingset.c index 37a94979900f..07e6836d0502 100644 --- a/mm/workingset.c +++ b/mm/workingset.c @@ -244,12 +244,15 @@ static void *lru_gen_eviction(struct folio *folio) int refs = folio_lru_refs(folio); bool workingset = folio_test_workingset(folio); int tier = lru_tier_from_refs(refs, workingset); - struct mem_cgroup *memcg = folio_memcg(folio); + struct mem_cgroup *memcg; struct pglist_data *pgdat = folio_pgdat(folio); + unsigned short memcg_id; BUILD_BUG_ON(LRU_GEN_WIDTH + LRU_REFS_WIDTH > BITS_PER_LONG - max(EVICTION_SHIFT, EVICTION_SHIFT_ANON)); + rcu_read_lock(); + memcg = folio_memcg(folio); lruvec = mem_cgroup_lruvec(memcg, pgdat); lrugen = &lruvec->lrugen; min_seq = READ_ONCE(lrugen->min_seq[type]); @@ -257,8 +260,10 @@ static void *lru_gen_eviction(struct folio *folio) hist = lru_hist_from_seq(min_seq); atomic_long_add(delta, &lrugen->evicted[hist][type][tier]); + memcg_id = mem_cgroup_private_id(memcg); + rcu_read_unlock(); - return pack_shadow(mem_cgroup_private_id(memcg), pgdat, token, workingset, type); + return pack_shadow(memcg_id, pgdat, token, workingset, type); } /* @@ -541,7 +546,6 @@ bool workingset_test_recent(void *shadow, bool file, bool *workingset, void workingset_refault(struct folio *folio, void *shadow) { bool file = folio_is_file_lru(folio); - struct pglist_data *pgdat; struct mem_cgroup *memcg; struct lruvec *lruvec; bool workingset; @@ -564,14 +568,12 @@ void workingset_refault(struct folio *folio, void *shadow) * locked to guarantee folio_memcg() stability throughout. */ nr = folio_nr_pages(folio); - memcg = folio_memcg(folio); - pgdat = folio_pgdat(folio); - lruvec = mem_cgroup_lruvec(memcg, pgdat); - + memcg = get_mem_cgroup_from_folio(folio); + lruvec = mem_cgroup_lruvec(memcg, folio_pgdat(folio)); mod_lruvec_state(lruvec, WORKINGSET_REFAULT_BASE + file, nr); if (!workingset_test_recent(shadow, file, &workingset, true)) - return; + goto out; folio_set_active(folio); workingset_age_nonresident(lruvec, nr); @@ -587,6 +589,8 @@ void workingset_refault(struct folio *folio, void *shadow) lru_note_cost_refault(folio); mod_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + file, nr); } +out: + mem_cgroup_put(memcg); } /** @@ -599,8 +603,11 @@ void workingset_activation(struct folio *folio) * Filter non-memcg pages here, e.g. unmap can call * mark_page_accessed() on VDSO pages. */ - if (mem_cgroup_disabled() || folio_memcg_charged(folio)) + if (mem_cgroup_disabled() || folio_memcg_charged(folio)) { + rcu_read_lock(); workingset_age_nonresident(folio_lruvec(folio), folio_nr_pages(folio)); + rcu_read_unlock(); + } } /* @@ -684,9 +691,10 @@ static unsigned long count_shadow_nodes(struct shrinker *shrinker, mem_cgroup_flush_stats_ratelimited(sc->memcg); lruvec = mem_cgroup_lruvec(sc->memcg, NODE_DATA(sc->nid)); + for (pages = 0, i = 0; i < NR_LRU_LISTS; i++) - pages += lruvec_page_state_local(lruvec, - NR_LRU_BASE + i); + pages += lruvec_lru_size(lruvec, i, MAX_NR_ZONES - 1); + pages += lruvec_page_state_local( lruvec, NR_SLAB_RECLAIMABLE_B) >> PAGE_SHIFT; pages += lruvec_page_state_local( diff --git a/mm/zswap.c b/mm/zswap.c index 0823cadd02b6..4b5149173b0e 100644 --- a/mm/zswap.c +++ b/mm/zswap.c @@ -242,6 +242,34 @@ static inline struct xarray *swap_zswap_tree(swp_entry_t swp) **********************************/ static void __zswap_pool_empty(struct percpu_ref *ref); +static void acomp_ctx_free(struct crypto_acomp_ctx *acomp_ctx) +{ + if (!acomp_ctx) + return; + + /* + * If there was an error in allocating @acomp_ctx->req, it + * would be set to NULL. + */ + if (acomp_ctx->req) + acomp_request_free(acomp_ctx->req); + + acomp_ctx->req = NULL; + + /* + * We have to handle both cases here: an error pointer return from + * crypto_alloc_acomp_node(); and a) NULL initialization by zswap, or + * b) NULL assignment done in a previous call to acomp_ctx_free(). + */ + if (!IS_ERR_OR_NULL(acomp_ctx->acomp)) + crypto_free_acomp(acomp_ctx->acomp); + + acomp_ctx->acomp = NULL; + + kfree(acomp_ctx->buffer); + acomp_ctx->buffer = NULL; +} + static struct zswap_pool *zswap_pool_create(char *compressor) { struct zswap_pool *pool; @@ -263,19 +291,27 @@ static struct zswap_pool *zswap_pool_create(char *compressor) strscpy(pool->tfm_name, compressor, sizeof(pool->tfm_name)); - pool->acomp_ctx = alloc_percpu(*pool->acomp_ctx); + /* Many things rely on the zero-initialization. */ + pool->acomp_ctx = alloc_percpu_gfp(*pool->acomp_ctx, + GFP_KERNEL | __GFP_ZERO); if (!pool->acomp_ctx) { pr_err("percpu alloc failed\n"); goto error; } - for_each_possible_cpu(cpu) - mutex_init(&per_cpu_ptr(pool->acomp_ctx, cpu)->mutex); - + /* + * This is serialized against CPU hotplug operations. Hence, cores + * cannot be offlined until this finishes. + */ ret = cpuhp_state_add_instance(CPUHP_MM_ZSWP_POOL_PREPARE, &pool->node); + + /* + * cpuhp_state_add_instance() will not cleanup on failure since + * we don't register a hotunplug callback. + */ if (ret) - goto error; + goto cpuhp_add_fail; /* being the current pool takes 1 ref; this func expects the * caller to always add the new pool as the current pool @@ -292,6 +328,10 @@ static struct zswap_pool *zswap_pool_create(char *compressor) ref_fail: cpuhp_state_remove_instance(CPUHP_MM_ZSWP_POOL_PREPARE, &pool->node); + +cpuhp_add_fail: + for_each_possible_cpu(cpu) + acomp_ctx_free(per_cpu_ptr(pool->acomp_ctx, cpu)); error: if (pool->acomp_ctx) free_percpu(pool->acomp_ctx); @@ -322,9 +362,15 @@ static struct zswap_pool *__zswap_pool_create_fallback(void) static void zswap_pool_destroy(struct zswap_pool *pool) { + int cpu; + zswap_pool_debug("destroying", pool); cpuhp_state_remove_instance(CPUHP_MM_ZSWP_POOL_PREPARE, &pool->node); + + for_each_possible_cpu(cpu) + acomp_ctx_free(per_cpu_ptr(pool->acomp_ctx, cpu)); + free_percpu(pool->acomp_ctx); zs_destroy_pool(pool->zs_pool); @@ -664,8 +710,10 @@ void zswap_folio_swapin(struct folio *folio) struct lruvec *lruvec; if (folio) { + rcu_read_lock(); lruvec = folio_lruvec(folio); atomic_long_inc(&lruvec->zswap_lruvec_state.nr_disk_swapins); + rcu_read_unlock(); } } @@ -736,39 +784,41 @@ static int zswap_cpu_comp_prepare(unsigned int cpu, struct hlist_node *node) { struct zswap_pool *pool = hlist_entry(node, struct zswap_pool, node); struct crypto_acomp_ctx *acomp_ctx = per_cpu_ptr(pool->acomp_ctx, cpu); - struct crypto_acomp *acomp = NULL; - struct acomp_req *req = NULL; - u8 *buffer = NULL; - int ret; - - buffer = kmalloc_node(PAGE_SIZE, GFP_KERNEL, cpu_to_node(cpu)); - if (!buffer) { - ret = -ENOMEM; - goto fail; - } - - acomp = crypto_alloc_acomp_node(pool->tfm_name, 0, 0, cpu_to_node(cpu)); - if (IS_ERR(acomp)) { - pr_err("could not alloc crypto acomp %s : %pe\n", - pool->tfm_name, acomp); - ret = PTR_ERR(acomp); - goto fail; - } - - req = acomp_request_alloc(acomp); - if (!req) { - pr_err("could not alloc crypto acomp_request %s\n", - pool->tfm_name); - ret = -ENOMEM; - goto fail; - } + int ret = -ENOMEM; /* - * Only hold the mutex after completing allocations, otherwise we may - * recurse into zswap through reclaim and attempt to hold the mutex - * again resulting in a deadlock. + * To handle cases where the CPU goes through online-offline-online + * transitions, we return if the acomp_ctx has already been initialized. */ - mutex_lock(&acomp_ctx->mutex); + if (acomp_ctx->acomp) { + WARN_ON_ONCE(IS_ERR(acomp_ctx->acomp)); + return 0; + } + + acomp_ctx->buffer = kmalloc_node(PAGE_SIZE, GFP_KERNEL, cpu_to_node(cpu)); + if (!acomp_ctx->buffer) + return ret; + + /* + * In case of an error, crypto_alloc_acomp_node() returns an + * error pointer, never NULL. + */ + acomp_ctx->acomp = crypto_alloc_acomp_node(pool->tfm_name, 0, 0, cpu_to_node(cpu)); + if (IS_ERR(acomp_ctx->acomp)) { + pr_err("could not alloc crypto acomp %s : %pe\n", + pool->tfm_name, acomp_ctx->acomp); + ret = PTR_ERR(acomp_ctx->acomp); + goto fail; + } + + /* acomp_request_alloc() returns NULL in case of an error. */ + acomp_ctx->req = acomp_request_alloc(acomp_ctx->acomp); + if (!acomp_ctx->req) { + pr_err("could not alloc crypto acomp_request %s\n", + pool->tfm_name); + goto fail; + } + crypto_init_wait(&acomp_ctx->wait); /* @@ -776,80 +826,17 @@ static int zswap_cpu_comp_prepare(unsigned int cpu, struct hlist_node *node) * crypto_wait_req(); if the backend of acomp is scomp, the callback * won't be called, crypto_wait_req() will return without blocking. */ - acomp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + acomp_request_set_callback(acomp_ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &acomp_ctx->wait); - acomp_ctx->buffer = buffer; - acomp_ctx->acomp = acomp; - acomp_ctx->req = req; - mutex_unlock(&acomp_ctx->mutex); + mutex_init(&acomp_ctx->mutex); return 0; fail: - if (!IS_ERR_OR_NULL(acomp)) - crypto_free_acomp(acomp); - kfree(buffer); + acomp_ctx_free(acomp_ctx); return ret; } -static int zswap_cpu_comp_dead(unsigned int cpu, struct hlist_node *node) -{ - struct zswap_pool *pool = hlist_entry(node, struct zswap_pool, node); - struct crypto_acomp_ctx *acomp_ctx = per_cpu_ptr(pool->acomp_ctx, cpu); - struct acomp_req *req; - struct crypto_acomp *acomp; - u8 *buffer; - - if (IS_ERR_OR_NULL(acomp_ctx)) - return 0; - - mutex_lock(&acomp_ctx->mutex); - req = acomp_ctx->req; - acomp = acomp_ctx->acomp; - buffer = acomp_ctx->buffer; - acomp_ctx->req = NULL; - acomp_ctx->acomp = NULL; - acomp_ctx->buffer = NULL; - mutex_unlock(&acomp_ctx->mutex); - - /* - * Do the actual freeing after releasing the mutex to avoid subtle - * locking dependencies causing deadlocks. - */ - if (!IS_ERR_OR_NULL(req)) - acomp_request_free(req); - if (!IS_ERR_OR_NULL(acomp)) - crypto_free_acomp(acomp); - kfree(buffer); - - return 0; -} - -static struct crypto_acomp_ctx *acomp_ctx_get_cpu_lock(struct zswap_pool *pool) -{ - struct crypto_acomp_ctx *acomp_ctx; - - for (;;) { - acomp_ctx = raw_cpu_ptr(pool->acomp_ctx); - mutex_lock(&acomp_ctx->mutex); - if (likely(acomp_ctx->req)) - return acomp_ctx; - /* - * It is possible that we were migrated to a different CPU after - * getting the per-CPU ctx but before the mutex was acquired. If - * the old CPU got offlined, zswap_cpu_comp_dead() could have - * already freed ctx->req (among other things) and set it to - * NULL. Just try again on the new CPU that we ended up on. - */ - mutex_unlock(&acomp_ctx->mutex); - } -} - -static void acomp_ctx_put_unlock(struct crypto_acomp_ctx *acomp_ctx) -{ - mutex_unlock(&acomp_ctx->mutex); -} - static bool zswap_compress(struct page *page, struct zswap_entry *entry, struct zswap_pool *pool) { @@ -862,7 +849,9 @@ static bool zswap_compress(struct page *page, struct zswap_entry *entry, u8 *dst; bool mapped = false; - acomp_ctx = acomp_ctx_get_cpu_lock(pool); + acomp_ctx = raw_cpu_ptr(pool->acomp_ctx); + mutex_lock(&acomp_ctx->mutex); + dst = acomp_ctx->buffer; sg_init_table(&input, 1); sg_set_page(&input, page, PAGE_SIZE, 0); @@ -893,11 +882,14 @@ static bool zswap_compress(struct page *page, struct zswap_entry *entry, * to the active LRU list in the case. */ if (comp_ret || !dlen || dlen >= PAGE_SIZE) { + rcu_read_lock(); if (!mem_cgroup_zswap_writeback_enabled( folio_memcg(page_folio(page)))) { + rcu_read_unlock(); comp_ret = comp_ret ? comp_ret : -EINVAL; goto unlock; } + rcu_read_unlock(); comp_ret = 0; dlen = PAGE_SIZE; dst = kmap_local_page(page); @@ -925,7 +917,7 @@ unlock: else if (alloc_ret) zswap_reject_alloc_fail++; - acomp_ctx_put_unlock(acomp_ctx); + mutex_unlock(&acomp_ctx->mutex); return comp_ret == 0 && alloc_ret == 0; } @@ -937,7 +929,8 @@ static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio) struct crypto_acomp_ctx *acomp_ctx; int ret = 0, dlen; - acomp_ctx = acomp_ctx_get_cpu_lock(pool); + acomp_ctx = raw_cpu_ptr(pool->acomp_ctx); + mutex_lock(&acomp_ctx->mutex); zs_obj_read_sg_begin(pool->zs_pool, entry->handle, input, entry->length); /* zswap entries of length PAGE_SIZE are not compressed. */ @@ -962,7 +955,7 @@ static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio) } zs_obj_read_sg_end(pool->zs_pool, entry->handle); - acomp_ctx_put_unlock(acomp_ctx); + mutex_unlock(&acomp_ctx->mutex); if (!ret && dlen == PAGE_SIZE) return true; @@ -1782,7 +1775,7 @@ static int zswap_setup(void) ret = cpuhp_setup_state_multi(CPUHP_MM_ZSWP_POOL_PREPARE, "mm/zswap_pool:prepare", zswap_cpu_comp_prepare, - zswap_cpu_comp_dead); + NULL); if (ret) goto hp_fail; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index c40f7d5c4fca..7aa3af8b10ea 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -172,39 +172,42 @@ int vlan_dev_set_egress_priority(const struct net_device *dev, u32 skb_prio, u16 vlan_prio) { struct vlan_dev_priv *vlan = vlan_dev_priv(dev); - struct vlan_priority_tci_mapping *mp = NULL; + struct vlan_priority_tci_mapping __rcu **mpp; + struct vlan_priority_tci_mapping *mp; struct vlan_priority_tci_mapping *np; + u32 bucket = skb_prio & 0xF; u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK; /* See if a priority mapping exists.. */ - mp = vlan->egress_priority_map[skb_prio & 0xF]; + mpp = &vlan->egress_priority_map[bucket]; + mp = rtnl_dereference(*mpp); while (mp) { if (mp->priority == skb_prio) { - if (mp->vlan_qos && !vlan_qos) + if (!vlan_qos) { + rcu_assign_pointer(*mpp, rtnl_dereference(mp->next)); vlan->nr_egress_mappings--; - else if (!mp->vlan_qos && vlan_qos) - vlan->nr_egress_mappings++; - mp->vlan_qos = vlan_qos; + kfree_rcu(mp, rcu); + } else { + WRITE_ONCE(mp->vlan_qos, vlan_qos); + } return 0; } - mp = mp->next; + mpp = &mp->next; + mp = rtnl_dereference(*mpp); } /* Create a new mapping then. */ - mp = vlan->egress_priority_map[skb_prio & 0xF]; + if (!vlan_qos) + return 0; + np = kmalloc_obj(struct vlan_priority_tci_mapping); if (!np) return -ENOBUFS; - np->next = mp; np->priority = skb_prio; np->vlan_qos = vlan_qos; - /* Before inserting this element in hash table, make sure all its fields - * are committed to memory. - * coupled with smp_rmb() in vlan_dev_get_egress_qos_mask() - */ - smp_wmb(); - vlan->egress_priority_map[skb_prio & 0xF] = np; + RCU_INIT_POINTER(np->next, rtnl_dereference(vlan->egress_priority_map[bucket])); + rcu_assign_pointer(vlan->egress_priority_map[bucket], np); if (vlan_qos) vlan->nr_egress_mappings++; return 0; @@ -604,11 +607,17 @@ void vlan_dev_free_egress_priority(const struct net_device *dev) int i; for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) { - while ((pm = vlan->egress_priority_map[i]) != NULL) { - vlan->egress_priority_map[i] = pm->next; - kfree(pm); + pm = rtnl_dereference(vlan->egress_priority_map[i]); + RCU_INIT_POINTER(vlan->egress_priority_map[i], NULL); + while (pm) { + struct vlan_priority_tci_mapping *next; + + next = rtnl_dereference(pm->next); + kfree_rcu(pm, rcu); + pm = next; } } + vlan->nr_egress_mappings = 0; } static void vlan_dev_uninit(struct net_device *dev) diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index a000b1ef0520..368d53ca7d87 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -260,13 +260,11 @@ static int vlan_fill_info(struct sk_buff *skb, const struct net_device *dev) goto nla_put_failure; for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) { - for (pm = vlan->egress_priority_map[i]; pm; - pm = pm->next) { - if (!pm->vlan_qos) - continue; - + for (pm = rcu_dereference_rtnl(vlan->egress_priority_map[i]); pm; + pm = rcu_dereference_rtnl(pm->next)) { + u16 vlan_qos = READ_ONCE(pm->vlan_qos); m.from = pm->priority; - m.to = (pm->vlan_qos >> 13) & 0x7; + m.to = (vlan_qos >> 13) & 0x7; if (nla_put(skb, IFLA_VLAN_QOS_MAPPING, sizeof(m), &m)) goto nla_put_failure; diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c index fa67374bda49..0e424e0895b7 100644 --- a/net/8021q/vlanproc.c +++ b/net/8021q/vlanproc.c @@ -262,15 +262,19 @@ static int vlandev_seq_show(struct seq_file *seq, void *offset) vlan->ingress_priority_map[7]); seq_printf(seq, " EGRESS priority mappings: "); + rcu_read_lock(); for (i = 0; i < 16; i++) { - const struct vlan_priority_tci_mapping *mp - = vlan->egress_priority_map[i]; + const struct vlan_priority_tci_mapping *mp = + rcu_dereference(vlan->egress_priority_map[i]); while (mp) { + u16 vlan_qos = READ_ONCE(mp->vlan_qos); + seq_printf(seq, "%u:%d ", - mp->priority, ((mp->vlan_qos >> 13) & 0x7)); - mp = mp->next; + mp->priority, ((vlan_qos >> 13) & 0x7)); + mp = rcu_dereference(mp->next); } } + rcu_read_unlock(); seq_puts(seq, "\n"); return 0; diff --git a/net/9p/trans_xen.c b/net/9p/trans_xen.c index 47af5a10e921..f9fb2db7a066 100644 --- a/net/9p/trans_xen.c +++ b/net/9p/trans_xen.c @@ -283,25 +283,33 @@ static void xen_9pfs_front_free(struct xen_9pfs_front_priv *priv) cancel_work_sync(&ring->work); - if (!priv->rings[i].intf) + if (!ring->intf) break; - if (priv->rings[i].irq > 0) - unbind_from_irqhandler(priv->rings[i].irq, ring); - if (priv->rings[i].data.in) { - for (j = 0; - j < (1 << priv->rings[i].intf->ring_order); + if (ring->irq >= 0) { + unbind_from_irqhandler(ring->irq, ring); + ring->irq = -1; + } + if (ring->data.in) { + for (j = 0; j < (1 << ring->intf->ring_order); j++) { grant_ref_t ref; - ref = priv->rings[i].intf->ref[j]; + ref = ring->intf->ref[j]; gnttab_end_foreign_access(ref, NULL); + ring->intf->ref[j] = INVALID_GRANT_REF; } - free_pages_exact(priv->rings[i].data.in, - 1UL << (priv->rings[i].intf->ring_order + - XEN_PAGE_SHIFT)); + free_pages_exact(ring->data.in, + 1UL << (ring->intf->ring_order + + XEN_PAGE_SHIFT)); + ring->data.in = NULL; + ring->data.out = NULL; } - gnttab_end_foreign_access(priv->rings[i].ref, NULL); - free_page((unsigned long)priv->rings[i].intf); + if (ring->ref != INVALID_GRANT_REF) { + gnttab_end_foreign_access(ring->ref, NULL); + ring->ref = INVALID_GRANT_REF; + } + free_page((unsigned long)ring->intf); + ring->intf = NULL; } kfree(priv->rings); } @@ -334,6 +342,12 @@ static int xen_9pfs_front_alloc_dataring(struct xenbus_device *dev, int ret = -ENOMEM; void *bytes = NULL; + ring->intf = NULL; + ring->data.in = NULL; + ring->data.out = NULL; + ring->ref = INVALID_GRANT_REF; + ring->irq = -1; + init_waitqueue_head(&ring->wq); spin_lock_init(&ring->lock); INIT_WORK(&ring->work, p9_xen_response); @@ -379,9 +393,18 @@ out: for (i--; i >= 0; i--) gnttab_end_foreign_access(ring->intf->ref[i], NULL); free_pages_exact(bytes, 1UL << (order + XEN_PAGE_SHIFT)); + ring->data.in = NULL; + ring->data.out = NULL; } - gnttab_end_foreign_access(ring->ref, NULL); - free_page((unsigned long)ring->intf); + if (ring->ref != INVALID_GRANT_REF) { + gnttab_end_foreign_access(ring->ref, NULL); + ring->ref = INVALID_GRANT_REF; + } + if (ring->intf) { + free_page((unsigned long)ring->intf); + ring->intf = NULL; + } + ring->irq = -1; return ret; } @@ -390,23 +413,29 @@ static int xen_9pfs_front_init(struct xenbus_device *dev) int ret, i; struct xenbus_transaction xbt; struct xen_9pfs_front_priv *priv; - char *versions, *v; - unsigned int max_rings, max_ring_order, len = 0; + char *versions, *v, *token; + bool version_1 = false; + unsigned int max_rings, max_ring_order, len = 0, version; versions = xenbus_read(XBT_NIL, dev->otherend, "versions", &len); if (IS_ERR(versions)) return PTR_ERR(versions); - for (v = versions; *v; v++) { - if (simple_strtoul(v, &v, 10) == 1) { - v = NULL; - break; + for (v = versions; (token = strsep(&v, ",")); ) { + if (!*token) + continue; + + ret = kstrtouint(token, 10, &version); + if (ret) { + kfree(versions); + return ret; } - } - if (v) { - kfree(versions); - return -EINVAL; + if (version == 1) + version_1 = true; } kfree(versions); + if (!version_1) + return -EINVAL; + max_rings = xenbus_read_unsigned(dev->otherend, "max-rings", 0); if (max_rings < XEN_9PFS_NUM_RINGS) return -EINVAL; diff --git a/net/Kconfig b/net/Kconfig index 62266eaf0e95..bdea8aef7983 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -414,7 +414,6 @@ endmenu # Network testing endmenu # Networking options -source "net/ax25/Kconfig" source "net/can/Kconfig" source "net/bluetooth/Kconfig" source "net/rxrpc/Kconfig" @@ -439,7 +438,6 @@ endif # WIRELESS source "net/rfkill/Kconfig" source "net/9p/Kconfig" -source "net/caif/Kconfig" source "net/ceph/Kconfig" source "net/nfc/Kconfig" source "net/psample/Kconfig" diff --git a/net/Makefile b/net/Makefile index 90e3d72bf58b..d2175fce0406 100644 --- a/net/Makefile +++ b/net/Makefile @@ -28,9 +28,6 @@ obj-y += dsa/ obj-$(CONFIG_ATALK) += appletalk/ obj-$(CONFIG_X25) += x25/ obj-$(CONFIG_LAPB) += lapb/ -obj-$(CONFIG_NETROM) += netrom/ -obj-$(CONFIG_ROSE) += rose/ -obj-$(CONFIG_AX25) += ax25/ obj-$(CONFIG_CAN) += can/ obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_SUNRPC) += sunrpc/ @@ -53,7 +50,6 @@ obj-$(CONFIG_IUCV) += iucv/ obj-$(CONFIG_SMC) += smc/ obj-$(CONFIG_RFKILL) += rfkill/ obj-$(CONFIG_NET_9P) += 9p/ -obj-$(CONFIG_CAIF) += caif/ obj-$(CONFIG_DCB) += dcb/ obj-$(CONFIG_6LOWPAN) += 6lowpan/ obj-$(CONFIG_IEEE802154) += ieee802154/ diff --git a/net/atm/Kconfig b/net/atm/Kconfig index 77343d57ff2a..dfdc3a8553ba 100644 --- a/net/atm/Kconfig +++ b/net/atm/Kconfig @@ -19,43 +19,6 @@ config ATM of ATM. See the file for further details. -config ATM_CLIP - tristate "Classical IP over ATM" - depends on ATM && INET - help - Classical IP over ATM for PVCs and SVCs, supporting InARP and - ATMARP. If you want to communication with other IP hosts on your ATM - network, you will typically either say Y here or to "LAN Emulation - (LANE)" below. - -config ATM_CLIP_NO_ICMP - bool "Do NOT send ICMP if no neighbour" - depends on ATM_CLIP - help - Normally, an "ICMP host unreachable" message is sent if a neighbour - cannot be reached because there is no VC to it in the kernel's - ATMARP table. This may cause problems when ATMARP table entries are - briefly removed during revalidation. If you say Y here, packets to - such neighbours are silently discarded instead. - -config ATM_LANE - tristate "LAN Emulation (LANE) support" - depends on ATM - help - LAN Emulation emulates services of existing LANs across an ATM - network. Besides operating as a normal ATM end station client, Linux - LANE client can also act as an proxy client bridging packets between - ELAN and Ethernet segments. You need LANE if you want to try MPOA. - -config ATM_MPOA - tristate "Multi-Protocol Over ATM (MPOA) support" - depends on ATM && INET && ATM_LANE!=n - help - Multi-Protocol Over ATM allows ATM edge devices such as routers, - bridges and ATM attached hosts establish direct ATM VCs across - subnetwork boundaries. These shortcut connections bypass routers - enhancing overall network performance. - config ATM_BR2684 tristate "RFC1483/2684 Bridged protocols" depends on ATM && INET diff --git a/net/atm/Makefile b/net/atm/Makefile index bfec0f2d83b5..484a1b1552cc 100644 --- a/net/atm/Makefile +++ b/net/atm/Makefile @@ -4,13 +4,9 @@ # atm-y := addr.o pvc.o signaling.o svc.o ioctl.o common.o atm_misc.o raw.o resources.o atm_sysfs.o -mpoa-objs := mpc.o mpoa_caches.o mpoa_proc.o obj-$(CONFIG_ATM) += atm.o -obj-$(CONFIG_ATM_CLIP) += clip.o obj-$(CONFIG_ATM_BR2684) += br2684.o atm-$(CONFIG_PROC_FS) += proc.o -obj-$(CONFIG_ATM_LANE) += lec.o -obj-$(CONFIG_ATM_MPOA) += mpoa.o obj-$(CONFIG_PPPOATM) += pppoatm.o diff --git a/net/atm/clip.c b/net/atm/clip.c deleted file mode 100644 index 516b2214680b..000000000000 --- a/net/atm/clip.c +++ /dev/null @@ -1,960 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* net/atm/clip.c - RFC1577 Classical IP over ATM */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ - -#include -#include -#include /* for UINT_MAX */ -#include -#include -#include -#include -#include -#include -#include /* for some manifest constants */ -#include -#include -#include -#include -#include -#include -#include /* for net/route.h */ -#include /* for struct sockaddr_in */ -#include /* for IFF_UP */ -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for struct rtable and routing */ -#include /* icmp_send */ -#include -#include /* for HZ */ -#include -#include /* for htons etc. */ -#include - -#include "common.h" -#include "resources.h" -#include - -static struct net_device *clip_devs; -static struct atm_vcc __rcu *atmarpd; -static DEFINE_MUTEX(atmarpd_lock); -static struct timer_list idle_timer; -static const struct neigh_ops clip_neigh_ops; - -static int to_atmarpd(enum atmarp_ctrl_type type, int itf, __be32 ip) -{ - struct sock *sk; - struct atmarp_ctrl *ctrl; - struct atm_vcc *vcc; - struct sk_buff *skb; - int err = 0; - - pr_debug("(%d)\n", type); - - rcu_read_lock(); - vcc = rcu_dereference(atmarpd); - if (!vcc) { - err = -EUNATCH; - goto unlock; - } - skb = alloc_skb(sizeof(struct atmarp_ctrl), GFP_ATOMIC); - if (!skb) { - err = -ENOMEM; - goto unlock; - } - ctrl = skb_put(skb, sizeof(struct atmarp_ctrl)); - ctrl->type = type; - ctrl->itf_num = itf; - ctrl->ip = ip; - atm_force_charge(vcc, skb->truesize); - - sk = sk_atm(vcc); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk); -unlock: - rcu_read_unlock(); - return err; -} - -static void link_vcc(struct clip_vcc *clip_vcc, struct atmarp_entry *entry) -{ - pr_debug("%p to entry %p (neigh %p)\n", clip_vcc, entry, entry->neigh); - clip_vcc->entry = entry; - clip_vcc->xoff = 0; /* @@@ may overrun buffer by one packet */ - clip_vcc->next = entry->vccs; - entry->vccs = clip_vcc; - entry->neigh->used = jiffies; -} - -static void unlink_clip_vcc(struct clip_vcc *clip_vcc) -{ - struct atmarp_entry *entry = clip_vcc->entry; - struct clip_vcc **walk; - - if (!entry) { - pr_err("!clip_vcc->entry (clip_vcc %p)\n", clip_vcc); - return; - } - netif_tx_lock_bh(entry->neigh->dev); /* block clip_start_xmit() */ - entry->neigh->used = jiffies; - for (walk = &entry->vccs; *walk; walk = &(*walk)->next) - if (*walk == clip_vcc) { - int error; - - *walk = clip_vcc->next; /* atomic */ - clip_vcc->entry = NULL; - if (clip_vcc->xoff) - netif_wake_queue(entry->neigh->dev); - if (entry->vccs) - goto out; - entry->expires = jiffies - 1; - /* force resolution or expiration */ - error = neigh_update(entry->neigh, NULL, NUD_NONE, - NEIGH_UPDATE_F_ADMIN, 0); - if (error) - pr_err("neigh_update failed with %d\n", error); - goto out; - } - pr_err("ATMARP: failed (entry %p, vcc 0x%p)\n", entry, clip_vcc); -out: - netif_tx_unlock_bh(entry->neigh->dev); -} - -/* The neighbour entry n->lock is held. */ -static int neigh_check_cb(struct neighbour *n) -{ - struct atmarp_entry *entry = neighbour_priv(n); - struct clip_vcc *cv; - - if (n->ops != &clip_neigh_ops) - return 0; - for (cv = entry->vccs; cv; cv = cv->next) { - unsigned long exp = cv->last_use + cv->idle_timeout; - - if (cv->idle_timeout && time_after(jiffies, exp)) { - pr_debug("releasing vcc %p->%p of entry %p\n", - cv, cv->vcc, entry); - vcc_release_async(cv->vcc, -ETIMEDOUT); - } - } - - if (entry->vccs || time_before(jiffies, entry->expires)) - return 0; - - if (refcount_read(&n->refcnt) > 1) { - struct sk_buff *skb; - - pr_debug("destruction postponed with ref %d\n", - refcount_read(&n->refcnt)); - - while ((skb = skb_dequeue(&n->arp_queue)) != NULL) - dev_kfree_skb(skb); - - return 0; - } - - pr_debug("expired neigh %p\n", n); - return 1; -} - -static void idle_timer_check(struct timer_list *unused) -{ - spin_lock(&arp_tbl.lock); - __neigh_for_each_release(&arp_tbl, neigh_check_cb); - mod_timer(&idle_timer, jiffies + CLIP_CHECK_INTERVAL * HZ); - spin_unlock(&arp_tbl.lock); -} - -static int clip_arp_rcv(struct sk_buff *skb) -{ - struct atm_vcc *vcc; - - pr_debug("\n"); - vcc = ATM_SKB(skb)->vcc; - if (!vcc || !atm_charge(vcc, skb->truesize)) { - dev_kfree_skb_any(skb); - return 0; - } - pr_debug("pushing to %p\n", vcc); - pr_debug("using %p\n", CLIP_VCC(vcc)->old_push); - CLIP_VCC(vcc)->old_push(vcc, skb); - return 0; -} - -static const unsigned char llc_oui[] = { - 0xaa, /* DSAP: non-ISO */ - 0xaa, /* SSAP: non-ISO */ - 0x03, /* Ctrl: Unnumbered Information Command PDU */ - 0x00, /* OUI: EtherType */ - 0x00, - 0x00 -}; - -static void clip_push(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct clip_vcc *clip_vcc = CLIP_VCC(vcc); - - pr_debug("\n"); - - if (!skb) { - pr_debug("removing VCC %p\n", clip_vcc); - if (clip_vcc->entry) - unlink_clip_vcc(clip_vcc); - clip_vcc->old_push(vcc, NULL); /* pass on the bad news */ - kfree(clip_vcc); - return; - } - atm_return(vcc, skb->truesize); - if (!clip_devs) { - kfree_skb(skb); - return; - } - - skb->dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : clip_devs; - /* clip_vcc->entry == NULL if we don't have an IP address yet */ - if (!skb->dev) { - dev_kfree_skb_any(skb); - return; - } - ATM_SKB(skb)->vcc = vcc; - skb_reset_mac_header(skb); - if (!clip_vcc->encap || - skb->len < RFC1483LLC_LEN || - memcmp(skb->data, llc_oui, sizeof(llc_oui))) - skb->protocol = htons(ETH_P_IP); - else { - skb->protocol = ((__be16 *)skb->data)[3]; - skb_pull(skb, RFC1483LLC_LEN); - if (skb->protocol == htons(ETH_P_ARP)) { - skb->dev->stats.rx_packets++; - skb->dev->stats.rx_bytes += skb->len; - clip_arp_rcv(skb); - return; - } - } - clip_vcc->last_use = jiffies; - skb->dev->stats.rx_packets++; - skb->dev->stats.rx_bytes += skb->len; - memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data)); - netif_rx(skb); -} - -/* - * Note: these spinlocks _must_not_ block on non-SMP. The only goal is that - * clip_pop is atomic with respect to the critical section in clip_start_xmit. - */ - -static void clip_pop(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct clip_vcc *clip_vcc = CLIP_VCC(vcc); - struct net_device *dev = skb->dev; - int old; - unsigned long flags; - - pr_debug("(vcc %p)\n", vcc); - clip_vcc->old_pop(vcc, skb); - /* skb->dev == NULL in outbound ARP packets */ - if (!dev) - return; - spin_lock_irqsave(&PRIV(dev)->xoff_lock, flags); - if (atm_may_send(vcc, 0)) { - old = xchg(&clip_vcc->xoff, 0); - if (old) - netif_wake_queue(dev); - } - spin_unlock_irqrestore(&PRIV(dev)->xoff_lock, flags); -} - -static void clip_neigh_solicit(struct neighbour *neigh, struct sk_buff *skb) -{ - __be32 *ip = (__be32 *) neigh->primary_key; - - pr_debug("(neigh %p, skb %p)\n", neigh, skb); - to_atmarpd(act_need, PRIV(neigh->dev)->number, *ip); -} - -static void clip_neigh_error(struct neighbour *neigh, struct sk_buff *skb) -{ -#ifndef CONFIG_ATM_CLIP_NO_ICMP - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); -#endif - kfree_skb(skb); -} - -static const struct neigh_ops clip_neigh_ops = { - .family = AF_INET, - .solicit = clip_neigh_solicit, - .error_report = clip_neigh_error, - .output = neigh_direct_output, - .connected_output = neigh_direct_output, -}; - -static int clip_constructor(struct net_device *dev, struct neighbour *neigh) -{ - struct atmarp_entry *entry = neighbour_priv(neigh); - - if (neigh->tbl->family != AF_INET) - return -EINVAL; - - if (neigh->type != RTN_UNICAST) - return -EINVAL; - - neigh->nud_state = NUD_NONE; - neigh->ops = &clip_neigh_ops; - neigh->output = neigh->ops->output; - entry->neigh = neigh; - entry->vccs = NULL; - entry->expires = jiffies - 1; - - return 0; -} - -/* @@@ copy bh locking from arp.c -- need to bh-enable atm code before */ - -/* - * We play with the resolve flag: 0 and 1 have the usual meaning, but -1 means - * to allocate the neighbour entry but not to ask atmarpd for resolution. Also, - * don't increment the usage count. This is used to create entries in - * clip_setentry. - */ - -static int clip_encap(struct atm_vcc *vcc, int mode) -{ - if (!CLIP_VCC(vcc)) - return -EBADFD; - - CLIP_VCC(vcc)->encap = mode; - return 0; -} - -static netdev_tx_t clip_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct clip_priv *clip_priv = PRIV(dev); - struct dst_entry *dst = skb_dst(skb); - struct atmarp_entry *entry; - struct neighbour *n; - struct atm_vcc *vcc; - struct rtable *rt; - __be32 *daddr; - int old; - unsigned long flags; - - pr_debug("(skb %p)\n", skb); - if (!dst) { - pr_err("skb_dst(skb) == NULL\n"); - dev_kfree_skb(skb); - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - rt = dst_rtable(dst); - if (rt->rt_gw_family == AF_INET) - daddr = &rt->rt_gw4; - else - daddr = &ip_hdr(skb)->daddr; - n = dst_neigh_lookup(dst, daddr); - if (!n) { - pr_err("NO NEIGHBOUR !\n"); - dev_kfree_skb(skb); - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - entry = neighbour_priv(n); - if (!entry->vccs) { - if (time_after(jiffies, entry->expires)) { - /* should be resolved */ - entry->expires = jiffies + ATMARP_RETRY_DELAY * HZ; - to_atmarpd(act_need, PRIV(dev)->number, *((__be32 *)n->primary_key)); - } - if (entry->neigh->arp_queue.qlen < ATMARP_MAX_UNRES_PACKETS) - skb_queue_tail(&entry->neigh->arp_queue, skb); - else { - dev_kfree_skb(skb); - dev->stats.tx_dropped++; - } - goto out_release_neigh; - } - pr_debug("neigh %p, vccs %p\n", entry, entry->vccs); - ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc; - pr_debug("using neighbour %p, vcc %p\n", n, vcc); - if (entry->vccs->encap) { - void *here; - - here = skb_push(skb, RFC1483LLC_LEN); - memcpy(here, llc_oui, sizeof(llc_oui)); - ((__be16 *) here)[3] = skb->protocol; - } - atm_account_tx(vcc, skb); - entry->vccs->last_use = jiffies; - pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, vcc, vcc->dev); - old = xchg(&entry->vccs->xoff, 1); /* assume XOFF ... */ - if (old) { - pr_warn("XOFF->XOFF transition\n"); - goto out_release_neigh; - } - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - vcc->send(vcc, skb); - if (atm_may_send(vcc, 0)) { - entry->vccs->xoff = 0; - goto out_release_neigh; - } - spin_lock_irqsave(&clip_priv->xoff_lock, flags); - netif_stop_queue(dev); /* XOFF -> throttle immediately */ - barrier(); - if (!entry->vccs->xoff) - netif_start_queue(dev); - /* Oh, we just raced with clip_pop. netif_start_queue should be - good enough, because nothing should really be asleep because - of the brief netif_stop_queue. If this isn't true or if it - changes, use netif_wake_queue instead. */ - spin_unlock_irqrestore(&clip_priv->xoff_lock, flags); -out_release_neigh: - neigh_release(n); - return NETDEV_TX_OK; -} - -static int clip_mkip(struct atm_vcc *vcc, int timeout) -{ - struct clip_vcc *clip_vcc; - - if (!vcc->push) - return -EBADFD; - if (vcc->user_back) - return -EINVAL; - clip_vcc = kmalloc_obj(struct clip_vcc); - if (!clip_vcc) - return -ENOMEM; - pr_debug("%p vcc %p\n", clip_vcc, vcc); - clip_vcc->vcc = vcc; - vcc->user_back = clip_vcc; - set_bit(ATM_VF_IS_CLIP, &vcc->flags); - clip_vcc->entry = NULL; - clip_vcc->xoff = 0; - clip_vcc->encap = 1; - clip_vcc->last_use = jiffies; - clip_vcc->idle_timeout = timeout * HZ; - clip_vcc->old_push = vcc->push; - clip_vcc->old_pop = vcc->pop; - vcc->push = clip_push; - vcc->pop = clip_pop; - - /* re-process everything received between connection setup and MKIP */ - vcc_process_recv_queue(vcc); - - return 0; -} - -static int clip_setentry(struct atm_vcc *vcc, __be32 ip) -{ - struct neighbour *neigh; - struct atmarp_entry *entry; - int error; - struct clip_vcc *clip_vcc; - struct rtable *rt; - - if (vcc->push != clip_push) { - pr_warn("non-CLIP VCC\n"); - return -EBADF; - } - clip_vcc = CLIP_VCC(vcc); - if (!ip) { - if (!clip_vcc->entry) { - pr_err("hiding hidden ATMARP entry\n"); - return 0; - } - pr_debug("remove\n"); - unlink_clip_vcc(clip_vcc); - return 0; - } - rt = ip_route_output(&init_net, ip, 0, 0, 0, RT_SCOPE_LINK); - if (IS_ERR(rt)) - return PTR_ERR(rt); - neigh = __neigh_lookup(&arp_tbl, &ip, rt->dst.dev, 1); - ip_rt_put(rt); - if (!neigh) - return -ENOMEM; - entry = neighbour_priv(neigh); - if (entry != clip_vcc->entry) { - if (!clip_vcc->entry) - pr_debug("add\n"); - else { - pr_debug("update\n"); - unlink_clip_vcc(clip_vcc); - } - link_vcc(clip_vcc, entry); - } - error = neigh_update(neigh, llc_oui, NUD_PERMANENT, - NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN, 0); - neigh_release(neigh); - return error; -} - -static const struct net_device_ops clip_netdev_ops = { - .ndo_start_xmit = clip_start_xmit, - .ndo_neigh_construct = clip_constructor, -}; - -static void clip_setup(struct net_device *dev) -{ - dev->netdev_ops = &clip_netdev_ops; - dev->type = ARPHRD_ATM; - dev->neigh_priv_len = sizeof(struct atmarp_entry); - dev->hard_header_len = RFC1483LLC_LEN; - dev->mtu = RFC1626_MTU; - dev->tx_queue_len = 100; /* "normal" queue (packets) */ - /* When using a "real" qdisc, the qdisc determines the queue */ - /* length. tx_queue_len is only used for the default case, */ - /* without any more elaborate queuing. 100 is a reasonable */ - /* compromise between decent burst-tolerance and protection */ - /* against memory hogs. */ - netif_keep_dst(dev); -} - -static int clip_create(int number) -{ - struct net_device *dev; - struct clip_priv *clip_priv; - int error; - - if (number != -1) { - for (dev = clip_devs; dev; dev = PRIV(dev)->next) - if (PRIV(dev)->number == number) - return -EEXIST; - } else { - number = 0; - for (dev = clip_devs; dev; dev = PRIV(dev)->next) - if (PRIV(dev)->number >= number) - number = PRIV(dev)->number + 1; - } - dev = alloc_netdev(sizeof(struct clip_priv), "", NET_NAME_UNKNOWN, - clip_setup); - if (!dev) - return -ENOMEM; - clip_priv = PRIV(dev); - sprintf(dev->name, "atm%d", number); - spin_lock_init(&clip_priv->xoff_lock); - clip_priv->number = number; - error = register_netdev(dev); - if (error) { - free_netdev(dev); - return error; - } - clip_priv->next = clip_devs; - clip_devs = dev; - pr_debug("registered (net:%s)\n", dev->name); - return number; -} - -static int clip_device_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (event == NETDEV_UNREGISTER) - return NOTIFY_DONE; - - /* ignore non-CLIP devices */ - if (dev->type != ARPHRD_ATM || dev->netdev_ops != &clip_netdev_ops) - return NOTIFY_DONE; - - switch (event) { - case NETDEV_UP: - pr_debug("NETDEV_UP\n"); - to_atmarpd(act_up, PRIV(dev)->number, 0); - break; - case NETDEV_GOING_DOWN: - pr_debug("NETDEV_DOWN\n"); - to_atmarpd(act_down, PRIV(dev)->number, 0); - break; - case NETDEV_CHANGE: - case NETDEV_CHANGEMTU: - pr_debug("NETDEV_CHANGE*\n"); - to_atmarpd(act_change, PRIV(dev)->number, 0); - break; - } - return NOTIFY_DONE; -} - -static int clip_inet_event(struct notifier_block *this, unsigned long event, - void *ifa) -{ - struct in_device *in_dev; - struct netdev_notifier_info info; - - in_dev = ((struct in_ifaddr *)ifa)->ifa_dev; - /* - * Transitions are of the down-change-up type, so it's sufficient to - * handle the change on up. - */ - if (event != NETDEV_UP) - return NOTIFY_DONE; - netdev_notifier_info_init(&info, in_dev->dev); - return clip_device_event(this, NETDEV_CHANGE, &info); -} - -static struct notifier_block clip_dev_notifier = { - .notifier_call = clip_device_event, -}; - - - -static struct notifier_block clip_inet_notifier = { - .notifier_call = clip_inet_event, -}; - - - -static void atmarpd_close(struct atm_vcc *vcc) -{ - pr_debug("\n"); - - mutex_lock(&atmarpd_lock); - RCU_INIT_POINTER(atmarpd, NULL); - mutex_unlock(&atmarpd_lock); - - synchronize_rcu(); - skb_queue_purge(&sk_atm(vcc)->sk_receive_queue); - - pr_debug("(done)\n"); - module_put(THIS_MODULE); -} - -static int atmarpd_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - atm_return_tx(vcc, skb); - dev_kfree_skb_any(skb); - return 0; -} - -static const struct atmdev_ops atmarpd_dev_ops = { - .close = atmarpd_close, - .send = atmarpd_send -}; - - -static struct atm_dev atmarpd_dev = { - .ops = &atmarpd_dev_ops, - .type = "arpd", - .number = 999, - .lock = __SPIN_LOCK_UNLOCKED(atmarpd_dev.lock) -}; - - -static int atm_init_atmarp(struct atm_vcc *vcc) -{ - if (vcc->push == clip_push) - return -EINVAL; - - mutex_lock(&atmarpd_lock); - if (atmarpd) { - mutex_unlock(&atmarpd_lock); - return -EADDRINUSE; - } - - mod_timer(&idle_timer, jiffies + CLIP_CHECK_INTERVAL * HZ); - - rcu_assign_pointer(atmarpd, vcc); - set_bit(ATM_VF_META, &vcc->flags); - set_bit(ATM_VF_READY, &vcc->flags); - /* allow replies and avoid getting closed if signaling dies */ - vcc->dev = &atmarpd_dev; - vcc_insert_socket(sk_atm(vcc)); - vcc->push = NULL; - vcc->pop = NULL; /* crash */ - vcc->push_oam = NULL; /* crash */ - mutex_unlock(&atmarpd_lock); - return 0; -} - -static int clip_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct atm_vcc *vcc = ATM_SD(sock); - struct sock *sk = sock->sk; - int err = 0; - - switch (cmd) { - case SIOCMKCLIP: - case ATMARPD_CTRL: - case ATMARP_MKIP: - case ATMARP_SETENTRY: - case ATMARP_ENCAP: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - break; - default: - return -ENOIOCTLCMD; - } - - switch (cmd) { - case SIOCMKCLIP: - err = clip_create(arg); - break; - case ATMARPD_CTRL: - lock_sock(sk); - err = atm_init_atmarp(vcc); - if (!err) { - sock->state = SS_CONNECTED; - __module_get(THIS_MODULE); - } - release_sock(sk); - break; - case ATMARP_MKIP: - lock_sock(sk); - err = clip_mkip(vcc, arg); - release_sock(sk); - break; - case ATMARP_SETENTRY: - err = clip_setentry(vcc, (__force __be32)arg); - break; - case ATMARP_ENCAP: - err = clip_encap(vcc, arg); - break; - } - return err; -} - -static struct atm_ioctl clip_ioctl_ops = { - .owner = THIS_MODULE, - .ioctl = clip_ioctl, -}; - -#ifdef CONFIG_PROC_FS - -static void svc_addr(struct seq_file *seq, struct sockaddr_atmsvc *addr) -{ - static int code[] = { 1, 2, 10, 6, 1, 0 }; - static int e164[] = { 1, 8, 4, 6, 1, 0 }; - - if (*addr->sas_addr.pub) { - seq_printf(seq, "%s", addr->sas_addr.pub); - if (*addr->sas_addr.prv) - seq_putc(seq, '+'); - } else if (!*addr->sas_addr.prv) { - seq_printf(seq, "%s", "(none)"); - return; - } - if (*addr->sas_addr.prv) { - unsigned char *prv = addr->sas_addr.prv; - int *fields; - int i, j; - - fields = *prv == ATM_AFI_E164 ? e164 : code; - for (i = 0; fields[i]; i++) { - for (j = fields[i]; j; j--) - seq_printf(seq, "%02X", *prv++); - if (fields[i + 1]) - seq_putc(seq, '.'); - } - } -} - -/* This means the neighbour entry has no attached VCC objects. */ -#define SEQ_NO_VCC_TOKEN ((void *) 2) - -static void atmarp_info(struct seq_file *seq, struct neighbour *n, - struct atmarp_entry *entry, struct clip_vcc *clip_vcc) -{ - struct net_device *dev = n->dev; - unsigned long exp; - char buf[17]; - int svc, llc, off; - - svc = ((clip_vcc == SEQ_NO_VCC_TOKEN) || - (sk_atm(clip_vcc->vcc)->sk_family == AF_ATMSVC)); - - llc = ((clip_vcc == SEQ_NO_VCC_TOKEN) || clip_vcc->encap); - - if (clip_vcc == SEQ_NO_VCC_TOKEN) - exp = entry->neigh->used; - else - exp = clip_vcc->last_use; - - exp = (jiffies - exp) / HZ; - - seq_printf(seq, "%-6s%-4s%-4s%5ld ", - dev->name, svc ? "SVC" : "PVC", llc ? "LLC" : "NULL", exp); - - off = scnprintf(buf, sizeof(buf) - 1, "%pI4", n->primary_key); - while (off < 16) - buf[off++] = ' '; - buf[off] = '\0'; - seq_printf(seq, "%s", buf); - - if (clip_vcc == SEQ_NO_VCC_TOKEN) { - if (time_before(jiffies, entry->expires)) - seq_printf(seq, "(resolving)\n"); - else - seq_printf(seq, "(expired, ref %d)\n", - refcount_read(&entry->neigh->refcnt)); - } else if (!svc) { - seq_printf(seq, "%d.%d.%d\n", - clip_vcc->vcc->dev->number, - clip_vcc->vcc->vpi, clip_vcc->vcc->vci); - } else { - svc_addr(seq, &clip_vcc->vcc->remote); - seq_putc(seq, '\n'); - } -} - -struct clip_seq_state { - /* This member must be first. */ - struct neigh_seq_state ns; - - /* Local to clip specific iteration. */ - struct clip_vcc *vcc; -}; - -static struct clip_vcc *clip_seq_next_vcc(struct atmarp_entry *e, - struct clip_vcc *curr) -{ - if (!curr) { - curr = e->vccs; - if (!curr) - return SEQ_NO_VCC_TOKEN; - return curr; - } - if (curr == SEQ_NO_VCC_TOKEN) - return NULL; - - curr = curr->next; - - return curr; -} - -static void *clip_seq_vcc_walk(struct clip_seq_state *state, - struct atmarp_entry *e, loff_t * pos) -{ - struct clip_vcc *vcc = state->vcc; - - vcc = clip_seq_next_vcc(e, vcc); - if (vcc && pos != NULL) { - while (*pos) { - vcc = clip_seq_next_vcc(e, vcc); - if (!vcc) - break; - --(*pos); - } - } - state->vcc = vcc; - - return vcc; -} - -static void *clip_seq_sub_iter(struct neigh_seq_state *_state, - struct neighbour *n, loff_t * pos) -{ - struct clip_seq_state *state = (struct clip_seq_state *)_state; - - if (n->dev->type != ARPHRD_ATM) - return NULL; - - return clip_seq_vcc_walk(state, neighbour_priv(n), pos); -} - -static void *clip_seq_start(struct seq_file *seq, loff_t * pos) -{ - struct clip_seq_state *state = seq->private; - state->ns.neigh_sub_iter = clip_seq_sub_iter; - return neigh_seq_start(seq, pos, &arp_tbl, NEIGH_SEQ_NEIGH_ONLY); -} - -static int clip_seq_show(struct seq_file *seq, void *v) -{ - static char atm_arp_banner[] = - "IPitf TypeEncp Idle IP address ATM address\n"; - - if (v == SEQ_START_TOKEN) { - seq_puts(seq, atm_arp_banner); - } else { - struct clip_seq_state *state = seq->private; - struct clip_vcc *vcc = state->vcc; - struct neighbour *n = v; - - atmarp_info(seq, n, neighbour_priv(n), vcc); - } - return 0; -} - -static const struct seq_operations arp_seq_ops = { - .start = clip_seq_start, - .next = neigh_seq_next, - .stop = neigh_seq_stop, - .show = clip_seq_show, -}; -#endif - -static void atm_clip_exit_noproc(void); - -static int __init atm_clip_init(void) -{ - register_atm_ioctl(&clip_ioctl_ops); - register_netdevice_notifier(&clip_dev_notifier); - register_inetaddr_notifier(&clip_inet_notifier); - - timer_setup(&idle_timer, idle_timer_check, 0); - -#ifdef CONFIG_PROC_FS - { - struct proc_dir_entry *p; - - p = proc_create_net("arp", 0444, atm_proc_root, &arp_seq_ops, - sizeof(struct clip_seq_state)); - if (!p) { - pr_err("Unable to initialize /proc/net/atm/arp\n"); - atm_clip_exit_noproc(); - return -ENOMEM; - } - } -#endif - - return 0; -} - -static void atm_clip_exit_noproc(void) -{ - struct net_device *dev, *next; - - unregister_inetaddr_notifier(&clip_inet_notifier); - unregister_netdevice_notifier(&clip_dev_notifier); - - deregister_atm_ioctl(&clip_ioctl_ops); - - /* First, stop the idle timer, so it stops banging - * on the table. - */ - timer_delete_sync(&idle_timer); - - dev = clip_devs; - while (dev) { - next = PRIV(dev)->next; - unregister_netdev(dev); - free_netdev(dev); - dev = next; - } -} - -static void __exit atm_clip_exit(void) -{ - remove_proc_entry("arp", atm_proc_root); - - atm_clip_exit_noproc(); -} - -module_init(atm_clip_init); -module_exit(atm_clip_exit); -MODULE_AUTHOR("Werner Almesberger"); -MODULE_DESCRIPTION("Classical/IP over ATM interface"); -MODULE_LICENSE("GPL"); diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c index 0f7a39aeccc8..0f3f9ad8301f 100644 --- a/net/atm/ioctl.c +++ b/net/atm/ioctl.c @@ -11,14 +11,10 @@ #include /* struct socket, struct proto_ops */ #include /* ATM stuff */ #include -#include /* CLIP_*ENCAP */ #include /* manifest constants */ #include #include /* for ioctls */ #include -#include -#include -#include #include #include #include @@ -138,16 +134,6 @@ static int do_vcc_ioctl(struct socket *sock, unsigned int cmd, } break; } - case ATMMPC_CTRL: - case ATMMPC_DATA: - request_module("mpoa"); - break; - case ATMARPD_CTRL: - request_module("clip"); - break; - case ATMLEC_CTRL: - request_module("lec"); - break; } error = -ENOIOCTLCMD; diff --git a/net/atm/lec.c b/net/atm/lec.c deleted file mode 100644 index 10e260acf602..000000000000 --- a/net/atm/lec.c +++ /dev/null @@ -1,2274 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * lec.c: Lan Emulation driver - * - * Marko Kiiskila - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ - -#include -#include -#include -#include - -/* We are ethernet device */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* And atm device */ -#include -#include - -/* Proxy LEC knows about bridging */ -#if IS_ENABLED(CONFIG_BRIDGE) -#include "../bridge/br_private.h" - -static unsigned char bridge_ula_lec[] = { 0x01, 0x80, 0xc2, 0x00, 0x00 }; -#endif - -/* Modular too */ -#include -#include - -/* Hardening for Spectre-v1 */ -#include - -#include "lec.h" -#include "lec_arpc.h" -#include "resources.h" - -#define DUMP_PACKETS 0 /* - * 0 = None, - * 1 = 30 first bytes - * 2 = Whole packet - */ - -#define LEC_UNRES_QUE_LEN 8 /* - * number of tx packets to queue for a - * single destination while waiting for SVC - */ - -static int lec_open(struct net_device *dev); -static netdev_tx_t lec_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static int lec_close(struct net_device *dev); -static struct lec_arp_table *lec_arp_find(struct lec_priv *priv, - const unsigned char *mac_addr); -static int lec_arp_remove(struct lec_priv *priv, - struct lec_arp_table *to_remove); -/* LANE2 functions */ -static void lane2_associate_ind(struct net_device *dev, const u8 *mac_address, - const u8 *tlvs, u32 sizeoftlvs); -static int lane2_resolve(struct net_device *dev, const u8 *dst_mac, int force, - u8 **tlvs, u32 *sizeoftlvs); -static int lane2_associate_req(struct net_device *dev, const u8 *lan_dst, - const u8 *tlvs, u32 sizeoftlvs); - -static int lec_addr_delete(struct lec_priv *priv, const unsigned char *atm_addr, - unsigned long permanent); -static void lec_arp_check_empties(struct lec_priv *priv, - struct atm_vcc *vcc, struct sk_buff *skb); -static void lec_arp_destroy(struct lec_priv *priv); -static void lec_arp_init(struct lec_priv *priv); -static struct atm_vcc *lec_arp_resolve(struct lec_priv *priv, - const unsigned char *mac_to_find, - int is_rdesc, - struct lec_arp_table **ret_entry); -static void lec_arp_update(struct lec_priv *priv, const unsigned char *mac_addr, - const unsigned char *atm_addr, - unsigned long remoteflag, - unsigned int targetless_le_arp); -static void lec_flush_complete(struct lec_priv *priv, unsigned long tran_id); -static int lec_mcast_make(struct lec_priv *priv, struct atm_vcc *vcc); -static void lec_set_flush_tran_id(struct lec_priv *priv, - const unsigned char *atm_addr, - unsigned long tran_id); -static void lec_vcc_added(struct lec_priv *priv, - const struct atmlec_ioc *ioc_data, - struct atm_vcc *vcc, - void (*old_push)(struct atm_vcc *vcc, - struct sk_buff *skb)); -static void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc); - -/* must be done under lec_arp_lock */ -static inline void lec_arp_hold(struct lec_arp_table *entry) -{ - refcount_inc(&entry->usage); -} - -static inline void lec_arp_put(struct lec_arp_table *entry) -{ - if (refcount_dec_and_test(&entry->usage)) - kfree(entry); -} - -static struct lane2_ops lane2_ops = { - .resolve = lane2_resolve, /* spec 3.1.3 */ - .associate_req = lane2_associate_req, /* spec 3.1.4 */ - .associate_indicator = NULL /* spec 3.1.5 */ -}; - -static unsigned char bus_mac[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - -/* Device structures */ -static struct net_device *dev_lec[MAX_LEC_ITF]; -static DEFINE_MUTEX(lec_mutex); - -#if IS_ENABLED(CONFIG_BRIDGE) -static void lec_handle_bridge(struct sk_buff *skb, struct net_device *dev) -{ - char *buff; - struct lec_priv *priv; - - /* - * Check if this is a BPDU. If so, ask zeppelin to send - * LE_TOPOLOGY_REQUEST with the same value of Topology Change bit - * as the Config BPDU has - */ - buff = skb->data + skb->dev->hard_header_len; - if (*buff++ == 0x42 && *buff++ == 0x42 && *buff++ == 0x03) { - struct sock *sk; - struct sk_buff *skb2; - struct atmlec_msg *mesg; - - skb2 = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC); - if (skb2 == NULL) - return; - skb2->len = sizeof(struct atmlec_msg); - mesg = (struct atmlec_msg *)skb2->data; - mesg->type = l_topology_change; - buff += 4; - mesg->content.normal.flag = *buff & 0x01; - /* 0x01 is topology change */ - - priv = netdev_priv(dev); - struct atm_vcc *vcc; - - rcu_read_lock(); - vcc = rcu_dereference(priv->lecd); - if (vcc) { - atm_force_charge(vcc, skb2->truesize); - sk = sk_atm(vcc); - skb_queue_tail(&sk->sk_receive_queue, skb2); - sk->sk_data_ready(sk); - } else { - dev_kfree_skb(skb2); - } - rcu_read_unlock(); - } -} -#endif /* IS_ENABLED(CONFIG_BRIDGE) */ - -/* - * Open/initialize the netdevice. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ - -static int lec_open(struct net_device *dev) -{ - netif_start_queue(dev); - - return 0; -} - -static void -lec_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - unsigned int len = skb->len; - - ATM_SKB(skb)->vcc = vcc; - atm_account_tx(vcc, skb); - - if (vcc->send(vcc, skb) < 0) { - dev->stats.tx_dropped++; - return; - } - - dev->stats.tx_packets++; - dev->stats.tx_bytes += len; -} - -static void lec_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - pr_info("%s\n", dev->name); - netif_trans_update(dev); - netif_wake_queue(dev); -} - -static netdev_tx_t lec_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct sk_buff *skb2; - struct lec_priv *priv = netdev_priv(dev); - struct lecdatahdr_8023 *lec_h; - struct atm_vcc *vcc; - struct lec_arp_table *entry; - unsigned char *dst; - int min_frame_size; - int is_rdesc; - - pr_debug("called\n"); - if (!rcu_access_pointer(priv->lecd)) { - pr_info("%s:No lecd attached\n", dev->name); - dev->stats.tx_errors++; - netif_stop_queue(dev); - kfree_skb(skb); - return NETDEV_TX_OK; - } - - pr_debug("skbuff head:%lx data:%lx tail:%lx end:%lx\n", - (long)skb->head, (long)skb->data, (long)skb_tail_pointer(skb), - (long)skb_end_pointer(skb)); -#if IS_ENABLED(CONFIG_BRIDGE) - if (memcmp(skb->data, bridge_ula_lec, sizeof(bridge_ula_lec)) == 0) - lec_handle_bridge(skb, dev); -#endif - - /* Make sure we have room for lec_id */ - if (skb_headroom(skb) < 2) { - pr_debug("reallocating skb\n"); - skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN); - if (unlikely(!skb2)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } - consume_skb(skb); - skb = skb2; - } - skb_push(skb, 2); - - /* Put le header to place */ - lec_h = (struct lecdatahdr_8023 *)skb->data; - lec_h->le_header = htons(priv->lecid); - -#if DUMP_PACKETS >= 2 -#define MAX_DUMP_SKB 99 -#elif DUMP_PACKETS >= 1 -#define MAX_DUMP_SKB 30 -#endif -#if DUMP_PACKETS >= 1 - printk(KERN_DEBUG "%s: send datalen:%ld lecid:%4.4x\n", - dev->name, skb->len, priv->lecid); - print_hex_dump(KERN_DEBUG, "", DUMP_OFFSET, 16, 1, - skb->data, min(skb->len, MAX_DUMP_SKB), true); -#endif /* DUMP_PACKETS >= 1 */ - - /* Minimum ethernet-frame size */ - min_frame_size = LEC_MINIMUM_8023_SIZE; - if (skb->len < min_frame_size) { - if ((skb->len + skb_tailroom(skb)) < min_frame_size) { - skb2 = skb_copy_expand(skb, 0, - min_frame_size - skb->truesize, - GFP_ATOMIC); - dev_kfree_skb(skb); - if (skb2 == NULL) { - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - skb = skb2; - } - skb_put(skb, min_frame_size - skb->len); - } - - /* Send to right vcc */ - is_rdesc = 0; - dst = lec_h->h_dest; - entry = NULL; - vcc = lec_arp_resolve(priv, dst, is_rdesc, &entry); - pr_debug("%s:vcc:%p vcc_flags:%lx, entry:%p\n", - dev->name, vcc, vcc ? vcc->flags : 0, entry); - if (!vcc || !test_bit(ATM_VF_READY, &vcc->flags)) { - if (entry && (entry->tx_wait.qlen < LEC_UNRES_QUE_LEN)) { - pr_debug("%s:queuing packet, MAC address %pM\n", - dev->name, lec_h->h_dest); - skb_queue_tail(&entry->tx_wait, skb); - } else { - pr_debug("%s:tx queue full or no arp entry, dropping, MAC address: %pM\n", - dev->name, lec_h->h_dest); - dev->stats.tx_dropped++; - dev_kfree_skb(skb); - } - goto out; - } -#if DUMP_PACKETS > 0 - printk(KERN_DEBUG "%s:sending to vpi:%d vci:%d\n", - dev->name, vcc->vpi, vcc->vci); -#endif /* DUMP_PACKETS > 0 */ - - while (entry && (skb2 = skb_dequeue(&entry->tx_wait))) { - pr_debug("emptying tx queue, MAC address %pM\n", lec_h->h_dest); - lec_send(vcc, skb2); - } - - lec_send(vcc, skb); - - if (!atm_may_send(vcc, 0)) { - struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc); - - vpriv->xoff = 1; - netif_stop_queue(dev); - - /* - * vcc->pop() might have occurred in between, making - * the vcc usuable again. Since xmit is serialized, - * this is the only situation we have to re-test. - */ - - if (atm_may_send(vcc, 0)) - netif_wake_queue(dev); - } - -out: - if (entry) - lec_arp_put(entry); - netif_trans_update(dev); - return NETDEV_TX_OK; -} - -/* The inverse routine to net_open(). */ -static int lec_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - -static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - static const u8 zero_addr[ETH_ALEN] = {}; - unsigned long flags; - struct net_device *dev = (struct net_device *)vcc->proto_data; - struct lec_priv *priv = netdev_priv(dev); - struct atmlec_msg *mesg; - struct lec_arp_table *entry; - char *tmp; /* FIXME */ - - WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc)); - mesg = (struct atmlec_msg *)skb->data; - tmp = skb->data; - tmp += sizeof(struct atmlec_msg); - pr_debug("%s: msg from zeppelin:%d\n", dev->name, mesg->type); - switch (mesg->type) { - case l_set_mac_addr: - eth_hw_addr_set(dev, mesg->content.normal.mac_addr); - break; - case l_del_mac_addr: - eth_hw_addr_set(dev, zero_addr); - break; - case l_addr_delete: - lec_addr_delete(priv, mesg->content.normal.atm_addr, - mesg->content.normal.flag); - break; - case l_topology_change: - priv->topology_change = mesg->content.normal.flag; - break; - case l_flush_complete: - lec_flush_complete(priv, mesg->content.normal.flag); - break; - case l_narp_req: /* LANE2: see 7.1.35 in the lane2 spec */ - spin_lock_irqsave(&priv->lec_arp_lock, flags); - entry = lec_arp_find(priv, mesg->content.normal.mac_addr); - lec_arp_remove(priv, entry); - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - - if (mesg->content.normal.no_source_le_narp) - break; - fallthrough; - case l_arp_update: - lec_arp_update(priv, mesg->content.normal.mac_addr, - mesg->content.normal.atm_addr, - mesg->content.normal.flag, - mesg->content.normal.targetless_le_arp); - pr_debug("in l_arp_update\n"); - if (mesg->sizeoftlvs != 0) { /* LANE2 3.1.5 */ - pr_debug("LANE2 3.1.5, got tlvs, size %d\n", - mesg->sizeoftlvs); - lane2_associate_ind(dev, mesg->content.normal.mac_addr, - tmp, mesg->sizeoftlvs); - } - break; - case l_config: - priv->maximum_unknown_frame_count = - mesg->content.config.maximum_unknown_frame_count; - priv->max_unknown_frame_time = - (mesg->content.config.max_unknown_frame_time * HZ); - priv->max_retry_count = mesg->content.config.max_retry_count; - priv->aging_time = (mesg->content.config.aging_time * HZ); - priv->forward_delay_time = - (mesg->content.config.forward_delay_time * HZ); - priv->arp_response_time = - (mesg->content.config.arp_response_time * HZ); - priv->flush_timeout = (mesg->content.config.flush_timeout * HZ); - priv->path_switching_delay = - (mesg->content.config.path_switching_delay * HZ); - priv->lane_version = mesg->content.config.lane_version; - /* LANE2 */ - priv->lane2_ops = NULL; - if (priv->lane_version > 1) - priv->lane2_ops = &lane2_ops; - rtnl_lock(); - if (dev_set_mtu(dev, mesg->content.config.mtu)) - pr_info("%s: change_mtu to %d failed\n", - dev->name, mesg->content.config.mtu); - rtnl_unlock(); - priv->is_proxy = mesg->content.config.is_proxy; - break; - case l_flush_tran_id: - lec_set_flush_tran_id(priv, mesg->content.normal.atm_addr, - mesg->content.normal.flag); - break; - case l_set_lecid: - priv->lecid = - (unsigned short)(0xffff & mesg->content.normal.flag); - break; - case l_should_bridge: -#if IS_ENABLED(CONFIG_BRIDGE) - { - pr_debug("%s: bridge zeppelin asks about %pM\n", - dev->name, mesg->content.proxy.mac_addr); - - if (br_fdb_test_addr_hook == NULL) - break; - - if (br_fdb_test_addr_hook(dev, mesg->content.proxy.mac_addr)) { - /* hit from bridge table, send LE_ARP_RESPONSE */ - struct sk_buff *skb2; - struct sock *sk; - - pr_debug("%s: entry found, responding to zeppelin\n", - dev->name); - skb2 = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC); - if (skb2 == NULL) - break; - skb2->len = sizeof(struct atmlec_msg); - skb_copy_to_linear_data(skb2, mesg, sizeof(*mesg)); - struct atm_vcc *vcc; - - rcu_read_lock(); - vcc = rcu_dereference(priv->lecd); - if (vcc) { - atm_force_charge(vcc, skb2->truesize); - sk = sk_atm(vcc); - skb_queue_tail(&sk->sk_receive_queue, skb2); - sk->sk_data_ready(sk); - } else { - dev_kfree_skb(skb2); - } - rcu_read_unlock(); - } - } -#endif /* IS_ENABLED(CONFIG_BRIDGE) */ - break; - default: - pr_info("%s: Unknown message type %d\n", dev->name, mesg->type); - dev_kfree_skb(skb); - return -EINVAL; - } - dev_kfree_skb(skb); - return 0; -} - -static void lec_atm_close(struct atm_vcc *vcc) -{ - struct net_device *dev = (struct net_device *)vcc->proto_data; - struct lec_priv *priv = netdev_priv(dev); - - rcu_assign_pointer(priv->lecd, NULL); - synchronize_rcu(); - /* Do something needful? */ - - netif_stop_queue(dev); - lec_arp_destroy(priv); - - pr_info("%s: Shut down!\n", dev->name); - module_put(THIS_MODULE); -} - -static const struct atmdev_ops lecdev_ops = { - .close = lec_atm_close, - .send = lec_atm_send -}; - -static struct atm_dev lecatm_dev = { - .ops = &lecdev_ops, - .type = "lec", - .number = 999, /* dummy device number */ - .lock = __SPIN_LOCK_UNLOCKED(lecatm_dev.lock) -}; - -/* - * LANE2: new argument struct sk_buff *data contains - * the LE_ARP based TLVs introduced in the LANE2 spec - */ -static int -send_to_lecd(struct lec_priv *priv, atmlec_msg_type type, - const unsigned char *mac_addr, const unsigned char *atm_addr, - struct sk_buff *data) -{ - struct atm_vcc *vcc; - struct sock *sk; - struct sk_buff *skb; - struct atmlec_msg *mesg; - - if (!priv || !rcu_access_pointer(priv->lecd)) - return -1; - - skb = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC); - if (!skb) - return -1; - skb->len = sizeof(struct atmlec_msg); - mesg = (struct atmlec_msg *)skb->data; - memset(mesg, 0, sizeof(struct atmlec_msg)); - mesg->type = type; - if (data != NULL) - mesg->sizeoftlvs = data->len; - if (mac_addr) - ether_addr_copy(mesg->content.normal.mac_addr, mac_addr); - else - mesg->content.normal.targetless_le_arp = 1; - if (atm_addr) - memcpy(&mesg->content.normal.atm_addr, atm_addr, ATM_ESA_LEN); - - rcu_read_lock(); - vcc = rcu_dereference(priv->lecd); - if (!vcc) { - rcu_read_unlock(); - kfree_skb(skb); - return -1; - } - - atm_force_charge(vcc, skb->truesize); - sk = sk_atm(vcc); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk); - - if (data != NULL) { - pr_debug("about to send %d bytes of data\n", data->len); - atm_force_charge(vcc, data->truesize); - skb_queue_tail(&sk->sk_receive_queue, data); - sk->sk_data_ready(sk); - } - - rcu_read_unlock(); - return 0; -} - -static void lec_set_multicast_list(struct net_device *dev) -{ - /* - * by default, all multicast frames arrive over the bus. - * eventually support selective multicast service - */ -} - -static const struct net_device_ops lec_netdev_ops = { - .ndo_open = lec_open, - .ndo_stop = lec_close, - .ndo_start_xmit = lec_start_xmit, - .ndo_tx_timeout = lec_tx_timeout, - .ndo_set_rx_mode = lec_set_multicast_list, -}; - -static const unsigned char lec_ctrl_magic[] = { - 0xff, - 0x00, - 0x01, - 0x01 -}; - -#define LEC_DATA_DIRECT_8023 2 -#define LEC_DATA_DIRECT_8025 3 - -static int lec_is_data_direct(struct atm_vcc *vcc) -{ - return ((vcc->sap.blli[0].l3.tr9577.snap[4] == LEC_DATA_DIRECT_8023) || - (vcc->sap.blli[0].l3.tr9577.snap[4] == LEC_DATA_DIRECT_8025)); -} - -static void lec_push(struct atm_vcc *vcc, struct sk_buff *skb) -{ - unsigned long flags; - struct net_device *dev = (struct net_device *)vcc->proto_data; - struct lec_priv *priv = netdev_priv(dev); - -#if DUMP_PACKETS > 0 - printk(KERN_DEBUG "%s: vcc vpi:%d vci:%d\n", - dev->name, vcc->vpi, vcc->vci); -#endif - if (!skb) { - pr_debug("%s: null skb\n", dev->name); - lec_vcc_close(priv, vcc); - return; - } -#if DUMP_PACKETS >= 2 -#define MAX_SKB_DUMP 99 -#elif DUMP_PACKETS >= 1 -#define MAX_SKB_DUMP 30 -#endif -#if DUMP_PACKETS > 0 - printk(KERN_DEBUG "%s: rcv datalen:%ld lecid:%4.4x\n", - dev->name, skb->len, priv->lecid); - print_hex_dump(KERN_DEBUG, "", DUMP_OFFSET, 16, 1, - skb->data, min(MAX_SKB_DUMP, skb->len), true); -#endif /* DUMP_PACKETS > 0 */ - if (memcmp(skb->data, lec_ctrl_magic, 4) == 0) { - /* Control frame, to daemon */ - struct sock *sk = sk_atm(vcc); - - pr_debug("%s: To daemon\n", dev->name); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk); - } else { /* Data frame, queue to protocol handlers */ - struct lec_arp_table *entry; - unsigned char *src, *dst; - - atm_return(vcc, skb->truesize); - if (*(__be16 *) skb->data == htons(priv->lecid) || - !rcu_access_pointer(priv->lecd) || !(dev->flags & IFF_UP)) { - /* - * Probably looping back, or if lecd is missing, - * lecd has gone down - */ - pr_debug("Ignoring frame...\n"); - dev_kfree_skb(skb); - return; - } - dst = ((struct lecdatahdr_8023 *)skb->data)->h_dest; - - /* - * If this is a Data Direct VCC, and the VCC does not match - * the LE_ARP cache entry, delete the LE_ARP cache entry. - */ - spin_lock_irqsave(&priv->lec_arp_lock, flags); - if (lec_is_data_direct(vcc)) { - src = ((struct lecdatahdr_8023 *)skb->data)->h_source; - entry = lec_arp_find(priv, src); - if (entry && entry->vcc != vcc) { - lec_arp_remove(priv, entry); - lec_arp_put(entry); - } - } - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - - if (!(dst[0] & 0x01) && /* Never filter Multi/Broadcast */ - !priv->is_proxy && /* Proxy wants all the packets */ - memcmp(dst, dev->dev_addr, dev->addr_len)) { - dev_kfree_skb(skb); - return; - } - if (!hlist_empty(&priv->lec_arp_empty_ones)) - lec_arp_check_empties(priv, vcc, skb); - skb_pull(skb, 2); /* skip lec_id */ - skb->protocol = eth_type_trans(skb, dev); - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data)); - netif_rx(skb); - } -} - -static void lec_pop(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc); - struct net_device *dev = skb->dev; - - if (vpriv == NULL) { - pr_info("vpriv = NULL!?!?!?\n"); - return; - } - - vpriv->old_pop(vcc, skb); - - if (vpriv->xoff && atm_may_send(vcc, 0)) { - vpriv->xoff = 0; - if (netif_running(dev) && netif_queue_stopped(dev)) - netif_wake_queue(dev); - } -} - -static int lec_vcc_attach(struct atm_vcc *vcc, void __user *arg) -{ - struct lec_vcc_priv *vpriv; - int bytes_left; - struct atmlec_ioc ioc_data; - - lockdep_assert_held(&lec_mutex); - /* Lecd must be up in this case */ - bytes_left = copy_from_user(&ioc_data, arg, sizeof(struct atmlec_ioc)); - if (bytes_left != 0) - pr_info("copy from user failed for %d bytes\n", bytes_left); - if (ioc_data.dev_num < 0 || ioc_data.dev_num >= MAX_LEC_ITF) - return -EINVAL; - ioc_data.dev_num = array_index_nospec(ioc_data.dev_num, MAX_LEC_ITF); - if (!dev_lec[ioc_data.dev_num]) - return -EINVAL; - vpriv = kmalloc_obj(struct lec_vcc_priv); - if (!vpriv) - return -ENOMEM; - vpriv->xoff = 0; - vpriv->old_pop = vcc->pop; - vcc->user_back = vpriv; - vcc->pop = lec_pop; - lec_vcc_added(netdev_priv(dev_lec[ioc_data.dev_num]), - &ioc_data, vcc, vcc->push); - vcc->proto_data = dev_lec[ioc_data.dev_num]; - vcc->push = lec_push; - return 0; -} - -static int lec_mcast_attach(struct atm_vcc *vcc, int arg) -{ - lockdep_assert_held(&lec_mutex); - if (arg < 0 || arg >= MAX_LEC_ITF) - return -EINVAL; - arg = array_index_nospec(arg, MAX_LEC_ITF); - if (!dev_lec[arg]) - return -EINVAL; - vcc->proto_data = dev_lec[arg]; - return lec_mcast_make(netdev_priv(dev_lec[arg]), vcc); -} - -/* Initialize device. */ -static int lecd_attach(struct atm_vcc *vcc, int arg) -{ - int i; - struct lec_priv *priv; - - lockdep_assert_held(&lec_mutex); - if (arg < 0) - arg = 0; - if (arg >= MAX_LEC_ITF) - return -EINVAL; - i = array_index_nospec(arg, MAX_LEC_ITF); - if (!dev_lec[i]) { - int size; - - size = sizeof(struct lec_priv); - dev_lec[i] = alloc_etherdev(size); - if (!dev_lec[i]) - return -ENOMEM; - dev_lec[i]->netdev_ops = &lec_netdev_ops; - dev_lec[i]->max_mtu = 18190; - snprintf(dev_lec[i]->name, IFNAMSIZ, "lec%d", i); - if (register_netdev(dev_lec[i])) { - free_netdev(dev_lec[i]); - dev_lec[i] = NULL; - return -EINVAL; - } - - priv = netdev_priv(dev_lec[i]); - } else { - priv = netdev_priv(dev_lec[i]); - if (rcu_access_pointer(priv->lecd)) - return -EADDRINUSE; - } - lec_arp_init(priv); - priv->itfnum = i; /* LANE2 addition */ - rcu_assign_pointer(priv->lecd, vcc); - vcc->dev = &lecatm_dev; - vcc_insert_socket(sk_atm(vcc)); - - vcc->proto_data = dev_lec[i]; - set_bit(ATM_VF_META, &vcc->flags); - set_bit(ATM_VF_READY, &vcc->flags); - - /* Set default values to these variables */ - priv->maximum_unknown_frame_count = 1; - priv->max_unknown_frame_time = (1 * HZ); - priv->vcc_timeout_period = (1200 * HZ); - priv->max_retry_count = 1; - priv->aging_time = (300 * HZ); - priv->forward_delay_time = (15 * HZ); - priv->topology_change = 0; - priv->arp_response_time = (1 * HZ); - priv->flush_timeout = (4 * HZ); - priv->path_switching_delay = (6 * HZ); - - if (dev_lec[i]->flags & IFF_UP) - netif_start_queue(dev_lec[i]); - __module_get(THIS_MODULE); - return i; -} - -#ifdef CONFIG_PROC_FS -static const char *lec_arp_get_status_string(unsigned char status) -{ - static const char *const lec_arp_status_string[] = { - "ESI_UNKNOWN ", - "ESI_ARP_PENDING ", - "ESI_VC_PENDING ", - " ", - "ESI_FLUSH_PENDING ", - "ESI_FORWARD_DIRECT" - }; - - if (status > ESI_FORWARD_DIRECT) - status = 3; /* ESI_UNDEFINED */ - return lec_arp_status_string[status]; -} - -static void lec_info(struct seq_file *seq, struct lec_arp_table *entry) -{ - seq_printf(seq, "%pM ", entry->mac_addr); - seq_printf(seq, "%*phN ", ATM_ESA_LEN, entry->atm_addr); - seq_printf(seq, "%s %4.4x", lec_arp_get_status_string(entry->status), - entry->flags & 0xffff); - if (entry->vcc) - seq_printf(seq, "%3d %3d ", entry->vcc->vpi, entry->vcc->vci); - else - seq_printf(seq, " "); - if (entry->recv_vcc) { - seq_printf(seq, " %3d %3d", entry->recv_vcc->vpi, - entry->recv_vcc->vci); - } - seq_putc(seq, '\n'); -} - -struct lec_state { - unsigned long flags; - struct lec_priv *locked; - struct hlist_node *node; - struct net_device *dev; - int itf; - int arp_table; - int misc_table; -}; - -static void *lec_tbl_walk(struct lec_state *state, struct hlist_head *tbl, - loff_t *l) -{ - struct hlist_node *e = state->node; - - if (!e) - e = tbl->first; - if (e == SEQ_START_TOKEN) { - e = tbl->first; - --*l; - } - - for (; e; e = e->next) { - if (--*l < 0) - break; - } - state->node = e; - - return (*l < 0) ? state : NULL; -} - -static void *lec_arp_walk(struct lec_state *state, loff_t *l, - struct lec_priv *priv) -{ - void *v = NULL; - int p; - - for (p = state->arp_table; p < LEC_ARP_TABLE_SIZE; p++) { - v = lec_tbl_walk(state, &priv->lec_arp_tables[p], l); - if (v) - break; - } - state->arp_table = p; - return v; -} - -static void *lec_misc_walk(struct lec_state *state, loff_t *l, - struct lec_priv *priv) -{ - struct hlist_head *lec_misc_tables[] = { - &priv->lec_arp_empty_ones, - &priv->lec_no_forward, - &priv->mcast_fwds - }; - void *v = NULL; - int q; - - for (q = state->misc_table; q < ARRAY_SIZE(lec_misc_tables); q++) { - v = lec_tbl_walk(state, lec_misc_tables[q], l); - if (v) - break; - } - state->misc_table = q; - return v; -} - -static void *lec_priv_walk(struct lec_state *state, loff_t *l, - struct lec_priv *priv) -{ - if (!state->locked) { - state->locked = priv; - spin_lock_irqsave(&priv->lec_arp_lock, state->flags); - } - if (!lec_arp_walk(state, l, priv) && !lec_misc_walk(state, l, priv)) { - spin_unlock_irqrestore(&priv->lec_arp_lock, state->flags); - state->locked = NULL; - /* Partial state reset for the next time we get called */ - state->arp_table = state->misc_table = 0; - } - return state->locked; -} - -static void *lec_itf_walk(struct lec_state *state, loff_t *l) -{ - struct net_device *dev; - void *v; - - dev = state->dev ? state->dev : dev_lec[state->itf]; - v = (dev && netdev_priv(dev)) ? - lec_priv_walk(state, l, netdev_priv(dev)) : NULL; - if (!v && dev) { - /* Partial state reset for the next time we get called */ - dev = NULL; - } - state->dev = dev; - return v; -} - -static void *lec_get_idx(struct lec_state *state, loff_t l) -{ - void *v = NULL; - - for (; state->itf < MAX_LEC_ITF; state->itf++) { - v = lec_itf_walk(state, &l); - if (v) - break; - } - return v; -} - -static void *lec_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct lec_state *state = seq->private; - - mutex_lock(&lec_mutex); - state->itf = 0; - state->dev = NULL; - state->locked = NULL; - state->arp_table = 0; - state->misc_table = 0; - state->node = SEQ_START_TOKEN; - - return *pos ? lec_get_idx(state, *pos) : SEQ_START_TOKEN; -} - -static void lec_seq_stop(struct seq_file *seq, void *v) -{ - struct lec_state *state = seq->private; - - if (state->dev) { - spin_unlock_irqrestore(&state->locked->lec_arp_lock, - state->flags); - state->dev = NULL; - } - mutex_unlock(&lec_mutex); -} - -static void *lec_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct lec_state *state = seq->private; - - ++*pos; - return lec_get_idx(state, 1); -} - -static int lec_seq_show(struct seq_file *seq, void *v) -{ - static const char lec_banner[] = - "Itf MAC ATM destination" - " Status Flags " - "VPI/VCI Recv VPI/VCI\n"; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, lec_banner); - else { - struct lec_state *state = seq->private; - struct net_device *dev = state->dev; - struct lec_arp_table *entry = hlist_entry(state->node, - struct lec_arp_table, - next); - - seq_printf(seq, "%s ", dev->name); - lec_info(seq, entry); - } - return 0; -} - -static const struct seq_operations lec_seq_ops = { - .start = lec_seq_start, - .next = lec_seq_next, - .stop = lec_seq_stop, - .show = lec_seq_show, -}; -#endif - -static int lane_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct atm_vcc *vcc = ATM_SD(sock); - int err = 0; - - switch (cmd) { - case ATMLEC_CTRL: - case ATMLEC_MCAST: - case ATMLEC_DATA: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - break; - default: - return -ENOIOCTLCMD; - } - - mutex_lock(&lec_mutex); - switch (cmd) { - case ATMLEC_CTRL: - err = lecd_attach(vcc, (int)arg); - if (err >= 0) - sock->state = SS_CONNECTED; - break; - case ATMLEC_MCAST: - err = lec_mcast_attach(vcc, (int)arg); - break; - case ATMLEC_DATA: - err = lec_vcc_attach(vcc, (void __user *)arg); - break; - } - - mutex_unlock(&lec_mutex); - return err; -} - -static struct atm_ioctl lane_ioctl_ops = { - .owner = THIS_MODULE, - .ioctl = lane_ioctl, -}; - -static int __init lane_module_init(void) -{ -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *p; - - p = proc_create_seq_private("lec", 0444, atm_proc_root, &lec_seq_ops, - sizeof(struct lec_state), NULL); - if (!p) { - pr_err("Unable to initialize /proc/net/atm/lec\n"); - return -ENOMEM; - } -#endif - - register_atm_ioctl(&lane_ioctl_ops); - pr_info("lec.c: initialized\n"); - return 0; -} - -static void __exit lane_module_cleanup(void) -{ - int i; - -#ifdef CONFIG_PROC_FS - remove_proc_entry("lec", atm_proc_root); -#endif - - deregister_atm_ioctl(&lane_ioctl_ops); - - for (i = 0; i < MAX_LEC_ITF; i++) { - if (dev_lec[i] != NULL) { - unregister_netdev(dev_lec[i]); - free_netdev(dev_lec[i]); - dev_lec[i] = NULL; - } - } -} - -module_init(lane_module_init); -module_exit(lane_module_cleanup); - -/* - * LANE2: 3.1.3, LE_RESOLVE.request - * Non force allocates memory and fills in *tlvs, fills in *sizeoftlvs. - * If sizeoftlvs == NULL the default TLVs associated with this - * lec will be used. - * If dst_mac == NULL, targetless LE_ARP will be sent - */ -static int lane2_resolve(struct net_device *dev, const u8 *dst_mac, int force, - u8 **tlvs, u32 *sizeoftlvs) -{ - unsigned long flags; - struct lec_priv *priv = netdev_priv(dev); - struct lec_arp_table *table; - struct sk_buff *skb; - int retval; - - if (force == 0) { - spin_lock_irqsave(&priv->lec_arp_lock, flags); - table = lec_arp_find(priv, dst_mac); - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - if (table == NULL) - return -1; - - *tlvs = kmemdup(table->tlvs, table->sizeoftlvs, GFP_ATOMIC); - if (*tlvs == NULL) - return -1; - - *sizeoftlvs = table->sizeoftlvs; - - return 0; - } - - if (sizeoftlvs == NULL) - retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, NULL); - - else { - skb = alloc_skb(*sizeoftlvs, GFP_ATOMIC); - if (skb == NULL) - return -1; - skb->len = *sizeoftlvs; - skb_copy_to_linear_data(skb, *tlvs, *sizeoftlvs); - retval = send_to_lecd(priv, l_arp_xmt, dst_mac, NULL, skb); - } - return retval; -} - -/* - * LANE2: 3.1.4, LE_ASSOCIATE.request - * Associate the *tlvs with the *lan_dst address. - * Will overwrite any previous association - * Returns 1 for success, 0 for failure (out of memory) - * - */ -static int lane2_associate_req(struct net_device *dev, const u8 *lan_dst, - const u8 *tlvs, u32 sizeoftlvs) -{ - int retval; - struct sk_buff *skb; - struct lec_priv *priv = netdev_priv(dev); - - if (!ether_addr_equal(lan_dst, dev->dev_addr)) - return 0; /* not our mac address */ - - kfree(priv->tlvs); /* NULL if there was no previous association */ - - priv->tlvs = kmemdup(tlvs, sizeoftlvs, GFP_KERNEL); - if (priv->tlvs == NULL) - return 0; - priv->sizeoftlvs = sizeoftlvs; - - skb = alloc_skb(sizeoftlvs, GFP_ATOMIC); - if (skb == NULL) - return 0; - skb->len = sizeoftlvs; - skb_copy_to_linear_data(skb, tlvs, sizeoftlvs); - retval = send_to_lecd(priv, l_associate_req, NULL, NULL, skb); - if (retval != 0) - pr_info("lec.c: lane2_associate_req() failed\n"); - /* - * If the previous association has changed we must - * somehow notify other LANE entities about the change - */ - return 1; -} - -/* - * LANE2: 3.1.5, LE_ASSOCIATE.indication - * - */ -static void lane2_associate_ind(struct net_device *dev, const u8 *mac_addr, - const u8 *tlvs, u32 sizeoftlvs) -{ -#if 0 - int i = 0; -#endif - struct lec_priv *priv = netdev_priv(dev); -#if 0 /* - * Why have the TLVs in LE_ARP entries - * since we do not use them? When you - * uncomment this code, make sure the - * TLVs get freed when entry is killed - */ - struct lec_arp_table *entry = lec_arp_find(priv, mac_addr); - - if (entry == NULL) - return; /* should not happen */ - - kfree(entry->tlvs); - - entry->tlvs = kmemdup(tlvs, sizeoftlvs, GFP_KERNEL); - if (entry->tlvs == NULL) - return; - entry->sizeoftlvs = sizeoftlvs; -#endif -#if 0 - pr_info("\n"); - pr_info("dump of tlvs, sizeoftlvs=%d\n", sizeoftlvs); - while (i < sizeoftlvs) - pr_cont("%02x ", tlvs[i++]); - - pr_cont("\n"); -#endif - - /* tell MPOA about the TLVs we saw */ - if (priv->lane2_ops && priv->lane2_ops->associate_indicator) { - priv->lane2_ops->associate_indicator(dev, mac_addr, - tlvs, sizeoftlvs); - } -} - -/* - * Here starts what used to lec_arpc.c - * - * lec_arpc.c was added here when making - * lane client modular. October 1997 - */ - -#include -#include -#include -#include -#include -#include - -#if 0 -#define pr_debug(format, args...) -/* - #define pr_debug printk -*/ -#endif -#define DEBUG_ARP_TABLE 0 - -#define LEC_ARP_REFRESH_INTERVAL (3*HZ) - -static void lec_arp_check_expire(struct work_struct *work); -static void lec_arp_expire_arp(struct timer_list *t); - -/* - * Arp table funcs - */ - -#define HASH(ch) (ch & (LEC_ARP_TABLE_SIZE - 1)) - -/* - * Initialization of arp-cache - */ -static void lec_arp_init(struct lec_priv *priv) -{ - unsigned short i; - - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) - INIT_HLIST_HEAD(&priv->lec_arp_tables[i]); - INIT_HLIST_HEAD(&priv->lec_arp_empty_ones); - INIT_HLIST_HEAD(&priv->lec_no_forward); - INIT_HLIST_HEAD(&priv->mcast_fwds); - spin_lock_init(&priv->lec_arp_lock); - INIT_DELAYED_WORK(&priv->lec_arp_work, lec_arp_check_expire); - schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL); -} - -static void lec_arp_clear_vccs(struct lec_arp_table *entry) -{ - if (entry->vcc) { - struct atm_vcc *vcc = entry->vcc; - struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc); - struct net_device *dev = (struct net_device *)vcc->proto_data; - - if (vpriv) { - vcc->pop = vpriv->old_pop; - if (vpriv->xoff) - netif_wake_queue(dev); - kfree(vpriv); - vcc->user_back = NULL; - vcc->push = entry->old_push; - vcc_release_async(vcc, -EPIPE); - } - entry->vcc = NULL; - } - if (entry->recv_vcc) { - struct atm_vcc *vcc = entry->recv_vcc; - struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc); - - if (vpriv) { - kfree(vpriv); - vcc->user_back = NULL; - - entry->recv_vcc->push = entry->old_recv_push; - vcc_release_async(entry->recv_vcc, -EPIPE); - } - entry->recv_vcc = NULL; - } -} - -/* - * Insert entry to lec_arp_table - * LANE2: Add to the end of the list to satisfy 8.1.13 - */ -static inline void -lec_arp_add(struct lec_priv *priv, struct lec_arp_table *entry) -{ - struct hlist_head *tmp; - - tmp = &priv->lec_arp_tables[HASH(entry->mac_addr[ETH_ALEN - 1])]; - hlist_add_head(&entry->next, tmp); - - pr_debug("Added entry:%pM\n", entry->mac_addr); -} - -/* - * Remove entry from lec_arp_table - */ -static int -lec_arp_remove(struct lec_priv *priv, struct lec_arp_table *to_remove) -{ - struct lec_arp_table *entry; - int i, remove_vcc = 1; - - if (!to_remove) - return -1; - - hlist_del(&to_remove->next); - timer_delete(&to_remove->timer); - - /* - * If this is the only MAC connected to this VCC, - * also tear down the VCC - */ - if (to_remove->status >= ESI_FLUSH_PENDING) { - /* - * ESI_FLUSH_PENDING, ESI_FORWARD_DIRECT - */ - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry(entry, - &priv->lec_arp_tables[i], next) { - if (memcmp(to_remove->atm_addr, - entry->atm_addr, ATM_ESA_LEN) == 0) { - remove_vcc = 0; - break; - } - } - } - if (remove_vcc) - lec_arp_clear_vccs(to_remove); - } - skb_queue_purge(&to_remove->tx_wait); /* FIXME: good place for this? */ - - pr_debug("Removed entry:%pM\n", to_remove->mac_addr); - return 0; -} - -#if DEBUG_ARP_TABLE -static const char *get_status_string(unsigned char st) -{ - switch (st) { - case ESI_UNKNOWN: - return "ESI_UNKNOWN"; - case ESI_ARP_PENDING: - return "ESI_ARP_PENDING"; - case ESI_VC_PENDING: - return "ESI_VC_PENDING"; - case ESI_FLUSH_PENDING: - return "ESI_FLUSH_PENDING"; - case ESI_FORWARD_DIRECT: - return "ESI_FORWARD_DIRECT"; - } - return ""; -} - -static void dump_arp_table(struct lec_priv *priv) -{ - struct lec_arp_table *rulla; - char buf[256]; - int i, offset; - - pr_info("Dump %p:\n", priv); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry(rulla, - &priv->lec_arp_tables[i], next) { - offset = 0; - offset += sprintf(buf, "%d: %p\n", i, rulla); - offset += sprintf(buf + offset, "Mac: %pM ", - rulla->mac_addr); - offset += sprintf(buf + offset, "Atm: %*ph ", ATM_ESA_LEN, - rulla->atm_addr); - offset += sprintf(buf + offset, - "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ", - rulla->vcc ? rulla->vcc->vpi : 0, - rulla->vcc ? rulla->vcc->vci : 0, - rulla->recv_vcc ? rulla->recv_vcc-> - vpi : 0, - rulla->recv_vcc ? rulla->recv_vcc-> - vci : 0, rulla->last_used, - rulla->timestamp, rulla->no_tries); - offset += - sprintf(buf + offset, - "Flags:%x, Packets_flooded:%x, Status: %s ", - rulla->flags, rulla->packets_flooded, - get_status_string(rulla->status)); - pr_info("%s\n", buf); - } - } - - if (!hlist_empty(&priv->lec_no_forward)) - pr_info("No forward\n"); - hlist_for_each_entry(rulla, &priv->lec_no_forward, next) { - offset = 0; - offset += sprintf(buf + offset, "Mac: %pM ", rulla->mac_addr); - offset += sprintf(buf + offset, "Atm: %*ph ", ATM_ESA_LEN, - rulla->atm_addr); - offset += sprintf(buf + offset, - "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ", - rulla->vcc ? rulla->vcc->vpi : 0, - rulla->vcc ? rulla->vcc->vci : 0, - rulla->recv_vcc ? rulla->recv_vcc->vpi : 0, - rulla->recv_vcc ? rulla->recv_vcc->vci : 0, - rulla->last_used, - rulla->timestamp, rulla->no_tries); - offset += sprintf(buf + offset, - "Flags:%x, Packets_flooded:%x, Status: %s ", - rulla->flags, rulla->packets_flooded, - get_status_string(rulla->status)); - pr_info("%s\n", buf); - } - - if (!hlist_empty(&priv->lec_arp_empty_ones)) - pr_info("Empty ones\n"); - hlist_for_each_entry(rulla, &priv->lec_arp_empty_ones, next) { - offset = 0; - offset += sprintf(buf + offset, "Mac: %pM ", rulla->mac_addr); - offset += sprintf(buf + offset, "Atm: %*ph ", ATM_ESA_LEN, - rulla->atm_addr); - offset += sprintf(buf + offset, - "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ", - rulla->vcc ? rulla->vcc->vpi : 0, - rulla->vcc ? rulla->vcc->vci : 0, - rulla->recv_vcc ? rulla->recv_vcc->vpi : 0, - rulla->recv_vcc ? rulla->recv_vcc->vci : 0, - rulla->last_used, - rulla->timestamp, rulla->no_tries); - offset += sprintf(buf + offset, - "Flags:%x, Packets_flooded:%x, Status: %s ", - rulla->flags, rulla->packets_flooded, - get_status_string(rulla->status)); - pr_info("%s", buf); - } - - if (!hlist_empty(&priv->mcast_fwds)) - pr_info("Multicast Forward VCCs\n"); - hlist_for_each_entry(rulla, &priv->mcast_fwds, next) { - offset = 0; - offset += sprintf(buf + offset, "Mac: %pM ", rulla->mac_addr); - offset += sprintf(buf + offset, "Atm: %*ph ", ATM_ESA_LEN, - rulla->atm_addr); - offset += sprintf(buf + offset, - "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ", - rulla->vcc ? rulla->vcc->vpi : 0, - rulla->vcc ? rulla->vcc->vci : 0, - rulla->recv_vcc ? rulla->recv_vcc->vpi : 0, - rulla->recv_vcc ? rulla->recv_vcc->vci : 0, - rulla->last_used, - rulla->timestamp, rulla->no_tries); - offset += sprintf(buf + offset, - "Flags:%x, Packets_flooded:%x, Status: %s ", - rulla->flags, rulla->packets_flooded, - get_status_string(rulla->status)); - pr_info("%s\n", buf); - } - -} -#else -#define dump_arp_table(priv) do { } while (0) -#endif - -/* - * Destruction of arp-cache - */ -static void lec_arp_destroy(struct lec_priv *priv) -{ - unsigned long flags; - struct hlist_node *next; - struct lec_arp_table *entry; - int i; - - cancel_delayed_work_sync(&priv->lec_arp_work); - - /* - * Remove all entries - */ - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_tables[i], next) { - lec_arp_remove(priv, entry); - lec_arp_put(entry); - } - INIT_HLIST_HEAD(&priv->lec_arp_tables[i]); - } - - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_empty_ones, next) { - timer_delete_sync(&entry->timer); - lec_arp_clear_vccs(entry); - hlist_del(&entry->next); - lec_arp_put(entry); - } - INIT_HLIST_HEAD(&priv->lec_arp_empty_ones); - - hlist_for_each_entry_safe(entry, next, - &priv->lec_no_forward, next) { - timer_delete_sync(&entry->timer); - lec_arp_clear_vccs(entry); - hlist_del(&entry->next); - lec_arp_put(entry); - } - INIT_HLIST_HEAD(&priv->lec_no_forward); - - hlist_for_each_entry_safe(entry, next, &priv->mcast_fwds, next) { - /* No timer, LANEv2 7.1.20 and 2.3.5.3 */ - lec_arp_clear_vccs(entry); - hlist_del(&entry->next); - lec_arp_put(entry); - } - INIT_HLIST_HEAD(&priv->mcast_fwds); - priv->mcast_vcc = NULL; - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); -} - -/* - * Find entry by mac_address - */ -static struct lec_arp_table *lec_arp_find(struct lec_priv *priv, - const unsigned char *mac_addr) -{ - struct hlist_head *head; - struct lec_arp_table *entry; - - pr_debug("%pM\n", mac_addr); - - head = &priv->lec_arp_tables[HASH(mac_addr[ETH_ALEN - 1])]; - hlist_for_each_entry(entry, head, next) { - if (ether_addr_equal(mac_addr, entry->mac_addr)) - return entry; - } - return NULL; -} - -static struct lec_arp_table *make_entry(struct lec_priv *priv, - const unsigned char *mac_addr) -{ - struct lec_arp_table *to_return; - - to_return = kzalloc_obj(struct lec_arp_table, GFP_ATOMIC); - if (!to_return) - return NULL; - ether_addr_copy(to_return->mac_addr, mac_addr); - INIT_HLIST_NODE(&to_return->next); - timer_setup(&to_return->timer, lec_arp_expire_arp, 0); - to_return->last_used = jiffies; - to_return->priv = priv; - skb_queue_head_init(&to_return->tx_wait); - refcount_set(&to_return->usage, 1); - return to_return; -} - -/* Arp sent timer expired */ -static void lec_arp_expire_arp(struct timer_list *t) -{ - struct lec_arp_table *entry; - - entry = timer_container_of(entry, t, timer); - - pr_debug("\n"); - if (entry->status == ESI_ARP_PENDING) { - if (entry->no_tries <= entry->priv->max_retry_count) { - if (entry->is_rdesc) - send_to_lecd(entry->priv, l_rdesc_arp_xmt, - entry->mac_addr, NULL, NULL); - else - send_to_lecd(entry->priv, l_arp_xmt, - entry->mac_addr, NULL, NULL); - entry->no_tries++; - } - mod_timer(&entry->timer, jiffies + (1 * HZ)); - } -} - -/* Unknown/unused vcc expire, remove associated entry */ -static void lec_arp_expire_vcc(struct timer_list *t) -{ - unsigned long flags; - struct lec_arp_table *to_remove = timer_container_of(to_remove, t, - timer); - struct lec_priv *priv = to_remove->priv; - - timer_delete(&to_remove->timer); - - pr_debug("%p %p: vpi:%d vci:%d\n", - to_remove, priv, - to_remove->vcc ? to_remove->recv_vcc->vpi : 0, - to_remove->vcc ? to_remove->recv_vcc->vci : 0); - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - hlist_del(&to_remove->next); - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - - lec_arp_clear_vccs(to_remove); - lec_arp_put(to_remove); -} - -static bool __lec_arp_check_expire(struct lec_arp_table *entry, - unsigned long now, - struct lec_priv *priv) -{ - unsigned long time_to_check; - - if ((entry->flags) & LEC_REMOTE_FLAG && priv->topology_change) - time_to_check = priv->forward_delay_time; - else - time_to_check = priv->aging_time; - - pr_debug("About to expire: %lx - %lx > %lx\n", - now, entry->last_used, time_to_check); - if (time_after(now, entry->last_used + time_to_check) && - !(entry->flags & LEC_PERMANENT_FLAG) && - !(entry->mac_addr[0] & 0x01)) { /* LANE2: 7.1.20 */ - /* Remove entry */ - pr_debug("Entry timed out\n"); - lec_arp_remove(priv, entry); - lec_arp_put(entry); - } else { - /* Something else */ - if ((entry->status == ESI_VC_PENDING || - entry->status == ESI_ARP_PENDING) && - time_after_eq(now, entry->timestamp + - priv->max_unknown_frame_time)) { - entry->timestamp = jiffies; - entry->packets_flooded = 0; - if (entry->status == ESI_VC_PENDING) - send_to_lecd(priv, l_svc_setup, - entry->mac_addr, - entry->atm_addr, - NULL); - } - if (entry->status == ESI_FLUSH_PENDING && - time_after_eq(now, entry->timestamp + - priv->path_switching_delay)) { - lec_arp_hold(entry); - return true; - } - } - - return false; -} -/* - * Expire entries. - * 1. Re-set timer - * 2. For each entry, delete entries that have aged past the age limit. - * 3. For each entry, depending on the status of the entry, perform - * the following maintenance. - * a. If status is ESI_VC_PENDING or ESI_ARP_PENDING then if the - * tick_count is above the max_unknown_frame_time, clear - * the tick_count to zero and clear the packets_flooded counter - * to zero. This supports the packet rate limit per address - * while flooding unknowns. - * b. If the status is ESI_FLUSH_PENDING and the tick_count is greater - * than or equal to the path_switching_delay, change the status - * to ESI_FORWARD_DIRECT. This causes the flush period to end - * regardless of the progress of the flush protocol. - */ -static void lec_arp_check_expire(struct work_struct *work) -{ - unsigned long flags; - struct lec_priv *priv = - container_of(work, struct lec_priv, lec_arp_work.work); - struct hlist_node *next; - struct lec_arp_table *entry; - unsigned long now; - int i; - - pr_debug("%p\n", priv); - now = jiffies; -restart: - spin_lock_irqsave(&priv->lec_arp_lock, flags); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_tables[i], next) { - if (__lec_arp_check_expire(entry, now, priv)) { - struct sk_buff *skb; - struct atm_vcc *vcc = entry->vcc; - - spin_unlock_irqrestore(&priv->lec_arp_lock, - flags); - while ((skb = skb_dequeue(&entry->tx_wait))) - lec_send(vcc, skb); - entry->last_used = jiffies; - entry->status = ESI_FORWARD_DIRECT; - lec_arp_put(entry); - - goto restart; - } - } - } - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - - schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL); -} - -/* - * Try to find vcc where mac_address is attached. - * - */ -static struct atm_vcc *lec_arp_resolve(struct lec_priv *priv, - const unsigned char *mac_to_find, - int is_rdesc, - struct lec_arp_table **ret_entry) -{ - unsigned long flags; - struct lec_arp_table *entry; - struct atm_vcc *found; - - if (mac_to_find[0] & 0x01) { - switch (priv->lane_version) { - case 1: - return priv->mcast_vcc; - case 2: /* LANE2 wants arp for multicast addresses */ - if (ether_addr_equal(mac_to_find, bus_mac)) - return priv->mcast_vcc; - break; - default: - break; - } - } - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - entry = lec_arp_find(priv, mac_to_find); - - if (entry) { - if (entry->status == ESI_FORWARD_DIRECT) { - /* Connection Ok */ - entry->last_used = jiffies; - lec_arp_hold(entry); - *ret_entry = entry; - found = entry->vcc; - goto out; - } - /* - * If the LE_ARP cache entry is still pending, reset count to 0 - * so another LE_ARP request can be made for this frame. - */ - if (entry->status == ESI_ARP_PENDING) - entry->no_tries = 0; - /* - * Data direct VC not yet set up, check to see if the unknown - * frame count is greater than the limit. If the limit has - * not been reached, allow the caller to send packet to - * BUS. - */ - if (entry->status != ESI_FLUSH_PENDING && - entry->packets_flooded < - priv->maximum_unknown_frame_count) { - entry->packets_flooded++; - pr_debug("Flooding..\n"); - found = priv->mcast_vcc; - goto out; - } - /* - * We got here because entry->status == ESI_FLUSH_PENDING - * or BUS flood limit was reached for an entry which is - * in ESI_ARP_PENDING or ESI_VC_PENDING state. - */ - lec_arp_hold(entry); - *ret_entry = entry; - pr_debug("entry->status %d entry->vcc %p\n", entry->status, - entry->vcc); - found = NULL; - } else { - /* No matching entry was found */ - entry = make_entry(priv, mac_to_find); - pr_debug("Making entry\n"); - if (!entry) { - found = priv->mcast_vcc; - goto out; - } - lec_arp_add(priv, entry); - /* We want arp-request(s) to be sent */ - entry->packets_flooded = 1; - entry->status = ESI_ARP_PENDING; - entry->no_tries = 1; - entry->last_used = entry->timestamp = jiffies; - entry->is_rdesc = is_rdesc; - if (entry->is_rdesc) - send_to_lecd(priv, l_rdesc_arp_xmt, mac_to_find, NULL, - NULL); - else - send_to_lecd(priv, l_arp_xmt, mac_to_find, NULL, NULL); - entry->timer.expires = jiffies + (1 * HZ); - entry->timer.function = lec_arp_expire_arp; - add_timer(&entry->timer); - found = priv->mcast_vcc; - } - -out: - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - return found; -} - -static int -lec_addr_delete(struct lec_priv *priv, const unsigned char *atm_addr, - unsigned long permanent) -{ - unsigned long flags; - struct hlist_node *next; - struct lec_arp_table *entry; - int i; - - pr_debug("\n"); - spin_lock_irqsave(&priv->lec_arp_lock, flags); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_tables[i], next) { - if (!memcmp(atm_addr, entry->atm_addr, ATM_ESA_LEN) && - (permanent || - !(entry->flags & LEC_PERMANENT_FLAG))) { - lec_arp_remove(priv, entry); - lec_arp_put(entry); - } - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - return 0; - } - } - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - return -1; -} - -/* - * Notifies: Response to arp_request (atm_addr != NULL) - */ -static void -lec_arp_update(struct lec_priv *priv, const unsigned char *mac_addr, - const unsigned char *atm_addr, unsigned long remoteflag, - unsigned int targetless_le_arp) -{ - unsigned long flags; - struct hlist_node *next; - struct lec_arp_table *entry, *tmp; - int i; - - pr_debug("%smac:%pM\n", - (targetless_le_arp) ? "targetless " : "", mac_addr); - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - entry = lec_arp_find(priv, mac_addr); - if (entry == NULL && targetless_le_arp) - goto out; /* - * LANE2: ignore targetless LE_ARPs for which - * we have no entry in the cache. 7.1.30 - */ - if (!hlist_empty(&priv->lec_arp_empty_ones)) { - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_empty_ones, next) { - if (memcmp(entry->atm_addr, atm_addr, ATM_ESA_LEN) == 0) { - hlist_del(&entry->next); - timer_delete(&entry->timer); - tmp = lec_arp_find(priv, mac_addr); - if (tmp) { - timer_delete(&tmp->timer); - tmp->status = ESI_FORWARD_DIRECT; - memcpy(tmp->atm_addr, atm_addr, ATM_ESA_LEN); - tmp->vcc = entry->vcc; - tmp->old_push = entry->old_push; - tmp->last_used = jiffies; - timer_delete(&entry->timer); - lec_arp_put(entry); - entry = tmp; - } else { - entry->status = ESI_FORWARD_DIRECT; - ether_addr_copy(entry->mac_addr, - mac_addr); - entry->last_used = jiffies; - lec_arp_add(priv, entry); - } - if (remoteflag) - entry->flags |= LEC_REMOTE_FLAG; - else - entry->flags &= ~LEC_REMOTE_FLAG; - pr_debug("After update\n"); - dump_arp_table(priv); - goto out; - } - } - } - - entry = lec_arp_find(priv, mac_addr); - if (!entry) { - entry = make_entry(priv, mac_addr); - if (!entry) - goto out; - entry->status = ESI_UNKNOWN; - lec_arp_add(priv, entry); - /* Temporary, changes before end of function */ - } - memcpy(entry->atm_addr, atm_addr, ATM_ESA_LEN); - timer_delete(&entry->timer); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry(tmp, - &priv->lec_arp_tables[i], next) { - if (entry != tmp && - !memcmp(tmp->atm_addr, atm_addr, ATM_ESA_LEN)) { - /* Vcc to this host exists */ - if (tmp->status > ESI_VC_PENDING) { - /* - * ESI_FLUSH_PENDING, - * ESI_FORWARD_DIRECT - */ - entry->vcc = tmp->vcc; - entry->old_push = tmp->old_push; - } - entry->status = tmp->status; - break; - } - } - } - if (remoteflag) - entry->flags |= LEC_REMOTE_FLAG; - else - entry->flags &= ~LEC_REMOTE_FLAG; - if (entry->status == ESI_ARP_PENDING || entry->status == ESI_UNKNOWN) { - entry->status = ESI_VC_PENDING; - send_to_lecd(priv, l_svc_setup, entry->mac_addr, atm_addr, NULL); - } - pr_debug("After update2\n"); - dump_arp_table(priv); -out: - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); -} - -/* - * Notifies: Vcc setup ready - */ -static void -lec_vcc_added(struct lec_priv *priv, const struct atmlec_ioc *ioc_data, - struct atm_vcc *vcc, - void (*old_push) (struct atm_vcc *vcc, struct sk_buff *skb)) -{ - unsigned long flags; - struct lec_arp_table *entry; - int i, found_entry = 0; - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - /* Vcc for Multicast Forward. No timer, LANEv2 7.1.20 and 2.3.5.3 */ - if (ioc_data->receive == 2) { - pr_debug("LEC_ARP: Attaching mcast forward\n"); -#if 0 - entry = lec_arp_find(priv, bus_mac); - if (!entry) { - pr_info("LEC_ARP: Multicast entry not found!\n"); - goto out; - } - memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); - entry->recv_vcc = vcc; - entry->old_recv_push = old_push; -#endif - entry = make_entry(priv, bus_mac); - if (entry == NULL) - goto out; - timer_delete(&entry->timer); - memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); - entry->recv_vcc = vcc; - entry->old_recv_push = old_push; - hlist_add_head(&entry->next, &priv->mcast_fwds); - goto out; - } else if (ioc_data->receive == 1) { - /* - * Vcc which we don't want to make default vcc, - * attach it anyway. - */ - pr_debug("LEC_ARP:Attaching data direct, not default: %*phN\n", - ATM_ESA_LEN, ioc_data->atm_addr); - entry = make_entry(priv, bus_mac); - if (entry == NULL) - goto out; - memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); - eth_zero_addr(entry->mac_addr); - entry->recv_vcc = vcc; - entry->old_recv_push = old_push; - entry->status = ESI_UNKNOWN; - entry->timer.expires = jiffies + priv->vcc_timeout_period; - entry->timer.function = lec_arp_expire_vcc; - hlist_add_head(&entry->next, &priv->lec_no_forward); - add_timer(&entry->timer); - dump_arp_table(priv); - goto out; - } - pr_debug("LEC_ARP:Attaching data direct, default: %*phN\n", - ATM_ESA_LEN, ioc_data->atm_addr); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry(entry, - &priv->lec_arp_tables[i], next) { - if (memcmp - (ioc_data->atm_addr, entry->atm_addr, - ATM_ESA_LEN) == 0) { - pr_debug("LEC_ARP: Attaching data direct\n"); - pr_debug("Currently -> Vcc: %d, Rvcc:%d\n", - entry->vcc ? entry->vcc->vci : 0, - entry->recv_vcc ? entry->recv_vcc-> - vci : 0); - found_entry = 1; - timer_delete(&entry->timer); - entry->vcc = vcc; - entry->old_push = old_push; - if (entry->status == ESI_VC_PENDING) { - if (priv->maximum_unknown_frame_count - == 0) - entry->status = - ESI_FORWARD_DIRECT; - else { - entry->timestamp = jiffies; - entry->status = - ESI_FLUSH_PENDING; -#if 0 - send_to_lecd(priv, l_flush_xmt, - NULL, - entry->atm_addr, - NULL); -#endif - } - } else { - /* - * They were forming a connection - * to us, and we to them. Our - * ATM address is numerically lower - * than theirs, so we make connection - * we formed into default VCC (8.1.11). - * Connection they made gets torn - * down. This might confuse some - * clients. Can be changed if - * someone reports trouble... - */ - ; - } - } - } - } - if (found_entry) { - pr_debug("After vcc was added\n"); - dump_arp_table(priv); - goto out; - } - /* - * Not found, snatch address from first data packet that arrives - * from this vcc - */ - entry = make_entry(priv, bus_mac); - if (!entry) - goto out; - entry->vcc = vcc; - entry->old_push = old_push; - memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN); - eth_zero_addr(entry->mac_addr); - entry->status = ESI_UNKNOWN; - hlist_add_head(&entry->next, &priv->lec_arp_empty_ones); - entry->timer.expires = jiffies + priv->vcc_timeout_period; - entry->timer.function = lec_arp_expire_vcc; - add_timer(&entry->timer); - pr_debug("After vcc was added\n"); - dump_arp_table(priv); -out: - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); -} - -static void lec_flush_complete(struct lec_priv *priv, unsigned long tran_id) -{ - unsigned long flags; - struct lec_arp_table *entry; - int i; - - pr_debug("%lx\n", tran_id); -restart: - spin_lock_irqsave(&priv->lec_arp_lock, flags); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry(entry, - &priv->lec_arp_tables[i], next) { - if (entry->flush_tran_id == tran_id && - entry->status == ESI_FLUSH_PENDING) { - struct sk_buff *skb; - struct atm_vcc *vcc = entry->vcc; - - lec_arp_hold(entry); - spin_unlock_irqrestore(&priv->lec_arp_lock, - flags); - while ((skb = skb_dequeue(&entry->tx_wait))) - lec_send(vcc, skb); - entry->last_used = jiffies; - entry->status = ESI_FORWARD_DIRECT; - lec_arp_put(entry); - pr_debug("LEC_ARP: Flushed\n"); - goto restart; - } - } - } - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - dump_arp_table(priv); -} - -static void -lec_set_flush_tran_id(struct lec_priv *priv, - const unsigned char *atm_addr, unsigned long tran_id) -{ - unsigned long flags; - struct lec_arp_table *entry; - int i; - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) - hlist_for_each_entry(entry, - &priv->lec_arp_tables[i], next) { - if (!memcmp(atm_addr, entry->atm_addr, ATM_ESA_LEN)) { - entry->flush_tran_id = tran_id; - pr_debug("Set flush transaction id to %lx for %p\n", - tran_id, entry); - } - } - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); -} - -static int lec_mcast_make(struct lec_priv *priv, struct atm_vcc *vcc) -{ - unsigned long flags; - unsigned char mac_addr[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff - }; - struct lec_arp_table *to_add; - struct lec_vcc_priv *vpriv; - int err = 0; - - vpriv = kmalloc_obj(struct lec_vcc_priv); - if (!vpriv) - return -ENOMEM; - vpriv->xoff = 0; - vpriv->old_pop = vcc->pop; - vcc->user_back = vpriv; - vcc->pop = lec_pop; - spin_lock_irqsave(&priv->lec_arp_lock, flags); - to_add = make_entry(priv, mac_addr); - if (!to_add) { - vcc->pop = vpriv->old_pop; - kfree(vpriv); - err = -ENOMEM; - goto out; - } - memcpy(to_add->atm_addr, vcc->remote.sas_addr.prv, ATM_ESA_LEN); - to_add->status = ESI_FORWARD_DIRECT; - to_add->flags |= LEC_PERMANENT_FLAG; - to_add->vcc = vcc; - to_add->old_push = vcc->push; - vcc->push = lec_push; - priv->mcast_vcc = vcc; - lec_arp_add(priv, to_add); -out: - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - return err; -} - -static void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc) -{ - unsigned long flags; - struct hlist_node *next; - struct lec_arp_table *entry; - int i; - - pr_debug("LEC_ARP: lec_vcc_close vpi:%d vci:%d\n", vcc->vpi, vcc->vci); - dump_arp_table(priv); - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - - for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) { - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_tables[i], next) { - if (vcc == entry->vcc) { - lec_arp_remove(priv, entry); - lec_arp_put(entry); - if (priv->mcast_vcc == vcc) - priv->mcast_vcc = NULL; - } - } - } - - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_empty_ones, next) { - if (entry->vcc == vcc) { - lec_arp_clear_vccs(entry); - timer_delete(&entry->timer); - hlist_del(&entry->next); - lec_arp_put(entry); - } - } - - hlist_for_each_entry_safe(entry, next, - &priv->lec_no_forward, next) { - if (entry->recv_vcc == vcc) { - lec_arp_clear_vccs(entry); - timer_delete(&entry->timer); - hlist_del(&entry->next); - lec_arp_put(entry); - } - } - - hlist_for_each_entry_safe(entry, next, &priv->mcast_fwds, next) { - if (entry->recv_vcc == vcc) { - lec_arp_clear_vccs(entry); - /* No timer, LANEv2 7.1.20 and 2.3.5.3 */ - hlist_del(&entry->next); - lec_arp_put(entry); - } - } - - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); - dump_arp_table(priv); -} - -static void -lec_arp_check_empties(struct lec_priv *priv, - struct atm_vcc *vcc, struct sk_buff *skb) -{ - unsigned long flags; - struct hlist_node *next; - struct lec_arp_table *entry, *tmp; - struct lecdatahdr_8023 *hdr = (struct lecdatahdr_8023 *)skb->data; - unsigned char *src = hdr->h_source; - - spin_lock_irqsave(&priv->lec_arp_lock, flags); - hlist_for_each_entry_safe(entry, next, - &priv->lec_arp_empty_ones, next) { - if (vcc == entry->vcc) { - timer_delete(&entry->timer); - ether_addr_copy(entry->mac_addr, src); - entry->status = ESI_FORWARD_DIRECT; - entry->last_used = jiffies; - /* We might have got an entry */ - tmp = lec_arp_find(priv, src); - if (tmp) { - lec_arp_remove(priv, tmp); - lec_arp_put(tmp); - } - hlist_del(&entry->next); - lec_arp_add(priv, entry); - goto out; - } - } - pr_debug("LEC_ARP: Arp_check_empties: entry not found!\n"); -out: - spin_unlock_irqrestore(&priv->lec_arp_lock, flags); -} - -MODULE_DESCRIPTION("ATM LAN Emulation (LANE) support"); -MODULE_LICENSE("GPL"); diff --git a/net/atm/lec.h b/net/atm/lec.h deleted file mode 100644 index ec85709bf818..000000000000 --- a/net/atm/lec.h +++ /dev/null @@ -1,155 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Lan Emulation client header file - * - * Marko Kiiskila - */ - -#ifndef _LEC_H_ -#define _LEC_H_ - -#include -#include -#include - -#define LEC_HEADER_LEN 16 - -struct lecdatahdr_8023 { - __be16 le_header; - unsigned char h_dest[ETH_ALEN]; - unsigned char h_source[ETH_ALEN]; - __be16 h_type; -}; - -struct lecdatahdr_8025 { - __be16 le_header; - unsigned char ac_pad; - unsigned char fc; - unsigned char h_dest[ETH_ALEN]; - unsigned char h_source[ETH_ALEN]; -}; - -#define LEC_MINIMUM_8023_SIZE 62 -#define LEC_MINIMUM_8025_SIZE 16 - -/* - * Operations that LANE2 capable device can do. Two first functions - * are used to make the device do things. See spec 3.1.3 and 3.1.4. - * - * The third function is intended for the MPOA component sitting on - * top of the LANE device. The MPOA component assigns it's own function - * to (*associate_indicator)() and the LANE device will use that - * function to tell about TLVs it sees floating through. - * - */ -struct lane2_ops { - int (*resolve) (struct net_device *dev, const u8 *dst_mac, int force, - u8 **tlvs, u32 *sizeoftlvs); - int (*associate_req) (struct net_device *dev, const u8 *lan_dst, - const u8 *tlvs, u32 sizeoftlvs); - void (*associate_indicator) (struct net_device *dev, const u8 *mac_addr, - const u8 *tlvs, u32 sizeoftlvs); -}; - -/* - * ATM LAN Emulation supports both LLC & Dix Ethernet EtherType - * frames. - * - * 1. Dix Ethernet EtherType frames encoded by placing EtherType - * field in h_type field. Data follows immediately after header. - * 2. LLC Data frames whose total length, including LLC field and data, - * but not padding required to meet the minimum data frame length, - * is less than ETH_P_802_3_MIN MUST be encoded by placing that length - * in the h_type field. The LLC field follows header immediately. - * 3. LLC data frames longer than this maximum MUST be encoded by placing - * the value 0 in the h_type field. - * - */ - -/* Hash table size */ -#define LEC_ARP_TABLE_SIZE 16 - -struct lec_priv { - unsigned short lecid; /* Lecid of this client */ - struct hlist_head lec_arp_empty_ones; - /* Used for storing VCC's that don't have a MAC address attached yet */ - struct hlist_head lec_arp_tables[LEC_ARP_TABLE_SIZE]; - /* Actual LE ARP table */ - struct hlist_head lec_no_forward; - /* - * Used for storing VCC's (and forward packets from) which are to - * age out by not using them to forward packets. - * This is because to some LE clients there will be 2 VCCs. Only - * one of them gets used. - */ - struct hlist_head mcast_fwds; - /* - * With LANEv2 it is possible that BUS (or a special multicast server) - * establishes multiple Multicast Forward VCCs to us. This list - * collects all those VCCs. LANEv1 client has only one item in this - * list. These entries are not aged out. - */ - spinlock_t lec_arp_lock; - struct atm_vcc *mcast_vcc; /* Default Multicast Send VCC */ - struct atm_vcc __rcu *lecd; - struct delayed_work lec_arp_work; /* C10 */ - unsigned int maximum_unknown_frame_count; - /* - * Within the period of time defined by this variable, the client will send - * no more than C10 frames to BUS for a given unicast destination. (C11) - */ - unsigned long max_unknown_frame_time; - /* - * If no traffic has been sent in this vcc for this period of time, - * vcc will be torn down (C12) - */ - unsigned long vcc_timeout_period; - /* - * An LE Client MUST not retry an LE_ARP_REQUEST for a - * given frame's LAN Destination more than maximum retry count times, - * after the first LEC_ARP_REQUEST (C13) - */ - unsigned short max_retry_count; - /* - * Max time the client will maintain an entry in its arp cache in - * absence of a verification of that relationship (C17) - */ - unsigned long aging_time; - /* - * Max time the client will maintain an entry in cache when - * topology change flag is true (C18) - */ - unsigned long forward_delay_time; /* Topology change flag (C19) */ - int topology_change; - /* - * Max time the client expects an LE_ARP_REQUEST/LE_ARP_RESPONSE - * cycle to take (C20) - */ - unsigned long arp_response_time; - /* - * Time limit ot wait to receive an LE_FLUSH_RESPONSE after the - * LE_FLUSH_REQUEST has been sent before taking recover action. (C21) - */ - unsigned long flush_timeout; - /* The time since sending a frame to the bus after which the - * LE Client may assume that the frame has been either discarded or - * delivered to the recipient (C22) - */ - unsigned long path_switching_delay; - - u8 *tlvs; /* LANE2: TLVs are new */ - u32 sizeoftlvs; /* The size of the tlv array in bytes */ - int lane_version; /* LANE2 */ - int itfnum; /* e.g. 2 for lec2, 5 for lec5 */ - struct lane2_ops *lane2_ops; /* can be NULL for LANE v1 */ - int is_proxy; /* bridge between ATM and Ethernet */ -}; - -struct lec_vcc_priv { - void (*old_pop) (struct atm_vcc *vcc, struct sk_buff *skb); - int xoff; -}; - -#define LEC_VCC_PRIV(vcc) ((struct lec_vcc_priv *)((vcc)->user_back)) - -#endif /* _LEC_H_ */ diff --git a/net/atm/lec_arpc.h b/net/atm/lec_arpc.h deleted file mode 100644 index 39115fe074c4..000000000000 --- a/net/atm/lec_arpc.h +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Lec arp cache - * - * Marko Kiiskila - */ -#ifndef _LEC_ARP_H_ -#define _LEC_ARP_H_ -#include -#include -#include -#include - -struct lec_arp_table { - struct hlist_node next; /* Linked entry list */ - unsigned char atm_addr[ATM_ESA_LEN]; /* Atm address */ - unsigned char mac_addr[ETH_ALEN]; /* Mac address */ - int is_rdesc; /* Mac address is a route descriptor */ - struct atm_vcc *vcc; /* Vcc this entry is attached */ - struct atm_vcc *recv_vcc; /* Vcc we receive data from */ - - void (*old_push) (struct atm_vcc *vcc, struct sk_buff *skb); - /* Push that leads to daemon */ - - void (*old_recv_push) (struct atm_vcc *vcc, struct sk_buff *skb); - /* Push that leads to daemon */ - - unsigned long last_used; /* For expiry */ - unsigned long timestamp; /* Used for various timestamping things: - * 1. FLUSH started - * (status=ESI_FLUSH_PENDING) - * 2. Counting to - * max_unknown_frame_time - * (status=ESI_ARP_PENDING|| - * status=ESI_VC_PENDING) - */ - unsigned char no_tries; /* No of times arp retry has been tried */ - unsigned char status; /* Status of this entry */ - unsigned short flags; /* Flags for this entry */ - unsigned short packets_flooded; /* Data packets flooded */ - unsigned long flush_tran_id; /* Transaction id in flush protocol */ - struct timer_list timer; /* Arping timer */ - struct lec_priv *priv; /* Pointer back */ - u8 *tlvs; - u32 sizeoftlvs; /* - * LANE2: Each MAC address can have TLVs - * associated with it. sizeoftlvs tells - * the length of the tlvs array - */ - struct sk_buff_head tx_wait; /* wait queue for outgoing packets */ - refcount_t usage; /* usage count */ -}; - -/* - * LANE2: Template tlv struct for accessing - * the tlvs in the lec_arp_table->tlvs array - */ -struct tlv { - u32 type; - u8 length; - u8 value[255]; -}; - -/* Status fields */ -#define ESI_UNKNOWN 0 /* - * Next packet sent to this mac address - * causes ARP-request to be sent - */ -#define ESI_ARP_PENDING 1 /* - * There is no ATM address associated with this - * 48-bit address. The LE-ARP protocol is in - * progress. - */ -#define ESI_VC_PENDING 2 /* - * There is a valid ATM address associated with - * this 48-bit address but there is no VC set - * up to that ATM address. The signaling - * protocol is in process. - */ -#define ESI_FLUSH_PENDING 4 /* - * The LEC has been notified of the FLUSH_START - * status and it is assumed that the flush - * protocol is in process. - */ -#define ESI_FORWARD_DIRECT 5 /* - * Either the Path Switching Delay (C22) has - * elapsed or the LEC has notified the Mapping - * that the flush protocol has completed. In - * either case, it is safe to forward packets - * to this address via the data direct VC. - */ - -/* Flag values */ -#define LEC_REMOTE_FLAG 0x0001 -#define LEC_PERMANENT_FLAG 0x0002 - -#endif /* _LEC_ARP_H_ */ diff --git a/net/atm/mpc.c b/net/atm/mpc.c deleted file mode 100644 index ce8e9780373b..000000000000 --- a/net/atm/mpc.c +++ /dev/null @@ -1,1538 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* We are an ethernet device */ -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for ip_fast_csum() */ -#include -#include -#include - -/* And atm device */ -#include -#include -#include -/* Modular too */ -#include - -#include "lec.h" -#include "mpc.h" -#include "resources.h" - -/* - * mpc.c: Implementation of MPOA client kernel part - */ - -#if 0 -#define dprintk(format, args...) \ - printk(KERN_DEBUG "mpoa:%s: " format, __func__, ##args) -#define dprintk_cont(format, args...) printk(KERN_CONT format, ##args) -#else -#define dprintk(format, args...) \ - do { if (0) \ - printk(KERN_DEBUG "mpoa:%s: " format, __func__, ##args);\ - } while (0) -#define dprintk_cont(format, args...) \ - do { if (0) printk(KERN_CONT format, ##args); } while (0) -#endif - -#if 0 -#define ddprintk(format, args...) \ - printk(KERN_DEBUG "mpoa:%s: " format, __func__, ##args) -#define ddprintk_cont(format, args...) printk(KERN_CONT format, ##args) -#else -#define ddprintk(format, args...) \ - do { if (0) \ - printk(KERN_DEBUG "mpoa:%s: " format, __func__, ##args);\ - } while (0) -#define ddprintk_cont(format, args...) \ - do { if (0) printk(KERN_CONT format, ##args); } while (0) -#endif - -/* mpc_daemon -> kernel */ -static void MPOA_trigger_rcvd(struct k_message *msg, struct mpoa_client *mpc); -static void MPOA_res_reply_rcvd(struct k_message *msg, struct mpoa_client *mpc); -static void ingress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc); -static void egress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc); -static void mps_death(struct k_message *msg, struct mpoa_client *mpc); -static void clean_up(struct k_message *msg, struct mpoa_client *mpc, - int action); -static void MPOA_cache_impos_rcvd(struct k_message *msg, - struct mpoa_client *mpc); -static void set_mpc_ctrl_addr_rcvd(struct k_message *mesg, - struct mpoa_client *mpc); -static void set_mps_mac_addr_rcvd(struct k_message *mesg, - struct mpoa_client *mpc); - -static const uint8_t *copy_macs(struct mpoa_client *mpc, - const uint8_t *router_mac, - const uint8_t *tlvs, uint8_t mps_macs, - uint8_t device_type); -static void purge_egress_shortcut(struct atm_vcc *vcc, eg_cache_entry *entry); - -static void send_set_mps_ctrl_addr(const char *addr, struct mpoa_client *mpc); -static void mpoad_close(struct atm_vcc *vcc); -static int msg_from_mpoad(struct atm_vcc *vcc, struct sk_buff *skb); - -static void mpc_push(struct atm_vcc *vcc, struct sk_buff *skb); -static netdev_tx_t mpc_send_packet(struct sk_buff *skb, - struct net_device *dev); -static int mpoa_event_listener(struct notifier_block *mpoa_notifier, - unsigned long event, void *dev); -static void mpc_timer_refresh(void); -static void mpc_cache_check(struct timer_list *unused); - -static struct llc_snap_hdr llc_snap_mpoa_ctrl = { - 0xaa, 0xaa, 0x03, - {0x00, 0x00, 0x5e}, - {0x00, 0x03} /* For MPOA control PDUs */ -}; -static struct llc_snap_hdr llc_snap_mpoa_data = { - 0xaa, 0xaa, 0x03, - {0x00, 0x00, 0x00}, - {0x08, 0x00} /* This is for IP PDUs only */ -}; -static struct llc_snap_hdr llc_snap_mpoa_data_tagged = { - 0xaa, 0xaa, 0x03, - {0x00, 0x00, 0x00}, - {0x88, 0x4c} /* This is for tagged data PDUs */ -}; - -static struct notifier_block mpoa_notifier = { - mpoa_event_listener, - NULL, - 0 -}; - -struct mpoa_client *mpcs = NULL; /* FIXME */ -static struct atm_mpoa_qos *qos_head = NULL; -static DEFINE_TIMER(mpc_timer, mpc_cache_check); - - -static struct mpoa_client *find_mpc_by_itfnum(int itf) -{ - struct mpoa_client *mpc; - - mpc = mpcs; /* our global linked list */ - while (mpc != NULL) { - if (mpc->dev_num == itf) - return mpc; - mpc = mpc->next; - } - - return NULL; /* not found */ -} - -static struct mpoa_client *find_mpc_by_vcc(struct atm_vcc *vcc) -{ - struct mpoa_client *mpc; - - mpc = mpcs; /* our global linked list */ - while (mpc != NULL) { - if (mpc->mpoad_vcc == vcc) - return mpc; - mpc = mpc->next; - } - - return NULL; /* not found */ -} - -static struct mpoa_client *find_mpc_by_lec(struct net_device *dev) -{ - struct mpoa_client *mpc; - - mpc = mpcs; /* our global linked list */ - while (mpc != NULL) { - if (mpc->dev == dev) - return mpc; - mpc = mpc->next; - } - - return NULL; /* not found */ -} - -/* - * Functions for managing QoS list - */ - -/* - * Overwrites the old entry or makes a new one. - */ -struct atm_mpoa_qos *atm_mpoa_add_qos(__be32 dst_ip, struct atm_qos *qos) -{ - struct atm_mpoa_qos *entry; - - entry = atm_mpoa_search_qos(dst_ip); - if (entry != NULL) { - entry->qos = *qos; - return entry; - } - - entry = kmalloc_obj(struct atm_mpoa_qos); - if (entry == NULL) { - pr_info("mpoa: out of memory\n"); - return entry; - } - - entry->ipaddr = dst_ip; - entry->qos = *qos; - - entry->next = qos_head; - qos_head = entry; - - return entry; -} - -struct atm_mpoa_qos *atm_mpoa_search_qos(__be32 dst_ip) -{ - struct atm_mpoa_qos *qos; - - qos = qos_head; - while (qos) { - if (qos->ipaddr == dst_ip) - break; - qos = qos->next; - } - - return qos; -} - -/* - * Returns 0 for failure - */ -int atm_mpoa_delete_qos(struct atm_mpoa_qos *entry) -{ - struct atm_mpoa_qos *curr; - - if (entry == NULL) - return 0; - if (entry == qos_head) { - qos_head = qos_head->next; - kfree(entry); - return 1; - } - - curr = qos_head; - while (curr != NULL) { - if (curr->next == entry) { - curr->next = entry->next; - kfree(entry); - return 1; - } - curr = curr->next; - } - - return 0; -} - -/* this is buggered - we need locking for qos_head */ -void atm_mpoa_disp_qos(struct seq_file *m) -{ - struct atm_mpoa_qos *qos; - - qos = qos_head; - seq_printf(m, "QoS entries for shortcuts:\n"); - seq_printf(m, "IP address\n TX:max_pcr pcr min_pcr max_cdv max_sdu\n RX:max_pcr pcr min_pcr max_cdv max_sdu\n"); - - while (qos != NULL) { - seq_printf(m, "%pI4\n %-7d %-7d %-7d %-7d %-7d\n %-7d %-7d %-7d %-7d %-7d\n", - &qos->ipaddr, - qos->qos.txtp.max_pcr, - qos->qos.txtp.pcr, - qos->qos.txtp.min_pcr, - qos->qos.txtp.max_cdv, - qos->qos.txtp.max_sdu, - qos->qos.rxtp.max_pcr, - qos->qos.rxtp.pcr, - qos->qos.rxtp.min_pcr, - qos->qos.rxtp.max_cdv, - qos->qos.rxtp.max_sdu); - qos = qos->next; - } -} - -static struct net_device *find_lec_by_itfnum(int itf) -{ - struct net_device *dev; - char name[IFNAMSIZ]; - - sprintf(name, "lec%d", itf); - dev = dev_get_by_name(&init_net, name); - - return dev; -} - -static struct mpoa_client *alloc_mpc(void) -{ - struct mpoa_client *mpc; - - mpc = kzalloc_obj(struct mpoa_client); - if (mpc == NULL) - return NULL; - rwlock_init(&mpc->ingress_lock); - rwlock_init(&mpc->egress_lock); - mpc->next = mpcs; - atm_mpoa_init_cache(mpc); - - mpc->parameters.mpc_p1 = MPC_P1; - mpc->parameters.mpc_p2 = MPC_P2; - memset(mpc->parameters.mpc_p3, 0, sizeof(mpc->parameters.mpc_p3)); - mpc->parameters.mpc_p4 = MPC_P4; - mpc->parameters.mpc_p5 = MPC_P5; - mpc->parameters.mpc_p6 = MPC_P6; - - mpcs = mpc; - - return mpc; -} - -/* - * - * start_mpc() puts the MPC on line. All the packets destined - * to the lec underneath us are now being monitored and - * shortcuts will be established. - * - */ -static void start_mpc(struct mpoa_client *mpc, struct net_device *dev) -{ - - dprintk("(%s)\n", mpc->dev->name); - if (!dev->netdev_ops) - pr_info("(%s) not starting\n", dev->name); - else { - mpc->old_ops = dev->netdev_ops; - mpc->new_ops = *mpc->old_ops; - mpc->new_ops.ndo_start_xmit = mpc_send_packet; - dev->netdev_ops = &mpc->new_ops; - } -} - -static void stop_mpc(struct mpoa_client *mpc) -{ - struct net_device *dev = mpc->dev; - dprintk("(%s)", mpc->dev->name); - - /* Lets not nullify lec device's dev->hard_start_xmit */ - if (dev->netdev_ops != &mpc->new_ops) { - dprintk_cont(" mpc already stopped, not fatal\n"); - return; - } - dprintk_cont("\n"); - - dev->netdev_ops = mpc->old_ops; - mpc->old_ops = NULL; - - /* close_shortcuts(mpc); ??? FIXME */ -} - -static const char *mpoa_device_type_string(char type) __attribute__ ((unused)); - -static const char *mpoa_device_type_string(char type) -{ - switch (type) { - case NON_MPOA: - return "non-MPOA device"; - case MPS: - return "MPS"; - case MPC: - return "MPC"; - case MPS_AND_MPC: - return "both MPS and MPC"; - } - - return "unspecified (non-MPOA) device"; -} - -/* - * lec device calls this via its netdev_priv(dev)->lane2_ops - * ->associate_indicator() when it sees a TLV in LE_ARP packet. - * We fill in the pointer above when we see a LANE2 lec initializing - * See LANE2 spec 3.1.5 - * - * Quite a big and ugly function but when you look at it - * all it does is to try to locate and parse MPOA Device - * Type TLV. - * We give our lec a pointer to this function and when the - * lec sees a TLV it uses the pointer to call this function. - * - */ -static void lane2_assoc_ind(struct net_device *dev, const u8 *mac_addr, - const u8 *tlvs, u32 sizeoftlvs) -{ - uint32_t type; - uint8_t length, mpoa_device_type, number_of_mps_macs; - const uint8_t *end_of_tlvs; - struct mpoa_client *mpc; - - mpoa_device_type = number_of_mps_macs = 0; /* silence gcc */ - dprintk("(%s) received TLV(s), ", dev->name); - dprintk("total length of all TLVs %d\n", sizeoftlvs); - mpc = find_mpc_by_lec(dev); /* Sampo-Fix: moved here from below */ - if (mpc == NULL) { - pr_info("(%s) no mpc\n", dev->name); - return; - } - end_of_tlvs = tlvs + sizeoftlvs; - while (end_of_tlvs - tlvs >= 5) { - type = ((tlvs[0] << 24) | (tlvs[1] << 16) | - (tlvs[2] << 8) | tlvs[3]); - length = tlvs[4]; - tlvs += 5; - dprintk(" type 0x%x length %02x\n", type, length); - if (tlvs + length > end_of_tlvs) { - pr_info("TLV value extends past its buffer, aborting parse\n"); - return; - } - - if (type == 0) { - pr_info("mpoa: (%s) TLV type was 0, returning\n", - dev->name); - return; - } - - if (type != TLV_MPOA_DEVICE_TYPE) { - tlvs += length; - continue; /* skip other TLVs */ - } - mpoa_device_type = *tlvs++; - number_of_mps_macs = *tlvs++; - dprintk("(%s) MPOA device type '%s', ", - dev->name, mpoa_device_type_string(mpoa_device_type)); - if (mpoa_device_type == MPS_AND_MPC && - length < (42 + number_of_mps_macs*ETH_ALEN)) { /* :) */ - pr_info("(%s) short MPOA Device Type TLV\n", - dev->name); - continue; - } - if ((mpoa_device_type == MPS || mpoa_device_type == MPC) && - length < 22 + number_of_mps_macs*ETH_ALEN) { - pr_info("(%s) short MPOA Device Type TLV\n", dev->name); - continue; - } - if (mpoa_device_type != MPS && - mpoa_device_type != MPS_AND_MPC) { - dprintk("ignoring non-MPS device "); - if (mpoa_device_type == MPC) - tlvs += 20; - continue; /* we are only interested in MPSs */ - } - if (number_of_mps_macs == 0 && - mpoa_device_type == MPS_AND_MPC) { - pr_info("(%s) MPS_AND_MPC has zero MACs\n", dev->name); - continue; /* someone should read the spec */ - } - dprintk_cont("this MPS has %d MAC addresses\n", - number_of_mps_macs); - - /* - * ok, now we can go and tell our daemon - * the control address of MPS - */ - send_set_mps_ctrl_addr(tlvs, mpc); - - tlvs = copy_macs(mpc, mac_addr, tlvs, - number_of_mps_macs, mpoa_device_type); - if (tlvs == NULL) - return; - } - if (end_of_tlvs - tlvs != 0) - pr_info("(%s) ignoring %zd bytes of trailing TLV garbage\n", - dev->name, end_of_tlvs - tlvs); -} - -/* - * Store at least advertizing router's MAC address - * plus the possible MAC address(es) to mpc->mps_macs. - * For a freshly allocated MPOA client mpc->mps_macs == 0. - */ -static const uint8_t *copy_macs(struct mpoa_client *mpc, - const uint8_t *router_mac, - const uint8_t *tlvs, uint8_t mps_macs, - uint8_t device_type) -{ - int num_macs; - num_macs = (mps_macs > 1) ? mps_macs : 1; - - if (mpc->number_of_mps_macs != num_macs) { /* need to reallocate? */ - if (mpc->number_of_mps_macs != 0) - kfree(mpc->mps_macs); - mpc->number_of_mps_macs = 0; - mpc->mps_macs = kmalloc_array(ETH_ALEN, num_macs, GFP_KERNEL); - if (mpc->mps_macs == NULL) { - pr_info("(%s) out of mem\n", mpc->dev->name); - return NULL; - } - } - ether_addr_copy(mpc->mps_macs, router_mac); - tlvs += 20; if (device_type == MPS_AND_MPC) tlvs += 20; - if (mps_macs > 0) - memcpy(mpc->mps_macs, tlvs, mps_macs*ETH_ALEN); - tlvs += mps_macs*ETH_ALEN; - mpc->number_of_mps_macs = num_macs; - - return tlvs; -} - -static int send_via_shortcut(struct sk_buff *skb, struct mpoa_client *mpc) -{ - in_cache_entry *entry; - struct iphdr *iph; - char *buff; - __be32 ipaddr = 0; - - static struct { - struct llc_snap_hdr hdr; - __be32 tag; - } tagged_llc_snap_hdr = { - {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}, {0x88, 0x4c}}, - 0 - }; - - buff = skb->data + mpc->dev->hard_header_len; - iph = (struct iphdr *)buff; - ipaddr = iph->daddr; - - ddprintk("(%s) ipaddr 0x%x\n", - mpc->dev->name, ipaddr); - - entry = mpc->in_ops->get(ipaddr, mpc); - if (entry == NULL) { - entry = mpc->in_ops->add_entry(ipaddr, mpc); - if (entry != NULL) - mpc->in_ops->put(entry); - return 1; - } - /* threshold not exceeded or VCC not ready */ - if (mpc->in_ops->cache_hit(entry, mpc) != OPEN) { - ddprintk("(%s) cache_hit: returns != OPEN\n", - mpc->dev->name); - mpc->in_ops->put(entry); - return 1; - } - - ddprintk("(%s) using shortcut\n", - mpc->dev->name); - /* MPOA spec A.1.4, MPOA client must decrement IP ttl at least by one */ - if (iph->ttl <= 1) { - ddprintk("(%s) IP ttl = %u, using LANE\n", - mpc->dev->name, iph->ttl); - mpc->in_ops->put(entry); - return 1; - } - iph->ttl--; - iph->check = 0; - iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); - - if (entry->ctrl_info.tag != 0) { - ddprintk("(%s) adding tag 0x%x\n", - mpc->dev->name, entry->ctrl_info.tag); - tagged_llc_snap_hdr.tag = entry->ctrl_info.tag; - skb_pull(skb, ETH_HLEN); /* get rid of Eth header */ - skb_push(skb, sizeof(tagged_llc_snap_hdr)); - /* add LLC/SNAP header */ - skb_copy_to_linear_data(skb, &tagged_llc_snap_hdr, - sizeof(tagged_llc_snap_hdr)); - } else { - skb_pull(skb, ETH_HLEN); /* get rid of Eth header */ - skb_push(skb, sizeof(struct llc_snap_hdr)); - /* add LLC/SNAP header + tag */ - skb_copy_to_linear_data(skb, &llc_snap_mpoa_data, - sizeof(struct llc_snap_hdr)); - } - - atm_account_tx(entry->shortcut, skb); - entry->shortcut->send(entry->shortcut, skb); - entry->packets_fwded++; - mpc->in_ops->put(entry); - - return 0; -} - -/* - * Probably needs some error checks and locking, not sure... - */ -static netdev_tx_t mpc_send_packet(struct sk_buff *skb, - struct net_device *dev) -{ - struct mpoa_client *mpc; - struct ethhdr *eth; - int i = 0; - - mpc = find_mpc_by_lec(dev); /* this should NEVER fail */ - if (mpc == NULL) { - pr_info("(%s) no MPC found\n", dev->name); - goto non_ip; - } - - eth = (struct ethhdr *)skb->data; - if (eth->h_proto != htons(ETH_P_IP)) - goto non_ip; /* Multi-Protocol Over ATM :-) */ - - /* Weed out funny packets (e.g., AF_PACKET or raw). */ - if (skb->len < ETH_HLEN + sizeof(struct iphdr)) - goto non_ip; - skb_set_network_header(skb, ETH_HLEN); - if (skb->len < ETH_HLEN + ip_hdr(skb)->ihl * 4 || ip_hdr(skb)->ihl < 5) - goto non_ip; - - while (i < mpc->number_of_mps_macs) { - if (ether_addr_equal(eth->h_dest, mpc->mps_macs + i * ETH_ALEN)) - if (send_via_shortcut(skb, mpc) == 0) /* try shortcut */ - return NETDEV_TX_OK; - i++; - } - -non_ip: - return __netdev_start_xmit(mpc->old_ops, skb, dev, false); -} - -static int atm_mpoa_vcc_attach(struct atm_vcc *vcc, void __user *arg) -{ - int bytes_left; - struct mpoa_client *mpc; - struct atmmpc_ioc ioc_data; - in_cache_entry *in_entry; - __be32 ipaddr; - - bytes_left = copy_from_user(&ioc_data, arg, sizeof(struct atmmpc_ioc)); - if (bytes_left != 0) { - pr_info("mpoa:Short read (missed %d bytes) from userland\n", - bytes_left); - return -EFAULT; - } - ipaddr = ioc_data.ipaddr; - if (ioc_data.dev_num < 0 || ioc_data.dev_num >= MAX_LEC_ITF) - return -EINVAL; - - mpc = find_mpc_by_itfnum(ioc_data.dev_num); - if (mpc == NULL) - return -EINVAL; - - if (ioc_data.type == MPC_SOCKET_INGRESS) { - in_entry = mpc->in_ops->get(ipaddr, mpc); - if (in_entry == NULL || - in_entry->entry_state < INGRESS_RESOLVED) { - pr_info("(%s) did not find RESOLVED entry from ingress cache\n", - mpc->dev->name); - if (in_entry != NULL) - mpc->in_ops->put(in_entry); - return -EINVAL; - } - pr_info("(%s) attaching ingress SVC, entry = %pI4\n", - mpc->dev->name, &in_entry->ctrl_info.in_dst_ip); - in_entry->shortcut = vcc; - mpc->in_ops->put(in_entry); - } else { - pr_info("(%s) attaching egress SVC\n", mpc->dev->name); - } - - vcc->proto_data = mpc->dev; - vcc->push = mpc_push; - - return 0; -} - -/* - * - */ -static void mpc_vcc_close(struct atm_vcc *vcc, struct net_device *dev) -{ - struct mpoa_client *mpc; - in_cache_entry *in_entry; - eg_cache_entry *eg_entry; - - mpc = find_mpc_by_lec(dev); - if (mpc == NULL) { - pr_info("(%s) close for unknown MPC\n", dev->name); - return; - } - - dprintk("(%s)\n", dev->name); - in_entry = mpc->in_ops->get_by_vcc(vcc, mpc); - if (in_entry) { - dprintk("(%s) ingress SVC closed ip = %pI4\n", - mpc->dev->name, &in_entry->ctrl_info.in_dst_ip); - in_entry->shortcut = NULL; - mpc->in_ops->put(in_entry); - } - eg_entry = mpc->eg_ops->get_by_vcc(vcc, mpc); - if (eg_entry) { - dprintk("(%s) egress SVC closed\n", mpc->dev->name); - eg_entry->shortcut = NULL; - mpc->eg_ops->put(eg_entry); - } - - if (in_entry == NULL && eg_entry == NULL) - dprintk("(%s) unused vcc closed\n", dev->name); -} - -static void mpc_push(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct net_device *dev = (struct net_device *)vcc->proto_data; - struct sk_buff *new_skb; - eg_cache_entry *eg; - struct mpoa_client *mpc; - __be32 tag; - char *tmp; - - ddprintk("(%s)\n", dev->name); - if (skb == NULL) { - dprintk("(%s) null skb, closing VCC\n", dev->name); - mpc_vcc_close(vcc, dev); - return; - } - - skb->dev = dev; - if (memcmp(skb->data, &llc_snap_mpoa_ctrl, - sizeof(struct llc_snap_hdr)) == 0) { - struct sock *sk = sk_atm(vcc); - - dprintk("(%s) control packet arrived\n", dev->name); - /* Pass control packets to daemon */ - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk); - return; - } - - /* data coming over the shortcut */ - atm_return(vcc, skb->truesize); - - mpc = find_mpc_by_lec(dev); - if (mpc == NULL) { - pr_info("(%s) unknown MPC\n", dev->name); - return; - } - - if (memcmp(skb->data, &llc_snap_mpoa_data_tagged, - sizeof(struct llc_snap_hdr)) == 0) { /* MPOA tagged data */ - ddprintk("(%s) tagged data packet arrived\n", dev->name); - - } else if (memcmp(skb->data, &llc_snap_mpoa_data, - sizeof(struct llc_snap_hdr)) == 0) { /* MPOA data */ - pr_info("(%s) Unsupported non-tagged data packet arrived. Purging\n", - dev->name); - dev_kfree_skb_any(skb); - return; - } else { - pr_info("(%s) garbage arrived, purging\n", dev->name); - dev_kfree_skb_any(skb); - return; - } - - tmp = skb->data + sizeof(struct llc_snap_hdr); - tag = *(__be32 *)tmp; - - eg = mpc->eg_ops->get_by_tag(tag, mpc); - if (eg == NULL) { - pr_info("mpoa: (%s) Didn't find egress cache entry, tag = %u\n", - dev->name, tag); - purge_egress_shortcut(vcc, NULL); - dev_kfree_skb_any(skb); - return; - } - - /* - * See if ingress MPC is using shortcut we opened as a return channel. - * This means we have a bi-directional vcc opened by us. - */ - if (eg->shortcut == NULL) { - eg->shortcut = vcc; - pr_info("(%s) egress SVC in use\n", dev->name); - } - - skb_pull(skb, sizeof(struct llc_snap_hdr) + sizeof(tag)); - /* get rid of LLC/SNAP header */ - new_skb = skb_realloc_headroom(skb, eg->ctrl_info.DH_length); - /* LLC/SNAP is shorter than MAC header :( */ - dev_kfree_skb_any(skb); - if (new_skb == NULL) { - mpc->eg_ops->put(eg); - return; - } - skb_push(new_skb, eg->ctrl_info.DH_length); /* add MAC header */ - skb_copy_to_linear_data(new_skb, eg->ctrl_info.DLL_header, - eg->ctrl_info.DH_length); - new_skb->protocol = eth_type_trans(new_skb, dev); - skb_reset_network_header(new_skb); - - eg->latest_ip_addr = ip_hdr(new_skb)->saddr; - eg->packets_rcvd++; - mpc->eg_ops->put(eg); - - memset(ATM_SKB(new_skb), 0, sizeof(struct atm_skb_data)); - netif_rx(new_skb); -} - -static const struct atmdev_ops mpc_ops = { /* only send is required */ - .close = mpoad_close, - .send = msg_from_mpoad -}; - -static struct atm_dev mpc_dev = { - .ops = &mpc_ops, - .type = "mpc", - .number = 42, - .lock = __SPIN_LOCK_UNLOCKED(mpc_dev.lock) - /* members not explicitly initialised will be 0 */ -}; - -static int atm_mpoa_mpoad_attach(struct atm_vcc *vcc, int arg) -{ - struct mpoa_client *mpc; - struct lec_priv *priv; - int err; - - if (mpcs == NULL) { - mpc_timer_refresh(); - - /* This lets us now how our LECs are doing */ - err = register_netdevice_notifier(&mpoa_notifier); - if (err < 0) { - timer_delete(&mpc_timer); - return err; - } - } - - mpc = find_mpc_by_itfnum(arg); - if (mpc == NULL) { - dprintk("allocating new mpc for itf %d\n", arg); - mpc = alloc_mpc(); - if (mpc == NULL) - return -ENOMEM; - mpc->dev_num = arg; - mpc->dev = find_lec_by_itfnum(arg); - /* NULL if there was no lec */ - } - if (mpc->mpoad_vcc) { - pr_info("mpoad is already present for itf %d\n", arg); - return -EADDRINUSE; - } - - if (mpc->dev) { /* check if the lec is LANE2 capable */ - priv = netdev_priv(mpc->dev); - if (priv->lane_version < 2) { - dev_put(mpc->dev); - mpc->dev = NULL; - } else - priv->lane2_ops->associate_indicator = lane2_assoc_ind; - } - - mpc->mpoad_vcc = vcc; - vcc->dev = &mpc_dev; - vcc_insert_socket(sk_atm(vcc)); - set_bit(ATM_VF_META, &vcc->flags); - set_bit(ATM_VF_READY, &vcc->flags); - - if (mpc->dev) { - char empty[ATM_ESA_LEN]; - memset(empty, 0, ATM_ESA_LEN); - - start_mpc(mpc, mpc->dev); - /* set address if mpcd e.g. gets killed and restarted. - * If we do not do it now we have to wait for the next LE_ARP - */ - if (memcmp(mpc->mps_ctrl_addr, empty, ATM_ESA_LEN) != 0) - send_set_mps_ctrl_addr(mpc->mps_ctrl_addr, mpc); - } - - __module_get(THIS_MODULE); - return arg; -} - -static void send_set_mps_ctrl_addr(const char *addr, struct mpoa_client *mpc) -{ - struct k_message mesg; - - memcpy(mpc->mps_ctrl_addr, addr, ATM_ESA_LEN); - - mesg.type = SET_MPS_CTRL_ADDR; - memcpy(mesg.MPS_ctrl, addr, ATM_ESA_LEN); - msg_to_mpoad(&mesg, mpc); -} - -static void mpoad_close(struct atm_vcc *vcc) -{ - struct mpoa_client *mpc; - struct sk_buff *skb; - - mpc = find_mpc_by_vcc(vcc); - if (mpc == NULL) { - pr_info("did not find MPC\n"); - return; - } - if (!mpc->mpoad_vcc) { - pr_info("close for non-present mpoad\n"); - return; - } - - mpc->mpoad_vcc = NULL; - if (mpc->dev) { - struct lec_priv *priv = netdev_priv(mpc->dev); - priv->lane2_ops->associate_indicator = NULL; - stop_mpc(mpc); - dev_put(mpc->dev); - } - - mpc->in_ops->destroy_cache(mpc); - mpc->eg_ops->destroy_cache(mpc); - - while ((skb = skb_dequeue(&sk_atm(vcc)->sk_receive_queue))) { - atm_return(vcc, skb->truesize); - kfree_skb(skb); - } - - pr_info("(%s) going down\n", - (mpc->dev) ? mpc->dev->name : ""); - module_put(THIS_MODULE); -} - -/* - * - */ -static int msg_from_mpoad(struct atm_vcc *vcc, struct sk_buff *skb) -{ - - struct mpoa_client *mpc = find_mpc_by_vcc(vcc); - struct k_message *mesg = (struct k_message *)skb->data; - WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc)); - - if (mpc == NULL) { - pr_info("no mpc found\n"); - return 0; - } - dprintk("(%s)", mpc->dev ? mpc->dev->name : ""); - switch (mesg->type) { - case MPOA_RES_REPLY_RCVD: - dprintk_cont("mpoa_res_reply_rcvd\n"); - MPOA_res_reply_rcvd(mesg, mpc); - break; - case MPOA_TRIGGER_RCVD: - dprintk_cont("mpoa_trigger_rcvd\n"); - MPOA_trigger_rcvd(mesg, mpc); - break; - case INGRESS_PURGE_RCVD: - dprintk_cont("nhrp_purge_rcvd\n"); - ingress_purge_rcvd(mesg, mpc); - break; - case EGRESS_PURGE_RCVD: - dprintk_cont("egress_purge_reply_rcvd\n"); - egress_purge_rcvd(mesg, mpc); - break; - case MPS_DEATH: - dprintk_cont("mps_death\n"); - mps_death(mesg, mpc); - break; - case CACHE_IMPOS_RCVD: - dprintk_cont("cache_impos_rcvd\n"); - MPOA_cache_impos_rcvd(mesg, mpc); - break; - case SET_MPC_CTRL_ADDR: - dprintk_cont("set_mpc_ctrl_addr\n"); - set_mpc_ctrl_addr_rcvd(mesg, mpc); - break; - case SET_MPS_MAC_ADDR: - dprintk_cont("set_mps_mac_addr\n"); - set_mps_mac_addr_rcvd(mesg, mpc); - break; - case CLEAN_UP_AND_EXIT: - dprintk_cont("clean_up_and_exit\n"); - clean_up(mesg, mpc, DIE); - break; - case RELOAD: - dprintk_cont("reload\n"); - clean_up(mesg, mpc, RELOAD); - break; - case SET_MPC_PARAMS: - dprintk_cont("set_mpc_params\n"); - mpc->parameters = mesg->content.params; - break; - default: - dprintk_cont("unknown message %d\n", mesg->type); - break; - } - kfree_skb(skb); - - return 0; -} - -/* Remember that this function may not do things that sleep */ -int msg_to_mpoad(struct k_message *mesg, struct mpoa_client *mpc) -{ - struct sk_buff *skb; - struct sock *sk; - - if (mpc == NULL || !mpc->mpoad_vcc) { - pr_info("mesg %d to a non-existent mpoad\n", mesg->type); - return -ENXIO; - } - - skb = alloc_skb(sizeof(struct k_message), GFP_ATOMIC); - if (skb == NULL) - return -ENOMEM; - skb_put(skb, sizeof(struct k_message)); - skb_copy_to_linear_data(skb, mesg, sizeof(*mesg)); - atm_force_charge(mpc->mpoad_vcc, skb->truesize); - - sk = sk_atm(mpc->mpoad_vcc); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk); - - return 0; -} - -static int mpoa_event_listener(struct notifier_block *mpoa_notifier, - unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct mpoa_client *mpc; - struct lec_priv *priv; - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (strncmp(dev->name, "lec", 3)) - return NOTIFY_DONE; /* we are only interested in lec:s */ - - switch (event) { - case NETDEV_REGISTER: /* a new lec device was allocated */ - priv = netdev_priv(dev); - if (priv->lane_version < 2) - break; - priv->lane2_ops->associate_indicator = lane2_assoc_ind; - mpc = find_mpc_by_itfnum(priv->itfnum); - if (mpc == NULL) { - dprintk("allocating new mpc for %s\n", dev->name); - mpc = alloc_mpc(); - if (mpc == NULL) { - pr_info("no new mpc"); - break; - } - } - mpc->dev_num = priv->itfnum; - mpc->dev = dev; - dev_hold(dev); - dprintk("(%s) was initialized\n", dev->name); - break; - case NETDEV_UNREGISTER: - /* the lec device was deallocated */ - mpc = find_mpc_by_lec(dev); - if (mpc == NULL) - break; - dprintk("device (%s) was deallocated\n", dev->name); - stop_mpc(mpc); - dev_put(mpc->dev); - mpc->dev = NULL; - break; - case NETDEV_UP: - /* the dev was ifconfig'ed up */ - mpc = find_mpc_by_lec(dev); - if (mpc == NULL) - break; - if (mpc->mpoad_vcc != NULL) - start_mpc(mpc, dev); - break; - case NETDEV_DOWN: - /* the dev was ifconfig'ed down */ - /* this means that the flow of packets from the - * upper layer stops - */ - mpc = find_mpc_by_lec(dev); - if (mpc == NULL) - break; - if (mpc->mpoad_vcc != NULL) - stop_mpc(mpc); - break; - case NETDEV_REBOOT: - case NETDEV_CHANGE: - case NETDEV_CHANGEMTU: - case NETDEV_CHANGEADDR: - case NETDEV_GOING_DOWN: - break; - default: - break; - } - - return NOTIFY_DONE; -} - -/* - * Functions which are called after a message is received from mpcd. - * Msg is reused on purpose. - */ - - -static void MPOA_trigger_rcvd(struct k_message *msg, struct mpoa_client *mpc) -{ - __be32 dst_ip = msg->content.in_info.in_dst_ip; - in_cache_entry *entry; - - entry = mpc->in_ops->get(dst_ip, mpc); - if (entry == NULL) { - entry = mpc->in_ops->add_entry(dst_ip, mpc); - entry->entry_state = INGRESS_RESOLVING; - msg->type = SND_MPOA_RES_RQST; - msg->content.in_info = entry->ctrl_info; - msg_to_mpoad(msg, mpc); - entry->reply_wait = ktime_get_seconds(); - mpc->in_ops->put(entry); - return; - } - - if (entry->entry_state == INGRESS_INVALID) { - entry->entry_state = INGRESS_RESOLVING; - msg->type = SND_MPOA_RES_RQST; - msg->content.in_info = entry->ctrl_info; - msg_to_mpoad(msg, mpc); - entry->reply_wait = ktime_get_seconds(); - mpc->in_ops->put(entry); - return; - } - - pr_info("(%s) entry already in resolving state\n", - (mpc->dev) ? mpc->dev->name : ""); - mpc->in_ops->put(entry); -} - -/* - * Things get complicated because we have to check if there's an egress - * shortcut with suitable traffic parameters we could use. - */ -static void check_qos_and_open_shortcut(struct k_message *msg, - struct mpoa_client *client, - in_cache_entry *entry) -{ - __be32 dst_ip = msg->content.in_info.in_dst_ip; - struct atm_mpoa_qos *qos = atm_mpoa_search_qos(dst_ip); - eg_cache_entry *eg_entry = client->eg_ops->get_by_src_ip(dst_ip, client); - - if (eg_entry && eg_entry->shortcut) { - if (eg_entry->shortcut->qos.txtp.traffic_class & - msg->qos.txtp.traffic_class & - (qos ? qos->qos.txtp.traffic_class : ATM_UBR | ATM_CBR)) { - if (eg_entry->shortcut->qos.txtp.traffic_class == ATM_UBR) - entry->shortcut = eg_entry->shortcut; - else if (eg_entry->shortcut->qos.txtp.max_pcr > 0) - entry->shortcut = eg_entry->shortcut; - } - if (entry->shortcut) { - dprintk("(%s) using egress SVC to reach %pI4\n", - client->dev->name, &dst_ip); - client->eg_ops->put(eg_entry); - return; - } - } - if (eg_entry != NULL) - client->eg_ops->put(eg_entry); - - /* No luck in the egress cache we must open an ingress SVC */ - msg->type = OPEN_INGRESS_SVC; - if (qos && - (qos->qos.txtp.traffic_class == msg->qos.txtp.traffic_class)) { - msg->qos = qos->qos; - pr_info("(%s) trying to get a CBR shortcut\n", - client->dev->name); - } else - memset(&msg->qos, 0, sizeof(struct atm_qos)); - msg_to_mpoad(msg, client); -} - -static void MPOA_res_reply_rcvd(struct k_message *msg, struct mpoa_client *mpc) -{ - __be32 dst_ip = msg->content.in_info.in_dst_ip; - in_cache_entry *entry = mpc->in_ops->get(dst_ip, mpc); - - dprintk("(%s) ip %pI4\n", - mpc->dev->name, &dst_ip); - ddprintk("(%s) entry = %p", - mpc->dev->name, entry); - if (entry == NULL) { - pr_info("(%s) ARGH, received res. reply for an entry that doesn't exist.\n", - mpc->dev->name); - return; - } - ddprintk_cont(" entry_state = %d ", entry->entry_state); - - if (entry->entry_state == INGRESS_RESOLVED) { - pr_info("(%s) RESOLVED entry!\n", mpc->dev->name); - mpc->in_ops->put(entry); - return; - } - - entry->ctrl_info = msg->content.in_info; - entry->time = ktime_get_seconds(); - /* Used in refreshing func from now on */ - entry->reply_wait = ktime_get_seconds(); - entry->refresh_time = 0; - ddprintk_cont("entry->shortcut = %p\n", entry->shortcut); - - if (entry->entry_state == INGRESS_RESOLVING && - entry->shortcut != NULL) { - entry->entry_state = INGRESS_RESOLVED; - mpc->in_ops->put(entry); - return; /* Shortcut already open... */ - } - - if (entry->shortcut != NULL) { - pr_info("(%s) entry->shortcut != NULL, impossible!\n", - mpc->dev->name); - mpc->in_ops->put(entry); - return; - } - - check_qos_and_open_shortcut(msg, mpc, entry); - entry->entry_state = INGRESS_RESOLVED; - mpc->in_ops->put(entry); - - return; - -} - -static void ingress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc) -{ - __be32 dst_ip = msg->content.in_info.in_dst_ip; - __be32 mask = msg->ip_mask; - in_cache_entry *entry = mpc->in_ops->get_with_mask(dst_ip, mpc, mask); - - if (entry == NULL) { - pr_info("(%s) purge for a non-existing entry, ip = %pI4\n", - mpc->dev->name, &dst_ip); - return; - } - - do { - dprintk("(%s) removing an ingress entry, ip = %pI4\n", - mpc->dev->name, &dst_ip); - write_lock_bh(&mpc->ingress_lock); - mpc->in_ops->remove_entry(entry, mpc); - write_unlock_bh(&mpc->ingress_lock); - mpc->in_ops->put(entry); - entry = mpc->in_ops->get_with_mask(dst_ip, mpc, mask); - } while (entry != NULL); -} - -static void egress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc) -{ - __be32 cache_id = msg->content.eg_info.cache_id; - eg_cache_entry *entry = mpc->eg_ops->get_by_cache_id(cache_id, mpc); - - if (entry == NULL) { - dprintk("(%s) purge for a non-existing entry\n", - mpc->dev->name); - return; - } - - write_lock_irq(&mpc->egress_lock); - mpc->eg_ops->remove_entry(entry, mpc); - write_unlock_irq(&mpc->egress_lock); - - mpc->eg_ops->put(entry); -} - -static void purge_egress_shortcut(struct atm_vcc *vcc, eg_cache_entry *entry) -{ - struct sock *sk; - struct k_message *purge_msg; - struct sk_buff *skb; - - dprintk("entering\n"); - if (vcc == NULL) { - pr_info("vcc == NULL\n"); - return; - } - - skb = alloc_skb(sizeof(struct k_message), GFP_ATOMIC); - if (skb == NULL) { - pr_info("out of memory\n"); - return; - } - - skb_put(skb, sizeof(struct k_message)); - memset(skb->data, 0, sizeof(struct k_message)); - purge_msg = (struct k_message *)skb->data; - purge_msg->type = DATA_PLANE_PURGE; - if (entry != NULL) - purge_msg->content.eg_info = entry->ctrl_info; - - atm_force_charge(vcc, skb->truesize); - - sk = sk_atm(vcc); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk); - dprintk("exiting\n"); -} - -/* - * Our MPS died. Tell our daemon to send NHRP data plane purge to each - * of the egress shortcuts we have. - */ -static void mps_death(struct k_message *msg, struct mpoa_client *mpc) -{ - eg_cache_entry *entry; - - dprintk("(%s)\n", mpc->dev->name); - - if (memcmp(msg->MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN)) { - pr_info("(%s) wrong MPS\n", mpc->dev->name); - return; - } - - /* FIXME: This knows too much of the cache structure */ - read_lock_irq(&mpc->egress_lock); - entry = mpc->eg_cache; - while (entry != NULL) { - purge_egress_shortcut(entry->shortcut, entry); - entry = entry->next; - } - read_unlock_irq(&mpc->egress_lock); - - mpc->in_ops->destroy_cache(mpc); - mpc->eg_ops->destroy_cache(mpc); -} - -static void MPOA_cache_impos_rcvd(struct k_message *msg, - struct mpoa_client *mpc) -{ - uint16_t holding_time; - eg_cache_entry *entry = mpc->eg_ops->get_by_cache_id(msg->content.eg_info.cache_id, mpc); - - holding_time = msg->content.eg_info.holding_time; - dprintk("(%s) entry = %p, holding_time = %u\n", - mpc->dev->name, entry, holding_time); - if (entry == NULL && !holding_time) - return; - if (entry == NULL && holding_time) { - entry = mpc->eg_ops->add_entry(msg, mpc); - mpc->eg_ops->put(entry); - return; - } - if (holding_time) { - mpc->eg_ops->update(entry, holding_time); - return; - } - - write_lock_irq(&mpc->egress_lock); - mpc->eg_ops->remove_entry(entry, mpc); - write_unlock_irq(&mpc->egress_lock); - - mpc->eg_ops->put(entry); -} - -static void set_mpc_ctrl_addr_rcvd(struct k_message *mesg, - struct mpoa_client *mpc) -{ - struct lec_priv *priv; - int i, retval ; - - uint8_t tlv[4 + 1 + 1 + 1 + ATM_ESA_LEN]; - - tlv[0] = 00; tlv[1] = 0xa0; tlv[2] = 0x3e; tlv[3] = 0x2a; /* type */ - tlv[4] = 1 + 1 + ATM_ESA_LEN; /* length */ - tlv[5] = 0x02; /* MPOA client */ - tlv[6] = 0x00; /* number of MPS MAC addresses */ - - memcpy(&tlv[7], mesg->MPS_ctrl, ATM_ESA_LEN); /* MPC ctrl ATM addr */ - memcpy(mpc->our_ctrl_addr, mesg->MPS_ctrl, ATM_ESA_LEN); - - dprintk("(%s) setting MPC ctrl ATM address to", - mpc->dev ? mpc->dev->name : ""); - for (i = 7; i < sizeof(tlv); i++) - dprintk_cont(" %02x", tlv[i]); - dprintk_cont("\n"); - - if (mpc->dev) { - priv = netdev_priv(mpc->dev); - retval = priv->lane2_ops->associate_req(mpc->dev, - mpc->dev->dev_addr, - tlv, sizeof(tlv)); - if (retval == 0) - pr_info("(%s) MPOA device type TLV association failed\n", - mpc->dev->name); - retval = priv->lane2_ops->resolve(mpc->dev, NULL, 1, NULL, NULL); - if (retval < 0) - pr_info("(%s) targetless LE_ARP request failed\n", - mpc->dev->name); - } -} - -static void set_mps_mac_addr_rcvd(struct k_message *msg, - struct mpoa_client *client) -{ - - if (client->number_of_mps_macs) - kfree(client->mps_macs); - client->number_of_mps_macs = 0; - client->mps_macs = kmemdup(msg->MPS_ctrl, ETH_ALEN, GFP_KERNEL); - if (client->mps_macs == NULL) { - pr_info("out of memory\n"); - return; - } - client->number_of_mps_macs = 1; -} - -/* - * purge egress cache and tell daemon to 'action' (DIE, RELOAD) - */ -static void clean_up(struct k_message *msg, struct mpoa_client *mpc, int action) -{ - - eg_cache_entry *entry; - msg->type = SND_EGRESS_PURGE; - - - /* FIXME: This knows too much of the cache structure */ - read_lock_irq(&mpc->egress_lock); - entry = mpc->eg_cache; - while (entry != NULL) { - msg->content.eg_info = entry->ctrl_info; - dprintk("cache_id %u\n", entry->ctrl_info.cache_id); - msg_to_mpoad(msg, mpc); - entry = entry->next; - } - read_unlock_irq(&mpc->egress_lock); - - msg->type = action; - msg_to_mpoad(msg, mpc); -} - -static unsigned long checking_time; - -static void mpc_timer_refresh(void) -{ - mpc_timer.expires = jiffies + (MPC_P2 * HZ); - checking_time = mpc_timer.expires; - add_timer(&mpc_timer); -} - -static void mpc_cache_check(struct timer_list *unused) -{ - struct mpoa_client *mpc = mpcs; - static unsigned long previous_resolving_check_time; - static unsigned long previous_refresh_time; - - while (mpc != NULL) { - mpc->in_ops->clear_count(mpc); - mpc->eg_ops->clear_expired(mpc); - if (checking_time - previous_resolving_check_time > - mpc->parameters.mpc_p4 * HZ) { - mpc->in_ops->check_resolving(mpc); - previous_resolving_check_time = checking_time; - } - if (checking_time - previous_refresh_time > - mpc->parameters.mpc_p5 * HZ) { - mpc->in_ops->refresh(mpc); - previous_refresh_time = checking_time; - } - mpc = mpc->next; - } - mpc_timer_refresh(); -} - -static int atm_mpoa_ioctl(struct socket *sock, unsigned int cmd, - unsigned long arg) -{ - int err = 0; - struct atm_vcc *vcc = ATM_SD(sock); - - if (cmd != ATMMPC_CTRL && cmd != ATMMPC_DATA) - return -ENOIOCTLCMD; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch (cmd) { - case ATMMPC_CTRL: - err = atm_mpoa_mpoad_attach(vcc, (int)arg); - if (err >= 0) - sock->state = SS_CONNECTED; - break; - case ATMMPC_DATA: - err = atm_mpoa_vcc_attach(vcc, (void __user *)arg); - break; - default: - break; - } - return err; -} - -static struct atm_ioctl atm_ioctl_ops = { - .owner = THIS_MODULE, - .ioctl = atm_mpoa_ioctl, -}; - -static __init int atm_mpoa_init(void) -{ - register_atm_ioctl(&atm_ioctl_ops); - - if (mpc_proc_init() != 0) - pr_info("failed to initialize /proc/mpoa\n"); - - pr_info("mpc.c: initialized\n"); - - return 0; -} - -static void __exit atm_mpoa_cleanup(void) -{ - struct mpoa_client *mpc, *tmp; - struct atm_mpoa_qos *qos, *nextqos; - struct lec_priv *priv; - - mpc_proc_clean(); - - timer_delete_sync(&mpc_timer); - unregister_netdevice_notifier(&mpoa_notifier); - deregister_atm_ioctl(&atm_ioctl_ops); - - mpc = mpcs; - mpcs = NULL; - while (mpc != NULL) { - tmp = mpc->next; - if (mpc->dev != NULL) { - stop_mpc(mpc); - priv = netdev_priv(mpc->dev); - if (priv->lane2_ops != NULL) - priv->lane2_ops->associate_indicator = NULL; - } - ddprintk("about to clear caches\n"); - mpc->in_ops->destroy_cache(mpc); - mpc->eg_ops->destroy_cache(mpc); - ddprintk("caches cleared\n"); - kfree(mpc->mps_macs); - memset(mpc, 0, sizeof(struct mpoa_client)); - ddprintk("about to kfree %p\n", mpc); - kfree(mpc); - ddprintk("next mpc is at %p\n", tmp); - mpc = tmp; - } - - qos = qos_head; - qos_head = NULL; - while (qos != NULL) { - nextqos = qos->next; - dprintk("freeing qos entry %p\n", qos); - kfree(qos); - qos = nextqos; - } -} - -module_init(atm_mpoa_init); -module_exit(atm_mpoa_cleanup); - -MODULE_DESCRIPTION("Multi-Protocol Over ATM (MPOA) driver"); -MODULE_LICENSE("GPL"); diff --git a/net/atm/mpc.h b/net/atm/mpc.h deleted file mode 100644 index 454abd07651a..000000000000 --- a/net/atm/mpc.h +++ /dev/null @@ -1,65 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _MPC_H_ -#define _MPC_H_ - -#include -#include -#include -#include -#include -#include "mpoa_caches.h" - -/* kernel -> mpc-daemon */ -int msg_to_mpoad(struct k_message *msg, struct mpoa_client *mpc); - -struct mpoa_client { - struct mpoa_client *next; - struct net_device *dev; /* lec in question */ - int dev_num; /* e.g. 2 for lec2 */ - - struct atm_vcc *mpoad_vcc; /* control channel to mpoad */ - uint8_t mps_ctrl_addr[ATM_ESA_LEN]; /* MPS control ATM address */ - uint8_t our_ctrl_addr[ATM_ESA_LEN]; /* MPC's control ATM address */ - - rwlock_t ingress_lock; - const struct in_cache_ops *in_ops; /* ingress cache operations */ - in_cache_entry *in_cache; /* the ingress cache of this MPC */ - - rwlock_t egress_lock; - const struct eg_cache_ops *eg_ops; /* egress cache operations */ - eg_cache_entry *eg_cache; /* the egress cache of this MPC */ - - uint8_t *mps_macs; /* array of MPS MAC addresses, >=1 */ - int number_of_mps_macs; /* number of the above MAC addresses */ - struct mpc_parameters parameters; /* parameters for this client */ - - const struct net_device_ops *old_ops; - struct net_device_ops new_ops; -}; - - -struct atm_mpoa_qos { - struct atm_mpoa_qos *next; - __be32 ipaddr; - struct atm_qos qos; -}; - - -/* MPOA QoS operations */ -struct atm_mpoa_qos *atm_mpoa_add_qos(__be32 dst_ip, struct atm_qos *qos); -struct atm_mpoa_qos *atm_mpoa_search_qos(__be32 dst_ip); -int atm_mpoa_delete_qos(struct atm_mpoa_qos *qos); - -/* Display QoS entries. This is for the procfs */ -struct seq_file; -void atm_mpoa_disp_qos(struct seq_file *m); - -#ifdef CONFIG_PROC_FS -int mpc_proc_init(void); -void mpc_proc_clean(void); -#else -#define mpc_proc_init() (0) -#define mpc_proc_clean() do { } while(0) -#endif - -#endif /* _MPC_H_ */ diff --git a/net/atm/mpoa_caches.c b/net/atm/mpoa_caches.c deleted file mode 100644 index c8d4e6f2e831..000000000000 --- a/net/atm/mpoa_caches.c +++ /dev/null @@ -1,565 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include - -#include "mpoa_caches.h" -#include "mpc.h" - -/* - * mpoa_caches.c: Implementation of ingress and egress cache - * handling functions - */ - -#if 0 -#define dprintk(format, args...) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args) /* debug */ -#else -#define dprintk(format, args...) \ - do { if (0) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\ - } while (0) -#endif - -#if 0 -#define ddprintk(format, args...) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args) /* debug */ -#else -#define ddprintk(format, args...) \ - do { if (0) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\ - } while (0) -#endif - -static in_cache_entry *in_cache_get(__be32 dst_ip, - struct mpoa_client *client) -{ - in_cache_entry *entry; - - read_lock_bh(&client->ingress_lock); - entry = client->in_cache; - while (entry != NULL) { - if (entry->ctrl_info.in_dst_ip == dst_ip) { - refcount_inc(&entry->use); - read_unlock_bh(&client->ingress_lock); - return entry; - } - entry = entry->next; - } - read_unlock_bh(&client->ingress_lock); - - return NULL; -} - -static in_cache_entry *in_cache_get_with_mask(__be32 dst_ip, - struct mpoa_client *client, - __be32 mask) -{ - in_cache_entry *entry; - - read_lock_bh(&client->ingress_lock); - entry = client->in_cache; - while (entry != NULL) { - if ((entry->ctrl_info.in_dst_ip & mask) == (dst_ip & mask)) { - refcount_inc(&entry->use); - read_unlock_bh(&client->ingress_lock); - return entry; - } - entry = entry->next; - } - read_unlock_bh(&client->ingress_lock); - - return NULL; - -} - -static in_cache_entry *in_cache_get_by_vcc(struct atm_vcc *vcc, - struct mpoa_client *client) -{ - in_cache_entry *entry; - - read_lock_bh(&client->ingress_lock); - entry = client->in_cache; - while (entry != NULL) { - if (entry->shortcut == vcc) { - refcount_inc(&entry->use); - read_unlock_bh(&client->ingress_lock); - return entry; - } - entry = entry->next; - } - read_unlock_bh(&client->ingress_lock); - - return NULL; -} - -static in_cache_entry *in_cache_add_entry(__be32 dst_ip, - struct mpoa_client *client) -{ - in_cache_entry *entry = kzalloc_obj(in_cache_entry); - - if (entry == NULL) { - pr_info("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n"); - return NULL; - } - - dprintk("adding an ingress entry, ip = %pI4\n", &dst_ip); - - refcount_set(&entry->use, 1); - dprintk("new_in_cache_entry: about to lock\n"); - write_lock_bh(&client->ingress_lock); - entry->next = client->in_cache; - entry->prev = NULL; - if (client->in_cache != NULL) - client->in_cache->prev = entry; - client->in_cache = entry; - - memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); - entry->ctrl_info.in_dst_ip = dst_ip; - entry->time = ktime_get_seconds(); - entry->retry_time = client->parameters.mpc_p4; - entry->count = 1; - entry->entry_state = INGRESS_INVALID; - entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT; - refcount_inc(&entry->use); - - write_unlock_bh(&client->ingress_lock); - dprintk("new_in_cache_entry: unlocked\n"); - - return entry; -} - -static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc) -{ - struct atm_mpoa_qos *qos; - struct k_message msg; - - entry->count++; - if (entry->entry_state == INGRESS_RESOLVED && entry->shortcut != NULL) - return OPEN; - - if (entry->entry_state == INGRESS_REFRESHING) { - if (entry->count > mpc->parameters.mpc_p1) { - msg.type = SND_MPOA_RES_RQST; - msg.content.in_info = entry->ctrl_info; - memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN); - qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); - if (qos != NULL) - msg.qos = qos->qos; - msg_to_mpoad(&msg, mpc); - entry->reply_wait = ktime_get_seconds(); - entry->entry_state = INGRESS_RESOLVING; - } - if (entry->shortcut != NULL) - return OPEN; - return CLOSED; - } - - if (entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL) - return OPEN; - - if (entry->count > mpc->parameters.mpc_p1 && - entry->entry_state == INGRESS_INVALID) { - dprintk("(%s) threshold exceeded for ip %pI4, sending MPOA res req\n", - mpc->dev->name, &entry->ctrl_info.in_dst_ip); - entry->entry_state = INGRESS_RESOLVING; - msg.type = SND_MPOA_RES_RQST; - memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN); - msg.content.in_info = entry->ctrl_info; - qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); - if (qos != NULL) - msg.qos = qos->qos; - msg_to_mpoad(&msg, mpc); - entry->reply_wait = ktime_get_seconds(); - } - - return CLOSED; -} - -static void in_cache_put(in_cache_entry *entry) -{ - if (refcount_dec_and_test(&entry->use)) { - kfree_sensitive(entry); - } -} - -/* - * This should be called with write lock on - */ -static void in_cache_remove_entry(in_cache_entry *entry, - struct mpoa_client *client) -{ - struct atm_vcc *vcc; - struct k_message msg; - - vcc = entry->shortcut; - dprintk("removing an ingress entry, ip = %pI4\n", - &entry->ctrl_info.in_dst_ip); - - if (entry->prev != NULL) - entry->prev->next = entry->next; - else - client->in_cache = entry->next; - if (entry->next != NULL) - entry->next->prev = entry->prev; - client->in_ops->put(entry); - if (client->in_cache == NULL && client->eg_cache == NULL) { - msg.type = STOP_KEEP_ALIVE_SM; - msg_to_mpoad(&msg, client); - } - - /* Check if the egress side still uses this VCC */ - if (vcc != NULL) { - eg_cache_entry *eg_entry = client->eg_ops->get_by_vcc(vcc, - client); - if (eg_entry != NULL) { - client->eg_ops->put(eg_entry); - return; - } - vcc_release_async(vcc, -EPIPE); - } -} - -/* Call this every MPC-p2 seconds... Not exactly correct solution, - but an easy one... */ -static void clear_count_and_expired(struct mpoa_client *client) -{ - in_cache_entry *entry, *next_entry; - time64_t now; - - now = ktime_get_seconds(); - - write_lock_bh(&client->ingress_lock); - entry = client->in_cache; - while (entry != NULL) { - entry->count = 0; - next_entry = entry->next; - if ((now - entry->time) > entry->ctrl_info.holding_time) { - dprintk("holding time expired, ip = %pI4\n", - &entry->ctrl_info.in_dst_ip); - client->in_ops->remove_entry(entry, client); - } - entry = next_entry; - } - write_unlock_bh(&client->ingress_lock); -} - -/* Call this every MPC-p4 seconds. */ -static void check_resolving_entries(struct mpoa_client *client) -{ - - struct atm_mpoa_qos *qos; - in_cache_entry *entry; - time64_t now; - struct k_message msg; - - now = ktime_get_seconds(); - - read_lock_bh(&client->ingress_lock); - entry = client->in_cache; - while (entry != NULL) { - if (entry->entry_state == INGRESS_RESOLVING) { - - if ((now - entry->hold_down) - < client->parameters.mpc_p6) { - entry = entry->next; /* Entry in hold down */ - continue; - } - if ((now - entry->reply_wait) > entry->retry_time) { - entry->retry_time = MPC_C1 * (entry->retry_time); - /* - * Retry time maximum exceeded, - * put entry in hold down. - */ - if (entry->retry_time > client->parameters.mpc_p5) { - entry->hold_down = ktime_get_seconds(); - entry->retry_time = client->parameters.mpc_p4; - entry = entry->next; - continue; - } - /* Ask daemon to send a resolution request. */ - memset(&entry->hold_down, 0, sizeof(time64_t)); - msg.type = SND_MPOA_RES_RTRY; - memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN); - msg.content.in_info = entry->ctrl_info; - qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip); - if (qos != NULL) - msg.qos = qos->qos; - msg_to_mpoad(&msg, client); - entry->reply_wait = ktime_get_seconds(); - } - } - entry = entry->next; - } - read_unlock_bh(&client->ingress_lock); -} - -/* Call this every MPC-p5 seconds. */ -static void refresh_entries(struct mpoa_client *client) -{ - time64_t now; - struct in_cache_entry *entry = client->in_cache; - - ddprintk("refresh_entries\n"); - now = ktime_get_seconds(); - - read_lock_bh(&client->ingress_lock); - while (entry != NULL) { - if (entry->entry_state == INGRESS_RESOLVED) { - if (!(entry->refresh_time)) - entry->refresh_time = (2 * (entry->ctrl_info.holding_time))/3; - if ((now - entry->reply_wait) > - entry->refresh_time) { - dprintk("refreshing an entry.\n"); - entry->entry_state = INGRESS_REFRESHING; - - } - } - entry = entry->next; - } - read_unlock_bh(&client->ingress_lock); -} - -static void in_destroy_cache(struct mpoa_client *mpc) -{ - write_lock_irq(&mpc->ingress_lock); - while (mpc->in_cache != NULL) - mpc->in_ops->remove_entry(mpc->in_cache, mpc); - write_unlock_irq(&mpc->ingress_lock); -} - -static eg_cache_entry *eg_cache_get_by_cache_id(__be32 cache_id, - struct mpoa_client *mpc) -{ - eg_cache_entry *entry; - - read_lock_irq(&mpc->egress_lock); - entry = mpc->eg_cache; - while (entry != NULL) { - if (entry->ctrl_info.cache_id == cache_id) { - refcount_inc(&entry->use); - read_unlock_irq(&mpc->egress_lock); - return entry; - } - entry = entry->next; - } - read_unlock_irq(&mpc->egress_lock); - - return NULL; -} - -/* This can be called from any context since it saves CPU flags */ -static eg_cache_entry *eg_cache_get_by_tag(__be32 tag, struct mpoa_client *mpc) -{ - unsigned long flags; - eg_cache_entry *entry; - - read_lock_irqsave(&mpc->egress_lock, flags); - entry = mpc->eg_cache; - while (entry != NULL) { - if (entry->ctrl_info.tag == tag) { - refcount_inc(&entry->use); - read_unlock_irqrestore(&mpc->egress_lock, flags); - return entry; - } - entry = entry->next; - } - read_unlock_irqrestore(&mpc->egress_lock, flags); - - return NULL; -} - -/* This can be called from any context since it saves CPU flags */ -static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc, - struct mpoa_client *mpc) -{ - unsigned long flags; - eg_cache_entry *entry; - - read_lock_irqsave(&mpc->egress_lock, flags); - entry = mpc->eg_cache; - while (entry != NULL) { - if (entry->shortcut == vcc) { - refcount_inc(&entry->use); - read_unlock_irqrestore(&mpc->egress_lock, flags); - return entry; - } - entry = entry->next; - } - read_unlock_irqrestore(&mpc->egress_lock, flags); - - return NULL; -} - -static eg_cache_entry *eg_cache_get_by_src_ip(__be32 ipaddr, - struct mpoa_client *mpc) -{ - eg_cache_entry *entry; - - read_lock_irq(&mpc->egress_lock); - entry = mpc->eg_cache; - while (entry != NULL) { - if (entry->latest_ip_addr == ipaddr) { - refcount_inc(&entry->use); - read_unlock_irq(&mpc->egress_lock); - return entry; - } - entry = entry->next; - } - read_unlock_irq(&mpc->egress_lock); - - return NULL; -} - -static void eg_cache_put(eg_cache_entry *entry) -{ - if (refcount_dec_and_test(&entry->use)) { - kfree_sensitive(entry); - } -} - -/* - * This should be called with write lock on - */ -static void eg_cache_remove_entry(eg_cache_entry *entry, - struct mpoa_client *client) -{ - struct atm_vcc *vcc; - struct k_message msg; - - vcc = entry->shortcut; - dprintk("removing an egress entry.\n"); - if (entry->prev != NULL) - entry->prev->next = entry->next; - else - client->eg_cache = entry->next; - if (entry->next != NULL) - entry->next->prev = entry->prev; - client->eg_ops->put(entry); - if (client->in_cache == NULL && client->eg_cache == NULL) { - msg.type = STOP_KEEP_ALIVE_SM; - msg_to_mpoad(&msg, client); - } - - /* Check if the ingress side still uses this VCC */ - if (vcc != NULL) { - in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client); - if (in_entry != NULL) { - client->in_ops->put(in_entry); - return; - } - vcc_release_async(vcc, -EPIPE); - } -} - -static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, - struct mpoa_client *client) -{ - eg_cache_entry *entry = kzalloc_obj(eg_cache_entry); - - if (entry == NULL) { - pr_info("out of memory\n"); - return NULL; - } - - dprintk("adding an egress entry, ip = %pI4, this should be our IP\n", - &msg->content.eg_info.eg_dst_ip); - - refcount_set(&entry->use, 1); - dprintk("new_eg_cache_entry: about to lock\n"); - write_lock_irq(&client->egress_lock); - entry->next = client->eg_cache; - entry->prev = NULL; - if (client->eg_cache != NULL) - client->eg_cache->prev = entry; - client->eg_cache = entry; - - memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN); - entry->ctrl_info = msg->content.eg_info; - entry->time = ktime_get_seconds(); - entry->entry_state = EGRESS_RESOLVED; - dprintk("new_eg_cache_entry cache_id %u\n", - ntohl(entry->ctrl_info.cache_id)); - dprintk("mps_ip = %pI4\n", &entry->ctrl_info.mps_ip); - refcount_inc(&entry->use); - - write_unlock_irq(&client->egress_lock); - dprintk("new_eg_cache_entry: unlocked\n"); - - return entry; -} - -static void update_eg_cache_entry(eg_cache_entry *entry, uint16_t holding_time) -{ - entry->time = ktime_get_seconds(); - entry->entry_state = EGRESS_RESOLVED; - entry->ctrl_info.holding_time = holding_time; -} - -static void clear_expired(struct mpoa_client *client) -{ - eg_cache_entry *entry, *next_entry; - time64_t now; - struct k_message msg; - - now = ktime_get_seconds(); - - write_lock_irq(&client->egress_lock); - entry = client->eg_cache; - while (entry != NULL) { - next_entry = entry->next; - if ((now - entry->time) > entry->ctrl_info.holding_time) { - msg.type = SND_EGRESS_PURGE; - msg.content.eg_info = entry->ctrl_info; - dprintk("egress_cache: holding time expired, cache_id = %u.\n", - ntohl(entry->ctrl_info.cache_id)); - msg_to_mpoad(&msg, client); - client->eg_ops->remove_entry(entry, client); - } - entry = next_entry; - } - write_unlock_irq(&client->egress_lock); -} - -static void eg_destroy_cache(struct mpoa_client *mpc) -{ - write_lock_irq(&mpc->egress_lock); - while (mpc->eg_cache != NULL) - mpc->eg_ops->remove_entry(mpc->eg_cache, mpc); - write_unlock_irq(&mpc->egress_lock); -} - - -static const struct in_cache_ops ingress_ops = { - .add_entry = in_cache_add_entry, - .get = in_cache_get, - .get_with_mask = in_cache_get_with_mask, - .get_by_vcc = in_cache_get_by_vcc, - .put = in_cache_put, - .remove_entry = in_cache_remove_entry, - .cache_hit = cache_hit, - .clear_count = clear_count_and_expired, - .check_resolving = check_resolving_entries, - .refresh = refresh_entries, - .destroy_cache = in_destroy_cache -}; - -static const struct eg_cache_ops egress_ops = { - .add_entry = eg_cache_add_entry, - .get_by_cache_id = eg_cache_get_by_cache_id, - .get_by_tag = eg_cache_get_by_tag, - .get_by_vcc = eg_cache_get_by_vcc, - .get_by_src_ip = eg_cache_get_by_src_ip, - .put = eg_cache_put, - .remove_entry = eg_cache_remove_entry, - .update = update_eg_cache_entry, - .clear_expired = clear_expired, - .destroy_cache = eg_destroy_cache -}; - -void atm_mpoa_init_cache(struct mpoa_client *mpc) -{ - mpc->in_ops = &ingress_ops; - mpc->eg_ops = &egress_ops; -} diff --git a/net/atm/mpoa_caches.h b/net/atm/mpoa_caches.h deleted file mode 100644 index 464c4c7f8d1f..000000000000 --- a/net/atm/mpoa_caches.h +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef MPOA_CACHES_H -#define MPOA_CACHES_H - -#include -#include -#include -#include -#include -#include -#include - -struct mpoa_client; - -void atm_mpoa_init_cache(struct mpoa_client *mpc); - -typedef struct in_cache_entry { - struct in_cache_entry *next; - struct in_cache_entry *prev; - time64_t time; - time64_t reply_wait; - time64_t hold_down; - uint32_t packets_fwded; - uint16_t entry_state; - uint32_t retry_time; - uint32_t refresh_time; - uint32_t count; - struct atm_vcc *shortcut; - uint8_t MPS_ctrl_ATM_addr[ATM_ESA_LEN]; - struct in_ctrl_info ctrl_info; - refcount_t use; -} in_cache_entry; - -struct in_cache_ops{ - in_cache_entry *(*add_entry)(__be32 dst_ip, - struct mpoa_client *client); - in_cache_entry *(*get)(__be32 dst_ip, struct mpoa_client *client); - in_cache_entry *(*get_with_mask)(__be32 dst_ip, - struct mpoa_client *client, - __be32 mask); - in_cache_entry *(*get_by_vcc)(struct atm_vcc *vcc, - struct mpoa_client *client); - void (*put)(in_cache_entry *entry); - void (*remove_entry)(in_cache_entry *delEntry, - struct mpoa_client *client ); - int (*cache_hit)(in_cache_entry *entry, - struct mpoa_client *client); - void (*clear_count)(struct mpoa_client *client); - void (*check_resolving)(struct mpoa_client *client); - void (*refresh)(struct mpoa_client *client); - void (*destroy_cache)(struct mpoa_client *mpc); -}; - -typedef struct eg_cache_entry{ - struct eg_cache_entry *next; - struct eg_cache_entry *prev; - time64_t time; - uint8_t MPS_ctrl_ATM_addr[ATM_ESA_LEN]; - struct atm_vcc *shortcut; - uint32_t packets_rcvd; - uint16_t entry_state; - __be32 latest_ip_addr; /* The src IP address of the last packet */ - struct eg_ctrl_info ctrl_info; - refcount_t use; -} eg_cache_entry; - -struct eg_cache_ops{ - eg_cache_entry *(*add_entry)(struct k_message *msg, struct mpoa_client *client); - eg_cache_entry *(*get_by_cache_id)(__be32 cache_id, struct mpoa_client *client); - eg_cache_entry *(*get_by_tag)(__be32 cache_id, struct mpoa_client *client); - eg_cache_entry *(*get_by_vcc)(struct atm_vcc *vcc, struct mpoa_client *client); - eg_cache_entry *(*get_by_src_ip)(__be32 ipaddr, struct mpoa_client *client); - void (*put)(eg_cache_entry *entry); - void (*remove_entry)(eg_cache_entry *entry, struct mpoa_client *client); - void (*update)(eg_cache_entry *entry, uint16_t holding_time); - void (*clear_expired)(struct mpoa_client *client); - void (*destroy_cache)(struct mpoa_client *mpc); -}; - - -/* Ingress cache entry states */ - -#define INGRESS_REFRESHING 3 -#define INGRESS_RESOLVED 2 -#define INGRESS_RESOLVING 1 -#define INGRESS_INVALID 0 - -/* VCC states */ - -#define OPEN 1 -#define CLOSED 0 - -/* Egress cache entry states */ - -#define EGRESS_RESOLVED 2 -#define EGRESS_PURGE 1 -#define EGRESS_INVALID 0 - -#endif diff --git a/net/atm/mpoa_proc.c b/net/atm/mpoa_proc.c deleted file mode 100644 index aaf64b953915..000000000000 --- a/net/atm/mpoa_proc.c +++ /dev/null @@ -1,307 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ - -#ifdef CONFIG_PROC_FS -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mpc.h" -#include "mpoa_caches.h" - -/* - * mpoa_proc.c: Implementation MPOA client's proc - * file system statistics - */ - -#if 1 -#define dprintk(format, args...) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args) /* debug */ -#else -#define dprintk(format, args...) \ - do { if (0) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\ - } while (0) -#endif - -#if 0 -#define ddprintk(format, args...) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args) /* debug */ -#else -#define ddprintk(format, args...) \ - do { if (0) \ - printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\ - } while (0) -#endif - -#define STAT_FILE_NAME "mpc" /* Our statistic file's name */ - -extern struct mpoa_client *mpcs; -extern struct proc_dir_entry *atm_proc_root; /* from proc.c. */ - -static int proc_mpc_open(struct inode *inode, struct file *file); -static ssize_t proc_mpc_write(struct file *file, const char __user *buff, - size_t nbytes, loff_t *ppos); - -static int parse_qos(const char *buff); - -static const struct proc_ops mpc_proc_ops = { - .proc_open = proc_mpc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_write = proc_mpc_write, - .proc_release = seq_release, -}; - -/* - * Returns the state of an ingress cache entry as a string - */ -static const char *ingress_state_string(int state) -{ - switch (state) { - case INGRESS_RESOLVING: - return "resolving "; - case INGRESS_RESOLVED: - return "resolved "; - case INGRESS_INVALID: - return "invalid "; - case INGRESS_REFRESHING: - return "refreshing "; - } - - return ""; -} - -/* - * Returns the state of an egress cache entry as a string - */ -static const char *egress_state_string(int state) -{ - switch (state) { - case EGRESS_RESOLVED: - return "resolved "; - case EGRESS_PURGE: - return "purge "; - case EGRESS_INVALID: - return "invalid "; - } - - return ""; -} - -/* - * FIXME: mpcs (and per-mpc lists) have no locking whatsoever. - */ - -static void *mpc_start(struct seq_file *m, loff_t *pos) -{ - loff_t l = *pos; - struct mpoa_client *mpc; - - if (!l--) - return SEQ_START_TOKEN; - for (mpc = mpcs; mpc; mpc = mpc->next) - if (!l--) - return mpc; - return NULL; -} - -static void *mpc_next(struct seq_file *m, void *v, loff_t *pos) -{ - struct mpoa_client *p = v; - (*pos)++; - return v == SEQ_START_TOKEN ? mpcs : p->next; -} - -static void mpc_stop(struct seq_file *m, void *v) -{ -} - -/* - * READING function - called when the /proc/atm/mpoa file is read from. - */ -static int mpc_show(struct seq_file *m, void *v) -{ - struct mpoa_client *mpc = v; - int i; - in_cache_entry *in_entry; - eg_cache_entry *eg_entry; - time64_t now; - unsigned char ip_string[16]; - - if (v == SEQ_START_TOKEN) { - atm_mpoa_disp_qos(m); - return 0; - } - - seq_printf(m, "\nInterface %d:\n\n", mpc->dev_num); - seq_printf(m, "Ingress Entries:\nIP address State Holding time Packets fwded VPI VCI\n"); - now = ktime_get_seconds(); - - for (in_entry = mpc->in_cache; in_entry; in_entry = in_entry->next) { - unsigned long seconds_delta = now - in_entry->time; - - sprintf(ip_string, "%pI4", &in_entry->ctrl_info.in_dst_ip); - seq_printf(m, "%-16s%s%-14lu%-12u", - ip_string, - ingress_state_string(in_entry->entry_state), - in_entry->ctrl_info.holding_time - - seconds_delta, - in_entry->packets_fwded); - if (in_entry->shortcut) - seq_printf(m, " %-3d %-3d", - in_entry->shortcut->vpi, - in_entry->shortcut->vci); - seq_printf(m, "\n"); - } - - seq_printf(m, "\n"); - seq_printf(m, "Egress Entries:\nIngress MPC ATM addr\nCache-id State Holding time Packets recvd Latest IP addr VPI VCI\n"); - for (eg_entry = mpc->eg_cache; eg_entry; eg_entry = eg_entry->next) { - unsigned char *p = eg_entry->ctrl_info.in_MPC_data_ATM_addr; - unsigned long seconds_delta = now - eg_entry->time; - - for (i = 0; i < ATM_ESA_LEN; i++) - seq_printf(m, "%02x", p[i]); - seq_printf(m, "\n%-16lu%s%-14lu%-15u", - (unsigned long)ntohl(eg_entry->ctrl_info.cache_id), - egress_state_string(eg_entry->entry_state), - (eg_entry->ctrl_info.holding_time - seconds_delta), - eg_entry->packets_rcvd); - - /* latest IP address */ - sprintf(ip_string, "%pI4", &eg_entry->latest_ip_addr); - seq_printf(m, "%-16s", ip_string); - - if (eg_entry->shortcut) - seq_printf(m, " %-3d %-3d", - eg_entry->shortcut->vpi, - eg_entry->shortcut->vci); - seq_printf(m, "\n"); - } - seq_printf(m, "\n"); - return 0; -} - -static const struct seq_operations mpc_op = { - .start = mpc_start, - .next = mpc_next, - .stop = mpc_stop, - .show = mpc_show -}; - -static int proc_mpc_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &mpc_op); -} - -static ssize_t proc_mpc_write(struct file *file, const char __user *buff, - size_t nbytes, loff_t *ppos) -{ - char *page, *p; - unsigned int len; - - if (nbytes == 0) - return 0; - - if (nbytes >= PAGE_SIZE) - nbytes = PAGE_SIZE-1; - - page = (char *)__get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - for (p = page, len = 0; len < nbytes; p++) { - if (get_user(*p, buff++)) { - free_page((unsigned long)page); - return -EFAULT; - } - len += 1; - if (*p == '\0' || *p == '\n') - break; - } - - *p = '\0'; - - if (!parse_qos(page)) - printk("mpoa: proc_mpc_write: could not parse '%s'\n", page); - - free_page((unsigned long)page); - - return len; -} - -static int parse_qos(const char *buff) -{ - /* possible lines look like this - * add 130.230.54.142 tx=max_pcr,max_sdu rx=max_pcr,max_sdu - */ - unsigned char ip[4]; - int tx_pcr, tx_sdu, rx_pcr, rx_sdu; - __be32 ipaddr; - struct atm_qos qos; - - memset(&qos, 0, sizeof(struct atm_qos)); - - if (sscanf(buff, "del %hhu.%hhu.%hhu.%hhu", - ip, ip+1, ip+2, ip+3) == 4) { - ipaddr = *(__be32 *)ip; - return atm_mpoa_delete_qos(atm_mpoa_search_qos(ipaddr)); - } - - if (sscanf(buff, "add %hhu.%hhu.%hhu.%hhu tx=%d,%d rx=tx", - ip, ip+1, ip+2, ip+3, &tx_pcr, &tx_sdu) == 6) { - rx_pcr = tx_pcr; - rx_sdu = tx_sdu; - } else if (sscanf(buff, "add %hhu.%hhu.%hhu.%hhu tx=%d,%d rx=%d,%d", - ip, ip+1, ip+2, ip+3, &tx_pcr, &tx_sdu, &rx_pcr, &rx_sdu) != 8) - return 0; - - ipaddr = *(__be32 *)ip; - qos.txtp.traffic_class = ATM_CBR; - qos.txtp.max_pcr = tx_pcr; - qos.txtp.max_sdu = tx_sdu; - qos.rxtp.traffic_class = ATM_CBR; - qos.rxtp.max_pcr = rx_pcr; - qos.rxtp.max_sdu = rx_sdu; - qos.aal = ATM_AAL5; - dprintk("parse_qos(): setting qos parameters to tx=%d,%d rx=%d,%d\n", - qos.txtp.max_pcr, qos.txtp.max_sdu, - qos.rxtp.max_pcr, qos.rxtp.max_sdu); - - atm_mpoa_add_qos(ipaddr, &qos); - return 1; -} - -/* - * INITIALIZATION function - called when module is initialized/loaded. - */ -int mpc_proc_init(void) -{ - struct proc_dir_entry *p; - - p = proc_create(STAT_FILE_NAME, 0, atm_proc_root, &mpc_proc_ops); - if (!p) { - pr_err("Unable to initialize /proc/atm/%s\n", STAT_FILE_NAME); - return -ENOMEM; - } - return 0; -} - -/* - * DELETING function - called when module is removed. - */ -void mpc_proc_clean(void) -{ - remove_proc_entry(STAT_FILE_NAME, atm_proc_root); -} - -#endif /* CONFIG_PROC_FS */ diff --git a/net/atm/proc.c b/net/atm/proc.c index 9bf736290e48..b650da764a23 100644 --- a/net/atm/proc.c +++ b/net/atm/proc.c @@ -21,11 +21,9 @@ #include #include #include -#include #include /* for __init */ #include #include -#include #include #include /* for HZ */ #include @@ -155,15 +153,6 @@ static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc) class_name[vcc->qos.rxtp.traffic_class], vcc->qos.txtp.min_pcr, class_name[vcc->qos.txtp.traffic_class]); - if (test_bit(ATM_VF_IS_CLIP, &vcc->flags)) { - struct clip_vcc *clip_vcc = CLIP_VCC(vcc); - struct net_device *dev; - - dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL; - seq_printf(seq, "CLIP, Itf:%s, Encap:", - dev ? dev->name : "none?"); - seq_printf(seq, "%s", clip_vcc->encap ? "LLC/SNAP" : "None"); - } seq_putc(seq, '\n'); } diff --git a/net/atm/signaling.c b/net/atm/signaling.c index 358fbe5e4d1d..b991d937205a 100644 --- a/net/atm/signaling.c +++ b/net/atm/signaling.c @@ -179,6 +179,7 @@ as_indicate_complete: break; default: pr_alert("bad message type %d\n", (int)msg->type); + dev_kfree_skb(skb); /* Paired with find_get_vcc(msg->vcc) above */ sock_put(sk); return -EINVAL; diff --git a/net/ax25/Kconfig b/net/ax25/Kconfig deleted file mode 100644 index 310169ce1488..000000000000 --- a/net/ax25/Kconfig +++ /dev/null @@ -1,108 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Amateur Radio protocols and AX.25 device configuration -# - -menuconfig HAMRADIO - depends on NET - bool "Amateur Radio support" - help - If you want to connect your Linux box to an amateur radio, answer Y - here. You want to read - and more specifically about AX.25 on Linux - . - - Note that the answer to this question won't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about amateur radio. - -comment "Packet Radio protocols" - depends on HAMRADIO - -config AX25 - tristate "Amateur Radio AX.25 Level 2 protocol" - depends on HAMRADIO - help - This is the protocol used for computer communication over amateur - radio. It is either used by itself for point-to-point links, or to - carry other protocols such as tcp/ip. To use it, you need a device - that connects your Linux box to your amateur radio. You can either - use a low speed TNC (a Terminal Node Controller acts as a kind of - modem connecting your computer's serial port to your radio's - microphone input and speaker output) supporting the KISS protocol or - one of the various SCC cards that are supported by the generic Z8530 - or the DMA SCC driver. Another option are the Baycom modem serial - and parallel port hacks or the sound card modem (supported by their - own drivers). If you say Y here, you also have to say Y to one of - those drivers. - - Information about where to get supporting software for Linux amateur - radio as well as information about how to configure an AX.25 port is - contained in the AX25-HOWTO, available from - . You might also want to - check out the file in the - kernel source. More information about digital amateur radio in - general is on the WWW at - . - - To compile this driver as a module, choose M here: the - module will be called ax25. - -config AX25_DAMA_SLAVE - bool "AX.25 DAMA Slave support" - default y - depends on AX25 - help - DAMA is a mechanism to prevent collisions when doing AX.25 - networking. A DAMA server (called "master") accepts incoming traffic - from clients (called "slaves") and redistributes it to other slaves. - If you say Y here, your Linux box will act as a DAMA slave; this is - transparent in that you don't have to do any special DAMA - configuration. Linux cannot yet act as a DAMA server. This option - only compiles DAMA slave support into the kernel. It still needs to - be enabled at runtime. For more about DAMA see - . If unsure, say Y. - -config NETROM - tristate "Amateur Radio NET/ROM protocol" - depends on AX25 - help - NET/ROM is a network layer protocol on top of AX.25 useful for - routing. - - A comprehensive listing of all the software for Linux amateur radio - users as well as information about how to configure an AX.25 port is - contained in the Linux Ham Wiki, available from - . You also might want to check out - the file . More information - about digital amateur radio in general is on the WWW at - . - - To compile this driver as a module, choose M here: the - module will be called netrom. - -config ROSE - tristate "Amateur Radio X.25 PLP (Rose)" - depends on AX25 - help - The Packet Layer Protocol (PLP) is a way to route packets over X.25 - connections in general and amateur radio AX.25 connections in - particular, essentially an alternative to NET/ROM. - - A comprehensive listing of all the software for Linux amateur radio - users as well as information about how to configure an AX.25 port is - contained in the Linux Ham Wiki, available from - . You also might want to check out - the file . More information - about digital amateur radio in general is on the WWW at - . - - To compile this driver as a module, choose M here: the - module will be called rose. - -menu "AX.25 network device drivers" - depends on HAMRADIO && AX25 - -source "drivers/net/hamradio/Kconfig" - -endmenu diff --git a/net/ax25/Makefile b/net/ax25/Makefile deleted file mode 100644 index 2e53affc8568..000000000000 --- a/net/ax25/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the Linux AX.25 layer. -# - -obj-$(CONFIG_AX25) += ax25.o - -ax25-y := ax25_addr.o ax25_dev.o ax25_iface.o ax25_in.o ax25_ip.o ax25_out.o \ - ax25_route.o ax25_std_in.o ax25_std_subr.o ax25_std_timer.o \ - ax25_subr.o ax25_timer.o ax25_uid.o af_ax25.o -ax25-$(CONFIG_AX25_DAMA_SLAVE) += ax25_ds_in.o ax25_ds_subr.o ax25_ds_timer.o -ax25-$(CONFIG_SYSCTL) += sysctl_net_ax25.o diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c deleted file mode 100644 index 9d236e64f5f5..000000000000 --- a/net/ax25/af_ax25.c +++ /dev/null @@ -1,2089 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Darryl Miles G7LED (dlm@g7led.demon.co.uk) - * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) - * Copyright (C) Hans Alblas PE1AYX (hans@esrac.ele.tue.nl) - * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For TIOCINQ/OUTQ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -HLIST_HEAD(ax25_list); -DEFINE_SPINLOCK(ax25_list_lock); - -static const struct proto_ops ax25_proto_ops; - -static void ax25_free_sock(struct sock *sk) -{ - ax25_cb_put(sk_to_ax25(sk)); -} - -/* - * Socket removal during an interrupt is now safe. - */ -static void ax25_cb_del(ax25_cb *ax25) -{ - spin_lock_bh(&ax25_list_lock); - if (!hlist_unhashed(&ax25->ax25_node)) { - hlist_del_init(&ax25->ax25_node); - ax25_cb_put(ax25); - } - spin_unlock_bh(&ax25_list_lock); -} - -/* - * Kill all bound sockets on a dropped device. - */ -static void ax25_kill_by_device(struct net_device *dev) -{ - ax25_dev *ax25_dev; - ax25_cb *s; - struct sock *sk; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return; - ax25_dev->device_up = false; - - spin_lock_bh(&ax25_list_lock); -again: - ax25_for_each(s, &ax25_list) { - if (s->ax25_dev == ax25_dev) { - sk = s->sk; - if (!sk) { - spin_unlock_bh(&ax25_list_lock); - ax25_disconnect(s, ENETUNREACH); - s->ax25_dev = NULL; - ax25_cb_del(s); - spin_lock_bh(&ax25_list_lock); - goto again; - } - sock_hold(sk); - spin_unlock_bh(&ax25_list_lock); - lock_sock(sk); - ax25_disconnect(s, ENETUNREACH); - s->ax25_dev = NULL; - if (sk->sk_socket) { - netdev_put(ax25_dev->dev, - &s->dev_tracker); - ax25_dev_put(ax25_dev); - } - ax25_cb_del(s); - release_sock(sk); - spin_lock_bh(&ax25_list_lock); - sock_put(sk); - /* The entry could have been deleted from the - * list meanwhile and thus the next pointer is - * no longer valid. Play it safe and restart - * the scan. Forward progress is ensured - * because we set s->ax25_dev to NULL and we - * are never passed a NULL 'dev' argument. - */ - goto again; - } - } - spin_unlock_bh(&ax25_list_lock); -} - -/* - * Handle device status changes. - */ -static int ax25_device_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - /* Reject non AX.25 devices */ - if (dev->type != ARPHRD_AX25) - return NOTIFY_DONE; - - switch (event) { - case NETDEV_UP: - ax25_dev_device_up(dev); - break; - case NETDEV_DOWN: - ax25_kill_by_device(dev); - ax25_rt_device_down(dev); - ax25_dev_device_down(dev); - break; - default: - break; - } - - return NOTIFY_DONE; -} - -/* - * Add a socket to the bound sockets list. - */ -void ax25_cb_add(ax25_cb *ax25) -{ - spin_lock_bh(&ax25_list_lock); - ax25_cb_hold(ax25); - hlist_add_head(&ax25->ax25_node, &ax25_list); - spin_unlock_bh(&ax25_list_lock); -} - -/* - * Find a socket that wants to accept the SABM we have just - * received. - */ -struct sock *ax25_find_listener(ax25_address *addr, int digi, - struct net_device *dev, int type) -{ - ax25_cb *s; - - spin_lock(&ax25_list_lock); - ax25_for_each(s, &ax25_list) { - if ((s->iamdigi && !digi) || (!s->iamdigi && digi)) - continue; - if (s->sk && !ax25cmp(&s->source_addr, addr) && - s->sk->sk_type == type && s->sk->sk_state == TCP_LISTEN) { - /* If device is null we match any device */ - if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) { - sock_hold(s->sk); - spin_unlock(&ax25_list_lock); - return s->sk; - } - } - } - spin_unlock(&ax25_list_lock); - - return NULL; -} - -/* - * Find an AX.25 socket given both ends. - */ -struct sock *ax25_get_socket(ax25_address *my_addr, ax25_address *dest_addr, - int type) -{ - struct sock *sk = NULL; - ax25_cb *s; - - spin_lock(&ax25_list_lock); - ax25_for_each(s, &ax25_list) { - if (s->sk && !ax25cmp(&s->source_addr, my_addr) && - !ax25cmp(&s->dest_addr, dest_addr) && - s->sk->sk_type == type) { - sk = s->sk; - sock_hold(sk); - break; - } - } - - spin_unlock(&ax25_list_lock); - - return sk; -} - -/* - * Find an AX.25 control block given both ends. It will only pick up - * floating AX.25 control blocks or non Raw socket bound control blocks. - */ -ax25_cb *ax25_find_cb(const ax25_address *src_addr, ax25_address *dest_addr, - ax25_digi *digi, struct net_device *dev) -{ - ax25_cb *s; - - spin_lock_bh(&ax25_list_lock); - ax25_for_each(s, &ax25_list) { - if (s->sk && s->sk->sk_type != SOCK_SEQPACKET) - continue; - if (s->ax25_dev == NULL) - continue; - if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) { - if (digi != NULL && digi->ndigi != 0) { - if (s->digipeat == NULL) - continue; - if (ax25digicmp(s->digipeat, digi) != 0) - continue; - } else { - if (s->digipeat != NULL && s->digipeat->ndigi != 0) - continue; - } - ax25_cb_hold(s); - spin_unlock_bh(&ax25_list_lock); - - return s; - } - } - spin_unlock_bh(&ax25_list_lock); - - return NULL; -} - -EXPORT_SYMBOL(ax25_find_cb); - -void ax25_send_to_raw(ax25_address *addr, struct sk_buff *skb, int proto) -{ - ax25_cb *s; - struct sk_buff *copy; - - spin_lock(&ax25_list_lock); - ax25_for_each(s, &ax25_list) { - if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && - s->sk->sk_type == SOCK_RAW && - s->sk->sk_protocol == proto && - s->ax25_dev->dev == skb->dev && - atomic_read(&s->sk->sk_rmem_alloc) <= s->sk->sk_rcvbuf) { - if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL) - continue; - if (sock_queue_rcv_skb(s->sk, copy) != 0) - kfree_skb(copy); - } - } - spin_unlock(&ax25_list_lock); -} - -/* - * Deferred destroy. - */ -void ax25_destroy_socket(ax25_cb *); - -/* - * Handler for deferred kills. - */ -static void ax25_destroy_timer(struct timer_list *t) -{ - ax25_cb *ax25 = timer_container_of(ax25, t, dtimer); - struct sock *sk; - - sk=ax25->sk; - - bh_lock_sock(sk); - sock_hold(sk); - ax25_destroy_socket(ax25); - bh_unlock_sock(sk); - sock_put(sk); -} - -/* - * This is called from user mode and the timers. Thus it protects itself - * against interrupt users but doesn't worry about being called during - * work. Once it is removed from the queue no interrupt or bottom half - * will touch it and we are (fairly 8-) ) safe. - */ -void ax25_destroy_socket(ax25_cb *ax25) -{ - struct sk_buff *skb; - - ax25_cb_del(ax25); - - ax25_stop_heartbeat(ax25); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); - - ax25_clear_queues(ax25); /* Flush the queues */ - - if (ax25->sk != NULL) { - while ((skb = skb_dequeue(&ax25->sk->sk_receive_queue)) != NULL) { - if (skb->sk != ax25->sk) { - /* A pending connection */ - ax25_cb *sax25 = sk_to_ax25(skb->sk); - - /* Queue the unaccepted socket for death */ - sock_orphan(skb->sk); - - /* 9A4GL: hack to release unaccepted sockets */ - skb->sk->sk_state = TCP_LISTEN; - - ax25_start_heartbeat(sax25); - sax25->state = AX25_STATE_0; - } - - kfree_skb(skb); - } - skb_queue_purge(&ax25->sk->sk_write_queue); - } - - if (ax25->sk != NULL) { - if (sk_has_allocations(ax25->sk)) { - /* Defer: outstanding buffers */ - timer_setup(&ax25->dtimer, ax25_destroy_timer, 0); - ax25->dtimer.expires = jiffies + 2 * HZ; - add_timer(&ax25->dtimer); - } else { - struct sock *sk=ax25->sk; - ax25->sk=NULL; - sock_put(sk); - } - } else { - ax25_cb_put(ax25); - } -} - -/* - * dl1bke 960311: set parameters for existing AX.25 connections, - * includes a KILL command to abort any connection. - * VERY useful for debugging ;-) - */ -static int ax25_ctl_ioctl(const unsigned int cmd, void __user *arg) -{ - struct ax25_ctl_struct ax25_ctl; - ax25_digi digi; - ax25_dev *ax25_dev; - ax25_cb *ax25; - unsigned int k; - int ret = 0; - - if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl))) - return -EFAULT; - - if (ax25_ctl.digi_count > AX25_MAX_DIGIS) - return -EINVAL; - - if (ax25_ctl.arg > ULONG_MAX / HZ && ax25_ctl.cmd != AX25_KILL) - return -EINVAL; - - ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr); - if (!ax25_dev) - return -ENODEV; - - digi.ndigi = ax25_ctl.digi_count; - for (k = 0; k < digi.ndigi; k++) - digi.calls[k] = ax25_ctl.digi_addr[k]; - - ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, &digi, ax25_dev->dev); - if (!ax25) { - ax25_dev_put(ax25_dev); - return -ENOTCONN; - } - - switch (ax25_ctl.cmd) { - case AX25_KILL: - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); -#ifdef CONFIG_AX25_DAMA_SLAVE - if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE) - ax25_dama_off(ax25); -#endif - ax25_disconnect(ax25, ENETRESET); - break; - - case AX25_WINDOW: - if (ax25->modulus == AX25_MODULUS) { - if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7) - goto einval_put; - } else { - if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63) - goto einval_put; - } - ax25->window = ax25_ctl.arg; - break; - - case AX25_T1: - if (ax25_ctl.arg < 1 || ax25_ctl.arg > ULONG_MAX / HZ) - goto einval_put; - ax25->rtt = (ax25_ctl.arg * HZ) / 2; - ax25->t1 = ax25_ctl.arg * HZ; - break; - - case AX25_T2: - if (ax25_ctl.arg < 1 || ax25_ctl.arg > ULONG_MAX / HZ) - goto einval_put; - ax25->t2 = ax25_ctl.arg * HZ; - break; - - case AX25_N2: - if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31) - goto einval_put; - ax25->n2count = 0; - ax25->n2 = ax25_ctl.arg; - break; - - case AX25_T3: - if (ax25_ctl.arg > ULONG_MAX / HZ) - goto einval_put; - ax25->t3 = ax25_ctl.arg * HZ; - break; - - case AX25_IDLE: - if (ax25_ctl.arg > ULONG_MAX / (60 * HZ)) - goto einval_put; - - ax25->idle = ax25_ctl.arg * 60 * HZ; - break; - - case AX25_PACLEN: - if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535) - goto einval_put; - ax25->paclen = ax25_ctl.arg; - break; - - default: - goto einval_put; - } - -out_put: - ax25_dev_put(ax25_dev); - ax25_cb_put(ax25); - return ret; - -einval_put: - ret = -EINVAL; - goto out_put; -} - -static void ax25_fillin_cb_from_dev(ax25_cb *ax25, const ax25_dev *ax25_dev) -{ - ax25->rtt = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]) / 2; - ax25->t1 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]); - ax25->t2 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T2]); - ax25->t3 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T3]); - ax25->n2 = ax25_dev->values[AX25_VALUES_N2]; - ax25->paclen = ax25_dev->values[AX25_VALUES_PACLEN]; - ax25->idle = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_IDLE]); - ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF]; - - if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW]; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_WINDOW]; - } -} - -/* - * Fill in a created AX.25 created control block with the default - * values for a particular device. - */ -void ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev) -{ - ax25->ax25_dev = ax25_dev; - - if (ax25->ax25_dev != NULL) { - ax25_fillin_cb_from_dev(ax25, ax25_dev); - return; - } - - /* - * No device, use kernel / AX.25 spec default values - */ - ax25->rtt = msecs_to_jiffies(AX25_DEF_T1) / 2; - ax25->t1 = msecs_to_jiffies(AX25_DEF_T1); - ax25->t2 = msecs_to_jiffies(AX25_DEF_T2); - ax25->t3 = msecs_to_jiffies(AX25_DEF_T3); - ax25->n2 = AX25_DEF_N2; - ax25->paclen = AX25_DEF_PACLEN; - ax25->idle = msecs_to_jiffies(AX25_DEF_IDLE); - ax25->backoff = AX25_DEF_BACKOFF; - - if (AX25_DEF_AXDEFMODE) { - ax25->modulus = AX25_EMODULUS; - ax25->window = AX25_DEF_EWINDOW; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = AX25_DEF_WINDOW; - } -} - -/* - * Create an empty AX.25 control block. - */ -ax25_cb *ax25_create_cb(void) -{ - ax25_cb *ax25; - - if ((ax25 = kzalloc_obj(*ax25, GFP_ATOMIC)) == NULL) - return NULL; - - refcount_set(&ax25->refcount, 1); - - skb_queue_head_init(&ax25->write_queue); - skb_queue_head_init(&ax25->frag_queue); - skb_queue_head_init(&ax25->ack_queue); - skb_queue_head_init(&ax25->reseq_queue); - - ax25_setup_timers(ax25); - - ax25_fillin_cb(ax25, NULL); - - ax25->state = AX25_STATE_0; - - return ax25; -} - -/* - * Handling for system calls applied via the various interfaces to an - * AX25 socket object - */ - -static int ax25_setsockopt(struct socket *sock, int level, int optname, - sockptr_t optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - ax25_cb *ax25; - struct net_device *dev; - char devname[IFNAMSIZ]; - unsigned int opt; - int res = 0; - - if (level != SOL_AX25) - return -ENOPROTOOPT; - - if (optlen < sizeof(unsigned int)) - return -EINVAL; - - if (copy_from_sockptr(&opt, optval, sizeof(unsigned int))) - return -EFAULT; - - lock_sock(sk); - ax25 = sk_to_ax25(sk); - - switch (optname) { - case AX25_WINDOW: - if (ax25->modulus == AX25_MODULUS) { - if (opt < 1 || opt > 7) { - res = -EINVAL; - break; - } - } else { - if (opt < 1 || opt > 63) { - res = -EINVAL; - break; - } - } - ax25->window = opt; - break; - - case AX25_T1: - if (opt < 1 || opt > UINT_MAX / HZ) { - res = -EINVAL; - break; - } - ax25->rtt = (opt * HZ) >> 1; - ax25->t1 = opt * HZ; - break; - - case AX25_T2: - if (opt < 1 || opt > UINT_MAX / HZ) { - res = -EINVAL; - break; - } - ax25->t2 = opt * HZ; - break; - - case AX25_N2: - if (opt < 1 || opt > 31) { - res = -EINVAL; - break; - } - ax25->n2 = opt; - break; - - case AX25_T3: - if (opt < 1 || opt > UINT_MAX / HZ) { - res = -EINVAL; - break; - } - ax25->t3 = opt * HZ; - break; - - case AX25_IDLE: - if (opt > UINT_MAX / (60 * HZ)) { - res = -EINVAL; - break; - } - ax25->idle = opt * 60 * HZ; - break; - - case AX25_BACKOFF: - if (opt > 2) { - res = -EINVAL; - break; - } - ax25->backoff = opt; - break; - - case AX25_EXTSEQ: - ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS; - break; - - case AX25_PIDINCL: - ax25->pidincl = opt ? 1 : 0; - break; - - case AX25_IAMDIGI: - ax25->iamdigi = opt ? 1 : 0; - break; - - case AX25_PACLEN: - if (opt < 16 || opt > 65535) { - res = -EINVAL; - break; - } - ax25->paclen = opt; - break; - - case SO_BINDTODEVICE: - if (optlen > IFNAMSIZ - 1) - optlen = IFNAMSIZ - 1; - - memset(devname, 0, sizeof(devname)); - - if (copy_from_sockptr(devname, optval, optlen)) { - res = -EFAULT; - break; - } - - if (sk->sk_type == SOCK_SEQPACKET && - (sock->state != SS_UNCONNECTED || - sk->sk_state == TCP_LISTEN)) { - res = -EADDRNOTAVAIL; - break; - } - - rcu_read_lock(); - dev = dev_get_by_name_rcu(&init_net, devname); - if (!dev) { - rcu_read_unlock(); - res = -ENODEV; - break; - } - - if (ax25->ax25_dev) { - if (dev == ax25->ax25_dev->dev) { - rcu_read_unlock(); - break; - } - netdev_put(ax25->ax25_dev->dev, &ax25->dev_tracker); - ax25_dev_put(ax25->ax25_dev); - } - - ax25->ax25_dev = ax25_dev_ax25dev(dev); - if (!ax25->ax25_dev) { - rcu_read_unlock(); - res = -ENODEV; - break; - } - ax25_fillin_cb(ax25, ax25->ax25_dev); - netdev_hold(dev, &ax25->dev_tracker, GFP_ATOMIC); - ax25_dev_hold(ax25->ax25_dev); - rcu_read_unlock(); - break; - - default: - res = -ENOPROTOOPT; - } - release_sock(sk); - - return res; -} - -static int ax25_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - ax25_cb *ax25; - struct ax25_dev *ax25_dev; - char devname[IFNAMSIZ]; - void *valptr; - int val = 0; - int maxlen, length; - - if (level != SOL_AX25) - return -ENOPROTOOPT; - - if (get_user(maxlen, optlen)) - return -EFAULT; - - if (maxlen < 1) - return -EFAULT; - - valptr = &val; - length = min_t(unsigned int, maxlen, sizeof(int)); - - lock_sock(sk); - ax25 = sk_to_ax25(sk); - - switch (optname) { - case AX25_WINDOW: - val = ax25->window; - break; - - case AX25_T1: - val = ax25->t1 / HZ; - break; - - case AX25_T2: - val = ax25->t2 / HZ; - break; - - case AX25_N2: - val = ax25->n2; - break; - - case AX25_T3: - val = ax25->t3 / HZ; - break; - - case AX25_IDLE: - val = ax25->idle / (60 * HZ); - break; - - case AX25_BACKOFF: - val = ax25->backoff; - break; - - case AX25_EXTSEQ: - val = (ax25->modulus == AX25_EMODULUS); - break; - - case AX25_PIDINCL: - val = ax25->pidincl; - break; - - case AX25_IAMDIGI: - val = ax25->iamdigi; - break; - - case AX25_PACLEN: - val = ax25->paclen; - break; - - case SO_BINDTODEVICE: - ax25_dev = ax25->ax25_dev; - - if (ax25_dev != NULL && ax25_dev->dev != NULL) { - strscpy(devname, ax25_dev->dev->name, sizeof(devname)); - length = strlen(devname) + 1; - } else { - *devname = '\0'; - length = 1; - } - - valptr = devname; - break; - - default: - release_sock(sk); - return -ENOPROTOOPT; - } - release_sock(sk); - - if (put_user(length, optlen)) - return -EFAULT; - - return copy_to_user(optval, valptr, length) ? -EFAULT : 0; -} - -static int ax25_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - int res = 0; - - lock_sock(sk); - if (sk->sk_type == SOCK_SEQPACKET && sk->sk_state != TCP_LISTEN) { - sk->sk_max_ack_backlog = backlog; - sk->sk_state = TCP_LISTEN; - goto out; - } - res = -EOPNOTSUPP; - -out: - release_sock(sk); - - return res; -} - -/* - * XXX: when creating ax25_sock we should update the .obj_size setting - * below. - */ -static struct proto ax25_proto = { - .name = "AX25", - .owner = THIS_MODULE, - .obj_size = sizeof(struct ax25_sock), -}; - -static int ax25_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - ax25_cb *ax25; - - if (protocol < 0 || protocol > U8_MAX) - return -EINVAL; - - if (!net_eq(net, &init_net)) - return -EAFNOSUPPORT; - - switch (sock->type) { - case SOCK_DGRAM: - if (protocol == 0 || protocol == PF_AX25) - protocol = AX25_P_TEXT; - break; - - case SOCK_SEQPACKET: - switch (protocol) { - case 0: - case PF_AX25: /* For CLX */ - protocol = AX25_P_TEXT; - break; - case AX25_P_SEGMENT: -#ifdef CONFIG_INET - case AX25_P_ARP: - case AX25_P_IP: -#endif -#ifdef CONFIG_NETROM - case AX25_P_NETROM: -#endif -#ifdef CONFIG_ROSE - case AX25_P_ROSE: -#endif - return -ESOCKTNOSUPPORT; -#ifdef CONFIG_NETROM_MODULE - case AX25_P_NETROM: - if (ax25_protocol_is_registered(AX25_P_NETROM)) - return -ESOCKTNOSUPPORT; - break; -#endif -#ifdef CONFIG_ROSE_MODULE - case AX25_P_ROSE: - if (ax25_protocol_is_registered(AX25_P_ROSE)) - return -ESOCKTNOSUPPORT; - break; -#endif - default: - break; - } - break; - - case SOCK_RAW: - if (!capable(CAP_NET_RAW)) - return -EPERM; - break; - default: - return -ESOCKTNOSUPPORT; - } - - sk = sk_alloc(net, PF_AX25, GFP_ATOMIC, &ax25_proto, kern); - if (sk == NULL) - return -ENOMEM; - - ax25 = ax25_sk(sk)->cb = ax25_create_cb(); - if (!ax25) { - sk_free(sk); - return -ENOMEM; - } - - sock_init_data(sock, sk); - - sk->sk_destruct = ax25_free_sock; - sock->ops = &ax25_proto_ops; - sk->sk_protocol = protocol; - - ax25->sk = sk; - - return 0; -} - -struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev) -{ - struct sock *sk; - ax25_cb *ax25, *oax25; - - sk = sk_alloc(sock_net(osk), PF_AX25, GFP_ATOMIC, osk->sk_prot, 0); - if (sk == NULL) - return NULL; - - if ((ax25 = ax25_create_cb()) == NULL) { - sk_free(sk); - return NULL; - } - - switch (osk->sk_type) { - case SOCK_DGRAM: - break; - case SOCK_SEQPACKET: - break; - default: - sk_free(sk); - ax25_cb_put(ax25); - return NULL; - } - - sock_init_data(NULL, sk); - - sk->sk_type = osk->sk_type; - sk->sk_priority = READ_ONCE(osk->sk_priority); - sk->sk_protocol = osk->sk_protocol; - sk->sk_rcvbuf = osk->sk_rcvbuf; - sk->sk_sndbuf = osk->sk_sndbuf; - sk->sk_state = TCP_ESTABLISHED; - sock_copy_flags(sk, osk); - - oax25 = sk_to_ax25(osk); - - ax25->modulus = oax25->modulus; - ax25->backoff = oax25->backoff; - ax25->pidincl = oax25->pidincl; - ax25->iamdigi = oax25->iamdigi; - ax25->rtt = oax25->rtt; - ax25->t1 = oax25->t1; - ax25->t2 = oax25->t2; - ax25->t3 = oax25->t3; - ax25->n2 = oax25->n2; - ax25->idle = oax25->idle; - ax25->paclen = oax25->paclen; - ax25->window = oax25->window; - - ax25->ax25_dev = ax25_dev; - ax25->source_addr = oax25->source_addr; - - if (oax25->digipeat != NULL) { - ax25->digipeat = kmemdup(oax25->digipeat, sizeof(ax25_digi), - GFP_ATOMIC); - if (ax25->digipeat == NULL) { - sk_free(sk); - ax25_cb_put(ax25); - return NULL; - } - } - - ax25_sk(sk)->cb = ax25; - sk->sk_destruct = ax25_free_sock; - ax25->sk = sk; - - return sk; -} - -static int ax25_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - ax25_cb *ax25; - ax25_dev *ax25_dev; - - if (sk == NULL) - return 0; - - sock_hold(sk); - lock_sock(sk); - sock_orphan(sk); - ax25 = sk_to_ax25(sk); - ax25_dev = ax25->ax25_dev; - - if (sk->sk_type == SOCK_SEQPACKET) { - switch (ax25->state) { - case AX25_STATE_0: - if (!sock_flag(ax25->sk, SOCK_DEAD)) { - release_sock(sk); - ax25_disconnect(ax25, 0); - lock_sock(sk); - } - ax25_destroy_socket(ax25); - break; - - case AX25_STATE_1: - case AX25_STATE_2: - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - release_sock(sk); - ax25_disconnect(ax25, 0); - lock_sock(sk); - if (!sock_flag(ax25->sk, SOCK_DESTROY)) - ax25_destroy_socket(ax25); - break; - - case AX25_STATE_3: - case AX25_STATE_4: - ax25_clear_queues(ax25); - ax25->n2count = 0; - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_send_control(ax25, - AX25_DISC, - AX25_POLLON, - AX25_COMMAND); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); - break; -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); - break; -#endif - } - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25->state = AX25_STATE_2; - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DESTROY); - break; - - default: - break; - } - } else { - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - sk->sk_state_change(sk); - ax25_destroy_socket(ax25); - } - if (ax25_dev) { - if (!ax25_dev->device_up) { - timer_delete_sync(&ax25->timer); - timer_delete_sync(&ax25->t1timer); - timer_delete_sync(&ax25->t2timer); - timer_delete_sync(&ax25->t3timer); - timer_delete_sync(&ax25->idletimer); - } - netdev_put(ax25_dev->dev, &ax25->dev_tracker); - ax25_dev_put(ax25_dev); - } - - sock->sk = NULL; - release_sock(sk); - sock_put(sk); - - return 0; -} - -/* - * We support a funny extension here so you can (as root) give any callsign - * digipeated via a local address as source. This hack is obsolete now - * that we've implemented support for SO_BINDTODEVICE. It is however small - * and trivially backward compatible. - */ -static int ax25_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len) -{ - struct sock *sk = sock->sk; - struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr; - ax25_dev *ax25_dev = NULL; - ax25_uid_assoc *user; - ax25_address call; - ax25_cb *ax25; - int err = 0; - - if (addr_len != sizeof(struct sockaddr_ax25) && - addr_len != sizeof(struct full_sockaddr_ax25)) - /* support for old structure may go away some time - * ax25_bind(): uses old (6 digipeater) socket structure. - */ - if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || - (addr_len > sizeof(struct full_sockaddr_ax25))) - return -EINVAL; - - if (addr->fsa_ax25.sax25_family != AF_AX25) - return -EINVAL; - - user = ax25_findbyuid(current_euid()); - if (user) { - call = user->call; - ax25_uid_put(user); - } else { - if (ax25_uid_policy && !capable(CAP_NET_ADMIN)) - return -EACCES; - - call = addr->fsa_ax25.sax25_call; - } - - lock_sock(sk); - - ax25 = sk_to_ax25(sk); - if (!sock_flag(sk, SOCK_ZAPPED)) { - err = -EINVAL; - goto out; - } - - ax25->source_addr = call; - - /* - * User already set interface with SO_BINDTODEVICE - */ - if (ax25->ax25_dev != NULL) - goto done; - - if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) { - if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 && - (ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) { - err = -EADDRNOTAVAIL; - goto out; - } - } else { - if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) { - err = -EADDRNOTAVAIL; - goto out; - } - } - - if (ax25_dev) { - ax25_fillin_cb(ax25, ax25_dev); - netdev_hold(ax25_dev->dev, &ax25->dev_tracker, GFP_ATOMIC); - } - -done: - ax25_cb_add(ax25); - sock_reset_flag(sk, SOCK_ZAPPED); - -out: - release_sock(sk); - - return err; -} - -/* - * FIXME: nonblock behaviour looks like it may have a bug. - */ -static int __must_check ax25_connect(struct socket *sock, - struct sockaddr_unsized *uaddr, int addr_len, int flags) -{ - struct sock *sk = sock->sk; - ax25_cb *ax25 = sk_to_ax25(sk), *ax25t; - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; - ax25_digi *digi = NULL; - int ct = 0, err = 0; - - /* - * some sanity checks. code further down depends on this - */ - - if (addr_len == sizeof(struct sockaddr_ax25)) - /* support for this will go away in early 2.5.x - * ax25_connect(): uses obsolete socket structure - */ - ; - else if (addr_len != sizeof(struct full_sockaddr_ax25)) - /* support for old structure may go away some time - * ax25_connect(): uses old (6 digipeater) socket structure. - */ - if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || - (addr_len > sizeof(struct full_sockaddr_ax25))) - return -EINVAL; - - - if (fsa->fsa_ax25.sax25_family != AF_AX25) - return -EINVAL; - - lock_sock(sk); - - /* deal with restarts */ - if (sock->state == SS_CONNECTING) { - switch (sk->sk_state) { - case TCP_SYN_SENT: /* still trying */ - err = -EINPROGRESS; - goto out_release; - - case TCP_ESTABLISHED: /* connection established */ - sock->state = SS_CONNECTED; - goto out_release; - - case TCP_CLOSE: /* connection refused */ - sock->state = SS_UNCONNECTED; - err = -ECONNREFUSED; - goto out_release; - } - } - - if (sk->sk_state == TCP_ESTABLISHED && sk->sk_type == SOCK_SEQPACKET) { - err = -EISCONN; /* No reconnect on a seqpacket socket */ - goto out_release; - } - - sk->sk_state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - kfree(ax25->digipeat); - ax25->digipeat = NULL; - - /* - * Handle digi-peaters to be used. - */ - if (addr_len > sizeof(struct sockaddr_ax25) && - fsa->fsa_ax25.sax25_ndigis != 0) { - /* Valid number of digipeaters ? */ - if (fsa->fsa_ax25.sax25_ndigis < 1 || - fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS || - addr_len < sizeof(struct sockaddr_ax25) + - sizeof(ax25_address) * fsa->fsa_ax25.sax25_ndigis) { - err = -EINVAL; - goto out_release; - } - - if ((digi = kmalloc_obj(ax25_digi)) == NULL) { - err = -ENOBUFS; - goto out_release; - } - - digi->ndigi = fsa->fsa_ax25.sax25_ndigis; - digi->lastrepeat = -1; - - while (ct < fsa->fsa_ax25.sax25_ndigis) { - if ((fsa->fsa_digipeater[ct].ax25_call[6] & - AX25_HBIT) && ax25->iamdigi) { - digi->repeated[ct] = 1; - digi->lastrepeat = ct; - } else { - digi->repeated[ct] = 0; - } - digi->calls[ct] = fsa->fsa_digipeater[ct]; - ct++; - } - } - - /* Must bind first - autobinding does not work. */ - if (sock_flag(sk, SOCK_ZAPPED)) { - kfree(digi); - err = -EINVAL; - goto out_release; - } - - /* Check to see if the device has been filled in, error if it hasn't. */ - if (ax25->ax25_dev == NULL) { - kfree(digi); - err = -EHOSTUNREACH; - goto out_release; - } - - if (sk->sk_type == SOCK_SEQPACKET && - (ax25t=ax25_find_cb(&ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, - ax25->ax25_dev->dev))) { - kfree(digi); - err = -EADDRINUSE; /* Already such a connection */ - ax25_cb_put(ax25t); - goto out_release; - } - - ax25->dest_addr = fsa->fsa_ax25.sax25_call; - ax25->digipeat = digi; - - /* First the easy one */ - if (sk->sk_type != SOCK_SEQPACKET) { - sock->state = SS_CONNECTED; - sk->sk_state = TCP_ESTABLISHED; - goto out_release; - } - - /* Move to connecting socket, ax.25 lapb WAIT_UA.. */ - sock->state = SS_CONNECTING; - sk->sk_state = TCP_SYN_SENT; - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_establish_data_link(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - if (ax25->ax25_dev->dama.slave) - ax25_ds_establish_data_link(ax25); - else - ax25_std_establish_data_link(ax25); - break; -#endif - } - - ax25->state = AX25_STATE_1; - - ax25_start_heartbeat(ax25); - - /* Now the loop */ - if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) { - err = -EINPROGRESS; - goto out_release; - } - - if (sk->sk_state == TCP_SYN_SENT) { - DEFINE_WAIT(wait); - - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - if (sk->sk_state != TCP_SYN_SENT) - break; - if (!signal_pending(current)) { - release_sock(sk); - schedule(); - lock_sock(sk); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(sk_sleep(sk), &wait); - - if (err) - goto out_release; - } - - if (sk->sk_state != TCP_ESTABLISHED) { - /* Not in ABM, not in WAIT_UA -> failed */ - sock->state = SS_UNCONNECTED; - err = sock_error(sk); /* Always set at this point */ - goto out_release; - } - - sock->state = SS_CONNECTED; - - err = 0; -out_release: - release_sock(sk); - - return err; -} - -static int ax25_accept(struct socket *sock, struct socket *newsock, - struct proto_accept_arg *arg) -{ - struct sk_buff *skb; - struct sock *newsk; - ax25_dev *ax25_dev; - DEFINE_WAIT(wait); - struct sock *sk; - ax25_cb *ax25; - int err = 0; - - if (sock->state != SS_UNCONNECTED) - return -EINVAL; - - if ((sk = sock->sk) == NULL) - return -EINVAL; - - lock_sock(sk); - if (sk->sk_type != SOCK_SEQPACKET) { - err = -EOPNOTSUPP; - goto out; - } - - if (sk->sk_state != TCP_LISTEN) { - err = -EINVAL; - goto out; - } - - /* - * The read queue this time is holding sockets ready to use - * hooked into the SABM we saved - */ - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - skb = skb_dequeue(&sk->sk_receive_queue); - if (skb) - break; - - if (arg->flags & O_NONBLOCK) { - err = -EWOULDBLOCK; - break; - } - if (!signal_pending(current)) { - release_sock(sk); - schedule(); - lock_sock(sk); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(sk_sleep(sk), &wait); - - if (err) - goto out; - - newsk = skb->sk; - sock_graft(newsk, newsock); - - /* Now attach up the new socket */ - kfree_skb(skb); - sk_acceptq_removed(sk); - newsock->state = SS_CONNECTED; - ax25 = sk_to_ax25(newsk); - ax25_dev = ax25->ax25_dev; - netdev_hold(ax25_dev->dev, &ax25->dev_tracker, GFP_ATOMIC); - ax25_dev_hold(ax25_dev); - -out: - release_sock(sk); - - return err; -} - -static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, - int peer) -{ - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; - struct sock *sk = sock->sk; - unsigned char ndigi, i; - ax25_cb *ax25; - int err = 0; - - memset(fsa, 0, sizeof(*fsa)); - lock_sock(sk); - ax25 = sk_to_ax25(sk); - - if (peer != 0) { - if (sk->sk_state != TCP_ESTABLISHED) { - err = -ENOTCONN; - goto out; - } - - fsa->fsa_ax25.sax25_family = AF_AX25; - fsa->fsa_ax25.sax25_call = ax25->dest_addr; - - if (ax25->digipeat != NULL) { - ndigi = ax25->digipeat->ndigi; - fsa->fsa_ax25.sax25_ndigis = ndigi; - for (i = 0; i < ndigi; i++) - fsa->fsa_digipeater[i] = - ax25->digipeat->calls[i]; - } - } else { - fsa->fsa_ax25.sax25_family = AF_AX25; - fsa->fsa_ax25.sax25_call = ax25->source_addr; - fsa->fsa_ax25.sax25_ndigis = 1; - if (ax25->ax25_dev != NULL) { - memcpy(&fsa->fsa_digipeater[0], - ax25->ax25_dev->dev->dev_addr, AX25_ADDR_LEN); - } else { - fsa->fsa_digipeater[0] = null_ax25_address; - } - } - err = sizeof (struct full_sockaddr_ax25); - -out: - release_sock(sk); - - return err; -} - -static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) -{ - DECLARE_SOCKADDR(struct sockaddr_ax25 *, usax, msg->msg_name); - struct sock *sk = sock->sk; - struct sockaddr_ax25 sax; - struct sk_buff *skb; - ax25_digi dtmp, *dp; - ax25_cb *ax25; - size_t size; - int lv, err, addr_len = msg->msg_namelen; - - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_CMSG_COMPAT)) - return -EINVAL; - - lock_sock(sk); - ax25 = sk_to_ax25(sk); - - if (sock_flag(sk, SOCK_ZAPPED)) { - err = -EADDRNOTAVAIL; - goto out; - } - - if (sk->sk_shutdown & SEND_SHUTDOWN) { - send_sig(SIGPIPE, current, 0); - err = -EPIPE; - goto out; - } - - if (ax25->ax25_dev == NULL) { - err = -ENETUNREACH; - goto out; - } - - if (len > ax25->ax25_dev->dev->mtu) { - err = -EMSGSIZE; - goto out; - } - - if (usax != NULL) { - if (usax->sax25_family != AF_AX25) { - err = -EINVAL; - goto out; - } - - if (addr_len == sizeof(struct sockaddr_ax25)) - /* ax25_sendmsg(): uses obsolete socket structure */ - ; - else if (addr_len != sizeof(struct full_sockaddr_ax25)) - /* support for old structure may go away some time - * ax25_sendmsg(): uses old (6 digipeater) - * socket structure. - */ - if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || - (addr_len > sizeof(struct full_sockaddr_ax25))) { - err = -EINVAL; - goto out; - } - - - if (addr_len > sizeof(struct sockaddr_ax25) && usax->sax25_ndigis != 0) { - int ct = 0; - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)usax; - - /* Valid number of digipeaters ? */ - if (usax->sax25_ndigis < 1 || - usax->sax25_ndigis > AX25_MAX_DIGIS || - addr_len < sizeof(struct sockaddr_ax25) + - sizeof(ax25_address) * usax->sax25_ndigis) { - err = -EINVAL; - goto out; - } - - dtmp.ndigi = usax->sax25_ndigis; - - while (ct < usax->sax25_ndigis) { - dtmp.repeated[ct] = 0; - dtmp.calls[ct] = fsa->fsa_digipeater[ct]; - ct++; - } - - dtmp.lastrepeat = 0; - } - - sax = *usax; - if (sk->sk_type == SOCK_SEQPACKET && - ax25cmp(&ax25->dest_addr, &sax.sax25_call)) { - err = -EISCONN; - goto out; - } - if (usax->sax25_ndigis == 0) - dp = NULL; - else - dp = &dtmp; - } else { - /* - * FIXME: 1003.1g - if the socket is like this because - * it has become closed (not started closed) and is VC - * we ought to SIGPIPE, EPIPE - */ - if (sk->sk_state != TCP_ESTABLISHED) { - err = -ENOTCONN; - goto out; - } - sax.sax25_family = AF_AX25; - sax.sax25_call = ax25->dest_addr; - dp = ax25->digipeat; - } - - /* Build a packet */ - /* Assume the worst case */ - size = len + ax25->ax25_dev->dev->hard_header_len; - - skb = sock_alloc_send_skb(sk, size, msg->msg_flags&MSG_DONTWAIT, &err); - if (skb == NULL) - goto out; - - skb_reserve(skb, size - len); - - /* User data follows immediately after the AX.25 data */ - if (memcpy_from_msg(skb_put(skb, len), msg, len)) { - err = -EFAULT; - kfree_skb(skb); - goto out; - } - - skb_reset_network_header(skb); - - /* Add the PID if one is not supplied by the user in the skb */ - if (!ax25->pidincl) - *(u8 *)skb_push(skb, 1) = sk->sk_protocol; - - if (sk->sk_type == SOCK_SEQPACKET) { - /* Connected mode sockets go via the LAPB machine */ - if (sk->sk_state != TCP_ESTABLISHED) { - kfree_skb(skb); - err = -ENOTCONN; - goto out; - } - - /* Shove it onto the queue and kick */ - ax25_output(ax25, ax25->paclen, skb); - - err = len; - goto out; - } - - skb_push(skb, 1 + ax25_addr_size(dp)); - - /* Building AX.25 Header */ - - /* Build an AX.25 header */ - lv = ax25_addr_build(skb->data, &ax25->source_addr, &sax.sax25_call, - dp, AX25_COMMAND, AX25_MODULUS); - - skb_set_transport_header(skb, lv); - - *skb_transport_header(skb) = AX25_UI; - - /* Datagram frames go straight out of the door as UI */ - ax25_queue_xmit(skb, ax25->ax25_dev->dev); - - err = len; - -out: - release_sock(sk); - - return err; -} - -static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, - int flags) -{ - struct sock *sk = sock->sk; - struct sk_buff *skb, *last; - struct sk_buff_head *sk_queue; - int copied; - int err = 0; - int off = 0; - long timeo; - - lock_sock(sk); - /* - * This works for seqpacket too. The receiver has ordered the - * queue for us! We do one quick check first though - */ - if (sk->sk_type == SOCK_SEQPACKET && sk->sk_state != TCP_ESTABLISHED) { - err = -ENOTCONN; - goto out; - } - - /* We need support for non-blocking reads. */ - sk_queue = &sk->sk_receive_queue; - skb = __skb_try_recv_datagram(sk, sk_queue, flags, &off, &err, &last); - /* If no packet is available, release_sock(sk) and try again. */ - if (!skb) { - if (err != -EAGAIN) - goto out; - release_sock(sk); - timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); - while (timeo && !__skb_wait_for_more_packets(sk, sk_queue, &err, - &timeo, last)) { - skb = __skb_try_recv_datagram(sk, sk_queue, flags, &off, - &err, &last); - if (skb) - break; - - if (err != -EAGAIN) - goto done; - } - if (!skb) - goto done; - lock_sock(sk); - } - - if (!sk_to_ax25(sk)->pidincl) - skb_pull(skb, 1); /* Remove PID */ - - skb_reset_transport_header(skb); - copied = skb->len; - - if (copied > size) { - copied = size; - msg->msg_flags |= MSG_TRUNC; - } - - skb_copy_datagram_msg(skb, 0, msg, copied); - - if (msg->msg_name) { - ax25_digi digi; - ax25_address src; - const unsigned char *mac = skb_mac_header(skb); - DECLARE_SOCKADDR(struct sockaddr_ax25 *, sax, msg->msg_name); - - memset(sax, 0, sizeof(struct full_sockaddr_ax25)); - ax25_addr_parse(mac + 1, skb->data - mac - 1, &src, NULL, - &digi, NULL, NULL); - sax->sax25_family = AF_AX25; - /* We set this correctly, even though we may not let the - application know the digi calls further down (because it - did NOT ask to know them). This could get political... **/ - sax->sax25_ndigis = digi.ndigi; - sax->sax25_call = src; - - if (sax->sax25_ndigis != 0) { - int ct; - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)sax; - - for (ct = 0; ct < digi.ndigi; ct++) - fsa->fsa_digipeater[ct] = digi.calls[ct]; - } - msg->msg_namelen = sizeof(struct full_sockaddr_ax25); - } - - skb_free_datagram(sk, skb); - err = copied; - -out: - release_sock(sk); - -done: - return err; -} - -static int ax25_shutdown(struct socket *sk, int how) -{ - /* FIXME - generate DM and RNR states */ - return -EOPNOTSUPP; -} - -static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk = sock->sk; - void __user *argp = (void __user *)arg; - int res = 0; - - lock_sock(sk); - switch (cmd) { - case TIOCOUTQ: { - long amount; - - amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - if (amount < 0) - amount = 0; - res = put_user(amount, (int __user *)argp); - break; - } - - case TIOCINQ: { - struct sk_buff *skb; - long amount = 0L; - /* These two are safe on a single CPU system as only user tasks fiddle here */ - if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) - amount = skb->len; - res = put_user(amount, (int __user *) argp); - break; - } - - case SIOCAX25ADDUID: /* Add a uid to the uid/call map table */ - case SIOCAX25DELUID: /* Delete a uid from the uid/call map table */ - case SIOCAX25GETUID: { - struct sockaddr_ax25 sax25; - if (copy_from_user(&sax25, argp, sizeof(sax25))) { - res = -EFAULT; - break; - } - res = ax25_uid_ioctl(cmd, &sax25); - break; - } - - case SIOCAX25NOUID: { /* Set the default policy (default/bar) */ - long amount; - if (!capable(CAP_NET_ADMIN)) { - res = -EPERM; - break; - } - if (get_user(amount, (long __user *)argp)) { - res = -EFAULT; - break; - } - if (amount < 0 || amount > AX25_NOUID_BLOCK) { - res = -EINVAL; - break; - } - ax25_uid_policy = amount; - res = 0; - break; - } - - case SIOCADDRT: - case SIOCDELRT: - case SIOCAX25OPTRT: - if (!capable(CAP_NET_ADMIN)) { - res = -EPERM; - break; - } - res = ax25_rt_ioctl(cmd, argp); - break; - - case SIOCAX25CTLCON: - if (!capable(CAP_NET_ADMIN)) { - res = -EPERM; - break; - } - res = ax25_ctl_ioctl(cmd, argp); - break; - - case SIOCAX25GETINFO: - case SIOCAX25GETINFOOLD: { - ax25_cb *ax25 = sk_to_ax25(sk); - struct ax25_info_struct ax25_info; - - ax25_info.t1 = ax25->t1 / HZ; - ax25_info.t2 = ax25->t2 / HZ; - ax25_info.t3 = ax25->t3 / HZ; - ax25_info.idle = ax25->idle / (60 * HZ); - ax25_info.n2 = ax25->n2; - ax25_info.t1timer = ax25_display_timer(&ax25->t1timer) / HZ; - ax25_info.t2timer = ax25_display_timer(&ax25->t2timer) / HZ; - ax25_info.t3timer = ax25_display_timer(&ax25->t3timer) / HZ; - ax25_info.idletimer = ax25_display_timer(&ax25->idletimer) / (60 * HZ); - ax25_info.n2count = ax25->n2count; - ax25_info.state = ax25->state; - ax25_info.rcv_q = sk_rmem_alloc_get(sk); - ax25_info.snd_q = sk_wmem_alloc_get(sk); - ax25_info.vs = ax25->vs; - ax25_info.vr = ax25->vr; - ax25_info.va = ax25->va; - ax25_info.vs_max = ax25->vs; /* reserved */ - ax25_info.paclen = ax25->paclen; - ax25_info.window = ax25->window; - - /* old structure? */ - if (cmd == SIOCAX25GETINFOOLD) { - static int warned = 0; - if (!warned) { - printk(KERN_INFO "%s uses old SIOCAX25GETINFO\n", - current->comm); - warned=1; - } - - if (copy_to_user(argp, &ax25_info, sizeof(struct ax25_info_struct_deprecated))) { - res = -EFAULT; - break; - } - } else { - if (copy_to_user(argp, &ax25_info, sizeof(struct ax25_info_struct))) { - res = -EINVAL; - break; - } - } - res = 0; - break; - } - - case SIOCAX25ADDFWD: - case SIOCAX25DELFWD: { - struct ax25_fwd_struct ax25_fwd; - if (!capable(CAP_NET_ADMIN)) { - res = -EPERM; - break; - } - if (copy_from_user(&ax25_fwd, argp, sizeof(ax25_fwd))) { - res = -EFAULT; - break; - } - res = ax25_fwd_ioctl(cmd, &ax25_fwd); - break; - } - - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMETRIC: - case SIOCSIFMETRIC: - res = -EINVAL; - break; - - default: - res = -ENOIOCTLCMD; - break; - } - release_sock(sk); - - return res; -} - -#ifdef CONFIG_PROC_FS - -static void *ax25_info_start(struct seq_file *seq, loff_t *pos) - __acquires(ax25_list_lock) -{ - spin_lock_bh(&ax25_list_lock); - return seq_hlist_start(&ax25_list, *pos); -} - -static void *ax25_info_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &ax25_list, pos); -} - -static void ax25_info_stop(struct seq_file *seq, void *v) - __releases(ax25_list_lock) -{ - spin_unlock_bh(&ax25_list_lock); -} - -static int ax25_info_show(struct seq_file *seq, void *v) -{ - ax25_cb *ax25 = hlist_entry(v, struct ax25_cb, ax25_node); - char buf[11]; - int k; - - - /* - * New format: - * magic dev src_addr dest_addr,digi1,digi2,.. st vs vr va t1 t1 t2 t2 t3 t3 idle idle n2 n2 rtt window paclen Snd-Q Rcv-Q inode - */ - - seq_printf(seq, "%p %s %s%s ", - ax25, - ax25->ax25_dev == NULL? "???" : ax25->ax25_dev->dev->name, - ax2asc(buf, &ax25->source_addr), - ax25->iamdigi? "*":""); - seq_printf(seq, "%s", ax2asc(buf, &ax25->dest_addr)); - - for (k=0; (ax25->digipeat != NULL) && (k < ax25->digipeat->ndigi); k++) { - seq_printf(seq, ",%s%s", - ax2asc(buf, &ax25->digipeat->calls[k]), - ax25->digipeat->repeated[k]? "*":""); - } - - seq_printf(seq, " %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %d %d", - ax25->state, - ax25->vs, ax25->vr, ax25->va, - ax25_display_timer(&ax25->t1timer) / HZ, ax25->t1 / HZ, - ax25_display_timer(&ax25->t2timer) / HZ, ax25->t2 / HZ, - ax25_display_timer(&ax25->t3timer) / HZ, ax25->t3 / HZ, - ax25_display_timer(&ax25->idletimer) / (60 * HZ), - ax25->idle / (60 * HZ), - ax25->n2count, ax25->n2, - ax25->rtt / HZ, - ax25->window, - ax25->paclen); - - if (ax25->sk != NULL) { - seq_printf(seq, " %d %d %llu\n", - sk_wmem_alloc_get(ax25->sk), - sk_rmem_alloc_get(ax25->sk), - sock_i_ino(ax25->sk)); - } else { - seq_puts(seq, " * * *\n"); - } - return 0; -} - -static const struct seq_operations ax25_info_seqops = { - .start = ax25_info_start, - .next = ax25_info_next, - .stop = ax25_info_stop, - .show = ax25_info_show, -}; -#endif - -static const struct net_proto_family ax25_family_ops = { - .family = PF_AX25, - .create = ax25_create, - .owner = THIS_MODULE, -}; - -static const struct proto_ops ax25_proto_ops = { - .family = PF_AX25, - .owner = THIS_MODULE, - .release = ax25_release, - .bind = ax25_bind, - .connect = ax25_connect, - .socketpair = sock_no_socketpair, - .accept = ax25_accept, - .getname = ax25_getname, - .poll = datagram_poll, - .ioctl = ax25_ioctl, - .gettstamp = sock_gettstamp, - .listen = ax25_listen, - .shutdown = ax25_shutdown, - .setsockopt = ax25_setsockopt, - .getsockopt = ax25_getsockopt, - .sendmsg = ax25_sendmsg, - .recvmsg = ax25_recvmsg, - .mmap = sock_no_mmap, -}; - -/* - * Called by socket.c on kernel start up - */ -static struct packet_type ax25_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_AX25), - .func = ax25_kiss_rcv, -}; - -static struct notifier_block ax25_dev_notifier = { - .notifier_call = ax25_device_event, -}; - -static int __init ax25_init(void) -{ - int rc = proto_register(&ax25_proto, 0); - - if (rc != 0) - goto out; - - sock_register(&ax25_family_ops); - dev_add_pack(&ax25_packet_type); - register_netdevice_notifier(&ax25_dev_notifier); - - proc_create_seq("ax25_route", 0444, init_net.proc_net, &ax25_rt_seqops); - proc_create_seq("ax25", 0444, init_net.proc_net, &ax25_info_seqops); - proc_create_seq("ax25_calls", 0444, init_net.proc_net, - &ax25_uid_seqops); -out: - return rc; -} -module_init(ax25_init); - - -MODULE_AUTHOR("Jonathan Naylor G4KLX "); -MODULE_DESCRIPTION("The amateur radio AX.25 link layer protocol"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(PF_AX25); - -static void __exit ax25_exit(void) -{ - remove_proc_entry("ax25_route", init_net.proc_net); - remove_proc_entry("ax25", init_net.proc_net); - remove_proc_entry("ax25_calls", init_net.proc_net); - - unregister_netdevice_notifier(&ax25_dev_notifier); - - dev_remove_pack(&ax25_packet_type); - - sock_unregister(PF_AX25); - proto_unregister(&ax25_proto); - - ax25_rt_free(); - ax25_uid_free(); - ax25_dev_free(); -} -module_exit(ax25_exit); diff --git a/net/ax25/ax25_addr.c b/net/ax25/ax25_addr.c deleted file mode 100644 index f68865a4d0ab..000000000000 --- a/net/ax25/ax25_addr.c +++ /dev/null @@ -1,303 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * The default broadcast address of an interface is QST-0; the default address - * is LINUX-1. The null address is defined as a callsign of all spaces with - * an SSID of zero. - */ - -const ax25_address ax25_bcast = - {{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, 0 << 1}}; -const ax25_address ax25_defaddr = - {{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, 1 << 1}}; -const ax25_address null_ax25_address = - {{' ' << 1, ' ' << 1, ' ' << 1, ' ' << 1, ' ' << 1, ' ' << 1, 0 << 1}}; - -EXPORT_SYMBOL_GPL(ax25_bcast); -EXPORT_SYMBOL_GPL(ax25_defaddr); -EXPORT_SYMBOL(null_ax25_address); - -/* - * ax25 -> ascii conversion - */ -char *ax2asc(char *buf, const ax25_address *a) -{ - char c, *s; - int n; - - for (n = 0, s = buf; n < 6; n++) { - c = (a->ax25_call[n] >> 1) & 0x7F; - - if (c != ' ') *s++ = c; - } - - *s++ = '-'; - - if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) { - *s++ = '1'; - n -= 10; - } - - *s++ = n + '0'; - *s++ = '\0'; - - if (*buf == '\0' || *buf == '-') - return "*"; - - return buf; - -} - -EXPORT_SYMBOL(ax2asc); - -/* - * ascii -> ax25 conversion - */ -void asc2ax(ax25_address *addr, const char *callsign) -{ - const char *s; - int n; - - for (s = callsign, n = 0; n < 6; n++) { - if (*s != '\0' && *s != '-') - addr->ax25_call[n] = *s++; - else - addr->ax25_call[n] = ' '; - addr->ax25_call[n] <<= 1; - addr->ax25_call[n] &= 0xFE; - } - - if (*s++ == '\0') { - addr->ax25_call[6] = 0x00; - return; - } - - addr->ax25_call[6] = *s++ - '0'; - - if (*s != '\0') { - addr->ax25_call[6] *= 10; - addr->ax25_call[6] += *s++ - '0'; - } - - addr->ax25_call[6] <<= 1; - addr->ax25_call[6] &= 0x1E; -} - -EXPORT_SYMBOL(asc2ax); - -/* - * Compare two ax.25 addresses - */ -int ax25cmp(const ax25_address *a, const ax25_address *b) -{ - int ct = 0; - - while (ct < 6) { - if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */ - return 1; - ct++; - } - - if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */ - return 0; - - return 2; /* Partial match */ -} - -EXPORT_SYMBOL(ax25cmp); - -/* - * Compare two AX.25 digipeater paths. - */ -int ax25digicmp(const ax25_digi *digi1, const ax25_digi *digi2) -{ - int i; - - if (digi1->ndigi != digi2->ndigi) - return 1; - - if (digi1->lastrepeat != digi2->lastrepeat) - return 1; - - for (i = 0; i < digi1->ndigi; i++) - if (ax25cmp(&digi1->calls[i], &digi2->calls[i]) != 0) - return 1; - - return 0; -} - -/* - * Given an AX.25 address pull of to, from, digi list, command/response and the start of data - * - */ -const unsigned char *ax25_addr_parse(const unsigned char *buf, int len, - ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, - int *dama) -{ - int d = 0; - - if (len < 14) return NULL; - - if (flags != NULL) { - *flags = 0; - - if (buf[6] & AX25_CBIT) - *flags = AX25_COMMAND; - if (buf[13] & AX25_CBIT) - *flags = AX25_RESPONSE; - } - - if (dama != NULL) - *dama = ~buf[13] & AX25_DAMA_FLAG; - - /* Copy to, from */ - if (dest != NULL) - memcpy(dest, buf + 0, AX25_ADDR_LEN); - if (src != NULL) - memcpy(src, buf + 7, AX25_ADDR_LEN); - - buf += 2 * AX25_ADDR_LEN; - len -= 2 * AX25_ADDR_LEN; - - digi->lastrepeat = -1; - digi->ndigi = 0; - - while (!(buf[-1] & AX25_EBIT)) { - if (d >= AX25_MAX_DIGIS) - return NULL; - if (len < AX25_ADDR_LEN) - return NULL; - - memcpy(&digi->calls[d], buf, AX25_ADDR_LEN); - digi->ndigi = d + 1; - - if (buf[6] & AX25_HBIT) { - digi->repeated[d] = 1; - digi->lastrepeat = d; - } else { - digi->repeated[d] = 0; - } - - buf += AX25_ADDR_LEN; - len -= AX25_ADDR_LEN; - d++; - } - - return buf; -} - -/* - * Assemble an AX.25 header from the bits - */ -int ax25_addr_build(unsigned char *buf, const ax25_address *src, - const ax25_address *dest, const ax25_digi *d, int flag, int modulus) -{ - int len = 0; - int ct = 0; - - memcpy(buf, dest, AX25_ADDR_LEN); - buf[6] &= ~(AX25_EBIT | AX25_CBIT); - buf[6] |= AX25_SSSID_SPARE; - - if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT; - - buf += AX25_ADDR_LEN; - len += AX25_ADDR_LEN; - - memcpy(buf, src, AX25_ADDR_LEN); - buf[6] &= ~(AX25_EBIT | AX25_CBIT); - buf[6] &= ~AX25_SSSID_SPARE; - - if (modulus == AX25_MODULUS) - buf[6] |= AX25_SSSID_SPARE; - else - buf[6] |= AX25_ESSID_SPARE; - - if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT; - - /* - * Fast path the normal digiless path - */ - if (d == NULL || d->ndigi == 0) { - buf[6] |= AX25_EBIT; - return 2 * AX25_ADDR_LEN; - } - - buf += AX25_ADDR_LEN; - len += AX25_ADDR_LEN; - - while (ct < d->ndigi) { - memcpy(buf, &d->calls[ct], AX25_ADDR_LEN); - - if (d->repeated[ct]) - buf[6] |= AX25_HBIT; - else - buf[6] &= ~AX25_HBIT; - - buf[6] &= ~AX25_EBIT; - buf[6] |= AX25_SSSID_SPARE; - - buf += AX25_ADDR_LEN; - len += AX25_ADDR_LEN; - ct++; - } - - buf[-1] |= AX25_EBIT; - - return len; -} - -int ax25_addr_size(const ax25_digi *dp) -{ - if (dp == NULL) - return 2 * AX25_ADDR_LEN; - - return AX25_ADDR_LEN * (2 + dp->ndigi); -} - -/* - * Reverse Digipeat List. May not pass both parameters as same struct - */ -void ax25_digi_invert(const ax25_digi *in, ax25_digi *out) -{ - int ct; - - out->ndigi = in->ndigi; - out->lastrepeat = in->ndigi - in->lastrepeat - 2; - - /* Invert the digipeaters */ - for (ct = 0; ct < in->ndigi; ct++) { - out->calls[ct] = in->calls[in->ndigi - ct - 1]; - - if (ct <= out->lastrepeat) { - out->calls[ct].ax25_call[6] |= AX25_HBIT; - out->repeated[ct] = 1; - } else { - out->calls[ct].ax25_call[6] &= ~AX25_HBIT; - out->repeated[ct] = 0; - } - } -} diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c deleted file mode 100644 index 3c0544fc4ad5..000000000000 --- a/net/ax25/ax25_dev.c +++ /dev/null @@ -1,200 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static LIST_HEAD(ax25_dev_list); -DEFINE_SPINLOCK(ax25_dev_lock); - -ax25_dev *ax25_addr_ax25dev(ax25_address *addr) -{ - ax25_dev *ax25_dev, *res = NULL; - - spin_lock_bh(&ax25_dev_lock); - list_for_each_entry(ax25_dev, &ax25_dev_list, list) - if (ax25cmp(addr, (const ax25_address *)ax25_dev->dev->dev_addr) == 0) { - res = ax25_dev; - ax25_dev_hold(ax25_dev); - break; - } - spin_unlock_bh(&ax25_dev_lock); - - return res; -} - -/* - * This is called when an interface is brought up. These are - * reasonable defaults. - */ -void ax25_dev_device_up(struct net_device *dev) -{ - ax25_dev *ax25_dev; - - ax25_dev = kzalloc_obj(*ax25_dev); - if (!ax25_dev) { - printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n"); - return; - } - - refcount_set(&ax25_dev->refcount, 1); - ax25_dev->dev = dev; - netdev_hold(dev, &ax25_dev->dev_tracker, GFP_KERNEL); - ax25_dev->forward = NULL; - ax25_dev->device_up = true; - - ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE; - ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE; - ax25_dev->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF; - ax25_dev->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE; - ax25_dev->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW; - ax25_dev->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW; - ax25_dev->values[AX25_VALUES_T1] = AX25_DEF_T1; - ax25_dev->values[AX25_VALUES_T2] = AX25_DEF_T2; - ax25_dev->values[AX25_VALUES_T3] = AX25_DEF_T3; - ax25_dev->values[AX25_VALUES_IDLE] = AX25_DEF_IDLE; - ax25_dev->values[AX25_VALUES_N2] = AX25_DEF_N2; - ax25_dev->values[AX25_VALUES_PACLEN] = AX25_DEF_PACLEN; - ax25_dev->values[AX25_VALUES_PROTOCOL] = AX25_DEF_PROTOCOL; - -#ifdef CONFIG_AX25_DAMA_SLAVE - ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT; - - ax25_ds_setup_timer(ax25_dev); -#endif - - spin_lock_bh(&ax25_dev_lock); - list_add(&ax25_dev->list, &ax25_dev_list); - rcu_assign_pointer(dev->ax25_ptr, ax25_dev); - spin_unlock_bh(&ax25_dev_lock); - - ax25_register_dev_sysctl(ax25_dev); -} - -void ax25_dev_device_down(struct net_device *dev) -{ - ax25_dev *s, *ax25_dev; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return; - - ax25_unregister_dev_sysctl(ax25_dev); - - spin_lock_bh(&ax25_dev_lock); - -#ifdef CONFIG_AX25_DAMA_SLAVE - timer_shutdown_sync(&ax25_dev->dama.slave_timer); -#endif - - /* - * Remove any packet forwarding that points to this device. - */ - list_for_each_entry(s, &ax25_dev_list, list) - if (s->forward == dev) - s->forward = NULL; - - list_for_each_entry(s, &ax25_dev_list, list) { - if (s == ax25_dev) { - list_del(&s->list); - break; - } - } - - RCU_INIT_POINTER(dev->ax25_ptr, NULL); - spin_unlock_bh(&ax25_dev_lock); - netdev_put(dev, &ax25_dev->dev_tracker); - ax25_dev_put(ax25_dev); -} - -int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd) -{ - ax25_dev *ax25_dev, *fwd_dev; - - if ((ax25_dev = ax25_addr_ax25dev(&fwd->port_from)) == NULL) - return -EINVAL; - - switch (cmd) { - case SIOCAX25ADDFWD: - fwd_dev = ax25_addr_ax25dev(&fwd->port_to); - if (!fwd_dev) { - ax25_dev_put(ax25_dev); - return -EINVAL; - } - if (ax25_dev->forward) { - ax25_dev_put(fwd_dev); - ax25_dev_put(ax25_dev); - return -EINVAL; - } - ax25_dev->forward = fwd_dev->dev; - ax25_dev_put(fwd_dev); - ax25_dev_put(ax25_dev); - break; - - case SIOCAX25DELFWD: - if (!ax25_dev->forward) { - ax25_dev_put(ax25_dev); - return -EINVAL; - } - ax25_dev->forward = NULL; - ax25_dev_put(ax25_dev); - break; - - default: - ax25_dev_put(ax25_dev); - return -EINVAL; - } - - return 0; -} - -struct net_device *ax25_fwd_dev(struct net_device *dev) -{ - ax25_dev *ax25_dev; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return dev; - - if (ax25_dev->forward == NULL) - return dev; - - return ax25_dev->forward; -} - -/* - * Free all memory associated with device structures. - */ -void __exit ax25_dev_free(void) -{ - ax25_dev *s, *n; - - spin_lock_bh(&ax25_dev_lock); - list_for_each_entry_safe(s, n, &ax25_dev_list, list) { - netdev_put(s->dev, &s->dev_tracker); - list_del(&s->list); - ax25_dev_put(s); - } - spin_unlock_bh(&ax25_dev_lock); -} diff --git a/net/ax25/ax25_ds_in.c b/net/ax25/ax25_ds_in.c deleted file mode 100644 index c62f8fb06189..000000000000 --- a/net/ax25/ax25_ds_in.c +++ /dev/null @@ -1,298 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * State machine for state 1, Awaiting Connection State. - * The handling of the timer(s) is in file ax25_ds_timer.c. - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_SABME: - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); - break; - - case AX25_UA: - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25->state = AX25_STATE_3; - ax25->n2count = 0; - if (ax25->sk != NULL) { - bh_lock_sock(ax25->sk); - ax25->sk->sk_state = TCP_ESTABLISHED; - /* - * For WAIT_SABM connections we will produce an accept - * ready socket here - */ - if (!sock_flag(ax25->sk, SOCK_DEAD)) - ax25->sk->sk_state_change(ax25->sk); - bh_unlock_sock(ax25->sk); - } - ax25_dama_on(ax25); - - /* according to DK4EG's spec we are required to - * send a RR RESPONSE FINAL NR=0. - */ - - ax25_std_enquiry_response(ax25); - break; - - case AX25_DM: - if (pf) - ax25_disconnect(ax25, ECONNREFUSED); - break; - - default: - if (pf) - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - break; - } - - return 0; -} - -/* - * State machine for state 2, Awaiting Release State. - * The handling of the timer(s) is in file ax25_ds_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_dama_off(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_dama_off(ax25); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - case AX25_UA: - if (pf) { - ax25_dama_off(ax25); - ax25_disconnect(ax25, 0); - } - break; - - case AX25_I: - case AX25_REJ: - case AX25_RNR: - case AX25_RR: - if (pf) { - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_dama_off(ax25); - } - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 3, Connected State. - * The handling of the timer(s) is in file ax25_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) -{ - int queued = 0; - - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - if (frametype == AX25_SABM) { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } else { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - } - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->condition = 0x00; - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25_requeue_frames(ax25); - ax25_dama_on(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_dama_off(ax25); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - ax25_dama_off(ax25); - ax25_disconnect(ax25, ECONNRESET); - break; - - case AX25_RR: - case AX25_RNR: - if (frametype == AX25_RR) - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - else - ax25->condition |= AX25_COND_PEER_RX_BUSY; - - if (ax25_validate_nr(ax25, nr)) { - if (ax25_check_iframes_acked(ax25, nr)) - ax25->n2count=0; - if (type == AX25_COMMAND && pf) - ax25_ds_enquiry_response(ax25); - } else { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - - if (ax25_validate_nr(ax25, nr)) { - if (ax25->va != nr) - ax25->n2count=0; - - ax25_frames_acked(ax25, nr); - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_requeue_frames(ax25); - - if (type == AX25_COMMAND && pf) - ax25_ds_enquiry_response(ax25); - } else { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_I: - if (!ax25_validate_nr(ax25, nr)) { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - break; - } - if (ax25->condition & AX25_COND_PEER_RX_BUSY) { - ax25_frames_acked(ax25, nr); - ax25->n2count = 0; - } else { - if (ax25_check_iframes_acked(ax25, nr)) - ax25->n2count = 0; - } - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { - if (pf) ax25_ds_enquiry_response(ax25); - break; - } - if (ns == ax25->vr) { - ax25->vr = (ax25->vr + 1) % ax25->modulus; - queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25->vr = ns; /* ax25->vr - 1 */ - ax25->condition &= ~AX25_COND_REJECT; - if (pf) { - ax25_ds_enquiry_response(ax25); - } else { - if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->condition |= AX25_COND_ACK_PENDING; - ax25_start_t2timer(ax25); - } - } - } else { - if (ax25->condition & AX25_COND_REJECT) { - if (pf) ax25_ds_enquiry_response(ax25); - } else { - ax25->condition |= AX25_COND_REJECT; - ax25_ds_enquiry_response(ax25); - ax25->condition &= ~AX25_COND_ACK_PENDING; - } - } - break; - - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_ds_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - - default: - break; - } - - return queued; -} - -/* - * Higher level upcall for a LAPB frame - */ -int ax25_ds_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) -{ - int queued = 0, frametype, ns, nr, pf; - - frametype = ax25_decode(ax25, skb, &ns, &nr, &pf); - - switch (ax25->state) { - case AX25_STATE_1: - queued = ax25_ds_state1_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_2: - queued = ax25_ds_state2_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_3: - queued = ax25_ds_state3_machine(ax25, skb, frametype, ns, nr, pf, type); - break; - } - - return queued; -} diff --git a/net/ax25/ax25_ds_subr.c b/net/ax25/ax25_ds_subr.c deleted file mode 100644 index f00e27df3c76..000000000000 --- a/net/ax25/ax25_ds_subr.c +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void ax25_ds_nr_error_recovery(ax25_cb *ax25) -{ - ax25_ds_establish_data_link(ax25); -} - -/* - * dl1bke 960114: transmit I frames on DAMA poll - */ -void ax25_ds_enquiry_response(ax25_cb *ax25) -{ - ax25_cb *ax25o; - - /* Please note that neither DK4EG's nor DG2FEF's - * DAMA spec mention the following behaviour as seen - * with TheFirmware: - * - * DB0ACH->DL1BKE [DAMA] - * DL1BKE->DB0ACH - * DL1BKE-7->DB0PRA-6 DB0ACH - * DL1BKE->DB0ACH - * - * The Flexnet DAMA Master implementation apparently - * insists on the "proper" AX.25 behaviour: - * - * DB0ACH->DL1BKE [DAMA] - * DL1BKE->DB0ACH - * DL1BKE->DB0ACH - * DL1BKE-7->DB0PRA-6 DB0ACH - * - * Flexnet refuses to send us *any* I frame if we send - * a REJ in case AX25_COND_REJECT is set. It is superfluous in - * this mode anyway (a RR or RNR invokes the retransmission). - * Is this a Flexnet bug? - */ - - ax25_std_enquiry_response(ax25); - - if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) { - ax25_requeue_frames(ax25); - ax25_kick(ax25); - } - - if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL) - ax25_ds_t1_timeout(ax25); - else - ax25->n2count = 0; - - ax25_start_t3timer(ax25); - ax25_ds_set_timer(ax25->ax25_dev); - - spin_lock(&ax25_list_lock); - ax25_for_each(ax25o, &ax25_list) { - if (ax25o == ax25) - continue; - - if (ax25o->ax25_dev != ax25->ax25_dev) - continue; - - if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) { - ax25_ds_t1_timeout(ax25o); - continue; - } - - if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && ax25o->state == AX25_STATE_3) { - ax25_requeue_frames(ax25o); - ax25_kick(ax25o); - } - - if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL) - ax25_ds_t1_timeout(ax25o); - - /* do not start T3 for listening sockets (tnx DD8NE) */ - - if (ax25o->state != AX25_STATE_0) - ax25_start_t3timer(ax25o); - } - spin_unlock(&ax25_list_lock); -} - -void ax25_ds_establish_data_link(ax25_cb *ax25) -{ - ax25->condition &= AX25_COND_DAMA_MODE; - ax25->n2count = 0; - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t3timer(ax25); -} - -/* - * :::FIXME::: - * This is a kludge. Not all drivers recognize kiss commands. - * We need a driver level request to switch duplex mode, that does - * either SCC changing, PI config or KISS as required. Currently - * this request isn't reliable. - */ -static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char param) -{ - struct sk_buff *skb; - unsigned char *p; - - if (ax25_dev->dev == NULL) - return; - - if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL) - return; - - skb_reset_network_header(skb); - p = skb_put(skb, 2); - - *p++ = cmd; - *p++ = param; - - skb->protocol = ax25_type_trans(skb, ax25_dev->dev); - - dev_queue_xmit(skb); -} - -/* - * A nasty problem arises if we count the number of DAMA connections - * wrong, especially when connections on the device already existed - * and our network node (or the sysop) decides to turn on DAMA Master - * mode. We thus flag the 'real' slave connections with - * ax25->dama_slave=1 and look on every disconnect if still slave - * connections exist. - */ -static int ax25_check_dama_slave(ax25_dev *ax25_dev) -{ - ax25_cb *ax25; - int res = 0; - - spin_lock(&ax25_list_lock); - ax25_for_each(ax25, &ax25_list) - if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1) { - res = 1; - break; - } - spin_unlock(&ax25_list_lock); - - return res; -} - -static void ax25_dev_dama_on(ax25_dev *ax25_dev) -{ - if (ax25_dev == NULL) - return; - - if (ax25_dev->dama.slave == 0) - ax25_kiss_cmd(ax25_dev, 5, 1); - - ax25_dev->dama.slave = 1; - ax25_ds_set_timer(ax25_dev); -} - -void ax25_dev_dama_off(ax25_dev *ax25_dev) -{ - if (ax25_dev == NULL) - return; - - if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev)) { - ax25_kiss_cmd(ax25_dev, 5, 0); - ax25_dev->dama.slave = 0; - ax25_ds_del_timer(ax25_dev); - } -} - -void ax25_dama_on(ax25_cb *ax25) -{ - ax25_dev_dama_on(ax25->ax25_dev); - ax25->condition |= AX25_COND_DAMA_MODE; -} - -void ax25_dama_off(ax25_cb *ax25) -{ - ax25->condition &= ~AX25_COND_DAMA_MODE; - ax25_dev_dama_off(ax25->ax25_dev); -} diff --git a/net/ax25/ax25_ds_timer.c b/net/ax25/ax25_ds_timer.c deleted file mode 100644 index 0c9e7775aa54..000000000000 --- a/net/ax25/ax25_ds_timer.c +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void ax25_ds_timeout(struct timer_list *); - -/* - * Add DAMA slave timeout timer to timer list. - * Unlike the connection based timers the timeout function gets - * triggered every second. Please note that NET_AX25_DAMA_SLAVE_TIMEOUT - * (aka /proc/sys/net/ax25/{dev}/dama_slave_timeout) is still in - * 1/10th of a second. - */ - -void ax25_ds_setup_timer(ax25_dev *ax25_dev) -{ - timer_setup(&ax25_dev->dama.slave_timer, ax25_ds_timeout, 0); -} - -void ax25_ds_del_timer(ax25_dev *ax25_dev) -{ - if (ax25_dev) - timer_delete(&ax25_dev->dama.slave_timer); -} - -void ax25_ds_set_timer(ax25_dev *ax25_dev) -{ - if (ax25_dev == NULL) /* paranoia */ - return; - - ax25_dev->dama.slave_timeout = - msecs_to_jiffies(ax25_dev->values[AX25_VALUES_DS_TIMEOUT]) / 10; - mod_timer(&ax25_dev->dama.slave_timer, jiffies + HZ); -} - -/* - * DAMA Slave Timeout - * Silently discard all (slave) connections in case our master forgot us... - */ - -static void ax25_ds_timeout(struct timer_list *t) -{ - ax25_dev *ax25_dev = timer_container_of(ax25_dev, t, dama.slave_timer); - ax25_cb *ax25; - - if (ax25_dev == NULL || !ax25_dev->dama.slave) - return; /* Yikes! */ - - if (!ax25_dev->dama.slave_timeout || --ax25_dev->dama.slave_timeout) { - ax25_ds_set_timer(ax25_dev); - return; - } - - spin_lock(&ax25_list_lock); - ax25_for_each(ax25, &ax25_list) { - if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE)) - continue; - - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_disconnect(ax25, ETIMEDOUT); - } - spin_unlock(&ax25_list_lock); - - ax25_dev_dama_off(ax25_dev); -} - -void ax25_ds_heartbeat_expiry(ax25_cb *ax25) -{ - struct sock *sk=ax25->sk; - - if (sk) - bh_lock_sock(sk); - - switch (ax25->state) { - - case AX25_STATE_0: - case AX25_STATE_2: - /* Magic here: If we listen() and a new link dies before it - is accepted() it isn't 'dead' so doesn't get removed. */ - if (!sk || sock_flag(sk, SOCK_DESTROY) || - (sk->sk_state == TCP_LISTEN && - sock_flag(sk, SOCK_DEAD))) { - if (sk) { - sock_hold(sk); - ax25_destroy_socket(ax25); - bh_unlock_sock(sk); - /* Ungrab socket and destroy it */ - sock_put(sk); - } else - ax25_destroy_socket(ax25); - return; - } - break; - - case AX25_STATE_3: - /* - * Check the state of the receive buffer. - */ - if (sk != NULL) { - if (atomic_read(&sk->sk_rmem_alloc) < - (sk->sk_rcvbuf >> 1) && - (ax25->condition & AX25_COND_OWN_RX_BUSY)) { - ax25->condition &= ~AX25_COND_OWN_RX_BUSY; - ax25->condition &= ~AX25_COND_ACK_PENDING; - break; - } - } - break; - } - - if (sk) - bh_unlock_sock(sk); - - ax25_start_heartbeat(ax25); -} - -/* dl1bke 960114: T3 works much like the IDLE timeout, but - * gets reloaded with every frame for this - * connection. - */ -void ax25_ds_t3timer_expiry(ax25_cb *ax25) -{ - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_dama_off(ax25); - ax25_disconnect(ax25, ETIMEDOUT); -} - -/* dl1bke 960228: close the connection when IDLE expires. - * unlike T3 this timer gets reloaded only on - * I frames. - */ -void ax25_ds_idletimer_expiry(ax25_cb *ax25) -{ - ax25_clear_queues(ax25); - - ax25->n2count = 0; - ax25->state = AX25_STATE_2; - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25_stop_t3timer(ax25); - - if (ax25->sk != NULL) { - bh_lock_sock(ax25->sk); - ax25->sk->sk_state = TCP_CLOSE; - ax25->sk->sk_err = 0; - ax25->sk->sk_shutdown |= SEND_SHUTDOWN; - if (!sock_flag(ax25->sk, SOCK_DEAD)) { - ax25->sk->sk_state_change(ax25->sk); - sock_set_flag(ax25->sk, SOCK_DEAD); - } - bh_unlock_sock(ax25->sk); - } -} - -/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC - * within the poll of any connected channel. Remember - * that we are not allowed to send anything unless we - * get polled by the Master. - * - * Thus we'll have to do parts of our T1 handling in - * ax25_enquiry_response(). - */ -void ax25_ds_t1_timeout(ax25_cb *ax25) -{ - switch (ax25->state) { - case AX25_STATE_1: - if (ax25->n2count == ax25->n2) { - if (ax25->modulus == AX25_MODULUS) { - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25->n2count = 0; - ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND); - } - } else { - ax25->n2count++; - if (ax25->modulus == AX25_MODULUS) - ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_SABME, AX25_POLLOFF, AX25_COMMAND); - } - break; - - case AX25_STATE_2: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - if (!sock_flag(ax25->sk, SOCK_DESTROY)) - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - } - break; - - case AX25_STATE_3: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - } - break; - } - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); -} diff --git a/net/ax25/ax25_iface.c b/net/ax25/ax25_iface.c deleted file mode 100644 index 3ad454416a5c..000000000000 --- a/net/ax25/ax25_iface.c +++ /dev/null @@ -1,214 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct ax25_protocol *protocol_list; -static DEFINE_RWLOCK(protocol_list_lock); - -static HLIST_HEAD(ax25_linkfail_list); -static DEFINE_SPINLOCK(linkfail_lock); - -static struct listen_struct { - struct listen_struct *next; - ax25_address callsign; - struct net_device *dev; -} *listen_list = NULL; -static DEFINE_SPINLOCK(listen_lock); - -/* - * Do not register the internal protocols AX25_P_TEXT, AX25_P_SEGMENT, - * AX25_P_IP or AX25_P_ARP ... - */ -void ax25_register_pid(struct ax25_protocol *ap) -{ - write_lock_bh(&protocol_list_lock); - ap->next = protocol_list; - protocol_list = ap; - write_unlock_bh(&protocol_list_lock); -} - -EXPORT_SYMBOL_GPL(ax25_register_pid); - -void ax25_protocol_release(unsigned int pid) -{ - struct ax25_protocol *protocol; - - write_lock_bh(&protocol_list_lock); - protocol = protocol_list; - if (protocol == NULL) - goto out; - - if (protocol->pid == pid) { - protocol_list = protocol->next; - goto out; - } - - while (protocol != NULL && protocol->next != NULL) { - if (protocol->next->pid == pid) { - protocol->next = protocol->next->next; - goto out; - } - - protocol = protocol->next; - } -out: - write_unlock_bh(&protocol_list_lock); -} - -EXPORT_SYMBOL(ax25_protocol_release); - -void ax25_linkfail_register(struct ax25_linkfail *lf) -{ - spin_lock_bh(&linkfail_lock); - hlist_add_head(&lf->lf_node, &ax25_linkfail_list); - spin_unlock_bh(&linkfail_lock); -} - -EXPORT_SYMBOL(ax25_linkfail_register); - -void ax25_linkfail_release(struct ax25_linkfail *lf) -{ - spin_lock_bh(&linkfail_lock); - hlist_del_init(&lf->lf_node); - spin_unlock_bh(&linkfail_lock); -} - -EXPORT_SYMBOL(ax25_linkfail_release); - -int ax25_listen_register(const ax25_address *callsign, struct net_device *dev) -{ - struct listen_struct *listen; - - if (ax25_listen_mine(callsign, dev)) - return 0; - - if ((listen = kmalloc_obj(*listen, GFP_ATOMIC)) == NULL) - return -ENOMEM; - - listen->callsign = *callsign; - listen->dev = dev; - - spin_lock_bh(&listen_lock); - listen->next = listen_list; - listen_list = listen; - spin_unlock_bh(&listen_lock); - - return 0; -} - -EXPORT_SYMBOL(ax25_listen_register); - -void ax25_listen_release(const ax25_address *callsign, struct net_device *dev) -{ - struct listen_struct *s, *listen; - - spin_lock_bh(&listen_lock); - listen = listen_list; - if (listen == NULL) { - spin_unlock_bh(&listen_lock); - return; - } - - if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) { - listen_list = listen->next; - spin_unlock_bh(&listen_lock); - kfree(listen); - return; - } - - while (listen != NULL && listen->next != NULL) { - if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) { - s = listen->next; - listen->next = listen->next->next; - spin_unlock_bh(&listen_lock); - kfree(s); - return; - } - - listen = listen->next; - } - spin_unlock_bh(&listen_lock); -} - -EXPORT_SYMBOL(ax25_listen_release); - -int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *) -{ - int (*res)(struct sk_buff *, ax25_cb *) = NULL; - struct ax25_protocol *protocol; - - read_lock(&protocol_list_lock); - for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) - if (protocol->pid == pid) { - res = protocol->func; - break; - } - read_unlock(&protocol_list_lock); - - return res; -} - -int ax25_listen_mine(const ax25_address *callsign, struct net_device *dev) -{ - struct listen_struct *listen; - - spin_lock_bh(&listen_lock); - for (listen = listen_list; listen != NULL; listen = listen->next) - if (ax25cmp(&listen->callsign, callsign) == 0 && - (listen->dev == dev || listen->dev == NULL)) { - spin_unlock_bh(&listen_lock); - return 1; - } - spin_unlock_bh(&listen_lock); - - return 0; -} - -void ax25_link_failed(ax25_cb *ax25, int reason) -{ - struct ax25_linkfail *lf; - - spin_lock_bh(&linkfail_lock); - hlist_for_each_entry(lf, &ax25_linkfail_list, lf_node) - lf->func(ax25, reason); - spin_unlock_bh(&linkfail_lock); -} - -int ax25_protocol_is_registered(unsigned int pid) -{ - struct ax25_protocol *protocol; - int res = 0; - - read_lock_bh(&protocol_list_lock); - for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) - if (protocol->pid == pid) { - res = 1; - break; - } - read_unlock_bh(&protocol_list_lock); - - return res; -} diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c deleted file mode 100644 index d75b3e9ed93d..000000000000 --- a/net/ax25/ax25_in.c +++ /dev/null @@ -1,455 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Given a fragment, queue it on the fragment queue and if the fragment - * is complete, send it back to ax25_rx_iframe. - */ -static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb) -{ - struct sk_buff *skbn, *skbo; - - if (ax25->fragno != 0) { - if (!(*skb->data & AX25_SEG_FIRST)) { - if ((ax25->fragno - 1) == (*skb->data & AX25_SEG_REM)) { - /* Enqueue fragment */ - ax25->fragno = *skb->data & AX25_SEG_REM; - skb_pull(skb, 1); /* skip fragno */ - ax25->fraglen += skb->len; - skb_queue_tail(&ax25->frag_queue, skb); - - /* Last fragment received ? */ - if (ax25->fragno == 0) { - skbn = alloc_skb(AX25_MAX_HEADER_LEN + - ax25->fraglen, - GFP_ATOMIC); - if (!skbn) { - skb_queue_purge(&ax25->frag_queue); - return 1; - } - - skb_reserve(skbn, AX25_MAX_HEADER_LEN); - - skbn->dev = ax25->ax25_dev->dev; - skb_reset_network_header(skbn); - skb_reset_transport_header(skbn); - - /* Copy data from the fragments */ - while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) { - skb_copy_from_linear_data(skbo, - skb_put(skbn, skbo->len), - skbo->len); - kfree_skb(skbo); - } - - ax25->fraglen = 0; - - if (ax25_rx_iframe(ax25, skbn) == 0) - kfree_skb(skbn); - } - - return 1; - } - } - } else { - /* First fragment received */ - if (*skb->data & AX25_SEG_FIRST) { - skb_queue_purge(&ax25->frag_queue); - ax25->fragno = *skb->data & AX25_SEG_REM; - skb_pull(skb, 1); /* skip fragno */ - ax25->fraglen = skb->len; - skb_queue_tail(&ax25->frag_queue, skb); - return 1; - } - } - - return 0; -} - -/* - * This is where all valid I frames are sent to, to be dispatched to - * whichever protocol requires them. - */ -int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) -{ - int (*func)(struct sk_buff *, ax25_cb *); - unsigned char pid; - int queued = 0; - - if (skb == NULL) return 0; - - ax25_start_idletimer(ax25); - - pid = *skb->data; - - if (pid == AX25_P_IP) { - /* working around a TCP bug to keep additional listeners - * happy. TCP re-uses the buffer and destroys the original - * content. - */ - struct sk_buff *skbn = skb_copy(skb, GFP_ATOMIC); - if (skbn != NULL) { - kfree_skb(skb); - skb = skbn; - } - - skb_pull(skb, 1); /* Remove PID */ - skb->mac_header = skb->network_header; - skb_reset_network_header(skb); - skb->dev = ax25->ax25_dev->dev; - skb->pkt_type = PACKET_HOST; - skb->protocol = htons(ETH_P_IP); - netif_rx(skb); - return 1; - } - if (pid == AX25_P_SEGMENT) { - skb_pull(skb, 1); /* Remove PID */ - return ax25_rx_fragment(ax25, skb); - } - - if ((func = ax25_protocol_function(pid)) != NULL) { - skb_pull(skb, 1); /* Remove PID */ - return (*func)(skb, ax25); - } - - if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2) { - if ((!ax25->pidincl && ax25->sk->sk_protocol == pid) || - ax25->pidincl) { - if (sock_queue_rcv_skb(ax25->sk, skb) == 0) - queued = 1; - else - ax25->condition |= AX25_COND_OWN_RX_BUSY; - } - } - - return queued; -} - -/* - * Higher level upcall for a LAPB frame - */ -static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama) -{ - int queued = 0; - - if (ax25->state == AX25_STATE_0) - return 0; - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - queued = ax25_std_frame_in(ax25, skb, type); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (dama || ax25->ax25_dev->dama.slave) - queued = ax25_ds_frame_in(ax25, skb, type); - else - queued = ax25_std_frame_in(ax25, skb, type); - break; -#endif - } - - return queued; -} - -static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, - const ax25_address *dev_addr, struct packet_type *ptype) -{ - ax25_address src, dest, *next_digi = NULL; - int type = 0, mine = 0, dama; - struct sock *make, *sk; - ax25_digi dp, reverse_dp; - ax25_cb *ax25; - ax25_dev *ax25_dev; - - /* - * Process the AX.25/LAPB frame. - */ - - skb_reset_transport_header(skb); - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - goto free; - - /* - * Parse the address header. - */ - - if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) - goto free; - - /* - * Ours perhaps ? - */ - if (dp.lastrepeat + 1 < dp.ndigi) /* Not yet digipeated completely */ - next_digi = &dp.calls[dp.lastrepeat + 1]; - - /* - * Pull of the AX.25 headers leaving the CTRL/PID bytes - */ - skb_pull(skb, ax25_addr_size(&dp)); - - /* For our port addresses ? */ - if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi) - mine = 1; - - /* Also match on any registered callsign from L3/4 */ - if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi) - mine = 1; - - /* UI frame - bypass LAPB processing */ - if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) { - skb_set_transport_header(skb, 2); /* skip control and pid */ - - ax25_send_to_raw(&dest, skb, skb->data[1]); - - if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) - goto free; - - /* Now we are pointing at the pid byte */ - switch (skb->data[1]) { - case AX25_P_IP: - skb_pull(skb,2); /* drop PID/CTRL */ - skb_reset_transport_header(skb); - skb_reset_network_header(skb); - skb->dev = dev; - skb->pkt_type = PACKET_HOST; - skb->protocol = htons(ETH_P_IP); - netif_rx(skb); - break; - - case AX25_P_ARP: - skb_pull(skb,2); - skb_reset_transport_header(skb); - skb_reset_network_header(skb); - skb->dev = dev; - skb->pkt_type = PACKET_HOST; - skb->protocol = htons(ETH_P_ARP); - netif_rx(skb); - break; - case AX25_P_TEXT: - /* Now find a suitable dgram socket */ - sk = ax25_get_socket(&dest, &src, SOCK_DGRAM); - if (sk != NULL) { - bh_lock_sock(sk); - if (atomic_read(&sk->sk_rmem_alloc) >= - sk->sk_rcvbuf) { - kfree_skb(skb); - } else { - /* - * Remove the control and PID. - */ - skb_pull(skb, 2); - if (sock_queue_rcv_skb(sk, skb) != 0) - kfree_skb(skb); - } - bh_unlock_sock(sk); - sock_put(sk); - } else { - kfree_skb(skb); - } - break; - - default: - kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */ - break; - } - - return 0; - } - - /* - * Is connected mode supported on this device ? - * If not, should we DM the incoming frame (except DMs) or - * silently ignore them. For now we stay quiet. - */ - if (ax25_dev->values[AX25_VALUES_CONMODE] == 0) - goto free; - - /* LAPB */ - - /* AX.25 state 1-4 */ - - ax25_digi_invert(&dp, &reverse_dp); - - if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) { - /* - * Process the frame. If it is queued up internally it - * returns one otherwise we free it immediately. This - * routine itself wakes the user context layers so we do - * no further work - */ - if (ax25_process_rx_frame(ax25, skb, type, dama) == 0) - kfree_skb(skb); - - ax25_cb_put(ax25); - return 0; - } - - /* AX.25 state 0 (disconnected) */ - - /* a) received not a SABM(E) */ - - if ((*skb->data & ~AX25_PF) != AX25_SABM && - (*skb->data & ~AX25_PF) != AX25_SABME) { - /* - * Never reply to a DM. Also ignore any connects for - * addresses that are not our interfaces and not a socket. - */ - if ((*skb->data & ~AX25_PF) != AX25_DM && mine) - ax25_return_dm(dev, &src, &dest, &dp); - - goto free; - } - - /* b) received SABM(E) */ - - if (dp.lastrepeat + 1 == dp.ndigi) - sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET); - else - sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET); - - if (sk != NULL) { - bh_lock_sock(sk); - if (sk_acceptq_is_full(sk) || - (make = ax25_make_new(sk, ax25_dev)) == NULL) { - if (mine) - ax25_return_dm(dev, &src, &dest, &dp); - kfree_skb(skb); - bh_unlock_sock(sk); - sock_put(sk); - - return 0; - } - - ax25 = sk_to_ax25(make); - skb_set_owner_r(skb, make); - skb_queue_head(&sk->sk_receive_queue, skb); - - make->sk_state = TCP_ESTABLISHED; - - sk_acceptq_added(sk); - bh_unlock_sock(sk); - } else { - if (!mine) - goto free; - - if ((ax25 = ax25_create_cb()) == NULL) { - ax25_return_dm(dev, &src, &dest, &dp); - goto free; - } - - ax25_fillin_cb(ax25, ax25_dev); - } - - ax25->source_addr = dest; - ax25->dest_addr = src; - - /* - * Sort out any digipeated paths. - */ - if (dp.ndigi && !ax25->digipeat && - (ax25->digipeat = kmalloc_obj(ax25_digi, GFP_ATOMIC)) == NULL) { - kfree_skb(skb); - ax25_destroy_socket(ax25); - if (sk) - sock_put(sk); - return 0; - } - - if (dp.ndigi == 0) { - kfree(ax25->digipeat); - ax25->digipeat = NULL; - } else { - /* Reverse the source SABM's path */ - memcpy(ax25->digipeat, &reverse_dp, sizeof(ax25_digi)); - } - - if ((*skb->data & ~AX25_PF) == AX25_SABME) { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW]; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_WINDOW]; - } - - ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE); - -#ifdef CONFIG_AX25_DAMA_SLAVE - if (dama && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE) - ax25_dama_on(ax25); -#endif - - ax25->state = AX25_STATE_3; - - ax25_cb_add(ax25); - - ax25_start_heartbeat(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - - if (sk) { - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_data_ready(sk); - sock_put(sk); - } else { -free: - kfree_skb(skb); - } - return 0; -} - -/* - * Receive an AX.25 frame via a SLIP interface. - */ -int ax25_kiss_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *ptype, struct net_device *orig_dev) -{ - skb = skb_share_check(skb, GFP_ATOMIC); - if (!skb) - return NET_RX_DROP; - - skb_orphan(skb); - - if (!net_eq(dev_net(dev), &init_net)) { - kfree_skb(skb); - return 0; - } - - if ((*skb->data & 0x0F) != 0) { - kfree_skb(skb); /* Not a KISS data frame */ - return 0; - } - - skb_pull(skb, AX25_KISS_HEADER_LEN); /* Remove the KISS byte */ - - return ax25_rcv(skb, dev, (const ax25_address *)dev->dev_addr, ptype); -} diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c deleted file mode 100644 index 215d4ccf12b9..000000000000 --- a/net/ax25/ax25_ip.c +++ /dev/null @@ -1,247 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For TIOCINQ/OUTQ */ -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * IP over AX.25 encapsulation. - */ - -/* - * Shove an AX.25 UI header on an IP packet and handle ARP - */ - -#ifdef CONFIG_INET - -static int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned int len) -{ - unsigned char *buff; - - /* they sometimes come back to us... */ - if (type == ETH_P_AX25) - return 0; - - /* header is an AX.25 UI frame from us to them */ - buff = skb_push(skb, AX25_HEADER_LEN); - *buff++ = 0x00; /* KISS DATA */ - - if (daddr != NULL) - memcpy(buff, daddr, dev->addr_len); /* Address specified */ - - buff[6] &= ~AX25_CBIT; - buff[6] &= ~AX25_EBIT; - buff[6] |= AX25_SSSID_SPARE; - buff += AX25_ADDR_LEN; - - if (saddr != NULL) - memcpy(buff, saddr, dev->addr_len); - else - memcpy(buff, dev->dev_addr, dev->addr_len); - - buff[6] &= ~AX25_CBIT; - buff[6] |= AX25_EBIT; - buff[6] |= AX25_SSSID_SPARE; - buff += AX25_ADDR_LEN; - - *buff++ = AX25_UI; /* UI */ - - /* Append a suitable AX.25 PID */ - switch (type) { - case ETH_P_IP: - *buff++ = AX25_P_IP; - break; - case ETH_P_ARP: - *buff++ = AX25_P_ARP; - break; - default: - printk(KERN_ERR "AX.25: ax25_hard_header - wrong protocol type 0x%2.2x\n", type); - *buff++ = 0; - break; - } - - if (daddr != NULL) - return AX25_HEADER_LEN; - - return -AX25_HEADER_LEN; /* Unfinished header */ -} - -netdev_tx_t ax25_ip_xmit(struct sk_buff *skb) -{ - struct sk_buff *ourskb; - unsigned char *bp = skb->data; - ax25_route *route; - struct net_device *dev = NULL; - ax25_address *src, *dst; - ax25_digi *digipeat = NULL; - ax25_dev *ax25_dev; - ax25_cb *ax25; - char ip_mode = ' '; - - dst = (ax25_address *)(bp + 1); - src = (ax25_address *)(bp + 8); - - ax25_route_lock_use(); - route = ax25_get_route(dst, NULL); - if (route) { - digipeat = route->digipeat; - dev = route->dev; - ip_mode = route->ip_mode; - } - - if (dev == NULL) - dev = skb->dev; - - rcu_read_lock(); - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) { - kfree_skb(skb); - goto put; - } - - if (bp[16] == AX25_P_IP) { - if (ip_mode == 'V' || (ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { - /* - * We copy the buffer and release the original thereby - * keeping it straight - * - * Note: we report 1 back so the caller will - * not feed the frame direct to the physical device - * We don't want that to happen. (It won't be upset - * as we have pulled the frame from the queue by - * freeing it). - * - * NB: TCP modifies buffers that are still - * on a device queue, thus we use skb_copy() - * instead of using skb_clone() unless this - * gets fixed. - */ - - ax25_address src_c; - ax25_address dst_c; - - if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) { - kfree_skb(skb); - goto put; - } - - if (skb->sk != NULL) - skb_set_owner_w(ourskb, skb->sk); - - kfree_skb(skb); - /* dl9sau: bugfix - * after kfree_skb(), dst and src which were pointer - * to bp which is part of skb->data would not be valid - * anymore hope that after skb_pull(ourskb, ..) our - * dsc_c and src_c will not become invalid - */ - bp = ourskb->data; - dst_c = *(ax25_address *)(bp + 1); - src_c = *(ax25_address *)(bp + 8); - - skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */ - skb_reset_network_header(ourskb); - - ax25=ax25_send_frame( - ourskb, - ax25_dev->values[AX25_VALUES_PACLEN], - &src_c, - &dst_c, digipeat, dev); - if (ax25) { - ax25_cb_put(ax25); - } - goto put; - } - } - - bp[7] &= ~AX25_CBIT; - bp[7] &= ~AX25_EBIT; - bp[7] |= AX25_SSSID_SPARE; - - bp[14] &= ~AX25_CBIT; - bp[14] |= AX25_EBIT; - bp[14] |= AX25_SSSID_SPARE; - - skb_pull(skb, AX25_KISS_HEADER_LEN); - - if (digipeat != NULL) { - if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) - goto put; - - skb = ourskb; - } - - ax25_queue_xmit(skb, dev); - -put: - rcu_read_unlock(); - ax25_route_lock_unuse(); - return NETDEV_TX_OK; -} - -#else /* INET */ - -static int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned int len) -{ - return -AX25_HEADER_LEN; -} - -netdev_tx_t ax25_ip_xmit(struct sk_buff *skb) -{ - kfree_skb(skb); - return NETDEV_TX_OK; -} -#endif - -static bool ax25_validate_header(const char *header, unsigned int len) -{ - ax25_digi digi; - - if (!len) - return false; - - if (header[0]) - return true; - - return ax25_addr_parse(header + 1, len - 1, NULL, NULL, &digi, NULL, - NULL); -} - -const struct header_ops ax25_header_ops = { - .create = ax25_hard_header, - .validate = ax25_validate_header, -}; - -EXPORT_SYMBOL(ax25_header_ops); -EXPORT_SYMBOL(ax25_ip_xmit); diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c deleted file mode 100644 index 8bca2ace98e5..000000000000 --- a/net/ax25/ax25_out.c +++ /dev/null @@ -1,398 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static DEFINE_SPINLOCK(ax25_frag_lock); - -ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, const ax25_address *src, ax25_address *dest, ax25_digi *digi, struct net_device *dev) -{ - ax25_dev *ax25_dev; - ax25_cb *ax25; - - /* - * Take the default packet length for the device if zero is - * specified. - */ - if (paclen == 0) { - rcu_read_lock(); - ax25_dev = ax25_dev_ax25dev(dev); - if (!ax25_dev) { - rcu_read_unlock(); - return NULL; - } - paclen = ax25_dev->values[AX25_VALUES_PACLEN]; - rcu_read_unlock(); - } - - /* - * Look for an existing connection. - */ - if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) { - ax25_output(ax25, paclen, skb); - return ax25; /* It already existed */ - } - - rcu_read_lock(); - ax25_dev = ax25_dev_ax25dev(dev); - if (!ax25_dev) { - rcu_read_unlock(); - return NULL; - } - - if ((ax25 = ax25_create_cb()) == NULL) { - rcu_read_unlock(); - return NULL; - } - ax25_fillin_cb(ax25, ax25_dev); - rcu_read_unlock(); - - ax25->source_addr = *src; - ax25->dest_addr = *dest; - - if (digi != NULL) { - ax25->digipeat = kmemdup(digi, sizeof(*digi), GFP_ATOMIC); - if (ax25->digipeat == NULL) { - ax25_cb_put(ax25); - return NULL; - } - } - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_establish_data_link(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25_dev->dama.slave) - ax25_ds_establish_data_link(ax25); - else - ax25_std_establish_data_link(ax25); - break; -#endif - } - - /* - * There is one ref for the state machine; a caller needs - * one more to put it back, just like with the existing one. - */ - ax25_cb_hold(ax25); - - ax25_cb_add(ax25); - - ax25->state = AX25_STATE_1; - - ax25_start_heartbeat(ax25); - - ax25_output(ax25, paclen, skb); - - return ax25; /* We had to create it */ -} - -EXPORT_SYMBOL(ax25_send_frame); - -/* - * All outgoing AX.25 I frames pass via this routine. Therefore this is - * where the fragmentation of frames takes place. If fragment is set to - * zero then we are not allowed to do fragmentation, even if the frame - * is too large. - */ -void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) -{ - struct sk_buff *skbn; - unsigned char *p; - int frontlen, len, fragno, ka9qfrag, first = 1; - - if (paclen < 16) { - WARN_ON_ONCE(1); - kfree_skb(skb); - return; - } - - if ((skb->len - 1) > paclen) { - if (*skb->data == AX25_P_TEXT) { - skb_pull(skb, 1); /* skip PID */ - ka9qfrag = 0; - } else { - paclen -= 2; /* Allow for fragment control info */ - ka9qfrag = 1; - } - - fragno = skb->len / paclen; - if (skb->len % paclen == 0) fragno--; - - frontlen = skb_headroom(skb); /* Address space + CTRL */ - - while (skb->len > 0) { - spin_lock_bh(&ax25_frag_lock); - if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) { - spin_unlock_bh(&ax25_frag_lock); - printk(KERN_CRIT "AX.25: ax25_output - out of memory\n"); - return; - } - - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); - - spin_unlock_bh(&ax25_frag_lock); - - len = (paclen > skb->len) ? skb->len : paclen; - - if (ka9qfrag == 1) { - skb_reserve(skbn, frontlen + 2); - skb_set_network_header(skbn, - skb_network_offset(skb)); - skb_copy_from_linear_data(skb, skb_put(skbn, len), len); - p = skb_push(skbn, 2); - - *p++ = AX25_P_SEGMENT; - - *p = fragno--; - if (first) { - *p |= AX25_SEG_FIRST; - first = 0; - } - } else { - skb_reserve(skbn, frontlen + 1); - skb_set_network_header(skbn, - skb_network_offset(skb)); - skb_copy_from_linear_data(skb, skb_put(skbn, len), len); - p = skb_push(skbn, 1); - *p = AX25_P_TEXT; - } - - skb_pull(skb, len); - skb_queue_tail(&ax25->write_queue, skbn); /* Throw it on the queue */ - } - - kfree_skb(skb); - } else { - skb_queue_tail(&ax25->write_queue, skb); /* Throw it on the queue */ - } - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_kick(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - /* - * A DAMA slave is _required_ to work as normal AX.25L2V2 - * if no DAMA master is available. - */ - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) ax25_kick(ax25); - break; -#endif - } -} - -/* - * This procedure is passed a buffer descriptor for an iframe. It builds - * the rest of the control part of the frame and then writes it out. - */ -static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit) -{ - unsigned char *frame; - - if (skb == NULL) - return; - - skb_reset_network_header(skb); - - if (ax25->modulus == AX25_MODULUS) { - frame = skb_push(skb, 1); - - *frame = AX25_I; - *frame |= (poll_bit) ? AX25_PF : 0; - *frame |= (ax25->vr << 5); - *frame |= (ax25->vs << 1); - } else { - frame = skb_push(skb, 2); - - frame[0] = AX25_I; - frame[0] |= (ax25->vs << 1); - frame[1] = (poll_bit) ? AX25_EPF : 0; - frame[1] |= (ax25->vr << 1); - } - - ax25_start_idletimer(ax25); - - ax25_transmit_buffer(ax25, skb, AX25_COMMAND); -} - -void ax25_kick(ax25_cb *ax25) -{ - struct sk_buff *skb, *skbn; - int last = 1; - unsigned short start, end, next; - - if (ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4) - return; - - if (ax25->condition & AX25_COND_PEER_RX_BUSY) - return; - - if (skb_peek(&ax25->write_queue) == NULL) - return; - - start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs; - end = (ax25->va + ax25->window) % ax25->modulus; - - if (start == end) - return; - - /* - * Transmit data until either we're out of data to send or - * the window is full. Send a poll on the final I frame if - * the window is filled. - */ - - /* - * Dequeue the frame and copy it. - * Check for race with ax25_clear_queues(). - */ - skb = skb_dequeue(&ax25->write_queue); - if (!skb) - return; - - ax25->vs = start; - - do { - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { - skb_queue_head(&ax25->write_queue, skb); - break; - } - - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); - - next = (ax25->vs + 1) % ax25->modulus; - last = (next == end); - - /* - * Transmit the frame copy. - * bke 960114: do not set the Poll bit on the last frame - * in DAMA mode. - */ - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25_send_iframe(ax25, skbn, AX25_POLLOFF); - break; -#endif - } - - ax25->vs = next; - - /* - * Requeue the original data frame. - */ - skb_queue_tail(&ax25->ack_queue, skb); - - } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL); - - ax25->condition &= ~AX25_COND_ACK_PENDING; - - if (!ax25_t1timer_running(ax25)) { - ax25_stop_t3timer(ax25); - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - } -} - -void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) -{ - unsigned char *ptr; - int headroom; - - if (ax25->ax25_dev == NULL) { - ax25_disconnect(ax25, ENETUNREACH); - return; - } - - headroom = ax25_addr_size(ax25->digipeat); - - if (unlikely(skb_headroom(skb) < headroom)) { - skb = skb_expand_head(skb, headroom); - if (!skb) { - printk(KERN_CRIT "AX.25: ax25_transmit_buffer - out of memory\n"); - return; - } - } - - ptr = skb_push(skb, headroom); - - ax25_addr_build(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus); - - ax25_queue_xmit(skb, ax25->ax25_dev->dev); -} - -/* - * A small shim to dev_queue_xmit to add the KISS control byte, and do - * any packet forwarding in operation. - */ -void ax25_queue_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned char *ptr; - - rcu_read_lock(); - skb->protocol = ax25_type_trans(skb, ax25_fwd_dev(dev)); - rcu_read_unlock(); - - ptr = skb_push(skb, 1); - *ptr = 0x00; /* KISS */ - - dev_queue_xmit(skb); -} - -int ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr) -{ - if (ax25->vs == nr) { - ax25_frames_acked(ax25, nr); - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - return 1; - } else { - if (ax25->va != nr) { - ax25_frames_acked(ax25, nr); - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - return 1; - } - } - return 0; -} diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c deleted file mode 100644 index 1d5c59ccf142..000000000000 --- a/net/ax25/ax25_route.c +++ /dev/null @@ -1,416 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) - * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static ax25_route *ax25_route_list; -DEFINE_RWLOCK(ax25_route_lock); - -void ax25_rt_device_down(struct net_device *dev) -{ - ax25_route *s, *t, *ax25_rt; - - write_lock_bh(&ax25_route_lock); - ax25_rt = ax25_route_list; - while (ax25_rt != NULL) { - s = ax25_rt; - ax25_rt = ax25_rt->next; - - if (s->dev == dev) { - if (ax25_route_list == s) { - ax25_route_list = s->next; - kfree(s->digipeat); - kfree(s); - } else { - for (t = ax25_route_list; t != NULL; t = t->next) { - if (t->next == s) { - t->next = s->next; - kfree(s->digipeat); - kfree(s); - break; - } - } - } - } - } - write_unlock_bh(&ax25_route_lock); -} - -static int __must_check ax25_rt_add(struct ax25_routes_struct *route) -{ - ax25_route *ax25_rt; - ax25_dev *ax25_dev; - int i; - - if (route->digi_count > AX25_MAX_DIGIS) - return -EINVAL; - - ax25_dev = ax25_addr_ax25dev(&route->port_addr); - if (!ax25_dev) - return -EINVAL; - - write_lock_bh(&ax25_route_lock); - - ax25_rt = ax25_route_list; - while (ax25_rt != NULL) { - if (ax25cmp(&ax25_rt->callsign, &route->dest_addr) == 0 && - ax25_rt->dev == ax25_dev->dev) { - kfree(ax25_rt->digipeat); - ax25_rt->digipeat = NULL; - if (route->digi_count != 0) { - if ((ax25_rt->digipeat = kmalloc_obj(ax25_digi, GFP_ATOMIC)) == NULL) { - write_unlock_bh(&ax25_route_lock); - ax25_dev_put(ax25_dev); - return -ENOMEM; - } - ax25_rt->digipeat->lastrepeat = -1; - ax25_rt->digipeat->ndigi = route->digi_count; - for (i = 0; i < route->digi_count; i++) { - ax25_rt->digipeat->repeated[i] = 0; - ax25_rt->digipeat->calls[i] = route->digi_addr[i]; - } - } - write_unlock_bh(&ax25_route_lock); - ax25_dev_put(ax25_dev); - return 0; - } - ax25_rt = ax25_rt->next; - } - - if ((ax25_rt = kmalloc_obj(ax25_route, GFP_ATOMIC)) == NULL) { - write_unlock_bh(&ax25_route_lock); - ax25_dev_put(ax25_dev); - return -ENOMEM; - } - - ax25_rt->callsign = route->dest_addr; - ax25_rt->dev = ax25_dev->dev; - ax25_rt->digipeat = NULL; - ax25_rt->ip_mode = ' '; - if (route->digi_count != 0) { - if ((ax25_rt->digipeat = kmalloc_obj(ax25_digi, GFP_ATOMIC)) == NULL) { - write_unlock_bh(&ax25_route_lock); - kfree(ax25_rt); - ax25_dev_put(ax25_dev); - return -ENOMEM; - } - ax25_rt->digipeat->lastrepeat = -1; - ax25_rt->digipeat->ndigi = route->digi_count; - for (i = 0; i < route->digi_count; i++) { - ax25_rt->digipeat->repeated[i] = 0; - ax25_rt->digipeat->calls[i] = route->digi_addr[i]; - } - } - ax25_rt->next = ax25_route_list; - ax25_route_list = ax25_rt; - write_unlock_bh(&ax25_route_lock); - ax25_dev_put(ax25_dev); - - return 0; -} - -void __ax25_put_route(ax25_route *ax25_rt) -{ - kfree(ax25_rt->digipeat); - kfree(ax25_rt); -} - -static int ax25_rt_del(struct ax25_routes_struct *route) -{ - ax25_route *s, *t, *ax25_rt; - ax25_dev *ax25_dev; - - if ((ax25_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL) - return -EINVAL; - - write_lock_bh(&ax25_route_lock); - - ax25_rt = ax25_route_list; - while (ax25_rt != NULL) { - s = ax25_rt; - ax25_rt = ax25_rt->next; - if (s->dev == ax25_dev->dev && - ax25cmp(&route->dest_addr, &s->callsign) == 0) { - if (ax25_route_list == s) { - ax25_route_list = s->next; - __ax25_put_route(s); - } else { - for (t = ax25_route_list; t != NULL; t = t->next) { - if (t->next == s) { - t->next = s->next; - __ax25_put_route(s); - break; - } - } - } - } - } - write_unlock_bh(&ax25_route_lock); - ax25_dev_put(ax25_dev); - - return 0; -} - -static int ax25_rt_opt(struct ax25_route_opt_struct *rt_option) -{ - ax25_route *ax25_rt; - ax25_dev *ax25_dev; - int err = 0; - - if ((ax25_dev = ax25_addr_ax25dev(&rt_option->port_addr)) == NULL) - return -EINVAL; - - write_lock_bh(&ax25_route_lock); - - ax25_rt = ax25_route_list; - while (ax25_rt != NULL) { - if (ax25_rt->dev == ax25_dev->dev && - ax25cmp(&rt_option->dest_addr, &ax25_rt->callsign) == 0) { - switch (rt_option->cmd) { - case AX25_SET_RT_IPMODE: - switch (rt_option->arg) { - case ' ': - case 'D': - case 'V': - ax25_rt->ip_mode = rt_option->arg; - break; - default: - err = -EINVAL; - goto out; - } - break; - default: - err = -EINVAL; - goto out; - } - } - ax25_rt = ax25_rt->next; - } - -out: - write_unlock_bh(&ax25_route_lock); - ax25_dev_put(ax25_dev); - return err; -} - -int ax25_rt_ioctl(unsigned int cmd, void __user *arg) -{ - struct ax25_route_opt_struct rt_option; - struct ax25_routes_struct route; - - switch (cmd) { - case SIOCADDRT: - if (copy_from_user(&route, arg, sizeof(route))) - return -EFAULT; - return ax25_rt_add(&route); - - case SIOCDELRT: - if (copy_from_user(&route, arg, sizeof(route))) - return -EFAULT; - return ax25_rt_del(&route); - - case SIOCAX25OPTRT: - if (copy_from_user(&rt_option, arg, sizeof(rt_option))) - return -EFAULT; - return ax25_rt_opt(&rt_option); - - default: - return -EINVAL; - } -} - -#ifdef CONFIG_PROC_FS - -static void *ax25_rt_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(ax25_route_lock) -{ - struct ax25_route *ax25_rt; - int i = 1; - - read_lock(&ax25_route_lock); - if (*pos == 0) - return SEQ_START_TOKEN; - - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (i == *pos) - return ax25_rt; - ++i; - } - - return NULL; -} - -static void *ax25_rt_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - return (v == SEQ_START_TOKEN) ? ax25_route_list : - ((struct ax25_route *) v)->next; -} - -static void ax25_rt_seq_stop(struct seq_file *seq, void *v) - __releases(ax25_route_lock) -{ - read_unlock(&ax25_route_lock); -} - -static int ax25_rt_seq_show(struct seq_file *seq, void *v) -{ - char buf[11]; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, "callsign dev mode digipeaters\n"); - else { - struct ax25_route *ax25_rt = v; - const char *callsign; - int i; - - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0) - callsign = "default"; - else - callsign = ax2asc(buf, &ax25_rt->callsign); - - seq_printf(seq, "%-9s %-4s", - callsign, - ax25_rt->dev ? ax25_rt->dev->name : "???"); - - switch (ax25_rt->ip_mode) { - case 'V': - seq_puts(seq, " vc"); - break; - case 'D': - seq_puts(seq, " dg"); - break; - default: - seq_puts(seq, " *"); - break; - } - - if (ax25_rt->digipeat != NULL) - for (i = 0; i < ax25_rt->digipeat->ndigi; i++) - seq_printf(seq, " %s", - ax2asc(buf, &ax25_rt->digipeat->calls[i])); - - seq_puts(seq, "\n"); - } - return 0; -} - -const struct seq_operations ax25_rt_seqops = { - .start = ax25_rt_seq_start, - .next = ax25_rt_seq_next, - .stop = ax25_rt_seq_stop, - .show = ax25_rt_seq_show, -}; -#endif - -/* - * Find AX.25 route - * - * Only routes with a reference count of zero can be destroyed. - * Must be called with ax25_route_lock read locked. - */ -ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev) -{ - ax25_route *ax25_spe_rt = NULL; - ax25_route *ax25_def_rt = NULL; - ax25_route *ax25_rt; - - /* - * Bind to the physical interface we heard them on, or the default - * route if none is found; - */ - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (dev == NULL) { - if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL) - ax25_spe_rt = ax25_rt; - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL) - ax25_def_rt = ax25_rt; - } else { - if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev) - ax25_spe_rt = ax25_rt; - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev) - ax25_def_rt = ax25_rt; - } - } - - ax25_rt = ax25_def_rt; - if (ax25_spe_rt != NULL) - ax25_rt = ax25_spe_rt; - - return ax25_rt; -} - - -struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, - ax25_address *dest, ax25_digi *digi) -{ - unsigned char *bp; - int len; - - len = digi->ndigi * AX25_ADDR_LEN; - - if (unlikely(skb_headroom(skb) < len)) { - skb = skb_expand_head(skb, len); - if (!skb) { - printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n"); - return NULL; - } - } - - bp = skb_push(skb, len); - - ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS); - - return skb; -} - -/* - * Free all memory associated with routing structures. - */ -void __exit ax25_rt_free(void) -{ - ax25_route *s, *ax25_rt = ax25_route_list; - - write_lock_bh(&ax25_route_lock); - while (ax25_rt != NULL) { - s = ax25_rt; - ax25_rt = ax25_rt->next; - - kfree(s->digipeat); - kfree(s); - } - write_unlock_bh(&ax25_route_lock); -} diff --git a/net/ax25/ax25_std_in.c b/net/ax25/ax25_std_in.c deleted file mode 100644 index ba176196ae06..000000000000 --- a/net/ax25/ax25_std_in.c +++ /dev/null @@ -1,443 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) - * - * Most of this code is based on the SDL diagrams published in the 7th ARRL - * Computer Networking Conference papers. The diagrams have mistakes in them, - * but are mostly correct. Before you modify the code could you read the SDL - * diagrams as the code is not obvious and probably very easy to break. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * State machine for state 1, Awaiting Connection State. - * The handling of the timer(s) is in file ax25_std_timer.c. - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_SABME: - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); - break; - - case AX25_UA: - if (pf) { - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25->state = AX25_STATE_3; - ax25->n2count = 0; - if (ax25->sk != NULL) { - bh_lock_sock(ax25->sk); - ax25->sk->sk_state = TCP_ESTABLISHED; - /* For WAIT_SABM connections we will produce an accept ready socket here */ - if (!sock_flag(ax25->sk, SOCK_DEAD)) - ax25->sk->sk_state_change(ax25->sk); - bh_unlock_sock(ax25->sk); - } - } - break; - - case AX25_DM: - if (pf) { - if (ax25->modulus == AX25_MODULUS) { - ax25_disconnect(ax25, ECONNREFUSED); - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } - } - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 2, Awaiting Release State. - * The handling of the timer(s) is in file ax25_std_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - case AX25_UA: - if (pf) - ax25_disconnect(ax25, 0); - break; - - case AX25_I: - case AX25_REJ: - case AX25_RNR: - case AX25_RR: - if (pf) ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 3, Connected State. - * The handling of the timer(s) is in file ax25_std_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) -{ - int queued = 0; - - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - if (frametype == AX25_SABM) { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } else { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - } - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->condition = 0x00; - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25_requeue_frames(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - ax25_disconnect(ax25, ECONNRESET); - break; - - case AX25_RR: - case AX25_RNR: - if (frametype == AX25_RR) - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - else - ax25->condition |= AX25_COND_PEER_RX_BUSY; - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_check_iframes_acked(ax25, nr); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_requeue_frames(ax25); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_I: - if (!ax25_validate_nr(ax25, nr)) { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - break; - } - if (ax25->condition & AX25_COND_PEER_RX_BUSY) { - ax25_frames_acked(ax25, nr); - } else { - ax25_check_iframes_acked(ax25, nr); - } - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { - if (pf) ax25_std_enquiry_response(ax25); - break; - } - if (ns == ax25->vr) { - ax25->vr = (ax25->vr + 1) % ax25->modulus; - queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25->vr = ns; /* ax25->vr - 1 */ - ax25->condition &= ~AX25_COND_REJECT; - if (pf) { - ax25_std_enquiry_response(ax25); - } else { - if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->condition |= AX25_COND_ACK_PENDING; - ax25_start_t2timer(ax25); - } - } - } else { - if (ax25->condition & AX25_COND_REJECT) { - if (pf) ax25_std_enquiry_response(ax25); - } else { - ax25->condition |= AX25_COND_REJECT; - ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE); - ax25->condition &= ~AX25_COND_ACK_PENDING; - } - } - break; - - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_std_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - - default: - break; - } - - return queued; -} - -/* - * State machine for state 4, Timer Recovery State. - * The handling of the timer(s) is in file ax25_std_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) -{ - int queued = 0; - - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - if (frametype == AX25_SABM) { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } else { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - } - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->condition = 0x00; - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25->state = AX25_STATE_3; - ax25->n2count = 0; - ax25_requeue_frames(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - ax25_disconnect(ax25, ECONNRESET); - break; - - case AX25_RR: - case AX25_RNR: - if (frametype == AX25_RR) - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - else - ax25->condition |= AX25_COND_PEER_RX_BUSY; - if (type == AX25_RESPONSE && pf) { - ax25_stop_t1timer(ax25); - ax25->n2count = 0; - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - if (ax25->vs == ax25->va) { - ax25_start_t3timer(ax25); - ax25->state = AX25_STATE_3; - } else { - ax25_requeue_frames(ax25); - } - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - } - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - if (pf && type == AX25_RESPONSE) { - ax25_stop_t1timer(ax25); - ax25->n2count = 0; - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - if (ax25->vs == ax25->va) { - ax25_start_t3timer(ax25); - ax25->state = AX25_STATE_3; - } else { - ax25_requeue_frames(ax25); - } - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - } - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - ax25_requeue_frames(ax25); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_I: - if (!ax25_validate_nr(ax25, nr)) { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - break; - } - ax25_frames_acked(ax25, nr); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { - if (pf) - ax25_std_enquiry_response(ax25); - break; - } - if (ns == ax25->vr) { - ax25->vr = (ax25->vr + 1) % ax25->modulus; - queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25->vr = ns; /* ax25->vr - 1 */ - ax25->condition &= ~AX25_COND_REJECT; - if (pf) { - ax25_std_enquiry_response(ax25); - } else { - if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->condition |= AX25_COND_ACK_PENDING; - ax25_start_t2timer(ax25); - } - } - } else { - if (ax25->condition & AX25_COND_REJECT) { - if (pf) ax25_std_enquiry_response(ax25); - } else { - ax25->condition |= AX25_COND_REJECT; - ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE); - ax25->condition &= ~AX25_COND_ACK_PENDING; - } - } - break; - - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_std_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - - default: - break; - } - - return queued; -} - -/* - * Higher level upcall for a LAPB frame - */ -int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) -{ - int queued = 0, frametype, ns, nr, pf; - - frametype = ax25_decode(ax25, skb, &ns, &nr, &pf); - - switch (ax25->state) { - case AX25_STATE_1: - queued = ax25_std_state1_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_2: - queued = ax25_std_state2_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_3: - queued = ax25_std_state3_machine(ax25, skb, frametype, ns, nr, pf, type); - break; - case AX25_STATE_4: - queued = ax25_std_state4_machine(ax25, skb, frametype, ns, nr, pf, type); - break; - } - - ax25_kick(ax25); - - return queued; -} diff --git a/net/ax25/ax25_std_subr.c b/net/ax25/ax25_std_subr.c deleted file mode 100644 index 4c36f1342558..000000000000 --- a/net/ax25/ax25_std_subr.c +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * The following routines are taken from page 170 of the 7th ARRL Computer - * Networking Conference paper, as is the whole state machine. - */ - -void ax25_std_nr_error_recovery(ax25_cb *ax25) -{ - ax25_std_establish_data_link(ax25); -} - -void ax25_std_establish_data_link(ax25_cb *ax25) -{ - ax25->condition = 0x00; - ax25->n2count = 0; - - if (ax25->modulus == AX25_MODULUS) - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND); - - ax25_calculate_t1(ax25); - ax25_stop_idletimer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t1timer(ax25); -} - -void ax25_std_transmit_enquiry(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_COMMAND); - - ax25->condition &= ~AX25_COND_ACK_PENDING; - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); -} - -void ax25_std_enquiry_response(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_RESPONSE); - else - ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_RESPONSE); - - ax25->condition &= ~AX25_COND_ACK_PENDING; -} - -void ax25_std_timeout_response(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25_send_control(ax25, AX25_RNR, AX25_POLLOFF, AX25_RESPONSE); - else - ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE); - - ax25->condition &= ~AX25_COND_ACK_PENDING; -} diff --git a/net/ax25/ax25_std_timer.c b/net/ax25/ax25_std_timer.c deleted file mode 100644 index b17da41210cb..000000000000 --- a/net/ax25/ax25_std_timer.c +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void ax25_std_heartbeat_expiry(ax25_cb *ax25) -{ - struct sock *sk = ax25->sk; - - if (sk) - bh_lock_sock(sk); - - switch (ax25->state) { - case AX25_STATE_0: - case AX25_STATE_2: - /* Magic here: If we listen() and a new link dies before it - is accepted() it isn't 'dead' so doesn't get removed. */ - if (!sk || sock_flag(sk, SOCK_DESTROY) || - (sk->sk_state == TCP_LISTEN && - sock_flag(sk, SOCK_DEAD))) { - if (sk) { - sock_hold(sk); - ax25_destroy_socket(ax25); - bh_unlock_sock(sk); - /* Ungrab socket and destroy it */ - sock_put(sk); - } else - ax25_destroy_socket(ax25); - return; - } - break; - - case AX25_STATE_3: - case AX25_STATE_4: - /* - * Check the state of the receive buffer. - */ - if (sk != NULL) { - if (atomic_read(&sk->sk_rmem_alloc) < - (sk->sk_rcvbuf >> 1) && - (ax25->condition & AX25_COND_OWN_RX_BUSY)) { - ax25->condition &= ~AX25_COND_OWN_RX_BUSY; - ax25->condition &= ~AX25_COND_ACK_PENDING; - ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE); - break; - } - } - } - - if (sk) - bh_unlock_sock(sk); - - ax25_start_heartbeat(ax25); -} - -void ax25_std_t2timer_expiry(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_ACK_PENDING) { - ax25->condition &= ~AX25_COND_ACK_PENDING; - ax25_std_timeout_response(ax25); - } -} - -void ax25_std_t3timer_expiry(ax25_cb *ax25) -{ - ax25->n2count = 0; - ax25_std_transmit_enquiry(ax25); - ax25->state = AX25_STATE_4; -} - -void ax25_std_idletimer_expiry(ax25_cb *ax25) -{ - ax25_clear_queues(ax25); - - ax25->n2count = 0; - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25->state = AX25_STATE_2; - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - - if (ax25->sk != NULL) { - bh_lock_sock(ax25->sk); - ax25->sk->sk_state = TCP_CLOSE; - ax25->sk->sk_err = 0; - ax25->sk->sk_shutdown |= SEND_SHUTDOWN; - if (!sock_flag(ax25->sk, SOCK_DEAD)) { - ax25->sk->sk_state_change(ax25->sk); - sock_set_flag(ax25->sk, SOCK_DEAD); - } - bh_unlock_sock(ax25->sk); - } -} - -void ax25_std_t1timer_expiry(ax25_cb *ax25) -{ - switch (ax25->state) { - case AX25_STATE_1: - if (ax25->n2count == ax25->n2) { - if (ax25->modulus == AX25_MODULUS) { - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25->n2count = 0; - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - } - } else { - ax25->n2count++; - if (ax25->modulus == AX25_MODULUS) - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND); - } - break; - - case AX25_STATE_2: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - if (!sock_flag(ax25->sk, SOCK_DESTROY)) - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - } - break; - - case AX25_STATE_3: - ax25->n2count = 1; - ax25_std_transmit_enquiry(ax25); - ax25->state = AX25_STATE_4; - break; - - case AX25_STATE_4: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - ax25_std_transmit_enquiry(ax25); - } - break; - } - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); -} diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c deleted file mode 100644 index bff4b203a893..000000000000 --- a/net/ax25/ax25_subr.c +++ /dev/null @@ -1,296 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * This routine purges all the queues of frames. - */ -void ax25_clear_queues(ax25_cb *ax25) -{ - skb_queue_purge(&ax25->write_queue); - skb_queue_purge(&ax25->ack_queue); - skb_queue_purge(&ax25->reseq_queue); - skb_queue_purge(&ax25->frag_queue); -} - -/* - * This routine purges the input queue of those frames that have been - * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the - * SDL diagram. - */ -void ax25_frames_acked(ax25_cb *ax25, unsigned short nr) -{ - struct sk_buff *skb; - - /* - * Remove all the ack-ed frames from the ack queue. - */ - if (ax25->va != nr) { - while (skb_peek(&ax25->ack_queue) != NULL && ax25->va != nr) { - skb = skb_dequeue(&ax25->ack_queue); - kfree_skb(skb); - ax25->va = (ax25->va + 1) % ax25->modulus; - } - } -} - -void ax25_requeue_frames(ax25_cb *ax25) -{ - struct sk_buff *skb; - - /* - * Requeue all the un-ack-ed frames on the output queue to be picked - * up by ax25_kick called from the timer. This arrangement handles the - * possibility of an empty output queue. - */ - while ((skb = skb_dequeue_tail(&ax25->ack_queue)) != NULL) - skb_queue_head(&ax25->write_queue, skb); -} - -/* - * Validate that the value of nr is between va and vs. Return true or - * false for testing. - */ -int ax25_validate_nr(ax25_cb *ax25, unsigned short nr) -{ - unsigned short vc = ax25->va; - - while (vc != ax25->vs) { - if (nr == vc) return 1; - vc = (vc + 1) % ax25->modulus; - } - - if (nr == ax25->vs) return 1; - - return 0; -} - -/* - * This routine is the centralised routine for parsing the control - * information for the different frame formats. - */ -int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf) -{ - unsigned char *frame; - int frametype = AX25_ILLEGAL; - - frame = skb->data; - *ns = *nr = *pf = 0; - - if (ax25->modulus == AX25_MODULUS) { - if ((frame[0] & AX25_S) == 0) { - frametype = AX25_I; /* I frame - carries NR/NS/PF */ - *ns = (frame[0] >> 1) & 0x07; - *nr = (frame[0] >> 5) & 0x07; - *pf = frame[0] & AX25_PF; - } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ - frametype = frame[0] & 0x0F; - *nr = (frame[0] >> 5) & 0x07; - *pf = frame[0] & AX25_PF; - } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ - frametype = frame[0] & ~AX25_PF; - *pf = frame[0] & AX25_PF; - } - skb_pull(skb, 1); - } else { - if ((frame[0] & AX25_S) == 0) { - frametype = AX25_I; /* I frame - carries NR/NS/PF */ - *ns = (frame[0] >> 1) & 0x7F; - *nr = (frame[1] >> 1) & 0x7F; - *pf = frame[1] & AX25_EPF; - skb_pull(skb, 2); - } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ - frametype = frame[0] & 0x0F; - *nr = (frame[1] >> 1) & 0x7F; - *pf = frame[1] & AX25_EPF; - skb_pull(skb, 2); - } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ - frametype = frame[0] & ~AX25_PF; - *pf = frame[0] & AX25_PF; - skb_pull(skb, 1); - } - } - - return frametype; -} - -/* - * This routine is called when the HDLC layer internally generates a - * command or response for the remote machine ( eg. RR, UA etc. ). - * Only supervisory or unnumbered frames are processed. - */ -void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type) -{ - struct sk_buff *skb; - unsigned char *dptr; - - if ((skb = alloc_skb(ax25->ax25_dev->dev->hard_header_len + 2, GFP_ATOMIC)) == NULL) - return; - - skb_reserve(skb, ax25->ax25_dev->dev->hard_header_len); - - skb_reset_network_header(skb); - - /* Assume a response - address structure for DTE */ - if (ax25->modulus == AX25_MODULUS) { - dptr = skb_put(skb, 1); - *dptr = frametype; - *dptr |= (poll_bit) ? AX25_PF : 0; - if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */ - *dptr |= (ax25->vr << 5); - } else { - if ((frametype & AX25_U) == AX25_U) { - dptr = skb_put(skb, 1); - *dptr = frametype; - *dptr |= (poll_bit) ? AX25_PF : 0; - } else { - dptr = skb_put(skb, 2); - dptr[0] = frametype; - dptr[1] = (ax25->vr << 1); - dptr[1] |= (poll_bit) ? AX25_EPF : 0; - } - } - - ax25_transmit_buffer(ax25, skb, type); -} - -/* - * Send a 'DM' to an unknown connection attempt, or an invalid caller. - * - * Note: src here is the sender, thus it's the target of the DM - */ -void ax25_return_dm(struct net_device *dev, ax25_address *src, ax25_address *dest, ax25_digi *digi) -{ - struct sk_buff *skb; - char *dptr; - ax25_digi retdigi; - - if (dev == NULL) - return; - - if ((skb = alloc_skb(dev->hard_header_len + 1, GFP_ATOMIC)) == NULL) - return; /* Next SABM will get DM'd */ - - skb_reserve(skb, dev->hard_header_len); - skb_reset_network_header(skb); - - ax25_digi_invert(digi, &retdigi); - - dptr = skb_put(skb, 1); - - *dptr = AX25_DM | AX25_PF; - - /* - * Do the address ourselves - */ - dptr = skb_push(skb, ax25_addr_size(digi)); - dptr += ax25_addr_build(dptr, dest, src, &retdigi, AX25_RESPONSE, AX25_MODULUS); - - ax25_queue_xmit(skb, dev); -} - -/* - * Exponential backoff for AX.25 - */ -void ax25_calculate_t1(ax25_cb *ax25) -{ - int n, t = 2; - - switch (ax25->backoff) { - case 0: - break; - - case 1: - t += 2 * ax25->n2count; - break; - - case 2: - for (n = 0; n < ax25->n2count; n++) - t *= 2; - if (t > 8) t = 8; - break; - } - - ax25->t1 = t * ax25->rtt; -} - -/* - * Calculate the Round Trip Time - */ -void ax25_calculate_rtt(ax25_cb *ax25) -{ - if (ax25->backoff == 0) - return; - - if (ax25_t1timer_running(ax25) && ax25->n2count == 0) - ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25_display_timer(&ax25->t1timer)) / 10; - - if (ax25->rtt < AX25_T1CLAMPLO) - ax25->rtt = AX25_T1CLAMPLO; - - if (ax25->rtt > AX25_T1CLAMPHI) - ax25->rtt = AX25_T1CLAMPHI; -} - -void ax25_disconnect(ax25_cb *ax25, int reason) -{ - ax25_clear_queues(ax25); - - if (reason == ENETUNREACH) { - timer_delete_sync(&ax25->timer); - timer_delete_sync(&ax25->t1timer); - timer_delete_sync(&ax25->t2timer); - timer_delete_sync(&ax25->t3timer); - timer_delete_sync(&ax25->idletimer); - } else { - if (ax25->sk && !sock_flag(ax25->sk, SOCK_DESTROY)) - ax25_stop_heartbeat(ax25); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); - } - - ax25->state = AX25_STATE_0; - - ax25_link_failed(ax25, reason); - - if (ax25->sk != NULL) { - local_bh_disable(); - bh_lock_sock(ax25->sk); - ax25->sk->sk_state = TCP_CLOSE; - ax25->sk->sk_err = reason; - ax25->sk->sk_shutdown |= SEND_SHUTDOWN; - if (!sock_flag(ax25->sk, SOCK_DEAD)) { - ax25->sk->sk_state_change(ax25->sk); - sock_set_flag(ax25->sk, SOCK_DEAD); - } - bh_unlock_sock(ax25->sk); - local_bh_enable(); - } -} diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c deleted file mode 100644 index a69bfbc8b679..000000000000 --- a/net/ax25/ax25_timer.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Tomi Manninen OH2BNS (oh2bns@sral.fi) - * Copyright (C) Darryl Miles G7LED (dlm@g7led.demon.co.uk) - * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) - * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) - * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void ax25_heartbeat_expiry(struct timer_list *); -static void ax25_t1timer_expiry(struct timer_list *); -static void ax25_t2timer_expiry(struct timer_list *); -static void ax25_t3timer_expiry(struct timer_list *); -static void ax25_idletimer_expiry(struct timer_list *); - -void ax25_setup_timers(ax25_cb *ax25) -{ - timer_setup(&ax25->timer, ax25_heartbeat_expiry, 0); - timer_setup(&ax25->t1timer, ax25_t1timer_expiry, 0); - timer_setup(&ax25->t2timer, ax25_t2timer_expiry, 0); - timer_setup(&ax25->t3timer, ax25_t3timer_expiry, 0); - timer_setup(&ax25->idletimer, ax25_idletimer_expiry, 0); -} - -void ax25_start_heartbeat(ax25_cb *ax25) -{ - mod_timer(&ax25->timer, jiffies + 5 * HZ); -} - -void ax25_start_t1timer(ax25_cb *ax25) -{ - mod_timer(&ax25->t1timer, jiffies + ax25->t1); -} - -void ax25_start_t2timer(ax25_cb *ax25) -{ - mod_timer(&ax25->t2timer, jiffies + ax25->t2); -} - -void ax25_start_t3timer(ax25_cb *ax25) -{ - if (ax25->t3 > 0) - mod_timer(&ax25->t3timer, jiffies + ax25->t3); - else - timer_delete(&ax25->t3timer); -} - -void ax25_start_idletimer(ax25_cb *ax25) -{ - if (ax25->idle > 0) - mod_timer(&ax25->idletimer, jiffies + ax25->idle); - else - timer_delete(&ax25->idletimer); -} - -void ax25_stop_heartbeat(ax25_cb *ax25) -{ - timer_delete(&ax25->timer); -} - -void ax25_stop_t1timer(ax25_cb *ax25) -{ - timer_delete(&ax25->t1timer); -} - -void ax25_stop_t2timer(ax25_cb *ax25) -{ - timer_delete(&ax25->t2timer); -} - -void ax25_stop_t3timer(ax25_cb *ax25) -{ - timer_delete(&ax25->t3timer); -} - -void ax25_stop_idletimer(ax25_cb *ax25) -{ - timer_delete(&ax25->idletimer); -} - -int ax25_t1timer_running(ax25_cb *ax25) -{ - return timer_pending(&ax25->t1timer); -} - -unsigned long ax25_display_timer(struct timer_list *timer) -{ - long delta = timer->expires - jiffies; - - if (!timer_pending(timer)) - return 0; - - return max(0L, delta); -} - -EXPORT_SYMBOL(ax25_display_timer); - -static void ax25_heartbeat_expiry(struct timer_list *t) -{ - int proto = AX25_PROTO_STD_SIMPLEX; - ax25_cb *ax25 = timer_container_of(ax25, t, timer); - - if (ax25->ax25_dev) - proto = ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]; - - switch (proto) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_heartbeat_expiry(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25->ax25_dev->dama.slave) - ax25_ds_heartbeat_expiry(ax25); - else - ax25_std_heartbeat_expiry(ax25); - break; -#endif - } -} - -static void ax25_t1timer_expiry(struct timer_list *t) -{ - ax25_cb *ax25 = timer_container_of(ax25, t, t1timer); - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t1timer_expiry(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) - ax25_std_t1timer_expiry(ax25); - break; -#endif - } -} - -static void ax25_t2timer_expiry(struct timer_list *t) -{ - ax25_cb *ax25 = timer_container_of(ax25, t, t2timer); - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t2timer_expiry(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) - ax25_std_t2timer_expiry(ax25); - break; -#endif - } -} - -static void ax25_t3timer_expiry(struct timer_list *t) -{ - ax25_cb *ax25 = timer_container_of(ax25, t, t3timer); - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t3timer_expiry(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25->ax25_dev->dama.slave) - ax25_ds_t3timer_expiry(ax25); - else - ax25_std_t3timer_expiry(ax25); - break; -#endif - } -} - -static void ax25_idletimer_expiry(struct timer_list *t) -{ - ax25_cb *ax25 = timer_container_of(ax25, t, idletimer); - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_idletimer_expiry(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25->ax25_dev->dama.slave) - ax25_ds_idletimer_expiry(ax25); - else - ax25_std_idletimer_expiry(ax25); - break; -#endif - } -} diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c deleted file mode 100644 index 159ce74273f0..000000000000 --- a/net/ax25/ax25_uid.c +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Callsign/UID mapper. This is in kernel space for security on multi-amateur machines. - */ - -static HLIST_HEAD(ax25_uid_list); -static DEFINE_RWLOCK(ax25_uid_lock); - -int ax25_uid_policy; - -EXPORT_SYMBOL(ax25_uid_policy); - -ax25_uid_assoc *ax25_findbyuid(kuid_t uid) -{ - ax25_uid_assoc *ax25_uid, *res = NULL; - - read_lock(&ax25_uid_lock); - ax25_uid_for_each(ax25_uid, &ax25_uid_list) { - if (uid_eq(ax25_uid->uid, uid)) { - ax25_uid_hold(ax25_uid); - res = ax25_uid; - break; - } - } - read_unlock(&ax25_uid_lock); - - return res; -} - -EXPORT_SYMBOL(ax25_findbyuid); - -int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) -{ - ax25_uid_assoc *ax25_uid; - ax25_uid_assoc *user; - unsigned long res; - - switch (cmd) { - case SIOCAX25GETUID: - res = -ENOENT; - read_lock(&ax25_uid_lock); - ax25_uid_for_each(ax25_uid, &ax25_uid_list) { - if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) { - res = from_kuid_munged(current_user_ns(), ax25_uid->uid); - break; - } - } - read_unlock(&ax25_uid_lock); - - return res; - - case SIOCAX25ADDUID: - { - kuid_t sax25_kuid; - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - sax25_kuid = make_kuid(current_user_ns(), sax->sax25_uid); - if (!uid_valid(sax25_kuid)) - return -EINVAL; - user = ax25_findbyuid(sax25_kuid); - if (user) { - ax25_uid_put(user); - return -EEXIST; - } - if (sax->sax25_uid == 0) - return -EINVAL; - if ((ax25_uid = kmalloc_obj(*ax25_uid)) == NULL) - return -ENOMEM; - - refcount_set(&ax25_uid->refcount, 1); - ax25_uid->uid = sax25_kuid; - ax25_uid->call = sax->sax25_call; - - write_lock(&ax25_uid_lock); - hlist_add_head(&ax25_uid->uid_node, &ax25_uid_list); - write_unlock(&ax25_uid_lock); - - return 0; - } - case SIOCAX25DELUID: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - ax25_uid = NULL; - write_lock(&ax25_uid_lock); - ax25_uid_for_each(ax25_uid, &ax25_uid_list) { - if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) - break; - } - if (ax25_uid == NULL) { - write_unlock(&ax25_uid_lock); - return -ENOENT; - } - hlist_del_init(&ax25_uid->uid_node); - ax25_uid_put(ax25_uid); - write_unlock(&ax25_uid_lock); - - return 0; - - default: - return -EINVAL; - } - - return -EINVAL; /*NOTREACHED */ -} - -#ifdef CONFIG_PROC_FS - -static void *ax25_uid_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(ax25_uid_lock) -{ - read_lock(&ax25_uid_lock); - return seq_hlist_start_head(&ax25_uid_list, *pos); -} - -static void *ax25_uid_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &ax25_uid_list, pos); -} - -static void ax25_uid_seq_stop(struct seq_file *seq, void *v) - __releases(ax25_uid_lock) -{ - read_unlock(&ax25_uid_lock); -} - -static int ax25_uid_seq_show(struct seq_file *seq, void *v) -{ - char buf[11]; - - if (v == SEQ_START_TOKEN) - seq_printf(seq, "Policy: %d\n", ax25_uid_policy); - else { - struct ax25_uid_assoc *pt; - - pt = hlist_entry(v, struct ax25_uid_assoc, uid_node); - seq_printf(seq, "%6d %s\n", - from_kuid_munged(seq_user_ns(seq), pt->uid), - ax2asc(buf, &pt->call)); - } - return 0; -} - -const struct seq_operations ax25_uid_seqops = { - .start = ax25_uid_seq_start, - .next = ax25_uid_seq_next, - .stop = ax25_uid_seq_stop, - .show = ax25_uid_seq_show, -}; -#endif - -/* - * Free all memory associated with UID/Callsign structures. - */ -void __exit ax25_uid_free(void) -{ - ax25_uid_assoc *ax25_uid; - - write_lock(&ax25_uid_lock); -again: - ax25_uid_for_each(ax25_uid, &ax25_uid_list) { - hlist_del_init(&ax25_uid->uid_node); - ax25_uid_put(ax25_uid); - goto again; - } - write_unlock(&ax25_uid_lock); -} diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c deleted file mode 100644 index 68753aa30334..000000000000 --- a/net/ax25/sysctl_net_ax25.c +++ /dev/null @@ -1,181 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com) - */ -#include -#include -#include -#include -#include - -static int min_ipdefmode[1], max_ipdefmode[] = {1}; -static int min_axdefmode[1], max_axdefmode[] = {1}; -static int min_backoff[1], max_backoff[] = {2}; -static int min_conmode[1], max_conmode[] = {2}; -static int min_window[] = {1}, max_window[] = {7}; -static int min_ewindow[] = {1}, max_ewindow[] = {63}; -static int min_t1[] = {1}, max_t1[] = {30000}; -static int min_t2[] = {1}, max_t2[] = {20000}; -static int min_t3[1], max_t3[] = {3600000}; -static int min_idle[1], max_idle[] = {65535000}; -static int min_n2[] = {1}, max_n2[] = {31}; -static int min_paclen[] = {1}, max_paclen[] = {512}; -static int min_proto[1], max_proto[] = { AX25_PROTO_MAX }; -#ifdef CONFIG_AX25_DAMA_SLAVE -static int min_ds_timeout[1], max_ds_timeout[] = {65535000}; -#endif - -static const struct ctl_table ax25_param_table[] = { - { - .procname = "ip_default_mode", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_ipdefmode, - .extra2 = &max_ipdefmode - }, - { - .procname = "ax25_default_mode", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_axdefmode, - .extra2 = &max_axdefmode - }, - { - .procname = "backoff_type", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_backoff, - .extra2 = &max_backoff - }, - { - .procname = "connect_mode", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_conmode, - .extra2 = &max_conmode - }, - { - .procname = "standard_window_size", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_window, - .extra2 = &max_window - }, - { - .procname = "extended_window_size", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_ewindow, - .extra2 = &max_ewindow - }, - { - .procname = "t1_timeout", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_t1, - .extra2 = &max_t1 - }, - { - .procname = "t2_timeout", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_t2, - .extra2 = &max_t2 - }, - { - .procname = "t3_timeout", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_t3, - .extra2 = &max_t3 - }, - { - .procname = "idle_timeout", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_idle, - .extra2 = &max_idle - }, - { - .procname = "maximum_retry_count", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_n2, - .extra2 = &max_n2 - }, - { - .procname = "maximum_packet_length", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_paclen, - .extra2 = &max_paclen - }, - { - .procname = "protocol", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_proto, - .extra2 = &max_proto - }, -#ifdef CONFIG_AX25_DAMA_SLAVE - { - .procname = "dama_slave_timeout", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_ds_timeout, - .extra2 = &max_ds_timeout - }, -#endif -}; - -int ax25_register_dev_sysctl(ax25_dev *ax25_dev) -{ - char path[sizeof("net/ax25/") + IFNAMSIZ]; - int k; - struct ctl_table *table; - - table = kmemdup(ax25_param_table, sizeof(ax25_param_table), GFP_KERNEL); - if (!table) - return -ENOMEM; - - BUILD_BUG_ON(ARRAY_SIZE(ax25_param_table) != AX25_MAX_VALUES); - for (k = 0; k < AX25_MAX_VALUES; k++) - table[k].data = &ax25_dev->values[k]; - - snprintf(path, sizeof(path), "net/ax25/%s", ax25_dev->dev->name); - ax25_dev->sysheader = register_net_sysctl_sz(&init_net, path, table, - ARRAY_SIZE(ax25_param_table)); - if (!ax25_dev->sysheader) { - kfree(table); - return -ENOMEM; - } - return 0; -} - -void ax25_unregister_dev_sysctl(ax25_dev *ax25_dev) -{ - struct ctl_table_header *header = ax25_dev->sysheader; - const struct ctl_table *table; - - if (header) { - ax25_dev->sysheader = NULL; - table = header->ctl_table_arg; - unregister_net_sysctl_table(header); - kfree(table); - } -} diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index f28e9cbf8ad5..74ef7dc2b2f9 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -173,19 +173,12 @@ free_orig_node_hash: static struct batadv_neigh_node * batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, const u8 *neigh_addr, - struct batadv_orig_node *orig_node, - struct batadv_orig_node *orig_neigh) + struct batadv_orig_node *orig_node) { struct batadv_neigh_node *neigh_node; neigh_node = batadv_neigh_node_get_or_create(orig_node, hard_iface, neigh_addr); - if (!neigh_node) - goto out; - - neigh_node->orig_node = orig_neigh; - -out: return neigh_node; } @@ -335,7 +328,7 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet, struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); const char *fwd_str; u8 packet_num; - s16 buff_pos; + int buff_pos; struct batadv_ogm_packet *batadv_ogm_packet; struct sk_buff *skb; u8 *packet_pos; @@ -906,6 +899,31 @@ static u8 batadv_iv_orig_ifinfo_sum(struct batadv_orig_node *orig_node, return sum; } +/** + * batadv_iv_ogm_neigh_ifinfo_sum() - Get bcast_own sum for a last-hop neighbor + * @bat_priv: the bat priv with all the mesh interface information + * @neigh_node: last-hop neighbor of an originator + * + * Return: Number of replied (rebroadcasted) OGMs for the originator currently + * announced by the neighbor. Returns 0 if the neighbor's originator entry is + * not available anymore. + */ +static u8 batadv_iv_ogm_neigh_ifinfo_sum(struct batadv_priv *bat_priv, + const struct batadv_neigh_node *neigh_node) +{ + struct batadv_orig_node *orig_neigh; + u8 sum; + + orig_neigh = batadv_orig_hash_find(bat_priv, neigh_node->addr); + if (!orig_neigh) + return 0; + + sum = batadv_iv_orig_ifinfo_sum(orig_neigh, neigh_node->if_incoming); + batadv_orig_node_put(orig_neigh); + + return sum; +} + /** * batadv_iv_ogm_orig_update() - use OGM to update corresponding data in an * originator @@ -975,17 +993,9 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, } if (!neigh_node) { - struct batadv_orig_node *orig_tmp; - - orig_tmp = batadv_iv_ogm_orig_get(bat_priv, ethhdr->h_source); - if (!orig_tmp) - goto unlock; - neigh_node = batadv_iv_ogm_neigh_new(if_incoming, ethhdr->h_source, - orig_node, orig_tmp); - - batadv_orig_node_put(orig_tmp); + orig_node); if (!neigh_node) goto unlock; } else { @@ -1037,10 +1047,9 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, */ if (router_ifinfo && neigh_ifinfo->bat_iv.tq_avg == router_ifinfo->bat_iv.tq_avg) { - sum_orig = batadv_iv_orig_ifinfo_sum(router->orig_node, - router->if_incoming); - sum_neigh = batadv_iv_orig_ifinfo_sum(neigh_node->orig_node, - neigh_node->if_incoming); + sum_orig = batadv_iv_ogm_neigh_ifinfo_sum(bat_priv, router); + sum_neigh = batadv_iv_ogm_neigh_ifinfo_sum(bat_priv, + neigh_node); if (sum_orig >= sum_neigh) goto out; } @@ -1106,7 +1115,6 @@ static bool batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, if (!neigh_node) neigh_node = batadv_iv_ogm_neigh_new(if_incoming, orig_neigh_node->orig, - orig_neigh_node, orig_neigh_node); if (!neigh_node) @@ -1302,6 +1310,32 @@ out: return ret; } +/** + * batadv_orig_to_direct_router() - get direct next hop neighbor to an orig address + * @bat_priv: the bat priv with all the mesh interface information + * @orig_addr: the originator MAC address to search the best next hop router for + * @if_outgoing: the interface where the OGM should be sent to + * + * Return: A neighbor node which is the best router towards the given originator + * address. Bonding candidates are ignored. + */ +static struct batadv_neigh_node * +batadv_orig_to_direct_router(struct batadv_priv *bat_priv, u8 *orig_addr, + struct batadv_hard_iface *if_outgoing) +{ + struct batadv_neigh_node *neigh_node; + struct batadv_orig_node *orig_node; + + orig_node = batadv_orig_hash_find(bat_priv, orig_addr); + if (!orig_node) + return NULL; + + neigh_node = batadv_orig_router_get(orig_node, if_outgoing); + batadv_orig_node_put(orig_node); + + return neigh_node; +} + /** * batadv_iv_ogm_process_per_outif() - process a batman iv OGM for an outgoing * interface @@ -1372,8 +1406,9 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset, router = batadv_orig_router_get(orig_node, if_outgoing); if (router) { - router_router = batadv_orig_router_get(router->orig_node, - if_outgoing); + router_router = batadv_orig_to_direct_router(bat_priv, + router->addr, + if_outgoing); router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing); } diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 51fe028b9088..cec11f1251d6 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -318,8 +318,8 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw) if (claim->backbone_gw != backbone_gw) continue; - batadv_claim_put(claim); hlist_del_rcu(&claim->hash_entry); + batadv_claim_put(claim); } spin_unlock_bh(list_lock); } @@ -723,6 +723,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, if (unlikely(hash_added != 0)) { /* only local changes happened. */ + batadv_backbone_gw_put(backbone_gw); kfree(claim); return; } @@ -1288,6 +1289,13 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, rcu_read_lock(); hlist_for_each_entry_rcu(claim, head, hash_entry) { + /* only purge claims not currently in the process of being released. + * Such claims could otherwise have a NULL-ptr backbone_gw set because + * they already went through batadv_claim_release() + */ + if (!kref_get_unless_zero(&claim->refcount)) + continue; + backbone_gw = batadv_bla_claim_get_backbone_gw(claim); if (now) goto purge_now; @@ -1313,6 +1321,7 @@ purge_now: claim->addr, claim->vid); skip: batadv_backbone_gw_put(backbone_gw); + batadv_claim_put(claim); } rcu_read_unlock(); } diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 3a35aadd8b41..a4d33ee0fda5 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -249,6 +249,7 @@ void batadv_mesh_free(struct net_device *mesh_iface) atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); batadv_purge_outstanding_packets(bat_priv, NULL); + batadv_tp_stop_all(bat_priv); batadv_gw_node_free(bat_priv); diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 2e42f6b348c8..066c76113fc4 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -365,23 +366,38 @@ static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars) } /** - * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer - * @bat_priv: the bat priv with all the mesh interface information - * @tp_vars: the private data of the current TP meter session to cleanup + * batadv_tp_list_detach() - remove tp session from mesh session list once + * @tp_vars: the private data of the current TP meter session */ -static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv, - struct batadv_tp_vars *tp_vars) +static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars) { - cancel_delayed_work(&tp_vars->finish_work); + bool detached = false; spin_lock_bh(&tp_vars->bat_priv->tp_list_lock); - hlist_del_rcu(&tp_vars->list); + if (!hlist_unhashed(&tp_vars->list)) { + hlist_del_init_rcu(&tp_vars->list); + detached = true; + } spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock); + if (!detached) + return; + + atomic_dec(&tp_vars->bat_priv->tp_num); + /* drop list reference */ batadv_tp_vars_put(tp_vars); +} - atomic_dec(&tp_vars->bat_priv->tp_num); +/** + * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer + * @tp_vars: the private data of the current TP meter session to cleanup + */ +static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars) +{ + cancel_delayed_work_sync(&tp_vars->finish_work); + + batadv_tp_list_detach(tp_vars); /* kill the timer and remove its reference */ timer_delete_sync(&tp_vars->timer); @@ -886,7 +902,8 @@ out: batadv_orig_node_put(orig_node); batadv_tp_sender_end(bat_priv, tp_vars); - batadv_tp_sender_cleanup(bat_priv, tp_vars); + batadv_tp_sender_cleanup(tp_vars); + complete(&tp_vars->finished); batadv_tp_vars_put(tp_vars); @@ -918,7 +935,8 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars) batadv_tp_vars_put(tp_vars); /* cleanup of failed tp meter variables */ - batadv_tp_sender_cleanup(bat_priv, tp_vars); + batadv_tp_sender_cleanup(tp_vars); + complete(&tp_vars->finished); return; } @@ -947,6 +965,13 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, /* look for an already existing test towards this node */ spin_lock_bh(&bat_priv->tp_list_lock); + if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) { + spin_unlock_bh(&bat_priv->tp_list_lock); + batadv_tp_batctl_error_notify(BATADV_TP_REASON_DST_UNREACHABLE, + dst, bat_priv, session_cookie); + return; + } + tp_vars = batadv_tp_list_find(bat_priv, dst); if (tp_vars) { spin_unlock_bh(&bat_priv->tp_list_lock); @@ -969,6 +994,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, tp_vars = kmalloc_obj(*tp_vars, GFP_ATOMIC); if (!tp_vars) { + atomic_dec(&bat_priv->tp_num); spin_unlock_bh(&bat_priv->tp_list_lock); batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: %s cannot allocate list elements\n", @@ -1017,6 +1043,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, tp_vars->start_time = jiffies; init_waitqueue_head(&tp_vars->more_bytes); + init_completion(&tp_vars->finished); spin_lock_init(&tp_vars->unacked_lock); INIT_LIST_HEAD(&tp_vars->unacked_list); @@ -1119,14 +1146,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) "Shutting down for inactivity (more than %dms) from %pM\n", BATADV_TP_RECV_TIMEOUT, tp_vars->other_end); - spin_lock_bh(&tp_vars->bat_priv->tp_list_lock); - hlist_del_rcu(&tp_vars->list); - spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock); - - /* drop list reference */ - batadv_tp_vars_put(tp_vars); - - atomic_dec(&bat_priv->tp_num); + batadv_tp_list_detach(tp_vars); spin_lock_bh(&tp_vars->unacked_lock); list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { @@ -1329,9 +1349,12 @@ static struct batadv_tp_vars * batadv_tp_init_recv(struct batadv_priv *bat_priv, const struct batadv_icmp_tp_packet *icmp) { - struct batadv_tp_vars *tp_vars; + struct batadv_tp_vars *tp_vars = NULL; spin_lock_bh(&bat_priv->tp_list_lock); + if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) + goto out_unlock; + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, icmp->session); if (tp_vars) @@ -1344,8 +1367,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, } tp_vars = kmalloc_obj(*tp_vars, GFP_ATOMIC); - if (!tp_vars) + if (!tp_vars) { + atomic_dec(&bat_priv->tp_num); goto out_unlock; + } ether_addr_copy(tp_vars->other_end, icmp->orig); tp_vars->role = BATADV_TP_RECEIVER; @@ -1464,6 +1489,9 @@ void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb) { struct batadv_icmp_tp_packet *icmp; + if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) + goto out; + icmp = (struct batadv_icmp_tp_packet *)skb->data; switch (icmp->subtype) { @@ -1478,9 +1506,57 @@ void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb) "Received unknown TP Metric packet type %u\n", icmp->subtype); } + +out: consume_skb(skb); } +/** + * batadv_tp_stop_all() - stop all currently running tp meter sessions + * @bat_priv: the bat priv with all the mesh interface information + */ +void batadv_tp_stop_all(struct batadv_priv *bat_priv) +{ + struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM]; + struct batadv_tp_vars *tp_var; + size_t count = 0; + size_t i; + + spin_lock_bh(&bat_priv->tp_list_lock); + hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) { + if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM)) + break; + + if (!kref_get_unless_zero(&tp_var->refcount)) + continue; + + tp_vars[count++] = tp_var; + } + spin_unlock_bh(&bat_priv->tp_list_lock); + + for (i = 0; i < count; i++) { + tp_var = tp_vars[i]; + + switch (tp_var->role) { + case BATADV_TP_SENDER: + batadv_tp_sender_shutdown(tp_var, + BATADV_TP_REASON_CANCEL); + wake_up(&tp_var->more_bytes); + wait_for_completion(&tp_var->finished); + break; + case BATADV_TP_RECEIVER: + batadv_tp_list_detach(tp_var); + if (timer_shutdown_sync(&tp_var->timer)) + batadv_tp_vars_put(tp_var); + break; + } + + batadv_tp_vars_put(tp_var); + } + + synchronize_net(); +} + /** * batadv_tp_meter_init() - initialize global tp_meter structures */ diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h index f0046d366eac..4e97cd10cd02 100644 --- a/net/batman-adv/tp_meter.h +++ b/net/batman-adv/tp_meter.h @@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, u32 test_length, u32 *cookie); void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst, u8 return_value); +void batadv_tp_stop_all(struct batadv_priv *bat_priv); void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb); #endif /* _NET_BATMAN_ADV_TP_METER_H_ */ diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 8fc5fe0e9b05..daa06f421154 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1328,6 +1329,9 @@ struct batadv_tp_vars { /** @finish_work: work item for the finishing procedure */ struct delayed_work finish_work; + /** @finished: completion signaled when a sender thread exits */ + struct completion finished; + /** @test_length: test length in milliseconds */ u32 test_length; diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 6b2b65a66700..ee6457d1a5ee 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -33,7 +33,6 @@ menuconfig BT HCI Device drivers (Interface to the hardware) RFCOMM Module (RFCOMM Protocol) BNEP Module (Bluetooth Network Encapsulation Protocol) - CMTP Module (CAPI Message Transport Protocol) HIDP Module (Human Interface Device Protocol) Say Y here to compile Bluetooth support into the kernel or say M to @@ -58,8 +57,6 @@ source "net/bluetooth/rfcomm/Kconfig" source "net/bluetooth/bnep/Kconfig" -source "net/bluetooth/cmtp/Kconfig" - source "net/bluetooth/hidp/Kconfig" config BT_LE diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index a7eede7616d8..41049b280887 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_BT) += bluetooth.o obj-$(CONFIG_BT_RFCOMM) += rfcomm/ obj-$(CONFIG_BT_BNEP) += bnep/ -obj-$(CONFIG_BT_CMTP) += cmtp/ obj-$(CONFIG_BT_HIDP) += hidp/ obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index d44987d4515c..853c8d7644b5 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -330,11 +330,18 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb) goto badframe; break; case BNEP_FILTER_MULTI_ADDR_SET: - case BNEP_FILTER_NET_TYPE_SET: - /* Pull: ctrl type (1 b), len (2 b), data (len bytes) */ - if (!skb_pull(skb, 3 + *(u16 *)(skb->data + 1) * 2)) + case BNEP_FILTER_NET_TYPE_SET: { + u8 *hdr; + + /* Pull ctrl type (1 b) + len (2 b) */ + hdr = skb_pull_data(skb, 3); + if (!hdr) + goto badframe; + /* Pull data (len bytes); length is big-endian */ + if (!skb_pull(skb, get_unaligned_be16(&hdr[1]))) goto badframe; break; + } default: kfree_skb(skb); return 0; diff --git a/net/bluetooth/cmtp/Kconfig b/net/bluetooth/cmtp/Kconfig deleted file mode 100644 index 34e923466236..000000000000 --- a/net/bluetooth/cmtp/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config BT_CMTP - tristate "CMTP protocol support (DEPRECATED)" - depends on BT_BREDR && ISDN_CAPI && DEPRECATED - help - CMTP (CAPI Message Transport Protocol) is a transport layer - for CAPI messages. CMTP is required for the Bluetooth Common - ISDN Access Profile. - - Say Y here to compile CMTP support into the kernel or say M to - compile it as module (cmtp). - diff --git a/net/bluetooth/cmtp/Makefile b/net/bluetooth/cmtp/Makefile deleted file mode 100644 index b2262ca97499..000000000000 --- a/net/bluetooth/cmtp/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Linux Bluetooth CMTP layer -# - -obj-$(CONFIG_BT_CMTP) += cmtp.o - -cmtp-objs := core.o sock.o capi.o diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c deleted file mode 100644 index b95413bffa16..000000000000 --- a/net/bluetooth/cmtp/capi.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "cmtp.h" - -#define CAPI_INTEROPERABILITY 0x20 - -#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ) -#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF) -#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND) -#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP) - -#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2) -#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4) -#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2) -#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2) - -#define CAPI_FUNCTION_REGISTER 0 -#define CAPI_FUNCTION_RELEASE 1 -#define CAPI_FUNCTION_GET_PROFILE 2 -#define CAPI_FUNCTION_GET_MANUFACTURER 3 -#define CAPI_FUNCTION_GET_VERSION 4 -#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5 -#define CAPI_FUNCTION_MANUFACTURER 6 -#define CAPI_FUNCTION_LOOPBACK 7 - - -#define CMTP_MSGNUM 1 -#define CMTP_APPLID 2 -#define CMTP_MAPPING 3 - -static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl) -{ - struct cmtp_application *app = kzalloc_obj(*app); - - BT_DBG("session %p application %p appl %u", session, app, appl); - - if (!app) - return NULL; - - app->state = BT_OPEN; - app->appl = appl; - - list_add_tail(&app->list, &session->applications); - - return app; -} - -static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app) -{ - BT_DBG("session %p application %p", session, app); - - if (app) { - list_del(&app->list); - kfree(app); - } -} - -static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value) -{ - struct cmtp_application *app; - - list_for_each_entry(app, &session->applications, list) { - switch (pattern) { - case CMTP_MSGNUM: - if (app->msgnum == value) - return app; - break; - case CMTP_APPLID: - if (app->appl == value) - return app; - break; - case CMTP_MAPPING: - if (app->mapping == value) - return app; - break; - } - } - - return NULL; -} - -static int cmtp_msgnum_get(struct cmtp_session *session) -{ - session->msgnum++; - - if ((session->msgnum & 0xff) > 200) - session->msgnum = CMTP_INITIAL_MSGNUM + 1; - - return session->msgnum; -} - -static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb) -{ - struct cmtp_scb *scb = (void *) skb->cb; - - BT_DBG("session %p skb %p len %u", session, skb, skb->len); - - scb->id = -1; - scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3); - - skb_queue_tail(&session->transmit, skb); - - wake_up_interruptible(sk_sleep(session->sock->sk)); -} - -static void cmtp_send_interopmsg(struct cmtp_session *session, - __u8 subcmd, __u16 appl, __u16 msgnum, - __u16 function, unsigned char *buf, int len) -{ - struct sk_buff *skb; - unsigned char *s; - - BT_DBG("session %p subcmd 0x%02x appl %u msgnum %u", session, subcmd, appl, msgnum); - - skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC); - if (!skb) { - BT_ERR("Can't allocate memory for interoperability packet"); - return; - } - - s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len); - - capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len); - capimsg_setu16(s, 2, appl); - capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY); - capimsg_setu8 (s, 5, subcmd); - capimsg_setu16(s, 6, msgnum); - - /* Interoperability selector (Bluetooth Device Management) */ - capimsg_setu16(s, 8, 0x0001); - - capimsg_setu8 (s, 10, 3 + len); - capimsg_setu16(s, 11, function); - capimsg_setu8 (s, 13, len); - - if (len > 0) - memcpy(s + 14, buf, len); - - cmtp_send_capimsg(session, skb); -} - -static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb) -{ - struct capi_ctr *ctrl = &session->ctrl; - struct cmtp_application *application; - __u16 appl, msgnum, func, info; - __u32 controller; - - BT_DBG("session %p skb %p len %u", session, skb, skb->len); - - switch (CAPIMSG_SUBCOMMAND(skb->data)) { - case CAPI_CONF: - if (skb->len < CAPI_MSG_BASELEN + 10) - break; - - func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5); - info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8); - - switch (func) { - case CAPI_FUNCTION_REGISTER: - msgnum = CAPIMSG_MSGID(skb->data); - - application = cmtp_application_get(session, CMTP_MSGNUM, msgnum); - if (application) { - application->state = BT_CONNECTED; - application->msgnum = 0; - application->mapping = CAPIMSG_APPID(skb->data); - wake_up_interruptible(&session->wait); - } - - break; - - case CAPI_FUNCTION_RELEASE: - appl = CAPIMSG_APPID(skb->data); - - application = cmtp_application_get(session, CMTP_MAPPING, appl); - if (application) { - application->state = BT_CLOSED; - application->msgnum = 0; - wake_up_interruptible(&session->wait); - } - - break; - - case CAPI_FUNCTION_GET_PROFILE: - if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile)) - break; - - controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11); - msgnum = CAPIMSG_MSGID(skb->data); - - if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) { - session->ncontroller = controller; - wake_up_interruptible(&session->wait); - break; - } - - if (!info && ctrl) { - memcpy(&ctrl->profile, - skb->data + CAPI_MSG_BASELEN + 11, - sizeof(capi_profile)); - session->state = BT_CONNECTED; - capi_ctr_ready(ctrl); - } - - break; - - case CAPI_FUNCTION_GET_MANUFACTURER: - if (!info && ctrl && skb->len > CAPI_MSG_BASELEN + 14) - strscpy_pad(ctrl->manu, - skb->data + CAPI_MSG_BASELEN + 15, - skb->data[CAPI_MSG_BASELEN + 14]); - break; - - case CAPI_FUNCTION_GET_VERSION: - if (skb->len < CAPI_MSG_BASELEN + 32) - break; - - if (!info && ctrl) { - ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16); - ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20); - ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24); - ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28); - } - - break; - - case CAPI_FUNCTION_GET_SERIAL_NUMBER: - if (!info && ctrl && skb->len > CAPI_MSG_BASELEN + 16) - strscpy_pad(ctrl->serial, - skb->data + CAPI_MSG_BASELEN + 17, - skb->data[CAPI_MSG_BASELEN + 16]); - break; - } - - break; - - case CAPI_IND: - if (skb->len < CAPI_MSG_BASELEN + 6) - break; - - func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3); - - if (func == CAPI_FUNCTION_LOOPBACK) { - int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6, - skb->data[CAPI_MSG_BASELEN + 5]); - appl = CAPIMSG_APPID(skb->data); - msgnum = CAPIMSG_MSGID(skb->data); - cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func, - skb->data + CAPI_MSG_BASELEN + 6, len); - } - - break; - } - - kfree_skb(skb); -} - -void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) -{ - struct capi_ctr *ctrl = &session->ctrl; - struct cmtp_application *application; - __u16 appl; - __u32 contr; - - BT_DBG("session %p skb %p len %u", session, skb, skb->len); - - if (skb->len < CAPI_MSG_BASELEN) - return; - - if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) { - cmtp_recv_interopmsg(session, skb); - return; - } - - if (session->flags & BIT(CMTP_LOOPBACK)) { - kfree_skb(skb); - return; - } - - appl = CAPIMSG_APPID(skb->data); - contr = CAPIMSG_CONTROL(skb->data); - - application = cmtp_application_get(session, CMTP_MAPPING, appl); - if (application) { - appl = application->appl; - CAPIMSG_SETAPPID(skb->data, appl); - } else { - BT_ERR("Can't find application with id %u", appl); - kfree_skb(skb); - return; - } - - if ((contr & 0x7f) == 0x01) { - contr = (contr & 0xffffff80) | session->num; - CAPIMSG_SETCONTROL(skb->data, contr); - } - - capi_ctr_handle_message(ctrl, appl, skb); -} - -static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) -{ - BT_DBG("ctrl %p data %p", ctrl, data); - - return 0; -} - -static void cmtp_reset_ctr(struct capi_ctr *ctrl) -{ - struct cmtp_session *session = ctrl->driverdata; - - BT_DBG("ctrl %p", ctrl); - - capi_ctr_down(ctrl); - - atomic_inc(&session->terminate); - wake_up_process(session->task); -} - -static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp) -{ - DECLARE_WAITQUEUE(wait, current); - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *application; - unsigned long timeo = CMTP_INTEROP_TIMEOUT; - unsigned char buf[8]; - int err = 0, nconn, want = rp->level3cnt; - - BT_DBG("ctrl %p appl %u level3cnt %u datablkcnt %u datablklen %u", - ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen); - - application = cmtp_application_add(session, appl); - if (!application) { - BT_ERR("Can't allocate memory for new application"); - return; - } - - if (want < 0) - nconn = ctrl->profile.nbchannel * -want; - else - nconn = want; - - if (nconn == 0) - nconn = ctrl->profile.nbchannel; - - capimsg_setu16(buf, 0, nconn); - capimsg_setu16(buf, 2, rp->datablkcnt); - capimsg_setu16(buf, 4, rp->datablklen); - - application->state = BT_CONFIG; - application->msgnum = cmtp_msgnum_get(session); - - cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum, - CAPI_FUNCTION_REGISTER, buf, 6); - - add_wait_queue(&session->wait, &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (!timeo) { - err = -EAGAIN; - break; - } - - if (application->state == BT_CLOSED) { - err = -application->err; - break; - } - - if (application->state == BT_CONNECTED) - break; - - if (signal_pending(current)) { - err = -EINTR; - break; - } - - timeo = schedule_timeout(timeo); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&session->wait, &wait); - - if (err) { - cmtp_application_del(session, application); - return; - } -} - -static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl) -{ - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *application; - - BT_DBG("ctrl %p appl %u", ctrl, appl); - - application = cmtp_application_get(session, CMTP_APPLID, appl); - if (!application) { - BT_ERR("Can't find application"); - return; - } - - application->msgnum = cmtp_msgnum_get(session); - - cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum, - CAPI_FUNCTION_RELEASE, NULL, 0); - - wait_event_interruptible_timeout(session->wait, - (application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT); - - cmtp_application_del(session, application); -} - -static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) -{ - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *application; - __u16 appl; - __u32 contr; - - BT_DBG("ctrl %p skb %p", ctrl, skb); - - appl = CAPIMSG_APPID(skb->data); - contr = CAPIMSG_CONTROL(skb->data); - - application = cmtp_application_get(session, CMTP_APPLID, appl); - if ((!application) || (application->state != BT_CONNECTED)) { - BT_ERR("Can't find application with id %u", appl); - return CAPI_ILLAPPNR; - } - - CAPIMSG_SETAPPID(skb->data, application->mapping); - - if ((contr & 0x7f) == session->num) { - contr = (contr & 0xffffff80) | 0x01; - CAPIMSG_SETCONTROL(skb->data, contr); - } - - cmtp_send_capimsg(session, skb); - - return CAPI_NOERROR; -} - -static char *cmtp_procinfo(struct capi_ctr *ctrl) -{ - return "CAPI Message Transport Protocol"; -} - -static int cmtp_proc_show(struct seq_file *m, void *v) -{ - struct capi_ctr *ctrl = m->private; - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *app; - - seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl)); - seq_printf(m, "addr %s\n", session->name); - seq_printf(m, "ctrl %d\n", session->num); - - list_for_each_entry(app, &session->applications, list) { - seq_printf(m, "appl %u -> %u\n", app->appl, app->mapping); - } - - return 0; -} - -int cmtp_attach_device(struct cmtp_session *session) -{ - unsigned char buf[4]; - long ret; - - BT_DBG("session %p", session); - - capimsg_setu32(buf, 0, 0); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM, - CAPI_FUNCTION_GET_PROFILE, buf, 4); - - ret = wait_event_interruptible_timeout(session->wait, - session->ncontroller, CMTP_INTEROP_TIMEOUT); - - BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name); - - if (!ret) - return -ETIMEDOUT; - - if (!session->ncontroller) - return -ENODEV; - - if (session->ncontroller > 1) - BT_INFO("Setting up only CAPI controller 1"); - - session->ctrl.owner = THIS_MODULE; - session->ctrl.driverdata = session; - strcpy(session->ctrl.name, session->name); - - session->ctrl.driver_name = "cmtp"; - session->ctrl.load_firmware = cmtp_load_firmware; - session->ctrl.reset_ctr = cmtp_reset_ctr; - session->ctrl.register_appl = cmtp_register_appl; - session->ctrl.release_appl = cmtp_release_appl; - session->ctrl.send_message = cmtp_send_message; - - session->ctrl.procinfo = cmtp_procinfo; - session->ctrl.proc_show = cmtp_proc_show; - - if (attach_capi_ctr(&session->ctrl) < 0) { - BT_ERR("Can't attach new controller"); - return -EBUSY; - } - - session->num = session->ctrl.cnr; - - BT_DBG("session %p num %d", session, session->num); - - capimsg_setu32(buf, 0, 1); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_MANUFACTURER, buf, 4); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_VERSION, buf, 4); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_PROFILE, buf, 4); - - return 0; -} - -void cmtp_detach_device(struct cmtp_session *session) -{ - BT_DBG("session %p", session); - - detach_capi_ctr(&session->ctrl); -} diff --git a/net/bluetooth/cmtp/cmtp.h b/net/bluetooth/cmtp/cmtp.h deleted file mode 100644 index f6b9dc4e408f..000000000000 --- a/net/bluetooth/cmtp/cmtp.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#ifndef __CMTP_H -#define __CMTP_H - -#include -#include - -#define BTNAMSIZ 21 - -/* CMTP ioctl defines */ -#define CMTPCONNADD _IOW('C', 200, int) -#define CMTPCONNDEL _IOW('C', 201, int) -#define CMTPGETCONNLIST _IOR('C', 210, int) -#define CMTPGETCONNINFO _IOR('C', 211, int) - -#define CMTP_LOOPBACK 0 - -struct cmtp_connadd_req { - int sock; /* Connected socket */ - __u32 flags; -}; - -struct cmtp_conndel_req { - bdaddr_t bdaddr; - __u32 flags; -}; - -struct cmtp_conninfo { - bdaddr_t bdaddr; - __u32 flags; - __u16 state; - int num; -}; - -struct cmtp_connlist_req { - __u32 cnum; - struct cmtp_conninfo __user *ci; -}; - -int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock); -int cmtp_del_connection(struct cmtp_conndel_req *req); -int cmtp_get_connlist(struct cmtp_connlist_req *req); -int cmtp_get_conninfo(struct cmtp_conninfo *ci); - -/* CMTP session defines */ -#define CMTP_INTEROP_TIMEOUT (HZ * 5) -#define CMTP_INITIAL_MSGNUM 0xff00 - -struct cmtp_session { - struct list_head list; - - struct socket *sock; - - bdaddr_t bdaddr; - - unsigned long state; - unsigned long flags; - - uint mtu; - - char name[BTNAMSIZ]; - - atomic_t terminate; - struct task_struct *task; - - wait_queue_head_t wait; - - int ncontroller; - int num; - struct capi_ctr ctrl; - - struct list_head applications; - - unsigned long blockids; - int msgnum; - - struct sk_buff_head transmit; - - struct sk_buff *reassembly[16]; -}; - -struct cmtp_application { - struct list_head list; - - unsigned long state; - int err; - - __u16 appl; - __u16 mapping; - - __u16 msgnum; -}; - -struct cmtp_scb { - int id; - int data; -}; - -int cmtp_attach_device(struct cmtp_session *session); -void cmtp_detach_device(struct cmtp_session *session); - -void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb); - -/* CMTP init defines */ -int cmtp_init_sockets(void); -void cmtp_cleanup_sockets(void); - -#endif /* __CMTP_H */ diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c deleted file mode 100644 index 261aeeda3236..000000000000 --- a/net/bluetooth/cmtp/core.c +++ /dev/null @@ -1,519 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "cmtp.h" - -#define VERSION "1.0" - -static DECLARE_RWSEM(cmtp_session_sem); -static LIST_HEAD(cmtp_session_list); - -static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr) -{ - struct cmtp_session *session; - - BT_DBG(""); - - list_for_each_entry(session, &cmtp_session_list, list) - if (!bacmp(bdaddr, &session->bdaddr)) - return session; - - return NULL; -} - -static void __cmtp_link_session(struct cmtp_session *session) -{ - list_add(&session->list, &cmtp_session_list); -} - -static void __cmtp_unlink_session(struct cmtp_session *session) -{ - list_del(&session->list); -} - -static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci) -{ - u32 valid_flags = BIT(CMTP_LOOPBACK); - memset(ci, 0, sizeof(*ci)); - bacpy(&ci->bdaddr, &session->bdaddr); - - ci->flags = session->flags & valid_flags; - ci->state = session->state; - - ci->num = session->num; -} - - -static inline int cmtp_alloc_block_id(struct cmtp_session *session) -{ - int i, id = -1; - - for (i = 0; i < 16; i++) - if (!test_and_set_bit(i, &session->blockids)) { - id = i; - break; - } - - return id; -} - -static inline void cmtp_free_block_id(struct cmtp_session *session, int id) -{ - clear_bit(id, &session->blockids); -} - -static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count) -{ - struct sk_buff *skb = session->reassembly[id], *nskb; - int size; - - BT_DBG("session %p buf %p count %d", session, buf, count); - - size = (skb) ? skb->len + count : count; - - nskb = alloc_skb(size, GFP_ATOMIC); - if (!nskb) { - BT_ERR("Can't allocate memory for CAPI message"); - return; - } - - if (skb && (skb->len > 0)) - skb_copy_from_linear_data(skb, skb_put(nskb, skb->len), skb->len); - - skb_put_data(nskb, buf, count); - - session->reassembly[id] = nskb; - - kfree_skb(skb); -} - -static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb) -{ - __u8 hdr, hdrlen, id; - __u16 len; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - while (skb->len > 0) { - hdr = skb->data[0]; - - switch (hdr & 0xc0) { - case 0x40: - hdrlen = 2; - len = skb->data[1]; - break; - case 0x80: - hdrlen = 3; - len = skb->data[1] | (skb->data[2] << 8); - break; - default: - hdrlen = 1; - len = 0; - break; - } - - id = (hdr & 0x3c) >> 2; - - BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id); - - if (hdrlen + len > skb->len) { - BT_ERR("Wrong size or header information in CMTP frame"); - break; - } - - if (len == 0) { - skb_pull(skb, hdrlen); - continue; - } - - switch (hdr & 0x03) { - case 0x00: - cmtp_add_msgpart(session, id, skb->data + hdrlen, len); - cmtp_recv_capimsg(session, session->reassembly[id]); - session->reassembly[id] = NULL; - break; - case 0x01: - cmtp_add_msgpart(session, id, skb->data + hdrlen, len); - break; - default: - kfree_skb(session->reassembly[id]); - session->reassembly[id] = NULL; - break; - } - - skb_pull(skb, hdrlen + len); - } - - kfree_skb(skb); - return 0; -} - -static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len) -{ - struct socket *sock = session->sock; - struct kvec iv = { data, len }; - struct msghdr msg; - - BT_DBG("session %p data %p len %d", session, data, len); - - if (!len) - return 0; - - memset(&msg, 0, sizeof(msg)); - - return kernel_sendmsg(sock, &msg, &iv, 1, len); -} - -static void cmtp_process_transmit(struct cmtp_session *session) -{ - struct sk_buff *skb, *nskb; - unsigned char *hdr; - unsigned int size, tail; - - BT_DBG("session %p", session); - - nskb = alloc_skb(session->mtu, GFP_ATOMIC); - if (!nskb) { - BT_ERR("Can't allocate memory for new frame"); - return; - } - - while ((skb = skb_dequeue(&session->transmit))) { - struct cmtp_scb *scb = (void *) skb->cb; - - tail = session->mtu - nskb->len; - if (tail < 5) { - cmtp_send_frame(session, nskb->data, nskb->len); - skb_trim(nskb, 0); - tail = session->mtu; - } - - size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len); - - if (scb->id < 0) { - scb->id = cmtp_alloc_block_id(session); - if (scb->id < 0) { - skb_queue_head(&session->transmit, skb); - break; - } - } - - if (size < 256) { - hdr = skb_put(nskb, 2); - hdr[0] = 0x40 - | ((scb->id << 2) & 0x3c) - | ((skb->len == size) ? 0x00 : 0x01); - hdr[1] = size; - } else { - hdr = skb_put(nskb, 3); - hdr[0] = 0x80 - | ((scb->id << 2) & 0x3c) - | ((skb->len == size) ? 0x00 : 0x01); - hdr[1] = size & 0xff; - hdr[2] = size >> 8; - } - - skb_copy_from_linear_data(skb, skb_put(nskb, size), size); - skb_pull(skb, size); - - if (skb->len > 0) { - skb_queue_head(&session->transmit, skb); - } else { - cmtp_free_block_id(session, scb->id); - if (scb->data) { - cmtp_send_frame(session, nskb->data, nskb->len); - skb_trim(nskb, 0); - } - kfree_skb(skb); - } - } - - cmtp_send_frame(session, nskb->data, nskb->len); - - kfree_skb(nskb); -} - -static int cmtp_session(void *arg) -{ - struct cmtp_session *session = arg; - struct sock *sk = session->sock->sk; - struct sk_buff *skb; - DEFINE_WAIT_FUNC(wait, woken_wake_function); - - BT_DBG("session %p", session); - - set_user_nice(current, -15); - - add_wait_queue(sk_sleep(sk), &wait); - while (1) { - if (atomic_read(&session->terminate)) - break; - if (sk->sk_state != BT_CONNECTED) - break; - - while ((skb = skb_dequeue(&sk->sk_receive_queue))) { - skb_orphan(skb); - if (!skb_linearize(skb)) - cmtp_recv_frame(session, skb); - else - kfree_skb(skb); - } - - cmtp_process_transmit(session); - - /* - * wait_woken() performs the necessary memory barriers - * for us; see the header comment for this primitive. - */ - wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); - } - remove_wait_queue(sk_sleep(sk), &wait); - - down_write(&cmtp_session_sem); - - if (!(session->flags & BIT(CMTP_LOOPBACK))) - cmtp_detach_device(session); - - fput(session->sock->file); - - __cmtp_unlink_session(session); - - up_write(&cmtp_session_sem); - - kfree(session); - module_put_and_kthread_exit(0); - return 0; -} - -int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) -{ - u32 valid_flags = BIT(CMTP_LOOPBACK); - struct cmtp_session *session, *s; - int i, err; - - BT_DBG(""); - - if (!l2cap_is_socket(sock)) - return -EBADFD; - - if (req->flags & ~valid_flags) - return -EINVAL; - - session = kzalloc_obj(struct cmtp_session); - if (!session) - return -ENOMEM; - - down_write(&cmtp_session_sem); - - s = __cmtp_get_session(&l2cap_pi(sock->sk)->chan->dst); - if (s && s->state == BT_CONNECTED) { - err = -EEXIST; - goto failed; - } - - bacpy(&session->bdaddr, &l2cap_pi(sock->sk)->chan->dst); - - session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu, - l2cap_pi(sock->sk)->chan->imtu); - - BT_DBG("mtu %d", session->mtu); - - sprintf(session->name, "%pMR", &session->bdaddr); - - session->sock = sock; - session->state = BT_CONFIG; - - init_waitqueue_head(&session->wait); - - session->msgnum = CMTP_INITIAL_MSGNUM; - - INIT_LIST_HEAD(&session->applications); - - skb_queue_head_init(&session->transmit); - - for (i = 0; i < 16; i++) - session->reassembly[i] = NULL; - - session->flags = req->flags; - - __cmtp_link_session(session); - - __module_get(THIS_MODULE); - session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d", - session->num); - if (IS_ERR(session->task)) { - module_put(THIS_MODULE); - err = PTR_ERR(session->task); - goto unlink; - } - - if (!(session->flags & BIT(CMTP_LOOPBACK))) { - err = cmtp_attach_device(session); - if (err < 0) { - /* Caller will call fput in case of failure, and so - * will cmtp_session kthread. - */ - get_file(session->sock->file); - - atomic_inc(&session->terminate); - wake_up_interruptible(sk_sleep(session->sock->sk)); - up_write(&cmtp_session_sem); - return err; - } - } - - up_write(&cmtp_session_sem); - return 0; - -unlink: - __cmtp_unlink_session(session); - -failed: - up_write(&cmtp_session_sem); - kfree(session); - return err; -} - -int cmtp_del_connection(struct cmtp_conndel_req *req) -{ - u32 valid_flags = 0; - struct cmtp_session *session; - int err = 0; - - BT_DBG(""); - - if (req->flags & ~valid_flags) - return -EINVAL; - - down_read(&cmtp_session_sem); - - session = __cmtp_get_session(&req->bdaddr); - if (session) { - /* Flush the transmit queue */ - skb_queue_purge(&session->transmit); - - /* Stop session thread */ - atomic_inc(&session->terminate); - - /* - * See the comment preceding the call to wait_woken() - * in cmtp_session(). - */ - wake_up_interruptible(sk_sleep(session->sock->sk)); - } else - err = -ENOENT; - - up_read(&cmtp_session_sem); - return err; -} - -int cmtp_get_connlist(struct cmtp_connlist_req *req) -{ - struct cmtp_session *session; - int err = 0, n = 0; - - BT_DBG(""); - - down_read(&cmtp_session_sem); - - list_for_each_entry(session, &cmtp_session_list, list) { - struct cmtp_conninfo ci; - - __cmtp_copy_session(session, &ci); - - if (copy_to_user(req->ci, &ci, sizeof(ci))) { - err = -EFAULT; - break; - } - - if (++n >= req->cnum) - break; - - req->ci++; - } - req->cnum = n; - - up_read(&cmtp_session_sem); - return err; -} - -int cmtp_get_conninfo(struct cmtp_conninfo *ci) -{ - struct cmtp_session *session; - int err = 0; - - down_read(&cmtp_session_sem); - - session = __cmtp_get_session(&ci->bdaddr); - if (session) - __cmtp_copy_session(session, ci); - else - err = -ENOENT; - - up_read(&cmtp_session_sem); - return err; -} - - -static int __init cmtp_init(void) -{ - BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION); - - return cmtp_init_sockets(); -} - -static void __exit cmtp_exit(void) -{ - cmtp_cleanup_sockets(); -} - -module_init(cmtp_init); -module_exit(cmtp_exit); - -MODULE_AUTHOR("Marcel Holtmann "); -MODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("bt-proto-5"); diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c deleted file mode 100644 index 96d49d9fae96..000000000000 --- a/net/bluetooth/cmtp/sock.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -#include "cmtp.h" - -static struct bt_sock_list cmtp_sk_list = { - .lock = __RW_LOCK_UNLOCKED(cmtp_sk_list.lock) -}; - -static int cmtp_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!sk) - return 0; - - bt_sock_unlink(&cmtp_sk_list, sk); - - sock_orphan(sk); - sock_put(sk); - - return 0; -} - -static int do_cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, void __user *argp) -{ - struct cmtp_connadd_req ca; - struct cmtp_conndel_req cd; - struct cmtp_connlist_req cl; - struct cmtp_conninfo ci; - struct socket *nsock; - int err; - - BT_DBG("cmd %x arg %p", cmd, argp); - - switch (cmd) { - case CMTPCONNADD: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (copy_from_user(&ca, argp, sizeof(ca))) - return -EFAULT; - - nsock = sockfd_lookup(ca.sock, &err); - if (!nsock) - return err; - - if (nsock->sk->sk_state != BT_CONNECTED) { - sockfd_put(nsock); - return -EBADFD; - } - - err = cmtp_add_connection(&ca, nsock); - if (!err) { - if (copy_to_user(argp, &ca, sizeof(ca))) - err = -EFAULT; - } else - sockfd_put(nsock); - - return err; - - case CMTPCONNDEL: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (copy_from_user(&cd, argp, sizeof(cd))) - return -EFAULT; - - return cmtp_del_connection(&cd); - - case CMTPGETCONNLIST: - if (copy_from_user(&cl, argp, sizeof(cl))) - return -EFAULT; - - if (cl.cnum <= 0) - return -EINVAL; - - err = cmtp_get_connlist(&cl); - if (!err && copy_to_user(argp, &cl, sizeof(cl))) - return -EFAULT; - - return err; - - case CMTPGETCONNINFO: - if (copy_from_user(&ci, argp, sizeof(ci))) - return -EFAULT; - - err = cmtp_get_conninfo(&ci); - if (!err && copy_to_user(argp, &ci, sizeof(ci))) - return -EFAULT; - - return err; - } - - return -EINVAL; -} - -static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - return do_cmtp_sock_ioctl(sock, cmd, (void __user *)arg); -} - -#ifdef CONFIG_COMPAT -static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - void __user *argp = compat_ptr(arg); - if (cmd == CMTPGETCONNLIST) { - struct cmtp_connlist_req cl; - u32 __user *p = argp; - u32 uci; - int err; - - if (get_user(cl.cnum, p) || get_user(uci, p + 1)) - return -EFAULT; - - cl.ci = compat_ptr(uci); - - if (cl.cnum <= 0) - return -EINVAL; - - err = cmtp_get_connlist(&cl); - - if (!err && put_user(cl.cnum, p)) - err = -EFAULT; - - return err; - } - - return do_cmtp_sock_ioctl(sock, cmd, argp); -} -#endif - -static const struct proto_ops cmtp_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = cmtp_sock_release, - .ioctl = cmtp_sock_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = cmtp_sock_compat_ioctl, -#endif - .bind = sock_no_bind, - .getname = sock_no_getname, - .sendmsg = sock_no_sendmsg, - .recvmsg = sock_no_recvmsg, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - -static struct proto cmtp_proto = { - .name = "CMTP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct bt_sock) -}; - -static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &cmtp_proto, kern); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - - sock->ops = &cmtp_sock_ops; - - sock->state = SS_UNCONNECTED; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = protocol; - sk->sk_state = BT_OPEN; - - bt_sock_link(&cmtp_sk_list, sk); - - return 0; -} - -static const struct net_proto_family cmtp_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = cmtp_sock_create -}; - -int cmtp_init_sockets(void) -{ - int err; - - err = proto_register(&cmtp_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops); - if (err < 0) { - BT_ERR("Can't register CMTP socket"); - goto error; - } - - err = bt_procfs_init(&init_net, "cmtp", &cmtp_sk_list, NULL); - if (err < 0) { - BT_ERR("Failed to create CMTP proc file"); - bt_sock_unregister(BTPROTO_HIDP); - goto error; - } - - BT_INFO("CMTP socket layer initialized"); - - return 0; - -error: - proto_unregister(&cmtp_proto); - return err; -} - -void cmtp_cleanup_sockets(void) -{ - bt_procfs_cleanup(&init_net, "cmtp"); - bt_sock_unregister(BTPROTO_CMTP); - proto_unregister(&cmtp_proto); -} diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3a0592599086..17b46ad6a349 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -480,40 +480,107 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle) return hci_setup_sync_conn(conn, handle); } -u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, - u16 to_multiplier) +struct le_conn_update_data { + struct hci_conn *conn; + u16 min; + u16 max; + u16 latency; + u16 to_multiplier; +}; + +static int le_conn_update_sync(struct hci_dev *hdev, void *data) { - struct hci_dev *hdev = conn->hdev; + struct le_conn_update_data *d = data; + struct hci_conn *conn = d->conn; struct hci_conn_params *params; struct hci_cp_le_conn_update cp; + u16 timeout; + u8 store_hint; + int err; + /* Verify connection is still alive and read conn fields under + * the same lock to prevent a concurrent disconnect from freeing + * or reusing the connection while we build the HCI command. + */ + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) { + hci_dev_unlock(hdev); + return -ECANCELED; + } + + memset(&cp, 0, sizeof(cp)); + cp.handle = cpu_to_le16(conn->handle); + cp.conn_interval_min = cpu_to_le16(d->min); + cp.conn_interval_max = cpu_to_le16(d->max); + cp.conn_latency = cpu_to_le16(d->latency); + cp.supervision_timeout = cpu_to_le16(d->to_multiplier); + cp.min_ce_len = cpu_to_le16(0x0000); + cp.max_ce_len = cpu_to_le16(0x0000); + timeout = conn->conn_timeout; + + hci_dev_unlock(hdev); + + err = __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_CONN_UPDATE, + sizeof(cp), &cp, + HCI_EV_LE_CONN_UPDATE_COMPLETE, + timeout, NULL); + if (err) + return err; + + /* Update stored connection parameters after the controller has + * confirmed the update via the LE Connection Update Complete event. + */ hci_dev_lock(hdev); params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); if (params) { - params->conn_min_interval = min; - params->conn_max_interval = max; - params->conn_latency = latency; - params->supervision_timeout = to_multiplier; + params->conn_min_interval = d->min; + params->conn_max_interval = d->max; + params->conn_latency = d->latency; + params->supervision_timeout = d->to_multiplier; + store_hint = 0x01; + } else { + store_hint = 0x00; } hci_dev_unlock(hdev); - memset(&cp, 0, sizeof(cp)); - cp.handle = cpu_to_le16(conn->handle); - cp.conn_interval_min = cpu_to_le16(min); - cp.conn_interval_max = cpu_to_le16(max); - cp.conn_latency = cpu_to_le16(latency); - cp.supervision_timeout = cpu_to_le16(to_multiplier); - cp.min_ce_len = cpu_to_le16(0x0000); - cp.max_ce_len = cpu_to_le16(0x0000); + mgmt_new_conn_param(hdev, &conn->dst, conn->dst_type, store_hint, + d->min, d->max, d->latency, d->to_multiplier); - hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp); + return 0; +} - if (params) - return 0x01; +static void le_conn_update_complete(struct hci_dev *hdev, void *data, int err) +{ + struct le_conn_update_data *d = data; - return 0x00; + hci_conn_put(d->conn); + kfree(d); +} + +void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, + u16 to_multiplier) +{ + struct le_conn_update_data *d; + + d = kzalloc_obj(*d); + if (!d) + return; + + hci_conn_get(conn); + d->conn = conn; + d->min = min; + d->max = max; + d->latency = latency; + d->to_multiplier = to_multiplier; + + if (hci_cmd_sync_queue(conn->hdev, le_conn_update_sync, d, + le_conn_update_complete) < 0) { + hci_conn_put(conn); + kfree(d); + } } void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, @@ -2130,6 +2197,9 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; + if (!hci_conn_valid(hdev, conn)) + return -ECANCELED; + if (qos->bcast.out.phys == BIT(1)) flags |= MGMT_ADV_FLAG_SEC_2M; @@ -2204,11 +2274,24 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "conn %p", conn); + if (err == -ECANCELED) + goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) + goto unlock; + if (err) { bt_dev_err(hdev, "Unable to create BIG: %d", err); hci_connect_cfm(conn, err); hci_conn_del(conn); } + +unlock: + hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid, @@ -2336,10 +2419,11 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst, BT_BOUND, &data); /* Queue start periodic advertising and create BIG */ - err = hci_cmd_sync_queue(hdev, create_big_sync, conn, + err = hci_cmd_sync_queue(hdev, create_big_sync, hci_conn_get(conn), create_big_complete); if (err < 0) { hci_conn_drop(conn); + hci_conn_put(conn); return ERR_PTR(err); } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b2ee6b6a0f56..eea2f810aafa 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -7118,9 +7118,29 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data, continue; } - if (hci_conn_set_handle(conn, - __le16_to_cpu(ev->bis_handle[i++]))) + if (ev->num_bis <= i) { + bt_dev_err(hdev, + "Not enough BIS handles for BIG 0x%2.2x", + ev->handle); + ev->status = HCI_ERROR_UNSPECIFIED; + hci_connect_cfm(conn, ev->status); + hci_conn_del(conn); continue; + } + + if (hci_conn_set_handle(conn, + __le16_to_cpu(ev->bis_handle[i++]))) { + bt_dev_err(hdev, + "Failed to set BIS handle for BIG 0x%2.2x", + ev->handle); + /* Force error so BIG gets terminated as not all BIS + * could be connected. + */ + ev->status = HCI_ERROR_UNSPECIFIED; + hci_connect_cfm(conn, ev->status); + hci_conn_del(conn); + continue; + } conn->state = BT_CONNECTED; set_bit(HCI_CONN_BIG_CREATED, &conn->flags); @@ -7129,7 +7149,10 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data, hci_iso_setup_path(conn); } - if (!ev->status && !i) + /* If there is an unexpected error or if no BISes have been connected + * for the BIG, terminate it. + */ + if (ev->status == HCI_ERROR_UNSPECIFIED || (!ev->status && !i)) /* If no BISes have been connected for the BIG, * terminate. This is in case all bound connections * have been closed before the BIG creation @@ -7168,7 +7191,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data, clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags); conn->num_bis = 0; - memset(conn->bis, 0, sizeof(conn->num_bis)); + memset(conn->bis, 0, sizeof(conn->bis)); for (i = 0; i < ev->num_bis; i++) { u16 handle = le16_to_cpu(ev->bis[i]); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 7bcf8c5ceaee..976f91eeb745 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -1035,6 +1035,28 @@ static struct hidp_session *hidp_session_find(const bdaddr_t *bdaddr) return session; } +/* + * Consume session->conn: clear the member under hidp_session_sem, then + * l2cap_unregister_user() and l2cap_conn_put() the snapshot outside the + * sem. At most one caller wins; later callers see NULL and skip. The + * reference is the one hidp_session_new() took via l2cap_conn_get(). + */ +static void hidp_session_unregister_conn(struct hidp_session *session) +{ + struct l2cap_conn *conn; + + down_write(&hidp_session_sem); + conn = session->conn; + if (conn) + session->conn = NULL; + up_write(&hidp_session_sem); + + if (conn) { + l2cap_unregister_user(conn, &session->user); + l2cap_conn_put(conn); + } +} + /* * Start session synchronously * This starts a session thread and waits until initialization @@ -1311,8 +1333,7 @@ static int hidp_session_thread(void *arg) * Instead, this call has the same semantics as if user-space tried to * delete the session. */ - if (session->conn) - l2cap_unregister_user(session->conn, &session->user); + hidp_session_unregister_conn(session); hidp_session_put(session); @@ -1418,7 +1439,7 @@ int hidp_connection_del(struct hidp_conndel_req *req) HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, NULL, 0); else - l2cap_unregister_user(session->conn, &session->user); + hidp_session_unregister_conn(session); hidp_session_put(session); diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index be145e2736b7..7cb2864fe872 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -347,6 +347,7 @@ static int iso_connect_bis(struct sock *sk) return -EHOSTUNREACH; hci_dev_lock(hdev); + lock_sock(sk); if (!bis_capable(hdev)) { err = -EOPNOTSUPP; @@ -399,13 +400,9 @@ static int iso_connect_bis(struct sock *sk) goto unlock; } - lock_sock(sk); - err = iso_chan_add(conn, sk, NULL); - if (err) { - release_sock(sk); + if (err) goto unlock; - } /* Update source addr of the socket */ bacpy(&iso_pi(sk)->src, &hcon->src); @@ -421,9 +418,8 @@ static int iso_connect_bis(struct sock *sk) iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo)); } - release_sock(sk); - unlock: + release_sock(sk); hci_dev_unlock(hdev); hci_dev_put(hdev); return err; @@ -444,6 +440,7 @@ static int iso_connect_cis(struct sock *sk) return -EHOSTUNREACH; hci_dev_lock(hdev); + lock_sock(sk); if (!cis_central_capable(hdev)) { err = -EOPNOTSUPP; @@ -498,13 +495,9 @@ static int iso_connect_cis(struct sock *sk) goto unlock; } - lock_sock(sk); - err = iso_chan_add(conn, sk, NULL); - if (err) { - release_sock(sk); + if (err) goto unlock; - } /* Update source addr of the socket */ bacpy(&iso_pi(sk)->src, &hcon->src); @@ -520,9 +513,8 @@ static int iso_connect_cis(struct sock *sk) iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo)); } - release_sock(sk); - unlock: + release_sock(sk); hci_dev_unlock(hdev); hci_dev_put(hdev); return err; @@ -1193,7 +1185,7 @@ static int iso_sock_connect(struct socket *sock, struct sockaddr_unsized *addr, release_sock(sk); - if (bacmp(&iso_pi(sk)->dst, BDADDR_ANY)) + if (bacmp(&sa->iso_bdaddr, BDADDR_ANY)) err = iso_connect_cis(sk); else err = iso_connect_bis(sk); @@ -2256,8 +2248,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_LISTEN, iso_match_sid, ev1); if (sk && !ev1->status) { + lock_sock(sk); iso_pi(sk)->sync_handle = le16_to_cpu(ev1->handle); iso_pi(sk)->bc_sid = ev1->sid; + release_sock(sk); } goto done; @@ -2268,8 +2262,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) sk = iso_get_sock(hdev, &hdev->bdaddr, bdaddr, BT_LISTEN, iso_match_sid_past, ev1a); if (sk && !ev1a->status) { + lock_sock(sk); iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle); iso_pi(sk)->bc_sid = ev1a->sid; + release_sock(sk); } goto done; @@ -2296,27 +2292,35 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) ev2); if (sk) { - int err; - struct hci_conn *hcon = iso_pi(sk)->conn->hcon; + int err = 0; + bool big_sync; + struct hci_conn *hcon; + lock_sock(sk); + + hcon = iso_pi(sk)->conn->hcon; iso_pi(sk)->qos.bcast.encryption = ev2->encryption; if (ev2->num_bis < iso_pi(sk)->bc_num_bis) iso_pi(sk)->bc_num_bis = ev2->num_bis; - if (!test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) && - !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) { + big_sync = !test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) && + !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags); + + if (big_sync) err = hci_conn_big_create_sync(hdev, hcon, &iso_pi(sk)->qos, iso_pi(sk)->sync_handle, iso_pi(sk)->bc_num_bis, iso_pi(sk)->bc_bis); - if (err) { - bt_dev_err(hdev, "hci_le_big_create_sync: %d", - err); - sock_put(sk); - sk = NULL; - } + + release_sock(sk); + + if (big_sync && err) { + bt_dev_err(hdev, "hci_le_big_create_sync: %d", + err); + sock_put(sk); + sk = NULL; } } @@ -2370,8 +2374,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) if (!base || base_len > BASE_MAX_LENGTH) goto done; + lock_sock(sk); memcpy(iso_pi(sk)->base, base, base_len); iso_pi(sk)->base_len = base_len; + release_sock(sk); } else { /* This is a PA data fragment. Keep pa_data_len set to 0 * until all data has been reassembled. diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 77dec104a9c3..7701528f1167 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4706,16 +4706,8 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, sizeof(rsp), &rsp); - if (!err) { - u8 store_hint; - - store_hint = hci_le_conn_update(hcon, min, max, latency, - to_multiplier); - mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type, - store_hint, min, max, latency, - to_multiplier); - - } + if (!err) + hci_le_conn_update(hcon, min, max, latency, to_multiplier); return 0; } @@ -5428,7 +5420,7 @@ static inline int l2cap_ecred_reconf_req(struct l2cap_conn *conn, * configured, the MPS field may be less than the current MPS * of that channel. */ - if (chan[i]->remote_mps >= mps && i) { + if (chan[i]->remote_mps > mps && num_scid > 1) { BT_ERR("chan %p decreased MPS %u -> %u", chan[i], chan[i]->remote_mps, mps); result = L2CAP_RECONF_INVALID_MPS; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 71e8c1b45bce..cf590a67d364 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1498,6 +1498,9 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) { struct sock *sk, *parent = chan->data; + if (!parent) + return NULL; + lock_sock(parent); /* Check for backlog size */ @@ -1657,6 +1660,9 @@ static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state, { struct sock *sk = chan->data; + if (!sk) + return; + sk->sk_state = state; if (err) @@ -1758,6 +1764,9 @@ static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan) { struct sock *sk = chan->data; + if (!sk) + return 0; + return READ_ONCE(sk->sk_sndtimeo); } diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 611a9a94151e..d11bd5337d57 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1715,9 +1715,12 @@ static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk } if (pf && d->cfc) { - u8 credits = *(u8 *) skb->data; skb_pull(skb, 1); + u8 *credits = skb_pull_data(skb, 1); - d->tx_credits += credits; + if (!credits) + goto drop; + + d->tx_credits += *credits; if (d->tx_credits) clear_bit(RFCOMM_TX_THROTTLED, &d->flags); } diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 18826d4b9c0b..eba44525d41d 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -472,9 +472,13 @@ static struct sock *sco_get_sock_listen(bdaddr_t *src) sk1 = sk; } + sk = sk ? sk : sk1; + if (sk) + sock_hold(sk); + read_unlock(&sco_sk_list.lock); - return sk ? sk : sk1; + return sk; } static void sco_sock_destruct(struct sock *sk) @@ -515,11 +519,13 @@ static void sco_sock_kill(struct sock *sk) BT_DBG("sk %p state %d", sk, sk->sk_state); /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */ + lock_sock(sk); if (sco_pi(sk)->conn) { sco_conn_lock(sco_pi(sk)->conn); sco_pi(sk)->conn->sk = NULL; sco_conn_unlock(sco_pi(sk)->conn); } + release_sock(sk); /* Kill poor orphan */ bt_sock_unlink(&sco_sk_list, sk); @@ -1365,40 +1371,51 @@ static int sco_sock_release(struct socket *sock) static void sco_conn_ready(struct sco_conn *conn) { - struct sock *parent; - struct sock *sk = conn->sk; + struct sock *parent, *sk; + + sco_conn_lock(conn); + sk = sco_sock_hold(conn); + sco_conn_unlock(conn); BT_DBG("conn %p", conn); if (sk) { lock_sock(sk); - sco_sock_clear_timer(sk); - sk->sk_state = BT_CONNECTED; - sk->sk_state_change(sk); - release_sock(sk); - } else { - sco_conn_lock(conn); - if (!conn->hcon) { - sco_conn_unlock(conn); - return; + /* conn->sk may have become NULL if racing with sk close, but + * due to held hdev->lock, it can't become different sk. + */ + if (conn->sk) { + sco_sock_clear_timer(sk); + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); } + release_sock(sk); + sock_put(sk); + } else { + if (!conn->hcon) + return; + + lockdep_assert_held(&conn->hcon->hdev->lock); + parent = sco_get_sock_listen(&conn->hcon->src); - if (!parent) { - sco_conn_unlock(conn); + if (!parent) return; - } lock_sock(parent); + sco_conn_lock(conn); + + /* hdev->lock guarantees conn->sk == NULL still here */ + + if (parent->sk_state != BT_LISTEN) + goto release; + sk = sco_sock_alloc(sock_net(parent), NULL, BTPROTO_SCO, GFP_ATOMIC, 0); - if (!sk) { - release_sock(parent); - sco_conn_unlock(conn); - return; - } + if (!sk) + goto release; sco_sock_init(sk, parent); @@ -1417,9 +1434,10 @@ static void sco_conn_ready(struct sco_conn *conn) /* Wake up parent */ parent->sk_data_ready(parent); - release_sock(parent); - +release: sco_conn_unlock(conn); + release_sock(parent); + sock_put(parent); } } diff --git a/net/bridge/br.c b/net/bridge/br.c index c37e52e2f29a..a5e5b2db110e 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -464,10 +464,6 @@ static int __init br_init(void) brioctl_set(br_ioctl_stub); -#if IS_ENABLED(CONFIG_ATM_LANE) - br_fdb_test_addr_hook = br_fdb_test_addr; -#endif - #if IS_MODULE(CONFIG_BRIDGE_NETFILTER) pr_info("bridge: filtering via arp/ip/ip6tables is no longer available " "by default. Update your scripts to load br_netfilter if you " @@ -506,9 +502,6 @@ static void __exit br_deinit(void) rcu_barrier(); /* Wait for completion of call_rcu()'s */ br_nf_core_fini(); -#if IS_ENABLED(CONFIG_ATM_LANE) - br_fdb_test_addr_hook = NULL; -#endif br_fdb_fini(); } diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c index 0c8a06cdd46f..deb1ab1f24b0 100644 --- a/net/bridge/br_arp_nd_proxy.c +++ b/net/bridge/br_arp_nd_proxy.c @@ -201,11 +201,12 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, f = br_fdb_find_rcu(br, n->ha, vid); if (f) { + const struct net_bridge_port *dst = READ_ONCE(f->dst); bool replied = false; if ((p && (p->flags & BR_PROXYARP)) || - (f->dst && (f->dst->flags & BR_PROXYARP_WIFI)) || - br_is_neigh_suppress_enabled(f->dst, vid)) { + (dst && (dst->flags & BR_PROXYARP_WIFI)) || + br_is_neigh_suppress_enabled(dst, vid)) { if (!vid) br_arp_send(br, p, skb->dev, sip, tip, sha, n->ha, sha, 0, 0); @@ -469,9 +470,10 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, f = br_fdb_find_rcu(br, n->ha, vid); if (f) { + const struct net_bridge_port *dst = READ_ONCE(f->dst); bool replied = false; - if (br_is_neigh_suppress_enabled(f->dst, vid)) { + if (br_is_neigh_suppress_enabled(dst, vid)) { if (vid != 0) br_nd_send(br, p, skb, n, skb->vlan_proto, diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index e2c17f620f00..ac81e58d5f70 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -236,6 +236,7 @@ struct net_device *br_fdb_find_port(const struct net_device *br_dev, const unsigned char *addr, __u16 vid) { + const struct net_bridge_port *dst; struct net_bridge_fdb_entry *f; struct net_device *dev = NULL; struct net_bridge *br; @@ -248,8 +249,11 @@ struct net_device *br_fdb_find_port(const struct net_device *br_dev, br = netdev_priv(br_dev); rcu_read_lock(); f = br_fdb_find_rcu(br, addr, vid); - if (f && f->dst) - dev = f->dst->dev; + if (f) { + dst = READ_ONCE(f->dst); + if (dst) + dev = dst->dev; + } rcu_read_unlock(); return dev; @@ -346,7 +350,7 @@ static void fdb_delete_local(struct net_bridge *br, vg = nbp_vlan_group(op); if (op != p && ether_addr_equal(op->dev->dev_addr, addr) && (!vid || br_vlan_find(vg, vid))) { - f->dst = op; + WRITE_ONCE(f->dst, op); clear_bit(BR_FDB_ADDED_BY_USER, &f->flags); return; } @@ -357,7 +361,7 @@ static void fdb_delete_local(struct net_bridge *br, /* Maybe bridge device has same hw addr? */ if (p && ether_addr_equal(br->dev->dev_addr, addr) && (!vid || (v && br_vlan_should_use(v)))) { - f->dst = NULL; + WRITE_ONCE(f->dst, NULL); clear_bit(BR_FDB_ADDED_BY_USER, &f->flags); return; } @@ -892,35 +896,6 @@ void br_fdb_delete_by_port(struct net_bridge *br, spin_unlock_bh(&br->hash_lock); } -#if IS_ENABLED(CONFIG_ATM_LANE) -/* Interface used by ATM LANE hook to test - * if an addr is on some other bridge port */ -int br_fdb_test_addr(struct net_device *dev, unsigned char *addr) -{ - struct net_bridge_fdb_entry *fdb; - struct net_bridge_port *port; - int ret; - - rcu_read_lock(); - port = br_port_get_rcu(dev); - if (!port) - ret = 0; - else { - const struct net_bridge_port *dst = NULL; - - fdb = br_fdb_find_rcu(port->br, addr, 0); - if (fdb) - dst = READ_ONCE(fdb->dst); - - ret = dst && dst->dev != dev && - dst->state == BR_STATE_FORWARDING; - } - rcu_read_unlock(); - - return ret; -} -#endif /* CONFIG_ATM_LANE */ - /* * Fill buffer with forwarding table records in * the API format. @@ -928,6 +903,7 @@ int br_fdb_test_addr(struct net_device *dev, unsigned char *addr) int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long maxnum, unsigned long skip) { + const struct net_bridge_port *dst; struct net_bridge_fdb_entry *f; struct __fdb_entry *fe = buf; unsigned long delta; @@ -944,7 +920,8 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf, continue; /* ignore pseudo entry for local MAC address */ - if (!f->dst) + dst = READ_ONCE(f->dst); + if (!dst) continue; if (skip) { @@ -956,8 +933,8 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf, memcpy(fe->mac_addr, f->key.addr.addr, ETH_ALEN); /* due to ABI compat need to split into hi/lo */ - fe->port_no = f->dst->port_no; - fe->port_hi = f->dst->port_no >> 8; + fe->port_no = dst->port_no; + fe->port_hi = dst->port_no >> 8; fe->is_local = test_bit(BR_FDB_LOCAL, &f->flags); if (!test_bit(BR_FDB_STATIC, &f->flags)) { @@ -1083,9 +1060,11 @@ int br_fdb_dump(struct sk_buff *skb, rcu_read_lock(); hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { + const struct net_bridge_port *dst = READ_ONCE(f->dst); + if (*idx < ctx->fdb_idx) goto skip; - if (filter_dev && (!f->dst || f->dst->dev != filter_dev)) { + if (filter_dev && (!dst || dst->dev != filter_dev)) { if (filter_dev != dev) goto skip; /* !f->dst is a special case for bridge @@ -1093,10 +1072,10 @@ int br_fdb_dump(struct sk_buff *skb, * Therefore need a little more filtering * we only want to dump the !f->dst case */ - if (f->dst) + if (dst) goto skip; } - if (!filter_dev && f->dst) + if (!filter_dev && dst) goto skip; err = fdb_fill_info(skb, br, f, diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 361a9b84451e..bed1b1d9b282 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -855,7 +855,6 @@ void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_fdb_entry *br_fdb_find_rcu(struct net_bridge *br, const unsigned char *addr, __u16 vid); -int br_fdb_test_addr(struct net_device *dev, unsigned char *addr); int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count, unsigned long off); int br_fdb_add_local(struct net_bridge *br, struct net_bridge_port *source, @@ -2065,9 +2064,6 @@ void br_stp_port_timer_init(struct net_bridge_port *p); unsigned long br_timer_value(const struct timer_list *timer); /* br.c */ -#if IS_ENABLED(CONFIG_ATM_LANE) -extern int (*br_fdb_test_addr_hook)(struct net_device *dev, unsigned char *addr); -#endif /* br_mrp.c */ #if IS_ENABLED(CONFIG_BRIDGE_MRP) diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index 741360219552..f05c79f215ea 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c @@ -112,24 +112,22 @@ static struct pernet_operations broute_net_ops = { static int __init ebtable_broute_init(void) { - int ret = ebt_register_template(&broute_table, broute_table_init); + int ret = register_pernet_subsys(&broute_net_ops); if (ret) return ret; - ret = register_pernet_subsys(&broute_net_ops); - if (ret) { - ebt_unregister_template(&broute_table); - return ret; - } + ret = ebt_register_template(&broute_table, broute_table_init); + if (ret) + unregister_pernet_subsys(&broute_net_ops); - return 0; + return ret; } static void __exit ebtable_broute_fini(void) { - unregister_pernet_subsys(&broute_net_ops); ebt_unregister_template(&broute_table); + unregister_pernet_subsys(&broute_net_ops); } module_init(ebtable_broute_init); diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index dacd81b12e62..0fc03b07e62a 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -93,24 +93,22 @@ static struct pernet_operations frame_filter_net_ops = { static int __init ebtable_filter_init(void) { - int ret = ebt_register_template(&frame_filter, frame_filter_table_init); + int ret = register_pernet_subsys(&frame_filter_net_ops); if (ret) return ret; - ret = register_pernet_subsys(&frame_filter_net_ops); - if (ret) { - ebt_unregister_template(&frame_filter); - return ret; - } + ret = ebt_register_template(&frame_filter, frame_filter_table_init); + if (ret) + unregister_pernet_subsys(&frame_filter_net_ops); - return 0; + return ret; } static void __exit ebtable_filter_fini(void) { - unregister_pernet_subsys(&frame_filter_net_ops); ebt_unregister_template(&frame_filter); + unregister_pernet_subsys(&frame_filter_net_ops); } module_init(ebtable_filter_init); diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index 0f2a8c6118d4..8a10375d8909 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -93,24 +93,22 @@ static struct pernet_operations frame_nat_net_ops = { static int __init ebtable_nat_init(void) { - int ret = ebt_register_template(&frame_nat, frame_nat_table_init); + int ret = register_pernet_subsys(&frame_nat_net_ops); if (ret) return ret; - ret = register_pernet_subsys(&frame_nat_net_ops); - if (ret) { - ebt_unregister_template(&frame_nat); - return ret; - } + ret = ebt_register_template(&frame_nat, frame_nat_table_init); + if (ret) + unregister_pernet_subsys(&frame_nat_net_ops); return ret; } static void __exit ebtable_nat_fini(void) { - unregister_pernet_subsys(&frame_nat_net_ops); ebt_unregister_template(&frame_nat); + unregister_pernet_subsys(&frame_nat_net_ops); } module_init(ebtable_nat_init); diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index aea3e19875c6..b9f4daac09af 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -42,6 +42,7 @@ struct ebt_pernet { struct list_head tables; + struct list_head dead_tables; }; struct ebt_template { @@ -1162,11 +1163,6 @@ free_newinfo: static void __ebt_unregister_table(struct net *net, struct ebt_table *table) { - mutex_lock(&ebt_mutex); - list_del(&table->list); - mutex_unlock(&ebt_mutex); - audit_log_nfcfg(table->name, AF_BRIDGE, table->private->nentries, - AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, ebt_cleanup_entry, net, NULL); if (table->private->nentries) @@ -1267,13 +1263,15 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table, for (i = 0; i < num_ops; i++) ops[i].priv = table; - list_add(&table->list, &ebt_net->tables); - mutex_unlock(&ebt_mutex); - table->ops = ops; ret = nf_register_net_hooks(net, ops, num_ops); - if (ret) + if (ret) { + synchronize_rcu(); __ebt_unregister_table(net, table); + } else { + list_add(&table->list, &ebt_net->tables); + } + mutex_unlock(&ebt_mutex); audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries, AUDIT_XT_OP_REGISTER, GFP_KERNEL); @@ -1339,7 +1337,7 @@ void ebt_unregister_template(const struct ebt_table *t) } EXPORT_SYMBOL(ebt_unregister_template); -static struct ebt_table *__ebt_find_table(struct net *net, const char *name) +void ebt_unregister_table_pre_exit(struct net *net, const char *name) { struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); struct ebt_table *t; @@ -1348,30 +1346,36 @@ static struct ebt_table *__ebt_find_table(struct net *net, const char *name) list_for_each_entry(t, &ebt_net->tables, list) { if (strcmp(t->name, name) == 0) { + list_move(&t->list, &ebt_net->dead_tables); mutex_unlock(&ebt_mutex); - return t; + nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); + return; } } mutex_unlock(&ebt_mutex); - return NULL; -} - -void ebt_unregister_table_pre_exit(struct net *net, const char *name) -{ - struct ebt_table *table = __ebt_find_table(net, name); - - if (table) - nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); } EXPORT_SYMBOL(ebt_unregister_table_pre_exit); void ebt_unregister_table(struct net *net, const char *name) { - struct ebt_table *table = __ebt_find_table(net, name); + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + struct ebt_table *t; - if (table) - __ebt_unregister_table(net, table); + mutex_lock(&ebt_mutex); + + list_for_each_entry(t, &ebt_net->dead_tables, list) { + if (strcmp(t->name, name) == 0) { + list_del(&t->list); + audit_log_nfcfg(t->name, AF_BRIDGE, t->private->nentries, + AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); + __ebt_unregister_table(net, t); + mutex_unlock(&ebt_mutex); + return; + } + } + + mutex_unlock(&ebt_mutex); } /* userspace just supplied us with counters */ @@ -2556,11 +2560,21 @@ static int __net_init ebt_pernet_init(struct net *net) struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); INIT_LIST_HEAD(&ebt_net->tables); + INIT_LIST_HEAD(&ebt_net->dead_tables); return 0; } +static void __net_exit ebt_pernet_exit(struct net *net) +{ + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + + WARN_ON_ONCE(!list_empty(&ebt_net->tables)); + WARN_ON_ONCE(!list_empty(&ebt_net->dead_tables)); +} + static struct pernet_operations ebt_net_ops = { .init = ebt_pernet_init, + .exit = ebt_pernet_exit, .id = &ebt_pernet_id, .size = sizeof(struct ebt_pernet), }; @@ -2569,19 +2583,20 @@ static int __init ebtables_init(void) { int ret; - ret = xt_register_target(&ebt_standard_target); + ret = register_pernet_subsys(&ebt_net_ops); if (ret < 0) return ret; - ret = nf_register_sockopt(&ebt_sockopts); + + ret = xt_register_target(&ebt_standard_target); if (ret < 0) { - xt_unregister_target(&ebt_standard_target); + unregister_pernet_subsys(&ebt_net_ops); return ret; } - ret = register_pernet_subsys(&ebt_net_ops); + ret = nf_register_sockopt(&ebt_sockopts); if (ret < 0) { - nf_unregister_sockopt(&ebt_sockopts); xt_unregister_target(&ebt_standard_target); + unregister_pernet_subsys(&ebt_net_ops); return ret; } diff --git a/net/caif/Kconfig b/net/caif/Kconfig deleted file mode 100644 index 87205251cc25..000000000000 --- a/net/caif/Kconfig +++ /dev/null @@ -1,54 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# CAIF net configurations -# - -menuconfig CAIF - tristate "CAIF support" - select CRC_CCITT - default n - help - The "Communication CPU to Application CPU Interface" (CAIF) is a packet - based connection-oriented MUX protocol developed by ST-Ericsson for use - with its modems. It is accessed from user space as sockets (PF_CAIF). - - Say Y (or M) here if you build for a phone product (e.g. Android or - MeeGo) that uses CAIF as transport. If unsure say N. - - If you select to build it as module then CAIF_NETDEV also needs to be - built as a module. You will also need to say Y (or M) to any CAIF - physical devices that your platform requires. - - See Documentation/networking/caif for a further explanation on how to - use and configure CAIF. - -config CAIF_DEBUG - bool "Enable Debug" - depends on CAIF - default n - help - Enable the inclusion of debug code in the CAIF stack. - Be aware that doing this will impact performance. - If unsure say N. - -config CAIF_NETDEV - tristate "CAIF GPRS Network device" - depends on CAIF - default CAIF - help - Say Y if you will be using a CAIF based GPRS network device. - This can be either built-in or a loadable module. - If you select to build it as a built-in then the main CAIF device must - also be a built-in. - If unsure say Y. - -config CAIF_USB - tristate "CAIF USB support" - depends on CAIF - default n - help - Say Y if you are using CAIF over USB CDC NCM. - This can be either built-in or a loadable module. - If you select to build it as a built-in then the main CAIF device must - also be a built-in. - If unsure say N. diff --git a/net/caif/Makefile b/net/caif/Makefile deleted file mode 100644 index 4f6c0517cdfb..000000000000 --- a/net/caif/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -ccflags-$(CONFIG_CAIF_DEBUG) := -DDEBUG - -caif-y := caif_dev.o \ - cfcnfg.o cfmuxl.o cfctrl.o \ - cffrml.o cfveil.o cfdbgl.o\ - cfserl.o cfdgml.o \ - cfrfml.o cfvidl.o cfutill.o \ - cfsrvl.o cfpkt_skbuff.o - -obj-$(CONFIG_CAIF) += caif.o -obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o -obj-$(CONFIG_CAIF) += caif_socket.o -obj-$(CONFIG_CAIF_USB) += caif_usb.o - -export-y := caif.o diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c deleted file mode 100644 index 922de3d611c0..000000000000 --- a/net/caif/caif_dev.c +++ /dev/null @@ -1,586 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * CAIF Interface registration. - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - * - * Borrowed heavily from file: pn_dev.c. Thanks to Remi Denis-Courmont - * and Sakari Ailus - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol support"); -MODULE_LICENSE("GPL"); - -/* Used for local tracking of the CAIF net devices */ -struct caif_device_entry { - struct cflayer layer; - struct list_head list; - struct net_device *netdev; - int __percpu *pcpu_refcnt; - spinlock_t flow_lock; - struct sk_buff *xoff_skb; - void (*xoff_skb_dtor)(struct sk_buff *skb); - bool xoff; -}; - -struct caif_device_entry_list { - struct list_head list; - /* Protects simulanous deletes in list */ - struct mutex lock; -}; - -struct caif_net { - struct cfcnfg *cfg; - struct caif_device_entry_list caifdevs; -}; - -static unsigned int caif_net_id; -static int q_high = 50; /* Percent */ - -struct cfcnfg *get_cfcnfg(struct net *net) -{ - struct caif_net *caifn; - caifn = net_generic(net, caif_net_id); - return caifn->cfg; -} -EXPORT_SYMBOL(get_cfcnfg); - -static struct caif_device_entry_list *caif_device_list(struct net *net) -{ - struct caif_net *caifn; - caifn = net_generic(net, caif_net_id); - return &caifn->caifdevs; -} - -static void caifd_put(struct caif_device_entry *e) -{ - this_cpu_dec(*e->pcpu_refcnt); -} - -static void caifd_hold(struct caif_device_entry *e) -{ - this_cpu_inc(*e->pcpu_refcnt); -} - -static int caifd_refcnt_read(struct caif_device_entry *e) -{ - int i, refcnt = 0; - for_each_possible_cpu(i) - refcnt += *per_cpu_ptr(e->pcpu_refcnt, i); - return refcnt; -} - -/* Allocate new CAIF device. */ -static struct caif_device_entry *caif_device_alloc(struct net_device *dev) -{ - struct caif_device_entry *caifd; - - caifd = kzalloc_obj(*caifd); - if (!caifd) - return NULL; - caifd->pcpu_refcnt = alloc_percpu(int); - if (!caifd->pcpu_refcnt) { - kfree(caifd); - return NULL; - } - caifd->netdev = dev; - dev_hold(dev); - return caifd; -} - -static struct caif_device_entry *caif_get(struct net_device *dev) -{ - struct caif_device_entry_list *caifdevs = - caif_device_list(dev_net(dev)); - struct caif_device_entry *caifd; - - list_for_each_entry_rcu(caifd, &caifdevs->list, list, - lockdep_rtnl_is_held()) { - if (caifd->netdev == dev) - return caifd; - } - return NULL; -} - -static void caif_flow_cb(struct sk_buff *skb) -{ - struct caif_device_entry *caifd; - void (*dtor)(struct sk_buff *skb) = NULL; - bool send_xoff; - - WARN_ON(skb->dev == NULL); - - rcu_read_lock(); - caifd = caif_get(skb->dev); - - WARN_ON(caifd == NULL); - if (!caifd) { - rcu_read_unlock(); - return; - } - - caifd_hold(caifd); - rcu_read_unlock(); - - spin_lock_bh(&caifd->flow_lock); - send_xoff = caifd->xoff; - caifd->xoff = false; - dtor = caifd->xoff_skb_dtor; - - if (WARN_ON(caifd->xoff_skb != skb)) - skb = NULL; - - caifd->xoff_skb = NULL; - caifd->xoff_skb_dtor = NULL; - - spin_unlock_bh(&caifd->flow_lock); - - if (dtor && skb) - dtor(skb); - - if (send_xoff) - caifd->layer.up-> - ctrlcmd(caifd->layer.up, - _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND, - caifd->layer.id); - caifd_put(caifd); -} - -static int transmit(struct cflayer *layer, struct cfpkt *pkt) -{ - int err, high = 0, qlen = 0; - struct caif_device_entry *caifd = - container_of(layer, struct caif_device_entry, layer); - struct sk_buff *skb; - struct netdev_queue *txq; - - rcu_read_lock_bh(); - - skb = cfpkt_tonative(pkt); - skb->dev = caifd->netdev; - skb_reset_network_header(skb); - skb->protocol = htons(ETH_P_CAIF); - - /* Check if we need to handle xoff */ - if (likely(caifd->netdev->priv_flags & IFF_NO_QUEUE)) - goto noxoff; - - if (unlikely(caifd->xoff)) - goto noxoff; - - if (likely(!netif_queue_stopped(caifd->netdev))) { - struct Qdisc *sch; - - /* If we run with a TX queue, check if the queue is too long*/ - txq = netdev_get_tx_queue(skb->dev, 0); - sch = rcu_dereference_bh(txq->qdisc); - if (likely(qdisc_is_empty(sch))) - goto noxoff; - - /* can check for explicit qdisc len value only !NOLOCK, - * always set flow off otherwise - */ - high = (caifd->netdev->tx_queue_len * q_high) / 100; - if (!(sch->flags & TCQ_F_NOLOCK) && likely(sch->q.qlen < high)) - goto noxoff; - } - - /* Hold lock while accessing xoff */ - spin_lock_bh(&caifd->flow_lock); - if (caifd->xoff) { - spin_unlock_bh(&caifd->flow_lock); - goto noxoff; - } - - /* - * Handle flow off, we do this by temporary hi-jacking this - * skb's destructor function, and replace it with our own - * flow-on callback. The callback will set flow-on and call - * the original destructor. - */ - - pr_debug("queue has stopped(%d) or is full (%d > %d)\n", - netif_queue_stopped(caifd->netdev), - qlen, high); - caifd->xoff = true; - caifd->xoff_skb = skb; - caifd->xoff_skb_dtor = skb->destructor; - skb->destructor = caif_flow_cb; - spin_unlock_bh(&caifd->flow_lock); - - caifd->layer.up->ctrlcmd(caifd->layer.up, - _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, - caifd->layer.id); -noxoff: - rcu_read_unlock_bh(); - - err = dev_queue_xmit(skb); - if (err > 0) - err = -EIO; - - return err; -} - -/* - * Stuff received packets into the CAIF stack. - * On error, returns non-zero and releases the skb. - */ -static int receive(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pkttype, struct net_device *orig_dev) -{ - struct cfpkt *pkt; - struct caif_device_entry *caifd; - int err; - - pkt = cfpkt_fromnative(CAIF_DIR_IN, skb); - - rcu_read_lock(); - caifd = caif_get(dev); - - if (!caifd || !caifd->layer.up || !caifd->layer.up->receive || - !netif_oper_up(caifd->netdev)) { - rcu_read_unlock(); - kfree_skb(skb); - return NET_RX_DROP; - } - - /* Hold reference to netdevice while using CAIF stack */ - caifd_hold(caifd); - rcu_read_unlock(); - - err = caifd->layer.up->receive(caifd->layer.up, pkt); - - /* For -EILSEQ the packet is not freed so free it now */ - if (err == -EILSEQ) - cfpkt_destroy(pkt); - - /* Release reference to stack upwards */ - caifd_put(caifd); - - if (err != 0) - err = NET_RX_DROP; - return err; -} - -static struct packet_type caif_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_CAIF), - .func = receive, -}; - -static void dev_flowctrl(struct net_device *dev, int on) -{ - struct caif_device_entry *caifd; - - rcu_read_lock(); - - caifd = caif_get(dev); - if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) { - rcu_read_unlock(); - return; - } - - caifd_hold(caifd); - rcu_read_unlock(); - - caifd->layer.up->ctrlcmd(caifd->layer.up, - on ? - _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND : - _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND, - caifd->layer.id); - caifd_put(caifd); -} - -int caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev, - struct cflayer *link_support, int head_room, - struct cflayer **layer, - int (**rcv_func)(struct sk_buff *, struct net_device *, - struct packet_type *, - struct net_device *)) -{ - struct caif_device_entry *caifd; - enum cfcnfg_phy_preference pref; - struct cfcnfg *cfg = get_cfcnfg(dev_net(dev)); - struct caif_device_entry_list *caifdevs; - int res; - - caifdevs = caif_device_list(dev_net(dev)); - caifd = caif_device_alloc(dev); - if (!caifd) - return -ENOMEM; - *layer = &caifd->layer; - spin_lock_init(&caifd->flow_lock); - - switch (caifdev->link_select) { - case CAIF_LINK_HIGH_BANDW: - pref = CFPHYPREF_HIGH_BW; - break; - case CAIF_LINK_LOW_LATENCY: - pref = CFPHYPREF_LOW_LAT; - break; - default: - pref = CFPHYPREF_HIGH_BW; - break; - } - mutex_lock(&caifdevs->lock); - list_add_rcu(&caifd->list, &caifdevs->list); - - strscpy(caifd->layer.name, dev->name, - sizeof(caifd->layer.name)); - caifd->layer.transmit = transmit; - res = cfcnfg_add_phy_layer(cfg, - dev, - &caifd->layer, - pref, - link_support, - caifdev->use_fcs, - head_room); - mutex_unlock(&caifdevs->lock); - if (rcv_func) - *rcv_func = receive; - return res; -} -EXPORT_SYMBOL(caif_enroll_dev); - -/* notify Caif of device events */ -static int caif_device_notify(struct notifier_block *me, unsigned long what, - void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct caif_device_entry *caifd = NULL; - struct caif_dev_common *caifdev; - struct cfcnfg *cfg; - struct cflayer *layer, *link_support; - int head_room = 0; - struct caif_device_entry_list *caifdevs; - int res; - - cfg = get_cfcnfg(dev_net(dev)); - caifdevs = caif_device_list(dev_net(dev)); - - caifd = caif_get(dev); - if (caifd == NULL && dev->type != ARPHRD_CAIF) - return 0; - - switch (what) { - case NETDEV_REGISTER: - if (caifd != NULL) - break; - - caifdev = netdev_priv(dev); - - link_support = NULL; - if (caifdev->use_frag) { - head_room = 1; - link_support = cfserl_create(dev->ifindex, - caifdev->use_stx); - if (!link_support) { - pr_warn("Out of memory\n"); - break; - } - } - res = caif_enroll_dev(dev, caifdev, link_support, head_room, - &layer, NULL); - if (res) - cfserl_release(link_support); - caifdev->flowctrl = dev_flowctrl; - break; - - case NETDEV_UP: - rcu_read_lock(); - - caifd = caif_get(dev); - if (caifd == NULL) { - rcu_read_unlock(); - break; - } - - caifd->xoff = false; - cfcnfg_set_phy_state(cfg, &caifd->layer, true); - rcu_read_unlock(); - - break; - - case NETDEV_DOWN: - rcu_read_lock(); - - caifd = caif_get(dev); - if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) { - rcu_read_unlock(); - return -EINVAL; - } - - cfcnfg_set_phy_state(cfg, &caifd->layer, false); - caifd_hold(caifd); - rcu_read_unlock(); - - caifd->layer.up->ctrlcmd(caifd->layer.up, - _CAIF_CTRLCMD_PHYIF_DOWN_IND, - caifd->layer.id); - - spin_lock_bh(&caifd->flow_lock); - - /* - * Replace our xoff-destructor with original destructor. - * We trust that skb->destructor *always* is called before - * the skb reference is invalid. The hijacked SKB destructor - * takes the flow_lock so manipulating the skb->destructor here - * should be safe. - */ - if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL) - caifd->xoff_skb->destructor = caifd->xoff_skb_dtor; - - caifd->xoff = false; - caifd->xoff_skb_dtor = NULL; - caifd->xoff_skb = NULL; - - spin_unlock_bh(&caifd->flow_lock); - caifd_put(caifd); - break; - - case NETDEV_UNREGISTER: - mutex_lock(&caifdevs->lock); - - caifd = caif_get(dev); - if (caifd == NULL) { - mutex_unlock(&caifdevs->lock); - break; - } - list_del_rcu(&caifd->list); - - /* - * NETDEV_UNREGISTER is called repeatedly until all reference - * counts for the net-device are released. If references to - * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for - * the next call to NETDEV_UNREGISTER. - * - * If any packets are in flight down the CAIF Stack, - * cfcnfg_del_phy_layer will return nonzero. - * If no packets are in flight, the CAIF Stack associated - * with the net-device un-registering is freed. - */ - - if (caifd_refcnt_read(caifd) != 0 || - cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) { - - pr_info("Wait for device inuse\n"); - /* Enrole device if CAIF Stack is still in use */ - list_add_rcu(&caifd->list, &caifdevs->list); - mutex_unlock(&caifdevs->lock); - break; - } - - synchronize_rcu(); - dev_put(caifd->netdev); - free_percpu(caifd->pcpu_refcnt); - kfree(caifd); - - mutex_unlock(&caifdevs->lock); - break; - } - return 0; -} - -static struct notifier_block caif_device_notifier = { - .notifier_call = caif_device_notify, - .priority = 0, -}; - -/* Per-namespace Caif devices handling */ -static int caif_init_net(struct net *net) -{ - struct caif_net *caifn = net_generic(net, caif_net_id); - INIT_LIST_HEAD(&caifn->caifdevs.list); - mutex_init(&caifn->caifdevs.lock); - - caifn->cfg = cfcnfg_create(); - if (!caifn->cfg) - return -ENOMEM; - - return 0; -} - -static void caif_exit_net(struct net *net) -{ - struct caif_device_entry *caifd, *tmp; - struct caif_device_entry_list *caifdevs = - caif_device_list(net); - struct cfcnfg *cfg = get_cfcnfg(net); - - rtnl_lock(); - mutex_lock(&caifdevs->lock); - - list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) { - int i = 0; - list_del_rcu(&caifd->list); - cfcnfg_set_phy_state(cfg, &caifd->layer, false); - - while (i < 10 && - (caifd_refcnt_read(caifd) != 0 || - cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) { - - pr_info("Wait for device inuse\n"); - msleep(250); - i++; - } - synchronize_rcu(); - dev_put(caifd->netdev); - free_percpu(caifd->pcpu_refcnt); - kfree(caifd); - } - cfcnfg_remove(cfg); - - mutex_unlock(&caifdevs->lock); - rtnl_unlock(); -} - -static struct pernet_operations caif_net_ops = { - .init = caif_init_net, - .exit = caif_exit_net, - .id = &caif_net_id, - .size = sizeof(struct caif_net), -}; - -/* Initialize Caif devices list */ -static int __init caif_device_init(void) -{ - int result; - - result = register_pernet_subsys(&caif_net_ops); - - if (result) - return result; - - register_netdevice_notifier(&caif_device_notifier); - dev_add_pack(&caif_packet_type); - - return result; -} - -static void __exit caif_device_exit(void) -{ - unregister_netdevice_notifier(&caif_device_notifier); - dev_remove_pack(&caif_packet_type); - unregister_pernet_subsys(&caif_net_ops); -} - -module_init(caif_device_init); -module_exit(caif_device_exit); diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c deleted file mode 100644 index af218742af5a..000000000000 --- a/net/caif/caif_socket.c +++ /dev/null @@ -1,1114 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol socket support (AF_CAIF)"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(AF_CAIF); - -/* - * CAIF state is re-using the TCP socket states. - * caif_states stored in sk_state reflect the state as reported by - * the CAIF stack, while sk_socket->state is the state of the socket. - */ -enum caif_states { - CAIF_CONNECTED = TCP_ESTABLISHED, - CAIF_CONNECTING = TCP_SYN_SENT, - CAIF_DISCONNECTED = TCP_CLOSE -}; - -#define TX_FLOW_ON_BIT 1 -#define RX_FLOW_ON_BIT 2 - -struct caifsock { - struct sock sk; /* must be first member */ - struct cflayer layer; - unsigned long flow_state; - struct caif_connect_request conn_req; - struct mutex readlock; - struct dentry *debugfs_socket_dir; - int headroom, tailroom, maxframe; -}; - -static int rx_flow_is_on(struct caifsock *cf_sk) -{ - return test_bit(RX_FLOW_ON_BIT, &cf_sk->flow_state); -} - -static int tx_flow_is_on(struct caifsock *cf_sk) -{ - return test_bit(TX_FLOW_ON_BIT, &cf_sk->flow_state); -} - -static void set_rx_flow_off(struct caifsock *cf_sk) -{ - clear_bit(RX_FLOW_ON_BIT, &cf_sk->flow_state); -} - -static void set_rx_flow_on(struct caifsock *cf_sk) -{ - set_bit(RX_FLOW_ON_BIT, &cf_sk->flow_state); -} - -static void set_tx_flow_off(struct caifsock *cf_sk) -{ - clear_bit(TX_FLOW_ON_BIT, &cf_sk->flow_state); -} - -static void set_tx_flow_on(struct caifsock *cf_sk) -{ - set_bit(TX_FLOW_ON_BIT, &cf_sk->flow_state); -} - -static void caif_read_lock(struct sock *sk) -{ - struct caifsock *cf_sk; - cf_sk = container_of(sk, struct caifsock, sk); - mutex_lock(&cf_sk->readlock); -} - -static void caif_read_unlock(struct sock *sk) -{ - struct caifsock *cf_sk; - cf_sk = container_of(sk, struct caifsock, sk); - mutex_unlock(&cf_sk->readlock); -} - -static int sk_rcvbuf_lowwater(struct caifsock *cf_sk) -{ - /* A quarter of full buffer is used a low water mark */ - return cf_sk->sk.sk_rcvbuf / 4; -} - -static void caif_flow_ctrl(struct sock *sk, int mode) -{ - struct caifsock *cf_sk; - cf_sk = container_of(sk, struct caifsock, sk); - if (cf_sk->layer.dn && cf_sk->layer.dn->modemcmd) - cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, mode); -} - -/* - * Copied from sock.c:sock_queue_rcv_skb(), but changed so packets are - * not dropped, but CAIF is sending flow off instead. - */ -static void caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) -{ - int err; - unsigned long flags; - struct sk_buff_head *list = &sk->sk_receive_queue; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - bool queued = false; - - if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= - (unsigned int)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) { - net_dbg_ratelimited("sending flow OFF (queue len = %d %d)\n", - atomic_read(&cf_sk->sk.sk_rmem_alloc), - sk_rcvbuf_lowwater(cf_sk)); - set_rx_flow_off(cf_sk); - caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ); - } - - err = sk_filter(sk, skb); - if (err) - goto out; - - if (!sk_rmem_schedule(sk, skb, skb->truesize) && rx_flow_is_on(cf_sk)) { - set_rx_flow_off(cf_sk); - net_dbg_ratelimited("sending flow OFF due to rmem_schedule\n"); - caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ); - } - skb->dev = NULL; - skb_set_owner_r(skb, sk); - spin_lock_irqsave(&list->lock, flags); - queued = !sock_flag(sk, SOCK_DEAD); - if (queued) - __skb_queue_tail(list, skb); - spin_unlock_irqrestore(&list->lock, flags); -out: - if (queued) - sk->sk_data_ready(sk); - else - kfree_skb(skb); -} - -/* Packet Receive Callback function called from CAIF Stack */ -static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt) -{ - struct caifsock *cf_sk; - struct sk_buff *skb; - - cf_sk = container_of(layr, struct caifsock, layer); - skb = cfpkt_tonative(pkt); - - if (unlikely(cf_sk->sk.sk_state != CAIF_CONNECTED)) { - kfree_skb(skb); - return 0; - } - caif_queue_rcv_skb(&cf_sk->sk, skb); - return 0; -} - -static void cfsk_hold(struct cflayer *layr) -{ - struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); - sock_hold(&cf_sk->sk); -} - -static void cfsk_put(struct cflayer *layr) -{ - struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); - sock_put(&cf_sk->sk); -} - -/* Packet Control Callback function called from CAIF */ -static void caif_ctrl_cb(struct cflayer *layr, - enum caif_ctrlcmd flow, - int phyid) -{ - struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); - switch (flow) { - case CAIF_CTRLCMD_FLOW_ON_IND: - /* OK from modem to start sending again */ - set_tx_flow_on(cf_sk); - cf_sk->sk.sk_state_change(&cf_sk->sk); - break; - - case CAIF_CTRLCMD_FLOW_OFF_IND: - /* Modem asks us to shut up */ - set_tx_flow_off(cf_sk); - cf_sk->sk.sk_state_change(&cf_sk->sk); - break; - - case CAIF_CTRLCMD_INIT_RSP: - /* We're now connected */ - caif_client_register_refcnt(&cf_sk->layer, - cfsk_hold, cfsk_put); - cf_sk->sk.sk_state = CAIF_CONNECTED; - set_tx_flow_on(cf_sk); - cf_sk->sk.sk_shutdown = 0; - cf_sk->sk.sk_state_change(&cf_sk->sk); - break; - - case CAIF_CTRLCMD_DEINIT_RSP: - /* We're now disconnected */ - cf_sk->sk.sk_state = CAIF_DISCONNECTED; - cf_sk->sk.sk_state_change(&cf_sk->sk); - break; - - case CAIF_CTRLCMD_INIT_FAIL_RSP: - /* Connect request failed */ - cf_sk->sk.sk_err = ECONNREFUSED; - cf_sk->sk.sk_state = CAIF_DISCONNECTED; - cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; - /* - * Socket "standards" seems to require POLLOUT to - * be set at connect failure. - */ - set_tx_flow_on(cf_sk); - cf_sk->sk.sk_state_change(&cf_sk->sk); - break; - - case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: - /* Modem has closed this connection, or device is down. */ - cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; - cf_sk->sk.sk_err = ECONNRESET; - set_rx_flow_on(cf_sk); - sk_error_report(&cf_sk->sk); - break; - - default: - pr_debug("Unexpected flow command %d\n", flow); - } -} - -static void caif_check_flow_release(struct sock *sk) -{ - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - - if (rx_flow_is_on(cf_sk)) - return; - - if (atomic_read(&sk->sk_rmem_alloc) <= sk_rcvbuf_lowwater(cf_sk)) { - set_rx_flow_on(cf_sk); - caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_ON_REQ); - } -} - -/* - * Copied from unix_dgram_recvmsg, but removed credit checks, - * changed locking, address handling and added MSG_TRUNC. - */ -static int caif_seqpkt_recvmsg(struct socket *sock, struct msghdr *m, - size_t len, int flags) - -{ - struct sock *sk = sock->sk; - struct sk_buff *skb; - int ret; - int copylen; - - ret = -EOPNOTSUPP; - if (flags & MSG_OOB) - goto read_error; - - skb = skb_recv_datagram(sk, flags, &ret); - if (!skb) - goto read_error; - copylen = skb->len; - if (len < copylen) { - m->msg_flags |= MSG_TRUNC; - copylen = len; - } - - ret = skb_copy_datagram_msg(skb, 0, m, copylen); - if (ret) - goto out_free; - - ret = (flags & MSG_TRUNC) ? skb->len : copylen; -out_free: - skb_free_datagram(sk, skb); - caif_check_flow_release(sk); - return ret; - -read_error: - return ret; -} - - -/* Copied from unix_stream_wait_data, identical except for lock call. */ -static long caif_stream_data_wait(struct sock *sk, long timeo) -{ - DEFINE_WAIT(wait); - lock_sock(sk); - - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - - if (!skb_queue_empty(&sk->sk_receive_queue) || - sk->sk_err || - sk->sk_state != CAIF_CONNECTED || - sock_flag(sk, SOCK_DEAD) || - (sk->sk_shutdown & RCV_SHUTDOWN) || - signal_pending(current) || - !timeo) - break; - - sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - - if (sock_flag(sk, SOCK_DEAD)) - break; - - sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); - } - - finish_wait(sk_sleep(sk), &wait); - release_sock(sk); - return timeo; -} - - -/* - * Copied from unix_stream_recvmsg, but removed credit checks, - * changed locking calls, changed address handling. - */ -static int caif_stream_recvmsg(struct socket *sock, struct msghdr *msg, - size_t size, int flags) -{ - struct sock *sk = sock->sk; - int copied = 0; - int target; - int err = 0; - long timeo; - - err = -EOPNOTSUPP; - if (flags&MSG_OOB) - goto out; - - /* - * Lock the socket to prevent queue disordering - * while sleeps in memcpy_tomsg - */ - err = -EAGAIN; - if (sk->sk_state == CAIF_CONNECTING) - goto out; - - caif_read_lock(sk); - target = sock_rcvlowat(sk, flags&MSG_WAITALL, size); - timeo = sock_rcvtimeo(sk, flags&MSG_DONTWAIT); - - do { - int chunk; - struct sk_buff *skb; - - lock_sock(sk); - if (sock_flag(sk, SOCK_DEAD)) { - err = -ECONNRESET; - goto unlock; - } - skb = skb_dequeue(&sk->sk_receive_queue); - caif_check_flow_release(sk); - - if (skb == NULL) { - if (copied >= target) - goto unlock; - /* - * POSIX 1003.1g mandates this order. - */ - err = sock_error(sk); - if (err) - goto unlock; - err = -ECONNRESET; - if (sk->sk_shutdown & RCV_SHUTDOWN) - goto unlock; - - err = -EPIPE; - if (sk->sk_state != CAIF_CONNECTED) - goto unlock; - if (sock_flag(sk, SOCK_DEAD)) - goto unlock; - - release_sock(sk); - - err = -EAGAIN; - if (!timeo) - break; - - caif_read_unlock(sk); - - timeo = caif_stream_data_wait(sk, timeo); - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - goto out; - } - caif_read_lock(sk); - continue; -unlock: - release_sock(sk); - break; - } - release_sock(sk); - chunk = min_t(unsigned int, skb->len, size); - if (memcpy_to_msg(msg, skb->data, chunk)) { - skb_queue_head(&sk->sk_receive_queue, skb); - if (copied == 0) - copied = -EFAULT; - break; - } - copied += chunk; - size -= chunk; - - /* Mark read part of skb as used */ - if (!(flags & MSG_PEEK)) { - skb_pull(skb, chunk); - - /* put the skb back if we didn't use it up. */ - if (skb->len) { - skb_queue_head(&sk->sk_receive_queue, skb); - break; - } - kfree_skb(skb); - - } else { - /* - * It is questionable, see note in unix_dgram_recvmsg. - */ - /* put message back and return */ - skb_queue_head(&sk->sk_receive_queue, skb); - break; - } - } while (size); - caif_read_unlock(sk); - -out: - return copied ? : err; -} - -/* - * Copied from sock.c:sock_wait_for_wmem, but change to wait for - * CAIF flow-on and sock_writable. - */ -static long caif_wait_for_flow_on(struct caifsock *cf_sk, - int wait_writeable, long timeo, int *err) -{ - struct sock *sk = &cf_sk->sk; - DEFINE_WAIT(wait); - for (;;) { - *err = 0; - if (tx_flow_is_on(cf_sk) && - (!wait_writeable || sock_writeable(&cf_sk->sk))) - break; - *err = -ETIMEDOUT; - if (!timeo) - break; - *err = -ERESTARTSYS; - if (signal_pending(current)) - break; - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - *err = -ECONNRESET; - if (sk->sk_shutdown & SHUTDOWN_MASK) - break; - *err = -sk->sk_err; - if (sk->sk_err) - break; - *err = -EPIPE; - if (cf_sk->sk.sk_state != CAIF_CONNECTED) - break; - timeo = schedule_timeout(timeo); - } - finish_wait(sk_sleep(sk), &wait); - return timeo; -} - -/* - * Transmit a SKB. The device may temporarily request re-transmission - * by returning EAGAIN. - */ -static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk, - int noblock, long timeo) -{ - struct cfpkt *pkt; - - pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb); - memset(skb->cb, 0, sizeof(struct caif_payload_info)); - cfpkt_set_prio(pkt, cf_sk->sk.sk_priority); - - if (cf_sk->layer.dn == NULL) { - kfree_skb(skb); - return -EINVAL; - } - - return cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt); -} - -/* Copied from af_unix:unix_dgram_sendmsg, and adapted to CAIF */ -static int caif_seqpkt_sendmsg(struct socket *sock, struct msghdr *msg, - size_t len) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - int buffer_size; - int ret = 0; - struct sk_buff *skb = NULL; - int noblock; - long timeo; - caif_assert(cf_sk); - ret = sock_error(sk); - if (ret) - goto err; - - ret = -EOPNOTSUPP; - if (msg->msg_flags&MSG_OOB) - goto err; - - ret = -EOPNOTSUPP; - if (msg->msg_namelen) - goto err; - - noblock = msg->msg_flags & MSG_DONTWAIT; - - timeo = sock_sndtimeo(sk, noblock); - timeo = caif_wait_for_flow_on(container_of(sk, struct caifsock, sk), - 1, timeo, &ret); - - if (ret) - goto err; - ret = -EPIPE; - if (cf_sk->sk.sk_state != CAIF_CONNECTED || - sock_flag(sk, SOCK_DEAD) || - (sk->sk_shutdown & RCV_SHUTDOWN)) - goto err; - - /* Error if trying to write more than maximum frame size. */ - ret = -EMSGSIZE; - if (len > cf_sk->maxframe && cf_sk->sk.sk_protocol != CAIFPROTO_RFM) - goto err; - - buffer_size = len + cf_sk->headroom + cf_sk->tailroom; - - ret = -ENOMEM; - skb = sock_alloc_send_skb(sk, buffer_size, noblock, &ret); - - if (!skb || skb_tailroom(skb) < buffer_size) - goto err; - - skb_reserve(skb, cf_sk->headroom); - - ret = memcpy_from_msg(skb_put(skb, len), msg, len); - - if (ret) - goto err; - ret = transmit_skb(skb, cf_sk, noblock, timeo); - if (ret < 0) - /* skb is already freed */ - return ret; - - return len; -err: - kfree_skb(skb); - return ret; -} - -/* - * Copied from unix_stream_sendmsg and adapted to CAIF: - * Changed removed permission handling and added waiting for flow on - * and other minor adaptations. - */ -static int caif_stream_sendmsg(struct socket *sock, struct msghdr *msg, - size_t len) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - int err, size; - struct sk_buff *skb; - int sent = 0; - long timeo; - - err = -EOPNOTSUPP; - if (unlikely(msg->msg_flags&MSG_OOB)) - goto out_err; - - if (unlikely(msg->msg_namelen)) - goto out_err; - - timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); - timeo = caif_wait_for_flow_on(cf_sk, 1, timeo, &err); - - if (unlikely(sk->sk_shutdown & SEND_SHUTDOWN)) - goto pipe_err; - - while (sent < len) { - - size = len-sent; - - if (size > cf_sk->maxframe) - size = cf_sk->maxframe; - - /* If size is more than half of sndbuf, chop up message */ - if (size > ((sk->sk_sndbuf >> 1) - 64)) - size = (sk->sk_sndbuf >> 1) - 64; - - if (size > SKB_MAX_ALLOC) - size = SKB_MAX_ALLOC; - - skb = sock_alloc_send_skb(sk, - size + cf_sk->headroom + - cf_sk->tailroom, - msg->msg_flags&MSG_DONTWAIT, - &err); - if (skb == NULL) - goto out_err; - - skb_reserve(skb, cf_sk->headroom); - /* - * If you pass two values to the sock_alloc_send_skb - * it tries to grab the large buffer with GFP_NOFS - * (which can fail easily), and if it fails grab the - * fallback size buffer which is under a page and will - * succeed. [Alan] - */ - size = min_t(int, size, skb_tailroom(skb)); - - err = memcpy_from_msg(skb_put(skb, size), msg, size); - if (err) { - kfree_skb(skb); - goto out_err; - } - err = transmit_skb(skb, cf_sk, - msg->msg_flags&MSG_DONTWAIT, timeo); - if (err < 0) - /* skb is already freed */ - goto pipe_err; - - sent += size; - } - - return sent; - -pipe_err: - if (sent == 0 && !(msg->msg_flags&MSG_NOSIGNAL)) - send_sig(SIGPIPE, current, 0); - err = -EPIPE; -out_err: - return sent ? : err; -} - -static int setsockopt(struct socket *sock, int lvl, int opt, sockptr_t ov, - unsigned int ol) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - int linksel; - - if (cf_sk->sk.sk_socket->state != SS_UNCONNECTED) - return -ENOPROTOOPT; - - switch (opt) { - case CAIFSO_LINK_SELECT: - if (ol < sizeof(int)) - return -EINVAL; - if (lvl != SOL_CAIF) - goto bad_sol; - if (copy_from_sockptr(&linksel, ov, sizeof(int))) - return -EINVAL; - lock_sock(&(cf_sk->sk)); - cf_sk->conn_req.link_selector = linksel; - release_sock(&cf_sk->sk); - return 0; - - case CAIFSO_REQ_PARAM: - if (lvl != SOL_CAIF) - goto bad_sol; - if (cf_sk->sk.sk_protocol != CAIFPROTO_UTIL) - return -ENOPROTOOPT; - lock_sock(&(cf_sk->sk)); - if (ol > sizeof(cf_sk->conn_req.param.data) || - copy_from_sockptr(&cf_sk->conn_req.param.data, ov, ol)) { - release_sock(&cf_sk->sk); - return -EINVAL; - } - cf_sk->conn_req.param.size = ol; - release_sock(&cf_sk->sk); - return 0; - - default: - return -ENOPROTOOPT; - } - - return 0; -bad_sol: - return -ENOPROTOOPT; - -} - -/* - * caif_connect() - Connect a CAIF Socket - * Copied and modified af_irda.c:irda_connect(). - * - * Note : by consulting "errno", the user space caller may learn the cause - * of the failure. Most of them are visible in the function, others may come - * from subroutines called and are listed here : - * o -EAFNOSUPPORT: bad socket family or type. - * o -ESOCKTNOSUPPORT: bad socket type or protocol - * o -EINVAL: bad socket address, or CAIF link type - * o -ECONNREFUSED: remote end refused the connection. - * o -EINPROGRESS: connect request sent but timed out (or non-blocking) - * o -EISCONN: already connected. - * o -ETIMEDOUT: Connection timed out (send timeout) - * o -ENODEV: No link layer to send request - * o -ECONNRESET: Received Shutdown indication or lost link layer - * o -ENOMEM: Out of memory - * - * State Strategy: - * o sk_state: holds the CAIF_* protocol state, it's updated by - * caif_ctrl_cb. - * o sock->state: holds the SS_* socket state and is updated by connect and - * disconnect. - */ -static int caif_connect(struct socket *sock, struct sockaddr_unsized *uaddr, - int addr_len, int flags) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - long timeo; - int err; - int ifindex, headroom, tailroom; - unsigned int mtu; - struct net_device *dev; - - lock_sock(sk); - - err = -EINVAL; - if (addr_len < offsetofend(struct sockaddr, sa_family)) - goto out; - - err = -EAFNOSUPPORT; - if (uaddr->sa_family != AF_CAIF) - goto out; - - switch (sock->state) { - case SS_UNCONNECTED: - /* Normal case, a fresh connect */ - caif_assert(sk->sk_state == CAIF_DISCONNECTED); - break; - case SS_CONNECTING: - switch (sk->sk_state) { - case CAIF_CONNECTED: - sock->state = SS_CONNECTED; - err = -EISCONN; - goto out; - case CAIF_DISCONNECTED: - /* Reconnect allowed */ - break; - case CAIF_CONNECTING: - err = -EALREADY; - if (flags & O_NONBLOCK) - goto out; - goto wait_connect; - } - break; - case SS_CONNECTED: - caif_assert(sk->sk_state == CAIF_CONNECTED || - sk->sk_state == CAIF_DISCONNECTED); - if (sk->sk_shutdown & SHUTDOWN_MASK) { - /* Allow re-connect after SHUTDOWN_IND */ - caif_disconnect_client(sock_net(sk), &cf_sk->layer); - caif_free_client(&cf_sk->layer); - break; - } - /* No reconnect on a seqpacket socket */ - err = -EISCONN; - goto out; - case SS_DISCONNECTING: - case SS_FREE: - caif_assert(1); /*Should never happen */ - break; - } - sk->sk_state = CAIF_DISCONNECTED; - sock->state = SS_UNCONNECTED; - sk_stream_kill_queues(&cf_sk->sk); - - err = -EINVAL; - if (addr_len != sizeof(struct sockaddr_caif)) - goto out; - - memcpy(&cf_sk->conn_req.sockaddr, uaddr, - sizeof(struct sockaddr_caif)); - - /* Move to connecting socket, start sending Connect Requests */ - sock->state = SS_CONNECTING; - sk->sk_state = CAIF_CONNECTING; - - /* Check priority value comming from socket */ - /* if priority value is out of range it will be ajusted */ - if (cf_sk->sk.sk_priority > CAIF_PRIO_MAX) - cf_sk->conn_req.priority = CAIF_PRIO_MAX; - else if (cf_sk->sk.sk_priority < CAIF_PRIO_MIN) - cf_sk->conn_req.priority = CAIF_PRIO_MIN; - else - cf_sk->conn_req.priority = cf_sk->sk.sk_priority; - - /*ifindex = id of the interface.*/ - cf_sk->conn_req.ifindex = cf_sk->sk.sk_bound_dev_if; - - cf_sk->layer.receive = caif_sktrecv_cb; - - err = caif_connect_client(sock_net(sk), &cf_sk->conn_req, - &cf_sk->layer, &ifindex, &headroom, &tailroom); - - if (err < 0) { - cf_sk->sk.sk_socket->state = SS_UNCONNECTED; - cf_sk->sk.sk_state = CAIF_DISCONNECTED; - goto out; - } - - err = -ENODEV; - rcu_read_lock(); - dev = dev_get_by_index_rcu(sock_net(sk), ifindex); - if (!dev) { - rcu_read_unlock(); - goto out; - } - cf_sk->headroom = LL_RESERVED_SPACE_EXTRA(dev, headroom); - mtu = dev->mtu; - rcu_read_unlock(); - - cf_sk->tailroom = tailroom; - cf_sk->maxframe = mtu - (headroom + tailroom); - if (cf_sk->maxframe < 1) { - pr_warn("CAIF Interface MTU too small (%d)\n", dev->mtu); - err = -ENODEV; - goto out; - } - - err = -EINPROGRESS; -wait_connect: - - if (sk->sk_state != CAIF_CONNECTED && (flags & O_NONBLOCK)) - goto out; - - timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); - - release_sock(sk); - err = -ERESTARTSYS; - timeo = wait_event_interruptible_timeout(*sk_sleep(sk), - sk->sk_state != CAIF_CONNECTING, - timeo); - lock_sock(sk); - if (timeo < 0) - goto out; /* -ERESTARTSYS */ - - err = -ETIMEDOUT; - if (timeo == 0 && sk->sk_state != CAIF_CONNECTED) - goto out; - if (sk->sk_state != CAIF_CONNECTED) { - sock->state = SS_UNCONNECTED; - err = sock_error(sk); - if (!err) - err = -ECONNREFUSED; - goto out; - } - sock->state = SS_CONNECTED; - err = 0; -out: - release_sock(sk); - return err; -} - -/* - * caif_release() - Disconnect a CAIF Socket - * Copied and modified af_irda.c:irda_release(). - */ -static int caif_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - - if (!sk) - return 0; - - set_tx_flow_off(cf_sk); - - /* - * Ensure that packets are not queued after this point in time. - * caif_queue_rcv_skb checks SOCK_DEAD holding the queue lock, - * this ensures no packets when sock is dead. - */ - spin_lock_bh(&sk->sk_receive_queue.lock); - sock_set_flag(sk, SOCK_DEAD); - spin_unlock_bh(&sk->sk_receive_queue.lock); - sock->sk = NULL; - - WARN_ON(IS_ERR(cf_sk->debugfs_socket_dir)); - debugfs_remove_recursive(cf_sk->debugfs_socket_dir); - - lock_sock(&(cf_sk->sk)); - sk->sk_state = CAIF_DISCONNECTED; - sk->sk_shutdown = SHUTDOWN_MASK; - - caif_disconnect_client(sock_net(sk), &cf_sk->layer); - cf_sk->sk.sk_socket->state = SS_DISCONNECTING; - wake_up_interruptible_poll(sk_sleep(sk), EPOLLERR|EPOLLHUP); - - sock_orphan(sk); - sk_stream_kill_queues(&cf_sk->sk); - release_sock(sk); - sock_put(sk); - return 0; -} - -/* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */ -static __poll_t caif_poll(struct file *file, - struct socket *sock, poll_table *wait) -{ - struct sock *sk = sock->sk; - __poll_t mask; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - - sock_poll_wait(file, sock, wait); - mask = 0; - - /* exceptional events? */ - if (sk->sk_err) - mask |= EPOLLERR; - if (sk->sk_shutdown == SHUTDOWN_MASK) - mask |= EPOLLHUP; - if (sk->sk_shutdown & RCV_SHUTDOWN) - mask |= EPOLLRDHUP; - - /* readable? */ - if (!skb_queue_empty_lockless(&sk->sk_receive_queue) || - (sk->sk_shutdown & RCV_SHUTDOWN)) - mask |= EPOLLIN | EPOLLRDNORM; - - /* - * we set writable also when the other side has shut down the - * connection. This prevents stuck sockets. - */ - if (sock_writeable(sk) && tx_flow_is_on(cf_sk)) - mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; - - return mask; -} - -static const struct proto_ops caif_seqpacket_ops = { - .family = PF_CAIF, - .owner = THIS_MODULE, - .release = caif_release, - .bind = sock_no_bind, - .connect = caif_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = sock_no_getname, - .poll = caif_poll, - .ioctl = sock_no_ioctl, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = setsockopt, - .sendmsg = caif_seqpkt_sendmsg, - .recvmsg = caif_seqpkt_recvmsg, - .mmap = sock_no_mmap, -}; - -static const struct proto_ops caif_stream_ops = { - .family = PF_CAIF, - .owner = THIS_MODULE, - .release = caif_release, - .bind = sock_no_bind, - .connect = caif_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = sock_no_getname, - .poll = caif_poll, - .ioctl = sock_no_ioctl, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = setsockopt, - .sendmsg = caif_stream_sendmsg, - .recvmsg = caif_stream_recvmsg, - .mmap = sock_no_mmap, -}; - -/* This function is called when a socket is finally destroyed. */ -static void caif_sock_destructor(struct sock *sk) -{ - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - caif_assert(!refcount_read(&sk->sk_wmem_alloc)); - caif_assert(sk_unhashed(sk)); - caif_assert(!sk->sk_socket); - if (!sock_flag(sk, SOCK_DEAD)) { - pr_debug("Attempt to release alive CAIF socket: %p\n", sk); - return; - } - sk_stream_kill_queues(&cf_sk->sk); - WARN_ON_ONCE(sk->sk_forward_alloc); - caif_free_client(&cf_sk->layer); -} - -static int caif_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk = NULL; - struct caifsock *cf_sk = NULL; - static struct proto prot = {.name = "PF_CAIF", - .owner = THIS_MODULE, - .obj_size = sizeof(struct caifsock), - .useroffset = offsetof(struct caifsock, conn_req.param), - .usersize = sizeof_field(struct caifsock, conn_req.param) - }; - - if (!capable(CAP_SYS_ADMIN) && !capable(CAP_NET_ADMIN)) - return -EPERM; - /* - * The sock->type specifies the socket type to use. - * The CAIF socket is a packet stream in the sense - * that it is packet based. CAIF trusts the reliability - * of the link, no resending is implemented. - */ - if (sock->type == SOCK_SEQPACKET) - sock->ops = &caif_seqpacket_ops; - else if (sock->type == SOCK_STREAM) - sock->ops = &caif_stream_ops; - else - return -ESOCKTNOSUPPORT; - - if (protocol < 0 || protocol >= CAIFPROTO_MAX) - return -EPROTONOSUPPORT; - /* - * Set the socket state to unconnected. The socket state - * is really not used at all in the net/core or socket.c but the - * initialization makes sure that sock->state is not uninitialized. - */ - sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot, kern); - if (!sk) - return -ENOMEM; - - cf_sk = container_of(sk, struct caifsock, sk); - - /* Store the protocol */ - sk->sk_protocol = (unsigned char) protocol; - - /* Initialize default priority for well-known cases */ - switch (protocol) { - case CAIFPROTO_AT: - sk->sk_priority = TC_PRIO_CONTROL; - break; - case CAIFPROTO_RFM: - sk->sk_priority = TC_PRIO_INTERACTIVE_BULK; - break; - default: - sk->sk_priority = TC_PRIO_BESTEFFORT; - } - - /* - * Lock in order to try to stop someone from opening the socket - * too early. - */ - lock_sock(&(cf_sk->sk)); - - /* Initialize the nozero default sock structure data. */ - sock_init_data(sock, sk); - sk->sk_destruct = caif_sock_destructor; - - mutex_init(&cf_sk->readlock); /* single task reading lock */ - cf_sk->layer.ctrlcmd = caif_ctrl_cb; - cf_sk->sk.sk_socket->state = SS_UNCONNECTED; - cf_sk->sk.sk_state = CAIF_DISCONNECTED; - - set_tx_flow_off(cf_sk); - set_rx_flow_on(cf_sk); - - /* Set default options on configuration */ - cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY; - cf_sk->conn_req.protocol = protocol; - release_sock(&cf_sk->sk); - return 0; -} - - -static const struct net_proto_family caif_family_ops = { - .family = PF_CAIF, - .create = caif_create, - .owner = THIS_MODULE, -}; - -static int __init caif_sktinit_module(void) -{ - return sock_register(&caif_family_ops); -} - -static void __exit caif_sktexit_module(void) -{ - sock_unregister(PF_CAIF); -} -module_init(caif_sktinit_module); -module_exit(caif_sktexit_module); diff --git a/net/caif/caif_usb.c b/net/caif/caif_usb.c deleted file mode 100644 index 4d44960d4c2f..000000000000 --- a/net/caif/caif_usb.c +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * CAIF USB handler - * Copyright (C) ST-Ericsson AB 2011 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol USB support"); -MODULE_LICENSE("GPL"); - -#define CFUSB_PAD_DESCR_SZ 1 /* Alignment descriptor length */ -#define CFUSB_ALIGNMENT 4 /* Number of bytes to align. */ -#define CFUSB_MAX_HEADLEN (CFUSB_PAD_DESCR_SZ + CFUSB_ALIGNMENT-1) -#define STE_USB_VID 0x04cc /* USB Product ID for ST-Ericsson */ -#define STE_USB_PID_CAIF 0x230f /* Product id for CAIF Modems */ - -struct cfusbl { - struct cflayer layer; - u8 tx_eth_hdr[ETH_HLEN]; -}; - -static bool pack_added; - -static int cfusbl_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 hpad; - - /* Remove padding. */ - cfpkt_extr_head(pkt, &hpad, 1); - cfpkt_extr_head(pkt, NULL, hpad); - return layr->up->receive(layr->up, pkt); -} - -static int cfusbl_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - struct caif_payload_info *info; - u8 hpad; - u8 zeros[CFUSB_ALIGNMENT]; - struct sk_buff *skb; - struct cfusbl *usbl = container_of(layr, struct cfusbl, layer); - - skb = cfpkt_tonative(pkt); - - skb_reset_network_header(skb); - skb->protocol = htons(ETH_P_IP); - - info = cfpkt_info(pkt); - hpad = (info->hdr_len + CFUSB_PAD_DESCR_SZ) & (CFUSB_ALIGNMENT - 1); - - if (skb_headroom(skb) < ETH_HLEN + CFUSB_PAD_DESCR_SZ + hpad) { - pr_warn("Headroom too small\n"); - kfree_skb(skb); - return -EIO; - } - memset(zeros, 0, hpad); - - cfpkt_add_head(pkt, zeros, hpad); - cfpkt_add_head(pkt, &hpad, 1); - cfpkt_add_head(pkt, usbl->tx_eth_hdr, sizeof(usbl->tx_eth_hdr)); - return layr->dn->transmit(layr->dn, pkt); -} - -static void cfusbl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid) -{ - if (layr->up && layr->up->ctrlcmd) - layr->up->ctrlcmd(layr->up, ctrl, layr->id); -} - -static struct cflayer *cfusbl_create(int phyid, const u8 ethaddr[ETH_ALEN], - u8 braddr[ETH_ALEN]) -{ - struct cfusbl *this = kmalloc_obj(struct cfusbl, GFP_ATOMIC); - - if (!this) - return NULL; - - caif_assert(offsetof(struct cfusbl, layer) == 0); - - memset(&this->layer, 0, sizeof(this->layer)); - this->layer.receive = cfusbl_receive; - this->layer.transmit = cfusbl_transmit; - this->layer.ctrlcmd = cfusbl_ctrlcmd; - snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "usb%d", phyid); - this->layer.id = phyid; - - /* - * Construct TX ethernet header: - * 0-5 destination address - * 5-11 source address - * 12-13 protocol type - */ - ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], braddr); - ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], ethaddr); - this->tx_eth_hdr[12] = cpu_to_be16(ETH_P_802_EX1) & 0xff; - this->tx_eth_hdr[13] = (cpu_to_be16(ETH_P_802_EX1) >> 8) & 0xff; - pr_debug("caif ethernet TX-header dst:%pM src:%pM type:%02x%02x\n", - this->tx_eth_hdr, this->tx_eth_hdr + ETH_ALEN, - this->tx_eth_hdr[12], this->tx_eth_hdr[13]); - - return (struct cflayer *) this; -} - -static void cfusbl_release(struct cflayer *layer) -{ - kfree(layer); -} - -static struct packet_type caif_usb_type __read_mostly = { - .type = cpu_to_be16(ETH_P_802_EX1), -}; - -static int cfusbl_device_notify(struct notifier_block *me, unsigned long what, - void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct caif_dev_common common; - struct cflayer *layer, *link_support; - struct usbnet *usbnet; - struct usb_device *usbdev; - int res; - - if (what == NETDEV_UNREGISTER && dev->reg_state >= NETREG_UNREGISTERED) - return 0; - - /* Check whether we have a NCM device, and find its VID/PID. */ - if (!(dev->dev.parent && dev->dev.parent->driver && - strcmp(dev->dev.parent->driver->name, "cdc_ncm") == 0)) - return 0; - - usbnet = netdev_priv(dev); - usbdev = usbnet->udev; - - pr_debug("USB CDC NCM device VID:0x%4x PID:0x%4x\n", - le16_to_cpu(usbdev->descriptor.idVendor), - le16_to_cpu(usbdev->descriptor.idProduct)); - - /* Check for VID/PID that supports CAIF */ - if (!(le16_to_cpu(usbdev->descriptor.idVendor) == STE_USB_VID && - le16_to_cpu(usbdev->descriptor.idProduct) == STE_USB_PID_CAIF)) - return 0; - - if (what == NETDEV_UNREGISTER) - module_put(THIS_MODULE); - - if (what != NETDEV_REGISTER) - return 0; - - __module_get(THIS_MODULE); - - memset(&common, 0, sizeof(common)); - common.use_frag = false; - common.use_fcs = false; - common.use_stx = false; - common.link_select = CAIF_LINK_HIGH_BANDW; - common.flowctrl = NULL; - - link_support = cfusbl_create(dev->ifindex, dev->dev_addr, - dev->broadcast); - - if (!link_support) - return -ENOMEM; - - if (dev->num_tx_queues > 1) - pr_warn("USB device uses more than one tx queue\n"); - - res = caif_enroll_dev(dev, &common, link_support, CFUSB_MAX_HEADLEN, - &layer, &caif_usb_type.func); - if (res) - goto err; - - if (!pack_added) - dev_add_pack(&caif_usb_type); - pack_added = true; - - strscpy(layer->name, dev->name, sizeof(layer->name)); - - return 0; -err: - cfusbl_release(link_support); - return res; -} - -static struct notifier_block caif_device_notifier = { - .notifier_call = cfusbl_device_notify, - .priority = 0, -}; - -static int __init cfusbl_init(void) -{ - return register_netdevice_notifier(&caif_device_notifier); -} - -static void __exit cfusbl_exit(void) -{ - unregister_netdevice_notifier(&caif_device_notifier); - dev_remove_pack(&caif_usb_type); -} - -module_init(cfusbl_init); -module_exit(cfusbl_exit); diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c deleted file mode 100644 index 8a80914783e8..000000000000 --- a/net/caif/cfcnfg.c +++ /dev/null @@ -1,612 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) container_of(layr, struct cfcnfg, layer) - -/* Information about CAIF physical interfaces held by Config Module in order - * to manage physical interfaces - */ -struct cfcnfg_phyinfo { - struct list_head node; - bool up; - - /* Pointer to the layer below the MUX (framing layer) */ - struct cflayer *frm_layer; - /* Pointer to the lowest actual physical layer */ - struct cflayer *phy_layer; - /* Unique identifier of the physical interface */ - unsigned int id; - /* Preference of the physical in interface */ - enum cfcnfg_phy_preference pref; - - /* Information about the physical device */ - struct dev_info dev_info; - - /* Interface index */ - int ifindex; - - /* Protocol head room added for CAIF link layer */ - int head_room; - - /* Use Start of frame checksum */ - bool use_fcs; -}; - -struct cfcnfg { - struct cflayer layer; - struct cflayer *ctrl; - struct cflayer *mux; - struct list_head phys; - struct mutex lock; -}; - -static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, - enum cfctrl_srv serv, u8 phyid, - struct cflayer *adapt_layer); -static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id); -static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, - struct cflayer *adapt_layer); -static void cfctrl_resp_func(void); -static void cfctrl_enum_resp(void); - -struct cfcnfg *cfcnfg_create(void) -{ - struct cfcnfg *this; - struct cfctrl_rsp *resp; - - might_sleep(); - - /* Initiate this layer */ - this = kzalloc_obj(struct cfcnfg, GFP_ATOMIC); - if (!this) - return NULL; - this->mux = cfmuxl_create(); - if (!this->mux) - goto out_of_mem; - this->ctrl = cfctrl_create(); - if (!this->ctrl) - goto out_of_mem; - /* Initiate response functions */ - resp = cfctrl_get_respfuncs(this->ctrl); - resp->enum_rsp = cfctrl_enum_resp; - resp->linkerror_ind = cfctrl_resp_func; - resp->linkdestroy_rsp = cfcnfg_linkdestroy_rsp; - resp->sleep_rsp = cfctrl_resp_func; - resp->wake_rsp = cfctrl_resp_func; - resp->restart_rsp = cfctrl_resp_func; - resp->radioset_rsp = cfctrl_resp_func; - resp->linksetup_rsp = cfcnfg_linkup_rsp; - resp->reject_rsp = cfcnfg_reject_rsp; - INIT_LIST_HEAD(&this->phys); - - cfmuxl_set_uplayer(this->mux, this->ctrl, 0); - layer_set_dn(this->ctrl, this->mux); - layer_set_up(this->ctrl, this); - mutex_init(&this->lock); - - return this; -out_of_mem: - synchronize_rcu(); - - kfree(this->mux); - kfree(this->ctrl); - kfree(this); - return NULL; -} - -void cfcnfg_remove(struct cfcnfg *cfg) -{ - might_sleep(); - if (cfg) { - synchronize_rcu(); - - kfree(cfg->mux); - cfctrl_remove(cfg->ctrl); - kfree(cfg); - } -} - -static void cfctrl_resp_func(void) -{ -} - -static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg, - u8 phyid) -{ - struct cfcnfg_phyinfo *phy; - - list_for_each_entry_rcu(phy, &cnfg->phys, node) - if (phy->id == phyid) - return phy; - return NULL; -} - -static void cfctrl_enum_resp(void) -{ -} - -static struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg, - enum cfcnfg_phy_preference phy_pref) -{ - /* Try to match with specified preference */ - struct cfcnfg_phyinfo *phy; - - list_for_each_entry_rcu(phy, &cnfg->phys, node) { - if (phy->up && phy->pref == phy_pref && - phy->frm_layer != NULL) - - return &phy->dev_info; - } - - /* Otherwise just return something */ - list_for_each_entry_rcu(phy, &cnfg->phys, node) - if (phy->up) - return &phy->dev_info; - - return NULL; -} - -static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi) -{ - struct cfcnfg_phyinfo *phy; - - list_for_each_entry_rcu(phy, &cnfg->phys, node) - if (phy->ifindex == ifi && phy->up) - return phy->id; - return -ENODEV; -} - -int caif_disconnect_client(struct net *net, struct cflayer *adap_layer) -{ - u8 channel_id; - struct cfcnfg *cfg = get_cfcnfg(net); - - caif_assert(adap_layer != NULL); - cfctrl_cancel_req(cfg->ctrl, adap_layer); - channel_id = adap_layer->id; - if (channel_id != 0) { - struct cflayer *servl; - servl = cfmuxl_remove_uplayer(cfg->mux, channel_id); - cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer); - if (servl != NULL) - layer_set_up(servl, NULL); - } else - pr_debug("nothing to disconnect\n"); - - /* Do RCU sync before initiating cleanup */ - synchronize_rcu(); - if (adap_layer->ctrlcmd != NULL) - adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); - return 0; - -} -EXPORT_SYMBOL(caif_disconnect_client); - -static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id) -{ -} - -static const int protohead[CFCTRL_SRV_MASK] = { - [CFCTRL_SRV_VEI] = 4, - [CFCTRL_SRV_DATAGRAM] = 7, - [CFCTRL_SRV_UTIL] = 4, - [CFCTRL_SRV_RFM] = 3, - [CFCTRL_SRV_DBG] = 3, -}; - - -static int caif_connect_req_to_link_param(struct cfcnfg *cnfg, - struct caif_connect_request *s, - struct cfctrl_link_param *l) -{ - struct dev_info *dev_info; - enum cfcnfg_phy_preference pref; - int res; - - memset(l, 0, sizeof(*l)); - /* In caif protocol low value is high priority */ - l->priority = CAIF_PRIO_MAX - s->priority + 1; - - if (s->ifindex != 0) { - res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex); - if (res < 0) - return res; - l->phyid = res; - } else { - switch (s->link_selector) { - case CAIF_LINK_HIGH_BANDW: - pref = CFPHYPREF_HIGH_BW; - break; - case CAIF_LINK_LOW_LATENCY: - pref = CFPHYPREF_LOW_LAT; - break; - default: - return -EINVAL; - } - dev_info = cfcnfg_get_phyid(cnfg, pref); - if (dev_info == NULL) - return -ENODEV; - l->phyid = dev_info->id; - } - switch (s->protocol) { - case CAIFPROTO_AT: - l->linktype = CFCTRL_SRV_VEI; - l->endpoint = (s->sockaddr.u.at.type >> 2) & 0x3; - l->chtype = s->sockaddr.u.at.type & 0x3; - break; - case CAIFPROTO_DATAGRAM: - l->linktype = CFCTRL_SRV_DATAGRAM; - l->chtype = 0x00; - l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; - break; - case CAIFPROTO_DATAGRAM_LOOP: - l->linktype = CFCTRL_SRV_DATAGRAM; - l->chtype = 0x03; - l->endpoint = 0x00; - l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; - break; - case CAIFPROTO_RFM: - l->linktype = CFCTRL_SRV_RFM; - l->u.datagram.connid = s->sockaddr.u.rfm.connection_id; - strscpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume, - sizeof(l->u.rfm.volume)); - break; - case CAIFPROTO_UTIL: - l->linktype = CFCTRL_SRV_UTIL; - l->endpoint = 0x00; - l->chtype = 0x00; - strscpy(l->u.utility.name, s->sockaddr.u.util.service, - sizeof(l->u.utility.name)); - caif_assert(sizeof(l->u.utility.name) > 10); - l->u.utility.paramlen = s->param.size; - if (l->u.utility.paramlen > sizeof(l->u.utility.params)) - l->u.utility.paramlen = sizeof(l->u.utility.params); - - memcpy(l->u.utility.params, s->param.data, - l->u.utility.paramlen); - - break; - case CAIFPROTO_DEBUG: - l->linktype = CFCTRL_SRV_DBG; - l->endpoint = s->sockaddr.u.dbg.service; - l->chtype = s->sockaddr.u.dbg.type; - break; - default: - return -EINVAL; - } - return 0; -} - -int caif_connect_client(struct net *net, struct caif_connect_request *conn_req, - struct cflayer *adap_layer, int *ifindex, - int *proto_head, int *proto_tail) -{ - struct cflayer *frml; - struct cfcnfg_phyinfo *phy; - int err; - struct cfctrl_link_param param; - struct cfcnfg *cfg = get_cfcnfg(net); - - rcu_read_lock(); - err = caif_connect_req_to_link_param(cfg, conn_req, ¶m); - if (err) - goto unlock; - - phy = cfcnfg_get_phyinfo_rcu(cfg, param.phyid); - if (!phy) { - err = -ENODEV; - goto unlock; - } - err = -EINVAL; - - if (adap_layer == NULL) { - pr_err("adap_layer is zero\n"); - goto unlock; - } - if (adap_layer->receive == NULL) { - pr_err("adap_layer->receive is NULL\n"); - goto unlock; - } - if (adap_layer->ctrlcmd == NULL) { - pr_err("adap_layer->ctrlcmd == NULL\n"); - goto unlock; - } - - err = -ENODEV; - frml = phy->frm_layer; - if (frml == NULL) { - pr_err("Specified PHY type does not exist!\n"); - goto unlock; - } - caif_assert(param.phyid == phy->id); - caif_assert(phy->frm_layer->id == - param.phyid); - caif_assert(phy->phy_layer->id == - param.phyid); - - *ifindex = phy->ifindex; - *proto_tail = 2; - *proto_head = protohead[param.linktype] + phy->head_room; - - rcu_read_unlock(); - - /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */ - cfctrl_enum_req(cfg->ctrl, param.phyid); - return cfctrl_linkup_request(cfg->ctrl, ¶m, adap_layer); - -unlock: - rcu_read_unlock(); - return err; -} -EXPORT_SYMBOL(caif_connect_client); - -static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, - struct cflayer *adapt_layer) -{ - if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL) - adapt_layer->ctrlcmd(adapt_layer, - CAIF_CTRLCMD_INIT_FAIL_RSP, 0); -} - -static void -cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, - u8 phyid, struct cflayer *adapt_layer) -{ - struct cfcnfg *cnfg = container_obj(layer); - struct cflayer *servicel = NULL; - struct cfcnfg_phyinfo *phyinfo; - struct net_device *netdev; - - if (channel_id == 0) { - pr_warn("received channel_id zero\n"); - if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL) - adapt_layer->ctrlcmd(adapt_layer, - CAIF_CTRLCMD_INIT_FAIL_RSP, 0); - return; - } - - rcu_read_lock(); - - if (adapt_layer == NULL) { - pr_debug("link setup response but no client exist, send linkdown back\n"); - cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL); - goto unlock; - } - - caif_assert(cnfg != NULL); - caif_assert(phyid != 0); - - phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); - if (phyinfo == NULL) { - pr_err("ERROR: Link Layer Device disappeared while connecting\n"); - goto unlock; - } - - caif_assert(phyinfo != NULL); - caif_assert(phyinfo->id == phyid); - caif_assert(phyinfo->phy_layer != NULL); - caif_assert(phyinfo->phy_layer->id == phyid); - - adapt_layer->id = channel_id; - - switch (serv) { - case CFCTRL_SRV_VEI: - servicel = cfvei_create(channel_id, &phyinfo->dev_info); - break; - case CFCTRL_SRV_DATAGRAM: - servicel = cfdgml_create(channel_id, - &phyinfo->dev_info); - break; - case CFCTRL_SRV_RFM: - netdev = phyinfo->dev_info.dev; - servicel = cfrfml_create(channel_id, &phyinfo->dev_info, - netdev->mtu); - break; - case CFCTRL_SRV_UTIL: - servicel = cfutill_create(channel_id, &phyinfo->dev_info); - break; - case CFCTRL_SRV_VIDEO: - servicel = cfvidl_create(channel_id, &phyinfo->dev_info); - break; - case CFCTRL_SRV_DBG: - servicel = cfdbgl_create(channel_id, &phyinfo->dev_info); - break; - default: - pr_err("Protocol error. Link setup response - unknown channel type\n"); - goto unlock; - } - if (!servicel) - goto unlock; - layer_set_dn(servicel, cnfg->mux); - cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id); - layer_set_up(servicel, adapt_layer); - layer_set_dn(adapt_layer, servicel); - - rcu_read_unlock(); - - servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0); - return; -unlock: - rcu_read_unlock(); -} - -int -cfcnfg_add_phy_layer(struct cfcnfg *cnfg, - struct net_device *dev, struct cflayer *phy_layer, - enum cfcnfg_phy_preference pref, - struct cflayer *link_support, - bool fcs, int head_room) -{ - struct cflayer *frml; - struct cfcnfg_phyinfo *phyinfo = NULL; - int i, res = 0; - u8 phyid; - - mutex_lock(&cnfg->lock); - - /* CAIF protocol allow maximum 6 link-layers */ - for (i = 0; i < 7; i++) { - phyid = (dev->ifindex + i) & 0x7; - if (phyid == 0) - continue; - if (cfcnfg_get_phyinfo_rcu(cnfg, phyid) == NULL) - goto got_phyid; - } - pr_warn("Too many CAIF Link Layers (max 6)\n"); - res = -EEXIST; - goto out; - -got_phyid: - phyinfo = kzalloc_obj(struct cfcnfg_phyinfo, GFP_ATOMIC); - if (!phyinfo) { - res = -ENOMEM; - goto out; - } - - phy_layer->id = phyid; - phyinfo->pref = pref; - phyinfo->id = phyid; - phyinfo->dev_info.id = phyid; - phyinfo->dev_info.dev = dev; - phyinfo->phy_layer = phy_layer; - phyinfo->ifindex = dev->ifindex; - phyinfo->head_room = head_room; - phyinfo->use_fcs = fcs; - - frml = cffrml_create(phyid, fcs); - - if (!frml) { - res = -ENOMEM; - goto out_err; - } - phyinfo->frm_layer = frml; - layer_set_up(frml, cnfg->mux); - - if (link_support != NULL) { - link_support->id = phyid; - layer_set_dn(frml, link_support); - layer_set_up(link_support, frml); - layer_set_dn(link_support, phy_layer); - layer_set_up(phy_layer, link_support); - } else { - layer_set_dn(frml, phy_layer); - layer_set_up(phy_layer, frml); - } - - list_add_rcu(&phyinfo->node, &cnfg->phys); -out: - mutex_unlock(&cnfg->lock); - return res; - -out_err: - kfree(phyinfo); - mutex_unlock(&cnfg->lock); - return res; -} -EXPORT_SYMBOL(cfcnfg_add_phy_layer); - -int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer, - bool up) -{ - struct cfcnfg_phyinfo *phyinfo; - - rcu_read_lock(); - phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phy_layer->id); - if (phyinfo == NULL) { - rcu_read_unlock(); - return -ENODEV; - } - - if (phyinfo->up == up) { - rcu_read_unlock(); - return 0; - } - phyinfo->up = up; - - if (up) { - cffrml_hold(phyinfo->frm_layer); - cfmuxl_set_dnlayer(cnfg->mux, phyinfo->frm_layer, - phy_layer->id); - } else { - cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id); - cffrml_put(phyinfo->frm_layer); - } - - rcu_read_unlock(); - return 0; -} -EXPORT_SYMBOL(cfcnfg_set_phy_state); - -int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer) -{ - struct cflayer *frml, *frml_dn; - u16 phyid; - struct cfcnfg_phyinfo *phyinfo; - - might_sleep(); - - mutex_lock(&cnfg->lock); - - phyid = phy_layer->id; - phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); - - if (phyinfo == NULL) { - mutex_unlock(&cnfg->lock); - return 0; - } - caif_assert(phyid == phyinfo->id); - caif_assert(phy_layer == phyinfo->phy_layer); - caif_assert(phy_layer->id == phyid); - caif_assert(phyinfo->frm_layer->id == phyid); - - list_del_rcu(&phyinfo->node); - synchronize_rcu(); - - /* Fail if reference count is not zero */ - if (cffrml_refcnt_read(phyinfo->frm_layer) != 0) { - pr_info("Wait for device inuse\n"); - list_add_rcu(&phyinfo->node, &cnfg->phys); - mutex_unlock(&cnfg->lock); - return -EAGAIN; - } - - frml = phyinfo->frm_layer; - frml_dn = frml->dn; - cffrml_set_uplayer(frml, NULL); - cffrml_set_dnlayer(frml, NULL); - if (phy_layer != frml_dn) { - layer_set_up(frml_dn, NULL); - layer_set_dn(frml_dn, NULL); - } - layer_set_up(phy_layer, NULL); - - if (phyinfo->phy_layer != frml_dn) - kfree(frml_dn); - - cffrml_free(frml); - kfree(phyinfo); - mutex_unlock(&cnfg->lock); - - return 0; -} -EXPORT_SYMBOL(cfcnfg_del_phy_layer); diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c deleted file mode 100644 index c6cc2bfed65d..000000000000 --- a/net/caif/cfctrl.c +++ /dev/null @@ -1,631 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) container_of(layr, struct cfctrl, serv.layer) -#define UTILITY_NAME_LENGTH 16 -#define CFPKT_CTRL_PKT_LEN 20 - -#ifdef CAIF_NO_LOOP -static int handle_loop(struct cfctrl *ctrl, - int cmd, struct cfpkt *pkt){ - return -1; -} -#else -static int handle_loop(struct cfctrl *ctrl, - int cmd, struct cfpkt *pkt); -#endif -static int cfctrl_recv(struct cflayer *layr, struct cfpkt *pkt); -static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid); - - -struct cflayer *cfctrl_create(void) -{ - struct dev_info dev_info; - struct cfctrl *this = - kzalloc_obj(struct cfctrl, GFP_ATOMIC); - if (!this) - return NULL; - caif_assert(offsetof(struct cfctrl, serv.layer) == 0); - memset(&dev_info, 0, sizeof(dev_info)); - dev_info.id = 0xff; - cfsrvl_init(&this->serv, 0, &dev_info, false); - atomic_set(&this->req_seq_no, 1); - atomic_set(&this->rsp_seq_no, 1); - this->serv.layer.receive = cfctrl_recv; - sprintf(this->serv.layer.name, "ctrl"); - this->serv.layer.ctrlcmd = cfctrl_ctrlcmd; -#ifndef CAIF_NO_LOOP - spin_lock_init(&this->loop_linkid_lock); - this->loop_linkid = 1; -#endif - spin_lock_init(&this->info_list_lock); - INIT_LIST_HEAD(&this->list); - return &this->serv.layer; -} - -void cfctrl_remove(struct cflayer *layer) -{ - struct cfctrl_request_info *p, *tmp; - struct cfctrl *ctrl = container_obj(layer); - - spin_lock_bh(&ctrl->info_list_lock); - list_for_each_entry_safe(p, tmp, &ctrl->list, list) { - list_del(&p->list); - kfree(p); - } - spin_unlock_bh(&ctrl->info_list_lock); - kfree(layer); -} - -static bool param_eq(const struct cfctrl_link_param *p1, - const struct cfctrl_link_param *p2) -{ - bool eq = - p1->linktype == p2->linktype && - p1->priority == p2->priority && - p1->phyid == p2->phyid && - p1->endpoint == p2->endpoint && p1->chtype == p2->chtype; - - if (!eq) - return false; - - switch (p1->linktype) { - case CFCTRL_SRV_VEI: - return true; - case CFCTRL_SRV_DATAGRAM: - return p1->u.datagram.connid == p2->u.datagram.connid; - case CFCTRL_SRV_RFM: - return - p1->u.rfm.connid == p2->u.rfm.connid && - strcmp(p1->u.rfm.volume, p2->u.rfm.volume) == 0; - case CFCTRL_SRV_UTIL: - return - p1->u.utility.fifosize_kb == p2->u.utility.fifosize_kb - && p1->u.utility.fifosize_bufs == - p2->u.utility.fifosize_bufs - && strcmp(p1->u.utility.name, p2->u.utility.name) == 0 - && p1->u.utility.paramlen == p2->u.utility.paramlen - && memcmp(p1->u.utility.params, p2->u.utility.params, - p1->u.utility.paramlen) == 0; - - case CFCTRL_SRV_VIDEO: - return p1->u.video.connid == p2->u.video.connid; - case CFCTRL_SRV_DBG: - return true; - case CFCTRL_SRV_DECM: - return false; - default: - return false; - } - return false; -} - -static bool cfctrl_req_eq(const struct cfctrl_request_info *r1, - const struct cfctrl_request_info *r2) -{ - if (r1->cmd != r2->cmd) - return false; - if (r1->cmd == CFCTRL_CMD_LINK_SETUP) - return param_eq(&r1->param, &r2->param); - else - return r1->channel_id == r2->channel_id; -} - -/* Insert request at the end */ -static void cfctrl_insert_req(struct cfctrl *ctrl, - struct cfctrl_request_info *req) -{ - spin_lock_bh(&ctrl->info_list_lock); - atomic_inc(&ctrl->req_seq_no); - req->sequence_no = atomic_read(&ctrl->req_seq_no); - list_add_tail(&req->list, &ctrl->list); - spin_unlock_bh(&ctrl->info_list_lock); -} - -/* Compare and remove request */ -static struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl, - struct cfctrl_request_info *req) -{ - struct cfctrl_request_info *p, *tmp, *first; - - first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list); - - list_for_each_entry_safe(p, tmp, &ctrl->list, list) { - if (cfctrl_req_eq(req, p)) { - if (p != first) - pr_warn("Requests are not received in order\n"); - - atomic_set(&ctrl->rsp_seq_no, - p->sequence_no); - list_del(&p->list); - goto out; - } - } - p = NULL; -out: - return p; -} - -struct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer) -{ - struct cfctrl *this = container_obj(layer); - return &this->res; -} - -static void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl) -{ - info->hdr_len = 0; - info->channel_id = cfctrl->serv.layer.id; - info->dev_info = &cfctrl->serv.dev_info; -} - -void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid) -{ - struct cfpkt *pkt; - struct cfctrl *cfctrl = container_obj(layer); - struct cflayer *dn = cfctrl->serv.layer.dn; - - if (!dn) { - pr_debug("not able to send enum request\n"); - return; - } - pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); - if (!pkt) - return; - caif_assert(offsetof(struct cfctrl, serv.layer) == 0); - init_info(cfpkt_info(pkt), cfctrl); - cfpkt_info(pkt)->dev_info->id = physlinkid; - cfctrl->serv.dev_info.id = physlinkid; - cfpkt_addbdy(pkt, CFCTRL_CMD_ENUM); - cfpkt_addbdy(pkt, physlinkid); - cfpkt_set_prio(pkt, TC_PRIO_CONTROL); - dn->transmit(dn, pkt); -} - -int cfctrl_linkup_request(struct cflayer *layer, - struct cfctrl_link_param *param, - struct cflayer *user_layer) -{ - struct cfctrl *cfctrl = container_obj(layer); - struct cflayer *dn = cfctrl->serv.layer.dn; - char utility_name[UTILITY_NAME_LENGTH]; - struct cfctrl_request_info *req; - struct cfpkt *pkt; - u32 tmp32; - u16 tmp16; - u8 tmp8; - int ret; - - if (!dn) { - pr_debug("not able to send linkup request\n"); - return -ENODEV; - } - - if (cfctrl_cancel_req(layer, user_layer) > 0) { - /* Slight Paranoia, check if already connecting */ - pr_err("Duplicate connect request for same client\n"); - WARN_ON(1); - return -EALREADY; - } - - pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); - if (!pkt) - return -ENOMEM; - cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP); - cfpkt_addbdy(pkt, (param->chtype << 4) | param->linktype); - cfpkt_addbdy(pkt, (param->priority << 3) | param->phyid); - cfpkt_addbdy(pkt, param->endpoint & 0x03); - - switch (param->linktype) { - case CFCTRL_SRV_VEI: - break; - case CFCTRL_SRV_VIDEO: - cfpkt_addbdy(pkt, (u8) param->u.video.connid); - break; - case CFCTRL_SRV_DBG: - break; - case CFCTRL_SRV_DATAGRAM: - tmp32 = cpu_to_le32(param->u.datagram.connid); - cfpkt_add_body(pkt, &tmp32, 4); - break; - case CFCTRL_SRV_RFM: - /* Construct a frame, convert DatagramConnectionID to network - * format long and copy it out... - */ - tmp32 = cpu_to_le32(param->u.rfm.connid); - cfpkt_add_body(pkt, &tmp32, 4); - /* Add volume name, including zero termination... */ - cfpkt_add_body(pkt, param->u.rfm.volume, - strlen(param->u.rfm.volume) + 1); - break; - case CFCTRL_SRV_UTIL: - tmp16 = cpu_to_le16(param->u.utility.fifosize_kb); - cfpkt_add_body(pkt, &tmp16, 2); - tmp16 = cpu_to_le16(param->u.utility.fifosize_bufs); - cfpkt_add_body(pkt, &tmp16, 2); - strscpy_pad(utility_name, param->u.utility.name); - cfpkt_add_body(pkt, utility_name, UTILITY_NAME_LENGTH); - tmp8 = param->u.utility.paramlen; - cfpkt_add_body(pkt, &tmp8, 1); - cfpkt_add_body(pkt, param->u.utility.params, - param->u.utility.paramlen); - break; - default: - pr_warn("Request setup of bad link type = %d\n", - param->linktype); - cfpkt_destroy(pkt); - return -EINVAL; - } - req = kzalloc_obj(*req); - if (!req) { - cfpkt_destroy(pkt); - return -ENOMEM; - } - - req->client_layer = user_layer; - req->cmd = CFCTRL_CMD_LINK_SETUP; - req->param = *param; - cfctrl_insert_req(cfctrl, req); - init_info(cfpkt_info(pkt), cfctrl); - /* - * NOTE:Always send linkup and linkdown request on the same - * device as the payload. Otherwise old queued up payload - * might arrive with the newly allocated channel ID. - */ - cfpkt_info(pkt)->dev_info->id = param->phyid; - cfpkt_set_prio(pkt, TC_PRIO_CONTROL); - ret = - dn->transmit(dn, pkt); - if (ret < 0) { - int count; - - count = cfctrl_cancel_req(&cfctrl->serv.layer, - user_layer); - if (count != 1) { - pr_err("Could not remove request (%d)", count); - return -ENODEV; - } - } - return 0; -} - -int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid, - struct cflayer *client) -{ - int ret; - struct cfpkt *pkt; - struct cfctrl *cfctrl = container_obj(layer); - struct cflayer *dn = cfctrl->serv.layer.dn; - - if (!dn) { - pr_debug("not able to send link-down request\n"); - return -ENODEV; - } - pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); - if (!pkt) - return -ENOMEM; - cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY); - cfpkt_addbdy(pkt, channelid); - init_info(cfpkt_info(pkt), cfctrl); - cfpkt_set_prio(pkt, TC_PRIO_CONTROL); - ret = - dn->transmit(dn, pkt); -#ifndef CAIF_NO_LOOP - cfctrl->loop_linkused[channelid] = 0; -#endif - return ret; -} - -int cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer) -{ - struct cfctrl_request_info *p, *tmp; - struct cfctrl *ctrl = container_obj(layr); - int found = 0; - spin_lock_bh(&ctrl->info_list_lock); - - list_for_each_entry_safe(p, tmp, &ctrl->list, list) { - if (p->client_layer == adap_layer) { - list_del(&p->list); - kfree(p); - found++; - } - } - - spin_unlock_bh(&ctrl->info_list_lock); - return found; -} - -static int cfctrl_link_setup(struct cfctrl *cfctrl, struct cfpkt *pkt, u8 cmdrsp) -{ - u8 len; - u8 linkid = 0; - enum cfctrl_srv serv; - enum cfctrl_srv servtype; - u8 endpoint; - u8 physlinkid; - u8 prio; - u8 tmp; - u8 *cp; - int i; - struct cfctrl_link_param linkparam; - struct cfctrl_request_info rsp, *req; - - memset(&linkparam, 0, sizeof(linkparam)); - - tmp = cfpkt_extr_head_u8(pkt); - - serv = tmp & CFCTRL_SRV_MASK; - linkparam.linktype = serv; - - servtype = tmp >> 4; - linkparam.chtype = servtype; - - tmp = cfpkt_extr_head_u8(pkt); - physlinkid = tmp & 0x07; - prio = tmp >> 3; - - linkparam.priority = prio; - linkparam.phyid = physlinkid; - endpoint = cfpkt_extr_head_u8(pkt); - linkparam.endpoint = endpoint & 0x03; - - switch (serv) { - case CFCTRL_SRV_VEI: - case CFCTRL_SRV_DBG: - if (CFCTRL_ERR_BIT & cmdrsp) - break; - /* Link ID */ - linkid = cfpkt_extr_head_u8(pkt); - break; - case CFCTRL_SRV_VIDEO: - tmp = cfpkt_extr_head_u8(pkt); - linkparam.u.video.connid = tmp; - if (CFCTRL_ERR_BIT & cmdrsp) - break; - /* Link ID */ - linkid = cfpkt_extr_head_u8(pkt); - break; - - case CFCTRL_SRV_DATAGRAM: - linkparam.u.datagram.connid = cfpkt_extr_head_u32(pkt); - if (CFCTRL_ERR_BIT & cmdrsp) - break; - /* Link ID */ - linkid = cfpkt_extr_head_u8(pkt); - break; - case CFCTRL_SRV_RFM: - /* Construct a frame, convert - * DatagramConnectionID - * to network format long and copy it out... - */ - linkparam.u.rfm.connid = cfpkt_extr_head_u32(pkt); - cp = (u8 *) linkparam.u.rfm.volume; - for (tmp = cfpkt_extr_head_u8(pkt); - cfpkt_more(pkt) && tmp != '\0'; - tmp = cfpkt_extr_head_u8(pkt)) - *cp++ = tmp; - *cp = '\0'; - - if (CFCTRL_ERR_BIT & cmdrsp) - break; - /* Link ID */ - linkid = cfpkt_extr_head_u8(pkt); - - break; - case CFCTRL_SRV_UTIL: - /* Construct a frame, convert - * DatagramConnectionID - * to network format long and copy it out... - */ - /* Fifosize KB */ - linkparam.u.utility.fifosize_kb = cfpkt_extr_head_u16(pkt); - /* Fifosize bufs */ - linkparam.u.utility.fifosize_bufs = cfpkt_extr_head_u16(pkt); - /* name */ - cp = (u8 *) linkparam.u.utility.name; - caif_assert(sizeof(linkparam.u.utility.name) - >= UTILITY_NAME_LENGTH); - for (i = 0; i < UTILITY_NAME_LENGTH && cfpkt_more(pkt); i++) { - tmp = cfpkt_extr_head_u8(pkt); - *cp++ = tmp; - } - /* Length */ - len = cfpkt_extr_head_u8(pkt); - linkparam.u.utility.paramlen = len; - /* Param Data */ - cp = linkparam.u.utility.params; - while (cfpkt_more(pkt) && len--) { - tmp = cfpkt_extr_head_u8(pkt); - *cp++ = tmp; - } - if (CFCTRL_ERR_BIT & cmdrsp) - break; - /* Link ID */ - linkid = cfpkt_extr_head_u8(pkt); - /* Length */ - len = cfpkt_extr_head_u8(pkt); - /* Param Data */ - cfpkt_extr_head(pkt, NULL, len); - break; - default: - pr_warn("Request setup, invalid type (%d)\n", serv); - return -1; - } - - rsp.cmd = CFCTRL_CMD_LINK_SETUP; - rsp.param = linkparam; - spin_lock_bh(&cfctrl->info_list_lock); - req = cfctrl_remove_req(cfctrl, &rsp); - - if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) || - cfpkt_erroneous(pkt)) { - pr_err("Invalid O/E bit or parse error " - "on CAIF control channel\n"); - cfctrl->res.reject_rsp(cfctrl->serv.layer.up, 0, - req ? req->client_layer : NULL); - } else { - cfctrl->res.linksetup_rsp(cfctrl->serv.layer.up, linkid, - serv, physlinkid, - req ? req->client_layer : NULL); - } - - kfree(req); - - spin_unlock_bh(&cfctrl->info_list_lock); - - return 0; -} - -static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) -{ - u8 cmdrsp; - u8 cmd; - int ret = 0; - u8 linkid = 0; - struct cfctrl *cfctrl = container_obj(layer); - - cmdrsp = cfpkt_extr_head_u8(pkt); - cmd = cmdrsp & CFCTRL_CMD_MASK; - if (cmd != CFCTRL_CMD_LINK_ERR - && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp) - && CFCTRL_ERR_BIT != (CFCTRL_ERR_BIT & cmdrsp)) { - if (handle_loop(cfctrl, cmd, pkt) != 0) - cmdrsp |= CFCTRL_ERR_BIT; - } - - switch (cmd) { - case CFCTRL_CMD_LINK_SETUP: - ret = cfctrl_link_setup(cfctrl, pkt, cmdrsp); - break; - case CFCTRL_CMD_LINK_DESTROY: - linkid = cfpkt_extr_head_u8(pkt); - cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid); - break; - case CFCTRL_CMD_LINK_ERR: - pr_err("Frame Error Indication received\n"); - cfctrl->res.linkerror_ind(); - break; - case CFCTRL_CMD_ENUM: - cfctrl->res.enum_rsp(); - break; - case CFCTRL_CMD_SLEEP: - cfctrl->res.sleep_rsp(); - break; - case CFCTRL_CMD_WAKE: - cfctrl->res.wake_rsp(); - break; - case CFCTRL_CMD_LINK_RECONF: - cfctrl->res.restart_rsp(); - break; - case CFCTRL_CMD_RADIO_SET: - cfctrl->res.radioset_rsp(); - break; - default: - pr_err("Unrecognized Control Frame\n"); - ret = -1; - goto error; - } -error: - cfpkt_destroy(pkt); - return ret; -} - -static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid) -{ - struct cfctrl *this = container_obj(layr); - switch (ctrl) { - case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: - case CAIF_CTRLCMD_FLOW_OFF_IND: - spin_lock_bh(&this->info_list_lock); - if (!list_empty(&this->list)) - pr_debug("Received flow off in control layer\n"); - spin_unlock_bh(&this->info_list_lock); - break; - case _CAIF_CTRLCMD_PHYIF_DOWN_IND: { - struct cfctrl_request_info *p, *tmp; - - /* Find all connect request and report failure */ - spin_lock_bh(&this->info_list_lock); - list_for_each_entry_safe(p, tmp, &this->list, list) { - if (p->param.phyid == phyid) { - list_del(&p->list); - p->client_layer->ctrlcmd(p->client_layer, - CAIF_CTRLCMD_INIT_FAIL_RSP, - phyid); - kfree(p); - } - } - spin_unlock_bh(&this->info_list_lock); - break; - } - default: - break; - } -} - -#ifndef CAIF_NO_LOOP -static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt) -{ - static int last_linkid; - static int dec; - u8 linkid, linktype, tmp; - switch (cmd) { - case CFCTRL_CMD_LINK_SETUP: - spin_lock_bh(&ctrl->loop_linkid_lock); - if (!dec) { - for (linkid = last_linkid + 1; linkid < 254; linkid++) - if (!ctrl->loop_linkused[linkid]) - goto found; - } - dec = 1; - for (linkid = last_linkid - 1; linkid > 1; linkid--) - if (!ctrl->loop_linkused[linkid]) - goto found; - spin_unlock_bh(&ctrl->loop_linkid_lock); - return -1; -found: - if (linkid < 10) - dec = 0; - - if (!ctrl->loop_linkused[linkid]) - ctrl->loop_linkused[linkid] = 1; - - last_linkid = linkid; - - cfpkt_add_trail(pkt, &linkid, 1); - spin_unlock_bh(&ctrl->loop_linkid_lock); - cfpkt_peek_head(pkt, &linktype, 1); - if (linktype == CFCTRL_SRV_UTIL) { - tmp = 0x01; - cfpkt_add_trail(pkt, &tmp, 1); - cfpkt_add_trail(pkt, &tmp, 1); - } - break; - - case CFCTRL_CMD_LINK_DESTROY: - spin_lock_bh(&ctrl->loop_linkid_lock); - cfpkt_peek_head(pkt, &linkid, 1); - ctrl->loop_linkused[linkid] = 0; - spin_unlock_bh(&ctrl->loop_linkid_lock); - break; - default: - break; - } - return 0; -} -#endif diff --git a/net/caif/cfdbgl.c b/net/caif/cfdbgl.c deleted file mode 100644 index 57ad3f82e004..000000000000 --- a/net/caif/cfdbgl.c +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include - -#define container_obj(layr) ((struct cfsrvl *) layr) - -static int cfdbgl_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt); - -struct cflayer *cfdbgl_create(u8 channel_id, struct dev_info *dev_info) -{ - struct cfsrvl *dbg = kzalloc_obj(struct cfsrvl, GFP_ATOMIC); - if (!dbg) - return NULL; - caif_assert(offsetof(struct cfsrvl, layer) == 0); - cfsrvl_init(dbg, channel_id, dev_info, false); - dbg->layer.receive = cfdbgl_receive; - dbg->layer.transmit = cfdbgl_transmit; - snprintf(dbg->layer.name, CAIF_LAYER_NAME_SZ, "dbg%d", channel_id); - return &dbg->layer; -} - -static int cfdbgl_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - return layr->up->receive(layr->up, pkt); -} - -static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - struct cfsrvl *service = container_obj(layr); - struct caif_payload_info *info; - int ret; - - if (!cfsrvl_ready(service, &ret)) { - cfpkt_destroy(pkt); - return ret; - } - - /* Add info for MUX-layer to route the packet out */ - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - info->dev_info = &service->dev_info; - - return layr->dn->transmit(layr->dn, pkt); -} diff --git a/net/caif/cfdgml.c b/net/caif/cfdgml.c deleted file mode 100644 index c451ddd155a7..000000000000 --- a/net/caif/cfdgml.c +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include - - -#define container_obj(layr) ((struct cfsrvl *) layr) - -#define DGM_CMD_BIT 0x80 -#define DGM_FLOW_OFF 0x81 -#define DGM_FLOW_ON 0x80 -#define DGM_MTU 1500 - -static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt); - -struct cflayer *cfdgml_create(u8 channel_id, struct dev_info *dev_info) -{ - struct cfsrvl *dgm = kzalloc_obj(struct cfsrvl, GFP_ATOMIC); - if (!dgm) - return NULL; - caif_assert(offsetof(struct cfsrvl, layer) == 0); - cfsrvl_init(dgm, channel_id, dev_info, true); - dgm->layer.receive = cfdgml_receive; - dgm->layer.transmit = cfdgml_transmit; - snprintf(dgm->layer.name, CAIF_LAYER_NAME_SZ, "dgm%d", channel_id); - return &dgm->layer; -} - -static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 cmd = -1; - u8 dgmhdr[3]; - int ret; - caif_assert(layr->up != NULL); - caif_assert(layr->receive != NULL); - caif_assert(layr->ctrlcmd != NULL); - - if (cfpkt_extr_head(pkt, &cmd, 1) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - - if ((cmd & DGM_CMD_BIT) == 0) { - if (cfpkt_extr_head(pkt, &dgmhdr, 3) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - ret = layr->up->receive(layr->up, pkt); - return ret; - } - - switch (cmd) { - case DGM_FLOW_OFF: /* FLOW OFF */ - layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0); - cfpkt_destroy(pkt); - return 0; - case DGM_FLOW_ON: /* FLOW ON */ - layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0); - cfpkt_destroy(pkt); - return 0; - default: - cfpkt_destroy(pkt); - pr_info("Unknown datagram control %d (0x%x)\n", cmd, cmd); - return -EPROTO; - } -} - -static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 packet_type; - u32 zero = 0; - struct caif_payload_info *info; - struct cfsrvl *service = container_obj(layr); - int ret; - - if (!cfsrvl_ready(service, &ret)) { - cfpkt_destroy(pkt); - return ret; - } - - /* STE Modem cannot handle more than 1500 bytes datagrams */ - if (cfpkt_getlen(pkt) > DGM_MTU) { - cfpkt_destroy(pkt); - return -EMSGSIZE; - } - - cfpkt_add_head(pkt, &zero, 3); - packet_type = 0x08; /* B9 set - UNCLASSIFIED */ - cfpkt_add_head(pkt, &packet_type, 1); - - /* Add info for MUX-layer to route the packet out. */ - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - /* To optimize alignment, we add up the size of CAIF header - * before payload. - */ - info->hdr_len = 4; - info->dev_info = &service->dev_info; - return layr->dn->transmit(layr->dn, pkt); -} diff --git a/net/caif/cffrml.c b/net/caif/cffrml.c deleted file mode 100644 index 0f4979d89fcb..000000000000 --- a/net/caif/cffrml.c +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * CAIF Framing Layer. - * - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) container_of(layr, struct cffrml, layer) - -struct cffrml { - struct cflayer layer; - bool dofcs; /* !< FCS active */ - int __percpu *pcpu_refcnt; -}; - -static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt); -static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid); - -static u32 cffrml_rcv_error; -static u32 cffrml_rcv_checsum_error; -struct cflayer *cffrml_create(u16 phyid, bool use_fcs) -{ - struct cffrml *this = kzalloc_obj(struct cffrml, GFP_ATOMIC); - if (!this) - return NULL; - this->pcpu_refcnt = alloc_percpu(int); - if (this->pcpu_refcnt == NULL) { - kfree(this); - return NULL; - } - - caif_assert(offsetof(struct cffrml, layer) == 0); - - this->layer.receive = cffrml_receive; - this->layer.transmit = cffrml_transmit; - this->layer.ctrlcmd = cffrml_ctrlcmd; - snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "frm%d", phyid); - this->dofcs = use_fcs; - this->layer.id = phyid; - return (struct cflayer *) this; -} - -void cffrml_free(struct cflayer *layer) -{ - struct cffrml *this = container_obj(layer); - free_percpu(this->pcpu_refcnt); - kfree(layer); -} - -void cffrml_set_uplayer(struct cflayer *this, struct cflayer *up) -{ - this->up = up; -} - -void cffrml_set_dnlayer(struct cflayer *this, struct cflayer *dn) -{ - this->dn = dn; -} - -static u16 cffrml_checksum(u16 chks, void *buf, u16 len) -{ - /* FIXME: FCS should be moved to glue in order to use OS-Specific - * solutions - */ - return crc_ccitt(chks, buf, len); -} - -static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u16 tmp; - u16 len; - u16 hdrchks; - int pktchks; - struct cffrml *this; - this = container_obj(layr); - - cfpkt_extr_head(pkt, &tmp, 2); - len = le16_to_cpu(tmp); - - /* Subtract for FCS on length if FCS is not used. */ - if (!this->dofcs) { - if (len < 2) { - ++cffrml_rcv_error; - pr_err("Invalid frame length (%d)\n", len); - cfpkt_destroy(pkt); - return -EPROTO; - } - len -= 2; - } - - if (cfpkt_setlen(pkt, len) < 0) { - ++cffrml_rcv_error; - pr_err("Framing length error (%d)\n", len); - cfpkt_destroy(pkt); - return -EPROTO; - } - /* - * Don't do extract if FCS is false, rather do setlen - then we don't - * get a cache-miss. - */ - if (this->dofcs) { - cfpkt_extr_trail(pkt, &tmp, 2); - hdrchks = le16_to_cpu(tmp); - pktchks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff); - if (pktchks != hdrchks) { - cfpkt_add_trail(pkt, &tmp, 2); - ++cffrml_rcv_error; - ++cffrml_rcv_checsum_error; - pr_info("Frame checksum error (0x%x != 0x%x)\n", - hdrchks, pktchks); - return -EILSEQ; - } - } - if (cfpkt_erroneous(pkt)) { - ++cffrml_rcv_error; - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - - if (layr->up == NULL) { - pr_err("Layr up is missing!\n"); - cfpkt_destroy(pkt); - return -EINVAL; - } - - return layr->up->receive(layr->up, pkt); -} - -static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - u16 chks; - u16 len; - __le16 data; - - struct cffrml *this = container_obj(layr); - if (this->dofcs) { - chks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff); - data = cpu_to_le16(chks); - cfpkt_add_trail(pkt, &data, 2); - } else { - cfpkt_pad_trail(pkt, 2); - } - len = cfpkt_getlen(pkt); - data = cpu_to_le16(len); - cfpkt_add_head(pkt, &data, 2); - cfpkt_info(pkt)->hdr_len += 2; - if (cfpkt_erroneous(pkt)) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - - if (layr->dn == NULL) { - cfpkt_destroy(pkt); - return -ENODEV; - - } - return layr->dn->transmit(layr->dn, pkt); -} - -static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid) -{ - if (layr->up && layr->up->ctrlcmd) - layr->up->ctrlcmd(layr->up, ctrl, layr->id); -} - -void cffrml_put(struct cflayer *layr) -{ - struct cffrml *this = container_obj(layr); - if (layr != NULL && this->pcpu_refcnt != NULL) - this_cpu_dec(*this->pcpu_refcnt); -} - -void cffrml_hold(struct cflayer *layr) -{ - struct cffrml *this = container_obj(layr); - if (layr != NULL && this->pcpu_refcnt != NULL) - this_cpu_inc(*this->pcpu_refcnt); -} - -int cffrml_refcnt_read(struct cflayer *layr) -{ - int i, refcnt = 0; - struct cffrml *this = container_obj(layr); - for_each_possible_cpu(i) - refcnt += *per_cpu_ptr(this->pcpu_refcnt, i); - return refcnt; -} diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c deleted file mode 100644 index 77a1f31639b7..000000000000 --- a/net/caif/cfmuxl.c +++ /dev/null @@ -1,267 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) container_of(layr, struct cfmuxl, layer) - -#define CAIF_CTRL_CHANNEL 0 -#define UP_CACHE_SIZE 8 -#define DN_CACHE_SIZE 8 - -struct cfmuxl { - struct cflayer layer; - struct list_head srvl_list; - struct list_head frml_list; - struct cflayer *up_cache[UP_CACHE_SIZE]; - struct cflayer *dn_cache[DN_CACHE_SIZE]; - /* - * Set when inserting or removing downwards layers. - */ - spinlock_t transmit_lock; - - /* - * Set when inserting or removing upwards layers. - */ - spinlock_t receive_lock; - -}; - -static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt); -static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid); -static struct cflayer *get_up(struct cfmuxl *muxl, u16 id); - -struct cflayer *cfmuxl_create(void) -{ - struct cfmuxl *this = kzalloc_obj(struct cfmuxl, GFP_ATOMIC); - - if (!this) - return NULL; - this->layer.receive = cfmuxl_receive; - this->layer.transmit = cfmuxl_transmit; - this->layer.ctrlcmd = cfmuxl_ctrlcmd; - INIT_LIST_HEAD(&this->srvl_list); - INIT_LIST_HEAD(&this->frml_list); - spin_lock_init(&this->transmit_lock); - spin_lock_init(&this->receive_lock); - snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "mux"); - return &this->layer; -} - -int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid) -{ - struct cfmuxl *muxl = (struct cfmuxl *) layr; - - spin_lock_bh(&muxl->transmit_lock); - list_add_rcu(&dn->node, &muxl->frml_list); - spin_unlock_bh(&muxl->transmit_lock); - return 0; -} - -static struct cflayer *get_from_id(struct list_head *list, u16 id) -{ - struct cflayer *lyr; - list_for_each_entry_rcu(lyr, list, node) { - if (lyr->id == id) - return lyr; - } - - return NULL; -} - -int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid) -{ - struct cfmuxl *muxl = container_obj(layr); - struct cflayer *old; - - spin_lock_bh(&muxl->receive_lock); - - /* Two entries with same id is wrong, so remove old layer from mux */ - old = get_from_id(&muxl->srvl_list, linkid); - if (old != NULL) - list_del_rcu(&old->node); - - list_add_rcu(&up->node, &muxl->srvl_list); - spin_unlock_bh(&muxl->receive_lock); - - return 0; -} - -struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid) -{ - struct cfmuxl *muxl = container_obj(layr); - struct cflayer *dn; - int idx = phyid % DN_CACHE_SIZE; - - spin_lock_bh(&muxl->transmit_lock); - RCU_INIT_POINTER(muxl->dn_cache[idx], NULL); - dn = get_from_id(&muxl->frml_list, phyid); - if (dn == NULL) - goto out; - - list_del_rcu(&dn->node); - caif_assert(dn != NULL); -out: - spin_unlock_bh(&muxl->transmit_lock); - return dn; -} - -static struct cflayer *get_up(struct cfmuxl *muxl, u16 id) -{ - struct cflayer *up; - int idx = id % UP_CACHE_SIZE; - up = rcu_dereference(muxl->up_cache[idx]); - if (up == NULL || up->id != id) { - spin_lock_bh(&muxl->receive_lock); - up = get_from_id(&muxl->srvl_list, id); - rcu_assign_pointer(muxl->up_cache[idx], up); - spin_unlock_bh(&muxl->receive_lock); - } - return up; -} - -static struct cflayer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info) -{ - struct cflayer *dn; - int idx = dev_info->id % DN_CACHE_SIZE; - dn = rcu_dereference(muxl->dn_cache[idx]); - if (dn == NULL || dn->id != dev_info->id) { - spin_lock_bh(&muxl->transmit_lock); - dn = get_from_id(&muxl->frml_list, dev_info->id); - rcu_assign_pointer(muxl->dn_cache[idx], dn); - spin_unlock_bh(&muxl->transmit_lock); - } - return dn; -} - -struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id) -{ - struct cflayer *up; - struct cfmuxl *muxl = container_obj(layr); - int idx = id % UP_CACHE_SIZE; - - if (id == 0) { - pr_warn("Trying to remove control layer\n"); - return NULL; - } - - spin_lock_bh(&muxl->receive_lock); - up = get_from_id(&muxl->srvl_list, id); - if (up == NULL) - goto out; - - RCU_INIT_POINTER(muxl->up_cache[idx], NULL); - list_del_rcu(&up->node); -out: - spin_unlock_bh(&muxl->receive_lock); - return up; -} - -static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - int ret; - struct cfmuxl *muxl = container_obj(layr); - u8 id; - struct cflayer *up; - if (cfpkt_extr_head(pkt, &id, 1) < 0) { - pr_err("erroneous Caif Packet\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - rcu_read_lock(); - up = get_up(muxl, id); - - if (up == NULL) { - pr_debug("Received data on unknown link ID = %d (0x%x)" - " up == NULL", id, id); - cfpkt_destroy(pkt); - /* - * Don't return ERROR, since modem misbehaves and sends out - * flow on before linksetup response. - */ - - rcu_read_unlock(); - return /* CFGLU_EPROT; */ 0; - } - - /* We can't hold rcu_lock during receive, so take a ref count instead */ - cfsrvl_get(up); - rcu_read_unlock(); - - ret = up->receive(up, pkt); - - cfsrvl_put(up); - return ret; -} - -static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - struct cfmuxl *muxl = container_obj(layr); - int err; - u8 linkid; - struct cflayer *dn; - struct caif_payload_info *info = cfpkt_info(pkt); - BUG_ON(!info); - - rcu_read_lock(); - - dn = get_dn(muxl, info->dev_info); - if (dn == NULL) { - pr_debug("Send data on unknown phy ID = %d (0x%x)\n", - info->dev_info->id, info->dev_info->id); - rcu_read_unlock(); - cfpkt_destroy(pkt); - return -ENOTCONN; - } - - info->hdr_len += 1; - linkid = info->channel_id; - cfpkt_add_head(pkt, &linkid, 1); - - /* We can't hold rcu_lock during receive, so take a ref count instead */ - cffrml_hold(dn); - - rcu_read_unlock(); - - err = dn->transmit(dn, pkt); - - cffrml_put(dn); - return err; -} - -static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid) -{ - struct cfmuxl *muxl = container_obj(layr); - struct cflayer *layer; - - rcu_read_lock(); - list_for_each_entry_rcu(layer, &muxl->srvl_list, node) { - - if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) { - - if ((ctrl == _CAIF_CTRLCMD_PHYIF_DOWN_IND || - ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) && - layer->id != 0) - cfmuxl_remove_uplayer(layr, layer->id); - - /* NOTE: ctrlcmd is not allowed to block */ - layer->ctrlcmd(layer, ctrl, phyid); - } - } - rcu_read_unlock(); -} diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c deleted file mode 100644 index 96236d21b18e..000000000000 --- a/net/caif/cfpkt_skbuff.c +++ /dev/null @@ -1,373 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include - -#define PKT_PREFIX 48 -#define PKT_POSTFIX 2 -#define PKT_LEN_WHEN_EXTENDING 128 -#define PKT_ERROR(pkt, errmsg) \ -do { \ - cfpkt_priv(pkt)->erronous = true; \ - skb_reset_tail_pointer(&pkt->skb); \ - pr_warn(errmsg); \ -} while (0) - -/* - * net/caif/ is generic and does not - * understand SKB, so we do this typecast - */ -struct cfpkt { - struct sk_buff skb; -}; - -/* Private data inside SKB */ -struct cfpkt_priv_data { - struct dev_info dev_info; - bool erronous; -}; - -static inline struct cfpkt_priv_data *cfpkt_priv(struct cfpkt *pkt) -{ - return (struct cfpkt_priv_data *) pkt->skb.cb; -} - -static inline bool is_erronous(struct cfpkt *pkt) -{ - return cfpkt_priv(pkt)->erronous; -} - -static inline struct sk_buff *pkt_to_skb(struct cfpkt *pkt) -{ - return &pkt->skb; -} - -static inline struct cfpkt *skb_to_pkt(struct sk_buff *skb) -{ - return (struct cfpkt *) skb; -} - -struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt) -{ - struct cfpkt *pkt = skb_to_pkt(nativepkt); - cfpkt_priv(pkt)->erronous = false; - return pkt; -} -EXPORT_SYMBOL(cfpkt_fromnative); - -void *cfpkt_tonative(struct cfpkt *pkt) -{ - return (void *) pkt; -} -EXPORT_SYMBOL(cfpkt_tonative); - -static struct cfpkt *cfpkt_create_pfx(u16 len, u16 pfx) -{ - struct sk_buff *skb; - - skb = alloc_skb(len + pfx, GFP_ATOMIC); - if (unlikely(skb == NULL)) - return NULL; - - skb_reserve(skb, pfx); - return skb_to_pkt(skb); -} - -inline struct cfpkt *cfpkt_create(u16 len) -{ - return cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX); -} - -void cfpkt_destroy(struct cfpkt *pkt) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - kfree_skb(skb); -} - -inline bool cfpkt_more(struct cfpkt *pkt) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - return skb->len > 0; -} - -int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - if (skb_headlen(skb) >= len) { - memcpy(data, skb->data, len); - return 0; - } - return !cfpkt_extr_head(pkt, data, len) && - !cfpkt_add_head(pkt, data, len); -} - -int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - u8 *from; - if (unlikely(is_erronous(pkt))) - return -EPROTO; - - if (unlikely(len > skb->len)) { - PKT_ERROR(pkt, "read beyond end of packet\n"); - return -EPROTO; - } - - if (unlikely(len > skb_headlen(skb))) { - if (unlikely(skb_linearize(skb) != 0)) { - PKT_ERROR(pkt, "linearize failed\n"); - return -EPROTO; - } - } - from = skb_pull(skb, len); - from -= len; - if (data) - memcpy(data, from, len); - return 0; -} -EXPORT_SYMBOL(cfpkt_extr_head); - -int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - u8 *data = dta; - u8 *from; - if (unlikely(is_erronous(pkt))) - return -EPROTO; - - if (unlikely(skb_linearize(skb) != 0)) { - PKT_ERROR(pkt, "linearize failed\n"); - return -EPROTO; - } - if (unlikely(skb->data + len > skb_tail_pointer(skb))) { - PKT_ERROR(pkt, "read beyond end of packet\n"); - return -EPROTO; - } - from = skb_tail_pointer(skb) - len; - skb_trim(skb, skb->len - len); - memcpy(data, from, len); - return 0; -} - -int cfpkt_pad_trail(struct cfpkt *pkt, u16 len) -{ - return cfpkt_add_body(pkt, NULL, len); -} - -int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - struct sk_buff *lastskb; - u8 *to; - u16 addlen = 0; - - - if (unlikely(is_erronous(pkt))) - return -EPROTO; - - lastskb = skb; - - /* Check whether we need to add space at the tail */ - if (unlikely(skb_tailroom(skb) < len)) { - if (likely(len < PKT_LEN_WHEN_EXTENDING)) - addlen = PKT_LEN_WHEN_EXTENDING; - else - addlen = len; - } - - /* Check whether we need to change the SKB before writing to the tail */ - if (unlikely((addlen > 0) || skb_cloned(skb) || skb_shared(skb))) { - - /* Make sure data is writable */ - if (unlikely(skb_cow_data(skb, addlen, &lastskb) < 0)) { - PKT_ERROR(pkt, "cow failed\n"); - return -EPROTO; - } - } - - /* All set to put the last SKB and optionally write data there. */ - to = pskb_put(skb, lastskb, len); - if (likely(data)) - memcpy(to, data, len); - return 0; -} - -inline int cfpkt_addbdy(struct cfpkt *pkt, u8 data) -{ - return cfpkt_add_body(pkt, &data, 1); -} - -int cfpkt_add_head(struct cfpkt *pkt, const void *data2, u16 len) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - struct sk_buff *lastskb; - u8 *to; - const u8 *data = data2; - int ret; - if (unlikely(is_erronous(pkt))) - return -EPROTO; - if (unlikely(skb_headroom(skb) < len)) { - PKT_ERROR(pkt, "no headroom\n"); - return -EPROTO; - } - - /* Make sure data is writable */ - ret = skb_cow_data(skb, 0, &lastskb); - if (unlikely(ret < 0)) { - PKT_ERROR(pkt, "cow failed\n"); - return ret; - } - - to = skb_push(skb, len); - memcpy(to, data, len); - return 0; -} -EXPORT_SYMBOL(cfpkt_add_head); - -inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len) -{ - return cfpkt_add_body(pkt, data, len); -} - -inline u16 cfpkt_getlen(struct cfpkt *pkt) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - return skb->len; -} - -int cfpkt_iterate(struct cfpkt *pkt, - u16 (*iter_func)(u16, void *, u16), - u16 data) -{ - /* - * Don't care about the performance hit of linearizing, - * Checksum should not be used on high-speed interfaces anyway. - */ - if (unlikely(is_erronous(pkt))) - return -EPROTO; - if (unlikely(skb_linearize(&pkt->skb) != 0)) { - PKT_ERROR(pkt, "linearize failed\n"); - return -EPROTO; - } - return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt)); -} - -int cfpkt_setlen(struct cfpkt *pkt, u16 len) -{ - struct sk_buff *skb = pkt_to_skb(pkt); - - - if (unlikely(is_erronous(pkt))) - return -EPROTO; - - if (likely(len <= skb->len)) { - if (unlikely(skb->data_len)) - ___pskb_trim(skb, len); - else - skb_trim(skb, len); - - return cfpkt_getlen(pkt); - } - - /* Need to expand SKB */ - if (unlikely(!cfpkt_pad_trail(pkt, len - skb->len))) - PKT_ERROR(pkt, "skb_pad_trail failed\n"); - - return cfpkt_getlen(pkt); -} - -struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, - struct cfpkt *addpkt, - u16 expectlen) -{ - struct sk_buff *dst = pkt_to_skb(dstpkt); - struct sk_buff *add = pkt_to_skb(addpkt); - u16 addlen = skb_headlen(add); - u16 neededtailspace; - struct sk_buff *tmp; - u16 dstlen; - u16 createlen; - if (unlikely(is_erronous(dstpkt) || is_erronous(addpkt))) { - return dstpkt; - } - - neededtailspace = max(expectlen, addlen); - - if (dst->tail + neededtailspace > dst->end) { - /* Create a dumplicate of 'dst' with more tail space */ - struct cfpkt *tmppkt; - dstlen = skb_headlen(dst); - createlen = dstlen + neededtailspace; - tmppkt = cfpkt_create(createlen + PKT_PREFIX + PKT_POSTFIX); - if (tmppkt == NULL) - return NULL; - tmp = pkt_to_skb(tmppkt); - skb_put_data(tmp, dst->data, dstlen); - cfpkt_destroy(dstpkt); - dst = tmp; - } - skb_put_data(dst, add->data, skb_headlen(add)); - cfpkt_destroy(addpkt); - return skb_to_pkt(dst); -} - -struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos) -{ - struct sk_buff *skb2; - struct sk_buff *skb = pkt_to_skb(pkt); - struct cfpkt *tmppkt; - u8 *split = skb->data + pos; - u16 len2nd = skb_tail_pointer(skb) - split; - - if (unlikely(is_erronous(pkt))) - return NULL; - - if (skb->data + pos > skb_tail_pointer(skb)) { - PKT_ERROR(pkt, "trying to split beyond end of packet\n"); - return NULL; - } - - /* Create a new packet for the second part of the data */ - tmppkt = cfpkt_create_pfx(len2nd + PKT_PREFIX + PKT_POSTFIX, - PKT_PREFIX); - if (tmppkt == NULL) - return NULL; - skb2 = pkt_to_skb(tmppkt); - - - if (skb2 == NULL) - return NULL; - - skb_put_data(skb2, split, len2nd); - - /* Reduce the length of the original packet */ - skb_trim(skb, pos); - - skb2->priority = skb->priority; - return skb_to_pkt(skb2); -} - -bool cfpkt_erroneous(struct cfpkt *pkt) -{ - return cfpkt_priv(pkt)->erronous; -} - -struct caif_payload_info *cfpkt_info(struct cfpkt *pkt) -{ - return (struct caif_payload_info *)&pkt_to_skb(pkt)->cb; -} -EXPORT_SYMBOL(cfpkt_info); - -void cfpkt_set_prio(struct cfpkt *pkt, int prio) -{ - pkt_to_skb(pkt)->priority = prio; -} -EXPORT_SYMBOL(cfpkt_set_prio); diff --git a/net/caif/cfrfml.c b/net/caif/cfrfml.c deleted file mode 100644 index 93732ebbd1e2..000000000000 --- a/net/caif/cfrfml.c +++ /dev/null @@ -1,299 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) container_of(layr, struct cfrfml, serv.layer) -#define RFM_SEGMENTATION_BIT 0x01 -#define RFM_HEAD_SIZE 7 - -static int cfrfml_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt); - -struct cfrfml { - struct cfsrvl serv; - struct cfpkt *incomplete_frm; - int fragment_size; - u8 seghead[6]; - u16 pdu_size; - /* Protects serialized processing of packets */ - spinlock_t sync; -}; - -static void cfrfml_release(struct cflayer *layer) -{ - struct cfsrvl *srvl = container_of(layer, struct cfsrvl, layer); - struct cfrfml *rfml = container_obj(&srvl->layer); - - if (rfml->incomplete_frm) - cfpkt_destroy(rfml->incomplete_frm); - - kfree(srvl); -} - -struct cflayer *cfrfml_create(u8 channel_id, struct dev_info *dev_info, - int mtu_size) -{ - int tmp; - struct cfrfml *this = kzalloc_obj(struct cfrfml, GFP_ATOMIC); - - if (!this) - return NULL; - - cfsrvl_init(&this->serv, channel_id, dev_info, false); - this->serv.release = cfrfml_release; - this->serv.layer.receive = cfrfml_receive; - this->serv.layer.transmit = cfrfml_transmit; - - /* Round down to closest multiple of 16 */ - tmp = (mtu_size - RFM_HEAD_SIZE - 6) / 16; - tmp *= 16; - - this->fragment_size = tmp; - spin_lock_init(&this->sync); - snprintf(this->serv.layer.name, CAIF_LAYER_NAME_SZ, - "rfm%d", channel_id); - - return &this->serv.layer; -} - -static struct cfpkt *rfm_append(struct cfrfml *rfml, char *seghead, - struct cfpkt *pkt, int *err) -{ - struct cfpkt *tmppkt; - *err = -EPROTO; - /* n-th but not last segment */ - - if (cfpkt_extr_head(pkt, seghead, 6) < 0) - return NULL; - - /* Verify correct header */ - if (memcmp(seghead, rfml->seghead, 6) != 0) - return NULL; - - tmppkt = cfpkt_append(rfml->incomplete_frm, pkt, - rfml->pdu_size + RFM_HEAD_SIZE); - - /* If cfpkt_append failes input pkts are not freed */ - *err = -ENOMEM; - if (tmppkt == NULL) - return NULL; - - *err = 0; - return tmppkt; -} - -static int cfrfml_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 tmp; - bool segmented; - int err; - u8 seghead[6]; - struct cfrfml *rfml; - struct cfpkt *tmppkt = NULL; - - caif_assert(layr->up != NULL); - caif_assert(layr->receive != NULL); - rfml = container_obj(layr); - spin_lock(&rfml->sync); - - err = -EPROTO; - if (cfpkt_extr_head(pkt, &tmp, 1) < 0) - goto out; - segmented = tmp & RFM_SEGMENTATION_BIT; - - if (segmented) { - if (rfml->incomplete_frm == NULL) { - /* Initial Segment */ - if (cfpkt_peek_head(pkt, rfml->seghead, 6) != 0) - goto out; - - rfml->pdu_size = get_unaligned_le16(rfml->seghead+4); - - if (cfpkt_erroneous(pkt)) - goto out; - rfml->incomplete_frm = pkt; - pkt = NULL; - } else { - - tmppkt = rfm_append(rfml, seghead, pkt, &err); - if (tmppkt == NULL) - goto out; - - if (cfpkt_erroneous(tmppkt)) - goto out; - - rfml->incomplete_frm = tmppkt; - - - if (cfpkt_erroneous(tmppkt)) - goto out; - } - err = 0; - goto out; - } - - if (rfml->incomplete_frm) { - - /* Last Segment */ - tmppkt = rfm_append(rfml, seghead, pkt, &err); - if (tmppkt == NULL) - goto out; - - if (cfpkt_erroneous(tmppkt)) - goto out; - - rfml->incomplete_frm = NULL; - pkt = tmppkt; - tmppkt = NULL; - - /* Verify that length is correct */ - err = -EPROTO; - if (rfml->pdu_size != cfpkt_getlen(pkt) - RFM_HEAD_SIZE + 1) - goto out; - } - - err = rfml->serv.layer.up->receive(rfml->serv.layer.up, pkt); - -out: - - if (err != 0) { - if (tmppkt) - cfpkt_destroy(tmppkt); - if (pkt) - cfpkt_destroy(pkt); - if (rfml->incomplete_frm) - cfpkt_destroy(rfml->incomplete_frm); - rfml->incomplete_frm = NULL; - - pr_info("Connection error %d triggered on RFM link\n", err); - - /* Trigger connection error upon failure.*/ - layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, - rfml->serv.dev_info.id); - } - spin_unlock(&rfml->sync); - - if (unlikely(err == -EAGAIN)) - /* It is not possible to recover after drop of a fragment */ - err = -EIO; - - return err; -} - - -static int cfrfml_transmit_segment(struct cfrfml *rfml, struct cfpkt *pkt) -{ - caif_assert(cfpkt_getlen(pkt) < rfml->fragment_size + RFM_HEAD_SIZE); - - /* Add info for MUX-layer to route the packet out. */ - cfpkt_info(pkt)->channel_id = rfml->serv.layer.id; - - /* - * To optimize alignment, we add up the size of CAIF header before - * payload. - */ - cfpkt_info(pkt)->hdr_len = RFM_HEAD_SIZE; - cfpkt_info(pkt)->dev_info = &rfml->serv.dev_info; - - return rfml->serv.layer.dn->transmit(rfml->serv.layer.dn, pkt); -} - -static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - int err; - u8 seg; - u8 head[6]; - struct cfpkt *rearpkt = NULL; - struct cfpkt *frontpkt = pkt; - struct cfrfml *rfml = container_obj(layr); - - caif_assert(layr->dn != NULL); - caif_assert(layr->dn->transmit != NULL); - - if (!cfsrvl_ready(&rfml->serv, &err)) - goto out; - - err = -EPROTO; - if (cfpkt_getlen(pkt) <= RFM_HEAD_SIZE-1) - goto out; - - err = 0; - if (cfpkt_getlen(pkt) > rfml->fragment_size + RFM_HEAD_SIZE) - err = cfpkt_peek_head(pkt, head, 6); - - if (err != 0) - goto out; - - while (cfpkt_getlen(frontpkt) > rfml->fragment_size + RFM_HEAD_SIZE) { - - seg = 1; - err = -EPROTO; - - if (cfpkt_add_head(frontpkt, &seg, 1) < 0) - goto out; - /* - * On OOM error cfpkt_split returns NULL. - * - * NOTE: Segmented pdu is not correctly aligned. - * This has negative performance impact. - */ - - rearpkt = cfpkt_split(frontpkt, rfml->fragment_size); - if (rearpkt == NULL) - goto out; - - err = cfrfml_transmit_segment(rfml, frontpkt); - - if (err != 0) { - frontpkt = NULL; - goto out; - } - - frontpkt = rearpkt; - rearpkt = NULL; - - err = -EPROTO; - if (cfpkt_add_head(frontpkt, head, 6) < 0) - goto out; - - } - - seg = 0; - err = -EPROTO; - - if (cfpkt_add_head(frontpkt, &seg, 1) < 0) - goto out; - - err = cfrfml_transmit_segment(rfml, frontpkt); - - frontpkt = NULL; -out: - - if (err != 0) { - pr_info("Connection error %d triggered on RFM link\n", err); - /* Trigger connection error upon failure.*/ - - layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, - rfml->serv.dev_info.id); - - if (rearpkt) - cfpkt_destroy(rearpkt); - - if (frontpkt) - cfpkt_destroy(frontpkt); - } - - return err; -} diff --git a/net/caif/cfserl.c b/net/caif/cfserl.c deleted file mode 100644 index faf78fb754e2..000000000000 --- a/net/caif/cfserl.c +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) ((struct cfserl *) layr) - -#define CFSERL_STX 0x02 -#define SERIAL_MINIUM_PACKET_SIZE 4 -#define SERIAL_MAX_FRAMESIZE 4096 -struct cfserl { - struct cflayer layer; - struct cfpkt *incomplete_frm; - /* Protects parallel processing of incoming packets */ - spinlock_t sync; - bool usestx; -}; - -static int cfserl_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfserl_transmit(struct cflayer *layr, struct cfpkt *pkt); -static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid); - -void cfserl_release(struct cflayer *layer) -{ - kfree(layer); -} - -struct cflayer *cfserl_create(int instance, bool use_stx) -{ - struct cfserl *this = kzalloc_obj(struct cfserl, GFP_ATOMIC); - if (!this) - return NULL; - caif_assert(offsetof(struct cfserl, layer) == 0); - this->layer.receive = cfserl_receive; - this->layer.transmit = cfserl_transmit; - this->layer.ctrlcmd = cfserl_ctrlcmd; - this->usestx = use_stx; - spin_lock_init(&this->sync); - snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "ser1"); - return &this->layer; -} - -static int cfserl_receive(struct cflayer *l, struct cfpkt *newpkt) -{ - struct cfserl *layr = container_obj(l); - u16 pkt_len; - struct cfpkt *pkt = NULL; - struct cfpkt *tail_pkt = NULL; - u8 tmp8; - u16 tmp; - u8 stx = CFSERL_STX; - int ret; - u16 expectlen = 0; - - caif_assert(newpkt != NULL); - spin_lock(&layr->sync); - - if (layr->incomplete_frm != NULL) { - layr->incomplete_frm = - cfpkt_append(layr->incomplete_frm, newpkt, expectlen); - pkt = layr->incomplete_frm; - if (pkt == NULL) { - spin_unlock(&layr->sync); - return -ENOMEM; - } - } else { - pkt = newpkt; - } - layr->incomplete_frm = NULL; - - do { - /* Search for STX at start of pkt if STX is used */ - if (layr->usestx) { - cfpkt_extr_head(pkt, &tmp8, 1); - if (tmp8 != CFSERL_STX) { - while (cfpkt_more(pkt) - && tmp8 != CFSERL_STX) { - cfpkt_extr_head(pkt, &tmp8, 1); - } - if (!cfpkt_more(pkt)) { - cfpkt_destroy(pkt); - layr->incomplete_frm = NULL; - spin_unlock(&layr->sync); - return -EPROTO; - } - } - } - - pkt_len = cfpkt_getlen(pkt); - - /* - * pkt_len is the accumulated length of the packet data - * we have received so far. - * Exit if frame doesn't hold length. - */ - - if (pkt_len < 2) { - if (layr->usestx) - cfpkt_add_head(pkt, &stx, 1); - layr->incomplete_frm = pkt; - spin_unlock(&layr->sync); - return 0; - } - - /* - * Find length of frame. - * expectlen is the length we need for a full frame. - */ - cfpkt_peek_head(pkt, &tmp, 2); - expectlen = le16_to_cpu(tmp) + 2; - /* - * Frame error handling - */ - if (expectlen < SERIAL_MINIUM_PACKET_SIZE - || expectlen > SERIAL_MAX_FRAMESIZE) { - if (!layr->usestx) { - if (pkt != NULL) - cfpkt_destroy(pkt); - layr->incomplete_frm = NULL; - spin_unlock(&layr->sync); - return -EPROTO; - } - continue; - } - - if (pkt_len < expectlen) { - /* Too little received data */ - if (layr->usestx) - cfpkt_add_head(pkt, &stx, 1); - layr->incomplete_frm = pkt; - spin_unlock(&layr->sync); - return 0; - } - - /* - * Enough data for at least one frame. - * Split the frame, if too long - */ - if (pkt_len > expectlen) - tail_pkt = cfpkt_split(pkt, expectlen); - else - tail_pkt = NULL; - - /* Send the first part of packet upwards.*/ - spin_unlock(&layr->sync); - ret = layr->layer.up->receive(layr->layer.up, pkt); - spin_lock(&layr->sync); - if (ret == -EILSEQ) { - if (layr->usestx) { - if (tail_pkt != NULL) - pkt = cfpkt_append(pkt, tail_pkt, 0); - /* Start search for next STX if frame failed */ - continue; - } else { - cfpkt_destroy(pkt); - pkt = NULL; - } - } - - pkt = tail_pkt; - - } while (pkt != NULL); - - spin_unlock(&layr->sync); - return 0; -} - -static int cfserl_transmit(struct cflayer *layer, struct cfpkt *newpkt) -{ - struct cfserl *layr = container_obj(layer); - u8 tmp8 = CFSERL_STX; - if (layr->usestx) - cfpkt_add_head(newpkt, &tmp8, 1); - return layer->dn->transmit(layer->dn, newpkt); -} - -static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid) -{ - layr->up->ctrlcmd(layr->up, ctrl, phyid); -} diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c deleted file mode 100644 index d687fd0b4ed3..000000000000 --- a/net/caif/cfsrvl.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SRVL_CTRL_PKT_SIZE 1 -#define SRVL_FLOW_OFF 0x81 -#define SRVL_FLOW_ON 0x80 -#define SRVL_SET_PIN 0x82 - -#define container_obj(layr) container_of(layr, struct cfsrvl, layer) - -static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, - int phyid) -{ - struct cfsrvl *service = container_obj(layr); - - if (layr->up == NULL || layr->up->ctrlcmd == NULL) - return; - - switch (ctrl) { - case CAIF_CTRLCMD_INIT_RSP: - service->open = true; - layr->up->ctrlcmd(layr->up, ctrl, phyid); - break; - case CAIF_CTRLCMD_DEINIT_RSP: - case CAIF_CTRLCMD_INIT_FAIL_RSP: - service->open = false; - layr->up->ctrlcmd(layr->up, ctrl, phyid); - break; - case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: - if (phyid != service->dev_info.id) - break; - if (service->modem_flow_on) - layr->up->ctrlcmd(layr->up, - CAIF_CTRLCMD_FLOW_OFF_IND, phyid); - service->phy_flow_on = false; - break; - case _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND: - if (phyid != service->dev_info.id) - return; - if (service->modem_flow_on) { - layr->up->ctrlcmd(layr->up, - CAIF_CTRLCMD_FLOW_ON_IND, - phyid); - } - service->phy_flow_on = true; - break; - case CAIF_CTRLCMD_FLOW_OFF_IND: - if (service->phy_flow_on) { - layr->up->ctrlcmd(layr->up, - CAIF_CTRLCMD_FLOW_OFF_IND, phyid); - } - service->modem_flow_on = false; - break; - case CAIF_CTRLCMD_FLOW_ON_IND: - if (service->phy_flow_on) { - layr->up->ctrlcmd(layr->up, - CAIF_CTRLCMD_FLOW_ON_IND, phyid); - } - service->modem_flow_on = true; - break; - case _CAIF_CTRLCMD_PHYIF_DOWN_IND: - /* In case interface is down, let's fake a remove shutdown */ - layr->up->ctrlcmd(layr->up, - CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, phyid); - break; - case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: - layr->up->ctrlcmd(layr->up, ctrl, phyid); - break; - default: - pr_warn("Unexpected ctrl in cfsrvl (%d)\n", ctrl); - /* We have both modem and phy flow on, send flow on */ - layr->up->ctrlcmd(layr->up, ctrl, phyid); - service->phy_flow_on = true; - break; - } -} - -static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl) -{ - struct cfsrvl *service = container_obj(layr); - - caif_assert(layr != NULL); - caif_assert(layr->dn != NULL); - caif_assert(layr->dn->transmit != NULL); - - if (!service->supports_flowctrl) - return 0; - - switch (ctrl) { - case CAIF_MODEMCMD_FLOW_ON_REQ: - { - struct cfpkt *pkt; - struct caif_payload_info *info; - u8 flow_on = SRVL_FLOW_ON; - pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE); - if (!pkt) - return -ENOMEM; - - if (cfpkt_add_head(pkt, &flow_on, 1) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - info->hdr_len = 1; - info->dev_info = &service->dev_info; - cfpkt_set_prio(pkt, TC_PRIO_CONTROL); - return layr->dn->transmit(layr->dn, pkt); - } - case CAIF_MODEMCMD_FLOW_OFF_REQ: - { - struct cfpkt *pkt; - struct caif_payload_info *info; - u8 flow_off = SRVL_FLOW_OFF; - pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE); - if (!pkt) - return -ENOMEM; - - if (cfpkt_add_head(pkt, &flow_off, 1) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - info->hdr_len = 1; - info->dev_info = &service->dev_info; - cfpkt_set_prio(pkt, TC_PRIO_CONTROL); - return layr->dn->transmit(layr->dn, pkt); - } - default: - break; - } - return -EINVAL; -} - -static void cfsrvl_release(struct cflayer *layer) -{ - struct cfsrvl *service = container_of(layer, struct cfsrvl, layer); - kfree(service); -} - -void cfsrvl_init(struct cfsrvl *service, - u8 channel_id, - struct dev_info *dev_info, - bool supports_flowctrl) -{ - caif_assert(offsetof(struct cfsrvl, layer) == 0); - service->open = false; - service->modem_flow_on = true; - service->phy_flow_on = true; - service->layer.id = channel_id; - service->layer.ctrlcmd = cfservl_ctrlcmd; - service->layer.modemcmd = cfservl_modemcmd; - service->dev_info = *dev_info; - service->supports_flowctrl = supports_flowctrl; - service->release = cfsrvl_release; -} - -bool cfsrvl_ready(struct cfsrvl *service, int *err) -{ - if (!service->open) { - *err = -ENOTCONN; - return false; - } - return true; -} - -bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) -{ - struct cfsrvl *servl = container_obj(layer); - return servl->dev_info.id == phyid; -} - -void caif_free_client(struct cflayer *adap_layer) -{ - struct cflayer *serv_layer; - struct cfsrvl *servl; - - if (!adap_layer) - return; - - serv_layer = adap_layer->dn; - if (!serv_layer) - return; - - layer_set_dn(adap_layer, NULL); - layer_set_up(serv_layer, NULL); - - servl = container_obj(serv_layer); - servl->release(&servl->layer); -} -EXPORT_SYMBOL(caif_free_client); - -void caif_client_register_refcnt(struct cflayer *adapt_layer, - void (*hold)(struct cflayer *lyr), - void (*put)(struct cflayer *lyr)) -{ - struct cfsrvl *service; - - if (WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL)) - return; - service = container_of(adapt_layer->dn, struct cfsrvl, layer); - service->hold = hold; - service->put = put; -} -EXPORT_SYMBOL(caif_client_register_refcnt); diff --git a/net/caif/cfutill.c b/net/caif/cfutill.c deleted file mode 100644 index 5111090bb2c0..000000000000 --- a/net/caif/cfutill.c +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) ((struct cfsrvl *) layr) -#define UTIL_PAYLOAD 0x00 -#define UTIL_CMD_BIT 0x80 -#define UTIL_REMOTE_SHUTDOWN 0x82 -#define UTIL_FLOW_OFF 0x81 -#define UTIL_FLOW_ON 0x80 - -static int cfutill_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt); - -struct cflayer *cfutill_create(u8 channel_id, struct dev_info *dev_info) -{ - struct cfsrvl *util = kzalloc_obj(struct cfsrvl, GFP_ATOMIC); - if (!util) - return NULL; - caif_assert(offsetof(struct cfsrvl, layer) == 0); - cfsrvl_init(util, channel_id, dev_info, true); - util->layer.receive = cfutill_receive; - util->layer.transmit = cfutill_transmit; - snprintf(util->layer.name, CAIF_LAYER_NAME_SZ, "util1"); - return &util->layer; -} - -static int cfutill_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 cmd = -1; - struct cfsrvl *service = container_obj(layr); - caif_assert(layr != NULL); - caif_assert(layr->up != NULL); - caif_assert(layr->up->receive != NULL); - caif_assert(layr->up->ctrlcmd != NULL); - if (cfpkt_extr_head(pkt, &cmd, 1) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - - switch (cmd) { - case UTIL_PAYLOAD: - return layr->up->receive(layr->up, pkt); - case UTIL_FLOW_OFF: - layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0); - cfpkt_destroy(pkt); - return 0; - case UTIL_FLOW_ON: - layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0); - cfpkt_destroy(pkt); - return 0; - case UTIL_REMOTE_SHUTDOWN: /* Remote Shutdown Request */ - pr_err("REMOTE SHUTDOWN REQUEST RECEIVED\n"); - layr->ctrlcmd(layr, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, 0); - service->open = false; - cfpkt_destroy(pkt); - return 0; - default: - cfpkt_destroy(pkt); - pr_warn("Unknown service control %d (0x%x)\n", cmd, cmd); - return -EPROTO; - } -} - -static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 zero = 0; - struct caif_payload_info *info; - int ret; - struct cfsrvl *service = container_obj(layr); - caif_assert(layr != NULL); - caif_assert(layr->dn != NULL); - caif_assert(layr->dn->transmit != NULL); - - if (!cfsrvl_ready(service, &ret)) { - cfpkt_destroy(pkt); - return ret; - } - - cfpkt_add_head(pkt, &zero, 1); - /* Add info for MUX-layer to route the packet out. */ - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - /* - * To optimize alignment, we add up the size of CAIF header before - * payload. - */ - info->hdr_len = 1; - info->dev_info = &service->dev_info; - return layr->dn->transmit(layr->dn, pkt); -} diff --git a/net/caif/cfveil.c b/net/caif/cfveil.c deleted file mode 100644 index 53f844c49bbb..000000000000 --- a/net/caif/cfveil.c +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include - -#define VEI_PAYLOAD 0x00 -#define VEI_CMD_BIT 0x80 -#define VEI_FLOW_OFF 0x81 -#define VEI_FLOW_ON 0x80 -#define VEI_SET_PIN 0x82 - -#define container_obj(layr) container_of(layr, struct cfsrvl, layer) - -static int cfvei_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt); - -struct cflayer *cfvei_create(u8 channel_id, struct dev_info *dev_info) -{ - struct cfsrvl *vei = kzalloc_obj(struct cfsrvl, GFP_ATOMIC); - if (!vei) - return NULL; - caif_assert(offsetof(struct cfsrvl, layer) == 0); - cfsrvl_init(vei, channel_id, dev_info, true); - vei->layer.receive = cfvei_receive; - vei->layer.transmit = cfvei_transmit; - snprintf(vei->layer.name, CAIF_LAYER_NAME_SZ, "vei%d", channel_id); - return &vei->layer; -} - -static int cfvei_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 cmd; - int ret; - caif_assert(layr->up != NULL); - caif_assert(layr->receive != NULL); - caif_assert(layr->ctrlcmd != NULL); - - - if (cfpkt_extr_head(pkt, &cmd, 1) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - switch (cmd) { - case VEI_PAYLOAD: - ret = layr->up->receive(layr->up, pkt); - return ret; - case VEI_FLOW_OFF: - layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0); - cfpkt_destroy(pkt); - return 0; - case VEI_FLOW_ON: - layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0); - cfpkt_destroy(pkt); - return 0; - case VEI_SET_PIN: /* SET RS232 PIN */ - cfpkt_destroy(pkt); - return 0; - default: /* SET RS232 PIN */ - pr_warn("Unknown VEI control packet %d (0x%x)!\n", cmd, cmd); - cfpkt_destroy(pkt); - return -EPROTO; - } -} - -static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - u8 tmp = 0; - struct caif_payload_info *info; - int ret; - struct cfsrvl *service = container_obj(layr); - if (!cfsrvl_ready(service, &ret)) - goto err; - caif_assert(layr->dn != NULL); - caif_assert(layr->dn->transmit != NULL); - - if (cfpkt_add_head(pkt, &tmp, 1) < 0) { - pr_err("Packet is erroneous!\n"); - ret = -EPROTO; - goto err; - } - - /* Add info-> for MUX-layer to route the packet out. */ - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - info->hdr_len = 1; - info->dev_info = &service->dev_info; - return layr->dn->transmit(layr->dn, pkt); -err: - cfpkt_destroy(pkt); - return ret; -} diff --git a/net/caif/cfvidl.c b/net/caif/cfvidl.c deleted file mode 100644 index 39e075b0a259..000000000000 --- a/net/caif/cfvidl.c +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include - -#define container_obj(layr) ((struct cfsrvl *) layr) - -static int cfvidl_receive(struct cflayer *layr, struct cfpkt *pkt); -static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt); - -struct cflayer *cfvidl_create(u8 channel_id, struct dev_info *dev_info) -{ - struct cfsrvl *vid = kzalloc_obj(struct cfsrvl, GFP_ATOMIC); - if (!vid) - return NULL; - caif_assert(offsetof(struct cfsrvl, layer) == 0); - - cfsrvl_init(vid, channel_id, dev_info, false); - vid->layer.receive = cfvidl_receive; - vid->layer.transmit = cfvidl_transmit; - snprintf(vid->layer.name, CAIF_LAYER_NAME_SZ, "vid1"); - return &vid->layer; -} - -static int cfvidl_receive(struct cflayer *layr, struct cfpkt *pkt) -{ - u32 videoheader; - if (cfpkt_extr_head(pkt, &videoheader, 4) < 0) { - pr_err("Packet is erroneous!\n"); - cfpkt_destroy(pkt); - return -EPROTO; - } - return layr->up->receive(layr->up, pkt); -} - -static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt) -{ - struct cfsrvl *service = container_obj(layr); - struct caif_payload_info *info; - u32 videoheader = 0; - int ret; - - if (!cfsrvl_ready(service, &ret)) { - cfpkt_destroy(pkt); - return ret; - } - - cfpkt_add_head(pkt, &videoheader, 4); - /* Add info for MUX-layer to route the packet out */ - info = cfpkt_info(pkt); - info->channel_id = service->layer.id; - info->dev_info = &service->dev_info; - return layr->dn->transmit(layr->dn, pkt); -} diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c deleted file mode 100644 index fa6a3c2634a8..000000000000 --- a/net/caif/chnl_net.c +++ /dev/null @@ -1,531 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Authors: Sjur Brendeland - * Daniel Martensson - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* GPRS PDP connection has MTU to 1500 */ -#define GPRS_PDP_MTU 1500 -/* 5 sec. connect timeout */ -#define CONNECT_TIMEOUT (5 * HZ) -#define CAIF_NET_DEFAULT_QUEUE_LEN 500 -#define UNDEF_CONNID 0xffffffff - -/*This list is protected by the rtnl lock. */ -static LIST_HEAD(chnl_net_list); - -MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol GPRS network device"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_RTNL_LINK("caif"); - -enum caif_states { - CAIF_CONNECTED = 1, - CAIF_CONNECTING, - CAIF_DISCONNECTED, - CAIF_SHUTDOWN -}; - -struct chnl_net { - struct cflayer chnl; - struct caif_connect_request conn_req; - struct list_head list_field; - struct net_device *netdev; - wait_queue_head_t netmgmt_wq; - /* Flow status to remember and control the transmission. */ - bool flowenabled; - enum caif_states state; -}; - -static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) -{ - struct sk_buff *skb; - struct chnl_net *priv; - int pktlen; - const u8 *ip_version; - u8 buf; - - priv = container_of(layr, struct chnl_net, chnl); - - skb = (struct sk_buff *) cfpkt_tonative(pkt); - - /* Get length of CAIF packet. */ - pktlen = skb->len; - - /* Pass some minimum information and - * send the packet to the net stack. - */ - skb->dev = priv->netdev; - - /* check the version of IP */ - ip_version = skb_header_pointer(skb, 0, 1, &buf); - if (!ip_version) { - kfree_skb(skb); - return -EINVAL; - } - - switch (*ip_version >> 4) { - case 4: - skb->protocol = htons(ETH_P_IP); - break; - case 6: - skb->protocol = htons(ETH_P_IPV6); - break; - default: - kfree_skb(skb); - priv->netdev->stats.rx_errors++; - return -EINVAL; - } - - /* If we change the header in loop mode, the checksum is corrupted. */ - if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) - skb->ip_summed = CHECKSUM_UNNECESSARY; - else - skb->ip_summed = CHECKSUM_NONE; - - netif_rx(skb); - - /* Update statistics. */ - priv->netdev->stats.rx_packets++; - priv->netdev->stats.rx_bytes += pktlen; - - return 0; -} - -static int delete_device(struct chnl_net *dev) -{ - ASSERT_RTNL(); - if (dev->netdev) - unregister_netdevice(dev->netdev); - return 0; -} - -static void close_work(struct work_struct *work) -{ - struct chnl_net *dev = NULL; - struct list_head *list_node; - struct list_head *_tmp; - - rtnl_lock(); - list_for_each_safe(list_node, _tmp, &chnl_net_list) { - dev = list_entry(list_node, struct chnl_net, list_field); - if (dev->state == CAIF_SHUTDOWN) - dev_close(dev->netdev); - } - rtnl_unlock(); -} -static DECLARE_WORK(close_worker, close_work); - -static void chnl_hold(struct cflayer *lyr) -{ - struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl); - dev_hold(priv->netdev); -} - -static void chnl_put(struct cflayer *lyr) -{ - struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl); - dev_put(priv->netdev); -} - -static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, - int phyid) -{ - struct chnl_net *priv = container_of(layr, struct chnl_net, chnl); - pr_debug("NET flowctrl func called flow: %s\n", - flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" : - flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" : - flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" : - flow == CAIF_CTRLCMD_DEINIT_RSP ? "CLOSE/DEINIT" : - flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "OPEN_FAIL" : - flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? - "REMOTE_SHUTDOWN" : "UNKNOWN CTRL COMMAND"); - - - - switch (flow) { - case CAIF_CTRLCMD_FLOW_OFF_IND: - priv->flowenabled = false; - netif_stop_queue(priv->netdev); - break; - case CAIF_CTRLCMD_DEINIT_RSP: - priv->state = CAIF_DISCONNECTED; - break; - case CAIF_CTRLCMD_INIT_FAIL_RSP: - priv->state = CAIF_DISCONNECTED; - wake_up_interruptible(&priv->netmgmt_wq); - break; - case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: - priv->state = CAIF_SHUTDOWN; - netif_tx_disable(priv->netdev); - schedule_work(&close_worker); - break; - case CAIF_CTRLCMD_FLOW_ON_IND: - priv->flowenabled = true; - netif_wake_queue(priv->netdev); - break; - case CAIF_CTRLCMD_INIT_RSP: - caif_client_register_refcnt(&priv->chnl, chnl_hold, chnl_put); - priv->state = CAIF_CONNECTED; - priv->flowenabled = true; - netif_wake_queue(priv->netdev); - wake_up_interruptible(&priv->netmgmt_wq); - break; - default: - break; - } -} - -static netdev_tx_t chnl_net_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct chnl_net *priv; - struct cfpkt *pkt = NULL; - int len; - int result = -1; - /* Get our private data. */ - priv = netdev_priv(dev); - - if (skb->len > priv->netdev->mtu) { - pr_warn("Size of skb exceeded MTU\n"); - kfree_skb(skb); - dev->stats.tx_errors++; - return NETDEV_TX_OK; - } - - if (!priv->flowenabled) { - pr_debug("dropping packets flow off\n"); - kfree_skb(skb); - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - - if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) - swap(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); - - /* Store original SKB length. */ - len = skb->len; - - pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb); - - /* Send the packet down the stack. */ - result = priv->chnl.dn->transmit(priv->chnl.dn, pkt); - if (result) { - dev->stats.tx_dropped++; - return NETDEV_TX_OK; - } - - /* Update statistics. */ - dev->stats.tx_packets++; - dev->stats.tx_bytes += len; - - return NETDEV_TX_OK; -} - -static int chnl_net_open(struct net_device *dev) -{ - struct chnl_net *priv = NULL; - int result = -1; - int llifindex, headroom, tailroom, mtu; - struct net_device *lldev; - ASSERT_RTNL(); - priv = netdev_priv(dev); - if (!priv) { - pr_debug("chnl_net_open: no priv\n"); - return -ENODEV; - } - - if (priv->state != CAIF_CONNECTING) { - priv->state = CAIF_CONNECTING; - result = caif_connect_client(dev_net(dev), &priv->conn_req, - &priv->chnl, &llifindex, - &headroom, &tailroom); - if (result != 0) { - pr_debug("err: " - "Unable to register and open device," - " Err:%d\n", - result); - goto error; - } - - lldev = __dev_get_by_index(dev_net(dev), llifindex); - - if (lldev == NULL) { - pr_debug("no interface?\n"); - result = -ENODEV; - goto error; - } - - dev->needed_tailroom = tailroom + lldev->needed_tailroom; - dev->hard_header_len = headroom + lldev->hard_header_len + - lldev->needed_tailroom; - - /* - * MTU, head-room etc is not know before we have a - * CAIF link layer device available. MTU calculation may - * override initial RTNL configuration. - * MTU is minimum of current mtu, link layer mtu pluss - * CAIF head and tail, and PDP GPRS contexts max MTU. - */ - mtu = min_t(int, dev->mtu, lldev->mtu - (headroom + tailroom)); - mtu = min_t(int, GPRS_PDP_MTU, mtu); - dev_set_mtu(dev, mtu); - - if (mtu < 100) { - pr_warn("CAIF Interface MTU too small (%d)\n", mtu); - result = -ENODEV; - goto error; - } - } - - rtnl_unlock(); /* Release RTNL lock during connect wait */ - - result = wait_event_interruptible_timeout(priv->netmgmt_wq, - priv->state != CAIF_CONNECTING, - CONNECT_TIMEOUT); - - rtnl_lock(); - - if (result == -ERESTARTSYS) { - pr_debug("wait_event_interruptible woken by a signal\n"); - result = -ERESTARTSYS; - goto error; - } - - if (result == 0) { - pr_debug("connect timeout\n"); - result = -ETIMEDOUT; - goto error; - } - - if (priv->state != CAIF_CONNECTED) { - pr_debug("connect failed\n"); - result = -ECONNREFUSED; - goto error; - } - pr_debug("CAIF Netdevice connected\n"); - return 0; - -error: - caif_disconnect_client(dev_net(dev), &priv->chnl); - priv->state = CAIF_DISCONNECTED; - pr_debug("state disconnected\n"); - return result; - -} - -static int chnl_net_stop(struct net_device *dev) -{ - struct chnl_net *priv; - - ASSERT_RTNL(); - priv = netdev_priv(dev); - priv->state = CAIF_DISCONNECTED; - caif_disconnect_client(dev_net(dev), &priv->chnl); - return 0; -} - -static int chnl_net_init(struct net_device *dev) -{ - struct chnl_net *priv; - ASSERT_RTNL(); - priv = netdev_priv(dev); - INIT_LIST_HEAD(&priv->list_field); - return 0; -} - -static void chnl_net_uninit(struct net_device *dev) -{ - struct chnl_net *priv; - ASSERT_RTNL(); - priv = netdev_priv(dev); - list_del_init(&priv->list_field); -} - -static const struct net_device_ops netdev_ops = { - .ndo_open = chnl_net_open, - .ndo_stop = chnl_net_stop, - .ndo_init = chnl_net_init, - .ndo_uninit = chnl_net_uninit, - .ndo_start_xmit = chnl_net_start_xmit, -}; - -static void chnl_net_destructor(struct net_device *dev) -{ - struct chnl_net *priv = netdev_priv(dev); - caif_free_client(&priv->chnl); -} - -static void ipcaif_net_setup(struct net_device *dev) -{ - struct chnl_net *priv; - dev->netdev_ops = &netdev_ops; - dev->needs_free_netdev = true; - dev->priv_destructor = chnl_net_destructor; - dev->flags |= IFF_NOARP; - dev->flags |= IFF_POINTOPOINT; - dev->mtu = GPRS_PDP_MTU; - dev->tx_queue_len = CAIF_NET_DEFAULT_QUEUE_LEN; - - priv = netdev_priv(dev); - priv->chnl.receive = chnl_recv_cb; - priv->chnl.ctrlcmd = chnl_flowctrl_cb; - priv->netdev = dev; - priv->conn_req.protocol = CAIFPROTO_DATAGRAM; - priv->conn_req.link_selector = CAIF_LINK_HIGH_BANDW; - priv->conn_req.priority = CAIF_PRIO_LOW; - /* Insert illegal value */ - priv->conn_req.sockaddr.u.dgm.connection_id = UNDEF_CONNID; - priv->flowenabled = false; - - init_waitqueue_head(&priv->netmgmt_wq); -} - - -static int ipcaif_fill_info(struct sk_buff *skb, const struct net_device *dev) -{ - struct chnl_net *priv; - u8 loop; - priv = netdev_priv(dev); - if (nla_put_u32(skb, IFLA_CAIF_IPV4_CONNID, - priv->conn_req.sockaddr.u.dgm.connection_id) || - nla_put_u32(skb, IFLA_CAIF_IPV6_CONNID, - priv->conn_req.sockaddr.u.dgm.connection_id)) - goto nla_put_failure; - loop = priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP; - if (nla_put_u8(skb, IFLA_CAIF_LOOPBACK, loop)) - goto nla_put_failure; - return 0; -nla_put_failure: - return -EMSGSIZE; - -} - -static void caif_netlink_parms(struct nlattr *data[], - struct caif_connect_request *conn_req) -{ - if (!data) { - pr_warn("no params data found\n"); - return; - } - if (data[IFLA_CAIF_IPV4_CONNID]) - conn_req->sockaddr.u.dgm.connection_id = - nla_get_u32(data[IFLA_CAIF_IPV4_CONNID]); - if (data[IFLA_CAIF_IPV6_CONNID]) - conn_req->sockaddr.u.dgm.connection_id = - nla_get_u32(data[IFLA_CAIF_IPV6_CONNID]); - if (data[IFLA_CAIF_LOOPBACK]) { - if (nla_get_u8(data[IFLA_CAIF_LOOPBACK])) - conn_req->protocol = CAIFPROTO_DATAGRAM_LOOP; - else - conn_req->protocol = CAIFPROTO_DATAGRAM; - } -} - -static int ipcaif_newlink(struct net_device *dev, - struct rtnl_newlink_params *params, - struct netlink_ext_ack *extack) -{ - struct nlattr **data = params->data; - int ret; - struct chnl_net *caifdev; - ASSERT_RTNL(); - caifdev = netdev_priv(dev); - caif_netlink_parms(data, &caifdev->conn_req); - - ret = register_netdevice(dev); - if (ret) - pr_warn("device rtml registration failed\n"); - else - list_add(&caifdev->list_field, &chnl_net_list); - - /* Use ifindex as connection id, and use loopback channel default. */ - if (caifdev->conn_req.sockaddr.u.dgm.connection_id == UNDEF_CONNID) { - caifdev->conn_req.sockaddr.u.dgm.connection_id = dev->ifindex; - caifdev->conn_req.protocol = CAIFPROTO_DATAGRAM_LOOP; - } - return ret; -} - -static int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[], - struct nlattr *data[], - struct netlink_ext_ack *extack) -{ - struct chnl_net *caifdev; - ASSERT_RTNL(); - caifdev = netdev_priv(dev); - caif_netlink_parms(data, &caifdev->conn_req); - netdev_state_change(dev); - return 0; -} - -static size_t ipcaif_get_size(const struct net_device *dev) -{ - return - /* IFLA_CAIF_IPV4_CONNID */ - nla_total_size(4) + - /* IFLA_CAIF_IPV6_CONNID */ - nla_total_size(4) + - /* IFLA_CAIF_LOOPBACK */ - nla_total_size(2) + - 0; -} - -static const struct nla_policy ipcaif_policy[IFLA_CAIF_MAX + 1] = { - [IFLA_CAIF_IPV4_CONNID] = { .type = NLA_U32 }, - [IFLA_CAIF_IPV6_CONNID] = { .type = NLA_U32 }, - [IFLA_CAIF_LOOPBACK] = { .type = NLA_U8 } -}; - - -static struct rtnl_link_ops ipcaif_link_ops __read_mostly = { - .kind = "caif", - .priv_size = sizeof(struct chnl_net), - .setup = ipcaif_net_setup, - .maxtype = IFLA_CAIF_MAX, - .policy = ipcaif_policy, - .newlink = ipcaif_newlink, - .changelink = ipcaif_changelink, - .get_size = ipcaif_get_size, - .fill_info = ipcaif_fill_info, - -}; - -static int __init chnl_init_module(void) -{ - return rtnl_link_register(&ipcaif_link_ops); -} - -static void __exit chnl_exit_module(void) -{ - struct chnl_net *dev = NULL; - struct list_head *list_node; - struct list_head *_tmp; - rtnl_link_unregister(&ipcaif_link_ops); - rtnl_lock(); - list_for_each_safe(list_node, _tmp, &chnl_net_list) { - dev = list_entry(list_node, struct chnl_net, list_field); - list_del_init(list_node); - delete_device(dev); - } - rtnl_unlock(); -} - -module_init(chnl_init_module); -module_exit(chnl_exit_module); diff --git a/net/ceph/auth.c b/net/ceph/auth.c index 901b93530b21..17660bde896b 100644 --- a/net/ceph/auth.c +++ b/net/ceph/auth.c @@ -245,7 +245,7 @@ int ceph_handle_auth_reply(struct ceph_auth_client *ac, ac->protocol = 0; ac->ops = NULL; } - if (ac->protocol != protocol) { + if (!ac->protocol) { ret = init_protocol(ac, protocol); if (ret) { pr_err("auth protocol '%s' init failed: %d\n", @@ -257,7 +257,7 @@ int ceph_handle_auth_reply(struct ceph_auth_client *ac, ac->negotiating = false; } - if (result) { + if (result < 0) { pr_err("auth protocol '%s' mauth authentication failed: %d\n", ceph_auth_proto_name(ac->protocol), result); ret = result; diff --git a/net/ceph/auth_x.c b/net/ceph/auth_x.c index 692e0b868822..9e64e82d0b63 100644 --- a/net/ceph/auth_x.c +++ b/net/ceph/auth_x.c @@ -115,6 +115,11 @@ static int __ceph_x_decrypt(const struct ceph_crypto_key *key, int usage_slot, if (ret) return ret; + if (plaintext_len < sizeof(*hdr)) { + pr_err("%s plaintext too small %d\n", __func__, plaintext_len); + return -EINVAL; + } + hdr = p + ceph_crypt_data_offset(key); if (le64_to_cpu(hdr->magic) != CEPHX_ENC_MAGIC) { pr_err("%s bad magic\n", __func__); diff --git a/net/ceph/crush/crush.c b/net/ceph/crush/crush.c index 254ded0b05f6..521aec1d5fc0 100644 --- a/net/ceph/crush/crush.c +++ b/net/ceph/crush/crush.c @@ -47,7 +47,6 @@ int crush_get_bucket_item_weight(const struct crush_bucket *b, int p) void crush_destroy_bucket_uniform(struct crush_bucket_uniform *b) { kfree(b->h.items); - kfree(b); } void crush_destroy_bucket_list(struct crush_bucket_list *b) @@ -55,14 +54,12 @@ void crush_destroy_bucket_list(struct crush_bucket_list *b) kfree(b->item_weights); kfree(b->sum_weights); kfree(b->h.items); - kfree(b); } void crush_destroy_bucket_tree(struct crush_bucket_tree *b) { kfree(b->h.items); kfree(b->node_weights); - kfree(b); } void crush_destroy_bucket_straw(struct crush_bucket_straw *b) @@ -70,14 +67,12 @@ void crush_destroy_bucket_straw(struct crush_bucket_straw *b) kfree(b->straws); kfree(b->item_weights); kfree(b->h.items); - kfree(b); } void crush_destroy_bucket_straw2(struct crush_bucket_straw2 *b) { kfree(b->item_weights); kfree(b->h.items); - kfree(b); } void crush_destroy_bucket(struct crush_bucket *b) @@ -99,6 +94,7 @@ void crush_destroy_bucket(struct crush_bucket *b) crush_destroy_bucket_straw2((struct crush_bucket_straw2 *)b); break; } + kfree(b); } /** diff --git a/net/ceph/crush/mapper.c b/net/ceph/crush/mapper.c index 3a5bd1cd1e99..17b041779fb9 100644 --- a/net/ceph/crush/mapper.c +++ b/net/ceph/crush/mapper.c @@ -911,7 +911,7 @@ int crush_do_rule(const struct crush_map *map, int osize; const struct crush_rule *rule; __u32 step; - int i, j; + int i; int numrep; int out_size; /* @@ -1012,7 +1012,6 @@ int crush_do_rule(const struct crush_map *map, if (numrep <= 0) continue; } - j = 0; /* make sure bucket id is valid */ bno = -1 - w[i]; if (bno < 0 || bno >= map->max_buckets) { @@ -1036,7 +1035,7 @@ int crush_do_rule(const struct crush_map *map, weight, weight_max, x, numrep, curstep->arg2, - o+osize, j, + o+osize, 0, result_max-osize, choose_tries, recurse_tries, @@ -1058,7 +1057,7 @@ int crush_do_rule(const struct crush_map *map, weight, weight_max, x, out_size, numrep, curstep->arg2, - o+osize, j, + o+osize, 0, choose_tries, choose_leaf_tries ? choose_leaf_tries : 1, diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 108adb583744..34b3097b4c7b 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -368,8 +368,8 @@ static void ceph_sock_write_space(struct sock *sk) /* only queue to workqueue if there is data we want to write, * and there is sufficient space in the socket buffer to accept * more data. clear SOCK_NOSPACE so that ceph_sock_write_space() - * doesn't get called again until try_write() fills the socket - * buffer. See net/ipv4/tcp_input.c:tcp_check_space() + * doesn't get called again until ceph_con_v[12]_try_write() fills + * the socket buffer. See net/ipv4/tcp_input.c:tcp_check_space() * and net/core/stream.c:sk_stream_write_space(). */ if (ceph_con_flag_test(con, CEPH_CON_F_WRITE_PENDING)) { diff --git a/net/ceph/messenger_v2.c b/net/ceph/messenger_v2.c index 50f65820f623..05f6eea299fc 100644 --- a/net/ceph/messenger_v2.c +++ b/net/ceph/messenger_v2.c @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -2352,16 +2351,14 @@ bad: } /* - * Align session_key and con_secret to avoid GFP_ATOMIC allocation - * inside crypto_shash_setkey() and crypto_aead_setkey() called from - * setup_crypto(). __aligned(16) isn't guaranteed to work for stack - * objects, so do it by hand. + * Align con_secret to avoid GFP_ATOMIC allocation inside + * crypto_aead_setkey() called from setup_crypto(). __aligned(16) + * isn't guaranteed to work for stack objects, so do it by hand. */ static int process_auth_done(struct ceph_connection *con, void *p, void *end) { - u8 session_key_buf[CEPH_MAX_KEY_LEN + 16]; + u8 session_key[CEPH_MAX_KEY_LEN]; u8 con_secret_buf[CEPH_MAX_CON_SECRET_LEN + 16]; - u8 *session_key = PTR_ALIGN(&session_key_buf[0], 16); u8 *con_secret = PTR_ALIGN(&con_secret_buf[0], 16); int session_key_len, con_secret_len; int payload_len; @@ -2415,7 +2412,7 @@ static int process_auth_done(struct ceph_connection *con, void *p, void *end) con->state = CEPH_CON_S_V2_AUTH_SIGNATURE; out: - memzero_explicit(session_key_buf, sizeof(session_key_buf)); + memzero_explicit(session_key, sizeof(session_key)); memzero_explicit(con_secret_buf, sizeof(con_secret_buf)); return ret; diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index d5080530ce0c..d2cdc8ee3155 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -174,6 +174,8 @@ int ceph_monmap_contains(struct ceph_monmap *m, struct ceph_entity_addr *addr) */ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len) { + BUG_ON(len > monc->m_auth->front_alloc_len); + monc->pending_auth = 1; monc->m_auth->front.iov_len = len; monc->m_auth->hdr.front_len = cpu_to_le32(len); diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index c89e66d4fcb7..8b5b0587a0cf 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -72,8 +72,7 @@ static int crush_decode_uniform_bucket(void **p, void *end, struct crush_bucket_uniform *b) { dout("crush_decode_uniform_bucket %p to %p\n", *p, end); - ceph_decode_need(p, end, (1+b->h.size) * sizeof(u32), bad); - b->item_weight = ceph_decode_32(p); + ceph_decode_32_safe(p, end, b->item_weight, bad); return 0; bad: return -EINVAL; @@ -389,11 +388,15 @@ static int decode_choose_args(void **p, void *end, struct crush_map *c) goto fail; if (arg->ids_size && - arg->ids_size != c->buckets[bucket_index]->size) + (!c->buckets[bucket_index] || + arg->ids_size != c->buckets[bucket_index]->size)) goto e_inval; } - insert_choose_arg_map(&c->choose_args, arg_map); + if (!__insert_choose_arg_map(&c->choose_args, arg_map)) { + ret = -EEXIST; + goto fail; + } } return 0; @@ -516,6 +519,10 @@ static struct crush_map *crush_decode(void *pbyval, void *end) b->id = ceph_decode_32(p); b->type = ceph_decode_16(p); b->alg = ceph_decode_8(p); + if (b->alg != alg) { + b->alg = 0; + goto bad; + } b->hash = ceph_decode_8(p); b->weight = ceph_decode_32(p); b->size = ceph_decode_32(p); @@ -1702,7 +1709,7 @@ static int osdmap_decode(void **p, void *end, bool msgr2, ceph_decode_need(p, end, 3*sizeof(u32) + map->max_osd*(struct_v >= 5 ? sizeof(u32) : sizeof(u8)) + - sizeof(*map->osd_weight), e_inval); + map->max_osd*sizeof(*map->osd_weight), e_inval); if (ceph_decode_32(p) != map->max_osd) goto e_inval; diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index 14eb7812bda4..ecd659f79fd4 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -172,7 +172,7 @@ int bpf_sk_storage_clone(const struct sock *sk, struct sock *newsk) struct bpf_map *map; smap = rcu_dereference(SDATA(selem)->smap); - if (!(smap->map.map_flags & BPF_F_CLONE)) + if (!smap || !(smap->map.map_flags & BPF_F_CLONE)) continue; /* Note that for lockless listeners adding new element @@ -531,10 +531,10 @@ err_free: } EXPORT_SYMBOL_GPL(bpf_sk_storage_diag_alloc); -static int diag_get(struct bpf_local_storage_data *sdata, struct sk_buff *skb) +static int diag_get(struct bpf_local_storage_map *smap, + struct bpf_local_storage_data *sdata, struct sk_buff *skb) { struct nlattr *nla_stg, *nla_value; - struct bpf_local_storage_map *smap; /* It cannot exceed max nlattr's payload */ BUILD_BUG_ON(U16_MAX - NLA_HDRLEN < BPF_LOCAL_STORAGE_MAX_VALUE_SIZE); @@ -543,7 +543,6 @@ static int diag_get(struct bpf_local_storage_data *sdata, struct sk_buff *skb) if (!nla_stg) return -EMSGSIZE; - smap = rcu_dereference(sdata->smap); if (nla_put_u32(skb, SK_DIAG_BPF_STORAGE_MAP_ID, smap->map.id)) goto errout; @@ -558,6 +557,7 @@ static int diag_get(struct bpf_local_storage_data *sdata, struct sk_buff *skb) sdata->data, true); else copy_map_value(&smap->map, nla_data(nla_value), sdata->data); + check_and_init_map_value(&smap->map, nla_data(nla_value)); nla_nest_end(skb, nla_stg); return 0; @@ -596,9 +596,11 @@ static int bpf_sk_storage_diag_put_all(struct sock *sk, struct sk_buff *skb, saved_len = skb->len; hlist_for_each_entry_rcu(selem, &sk_storage->list, snode) { smap = rcu_dereference(SDATA(selem)->smap); + if (!smap) + continue; diag_size += nla_value_size(smap->map.value_size); - if (nla_stgs && diag_get(SDATA(selem), skb)) + if (nla_stgs && diag_get(smap, SDATA(selem), skb)) /* Continue to learn diag_size */ err = -EMSGSIZE; } @@ -665,7 +667,7 @@ int bpf_sk_storage_diag_put(struct bpf_sk_storage_diag *diag, diag_size += nla_value_size(diag->maps[i]->value_size); - if (nla_stgs && diag_get(sdata, skb)) + if (nla_stgs && diag_get((struct bpf_local_storage_map *)diag->maps[i], sdata, skb)) /* Continue to learn diag_size */ err = -EMSGSIZE; } diff --git a/net/core/dev.c b/net/core/dev.c index e59f6025067c..0c6c270d9f7d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -371,7 +371,7 @@ static void netdev_name_node_alt_free(struct rcu_head *head) static void __netdev_name_node_alt_destroy(struct netdev_name_node *name_node) { netdev_name_node_del(name_node); - list_del(&name_node->list); + list_del_rcu(&name_node->list); call_rcu(&name_node->rcu, netdev_name_node_alt_free); } @@ -5862,13 +5862,6 @@ static __latent_entropy void net_tx_action(void) xfrm_dev_backlog(sd); } -#if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_ATM_LANE) -/* This hook is defined here for ATM LANE */ -int (*br_fdb_test_addr_hook)(struct net_device *dev, - unsigned char *addr) __read_mostly; -EXPORT_SYMBOL_GPL(br_fdb_test_addr_hook); -#endif - /** * netdev_is_rx_handler_busy - check if receive handler is registered * @dev: device to check @@ -6869,9 +6862,9 @@ static void skb_defer_free_flush(void) #if defined(CONFIG_NET_RX_BUSY_POLL) -static void __busy_poll_stop(struct napi_struct *napi, bool skip_schedule) +static void __busy_poll_stop(struct napi_struct *napi, unsigned long timeout) { - if (!skip_schedule) { + if (!timeout) { gro_normal_list(&napi->gro); __napi_schedule(napi); return; @@ -6881,6 +6874,8 @@ static void __busy_poll_stop(struct napi_struct *napi, bool skip_schedule) gro_flush_normal(&napi->gro, HZ >= 1000); clear_bit(NAPI_STATE_SCHED, &napi->state); + hrtimer_start(&napi->timer, ns_to_ktime(timeout), + HRTIMER_MODE_REL_PINNED); } enum { @@ -6892,8 +6887,7 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, unsigned flags, u16 budget) { struct bpf_net_context __bpf_net_ctx, *bpf_net_ctx; - bool skip_schedule = false; - unsigned long timeout; + unsigned long timeout = 0; int rc; /* Busy polling means there is a high chance device driver hard irq @@ -6913,10 +6907,12 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, if (flags & NAPI_F_PREFER_BUSY_POLL) { napi->defer_hard_irqs_count = napi_get_defer_hard_irqs(napi); - timeout = napi_get_gro_flush_timeout(napi); - if (napi->defer_hard_irqs_count && timeout) { - hrtimer_start(&napi->timer, ns_to_ktime(timeout), HRTIMER_MODE_REL_PINNED); - skip_schedule = true; + if (napi->defer_hard_irqs_count) { + /* A short enough gro flush timeout and long enough + * poll can result in timer firing too early. + * Timer will be armed later if necessary. + */ + timeout = napi_get_gro_flush_timeout(napi); } } @@ -6931,7 +6927,7 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, trace_napi_poll(napi, rc, budget); netpoll_poll_unlock(have_poll_lock); if (rc == budget) - __busy_poll_stop(napi, skip_schedule); + __busy_poll_stop(napi, timeout); bpf_net_ctx_clear(bpf_net_ctx); local_bh_enable(); } @@ -9593,14 +9589,14 @@ static void dev_change_rx_flags(struct net_device *dev, int flags) ops->ndo_change_rx_flags(dev, flags); } -static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify) +int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify) { unsigned int old_flags = dev->flags; unsigned int promiscuity, flags; kuid_t uid; kgid_t gid; - ASSERT_RTNL(); + netdev_ops_assert_locked(dev); promiscuity = dev->promiscuity + inc; if (promiscuity == 0) { @@ -9636,16 +9632,8 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify) dev_change_rx_flags(dev, IFF_PROMISC); } - if (notify) { - /* The ops lock is only required to ensure consistent locking - * for `NETDEV_CHANGE` notifiers. This function is sometimes - * called without the lock, even for devices that are ops - * locked, such as in `dev_uc_sync_multiple` when using - * bonding or teaming. - */ - netdev_ops_assert_locked(dev); + if (notify) __dev_notify_flags(dev, old_flags, IFF_PROMISC, 0, NULL); - } return 0; } @@ -9667,7 +9655,7 @@ int netif_set_allmulti(struct net_device *dev, int inc, bool notify) unsigned int old_flags = dev->flags, old_gflags = dev->gflags; unsigned int allmulti, flags; - ASSERT_RTNL(); + netdev_ops_assert_locked(dev); allmulti = dev->allmulti + inc; if (allmulti == 0) { @@ -9697,46 +9685,6 @@ int netif_set_allmulti(struct net_device *dev, int inc, bool notify) return 0; } -/* - * Upload unicast and multicast address lists to device and - * configure RX filtering. When the device doesn't support unicast - * filtering it is put in promiscuous mode while unicast addresses - * are present. - */ -void __dev_set_rx_mode(struct net_device *dev) -{ - const struct net_device_ops *ops = dev->netdev_ops; - - /* dev_open will call this function so the list will stay sane. */ - if (!(dev->flags&IFF_UP)) - return; - - if (!netif_device_present(dev)) - return; - - if (!(dev->priv_flags & IFF_UNICAST_FLT)) { - /* Unicast addresses changes may only happen under the rtnl, - * therefore calling __dev_set_promiscuity here is safe. - */ - if (!netdev_uc_empty(dev) && !dev->uc_promisc) { - __dev_set_promiscuity(dev, 1, false); - dev->uc_promisc = true; - } else if (netdev_uc_empty(dev) && dev->uc_promisc) { - __dev_set_promiscuity(dev, -1, false); - dev->uc_promisc = false; - } - } - - if (ops->ndo_set_rx_mode) - ops->ndo_set_rx_mode(dev); -} - -void dev_set_rx_mode(struct net_device *dev) -{ - netif_addr_lock_bh(dev); - __dev_set_rx_mode(dev); - netif_addr_unlock_bh(dev); -} /** * netif_get_flags() - get flags reported to userspace @@ -9775,7 +9723,7 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags, unsigned int old_flags = dev->flags; int ret; - ASSERT_RTNL(); + netdev_ops_assert_locked(dev); /* * Set the flags on our device. @@ -11408,6 +11356,11 @@ int register_netdevice(struct net_device *dev) goto err_uninit; } + if (netdev_need_ops_lock(dev) && + dev->netdev_ops->ndo_set_rx_mode && + !dev->netdev_ops->ndo_set_rx_mode_async) + netdev_WARN(dev, "ops-locked drivers should use ndo_set_rx_mode_async\n"); + ret = netdev_do_alloc_pcpu_stats(dev); if (ret) goto err_uninit; @@ -12127,6 +12080,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, #endif mutex_init(&dev->lock); + INIT_LIST_HEAD(&dev->rx_mode_node); + __hw_addr_init(&dev->rx_mode_addr_cache); dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM; setup(dev); @@ -12231,6 +12186,8 @@ void free_netdev(struct net_device *dev) kfree(rcu_dereference_protected(dev->ingress_queue, 1)); + __hw_addr_flush(&dev->rx_mode_addr_cache); + /* Flush device addresses */ dev_addr_flush(dev); diff --git a/net/core/dev.h b/net/core/dev.h index 628bdaebf0ca..0cf24b8f5008 100644 --- a/net/core/dev.h +++ b/net/core/dev.h @@ -78,6 +78,7 @@ void linkwatch_run_queue(void); void dev_addr_flush(struct net_device *dev); int dev_addr_init(struct net_device *dev); void dev_addr_check(struct net_device *dev); +void __hw_addr_flush(struct netdev_hw_addr_list *list); #if IS_ENABLED(CONFIG_NET_SHAPER) void net_shaper_flush_netdev(struct net_device *dev); @@ -164,6 +165,9 @@ int netif_change_carrier(struct net_device *dev, bool new_carrier); int dev_change_carrier(struct net_device *dev, bool new_carrier); void __dev_set_rx_mode(struct net_device *dev); +int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify); +bool netif_rx_mode_clean(struct net_device *dev); +void netif_rx_mode_sync(struct net_device *dev); void __dev_notify_flags(struct net_device *dev, unsigned int old_flags, unsigned int gchanges, u32 portid, diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 76c91f224886..d73fcb0c6785 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -11,9 +11,18 @@ #include #include #include +#include +#include +#include #include "dev.h" +static void netdev_rx_mode_work(struct work_struct *work); + +static LIST_HEAD(rx_mode_list); +static DEFINE_SPINLOCK(rx_mode_lock); +static DECLARE_WORK(rx_mode_work, netdev_rx_mode_work); + /* * General list handling functions */ @@ -481,7 +490,7 @@ void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list, } EXPORT_SYMBOL(__hw_addr_unsync_dev); -static void __hw_addr_flush(struct netdev_hw_addr_list *list) +void __hw_addr_flush(struct netdev_hw_addr_list *list) { struct netdev_hw_addr *ha, *tmp; @@ -492,6 +501,7 @@ static void __hw_addr_flush(struct netdev_hw_addr_list *list) } list->count = 0; } +EXPORT_SYMBOL_IF_KUNIT(__hw_addr_flush); void __hw_addr_init(struct netdev_hw_addr_list *list) { @@ -501,6 +511,133 @@ void __hw_addr_init(struct netdev_hw_addr_list *list) } EXPORT_SYMBOL(__hw_addr_init); +static void __hw_addr_splice(struct netdev_hw_addr_list *dst, + struct netdev_hw_addr_list *src) +{ + src->tree = RB_ROOT; + list_splice_init(&src->list, &dst->list); + dst->count += src->count; + src->count = 0; +} + +/** + * __hw_addr_list_snapshot - create a snapshot copy of an address list + * @snap: destination snapshot list (needs to be __hw_addr_init-initialized) + * @list: source address list to snapshot + * @addr_len: length of addresses + * @cache: entry cache to reuse entries from; falls back to GFP_ATOMIC + * + * Creates a copy of @list reusing entries from @cache when available. + * Must be called under a spinlock. + * + * Return: 0 on success, -errno on failure. + */ +int __hw_addr_list_snapshot(struct netdev_hw_addr_list *snap, + const struct netdev_hw_addr_list *list, + int addr_len, struct netdev_hw_addr_list *cache) +{ + struct netdev_hw_addr *ha, *entry; + + list_for_each_entry(ha, &list->list, list) { + if (cache->count) { + entry = list_first_entry(&cache->list, + struct netdev_hw_addr, list); + list_del(&entry->list); + cache->count--; + memcpy(entry->addr, ha->addr, addr_len); + entry->type = ha->type; + entry->global_use = false; + entry->synced = 0; + } else { + entry = __hw_addr_create(ha->addr, addr_len, ha->type, + false, false); + if (!entry) { + __hw_addr_flush(snap); + return -ENOMEM; + } + } + entry->sync_cnt = ha->sync_cnt; + entry->refcount = ha->refcount; + + list_add_tail(&entry->list, &snap->list); + __hw_addr_insert(snap, entry, addr_len); + snap->count++; + } + + return 0; +} +EXPORT_SYMBOL_IF_KUNIT(__hw_addr_list_snapshot); + +/** + * __hw_addr_list_reconcile - sync snapshot changes back and free snapshots + * @real_list: the real address list to update + * @work: the working snapshot (modified by driver via __hw_addr_sync_dev) + * @ref: the reference snapshot (untouched copy of original state) + * @addr_len: length of addresses + * @cache: entry cache to return snapshot entries to for reuse + * + * Walks the reference snapshot and compares each entry against the work + * snapshot to compute sync_cnt deltas. Applies those deltas to @real_list. + * Returns snapshot entries to @cache for reuse; frees both snapshots. + * Caller must hold netif_addr_lock_bh. + */ +void __hw_addr_list_reconcile(struct netdev_hw_addr_list *real_list, + struct netdev_hw_addr_list *work, + struct netdev_hw_addr_list *ref, int addr_len, + struct netdev_hw_addr_list *cache) +{ + struct netdev_hw_addr *ref_ha, *tmp, *work_ha, *real_ha; + int delta; + + list_for_each_entry_safe(ref_ha, tmp, &ref->list, list) { + work_ha = __hw_addr_lookup(work, ref_ha->addr, addr_len, + ref_ha->type); + if (work_ha) + delta = work_ha->sync_cnt - ref_ha->sync_cnt; + else + delta = -1; + + if (delta == 0) + continue; + + real_ha = __hw_addr_lookup(real_list, ref_ha->addr, addr_len, + ref_ha->type); + if (!real_ha) { + /* The real entry was concurrently removed. If the + * driver synced this addr to hardware (delta > 0), + * re-insert it as a stale entry so the next work + * run unsyncs it from hardware. + */ + if (delta > 0) { + rb_erase(&ref_ha->node, &ref->tree); + list_del(&ref_ha->list); + ref->count--; + ref_ha->sync_cnt = delta; + ref_ha->refcount = delta; + list_add_tail_rcu(&ref_ha->list, + &real_list->list); + __hw_addr_insert(real_list, ref_ha, + addr_len); + real_list->count++; + } + continue; + } + + real_ha->sync_cnt += delta; + real_ha->refcount += delta; + if (!real_ha->refcount) { + rb_erase(&real_ha->node, &real_list->tree); + list_del_rcu(&real_ha->list); + kfree_rcu(real_ha, rcu_head); + real_list->count--; + } + } + + __hw_addr_splice(cache, work); + __hw_addr_splice(cache, ref); +} +EXPORT_SYMBOL_IF_KUNIT(__hw_addr_list_reconcile); + /* * Device addresses handling functions */ @@ -1049,3 +1186,249 @@ void dev_mc_init(struct net_device *dev) __hw_addr_init(&dev->mc); } EXPORT_SYMBOL(dev_mc_init); + +static int netif_addr_lists_snapshot(struct net_device *dev, + struct netdev_hw_addr_list *uc_snap, + struct netdev_hw_addr_list *mc_snap, + struct netdev_hw_addr_list *uc_ref, + struct netdev_hw_addr_list *mc_ref) +{ + int err; + + err = __hw_addr_list_snapshot(uc_snap, &dev->uc, dev->addr_len, + &dev->rx_mode_addr_cache); + if (!err) + err = __hw_addr_list_snapshot(uc_ref, &dev->uc, dev->addr_len, + &dev->rx_mode_addr_cache); + if (!err) + err = __hw_addr_list_snapshot(mc_snap, &dev->mc, + dev->addr_len, + &dev->rx_mode_addr_cache); + if (!err) + err = __hw_addr_list_snapshot(mc_ref, &dev->mc, dev->addr_len, + &dev->rx_mode_addr_cache); + + if (err) { + __hw_addr_flush(uc_snap); + __hw_addr_flush(uc_ref); + __hw_addr_flush(mc_snap); + } + + return err; +} + +static void netif_addr_lists_reconcile(struct net_device *dev, + struct netdev_hw_addr_list *uc_snap, + struct netdev_hw_addr_list *mc_snap, + struct netdev_hw_addr_list *uc_ref, + struct netdev_hw_addr_list *mc_ref) +{ + __hw_addr_list_reconcile(&dev->uc, uc_snap, uc_ref, dev->addr_len, + &dev->rx_mode_addr_cache); + __hw_addr_list_reconcile(&dev->mc, mc_snap, mc_ref, dev->addr_len, + &dev->rx_mode_addr_cache); +} + +/** + * netif_uc_promisc_update() - evaluate whether uc_promisc should be toggled. + * @dev: device + * + * Must be called under netif_addr_lock_bh. + * Return: +1 to enter promisc, -1 to leave, 0 for no change. + */ +static int netif_uc_promisc_update(struct net_device *dev) +{ + if (dev->priv_flags & IFF_UNICAST_FLT) + return 0; + + if (!netdev_uc_empty(dev) && !dev->uc_promisc) { + dev->uc_promisc = true; + return 1; + } + if (netdev_uc_empty(dev) && dev->uc_promisc) { + dev->uc_promisc = false; + return -1; + } + return 0; +} + +static void netif_rx_mode_run(struct net_device *dev) +{ + struct netdev_hw_addr_list uc_snap, mc_snap, uc_ref, mc_ref; + const struct net_device_ops *ops = dev->netdev_ops; + int promisc_inc; + int err; + + might_sleep(); + netdev_ops_assert_locked(dev); + + __hw_addr_init(&uc_snap); + __hw_addr_init(&mc_snap); + __hw_addr_init(&uc_ref); + __hw_addr_init(&mc_ref); + + if (!(dev->flags & IFF_UP) || !netif_device_present(dev)) + return; + + if (ops->ndo_set_rx_mode_async) { + netif_addr_lock_bh(dev); + err = netif_addr_lists_snapshot(dev, &uc_snap, &mc_snap, + &uc_ref, &mc_ref); + if (err) { + netdev_WARN(dev, "failed to sync uc/mc addresses\n"); + netif_addr_unlock_bh(dev); + return; + } + + promisc_inc = netif_uc_promisc_update(dev); + netif_addr_unlock_bh(dev); + } else { + netif_addr_lock_bh(dev); + promisc_inc = netif_uc_promisc_update(dev); + netif_addr_unlock_bh(dev); + } + + if (promisc_inc) + __dev_set_promiscuity(dev, promisc_inc, false); + + if (ops->ndo_set_rx_mode_async) { + ops->ndo_set_rx_mode_async(dev, &uc_snap, &mc_snap); + + netif_addr_lock_bh(dev); + netif_addr_lists_reconcile(dev, &uc_snap, &mc_snap, + &uc_ref, &mc_ref); + netif_addr_unlock_bh(dev); + } else if (ops->ndo_set_rx_mode) { + netif_addr_lock_bh(dev); + ops->ndo_set_rx_mode(dev); + netif_addr_unlock_bh(dev); + } +} + +static void netdev_rx_mode_work(struct work_struct *work) +{ + struct net_device *dev; + + rtnl_lock(); + + while (true) { + spin_lock_bh(&rx_mode_lock); + if (list_empty(&rx_mode_list)) { + spin_unlock_bh(&rx_mode_lock); + break; + } + dev = list_first_entry(&rx_mode_list, struct net_device, + rx_mode_node); + list_del_init(&dev->rx_mode_node); + /* We must free netdev tracker under + * the spinlock protection. + */ + netdev_tracker_free(dev, &dev->rx_mode_tracker); + spin_unlock_bh(&rx_mode_lock); + + netdev_lock_ops(dev); + netif_rx_mode_run(dev); + netdev_unlock_ops(dev); + /* Use __dev_put() because netdev_tracker_free() was already + * called above. Must be after netdev_unlock_ops() to prevent + * netdev_run_todo() from freeing the device while still in use. + */ + __dev_put(dev); + } + + rtnl_unlock(); +} + +static void netif_rx_mode_queue(struct net_device *dev) +{ + spin_lock_bh(&rx_mode_lock); + if (list_empty(&dev->rx_mode_node)) { + list_add_tail(&dev->rx_mode_node, &rx_mode_list); + netdev_hold(dev, &dev->rx_mode_tracker, GFP_ATOMIC); + } + spin_unlock_bh(&rx_mode_lock); + schedule_work(&rx_mode_work); +} + +/** + * __dev_set_rx_mode() - upload unicast and multicast address lists to device + * and configure RX filtering. + * @dev: device + * + * When the device doesn't support unicast filtering it is put in promiscuous + * mode while unicast addresses are present. + */ +void __dev_set_rx_mode(struct net_device *dev) +{ + const struct net_device_ops *ops = dev->netdev_ops; + int promisc_inc; + + /* dev_open will call this function so the list will stay sane. */ + if (!(dev->flags & IFF_UP)) + return; + + if (!netif_device_present(dev)) + return; + + if (ops->ndo_set_rx_mode_async || ops->ndo_change_rx_flags || + netdev_need_ops_lock(dev)) { + netif_rx_mode_queue(dev); + return; + } + + /* Legacy path for non-ops-locked HW devices. */ + + promisc_inc = netif_uc_promisc_update(dev); + if (promisc_inc) + __dev_set_promiscuity(dev, promisc_inc, false); + + if (ops->ndo_set_rx_mode) + ops->ndo_set_rx_mode(dev); +} + +void dev_set_rx_mode(struct net_device *dev) +{ + netif_addr_lock_bh(dev); + __dev_set_rx_mode(dev); + netif_addr_unlock_bh(dev); +} + +bool netif_rx_mode_clean(struct net_device *dev) +{ + bool clean = false; + + spin_lock_bh(&rx_mode_lock); + if (!list_empty(&dev->rx_mode_node)) { + list_del_init(&dev->rx_mode_node); + clean = true; + /* We must release netdev tracker under + * the spinlock protection. + */ + netdev_tracker_free(dev, &dev->rx_mode_tracker); + } + spin_unlock_bh(&rx_mode_lock); + + return clean; +} + +/** + * netif_rx_mode_sync() - sync rx mode inline + * @dev: network device + * + * Drivers implementing ndo_set_rx_mode_async() have their rx mode callback + * executed from a workqueue. This allows the callback to sleep, but means + * the hardware update is deferred and may not be visible to userspace + * by the time the initiating syscall returns. netif_rx_mode_sync() steals + * workqueue update and executes it inline. This preserves the atomicity of + * operations to the userspace. + */ +void netif_rx_mode_sync(struct net_device *dev) +{ + if (netif_rx_mode_clean(dev)) { + netif_rx_mode_run(dev); + /* Use __dev_put() because netdev_tracker_free() was already + * called inside netif_rx_mode_clean(). + */ + __dev_put(dev); + } +} diff --git a/net/core/dev_addr_lists_test.c b/net/core/dev_addr_lists_test.c index 8e1dba825e94..260e71a2399f 100644 --- a/net/core/dev_addr_lists_test.c +++ b/net/core/dev_addr_lists_test.c @@ -2,22 +2,31 @@ #include #include +#include #include #include static const struct net_device_ops dummy_netdev_ops = { }; +#define ADDR_A 1 +#define ADDR_B 2 +#define ADDR_C 3 + struct dev_addr_test_priv { u32 addr_seen; + u32 addr_synced; + u32 addr_unsynced; }; static int dev_addr_test_sync(struct net_device *netdev, const unsigned char *a) { struct dev_addr_test_priv *datp = netdev_priv(netdev); - if (a[0] < 31 && !memchr_inv(a, a[0], ETH_ALEN)) + if (a[0] < 31 && !memchr_inv(a, a[0], ETH_ALEN)) { datp->addr_seen |= 1 << a[0]; + datp->addr_synced |= 1 << a[0]; + } return 0; } @@ -26,11 +35,22 @@ static int dev_addr_test_unsync(struct net_device *netdev, { struct dev_addr_test_priv *datp = netdev_priv(netdev); - if (a[0] < 31 && !memchr_inv(a, a[0], ETH_ALEN)) + if (a[0] < 31 && !memchr_inv(a, a[0], ETH_ALEN)) { datp->addr_seen &= ~(1 << a[0]); + datp->addr_unsynced |= 1 << a[0]; + } return 0; } +static void dev_addr_test_reset(struct net_device *netdev) +{ + struct dev_addr_test_priv *datp = netdev_priv(netdev); + + datp->addr_seen = 0; + datp->addr_synced = 0; + datp->addr_unsynced = 0; +} + static int dev_addr_test_init(struct kunit *test) { struct dev_addr_test_priv *datp; @@ -225,6 +245,363 @@ static void dev_addr_test_add_excl(struct kunit *test) rtnl_unlock(); } +/* Snapshot test: basic sync with no concurrent modifications. + * Add one address, snapshot, driver syncs it, reconcile propagates + * sync_cnt delta back to real list. + */ +static void dev_addr_test_snapshot_sync(struct kunit *test) +{ + struct netdev_hw_addr_list snap, ref, cache; + struct net_device *netdev = test->priv; + struct dev_addr_test_priv *datp; + struct netdev_hw_addr *ha; + u8 addr[ETH_ALEN]; + + datp = netdev_priv(netdev); + + rtnl_lock(); + + memset(addr, ADDR_A, sizeof(addr)); + KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr)); + + /* Snapshot: ADDR_A has sync_cnt=0, refcount=1 (new) */ + netif_addr_lock_bh(netdev); + __hw_addr_init(&snap); + __hw_addr_init(&ref); + __hw_addr_init(&cache); + KUNIT_EXPECT_EQ(test, 0, + __hw_addr_list_snapshot(&snap, &netdev->uc, ETH_ALEN, + &cache)); + KUNIT_EXPECT_EQ(test, 0, + __hw_addr_list_snapshot(&ref, &netdev->uc, ETH_ALEN, + &cache)); + netif_addr_unlock_bh(netdev); + + /* Driver syncs ADDR_A to hardware */ + dev_addr_test_reset(netdev); + __hw_addr_sync_dev(&snap, netdev, dev_addr_test_sync, + dev_addr_test_unsync); + KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_synced); + KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced); + + /* Reconcile: delta=+1 applied to real entry */ + netif_addr_lock_bh(netdev); + __hw_addr_list_reconcile(&netdev->uc, &snap, &ref, ETH_ALEN, + &cache); + netif_addr_unlock_bh(netdev); + + /* Real entry should now reflect the sync: sync_cnt=1, refcount=2 */ + KUNIT_EXPECT_EQ(test, 1, netdev->uc.count); + ha = list_first_entry(&netdev->uc.list, struct netdev_hw_addr, list); + KUNIT_EXPECT_MEMEQ(test, ha->addr, addr, ETH_ALEN); + KUNIT_EXPECT_EQ(test, 1, ha->sync_cnt); + KUNIT_EXPECT_EQ(test, 2, ha->refcount); + + /* Second work run: already synced, nothing to do */ + dev_addr_test_reset(netdev); + __hw_addr_sync_dev(&netdev->uc, netdev, dev_addr_test_sync, + dev_addr_test_unsync); + KUNIT_EXPECT_EQ(test, 0, datp->addr_synced); + KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced); + KUNIT_EXPECT_EQ(test, 1, netdev->uc.count); + + __hw_addr_flush(&cache); + rtnl_unlock(); +} + +/* Snapshot test: ADDR_A synced to hardware, then concurrently removed + * from the real list before reconcile runs. Reconcile re-inserts ADDR_A as + * a stale entry so the next work run unsyncs it from hardware. + */ +static void dev_addr_test_snapshot_remove_during_sync(struct kunit *test) +{ + struct netdev_hw_addr_list snap, ref, cache; + struct net_device *netdev = test->priv; + struct dev_addr_test_priv *datp; + struct netdev_hw_addr *ha; + u8 addr[ETH_ALEN]; + + datp = netdev_priv(netdev); + + rtnl_lock(); + + memset(addr, ADDR_A, sizeof(addr)); + KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr)); + + /* Snapshot: ADDR_A is new (sync_cnt=0, refcount=1) */ + netif_addr_lock_bh(netdev); + __hw_addr_init(&snap); + __hw_addr_init(&ref); + __hw_addr_init(&cache); + KUNIT_EXPECT_EQ(test, 0, + __hw_addr_list_snapshot(&snap, &netdev->uc, ETH_ALEN, + &cache)); + KUNIT_EXPECT_EQ(test, 0, + __hw_addr_list_snapshot(&ref, &netdev->uc, ETH_ALEN, + &cache)); + netif_addr_unlock_bh(netdev); + + /* Driver syncs ADDR_A to hardware */ + dev_addr_test_reset(netdev); + __hw_addr_sync_dev(&snap, netdev, dev_addr_test_sync, + dev_addr_test_unsync); + KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_synced); + KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced); + + /* Concurrent removal: user deletes ADDR_A while driver was working */ + memset(addr, ADDR_A, sizeof(addr)); + KUNIT_EXPECT_EQ(test, 0, dev_uc_del(netdev, addr)); + KUNIT_EXPECT_EQ(test, 0, netdev->uc.count); + + /* Reconcile: ADDR_A gone from real list but driver synced it, + * so it gets re-inserted as stale (sync_cnt=1, refcount=1). + */ + netif_addr_lock_bh(netdev); + __hw_addr_list_reconcile(&netdev->uc, &snap, &ref, ETH_ALEN, + &cache); + netif_addr_unlock_bh(netdev); + + KUNIT_EXPECT_EQ(test, 1, netdev->uc.count); + ha = list_first_entry(&netdev->uc.list, struct netdev_hw_addr, list); + KUNIT_EXPECT_MEMEQ(test, ha->addr, addr, ETH_ALEN); + KUNIT_EXPECT_EQ(test, 1, ha->sync_cnt); + KUNIT_EXPECT_EQ(test, 1, ha->refcount); + + /* Second work run: stale entry gets unsynced from HW and removed */ + dev_addr_test_reset(netdev); + __hw_addr_sync_dev(&netdev->uc, netdev, dev_addr_test_sync, + dev_addr_test_unsync); + KUNIT_EXPECT_EQ(test, 0, datp->addr_synced); + KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_unsynced); + KUNIT_EXPECT_EQ(test, 0, netdev->uc.count); + + __hw_addr_flush(&cache); + rtnl_unlock(); +} + +/* Snapshot test: ADDR_A was stale (unsynced from hardware by driver), + * but concurrently re-added by the user. The re-add bumps refcount of + * the existing stale entry. Reconcile applies delta=-1, leaving ADDR_A + * as a fresh entry (sync_cnt=0, refcount=1) for the next work run. + */ +static void dev_addr_test_snapshot_readd_during_unsync(struct kunit *test) +{ + struct netdev_hw_addr_list snap, ref, cache; + struct net_device *netdev = test->priv; + struct dev_addr_test_priv *datp; + struct netdev_hw_addr *ha; + u8 addr[ETH_ALEN]; + + datp = netdev_priv(netdev); + + rtnl_lock(); + + memset(addr, ADDR_A, sizeof(addr)); + KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr)); + + /* Sync ADDR_A to hardware: sync_cnt=1, refcount=2 */ + dev_addr_test_reset(netdev); + __hw_addr_sync_dev(&netdev->uc, netdev, dev_addr_test_sync, + dev_addr_test_unsync); + KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_synced); + KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced); + + /* User removes ADDR_A: refcount=1, sync_cnt=1 -> stale */ + KUNIT_EXPECT_EQ(test, 0, dev_uc_del(netdev, addr)); + + /* Snapshot: ADDR_A is stale (sync_cnt=1, refcount=1) */ + netif_addr_lock_bh(netdev); + __hw_addr_init(&snap); + __hw_addr_init(&ref); + __hw_addr_init(&cache); + KUNIT_EXPECT_EQ(test, 0, + __hw_addr_list_snapshot(&snap, &netdev->uc, ETH_ALEN, + &cache)); + KUNIT_EXPECT_EQ(test, 0, + __hw_addr_list_snapshot(&ref, &netdev->uc, ETH_ALEN, + &cache)); + netif_addr_unlock_bh(netdev); + + /* Driver unsyncs stale ADDR_A from hardware */ + dev_addr_test_reset(netdev); + __hw_addr_sync_dev(&snap, netdev, dev_addr_test_sync, + dev_addr_test_unsync); + KUNIT_EXPECT_EQ(test, 0, datp->addr_synced); + KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_unsynced); + + /* Concurrent: user re-adds ADDR_A. dev_uc_add finds the existing + * stale entry and bumps refcount from 1 -> 2. sync_cnt stays 1. + */ + KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr)); + KUNIT_EXPECT_EQ(test, 1, netdev->uc.count); + + /* Reconcile: ref sync_cnt=1 matches real sync_cnt=1, delta=-1 + * applied. Result: sync_cnt=0, refcount=1 (fresh). + */ + netif_addr_lock_bh(netdev); + __hw_addr_list_reconcile(&netdev->uc, &snap, &ref, ETH_ALEN, + &cache); + netif_addr_unlock_bh(netdev); + + /* Entry survives as fresh: needs re-sync to HW */ + KUNIT_EXPECT_EQ(test, 1, netdev->uc.count); + ha = list_first_entry(&netdev->uc.list, struct netdev_hw_addr, list); + KUNIT_EXPECT_MEMEQ(test, ha->addr, addr, ETH_ALEN); + KUNIT_EXPECT_EQ(test, 0, ha->sync_cnt); + KUNIT_EXPECT_EQ(test, 1, ha->refcount); + + /* Second work run: fresh entry gets synced to HW */ + dev_addr_test_reset(netdev); + __hw_addr_sync_dev(&netdev->uc, netdev, dev_addr_test_sync, + dev_addr_test_unsync); + KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_synced); + KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced); + + __hw_addr_flush(&cache); + rtnl_unlock(); +} + +/* Snapshot test: ADDR_A is new (synced by driver), and independent ADDR_B + * is concurrently removed from the real list. A's sync delta propagates + * normally; B's absence doesn't interfere. + */ +static void dev_addr_test_snapshot_add_and_remove(struct kunit *test) +{ + struct netdev_hw_addr_list snap, ref, cache; + struct net_device *netdev = test->priv; + struct dev_addr_test_priv *datp; + struct netdev_hw_addr *ha; + u8 addr[ETH_ALEN]; + + datp = netdev_priv(netdev); + + rtnl_lock(); + + /* Add ADDR_A and ADDR_B (will be synced then removed) */ + memset(addr, ADDR_A, sizeof(addr)); + KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr)); + memset(addr, ADDR_B, sizeof(addr)); + KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr)); + + /* Sync both to hardware: sync_cnt=1, refcount=2 */ + __hw_addr_sync_dev(&netdev->uc, netdev, dev_addr_test_sync, + dev_addr_test_unsync); + + /* Add ADDR_C (new, will be synced by snapshot) */ + memset(addr, ADDR_C, sizeof(addr)); + KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr)); + + /* Snapshot: A,B synced (sync_cnt=1,refcount=2); C new (0,1) */ + netif_addr_lock_bh(netdev); + __hw_addr_init(&snap); + __hw_addr_init(&ref); + __hw_addr_init(&cache); + KUNIT_EXPECT_EQ(test, 0, + __hw_addr_list_snapshot(&snap, &netdev->uc, ETH_ALEN, + &cache)); + KUNIT_EXPECT_EQ(test, 0, + __hw_addr_list_snapshot(&ref, &netdev->uc, ETH_ALEN, + &cache)); + netif_addr_unlock_bh(netdev); + + /* Driver syncs snapshot: ADDR_C is new -> synced; A,B already synced */ + dev_addr_test_reset(netdev); + __hw_addr_sync_dev(&snap, netdev, dev_addr_test_sync, + dev_addr_test_unsync); + KUNIT_EXPECT_EQ(test, 1 << ADDR_C, datp->addr_synced); + KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced); + + /* Concurrent: user removes addr B while driver was working */ + memset(addr, ADDR_B, sizeof(addr)); + KUNIT_EXPECT_EQ(test, 0, dev_uc_del(netdev, addr)); + + /* Reconcile: ADDR_C's delta=+1 applied to real list. + * ADDR_B's delta=0 (unchanged in snapshot), + * so nothing to apply to ADDR_B. + */ + netif_addr_lock_bh(netdev); + __hw_addr_list_reconcile(&netdev->uc, &snap, &ref, ETH_ALEN, + &cache); + netif_addr_unlock_bh(netdev); + + /* ADDR_A: unchanged (sync_cnt=1, refcount=2) + * ADDR_B: refcount went from 2->1 via dev_uc_del (still present, stale) + * ADDR_C: sync propagated (sync_cnt=1, refcount=2) + */ + KUNIT_EXPECT_EQ(test, 3, netdev->uc.count); + netdev_hw_addr_list_for_each(ha, &netdev->uc) { + u8 id = ha->addr[0]; + + if (!memchr_inv(ha->addr, id, ETH_ALEN)) { + if (id == ADDR_A) { + KUNIT_EXPECT_EQ(test, 1, ha->sync_cnt); + KUNIT_EXPECT_EQ(test, 2, ha->refcount); + } else if (id == ADDR_B) { + /* B: still present but now stale */ + KUNIT_EXPECT_EQ(test, 1, ha->sync_cnt); + KUNIT_EXPECT_EQ(test, 1, ha->refcount); + } else if (id == ADDR_C) { + KUNIT_EXPECT_EQ(test, 1, ha->sync_cnt); + KUNIT_EXPECT_EQ(test, 2, ha->refcount); + } + } + } + + /* Second work run: ADDR_B is stale, gets unsynced and removed */ + dev_addr_test_reset(netdev); + __hw_addr_sync_dev(&netdev->uc, netdev, dev_addr_test_sync, + dev_addr_test_unsync); + KUNIT_EXPECT_EQ(test, 0, datp->addr_synced); + KUNIT_EXPECT_EQ(test, 1 << ADDR_B, datp->addr_unsynced); + KUNIT_EXPECT_EQ(test, 2, netdev->uc.count); + + __hw_addr_flush(&cache); + rtnl_unlock(); +} + +static void dev_addr_test_snapshot_benchmark(struct kunit *test) +{ + struct net_device *netdev = test->priv; + struct netdev_hw_addr_list snap, cache; + u8 addr[ETH_ALEN]; + s64 duration = 0; + ktime_t start; + int i, iter; + + rtnl_lock(); + + for (i = 0; i < 1024; i++) { + memset(addr, 0, sizeof(addr)); + addr[0] = (i >> 8) & 0xff; + addr[1] = i & 0xff; + KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr)); + } + + __hw_addr_init(&cache); + + for (iter = 0; iter < 1000; iter++) { + netif_addr_lock_bh(netdev); + __hw_addr_init(&snap); + + start = ktime_get(); + KUNIT_EXPECT_EQ(test, 0, + __hw_addr_list_snapshot(&snap, &netdev->uc, + ETH_ALEN, &cache)); + duration += ktime_to_ns(ktime_sub(ktime_get(), start)); + + netif_addr_unlock_bh(netdev); + __hw_addr_flush(&snap); + } + + __hw_addr_flush(&cache); + + kunit_info(test, + "1024 addrs x 1000 snapshots: %lld ns total, %lld ns/iter", + duration, div_s64(duration, 1000)); + + rtnl_unlock(); +} + static struct kunit_case dev_addr_test_cases[] = { KUNIT_CASE(dev_addr_test_basic), KUNIT_CASE(dev_addr_test_sync_one), @@ -232,6 +609,11 @@ static struct kunit_case dev_addr_test_cases[] = { KUNIT_CASE(dev_addr_test_del_main), KUNIT_CASE(dev_addr_test_add_set), KUNIT_CASE(dev_addr_test_add_excl), + KUNIT_CASE(dev_addr_test_snapshot_sync), + KUNIT_CASE(dev_addr_test_snapshot_remove_during_sync), + KUNIT_CASE(dev_addr_test_snapshot_readd_during_unsync), + KUNIT_CASE(dev_addr_test_snapshot_add_and_remove), + KUNIT_CASE_SLOW(dev_addr_test_snapshot_benchmark), {} }; @@ -243,5 +625,6 @@ static struct kunit_suite dev_addr_test_suite = { }; kunit_test_suite(dev_addr_test_suite); +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); MODULE_DESCRIPTION("KUnit tests for struct netdev_hw_addr_list"); MODULE_LICENSE("GPL"); diff --git a/net/core/dev_api.c b/net/core/dev_api.c index f28852078aa6..437947dd08ed 100644 --- a/net/core/dev_api.c +++ b/net/core/dev_api.c @@ -66,6 +66,7 @@ int dev_change_flags(struct net_device *dev, unsigned int flags, netdev_lock_ops(dev); ret = netif_change_flags(dev, flags, extack); + netif_rx_mode_sync(dev); netdev_unlock_ops(dev); return ret; @@ -285,6 +286,7 @@ int dev_set_promiscuity(struct net_device *dev, int inc) netdev_lock_ops(dev); ret = netif_set_promiscuity(dev, inc); + netif_rx_mode_sync(dev); netdev_unlock_ops(dev); return ret; @@ -311,6 +313,7 @@ int dev_set_allmulti(struct net_device *dev, int inc) netdev_lock_ops(dev); ret = netif_set_allmulti(dev, inc, true); + netif_rx_mode_sync(dev); netdev_unlock_ops(dev); return ret; diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index 7a8966544c9d..f3979b276090 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -586,24 +586,26 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data, return err; case SIOCADDMULTI: - if (!ops->ndo_set_rx_mode || + if ((!ops->ndo_set_rx_mode && !ops->ndo_set_rx_mode_async) || ifr->ifr_hwaddr.sa_family != AF_UNSPEC) return -EINVAL; if (!netif_device_present(dev)) return -ENODEV; netdev_lock_ops(dev); err = dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data); + netif_rx_mode_sync(dev); netdev_unlock_ops(dev); return err; case SIOCDELMULTI: - if (!ops->ndo_set_rx_mode || + if ((!ops->ndo_set_rx_mode && !ops->ndo_set_rx_mode_async) || ifr->ifr_hwaddr.sa_family != AF_UNSPEC) return -EINVAL; if (!netif_device_present(dev)) return -ENODEV; netdev_lock_ops(dev); err = dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data); + netif_rx_mode_sync(dev); netdev_unlock_ops(dev); return err; diff --git a/net/core/devmem.c b/net/core/devmem.c index cde4c89bc146..468344739db2 100644 --- a/net/core/devmem.c +++ b/net/core/devmem.c @@ -297,8 +297,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, for (i = 0; i < owner->area.num_niovs; i++) { niov = &owner->area.niovs[i]; - niov->type = NET_IOV_DMABUF; - niov->owner = &owner->area; + net_iov_init(niov, &owner->area, NET_IOV_DMABUF); page_pool_set_dma_addr_netmem(net_iov_to_netmem(niov), net_devmem_get_dma_addr(niov)); if (direction == DMA_TO_DEVICE) diff --git a/net/core/failover.c b/net/core/failover.c index 11bb183c7a1b..e43c59cd6868 100644 --- a/net/core/failover.c +++ b/net/core/failover.c @@ -12,6 +12,7 @@ #include #include #include +#include #include static LIST_HEAD(failover_list); @@ -221,8 +222,11 @@ failover_existing_slave_register(struct net_device *failover_dev) for_each_netdev(net, dev) { if (netif_is_failover(dev)) continue; - if (ether_addr_equal(failover_dev->perm_addr, dev->perm_addr)) + if (ether_addr_equal(failover_dev->perm_addr, dev->perm_addr)) { + netdev_lock_ops(dev); failover_slave_register(dev); + netdev_unlock_ops(dev); + } } rtnl_unlock(); } diff --git a/net/core/filter.c b/net/core/filter.c index fcfcb72663ca..9590877b0714 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -503,7 +503,7 @@ static bool convert_bpf_ld_abs(struct sock_filter *fp, struct bpf_insn **insnp) ((unaligned_ok && offset >= 0) || (!unaligned_ok && offset >= 0 && offset + ip_align >= 0 && - offset + ip_align % size == 0))) { + (offset + ip_align) % size == 0))) { bool ldx_off_ok = offset <= S16_MAX; *insn++ = BPF_MOV64_REG(BPF_REG_TMP, BPF_REG_H); @@ -1654,15 +1654,24 @@ err_prog_put: return err; } +static void sk_reuseport_prog_free_rcu(struct rcu_head *rcu) +{ + struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu); + struct bpf_prog *prog = aux->prog; + + bpf_release_orig_filter(prog); + bpf_prog_free(prog); +} + void sk_reuseport_prog_free(struct bpf_prog *prog) { if (!prog) return; - if (prog->type == BPF_PROG_TYPE_SK_REUSEPORT) - bpf_prog_put(prog); + if (bpf_prog_was_classic(prog)) + call_rcu(&prog->aux->rcu, sk_reuseport_prog_free_rcu); else - bpf_prog_destroy(prog); + bpf_prog_put(prog); } static inline int __bpf_try_make_writable(struct sk_buff *skb, @@ -5396,7 +5405,7 @@ static int bpf_sol_tcp_setsockopt(struct sock *sk, int optname, if (val <= 0) return -EINVAL; tp->snd_cwnd_clamp = val; - tp->snd_ssthresh = val; + WRITE_ONCE(tp->snd_ssthresh, val); break; case TCP_BPF_DELACK_MAX: timeout = usecs_to_jiffies(val); @@ -5481,7 +5490,7 @@ static int sol_tcp_sockopt(struct sock *sk, int optname, char *optval, int *optlen, bool getopt) { - if (sk->sk_protocol != IPPROTO_TCP) + if (!sk_is_tcp(sk)) return -EINVAL; switch (optname) { @@ -5688,6 +5697,30 @@ const struct bpf_func_proto bpf_sk_getsockopt_proto = { .arg5_type = ARG_CONST_SIZE, }; +BPF_CALL_5(bpf_sk_setsockopt_nodelay, struct sock *, sk, int, level, + int, optname, char *, optval, int, optlen) +{ + /* + * TCP_NODELAY triggers tcp_push_pending_frames() and re-enters + * CA_EVENT_TX_START in bpf_tcp_cc. + */ + if (level == SOL_TCP && optname == TCP_NODELAY) + return -EOPNOTSUPP; + + return _bpf_setsockopt(sk, level, optname, optval, optlen); +} + +const struct bpf_func_proto bpf_sk_setsockopt_nodelay_proto = { + .func = bpf_sk_setsockopt_nodelay, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, + .arg5_type = ARG_CONST_SIZE, +}; + BPF_CALL_5(bpf_unlocked_sk_setsockopt, struct sock *, sk, int, level, int, optname, char *, optval, int, optlen) { @@ -5833,6 +5866,12 @@ BPF_CALL_5(bpf_sock_ops_setsockopt, struct bpf_sock_ops_kern *, bpf_sock, if (!is_locked_tcp_sock_ops(bpf_sock)) return -EOPNOTSUPP; + /* TCP_NODELAY triggers tcp_push_pending_frames() and re-enters these callbacks. */ + if ((bpf_sock->op == BPF_SOCK_OPS_HDR_OPT_LEN_CB || + bpf_sock->op == BPF_SOCK_OPS_WRITE_HDR_OPT_CB) && + level == SOL_TCP && optname == TCP_NODELAY) + return -EOPNOTSUPP; + return _bpf_setsockopt(bpf_sock->sk, level, optname, optval, optlen); } @@ -6443,6 +6482,8 @@ BPF_CALL_4(bpf_skb_fib_lookup, struct sk_buff *, skb, * against MTU of FIB lookup resulting net_device */ dev = dev_get_by_index_rcu(net, params->ifindex); + if (unlikely(!dev)) + return -ENODEV; if (!is_skb_forwardable(dev, skb)) rc = BPF_FIB_LKUP_RET_FRAG_NEEDED; @@ -7443,7 +7484,7 @@ u32 bpf_tcp_sock_convert_ctx_access(enum bpf_access_type type, BPF_CALL_1(bpf_tcp_sock, struct sock *, sk) { - if (sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP) + if (sk_fullsock(sk) && sk_is_tcp(sk)) return (unsigned long)sk; return (unsigned long)NULL; @@ -11915,7 +11956,7 @@ BPF_CALL_1(bpf_skc_to_tcp6_sock, struct sock *, sk) */ BTF_TYPE_EMIT(struct tcp6_sock); if (sk && sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP && - sk->sk_family == AF_INET6) + sk->sk_type == SOCK_STREAM && sk->sk_family == AF_INET6) return (unsigned long)sk; return (unsigned long)NULL; @@ -11931,7 +11972,7 @@ const struct bpf_func_proto bpf_skc_to_tcp6_sock_proto = { BPF_CALL_1(bpf_skc_to_tcp_sock, struct sock *, sk) { - if (sk && sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP) + if (sk && sk_fullsock(sk) && sk_is_tcp(sk)) return (unsigned long)sk; return (unsigned long)NULL; diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 1b61bb25ba0e..2a98f5fa74eb 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -1374,16 +1374,13 @@ proto_again: break; } - /* least significant bit of the most significant octet - * indicates if protocol field was compressed + /* PFC (compressed 1-byte protocol) frames are not processed. + * A compressed protocol field has the least significant bit of + * the most significant octet set, which will fail the following + * ppp_proto_is_valid(), returning FLOW_DISSECT_RET_OUT_BAD. */ ppp_proto = ntohs(hdr->proto); - if (ppp_proto & 0x0100) { - ppp_proto = ppp_proto >> 8; - nhoff += PPPOE_SES_HLEN - 1; - } else { - nhoff += PPPOE_SES_HLEN; - } + nhoff += PPPOE_SES_HLEN; if (ppp_proto == PPP_IP) { proto = htons(ETH_P_IP); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 9e12524b67fa..5d9216016507 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -3210,8 +3210,10 @@ int neigh_xmit(int index, struct net_device *dev, rcu_read_lock(); tbl = rcu_dereference(neigh_tables[index]); - if (!tbl) - goto out_unlock; + if (!tbl) { + rcu_read_unlock(); + goto out_kfree_skb; + } if (index == NEIGH_ARP_TABLE) { u32 key = *((u32 *)addr); @@ -3227,7 +3229,6 @@ int neigh_xmit(int index, struct net_device *dev, goto out_kfree_skb; } err = READ_ONCE(neigh->output)(neigh, skb); -out_unlock: rcu_read_unlock(); } else if (index == NEIGH_LINK_TABLE) { @@ -3237,11 +3238,10 @@ out_unlock: goto out_kfree_skb; err = dev_queue_xmit(skb); } -out: return err; out_kfree_skb: kfree_skb(skb); - goto out; + return err; } EXPORT_SYMBOL(neigh_xmit); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index cd74beffd209..84faace50ac2 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -608,14 +608,16 @@ EXPORT_SYMBOL_GPL(__netpoll_setup); /* * Returns a pointer to a string representation of the identifier used * to select the egress interface for the given netpoll instance. buf - * must be a buffer of length at least MAC_ADDR_STR_LEN + 1. + * is used to format np->dev_mac when np->dev_name is empty; bufsz must + * be at least MAC_ADDR_STR_LEN + 1 to fit the formatted MAC address + * and its NUL terminator. */ -static char *egress_dev(struct netpoll *np, char *buf) +static char *egress_dev(struct netpoll *np, char *buf, size_t bufsz) { if (np->dev_name[0]) return np->dev_name; - snprintf(buf, MAC_ADDR_STR_LEN, "%pM", np->dev_mac); + snprintf(buf, bufsz, "%pM", np->dev_mac); return buf; } @@ -645,7 +647,7 @@ static int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev) if (!IS_ENABLED(CONFIG_IPV6)) { np_err(np, "IPv6 is not supported %s, aborting\n", - egress_dev(np, buf)); + egress_dev(np, buf, sizeof(buf))); return -EINVAL; } @@ -667,7 +669,7 @@ static int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev) } if (err) { np_err(np, "no IPv6 address for %s, aborting\n", - egress_dev(np, buf)); + egress_dev(np, buf, sizeof(buf))); return err; } @@ -687,14 +689,14 @@ static int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev) in_dev = __in_dev_get_rtnl(ndev); if (!in_dev) { np_err(np, "no IP address for %s, aborting\n", - egress_dev(np, buf)); + egress_dev(np, buf, sizeof(buf))); return -EDESTADDRREQ; } ifa = rtnl_dereference(in_dev->ifa_list); if (!ifa) { np_err(np, "no IP address for %s, aborting\n", - egress_dev(np, buf)); + egress_dev(np, buf, sizeof(buf))); return -EDESTADDRREQ; } @@ -704,6 +706,23 @@ static int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev) return 0; } +/* + * Test whether the caller left np->local_ip unset, so that + * netpoll_setup() should auto-populate it from the egress device. + * + * np->local_ip is a union of __be32 (IPv4) and struct in6_addr (IPv6), + * so an IPv6 address whose first 4 bytes are zero (e.g. ::1, ::2, + * IPv4-mapped ::ffff:a.b.c.d) must not be tested via the IPv4 arm — + * doing so would misclassify a caller-supplied address as unset and + * silently overwrite it with whatever address the device exposes. + */ +static bool netpoll_local_ip_unset(const struct netpoll *np) +{ + if (np->ipv6) + return ipv6_addr_any(&np->local_ip.in6); + return !np->local_ip.ip; +} + int netpoll_setup(struct netpoll *np) { struct net *net = current->nsproxy->net_ns; @@ -719,7 +738,8 @@ int netpoll_setup(struct netpoll *np) ndev = dev_getbyhwaddr(net, ARPHRD_ETHER, np->dev_mac); if (!ndev) { - np_err(np, "%s doesn't exist, aborting\n", egress_dev(np, buf)); + np_err(np, "%s doesn't exist, aborting\n", + egress_dev(np, buf, sizeof(buf))); err = -ENODEV; goto unlock; } @@ -727,14 +747,14 @@ int netpoll_setup(struct netpoll *np) if (netdev_master_upper_dev_get(ndev)) { np_err(np, "%s is a slave device, aborting\n", - egress_dev(np, buf)); + egress_dev(np, buf, sizeof(buf))); err = -EBUSY; goto put; } if (!netif_running(ndev)) { np_info(np, "device %s not up yet, forcing it\n", - egress_dev(np, buf)); + egress_dev(np, buf, sizeof(buf))); err = dev_open(ndev, NULL); if (err) { @@ -747,7 +767,7 @@ int netpoll_setup(struct netpoll *np) rtnl_lock(); } - if (!np->local_ip.ip) { + if (netpoll_local_ip_unset(np)) { if (!np->ipv6) { err = netpoll_take_ipv4(np, ndev); if (err) diff --git a/net/core/page_pool.c b/net/core/page_pool.c index 877bbf7a1938..6e576dec80db 100644 --- a/net/core/page_pool.c +++ b/net/core/page_pool.c @@ -327,6 +327,11 @@ static void page_pool_uninit(struct page_pool *pool) if (!pool->system) free_percpu(pool->recycle_stats); #endif + + if (pool->mp_ops) { + pool->mp_ops->destroy(pool); + static_branch_dec(&page_pool_mem_providers); + } } /** @@ -1146,11 +1151,6 @@ static void __page_pool_destroy(struct page_pool *pool) page_pool_unlist(pool); page_pool_uninit(pool); - if (pool->mp_ops) { - pool->mp_ops->destroy(pool); - static_branch_dec(&page_pool_mem_providers); - } - kfree(pool); } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 69daba3ddaf0..df042da422ef 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1572,6 +1572,7 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb, port_guid.vf = ivi.vf; memcpy(vf_mac.mac, ivi.mac, sizeof(ivi.mac)); + memset(&vf_broadcast, 0, sizeof(vf_broadcast)); memcpy(vf_broadcast.broadcast, dev->broadcast, dev->addr_len); vf_vlan.vlan = ivi.vlan; vf_vlan.qos = ivi.qos; @@ -3431,6 +3432,7 @@ errout: dev->name); } + netif_rx_mode_sync(dev); netdev_unlock_ops(dev); return err; diff --git a/net/core/sock_map.c b/net/core/sock_map.c index b0e96337a269..99e3789492a0 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -530,7 +530,7 @@ static bool sock_map_redirect_allowed(const struct sock *sk) if (sk_is_tcp(sk)) return sk->sk_state != TCP_LISTEN; else - return sk->sk_state == TCP_ESTABLISHED; + return READ_ONCE(sk->sk_state) == TCP_ESTABLISHED; } static bool sock_map_sk_is_suitable(const struct sock *sk) @@ -543,7 +543,7 @@ static bool sock_map_sk_state_allowed(const struct sock *sk) if (sk_is_tcp(sk)) return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_LISTEN); if (sk_is_stream_unix(sk)) - return (1 << sk->sk_state) & TCPF_ESTABLISHED; + return (1 << READ_ONCE(sk->sk_state)) & TCPF_ESTABLISHED; if (sk_is_vsock(sk) && (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET)) return (1 << sk->sk_state) & TCPF_ESTABLISHED; @@ -1630,18 +1630,23 @@ void sock_map_unhash(struct sock *sk) void (*saved_unhash)(struct sock *sk); struct sk_psock *psock; +retry: rcu_read_lock(); psock = sk_psock(sk); if (unlikely(!psock)) { rcu_read_unlock(); saved_unhash = READ_ONCE(sk->sk_prot)->unhash; + if (unlikely(saved_unhash == sock_map_unhash)) + goto retry; } else { saved_unhash = psock->saved_unhash; sock_map_remove_links(sk, psock); rcu_read_unlock(); + + if (WARN_ON_ONCE(saved_unhash == sock_map_unhash)) + return; } - if (WARN_ON_ONCE(saved_unhash == sock_map_unhash)) - return; + if (saved_unhash) saved_unhash(sk); } @@ -1652,20 +1657,25 @@ void sock_map_destroy(struct sock *sk) void (*saved_destroy)(struct sock *sk); struct sk_psock *psock; +retry: rcu_read_lock(); psock = sk_psock_get(sk); if (unlikely(!psock)) { rcu_read_unlock(); saved_destroy = READ_ONCE(sk->sk_prot)->destroy; + if (unlikely(saved_destroy == sock_map_destroy)) + goto retry; } else { saved_destroy = psock->saved_destroy; sock_map_remove_links(sk, psock); rcu_read_unlock(); sk_psock_stop(psock); sk_psock_put(sk, psock); + + if (WARN_ON_ONCE(saved_destroy == sock_map_destroy)) + return; } - if (WARN_ON_ONCE(saved_destroy == sock_map_destroy)) - return; + if (saved_destroy) saved_destroy(sk); } @@ -1676,32 +1686,33 @@ void sock_map_close(struct sock *sk, long timeout) void (*saved_close)(struct sock *sk, long timeout); struct sk_psock *psock; +retry: lock_sock(sk); rcu_read_lock(); - psock = sk_psock(sk); + psock = sk_psock_get(sk); if (likely(psock)) { saved_close = psock->saved_close; sock_map_remove_links(sk, psock); - psock = sk_psock_get(sk); - if (unlikely(!psock)) - goto no_psock; rcu_read_unlock(); sk_psock_stop(psock); release_sock(sk); cancel_delayed_work_sync(&psock->work); sk_psock_put(sk, psock); + + /* Make sure we do not recurse. This is a bug. + * Leak the socket instead of crashing on a stack overflow. + */ + if (WARN_ON_ONCE(saved_close == sock_map_close)) + return; } else { saved_close = READ_ONCE(sk->sk_prot)->close; -no_psock: rcu_read_unlock(); release_sock(sk); + + if (unlikely(saved_close == sock_map_close)) + goto retry; } - /* Make sure we do not recurse. This is a bug. - * Leak the socket instead of crashing on a stack overflow. - */ - if (WARN_ON_ONCE(saved_close == sock_map_close)) - return; saved_close(sk, timeout); } EXPORT_SYMBOL_GPL(sock_map_close); diff --git a/net/dsa/conduit.c b/net/dsa/conduit.c index a1b044467bd6..8398d72d7e4d 100644 --- a/net/dsa/conduit.c +++ b/net/dsa/conduit.c @@ -27,9 +27,7 @@ static int dsa_conduit_get_regs_len(struct net_device *dev) int len; if (ops && ops->get_regs_len) { - netdev_lock_ops(dev); len = ops->get_regs_len(dev); - netdev_unlock_ops(dev); if (len < 0) return len; ret += len; @@ -60,15 +58,11 @@ static void dsa_conduit_get_regs(struct net_device *dev, int len; if (ops && ops->get_regs_len && ops->get_regs) { - netdev_lock_ops(dev); len = ops->get_regs_len(dev); - if (len < 0) { - netdev_unlock_ops(dev); + if (len < 0) return; - } regs->len = len; ops->get_regs(dev, regs, data); - netdev_unlock_ops(dev); data += regs->len; } @@ -115,10 +109,8 @@ static void dsa_conduit_get_ethtool_stats(struct net_device *dev, int count, mcount = 0; if (ops && ops->get_sset_count && ops->get_ethtool_stats) { - netdev_lock_ops(dev); mcount = ops->get_sset_count(dev, ETH_SS_STATS); ops->get_ethtool_stats(dev, stats, data); - netdev_unlock_ops(dev); } list_for_each_entry(dp, &dst->ports, list) { @@ -149,10 +141,8 @@ static void dsa_conduit_get_ethtool_phy_stats(struct net_device *dev, if (count >= 0) phy_ethtool_get_stats(dev->phydev, stats, data); } else if (ops && ops->get_sset_count && ops->get_ethtool_phy_stats) { - netdev_lock_ops(dev); count = ops->get_sset_count(dev, ETH_SS_PHY_STATS); ops->get_ethtool_phy_stats(dev, stats, data); - netdev_unlock_ops(dev); } if (count < 0) @@ -176,13 +166,11 @@ static int dsa_conduit_get_sset_count(struct net_device *dev, int sset) struct dsa_switch_tree *dst = cpu_dp->dst; int count = 0; - netdev_lock_ops(dev); if (sset == ETH_SS_PHY_STATS && dev->phydev && (!ops || !ops->get_ethtool_phy_stats)) count = phy_ethtool_get_sset_count(dev->phydev); else if (ops && ops->get_sset_count) count = ops->get_sset_count(dev, sset); - netdev_unlock_ops(dev); if (count < 0) count = 0; @@ -239,7 +227,6 @@ static void dsa_conduit_get_strings(struct net_device *dev, u32 stringset, struct dsa_switch_tree *dst = cpu_dp->dst; int count, mcount = 0; - netdev_lock_ops(dev); if (stringset == ETH_SS_PHY_STATS && dev->phydev && !ops->get_ethtool_phy_stats) { mcount = phy_ethtool_get_sset_count(dev->phydev); @@ -253,7 +240,6 @@ static void dsa_conduit_get_strings(struct net_device *dev, u32 stringset, mcount = 0; ops->get_strings(dev, stringset, data); } - netdev_unlock_ops(dev); list_for_each_entry(dp, &dst->ports, list) { if (!dsa_port_is_dsa(dp) && !dsa_port_is_cpu(dp)) diff --git a/net/ethtool/bitset.c b/net/ethtool/bitset.c index 8bb98d3ea3db..a3a2cc6480a0 100644 --- a/net/ethtool/bitset.c +++ b/net/ethtool/bitset.c @@ -92,7 +92,7 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, u32 mask; if (end <= start) - return true; + return false; if (start % 32) { mask = ethnl_upper_bits(start); @@ -105,11 +105,11 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, start_word++; } - if (!memchr_inv(map + start_word, '\0', - (end_word - start_word) * sizeof(u32))) + if (memchr_inv(map + start_word, '\0', + (end_word - start_word) * sizeof(u32))) return true; if (end % 32 == 0) - return true; + return false; return map[end_word] & ethnl_lower_bits(end); } diff --git a/net/ethtool/phy.c b/net/ethtool/phy.c index d4e6887055ab..ddc6eab701ed 100644 --- a/net/ethtool/phy.c +++ b/net/ethtool/phy.c @@ -76,6 +76,7 @@ static int phy_prepare_data(const struct ethnl_req_info *req_info, struct nlattr **tb = info->attrs; struct phy_device_node *pdn; struct phy_device *phydev; + int ret; /* RTNL is held by the caller */ phydev = ethnl_req_get_phydev(req_info, tb, ETHTOOL_A_PHY_HEADER, @@ -88,8 +89,19 @@ static int phy_prepare_data(const struct ethnl_req_info *req_info, return -EOPNOTSUPP; rep_data->phyindex = phydev->phyindex; + rep_data->name = kstrdup(dev_name(&phydev->mdio.dev), GFP_KERNEL); - rep_data->drvname = kstrdup(phydev->drv->name, GFP_KERNEL); + if (!rep_data->name) + return -ENOMEM; + + if (phydev->drv) { + rep_data->drvname = kstrdup(phydev->drv->name, GFP_KERNEL); + if (!rep_data->drvname) { + ret = -ENOMEM; + goto err_free_name; + } + } + rep_data->upstream_type = pdn->upstream_type; if (pdn->upstream_type == PHY_UPSTREAM_PHY) { @@ -97,15 +109,33 @@ static int phy_prepare_data(const struct ethnl_req_info *req_info, rep_data->upstream_index = upstream->phyindex; } - if (pdn->parent_sfp_bus) + if (pdn->parent_sfp_bus) { rep_data->upstream_sfp_name = kstrdup(sfp_get_name(pdn->parent_sfp_bus), GFP_KERNEL); + if (!rep_data->upstream_sfp_name) { + ret = -ENOMEM; + goto err_free_drvname; + } + } - if (phydev->sfp_bus) + if (phydev->sfp_bus) { rep_data->downstream_sfp_name = kstrdup(sfp_get_name(phydev->sfp_bus), GFP_KERNEL); + if (!rep_data->downstream_sfp_name) { + ret = -ENOMEM; + goto err_free_upstream_sfp; + } + } return 0; + +err_free_upstream_sfp: + kfree(rep_data->upstream_sfp_name); +err_free_drvname: + kfree(rep_data->drvname); +err_free_name: + kfree(rep_data->name); + return ret; } static int phy_fill_reply(struct sk_buff *skb, diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index d09875b33588..124619920d38 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -889,7 +889,10 @@ int hsr_get_node_data(struct hsr_priv *hsr, if (node->addr_B_port != HSR_PT_NONE) { port = hsr_port_get_hsr(hsr, node->addr_B_port); - *addr_b_ifindex = port->dev->ifindex; + if (port) + *addr_b_ifindex = port->dev->ifindex; + else + *addr_b_ifindex = -1; } else { *addr_b_ifindex = -1; } diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index 5fb812443a08..4366cbac3f06 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -124,9 +124,14 @@ static void ah_output_done(void *data, int err) struct iphdr *top_iph = ip_hdr(skb); struct ip_auth_hdr *ah = ip_auth_hdr(skb); int ihl = ip_hdrlen(skb); + int seqhi_len = 0; + __be32 *seqhi; + if (x->props.flags & XFRM_STATE_ESN) + seqhi_len = sizeof(*seqhi); iph = AH_SKB_CB(skb)->tmp; - icv = ah_tmp_icv(iph, ihl); + seqhi = (__be32 *)((char *)iph + ihl); + icv = ah_tmp_icv(seqhi, seqhi_len); memcpy(ah->auth_data, icv, ahp->icv_trunc_len); top_iph->tos = iph->tos; @@ -270,12 +275,17 @@ static void ah_input_done(void *data, int err) struct ip_auth_hdr *ah = ip_auth_hdr(skb); int ihl = ip_hdrlen(skb); int ah_hlen = (ah->hdrlen + 2) << 2; + int seqhi_len = 0; + __be32 *seqhi; if (err) goto out; + if (x->props.flags & XFRM_STATE_ESN) + seqhi_len = sizeof(*seqhi); work_iph = AH_SKB_CB(skb)->tmp; - auth_data = ah_tmp_auth(work_iph, ihl); + seqhi = (__be32 *)((char *)work_iph + ihl); + auth_data = ah_tmp_auth(seqhi, seqhi_len); icv = ah_tmp_icv(auth_data, ahp->icv_trunc_len); err = crypto_memneq(icv, auth_data, ahp->icv_trunc_len) ? -EBADMSG : 0; diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 51d70180e1cc..d409f606aec0 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -109,7 +109,6 @@ #include #include #include -#include #include #include diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c index 008edc7f6688..791e15063237 100644 --- a/net/ipv4/bpf_tcp_ca.c +++ b/net/ipv4/bpf_tcp_ca.c @@ -168,7 +168,7 @@ bpf_tcp_ca_get_func_proto(enum bpf_func_id func_id, */ if (prog_ops_moff(prog) != offsetof(struct tcp_congestion_ops, release)) - return &bpf_sk_setsockopt_proto; + return &bpf_sk_setsockopt_nodelay_proto; return NULL; case BPF_FUNC_getsockopt: /* Since get/setsockopt is usually expected to diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 6dfc0bcdef65..6a5febbdbee4 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -873,7 +873,8 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb) nfrags = 1; goto skip_cow; - } else if (!skb_has_frag_list(skb)) { + } else if (!skb_has_frag_list(skb) && + !skb_has_shared_frag(skb)) { nfrags = skb_shinfo(skb)->nr_frags; nfrags++; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 2f4fac22d1ab..7eeff658b467 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -371,7 +372,9 @@ static int icmp_glue_bits(void *from, char *to, int offset, int len, int odd, to, len); skb->csum = csum_block_add(skb->csum, csum, odd); - if (icmp_pointers[icmp_param->data.icmph.type].error) + if (icmp_param->data.icmph.type <= NR_ICMP_TYPES && + icmp_pointers[array_index_nospec(icmp_param->data.icmph.type, + NR_ICMP_TYPES + 1)].error) nf_ct_attach(skb, icmp_param->skb); return 0; } diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index a674fb44ec25..a9ad39064f3b 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -122,16 +122,29 @@ * contradict to specs provided this delay is small enough. */ -#define IGMP_V1_SEEN(in_dev) \ - (IPV4_DEVCONF_ALL_RO(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 1 || \ - IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 1 || \ - ((in_dev)->mr_v1_seen && \ - time_before(jiffies, (in_dev)->mr_v1_seen))) -#define IGMP_V2_SEEN(in_dev) \ - (IPV4_DEVCONF_ALL_RO(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 2 || \ - IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 2 || \ - ((in_dev)->mr_v2_seen && \ - time_before(jiffies, (in_dev)->mr_v2_seen))) +static bool IGMP_V1_SEEN(const struct in_device *in_dev) +{ + unsigned long seen; + + if (IPV4_DEVCONF_ALL_RO(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 1) + return true; + if (IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 1) + return true; + seen = READ_ONCE(in_dev->mr_v1_seen); + return seen && time_before(jiffies, seen); +} + +static bool IGMP_V2_SEEN(const struct in_device *in_dev) +{ + unsigned long seen; + + if (IPV4_DEVCONF_ALL_RO(dev_net(in_dev->dev), FORCE_IGMP_VERSION) == 2) + return true; + if (IN_DEV_CONF_GET((in_dev), FORCE_IGMP_VERSION) == 2) + return true; + seen = READ_ONCE(in_dev->mr_v2_seen); + return seen && time_before(jiffies, seen); +} static int unsolicited_report_interval(struct in_device *in_dev) { @@ -954,23 +967,21 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, int max_delay; int mark = 0; struct net *net = dev_net(in_dev->dev); - + unsigned long seen; if (len == 8) { + seen = jiffies + READ_ONCE(in_dev->mr_qrv) * READ_ONCE(in_dev->mr_qi) + + READ_ONCE(in_dev->mr_qri); if (ih->code == 0) { /* Alas, old v1 router presents here. */ max_delay = IGMP_QUERY_RESPONSE_INTERVAL; - in_dev->mr_v1_seen = jiffies + - (in_dev->mr_qrv * in_dev->mr_qi) + - in_dev->mr_qri; + WRITE_ONCE(in_dev->mr_v1_seen, seen); group = 0; } else { /* v2 router present */ max_delay = ih->code*(HZ/IGMP_TIMER_SCALE); - in_dev->mr_v2_seen = jiffies + - (in_dev->mr_qrv * in_dev->mr_qi) + - in_dev->mr_qri; + WRITE_ONCE(in_dev->mr_v2_seen, seen); } /* cancel the interface change timer */ WRITE_ONCE(in_dev->mr_ifc_count, 0); @@ -995,6 +1006,8 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, if (!max_delay) max_delay = 1; /* can't mod w/ 0 */ } else { /* v3 */ + unsigned long mr_qi; + if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) return true; @@ -1015,15 +1028,16 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, * received value was zero, use the default or statically * configured value. */ - in_dev->mr_qrv = ih3->qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); - in_dev->mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL; - + WRITE_ONCE(in_dev->mr_qrv, + ih3->qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv)); + mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL; + WRITE_ONCE(in_dev->mr_qi, mr_qi); /* RFC3376, 8.3. Query Response Interval: * The number of seconds represented by the [Query Response * Interval] must be less than the [Query Interval]. */ - if (in_dev->mr_qri >= in_dev->mr_qi) - in_dev->mr_qri = (in_dev->mr_qi/HZ - 1)*HZ; + if (READ_ONCE(in_dev->mr_qri) >= mr_qi) + WRITE_ONCE(in_dev->mr_qri, (mr_qi/HZ - 1) * HZ); if (!group) { /* general query */ if (ih3->nsrcs) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 4ac3ae1bc1af..dbcd37dfdc15 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1108,7 +1108,7 @@ static void reqsk_timer_handler(struct timer_list *t) if (!inet_ehash_insert(req_to_sk(nreq), req_to_sk(oreq), NULL)) { /* delete timer */ - __inet_csk_reqsk_queue_drop(sk_listener, nreq, true); + __inet_csk_reqsk_queue_drop(sk_listener, nreq, false); goto no_ownership; } @@ -1134,7 +1134,7 @@ no_ownership: } drop: - __inet_csk_reqsk_queue_drop(sk_listener, oreq, true); + __inet_csk_reqsk_queue_drop(oreq->rsk_listener, oreq, true); reqsk_put(oreq); } @@ -1479,16 +1479,19 @@ void inet_csk_listen_stop(struct sock *sk) if (nreq) { refcount_set(&nreq->rsk_refcnt, 1); + rcu_read_lock(); if (inet_csk_reqsk_queue_add(nsk, nreq, child)) { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQSUCCESS); reqsk_migrate_reset(req); + READ_ONCE(nsk->sk_data_ready)(nsk); } else { __NET_INC_STATS(sock_net(nsk), LINUX_MIB_TCPMIGRATEREQFAILURE); reqsk_migrate_reset(nreq); __reqsk_free(nreq); } + rcu_read_unlock(); /* inet_csk_reqsk_queue_add() has already * called inet_child_forget() on failure case. diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index d8083b9033c2..5b957a831e7c 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -179,7 +179,8 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base, seq = read_seqbegin(&base->lock); p = lookup(daddr, base, seq, NULL, &gc_cnt, &parent, &pp); - if (p) + /* Make sure tree was not modified during our lookup. */ + if (p && !read_seqretry(&base->lock, seq)) return p; /* retry an exact lookup, taking the lock before. diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index e4790cc7b5c2..5bcd73cbdb41 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1233,6 +1233,8 @@ alloc_new_skb: if (err < 0) goto error; copy = err; + if (!(flags & MSG_NO_SHARED_FRAGS)) + skb_shinfo(skb)->flags |= SKBFL_SHARED_FRAG; wmem_alloc_delta += copy; } else if (!zc) { int i = skb_shinfo(skb)->nr_frags; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 8a08d09b4c30..2628cd3a93a6 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -151,16 +151,6 @@ static struct mr_table *__ipmr_get_table(struct net *net, u32 id) return NULL; } -static struct mr_table *ipmr_get_table(struct net *net, u32 id) -{ - struct mr_table *mrt; - - rcu_read_lock(); - mrt = __ipmr_get_table(net, id); - rcu_read_unlock(); - return mrt; -} - static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, struct mr_table **mrt) { @@ -293,7 +283,7 @@ static void __net_exit ipmr_rules_exit_rtnl(struct net *net, struct mr_table *mrt, *next; list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) { - list_del(&mrt->list); + list_del_rcu(&mrt->list); ipmr_free_table(mrt, dev_kill_list); } } @@ -315,28 +305,30 @@ bool ipmr_rule_default(const struct fib_rule *rule) } EXPORT_SYMBOL(ipmr_rule_default); #else -#define ipmr_for_each_table(mrt, net) \ - for (mrt = net->ipv4.mrt; mrt; mrt = NULL) - static struct mr_table *ipmr_mr_table_iter(struct net *net, struct mr_table *mrt) { if (!mrt) - return net->ipv4.mrt; + return rcu_dereference(net->ipv4.mrt); return NULL; } -static struct mr_table *ipmr_get_table(struct net *net, u32 id) +static struct mr_table *__ipmr_get_table(struct net *net, u32 id) { - return net->ipv4.mrt; + return rcu_dereference_check(net->ipv4.mrt, + lockdep_rtnl_is_held() || + !rcu_access_pointer(net->ipv4.mrt)); } -#define __ipmr_get_table ipmr_get_table +#define ipmr_for_each_table(mrt, net) \ + for (mrt = __ipmr_get_table(net, 0); mrt; mrt = NULL) static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, struct mr_table **mrt) { - *mrt = net->ipv4.mrt; + *mrt = rcu_dereference(net->ipv4.mrt); + if (!*mrt) + return -EAGAIN; return 0; } @@ -347,7 +339,8 @@ static int __net_init ipmr_rules_init(struct net *net) mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); if (IS_ERR(mrt)) return PTR_ERR(mrt); - net->ipv4.mrt = mrt; + + rcu_assign_pointer(net->ipv4.mrt, mrt); return 0; } @@ -358,9 +351,10 @@ static void __net_exit ipmr_rules_exit(struct net *net) static void __net_exit ipmr_rules_exit_rtnl(struct net *net, struct list_head *dev_kill_list) { - ipmr_free_table(net->ipv4.mrt, dev_kill_list); + struct mr_table *mrt = rcu_dereference_protected(net->ipv4.mrt, 1); - net->ipv4.mrt = NULL; + RCU_INIT_POINTER(net->ipv4.mrt, NULL); + ipmr_free_table(mrt, dev_kill_list); } static int ipmr_rules_dump(struct net *net, struct notifier_block *nb, @@ -381,6 +375,17 @@ bool ipmr_rule_default(const struct fib_rule *rule) EXPORT_SYMBOL(ipmr_rule_default); #endif +static struct mr_table *ipmr_get_table(struct net *net, u32 id) +{ + struct mr_table *mrt; + + rcu_read_lock(); + mrt = __ipmr_get_table(net, id); + rcu_read_unlock(); + + return mrt; +} + static inline int ipmr_hash_cmp(struct rhashtable_compare_arg *arg, const void *ptr) { @@ -441,12 +446,11 @@ static void ipmr_free_table(struct mr_table *mrt, struct list_head *dev_kill_lis WARN_ON_ONCE(!mr_can_free_table(net)); - timer_shutdown_sync(&mrt->ipmr_expire_timer); mroute_clean_tables(mrt, MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC | MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC, &ipmr_dev_kill_list); - rhltable_destroy(&mrt->mfc_hash); - kfree(mrt); + timer_shutdown_sync(&mrt->ipmr_expire_timer); + mr_table_free(mrt); WARN_ON_ONCE(!net_initialized(net) && !list_empty(&ipmr_dev_kill_list)); list_splice(&ipmr_dev_kill_list, dev_kill_list); @@ -533,15 +537,16 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) }; int err; + rcu_read_lock(); err = ipmr_fib_lookup(net, &fl4, &mrt); if (err < 0) { + rcu_read_unlock(); kfree_skb(skb); return err; } DEV_STATS_ADD(dev, tx_bytes, skb->len); DEV_STATS_INC(dev, tx_packets); - rcu_read_lock(); /* Pairs with WRITE_ONCE() in vif_add() and vif_delete() */ ipmr_cache_report(mrt, skb, READ_ONCE(mrt->mroute_reg_vif_num), @@ -1108,11 +1113,12 @@ static int ipmr_cache_report(const struct mr_table *mrt, msg->im_vif_hi = vifi >> 8; ipv4_pktinfo_prepare(mroute_sk, pkt, false); memcpy(skb->cb, pkt->cb, sizeof(skb->cb)); - /* Add our header */ - igmp = skb_put(skb, sizeof(struct igmphdr)); + /* Add our header. + * Note that code, csum and group fields are cleared. + */ + igmp = skb_put_zero(skb, sizeof(struct igmphdr)); igmp->type = assert; msg->im_msgtype = assert; - igmp->code = 0; ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */ skb->transport_header = skb->network_header; } @@ -1135,12 +1141,19 @@ static int ipmr_cache_report(const struct mr_table *mrt, static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb, struct net_device *dev) { + struct net *net = read_pnet(&mrt->net); const struct iphdr *iph = ip_hdr(skb); - struct mfc_cache *c; + struct mfc_cache *c = NULL; bool found = false; int err; spin_lock_bh(&mfc_unres_lock); + + if (!check_net(net)) { + err = -EINVAL; + goto err; + } + list_for_each_entry(c, &mrt->mfc_unres_queue, _c.list) { if (c->mfc_mcastgrp == iph->daddr && c->mfc_origin == iph->saddr) { @@ -1153,10 +1166,8 @@ static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, /* Create a new entry if allowable */ c = ipmr_cache_alloc_unres(); if (!c) { - spin_unlock_bh(&mfc_unres_lock); - - kfree_skb(skb); - return -ENOBUFS; + err = -ENOBUFS; + goto err; } /* Fill in the new cache entry */ @@ -1166,17 +1177,8 @@ static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, /* Reflect first query at mrouted. */ err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE); - - if (err < 0) { - /* If the report failed throw the cache entry - out - Brad Parker - */ - spin_unlock_bh(&mfc_unres_lock); - - ipmr_cache_free(c); - kfree_skb(skb); - return err; - } + if (err < 0) + goto err; atomic_inc(&mrt->cache_resolve_queue_len); list_add(&c->_c.list, &mrt->mfc_unres_queue); @@ -1189,18 +1191,26 @@ static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, /* See if we can append the packet */ if (c->_c.mfc_un.unres.unresolved.qlen > 3) { - kfree_skb(skb); + c = NULL; err = -ENOBUFS; - } else { - if (dev) { - skb->dev = dev; - skb->skb_iif = dev->ifindex; - } - skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb); - err = 0; + goto err; } + if (dev) { + skb->dev = dev; + skb->skb_iif = dev->ifindex; + } + + skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb); + spin_unlock_bh(&mfc_unres_lock); + return 0; + +err: + spin_unlock_bh(&mfc_unres_lock); + if (c) + ipmr_cache_free(c); + kfree_skb(skb); return err; } @@ -1346,7 +1356,7 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags, } if (flags & MRT_FLUSH_MFC) { - if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { + if (atomic_read(&mrt->cache_resolve_queue_len) != 0 || !check_net(net)) { spin_lock_bh(&mfc_unres_lock); list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { list_del(&c->list); diff --git a/net/ipv4/ipmr_base.c b/net/ipv4/ipmr_base.c index 37a3c144276c..3930d612c3de 100644 --- a/net/ipv4/ipmr_base.c +++ b/net/ipv4/ipmr_base.c @@ -28,6 +28,20 @@ void vif_device_init(struct vif_device *v, v->link = dev->ifindex; } +static void __mr_free_table(struct work_struct *work) +{ + struct mr_table *mrt = container_of(to_rcu_work(work), + struct mr_table, work); + + rhltable_destroy(&mrt->mfc_hash); + kfree(mrt); +} + +void mr_table_free(struct mr_table *mrt) +{ + queue_rcu_work(system_unbound_wq, &mrt->work); +} + struct mr_table * mr_table_alloc(struct net *net, u32 id, struct mr_table_ops *ops, @@ -50,6 +64,8 @@ mr_table_alloc(struct net *net, u32 id, kfree(mrt); return ERR_PTR(err); } + + INIT_RCU_WORK(&mrt->work, __mr_free_table); INIT_LIST_HEAD(&mrt->mfc_cache_list); INIT_LIST_HEAD(&mrt->mfc_unres_queue); diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 1cdd9c28ab2d..ad2259678c78 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -110,13 +110,25 @@ static inline int arp_packet_match(const struct arphdr *arphdr, arpptr += dev->addr_len; memcpy(&src_ipaddr, arpptr, sizeof(u32)); arpptr += sizeof(u32); - tgt_devaddr = arpptr; - arpptr += dev->addr_len; + + if (IS_ENABLED(CONFIG_FIREWIRE_NET) && dev->type == ARPHRD_IEEE1394) { + if (unlikely(memchr_inv(arpinfo->tgt_devaddr.mask, 0, + sizeof(arpinfo->tgt_devaddr.mask)))) + return 0; + + tgt_devaddr = NULL; + } else { + tgt_devaddr = arpptr; + arpptr += dev->addr_len; + } memcpy(&tgt_ipaddr, arpptr, sizeof(u32)); if (NF_INVF(arpinfo, ARPT_INV_SRCDEVADDR, arp_devaddr_compare(&arpinfo->src_devaddr, src_devaddr, - dev->addr_len)) || + dev->addr_len))) + return 0; + + if (tgt_devaddr && NF_INVF(arpinfo, ARPT_INV_TGTDEVADDR, arp_devaddr_compare(&arpinfo->tgt_devaddr, tgt_devaddr, dev->addr_len))) @@ -1489,13 +1501,11 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len static void __arpt_unregister_table(struct net *net, struct xt_table *table) { - struct xt_table_info *private; - void *loc_cpu_entry; + struct xt_table_info *private = table->private; struct module *table_owner = table->me; + void *loc_cpu_entry; struct arpt_entry *iter; - private = xt_unregister_table(table); - /* Decrease module usage counts and free resources */ loc_cpu_entry = private->entries; xt_entry_foreach(iter, loc_cpu_entry, private->size) @@ -1503,6 +1513,7 @@ static void __arpt_unregister_table(struct net *net, struct xt_table *table) if (private->number > private->initial_entries) module_put(table_owner); xt_free_table_info(private); + kfree(table); } int arpt_register_table(struct net *net, @@ -1510,13 +1521,11 @@ int arpt_register_table(struct net *net, const struct arpt_replace *repl, const struct nf_hook_ops *template_ops) { - struct nf_hook_ops *ops; - unsigned int num_ops; - int ret, i; - struct xt_table_info *newinfo; struct xt_table_info bootstrap = {0}; - void *loc_cpu_entry; + struct xt_table_info *newinfo; struct xt_table *new_table; + void *loc_cpu_entry; + int ret; newinfo = xt_alloc_table_info(repl->size); if (!newinfo) @@ -1531,7 +1540,7 @@ int arpt_register_table(struct net *net, return ret; } - new_table = xt_register_table(net, table, &bootstrap, newinfo); + new_table = xt_register_table(net, table, template_ops, &bootstrap, newinfo); if (IS_ERR(new_table)) { struct arpt_entry *iter; @@ -1541,46 +1550,12 @@ int arpt_register_table(struct net *net, return PTR_ERR(new_table); } - num_ops = hweight32(table->valid_hooks); - if (num_ops == 0) { - ret = -EINVAL; - goto out_free; - } - - ops = kmemdup_array(template_ops, num_ops, sizeof(*ops), GFP_KERNEL); - if (!ops) { - ret = -ENOMEM; - goto out_free; - } - - for (i = 0; i < num_ops; i++) - ops[i].priv = new_table; - - new_table->ops = ops; - - ret = nf_register_net_hooks(net, ops, num_ops); - if (ret != 0) - goto out_free; - - return ret; - -out_free: - __arpt_unregister_table(net, new_table); return ret; } -void arpt_unregister_table_pre_exit(struct net *net, const char *name) -{ - struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); - - if (table) - nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); -} -EXPORT_SYMBOL(arpt_unregister_table_pre_exit); - void arpt_unregister_table(struct net *net, const char *name) { - struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); + struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_ARP, name); if (table) __arpt_unregister_table(net, table); diff --git a/net/ipv4/netfilter/arpt_mangle.c b/net/ipv4/netfilter/arpt_mangle.c index a4e07e5e9c11..f65dd339208e 100644 --- a/net/ipv4/netfilter/arpt_mangle.c +++ b/net/ipv4/netfilter/arpt_mangle.c @@ -40,6 +40,10 @@ target(struct sk_buff *skb, const struct xt_action_param *par) } arpptr += pln; if (mangle->flags & ARPT_MANGLE_TDEV) { + if (unlikely(IS_ENABLED(CONFIG_FIREWIRE_NET) && + skb->dev->type == ARPHRD_IEEE1394)) + return NF_DROP; + if (ARPT_DEV_ADDR_LEN_MAX < hln || (arpptr + hln > skb_tail_pointer(skb))) return NF_DROP; @@ -47,6 +51,10 @@ target(struct sk_buff *skb, const struct xt_action_param *par) } arpptr += hln; if (mangle->flags & ARPT_MANGLE_TIP) { + if (unlikely(IS_ENABLED(CONFIG_FIREWIRE_NET) && + skb->dev->type == ARPHRD_IEEE1394)) + return NF_DROP; + if (ARPT_MANGLE_ADDR_LEN_MAX < pln || (arpptr + pln > skb_tail_pointer(skb))) return NF_DROP; diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c index 78cd5ee24448..370b635e3523 100644 --- a/net/ipv4/netfilter/arptable_filter.c +++ b/net/ipv4/netfilter/arptable_filter.c @@ -43,7 +43,7 @@ static int arptable_filter_table_init(struct net *net) static void __net_exit arptable_filter_net_pre_exit(struct net *net) { - arpt_unregister_table_pre_exit(net, "filter"); + xt_unregister_table_pre_exit(net, NFPROTO_ARP, "filter"); } static void __net_exit arptable_filter_net_exit(struct net *net) @@ -58,32 +58,33 @@ static struct pernet_operations arptable_filter_net_ops = { static int __init arptable_filter_init(void) { - int ret = xt_register_template(&packet_filter, - arptable_filter_table_init); - - if (ret < 0) - return ret; + int ret; arpfilter_ops = xt_hook_ops_alloc(&packet_filter, arpt_do_table); - if (IS_ERR(arpfilter_ops)) { - xt_unregister_template(&packet_filter); + if (IS_ERR(arpfilter_ops)) return PTR_ERR(arpfilter_ops); - } ret = register_pernet_subsys(&arptable_filter_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&packet_filter, + arptable_filter_table_init); if (ret < 0) { - xt_unregister_template(&packet_filter); - kfree(arpfilter_ops); - return ret; + unregister_pernet_subsys(&arptable_filter_net_ops); + goto err_free; } + return 0; +err_free: + kfree(arpfilter_ops); return ret; } static void __exit arptable_filter_fini(void) { - unregister_pernet_subsys(&arptable_filter_net_ops); xt_unregister_template(&packet_filter); + unregister_pernet_subsys(&arptable_filter_net_ops); kfree(arpfilter_ops); } diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 23c8deff8095..5cbdb0815857 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -1704,12 +1704,10 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) static void __ipt_unregister_table(struct net *net, struct xt_table *table) { - struct xt_table_info *private; - void *loc_cpu_entry; + struct xt_table_info *private = table->private; struct module *table_owner = table->me; struct ipt_entry *iter; - - private = xt_unregister_table(table); + void *loc_cpu_entry; /* Decrease module usage counts and free resources */ loc_cpu_entry = private->entries; @@ -1718,19 +1716,18 @@ static void __ipt_unregister_table(struct net *net, struct xt_table *table) if (private->number > private->initial_entries) module_put(table_owner); xt_free_table_info(private); + kfree(table); } int ipt_register_table(struct net *net, const struct xt_table *table, const struct ipt_replace *repl, const struct nf_hook_ops *template_ops) { - struct nf_hook_ops *ops; - unsigned int num_ops; - int ret, i; - struct xt_table_info *newinfo; struct xt_table_info bootstrap = {0}; - void *loc_cpu_entry; + struct xt_table_info *newinfo; struct xt_table *new_table; + void *loc_cpu_entry; + int ret; newinfo = xt_alloc_table_info(repl->size); if (!newinfo) @@ -1745,7 +1742,7 @@ int ipt_register_table(struct net *net, const struct xt_table *table, return ret; } - new_table = xt_register_table(net, table, &bootstrap, newinfo); + new_table = xt_register_table(net, table, template_ops, &bootstrap, newinfo); if (IS_ERR(new_table)) { struct ipt_entry *iter; @@ -1755,51 +1752,12 @@ int ipt_register_table(struct net *net, const struct xt_table *table, return PTR_ERR(new_table); } - /* No template? No need to do anything. This is used by 'nat' table, it registers - * with the nat core instead of the netfilter core. - */ - if (!template_ops) - return 0; - - num_ops = hweight32(table->valid_hooks); - if (num_ops == 0) { - ret = -EINVAL; - goto out_free; - } - - ops = kmemdup_array(template_ops, num_ops, sizeof(*ops), GFP_KERNEL); - if (!ops) { - ret = -ENOMEM; - goto out_free; - } - - for (i = 0; i < num_ops; i++) - ops[i].priv = new_table; - - new_table->ops = ops; - - ret = nf_register_net_hooks(net, ops, num_ops); - if (ret != 0) - goto out_free; - return ret; - -out_free: - __ipt_unregister_table(net, new_table); - return ret; -} - -void ipt_unregister_table_pre_exit(struct net *net, const char *name) -{ - struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); - - if (table) - nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); } void ipt_unregister_table_exit(struct net *net, const char *name) { - struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); + struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV4, name); if (table) __ipt_unregister_table(net, table); @@ -1887,7 +1845,6 @@ static void __exit ip_tables_fini(void) } EXPORT_SYMBOL(ipt_register_table); -EXPORT_SYMBOL(ipt_unregister_table_pre_exit); EXPORT_SYMBOL(ipt_unregister_table_exit); EXPORT_SYMBOL(ipt_do_table); module_init(ip_tables_init); diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index 3ab908b74795..672d7da1071d 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -61,7 +61,7 @@ static int __net_init iptable_filter_net_init(struct net *net) static void __net_exit iptable_filter_net_pre_exit(struct net *net) { - ipt_unregister_table_pre_exit(net, "filter"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "filter"); } static void __net_exit iptable_filter_net_exit(struct net *net) @@ -77,32 +77,33 @@ static struct pernet_operations iptable_filter_net_ops = { static int __init iptable_filter_init(void) { - int ret = xt_register_template(&packet_filter, - iptable_filter_table_init); - - if (ret < 0) - return ret; + int ret; filter_ops = xt_hook_ops_alloc(&packet_filter, ipt_do_table); - if (IS_ERR(filter_ops)) { - xt_unregister_template(&packet_filter); + if (IS_ERR(filter_ops)) return PTR_ERR(filter_ops); - } ret = register_pernet_subsys(&iptable_filter_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&packet_filter, + iptable_filter_table_init); if (ret < 0) { - xt_unregister_template(&packet_filter); - kfree(filter_ops); - return ret; + unregister_pernet_subsys(&iptable_filter_net_ops); + goto err_free; } return 0; +err_free: + kfree(filter_ops); + return ret; } static void __exit iptable_filter_fini(void) { - unregister_pernet_subsys(&iptable_filter_net_ops); xt_unregister_template(&packet_filter); + unregister_pernet_subsys(&iptable_filter_net_ops); kfree(filter_ops); } diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 385d945d8ebe..13d25d9a4610 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -96,7 +96,7 @@ static int iptable_mangle_table_init(struct net *net) static void __net_exit iptable_mangle_net_pre_exit(struct net *net) { - ipt_unregister_table_pre_exit(net, "mangle"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "mangle"); } static void __net_exit iptable_mangle_net_exit(struct net *net) @@ -111,32 +111,33 @@ static struct pernet_operations iptable_mangle_net_ops = { static int __init iptable_mangle_init(void) { - int ret = xt_register_template(&packet_mangler, - iptable_mangle_table_init); - if (ret < 0) - return ret; + int ret; mangle_ops = xt_hook_ops_alloc(&packet_mangler, iptable_mangle_hook); - if (IS_ERR(mangle_ops)) { - xt_unregister_template(&packet_mangler); - ret = PTR_ERR(mangle_ops); - return ret; - } + if (IS_ERR(mangle_ops)) + return PTR_ERR(mangle_ops); ret = register_pernet_subsys(&iptable_mangle_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&packet_mangler, + iptable_mangle_table_init); if (ret < 0) { - xt_unregister_template(&packet_mangler); - kfree(mangle_ops); - return ret; + unregister_pernet_subsys(&iptable_mangle_net_ops); + goto err_free; } + return 0; +err_free: + kfree(mangle_ops); return ret; } static void __exit iptable_mangle_fini(void) { - unregister_pernet_subsys(&iptable_mangle_net_ops); xt_unregister_template(&packet_mangler); + unregister_pernet_subsys(&iptable_mangle_net_ops); kfree(mangle_ops); } diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index a5db7c67d61b..a0df72554025 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c @@ -79,7 +79,7 @@ static int ipt_nat_register_lookups(struct net *net) while (i) nf_nat_ipv4_unregister_fn(net, &ops[--i]); - kfree(ops); + kfree_rcu(ops, rcu); return ret; } } @@ -100,7 +100,7 @@ static void ipt_nat_unregister_lookups(struct net *net) for (i = 0; i < ARRAY_SIZE(nf_nat_ipv4_ops); i++) nf_nat_ipv4_unregister_fn(net, &ops[i]); - kfree(ops); + kfree_rcu(ops, rcu); } static int iptable_nat_table_init(struct net *net) @@ -119,8 +119,11 @@ static int iptable_nat_table_init(struct net *net) } ret = ipt_nat_register_lookups(net); - if (ret < 0) + if (ret < 0) { + xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); + synchronize_rcu(); ipt_unregister_table_exit(net, "nat"); + } kfree(repl); return ret; @@ -129,6 +132,7 @@ static int iptable_nat_table_init(struct net *net) static void __net_exit iptable_nat_net_pre_exit(struct net *net) { ipt_nat_unregister_lookups(net); + xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); } static void __net_exit iptable_nat_net_exit(struct net *net) diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c index 0e7f53964d0a..2745c22f4034 100644 --- a/net/ipv4/netfilter/iptable_raw.c +++ b/net/ipv4/netfilter/iptable_raw.c @@ -53,7 +53,7 @@ static int iptable_raw_table_init(struct net *net) static void __net_exit iptable_raw_net_pre_exit(struct net *net) { - ipt_unregister_table_pre_exit(net, "raw"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "raw"); } static void __net_exit iptable_raw_net_exit(struct net *net) @@ -77,32 +77,32 @@ static int __init iptable_raw_init(void) pr_info("Enabling raw table before defrag\n"); } - ret = xt_register_template(table, - iptable_raw_table_init); - if (ret < 0) - return ret; - rawtable_ops = xt_hook_ops_alloc(table, ipt_do_table); - if (IS_ERR(rawtable_ops)) { - xt_unregister_template(table); + if (IS_ERR(rawtable_ops)) return PTR_ERR(rawtable_ops); - } ret = register_pernet_subsys(&iptable_raw_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(table, + iptable_raw_table_init); if (ret < 0) { - xt_unregister_template(table); - kfree(rawtable_ops); - return ret; + unregister_pernet_subsys(&iptable_raw_net_ops); + goto err_free; } + return 0; +err_free: + kfree(rawtable_ops); return ret; } static void __exit iptable_raw_fini(void) { + xt_unregister_template(&packet_raw); unregister_pernet_subsys(&iptable_raw_net_ops); kfree(rawtable_ops); - xt_unregister_template(&packet_raw); } module_init(iptable_raw_init); diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c index d885443cb267..491894511c54 100644 --- a/net/ipv4/netfilter/iptable_security.c +++ b/net/ipv4/netfilter/iptable_security.c @@ -50,7 +50,7 @@ static int iptable_security_table_init(struct net *net) static void __net_exit iptable_security_net_pre_exit(struct net *net) { - ipt_unregister_table_pre_exit(net, "security"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "security"); } static void __net_exit iptable_security_net_exit(struct net *net) @@ -65,33 +65,34 @@ static struct pernet_operations iptable_security_net_ops = { static int __init iptable_security_init(void) { - int ret = xt_register_template(&security_table, - iptable_security_table_init); - - if (ret < 0) - return ret; + int ret; sectbl_ops = xt_hook_ops_alloc(&security_table, ipt_do_table); - if (IS_ERR(sectbl_ops)) { - xt_unregister_template(&security_table); + if (IS_ERR(sectbl_ops)) return PTR_ERR(sectbl_ops); - } ret = register_pernet_subsys(&iptable_security_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&security_table, + iptable_security_table_init); if (ret < 0) { - xt_unregister_template(&security_table); - kfree(sectbl_ops); - return ret; + unregister_pernet_subsys(&iptable_security_net_ops); + goto err_free; } + return 0; +err_free: + kfree(sectbl_ops); return ret; } static void __exit iptable_security_fini(void) { + xt_unregister_template(&security_table); unregister_pernet_subsys(&iptable_security_net_ops); kfree(sectbl_ops); - xt_unregister_template(&security_table); } module_init(iptable_security_init); diff --git a/net/ipv4/netfilter/nf_socket_ipv4.c b/net/ipv4/netfilter/nf_socket_ipv4.c index 5080fa5fbf6a..f9c6755f5ec5 100644 --- a/net/ipv4/netfilter/nf_socket_ipv4.c +++ b/net/ipv4/netfilter/nf_socket_ipv4.c @@ -94,6 +94,9 @@ struct sock *nf_sk_lookup_slow_v4(struct net *net, const struct sk_buff *skb, #endif int doff = 0; + if (ntohs(iph->frag_off) & IP_OFFSET) + return NULL; + if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) { struct tcphdr _hdr; struct udphdr *hp; diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index 904a060a7330..f92fcc39fc4c 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -2469,10 +2469,10 @@ static int replace_nexthop_single(struct net *net, struct nexthop *old, goto err_notify; } - /* When replacing an IPv4 nexthop with an IPv6 nexthop, potentially + /* When replacing a nexthop with one of a different family, potentially * update IPv4 indication in all the groups using the nexthop. */ - if (oldi->family == AF_INET && newi->family == AF_INET6) { + if (oldi->family != newi->family) { list_for_each_entry(nhge, &old->grp_list, nh_list) { struct nexthop *nhp = nhge->nh_parent; struct nh_group *nhg; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 2014a6408e93..432fa28e47d4 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3424,7 +3424,7 @@ int tcp_disconnect(struct sock *sk, int flags) icsk->icsk_rto = TCP_TIMEOUT_INIT; WRITE_ONCE(icsk->icsk_rto_min, TCP_RTO_MIN); WRITE_ONCE(icsk->icsk_delack_max, TCP_DELACK_MAX); - tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; + WRITE_ONCE(tp->snd_ssthresh, TCP_INFINITE_SSTHRESH); tcp_snd_cwnd_set(tp, TCP_INIT_CWND); tp->snd_cwnd_cnt = 0; tp->is_cwnd_limited = 0; @@ -3622,7 +3622,8 @@ static void tcp_enable_tx_delay(struct sock *sk, int val) if (delta && sk->sk_state == TCP_ESTABLISHED) { s64 srtt = (s64)tp->srtt_us + delta; - tp->srtt_us = clamp_t(s64, srtt, 1, ~0U); + WRITE_ONCE(tp->srtt_us, + clamp_t(s64, srtt, 1, ~0U)); /* Note: does not deal with non zero icsk_backoff */ tcp_set_rto(sk); @@ -4190,12 +4191,18 @@ static void tcp_get_info_chrono_stats(const struct tcp_sock *tp, struct tcp_info *info) { u64 stats[__TCP_CHRONO_MAX], total = 0; - enum tcp_chrono i; + enum tcp_chrono i, cur; + /* Following READ_ONCE()s pair with WRITE_ONCE()s in tcp_chrono_set(). + * This is because socket lock might not be owned by us at this point. + * This is best effort, tcp_get_timestamping_opt_stats() can + * see wrong values. A real fix would be too costly for TCP fast path. + */ + cur = READ_ONCE(tp->chrono_type); for (i = TCP_CHRONO_BUSY; i < __TCP_CHRONO_MAX; ++i) { - stats[i] = tp->chrono_stat[i - 1]; - if (i == tp->chrono_type) - stats[i] += tcp_jiffies32 - tp->chrono_start; + stats[i] = READ_ONCE(tp->chrono_stat[i - 1]); + if (i == cur) + stats[i] += tcp_jiffies32 - READ_ONCE(tp->chrono_start); stats[i] *= USEC_PER_SEC / HZ; total += stats[i]; } @@ -4427,9 +4434,9 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk, nla_put_u64_64bit(stats, TCP_NLA_SNDBUF_LIMITED, info.tcpi_sndbuf_limited, TCP_NLA_PAD); nla_put_u64_64bit(stats, TCP_NLA_DATA_SEGS_OUT, - tp->data_segs_out, TCP_NLA_PAD); + READ_ONCE(tp->data_segs_out), TCP_NLA_PAD); nla_put_u64_64bit(stats, TCP_NLA_TOTAL_RETRANS, - tp->total_retrans, TCP_NLA_PAD); + READ_ONCE(tp->total_retrans), TCP_NLA_PAD); rate = READ_ONCE(sk->sk_pacing_rate); rate64 = (rate != ~0UL) ? rate : ~0ULL; @@ -4438,37 +4445,42 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk, rate64 = tcp_compute_delivery_rate(tp); nla_put_u64_64bit(stats, TCP_NLA_DELIVERY_RATE, rate64, TCP_NLA_PAD); - nla_put_u32(stats, TCP_NLA_SND_CWND, tcp_snd_cwnd(tp)); - nla_put_u32(stats, TCP_NLA_REORDERING, tp->reordering); - nla_put_u32(stats, TCP_NLA_MIN_RTT, tcp_min_rtt(tp)); + nla_put_u32(stats, TCP_NLA_SND_CWND, READ_ONCE(tp->snd_cwnd)); + nla_put_u32(stats, TCP_NLA_REORDERING, READ_ONCE(tp->reordering)); + nla_put_u32(stats, TCP_NLA_MIN_RTT, data_race(tcp_min_rtt(tp))); nla_put_u8(stats, TCP_NLA_RECUR_RETRANS, READ_ONCE(inet_csk(sk)->icsk_retransmits)); - nla_put_u8(stats, TCP_NLA_DELIVERY_RATE_APP_LMT, !!tp->rate_app_limited); - nla_put_u32(stats, TCP_NLA_SND_SSTHRESH, tp->snd_ssthresh); - nla_put_u32(stats, TCP_NLA_DELIVERED, tp->delivered); - nla_put_u32(stats, TCP_NLA_DELIVERED_CE, tp->delivered_ce); + nla_put_u8(stats, TCP_NLA_DELIVERY_RATE_APP_LMT, data_race(!!tp->rate_app_limited)); + nla_put_u32(stats, TCP_NLA_SND_SSTHRESH, READ_ONCE(tp->snd_ssthresh)); + nla_put_u32(stats, TCP_NLA_DELIVERED, READ_ONCE(tp->delivered)); + nla_put_u32(stats, TCP_NLA_DELIVERED_CE, READ_ONCE(tp->delivered_ce)); - nla_put_u32(stats, TCP_NLA_SNDQ_SIZE, tp->write_seq - tp->snd_una); + nla_put_u32(stats, TCP_NLA_SNDQ_SIZE, + max_t(int, 0, + READ_ONCE(tp->write_seq) - READ_ONCE(tp->snd_una))); nla_put_u8(stats, TCP_NLA_CA_STATE, inet_csk(sk)->icsk_ca_state); - nla_put_u64_64bit(stats, TCP_NLA_BYTES_SENT, tp->bytes_sent, + nla_put_u64_64bit(stats, TCP_NLA_BYTES_SENT, READ_ONCE(tp->bytes_sent), TCP_NLA_PAD); - nla_put_u64_64bit(stats, TCP_NLA_BYTES_RETRANS, tp->bytes_retrans, - TCP_NLA_PAD); - nla_put_u32(stats, TCP_NLA_DSACK_DUPS, tp->dsack_dups); - nla_put_u32(stats, TCP_NLA_REORD_SEEN, tp->reord_seen); - nla_put_u32(stats, TCP_NLA_SRTT, tp->srtt_us >> 3); - nla_put_u16(stats, TCP_NLA_TIMEOUT_REHASH, tp->timeout_rehash); + nla_put_u64_64bit(stats, TCP_NLA_BYTES_RETRANS, + READ_ONCE(tp->bytes_retrans), TCP_NLA_PAD); + nla_put_u32(stats, TCP_NLA_DSACK_DUPS, READ_ONCE(tp->dsack_dups)); + nla_put_u32(stats, TCP_NLA_REORD_SEEN, READ_ONCE(tp->reord_seen)); + nla_put_u32(stats, TCP_NLA_SRTT, READ_ONCE(tp->srtt_us) >> 3); + nla_put_u16(stats, TCP_NLA_TIMEOUT_REHASH, + READ_ONCE(tp->timeout_rehash)); nla_put_u32(stats, TCP_NLA_BYTES_NOTSENT, - max_t(int, 0, tp->write_seq - tp->snd_nxt)); + max_t(int, 0, + READ_ONCE(tp->write_seq) - READ_ONCE(tp->snd_nxt))); nla_put_u64_64bit(stats, TCP_NLA_EDT, orig_skb->skb_mstamp_ns, TCP_NLA_PAD); if (ack_skb) nla_put_u8(stats, TCP_NLA_TTL, tcp_skb_ttl_or_hop_limit(ack_skb)); - nla_put_u32(stats, TCP_NLA_REHASH, tp->plb_rehash + tp->timeout_rehash); + nla_put_u32(stats, TCP_NLA_REHASH, + READ_ONCE(tp->plb_rehash) + READ_ONCE(tp->timeout_rehash)); return stats; } diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index a97cdf3e6af4..0a4b38b315fe 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -116,7 +116,8 @@ struct tcp_ao_key *tcp_ao_established_key(const struct sock *sk, { struct tcp_ao_key *key; - hlist_for_each_entry_rcu(key, &ao->head, node, lockdep_sock_is_held(sk)) { + hlist_for_each_entry_rcu(key, &ao->head, node, + sk_fullsock(sk) && lockdep_sock_is_held(sk)) { if ((sndid >= 0 && key->sndid != sndid) || (rcvid >= 0 && key->rcvid != rcvid)) continue; diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c index 1ddc20a399b0..aec7805b1d37 100644 --- a/net/ipv4/tcp_bbr.c +++ b/net/ipv4/tcp_bbr.c @@ -897,8 +897,8 @@ static void bbr_check_drain(struct sock *sk, const struct rate_sample *rs) if (bbr->mode == BBR_STARTUP && bbr_full_bw_reached(sk)) { bbr->mode = BBR_DRAIN; /* drain queue we created */ - tcp_sk(sk)->snd_ssthresh = - bbr_inflight(sk, bbr_max_bw(sk), BBR_UNIT); + WRITE_ONCE(tcp_sk(sk)->snd_ssthresh, + bbr_inflight(sk, bbr_max_bw(sk), BBR_UNIT)); } /* fall through to check if in-flight is already small: */ if (bbr->mode == BBR_DRAIN && bbr_packets_in_net_at_edt(sk, tcp_packets_in_flight(tcp_sk(sk))) <= @@ -1043,7 +1043,7 @@ __bpf_kfunc static void bbr_init(struct sock *sk) struct bbr *bbr = inet_csk_ca(sk); bbr->prior_cwnd = 0; - tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; + WRITE_ONCE(tp->snd_ssthresh, TCP_INFINITE_SSTHRESH); bbr->rtt_cnt = 0; bbr->next_rtt_delivered = tp->delivered; bbr->prev_ca_state = TCP_CA_Open; diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c index 58358bf92e1b..65444ff14241 100644 --- a/net/ipv4/tcp_bic.c +++ b/net/ipv4/tcp_bic.c @@ -74,7 +74,7 @@ static void bictcp_init(struct sock *sk) bictcp_reset(ca); if (initial_ssthresh) - tcp_sk(sk)->snd_ssthresh = initial_ssthresh; + WRITE_ONCE(tcp_sk(sk)->snd_ssthresh, initial_ssthresh); } /* diff --git a/net/ipv4/tcp_cdg.c b/net/ipv4/tcp_cdg.c index ceabfd690a29..0812c390aee5 100644 --- a/net/ipv4/tcp_cdg.c +++ b/net/ipv4/tcp_cdg.c @@ -162,7 +162,7 @@ static void tcp_cdg_hystart_update(struct sock *sk) NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTTRAINCWND, tcp_snd_cwnd(tp)); - tp->snd_ssthresh = tcp_snd_cwnd(tp); + WRITE_ONCE(tp->snd_ssthresh, tcp_snd_cwnd(tp)); return; } } @@ -181,7 +181,7 @@ static void tcp_cdg_hystart_update(struct sock *sk) NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTDELAYCWND, tcp_snd_cwnd(tp)); - tp->snd_ssthresh = tcp_snd_cwnd(tp); + WRITE_ONCE(tp->snd_ssthresh, tcp_snd_cwnd(tp)); } } } diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index ab78b5ae8d0e..119bf8cbb007 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -136,7 +136,7 @@ __bpf_kfunc static void cubictcp_init(struct sock *sk) bictcp_hystart_reset(sk); if (!hystart && initial_ssthresh) - tcp_sk(sk)->snd_ssthresh = initial_ssthresh; + WRITE_ONCE(tcp_sk(sk)->snd_ssthresh, initial_ssthresh); } __bpf_kfunc static void cubictcp_cwnd_event_tx_start(struct sock *sk) @@ -420,7 +420,7 @@ static void hystart_update(struct sock *sk, u32 delay) NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTTRAINCWND, tcp_snd_cwnd(tp)); - tp->snd_ssthresh = tcp_snd_cwnd(tp); + WRITE_ONCE(tp->snd_ssthresh, tcp_snd_cwnd(tp)); } } } @@ -440,7 +440,7 @@ static void hystart_update(struct sock *sk, u32 delay) NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTDELAYCWND, tcp_snd_cwnd(tp)); - tp->snd_ssthresh = tcp_snd_cwnd(tp); + WRITE_ONCE(tp->snd_ssthresh, tcp_snd_cwnd(tp)); } } } diff --git a/net/ipv4/tcp_dctcp.c b/net/ipv4/tcp_dctcp.c index 96c99999e09d..274e628e7cf8 100644 --- a/net/ipv4/tcp_dctcp.c +++ b/net/ipv4/tcp_dctcp.c @@ -177,7 +177,7 @@ static void dctcp_react_to_loss(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); ca->loss_cwnd = tcp_snd_cwnd(tp); - tp->snd_ssthresh = max(tcp_snd_cwnd(tp) >> 1U, 2U); + WRITE_ONCE(tp->snd_ssthresh, max(tcp_snd_cwnd(tp) >> 1U, 2U)); } __bpf_kfunc static void dctcp_state(struct sock *sk, u8 new_state) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 021f745747c5..d5c9e65d9760 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -476,14 +476,14 @@ static bool tcp_accecn_process_option(struct tcp_sock *tp, static void tcp_count_delivered_ce(struct tcp_sock *tp, u32 ecn_count) { - tp->delivered_ce += ecn_count; + WRITE_ONCE(tp->delivered_ce, tp->delivered_ce + ecn_count); } /* Updates the delivered and delivered_ce counts */ static void tcp_count_delivered(struct tcp_sock *tp, u32 delivered, bool ece_ack) { - tp->delivered += delivered; + WRITE_ONCE(tp->delivered, tp->delivered + delivered); if (tcp_ecn_mode_rfc3168(tp) && ece_ack) tcp_count_delivered_ce(tp, delivered); } @@ -1132,7 +1132,7 @@ static void tcp_rtt_estimator(struct sock *sk, long mrtt_us) tcp_bpf_rtt(sk, mrtt_us, srtt); } - tp->srtt_us = max(1U, srtt); + WRITE_ONCE(tp->srtt_us, max(1U, srtt)); } void tcp_update_pacing_rate(struct sock *sk) @@ -1246,7 +1246,7 @@ static u32 tcp_dsack_seen(struct tcp_sock *tp, u32 start_seq, else if (tp->tlp_high_seq && tp->tlp_high_seq == end_seq) state->flag |= FLAG_DSACK_TLP; - tp->dsack_dups += dup_segs; + WRITE_ONCE(tp->dsack_dups, tp->dsack_dups + dup_segs); /* Skip the DSACK if dup segs weren't retransmitted by sender */ if (tp->dsack_dups > tp->total_retrans) return 0; @@ -1293,12 +1293,13 @@ static void tcp_check_sack_reordering(struct sock *sk, const u32 low_seq, tp->sacked_out, tp->undo_marker ? tp->undo_retrans : 0); #endif - tp->reordering = min_t(u32, (metric + mss - 1) / mss, - READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_max_reordering)); + WRITE_ONCE(tp->reordering, + min_t(u32, (metric + mss - 1) / mss, + READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_max_reordering))); } /* This exciting event is worth to be remembered. 8) */ - tp->reord_seen++; + WRITE_ONCE(tp->reord_seen, tp->reord_seen + 1); NET_INC_STATS(sock_net(sk), ts ? LINUX_MIB_TCPTSREORDER : LINUX_MIB_TCPSACKREORDER); } @@ -2439,9 +2440,10 @@ static void tcp_check_reno_reordering(struct sock *sk, const int addend) if (!tcp_limit_reno_sacked(tp)) return; - tp->reordering = min_t(u32, tp->packets_out + addend, - READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_max_reordering)); - tp->reord_seen++; + WRITE_ONCE(tp->reordering, + min_t(u32, tp->packets_out + addend, + READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_max_reordering))); + WRITE_ONCE(tp->reord_seen, tp->reord_seen + 1); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPRENOREORDER); } @@ -2565,7 +2567,7 @@ void tcp_enter_loss(struct sock *sk) (icsk->icsk_ca_state == TCP_CA_Loss && !icsk->icsk_retransmits)) { tp->prior_ssthresh = tcp_current_ssthresh(sk); tp->prior_cwnd = tcp_snd_cwnd(tp); - tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk); + WRITE_ONCE(tp->snd_ssthresh, icsk->icsk_ca_ops->ssthresh(sk)); tcp_ca_event(sk, CA_EVENT_LOSS); tcp_init_undo(tp); } @@ -2579,8 +2581,8 @@ void tcp_enter_loss(struct sock *sk) reordering = READ_ONCE(net->ipv4.sysctl_tcp_reordering); if (icsk->icsk_ca_state <= TCP_CA_Disorder && tp->sacked_out >= reordering) - tp->reordering = min_t(unsigned int, tp->reordering, - reordering); + WRITE_ONCE(tp->reordering, + min_t(unsigned int, tp->reordering, reordering)); tcp_set_ca_state(sk, TCP_CA_Loss); tp->high_seq = tp->snd_nxt; @@ -2858,7 +2860,7 @@ static void tcp_undo_cwnd_reduction(struct sock *sk, bool unmark_loss) tcp_snd_cwnd_set(tp, icsk->icsk_ca_ops->undo_cwnd(sk)); if (tp->prior_ssthresh > tp->snd_ssthresh) { - tp->snd_ssthresh = tp->prior_ssthresh; + WRITE_ONCE(tp->snd_ssthresh, tp->prior_ssthresh); tcp_ecn_withdraw_cwr(tp); } } @@ -2976,7 +2978,7 @@ static void tcp_init_cwnd_reduction(struct sock *sk) tp->prior_cwnd = tcp_snd_cwnd(tp); tp->prr_delivered = 0; tp->prr_out = 0; - tp->snd_ssthresh = inet_csk(sk)->icsk_ca_ops->ssthresh(sk); + WRITE_ONCE(tp->snd_ssthresh, inet_csk(sk)->icsk_ca_ops->ssthresh(sk)); tcp_ecn_queue_cwr(tp); } @@ -3118,7 +3120,7 @@ static void tcp_non_congestion_loss_retransmit(struct sock *sk) if (icsk->icsk_ca_state != TCP_CA_Loss) { tp->high_seq = tp->snd_nxt; - tp->snd_ssthresh = tcp_current_ssthresh(sk); + WRITE_ONCE(tp->snd_ssthresh, tcp_current_ssthresh(sk)); tp->prior_ssthresh = 0; tp->undo_marker = 0; tcp_set_ca_state(sk, TCP_CA_Loss); @@ -3910,7 +3912,7 @@ static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack) sock_owned_by_me((struct sock *)tp); tp->bytes_acked += delta; tcp_snd_sne_update(tp, ack); - tp->snd_una = ack; + WRITE_ONCE(tp->snd_una, ack); } static void tcp_rcv_sne_update(struct tcp_sock *tp, u32 seq) @@ -4284,11 +4286,15 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) goto old_ack; } - /* If the ack includes data we haven't sent yet, discard - * this segment (RFC793 Section 3.9). + /* If the ack includes data we haven't sent yet, drop the + * segment. RFC 793 Section 3.9 and RFC 5961 Section 5.2 + * require us to send an ACK back in that case. */ - if (after(ack, tp->snd_nxt)) + if (after(ack, tp->snd_nxt)) { + if (!(flag & FLAG_NO_CHALLENGE_ACK)) + tcp_send_challenge_ack(sk, false); return -SKB_DROP_REASON_TCP_ACK_UNSENT_DATA; + } if (after(ack, prior_snd_una)) { flag |= FLAG_SND_UNA_ADVANCED; @@ -6777,7 +6783,7 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPFASTOPENACTIVE); /* SYN-data is counted as two separate packets in tcp_ack() */ if (tp->delivered > 1) - --tp->delivered; + WRITE_ONCE(tp->delivered, tp->delivered - 1); } tcp_fastopen_add_skb(sk, synack); @@ -7210,7 +7216,7 @@ tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) SKB_DR_SET(reason, NOT_SPECIFIED); switch (sk->sk_state) { case TCP_SYN_RECV: - tp->delivered++; /* SYN-ACK delivery isn't tracked in tcp_ack */ + WRITE_ONCE(tp->delivered, tp->delivered + 1); /* SYN-ACK delivery isn't tracked in tcp_ack */ if (!tp->srtt_us) tcp_synack_rtt_meas(sk, req); @@ -7238,7 +7244,7 @@ tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) if (sk->sk_socket) sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT); - tp->snd_una = TCP_SKB_CB(skb)->ack_seq; + WRITE_ONCE(tp->snd_una, TCP_SKB_CB(skb)->ack_seq); tp->snd_wnd = ntohs(th->window) << tp->rx_opt.snd_wscale; tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 8fc24c3743c5..c0526cc03980 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1827,7 +1827,6 @@ INDIRECT_CALLABLE_DECLARE(struct dst_entry *ipv4_dst_check(struct dst_entry *, int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) { enum skb_drop_reason reason; - struct sock *rsk; reason = psp_sk_rx_policy_check(sk, skb); if (reason) @@ -1863,24 +1862,21 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) return 0; if (nsk != sk) { reason = tcp_child_process(sk, nsk, skb); - if (reason) { - rsk = nsk; + sock_put(nsk); + if (reason) goto reset; - } return 0; } } else sock_rps_save_rxhash(sk, skb); reason = tcp_rcv_state_process(sk, skb); - if (reason) { - rsk = sk; + if (reason) goto reset; - } return 0; reset: - tcp_v4_send_reset(rsk, skb, sk_rst_convert_drop_reason(reason)); + tcp_v4_send_reset(sk, skb, sk_rst_convert_drop_reason(reason)); discard: sk_skb_reason_drop(sk, skb, reason); /* Be careful here. If this function gets more complicated and @@ -2193,8 +2189,10 @@ lookup: rst_reason = sk_rst_convert_drop_reason(drop_reason); tcp_v4_send_reset(nsk, skb, rst_reason); + sock_put(nsk); goto discard_and_relse; } + sock_put(nsk); sock_put(sk); return 0; } diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 06b1d5d3b6df..dc0c081fc1f3 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -490,13 +490,13 @@ void tcp_init_metrics(struct sock *sk) val = READ_ONCE(net->ipv4.sysctl_tcp_no_ssthresh_metrics_save) ? 0 : tcp_metric_get(tm, TCP_METRIC_SSTHRESH); if (val) { - tp->snd_ssthresh = val; + WRITE_ONCE(tp->snd_ssthresh, val); if (tp->snd_ssthresh > tp->snd_cwnd_clamp) - tp->snd_ssthresh = tp->snd_cwnd_clamp; + WRITE_ONCE(tp->snd_ssthresh, tp->snd_cwnd_clamp); } val = tcp_metric_get(tm, TCP_METRIC_REORDERING); if (val && tp->reordering != val) - tp->reordering = val; + WRITE_ONCE(tp->reordering, val); crtt = tcp_metric_get(tm, TCP_METRIC_RTT); rcu_read_unlock(); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 199f0b579e89..e6092c3ac840 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -1012,6 +1012,6 @@ enum skb_drop_reason tcp_child_process(struct sock *parent, struct sock *child, } bh_unlock_sock(child); - sock_put(child); + return reason; } diff --git a/net/ipv4/tcp_nv.c b/net/ipv4/tcp_nv.c index a60662f4bdf9..f345897a68df 100644 --- a/net/ipv4/tcp_nv.c +++ b/net/ipv4/tcp_nv.c @@ -396,8 +396,8 @@ static void tcpnv_acked(struct sock *sk, const struct ack_sample *sample) /* We have enough data to determine we are congested */ ca->nv_allow_cwnd_growth = 0; - tp->snd_ssthresh = - (nv_ssthresh_factor * max_win) >> 3; + WRITE_ONCE(tp->snd_ssthresh, + (nv_ssthresh_factor * max_win) >> 3); if (tcp_snd_cwnd(tp) - max_win > 2) { /* gap > 2, we do exponential cwnd decrease */ int dec; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 8e99687526a6..f9d8755705f7 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -171,7 +171,7 @@ void tcp_cwnd_restart(struct sock *sk, s32 delta) tcp_ca_event(sk, CA_EVENT_CWND_RESTART); - tp->snd_ssthresh = tcp_current_ssthresh(sk); + WRITE_ONCE(tp->snd_ssthresh, tcp_current_ssthresh(sk)); restart_cwnd = min(restart_cwnd, cwnd); while ((delta -= inet_csk(sk)->icsk_rto) > 0 && cwnd > restart_cwnd) @@ -1688,8 +1688,10 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, if (skb->len != tcp_header_size) { tcp_event_data_sent(tp, sk); - tp->data_segs_out += tcp_skb_pcount(skb); - tp->bytes_sent += skb->len - tcp_header_size; + WRITE_ONCE(tp->data_segs_out, + tp->data_segs_out + tcp_skb_pcount(skb)); + WRITE_ONCE(tp->bytes_sent, + tp->bytes_sent + skb->len - tcp_header_size); } if (after(tcb->end_seq, tp->snd_nxt) || tcb->seq == tcb->end_seq) @@ -2142,7 +2144,7 @@ static void tcp_cwnd_application_limited(struct sock *sk) u32 init_win = tcp_init_cwnd(tp, __sk_dst_get(sk)); u32 win_used = max(tp->snd_cwnd_used, init_win); if (win_used < tcp_snd_cwnd(tp)) { - tp->snd_ssthresh = tcp_current_ssthresh(sk); + WRITE_ONCE(tp->snd_ssthresh, tcp_current_ssthresh(sk)); tcp_snd_cwnd_set(tp, (tcp_snd_cwnd(tp) + win_used) >> 1); } tp->snd_cwnd_used = 0; @@ -3642,8 +3644,8 @@ start: TCP_ADD_STATS(sock_net(sk), TCP_MIB_RETRANSSEGS, segs); if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN) __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSYNRETRANS); - tp->total_retrans += segs; - tp->bytes_retrans += skb->len; + WRITE_ONCE(tp->total_retrans, tp->total_retrans + segs); + WRITE_ONCE(tp->bytes_retrans, tp->bytes_retrans + skb->len); /* make sure skb->data is aligned on arches that require it * and check if ack-trimming & collapsing extended the headroom @@ -4152,7 +4154,7 @@ static void tcp_connect_init(struct sock *sk) tp->snd_wnd = 0; tcp_init_wl(tp, 0); tcp_write_queue_purge(sk); - tp->snd_una = tp->write_seq; + WRITE_ONCE(tp->snd_una, tp->write_seq); tp->snd_sml = tp->write_seq; tp->snd_up = tp->write_seq; WRITE_ONCE(tp->snd_nxt, tp->write_seq); @@ -4646,7 +4648,8 @@ int tcp_rtx_synack(const struct sock *sk, struct request_sock *req) * However in this case, we are dealing with a passive fastopen * socket thus we can change total_retrans value. */ - tcp_sk_rw(sk)->total_retrans++; + WRITE_ONCE(tcp_sk_rw(sk)->total_retrans, + tcp_sk_rw(sk)->total_retrans + 1); } trace_tcp_retransmit_synack(sk, req); WRITE_ONCE(req->num_retrans, req->num_retrans + 1); diff --git a/net/ipv4/tcp_plb.c b/net/ipv4/tcp_plb.c index 68ccdb9a5412..c11a0cd3f8fe 100644 --- a/net/ipv4/tcp_plb.c +++ b/net/ipv4/tcp_plb.c @@ -80,7 +80,7 @@ void tcp_plb_check_rehash(struct sock *sk, struct tcp_plb_state *plb) sk_rethink_txhash(sk); plb->consec_cong_rounds = 0; - tcp_sk(sk)->plb_rehash++; + WRITE_ONCE(tcp_sk(sk)->plb_rehash, tcp_sk(sk)->plb_rehash + 1); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPPLBREHASH); } EXPORT_SYMBOL_GPL(tcp_plb_check_rehash); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index ea99988795e7..322db13333c7 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -50,7 +50,8 @@ static u32 tcp_clamp_rto_to_user_timeout(const struct sock *sk) u32 tcp_clamp_probe0_to_user_timeout(const struct sock *sk, u32 when) { const struct inet_connection_sock *icsk = inet_csk(sk); - u32 remaining, user_timeout; + u32 user_timeout; + s32 remaining; s32 elapsed; user_timeout = READ_ONCE(icsk->icsk_user_timeout); @@ -61,7 +62,7 @@ u32 tcp_clamp_probe0_to_user_timeout(const struct sock *sk, u32 when) if (unlikely(elapsed < 0)) elapsed = 0; remaining = msecs_to_jiffies(user_timeout) - elapsed; - remaining = max_t(u32, remaining, TCP_TIMEOUT_MIN); + remaining = max_t(int, remaining, TCP_TIMEOUT_MIN); return min_t(u32, remaining, when); } @@ -297,7 +298,7 @@ static int tcp_write_timeout(struct sock *sk) } if (sk_rethink_txhash(sk)) { - tp->timeout_rehash++; + WRITE_ONCE(tp->timeout_rehash, tp->timeout_rehash + 1); __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPTIMEOUTREHASH); } diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c index 950a66966059..574453af6bc0 100644 --- a/net/ipv4/tcp_vegas.c +++ b/net/ipv4/tcp_vegas.c @@ -245,7 +245,8 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked) */ tcp_snd_cwnd_set(tp, min(tcp_snd_cwnd(tp), (u32)target_cwnd + 1)); - tp->snd_ssthresh = tcp_vegas_ssthresh(tp); + WRITE_ONCE(tp->snd_ssthresh, + tcp_vegas_ssthresh(tp)); } else if (tcp_in_slow_start(tp)) { /* Slow start. */ @@ -261,8 +262,8 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked) * we slow down. */ tcp_snd_cwnd_set(tp, tcp_snd_cwnd(tp) - 1); - tp->snd_ssthresh - = tcp_vegas_ssthresh(tp); + WRITE_ONCE(tp->snd_ssthresh, + tcp_vegas_ssthresh(tp)); } else if (diff < alpha) { /* We don't have enough extra packets * in the network, so speed up. @@ -280,7 +281,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked) else if (tcp_snd_cwnd(tp) > tp->snd_cwnd_clamp) tcp_snd_cwnd_set(tp, tp->snd_cwnd_clamp); - tp->snd_ssthresh = tcp_current_ssthresh(sk); + WRITE_ONCE(tp->snd_ssthresh, tcp_current_ssthresh(sk)); } /* Wipe the slate clean for the next RTT. */ diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c index c6e97141eef2..b5a42adfd6ca 100644 --- a/net/ipv4/tcp_westwood.c +++ b/net/ipv4/tcp_westwood.c @@ -244,11 +244,11 @@ static void tcp_westwood_event(struct sock *sk, enum tcp_ca_event event) switch (event) { case CA_EVENT_COMPLETE_CWR: - tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk); + WRITE_ONCE(tp->snd_ssthresh, tcp_westwood_bw_rttmin(sk)); tcp_snd_cwnd_set(tp, tp->snd_ssthresh); break; case CA_EVENT_LOSS: - tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk); + WRITE_ONCE(tp->snd_ssthresh, tcp_westwood_bw_rttmin(sk)); /* Update RTT_min when next ack arrives */ w->reset_rtt_min = 1; break; diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c index b22b3dccd05e..9e581154f18f 100644 --- a/net/ipv4/tcp_yeah.c +++ b/net/ipv4/tcp_yeah.c @@ -147,7 +147,8 @@ do_vegas: tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp), yeah->reno_count)); - tp->snd_ssthresh = tcp_snd_cwnd(tp); + WRITE_ONCE(tp->snd_ssthresh, + tcp_snd_cwnd(tp)); } if (yeah->reno_count <= 2) diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index c024aa77f25b..c3806c6ac96f 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -164,7 +164,7 @@ config IPV6_SIT select INET_TUNNEL select NET_IP_TUNNEL select IPV6_NDISC_NODETYPE - default y + default m help Tunneling means encapsulating data of one protocol type within another protocol and sending it over a channel that understands the @@ -172,7 +172,7 @@ config IPV6_SIT into IPv4 packets. This is useful if you want to connect two IPv6 networks over an IPv4-only path. - Saying M here will produce a module called sit. If unsure, say Y. + Saying M here will produce a module called sit. If unsure, say M. config IPV6_SIT_6RD bool "IPv6: IPv6 Rapid Deployment (6RD)" diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index cb26beea4398..de1e68199a01 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -317,14 +317,19 @@ static void ah6_output_done(void *data, int err) struct ipv6hdr *top_iph = ipv6_hdr(skb); struct ip_auth_hdr *ah = ip_auth_hdr(skb); struct tmp_ext *iph_ext; + int seqhi_len = 0; + __be32 *seqhi; extlen = skb_network_header_len(skb) - sizeof(struct ipv6hdr); if (extlen) extlen += sizeof(*iph_ext); + if (x->props.flags & XFRM_STATE_ESN) + seqhi_len = sizeof(*seqhi); iph_base = AH_SKB_CB(skb)->tmp; iph_ext = ah_tmp_ext(iph_base); - icv = ah_tmp_icv(iph_ext, extlen); + seqhi = (__be32 *)((char *)iph_ext + extlen); + icv = ah_tmp_icv(seqhi, seqhi_len); memcpy(ah->auth_data, icv, ahp->icv_trunc_len); memcpy(top_iph, iph_base, IPV6HDR_BASELEN); @@ -471,13 +476,18 @@ static void ah6_input_done(void *data, int err) struct ip_auth_hdr *ah = ip_auth_hdr(skb); int hdr_len = skb_network_header_len(skb); int ah_hlen = ipv6_authlen(ah); + int seqhi_len = 0; + __be32 *seqhi; if (err) goto out; + if (x->props.flags & XFRM_STATE_ESN) + seqhi_len = sizeof(*seqhi); work_iph = AH_SKB_CB(skb)->tmp; auth_data = ah_tmp_auth(work_iph, hdr_len); - icv = ah_tmp_icv(auth_data, ahp->icv_trunc_len); + seqhi = (__be32 *)(auth_data + ahp->icv_trunc_len); + icv = ah_tmp_icv(seqhi, seqhi_len); err = crypto_memneq(icv, auth_data, ahp->icv_trunc_len) ? -EBADMSG : 0; if (err) diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 9f75313734f8..9c06c5a1419d 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -915,7 +915,8 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb) nfrags = 1; goto skip_cow; - } else if (!skb_has_frag_list(skb)) { + } else if (!skb_has_frag_list(skb) && + !skb_has_shared_frag(skb)) { nfrags = skb_shinfo(skb)->nr_frags; nfrags++; diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 95558fd6f447..03cbce842c1a 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -491,6 +491,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb) struct net *net = dev_net(skb->dev); struct inet6_dev *idev; struct ipv6hdr *oldhdr; + unsigned int chdr_len; unsigned char *buf; int accept_rpl_seg; int i, err; @@ -592,8 +593,10 @@ looped_back: skb_pull(skb, ((hdr->hdrlen + 1) << 3)); skb_postpull_rcsum(skb, oldhdr, sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3)); - if (unlikely(!hdr->segments_left)) { - if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0, + chdr_len = sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3); + if (unlikely(!hdr->segments_left || + skb_headroom(skb) < chdr_len + skb->mac_len)) { + if (pskb_expand_head(skb, chdr_len + skb->mac_len, 0, GFP_ATOMIC)) { __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS); kfree_skb(skb); @@ -603,7 +606,7 @@ looped_back: oldhdr = ipv6_hdr(skb); } - skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr)); + skb_push(skb, chdr_len); skb_reset_network_header(skb); skb_mac_header_rebuild(skb); skb_set_transport_header(skb, sizeof(struct ipv6hdr)); diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index 49e31e4ae7b7..9d06d487e8b1 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c @@ -73,6 +73,7 @@ int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, __be16 *frag_offp) { u8 nexthdr = *nexthdrp; + int exthdr_cnt = 0; *frag_offp = 0; @@ -82,6 +83,8 @@ int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, if (nexthdr == NEXTHDR_NONE) return -1; + if (unlikely(exthdr_cnt++ >= IP6_MAX_EXT_HDRS_CNT)) + return -1; hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); if (!hp) return -1; @@ -190,6 +193,7 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, { unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); u8 nexthdr = ipv6_hdr(skb)->nexthdr; + int exthdr_cnt = 0; bool found; if (fragoff) @@ -216,6 +220,9 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, return -ENOENT; } + if (unlikely(exthdr_cnt++ >= IP6_MAX_EXT_HDRS_CNT)) + return -EBADMSG; + hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); if (!hp) return -EBADMSG; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 799d9e9ac45d..efb23807a026 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -1104,7 +1104,6 @@ static int icmpv6_rcv(struct sk_buff *skb) struct net *net = dev_net_rcu(skb->dev); struct net_device *dev = icmp6_dev(skb); struct inet6_dev *idev = __in6_dev_get(dev); - const struct in6_addr *saddr, *daddr; struct icmp6hdr *hdr; u8 type; @@ -1135,12 +1134,10 @@ static int icmpv6_rcv(struct sk_buff *skb) __ICMP6_INC_STATS(dev_net_rcu(dev), idev, ICMP6_MIB_INMSGS); - saddr = &ipv6_hdr(skb)->saddr; - daddr = &ipv6_hdr(skb)->daddr; - if (skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo)) { net_dbg_ratelimited("ICMPv6 checksum failed [%pI6c > %pI6c]\n", - saddr, daddr); + &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr); goto csum_error; } @@ -1220,7 +1217,8 @@ static int icmpv6_rcv(struct sk_buff *skb) break; net_dbg_ratelimited("icmpv6: msg of unknown type [%pI6c > %pI6c]\n", - saddr, daddr); + &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr); /* * error of unknown type. diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index c92f98c6f6ec..b1ccdf0dc646 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -36,11 +36,11 @@ /* FL hash table */ #define FL_MAX_PER_SOCK 32 -#define FL_MAX_SIZE 4096 +#define FL_MAX_SIZE 8192 #define FL_HASH_MASK 255 #define FL_HASH(l) (ntohl(l)&FL_HASH_MASK) -static atomic_t fl_size = ATOMIC_INIT(0); +static int fl_size; static struct ip6_flowlabel __rcu *fl_ht[FL_HASH_MASK+1]; static void ip6_fl_gc(struct timer_list *unused); @@ -162,8 +162,9 @@ static void ip6_fl_gc(struct timer_list *unused) ttd = fl->expires; if (time_after_eq(now, ttd)) { *flp = fl->next; + fl_size--; + fl->fl_net->ipv6.flowlabel_count--; fl_free(fl); - atomic_dec(&fl_size); continue; } if (!sched || time_before(ttd, sched)) @@ -172,7 +173,7 @@ static void ip6_fl_gc(struct timer_list *unused) flp = &fl->next; } } - if (!sched && atomic_read(&fl_size)) + if (!sched && fl_size) sched = now + FL_MAX_LINGER; if (sched) { mod_timer(&ip6_fl_gc_timer, sched); @@ -196,7 +197,8 @@ static void __net_exit ip6_fl_purge(struct net *net) atomic_read(&fl->users) == 0) { *flp = fl->next; fl_free(fl); - atomic_dec(&fl_size); + fl_size--; + net->ipv6.flowlabel_count--; continue; } flp = &fl->next; @@ -210,10 +212,10 @@ static struct ip6_flowlabel *fl_intern(struct net *net, { struct ip6_flowlabel *lfl; + lockdep_assert_held(&ip6_fl_lock); + fl->label = label & IPV6_FLOWLABEL_MASK; - rcu_read_lock(); - spin_lock_bh(&ip6_fl_lock); if (label == 0) { for (;;) { fl->label = htonl(get_random_u32())&IPV6_FLOWLABEL_MASK; @@ -235,8 +237,6 @@ static struct ip6_flowlabel *fl_intern(struct net *net, lfl = __fl_lookup(net, fl->label); if (lfl) { atomic_inc(&lfl->users); - spin_unlock_bh(&ip6_fl_lock); - rcu_read_unlock(); return lfl; } } @@ -244,9 +244,8 @@ static struct ip6_flowlabel *fl_intern(struct net *net, fl->lastuse = jiffies; fl->next = fl_ht[FL_HASH(fl->label)]; rcu_assign_pointer(fl_ht[FL_HASH(fl->label)], fl); - atomic_inc(&fl_size); - spin_unlock_bh(&ip6_fl_lock); - rcu_read_unlock(); + fl_size++; + net->ipv6.flowlabel_count++; return NULL; } @@ -464,10 +463,17 @@ done: static int mem_check(struct sock *sk) { - int room = FL_MAX_SIZE - atomic_read(&fl_size); + const int unpriv_total_limit = FL_MAX_SIZE - (FL_MAX_SIZE / 4); + const int unpriv_user_limit = unpriv_total_limit / 2; + struct net *net = sock_net(sk); + int room; struct ipv6_fl_socklist *sfl; int count = 0; + lockdep_assert_held(&ip6_fl_lock); + + room = FL_MAX_SIZE - fl_size; + if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK) return 0; @@ -478,7 +484,9 @@ static int mem_check(struct sock *sk) if (room <= 0 || ((count >= FL_MAX_PER_SOCK || - (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) && + (count > 0 && room < FL_MAX_SIZE / 2) || + room < FL_MAX_SIZE / 4 || + net->ipv6.flowlabel_count >= unpriv_user_limit) && !capable(CAP_NET_ADMIN))) return -ENOBUFS; @@ -692,11 +700,19 @@ release: if (!sfl1) goto done; + rcu_read_lock(); + spin_lock_bh(&ip6_fl_lock); err = mem_check(sk); + if (err == 0) + fl1 = fl_intern(net, fl, freq->flr_label); + else + fl1 = NULL; + spin_unlock_bh(&ip6_fl_lock); + rcu_read_unlock(); + if (err != 0) goto done; - fl1 = fl_intern(net, fl, freq->flr_label); if (fl1) goto recheck; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 63fc8556b475..365b4059eb20 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -2262,10 +2262,11 @@ static int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { - struct ip6gre_net *ign = net_generic(dev_net(dev), ip6gre_net_id); + struct ip6_tnl *t = netdev_priv(dev); struct __ip6_tnl_parm p; - struct ip6_tnl *t; + struct ip6gre_net *ign; + ign = net_generic(t->net, ip6gre_net_id); t = ip6gre_changelink_common(dev, tb, data, &p, extack); if (IS_ERR(t)) return PTR_ERR(t); diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 967b07aeb683..8972863c93ee 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -403,6 +403,7 @@ INDIRECT_CALLABLE_DECLARE(int tcp_v6_rcv(struct sk_buff *)); void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr, bool have_final) { + int exthdr_cnt = IP6CB(skb)->flags & IP6SKB_HOPBYHOP ? 1 : 0; const struct inet6_protocol *ipprot; struct inet6_dev *idev; unsigned int nhoff; @@ -487,6 +488,10 @@ resubmit_final: nexthdr = ret; goto resubmit_final; } else { + if (unlikely(exthdr_cnt++ >= IP6_MAX_EXT_HDRS_CNT)) { + SKB_DR_SET(reason, IPV6_TOO_MANY_EXTHDRS); + goto discard; + } goto resubmit; } } else if (ret == 0) { diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 7e92909ab5be..c14adcdd4396 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -468,6 +468,7 @@ static int ip6_forward_proxy_check(struct sk_buff *skb) default: break; } + hdr = ipv6_hdr(skb); } /* @@ -582,6 +583,8 @@ int ip6_forward(struct sk_buff *skb) if (READ_ONCE(net->ipv6.devconf_all->proxy_ndp) && pneigh_lookup(&nd_tbl, net, &hdr->daddr, skb->dev)) { int proxied = ip6_forward_proxy_check(skb); + + hdr = ipv6_hdr(skb); if (proxied > 0) { /* It's tempting to decrease the hop limit * here by 1, as we do at the end of the @@ -1794,6 +1797,8 @@ alloc_new_skb: if (err < 0) goto error; copy = err; + if (!(flags & MSG_NO_SHARED_FRAGS)) + skb_shinfo(skb)->flags |= SKBFL_SHARED_FRAG; wmem_alloc_delta += copy; } else if (!zc) { int i = skb_shinfo(skb)->nr_frags; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 46bc06506470..9d1037ac082f 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -62,6 +62,8 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS_RTNL_LINK("ip6tnl"); MODULE_ALIAS_NETDEV("ip6tnl0"); +#define IP6_TUNNEL_MAX_DEST_TLVS 8 + #define IP6_TUNNEL_HASH_SIZE_SHIFT 5 #define IP6_TUNNEL_HASH_SIZE (1 << IP6_TUNNEL_HASH_SIZE_SHIFT) @@ -397,11 +399,15 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw) unsigned int nhoff = raw - skb->data; unsigned int off = nhoff + sizeof(*ipv6h); u8 nexthdr = ipv6h->nexthdr; + int exthdr_cnt = 0; while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) { struct ipv6_opt_hdr *hdr; u16 optlen; + if (unlikely(exthdr_cnt++ >= IP6_MAX_EXT_HDRS_CNT)) + break; + if (!pskb_may_pull(skb, off + sizeof(*hdr))) break; @@ -425,11 +431,15 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw) break; } if (nexthdr == NEXTHDR_DEST) { + int tlv_cnt = 0; u16 i = 2; while (1) { struct ipv6_tlv_tnl_enc_lim *tel; + if (unlikely(tlv_cnt++ >= IP6_TUNNEL_MAX_DEST_TLVS)) + break; + /* No more room for encapsulation limit */ if (i + sizeof(*tel) > optlen) break; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index d585ac3c1113..9d9c3763f2f5 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -1713,12 +1713,10 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) static void __ip6t_unregister_table(struct net *net, struct xt_table *table) { - struct xt_table_info *private; - void *loc_cpu_entry; + struct xt_table_info *private = table->private; struct module *table_owner = table->me; struct ip6t_entry *iter; - - private = xt_unregister_table(table); + void *loc_cpu_entry; /* Decrease module usage counts and free resources */ loc_cpu_entry = private->entries; @@ -1727,19 +1725,18 @@ static void __ip6t_unregister_table(struct net *net, struct xt_table *table) if (private->number > private->initial_entries) module_put(table_owner); xt_free_table_info(private); + kfree(table); } int ip6t_register_table(struct net *net, const struct xt_table *table, const struct ip6t_replace *repl, const struct nf_hook_ops *template_ops) { - struct nf_hook_ops *ops; - unsigned int num_ops; - int ret, i; - struct xt_table_info *newinfo; struct xt_table_info bootstrap = {0}; - void *loc_cpu_entry; + struct xt_table_info *newinfo; struct xt_table *new_table; + void *loc_cpu_entry; + int ret; newinfo = xt_alloc_table_info(repl->size); if (!newinfo) @@ -1754,7 +1751,7 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, return ret; } - new_table = xt_register_table(net, table, &bootstrap, newinfo); + new_table = xt_register_table(net, table, template_ops, &bootstrap, newinfo); if (IS_ERR(new_table)) { struct ip6t_entry *iter; @@ -1764,48 +1761,12 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, return PTR_ERR(new_table); } - if (!template_ops) - return 0; - - num_ops = hweight32(table->valid_hooks); - if (num_ops == 0) { - ret = -EINVAL; - goto out_free; - } - - ops = kmemdup_array(template_ops, num_ops, sizeof(*ops), GFP_KERNEL); - if (!ops) { - ret = -ENOMEM; - goto out_free; - } - - for (i = 0; i < num_ops; i++) - ops[i].priv = new_table; - - new_table->ops = ops; - - ret = nf_register_net_hooks(net, ops, num_ops); - if (ret != 0) - goto out_free; - return ret; - -out_free: - __ip6t_unregister_table(net, new_table); - return ret; -} - -void ip6t_unregister_table_pre_exit(struct net *net, const char *name) -{ - struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); - - if (table) - nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); } void ip6t_unregister_table_exit(struct net *net, const char *name) { - struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); + struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV6, name); if (table) __ip6t_unregister_table(net, table); @@ -1894,7 +1855,6 @@ static void __exit ip6_tables_fini(void) } EXPORT_SYMBOL(ip6t_register_table); -EXPORT_SYMBOL(ip6t_unregister_table_pre_exit); EXPORT_SYMBOL(ip6t_unregister_table_exit); EXPORT_SYMBOL(ip6t_do_table); diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index e8992693e14a..b074fc477676 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -60,7 +60,7 @@ static int __net_init ip6table_filter_net_init(struct net *net) static void __net_exit ip6table_filter_net_pre_exit(struct net *net) { - ip6t_unregister_table_pre_exit(net, "filter"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "filter"); } static void __net_exit ip6table_filter_net_exit(struct net *net) @@ -76,32 +76,32 @@ static struct pernet_operations ip6table_filter_net_ops = { static int __init ip6table_filter_init(void) { - int ret = xt_register_template(&packet_filter, - ip6table_filter_table_init); - - if (ret < 0) - return ret; + int ret; filter_ops = xt_hook_ops_alloc(&packet_filter, ip6t_do_table); - if (IS_ERR(filter_ops)) { - xt_unregister_template(&packet_filter); + if (IS_ERR(filter_ops)) return PTR_ERR(filter_ops); - } ret = register_pernet_subsys(&ip6table_filter_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&packet_filter, ip6table_filter_table_init); if (ret < 0) { - xt_unregister_template(&packet_filter); - kfree(filter_ops); - return ret; + unregister_pernet_subsys(&ip6table_filter_net_ops); + goto err_free; } + return 0; +err_free: + kfree(filter_ops); return ret; } static void __exit ip6table_filter_fini(void) { - unregister_pernet_subsys(&ip6table_filter_net_ops); xt_unregister_template(&packet_filter); + unregister_pernet_subsys(&ip6table_filter_net_ops); kfree(filter_ops); } diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index 8dd4cd0c47bd..e6ee036a9b2c 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -89,7 +89,7 @@ static int ip6table_mangle_table_init(struct net *net) static void __net_exit ip6table_mangle_net_pre_exit(struct net *net) { - ip6t_unregister_table_pre_exit(net, "mangle"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "mangle"); } static void __net_exit ip6table_mangle_net_exit(struct net *net) @@ -104,32 +104,33 @@ static struct pernet_operations ip6table_mangle_net_ops = { static int __init ip6table_mangle_init(void) { - int ret = xt_register_template(&packet_mangler, - ip6table_mangle_table_init); - - if (ret < 0) - return ret; + int ret; mangle_ops = xt_hook_ops_alloc(&packet_mangler, ip6table_mangle_hook); - if (IS_ERR(mangle_ops)) { - xt_unregister_template(&packet_mangler); + if (IS_ERR(mangle_ops)) return PTR_ERR(mangle_ops); - } ret = register_pernet_subsys(&ip6table_mangle_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&packet_mangler, + ip6table_mangle_table_init); if (ret < 0) { - xt_unregister_template(&packet_mangler); - kfree(mangle_ops); - return ret; + unregister_pernet_subsys(&ip6table_mangle_net_ops); + goto err_free; } + return 0; +err_free: + kfree(mangle_ops); return ret; } static void __exit ip6table_mangle_fini(void) { - unregister_pernet_subsys(&ip6table_mangle_net_ops); xt_unregister_template(&packet_mangler); + unregister_pernet_subsys(&ip6table_mangle_net_ops); kfree(mangle_ops); } diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c index e119d4f090cc..c2394e2c94b5 100644 --- a/net/ipv6/netfilter/ip6table_nat.c +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -81,7 +81,7 @@ static int ip6t_nat_register_lookups(struct net *net) while (i) nf_nat_ipv6_unregister_fn(net, &ops[--i]); - kfree(ops); + kfree_rcu(ops, rcu); return ret; } } @@ -102,7 +102,7 @@ static void ip6t_nat_unregister_lookups(struct net *net) for (i = 0; i < ARRAY_SIZE(nf_nat_ipv6_ops); i++) nf_nat_ipv6_unregister_fn(net, &ops[i]); - kfree(ops); + kfree_rcu(ops, rcu); } static int ip6table_nat_table_init(struct net *net) @@ -121,8 +121,11 @@ static int ip6table_nat_table_init(struct net *net) } ret = ip6t_nat_register_lookups(net); - if (ret < 0) + if (ret < 0) { + xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); + synchronize_rcu(); ip6t_unregister_table_exit(net, "nat"); + } kfree(repl); return ret; @@ -131,6 +134,7 @@ static int ip6table_nat_table_init(struct net *net) static void __net_exit ip6table_nat_net_pre_exit(struct net *net) { ip6t_nat_unregister_lookups(net); + xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); } static void __net_exit ip6table_nat_net_exit(struct net *net) diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c index fc9f6754028f..3b161ee875bc 100644 --- a/net/ipv6/netfilter/ip6table_raw.c +++ b/net/ipv6/netfilter/ip6table_raw.c @@ -52,7 +52,7 @@ static int ip6table_raw_table_init(struct net *net) static void __net_exit ip6table_raw_net_pre_exit(struct net *net) { - ip6t_unregister_table_pre_exit(net, "raw"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "raw"); } static void __net_exit ip6table_raw_net_exit(struct net *net) @@ -75,31 +75,31 @@ static int __init ip6table_raw_init(void) pr_info("Enabling raw table before defrag\n"); } - ret = xt_register_template(table, ip6table_raw_table_init); - if (ret < 0) - return ret; - /* Register hooks */ rawtable_ops = xt_hook_ops_alloc(table, ip6t_do_table); - if (IS_ERR(rawtable_ops)) { - xt_unregister_template(table); + if (IS_ERR(rawtable_ops)) return PTR_ERR(rawtable_ops); - } ret = register_pernet_subsys(&ip6table_raw_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(table, ip6table_raw_table_init); if (ret < 0) { - kfree(rawtable_ops); - xt_unregister_template(table); - return ret; + unregister_pernet_subsys(&ip6table_raw_net_ops); + goto err_free; } + return 0; +err_free: + kfree(rawtable_ops); return ret; } static void __exit ip6table_raw_fini(void) { - unregister_pernet_subsys(&ip6table_raw_net_ops); xt_unregister_template(&packet_raw); + unregister_pernet_subsys(&ip6table_raw_net_ops); kfree(rawtable_ops); } diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c index 4df14a9bae78..4bd5d97b8ab6 100644 --- a/net/ipv6/netfilter/ip6table_security.c +++ b/net/ipv6/netfilter/ip6table_security.c @@ -49,7 +49,7 @@ static int ip6table_security_table_init(struct net *net) static void __net_exit ip6table_security_net_pre_exit(struct net *net) { - ip6t_unregister_table_pre_exit(net, "security"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "security"); } static void __net_exit ip6table_security_net_exit(struct net *net) @@ -64,32 +64,33 @@ static struct pernet_operations ip6table_security_net_ops = { static int __init ip6table_security_init(void) { - int ret = xt_register_template(&security_table, - ip6table_security_table_init); - - if (ret < 0) - return ret; + int ret; sectbl_ops = xt_hook_ops_alloc(&security_table, ip6t_do_table); - if (IS_ERR(sectbl_ops)) { - xt_unregister_template(&security_table); + if (IS_ERR(sectbl_ops)) return PTR_ERR(sectbl_ops); - } ret = register_pernet_subsys(&ip6table_security_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&security_table, + ip6table_security_table_init); if (ret < 0) { - kfree(sectbl_ops); - xt_unregister_template(&security_table); - return ret; + unregister_pernet_subsys(&ip6table_security_net_ops); + goto err_free; } + return 0; +err_free: + kfree(sectbl_ops); return ret; } static void __exit ip6table_security_fini(void) { - unregister_pernet_subsys(&ip6table_security_net_ops); xt_unregister_template(&security_table); + unregister_pernet_subsys(&ip6table_security_net_ops); kfree(sectbl_ops); } diff --git a/net/ipv6/netfilter/nf_socket_ipv6.c b/net/ipv6/netfilter/nf_socket_ipv6.c index ced8bd44828e..893f2aeb4711 100644 --- a/net/ipv6/netfilter/nf_socket_ipv6.c +++ b/net/ipv6/netfilter/nf_socket_ipv6.c @@ -100,6 +100,7 @@ struct sock *nf_sk_lookup_slow_v6(struct net *net, const struct sk_buff *skb, const struct in6_addr *daddr = NULL, *saddr = NULL; struct ipv6hdr *iph = ipv6_hdr(skb), ipv6_var; struct sk_buff *data_skb = NULL; + unsigned short fragoff = 0; int doff = 0; int thoff = 0, tproto; #if IS_ENABLED(CONFIG_NF_CONNTRACK) @@ -107,8 +108,8 @@ struct sock *nf_sk_lookup_slow_v6(struct net *net, const struct sk_buff *skb, struct nf_conn const *ct; #endif - tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); - if (tproto < 0) { + tproto = ipv6_find_hdr(skb, &thoff, -1, &fragoff, NULL); + if (tproto < 0 || fragoff) { pr_debug("unable to find transport header in IPv6 packet, dropping\n"); return NULL; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 19eb6b702227..e3d355d1fbd6 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1645,6 +1645,10 @@ static unsigned int fib6_mtu(const struct fib6_result *res) rcu_read_lock(); idev = __in6_dev_get(dev); + if (!idev) { + rcu_read_unlock(); + return 0; + } mtu = READ_ONCE(idev->cnf.mtu6); rcu_read_unlock(); } @@ -4995,6 +4999,7 @@ static int fib6_ifdown(struct fib6_info *rt, void *p_arg) rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) break; rt->fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN; + fib6_update_sernum(net, rt); rt6_multipath_rebalance(rt); break; } diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c index c7942cf65567..4e10adcd70e8 100644 --- a/net/ipv6/rpl_iptunnel.c +++ b/net/ipv6/rpl_iptunnel.c @@ -287,7 +287,16 @@ static int rpl_input(struct sk_buff *skb) if (!dst) { ip6_route_input(skb); + + /* ip6_route_input() sets a NOREF dst; force a refcount on it + * before caching or further use. + */ + skb_dst_force(skb); dst = skb_dst(skb); + if (unlikely(!dst)) { + err = -ENETUNREACH; + goto drop; + } /* cache only if we don't create a dst reference loop */ if (!dst->error && lwtst != dst->lwtstate) { diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 97b50d9b1365..4c45c0a77d75 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -515,7 +515,16 @@ static int seg6_input_core(struct net *net, struct sock *sk, if (!dst) { ip6_route_input(skb); + + /* ip6_route_input() sets a NOREF dst; force a refcount on it + * before caching or further use. + */ + skb_dst_force(skb); dst = skb_dst(skb); + if (unlikely(!dst)) { + err = -ENETUNREACH; + goto drop; + } /* cache only if we don't create a dst reference loop */ if (!dst->error && lwtst != dst->lwtstate) { @@ -746,7 +755,8 @@ static int seg6_build_state(struct net *net, struct nlattr *nla, newts->type = LWTUNNEL_ENCAP_SEG6; newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT; - if (tuninfo->mode != SEG6_IPTUN_MODE_L2ENCAP) + if (tuninfo->mode != SEG6_IPTUN_MODE_L2ENCAP && + tuninfo->mode != SEG6_IPTUN_MODE_L2ENCAP_RED) newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT; newts->headroom = seg6_lwt_headroom(tuninfo); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 2c3f7a739709..d13d49bfef19 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -288,8 +288,10 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr_unsized *uaddr, saddr = &fl6->saddr; err = inet_bhash2_update_saddr(sk, saddr, AF_INET6); - if (err) + if (err) { + dst_release(dst); goto failure; + } } /* set the source address */ @@ -1617,12 +1619,13 @@ int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) if (sk->sk_state == TCP_LISTEN) { struct sock *nsk = tcp_v6_cookie_check(sk, skb); + if (!nsk) + return 0; if (nsk != sk) { - if (nsk) { - reason = tcp_child_process(sk, nsk, skb); - if (reason) - goto reset; - } + reason = tcp_child_process(sk, nsk, skb); + sock_put(nsk); + if (reason) + goto reset; return 0; } } else @@ -1827,8 +1830,10 @@ lookup: rst_reason = sk_rst_convert_drop_reason(drop_reason); tcp_v6_send_reset(nsk, skb, rst_reason); + sock_put(nsk); goto discard_and_relse; } + sock_put(nsk); sock_put(sk); return 0; } diff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c index ea2f805d3b01..9b586fcec485 100644 --- a/net/ipv6/xfrm6_protocol.c +++ b/net/ipv6/xfrm6_protocol.c @@ -88,8 +88,10 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, skb, flags); - if (dst->error) + if (dst->error) { + dst_release(dst); goto drop; + } skb_dst_set(skb, dst); } diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 59d593bb5d18..1b210db3119e 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -520,8 +520,10 @@ static int llc_ui_connect(struct socket *sock, struct sockaddr_unsized *uaddr, if (sk->sk_state == TCP_SYN_SENT) { const long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); - if (!timeo || !llc_ui_wait_for_conn(sk, timeo)) + if (!timeo || !llc_ui_wait_for_conn(sk, timeo)) { + rc = -EINPROGRESS; goto out; + } rc = sock_intr_errno(timeo); if (signal_pending(current)) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 160ae65a5c64..0a0f27836d57 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -437,6 +437,15 @@ ieee80211_verify_sta_ht_mcs_support(struct ieee80211_sub_if_data *sdata, memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap)); ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap); + /* + * Some Xfinity XB8 firmware advertises >1 spatial stream MCS indexes in + * their basic HT-MCS set. On cards with lower spatial streams, the check + * would fail, and we'd be stuck with no HT when it in fact work fine with + * its own supported rate. So check it only in strict mode. + */ + if (!ieee80211_hw_check(&sdata->local->hw, STRICT)) + return true; + /* * P802.11REVme/D7.0 - 6.5.4.2.4 * ... @@ -9140,7 +9149,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)cbss->priv; struct sta_info *new_sta = NULL; struct ieee80211_link_data *link; - bool have_sta = false; + struct sta_info *have_sta = NULL; bool mlo; int err; u16 new_links; @@ -9159,11 +9168,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, mlo = false; } - if (assoc) { - rcu_read_lock(); + if (assoc) have_sta = sta_info_get(sdata, ap_mld_addr); - rcu_read_unlock(); - } if (mlo && !have_sta && WARN_ON(sdata->vif.valid_links || sdata->vif.active_links)) @@ -9327,6 +9333,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, out_release_chan: ieee80211_link_release_channel(link); out_err: + if (mlo && have_sta) + WARN_ON(__sta_info_destroy(have_sta)); ieee80211_vif_set_links(sdata, 0, 0); return err; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 3e5d1c47a5b0..d18e962126ce 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -4971,7 +4971,7 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx, struct sk_buff *skb = rx->skb; struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - static ieee80211_rx_result res; + ieee80211_rx_result res; int orig_len = skb->len; int hdrlen = ieee80211_hdrlen(hdr->frame_control); int snap_offs = hdrlen; @@ -5380,7 +5380,9 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, if (!link_sta) goto out; - ieee80211_rx_data_set_link(&rx, link_sta->link_id); + if (!ieee80211_rx_data_set_link(&rx, + link_sta->link_id)) + goto out; } if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) diff --git a/net/mac80211/tests/chan-mode.c b/net/mac80211/tests/chan-mode.c index adc069065e73..fa370831d617 100644 --- a/net/mac80211/tests/chan-mode.c +++ b/net/mac80211/tests/chan-mode.c @@ -65,6 +65,7 @@ static const struct determine_chan_mode_case { .ht_capa_mask = { .mcs.rx_mask[0] = 0xf7, }, + .strict = true, }, { .desc = "Masking out a RX rate in VHT capabilities", .conn_mode = IEEE80211_CONN_MODE_EHT, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index b093bc203c81..2529b01e2cd5 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3700,11 +3700,11 @@ void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy, struct ieee80211_local *local = container_of(work, struct ieee80211_local, radar_detected_work); struct cfg80211_chan_def chandef; - struct ieee80211_chanctx *ctx; + struct ieee80211_chanctx *ctx, *tmp; lockdep_assert_wiphy(local->hw.wiphy); - list_for_each_entry(ctx, &local->chanctx_list, list) { + list_for_each_entry_safe(ctx, tmp, &local->chanctx_list, list) { if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) continue; diff --git a/net/mctp/route.c b/net/mctp/route.c index 26fb8c6bbad2..1f3dccbb7aed 100644 --- a/net/mctp/route.c +++ b/net/mctp/route.c @@ -441,6 +441,7 @@ static int mctp_dst_input(struct mctp_dst *dst, struct sk_buff *skb) unsigned long f; u8 tag, flags; int rc; + u8 ver; msk = NULL; rc = -EINVAL; @@ -467,7 +468,8 @@ static int mctp_dst_input(struct mctp_dst *dst, struct sk_buff *skb) netid = mctp_cb(skb)->net; skb_pull(skb, sizeof(struct mctp_hdr)); - if (mh->ver != 1) + ver = mh->ver & MCTP_HDR_VER_MASK; + if (ver < MCTP_VER_MIN || ver > MCTP_VER_MAX) goto out; flags = mh->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM); @@ -1317,6 +1319,7 @@ static int mctp_pkttype_receive(struct sk_buff *skb, struct net_device *dev, struct mctp_dst dst; struct mctp_hdr *mh; int rc; + u8 ver; rcu_read_lock(); mdev = __mctp_dev_get(dev); @@ -1334,7 +1337,8 @@ static int mctp_pkttype_receive(struct sk_buff *skb, struct net_device *dev, /* We have enough for a header; decode and route */ mh = mctp_hdr(skb); - if (mh->ver < MCTP_VER_MIN || mh->ver > MCTP_VER_MAX) + ver = mh->ver & MCTP_HDR_VER_MASK; + if (ver < MCTP_VER_MIN || ver > MCTP_VER_MAX) goto err_drop; /* source must be valid unicast or null; drop reserved ranges and diff --git a/net/mctp/test/route-test.c b/net/mctp/test/route-test.c index e1033643fab0..e4b230ef6099 100644 --- a/net/mctp/test/route-test.c +++ b/net/mctp/test/route-test.c @@ -920,9 +920,9 @@ static void mctp_test_route_input_cloned_frag(struct kunit *test) static void mctp_test_route_input_null_eid(struct kunit *test) { struct mctp_hdr hdr = RX_HDR(1, 10, 0, FL_S | FL_E | FL_TO); + struct sockaddr_mctp addr = { 0 }; struct sk_buff *skb_pkt, *skb_sk; struct mctp_test_dev *dev; - struct sockaddr_mctp addr; struct socket *sock; u8 type = 0; int rc; diff --git a/net/mctp/test/utils.c b/net/mctp/test/utils.c index c3987d5ade7a..6eef8d485c25 100644 --- a/net/mctp/test/utils.c +++ b/net/mctp/test/utils.c @@ -116,7 +116,7 @@ void mctp_test_destroy_dev(struct mctp_test_dev *dev) static int mctp_test_dst_output(struct mctp_dst *dst, struct sk_buff *skb) { skb->dev = dst->dev->dev; - dev_queue_xmit(skb); + dev_direct_xmit(skb, 0); return 0; } diff --git a/net/mptcp/bpf.c b/net/mptcp/bpf.c index 8a16672b94e2..4cc16cbeb328 100644 --- a/net/mptcp/bpf.c +++ b/net/mptcp/bpf.c @@ -14,7 +14,7 @@ struct mptcp_sock *bpf_mptcp_sock_from_subflow(struct sock *sk) { - if (sk && sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP && sk_is_mptcp(sk)) + if (sk && sk_fullsock(sk) && sk_is_tcp(sk) && sk_is_mptcp(sk)) return mptcp_sk(mptcp_subflow_ctx(sk)->conn); return NULL; diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c index 82ec15bcfd7f..082c46c0f50e 100644 --- a/net/mptcp/fastopen.c +++ b/net/mptcp/fastopen.c @@ -12,6 +12,7 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf struct sock *sk, *ssk; struct sk_buff *skb; struct tcp_sock *tp; + bool has_rxtstamp; /* on early fallback the subflow context is deleted by * subflow_syn_recv_sock() @@ -40,12 +41,13 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf */ tp->copied_seq += skb->len; subflow->ssn_offset += skb->len; + has_rxtstamp = TCP_SKB_CB(skb)->has_rxtstamp; /* Only the sequence delta is relevant */ MPTCP_SKB_CB(skb)->map_seq = -skb->len; MPTCP_SKB_CB(skb)->end_seq = 0; MPTCP_SKB_CB(skb)->offset = 0; - MPTCP_SKB_CB(skb)->has_rxtstamp = TCP_SKB_CB(skb)->has_rxtstamp; + MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp; MPTCP_SKB_CB(skb)->cant_coalesce = 1; mptcp_data_lock(sk); diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index 57a456690406..3c152bf66cd5 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -16,6 +16,7 @@ struct mptcp_pm_add_entry { struct list_head list; struct mptcp_addr_info addr; u8 retrans_times; + bool timer_done; struct timer_list add_timer; struct mptcp_sock *sock; struct rcu_head rcu; @@ -283,6 +284,9 @@ int mptcp_pm_mp_prio_send_ack(struct mptcp_sock *msk, struct sock *ssk = mptcp_subflow_tcp_sock(subflow); struct mptcp_addr_info local, remote; + if (!__mptcp_subflow_active(subflow)) + continue; + mptcp_local_address((struct sock_common *)ssk, &local); if (!mptcp_addresses_equal(&local, addr, addr->port)) continue; @@ -305,18 +309,31 @@ static unsigned int mptcp_adjust_add_addr_timeout(struct mptcp_sock *msk) const struct net *net = sock_net((struct sock *)msk); unsigned int rto = mptcp_get_add_addr_timeout(net); struct mptcp_subflow_context *subflow; - unsigned int max = 0; + unsigned int max = 0, max_stale = 0; + + if (!rto) + return 0; mptcp_for_each_subflow(msk, subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); struct inet_connection_sock *icsk = inet_csk(ssk); - if (icsk->icsk_rto > max) + if (!__mptcp_subflow_active(subflow)) + continue; + + if (unlikely(subflow->stale)) { + if (icsk->icsk_rto > max_stale) + max_stale = icsk->icsk_rto; + } else if (icsk->icsk_rto > max) { max = icsk->icsk_rto; + } } - if (max && max < rto) - rto = max; + if (max) + return min(max, rto); + + if (max_stale) + return min(max_stale, rto); return rto; } @@ -327,26 +344,22 @@ static void mptcp_pm_add_timer(struct timer_list *timer) add_timer); struct mptcp_sock *msk = entry->sock; struct sock *sk = (struct sock *)msk; - unsigned int timeout; + unsigned int timeout = 0; pr_debug("msk=%p\n", msk); - if (!msk) - return; + bh_lock_sock(sk); + if (unlikely(inet_sk_state_load(sk) == TCP_CLOSE)) + goto out; - if (inet_sk_state_load(sk) == TCP_CLOSE) - return; - - if (!entry->addr.id) - return; - - if (mptcp_pm_should_add_signal_addr(msk)) { - sk_reset_timer(sk, timer, jiffies + TCP_RTO_MAX / 8); + if (sock_owned_by_user(sk)) { + /* Try again later. */ + timeout = HZ / 20; goto out; } timeout = mptcp_adjust_add_addr_timeout(msk); - if (!timeout) + if (!timeout || mptcp_pm_should_add_signal_addr(msk)) goto out; spin_lock_bh(&msk->pm.lock); @@ -359,8 +372,9 @@ static void mptcp_pm_add_timer(struct timer_list *timer) } if (entry->retrans_times < ADD_ADDR_RETRANS_MAX) - sk_reset_timer(sk, timer, - jiffies + (timeout << entry->retrans_times)); + timeout <<= entry->retrans_times; + else + timeout = 0; spin_unlock_bh(&msk->pm.lock); @@ -368,7 +382,13 @@ static void mptcp_pm_add_timer(struct timer_list *timer) mptcp_pm_subflow_established(msk); out: - __sock_put(sk); + if (timeout) + sk_reset_timer(sk, timer, jiffies + timeout); + else + /* if sock_put calls sk_free: avoid waiting for this timer */ + entry->timer_done = true; + bh_unlock_sock(sk); + sock_put(sk); } struct mptcp_pm_add_entry * @@ -431,6 +451,7 @@ bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk, timer_setup(&add_entry->add_timer, mptcp_pm_add_timer, 0); reset_timer: + add_entry->timer_done = false; timeout = mptcp_adjust_add_addr_timeout(msk); if (timeout) sk_reset_timer(sk, &add_entry->add_timer, jiffies + timeout); @@ -451,7 +472,8 @@ static void mptcp_pm_free_anno_list(struct mptcp_sock *msk) spin_unlock_bh(&msk->pm.lock); list_for_each_entry_safe(entry, tmp, &free_list, list) { - sk_stop_timer_sync(sk, &entry->add_timer); + if (!entry->timer_done) + sk_stop_timer_sync(sk, &entry->add_timer); kfree_rcu(entry, rcu); } } diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c index 0ebf43be9939..fc818b63752e 100644 --- a/net/mptcp/pm_kernel.c +++ b/net/mptcp/pm_kernel.c @@ -347,6 +347,8 @@ static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk) /* check first for announce */ if (msk->pm.add_addr_signaled < endp_signal_max) { + u8 endp_id; + /* due to racing events on both ends we can reach here while * previous add address is still running: if we invoke now * mptcp_pm_announce_addr(), that will fail and the @@ -360,19 +362,20 @@ static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk) if (!select_signal_address(pernet, msk, &local)) goto subflow; + /* Special case for ID0: set the correct ID */ + endp_id = local.addr.id; + if (endp_id == msk->mpc_endpoint_id) + local.addr.id = 0; + /* If the alloc fails, we are on memory pressure, not worth * continuing, and trying to create subflows. */ if (!mptcp_pm_alloc_anno_list(msk, &local.addr)) return; - __clear_bit(local.addr.id, msk->pm.id_avail_bitmap); + __clear_bit(endp_id, msk->pm.id_avail_bitmap); msk->pm.add_addr_signaled++; - /* Special case for ID0: set the correct ID */ - if (local.addr.id == msk->mpc_endpoint_id) - local.addr.id = 0; - mptcp_pm_announce_addr(msk, &local.addr, false); mptcp_pm_addr_send_ack(msk); @@ -1278,6 +1281,7 @@ static void __reset_counters(struct pm_nl_pernet *pernet) WRITE_ONCE(pernet->endp_signal_max, 0); WRITE_ONCE(pernet->endp_subflow_max, 0); WRITE_ONCE(pernet->endp_laminar_max, 0); + WRITE_ONCE(pernet->endp_fullmesh_max, 0); pernet->endpoints = 0; } diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index fbffd3a43fe8..4546a8b09884 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3302,7 +3302,8 @@ bool __mptcp_close(struct sock *sk, long timeout) goto cleanup; } - if (mptcp_data_avail(msk) || timeout < 0) { + if (mptcp_data_avail(msk) || timeout < 0 || + (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime)) { /* If the msk has read data, or the caller explicitly ask it, * do the MPTCP equivalent of TCP reset, aka MPTCP fastclose */ @@ -3594,7 +3595,6 @@ struct sock *mptcp_sk_clone_init(const struct sock *sk, * uses the correct data */ mptcp_copy_inaddrs(nsk, ssk); - __mptcp_propagate_sndbuf(nsk, ssk); mptcp_rcv_space_init(msk, ssk); msk->rcvq_space.time = mptcp_stamp(); @@ -4252,6 +4252,7 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock, mptcp_graft_subflows(newsk); mptcp_rps_record_subflows(msk); + __mptcp_propagate_sndbuf(newsk, mptcp_subflow_tcp_sock(subflow)); /* Do late cleanup for the first subflow as necessary. Also * deal with bad peers not doing a complete shutdown. diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index de90a2897d2d..1cf608e7357b 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -159,10 +159,10 @@ static int mptcp_setsockopt_sol_socket_tstamp(struct mptcp_sock *msk, int optnam lock_sock(sk); mptcp_for_each_subflow(msk, subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); - bool slow = lock_sock_fast(ssk); - sock_set_timestamp(sk, optname, !!val); - unlock_sock_fast(ssk, slow); + lock_sock(ssk); + sock_set_timestamp(ssk, optname, !!val); + release_sock(ssk); } release_sock(sk); @@ -235,10 +235,10 @@ static int mptcp_setsockopt_sol_socket_timestamping(struct mptcp_sock *msk, mptcp_for_each_subflow(msk, subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); - bool slow = lock_sock_fast(ssk); - sock_set_timestamping(sk, optname, timestamping); - unlock_sock_fast(ssk, slow); + lock_sock(ssk); + sock_set_timestamping(ssk, optname, timestamping); + release_sock(ssk); } release_sock(sk); @@ -812,6 +812,10 @@ static int mptcp_setsockopt_all_sf(struct mptcp_sock *msk, int level, if (ret) break; } + + if (!ret) + sockopt_seq_inc(msk); + return ret; } diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index e2cb9d23e4a0..d562e149606f 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -581,7 +581,7 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb) subflow->backup); if (!subflow_thmac_valid(subflow)) { - MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_JOINACKMAC); + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_JOINSYNACKMAC); subflow->reset_reason = MPTCP_RST_EMPTCP; goto do_reset; } @@ -908,7 +908,7 @@ create_child: if (!subflow_hmac_valid(subflow_req, &mp_opt)) { SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINACKMAC); - subflow_add_reset_reason(skb, MPTCP_RST_EPROHIBIT); + subflow_add_reset_reason(skb, MPTCP_RST_EMPTCP); goto dispose_child; } diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 2082bfb2d93c..9ea6b4fa78bf 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -267,27 +267,20 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) hash_key2 = hash_key; use2 = false; } + conn_tab_lock(t, cp, hash_key, hash_key2, use2, true /* new_hash */, &head, &head2); - spin_lock(&cp->lock); - if (!(cp->flags & IP_VS_CONN_F_HASHED)) { - cp->flags |= IP_VS_CONN_F_HASHED; - WRITE_ONCE(cp->hn0.hash_key, hash_key); - WRITE_ONCE(cp->hn1.hash_key, hash_key2); - refcount_inc(&cp->refcnt); - hlist_bl_add_head_rcu(&cp->hn0.node, head); - if (use2) - hlist_bl_add_head_rcu(&cp->hn1.node, head2); - ret = 1; - } else { - pr_err("%s(): request for already hashed, called from %pS\n", - __func__, __builtin_return_address(0)); - ret = 0; - } + cp->flags |= IP_VS_CONN_F_HASHED; + WRITE_ONCE(cp->hn0.hash_key, hash_key); + WRITE_ONCE(cp->hn1.hash_key, hash_key2); + refcount_inc(&cp->refcnt); + hlist_bl_add_head_rcu(&cp->hn0.node, head); + if (use2) + hlist_bl_add_head_rcu(&cp->hn1.node, head2); - spin_unlock(&cp->lock); conn_tab_unlock(head, head2); + ret = 1; /* Schedule resizing if load increases */ if (atomic_read(&ipvs->conn_count) > t->u_thresh && @@ -321,7 +314,6 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp) conn_tab_lock(t, cp, hash_key, hash_key2, use2, false /* new_hash */, &head, &head2); - spin_lock(&cp->lock); if (cp->flags & IP_VS_CONN_F_HASHED) { /* Decrease refcnt and unlink conn only if we are last user */ @@ -334,7 +326,6 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp) } } - spin_unlock(&cp->lock); conn_tab_unlock(head, head2); rcu_read_unlock(); @@ -637,6 +628,7 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport) struct ip_vs_conn_hnode *hn; u32 hash_key, hash_key_new; struct ip_vs_conn_param p; + bool by_me = false; int ntbl; int dir; @@ -664,8 +656,16 @@ retry: t = rcu_dereference(t->new_tbl); ntbl++; /* We are lost? */ - if (ntbl >= 2) + if (ntbl >= 2) { + spin_lock_bh(&cp->lock); + if (cp->flags & IP_VS_CONN_F_NO_CPORT && by_me) + cp->cport = 0; + /* hn1 will be rehashed on next packet */ + spin_unlock_bh(&cp->lock); + IP_VS_ERR_RL("%s(): Too many ht changes for dir %d\n", + __func__, dir); return; + } } /* Rehashing during resize? Use the recent table for adds */ @@ -683,10 +683,13 @@ retry: if (head > head2 && t == t2) swap(head, head2); + /* Protect the cp->flags modification */ + spin_lock_bh(&cp->lock); + /* Lock seqcount only for the old bucket, even if we are on new table * because it affects the del operation, not the adding. */ - spin_lock_bh(&t->lock[hash_key & t->lock_mask].l); + spin_lock(&t->lock[hash_key & t->lock_mask].l); preempt_disable_nested(); write_seqcount_begin(&t->seqc[hash_key & t->seqc_mask]); @@ -704,14 +707,23 @@ retry: hlist_bl_unlock(head); write_seqcount_end(&t->seqc[hash_key & t->seqc_mask]); preempt_enable_nested(); - spin_unlock_bh(&t->lock[hash_key & t->lock_mask].l); + spin_unlock(&t->lock[hash_key & t->lock_mask].l); + spin_unlock_bh(&cp->lock); hash_key = hash_key_new; goto retry; } - spin_lock(&cp->lock); - if ((cp->flags & IP_VS_CONN_F_NO_CPORT) && - (cp->flags & IP_VS_CONN_F_HASHED)) { + /* Fill cport once, even if multiple packets try to do it */ + if (cp->flags & IP_VS_CONN_F_NO_CPORT && (!cp->cport || by_me)) { + /* If we race with resizing make sure cport is set for dir 1 */ + if (!cp->cport) { + cp->cport = cport; + by_me = true; + } + if (!dir) { + atomic_dec(&ipvs->no_cport_conns[af_id]); + cp->flags &= ~IP_VS_CONN_F_NO_CPORT; + } /* We do not recalc hash_key_r under lock, we assume the * parameters in cp do not change, i.e. cport is * the only possible change. @@ -726,21 +738,17 @@ retry: hlist_bl_del_rcu(&hn->node); hlist_bl_add_head_rcu(&hn->node, head_new); } - if (!dir) { - atomic_dec(&ipvs->no_cport_conns[af_id]); - cp->flags &= ~IP_VS_CONN_F_NO_CPORT; - cp->cport = cport; - } } - spin_unlock(&cp->lock); if (head != head2) hlist_bl_unlock(head2); hlist_bl_unlock(head); write_seqcount_end(&t->seqc[hash_key & t->seqc_mask]); preempt_enable_nested(); - spin_unlock_bh(&t->lock[hash_key & t->lock_mask].l); - if (dir--) + spin_unlock(&t->lock[hash_key & t->lock_mask].l); + + spin_unlock_bh(&cp->lock); + if (dir-- && by_me) goto next_dir; } @@ -1835,7 +1843,7 @@ static void ip_vs_conn_flush(struct netns_ipvs *ipvs) if (!rcu_dereference_protected(ipvs->conn_tab, 1)) return; - cancel_delayed_work_sync(&ipvs->conn_resize_work); + disable_delayed_work_sync(&ipvs->conn_resize_work); if (!atomic_read(&ipvs->conn_count)) goto unreg; diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index f5b7a2047291..d40b404c1bf6 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -237,7 +237,7 @@ int ip_vs_rht_desired_size(struct netns_ipvs *ipvs, struct ip_vs_rht *t, int n, { if (!t) return 1 << min_bits; - n = roundup_pow_of_two(n); + n = n > 0 ? roundup_pow_of_two(n) : 1; if (lfactor < 0) { int factor = min(-lfactor, max_bits); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 6632daa87ded..c7c7f6a7a9f6 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -261,12 +261,28 @@ static void est_reload_work_handler(struct work_struct *work) if (!kd) continue; /* New config ? Stop kthread tasks */ - if (genid != genid_done) - ip_vs_est_kthread_stop(kd); + if (genid != genid_done) { + if (!id) { + /* Only we can stop kt 0 but not under mutex */ + mutex_unlock(&ipvs->est_mutex); + ip_vs_est_kthread_stop(kd); + mutex_lock(&ipvs->est_mutex); + if (!READ_ONCE(ipvs->enable)) + goto unlock; + /* kd for kt 0 is never destroyed */ + } else { + ip_vs_est_kthread_stop(kd); + } + } if (!kd->task && !ip_vs_est_stopped(ipvs)) { + bool start; + /* Do not start kthreads above 0 in calc phase */ - if ((!id || !ipvs->est_calc_phase) && - ip_vs_est_kthread_start(ipvs, kd) < 0) + if (id) + start = !ipvs->est_calc_phase; + else + start = kd->needed; + if (start && ip_vs_est_kthread_start(ipvs, kd) < 0) repeat = true; } } @@ -1102,6 +1118,24 @@ out: return dest; } +/* Put destination in trash */ +static void ip_vs_trash_put_dest(struct netns_ipvs *ipvs, + struct ip_vs_dest *dest, unsigned long istart, + bool cleanup) +{ + spin_lock_bh(&ipvs->dest_trash_lock); + IP_VS_DBG_BUF(3, "Moving dest %s:%u into trash, dest->refcnt=%d\n", + IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port), + refcount_read(&dest->refcnt)); + if (list_empty(&ipvs->dest_trash) && !cleanup) + mod_timer(&ipvs->dest_trash_timer, + jiffies + (IP_VS_DEST_TRASH_PERIOD >> 1)); + /* dest lives in trash with reference */ + list_add(&dest->t_list, &ipvs->dest_trash); + dest->idle_start = istart; + spin_unlock_bh(&ipvs->dest_trash_lock); +} + static void ip_vs_dest_rcu_free(struct rcu_head *head) { struct ip_vs_dest *dest; @@ -1461,9 +1495,12 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) ntohs(dest->vport)); ret = ip_vs_start_estimator(svc->ipvs, &dest->stats); + /* On error put back dest into the trash */ if (ret < 0) - return ret; - __ip_vs_update_dest(svc, dest, udest, 1); + ip_vs_trash_put_dest(svc->ipvs, dest, dest->idle_start, + false); + else + __ip_vs_update_dest(svc, dest, udest, 1); } else { /* * Allocate and initialize the dest structure @@ -1533,17 +1570,7 @@ static void __ip_vs_del_dest(struct netns_ipvs *ipvs, struct ip_vs_dest *dest, */ ip_vs_rs_unhash(dest); - spin_lock_bh(&ipvs->dest_trash_lock); - IP_VS_DBG_BUF(3, "Moving dest %s:%u into trash, dest->refcnt=%d\n", - IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port), - refcount_read(&dest->refcnt)); - if (list_empty(&ipvs->dest_trash) && !cleanup) - mod_timer(&ipvs->dest_trash_timer, - jiffies + (IP_VS_DEST_TRASH_PERIOD >> 1)); - /* dest lives in trash with reference */ - list_add(&dest->t_list, &ipvs->dest_trash); - dest->idle_start = 0; - spin_unlock_bh(&ipvs->dest_trash_lock); + ip_vs_trash_put_dest(ipvs, dest, 0, cleanup); /* Queue up delayed work to expire all no destination connections. * No-op when CONFIG_SYSCTL is disabled. @@ -1812,11 +1839,16 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, *svc_p = svc; if (!READ_ONCE(ipvs->enable)) { + mutex_lock(&ipvs->est_mutex); + /* Now there is a service - full throttle */ WRITE_ONCE(ipvs->enable, 1); + ipvs->est_max_threads = ip_vs_est_max_threads(ipvs); + /* Start estimation for first time */ - ip_vs_est_reload_start(ipvs); + ip_vs_est_reload_start(ipvs, true); + mutex_unlock(&ipvs->est_mutex); } return 0; @@ -2032,6 +2064,9 @@ static int ip_vs_del_service(struct ip_vs_service *svc) cancel_delayed_work_sync(&ipvs->svc_resize_work); if (t) { rcu_assign_pointer(ipvs->svc_table, NULL); + /* Inform readers that table is removed */ + smp_mb__before_atomic(); + atomic_inc(&ipvs->svc_table_changes); while (1) { p = rcu_dereference_protected(t->new_tbl, 1); call_rcu(&t->rcu_head, ip_vs_rht_rcu_free); @@ -2078,6 +2113,9 @@ static int ip_vs_flush(struct netns_ipvs *ipvs, bool cleanup) t = rcu_dereference_protected(ipvs->svc_table, 1); if (t) { rcu_assign_pointer(ipvs->svc_table, NULL); + /* Inform readers that table is removed */ + smp_mb__before_atomic(); + atomic_inc(&ipvs->svc_table_changes); while (1) { p = rcu_dereference_protected(t->new_tbl, 1); call_rcu(&t->rcu_head, ip_vs_rht_rcu_free); @@ -2086,6 +2124,11 @@ static int ip_vs_flush(struct netns_ipvs *ipvs, bool cleanup) t = p; } } + /* Stop the tot_stats estimator early under service_mutex + * to avoid locking it again later. + */ + if (cleanup) + ip_vs_stop_estimator_tot_stats(ipvs); return 0; } @@ -2331,7 +2374,7 @@ static int ipvs_proc_est_cpumask_set(const struct ctl_table *table, /* est_max_threads may depend on cpulist size */ ipvs->est_max_threads = ip_vs_est_max_threads(ipvs); ipvs->est_calc_phase = 1; - ip_vs_est_reload_start(ipvs); + ip_vs_est_reload_start(ipvs, true); unlock: mutex_unlock(&ipvs->est_mutex); @@ -2351,11 +2394,14 @@ static int ipvs_proc_est_cpumask_get(const struct ctl_table *table, mutex_lock(&ipvs->est_mutex); - if (ipvs->est_cpulist_valid) - mask = *valp; - else - mask = (struct cpumask *)housekeeping_cpumask(HK_TYPE_KTHREAD); - ret = scnprintf(buffer, size, "%*pbl\n", cpumask_pr_args(mask)); + /* HK_TYPE_KTHREAD cpumask needs RCU protection */ + scoped_guard(rcu) { + if (ipvs->est_cpulist_valid) + mask = *valp; + else + mask = (struct cpumask *)housekeeping_cpumask(HK_TYPE_KTHREAD); + ret = scnprintf(buffer, size, "%*pbl\n", cpumask_pr_args(mask)); + } mutex_unlock(&ipvs->est_mutex); @@ -2411,7 +2457,7 @@ static int ipvs_proc_est_nice(const struct ctl_table *table, int write, mutex_lock(&ipvs->est_mutex); if (*valp != val) { *valp = val; - ip_vs_est_reload_start(ipvs); + ip_vs_est_reload_start(ipvs, true); } mutex_unlock(&ipvs->est_mutex); } @@ -2438,7 +2484,7 @@ static int ipvs_proc_run_estimation(const struct ctl_table *table, int write, mutex_lock(&ipvs->est_mutex); if (*valp != val) { *valp = val; - ip_vs_est_reload_start(ipvs); + ip_vs_est_reload_start(ipvs, true); } mutex_unlock(&ipvs->est_mutex); } @@ -2463,7 +2509,7 @@ static int ipvs_proc_conn_lfactor(const struct ctl_table *table, int write, if (val < -8 || val > 8) { ret = -EINVAL; } else { - *valp = val; + WRITE_ONCE(*valp, val); if (rcu_access_pointer(ipvs->conn_tab)) mod_delayed_work(system_unbound_wq, &ipvs->conn_resize_work, 0); @@ -2490,10 +2536,16 @@ static int ipvs_proc_svc_lfactor(const struct ctl_table *table, int write, if (val < -8 || val > 8) { ret = -EINVAL; } else { - *valp = val; - if (rcu_access_pointer(ipvs->svc_table)) + mutex_lock(&ipvs->service_mutex); + WRITE_ONCE(*valp, val); + /* Make sure the services are present */ + if (rcu_access_pointer(ipvs->svc_table) && + READ_ONCE(ipvs->enable) && + !test_bit(IP_VS_WORK_SVC_NORESIZE, + &ipvs->work_flags)) mod_delayed_work(system_unbound_wq, &ipvs->svc_resize_work, 0); + mutex_unlock(&ipvs->service_mutex); } } return ret; @@ -3004,7 +3056,8 @@ static int ip_vs_status_show(struct seq_file *seq, void *v) int old_gen, new_gen; u32 counts[8]; u32 bucket; - int count; + u32 count; + int loops; u32 sum1; u32 sum; int i; @@ -3020,6 +3073,7 @@ static int ip_vs_status_show(struct seq_file *seq, void *v) if (!atomic_read(&ipvs->conn_count)) goto after_conns; old_gen = atomic_read(&ipvs->conn_tab_changes); + loops = 0; repeat_conn: smp_rmb(); /* ipvs->conn_tab and conn_tab_changes */ @@ -3032,8 +3086,11 @@ repeat_conn: resched_score++; ip_vs_rht_walk_bucket_rcu(t, bucket, head) { count = 0; - hlist_bl_for_each_entry_rcu(hn, e, head, node) + hlist_bl_for_each_entry_rcu(hn, e, head, node) { count++; + if (count >= ARRAY_SIZE(counts) - 1) + break; + } } resched_score += count; if (resched_score >= 100) { @@ -3042,37 +3099,41 @@ repeat_conn: new_gen = atomic_read(&ipvs->conn_tab_changes); /* New table installed ? */ if (old_gen != new_gen) { + /* Too many changes? */ + if (++loops >= 5) + goto after_conns; old_gen = new_gen; goto repeat_conn; } } - counts[min(count, (int)ARRAY_SIZE(counts) - 1)]++; + counts[count]++; } } for (sum = 0, i = 0; i < ARRAY_SIZE(counts); i++) sum += counts[i]; sum1 = sum - counts[0]; - seq_printf(seq, "Conn buckets empty:\t%u (%lu%%)\n", - counts[0], (unsigned long)counts[0] * 100 / max(sum, 1U)); + seq_printf(seq, "Conn buckets empty:\t%u (%llu%%)\n", + counts[0], div_u64((u64)counts[0] * 100U, max(sum, 1U))); for (i = 1; i < ARRAY_SIZE(counts); i++) { if (!counts[i]) continue; - seq_printf(seq, "Conn buckets len-%d:\t%u (%lu%%)\n", + seq_printf(seq, "Conn buckets len-%d:\t%u (%llu%%)\n", i, counts[i], - (unsigned long)counts[i] * 100 / max(sum1, 1U)); + div_u64((u64)counts[i] * 100U, max(sum1, 1U))); } after_conns: t = rcu_dereference(ipvs->svc_table); count = ip_vs_get_num_services(ipvs); - seq_printf(seq, "Services:\t%d\n", count); + seq_printf(seq, "Services:\t%u\n", count); seq_printf(seq, "Service buckets:\t%d (%d bits, lfactor %d)\n", t ? t->size : 0, t ? t->bits : 0, t ? t->lfactor : 0); if (!count) goto after_svc; old_gen = atomic_read(&ipvs->svc_table_changes); + loops = 0; repeat_svc: smp_rmb(); /* ipvs->svc_table and svc_table_changes */ @@ -3086,8 +3147,11 @@ repeat_svc: ip_vs_rht_walk_bucket_rcu(t, bucket, head) { count = 0; hlist_bl_for_each_entry_rcu(svc, e, head, - s_list) + s_list) { count++; + if (count >= ARRAY_SIZE(counts) - 1) + break; + } } resched_score += count; if (resched_score >= 100) { @@ -3096,24 +3160,27 @@ repeat_svc: new_gen = atomic_read(&ipvs->svc_table_changes); /* New table installed ? */ if (old_gen != new_gen) { + /* Too many changes? */ + if (++loops >= 5) + goto after_svc; old_gen = new_gen; goto repeat_svc; } } - counts[min(count, (int)ARRAY_SIZE(counts) - 1)]++; + counts[count]++; } } for (sum = 0, i = 0; i < ARRAY_SIZE(counts); i++) sum += counts[i]; sum1 = sum - counts[0]; - seq_printf(seq, "Service buckets empty:\t%u (%lu%%)\n", - counts[0], (unsigned long)counts[0] * 100 / max(sum, 1U)); + seq_printf(seq, "Service buckets empty:\t%u (%llu%%)\n", + counts[0], div_u64((u64)counts[0] * 100U, max(sum, 1U))); for (i = 1; i < ARRAY_SIZE(counts); i++) { if (!counts[i]) continue; - seq_printf(seq, "Service buckets len-%d:\t%u (%lu%%)\n", + seq_printf(seq, "Service buckets len-%d:\t%u (%llu%%)\n", i, counts[i], - (unsigned long)counts[i] * 100 / max(sum1, 1U)); + div_u64((u64)counts[i] * 100U, max(sum1, 1U))); } after_svc: @@ -4967,7 +5034,14 @@ static void __net_exit ip_vs_control_net_cleanup_sysctl(struct netns_ipvs *ipvs) cancel_delayed_work_sync(&ipvs->defense_work); cancel_work_sync(&ipvs->defense_work.work); unregister_net_sysctl_table(ipvs->sysctl_hdr); - ip_vs_stop_estimator(ipvs, &ipvs->tot_stats->s); + if (ipvs->tot_stats->s.est.ktid != -2) { + /* Not stopped yet? This happens only on netns init error and + * we even do not need to lock the service_mutex for this case. + */ + mutex_lock(&ipvs->service_mutex); + ip_vs_stop_estimator(ipvs, &ipvs->tot_stats->s); + mutex_unlock(&ipvs->service_mutex); + } if (ipvs->est_cpulist_valid) free_cpumask_var(ipvs->sysctl_est_cpulist); @@ -5039,7 +5113,7 @@ int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs) ipvs->net->proc_net, ip_vs_stats_percpu_show, NULL)) goto err_percpu; - if (!proc_create_net_single("ip_vs_status", 0, ipvs->net->proc_net, + if (!proc_create_net_single("ip_vs_status", 0440, ipvs->net->proc_net, ip_vs_status_show, NULL)) goto err_status; #endif diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 433ba3cab58c..ab09f5182951 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -68,6 +68,11 @@ and the limit of estimators per kthread - est_add_ktid: ktid where to add new ests, can point to empty slot where we should add kt data + - data protected by service_mutex: est_temp_list, est_add_ktid, + est_kt_count(R/W), est_kt_arr(R/W), est_genid_done, kd->needed(R/W) + - data protected by est_mutex: est_genid, est_max_threads, sysctl_est_cpulist, + est_cpulist_valid, sysctl_est_nice, est_stopped, sysctl_run_estimation, + est_kt_count(R), est_kt_arr(R), kd->needed(R), kd->task (id > 0) */ static struct lock_class_key __ipvs_est_key; @@ -227,14 +232,17 @@ static int ip_vs_estimation_kthread(void *data) } /* Schedule stop/start for kthread tasks */ -void ip_vs_est_reload_start(struct netns_ipvs *ipvs) +void ip_vs_est_reload_start(struct netns_ipvs *ipvs, bool restart) { + lockdep_assert_held(&ipvs->est_mutex); + /* Ignore reloads before first service is added */ if (!READ_ONCE(ipvs->enable)) return; ip_vs_est_stopped_recalc(ipvs); - /* Bump the kthread configuration genid */ - atomic_inc(&ipvs->est_genid); + /* Bump the kthread configuration genid if stopping is requested */ + if (restart) + atomic_inc(&ipvs->est_genid); queue_delayed_work(system_long_wq, &ipvs->est_reload_work, 0); } @@ -304,12 +312,17 @@ static int ip_vs_est_add_kthread(struct netns_ipvs *ipvs) void *arr = NULL; int i; - if ((unsigned long)ipvs->est_kt_count >= ipvs->est_max_threads && - READ_ONCE(ipvs->enable) && ipvs->est_max_threads) - return -EINVAL; - mutex_lock(&ipvs->est_mutex); + /* Allow kt 0 data to be created before the services are added + * and limit the kthreads when services are present. + */ + if ((unsigned long)ipvs->est_kt_count >= ipvs->est_max_threads && + READ_ONCE(ipvs->enable) && ipvs->est_max_threads) { + ret = -EINVAL; + goto out; + } + for (i = 0; i < id; i++) { if (!ipvs->est_kt_arr[i]) break; @@ -333,6 +346,7 @@ static int ip_vs_est_add_kthread(struct netns_ipvs *ipvs) kd->est_timer = jiffies; kd->id = id; ip_vs_est_set_params(ipvs, kd); + kd->needed = 1; /* Pre-allocate stats used in calc phase */ if (!id && !kd->calc_stats) { @@ -341,12 +355,8 @@ static int ip_vs_est_add_kthread(struct netns_ipvs *ipvs) goto out; } - /* Start kthread tasks only when services are present */ - if (READ_ONCE(ipvs->enable) && !ip_vs_est_stopped(ipvs)) { - ret = ip_vs_est_kthread_start(ipvs, kd); - if (ret < 0) - goto out; - } + /* Request kthread to be started */ + ip_vs_est_reload_start(ipvs, false); if (arr) ipvs->est_kt_count++; @@ -482,12 +492,11 @@ out: /* Start estimation for stats */ int ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats) { + struct ip_vs_est_kt_data *kd = ipvs->est_kt_count > 0 ? + ipvs->est_kt_arr[0] : NULL; struct ip_vs_estimator *est = &stats->est; int ret; - if (!ipvs->est_max_threads && READ_ONCE(ipvs->enable)) - ipvs->est_max_threads = ip_vs_est_max_threads(ipvs); - est->ktid = -1; est->ktrow = IPVS_EST_NTICKS - 1; /* Initial delay */ @@ -496,8 +505,15 @@ int ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats) * will not allocate much memory, just for kt 0. */ ret = 0; - if (!ipvs->est_kt_count || !ipvs->est_kt_arr[0]) + if (!kd) { ret = ip_vs_est_add_kthread(ipvs); + } else if (!kd->needed) { + mutex_lock(&ipvs->est_mutex); + /* We have job for the kt 0 task */ + kd->needed = 1; + ip_vs_est_reload_start(ipvs, true); + mutex_unlock(&ipvs->est_mutex); + } if (ret >= 0) hlist_add_head(&est->list, &ipvs->est_temp_list); else @@ -578,16 +594,14 @@ void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats) } end_kt0: - /* kt 0 is freed after all other kthreads and chains are empty */ + /* kt 0 task is stopped after all other kt slots and chains are empty */ if (ipvs->est_kt_count == 1 && hlist_empty(&ipvs->est_temp_list)) { kd = ipvs->est_kt_arr[0]; - if (!kd || !kd->est_count) { + if (kd && !kd->est_count) { mutex_lock(&ipvs->est_mutex); - if (kd) { - ip_vs_est_kthread_destroy(kd); - ipvs->est_kt_arr[0] = NULL; - } - ipvs->est_kt_count--; + /* Keep the kt0 data but request kthread_stop */ + kd->needed = 0; + ip_vs_est_reload_start(ipvs, true); mutex_unlock(&ipvs->est_mutex); ipvs->est_add_ktid = 0; } @@ -647,9 +661,9 @@ static int ip_vs_est_calc_limits(struct netns_ipvs *ipvs, int *chain_max) u64 val; INIT_HLIST_HEAD(&chain); - mutex_lock(&ipvs->service_mutex); + mutex_lock(&ipvs->est_mutex); kd = ipvs->est_kt_arr[0]; - mutex_unlock(&ipvs->service_mutex); + mutex_unlock(&ipvs->est_mutex); s = kd ? kd->calc_stats : NULL; if (!s) goto out; @@ -748,16 +762,16 @@ static void ip_vs_est_calc_phase(struct netns_ipvs *ipvs) if (!ip_vs_est_calc_limits(ipvs, &chain_max)) return; - mutex_lock(&ipvs->service_mutex); - /* Stop all other tasks, so that we can immediately move the * estimators to est_temp_list without RCU grace period */ mutex_lock(&ipvs->est_mutex); for (id = 1; id < ipvs->est_kt_count; id++) { /* netns clean up started, abort */ - if (!READ_ONCE(ipvs->enable)) - goto unlock2; + if (kthread_should_stop() || !READ_ONCE(ipvs->enable)) { + mutex_unlock(&ipvs->est_mutex); + return; + } kd = ipvs->est_kt_arr[id]; if (!kd) continue; @@ -765,9 +779,11 @@ static void ip_vs_est_calc_phase(struct netns_ipvs *ipvs) } mutex_unlock(&ipvs->est_mutex); + mutex_lock(&ipvs->service_mutex); + /* Move all estimators to est_temp_list but carefully, * all estimators and kthread data can be released while - * we reschedule. Even for kthread 0. + * we reschedule. */ step = 0; @@ -849,9 +865,7 @@ walk_chain: ip_vs_stop_estimator(ipvs, stats); /* Tasks are stopped, move without RCU grace period */ est->ktid = -1; - est->ktrow = row - kd->est_row; - if (est->ktrow < 0) - est->ktrow += IPVS_EST_NTICKS; + est->ktrow = delay; hlist_add_head(&est->list, &ipvs->est_temp_list); /* kd freed ? */ if (last) @@ -889,7 +903,6 @@ end_dequeue: if (genid == atomic_read(&ipvs->est_genid)) ipvs->est_calc_phase = 0; -unlock2: mutex_unlock(&ipvs->est_mutex); unlock: diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 0fb5162992e5..ce542ed4b013 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -102,6 +102,18 @@ __ip_vs_dst_check(struct ip_vs_dest *dest) return dest_dst; } +/* Based on ip_exceeds_mtu(). */ +static bool ip_vs_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu) +{ + if (skb->len <= mtu) + return false; + + if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu)) + return false; + + return true; +} + static inline bool __mtu_check_toobig_v6(const struct sk_buff *skb, u32 mtu) { @@ -111,10 +123,9 @@ __mtu_check_toobig_v6(const struct sk_buff *skb, u32 mtu) */ if (IP6CB(skb)->frag_max_size > mtu) return true; /* largest fragment violate MTU */ - } - else if (skb->len > mtu && !skb_is_gso(skb)) { + } else if (ip_vs_exceeds_mtu(skb, mtu)) return true; /* Packet size violate MTU size */ - } + return false; } @@ -232,7 +243,7 @@ static inline bool ensure_mtu_is_adequate(struct netns_ipvs *ipvs, int skb_af, return true; if (unlikely(ip_hdr(skb)->frag_off & htons(IP_DF) && - skb->len > mtu && !skb_is_gso(skb) && + ip_vs_exceeds_mtu(skb, mtu) && !ip_vs_iph_icmp(ipvsh))) { icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c index 4f39bf7c843f..75e53fde6b29 100644 --- a/net/netfilter/nf_conntrack_broadcast.c +++ b/net/netfilter/nf_conntrack_broadcast.c @@ -72,6 +72,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, exp->flags = NF_CT_EXPECT_PERMANENT; exp->class = NF_CT_EXPECT_CLASS_DEFAULT; rcu_assign_pointer(exp->helper, helper); + rcu_assign_pointer(exp->assign_helper, NULL); write_pnet(&exp->net, net); #ifdef CONFIG_NF_CONNTRACK_ZONES exp->zone = ct->zone; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index b08189226320..8ba5b22a1eef 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1811,14 +1811,17 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, spin_lock_bh(&nf_conntrack_expect_lock); exp = nf_ct_find_expectation(net, zone, tuple, !tmpl || nf_ct_is_confirmed(tmpl)); if (exp) { + struct nf_conntrack_helper *assign_helper; + /* Welcome, Mr. Bond. We've been expecting you... */ __set_bit(IPS_EXPECTED_BIT, &ct->status); /* exp->master safe, refcnt bumped in nf_ct_find_expectation */ ct->master = exp->master; - if (exp->helper) { + assign_helper = rcu_dereference(exp->assign_helper); + if (assign_helper) { help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); if (help) - rcu_assign_pointer(help->helper, exp->helper); + rcu_assign_pointer(help->helper, assign_helper); } #ifdef CONFIG_NF_CONNTRACK_MARK diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 24d0576d84b7..8e943efbdf0a 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -344,6 +344,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, helper = rcu_dereference(help->helper); rcu_assign_pointer(exp->helper, helper); + rcu_assign_pointer(exp->assign_helper, NULL); write_pnet(&exp->net, net); #ifdef CONFIG_NF_CONNTRACK_ZONES exp->zone = ct->zone; diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 3f5c50455b71..b2fe6554b9cf 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -643,7 +643,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); - rcu_assign_pointer(exp->helper, &nf_conntrack_helper_h245); + rcu_assign_pointer(exp->assign_helper, &nf_conntrack_helper_h245); nathook = rcu_dereference(nfct_h323_nat_hook); if (memcmp(&ct->tuplehash[dir].tuple.src.u3, @@ -767,7 +767,7 @@ static int expect_callforwarding(struct sk_buff *skb, nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); - rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); nathook = rcu_dereference(nfct_h323_nat_hook); if (memcmp(&ct->tuplehash[dir].tuple.src.u3, @@ -1234,7 +1234,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3 : NULL, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); - rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple calls */ nathook = rcu_dereference(nfct_h323_nat_hook); @@ -1306,7 +1306,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct, nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_UDP, NULL, &port); - rcu_assign_pointer(exp->helper, nf_conntrack_helper_ras); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_ras); if (nf_ct_expect_related(exp, 0) == 0) { pr_debug("nf_ct_ras: expect RAS "); @@ -1523,7 +1523,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); exp->flags = NF_CT_EXPECT_PERMANENT; - rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); if (nf_ct_expect_related(exp, 0) == 0) { pr_debug("nf_ct_ras: expect Q.931 "); @@ -1577,7 +1577,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); exp->flags = NF_CT_EXPECT_PERMANENT; - rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); if (nf_ct_expect_related(exp, 0) == 0) { pr_debug("nf_ct_ras: expect Q.931 "); diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index a715304a53d8..b594cd244fe1 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -400,6 +400,11 @@ static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data) this = rcu_dereference_protected(exp->helper, lockdep_is_held(&nf_conntrack_expect_lock)); + if (this == me) + return true; + + this = rcu_dereference_protected(exp->assign_helper, + lockdep_is_held(&nf_conntrack_expect_lock)); return this == me; } diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index eda5fe4a75c8..befa7e83ee49 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2634,6 +2634,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { static struct nf_conntrack_expect * ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct, + const struct nf_conntrack_helper *assign_helper, struct nf_conntrack_tuple *tuple, struct nf_conntrack_tuple *mask); @@ -2860,6 +2861,7 @@ static int ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, u32 portid, u32 report) { + struct nf_conntrack_helper *assign_helper = NULL; struct nlattr *cda[CTA_EXPECT_MAX+1]; struct nf_conntrack_tuple tuple, mask; struct nf_conntrack_expect *exp; @@ -2870,13 +2872,26 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, if (err < 0) return err; + if (!cda[CTA_EXPECT_TUPLE] || !cda[CTA_EXPECT_MASK]) + return -EINVAL; + err = ctnetlink_glue_exp_parse((const struct nlattr * const *)cda, ct, &tuple, &mask); if (err < 0) return err; + if (cda[CTA_EXPECT_HELP_NAME]) { + const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]); + + assign_helper = __nf_conntrack_helper_find(helpname, + nf_ct_l3num(ct), + tuple.dst.protonum); + if (!assign_helper) + return -EOPNOTSUPP; + } + exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct, - &tuple, &mask); + assign_helper, &tuple, &mask); if (IS_ERR(exp)) return PTR_ERR(exp); @@ -3515,6 +3530,7 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr, static struct nf_conntrack_expect * ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, + const struct nf_conntrack_helper *assign_helper, struct nf_conntrack_tuple *tuple, struct nf_conntrack_tuple *mask) { @@ -3568,6 +3584,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, exp->zone = ct->zone; #endif rcu_assign_pointer(exp->helper, helper); + rcu_assign_pointer(exp->assign_helper, assign_helper); exp->tuple = *tuple; exp->mask.src.u3 = mask->src.u3; exp->mask.src.u.all = mask->src.u.all; @@ -3623,7 +3640,7 @@ ctnetlink_create_expect(struct net *net, ct = nf_ct_tuplehash_to_ctrack(h); rcu_read_lock(); - exp = ctnetlink_alloc_expect(cda, ct, &tuple, &mask); + exp = ctnetlink_alloc_expect(cda, ct, NULL, &tuple, &mask); if (IS_ERR(exp)) { err = PTR_ERR(exp); goto err_rcu; diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 645d2c43ebf7..7e10fa65cbdd 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -466,9 +466,13 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, if (!ih) goto out_unlock; - if (ct->proto.sctp.init[dir] && ct->proto.sctp.init[!dir]) - ct->proto.sctp.init[!dir] = 0; - ct->proto.sctp.init[dir] = 1; + /* Do not record INIT matching peer vtag (stale or retransmitted INIT). */ + if (old_state == SCTP_CONNTRACK_NONE || + ct->proto.sctp.vtag[!dir] != ih->init_tag) { + if (ct->proto.sctp.init[dir] && ct->proto.sctp.init[!dir]) + ct->proto.sctp.init[!dir] = 0; + ct->proto.sctp.init[dir] = 1; + } pr_debug("Setting vtag %x for dir %d\n", ih->init_tag, !dir); ct->proto.sctp.vtag[!dir] = ih->init_tag; diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 182cfb119448..e69941f1a101 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -181,6 +181,57 @@ static int sip_parse_addr(const struct nf_conn *ct, const char *cp, return 1; } +/* Parse optional port number after IP address. + * Returns false on malformed input, true otherwise. + * If port is non-NULL, stores parsed port in network byte order. + * If no port is present, sets *port to default SIP port. + */ +static bool sip_parse_port(const char *dptr, const char **endp, + const char *limit, __be16 *port) +{ + unsigned int p = 0; + int len = 0; + + if (dptr >= limit) + return false; + + if (*dptr != ':') { + if (port) + *port = htons(SIP_PORT); + if (endp) + *endp = dptr; + return true; + } + + dptr++; /* skip ':' */ + + while (dptr < limit && isdigit(*dptr)) { + p = p * 10 + (*dptr - '0'); + dptr++; + len++; + if (len > 5) /* max "65535" */ + return false; + } + + if (len == 0) + return false; + + /* reached limit while parsing port */ + if (dptr >= limit) + return false; + + if (p < 1024 || p > 65535) + return false; + + if (port) + *port = htons(p); + + if (endp) + *endp = dptr; + + return true; +} + /* skip ip address. returns its length. */ static int epaddr_len(const struct nf_conn *ct, const char *dptr, const char *limit, int *shift) @@ -193,11 +244,8 @@ static int epaddr_len(const struct nf_conn *ct, const char *dptr, return 0; } - /* Port number */ - if (*dptr == ':') { - dptr++; - dptr += digits_len(ct, dptr, limit, shift); - } + if (!sip_parse_port(dptr, &dptr, limit, NULL)) + return 0; return dptr - aux; } @@ -228,6 +276,51 @@ static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr, return epaddr_len(ct, dptr, limit, shift); } +/* simple_strtoul stops after first non-number character. + * But as we're not dealing with c-strings, we can't rely on + * hitting \r,\n,\0 etc. before moving past end of buffer. + * + * This is a variant of simple_strtoul, but doesn't require + * a c-string. + * + * If value exceeds UINT_MAX, 0 is returned. + */ +static unsigned int sip_strtouint(const char *cp, unsigned int len, char **endp) +{ + const unsigned int max = sizeof("4294967295"); + unsigned int olen = len; + const char *s = cp; + u64 result = 0; + + if (len > max) + len = max; + + while (olen > 0 && isdigit(*s)) { + unsigned int value; + + if (len == 0) + goto err; + + value = *s - '0'; + result = result * 10 + value; + + if (result > UINT_MAX) + goto err; + s++; + len--; + olen--; + } + + if (endp) + *endp = (char *)s; + + return result; +err: + if (endp) + *endp = (char *)cp; + return 0; +} + /* Parse a SIP request line of the form: * * Request-Line = Method SP Request-URI SP SIP-Version CRLF @@ -241,7 +334,6 @@ int ct_sip_parse_request(const struct nf_conn *ct, { const char *start = dptr, *limit = dptr + datalen, *end; unsigned int mlen; - unsigned int p; int shift = 0; /* Skip method and following whitespace */ @@ -267,14 +359,8 @@ int ct_sip_parse_request(const struct nf_conn *ct, if (!sip_parse_addr(ct, dptr, &end, addr, limit, true)) return -1; - if (end < limit && *end == ':') { - end++; - p = simple_strtoul(end, (char **)&end, 10); - if (p < 1024 || p > 65535) - return -1; - *port = htons(p); - } else - *port = htons(SIP_PORT); + if (!sip_parse_port(end, &end, limit, port)) + return -1; if (end == dptr) return 0; @@ -509,7 +595,6 @@ int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr, union nf_inet_addr *addr, __be16 *port) { const char *c, *limit = dptr + datalen; - unsigned int p; int ret; ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen, @@ -520,14 +605,8 @@ int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr, if (!sip_parse_addr(ct, dptr + *matchoff, &c, addr, limit, true)) return -1; - if (*c == ':') { - c++; - p = simple_strtoul(c, (char **)&c, 10); - if (p < 1024 || p > 65535) - return -1; - *port = htons(p); - } else - *port = htons(SIP_PORT); + if (!sip_parse_port(c, &c, limit, port)) + return -1; if (dataoff) *dataoff = c - dptr; @@ -609,7 +688,7 @@ int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, return 0; start += strlen(name); - *val = simple_strtoul(start, &end, 0); + *val = sip_strtouint(start, limit - start, (char **)&end); if (start == end) return -1; if (matchoff && matchlen) { @@ -1064,6 +1143,8 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, mediaoff = sdpoff; for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) { + char *end; + if (ct_sip_get_sdp_header(ct, *dptr, mediaoff, *datalen, SDP_HDR_MEDIA, SDP_HDR_UNSPEC, &mediaoff, &medialen) <= 0) @@ -1079,8 +1160,8 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, mediaoff += t->len; medialen -= t->len; - port = simple_strtoul(*dptr + mediaoff, NULL, 10); - if (port == 0) + port = sip_strtouint(*dptr + mediaoff, *datalen - mediaoff, (char **)&end); + if (port == 0 || *dptr + mediaoff == end) continue; if (port < 1024 || port > 65535) { nf_ct_helper_log(skb, ct, "wrong port %u", port); @@ -1254,7 +1335,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, */ if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES, &matchoff, &matchlen) > 0) - expires = simple_strtoul(*dptr + matchoff, NULL, 10); + expires = sip_strtouint(*dptr + matchoff, *datalen - matchoff, NULL); ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, SIP_HDR_CONTACT, NULL, @@ -1285,6 +1366,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, goto store_cseq; } + helper = rcu_dereference(nfct_help(ct)->helper); + if (!helper) + return NF_DROP; + exp = nf_ct_expect_alloc(ct); if (!exp) { nf_ct_helper_log(skb, ct, "cannot alloc expectation"); @@ -1295,14 +1380,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, if (sip_direct_signalling) saddr = &ct->tuplehash[!dir].tuple.src.u3; - helper = rcu_dereference(nfct_help(ct)->helper); - if (!helper) - return NF_DROP; - nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct), saddr, &daddr, proto, NULL, &port); exp->timeout.expires = sip_timeout * HZ; - rcu_assign_pointer(exp->helper, helper); + rcu_assign_pointer(exp->assign_helper, helper); exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; hooks = rcu_dereference(nf_nat_sip_hooks); @@ -1358,7 +1439,7 @@ static int process_register_response(struct sk_buff *skb, unsigned int protoff, if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES, &matchoff, &matchlen) > 0) - expires = simple_strtoul(*dptr + matchoff, NULL, 10); + expires = sip_strtouint(*dptr + matchoff, *datalen - matchoff, NULL); while (1) { unsigned int c_expires = expires; @@ -1418,10 +1499,12 @@ static int process_sip_response(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct = nf_ct_get(skb, &ctinfo); unsigned int matchoff, matchlen, matchend; unsigned int code, cseq, i; + char *end; if (*datalen < strlen("SIP/2.0 200")) return NF_ACCEPT; - code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10); + code = sip_strtouint(*dptr + strlen("SIP/2.0 "), + *datalen - strlen("SIP/2.0 "), NULL); if (!code) { nf_ct_helper_log(skb, ct, "cannot get code"); return NF_DROP; @@ -1432,8 +1515,8 @@ static int process_sip_response(struct sk_buff *skb, unsigned int protoff, nf_ct_helper_log(skb, ct, "cannot parse cseq"); return NF_DROP; } - cseq = simple_strtoul(*dptr + matchoff, NULL, 10); - if (!cseq && *(*dptr + matchoff) != '0') { + cseq = sip_strtouint(*dptr + matchoff, *datalen - matchoff, (char **)&end); + if (*dptr + matchoff == end) { nf_ct_helper_log(skb, ct, "cannot get cseq"); return NF_DROP; } @@ -1482,6 +1565,7 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff, for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { const struct sip_handler *handler; + char *end; handler = &sip_handlers[i]; if (handler->request == NULL) @@ -1498,8 +1582,8 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff, nf_ct_helper_log(skb, ct, "cannot parse cseq"); return NF_DROP; } - cseq = simple_strtoul(*dptr + matchoff, NULL, 10); - if (!cseq && *(*dptr + matchoff) != '0') { + cseq = sip_strtouint(*dptr + matchoff, *datalen - matchoff, (char **)&end); + if (*dptr + matchoff == end) { nf_ct_helper_log(skb, ct, "cannot get cseq"); return NF_DROP; } @@ -1575,7 +1659,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, &matchoff, &matchlen) <= 0) break; - clen = simple_strtoul(dptr + matchoff, (char **)&end, 10); + clen = sip_strtouint(dptr + matchoff, datalen - matchoff, (char **)&end); if (dptr + matchoff == end) break; diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c index e348fb90b8dc..3b0a70e154cd 100644 --- a/net/netfilter/nf_dup_netdev.c +++ b/net/netfilter/nf_dup_netdev.c @@ -13,22 +13,6 @@ #include #include -#define NF_RECURSION_LIMIT 2 - -#ifndef CONFIG_PREEMPT_RT -static u8 *nf_get_nf_dup_skb_recursion(void) -{ - return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion); -} -#else - -static u8 *nf_get_nf_dup_skb_recursion(void) -{ - return ¤t->net_xmit.nf_dup_skb_recursion; -} - -#endif - static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev, enum nf_dev_hooks hook) { diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 2c4140e6f53c..785d8c244a77 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -122,6 +122,7 @@ static int flow_offload_fill_route(struct flow_offload *flow, flow_tuple->tun = route->tuple[dir].in.tun; flow_tuple->encap_num = route->tuple[dir].in.num_encaps; + flow_tuple->needs_gso_segment = route->tuple[dir].out.needs_gso_segment; flow_tuple->tun_num = route->tuple[dir].in.num_tuns; switch (route->tuple[dir].xmit_type) { diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index fd56d663cb5b..9c05a50d6013 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -445,13 +445,13 @@ static void nf_flow_encap_pop(struct nf_flowtable_ctx *ctx, switch (skb->protocol) { case htons(ETH_P_8021Q): vlan_hdr = (struct vlan_hdr *)skb->data; - __skb_pull(skb, VLAN_HLEN); + skb_pull_rcsum(skb, VLAN_HLEN); vlan_set_encap_proto(skb, vlan_hdr); skb_reset_network_header(skb); break; case htons(ETH_P_PPP_SES): skb->protocol = __nf_flow_pppoe_proto(skb); - skb_pull(skb, PPPOE_SES_HLEN); + skb_pull_rcsum(skb, PPPOE_SES_HLEN); skb_reset_network_header(skb); break; } @@ -462,23 +462,6 @@ static void nf_flow_encap_pop(struct nf_flowtable_ctx *ctx, nf_flow_ip_tunnel_pop(ctx, skb); } -struct nf_flow_xmit { - const void *dest; - const void *source; - struct net_device *outdev; -}; - -static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb, - struct nf_flow_xmit *xmit) -{ - skb->dev = xmit->outdev; - dev_hard_header(skb, skb->dev, ntohs(skb->protocol), - xmit->dest, xmit->source, skb->len); - dev_queue_xmit(skb); - - return NF_STOLEN; -} - static struct flow_offload_tuple_rhash * nf_flow_offload_lookup(struct nf_flowtable_ctx *ctx, struct nf_flowtable *flow_table, struct sk_buff *skb) @@ -524,7 +507,7 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx, return 0; } - if (skb_try_make_writable(skb, thoff + ctx->hdrsize)) + if (skb_ensure_writable(skb, thoff + ctx->hdrsize)) return -1; flow_offload_refresh(flow_table, flow, false); @@ -544,7 +527,34 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx, return 1; } -static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id) +/* Similar to skb_vlan_push. */ +static int nf_flow_vlan_push(struct sk_buff *skb, __be16 proto, u16 id, + u32 needed_headroom) +{ + if (skb_vlan_tag_present(skb)) { + struct vlan_hdr *vhdr; + + if (skb_cow_head(skb, needed_headroom + VLAN_HLEN)) + return -1; + + __skb_push(skb, VLAN_HLEN); + if (skb_mac_header_was_set(skb)) + skb->mac_header -= VLAN_HLEN; + + vhdr = (struct vlan_hdr *)skb->data; + skb->network_header -= VLAN_HLEN; + vhdr->h_vlan_TCI = htons(skb_vlan_tag_get(skb)); + vhdr->h_vlan_encapsulated_proto = skb->protocol; + skb->protocol = skb->vlan_proto; + skb_postpush_rcsum(skb, skb->data, VLAN_HLEN); + } + __vlan_hwaccel_put_tag(skb, proto, id); + + return 0; +} + +static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id, + u32 needed_headroom) { int data_len = skb->len + sizeof(__be16); struct ppp_hdr { @@ -553,7 +563,7 @@ static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id) } *ph; __be16 proto; - if (skb_cow_head(skb, PPPOE_SES_HLEN)) + if (skb_cow_head(skb, needed_headroom + PPPOE_SES_HLEN)) return -1; switch (skb->protocol) { @@ -730,21 +740,24 @@ static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb, } static int nf_flow_encap_push(struct sk_buff *skb, - struct flow_offload_tuple *tuple) + struct flow_offload_tuple *tuple, + struct net_device *outdev) { + u32 needed_headroom = LL_RESERVED_SPACE(outdev); int i; - for (i = 0; i < tuple->encap_num; i++) { + for (i = tuple->encap_num - 1; i >= 0; i--) { switch (tuple->encap[i].proto) { case htons(ETH_P_8021Q): case htons(ETH_P_8021AD): - skb_reset_mac_header(skb); - if (skb_vlan_push(skb, tuple->encap[i].proto, - tuple->encap[i].id) < 0) + if (nf_flow_vlan_push(skb, tuple->encap[i].proto, + tuple->encap[i].id, + needed_headroom) < 0) return -1; break; case htons(ETH_P_PPP_SES): - if (nf_flow_pppoe_push(skb, tuple->encap[i].id) < 0) + if (nf_flow_pppoe_push(skb, tuple->encap[i].id, + needed_headroom) < 0) return -1; break; } @@ -753,6 +766,76 @@ static int nf_flow_encap_push(struct sk_buff *skb, return 0; } +struct nf_flow_xmit { + const void *dest; + const void *source; + struct net_device *outdev; + struct flow_offload_tuple *tuple; + bool needs_gso_segment; +}; + +static void __nf_flow_queue_xmit(struct net *net, struct sk_buff *skb, + struct nf_flow_xmit *xmit) +{ + struct net_device *dev = xmit->outdev; + unsigned int hh_len = LL_RESERVED_SPACE(dev); + + if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) { + skb = skb_expand_head(skb, hh_len); + if (!skb) + return; + } + + skb->dev = dev; + dev_hard_header(skb, dev, ntohs(skb->protocol), + xmit->dest, xmit->source, skb->len); + dev_queue_xmit(skb); +} + +static unsigned int nf_flow_encap_gso_xmit(struct net *net, struct sk_buff *skb, + struct nf_flow_xmit *xmit) +{ + struct sk_buff *segs, *nskb; + + segs = skb_gso_segment(skb, 0); + if (IS_ERR(segs)) + return NF_DROP; + + if (segs) + consume_skb(skb); + else + segs = skb; + + skb_list_walk_safe(segs, segs, nskb) { + skb_mark_not_on_list(segs); + + if (nf_flow_encap_push(segs, xmit->tuple, xmit->outdev) < 0) { + kfree_skb(segs); + kfree_skb_list(nskb); + return NF_STOLEN; + } + __nf_flow_queue_xmit(net, segs, xmit); + } + + return NF_STOLEN; +} + +static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb, + struct nf_flow_xmit *xmit) +{ + if (xmit->tuple->encap_num) { + if (skb_is_gso(skb) && xmit->needs_gso_segment) + return nf_flow_encap_gso_xmit(net, skb, xmit); + + if (nf_flow_encap_push(skb, xmit->tuple, xmit->outdev) < 0) + return NF_DROP; + } + + __nf_flow_queue_xmit(net, skb, xmit); + + return NF_STOLEN; +} + unsigned int nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) @@ -797,9 +880,6 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, &ip_daddr) < 0) return NF_DROP; - if (nf_flow_encap_push(skb, other_tuple) < 0) - return NF_DROP; - switch (tuplehash->tuple.xmit_type) { case FLOW_OFFLOAD_XMIT_NEIGH: rt = dst_rtable(tuplehash->tuple.dst_cache); @@ -829,6 +909,8 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, WARN_ON_ONCE(1); return NF_DROP; } + xmit.tuple = other_tuple; + xmit.needs_gso_segment = tuplehash->tuple.needs_gso_segment; return nf_flow_queue_xmit(state->net, skb, &xmit); } @@ -1037,7 +1119,7 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx, return 0; } - if (skb_try_make_writable(skb, thoff + ctx->hdrsize)) + if (skb_ensure_writable(skb, thoff + ctx->hdrsize)) return -1; flow_offload_refresh(flow_table, flow, false); @@ -1119,9 +1201,6 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, &ip6_daddr, encap_limit) < 0) return NF_DROP; - if (nf_flow_encap_push(skb, other_tuple) < 0) - return NF_DROP; - switch (tuplehash->tuple.xmit_type) { case FLOW_OFFLOAD_XMIT_NEIGH: rt = dst_rt6_info(tuplehash->tuple.dst_cache); @@ -1151,6 +1230,8 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, WARN_ON_ONCE(1); return NF_DROP; } + xmit.tuple = other_tuple; + xmit.needs_gso_segment = tuplehash->tuple.needs_gso_segment; return nf_flow_queue_xmit(state->net, skb, &xmit); } diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c index 6bb9579dcc2a..9e88ea6a2eef 100644 --- a/net/netfilter/nf_flow_table_path.c +++ b/net/netfilter/nf_flow_table_path.c @@ -86,6 +86,7 @@ struct nft_forward_info { u8 ingress_vlans; u8 h_source[ETH_ALEN]; u8 h_dest[ETH_ALEN]; + bool needs_gso_segment; enum flow_offload_xmit_type xmit_type; }; @@ -138,8 +139,11 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack, path->encap.proto; info->num_encaps++; } - if (path->type == DEV_PATH_PPPOE) + if (path->type == DEV_PATH_PPPOE) { memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN); + info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; + info->needs_gso_segment = 1; + } break; case DEV_PATH_BRIDGE: if (is_zero_ether_addr(info->h_source)) @@ -279,6 +283,7 @@ static void nft_dev_forward_path(const struct nft_pktinfo *pkt, memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN); route->tuple[dir].xmit_type = info.xmit_type; } + route->tuple[dir].out.needs_gso_segment = info.needs_gso_segment; } int nft_flow_route(const struct nft_pktinfo *pkt, const struct nf_conn *ct, diff --git a/net/netfilter/nf_nat_amanda.c b/net/netfilter/nf_nat_amanda.c index 98deef6cde69..8f1054920a85 100644 --- a/net/netfilter/nf_nat_amanda.c +++ b/net/netfilter/nf_nat_amanda.c @@ -50,7 +50,7 @@ static unsigned int help(struct sk_buff *skb, return NF_DROP; } - sprintf(buffer, "%u", port); + snprintf(buffer, sizeof(buffer), "%u", port); if (!nf_nat_mangle_udp_packet(skb, exp->master, ctinfo, protoff, matchoff, matchlen, buffer, strlen(buffer))) { diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 83b2b5e9759a..74ec224ce0d6 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -1222,9 +1222,11 @@ int nf_nat_register_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops, ret = nf_register_net_hooks(net, nat_ops, ops_count); if (ret < 0) { mutex_unlock(&nf_nat_proto_mutex); - for (i = 0; i < ops_count; i++) - kfree(nat_ops[i].priv); - kfree(nat_ops); + for (i = 0; i < ops_count; i++) { + priv = nat_ops[i].priv; + kfree_rcu(priv, rcu_head); + } + kfree_rcu(nat_ops, rcu); return ret; } @@ -1288,7 +1290,7 @@ void nf_nat_unregister_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops, } nat_proto_net->nat_hook_ops = NULL; - kfree(nat_ops); + kfree_rcu(nat_ops, rcu); } unlock: mutex_unlock(&nf_nat_proto_mutex); diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index cf4aeb299bde..9fbfc6bff0c2 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -68,25 +68,27 @@ static unsigned int mangle_packet(struct sk_buff *skb, unsigned int protoff, } static int sip_sprintf_addr(const struct nf_conn *ct, char *buffer, + size_t size, const union nf_inet_addr *addr, bool delim) { if (nf_ct_l3num(ct) == NFPROTO_IPV4) - return sprintf(buffer, "%pI4", &addr->ip); + return scnprintf(buffer, size, "%pI4", &addr->ip); else { if (delim) - return sprintf(buffer, "[%pI6c]", &addr->ip6); + return scnprintf(buffer, size, "[%pI6c]", &addr->ip6); else - return sprintf(buffer, "%pI6c", &addr->ip6); + return scnprintf(buffer, size, "%pI6c", &addr->ip6); } } static int sip_sprintf_addr_port(const struct nf_conn *ct, char *buffer, + size_t size, const union nf_inet_addr *addr, u16 port) { if (nf_ct_l3num(ct) == NFPROTO_IPV4) - return sprintf(buffer, "%pI4:%u", &addr->ip, port); + return scnprintf(buffer, size, "%pI4:%u", &addr->ip, port); else - return sprintf(buffer, "[%pI6c]:%u", &addr->ip6, port); + return scnprintf(buffer, size, "[%pI6c]:%u", &addr->ip6, port); } static int map_addr(struct sk_buff *skb, unsigned int protoff, @@ -119,7 +121,7 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff, if (nf_inet_addr_cmp(&newaddr, addr) && newport == port) return 1; - buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, ntohs(newport)); + buflen = sip_sprintf_addr_port(ct, buffer, sizeof(buffer), &newaddr, ntohs(newport)); return mangle_packet(skb, protoff, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen); } @@ -212,7 +214,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, &addr, true) > 0 && nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.src.u3) && !nf_inet_addr_cmp(&addr, &ct->tuplehash[!dir].tuple.dst.u3)) { - buflen = sip_sprintf_addr(ct, buffer, + buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer), &ct->tuplehash[!dir].tuple.dst.u3, true); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, @@ -229,7 +231,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, &addr, false) > 0 && nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.dst.u3) && !nf_inet_addr_cmp(&addr, &ct->tuplehash[!dir].tuple.src.u3)) { - buflen = sip_sprintf_addr(ct, buffer, + buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer), &ct->tuplehash[!dir].tuple.src.u3, false); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, @@ -244,10 +246,11 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen, "rport=", &poff, &plen, &n) > 0 && + n >= 1024 && n <= 65535 && htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port && htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) { __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port; - buflen = sprintf(buffer, "%u", ntohs(p)); + buflen = scnprintf(buffer, sizeof(buffer), "%u", ntohs(p)); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, poff, plen, buffer, buflen)) { nf_ct_helper_log(skb, ct, "cannot mangle rport"); @@ -418,7 +421,8 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, if (!nf_inet_addr_cmp(&exp->tuple.dst.u3, &exp->saved_addr) || exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { - buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, port); + buflen = sip_sprintf_addr_port(ct, buffer, sizeof(buffer), + &newaddr, port); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen)) { nf_ct_helper_log(skb, ct, "cannot mangle packet"); @@ -438,8 +442,8 @@ static int mangle_content_len(struct sk_buff *skb, unsigned int protoff, { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + char buffer[sizeof("4294967295")]; unsigned int matchoff, matchlen; - char buffer[sizeof("65536")]; int buflen, c_len; /* Get actual SDP length */ @@ -454,7 +458,7 @@ static int mangle_content_len(struct sk_buff *skb, unsigned int protoff, &matchoff, &matchlen) <= 0) return 0; - buflen = sprintf(buffer, "%u", c_len); + buflen = scnprintf(buffer, sizeof(buffer), "%u", c_len); return mangle_packet(skb, protoff, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen); } @@ -491,7 +495,7 @@ static unsigned int nf_nat_sdp_addr(struct sk_buff *skb, unsigned int protoff, char buffer[INET6_ADDRSTRLEN]; unsigned int buflen; - buflen = sip_sprintf_addr(ct, buffer, addr, false); + buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer), addr, false); if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff, type, term, buffer, buflen)) return 0; @@ -509,7 +513,7 @@ static unsigned int nf_nat_sdp_port(struct sk_buff *skb, unsigned int protoff, char buffer[sizeof("nnnnn")]; unsigned int buflen; - buflen = sprintf(buffer, "%u", port); + buflen = scnprintf(buffer, sizeof(buffer), "%u", port); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen)) return 0; @@ -529,7 +533,7 @@ static unsigned int nf_nat_sdp_session(struct sk_buff *skb, unsigned int protoff unsigned int buflen; /* Mangle session description owner and contact addresses */ - buflen = sip_sprintf_addr(ct, buffer, addr, false); + buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer), addr, false); if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff, SDP_HDR_OWNER, SDP_HDR_MEDIA, buffer, buflen)) return 0; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 8537b94653d3..87387adbca65 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -374,7 +374,40 @@ static void nft_netdev_hook_free_rcu(struct nft_hook *hook) call_rcu(&hook->rcu, __nft_netdev_hook_free_rcu); } +static void nft_netdev_hook_unlink_free_rcu(struct nft_hook *hook) +{ + list_del_rcu(&hook->list); + nft_netdev_hook_free_rcu(hook); +} + +static void nft_trans_hook_destroy(struct nft_trans_hook *trans_hook) +{ + list_del(&trans_hook->list); + kfree(trans_hook); +} + +static void nft_netdev_unregister_trans_hook(struct net *net, + const struct nft_table *table, + struct list_head *hook_list) +{ + struct nft_trans_hook *trans_hook, *next; + struct nf_hook_ops *ops; + struct nft_hook *hook; + + list_for_each_entry_safe(trans_hook, next, hook_list, list) { + hook = trans_hook->hook; + + if (!(table->flags & NFT_TABLE_F_DORMANT)) { + list_for_each_entry(ops, &hook->ops_list, list) + nf_unregister_net_hook(net, ops); + } + nft_netdev_hook_unlink_free_rcu(hook); + nft_trans_hook_destroy(trans_hook); + } +} + static void nft_netdev_unregister_hooks(struct net *net, + const struct nft_table *table, struct list_head *hook_list, bool release_netdev) { @@ -382,12 +415,12 @@ static void nft_netdev_unregister_hooks(struct net *net, struct nf_hook_ops *ops; list_for_each_entry_safe(hook, next, hook_list, list) { - list_for_each_entry(ops, &hook->ops_list, list) - nf_unregister_net_hook(net, ops); - if (release_netdev) { - list_del(&hook->list); - nft_netdev_hook_free_rcu(hook); + if (!(table->flags & NFT_TABLE_F_DORMANT)) { + list_for_each_entry(ops, &hook->ops_list, list) + nf_unregister_net_hook(net, ops); } + if (release_netdev) + nft_netdev_hook_unlink_free_rcu(hook); } } @@ -422,20 +455,25 @@ static void __nf_tables_unregister_hook(struct net *net, struct nft_base_chain *basechain; const struct nf_hook_ops *ops; - if (table->flags & NFT_TABLE_F_DORMANT || - !nft_is_base_chain(chain)) + if (!nft_is_base_chain(chain)) return; basechain = nft_base_chain(chain); ops = &basechain->ops; + /* must also be called for dormant tables */ + if (nft_base_chain_netdev(table->family, basechain->ops.hooknum)) { + nft_netdev_unregister_hooks(net, table, &basechain->hook_list, + release_netdev); + return; + } + + if (table->flags & NFT_TABLE_F_DORMANT) + return; + if (basechain->type->ops_unregister) return basechain->type->ops_unregister(net, ops); - if (nft_base_chain_netdev(table->family, basechain->ops.hooknum)) - nft_netdev_unregister_hooks(net, &basechain->hook_list, - release_netdev); - else - nf_unregister_net_hook(net, &basechain->ops); + nf_unregister_net_hook(net, &basechain->ops); } static void nf_tables_unregister_hook(struct net *net, @@ -1942,15 +1980,69 @@ static int nft_nla_put_hook_dev(struct sk_buff *skb, struct nft_hook *hook) return nla_put_string(skb, attr, hook->ifname); } +struct nft_hook_dump_ctx { + struct nft_hook *first; + int n; +}; + +static int nft_dump_basechain_hook_one(struct sk_buff *skb, + struct nft_hook *hook, + struct nft_hook_dump_ctx *dump_ctx) +{ + if (!dump_ctx->first) + dump_ctx->first = hook; + + if (nft_nla_put_hook_dev(skb, hook)) + return -1; + + dump_ctx->n++; + + return 0; +} + +static int nft_dump_basechain_hook_list(struct sk_buff *skb, + const struct net *net, + const struct list_head *hook_list, + struct nft_hook_dump_ctx *dump_ctx) +{ + struct nft_hook *hook; + int err; + + list_for_each_entry_rcu(hook, hook_list, list, + lockdep_commit_lock_is_held(net)) { + err = nft_dump_basechain_hook_one(skb, hook, dump_ctx); + if (err < 0) + return err; + } + + return 0; +} + +static int nft_dump_basechain_trans_hook_list(struct sk_buff *skb, + const struct list_head *trans_hook_list, + struct nft_hook_dump_ctx *dump_ctx) +{ + struct nft_trans_hook *trans_hook; + int err; + + list_for_each_entry(trans_hook, trans_hook_list, list) { + err = nft_dump_basechain_hook_one(skb, trans_hook->hook, dump_ctx); + if (err < 0) + return err; + } + + return 0; +} + static int nft_dump_basechain_hook(struct sk_buff *skb, const struct net *net, int family, const struct nft_base_chain *basechain, - const struct list_head *hook_list) + const struct list_head *hook_list, + const struct list_head *trans_hook_list) { const struct nf_hook_ops *ops = &basechain->ops; - struct nft_hook *hook, *first = NULL; + struct nft_hook_dump_ctx dump_hook_ctx = {}; struct nlattr *nest, *nest_devs; - int n = 0; nest = nla_nest_start_noflag(skb, NFTA_CHAIN_HOOK); if (nest == NULL) @@ -1965,23 +2057,23 @@ static int nft_dump_basechain_hook(struct sk_buff *skb, if (!nest_devs) goto nla_put_failure; - if (!hook_list) + if (!hook_list && !trans_hook_list) hook_list = &basechain->hook_list; - list_for_each_entry_rcu(hook, hook_list, list, - lockdep_commit_lock_is_held(net)) { - if (!first) - first = hook; - - if (nft_nla_put_hook_dev(skb, hook)) - goto nla_put_failure; - n++; + if (hook_list && + nft_dump_basechain_hook_list(skb, net, hook_list, &dump_hook_ctx)) { + goto nla_put_failure; + } else if (trans_hook_list && + nft_dump_basechain_trans_hook_list(skb, trans_hook_list, + &dump_hook_ctx)) { + goto nla_put_failure; } + nla_nest_end(skb, nest_devs); - if (n == 1 && - !hook_is_prefix(first) && - nla_put_string(skb, NFTA_HOOK_DEV, first->ifname)) + if (dump_hook_ctx.n == 1 && + !hook_is_prefix(dump_hook_ctx.first) && + nla_put_string(skb, NFTA_HOOK_DEV, dump_hook_ctx.first->ifname)) goto nla_put_failure; } nla_nest_end(skb, nest); @@ -1995,7 +2087,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net, u32 portid, u32 seq, int event, u32 flags, int family, const struct nft_table *table, const struct nft_chain *chain, - const struct list_head *hook_list) + const struct list_head *hook_list, + const struct list_head *trans_hook_list) { struct nlmsghdr *nlh; @@ -2011,7 +2104,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net, NFTA_CHAIN_PAD)) goto nla_put_failure; - if (!hook_list && + if (!hook_list && !trans_hook_list && (event == NFT_MSG_DELCHAIN || event == NFT_MSG_DESTROYCHAIN)) { nlmsg_end(skb, nlh); @@ -2022,7 +2115,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net, const struct nft_base_chain *basechain = nft_base_chain(chain); struct nft_stats __percpu *stats; - if (nft_dump_basechain_hook(skb, net, family, basechain, hook_list)) + if (nft_dump_basechain_hook(skb, net, family, basechain, + hook_list, trans_hook_list)) goto nla_put_failure; if (nla_put_be32(skb, NFTA_CHAIN_POLICY, @@ -2058,7 +2152,8 @@ nla_put_failure: } static void nf_tables_chain_notify(const struct nft_ctx *ctx, int event, - const struct list_head *hook_list) + const struct list_head *hook_list, + const struct list_head *trans_hook_list) { struct nftables_pernet *nft_net; struct sk_buff *skb; @@ -2078,7 +2173,7 @@ static void nf_tables_chain_notify(const struct nft_ctx *ctx, int event, err = nf_tables_fill_chain_info(skb, ctx->net, ctx->portid, ctx->seq, event, flags, ctx->family, ctx->table, - ctx->chain, hook_list); + ctx->chain, hook_list, trans_hook_list); if (err < 0) { kfree_skb(skb); goto err; @@ -2124,7 +2219,7 @@ static int nf_tables_dump_chains(struct sk_buff *skb, NFT_MSG_NEWCHAIN, NLM_F_MULTI, table->family, table, - chain, NULL) < 0) + chain, NULL, NULL) < 0) goto done; nl_dump_check_consistent(cb, nlmsg_hdr(skb)); @@ -2178,7 +2273,7 @@ static int nf_tables_getchain(struct sk_buff *skb, const struct nfnl_info *info, err = nf_tables_fill_chain_info(skb2, net, NETLINK_CB(skb).portid, info->nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, - 0, family, table, chain, NULL); + 0, family, table, chain, NULL, NULL); if (err < 0) goto err_fill_chain_info; @@ -2271,10 +2366,8 @@ void nf_tables_chain_destroy(struct nft_chain *chain) if (nft_base_chain_netdev(table->family, basechain->ops.hooknum)) { list_for_each_entry_safe(hook, next, - &basechain->hook_list, list) { - list_del_rcu(&hook->list); - nft_netdev_hook_free_rcu(hook); - } + &basechain->hook_list, list) + nft_netdev_hook_unlink_free_rcu(hook); } module_put(basechain->type->owner); if (rcu_access_pointer(basechain->stats)) { @@ -2343,8 +2436,12 @@ static struct nft_hook *nft_hook_list_find(struct list_head *hook_list, list_for_each_entry(hook, hook_list, list) { if (!strncmp(hook->ifname, this->ifname, - min(hook->ifnamelen, this->ifnamelen))) + min(hook->ifnamelen, this->ifnamelen))) { + if (hook->flags & NFT_HOOK_REMOVE) + continue; + return hook; + } } return NULL; @@ -2974,6 +3071,7 @@ err_hooks: list_for_each_entry(ops, &h->ops_list, list) nf_unregister_net_hook(ctx->net, ops); } + /* hook.list is on stack, no need for list_del_rcu() */ list_del(&h->list); nft_netdev_hook_free_rcu(h); } @@ -3102,6 +3200,32 @@ static int nf_tables_newchain(struct sk_buff *skb, const struct nfnl_info *info, return nf_tables_addchain(&ctx, family, policy, flags, extack); } +static int nft_trans_delhook(struct nft_hook *hook, + struct list_head *del_list) +{ + struct nft_trans_hook *trans_hook; + + trans_hook = kmalloc_obj(*trans_hook, GFP_KERNEL); + if (!trans_hook) + return -ENOMEM; + + trans_hook->hook = hook; + list_add_tail(&trans_hook->list, del_list); + hook->flags |= NFT_HOOK_REMOVE; + + return 0; +} + +static void nft_trans_delhook_abort(struct list_head *del_list) +{ + struct nft_trans_hook *trans_hook, *next; + + list_for_each_entry_safe(trans_hook, next, del_list, list) { + trans_hook->hook->flags &= ~NFT_HOOK_REMOVE; + nft_trans_hook_destroy(trans_hook); + } +} + static int nft_delchain_hook(struct nft_ctx *ctx, struct nft_base_chain *basechain, struct netlink_ext_ack *extack) @@ -3128,7 +3252,10 @@ static int nft_delchain_hook(struct nft_ctx *ctx, err = -ENOENT; goto err_chain_del_hook; } - list_move(&hook->list, &chain_del_list); + if (nft_trans_delhook(hook, &chain_del_list) < 0) { + err = -ENOMEM; + goto err_chain_del_hook; + } } trans = nft_trans_alloc_chain(ctx, NFT_MSG_DELCHAIN); @@ -3148,7 +3275,7 @@ static int nft_delchain_hook(struct nft_ctx *ctx, return 0; err_chain_del_hook: - list_splice(&chain_del_list, &basechain->hook_list); + nft_trans_delhook_abort(&chain_del_list); nft_chain_release_hook(&chain_hook); return err; @@ -4086,6 +4213,7 @@ static int nft_table_validate(struct net *net, const struct nft_table *table) struct nft_chain *chain; struct nft_ctx ctx = { .net = net, + .table = (struct nft_table *)table, .family = table->family, }; int err = 0; @@ -8852,10 +8980,8 @@ static void __nft_unregister_flowtable_net_hooks(struct net *net, list_for_each_entry_safe(hook, next, hook_list, list) { list_for_each_entry(ops, &hook->ops_list, list) nft_unregister_flowtable_ops(net, flowtable, ops); - if (release_netdev) { - list_del(&hook->list); - nft_netdev_hook_free_rcu(hook); - } + if (release_netdev) + nft_netdev_hook_unlink_free_rcu(hook); } } @@ -8926,8 +9052,7 @@ err_unregister_net_hooks: nft_unregister_flowtable_ops(net, flowtable, ops); } - list_del_rcu(&hook->list); - nft_netdev_hook_free_rcu(hook); + nft_netdev_hook_unlink_free_rcu(hook); } return err; @@ -8937,9 +9062,25 @@ static void nft_hooks_destroy(struct list_head *hook_list) { struct nft_hook *hook, *next; - list_for_each_entry_safe(hook, next, hook_list, list) { - list_del_rcu(&hook->list); - nft_netdev_hook_free_rcu(hook); + list_for_each_entry_safe(hook, next, hook_list, list) + nft_netdev_hook_unlink_free_rcu(hook); +} + +static void nft_flowtable_unregister_trans_hook(struct net *net, + struct nft_flowtable *flowtable, + struct list_head *hook_list) +{ + struct nft_trans_hook *trans_hook, *next; + struct nf_hook_ops *ops; + struct nft_hook *hook; + + list_for_each_entry_safe(trans_hook, next, hook_list, list) { + hook = trans_hook->hook; + list_for_each_entry(ops, &hook->ops_list, list) + nft_unregister_flowtable_ops(net, flowtable, ops); + + nft_netdev_hook_unlink_free_rcu(hook); + nft_trans_hook_destroy(trans_hook); } } @@ -9028,8 +9169,7 @@ err_flowtable_update_hook: nft_unregister_flowtable_ops(ctx->net, flowtable, ops); } - list_del_rcu(&hook->list); - nft_netdev_hook_free_rcu(hook); + nft_netdev_hook_unlink_free_rcu(hook); } return err; @@ -9202,7 +9342,10 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx, err = -ENOENT; goto err_flowtable_del_hook; } - list_move(&hook->list, &flowtable_del_list); + if (nft_trans_delhook(hook, &flowtable_del_list) < 0) { + err = -ENOMEM; + goto err_flowtable_del_hook; + } } trans = nft_trans_alloc(ctx, NFT_MSG_DELFLOWTABLE, @@ -9223,7 +9366,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx, return 0; err_flowtable_del_hook: - list_splice(&flowtable_del_list, &flowtable->hook_list); + nft_trans_delhook_abort(&flowtable_del_list); nft_flowtable_hook_release(&flowtable_hook); return err; @@ -9288,8 +9431,10 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net, u32 portid, u32 seq, int event, u32 flags, int family, struct nft_flowtable *flowtable, - struct list_head *hook_list) + struct list_head *hook_list, + struct list_head *trans_hook_list) { + struct nft_trans_hook *trans_hook; struct nlattr *nest, *nest_devs; struct nft_hook *hook; struct nlmsghdr *nlh; @@ -9306,7 +9451,7 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net, NFTA_FLOWTABLE_PAD)) goto nla_put_failure; - if (!hook_list && + if (!hook_list && !trans_hook_list && (event == NFT_MSG_DELFLOWTABLE || event == NFT_MSG_DESTROYFLOWTABLE)) { nlmsg_end(skb, nlh); @@ -9328,13 +9473,20 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net, if (!nest_devs) goto nla_put_failure; - if (!hook_list) + if (!hook_list && !trans_hook_list) hook_list = &flowtable->hook_list; - list_for_each_entry_rcu(hook, hook_list, list, - lockdep_commit_lock_is_held(net)) { - if (nft_nla_put_hook_dev(skb, hook)) - goto nla_put_failure; + if (hook_list) { + list_for_each_entry_rcu(hook, hook_list, list, + lockdep_commit_lock_is_held(net)) { + if (nft_nla_put_hook_dev(skb, hook)) + goto nla_put_failure; + } + } else if (trans_hook_list) { + list_for_each_entry(trans_hook, trans_hook_list, list) { + if (nft_nla_put_hook_dev(skb, trans_hook->hook)) + goto nla_put_failure; + } } nla_nest_end(skb, nest_devs); nla_nest_end(skb, nest); @@ -9388,7 +9540,7 @@ static int nf_tables_dump_flowtable(struct sk_buff *skb, NFT_MSG_NEWFLOWTABLE, NLM_F_MULTI | NLM_F_APPEND, table->family, - flowtable, NULL) < 0) + flowtable, NULL, NULL) < 0) goto done; nl_dump_check_consistent(cb, nlmsg_hdr(skb)); @@ -9488,7 +9640,7 @@ static int nf_tables_getflowtable(struct sk_buff *skb, err = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid, info->nlh->nlmsg_seq, NFT_MSG_NEWFLOWTABLE, 0, family, - flowtable, NULL); + flowtable, NULL, NULL); if (err < 0) goto err_fill_flowtable_info; @@ -9501,7 +9653,9 @@ err_fill_flowtable_info: static void nf_tables_flowtable_notify(struct nft_ctx *ctx, struct nft_flowtable *flowtable, - struct list_head *hook_list, int event) + struct list_head *hook_list, + struct list_head *trans_hook_list, + int event) { struct nftables_pernet *nft_net = nft_pernet(ctx->net); struct sk_buff *skb; @@ -9521,7 +9675,8 @@ static void nf_tables_flowtable_notify(struct nft_ctx *ctx, err = nf_tables_fill_flowtable_info(skb, ctx->net, ctx->portid, ctx->seq, event, flags, - ctx->family, flowtable, hook_list); + ctx->family, flowtable, + hook_list, trans_hook_list); if (err < 0) { kfree_skb(skb); goto err; @@ -9535,13 +9690,8 @@ err: static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable) { - struct nft_hook *hook, *next; - flowtable->data.type->free(&flowtable->data); - list_for_each_entry_safe(hook, next, &flowtable->hook_list, list) { - list_del_rcu(&hook->list); - nft_netdev_hook_free_rcu(hook); - } + nft_hooks_destroy(&flowtable->hook_list); kfree(flowtable->name); module_put(flowtable->data.type->owner); kfree(flowtable); @@ -10060,9 +10210,7 @@ static void nft_commit_release(struct nft_trans *trans) break; case NFT_MSG_DELCHAIN: case NFT_MSG_DESTROYCHAIN: - if (nft_trans_chain_update(trans)) - nft_hooks_destroy(&nft_trans_chain_hooks(trans)); - else + if (!nft_trans_chain_update(trans)) nf_tables_chain_destroy(nft_trans_chain(trans)); break; case NFT_MSG_DELRULE: @@ -10083,9 +10231,7 @@ static void nft_commit_release(struct nft_trans *trans) break; case NFT_MSG_DELFLOWTABLE: case NFT_MSG_DESTROYFLOWTABLE: - if (nft_trans_flowtable_update(trans)) - nft_hooks_destroy(&nft_trans_flowtable_hooks(trans)); - else + if (!nft_trans_flowtable_update(trans)) nf_tables_flowtable_destroy(nft_trans_flowtable(trans)); break; } @@ -10845,31 +10991,28 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) if (nft_trans_chain_update(trans)) { nft_chain_commit_update(nft_trans_container_chain(trans)); nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN, - &nft_trans_chain_hooks(trans)); - list_splice(&nft_trans_chain_hooks(trans), - &nft_trans_basechain(trans)->hook_list); + &nft_trans_chain_hooks(trans), NULL); + list_splice_rcu(&nft_trans_chain_hooks(trans), + &nft_trans_basechain(trans)->hook_list); /* trans destroyed after rcu grace period */ } else { nft_chain_commit_drop_policy(nft_trans_container_chain(trans)); nft_clear(net, nft_trans_chain(trans)); - nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN, NULL); + nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN, NULL, NULL); nft_trans_destroy(trans); } break; case NFT_MSG_DELCHAIN: case NFT_MSG_DESTROYCHAIN: if (nft_trans_chain_update(trans)) { - nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN, + nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN, NULL, &nft_trans_chain_hooks(trans)); - if (!(table->flags & NFT_TABLE_F_DORMANT)) { - nft_netdev_unregister_hooks(net, - &nft_trans_chain_hooks(trans), - true); - } + nft_netdev_unregister_trans_hook(net, table, + &nft_trans_chain_hooks(trans)); } else { nft_chain_del(nft_trans_chain(trans)); nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN, - NULL); + NULL, NULL); nf_tables_unregister_hook(ctx.net, ctx.table, nft_trans_chain(trans)); } @@ -10975,14 +11118,16 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) nf_tables_flowtable_notify(&ctx, nft_trans_flowtable(trans), &nft_trans_flowtable_hooks(trans), + NULL, NFT_MSG_NEWFLOWTABLE); - list_splice(&nft_trans_flowtable_hooks(trans), - &nft_trans_flowtable(trans)->hook_list); + list_splice_rcu(&nft_trans_flowtable_hooks(trans), + &nft_trans_flowtable(trans)->hook_list); } else { nft_clear(net, nft_trans_flowtable(trans)); nf_tables_flowtable_notify(&ctx, nft_trans_flowtable(trans), NULL, + NULL, NFT_MSG_NEWFLOWTABLE); } nft_trans_destroy(trans); @@ -10992,16 +11137,18 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) if (nft_trans_flowtable_update(trans)) { nf_tables_flowtable_notify(&ctx, nft_trans_flowtable(trans), + NULL, &nft_trans_flowtable_hooks(trans), trans->msg_type); - nft_unregister_flowtable_net_hooks(net, - nft_trans_flowtable(trans), - &nft_trans_flowtable_hooks(trans)); + nft_flowtable_unregister_trans_hook(net, + nft_trans_flowtable(trans), + &nft_trans_flowtable_hooks(trans)); } else { list_del_rcu(&nft_trans_flowtable(trans)->list); nf_tables_flowtable_notify(&ctx, nft_trans_flowtable(trans), NULL, + NULL, trans->msg_type); nft_unregister_flowtable_net_hooks(net, nft_trans_flowtable(trans), @@ -11143,11 +11290,9 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) break; case NFT_MSG_NEWCHAIN: if (nft_trans_chain_update(trans)) { - if (!(table->flags & NFT_TABLE_F_DORMANT)) { - nft_netdev_unregister_hooks(net, - &nft_trans_chain_hooks(trans), - true); - } + nft_netdev_unregister_hooks(net, table, + &nft_trans_chain_hooks(trans), + true); free_percpu(nft_trans_chain_stats(trans)); kfree(nft_trans_chain_name(trans)); nft_trans_destroy(trans); @@ -11165,8 +11310,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) case NFT_MSG_DELCHAIN: case NFT_MSG_DESTROYCHAIN: if (nft_trans_chain_update(trans)) { - list_splice(&nft_trans_chain_hooks(trans), - &nft_trans_basechain(trans)->hook_list); + nft_trans_delhook_abort(&nft_trans_chain_hooks(trans)); } else { nft_use_inc_restore(&table->use); nft_clear(trans->net, nft_trans_chain(trans)); @@ -11280,8 +11424,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) case NFT_MSG_DELFLOWTABLE: case NFT_MSG_DESTROYFLOWTABLE: if (nft_trans_flowtable_update(trans)) { - list_splice(&nft_trans_flowtable_hooks(trans), - &nft_trans_flowtable(trans)->hook_list); + nft_trans_delhook_abort(&nft_trans_flowtable_hooks(trans)); } else { nft_use_inc_restore(&table->use); nft_clear(trans->net, nft_trans_flowtable(trans)); diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 5ddd5b6e135f..8ab186f86dd4 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -153,7 +153,7 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr, if (priv->base == NFT_PAYLOAD_NETWORK_HEADER) ptr = skb_network_header(skb) + pkt->nhoff; else { - if (!(pkt->flags & NFT_PKTINFO_L4PROTO)) + if (!(pkt->flags & NFT_PKTINFO_L4PROTO) || pkt->fragoff) return false; ptr = skb->data + nft_thoff(pkt); } diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c index d64ce21c7b55..acb753ec5697 100644 --- a/net/netfilter/nfnetlink_osf.c +++ b/net/netfilter/nfnetlink_osf.c @@ -31,26 +31,18 @@ EXPORT_SYMBOL_GPL(nf_osf_fingers); static inline int nf_osf_ttl(const struct sk_buff *skb, int ttl_check, unsigned char f_ttl) { - struct in_device *in_dev = __in_dev_get_rcu(skb->dev); const struct iphdr *ip = ip_hdr(skb); - const struct in_ifaddr *ifa; - int ret = 0; - if (ttl_check == NF_OSF_TTL_TRUE) + switch (ttl_check) { + case NF_OSF_TTL_TRUE: return ip->ttl == f_ttl; - if (ttl_check == NF_OSF_TTL_NOCHECK) + break; + case NF_OSF_TTL_NOCHECK: return 1; - else if (ip->ttl <= f_ttl) - return 1; - - in_dev_for_each_ifa_rcu(ifa, in_dev) { - if (inet_ifa_match(ip->saddr, ifa)) { - ret = (ip->ttl == f_ttl); - break; - } + case NF_OSF_TTL_LESS: + default: + return ip->ttl <= f_ttl; } - - return ret; } struct nf_osf_hdr_ctx { @@ -64,9 +56,9 @@ struct nf_osf_hdr_ctx { static bool nf_osf_match_one(const struct sk_buff *skb, const struct nf_osf_user_finger *f, int ttl_check, - struct nf_osf_hdr_ctx *ctx) + const struct nf_osf_hdr_ctx *ctx) { - const __u8 *optpinit = ctx->optp; + const __u8 *optp = ctx->optp; unsigned int check_WSS = 0; int fmatch = FMATCH_WRONG; int foptsize, optnum; @@ -95,17 +87,17 @@ static bool nf_osf_match_one(const struct sk_buff *skb, check_WSS = f->wss.wc; for (optnum = 0; optnum < f->opt_num; ++optnum) { - if (f->opt[optnum].kind == *ctx->optp) { + if (f->opt[optnum].kind == *optp) { __u32 len = f->opt[optnum].length; - const __u8 *optend = ctx->optp + len; + const __u8 *optend = optp + len; fmatch = FMATCH_OK; - switch (*ctx->optp) { + switch (*optp) { case OSFOPT_MSS: - mss = ctx->optp[3]; + mss = optp[3]; mss <<= 8; - mss |= ctx->optp[2]; + mss |= optp[2]; mss = ntohs((__force __be16)mss); break; @@ -113,7 +105,7 @@ static bool nf_osf_match_one(const struct sk_buff *skb, break; } - ctx->optp = optend; + optp = optend; } else fmatch = FMATCH_OPT_WRONG; @@ -156,9 +148,6 @@ static bool nf_osf_match_one(const struct sk_buff *skb, } } - if (fmatch != FMATCH_OK) - ctx->optp = optpinit; - return fmatch == FMATCH_OK; } @@ -320,6 +309,10 @@ static int nfnl_osf_add_callback(struct sk_buff *skb, if (f->opt_num > ARRAY_SIZE(f->opt)) return -EINVAL; + if (f->wss.wc >= OSF_WSS_MAX || + (f->wss.wc == OSF_WSS_MODULO && f->wss.val == 0)) + return -EINVAL; + for (i = 0; i < f->opt_num; i++) { if (!f->opt[i].length || f->opt[i].length > MAX_IPOPTLEN) return -EINVAL; diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index 13808e9cd999..94dccdcfa06b 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -196,7 +196,8 @@ static int nft_bitwise_init_shift(struct nft_bitwise *priv, if (err < 0) return err; - if (priv->data.data[0] >= BITS_PER_TYPE(u32)) { + if (!priv->data.data[0] || + priv->data.data[0] >= BITS_PER_TYPE(u32)) { nft_data_release(&priv->data, desc.type); return -EINVAL; } diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index decc725a33c2..0caa9304d2d0 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -261,10 +261,10 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr, return ret; } - nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv); - nft_compat_wait_for_destructors(ctx->net); + nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv); + ret = xt_check_target(&par, size, proto, inv); if (ret < 0) { if (ret == -ENOENT) { @@ -353,8 +353,6 @@ nla_put_failure: static int nft_target_validate(const struct nft_ctx *ctx, const struct nft_expr *expr) { - struct xt_target *target = expr->ops->data; - unsigned int hook_mask = 0; int ret; if (ctx->family != NFPROTO_IPV4 && @@ -377,11 +375,21 @@ static int nft_target_validate(const struct nft_ctx *ctx, const struct nft_base_chain *basechain = nft_base_chain(ctx->chain); const struct nf_hook_ops *ops = &basechain->ops; + unsigned int hook_mask = 1 << ops->hooknum; + struct xt_target *target = expr->ops->data; + void *info = nft_expr_priv(expr); + struct xt_tgchk_param par; + union nft_entry e = {}; - hook_mask = 1 << ops->hooknum; if (target->hooks && !(hook_mask & target->hooks)) return -EINVAL; + nft_target_set_tgchk_param(&par, ctx, target, info, &e, 0, false); + + ret = xt_check_hooks_target(&par); + if (ret < 0) + return ret; + ret = nft_compat_chain_validate_dependency(ctx, target->table); if (ret < 0) return ret; @@ -515,10 +523,10 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, return ret; } - nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv); - nft_compat_wait_for_destructors(ctx->net); + nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv); + return xt_check_match(&par, size, proto, inv); } @@ -614,8 +622,6 @@ static int nft_match_large_dump(struct sk_buff *skb, static int nft_match_validate(const struct nft_ctx *ctx, const struct nft_expr *expr) { - struct xt_match *match = expr->ops->data; - unsigned int hook_mask = 0; int ret; if (ctx->family != NFPROTO_IPV4 && @@ -638,11 +644,30 @@ static int nft_match_validate(const struct nft_ctx *ctx, const struct nft_base_chain *basechain = nft_base_chain(ctx->chain); const struct nf_hook_ops *ops = &basechain->ops; + unsigned int hook_mask = 1 << ops->hooknum; + struct xt_match *match = expr->ops->data; + size_t size = XT_ALIGN(match->matchsize); + struct xt_mtchk_param par; + union nft_entry e = {}; + void *info; - hook_mask = 1 << ops->hooknum; if (match->hooks && !(hook_mask & match->hooks)) return -EINVAL; + if (NFT_EXPR_SIZE(size) > NFT_MATCH_LARGE_THRESH) { + struct nft_xt_match_priv *priv = nft_expr_priv(expr); + + info = priv->info; + } else { + info = nft_expr_priv(expr); + } + + nft_match_set_mtchk_param(&par, ctx, match, info, &e, 0, false); + + ret = xt_check_hooks_match(&par); + if (ret < 0) + return ret; + ret = nft_compat_chain_validate_dependency(ctx, match->table); if (ret < 0) return ret; diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 60ee8d932fcb..fa2cc556331c 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -1334,6 +1334,8 @@ static void nft_ct_expect_obj_eval(struct nft_object *obj, if (nf_ct_expect_related(exp, 0) != 0) regs->verdict.code = NF_DROP; + + nf_ct_expect_put(exp); } static const struct nla_policy nft_ct_expect_policy[NFTA_CT_EXPECT_MAX + 1] = { diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index 0407d6f708ae..e6a07c0df207 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -376,7 +376,7 @@ static void nft_exthdr_sctp_eval(const struct nft_expr *expr, const struct sctp_chunkhdr *sch; struct sctp_chunkhdr _sch; - if (pkt->tprot != IPPROTO_SCTP) + if (pkt->tprot != IPPROTO_SCTP || pkt->fragoff) goto err; do { diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c index 4bce36c3a6a0..b9e88d7cf308 100644 --- a/net/netfilter/nft_fwd_netdev.c +++ b/net/netfilter/nft_fwd_netdev.c @@ -95,12 +95,15 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) { + u8 *nf_dup_skb_recursion = nf_get_nf_dup_skb_recursion(); struct nft_fwd_neigh *priv = nft_expr_priv(expr); void *addr = ®s->data[priv->sreg_addr]; int oif = regs->data[priv->sreg_dev]; unsigned int verdict = NF_STOLEN; struct sk_buff *skb = pkt->skb; + int nhoff = skb_network_offset(skb); struct net_device *dev; + unsigned int hh_len; int neigh_table; switch (priv->nfproto) { @@ -111,7 +114,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, verdict = NFT_BREAK; goto out; } - if (skb_try_make_writable(skb, sizeof(*iph))) { + if (skb_ensure_writable(skb, nhoff + sizeof(*iph))) { verdict = NF_DROP; goto out; } @@ -132,7 +135,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, verdict = NFT_BREAK; goto out; } - if (skb_try_make_writable(skb, sizeof(*ip6h))) { + if (skb_ensure_writable(skb, nhoff + sizeof(*ip6h))) { verdict = NF_DROP; goto out; } @@ -151,13 +154,31 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, goto out; } + if (*nf_dup_skb_recursion > NF_RECURSION_LIMIT) { + verdict = NF_DROP; + goto out; + } + dev = dev_get_by_index_rcu(nft_net(pkt), oif); - if (dev == NULL) - return; + if (dev == NULL) { + verdict = NF_DROP; + goto out; + } + + hh_len = LL_RESERVED_SPACE(dev); + if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) { + skb = skb_expand_head(skb, hh_len); + if (!skb) { + verdict = NF_STOLEN; + goto out; + } + } skb->dev = dev; skb_clear_tstamp(skb); + (*nf_dup_skb_recursion)++; neigh_xmit(neigh_table, dev, addr, skb); + (*nf_dup_skb_recursion)--; out: regs->verdict.code = verdict; } diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c index 18003433476c..45fe56da5044 100644 --- a/net/netfilter/nft_osf.c +++ b/net/netfilter/nft_osf.c @@ -28,7 +28,12 @@ static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs, struct nf_osf_data data; struct tcphdr _tcph; - if (pkt->tprot != IPPROTO_TCP) { + if (nft_pf(pkt) != NFPROTO_IPV4) { + regs->verdict.code = NFT_BREAK; + return; + } + + if (pkt->tprot != IPPROTO_TCP || pkt->fragoff) { regs->verdict.code = NFT_BREAK; return; } @@ -114,7 +119,6 @@ static int nft_osf_validate(const struct nft_ctx *ctx, switch (ctx->family) { case NFPROTO_IPV4: - case NFPROTO_IPV6: case NFPROTO_INET: hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_PRE_ROUTING) | diff --git a/net/netfilter/nft_tproxy.c b/net/netfilter/nft_tproxy.c index f2101af8c867..89be443734f6 100644 --- a/net/netfilter/nft_tproxy.c +++ b/net/netfilter/nft_tproxy.c @@ -30,8 +30,8 @@ static void nft_tproxy_eval_v4(const struct nft_expr *expr, __be16 tport = 0; struct sock *sk; - if (pkt->tprot != IPPROTO_TCP && - pkt->tprot != IPPROTO_UDP) { + if ((pkt->tprot != IPPROTO_TCP && + pkt->tprot != IPPROTO_UDP) || pkt->fragoff) { regs->verdict.code = NFT_BREAK; return; } @@ -97,8 +97,8 @@ static void nft_tproxy_eval_v6(const struct nft_expr *expr, memset(&taddr, 0, sizeof(taddr)); - if (pkt->tprot != IPPROTO_TCP && - pkt->tprot != IPPROTO_UDP) { + if ((pkt->tprot != IPPROTO_TCP && + pkt->tprot != IPPROTO_UDP) || pkt->fragoff) { regs->verdict.code = NFT_BREAK; return; } diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 9f837fb5ceb4..4e6708c23922 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -55,6 +55,9 @@ static struct list_head xt_templates[NFPROTO_NUMPROTO]; struct xt_pernet { struct list_head tables[NFPROTO_NUMPROTO]; + + /* stash area used during netns exit */ + struct list_head dead_tables[NFPROTO_NUMPROTO]; }; struct compat_delta { @@ -477,11 +480,9 @@ int xt_check_proc_name(const char *name, unsigned int size) } EXPORT_SYMBOL(xt_check_proc_name); -int xt_check_match(struct xt_mtchk_param *par, - unsigned int size, u16 proto, bool inv_proto) +static int xt_check_match_common(struct xt_mtchk_param *par, + unsigned int size, u16 proto, bool inv_proto) { - int ret; - if (XT_ALIGN(par->match->matchsize) != size && par->match->matchsize != -1) { /* @@ -530,6 +531,14 @@ int xt_check_match(struct xt_mtchk_param *par, par->match->proto); return -EINVAL; } + + return 0; +} + +static int xt_checkentry_match(struct xt_mtchk_param *par) +{ + int ret; + if (par->match->checkentry != NULL) { ret = par->match->checkentry(par); if (ret < 0) @@ -538,8 +547,34 @@ int xt_check_match(struct xt_mtchk_param *par, /* Flag up potential errors. */ return -EIO; } + return 0; } + +int xt_check_hooks_match(struct xt_mtchk_param *par) +{ + if (par->match->check_hooks != NULL) + return par->match->check_hooks(par); + + return 0; +} +EXPORT_SYMBOL_GPL(xt_check_hooks_match); + +int xt_check_match(struct xt_mtchk_param *par, + unsigned int size, u16 proto, bool inv_proto) +{ + int ret; + + ret = xt_check_match_common(par, size, proto, inv_proto); + if (ret < 0) + return ret; + + ret = xt_check_hooks_match(par); + if (ret < 0) + return ret; + + return xt_checkentry_match(par); +} EXPORT_SYMBOL_GPL(xt_check_match); /** xt_check_entry_match - check that matches end before start of target @@ -1012,11 +1047,9 @@ bool xt_find_jump_offset(const unsigned int *offsets, } EXPORT_SYMBOL(xt_find_jump_offset); -int xt_check_target(struct xt_tgchk_param *par, - unsigned int size, u16 proto, bool inv_proto) +static int xt_check_target_common(struct xt_tgchk_param *par, + unsigned int size, u16 proto, bool inv_proto) { - int ret; - if (XT_ALIGN(par->target->targetsize) != size) { pr_err_ratelimited("%s_tables: %s.%u target: invalid size %u (kernel) != (user) %u\n", xt_prefix[par->family], par->target->name, @@ -1061,6 +1094,23 @@ int xt_check_target(struct xt_tgchk_param *par, par->target->proto); return -EINVAL; } + + return 0; +} + +int xt_check_hooks_target(struct xt_tgchk_param *par) +{ + if (par->target->check_hooks != NULL) + return par->target->check_hooks(par); + + return 0; +} +EXPORT_SYMBOL_GPL(xt_check_hooks_target); + +static int xt_checkentry_target(struct xt_tgchk_param *par) +{ + int ret; + if (par->target->checkentry != NULL) { ret = par->target->checkentry(par); if (ret < 0) @@ -1071,6 +1121,22 @@ int xt_check_target(struct xt_tgchk_param *par, } return 0; } + +int xt_check_target(struct xt_tgchk_param *par, + unsigned int size, u16 proto, bool inv_proto) +{ + int ret; + + ret = xt_check_target_common(par, size, proto, inv_proto); + if (ret < 0) + return ret; + + ret = xt_check_hooks_target(par); + if (ret < 0) + return ret; + + return xt_checkentry_target(par); +} EXPORT_SYMBOL_GPL(xt_check_target); /** @@ -1409,11 +1475,9 @@ struct xt_counters *xt_counters_alloc(unsigned int counters) } EXPORT_SYMBOL(xt_counters_alloc); -struct xt_table_info * -xt_replace_table(struct xt_table *table, - unsigned int num_counters, - struct xt_table_info *newinfo, - int *error) +static struct xt_table_info * +do_replace_table(struct xt_table *table, unsigned int num_counters, + struct xt_table_info *newinfo, int *error) { struct xt_table_info *private; unsigned int cpu; @@ -1468,30 +1532,54 @@ xt_replace_table(struct xt_table *table, } } - audit_log_nfcfg(table->name, table->af, private->number, - !private->number ? AUDIT_XT_OP_REGISTER : - AUDIT_XT_OP_REPLACE, - GFP_KERNEL); + return private; +} + +struct xt_table_info * +xt_replace_table(struct xt_table *table, unsigned int num_counters, + struct xt_table_info *newinfo, + int *error) +{ + struct xt_table_info *private; + + private = do_replace_table(table, num_counters, newinfo, error); + if (private) + audit_log_nfcfg(table->name, table->af, private->number, + AUDIT_XT_OP_REPLACE, + GFP_KERNEL); + return private; } EXPORT_SYMBOL_GPL(xt_replace_table); struct xt_table *xt_register_table(struct net *net, const struct xt_table *input_table, + const struct nf_hook_ops *template_ops, struct xt_table_info *bootstrap, struct xt_table_info *newinfo) { struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + struct xt_table *t, *table = NULL; + struct nf_hook_ops *ops = NULL; struct xt_table_info *private; - struct xt_table *t, *table; - int ret; + unsigned int num_ops; + int ret = -EINVAL; + + num_ops = hweight32(input_table->valid_hooks); + if (num_ops == 0) + goto out; + + ret = -ENOMEM; + if (template_ops) { + ops = kmemdup_array(template_ops, num_ops, sizeof(*ops), GFP_KERNEL); + if (!ops) + goto out; + } /* Don't add one object to multiple lists. */ table = kmemdup(input_table, sizeof(struct xt_table), GFP_KERNEL); - if (!table) { - ret = -ENOMEM; + if (!table) goto out; - } mutex_lock(&xt[table->af].mutex); /* Don't autoload: we'd eat our tail... */ @@ -1505,7 +1593,7 @@ struct xt_table *xt_register_table(struct net *net, /* Simplifies replace_table code. */ table->private = bootstrap; - if (!xt_replace_table(table, 0, newinfo, &ret)) + if (!do_replace_table(table, 0, newinfo, &ret)) goto unlock; private = table->private; @@ -1514,34 +1602,122 @@ struct xt_table *xt_register_table(struct net *net, /* save number of initial entries */ private->initial_entries = private->number; + if (ops) { + int i; + + for (i = 0; i < num_ops; i++) + ops[i].priv = table; + + ret = nf_register_net_hooks(net, ops, num_ops); + if (ret != 0) { + mutex_unlock(&xt[table->af].mutex); + /* nf_register_net_hooks() might have published a + * base chain before internal error unwind. + */ + synchronize_rcu(); + goto out; + } + + table->ops = ops; + } + + audit_log_nfcfg(table->name, table->af, private->number, + AUDIT_XT_OP_REGISTER, GFP_KERNEL); + list_add(&table->list, &xt_net->tables[table->af]); mutex_unlock(&xt[table->af].mutex); return table; unlock: mutex_unlock(&xt[table->af].mutex); - kfree(table); out: + kfree(table); + kfree(ops); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(xt_register_table); -void *xt_unregister_table(struct xt_table *table) +/** + * xt_unregister_table_pre_exit - pre-shutdown unregister of a table + * @net: network namespace + * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) + * @name: name of the table to unregister + * + * Unregisters the specified netfilter table from the given network namespace + * and also unregisters the hooks from netfilter core: no new packets will be + * processed. + * + * This must be called prior to xt_unregister_table_exit() from the pernet + * .pre_exit callback. After this call, the table is no longer visible to + * the get/setsockopt path. In case of rmmod, module exit path must have + * called xt_unregister_template() prior to unregistering pernet ops to + * prevent re-instantiation of the table. + * + * See also: xt_unregister_table_exit() + */ +void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) { - struct xt_table_info *private; + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + struct xt_table *t; - mutex_lock(&xt[table->af].mutex); - private = table->private; - list_del(&table->list); - mutex_unlock(&xt[table->af].mutex); - audit_log_nfcfg(table->name, table->af, private->number, - AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); - kfree(table->ops); - kfree(table); + mutex_lock(&xt[af].mutex); + list_for_each_entry(t, &xt_net->tables[af], list) { + if (strcmp(t->name, name) == 0) { + list_move(&t->list, &xt_net->dead_tables[af]); + mutex_unlock(&xt[af].mutex); - return private; + if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ + nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); + return; + } + } + mutex_unlock(&xt[af].mutex); } -EXPORT_SYMBOL_GPL(xt_unregister_table); +EXPORT_SYMBOL(xt_unregister_table_pre_exit); + +/** + * xt_unregister_table_exit - remove a table during namespace teardown + * @net: the network namespace from which to unregister the table + * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) + * @name: name of the table to unregister + * + * Completes the unregister process for a table. This must be called from + * the pernet ops .exit callback. This is the second stage after + * xt_unregister_table_pre_exit(). + * + * pair with xt_unregister_table_pre_exit() during namespace shutdown. + * + * Return: the unregistered table or NULL if the table was never + * instantiated. The caller needs to kfree() the table after it + * has removed the family specific matches/targets. + */ +struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name) +{ + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + struct xt_table *table; + + mutex_lock(&xt[af].mutex); + list_for_each_entry(table, &xt_net->dead_tables[af], list) { + struct nf_hook_ops *ops = NULL; + + if (strcmp(table->name, name) != 0) + continue; + + list_del(&table->list); + + audit_log_nfcfg(table->name, table->af, table->private->number, + AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); + swap(table->ops, ops); + mutex_unlock(&xt[af].mutex); + + kfree(ops); + return table; + } + mutex_unlock(&xt[af].mutex); + + return NULL; +} +EXPORT_SYMBOL_GPL(xt_unregister_table_exit); #endif #ifdef CONFIG_PROC_FS @@ -1988,8 +2164,10 @@ static int __net_init xt_net_init(struct net *net) struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); int i; - for (i = 0; i < NFPROTO_NUMPROTO; i++) + for (i = 0; i < NFPROTO_NUMPROTO; i++) { INIT_LIST_HEAD(&xt_net->tables[i]); + INIT_LIST_HEAD(&xt_net->dead_tables[i]); + } return 0; } @@ -1998,8 +2176,10 @@ static void __net_exit xt_net_exit(struct net *net) struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); int i; - for (i = 0; i < NFPROTO_NUMPROTO; i++) + for (i = 0; i < NFPROTO_NUMPROTO; i++) { WARN_ON_ONCE(!list_empty(&xt_net->tables[i])); + WARN_ON_ONCE(!list_empty(&xt_net->dead_tables[i])); + } } static struct pernet_operations xt_net_ops = { diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index 498f5871c84a..d2aeacf94230 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -354,7 +354,7 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = { .family = NFPROTO_IPV4, .revision = 1, .targetsize = sizeof(struct xt_ct_target_info_v1), - .usersize = offsetof(struct xt_ct_target_info, ct), + .usersize = offsetof(struct xt_ct_target_info_v1, ct), .checkentry = xt_ct_tg_check_v1, .destroy = xt_ct_tg_destroy_v1, .target = xt_ct_target_v1, @@ -366,7 +366,7 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = { .family = NFPROTO_IPV4, .revision = 2, .targetsize = sizeof(struct xt_ct_target_info_v1), - .usersize = offsetof(struct xt_ct_target_info, ct), + .usersize = offsetof(struct xt_ct_target_info_v1, ct), .checkentry = xt_ct_tg_check_v2, .destroy = xt_ct_tg_destroy_v1, .target = xt_ct_target_v1, @@ -398,7 +398,7 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = { .family = NFPROTO_IPV6, .revision = 1, .targetsize = sizeof(struct xt_ct_target_info_v1), - .usersize = offsetof(struct xt_ct_target_info, ct), + .usersize = offsetof(struct xt_ct_target_info_v1, ct), .checkentry = xt_ct_tg_check_v1, .destroy = xt_ct_tg_destroy_v1, .target = xt_ct_target_v1, @@ -410,7 +410,7 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = { .family = NFPROTO_IPV6, .revision = 2, .targetsize = sizeof(struct xt_ct_target_info_v1), - .usersize = offsetof(struct xt_ct_target_info, ct), + .usersize = offsetof(struct xt_ct_target_info_v1, ct), .checkentry = xt_ct_tg_check_v2, .destroy = xt_ct_tg_destroy_v1, .target = xt_ct_target_v1, diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index 116a885adb3c..80e1634bc51f 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -247,6 +247,21 @@ tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par) } #endif +static int tcpmss_tg4_check_hooks(const struct xt_tgchk_param *par) +{ + const struct xt_tcpmss_info *info = par->targinfo; + + if (info->mss == XT_TCPMSS_CLAMP_PMTU && + (par->hook_mask & ~((1 << NF_INET_FORWARD) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING))) != 0) { + pr_info_ratelimited("path-MTU clamping only supported in FORWARD, OUTPUT and POSTROUTING hooks\n"); + return -EINVAL; + } + + return 0; +} + /* Must specify -p tcp --syn */ static inline bool find_syn_match(const struct xt_entry_match *m) { @@ -262,17 +277,9 @@ static inline bool find_syn_match(const struct xt_entry_match *m) static int tcpmss_tg4_check(const struct xt_tgchk_param *par) { - const struct xt_tcpmss_info *info = par->targinfo; const struct ipt_entry *e = par->entryinfo; const struct xt_entry_match *ematch; - if (info->mss == XT_TCPMSS_CLAMP_PMTU && - (par->hook_mask & ~((1 << NF_INET_FORWARD) | - (1 << NF_INET_LOCAL_OUT) | - (1 << NF_INET_POST_ROUTING))) != 0) { - pr_info_ratelimited("path-MTU clamping only supported in FORWARD, OUTPUT and POSTROUTING hooks\n"); - return -EINVAL; - } if (par->nft_compat) return 0; @@ -286,17 +293,9 @@ static int tcpmss_tg4_check(const struct xt_tgchk_param *par) #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) static int tcpmss_tg6_check(const struct xt_tgchk_param *par) { - const struct xt_tcpmss_info *info = par->targinfo; const struct ip6t_entry *e = par->entryinfo; const struct xt_entry_match *ematch; - if (info->mss == XT_TCPMSS_CLAMP_PMTU && - (par->hook_mask & ~((1 << NF_INET_FORWARD) | - (1 << NF_INET_LOCAL_OUT) | - (1 << NF_INET_POST_ROUTING))) != 0) { - pr_info_ratelimited("path-MTU clamping only supported in FORWARD, OUTPUT and POSTROUTING hooks\n"); - return -EINVAL; - } if (par->nft_compat) return 0; @@ -312,6 +311,7 @@ static struct xt_target tcpmss_tg_reg[] __read_mostly = { { .family = NFPROTO_IPV4, .name = "TCPMSS", + .check_hooks = tcpmss_tg4_check_hooks, .checkentry = tcpmss_tg4_check, .target = tcpmss_tg4, .targetsize = sizeof(struct xt_tcpmss_info), @@ -322,6 +322,7 @@ static struct xt_target tcpmss_tg_reg[] __read_mostly = { { .family = NFPROTO_IPV6, .name = "TCPMSS", + .check_hooks = tcpmss_tg4_check_hooks, .checkentry = tcpmss_tg6_check, .target = tcpmss_tg6, .targetsize = sizeof(struct xt_tcpmss_info), diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index e4bea1d346cf..5f60e7298a1e 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -86,6 +86,9 @@ tproxy_tg4_v0(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_tproxy_target_info *tgi = par->targinfo; + if (par->fragoff) + return NF_DROP; + return tproxy_tg4(xt_net(par), skb, tgi->laddr, tgi->lport, tgi->mark_mask, tgi->mark_value); } @@ -95,6 +98,9 @@ tproxy_tg4_v1(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; + if (par->fragoff) + return NF_DROP; + return tproxy_tg4(xt_net(par), skb, tgi->laddr.ip, tgi->lport, tgi->mark_mask, tgi->mark_value); } @@ -106,6 +112,7 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) { const struct ipv6hdr *iph = ipv6_hdr(skb); const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; + unsigned short fragoff = 0; struct udphdr _hdr, *hp; struct sock *sk; const struct in6_addr *laddr; @@ -113,8 +120,8 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) int thoff = 0; int tproto; - tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); - if (tproto < 0) + tproto = ipv6_find_hdr(skb, &thoff, -1, &fragoff, NULL); + if (tproto < 0 || fragoff) return NF_DROP; hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); diff --git a/net/netfilter/xt_addrtype.c b/net/netfilter/xt_addrtype.c index a77088943107..913dbe3aa5e2 100644 --- a/net/netfilter/xt_addrtype.c +++ b/net/netfilter/xt_addrtype.c @@ -153,14 +153,10 @@ addrtype_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) return ret; } -static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par) +static int addrtype_mt_check_hooks(const struct xt_mtchk_param *par) { - const char *errmsg = "both incoming and outgoing interface limitation cannot be selected"; struct xt_addrtype_info_v1 *info = par->matchinfo; - - if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN && - info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT) - goto err; + const char *errmsg; if (par->hook_mask & ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) && @@ -176,6 +172,21 @@ static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par) goto err; } + return 0; +err: + pr_info_ratelimited("%s\n", errmsg); + return -EINVAL; +} + +static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par) +{ + const char *errmsg = "both incoming and outgoing interface limitation cannot be selected"; + struct xt_addrtype_info_v1 *info = par->matchinfo; + + if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN && + info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT) + goto err; + #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) if (par->family == NFPROTO_IPV6) { if ((info->source | info->dest) & XT_ADDRTYPE_BLACKHOLE) { @@ -211,6 +222,7 @@ static struct xt_match addrtype_mt_reg[] __read_mostly = { .family = NFPROTO_IPV4, .revision = 1, .match = addrtype_mt_v1, + .check_hooks = addrtype_mt_check_hooks, .checkentry = addrtype_mt_checkentry_v1, .matchsize = sizeof(struct xt_addrtype_info_v1), .me = THIS_MODULE @@ -221,6 +233,7 @@ static struct xt_match addrtype_mt_reg[] __read_mostly = { .family = NFPROTO_IPV6, .revision = 1, .match = addrtype_mt_v1, + .check_hooks = addrtype_mt_check_hooks, .checkentry = addrtype_mt_checkentry_v1, .matchsize = sizeof(struct xt_addrtype_info_v1), .me = THIS_MODULE diff --git a/net/netfilter/xt_devgroup.c b/net/netfilter/xt_devgroup.c index 9520dd00070b..6d1a44ab5eee 100644 --- a/net/netfilter/xt_devgroup.c +++ b/net/netfilter/xt_devgroup.c @@ -33,14 +33,10 @@ static bool devgroup_mt(const struct sk_buff *skb, struct xt_action_param *par) return true; } -static int devgroup_mt_checkentry(const struct xt_mtchk_param *par) +static int devgroup_mt_check_hooks(const struct xt_mtchk_param *par) { const struct xt_devgroup_info *info = par->matchinfo; - if (info->flags & ~(XT_DEVGROUP_MATCH_SRC | XT_DEVGROUP_INVERT_SRC | - XT_DEVGROUP_MATCH_DST | XT_DEVGROUP_INVERT_DST)) - return -EINVAL; - if (info->flags & XT_DEVGROUP_MATCH_SRC && par->hook_mask & ~((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN) | @@ -56,9 +52,21 @@ static int devgroup_mt_checkentry(const struct xt_mtchk_param *par) return 0; } +static int devgroup_mt_checkentry(const struct xt_mtchk_param *par) +{ + const struct xt_devgroup_info *info = par->matchinfo; + + if (info->flags & ~(XT_DEVGROUP_MATCH_SRC | XT_DEVGROUP_INVERT_SRC | + XT_DEVGROUP_MATCH_DST | XT_DEVGROUP_INVERT_DST)) + return -EINVAL; + + return 0; +} + static struct xt_match devgroup_mt_reg __read_mostly = { .name = "devgroup", .match = devgroup_mt, + .check_hooks = devgroup_mt_check_hooks, .checkentry = devgroup_mt_checkentry, .matchsize = sizeof(struct xt_devgroup_info), .family = NFPROTO_UNSPEC, diff --git a/net/netfilter/xt_ecn.c b/net/netfilter/xt_ecn.c index b96e8203ac54..a8503f5d26bf 100644 --- a/net/netfilter/xt_ecn.c +++ b/net/netfilter/xt_ecn.c @@ -30,6 +30,10 @@ static bool match_tcp(const struct sk_buff *skb, struct xt_action_param *par) struct tcphdr _tcph; const struct tcphdr *th; + /* this is fine for IPv6 as ecn_mt_check6() enforces -p tcp */ + if (par->fragoff) + return false; + /* In practice, TCP match does this, so can't fail. But let's * be good citizens. */ diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 3bd127bfc114..2704b4b60d1e 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -658,6 +658,8 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo, if (!(hinfo->cfg.mode & (XT_HASHLIMIT_HASH_DPT | XT_HASHLIMIT_HASH_SPT))) return 0; + if (ntohs(ip_hdr(skb)->frag_off) & IP_OFFSET) + return -1; nexthdr = ip_hdr(skb)->protocol; break; #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) @@ -681,7 +683,7 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo, return 0; nexthdr = ipv6_hdr(skb)->nexthdr; protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, &frag_off); - if ((int)protoff < 0) + if ((int)protoff < 0 || ntohs(frag_off) & IP6_OFFSET) return -1; break; } diff --git a/net/netfilter/xt_mac.c b/net/netfilter/xt_mac.c index 4798cd2ca26e..7fc5156825e4 100644 --- a/net/netfilter/xt_mac.c +++ b/net/netfilter/xt_mac.c @@ -36,25 +36,37 @@ static bool mac_mt(const struct sk_buff *skb, struct xt_action_param *par) return ret; } -static struct xt_match mac_mt_reg __read_mostly = { - .name = "mac", - .revision = 0, - .family = NFPROTO_UNSPEC, - .match = mac_mt, - .matchsize = sizeof(struct xt_mac_info), - .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN) | - (1 << NF_INET_FORWARD), - .me = THIS_MODULE, +static struct xt_match mac_mt_reg[] __read_mostly = { + { + .name = "mac", + .family = NFPROTO_IPV4, + .match = mac_mt, + .matchsize = sizeof(struct xt_mac_info), + .hooks = (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_FORWARD), + .me = THIS_MODULE, + }, + { + .name = "mac", + .family = NFPROTO_IPV6, + .match = mac_mt, + .matchsize = sizeof(struct xt_mac_info), + .hooks = (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_FORWARD), + .me = THIS_MODULE, + }, }; static int __init mac_mt_init(void) { - return xt_register_match(&mac_mt_reg); + return xt_register_matches(mac_mt_reg, ARRAY_SIZE(mac_mt_reg)); } static void __exit mac_mt_exit(void) { - xt_unregister_match(&mac_mt_reg); + xt_unregister_matches(mac_mt_reg, ARRAY_SIZE(mac_mt_reg)); } module_init(mac_mt_init); diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c index dc9485854002..e8807caede68 100644 --- a/net/netfilter/xt_osf.c +++ b/net/netfilter/xt_osf.c @@ -27,6 +27,9 @@ static bool xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p) { + if (p->fragoff) + return false; + return nf_osf_match(skb, xt_family(p), xt_hooknum(p), xt_in(p), xt_out(p), p->matchinfo, xt_net(p), nf_osf_fingers); } diff --git a/net/netfilter/xt_owner.c b/net/netfilter/xt_owner.c index 5bfb4843df66..8f2e57b2a586 100644 --- a/net/netfilter/xt_owner.c +++ b/net/netfilter/xt_owner.c @@ -127,26 +127,39 @@ owner_mt(const struct sk_buff *skb, struct xt_action_param *par) return true; } -static struct xt_match owner_mt_reg __read_mostly = { - .name = "owner", - .revision = 1, - .family = NFPROTO_UNSPEC, - .checkentry = owner_check, - .match = owner_mt, - .matchsize = sizeof(struct xt_owner_match_info), - .hooks = (1 << NF_INET_LOCAL_OUT) | - (1 << NF_INET_POST_ROUTING), - .me = THIS_MODULE, +static struct xt_match owner_mt_reg[] __read_mostly = { + { + .name = "owner", + .revision = 1, + .family = NFPROTO_IPV4, + .checkentry = owner_check, + .match = owner_mt, + .matchsize = sizeof(struct xt_owner_match_info), + .hooks = (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING), + .me = THIS_MODULE, + }, + { + .name = "owner", + .revision = 1, + .family = NFPROTO_IPV6, + .checkentry = owner_check, + .match = owner_mt, + .matchsize = sizeof(struct xt_owner_match_info), + .hooks = (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING), + .me = THIS_MODULE, + } }; static int __init owner_mt_init(void) { - return xt_register_match(&owner_mt_reg); + return xt_register_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg)); } static void __exit owner_mt_exit(void) { - xt_unregister_match(&owner_mt_reg); + xt_unregister_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg)); } module_init(owner_mt_init); diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c index 53997771013f..dd98f758176c 100644 --- a/net/netfilter/xt_physdev.c +++ b/net/netfilter/xt_physdev.c @@ -91,6 +91,21 @@ match_outdev: return (!!ret ^ !(info->invert & XT_PHYSDEV_OP_OUT)); } +static int physdev_mt_check_hooks(const struct xt_mtchk_param *par) +{ + const struct xt_physdev_info *info = par->matchinfo; + + if (info->bitmask & (XT_PHYSDEV_OP_OUT | XT_PHYSDEV_OP_ISOUT) && + (!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) || + info->invert & XT_PHYSDEV_OP_BRIDGED) && + par->hook_mask & (1 << NF_INET_LOCAL_OUT)) { + pr_info_ratelimited("--physdev-out and --physdev-is-out only supported in the FORWARD and POSTROUTING chains with bridged traffic\n"); + return -EINVAL; + } + + return 0; +} + static int physdev_mt_check(const struct xt_mtchk_param *par) { const struct xt_physdev_info *info = par->matchinfo; @@ -99,13 +114,6 @@ static int physdev_mt_check(const struct xt_mtchk_param *par) if (!(info->bitmask & XT_PHYSDEV_OP_MASK) || info->bitmask & ~XT_PHYSDEV_OP_MASK) return -EINVAL; - if (info->bitmask & (XT_PHYSDEV_OP_OUT | XT_PHYSDEV_OP_ISOUT) && - (!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) || - info->invert & XT_PHYSDEV_OP_BRIDGED) && - par->hook_mask & (1 << NF_INET_LOCAL_OUT)) { - pr_info_ratelimited("--physdev-out and --physdev-is-out only supported in the FORWARD and POSTROUTING chains with bridged traffic\n"); - return -EINVAL; - } #define X(memb) strnlen(info->memb, sizeof(info->memb)) >= sizeof(info->memb) if (info->bitmask & XT_PHYSDEV_OP_IN) { @@ -137,24 +145,35 @@ static int physdev_mt_check(const struct xt_mtchk_param *par) return 0; } -static struct xt_match physdev_mt_reg __read_mostly = { - .name = "physdev", - .revision = 0, - .family = NFPROTO_UNSPEC, - .checkentry = physdev_mt_check, - .match = physdev_mt, - .matchsize = sizeof(struct xt_physdev_info), - .me = THIS_MODULE, +static struct xt_match physdev_mt_reg[] __read_mostly = { + { + .name = "physdev", + .family = NFPROTO_IPV4, + .check_hooks = physdev_mt_check_hooks, + .checkentry = physdev_mt_check, + .match = physdev_mt, + .matchsize = sizeof(struct xt_physdev_info), + .me = THIS_MODULE, + }, + { + .name = "physdev", + .family = NFPROTO_IPV6, + .check_hooks = physdev_mt_check_hooks, + .checkentry = physdev_mt_check, + .match = physdev_mt, + .matchsize = sizeof(struct xt_physdev_info), + .me = THIS_MODULE, + }, }; static int __init physdev_mt_init(void) { - return xt_register_match(&physdev_mt_reg); + return xt_register_matches(physdev_mt_reg, ARRAY_SIZE(physdev_mt_reg)); } static void __exit physdev_mt_exit(void) { - xt_unregister_match(&physdev_mt_reg); + xt_unregister_matches(physdev_mt_reg, ARRAY_SIZE(physdev_mt_reg)); } module_init(physdev_mt_init); diff --git a/net/netfilter/xt_policy.c b/net/netfilter/xt_policy.c index cb6e8279010a..ff54e3a8581e 100644 --- a/net/netfilter/xt_policy.c +++ b/net/netfilter/xt_policy.c @@ -63,7 +63,7 @@ match_policy_in(const struct sk_buff *skb, const struct xt_policy_info *info, return 0; for (i = sp->len - 1; i >= 0; i--) { - pos = strict ? i - sp->len + 1 : 0; + pos = strict ? sp->len - i - 1 : 0; if (pos >= info->len) return 0; e = &info->pol[pos]; @@ -126,13 +126,10 @@ policy_mt(const struct sk_buff *skb, struct xt_action_param *par) return ret; } -static int policy_mt_check(const struct xt_mtchk_param *par) +static int policy_mt_check_hooks(const struct xt_mtchk_param *par) { const struct xt_policy_info *info = par->matchinfo; - const char *errmsg = "neither incoming nor outgoing policy selected"; - - if (!(info->flags & (XT_POLICY_MATCH_IN|XT_POLICY_MATCH_OUT))) - goto err; + const char *errmsg; if (par->hook_mask & ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) && info->flags & XT_POLICY_MATCH_OUT) { @@ -144,6 +141,21 @@ static int policy_mt_check(const struct xt_mtchk_param *par) errmsg = "input policy not valid in POSTROUTING and OUTPUT"; goto err; } + + return 0; +err: + pr_info_ratelimited("%s\n", errmsg); + return -EINVAL; +} + +static int policy_mt_check(const struct xt_mtchk_param *par) +{ + const struct xt_policy_info *info = par->matchinfo; + const char *errmsg = "neither incoming nor outgoing policy selected"; + + if (!(info->flags & (XT_POLICY_MATCH_IN|XT_POLICY_MATCH_OUT))) + goto err; + if (info->len > XT_POLICY_MAX_ELEM) { errmsg = "too many policy elements"; goto err; @@ -158,6 +170,7 @@ static struct xt_match policy_mt_reg[] __read_mostly = { { .name = "policy", .family = NFPROTO_IPV4, + .check_hooks = policy_mt_check_hooks, .checkentry = policy_mt_check, .match = policy_mt, .matchsize = sizeof(struct xt_policy_info), @@ -166,6 +179,7 @@ static struct xt_match policy_mt_reg[] __read_mostly = { { .name = "policy", .family = NFPROTO_IPV6, + .check_hooks = policy_mt_check_hooks, .checkentry = policy_mt_check, .match = policy_mt, .matchsize = sizeof(struct xt_policy_info), diff --git a/net/netfilter/xt_realm.c b/net/netfilter/xt_realm.c index 6df485f4403d..61b2f1e58d15 100644 --- a/net/netfilter/xt_realm.c +++ b/net/netfilter/xt_realm.c @@ -33,7 +33,7 @@ static struct xt_match realm_mt_reg __read_mostly = { .matchsize = sizeof(struct xt_realm_info), .hooks = (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_FORWARD) | (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_LOCAL_IN), - .family = NFPROTO_UNSPEC, + .family = NFPROTO_IPV4, .me = THIS_MODULE }; diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c index 731bc2cafae4..4ae04bba9358 100644 --- a/net/netfilter/xt_set.c +++ b/net/netfilter/xt_set.c @@ -430,6 +430,29 @@ set_target_v3(struct sk_buff *skb, const struct xt_action_param *par) return XT_CONTINUE; } +static int +set_target_v3_check_hooks(const struct xt_tgchk_param *par) +{ + const struct xt_set_info_target_v3 *info = par->targinfo; + + if (info->map_set.index != IPSET_INVALID_ID) { + if (strncmp(par->table, "mangle", 7)) { + pr_info_ratelimited("--map-set only usable from mangle table\n"); + return -EINVAL; + } + if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) | + (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) && + (par->hook_mask & ~(1 << NF_INET_FORWARD | + 1 << NF_INET_LOCAL_OUT | + 1 << NF_INET_POST_ROUTING))) { + pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n"); + return -EINVAL; + } + } + + return 0; +} + static int set_target_v3_checkentry(const struct xt_tgchk_param *par) { @@ -459,20 +482,6 @@ set_target_v3_checkentry(const struct xt_tgchk_param *par) } if (info->map_set.index != IPSET_INVALID_ID) { - if (strncmp(par->table, "mangle", 7)) { - pr_info_ratelimited("--map-set only usable from mangle table\n"); - ret = -EINVAL; - goto cleanup_del; - } - if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) | - (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) && - (par->hook_mask & ~(1 << NF_INET_FORWARD | - 1 << NF_INET_LOCAL_OUT | - 1 << NF_INET_POST_ROUTING))) { - pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n"); - ret = -EINVAL; - goto cleanup_del; - } index = ip_set_nfnl_get_byindex(par->net, info->map_set.index); if (index == IPSET_INVALID_ID) { @@ -672,6 +681,7 @@ static struct xt_target set_targets[] __read_mostly = { .family = NFPROTO_IPV4, .target = set_target_v3, .targetsize = sizeof(struct xt_set_info_target_v3), + .check_hooks = set_target_v3_check_hooks, .checkentry = set_target_v3_checkentry, .destroy = set_target_v3_destroy, .me = THIS_MODULE @@ -682,6 +692,7 @@ static struct xt_target set_targets[] __read_mostly = { .family = NFPROTO_IPV6, .target = set_target_v3, .targetsize = sizeof(struct xt_set_info_target_v3), + .check_hooks = set_target_v3_check_hooks, .checkentry = set_target_v3_checkentry, .destroy = set_target_v3_destroy, .me = THIS_MODULE diff --git a/net/netfilter/xt_tcpmss.c b/net/netfilter/xt_tcpmss.c index 0d32d4841cb3..b9da8269161d 100644 --- a/net/netfilter/xt_tcpmss.c +++ b/net/netfilter/xt_tcpmss.c @@ -32,6 +32,10 @@ tcpmss_mt(const struct sk_buff *skb, struct xt_action_param *par) u8 _opt[15 * 4 - sizeof(_tcph)]; unsigned int i, optlen; + /* this is fine for IPv6 as xt_tcpmss enforces -p tcp */ + if (par->fragoff) + return false; + /* If we don't have the whole header, drop packet. */ th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); if (th == NULL) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index d251d894afd4..0da39eaed255 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -1972,8 +1972,10 @@ int genlmsg_multicast_allns(const struct genl_family *family, struct sk_buff *skb, u32 portid, unsigned int group) { - if (WARN_ON_ONCE(group >= family->n_mcgrps)) + if (WARN_ON_ONCE(group >= family->n_mcgrps)) { + kfree_skb(skb); return -EINVAL; + } group = family->mcgrp_offset + group; return genlmsg_mcast(skb, portid, group); @@ -1986,8 +1988,10 @@ void genl_notify(const struct genl_family *family, struct sk_buff *skb, struct net *net = genl_info_net(info); struct sock *sk = net->genl_sock; - if (WARN_ON_ONCE(group >= family->n_mcgrps)) + if (WARN_ON_ONCE(group >= family->n_mcgrps)) { + kfree_skb(skb); return; + } group = family->mcgrp_offset + group; nlmsg_notify(sk, skb, info->snd_portid, group, diff --git a/net/netrom/Makefile b/net/netrom/Makefile deleted file mode 100644 index 603e36c9af2e..000000000000 --- a/net/netrom/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Linux NET/ROM layer. -# - -obj-$(CONFIG_NETROM) += netrom.o - -netrom-y := af_netrom.o nr_dev.o nr_in.o nr_loopback.o \ - nr_out.o nr_route.o nr_subr.o nr_timer.o -netrom-$(CONFIG_SYSCTL) += sysctl_net_netrom.o diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c deleted file mode 100644 index 5fc54836dfa8..000000000000 --- a/net/netrom/af_netrom.c +++ /dev/null @@ -1,1536 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For TIOCINQ/OUTQ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int nr_ndevs = 4; - -int sysctl_netrom_default_path_quality = NR_DEFAULT_QUAL; -int sysctl_netrom_obsolescence_count_initialiser = NR_DEFAULT_OBS; -int sysctl_netrom_network_ttl_initialiser = NR_DEFAULT_TTL; -int sysctl_netrom_transport_timeout = NR_DEFAULT_T1; -int sysctl_netrom_transport_maximum_tries = NR_DEFAULT_N2; -int sysctl_netrom_transport_acknowledge_delay = NR_DEFAULT_T2; -int sysctl_netrom_transport_busy_delay = NR_DEFAULT_T4; -int sysctl_netrom_transport_requested_window_size = NR_DEFAULT_WINDOW; -int sysctl_netrom_transport_no_activity_timeout = NR_DEFAULT_IDLE; -int sysctl_netrom_routing_control = NR_DEFAULT_ROUTING; -int sysctl_netrom_link_fails_count = NR_DEFAULT_FAILS; -int sysctl_netrom_reset_circuit = NR_DEFAULT_RESET; - -static unsigned short circuit = 0x101; - -static HLIST_HEAD(nr_list); -static DEFINE_SPINLOCK(nr_list_lock); - -static const struct proto_ops nr_proto_ops; - -/* - * NETROM network devices are virtual network devices encapsulating NETROM - * frames into AX.25 which will be sent through an AX.25 device, so form a - * special "super class" of normal net devices; split their locks off into a - * separate class since they always nest. - */ -static struct lock_class_key nr_netdev_xmit_lock_key; -static struct lock_class_key nr_netdev_addr_lock_key; - -static void nr_set_lockdep_one(struct net_device *dev, - struct netdev_queue *txq, - void *_unused) -{ - lockdep_set_class(&txq->_xmit_lock, &nr_netdev_xmit_lock_key); -} - -static void nr_set_lockdep_key(struct net_device *dev) -{ - lockdep_set_class(&dev->addr_list_lock, &nr_netdev_addr_lock_key); - netdev_for_each_tx_queue(dev, nr_set_lockdep_one, NULL); -} - -/* - * Socket removal during an interrupt is now safe. - */ -static void nr_remove_socket(struct sock *sk) -{ - spin_lock_bh(&nr_list_lock); - sk_del_node_init(sk); - spin_unlock_bh(&nr_list_lock); -} - -/* - * Kill all bound sockets on a dropped device. - */ -static void nr_kill_by_device(struct net_device *dev) -{ - struct sock *s; - - spin_lock_bh(&nr_list_lock); - sk_for_each(s, &nr_list) - if (nr_sk(s)->device == dev) - nr_disconnect(s, ENETUNREACH); - spin_unlock_bh(&nr_list_lock); -} - -/* - * Handle device status changes. - */ -static int nr_device_event(struct notifier_block *this, unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (event != NETDEV_DOWN) - return NOTIFY_DONE; - - nr_kill_by_device(dev); - nr_rt_device_down(dev); - - return NOTIFY_DONE; -} - -/* - * Add a socket to the bound sockets list. - */ -static void nr_insert_socket(struct sock *sk) -{ - spin_lock_bh(&nr_list_lock); - sk_add_node(sk, &nr_list); - spin_unlock_bh(&nr_list_lock); -} - -/* - * Find a socket that wants to accept the Connect Request we just - * received. - */ -static struct sock *nr_find_listener(ax25_address *addr) -{ - struct sock *s; - - spin_lock_bh(&nr_list_lock); - sk_for_each(s, &nr_list) - if (!ax25cmp(&nr_sk(s)->source_addr, addr) && - s->sk_state == TCP_LISTEN) { - sock_hold(s); - goto found; - } - s = NULL; -found: - spin_unlock_bh(&nr_list_lock); - return s; -} - -/* - * Find a connected NET/ROM socket given my circuit IDs. - */ -static struct sock *nr_find_socket(unsigned char index, unsigned char id) -{ - struct sock *s; - - spin_lock_bh(&nr_list_lock); - sk_for_each(s, &nr_list) { - struct nr_sock *nr = nr_sk(s); - - if (nr->my_index == index && nr->my_id == id) { - sock_hold(s); - goto found; - } - } - s = NULL; -found: - spin_unlock_bh(&nr_list_lock); - return s; -} - -/* - * Find a connected NET/ROM socket given their circuit IDs. - */ -static struct sock *nr_find_peer(unsigned char index, unsigned char id, - ax25_address *dest) -{ - struct sock *s; - - spin_lock_bh(&nr_list_lock); - sk_for_each(s, &nr_list) { - struct nr_sock *nr = nr_sk(s); - - if (nr->your_index == index && nr->your_id == id && - !ax25cmp(&nr->dest_addr, dest)) { - sock_hold(s); - goto found; - } - } - s = NULL; -found: - spin_unlock_bh(&nr_list_lock); - return s; -} - -/* - * Find next free circuit ID. - */ -static unsigned short nr_find_next_circuit(void) -{ - unsigned short id = circuit; - unsigned char i, j; - struct sock *sk; - - for (;;) { - i = id / 256; - j = id % 256; - - if (i != 0 && j != 0) { - if ((sk=nr_find_socket(i, j)) == NULL) - break; - sock_put(sk); - } - - id++; - } - - return id; -} - -/* - * Deferred destroy. - */ -void nr_destroy_socket(struct sock *); - -/* - * Handler for deferred kills. - */ -static void nr_destroy_timer(struct timer_list *t) -{ - struct sock *sk = timer_container_of(sk, t, sk_timer); - bh_lock_sock(sk); - sock_hold(sk); - nr_destroy_socket(sk); - bh_unlock_sock(sk); - sock_put(sk); -} - -/* - * This is called from user mode and the timers. Thus it protects itself - * against interrupt users but doesn't worry about being called during - * work. Once it is removed from the queue no interrupt or bottom half - * will touch it and we are (fairly 8-) ) safe. - */ -void nr_destroy_socket(struct sock *sk) -{ - struct sk_buff *skb; - - nr_remove_socket(sk); - - nr_stop_heartbeat(sk); - nr_stop_t1timer(sk); - nr_stop_t2timer(sk); - nr_stop_t4timer(sk); - nr_stop_idletimer(sk); - - nr_clear_queues(sk); /* Flush the queues */ - - while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { - if (skb->sk != sk) { /* A pending connection */ - /* Queue the unaccepted socket for death */ - sock_set_flag(skb->sk, SOCK_DEAD); - nr_start_heartbeat(skb->sk); - nr_sk(skb->sk)->state = NR_STATE_0; - } - - kfree_skb(skb); - } - - if (sk_has_allocations(sk)) { - /* Defer: outstanding buffers */ - sk->sk_timer.function = nr_destroy_timer; - sk->sk_timer.expires = jiffies + 2 * HZ; - add_timer(&sk->sk_timer); - } else - sock_put(sk); -} - -/* - * Handling for system calls applied via the various interfaces to a - * NET/ROM socket object. - */ - -static int nr_setsockopt(struct socket *sock, int level, int optname, - sockptr_t optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct nr_sock *nr = nr_sk(sk); - unsigned int opt; - - if (level != SOL_NETROM) - return -ENOPROTOOPT; - - if (optlen < sizeof(unsigned int)) - return -EINVAL; - - if (copy_from_sockptr(&opt, optval, sizeof(opt))) - return -EFAULT; - - switch (optname) { - case NETROM_T1: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - nr->t1 = opt * HZ; - return 0; - - case NETROM_T2: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - nr->t2 = opt * HZ; - return 0; - - case NETROM_N2: - if (opt < 1 || opt > 31) - return -EINVAL; - nr->n2 = opt; - return 0; - - case NETROM_T4: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - nr->t4 = opt * HZ; - return 0; - - case NETROM_IDLE: - if (opt > UINT_MAX / (60 * HZ)) - return -EINVAL; - nr->idle = opt * 60 * HZ; - return 0; - - default: - return -ENOPROTOOPT; - } -} - -static int nr_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct nr_sock *nr = nr_sk(sk); - int val = 0; - int len; - - if (level != SOL_NETROM) - return -ENOPROTOOPT; - - if (get_user(len, optlen)) - return -EFAULT; - - if (len < 0) - return -EINVAL; - - switch (optname) { - case NETROM_T1: - val = nr->t1 / HZ; - break; - - case NETROM_T2: - val = nr->t2 / HZ; - break; - - case NETROM_N2: - val = nr->n2; - break; - - case NETROM_T4: - val = nr->t4 / HZ; - break; - - case NETROM_IDLE: - val = nr->idle / (60 * HZ); - break; - - default: - return -ENOPROTOOPT; - } - - len = min_t(unsigned int, len, sizeof(int)); - - if (put_user(len, optlen)) - return -EFAULT; - - return copy_to_user(optval, &val, len) ? -EFAULT : 0; -} - -static int nr_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - - lock_sock(sk); - if (sock->state != SS_UNCONNECTED) { - release_sock(sk); - return -EINVAL; - } - - if (sk->sk_state != TCP_LISTEN) { - memset(&nr_sk(sk)->user_addr, 0, AX25_ADDR_LEN); - sk->sk_max_ack_backlog = backlog; - sk->sk_state = TCP_LISTEN; - release_sock(sk); - return 0; - } - release_sock(sk); - - return -EOPNOTSUPP; -} - -static struct proto nr_proto = { - .name = "NETROM", - .owner = THIS_MODULE, - .obj_size = sizeof(struct nr_sock), -}; - -static int nr_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - struct nr_sock *nr; - - if (!net_eq(net, &init_net)) - return -EAFNOSUPPORT; - - if (sock->type != SOCK_SEQPACKET || protocol != 0) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_NETROM, GFP_ATOMIC, &nr_proto, kern); - if (sk == NULL) - return -ENOMEM; - - nr = nr_sk(sk); - - sock_init_data(sock, sk); - - sock->ops = &nr_proto_ops; - sk->sk_protocol = protocol; - - skb_queue_head_init(&nr->ack_queue); - skb_queue_head_init(&nr->reseq_queue); - skb_queue_head_init(&nr->frag_queue); - - nr_init_timers(sk); - - nr->t1 = - msecs_to_jiffies(READ_ONCE(sysctl_netrom_transport_timeout)); - nr->t2 = - msecs_to_jiffies(READ_ONCE(sysctl_netrom_transport_acknowledge_delay)); - nr->n2 = - msecs_to_jiffies(READ_ONCE(sysctl_netrom_transport_maximum_tries)); - nr->t4 = - msecs_to_jiffies(READ_ONCE(sysctl_netrom_transport_busy_delay)); - nr->idle = - msecs_to_jiffies(READ_ONCE(sysctl_netrom_transport_no_activity_timeout)); - nr->window = READ_ONCE(sysctl_netrom_transport_requested_window_size); - - nr->bpqext = 1; - nr->state = NR_STATE_0; - - return 0; -} - -static struct sock *nr_make_new(struct sock *osk) -{ - struct sock *sk; - struct nr_sock *nr, *onr; - - if (osk->sk_type != SOCK_SEQPACKET) - return NULL; - - sk = sk_alloc(sock_net(osk), PF_NETROM, GFP_ATOMIC, osk->sk_prot, 0); - if (sk == NULL) - return NULL; - - nr = nr_sk(sk); - - sock_init_data(NULL, sk); - - sk->sk_type = osk->sk_type; - sk->sk_priority = READ_ONCE(osk->sk_priority); - sk->sk_protocol = osk->sk_protocol; - sk->sk_rcvbuf = osk->sk_rcvbuf; - sk->sk_sndbuf = osk->sk_sndbuf; - sk->sk_state = TCP_ESTABLISHED; - sock_copy_flags(sk, osk); - - skb_queue_head_init(&nr->ack_queue); - skb_queue_head_init(&nr->reseq_queue); - skb_queue_head_init(&nr->frag_queue); - - nr_init_timers(sk); - - onr = nr_sk(osk); - - nr->t1 = onr->t1; - nr->t2 = onr->t2; - nr->n2 = onr->n2; - nr->t4 = onr->t4; - nr->idle = onr->idle; - nr->window = onr->window; - - nr->device = onr->device; - nr->bpqext = onr->bpqext; - - return sk; -} - -static int nr_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - struct nr_sock *nr; - - if (sk == NULL) return 0; - - sock_hold(sk); - sock_orphan(sk); - lock_sock(sk); - nr = nr_sk(sk); - - switch (nr->state) { - case NR_STATE_0: - case NR_STATE_1: - case NR_STATE_2: - nr_disconnect(sk, 0); - nr_destroy_socket(sk); - break; - - case NR_STATE_3: - nr_clear_queues(sk); - nr->n2count = 0; - nr_write_internal(sk, NR_DISCREQ); - nr_start_t1timer(sk); - nr_stop_t2timer(sk); - nr_stop_t4timer(sk); - nr_stop_idletimer(sk); - nr->state = NR_STATE_2; - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DESTROY); - break; - - default: - break; - } - - sock->sk = NULL; - release_sock(sk); - sock_put(sk); - - return 0; -} - -static int nr_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len) -{ - struct sock *sk = sock->sk; - struct nr_sock *nr = nr_sk(sk); - struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr; - struct net_device *dev; - ax25_uid_assoc *user; - ax25_address *source; - - lock_sock(sk); - if (!sock_flag(sk, SOCK_ZAPPED)) { - release_sock(sk); - return -EINVAL; - } - if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25)) { - release_sock(sk); - return -EINVAL; - } - if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25))) { - release_sock(sk); - return -EINVAL; - } - if (addr->fsa_ax25.sax25_family != AF_NETROM) { - release_sock(sk); - return -EINVAL; - } - if ((dev = nr_dev_get(&addr->fsa_ax25.sax25_call)) == NULL) { - release_sock(sk); - return -EADDRNOTAVAIL; - } - - /* - * Only the super user can set an arbitrary user callsign. - */ - if (addr->fsa_ax25.sax25_ndigis == 1) { - if (!capable(CAP_NET_BIND_SERVICE)) { - dev_put(dev); - release_sock(sk); - return -EPERM; - } - nr->user_addr = addr->fsa_digipeater[0]; - nr->source_addr = addr->fsa_ax25.sax25_call; - } else { - source = &addr->fsa_ax25.sax25_call; - - user = ax25_findbyuid(current_euid()); - if (user) { - nr->user_addr = user->call; - ax25_uid_put(user); - } else { - if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) { - release_sock(sk); - dev_put(dev); - return -EPERM; - } - nr->user_addr = *source; - } - - nr->source_addr = *source; - } - - nr->device = dev; - nr_insert_socket(sk); - - sock_reset_flag(sk, SOCK_ZAPPED); - dev_put(dev); - release_sock(sk); - - return 0; -} - -static int nr_connect(struct socket *sock, struct sockaddr_unsized *uaddr, - int addr_len, int flags) -{ - struct sock *sk = sock->sk; - struct nr_sock *nr = nr_sk(sk); - struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr; - const ax25_address *source = NULL; - ax25_uid_assoc *user; - struct net_device *dev; - int err = 0; - - lock_sock(sk); - if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { - sock->state = SS_CONNECTED; - goto out_release; /* Connect completed during a ERESTARTSYS event */ - } - - if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) { - sock->state = SS_UNCONNECTED; - err = -ECONNREFUSED; - goto out_release; - } - - if (sk->sk_state == TCP_ESTABLISHED) { - err = -EISCONN; /* No reconnect on a seqpacket socket */ - goto out_release; - } - - if (sock->state == SS_CONNECTING) { - err = -EALREADY; - goto out_release; - } - - sk->sk_state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25)) { - err = -EINVAL; - goto out_release; - } - if (addr->sax25_family != AF_NETROM) { - err = -EINVAL; - goto out_release; - } - if (sock_flag(sk, SOCK_ZAPPED)) { /* Must bind first - autobinding in this may or may not work */ - sock_reset_flag(sk, SOCK_ZAPPED); - - if ((dev = nr_dev_first()) == NULL) { - err = -ENETUNREACH; - goto out_release; - } - source = (const ax25_address *)dev->dev_addr; - - user = ax25_findbyuid(current_euid()); - if (user) { - nr->user_addr = user->call; - ax25_uid_put(user); - } else { - if (ax25_uid_policy && !capable(CAP_NET_ADMIN)) { - dev_put(dev); - err = -EPERM; - goto out_release; - } - nr->user_addr = *source; - } - - nr->source_addr = *source; - nr->device = dev; - - dev_put(dev); - nr_insert_socket(sk); /* Finish the bind */ - } - - nr->dest_addr = addr->sax25_call; - - release_sock(sk); - circuit = nr_find_next_circuit(); - lock_sock(sk); - - nr->my_index = circuit / 256; - nr->my_id = circuit % 256; - - circuit++; - - /* Move to connecting socket, start sending Connect Requests */ - sock->state = SS_CONNECTING; - sk->sk_state = TCP_SYN_SENT; - - nr_establish_data_link(sk); - - nr->state = NR_STATE_1; - - nr_start_heartbeat(sk); - - /* Now the loop */ - if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) { - err = -EINPROGRESS; - goto out_release; - } - - /* - * A Connect Ack with Choke or timeout or failed routing will go to - * closed. - */ - if (sk->sk_state == TCP_SYN_SENT) { - DEFINE_WAIT(wait); - - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - if (sk->sk_state != TCP_SYN_SENT) - break; - if (!signal_pending(current)) { - release_sock(sk); - schedule(); - lock_sock(sk); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(sk_sleep(sk), &wait); - if (err) - goto out_release; - } - - if (sk->sk_state != TCP_ESTABLISHED) { - sock->state = SS_UNCONNECTED; - err = sock_error(sk); /* Always set at this point */ - goto out_release; - } - - sock->state = SS_CONNECTED; - -out_release: - release_sock(sk); - - return err; -} - -static int nr_accept(struct socket *sock, struct socket *newsock, - struct proto_accept_arg *arg) -{ - struct sk_buff *skb; - struct sock *newsk; - DEFINE_WAIT(wait); - struct sock *sk; - int err = 0; - - if ((sk = sock->sk) == NULL) - return -EINVAL; - - lock_sock(sk); - if (sk->sk_type != SOCK_SEQPACKET) { - err = -EOPNOTSUPP; - goto out_release; - } - - if (sk->sk_state != TCP_LISTEN) { - err = -EINVAL; - goto out_release; - } - - /* - * The write queue this time is holding sockets ready to use - * hooked into the SABM we saved - */ - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - skb = skb_dequeue(&sk->sk_receive_queue); - if (skb) - break; - - if (arg->flags & O_NONBLOCK) { - err = -EWOULDBLOCK; - break; - } - if (!signal_pending(current)) { - release_sock(sk); - schedule(); - lock_sock(sk); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(sk_sleep(sk), &wait); - if (err) - goto out_release; - - newsk = skb->sk; - sock_graft(newsk, newsock); - - /* Now attach up the new socket */ - kfree_skb(skb); - sk_acceptq_removed(sk); - -out_release: - release_sock(sk); - - return err; -} - -static int nr_getname(struct socket *sock, struct sockaddr *uaddr, - int peer) -{ - struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr; - struct sock *sk = sock->sk; - struct nr_sock *nr = nr_sk(sk); - int uaddr_len; - - memset(&sax->fsa_ax25, 0, sizeof(struct sockaddr_ax25)); - - lock_sock(sk); - if (peer != 0) { - if (sk->sk_state != TCP_ESTABLISHED) { - release_sock(sk); - return -ENOTCONN; - } - sax->fsa_ax25.sax25_family = AF_NETROM; - sax->fsa_ax25.sax25_ndigis = 1; - sax->fsa_ax25.sax25_call = nr->user_addr; - memset(sax->fsa_digipeater, 0, sizeof(sax->fsa_digipeater)); - sax->fsa_digipeater[0] = nr->dest_addr; - uaddr_len = sizeof(struct full_sockaddr_ax25); - } else { - sax->fsa_ax25.sax25_family = AF_NETROM; - sax->fsa_ax25.sax25_ndigis = 0; - sax->fsa_ax25.sax25_call = nr->source_addr; - uaddr_len = sizeof(struct sockaddr_ax25); - } - release_sock(sk); - - return uaddr_len; -} - -int nr_rx_frame(struct sk_buff *skb, struct net_device *dev) -{ - struct sock *sk; - struct sock *make; - struct nr_sock *nr_make; - ax25_address *src, *dest, *user; - unsigned short circuit_index, circuit_id; - unsigned short peer_circuit_index, peer_circuit_id; - unsigned short frametype, flags, window, timeout; - int ret; - - skb_orphan(skb); - - /* - * skb->data points to the netrom frame start - */ - - src = (ax25_address *)(skb->data + 0); - dest = (ax25_address *)(skb->data + 7); - - circuit_index = skb->data[15]; - circuit_id = skb->data[16]; - peer_circuit_index = skb->data[17]; - peer_circuit_id = skb->data[18]; - frametype = skb->data[19] & 0x0F; - flags = skb->data[19] & 0xF0; - - /* - * Check for an incoming IP over NET/ROM frame. - */ - if (frametype == NR_PROTOEXT && - circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) { - skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); - skb_reset_transport_header(skb); - - return nr_rx_ip(skb, dev); - } - - /* - * Find an existing socket connection, based on circuit ID, if it's - * a Connect Request base it on their circuit ID. - * - * Circuit ID 0/0 is not valid but it could still be a "reset" for a - * circuit that no longer exists at the other end ... - */ - - sk = NULL; - - if (circuit_index == 0 && circuit_id == 0) { - if (frametype == NR_CONNACK && flags == NR_CHOKE_FLAG) - sk = nr_find_peer(peer_circuit_index, peer_circuit_id, src); - } else { - if (frametype == NR_CONNREQ) - sk = nr_find_peer(circuit_index, circuit_id, src); - else - sk = nr_find_socket(circuit_index, circuit_id); - } - - if (sk != NULL) { - bh_lock_sock(sk); - skb_reset_transport_header(skb); - - if (frametype == NR_CONNACK && skb->len == 22) - nr_sk(sk)->bpqext = 1; - else - nr_sk(sk)->bpqext = 0; - - ret = nr_process_rx_frame(sk, skb); - bh_unlock_sock(sk); - sock_put(sk); - return ret; - } - - /* - * Now it should be a CONNREQ. - */ - if (frametype != NR_CONNREQ) { - /* - * Here it would be nice to be able to send a reset but - * NET/ROM doesn't have one. We've tried to extend the protocol - * by sending NR_CONNACK | NR_CHOKE_FLAGS replies but that - * apparently kills BPQ boxes... :-( - * So now we try to follow the established behaviour of - * G8PZT's Xrouter which is sending packets with command type 7 - * as an extension of the protocol. - */ - if (READ_ONCE(sysctl_netrom_reset_circuit) && - (frametype != NR_RESET || flags != 0)) - nr_transmit_reset(skb, 1); - - return 0; - } - - sk = nr_find_listener(dest); - - user = (ax25_address *)(skb->data + 21); - - if (sk == NULL || sk_acceptq_is_full(sk) || - (make = nr_make_new(sk)) == NULL) { - nr_transmit_refusal(skb, 0); - if (sk) - sock_put(sk); - return 0; - } - - bh_lock_sock(sk); - - window = skb->data[20]; - - sock_hold(make); - skb->sk = make; - skb->destructor = sock_efree; - make->sk_state = TCP_ESTABLISHED; - - /* Fill in his circuit details */ - nr_make = nr_sk(make); - nr_make->source_addr = *dest; - nr_make->dest_addr = *src; - nr_make->user_addr = *user; - - nr_make->your_index = circuit_index; - nr_make->your_id = circuit_id; - - bh_unlock_sock(sk); - circuit = nr_find_next_circuit(); - bh_lock_sock(sk); - - nr_make->my_index = circuit / 256; - nr_make->my_id = circuit % 256; - - circuit++; - - /* Window negotiation */ - if (window < nr_make->window) - nr_make->window = window; - - /* L4 timeout negotiation */ - if (skb->len == 37) { - timeout = skb->data[36] * 256 + skb->data[35]; - if (timeout * HZ < nr_make->t1) - nr_make->t1 = timeout * HZ; - nr_make->bpqext = 1; - } else { - nr_make->bpqext = 0; - } - - nr_write_internal(make, NR_CONNACK); - - nr_make->condition = 0x00; - nr_make->vs = 0; - nr_make->va = 0; - nr_make->vr = 0; - nr_make->vl = 0; - nr_make->state = NR_STATE_3; - sk_acceptq_added(sk); - skb_queue_head(&sk->sk_receive_queue, skb); - - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_data_ready(sk); - - bh_unlock_sock(sk); - sock_put(sk); - - nr_insert_socket(make); - - nr_start_heartbeat(make); - nr_start_idletimer(make); - - return 1; -} - -static int nr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct nr_sock *nr = nr_sk(sk); - DECLARE_SOCKADDR(struct sockaddr_ax25 *, usax, msg->msg_name); - int err; - struct sockaddr_ax25 sax; - struct sk_buff *skb; - unsigned char *asmptr; - int size; - - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_CMSG_COMPAT)) - return -EINVAL; - - lock_sock(sk); - if (sock_flag(sk, SOCK_ZAPPED)) { - err = -EADDRNOTAVAIL; - goto out; - } - - if (sk->sk_shutdown & SEND_SHUTDOWN) { - send_sig(SIGPIPE, current, 0); - err = -EPIPE; - goto out; - } - - if (nr->device == NULL) { - err = -ENETUNREACH; - goto out; - } - - if (usax) { - if (msg->msg_namelen < sizeof(sax)) { - err = -EINVAL; - goto out; - } - sax = *usax; - if (ax25cmp(&nr->dest_addr, &sax.sax25_call) != 0) { - err = -EISCONN; - goto out; - } - if (sax.sax25_family != AF_NETROM) { - err = -EINVAL; - goto out; - } - } else { - if (sk->sk_state != TCP_ESTABLISHED) { - err = -ENOTCONN; - goto out; - } - sax.sax25_family = AF_NETROM; - sax.sax25_call = nr->dest_addr; - } - - /* Build a packet - the conventional user limit is 236 bytes. We can - do ludicrously large NetROM frames but must not overflow */ - if (len > 65536) { - err = -EMSGSIZE; - goto out; - } - - size = len + NR_NETWORK_LEN + NR_TRANSPORT_LEN; - - if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) - goto out; - - skb_reserve(skb, size - len); - skb_reset_transport_header(skb); - - /* - * Push down the NET/ROM header - */ - - asmptr = skb_push(skb, NR_TRANSPORT_LEN); - - /* Build a NET/ROM Transport header */ - - *asmptr++ = nr->your_index; - *asmptr++ = nr->your_id; - *asmptr++ = 0; /* To be filled in later */ - *asmptr++ = 0; /* Ditto */ - *asmptr++ = NR_INFO; - - /* - * Put the data on the end - */ - skb_put(skb, len); - - /* User data follows immediately after the NET/ROM transport header */ - if (memcpy_from_msg(skb_transport_header(skb), msg, len)) { - kfree_skb(skb); - err = -EFAULT; - goto out; - } - - if (sk->sk_state != TCP_ESTABLISHED) { - kfree_skb(skb); - err = -ENOTCONN; - goto out; - } - - nr_output(sk, skb); /* Shove it onto the queue */ - - err = len; -out: - release_sock(sk); - return err; -} - -static int nr_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, - int flags) -{ - struct sock *sk = sock->sk; - DECLARE_SOCKADDR(struct sockaddr_ax25 *, sax, msg->msg_name); - size_t copied; - struct sk_buff *skb; - int er; - - /* - * This works for seqpacket too. The receiver has ordered the queue for - * us! We do one quick check first though - */ - - lock_sock(sk); - if (sk->sk_state != TCP_ESTABLISHED) { - release_sock(sk); - return -ENOTCONN; - } - - /* Now we can treat all alike */ - skb = skb_recv_datagram(sk, flags, &er); - if (!skb) { - release_sock(sk); - return er; - } - - skb_reset_transport_header(skb); - copied = skb->len; - - if (copied > size) { - copied = size; - msg->msg_flags |= MSG_TRUNC; - } - - er = skb_copy_datagram_msg(skb, 0, msg, copied); - if (er < 0) { - skb_free_datagram(sk, skb); - release_sock(sk); - return er; - } - - if (sax != NULL) { - memset(sax, 0, sizeof(*sax)); - sax->sax25_family = AF_NETROM; - skb_copy_from_linear_data_offset(skb, 7, sax->sax25_call.ax25_call, - AX25_ADDR_LEN); - msg->msg_namelen = sizeof(*sax); - } - - skb_free_datagram(sk, skb); - - release_sock(sk); - return copied; -} - - -static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk = sock->sk; - void __user *argp = (void __user *)arg; - - switch (cmd) { - case TIOCOUTQ: { - long amount; - - lock_sock(sk); - amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - if (amount < 0) - amount = 0; - release_sock(sk); - return put_user(amount, (int __user *)argp); - } - - case TIOCINQ: { - struct sk_buff *skb; - long amount = 0L; - - lock_sock(sk); - /* These two are safe on a single CPU system as only user tasks fiddle here */ - if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) - amount = skb->len; - release_sock(sk); - return put_user(amount, (int __user *)argp); - } - - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMETRIC: - case SIOCSIFMETRIC: - return -EINVAL; - - case SIOCADDRT: - case SIOCDELRT: - case SIOCNRDECOBS: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - return nr_rt_ioctl(cmd, argp); - - default: - return -ENOIOCTLCMD; - } - - return 0; -} - -#ifdef CONFIG_PROC_FS - -static void *nr_info_start(struct seq_file *seq, loff_t *pos) - __acquires(&nr_list_lock) -{ - spin_lock_bh(&nr_list_lock); - return seq_hlist_start_head(&nr_list, *pos); -} - -static void *nr_info_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &nr_list, pos); -} - -static void nr_info_stop(struct seq_file *seq, void *v) - __releases(&nr_list_lock) -{ - spin_unlock_bh(&nr_list_lock); -} - -static int nr_info_show(struct seq_file *seq, void *v) -{ - struct sock *s = sk_entry(v); - struct net_device *dev; - struct nr_sock *nr; - const char *devname; - char buf[11]; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, -"user_addr dest_node src_node dev my your st vs vr va t1 t2 t4 idle n2 wnd Snd-Q Rcv-Q inode\n"); - - else { - - bh_lock_sock(s); - nr = nr_sk(s); - - if ((dev = nr->device) == NULL) - devname = "???"; - else - devname = dev->name; - - seq_printf(seq, "%-9s ", ax2asc(buf, &nr->user_addr)); - seq_printf(seq, "%-9s ", ax2asc(buf, &nr->dest_addr)); - seq_printf(seq, -"%-9s %-3s %02X/%02X %02X/%02X %2d %3d %3d %3d %3lu/%03lu %2lu/%02lu %3lu/%03lu %3lu/%03lu %2d/%02d %3d %5d %5d %llu\n", - ax2asc(buf, &nr->source_addr), - devname, - nr->my_index, - nr->my_id, - nr->your_index, - nr->your_id, - nr->state, - nr->vs, - nr->vr, - nr->va, - ax25_display_timer(&nr->t1timer) / HZ, - nr->t1 / HZ, - ax25_display_timer(&nr->t2timer) / HZ, - nr->t2 / HZ, - ax25_display_timer(&nr->t4timer) / HZ, - nr->t4 / HZ, - ax25_display_timer(&nr->idletimer) / (60 * HZ), - nr->idle / (60 * HZ), - nr->n2count, - nr->n2, - nr->window, - sk_wmem_alloc_get(s), - sk_rmem_alloc_get(s), - s->sk_socket ? SOCK_INODE(s->sk_socket)->i_ino : (u64)0); - - bh_unlock_sock(s); - } - return 0; -} - -static const struct seq_operations nr_info_seqops = { - .start = nr_info_start, - .next = nr_info_next, - .stop = nr_info_stop, - .show = nr_info_show, -}; -#endif /* CONFIG_PROC_FS */ - -static const struct net_proto_family nr_family_ops = { - .family = PF_NETROM, - .create = nr_create, - .owner = THIS_MODULE, -}; - -static const struct proto_ops nr_proto_ops = { - .family = PF_NETROM, - .owner = THIS_MODULE, - .release = nr_release, - .bind = nr_bind, - .connect = nr_connect, - .socketpair = sock_no_socketpair, - .accept = nr_accept, - .getname = nr_getname, - .poll = datagram_poll, - .ioctl = nr_ioctl, - .gettstamp = sock_gettstamp, - .listen = nr_listen, - .shutdown = sock_no_shutdown, - .setsockopt = nr_setsockopt, - .getsockopt = nr_getsockopt, - .sendmsg = nr_sendmsg, - .recvmsg = nr_recvmsg, - .mmap = sock_no_mmap, -}; - -static struct notifier_block nr_dev_notifier = { - .notifier_call = nr_device_event, -}; - -static struct net_device **dev_nr; - -static struct ax25_protocol nr_pid = { - .pid = AX25_P_NETROM, - .func = nr_route_frame -}; - -static struct ax25_linkfail nr_linkfail_notifier = { - .func = nr_link_failed, -}; - -static int __init nr_proto_init(void) -{ - int i; - int rc = proto_register(&nr_proto, 0); - - if (rc) - return rc; - - if (nr_ndevs > 0x7fffffff/sizeof(struct net_device *)) { - pr_err("NET/ROM: %s - nr_ndevs parameter too large\n", - __func__); - rc = -EINVAL; - goto unregister_proto; - } - - dev_nr = kzalloc_objs(struct net_device *, nr_ndevs); - if (!dev_nr) { - pr_err("NET/ROM: %s - unable to allocate device array\n", - __func__); - rc = -ENOMEM; - goto unregister_proto; - } - - for (i = 0; i < nr_ndevs; i++) { - char name[IFNAMSIZ]; - struct net_device *dev; - - sprintf(name, "nr%d", i); - dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, nr_setup); - if (!dev) { - rc = -ENOMEM; - goto fail; - } - - dev->base_addr = i; - rc = register_netdev(dev); - if (rc) { - free_netdev(dev); - goto fail; - } - nr_set_lockdep_key(dev); - dev_nr[i] = dev; - } - - rc = sock_register(&nr_family_ops); - if (rc) - goto fail; - - rc = register_netdevice_notifier(&nr_dev_notifier); - if (rc) - goto out_sock; - - ax25_register_pid(&nr_pid); - ax25_linkfail_register(&nr_linkfail_notifier); - -#ifdef CONFIG_SYSCTL - rc = nr_register_sysctl(); - if (rc) - goto out_sysctl; -#endif - - nr_loopback_init(); - - rc = -ENOMEM; - if (!proc_create_seq("nr", 0444, init_net.proc_net, &nr_info_seqops)) - goto proc_remove1; - if (!proc_create_seq("nr_neigh", 0444, init_net.proc_net, - &nr_neigh_seqops)) - goto proc_remove2; - if (!proc_create_seq("nr_nodes", 0444, init_net.proc_net, - &nr_node_seqops)) - goto proc_remove3; - - return 0; - -proc_remove3: - remove_proc_entry("nr_neigh", init_net.proc_net); -proc_remove2: - remove_proc_entry("nr", init_net.proc_net); -proc_remove1: - - nr_loopback_clear(); - nr_rt_free(); - -#ifdef CONFIG_SYSCTL - nr_unregister_sysctl(); -out_sysctl: -#endif - ax25_linkfail_release(&nr_linkfail_notifier); - ax25_protocol_release(AX25_P_NETROM); - unregister_netdevice_notifier(&nr_dev_notifier); -out_sock: - sock_unregister(PF_NETROM); -fail: - while (--i >= 0) { - unregister_netdev(dev_nr[i]); - free_netdev(dev_nr[i]); - } - kfree(dev_nr); -unregister_proto: - proto_unregister(&nr_proto); - return rc; -} - -module_init(nr_proto_init); - -module_param(nr_ndevs, int, 0); -MODULE_PARM_DESC(nr_ndevs, "number of NET/ROM devices"); - -MODULE_AUTHOR("Jonathan Naylor G4KLX "); -MODULE_DESCRIPTION("The amateur radio NET/ROM network and transport layer protocol"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(PF_NETROM); - -static void __exit nr_exit(void) -{ - int i; - - remove_proc_entry("nr", init_net.proc_net); - remove_proc_entry("nr_neigh", init_net.proc_net); - remove_proc_entry("nr_nodes", init_net.proc_net); - nr_loopback_clear(); - - nr_rt_free(); - -#ifdef CONFIG_SYSCTL - nr_unregister_sysctl(); -#endif - - ax25_linkfail_release(&nr_linkfail_notifier); - ax25_protocol_release(AX25_P_NETROM); - - unregister_netdevice_notifier(&nr_dev_notifier); - - sock_unregister(PF_NETROM); - - for (i = 0; i < nr_ndevs; i++) { - struct net_device *dev = dev_nr[i]; - if (dev) { - unregister_netdev(dev); - free_netdev(dev); - } - } - - kfree(dev_nr); - proto_unregister(&nr_proto); -} -module_exit(nr_exit); diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c deleted file mode 100644 index 2c34389c3ce6..000000000000 --- a/net/netrom/nr_dev.c +++ /dev/null @@ -1,178 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For the statistics structure. */ -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -/* - * Only allow IP over NET/ROM frames through if the netrom device is up. - */ - -int nr_rx_ip(struct sk_buff *skb, struct net_device *dev) -{ - struct net_device_stats *stats = &dev->stats; - - if (!netif_running(dev)) { - stats->rx_dropped++; - return 0; - } - - stats->rx_packets++; - stats->rx_bytes += skb->len; - - skb->protocol = htons(ETH_P_IP); - - /* Spoof incoming device */ - skb->dev = dev; - skb->mac_header = skb->network_header; - skb_reset_network_header(skb); - skb->pkt_type = PACKET_HOST; - - netif_rx(skb); - - return 1; -} - -static int nr_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, - const void *daddr, const void *saddr, unsigned int len) -{ - unsigned char *buff = skb_push(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); - - memcpy(buff, (saddr != NULL) ? saddr : dev->dev_addr, dev->addr_len); - buff[6] &= ~AX25_CBIT; - buff[6] &= ~AX25_EBIT; - buff[6] |= AX25_SSSID_SPARE; - buff += AX25_ADDR_LEN; - - if (daddr != NULL) - memcpy(buff, daddr, dev->addr_len); - buff[6] &= ~AX25_CBIT; - buff[6] |= AX25_EBIT; - buff[6] |= AX25_SSSID_SPARE; - buff += AX25_ADDR_LEN; - - *buff++ = READ_ONCE(sysctl_netrom_network_ttl_initialiser); - - *buff++ = NR_PROTO_IP; - *buff++ = NR_PROTO_IP; - *buff++ = 0; - *buff++ = 0; - *buff++ = NR_PROTOEXT; - - if (daddr != NULL) - return 37; - - return -37; -} - -static int __must_check nr_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = addr; - int err; - - if (!memcmp(dev->dev_addr, sa->sa_data, dev->addr_len)) - return 0; - - if (dev->flags & IFF_UP) { - err = ax25_listen_register((ax25_address *)sa->sa_data, NULL); - if (err) - return err; - - ax25_listen_release((const ax25_address *)dev->dev_addr, NULL); - } - - dev_addr_set(dev, sa->sa_data); - - return 0; -} - -static int nr_open(struct net_device *dev) -{ - int err; - - err = ax25_listen_register((const ax25_address *)dev->dev_addr, NULL); - if (err) - return err; - - netif_start_queue(dev); - - return 0; -} - -static int nr_close(struct net_device *dev) -{ - ax25_listen_release((const ax25_address *)dev->dev_addr, NULL); - netif_stop_queue(dev); - return 0; -} - -static netdev_tx_t nr_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct net_device_stats *stats = &dev->stats; - unsigned int len = skb->len; - - if (!nr_route_frame(skb, NULL)) { - kfree_skb(skb); - stats->tx_errors++; - return NETDEV_TX_OK; - } - - stats->tx_packets++; - stats->tx_bytes += len; - - return NETDEV_TX_OK; -} - -static const struct header_ops nr_header_ops = { - .create = nr_header, -}; - -static const struct net_device_ops nr_netdev_ops = { - .ndo_open = nr_open, - .ndo_stop = nr_close, - .ndo_start_xmit = nr_xmit, - .ndo_set_mac_address = nr_set_mac_address, -}; - -void nr_setup(struct net_device *dev) -{ - dev->mtu = NR_MAX_PACKET_SIZE; - dev->netdev_ops = &nr_netdev_ops; - dev->header_ops = &nr_header_ops; - dev->hard_header_len = NR_NETWORK_LEN + NR_TRANSPORT_LEN; - dev->addr_len = AX25_ADDR_LEN; - dev->type = ARPHRD_NETROM; - - /* New-style flags. */ - dev->flags = IFF_NOARP; -} diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c deleted file mode 100644 index 97944db6b5ac..000000000000 --- a/net/netrom/nr_in.c +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) -{ - struct sk_buff *skbo, *skbn = skb; - struct nr_sock *nr = nr_sk(sk); - - skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); - - nr_start_idletimer(sk); - - if (more) { - nr->fraglen += skb->len; - skb_queue_tail(&nr->frag_queue, skb); - return 0; - } - - if (!more && nr->fraglen > 0) { /* End of fragment */ - nr->fraglen += skb->len; - skb_queue_tail(&nr->frag_queue, skb); - - if ((skbn = alloc_skb(nr->fraglen, GFP_ATOMIC)) == NULL) - return 1; - - skb_reset_transport_header(skbn); - - while ((skbo = skb_dequeue(&nr->frag_queue)) != NULL) { - skb_copy_from_linear_data(skbo, - skb_put(skbn, skbo->len), - skbo->len); - kfree_skb(skbo); - } - - nr->fraglen = 0; - } - - return sock_queue_rcv_skb(sk, skbn); -} - -/* - * State machine for state 1, Awaiting Connection State. - * The handling of the timer(s) is in file nr_timer.c. - * Handling of state 0 and connection release is in netrom.c. - */ -static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, - int frametype) -{ - switch (frametype) { - case NR_CONNACK: { - struct nr_sock *nr = nr_sk(sk); - - nr_stop_t1timer(sk); - nr_start_idletimer(sk); - nr->your_index = skb->data[17]; - nr->your_id = skb->data[18]; - nr->vs = 0; - nr->va = 0; - nr->vr = 0; - nr->vl = 0; - nr->state = NR_STATE_3; - nr->n2count = 0; - nr->window = skb->data[20]; - sk->sk_state = TCP_ESTABLISHED; - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_state_change(sk); - break; - } - - case NR_CONNACK | NR_CHOKE_FLAG: - nr_disconnect(sk, ECONNREFUSED); - break; - - case NR_RESET: - if (READ_ONCE(sysctl_netrom_reset_circuit)) - nr_disconnect(sk, ECONNRESET); - break; - - default: - break; - } - return 0; -} - -/* - * State machine for state 2, Awaiting Release State. - * The handling of the timer(s) is in file nr_timer.c - * Handling of state 0 and connection release is in netrom.c. - */ -static int nr_state2_machine(struct sock *sk, struct sk_buff *skb, - int frametype) -{ - switch (frametype) { - case NR_CONNACK | NR_CHOKE_FLAG: - nr_disconnect(sk, ECONNRESET); - break; - - case NR_DISCREQ: - nr_write_internal(sk, NR_DISCACK); - fallthrough; - case NR_DISCACK: - nr_disconnect(sk, 0); - break; - - case NR_RESET: - if (READ_ONCE(sysctl_netrom_reset_circuit)) - nr_disconnect(sk, ECONNRESET); - break; - - default: - break; - } - return 0; -} - -/* - * State machine for state 3, Connected State. - * The handling of the timer(s) is in file nr_timer.c - * Handling of state 0 and connection release is in netrom.c. - */ -static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype) -{ - struct nr_sock *nrom = nr_sk(sk); - struct sk_buff_head temp_queue; - struct sk_buff *skbn; - unsigned short save_vr; - unsigned short nr, ns; - int queued = 0; - - nr = skb->data[18]; - - switch (frametype) { - case NR_CONNREQ: - nr_write_internal(sk, NR_CONNACK); - break; - - case NR_DISCREQ: - nr_write_internal(sk, NR_DISCACK); - nr_disconnect(sk, 0); - break; - - case NR_CONNACK | NR_CHOKE_FLAG: - case NR_DISCACK: - nr_disconnect(sk, ECONNRESET); - break; - - case NR_INFOACK: - case NR_INFOACK | NR_CHOKE_FLAG: - case NR_INFOACK | NR_NAK_FLAG: - case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG: - if (frametype & NR_CHOKE_FLAG) { - nrom->condition |= NR_COND_PEER_RX_BUSY; - nr_start_t4timer(sk); - } else { - nrom->condition &= ~NR_COND_PEER_RX_BUSY; - nr_stop_t4timer(sk); - } - if (!nr_validate_nr(sk, nr)) { - break; - } - if (frametype & NR_NAK_FLAG) { - nr_frames_acked(sk, nr); - nr_send_nak_frame(sk); - } else { - if (nrom->condition & NR_COND_PEER_RX_BUSY) { - nr_frames_acked(sk, nr); - } else { - nr_check_iframes_acked(sk, nr); - } - } - break; - - case NR_INFO: - case NR_INFO | NR_NAK_FLAG: - case NR_INFO | NR_CHOKE_FLAG: - case NR_INFO | NR_MORE_FLAG: - case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG: - case NR_INFO | NR_CHOKE_FLAG | NR_MORE_FLAG: - case NR_INFO | NR_NAK_FLAG | NR_MORE_FLAG: - case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG: - if (frametype & NR_CHOKE_FLAG) { - nrom->condition |= NR_COND_PEER_RX_BUSY; - nr_start_t4timer(sk); - } else { - nrom->condition &= ~NR_COND_PEER_RX_BUSY; - nr_stop_t4timer(sk); - } - if (nr_validate_nr(sk, nr)) { - if (frametype & NR_NAK_FLAG) { - nr_frames_acked(sk, nr); - nr_send_nak_frame(sk); - } else { - if (nrom->condition & NR_COND_PEER_RX_BUSY) { - nr_frames_acked(sk, nr); - } else { - nr_check_iframes_acked(sk, nr); - } - } - } - queued = 1; - skb_queue_head(&nrom->reseq_queue, skb); - if (nrom->condition & NR_COND_OWN_RX_BUSY) - break; - skb_queue_head_init(&temp_queue); - do { - save_vr = nrom->vr; - while ((skbn = skb_dequeue(&nrom->reseq_queue)) != NULL) { - ns = skbn->data[17]; - if (ns == nrom->vr) { - if (nr_queue_rx_frame(sk, skbn, frametype & NR_MORE_FLAG) == 0) { - nrom->vr = (nrom->vr + 1) % NR_MODULUS; - } else { - nrom->condition |= NR_COND_OWN_RX_BUSY; - skb_queue_tail(&temp_queue, skbn); - } - } else if (nr_in_rx_window(sk, ns)) { - skb_queue_tail(&temp_queue, skbn); - } else { - kfree_skb(skbn); - } - } - while ((skbn = skb_dequeue(&temp_queue)) != NULL) { - skb_queue_tail(&nrom->reseq_queue, skbn); - } - } while (save_vr != nrom->vr); - /* - * Window is full, ack it immediately. - */ - if (((nrom->vl + nrom->window) % NR_MODULUS) == nrom->vr) { - nr_enquiry_response(sk); - } else { - if (!(nrom->condition & NR_COND_ACK_PENDING)) { - nrom->condition |= NR_COND_ACK_PENDING; - nr_start_t2timer(sk); - } - } - break; - - case NR_RESET: - if (READ_ONCE(sysctl_netrom_reset_circuit)) - nr_disconnect(sk, ECONNRESET); - break; - - default: - break; - } - return queued; -} - -/* Higher level upcall for a LAPB frame - called with sk locked */ -int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb) -{ - struct nr_sock *nr = nr_sk(sk); - int queued = 0, frametype; - - if (nr->state == NR_STATE_0) - return 0; - - frametype = skb->data[19]; - - switch (nr->state) { - case NR_STATE_1: - queued = nr_state1_machine(sk, skb, frametype); - break; - case NR_STATE_2: - queued = nr_state2_machine(sk, skb, frametype); - break; - case NR_STATE_3: - queued = nr_state3_machine(sk, skb, frametype); - break; - } - - nr_kick(sk); - - return queued; -} diff --git a/net/netrom/nr_loopback.c b/net/netrom/nr_loopback.c deleted file mode 100644 index 7a9d765b30c0..000000000000 --- a/net/netrom/nr_loopback.c +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Tomi Manninen OH2BNS (oh2bns@sral.fi) - */ -#include -#include -#include -#include -#include -#include -#include -#include - -static void nr_loopback_timer(struct timer_list *); - -static struct sk_buff_head loopback_queue; -static DEFINE_TIMER(loopback_timer, nr_loopback_timer); - -void __init nr_loopback_init(void) -{ - skb_queue_head_init(&loopback_queue); -} - -static inline int nr_loopback_running(void) -{ - return timer_pending(&loopback_timer); -} - -int nr_loopback_queue(struct sk_buff *skb) -{ - struct sk_buff *skbn; - - if ((skbn = alloc_skb(skb->len, GFP_ATOMIC)) != NULL) { - skb_copy_from_linear_data(skb, skb_put(skbn, skb->len), skb->len); - skb_reset_transport_header(skbn); - - skb_queue_tail(&loopback_queue, skbn); - - if (!nr_loopback_running()) - mod_timer(&loopback_timer, jiffies + 10); - } - - kfree_skb(skb); - return 1; -} - -static void nr_loopback_timer(struct timer_list *unused) -{ - struct sk_buff *skb; - ax25_address *nr_dest; - struct net_device *dev; - - if ((skb = skb_dequeue(&loopback_queue)) != NULL) { - nr_dest = (ax25_address *)(skb->data + 7); - - dev = nr_dev_get(nr_dest); - - if (dev == NULL || nr_rx_frame(skb, dev) == 0) - kfree_skb(skb); - - dev_put(dev); - - if (!skb_queue_empty(&loopback_queue) && !nr_loopback_running()) - mod_timer(&loopback_timer, jiffies + 10); - } -} - -void nr_loopback_clear(void) -{ - timer_delete_sync(&loopback_timer); - skb_queue_purge(&loopback_queue); -} diff --git a/net/netrom/nr_out.c b/net/netrom/nr_out.c deleted file mode 100644 index 2b3cbceb0b52..000000000000 --- a/net/netrom/nr_out.c +++ /dev/null @@ -1,272 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * This is where all NET/ROM frames pass, except for IP-over-NET/ROM which - * cannot be fragmented in this manner. - */ -void nr_output(struct sock *sk, struct sk_buff *skb) -{ - struct sk_buff *skbn; - unsigned char transport[NR_TRANSPORT_LEN]; - int err, frontlen, len; - - if (skb->len - NR_TRANSPORT_LEN > NR_MAX_PACKET_SIZE) { - /* Save a copy of the Transport Header */ - skb_copy_from_linear_data(skb, transport, NR_TRANSPORT_LEN); - skb_pull(skb, NR_TRANSPORT_LEN); - - frontlen = skb_headroom(skb); - - while (skb->len > 0) { - if ((skbn = sock_alloc_send_skb(sk, frontlen + NR_MAX_PACKET_SIZE, 0, &err)) == NULL) { - kfree_skb(skb); - return; - } - - skb_reserve(skbn, frontlen); - - len = (NR_MAX_PACKET_SIZE > skb->len) ? skb->len : NR_MAX_PACKET_SIZE; - - /* Copy the user data */ - skb_copy_from_linear_data(skb, skb_put(skbn, len), len); - skb_pull(skb, len); - - /* Duplicate the Transport Header */ - skb_push(skbn, NR_TRANSPORT_LEN); - skb_copy_to_linear_data(skbn, transport, - NR_TRANSPORT_LEN); - if (skb->len > 0) - skbn->data[4] |= NR_MORE_FLAG; - - skb_queue_tail(&sk->sk_write_queue, skbn); /* Throw it on the queue */ - } - - kfree_skb(skb); - } else { - skb_queue_tail(&sk->sk_write_queue, skb); /* Throw it on the queue */ - } - - nr_kick(sk); -} - -/* - * This procedure is passed a buffer descriptor for an iframe. It builds - * the rest of the control part of the frame and then writes it out. - */ -static void nr_send_iframe(struct sock *sk, struct sk_buff *skb) -{ - struct nr_sock *nr = nr_sk(sk); - - if (skb == NULL) - return; - - skb->data[2] = nr->vs; - skb->data[3] = nr->vr; - - if (nr->condition & NR_COND_OWN_RX_BUSY) - skb->data[4] |= NR_CHOKE_FLAG; - - nr_start_idletimer(sk); - - nr_transmit_buffer(sk, skb); -} - -void nr_send_nak_frame(struct sock *sk) -{ - struct sk_buff *skb, *skbn; - struct nr_sock *nr = nr_sk(sk); - - if ((skb = skb_peek(&nr->ack_queue)) == NULL) - return; - - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) - return; - - skbn->data[2] = nr->va; - skbn->data[3] = nr->vr; - - if (nr->condition & NR_COND_OWN_RX_BUSY) - skbn->data[4] |= NR_CHOKE_FLAG; - - nr_transmit_buffer(sk, skbn); - - nr->condition &= ~NR_COND_ACK_PENDING; - nr->vl = nr->vr; - - nr_stop_t1timer(sk); -} - -void nr_kick(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - struct sk_buff *skb, *skbn; - unsigned short start, end; - - if (nr->state != NR_STATE_3) - return; - - if (nr->condition & NR_COND_PEER_RX_BUSY) - return; - - if (!skb_peek(&sk->sk_write_queue)) - return; - - start = (skb_peek(&nr->ack_queue) == NULL) ? nr->va : nr->vs; - end = (nr->va + nr->window) % NR_MODULUS; - - if (start == end) - return; - - nr->vs = start; - - /* - * Transmit data until either we're out of data to send or - * the window is full. - */ - - /* - * Dequeue the frame and copy it. - */ - skb = skb_dequeue(&sk->sk_write_queue); - - do { - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { - skb_queue_head(&sk->sk_write_queue, skb); - break; - } - - skb_set_owner_w(skbn, sk); - - /* - * Transmit the frame copy. - */ - nr_send_iframe(sk, skbn); - - nr->vs = (nr->vs + 1) % NR_MODULUS; - - /* - * Requeue the original data frame. - */ - skb_queue_tail(&nr->ack_queue, skb); - - } while (nr->vs != end && - (skb = skb_dequeue(&sk->sk_write_queue)) != NULL); - - nr->vl = nr->vr; - nr->condition &= ~NR_COND_ACK_PENDING; - - if (!nr_t1timer_running(sk)) - nr_start_t1timer(sk); -} - -void nr_transmit_buffer(struct sock *sk, struct sk_buff *skb) -{ - struct nr_sock *nr = nr_sk(sk); - unsigned char *dptr; - - /* - * Add the protocol byte and network header. - */ - dptr = skb_push(skb, NR_NETWORK_LEN); - - memcpy(dptr, &nr->source_addr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] &= ~AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - - memcpy(dptr, &nr->dest_addr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] |= AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - - *dptr++ = READ_ONCE(sysctl_netrom_network_ttl_initialiser); - - if (!nr_route_frame(skb, NULL)) { - kfree_skb(skb); - nr_disconnect(sk, ENETUNREACH); - } -} - -/* - * The following routines are taken from page 170 of the 7th ARRL Computer - * Networking Conference paper, as is the whole state machine. - */ - -void nr_establish_data_link(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - nr->condition = 0x00; - nr->n2count = 0; - - nr_write_internal(sk, NR_CONNREQ); - - nr_stop_t2timer(sk); - nr_stop_t4timer(sk); - nr_stop_idletimer(sk); - nr_start_t1timer(sk); -} - -/* - * Never send a NAK when we are CHOKEd. - */ -void nr_enquiry_response(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - int frametype = NR_INFOACK; - - if (nr->condition & NR_COND_OWN_RX_BUSY) { - frametype |= NR_CHOKE_FLAG; - } else { - if (skb_peek(&nr->reseq_queue) != NULL) - frametype |= NR_NAK_FLAG; - } - - nr_write_internal(sk, frametype); - - nr->vl = nr->vr; - nr->condition &= ~NR_COND_ACK_PENDING; -} - -void nr_check_iframes_acked(struct sock *sk, unsigned short nr) -{ - struct nr_sock *nrom = nr_sk(sk); - - if (nrom->vs == nr) { - nr_frames_acked(sk, nr); - nr_stop_t1timer(sk); - nrom->n2count = 0; - } else { - if (nrom->va != nr) { - nr_frames_acked(sk, nr); - nr_start_t1timer(sk); - } - } -} diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c deleted file mode 100644 index 9cc29ae85b06..000000000000 --- a/net/netrom/nr_route.c +++ /dev/null @@ -1,989 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright Tomi Manninen OH2BNS (oh2bns@sral.fi) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For TIOCINQ/OUTQ */ -#include -#include -#include -#include -#include -#include -#include -#include - -static unsigned int nr_neigh_no = 1; - -static HLIST_HEAD(nr_node_list); -static DEFINE_SPINLOCK(nr_node_list_lock); -static HLIST_HEAD(nr_neigh_list); -static DEFINE_SPINLOCK(nr_neigh_list_lock); - -static struct nr_node *nr_node_get(ax25_address *callsign) -{ - struct nr_node *found = NULL; - struct nr_node *nr_node; - - spin_lock_bh(&nr_node_list_lock); - nr_node_for_each(nr_node, &nr_node_list) - if (ax25cmp(callsign, &nr_node->callsign) == 0) { - nr_node_hold(nr_node); - found = nr_node; - break; - } - spin_unlock_bh(&nr_node_list_lock); - return found; -} - -static struct nr_neigh *nr_neigh_get_dev(ax25_address *callsign, - struct net_device *dev) -{ - struct nr_neigh *found = NULL; - struct nr_neigh *nr_neigh; - - spin_lock_bh(&nr_neigh_list_lock); - nr_neigh_for_each(nr_neigh, &nr_neigh_list) - if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && - nr_neigh->dev == dev) { - nr_neigh_hold(nr_neigh); - found = nr_neigh; - break; - } - spin_unlock_bh(&nr_neigh_list_lock); - return found; -} - -static void nr_remove_neigh(struct nr_neigh *); - -/* re-sort the routes in quality order. */ -static void re_sort_routes(struct nr_node *nr_node, int x, int y) -{ - if (nr_node->routes[y].quality > nr_node->routes[x].quality) { - if (nr_node->which == x) - nr_node->which = y; - else if (nr_node->which == y) - nr_node->which = x; - - swap(nr_node->routes[x], nr_node->routes[y]); - } -} - -/* - * Add a new route to a node, and in the process add the node and the - * neighbour if it is new. - */ -static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic, - ax25_address *ax25, ax25_digi *ax25_digi, struct net_device *dev, - int quality, int obs_count) -{ - struct nr_node *nr_node; - struct nr_neigh *nr_neigh; - int i, found; - struct net_device *odev; - - if ((odev=nr_dev_get(nr)) != NULL) { /* Can't add routes to ourself */ - dev_put(odev); - return -EINVAL; - } - - nr_node = nr_node_get(nr); - - nr_neigh = nr_neigh_get_dev(ax25, dev); - - /* - * The L2 link to a neighbour has failed in the past - * and now a frame comes from this neighbour. We assume - * it was a temporary trouble with the link and reset the - * routes now (and not wait for a node broadcast). - */ - if (nr_neigh != NULL && nr_neigh->failed != 0 && quality == 0) { - struct nr_node *nr_nodet; - - spin_lock_bh(&nr_node_list_lock); - nr_node_for_each(nr_nodet, &nr_node_list) { - nr_node_lock(nr_nodet); - for (i = 0; i < nr_nodet->count; i++) - if (nr_nodet->routes[i].neighbour == nr_neigh) - if (i < nr_nodet->which) - nr_nodet->which = i; - nr_node_unlock(nr_nodet); - } - spin_unlock_bh(&nr_node_list_lock); - } - - if (nr_neigh != NULL) - nr_neigh->failed = 0; - - if (quality == 0 && nr_neigh != NULL && nr_node != NULL) { - nr_neigh_put(nr_neigh); - nr_node_put(nr_node); - return 0; - } - - if (nr_neigh == NULL) { - if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) { - if (nr_node) - nr_node_put(nr_node); - return -ENOMEM; - } - - nr_neigh->callsign = *ax25; - nr_neigh->digipeat = NULL; - nr_neigh->ax25 = NULL; - nr_neigh->dev = dev; - nr_neigh->quality = READ_ONCE(sysctl_netrom_default_path_quality); - nr_neigh->locked = 0; - nr_neigh->count = 0; - nr_neigh->number = nr_neigh_no++; - nr_neigh->failed = 0; - refcount_set(&nr_neigh->refcount, 1); - - if (ax25_digi != NULL && ax25_digi->ndigi > 0) { - nr_neigh->digipeat = kmemdup(ax25_digi, - sizeof(*ax25_digi), - GFP_KERNEL); - if (nr_neigh->digipeat == NULL) { - kfree(nr_neigh); - if (nr_node) - nr_node_put(nr_node); - return -ENOMEM; - } - } - - spin_lock_bh(&nr_neigh_list_lock); - hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list); - nr_neigh_hold(nr_neigh); - spin_unlock_bh(&nr_neigh_list_lock); - } - - if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked) - nr_neigh->quality = quality; - - if (nr_node == NULL) { - if ((nr_node = kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL) { - if (nr_neigh) - nr_neigh_put(nr_neigh); - return -ENOMEM; - } - - nr_node->callsign = *nr; - strscpy(nr_node->mnemonic, mnemonic); - - nr_node->which = 0; - nr_node->count = 1; - refcount_set(&nr_node->refcount, 1); - spin_lock_init(&nr_node->node_lock); - - nr_node->routes[0].quality = quality; - nr_node->routes[0].obs_count = obs_count; - nr_node->routes[0].neighbour = nr_neigh; - - nr_neigh_hold(nr_neigh); - nr_neigh->count++; - - spin_lock_bh(&nr_node_list_lock); - hlist_add_head(&nr_node->node_node, &nr_node_list); - /* refcount initialized at 1 */ - spin_unlock_bh(&nr_node_list_lock); - - nr_neigh_put(nr_neigh); - return 0; - } - nr_node_lock(nr_node); - - if (quality != 0) - strscpy(nr_node->mnemonic, mnemonic); - - for (found = 0, i = 0; i < nr_node->count; i++) { - if (nr_node->routes[i].neighbour == nr_neigh) { - nr_node->routes[i].quality = quality; - nr_node->routes[i].obs_count = obs_count; - found = 1; - break; - } - } - - if (!found) { - /* We have space at the bottom, slot it in */ - if (nr_node->count < 3) { - nr_node->routes[2] = nr_node->routes[1]; - nr_node->routes[1] = nr_node->routes[0]; - - nr_node->routes[0].quality = quality; - nr_node->routes[0].obs_count = obs_count; - nr_node->routes[0].neighbour = nr_neigh; - - nr_node->which++; - nr_node->count++; - nr_neigh_hold(nr_neigh); - nr_neigh->count++; - } else { - /* It must be better than the worst */ - if (quality > nr_node->routes[2].quality) { - nr_node->routes[2].neighbour->count--; - nr_neigh_put(nr_node->routes[2].neighbour); - - if (nr_node->routes[2].neighbour->count == 0 && !nr_node->routes[2].neighbour->locked) - nr_remove_neigh(nr_node->routes[2].neighbour); - - nr_node->routes[2].quality = quality; - nr_node->routes[2].obs_count = obs_count; - nr_node->routes[2].neighbour = nr_neigh; - - nr_neigh_hold(nr_neigh); - nr_neigh->count++; - } - } - } - - /* Now re-sort the routes in quality order */ - switch (nr_node->count) { - case 3: - re_sort_routes(nr_node, 0, 1); - re_sort_routes(nr_node, 1, 2); - fallthrough; - case 2: - re_sort_routes(nr_node, 0, 1); - break; - case 1: - break; - } - - for (i = 0; i < nr_node->count; i++) { - if (nr_node->routes[i].neighbour == nr_neigh) { - if (i < nr_node->which) - nr_node->which = i; - break; - } - } - - nr_neigh_put(nr_neigh); - nr_node_unlock(nr_node); - nr_node_put(nr_node); - return 0; -} - -static void nr_remove_node_locked(struct nr_node *nr_node) -{ - lockdep_assert_held(&nr_node_list_lock); - - hlist_del_init(&nr_node->node_node); - nr_node_put(nr_node); -} - -static inline void __nr_remove_neigh(struct nr_neigh *nr_neigh) -{ - hlist_del_init(&nr_neigh->neigh_node); - nr_neigh_put(nr_neigh); -} - -#define nr_remove_neigh_locked(__neigh) \ - __nr_remove_neigh(__neigh) - -static void nr_remove_neigh(struct nr_neigh *nr_neigh) -{ - spin_lock_bh(&nr_neigh_list_lock); - __nr_remove_neigh(nr_neigh); - spin_unlock_bh(&nr_neigh_list_lock); -} - -/* - * "Delete" a node. Strictly speaking remove a route to a node. The node - * is only deleted if no routes are left to it. - */ -static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct net_device *dev) -{ - struct nr_node *nr_node; - struct nr_neigh *nr_neigh; - int i; - - nr_node = nr_node_get(callsign); - - if (nr_node == NULL) - return -EINVAL; - - nr_neigh = nr_neigh_get_dev(neighbour, dev); - - if (nr_neigh == NULL) { - nr_node_put(nr_node); - return -EINVAL; - } - - spin_lock_bh(&nr_node_list_lock); - nr_node_lock(nr_node); - for (i = 0; i < nr_node->count; i++) { - if (nr_node->routes[i].neighbour == nr_neigh) { - nr_neigh->count--; - nr_neigh_put(nr_neigh); - - if (nr_neigh->count == 0 && !nr_neigh->locked) - nr_remove_neigh(nr_neigh); - nr_neigh_put(nr_neigh); - - nr_node->count--; - - if (nr_node->count == 0) { - nr_remove_node_locked(nr_node); - } else { - switch (i) { - case 0: - nr_node->routes[0] = nr_node->routes[1]; - fallthrough; - case 1: - nr_node->routes[1] = nr_node->routes[2]; - fallthrough; - case 2: - break; - } - nr_node_put(nr_node); - } - nr_node_unlock(nr_node); - spin_unlock_bh(&nr_node_list_lock); - - return 0; - } - } - nr_neigh_put(nr_neigh); - nr_node_unlock(nr_node); - spin_unlock_bh(&nr_node_list_lock); - nr_node_put(nr_node); - - return -EINVAL; -} - -/* - * Lock a neighbour with a quality. - */ -static int __must_check nr_add_neigh(ax25_address *callsign, - ax25_digi *ax25_digi, struct net_device *dev, unsigned int quality) -{ - struct nr_neigh *nr_neigh; - - nr_neigh = nr_neigh_get_dev(callsign, dev); - if (nr_neigh) { - nr_neigh->quality = quality; - nr_neigh->locked = 1; - nr_neigh_put(nr_neigh); - return 0; - } - - if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) - return -ENOMEM; - - nr_neigh->callsign = *callsign; - nr_neigh->digipeat = NULL; - nr_neigh->ax25 = NULL; - nr_neigh->dev = dev; - nr_neigh->quality = quality; - nr_neigh->locked = 1; - nr_neigh->count = 0; - nr_neigh->number = nr_neigh_no++; - nr_neigh->failed = 0; - refcount_set(&nr_neigh->refcount, 1); - - if (ax25_digi != NULL && ax25_digi->ndigi > 0) { - nr_neigh->digipeat = kmemdup(ax25_digi, sizeof(*ax25_digi), - GFP_KERNEL); - if (nr_neigh->digipeat == NULL) { - kfree(nr_neigh); - return -ENOMEM; - } - } - - spin_lock_bh(&nr_neigh_list_lock); - hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list); - /* refcount is initialized at 1 */ - spin_unlock_bh(&nr_neigh_list_lock); - - return 0; -} - -/* - * "Delete" a neighbour. The neighbour is only removed if the number - * of nodes that may use it is zero. - */ -static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned int quality) -{ - struct nr_neigh *nr_neigh; - - nr_neigh = nr_neigh_get_dev(callsign, dev); - - if (nr_neigh == NULL) return -EINVAL; - - nr_neigh->quality = quality; - nr_neigh->locked = 0; - - if (nr_neigh->count == 0) - nr_remove_neigh(nr_neigh); - nr_neigh_put(nr_neigh); - - return 0; -} - -/* - * Decrement the obsolescence count by one. If a route is reduced to a - * count of zero, remove it. Also remove any unlocked neighbours with - * zero nodes routing via it. - */ -static int nr_dec_obs(void) -{ - struct nr_neigh *nr_neigh; - struct nr_node *s; - struct hlist_node *nodet; - int i; - - spin_lock_bh(&nr_node_list_lock); - nr_node_for_each_safe(s, nodet, &nr_node_list) { - nr_node_lock(s); - for (i = 0; i < s->count; i++) { - switch (s->routes[i].obs_count) { - case 0: /* A locked entry */ - break; - - case 1: /* From 1 -> 0 */ - nr_neigh = s->routes[i].neighbour; - - nr_neigh->count--; - nr_neigh_put(nr_neigh); - - if (nr_neigh->count == 0 && !nr_neigh->locked) - nr_remove_neigh(nr_neigh); - - s->count--; - - switch (i) { - case 0: - s->routes[0] = s->routes[1]; - fallthrough; - case 1: - s->routes[1] = s->routes[2]; - break; - case 2: - break; - } - break; - - default: - s->routes[i].obs_count--; - break; - - } - } - - if (s->count <= 0) - nr_remove_node_locked(s); - nr_node_unlock(s); - } - spin_unlock_bh(&nr_node_list_lock); - - return 0; -} - -/* - * A device has been removed. Remove its routes and neighbours. - */ -void nr_rt_device_down(struct net_device *dev) -{ - struct nr_neigh *s; - struct hlist_node *nodet, *node2t; - struct nr_node *t; - int i; - - spin_lock_bh(&nr_neigh_list_lock); - nr_neigh_for_each_safe(s, nodet, &nr_neigh_list) { - if (s->dev == dev) { - spin_lock_bh(&nr_node_list_lock); - nr_node_for_each_safe(t, node2t, &nr_node_list) { - nr_node_lock(t); - for (i = 0; i < t->count; i++) { - if (t->routes[i].neighbour == s) { - t->count--; - - switch (i) { - case 0: - t->routes[0] = t->routes[1]; - fallthrough; - case 1: - t->routes[1] = t->routes[2]; - break; - case 2: - break; - } - } - } - - if (t->count <= 0) - nr_remove_node_locked(t); - nr_node_unlock(t); - } - spin_unlock_bh(&nr_node_list_lock); - - nr_remove_neigh_locked(s); - } - } - spin_unlock_bh(&nr_neigh_list_lock); -} - -/* - * Check that the device given is a valid AX.25 interface that is "up". - * Or a valid ethernet interface with an AX.25 callsign binding. - */ -static struct net_device *nr_ax25_dev_get(char *devname) -{ - struct net_device *dev; - - if ((dev = dev_get_by_name(&init_net, devname)) == NULL) - return NULL; - - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25) - return dev; - - dev_put(dev); - return NULL; -} - -/* - * Find the first active NET/ROM device, usually "nr0". - */ -struct net_device *nr_dev_first(void) -{ - struct net_device *dev, *first = NULL; - - rcu_read_lock(); - for_each_netdev_rcu(&init_net, dev) { - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM) - if (first == NULL || strncmp(dev->name, first->name, 3) < 0) - first = dev; - } - dev_hold(first); - rcu_read_unlock(); - - return first; -} - -/* - * Find the NET/ROM device for the given callsign. - */ -struct net_device *nr_dev_get(ax25_address *addr) -{ - struct net_device *dev; - - rcu_read_lock(); - for_each_netdev_rcu(&init_net, dev) { - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && - ax25cmp(addr, (const ax25_address *)dev->dev_addr) == 0) { - dev_hold(dev); - goto out; - } - } - dev = NULL; -out: - rcu_read_unlock(); - return dev; -} - -static ax25_digi *nr_call_to_digi(ax25_digi *digi, int ndigis, - ax25_address *digipeaters) -{ - int i; - - if (ndigis == 0) - return NULL; - - for (i = 0; i < ndigis; i++) { - digi->calls[i] = digipeaters[i]; - digi->repeated[i] = 0; - } - - digi->ndigi = ndigis; - digi->lastrepeat = -1; - - return digi; -} - -/* - * Handle the ioctls that control the routing functions. - */ -int nr_rt_ioctl(unsigned int cmd, void __user *arg) -{ - struct nr_route_struct nr_route; - struct net_device *dev; - ax25_digi digi; - int ret; - - switch (cmd) { - case SIOCADDRT: - if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) - return -EFAULT; - if (nr_route.ndigis > AX25_MAX_DIGIS) - return -EINVAL; - if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) - return -EINVAL; - switch (nr_route.type) { - case NETROM_NODE: - if (strnlen(nr_route.mnemonic, 7) == 7) { - ret = -EINVAL; - break; - } - - ret = nr_add_node(&nr_route.callsign, - nr_route.mnemonic, - &nr_route.neighbour, - nr_call_to_digi(&digi, nr_route.ndigis, - nr_route.digipeaters), - dev, nr_route.quality, - nr_route.obs_count); - break; - case NETROM_NEIGH: - ret = nr_add_neigh(&nr_route.callsign, - nr_call_to_digi(&digi, nr_route.ndigis, - nr_route.digipeaters), - dev, nr_route.quality); - break; - default: - ret = -EINVAL; - } - dev_put(dev); - return ret; - - case SIOCDELRT: - if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) - return -EFAULT; - if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) - return -EINVAL; - switch (nr_route.type) { - case NETROM_NODE: - ret = nr_del_node(&nr_route.callsign, - &nr_route.neighbour, dev); - break; - case NETROM_NEIGH: - ret = nr_del_neigh(&nr_route.callsign, - dev, nr_route.quality); - break; - default: - ret = -EINVAL; - } - dev_put(dev); - return ret; - - case SIOCNRDECOBS: - return nr_dec_obs(); - - default: - return -EINVAL; - } - - return 0; -} - -/* - * A level 2 link has timed out, therefore it appears to be a poor link, - * then don't use that neighbour until it is reset. - */ -void nr_link_failed(ax25_cb *ax25, int reason) -{ - struct nr_neigh *s, *nr_neigh = NULL; - struct nr_node *nr_node = NULL; - - spin_lock_bh(&nr_neigh_list_lock); - nr_neigh_for_each(s, &nr_neigh_list) { - if (s->ax25 == ax25) { - nr_neigh_hold(s); - nr_neigh = s; - break; - } - } - spin_unlock_bh(&nr_neigh_list_lock); - - if (nr_neigh == NULL) - return; - - nr_neigh->ax25 = NULL; - ax25_cb_put(ax25); - - if (++nr_neigh->failed < READ_ONCE(sysctl_netrom_link_fails_count)) { - nr_neigh_put(nr_neigh); - return; - } - spin_lock_bh(&nr_node_list_lock); - nr_node_for_each(nr_node, &nr_node_list) { - nr_node_lock(nr_node); - if (nr_node->which < nr_node->count && - nr_node->routes[nr_node->which].neighbour == nr_neigh) - nr_node->which++; - nr_node_unlock(nr_node); - } - spin_unlock_bh(&nr_node_list_lock); - nr_neigh_put(nr_neigh); -} - -/* - * Route a frame to an appropriate AX.25 connection. A NULL ax25_cb - * indicates an internally generated frame. - */ -int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25) -{ - ax25_address *nr_src, *nr_dest; - struct nr_neigh *nr_neigh; - struct nr_node *nr_node; - struct net_device *dev; - unsigned char *dptr; - ax25_cb *ax25s; - int ret; - struct sk_buff *nskb, *oskb; - - /* - * Reject malformed packets early. Check that it contains at least 2 - * addresses and 1 byte more for Time-To-Live - */ - if (skb->len < 2 * sizeof(ax25_address) + 1) - return 0; - - nr_src = (ax25_address *)(skb->data + 0); - nr_dest = (ax25_address *)(skb->data + 7); - - if (ax25 != NULL) { - ret = nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat, - ax25->ax25_dev->dev, 0, - READ_ONCE(sysctl_netrom_obsolescence_count_initialiser)); - if (ret) - return ret; - } - - if ((dev = nr_dev_get(nr_dest)) != NULL) { /* Its for me */ - if (ax25 == NULL) /* Its from me */ - ret = nr_loopback_queue(skb); - else - ret = nr_rx_frame(skb, dev); - dev_put(dev); - return ret; - } - - if (!READ_ONCE(sysctl_netrom_routing_control) && ax25 != NULL) - return 0; - - /* Its Time-To-Live has expired */ - if (skb->data[14] == 1) { - return 0; - } - - nr_node = nr_node_get(nr_dest); - if (nr_node == NULL) - return 0; - nr_node_lock(nr_node); - - if (nr_node->which >= nr_node->count) { - nr_node_unlock(nr_node); - nr_node_put(nr_node); - return 0; - } - - nr_neigh = nr_node->routes[nr_node->which].neighbour; - - if ((dev = nr_dev_first()) == NULL) { - nr_node_unlock(nr_node); - nr_node_put(nr_node); - return 0; - } - - /* We are going to change the netrom headers so we should get our - own skb, we also did not know until now how much header space - we had to reserve... - RXQ */ - nskb = skb_copy_expand(skb, dev->hard_header_len, 0, GFP_ATOMIC); - - if (!nskb) { - nr_node_unlock(nr_node); - nr_node_put(nr_node); - dev_put(dev); - return 0; - } - oskb = skb; - skb = nskb; - skb->data[14]--; - - dptr = skb_push(skb, 1); - *dptr = AX25_P_NETROM; - - ax25s = nr_neigh->ax25; - nr_neigh->ax25 = ax25_send_frame(skb, 256, - (const ax25_address *)dev->dev_addr, - &nr_neigh->callsign, - nr_neigh->digipeat, nr_neigh->dev); - if (ax25s) - ax25_cb_put(ax25s); - - dev_put(dev); - ret = (nr_neigh->ax25 != NULL); - nr_node_unlock(nr_node); - nr_node_put(nr_node); - - if (ret) - kfree_skb(oskb); - - return ret; -} - -#ifdef CONFIG_PROC_FS - -static void *nr_node_start(struct seq_file *seq, loff_t *pos) - __acquires(&nr_node_list_lock) -{ - spin_lock_bh(&nr_node_list_lock); - return seq_hlist_start_head(&nr_node_list, *pos); -} - -static void *nr_node_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &nr_node_list, pos); -} - -static void nr_node_stop(struct seq_file *seq, void *v) - __releases(&nr_node_list_lock) -{ - spin_unlock_bh(&nr_node_list_lock); -} - -static int nr_node_show(struct seq_file *seq, void *v) -{ - char buf[11]; - int i; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "callsign mnemonic w n qual obs neigh qual obs neigh qual obs neigh\n"); - else { - struct nr_node *nr_node = hlist_entry(v, struct nr_node, - node_node); - - nr_node_lock(nr_node); - seq_printf(seq, "%-9s %-7s %d %d", - ax2asc(buf, &nr_node->callsign), - (nr_node->mnemonic[0] == '\0') ? "*" : nr_node->mnemonic, - nr_node->which + 1, - nr_node->count); - - for (i = 0; i < nr_node->count; i++) { - seq_printf(seq, " %3d %d %05d", - nr_node->routes[i].quality, - nr_node->routes[i].obs_count, - nr_node->routes[i].neighbour->number); - } - nr_node_unlock(nr_node); - - seq_puts(seq, "\n"); - } - return 0; -} - -const struct seq_operations nr_node_seqops = { - .start = nr_node_start, - .next = nr_node_next, - .stop = nr_node_stop, - .show = nr_node_show, -}; - -static void *nr_neigh_start(struct seq_file *seq, loff_t *pos) - __acquires(&nr_neigh_list_lock) -{ - spin_lock_bh(&nr_neigh_list_lock); - return seq_hlist_start_head(&nr_neigh_list, *pos); -} - -static void *nr_neigh_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &nr_neigh_list, pos); -} - -static void nr_neigh_stop(struct seq_file *seq, void *v) - __releases(&nr_neigh_list_lock) -{ - spin_unlock_bh(&nr_neigh_list_lock); -} - -static int nr_neigh_show(struct seq_file *seq, void *v) -{ - char buf[11]; - int i; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, "addr callsign dev qual lock count failed digipeaters\n"); - else { - struct nr_neigh *nr_neigh; - - nr_neigh = hlist_entry(v, struct nr_neigh, neigh_node); - seq_printf(seq, "%05d %-9s %-4s %3d %d %3d %3d", - nr_neigh->number, - ax2asc(buf, &nr_neigh->callsign), - nr_neigh->dev ? nr_neigh->dev->name : "???", - nr_neigh->quality, - nr_neigh->locked, - nr_neigh->count, - nr_neigh->failed); - - if (nr_neigh->digipeat != NULL) { - for (i = 0; i < nr_neigh->digipeat->ndigi; i++) - seq_printf(seq, " %s", - ax2asc(buf, &nr_neigh->digipeat->calls[i])); - } - - seq_puts(seq, "\n"); - } - return 0; -} - -const struct seq_operations nr_neigh_seqops = { - .start = nr_neigh_start, - .next = nr_neigh_next, - .stop = nr_neigh_stop, - .show = nr_neigh_show, -}; -#endif - -/* - * Free all memory associated with the nodes and routes lists. - */ -void nr_rt_free(void) -{ - struct nr_neigh *s = NULL; - struct nr_node *t = NULL; - struct hlist_node *nodet; - - spin_lock_bh(&nr_neigh_list_lock); - spin_lock_bh(&nr_node_list_lock); - nr_node_for_each_safe(t, nodet, &nr_node_list) { - nr_node_lock(t); - nr_remove_node_locked(t); - nr_node_unlock(t); - } - nr_neigh_for_each_safe(s, nodet, &nr_neigh_list) { - while(s->count) { - s->count--; - nr_neigh_put(s); - } - nr_remove_neigh_locked(s); - } - spin_unlock_bh(&nr_node_list_lock); - spin_unlock_bh(&nr_neigh_list_lock); -} diff --git a/net/netrom/nr_subr.c b/net/netrom/nr_subr.c deleted file mode 100644 index c3bbd5880850..000000000000 --- a/net/netrom/nr_subr.c +++ /dev/null @@ -1,280 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * This routine purges all of the queues of frames. - */ -void nr_clear_queues(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - skb_queue_purge(&sk->sk_write_queue); - skb_queue_purge(&nr->ack_queue); - skb_queue_purge(&nr->reseq_queue); - skb_queue_purge(&nr->frag_queue); -} - -/* - * This routine purges the input queue of those frames that have been - * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the - * SDL diagram. - */ -void nr_frames_acked(struct sock *sk, unsigned short nr) -{ - struct nr_sock *nrom = nr_sk(sk); - struct sk_buff *skb; - - /* - * Remove all the ack-ed frames from the ack queue. - */ - if (nrom->va != nr) { - while (skb_peek(&nrom->ack_queue) != NULL && nrom->va != nr) { - skb = skb_dequeue(&nrom->ack_queue); - kfree_skb(skb); - nrom->va = (nrom->va + 1) % NR_MODULUS; - } - } -} - -/* - * Requeue all the un-ack-ed frames on the output queue to be picked - * up by nr_kick called from the timer. This arrangement handles the - * possibility of an empty output queue. - */ -void nr_requeue_frames(struct sock *sk) -{ - struct sk_buff *skb, *skb_prev = NULL; - - while ((skb = skb_dequeue(&nr_sk(sk)->ack_queue)) != NULL) { - if (skb_prev == NULL) - skb_queue_head(&sk->sk_write_queue, skb); - else - skb_append(skb_prev, skb, &sk->sk_write_queue); - skb_prev = skb; - } -} - -/* - * Validate that the value of nr is between va and vs. Return true or - * false for testing. - */ -int nr_validate_nr(struct sock *sk, unsigned short nr) -{ - struct nr_sock *nrom = nr_sk(sk); - unsigned short vc = nrom->va; - - while (vc != nrom->vs) { - if (nr == vc) return 1; - vc = (vc + 1) % NR_MODULUS; - } - - return nr == nrom->vs; -} - -/* - * Check that ns is within the receive window. - */ -int nr_in_rx_window(struct sock *sk, unsigned short ns) -{ - struct nr_sock *nr = nr_sk(sk); - unsigned short vc = nr->vr; - unsigned short vt = (nr->vl + nr->window) % NR_MODULUS; - - while (vc != vt) { - if (ns == vc) return 1; - vc = (vc + 1) % NR_MODULUS; - } - - return 0; -} - -/* - * This routine is called when the HDLC layer internally generates a - * control frame. - */ -void nr_write_internal(struct sock *sk, int frametype) -{ - struct nr_sock *nr = nr_sk(sk); - struct sk_buff *skb; - unsigned char *dptr; - int len, timeout; - - len = NR_TRANSPORT_LEN; - - switch (frametype & 0x0F) { - case NR_CONNREQ: - len += 17; - break; - case NR_CONNACK: - len += (nr->bpqext) ? 2 : 1; - break; - case NR_DISCREQ: - case NR_DISCACK: - case NR_INFOACK: - break; - default: - printk(KERN_ERR "NET/ROM: nr_write_internal - invalid frame type %d\n", frametype); - return; - } - - skb = alloc_skb(NR_NETWORK_LEN + len, GFP_ATOMIC); - if (!skb) - return; - - /* - * Space for AX.25 and NET/ROM network header - */ - skb_reserve(skb, NR_NETWORK_LEN); - - dptr = skb_put(skb, len); - - switch (frametype & 0x0F) { - case NR_CONNREQ: - timeout = nr->t1 / HZ; - *dptr++ = nr->my_index; - *dptr++ = nr->my_id; - *dptr++ = 0; - *dptr++ = 0; - *dptr++ = frametype; - *dptr++ = nr->window; - memcpy(dptr, &nr->user_addr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] &= ~AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - memcpy(dptr, &nr->source_addr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] &= ~AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - *dptr++ = timeout % 256; - *dptr++ = timeout / 256; - break; - - case NR_CONNACK: - *dptr++ = nr->your_index; - *dptr++ = nr->your_id; - *dptr++ = nr->my_index; - *dptr++ = nr->my_id; - *dptr++ = frametype; - *dptr++ = nr->window; - if (nr->bpqext) - *dptr++ = READ_ONCE(sysctl_netrom_network_ttl_initialiser); - break; - - case NR_DISCREQ: - case NR_DISCACK: - *dptr++ = nr->your_index; - *dptr++ = nr->your_id; - *dptr++ = 0; - *dptr++ = 0; - *dptr++ = frametype; - break; - - case NR_INFOACK: - *dptr++ = nr->your_index; - *dptr++ = nr->your_id; - *dptr++ = 0; - *dptr++ = nr->vr; - *dptr++ = frametype; - break; - } - - nr_transmit_buffer(sk, skb); -} - -/* - * This routine is called to send an error reply. - */ -void __nr_transmit_reply(struct sk_buff *skb, int mine, unsigned char cmdflags) -{ - struct sk_buff *skbn; - unsigned char *dptr; - int len; - - len = NR_NETWORK_LEN + NR_TRANSPORT_LEN + 1; - - if ((skbn = alloc_skb(len, GFP_ATOMIC)) == NULL) - return; - - skb_reserve(skbn, 0); - - dptr = skb_put(skbn, NR_NETWORK_LEN + NR_TRANSPORT_LEN); - - skb_copy_from_linear_data_offset(skb, 7, dptr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] &= ~AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - - skb_copy_from_linear_data(skb, dptr, AX25_ADDR_LEN); - dptr[6] &= ~AX25_CBIT; - dptr[6] |= AX25_EBIT; - dptr[6] |= AX25_SSSID_SPARE; - dptr += AX25_ADDR_LEN; - - *dptr++ = READ_ONCE(sysctl_netrom_network_ttl_initialiser); - - if (mine) { - *dptr++ = 0; - *dptr++ = 0; - *dptr++ = skb->data[15]; - *dptr++ = skb->data[16]; - } else { - *dptr++ = skb->data[15]; - *dptr++ = skb->data[16]; - *dptr++ = 0; - *dptr++ = 0; - } - - *dptr++ = cmdflags; - *dptr++ = 0; - - if (!nr_route_frame(skbn, NULL)) - kfree_skb(skbn); -} - -void nr_disconnect(struct sock *sk, int reason) -{ - nr_stop_t1timer(sk); - nr_stop_t2timer(sk); - nr_stop_t4timer(sk); - nr_stop_idletimer(sk); - - nr_clear_queues(sk); - - nr_sk(sk)->state = NR_STATE_0; - - sk->sk_state = TCP_CLOSE; - sk->sk_err = reason; - sk->sk_shutdown |= SEND_SHUTDOWN; - - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DEAD); - } -} diff --git a/net/netrom/nr_timer.c b/net/netrom/nr_timer.c deleted file mode 100644 index b3a62b1f3a09..000000000000 --- a/net/netrom/nr_timer.c +++ /dev/null @@ -1,249 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void nr_heartbeat_expiry(struct timer_list *); -static void nr_t1timer_expiry(struct timer_list *); -static void nr_t2timer_expiry(struct timer_list *); -static void nr_t4timer_expiry(struct timer_list *); -static void nr_idletimer_expiry(struct timer_list *); - -void nr_init_timers(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - timer_setup(&nr->t1timer, nr_t1timer_expiry, 0); - timer_setup(&nr->t2timer, nr_t2timer_expiry, 0); - timer_setup(&nr->t4timer, nr_t4timer_expiry, 0); - timer_setup(&nr->idletimer, nr_idletimer_expiry, 0); - - /* initialized by sock_init_data */ - sk->sk_timer.function = nr_heartbeat_expiry; -} - -void nr_start_t1timer(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - sk_reset_timer(sk, &nr->t1timer, jiffies + nr->t1); -} - -void nr_start_t2timer(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - sk_reset_timer(sk, &nr->t2timer, jiffies + nr->t2); -} - -void nr_start_t4timer(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - sk_reset_timer(sk, &nr->t4timer, jiffies + nr->t4); -} - -void nr_start_idletimer(struct sock *sk) -{ - struct nr_sock *nr = nr_sk(sk); - - if (nr->idle > 0) - sk_reset_timer(sk, &nr->idletimer, jiffies + nr->idle); -} - -void nr_start_heartbeat(struct sock *sk) -{ - sk_reset_timer(sk, &sk->sk_timer, jiffies + 5 * HZ); -} - -void nr_stop_t1timer(struct sock *sk) -{ - sk_stop_timer(sk, &nr_sk(sk)->t1timer); -} - -void nr_stop_t2timer(struct sock *sk) -{ - sk_stop_timer(sk, &nr_sk(sk)->t2timer); -} - -void nr_stop_t4timer(struct sock *sk) -{ - sk_stop_timer(sk, &nr_sk(sk)->t4timer); -} - -void nr_stop_idletimer(struct sock *sk) -{ - sk_stop_timer(sk, &nr_sk(sk)->idletimer); -} - -void nr_stop_heartbeat(struct sock *sk) -{ - sk_stop_timer(sk, &sk->sk_timer); -} - -int nr_t1timer_running(struct sock *sk) -{ - return timer_pending(&nr_sk(sk)->t1timer); -} - -static void nr_heartbeat_expiry(struct timer_list *t) -{ - struct sock *sk = timer_container_of(sk, t, sk_timer); - struct nr_sock *nr = nr_sk(sk); - - bh_lock_sock(sk); - switch (nr->state) { - case NR_STATE_0: - /* Magic here: If we listen() and a new link dies before it - is accepted() it isn't 'dead' so doesn't get removed. */ - if (sock_flag(sk, SOCK_DESTROY) || - (sk->sk_state == TCP_LISTEN && sock_flag(sk, SOCK_DEAD))) { - if (sk->sk_state == TCP_LISTEN) - sock_hold(sk); - bh_unlock_sock(sk); - nr_destroy_socket(sk); - goto out; - } - break; - - case NR_STATE_3: - /* - * Check for the state of the receive buffer. - */ - if (atomic_read(&sk->sk_rmem_alloc) < (sk->sk_rcvbuf / 2) && - (nr->condition & NR_COND_OWN_RX_BUSY)) { - nr->condition &= ~NR_COND_OWN_RX_BUSY; - nr->condition &= ~NR_COND_ACK_PENDING; - nr->vl = nr->vr; - nr_write_internal(sk, NR_INFOACK); - break; - } - break; - } - - nr_start_heartbeat(sk); - bh_unlock_sock(sk); -out: - sock_put(sk); -} - -static void nr_t2timer_expiry(struct timer_list *t) -{ - struct nr_sock *nr = timer_container_of(nr, t, t2timer); - struct sock *sk = &nr->sock; - - bh_lock_sock(sk); - if (nr->condition & NR_COND_ACK_PENDING) { - nr->condition &= ~NR_COND_ACK_PENDING; - nr_enquiry_response(sk); - } - bh_unlock_sock(sk); - sock_put(sk); -} - -static void nr_t4timer_expiry(struct timer_list *t) -{ - struct nr_sock *nr = timer_container_of(nr, t, t4timer); - struct sock *sk = &nr->sock; - - bh_lock_sock(sk); - nr_sk(sk)->condition &= ~NR_COND_PEER_RX_BUSY; - bh_unlock_sock(sk); - sock_put(sk); -} - -static void nr_idletimer_expiry(struct timer_list *t) -{ - struct nr_sock *nr = timer_container_of(nr, t, idletimer); - struct sock *sk = &nr->sock; - - bh_lock_sock(sk); - - nr_clear_queues(sk); - - nr->n2count = 0; - nr_write_internal(sk, NR_DISCREQ); - nr->state = NR_STATE_2; - - nr_start_t1timer(sk); - nr_stop_t2timer(sk); - nr_stop_t4timer(sk); - - sk->sk_state = TCP_CLOSE; - sk->sk_err = 0; - sk->sk_shutdown |= SEND_SHUTDOWN; - - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DEAD); - } - bh_unlock_sock(sk); - sock_put(sk); -} - -static void nr_t1timer_expiry(struct timer_list *t) -{ - struct nr_sock *nr = timer_container_of(nr, t, t1timer); - struct sock *sk = &nr->sock; - - bh_lock_sock(sk); - switch (nr->state) { - case NR_STATE_1: - if (nr->n2count == nr->n2) { - nr_disconnect(sk, ETIMEDOUT); - goto out; - } else { - nr->n2count++; - nr_write_internal(sk, NR_CONNREQ); - } - break; - - case NR_STATE_2: - if (nr->n2count == nr->n2) { - nr_disconnect(sk, ETIMEDOUT); - goto out; - } else { - nr->n2count++; - nr_write_internal(sk, NR_DISCREQ); - } - break; - - case NR_STATE_3: - if (nr->n2count == nr->n2) { - nr_disconnect(sk, ETIMEDOUT); - goto out; - } else { - nr->n2count++; - nr_requeue_frames(sk); - } - break; - } - - nr_start_t1timer(sk); -out: - bh_unlock_sock(sk); - sock_put(sk); -} diff --git a/net/netrom/sysctl_net_netrom.c b/net/netrom/sysctl_net_netrom.c deleted file mode 100644 index 7dc0fa628f2e..000000000000 --- a/net/netrom/sysctl_net_netrom.c +++ /dev/null @@ -1,156 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com) - */ -#include -#include -#include -#include -#include - -/* - * Values taken from NET/ROM documentation. - */ -static int min_quality[] = {0}, max_quality[] = {255}; -static int min_obs[] = {0}, max_obs[] = {255}; -static int min_ttl[] = {0}, max_ttl[] = {255}; -static int min_t1[] = {5 * HZ}; -static int max_t1[] = {600 * HZ}; -static int min_n2[] = {2}, max_n2[] = {127}; -static int min_t2[] = {1 * HZ}; -static int max_t2[] = {60 * HZ}; -static int min_t4[] = {1 * HZ}; -static int max_t4[] = {1000 * HZ}; -static int min_window[] = {1}, max_window[] = {127}; -static int min_idle[] = {0 * HZ}; -static int max_idle[] = {65535 * HZ}; -static int min_route[] = {0}, max_route[] = {1}; -static int min_fails[] = {1}, max_fails[] = {10}; -static int min_reset[] = {0}, max_reset[] = {1}; - -static struct ctl_table_header *nr_table_header; - -static struct ctl_table nr_table[] = { - { - .procname = "default_path_quality", - .data = &sysctl_netrom_default_path_quality, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_quality, - .extra2 = &max_quality - }, - { - .procname = "obsolescence_count_initialiser", - .data = &sysctl_netrom_obsolescence_count_initialiser, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_obs, - .extra2 = &max_obs - }, - { - .procname = "network_ttl_initialiser", - .data = &sysctl_netrom_network_ttl_initialiser, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_ttl, - .extra2 = &max_ttl - }, - { - .procname = "transport_timeout", - .data = &sysctl_netrom_transport_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_t1, - .extra2 = &max_t1 - }, - { - .procname = "transport_maximum_tries", - .data = &sysctl_netrom_transport_maximum_tries, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_n2, - .extra2 = &max_n2 - }, - { - .procname = "transport_acknowledge_delay", - .data = &sysctl_netrom_transport_acknowledge_delay, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_t2, - .extra2 = &max_t2 - }, - { - .procname = "transport_busy_delay", - .data = &sysctl_netrom_transport_busy_delay, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_t4, - .extra2 = &max_t4 - }, - { - .procname = "transport_requested_window_size", - .data = &sysctl_netrom_transport_requested_window_size, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_window, - .extra2 = &max_window - }, - { - .procname = "transport_no_activity_timeout", - .data = &sysctl_netrom_transport_no_activity_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_idle, - .extra2 = &max_idle - }, - { - .procname = "routing_control", - .data = &sysctl_netrom_routing_control, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_route, - .extra2 = &max_route - }, - { - .procname = "link_fails_count", - .data = &sysctl_netrom_link_fails_count, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_fails, - .extra2 = &max_fails - }, - { - .procname = "reset", - .data = &sysctl_netrom_reset_circuit, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_reset, - .extra2 = &max_reset - }, -}; - -int __init nr_register_sysctl(void) -{ - nr_table_header = register_net_sysctl(&init_net, "net/netrom", nr_table); - if (!nr_table_header) - return -ENOMEM; - return 0; -} - -void nr_unregister_sysctl(void) -{ - unregister_net_sysctl_table(nr_table_header); -} diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index e209099218b4..bbbde50fc649 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -2184,9 +2184,40 @@ error: return err; } +static size_t ovs_vport_cmd_msg_size(void) +{ + size_t msgsize = NLMSG_ALIGN(sizeof(struct ovs_header)); + + msgsize += nla_total_size(sizeof(u32)); /* OVS_VPORT_ATTR_PORT_NO */ + msgsize += nla_total_size(sizeof(u32)); /* OVS_VPORT_ATTR_TYPE */ + msgsize += nla_total_size(IFNAMSIZ); /* OVS_VPORT_ATTR_NAME */ + msgsize += nla_total_size(sizeof(u32)); /* OVS_VPORT_ATTR_IFINDEX */ + msgsize += nla_total_size(sizeof(s32)); /* OVS_VPORT_ATTR_NETNSID */ + + /* OVS_VPORT_ATTR_STATS */ + msgsize += nla_total_size_64bit(sizeof(struct ovs_vport_stats)); + + /* OVS_VPORT_ATTR_UPCALL_STATS(OVS_VPORT_UPCALL_ATTR_SUCCESS + + * OVS_VPORT_UPCALL_ATTR_FAIL) + */ + msgsize += nla_total_size(nla_total_size_64bit(sizeof(u64)) + + nla_total_size_64bit(sizeof(u64))); + + /* OVS_VPORT_ATTR_UPCALL_PID */ + msgsize += nla_total_size(nr_cpu_ids * sizeof(u32)); + + /* OVS_VPORT_ATTR_OPTIONS(OVS_TUNNEL_ATTR_DST_PORT + + * OVS_TUNNEL_ATTR_EXTENSION(OVS_VXLAN_EXT_GBP)) + */ + msgsize += nla_total_size(nla_total_size(sizeof(u16)) + + nla_total_size(nla_total_size(0))); + + return msgsize; +} + static struct sk_buff *ovs_vport_cmd_alloc_info(void) { - return nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + return genlmsg_new(ovs_vport_cmd_msg_size(), GFP_KERNEL); } /* Called with ovs_mutex, only via ovs_dp_notify_wq(). */ @@ -2196,7 +2227,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net, struct sk_buff *skb; int retval; - skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + skb = ovs_vport_cmd_alloc_info(); if (!skb) return ERR_PTR(-ENOMEM); diff --git a/net/openvswitch/vport-geneve.c b/net/openvswitch/vport-geneve.c index b10e1602c6b1..cb5ea4424ffc 100644 --- a/net/openvswitch/vport-geneve.c +++ b/net/openvswitch/vport-geneve.c @@ -97,6 +97,9 @@ static struct vport *geneve_tnl_create(const struct vport_parms *parms) goto error; } + vport->dev = dev; + netdev_hold(vport->dev, &vport->dev_tracker, GFP_KERNEL); + rtnl_unlock(); return vport; error: @@ -111,7 +114,7 @@ static struct vport *geneve_create(const struct vport_parms *parms) if (IS_ERR(vport)) return vport; - return ovs_netdev_link(vport, parms->name); + return ovs_netdev_link(vport, true); } static struct vport_ops ovs_geneve_vport_ops = { diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c index 4014c9b5eb79..6cb5a697b396 100644 --- a/net/openvswitch/vport-gre.c +++ b/net/openvswitch/vport-gre.c @@ -63,6 +63,9 @@ static struct vport *gre_tnl_create(const struct vport_parms *parms) return ERR_PTR(err); } + vport->dev = dev; + netdev_hold(vport->dev, &vport->dev_tracker, GFP_KERNEL); + rtnl_unlock(); return vport; } @@ -75,7 +78,7 @@ static struct vport *gre_create(const struct vport_parms *parms) if (IS_ERR(vport)) return vport; - return ovs_netdev_link(vport, parms->name); + return ovs_netdev_link(vport, true); } static struct vport_ops ovs_gre_vport_ops = { diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 12055af832dc..c42642075685 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -73,37 +73,21 @@ static struct net_device *get_dpdev(const struct datapath *dp) return local->dev; } -struct vport *ovs_netdev_link(struct vport *vport, const char *name) +struct vport *ovs_netdev_link(struct vport *vport, bool tunnel) { int err; - vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), name); - if (!vport->dev) { + if (WARN_ON_ONCE(!vport->dev)) { err = -ENODEV; goto error_free_vport; } - /* Ensure that the device exists and that the provided - * name is not one of its aliases. - */ - if (strcmp(name, ovs_vport_name(vport))) { - err = -ENODEV; - goto error_put; - } - netdev_tracker_alloc(vport->dev, &vport->dev_tracker, GFP_KERNEL); - if (vport->dev->flags & IFF_LOOPBACK || - (vport->dev->type != ARPHRD_ETHER && - vport->dev->type != ARPHRD_NONE) || - ovs_is_internal_dev(vport->dev)) { - err = -EINVAL; - goto error_put; - } rtnl_lock(); err = netdev_master_upper_dev_link(vport->dev, get_dpdev(vport->dp), NULL, NULL, NULL); if (err) - goto error_unlock; + goto error_put_unlock; err = netdev_rx_handler_register(vport->dev, netdev_frame_hook, vport); @@ -119,10 +103,11 @@ struct vport *ovs_netdev_link(struct vport *vport, const char *name) error_master_upper_dev_unlink: netdev_upper_dev_unlink(vport->dev, get_dpdev(vport->dp)); -error_unlock: - rtnl_unlock(); -error_put: +error_put_unlock: + if (tunnel && vport->dev->reg_state == NETREG_REGISTERED) + rtnl_delete_link(vport->dev, 0, NULL); netdev_put(vport->dev, &vport->dev_tracker); + rtnl_unlock(); error_free_vport: ovs_vport_free(vport); return ERR_PTR(err); @@ -132,12 +117,39 @@ EXPORT_SYMBOL_GPL(ovs_netdev_link); static struct vport *netdev_create(const struct vport_parms *parms) { struct vport *vport; + int err; vport = ovs_vport_alloc(0, &ovs_netdev_vport_ops, parms); if (IS_ERR(vport)) return vport; - return ovs_netdev_link(vport, parms->name); + vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), parms->name); + if (!vport->dev) { + err = -ENODEV; + goto error_free_vport; + } + netdev_tracker_alloc(vport->dev, &vport->dev_tracker, GFP_KERNEL); + + /* Ensure that the provided name is not an alias. */ + if (strcmp(parms->name, ovs_vport_name(vport))) { + err = -ENODEV; + goto error_put; + } + + if (vport->dev->flags & IFF_LOOPBACK || + (vport->dev->type != ARPHRD_ETHER && + vport->dev->type != ARPHRD_NONE) || + ovs_is_internal_dev(vport->dev)) { + err = -EINVAL; + goto error_put; + } + + return ovs_netdev_link(vport, false); +error_put: + netdev_put(vport->dev, &vport->dev_tracker); +error_free_vport: + ovs_vport_free(vport); + return ERR_PTR(err); } static void vport_netdev_free(struct rcu_head *rcu) @@ -196,9 +208,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) */ if (vport->dev->reg_state == NETREG_REGISTERED) rtnl_delete_link(vport->dev, 0, NULL); - rtnl_unlock(); + /* We can't put the device reference yet, since it can still be in + * use, but rtnl_unlock()->netdev_run_todo() will block until all + * the references are released, so the RCU call must be before it. + */ call_rcu(&vport->rcu, vport_netdev_free); + rtnl_unlock(); } EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); diff --git a/net/openvswitch/vport-netdev.h b/net/openvswitch/vport-netdev.h index c5d83a43bfc4..6c0d7366f986 100644 --- a/net/openvswitch/vport-netdev.h +++ b/net/openvswitch/vport-netdev.h @@ -13,7 +13,7 @@ struct vport *ovs_netdev_get_vport(struct net_device *dev); -struct vport *ovs_netdev_link(struct vport *vport, const char *name); +struct vport *ovs_netdev_link(struct vport *vport, bool tunnel); void ovs_netdev_detach_dev(struct vport *); int __init ovs_netdev_init(void); diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c index 0b881b043bcf..c1b37b50d29e 100644 --- a/net/openvswitch/vport-vxlan.c +++ b/net/openvswitch/vport-vxlan.c @@ -126,6 +126,9 @@ static struct vport *vxlan_tnl_create(const struct vport_parms *parms) goto error; } + vport->dev = dev; + netdev_hold(vport->dev, &vport->dev_tracker, GFP_KERNEL); + rtnl_unlock(); return vport; error: @@ -140,7 +143,7 @@ static struct vport *vxlan_create(const struct vport_parms *parms) if (IS_ERR(vport)) return vport; - return ovs_netdev_link(vport, parms->name); + return ovs_netdev_link(vport, true); } static struct vport_ops ovs_vxlan_netdev_vport_ops = { diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 23f629e94a36..56b2e2d1a749 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -406,6 +406,9 @@ int ovs_vport_set_upcall_portids(struct vport *vport, const struct nlattr *ids) if (!nla_len(ids) || nla_len(ids) % sizeof(u32)) return -EINVAL; + if (nla_len(ids) / sizeof(u32) > nr_cpu_ids) + return -EINVAL; + old = ovsl_dereference(vport->upcall_portids); vport_portids = kmalloc(sizeof(*vport_portids) + nla_len(ids), diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 4b043241fd56..8e6f3a734ba0 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2718,7 +2718,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) { struct sk_buff *skb = NULL; struct net_device *dev; - struct virtio_net_hdr *vnet_hdr = NULL; + struct virtio_net_hdr vnet_hdr; + bool has_vnet_hdr = false; struct sockcm_cookie sockc; __be16 proto; int err, reserve = 0; @@ -2819,16 +2820,20 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) hlen = LL_RESERVED_SPACE(dev); tlen = dev->needed_tailroom; if (vnet_hdr_sz) { - vnet_hdr = data; data += vnet_hdr_sz; tp_len -= vnet_hdr_sz; - if (tp_len < 0 || - __packet_snd_vnet_parse(vnet_hdr, tp_len)) { + if (tp_len < 0) { + tp_len = -EINVAL; + goto tpacket_error; + } + memcpy(&vnet_hdr, data - vnet_hdr_sz, sizeof(vnet_hdr)); + if (__packet_snd_vnet_parse(&vnet_hdr, tp_len)) { tp_len = -EINVAL; goto tpacket_error; } copylen = __virtio16_to_cpu(vio_le(), - vnet_hdr->hdr_len); + vnet_hdr.hdr_len); + has_vnet_hdr = true; } copylen = max_t(int, copylen, dev->hard_header_len); skb = sock_alloc_send_skb(&po->sk, @@ -2865,12 +2870,12 @@ tpacket_error: } } - if (vnet_hdr_sz) { - if (virtio_net_hdr_to_skb(skb, vnet_hdr, vio_le())) { + if (has_vnet_hdr) { + if (virtio_net_hdr_to_skb(skb, &vnet_hdr, vio_le())) { tp_len = -EINVAL; goto tpacket_error; } - virtio_net_hdr_set_proto(skb, vnet_hdr); + virtio_net_hdr_set_proto(skb, &vnet_hdr); } skb->destructor = tpacket_destruct_skb; diff --git a/net/phonet/socket.c b/net/phonet/socket.c index c4af26357144..631a99cdbd00 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -208,9 +208,15 @@ static int pn_socket_autobind(struct socket *sock) sa.spn_family = AF_PHONET; err = pn_socket_bind(sock, (struct sockaddr_unsized *)&sa, sizeof(struct sockaddr_pn)); - if (err != -EINVAL) + /* + * pn_socket_bind() also returns -EINVAL when sk_state != TCP_CLOSE + * without a prior bind, so -EINVAL alone is not sufficient to infer + * that the socket was already bound. Only treat it as "already + * bound" when the port is non-zero; otherwise propagate the error + * instead of crashing the kernel. + */ + if (err != -EINVAL || unlikely(!pn_port(pn_sk(sock->sk)->sobject))) return err; - BUG_ON(!pn_port(pn_sk(sock->sk)->sobject)); return 0; /* socket was already bound */ } diff --git a/net/psp/psp-nl-gen.c b/net/psp/psp-nl-gen.c index 22a48d0fa378..953309952cef 100644 --- a/net/psp/psp-nl-gen.c +++ b/net/psp/psp-nl-gen.c @@ -76,7 +76,7 @@ static const struct genl_split_ops psp_nl_ops[] = { .post_doit = psp_device_unlock, .policy = psp_dev_set_nl_policy, .maxattr = PSP_A_DEV_PSP_VERSIONS_ENA, - .flags = GENL_CMD_CAP_DO, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { .cmd = PSP_CMD_KEY_ROTATE, @@ -85,7 +85,7 @@ static const struct genl_split_ops psp_nl_ops[] = { .post_doit = psp_device_unlock, .policy = psp_key_rotate_nl_policy, .maxattr = PSP_A_DEV_ID, - .flags = GENL_CMD_CAP_DO, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, { .cmd = PSP_CMD_RX_ASSOC, diff --git a/net/psp/psp_main.c b/net/psp/psp_main.c index 9508b6c38003..e45549f08eef 100644 --- a/net/psp/psp_main.c +++ b/net/psp/psp_main.c @@ -263,15 +263,16 @@ EXPORT_SYMBOL(psp_dev_encapsulate); /* Receive handler for PSP packets. * - * Presently it accepts only already-authenticated packets and does not - * support optional fields, such as virtualization cookies. The caller should - * ensure that skb->data is pointing to the mac header, and that skb->mac_len - * is set. This function does not currently adjust skb->csum (CHECKSUM_COMPLETE - * is not supported). + * Accepts only already-authenticated packets. The full PSP header is + * stripped according to psph->hdrlen; any optional fields it advertises + * (virtualization cookies, etc.) are ignored and discarded along with the + * rest of the header. The caller should ensure that skb->data is pointing + * to the mac header, and that skb->mac_len is set. This function does not + * currently adjust skb->csum (CHECKSUM_COMPLETE is not supported). */ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv) { - int l2_hlen = 0, l3_hlen, encap; + int l2_hlen = 0, l3_hlen, encap, psp_hlen; struct psp_skb_ext *pse; struct psphdr *psph; struct ethhdr *eth; @@ -312,18 +313,36 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv) if (unlikely(uh->dest != htons(PSP_DEFAULT_UDP_PORT))) return -EINVAL; - pse = skb_ext_add(skb, SKB_EXT_PSP); - if (!pse) + psph = (struct psphdr *)(skb->data + l2_hlen + l3_hlen + + sizeof(struct udphdr)); + + /* Strip the full PSP header per psph->hdrlen; VC/options are pulled + * into the linear region only so they can be discarded with the + * rest of the header. + */ + psp_hlen = (psph->hdrlen + 1) * 8; + + if (unlikely(psp_hlen < sizeof(struct psphdr))) + return -EINVAL; + + if (psp_hlen > sizeof(struct psphdr) && + !pskb_may_pull(skb, l2_hlen + l3_hlen + + sizeof(struct udphdr) + psp_hlen)) return -EINVAL; psph = (struct psphdr *)(skb->data + l2_hlen + l3_hlen + sizeof(struct udphdr)); + + pse = skb_ext_add(skb, SKB_EXT_PSP); + if (!pse) + return -EINVAL; + pse->spi = psph->spi; pse->dev_id = dev_id; pse->generation = generation; pse->version = FIELD_GET(PSPHDR_VERFL_VERSION, psph->verfl); - encap = PSP_ENCAP_HLEN; + encap = sizeof(struct udphdr) + psp_hlen; encap += strip_icv ? PSP_TRL_SIZE : 0; if (proto == htons(ETH_P_IP)) { @@ -340,8 +359,9 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv) ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) - encap); } - memmove(skb->data + PSP_ENCAP_HLEN, skb->data, l2_hlen + l3_hlen); - skb_pull(skb, PSP_ENCAP_HLEN); + memmove(skb->data + sizeof(struct udphdr) + psp_hlen, + skb->data, l2_hlen + l3_hlen); + skb_pull(skb, sizeof(struct udphdr) + psp_hlen); if (strip_icv) pskb_trim(skb, skb->len - PSP_TRL_SIZE); diff --git a/net/psp/psp_nl.c b/net/psp/psp_nl.c index 6afd7707ec12..0cc744a6e1c9 100644 --- a/net/psp/psp_nl.c +++ b/net/psp/psp_nl.c @@ -305,8 +305,13 @@ int psp_assoc_device_get_locked(const struct genl_split_ops *ops, psd = psp_dev_get_for_sock(socket->sk); if (psd) { - err = psp_dev_check_access(psd, genl_info_net(info)); - if (err) { + /* Extra care needed here, psp_dev_get_for_sock() only gives + * us access to struct psp_dev's memory, which is quite weak. + */ + mutex_lock(&psd->lock); + if (!psp_dev_is_registered(psd) || + psp_dev_check_access(psd, genl_info_net(info))) { + mutex_unlock(&psd->lock); psp_dev_put(psd); psd = NULL; } @@ -319,7 +324,6 @@ int psp_assoc_device_get_locked(const struct genl_split_ops *ops, id = info->attrs[PSP_A_ASSOC_DEV_ID]; if (psd) { - mutex_lock(&psd->lock); if (id && psd->id != nla_get_u32(id)) { mutex_unlock(&psd->lock); NL_SET_ERR_MSG_ATTR(info->extack, id, diff --git a/net/rds/connection.c b/net/rds/connection.c index 412441aaa298..c10b7ed06c49 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -701,6 +701,13 @@ void rds_for_each_conn_info(struct socket *sock, unsigned int len, i++, head++) { hlist_for_each_entry_rcu(conn, head, c_hash_node) { + /* Zero the per-item buffer before handing it to the + * visitor so any field the visitor does not write - + * including implicit alignment padding - cannot leak + * stack contents to user space via rds_info_copy(). + */ + memset(buffer, 0, item_len); + /* XXX no c_lock usage.. */ if (!visitor(conn, buffer)) continue; @@ -750,6 +757,13 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len, */ cp = conn->c_path; + /* Zero the per-item buffer for the same reason as + * rds_for_each_conn_info(): any byte the visitor + * does not write (including alignment padding) must + * not leak stack contents via rds_info_copy(). + */ + memset(buffer, 0, item_len); + /* XXX no cp_lock usage.. */ if (!visitor(cp, buffer)) continue; diff --git a/net/rds/message.c b/net/rds/message.c index eaa6f22601a4..7feb0eb6537d 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -131,24 +131,34 @@ static void rds_rm_zerocopy_callback(struct rds_sock *rs, */ static void rds_message_purge(struct rds_message *rm) { + struct rds_znotifier *znotifier; unsigned long i, flags; - bool zcopy = false; + bool zcopy; if (unlikely(test_bit(RDS_MSG_PAGEVEC, &rm->m_flags))) return; spin_lock_irqsave(&rm->m_rs_lock, flags); + znotifier = rm->data.op_mmp_znotifier; + rm->data.op_mmp_znotifier = NULL; + zcopy = !!znotifier; + if (rm->m_rs) { struct rds_sock *rs = rm->m_rs; - if (rm->data.op_mmp_znotifier) { - zcopy = true; - rds_rm_zerocopy_callback(rs, rm->data.op_mmp_znotifier); + if (znotifier) { + rds_rm_zerocopy_callback(rs, znotifier); rds_wake_sk_sleep(rs); - rm->data.op_mmp_znotifier = NULL; } sock_put(rds_rs_to_sk(rs)); rm->m_rs = NULL; + } else if (znotifier) { + /* + * Zerocopy can fail before the message is queued on the + * socket, so there is no rs to carry the notification. + */ + mm_unaccount_pinned_pages(&znotifier->z_mmp); + kfree(rds_info_from_znotifier(znotifier)); } spin_unlock_irqrestore(&rm->m_rs_lock, flags); @@ -438,6 +448,7 @@ static int rds_message_zcopy_from_user(struct rds_message *rm, struct iov_iter * for (i = 0; i < rm->data.op_nents; i++) put_page(sg_page(&rm->data.op_sg[i])); + rm->data.op_nents = 0; mmp = &rm->data.op_mmp_znotifier->z_mmp; mm_unaccount_pinned_pages(mmp); ret = -EFAULT; diff --git a/net/rds/rdma.c b/net/rds/rdma.c index aa6465dc742c..61fb6e45281b 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } diff --git a/net/rose/Makefile b/net/rose/Makefile deleted file mode 100644 index 3e6638f5ba57..000000000000 --- a/net/rose/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Linux Rose (X.25 PLP) layer. -# - -obj-$(CONFIG_ROSE) += rose.o - -rose-y := af_rose.o rose_dev.o rose_in.o rose_link.o rose_loopback.o \ - rose_out.o rose_route.o rose_subr.o rose_timer.o -rose-$(CONFIG_SYSCTL) += sysctl_net_rose.o diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c deleted file mode 100644 index d5032840ee48..000000000000 --- a/net/rose/af_rose.c +++ /dev/null @@ -1,1687 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) - * Copyright (C) Terry Dawson VK2KTJ (terry@animats.net) - * Copyright (C) Tomi Manninen OH2BNS (oh2bns@sral.fi) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int rose_ndevs = 10; - -int sysctl_rose_restart_request_timeout = ROSE_DEFAULT_T0; -int sysctl_rose_call_request_timeout = ROSE_DEFAULT_T1; -int sysctl_rose_reset_request_timeout = ROSE_DEFAULT_T2; -int sysctl_rose_clear_request_timeout = ROSE_DEFAULT_T3; -int sysctl_rose_no_activity_timeout = ROSE_DEFAULT_IDLE; -int sysctl_rose_ack_hold_back_timeout = ROSE_DEFAULT_HB; -int sysctl_rose_routing_control = ROSE_DEFAULT_ROUTING; -int sysctl_rose_link_fail_timeout = ROSE_DEFAULT_FAIL_TIMEOUT; -int sysctl_rose_maximum_vcs = ROSE_DEFAULT_MAXVC; -int sysctl_rose_window_size = ROSE_DEFAULT_WINDOW_SIZE; - -static HLIST_HEAD(rose_list); -static DEFINE_SPINLOCK(rose_list_lock); - -static const struct proto_ops rose_proto_ops; - -ax25_address rose_callsign; - -/* - * ROSE network devices are virtual network devices encapsulating ROSE - * frames into AX.25 which will be sent through an AX.25 device, so form a - * special "super class" of normal net devices; split their locks off into a - * separate class since they always nest. - */ -static struct lock_class_key rose_netdev_xmit_lock_key; -static struct lock_class_key rose_netdev_addr_lock_key; - -static void rose_set_lockdep_one(struct net_device *dev, - struct netdev_queue *txq, - void *_unused) -{ - lockdep_set_class(&txq->_xmit_lock, &rose_netdev_xmit_lock_key); -} - -static void rose_set_lockdep_key(struct net_device *dev) -{ - lockdep_set_class(&dev->addr_list_lock, &rose_netdev_addr_lock_key); - netdev_for_each_tx_queue(dev, rose_set_lockdep_one, NULL); -} - -/* - * Convert a ROSE address into text. - */ -char *rose2asc(char *buf, const rose_address *addr) -{ - if (addr->rose_addr[0] == 0x00 && addr->rose_addr[1] == 0x00 && - addr->rose_addr[2] == 0x00 && addr->rose_addr[3] == 0x00 && - addr->rose_addr[4] == 0x00) { - strcpy(buf, "*"); - } else { - sprintf(buf, "%02X%02X%02X%02X%02X", addr->rose_addr[0] & 0xFF, - addr->rose_addr[1] & 0xFF, - addr->rose_addr[2] & 0xFF, - addr->rose_addr[3] & 0xFF, - addr->rose_addr[4] & 0xFF); - } - - return buf; -} - -/* - * Compare two ROSE addresses, 0 == equal. - */ -int rosecmp(const rose_address *addr1, const rose_address *addr2) -{ - int i; - - for (i = 0; i < 5; i++) - if (addr1->rose_addr[i] != addr2->rose_addr[i]) - return 1; - - return 0; -} - -/* - * Compare two ROSE addresses for only mask digits, 0 == equal. - */ -int rosecmpm(const rose_address *addr1, const rose_address *addr2, - unsigned short mask) -{ - unsigned int i, j; - - if (mask > 10) - return 1; - - for (i = 0; i < mask; i++) { - j = i / 2; - - if ((i % 2) != 0) { - if ((addr1->rose_addr[j] & 0x0F) != (addr2->rose_addr[j] & 0x0F)) - return 1; - } else { - if ((addr1->rose_addr[j] & 0xF0) != (addr2->rose_addr[j] & 0xF0)) - return 1; - } - } - - return 0; -} - -/* - * Socket removal during an interrupt is now safe. - */ -static void rose_remove_socket(struct sock *sk) -{ - spin_lock_bh(&rose_list_lock); - sk_del_node_init(sk); - spin_unlock_bh(&rose_list_lock); -} - -/* - * Kill all bound sockets on a broken link layer connection to a - * particular neighbour. - */ -void rose_kill_by_neigh(struct rose_neigh *neigh) -{ - struct sock *s; - - spin_lock_bh(&rose_list_lock); - sk_for_each(s, &rose_list) { - struct rose_sock *rose = rose_sk(s); - - if (rose->neighbour == neigh) { - rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); - rose_neigh_put(rose->neighbour); - rose->neighbour = NULL; - } - } - spin_unlock_bh(&rose_list_lock); -} - -/* - * Kill all bound sockets on a dropped device. - */ -static void rose_kill_by_device(struct net_device *dev) -{ - struct sock *sk, *array[16]; - struct rose_sock *rose; - bool rescan; - int i, cnt; - -start: - rescan = false; - cnt = 0; - spin_lock_bh(&rose_list_lock); - sk_for_each(sk, &rose_list) { - rose = rose_sk(sk); - if (rose->device == dev) { - if (cnt == ARRAY_SIZE(array)) { - rescan = true; - break; - } - sock_hold(sk); - array[cnt++] = sk; - } - } - spin_unlock_bh(&rose_list_lock); - - for (i = 0; i < cnt; i++) { - sk = array[i]; - rose = rose_sk(sk); - lock_sock(sk); - spin_lock_bh(&rose_list_lock); - if (rose->device == dev) { - rose_disconnect(sk, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); - if (rose->neighbour) - rose_neigh_put(rose->neighbour); - netdev_put(rose->device, &rose->dev_tracker); - rose->device = NULL; - } - spin_unlock_bh(&rose_list_lock); - release_sock(sk); - sock_put(sk); - cond_resched(); - } - if (rescan) - goto start; -} - -/* - * Handle device status changes. - */ -static int rose_device_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (event != NETDEV_DOWN) - return NOTIFY_DONE; - - switch (dev->type) { - case ARPHRD_ROSE: - rose_kill_by_device(dev); - break; - case ARPHRD_AX25: - rose_link_device_down(dev); - rose_rt_device_down(dev); - break; - } - - return NOTIFY_DONE; -} - -/* - * Add a socket to the bound sockets list. - */ -static void rose_insert_socket(struct sock *sk) -{ - - spin_lock_bh(&rose_list_lock); - sk_add_node(sk, &rose_list); - spin_unlock_bh(&rose_list_lock); -} - -/* - * Find a socket that wants to accept the Call Request we just - * received. - */ -static struct sock *rose_find_listener(rose_address *addr, ax25_address *call) -{ - struct sock *s; - - spin_lock_bh(&rose_list_lock); - sk_for_each(s, &rose_list) { - struct rose_sock *rose = rose_sk(s); - - if (!rosecmp(&rose->source_addr, addr) && - !ax25cmp(&rose->source_call, call) && - !rose->source_ndigis && s->sk_state == TCP_LISTEN) - goto found; - } - - sk_for_each(s, &rose_list) { - struct rose_sock *rose = rose_sk(s); - - if (!rosecmp(&rose->source_addr, addr) && - !ax25cmp(&rose->source_call, &null_ax25_address) && - s->sk_state == TCP_LISTEN) - goto found; - } - s = NULL; -found: - spin_unlock_bh(&rose_list_lock); - return s; -} - -/* - * Find a connected ROSE socket given my LCI and device. - */ -struct sock *rose_find_socket(unsigned int lci, struct rose_neigh *neigh) -{ - struct sock *s; - - spin_lock_bh(&rose_list_lock); - sk_for_each(s, &rose_list) { - struct rose_sock *rose = rose_sk(s); - - if (rose->lci == lci && rose->neighbour == neigh) - goto found; - } - s = NULL; -found: - spin_unlock_bh(&rose_list_lock); - return s; -} - -/* - * Find a unique LCI for a given device. - */ -unsigned int rose_new_lci(struct rose_neigh *neigh) -{ - int lci; - - if (neigh->dce_mode) { - for (lci = 1; lci <= sysctl_rose_maximum_vcs; lci++) - if (rose_find_socket(lci, neigh) == NULL && rose_route_free_lci(lci, neigh) == NULL) - return lci; - } else { - for (lci = sysctl_rose_maximum_vcs; lci > 0; lci--) - if (rose_find_socket(lci, neigh) == NULL && rose_route_free_lci(lci, neigh) == NULL) - return lci; - } - - return 0; -} - -/* - * Deferred destroy. - */ -void rose_destroy_socket(struct sock *); - -/* - * Handler for deferred kills. - */ -static void rose_destroy_timer(struct timer_list *t) -{ - struct sock *sk = timer_container_of(sk, t, sk_timer); - - rose_destroy_socket(sk); -} - -/* - * This is called from user mode and the timers. Thus it protects itself - * against interrupt users but doesn't worry about being called during - * work. Once it is removed from the queue no interrupt or bottom half - * will touch it and we are (fairly 8-) ) safe. - */ -void rose_destroy_socket(struct sock *sk) -{ - struct sk_buff *skb; - - rose_remove_socket(sk); - rose_stop_heartbeat(sk); - rose_stop_idletimer(sk); - rose_stop_timer(sk); - - rose_clear_queues(sk); /* Flush the queues */ - - while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { - if (skb->sk != sk) { /* A pending connection */ - /* Queue the unaccepted socket for death */ - sock_set_flag(skb->sk, SOCK_DEAD); - rose_start_heartbeat(skb->sk); - rose_sk(skb->sk)->state = ROSE_STATE_0; - } - - kfree_skb(skb); - } - - if (sk_has_allocations(sk)) { - /* Defer: outstanding buffers */ - timer_setup(&sk->sk_timer, rose_destroy_timer, 0); - sk->sk_timer.expires = jiffies + 10 * HZ; - add_timer(&sk->sk_timer); - } else - sock_put(sk); -} - -/* - * Handling for system calls applied via the various interfaces to a - * ROSE socket object. - */ - -static int rose_setsockopt(struct socket *sock, int level, int optname, - sockptr_t optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - unsigned int opt; - - if (level != SOL_ROSE) - return -ENOPROTOOPT; - - if (optlen < sizeof(unsigned int)) - return -EINVAL; - - if (copy_from_sockptr(&opt, optval, sizeof(unsigned int))) - return -EFAULT; - - switch (optname) { - case ROSE_DEFER: - rose->defer = opt ? 1 : 0; - return 0; - - case ROSE_T1: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - rose->t1 = opt * HZ; - return 0; - - case ROSE_T2: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - rose->t2 = opt * HZ; - return 0; - - case ROSE_T3: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - rose->t3 = opt * HZ; - return 0; - - case ROSE_HOLDBACK: - if (opt < 1 || opt > UINT_MAX / HZ) - return -EINVAL; - rose->hb = opt * HZ; - return 0; - - case ROSE_IDLE: - if (opt > UINT_MAX / (60 * HZ)) - return -EINVAL; - rose->idle = opt * 60 * HZ; - return 0; - - case ROSE_QBITINCL: - rose->qbitincl = opt ? 1 : 0; - return 0; - - default: - return -ENOPROTOOPT; - } -} - -static int rose_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - int val = 0; - int len; - - if (level != SOL_ROSE) - return -ENOPROTOOPT; - - if (get_user(len, optlen)) - return -EFAULT; - - if (len < 0) - return -EINVAL; - - switch (optname) { - case ROSE_DEFER: - val = rose->defer; - break; - - case ROSE_T1: - val = rose->t1 / HZ; - break; - - case ROSE_T2: - val = rose->t2 / HZ; - break; - - case ROSE_T3: - val = rose->t3 / HZ; - break; - - case ROSE_HOLDBACK: - val = rose->hb / HZ; - break; - - case ROSE_IDLE: - val = rose->idle / (60 * HZ); - break; - - case ROSE_QBITINCL: - val = rose->qbitincl; - break; - - default: - return -ENOPROTOOPT; - } - - len = min_t(unsigned int, len, sizeof(int)); - - if (put_user(len, optlen)) - return -EFAULT; - - return copy_to_user(optval, &val, len) ? -EFAULT : 0; -} - -static int rose_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - - lock_sock(sk); - if (sock->state != SS_UNCONNECTED) { - release_sock(sk); - return -EINVAL; - } - - if (sk->sk_state != TCP_LISTEN) { - struct rose_sock *rose = rose_sk(sk); - - rose->dest_ndigis = 0; - memset(&rose->dest_addr, 0, ROSE_ADDR_LEN); - memset(&rose->dest_call, 0, AX25_ADDR_LEN); - memset(rose->dest_digis, 0, AX25_ADDR_LEN * ROSE_MAX_DIGIS); - sk->sk_max_ack_backlog = backlog; - sk->sk_state = TCP_LISTEN; - release_sock(sk); - return 0; - } - release_sock(sk); - - return -EOPNOTSUPP; -} - -static struct proto rose_proto = { - .name = "ROSE", - .owner = THIS_MODULE, - .obj_size = sizeof(struct rose_sock), -}; - -static int rose_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - struct rose_sock *rose; - - if (!net_eq(net, &init_net)) - return -EAFNOSUPPORT; - - if (sock->type != SOCK_SEQPACKET || protocol != 0) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_ROSE, GFP_ATOMIC, &rose_proto, kern); - if (sk == NULL) - return -ENOMEM; - - rose = rose_sk(sk); - - sock_init_data(sock, sk); - - skb_queue_head_init(&rose->ack_queue); -#ifdef M_BIT - skb_queue_head_init(&rose->frag_queue); - rose->fraglen = 0; -#endif - - sock->ops = &rose_proto_ops; - sk->sk_protocol = protocol; - - timer_setup(&rose->timer, NULL, 0); - timer_setup(&rose->idletimer, NULL, 0); - - rose->t1 = msecs_to_jiffies(sysctl_rose_call_request_timeout); - rose->t2 = msecs_to_jiffies(sysctl_rose_reset_request_timeout); - rose->t3 = msecs_to_jiffies(sysctl_rose_clear_request_timeout); - rose->hb = msecs_to_jiffies(sysctl_rose_ack_hold_back_timeout); - rose->idle = msecs_to_jiffies(sysctl_rose_no_activity_timeout); - - rose->state = ROSE_STATE_0; - - return 0; -} - -static struct sock *rose_make_new(struct sock *osk) -{ - struct sock *sk; - struct rose_sock *rose, *orose; - - if (osk->sk_type != SOCK_SEQPACKET) - return NULL; - - sk = sk_alloc(sock_net(osk), PF_ROSE, GFP_ATOMIC, &rose_proto, 0); - if (sk == NULL) - return NULL; - - rose = rose_sk(sk); - - sock_init_data(NULL, sk); - - skb_queue_head_init(&rose->ack_queue); -#ifdef M_BIT - skb_queue_head_init(&rose->frag_queue); - rose->fraglen = 0; -#endif - - sk->sk_type = osk->sk_type; - sk->sk_priority = READ_ONCE(osk->sk_priority); - sk->sk_protocol = osk->sk_protocol; - sk->sk_rcvbuf = osk->sk_rcvbuf; - sk->sk_sndbuf = osk->sk_sndbuf; - sk->sk_state = TCP_ESTABLISHED; - sock_copy_flags(sk, osk); - - timer_setup(&rose->timer, NULL, 0); - timer_setup(&rose->idletimer, NULL, 0); - - orose = rose_sk(osk); - rose->t1 = orose->t1; - rose->t2 = orose->t2; - rose->t3 = orose->t3; - rose->hb = orose->hb; - rose->idle = orose->idle; - rose->defer = orose->defer; - rose->device = orose->device; - if (rose->device) - netdev_hold(rose->device, &rose->dev_tracker, GFP_ATOMIC); - rose->qbitincl = orose->qbitincl; - - return sk; -} - -static int rose_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose; - - if (sk == NULL) return 0; - - sock_hold(sk); - sock_orphan(sk); - lock_sock(sk); - rose = rose_sk(sk); - - switch (rose->state) { - case ROSE_STATE_0: - release_sock(sk); - rose_disconnect(sk, 0, -1, -1); - lock_sock(sk); - rose_destroy_socket(sk); - break; - - case ROSE_STATE_2: - rose_neigh_put(rose->neighbour); - release_sock(sk); - rose_disconnect(sk, 0, -1, -1); - lock_sock(sk); - rose_destroy_socket(sk); - break; - - case ROSE_STATE_1: - case ROSE_STATE_3: - case ROSE_STATE_4: - case ROSE_STATE_5: - rose_clear_queues(sk); - rose_stop_idletimer(sk); - rose_write_internal(sk, ROSE_CLEAR_REQUEST); - rose_start_t3timer(sk); - rose->state = ROSE_STATE_2; - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DEAD); - sock_set_flag(sk, SOCK_DESTROY); - break; - - default: - break; - } - - spin_lock_bh(&rose_list_lock); - netdev_put(rose->device, &rose->dev_tracker); - rose->device = NULL; - spin_unlock_bh(&rose_list_lock); - sock->sk = NULL; - release_sock(sk); - sock_put(sk); - - return 0; -} - -static int rose_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; - struct net_device *dev; - ax25_address *source; - ax25_uid_assoc *user; - int err = -EINVAL; - int n; - - if (addr_len != sizeof(struct sockaddr_rose) && addr_len != sizeof(struct full_sockaddr_rose)) - return -EINVAL; - - if (addr->srose_family != AF_ROSE) - return -EINVAL; - - if (addr_len == sizeof(struct sockaddr_rose) && addr->srose_ndigis > 1) - return -EINVAL; - - if ((unsigned int) addr->srose_ndigis > ROSE_MAX_DIGIS) - return -EINVAL; - - lock_sock(sk); - - if (!sock_flag(sk, SOCK_ZAPPED)) - goto out_release; - - err = -EADDRNOTAVAIL; - dev = rose_dev_get(&addr->srose_addr); - if (!dev) - goto out_release; - - source = &addr->srose_call; - - user = ax25_findbyuid(current_euid()); - if (user) { - rose->source_call = user->call; - ax25_uid_put(user); - } else { - if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) { - dev_put(dev); - err = -EACCES; - goto out_release; - } - rose->source_call = *source; - } - - rose->source_addr = addr->srose_addr; - rose->device = dev; - netdev_tracker_alloc(rose->device, &rose->dev_tracker, GFP_KERNEL); - rose->source_ndigis = addr->srose_ndigis; - - if (addr_len == sizeof(struct full_sockaddr_rose)) { - struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr; - for (n = 0 ; n < addr->srose_ndigis ; n++) - rose->source_digis[n] = full_addr->srose_digis[n]; - } else { - if (rose->source_ndigis == 1) { - rose->source_digis[0] = addr->srose_digi; - } - } - - rose_insert_socket(sk); - - sock_reset_flag(sk, SOCK_ZAPPED); - err = 0; -out_release: - release_sock(sk); - return err; -} - -static int rose_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len, - int flags) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; - unsigned char cause, diagnostic; - ax25_uid_assoc *user; - int n, err = 0; - - if (addr_len != sizeof(struct sockaddr_rose) && addr_len != sizeof(struct full_sockaddr_rose)) - return -EINVAL; - - if (addr->srose_family != AF_ROSE) - return -EINVAL; - - if (addr_len == sizeof(struct sockaddr_rose) && addr->srose_ndigis > 1) - return -EINVAL; - - if ((unsigned int) addr->srose_ndigis > ROSE_MAX_DIGIS) - return -EINVAL; - - /* Source + Destination digis should not exceed ROSE_MAX_DIGIS */ - if ((rose->source_ndigis + addr->srose_ndigis) > ROSE_MAX_DIGIS) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { - /* Connect completed during a ERESTARTSYS event */ - sock->state = SS_CONNECTED; - goto out_release; - } - - if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) { - sock->state = SS_UNCONNECTED; - err = -ECONNREFUSED; - goto out_release; - } - - if (sk->sk_state == TCP_ESTABLISHED) { - /* No reconnect on a seqpacket socket */ - err = -EISCONN; - goto out_release; - } - - if (sk->sk_state == TCP_SYN_SENT) { - err = -EALREADY; - goto out_release; - } - - sk->sk_state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, - &diagnostic, 0); - if (!rose->neighbour) { - err = -ENETUNREACH; - goto out_release; - } - - rose->lci = rose_new_lci(rose->neighbour); - if (!rose->lci) { - err = -ENETUNREACH; - rose_neigh_put(rose->neighbour); - goto out_release; - } - - if (sock_flag(sk, SOCK_ZAPPED)) { /* Must bind first - autobinding in this may or may not work */ - struct net_device *dev; - - sock_reset_flag(sk, SOCK_ZAPPED); - - dev = rose_dev_first(); - if (!dev) { - err = -ENETUNREACH; - rose_neigh_put(rose->neighbour); - goto out_release; - } - - user = ax25_findbyuid(current_euid()); - if (!user) { - err = -EINVAL; - rose_neigh_put(rose->neighbour); - dev_put(dev); - goto out_release; - } - - memcpy(&rose->source_addr, dev->dev_addr, ROSE_ADDR_LEN); - rose->source_call = user->call; - rose->device = dev; - netdev_tracker_alloc(rose->device, &rose->dev_tracker, - GFP_KERNEL); - ax25_uid_put(user); - - rose_insert_socket(sk); /* Finish the bind */ - } - rose->dest_addr = addr->srose_addr; - rose->dest_call = addr->srose_call; - rose->rand = ((long)rose & 0xFFFF) + rose->lci; - rose->dest_ndigis = addr->srose_ndigis; - - if (addr_len == sizeof(struct full_sockaddr_rose)) { - struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr; - for (n = 0 ; n < addr->srose_ndigis ; n++) - rose->dest_digis[n] = full_addr->srose_digis[n]; - } else { - if (rose->dest_ndigis == 1) { - rose->dest_digis[0] = addr->srose_digi; - } - } - - /* Move to connecting socket, start sending Connect Requests */ - sock->state = SS_CONNECTING; - sk->sk_state = TCP_SYN_SENT; - - rose->state = ROSE_STATE_1; - - rose_write_internal(sk, ROSE_CALL_REQUEST); - rose_start_heartbeat(sk); - rose_start_t1timer(sk); - - /* Now the loop */ - if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) { - err = -EINPROGRESS; - goto out_release; - } - - /* - * A Connect Ack with Choke or timeout or failed routing will go to - * closed. - */ - if (sk->sk_state == TCP_SYN_SENT) { - DEFINE_WAIT(wait); - - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - if (sk->sk_state != TCP_SYN_SENT) - break; - if (!signal_pending(current)) { - release_sock(sk); - schedule(); - lock_sock(sk); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(sk_sleep(sk), &wait); - - if (err) - goto out_release; - } - - if (sk->sk_state != TCP_ESTABLISHED) { - sock->state = SS_UNCONNECTED; - err = sock_error(sk); /* Always set at this point */ - goto out_release; - } - - sock->state = SS_CONNECTED; - -out_release: - release_sock(sk); - - return err; -} - -static int rose_accept(struct socket *sock, struct socket *newsock, - struct proto_accept_arg *arg) -{ - struct sk_buff *skb; - struct sock *newsk; - DEFINE_WAIT(wait); - struct sock *sk; - int err = 0; - - if ((sk = sock->sk) == NULL) - return -EINVAL; - - lock_sock(sk); - if (sk->sk_type != SOCK_SEQPACKET) { - err = -EOPNOTSUPP; - goto out_release; - } - - if (sk->sk_state != TCP_LISTEN) { - err = -EINVAL; - goto out_release; - } - - /* - * The write queue this time is holding sockets ready to use - * hooked into the SABM we saved - */ - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - - skb = skb_dequeue(&sk->sk_receive_queue); - if (skb) - break; - - if (arg->flags & O_NONBLOCK) { - err = -EWOULDBLOCK; - break; - } - if (!signal_pending(current)) { - release_sock(sk); - schedule(); - lock_sock(sk); - continue; - } - err = -ERESTARTSYS; - break; - } - finish_wait(sk_sleep(sk), &wait); - if (err) - goto out_release; - - newsk = skb->sk; - sock_graft(newsk, newsock); - - /* Now attach up the new socket */ - skb->sk = NULL; - kfree_skb(skb); - sk_acceptq_removed(sk); - -out_release: - release_sock(sk); - - return err; -} - -static int rose_getname(struct socket *sock, struct sockaddr *uaddr, - int peer) -{ - struct full_sockaddr_rose *srose = (struct full_sockaddr_rose *)uaddr; - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - int n; - - memset(srose, 0, sizeof(*srose)); - if (peer != 0) { - if (sk->sk_state != TCP_ESTABLISHED) - return -ENOTCONN; - srose->srose_family = AF_ROSE; - srose->srose_addr = rose->dest_addr; - srose->srose_call = rose->dest_call; - srose->srose_ndigis = rose->dest_ndigis; - for (n = 0; n < rose->dest_ndigis; n++) - srose->srose_digis[n] = rose->dest_digis[n]; - } else { - srose->srose_family = AF_ROSE; - srose->srose_addr = rose->source_addr; - srose->srose_call = rose->source_call; - srose->srose_ndigis = rose->source_ndigis; - for (n = 0; n < rose->source_ndigis; n++) - srose->srose_digis[n] = rose->source_digis[n]; - } - - return sizeof(struct full_sockaddr_rose); -} - -int rose_rx_call_request(struct sk_buff *skb, struct net_device *dev, struct rose_neigh *neigh, unsigned int lci) -{ - struct sock *sk; - struct sock *make; - struct rose_sock *make_rose; - struct rose_facilities_struct facilities; - int n; - - skb->sk = NULL; /* Initially we don't know who it's for */ - - /* - * skb->data points to the rose frame start - */ - memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); - - if (!rose_parse_facilities(skb->data + ROSE_CALL_REQ_FACILITIES_OFF, - skb->len - ROSE_CALL_REQ_FACILITIES_OFF, - &facilities)) { - rose_transmit_clear_request(neigh, lci, ROSE_INVALID_FACILITY, 76); - return 0; - } - - sk = rose_find_listener(&facilities.source_addr, &facilities.source_call); - - /* - * We can't accept the Call Request. - */ - if (sk == NULL || sk_acceptq_is_full(sk) || - (make = rose_make_new(sk)) == NULL) { - rose_transmit_clear_request(neigh, lci, ROSE_NETWORK_CONGESTION, 120); - return 0; - } - - skb->sk = make; - make->sk_state = TCP_ESTABLISHED; - make_rose = rose_sk(make); - - make_rose->lci = lci; - make_rose->dest_addr = facilities.dest_addr; - make_rose->dest_call = facilities.dest_call; - make_rose->dest_ndigis = facilities.dest_ndigis; - for (n = 0 ; n < facilities.dest_ndigis ; n++) - make_rose->dest_digis[n] = facilities.dest_digis[n]; - make_rose->source_addr = facilities.source_addr; - make_rose->source_call = facilities.source_call; - make_rose->source_ndigis = facilities.source_ndigis; - for (n = 0 ; n < facilities.source_ndigis ; n++) - make_rose->source_digis[n] = facilities.source_digis[n]; - make_rose->neighbour = neigh; - make_rose->device = dev; - /* Caller got a reference for us. */ - netdev_tracker_alloc(make_rose->device, &make_rose->dev_tracker, - GFP_ATOMIC); - make_rose->facilities = facilities; - - rose_neigh_hold(make_rose->neighbour); - - if (rose_sk(sk)->defer) { - make_rose->state = ROSE_STATE_5; - } else { - rose_write_internal(make, ROSE_CALL_ACCEPTED); - make_rose->state = ROSE_STATE_3; - rose_start_idletimer(make); - } - - make_rose->condition = 0x00; - make_rose->vs = 0; - make_rose->va = 0; - make_rose->vr = 0; - make_rose->vl = 0; - sk_acceptq_added(sk); - - rose_insert_socket(make); - - skb_queue_head(&sk->sk_receive_queue, skb); - - rose_start_heartbeat(make); - - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_data_ready(sk); - - return 1; -} - -static int rose_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - DECLARE_SOCKADDR(struct sockaddr_rose *, usrose, msg->msg_name); - int err; - struct full_sockaddr_rose srose; - struct sk_buff *skb; - unsigned char *asmptr; - int n, size, qbit = 0; - - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_CMSG_COMPAT)) - return -EINVAL; - - if (sock_flag(sk, SOCK_ZAPPED)) - return -EADDRNOTAVAIL; - - if (sk->sk_shutdown & SEND_SHUTDOWN) { - send_sig(SIGPIPE, current, 0); - return -EPIPE; - } - - if (rose->neighbour == NULL || rose->device == NULL) - return -ENETUNREACH; - - if (usrose != NULL) { - if (msg->msg_namelen != sizeof(struct sockaddr_rose) && msg->msg_namelen != sizeof(struct full_sockaddr_rose)) - return -EINVAL; - memset(&srose, 0, sizeof(struct full_sockaddr_rose)); - memcpy(&srose, usrose, msg->msg_namelen); - if (rosecmp(&rose->dest_addr, &srose.srose_addr) != 0 || - ax25cmp(&rose->dest_call, &srose.srose_call) != 0) - return -EISCONN; - if (srose.srose_ndigis != rose->dest_ndigis) - return -EISCONN; - if (srose.srose_ndigis == rose->dest_ndigis) { - for (n = 0 ; n < srose.srose_ndigis ; n++) - if (ax25cmp(&rose->dest_digis[n], - &srose.srose_digis[n])) - return -EISCONN; - } - if (srose.srose_family != AF_ROSE) - return -EINVAL; - } else { - if (sk->sk_state != TCP_ESTABLISHED) - return -ENOTCONN; - - srose.srose_family = AF_ROSE; - srose.srose_addr = rose->dest_addr; - srose.srose_call = rose->dest_call; - srose.srose_ndigis = rose->dest_ndigis; - for (n = 0 ; n < rose->dest_ndigis ; n++) - srose.srose_digis[n] = rose->dest_digis[n]; - } - - /* Build a packet */ - /* Sanity check the packet size */ - if (len > 65535) - return -EMSGSIZE; - - size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN; - - if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) - return err; - - skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN); - - /* - * Put the data on the end - */ - - skb_reset_transport_header(skb); - skb_put(skb, len); - - err = memcpy_from_msg(skb_transport_header(skb), msg, len); - if (err) { - kfree_skb(skb); - return err; - } - - /* - * If the Q BIT Include socket option is in force, the first - * byte of the user data is the logical value of the Q Bit. - */ - if (rose->qbitincl) { - qbit = skb->data[0]; - skb_pull(skb, 1); - } - - /* - * Push down the ROSE header - */ - asmptr = skb_push(skb, ROSE_MIN_LEN); - - /* Build a ROSE Network header */ - asmptr[0] = ((rose->lci >> 8) & 0x0F) | ROSE_GFI; - asmptr[1] = (rose->lci >> 0) & 0xFF; - asmptr[2] = ROSE_DATA; - - if (qbit) - asmptr[0] |= ROSE_Q_BIT; - - if (sk->sk_state != TCP_ESTABLISHED) { - kfree_skb(skb); - return -ENOTCONN; - } - -#ifdef M_BIT -#define ROSE_PACLEN (256-ROSE_MIN_LEN) - if (skb->len - ROSE_MIN_LEN > ROSE_PACLEN) { - unsigned char header[ROSE_MIN_LEN]; - struct sk_buff *skbn; - int frontlen; - int lg; - - /* Save a copy of the Header */ - skb_copy_from_linear_data(skb, header, ROSE_MIN_LEN); - skb_pull(skb, ROSE_MIN_LEN); - - frontlen = skb_headroom(skb); - - while (skb->len > 0) { - if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, &err)) == NULL) { - kfree_skb(skb); - return err; - } - - skbn->sk = sk; - skbn->free = 1; - skbn->arp = 1; - - skb_reserve(skbn, frontlen); - - lg = (ROSE_PACLEN > skb->len) ? skb->len : ROSE_PACLEN; - - /* Copy the user data */ - skb_copy_from_linear_data(skb, skb_put(skbn, lg), lg); - skb_pull(skb, lg); - - /* Duplicate the Header */ - skb_push(skbn, ROSE_MIN_LEN); - skb_copy_to_linear_data(skbn, header, ROSE_MIN_LEN); - - if (skb->len > 0) - skbn->data[2] |= M_BIT; - - skb_queue_tail(&sk->sk_write_queue, skbn); /* Throw it on the queue */ - } - - skb->free = 1; - kfree_skb(skb); - } else { - skb_queue_tail(&sk->sk_write_queue, skb); /* Throw it on the queue */ - } -#else - skb_queue_tail(&sk->sk_write_queue, skb); /* Shove it onto the queue */ -#endif - - rose_kick(sk); - - return len; -} - - -static int rose_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, - int flags) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - size_t copied; - unsigned char *asmptr; - struct sk_buff *skb; - int n, er, qbit; - - /* - * This works for seqpacket too. The receiver has ordered the queue for - * us! We do one quick check first though - */ - if (sk->sk_state != TCP_ESTABLISHED) - return -ENOTCONN; - - /* Now we can treat all alike */ - skb = skb_recv_datagram(sk, flags, &er); - if (!skb) - return er; - - qbit = (skb->data[0] & ROSE_Q_BIT) == ROSE_Q_BIT; - - skb_pull(skb, ROSE_MIN_LEN); - - if (rose->qbitincl) { - asmptr = skb_push(skb, 1); - *asmptr = qbit; - } - - skb_reset_transport_header(skb); - copied = skb->len; - - if (copied > size) { - copied = size; - msg->msg_flags |= MSG_TRUNC; - } - - skb_copy_datagram_msg(skb, 0, msg, copied); - - if (msg->msg_name) { - struct sockaddr_rose *srose; - DECLARE_SOCKADDR(struct full_sockaddr_rose *, full_srose, - msg->msg_name); - - memset(msg->msg_name, 0, sizeof(struct full_sockaddr_rose)); - srose = msg->msg_name; - srose->srose_family = AF_ROSE; - srose->srose_addr = rose->dest_addr; - srose->srose_call = rose->dest_call; - srose->srose_ndigis = rose->dest_ndigis; - for (n = 0 ; n < rose->dest_ndigis ; n++) - full_srose->srose_digis[n] = rose->dest_digis[n]; - msg->msg_namelen = sizeof(struct full_sockaddr_rose); - } - - skb_free_datagram(sk, skb); - - return copied; -} - - -static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk = sock->sk; - struct rose_sock *rose = rose_sk(sk); - void __user *argp = (void __user *)arg; - - switch (cmd) { - case TIOCOUTQ: { - long amount; - - amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - if (amount < 0) - amount = 0; - return put_user(amount, (unsigned int __user *) argp); - } - - case TIOCINQ: { - struct sk_buff *skb; - long amount = 0L; - - spin_lock_irq(&sk->sk_receive_queue.lock); - if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) - amount = skb->len; - spin_unlock_irq(&sk->sk_receive_queue.lock); - return put_user(amount, (unsigned int __user *) argp); - } - - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMETRIC: - case SIOCSIFMETRIC: - return -EINVAL; - - case SIOCADDRT: - case SIOCDELRT: - case SIOCRSCLRRT: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - return rose_rt_ioctl(cmd, argp); - - case SIOCRSGCAUSE: { - struct rose_cause_struct rose_cause; - rose_cause.cause = rose->cause; - rose_cause.diagnostic = rose->diagnostic; - return copy_to_user(argp, &rose_cause, sizeof(struct rose_cause_struct)) ? -EFAULT : 0; - } - - case SIOCRSSCAUSE: { - struct rose_cause_struct rose_cause; - if (copy_from_user(&rose_cause, argp, sizeof(struct rose_cause_struct))) - return -EFAULT; - rose->cause = rose_cause.cause; - rose->diagnostic = rose_cause.diagnostic; - return 0; - } - - case SIOCRSSL2CALL: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) - ax25_listen_release(&rose_callsign, NULL); - if (copy_from_user(&rose_callsign, argp, sizeof(ax25_address))) - return -EFAULT; - if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) - return ax25_listen_register(&rose_callsign, NULL); - - return 0; - - case SIOCRSGL2CALL: - return copy_to_user(argp, &rose_callsign, sizeof(ax25_address)) ? -EFAULT : 0; - - case SIOCRSACCEPT: - if (rose->state == ROSE_STATE_5) { - rose_write_internal(sk, ROSE_CALL_ACCEPTED); - rose_start_idletimer(sk); - rose->condition = 0x00; - rose->vs = 0; - rose->va = 0; - rose->vr = 0; - rose->vl = 0; - rose->state = ROSE_STATE_3; - } - return 0; - - default: - return -ENOIOCTLCMD; - } - - return 0; -} - -#ifdef CONFIG_PROC_FS -static void *rose_info_start(struct seq_file *seq, loff_t *pos) - __acquires(rose_list_lock) -{ - spin_lock_bh(&rose_list_lock); - return seq_hlist_start_head(&rose_list, *pos); -} - -static void *rose_info_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &rose_list, pos); -} - -static void rose_info_stop(struct seq_file *seq, void *v) - __releases(rose_list_lock) -{ - spin_unlock_bh(&rose_list_lock); -} - -static int rose_info_show(struct seq_file *seq, void *v) -{ - char buf[11], rsbuf[11]; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "dest_addr dest_call src_addr src_call dev lci neigh st vs vr va t t1 t2 t3 hb idle Snd-Q Rcv-Q inode\n"); - - else { - struct sock *s = sk_entry(v); - struct rose_sock *rose = rose_sk(s); - const char *devname, *callsign; - const struct net_device *dev = rose->device; - - if (!dev) - devname = "???"; - else - devname = dev->name; - - seq_printf(seq, "%-10s %-9s ", - rose2asc(rsbuf, &rose->dest_addr), - ax2asc(buf, &rose->dest_call)); - - if (ax25cmp(&rose->source_call, &null_ax25_address) == 0) - callsign = "??????-?"; - else - callsign = ax2asc(buf, &rose->source_call); - - seq_printf(seq, - "%-10s %-9s %-5s %3.3X %05d %d %d %d %d %3lu %3lu %3lu %3lu %3lu %3lu/%03lu %5d %5d %llu\n", - rose2asc(rsbuf, &rose->source_addr), - callsign, - devname, - rose->lci & 0x0FFF, - (rose->neighbour) ? rose->neighbour->number : 0, - rose->state, - rose->vs, - rose->vr, - rose->va, - ax25_display_timer(&rose->timer) / HZ, - rose->t1 / HZ, - rose->t2 / HZ, - rose->t3 / HZ, - rose->hb / HZ, - ax25_display_timer(&rose->idletimer) / (60 * HZ), - rose->idle / (60 * HZ), - sk_wmem_alloc_get(s), - sk_rmem_alloc_get(s), - s->sk_socket ? SOCK_INODE(s->sk_socket)->i_ino : (u64)0); - } - - return 0; -} - -static const struct seq_operations rose_info_seqops = { - .start = rose_info_start, - .next = rose_info_next, - .stop = rose_info_stop, - .show = rose_info_show, -}; -#endif /* CONFIG_PROC_FS */ - -static const struct net_proto_family rose_family_ops = { - .family = PF_ROSE, - .create = rose_create, - .owner = THIS_MODULE, -}; - -static const struct proto_ops rose_proto_ops = { - .family = PF_ROSE, - .owner = THIS_MODULE, - .release = rose_release, - .bind = rose_bind, - .connect = rose_connect, - .socketpair = sock_no_socketpair, - .accept = rose_accept, - .getname = rose_getname, - .poll = datagram_poll, - .ioctl = rose_ioctl, - .gettstamp = sock_gettstamp, - .listen = rose_listen, - .shutdown = sock_no_shutdown, - .setsockopt = rose_setsockopt, - .getsockopt = rose_getsockopt, - .sendmsg = rose_sendmsg, - .recvmsg = rose_recvmsg, - .mmap = sock_no_mmap, -}; - -static struct notifier_block rose_dev_notifier = { - .notifier_call = rose_device_event, -}; - -static struct net_device **dev_rose; - -static struct ax25_protocol rose_pid = { - .pid = AX25_P_ROSE, - .func = rose_route_frame -}; - -static struct ax25_linkfail rose_linkfail_notifier = { - .func = rose_link_failed -}; - -static int __init rose_proto_init(void) -{ - int i; - int rc; - - if (rose_ndevs > 0x7FFFFFFF/sizeof(struct net_device *)) { - printk(KERN_ERR "ROSE: rose_proto_init - rose_ndevs parameter too large\n"); - rc = -EINVAL; - goto out; - } - - rc = proto_register(&rose_proto, 0); - if (rc != 0) - goto out; - - rose_callsign = null_ax25_address; - - dev_rose = kzalloc_objs(struct net_device *, rose_ndevs); - if (dev_rose == NULL) { - printk(KERN_ERR "ROSE: rose_proto_init - unable to allocate device structure\n"); - rc = -ENOMEM; - goto out_proto_unregister; - } - - for (i = 0; i < rose_ndevs; i++) { - struct net_device *dev; - char name[IFNAMSIZ]; - - sprintf(name, "rose%d", i); - dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, rose_setup); - if (!dev) { - printk(KERN_ERR "ROSE: rose_proto_init - unable to allocate memory\n"); - rc = -ENOMEM; - goto fail; - } - rc = register_netdev(dev); - if (rc) { - printk(KERN_ERR "ROSE: netdevice registration failed\n"); - free_netdev(dev); - goto fail; - } - rose_set_lockdep_key(dev); - dev_rose[i] = dev; - } - - sock_register(&rose_family_ops); - register_netdevice_notifier(&rose_dev_notifier); - - ax25_register_pid(&rose_pid); - ax25_linkfail_register(&rose_linkfail_notifier); - -#ifdef CONFIG_SYSCTL - rose_register_sysctl(); -#endif - rose_loopback_init(); - - rose_add_loopback_neigh(); - - proc_create_seq("rose", 0444, init_net.proc_net, &rose_info_seqops); - proc_create_seq("rose_neigh", 0444, init_net.proc_net, - &rose_neigh_seqops); - proc_create_seq("rose_nodes", 0444, init_net.proc_net, - &rose_node_seqops); - proc_create_seq("rose_routes", 0444, init_net.proc_net, - &rose_route_seqops); -out: - return rc; -fail: - while (--i >= 0) { - unregister_netdev(dev_rose[i]); - free_netdev(dev_rose[i]); - } - kfree(dev_rose); -out_proto_unregister: - proto_unregister(&rose_proto); - goto out; -} -module_init(rose_proto_init); - -module_param(rose_ndevs, int, 0); -MODULE_PARM_DESC(rose_ndevs, "number of ROSE devices"); - -MODULE_AUTHOR("Jonathan Naylor G4KLX "); -MODULE_DESCRIPTION("The amateur radio ROSE network layer protocol"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(PF_ROSE); - -static void __exit rose_exit(void) -{ - int i; - - remove_proc_entry("rose", init_net.proc_net); - remove_proc_entry("rose_neigh", init_net.proc_net); - remove_proc_entry("rose_nodes", init_net.proc_net); - remove_proc_entry("rose_routes", init_net.proc_net); - rose_loopback_clear(); - - rose_rt_free(); - - ax25_protocol_release(AX25_P_ROSE); - ax25_linkfail_release(&rose_linkfail_notifier); - - if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) - ax25_listen_release(&rose_callsign, NULL); - -#ifdef CONFIG_SYSCTL - rose_unregister_sysctl(); -#endif - unregister_netdevice_notifier(&rose_dev_notifier); - - sock_unregister(PF_ROSE); - - for (i = 0; i < rose_ndevs; i++) { - struct net_device *dev = dev_rose[i]; - - if (dev) { - unregister_netdev(dev); - free_netdev(dev); - } - } - - kfree(dev_rose); - proto_unregister(&rose_proto); -} - -module_exit(rose_exit); diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c deleted file mode 100644 index f1a76a5820f1..000000000000 --- a/net/rose/rose_dev.c +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -static int rose_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, - const void *daddr, const void *saddr, unsigned int len) -{ - unsigned char *buff = skb_push(skb, ROSE_MIN_LEN + 2); - - if (daddr) - memcpy(buff + 7, daddr, dev->addr_len); - - *buff++ = ROSE_GFI | ROSE_Q_BIT; - *buff++ = 0x00; - *buff++ = ROSE_DATA; - *buff++ = 0x7F; - *buff++ = AX25_P_IP; - - if (daddr != NULL) - return 37; - - return -37; -} - -static int rose_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = addr; - int err; - - if (!memcmp(dev->dev_addr, sa->sa_data, dev->addr_len)) - return 0; - - if (dev->flags & IFF_UP) { - err = rose_add_loopback_node((rose_address *)sa->sa_data); - if (err) - return err; - - rose_del_loopback_node((const rose_address *)dev->dev_addr); - } - - dev_addr_set(dev, sa->sa_data); - - return 0; -} - -static int rose_open(struct net_device *dev) -{ - int err; - - err = rose_add_loopback_node((const rose_address *)dev->dev_addr); - if (err) - return err; - - netif_start_queue(dev); - - return 0; -} - -static int rose_close(struct net_device *dev) -{ - netif_stop_queue(dev); - rose_del_loopback_node((const rose_address *)dev->dev_addr); - return 0; -} - -static netdev_tx_t rose_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct net_device_stats *stats = &dev->stats; - unsigned int len = skb->len; - - if (!netif_running(dev)) { - printk(KERN_ERR "ROSE: rose_xmit - called when iface is down\n"); - return NETDEV_TX_BUSY; - } - - if (!rose_route_frame(skb, NULL)) { - dev_kfree_skb(skb); - stats->tx_errors++; - return NETDEV_TX_OK; - } - - stats->tx_packets++; - stats->tx_bytes += len; - return NETDEV_TX_OK; -} - -static const struct header_ops rose_header_ops = { - .create = rose_header, -}; - -static const struct net_device_ops rose_netdev_ops = { - .ndo_open = rose_open, - .ndo_stop = rose_close, - .ndo_start_xmit = rose_xmit, - .ndo_set_mac_address = rose_set_mac_address, -}; - -void rose_setup(struct net_device *dev) -{ - dev->mtu = ROSE_MAX_PACKET_SIZE - 2; - dev->netdev_ops = &rose_netdev_ops; - - dev->header_ops = &rose_header_ops; - dev->hard_header_len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN; - dev->addr_len = ROSE_ADDR_LEN; - dev->type = ARPHRD_ROSE; - - /* New-style flags. */ - dev->flags = IFF_NOARP; -} diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c deleted file mode 100644 index ca4f217ef3d3..000000000000 --- a/net/rose/rose_in.c +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * - * Most of this code is based on the SDL diagrams published in the 7th ARRL - * Computer Networking Conference papers. The diagrams have mistakes in them, - * but are mostly correct. Before you modify the code could you read the SDL - * diagrams as the code is not obvious and probably very easy to break. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * State machine for state 1, Awaiting Call Accepted State. - * The handling of the timer(s) is in file rose_timer.c. - * Handling of state 0 and connection release is in af_rose.c. - */ -static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype) -{ - struct rose_sock *rose = rose_sk(sk); - - switch (frametype) { - case ROSE_CALL_ACCEPTED: - rose_stop_timer(sk); - rose_start_idletimer(sk); - rose->condition = 0x00; - rose->vs = 0; - rose->va = 0; - rose->vr = 0; - rose->vl = 0; - rose->state = ROSE_STATE_3; - sk->sk_state = TCP_ESTABLISHED; - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_state_change(sk); - break; - - case ROSE_CLEAR_REQUEST: - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); - rose_neigh_put(rose->neighbour); - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 2, Awaiting Clear Confirmation State. - * The handling of the timer(s) is in file rose_timer.c - * Handling of state 0 and connection release is in af_rose.c. - */ -static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype) -{ - struct rose_sock *rose = rose_sk(sk); - - switch (frametype) { - case ROSE_CLEAR_REQUEST: - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - rose_neigh_put(rose->neighbour); - break; - - case ROSE_CLEAR_CONFIRMATION: - rose_disconnect(sk, 0, -1, -1); - rose_neigh_put(rose->neighbour); - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 3, Connected State. - * The handling of the timer(s) is in file rose_timer.c - * Handling of state 0 and connection release is in af_rose.c. - */ -static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype, int ns, int nr, int q, int d, int m) -{ - struct rose_sock *rose = rose_sk(sk); - int queued = 0; - - switch (frametype) { - case ROSE_RESET_REQUEST: - rose_stop_timer(sk); - rose_start_idletimer(sk); - rose_write_internal(sk, ROSE_RESET_CONFIRMATION); - rose->condition = 0x00; - rose->vs = 0; - rose->vr = 0; - rose->va = 0; - rose->vl = 0; - rose_requeue_frames(sk); - break; - - case ROSE_CLEAR_REQUEST: - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - rose_neigh_put(rose->neighbour); - break; - - case ROSE_RR: - case ROSE_RNR: - if (!rose_validate_nr(sk, nr)) { - rose_write_internal(sk, ROSE_RESET_REQUEST); - rose->condition = 0x00; - rose->vs = 0; - rose->vr = 0; - rose->va = 0; - rose->vl = 0; - rose->state = ROSE_STATE_4; - rose_start_t2timer(sk); - rose_stop_idletimer(sk); - } else { - rose_frames_acked(sk, nr); - if (frametype == ROSE_RNR) { - rose->condition |= ROSE_COND_PEER_RX_BUSY; - } else { - rose->condition &= ~ROSE_COND_PEER_RX_BUSY; - } - } - break; - - case ROSE_DATA: /* XXX */ - rose->condition &= ~ROSE_COND_PEER_RX_BUSY; - if (!rose_validate_nr(sk, nr)) { - rose_write_internal(sk, ROSE_RESET_REQUEST); - rose->condition = 0x00; - rose->vs = 0; - rose->vr = 0; - rose->va = 0; - rose->vl = 0; - rose->state = ROSE_STATE_4; - rose_start_t2timer(sk); - rose_stop_idletimer(sk); - break; - } - rose_frames_acked(sk, nr); - if (ns == rose->vr) { - rose_start_idletimer(sk); - if (!sk_filter_trim_cap(sk, skb, ROSE_MIN_LEN) && - __sock_queue_rcv_skb(sk, skb) == 0) { - rose->vr = (rose->vr + 1) % ROSE_MODULUS; - queued = 1; - } else { - /* Should never happen ! */ - rose_write_internal(sk, ROSE_RESET_REQUEST); - rose->condition = 0x00; - rose->vs = 0; - rose->vr = 0; - rose->va = 0; - rose->vl = 0; - rose->state = ROSE_STATE_4; - rose_start_t2timer(sk); - rose_stop_idletimer(sk); - break; - } - if (atomic_read(&sk->sk_rmem_alloc) > - (sk->sk_rcvbuf >> 1)) - rose->condition |= ROSE_COND_OWN_RX_BUSY; - } - /* - * If the window is full, ack the frame, else start the - * acknowledge hold back timer. - */ - if (((rose->vl + sysctl_rose_window_size) % ROSE_MODULUS) == rose->vr) { - rose->condition &= ~ROSE_COND_ACK_PENDING; - rose_stop_timer(sk); - rose_enquiry_response(sk); - } else { - rose->condition |= ROSE_COND_ACK_PENDING; - rose_start_hbtimer(sk); - } - break; - - default: - printk(KERN_WARNING "ROSE: unknown %02X in state 3\n", frametype); - break; - } - - return queued; -} - -/* - * State machine for state 4, Awaiting Reset Confirmation State. - * The handling of the timer(s) is in file rose_timer.c - * Handling of state 0 and connection release is in af_rose.c. - */ -static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype) -{ - struct rose_sock *rose = rose_sk(sk); - - switch (frametype) { - case ROSE_RESET_REQUEST: - rose_write_internal(sk, ROSE_RESET_CONFIRMATION); - fallthrough; - case ROSE_RESET_CONFIRMATION: - rose_stop_timer(sk); - rose_start_idletimer(sk); - rose->condition = 0x00; - rose->va = 0; - rose->vr = 0; - rose->vs = 0; - rose->vl = 0; - rose->state = ROSE_STATE_3; - rose_requeue_frames(sk); - break; - - case ROSE_CLEAR_REQUEST: - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - rose_neigh_put(rose->neighbour); - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 5, Awaiting Call Acceptance State. - * The handling of the timer(s) is in file rose_timer.c - * Handling of state 0 and connection release is in af_rose.c. - */ -static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int frametype) -{ - if (frametype == ROSE_CLEAR_REQUEST) { - rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - rose_disconnect(sk, 0, skb->data[3], skb->data[4]); - rose_neigh_put(rose_sk(sk)->neighbour); - } - - return 0; -} - -/* Higher level upcall for a LAPB frame */ -int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb) -{ - struct rose_sock *rose = rose_sk(sk); - int queued = 0, frametype, ns, nr, q, d, m; - - if (rose->state == ROSE_STATE_0) - return 0; - - frametype = rose_decode(skb, &ns, &nr, &q, &d, &m); - - /* - * ROSE_CLEAR_REQUEST carries cause and diagnostic in bytes 3..4. - * Reject a malformed frame that is too short to contain them. - */ - if (frametype == ROSE_CLEAR_REQUEST && skb->len < 5) - return 0; - - switch (rose->state) { - case ROSE_STATE_1: - queued = rose_state1_machine(sk, skb, frametype); - break; - case ROSE_STATE_2: - queued = rose_state2_machine(sk, skb, frametype); - break; - case ROSE_STATE_3: - queued = rose_state3_machine(sk, skb, frametype, ns, nr, q, d, m); - break; - case ROSE_STATE_4: - queued = rose_state4_machine(sk, skb, frametype); - break; - case ROSE_STATE_5: - queued = rose_state5_machine(sk, skb, frametype); - break; - } - - rose_kick(sk); - - return queued; -} diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c deleted file mode 100644 index 7746229fdc8c..000000000000 --- a/net/rose/rose_link.c +++ /dev/null @@ -1,289 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void rose_ftimer_expiry(struct timer_list *); -static void rose_t0timer_expiry(struct timer_list *); - -static void rose_transmit_restart_confirmation(struct rose_neigh *neigh); -static void rose_transmit_restart_request(struct rose_neigh *neigh); - -void rose_start_ftimer(struct rose_neigh *neigh) -{ - timer_delete(&neigh->ftimer); - - neigh->ftimer.function = rose_ftimer_expiry; - neigh->ftimer.expires = - jiffies + msecs_to_jiffies(sysctl_rose_link_fail_timeout); - - add_timer(&neigh->ftimer); -} - -static void rose_start_t0timer(struct rose_neigh *neigh) -{ - timer_delete(&neigh->t0timer); - - neigh->t0timer.function = rose_t0timer_expiry; - neigh->t0timer.expires = - jiffies + msecs_to_jiffies(sysctl_rose_restart_request_timeout); - - add_timer(&neigh->t0timer); -} - -void rose_stop_ftimer(struct rose_neigh *neigh) -{ - timer_delete(&neigh->ftimer); -} - -void rose_stop_t0timer(struct rose_neigh *neigh) -{ - timer_delete(&neigh->t0timer); -} - -int rose_ftimer_running(struct rose_neigh *neigh) -{ - return timer_pending(&neigh->ftimer); -} - -static int rose_t0timer_running(struct rose_neigh *neigh) -{ - return timer_pending(&neigh->t0timer); -} - -static void rose_ftimer_expiry(struct timer_list *t) -{ -} - -static void rose_t0timer_expiry(struct timer_list *t) -{ - struct rose_neigh *neigh = timer_container_of(neigh, t, t0timer); - - rose_transmit_restart_request(neigh); - - neigh->dce_mode = 0; - - rose_start_t0timer(neigh); -} - -/* - * Interface to ax25_send_frame. Changes my level 2 callsign depending - * on whether we have a global ROSE callsign or use the default port - * callsign. - */ -static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh) -{ - const ax25_address *rose_call; - ax25_cb *ax25s; - - if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) - rose_call = (const ax25_address *)neigh->dev->dev_addr; - else - rose_call = &rose_callsign; - - ax25s = neigh->ax25; - neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); - if (ax25s) - ax25_cb_put(ax25s); - - return neigh->ax25 != NULL; -} - -/* - * Interface to ax25_link_up. Changes my level 2 callsign depending - * on whether we have a global ROSE callsign or use the default port - * callsign. - */ -static int rose_link_up(struct rose_neigh *neigh) -{ - const ax25_address *rose_call; - ax25_cb *ax25s; - - if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) - rose_call = (const ax25_address *)neigh->dev->dev_addr; - else - rose_call = &rose_callsign; - - ax25s = neigh->ax25; - neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); - if (ax25s) - ax25_cb_put(ax25s); - - return neigh->ax25 != NULL; -} - -/* - * This handles all restart and diagnostic frames. - */ -void rose_link_rx_restart(struct sk_buff *skb, struct rose_neigh *neigh, unsigned short frametype) -{ - struct sk_buff *skbn; - - switch (frametype) { - case ROSE_RESTART_REQUEST: - rose_stop_t0timer(neigh); - neigh->restarted = 1; - neigh->dce_mode = (skb->data[3] == ROSE_DTE_ORIGINATED); - rose_transmit_restart_confirmation(neigh); - break; - - case ROSE_RESTART_CONFIRMATION: - rose_stop_t0timer(neigh); - neigh->restarted = 1; - break; - - case ROSE_DIAGNOSTIC: - pr_warn("ROSE: received diagnostic #%d - %3ph\n", skb->data[3], - skb->data + 4); - break; - - default: - printk(KERN_WARNING "ROSE: received unknown %02X with LCI 000\n", frametype); - break; - } - - if (neigh->restarted) { - while ((skbn = skb_dequeue(&neigh->queue)) != NULL) - if (!rose_send_frame(skbn, neigh)) - kfree_skb(skbn); - } -} - -/* - * This routine is called when a Restart Request is needed - */ -static void rose_transmit_restart_request(struct rose_neigh *neigh) -{ - struct sk_buff *skb; - unsigned char *dptr; - int len; - - len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3; - - if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) - return; - - skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN); - - dptr = skb_put(skb, ROSE_MIN_LEN + 3); - - *dptr++ = AX25_P_ROSE; - *dptr++ = ROSE_GFI; - *dptr++ = 0x00; - *dptr++ = ROSE_RESTART_REQUEST; - *dptr++ = ROSE_DTE_ORIGINATED; - *dptr++ = 0; - - if (!rose_send_frame(skb, neigh)) - kfree_skb(skb); -} - -/* - * This routine is called when a Restart Confirmation is needed - */ -static void rose_transmit_restart_confirmation(struct rose_neigh *neigh) -{ - struct sk_buff *skb; - unsigned char *dptr; - int len; - - len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1; - - if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) - return; - - skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN); - - dptr = skb_put(skb, ROSE_MIN_LEN + 1); - - *dptr++ = AX25_P_ROSE; - *dptr++ = ROSE_GFI; - *dptr++ = 0x00; - *dptr++ = ROSE_RESTART_CONFIRMATION; - - if (!rose_send_frame(skb, neigh)) - kfree_skb(skb); -} - -/* - * This routine is called when a Clear Request is needed outside of the context - * of a connected socket. - */ -void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, unsigned char cause, unsigned char diagnostic) -{ - struct sk_buff *skb; - unsigned char *dptr; - int len; - - if (!neigh->dev) - return; - - len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3; - - if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) - return; - - skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN); - - dptr = skb_put(skb, ROSE_MIN_LEN + 3); - - *dptr++ = AX25_P_ROSE; - *dptr++ = ((lci >> 8) & 0x0F) | ROSE_GFI; - *dptr++ = ((lci >> 0) & 0xFF); - *dptr++ = ROSE_CLEAR_REQUEST; - *dptr++ = cause; - *dptr++ = diagnostic; - - if (!rose_send_frame(skb, neigh)) - kfree_skb(skb); -} - -void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh) -{ - unsigned char *dptr; - - if (neigh->loopback) { - rose_loopback_queue(skb, neigh); - return; - } - - if (!rose_link_up(neigh)) - neigh->restarted = 0; - - dptr = skb_push(skb, 1); - *dptr++ = AX25_P_ROSE; - - if (neigh->restarted) { - if (!rose_send_frame(skb, neigh)) - kfree_skb(skb); - } else { - skb_queue_tail(&neigh->queue, skb); - - if (!rose_t0timer_running(neigh)) { - rose_transmit_restart_request(neigh); - neigh->dce_mode = 0; - rose_start_t0timer(neigh); - } - } -} diff --git a/net/rose/rose_loopback.c b/net/rose/rose_loopback.c deleted file mode 100644 index b538e39b3df5..000000000000 --- a/net/rose/rose_loopback.c +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include - -static struct sk_buff_head loopback_queue; -#define ROSE_LOOPBACK_LIMIT 1000 -static struct timer_list loopback_timer; - -static void rose_set_loopback_timer(void); -static void rose_loopback_timer(struct timer_list *unused); - -void rose_loopback_init(void) -{ - skb_queue_head_init(&loopback_queue); - - timer_setup(&loopback_timer, rose_loopback_timer, 0); -} - -static int rose_loopback_running(void) -{ - return timer_pending(&loopback_timer); -} - -int rose_loopback_queue(struct sk_buff *skb, struct rose_neigh *neigh) -{ - struct sk_buff *skbn = NULL; - - if (skb_queue_len(&loopback_queue) < ROSE_LOOPBACK_LIMIT) - skbn = skb_clone(skb, GFP_ATOMIC); - - if (skbn) { - consume_skb(skb); - skb_queue_tail(&loopback_queue, skbn); - - if (!rose_loopback_running()) - rose_set_loopback_timer(); - } else { - kfree_skb(skb); - } - - return 1; -} - -static void rose_set_loopback_timer(void) -{ - mod_timer(&loopback_timer, jiffies + 10); -} - -static void rose_loopback_timer(struct timer_list *unused) -{ - struct sk_buff *skb; - struct net_device *dev; - rose_address *dest; - struct sock *sk; - unsigned short frametype; - unsigned int lci_i, lci_o; - int count; - - for (count = 0; count < ROSE_LOOPBACK_LIMIT; count++) { - skb = skb_dequeue(&loopback_queue); - if (!skb) - return; - if (skb->len < ROSE_MIN_LEN) { - kfree_skb(skb); - continue; - } - lci_i = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); - frametype = skb->data[2]; - if (frametype == ROSE_CALL_REQUEST && - (skb->len <= ROSE_CALL_REQ_FACILITIES_OFF || - skb->data[ROSE_CALL_REQ_ADDR_LEN_OFF] != - ROSE_CALL_REQ_ADDR_LEN_VAL)) { - kfree_skb(skb); - continue; - } - dest = (rose_address *)(skb->data + ROSE_CALL_REQ_DEST_ADDR_OFF); - lci_o = ROSE_DEFAULT_MAXVC + 1 - lci_i; - - skb_reset_transport_header(skb); - - sk = rose_find_socket(lci_o, rose_loopback_neigh); - if (sk) { - if (rose_process_rx_frame(sk, skb) == 0) - kfree_skb(skb); - continue; - } - - if (frametype == ROSE_CALL_REQUEST) { - if (!rose_loopback_neigh->dev && - !rose_loopback_neigh->loopback) { - kfree_skb(skb); - continue; - } - - dev = rose_dev_get(dest); - if (!dev) { - kfree_skb(skb); - continue; - } - - if (rose_rx_call_request(skb, dev, rose_loopback_neigh, lci_o) == 0) { - dev_put(dev); - kfree_skb(skb); - } - } else { - kfree_skb(skb); - } - } - if (!skb_queue_empty(&loopback_queue)) - mod_timer(&loopback_timer, jiffies + 1); -} - -void __exit rose_loopback_clear(void) -{ - struct sk_buff *skb; - - timer_delete(&loopback_timer); - - while ((skb = skb_dequeue(&loopback_queue)) != NULL) { - skb->sk = NULL; - kfree_skb(skb); - } -} diff --git a/net/rose/rose_out.c b/net/rose/rose_out.c deleted file mode 100644 index 9050e33c9604..000000000000 --- a/net/rose/rose_out.c +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * This procedure is passed a buffer descriptor for an iframe. It builds - * the rest of the control part of the frame and then writes it out. - */ -static void rose_send_iframe(struct sock *sk, struct sk_buff *skb) -{ - struct rose_sock *rose = rose_sk(sk); - - if (skb == NULL) - return; - - skb->data[2] |= (rose->vr << 5) & 0xE0; - skb->data[2] |= (rose->vs << 1) & 0x0E; - - rose_start_idletimer(sk); - - rose_transmit_link(skb, rose->neighbour); -} - -void rose_kick(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - struct sk_buff *skb, *skbn; - unsigned short start, end; - - if (rose->state != ROSE_STATE_3) - return; - - if (rose->condition & ROSE_COND_PEER_RX_BUSY) - return; - - if (!skb_peek(&sk->sk_write_queue)) - return; - - start = (skb_peek(&rose->ack_queue) == NULL) ? rose->va : rose->vs; - end = (rose->va + sysctl_rose_window_size) % ROSE_MODULUS; - - if (start == end) - return; - - rose->vs = start; - - /* - * Transmit data until either we're out of data to send or - * the window is full. - */ - - skb = skb_dequeue(&sk->sk_write_queue); - - do { - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { - skb_queue_head(&sk->sk_write_queue, skb); - break; - } - - skb_set_owner_w(skbn, sk); - - /* - * Transmit the frame copy. - */ - rose_send_iframe(sk, skbn); - - rose->vs = (rose->vs + 1) % ROSE_MODULUS; - - /* - * Requeue the original data frame. - */ - skb_queue_tail(&rose->ack_queue, skb); - - } while (rose->vs != end && - (skb = skb_dequeue(&sk->sk_write_queue)) != NULL); - - rose->vl = rose->vr; - rose->condition &= ~ROSE_COND_ACK_PENDING; - - rose_stop_timer(sk); -} - -/* - * The following routines are taken from page 170 of the 7th ARRL Computer - * Networking Conference paper, as is the whole state machine. - */ - -void rose_enquiry_response(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - - if (rose->condition & ROSE_COND_OWN_RX_BUSY) - rose_write_internal(sk, ROSE_RNR); - else - rose_write_internal(sk, ROSE_RR); - - rose->vl = rose->vr; - rose->condition &= ~ROSE_COND_ACK_PENDING; - - rose_stop_timer(sk); -} diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c deleted file mode 100644 index e31842e6b3c8..000000000000 --- a/net/rose/rose_route.c +++ /dev/null @@ -1,1333 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) Terry Dawson VK2KTJ (terry@animats.net) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For TIOCINQ/OUTQ */ -#include -#include -#include -#include -#include -#include -#include - -static unsigned int rose_neigh_no = 1; - -static struct rose_node *rose_node_list; -static DEFINE_SPINLOCK(rose_node_list_lock); -static struct rose_neigh *rose_neigh_list; -static DEFINE_SPINLOCK(rose_neigh_list_lock); -static struct rose_route *rose_route_list; -static DEFINE_SPINLOCK(rose_route_list_lock); - -struct rose_neigh *rose_loopback_neigh; - -/* - * Add a new route to a node, and in the process add the node and the - * neighbour if it is new. - */ -static int __must_check rose_add_node(struct rose_route_struct *rose_route, - struct net_device *dev) -{ - struct rose_node *rose_node, *rose_tmpn, *rose_tmpp; - struct rose_neigh *rose_neigh; - int i, res = 0; - - spin_lock_bh(&rose_node_list_lock); - spin_lock_bh(&rose_neigh_list_lock); - - rose_node = rose_node_list; - while (rose_node != NULL) { - if ((rose_node->mask == rose_route->mask) && - (rosecmpm(&rose_route->address, &rose_node->address, - rose_route->mask) == 0)) - break; - rose_node = rose_node->next; - } - - if (rose_node != NULL && rose_node->loopback) { - res = -EINVAL; - goto out; - } - - rose_neigh = rose_neigh_list; - while (rose_neigh != NULL) { - if (ax25cmp(&rose_route->neighbour, - &rose_neigh->callsign) == 0 && - rose_neigh->dev == dev) - break; - rose_neigh = rose_neigh->next; - } - - if (rose_neigh == NULL) { - rose_neigh = kmalloc_obj(*rose_neigh, GFP_ATOMIC); - if (rose_neigh == NULL) { - res = -ENOMEM; - goto out; - } - - rose_neigh->callsign = rose_route->neighbour; - rose_neigh->digipeat = NULL; - rose_neigh->ax25 = NULL; - rose_neigh->dev = dev; - rose_neigh->count = 0; - rose_neigh->dce_mode = 0; - rose_neigh->loopback = 0; - rose_neigh->number = rose_neigh_no++; - rose_neigh->restarted = 0; - refcount_set(&rose_neigh->use, 1); - - skb_queue_head_init(&rose_neigh->queue); - - timer_setup(&rose_neigh->ftimer, NULL, 0); - timer_setup(&rose_neigh->t0timer, NULL, 0); - - if (rose_route->ndigis != 0) { - rose_neigh->digipeat = - kmalloc_obj(ax25_digi, GFP_ATOMIC); - if (rose_neigh->digipeat == NULL) { - kfree(rose_neigh); - res = -ENOMEM; - goto out; - } - - rose_neigh->digipeat->ndigi = rose_route->ndigis; - rose_neigh->digipeat->lastrepeat = -1; - - for (i = 0; i < rose_route->ndigis; i++) { - rose_neigh->digipeat->calls[i] = - rose_route->digipeaters[i]; - rose_neigh->digipeat->repeated[i] = 0; - } - } - - rose_neigh->next = rose_neigh_list; - rose_neigh_list = rose_neigh; - } - - /* - * This is a new node to be inserted into the list. Find where it needs - * to be inserted into the list, and insert it. We want to be sure - * to order the list in descending order of mask size to ensure that - * later when we are searching this list the first match will be the - * best match. - */ - if (rose_node == NULL) { - rose_tmpn = rose_node_list; - rose_tmpp = NULL; - - while (rose_tmpn != NULL) { - if (rose_tmpn->mask > rose_route->mask) { - rose_tmpp = rose_tmpn; - rose_tmpn = rose_tmpn->next; - } else { - break; - } - } - - /* create new node */ - rose_node = kmalloc_obj(*rose_node, GFP_ATOMIC); - if (rose_node == NULL) { - res = -ENOMEM; - goto out; - } - - rose_node->address = rose_route->address; - rose_node->mask = rose_route->mask; - rose_node->count = 1; - rose_node->loopback = 0; - rose_node->neighbour[0] = rose_neigh; - - if (rose_tmpn == NULL) { - if (rose_tmpp == NULL) { /* Empty list */ - rose_node_list = rose_node; - rose_node->next = NULL; - } else { - rose_tmpp->next = rose_node; - rose_node->next = NULL; - } - } else { - if (rose_tmpp == NULL) { /* 1st node */ - rose_node->next = rose_node_list; - rose_node_list = rose_node; - } else { - rose_tmpp->next = rose_node; - rose_node->next = rose_tmpn; - } - } - rose_neigh->count++; - rose_neigh_hold(rose_neigh); - - goto out; - } - - /* We have space, slot it in */ - if (rose_node->count < 3) { - rose_node->neighbour[rose_node->count] = rose_neigh; - rose_node->count++; - rose_neigh->count++; - rose_neigh_hold(rose_neigh); - } - -out: - spin_unlock_bh(&rose_neigh_list_lock); - spin_unlock_bh(&rose_node_list_lock); - - return res; -} - -/* - * Caller is holding rose_node_list_lock. - */ -static void rose_remove_node(struct rose_node *rose_node) -{ - struct rose_node *s; - - if ((s = rose_node_list) == rose_node) { - rose_node_list = rose_node->next; - kfree(rose_node); - return; - } - - while (s != NULL && s->next != NULL) { - if (s->next == rose_node) { - s->next = rose_node->next; - kfree(rose_node); - return; - } - - s = s->next; - } -} - -/* - * Caller is holding rose_neigh_list_lock. - */ -static void rose_remove_neigh(struct rose_neigh *rose_neigh) -{ - struct rose_neigh *s; - - timer_delete_sync(&rose_neigh->ftimer); - timer_delete_sync(&rose_neigh->t0timer); - - skb_queue_purge(&rose_neigh->queue); - - if ((s = rose_neigh_list) == rose_neigh) { - rose_neigh_list = rose_neigh->next; - return; - } - - while (s != NULL && s->next != NULL) { - if (s->next == rose_neigh) { - s->next = rose_neigh->next; - return; - } - - s = s->next; - } -} - -/* - * Caller is holding rose_route_list_lock. - */ -static void rose_remove_route(struct rose_route *rose_route) -{ - struct rose_route *s; - - if (rose_route->neigh1 != NULL) - rose_neigh_put(rose_route->neigh1); - - if (rose_route->neigh2 != NULL) - rose_neigh_put(rose_route->neigh2); - - if ((s = rose_route_list) == rose_route) { - rose_route_list = rose_route->next; - kfree(rose_route); - return; - } - - while (s != NULL && s->next != NULL) { - if (s->next == rose_route) { - s->next = rose_route->next; - kfree(rose_route); - return; - } - - s = s->next; - } -} - -/* - * "Delete" a node. Strictly speaking remove a route to a node. The node - * is only deleted if no routes are left to it. - */ -static int rose_del_node(struct rose_route_struct *rose_route, - struct net_device *dev) -{ - struct rose_node *rose_node; - struct rose_neigh *rose_neigh; - int i, err = 0; - - spin_lock_bh(&rose_node_list_lock); - spin_lock_bh(&rose_neigh_list_lock); - - rose_node = rose_node_list; - while (rose_node != NULL) { - if ((rose_node->mask == rose_route->mask) && - (rosecmpm(&rose_route->address, &rose_node->address, - rose_route->mask) == 0)) - break; - rose_node = rose_node->next; - } - - if (rose_node == NULL || rose_node->loopback) { - err = -EINVAL; - goto out; - } - - rose_neigh = rose_neigh_list; - while (rose_neigh != NULL) { - if (ax25cmp(&rose_route->neighbour, - &rose_neigh->callsign) == 0 && - rose_neigh->dev == dev) - break; - rose_neigh = rose_neigh->next; - } - - if (rose_neigh == NULL) { - err = -EINVAL; - goto out; - } - - for (i = 0; i < rose_node->count; i++) { - if (rose_node->neighbour[i] == rose_neigh) { - rose_neigh->count--; - rose_neigh_put(rose_neigh); - - if (rose_neigh->count == 0) { - rose_remove_neigh(rose_neigh); - rose_neigh_put(rose_neigh); - } - - rose_node->count--; - - if (rose_node->count == 0) { - rose_remove_node(rose_node); - } else { - switch (i) { - case 0: - rose_node->neighbour[0] = - rose_node->neighbour[1]; - fallthrough; - case 1: - rose_node->neighbour[1] = - rose_node->neighbour[2]; - break; - case 2: - break; - } - } - goto out; - } - } - err = -EINVAL; - -out: - spin_unlock_bh(&rose_neigh_list_lock); - spin_unlock_bh(&rose_node_list_lock); - - return err; -} - -/* - * Add the loopback neighbour. - */ -void rose_add_loopback_neigh(void) -{ - struct rose_neigh *sn; - - rose_loopback_neigh = kmalloc_obj(struct rose_neigh); - if (!rose_loopback_neigh) - return; - sn = rose_loopback_neigh; - - sn->callsign = null_ax25_address; - sn->digipeat = NULL; - sn->ax25 = NULL; - sn->dev = NULL; - sn->count = 0; - sn->dce_mode = 1; - sn->loopback = 1; - sn->number = rose_neigh_no++; - sn->restarted = 1; - refcount_set(&sn->use, 1); - - skb_queue_head_init(&sn->queue); - - timer_setup(&sn->ftimer, NULL, 0); - timer_setup(&sn->t0timer, NULL, 0); - - spin_lock_bh(&rose_neigh_list_lock); - sn->next = rose_neigh_list; - rose_neigh_list = sn; - spin_unlock_bh(&rose_neigh_list_lock); -} - -/* - * Add a loopback node. - */ -int rose_add_loopback_node(const rose_address *address) -{ - struct rose_node *rose_node; - int err = 0; - - spin_lock_bh(&rose_node_list_lock); - - rose_node = rose_node_list; - while (rose_node != NULL) { - if ((rose_node->mask == 10) && - (rosecmpm(address, &rose_node->address, 10) == 0) && - rose_node->loopback) - break; - rose_node = rose_node->next; - } - - if (rose_node != NULL) - goto out; - - if ((rose_node = kmalloc_obj(*rose_node, GFP_ATOMIC)) == NULL) { - err = -ENOMEM; - goto out; - } - - rose_node->address = *address; - rose_node->mask = 10; - rose_node->count = 1; - rose_node->loopback = 1; - rose_node->neighbour[0] = rose_loopback_neigh; - - /* Insert at the head of list. Address is always mask=10 */ - rose_node->next = rose_node_list; - rose_node_list = rose_node; - - rose_loopback_neigh->count++; - rose_neigh_hold(rose_loopback_neigh); - -out: - spin_unlock_bh(&rose_node_list_lock); - - return err; -} - -/* - * Delete a loopback node. - */ -void rose_del_loopback_node(const rose_address *address) -{ - struct rose_node *rose_node; - - spin_lock_bh(&rose_node_list_lock); - - rose_node = rose_node_list; - while (rose_node != NULL) { - if ((rose_node->mask == 10) && - (rosecmpm(address, &rose_node->address, 10) == 0) && - rose_node->loopback) - break; - rose_node = rose_node->next; - } - - if (rose_node == NULL) - goto out; - - rose_remove_node(rose_node); - - rose_loopback_neigh->count--; - rose_neigh_put(rose_loopback_neigh); - -out: - spin_unlock_bh(&rose_node_list_lock); -} - -/* - * A device has been removed. Remove its routes and neighbours. - */ -void rose_rt_device_down(struct net_device *dev) -{ - struct rose_neigh *s, *rose_neigh; - struct rose_node *t, *rose_node; - int i; - - spin_lock_bh(&rose_node_list_lock); - spin_lock_bh(&rose_neigh_list_lock); - rose_neigh = rose_neigh_list; - while (rose_neigh != NULL) { - s = rose_neigh; - rose_neigh = rose_neigh->next; - - if (s->dev != dev) - continue; - - rose_node = rose_node_list; - - while (rose_node != NULL) { - t = rose_node; - rose_node = rose_node->next; - - for (i = t->count - 1; i >= 0; i--) { - if (t->neighbour[i] != s) - continue; - - t->count--; - - memmove(&t->neighbour[i], &t->neighbour[i + 1], - sizeof(t->neighbour[0]) * - (t->count - i)); - rose_neigh_put(s); - } - - if (t->count <= 0) - rose_remove_node(t); - } - - rose_remove_neigh(s); - rose_neigh_put(s); - } - spin_unlock_bh(&rose_neigh_list_lock); - spin_unlock_bh(&rose_node_list_lock); -} - -#if 0 /* Currently unused */ -/* - * A device has been removed. Remove its links. - */ -void rose_route_device_down(struct net_device *dev) -{ - struct rose_route *s, *rose_route; - - spin_lock_bh(&rose_route_list_lock); - rose_route = rose_route_list; - while (rose_route != NULL) { - s = rose_route; - rose_route = rose_route->next; - - if (s->neigh1->dev == dev || s->neigh2->dev == dev) - rose_remove_route(s); - } - spin_unlock_bh(&rose_route_list_lock); -} -#endif - -/* - * Clear all nodes and neighbours out, except for neighbours with - * active connections going through them. - * Do not clear loopback neighbour and nodes. - */ -static int rose_clear_routes(void) -{ - struct rose_neigh *s, *rose_neigh; - struct rose_node *t, *rose_node; - int i; - - spin_lock_bh(&rose_node_list_lock); - spin_lock_bh(&rose_neigh_list_lock); - - rose_neigh = rose_neigh_list; - rose_node = rose_node_list; - - while (rose_node != NULL) { - t = rose_node; - rose_node = rose_node->next; - - if (!t->loopback) { - for (i = 0; i < t->count; i++) - rose_neigh_put(t->neighbour[i]); - rose_remove_node(t); - } - } - - while (rose_neigh != NULL) { - s = rose_neigh; - rose_neigh = rose_neigh->next; - - if (!s->loopback) { - rose_remove_neigh(s); - rose_neigh_put(s); - } - } - - spin_unlock_bh(&rose_neigh_list_lock); - spin_unlock_bh(&rose_node_list_lock); - - return 0; -} - -/* - * Check that the device given is a valid AX.25 interface that is "up". - * called with RTNL - */ -static struct net_device *rose_ax25_dev_find(char *devname) -{ - struct net_device *dev; - - if ((dev = __dev_get_by_name(&init_net, devname)) == NULL) - return NULL; - - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25) - return dev; - - return NULL; -} - -/* - * Find the first active ROSE device, usually "rose0". - */ -struct net_device *rose_dev_first(void) -{ - struct net_device *dev, *first = NULL; - - rcu_read_lock(); - for_each_netdev_rcu(&init_net, dev) { - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE) - if (first == NULL || strncmp(dev->name, first->name, 3) < 0) - first = dev; - } - if (first) - dev_hold(first); - rcu_read_unlock(); - - return first; -} - -/* - * Find the ROSE device for the given address. - */ -struct net_device *rose_dev_get(rose_address *addr) -{ - struct net_device *dev; - - rcu_read_lock(); - for_each_netdev_rcu(&init_net, dev) { - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && - rosecmp(addr, (const rose_address *)dev->dev_addr) == 0) { - dev_hold(dev); - goto out; - } - } - dev = NULL; -out: - rcu_read_unlock(); - return dev; -} - -static int rose_dev_exists(rose_address *addr) -{ - struct net_device *dev; - - rcu_read_lock(); - for_each_netdev_rcu(&init_net, dev) { - if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && - rosecmp(addr, (const rose_address *)dev->dev_addr) == 0) - goto out; - } - dev = NULL; -out: - rcu_read_unlock(); - return dev != NULL; -} - - - - -struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neigh) -{ - struct rose_route *rose_route; - - for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) - if ((rose_route->neigh1 == neigh && rose_route->lci1 == lci) || - (rose_route->neigh2 == neigh && rose_route->lci2 == lci)) - return rose_route; - - return NULL; -} - -/* - * Find a neighbour or a route given a ROSE address. - */ -struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, - unsigned char *diagnostic, int route_frame) -{ - struct rose_neigh *res = NULL; - struct rose_node *node; - int failed = 0; - int i; - - if (!route_frame) spin_lock_bh(&rose_node_list_lock); - for (node = rose_node_list; node != NULL; node = node->next) { - if (rosecmpm(addr, &node->address, node->mask) == 0) { - for (i = 0; i < node->count; i++) { - if (node->neighbour[i]->restarted) { - res = node->neighbour[i]; - rose_neigh_hold(node->neighbour[i]); - goto out; - } - } - } - } - if (!route_frame) { /* connect request */ - for (node = rose_node_list; node != NULL; node = node->next) { - if (rosecmpm(addr, &node->address, node->mask) == 0) { - for (i = 0; i < node->count; i++) { - if (!rose_ftimer_running(node->neighbour[i])) { - res = node->neighbour[i]; - rose_neigh_hold(node->neighbour[i]); - goto out; - } - failed = 1; - } - } - } - } - - if (failed) { - *cause = ROSE_OUT_OF_ORDER; - *diagnostic = 0; - } else { - *cause = ROSE_NOT_OBTAINABLE; - *diagnostic = 0; - } - -out: - if (!route_frame) spin_unlock_bh(&rose_node_list_lock); - return res; -} - -/* - * Handle the ioctls that control the routing functions. - */ -int rose_rt_ioctl(unsigned int cmd, void __user *arg) -{ - struct rose_route_struct rose_route; - struct net_device *dev; - int err; - - switch (cmd) { - case SIOCADDRT: - if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) - return -EFAULT; - if ((dev = rose_ax25_dev_find(rose_route.device)) == NULL) - return -EINVAL; - if (rose_dev_exists(&rose_route.address)) /* Can't add routes to ourself */ - return -EINVAL; - if (rose_route.mask > 10) /* Mask can't be more than 10 digits */ - return -EINVAL; - if (rose_route.ndigis > AX25_MAX_DIGIS) - return -EINVAL; - err = rose_add_node(&rose_route, dev); - return err; - - case SIOCDELRT: - if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) - return -EFAULT; - if ((dev = rose_ax25_dev_find(rose_route.device)) == NULL) - return -EINVAL; - err = rose_del_node(&rose_route, dev); - return err; - - case SIOCRSCLRRT: - return rose_clear_routes(); - - default: - return -EINVAL; - } - - return 0; -} - -static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) -{ - struct rose_route *rose_route, *s; - - rose_neigh->restarted = 0; - - rose_stop_t0timer(rose_neigh); - rose_start_ftimer(rose_neigh); - - skb_queue_purge(&rose_neigh->queue); - - spin_lock_bh(&rose_route_list_lock); - - rose_route = rose_route_list; - - while (rose_route != NULL) { - if ((rose_route->neigh1 == rose_neigh && rose_route->neigh2 == rose_neigh) || - (rose_route->neigh1 == rose_neigh && rose_route->neigh2 == NULL) || - (rose_route->neigh2 == rose_neigh && rose_route->neigh1 == NULL)) { - s = rose_route->next; - rose_remove_route(rose_route); - rose_route = s; - continue; - } - - if (rose_route->neigh1 == rose_neigh) { - rose_neigh_put(rose_route->neigh1); - rose_route->neigh1 = NULL; - rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0); - } - - if (rose_route->neigh2 == rose_neigh) { - rose_neigh_put(rose_route->neigh2); - rose_route->neigh2 = NULL; - rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0); - } - - rose_route = rose_route->next; - } - spin_unlock_bh(&rose_route_list_lock); -} - -/* - * A level 2 link has timed out, therefore it appears to be a poor link, - * then don't use that neighbour until it is reset. Blow away all through - * routes and connections using this route. - */ -void rose_link_failed(ax25_cb *ax25, int reason) -{ - struct rose_neigh *rose_neigh; - - spin_lock_bh(&rose_neigh_list_lock); - rose_neigh = rose_neigh_list; - while (rose_neigh != NULL) { - if (rose_neigh->ax25 == ax25) - break; - rose_neigh = rose_neigh->next; - } - - if (rose_neigh != NULL) { - rose_neigh->ax25 = NULL; - ax25_cb_put(ax25); - - rose_del_route_by_neigh(rose_neigh); - rose_kill_by_neigh(rose_neigh); - } - spin_unlock_bh(&rose_neigh_list_lock); -} - -/* - * A device has been "downed" remove its link status. Blow away all - * through routes and connections that use this device. - */ -void rose_link_device_down(struct net_device *dev) -{ - struct rose_neigh *rose_neigh; - - for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) { - if (rose_neigh->dev == dev) { - rose_del_route_by_neigh(rose_neigh); - rose_kill_by_neigh(rose_neigh); - } - } -} - -/* - * Route a frame to an appropriate AX.25 connection. - * A NULL ax25_cb indicates an internally generated frame. - */ -int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) -{ - struct rose_neigh *rose_neigh, *new_neigh; - struct rose_route *rose_route; - struct rose_facilities_struct facilities; - rose_address *src_addr, *dest_addr; - struct sock *sk; - unsigned short frametype; - unsigned int lci, new_lci; - unsigned char cause, diagnostic; - struct net_device *dev; - int res = 0; - char buf[11]; - - if (skb->len < ROSE_MIN_LEN) - return res; - - if (!ax25) - return rose_loopback_queue(skb, NULL); - - frametype = skb->data[2]; - lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); - if (frametype == ROSE_CALL_REQUEST && - (skb->len <= ROSE_CALL_REQ_FACILITIES_OFF || - skb->data[ROSE_CALL_REQ_ADDR_LEN_OFF] != - ROSE_CALL_REQ_ADDR_LEN_VAL)) - return res; - src_addr = (rose_address *)(skb->data + ROSE_CALL_REQ_SRC_ADDR_OFF); - dest_addr = (rose_address *)(skb->data + ROSE_CALL_REQ_DEST_ADDR_OFF); - - spin_lock_bh(&rose_neigh_list_lock); - spin_lock_bh(&rose_route_list_lock); - - rose_neigh = rose_neigh_list; - while (rose_neigh != NULL) { - if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && - ax25->ax25_dev->dev == rose_neigh->dev) - break; - rose_neigh = rose_neigh->next; - } - - if (rose_neigh == NULL) { - printk("rose_route : unknown neighbour or device %s\n", - ax2asc(buf, &ax25->dest_addr)); - goto out; - } - - /* - * Obviously the link is working, halt the ftimer. - */ - rose_stop_ftimer(rose_neigh); - - /* - * LCI of zero is always for us, and its always a restart - * frame. - */ - if (lci == 0) { - rose_link_rx_restart(skb, rose_neigh, frametype); - goto out; - } - - /* - * Find an existing socket. - */ - if ((sk = rose_find_socket(lci, rose_neigh)) != NULL) { - if (frametype == ROSE_CALL_REQUEST) { - struct rose_sock *rose = rose_sk(sk); - - /* Remove an existing unused socket */ - rose_clear_queues(sk); - rose->cause = ROSE_NETWORK_CONGESTION; - rose->diagnostic = 0; - rose_neigh_put(rose->neighbour); - rose->neighbour = NULL; - rose->lci = 0; - rose->state = ROSE_STATE_0; - sk->sk_state = TCP_CLOSE; - sk->sk_err = 0; - sk->sk_shutdown |= SEND_SHUTDOWN; - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DEAD); - } - } - else { - skb_reset_transport_header(skb); - res = rose_process_rx_frame(sk, skb); - goto out; - } - } - - /* - * Is is a Call Request and is it for us ? - */ - if (frametype == ROSE_CALL_REQUEST) - if ((dev = rose_dev_get(dest_addr)) != NULL) { - res = rose_rx_call_request(skb, dev, rose_neigh, lci); - dev_put(dev); - goto out; - } - - if (!sysctl_rose_routing_control) { - rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 0); - goto out; - } - - /* - * Route it to the next in line if we have an entry for it. - */ - rose_route = rose_route_list; - while (rose_route != NULL) { - if (rose_route->lci1 == lci && - rose_route->neigh1 == rose_neigh) { - if (frametype == ROSE_CALL_REQUEST) { - /* F6FBB - Remove an existing unused route */ - rose_remove_route(rose_route); - break; - } else if (rose_route->neigh2 != NULL) { - skb->data[0] &= 0xF0; - skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F; - skb->data[1] = (rose_route->lci2 >> 0) & 0xFF; - rose_transmit_link(skb, rose_route->neigh2); - if (frametype == ROSE_CLEAR_CONFIRMATION) - rose_remove_route(rose_route); - res = 1; - goto out; - } else { - if (frametype == ROSE_CLEAR_CONFIRMATION) - rose_remove_route(rose_route); - goto out; - } - } - if (rose_route->lci2 == lci && - rose_route->neigh2 == rose_neigh) { - if (frametype == ROSE_CALL_REQUEST) { - /* F6FBB - Remove an existing unused route */ - rose_remove_route(rose_route); - break; - } else if (rose_route->neigh1 != NULL) { - skb->data[0] &= 0xF0; - skb->data[0] |= (rose_route->lci1 >> 8) & 0x0F; - skb->data[1] = (rose_route->lci1 >> 0) & 0xFF; - rose_transmit_link(skb, rose_route->neigh1); - if (frametype == ROSE_CLEAR_CONFIRMATION) - rose_remove_route(rose_route); - res = 1; - goto out; - } else { - if (frametype == ROSE_CLEAR_CONFIRMATION) - rose_remove_route(rose_route); - goto out; - } - } - rose_route = rose_route->next; - } - - /* - * We know that: - * 1. The frame isn't for us, - * 2. It isn't "owned" by any existing route. - */ - if (frametype != ROSE_CALL_REQUEST) { /* XXX */ - res = 0; - goto out; - } - - memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); - - if (!rose_parse_facilities(skb->data + ROSE_CALL_REQ_FACILITIES_OFF, - skb->len - ROSE_CALL_REQ_FACILITIES_OFF, - &facilities)) { - rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76); - goto out; - } - - /* - * Check for routing loops. - */ - rose_route = rose_route_list; - while (rose_route != NULL) { - if (rose_route->rand == facilities.rand && - rosecmp(src_addr, &rose_route->src_addr) == 0 && - ax25cmp(&facilities.dest_call, &rose_route->src_call) == 0 && - ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) { - rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120); - goto out; - } - rose_route = rose_route->next; - } - - if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic, 1)) == NULL) { - rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic); - goto out; - } - - if ((new_lci = rose_new_lci(new_neigh)) == 0) { - rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71); - goto put_neigh; - } - - if ((rose_route = kmalloc_obj(*rose_route, GFP_ATOMIC)) == NULL) { - rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120); - goto put_neigh; - } - - rose_route->lci1 = lci; - rose_route->src_addr = *src_addr; - rose_route->dest_addr = *dest_addr; - rose_route->src_call = facilities.dest_call; - rose_route->dest_call = facilities.source_call; - rose_route->rand = facilities.rand; - rose_route->neigh1 = rose_neigh; - rose_route->lci2 = new_lci; - rose_route->neigh2 = new_neigh; - - rose_neigh_hold(rose_route->neigh1); - rose_neigh_hold(rose_route->neigh2); - - rose_route->next = rose_route_list; - rose_route_list = rose_route; - - skb->data[0] &= 0xF0; - skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F; - skb->data[1] = (rose_route->lci2 >> 0) & 0xFF; - - rose_transmit_link(skb, rose_route->neigh2); - res = 1; - -put_neigh: - rose_neigh_put(new_neigh); -out: - spin_unlock_bh(&rose_route_list_lock); - spin_unlock_bh(&rose_neigh_list_lock); - - return res; -} - -#ifdef CONFIG_PROC_FS - -static void *rose_node_start(struct seq_file *seq, loff_t *pos) - __acquires(rose_node_list_lock) -{ - struct rose_node *rose_node; - int i = 1; - - spin_lock_bh(&rose_node_list_lock); - if (*pos == 0) - return SEQ_START_TOKEN; - - for (rose_node = rose_node_list; rose_node && i < *pos; - rose_node = rose_node->next, ++i); - - return (i == *pos) ? rose_node : NULL; -} - -static void *rose_node_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - - return (v == SEQ_START_TOKEN) ? rose_node_list - : ((struct rose_node *)v)->next; -} - -static void rose_node_stop(struct seq_file *seq, void *v) - __releases(rose_node_list_lock) -{ - spin_unlock_bh(&rose_node_list_lock); -} - -static int rose_node_show(struct seq_file *seq, void *v) -{ - char rsbuf[11]; - int i; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, "address mask n neigh neigh neigh\n"); - else { - const struct rose_node *rose_node = v; - seq_printf(seq, "%-10s %04d %d", - rose2asc(rsbuf, &rose_node->address), - rose_node->mask, - rose_node->count); - - for (i = 0; i < rose_node->count; i++) - seq_printf(seq, " %05d", rose_node->neighbour[i]->number); - - seq_puts(seq, "\n"); - } - return 0; -} - -const struct seq_operations rose_node_seqops = { - .start = rose_node_start, - .next = rose_node_next, - .stop = rose_node_stop, - .show = rose_node_show, -}; - -static void *rose_neigh_start(struct seq_file *seq, loff_t *pos) - __acquires(rose_neigh_list_lock) -{ - struct rose_neigh *rose_neigh; - int i = 1; - - spin_lock_bh(&rose_neigh_list_lock); - if (*pos == 0) - return SEQ_START_TOKEN; - - for (rose_neigh = rose_neigh_list; rose_neigh && i < *pos; - rose_neigh = rose_neigh->next, ++i); - - return (i == *pos) ? rose_neigh : NULL; -} - -static void *rose_neigh_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - - return (v == SEQ_START_TOKEN) ? rose_neigh_list - : ((struct rose_neigh *)v)->next; -} - -static void rose_neigh_stop(struct seq_file *seq, void *v) - __releases(rose_neigh_list_lock) -{ - spin_unlock_bh(&rose_neigh_list_lock); -} - -static int rose_neigh_show(struct seq_file *seq, void *v) -{ - char buf[11]; - int i; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "addr callsign dev count use mode restart t0 tf digipeaters\n"); - else { - struct rose_neigh *rose_neigh = v; - - /* if (!rose_neigh->loopback) { */ - seq_printf(seq, "%05d %-9s %-4s %3d %3d %3s %3s %3lu %3lu", - rose_neigh->number, - (rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(buf, &rose_neigh->callsign), - rose_neigh->dev ? rose_neigh->dev->name : "???", - rose_neigh->count, - refcount_read(&rose_neigh->use) - rose_neigh->count - 1, - (rose_neigh->dce_mode) ? "DCE" : "DTE", - (rose_neigh->restarted) ? "yes" : "no", - ax25_display_timer(&rose_neigh->t0timer) / HZ, - ax25_display_timer(&rose_neigh->ftimer) / HZ); - - if (rose_neigh->digipeat != NULL) { - for (i = 0; i < rose_neigh->digipeat->ndigi; i++) - seq_printf(seq, " %s", ax2asc(buf, &rose_neigh->digipeat->calls[i])); - } - - seq_puts(seq, "\n"); - } - return 0; -} - - -const struct seq_operations rose_neigh_seqops = { - .start = rose_neigh_start, - .next = rose_neigh_next, - .stop = rose_neigh_stop, - .show = rose_neigh_show, -}; - -static void *rose_route_start(struct seq_file *seq, loff_t *pos) - __acquires(rose_route_list_lock) -{ - struct rose_route *rose_route; - int i = 1; - - spin_lock_bh(&rose_route_list_lock); - if (*pos == 0) - return SEQ_START_TOKEN; - - for (rose_route = rose_route_list; rose_route && i < *pos; - rose_route = rose_route->next, ++i); - - return (i == *pos) ? rose_route : NULL; -} - -static void *rose_route_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - - return (v == SEQ_START_TOKEN) ? rose_route_list - : ((struct rose_route *)v)->next; -} - -static void rose_route_stop(struct seq_file *seq, void *v) - __releases(rose_route_list_lock) -{ - spin_unlock_bh(&rose_route_list_lock); -} - -static int rose_route_show(struct seq_file *seq, void *v) -{ - char buf[11], rsbuf[11]; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "lci address callsign neigh <-> lci address callsign neigh\n"); - else { - struct rose_route *rose_route = v; - - if (rose_route->neigh1) - seq_printf(seq, - "%3.3X %-10s %-9s %05d ", - rose_route->lci1, - rose2asc(rsbuf, &rose_route->src_addr), - ax2asc(buf, &rose_route->src_call), - rose_route->neigh1->number); - else - seq_puts(seq, - "000 * * 00000 "); - - if (rose_route->neigh2) - seq_printf(seq, - "%3.3X %-10s %-9s %05d\n", - rose_route->lci2, - rose2asc(rsbuf, &rose_route->dest_addr), - ax2asc(buf, &rose_route->dest_call), - rose_route->neigh2->number); - else - seq_puts(seq, - "000 * * 00000\n"); - } - return 0; -} - -struct seq_operations rose_route_seqops = { - .start = rose_route_start, - .next = rose_route_next, - .stop = rose_route_stop, - .show = rose_route_show, -}; -#endif /* CONFIG_PROC_FS */ - -/* - * Release all memory associated with ROSE routing structures. - */ -void __exit rose_rt_free(void) -{ - struct rose_neigh *s, *rose_neigh = rose_neigh_list; - struct rose_node *t, *rose_node = rose_node_list; - struct rose_route *u, *rose_route = rose_route_list; - int i; - - while (rose_neigh != NULL) { - s = rose_neigh; - rose_neigh = rose_neigh->next; - - rose_remove_neigh(s); - rose_neigh_put(s); - } - - while (rose_node != NULL) { - t = rose_node; - rose_node = rose_node->next; - - for (i = 0; i < t->count; i++) - rose_neigh_put(t->neighbour[i]); - rose_remove_node(t); - } - - while (rose_route != NULL) { - u = rose_route; - rose_route = rose_route->next; - - rose_remove_route(u); - } -} diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c deleted file mode 100644 index 4dbc437a9e22..000000000000 --- a/net/rose/rose_subr.c +++ /dev/null @@ -1,556 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose); - -/* - * This routine purges all of the queues of frames. - */ -void rose_clear_queues(struct sock *sk) -{ - skb_queue_purge(&sk->sk_write_queue); - skb_queue_purge(&rose_sk(sk)->ack_queue); -} - -/* - * This routine purges the input queue of those frames that have been - * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the - * SDL diagram. - */ -void rose_frames_acked(struct sock *sk, unsigned short nr) -{ - struct sk_buff *skb; - struct rose_sock *rose = rose_sk(sk); - - /* - * Remove all the ack-ed frames from the ack queue. - */ - if (rose->va != nr) { - while (skb_peek(&rose->ack_queue) != NULL && rose->va != nr) { - skb = skb_dequeue(&rose->ack_queue); - kfree_skb(skb); - rose->va = (rose->va + 1) % ROSE_MODULUS; - } - } -} - -void rose_requeue_frames(struct sock *sk) -{ - struct sk_buff *skb, *skb_prev = NULL; - - /* - * Requeue all the un-ack-ed frames on the output queue to be picked - * up by rose_kick. This arrangement handles the possibility of an - * empty output queue. - */ - while ((skb = skb_dequeue(&rose_sk(sk)->ack_queue)) != NULL) { - if (skb_prev == NULL) - skb_queue_head(&sk->sk_write_queue, skb); - else - skb_append(skb_prev, skb, &sk->sk_write_queue); - skb_prev = skb; - } -} - -/* - * Validate that the value of nr is between va and vs. Return true or - * false for testing. - */ -int rose_validate_nr(struct sock *sk, unsigned short nr) -{ - struct rose_sock *rose = rose_sk(sk); - unsigned short vc = rose->va; - - while (vc != rose->vs) { - if (nr == vc) return 1; - vc = (vc + 1) % ROSE_MODULUS; - } - - return nr == rose->vs; -} - -/* - * This routine is called when the packet layer internally generates a - * control frame. - */ -void rose_write_internal(struct sock *sk, int frametype) -{ - struct rose_sock *rose = rose_sk(sk); - struct sk_buff *skb; - unsigned char *dptr; - unsigned char lci1, lci2; - int maxfaclen = 0; - int len, faclen; - int reserve; - - reserve = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1; - len = ROSE_MIN_LEN; - - switch (frametype) { - case ROSE_CALL_REQUEST: - len += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN; - maxfaclen = 256; - break; - case ROSE_CALL_ACCEPTED: - case ROSE_CLEAR_REQUEST: - case ROSE_RESET_REQUEST: - len += 2; - break; - } - - skb = alloc_skb(reserve + len + maxfaclen, GFP_ATOMIC); - if (!skb) - return; - - /* - * Space for AX.25 header and PID. - */ - skb_reserve(skb, reserve); - - dptr = skb_put(skb, len); - - lci1 = (rose->lci >> 8) & 0x0F; - lci2 = (rose->lci >> 0) & 0xFF; - - switch (frametype) { - case ROSE_CALL_REQUEST: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - *dptr++ = ROSE_CALL_REQ_ADDR_LEN_VAL; - memcpy(dptr, &rose->dest_addr, ROSE_ADDR_LEN); - dptr += ROSE_ADDR_LEN; - memcpy(dptr, &rose->source_addr, ROSE_ADDR_LEN); - dptr += ROSE_ADDR_LEN; - faclen = rose_create_facilities(dptr, rose); - skb_put(skb, faclen); - dptr += faclen; - break; - - case ROSE_CALL_ACCEPTED: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - *dptr++ = 0x00; /* Address length */ - *dptr++ = 0; /* Facilities length */ - break; - - case ROSE_CLEAR_REQUEST: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - *dptr++ = rose->cause; - *dptr++ = rose->diagnostic; - break; - - case ROSE_RESET_REQUEST: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - *dptr++ = ROSE_DTE_ORIGINATED; - *dptr++ = 0; - break; - - case ROSE_RR: - case ROSE_RNR: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr = frametype; - *dptr++ |= (rose->vr << 5) & 0xE0; - break; - - case ROSE_CLEAR_CONFIRMATION: - case ROSE_RESET_CONFIRMATION: - *dptr++ = ROSE_GFI | lci1; - *dptr++ = lci2; - *dptr++ = frametype; - break; - - default: - printk(KERN_ERR "ROSE: rose_write_internal - invalid frametype %02X\n", frametype); - kfree_skb(skb); - return; - } - - rose_transmit_link(skb, rose->neighbour); -} - -int rose_decode(struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m) -{ - unsigned char *frame; - - frame = skb->data; - - *ns = *nr = *q = *d = *m = 0; - - switch (frame[2]) { - case ROSE_CALL_REQUEST: - case ROSE_CALL_ACCEPTED: - case ROSE_CLEAR_REQUEST: - case ROSE_CLEAR_CONFIRMATION: - case ROSE_RESET_REQUEST: - case ROSE_RESET_CONFIRMATION: - return frame[2]; - default: - break; - } - - if ((frame[2] & 0x1F) == ROSE_RR || - (frame[2] & 0x1F) == ROSE_RNR) { - *nr = (frame[2] >> 5) & 0x07; - return frame[2] & 0x1F; - } - - if ((frame[2] & 0x01) == ROSE_DATA) { - *q = (frame[0] & ROSE_Q_BIT) == ROSE_Q_BIT; - *d = (frame[0] & ROSE_D_BIT) == ROSE_D_BIT; - *m = (frame[2] & ROSE_M_BIT) == ROSE_M_BIT; - *nr = (frame[2] >> 5) & 0x07; - *ns = (frame[2] >> 1) & 0x07; - return ROSE_DATA; - } - - return ROSE_ILLEGAL; -} - -static int rose_parse_national(unsigned char *p, struct rose_facilities_struct *facilities, int len) -{ - unsigned char *pt; - unsigned char l, lg, n = 0; - int fac_national_digis_received = 0; - - do { - switch (*p & 0xC0) { - case 0x00: - if (len < 2) - return -1; - p += 2; - n += 2; - len -= 2; - break; - - case 0x40: - if (len < 3) - return -1; - if (*p == FAC_NATIONAL_RAND) - facilities->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF); - p += 3; - n += 3; - len -= 3; - break; - - case 0x80: - if (len < 4) - return -1; - p += 4; - n += 4; - len -= 4; - break; - - case 0xC0: - if (len < 2) - return -1; - l = p[1]; - if (len < 2 + l) - return -1; - if (*p == FAC_NATIONAL_DEST_DIGI) { - if (!fac_national_digis_received) { - if (l < AX25_ADDR_LEN) - return -1; - memcpy(&facilities->source_digis[0], p + 2, AX25_ADDR_LEN); - facilities->source_ndigis = 1; - } - } - else if (*p == FAC_NATIONAL_SRC_DIGI) { - if (!fac_national_digis_received) { - if (l < AX25_ADDR_LEN) - return -1; - memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN); - facilities->dest_ndigis = 1; - } - } - else if (*p == FAC_NATIONAL_FAIL_CALL) { - if (l < AX25_ADDR_LEN) - return -1; - memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN); - } - else if (*p == FAC_NATIONAL_FAIL_ADD) { - if (l < 1 + ROSE_ADDR_LEN) - return -1; - memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN); - } - else if (*p == FAC_NATIONAL_DIGIS) { - if (l % AX25_ADDR_LEN) - return -1; - fac_national_digis_received = 1; - facilities->source_ndigis = 0; - facilities->dest_ndigis = 0; - for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) { - if (pt[6] & AX25_HBIT) { - if (facilities->dest_ndigis >= ROSE_MAX_DIGIS) - return -1; - memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN); - } else { - if (facilities->source_ndigis >= ROSE_MAX_DIGIS) - return -1; - memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN); - } - } - } - p += l + 2; - n += l + 2; - len -= l + 2; - break; - } - } while (*p != 0x00 && len > 0); - - return n; -} - -static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *facilities, int len) -{ - unsigned char l, n = 0; - char callsign[11]; - - do { - switch (*p & 0xC0) { - case 0x00: - if (len < 2) - return -1; - p += 2; - n += 2; - len -= 2; - break; - - case 0x40: - if (len < 3) - return -1; - p += 3; - n += 3; - len -= 3; - break; - - case 0x80: - if (len < 4) - return -1; - p += 4; - n += 4; - len -= 4; - break; - - case 0xC0: - if (len < 2) - return -1; - l = p[1]; - - /* Prevent overflows*/ - if (l < 10 || l > 20) - return -1; - - if (*p == FAC_CCITT_DEST_NSAP) { - memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN); - memcpy(callsign, p + 12, l - 10); - callsign[l - 10] = '\0'; - asc2ax(&facilities->source_call, callsign); - } - if (*p == FAC_CCITT_SRC_NSAP) { - memcpy(&facilities->dest_addr, p + 7, ROSE_ADDR_LEN); - memcpy(callsign, p + 12, l - 10); - callsign[l - 10] = '\0'; - asc2ax(&facilities->dest_call, callsign); - } - p += l + 2; - n += l + 2; - len -= l + 2; - break; - } - } while (*p != 0x00 && len > 0); - - return n; -} - -int rose_parse_facilities(unsigned char *p, unsigned packet_len, - struct rose_facilities_struct *facilities) -{ - int facilities_len, len; - - facilities_len = *p++; - - if (facilities_len == 0 || (unsigned int)facilities_len > packet_len) - return 0; - - while (facilities_len >= 3 && *p == 0x00) { - facilities_len--; - p++; - - switch (*p) { - case FAC_NATIONAL: /* National */ - len = rose_parse_national(p + 1, facilities, facilities_len - 1); - break; - - case FAC_CCITT: /* CCITT */ - len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1); - break; - - default: - printk(KERN_DEBUG "ROSE: rose_parse_facilities - unknown facilities family %02X\n", *p); - len = 1; - break; - } - - if (len < 0) - return 0; - if (WARN_ON(len >= facilities_len)) - return 0; - facilities_len -= len + 1; - p += len + 1; - } - - return facilities_len == 0; -} - -static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose) -{ - unsigned char *p = buffer + 1; - char *callsign; - char buf[11]; - int len, nb; - - /* National Facilities */ - if (rose->rand != 0 || rose->source_ndigis == 1 || rose->dest_ndigis == 1) { - *p++ = 0x00; - *p++ = FAC_NATIONAL; - - if (rose->rand != 0) { - *p++ = FAC_NATIONAL_RAND; - *p++ = (rose->rand >> 8) & 0xFF; - *p++ = (rose->rand >> 0) & 0xFF; - } - - /* Sent before older facilities */ - if ((rose->source_ndigis > 0) || (rose->dest_ndigis > 0)) { - int maxdigi = 0; - *p++ = FAC_NATIONAL_DIGIS; - *p++ = AX25_ADDR_LEN * (rose->source_ndigis + rose->dest_ndigis); - for (nb = 0 ; nb < rose->source_ndigis ; nb++) { - if (++maxdigi >= ROSE_MAX_DIGIS) - break; - memcpy(p, &rose->source_digis[nb], AX25_ADDR_LEN); - p[6] |= AX25_HBIT; - p += AX25_ADDR_LEN; - } - for (nb = 0 ; nb < rose->dest_ndigis ; nb++) { - if (++maxdigi >= ROSE_MAX_DIGIS) - break; - memcpy(p, &rose->dest_digis[nb], AX25_ADDR_LEN); - p[6] &= ~AX25_HBIT; - p += AX25_ADDR_LEN; - } - } - - /* For compatibility */ - if (rose->source_ndigis > 0) { - *p++ = FAC_NATIONAL_SRC_DIGI; - *p++ = AX25_ADDR_LEN; - memcpy(p, &rose->source_digis[0], AX25_ADDR_LEN); - p += AX25_ADDR_LEN; - } - - /* For compatibility */ - if (rose->dest_ndigis > 0) { - *p++ = FAC_NATIONAL_DEST_DIGI; - *p++ = AX25_ADDR_LEN; - memcpy(p, &rose->dest_digis[0], AX25_ADDR_LEN); - p += AX25_ADDR_LEN; - } - } - - *p++ = 0x00; - *p++ = FAC_CCITT; - - *p++ = FAC_CCITT_DEST_NSAP; - - callsign = ax2asc(buf, &rose->dest_call); - - *p++ = strlen(callsign) + 10; - *p++ = (strlen(callsign) + 9) * 2; /* ??? */ - - *p++ = 0x47; *p++ = 0x00; *p++ = 0x11; - *p++ = ROSE_ADDR_LEN * 2; - memcpy(p, &rose->dest_addr, ROSE_ADDR_LEN); - p += ROSE_ADDR_LEN; - - memcpy(p, callsign, strlen(callsign)); - p += strlen(callsign); - - *p++ = FAC_CCITT_SRC_NSAP; - - callsign = ax2asc(buf, &rose->source_call); - - *p++ = strlen(callsign) + 10; - *p++ = (strlen(callsign) + 9) * 2; /* ??? */ - - *p++ = 0x47; *p++ = 0x00; *p++ = 0x11; - *p++ = ROSE_ADDR_LEN * 2; - memcpy(p, &rose->source_addr, ROSE_ADDR_LEN); - p += ROSE_ADDR_LEN; - - memcpy(p, callsign, strlen(callsign)); - p += strlen(callsign); - - len = p - buffer; - buffer[0] = len - 1; - - return len; -} - -void rose_disconnect(struct sock *sk, int reason, int cause, int diagnostic) -{ - struct rose_sock *rose = rose_sk(sk); - - rose_stop_timer(sk); - rose_stop_idletimer(sk); - - rose_clear_queues(sk); - - rose->lci = 0; - rose->state = ROSE_STATE_0; - - if (cause != -1) - rose->cause = cause; - - if (diagnostic != -1) - rose->diagnostic = diagnostic; - - sk->sk_state = TCP_CLOSE; - sk->sk_err = reason; - sk->sk_shutdown |= SEND_SHUTDOWN; - - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DEAD); - } -} diff --git a/net/rose/rose_timer.c b/net/rose/rose_timer.c deleted file mode 100644 index bb60a1654d61..000000000000 --- a/net/rose/rose_timer.c +++ /dev/null @@ -1,227 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) - * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void rose_heartbeat_expiry(struct timer_list *t); -static void rose_timer_expiry(struct timer_list *); -static void rose_idletimer_expiry(struct timer_list *); - -void rose_start_heartbeat(struct sock *sk) -{ - sk_stop_timer(sk, &sk->sk_timer); - - sk->sk_timer.function = rose_heartbeat_expiry; - sk->sk_timer.expires = jiffies + 5 * HZ; - - sk_reset_timer(sk, &sk->sk_timer, sk->sk_timer.expires); -} - -void rose_start_t1timer(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - - sk_stop_timer(sk, &rose->timer); - - rose->timer.function = rose_timer_expiry; - rose->timer.expires = jiffies + rose->t1; - - sk_reset_timer(sk, &rose->timer, rose->timer.expires); -} - -void rose_start_t2timer(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - - sk_stop_timer(sk, &rose->timer); - - rose->timer.function = rose_timer_expiry; - rose->timer.expires = jiffies + rose->t2; - - sk_reset_timer(sk, &rose->timer, rose->timer.expires); -} - -void rose_start_t3timer(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - - sk_stop_timer(sk, &rose->timer); - - rose->timer.function = rose_timer_expiry; - rose->timer.expires = jiffies + rose->t3; - - sk_reset_timer(sk, &rose->timer, rose->timer.expires); -} - -void rose_start_hbtimer(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - - sk_stop_timer(sk, &rose->timer); - - rose->timer.function = rose_timer_expiry; - rose->timer.expires = jiffies + rose->hb; - - sk_reset_timer(sk, &rose->timer, rose->timer.expires); -} - -void rose_start_idletimer(struct sock *sk) -{ - struct rose_sock *rose = rose_sk(sk); - - sk_stop_timer(sk, &rose->idletimer); - - if (rose->idle > 0) { - rose->idletimer.function = rose_idletimer_expiry; - rose->idletimer.expires = jiffies + rose->idle; - - sk_reset_timer(sk, &rose->idletimer, rose->idletimer.expires); - } -} - -void rose_stop_heartbeat(struct sock *sk) -{ - sk_stop_timer(sk, &sk->sk_timer); -} - -void rose_stop_timer(struct sock *sk) -{ - sk_stop_timer(sk, &rose_sk(sk)->timer); -} - -void rose_stop_idletimer(struct sock *sk) -{ - sk_stop_timer(sk, &rose_sk(sk)->idletimer); -} - -static void rose_heartbeat_expiry(struct timer_list *t) -{ - struct sock *sk = timer_container_of(sk, t, sk_timer); - struct rose_sock *rose = rose_sk(sk); - - bh_lock_sock(sk); - if (sock_owned_by_user(sk)) { - sk_reset_timer(sk, &sk->sk_timer, jiffies + HZ/20); - goto out; - } - switch (rose->state) { - case ROSE_STATE_0: - /* Magic here: If we listen() and a new link dies before it - is accepted() it isn't 'dead' so doesn't get removed. */ - if (sock_flag(sk, SOCK_DESTROY) || - (sk->sk_state == TCP_LISTEN && sock_flag(sk, SOCK_DEAD))) { - bh_unlock_sock(sk); - rose_destroy_socket(sk); - sock_put(sk); - return; - } - break; - - case ROSE_STATE_3: - /* - * Check for the state of the receive buffer. - */ - if (atomic_read(&sk->sk_rmem_alloc) < (sk->sk_rcvbuf / 2) && - (rose->condition & ROSE_COND_OWN_RX_BUSY)) { - rose->condition &= ~ROSE_COND_OWN_RX_BUSY; - rose->condition &= ~ROSE_COND_ACK_PENDING; - rose->vl = rose->vr; - rose_write_internal(sk, ROSE_RR); - rose_stop_timer(sk); /* HB */ - break; - } - break; - } - - rose_start_heartbeat(sk); -out: - bh_unlock_sock(sk); - sock_put(sk); -} - -static void rose_timer_expiry(struct timer_list *t) -{ - struct rose_sock *rose = timer_container_of(rose, t, timer); - struct sock *sk = &rose->sock; - - bh_lock_sock(sk); - if (sock_owned_by_user(sk)) { - sk_reset_timer(sk, &rose->timer, jiffies + HZ/20); - goto out; - } - switch (rose->state) { - case ROSE_STATE_1: /* T1 */ - case ROSE_STATE_4: /* T2 */ - rose_write_internal(sk, ROSE_CLEAR_REQUEST); - rose->state = ROSE_STATE_2; - rose_start_t3timer(sk); - break; - - case ROSE_STATE_2: /* T3 */ - rose_neigh_put(rose->neighbour); - rose_disconnect(sk, ETIMEDOUT, -1, -1); - break; - - case ROSE_STATE_3: /* HB */ - if (rose->condition & ROSE_COND_ACK_PENDING) { - rose->condition &= ~ROSE_COND_ACK_PENDING; - rose_enquiry_response(sk); - } - break; - } -out: - bh_unlock_sock(sk); - sock_put(sk); -} - -static void rose_idletimer_expiry(struct timer_list *t) -{ - struct rose_sock *rose = timer_container_of(rose, t, idletimer); - struct sock *sk = &rose->sock; - - bh_lock_sock(sk); - if (sock_owned_by_user(sk)) { - sk_reset_timer(sk, &rose->idletimer, jiffies + HZ/20); - goto out; - } - rose_clear_queues(sk); - - rose_write_internal(sk, ROSE_CLEAR_REQUEST); - rose_sk(sk)->state = ROSE_STATE_2; - - rose_start_t3timer(sk); - - sk->sk_state = TCP_CLOSE; - sk->sk_err = 0; - sk->sk_shutdown |= SEND_SHUTDOWN; - - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - sock_set_flag(sk, SOCK_DEAD); - } -out: - bh_unlock_sock(sk); - sock_put(sk); -} diff --git a/net/rose/sysctl_net_rose.c b/net/rose/sysctl_net_rose.c deleted file mode 100644 index d801315b7083..000000000000 --- a/net/rose/sysctl_net_rose.c +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com) - */ -#include -#include -#include -#include -#include - -static int min_timer[] = {1 * HZ}; -static int max_timer[] = {300 * HZ}; -static int min_idle[] = {0 * HZ}; -static int max_idle[] = {65535 * HZ}; -static int min_route[1], max_route[] = {1}; -static int min_ftimer[] = {60 * HZ}; -static int max_ftimer[] = {600 * HZ}; -static int min_maxvcs[] = {1}, max_maxvcs[] = {254}; -static int min_window[] = {1}, max_window[] = {7}; - -static struct ctl_table_header *rose_table_header; - -static struct ctl_table rose_table[] = { - { - .procname = "restart_request_timeout", - .data = &sysctl_rose_restart_request_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_timer, - .extra2 = &max_timer - }, - { - .procname = "call_request_timeout", - .data = &sysctl_rose_call_request_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_timer, - .extra2 = &max_timer - }, - { - .procname = "reset_request_timeout", - .data = &sysctl_rose_reset_request_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_timer, - .extra2 = &max_timer - }, - { - .procname = "clear_request_timeout", - .data = &sysctl_rose_clear_request_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_timer, - .extra2 = &max_timer - }, - { - .procname = "no_activity_timeout", - .data = &sysctl_rose_no_activity_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_idle, - .extra2 = &max_idle - }, - { - .procname = "acknowledge_hold_back_timeout", - .data = &sysctl_rose_ack_hold_back_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_timer, - .extra2 = &max_timer - }, - { - .procname = "routing_control", - .data = &sysctl_rose_routing_control, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_route, - .extra2 = &max_route - }, - { - .procname = "link_fail_timeout", - .data = &sysctl_rose_link_fail_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_ftimer, - .extra2 = &max_ftimer - }, - { - .procname = "maximum_virtual_circuits", - .data = &sysctl_rose_maximum_vcs, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_maxvcs, - .extra2 = &max_maxvcs - }, - { - .procname = "window_size", - .data = &sysctl_rose_window_size, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_window, - .extra2 = &max_window - }, -}; - -void __init rose_register_sysctl(void) -{ - rose_table_header = register_net_sysctl(&init_net, "net/rose", rose_table); -} - -void rose_unregister_sysctl(void) -{ - unregister_net_sysctl_table(rose_table_header); -} diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 96ecb83c9071..27c2aa2dd023 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int); void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *); void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace); void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace); -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace); void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace); void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace); void rxrpc_purge_queue(struct sk_buff_head *); diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index fec59d9338b9..2b19b252225e 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -332,7 +332,27 @@ bool rxrpc_input_call_event(struct rxrpc_call *call) saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK; - rxrpc_input_call_packet(call, skb); + if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && + sp->hdr.securityIndex != 0 && + (skb_cloned(skb) || + skb_has_frag_list(skb) || + skb_has_shared_frag(skb))) { + /* Unshare the packet so that it can be + * modified by in-place decryption. + */ + struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC); + + if (nskb) { + rxrpc_new_skb(nskb, rxrpc_skb_new_unshared); + rxrpc_input_call_packet(call, nskb); + rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx); + } else { + /* OOM - Drop the packet. */ + rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem); + } + } else { + rxrpc_input_call_packet(call, skb); + } rxrpc_free_skb(skb, rxrpc_skb_put_call_rx); did_receive = true; } diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index 9a41ec708aeb..442414d90ba1 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -240,6 +240,34 @@ static void rxrpc_call_is_secure(struct rxrpc_call *call) rxrpc_notify_socket(call); } +static int rxrpc_verify_response(struct rxrpc_connection *conn, + struct sk_buff *skb) +{ + int ret; + + if (skb_cloned(skb) || skb_has_frag_list(skb) || + skb_has_shared_frag(skb)) { + /* Copy the packet if shared so that we can do in-place + * decryption. + */ + struct sk_buff *nskb = skb_copy(skb, GFP_NOFS); + + if (nskb) { + rxrpc_new_skb(nskb, rxrpc_skb_new_unshared); + ret = conn->security->verify_response(conn, nskb); + rxrpc_free_skb(nskb, rxrpc_skb_put_response_copy); + } else { + /* OOM - Drop the packet. */ + rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem); + ret = -ENOMEM; + } + } else { + ret = conn->security->verify_response(conn, skb); + } + + return ret; +} + /* * connection-level Rx packet processor */ @@ -270,7 +298,7 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, } spin_unlock_irq(&conn->state_lock); - ret = conn->security->verify_response(conn, skb); + ret = rxrpc_verify_response(conn, skb); if (ret < 0) return ret; @@ -362,7 +390,6 @@ again: static void rxrpc_do_process_connection(struct rxrpc_connection *conn) { struct sk_buff *skb; - int ret; if (test_and_clear_bit(RXRPC_CONN_EV_CHALLENGE, &conn->events)) rxrpc_secure_connection(conn); @@ -371,17 +398,8 @@ static void rxrpc_do_process_connection(struct rxrpc_connection *conn) * connection that each one has when we've finished with it */ while ((skb = skb_dequeue(&conn->rx_queue))) { rxrpc_see_skb(skb, rxrpc_skb_see_conn_work); - ret = rxrpc_process_event(conn, skb); - switch (ret) { - case -ENOMEM: - case -EAGAIN: - skb_queue_head(&conn->rx_queue, skb); - rxrpc_queue_conn(conn, rxrpc_conn_queue_retry_work); - break; - default: - rxrpc_free_skb(skb, rxrpc_skb_put_conn_work); - break; - } + rxrpc_process_event(conn, skb); + rxrpc_free_skb(skb, rxrpc_skb_put_conn_work); } } diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c index 697956931925..dc5184a2fa9d 100644 --- a/net/rxrpc/io_thread.c +++ b/net/rxrpc/io_thread.c @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb) /* * Process packets received on the local endpoint */ -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb) { struct rxrpc_connection *conn; struct sockaddr_rxrpc peer_srx; struct rxrpc_skb_priv *sp; struct rxrpc_peer *peer = NULL; - struct sk_buff *skb = *_skb; bool ret = false; skb_pull(skb, sizeof(struct udphdr)); @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb) return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call); if (sp->hdr.seq == 0) return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq); - - /* Unshare the packet so that it can be modified for in-place - * decryption. - */ - if (sp->hdr.securityIndex != 0) { - skb = skb_unshare(skb, GFP_ATOMIC); - if (!skb) { - rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem); - *_skb = NULL; - return just_discard; - } - - if (skb != *_skb) { - rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare); - *_skb = skb; - rxrpc_new_skb(skb, rxrpc_skb_new_unshared); - sp = rxrpc_skb(skb); - } - } break; case RXRPC_PACKET_TYPE_CHALLENGE: @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data) switch (skb->mark) { case RXRPC_SKB_MARK_PACKET: skb->priority = 0; - if (!rxrpc_input_packet(local, &skb)) + if (!rxrpc_input_packet(local, skb)) rxrpc_reject_packet(local, skb); trace_rxrpc_rx_done(skb->mark, skb->priority); rxrpc_free_skb(skb, rxrpc_skb_put_input); diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index 6301d79ee35a..3ec3d89fdf14 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -502,6 +502,10 @@ static int rxrpc_preparse(struct key_preparsed_payload *prep) if (v1->security_index != RXRPC_SECURITY_RXKAD) goto error; + ret = -EKEYREJECTED; + if (v1->ticket_length > AFSTOKEN_RK_TIX_MAX) + goto error; + plen = sizeof(*token->kad) + v1->ticket_length; prep->quotalen += plen + sizeof(*token); diff --git a/net/rxrpc/rxgk_app.c b/net/rxrpc/rxgk_app.c index 30275cb5ba3e..0ef2a29eb695 100644 --- a/net/rxrpc/rxgk_app.c +++ b/net/rxrpc/rxgk_app.c @@ -214,7 +214,7 @@ int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb, ticket_len = ntohl(container.token_len); ticket_offset = token_offset + sizeof(container); - if (xdr_round_up(ticket_len) > token_len - sizeof(container)) + if (ticket_len > xdr_round_down(token_len - sizeof(container))) goto short_packet; _debug("KVNO %u", kvno); @@ -245,6 +245,7 @@ int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb, if (ret != -ENOMEM) return rxrpc_abort_conn(conn, skb, ec, ret, rxgk_abort_resp_tok_dec); + return ret; } ret = conn->security->default_decode_ticket(conn, skb, ticket_offset, diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h index 80164d89e19c..1e257d7ab8ec 100644 --- a/net/rxrpc/rxgk_common.h +++ b/net/rxrpc/rxgk_common.h @@ -34,6 +34,7 @@ struct rxgk_context { }; #define xdr_round_up(x) (round_up((x), sizeof(__be32))) +#define xdr_round_down(x) (round_down((x), sizeof(__be32))) #define xdr_object_len(x) (4 + xdr_round_up(x)) /* diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index eb7f2769d2b1..cba7935977f0 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -510,6 +510,9 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, rxkad_abort_2_short_header); + /* Don't let the crypto algo see a misaligned length. */ + sp->len = round_down(sp->len, 8); + /* Decrypt the skbuff in-place. TODO: We really want to decrypt * directly into the target buffer. */ @@ -543,8 +546,10 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, if (sg != _sg) kfree(sg); if (ret < 0) { - WARN_ON_ONCE(ret != -ENOMEM); - return ret; + if (ret == -ENOMEM) + return ret; + return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, + rxkad_abort_2_crypto_unaligned); } /* Extract the decrypted packet length */ @@ -1136,7 +1141,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, struct rxrpc_crypt session_key; struct key *server_key; time64_t expiry; - void *ticket; + void *ticket = NULL; u32 version, kvno, ticket_len, level; __be32 csum; int ret, i; @@ -1162,13 +1167,13 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, ret = -ENOMEM; response = kzalloc_obj(struct rxkad_response, GFP_NOFS); if (!response) - goto temporary_error; + goto error; if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), response, sizeof(*response)) < 0) { - rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO, - rxkad_abort_resp_short); - goto protocol_error; + ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO, + rxkad_abort_resp_short); + goto error; } version = ntohl(response->version); @@ -1178,62 +1183,62 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, trace_rxrpc_rx_response(conn, sp->hdr.serial, version, kvno, ticket_len); if (version != RXKAD_VERSION) { - rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO, - rxkad_abort_resp_version); - goto protocol_error; + ret = rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO, + rxkad_abort_resp_version); + goto error; } if (ticket_len < 4 || ticket_len > MAXKRB5TICKETLEN) { - rxrpc_abort_conn(conn, skb, RXKADTICKETLEN, -EPROTO, - rxkad_abort_resp_tkt_len); - goto protocol_error; + ret = rxrpc_abort_conn(conn, skb, RXKADTICKETLEN, -EPROTO, + rxkad_abort_resp_tkt_len); + goto error; } if (kvno >= RXKAD_TKT_TYPE_KERBEROS_V5) { - rxrpc_abort_conn(conn, skb, RXKADUNKNOWNKEY, -EPROTO, - rxkad_abort_resp_unknown_tkt); - goto protocol_error; + ret = rxrpc_abort_conn(conn, skb, RXKADUNKNOWNKEY, -EPROTO, + rxkad_abort_resp_unknown_tkt); + goto error; } /* extract the kerberos ticket and decrypt and decode it */ ret = -ENOMEM; ticket = kmalloc(ticket_len, GFP_NOFS); if (!ticket) - goto temporary_error_free_resp; + goto error; if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response), ticket, ticket_len) < 0) { - rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO, - rxkad_abort_resp_short_tkt); - goto protocol_error; + ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO, + rxkad_abort_resp_short_tkt); + goto error; } ret = rxkad_decrypt_ticket(conn, server_key, skb, ticket, ticket_len, &session_key, &expiry); if (ret < 0) - goto temporary_error_free_ticket; + goto error; /* use the session key from inside the ticket to decrypt the * response */ ret = rxkad_decrypt_response(conn, response, &session_key); if (ret < 0) - goto temporary_error_free_ticket; + goto error; if (ntohl(response->encrypted.epoch) != conn->proto.epoch || ntohl(response->encrypted.cid) != conn->proto.cid || ntohl(response->encrypted.securityIndex) != conn->security_ix) { - rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, - rxkad_abort_resp_bad_param); - goto protocol_error_free; + ret = rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, + rxkad_abort_resp_bad_param); + goto error; } csum = response->encrypted.checksum; response->encrypted.checksum = 0; rxkad_calc_response_checksum(response); if (response->encrypted.checksum != csum) { - rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, - rxkad_abort_resp_bad_checksum); - goto protocol_error_free; + ret = rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, + rxkad_abort_resp_bad_checksum); + goto error; } for (i = 0; i < RXRPC_MAXCALLS; i++) { @@ -1241,38 +1246,38 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, u32 counter = READ_ONCE(conn->channels[i].call_counter); if (call_id > INT_MAX) { - rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, - rxkad_abort_resp_bad_callid); - goto protocol_error_free; + ret = rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, + rxkad_abort_resp_bad_callid); + goto error; } if (call_id < counter) { - rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, - rxkad_abort_resp_call_ctr); - goto protocol_error_free; + ret = rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, + rxkad_abort_resp_call_ctr); + goto error; } if (call_id > counter) { if (conn->channels[i].call) { - rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, + ret = rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO, rxkad_abort_resp_call_state); - goto protocol_error_free; + goto error; } conn->channels[i].call_counter = call_id; } } if (ntohl(response->encrypted.inc_nonce) != conn->rxkad.nonce + 1) { - rxrpc_abort_conn(conn, skb, RXKADOUTOFSEQUENCE, -EPROTO, - rxkad_abort_resp_ooseq); - goto protocol_error_free; + ret = rxrpc_abort_conn(conn, skb, RXKADOUTOFSEQUENCE, -EPROTO, + rxkad_abort_resp_ooseq); + goto error; } level = ntohl(response->encrypted.level); if (level > RXRPC_SECURITY_ENCRYPT) { - rxrpc_abort_conn(conn, skb, RXKADLEVELFAIL, -EPROTO, - rxkad_abort_resp_level); - goto protocol_error_free; + ret = rxrpc_abort_conn(conn, skb, RXKADLEVELFAIL, -EPROTO, + rxkad_abort_resp_level); + goto error; } conn->security_level = level; @@ -1280,31 +1285,12 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, * this the connection security can be handled in exactly the same way * as for a client connection */ ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno); - if (ret < 0) - goto temporary_error_free_ticket; +error: kfree(ticket); - kfree(response); - _leave(" = 0"); - return 0; - -protocol_error_free: - kfree(ticket); -protocol_error: kfree(response); key_put(server_key); - return -EPROTO; - -temporary_error_free_ticket: - kfree(ticket); -temporary_error_free_resp: - kfree(response); -temporary_error: - /* Ignore the response packet if we got a temporary error such as - * ENOMEM. We just want to send the challenge again. Note that we - * also come out this way if the ticket decryption fails. - */ - key_put(server_key); + _leave(" = %d", ret); return ret; } diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c index 3bcd6ee80396..e2169d1a14b5 100644 --- a/net/rxrpc/skbuff.c +++ b/net/rxrpc/skbuff.c @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why) skb_get(skb); } -/* - * Note the dropping of a ref on a socket buffer by the core. - */ -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why) -{ - int n = atomic_inc_return(&rxrpc_n_rx_skbs); - trace_rxrpc_skb(skb, 0, n, why); -} - /* * Note the destruction of a socket buffer. */ diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 05e0b14b5773..2c5a7a321a94 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -354,7 +354,7 @@ static int tcf_blockcast_redir(struct sk_buff *skb, struct tcf_mirred *m, goto assign_prev; tcf_mirred_to_dev(skb, m, dev_prev, - dev_is_mac_header_xmit(dev), + dev_is_mac_header_xmit(dev_prev), mirred_eaction, retval); assign_prev: dev_prev = dev; diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index ffea9fbd522d..5862933be8d7 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -399,14 +399,14 @@ static void cake_configure_rates(struct Qdisc *sch, u64 rate, bool rate_adjust); * Here, invsqrt is a fixed point number (< 1.0), 32bit mantissa, aka Q0.32 */ -static void cobalt_newton_step(struct cobalt_vars *vars) +static void cobalt_newton_step(struct cobalt_vars *vars, u32 count) { u32 invsqrt, invsqrt2; u64 val; invsqrt = vars->rec_inv_sqrt; invsqrt2 = ((u64)invsqrt * invsqrt) >> 32; - val = (3LL << 32) - ((u64)vars->count * invsqrt2); + val = (3LL << 32) - ((u64)count * invsqrt2); val >>= 2; /* avoid overflow in following multiply */ val = (val * invsqrt) >> (32 - 2 + 1); @@ -414,12 +414,12 @@ static void cobalt_newton_step(struct cobalt_vars *vars) vars->rec_inv_sqrt = val; } -static void cobalt_invsqrt(struct cobalt_vars *vars) +static void cobalt_invsqrt(struct cobalt_vars *vars, u32 count) { - if (vars->count < REC_INV_SQRT_CACHE) - vars->rec_inv_sqrt = inv_sqrt_cache[vars->count]; + if (count < REC_INV_SQRT_CACHE) + vars->rec_inv_sqrt = inv_sqrt_cache[count]; else - cobalt_newton_step(vars); + cobalt_newton_step(vars, count); } static void cobalt_vars_init(struct cobalt_vars *vars) @@ -449,16 +449,19 @@ static bool cobalt_queue_full(struct cobalt_vars *vars, bool up = false; if (ktime_to_ns(ktime_sub(now, vars->blue_timer)) > p->target) { - up = !vars->p_drop; - vars->p_drop += p->p_inc; - if (vars->p_drop < p->p_inc) - vars->p_drop = ~0; - vars->blue_timer = now; + u32 p_drop = vars->p_drop; + + up = !p_drop; + p_drop += p->p_inc; + if (p_drop < p->p_inc) + p_drop = ~0; + WRITE_ONCE(vars->p_drop, p_drop); + WRITE_ONCE(vars->blue_timer, now); } - vars->dropping = true; - vars->drop_next = now; + WRITE_ONCE(vars->dropping, true); + WRITE_ONCE(vars->drop_next, now); if (!vars->count) - vars->count = 1; + WRITE_ONCE(vars->count, 1); return up; } @@ -475,20 +478,20 @@ static bool cobalt_queue_empty(struct cobalt_vars *vars, if (vars->p_drop && ktime_to_ns(ktime_sub(now, vars->blue_timer)) > p->target) { if (vars->p_drop < p->p_dec) - vars->p_drop = 0; + WRITE_ONCE(vars->p_drop, 0); else - vars->p_drop -= p->p_dec; - vars->blue_timer = now; + WRITE_ONCE(vars->p_drop, vars->p_drop - p->p_dec); + WRITE_ONCE(vars->blue_timer, now); down = !vars->p_drop; } - vars->dropping = false; + WRITE_ONCE(vars->dropping, false); if (vars->count && ktime_to_ns(ktime_sub(now, vars->drop_next)) >= 0) { - vars->count--; - cobalt_invsqrt(vars); - vars->drop_next = cobalt_control(vars->drop_next, - p->interval, - vars->rec_inv_sqrt); + WRITE_ONCE(vars->count, vars->count - 1); + cobalt_invsqrt(vars, vars->count); + WRITE_ONCE(vars->drop_next, + cobalt_control(vars->drop_next, p->interval, + vars->rec_inv_sqrt)); } return down; @@ -507,6 +510,7 @@ static enum qdisc_drop_reason cobalt_should_drop(struct cobalt_vars *vars, bool next_due, over_target; ktime_t schedule; u64 sojourn; + u32 count; /* The 'schedule' variable records, in its sign, whether 'now' is before or * after 'drop_next'. This allows 'drop_next' to be updated before the next @@ -528,21 +532,22 @@ static enum qdisc_drop_reason cobalt_should_drop(struct cobalt_vars *vars, over_target = sojourn > p->target && sojourn > p->mtu_time * bulk_flows * 2 && sojourn > p->mtu_time * 4; - next_due = vars->count && ktime_to_ns(schedule) >= 0; + count = vars->count; + next_due = count && ktime_to_ns(schedule) >= 0; vars->ecn_marked = false; if (over_target) { if (!vars->dropping) { - vars->dropping = true; - vars->drop_next = cobalt_control(now, - p->interval, - vars->rec_inv_sqrt); + WRITE_ONCE(vars->dropping, true); + WRITE_ONCE(vars->drop_next, + cobalt_control(now, p->interval, + vars->rec_inv_sqrt)); } - if (!vars->count) - vars->count = 1; + if (!count) + count = 1; } else if (vars->dropping) { - vars->dropping = false; + WRITE_ONCE(vars->dropping, false); } if (next_due && vars->dropping) { @@ -550,23 +555,23 @@ static enum qdisc_drop_reason cobalt_should_drop(struct cobalt_vars *vars, if (!(vars->ecn_marked = INET_ECN_set_ce(skb))) reason = QDISC_DROP_CONGESTED; - vars->count++; - if (!vars->count) - vars->count--; - cobalt_invsqrt(vars); - vars->drop_next = cobalt_control(vars->drop_next, - p->interval, - vars->rec_inv_sqrt); + count++; + if (!count) + count--; + cobalt_invsqrt(vars, count); + WRITE_ONCE(vars->drop_next, + cobalt_control(vars->drop_next, p->interval, + vars->rec_inv_sqrt)); schedule = ktime_sub(now, vars->drop_next); } else { while (next_due) { - vars->count--; - cobalt_invsqrt(vars); - vars->drop_next = cobalt_control(vars->drop_next, - p->interval, - vars->rec_inv_sqrt); + count--; + cobalt_invsqrt(vars, count); + WRITE_ONCE(vars->drop_next, + cobalt_control(vars->drop_next, p->interval, + vars->rec_inv_sqrt)); schedule = ktime_sub(now, vars->drop_next); - next_due = vars->count && ktime_to_ns(schedule) >= 0; + next_due = count && ktime_to_ns(schedule) >= 0; } } @@ -575,11 +580,12 @@ static enum qdisc_drop_reason cobalt_should_drop(struct cobalt_vars *vars, get_random_u32() < vars->p_drop) reason = QDISC_DROP_FLOOD_PROTECTION; + WRITE_ONCE(vars->count, count); /* Overload the drop_next field as an activity timeout */ - if (!vars->count) - vars->drop_next = ktime_add_ns(now, p->interval); + if (!count) + WRITE_ONCE(vars->drop_next, ktime_add_ns(now, p->interval)); else if (ktime_to_ns(schedule) > 0 && reason == QDISC_DROP_UNSPEC) - vars->drop_next = now; + WRITE_ONCE(vars->drop_next, now); return reason; } @@ -619,7 +625,7 @@ static bool cake_update_flowkeys(struct flow_keys *keys, } port = rev ? tuple.src.u.all : tuple.dst.u.all; if (port != keys->ports.dst) { - port = keys->ports.dst; + keys->ports.dst = port; upd = true; } } @@ -813,7 +819,7 @@ skip_hash: i++, k = (k + 1) % CAKE_SET_WAYS) { if (q->tags[outer_hash + k] == flow_hash) { if (i) - q->way_hits++; + WRITE_ONCE(q->way_hits, q->way_hits + 1); if (!q->flows[outer_hash + k].set) { /* need to increment host refcnts */ @@ -831,7 +837,7 @@ skip_hash: for (i = 0; i < CAKE_SET_WAYS; i++, k = (k + 1) % CAKE_SET_WAYS) { if (!q->flows[outer_hash + k].set) { - q->way_misses++; + WRITE_ONCE(q->way_misses, q->way_misses + 1); allocate_src = cake_dsrc(flow_mode); allocate_dst = cake_ddst(flow_mode); goto found; @@ -841,7 +847,7 @@ skip_hash: /* With no empty queues, default to the original * queue, accept the collision, update the host tags. */ - q->way_collisions++; + WRITE_ONCE(q->way_collisions, q->way_collisions + 1); allocate_src = cake_dsrc(flow_mode); allocate_dst = cake_ddst(flow_mode); @@ -914,7 +920,7 @@ static struct sk_buff *dequeue_head(struct cake_flow *flow) struct sk_buff *skb = flow->head; if (skb) { - flow->head = skb->next; + WRITE_ONCE(flow->head, skb->next); skb_mark_not_on_list(skb); } @@ -926,7 +932,7 @@ static struct sk_buff *dequeue_head(struct cake_flow *flow) static void flow_queue_add(struct cake_flow *flow, struct sk_buff *skb) { if (!flow->head) - flow->head = skb; + WRITE_ONCE(flow->head, skb); else flow->tail->next = skb; flow->tail = skb; @@ -1357,7 +1363,7 @@ found: if (elig_ack_prev) elig_ack_prev->next = elig_ack->next; else - flow->head = elig_ack->next; + WRITE_ONCE(flow->head, elig_ack->next); skb_mark_not_on_list(elig_ack); @@ -1379,9 +1385,9 @@ static u32 cake_calc_overhead(struct cake_sched_data *qd, u32 len, u32 off) len -= off; if (qd->max_netlen < len) - qd->max_netlen = len; + WRITE_ONCE(qd->max_netlen, len); if (qd->min_netlen > len) - qd->min_netlen = len; + WRITE_ONCE(qd->min_netlen, len); len += q->rate_overhead; @@ -1401,9 +1407,9 @@ static u32 cake_calc_overhead(struct cake_sched_data *qd, u32 len, u32 off) } if (qd->max_adjlen < len) - qd->max_adjlen = len; + WRITE_ONCE(qd->max_adjlen, len); if (qd->min_adjlen > len) - qd->min_adjlen = len; + WRITE_ONCE(qd->min_adjlen, len); return len; } @@ -1416,7 +1422,7 @@ static u32 cake_overhead(struct cake_sched_data *q, const struct sk_buff *skb) u16 segs = qdisc_pkt_segs(skb); u32 len = qdisc_pkt_len(skb); - q->avg_netoff = cake_ewma(q->avg_netoff, off << 16, 8); + WRITE_ONCE(q->avg_netoff, cake_ewma(q->avg_netoff, off << 16, 8)); if (segs == 1) return cake_calc_overhead(q, len, off); @@ -1590,16 +1596,17 @@ static unsigned int cake_drop(struct Qdisc *sch, struct sk_buff **to_free) } if (cobalt_queue_full(&flow->cvars, &b->cparams, now)) - b->unresponsive_flow_count++; + WRITE_ONCE(b->unresponsive_flow_count, + b->unresponsive_flow_count + 1); len = qdisc_pkt_len(skb); q->buffer_used -= skb->truesize; - b->backlogs[idx] -= len; - b->tin_backlog -= len; + WRITE_ONCE(b->tin_backlog, b->tin_backlog - len); + WRITE_ONCE(b->backlogs[idx], b->backlogs[idx] - len); sch->qstats.backlog -= len; - flow->dropped++; - b->tin_dropped++; + WRITE_ONCE(flow->dropped, flow->dropped + 1); + WRITE_ONCE(b->tin_dropped, b->tin_dropped + 1); if (q->config->rate_flags & CAKE_FLAG_INGRESS) cake_advance_shaper(q, b, skb, now, true); @@ -1795,7 +1802,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, } if (unlikely(len > b->max_skblen)) - b->max_skblen = len; + WRITE_ONCE(b->max_skblen, len); if (qdisc_pkt_segs(skb) > 1 && q->config->rate_flags & CAKE_FLAG_SPLIT_GSO) { struct sk_buff *segs, *nskb; @@ -1819,15 +1826,15 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, numsegs++; slen += segs->len; q->buffer_used += segs->truesize; - b->packets++; + WRITE_ONCE(b->packets, b->packets + 1); } /* stats */ - b->bytes += slen; - b->backlogs[idx] += slen; - b->tin_backlog += slen; sch->qstats.backlog += slen; q->avg_window_bytes += slen; + WRITE_ONCE(b->bytes, b->bytes + slen); + WRITE_ONCE(b->tin_backlog, b->tin_backlog + slen); + WRITE_ONCE(b->backlogs[idx], b->backlogs[idx] + slen); qdisc_tree_reduce_backlog(sch, 1-numsegs, len-slen); consume_skb(skb); @@ -1843,10 +1850,10 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, ack = cake_ack_filter(q, flow); if (ack) { - b->ack_drops++; + WRITE_ONCE(b->ack_drops, b->ack_drops + 1); sch->qstats.drops++; ack_pkt_len = qdisc_pkt_len(ack); - b->bytes += ack_pkt_len; + WRITE_ONCE(b->bytes, b->bytes + ack_pkt_len); q->buffer_used += skb->truesize - ack->truesize; if (q->config->rate_flags & CAKE_FLAG_INGRESS) cake_advance_shaper(q, b, ack, now, true); @@ -1859,12 +1866,12 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, } /* stats */ - b->packets++; - b->bytes += len - ack_pkt_len; - b->backlogs[idx] += len - ack_pkt_len; - b->tin_backlog += len - ack_pkt_len; + WRITE_ONCE(b->packets, b->packets + 1); sch->qstats.backlog += len - ack_pkt_len; q->avg_window_bytes += len - ack_pkt_len; + WRITE_ONCE(b->bytes, b->bytes + len - ack_pkt_len); + WRITE_ONCE(b->tin_backlog, b->tin_backlog + len - ack_pkt_len); + WRITE_ONCE(b->backlogs[idx], b->backlogs[idx] + len - ack_pkt_len); } if (q->overflow_timeout) @@ -1894,9 +1901,9 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, u64 b = q->avg_window_bytes * (u64)NSEC_PER_SEC; b = div64_u64(b, window_interval); - q->avg_peak_bandwidth = - cake_ewma(q->avg_peak_bandwidth, b, - b > q->avg_peak_bandwidth ? 2 : 8); + WRITE_ONCE(q->avg_peak_bandwidth, + cake_ewma(q->avg_peak_bandwidth, b, + b > q->avg_peak_bandwidth ? 2 : 8)); q->avg_window_bytes = 0; q->avg_window_begin = now; @@ -1917,27 +1924,27 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (!flow->set) { list_add_tail(&flow->flowchain, &b->new_flows); } else { - b->decaying_flow_count--; + WRITE_ONCE(b->decaying_flow_count, b->decaying_flow_count - 1); list_move_tail(&flow->flowchain, &b->new_flows); } flow->set = CAKE_SET_SPARSE; - b->sparse_flow_count++; + WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count + 1); - flow->deficit = cake_get_flow_quantum(b, flow, q->config->flow_mode); + WRITE_ONCE(flow->deficit, cake_get_flow_quantum(b, flow, q->config->flow_mode)); } else if (flow->set == CAKE_SET_SPARSE_WAIT) { /* this flow was empty, accounted as a sparse flow, but actually * in the bulk rotation. */ flow->set = CAKE_SET_BULK; - b->sparse_flow_count--; - b->bulk_flow_count++; + WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count - 1); + WRITE_ONCE(b->bulk_flow_count, b->bulk_flow_count + 1); cake_inc_srchost_bulk_flow_count(b, flow, q->config->flow_mode); cake_inc_dsthost_bulk_flow_count(b, flow, q->config->flow_mode); } if (q->buffer_used > q->buffer_max_used) - q->buffer_max_used = q->buffer_used; + WRITE_ONCE(q->buffer_max_used, q->buffer_used); if (q->buffer_used <= q->buffer_limit) return NET_XMIT_SUCCESS; @@ -1976,8 +1983,8 @@ static struct sk_buff *cake_dequeue_one(struct Qdisc *sch) if (flow->head) { skb = dequeue_head(flow); len = qdisc_pkt_len(skb); - b->backlogs[q->cur_flow] -= len; - b->tin_backlog -= len; + WRITE_ONCE(b->backlogs[q->cur_flow], b->backlogs[q->cur_flow] - len); + WRITE_ONCE(b->tin_backlog, b->tin_backlog - len); sch->qstats.backlog -= len; q->buffer_used -= skb->truesize; sch->q.qlen--; @@ -2042,7 +2049,7 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch) cake_configure_rates(sch, new_rate, true); q->last_checked_active = now; - q->active_queues = num_active_qs; + WRITE_ONCE(q->active_queues, num_active_qs); } begin: @@ -2149,8 +2156,8 @@ retry: */ if (flow->set == CAKE_SET_SPARSE) { if (flow->head) { - b->sparse_flow_count--; - b->bulk_flow_count++; + WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count - 1); + WRITE_ONCE(b->bulk_flow_count, b->bulk_flow_count + 1); cake_inc_srchost_bulk_flow_count(b, flow, q->config->flow_mode); cake_inc_dsthost_bulk_flow_count(b, flow, q->config->flow_mode); @@ -2165,7 +2172,8 @@ retry: } } - flow->deficit += cake_get_flow_quantum(b, flow, q->config->flow_mode); + WRITE_ONCE(flow->deficit, + flow->deficit + cake_get_flow_quantum(b, flow, q->config->flow_mode)); list_move_tail(&flow->flowchain, &b->old_flows); goto retry; @@ -2177,7 +2185,8 @@ retry: if (!skb) { /* this queue was actually empty */ if (cobalt_queue_empty(&flow->cvars, &b->cparams, now)) - b->unresponsive_flow_count--; + WRITE_ONCE(b->unresponsive_flow_count, + b->unresponsive_flow_count - 1); if (flow->cvars.p_drop || flow->cvars.count || ktime_before(now, flow->cvars.drop_next)) { @@ -2187,32 +2196,32 @@ retry: list_move_tail(&flow->flowchain, &b->decaying_flows); if (flow->set == CAKE_SET_BULK) { - b->bulk_flow_count--; + WRITE_ONCE(b->bulk_flow_count, b->bulk_flow_count - 1); cake_dec_srchost_bulk_flow_count(b, flow, q->config->flow_mode); cake_dec_dsthost_bulk_flow_count(b, flow, q->config->flow_mode); - b->decaying_flow_count++; + WRITE_ONCE(b->decaying_flow_count, b->decaying_flow_count + 1); } else if (flow->set == CAKE_SET_SPARSE || flow->set == CAKE_SET_SPARSE_WAIT) { - b->sparse_flow_count--; - b->decaying_flow_count++; + WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count - 1); + WRITE_ONCE(b->decaying_flow_count, b->decaying_flow_count + 1); } flow->set = CAKE_SET_DECAYING; } else { /* remove empty queue from the flowchain */ list_del_init(&flow->flowchain); if (flow->set == CAKE_SET_SPARSE || - flow->set == CAKE_SET_SPARSE_WAIT) - b->sparse_flow_count--; - else if (flow->set == CAKE_SET_BULK) { - b->bulk_flow_count--; + flow->set == CAKE_SET_SPARSE_WAIT) { + WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count - 1); + } else if (flow->set == CAKE_SET_BULK) { + WRITE_ONCE(b->bulk_flow_count, b->bulk_flow_count - 1); cake_dec_srchost_bulk_flow_count(b, flow, q->config->flow_mode); cake_dec_dsthost_bulk_flow_count(b, flow, q->config->flow_mode); - } else - b->decaying_flow_count--; - + } else { + WRITE_ONCE(b->decaying_flow_count, b->decaying_flow_count - 1); + } flow->set = CAKE_SET_NONE; } goto begin; @@ -2230,11 +2239,11 @@ retry: if (q->config->rate_flags & CAKE_FLAG_INGRESS) { len = cake_advance_shaper(q, b, skb, now, true); - flow->deficit -= len; + WRITE_ONCE(flow->deficit, flow->deficit - len); b->tin_deficit -= len; } - flow->dropped++; - b->tin_dropped++; + WRITE_ONCE(flow->dropped, flow->dropped + 1); + WRITE_ONCE(b->tin_dropped, b->tin_dropped + 1); qdisc_tree_reduce_backlog(sch, 1, qdisc_pkt_len(skb)); qdisc_qstats_drop(sch); qdisc_dequeue_drop(sch, skb, reason); @@ -2242,20 +2251,22 @@ retry: goto retry; } - b->tin_ecn_mark += !!flow->cvars.ecn_marked; + WRITE_ONCE(b->tin_ecn_mark, b->tin_ecn_mark + !!flow->cvars.ecn_marked); qdisc_bstats_update(sch, skb); WRITE_ONCE(q->last_active, now); /* collect delay stats */ delay = ktime_to_ns(ktime_sub(now, cobalt_get_enqueue_time(skb))); - b->avge_delay = cake_ewma(b->avge_delay, delay, 8); - b->peak_delay = cake_ewma(b->peak_delay, delay, - delay > b->peak_delay ? 2 : 8); - b->base_delay = cake_ewma(b->base_delay, delay, - delay < b->base_delay ? 2 : 8); + WRITE_ONCE(b->avge_delay, cake_ewma(b->avge_delay, delay, 8)); + WRITE_ONCE(b->peak_delay, + cake_ewma(b->peak_delay, delay, + delay > b->peak_delay ? 2 : 8)); + WRITE_ONCE(b->base_delay, + cake_ewma(b->base_delay, delay, + delay < b->base_delay ? 2 : 8)); len = cake_advance_shaper(q, b, skb, now, false); - flow->deficit -= len; + WRITE_ONCE(flow->deficit, flow->deficit - len); b->tin_deficit -= len; if (ktime_after(q->time_next_packet, now) && sch->q.qlen) { @@ -2329,9 +2340,9 @@ static void cake_set_rate(struct cake_tin_data *b, u64 rate, u32 mtu, u8 rate_shft = 0; u64 rate_ns = 0; - b->flow_quantum = 1514; if (rate) { - b->flow_quantum = max(min(rate >> 12, 1514ULL), 300ULL); + WRITE_ONCE(b->flow_quantum, + max(min(rate >> 12, 1514ULL), 300ULL)); rate_shft = 34; rate_ns = ((u64)NSEC_PER_SEC) << rate_shft; rate_ns = div64_u64(rate_ns, max(MIN_RATE, rate)); @@ -2339,9 +2350,11 @@ static void cake_set_rate(struct cake_tin_data *b, u64 rate, u32 mtu, rate_ns >>= 1; rate_shft--; } - } /* else unlimited, ie. zero delay */ - - b->tin_rate_bps = rate; + } else { + /* else unlimited, ie. zero delay */ + WRITE_ONCE(b->flow_quantum, 1514); + } + WRITE_ONCE(b->tin_rate_bps, rate); b->tin_rate_ns = rate_ns; b->tin_rate_shft = rate_shft; @@ -2350,10 +2363,11 @@ static void cake_set_rate(struct cake_tin_data *b, u64 rate, u32 mtu, byte_target_ns = (byte_target * rate_ns) >> rate_shft; - b->cparams.target = max((byte_target_ns * 3) / 2, target_ns); - b->cparams.interval = max(rtt_est_ns + - b->cparams.target - target_ns, - b->cparams.target * 2); + WRITE_ONCE(b->cparams.target, + max((byte_target_ns * 3) / 2, target_ns)); + WRITE_ONCE(b->cparams.interval, + max(rtt_est_ns + b->cparams.target - target_ns, + b->cparams.target * 2)); b->cparams.mtu_time = byte_target_ns; b->cparams.p_inc = 1 << 24; /* 1/256 */ b->cparams.p_dec = 1 << 20; /* 1/4096 */ @@ -2611,25 +2625,27 @@ static void cake_reconfigure(struct Qdisc *sch) { struct cake_sched_data *qd = qdisc_priv(sch); struct cake_sched_config *q = qd->config; + u32 buffer_limit; cake_configure_rates(sch, qd->config->rate_bps, false); if (q->buffer_config_limit) { - qd->buffer_limit = q->buffer_config_limit; + buffer_limit = q->buffer_config_limit; } else if (q->rate_bps) { u64 t = q->rate_bps * q->interval; do_div(t, USEC_PER_SEC / 4); - qd->buffer_limit = max_t(u32, t, 4U << 20); + buffer_limit = max_t(u32, t, 4U << 20); } else { - qd->buffer_limit = ~0; + buffer_limit = ~0; } sch->flags &= ~TCQ_F_CAN_BYPASS; - qd->buffer_limit = min(qd->buffer_limit, - max(sch->limit * psched_mtu(qdisc_dev(sch)), - q->buffer_config_limit)); + WRITE_ONCE(qd->buffer_limit, + min(buffer_limit, + max(sch->limit * psched_mtu(qdisc_dev(sch)), + q->buffer_config_limit))); } static int cake_config_change(struct cake_sched_config *q, struct nlattr *opt, @@ -2774,10 +2790,10 @@ static int cake_change(struct Qdisc *sch, struct nlattr *opt, return ret; if (overhead_changed) { - qd->max_netlen = 0; - qd->max_adjlen = 0; - qd->min_netlen = ~0; - qd->min_adjlen = ~0; + WRITE_ONCE(qd->max_netlen, 0); + WRITE_ONCE(qd->max_adjlen, 0); + WRITE_ONCE(qd->min_netlen, ~0); + WRITE_ONCE(qd->min_adjlen, ~0); } if (qd->tins) { @@ -2995,15 +3011,15 @@ static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d) goto nla_put_failure; \ } while (0) - PUT_STAT_U64(CAPACITY_ESTIMATE64, q->avg_peak_bandwidth); - PUT_STAT_U32(MEMORY_LIMIT, q->buffer_limit); - PUT_STAT_U32(MEMORY_USED, q->buffer_max_used); - PUT_STAT_U32(AVG_NETOFF, ((q->avg_netoff + 0x8000) >> 16)); - PUT_STAT_U32(MAX_NETLEN, q->max_netlen); - PUT_STAT_U32(MAX_ADJLEN, q->max_adjlen); - PUT_STAT_U32(MIN_NETLEN, q->min_netlen); - PUT_STAT_U32(MIN_ADJLEN, q->min_adjlen); - PUT_STAT_U32(ACTIVE_QUEUES, q->active_queues); + PUT_STAT_U64(CAPACITY_ESTIMATE64, READ_ONCE(q->avg_peak_bandwidth)); + PUT_STAT_U32(MEMORY_LIMIT, READ_ONCE(q->buffer_limit)); + PUT_STAT_U32(MEMORY_USED, READ_ONCE(q->buffer_max_used)); + PUT_STAT_U32(AVG_NETOFF, ((READ_ONCE(q->avg_netoff) + 0x8000) >> 16)); + PUT_STAT_U32(MAX_NETLEN, READ_ONCE(q->max_netlen)); + PUT_STAT_U32(MAX_ADJLEN, READ_ONCE(q->max_adjlen)); + PUT_STAT_U32(MIN_NETLEN, READ_ONCE(q->min_netlen)); + PUT_STAT_U32(MIN_ADJLEN, READ_ONCE(q->min_adjlen)); + PUT_STAT_U32(ACTIVE_QUEUES, READ_ONCE(q->active_queues)); #undef PUT_STAT_U32 #undef PUT_STAT_U64 @@ -3029,38 +3045,38 @@ static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d) if (!ts) goto nla_put_failure; - PUT_TSTAT_U64(THRESHOLD_RATE64, b->tin_rate_bps); - PUT_TSTAT_U64(SENT_BYTES64, b->bytes); - PUT_TSTAT_U32(BACKLOG_BYTES, b->tin_backlog); + PUT_TSTAT_U64(THRESHOLD_RATE64, READ_ONCE(b->tin_rate_bps)); + PUT_TSTAT_U64(SENT_BYTES64, READ_ONCE(b->bytes)); + PUT_TSTAT_U32(BACKLOG_BYTES, READ_ONCE(b->tin_backlog)); PUT_TSTAT_U32(TARGET_US, - ktime_to_us(ns_to_ktime(b->cparams.target))); + ktime_to_us(ns_to_ktime(READ_ONCE(b->cparams.target)))); PUT_TSTAT_U32(INTERVAL_US, - ktime_to_us(ns_to_ktime(b->cparams.interval))); + ktime_to_us(ns_to_ktime(READ_ONCE(b->cparams.interval)))); - PUT_TSTAT_U32(SENT_PACKETS, b->packets); - PUT_TSTAT_U32(DROPPED_PACKETS, b->tin_dropped); - PUT_TSTAT_U32(ECN_MARKED_PACKETS, b->tin_ecn_mark); - PUT_TSTAT_U32(ACKS_DROPPED_PACKETS, b->ack_drops); + PUT_TSTAT_U32(SENT_PACKETS, READ_ONCE(b->packets)); + PUT_TSTAT_U32(DROPPED_PACKETS, READ_ONCE(b->tin_dropped)); + PUT_TSTAT_U32(ECN_MARKED_PACKETS, READ_ONCE(b->tin_ecn_mark)); + PUT_TSTAT_U32(ACKS_DROPPED_PACKETS, READ_ONCE(b->ack_drops)); PUT_TSTAT_U32(PEAK_DELAY_US, - ktime_to_us(ns_to_ktime(b->peak_delay))); + ktime_to_us(ns_to_ktime(READ_ONCE(b->peak_delay)))); PUT_TSTAT_U32(AVG_DELAY_US, - ktime_to_us(ns_to_ktime(b->avge_delay))); + ktime_to_us(ns_to_ktime(READ_ONCE(b->avge_delay)))); PUT_TSTAT_U32(BASE_DELAY_US, - ktime_to_us(ns_to_ktime(b->base_delay))); + ktime_to_us(ns_to_ktime(READ_ONCE(b->base_delay)))); - PUT_TSTAT_U32(WAY_INDIRECT_HITS, b->way_hits); - PUT_TSTAT_U32(WAY_MISSES, b->way_misses); - PUT_TSTAT_U32(WAY_COLLISIONS, b->way_collisions); + PUT_TSTAT_U32(WAY_INDIRECT_HITS, READ_ONCE(b->way_hits)); + PUT_TSTAT_U32(WAY_MISSES, READ_ONCE(b->way_misses)); + PUT_TSTAT_U32(WAY_COLLISIONS, READ_ONCE(b->way_collisions)); - PUT_TSTAT_U32(SPARSE_FLOWS, b->sparse_flow_count + - b->decaying_flow_count); - PUT_TSTAT_U32(BULK_FLOWS, b->bulk_flow_count); - PUT_TSTAT_U32(UNRESPONSIVE_FLOWS, b->unresponsive_flow_count); - PUT_TSTAT_U32(MAX_SKBLEN, b->max_skblen); + PUT_TSTAT_U32(SPARSE_FLOWS, READ_ONCE(b->sparse_flow_count) + + READ_ONCE(b->decaying_flow_count)); + PUT_TSTAT_U32(BULK_FLOWS, READ_ONCE(b->bulk_flow_count)); + PUT_TSTAT_U32(UNRESPONSIVE_FLOWS, READ_ONCE(b->unresponsive_flow_count)); + PUT_TSTAT_U32(MAX_SKBLEN, READ_ONCE(b->max_skblen)); - PUT_TSTAT_U32(FLOW_QUANTUM, b->flow_quantum); + PUT_TSTAT_U32(FLOW_QUANTUM, READ_ONCE(b->flow_quantum)); nla_nest_end(d->skb, ts); } @@ -3128,7 +3144,7 @@ static int cake_dump_class_stats(struct Qdisc *sch, unsigned long cl, flow = &b->flows[idx % CAKE_QUEUES]; - if (flow->head) { + if (READ_ONCE(flow->head)) { sch_tree_lock(sch); skb = flow->head; while (skb) { @@ -3137,13 +3153,15 @@ static int cake_dump_class_stats(struct Qdisc *sch, unsigned long cl, } sch_tree_unlock(sch); } - qs.backlog = b->backlogs[idx % CAKE_QUEUES]; - qs.drops = flow->dropped; + qs.backlog = READ_ONCE(b->backlogs[idx % CAKE_QUEUES]); + qs.drops = READ_ONCE(flow->dropped); } if (gnet_stats_copy_queue(d, NULL, &qs, qs.qlen) < 0) return -1; if (flow) { ktime_t now = ktime_get(); + bool dropping; + u32 p_drop; stats = nla_nest_start_noflag(d->skb, TCA_STATS_APP); if (!stats) @@ -3158,21 +3176,23 @@ static int cake_dump_class_stats(struct Qdisc *sch, unsigned long cl, goto nla_put_failure; \ } while (0) - PUT_STAT_S32(DEFICIT, flow->deficit); - PUT_STAT_U32(DROPPING, flow->cvars.dropping); - PUT_STAT_U32(COBALT_COUNT, flow->cvars.count); - PUT_STAT_U32(P_DROP, flow->cvars.p_drop); - if (flow->cvars.p_drop) { + PUT_STAT_S32(DEFICIT, READ_ONCE(flow->deficit)); + dropping = READ_ONCE(flow->cvars.dropping); + PUT_STAT_U32(DROPPING, dropping); + PUT_STAT_U32(COBALT_COUNT, READ_ONCE(flow->cvars.count)); + p_drop = READ_ONCE(flow->cvars.p_drop); + PUT_STAT_U32(P_DROP, p_drop); + if (p_drop) { PUT_STAT_S32(BLUE_TIMER_US, ktime_to_us( ktime_sub(now, - flow->cvars.blue_timer))); + READ_ONCE(flow->cvars.blue_timer)))); } - if (flow->cvars.dropping) { + if (dropping) { PUT_STAT_S32(DROP_NEXT_US, ktime_to_us( ktime_sub(now, - flow->cvars.drop_next))); + READ_ONCE(flow->cvars.drop_next)))); } if (nla_nest_end(d->skb, stats) < 0) @@ -3298,10 +3318,10 @@ static int cake_mq_change(struct Qdisc *sch, struct nlattr *opt, struct cake_sched_data *qd = qdisc_priv(chld); if (overhead_changed) { - qd->max_netlen = 0; - qd->max_adjlen = 0; - qd->min_netlen = ~0; - qd->min_adjlen = ~0; + WRITE_ONCE(qd->max_netlen, 0); + WRITE_ONCE(qd->max_adjlen, 0); + WRITE_ONCE(qd->min_netlen, ~0); + WRITE_ONCE(qd->min_adjlen, ~0); } if (qd->tins) { diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c index 8c9a0400c862..0f953bd46b58 100644 --- a/net/sched/sch_cbs.c +++ b/net/sched/sch_cbs.c @@ -243,6 +243,20 @@ static struct sk_buff *cbs_dequeue(struct Qdisc *sch) return q->dequeue(sch); } +static void cbs_reset(struct Qdisc *sch) +{ + struct cbs_sched_data *q = qdisc_priv(sch); + + /* Nothing to do if we couldn't create the underlying qdisc */ + if (!q->qdisc) + return; + + qdisc_reset(q->qdisc); + qdisc_watchdog_cancel(&q->watchdog); + q->credits = 0; + q->last = 0; +} + static const struct nla_policy cbs_policy[TCA_CBS_MAX + 1] = { [TCA_CBS_PARMS] = { .len = sizeof(struct tc_cbs_qopt) }, }; @@ -540,7 +554,7 @@ static struct Qdisc_ops cbs_qdisc_ops __read_mostly = { .dequeue = cbs_dequeue, .peek = qdisc_peek_dequeued, .init = cbs_init, - .reset = qdisc_reset_queue, + .reset = cbs_reset, .destroy = cbs_destroy, .change = cbs_change, .dump = cbs_dump, diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index 94df8e741a97..2875bcdb18a4 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c @@ -229,7 +229,7 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch, /* Draw a packet at random from queue and compare flow */ if (choke_match_random(q, skb, &idx)) { - q->stats.matched++; + WRITE_ONCE(q->stats.matched, q->stats.matched + 1); choke_drop_by_idx(sch, idx, to_free); goto congestion_drop; } @@ -241,11 +241,13 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch, qdisc_qstats_overlimit(sch); if (use_harddrop(q) || !use_ecn(q) || !INET_ECN_set_ce(skb)) { - q->stats.forced_drop++; + WRITE_ONCE(q->stats.forced_drop, + q->stats.forced_drop + 1); goto congestion_drop; } - q->stats.forced_mark++; + WRITE_ONCE(q->stats.forced_mark, + q->stats.forced_mark + 1); } else if (++q->vars.qcount) { if (red_mark_probability(p, &q->vars, q->vars.qavg)) { q->vars.qcount = 0; @@ -253,11 +255,13 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch, qdisc_qstats_overlimit(sch); if (!use_ecn(q) || !INET_ECN_set_ce(skb)) { - q->stats.prob_drop++; + WRITE_ONCE(q->stats.prob_drop, + q->stats.prob_drop + 1); goto congestion_drop; } - q->stats.prob_mark++; + WRITE_ONCE(q->stats.prob_mark, + q->stats.prob_mark + 1); } } else q->vars.qR = red_random(p); @@ -272,7 +276,7 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch, return NET_XMIT_SUCCESS; } - q->stats.pdrop++; + WRITE_ONCE(q->stats.pdrop, q->stats.pdrop + 1); return qdisc_drop(skb, sch, to_free); congestion_drop: @@ -461,10 +465,12 @@ static int choke_dump_stats(struct Qdisc *sch, struct gnet_dump *d) { struct choke_sched_data *q = qdisc_priv(sch); struct tc_choke_xstats st = { - .early = q->stats.prob_drop + q->stats.forced_drop, - .marked = q->stats.prob_mark + q->stats.forced_mark, - .pdrop = q->stats.pdrop, - .matched = q->stats.matched, + .early = READ_ONCE(q->stats.prob_drop) + + READ_ONCE(q->stats.forced_drop), + .marked = READ_ONCE(q->stats.prob_mark) + + READ_ONCE(q->stats.forced_mark), + .pdrop = READ_ONCE(q->stats.pdrop), + .matched = READ_ONCE(q->stats.matched), }; return gnet_stats_copy_app(d, &st, sizeof(st)); diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index fe6f5e889625..a22489c14458 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -868,11 +868,35 @@ static int dualpi2_change(struct Qdisc *sch, struct nlattr *opt, old_backlog = sch->qstats.backlog; while (qdisc_qlen(sch) > sch->limit || q->memory_used > q->memory_limit) { - struct sk_buff *skb = qdisc_dequeue_internal(sch, true); + struct sk_buff *skb = NULL; - q->memory_used -= skb->truesize; - qdisc_qstats_backlog_dec(sch, skb); - rtnl_qdisc_drop(skb, sch); + if (qdisc_qlen(sch) > qdisc_qlen(q->l_queue)) { + skb = qdisc_dequeue_internal(sch, true); + if (unlikely(!skb)) { + WARN_ON_ONCE(1); + break; + } + q->memory_used -= skb->truesize; + rtnl_qdisc_drop(skb, sch); + } else if (qdisc_qlen(q->l_queue)) { + skb = qdisc_dequeue_internal(q->l_queue, true); + if (unlikely(!skb)) { + WARN_ON_ONCE(1); + break; + } + /* L-queue packets are counted in both sch and + * l_queue on enqueue; qdisc_dequeue_internal() + * handled l_queue, so we further account for sch. + */ + --sch->q.qlen; + qdisc_qstats_backlog_dec(sch, skb); + q->memory_used -= skb->truesize; + rtnl_qdisc_drop(skb, q->l_queue); + qdisc_qstats_drop(sch); + } else { + WARN_ON_ONCE(1); + break; + } } qdisc_tree_reduce_backlog(sch, old_qlen - qdisc_qlen(sch), old_backlog - sch->qstats.backlog); @@ -914,6 +938,8 @@ static int dualpi2_init(struct Qdisc *sch, struct nlattr *opt, int err; sch->flags |= TCQ_F_DEQUEUE_DROPS; + hrtimer_setup(&q->pi2_timer, dualpi2_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_ABS_PINNED_SOFT); q->l_queue = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, TC_H_MAKE(sch->handle, 1), extack); @@ -926,8 +952,6 @@ static int dualpi2_init(struct Qdisc *sch, struct nlattr *opt, q->sch = sch; dualpi2_reset_default(sch); - hrtimer_setup(&q->pi2_timer, dualpi2_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_ABS_PINNED_SOFT); if (opt && nla_len(opt)) { err = dualpi2_change(sch, opt, extack); diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index 2a3d758f67ab..24db54684e8a 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -117,7 +117,7 @@ static inline struct sk_buff *dequeue_head(struct fq_codel_flow *flow) { struct sk_buff *skb = flow->head; - flow->head = skb->next; + WRITE_ONCE(flow->head, skb->next); skb_mark_not_on_list(skb); return skb; } @@ -127,7 +127,7 @@ static inline void flow_queue_add(struct fq_codel_flow *flow, struct sk_buff *skb) { if (flow->head == NULL) - flow->head = skb; + WRITE_ONCE(flow->head, skb); else flow->tail->next = skb; flow->tail = skb; @@ -173,8 +173,8 @@ static unsigned int fq_codel_drop(struct Qdisc *sch, unsigned int max_packets, } while (++i < max_packets && len < threshold); /* Tell codel to increase its signal strength also */ - flow->cvars.count += i; - q->backlogs[idx] -= len; + WRITE_ONCE(flow->cvars.count, flow->cvars.count + i); + WRITE_ONCE(q->backlogs[idx], q->backlogs[idx] - len); q->memory_usage -= mem; sch->qstats.drops += i; sch->qstats.backlog -= len; @@ -204,13 +204,13 @@ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch, codel_set_enqueue_time(skb); flow = &q->flows[idx]; flow_queue_add(flow, skb); - q->backlogs[idx] += qdisc_pkt_len(skb); + WRITE_ONCE(q->backlogs[idx], q->backlogs[idx] + qdisc_pkt_len(skb)); qdisc_qstats_backlog_inc(sch, skb); if (list_empty(&flow->flowchain)) { list_add_tail(&flow->flowchain, &q->new_flows); q->new_flow_count++; - flow->deficit = q->quantum; + WRITE_ONCE(flow->deficit, q->quantum); } get_codel_cb(skb)->mem_usage = skb->truesize; q->memory_usage += get_codel_cb(skb)->mem_usage; @@ -263,7 +263,8 @@ static struct sk_buff *dequeue_func(struct codel_vars *vars, void *ctx) flow = container_of(vars, struct fq_codel_flow, cvars); if (flow->head) { skb = dequeue_head(flow); - q->backlogs[flow - q->flows] -= qdisc_pkt_len(skb); + WRITE_ONCE(q->backlogs[flow - q->flows], + q->backlogs[flow - q->flows] - qdisc_pkt_len(skb)); q->memory_usage -= get_codel_cb(skb)->mem_usage; sch->q.qlen--; sch->qstats.backlog -= qdisc_pkt_len(skb); @@ -296,7 +297,7 @@ begin: flow = list_first_entry(head, struct fq_codel_flow, flowchain); if (flow->deficit <= 0) { - flow->deficit += q->quantum; + WRITE_ONCE(flow->deficit, flow->deficit + q->quantum); list_move_tail(&flow->flowchain, &q->old_flows); goto begin; } @@ -314,7 +315,7 @@ begin: goto begin; } qdisc_bstats_update(sch, skb); - flow->deficit -= qdisc_pkt_len(skb); + WRITE_ONCE(flow->deficit, flow->deficit - qdisc_pkt_len(skb)); if (q->cstats.drop_count) { qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, @@ -328,7 +329,7 @@ begin: static void fq_codel_flow_purge(struct fq_codel_flow *flow) { rtnl_kfree_skbs(flow->head, flow->tail); - flow->head = NULL; + WRITE_ONCE(flow->head, NULL); } static void fq_codel_reset(struct Qdisc *sch) @@ -585,6 +586,8 @@ static int fq_codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d) }; struct list_head *pos; + sch_tree_lock(sch); + st.qdisc_stats.maxpacket = q->cstats.maxpacket; st.qdisc_stats.drop_overlimit = q->drop_overlimit; st.qdisc_stats.ecn_mark = q->cstats.ecn_mark; @@ -593,7 +596,6 @@ static int fq_codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d) st.qdisc_stats.memory_usage = q->memory_usage; st.qdisc_stats.drop_overmemory = q->drop_overmemory; - sch_tree_lock(sch); list_for_each(pos, &q->new_flows) st.qdisc_stats.new_flows_len++; @@ -655,21 +657,21 @@ static int fq_codel_dump_class_stats(struct Qdisc *sch, unsigned long cl, memset(&xstats, 0, sizeof(xstats)); xstats.type = TCA_FQ_CODEL_XSTATS_CLASS; - xstats.class_stats.deficit = flow->deficit; + xstats.class_stats.deficit = READ_ONCE(flow->deficit); xstats.class_stats.ldelay = - codel_time_to_us(flow->cvars.ldelay); - xstats.class_stats.count = flow->cvars.count; - xstats.class_stats.lastcount = flow->cvars.lastcount; - xstats.class_stats.dropping = flow->cvars.dropping; - if (flow->cvars.dropping) { - codel_tdiff_t delta = flow->cvars.drop_next - + codel_time_to_us(READ_ONCE(flow->cvars.ldelay)); + xstats.class_stats.count = READ_ONCE(flow->cvars.count); + xstats.class_stats.lastcount = READ_ONCE(flow->cvars.lastcount); + xstats.class_stats.dropping = READ_ONCE(flow->cvars.dropping); + if (xstats.class_stats.dropping) { + codel_tdiff_t delta = READ_ONCE(flow->cvars.drop_next) - codel_get_time(); xstats.class_stats.drop_next = (delta >= 0) ? codel_time_to_us(delta) : -codel_time_to_us(-delta); } - if (flow->head) { + if (READ_ONCE(flow->head)) { sch_tree_lock(sch); skb = flow->head; while (skb) { @@ -678,7 +680,7 @@ static int fq_codel_dump_class_stats(struct Qdisc *sch, unsigned long cl, } sch_tree_unlock(sch); } - qs.backlog = q->backlogs[idx]; + qs.backlog = READ_ONCE(q->backlogs[idx]); qs.drops = 0; } if (gnet_stats_copy_queue(d, NULL, &qs, qs.qlen) < 0) diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c index 154c70f489f2..7becbf5362b3 100644 --- a/net/sched/sch_fq_pie.c +++ b/net/sched/sch_fq_pie.c @@ -509,18 +509,19 @@ nla_put_failure: static int fq_pie_dump_stats(struct Qdisc *sch, struct gnet_dump *d) { struct fq_pie_sched_data *q = qdisc_priv(sch); - struct tc_fq_pie_xstats st = { - .packets_in = q->stats.packets_in, - .overlimit = q->stats.overlimit, - .overmemory = q->overmemory, - .dropped = q->stats.dropped, - .ecn_mark = q->stats.ecn_mark, - .new_flow_count = q->new_flow_count, - .memory_usage = q->memory_usage, - }; + struct tc_fq_pie_xstats st = { 0 }; struct list_head *pos; sch_tree_lock(sch); + + st.packets_in = q->stats.packets_in; + st.overlimit = q->stats.overlimit; + st.overmemory = q->overmemory; + st.dropped = q->stats.dropped; + st.ecn_mark = q->stats.ecn_mark; + st.new_flow_count = q->new_flow_count; + st.memory_usage = q->memory_usage; + list_for_each(pos, &q->new_flows) st.new_flows_len++; diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c index 95e5d9bfd9c8..96021f52d835 100644 --- a/net/sched/sch_hhf.c +++ b/net/sched/sch_hhf.c @@ -198,7 +198,8 @@ static struct hh_flow_state *seek_list(const u32 hash, return NULL; list_del(&flow->flowchain); kfree(flow); - q->hh_flows_current_cnt--; + WRITE_ONCE(q->hh_flows_current_cnt, + q->hh_flows_current_cnt - 1); } else if (flow->hash_id == hash) { return flow; } @@ -226,7 +227,7 @@ static struct hh_flow_state *alloc_new_hh(struct list_head *head, } if (q->hh_flows_current_cnt >= q->hh_flows_limit) { - q->hh_flows_overlimit++; + WRITE_ONCE(q->hh_flows_overlimit, q->hh_flows_overlimit + 1); return NULL; } /* Create new entry. */ @@ -234,7 +235,7 @@ static struct hh_flow_state *alloc_new_hh(struct list_head *head, if (!flow) return NULL; - q->hh_flows_current_cnt++; + WRITE_ONCE(q->hh_flows_current_cnt, q->hh_flows_current_cnt + 1); INIT_LIST_HEAD(&flow->flowchain); list_add_tail(&flow->flowchain, head); @@ -309,7 +310,7 @@ static enum wdrr_bucket_idx hhf_classify(struct sk_buff *skb, struct Qdisc *sch) return WDRR_BUCKET_FOR_NON_HH; flow->hash_id = hash; flow->hit_timestamp = now; - q->hh_flows_total_cnt++; + WRITE_ONCE(q->hh_flows_total_cnt, q->hh_flows_total_cnt + 1); /* By returning without updating counters in q->hhf_arrays, * we implicitly implement "shielding" (see Optimization O1). @@ -403,7 +404,7 @@ static int hhf_enqueue(struct sk_buff *skb, struct Qdisc *sch, return NET_XMIT_SUCCESS; prev_backlog = sch->qstats.backlog; - q->drop_overlimit++; + WRITE_ONCE(q->drop_overlimit, q->drop_overlimit + 1); /* Return Congestion Notification only if we dropped a packet from this * bucket. */ @@ -686,10 +687,10 @@ static int hhf_dump_stats(struct Qdisc *sch, struct gnet_dump *d) { struct hhf_sched_data *q = qdisc_priv(sch); struct tc_hhf_xstats st = { - .drop_overlimit = q->drop_overlimit, - .hh_overlimit = q->hh_flows_overlimit, - .hh_tot_count = q->hh_flows_total_cnt, - .hh_cur_count = q->hh_flows_current_cnt, + .drop_overlimit = READ_ONCE(q->drop_overlimit), + .hh_overlimit = READ_ONCE(q->hh_flows_overlimit), + .hh_tot_count = READ_ONCE(q->hh_flows_total_cnt), + .hh_cur_count = READ_ONCE(q->hh_flows_current_cnt), }; return gnet_stats_copy_app(d, &st, sizeof(st)); diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 20df1c08b1e9..bc18e1976b6e 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -227,10 +227,10 @@ static bool loss_4state(struct netem_sched_data *q) if (rnd < clg->a4) { clg->state = LOST_IN_GAP_PERIOD; return true; - } else if (clg->a4 < rnd && rnd < clg->a1 + clg->a4) { + } else if (rnd < clg->a1 + clg->a4) { clg->state = LOST_IN_BURST_PERIOD; return true; - } else if (clg->a1 + clg->a4 < rnd) { + } else { clg->state = TX_IN_GAP_PERIOD; } @@ -247,9 +247,9 @@ static bool loss_4state(struct netem_sched_data *q) case LOST_IN_BURST_PERIOD: if (rnd < clg->a3) clg->state = TX_IN_BURST_PERIOD; - else if (clg->a3 < rnd && rnd < clg->a2 + clg->a3) { + else if (rnd < clg->a2 + clg->a3) { clg->state = TX_IN_GAP_PERIOD; - } else if (clg->a2 + clg->a3 < rnd) { + } else { clg->state = LOST_IN_BURST_PERIOD; return true; } @@ -524,7 +524,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, 1 << get_random_u32_below(8); } - if (unlikely(q->t_len >= sch->limit)) { + if (unlikely(sch->q.qlen >= sch->limit)) { /* re-link segs, so that qdisc_drop_all() frees them all */ skb->next = segs; qdisc_drop_all(skb, sch, to_free); @@ -659,9 +659,8 @@ static void get_slot_next(struct netem_sched_data *q, u64 now) if (!q->slot_dist) next_delay = q->slot_config.min_delay + - (get_random_u32() * - (q->slot_config.max_delay - - q->slot_config.min_delay) >> 32); + mul_u64_u32_shr(q->slot_config.max_delay - q->slot_config.min_delay, + get_random_u32(), 32); else next_delay = tabledist(q->slot_config.dist_delay, (s32)(q->slot_config.dist_jitter), @@ -827,6 +826,39 @@ static int get_dist_table(struct disttable **tbl, const struct nlattr *attr) return 0; } +static int validate_time(const struct nlattr *attr, const char *name, + struct netlink_ext_ack *extack) +{ + if (nla_get_s64(attr) < 0) { + NL_SET_ERR_MSG_ATTR_FMT(extack, attr, "negative %s", name); + return -EINVAL; + } + return 0; +} + +static int validate_slot(const struct nlattr *attr, struct netlink_ext_ack *extack) +{ + const struct tc_netem_slot *c = nla_data(attr); + + if (c->min_delay < 0 || c->max_delay < 0) { + NL_SET_ERR_MSG_ATTR(extack, attr, "negative slot delay"); + return -EINVAL; + } + if (c->min_delay > c->max_delay) { + NL_SET_ERR_MSG_ATTR(extack, attr, "slot min delay greater than max delay"); + return -EINVAL; + } + if (c->dist_delay < 0 || c->dist_jitter < 0) { + NL_SET_ERR_MSG_ATTR(extack, attr, "negative dist delay"); + return -EINVAL; + } + if (c->max_packets < 0 || c->max_bytes < 0) { + NL_SET_ERR_MSG_ATTR(extack, attr, "negative slot limit"); + return -EINVAL; + } + return 0; +} + static void get_slot(struct netem_sched_data *q, const struct nlattr *attr) { const struct tc_netem_slot *c = nla_data(attr); @@ -1040,6 +1072,24 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, goto table_free; } + if (tb[TCA_NETEM_SLOT]) { + ret = validate_slot(tb[TCA_NETEM_SLOT], extack); + if (ret) + goto table_free; + } + + if (tb[TCA_NETEM_LATENCY64]) { + ret = validate_time(tb[TCA_NETEM_LATENCY64], "latency", extack); + if (ret) + goto table_free; + } + + if (tb[TCA_NETEM_JITTER64]) { + ret = validate_time(tb[TCA_NETEM_JITTER64], "jitter", extack); + if (ret) + goto table_free; + } + sch_tree_lock(sch); /* backup q->clg and q->loss_model */ old_clg = q->clg; @@ -1112,11 +1162,10 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, /* capping jitter to the range acceptable by tabledist() */ q->jitter = min_t(s64, abs(q->jitter), INT_MAX); - if (tb[TCA_NETEM_PRNG_SEED]) + if (tb[TCA_NETEM_PRNG_SEED]) { q->prng.seed = nla_get_u64(tb[TCA_NETEM_PRNG_SEED]); - else - q->prng.seed = get_random_u64(); - prandom_seed_state(&q->prng.prng_state, q->prng.seed); + prandom_seed_state(&q->prng.prng_state, q->prng.seed); + } unlock: sch_tree_unlock(sch); @@ -1139,6 +1188,9 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt, return -EINVAL; q->loss_model = CLG_RANDOM; + q->prng.seed = get_random_u64(); + prandom_seed_state(&q->prng.prng_state, q->prng.seed); + ret = netem_change(sch, opt, extack); if (ret) pr_info("netem: change failed\n"); diff --git a/net/sched/sch_pie.c b/net/sched/sch_pie.c index 16f3f629cb8e..b41f2def2e2c 100644 --- a/net/sched/sch_pie.c +++ b/net/sched/sch_pie.c @@ -90,7 +90,7 @@ static int pie_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, bool enqueue = false; if (unlikely(qdisc_qlen(sch) >= sch->limit)) { - q->stats.overlimit++; + WRITE_ONCE(q->stats.overlimit, q->stats.overlimit + 1); goto out; } @@ -104,7 +104,7 @@ static int pie_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, /* If packet is ecn capable, mark it if drop probability * is lower than 10%, else drop it. */ - q->stats.ecn_mark++; + WRITE_ONCE(q->stats.ecn_mark, q->stats.ecn_mark + 1); enqueue = true; } @@ -114,15 +114,15 @@ static int pie_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (!q->params.dq_rate_estimator) pie_set_enqueue_time(skb); - q->stats.packets_in++; + WRITE_ONCE(q->stats.packets_in, q->stats.packets_in + 1); if (qdisc_qlen(sch) > q->stats.maxq) - q->stats.maxq = qdisc_qlen(sch); + WRITE_ONCE(q->stats.maxq, qdisc_qlen(sch)); return qdisc_enqueue_tail(skb, sch); } out: - q->stats.dropped++; + WRITE_ONCE(q->stats.dropped, q->stats.dropped + 1); q->vars.accu_prob = 0; return qdisc_drop_reason(skb, sch, to_free, reason); } @@ -219,16 +219,14 @@ void pie_process_dequeue(struct sk_buff *skb, struct pie_params *params, * packet timestamp. */ if (!params->dq_rate_estimator) { - vars->qdelay = now - pie_get_enqueue_time(skb); + WRITE_ONCE(vars->qdelay, + backlog ? now - pie_get_enqueue_time(skb) : 0); if (vars->dq_tstamp != DTIME_INVALID) dtime = now - vars->dq_tstamp; vars->dq_tstamp = now; - if (backlog == 0) - vars->qdelay = 0; - if (dtime == 0) return; @@ -267,11 +265,11 @@ void pie_process_dequeue(struct sk_buff *skb, struct pie_params *params, count = count / dtime; if (vars->avg_dq_rate == 0) - vars->avg_dq_rate = count; + WRITE_ONCE(vars->avg_dq_rate, count); else - vars->avg_dq_rate = + WRITE_ONCE(vars->avg_dq_rate, (vars->avg_dq_rate - - (vars->avg_dq_rate >> 3)) + (count >> 3); + (vars->avg_dq_rate >> 3)) + (count >> 3)); /* If the queue has receded below the threshold, we hold * on to the last drain rate calculated, else we reset @@ -376,12 +374,12 @@ void pie_calculate_probability(struct pie_params *params, struct pie_vars *vars, if (qdelay > (PSCHED_NS2TICKS(250 * NSEC_PER_MSEC))) delta += MAX_PROB / (100 / 2); - vars->prob += delta; + WRITE_ONCE(vars->prob, vars->prob + delta); if (delta > 0) { /* prevent overflow */ if (vars->prob < oldprob) { - vars->prob = MAX_PROB; + WRITE_ONCE(vars->prob, MAX_PROB); /* Prevent normalization error. If probability is at * maximum value already, we normalize it here, and * skip the check to do a non-linear drop in the next @@ -392,7 +390,7 @@ void pie_calculate_probability(struct pie_params *params, struct pie_vars *vars, } else { /* prevent underflow */ if (vars->prob > oldprob) - vars->prob = 0; + WRITE_ONCE(vars->prob, 0); } /* Non-linear drop in probability: Reduce drop probability quickly if @@ -401,9 +399,9 @@ void pie_calculate_probability(struct pie_params *params, struct pie_vars *vars, if (qdelay == 0 && qdelay_old == 0 && update_prob) /* Reduce drop probability to 98.4% */ - vars->prob -= vars->prob / 64; + WRITE_ONCE(vars->prob, vars->prob - vars->prob / 64); - vars->qdelay = qdelay; + WRITE_ONCE(vars->qdelay, qdelay); vars->backlog_old = backlog; /* We restart the measurement cycle if the following conditions are met @@ -501,22 +499,22 @@ static int pie_dump_stats(struct Qdisc *sch, struct gnet_dump *d) { struct pie_sched_data *q = qdisc_priv(sch); struct tc_pie_xstats st = { - .prob = q->vars.prob << BITS_PER_BYTE, - .delay = ((u32)PSCHED_TICKS2NS(q->vars.qdelay)) / + .prob = READ_ONCE(q->vars.prob) << BITS_PER_BYTE, + .delay = ((u32)PSCHED_TICKS2NS(READ_ONCE(q->vars.qdelay))) / NSEC_PER_USEC, - .packets_in = q->stats.packets_in, - .overlimit = q->stats.overlimit, - .maxq = q->stats.maxq, - .dropped = q->stats.dropped, - .ecn_mark = q->stats.ecn_mark, + .packets_in = READ_ONCE(q->stats.packets_in), + .overlimit = READ_ONCE(q->stats.overlimit), + .maxq = READ_ONCE(q->stats.maxq), + .dropped = READ_ONCE(q->stats.dropped), + .ecn_mark = READ_ONCE(q->stats.ecn_mark), }; /* avg_dq_rate is only valid if dq_rate_estimator is enabled */ - st.dq_rate_estimating = q->params.dq_rate_estimator; + st.dq_rate_estimating = READ_ONCE(q->params.dq_rate_estimator); /* unscale and return dq_rate in bytes per sec */ - if (q->params.dq_rate_estimator) - st.avg_dq_rate = q->vars.avg_dq_rate * + if (st.dq_rate_estimating) + st.avg_dq_rate = READ_ONCE(q->vars.avg_dq_rate) * (PSCHED_TICKS_PER_SEC) >> PIE_SCALE; return gnet_stats_copy_app(d, &st, sizeof(st)); diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index c8d3d09f15e3..4d0e44a2e7c6 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -90,17 +90,20 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch, case RED_PROB_MARK: qdisc_qstats_overlimit(sch); if (!red_use_ecn(q)) { - q->stats.prob_drop++; + WRITE_ONCE(q->stats.prob_drop, + q->stats.prob_drop + 1); goto congestion_drop; } if (INET_ECN_set_ce(skb)) { - q->stats.prob_mark++; + WRITE_ONCE(q->stats.prob_mark, + q->stats.prob_mark + 1); skb = tcf_qevent_handle(&q->qe_mark, sch, skb, to_free, &ret); if (!skb) return NET_XMIT_CN | ret; } else if (!red_use_nodrop(q)) { - q->stats.prob_drop++; + WRITE_ONCE(q->stats.prob_drop, + q->stats.prob_drop + 1); goto congestion_drop; } @@ -111,17 +114,20 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch, reason = QDISC_DROP_OVERLIMIT; qdisc_qstats_overlimit(sch); if (red_use_harddrop(q) || !red_use_ecn(q)) { - q->stats.forced_drop++; + WRITE_ONCE(q->stats.forced_drop, + q->stats.forced_drop + 1); goto congestion_drop; } if (INET_ECN_set_ce(skb)) { - q->stats.forced_mark++; + WRITE_ONCE(q->stats.forced_mark, + q->stats.forced_mark + 1); skb = tcf_qevent_handle(&q->qe_mark, sch, skb, to_free, &ret); if (!skb) return NET_XMIT_CN | ret; } else if (!red_use_nodrop(q)) { - q->stats.forced_drop++; + WRITE_ONCE(q->stats.forced_drop, + q->stats.forced_drop + 1); goto congestion_drop; } @@ -135,7 +141,8 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch, sch->qstats.backlog += len; sch->q.qlen++; } else if (net_xmit_drop_count(ret)) { - q->stats.pdrop++; + WRITE_ONCE(q->stats.pdrop, + q->stats.pdrop + 1); qdisc_qstats_drop(sch); } return ret; @@ -155,7 +162,7 @@ static struct sk_buff *red_dequeue(struct Qdisc *sch) struct red_sched_data *q = qdisc_priv(sch); struct Qdisc *child = q->qdisc; - skb = child->dequeue(child); + skb = qdisc_dequeue_peeked(child); if (skb) { qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); @@ -463,9 +470,13 @@ static int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d) dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED, &hw_stats_request); } - st.early = q->stats.prob_drop + q->stats.forced_drop; - st.pdrop = q->stats.pdrop; - st.marked = q->stats.prob_mark + q->stats.forced_mark; + st.early = READ_ONCE(q->stats.prob_drop) + + READ_ONCE(q->stats.forced_drop); + + st.pdrop = READ_ONCE(q->stats.pdrop); + + st.marked = READ_ONCE(q->stats.prob_mark) + + READ_ONCE(q->stats.forced_mark); return gnet_stats_copy_app(d, &st, sizeof(st)); } diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index 013738662128..d3ee8e5479b3 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -130,7 +130,7 @@ static void increment_one_qlen(u32 sfbhash, u32 slot, struct sfb_sched_data *q) sfbhash >>= SFB_BUCKET_SHIFT; if (b[hash].qlen < 0xFFFF) - b[hash].qlen++; + WRITE_ONCE(b[hash].qlen, b[hash].qlen + 1); b += SFB_NUMBUCKETS; /* next level */ } } @@ -159,7 +159,7 @@ static void decrement_one_qlen(u32 sfbhash, u32 slot, sfbhash >>= SFB_BUCKET_SHIFT; if (b[hash].qlen > 0) - b[hash].qlen--; + WRITE_ONCE(b[hash].qlen, b[hash].qlen - 1); b += SFB_NUMBUCKETS; /* next level */ } } @@ -179,12 +179,12 @@ static void decrement_qlen(const struct sk_buff *skb, struct sfb_sched_data *q) static void decrement_prob(struct sfb_bucket *b, struct sfb_sched_data *q) { - b->p_mark = prob_minus(b->p_mark, q->decrement); + WRITE_ONCE(b->p_mark, prob_minus(b->p_mark, q->decrement)); } static void increment_prob(struct sfb_bucket *b, struct sfb_sched_data *q) { - b->p_mark = prob_plus(b->p_mark, q->increment); + WRITE_ONCE(b->p_mark, prob_plus(b->p_mark, q->increment)); } static void sfb_zero_all_buckets(struct sfb_sched_data *q) @@ -202,11 +202,14 @@ static u32 sfb_compute_qlen(u32 *prob_r, u32 *avgpm_r, const struct sfb_sched_da const struct sfb_bucket *b = &q->bins[q->slot].bins[0][0]; for (i = 0; i < SFB_LEVELS * SFB_NUMBUCKETS; i++) { - if (qlen < b->qlen) - qlen = b->qlen; - totalpm += b->p_mark; - if (prob < b->p_mark) - prob = b->p_mark; + u32 b_qlen = READ_ONCE(b->qlen); + u32 b_mark = READ_ONCE(b->p_mark); + + if (qlen < b_qlen) + qlen = b_qlen; + totalpm += b_mark; + if (prob < b_mark) + prob = b_mark; b++; } *prob_r = prob; @@ -295,7 +298,8 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (unlikely(sch->q.qlen >= q->limit)) { qdisc_qstats_overlimit(sch); - q->stats.queuedrop++; + WRITE_ONCE(q->stats.queuedrop, + q->stats.queuedrop + 1); goto drop; } @@ -348,7 +352,8 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (unlikely(minqlen >= q->max)) { qdisc_qstats_overlimit(sch); - q->stats.bucketdrop++; + WRITE_ONCE(q->stats.bucketdrop, + q->stats.bucketdrop + 1); goto drop; } @@ -374,7 +379,8 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch, } if (sfb_rate_limit(skb, q)) { qdisc_qstats_overlimit(sch); - q->stats.penaltydrop++; + WRITE_ONCE(q->stats.penaltydrop, + q->stats.penaltydrop + 1); goto drop; } goto enqueue; @@ -390,14 +396,17 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch, * In either case, we want to start dropping packets. */ if (r < (p_min - SFB_MAX_PROB / 2) * 2) { - q->stats.earlydrop++; + WRITE_ONCE(q->stats.earlydrop, + q->stats.earlydrop + 1); goto drop; } } if (INET_ECN_set_ce(skb)) { - q->stats.marked++; + WRITE_ONCE(q->stats.marked, + q->stats.marked + 1); } else { - q->stats.earlydrop++; + WRITE_ONCE(q->stats.earlydrop, + q->stats.earlydrop + 1); goto drop; } } @@ -410,7 +419,8 @@ enqueue: sch->q.qlen++; increment_qlen(&cb, q); } else if (net_xmit_drop_count(ret)) { - q->stats.childdrop++; + WRITE_ONCE(q->stats.childdrop, + q->stats.childdrop + 1); qdisc_qstats_drop(sch); } return ret; @@ -431,7 +441,7 @@ static struct sk_buff *sfb_dequeue(struct Qdisc *sch) struct Qdisc *child = q->qdisc; struct sk_buff *skb; - skb = child->dequeue(q->qdisc); + skb = qdisc_dequeue_peeked(child); if (skb) { qdisc_bstats_update(sch, skb); @@ -599,12 +609,12 @@ static int sfb_dump_stats(struct Qdisc *sch, struct gnet_dump *d) { struct sfb_sched_data *q = qdisc_priv(sch); struct tc_sfb_xstats st = { - .earlydrop = q->stats.earlydrop, - .penaltydrop = q->stats.penaltydrop, - .bucketdrop = q->stats.bucketdrop, - .queuedrop = q->stats.queuedrop, - .childdrop = q->stats.childdrop, - .marked = q->stats.marked, + .earlydrop = READ_ONCE(q->stats.earlydrop), + .penaltydrop = READ_ONCE(q->stats.penaltydrop), + .bucketdrop = READ_ONCE(q->stats.bucketdrop), + .queuedrop = READ_ONCE(q->stats.queuedrop), + .childdrop = READ_ONCE(q->stats.childdrop), + .marked = READ_ONCE(q->stats.marked), }; st.maxqlen = sfb_compute_qlen(&st.maxprob, &st.avgprob, q); diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index c3f3181dba54..f39822babf88 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -225,7 +225,8 @@ static inline void sfq_dec(struct sfq_sched_data *q, sfq_index x) sfq_unlink(q, x, n, p); - d = q->slots[x].qlen--; + d = q->slots[x].qlen; + WRITE_ONCE(q->slots[x].qlen, d - 1); if (n == p && q->cur_depth == d) q->cur_depth--; sfq_link(q, x); @@ -238,7 +239,8 @@ static inline void sfq_inc(struct sfq_sched_data *q, sfq_index x) sfq_unlink(q, x, n, p); - d = ++q->slots[x].qlen; + d = q->slots[x].qlen + 1; + WRITE_ONCE(q->slots[x].qlen, d); if (q->cur_depth < d) q->cur_depth = d; sfq_link(q, x); @@ -298,7 +300,7 @@ static unsigned int sfq_drop(struct Qdisc *sch, struct sk_buff **to_free) drop: skb = q->headdrop ? slot_dequeue_head(slot) : slot_dequeue_tail(slot); len = qdisc_pkt_len(skb); - slot->backlog -= len; + WRITE_ONCE(slot->backlog, slot->backlog - len); sfq_dec(q, x); sch->q.qlen--; qdisc_qstats_backlog_dec(sch, skb); @@ -314,7 +316,7 @@ drop: q->tail = NULL; /* no more active slots */ else q->tail->next = slot->next; - q->ht[slot->hash] = SFQ_EMPTY_SLOT; + WRITE_ONCE(q->ht[slot->hash], SFQ_EMPTY_SLOT); goto drop; } @@ -364,10 +366,10 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) x = q->dep[0].next; /* get a free slot */ if (x >= SFQ_MAX_FLOWS) return qdisc_drop_reason(skb, sch, to_free, QDISC_DROP_MAXFLOWS); - q->ht[hash] = x; + WRITE_ONCE(q->ht[hash], x); slot = &q->slots[x]; slot->hash = hash; - slot->backlog = 0; /* should already be 0 anyway... */ + WRITE_ONCE(slot->backlog, 0); /* should already be 0 anyway... */ red_set_vars(&slot->vars); goto enqueue; } @@ -426,7 +428,7 @@ congestion_drop: head = slot_dequeue_head(slot); delta = qdisc_pkt_len(head) - qdisc_pkt_len(skb); sch->qstats.backlog -= delta; - slot->backlog -= delta; + WRITE_ONCE(slot->backlog, slot->backlog - delta); qdisc_drop_reason(head, sch, to_free, QDISC_DROP_FLOW_LIMIT); slot_queue_add(slot, skb); @@ -436,7 +438,7 @@ congestion_drop: enqueue: qdisc_qstats_backlog_inc(sch, skb); - slot->backlog += qdisc_pkt_len(skb); + WRITE_ONCE(slot->backlog, slot->backlog + qdisc_pkt_len(skb)); slot_queue_add(slot, skb); sfq_inc(q, x); if (slot->qlen == 1) { /* The flow is new */ @@ -452,7 +454,7 @@ enqueue: */ q->tail = slot; /* We could use a bigger initial quantum for new flows */ - slot->allot = q->quantum; + WRITE_ONCE(slot->allot, q->quantum); } if (++sch->q.qlen <= q->limit) return NET_XMIT_SUCCESS; @@ -489,7 +491,7 @@ next_slot: slot = &q->slots[a]; if (slot->allot <= 0) { q->tail = slot; - slot->allot += q->quantum; + WRITE_ONCE(slot->allot, slot->allot + q->quantum); goto next_slot; } skb = slot_dequeue_head(slot); @@ -497,10 +499,10 @@ next_slot: qdisc_bstats_update(sch, skb); sch->q.qlen--; qdisc_qstats_backlog_dec(sch, skb); - slot->backlog -= qdisc_pkt_len(skb); + WRITE_ONCE(slot->backlog, slot->backlog - qdisc_pkt_len(skb)); /* Is the slot empty? */ if (slot->qlen == 0) { - q->ht[slot->hash] = SFQ_EMPTY_SLOT; + WRITE_ONCE(q->ht[slot->hash], SFQ_EMPTY_SLOT); next_a = slot->next; if (a == next_a) { q->tail = NULL; /* no more active slots */ @@ -508,7 +510,7 @@ next_slot: } q->tail->next = next_a; } else { - slot->allot -= qdisc_pkt_len(skb); + WRITE_ONCE(slot->allot, slot->allot - qdisc_pkt_len(skb)); } return skb; } @@ -549,9 +551,9 @@ static void sfq_rehash(struct Qdisc *sch) sfq_dec(q, i); __skb_queue_tail(&list, skb); } - slot->backlog = 0; + WRITE_ONCE(slot->backlog, 0); red_set_vars(&slot->vars); - q->ht[slot->hash] = SFQ_EMPTY_SLOT; + WRITE_ONCE(q->ht[slot->hash], SFQ_EMPTY_SLOT); } q->tail = NULL; @@ -570,7 +572,7 @@ drop: dropped++; continue; } - q->ht[hash] = x; + WRITE_ONCE(q->ht[hash], x); slot = &q->slots[x]; slot->hash = hash; } @@ -581,7 +583,7 @@ drop: slot->vars.qavg = red_calc_qavg(q->red_parms, &slot->vars, slot->backlog); - slot->backlog += qdisc_pkt_len(skb); + WRITE_ONCE(slot->backlog, slot->backlog + qdisc_pkt_len(skb)); sfq_inc(q, x); if (slot->qlen == 1) { /* The flow is new */ if (q->tail == NULL) { /* It is the first flow */ @@ -591,7 +593,7 @@ drop: q->tail->next = x; } q->tail = slot; - slot->allot = q->quantum; + WRITE_ONCE(slot->allot, q->quantum); } } sch->q.qlen -= dropped; @@ -905,16 +907,16 @@ static int sfq_dump_class_stats(struct Qdisc *sch, unsigned long cl, struct gnet_dump *d) { struct sfq_sched_data *q = qdisc_priv(sch); - sfq_index idx = q->ht[cl - 1]; + sfq_index idx = READ_ONCE(q->ht[cl - 1]); struct gnet_stats_queue qs = { 0 }; struct tc_sfq_xstats xstats = { 0 }; if (idx != SFQ_EMPTY_SLOT) { const struct sfq_slot *slot = &q->slots[idx]; - xstats.allot = slot->allot; - qs.qlen = slot->qlen; - qs.backlog = slot->backlog; + xstats.allot = READ_ONCE(slot->allot); + qs.qlen = READ_ONCE(slot->qlen); + qs.backlog = READ_ONCE(slot->backlog); } if (gnet_stats_copy_queue(d, NULL, &qs, qs.qlen) < 0) return -1; @@ -930,7 +932,7 @@ static void sfq_walk(struct Qdisc *sch, struct qdisc_walker *arg) return; for (i = 0; i < q->divisor; i++) { - if (q->ht[i] == SFQ_EMPTY_SLOT) { + if (READ_ONCE(q->ht[i]) == SFQ_EMPTY_SLOT) { arg->count++; continue; } diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 8e3752811950..45245157e00a 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -634,7 +634,7 @@ static int taprio_enqueue(struct sk_buff *skb, struct Qdisc *sch, queue = skb_get_queue_mapping(skb); child = q->qdiscs[queue]; - if (unlikely(!child)) + if (unlikely(child == &noop_qdisc)) return qdisc_drop(skb, sch, to_free); if (taprio_skb_exceeds_queue_max_sdu(sch, skb)) { @@ -717,7 +717,7 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq, int len; u8 tc; - if (unlikely(!child)) + if (unlikely(child == &noop_qdisc)) return NULL; if (TXTIME_ASSIST_IS_ENABLED(q->flags)) @@ -972,11 +972,12 @@ static enum hrtimer_restart advance_sched(struct hrtimer *timer) } if (should_change_schedules(admin, oper, end_time)) { - /* Set things so the next time this runs, the new - * schedule runs. - */ - end_time = sched_base_time(admin); switch_schedules(q, &admin, &oper); + /* After changing schedules, the next entry is the first one + * in the new schedule, with a pre-calculated end_time. + */ + next = list_first_entry(&oper->entries, struct sched_entry, list); + end_time = next->end_time; } next->end_time = end_time; @@ -2183,6 +2184,9 @@ static int taprio_graft(struct Qdisc *sch, unsigned long cl, if (!dev_queue) return -EINVAL; + if (!new) + new = &noop_qdisc; + if (dev->flags & IFF_UP) dev_deactivate(dev, false); @@ -2196,14 +2200,14 @@ static int taprio_graft(struct Qdisc *sch, unsigned long cl, *old = q->qdiscs[cl - 1]; if (FULL_OFFLOAD_IS_ENABLED(q->flags)) { WARN_ON_ONCE(dev_graft_qdisc(dev_queue, new) != *old); - if (new) + if (new != &noop_qdisc) qdisc_refcount_inc(new); - if (*old) + if (*old && *old != &noop_qdisc) qdisc_put(*old); } q->qdiscs[cl - 1] = new; - if (new) + if (new != &noop_qdisc) new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; if (dev->flags & IFF_UP) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 7b823d759141..8e89a870780c 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -1556,6 +1556,12 @@ static enum sctp_disposition sctp_sf_do_unexpected_init( /* Tag the variable length parameters. */ chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(struct sctp_inithdr)); + if (asoc->state >= SCTP_STATE_ESTABLISHED) { + /* Discard INIT matching peer vtag after handshake completion (stale INIT). */ + if (ntohl(chunk->subh.init_hdr->init_tag) == asoc->peer.i.init_tag) + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + } + /* Verify the INIT chunk before processing it. */ err_chunk = NULL; if (!sctp_verify_init(net, ep, asoc, chunk->chunk_hdr->type, diff --git a/net/sctp/socket.c b/net/sctp/socket.c index d2665bbd41a2..1d2568bb6bc2 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1986,6 +1986,15 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) goto out_unlock; iov_iter_revert(&msg->msg_iter, err); + + /* sctp_sendmsg_to_asoc() may have released the socket + * lock (sctp_wait_for_sndbuf), during which other + * associations on ep->asocs could have been peeled + * off or freed. @asoc itself is revalidated by the + * base.dead and base.sk checks in sctp_wait_for_sndbuf, + * so re-derive the cached cursor from it. + */ + tmp = list_next_entry(asoc, asocs); } goto out_unlock; @@ -4855,8 +4864,9 @@ static struct sock *sctp_clone_sock(struct sock *sk, if (!newsk) return ERR_PTR(err); - /* sk_clone() sets refcnt to 2 */ + /* sk_clone() sets refcnt to 2 and increments sockets_allocated */ sock_put(newsk); + sk_sockets_allocated_dec(newsk); newinet = inet_sk(newsk); newsp = sctp_sk(newsk); @@ -7033,7 +7043,7 @@ static int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len, /* See if the user provided enough room for all the data */ num_chunks = ntohs(ch->param_hdr.length) - sizeof(struct sctp_paramhdr); - if (len < num_chunks) + if (len < sizeof(struct sctp_authchunks) + num_chunks) return -EINVAL; if (copy_to_user(to, ch->chunks, num_chunks)) diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index 94bc9c7382ea..b1c65110f04d 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -21,6 +21,8 @@ #define NET_SHAPER_ID_UNSPEC NET_SHAPER_ID_MASK +static_assert(NET_SHAPER_ID_UNSPEC == NET_SHAPER_MAX_HANDLE_ID + 1); + struct net_shaper_hierarchy { struct xarray shapers; }; @@ -90,6 +92,12 @@ static int net_shaper_handle_size(void) nla_total_size(sizeof(u32))); } +static int net_shaper_group_reply_size(void) +{ + return nla_total_size(sizeof(u32)) + /* NET_SHAPER_A_IFINDEX */ + net_shaper_handle_size(); /* NET_SHAPER_A_HANDLE */ +} + static int net_shaper_fill_binding(struct sk_buff *msg, const struct net_shaper_binding *binding, u32 type) @@ -275,11 +283,13 @@ static void net_shaper_default_parent(const struct net_shaper_handle *handle, parent->id = 0; } -/* - * MARK_0 is already in use due to XA_FLAGS_ALLOC, can't reuse such flag as - * it's cleared by xa_store(). +/* MARK_0 is already in use due to XA_FLAGS_ALLOC. The VALID mark is set on + * an entry only after the device-side configuration has completed + * successfully (see net_shaper_commit()). Lookups and dumps must filter on + * this mark to avoid exposing tentative entries inserted by + * net_shaper_pre_insert() while the driver call is still in flight. */ -#define NET_SHAPER_NOT_VALID XA_MARK_1 +#define NET_SHAPER_VALID XA_MARK_1 static struct net_shaper * net_shaper_lookup(struct net_shaper_binding *binding, @@ -289,10 +299,14 @@ net_shaper_lookup(struct net_shaper_binding *binding, struct net_shaper_hierarchy *hierarchy; hierarchy = net_shaper_hierarchy_rcu(binding); - if (!hierarchy || xa_get_mark(&hierarchy->shapers, index, - NET_SHAPER_NOT_VALID)) + if (!hierarchy || !xa_get_mark(&hierarchy->shapers, index, + NET_SHAPER_VALID)) return NULL; + /* Pairs with smp_wmb() in net_shaper_commit(): if the entry is + * valid, its contents must be visible too. + */ + smp_rmb(); return xa_load(&hierarchy->shapers, index); } @@ -348,7 +362,7 @@ static int net_shaper_pre_insert(struct net_shaper_binding *binding, handle->id == NET_SHAPER_ID_UNSPEC) { u32 min, max; - handle->id = NET_SHAPER_ID_MASK - 1; + handle->id = NET_SHAPER_MAX_HANDLE_ID; max = net_shaper_handle_to_index(handle); handle->id = 0; min = net_shaper_handle_to_index(handle); @@ -370,13 +384,10 @@ static int net_shaper_pre_insert(struct net_shaper_binding *binding, goto free_id; } - /* Mark 'tentative' shaper inside the hierarchy container. - * xa_set_mark is a no-op if the previous store fails. + /* Insert as 'tentative' (no VALID mark). The mark will be set by + * net_shaper_commit() once the driver-side configuration succeeds. */ - xa_lock(&hierarchy->shapers); - prev = __xa_store(&hierarchy->shapers, index, cur, GFP_KERNEL); - __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_NOT_VALID); - xa_unlock(&hierarchy->shapers); + prev = xa_store(&hierarchy->shapers, index, cur, GFP_KERNEL); if (xa_err(prev)) { NL_SET_ERR_MSG(extack, "Can't insert shaper into device store"); kfree_rcu(cur, rcu); @@ -413,9 +424,9 @@ static void net_shaper_commit(struct net_shaper_binding *binding, /* Successful update: drop the tentative mark * and update the hierarchy container. */ - __xa_clear_mark(&hierarchy->shapers, index, - NET_SHAPER_NOT_VALID); *cur = shapers[i]; + smp_wmb(); + __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); } xa_unlock(&hierarchy->shapers); } @@ -431,8 +442,9 @@ static void net_shaper_rollback(struct net_shaper_binding *binding) return; xa_lock(&hierarchy->shapers); - xa_for_each_marked(&hierarchy->shapers, index, cur, - NET_SHAPER_NOT_VALID) { + xa_for_each(&hierarchy->shapers, index, cur) { + if (xa_get_mark(&hierarchy->shapers, index, NET_SHAPER_VALID)) + continue; __xa_erase(&hierarchy->shapers, index); kfree(cur); } @@ -465,10 +477,21 @@ static int net_shaper_parse_handle(const struct nlattr *attr, * shaper (any other value). */ id_attr = tb[NET_SHAPER_A_HANDLE_ID]; - if (id_attr) + if (id_attr) { id = nla_get_u32(id_attr); - else if (handle->scope == NET_SHAPER_SCOPE_NODE) + } else if (handle->scope == NET_SHAPER_SCOPE_NODE) { id = NET_SHAPER_ID_UNSPEC; + } else if (handle->scope == NET_SHAPER_SCOPE_QUEUE) { + NL_SET_ERR_ATTR_MISS(info->extack, attr, + NET_SHAPER_A_HANDLE_ID); + return -EINVAL; + } + + if (id && handle->scope == NET_SHAPER_SCOPE_NETDEV) { + NL_SET_ERR_MSG_ATTR(info->extack, id_attr, + "Netdev scope is a singleton, must use ID 0"); + return -EINVAL; + } handle->id = id; return 0; @@ -836,7 +859,12 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb, goto out_unlock; for (; (shaper = xa_find(&hierarchy->shapers, &ctx->start_index, - U32_MAX, XA_PRESENT)); ctx->start_index++) { + U32_MAX, NET_SHAPER_VALID)); + ctx->start_index++) { + /* Pairs with smp_wmb() in net_shaper_commit(): the entry + * is marked VALID, so its contents must be visible too. + */ + smp_rmb(); ret = net_shaper_fill_one(skb, binding, shaper, info); if (ret) break; @@ -932,6 +960,46 @@ static int net_shaper_handle_cmp(const struct net_shaper_handle *a, return memcmp(a, b, sizeof(*a)); } +static int net_shaper_parse_leaves(struct net_shaper_binding *binding, + struct genl_info *info, + const struct net_shaper *node, + struct net_shaper *leaves, + int leaves_count) +{ + struct nlattr *attr; + int i, j, ret, rem; + + i = 0; + nla_for_each_attr_type(attr, NET_SHAPER_A_LEAVES, + genlmsg_data(info->genlhdr), + genlmsg_len(info->genlhdr), rem) { + if (WARN_ON_ONCE(i >= leaves_count)) + return -EINVAL; + + ret = net_shaper_parse_leaf(binding, attr, info, + node, &leaves[i]); + if (ret) + return ret; + + /* Reject duplicates */ + for (j = 0; j < i; j++) { + if (net_shaper_handle_cmp(&leaves[i].handle, + &leaves[j].handle)) + continue; + + NL_SET_ERR_MSG_ATTR_FMT(info->extack, attr, + "Duplicate leaf shaper %d:%d", + leaves[i].handle.scope, + leaves[i].handle.id); + return -EINVAL; + } + + i++; + } + + return 0; +} + static int net_shaper_parent_from_leaves(int leaves_count, const struct net_shaper *leaves, struct net_shaper *node, @@ -964,15 +1032,22 @@ static int __net_shaper_group(struct net_shaper_binding *binding, int i, ret; if (node->handle.scope == NET_SHAPER_SCOPE_NODE) { + struct net_shaper *cur = NULL; + new_node = node->handle.id == NET_SHAPER_ID_UNSPEC; - if (!new_node && !net_shaper_lookup(binding, &node->handle)) { - /* The related attribute is not available when - * reaching here from the delete() op. - */ - NL_SET_ERR_MSG_FMT(extack, "Node shaper %d:%d does not exists", - node->handle.scope, node->handle.id); - return -ENOENT; + if (!new_node) { + cur = net_shaper_lookup(binding, &node->handle); + if (!cur) { + /* The related attribute is not available + * when reaching here from the delete() op. + */ + NL_SET_ERR_MSG_FMT(extack, + "Node shaper %d:%d does not exist", + node->handle.scope, + node->handle.id); + return -ENOENT; + } } /* When unspecified, the node parent scope is inherited from @@ -986,6 +1061,15 @@ static int __net_shaper_group(struct net_shaper_binding *binding, return ret; } + if (cur && net_shaper_handle_cmp(&cur->parent, + &node->parent)) { + NL_SET_ERR_MSG_FMT(extack, + "Cannot reparent node shaper %d:%d", + node->handle.scope, + node->handle.id); + return -EOPNOTSUPP; + } + } else { net_shaper_default_parent(&node->handle, &node->parent); } @@ -1162,7 +1246,7 @@ static int net_shaper_group_send_reply(struct net_shaper_binding *binding, free_msg: /* Should never happen as msg is pre-allocated with enough space. */ WARN_ONCE(true, "calculated message payload length (%d)", - net_shaper_handle_size()); + net_shaper_group_reply_size()); nlmsg_free(msg); return -EMSGSIZE; } @@ -1172,10 +1256,9 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) struct net_shaper **old_nodes, *leaves, node = {}; struct net_shaper_hierarchy *hierarchy; struct net_shaper_binding *binding; - int i, ret, rem, leaves_count; + int i, ret, leaves_count; int old_nodes_count = 0; struct sk_buff *msg; - struct nlattr *attr; if (GENL_REQ_ATTR_CHECK(info, NET_SHAPER_A_LEAVES)) return -EINVAL; @@ -1203,26 +1286,19 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) if (ret) goto free_leaves; - i = 0; - nla_for_each_attr_type(attr, NET_SHAPER_A_LEAVES, - genlmsg_data(info->genlhdr), - genlmsg_len(info->genlhdr), rem) { - if (WARN_ON_ONCE(i >= leaves_count)) - goto free_leaves; - - ret = net_shaper_parse_leaf(binding, attr, info, - &node, &leaves[i]); - if (ret) - goto free_leaves; - i++; - } + ret = net_shaper_parse_leaves(binding, info, &node, + leaves, leaves_count); + if (ret) + goto free_leaves; /* Prepare the msg reply in advance, to avoid device operation * rollback on allocation failure. */ - msg = genlmsg_new(net_shaper_handle_size(), GFP_KERNEL); - if (!msg) + msg = genlmsg_new(net_shaper_group_reply_size(), GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; goto free_leaves; + } hierarchy = net_shaper_hierarchy_setup(binding); if (!hierarchy) { diff --git a/net/shaper/shaper_nl_gen.c b/net/shaper/shaper_nl_gen.c index 9b29be3ef19a..76eff85ec66d 100644 --- a/net/shaper/shaper_nl_gen.c +++ b/net/shaper/shaper_nl_gen.c @@ -11,10 +11,15 @@ #include +/* Integer value ranges */ +static const struct netlink_range_validation net_shaper_a_handle_id_range = { + .max = NET_SHAPER_MAX_HANDLE_ID, +}; + /* Common nested types */ const struct nla_policy net_shaper_handle_nl_policy[NET_SHAPER_A_HANDLE_ID + 1] = { [NET_SHAPER_A_HANDLE_SCOPE] = NLA_POLICY_MAX(NLA_U32, 3), - [NET_SHAPER_A_HANDLE_ID] = { .type = NLA_U32, }, + [NET_SHAPER_A_HANDLE_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &net_shaper_a_handle_id_range), }; const struct nla_policy net_shaper_leaf_info_nl_policy[NET_SHAPER_A_WEIGHT + 1] = { diff --git a/net/shaper/shaper_nl_gen.h b/net/shaper/shaper_nl_gen.h index 42c46c52c775..2406652a9014 100644 --- a/net/shaper/shaper_nl_gen.h +++ b/net/shaper/shaper_nl_gen.h @@ -12,6 +12,8 @@ #include +#define NET_SHAPER_MAX_HANDLE_ID 67108862 + /* Common nested types */ extern const struct nla_policy net_shaper_handle_nl_policy[NET_SHAPER_A_HANDLE_ID + 1]; extern const struct nla_policy net_shaper_leaf_info_nl_policy[NET_SHAPER_A_WEIGHT + 1]; diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 1a565095376a..dffbd529762d 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -1400,7 +1400,8 @@ smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm *aclc, int i; for (i = 0; i < ini->ism_offered_cnt + 1; i++) { - if (ini->ism_chid[i] == ntohs(aclc->d1.chid)) { + if (ini->ism_dev[i] && + ini->ism_chid[i] == ntohs(aclc->d1.chid)) { ini->ism_selected = i; return 0; } @@ -1628,12 +1629,8 @@ static void smc_connect_work(struct work_struct *work) lock_sock(&smc->sk); if (rc != 0 || smc->sk.sk_err) { smc->sk.sk_state = SMC_CLOSED; - if (rc == -EPIPE || rc == -EAGAIN) - smc->sk.sk_err = EPIPE; - else if (rc == -ECONNREFUSED) - smc->sk.sk_err = ECONNREFUSED; - else if (signal_pending(current)) - smc->sk.sk_err = -sock_intr_errno(timeo); + if (!smc->sk.sk_err) + smc->sk.sk_err = (rc == -EAGAIN) ? EPIPE : -rc; sock_put(&smc->sk); /* passive closing */ goto out; } @@ -3058,18 +3055,17 @@ static int __smc_setsockopt(struct socket *sock, int level, int optname, smc = smc_sk(sk); + /* pre-fetch user data outside the lock */ + if (optname == SMC_LIMIT_HS) { + if (optlen < sizeof(int)) + return -EINVAL; + if (copy_from_sockptr(&val, optval, sizeof(int))) + return -EFAULT; + } + lock_sock(sk); switch (optname) { case SMC_LIMIT_HS: - if (optlen < sizeof(int)) { - rc = -EINVAL; - break; - } - if (copy_from_sockptr(&val, optval, sizeof(int))) { - rc = -EFAULT; - break; - } - smc->limit_smc_hs = !!val; rc = 0; break; diff --git a/net/smc/smc_clc.c b/net/smc/smc_clc.c index c38fc7bf0a7e..014d527d5462 100644 --- a/net/smc/smc_clc.c +++ b/net/smc/smc_clc.c @@ -788,8 +788,8 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen, dclc = (struct smc_clc_msg_decline *)clcm; reason_code = SMC_CLC_DECL_PEERDECL; smc->peer_diagnosis = ntohl(dclc->peer_diagnosis); - if (((struct smc_clc_msg_decline *)buf)->hdr.typev2 & - SMC_FIRST_CONTACT_MASK) { + if ((dclc->hdr.typev2 & SMC_FIRST_CONTACT_MASK) && + smc->conn.lgr) { smc->conn.lgr->sync_err = 1; smc_lgr_terminate_sched(smc->conn.lgr); } diff --git a/net/smc/smc_tracepoint.h b/net/smc/smc_tracepoint.h index a9a6e3c1113a..53da84f57fd6 100644 --- a/net/smc/smc_tracepoint.h +++ b/net/smc/smc_tracepoint.h @@ -51,7 +51,7 @@ DECLARE_EVENT_CLASS(smc_msg_event, __field(const void *, smc) __field(u64, net_cookie) __field(size_t, len) - __string(name, smc->conn.lnk->ibname) + __string(name, smc->conn.lnk ? smc->conn.lnk->ibname : "") ), TP_fast_assign( diff --git a/net/sunrpc/auth_gss/gss_krb5_test.c b/net/sunrpc/auth_gss/gss_krb5_test.c index a5bff02cd7ba..dde1ee934d0d 100644 --- a/net/sunrpc/auth_gss/gss_krb5_test.c +++ b/net/sunrpc/auth_gss/gss_krb5_test.c @@ -63,10 +63,11 @@ static void kdf_case(struct kunit *test) KUNIT_ASSERT_EQ(test, err, 0); /* Assert */ - KUNIT_EXPECT_EQ_MSG(test, - memcmp(param->expected_result->data, - derivedkey.data, derivedkey.len), 0, - "key mismatch"); + KUNIT_EXPECT_MEMEQ_MSG(test, + param->expected_result->data, + derivedkey.data, + derivedkey.len, + "key mismatch"); } static void checksum_case(struct kunit *test) @@ -111,10 +112,11 @@ static void checksum_case(struct kunit *test) KUNIT_ASSERT_EQ(test, err, 0); /* Assert */ - KUNIT_EXPECT_EQ_MSG(test, - memcmp(param->expected_result->data, - checksum.data, checksum.len), 0, - "checksum mismatch"); + KUNIT_EXPECT_MEMEQ_MSG(test, + param->expected_result->data, + checksum.data, + checksum.len, + "checksum mismatch"); crypto_free_ahash(tfm); } @@ -314,10 +316,11 @@ static void rfc3961_nfold_case(struct kunit *test) param->expected_result->len * 8, result); /* Assert */ - KUNIT_EXPECT_EQ_MSG(test, - memcmp(param->expected_result->data, - result, param->expected_result->len), 0, - "result mismatch"); + KUNIT_EXPECT_MEMEQ_MSG(test, + param->expected_result->data, + result, + param->expected_result->len, + "result mismatch"); } static struct kunit_case rfc3961_test_cases[] = { @@ -569,14 +572,16 @@ static void rfc3962_encrypt_case(struct kunit *test) KUNIT_EXPECT_EQ_MSG(test, param->expected_result->len, buf.len, "ciphertext length mismatch"); - KUNIT_EXPECT_EQ_MSG(test, - memcmp(param->expected_result->data, - text, param->expected_result->len), 0, - "ciphertext mismatch"); - KUNIT_EXPECT_EQ_MSG(test, - memcmp(param->next_iv->data, iv, - param->next_iv->len), 0, - "IV mismatch"); + KUNIT_EXPECT_MEMEQ_MSG(test, + param->expected_result->data, + text, + param->expected_result->len, + "ciphertext mismatch"); + KUNIT_EXPECT_MEMEQ_MSG(test, + param->next_iv->data, + iv, + param->next_iv->len, + "IV mismatch"); crypto_free_sync_skcipher(cts_tfm); crypto_free_sync_skcipher(cbc_tfm); @@ -1194,15 +1199,17 @@ static void rfc6803_encrypt_case(struct kunit *test) KUNIT_EXPECT_EQ_MSG(test, param->expected_result->len, buf.len + checksum.len, "ciphertext length mismatch"); - KUNIT_EXPECT_EQ_MSG(test, - memcmp(param->expected_result->data, - buf.head[0].iov_base, buf.len), 0, - "encrypted result mismatch"); - KUNIT_EXPECT_EQ_MSG(test, - memcmp(param->expected_result->data + - (param->expected_result->len - checksum.len), - checksum.data, checksum.len), 0, - "HMAC mismatch"); + KUNIT_EXPECT_MEMEQ_MSG(test, + param->expected_result->data, + buf.head[0].iov_base, + buf.len, + "encrypted result mismatch"); + KUNIT_EXPECT_MEMEQ_MSG(test, + param->expected_result->data + + (param->expected_result->len - checksum.len), + checksum.data, + checksum.len, + "HMAC mismatch"); crypto_free_ahash(ahash_tfm); crypto_free_sync_skcipher(cts_tfm); @@ -1687,15 +1694,16 @@ static void rfc8009_encrypt_case(struct kunit *test) KUNIT_EXPECT_EQ_MSG(test, param->expected_result->len, buf.len, "ciphertext length mismatch"); - KUNIT_EXPECT_EQ_MSG(test, - memcmp(param->expected_result->data, - buf.head[0].iov_base, - param->expected_result->len), 0, - "ciphertext mismatch"); - KUNIT_EXPECT_EQ_MSG(test, memcmp(param->expected_hmac->data, - checksum.data, - checksum.len), 0, - "HMAC mismatch"); + KUNIT_EXPECT_MEMEQ_MSG(test, + param->expected_result->data, + buf.head[0].iov_base, + param->expected_result->len, + "ciphertext mismatch"); + KUNIT_EXPECT_MEMEQ_MSG(test, + param->expected_hmac->data, + checksum.data, + checksum.len, + "HMAC mismatch"); crypto_free_ahash(ahash_tfm); crypto_free_sync_skcipher(cts_tfm); @@ -1826,10 +1834,11 @@ static void encrypt_selftest_case(struct kunit *test) KUNIT_EXPECT_EQ_MSG(test, param->plaintext->len, buf.len, "length mismatch"); - KUNIT_EXPECT_EQ_MSG(test, - memcmp(param->plaintext->data, - buf.head[0].iov_base, buf.len), 0, - "plaintext mismatch"); + KUNIT_EXPECT_MEMEQ_MSG(test, + param->plaintext->data, + buf.head[0].iov_base, + buf.len, + "plaintext mismatch"); crypto_free_sync_skcipher(cts_tfm); crypto_free_sync_skcipher(cbc_tfm); diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index ef8b7e8b1e9c..b5474ce534fb 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -134,11 +134,11 @@ static struct cache_head *sunrpc_cache_add_entry(struct cache_detail *detail, return tmp; } + cache_get(new); hlist_add_head_rcu(&new->cache_list, head); detail->entries++; if (detail->nextcheck > new->expiry_time) detail->nextcheck = new->expiry_time + 1; - cache_get(new); spin_unlock(&detail->hash_lock); if (freeme) @@ -233,9 +233,9 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail, spin_lock(&detail->hash_lock); cache_entry_update(detail, tmp, new); - hlist_add_head(&tmp->cache_list, &detail->hash_table[hash]); - detail->entries++; cache_get(tmp); + hlist_add_head_rcu(&tmp->cache_list, &detail->hash_table[hash]); + detail->entries++; cache_fresh_locked(tmp, new->expiry_time, detail); cache_fresh_locked(old, 0, detail); spin_unlock(&detail->hash_lock); @@ -399,7 +399,11 @@ static struct delayed_work cache_cleaner; void sunrpc_init_cache_detail(struct cache_detail *cd) { spin_lock_init(&cd->hash_lock); - INIT_LIST_HEAD(&cd->queue); + INIT_LIST_HEAD(&cd->requests); + INIT_LIST_HEAD(&cd->readers); + spin_lock_init(&cd->queue_lock); + init_waitqueue_head(&cd->queue_wait); + cd->next_seqno = 1; spin_lock(&cache_list_lock); cd->nextcheck = 0; cd->entries = 0; @@ -794,31 +798,20 @@ void cache_clean_deferred(void *owner) * On read, you get a full request, or block. * On write, an update request is processed. * Poll works if anything to read, and always allows write. - * - * Implemented by linked list of requests. Each open file has - * a ->private that also exists in this list. New requests are added - * to the end and may wakeup and preceding readers. - * New readers are added to the head. If, on read, an item is found with - * CACHE_UPCALLING clear, we free it from the list. - * */ -static DEFINE_SPINLOCK(queue_lock); - -struct cache_queue { - struct list_head list; - int reader; /* if 0, then request */ -}; struct cache_request { - struct cache_queue q; + struct list_head list; struct cache_head *item; - char * buf; + char *buf; int len; int readers; + u64 seqno; }; struct cache_reader { - struct cache_queue q; + struct list_head list; int offset; /* if non-0, we have a refcnt on next request */ + u64 next_seqno; }; static int cache_request(struct cache_detail *detail, @@ -833,6 +826,17 @@ static int cache_request(struct cache_detail *detail, return PAGE_SIZE - len; } +static struct cache_request * +cache_next_request(struct cache_detail *cd, u64 seqno) +{ + struct cache_request *rq; + + list_for_each_entry(rq, &cd->requests, list) + if (rq->seqno >= seqno) + return rq; + return NULL; +} + static ssize_t cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos, struct cache_detail *cd) { @@ -847,25 +851,18 @@ static ssize_t cache_read(struct file *filp, char __user *buf, size_t count, inode_lock(inode); /* protect against multiple concurrent * readers on this file */ again: - spin_lock(&queue_lock); + spin_lock(&cd->queue_lock); /* need to find next request */ - while (rp->q.list.next != &cd->queue && - list_entry(rp->q.list.next, struct cache_queue, list) - ->reader) { - struct list_head *next = rp->q.list.next; - list_move(&rp->q.list, next); - } - if (rp->q.list.next == &cd->queue) { - spin_unlock(&queue_lock); + rq = cache_next_request(cd, rp->next_seqno); + if (!rq) { + spin_unlock(&cd->queue_lock); inode_unlock(inode); WARN_ON_ONCE(rp->offset); return 0; } - rq = container_of(rp->q.list.next, struct cache_request, q.list); - WARN_ON_ONCE(rq->q.reader); if (rp->offset == 0) rq->readers++; - spin_unlock(&queue_lock); + spin_unlock(&cd->queue_lock); if (rq->len == 0) { err = cache_request(cd, rq); @@ -876,9 +873,7 @@ static ssize_t cache_read(struct file *filp, char __user *buf, size_t count, if (rp->offset == 0 && !test_bit(CACHE_PENDING, &rq->item->flags)) { err = -EAGAIN; - spin_lock(&queue_lock); - list_move(&rp->q.list, &rq->q.list); - spin_unlock(&queue_lock); + rp->next_seqno = rq->seqno + 1; } else { if (rp->offset + count > rq->len) count = rq->len - rp->offset; @@ -888,26 +883,24 @@ static ssize_t cache_read(struct file *filp, char __user *buf, size_t count, rp->offset += count; if (rp->offset >= rq->len) { rp->offset = 0; - spin_lock(&queue_lock); - list_move(&rp->q.list, &rq->q.list); - spin_unlock(&queue_lock); + rp->next_seqno = rq->seqno + 1; } err = 0; } out: if (rp->offset == 0) { /* need to release rq */ - spin_lock(&queue_lock); + spin_lock(&cd->queue_lock); rq->readers--; if (rq->readers == 0 && !test_bit(CACHE_PENDING, &rq->item->flags)) { - list_del(&rq->q.list); - spin_unlock(&queue_lock); + list_del(&rq->list); + spin_unlock(&cd->queue_lock); cache_put(rq->item, cd); kfree(rq->buf); kfree(rq); } else - spin_unlock(&queue_lock); + spin_unlock(&cd->queue_lock); } if (err == -EAGAIN) goto again; @@ -971,16 +964,13 @@ out: return ret; } -static DECLARE_WAIT_QUEUE_HEAD(queue_wait); - static __poll_t cache_poll(struct file *filp, poll_table *wait, struct cache_detail *cd) { __poll_t mask; struct cache_reader *rp = filp->private_data; - struct cache_queue *cq; - poll_wait(filp, &queue_wait, wait); + poll_wait(filp, &cd->queue_wait, wait); /* alway allow write */ mask = EPOLLOUT | EPOLLWRNORM; @@ -988,15 +978,11 @@ static __poll_t cache_poll(struct file *filp, poll_table *wait, if (!rp) return mask; - spin_lock(&queue_lock); + spin_lock(&cd->queue_lock); - for (cq= &rp->q; &cq->list != &cd->queue; - cq = list_entry(cq->list.next, struct cache_queue, list)) - if (!cq->reader) { - mask |= EPOLLIN | EPOLLRDNORM; - break; - } - spin_unlock(&queue_lock); + if (cache_next_request(cd, rp->next_seqno)) + mask |= EPOLLIN | EPOLLRDNORM; + spin_unlock(&cd->queue_lock); return mask; } @@ -1006,25 +992,20 @@ static int cache_ioctl(struct inode *ino, struct file *filp, { int len = 0; struct cache_reader *rp = filp->private_data; - struct cache_queue *cq; + struct cache_request *rq; if (cmd != FIONREAD || !rp) return -EINVAL; - spin_lock(&queue_lock); + spin_lock(&cd->queue_lock); /* only find the length remaining in current request, * or the length of the next request */ - for (cq= &rp->q; &cq->list != &cd->queue; - cq = list_entry(cq->list.next, struct cache_queue, list)) - if (!cq->reader) { - struct cache_request *cr = - container_of(cq, struct cache_request, q); - len = cr->len - rp->offset; - break; - } - spin_unlock(&queue_lock); + rq = cache_next_request(cd, rp->next_seqno); + if (rq) + len = rq->len - rp->offset; + spin_unlock(&cd->queue_lock); return put_user(len, (int __user *)arg); } @@ -1044,11 +1025,11 @@ static int cache_open(struct inode *inode, struct file *filp, return -ENOMEM; } rp->offset = 0; - rp->q.reader = 1; + rp->next_seqno = 0; - spin_lock(&queue_lock); - list_add(&rp->q.list, &cd->queue); - spin_unlock(&queue_lock); + spin_lock(&cd->queue_lock); + list_add(&rp->list, &cd->readers); + spin_unlock(&cd->queue_lock); } if (filp->f_mode & FMODE_WRITE) atomic_inc(&cd->writers); @@ -1064,29 +1045,24 @@ static int cache_release(struct inode *inode, struct file *filp, if (rp) { struct cache_request *rq = NULL; - spin_lock(&queue_lock); + spin_lock(&cd->queue_lock); if (rp->offset) { - struct cache_queue *cq; - for (cq = &rp->q; &cq->list != &cd->queue; - cq = list_entry(cq->list.next, - struct cache_queue, list)) - if (!cq->reader) { - struct cache_request *cr = - container_of(cq, - struct cache_request, q); - cr->readers--; - if (cr->readers == 0 && - !test_bit(CACHE_PENDING, - &cr->item->flags)) { - list_del(&cr->q.list); - rq = cr; - } - break; + struct cache_request *cr; + + cr = cache_next_request(cd, rp->next_seqno); + if (cr) { + cr->readers--; + if (cr->readers == 0 && + !test_bit(CACHE_PENDING, + &cr->item->flags)) { + list_del(&cr->list); + rq = cr; } + } rp->offset = 0; } - list_del(&rp->q.list); - spin_unlock(&queue_lock); + list_del(&rp->list); + spin_unlock(&cd->queue_lock); if (rq) { cache_put(rq->item, cd); @@ -1109,27 +1085,24 @@ static int cache_release(struct inode *inode, struct file *filp, static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch) { - struct cache_queue *cq, *tmp; - struct cache_request *cr; + struct cache_request *cr, *tmp; LIST_HEAD(dequeued); - spin_lock(&queue_lock); - list_for_each_entry_safe(cq, tmp, &detail->queue, list) - if (!cq->reader) { - cr = container_of(cq, struct cache_request, q); - if (cr->item != ch) - continue; - if (test_bit(CACHE_PENDING, &ch->flags)) - /* Lost a race and it is pending again */ - break; - if (cr->readers != 0) - continue; - list_move(&cr->q.list, &dequeued); - } - spin_unlock(&queue_lock); + spin_lock(&detail->queue_lock); + list_for_each_entry_safe(cr, tmp, &detail->requests, list) { + if (cr->item != ch) + continue; + if (test_bit(CACHE_PENDING, &ch->flags)) + /* Lost a race and it is pending again */ + break; + if (cr->readers != 0) + continue; + list_move(&cr->list, &dequeued); + } + spin_unlock(&detail->queue_lock); while (!list_empty(&dequeued)) { - cr = list_entry(dequeued.next, struct cache_request, q.list); - list_del(&cr->q.list); + cr = list_entry(dequeued.next, struct cache_request, list); + list_del(&cr->list); cache_put(cr->item, detail); kfree(cr->buf); kfree(cr); @@ -1247,20 +1220,20 @@ static int cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) return -EAGAIN; } - crq->q.reader = 0; crq->buf = buf; crq->len = 0; crq->readers = 0; - spin_lock(&queue_lock); + spin_lock(&detail->queue_lock); if (test_bit(CACHE_PENDING, &h->flags)) { crq->item = cache_get(h); - list_add_tail(&crq->q.list, &detail->queue); + crq->seqno = detail->next_seqno++; + list_add_tail(&crq->list, &detail->requests); trace_cache_entry_upcall(detail, h); } else /* Lost a race, no longer PENDING, so don't enqueue */ ret = -EAGAIN; - spin_unlock(&queue_lock); - wake_up(&queue_wait); + spin_unlock(&detail->queue_lock); + wake_up(&detail->queue_wait); if (ret == -EAGAIN) { kfree(buf); kfree(crq); @@ -1378,18 +1351,14 @@ static void *__cache_seq_start(struct seq_file *m, loff_t *pos) hlist_for_each_entry_rcu(ch, &cd->hash_table[hash], cache_list) if (!entry--) return ch; - n &= ~((1LL<<32) - 1); - do { - hash++; - n += 1LL<<32; - } while(hash < cd->hash_size && - hlist_empty(&cd->hash_table[hash])); - if (hash >= cd->hash_size) - return NULL; - *pos = n+1; - return hlist_entry_safe(rcu_dereference_raw( + ch = NULL; + while (!ch && ++hash < cd->hash_size) + ch = hlist_entry_safe(rcu_dereference( hlist_first_rcu(&cd->hash_table[hash])), struct cache_head, cache_list); + + *pos = ((long long)hash << 32) + 1; + return ch; } static void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos) @@ -1398,29 +1367,29 @@ static void *cache_seq_next(struct seq_file *m, void *p, loff_t *pos) int hash = (*pos >> 32); struct cache_detail *cd = m->private; - if (p == SEQ_START_TOKEN) + if (p == SEQ_START_TOKEN) { hash = 0; - else if (ch->cache_list.next == NULL) { - hash++; - *pos += 1LL<<32; - } else { - ++*pos; - return hlist_entry_safe(rcu_dereference_raw( + ch = NULL; + } + while (hash < cd->hash_size) { + if (ch) + ch = hlist_entry_safe( + rcu_dereference( hlist_next_rcu(&ch->cache_list)), - struct cache_head, cache_list); - } - *pos &= ~((1LL<<32) - 1); - while (hash < cd->hash_size && - hlist_empty(&cd->hash_table[hash])) { - hash++; - *pos += 1LL<<32; - } - if (hash >= cd->hash_size) - return NULL; - ++*pos; - return hlist_entry_safe(rcu_dereference_raw( - hlist_first_rcu(&cd->hash_table[hash])), struct cache_head, cache_list); + else + ch = hlist_entry_safe( + rcu_dereference( + hlist_first_rcu(&cd->hash_table[hash])), + struct cache_head, cache_list); + if (ch) { + ++*pos; + return ch; + } + hash++; + *pos = (long long)hash << 32; + } + return NULL; } void *cache_seq_start_rcu(struct seq_file *m, loff_t *pos) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index d8ccb8e4b5c2..576fa42e7abf 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -638,13 +638,25 @@ svc_init_buffer(struct svc_rqst *rqstp, const struct svc_serv *serv, int node) { rqstp->rq_maxpages = svc_serv_maxpages(serv); - /* rq_pages' last entry is NULL for historical reasons. */ + /* +1 for a NULL sentinel readable by nfsd_splice_actor() */ rqstp->rq_pages = kcalloc_node(rqstp->rq_maxpages + 1, sizeof(struct page *), GFP_KERNEL, node); if (!rqstp->rq_pages) return false; + /* +1 for a NULL sentinel at rq_page_end (see svc_rqst_replace_page) */ + rqstp->rq_respages = kcalloc_node(rqstp->rq_maxpages + 1, + sizeof(struct page *), + GFP_KERNEL, node); + if (!rqstp->rq_respages) { + kfree(rqstp->rq_pages); + rqstp->rq_pages = NULL; + return false; + } + + rqstp->rq_pages_nfree = rqstp->rq_maxpages; + rqstp->rq_next_page = rqstp->rq_respages + rqstp->rq_maxpages; return true; } @@ -656,10 +668,19 @@ svc_release_buffer(struct svc_rqst *rqstp) { unsigned long i; - for (i = 0; i < rqstp->rq_maxpages; i++) - if (rqstp->rq_pages[i]) - put_page(rqstp->rq_pages[i]); - kfree(rqstp->rq_pages); + if (rqstp->rq_pages) { + for (i = 0; i < rqstp->rq_maxpages; i++) + if (rqstp->rq_pages[i]) + put_page(rqstp->rq_pages[i]); + kfree(rqstp->rq_pages); + } + + if (rqstp->rq_respages) { + for (i = 0; i < rqstp->rq_maxpages; i++) + if (rqstp->rq_respages[i]) + put_page(rqstp->rq_respages[i]); + kfree(rqstp->rq_respages); + } } static void @@ -934,11 +955,11 @@ svc_set_num_threads(struct svc_serv *serv, unsigned int min_threads, EXPORT_SYMBOL_GPL(svc_set_num_threads); /** - * svc_rqst_replace_page - Replace one page in rq_pages[] + * svc_rqst_replace_page - Replace one page in rq_respages[] * @rqstp: svc_rqst with pages to replace * @page: replacement page * - * When replacing a page in rq_pages, batch the release of the + * When replacing a page in rq_respages, batch the release of the * replaced pages to avoid hammering the page allocator. * * Return values: @@ -947,19 +968,16 @@ EXPORT_SYMBOL_GPL(svc_set_num_threads); */ bool svc_rqst_replace_page(struct svc_rqst *rqstp, struct page *page) { - struct page **begin = rqstp->rq_pages; - struct page **end = &rqstp->rq_pages[rqstp->rq_maxpages]; + struct page **begin = rqstp->rq_respages; + struct page **end = rqstp->rq_page_end; if (unlikely(rqstp->rq_next_page < begin || rqstp->rq_next_page > end)) { trace_svc_replace_page_err(rqstp); return false; } - if (*rqstp->rq_next_page) { - if (!folio_batch_add(&rqstp->rq_fbatch, - page_folio(*rqstp->rq_next_page))) - __folio_batch_release(&rqstp->rq_fbatch); - } + if (*rqstp->rq_next_page) + svc_rqst_page_release(rqstp, *rqstp->rq_next_page); get_page(page); *(rqstp->rq_next_page++) = page; @@ -971,18 +989,24 @@ EXPORT_SYMBOL_GPL(svc_rqst_replace_page); * svc_rqst_release_pages - Release Reply buffer pages * @rqstp: RPC transaction context * - * Release response pages that might still be in flight after - * svc_send, and any spliced filesystem-owned pages. + * Release response pages in the range [rq_respages, rq_next_page). + * NULL entries in this range are skipped, allowing transports to + * transfer pages to a send context before this function runs. */ void svc_rqst_release_pages(struct svc_rqst *rqstp) { - int i, count = rqstp->rq_next_page - rqstp->rq_respages; + struct page **pp; - if (count) { - release_pages(rqstp->rq_respages, count); - for (i = 0; i < count; i++) - rqstp->rq_respages[i] = NULL; + for (pp = rqstp->rq_respages; pp < rqstp->rq_next_page; pp++) { + if (*pp) { + if (!folio_batch_add(&rqstp->rq_fbatch, + page_folio(*pp))) + __folio_batch_release(&rqstp->rq_fbatch); + *pp = NULL; + } } + if (rqstp->rq_fbatch.nr) + __folio_batch_release(&rqstp->rq_fbatch); } /** diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 56a663b8939f..b16e710926c1 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -650,14 +650,13 @@ static void svc_check_conn_limits(struct svc_serv *serv) } } -static bool svc_alloc_arg(struct svc_rqst *rqstp) +static bool svc_fill_pages(struct svc_rqst *rqstp, struct page **pages, + unsigned long npages) { - struct xdr_buf *arg = &rqstp->rq_arg; - unsigned long pages, filled, ret; + unsigned long filled, ret; - pages = rqstp->rq_maxpages; - for (filled = 0; filled < pages; filled = ret) { - ret = alloc_pages_bulk(GFP_KERNEL, pages, rqstp->rq_pages); + for (filled = 0; filled < npages; filled = ret) { + ret = alloc_pages_bulk(GFP_KERNEL, npages, pages); if (ret > filled) /* Made progress, don't sleep yet */ continue; @@ -667,11 +666,40 @@ static bool svc_alloc_arg(struct svc_rqst *rqstp) set_current_state(TASK_RUNNING); return false; } - trace_svc_alloc_arg_err(pages, ret); + trace_svc_alloc_arg_err(npages, ret); memalloc_retry_wait(GFP_KERNEL); } - rqstp->rq_page_end = &rqstp->rq_pages[pages]; - rqstp->rq_pages[pages] = NULL; /* this might be seen in nfsd_splice_actor() */ + return true; +} + +static bool svc_alloc_arg(struct svc_rqst *rqstp) +{ + struct xdr_buf *arg = &rqstp->rq_arg; + unsigned long pages, nfree; + + pages = rqstp->rq_maxpages; + + nfree = rqstp->rq_pages_nfree; + if (nfree) { + if (!svc_fill_pages(rqstp, rqstp->rq_pages, nfree)) + return false; + rqstp->rq_pages_nfree = 0; + } + + if (WARN_ON_ONCE(rqstp->rq_next_page < rqstp->rq_respages)) + return false; + nfree = rqstp->rq_next_page - rqstp->rq_respages; + if (nfree) { + if (!svc_fill_pages(rqstp, rqstp->rq_respages, nfree)) + return false; + } + + rqstp->rq_next_page = rqstp->rq_respages; + rqstp->rq_page_end = &rqstp->rq_respages[pages]; + /* svc_rqst_replace_page() dereferences *rq_next_page even + * at rq_page_end; NULL prevents releasing a garbage page. + */ + rqstp->rq_page_end[0] = NULL; /* Make arg->head point to first page and arg->pages point to rest */ arg->head[0].iov_base = page_address(rqstp->rq_pages[0]); @@ -1277,7 +1305,6 @@ static noinline int svc_deferred_recv(struct svc_rqst *rqstp) rqstp->rq_addrlen = dr->addrlen; /* Save off transport header len in case we get deferred again */ rqstp->rq_daddr = dr->daddr; - rqstp->rq_respages = rqstp->rq_pages; rqstp->rq_xprt_ctxt = dr->xprt_ctxt; dr->xprt_ctxt = NULL; diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index f28c6076f7e8..7be3de1a1aed 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -351,8 +351,6 @@ static ssize_t svc_tcp_read_msg(struct svc_rqst *rqstp, size_t buflen, for (i = 0, t = 0; t < buflen; i++, t += PAGE_SIZE) bvec_set_page(&bvec[i], rqstp->rq_pages[i], PAGE_SIZE, 0); - rqstp->rq_respages = &rqstp->rq_pages[i]; - rqstp->rq_next_page = rqstp->rq_respages + 1; iov_iter_bvec(&msg.msg_iter, ITER_DEST, bvec, i, buflen); if (seek) { @@ -677,13 +675,9 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) if (len <= rqstp->rq_arg.head[0].iov_len) { rqstp->rq_arg.head[0].iov_len = len; rqstp->rq_arg.page_len = 0; - rqstp->rq_respages = rqstp->rq_pages+1; } else { rqstp->rq_arg.page_len = len - rqstp->rq_arg.head[0].iov_len; - rqstp->rq_respages = rqstp->rq_pages + 1 + - DIV_ROUND_UP(rqstp->rq_arg.page_len, PAGE_SIZE); } - rqstp->rq_next_page = rqstp->rq_respages+1; if (serv->sv_stats) serv->sv_stats->netudpcnt++; @@ -994,7 +988,7 @@ static size_t svc_tcp_restore_pages(struct svc_sock *svsk, npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; for (i = 0; i < npages; i++) { if (rqstp->rq_pages[i] != NULL) - put_page(rqstp->rq_pages[i]); + svc_rqst_page_release(rqstp, rqstp->rq_pages[i]); BUG_ON(svsk->sk_pages[i] == NULL); rqstp->rq_pages[i] = svsk->sk_pages[i]; svsk->sk_pages[i] = NULL; @@ -1015,6 +1009,7 @@ static void svc_tcp_save_pages(struct svc_sock *svsk, struct svc_rqst *rqstp) svsk->sk_pages[i] = rqstp->rq_pages[i]; rqstp->rq_pages[i] = NULL; } + rqstp->rq_pages_nfree = npages; } static void svc_tcp_clear_pages(struct svc_sock *svsk) diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 4fbb57a29704..48a3618cbb29 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1663,6 +1663,22 @@ void xprt_add_backlog(struct rpc_xprt *xprt, struct rpc_task *task) } EXPORT_SYMBOL_GPL(xprt_add_backlog); +/** + * xprt_add_backlog_noncongested - queue task on backlog + * @xprt: transport whose backlog queue receives the task + * @task: task to queue + * + * Like xprt_add_backlog, but does not set XPRT_CONGESTED. + * For transports whose free_slot path does not synchronize + * with xprt_throttle_congested via reserve_lock. + */ +void xprt_add_backlog_noncongested(struct rpc_xprt *xprt, + struct rpc_task *task) +{ + rpc_sleep_on(&xprt->backlog, task, xprt_complete_request_init); +} +EXPORT_SYMBOL_GPL(xprt_add_backlog_noncongested); + static bool __xprt_set_rq(struct rpc_task *task, void *data) { struct rpc_rqst *req = data; diff --git a/net/sunrpc/xprtrdma/frwr_ops.c b/net/sunrpc/xprtrdma/frwr_ops.c index 31434aeb8e29..7f79a0a2601e 100644 --- a/net/sunrpc/xprtrdma/frwr_ops.c +++ b/net/sunrpc/xprtrdma/frwr_ops.c @@ -244,9 +244,10 @@ int frwr_query_device(struct rpcrdma_ep *ep, const struct ib_device *device) } ep->re_attr.cap.max_send_wr += RPCRDMA_BACKWARD_WRS; ep->re_attr.cap.max_send_wr += 1; /* for ib_drain_sq */ + ep->re_recv_batch = ep->re_max_requests >> 2; ep->re_attr.cap.max_recv_wr = ep->re_max_requests; ep->re_attr.cap.max_recv_wr += RPCRDMA_BACKWARD_WRS; - ep->re_attr.cap.max_recv_wr += RPCRDMA_MAX_RECV_BATCH; + ep->re_attr.cap.max_recv_wr += ep->re_recv_batch; ep->re_attr.cap.max_recv_wr += 1; /* for ib_drain_rq */ ep->re_max_rdma_segs = @@ -268,10 +269,9 @@ int frwr_query_device(struct rpcrdma_ep *ep, const struct ib_device *device) } /** - * frwr_map - Register a memory region + * frwr_map - Register a memory region from an xdr_buf cursor * @r_xprt: controlling transport - * @seg: memory region co-ordinates - * @nsegs: number of segments remaining + * @cur: cursor tracking position within the xdr_buf * @writing: true when RDMA Write will be used * @xid: XID of RPC using the registered memory * @mr: MR to fill in @@ -279,34 +279,104 @@ int frwr_query_device(struct rpcrdma_ep *ep, const struct ib_device *device) * Prepare a REG_MR Work Request to register a memory region * for remote access via RDMA READ or RDMA WRITE. * - * Returns the next segment or a negative errno pointer. - * On success, @mr is filled in. + * Returns 0 on success (cursor advanced past consumed data, + * @mr populated) or a negative errno on failure. */ -struct rpcrdma_mr_seg *frwr_map(struct rpcrdma_xprt *r_xprt, - struct rpcrdma_mr_seg *seg, - int nsegs, bool writing, __be32 xid, - struct rpcrdma_mr *mr) +int frwr_map(struct rpcrdma_xprt *r_xprt, + struct rpcrdma_xdr_cursor *cur, + bool writing, __be32 xid, + struct rpcrdma_mr *mr) { struct rpcrdma_ep *ep = r_xprt->rx_ep; + const struct xdr_buf *xdrbuf = cur->xc_buf; + bool sg_gaps = ep->re_mrtype == IB_MR_TYPE_SG_GAPS; + unsigned int max_depth = ep->re_max_fr_depth; struct ib_reg_wr *reg_wr; int i, n, dma_nents; struct ib_mr *ibmr; u8 key; - if (nsegs > ep->re_max_fr_depth) - nsegs = ep->re_max_fr_depth; - for (i = 0; i < nsegs;) { - sg_set_page(&mr->mr_sg[i], seg->mr_page, - seg->mr_len, seg->mr_offset); + i = 0; - ++seg; - ++i; - if (ep->re_mrtype == IB_MR_TYPE_SG_GAPS) - continue; - if ((i < nsegs && seg->mr_offset) || - offset_in_page((seg-1)->mr_offset + (seg-1)->mr_len)) - break; + /* Head kvec */ + if (!(cur->xc_flags & XC_HEAD_DONE)) { + const struct kvec *head = &xdrbuf->head[0]; + + sg_set_page(&mr->mr_sg[i], + virt_to_page(head->iov_base), + head->iov_len, + offset_in_page(head->iov_base)); + cur->xc_flags |= XC_HEAD_DONE; + i++; + /* Without sg-gap support, each non-contiguous region + * must be registered as a separate MR. Returning + * here after the head kvec causes the caller to + * invoke frwr_map() again for the page list and + * tail. + */ + if (!sg_gaps) + goto finish; } + + /* Page list */ + if (!(cur->xc_flags & XC_PAGES_DONE) && xdrbuf->page_len) { + unsigned int page_base, remaining; + struct page **ppages; + + remaining = xdrbuf->page_len - cur->xc_page_offset; + page_base = offset_in_page(xdrbuf->page_base + + cur->xc_page_offset); + ppages = xdrbuf->pages + + ((xdrbuf->page_base + cur->xc_page_offset) + >> PAGE_SHIFT); + + while (remaining > 0 && i < max_depth) { + unsigned int len; + + len = min_t(unsigned int, + PAGE_SIZE - page_base, remaining); + sg_set_page(&mr->mr_sg[i], *ppages, + len, page_base); + cur->xc_page_offset += len; + i++; + ppages++; + remaining -= len; + + if (!sg_gaps && remaining > 0 && + offset_in_page(page_base + len)) + goto finish; + page_base = 0; + } + if (remaining == 0) + cur->xc_flags |= XC_PAGES_DONE; + } else if (!(cur->xc_flags & XC_PAGES_DONE)) { + cur->xc_flags |= XC_PAGES_DONE; + } + + /* Tail kvec */ + if (!(cur->xc_flags & XC_TAIL_DONE) && xdrbuf->tail[0].iov_len && + i < max_depth) { + const struct kvec *tail = &xdrbuf->tail[0]; + + if (!sg_gaps && i > 0) { + struct scatterlist *prev = &mr->mr_sg[i - 1]; + + if (offset_in_page(prev->offset + prev->length) || + offset_in_page(tail->iov_base)) + goto finish; + } + sg_set_page(&mr->mr_sg[i], + virt_to_page(tail->iov_base), + tail->iov_len, + offset_in_page(tail->iov_base)); + cur->xc_flags |= XC_TAIL_DONE; + i++; + } else if (!(cur->xc_flags & XC_TAIL_DONE) && + !xdrbuf->tail[0].iov_len) { + cur->xc_flags |= XC_TAIL_DONE; + } + +finish: mr->mr_dir = rpcrdma_data_dir(writing); mr->mr_nents = i; @@ -338,15 +408,15 @@ struct rpcrdma_mr_seg *frwr_map(struct rpcrdma_xprt *r_xprt, mr->mr_offset = ibmr->iova; trace_xprtrdma_mr_map(mr); - return seg; + return 0; out_dmamap_err: trace_xprtrdma_frwr_sgerr(mr, i); - return ERR_PTR(-EIO); + return -EIO; out_mapmr_err: trace_xprtrdma_frwr_maperr(mr, n); - return ERR_PTR(-EIO); + return -EIO; } /** @@ -669,9 +739,13 @@ void frwr_unmap_async(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req) */ int frwr_wp_create(struct rpcrdma_xprt *r_xprt) { + struct rpcrdma_buffer *buf = &r_xprt->rx_buf; struct rpcrdma_ep *ep = r_xprt->rx_ep; - struct rpcrdma_mr_seg seg; + struct ib_reg_wr *reg_wr; struct rpcrdma_mr *mr; + struct ib_mr *ibmr; + int dma_nents; + int ret; mr = rpcrdma_mr_get(r_xprt); if (!mr) @@ -679,11 +753,39 @@ int frwr_wp_create(struct rpcrdma_xprt *r_xprt) mr->mr_req = NULL; ep->re_write_pad_mr = mr; - seg.mr_len = XDR_UNIT; - seg.mr_page = virt_to_page(ep->re_write_pad); - seg.mr_offset = offset_in_page(ep->re_write_pad); - if (IS_ERR(frwr_map(r_xprt, &seg, 1, true, xdr_zero, mr))) - return -EIO; + sg_init_table(mr->mr_sg, 1); + sg_set_page(mr->mr_sg, virt_to_page(ep->re_write_pad), + XDR_UNIT, offset_in_page(ep->re_write_pad)); + + mr->mr_dir = DMA_FROM_DEVICE; + mr->mr_nents = 1; + dma_nents = ib_dma_map_sg(ep->re_id->device, mr->mr_sg, + mr->mr_nents, mr->mr_dir); + if (!dma_nents) { + ret = -EIO; + goto out_mr; + } + mr->mr_device = ep->re_id->device; + + ibmr = mr->mr_ibmr; + if (ib_map_mr_sg(ibmr, mr->mr_sg, dma_nents, NULL, + PAGE_SIZE) != dma_nents) { + ret = -EIO; + goto out_unmap; + } + + /* IOVA is not tagged with an XID; the write-pad is not RPC-specific. */ + ib_update_fast_reg_key(ibmr, ib_inc_rkey(ibmr->rkey)); + + reg_wr = &mr->mr_regwr; + reg_wr->mr = ibmr; + reg_wr->key = ibmr->rkey; + reg_wr->access = IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE; + + mr->mr_handle = ibmr->rkey; + mr->mr_length = ibmr->length; + mr->mr_offset = ibmr->iova; + trace_xprtrdma_mr_fastreg(mr); mr->mr_cqe.done = frwr_wc_fastreg; @@ -693,5 +795,16 @@ int frwr_wp_create(struct rpcrdma_xprt *r_xprt) mr->mr_regwr.wr.opcode = IB_WR_REG_MR; mr->mr_regwr.wr.send_flags = 0; - return ib_post_send(ep->re_id->qp, &mr->mr_regwr.wr, NULL); + ret = ib_post_send(ep->re_id->qp, &mr->mr_regwr.wr, NULL); + if (!ret) + return 0; + +out_unmap: + frwr_mr_unmap(mr); +out_mr: + ep->re_write_pad_mr = NULL; + spin_lock(&buf->rb_lock); + rpcrdma_mr_push(mr, &buf->rb_mrs); + spin_unlock(&buf->rb_lock); + return ret; } diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 3aac1456e23e..0e0f21974710 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -200,67 +200,30 @@ rpcrdma_alloc_sparse_pages(struct xdr_buf *buf) return 0; } -/* Convert @vec to a single SGL element. - * - * Returns pointer to next available SGE, and bumps the total number - * of SGEs consumed. - */ -static struct rpcrdma_mr_seg * -rpcrdma_convert_kvec(struct kvec *vec, struct rpcrdma_mr_seg *seg, - unsigned int *n) +static void +rpcrdma_xdr_cursor_init(struct rpcrdma_xdr_cursor *cur, + const struct xdr_buf *xdrbuf, + unsigned int pos, enum rpcrdma_chunktype type) { - seg->mr_page = virt_to_page(vec->iov_base); - seg->mr_offset = offset_in_page(vec->iov_base); - seg->mr_len = vec->iov_len; - ++seg; - ++(*n); - return seg; + cur->xc_buf = xdrbuf; + cur->xc_page_offset = 0; + cur->xc_flags = 0; + + if (pos != 0) + cur->xc_flags |= XC_HEAD_DONE; + if (!xdrbuf->page_len) + cur->xc_flags |= XC_PAGES_DONE; + if (type == rpcrdma_readch || type == rpcrdma_writech || + !xdrbuf->tail[0].iov_len) + cur->xc_flags |= XC_TAIL_DONE; } -/* Convert @xdrbuf into SGEs no larger than a page each. As they - * are registered, these SGEs are then coalesced into RDMA segments - * when the selected memreg mode supports it. - * - * Returns positive number of SGEs consumed, or a negative errno. - */ - -static int -rpcrdma_convert_iovs(struct rpcrdma_xprt *r_xprt, struct xdr_buf *xdrbuf, - unsigned int pos, enum rpcrdma_chunktype type, - struct rpcrdma_mr_seg *seg) +static bool +rpcrdma_xdr_cursor_done(const struct rpcrdma_xdr_cursor *cur) { - unsigned long page_base; - unsigned int len, n; - struct page **ppages; - - n = 0; - if (pos == 0) - seg = rpcrdma_convert_kvec(&xdrbuf->head[0], seg, &n); - - len = xdrbuf->page_len; - ppages = xdrbuf->pages + (xdrbuf->page_base >> PAGE_SHIFT); - page_base = offset_in_page(xdrbuf->page_base); - while (len) { - seg->mr_page = *ppages; - seg->mr_offset = page_base; - seg->mr_len = min_t(u32, PAGE_SIZE - page_base, len); - len -= seg->mr_len; - ++ppages; - ++seg; - ++n; - page_base = 0; - } - - if (type == rpcrdma_readch || type == rpcrdma_writech) - goto out; - - if (xdrbuf->tail[0].iov_len) - rpcrdma_convert_kvec(&xdrbuf->tail[0], seg, &n); - -out: - if (unlikely(n > RPCRDMA_MAX_SEGS)) - return -EIO; - return n; + return (cur->xc_flags & (XC_HEAD_DONE | XC_PAGES_DONE | + XC_TAIL_DONE)) == + (XC_HEAD_DONE | XC_PAGES_DONE | XC_TAIL_DONE); } static int @@ -292,11 +255,10 @@ encode_read_segment(struct xdr_stream *xdr, struct rpcrdma_mr *mr, return 0; } -static struct rpcrdma_mr_seg *rpcrdma_mr_prepare(struct rpcrdma_xprt *r_xprt, - struct rpcrdma_req *req, - struct rpcrdma_mr_seg *seg, - int nsegs, bool writing, - struct rpcrdma_mr **mr) +static int rpcrdma_mr_prepare(struct rpcrdma_xprt *r_xprt, + struct rpcrdma_req *req, + struct rpcrdma_xdr_cursor *cur, + bool writing, struct rpcrdma_mr **mr) { *mr = rpcrdma_mr_pop(&req->rl_free_mrs); if (!*mr) { @@ -307,13 +269,13 @@ static struct rpcrdma_mr_seg *rpcrdma_mr_prepare(struct rpcrdma_xprt *r_xprt, } rpcrdma_mr_push(*mr, &req->rl_registered); - return frwr_map(r_xprt, seg, nsegs, writing, req->rl_slot.rq_xid, *mr); + return frwr_map(r_xprt, cur, writing, req->rl_slot.rq_xid, *mr); out_getmr_err: trace_xprtrdma_nomrs_err(r_xprt, req); xprt_wait_for_buffer_space(&r_xprt->rx_xprt); rpcrdma_mrs_refresh(r_xprt); - return ERR_PTR(-EAGAIN); + return -EAGAIN; } /* Register and XDR encode the Read list. Supports encoding a list of read @@ -336,10 +298,10 @@ static int rpcrdma_encode_read_list(struct rpcrdma_xprt *r_xprt, enum rpcrdma_chunktype rtype) { struct xdr_stream *xdr = &req->rl_stream; - struct rpcrdma_mr_seg *seg; + struct rpcrdma_xdr_cursor cur; struct rpcrdma_mr *mr; unsigned int pos; - int nsegs; + int ret; if (rtype == rpcrdma_noch_pullup || rtype == rpcrdma_noch_mapped) goto done; @@ -347,24 +309,20 @@ static int rpcrdma_encode_read_list(struct rpcrdma_xprt *r_xprt, pos = rqst->rq_snd_buf.head[0].iov_len; if (rtype == rpcrdma_areadch) pos = 0; - seg = req->rl_segments; - nsegs = rpcrdma_convert_iovs(r_xprt, &rqst->rq_snd_buf, pos, - rtype, seg); - if (nsegs < 0) - return nsegs; + rpcrdma_xdr_cursor_init(&cur, &rqst->rq_snd_buf, pos, rtype); do { - seg = rpcrdma_mr_prepare(r_xprt, req, seg, nsegs, false, &mr); - if (IS_ERR(seg)) - return PTR_ERR(seg); + ret = rpcrdma_mr_prepare(r_xprt, req, &cur, false, &mr); + if (ret) + return ret; if (encode_read_segment(xdr, mr, pos) < 0) return -EMSGSIZE; - trace_xprtrdma_chunk_read(rqst->rq_task, pos, mr, nsegs); + trace_xprtrdma_chunk_read(rqst->rq_task, pos, mr, + rpcrdma_xdr_cursor_done(&cur)); r_xprt->rx_stats.read_chunk_count++; - nsegs -= mr->mr_nents; - } while (nsegs); + } while (!rpcrdma_xdr_cursor_done(&cur)); done: if (xdr_stream_encode_item_absent(xdr) < 0) @@ -394,20 +352,16 @@ static int rpcrdma_encode_write_list(struct rpcrdma_xprt *r_xprt, { struct xdr_stream *xdr = &req->rl_stream; struct rpcrdma_ep *ep = r_xprt->rx_ep; - struct rpcrdma_mr_seg *seg; + struct rpcrdma_xdr_cursor cur; struct rpcrdma_mr *mr; - int nsegs, nchunks; + int nchunks, ret; __be32 *segcount; if (wtype != rpcrdma_writech) goto done; - seg = req->rl_segments; - nsegs = rpcrdma_convert_iovs(r_xprt, &rqst->rq_rcv_buf, - rqst->rq_rcv_buf.head[0].iov_len, - wtype, seg); - if (nsegs < 0) - return nsegs; + rpcrdma_xdr_cursor_init(&cur, &rqst->rq_rcv_buf, + rqst->rq_rcv_buf.head[0].iov_len, wtype); if (xdr_stream_encode_item_present(xdr) < 0) return -EMSGSIZE; @@ -418,30 +372,30 @@ static int rpcrdma_encode_write_list(struct rpcrdma_xprt *r_xprt, nchunks = 0; do { - seg = rpcrdma_mr_prepare(r_xprt, req, seg, nsegs, true, &mr); - if (IS_ERR(seg)) - return PTR_ERR(seg); + ret = rpcrdma_mr_prepare(r_xprt, req, &cur, true, &mr); + if (ret) + return ret; if (encode_rdma_segment(xdr, mr) < 0) return -EMSGSIZE; - trace_xprtrdma_chunk_write(rqst->rq_task, mr, nsegs); + trace_xprtrdma_chunk_write(rqst->rq_task, mr, + rpcrdma_xdr_cursor_done(&cur)); r_xprt->rx_stats.write_chunk_count++; r_xprt->rx_stats.total_rdma_request += mr->mr_length; nchunks++; - nsegs -= mr->mr_nents; - } while (nsegs); + } while (!rpcrdma_xdr_cursor_done(&cur)); if (xdr_pad_size(rqst->rq_rcv_buf.page_len)) { if (encode_rdma_segment(xdr, ep->re_write_pad_mr) < 0) return -EMSGSIZE; trace_xprtrdma_chunk_wp(rqst->rq_task, ep->re_write_pad_mr, - nsegs); + true); r_xprt->rx_stats.write_chunk_count++; - r_xprt->rx_stats.total_rdma_request += mr->mr_length; + r_xprt->rx_stats.total_rdma_request += + ep->re_write_pad_mr->mr_length; nchunks++; - nsegs -= mr->mr_nents; } /* Update count of segments in this Write chunk */ @@ -471,9 +425,9 @@ static int rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt, enum rpcrdma_chunktype wtype) { struct xdr_stream *xdr = &req->rl_stream; - struct rpcrdma_mr_seg *seg; + struct rpcrdma_xdr_cursor cur; struct rpcrdma_mr *mr; - int nsegs, nchunks; + int nchunks, ret; __be32 *segcount; if (wtype != rpcrdma_replych) { @@ -482,10 +436,7 @@ static int rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt, return 0; } - seg = req->rl_segments; - nsegs = rpcrdma_convert_iovs(r_xprt, &rqst->rq_rcv_buf, 0, wtype, seg); - if (nsegs < 0) - return nsegs; + rpcrdma_xdr_cursor_init(&cur, &rqst->rq_rcv_buf, 0, wtype); if (xdr_stream_encode_item_present(xdr) < 0) return -EMSGSIZE; @@ -496,19 +447,19 @@ static int rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt, nchunks = 0; do { - seg = rpcrdma_mr_prepare(r_xprt, req, seg, nsegs, true, &mr); - if (IS_ERR(seg)) - return PTR_ERR(seg); + ret = rpcrdma_mr_prepare(r_xprt, req, &cur, true, &mr); + if (ret) + return ret; if (encode_rdma_segment(xdr, mr) < 0) return -EMSGSIZE; - trace_xprtrdma_chunk_reply(rqst->rq_task, mr, nsegs); + trace_xprtrdma_chunk_reply(rqst->rq_task, mr, + rpcrdma_xdr_cursor_done(&cur)); r_xprt->rx_stats.reply_chunk_count++; r_xprt->rx_stats.total_rdma_request += mr->mr_length; nchunks++; - nsegs -= mr->mr_nents; - } while (nsegs); + } while (!rpcrdma_xdr_cursor_done(&cur)); /* Update count of segments in the Reply chunk */ *segcount = cpu_to_be32(nchunks); @@ -1471,7 +1422,6 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) credits = 1; /* don't deadlock */ else if (credits > r_xprt->rx_ep->re_max_requests) credits = r_xprt->rx_ep->re_max_requests; - rpcrdma_post_recvs(r_xprt, credits + (buf->rb_bc_srv_max_requests << 1)); if (buf->rb_credits != credits) rpcrdma_update_cwnd(r_xprt, credits); @@ -1490,15 +1440,20 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep) /* LocalInv completion will complete the RPC */ else kref_put(&req->rl_kref, rpcrdma_reply_done); - return; -out_badversion: - trace_xprtrdma_reply_vers_err(rep); - goto out; +out_post: + rpcrdma_post_recvs(r_xprt, + credits + (buf->rb_bc_srv_max_requests << 1)); + return; out_norqst: spin_unlock(&xprt->queue_lock); trace_xprtrdma_reply_rqst_err(rep); + rpcrdma_rep_put(buf, rep); + goto out_post; + +out_badversion: + trace_xprtrdma_reply_vers_err(rep); goto out; out_shortreply: diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index e7e4a39ca6c6..f8a0638eb095 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -118,7 +118,8 @@ svc_rdma_next_recv_ctxt(struct list_head *list) static struct svc_rdma_recv_ctxt * svc_rdma_recv_ctxt_alloc(struct svcxprt_rdma *rdma) { - int node = ibdev_to_node(rdma->sc_cm_id->device); + struct ib_device *device = rdma->sc_cm_id->device; + int node = ibdev_to_node(device); struct svc_rdma_recv_ctxt *ctxt; unsigned long pages; dma_addr_t addr; @@ -133,9 +134,9 @@ svc_rdma_recv_ctxt_alloc(struct svcxprt_rdma *rdma) buffer = kmalloc_node(rdma->sc_max_req_size, GFP_KERNEL, node); if (!buffer) goto fail1; - addr = ib_dma_map_single(rdma->sc_pd->device, buffer, - rdma->sc_max_req_size, DMA_FROM_DEVICE); - if (ib_dma_mapping_error(rdma->sc_pd->device, addr)) + addr = ib_dma_map_single(device, buffer, rdma->sc_max_req_size, + DMA_FROM_DEVICE); + if (ib_dma_mapping_error(device, addr)) goto fail2; svc_rdma_recv_cid_init(rdma, &ctxt->rc_cid); @@ -167,7 +168,7 @@ fail0: static void svc_rdma_recv_ctxt_destroy(struct svcxprt_rdma *rdma, struct svc_rdma_recv_ctxt *ctxt) { - ib_dma_unmap_single(rdma->sc_pd->device, ctxt->rc_recv_sge.addr, + ib_dma_unmap_single(rdma->sc_cm_id->device, ctxt->rc_recv_sge.addr, ctxt->rc_recv_sge.length, DMA_FROM_DEVICE); kfree(ctxt->rc_recv_buf); kfree(ctxt); @@ -861,18 +862,12 @@ static noinline void svc_rdma_read_complete(struct svc_rqst *rqstp, unsigned int i; /* Transfer the Read chunk pages into @rqstp.rq_pages, replacing - * the rq_pages that were already allocated for this rqstp. + * the receive buffer pages already allocated for this rqstp. */ - release_pages(rqstp->rq_respages, ctxt->rc_page_count); + release_pages(rqstp->rq_pages, ctxt->rc_page_count); for (i = 0; i < ctxt->rc_page_count; i++) rqstp->rq_pages[i] = ctxt->rc_pages[i]; - /* Update @rqstp's result send buffer to start after the - * last page in the RDMA Read payload. - */ - rqstp->rq_respages = &rqstp->rq_pages[ctxt->rc_page_count]; - rqstp->rq_next_page = rqstp->rq_respages + 1; - /* Prevent svc_rdma_recv_ctxt_put() from releasing the * pages in ctxt::rc_pages a second time. */ @@ -931,10 +926,9 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) struct svc_rdma_recv_ctxt *ctxt; int ret; - /* Prevent svc_xprt_release() from releasing pages in rq_pages - * when returning 0 or an error. + /* Precaution: a zero page count on error return causes + * svc_rqst_release_pages() to release nothing. */ - rqstp->rq_respages = rqstp->rq_pages; rqstp->rq_next_page = rqstp->rq_respages; rqstp->rq_xprt_ctxt = NULL; @@ -962,7 +956,7 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) return 0; percpu_counter_inc(&svcrdma_stat_recv); - ib_dma_sync_single_for_cpu(rdma_xprt->sc_pd->device, + ib_dma_sync_single_for_cpu(rdma_xprt->sc_cm_id->device, ctxt->rc_recv_sge.addr, ctxt->rc_byte_len, DMA_FROM_DEVICE); svc_rdma_build_arg_xdr(rqstp, ctxt); diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index 4ec2f9ae06aa..402e2ceca4ff 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -251,6 +251,28 @@ static void svc_rdma_write_info_free(struct svc_rdma_write_info *info) queue_work(svcrdma_wq, &info->wi_work); } +/** + * svc_rdma_write_chunk_release - Release Write chunk I/O resources + * @rdma: controlling transport + * @ctxt: Send context that is being released + * + * Write chunk resources remain live until Send completion because + * Write WRs are chained to the Send WR. This function releases all + * write_info structures accumulated on @ctxt->sc_write_info_list. + */ +void svc_rdma_write_chunk_release(struct svcxprt_rdma *rdma, + struct svc_rdma_send_ctxt *ctxt) +{ + struct svc_rdma_write_info *info; + + while (!list_empty(&ctxt->sc_write_info_list)) { + info = list_first_entry(&ctxt->sc_write_info_list, + struct svc_rdma_write_info, wi_list); + list_del(&info->wi_list); + svc_rdma_write_info_free(info); + } +} + /** * svc_rdma_reply_chunk_release - Release Reply chunk I/O resources * @rdma: controlling transport @@ -307,13 +329,11 @@ static void svc_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc) struct ib_cqe *cqe = wc->wr_cqe; struct svc_rdma_chunk_ctxt *cc = container_of(cqe, struct svc_rdma_chunk_ctxt, cc_cqe); - struct svc_rdma_write_info *info = - container_of(cc, struct svc_rdma_write_info, wi_cc); switch (wc->status) { case IB_WC_SUCCESS: trace_svcrdma_wc_write(&cc->cc_cid); - break; + return; case IB_WC_WR_FLUSH_ERR: trace_svcrdma_wc_write_flush(wc, &cc->cc_cid); break; @@ -321,12 +341,11 @@ static void svc_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc) trace_svcrdma_wc_write_err(wc, &cc->cc_cid); } - svc_rdma_wake_send_waiters(rdma, cc->cc_sqecount); - - if (unlikely(wc->status != IB_WC_SUCCESS)) - svc_xprt_deferred_close(&rdma->sc_xprt); - - svc_rdma_write_info_free(info); + /* The RDMA Write has flushed, so the client won't get + * some of the outgoing RPC message. Signal the loss + * to the client by closing the connection. + */ + svc_xprt_deferred_close(&rdma->sc_xprt); } /** @@ -405,34 +424,17 @@ static int svc_rdma_post_chunk_ctxt(struct svcxprt_rdma *rdma, cqe = NULL; } - do { - if (atomic_sub_return(cc->cc_sqecount, - &rdma->sc_sq_avail) > 0) { - cc->cc_posttime = ktime_get(); - ret = ib_post_send(rdma->sc_qp, first_wr, &bad_wr); - if (ret) - break; - return 0; - } + ret = svc_rdma_sq_wait(rdma, &cc->cc_cid, cc->cc_sqecount); + if (ret < 0) + return ret; - percpu_counter_inc(&svcrdma_stat_sq_starve); - trace_svcrdma_sq_full(rdma, &cc->cc_cid); - atomic_add(cc->cc_sqecount, &rdma->sc_sq_avail); - wait_event(rdma->sc_send_wait, - atomic_read(&rdma->sc_sq_avail) > cc->cc_sqecount); - trace_svcrdma_sq_retry(rdma, &cc->cc_cid); - } while (1); - - trace_svcrdma_sq_post_err(rdma, &cc->cc_cid, ret); - svc_xprt_deferred_close(&rdma->sc_xprt); - - /* If even one was posted, there will be a completion. */ - if (bad_wr != first_wr) - return 0; - - atomic_add(cc->cc_sqecount, &rdma->sc_sq_avail); - wake_up(&rdma->sc_send_wait); - return -ENOTCONN; + cc->cc_posttime = ktime_get(); + ret = ib_post_send(rdma->sc_qp, first_wr, &bad_wr); + if (ret) + return svc_rdma_post_send_err(rdma, &cc->cc_cid, bad_wr, + first_wr, cc->cc_sqecount, + ret); + return 0; } /* Build a bvec that covers one kvec in an xdr_buf. @@ -617,9 +619,37 @@ static int svc_rdma_xb_write(const struct xdr_buf *xdr, void *data) return xdr->len; } -static int svc_rdma_send_write_chunk(struct svcxprt_rdma *rdma, - const struct svc_rdma_chunk *chunk, - const struct xdr_buf *xdr) +/* Link chunk WRs onto @sctxt's WR chain. Completion is requested + * for the tail WR, which is posted first. + */ +static void svc_rdma_cc_link_wrs(struct svcxprt_rdma *rdma, + struct svc_rdma_send_ctxt *sctxt, + struct svc_rdma_chunk_ctxt *cc) +{ + struct ib_send_wr *first_wr; + struct list_head *pos; + struct ib_cqe *cqe; + + first_wr = sctxt->sc_wr_chain; + cqe = &cc->cc_cqe; + list_for_each(pos, &cc->cc_rwctxts) { + struct svc_rdma_rw_ctxt *rwc; + + rwc = list_entry(pos, struct svc_rdma_rw_ctxt, rw_list); + first_wr = rdma_rw_ctx_wrs(&rwc->rw_ctx, rdma->sc_qp, + rdma->sc_port_num, cqe, first_wr); + cqe = NULL; + } + sctxt->sc_wr_chain = first_wr; + sctxt->sc_sqecount += cc->cc_sqecount; +} + +/* Link Write WRs for @chunk onto @sctxt's WR chain. + */ +static int svc_rdma_prepare_write_chunk(struct svcxprt_rdma *rdma, + struct svc_rdma_send_ctxt *sctxt, + const struct svc_rdma_chunk *chunk, + const struct xdr_buf *xdr) { struct svc_rdma_write_info *info; struct svc_rdma_chunk_ctxt *cc; @@ -639,10 +669,14 @@ static int svc_rdma_send_write_chunk(struct svcxprt_rdma *rdma, if (ret != payload.len) goto out_err; - trace_svcrdma_post_write_chunk(&cc->cc_cid, cc->cc_sqecount); - ret = svc_rdma_post_chunk_ctxt(rdma, cc); - if (ret < 0) + ret = -EINVAL; + if (unlikely(sctxt->sc_sqecount + cc->cc_sqecount > rdma->sc_sq_depth)) goto out_err; + + svc_rdma_cc_link_wrs(rdma, sctxt, cc); + list_add(&info->wi_list, &sctxt->sc_write_info_list); + + trace_svcrdma_post_write_chunk(&cc->cc_cid, cc->cc_sqecount); return 0; out_err: @@ -651,17 +685,19 @@ out_err: } /** - * svc_rdma_send_write_list - Send all chunks on the Write list + * svc_rdma_prepare_write_list - Construct WR chain for sending Write list * @rdma: controlling RDMA transport * @rctxt: Write list provisioned by the client + * @sctxt: Send WR resources * @xdr: xdr_buf containing an RPC Reply message * - * Returns zero on success, or a negative errno if one or more - * Write chunks could not be sent. + * Returns zero on success, or a negative errno if WR chain + * construction fails for one or more Write chunks. */ -int svc_rdma_send_write_list(struct svcxprt_rdma *rdma, - const struct svc_rdma_recv_ctxt *rctxt, - const struct xdr_buf *xdr) +int svc_rdma_prepare_write_list(struct svcxprt_rdma *rdma, + const struct svc_rdma_recv_ctxt *rctxt, + struct svc_rdma_send_ctxt *sctxt, + const struct xdr_buf *xdr) { struct svc_rdma_chunk *chunk; int ret; @@ -669,7 +705,7 @@ int svc_rdma_send_write_list(struct svcxprt_rdma *rdma, pcl_for_each_chunk(chunk, &rctxt->rc_write_pcl) { if (!chunk->ch_payload_length) break; - ret = svc_rdma_send_write_chunk(rdma, chunk, xdr); + ret = svc_rdma_prepare_write_chunk(rdma, sctxt, chunk, xdr); if (ret < 0) return ret; } @@ -699,9 +735,6 @@ int svc_rdma_prepare_reply_chunk(struct svcxprt_rdma *rdma, { struct svc_rdma_write_info *info = &sctxt->sc_reply_info; struct svc_rdma_chunk_ctxt *cc = &info->wi_cc; - struct ib_send_wr *first_wr; - struct list_head *pos; - struct ib_cqe *cqe; int ret; info->wi_rdma = rdma; @@ -715,23 +748,222 @@ int svc_rdma_prepare_reply_chunk(struct svcxprt_rdma *rdma, if (ret < 0) return ret; - first_wr = sctxt->sc_wr_chain; - cqe = &cc->cc_cqe; - list_for_each(pos, &cc->cc_rwctxts) { - struct svc_rdma_rw_ctxt *rwc; - - rwc = list_entry(pos, struct svc_rdma_rw_ctxt, rw_list); - first_wr = rdma_rw_ctx_wrs(&rwc->rw_ctx, rdma->sc_qp, - rdma->sc_port_num, cqe, first_wr); - cqe = NULL; - } - sctxt->sc_wr_chain = first_wr; - sctxt->sc_sqecount += cc->cc_sqecount; + svc_rdma_cc_link_wrs(rdma, sctxt, cc); trace_svcrdma_post_reply_chunk(&cc->cc_cid, cc->cc_sqecount); return xdr->len; } +/* + * Cap contiguous RDMA Read sink allocations at order-4. + * Higher orders risk allocation failure under + * __GFP_NORETRY, which would negate the benefit of the + * contiguous fast path. + */ +#define SVC_RDMA_CONTIG_MAX_ORDER 4 + +/** + * svc_rdma_alloc_read_pages - Allocate physically contiguous pages + * @nr_pages: number of pages needed + * @order: on success, set to the allocation order + * + * Attempts a higher-order allocation, falling back to smaller orders. + * The returned pages are split immediately so each sub-page has its + * own refcount and can be freed independently. + * + * Returns a pointer to the first page on success, or NULL if even + * order-1 allocation fails. + */ +static struct page * +svc_rdma_alloc_read_pages(unsigned int nr_pages, unsigned int *order) +{ + unsigned int o; + struct page *page; + + o = min(get_order(nr_pages << PAGE_SHIFT), + SVC_RDMA_CONTIG_MAX_ORDER); + + while (o >= 1) { + page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN, + o); + if (page) { + split_page(page, o); + *order = o; + return page; + } + o--; + } + return NULL; +} + +/* + * svc_rdma_fill_contig_bvec - Replace rq_pages with a contiguous allocation + * @rqstp: RPC transaction context + * @head: context for ongoing I/O + * @bv: bvec entry to fill + * @pages_left: number of data pages remaining in the segment + * @len_left: bytes remaining in the segment + * + * On success, fills @bv with a bvec spanning the contiguous range and + * advances rc_curpage/rc_page_count. Returns the byte length covered, + * or zero if the allocation failed or would overrun rq_maxpages. + */ +static unsigned int +svc_rdma_fill_contig_bvec(struct svc_rqst *rqstp, + struct svc_rdma_recv_ctxt *head, + struct bio_vec *bv, unsigned int pages_left, + unsigned int len_left) +{ + unsigned int order, npages, chunk_pages, chunk_len, i; + struct page *page; + + page = svc_rdma_alloc_read_pages(pages_left, &order); + if (!page) + return 0; + npages = 1 << order; + + if (head->rc_curpage + npages > rqstp->rq_maxpages) { + for (i = 0; i < npages; i++) + __free_page(page + i); + return 0; + } + + /* + * Replace rq_pages[] entries with pages from the contiguous + * allocation. If npages exceeds chunk_pages, the extra pages + * stay in rq_pages[] for later reuse or normal rqst teardown. + */ + for (i = 0; i < npages; i++) { + svc_rqst_page_release(rqstp, + rqstp->rq_pages[head->rc_curpage + i]); + rqstp->rq_pages[head->rc_curpage + i] = page + i; + } + + chunk_pages = min(npages, pages_left); + chunk_len = min_t(unsigned int, chunk_pages << PAGE_SHIFT, len_left); + bvec_set_page(bv, page, chunk_len, 0); + head->rc_page_count += chunk_pages; + head->rc_curpage += chunk_pages; + return chunk_len; +} + +/* + * svc_rdma_fill_page_bvec - Add a single rq_page to the bvec array + * @head: context for ongoing I/O + * @ctxt: R/W context whose bvec array is being filled + * @cur: page to add + * @bvec_idx: pointer to current bvec index, not advanced on merge + * @len_left: bytes remaining in the segment + * + * If @cur is physically contiguous with the preceding bvec, it is + * merged by extending that bvec's length. Otherwise a new bvec + * entry is created. Returns the byte length covered. + */ +static unsigned int +svc_rdma_fill_page_bvec(struct svc_rdma_recv_ctxt *head, + struct svc_rdma_rw_ctxt *ctxt, struct page *cur, + unsigned int *bvec_idx, unsigned int len_left) +{ + unsigned int chunk_len = min_t(unsigned int, PAGE_SIZE, len_left); + + head->rc_page_count++; + head->rc_curpage++; + + if (*bvec_idx > 0) { + struct bio_vec *prev = &ctxt->rw_bvec[*bvec_idx - 1]; + + if (page_to_phys(prev->bv_page) + prev->bv_offset + + prev->bv_len == page_to_phys(cur)) { + prev->bv_len += chunk_len; + return chunk_len; + } + } + + bvec_set_page(&ctxt->rw_bvec[*bvec_idx], cur, chunk_len, 0); + (*bvec_idx)++; + return chunk_len; +} + +/** + * svc_rdma_build_read_segment_contig - Build RDMA Read WR with contiguous pages + * @rqstp: RPC transaction context + * @head: context for ongoing I/O + * @segment: co-ordinates of remote memory to be read + * + * Greedily allocates higher-order pages to cover the segment, + * building one bvec per contiguous chunk. Each allocation is + * split so sub-pages have independent refcounts. When a + * higher-order allocation fails, remaining pages are covered + * individually, merging adjacent pages into the preceding bvec + * when they are physically contiguous. The split sub-pages + * replace entries in rq_pages[] so downstream cleanup is + * unchanged. + * + * Returns: + * %0: the Read WR was constructed successfully + * %-ENOMEM: allocation failed + * %-EIO: a DMA mapping error occurred + */ +static int svc_rdma_build_read_segment_contig(struct svc_rqst *rqstp, + struct svc_rdma_recv_ctxt *head, + const struct svc_rdma_segment *segment) +{ + struct svcxprt_rdma *rdma = svc_rdma_rqst_rdma(rqstp); + struct svc_rdma_chunk_ctxt *cc = &head->rc_cc; + unsigned int nr_data_pages, bvec_idx; + struct svc_rdma_rw_ctxt *ctxt; + unsigned int len_left; + int ret; + + nr_data_pages = PAGE_ALIGN(segment->rs_length) >> PAGE_SHIFT; + if (head->rc_curpage + nr_data_pages > rqstp->rq_maxpages) + return -ENOMEM; + + ctxt = svc_rdma_get_rw_ctxt(rdma, nr_data_pages); + if (!ctxt) + return -ENOMEM; + + bvec_idx = 0; + len_left = segment->rs_length; + while (len_left) { + unsigned int pages_left = PAGE_ALIGN(len_left) >> PAGE_SHIFT; + unsigned int chunk_len = 0; + + if (pages_left >= 2) + chunk_len = svc_rdma_fill_contig_bvec(rqstp, head, + &ctxt->rw_bvec[bvec_idx], + pages_left, len_left); + if (chunk_len) { + bvec_idx++; + } else { + struct page *cur = + rqstp->rq_pages[head->rc_curpage]; + chunk_len = svc_rdma_fill_page_bvec(head, ctxt, cur, + &bvec_idx, + len_left); + } + + len_left -= chunk_len; + } + + ctxt->rw_nents = bvec_idx; + + head->rc_pageoff = offset_in_page(segment->rs_length); + if (head->rc_pageoff) + head->rc_curpage--; + + ret = svc_rdma_rw_ctx_init(rdma, ctxt, segment->rs_offset, + segment->rs_handle, segment->rs_length, + DMA_FROM_DEVICE); + if (ret < 0) + return -EIO; + percpu_counter_inc(&svcrdma_stat_read); + + list_add(&ctxt->rw_list, &cc->cc_rwctxts); + cc->cc_sqecount += ret; + return 0; +} + /** * svc_rdma_build_read_segment - Build RDMA Read WQEs to pull one RDMA segment * @rqstp: RPC transaction context @@ -758,6 +990,14 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp, if (check_add_overflow(head->rc_pageoff, len, &total)) return -EINVAL; nr_bvec = PAGE_ALIGN(total) >> PAGE_SHIFT; + + if (head->rc_pageoff == 0 && nr_bvec >= 2) { + ret = svc_rdma_build_read_segment_contig(rqstp, head, + segment); + if (ret != -ENOMEM) + return ret; + } + ctxt = svc_rdma_get_rw_ctxt(rdma, nr_bvec); if (!ctxt) return -ENOMEM; @@ -1103,10 +1343,16 @@ static void svc_rdma_clear_rqst_pages(struct svc_rqst *rqstp, { unsigned int i; + /* + * Move only pages containing RPC data into rc_pages[]. Pages + * from a contiguous allocation that were not used for the + * payload remain in rq_pages[] for subsequent reuse. + */ for (i = 0; i < head->rc_page_count; i++) { head->rc_pages[i] = rqstp->rq_pages[i]; rqstp->rq_pages[i] = NULL; } + rqstp->rq_pages_nfree = head->rc_page_count; } /** diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index 914cd263c2f1..8b3f0c8c14b2 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -116,7 +116,8 @@ static void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc); static struct svc_rdma_send_ctxt * svc_rdma_send_ctxt_alloc(struct svcxprt_rdma *rdma) { - int node = ibdev_to_node(rdma->sc_cm_id->device); + struct ib_device *device = rdma->sc_cm_id->device; + int node = ibdev_to_node(device); struct svc_rdma_send_ctxt *ctxt; unsigned long pages; dma_addr_t addr; @@ -136,9 +137,9 @@ svc_rdma_send_ctxt_alloc(struct svcxprt_rdma *rdma) buffer = kmalloc_node(rdma->sc_max_req_size, GFP_KERNEL, node); if (!buffer) goto fail2; - addr = ib_dma_map_single(rdma->sc_pd->device, buffer, - rdma->sc_max_req_size, DMA_TO_DEVICE); - if (ib_dma_mapping_error(rdma->sc_pd->device, addr)) + addr = ib_dma_map_single(device, buffer, rdma->sc_max_req_size, + DMA_TO_DEVICE); + if (ib_dma_mapping_error(device, addr)) goto fail3; svc_rdma_send_cid_init(rdma, &ctxt->sc_cid); @@ -149,6 +150,7 @@ svc_rdma_send_ctxt_alloc(struct svcxprt_rdma *rdma) ctxt->sc_send_wr.sg_list = ctxt->sc_sges; ctxt->sc_send_wr.send_flags = IB_SEND_SIGNALED; ctxt->sc_cqe.done = svc_rdma_wc_send; + INIT_LIST_HEAD(&ctxt->sc_write_info_list); ctxt->sc_xprt_buf = buffer; xdr_buf_init(&ctxt->sc_hdrbuf, ctxt->sc_xprt_buf, rdma->sc_max_req_size); @@ -175,15 +177,14 @@ fail0: */ void svc_rdma_send_ctxts_destroy(struct svcxprt_rdma *rdma) { + struct ib_device *device = rdma->sc_cm_id->device; struct svc_rdma_send_ctxt *ctxt; struct llist_node *node; while ((node = llist_del_first(&rdma->sc_send_ctxts)) != NULL) { ctxt = llist_entry(node, struct svc_rdma_send_ctxt, sc_node); - ib_dma_unmap_single(rdma->sc_pd->device, - ctxt->sc_sges[0].addr, - rdma->sc_max_req_size, - DMA_TO_DEVICE); + ib_dma_unmap_single(device, ctxt->sc_sges[0].addr, + rdma->sc_max_req_size, DMA_TO_DEVICE); kfree(ctxt->sc_xprt_buf); kfree(ctxt->sc_pages); kfree(ctxt); @@ -237,6 +238,7 @@ static void svc_rdma_send_ctxt_release(struct svcxprt_rdma *rdma, struct ib_device *device = rdma->sc_cm_id->device; unsigned int i; + svc_rdma_write_chunk_release(rdma, ctxt); svc_rdma_reply_chunk_release(rdma, ctxt); if (ctxt->sc_page_count) @@ -294,6 +296,117 @@ void svc_rdma_wake_send_waiters(struct svcxprt_rdma *rdma, int avail) wake_up(&rdma->sc_send_wait); } +/** + * svc_rdma_sq_wait - Wait for SQ slots using fair queuing + * @rdma: controlling transport + * @cid: completion ID for tracing + * @sqecount: number of SQ entries needed + * + * A ticket-based system ensures fair ordering when multiple threads + * wait for Send Queue capacity. Each waiter takes a ticket and is + * served in order, preventing starvation. + * + * Protocol invariant: every ticket holder must increment + * sc_sq_ticket_tail exactly once, whether the reservation + * succeeds or the connection closes. Failing to advance the + * tail stalls all subsequent waiters. + * + * The ticket counters are signed 32-bit atomics. After + * wrapping through INT_MAX, the equality check + * (tail == ticket) remains correct because both counters + * advance monotonically and the comparison uses exact + * equality rather than relational operators. + * + * Return values: + * %0: SQ slots were reserved successfully + * %-ENOTCONN: The connection was lost + */ +int svc_rdma_sq_wait(struct svcxprt_rdma *rdma, + const struct rpc_rdma_cid *cid, int sqecount) +{ + int ticket; + + /* Fast path: try to reserve SQ slots without waiting. + * + * A failed reservation temporarily understates sc_sq_avail + * until the compensating atomic_add restores it. A Send + * completion arriving in that window sees a lower count + * than reality, but the value self-corrects once the add + * completes. No ordering guarantee is needed here because + * the slow path serializes all contended waiters. + */ + if (likely(atomic_sub_return(sqecount, &rdma->sc_sq_avail) >= 0)) + return 0; + atomic_add(sqecount, &rdma->sc_sq_avail); + + /* Slow path: take a ticket and wait in line */ + ticket = atomic_fetch_inc(&rdma->sc_sq_ticket_head); + + percpu_counter_inc(&svcrdma_stat_sq_starve); + trace_svcrdma_sq_full(rdma, cid); + + /* Wait until all earlier tickets have been served */ + wait_event(rdma->sc_sq_ticket_wait, + test_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags) || + atomic_read(&rdma->sc_sq_ticket_tail) == ticket); + if (test_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags)) + goto out_close; + + /* It's our turn. Wait for enough SQ slots to be available. */ + while (atomic_sub_return(sqecount, &rdma->sc_sq_avail) < 0) { + atomic_add(sqecount, &rdma->sc_sq_avail); + + wait_event(rdma->sc_send_wait, + test_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags) || + atomic_read(&rdma->sc_sq_avail) >= sqecount); + if (test_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags)) + goto out_close; + } + + /* Slots reserved successfully. Let the next waiter proceed. */ + atomic_inc(&rdma->sc_sq_ticket_tail); + wake_up(&rdma->sc_sq_ticket_wait); + trace_svcrdma_sq_retry(rdma, cid); + return 0; + +out_close: + atomic_inc(&rdma->sc_sq_ticket_tail); + wake_up(&rdma->sc_sq_ticket_wait); + return -ENOTCONN; +} + +/** + * svc_rdma_post_send_err - Handle ib_post_send failure + * @rdma: controlling transport + * @cid: completion ID for tracing + * @bad_wr: first WR that was not posted + * @first_wr: first WR in the chain + * @sqecount: number of SQ entries that were reserved + * @ret: error code from ib_post_send + * + * Return values: + * %0: At least one WR was posted; a completion handles cleanup + * %-ENOTCONN: No WRs were posted; SQ slots are released + */ +int svc_rdma_post_send_err(struct svcxprt_rdma *rdma, + const struct rpc_rdma_cid *cid, + const struct ib_send_wr *bad_wr, + const struct ib_send_wr *first_wr, + int sqecount, int ret) +{ + trace_svcrdma_sq_post_err(rdma, cid, ret); + svc_xprt_deferred_close(&rdma->sc_xprt); + + /* If even one WR was posted, a Send completion will + * return the reserved SQ slots. + */ + if (bad_wr != first_wr) + return 0; + + svc_rdma_wake_send_waiters(rdma, sqecount); + return -ENOTCONN; +} + /** * svc_rdma_wc_send - Invoked by RDMA provider for each polled Send WC * @cq: Completion Queue context @@ -336,11 +449,6 @@ flushed: * that these values remain available after the ib_post_send() call. * In some error flow cases, svc_rdma_wc_send() releases @ctxt. * - * Note there is potential for starvation when the Send Queue is - * full because there is no order to when waiting threads are - * awoken. The transport is typically provisioned with a deep - * enough Send Queue that SQ exhaustion should be a rare event. - * * Return values: * %0: @ctxt's WR chain was posted successfully * %-ENOTCONN: The connection was lost @@ -357,47 +465,21 @@ int svc_rdma_post_send(struct svcxprt_rdma *rdma, might_sleep(); /* Sync the transport header buffer */ - ib_dma_sync_single_for_device(rdma->sc_pd->device, + ib_dma_sync_single_for_device(rdma->sc_cm_id->device, send_wr->sg_list[0].addr, send_wr->sg_list[0].length, DMA_TO_DEVICE); - /* If the SQ is full, wait until an SQ entry is available */ - while (!test_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags)) { - if (atomic_sub_return(sqecount, &rdma->sc_sq_avail) < 0) { - svc_rdma_wake_send_waiters(rdma, sqecount); + ret = svc_rdma_sq_wait(rdma, &cid, sqecount); + if (ret < 0) + return ret; - /* When the transport is torn down, assume - * ib_drain_sq() will trigger enough Send - * completions to wake us. The XPT_CLOSE test - * above should then cause the while loop to - * exit. - */ - percpu_counter_inc(&svcrdma_stat_sq_starve); - trace_svcrdma_sq_full(rdma, &cid); - wait_event(rdma->sc_send_wait, - atomic_read(&rdma->sc_sq_avail) > 0); - trace_svcrdma_sq_retry(rdma, &cid); - continue; - } - - trace_svcrdma_post_send(ctxt); - ret = ib_post_send(rdma->sc_qp, first_wr, &bad_wr); - if (ret) { - trace_svcrdma_sq_post_err(rdma, &cid, ret); - svc_xprt_deferred_close(&rdma->sc_xprt); - - /* If even one WR was posted, there will be a - * Send completion that bumps sc_sq_avail. - */ - if (bad_wr == first_wr) { - svc_rdma_wake_send_waiters(rdma, sqecount); - break; - } - } - return 0; - } - return -ENOTCONN; + trace_svcrdma_post_send(ctxt); + ret = ib_post_send(rdma->sc_qp, first_wr, &bad_wr); + if (ret) + return svc_rdma_post_send_err(rdma, &cid, bad_wr, + first_wr, sqecount, ret); + return 0; } /** @@ -858,7 +940,8 @@ int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, /* The svc_rqst and all resources it owns are released as soon as * svc_rdma_sendto returns. Transfer pages under I/O to the ctxt - * so they are released by the Send completion handler. + * so they are released only after Send completion, and not by + * svc_rqst_release_pages(). */ static void svc_rdma_save_io_pages(struct svc_rqst *rqstp, struct svc_rdma_send_ctxt *ctxt) @@ -870,9 +953,6 @@ static void svc_rdma_save_io_pages(struct svc_rqst *rqstp, ctxt->sc_pages[i] = rqstp->rq_respages[i]; rqstp->rq_respages[i] = NULL; } - - /* Prevent svc_xprt_release from releasing pages in rq_pages */ - rqstp->rq_next_page = rqstp->rq_respages; } /* Prepare the portion of the RPC Reply that will be transmitted @@ -976,6 +1056,12 @@ void svc_rdma_send_error_msg(struct svcxprt_rdma *rdma, sctxt->sc_send_wr.num_sge = 1; sctxt->sc_send_wr.opcode = IB_WR_SEND; sctxt->sc_sges[0].length = sctxt->sc_hdrbuf.len; + + /* Ensure only the error message is posted, not any previously + * prepared Write chunk WRs. + */ + sctxt->sc_wr_chain = &sctxt->sc_send_wr; + sctxt->sc_sqecount = 1; if (svc_rdma_post_send(rdma, sctxt)) goto put_ctxt; return; @@ -1023,7 +1109,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) if (!p) goto put_ctxt; - ret = svc_rdma_send_write_list(rdma, rctxt, &rqstp->rq_res); + ret = svc_rdma_prepare_write_list(rdma, rctxt, sctxt, &rqstp->rq_res); if (ret < 0) goto put_ctxt; diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 9b623849723e..f18bc60d9f4f 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -179,6 +179,7 @@ static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv, init_llist_head(&cma_xprt->sc_recv_ctxts); init_llist_head(&cma_xprt->sc_rw_ctxts); init_waitqueue_head(&cma_xprt->sc_send_wait); + init_waitqueue_head(&cma_xprt->sc_sq_ticket_wait); spin_lock_init(&cma_xprt->sc_lock); spin_lock_init(&cma_xprt->sc_rq_dto_lock); @@ -414,7 +415,6 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) struct ib_qp_init_attr qp_attr; struct ib_device *dev; int ret = 0; - RPC_IFDEBUG(struct sockaddr *sap); listen_rdma = container_of(xprt, struct svcxprt_rdma, sc_xprt); clear_bit(XPT_CONN, &xprt->xpt_flags); @@ -478,6 +478,8 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) if (newxprt->sc_sq_depth > dev->attrs.max_qp_wr) newxprt->sc_sq_depth = dev->attrs.max_qp_wr; atomic_set(&newxprt->sc_sq_avail, newxprt->sc_sq_depth); + atomic_set(&newxprt->sc_sq_ticket_head, 0); + atomic_set(&newxprt->sc_sq_ticket_tail, 0); newxprt->sc_pd = ib_alloc_pd(dev, 0); if (IS_ERR(newxprt->sc_pd)) { @@ -560,18 +562,20 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) goto errout; } -#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) - dprintk("svcrdma: new connection accepted on device %s:\n", dev->name); - sap = (struct sockaddr *)&newxprt->sc_cm_id->route.addr.src_addr; - dprintk(" local address : %pIS:%u\n", sap, rpc_get_port(sap)); - sap = (struct sockaddr *)&newxprt->sc_cm_id->route.addr.dst_addr; - dprintk(" remote address : %pIS:%u\n", sap, rpc_get_port(sap)); - dprintk(" max_sge : %d\n", newxprt->sc_max_send_sges); - dprintk(" sq_depth : %d\n", newxprt->sc_sq_depth); - dprintk(" rdma_rw_ctxs : %d\n", ctxts); - dprintk(" max_requests : %d\n", newxprt->sc_max_requests); - dprintk(" ord : %d\n", conn_param.initiator_depth); -#endif + if (IS_ENABLED(CONFIG_SUNRPC_DEBUG)) { + struct sockaddr *sap; + + dprintk("svcrdma: new connection accepted on device %s:\n", dev->name); + sap = (struct sockaddr *)&newxprt->sc_cm_id->route.addr.src_addr; + dprintk(" local address : %pIS:%u\n", sap, rpc_get_port(sap)); + sap = (struct sockaddr *)&newxprt->sc_cm_id->route.addr.dst_addr; + dprintk(" remote address : %pIS:%u\n", sap, rpc_get_port(sap)); + dprintk(" max_sge : %d\n", newxprt->sc_max_send_sges); + dprintk(" sq_depth : %d\n", newxprt->sc_sq_depth); + dprintk(" rdma_rw_ctxs : %d\n", ctxts); + dprintk(" max_requests : %d\n", newxprt->sc_max_requests); + dprintk(" ord : %d\n", conn_param.initiator_depth); + } return &newxprt->sc_xprt; @@ -648,7 +652,8 @@ static int svc_rdma_has_wspace(struct svc_xprt *xprt) * If there are already waiters on the SQ, * return false. */ - if (waitqueue_active(&rdma->sc_send_wait)) + if (waitqueue_active(&rdma->sc_send_wait) || + waitqueue_active(&rdma->sc_sq_ticket_wait)) return 0; /* Otherwise return true. */ diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 9a8ce5df83ca..61706df5e485 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -510,8 +510,21 @@ xprt_rdma_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) return; out_sleep: - task->tk_status = -ENOMEM; - xprt_add_backlog(xprt, task); + task->tk_status = -EAGAIN; + xprt_add_backlog_noncongested(xprt, task); + /* A buffer freed between buffer_get and rpc_sleep_on + * goes back to the pool with no waiter to wake. + * Re-check after joining the backlog to close that gap. + */ + req = rpcrdma_buffer_get(&r_xprt->rx_buf); + if (req) { + struct rpc_rqst *rqst = &req->rl_slot; + + if (!xprt_wake_up_backlog(xprt, rqst)) { + memset(rqst, 0, sizeof(*rqst)); + rpcrdma_buffer_put(&r_xprt->rx_buf, req); + } + } } /** diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index b51a162885bb..aecf9c0a153f 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -708,6 +708,18 @@ out_emptyq: */ xprt_wait_for_buffer_space(&r_xprt->rx_xprt); r_xprt->rx_stats.empty_sendctx_q++; + + /* Recheck: a Send completion between the ring-empty test + * and the set_bit could cause its xprt_write_space() to + * miss, leaving XPRT_WRITE_SPACE set with a non-full ring. + * The smp_mb__after_atomic() pairs with smp_store_release() + * in rpcrdma_sendctx_put_locked(). + */ + smp_mb__after_atomic(); + next_head = rpcrdma_sendctx_next(buf, buf->rb_sc_head); + if (next_head != READ_ONCE(buf->rb_sc_tail)) + xprt_write_space(&r_xprt->rx_xprt); + return NULL; } @@ -739,7 +751,10 @@ static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt, } while (buf->rb_sc_ctxs[next_tail] != sc); - /* Paired with READ_ONCE */ + /* Paired with READ_ONCE in rpcrdma_sendctx_get_locked(): + * both the fast-path ring-full test and the post-set_bit + * recheck in the slow path depend on this store-release. + */ smp_store_release(&buf->rb_sc_tail, next_tail); xprt_write_space(&r_xprt->rx_xprt); @@ -1359,7 +1374,7 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed) if (likely(ep->re_receive_count > needed)) goto out; needed -= ep->re_receive_count; - needed += RPCRDMA_MAX_RECV_BATCH; + needed += ep->re_recv_batch; if (atomic_inc_return(&ep->re_receiving) > 1) goto out_dec; diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index 8147d2b41494..f53a77472724 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -96,6 +96,7 @@ struct rpcrdma_ep { struct rpcrdma_notification re_rn; int re_receive_count; unsigned int re_max_requests; /* depends on device */ + unsigned int re_recv_batch; unsigned int re_inline_send; /* negotiated */ unsigned int re_inline_recv; /* negotiated */ @@ -283,19 +284,36 @@ struct rpcrdma_mr { * registered or invalidated. Must handle a Reply chunk: */ enum { - RPCRDMA_MAX_IOV_SEGS = 3, + RPCRDMA_MAX_IOV_SEGS = 3, /* head, page-boundary, tail */ RPCRDMA_MAX_DATA_SEGS = ((1 * 1024 * 1024) / PAGE_SIZE) + 1, RPCRDMA_MAX_SEGS = RPCRDMA_MAX_DATA_SEGS + RPCRDMA_MAX_IOV_SEGS, }; -/* Arguments for DMA mapping and registration */ -struct rpcrdma_mr_seg { - u32 mr_len; /* length of segment */ - struct page *mr_page; /* underlying struct page */ - u64 mr_offset; /* IN: page offset, OUT: iova */ +/** + * struct rpcrdma_xdr_cursor - tracks position within an xdr_buf + * for iterative MR registration + * @xc_buf: the xdr_buf being iterated + * @xc_page_offset: byte offset into the page region consumed so far + * @xc_flags: combination of XC_* bits + * + * Each XC_*_DONE flag indicates that this region has no + * remaining MR registration work. That condition holds both when the region + * has already been registered by a prior frwr_map() call and + * when the region is excluded from this chunk type (pre-set + * at init time by rpcrdma_xdr_cursor_init()). frwr_map() + * treats the two cases identically: skip the region. + */ +struct rpcrdma_xdr_cursor { + const struct xdr_buf *xc_buf; + unsigned int xc_page_offset; + unsigned int xc_flags; }; +#define XC_HEAD_DONE BIT(0) +#define XC_PAGES_DONE BIT(1) +#define XC_TAIL_DONE BIT(2) + /* The Send SGE array is provisioned to send a maximum size * inline request: * - RPC-over-RDMA header @@ -330,7 +348,6 @@ struct rpcrdma_req { struct list_head rl_free_mrs; struct list_head rl_registered; - struct rpcrdma_mr_seg rl_segments[RPCRDMA_MAX_SEGS]; }; static inline struct rpcrdma_req * @@ -450,8 +467,8 @@ rpcrdma_portstr(const struct rpcrdma_xprt *r_xprt) } /* Setting this to 0 ensures interoperability with early servers. - * Setting this to 1 enhances certain unaligned read/write performance. - * Default is 0, see sysctl entry and rpc_rdma.c rpcrdma_convert_iovs() */ + * Setting this to 1 enhances unaligned read/write performance. + * Default is 0, see sysctl entry and rpc_rdma.c */ extern int xprt_rdma_pad_optimize; /* This setting controls the hunt for a supported memory @@ -535,10 +552,10 @@ void frwr_reset(struct rpcrdma_req *req); int frwr_query_device(struct rpcrdma_ep *ep, const struct ib_device *device); int frwr_mr_init(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr *mr); void frwr_mr_release(struct rpcrdma_mr *mr); -struct rpcrdma_mr_seg *frwr_map(struct rpcrdma_xprt *r_xprt, - struct rpcrdma_mr_seg *seg, - int nsegs, bool writing, __be32 xid, - struct rpcrdma_mr *mr); +int frwr_map(struct rpcrdma_xprt *r_xprt, + struct rpcrdma_xdr_cursor *cur, + bool writing, __be32 xid, + struct rpcrdma_mr *mr); int frwr_send(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req); void frwr_reminv(struct rpcrdma_rep *rep, struct list_head *mrs); void frwr_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req); diff --git a/net/tipc/msg.c b/net/tipc/msg.c index 76284fc538eb..b0bba0feef56 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -177,8 +177,20 @@ int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf) if (fragid == LAST_FRAGMENT) { TIPC_SKB_CB(head)->validated = 0; - if (unlikely(!tipc_msg_validate(&head))) + + /* If the reassembled skb has been freed in + * tipc_msg_validate() because of an invalid truesize, + * then head will point to a newly allocated reassembled + * skb, while *headbuf points to freed reassembled skb. + * In such cases, correct *headbuf for freeing the newly + * allocated reassembled skb later. + */ + if (unlikely(!tipc_msg_validate(&head))) { + if (head != *headbuf) + *headbuf = head; goto err; + } + *buf = head; TIPC_SKB_CB(head)->tail = NULL; *headbuf = NULL; diff --git a/net/tls/tls.h b/net/tls/tls.h index e8f81a006520..12f44cb649c9 100644 --- a/net/tls/tls.h +++ b/net/tls/tls.h @@ -188,6 +188,7 @@ int tls_strp_dev_init(void); void tls_strp_dev_exit(void); void tls_strp_done(struct tls_strparser *strp); +void __tls_strp_done(struct tls_strparser *strp); void tls_strp_stop(struct tls_strparser *strp); int tls_strp_init(struct tls_strparser *strp, struct sock *sk); void tls_strp_data_ready(struct tls_strparser *strp); diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c index 98e12f0ff57e..c72e88317627 100644 --- a/net/tls/tls_strp.c +++ b/net/tls/tls_strp.c @@ -624,6 +624,12 @@ void tls_strp_done(struct tls_strparser *strp) WARN_ON(!strp->stopped); cancel_work_sync(&strp->work); + __tls_strp_done(strp); +} + +/* For setup error paths where the strparser was initialized but never armed. */ +void __tls_strp_done(struct tls_strparser *strp) +{ tls_strp_anchor_free(strp); } diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 94d2ae0daa8c..3bfdaf5e64f5 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -789,23 +789,33 @@ static int tls_push_record(struct sock *sk, int flags, i = msg_pl->sg.end; sk_msg_iter_var_prev(i); + /* msg_pl->sg.data is a ring; data[MAX+1] is reserved for the wrap + * link (frags won't use it). 'i' is now the last filled entry: + * + * i end start + * v v v [ rsv ] + * [ d ][ d ][ ][ ]...[ ][ d ][ d ][ d ][chain] + * ^ END v + * `-----------------------------------------' + * + * Note that SGL does not allow chain-after-chain, so for TLS 1.3, + * we must make sure we don't create the wrap entry and then chain + * link to content_type immediately at index 0. + */ + if (i < msg_pl->sg.start) + sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), + msg_pl->sg.data); + rec->content_type = record_type; if (prot->version == TLS_1_3_VERSION) { /* Add content type to end of message. No padding added */ sg_set_buf(&rec->sg_content_type, &rec->content_type, 1); sg_mark_end(&rec->sg_content_type); - sg_chain(msg_pl->sg.data, msg_pl->sg.end + 1, - &rec->sg_content_type); + sg_chain(msg_pl->sg.data, i + 2, &rec->sg_content_type); } else { sg_mark_end(sk_msg_elem(msg_pl, i)); } - if (msg_pl->sg.end < msg_pl->sg.start) { - sg_chain(&msg_pl->sg.data[msg_pl->sg.start], - MAX_SKB_FRAGS - msg_pl->sg.start + 1, - msg_pl->sg.data); - } - i = msg_pl->sg.start; sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); @@ -2317,9 +2327,9 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, if (copied < 0) goto splice_requeue; - if (chunk < rxm->full_len) { - rxm->offset += len; - rxm->full_len -= len; + if (copied < rxm->full_len) { + rxm->offset += copied; + rxm->full_len -= copied; goto splice_requeue; } @@ -2624,8 +2634,12 @@ void tls_sw_free_ctx_rx(struct tls_context *tls_ctx) void tls_sw_free_resources_rx(struct sock *sk) { struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_sw_context_rx *ctx; + + ctx = tls_sw_ctx_rx(tls_ctx); tls_sw_release_resources_rx(sk); + __tls_strp_done(&ctx->strp); tls_sw_free_ctx_rx(tls_ctx); } diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 4c4a8d23ddd2..1cbf36ea043b 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1968,16 +1968,19 @@ static void unix_peek_fds(struct scm_cookie *scm, struct sk_buff *skb) static void unix_destruct_scm(struct sk_buff *skb) { - struct scm_cookie scm; + struct scm_cookie scm = {}; + + swap(scm.pid, UNIXCB(skb).pid); - memset(&scm, 0, sizeof(scm)); - scm.pid = UNIXCB(skb).pid; if (UNIXCB(skb).fp) unix_detach_fds(&scm, skb); - /* Alas, it calls VFS */ - /* So fscking what? fput() had been SMP-safe since the last Summer */ scm_destroy(&scm); +} + +static void unix_wfree(struct sk_buff *skb) +{ + unix_destruct_scm(skb); sock_wfree(skb); } @@ -1993,7 +1996,7 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen if (scm->fp && send_fds) err = unix_attach_fds(scm, skb); - skb->destructor = unix_destruct_scm; + skb->destructor = unix_wfree; return err; } @@ -2070,6 +2073,13 @@ static void scm_stat_del(struct sock *sk, struct sk_buff *skb) } } +static void unix_orphan_scm(struct sock *sk, struct sk_buff *skb) +{ + scm_stat_del(sk, skb); + unix_destruct_scm(skb); + skb->destructor = sock_wfree; +} + /* * Send AF_UNIX data. */ @@ -2683,10 +2693,16 @@ static int unix_read_skb(struct sock *sk, skb_read_actor_t recv_actor) int err; mutex_lock(&u->iolock); + skb = skb_recv_datagram(sk, MSG_DONTWAIT, &err); - mutex_unlock(&u->iolock); - if (!skb) + if (!skb) { + mutex_unlock(&u->iolock); return err; + } + + unix_orphan_scm(sk, skb); + + mutex_unlock(&u->iolock); return recv_actor(sk, skb); } @@ -2886,6 +2902,9 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor) #endif spin_unlock(&queue->lock); + + unix_orphan_scm(sk, skb); + mutex_unlock(&u->iolock); return recv_actor(sk, skb); @@ -3304,6 +3323,9 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) struct sk_buff *skb; int answ = 0; + if (sk->sk_type != SOCK_STREAM) + return -EOPNOTSUPP; + mutex_lock(&u->iolock); skb = skb_peek(&sk->sk_receive_queue); @@ -3735,15 +3757,15 @@ static int bpf_iter_unix_seq_show(struct seq_file *seq, void *v) struct bpf_prog *prog; struct sock *sk = v; uid_t uid; - bool slow; int ret; if (v == SEQ_START_TOKEN) return 0; - slow = lock_sock_fast(sk); + lock_sock(sk); + unix_state_lock(sk); - if (unlikely(sk_unhashed(sk))) { + if (unlikely(sock_flag(sk, SOCK_DEAD))) { ret = SEQ_SKIP; goto unlock; } @@ -3753,7 +3775,8 @@ static int bpf_iter_unix_seq_show(struct seq_file *seq, void *v) prog = bpf_iter_get_info(&meta, false); ret = unix_prog_seq_show(prog, &meta, v, uid); unlock: - unlock_sock_fast(sk, slow); + unix_state_unlock(sk); + release_sock(sk); return ret; } diff --git a/net/unix/garbage.c b/net/unix/garbage.c index a7967a345827..0783555e2526 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -607,6 +607,8 @@ static void unix_gc(struct work_struct *work) struct sk_buff_head hitlist; struct sk_buff *skb; + WRITE_ONCE(gc_in_progress, true); + spin_lock(&unix_gc_lock); if (unix_graph_state == UNIX_GRAPH_NOT_CYCLIC) { @@ -649,10 +651,8 @@ void unix_schedule_gc(struct user_struct *user) READ_ONCE(user->unix_inflight) < UNIX_INFLIGHT_SANE_USER) return; - if (!READ_ONCE(gc_in_progress)) { - WRITE_ONCE(gc_in_progress, true); + if (!READ_ONCE(gc_in_progress)) queue_work(system_dfl_wq, &unix_gc_work); - } if (user && READ_ONCE(unix_graph_cyclic_sccs)) flush_work(&unix_gc_work); diff --git a/net/unix/unix_bpf.c b/net/unix/unix_bpf.c index d14cd5454a8d..f86ff19e9764 100644 --- a/net/unix/unix_bpf.c +++ b/net/unix/unix_bpf.c @@ -185,6 +185,9 @@ int unix_stream_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool r */ if (!psock->sk_pair) { sk_pair = unix_peer(sk); + if (unlikely(!sk_pair)) + return -EINVAL; + sock_hold(sk_pair); psock->sk_pair = sk_pair; } diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c index 2b7c0b5896ed..7a8963595bf9 100644 --- a/net/vmw_vsock/hyperv_transport.c +++ b/net/vmw_vsock/hyperv_transport.c @@ -375,10 +375,10 @@ static void hvs_open_connection(struct vmbus_channel *chan) } else { sndbuf = max_t(int, sk->sk_sndbuf, RINGBUFFER_HVS_SND_SIZE); sndbuf = min_t(int, sndbuf, RINGBUFFER_HVS_MAX_SIZE); - sndbuf = ALIGN(sndbuf, HV_HYP_PAGE_SIZE); + sndbuf = VMBUS_RING_SIZE(sndbuf); rcvbuf = max_t(int, sk->sk_rcvbuf, RINGBUFFER_HVS_RCV_SIZE); rcvbuf = min_t(int, rcvbuf, RINGBUFFER_HVS_MAX_SIZE); - rcvbuf = ALIGN(rcvbuf, HV_HYP_PAGE_SIZE); + rcvbuf = VMBUS_RING_SIZE(rcvbuf); } chan->max_pkt_size = HVS_MAX_PKT_SIZE; @@ -694,7 +694,6 @@ out: static s64 hvs_stream_has_data(struct vsock_sock *vsk) { struct hvsock *hvs = vsk->trans; - bool need_refill; s64 ret; if (hvs->recv_data_len > 0) @@ -702,9 +701,31 @@ static s64 hvs_stream_has_data(struct vsock_sock *vsk) switch (hvs_channel_readable_payload(hvs->chan)) { case 1: - need_refill = !hvs->recv_desc; - if (!need_refill) - return -EIO; + if (hvs->recv_desc) { + /* Here hvs->recv_data_len is 0, so hvs->recv_desc must + * be NULL unless it points to the 0-byte-payload FIN + * packet or a malformed/short packet: see + * hvs_update_recv_data(). + * + * If hvs->recv_desc points to the FIN packet, here all + * the payload has been dequeued and the peer_shutdown + * flag is set, but hvs_channel_readable_payload() still + * returns 1, because the VMBus ringbuffer's read_index + * is not updated for the FIN packet: + * hvs_stream_dequeue() -> hv_pkt_iter_next() updates + * the cached priv_read_index but has no opportunity to + * update the read_index in hv_pkt_iter_close() as + * hvs_stream_has_data() returns 0 for the FIN packet, + * so it won't get dequeued. + * + * In case hvs->recv_desc points to a malformed/short + * packet, return -EIO. + */ + if (!(vsk->peer_shutdown & SEND_SHUTDOWN)) + return -EIO; + + return 0; + } hvs->recv_desc = hv_pkt_iter_first(hvs->chan); if (!hvs->recv_desc) diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index a152a9e208d0..989cc252d3d3 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -73,6 +73,7 @@ static bool virtio_transport_can_zcopy(const struct virtio_transport *t_ops, static int virtio_transport_init_zcopy_skb(struct vsock_sock *vsk, struct sk_buff *skb, struct msghdr *msg, + size_t pkt_len, bool zerocopy) { struct ubuf_info *uarg; @@ -81,12 +82,10 @@ static int virtio_transport_init_zcopy_skb(struct vsock_sock *vsk, uarg = msg->msg_ubuf; net_zcopy_get(uarg); } else { - struct iov_iter *iter = &msg->msg_iter; struct ubuf_info_msgzc *uarg_zc; uarg = msg_zerocopy_realloc(sk_vsock(vsk), - iter->count, - NULL, false); + pkt_len, NULL, false); if (!uarg) return -1; @@ -137,27 +136,6 @@ static void virtio_transport_init_hdr(struct sk_buff *skb, hdr->fwd_cnt = cpu_to_le32(0); } -static void virtio_transport_copy_nonlinear_skb(const struct sk_buff *skb, - void *dst, - size_t len) -{ - struct iov_iter iov_iter = { 0 }; - struct kvec kvec; - size_t to_copy; - - kvec.iov_base = dst; - kvec.iov_len = len; - - iov_iter.iter_type = ITER_KVEC; - iov_iter.kvec = &kvec; - iov_iter.nr_segs = 1; - - to_copy = min_t(size_t, len, skb->len); - - skb_copy_datagram_iter(skb, VIRTIO_VSOCK_SKB_CB(skb)->offset, - &iov_iter, to_copy); -} - /* Packet capture */ static struct sk_buff *virtio_transport_build_skb(void *opaque) { @@ -167,12 +145,12 @@ static struct sk_buff *virtio_transport_build_skb(void *opaque) struct sk_buff *skb; size_t payload_len; - /* A packet could be split to fit the RX buffer, so we can retrieve - * the payload length from the header and the buffer pointer taking - * care of the offset in the original packet. + /* A packet could be split to fit the RX buffer, so we use + * the payload length from the header, which has been updated + * by the sender to reflect the fragment size. */ pkt_hdr = virtio_vsock_hdr(pkt); - payload_len = pkt->len; + payload_len = le32_to_cpu(pkt_hdr->len); skb = alloc_skb(sizeof(*hdr) + sizeof(*pkt_hdr) + payload_len, GFP_ATOMIC); @@ -215,12 +193,18 @@ static struct sk_buff *virtio_transport_build_skb(void *opaque) skb_put_data(skb, pkt_hdr, sizeof(*pkt_hdr)); if (payload_len) { - if (skb_is_nonlinear(pkt)) { - void *data = skb_put(skb, payload_len); + struct iov_iter iov_iter; + struct kvec kvec; + void *data = skb_put(skb, payload_len); - virtio_transport_copy_nonlinear_skb(pkt, data, payload_len); - } else { - skb_put_data(skb, pkt->data, payload_len); + kvec.iov_base = data; + kvec.iov_len = payload_len; + iov_iter_kvec(&iov_iter, ITER_DEST, &kvec, 1, payload_len); + + if (skb_copy_datagram_iter(pkt, VIRTIO_VSOCK_SKB_CB(pkt)->offset, + &iov_iter, payload_len)) { + kfree_skb(skb); + return NULL; } } @@ -398,11 +382,17 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, * each iteration. If this is last skb for this buffer * and MSG_ZEROCOPY mode is in use - we must allocate * completion for the current syscall. + * + * Pass pkt_len because msg iter is already consumed + * by virtio_transport_fill_skb(), so iter->count + * can not be used for RLIMIT_MEMLOCK pinned-pages + * accounting done by msg_zerocopy_realloc(). */ if (info->msg && info->msg->msg_flags & MSG_ZEROCOPY && skb_len == rest_len && info->op == VIRTIO_VSOCK_OP_RW) { if (virtio_transport_init_zcopy_skb(vsk, skb, info->msg, + pkt_len, can_zcopy)) { kfree_skb(skb); ret = -ENOMEM; @@ -442,7 +432,9 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs, u32 len) { - if (vvs->buf_used + len > vvs->buf_alloc) + u64 skb_overhead = (skb_queue_len(&vvs->rx_queue) + 1) * SKB_TRUESIZE(0); + + if (skb_overhead + vvs->buf_used + len > vvs->buf_alloc) return false; vvs->rx_bytes += len; @@ -545,9 +537,8 @@ virtio_transport_stream_do_peek(struct vsock_sock *vsk, skb_queue_walk(&vvs->rx_queue, skb) { size_t bytes; - bytes = len - total; - if (bytes > skb->len) - bytes = skb->len; + bytes = min_t(size_t, len - total, + skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset); spin_unlock_bh(&vvs->rx_lock); @@ -1558,8 +1549,6 @@ virtio_transport_recv_listen(struct sock *sk, struct sk_buff *skb, return -ENOMEM; } - sk_acceptq_added(sk); - lock_sock_nested(child, SINGLE_DEPTH_NESTING); child->sk_state = TCP_ESTABLISHED; @@ -1581,6 +1570,7 @@ virtio_transport_recv_listen(struct sock *sk, struct sk_buff *skb, return ret; } + sk_acceptq_added(sk); if (virtio_transport_space_update(child, skb)) child->sk_write_space(child); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f334cdef8958..7db9cd433801 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1276,6 +1276,18 @@ static int nl80211_prepare_wdev_dump(struct netlink_callback *cb, rtnl_unlock(); return -ENODEV; } + + /* + * The first invocation validated the wdev's netns against + * the caller via __cfg80211_wdev_from_attrs(). The wiphy + * may have moved netns between dumpit invocations (via + * NL80211_CMD_SET_WIPHY_NETNS), so re-check here. + */ + if (!net_eq(wiphy_net(wiphy), sock_net(cb->skb->sk))) { + rtnl_unlock(); + return -ENODEV; + } + *rdev = wiphy_to_rdev(wiphy); *wdev = NULL; @@ -13867,6 +13879,19 @@ static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(net)) return PTR_ERR(net); + /* + * The caller already has CAP_NET_ADMIN over the source netns + * (enforced by GENL_UNS_ADMIN_PERM on the genl op). Mirror the + * convention used by net/core/rtnetlink.c::rtnl_get_net_ns_capable() + * and require CAP_NET_ADMIN over the target netns as well, so that + * a caller that is privileged in their own user namespace cannot + * push a wiphy into a netns where they have no privilege. + */ + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) { + put_net(net); + return -EPERM; + } + err = 0; /* check if anything to do */ @@ -19828,6 +19853,7 @@ static const struct genl_small_ops nl80211_small_ops[] = { .cmd = NL80211_CMD_SET_PMK, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .doit = nl80211_set_pmk, + .flags = GENL_UNS_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_CLEAR_SKB), }, @@ -19835,6 +19861,7 @@ static const struct genl_small_ops nl80211_small_ops[] = { .cmd = NL80211_CMD_DEL_PMK, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .doit = nl80211_del_pmk, + .flags = GENL_UNS_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), }, { diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index 4c8ea0583f94..d6cd0de64d1f 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -88,7 +88,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, out->ftm.ftms_per_burst = 0; if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]) out->ftm.ftms_per_burst = - nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]); + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]); if (capa->ftm.max_ftms_per_burst && (out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst || diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 887abed25466..5e5786cd9af5 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -646,9 +646,42 @@ static u64 xsk_skb_destructor_get_addr(struct sk_buff *skb) return (u64)((uintptr_t)skb_shinfo(skb)->destructor_arg & ~0x1UL); } -static void xsk_skb_destructor_set_addr(struct sk_buff *skb, u64 addr) +static struct xsk_addrs *__xsk_addrs_alloc(struct sk_buff *skb, u64 addr) { - skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL); + struct xsk_addrs *xsk_addr; + + xsk_addr = kmem_cache_zalloc(xsk_tx_generic_cache, GFP_KERNEL); + if (unlikely(!xsk_addr)) + return NULL; + + xsk_addr->addrs[0] = addr; + skb_shinfo(skb)->destructor_arg = (void *)xsk_addr; + return xsk_addr; +} + +static struct xsk_addrs *xsk_addrs_alloc(struct sk_buff *skb) +{ + struct xsk_addrs *xsk_addr; + + if (!xsk_skb_destructor_is_addr(skb)) + return (struct xsk_addrs *)skb_shinfo(skb)->destructor_arg; + + xsk_addr = __xsk_addrs_alloc(skb, xsk_skb_destructor_get_addr(skb)); + if (likely(xsk_addr)) + xsk_addr->num_descs = 1; + return xsk_addr; +} + +static int xsk_skb_destructor_set_addr(struct sk_buff *skb, u64 addr) +{ + if (IS_ENABLED(CONFIG_64BIT)) { + skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL); + return 0; + } + + if (unlikely(!__xsk_addrs_alloc(skb, addr))) + return -ENOMEM; + return 0; } static void xsk_inc_num_desc(struct sk_buff *skb) @@ -685,7 +718,7 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool, spin_lock_irqsave(&pool->cq_prod_lock, flags); idx = xskq_get_prod(pool->cq); - if (unlikely(num_descs > 1)) { + if (unlikely(!xsk_skb_destructor_is_addr(skb))) { xsk_addr = (struct xsk_addrs *)skb_shinfo(skb)->destructor_arg; for (i = 0; i < num_descs; i++) { @@ -724,14 +757,20 @@ void xsk_destruct_skb(struct sk_buff *skb) sock_wfree(skb); } -static void xsk_skb_init_misc(struct sk_buff *skb, struct xdp_sock *xs, - u64 addr) +static int xsk_skb_init_misc(struct sk_buff *skb, struct xdp_sock *xs, + u64 addr) { + int err; + + err = xsk_skb_destructor_set_addr(skb, addr); + if (unlikely(err)) + return err; + skb->dev = xs->dev; skb->priority = READ_ONCE(xs->sk.sk_priority); skb->mark = READ_ONCE(xs->sk.sk_mark); skb->destructor = xsk_destruct_skb; - xsk_skb_destructor_set_addr(skb, addr); + return 0; } static void xsk_consume_skb(struct sk_buff *skb) @@ -740,7 +779,7 @@ static void xsk_consume_skb(struct sk_buff *skb) u32 num_descs = xsk_get_num_desc(skb); struct xsk_addrs *xsk_addr; - if (unlikely(num_descs > 1)) { + if (unlikely(!xsk_skb_destructor_is_addr(skb))) { xsk_addr = (struct xsk_addrs *)skb_shinfo(skb)->destructor_arg; kmem_cache_free(xsk_tx_generic_cache, xsk_addr); } @@ -819,28 +858,19 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, return ERR_PTR(err); skb_reserve(skb, hr); - - xsk_skb_init_misc(skb, xs, desc->addr); if (desc->options & XDP_TX_METADATA) { err = xsk_skb_metadata(skb, buffer, desc, pool, hr); - if (unlikely(err)) + if (unlikely(err)) { + kfree_skb(skb); return ERR_PTR(err); + } } } else { struct xsk_addrs *xsk_addr; - if (xsk_skb_destructor_is_addr(skb)) { - xsk_addr = kmem_cache_zalloc(xsk_tx_generic_cache, - GFP_KERNEL); - if (!xsk_addr) - return ERR_PTR(-ENOMEM); - - xsk_addr->num_descs = 1; - xsk_addr->addrs[0] = xsk_skb_destructor_get_addr(skb); - skb_shinfo(skb)->destructor_arg = (void *)xsk_addr; - } else { - xsk_addr = (struct xsk_addrs *)skb_shinfo(skb)->destructor_arg; - } + xsk_addr = xsk_addrs_alloc(skb); + if (!xsk_addr) + return ERR_PTR(-ENOMEM); /* in case of -EOVERFLOW that could happen below, * xsk_consume_skb() will release this node as whole skb @@ -856,8 +886,11 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, addr = buffer - pool->addrs; for (copied = 0, i = skb_shinfo(skb)->nr_frags; copied < len; i++) { - if (unlikely(i >= MAX_SKB_FRAGS)) + if (unlikely(i >= MAX_SKB_FRAGS)) { + if (!xs->skb) + kfree_skb(skb); return ERR_PTR(-EOVERFLOW); + } page = pool->umem->pgs[addr >> PAGE_SHIFT]; get_page(page); @@ -914,7 +947,6 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, if (unlikely(err)) goto free_err; - xsk_skb_init_misc(skb, xs, desc->addr); if (desc->options & XDP_TX_METADATA) { err = xsk_skb_metadata(skb, buffer, desc, xs->pool, hr); @@ -927,19 +959,10 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, struct page *page; u8 *vaddr; - if (xsk_skb_destructor_is_addr(skb)) { - xsk_addr = kmem_cache_zalloc(xsk_tx_generic_cache, - GFP_KERNEL); - if (!xsk_addr) { - err = -ENOMEM; - goto free_err; - } - - xsk_addr->num_descs = 1; - xsk_addr->addrs[0] = xsk_skb_destructor_get_addr(skb); - skb_shinfo(skb)->destructor_arg = (void *)xsk_addr; - } else { - xsk_addr = (struct xsk_addrs *)skb_shinfo(skb)->destructor_arg; + xsk_addr = xsk_addrs_alloc(skb); + if (!xsk_addr) { + err = -ENOMEM; + goto free_err; } if (unlikely(nr_frags == (MAX_SKB_FRAGS - 1) && xp_mb_desc(desc))) { @@ -964,18 +987,28 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, } } + if (!xs->skb) { + err = xsk_skb_init_misc(skb, xs, desc->addr); + if (unlikely(err)) + goto free_err; + } xsk_inc_num_desc(skb); return skb; free_err: - if (skb && !skb_shinfo(skb)->nr_frags) + if (skb && !xs->skb) kfree_skb(skb); if (err == -EOVERFLOW) { - /* Drop the packet */ - xsk_inc_num_desc(xs->skb); - xsk_drop_skb(xs->skb); + if (xs->skb) { + /* Drop the packet */ + xsk_inc_num_desc(xs->skb); + xsk_drop_skb(xs->skb); + } else { + xsk_cq_cancel_locked(xs->pool, 1); + xs->tx->invalid_descs++; + } xskq_cons_release(xs->tx); } else { /* Let application retry */ diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index cd7bc50872f6..d981cfdd8535 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -175,6 +175,9 @@ int xp_assign_dev(struct xsk_buff_pool *pool, if (force_zc && force_copy) return -EINVAL; + if (pool->tx_sw_csum && (netdev->priv_flags & IFF_TX_SKB_NO_LINEAR)) + return -EOPNOTSUPP; + if (xsk_get_pool_from_qid(netdev, queue_id)) return -EBUSY; diff --git a/net/xdp/xskmap.c b/net/xdp/xskmap.c index afa457506274..3bff346308d0 100644 --- a/net/xdp/xskmap.c +++ b/net/xdp/xskmap.c @@ -184,6 +184,10 @@ static long xsk_map_update_elem(struct bpf_map *map, void *key, void *value, } xs = (struct xdp_sock *)sock->sk; + if (!READ_ONCE(xs->rx)) { + sockfd_put(sock); + return -ENOBUFS; + } map_entry = &m->xsk_map[i]; node = xsk_map_node_alloc(m, map_entry); diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index a9652b422f51..cc35c2fcbbe0 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -66,7 +66,9 @@ static int xfrm4_transport_output(struct xfrm_state *x, struct sk_buff *skb) struct iphdr *iph = ip_hdr(skb); int ihl = iph->ihl * 4; - skb_set_inner_transport_header(skb, skb_transport_offset(skb)); + if (!skb->inner_protocol) + skb_set_inner_transport_header(skb, + skb_transport_offset(skb)); skb_set_network_header(skb, -x->props.header_len); skb->mac_header = skb->network_header + @@ -167,7 +169,9 @@ static int xfrm6_transport_output(struct xfrm_state *x, struct sk_buff *skb) int hdr_len; iph = ipv6_hdr(skb); - skb_set_inner_transport_header(skb, skb_transport_offset(skb)); + if (!skb->inner_protocol) + skb_set_inner_transport_header(skb, + skb_transport_offset(skb)); hdr_len = xfrm6_hdr_offset(x, skb, &prevhdr); if (hdr_len < 0) @@ -276,8 +280,10 @@ static int xfrm4_tunnel_encap_add(struct xfrm_state *x, struct sk_buff *skb) struct iphdr *top_iph; int flags; - skb_set_inner_network_header(skb, skb_network_offset(skb)); - skb_set_inner_transport_header(skb, skb_transport_offset(skb)); + if (!skb->inner_protocol) { + skb_set_inner_network_header(skb, skb_network_offset(skb)); + skb_set_inner_transport_header(skb, skb_transport_offset(skb)); + } skb_set_network_header(skb, -x->props.header_len); skb->mac_header = skb->network_header + @@ -321,8 +327,10 @@ static int xfrm6_tunnel_encap_add(struct xfrm_state *x, struct sk_buff *skb) struct ipv6hdr *top_iph; int dsfield; - skb_set_inner_network_header(skb, skb_network_offset(skb)); - skb_set_inner_transport_header(skb, skb_transport_offset(skb)); + if (!skb->inner_protocol) { + skb_set_inner_network_header(skb, skb_network_offset(skb)); + skb_set_inner_transport_header(skb, skb_transport_offset(skb)); + } skb_set_network_header(skb, -x->props.header_len); skb->mac_header = skb->network_header + diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 1748d374abca..686014d39429 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -818,17 +818,17 @@ int __xfrm_state_delete(struct xfrm_state *x) spin_lock(&net->xfrm.xfrm_state_lock); list_del(&x->km.all); - hlist_del_rcu(&x->bydst); - hlist_del_rcu(&x->bysrc); - if (x->km.seq) - hlist_del_rcu(&x->byseq); + hlist_del_init_rcu(&x->bydst); + hlist_del_init_rcu(&x->bysrc); + if (!hlist_unhashed(&x->byseq)) + hlist_del_init_rcu(&x->byseq); if (!hlist_unhashed(&x->state_cache)) hlist_del_rcu(&x->state_cache); if (!hlist_unhashed(&x->state_cache_input)) hlist_del_rcu(&x->state_cache_input); - if (x->id.spi) - hlist_del_rcu(&x->byspi); + if (!hlist_unhashed(&x->byspi)) + hlist_del_init_rcu(&x->byspi); net->xfrm.state_num--; xfrm_nat_keepalive_state_updated(x); spin_unlock(&net->xfrm.xfrm_state_lock); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index d56450f61669..38a90e5ee3d9 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -3323,6 +3323,7 @@ const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32), [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), + [XFRM_MSG_MAPPING - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping), [XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default), [XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default), }; diff --git a/rust/Makefile b/rust/Makefile index b361bfedfdf0..b9e9f512cec3 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -403,6 +403,8 @@ BINDGEN_TARGET_x86 := x86_64-linux-gnu BINDGEN_TARGET_arm64 := aarch64-linux-gnu BINDGEN_TARGET_arm := arm-linux-gnueabi BINDGEN_TARGET_loongarch := loongarch64-linux-gnusf +# This is only for i386 UM builds, which need the 32-bit target not -m32 +BINDGEN_TARGET_i386 := i386-linux-gnu BINDGEN_TARGET_um := $(BINDGEN_TARGET_$(SUBARCH)) BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH)) diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs index ac8d6f763ae8..6438385e4322 100644 --- a/rust/kernel/alloc/kvec.rs +++ b/rust/kernel/alloc/kvec.rs @@ -9,7 +9,10 @@ use super::{ }; use crate::{ fmt, - page::AsPageIter, // + page::{ + AsPageIter, + PAGE_SIZE, // + }, }; use core::{ borrow::{Borrow, BorrowMut}, @@ -734,6 +737,115 @@ where self.truncate(num_kept); } } +// TODO: This is a temporary KVVec-specific implementation. It should be replaced with a generic +// `shrink_to()` for `impl Vec` that uses `A::realloc()` once the +// underlying allocators properly support shrinking via realloc. +impl Vec { + /// Shrinks the capacity of the vector with a lower bound. + /// + /// The capacity will remain at least as large as both the length and the supplied value. + /// If the current capacity is less than the lower limit, this is a no-op. + /// + /// For `kmalloc` allocations, this delegates to `realloc()`, which decides whether + /// shrinking is worthwhile. For `vmalloc` allocations, shrinking only occurs if the + /// operation would free at least one page of memory, and performs a deep copy since + /// `vrealloc` does not yet support in-place shrinking. + /// + /// # Examples + /// + /// ``` + /// // Allocate enough capacity to span multiple pages. + /// let elements_per_page = kernel::page::PAGE_SIZE / core::mem::size_of::(); + /// let mut v = KVVec::with_capacity(elements_per_page * 4, GFP_KERNEL)?; + /// v.push(1, GFP_KERNEL)?; + /// v.push(2, GFP_KERNEL)?; + /// + /// v.shrink_to(0, GFP_KERNEL)?; + /// # Ok::<(), Error>(()) + /// ``` + pub fn shrink_to(&mut self, min_capacity: usize, flags: Flags) -> Result<(), AllocError> { + let target_cap = core::cmp::max(self.len(), min_capacity); + + if self.capacity() <= target_cap { + return Ok(()); + } + + if Self::is_zst() { + return Ok(()); + } + + // For kmalloc allocations, delegate to realloc() and let the allocator decide + // whether shrinking is worthwhile. + // + // SAFETY: `self.ptr` points to a valid `KVmalloc` allocation. + if !unsafe { bindings::is_vmalloc_addr(self.ptr.as_ptr().cast()) } { + let new_layout = ArrayLayout::::new(target_cap).map_err(|_| AllocError)?; + + // SAFETY: + // - `self.ptr` is valid and was previously allocated with `KVmalloc`. + // - `self.layout` matches the `ArrayLayout` of the preceding allocation. + let ptr = unsafe { + KVmalloc::realloc( + Some(self.ptr.cast()), + new_layout.into(), + self.layout.into(), + flags, + NumaNode::NO_NODE, + )? + }; + + self.ptr = ptr.cast(); + self.layout = new_layout; + return Ok(()); + } + + // Only shrink if we would free at least one page. + let current_size = self.capacity() * core::mem::size_of::(); + let target_size = target_cap * core::mem::size_of::(); + let current_pages = current_size.div_ceil(PAGE_SIZE); + let target_pages = target_size.div_ceil(PAGE_SIZE); + + if current_pages <= target_pages { + return Ok(()); + } + + if target_cap == 0 { + if !self.layout.is_empty() { + // SAFETY: + // - `self.ptr` was previously allocated with `KVmalloc`. + // - `self.layout` matches the `ArrayLayout` of the preceding allocation. + unsafe { KVmalloc::free(self.ptr.cast(), self.layout.into()) }; + } + self.ptr = NonNull::dangling(); + self.layout = ArrayLayout::empty(); + return Ok(()); + } + + // SAFETY: `target_cap <= self.capacity()` and original capacity was valid. + let new_layout = unsafe { ArrayLayout::::new_unchecked(target_cap) }; + + let new_ptr = KVmalloc::alloc(new_layout.into(), flags, NumaNode::NO_NODE)?; + + // SAFETY: + // - `self.as_ptr()` is valid for reads of `self.len()` elements of `T`. + // - `new_ptr` is valid for writes of at least `target_cap >= self.len()` elements. + // - The two allocations do not overlap since `new_ptr` is freshly allocated. + // - Both pointers are properly aligned for `T`. + unsafe { + ptr::copy_nonoverlapping(self.as_ptr(), new_ptr.as_ptr().cast::(), self.len()) + }; + + // SAFETY: + // - `self.ptr` was previously allocated with `KVmalloc`. + // - `self.layout` matches the `ArrayLayout` of the preceding allocation. + unsafe { KVmalloc::free(self.ptr.cast(), self.layout.into()) }; + + self.ptr = new_ptr.cast::(); + self.layout = new_layout; + + Ok(()) + } +} impl Vec { /// Extend the vector by `n` clones of `value`. @@ -1398,4 +1510,106 @@ mod tests { func.push_within_capacity(false).unwrap(); } } + + #[test] + fn test_kvvec_shrink_to() { + use crate::page::PAGE_SIZE; + + // Create a vector with capacity spanning multiple pages. + let mut v = KVVec::::with_capacity(PAGE_SIZE * 4, GFP_KERNEL).unwrap(); + + // Add a few elements. + v.push(1, GFP_KERNEL).unwrap(); + v.push(2, GFP_KERNEL).unwrap(); + v.push(3, GFP_KERNEL).unwrap(); + + let initial_capacity = v.capacity(); + assert!(initial_capacity >= PAGE_SIZE * 4); + + // Shrink to a capacity that would free at least one page. + v.shrink_to(PAGE_SIZE, GFP_KERNEL).unwrap(); + + // Capacity should have been reduced. + assert!(v.capacity() < initial_capacity); + assert!(v.capacity() >= PAGE_SIZE); + + // Elements should be preserved. + assert_eq!(v.len(), 3); + assert_eq!(v[0], 1); + assert_eq!(v[1], 2); + assert_eq!(v[2], 3); + + // Shrink to zero (should shrink to len). + v.shrink_to(0, GFP_KERNEL).unwrap(); + + // Capacity should be at least the length. + assert!(v.capacity() >= v.len()); + + // Elements should still be preserved. + assert_eq!(v.len(), 3); + assert_eq!(v[0], 1); + assert_eq!(v[1], 2); + assert_eq!(v[2], 3); + } + + #[test] + fn test_kvvec_shrink_to_empty() { + use crate::page::PAGE_SIZE; + + // Create a vector with large capacity but no elements. + let mut v = KVVec::::with_capacity(PAGE_SIZE * 4, GFP_KERNEL).unwrap(); + + assert!(v.is_empty()); + + // Shrink empty vector to zero. + v.shrink_to(0, GFP_KERNEL).unwrap(); + + // Should have freed the allocation. + assert_eq!(v.capacity(), 0); + assert!(v.is_empty()); + } + + #[test] + fn test_kvvec_shrink_to_no_op() { + use crate::page::PAGE_SIZE; + + // Create a small vector. + let mut v = KVVec::::with_capacity(PAGE_SIZE, GFP_KERNEL).unwrap(); + v.push(1, GFP_KERNEL).unwrap(); + + let capacity_before = v.capacity(); + + // Try to shrink to a capacity larger than current - should be no-op. + v.shrink_to(capacity_before + 100, GFP_KERNEL).unwrap(); + + assert_eq!(v.capacity(), capacity_before); + assert_eq!(v.len(), 1); + assert_eq!(v[0], 1); + } + + #[test] + fn test_kvvec_shrink_to_respects_min_capacity() { + use crate::page::PAGE_SIZE; + + // Create a vector with large capacity. + let mut v = KVVec::::with_capacity(PAGE_SIZE * 4, GFP_KERNEL).unwrap(); + + // Add some elements. + for i in 0..10u8 { + v.push(i, GFP_KERNEL).unwrap(); + } + + // Shrink to a min_capacity larger than length. + let min_cap = PAGE_SIZE * 2; + v.shrink_to(min_cap, GFP_KERNEL).unwrap(); + + // Capacity should be at least min_capacity. + assert!(v.capacity() >= min_cap); + + // All elements preserved. + assert_eq!(v.len(), 10); + for i in 0..10u8 { + assert_eq!(v[i as usize], i); + } + } } diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index adbafe8db54d..403fc35353c7 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -119,13 +119,20 @@ impl Device { // compatible `Layout`. let layout = Kmalloc::aligned_layout(Layout::new::()); + // Use a temporary vtable without a `release` callback until `data` is initialized, so + // init failure can release the DRM device without dropping uninitialized fields. + let alloc_vtable = bindings::drm_driver { + release: None, + ..Self::VTABLE + }; + // SAFETY: - // - `VTABLE`, as a `const` is pinned to the read-only section of the compilation, + // - `alloc_vtable` reference remains valid until no longer used, // - `dev` is valid by its type invarants, let raw_drm: *mut Self = unsafe { bindings::__drm_dev_alloc( dev.as_raw(), - &Self::VTABLE, + &alloc_vtable, layout.size(), mem::offset_of!(Self, dev), ) @@ -133,6 +140,10 @@ impl Device { .cast(); let raw_drm = NonNull::new(from_err_ptr(raw_drm)?).ok_or(ENOMEM)?; + // SAFETY: `raw_drm` is a valid pointer to `Self`, given that `__drm_dev_alloc` was + // successful. + let drm_dev = unsafe { Self::into_drm_device(raw_drm) }; + // SAFETY: `raw_drm` is a valid pointer to `Self`. let raw_data = unsafe { ptr::addr_of_mut!((*raw_drm.as_ptr()).data) }; @@ -140,15 +151,14 @@ impl Device { // - `raw_data` is a valid pointer to uninitialized memory. // - `raw_data` will not move until it is dropped. unsafe { data.__pinned_init(raw_data) }.inspect_err(|_| { - // SAFETY: `raw_drm` is a valid pointer to `Self`, given that `__drm_dev_alloc` was - // successful. - let drm_dev = unsafe { Self::into_drm_device(raw_drm) }; - // SAFETY: `__drm_dev_alloc()` was successful, hence `drm_dev` must be valid and the // refcount must be non-zero. unsafe { bindings::drm_dev_put(drm_dev) }; })?; + // SAFETY: `drm_dev` is still private to this function. + unsafe { (*drm_dev).driver = const { &Self::VTABLE } }; + // SAFETY: The reference count is one, and now we take ownership of that reference as a // `drm::Device`. Ok(unsafe { ARef::from_raw(raw_drm) }) diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index 75acda7ba500..01b5bd47a333 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -277,8 +277,17 @@ impl Object { // SAFETY: `obj.as_raw()` is guaranteed to be valid by the initialization above. unsafe { (*obj.as_raw()).funcs = &Self::OBJECT_FUNCS }; - // SAFETY: The arguments are all valid per the type invariants. - to_result(unsafe { bindings::drm_gem_object_init(dev.as_raw(), obj.obj.get(), size) })?; + if let Err(err) = + // SAFETY: The arguments are all valid per the type invariants. + to_result(unsafe { + bindings::drm_gem_object_init(dev.as_raw(), obj.obj.get(), size) + }) + { + // SAFETY: `drm_gem_object_init()` initializes the private GEM object state before + // failing, so `drm_gem_private_object_fini()` is the matching cleanup. + unsafe { bindings::drm_gem_private_object_fini(obj.obj.get()) }; + return Err(err); + } // SAFETY: We will never move out of `Self` as `ARef` is always treated as pinned. let ptr = KBox::into_raw(unsafe { Pin::into_inner_unchecked(obj) }); diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs index d025fb035195..e1b648920d2f 100644 --- a/rust/kernel/drm/gem/shmem.rs +++ b/rust/kernel/drm/gem/shmem.rs @@ -19,10 +19,8 @@ use crate::{ }, error::to_result, prelude::*, - types::{ - ARef, - Opaque, // - }, // + sync::aref::ARef, + types::Opaque, // }; use core::{ ops::{ diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs index 0616c0353c2b..9989f56d0605 100644 --- a/rust/kernel/sync/aref.rs +++ b/rust/kernel/sync/aref.rs @@ -170,3 +170,25 @@ impl Drop for ARef { unsafe { T::dec_ref(self.ptr) }; } } + +impl PartialEq> for ARef +where + T: AlwaysRefCounted + PartialEq, + U: AlwaysRefCounted, +{ + #[inline] + fn eq(&self, other: &ARef) -> bool { + T::eq(&**self, &**other) + } +} +impl Eq for ARef {} + +impl PartialEq<&'_ U> for ARef +where + T: AlwaysRefCounted + PartialEq, +{ + #[inline] + fn eq(&self, other: &&U) -> bool { + T::eq(&**self, other) + } +} diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index 049c8a4d45d8..38273f4eedb5 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -361,6 +361,15 @@ unsafe impl crate::sync::aref::AlwaysRefCounted for Task { } } +impl PartialEq for Task { + #[inline] + fn eq(&self, other: &Self) -> bool { + ptr::eq(self.as_ptr(), other.as_ptr()) + } +} + +impl Eq for Task {} + impl Kuid { /// Get the current euid. #[inline] diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs index 6c9c1cce3c63..5f6c4d7a1a51 100644 --- a/rust/kernel/uaccess.rs +++ b/rust/kernel/uaccess.rs @@ -21,7 +21,7 @@ use core::mem::{size_of, MaybeUninit}; /// /// This is the Rust equivalent to C pointers tagged with `__user`. #[repr(transparent)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Zeroable)] pub struct UserPtr(*mut c_void); impl UserPtr { diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index daa3f1c6466e..487ee0013faf 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -249,22 +249,6 @@ fn init_fields( }); // Again span for better diagnostics let write = quote_spanned!(ident.span()=> ::core::ptr::write); - // NOTE: the field accessor ensures that the initialized field is properly aligned. - // Unaligned fields will cause the compiler to emit E0793. We do not support - // unaligned fields since `Init::__init` requires an aligned pointer; the call to - // `ptr::write` below has the same requirement. - let accessor = if pinned { - let project_ident = format_ident!("__project_{ident}"); - quote! { - // SAFETY: TODO - unsafe { #data.#project_ident(&mut (*#slot).#ident) } - } - } else { - quote! { - // SAFETY: TODO - unsafe { &mut (*#slot).#ident } - } - }; quote! { #(#attrs)* { @@ -272,51 +256,31 @@ fn init_fields( // SAFETY: TODO unsafe { #write(&raw mut (*#slot).#ident, #value_ident) }; } - #(#cfgs)* - #[allow(unused_variables)] - let #ident = #accessor; } } InitializerKind::Init { ident, value, .. } => { // Again span for better diagnostics let init = format_ident!("init", span = value.span()); - // NOTE: the field accessor ensures that the initialized field is properly aligned. - // Unaligned fields will cause the compiler to emit E0793. We do not support - // unaligned fields since `Init::__init` requires an aligned pointer; the call to - // `ptr::write` below has the same requirement. - let (value_init, accessor) = if pinned { - let project_ident = format_ident!("__project_{ident}"); - ( - quote! { - // SAFETY: - // - `slot` is valid, because we are inside of an initializer closure, we - // return when an error/panic occurs. - // - We also use `#data` to require the correct trait (`Init` or `PinInit`) - // for `#ident`. - unsafe { #data.#ident(&raw mut (*#slot).#ident, #init)? }; - }, - quote! { - // SAFETY: TODO - unsafe { #data.#project_ident(&mut (*#slot).#ident) } - }, - ) + let value_init = if pinned { + quote! { + // SAFETY: + // - `slot` is valid, because we are inside of an initializer closure, we + // return when an error/panic occurs. + // - We also use `#data` to require the correct trait (`Init` or `PinInit`) + // for `#ident`. + unsafe { #data.#ident(&raw mut (*#slot).#ident, #init)? }; + } } else { - ( - quote! { - // SAFETY: `slot` is valid, because we are inside of an initializer - // closure, we return when an error/panic occurs. - unsafe { - ::pin_init::Init::__init( - #init, - &raw mut (*#slot).#ident, - )? - }; - }, - quote! { - // SAFETY: TODO - unsafe { &mut (*#slot).#ident } - }, - ) + quote! { + // SAFETY: `slot` is valid, because we are inside of an initializer + // closure, we return when an error/panic occurs. + unsafe { + ::pin_init::Init::__init( + #init, + &raw mut (*#slot).#ident, + )? + }; + } }; quote! { #(#attrs)* @@ -324,9 +288,6 @@ fn init_fields( let #init = #value; #value_init } - #(#cfgs)* - #[allow(unused_variables)] - let #ident = #accessor; } } InitializerKind::Code { block: value, .. } => quote! { @@ -339,18 +300,41 @@ fn init_fields( if let Some(ident) = kind.ident() { // `mixed_site` ensures that the guard is not accessible to the user-controlled code. let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); + + // NOTE: The reference is derived from the guard so that it only lives as long as the + // guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident` + // like the unaligned field guard, it will become effectively `'static`. + let accessor = if pinned { + let project_ident = format_ident!("__project_{ident}"); + quote! { + // SAFETY: the initialization is pinned. + unsafe { #data.#project_ident(#guard.let_binding()) } + } + } else { + quote! { + #guard.let_binding() + } + }; + res.extend(quote! { #(#cfgs)* - // Create the drop guard: + // Create the drop guard. // - // We rely on macro hygiene to make it impossible for users to access this local - // variable. - // SAFETY: We forget the guard later when initialization has succeeded. - let #guard = unsafe { + // SAFETY: + // - `&raw mut (*slot).#ident` is valid. + // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. + // - `(*slot).#ident` has been initialized above. + // - We only need the ownership to the pointee back when initialization has + // succeeded, where we `forget` the guard. + let mut #guard = unsafe { ::pin_init::__internal::DropGuard::new( &raw mut (*slot).#ident ) }; + + #(#cfgs)* + #[allow(unused_variables)] + let #ident = #accessor; }); guards.push(guard); guard_attrs.push(cfgs); @@ -367,49 +351,49 @@ fn init_fields( } } -/// Generate the check for ensuring that every field has been initialized. +/// Generate the check for ensuring that every field has been initialized and aligned. fn make_field_check( fields: &Punctuated, init_kind: InitKind, path: &Path, ) -> TokenStream { - let field_attrs = fields + let field_attrs: Vec<_> = fields .iter() - .filter_map(|f| f.kind.ident().map(|_| &f.attrs)); - let field_name = fields.iter().filter_map(|f| f.kind.ident()); - match init_kind { - InitKind::Normal => quote! { - // We use unreachable code to ensure that all fields have been mentioned exactly once, - // this struct initializer will still be type-checked and complain with a very natural - // error message if a field is forgotten/mentioned more than once. - #[allow(unreachable_code, clippy::diverging_sub_expression)] - // SAFETY: this code is never executed. - let _ = || unsafe { - ::core::ptr::write(slot, #path { - #( - #(#field_attrs)* - #field_name: ::core::panic!(), - )* - }) - }; - }, - InitKind::Zeroing => quote! { - // We use unreachable code to ensure that all fields have been mentioned at most once. - // Since the user specified `..Zeroable::zeroed()` at the end, all missing fields will - // be zeroed. This struct initializer will still be type-checked and complain with a - // very natural error message if a field is mentioned more than once, or doesn't exist. - #[allow(unreachable_code, clippy::diverging_sub_expression, unused_assignments)] - // SAFETY: this code is never executed. - let _ = || unsafe { - ::core::ptr::write(slot, #path { - #( - #(#field_attrs)* - #field_name: ::core::panic!(), - )* - ..::core::mem::zeroed() - }) - }; - }, + .filter_map(|f| f.kind.ident().map(|_| &f.attrs)) + .collect(); + let field_name: Vec<_> = fields.iter().filter_map(|f| f.kind.ident()).collect(); + let zeroing_trailer = match init_kind { + InitKind::Normal => None, + InitKind::Zeroing => Some(quote! { + ..::core::mem::zeroed() + }), + }; + quote! { + #[allow(unreachable_code, clippy::diverging_sub_expression)] + // We use unreachable code to perform field checks. They're still checked by the compiler. + // SAFETY: this code is never executed. + let _ = || unsafe { + // Create references to ensure that the initialized field is properly aligned. + // Unaligned fields will cause the compiler to emit E0793. We do not support + // unaligned fields since `Init::__init` requires an aligned pointer; the call to + // `ptr::write` for value-initialization case has the same requirement. + #( + #(#field_attrs)* + let _ = &(*slot).#field_name; + )* + + // If the zeroing trailer is not present, this checks that all fields have been + // mentioned exactly once. If the zeroing trailer is present, all missing fields will be + // zeroed, so this checks that all fields have been mentioned at most once. The use of + // struct initializer will still generate very natural error messages for any misuse. + ::core::ptr::write(slot, #path { + #( + #(#field_attrs)* + #field_name: ::core::panic!(), + )* + #zeroing_trailer + }) + }; } } diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index 90adbdc1893b..5720a621aed7 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -238,32 +238,42 @@ fn stack_init_reuse() { /// When a value of this type is dropped, it drops a `T`. /// /// Can be forgotten to prevent the drop. +/// +/// # Invariants +/// +/// - `ptr` is valid and properly aligned. +/// - `*ptr` is initialized and owned by this guard. pub struct DropGuard { ptr: *mut T, } impl DropGuard { - /// Creates a new [`DropGuard`]. It will [`ptr::drop_in_place`] `ptr` when it gets dropped. + /// Creates a drop guard and transfer the ownership of the pointer content. + /// + /// The ownership is only relinguished if the guard is forgotten via [`core::mem::forget`]. /// /// # Safety /// - /// `ptr` must be a valid pointer. - /// - /// It is the callers responsibility that `self` will only get dropped if the pointee of `ptr`: - /// - has not been dropped, - /// - is not accessible by any other means, - /// - will not be dropped by any other means. + /// - `ptr` is valid and properly aligned. + /// - `*ptr` is initialized, and the ownership is transferred to this guard. #[inline] pub unsafe fn new(ptr: *mut T) -> Self { + // INVARIANT: By safety requirement. Self { ptr } } + + /// Create a let binding for accessor use. + #[inline] + pub fn let_binding(&mut self) -> &mut T { + // SAFETY: Per type invariant. + unsafe { &mut *self.ptr } + } } impl Drop for DropGuard { #[inline] fn drop(&mut self) { - // SAFETY: A `DropGuard` can only be constructed using the unsafe `new` function - // ensuring that this operation is safe. + // SAFETY: `self.ptr` is valid, properly aligned and `*self.ptr` is owned by this guard. unsafe { ptr::drop_in_place(self.ptr) } } } diff --git a/samples/qmi/qmi_sample_client.c b/samples/qmi/qmi_sample_client.c index d1814582319b..247ec5e54c4f 100644 --- a/samples/qmi/qmi_sample_client.c +++ b/samples/qmi/qmi_sample_client.c @@ -592,7 +592,7 @@ static int qmi_sample_init(void) if (ret < 0) goto err_unregister_driver; - qmi_add_lookup(&lookup_client, 15, 0, 0); + qmi_add_lookup(&lookup_client, QMI_SERVICE_ID_TEST, 0, 0); return 0; diff --git a/scripts/bloat-o-meter b/scripts/bloat-o-meter index db5dd18dc2d5..9b4fb996d95b 100755 --- a/scripts/bloat-o-meter +++ b/scripts/bloat-o-meter @@ -18,8 +18,8 @@ group.add_argument('-c', help='categorize output based on symbol type', action=' group.add_argument('-d', help='Show delta of Data Section', action='store_true') group.add_argument('-t', help='Show delta of text Section', action='store_true') parser.add_argument('-p', dest='prefix', help='Arch prefix for the tool being used. Useful in cross build scenarios') -parser.add_argument('file1', help='First file to compare') -parser.add_argument('file2', help='Second file to compare') +parser.add_argument('file_old', help='First file to compare') +parser.add_argument('file_new', help='Second file to compare') args = parser.parse_args() @@ -86,7 +86,7 @@ def calc(oldfile, newfile, format): def print_result(symboltype, symbolformat): grow, shrink, add, remove, up, down, delta, old, new, otot, ntot = \ - calc(args.file1, args.file2, symbolformat) + calc(args.file_old, args.file_new, symbolformat) print("add/remove: %s/%s grow/shrink: %s/%s up/down: %s/%s (%s)" % \ (add, remove, grow, shrink, up, -down, up-down)) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index e56374662ff7..0492d6afc9a1 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -641,6 +641,7 @@ our $signature_tags = qr{(?xi: Reviewed-by:| Reported-by:| Suggested-by:| + Assisted-by:| To:| Cc: )}; @@ -2928,7 +2929,7 @@ sub process { } $checklicenseline = 1; - if ($realfile !~ /^MAINTAINERS/) { + if ($realfile !~ /^(MAINTAINERS|dev\/null)/) { my $last_binding_patch = $is_binding_patch; $is_binding_patch = () = $realfile =~ m@^(?:Documentation/devicetree/|include/dt-bindings/)@; @@ -3105,6 +3106,15 @@ sub process { } } + # Assisted-by uses AGENT_NAME:MODEL_VERSION format, not email + if ($sign_off =~ /^Assisted-by:/i) { + if ($email !~ /^\S+:\S+/) { + WARN("BAD_SIGN_OFF", + "Assisted-by expects 'AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]' format\n" . $herecurr); + } + next; + } + my ($email_name, $name_comment, $email_address, $comment) = parse_email($email); my $suggested_email = format_email(($email_name, $name_comment, $email_address, $comment)); if ($suggested_email eq "") { @@ -3856,6 +3866,14 @@ sub process { "Misplaced SPDX-License-Identifier tag - use line $checklicenseline instead\n" . $herecurr); } +# check for disallowed SPDX file tags + if ($rawline =~ /\bSPDX-.*:/ && + $rawline !~ /\bSPDX-License-Identifier:/ && + $rawline !~ /\bSPDX-FileCopyrightText:/) { + WARN("SPDX_LICENSE_TAG", + "Disallowed SPDX tag\n" . $herecurr); + } + # line length limit (with some exclusions) # # There are a few types of lines that may extend beyond $max_line_length: @@ -7502,10 +7520,10 @@ sub process { } # check for various structs that are normally const (ops, kgdb, device_tree) -# and avoid what seem like struct definitions 'struct foo {' +# and avoid what seem like struct definitions 'struct foo {' or forward declarations 'struct foo;' if (defined($const_structs) && $line !~ /\bconst\b/ && - $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) { + $line =~ /\bstruct\s+($const_structs)\b(?!\s*[\{;])/) { WARN("CONST_STRUCT", "struct $1 should normally be const\n" . $herecurr); } diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh index 8d01b741de62..39d60d477bf3 100755 --- a/scripts/decode_stacktrace.sh +++ b/scripts/decode_stacktrace.sh @@ -5,9 +5,11 @@ usage() { echo "Usage:" - echo " $0 -r " - echo " $0 [ [|auto []]]" + echo " $0 [-R] -r " + echo " $0 [-R] [ [|auto []]]" echo " $0 -h" + echo "Options:" + echo " -R: decode return address instead of caller address." } # Try to find a Rust demangler @@ -33,11 +35,17 @@ fi READELF=${UTIL_PREFIX}readelf${UTIL_SUFFIX} ADDR2LINE=${UTIL_PREFIX}addr2line${UTIL_SUFFIX} NM=${UTIL_PREFIX}nm${UTIL_SUFFIX} +decode_retaddr=false if [[ $1 == "-h" ]] ; then usage exit 0 -elif [[ $1 == "-r" ]] ; then +elif [[ $1 == "-R" ]] ; then + decode_retaddr=true + shift 1 +fi + +if [[ $1 == "-r" ]] ; then vmlinux="" basepath="auto" modpath="" @@ -176,13 +184,23 @@ parse_symbol() { # Let's start doing the math to get the exact address into the # symbol. First, strip out the symbol total length. local expr=${symbol%/*} + # Also parse the offset from symbol. + local offset=${expr#*+} + offset=$((offset)) # Now, replace the symbol name with the base address we found # before. expr=${expr/$name/0x$base_addr} # Evaluate it to find the actual address - expr=$((expr)) + # The stack trace shows the return address, which is the next + # instruction after the actual call, so as long as it's in the same + # symbol, subtract one from that to point the call instruction. + if [[ $decode_retaddr == false && $offset != 0 ]]; then + expr=$((expr-1)) + else + expr=$((expr)) + fi local address=$(printf "%x\n" "$expr") # Pass it to addr2line to get filename and line number diff --git a/scripts/decodecode b/scripts/decodecode index 6364218b2178..01d25dc110de 100755 --- a/scripts/decodecode +++ b/scripts/decodecode @@ -12,7 +12,6 @@ faultlinenum=1 cleanup() { rm -f $T $T.s $T.o $T.oo $T.aa $T.dis - exit 1 } die() { @@ -49,7 +48,7 @@ done if [ -z "$code" ]; then rm $T - exit + die "Code line not found" fi echo $code diff --git a/scripts/dtc/checks.c b/scripts/dtc/checks.c index 45d0213f3bf3..946c1429e0f1 100644 --- a/scripts/dtc/checks.c +++ b/scripts/dtc/checks.c @@ -324,7 +324,7 @@ ERROR(node_name_chars, check_node_name_chars, NODECHARS); static void check_node_name_chars_strict(struct check *c, struct dt_info *dti, struct node *node) { - int n = strspn(node->name, c->data); + size_t n = strspn(node->name, c->data); if (n < node->basenamelen) FAIL(c, dti, node, "Character '%c' not recommended in node name", diff --git a/scripts/dtc/dtc-lexer.l b/scripts/dtc/dtc-lexer.l index 15d585c80798..1b129b118b0f 100644 --- a/scripts/dtc/dtc-lexer.l +++ b/scripts/dtc/dtc-lexer.l @@ -39,8 +39,6 @@ extern bool treesource_error; #define DPRINT(fmt, ...) do { } while (0) #endif -static int dts_version = 1; - #define BEGIN_DEFAULT() DPRINT("\n"); \ BEGIN(V1); \ @@ -101,7 +99,6 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...); <*>"/dts-v1/" { DPRINT("Keyword: /dts-v1/\n"); - dts_version = 1; BEGIN_DEFAULT(); return DT_V1; } diff --git a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h index 7231200e5d02..473552ebf017 100644 --- a/scripts/dtc/dtc.h +++ b/scripts/dtc/dtc.h @@ -227,7 +227,7 @@ struct node { struct node *next_sibling; char *fullpath; - int basenamelen; + size_t basenamelen; cell_t phandle; int addr_cells, size_cells; diff --git a/scripts/dtc/libfdt/fdt.c b/scripts/dtc/libfdt/fdt.c index 95f644c31f94..56d4dcb2dc92 100644 --- a/scripts/dtc/libfdt/fdt.c +++ b/scripts/dtc/libfdt/fdt.c @@ -110,6 +110,14 @@ int fdt_check_header(const void *fdt) || (fdt_totalsize(fdt) > INT_MAX)) return -FDT_ERR_TRUNCATED; + /* memrsv block must be 8 byte aligned */ + if (fdt_off_mem_rsvmap(fdt) % sizeof(uint64_t)) + return -FDT_ERR_ALIGNMENT; + + /* Structure block must be 4 byte aligned */ + if (fdt_off_dt_struct(fdt) % FDT_TAGSIZE) + return -FDT_ERR_ALIGNMENT; + /* Bounds check memrsv block */ if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt))) diff --git a/scripts/dtc/libfdt/fdt_rw.c b/scripts/dtc/libfdt/fdt_rw.c index 7475cafce071..90ea14e944cc 100644 --- a/scripts/dtc/libfdt/fdt_rw.c +++ b/scripts/dtc/libfdt/fdt_rw.c @@ -22,6 +22,12 @@ static int fdt_blocks_misordered_(const void *fdt, (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); } +static void fdt_downgrade_version(void *fdt) +{ + if (!can_assume(LATEST) && fdt_version(fdt) > FDT_LAST_SUPPORTED_VERSION) + fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); +} + static int fdt_rw_probe_(void *fdt) { if (can_assume(VALID_DTB)) @@ -33,9 +39,8 @@ static int fdt_rw_probe_(void *fdt) if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry), fdt_size_dt_struct(fdt))) return -FDT_ERR_BADLAYOUT; - if (!can_assume(LATEST) && fdt_version(fdt) > 17) - fdt_set_version(fdt, 17); + fdt_downgrade_version(fdt); return 0; } diff --git a/scripts/dtc/version_gen.h b/scripts/dtc/version_gen.h index 5730bf457b33..122e684e76a1 100644 --- a/scripts/dtc/version_gen.h +++ b/scripts/dtc/version_gen.h @@ -1 +1 @@ -#define DTC_VERSION "DTC 1.7.2-ga26ef640" +#define DTC_VERSION "DTC 1.7.2-g53373d13" diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h index 8f1b3500f8e2..abb1964c44d4 100644 --- a/scripts/gcc-plugins/gcc-common.h +++ b/scripts/gcc-plugins/gcc-common.h @@ -309,7 +309,9 @@ typedef const gimple *const_gimple_ptr; #define gimple gimple_ptr #define const_gimple const_gimple_ptr #undef CONST_CAST_GIMPLE -#define CONST_CAST_GIMPLE(X) CONST_CAST(gimple, (X)) +#define CONST_CAST_GIMPLE(X) const_cast((X)) +#undef CONST_CAST_TREE +#define CONST_CAST_TREE(X) const_cast((X)) /* gimple related */ static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree lhs, tree op1, tree op2 MEM_STAT_DECL) diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py index d4308b726183..943ff1228b48 100644 --- a/scripts/gdb/linux/symbols.py +++ b/scripts/gdb/linux/symbols.py @@ -298,7 +298,7 @@ are loaded as well.""" if p == "-bpf": monitor_bpf = True else: - p.append(os.path.abspath(os.path.expanduser(p))) + self.module_paths.append(os.path.abspath(os.path.expanduser(p))) self.module_paths.append(os.getcwd()) if self.breakpoint is not None: diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index 4414194bedcf..f0ca0db6ddc2 100755 --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -375,8 +375,10 @@ sub read_maintainer_file { ##Filename pattern matching if ($type eq "F" || $type eq "X") { $value =~ s@\.@\\\.@g; ##Convert . to \. + $value =~ s/\*\*/\x00/g; ##Convert ** to placeholder $value =~ s/\*/\.\*/g; ##Convert * to .* $value =~ s/\?/\./g; ##Convert ? to . + $value =~ s/\x00/(?:.*)/g; ##Convert placeholder to (?:.*) ##if pattern is a directory and it lacks a trailing slash, add one if ((-d $value)) { $value =~ s@([^/])$@$1/@; @@ -746,8 +748,10 @@ sub self_test { if (($type eq "F" || $type eq "X") && ($self_test eq "" || $self_test =~ /\bpatterns\b/)) { $value =~ s@\.@\\\.@g; ##Convert . to \. + $value =~ s/\*\*/\x00/g; ##Convert ** to placeholder $value =~ s/\*/\.\*/g; ##Convert * to .* $value =~ s/\?/\./g; ##Convert ? to . + $value =~ s/\x00/(?:.*)/g; ##Convert placeholder to (?:.*) ##if pattern is a directory and it lacks a trailing slash, add one if ((-d $value)) { $value =~ s@([^/])$@$1/@; @@ -921,7 +925,7 @@ sub get_maintainers { my $value_pd = ($value =~ tr@/@@); my $file_pd = ($file =~ tr@/@@); $value_pd++ if (substr($value,-1,1) ne "/"); - $value_pd = -1 if ($value =~ /^\.\*/); + $value_pd = -1 if ($value =~ /^(\.\*|\(\?:\.\*\))/); if ($value_pd >= $file_pd && range_is_maintained($start, $end) && range_has_maintainer($start, $end)) { @@ -955,6 +959,7 @@ sub get_maintainers { $line =~ s/([^\\])\.([^\*])/$1\?$2/g; $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ? $line =~ s/\\\./\./g; ##Convert \. to . + $line =~ s/\(\?:\.\*\)/\*\*/g; ##Convert (?:.*) to ** $line =~ s/\.\*/\*/g; ##Convert .* to * } my $count = $line =~ s/^([A-Z]):/$1:\t/g; @@ -1048,7 +1053,7 @@ sub file_match_pattern { if ($file =~ m@^$pattern@) { my $s1 = ($file =~ tr@/@@); my $s2 = ($pattern =~ tr@/@@); - if ($s1 == $s2) { + if ($s1 == $s2 || $pattern =~ /\(\?:/) { return 1; } } diff --git a/scripts/module.lds.S b/scripts/module.lds.S index 2dc4c8c3e667..b62683061d79 100644 --- a/scripts/module.lds.S +++ b/scripts/module.lds.S @@ -40,9 +40,11 @@ SECTIONS { __kcfi_traps 0 : { KEEP(*(.kcfi_traps)) } #endif +#ifndef CONFIG_ARCH_WANTS_MODULES_TEXT_SECTIONS .text 0 : { *(.text .text.[0-9a-zA-Z_]*) } +#endif .bss 0 : { *(.bss .bss.[0-9a-zA-Z_]*) diff --git a/scripts/package/builddeb b/scripts/package/builddeb index 3627ca227e5a..ba1defc61652 100755 --- a/scripts/package/builddeb +++ b/scripts/package/builddeb @@ -139,7 +139,13 @@ install_kernel_headers () { pdir=debian/$1 version=${1#linux-headers-} - CC="${DEB_HOST_GNU_TYPE}-gcc" "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}" + # Override $CC only for cross-compiles, to not unnecessarily rebuild + # scripts/ including plugins, which may lead to a full kernel rebuild. + if [ -n "${CROSS_COMPILE}" ]; then + CC="${DEB_HOST_GNU_TYPE}-gcc" "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}" + else + "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}" + fi mkdir -p $pdir/lib/modules/$version/ ln -s /usr/src/linux-headers-$version $pdir/lib/modules/$version/build diff --git a/scripts/spelling.txt b/scripts/spelling.txt index 1e89b92c2f9a..2f2e81dbda03 100644 --- a/scripts/spelling.txt +++ b/scripts/spelling.txt @@ -57,8 +57,8 @@ acknowledgement||acknowledgment ackowledge||acknowledge ackowledged||acknowledged acording||according -activete||activate actived||activated +activete||activate actualy||actually actvie||active acumulating||accumulating @@ -66,12 +66,12 @@ acumulative||accumulative acumulator||accumulator acutally||actually adapater||adapter +adddress||address adderted||asserted addional||additional additionaly||additionally additonal||additional addres||address -adddress||address addreses||addresses addresss||address addrress||address @@ -95,9 +95,9 @@ alegorical||allegorical algined||aligned algorith||algorithm algorithmical||algorithmically +algorithmn||algorithm algoritm||algorithm algoritms||algorithms -algorithmn||algorithm algorrithm||algorithm algorritm||algorithm aligment||alignment @@ -128,20 +128,20 @@ amount of times||number of times amout||amount amplifer||amplifier amplifyer||amplifier -an union||a union -an user||a user -an userspace||a userspace -an one||a one analysator||analyzer ang||and anniversery||anniversary annoucement||announcement anomolies||anomalies anomoly||anomaly +an one||a one anonynous||anonymous +an union||a union +an user||a user +an userspace||a userspace anway||anyway -aplication||application apeared||appeared +aplication||application appearence||appearance applicaion||application appliction||application @@ -155,8 +155,8 @@ approriately||appropriately apropriate||appropriate aquainted||acquainted aquired||acquired -aquisition||acquisition aquires||acquires +aquisition||acquisition arbitary||arbitrary architechture||architecture archtecture||architecture @@ -189,30 +189,30 @@ assum||assume assumtpion||assumption asume||assume asuming||assuming -asycronous||asynchronous asychronous||asynchronous -asynchnous||asynchronous -asynchrnous||asynchronous -asynchronus||asynchronous -asynchromous||asynchronous +asycronous||asynchronous asymetric||asymmetric asymmeric||asymmetric +asynchnous||asynchronous +asynchrnous||asynchronous +asynchromous||asynchronous +asynchronus||asynchronous +atempt||attempt atleast||at least atomatically||automatically atomicly||atomically -atempt||attempt atrributes||attributes attachement||attachment attatch||attach attched||attached attemp||attempt -attemps||attempts attemping||attempting +attemps||attempts attepmpt||attempt attnetion||attention attruibutes||attributes -authentification||authentication authenicated||authenticated +authentification||authentication automaticaly||automatically automaticly||automatically automatize||automate @@ -257,6 +257,7 @@ begining||beginning beter||better betweeen||between bianries||binaries +binded||bound bitmast||bitmask bitwiedh||bitwidth boardcast||broadcast @@ -287,19 +288,19 @@ calucate||calculate calulate||calculate cancelation||cancellation cancle||cancel -cant||can't -cant'||can't -canot||cannot -cann't||can't cannnot||cannot +cann't||can't +canot||cannot +cant'||can't +cant||can't capabiity||capability capabilites||capabilities capabilties||capabilities capabilty||capability capabitilies||capabilities capablity||capability -capatibilities||capabilities capapbilities||capabilities +capatibilities||capabilities captuer||capture caputure||capture carefuly||carefully @@ -307,9 +308,9 @@ cariage||carriage casued||caused catagory||category cehck||check +chache||cache challange||challenge challanges||challenges -chache||cache chanell||channel changable||changeable chanined||chained @@ -347,6 +348,7 @@ colescing||coalescing collapsable||collapsible colorfull||colorful comand||command +comaptible||compatible comit||commit commerical||commercial comming||coming @@ -357,10 +359,6 @@ committ||commit commmand||command commnunication||communication commoditiy||commodity -comsume||consume -comsumer||consumer -comsuming||consuming -comaptible||compatible compability||compatibility compaibility||compatibility comparsion||comparison @@ -376,22 +374,25 @@ compleatly||completely completition||completion completly||completely complient||compliant -componnents||components compoment||component +componnents||components comppatible||compatible compres||compress compresion||compression compresser||compressor comression||compression +comsume||consume comsumed||consumed +comsumer||consumer +comsuming||consuming comunicate||communicate comunication||communication conbination||combination concurent||concurrent conditionaly||conditionally conditon||condition -condtion||condition condtional||conditional +condtion||condition conected||connected conector||connector configed||configured @@ -428,13 +429,13 @@ continous||continuous continously||continuously continueing||continuing contiuous||continuous -contraints||constraints -contruct||construct contol||control contoller||controller +contraints||constraints controled||controlled controler||controller controll||control +contruct||construct contruction||construction contry||country conuntry||country @@ -465,10 +466,9 @@ debouce||debounce decendant||descendant decendants||descendants decompres||decompress -decsribed||described decrese||decrease decription||description -detault||default +decsribed||described dectected||detected defailt||default deferal||deferral @@ -482,9 +482,9 @@ defintion||definition defintions||definitions defualt||default defult||default -deintializing||deinitializing -deintialize||deinitialize deintialized||deinitialized +deintialize||deinitialize +deintializing||deinitializing deivce||device delared||declared delare||declare @@ -494,8 +494,8 @@ delemiter||delimiter deley||delay delibrately||deliberately delievered||delivered -demodualtor||demodulator demension||dimension +demodualtor||demodulator dependancies||dependencies dependancy||dependency dependant||dependent @@ -505,15 +505,15 @@ depreacte||deprecate desactivate||deactivate desciptor||descriptor desciptors||descriptors -descritpor||descriptor descripto||descriptor descripton||description descrition||description +descritpor||descriptor descritptor||descriptor desctiptor||descriptor +desination||destination desriptor||descriptor desriptors||descriptors -desination||destination destionation||destination destoried||destroyed destory||destroy @@ -521,6 +521,7 @@ destoryed||destroyed destorys||destroys destroied||destroyed detabase||database +detault||default deteced||detected detecion||detection detectt||detect @@ -535,55 +536,54 @@ deveolpment||development devided||divided deviece||device devision||division -diable||disable diabled||disabled +diable||disable dicline||decline +diconnected||disconnected dictionnary||dictionary didnt||didn't diferent||different -differrence||difference -diffrent||different differenciate||differentiate +differrence||difference diffreential||differential +diffrent||different diffrentiate||differentiate difinition||definition digial||digital dimention||dimension dimesions||dimensions -diconnected||disconnected -disabed||disabled -disasembler||disassembler -disble||disable -disgest||digest -disired||desired -dispalying||displaying -dissable||disable -dissapeared||disappeared diplay||display -directon||direction direcly||directly +directon||direction direectly||directly diregard||disregard -disassocation||disassociation -disassocative||disassociative +disabed||disabled disapear||disappear disapeared||disappeared disappared||disappeared -disbale||disable +disasembler||disassembler +disassocation||disassociation +disassocative||disassociative disbaled||disabled -disble||disable +disbale||disable disbled||disabled +disble||disable +disble||disable disconnet||disconnect discontinous||discontinuous +disgest||digest disharge||discharge +disired||desired disnabled||disabled +dispalying||displaying dispertion||dispersion +dissable||disable +dissapeared||disappeared dissapears||disappears dissconect||disconnect distiction||distinction divisable||divisible divsiors||divisors -dsiabled||disabled docuentation||documentation documantation||documentation documentaion||documentation @@ -598,6 +598,7 @@ downlads||downloads droped||dropped droput||dropout druing||during +dsiabled||disabled dyanmic||dynamic dynmaic||dynamic eanable||enable @@ -621,20 +622,20 @@ enble||enable enchanced||enhanced encorporating||incorporating encrupted||encrypted -encrypiton||encryption encryped||encrypted +encrypiton||encryption encryptio||encryption endianess||endianness -enpoint||endpoint enhaced||enhanced enlightnment||enlightenment +enocded||encoded +enought||enough +enpoint||endpoint enqueing||enqueuing +enterily||entirely entires||entries entites||entities entrys||entries -enocded||encoded -enought||enough -enterily||entirely enviroiment||environment enviroment||environment environement||environment @@ -653,8 +654,9 @@ evalute||evaluate evalutes||evaluates evalution||evaluation evaulated||evaluated -excecutable||executable +exaclty||exactly excceed||exceed +excecutable||executable exceded||exceeded exceds||exceeds exceeed||exceed @@ -668,41 +670,41 @@ exeuction||execution existance||existence existant||existent exixt||exist -exsits||exists exlcude||exclude exlcuding||excluding exlcusive||exclusive -exlusive||exclusive exlicitly||explicitly +exlusive||exclusive exmaple||example expecially||especially experies||expires explicite||explicit -explicity||explicitly explicitely||explicitly -explict||explicit +explicity||explicitly explictely||explicitly +explict||explicit explictly||explicitly expresion||expression exprienced||experienced exprimental||experimental -extened||extended +exsits||exists exteneded||extended +extened||extended extensability||extensibility -extention||extension extenstion||extension +extention||extension extracter||extractor faied||failed faield||failed -faild||failed failded||failed +faild||failed failer||failure -faill||fail failied||failed +faill||fail faillure||failure +failng||failing failue||failure failuer||failure -failng||failing faireness||fairness falied||failed faliure||failure @@ -717,15 +719,15 @@ fetcing||fetching fileystem||filesystem fimrware||firmware fimware||firmware +finanize||finalize +findn||find +finilizes||finalizes +finsih||finish firmare||firmware firmaware||firmware firtly||firstly firware||firmware firwmare||firmware -finanize||finalize -findn||find -finilizes||finalizes -finsih||finish fliter||filter flusing||flushing folloing||following @@ -742,9 +744,9 @@ forwared||forwarded frambuffer||framebuffer framming||framing framwork||framework +frequancy||frequency frequence||frequency frequncy||frequency -frequancy||frequency frome||from fronend||frontend fucntion||function @@ -766,9 +768,9 @@ gatable||gateable gateing||gating gauage||gauge gaurenteed||guaranteed -generiously||generously genereate||generate genereted||generated +generiously||generously genric||generic gerenal||general geting||getting @@ -790,18 +792,17 @@ hanlde||handle hanled||handled happend||happened hardare||hardware -harware||hardware hardward||hardware +harware||hardware havind||having +hearbeat||heartbeat heigth||height +heirachy||hierarchy heirarchically||hierarchically heirarchy||hierarchy -heirachy||hierarchy helpfull||helpful -hearbeat||heartbeat heterogenous||heterogeneous hexdecimal||hexadecimal -hybernate||hibernate hiearchy||hierarchy hierachy||hierarchy hierarchie||hierarchy @@ -809,14 +810,14 @@ homogenous||homogeneous horizental||horizontal howver||however hsould||should +hybernate||hibernate hypervior||hypervisor hypter||hyper idel||idle identidier||identifier iligal||illegal -illigal||illegal illgal||illegal -iomaped||iomapped +illigal||illegal imblance||imbalance immeadiately||immediately immedaite||immediate @@ -831,13 +832,14 @@ implemantation||implementation implemenation||implementation implementaiton||implementation implementated||implemented -implemention||implementation implementd||implemented +implemention||implementation implemetation||implementation implemntation||implementation implentation||implementation implmentation||implementation implmenting||implementing +inavlid||invalid incative||inactive incomming||incoming incompaitiblity||incompatibility @@ -869,9 +871,9 @@ infromation||information ingore||ignore inheritence||inheritance inital||initial -initalized||initialized initalised||initialized initalise||initialize +initalized||initialized initalize||initialize initation||initiation initators||initiators @@ -879,20 +881,20 @@ initialiazation||initialization initializationg||initialization initializiation||initialization initializtion||initialization -initialze||initialize initialzed||initialized +initialze||initialize initialzing||initializing initilization||initialization +initilized||initialized initilize||initialize initliaze||initialize -initilized||initialized inofficial||unofficial inrerface||interface insititute||institute instace||instance instal||install -instanciate||instantiate instanciated||instantiated +instanciate||instantiate instuments||instruments insufficent||insufficient intead||instead @@ -911,16 +913,16 @@ intergrated||integrated intermittant||intermittent internel||internal interoprability||interoperability -interuupt||interrupt -interupt||interrupt -interupts||interrupts -interurpt||interrupt interrface||interface interrrupt||interrupt interrup||interrupt interrups||interrupts interruptted||interrupted interupted||interrupted +interupt||interrupt +interupts||interrupts +interurpt||interrupt +interuupt||interrupt intiailized||initialized intial||initial intialisation||initialisation @@ -934,18 +936,18 @@ intrerrupt||interrupt intrrupt||interrupt intterrupt||interrupt intuative||intuitive -inavlid||invalid invaid||invalid invaild||invalid invailid||invalid -invald||invalid invalde||invalid +invald||invalid invalide||invalid invalidiate||invalidate invalud||invalid invididual||individual invokation||invocation invokations||invocations +iomaped||iomapped ireelevant||irrelevant irrelevent||irrelevant isnt||isn't @@ -991,11 +993,11 @@ losted||lost maangement||management machinary||machinery maibox||mailbox +mailformed||malformed maintainance||maintenance maintainence||maintenance maintan||maintain makeing||making -mailformed||malformed malplaced||misplaced malplace||misplace managable||manageable @@ -1005,21 +1007,22 @@ mangement||management manger||manager manoeuvering||maneuvering manufaucturing||manufacturing -mappping||mapping maping||mapping +mappping||mapping matchs||matches mathimatical||mathematical mathimatic||mathematic mathimatics||mathematics -maxmium||maximum maximium||maximum maxium||maximum +maxmium||maximum mechamism||mechanism mechanim||mechanism meetign||meeting memeory||memory memmber||member memoery||memory +memomry||memory memroy||memory ment||meant mergable||mergeable @@ -1036,19 +1039,19 @@ migrateable||migratable miliseconds||milliseconds millenium||millennium milliseonds||milliseconds -minimim||minimum -minium||minimum minimam||minimum +minimim||minimum minimun||minimum +minium||minimum miniumum||minimum minumum||minimum misalinged||misaligned miscelleneous||miscellaneous misformed||malformed -mispelled||misspelled -mispelt||misspelt mising||missing mismactch||mismatch +mispelled||misspelled +mispelt||misspelt missign||missing missmanaged||mismanaged missmatch||mismatch @@ -1061,18 +1064,17 @@ modifer||modifier modul||module modulues||modules momery||memory -memomry||memory monitring||monitoring monochorome||monochrome monochromo||monochrome monocrome||monochrome mopdule||module mroe||more -mulitplied||multiplied muliple||multiple -multipler||multiplier +mulitplied||multiplied multidimensionnal||multidimensional multipe||multiple +multipler||multiplier multple||multiple mumber||number muticast||multicast @@ -1094,6 +1096,7 @@ nerver||never nescessary||necessary nessessary||necessary none existent||non-existent +notfify||notify noticable||noticeable notication||notification notications||notifications @@ -1101,7 +1104,6 @@ notifcations||notifications notifed||notified notifer||notifier notity||notify -notfify||notify nubmer||number numebr||number numer||number @@ -1119,10 +1121,10 @@ occurence||occurrence occure||occurred occuring||occurring ocurrence||occurrence -offser||offset offet||offset offlaod||offload offloded||offloaded +offser||offset offseting||offsetting oflload||offload omited||omitted @@ -1141,25 +1143,25 @@ optionnal||optional optmizations||optimizations orientatied||orientated orientied||oriented -orignal||original originial||original +orignal||original orphanded||orphaned otherise||otherwise ouput||output oustanding||outstanding -overaall||overall -overhread||overhead -overlaping||overlapping oveflow||overflow +overaall||overall overflw||overflow -overlfow||overflow +overhread||overhead overide||override +overlaping||overlapping +overlfow||overflow overrided||overridden overriden||overridden overrrun||overrun overun||overrun -overwritting||overwriting overwriten||overwritten +overwritting||overwriting pacakge||package pachage||package packacge||package @@ -1169,11 +1171,11 @@ packtes||packets pakage||package paket||packet pallette||palette -paln||plan palne||plane +paln||plan paramameters||parameters -paramaters||parameters paramater||parameter +paramaters||parameters paramenters||parameters parametes||parameters parametised||parametrised @@ -1241,8 +1243,6 @@ prefered||preferred prefferably||preferably prefitler||prefilter preform||perform -previleged||privileged -previlege||privilege premption||preemption prepaired||prepared prepate||prepare @@ -1250,6 +1250,8 @@ preperation||preparation preprare||prepare pressre||pressure presuambly||presumably +previleged||privileged +previlege||privilege previosuly||previously previsously||previously primative||primitive @@ -1258,17 +1260,17 @@ priorty||priority priting||printing privilaged||privileged privilage||privilege -priviledge||privilege priviledged||privileged +priviledge||privilege priviledges||privileges privleges||privileges -probaly||probably probabalistic||probabilistic +probaly||probably procceed||proceed proccesors||processors procesed||processed -proces||process procesing||processing +proces||process processessing||processing processess||processes processpr||processor @@ -1288,6 +1290,7 @@ progresss||progress prohibitted||prohibited prohibitting||prohibiting promiscous||promiscuous +promixity||proximity promps||prompts pronnounced||pronounced prononciation||pronunciation @@ -1296,15 +1299,14 @@ pronunce||pronounce propery||property propigate||propagate propigation||propagation -propogation||propagation propogate||propagate +propogation||propagation prosess||process protable||portable protcol||protocol protecion||protection protedcted||protected protocoll||protocol -promixity||proximity psudo||pseudo psuedo||pseudo psychadelic||psychedelic @@ -1333,8 +1335,8 @@ recieves||receives recieving||receiving recogniced||recognised recognizeable||recognizable -recompte||recompute recommanded||recommended +recompte||recompute recyle||recycle redect||reject redircet||redirect @@ -1344,8 +1346,8 @@ reename||rename refcounf||refcount refence||reference refered||referred -referencce||reference referenace||reference +referencce||reference refererence||reference refering||referring refernces||references @@ -1353,8 +1355,8 @@ refernnce||reference refrence||reference regiser||register registed||registered -registerd||registered registeration||registration +registerd||registered registeresd||registered registerred||registered registes||registers @@ -1371,8 +1373,8 @@ reloade||reload remoote||remote remore||remote removeable||removable -repective||respective repectively||respectively +repective||respective replacable||replaceable replacments||replacements replys||replies @@ -1389,8 +1391,8 @@ requieres||requires requirment||requirement requred||required requried||required -requst||request requsted||requested +requst||request reregisteration||reregistration reseting||resetting reseved||reserved @@ -1412,11 +1414,11 @@ retransmited||retransmitted retreived||retrieved retreive||retrieve retreiving||retrieving -retrive||retrieve retrived||retrieved +retrive||retrieve retrun||return -retun||return retuned||returned +retun||return reudce||reduce reuest||request reuqest||request @@ -1464,9 +1466,9 @@ seperate||separate seperatly||separately seperator||separator sepperate||separate -seqeunce||sequence -seqeuncer||sequencer seqeuencer||sequencer +seqeuncer||sequencer +seqeunce||sequence sequece||sequence sequemce||sequence sequencial||sequential @@ -1505,8 +1507,8 @@ soley||solely soluation||solution souce||source speach||speech -specfic||specific specfication||specification +specfic||specific specfield||specified speciefied||specified specifc||specific @@ -1515,8 +1517,8 @@ specificatin||specification specificaton||specification specificed||specified specifing||specifying -specifiy||specify specifiying||specifying +specifiy||specify speficied||specified speicify||specify speling||spelling @@ -1543,23 +1545,23 @@ stoppped||stopped straming||streaming struc||struct structres||structures -stuct||struct strucuture||structure +stuct||struct stucture||structure sturcture||structure subdirectoires||subdirectories suble||subtle -substract||subtract submited||submitted submition||submission +substract||subtract succeded||succeeded -suceed||succeed -succesfuly||successfully succesfully||successfully succesful||successful +succesfuly||successfully successed||succeeded successfull||successful successfuly||successfully +suceed||succeed sucessfully||successfully sucessful||successful sucess||success @@ -1568,9 +1570,9 @@ superseeded||superseded suplied||supplied suported||supported suport||support -supportet||supported suppored||supported supporing||supporting +supportet||supported supportin||supporting suppoted||supported suppported||supported @@ -1581,27 +1583,27 @@ surpressed||suppressed surpresses||suppresses susbsystem||subsystem suspeneded||suspended -suspsend||suspend suspicously||suspiciously +suspsend||suspend swaping||swapping switchs||switches -swith||switch swithable||switchable -swithc||switch swithced||switched swithcing||switching +swithc||switch swithed||switched swithing||switching +swith||switch swtich||switch +sychronization||synchronization +sychronously||synchronously syfs||sysfs symetric||symmetric synax||syntax synchonized||synchronized -sychronization||synchronization -sychronously||synchronously synchronuously||synchronously -syncronize||synchronize syncronized||synchronized +syncronize||synchronize syncronizing||synchronizing syncronus||synchronous syste||system @@ -1610,16 +1612,17 @@ sythesis||synthesis tagert||target taht||that tained||tainted -tarffic||traffic +tansition||transition tansmit||transmit +tarffic||traffic targetted||targeted targetting||targeting taskelt||tasklet teh||the temeprature||temperature temorary||temporary -temproarily||temporarily temperture||temperature +temproarily||temporarily theads||threads therfore||therefore thier||their @@ -1629,23 +1632,20 @@ threshhold||threshold thresold||threshold throtting||throttling throught||through -tansition||transition -trackling||tracking -troughput||throughput -trys||tries thses||these -tiggers||triggers tiggered||triggered tiggerring||triggering -tipically||typically +tiggers||triggers timeing||timing timming||timing timout||timeout +tipically||typically tmis||this tolarance||tolerance toogle||toggle torerable||tolerable torlence||tolerance +trackling||tracking traget||target traking||tracking tramsmitted||transmitted @@ -1669,20 +1669,20 @@ trasfer||transfer trasmission||transmission trasmitter||transmitter treshold||threshold -trigged||triggered -triggerd||triggered trigerred||triggered trigerring||triggering +trigged||triggered +triggerd||triggered +troughput||throughput trun||turn +trys||tries tunning||tuning ture||true tyep||type udpate||update -updtes||updates uesd||used -unknwon||unknown uknown||unknown -usccess||success +unamed||unnamed uncommited||uncommitted uncompatible||incompatible uncomressed||uncompressed @@ -1691,6 +1691,7 @@ undeflow||underflow undelying||underlying underun||underrun unecessary||unnecessary +uneeded||unneeded unexecpted||unexpected unexepected||unexpected unexpcted||unexpected @@ -1699,26 +1700,24 @@ unexpeted||unexpected unexpexted||unexpected unfortunatelly||unfortunately unifiy||unify -uniterrupted||uninterrupted uninterruptable||uninterruptible unintialized||uninitialized +uniterrupted||uninterrupted unitialized||uninitialized unkmown||unknown unknonw||unknown unknouwn||unknown unknow||unknown +unknwon||unknown unkown||unknown -unamed||unnamed -uneeded||unneeded -unneded||unneeded +unmached||unmatched unneccecary||unnecessary unneccesary||unnecessary unneccessary||unnecessary unnecesary||unnecessary +unneded||unneeded unneedingly||unnecessarily unnsupported||unsupported -unuspported||unsupported -unmached||unmatched unprecise||imprecise unpriviledged||unprivileged unpriviliged||unprivileged @@ -1726,18 +1725,21 @@ unregester||unregister unresgister||unregister unrgesiter||unregister unsinged||unsigned -unstabel||unstable -unsolicted||unsolicited unsolicitied||unsolicited +unsolicted||unsolicited +unstabel||unstable unsuccessfull||unsuccessful unsuported||unsupported untill||until ununsed||unused unuseful||useless +unuspported||unsupported unvalid||invalid upate||update +updtes||updates upsupported||unsupported upto||up to +usccess||success useable||usable usefule||useful usefull||useful @@ -1759,14 +1761,14 @@ variantions||variations varible||variable varient||variant vaule||value -verbse||verbose veify||verify +verbse||verbose verfication||verification veriosn||version -versoin||version verisons||versions verison||version veritical||vertical +versoin||version verson||version vicefersa||vice-versa virtal||virtual @@ -1779,13 +1781,13 @@ wakeus||wakeups was't||wasn't wathdog||watchdog wating||waiting -wiat||wait wether||whether whataver||whatever whcih||which whenver||whenever wheter||whether whe||when +wiat||wait wierd||weird wihout||without wiil||will diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index c1d42fc72fdb..3491e9f60194 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -409,7 +410,7 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d struct path_cond cond_exchange = { .mode = d_backing_inode(new_dentry)->i_mode, }; - vfsuid = i_uid_into_vfsuid(idmap, d_backing_inode(old_dentry)); + vfsuid = i_uid_into_vfsuid(idmap, d_backing_inode(new_dentry)); cond_exchange.uid = vfsuid_into_kuid(vfsuid); error = aa_path_perm(OP_RENAME_SRC, current_cred(), @@ -822,25 +823,23 @@ static int apparmor_getprocattr(struct task_struct *task, const char *name, char **value) { int error = -ENOENT; - /* released below */ - const struct cred *cred = get_task_cred(task); - struct aa_task_ctx *ctx = task_ctx(current); struct aa_label *label = NULL; + rcu_read_lock(); if (strcmp(name, "current") == 0) - label = aa_get_newest_label(cred_label(cred)); - else if (strcmp(name, "prev") == 0 && ctx->previous) - label = aa_get_newest_label(ctx->previous); - else if (strcmp(name, "exec") == 0 && ctx->onexec) - label = aa_get_newest_label(ctx->onexec); + label = aa_get_newest_cred_label(__task_cred(task)); + else if (strcmp(name, "prev") == 0 && task_ctx(task)->previous) + label = aa_get_newest_label(task_ctx(task)->previous); + else if (strcmp(name, "exec") == 0 && task_ctx(task)->onexec) + label = aa_get_newest_label(task_ctx(task)->onexec); else error = -EINVAL; + rcu_read_unlock(); if (label) error = aa_getprocattr(label, value, true); aa_put_label(label); - put_cred(cred); return error; } @@ -858,12 +857,9 @@ static int do_setattr(u64 attr, void *value, size_t size) /* AppArmor requires that the buffer must be null terminated atm */ if (args[size - 1] != '\0') { - /* null terminate */ - largs = args = kmalloc(size + 1, GFP_KERNEL); + largs = args = kmemdup_nul(value, size, GFP_KERNEL); if (!args) return -ENOMEM; - memcpy(args, value, size); - args[size] = '\0'; } error = -EINVAL; @@ -1528,15 +1524,11 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) static struct aa_label *sk_peer_get_label(struct sock *sk) { struct aa_sk_ctx *ctx = aa_sock(sk); - struct aa_label *label = ERR_PTR(-ENOPROTOOPT); if (rcu_access_pointer(ctx->peer)) return aa_get_label_rcu(&ctx->peer); - if (sk->sk_family != PF_UNIX) - return ERR_PTR(-ENOPROTOOPT); - - return label; + return ERR_PTR(-ENOPROTOOPT); } /** @@ -2073,7 +2065,7 @@ static int param_get_audit(char *buffer, const struct kernel_param *kp) return -EINVAL; if (apparmor_initialized && !aa_current_policy_view_capable(NULL)) return -EPERM; - return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]); + return sysfs_emit(buffer, "%s\n", audit_mode_names[aa_g_audit]); } static int param_set_audit(const char *val, const struct kernel_param *kp) @@ -2101,8 +2093,7 @@ static int param_get_mode(char *buffer, const struct kernel_param *kp) return -EINVAL; if (apparmor_initialized && !aa_current_policy_view_capable(NULL)) return -EPERM; - - return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]); + return sysfs_emit(buffer, "%s\n", aa_profile_mode_names[aa_g_profile_mode]); } static int param_set_mode(const char *val, const struct kernel_param *kp) @@ -2465,6 +2456,7 @@ static int __init aa_setup_dfa_engine(void) TO_ACCEPT2_FLAG(YYTD_DATA32)); if (IS_ERR(nulldfa)) { error = PTR_ERR(nulldfa); + nulldfa = NULL; goto fail; } nullpdb->dfa = aa_get_dfa(nulldfa); diff --git a/security/apparmor/match.c b/security/apparmor/match.c index e9fac67e5178..3a2c6cf02b3c 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -157,7 +157,7 @@ static int verify_dfa(struct aa_dfa *dfa) state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen; - if (state_count == 0) + if (state_count < 2) goto out; for (i = 0; i < state_count; i++) { if (DEFAULT_TABLE(dfa)[i] >= state_count) { diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 65a0ca5cc1bd..2494e8101538 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -164,14 +164,16 @@ static int d_namespace_path(const struct path *path, char *buf, char **name, } out: - /* Append "/" to directory paths, except for root "/" which - * already ends in a slash. + /* Append "/" to directory paths and reterminate string, except for + * root "/" which already ends in a slash. */ if (!error && isdir) { bool is_root = (*name)[0] == '/' && (*name)[1] == '\0'; - if (!is_root) + if (!is_root) { buf[aa_g_path_max - 2] = '/'; + buf[aa_g_path_max - 1] = '\0'; + } } return error; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 076d3ff14da6..9f45d5513d2c 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -879,6 +879,7 @@ static int unpack_tags(struct aa_ext *e, struct aa_tags_struct *tags, *info = "failed to unpack profile tag.sets"; goto fail; } + error = -EPROTO; if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; @@ -1465,6 +1466,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) if (*ns && strcmp(*ns, name)) { audit_iface(NULL, NULL, NULL, "invalid ns change", e, error); + return error; } else if (!*ns) { *ns = kstrdup(name, GFP_KERNEL); if (!*ns) diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 92b63039c654..45dfdedbdad4 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -18,6 +18,7 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \ platform_certs/load_powerpc.o \ platform_certs/keyring_handler.o +integrity-$(CONFIG_EFI) += efi_secureboot.o # The relative order of the 'ima' and 'evm' LSMs depends on the order below. obj-$(CONFIG_IMA) += ima/ obj-$(CONFIG_EVM) += evm/ diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 75c684cce370..1ed686154d7a 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -59,7 +59,7 @@ static struct key *integrity_keyring_from_id(const unsigned int id) } int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, - const char *digest, int digestlen) + const char *digest, int digestlen, u8 algo) { struct key *keyring; @@ -76,9 +76,11 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, return digsig_verify(keyring, sig + 1, siglen - 1, digest, digestlen); case 2: /* regular file data hash based signature */ - case 3: /* struct ima_file_id data based signature */ return asymmetric_verify(keyring, sig, siglen, digest, - digestlen); + digestlen); + case 3: /* struct ima_file_id data based signature */ + return asymmetric_verify_v3(keyring, sig, siglen, digest, + digestlen, algo); } return -EOPNOTSUPP; diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 87be85f477d1..6e68ec3becbd 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -131,3 +131,62 @@ out: pr_debug("%s() = %d\n", __func__, ret); return ret; } + +/* + * calc_file_id_hash - calculate the hash of the ima_file_id struct data + * @type: xattr type [enum evm_ima_xattr_type] + * @algo: hash algorithm [enum hash_algo] + * @digest: pointer to the digest to be hashed + * @hash: (out) pointer to the hash + * + * IMA signature version 3 disambiguates the data that is signed by + * indirectly signing the hash of the ima_file_id structure data. + * + * Return 0 on success, error code otherwise. + */ +static int calc_file_id_hash(enum evm_ima_xattr_type type, + enum hash_algo algo, const u8 *digest, + struct ima_max_digest_data *hash) +{ + struct ima_file_id file_id = {.hash_type = type, .hash_algorithm = algo}; + size_t digest_size = hash_digest_size[algo]; + struct crypto_shash *tfm; + size_t file_id_size; + int rc; + + if (type != IMA_VERITY_DIGSIG && type != EVM_IMA_XATTR_DIGSIG && + type != EVM_XATTR_PORTABLE_DIGSIG) + return -EINVAL; + + tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + memcpy(file_id.hash, digest, digest_size); + + /* Calculate the ima_file_id struct hash on the portion used. */ + file_id_size = sizeof(file_id) - (HASH_MAX_DIGESTSIZE - digest_size); + + hash->hdr.algo = algo; + hash->hdr.length = digest_size; + rc = crypto_shash_tfm_digest(tfm, (const u8 *)&file_id, file_id_size, + hash->digest); + + crypto_free_shash(tfm); + return rc; +} + +int asymmetric_verify_v3(struct key *keyring, const char *sig, int siglen, + const char *data, int datalen, u8 algo) +{ + struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; + struct ima_max_digest_data hash; + int rc; + + rc = calc_file_id_hash(hdr->type, algo, data, &hash); + if (rc) + return -EINVAL; + + return asymmetric_verify(keyring, sig, siglen, hash.digest, + hash.hdr.length); +} diff --git a/security/integrity/efi_secureboot.c b/security/integrity/efi_secureboot.c new file mode 100644 index 000000000000..bfd4260a83a3 --- /dev/null +++ b/security/integrity/efi_secureboot.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-1.0+ +/* + * Copyright (C) 2018 IBM Corporation + */ +#include +#include +#include + +#ifndef arch_efi_boot_mode +#define arch_efi_boot_mode efi_secureboot_mode_unset +#endif + +static enum efi_secureboot_mode get_sb_mode(void) +{ + enum efi_secureboot_mode mode; + + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) { + pr_info("integrity: secureboot mode unknown, no efi\n"); + return efi_secureboot_mode_unknown; + } + + mode = efi_get_secureboot_mode(efi.get_variable); + if (mode == efi_secureboot_mode_disabled) + pr_info("integrity: secureboot mode disabled\n"); + else if (mode == efi_secureboot_mode_unknown) + pr_info("integrity: secureboot mode unknown\n"); + else + pr_info("integrity: secureboot mode enabled\n"); + return mode; +} + +/* + * Query secure boot status + * + * Note don't call this function too early e.g. in __setup hook otherwise the + * kernel may hang when calling efi_get_secureboot_mode. + * + */ +bool arch_get_secureboot(void) +{ + static enum efi_secureboot_mode sb_mode; + static bool initialized; + + if (!initialized && efi_enabled(EFI_BOOT)) { + sb_mode = arch_efi_boot_mode; + + if (sb_mode == efi_secureboot_mode_unset) + sb_mode = get_sb_mode(); + initialized = true; + } + + if (sb_mode == efi_secureboot_mode_enabled) + return true; + else + return false; +} diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index 51aba5a54275..694552aceaf8 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -20,11 +20,12 @@ #define EVM_INIT_HMAC 0x0001 #define EVM_INIT_X509 0x0002 #define EVM_ALLOW_METADATA_WRITES 0x0004 +#define EVM_SIGV3_REQUIRED 0x0008 #define EVM_SETUP_COMPLETE 0x80000000 /* userland has signaled key load */ #define EVM_KEY_MASK (EVM_INIT_HMAC | EVM_INIT_X509) #define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP_COMPLETE | \ - EVM_ALLOW_METADATA_WRITES) + EVM_ALLOW_METADATA_WRITES | EVM_SIGV3_REQUIRED) struct xattr_list { struct list_head list; diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 41b053c900f2..b59e3f121b8a 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -72,17 +72,25 @@ static struct xattr_list evm_config_default_xattrnames[] = { LIST_HEAD(evm_config_xattrnames); -static int evm_fixmode __ro_after_init; -static int __init evm_set_fixmode(char *str) -{ - if (strncmp(str, "fix", 3) == 0) - evm_fixmode = 1; - else - pr_err("invalid \"%s\" mode", str); +static char *evm_cmdline __initdata; +core_param(evm, evm_cmdline, charp, 0); - return 1; +static int evm_fixmode __ro_after_init; +static void __init evm_set_fixmode(void) +{ + if (!evm_cmdline) + return; + + if (strncmp(evm_cmdline, "fix", 3) == 0) { + if (arch_get_secureboot()) { + pr_info("Secure boot enabled: ignoring evm=fix"); + return; + } + evm_fixmode = 1; + } else { + pr_err("invalid \"%s\" mode", evm_cmdline); + } } -__setup("evm=", evm_set_fixmode); static void __init evm_init_config(void) { @@ -128,6 +136,14 @@ static bool evm_hmac_disabled(void) return true; } +static bool evm_sigv3_required(void) +{ + if (evm_initialized & EVM_SIGV3_REQUIRED) + return true; + + return false; +} + static int evm_find_protected_xattrs(struct dentry *dentry) { struct inode *inode = d_backing_inode(dentry); @@ -250,6 +266,12 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, } hdr = (struct signature_v2_hdr *)xattr_data; + + if (evm_sigv3_required() && hdr->version != 3) { + evm_status = INTEGRITY_FAIL; + goto out; + } + digest.hdr.algo = hdr->hash_algo; rc = evm_calc_hash(dentry, xattr_name, xattr_value, xattr_value_len, xattr_data->type, &digest, @@ -258,7 +280,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, break; rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, (const char *)xattr_data, xattr_len, - digest.digest, digest.hdr.length); + digest.digest, digest.hdr.length, + digest.hdr.algo); if (!rc) { if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) { if (iint) @@ -787,6 +810,34 @@ bool evm_revalidate_status(const char *xattr_name) return true; } +/** + * evm_fix_hmac - Calculate the HMAC and add it to security.evm for fix mode + * @dentry: pointer to the affected dentry which doesn't yet have security.evm + * xattr + * @xattr_name: pointer to the affected extended attribute name + * @xattr_value: pointer to the new extended attribute value + * @xattr_value_len: pointer to the new extended attribute value length + * + * Expects to be called with i_mutex locked. + * + * Return: 0 on success, -EPERM/-ENOMEM/-EOPNOTSUPP on failure + */ +int evm_fix_hmac(struct dentry *dentry, const char *xattr_name, + const char *xattr_value, size_t xattr_value_len) + +{ + if (!evm_fixmode || !evm_revalidate_status((xattr_name))) + return -EPERM; + + if (!(evm_initialized & EVM_INIT_HMAC)) + return -EPERM; + + if (is_unsupported_hmac_fs(dentry)) + return -EOPNOTSUPP; + + return evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); +} + /** * evm_inode_post_setxattr - update 'security.evm' to reflect the changes * @dentry: pointer to the affected dentry @@ -1119,6 +1170,8 @@ static int __init init_evm(void) evm_init_config(); + evm_set_fixmode(); + error = integrity_init_keyring(INTEGRITY_KEYRING_EVM); if (error) goto error; diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 89ebe98ffc5e..69e9bf0b82c6 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -53,6 +53,7 @@ extern atomic_t ima_setxattr_allowed_hash_algorithms; struct ima_algo_desc { struct crypto_shash *tfm; enum hash_algo algo; + unsigned int digest_size; }; /* set during initialization */ @@ -144,6 +145,7 @@ struct ima_kexec_hdr { #define IMA_DIGSIG_REQUIRED 0x01000000 #define IMA_PERMIT_DIRECTIO 0x02000000 #define IMA_NEW_FILE 0x04000000 +#define IMA_SIGV3_REQUIRED 0x08000000 #define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000 #define IMA_MODSIG_ALLOWED 0x20000000 #define IMA_CHECK_BLACKLIST 0x40000000 @@ -176,7 +178,32 @@ struct ima_kexec_hdr { IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \ IMA_CREDS_APPRAISED) -/* IMA iint cache atomic_flags */ +/* + * IMA iint cache atomic_flags + * + * IMA_CHANGE_ATTR - indicates that chATTR() was called (chmod, chown, chgrp) + * and file attributes have changed. On file open, it causes IMA to clear + * iint->flags to re-evaluate policy and perform IMA functions again. + * + * IMA_CHANGE_XATTR - indicates that setxattr or removexattr was called and + * extended attributes have changed. On file open, it causes IMA to clear + * iint->flags IMA_DONE_MASK to re-appraise. + * + * IMA_UPDATE_XATTR - indicates that security.ima needs to be updated. It is + * cleared if file policy changes and no update is needed. + * + * IMA_DIGSIG - indicates that file security.ima has signature and file + * security.ima must not update on file close. + * + * IMA_MAY_EMIT_TOMTOU - indicates to add Time-of-Measure-Time-of-Use (ToMToU) + * integrity violation (a file that is already opened for read is opened for + * write) to the measurement list and to also emit an audit message. + * + * IMA_EMITTED_OPENWRITERS - indicates to add open-writers integrity violation + * (a file that is already opened for write is opened for read) to the + * measurement list and to also emit an audit message. + * + */ #define IMA_CHANGE_XATTR 0 #define IMA_UPDATE_XATTR 1 #define IMA_CHANGE_ATTR 2 diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index d15becc8c640..0916f24f005f 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -269,15 +269,20 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file, goto out; /* - * Detecting file change is based on i_version. On filesystems - * which do not support i_version, support was originally limited - * to an initial measurement/appraisal/audit, but was modified to - * assume the file changed. + * Detect file change based on STATX_CHANGE_COOKIE, when supported, + * and fallback to detecting file change based on i_version. + * + * On filesystems which did not support i_version, support was + * originally limited to an initial measurement/appraisal/audit, + * but was later modified to assume the file changed. */ result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE, AT_STATX_SYNC_AS_STAT); if (!result && (stat.result_mask & STATX_CHANGE_COOKIE)) i_version = stat.change_cookie; + else if (IS_I_VERSION(real_inode)) + i_version = inode_peek_iversion(real_inode); + hash.hdr.algo = algo; hash.hdr.length = hash_digest_size[algo]; diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 16c20c578ea8..de963b9f3634 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -27,7 +27,7 @@ core_param(ima_appraise, ima_appraise_cmdline_default, charp, 0); void __init ima_appraise_parse_cmdline(void) { const char *str = ima_appraise_cmdline_default; - bool sb_state = arch_ima_get_secureboot(); + bool sb_state = arch_get_secureboot(); int appraisal_state = ima_appraise; if (!str) @@ -234,40 +234,6 @@ int ima_read_xattr(struct dentry *dentry, return ret; } -/* - * calc_file_id_hash - calculate the hash of the ima_file_id struct data - * @type: xattr type [enum evm_ima_xattr_type] - * @algo: hash algorithm [enum hash_algo] - * @digest: pointer to the digest to be hashed - * @hash: (out) pointer to the hash - * - * IMA signature version 3 disambiguates the data that is signed by - * indirectly signing the hash of the ima_file_id structure data. - * - * Signing the ima_file_id struct is currently only supported for - * IMA_VERITY_DIGSIG type xattrs. - * - * Return 0 on success, error code otherwise. - */ -static int calc_file_id_hash(enum evm_ima_xattr_type type, - enum hash_algo algo, const u8 *digest, - struct ima_digest_data *hash) -{ - struct ima_file_id file_id = { - .hash_type = IMA_VERITY_DIGSIG, .hash_algorithm = algo}; - unsigned int unused = HASH_MAX_DIGESTSIZE - hash_digest_size[algo]; - - if (type != IMA_VERITY_DIGSIG) - return -EINVAL; - - memcpy(file_id.hash, digest, hash_digest_size[algo]); - - hash->algo = algo; - hash->length = hash_digest_size[algo]; - - return ima_calc_buffer_hash(&file_id, sizeof(file_id) - unused, hash); -} - /* * xattr_verify - verify xattr digest or signature * @@ -279,7 +245,6 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint, struct evm_ima_xattr_data *xattr_value, int xattr_len, enum integrity_status *status, const char **cause) { - struct ima_max_digest_data hash; struct signature_v2_hdr *sig; int rc = -EINVAL, hash_start = 0; int mask; @@ -332,16 +297,24 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint, } sig = (typeof(sig))xattr_value; - if (sig->version >= 3) { + if (sig->version > 3) { *cause = "invalid-signature-version"; *status = INTEGRITY_FAIL; break; } + + if ((iint->flags & IMA_SIGV3_REQUIRED) && sig->version != 3) { + *cause = "IMA-sigv3-required"; + *status = INTEGRITY_FAIL; + break; + } + rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, (const char *)xattr_value, xattr_len, iint->ima_hash->digest, - iint->ima_hash->length); + iint->ima_hash->length, + iint->ima_hash->algo); if (rc == -EOPNOTSUPP) { *status = INTEGRITY_UNKNOWN; break; @@ -352,7 +325,9 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint, (const char *)xattr_value, xattr_len, iint->ima_hash->digest, - iint->ima_hash->length); + iint->ima_hash->length, + iint->ima_hash->algo); + if (rc) { *cause = "invalid-signature"; *status = INTEGRITY_FAIL; @@ -378,21 +353,16 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint, break; } - rc = calc_file_id_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo, - iint->ima_hash->digest, - container_of(&hash.hdr, - struct ima_digest_data, hdr)); - if (rc) { - *cause = "sigv3-hashing-error"; - *status = INTEGRITY_FAIL; - break; - } - rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, (const char *)xattr_value, - xattr_len, hash.digest, - hash.hdr.length); - if (rc) { + xattr_len, + iint->ima_hash->digest, + iint->ima_hash->length, + iint->ima_hash->algo); + if (rc == -EOPNOTSUPP) { + *status = INTEGRITY_UNKNOWN; + break; + } else if (rc) { *cause = "invalid-verity-signature"; *status = INTEGRITY_FAIL; } else { @@ -591,6 +561,11 @@ out: xattr_value->type != EVM_IMA_XATTR_DIGSIG)) { if (!ima_fix_xattr(dentry, iint)) status = INTEGRITY_PASS; + } else if (status == INTEGRITY_NOLABEL) { + if (!evm_fix_hmac(dentry, XATTR_NAME_IMA, + (const char *)xattr_value, + xattr_len)) + status = INTEGRITY_PASS; } /* diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index aff61643415d..0d72b48249ee 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -11,51 +11,15 @@ */ #include -#include -#include #include #include -#include #include #include #include #include "ima.h" -/* minimum file size for ahash use */ -static unsigned long ima_ahash_minsize; -module_param_named(ahash_minsize, ima_ahash_minsize, ulong, 0644); -MODULE_PARM_DESC(ahash_minsize, "Minimum file size for ahash use"); - -/* default is 0 - 1 page. */ -static int ima_maxorder; -static unsigned int ima_bufsize = PAGE_SIZE; - -static int param_set_bufsize(const char *val, const struct kernel_param *kp) -{ - unsigned long long size; - int order; - - size = memparse(val, NULL); - order = get_order(size); - if (order > MAX_PAGE_ORDER) - return -EINVAL; - ima_maxorder = order; - ima_bufsize = PAGE_SIZE << order; - return 0; -} - -static const struct kernel_param_ops param_ops_bufsize = { - .set = param_set_bufsize, - .get = param_get_uint, -}; -#define param_check_bufsize(name, p) __param_check(name, p, unsigned int) - -module_param_named(ahash_bufsize, ima_bufsize, bufsize, 0644); -MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size"); - static struct crypto_shash *ima_shash_tfm; -static struct crypto_ahash *ima_ahash_tfm; int ima_sha1_idx __ro_after_init; int ima_hash_algo_idx __ro_after_init; @@ -109,6 +73,7 @@ static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo) int __init ima_init_crypto(void) { + unsigned int digest_size; enum hash_algo algo; long rc; int i; @@ -147,7 +112,9 @@ int __init ima_init_crypto(void) for (i = 0; i < NR_BANKS(ima_tpm_chip); i++) { algo = ima_tpm_chip->allocated_banks[i].crypto_id; + digest_size = ima_tpm_chip->allocated_banks[i].digest_size; ima_algo_array[i].algo = algo; + ima_algo_array[i].digest_size = digest_size; /* unknown TPM algorithm */ if (algo == HASH_ALGO__LAST) @@ -183,12 +150,15 @@ int __init ima_init_crypto(void) } ima_algo_array[ima_sha1_idx].algo = HASH_ALGO_SHA1; + ima_algo_array[ima_sha1_idx].digest_size = SHA1_DIGEST_SIZE; } if (ima_hash_algo_idx >= NR_BANKS(ima_tpm_chip) && ima_hash_algo_idx != ima_sha1_idx) { + digest_size = hash_digest_size[ima_hash_algo]; ima_algo_array[ima_hash_algo_idx].tfm = ima_shash_tfm; ima_algo_array[ima_hash_algo_idx].algo = ima_hash_algo; + ima_algo_array[ima_hash_algo_idx].digest_size = digest_size; } return 0; @@ -220,234 +190,6 @@ static void ima_free_tfm(struct crypto_shash *tfm) crypto_free_shash(tfm); } -/** - * ima_alloc_pages() - Allocate contiguous pages. - * @max_size: Maximum amount of memory to allocate. - * @allocated_size: Returned size of actual allocation. - * @last_warn: Should the min_size allocation warn or not. - * - * Tries to do opportunistic allocation for memory first trying to allocate - * max_size amount of memory and then splitting that until zero order is - * reached. Allocation is tried without generating allocation warnings unless - * last_warn is set. Last_warn set affects only last allocation of zero order. - * - * By default, ima_maxorder is 0 and it is equivalent to kmalloc(GFP_KERNEL) - * - * Return pointer to allocated memory, or NULL on failure. - */ -static void *ima_alloc_pages(loff_t max_size, size_t *allocated_size, - int last_warn) -{ - void *ptr; - int order = ima_maxorder; - gfp_t gfp_mask = __GFP_RECLAIM | __GFP_NOWARN | __GFP_NORETRY; - - if (order) - order = min(get_order(max_size), order); - - for (; order; order--) { - ptr = (void *)__get_free_pages(gfp_mask, order); - if (ptr) { - *allocated_size = PAGE_SIZE << order; - return ptr; - } - } - - /* order is zero - one page */ - - gfp_mask = GFP_KERNEL; - - if (!last_warn) - gfp_mask |= __GFP_NOWARN; - - ptr = (void *)__get_free_pages(gfp_mask, 0); - if (ptr) { - *allocated_size = PAGE_SIZE; - return ptr; - } - - *allocated_size = 0; - return NULL; -} - -/** - * ima_free_pages() - Free pages allocated by ima_alloc_pages(). - * @ptr: Pointer to allocated pages. - * @size: Size of allocated buffer. - */ -static void ima_free_pages(void *ptr, size_t size) -{ - if (!ptr) - return; - free_pages((unsigned long)ptr, get_order(size)); -} - -static struct crypto_ahash *ima_alloc_atfm(enum hash_algo algo) -{ - struct crypto_ahash *tfm = ima_ahash_tfm; - int rc; - - if (algo < 0 || algo >= HASH_ALGO__LAST) - algo = ima_hash_algo; - - if (algo != ima_hash_algo || !tfm) { - tfm = crypto_alloc_ahash(hash_algo_name[algo], 0, 0); - if (!IS_ERR(tfm)) { - if (algo == ima_hash_algo) - ima_ahash_tfm = tfm; - } else { - rc = PTR_ERR(tfm); - pr_err("Can not allocate %s (reason: %d)\n", - hash_algo_name[algo], rc); - } - } - return tfm; -} - -static void ima_free_atfm(struct crypto_ahash *tfm) -{ - if (tfm != ima_ahash_tfm) - crypto_free_ahash(tfm); -} - -static inline int ahash_wait(int err, struct crypto_wait *wait) -{ - - err = crypto_wait_req(err, wait); - - if (err) - pr_crit_ratelimited("ahash calculation failed: err: %d\n", err); - - return err; -} - -static int ima_calc_file_hash_atfm(struct file *file, - struct ima_digest_data *hash, - struct crypto_ahash *tfm) -{ - loff_t i_size, offset; - char *rbuf[2] = { NULL, }; - int rc, rbuf_len, active = 0, ahash_rc = 0; - struct ahash_request *req; - struct scatterlist sg[1]; - struct crypto_wait wait; - size_t rbuf_size[2]; - - hash->length = crypto_ahash_digestsize(tfm); - - req = ahash_request_alloc(tfm, GFP_KERNEL); - if (!req) - return -ENOMEM; - - crypto_init_wait(&wait); - ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | - CRYPTO_TFM_REQ_MAY_SLEEP, - crypto_req_done, &wait); - - rc = ahash_wait(crypto_ahash_init(req), &wait); - if (rc) - goto out1; - - i_size = i_size_read(file_inode(file)); - - if (i_size == 0) - goto out2; - - /* - * Try to allocate maximum size of memory. - * Fail if even a single page cannot be allocated. - */ - rbuf[0] = ima_alloc_pages(i_size, &rbuf_size[0], 1); - if (!rbuf[0]) { - rc = -ENOMEM; - goto out1; - } - - /* Only allocate one buffer if that is enough. */ - if (i_size > rbuf_size[0]) { - /* - * Try to allocate secondary buffer. If that fails fallback to - * using single buffering. Use previous memory allocation size - * as baseline for possible allocation size. - */ - rbuf[1] = ima_alloc_pages(i_size - rbuf_size[0], - &rbuf_size[1], 0); - } - - for (offset = 0; offset < i_size; offset += rbuf_len) { - if (!rbuf[1] && offset) { - /* Not using two buffers, and it is not the first - * read/request, wait for the completion of the - * previous ahash_update() request. - */ - rc = ahash_wait(ahash_rc, &wait); - if (rc) - goto out3; - } - /* read buffer */ - rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]); - rc = integrity_kernel_read(file, offset, rbuf[active], - rbuf_len); - if (rc != rbuf_len) { - if (rc >= 0) - rc = -EINVAL; - /* - * Forward current rc, do not overwrite with return value - * from ahash_wait() - */ - ahash_wait(ahash_rc, &wait); - goto out3; - } - - if (rbuf[1] && offset) { - /* Using two buffers, and it is not the first - * read/request, wait for the completion of the - * previous ahash_update() request. - */ - rc = ahash_wait(ahash_rc, &wait); - if (rc) - goto out3; - } - - sg_init_one(&sg[0], rbuf[active], rbuf_len); - ahash_request_set_crypt(req, sg, NULL, rbuf_len); - - ahash_rc = crypto_ahash_update(req); - - if (rbuf[1]) - active = !active; /* swap buffers, if we use two */ - } - /* wait for the last update request to complete */ - rc = ahash_wait(ahash_rc, &wait); -out3: - ima_free_pages(rbuf[0], rbuf_size[0]); - ima_free_pages(rbuf[1], rbuf_size[1]); -out2: - if (!rc) { - ahash_request_set_crypt(req, NULL, hash->digest, 0); - rc = ahash_wait(crypto_ahash_final(req), &wait); - } -out1: - ahash_request_free(req); - return rc; -} - -static int ima_calc_file_ahash(struct file *file, struct ima_digest_data *hash) -{ - struct crypto_ahash *tfm; - int rc; - - tfm = ima_alloc_atfm(hash->algo); - if (IS_ERR(tfm)) - return PTR_ERR(tfm); - - rc = ima_calc_file_hash_atfm(file, hash, tfm); - - ima_free_atfm(tfm); - - return rc; -} - static int ima_calc_file_hash_tfm(struct file *file, struct ima_digest_data *hash, struct crypto_shash *tfm) @@ -499,41 +241,15 @@ out: return rc; } -static int ima_calc_file_shash(struct file *file, struct ima_digest_data *hash) -{ - struct crypto_shash *tfm; - int rc; - - tfm = ima_alloc_tfm(hash->algo); - if (IS_ERR(tfm)) - return PTR_ERR(tfm); - - rc = ima_calc_file_hash_tfm(file, hash, tfm); - - ima_free_tfm(tfm); - - return rc; -} - /* * ima_calc_file_hash - calculate file hash - * - * Asynchronous hash (ahash) allows using HW acceleration for calculating - * a hash. ahash performance varies for different data sizes on different - * crypto accelerators. shash performance might be better for smaller files. - * The 'ima.ahash_minsize' module parameter allows specifying the best - * minimum file size for using ahash on the system. - * - * If the ima.ahash_minsize parameter is not specified, this function uses - * shash for the hash calculation. If ahash fails, it falls back to using - * shash. */ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) { - loff_t i_size; int rc; struct file *f = file; bool new_file_instance = false; + struct crypto_shash *tfm; /* * For consistency, fail file's opened with the O_DIRECT flag on @@ -557,16 +273,13 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) new_file_instance = true; } - i_size = i_size_read(file_inode(f)); - - if (ima_ahash_minsize && i_size >= ima_ahash_minsize) { - rc = ima_calc_file_ahash(f, hash); - if (!rc) - goto out; + tfm = ima_alloc_tfm(hash->algo); + if (IS_ERR(tfm)) { + rc = PTR_ERR(tfm); + } else { + rc = ima_calc_file_hash_tfm(f, hash, tfm); + ima_free_tfm(tfm); } - - rc = ima_calc_file_shash(f, hash); -out: if (new_file_instance) fput(f); return rc; @@ -655,63 +368,6 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data, return rc; } -static int calc_buffer_ahash_atfm(const void *buf, loff_t len, - struct ima_digest_data *hash, - struct crypto_ahash *tfm) -{ - struct ahash_request *req; - struct scatterlist sg; - struct crypto_wait wait; - int rc, ahash_rc = 0; - - hash->length = crypto_ahash_digestsize(tfm); - - req = ahash_request_alloc(tfm, GFP_KERNEL); - if (!req) - return -ENOMEM; - - crypto_init_wait(&wait); - ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | - CRYPTO_TFM_REQ_MAY_SLEEP, - crypto_req_done, &wait); - - rc = ahash_wait(crypto_ahash_init(req), &wait); - if (rc) - goto out; - - sg_init_one(&sg, buf, len); - ahash_request_set_crypt(req, &sg, NULL, len); - - ahash_rc = crypto_ahash_update(req); - - /* wait for the update request to complete */ - rc = ahash_wait(ahash_rc, &wait); - if (!rc) { - ahash_request_set_crypt(req, NULL, hash->digest, 0); - rc = ahash_wait(crypto_ahash_final(req), &wait); - } -out: - ahash_request_free(req); - return rc; -} - -static int calc_buffer_ahash(const void *buf, loff_t len, - struct ima_digest_data *hash) -{ - struct crypto_ahash *tfm; - int rc; - - tfm = ima_alloc_atfm(hash->algo); - if (IS_ERR(tfm)) - return PTR_ERR(tfm); - - rc = calc_buffer_ahash_atfm(buf, len, hash, tfm); - - ima_free_atfm(tfm); - - return rc; -} - static int calc_buffer_shash_tfm(const void *buf, loff_t size, struct ima_digest_data *hash, struct crypto_shash *tfm) @@ -742,8 +398,8 @@ static int calc_buffer_shash_tfm(const void *buf, loff_t size, return rc; } -static int calc_buffer_shash(const void *buf, loff_t len, - struct ima_digest_data *hash) +int ima_calc_buffer_hash(const void *buf, loff_t len, + struct ima_digest_data *hash) { struct crypto_shash *tfm; int rc; @@ -758,20 +414,6 @@ static int calc_buffer_shash(const void *buf, loff_t len, return rc; } -int ima_calc_buffer_hash(const void *buf, loff_t len, - struct ima_digest_data *hash) -{ - int rc; - - if (ima_ahash_minsize && len >= ima_ahash_minsize) { - rc = calc_buffer_ahash(buf, len, hash); - if (!rc) - return 0; - } - - return calc_buffer_shash(buf, len, hash); -} - static void ima_pcrread(u32 idx, struct tpm_digest *d) { if (!ima_tpm_chip) @@ -832,7 +474,7 @@ static int ima_calc_boot_aggregate_tfm(char *digest, u16 alg_id, } } if (!rc) - crypto_shash_final(shash, digest); + rc = crypto_shash_final(shash, digest); return rc; } diff --git a/security/integrity/ima/ima_efi.c b/security/integrity/ima/ima_efi.c index 138029bfcce1..bca57d836cb9 100644 --- a/security/integrity/ima/ima_efi.c +++ b/security/integrity/ima/ima_efi.c @@ -2,52 +2,9 @@ /* * Copyright (C) 2018 IBM Corporation */ -#include #include #include -#include - -#ifndef arch_ima_efi_boot_mode -#define arch_ima_efi_boot_mode efi_secureboot_mode_unset -#endif - -static enum efi_secureboot_mode get_sb_mode(void) -{ - enum efi_secureboot_mode mode; - - if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) { - pr_info("ima: secureboot mode unknown, no efi\n"); - return efi_secureboot_mode_unknown; - } - - mode = efi_get_secureboot_mode(efi.get_variable); - if (mode == efi_secureboot_mode_disabled) - pr_info("ima: secureboot mode disabled\n"); - else if (mode == efi_secureboot_mode_unknown) - pr_info("ima: secureboot mode unknown\n"); - else - pr_info("ima: secureboot mode enabled\n"); - return mode; -} - -bool arch_ima_get_secureboot(void) -{ - static enum efi_secureboot_mode sb_mode; - static bool initialized; - - if (!initialized && efi_enabled(EFI_BOOT)) { - sb_mode = arch_ima_efi_boot_mode; - - if (sb_mode == efi_secureboot_mode_unset) - sb_mode = get_sb_mode(); - initialized = true; - } - - if (sb_mode == efi_secureboot_mode_enabled) - return true; - else - return false; -} +#include /* secureboot arch rules */ static const char * const sb_arch_rules[] = { @@ -67,11 +24,9 @@ static const char * const sb_arch_rules[] = { const char * const *arch_get_ima_policy(void) { - if (IS_ENABLED(CONFIG_IMA_ARCH_POLICY) && arch_ima_get_secureboot()) { - if (IS_ENABLED(CONFIG_MODULE_SIG)) - set_module_sig_enforced(); - if (IS_ENABLED(CONFIG_KEXEC_SIG)) - set_kexec_sig_enforced(); + if (IS_ENABLED(CONFIG_IMA_ARCH_POLICY) && arch_get_secureboot()) { + set_module_sig_enforced(); + set_kexec_sig_enforced(); return sb_arch_rules; } return NULL; diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 012a58959ff0..ca4931a95098 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -132,16 +132,12 @@ int ima_measurements_show(struct seq_file *m, void *v) char *template_name; u32 pcr, namelen, template_data_len; /* temporary fields */ bool is_ima_template = false; - enum hash_algo algo; int i, algo_idx; algo_idx = ima_sha1_idx; - algo = HASH_ALGO_SHA1; - if (m->file != NULL) { + if (m->file != NULL) algo_idx = (unsigned long)file_inode(m->file)->i_private; - algo = ima_algo_array[algo_idx].algo; - } /* get entry */ e = qe->entry; @@ -160,7 +156,8 @@ int ima_measurements_show(struct seq_file *m, void *v) ima_putc(m, &pcr, sizeof(e->pcr)); /* 2nd: template digest */ - ima_putc(m, e->digests[algo_idx].digest, hash_digest_size[algo]); + ima_putc(m, e->digests[algo_idx].digest, + ima_algo_array[algo_idx].digest_size); /* 3rd: template name size */ namelen = !ima_canonical_fmt ? strlen(template_name) : @@ -229,16 +226,12 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) struct ima_queue_entry *qe = v; struct ima_template_entry *e; char *template_name; - enum hash_algo algo; int i, algo_idx; algo_idx = ima_sha1_idx; - algo = HASH_ALGO_SHA1; - if (m->file != NULL) { + if (m->file != NULL) algo_idx = (unsigned long)file_inode(m->file)->i_private; - algo = ima_algo_array[algo_idx].algo; - } /* get entry */ e = qe->entry; @@ -252,7 +245,8 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) seq_printf(m, "%2d ", e->pcr); /* 2nd: template hash */ - ima_print_digest(m, e->digests[algo_idx].digest, hash_digest_size[algo]); + ima_print_digest(m, e->digests[algo_idx].digest, + ima_algo_array[algo_idx].digest_size); /* 3th: template name */ seq_printf(m, " %s", template_name); @@ -404,16 +398,24 @@ static int __init create_securityfs_measurement_lists(void) char file_name[NAME_MAX + 1]; struct dentry *dentry; - sprintf(file_name, "ascii_runtime_measurements_%s", - hash_algo_name[algo]); + if (algo == HASH_ALGO__LAST) + sprintf(file_name, "ascii_runtime_measurements_tpm_alg_%x", + ima_tpm_chip->allocated_banks[i].alg_id); + else + sprintf(file_name, "ascii_runtime_measurements_%s", + hash_algo_name[algo]); dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP, ima_dir, (void *)(uintptr_t)i, &ima_ascii_measurements_ops); if (IS_ERR(dentry)) return PTR_ERR(dentry); - sprintf(file_name, "binary_runtime_measurements_%s", - hash_algo_name[algo]); + if (algo == HASH_ALGO__LAST) + sprintf(file_name, "binary_runtime_measurements_tpm_alg_%x", + ima_tpm_chip->allocated_banks[i].alg_id); + else + sprintf(file_name, "binary_runtime_measurements_%s", + hash_algo_name[algo]); dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP, ima_dir, (void *)(uintptr_t)i, &ima_measurements_ops); diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 1d6229b156fb..5cea53fc36df 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -180,6 +180,29 @@ static void ima_rdwr_violation_check(struct file *file, "invalid_pcr", "open_writers"); } +/* + * Detect file change based on STATX_CHANGE_COOKIE, when supported, and + * fallback to detecting file change based on i_version. On filesystems + * which do not support either, assume the file changed. + */ +static bool ima_detect_file_change(struct ima_iint_cache *iint, + struct inode *inode, struct file *file) +{ + struct kstat stat; + int result; + + result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE, + AT_STATX_SYNC_AS_STAT); + + if (!result && stat.result_mask & STATX_CHANGE_COOKIE) + return stat.change_cookie != iint->real_inode.version; + + if (IS_I_VERSION(inode)) + return !inode_eq_iversion(inode, iint->real_inode.version); + + return true; +} + static void ima_check_last_writer(struct ima_iint_cache *iint, struct inode *inode, struct file *file) { @@ -191,18 +214,13 @@ static void ima_check_last_writer(struct ima_iint_cache *iint, mutex_lock(&iint->mutex); if (atomic_read(&inode->i_writecount) == 1) { - struct kstat stat; - clear_bit(IMA_EMITTED_OPENWRITERS, &iint->atomic_flags); update = test_and_clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags); - if ((iint->flags & IMA_NEW_FILE) || - vfs_getattr_nosec(&file->f_path, &stat, - STATX_CHANGE_COOKIE, - AT_STATX_SYNC_AS_STAT) || - !(stat.result_mask & STATX_CHANGE_COOKIE) || - stat.change_cookie != iint->real_inode.version) { + + if (iint->flags & IMA_NEW_FILE || + ima_detect_file_change(iint, inode, file)) { iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE); iint->measured_pcrs = 0; if (update) @@ -953,8 +971,7 @@ static int ima_load_data(enum kernel_load_data_id id, bool contents) switch (id) { case LOADING_KEXEC_IMAGE: - if (IS_ENABLED(CONFIG_KEXEC_SIG) - && arch_ima_get_secureboot()) { + if (IS_ENABLED(CONFIG_KEXEC_SIG) && arch_get_secureboot()) { pr_err("impossible to appraise a kernel image without a file descriptor; try using kexec_file_load syscall.\n"); return -EACCES; } diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index bf2d7ba4c14a..f7f940a76922 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -1298,7 +1298,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) IMA_GID | IMA_EGID | IMA_FGROUP | IMA_DIGSIG_REQUIRED | IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS | - IMA_CHECK_BLACKLIST | IMA_VERITY_REQUIRED)) + IMA_CHECK_BLACKLIST | IMA_VERITY_REQUIRED | + IMA_SIGV3_REQUIRED)) return false; break; @@ -1833,9 +1834,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) break; case Opt_digest_type: ima_log_string(ab, "digest_type", args[0].from); - if (entry->flags & IMA_DIGSIG_REQUIRED) - result = -EINVAL; - else if ((strcmp(args[0].from, "verity")) == 0) + if ((strcmp(args[0].from, "verity")) == 0) entry->flags |= IMA_VERITY_REQUIRED; else result = -EINVAL; @@ -1849,14 +1848,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) else entry->flags |= IMA_DIGSIG_REQUIRED | IMA_CHECK_BLACKLIST; } else if (strcmp(args[0].from, "sigv3") == 0) { - /* Only fsverity supports sigv3 for now */ - if (entry->flags & IMA_VERITY_REQUIRED) - entry->flags |= IMA_DIGSIG_REQUIRED | IMA_CHECK_BLACKLIST; - else - result = -EINVAL; + entry->flags |= IMA_SIGV3_REQUIRED | + IMA_DIGSIG_REQUIRED | + IMA_CHECK_BLACKLIST; } else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) && strcmp(args[0].from, "imasig|modsig") == 0) { - if (entry->flags & IMA_VERITY_REQUIRED) + if ((entry->flags & IMA_VERITY_REQUIRED) || + (entry->flags & IMA_SIGV3_REQUIRED)) result = -EINVAL; else entry->flags |= IMA_DIGSIG_REQUIRED | @@ -1941,7 +1939,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) /* d-ngv2 template field recommended for unsigned fs-verity digests */ if (!result && entry->action == MEASURE && - entry->flags & IMA_VERITY_REQUIRED) { + (entry->flags & IMA_VERITY_REQUIRED)) { template_desc = entry->template ? entry->template : ima_template_desc_current(); check_template_field(template_desc, "d-ngv2", @@ -2309,7 +2307,7 @@ int ima_policy_show(struct seq_file *m, void *v) if (entry->template) seq_printf(m, "template=%s ", entry->template->name); if (entry->flags & IMA_DIGSIG_REQUIRED) { - if (entry->flags & IMA_VERITY_REQUIRED) + if (entry->flags & IMA_SIGV3_REQUIRED) seq_puts(m, "appraise_type=sigv3 "); else if (entry->flags & IMA_MODSIG_ALLOWED) seq_puts(m, "appraise_type=imasig|modsig "); diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 7b388b66cf80..0c581c03c5da 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -130,7 +131,7 @@ struct modsig; #ifdef CONFIG_INTEGRITY_SIGNATURE int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, - const char *digest, int digestlen); + const char *digest, int digestlen, u8 algo); int integrity_modsig_verify(unsigned int id, const struct modsig *modsig); int __init integrity_init_keyring(const unsigned int id); @@ -141,7 +142,8 @@ int __init integrity_load_cert(const unsigned int id, const char *source, static inline int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, - const char *digest, int digestlen) + const char *digest, int digestlen, + u8 algo) { return -EOPNOTSUPP; } @@ -169,12 +171,21 @@ static inline int __init integrity_load_cert(const unsigned int id, #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS int asymmetric_verify(struct key *keyring, const char *sig, int siglen, const char *data, int datalen); +int asymmetric_verify_v3(struct key *keyring, const char *sig, + int siglen, const char *data, int datalen, u8 algo); #else static inline int asymmetric_verify(struct key *keyring, const char *sig, int siglen, const char *data, int datalen) { return -EOPNOTSUPP; } + +static inline int asymmetric_verify_v3(struct key *keyring, + const char *sig, int siglen, + const char *data, int datalen, u8 algo) +{ + return -EOPNOTSUPP; +} #endif #ifdef CONFIG_IMA_APPRAISE_MODSIG diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c index d1fdd113450a..c0d6948446c3 100644 --- a/security/integrity/platform_certs/load_uefi.c +++ b/security/integrity/platform_certs/load_uefi.c @@ -212,7 +212,7 @@ static int __init load_uefi_certs(void) } /* the MOK/MOKx can not be trusted when secure boot is disabled */ - if (!arch_ima_get_secureboot()) + if (!arch_get_secureboot()) return 0; mokx = get_cert_list(L"MokListXRT", &mok_var, &mokxsize, &status); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 97801966bf32..0f704380a8c8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2966,7 +2966,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, { const struct cred_security_struct *crsec = selinux_cred(current_cred()); struct superblock_security_struct *sbsec; - struct xattr *xattr = lsm_get_xattr_slot(xattrs, xattr_count); + struct xattr *xattr; u32 newsid, clen; u16 newsclass; int rc; @@ -2992,6 +2992,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, !(sbsec->flags & SBLABEL_MNT)) return -EOPNOTSUPP; + xattr = lsm_get_xattr_slot(xattrs, xattr_count); if (xattr) { rc = security_sid_to_context_force(newsid, &context, &clen); @@ -3208,15 +3209,13 @@ static inline int task_avdcache_search(struct task_security_struct *tsec, * @tsec: the task's security state * @isec: the inode associated with the cache entry * @avd: the AVD to cache - * @audited: the permission audit bitmask to cache * - * Update the AVD cache in @tsec with the @avdc and @audited info associated + * Update the AVD cache in @tsec with the @avd info associated * with @isec. */ static inline void task_avdcache_update(struct task_security_struct *tsec, struct inode_security_struct *isec, - struct av_decision *avd, - u32 audited) + struct av_decision *avd) { int spot; @@ -3228,9 +3227,7 @@ static inline void task_avdcache_update(struct task_security_struct *tsec, spot = (tsec->avdcache.dir_spot + 1) & (TSEC_AVDC_DIR_SIZE - 1); tsec->avdcache.dir_spot = spot; tsec->avdcache.dir[spot].isid = isec->sid; - tsec->avdcache.dir[spot].audited = audited; - tsec->avdcache.dir[spot].allowed = avd->allowed; - tsec->avdcache.dir[spot].permissive = avd->flags & AVD_FLAGS_PERMISSIVE; + tsec->avdcache.dir[spot].avd = *avd; tsec->avdcache.permissive_neveraudit = (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT)); } @@ -3251,6 +3248,7 @@ static int selinux_inode_permission(struct inode *inode, int requested) struct task_security_struct *tsec; struct inode_security_struct *isec; struct avdc_entry *avdc; + struct av_decision avd, *avdp = &avd; int rc, rc2; u32 audited, denied; @@ -3272,23 +3270,21 @@ static int selinux_inode_permission(struct inode *inode, int requested) rc = task_avdcache_search(tsec, isec, &avdc); if (likely(!rc)) { /* Cache hit. */ - audited = perms & avdc->audited; - denied = perms & ~avdc->allowed; - if (unlikely(denied && enforcing_enabled() && - !avdc->permissive)) + avdp = &avdc->avd; + denied = perms & ~avdp->allowed; + if (unlikely(denied) && enforcing_enabled() && + !(avdp->flags & AVD_FLAGS_PERMISSIVE)) rc = -EACCES; } else { - struct av_decision avd; - /* Cache miss. */ rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, - perms, 0, &avd); - audited = avc_audit_required(perms, &avd, rc, - (requested & MAY_ACCESS) ? FILE__AUDIT_ACCESS : 0, - &denied); - task_avdcache_update(tsec, isec, &avd, audited); + perms, 0, avdp); + task_avdcache_update(tsec, isec, avdp); } + audited = avc_audit_required(perms, avdp, rc, + (requested & MAY_ACCESS) ? + FILE__AUDIT_ACCESS : 0, &denied); if (likely(!audited)) return rc; @@ -4920,7 +4916,7 @@ static bool sock_skip_has_perm(u32 sid) static int sock_has_perm(struct sock *sk, u32 perms) { - struct sk_security_struct *sksec = sk->sk_security; + struct sk_security_struct *sksec = selinux_sock(sk); struct common_audit_data ad; struct lsm_network_audit net; @@ -6227,7 +6223,7 @@ static unsigned int selinux_ip_postroute(void *priv, static int nlmsg_sock_has_extended_perms(struct sock *sk, u32 perms, u16 nlmsg_type) { - struct sk_security_struct *sksec = sk->sk_security; + struct sk_security_struct *sksec = selinux_sock(sk); struct common_audit_data ad; u8 driver; u8 xperm; diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index b19e5d978e82..3c0a16ec978b 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -32,9 +32,7 @@ struct avdc_entry { u32 isid; /* inode SID */ - u32 allowed; /* allowed permission bitmask */ - u32 audited; /* audited permission bitmask */ - bool permissive; /* AVC permissive flag */ + struct av_decision avd; /* av decision */ }; struct cred_security_struct { diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index d1f16d7f684d..0babb8992181 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -312,8 +312,6 @@ int security_context_to_sid_default(const char *scontext, u32 scontext_len, int security_context_to_sid_force(const char *scontext, u32 scontext_len, u32 *sid); -int security_get_user_sids(u32 fromsid, const char *username, u32 **sids, u32 *nel); - int security_port_sid(u8 protocol, u16 port, u32 *out_sid); int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid); diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 83aa765a09f9..25ca7d714014 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -76,7 +76,6 @@ struct selinux_fs_info { int *bool_pending_values; struct dentry *class_dir; unsigned long last_class_ino; - bool policy_opened; unsigned long last_ino; struct super_block *sb; }; @@ -272,35 +271,13 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char *page; - ssize_t length; - int new_value; - - if (count >= PAGE_SIZE) - return -ENOMEM; - - /* No partial writes. */ - if (*ppos != 0) - return -EINVAL; - - page = memdup_user_nul(buf, count); - if (IS_ERR(page)) - return PTR_ERR(page); - - if (sscanf(page, "%d", &new_value) != 1) { - length = -EINVAL; - goto out; - } - length = count; - - if (new_value) { - pr_err("SELinux: https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-runtime-disable\n"); - pr_err("SELinux: Runtime disable is not supported, use selinux=0 on the kernel cmdline.\n"); - } - -out: - kfree(page); - return length; + /* + * Setting disable is no longer supported, see + * https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-runtime-disable + */ + pr_err_once("SELinux: %s (%d) wrote to disable. This is no longer supported.\n", + current->comm, current->pid); + return count; } static const struct file_operations sel_disable_ops = { @@ -362,44 +339,31 @@ struct policy_load_memory { static int sel_open_policy(struct inode *inode, struct file *filp) { - struct selinux_fs_info *fsi = inode->i_sb->s_fs_info; struct policy_load_memory *plm = NULL; int rc; - BUG_ON(filp->private_data); - - mutex_lock(&selinux_state.policy_mutex); - rc = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); if (rc) - goto err; + return rc; - rc = -EBUSY; - if (fsi->policy_opened) - goto err; - - rc = -ENOMEM; plm = kzalloc_obj(*plm); if (!plm) - goto err; + return -ENOMEM; + mutex_lock(&selinux_state.policy_mutex); rc = security_read_policy(&plm->data, &plm->len); if (rc) goto err; - if ((size_t)i_size_read(inode) != plm->len) { inode_lock(inode); i_size_write(inode, plm->len); inode_unlock(inode); } - - fsi->policy_opened = 1; + mutex_unlock(&selinux_state.policy_mutex); filp->private_data = plm; - mutex_unlock(&selinux_state.policy_mutex); - return 0; err: mutex_unlock(&selinux_state.policy_mutex); @@ -412,13 +376,8 @@ err: static int sel_release_policy(struct inode *inode, struct file *filp) { - struct selinux_fs_info *fsi = inode->i_sb->s_fs_info; struct policy_load_memory *plm = filp->private_data; - BUG_ON(!plm); - - fsi->policy_opened = 0; - vfree(plm->data); kfree(plm); @@ -594,34 +553,31 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, if (!count) return -EINVAL; - mutex_lock(&selinux_state.policy_mutex); - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL); if (length) - goto out; + return length; data = vmalloc(count); - if (!data) { - length = -ENOMEM; - goto out; - } + if (!data) + return -ENOMEM; if (copy_from_user(data, buf, count) != 0) { length = -EFAULT; goto out; } + mutex_lock(&selinux_state.policy_mutex); length = security_load_policy(data, count, &load_state); if (length) { pr_warn_ratelimited("SELinux: failed to load policy\n"); - goto out; + goto out_unlock; } fsi = file_inode(file)->i_sb->s_fs_info; length = sel_make_policy_nodes(fsi, load_state.policy); if (length) { pr_warn_ratelimited("SELinux: failed to initialize selinuxfs\n"); selinux_policy_cancel(&load_state); - goto out; + goto out_unlock; } selinux_policy_commit(&load_state); @@ -631,8 +587,9 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); -out: +out_unlock: mutex_unlock(&selinux_state.policy_mutex); +out: vfree(data); return length; } @@ -689,46 +646,13 @@ static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf, static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char *page; - ssize_t length; - unsigned int new_value; - - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, - SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, - NULL); - if (length) - return length; - - if (count >= PAGE_SIZE) - return -ENOMEM; - - /* No partial writes. */ - if (*ppos != 0) - return -EINVAL; - - page = memdup_user_nul(buf, count); - if (IS_ERR(page)) - return PTR_ERR(page); - - if (sscanf(page, "%u", &new_value) != 1) { - length = -EINVAL; - goto out; - } - length = count; - - if (new_value) { - char comm[sizeof(current->comm)]; - - strscpy(comm, current->comm); - pr_err("SELinux: %s (%d) set checkreqprot to 1. This is no longer supported.\n", - comm, current->pid); - } - - selinux_ima_measure_state(); - -out: - kfree(page); - return length; + /* + * Setting checkreqprot is no longer supported, see + * https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-checkreqprot + */ + pr_err_once("SELinux: %s (%d) wrote to checkreqprot. This is no longer supported.\n", + current->comm, current->pid); + return count; } static const struct file_operations sel_checkreqprot_ops = { .read = sel_read_checkreqprot, @@ -1073,69 +997,11 @@ out: static ssize_t sel_write_user(struct file *file, char *buf, size_t size) { - char *con = NULL, *user = NULL, *ptr; - u32 sid, *sids = NULL; - ssize_t length; - char *newcon; - int rc; - u32 i, len, nsids; - - pr_warn_ratelimited("SELinux: %s (%d) wrote to /sys/fs/selinux/user!" - " This will not be supported in the future; please update your" - " userspace.\n", current->comm, current->pid); - ssleep(5); - - length = avc_has_perm(current_sid(), SECINITSID_SECURITY, - SECCLASS_SECURITY, SECURITY__COMPUTE_USER, - NULL); - if (length) - goto out; - - length = -ENOMEM; - con = kzalloc(size + 1, GFP_KERNEL); - if (!con) - goto out; - - length = -ENOMEM; - user = kzalloc(size + 1, GFP_KERNEL); - if (!user) - goto out; - - length = -EINVAL; - if (sscanf(buf, "%s %s", con, user) != 2) - goto out; - - length = security_context_str_to_sid(con, &sid, GFP_KERNEL); - if (length) - goto out; - - length = security_get_user_sids(sid, user, &sids, &nsids); - if (length) - goto out; - - length = sprintf(buf, "%u", nsids) + 1; - ptr = buf + length; - for (i = 0; i < nsids; i++) { - rc = security_sid_to_context(sids[i], &newcon, &len); - if (rc) { - length = rc; - goto out; - } - if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) { - kfree(newcon); - length = -ERANGE; - goto out; - } - memcpy(ptr, newcon, len); - kfree(newcon); - ptr += len; - length += len; - } -out: - kfree(sids); - kfree(user); - kfree(con); - return length; + pr_err_once("SELinux: %s (%d) wrote to user. This is no longer supported.\n", + current->comm, current->pid); + buf[0] = '0'; + buf[1] = 0; + return 2; } static ssize_t sel_write_member(struct file *file, char *buf, size_t size) diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index e8e7ccbd1e44..143021c5e326 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2746,131 +2746,6 @@ out: return rc; } -#define SIDS_NEL 25 - -/** - * security_get_user_sids - Obtain reachable SIDs for a user. - * @fromsid: starting SID - * @username: username - * @sids: array of reachable SIDs for user - * @nel: number of elements in @sids - * - * Generate the set of SIDs for legal security contexts - * for a given user that can be reached by @fromsid. - * Set *@sids to point to a dynamically allocated - * array containing the set of SIDs. Set *@nel to the - * number of elements in the array. - */ - -int security_get_user_sids(u32 fromsid, - const char *username, - u32 **sids, - u32 *nel) -{ - struct selinux_policy *policy; - struct policydb *policydb; - struct sidtab *sidtab; - struct context *fromcon, usercon; - u32 *mysids = NULL, *mysids2, sid; - u32 i, j, mynel, maxnel = SIDS_NEL; - struct user_datum *user; - struct role_datum *role; - struct ebitmap_node *rnode, *tnode; - int rc; - - *sids = NULL; - *nel = 0; - - if (!selinux_initialized()) - return 0; - - mysids = kcalloc(maxnel, sizeof(*mysids), GFP_KERNEL); - if (!mysids) - return -ENOMEM; - -retry: - mynel = 0; - rcu_read_lock(); - policy = rcu_dereference(selinux_state.policy); - policydb = &policy->policydb; - sidtab = policy->sidtab; - - context_init(&usercon); - - rc = -EINVAL; - fromcon = sidtab_search(sidtab, fromsid); - if (!fromcon) - goto out_unlock; - - rc = -EINVAL; - user = symtab_search(&policydb->p_users, username); - if (!user) - goto out_unlock; - - usercon.user = user->value; - - ebitmap_for_each_positive_bit(&user->roles, rnode, i) { - role = policydb->role_val_to_struct[i]; - usercon.role = i + 1; - ebitmap_for_each_positive_bit(&role->types, tnode, j) { - usercon.type = j + 1; - - if (mls_setup_user_range(policydb, fromcon, user, - &usercon)) - continue; - - rc = sidtab_context_to_sid(sidtab, &usercon, &sid); - if (rc == -ESTALE) { - rcu_read_unlock(); - goto retry; - } - if (rc) - goto out_unlock; - if (mynel < maxnel) { - mysids[mynel++] = sid; - } else { - rc = -ENOMEM; - maxnel += SIDS_NEL; - mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC); - if (!mysids2) - goto out_unlock; - memcpy(mysids2, mysids, mynel * sizeof(*mysids2)); - kfree(mysids); - mysids = mysids2; - mysids[mynel++] = sid; - } - } - } - rc = 0; -out_unlock: - rcu_read_unlock(); - if (rc || !mynel) { - kfree(mysids); - return rc; - } - - rc = -ENOMEM; - mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL); - if (!mysids2) { - kfree(mysids); - return rc; - } - for (i = 0, j = 0; i < mynel; i++) { - struct av_decision dummy_avd; - rc = avc_has_perm_noaudit(fromsid, mysids[i], - SECCLASS_PROCESS, /* kernel value */ - PROCESS__TRANSITION, AVC_STRICT, - &dummy_avd); - if (!rc) - mysids2[j++] = mysids[i]; - cond_resched(); - } - kfree(mysids); - *sids = mysids2; - *nel = j; - return 0; -} - /** * __security_genfs_sid - Helper to obtain a SID for a file in a filesystem * @policy: policy diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index bfacb3f2e2ed..1fba46aa357a 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c @@ -195,21 +195,19 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r) if (i & 1) { pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos, - " path%u.parent={ uid=%u gid=%u ino=%lu perm=0%o }", + " path%u.parent={ uid=%u gid=%u ino=%llu perm=0%o }", (i >> 1) + 1, from_kuid(&init_user_ns, stat->uid), from_kgid(&init_user_ns, stat->gid), - (unsigned long)stat->ino, - stat->mode & S_IALLUGO); + stat->ino, stat->mode & S_IALLUGO); continue; } pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos, - " path%u={ uid=%u gid=%u ino=%lu major=%u minor=%u perm=0%o type=%s", + " path%u={ uid=%u gid=%u ino=%llu major=%u minor=%u perm=0%o type=%s", (i >> 1) + 1, from_kuid(&init_user_ns, stat->uid), from_kgid(&init_user_ns, stat->gid), - (unsigned long)stat->ino, - MAJOR(dev), MINOR(dev), + stat->ino, MAJOR(dev), MINOR(dev), mode & S_IALLUGO, tomoyo_filetype(mode)); if (S_ISCHR(mode) || S_ISBLK(mode)) { dev = stat->rdev; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 4f1704c911ef..d098cf8aae61 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -567,7 +567,7 @@ struct tomoyo_address_group { struct tomoyo_mini_stat { kuid_t uid; kgid_t gid; - ino_t ino; + u64 ino; umode_t mode; dev_t dev; dev_t rdev; diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c index f8bcc083bb0d..8b107b1ffdab 100644 --- a/security/tomoyo/condition.c +++ b/security/tomoyo/condition.c @@ -766,8 +766,8 @@ bool tomoyo_condition(struct tomoyo_request_info *r, const struct tomoyo_condition *cond) { u32 i; - unsigned long min_v[2] = { 0, 0 }; - unsigned long max_v[2] = { 0, 0 }; + u64 min_v[2] = { 0, 0 }; + u64 max_v[2] = { 0, 0 }; const struct tomoyo_condition_element *condp; const struct tomoyo_number_union *numbers_p; const struct tomoyo_name_union *names_p; @@ -834,7 +834,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r, /* Check numeric or bit-op expressions. */ for (j = 0; j < 2; j++) { const u8 index = j ? right : left; - unsigned long value = 0; + u64 value = 0; switch (index) { case TOMOYO_TASK_UID: diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index 1e114dbcf93c..79eb557d4942 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -331,12 +331,15 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) if (dev->dev.of_node) { /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */ rst_gpio = devm_gpiod_get(&dev->dev, "reset", GPIOD_OUT_HIGH); - ret = PTR_ERR(rst_gpio); - if (ret == -ENOENT) - reset_gpio = -1; - else if (ret) - return ret; - reset_gpio = desc_to_gpio(rst_gpio); + if (IS_ERR(rst_gpio)) { + ret = PTR_ERR(rst_gpio); + if (ret == -ENOENT) + reset_gpio = -1; + else if (ret) + return ret; + } else { + reset_gpio = desc_to_gpio(rst_gpio); + } } else { if (cpu_is_pxa27x()) reset_gpio = 113; diff --git a/sound/core/misc.c b/sound/core/misc.c index 88d9e1f9a6e9..833124c8e4fa 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -100,14 +100,18 @@ static LIST_HEAD(snd_fasync_list); static void snd_fasync_work_fn(struct work_struct *work) { struct snd_fasync *fasync; + int signal, poll; spin_lock_irq(&snd_fasync_lock); while (!list_empty(&snd_fasync_list)) { fasync = list_first_entry(&snd_fasync_list, struct snd_fasync, list); list_del_init(&fasync->list); + if (!fasync->on) + continue; + signal = fasync->signal; + poll = fasync->poll; spin_unlock_irq(&snd_fasync_lock); - if (fasync->on) - kill_fasync(&fasync->fasync, fasync->signal, fasync->poll); + kill_fasync(&fasync->fasync, signal, poll); spin_lock_irq(&snd_fasync_lock); } spin_unlock_irq(&snd_fasync_lock); @@ -144,9 +148,11 @@ EXPORT_SYMBOL_GPL(snd_fasync_helper); void snd_kill_fasync(struct snd_fasync *fasync, int signal, int poll) { - if (!fasync || !fasync->on) + if (!fasync) return; guard(spinlock_irqsave)(&snd_fasync_lock); + if (!fasync->on) + return; fasync->signal = signal; fasync->poll = poll; list_move(&fasync->list, &snd_fasync_list); @@ -158,7 +164,12 @@ void snd_fasync_free(struct snd_fasync *fasync) { if (!fasync) return; - fasync->on = 0; + + scoped_guard(spinlock_irq, &snd_fasync_lock) { + fasync->on = 0; + list_del_init(&fasync->list); + } + flush_work(&snd_fasync_work); kfree(fasync); } diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index a140a0d9abb8..33fd34f0d615 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -2155,10 +2155,16 @@ static int snd_pcm_oss_get_trigger(struct snd_pcm_oss_file *pcm_oss_file) psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; - if (psubstream && psubstream->runtime && psubstream->runtime->oss.trigger) - result |= PCM_ENABLE_OUTPUT; - if (csubstream && csubstream->runtime && csubstream->runtime->oss.trigger) - result |= PCM_ENABLE_INPUT; + if (psubstream && psubstream->runtime) { + guard(mutex)(&psubstream->runtime->oss.params_lock); + if (psubstream->runtime->oss.trigger) + result |= PCM_ENABLE_OUTPUT; + } + if (csubstream && csubstream->runtime) { + guard(mutex)(&csubstream->runtime->oss.params_lock); + if (csubstream->runtime->oss.trigger) + result |= PCM_ENABLE_INPUT; + } return result; } @@ -2832,6 +2838,17 @@ static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream) runtime->oss.period_frames; } +static bool need_input_retrigger(struct snd_pcm_runtime *runtime) +{ + bool ret; + + guard(mutex)(&runtime->oss.params_lock); + ret = runtime->oss.trigger; + if (ret) + runtime->oss.trigger = 0; + return ret; +} + static __poll_t snd_pcm_oss_poll(struct file *file, poll_table * wait) { struct snd_pcm_oss_file *pcm_oss_file; @@ -2864,11 +2881,11 @@ static __poll_t snd_pcm_oss_poll(struct file *file, poll_table * wait) snd_pcm_oss_capture_ready(csubstream)) mask |= EPOLLIN | EPOLLRDNORM; } - if (ostate != SNDRV_PCM_STATE_RUNNING && runtime->oss.trigger) { + if (ostate != SNDRV_PCM_STATE_RUNNING && + need_input_retrigger(runtime)) { struct snd_pcm_oss_file ofile; memset(&ofile, 0, sizeof(ofile)); ofile.streams[SNDRV_PCM_STREAM_CAPTURE] = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; - runtime->oss.trigger = 0; snd_pcm_oss_set_trigger(&ofile, PCM_ENABLE_INPUT); } } diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 75a7a2af9d8c..5719637575a9 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1253,7 +1253,7 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client, if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3)) client->midi_version = client_info->midi_version; memcpy(client->event_filter, client_info->event_filter, 32); - client->group_filter = client_info->group_filter; + client->group_filter = client_info->group_filter & SND_SEQ_GROUP_FILTER_MASK; /* notify the change */ snd_seq_system_client_ev_client_change(client->number); diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h index ece02c58db70..feea8bb7d987 100644 --- a/sound/core/seq/seq_clientmgr.h +++ b/sound/core/seq/seq_clientmgr.h @@ -14,6 +14,9 @@ /* client manager */ +#define SND_SEQ_GROUP_FILTER_MASK GENMASK(SNDRV_UMP_MAX_GROUPS, 0) +#define SND_SEQ_GROUP_FILTER_GROUPS GENMASK(SNDRV_UMP_MAX_GROUPS, 1) + struct snd_seq_user_client { struct file *file; /* file struct of client */ /* ... */ @@ -40,7 +43,7 @@ struct snd_seq_client { int number; /* client number */ unsigned int filter; /* filter flags */ DECLARE_BITMAP(event_filter, 256); - unsigned short group_filter; + unsigned int group_filter; snd_use_lock_t use_lock; int event_lost; /* ports */ diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index fdc76f23e03f..9079ccfdc866 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -369,7 +369,7 @@ static void setup_client_group_filter(struct seq_ump_client *client) cptr = snd_seq_kernel_client_get(client->seq_client); if (!cptr) return; - filter = ~(1U << 0); /* always allow groupless messages */ + filter = SND_SEQ_GROUP_FILTER_GROUPS; /* always allow groupless messages */ for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) { if (client->ump->groups[p].active) filter &= ~(1U << (p + 1)); diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index aa0d2fcb1a18..a37a1695f51c 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -99,6 +99,9 @@ struct loopback_ops { struct loopback_cable { spinlock_t lock; struct loopback_pcm *streams[2]; + /* in-flight peer stops running outside cable->lock */ + atomic_t stop_count; + wait_queue_head_t stop_wait; struct snd_pcm_hardware hw; /* flags */ unsigned int valid; @@ -366,8 +369,11 @@ static int loopback_check_format(struct loopback_cable *cable, int stream) return 0; if (stream == SNDRV_PCM_STREAM_CAPTURE) return -EIO; - else if (cruntime->state == SNDRV_PCM_STATE_RUNNING) + else if (cruntime->state == SNDRV_PCM_STATE_RUNNING) { + /* close must not free the peer runtime below */ + atomic_inc(&cable->stop_count); stop_capture = true; + } } setup = get_setup(dpcm_play); @@ -396,8 +402,11 @@ static int loopback_check_format(struct loopback_cable *cable, int stream) } } - if (stop_capture) + if (stop_capture) { snd_pcm_stop(dpcm_capt->substream, SNDRV_PCM_STATE_DRAINING); + if (atomic_dec_and_test(&cable->stop_count)) + wake_up(&cable->stop_wait); + } return 0; } @@ -1049,23 +1058,29 @@ static void free_cable(struct snd_pcm_substream *substream) struct loopback *loopback = substream->private_data; int dev = get_cable_index(substream); struct loopback_cable *cable; + struct loopback_pcm *dpcm; + bool other_alive; cable = loopback->cables[substream->number][dev]; if (!cable) return; - if (cable->streams[!substream->stream]) { - /* other stream is still alive */ - guard(spinlock_irq)(&cable->lock); - cable->streams[substream->stream] = NULL; - } else { - struct loopback_pcm *dpcm = substream->runtime->private_data; - if (cable->ops && cable->ops->close_cable && dpcm) - cable->ops->close_cable(dpcm); - /* free the cable */ - loopback->cables[substream->number][dev] = NULL; - kfree(cable); + scoped_guard(spinlock_irq, &cable->lock) { + cable->streams[substream->stream] = NULL; + other_alive = cable->streams[!substream->stream]; } + + /* Pair with the stop_count increment in loopback_check_format(). */ + wait_event(cable->stop_wait, !atomic_read(&cable->stop_count)); + if (other_alive) + return; + + dpcm = substream->runtime->private_data; + if (cable->ops && cable->ops->close_cable && dpcm) + cable->ops->close_cable(dpcm); + /* free the cable */ + loopback->cables[substream->number][dev] = NULL; + kfree(cable); } static int loopback_jiffies_timer_open(struct loopback_pcm *dpcm) @@ -1260,6 +1275,8 @@ static int loopback_open(struct snd_pcm_substream *substream) goto unlock; } spin_lock_init(&cable->lock); + atomic_set(&cable->stop_count, 0); + init_waitqueue_head(&cable->stop_wait); cable->hw = loopback_pcm_hardware; if (loopback->timer_source) cable->ops = &loopback_snd_timer_ops; diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c index 768bb698adfb..7f93557b51ec 100644 --- a/sound/drivers/pcmtest.c +++ b/sound/drivers/pcmtest.c @@ -679,9 +679,9 @@ static ssize_t pattern_read(struct file *file, char __user *u_buff, size_t len, return 0; if (copy_to_user(u_buff, patt_buf->buf + *off, to_read)) - to_read = 0; - else - *off += to_read; + return -EFAULT; + + *off += to_read; return to_read; } @@ -754,13 +754,22 @@ static int __init mod_init(void) err = init_debug_files(buf_allocated); if (err) - return err; + goto err_free_patterns; err = platform_device_register(&pcmtst_pdev); if (err) - return err; + goto err_clear_debug; err = platform_driver_register(&pcmtst_pdrv); - if (err) + if (err) { platform_device_unregister(&pcmtst_pdev); + goto err_clear_debug; + } + + return 0; + +err_clear_debug: + clear_debug_files(); +err_free_patterns: + free_pattern_buffers(); return err; } diff --git a/sound/drivers/vx/vx_cmd.h b/sound/drivers/vx/vx_cmd.h index c2a520274493..1fc70c98f041 100644 --- a/sound/drivers/vx/vx_cmd.h +++ b/sound/drivers/vx/vx_cmd.h @@ -199,7 +199,7 @@ struct vx_cmd_info { void vx_init_rmh(struct vx_rmh *rmh, unsigned int cmd); /** - * vx_send_pipe_cmd_params - fill first command word for pipe commands + * vx_set_pipe_cmd_params - fill first command word for pipe commands * @rmh: the rmh to be modified * @is_capture: 0 = playback, 1 = capture operation * @param1: first pipe-parameter diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c index 867b4ea1096e..6270263e7bf4 100644 --- a/sound/firewire/tascam/tascam-hwdep.c +++ b/sound/firewire/tascam/tascam-hwdep.c @@ -73,6 +73,7 @@ static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf, length = rounddown(remained, sizeof(*entries)); if (length == 0) break; + tail_pos = head_pos + length / sizeof(*entries); spin_unlock_irq(&tscm->lock); if (copy_to_user(pos, &entries[head_pos], length)) diff --git a/sound/hda/codecs/Makefile b/sound/hda/codecs/Makefile index e7f03e281999..88d2f8a79467 100644 --- a/sound/hda/codecs/Makefile +++ b/sound/hda/codecs/Makefile @@ -7,7 +7,6 @@ snd-hda-codec-cm9825-y := cm9825.o snd-hda-codec-analog-y := analog.o snd-hda-codec-ca0110-y := ca0110.o snd-hda-codec-ca0132-y := ca0132.o -snd-hda-codec-cmedia-y := cmedia.o snd-hda-codec-conexant-y := conexant.o snd-hda-codec-idt-y := sigmatel.o snd-hda-codec-senarytech-y := senarytech.o @@ -26,7 +25,6 @@ obj-$(CONFIG_SND_HDA_CODEC_CM9825) += snd-hda-codec-cm9825.o obj-$(CONFIG_SND_HDA_CODEC_ANALOG) += snd-hda-codec-analog.o obj-$(CONFIG_SND_HDA_CODEC_CA0110) += snd-hda-codec-ca0110.o obj-$(CONFIG_SND_HDA_CODEC_CA0132) += snd-hda-codec-ca0132.o -obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o obj-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += snd-hda-codec-idt.o obj-$(CONFIG_SND_HDA_CODEC_SENARYTECH) += snd-hda-codec-senarytech.o diff --git a/sound/hda/codecs/ca0132.c b/sound/hda/codecs/ca0132.c index ad533b04ab29..be565ffaade0 100644 --- a/sound/hda/codecs/ca0132.c +++ b/sound/hda/codecs/ca0132.c @@ -5498,6 +5498,30 @@ static int zxr_headphone_gain_set(struct hda_codec *codec, long val) return 0; } +/* + * Manual output selection (HP/Speaker Playback Switch or alt Output Select) + * is meaningful only when HP/Speaker auto-detect is disabled, since the + * select_out path always prefers jack presence when auto-detect is on. When + * the user explicitly chooses an output, turn auto-detect off so the manual + * choice actually takes effect, and notify userspace so the auto-detect + * control reflects the new state. + */ +static void ca0132_disable_hp_auto_detect(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct snd_kcontrol *kctl; + + if (!spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]) + return; + + spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID] = 0; + kctl = snd_hda_find_mixer_ctl(codec, + "HP/Speaker Auto Detect Playback Switch"); + if (kctl) + snd_ctl_notify(codec->card, SNDRV_CTL_EVENT_MASK_VALUE, + &kctl->id); +} + static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -5510,14 +5534,11 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, int auto_jack; if (nid == VNID_HP_SEL) { - auto_jack = - spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; - if (!auto_jack) { - if (ca0132_use_alt_functions(spec)) - ca0132_alt_select_out(codec); - else - ca0132_select_out(codec); - } + ca0132_disable_hp_auto_detect(codec); + if (ca0132_use_alt_functions(spec)) + ca0132_alt_select_out(codec); + else + ca0132_select_out(codec); return 1; } @@ -5978,7 +5999,6 @@ static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol, struct ca0132_spec *spec = codec->spec; int sel = ucontrol->value.enumerated.item[0]; unsigned int items = NUM_OF_OUTPUTS; - unsigned int auto_jack; if (sel >= items) return 0; @@ -5988,10 +6008,8 @@ static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol, spec->out_enum_val = sel; - auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; - - if (!auto_jack) - ca0132_alt_select_out(codec); + ca0132_disable_hp_auto_detect(codec); + ca0132_alt_select_out(codec); return 1; } diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index 3a9717df39b4..e3b6aaabe3a9 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -1175,6 +1175,7 @@ static void add_cx5051_fake_mutes(struct hda_codec *codec) static int cx_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct conexant_spec *spec; + struct hda_jack_callback *callback; int err; codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name); @@ -1190,7 +1191,12 @@ static int cx_probe(struct hda_codec *codec, const struct hda_device_id *id) case 0x14f11f86: case 0x14f11f87: spec->is_cx11880_sn6140 = true; - snd_hda_jack_detect_enable_callback(codec, 0x19, cx_update_headset_mic_vref); + callback = snd_hda_jack_detect_enable_callback(codec, 0x19, + cx_update_headset_mic_vref); + if (IS_ERR(callback)) { + err = PTR_ERR(callback); + goto error; + } break; } diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index b33f425763f9..2f5d13032fd7 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -1669,6 +1669,21 @@ static void alc295_fixup_hp_mute_led_coefbit11(struct hda_codec *codec, } } +/* Override wrong pin to NID 0x1b (F.32 BIOS reports 0x18 via DMI OEM string) + * on HP pavilion 15-cs1xxx laptops + */ +static void alc295_fixup_hp_pavilion_mute_led_1b(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct alc_spec *spec = codec->spec; + + alc269_fixup_hp_mute_led(codec, fix, action); + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->mute_led_nid = 0x1b; +} + static void alc233_fixup_lenovo_coef_micmute_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -3390,6 +3405,19 @@ static void alc256_fixup_mic_no_presence_and_resume(struct hda_codec *codec, } } +static void alc256_fixup_xiaomi_pro15_resume(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + /* + * On the Xiaomi Mi Laptop Pro 15 (TM1905, SSID 1d72:1905) the ALC256 + * codec sets coefficient 0x10 bit 9 to 1 after S3 resume, silencing + * the internal speaker. Bluetooth and HDMI audio are unaffected. + * Clear the bit so the speaker keeps working across suspend cycles. + */ + alc_update_coef_idx(codec, 0x10, 1<<9, 0); +} + static void alc256_decrease_headphone_amp_val(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -3694,6 +3722,17 @@ static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec, spec->power_hook = alc287_s4_power_gpio3_default; spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook; } + +static void alc287_fixup_tb_vmaster_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->gen.vmaster_mute_led = 1; + + alc287_fixup_bind_dacs(codec, fix, action); +} /* GPIO2: mute led GPIO3: micmute led */ static void alc245_tas2781_spi_hp_fixup_muteled(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -3846,6 +3885,7 @@ enum { ALC290_FIXUP_SUBWOOFER, ALC290_FIXUP_SUBWOOFER_HSJACK, ALC295_FIXUP_HP_MUTE_LED_COEFBIT11, + ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B, ALC269_FIXUP_THINKPAD_ACPI, ALC269_FIXUP_LENOVO_XPAD_ACPI, ALC269_FIXUP_DMIC_THINKPAD_ACPI, @@ -4041,6 +4081,7 @@ enum { ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, ALC233_FIXUP_NO_AUDIO_JACK, ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME, + ALC256_FIXUP_XIAOMI_PRO15_RESUME, ALC285_FIXUP_LEGION_Y9000X_SPEAKERS, ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, ALC287_FIXUP_LEGION_16ACHG6, @@ -4054,6 +4095,7 @@ enum { ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED, ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED, ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE, + ALC295_FIXUP_FRAMEWORK_LAPTOP_LIMIT_INT_MIC_BOOST, ALC287_FIXUP_LEGION_16ITHG6, ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK, ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, @@ -4111,6 +4153,8 @@ enum { ALC245_FIXUP_ACER_MICMUTE_LED, ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED, ALC236_FIXUP_HP_DMIC, + ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO, + ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -5688,6 +5732,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc295_fixup_hp_mute_led_coefbit11, }, + [ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc295_fixup_hp_pavilion_mute_led_1b, + }, [ALC298_FIXUP_SAMSUNG_AMP] = { .type = HDA_FIXUP_FUNC, .v.func = alc298_fixup_samsung_amp, @@ -6228,6 +6276,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC }, + [ALC256_FIXUP_XIAOMI_PRO15_RESUME] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc256_fixup_xiaomi_pro15_resume, + }, [ALC287_FIXUP_LEGION_16ACHG6] = { .type = HDA_FIXUP_FUNC, .v.func = alc287_fixup_legion_16achg6_speakers, @@ -6295,6 +6347,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC }, + [ALC295_FIXUP_FRAMEWORK_LAPTOP_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE, + }, [ALC287_FIXUP_LEGION_16ITHG6] = { .type = HDA_FIXUP_FUNC, .v.func = alc287_fixup_legion_16ithg6_speakers, @@ -6447,7 +6505,7 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD] = { .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_bind_dacs, + .v.func = alc287_fixup_tb_vmaster_led, .chained = true, .chain_id = ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, }, @@ -6653,6 +6711,22 @@ static const struct hda_fixup alc269_fixups[] = { { 0x12, 0x90a60160 }, /* use as internal mic */ { } }, + }, + [ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x90170111 }, + { 0x19, 0x03a1113c }, + { 0x1a, 0x22a190a0 }, + { 0x1b, 0x90170110 }, + { } + } + }, + [ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_two, + .chained = true, + .chain_id = ALC245_FIXUP_HP_X360_MUTE_LEDS } }; @@ -6706,6 +6780,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1640, "Acer Aspire A315-44P", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x173a, "Acer Swift SFG14-73", ALC245_FIXUP_ACER_MICMUTE_LED), @@ -6882,6 +6958,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8548, "HP EliteBook x360 830 G6", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x854a, "HP EliteBook 830 G6", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x856a, "HP Pavilion 15-cs1xxx", ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B), SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), SND_PCI_QUIRK(0x103c, 0x8603, "HP Omen 17-cb0xxx", ALC285_FIXUP_HP_MUTE_LED), @@ -7073,7 +7150,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8be6, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8be7, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8be8, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy x360 2-in-1 Laptop 15-fh0xxx", ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX), SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre x360 2-in-1 Laptop 14-eu0xxx", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX), @@ -7123,6 +7200,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8caf, "HP Elite mt645 G8 Mobile Thin Client", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8cbc, "HP Pavilion Laptop 16-ag0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), SND_PCI_QUIRK(0x103c, 0x8cbd, "HP Pavilion Aero Laptop 13-bg0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), SND_PCI_QUIRK(0x103c, 0x8cdd, "HP Spectre", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), SND_PCI_QUIRK(0x103c, 0x8cde, "HP OmniBook Ultra Flip Laptop 14t", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), @@ -7142,6 +7220,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8d90, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8d91, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8dcd, "HP Victus 15-fa2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8d9b, "HP 17 Turbine OmniBook 7 UMA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8d9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), @@ -7214,6 +7293,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8f0e, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8f2d, "HP Auster 14", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8f2e, "HP Auster 14", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8f3c, "HP EliteBook 6 G2a", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8f3d, "HP EliteBook 6 G2a", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8f40, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8f41, "HP ZBook 8 G2a 16", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8f42, "HP ZBook 8 G2a 14W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), @@ -7431,6 +7512,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x144d, 0xc870, "Samsung Galaxy Book2 Pro (NP950XED)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), SND_PCI_QUIRK(0x144d, 0xc872, "Samsung Galaxy Book2 Pro (NP950XEE)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), SND_PCI_QUIRK(0x144d, 0xc886, "Samsung Galaxy Book3 Pro (NP964XFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), + SND_PCI_QUIRK(0x144d, 0xc902, "Samsung Galaxy Book5 360 (NP750QHA)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x144d, 0xc1ca, "Samsung Galaxy Book3 Pro 360 (NP960QFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x144d, 0xc1cb, "Samsung Galaxy Book3 Pro 360 (NP965QFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x144d, 0xc1cc, "Samsung Galaxy Book3 Ultra (NT960XFH)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), @@ -7602,6 +7684,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C), + /* Yoga Pro 9 16IMH9 and Legion 7 16ITHG6 share PCI SSID 17aa:3811 + * with Legion S7 15IMH05; use codec SSID to distinguish them + */ + HDA_CODEC_QUIRK(0x17aa, 0x38d5, "Lenovo Yoga Pro 9 16IMH9", ALC287_FIXUP_TAS2781_I2C), + HDA_CODEC_QUIRK(0x17aa, 0x38d6, "Lenovo Yoga Pro 9 16IMH9", ALC287_FIXUP_TAS2781_I2C), + HDA_CODEC_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6), SND_PCI_QUIRK(0x17aa, 0x3811, "Legion S7 15IMH05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7), @@ -7675,6 +7763,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x38fc, "Lenovo Yoga Pro 7 15ASH11", ALC245_FIXUP_BASS_HP_DAC), SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), @@ -7747,10 +7836,13 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1d72, 0x1905, "Xiaomi Mi Laptop Pro 15", ALC256_FIXUP_XIAOMI_PRO15_RESUME), SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1e39, 0xca14, "MEDION NM14LNL", ALC233_FIXUP_MEDION_MTL_SPK), + SND_PCI_QUIRK(0x1e50, 0x7007, "Positivo DN50E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1ee7, 0x2078, "HONOR BRB-X M1010", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1ee7, 0x2081, "HONOR MRB-XXX M1020", ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO), SND_PCI_QUIRK(0x1f4c, 0xe001, "Minisforum V3 (SE)", ALC245_FIXUP_BASS_HP_DAC), SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -7774,7 +7866,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0xf111, 0x0009, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0xf111, 0x000b, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0xf111, 0x000c, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0xf111, 0x000f, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0xf111, 0x000f, "Framework Laptop 13 Pro PTL", ALC295_FIXUP_FRAMEWORK_LAPTOP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0xf111, 0x010f, "Framework Laptop 13 PTL", ALC295_FIXUP_FRAMEWORK_LAPTOP_LIMIT_INT_MIC_BOOST), #if 0 /* Below is a quirk table taken from the old code. @@ -7971,6 +8064,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC236_FIXUP_LENOVO_INV_DMIC, .name = "alc236-fixup-lenovo-inv-mic"}, {.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"}, {.id = ALC245_FIXUP_BASS_HP_DAC, .name = "alc245-fixup-bass-hp-dac"}, + {.id = ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO, .name = "alc256-honor-mrb-xxx-m1020-audio"}, {} }; #define ALC225_STANDARD_PINS \ diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig index fc5651e555e3..e51964c0a091 100644 --- a/sound/hda/codecs/side-codecs/Kconfig +++ b/sound/hda/codecs/side-codecs/Kconfig @@ -94,7 +94,6 @@ menu "CS35L56 driver options" config SND_HDA_SCODEC_CS35L56_CAL_DEBUGFS bool "CS35L56 create debugfs for factory calibration" - default N depends on DEBUG_FS select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON help diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index b64890006bb7..acfccc848f82 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -1896,8 +1896,10 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i cs35l41->dacpi = adev; physdev = get_device(acpi_get_first_physical_node(adev)); - if (!physdev) + if (!physdev) { + acpi_dev_put(adev); return -ENODEV; + } sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); if (IS_ERR(sub)) diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c index 1ace4beef508..cdbc576569ef 100644 --- a/sound/hda/codecs/side-codecs/cs35l56_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c @@ -180,11 +180,15 @@ static int cs35l56_hda_mixer_get(struct snd_kcontrol *kcontrol, { struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol); unsigned int reg_val; - int i; + int i, ret; cs35l56_hda_wait_dsp_ready(cs35l56); - regmap_read(cs35l56->base.regmap, kcontrol->private_value, ®_val); + ret = regmap_read(cs35l56->base.regmap, kcontrol->private_value, + ®_val); + if (ret) + return ret; + reg_val &= CS35L56_ASP_TXn_SRC_MASK; for (i = 0; i < CS35L56_NUM_INPUT_SRC; ++i) { @@ -203,15 +207,20 @@ static int cs35l56_hda_mixer_put(struct snd_kcontrol *kcontrol, struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol); unsigned int item = ucontrol->value.enumerated.item[0]; bool changed; + int ret; if (item >= CS35L56_NUM_INPUT_SRC) return -EINVAL; cs35l56_hda_wait_dsp_ready(cs35l56); - regmap_update_bits_check(cs35l56->base.regmap, kcontrol->private_value, - CS35L56_INPUT_MASK, cs35l56_tx_input_values[item], - &changed); + ret = regmap_update_bits_check(cs35l56->base.regmap, + kcontrol->private_value, + CS35L56_INPUT_MASK, + cs35l56_tx_input_values[item], + &changed); + if (ret) + return ret; return changed; } @@ -967,6 +976,7 @@ static int cs35l56_hda_system_resume(struct device *dev) static int cs35l56_hda_fixup_yoga9(struct cs35l56_hda *cs35l56, int *bus_addr) { /* The cirrus,dev-index property has the wrong values */ + cs35l56->num_amps = 2; switch (*bus_addr) { case 0x30: cs35l56->index = 1; @@ -1016,7 +1026,6 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) char hid_string[8]; struct acpi_device *adev; const char *property, *sub; - size_t nval; int i, ret; /* @@ -1032,6 +1041,7 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) return -ENODEV; } ACPI_COMPANION_SET(cs35l56->base.dev, adev); + acpi_dev_put(adev); } /* Initialize things that could be overwritten by a fixup */ @@ -1052,13 +1062,14 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) ret = -EINVAL; goto err; } - nval = ret; + cs35l56->num_amps = ret; - ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval); + ret = device_property_read_u32_array(cs35l56->base.dev, property, values, + cs35l56->num_amps); if (ret) goto err; - for (i = 0; i < nval; i++) { + for (i = 0; i < cs35l56->num_amps; i++) { if (values[i] == id) { cs35l56->index = i; break; @@ -1081,7 +1092,8 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) "Read ACPI _SUB failed(%ld): fallback to generic firmware\n", PTR_ERR(sub)); } else { - ret = cirrus_scodec_get_speaker_id(cs35l56->base.dev, cs35l56->index, nval, -1); + ret = cirrus_scodec_get_speaker_id(cs35l56->base.dev, cs35l56->index, + cs35l56->num_amps, -1); if (ret == -ENOENT) { cs35l56->system_name = sub; } else if (ret >= 0) { diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.h b/sound/hda/codecs/side-codecs/cs35l56_hda.h index cb4b5e7356a3..3705af7c186b 100644 --- a/sound/hda/codecs/side-codecs/cs35l56_hda.h +++ b/sound/hda/codecs/side-codecs/cs35l56_hda.h @@ -26,6 +26,7 @@ struct cs35l56_hda { struct work_struct dsp_work; int index; + int num_amps; const char *system_name; const char *amp_name; diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c index f860e0eb7602..0e4f3553f273 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -132,10 +132,18 @@ static int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tas_priv, int ret, val; /* - * In our TAS2781 SPI mode, read/write was masked in last bit of - * address, it cause regmap_update_bits() not work as expected. + * In TAS2781 SPI mode, when accessing non-book-zero or page numbers + * greater than 1 in book 0, an additional byte must be read. The + * first byte in such cases is a dummy byte and should be ignored. */ - ret = tasdevice_dev_read(tas_priv, chn, reg, &val); + if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) { + unsigned char buf[2]; + + ret = tasdevice_dev_bulk_read(tas_priv, chn, reg, buf, 2); + val = buf[1]; + } else { + ret = tasdevice_dev_read(tas_priv, chn, reg, &val); + } if (ret < 0) { dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); return ret; @@ -788,6 +796,7 @@ static int tas2781_hda_spi_probe(struct spi_device *spi) } if (strstr(dev_name(&spi->dev), "TXNW2781")) { device_name = "TXNW2781"; + tas_hda->priv->chip_id = TAS2781; } else { dev_err(tas_priv->dev, "Unmatched spi dev %s\n", dev_name(&spi->dev)); diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index c2af2511a831..81f266b9b850 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -1699,6 +1699,9 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, unsigned short flags = 0; struct hda_nid_item *item; + if (!kctl) + return -EINVAL; + if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) { flags |= HDA_NID_ITEM_AMP; if (nid == 0) diff --git a/sound/hda/common/hda_controller.h b/sound/hda/common/hda_controller.h index c2d0109866e6..7434f38038a0 100644 --- a/sound/hda/common/hda_controller.h +++ b/sound/hda/common/hda_controller.h @@ -127,10 +127,6 @@ struct azx { unsigned int beep_mode; bool ctl_dev_id; -#ifdef CONFIG_SND_HDA_PATCH_LOADER - const struct firmware *fw; -#endif - /* flags */ int bdl_pos_adj; unsigned int running:1; diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 257c498c3260..c87d75dbd8aa 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -1385,9 +1385,6 @@ static void azx_free(struct azx *chip) azx_free_streams(chip); snd_hdac_bus_exit(bus); -#ifdef CONFIG_SND_HDA_PATCH_LOADER - release_firmware(chip->fw); -#endif display_power(chip, false); if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) @@ -2037,24 +2034,6 @@ static int azx_first_init(struct azx *chip) return 0; } -#ifdef CONFIG_SND_HDA_PATCH_LOADER -/* callback from request_firmware_nowait() */ -static void azx_firmware_cb(const struct firmware *fw, void *context) -{ - struct snd_card *card = context; - struct azx *chip = card->private_data; - - if (fw) - chip->fw = fw; - else - dev_err(card->dev, "Cannot load firmware, continue without patching\n"); - if (!chip->disabled) { - /* continue probing */ - azx_probe_continue(chip); - } -} -#endif - static int disable_msi_reset_irq(struct azx *chip) { struct hdac_bus *bus = azx_bus(chip); @@ -2131,7 +2110,6 @@ static int azx_probe(struct pci_dev *pci, struct snd_card *card; struct hda_intel *hda; struct azx *chip; - bool schedule_probe; int dev; int err; @@ -2227,22 +2205,7 @@ static int azx_probe(struct pci_dev *pci, chip->disabled = true; } - schedule_probe = !chip->disabled; - -#ifdef CONFIG_SND_HDA_PATCH_LOADER - if (patch[dev] && *patch[dev]) { - dev_info(card->dev, "Applying patch firmware '%s'\n", - patch[dev]); - err = request_firmware_nowait(THIS_MODULE, true, patch[dev], - &pci->dev, GFP_KERNEL, card, - azx_firmware_cb); - if (err < 0) - goto out_free; - schedule_probe = false; /* continued in azx_firmware_cb() */ - } -#endif /* CONFIG_SND_HDA_PATCH_LOADER */ - - if (schedule_probe) + if (!chip->disabled) schedule_delayed_work(&hda->probe_work, 0); set_bit(dev, probed_devs); @@ -2371,11 +2334,20 @@ static int azx_probe_continue(struct azx *chip) } #ifdef CONFIG_SND_HDA_PATCH_LOADER - if (chip->fw) { - err = snd_hda_load_patch(&chip->bus, chip->fw->size, - chip->fw->data); - if (err < 0) - goto out_free; + if (patch[dev] && *patch[dev]) { + const struct firmware *fw = NULL; + + dev_info(&pci->dev, "Applying patch firmware '%s'\n", + patch[dev]); + if (request_firmware(&fw, patch[dev], &pci->dev) < 0) { + dev_err(&pci->dev, + "Cannot load firmware, continue without patching\n"); + } else { + err = snd_hda_load_patch(&chip->bus, fw->size, fw->data); + release_firmware(fw); + if (err < 0) + goto out_free; + } } #endif diff --git a/sound/hda/core/hdmi_chmap.c b/sound/hda/core/hdmi_chmap.c index 7b276047f85a..c897fc443467 100644 --- a/sound/hda/core/hdmi_chmap.c +++ b/sound/hda/core/hdmi_chmap.c @@ -353,13 +353,16 @@ static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap, if (hdmi_channel_mapping[ca][1] == 0) { int hdmi_slot = 0; /* fill actual channel mappings in ALSA channel (i) order */ - for (i = 0; i < ch_alloc->channels; i++) { - while (!WARN_ON(hdmi_slot >= 8) && - !ch_alloc->speakers[7 - hdmi_slot]) - hdmi_slot++; /* skip zero slots */ + for (i = 0; i < ch_alloc->channels && hdmi_slot < 8; i++) { + while (!ch_alloc->speakers[7 - hdmi_slot]) { + /* skip zero slots */ + if (++hdmi_slot >= 8) + goto out; + } hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; } + out: /* fill the rest of the slots with ALSA channel 0xf */ for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) if (!ch_alloc->speakers[7 - hdmi_slot]) diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 33034e07b3d6..636f309c9424 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -421,30 +421,26 @@ static int snd_als4000_capture_trigger(struct snd_pcm_substream *substream, int { struct snd_sb *chip = snd_pcm_substream_chip(substream); int result = 0; - - /* FIXME race condition in here!!! - chip->mode non-atomic update gets consistently protected - by reg_lock always, _except_ for this place!! - Probably need to take reg_lock as outer (or inner??) lock, too. - (or serialize both lock operations? probably not, though... - racy?) - */ - guard(spinlock)(&chip->mixer_lock); - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - chip->mode |= SB_RATE_LOCK_CAPTURE; - snd_als4_cr_write(chip, ALS4K_CR1E_FIFO2_CONTROL, - capture_cmd(chip)); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - chip->mode &= ~SB_RATE_LOCK_CAPTURE; - snd_als4_cr_write(chip, ALS4K_CR1E_FIFO2_CONTROL, - capture_cmd(chip)); - break; - default: - result = -EINVAL; - break; + + guard(spinlock)(&chip->reg_lock); + scoped_guard(spinlock, &chip->mixer_lock) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + chip->mode |= SB_RATE_LOCK_CAPTURE; + snd_als4_cr_write(chip, ALS4K_CR1E_FIFO2_CONTROL, + capture_cmd(chip)); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + chip->mode &= ~SB_RATE_LOCK_CAPTURE; + snd_als4_cr_write(chip, ALS4K_CR1E_FIFO2_CONTROL, + capture_cmd(chip)); + break; + default: + result = -EINVAL; + break; + } } return result; } diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c index a7a551366a40..235d6cc83fa9 100644 --- a/sound/soc/amd/acp/acp-legacy-mach.c +++ b/sound/soc/amd/acp/acp-legacy-mach.c @@ -174,7 +174,7 @@ static int acp_asoc_probe(struct platform_device *pdev) acp_card_drvdata->acp_rev = mach->mach_params.subsystem_rev; dmi_id = dmi_first_match(acp_quirk_table); - if (dmi_id && dmi_id->driver_data) + if (dmi_id && dmi_id->driver_data == (void *)QUIRK_TDM_MODE_ENABLE) acp_card_drvdata->tdm_mode = dmi_id->driver_data; ret = acp_legacy_dai_links_create(card); diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index 09f6c9a2c041..ef784cca13f2 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "../../codecs/rt5682.h" #include "../../codecs/rt1019.h" @@ -37,15 +38,21 @@ #define NAU8821_FREQ_OUT 12288000 #define MAX98388_CODEC_DAI "max98388-aif1" -#define TDM_MODE_ENABLE 1 - const struct dmi_system_id acp_quirk_table[] = { { /* Google skyrim proto-0 */ .matches = { DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "Google_Skyrim"), }, - .driver_data = (void *)TDM_MODE_ENABLE, + .driver_data = (void *)QUIRK_TDM_MODE_ENABLE, + }, + { + /* Valve Steam Deck OLED */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Valve"), + DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"), + }, + .driver_data = (void *)QUIRK_REMAP_DMIC_BT, }, {} }; @@ -1401,6 +1408,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) struct snd_soc_dai_link *links; struct device *dev = card->dev; struct acp_card_drvdata *drv_data = card->drvdata; + const struct dmi_system_id *dmi_id = dmi_first_match(acp_quirk_table); int i = 0, num_links = 0; if (drv_data->hs_cpu_id) @@ -1572,6 +1580,9 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) links[i].codecs = &snd_soc_dummy_dlc; links[i].num_codecs = 1; } + + if (dmi_id && dmi_id->driver_data == (void *)QUIRK_REMAP_DMIC_BT) + links[i].id = DMIC_BE_ID; i++; } @@ -1587,6 +1598,11 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) links[i].capture_only = 1; links[i].nonatomic = true; links[i].no_pcm = 1; + + if (dmi_id && dmi_id->driver_data == (void *)QUIRK_REMAP_DMIC_BT) { + links[i].id = BT_BE_ID; + dev_dbg(dev, "quirk REMAP_DMIC_BT enabled\n"); + } } card->dai_link = links; diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h index f94c30c20f20..7177d3fd9619 100644 --- a/sound/soc/amd/acp/acp-mach.h +++ b/sound/soc/amd/acp/acp-mach.h @@ -26,6 +26,10 @@ #define acp_get_drvdata(card) ((struct acp_card_drvdata *)(card)->drvdata) +/* List of DMI quirks - check acp-mach-common.c for usage. */ +#define QUIRK_TDM_MODE_ENABLE 1 +#define QUIRK_REMAP_DMIC_BT 2 + enum be_id { HEADSET_BE_ID = 0, AMP_BE_ID, diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 6215e31ecedd..36ecef7013b9 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -110,7 +110,7 @@ static int acp_sof_probe(struct platform_device *pdev) acp_card_drvdata = card->drvdata; dmi_id = dmi_first_match(acp_quirk_table); - if (dmi_id && dmi_id->driver_data) + if (dmi_id && dmi_id->driver_data == (void *)QUIRK_TDM_MODE_ENABLE) acp_card_drvdata->tdm_mode = dmi_id->driver_data; acp_card_drvdata->acp_rev = mach->mach_params.subsystem_rev; diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index c5cf45881416..7a637d6b5576 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -59,6 +59,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "HP Laptop 15-fc0xxx"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "OMEN Gaming Laptop 16-ap0xxx"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -479,6 +486,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 B7ED"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 C7VE"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -668,6 +682,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "8EE4"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8E35"), + } + }, { .driver_data = &acp6x_card, .matches = { diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index fdda1b747bf7..6e8ef9cd1b31 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -60,19 +60,6 @@ low before proceeding with the configuration sequence */ #define AB8500_ANC_SM_DELAY 2000 -#define AB8500_FILTER_CONTROL(xname, xcount, xmin, xmax) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ - .info = filter_control_info, \ - .get = filter_control_get, .put = filter_control_put, \ - .private_value = (unsigned long)&(struct filter_control) \ - {.count = xcount, .min = xmin, .max = xmax} } - -struct filter_control { - long min, max; - unsigned int count; - long value[128]; -}; - /* Sidetone states */ static const char * const enum_sid_state[] = { "Unconfigured", @@ -85,45 +72,13 @@ enum sid_state { SID_FIR_CONFIGURED = 2, }; -static const char * const enum_anc_state[] = { - "Unconfigured", - "Apply FIR and IIR", - "FIR and IIR are configured", - "Apply FIR", - "FIR is configured", - "Apply IIR", - "IIR is configured" -}; -enum anc_state { - ANC_UNCONFIGURED = 0, - ANC_APPLY_FIR_IIR = 1, - ANC_FIR_IIR_CONFIGURED = 2, - ANC_APPLY_FIR = 3, - ANC_FIR_CONFIGURED = 4, - ANC_APPLY_IIR = 5, - ANC_IIR_CONFIGURED = 6 -}; - -/* Analog microphones */ -enum amic_idx { - AMIC_IDX_1A, - AMIC_IDX_1B, - AMIC_IDX_2 -}; - /* Private data for AB8500 device-driver */ struct ab8500_codec_drvdata { struct regmap *regmap; struct mutex ctrl_lock; /* Sidetone */ - long *sid_fir_values; enum sid_state sid_status; - - /* ANC */ - long *anc_fir_values; - long *anc_iir_values; - enum anc_state anc_status; }; static inline const char *amic_micbias_str(enum amic_micbias micbias) @@ -1024,89 +979,6 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = { {"MIC2 V-AMICx Enable", NULL, "V-AMIC2"}, }; -/* ANC FIR-coefficients configuration sequence */ -static void anc_fir(struct snd_soc_component *component, - unsigned int bnk, unsigned int par, unsigned int val) -{ - if (par == 0 && bnk == 0) - snd_soc_component_update_bits(component, AB8500_ANCCONF1, - BIT(AB8500_ANCCONF1_ANCFIRUPDATE), - BIT(AB8500_ANCCONF1_ANCFIRUPDATE)); - - snd_soc_component_write(component, AB8500_ANCCONF5, val >> 8 & 0xff); - snd_soc_component_write(component, AB8500_ANCCONF6, val & 0xff); - - if (par == AB8500_ANC_FIR_COEFFS - 1 && bnk == 1) - snd_soc_component_update_bits(component, AB8500_ANCCONF1, - BIT(AB8500_ANCCONF1_ANCFIRUPDATE), 0); -} - -/* ANC IIR-coefficients configuration sequence */ -static void anc_iir(struct snd_soc_component *component, unsigned int bnk, - unsigned int par, unsigned int val) -{ - if (par == 0) { - if (bnk == 0) { - snd_soc_component_update_bits(component, AB8500_ANCCONF1, - BIT(AB8500_ANCCONF1_ANCIIRINIT), - BIT(AB8500_ANCCONF1_ANCIIRINIT)); - usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2); - snd_soc_component_update_bits(component, AB8500_ANCCONF1, - BIT(AB8500_ANCCONF1_ANCIIRINIT), 0); - usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2); - } else { - snd_soc_component_update_bits(component, AB8500_ANCCONF1, - BIT(AB8500_ANCCONF1_ANCIIRUPDATE), - BIT(AB8500_ANCCONF1_ANCIIRUPDATE)); - } - } else if (par > 3) { - snd_soc_component_write(component, AB8500_ANCCONF7, 0); - snd_soc_component_write(component, AB8500_ANCCONF8, val >> 16 & 0xff); - } - - snd_soc_component_write(component, AB8500_ANCCONF7, val >> 8 & 0xff); - snd_soc_component_write(component, AB8500_ANCCONF8, val & 0xff); - - if (par == AB8500_ANC_IIR_COEFFS - 1 && bnk == 1) - snd_soc_component_update_bits(component, AB8500_ANCCONF1, - BIT(AB8500_ANCCONF1_ANCIIRUPDATE), 0); -} - -/* ANC IIR-/FIR-coefficients configuration sequence */ -static void anc_configure(struct snd_soc_component *component, - bool apply_fir, bool apply_iir) -{ - struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); - unsigned int bnk, par, val; - - dev_dbg(component->dev, "%s: Enter.\n", __func__); - - if (apply_fir) - snd_soc_component_update_bits(component, AB8500_ANCCONF1, - BIT(AB8500_ANCCONF1_ENANC), 0); - - snd_soc_component_update_bits(component, AB8500_ANCCONF1, - BIT(AB8500_ANCCONF1_ENANC), BIT(AB8500_ANCCONF1_ENANC)); - - if (apply_fir) - for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++) - for (par = 0; par < AB8500_ANC_FIR_COEFFS; par++) { - val = snd_soc_component_read(component, - drvdata->anc_fir_values[par]); - anc_fir(component, bnk, par, val); - } - - if (apply_iir) - for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++) - for (par = 0; par < AB8500_ANC_IIR_COEFFS; par++) { - val = snd_soc_component_read(component, - drvdata->anc_iir_values[par]); - anc_iir(component, bnk, par, val); - } - - dev_dbg(component->dev, "%s: Exit.\n", __func__); -} - /* * Control-events */ @@ -1130,7 +1002,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); - unsigned int param, sidconf, val; + unsigned int param, sidconf; int status = 1; dev_dbg(component->dev, "%s: Enter\n", __func__); @@ -1159,9 +1031,8 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, snd_soc_component_write(component, AB8500_SIDFIRADR, 0); for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) { - val = snd_soc_component_read(component, drvdata->sid_fir_values[param]); - snd_soc_component_write(component, AB8500_SIDFIRCOEF1, val >> 8 & 0xff); - snd_soc_component_write(component, AB8500_SIDFIRCOEF2, val & 0xff); + snd_soc_component_write(component, AB8500_SIDFIRCOEF1, 0); + snd_soc_component_write(component, AB8500_SIDFIRCOEF2, 0); } snd_soc_component_update_bits(component, AB8500_SIDFIRADR, @@ -1180,136 +1051,6 @@ out: return status; } -static int anc_status_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); - - mutex_lock(&drvdata->ctrl_lock); - ucontrol->value.enumerated.item[0] = drvdata->anc_status; - mutex_unlock(&drvdata->ctrl_lock); - - return 0; -} - -static int anc_status_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); - struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); - struct device *dev = component->dev; - bool apply_fir, apply_iir; - unsigned int req; - int status; - - dev_dbg(dev, "%s: Enter.\n", __func__); - - mutex_lock(&drvdata->ctrl_lock); - - req = ucontrol->value.enumerated.item[0]; - if (req >= ARRAY_SIZE(enum_anc_state)) { - status = -EINVAL; - goto cleanup; - } - if (req != ANC_APPLY_FIR_IIR && req != ANC_APPLY_FIR && - req != ANC_APPLY_IIR) { - dev_err(dev, "%s: ERROR: Unsupported status to set '%s'!\n", - __func__, enum_anc_state[req]); - status = -EINVAL; - goto cleanup; - } - apply_fir = req == ANC_APPLY_FIR || req == ANC_APPLY_FIR_IIR; - apply_iir = req == ANC_APPLY_IIR || req == ANC_APPLY_FIR_IIR; - - status = snd_soc_dapm_force_enable_pin(dapm, "ANC Configure Input"); - if (status < 0) { - dev_err(dev, - "%s: ERROR: Failed to enable power (status = %d)!\n", - __func__, status); - goto cleanup; - } - snd_soc_dapm_sync(dapm); - - anc_configure(component, apply_fir, apply_iir); - - if (apply_fir) { - if (drvdata->anc_status == ANC_IIR_CONFIGURED) - drvdata->anc_status = ANC_FIR_IIR_CONFIGURED; - else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED) - drvdata->anc_status = ANC_FIR_CONFIGURED; - } - if (apply_iir) { - if (drvdata->anc_status == ANC_FIR_CONFIGURED) - drvdata->anc_status = ANC_FIR_IIR_CONFIGURED; - else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED) - drvdata->anc_status = ANC_IIR_CONFIGURED; - } - - status = snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); - snd_soc_dapm_sync(dapm); - -cleanup: - mutex_unlock(&drvdata->ctrl_lock); - - if (status < 0) - dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n", - __func__, status); - - dev_dbg(dev, "%s: Exit.\n", __func__); - - return (status < 0) ? status : 1; -} - -static int filter_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct filter_control *fc = - (struct filter_control *)kcontrol->private_value; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = fc->count; - uinfo->value.integer.min = fc->min; - uinfo->value.integer.max = fc->max; - - return 0; -} - -static int filter_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct ab8500_codec_drvdata *drvdata = snd_soc_component_get_drvdata(component); - struct filter_control *fc = - (struct filter_control *)kcontrol->private_value; - unsigned int i; - - mutex_lock(&drvdata->ctrl_lock); - for (i = 0; i < fc->count; i++) - ucontrol->value.integer.value[i] = fc->value[i]; - mutex_unlock(&drvdata->ctrl_lock); - - return 0; -} - -static int filter_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct ab8500_codec_drvdata *drvdata = snd_soc_component_get_drvdata(component); - struct filter_control *fc = - (struct filter_control *)kcontrol->private_value; - unsigned int i; - - mutex_lock(&drvdata->ctrl_lock); - for (i = 0; i < fc->count; i++) - fc->value[i] = ucontrol->value.integer.value[i]; - mutex_unlock(&drvdata->ctrl_lock); - - return 0; -} - /* * Controls - Non-DAPM ASoC */ @@ -1597,7 +1338,6 @@ static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast, static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state); /* ANC */ -static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_ancstate, enum_anc_state); static struct snd_kcontrol_new ab8500_ctrls[] = { /* Charge pump */ @@ -1873,8 +1613,6 @@ static struct snd_kcontrol_new ab8500_ctrls[] = { AB8500_FIFOCONF6_BFIFOSAMPLE_MAX, 0), /* ANC */ - SOC_ENUM_EXT("ANC Status", soc_enum_ancstate, - anc_status_control_get, anc_status_control_put), SOC_SINGLE_XR_SX("ANC Warp Delay Shift", AB8500_ANCCONF2, 1, AB8500_ANCCONF2_SHIFT, AB8500_ANCCONF2_MIN, AB8500_ANCCONF2_MAX, 0), @@ -1895,21 +1633,6 @@ static struct snd_kcontrol_new ab8500_ctrls[] = { AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0), }; -static struct snd_kcontrol_new ab8500_filter_controls[] = { - AB8500_FILTER_CONTROL("ANC FIR Coefficients", AB8500_ANC_FIR_COEFFS, - AB8500_ANC_FIR_COEFF_MIN, AB8500_ANC_FIR_COEFF_MAX), - AB8500_FILTER_CONTROL("ANC IIR Coefficients", AB8500_ANC_IIR_COEFFS, - AB8500_ANC_IIR_COEFF_MIN, AB8500_ANC_IIR_COEFF_MAX), - AB8500_FILTER_CONTROL("Sidetone FIR Coefficients", - AB8500_SID_FIR_COEFFS, AB8500_SID_FIR_COEFF_MIN, - AB8500_SID_FIR_COEFF_MAX) -}; -enum ab8500_filter { - AB8500_FILTER_ANC_FIR = 0, - AB8500_FILTER_ANC_IIR = 1, - AB8500_FILTER_SID_FIR = 2, -}; - /* * Extended interface for codec-driver */ @@ -2454,7 +2177,6 @@ static int ab8500_codec_probe(struct snd_soc_component *component) struct device_node *np = dev->of_node; struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev); struct ab8500_codec_platform_data codec_pdata; - struct filter_control *fc; int status; dev_dbg(dev, "%s: Enter.\n", __func__); @@ -2486,25 +2208,6 @@ static int ab8500_codec_probe(struct snd_soc_component *component) snd_soc_component_write(component, AB8500_SHORTCIRCONF, BIT(AB8500_SHORTCIRCONF_HSZCDDIS)); - /* Add filter controls */ - status = snd_soc_add_component_controls(component, ab8500_filter_controls, - ARRAY_SIZE(ab8500_filter_controls)); - if (status < 0) { - dev_err(dev, - "%s: failed to add ab8500 filter controls (%d).\n", - __func__, status); - return status; - } - fc = (struct filter_control *) - &ab8500_filter_controls[AB8500_FILTER_ANC_FIR].private_value; - drvdata->anc_fir_values = (long *)fc->value; - fc = (struct filter_control *) - &ab8500_filter_controls[AB8500_FILTER_ANC_IIR].private_value; - drvdata->anc_iir_values = (long *)fc->value; - fc = (struct filter_control *) - &ab8500_filter_controls[AB8500_FILTER_SID_FIR].private_value; - drvdata->sid_fir_values = (long *)fc->value; - snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); mutex_init(&drvdata->ctrl_lock); @@ -2538,7 +2241,6 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev) if (!drvdata) return -ENOMEM; drvdata->sid_status = SID_UNCONFIGURED; - drvdata->anc_status = ANC_UNCONFIGURED; dev_set_drvdata(&pdev->dev, drvdata); drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev, diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index 3602b5b9f7d7..dd09bac652f7 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -456,8 +456,6 @@ static void aw88395_hw_reset(struct aw88395 *aw88395) usleep_range(AW88395_1000_US, AW88395_1000_US + 10); gpiod_set_value_cansleep(aw88395->reset_gpio, 1); usleep_range(AW88395_1000_US, AW88395_1000_US + 10); - } else { - dev_err(aw88395->aw_pa->dev, "%s failed", __func__); } } @@ -522,9 +520,10 @@ static int aw88395_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, aw88395); aw88395->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(aw88395->reset_gpio)) - dev_info(&i2c->dev, "reset gpio not defined\n"); - + if (IS_ERR(aw88395->reset_gpio)) { + return dev_err_probe(&i2c->dev, PTR_ERR(aw88395->reset_gpio), + "failed to get reset gpio\n"); + } /* hardware reset */ aw88395_hw_reset(aw88395); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index e05d975ba794..795e2764d67e 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -108,8 +108,6 @@ int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, "SND_SOC_CS35L56_SHARED"); static const struct reg_default cs35l56_reg_defaults[] = { - /* no defaults for OTP_MEM - first read populates cache */ - { CS35L56_ASP1_ENABLES1, 0x00000000 }, { CS35L56_ASP1_CONTROL1, 0x00000028 }, { CS35L56_ASP1_CONTROL2, 0x18180200 }, @@ -138,8 +136,6 @@ static const struct reg_default cs35l56_reg_defaults[] = { }; static const struct reg_default cs35l63_reg_defaults[] = { - /* no defaults for OTP_MEM - first read populates cache */ - { CS35L56_ASP1_ENABLES1, 0x00000000 }, { CS35L56_ASP1_CONTROL1, 0x00000028 }, { CS35L56_ASP1_CONTROL2, 0x18180200 }, @@ -282,6 +278,9 @@ static bool cs35l56_common_volatile_reg(unsigned int reg) case CS35L56_GLOBAL_ENABLES: /* owned by firmware */ case CS35L56_BLOCK_ENABLES: /* owned by firmware */ case CS35L56_BLOCK_ENABLES2: /* owned by firmware */ + case CS35L56_OTP_MEM_53: + case CS35L56_OTP_MEM_54: + case CS35L56_OTP_MEM_55: case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG: case CS35L56_UPDATE_REGS: case CS35L56_REFCLK_INPUT: /* owned by firmware */ @@ -852,9 +851,11 @@ out_sync: err: regcache_cache_only(cs35l56_base->regmap, true); - regmap_multi_reg_write_bypassed(cs35l56_base->regmap, - cs35l56_hibernate_seq, - ARRAY_SIZE(cs35l56_hibernate_seq)); + if (cs35l56_base->can_hibernate) { + regmap_multi_reg_write_bypassed(cs35l56_base->regmap, + cs35l56_hibernate_seq, + ARRAY_SIZE(cs35l56_hibernate_seq)); + } return ret; } @@ -1729,8 +1730,7 @@ int cs35l56_read_onchip_spkid(struct cs35l56_base *cs35l56_base) ret = regmap_read(regmap, CS35L56_GPIO_STATUS1, &val); if (ret) { - dev_err(cs35l56_base->dev, "GPIO%d status read failed: %d\n", - cs35l56_base->onchip_spkid_gpios[i] + 1, ret); + dev_err(cs35l56_base->dev, "GPIO status read failed: %d\n", ret); return ret; } diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 378017fcea10..849d70ca23d6 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1956,9 +1956,9 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) goto err; } - ret = devm_snd_soc_register_component(cs35l56->base.dev, - &soc_component_dev_cs35l56, - cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); + ret = snd_soc_register_component(cs35l56->base.dev, + &soc_component_dev_cs35l56, + cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); if (ret < 0) { dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); goto err; @@ -1970,6 +1970,9 @@ err: gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); + if (cs35l56->dsp_wq) + destroy_workqueue(cs35l56->dsp_wq); + return ret; } EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, "SND_SOC_CS35L56_CORE"); @@ -2057,6 +2060,8 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_init, "SND_SOC_CS35L56_CORE"); void cs35l56_remove(struct cs35l56_private *cs35l56) { + snd_soc_unregister_component(cs35l56->base.dev); + cs35l56->base.init_done = false; /* diff --git a/sound/soc/codecs/es8311.c b/sound/soc/codecs/es8311.c index 0b07a53cc792..564af5c04dbb 100644 --- a/sound/soc/codecs/es8311.c +++ b/sound/soc/codecs/es8311.c @@ -761,6 +761,7 @@ static int es8311_set_bias_level(struct snd_soc_component *component, { struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + int ret; switch (level) { case SND_SOC_BIAS_ON: @@ -769,17 +770,21 @@ static int es8311_set_bias_level(struct snd_soc_component *component, break; case SND_SOC_BIAS_STANDBY: if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) { - int ret = clk_prepare_enable(es8311->mclk); + ret = clk_prepare_enable(es8311->mclk); if (ret) { dev_err(component->dev, "unable to prepare mclk\n"); return ret; } - snd_soc_component_update_bits( - component, ES8311_SYS3, - ES8311_SYS3_PDN_VMIDSEL_MASK, - ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED); + ret = snd_soc_component_update_bits( + component, ES8311_SYS3, + ES8311_SYS3_PDN_VMIDSEL_MASK, + ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED); + if (ret < 0) { + clk_disable_unprepare(es8311->mclk); + return ret; + } } break; @@ -862,13 +867,18 @@ static int es8311_suspend(struct snd_soc_component *component) static int es8311_resume(struct snd_soc_component *component) { struct es8311_priv *es8311; + int ret; es8311 = snd_soc_component_get_drvdata(component); es8311_reset(component, false); regcache_cache_only(es8311->regmap, false); - regcache_sync(es8311->regmap); + ret = regcache_sync(es8311->regmap); + if (ret) { + dev_err(component->dev, "unable to sync regcache\n"); + return ret; + } return 0; } diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 8d418cae371a..449d9574b03a 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -892,7 +892,7 @@ static int es8389_probe(struct snd_soc_component *component) return ret; } - es8389->mclk = devm_clk_get(component->dev, "mclk"); + es8389->mclk = devm_clk_get_optional(component->dev, "mclk"); if (IS_ERR(es8389->mclk)) return dev_err_probe(component->dev, PTR_ERR(es8389->mclk), "ES8389 is unable to get mclk\n"); diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index b0aeeab26bd9..192faa431b5e 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1498,6 +1498,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) } if (!retry && !(value & 0x40)) { dev_err(dev, "%s: RAE is not ready to load\n", __func__); + release_firmware(rae_fw); return -ETIMEDOUT; } diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 423b7073b302..55211266927d 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -684,18 +684,33 @@ static int tas2764_read_die_temp(struct tas2764_priv *tas2764, long *result) * As per datasheet, subtract 93 from raw value to get degrees * Celsius. hwmon wants millidegrees. * - * NOTE: The chip will initialise the TAS2764_TEMP register to - * 2.6 *C to avoid triggering temperature protection. Since the - * ADC is powered down during software shutdown, this value will - * persist until the chip is fully powered up (e.g. the PCM it's - * attached to is opened). The ADC will power down again when - * the chip is put back into software shutdown, with the last - * value sampled persisting in the ADC's register. + * NOTE: The TAS2764 datasheet mentions initialising TAS2764_TEMP + * such that the temperature is 2.6 *C, however the register + * is actually initialised to 0. The ADC is also powered down during + * software shutdown. The last sampled temperature will persist + * in the register while the amp is in this power state. */ + if (reg == 0) + return -ENODATA; + *result = (reg - 93) * 1000; return 0; } +static int tas2764_hwmon_is_fault(struct tas2764_priv *tas2764, long *result) +{ + int ret; + long temp; + + ret = tas2764_read_die_temp(tas2764, &temp); + if (ret == -ENODATA) { + *result = true; + return 0; + } + + return ret; +} + static umode_t tas2764_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) @@ -705,6 +720,7 @@ static umode_t tas2764_hwmon_is_visible(const void *data, switch (attr) { case hwmon_temp_input: + case hwmon_temp_fault: return 0444; default: break; @@ -724,6 +740,9 @@ static int tas2764_hwmon_read(struct device *dev, case hwmon_temp_input: ret = tas2764_read_die_temp(tas2764, val); break; + case hwmon_temp_fault: + ret = tas2764_hwmon_is_fault(tas2764, val); + break; default: ret = -EOPNOTSUPP; break; @@ -733,7 +752,7 @@ static int tas2764_hwmon_read(struct device *dev, } static const struct hwmon_channel_info *const tas2764_hwmon_info[] = { - HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_FAULT), NULL }; @@ -904,6 +923,7 @@ static bool tas2764_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case TAS2764_SW_RST: + case TAS2764_TEMP: case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4: case TAS2764_INT_CLK_CFG: return true; diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index d4d7d056141b..dbda9f327535 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -624,7 +624,7 @@ static int tas2770_read_die_temp(struct tas2770_priv *tas2770, long *result) /* * As per datasheet: divide register by 16 and subtract 93 to get * degrees Celsius. hwmon requires millidegrees. Let's avoid rounding - * errors by subtracting 93 * 16 then multiplying by 1000 / 16. + * errors by subtracting 93 * 16 and scaling before dividing. * * NOTE: The ADC registers are initialised to 0 on reset. This means * that the temperature will read -93 *C until the chip is brought out @@ -633,10 +633,27 @@ static int tas2770_read_die_temp(struct tas2770_priv *tas2770, long *result) * value read back from its registers will be the last value sampled * before entering software shutdown. */ - *result = (reading - (93 * 16)) * (1000 / 16); + if (reading == 0) + return -ENODATA; + + *result = (reading - (93 * 16)) * 1000 / 16; return 0; } +static int tas2770_hwmon_is_fault(struct tas2770_priv *tas2770, long *result) +{ + int ret; + long temp; + + ret = tas2770_read_die_temp(tas2770, &temp); + if (ret == -ENODATA) { + *result = true; + return 0; + } + + return ret; +} + static umode_t tas2770_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) @@ -646,6 +663,7 @@ static umode_t tas2770_hwmon_is_visible(const void *data, switch (attr) { case hwmon_temp_input: + case hwmon_temp_fault: return 0444; default: break; @@ -665,6 +683,9 @@ static int tas2770_hwmon_read(struct device *dev, case hwmon_temp_input: ret = tas2770_read_die_temp(tas2770, val); break; + case hwmon_temp_fault: + ret = tas2770_hwmon_is_fault(tas2770, val); + break; default: ret = -EOPNOTSUPP; break; @@ -674,7 +695,7 @@ static int tas2770_hwmon_read(struct device *dev, } static const struct hwmon_channel_info *const tas2770_hwmon_info[] = { - HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_FAULT), NULL }; diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index a1d86bd309f4..885e0b6fed00 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -2487,7 +2487,7 @@ static int tas2781_cali_preproc(struct tasdevice_priv *priv, int i) if (spec == NULL) return -ENOMEM; priv->tasdevice[i].cali_specific = spec; - rc = tasdevice_dev_bulk_read(priv, i, p->r0_reg, r0_deflt, 4); + rc = priv->dev_bulk_read(priv, i, p->r0_reg, r0_deflt, 4); if (rc < 0) { dev_err(priv->dev, "invalid RE from %d = %d\n", i, rc); return rc; @@ -2511,9 +2511,8 @@ static int tas2781_cali_preproc(struct tasdevice_priv *priv, int i) TASDEVICE_REG(0, 0x1b, 0x34) : TASDEVICE_REG(0, 0x18, 0x1c); - rc = tasdevice_dev_bulk_read(priv, i, - spec->sin_gni_reg, - spec->sin_gni, 4); + rc = priv->dev_bulk_read(priv, i, spec->sin_gni_reg, + spec->sin_gni, 4); if (rc < 0) { dev_err(priv->dev, "wrong sinegaini %d = %d\n", i, rc); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index c593f9da0c5b..a78a8f9b9833 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -119,10 +119,10 @@ static const struct i2c_device_id tasdevice_id[] = { { "tas5827", TAS5827 }, { "tas5828", TAS5828 }, { "tas5830", TAS5830 }, + { "tas5832", TAS5832 }, {} }; -#ifdef CONFIG_OF static const struct of_device_id tasdevice_of_match[] = { { .compatible = "ti,tas2020", .data = &tasdevice_id[TAS2020] }, { .compatible = "ti,tas2118", .data = &tasdevice_id[TAS2118] }, @@ -143,10 +143,10 @@ static const struct of_device_id tasdevice_of_match[] = { { .compatible = "ti,tas5827", .data = &tasdevice_id[TAS5827] }, { .compatible = "ti,tas5828", .data = &tasdevice_id[TAS5828] }, { .compatible = "ti,tas5830", .data = &tasdevice_id[TAS5830] }, + { .compatible = "ti,tas5832", .data = &tasdevice_id[TAS5832] }, {}, }; MODULE_DEVICE_TABLE(of, tasdevice_of_match); -#endif /** * tas2781_digital_getvol - get the volum control @@ -1746,6 +1746,7 @@ out: case TAS5827: case TAS5828: case TAS5830: + case TAS5832: /* If DSP FW fail, DSP kcontrol won't be created. */ tasdevice_dsp_remove(tas_priv); } @@ -1917,6 +1918,7 @@ static int tasdevice_codec_probe(struct snd_soc_component *codec) case TAS5827: case TAS5828: case TAS5830: + case TAS5832: p = (struct snd_kcontrol_new *)tas5825_snd_controls; size = ARRAY_SIZE(tas5825_snd_controls); break; @@ -2083,7 +2085,6 @@ static void tasdevice_i2c_remove(struct i2c_client *client) tasdevice_remove(tas_priv); } -#ifdef CONFIG_ACPI static const struct acpi_device_id tasdevice_acpi_match[] = { { "TXNW2020", (kernel_ulong_t)&tasdevice_id[TAS2020] }, { "TXNW2118", (kernel_ulong_t)&tasdevice_id[TAS2118] }, @@ -2104,19 +2105,17 @@ static const struct acpi_device_id tasdevice_acpi_match[] = { { "TXNW5827", (kernel_ulong_t)&tasdevice_id[TAS5827] }, { "TXNW5828", (kernel_ulong_t)&tasdevice_id[TAS5828] }, { "TXNW5830", (kernel_ulong_t)&tasdevice_id[TAS5830] }, + { "TXNW5832", (kernel_ulong_t)&tasdevice_id[TAS5832] }, {}, }; MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match); -#endif static struct i2c_driver tasdevice_i2c_driver = { .driver = { .name = "tasdev-codec", - .of_match_table = of_match_ptr(tasdevice_of_match), -#ifdef CONFIG_ACPI - .acpi_match_table = ACPI_PTR(tasdevice_acpi_match), -#endif + .of_match_table = tasdevice_of_match, + .acpi_match_table = tasdevice_acpi_match, }, .probe = tasdevice_i2c_probe, .remove = tasdevice_i2c_remove, diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index 10a2d598caa7..72a53f95d688 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -546,6 +546,9 @@ static int wcd937x_codec_aux_dac_event(struct snd_soc_dapm_widget *w, snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(2), BIT(2)); + snd_soc_component_update_bits(component, + WCD937X_AUX_AUXPA, + BIT(4), BIT(4)); snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(2), BIT(2)); @@ -562,6 +565,9 @@ static int wcd937x_codec_aux_dac_event(struct snd_soc_dapm_widget *w, snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(2), 0x00); + snd_soc_component_update_bits(component, + WCD937X_AUX_AUXPA, + BIT(4), 0x00); break; } @@ -730,10 +736,23 @@ static int wcd937x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, snd_soc_component_update_bits(component, WCD937X_ANA_RX_SUPPLIES, BIT(1), BIT(1)); + /* Enable AUX PA related RX supplies */ + snd_soc_component_update_bits(component, + WCD937X_ANA_RX_SUPPLIES, + BIT(6), BIT(6)); + snd_soc_component_update_bits(component, + WCD937X_ANA_RX_SUPPLIES, + BIT(7), BIT(7)); enable_irq(wcd937x->aux_pdm_wd_int); break; case SND_SOC_DAPM_PRE_PMD: disable_irq_nosync(wcd937x->aux_pdm_wd_int); + snd_soc_component_update_bits(component, + WCD937X_ANA_RX_SUPPLIES, + BIT(6), 0x00); + snd_soc_component_update_bits(component, + WCD937X_ANA_RX_SUPPLIES, + BIT(7), 0x00); break; case SND_SOC_DAPM_POST_PMD: usleep_range(2000, 2010); @@ -2051,7 +2070,12 @@ static const struct snd_kcontrol_new wcd937x_snd_controls[] = { wcd937x_get_swr_port, wcd937x_set_swr_port), SOC_SINGLE_EXT("LO Switch", WCD937X_LO, 0, 1, 0, wcd937x_get_swr_port, wcd937x_set_swr_port), - + SOC_SINGLE_EXT("CLSH PA Switch", WCD937X_CLSH, 0, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DSD_L Switch", WCD937X_DSD_L, 0, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DSD_R Switch", WCD937X_DSD_R, 0, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), SOC_SINGLE_EXT("ADC1 Switch", WCD937X_ADC1, 1, 1, 0, wcd937x_get_swr_port, wcd937x_set_swr_port), SOC_SINGLE_EXT("ADC2 Switch", WCD937X_ADC2, 1, 1, 0, diff --git a/sound/soc/codecs/wm_adsp_fw_find_test.c b/sound/soc/codecs/wm_adsp_fw_find_test.c index d0c7fb30a95d..ae686dc4fa94 100644 --- a/sound/soc/codecs/wm_adsp_fw_find_test.c +++ b/sound/soc/codecs/wm_adsp_fw_find_test.c @@ -45,6 +45,34 @@ struct wm_adsp_fw_find_test_params { /* Dummy struct firmware to return from wm_adsp_request_firmware_files */ static const struct firmware wm_adsp_find_test_dummy_firmware; +static void wm_adsp_fw_find_test_release_firmware_files_stub(struct wm_adsp_fw_files *fw) +{ + /* + * fw->wmfw.firmware and fw->coeff.firmware allocated by this KUnit + * test are dummies not allocated by the real request_firmware() call + * so they must not be passed to release_firmware(). + * This function replaces wm_adsp_release_firmware_files(). + */ + + if (!fw) + return; + + kfree(fw->wmfw.filename); + kfree(fw->coeff.filename); + + fw->wmfw.firmware = NULL; + fw->coeff.firmware = NULL; + fw->wmfw.filename = NULL; + fw->coeff.filename = NULL; +} + +static void wm_adsp_free_found_fw(struct kunit *test) +{ + struct wm_adsp_fw_find_test *priv = test->priv; + + wm_adsp_fw_find_test_release_firmware_files_stub(&priv->found_fw); +} + /* Simple lookup of a filename in a list of names */ static int wm_adsp_fw_find_test_firmware_request_simple_stub(const struct firmware **firmware, const char *filename, @@ -97,9 +125,14 @@ static void wm_adsp_fw_find_test_pick_file(struct kunit *test) kunit_activate_static_stub(test, wm_adsp_firmware_request, wm_adsp_fw_find_test_firmware_request_simple_stub); + kunit_activate_static_stub(test, + wm_adsp_release_firmware_files, + wm_adsp_fw_find_test_release_firmware_files_stub); ret = wm_adsp_request_firmware_files(dsp, &priv->found_fw); kunit_deactivate_static_stub(test, wm_adsp_firmware_request); + kunit_deactivate_static_stub(test, wm_adsp_release_firmware_files); + KUNIT_EXPECT_EQ_MSG(test, ret, (params->expect_wmfw || params->expect_bin) ? 0 : -ENOENT, "%s\n", priv->searched_fw_files); @@ -173,10 +206,13 @@ static void wm_adsp_fw_find_test_search_order(struct kunit *test) kunit_activate_static_stub(test, wm_adsp_firmware_request, wm_adsp_fw_find_test_firmware_request_stub); + kunit_activate_static_stub(test, + wm_adsp_release_firmware_files, + wm_adsp_fw_find_test_release_firmware_files_stub); wm_adsp_request_firmware_files(dsp, &priv->found_fw); - kunit_deactivate_static_stub(test, wm_adsp_firmware_request); + kunit_deactivate_static_stub(test, wm_adsp_release_firmware_files); KUNIT_EXPECT_STREQ(test, priv->searched_fw_files, params->expected_searches); @@ -201,6 +237,7 @@ static void wm_adsp_fw_find_test_find_firmware_byindex(struct kunit *test) dsp->cs_dsp.name = "cs1234"; dsp->part = "dsp1"; + for (dsp->fw = 0;; dsp->fw++) { fw_name = wm_adsp_get_fwf_name_by_index(dsp->fw); if (!fw_name) @@ -209,14 +246,22 @@ static void wm_adsp_fw_find_test_find_firmware_byindex(struct kunit *test) kunit_activate_static_stub(test, wm_adsp_firmware_request, wm_adsp_fw_find_test_firmware_request_stub); + kunit_activate_static_stub(test, + wm_adsp_release_firmware_files, + wm_adsp_fw_find_test_release_firmware_files_stub); wm_adsp_request_firmware_files(dsp, &priv->found_fw); + kunit_deactivate_static_stub(test, wm_adsp_firmware_request); + kunit_deactivate_static_stub(test, wm_adsp_release_firmware_files); KUNIT_EXPECT_NOT_NULL_MSG(test, strstr(priv->searched_fw_files, fw_name), "fw#%d Did not find '%s' in '%s'\n", dsp->fw, fw_name, priv->searched_fw_files); + + wm_adsp_free_found_fw(test); + memset(priv->searched_fw_files, 0, sizeof(priv->searched_fw_files)); } } @@ -255,15 +300,7 @@ static int wm_adsp_fw_find_test_case_init(struct kunit *test) static void wm_adsp_fw_find_test_case_exit(struct kunit *test) { - struct wm_adsp_fw_find_test *priv = test->priv; - - /* - * priv->found_wmfw_firmware and priv->found_bin_firmware are - * dummies not allocated by the real request_firmware() call they - * must not be passed to release_firmware(). - */ - kfree(priv->found_fw.wmfw.filename); - kfree(priv->found_fw.coeff.filename); + wm_adsp_free_found_fw(test); } static void wm_adsp_fw_find_test_param_desc(const struct wm_adsp_fw_find_test_params *param, diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index ee16cf681488..6677d3bf36ec 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -228,10 +228,14 @@ static int fsl_xcvr_capds_put(struct snd_kcontrol *kcontrol, { struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + int changed; - memcpy(xcvr->cap_ds, ucontrol->value.bytes.data, FSL_XCVR_CAPDS_SIZE); + changed = memcmp(xcvr->cap_ds, ucontrol->value.bytes.data, + sizeof(xcvr->cap_ds)) != 0; + memcpy(xcvr->cap_ds, ucontrol->value.bytes.data, + sizeof(xcvr->cap_ds)); - return 0; + return changed; } static struct snd_kcontrol_new fsl_xcvr_earc_capds_kctl = { @@ -1040,10 +1044,15 @@ static int fsl_xcvr_tx_cs_put(struct snd_kcontrol *kcontrol, { struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + int changed; - memcpy(xcvr->tx_iec958.status, ucontrol->value.iec958.status, 24); + changed = memcmp(xcvr->tx_iec958.status, + ucontrol->value.iec958.status, + sizeof(xcvr->tx_iec958.status)) != 0; + memcpy(xcvr->tx_iec958.status, ucontrol->value.iec958.status, + sizeof(xcvr->tx_iec958.status)); - return 0; + return changed; } static struct snd_kcontrol_new fsl_xcvr_rx_ctls[] = { diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c index 4879f79aef29..4aa0cf49b033 100644 --- a/sound/soc/intel/boards/bytcr_wm5102.c +++ b/sound/soc/intel/boards/bytcr_wm5102.c @@ -170,6 +170,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, ret = byt_wm5102_prepare_and_enable_pll1(codec_dai, 48000); if (ret) { dev_err(card->dev, "Error setting codec sysclk: %d\n", ret); + clk_disable_unprepare(priv->mclk); return ret; } } else { diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 0eff5e572573..a13f753eff98 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -579,10 +579,10 @@ EXPORT_SYMBOL_GPL(audioreach_alloc_graph_pkt); int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_ibasic_rsp_result_t *result, struct mutex *cmd_lock, gpr_port_t *port, wait_queue_head_t *cmd_wait, - struct gpr_pkt *pkt, uint32_t rsp_opcode) + const struct gpr_pkt *pkt, uint32_t rsp_opcode) { - struct gpr_hdr *hdr = &pkt->hdr; + const struct gpr_hdr *hdr = &pkt->hdr; int rc; mutex_lock(cmd_lock); @@ -622,7 +622,7 @@ err: } EXPORT_SYMBOL_GPL(audioreach_send_cmd_sync); -int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt, +int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, const struct gpr_pkt *pkt, uint32_t rsp_opcode) { diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 6ddc287f0fb4..6859770b38a6 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -841,8 +841,8 @@ int audioreach_tplg_init(struct snd_soc_component *component); void audioreach_graph_free_buf(struct q6apm_graph *graph); int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_ibasic_rsp_result_t *result, struct mutex *cmd_lock, gpr_port_t *port, wait_queue_head_t *cmd_wait, - struct gpr_pkt *pkt, uint32_t rsp_opcode); -int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt, + const struct gpr_pkt *pkt, uint32_t rsp_opcode); +int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, const struct gpr_pkt *pkt, uint32_t rsp_opcode); int audioreach_set_media_format(struct q6apm_graph *graph, const struct audioreach_module *module, diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 3c119a6132e4..2ab378fb5032 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -29,7 +29,8 @@ struct apm_graph_mgmt_cmd { static struct q6apm *g_apm; -int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode) +int q6apm_send_cmd_sync(struct q6apm *apm, const struct gpr_pkt *pkt, + uint32_t rsp_opcode) { gpr_device_t *gdev = apm->gdev; diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 909fc337fd28..376a36700c53 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -142,7 +142,7 @@ int q6apm_alloc_fragments(struct q6apm_graph *graph, int q6apm_free_fragments(struct q6apm_graph *graph, unsigned int dir); int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id); /* Helpers */ -int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, +int q6apm_send_cmd_sync(struct q6apm *apm, const struct gpr_pkt *pkt, uint32_t rsp_opcode); /* Callback for graph specific */ diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c index a3f4785c4bbe..c81df41ace88 100644 --- a/sound/soc/qcom/x1e80100.c +++ b/sound/soc/qcom/x1e80100.c @@ -27,10 +27,29 @@ static int x1e80100_snd_init(struct snd_soc_pcm_runtime *rtd) { struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_card *card = rtd->card; struct snd_soc_jack *dp_jack = NULL; int dp_pcm_id = 0; switch (cpu_dai->id) { + case WSA_CODEC_DMA_RX_0: + case WSA_CODEC_DMA_RX_1: + /* + * Set limit of -3 dB on Digital Volume and 0 dB on PA Volume + * to reduce the risk of speaker damage until we have active + * speaker protection in place. + */ + snd_soc_limit_volume(card, "WSA WSA_RX0 Digital Volume", 81); + snd_soc_limit_volume(card, "WSA WSA_RX1 Digital Volume", 81); + snd_soc_limit_volume(card, "WSA2 WSA_RX0 Digital Volume", 81); + snd_soc_limit_volume(card, "WSA2 WSA_RX1 Digital Volume", 81); + snd_soc_limit_volume(card, "SpkrLeft PA Volume", 6); + snd_soc_limit_volume(card, "SpkrRight PA Volume", 6); + snd_soc_limit_volume(card, "WooferLeft PA Volume", 6); + snd_soc_limit_volume(card, "TweeterLeft PA Volume", 6); + snd_soc_limit_volume(card, "WooferRight PA Volume", 6); + snd_soc_limit_volume(card, "TweeterRight PA Volume", 6); + break; case DISPLAY_PORT_RX_0: dp_pcm_id = 0; dp_jack = &data->dp_jack[dp_pcm_id]; diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index fd6a254c9530..196bade11ab5 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -1006,8 +1006,11 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti control->has_fixed = true; fallthrough; case SDCA_ACCESS_MODE_RO: - control->deferrable = fwnode_property_read_bool(control_node, - "mipi-sdca-control-deferrable"); + ret = fwnode_property_read_u32(control_node, + "mipi-sdca-control-deferrable", + &tmp); + if (ret == 0) + control->deferrable = !!tmp; break; default: break; diff --git a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c index 2a7109d53cbe..e0e32a279787 100644 --- a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c +++ b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c @@ -40,12 +40,6 @@ static int asoc_sdw_bridge_cs35l56_asp_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:cs35l56-bridge", - card->components); - if (!card->components) - return -ENOMEM; - ret = snd_soc_dapm_new_controls(dapm, bridge_widgets, ARRAY_SIZE(bridge_widgets)); if (ret) { diff --git a/sound/soc/sdw_utils/soc_sdw_cs42l43.c b/sound/soc/sdw_utils/soc_sdw_cs42l43.c index 4a451b9d4f13..e99ea3c4e5dd 100644 --- a/sound/soc/sdw_utils/soc_sdw_cs42l43.c +++ b/sound/soc/sdw_utils/soc_sdw_cs42l43.c @@ -107,21 +107,11 @@ EXPORT_SYMBOL_NS(asoc_sdw_cs42l43_hs_rtd_init, "SND_SOC_SDW_UTILS"); int asoc_sdw_cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { - struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; + struct snd_soc_component *component = dai->component; struct snd_soc_card *card = rtd->card; struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); - struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); int ret; - if (!(ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS)) { - /* Will be set by the bridge code in this case */ - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:cs42l43-spk", - card->components); - if (!card->components) - return -ENOMEM; - } - ret = snd_soc_limit_volume(card, "cs42l43 Speaker Digital Volume", CS42L43_SPK_VOLUME_0DB); if (ret) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 2807f536eef0..849ae876d7a4 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -758,6 +758,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .codec_name = "cs42l43-codec", + .component_name = "cs42l43-spk", .dai_name = "cs42l43-dp6", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, @@ -1104,6 +1105,7 @@ static int asoc_sdw_find_codec_info_dai_index(const struct asoc_sdw_codec_info * int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; + struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); struct asoc_sdw_codec_info *codec_info; struct snd_soc_dai *dai; @@ -1179,16 +1181,26 @@ skip_add_controls_widgets: /* Generate the spk component string for card->components string */ if (codec_info->dais[dai_index].dai_type == SOC_SDW_DAI_TYPE_AMP && codec_info->dais[dai_index].component_name) { + const char *component; + + /* + * For the special case of cs42l43 with sidecar amps, use only + * "cs35l56-bridge" as the component name in card->components + */ + if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS && + !strcmp(codec_info->dais[dai_index].component_name, "cs42l43-spk")) + component = "cs35l56-bridge"; + else + component = codec_info->dais[dai_index].component_name; + if (strlen (spk_components) == 0) spk_components = - devm_kasprintf(card->dev, GFP_KERNEL, "%s", - codec_info->dais[dai_index].component_name); + devm_kasprintf(card->dev, GFP_KERNEL, "%s", component); else /* Append component name to spk_components */ spk_components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s+%s", spk_components, - codec_info->dais[dai_index].component_name); + "%s+%s", spk_components, component); } codec_info->dais[dai_index].rtd_init_done = true; @@ -1596,6 +1608,7 @@ int asoc_sdw_get_dai_type(u32 type) switch (type) { case SDCA_FUNCTION_TYPE_SMART_AMP: case SDCA_FUNCTION_TYPE_SIMPLE_AMP: + case SDCA_FUNCTION_TYPE_COMPANION_AMP: return SOC_SDW_DAI_TYPE_AMP; case SDCA_FUNCTION_TYPE_SMART_MIC: case SDCA_FUNCTION_TYPE_SIMPLE_MIC: diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index b3d61d973ce4..8662b422eb80 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1412,7 +1412,8 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev link_mask |= BIT(peripherals->array[i]->bus->link_id); link_num = hweight32(link_mask); - links = devm_kcalloc(sdev->dev, link_num, sizeof(*links), GFP_KERNEL); + /* An empty adr_link is needed to terminate the adr_link loop */ + links = devm_kcalloc(sdev->dev, link_num + 1, sizeof(*links), GFP_KERNEL); if (!links) return NULL; diff --git a/sound/soc/sof/intel/nvl.c b/sound/soc/sof/intel/nvl.c index 0d763998558f..86d2e1aa0eec 100644 --- a/sound/soc/sof/intel/nvl.c +++ b/sound/soc/sof/intel/nvl.c @@ -48,6 +48,7 @@ const struct sof_intel_dsp_desc nvl_chip_info = { .power_down_dsp = mtl_power_down_dsp, .disable_interrupts = lnl_dsp_disable_interrupts, .hw_ip_version = SOF_INTEL_ACE_4_0, + .platform = "nvl", }; const struct sof_intel_dsp_desc nvl_s_chip_info = { @@ -72,6 +73,7 @@ const struct sof_intel_dsp_desc nvl_s_chip_info = { .power_down_dsp = mtl_power_down_dsp, .disable_interrupts = lnl_dsp_disable_interrupts, .hw_ip_version = SOF_INTEL_ACE_4_0, + .platform = "nvl", }; MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL"); diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c index 1cb99f1abc7c..5420ca2aefbd 100644 --- a/sound/soc/spacemit/k1_i2s.c +++ b/sound/soc/spacemit/k1_i2s.c @@ -93,8 +93,8 @@ static void spacemit_i2s_init(struct spacemit_i2s_dev *i2s) u32 sscr_val, sspsp_val, ssfcr_val, ssrwt_val; sscr_val = SSCR_TRAIL | SSCR_FRF_PSP; - ssfcr_val = FIELD_PREP(SSFCR_FIELD_TFT, 5) | - FIELD_PREP(SSFCR_FIELD_RFT, 5) | + ssfcr_val = FIELD_PREP(SSFCR_FIELD_TFT, 0xF) | + FIELD_PREP(SSFCR_FIELD_RFT, 0xF) | SSFCR_RSRE | SSFCR_TSRE; ssrwt_val = SSRWT_RWOT; sspsp_val = SSPSP_SFRMP; @@ -106,6 +106,37 @@ static void spacemit_i2s_init(struct spacemit_i2s_dev *i2s) writel(0, i2s->base + SSINTEN); } +static int spacemit_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct spacemit_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai); + + switch (i2s->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + 2, 2); + snd_pcm_hw_constraint_mask64(substream->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FMTBIT_S16_LE); + break; + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + 1, 1); + snd_pcm_hw_constraint_mask64(substream->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FMTBIT_S32_LE); + break; + default: + dev_dbg(i2s->dev, "unexpected format type"); + return -EINVAL; + } + + return 0; +} + static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -117,10 +148,6 @@ static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream, u32 val; int ret; - val = readl(i2s->base + SSCR); - if (val & SSCR_SSE) - return 0; - dma_data = &i2s->playback_dma_data; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) @@ -157,22 +184,9 @@ static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream, dma_data->maxburst = 32; dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; } - - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, - 1, 2); - snd_pcm_hw_constraint_mask64(substream->runtime, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_FMTBIT_S16_LE); break; case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B: - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, - 1, 1); - snd_pcm_hw_constraint_mask64(substream->runtime, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_FMTBIT_S32_LE); break; default: dev_dbg(i2s->dev, "unexpected format type"); @@ -181,6 +195,9 @@ static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream, } val = readl(i2s->base + SSCR); + if (val & SSCR_SSE) + return 0; + val &= ~SSCR_DW_32BYTE; val |= data_width; writel(val, i2s->base + SSCR); @@ -303,6 +320,7 @@ static int spacemit_i2s_dai_remove(struct snd_soc_dai *dai) static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { .probe = spacemit_i2s_dai_probe, .remove = spacemit_i2s_dai_remove, + .startup = spacemit_i2s_startup, .hw_params = spacemit_i2s_hw_params, .set_sysclk = spacemit_i2s_set_sysclk, .set_fmt = spacemit_i2s_set_fmt, diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 3f396c87802e..1c18ef6971c0 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 # Tegra platform Support snd-soc-tegra-pcm-y := tegra_pcm.o -snd-soc-tegra-utils-y += tegra_asoc_utils.o snd-soc-tegra20-ac97-y := tegra20_ac97.o snd-soc-tegra20-das-y := tegra20_das.o snd-soc-tegra20-i2s-y := tegra20_i2s.o diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 75f82a92ff44..2f5f62079fa4 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -592,6 +592,7 @@ static __u32 reverse_bytes(__u32 b, int len) fallthrough; case 2: b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1); + fallthrough; case 1: case 0: break; diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index 874f6cd503ca..2c5648966412 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -31,7 +31,6 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable card */ static struct sfire_chip *chips[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; -static struct usb_device *devices[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the 6fire sound device"); @@ -44,32 +43,28 @@ static DEFINE_MUTEX(register_mutex); static void usb6fire_chip_abort(struct sfire_chip *chip) { - if (chip) { - if (chip->pcm) - usb6fire_pcm_abort(chip); - if (chip->midi) - usb6fire_midi_abort(chip); - if (chip->comm) - usb6fire_comm_abort(chip); - if (chip->control) - usb6fire_control_abort(chip); - } + if (chip->pcm) + usb6fire_pcm_abort(chip); + if (chip->midi) + usb6fire_midi_abort(chip); + if (chip->comm) + usb6fire_comm_abort(chip); + if (chip->control) + usb6fire_control_abort(chip); } static void usb6fire_card_free(struct snd_card *card) { struct sfire_chip *chip = card->private_data; - if (chip) { - if (chip->pcm) - usb6fire_pcm_destroy(chip); - if (chip->midi) - usb6fire_midi_destroy(chip); - if (chip->comm) - usb6fire_comm_destroy(chip); - if (chip->control) - usb6fire_control_destroy(chip); - } + if (chip->pcm) + usb6fire_pcm_destroy(chip); + if (chip->midi) + usb6fire_midi_destroy(chip); + if (chip->comm) + usb6fire_comm_destroy(chip); + if (chip->control) + usb6fire_control_destroy(chip); } static int usb6fire_chip_probe(struct usb_interface *intf, @@ -83,21 +78,18 @@ static int usb6fire_chip_probe(struct usb_interface *intf, struct snd_card *card = NULL; /* look if we already serve this card and return if so */ - scoped_guard(mutex, ®ister_mutex) { - for (i = 0; i < SNDRV_CARDS; i++) { - if (devices[i] == device) { - if (chips[i]) - chips[i]->intf_count++; - usb_set_intfdata(intf, chips[i]); - return 0; - } else if (!devices[i] && regidx < 0) - regidx = i; - } - if (regidx < 0) { - dev_err(&intf->dev, "too many cards registered.\n"); - return -ENODEV; - } - devices[regidx] = device; + guard(mutex)(®ister_mutex); + for (i = 0; i < SNDRV_CARDS; i++) { + if (chips[i] && chips[i]->dev == device) { + chips[i]->intf_count++; + usb_set_intfdata(intf, chips[i]); + return 0; + } else if (!chips[i] && regidx < 0) + regidx = i; + } + if (regidx < 0) { + dev_err(&intf->dev, "too many cards registered.\n"); + return -ENODEV; } /* check, if firmware is present on device, upload it if not */ @@ -124,7 +116,6 @@ static int usb6fire_chip_probe(struct usb_interface *intf, device->bus->busnum, device->devnum); chip = card->private_data; - chips[regidx] = chip; chip->dev = device; chip->regidx = regidx; chip->intf_count = 1; @@ -152,7 +143,10 @@ static int usb6fire_chip_probe(struct usb_interface *intf, dev_err(&intf->dev, "cannot register card."); goto destroy_chip; } + usb_set_intfdata(intf, chip); + chips[regidx] = chip; + return 0; destroy_chip: @@ -165,30 +159,31 @@ static void usb6fire_chip_disconnect(struct usb_interface *intf) struct sfire_chip *chip; struct snd_card *card; + guard(mutex)(®ister_mutex); chip = usb_get_intfdata(intf); - if (chip) { /* if !chip, fw upload has been performed */ - chip->intf_count--; - if (!chip->intf_count) { - scoped_guard(mutex, ®ister_mutex) { - devices[chip->regidx] = NULL; - chips[chip->regidx] = NULL; - } + /* if !chip, fw upload has been performed */ + if (!chip) + return; - /* - * Save card pointer before teardown. - * snd_card_free_when_closed() may free card (and - * the embedded chip) immediately, so it must be - * called last and chip must not be accessed after. - */ - card = chip->card; - chip->shutdown = true; - if (card) - snd_card_disconnect(card); - usb6fire_chip_abort(chip); - if (card) - snd_card_free_when_closed(card); - } - } + chip->intf_count--; + if (chip->intf_count) + return; + + chips[chip->regidx] = NULL; + + /* + * Save card pointer before teardown. + * snd_card_free_when_closed() may free card (and + * the embedded chip) immediately, so it must be + * called last and chip must not be accessed after. + */ + card = chip->card; + chip->shutdown = true; + if (card) + snd_card_disconnect(card); + usb6fire_chip_abort(chip); + if (card) + snd_card_free_when_closed(card); } static const struct usb_device_id device_table[] = { diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c index dd25a6407b63..c77a21a9acd7 100644 --- a/sound/usb/6fire/control.c +++ b/sound/usb/6fire/control.c @@ -290,15 +290,17 @@ static int usb6fire_control_input_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + int vol0 = ucontrol->value.integer.value[0] - 15; + int vol1 = ucontrol->value.integer.value[1] - 15; int changed = 0; - if (rt->input_vol[0] != ucontrol->value.integer.value[0]) { - rt->input_vol[0] = ucontrol->value.integer.value[0] - 15; + if (rt->input_vol[0] != vol0) { + rt->input_vol[0] = vol0; rt->ivol_updated &= ~(1 << 0); changed = 1; } - if (rt->input_vol[1] != ucontrol->value.integer.value[1]) { - rt->input_vol[1] = ucontrol->value.integer.value[1] - 15; + if (rt->input_vol[1] != vol1) { + rt->input_vol[1] = vol1; rt->ivol_updated &= ~(1 << 1); changed = 1; } diff --git a/sound/usb/caiaq/control.c b/sound/usb/caiaq/control.c index af459c49baf4..4598fb7e8be0 100644 --- a/sound/usb/caiaq/control.c +++ b/sound/usb/caiaq/control.c @@ -87,6 +87,7 @@ static int control_put(struct snd_kcontrol *kcontrol, struct snd_usb_caiaqdev *cdev = caiaqdev(chip->card); int pos = kcontrol->private_value; int v = ucontrol->value.integer.value[0]; + int ret; unsigned char cmd; switch (cdev->chip.usb_id) { @@ -103,6 +104,10 @@ static int control_put(struct snd_kcontrol *kcontrol, if (pos & CNT_INTVAL) { int i = pos & ~CNT_INTVAL; + unsigned char old = cdev->control_state[i]; + + if (old == v) + return 0; cdev->control_state[i] = v; @@ -113,10 +118,11 @@ static int control_put(struct snd_kcontrol *kcontrol, cdev->ep8_out_buf[0] = i; cdev->ep8_out_buf[1] = v; - usb_bulk_msg(cdev->chip.dev, - usb_sndbulkpipe(cdev->chip.dev, 8), - cdev->ep8_out_buf, sizeof(cdev->ep8_out_buf), - &actual_len, 200); + ret = usb_bulk_msg(cdev->chip.dev, + usb_sndbulkpipe(cdev->chip.dev, 8), + cdev->ep8_out_buf, + sizeof(cdev->ep8_out_buf), + &actual_len, 200); } else if (cdev->chip.usb_id == USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER)) { @@ -128,21 +134,36 @@ static int control_put(struct snd_kcontrol *kcontrol, offset = MASCHINE_BANK_SIZE; } - snd_usb_caiaq_send_command_bank(cdev, cmd, bank, - cdev->control_state + offset, - MASCHINE_BANK_SIZE); + ret = snd_usb_caiaq_send_command_bank(cdev, cmd, bank, + cdev->control_state + offset, + MASCHINE_BANK_SIZE); } else { - snd_usb_caiaq_send_command(cdev, cmd, - cdev->control_state, sizeof(cdev->control_state)); + ret = snd_usb_caiaq_send_command(cdev, cmd, + cdev->control_state, + sizeof(cdev->control_state)); + } + + if (ret < 0) { + cdev->control_state[i] = old; + return ret; } } else { - if (v) - cdev->control_state[pos / 8] |= 1 << (pos % 8); - else - cdev->control_state[pos / 8] &= ~(1 << (pos % 8)); + int idx = pos / 8; + unsigned char mask = 1 << (pos % 8); + unsigned char old = cdev->control_state[idx]; + unsigned char val = v ? (old | mask) : (old & ~mask); - snd_usb_caiaq_send_command(cdev, cmd, - cdev->control_state, sizeof(cdev->control_state)); + if (old == val) + return 0; + + cdev->control_state[idx] = val; + ret = snd_usb_caiaq_send_command(cdev, cmd, + cdev->control_state, + sizeof(cdev->control_state)); + if (ret < 0) { + cdev->control_state[idx] = old; + return ret; + } } return 1; @@ -640,4 +661,3 @@ int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *cdev) return ret; } - diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 51177ebfb8c6..b20aae0caf60 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -290,7 +290,7 @@ int snd_usb_caiaq_set_auto_msg(struct snd_usb_caiaqdev *cdev, tmp, sizeof(tmp)); } -static void setup_card(struct snd_usb_caiaqdev *cdev) +static int setup_card(struct snd_usb_caiaqdev *cdev) { int ret; char val[4]; @@ -325,8 +325,10 @@ static void setup_card(struct snd_usb_caiaqdev *cdev) snd_usb_caiaq_send_command(cdev, EP1_CMD_READ_IO, NULL, 0); if (!wait_event_timeout(cdev->ep1_wait_queue, - cdev->control_state[0] != 0xff, HZ)) - return; + cdev->control_state[0] != 0xff, HZ)) { + dev_err(dev, "Read timeout for control state\n"); + return -EINVAL; + } /* fix up some defaults */ if ((cdev->control_state[1] != 2) || @@ -347,33 +349,43 @@ static void setup_card(struct snd_usb_caiaqdev *cdev) cdev->spec.num_digital_audio_out + cdev->spec.num_digital_audio_in > 0) { ret = snd_usb_caiaq_audio_init(cdev); - if (ret < 0) + if (ret < 0) { dev_err(dev, "Unable to set up audio system (ret=%d)\n", ret); + return ret; + } } if (cdev->spec.num_midi_in + cdev->spec.num_midi_out > 0) { ret = snd_usb_caiaq_midi_init(cdev); - if (ret < 0) + if (ret < 0) { dev_err(dev, "Unable to set up MIDI system (ret=%d)\n", ret); + return ret; + } } #ifdef CONFIG_SND_USB_CAIAQ_INPUT ret = snd_usb_caiaq_input_init(cdev); - if (ret < 0) + if (ret < 0 && ret != -ENODEV) { dev_err(dev, "Unable to set up input system (ret=%d)\n", ret); + return ret; + } #endif /* finally, register the card and all its sub-instances */ ret = snd_card_register(cdev->chip.card); if (ret < 0) { dev_err(dev, "snd_card_register() returned %d\n", ret); - snd_card_free(cdev->chip.card); + return ret; } ret = snd_usb_caiaq_control_init(cdev); - if (ret < 0) + if (ret < 0) { dev_err(dev, "Unable to set up control system (ret=%d)\n", ret); + return ret; + } + + return 0; } static void card_free(struct snd_card *card) @@ -411,6 +423,7 @@ static int create_card(struct usb_device *usb_dev, cdev = caiaqdev(card); cdev->chip.dev = usb_get_dev(usb_dev); + card->private_free = card_free; cdev->chip.card = card; cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct)); @@ -499,8 +512,10 @@ static int init_card(struct snd_usb_caiaqdev *cdev) scnprintf(card->longname, sizeof(card->longname), "%s %s (%s)", cdev->vendor_name, cdev->product_name, usbpath); - setup_card(cdev); - card->private_free = card_free; + err = setup_card(cdev); + if (err < 0) + goto err_kill_urb; + return 0; err_kill_urb: diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index a9130891bb69..5c70fdf61cc1 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -804,7 +804,7 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *cdev) default: /* no input methods supported on this device */ - ret = -EINVAL; + ret = -ENODEV; goto exit_free_idev; } diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 842ba5b801ea..2e0c18e35281 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -208,11 +208,18 @@ static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip, } /* - * MOTU MicroBook IIc - * Sample rate changes takes more than 2 seconds for this device. Clock - * validity request returns false during that period. + * Quirk for older MOTU AVB / hybrid interfaces + * + * These devices take more than 2 seconds to switch sample rate or + * clock source. During this period the clock validity request + * returns false, causing ALSA to fail prematurely. + * + * Affected models (all use vendor 0x07fd): + * - MicroBook IIc → 0x0004 + * - 1248, 624, 8A, UltraLite AVB, 8M, 16A, ... → 0x0005 */ - if (chip->usb_id == USB_ID(0x07fd, 0x0004)) { + if (chip->usb_id == USB_ID(0x07fd, 0x0004) || /* MicroBook IIc */ + chip->usb_id == USB_ID(0x07fd, 0x0005)) { /* 1248 / 624 / 8A / UltraLite AVB / ... */ count = 0; while ((!ret) && (count < 50)) { diff --git a/sound/usb/format.c b/sound/usb/format.c index 030b4307927a..4830f9f93ad7 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -470,7 +470,7 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, nr_rates++; if (nr_rates >= MAX_NR_RATES) { usb_audio_err(chip, "invalid uac2 rates\n"); - break; + return nr_rates; } skip_rate: diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c index ea1324c22f46..841b64479252 100644 --- a/sound/usb/line6/podhd.c +++ b/sound/usb/line6/podhd.c @@ -28,6 +28,7 @@ enum { LINE6_PODHD500X, LINE6_PODHDDESKTOP, LINE6_PODHDPROX, + LINE6_PODHDPRO, }; struct usb_line6_podhd { @@ -442,6 +443,7 @@ static const struct usb_device_id podhd_id_table[] = { { LINE6_IF_NUM(0x4159, 0), .driver_info = LINE6_PODHD500X }, { LINE6_IF_NUM(0x4156, 0), .driver_info = LINE6_PODHDDESKTOP }, { LINE6_IF_NUM(0x415A, 0), .driver_info = LINE6_PODHDPROX }, + { LINE6_IF_NUM(0x4157, 0), .driver_info = LINE6_PODHDPRO }, {} }; @@ -542,6 +544,18 @@ static const struct line6_properties podhd_properties_table[] = { .ep_audio_r = 0x86, .ep_audio_w = 0x02, }, + [LINE6_PODHDPRO] = { + .id = "PODHDPRO", + .name = "POD HD PRO", + .capabilities = LINE6_CAP_PCM | LINE6_CAP_CONTROL + | LINE6_CAP_HWMON | LINE6_CAP_HWMON_CTL | LINE6_CAP_IN_NEEDS_OUT, + .altsetting = 1, + .ctrl_if = 1, + .ep_ctrl_r = 0x81, + .ep_ctrl_w = 0x01, + .ep_audio_r = 0x86, + .ep_audio_w = 0x02, + }, }; /* diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 0a5b8941ebda..d87e3f357cf7 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -1951,15 +1951,17 @@ static struct usb_ms_endpoint_descriptor *find_usb_ms_endpoint_descriptor( while (extralen > 3) { struct usb_ms_endpoint_descriptor *ms_ep = (struct usb_ms_endpoint_descriptor *)extra; + int length = ms_ep->bLength; - if (ms_ep->bLength > 3 && + if (!length || length > extralen) + break; + + if (length > 3 && ms_ep->bDescriptorType == USB_DT_CS_ENDPOINT && ms_ep->bDescriptorSubtype == UAC_MS_GENERAL) return ms_ep; - if (!extra[0]) - break; - extralen -= extra[0]; - extra += extra[0]; + extralen -= length; + extra += length; } return NULL; } diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 3546ba926cb3..04aeb9052f13 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -227,7 +227,7 @@ static void kill_midi_urbs(struct snd_usb_midi2_endpoint *ep, bool suspending) if (!ep) return; if (suspending) - ep->suspended = ep->running; + atomic_set(&ep->suspended, atomic_read(&ep->running)); atomic_set(&ep->running, 0); for (i = 0; i < ep->num_urbs; i++) { if (!ep->urbs[i].urb) @@ -496,15 +496,17 @@ static void *find_usb_ms_endpoint_descriptor(struct usb_host_endpoint *hostep, while (extralen > 3) { struct usb_ms_endpoint_descriptor *ms_ep = (struct usb_ms_endpoint_descriptor *)extra; + int length = ms_ep->bLength; - if (ms_ep->bLength > 3 && + if (!length || length > extralen) + break; + + if (length > 3 && ms_ep->bDescriptorType == USB_DT_CS_ENDPOINT && ms_ep->bDescriptorSubtype == subtype) return ms_ep; - if (!extra[0]) - break; - extralen -= extra[0]; - extra += extra[0]; + extralen -= length; + extra += length; } return NULL; } @@ -1188,10 +1190,11 @@ void snd_usb_midi_v2_suspend_all(struct snd_usb_audio *chip) static void resume_midi2_endpoint(struct snd_usb_midi2_endpoint *ep) { - ep->running = ep->suspended; - if (ep->direction == STR_IN) + atomic_set(&ep->running, atomic_read(&ep->suspended)); + atomic_set(&ep->suspended, 0); + + if (ep->direction == STR_IN || atomic_read(&ep->running)) submit_io_urbs(ep); - /* FIXME: does it all? */ } void snd_usb_midi_v2_resume_all(struct snd_usb_audio *chip) diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 49b3dd8d827d..d129b42eb979 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -974,6 +974,13 @@ static int detect_usb_format(struct ua101 *ua) ua->capture.channels = fmt_capture->bNrChannels; ua->playback.channels = fmt_playback->bNrChannels; + if (!ua->capture.channels || !ua->playback.channels) { + dev_err(&ua->dev->dev, + "invalid channel count: capture %u, playback %u\n", + ua->capture.channels, ua->playback.channels); + return -EINVAL; + } + ua->capture.frame_bytes = fmt_capture->bSubframeSize * ua->capture.channels; ua->playback.frame_bytes = diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index aa6ea3be100a..5fba456eb4a9 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1190,6 +1190,16 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, cval->res = 1; } break; + + case USB_ID(0x0e6f, 0x024a): /* PDP Riffmaster for PS4 */ + case USB_ID(0x0e6f, 0x0249): /* PDP Riffmaster for PS5 */ + if (!strcmp(kctl->id.name, "PCM Playback Volume")) { + usb_audio_info(chip, + "set volume quirk for PDP Riffmaster for PS4/PS5\n"); + cval->min = -2560; /* Mute under it */ + } + break; + case USB_ID(0x3302, 0x12db): /* MOONDROP Quark2 */ if (!strcmp(kctl->id.name, "PCM Playback Volume")) { usb_audio_info(chip, @@ -2664,6 +2674,16 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, /* get min/max values */ switch (type) { + case USB_XU_CLOCK_RATE: + /* + * E-Mu USB 0404/0202/TrackerPre/0204 + * samplerate control quirk + */ + cval->min = 0; + cval->max = 5; + cval->res = 1; + cval->initialized = 1; + break; case UAC_PROCESS_UP_DOWNMIX: { bool mode_sel = false; @@ -2687,31 +2707,17 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, cval->max = control_spec[0]; cval->res = 1; cval->initialized = 1; - err = 0; break; } - err = get_min_max(cval, valinfo->min_value); - break; + fallthrough; } - case USB_XU_CLOCK_RATE: - /* - * E-Mu USB 0404/0202/TrackerPre/0204 - * samplerate control quirk - */ - cval->min = 0; - cval->max = 5; - cval->res = 1; - cval->initialized = 1; - err = 0; - break; default: err = get_min_max(cval, valinfo->min_value); - break; - } - if (err < 0 && err != -EAGAIN) { - usb_mixer_elem_info_free(cval); - return err; + if (err < 0 && err != -EAGAIN) { + usb_mixer_elem_info_free(cval); + return err; + } } err = get_cur_ctl_value(cval, cval->control << 8, &val); diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index a01510a855c2..1bdaa46d4fe1 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -1538,15 +1538,17 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, { struct usb_mixer_interface *mixer; struct usb_mixer_elem_info *cval; + int err; int unitid = 12; /* SampleRate ExtensionUnit ID */ list_for_each_entry(mixer, &chip->mixer_list, list) { if (mixer->id_elems[unitid]) { cval = mixer_elem_list_to_info(mixer->id_elems[unitid]); - snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, - cval->control << 8, - samplerate_id); - snd_usb_mixer_notify_id(mixer, unitid); + err = snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, + cval->control << 8, + samplerate_id); + if (!err) + snd_usb_mixer_notify_id(mixer, unitid); break; } } @@ -2025,7 +2027,7 @@ static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, int err; reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a; - if (reg != list->kctl->private_value) + if (reg == list->kctl->private_value) return 0; kcontrol->private_value = reg; diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 8eaa96222759..0f83f8981213 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -6707,6 +6707,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) err = scarlett2_add_new_ctl( mixer, &scarlett2_autogain_status_ctl, i, 1, s, &private->autogain_status_ctls[i]); + if (err < 0) + return err; } /* Add autogain target controls */ diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index 5f993b88448c..a0009503b2c5 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -565,6 +565,7 @@ static unsigned long uaudio_iommu_map_pa(enum mem_type mtype, bool dma_coherent, unsigned long iova = 0; bool map = true; int prot = uaudio_iommu_map_prot(dma_coherent); + int ret; switch (mtype) { case MEM_EVENT_RING: @@ -582,10 +583,24 @@ static unsigned long uaudio_iommu_map_pa(enum mem_type mtype, bool dma_coherent, dev_err(uaudio_qdev->data->dev, "unknown mem type %d\n", mtype); } - if (!iova || !map) + if (!iova) return 0; - iommu_map(uaudio_qdev->data->domain, iova, pa, size, prot, GFP_KERNEL); + if (!map) + return iova; + + ret = iommu_map(uaudio_qdev->data->domain, iova, pa, size, prot, + GFP_KERNEL); + if (ret) { + dev_err(uaudio_qdev->data->dev, + "failed to map %zu bytes at iova 0x%08lx: %d\n", + size, iova, ret); + if (mtype == MEM_XFER_RING) + uaudio_put_iova(iova, size, + &uaudio_qdev->xfer_ring_list, + &uaudio_qdev->xfer_ring_iova_size); + return 0; + } return iova; } @@ -1054,15 +1069,17 @@ static int uaudio_transfer_buffer_setup(struct snd_usb_substream *subs, if (!xfer_buf) return -ENOMEM; - dma_get_sgtable(subs->dev->bus->sysdev, &xfer_buf_sgt, xfer_buf, - xfer_buf_dma, len); + ret = dma_get_sgtable(subs->dev->bus->sysdev, &xfer_buf_sgt, xfer_buf, + xfer_buf_dma, len); + if (ret) + goto free_xfer_buf; /* map the physical buffer into sysdev as well */ xfer_buf_dma_sysdev = uaudio_iommu_map_xfer_buf(dma_coherent, len, &xfer_buf_sgt); if (!xfer_buf_dma_sysdev) { ret = -ENOMEM; - goto unmap_sync; + goto free_sgt; } mem_info->dma = xfer_buf_dma; @@ -1073,7 +1090,9 @@ static int uaudio_transfer_buffer_setup(struct snd_usb_substream *subs, return 0; -unmap_sync: +free_sgt: + sg_free_table(&xfer_buf_sgt); +free_xfer_buf: usb_free_coherent(subs->dev, len, xfer_buf, xfer_buf_dma); return ret; diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 803e03d4d77b..4e9cfff4047f 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2652,6 +2652,9 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, + +#endif /* disabled */ + { /* * The AudioBox USB advertises S24_3LE as the only supported format @@ -2700,7 +2703,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, -#endif /* disabled */ { /* diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 8fc36d1cfb9d..31cbe383ae65 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -125,7 +125,7 @@ static int add_audio_stream_from_fixed_fmt(struct snd_usb_audio *chip, snd_usb_audioformat_set_sync_ep(chip, fp); - err = snd_usb_add_audio_stream(chip, stream, fp); + err = snd_usb_add_audio_stream(chip, stream, fp, NULL); if (err < 0) return err; @@ -2277,6 +2277,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x05e1, 0x0480, /* Hauppauge Woodbury */ QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER), + DEVICE_FLG(0x05fc, 0x0231, /* JBL Pebbles */ + QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL | QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL | + QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x0624, 0x3d3f, /* AB13X USB Audio */ QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x0644, 0x8043, /* TEAC UD-501/UD-501V2/UD-503/NT-503 */ @@ -2366,6 +2369,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x152a, 0x880a, /* NeuralDSP Quad Cortex */ 0), /* Doesn't have the vendor quirk which would otherwise apply */ + DEVICE_FLG(0x1532, 0x055e, /* Razer Nommo V2 X */ + QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x154e, 0x1002, /* Denon DCD-1500RE */ QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY), DEVICE_FLG(0x154e, 0x1003, /* Denon DA-300USB */ @@ -2458,6 +2463,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x2b53, 0x0031, /* Fiero SC-01 (firmware v1.1.0) */ QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x2b73, 0x0047, /* AlphaTheta EUPHONIA */ + QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x2d95, 0x8011, /* VIVO USB-C HEADSET */ QUIRK_FLAG_CTL_MSG_DELAY_1M), DEVICE_FLG(0x2d95, 0x8021, /* VIVO USB-C-XE710 HEADSET */ @@ -2472,8 +2479,12 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x3255, 0x0000, /* Luxman D-10X */ QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY), + DEVICE_FLG(0x3302, 0x17c2, /* TTGK Technology USB-C Audio */ + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x339b, 0x3a07, /* Synaptics HONOR USB-C HEADSET */ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), + DEVICE_FLG(0x3443, 0x930d, /* NexiGo N930W 60fps Webcam */ + QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x413c, 0xa506, /* Dell AE515 sound bar */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x534d, 0x0021, /* MacroSilicon MS2100/MS2106 */ diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 2532bf97e05e..b2c5c8198281 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -79,7 +79,7 @@ static void snd_usb_audio_pcm_free(struct snd_pcm *pcm) static void snd_usb_init_substream(struct snd_usb_stream *as, int stream, struct audioformat *fp, - struct snd_usb_power_domain *pd) + struct snd_usb_power_domain **pdptr) { struct snd_usb_substream *subs = &as->substream[stream]; @@ -105,10 +105,11 @@ static void snd_usb_init_substream(struct snd_usb_stream *as, if (fp->channels > subs->channels_max) subs->channels_max = fp->channels; - if (pd) { - subs->str_pd = pd; + if (pdptr && *pdptr) { + subs->str_pd = *pdptr; + *pdptr = NULL; /* assigned */ /* Initialize Power Domain to idle status D1 */ - snd_usb_power_domain_set(subs->stream->chip, pd, + snd_usb_power_domain_set(subs->stream->chip, subs->str_pd, UAC3_PD_STATE_D1); } @@ -352,6 +353,8 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor if (len < sizeof(*cs_desc)) break; cs_len = le16_to_cpu(cs_desc->wLength); + if (cs_len < sizeof(*cs_desc)) + break; if (len < cs_len) break; cs_type = cs_desc->bSegmentType; @@ -492,11 +495,14 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor * if not, create a new pcm stream. note, fp is added to the substream * fmt_list and will be freed on the chip instance release. do not free * fp or do remove it from the substream fmt_list to avoid double-free. + * + * pdptr is optional and can be NULL. When it's non-NULL and the PD gets + * assigned to the stream, *pdptr is cleared to NULL upon return. */ -static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, - int stream, - struct audioformat *fp, - struct snd_usb_power_domain *pd) +int snd_usb_add_audio_stream(struct snd_usb_audio *chip, + int stream, + struct audioformat *fp, + struct snd_usb_power_domain **pdptr) { struct snd_usb_stream *as; @@ -529,7 +535,7 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, err = snd_pcm_new_stream(as->pcm, stream, 1); if (err < 0) return err; - snd_usb_init_substream(as, stream, fp, pd); + snd_usb_init_substream(as, stream, fp, pdptr); return add_chmap(as->pcm, stream, subs); } @@ -558,7 +564,7 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, else strscpy(pcm->name, "USB Audio"); - snd_usb_init_substream(as, stream, fp, pd); + snd_usb_init_substream(as, stream, fp, pdptr); /* * Keep using head insertion for M-Audio Audiophile USB (tm) which has a @@ -576,21 +582,6 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, return add_chmap(pcm, stream, &as->substream[stream]); } -int snd_usb_add_audio_stream(struct snd_usb_audio *chip, - int stream, - struct audioformat *fp) -{ - return __snd_usb_add_audio_stream(chip, stream, fp, NULL); -} - -static int snd_usb_add_audio_stream_v3(struct snd_usb_audio *chip, - int stream, - struct audioformat *fp, - struct snd_usb_power_domain *pd) -{ - return __snd_usb_add_audio_stream(chip, stream, fp, pd); -} - static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, struct usb_host_interface *alts, int protocol, int iface_no) @@ -1003,7 +994,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, * and request Cluster Descriptor */ wLength = le16_to_cpu(hc_header.wLength); - if (wLength < sizeof(cluster)) + if (wLength < sizeof(*cluster)) return NULL; cluster = kzalloc(wLength, GFP_KERNEL); if (!cluster) @@ -1113,8 +1104,7 @@ found_clock: } } - if (pd) - *pd_out = pd; + *pd_out = pd; return fp; } @@ -1129,7 +1119,6 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip, struct usb_interface_descriptor *altsd; int i, altno, err, stream; struct audioformat *fp = NULL; - struct snd_usb_power_domain *pd = NULL; bool set_iface_first; int num, protocol; @@ -1171,6 +1160,12 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip, if (snd_usb_apply_interface_quirk(chip, iface_no, altno)) continue; + /* pd may be allocated at snd_usb_get_audioformat_uac3() and + * assigned at snd_usb_add_audio_stream(); otherwise it'll be + * freed automatically by cleanup at each loop. + */ + struct snd_usb_power_domain *pd __free(kfree) = NULL; + /* * Roland audio streaming interfaces are marked with protocols * 0/1/2, but are UAC 1 compatible. @@ -1226,23 +1221,16 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip, *has_non_pcm = true; if ((fp->fmt_type == UAC_FORMAT_TYPE_I) == non_pcm) { audioformat_free(fp); - kfree(pd); fp = NULL; - pd = NULL; continue; } snd_usb_audioformat_set_sync_ep(chip, fp); dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint); - if (protocol == UAC_VERSION_3) - err = snd_usb_add_audio_stream_v3(chip, stream, fp, pd); - else - err = snd_usb_add_audio_stream(chip, stream, fp); - + err = snd_usb_add_audio_stream(chip, stream, fp, &pd); if (err < 0) { audioformat_free(fp); - kfree(pd); return err; } diff --git a/sound/usb/stream.h b/sound/usb/stream.h index d92e18d5818f..61b9a133da01 100644 --- a/sound/usb/stream.h +++ b/sound/usb/stream.h @@ -7,7 +7,8 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int snd_usb_add_audio_stream(struct snd_usb_audio *chip, int stream, - struct audioformat *fp); + struct audioformat *fp, + struct snd_usb_power_domain **pdptr); #endif /* __USBAUDIO_STREAM_H */ diff --git a/sound/virtio/virtio_pcm.h b/sound/virtio/virtio_pcm.h index 5dd1b43b9493..368dafa5df3f 100644 --- a/sound/virtio/virtio_pcm.h +++ b/sound/virtio/virtio_pcm.h @@ -37,7 +37,6 @@ struct virtio_pcm_msg; * the device side at resume. * @msgs: Allocated I/O messages. * @nmsgs: Number of allocated I/O messages. - * @msg_last_enqueued: Index of the last I/O message added to the virtqueue. * @msg_count: Number of pending I/O messages in the virtqueue. * @msg_empty: Notify when msg_count is zero. */ diff --git a/tools/accounting/Makefile b/tools/accounting/Makefile index 20bbd461515e..007c0bb8cbbb 100644 --- a/tools/accounting/Makefile +++ b/tools/accounting/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 CC := $(CROSS_COMPILE)gcc -CFLAGS := -I../../usr/include +CFLAGS := -I../include/uapi/ PROGS := getdelays procacct delaytop diff --git a/tools/accounting/getdelays.c b/tools/accounting/getdelays.c index 50792df27707..368a622ca027 100644 --- a/tools/accounting/getdelays.c +++ b/tools/accounting/getdelays.c @@ -60,7 +60,7 @@ int print_task_context_switch_counts; } /* Maximum size of response requested or message sent */ -#define MAX_MSG_SIZE 1024 +#define MAX_MSG_SIZE 2048 /* Maximum number of cpus expected to be specified in a cpumask */ #define MAX_CPUS 32 @@ -115,6 +115,32 @@ error: return -1; } +static int recv_taskstats_msg(int sd, struct msgtemplate *msg) +{ + struct sockaddr_nl nladdr; + struct iovec iov = { + .iov_base = msg, + .iov_len = sizeof(*msg), + }; + struct msghdr hdr = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + int ret; + + ret = recvmsg(sd, &hdr, 0); + if (ret < 0) + return -1; + if (hdr.msg_flags & MSG_TRUNC) { + errno = EMSGSIZE; + return -1; + } + + return ret; +} + static int send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid, __u8 genl_cmd, __u16 nla_type, @@ -633,12 +659,16 @@ int main(int argc, char *argv[]) } do { - rep_len = recv(nl_sd, &msg, sizeof(msg), 0); + rep_len = recv_taskstats_msg(nl_sd, &msg); PRINTF("received %d bytes\n", rep_len); if (rep_len < 0) { - fprintf(stderr, "nonfatal reply error: errno %d\n", - errno); + if (errno == EMSGSIZE) + fprintf(stderr, + "dropped truncated taskstats netlink message, please increase MAX_MSG_SIZE\n"); + else + fprintf(stderr, "nonfatal reply error: errno %d\n", + errno); continue; } if (msg.n.nlmsg_type == NLMSG_ERROR || @@ -680,6 +710,9 @@ int main(int argc, char *argv[]) printf("TGID\t%d\n", rtid); break; case TASKSTATS_TYPE_STATS: + PRINTF("version %u\n", + ((struct taskstats *) + NLA_DATA(na))->version); if (print_delays) print_delayacct((struct taskstats *) NLA_DATA(na)); if (print_io_accounting) diff --git a/tools/accounting/procacct.c b/tools/accounting/procacct.c index e8dee05a6264..46e5986ad927 100644 --- a/tools/accounting/procacct.c +++ b/tools/accounting/procacct.c @@ -71,7 +71,7 @@ int print_task_context_switch_counts; } /* Maximum size of response requested or message sent */ -#define MAX_MSG_SIZE 1024 +#define MAX_MSG_SIZE 2048 /* Maximum number of cpus expected to be specified in a cpumask */ #define MAX_CPUS 32 @@ -121,6 +121,32 @@ error: return -1; } +static int recv_taskstats_msg(int sd, struct msgtemplate *msg) +{ + struct sockaddr_nl nladdr; + struct iovec iov = { + .iov_base = msg, + .iov_len = sizeof(*msg), + }; + struct msghdr hdr = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + int ret; + + ret = recvmsg(sd, &hdr, 0); + if (ret < 0) + return -1; + if (hdr.msg_flags & MSG_TRUNC) { + errno = EMSGSIZE; + return -1; + } + + return ret; +} + static int send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid, __u8 genl_cmd, __u16 nla_type, @@ -239,6 +265,8 @@ void handle_aggr(int mother, struct nlattr *na, int fd) PRINTF("TGID\t%d\n", rtid); break; case TASKSTATS_TYPE_STATS: + PRINTF("version %u\n", + ((struct taskstats *)NLA_DATA(na))->version); if (mother == TASKSTATS_TYPE_AGGR_PID) print_procacct((struct taskstats *) NLA_DATA(na)); if (fd) { @@ -347,12 +375,16 @@ int main(int argc, char *argv[]) } do { - rep_len = recv(nl_sd, &msg, sizeof(msg), 0); + rep_len = recv_taskstats_msg(nl_sd, &msg); PRINTF("received %d bytes\n", rep_len); if (rep_len < 0) { - fprintf(stderr, "nonfatal reply error: errno %d\n", - errno); + if (errno == EMSGSIZE) + fprintf(stderr, + "dropped truncated taskstats netlink message, please increase MAX_MSG_SIZE\n"); + else + fprintf(stderr, "nonfatal reply error: errno %d\n", + errno); continue; } if (msg.n.nlmsg_type == NLMSG_ERROR || diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h b/tools/arch/arm64/include/uapi/asm/kvm.h index a792a599b9d6..1c13bfa2d38a 100644 --- a/tools/arch/arm64/include/uapi/asm/kvm.h +++ b/tools/arch/arm64/include/uapi/asm/kvm.h @@ -428,6 +428,7 @@ enum { #define KVM_DEV_ARM_ITS_RESTORE_TABLES 2 #define KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES 3 #define KVM_DEV_ARM_ITS_CTRL_RESET 4 +#define KVM_DEV_ARM_VGIC_USERSPACE_PPIS 5 /* Device Control API on vcpu fd */ #define KVM_ARM_VCPU_PMU_V3_CTRL 0 diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h index 6673601246b3..eff29645719b 100644 --- a/tools/arch/x86/include/asm/msr-index.h +++ b/tools/arch/x86/include/asm/msr-index.h @@ -793,9 +793,10 @@ #define MSR_AMD64_LBR_SELECT 0xc000010e /* Zen4 */ -#define MSR_ZEN4_BP_CFG 0xc001102e +#define MSR_ZEN4_BP_CFG 0xc001102e #define MSR_ZEN4_BP_CFG_BP_SPEC_REDUCE_BIT 4 #define MSR_ZEN4_BP_CFG_SHARED_BTB_FIX_BIT 5 +#define MSR_ZEN2_BP_CFG_BUG_FIX_BIT 33 /* Fam 19h MSRs */ #define MSR_F19H_UMC_PERF_CTL 0xc0010800 diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 99bdc8a6d26e..704c687ed3ad 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -104,12 +104,18 @@ else endif endif +ifeq ($(findstring -static,${LDFLAGS}),-static) + PKG_CONFIG += --static +endif + all: $(FILES) __BUILD = $(CC) $(CFLAGS) -MD -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(LDFLAGS) BUILD = $(__BUILD) > $(@:.bin=.make.output) 2>&1 BUILD_BFD = $(BUILD) -DPACKAGE='"perf"' -lbfd -ldl - BUILD_ALL = $(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -lslang $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -ldl -lz -llzma -lzstd -lssl + BUILD_ALL = $(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -lslang \ + $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -ldl -lz -llzma -lzstd \ + $(shell $(PKG_CONFIG) --libs --cflags openssl 2>/dev/null) __BUILDXX = $(CXX) $(CXXFLAGS) -MD -Wall -Werror -o $@ $(patsubst %.bin,%.cpp,$(@F)) $(LDFLAGS) BUILDXX = $(__BUILDXX) > $(@:.bin=.make.output) 2>&1 @@ -388,7 +394,7 @@ $(OUTPUT)test-libpfm4.bin: $(BUILD) -lpfm $(OUTPUT)test-libopenssl.bin: - $(BUILD) -lssl + $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags openssl 2>/dev/null) $(OUTPUT)test-bpftool-skeletons.bin: $(SYSTEM_BPFTOOL) version | grep '^features:.*skeletons' \ diff --git a/tools/dma/dma_map_benchmark.c b/tools/dma/dma_map_benchmark.c index dd0ed528e6df..eab0ac611a23 100644 --- a/tools/dma/dma_map_benchmark.c +++ b/tools/dma/dma_map_benchmark.c @@ -20,12 +20,19 @@ static char *directions[] = { "FROM_DEVICE", }; +static char *mode[] = { + "SINGLE_MODE", + "SG_MODE", +}; + int main(int argc, char **argv) { struct map_benchmark map; int fd, opt; /* default single thread, run 20 seconds on NUMA_NO_NODE */ int threads = 1, seconds = 20, node = -1; + /* default single map mode */ + int map_mode = DMA_MAP_BENCH_SINGLE_MODE; /* default dma mask 32bit, bidirectional DMA */ int bits = 32, xdelay = 0, dir = DMA_MAP_BIDIRECTIONAL; /* default granule 1 PAGESIZE */ @@ -33,7 +40,7 @@ int main(int argc, char **argv) int cmd = DMA_MAP_BENCHMARK; - while ((opt = getopt(argc, argv, "t:s:n:b:d:x:g:")) != -1) { + while ((opt = getopt(argc, argv, "t:s:n:b:d:x:g:m:")) != -1) { switch (opt) { case 't': threads = atoi(optarg); @@ -56,11 +63,20 @@ int main(int argc, char **argv) case 'g': granule = atoi(optarg); break; + case 'm': + map_mode = atoi(optarg); + break; default: return -1; } } + if (map_mode < 0 || map_mode >= DMA_MAP_BENCH_MODE_MAX) { + fprintf(stderr, "invalid map mode, SINGLE_MODE:%d, SG_MODE: %d\n", + DMA_MAP_BENCH_SINGLE_MODE, DMA_MAP_BENCH_SG_MODE); + exit(1); + } + if (threads <= 0 || threads > DMA_MAP_MAX_THREADS) { fprintf(stderr, "invalid number of threads, must be in 1-%d\n", DMA_MAP_MAX_THREADS); @@ -110,14 +126,15 @@ int main(int argc, char **argv) map.dma_dir = dir; map.dma_trans_ns = xdelay; map.granule = granule; + map.map_mode = map_mode; if (ioctl(fd, cmd, &map)) { perror("ioctl"); exit(1); } - printf("dma mapping benchmark: threads:%d seconds:%d node:%d dir:%s granule: %d\n", - threads, seconds, node, directions[dir], granule); + printf("dma mapping benchmark(%s): threads:%d seconds:%d node:%d dir:%s granule:%d\n", + mode[map_mode], threads, seconds, node, directions[dir], granule); printf("average map latency(us):%.1f standard deviation:%.1f\n", map.avg_map_100ns/10.0, map.map_stddev/10.0); printf("average unmap latency(us):%.1f standard deviation:%.1f\n", diff --git a/tools/hv/Makefile b/tools/hv/Makefile index 34ffcec264ab..016753f3dd7f 100644 --- a/tools/hv/Makefile +++ b/tools/hv/Makefile @@ -2,7 +2,7 @@ # Makefile for Hyper-V tools include ../scripts/Makefile.include -ARCH := $(shell uname -m 2>/dev/null) +ARCH ?= $(shell uname -m 2>/dev/null) sbindir ?= /usr/sbin libexecdir ?= /usr/libexec sharedstatedir ?= /var/lib @@ -20,7 +20,7 @@ override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include override CFLAGS += -Wno-address-of-packed-member ALL_TARGETS := hv_kvp_daemon hv_vss_daemon -ifneq ($(ARCH), aarch64) +ifneq ($(filter x86_64 x86,$(ARCH)),) ALL_TARGETS += hv_fcopy_uio_daemon endif ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS)) diff --git a/tools/include/linux/mm.h b/tools/include/linux/mm.h index 028f3faf46e7..84b5954f66c3 100644 --- a/tools/include/linux/mm.h +++ b/tools/include/linux/mm.h @@ -17,6 +17,7 @@ #define __va(x) ((void *)((unsigned long)(x))) #define __pa(x) ((unsigned long)(x)) +#define __pa_symbol(x) ((unsigned long)(x)) #define pfn_to_page(pfn) ((void *)((pfn) * PAGE_SIZE)) @@ -32,8 +33,6 @@ static inline phys_addr_t virt_to_phys(volatile void *address) return (phys_addr_t)address; } -void reserve_bootmem_region(phys_addr_t start, phys_addr_t end, int nid); - static inline void totalram_pages_inc(void) { } diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index 80364d4dbebb..d0c0c8605976 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -1224,6 +1224,8 @@ enum kvm_device_type { #define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_PCHPIC, #define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC + KVM_DEV_TYPE_ARM_VGIC_V5, +#define KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_MAX, diff --git a/tools/include/uapi/linux/stddef.h b/tools/include/uapi/linux/stddef.h index c53cde425406..457498259494 100644 --- a/tools/include/uapi/linux/stddef.h +++ b/tools/include/uapi/linux/stddef.h @@ -3,7 +3,6 @@ #define _LINUX_STDDEF_H - #ifndef __always_inline #define __always_inline __inline__ #endif @@ -36,6 +35,11 @@ struct __struct_group_tag(TAG) { MEMBERS } ATTRS NAME; \ } ATTRS +#ifdef __cplusplus +/* sizeof(struct{}) is 1 in C++, not 0, can't use C version of the macro. */ +#define __DECLARE_FLEX_ARRAY(T, member) \ + T member[0] +#else /** * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union * @@ -52,3 +56,23 @@ TYPE NAME[]; \ } #endif + +#ifndef __counted_by +#define __counted_by(m) +#endif + +#ifndef __counted_by_le +#define __counted_by_le(m) +#endif + +#ifndef __counted_by_be +#define __counted_by_be(m) +#endif + +#ifndef __counted_by_ptr +#define __counted_by_ptr(m) +#endif + +#define __kernel_nonstring + +#endif /* _LINUX_STDDEF_H */ diff --git a/tools/include/uapi/linux/taskstats.h b/tools/include/uapi/linux/taskstats.h new file mode 100644 index 000000000000..3ae25f3ce067 --- /dev/null +++ b/tools/include/uapi/linux/taskstats.h @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: LGPL-2.1 WITH Linux-syscall-note */ +/* taskstats.h - exporting per-task statistics + * + * Copyright (C) Shailabh Nagar, IBM Corp. 2006 + * (C) Balbir Singh, IBM Corp. 2006 + * (C) Jay Lan, SGI, 2006 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _LINUX_TASKSTATS_H +#define _LINUX_TASKSTATS_H + +#include +#include + +/* Format for per-task data returned to userland when + * - a task exits + * - listener requests stats for a task + * + * The struct is versioned. Newer versions should only add fields to + * the bottom of the struct to maintain backward compatibility. + * + * + * To add new fields + * a) bump up TASKSTATS_VERSION + * b) add comment indicating new version number at end of struct + * c) add new fields after version comment; maintain 64-bit alignment + */ + + +#define TASKSTATS_VERSION 17 +#define TS_COMM_LEN 32 /* should be >= TASK_COMM_LEN + * in linux/sched.h */ + +struct taskstats { + + /* The version number of this struct. This field is always set to + * TAKSTATS_VERSION, which is defined in . + * Each time the struct is changed, the value should be incremented. + */ + __u16 version; + __u32 ac_exitcode; /* Exit status */ + + /* The accounting flags of a task as defined in + * Defined values are AFORK, ASU, ACOMPAT, ACORE, AXSIG, and AGROUP. + * (AGROUP since version 12). + */ + __u8 ac_flag; /* Record flags */ + __u8 ac_nice; /* task_nice */ + + /* Delay accounting fields start + * + * All values, until comment "Delay accounting fields end" are + * available only if delay accounting is enabled, even though the last + * few fields are not delays + * + * xxx_count is the number of delay values recorded + * xxx_delay_total is the corresponding cumulative delay in nanoseconds + * + * xxx_delay_total wraps around to zero on overflow + * xxx_count incremented regardless of overflow + */ + + /* Delay waiting for cpu, while runnable + * count, delay_total NOT updated atomically + */ + __u64 cpu_count __attribute__((aligned(8))); + __u64 cpu_delay_total; + + /* Following four fields atomically updated using task->delays->lock */ + + /* Delay waiting for synchronous block I/O to complete + * does not account for delays in I/O submission + */ + __u64 blkio_count; + __u64 blkio_delay_total; + + /* Delay waiting for page fault I/O (swap in only) */ + __u64 swapin_count; + __u64 swapin_delay_total; + + /* cpu "wall-clock" running time + * On some architectures, value will adjust for cpu time stolen + * from the kernel in involuntary waits due to virtualization. + * Value is cumulative, in nanoseconds, without a corresponding count + * and wraps around to zero silently on overflow + */ + __u64 cpu_run_real_total; + + /* cpu "virtual" running time + * Uses time intervals seen by the kernel i.e. no adjustment + * for kernel's involuntary waits due to virtualization. + * Value is cumulative, in nanoseconds, without a corresponding count + * and wraps around to zero silently on overflow + */ + __u64 cpu_run_virtual_total; + /* Delay accounting fields end */ + /* version 1 ends here */ + + /* Basic Accounting Fields start */ + char ac_comm[TS_COMM_LEN]; /* Command name */ + __u8 ac_sched __attribute__((aligned(8))); + /* Scheduling discipline */ + __u8 ac_pad[3]; + __u32 ac_uid __attribute__((aligned(8))); + /* User ID */ + __u32 ac_gid; /* Group ID */ + __u32 ac_pid; /* Process ID */ + __u32 ac_ppid; /* Parent process ID */ + /* __u32 range means times from 1970 to 2106 */ + __u32 ac_btime; /* Begin time [sec since 1970] */ + __u64 ac_etime __attribute__((aligned(8))); + /* Elapsed time [usec] */ + __u64 ac_utime; /* User CPU time [usec] */ + __u64 ac_stime; /* SYstem CPU time [usec] */ + __u64 ac_minflt; /* Minor Page Fault Count */ + __u64 ac_majflt; /* Major Page Fault Count */ + /* Basic Accounting Fields end */ + + /* Extended accounting fields start */ + /* Accumulated RSS usage in duration of a task, in MBytes-usecs. + * The current rss usage is added to this counter every time + * a tick is charged to a task's system time. So, at the end we + * will have memory usage multiplied by system time. Thus an + * average usage per system time unit can be calculated. + */ + __u64 coremem; /* accumulated RSS usage in MB-usec */ + /* Accumulated virtual memory usage in duration of a task. + * Same as acct_rss_mem1 above except that we keep track of VM usage. + */ + __u64 virtmem; /* accumulated VM usage in MB-usec */ + + /* High watermark of RSS and virtual memory usage in duration of + * a task, in KBytes. + */ + __u64 hiwater_rss; /* High-watermark of RSS usage, in KB */ + __u64 hiwater_vm; /* High-water VM usage, in KB */ + + /* The following four fields are I/O statistics of a task. */ + __u64 read_char; /* bytes read */ + __u64 write_char; /* bytes written */ + __u64 read_syscalls; /* read syscalls */ + __u64 write_syscalls; /* write syscalls */ + /* Extended accounting fields end */ + +#define TASKSTATS_HAS_IO_ACCOUNTING + /* Per-task storage I/O accounting starts */ + __u64 read_bytes; /* bytes of read I/O */ + __u64 write_bytes; /* bytes of write I/O */ + __u64 cancelled_write_bytes; /* bytes of cancelled write I/O */ + + __u64 nvcsw; /* voluntary_ctxt_switches */ + __u64 nivcsw; /* nonvoluntary_ctxt_switches */ + + /* time accounting for SMT machines */ + __u64 ac_utimescaled; /* utime scaled on frequency etc */ + __u64 ac_stimescaled; /* stime scaled on frequency etc */ + __u64 cpu_scaled_run_real_total; /* scaled cpu_run_real_total */ + + /* Delay waiting for memory reclaim */ + __u64 freepages_count; + __u64 freepages_delay_total; + + + /* Delay waiting for thrashing page */ + __u64 thrashing_count; + __u64 thrashing_delay_total; + + /* v10: 64-bit btime to avoid overflow */ + __u64 ac_btime64; /* 64-bit begin time */ + + /* v11: Delay waiting for memory compact */ + __u64 compact_count; + __u64 compact_delay_total; + + /* v12 begin */ + __u32 ac_tgid; /* thread group ID */ + /* Thread group walltime up to now. This is total process walltime if + * AGROUP flag is set. + */ + __u64 ac_tgetime __attribute__((aligned(8))); + /* Lightweight information to identify process binary files. + * This leaves userspace to match this to a file system path, using + * MAJOR() and MINOR() macros to identify a device and mount point, + * the inode to identify the executable file. This is /proc/self/exe + * at the end, so matching the most recent exec(). Values are zero + * for kernel threads. + */ + __u64 ac_exe_dev; /* program binary device ID */ + __u64 ac_exe_inode; /* program binary inode number */ + /* v12 end */ + + /* v13: Delay waiting for write-protect copy */ + __u64 wpcopy_count; + __u64 wpcopy_delay_total; + + /* v14: Delay waiting for IRQ/SOFTIRQ */ + __u64 irq_count; + __u64 irq_delay_total; + + /* v15: add Delay max and Delay min */ + + /* v16: move Delay max and Delay min to the end of taskstat */ + __u64 cpu_delay_max; + __u64 cpu_delay_min; + + __u64 blkio_delay_max; + __u64 blkio_delay_min; + + __u64 swapin_delay_max; + __u64 swapin_delay_min; + + __u64 freepages_delay_max; + __u64 freepages_delay_min; + + __u64 thrashing_delay_max; + __u64 thrashing_delay_min; + + __u64 compact_delay_max; + __u64 compact_delay_min; + + __u64 wpcopy_delay_max; + __u64 wpcopy_delay_min; + + __u64 irq_delay_max; + __u64 irq_delay_min; + + /*v17: delay max timestamp record*/ + struct __kernel_timespec cpu_delay_max_ts; + struct __kernel_timespec blkio_delay_max_ts; + struct __kernel_timespec swapin_delay_max_ts; + struct __kernel_timespec freepages_delay_max_ts; + struct __kernel_timespec thrashing_delay_max_ts; + struct __kernel_timespec compact_delay_max_ts; + struct __kernel_timespec wpcopy_delay_max_ts; + struct __kernel_timespec irq_delay_max_ts; +}; + + +/* + * Commands sent from userspace + * Not versioned. New commands should only be inserted at the enum's end + * prior to __TASKSTATS_CMD_MAX + */ + +enum { + TASKSTATS_CMD_UNSPEC = 0, /* Reserved */ + TASKSTATS_CMD_GET, /* user->kernel request/get-response */ + TASKSTATS_CMD_NEW, /* kernel->user event */ + __TASKSTATS_CMD_MAX, +}; + +#define TASKSTATS_CMD_MAX (__TASKSTATS_CMD_MAX - 1) + +enum { + TASKSTATS_TYPE_UNSPEC = 0, /* Reserved */ + TASKSTATS_TYPE_PID, /* Process id */ + TASKSTATS_TYPE_TGID, /* Thread group id */ + TASKSTATS_TYPE_STATS, /* taskstats structure */ + TASKSTATS_TYPE_AGGR_PID, /* contains pid + stats */ + TASKSTATS_TYPE_AGGR_TGID, /* contains tgid + stats */ + TASKSTATS_TYPE_NULL, /* contains nothing */ + __TASKSTATS_TYPE_MAX, +}; + +#define TASKSTATS_TYPE_MAX (__TASKSTATS_TYPE_MAX - 1) + +enum { + TASKSTATS_CMD_ATTR_UNSPEC = 0, + TASKSTATS_CMD_ATTR_PID, + TASKSTATS_CMD_ATTR_TGID, + TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, + TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, + __TASKSTATS_CMD_ATTR_MAX, +}; + +#define TASKSTATS_CMD_ATTR_MAX (__TASKSTATS_CMD_ATTR_MAX - 1) + +/* NETLINK_GENERIC related info */ + +#define TASKSTATS_GENL_NAME "TASKSTATS" +#define TASKSTATS_GENL_VERSION 0x1 + +#endif /* _LINUX_TASKSTATS_H */ diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 8b0c3246097f..3a80a018fc7d 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -5852,11 +5852,12 @@ static int load_module_btfs(struct bpf_object *obj) info.name = ptr_to_u64(name); info.name_len = sizeof(name); + btf = NULL; err = bpf_btf_get_info_by_fd(fd, &info, &len); if (err) { err = -errno; pr_warn("failed to get BTF object #%d info: %s\n", id, errstr(err)); - goto err_out; + break; } /* ignore non-module BTFs */ @@ -5870,15 +5871,15 @@ static int load_module_btfs(struct bpf_object *obj) if (err) { pr_warn("failed to load module [%s]'s BTF object #%d: %s\n", name, id, errstr(err)); - goto err_out; + break; } err = libbpf_ensure_mem((void **)&obj->btf_modules, &obj->btf_module_cap, sizeof(*obj->btf_modules), obj->btf_module_cnt + 1); if (err) - goto err_out; + break; - mod_btf = &obj->btf_modules[obj->btf_module_cnt++]; + mod_btf = &obj->btf_modules[obj->btf_module_cnt]; mod_btf->btf = btf; mod_btf->id = id; @@ -5886,16 +5887,16 @@ static int load_module_btfs(struct bpf_object *obj) mod_btf->name = strdup(name); if (!mod_btf->name) { err = -ENOMEM; - goto err_out; + break; } - continue; - -err_out: - close(fd); - return err; + obj->btf_module_cnt++; } - return 0; + if (err) { + btf__free(btf); + close(fd); + } + return err; } static struct bpf_core_cand_list * diff --git a/tools/lib/perf/cpumap.c b/tools/lib/perf/cpumap.c index 4160e7d2e120..e51b0490ad57 100644 --- a/tools/lib/perf/cpumap.c +++ b/tools/lib/perf/cpumap.c @@ -15,12 +15,12 @@ #define MAX_NR_CPUS 4096 -void perf_cpu_map__set_nr(struct perf_cpu_map *map, int nr_cpus) +void perf_cpu_map__set_nr(struct perf_cpu_map *map, unsigned int nr_cpus) { RC_CHK_ACCESS(map)->nr = nr_cpus; } -struct perf_cpu_map *perf_cpu_map__alloc(int nr_cpus) +struct perf_cpu_map *perf_cpu_map__alloc(unsigned int nr_cpus) { RC_STRUCT(perf_cpu_map) *cpus; struct perf_cpu_map *result; @@ -78,7 +78,7 @@ void perf_cpu_map__put(struct perf_cpu_map *map) static struct perf_cpu_map *cpu_map__new_sysconf(void) { struct perf_cpu_map *cpus; - int nr_cpus, nr_cpus_conf; + long nr_cpus, nr_cpus_conf; nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); if (nr_cpus < 0) @@ -86,15 +86,13 @@ static struct perf_cpu_map *cpu_map__new_sysconf(void) nr_cpus_conf = sysconf(_SC_NPROCESSORS_CONF); if (nr_cpus != nr_cpus_conf) { - pr_warning("Number of online CPUs (%d) differs from the number configured (%d) the CPU map will only cover the first %d CPUs.", + pr_warning("Number of online CPUs (%ld) differs from the number configured (%ld) the CPU map will only cover the first %ld CPUs.", nr_cpus, nr_cpus_conf, nr_cpus); } cpus = perf_cpu_map__alloc(nr_cpus); if (cpus != NULL) { - int i; - - for (i = 0; i < nr_cpus; ++i) + for (long i = 0; i < nr_cpus; ++i) RC_CHK_ACCESS(cpus)->map[i].cpu = i; } @@ -132,23 +130,23 @@ static int cmp_cpu(const void *a, const void *b) return cpu_a->cpu - cpu_b->cpu; } -static struct perf_cpu __perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx) +static struct perf_cpu __perf_cpu_map__cpu(const struct perf_cpu_map *cpus, unsigned int idx) { return RC_CHK_ACCESS(cpus)->map[idx]; } -static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, const struct perf_cpu *tmp_cpus) +static struct perf_cpu_map *cpu_map__trim_new(unsigned int nr_cpus, const struct perf_cpu *tmp_cpus) { size_t payload_size = nr_cpus * sizeof(struct perf_cpu); struct perf_cpu_map *cpus = perf_cpu_map__alloc(nr_cpus); - int i, j; if (cpus != NULL) { + unsigned int j = 0; + memcpy(RC_CHK_ACCESS(cpus)->map, tmp_cpus, payload_size); qsort(RC_CHK_ACCESS(cpus)->map, nr_cpus, sizeof(struct perf_cpu), cmp_cpu); /* Remove dups */ - j = 0; - for (i = 0; i < nr_cpus; i++) { + for (unsigned int i = 0; i < nr_cpus; i++) { if (i == 0 || __perf_cpu_map__cpu(cpus, i).cpu != __perf_cpu_map__cpu(cpus, i - 1).cpu) { @@ -167,9 +165,8 @@ struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list) struct perf_cpu_map *cpus = NULL; unsigned long start_cpu, end_cpu = 0; char *p = NULL; - int i, nr_cpus = 0; + unsigned int nr_cpus = 0, max_entries = 0; struct perf_cpu *tmp_cpus = NULL, *tmp; - int max_entries = 0; if (!cpu_list) return perf_cpu_map__new_online_cpus(); @@ -208,9 +205,10 @@ struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list) for (; start_cpu <= end_cpu; start_cpu++) { /* check for duplicates */ - for (i = 0; i < nr_cpus; i++) + for (unsigned int i = 0; i < nr_cpus; i++) { if (tmp_cpus[i].cpu == (int16_t)start_cpu) goto invalid; + } if (nr_cpus == max_entries) { max_entries += max(end_cpu - start_cpu + 1, 16UL); @@ -252,12 +250,12 @@ struct perf_cpu_map *perf_cpu_map__new_int(int cpu) return cpus; } -static int __perf_cpu_map__nr(const struct perf_cpu_map *cpus) +static unsigned int __perf_cpu_map__nr(const struct perf_cpu_map *cpus) { return RC_CHK_ACCESS(cpus)->nr; } -struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx) +struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, unsigned int idx) { struct perf_cpu result = { .cpu = -1 @@ -269,7 +267,7 @@ struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx) return result; } -int perf_cpu_map__nr(const struct perf_cpu_map *cpus) +unsigned int perf_cpu_map__nr(const struct perf_cpu_map *cpus) { return cpus ? __perf_cpu_map__nr(cpus) : 1; } @@ -294,7 +292,7 @@ bool perf_cpu_map__is_empty(const struct perf_cpu_map *map) int perf_cpu_map__idx(const struct perf_cpu_map *cpus, struct perf_cpu cpu) { - int low, high; + unsigned int low, high; if (!cpus) return -1; @@ -324,7 +322,7 @@ bool perf_cpu_map__has(const struct perf_cpu_map *cpus, struct perf_cpu cpu) bool perf_cpu_map__equal(const struct perf_cpu_map *lhs, const struct perf_cpu_map *rhs) { - int nr; + unsigned int nr; if (lhs == rhs) return true; @@ -336,7 +334,7 @@ bool perf_cpu_map__equal(const struct perf_cpu_map *lhs, const struct perf_cpu_m if (nr != __perf_cpu_map__nr(rhs)) return false; - for (int idx = 0; idx < nr; idx++) { + for (unsigned int idx = 0; idx < nr; idx++) { if (__perf_cpu_map__cpu(lhs, idx).cpu != __perf_cpu_map__cpu(rhs, idx).cpu) return false; } @@ -353,7 +351,7 @@ struct perf_cpu perf_cpu_map__min(const struct perf_cpu_map *map) struct perf_cpu cpu, result = { .cpu = -1 }; - int idx; + unsigned int idx; perf_cpu_map__for_each_cpu_skip_any(cpu, idx, map) { result = cpu; @@ -384,7 +382,7 @@ bool perf_cpu_map__is_subset(const struct perf_cpu_map *a, const struct perf_cpu if (!a || __perf_cpu_map__nr(b) > __perf_cpu_map__nr(a)) return false; - for (int i = 0, j = 0; i < __perf_cpu_map__nr(a); i++) { + for (unsigned int i = 0, j = 0; i < __perf_cpu_map__nr(a); i++) { if (__perf_cpu_map__cpu(a, i).cpu > __perf_cpu_map__cpu(b, j).cpu) return false; if (__perf_cpu_map__cpu(a, i).cpu == __perf_cpu_map__cpu(b, j).cpu) { @@ -410,8 +408,7 @@ bool perf_cpu_map__is_subset(const struct perf_cpu_map *a, const struct perf_cpu int perf_cpu_map__merge(struct perf_cpu_map **orig, struct perf_cpu_map *other) { struct perf_cpu *tmp_cpus; - int tmp_len; - int i, j, k; + unsigned int tmp_len, i, j, k; struct perf_cpu_map *merged; if (perf_cpu_map__is_subset(*orig, other)) @@ -455,7 +452,7 @@ int perf_cpu_map__merge(struct perf_cpu_map **orig, struct perf_cpu_map *other) struct perf_cpu_map *perf_cpu_map__intersect(struct perf_cpu_map *orig, struct perf_cpu_map *other) { - int i, j, k; + unsigned int i, j, k; struct perf_cpu_map *merged; if (perf_cpu_map__is_subset(other, orig)) diff --git a/tools/lib/perf/evsel.c b/tools/lib/perf/evsel.c index 13a307fc75ae..f747c0bc692d 100644 --- a/tools/lib/perf/evsel.c +++ b/tools/lib/perf/evsel.c @@ -127,7 +127,8 @@ int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus, struct perf_thread_map *threads) { struct perf_cpu cpu; - int idx, thread, err = 0; + unsigned int idx; + int thread, err = 0; if (cpus == NULL) { static struct perf_cpu_map *empty_cpu_map; @@ -460,7 +461,7 @@ int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu_map_idx) int perf_evsel__enable_thread(struct perf_evsel *evsel, int thread) { struct perf_cpu cpu __maybe_unused; - int idx; + unsigned int idx; int err; perf_cpu_map__for_each_cpu(cpu, idx, evsel->cpus) { @@ -499,12 +500,13 @@ int perf_evsel__disable(struct perf_evsel *evsel) int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter) { - int err = 0, i; + int err = 0; - for (i = 0; i < perf_cpu_map__nr(evsel->cpus) && !err; i++) + for (unsigned int i = 0; i < perf_cpu_map__nr(evsel->cpus) && !err; i++) { err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_SET_FILTER, (void *)filter, i); + } return err; } diff --git a/tools/lib/perf/include/internal/cpumap.h b/tools/lib/perf/include/internal/cpumap.h index e2be2d17c32b..c19678188b17 100644 --- a/tools/lib/perf/include/internal/cpumap.h +++ b/tools/lib/perf/include/internal/cpumap.h @@ -16,16 +16,16 @@ DECLARE_RC_STRUCT(perf_cpu_map) { refcount_t refcnt; /** Length of the map array. */ - int nr; + unsigned int nr; /** The CPU values. */ struct perf_cpu map[]; }; -struct perf_cpu_map *perf_cpu_map__alloc(int nr_cpus); +struct perf_cpu_map *perf_cpu_map__alloc(unsigned int nr_cpus); int perf_cpu_map__idx(const struct perf_cpu_map *cpus, struct perf_cpu cpu); bool perf_cpu_map__is_subset(const struct perf_cpu_map *a, const struct perf_cpu_map *b); -void perf_cpu_map__set_nr(struct perf_cpu_map *map, int nr_cpus); +void perf_cpu_map__set_nr(struct perf_cpu_map *map, unsigned int nr_cpus); static inline refcount_t *perf_cpu_map__refcnt(struct perf_cpu_map *map) { diff --git a/tools/lib/perf/include/perf/cpumap.h b/tools/lib/perf/include/perf/cpumap.h index 58cc5c5fa47c..a1dd25db65b6 100644 --- a/tools/lib/perf/include/perf/cpumap.h +++ b/tools/lib/perf/include/perf/cpumap.h @@ -49,7 +49,7 @@ LIBPERF_API void perf_cpu_map__put(struct perf_cpu_map *map); * perf_cpu_map__cpu - get the CPU value at the given index. Returns -1 if index * is invalid. */ -LIBPERF_API struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx); +LIBPERF_API struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, unsigned int idx); /** * perf_cpu_map__nr - for an empty map returns 1, as perf_cpu_map__cpu returns a * cpu of -1 for an invalid index, this makes an empty map @@ -57,7 +57,7 @@ LIBPERF_API struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, i * the result is the number CPUs in the map plus one if the * "any CPU"/dummy value is present. */ -LIBPERF_API int perf_cpu_map__nr(const struct perf_cpu_map *cpus); +LIBPERF_API unsigned int perf_cpu_map__nr(const struct perf_cpu_map *cpus); /** * perf_cpu_map__has_any_cpu_or_is_empty - is map either empty or has the "any CPU"/dummy value. */ diff --git a/tools/lib/python/kdoc/kdoc_parser.py b/tools/lib/python/kdoc/kdoc_parser.py index 74af7ae47aa4..c3f966da533e 100644 --- a/tools/lib/python/kdoc/kdoc_parser.py +++ b/tools/lib/python/kdoc/kdoc_parser.py @@ -439,6 +439,11 @@ class KernelDoc: # Ignore argument attributes arg = KernRe(r'\sPOS0?\s').sub(' ', arg) + # Replace '[at_least ' with '[static '. This allows sphinx to parse + # array parameter declarations like 'char A[at_least 4]', where + # 'at_least' is #defined to 'static' by the kernel headers. + arg = arg.replace('[at_least ', '[static ') + # Strip leading/trailing spaces arg = arg.strip() arg = KernRe(r'\s+').sub(' ', arg, count=1) diff --git a/tools/net/ynl/Makefile.deps b/tools/net/ynl/Makefile.deps index 08205f9fc525..cc53b2f21c44 100644 --- a/tools/net/ynl/Makefile.deps +++ b/tools/net/ynl/Makefile.deps @@ -15,9 +15,11 @@ UAPI_PATH:=../../../../include/uapi/ get_hdr_inc=-D$(1) -include $(UAPI_PATH)/linux/$(2) get_hdr_inc2=-D$(1) -D$(2) -include $(UAPI_PATH)/linux/$(3) +CFLAGS_dev-energymodel:=$(call get_hdr_inc,_LINUX_DEV_ENERGYMODEL_H,dev_energymodel.h) CFLAGS_devlink:=$(call get_hdr_inc,_LINUX_DEVLINK_H_,devlink.h) CFLAGS_dpll:=$(call get_hdr_inc,_LINUX_DPLL_H,dpll.h) -CFLAGS_ethtool:=$(call get_hdr_inc,_LINUX_ETHTOOL_H,ethtool.h) \ +CFLAGS_ethtool:=$(call get_hdr_inc,_LINUX_TYPELIMITS_H,typelimits.h) \ + $(call get_hdr_inc,_LINUX_ETHTOOL_H,ethtool.h) \ $(call get_hdr_inc,_LINUX_ETHTOOL_NETLINK_H_,ethtool_netlink.h) \ $(call get_hdr_inc,_LINUX_ETHTOOL_NETLINK_GENERATED_H,ethtool_netlink_generated.h) CFLAGS_handshake:=$(call get_hdr_inc,_LINUX_HANDSHAKE_H,handshake.h) diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py index 0e1e486c1185..cdc3646f2642 100755 --- a/tools/net/ynl/pyynl/ynl_gen_c.py +++ b/tools/net/ynl/pyynl/ynl_gen_c.py @@ -3212,6 +3212,8 @@ def render_uapi(family, cw): for const in family['definitions']: if const.get('header'): continue + if const.get('scope', 'uapi') != 'uapi': + continue if const['type'] != 'const': cw.writes_defines(defines) @@ -3339,6 +3341,25 @@ def render_uapi(family, cw): cw.p(f'#endif /* {hdr_prot} */') +def render_scoped_consts(family, cw, scope): + defines = [] + for const in family['definitions']: + if const['type'] != 'const': + continue + if const.get('header'): + continue + if const.get('scope') != scope: + continue + name_pfx = const.get('name-prefix', f"{family.ident_name}-") + defines.append([ + c_upper(family.get('c-define-name', + f"{name_pfx}{const['name']}")), + const['value']]) + if defines: + cw.writes_defines(defines) + cw.nl() + + def _render_user_ntf_entry(ri, op): if not ri.family.is_classic(): ri.cw.block_start(line=f"[{op.enum_name}] = ") @@ -3504,8 +3525,12 @@ def main(): cw.p('#include "ynl.h"') headers = [] for definition in parsed['definitions'] + parsed['attribute-sets']: - if 'header' in definition: - headers.append(definition['header']) + if 'header' not in definition: + continue + scope = definition.get('scope', 'uapi') + if scope != 'uapi' and scope != args.mode: + continue + headers.append(definition['header']) if args.mode == 'user': headers.append(parsed.uapi_header) seen_header = [] @@ -3522,6 +3547,7 @@ def main(): for one in args.user_header: cw.p(f'#include "{one}"') else: + render_scoped_consts(parsed, cw, 'user') cw.p('struct ynl_sock;') cw.nl() render_user_family(parsed, cw, True) @@ -3529,6 +3555,7 @@ def main(): if args.mode == "kernel": if args.header: + render_scoped_consts(parsed, cw, 'kernel') for _, struct in sorted(parsed.pure_nested_structs.items()): if struct.request: cw.p('/* Common nested types */') diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index 547f1a268018..a688738809c4 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt @@ -110,8 +110,11 @@ include::itrace.txt[] Interleave source code with assembly code. Enabled by default, disable with --no-source. ---symfs=:: - Look for files with symbols relative to this directory. +--symfs=:: + Look for files with symbols relative to this directory. The optional + layout can be 'hierarchy' (default, matches full path) or 'flat' + (only matches base name). This is useful when debug files are stored + in a flat directory structure. -M:: --disassembler-style=:: Set disassembler style for objdump. diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt index 1160224cb718..c5913cf59c98 100644 --- a/tools/perf/Documentation/perf-bench.txt +++ b/tools/perf/Documentation/perf-bench.txt @@ -274,6 +274,10 @@ Repeat mmap() invocation this number of times. --cycles:: Use perf's cpu-cycles event instead of gettimeofday syscall. +-t:: +--threads=:: +Create multiple threads to call mmap/munmap concurrently. + SUITES FOR 'numa' ~~~~~~~~~~~~~~~~~ *mem*:: diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt index 642d1c490d9e..9b223f892829 100644 --- a/tools/perf/Documentation/perf-config.txt +++ b/tools/perf/Documentation/perf-config.txt @@ -210,6 +210,12 @@ core.*:: Sets a timeout (in milliseconds) for parsing /proc//maps files. Can be overridden by the --proc-map-timeout option on supported subcommands. The default timeout is 500ms. + addr2line-disable-warn:: + When set to 'true' disable all warnings from 'addr2line' output. + Default setting is 'false' to show these warnings. + addr2line-timeout:: + Sets a timeout (in milliseconds) for parsing 'addr2line' + output. The default timeout is 5s. tui.*, gtk.*:: Subcommands that can be configured here are 'top', 'report' and 'annotate'. diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index 58efab72d2e5..8e4a3f093135 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt @@ -81,8 +81,11 @@ OPTIONS --force:: Don't do ownership validation. ---symfs=:: - Look for files with symbols relative to this directory. +--symfs=:: + Look for files with symbols relative to this directory. The optional + layout can be 'hierarchy' (default, matches full path) or 'flat' + (only matches base name). This is useful when debug files are stored + in a flat directory structure. -b:: --baseline-only:: diff --git a/tools/perf/Documentation/perf-kwork.txt b/tools/perf/Documentation/perf-kwork.txt index 21e607669d78..5c33a1fb2ffe 100644 --- a/tools/perf/Documentation/perf-kwork.txt +++ b/tools/perf/Documentation/perf-kwork.txt @@ -169,8 +169,11 @@ OPTIONS for 'perf kwork timehist' --max-stack:: Maximum number of functions to display in backtrace, default 5. ---symfs=:: - Look for files with symbols relative to this directory. +--symfs=:: + Look for files with symbols relative to this directory. The optional + layout can be 'hierarchy' (default, matches full path) or 'flat' + (only matches base name). This is useful when debug files are stored + in a flat directory structure. --time:: Only analyze samples within given time window: ,. Times diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 5c43a6edc0e5..2e5790325430 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -50,6 +50,12 @@ OPTIONS --source=PATH:: Specify path to kernel source. +--symfs=:: + Look for files with symbols relative to this directory. The optional + layout can be 'hierarchy' (default, matches full path) or 'flat' + (only matches base name). This is useful when debug files are stored + in a flat directory structure. + -v:: --verbose:: Be more verbose (show parsed arguments, etc). diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index acef3ff4178e..22f87eaa3279 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -88,7 +88,7 @@ OPTIONS Sort histogram entries by given key(s) - multiple keys can be specified in CSV format. Following sort keys are available: pid, comm, dso, symbol, parent, cpu, socket, srcline, weight, - local_weight, cgroup_id, addr. + local_weight, cgroup_id, addr, comm_nodigit. Each key has following meaning: @@ -136,13 +136,17 @@ OPTIONS - addr: (Full) virtual address of the sampled instruction - retire_lat: On X86, this reports pipeline stall of this instruction compared to the previous instruction in cycles. And currently supported only on X86 - - simd: Flags describing a SIMD operation. "e" for empty Arm SVE predicate. "p" for partial Arm SVE predicate + - simd: Flags describing a SIMD operation. The architecture type can be Arm's + ASE (Advanced SIMD extension), SVE, SME. It provides an extra tag for + predicate: "e" for empty predicate, "p" for partial predicate, "d" for + predicate disabled, and "f" for full predicate. - type: Data type of sample memory access. - typeoff: Offset in the data type of sample memory access. - symoff: Offset in the symbol. - weight1: Average value of event specific weight (1st field of weight_struct). - weight2: Average value of event specific weight (2nd field of weight_struct). - weight3: Average value of event specific weight (3rd field of weight_struct). + - comm_nodigit: same as comm, with numbers replaced by "" By default, overhead, comm, dso and symbol keys are used. (i.e. --sort overhead,comm,dso,symbol). @@ -368,8 +372,11 @@ OPTIONS --force:: Don't do ownership validation. ---symfs=:: - Look for files with symbols relative to this directory. +--symfs=:: + Look for files with symbols relative to this directory. The optional + layout can be 'hierarchy' (default, matches full path) or 'flat' + (only matches base name). This is useful when debug files are stored + in a flat directory structure. -C:: --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index 4d9981609c04..a4221398e5e0 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt @@ -437,8 +437,11 @@ OPTIONS for 'perf sched timehist' Show all scheduling events followed by a summary by thread with min, max, and average run times (in sec) and relative stddev. ---symfs=:: - Look for files with symbols relative to this directory. +--symfs=:: + Look for files with symbols relative to this directory. The optional + layout can be 'hierarchy' (default, matches full path) or 'flat' + (only matches base name). This is useful when debug files are stored + in a flat directory structure. -V:: --cpu-visual:: diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index ddf92f9c7821..200ea25891d8 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -307,8 +307,11 @@ OPTIONS --kallsyms=:: kallsyms pathname ---symfs=:: - Look for files with symbols relative to this directory. +--symfs=:: + Look for files with symbols relative to this directory. The optional + layout can be 'hierarchy' (default, matches full path) or 'flat' + (only matches base name). This is useful when debug files are stored + in a flat directory structure. -G:: --hide-call-graph:: diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 7cccc3a847d1..b72a29c9223c 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -578,6 +578,10 @@ $ perf config stat.no-csv-summary=true Only enable events on applying cpu with this type for hybrid platform (e.g. core or atom)" +--pmu-filter:: +Only enable events on applying pmu with specified for multiple +pmus with same type (e.g. hisi_sicl2_cpa0 or hisi_sicl0_cpa0) + EXAMPLES -------- diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt index ef2281c56743..bacc5df3c400 100644 --- a/tools/perf/Documentation/perf-timechart.txt +++ b/tools/perf/Documentation/perf-timechart.txt @@ -53,8 +53,11 @@ TIMECHART OPTIONS -f:: --force:: Don't complain, do it. ---symfs=:: - Look for files with symbols relative to this directory. +--symfs=:: + Look for files with symbols relative to this directory. The optional + layout can be 'hierarchy' (default, matches full path) or 'flat' + (only matches base name). This is useful when debug files are stored + in a flat directory structure. -n:: --proc-num:: Print task info for at least given number of tasks. diff --git a/tools/perf/Documentation/tips.txt b/tools/perf/Documentation/tips.txt index 3fee9b2a88ea..ebf12a8c5db5 100644 --- a/tools/perf/Documentation/tips.txt +++ b/tools/perf/Documentation/tips.txt @@ -11,7 +11,7 @@ Search options using a keyword: perf report -h Use parent filter to see specific call path: perf report -p List events using substring match: perf list To see list of saved events and attributes: perf evlist -v -Use --symfs if your symbol files are in non-standard locations +Use --symfs [,layout] if your symbol files are in non-standard locations. To see callchains in a more compact form: perf report -g folded To see call chains by final symbol taking CPU time (bottom up) use perf report -G Show individual samples with: perf script diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 15fbba9f4ca8..333ddd0e4bd8 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -349,6 +349,7 @@ CORE_CFLAGS += -fno-omit-frame-pointer CORE_CFLAGS += -Wall CORE_CFLAGS += -Wextra CORE_CFLAGS += -std=gnu11 +CORE_CFLAGS += -funsigned-char CXXFLAGS += -std=gnu++17 -fno-exceptions -fno-rtti CXXFLAGS += -Wall diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index f7b936deeaa2..cee19c923c06 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -547,7 +547,7 @@ $(clone_flags_array): $(beauty_uapi_linux_dir)/sched.h $(clone_flags_tbl) $(Q)$(SHELL) '$(clone_flags_tbl)' $(beauty_uapi_linux_dir) > $@ drm_ioctl_array := $(beauty_ioctl_outdir)/drm_ioctl_array.c -drm_hdr_dir := $(srctree)/tools/include/uapi/drm +drm_hdr_dir := $(srctree)/tools/perf/trace/beauty/include/uapi/drm drm_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/drm_ioctl.sh $(drm_ioctl_array): $(drm_hdr_dir)/drm.h $(drm_hdr_dir)/i915_drm.h $(drm_ioctl_tbl) @@ -556,8 +556,8 @@ $(drm_ioctl_array): $(drm_hdr_dir)/drm.h $(drm_hdr_dir)/i915_drm.h $(drm_ioctl_t fadvise_advice_array := $(beauty_outdir)/fadvise_advice_array.c fadvise_advice_tbl := $(srctree)/tools/perf/trace/beauty/fadvise.sh -$(fadvise_advice_array): $(linux_uapi_dir)/in.h $(fadvise_advice_tbl) - $(Q)$(SHELL) '$(fadvise_advice_tbl)' $(linux_uapi_dir) > $@ +$(fadvise_advice_array): $(beauty_uapi_linux_dir)/fadvise.h $(fadvise_advice_tbl) + $(Q)$(SHELL) '$(fadvise_advice_tbl)' $(beauty_uapi_linux_dir) > $@ fsmount_arrays := $(beauty_outdir)/fsmount_arrays.c fsmount_tbls := $(srctree)/tools/perf/trace/beauty/fsmount.sh diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c index eb6404267f17..27bb14c8b880 100644 --- a/tools/perf/arch/arm/util/auxtrace.c +++ b/tools/perf/arch/arm/util/auxtrace.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include "../../../util/auxtrace.h" @@ -27,7 +27,7 @@ static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err) /* arm_spe_xxxxxxxxx\0 */ char arm_spe_pmu_name[sizeof(ARM_SPE_PMU_NAME) + 10]; - arm_spe_pmus = zalloc(sizeof(struct perf_pmu *) * nr_cpus); + arm_spe_pmus = calloc(nr_cpus, sizeof(struct perf_pmu *)); if (!arm_spe_pmus) { pr_err("spes alloc failed\n"); *err = -ENOMEM; @@ -79,7 +79,7 @@ static struct perf_pmu **find_all_hisi_ptt_pmus(int *nr_ptts, int *err) if (!(*nr_ptts)) goto out; - hisi_ptt_pmus = zalloc(sizeof(struct perf_pmu *) * (*nr_ptts)); + hisi_ptt_pmus = calloc((*nr_ptts), sizeof(struct perf_pmu *)); if (!hisi_ptt_pmus) { pr_err("hisi_ptt alloc failed\n"); *err = -ENOMEM; diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c index 4418d21708d6..b7a839de8707 100644 --- a/tools/perf/arch/arm/util/cs-etm.c +++ b/tools/perf/arch/arm/util/cs-etm.c @@ -197,7 +197,8 @@ static struct perf_pmu *cs_etm_get_pmu(struct auxtrace_record *itr) static int cs_etm_validate_config(struct perf_pmu *cs_etm_pmu, struct evsel *evsel) { - int idx, err = 0; + unsigned int idx; + int err = 0; struct perf_cpu_map *event_cpus = evsel->evlist->core.user_requested_cpus; struct perf_cpu_map *intersect_cpus; struct perf_cpu cpu; @@ -546,7 +547,7 @@ static size_t cs_etm_info_priv_size(struct auxtrace_record *itr, struct evlist *evlist) { - int idx; + unsigned int idx; int etmv3 = 0, etmv4 = 0, ete = 0; struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus; struct perf_cpu_map *intersect_cpus; @@ -783,7 +784,7 @@ static int cs_etm_info_fill(struct auxtrace_record *itr, struct perf_record_auxtrace_info *info, size_t priv_size) { - int i; + unsigned int i; u32 offset; u64 nr_cpu, type; struct perf_cpu_map *cpu_map; diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c index 17ced7bbbdda..f00d72d087fc 100644 --- a/tools/perf/arch/arm64/util/arm-spe.c +++ b/tools/perf/arch/arm64/util/arm-spe.c @@ -144,7 +144,8 @@ static int arm_spe_info_fill(struct auxtrace_record *itr, struct perf_record_auxtrace_info *auxtrace_info, size_t priv_size) { - int i, ret; + unsigned int i; + int ret; size_t offset; struct arm_spe_recording *sper = container_of(itr, struct arm_spe_recording, itr); diff --git a/tools/perf/arch/arm64/util/header.c b/tools/perf/arch/arm64/util/header.c index cbc0ba101636..95e71c4f6c78 100644 --- a/tools/perf/arch/arm64/util/header.c +++ b/tools/perf/arch/arm64/util/header.c @@ -43,7 +43,7 @@ static int _get_cpuid(char *buf, size_t sz, struct perf_cpu cpu) int get_cpuid(char *buf, size_t sz, struct perf_cpu cpu) { struct perf_cpu_map *cpus; - int idx; + unsigned int idx; if (cpu.cpu != -1) return _get_cpuid(buf, sz, cpu); diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index 4908d54dd33b..21836f70f231 100644 --- a/tools/perf/arch/common.c +++ b/tools/perf/arch/common.c @@ -9,14 +9,14 @@ #include "../util/debug.h" #include -const char *const arc_triplets[] = { +static const char *const arc_triplets[] = { "arc-linux-", "arc-snps-linux-uclibc-", "arc-snps-linux-gnu-", NULL }; -const char *const arm_triplets[] = { +static const char *const arm_triplets[] = { "arm-eabi-", "arm-linux-androideabi-", "arm-unknown-linux-", @@ -28,13 +28,13 @@ const char *const arm_triplets[] = { NULL }; -const char *const arm64_triplets[] = { +static const char *const arm64_triplets[] = { "aarch64-linux-android-", "aarch64-linux-gnu-", NULL }; -const char *const powerpc_triplets[] = { +static const char *const powerpc_triplets[] = { "powerpc-unknown-linux-gnu-", "powerpc-linux-gnu-", "powerpc64-unknown-linux-gnu-", @@ -43,40 +43,40 @@ const char *const powerpc_triplets[] = { NULL }; -const char *const riscv32_triplets[] = { +static const char *const riscv32_triplets[] = { "riscv32-unknown-linux-gnu-", "riscv32-linux-android-", "riscv32-linux-gnu-", NULL }; -const char *const riscv64_triplets[] = { +static const char *const riscv64_triplets[] = { "riscv64-unknown-linux-gnu-", "riscv64-linux-android-", "riscv64-linux-gnu-", NULL }; -const char *const s390_triplets[] = { +static const char *const s390_triplets[] = { "s390-ibm-linux-", "s390x-linux-gnu-", NULL }; -const char *const sh_triplets[] = { +static const char *const sh_triplets[] = { "sh-unknown-linux-gnu-", "sh-linux-gnu-", NULL }; -const char *const sparc_triplets[] = { +static const char *const sparc_triplets[] = { "sparc-unknown-linux-gnu-", "sparc64-unknown-linux-gnu-", "sparc64-linux-gnu-", NULL }; -const char *const x86_triplets[] = { +static const char *const x86_triplets[] = { "x86_64-pc-linux-gnu-", "x86_64-unknown-linux-gnu-", "i686-pc-linux-gnu-", @@ -90,7 +90,7 @@ const char *const x86_triplets[] = { NULL }; -const char *const mips_triplets[] = { +static const char *const mips_triplets[] = { "mips-unknown-linux-gnu-", "mipsel-linux-android-", "mips-linux-gnu-", diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build index 3ad73d0289f3..8d91e78d31c9 100644 --- a/tools/perf/arch/loongarch/util/Build +++ b/tools/perf/arch/loongarch/util/Build @@ -1,4 +1,3 @@ perf-util-y += header.o perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o diff --git a/tools/perf/arch/powerpc/util/auxtrace.c b/tools/perf/arch/powerpc/util/auxtrace.c index 292ea335e4ff..e39deff6c857 100644 --- a/tools/perf/arch/powerpc/util/auxtrace.c +++ b/tools/perf/arch/powerpc/util/auxtrace.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "../../util/evlist.h" #include "../../util/debug.h" diff --git a/tools/perf/arch/sh/include/dwarf-regs-table.h b/tools/perf/arch/sh/include/dwarf-regs-table.h index 900e69619970..b5974a090fb4 100644 --- a/tools/perf/arch/sh/include/dwarf-regs-table.h +++ b/tools/perf/arch/sh/include/dwarf-regs-table.h @@ -2,7 +2,7 @@ #ifdef DEFINE_DWARF_REGSTR_TABLE /* This is included in perf/util/dwarf-regs.c */ -const char * const sh_regstr_tbl[] = { +static const char * const sh_regstr_tbl[] = { "r0", "r1", "r2", diff --git a/tools/perf/arch/x86/tests/amd-ibs-period.c b/tools/perf/arch/x86/tests/amd-ibs-period.c index 223e059e04de..cee9e11c05e0 100644 --- a/tools/perf/arch/x86/tests/amd-ibs-period.c +++ b/tools/perf/arch/x86/tests/amd-ibs-period.c @@ -8,7 +8,6 @@ #include "arch-tests.h" #include "linux/perf_event.h" -#include "linux/zalloc.h" #include "tests/tests.h" #include "../perf-sys.h" #include "pmu.h" @@ -60,7 +59,7 @@ static int dummy_workload_1(unsigned long count) 0xcc, /* int 3 */ }; - p = zalloc(2 * page_size); + p = calloc(2, page_size); if (!p) { printf("malloc() failed. %m"); return 1; diff --git a/tools/perf/arch/x86/tests/dwarf-unwind.c b/tools/perf/arch/x86/tests/dwarf-unwind.c index e91a73d09cec..99d2b7ed016f 100644 --- a/tools/perf/arch/x86/tests/dwarf-unwind.c +++ b/tools/perf/arch/x86/tests/dwarf-unwind.c @@ -54,22 +54,13 @@ int test__arch_unwind_sample(struct perf_sample *sample, struct thread *thread) { struct regs_dump *regs = perf_sample__user_regs(sample); - u64 *buf; + u64 *buf = calloc(PERF_REGS_MAX, sizeof(u64)); - buf = malloc(sizeof(u64) * PERF_REGS_MAX); if (!buf) { pr_debug("failed to allocate sample uregs data\n"); return -1; } -#ifdef MEMORY_SANITIZER - /* - * Assignments to buf in the assembly function perf_regs_load aren't - * seen by memory sanitizer. Zero the memory to convince memory - * sanitizer the memory is initialized. - */ - memset(buf, 0, sizeof(u64) * PERF_REGS_MAX); -#endif perf_regs_load(buf); regs->abi = PERF_SAMPLE_REGS_ABI; regs->regs = buf; diff --git a/tools/perf/arch/x86/util/pmu.c b/tools/perf/arch/x86/util/pmu.c index a3f96221758d..7c9d238922a6 100644 --- a/tools/perf/arch/x86/util/pmu.c +++ b/tools/perf/arch/x86/util/pmu.c @@ -5,8 +5,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -71,11 +71,6 @@ static int snc_nodes_per_l3_cache(void) return snc_nodes; } -static bool starts_with(const char *str, const char *prefix) -{ - return !strncmp(prefix, str, strlen(prefix)); -} - static int num_chas(void) { static bool checked_chas; @@ -93,7 +88,7 @@ static int num_chas(void) while ((dent = io_dir__readdir(&dir)) != NULL) { /* Note, dent->d_type will be DT_LNK and so isn't a useful filter. */ - if (starts_with(dent->d_name, "uncore_cha_")) + if (strstarts(dent->d_name, "uncore_cha_")) num_chas++; } close(fd); @@ -225,7 +220,8 @@ static void gnr_uncore_cha_imc_adjust_cpumask_for_snc(struct perf_pmu *pmu, bool static struct perf_cpu_map *cha_adjusted[MAX_SNCS]; static struct perf_cpu_map *imc_adjusted[MAX_SNCS]; struct perf_cpu_map **adjusted = cha ? cha_adjusted : imc_adjusted; - int idx, pmu_snc, cpu_adjust; + unsigned int idx; + int pmu_snc, cpu_adjust; struct perf_cpu cpu; bool alloc; @@ -305,9 +301,9 @@ void perf_pmu__arch_init(struct perf_pmu *pmu) else pmu->mem_events = perf_mem_events_intel; } else if (x86__is_intel_graniterapids()) { - if (starts_with(pmu->name, "uncore_cha_")) + if (strstarts(pmu->name, "uncore_cha_")) gnr_uncore_cha_imc_adjust_cpumask_for_snc(pmu, /*cha=*/true); - else if (starts_with(pmu->name, "uncore_imc_")) + else if (strstarts(pmu->name, "uncore_imc_")) gnr_uncore_cha_imc_adjust_cpumask_for_snc(pmu, /*cha=*/false); } } diff --git a/tools/perf/bench/breakpoint.c b/tools/perf/bench/breakpoint.c index dfd18f5db97d..1b7cd4481bd2 100644 --- a/tools/perf/bench/breakpoint.c +++ b/tools/perf/bench/breakpoint.c @@ -16,7 +16,7 @@ #include "bench.h" #include "futex.h" -struct { +static struct { unsigned int nbreakpoints; unsigned int nparallel; unsigned int nthreads; @@ -173,7 +173,7 @@ int bench_breakpoint_thread(int argc, const char **argv) return 0; } -struct { +static struct { unsigned int npassive; unsigned int nactive; } enable_params = { diff --git a/tools/perf/bench/mem-functions.c b/tools/perf/bench/mem-functions.c index 2908a3a796c9..5ede52853953 100644 --- a/tools/perf/bench/mem-functions.c +++ b/tools/perf/bench/mem-functions.c @@ -7,13 +7,14 @@ * Written by Hitoshi Mitake */ -#include "debug.h" +#include "bench.h" #include "../perf-sys.h" #include -#include "../util/header.h" -#include "../util/cloexec.h" -#include "../util/string2.h" -#include "bench.h" +#include "util/cloexec.h" +#include "util/debug.h" +#include "util/header.h" +#include "util/stat.h" +#include "util/string2.h" #include "mem-memcpy-arch.h" #include "mem-memset-arch.h" @@ -26,6 +27,7 @@ #include #include #include +#include #define K 1024 @@ -41,6 +43,7 @@ static unsigned int nr_loops = 1; static bool use_cycles; static int cycles_fd; static unsigned int seed; +static unsigned int nr_threads = 1; static const struct option bench_common_options[] = { OPT_STRING('s', "size", &size_str, "1MB", @@ -121,6 +124,8 @@ static struct perf_event_attr cycle_attr = { .config = PERF_COUNT_HW_CPU_CYCLES }; +static struct stats stats; + static int init_cycles(void) { cycles_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, perf_event_open_cloexec_flag()); @@ -174,18 +179,18 @@ static void clock_accum(union bench_clock *a, union bench_clock *b) static double timeval2double(struct timeval *ts) { - return (double)ts->tv_sec + (double)ts->tv_usec / (double)USEC_PER_SEC; + return ((double)ts->tv_sec + (double)ts->tv_usec / (double)USEC_PER_SEC) / nr_threads; } #define print_bps(x) do { \ if (x < K) \ - printf(" %14lf bytes/sec\n", x); \ + printf(" %14lf bytes/sec", x); \ else if (x < K * K) \ - printf(" %14lfd KB/sec\n", x / K); \ + printf(" %14lfd KB/sec", x / K); \ else if (x < K * K * K) \ - printf(" %14lf MB/sec\n", x / K / K); \ + printf(" %14lf MB/sec", x / K / K); \ else \ - printf(" %14lf GB/sec\n", x / K / K / K); \ + printf(" %14lf GB/sec", x / K / K / K); \ } while (0) static void __bench_mem_function(struct bench_mem_info *info, struct bench_params *p, @@ -196,6 +201,7 @@ static void __bench_mem_function(struct bench_mem_info *info, struct bench_param union bench_clock rt = { 0 }; void *src = NULL, *dst = NULL; + init_stats(&stats); printf("# function '%s' (%s)\n", r->name, r->desc); if (r->fn.init && r->fn.init(info, p, &src, &dst)) @@ -210,11 +216,16 @@ static void __bench_mem_function(struct bench_mem_info *info, struct bench_param switch (bench_format) { case BENCH_FORMAT_DEFAULT: if (use_cycles) { - printf(" %14lf cycles/byte\n", (double)rt.cycles/(double)p->size_total); + printf(" %14lf cycles/byte", (double)rt.cycles/(double)p->size_total); } else { result_bps = (double)p->size_total/timeval2double(&rt.tv); print_bps(result_bps); } + if (nr_threads > 1) { + printf("/thread\t( +- %6.2f%% )", + rel_stddev_stats(stddev_stats(&stats), avg_stats(&stats))); + } + printf("\n"); break; case BENCH_FORMAT_SIMPLE: @@ -388,7 +399,7 @@ static void mem_free(struct bench_mem_info *info __maybe_unused, *dst = *src = NULL; } -struct function memcpy_functions[] = { +static struct function memcpy_functions[] = { { .name = "default", .desc = "Default memcpy() provided by glibc", .fn.init = mem_alloc, @@ -494,16 +505,27 @@ static void mmap_page_touch(void *dst, size_t size, unsigned int page_shift, boo } } -static int do_mmap(const struct function *r, struct bench_params *p, - void *src __maybe_unused, void *dst __maybe_unused, - union bench_clock *accum) +struct mmap_data { + pthread_t id; + const struct function *func; + struct bench_params *params; + union bench_clock result; + unsigned int seed; + int error; +}; + +static void *do_mmap_thread(void *arg) { + struct mmap_data *data = arg; + const struct function *r = data->func; + struct bench_params *p = data->params; union bench_clock start, end, diff; mmap_op_t fn = r->fn.mmap_op; bool populate = strcmp(r->name, "populate") == 0; + void *dst; - if (p->seed) - srand(p->seed); + if (data->seed) + srand(data->seed); for (unsigned int i = 0; i < p->nr_loops; i++) { clock_get(&start); @@ -514,16 +536,59 @@ static int do_mmap(const struct function *r, struct bench_params *p, fn(dst, p->size, p->page_shift, p->seed); clock_get(&end); diff = clock_diff(&start, &end); - clock_accum(accum, &diff); + clock_accum(&data->result, &diff); bench_munmap(dst, p->size); } - return 0; + return data; out: - printf("# Memory allocation failed - maybe size (%s) %s?\n", size_str, - p->page_shift != PAGE_SHIFT_4KB ? "has insufficient hugepages" : "is too large"); - return -1; + data->error = -ENOMEM; + return NULL; +} + +static int do_mmap(const struct function *r, struct bench_params *p, + void *src __maybe_unused, void *dst __maybe_unused, + union bench_clock *accum) +{ + struct mmap_data *data; + int error = 0; + + data = calloc(nr_threads, sizeof(*data)); + if (!data) { + printf("# Failed to allocate thread resources\n"); + return -1; + } + + for (unsigned int i = 0; i < nr_threads; i++) { + data[i].func = r; + data[i].params = p; + if (p->seed) + data[i].seed = p->seed + i; + + if (pthread_create(&data[i].id, NULL, do_mmap_thread, &data[i]) < 0) + data[i].error = -errno; + } + + for (unsigned int i = 0; i < nr_threads; i++) { + union bench_clock *t = &data[i].result; + + pthread_join(data[i].id, NULL); + + clock_accum(accum, t); + if (use_cycles) + update_stats(&stats, t->cycles); + else + update_stats(&stats, t->tv.tv_sec * 1e6 + t->tv.tv_usec); + error |= data[i].error; + } + free(data); + + if (error) { + printf("# Memory allocation failed - maybe size (%s) %s?\n", size_str, + p->page_shift != PAGE_SHIFT_4KB ? "has insufficient hugepages" : "is too large"); + } + return error ? -1 : 0; } static const char * const bench_mem_mmap_usage[] = { @@ -548,6 +613,8 @@ int bench_mem_mmap(int argc, const char **argv) static const struct option bench_mmap_options[] = { OPT_UINTEGER('r', "randomize", &seed, "Seed to randomize page access offset."), + OPT_UINTEGER('t', "threads", &nr_threads, + "Number of threads to run concurrently (default: 1)."), OPT_PARENT(bench_common_options), OPT_END() }; diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 19be2aaf4dc0..42d7afc03f9b 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c @@ -32,7 +32,6 @@ #include #include #include -#include #include "../util/header.h" #include "../util/mutex.h" @@ -166,7 +165,7 @@ static struct global_info *g = NULL; static int parse_cpus_opt(const struct option *opt, const char *arg, int unset); static int parse_nodes_opt(const struct option *opt, const char *arg, int unset); -struct params p0; +static struct params p0; static const struct option options[] = { OPT_INTEGER('p', "nr_proc" , &p0.nr_proc, "number of processes"), @@ -980,10 +979,8 @@ static int count_process_nodes(int process_nr) int nodes; int n, t; - node_present = (char *)malloc(g->p.nr_nodes * sizeof(char)); + node_present = calloc(g->p.nr_nodes, sizeof(char)); BUG_ON(!node_present); - for (nodes = 0; nodes < g->p.nr_nodes; nodes++) - node_present[nodes] = 0; for (t = 0; t < g->p.nr_threads; t++) { struct thread_data *td; @@ -1090,10 +1087,8 @@ static void calc_convergence(double runtime_ns_max, double *convergence) if (!g->p.show_convergence && !g->p.measure_convergence) return; - nodes = (int *)malloc(g->p.nr_nodes * sizeof(int)); + nodes = calloc(g->p.nr_nodes, sizeof(int)); BUG_ON(!nodes); - for (node = 0; node < g->p.nr_nodes; node++) - nodes[node] = 0; loops_done_min = -1; loops_done_max = 0; @@ -1423,7 +1418,7 @@ static void worker_process(int process_nr) bind_to_memnode(td->bind_node); bind_to_cpumask(td->bind_cpumask); - pthreads = zalloc(g->p.nr_threads * sizeof(pthread_t)); + pthreads = calloc(g->p.nr_threads, sizeof(pthread_t)); process_data = setup_private_data(g->p.bytes_process); if (g->p.show_details >= 3) { @@ -1629,7 +1624,7 @@ static int __bench_numa(const char *name) if (init()) return -1; - pids = zalloc(g->p.nr_proc * sizeof(*pids)); + pids = calloc(g->p.nr_proc, sizeof(*pids)); pid = -1; if (g->p.serialize_startup) { diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c index 93dcd9dba3d0..4fb6657fc826 100644 --- a/tools/perf/bench/sched-messaging.c +++ b/tools/perf/bench/sched-messaging.c @@ -301,7 +301,7 @@ int bench_sched_messaging(int argc, const char **argv) argc = parse_options(argc, argv, options, bench_sched_message_usage, 0); - worker_tab = malloc(num_fds * 2 * num_groups * sizeof(union messaging_worker)); + worker_tab = calloc(num_fds * 2 * num_groups, sizeof(union messaging_worker)); if (!worker_tab) err(EXIT_FAILURE, "main:malloc()"); diff --git a/tools/perf/bench/uprobe.c b/tools/perf/bench/uprobe.c index c4dac868f1ee..89697ff788ef 100644 --- a/tools/perf/bench/uprobe.c +++ b/tools/perf/bench/uprobe.c @@ -58,7 +58,7 @@ static const char * const bench_uprobe_usage[] = { goto cleanup; \ } -struct bench_uprobe_bpf *skel; +static struct bench_uprobe_bpf *skel; static int bench_uprobe__setup_bpf_skel(enum bench_uprobe bench) { diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 9c27bb30b708..5e57b78548f4 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -13,7 +13,6 @@ #include #include "util/cache.h" #include -#include #include "util/symbol.h" #include "util/debug.h" @@ -313,15 +312,6 @@ out_put: return ret; } -static int process_feature_event(const struct perf_tool *tool __maybe_unused, - struct perf_session *session, - union perf_event *event) -{ - if (event->feat.feat_id < HEADER_LAST_FEATURE) - return perf_event__process_feature(session, event); - return 0; -} - static int hist_entry__stdio_annotate(struct hist_entry *he, struct evsel *evsel, struct perf_annotate *ann) @@ -744,8 +734,7 @@ int cmd_annotate(int argc, const char **argv) &annotate.group_set, "Show event group information together"), OPT_STRING('C', "cpu", &annotate.cpu_list, "cpu", "list of cpus to profile"), - OPT_CALLBACK(0, "symfs", NULL, "directory", - "Look for files with symbols relative to this directory", + OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP, symbol__config_symfs), OPT_BOOLEAN(0, "source", &annotate_opts.annotate_src, "Interleave source code with assembly code (default)"), @@ -876,7 +865,7 @@ int cmd_annotate(int argc, const char **argv) annotate.tool.id_index = perf_event__process_id_index; annotate.tool.auxtrace_info = perf_event__process_auxtrace_info; annotate.tool.auxtrace = perf_event__process_auxtrace; - annotate.tool.feature = process_feature_event; + annotate.tool.feature = perf_event__process_feature; annotate.tool.ordering_requires_timestamps = true; annotate.session = perf_session__new(&data, &annotate.tool); diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index 02dea1b88228..02d47913cc6a 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -37,14 +37,14 @@ struct bench { }; #ifdef HAVE_LIBNUMA_SUPPORT -static struct bench numa_benchmarks[] = { +static const struct bench numa_benchmarks[] = { { "mem", "Benchmark for NUMA workloads", bench_numa }, { "all", "Run all NUMA benchmarks", NULL }, { NULL, NULL, NULL } }; #endif -static struct bench sched_benchmarks[] = { +static const struct bench sched_benchmarks[] = { { "messaging", "Benchmark for scheduling and IPC", bench_sched_messaging }, { "pipe", "Benchmark for pipe() between two processes", bench_sched_pipe }, { "seccomp-notify", "Benchmark for seccomp user notify", bench_sched_seccomp_notify}, @@ -52,7 +52,7 @@ static struct bench sched_benchmarks[] = { { NULL, NULL, NULL } }; -static struct bench syscall_benchmarks[] = { +static const struct bench syscall_benchmarks[] = { { "basic", "Benchmark for basic getppid(2) calls", bench_syscall_basic }, { "getpgid", "Benchmark for getpgid(2) calls", bench_syscall_getpgid }, { "fork", "Benchmark for fork(2) calls", bench_syscall_fork }, @@ -61,7 +61,7 @@ static struct bench syscall_benchmarks[] = { { NULL, NULL, NULL }, }; -static struct bench mem_benchmarks[] = { +static const struct bench mem_benchmarks[] = { { "memcpy", "Benchmark for memcpy() functions", bench_mem_memcpy }, { "memset", "Benchmark for memset() functions", bench_mem_memset }, { "find_bit", "Benchmark for find_bit() functions", bench_mem_find_bit }, @@ -70,7 +70,7 @@ static struct bench mem_benchmarks[] = { { NULL, NULL, NULL } }; -static struct bench futex_benchmarks[] = { +static const struct bench futex_benchmarks[] = { { "hash", "Benchmark for futex hash table", bench_futex_hash }, { "wake", "Benchmark for futex wake calls", bench_futex_wake }, { "wake-parallel", "Benchmark for parallel futex wake calls", bench_futex_wake_parallel }, @@ -82,7 +82,7 @@ static struct bench futex_benchmarks[] = { }; #ifdef HAVE_EVENTFD_SUPPORT -static struct bench epoll_benchmarks[] = { +static const struct bench epoll_benchmarks[] = { { "wait", "Benchmark epoll concurrent epoll_waits", bench_epoll_wait }, { "ctl", "Benchmark epoll concurrent epoll_ctls", bench_epoll_ctl }, { "all", "Run all futex benchmarks", NULL }, @@ -90,7 +90,7 @@ static struct bench epoll_benchmarks[] = { }; #endif // HAVE_EVENTFD_SUPPORT -static struct bench internals_benchmarks[] = { +static const struct bench internals_benchmarks[] = { { "synthesize", "Benchmark perf event synthesis", bench_synthesize }, { "kallsyms-parse", "Benchmark kallsyms parsing", bench_kallsyms_parse }, { "inject-build-id", "Benchmark build-id injection", bench_inject_build_id }, @@ -99,14 +99,14 @@ static struct bench internals_benchmarks[] = { { NULL, NULL, NULL } }; -static struct bench breakpoint_benchmarks[] = { +static const struct bench breakpoint_benchmarks[] = { { "thread", "Benchmark thread start/finish with breakpoints", bench_breakpoint_thread}, { "enable", "Benchmark breakpoint enable/disable", bench_breakpoint_enable}, { "all", "Run all breakpoint benchmarks", NULL}, { NULL, NULL, NULL }, }; -static struct bench uprobe_benchmarks[] = { +static const struct bench uprobe_benchmarks[] = { { "baseline", "Baseline libc usleep(1000) call", bench_uprobe_baseline, }, { "empty", "Attach empty BPF prog to uprobe on usleep, system wide", bench_uprobe_empty, }, { "trace_printk", "Attach trace_printk BPF prog to uprobe on usleep syswide", bench_uprobe_trace_printk, }, @@ -116,12 +116,12 @@ static struct bench uprobe_benchmarks[] = { }; struct collection { - const char *name; - const char *summary; - struct bench *benchmarks; + const char *name; + const char *summary; + const struct bench *benchmarks; }; -static struct collection collections[] = { +static const struct collection collections[] = { { "sched", "Scheduler and IPC benchmarks", sched_benchmarks }, { "syscall", "System call benchmarks", syscall_benchmarks }, { "mem", "Memory access benchmarks", mem_benchmarks }, @@ -147,9 +147,9 @@ static struct collection collections[] = { #define for_each_bench(coll, bench) \ for (bench = coll->benchmarks; bench && bench->name; bench++) -static void dump_benchmarks(struct collection *coll) +static void dump_benchmarks(const struct collection *coll) { - struct bench *bench; + const struct bench *bench; printf("\n # List of available benchmarks for collection '%s':\n\n", coll->name); @@ -178,7 +178,7 @@ static const char * const bench_usage[] = { static void print_usage(void) { - struct collection *coll; + const struct collection *coll; int i; printf("Usage: \n"); @@ -234,9 +234,9 @@ static int run_bench(const char *coll_name, const char *bench_name, bench_fn_t f return ret; } -static void run_collection(struct collection *coll) +static void run_collection(const struct collection *coll) { - struct bench *bench; + const struct bench *bench; const char *argv[2]; argv[1] = NULL; @@ -260,7 +260,7 @@ static void run_collection(struct collection *coll) static void run_all_collections(void) { - struct collection *coll; + const struct collection *coll; for_each_collection(coll) run_collection(coll); @@ -268,7 +268,7 @@ static void run_all_collections(void) int cmd_bench(int argc, const char **argv) { - struct collection *coll; + const struct collection *coll; int ret = 0; /* Unbuffered output */ @@ -306,7 +306,7 @@ int cmd_bench(int argc, const char **argv) } for_each_collection(coll) { - struct bench *bench; + const struct bench *bench; if (strcmp(coll->name, argv[0])) continue; diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index d390ae4e3ec8..72a7802775ee 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -155,7 +155,7 @@ static void *c2c_he_zalloc(size_t size) if (!c2c_he->nodeset) goto out_free; - c2c_he->node_stats = zalloc(c2c.nodes_cnt * sizeof(*c2c_he->node_stats)); + c2c_he->node_stats = calloc(c2c.nodes_cnt, sizeof(*c2c_he->node_stats)); if (!c2c_he->node_stats) goto out_free; @@ -2310,7 +2310,6 @@ static int setup_nodes(struct perf_session *session) { struct numa_node *n; unsigned long **nodes; - int node, idx; struct perf_cpu cpu; int *cpu2node; struct perf_env *env = perf_session__env(session); @@ -2325,24 +2324,25 @@ static int setup_nodes(struct perf_session *session) if (!n) return -EINVAL; - nodes = zalloc(sizeof(unsigned long *) * c2c.nodes_cnt); + nodes = calloc(c2c.nodes_cnt, sizeof(unsigned long *)); if (!nodes) return -ENOMEM; c2c.nodes = nodes; - cpu2node = zalloc(sizeof(int) * c2c.cpus_cnt); + cpu2node = calloc(c2c.cpus_cnt, sizeof(int)); if (!cpu2node) return -ENOMEM; - for (idx = 0; idx < c2c.cpus_cnt; idx++) + for (int idx = 0; idx < c2c.cpus_cnt; idx++) cpu2node[idx] = -1; c2c.cpu2node = cpu2node; - for (node = 0; node < c2c.nodes_cnt; node++) { + for (int node = 0; node < c2c.nodes_cnt; node++) { struct perf_cpu_map *map = n[node].map; unsigned long *set; + unsigned int idx; set = bitmap_zalloc(c2c.cpus_cnt); if (!set) @@ -2892,9 +2892,10 @@ static int ui_quirks(void) #define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent" -const char callchain_help[] = "Display call graph (stack chain/backtrace):\n\n" - CALLCHAIN_REPORT_HELP - "\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT; +static const char callchain_help[] = + "Display call graph (stack chain/backtrace):\n\n" + CALLCHAIN_REPORT_HELP + "\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT; static int parse_callchain_opt(const struct option *opt, const char *arg, int unset) diff --git a/tools/perf/builtin-config.c b/tools/perf/builtin-config.c index 45b5312fbe83..237600643bbd 100644 --- a/tools/perf/builtin-config.c +++ b/tools/perf/builtin-config.c @@ -23,7 +23,7 @@ static const char * const config_usage[] = { NULL }; -enum actions { +static enum actions { ACTION_LIST = 1 } actions; diff --git a/tools/perf/builtin-daemon.c b/tools/perf/builtin-daemon.c index 33473e071392..c4632577d129 100644 --- a/tools/perf/builtin-daemon.c +++ b/tools/perf/builtin-daemon.c @@ -1016,7 +1016,7 @@ static int setup_config_changes(struct daemon *daemon) { char *basen = strdup(daemon->config_real); char *dirn = strdup(daemon->config_real); - char *base, *dir; + const char *base, *dir; int fd, wd = -1; if (!dirn || !basen) @@ -1029,7 +1029,7 @@ static int setup_config_changes(struct daemon *daemon) } dir = dirname(dirn); - base = basename(basen); + base = perf_basename(basen); pr_debug("config file: %s, dir: %s\n", base, dir); wd = inotify_add_watch(fd, dir, IN_CLOSE_WRITE); diff --git a/tools/perf/builtin-data.c b/tools/perf/builtin-data.c index 85f59886b5cf..4c08ccb8c06b 100644 --- a/tools/perf/builtin-data.c +++ b/tools/perf/builtin-data.c @@ -28,15 +28,15 @@ static const char *data_usage[] = { NULL }; -const char *to_json; -const char *to_ctf; -struct perf_data_convert_opts opts = { +static const char *to_json; +static const char *to_ctf; +static struct perf_data_convert_opts opts = { .force = false, .all = false, .time_str = NULL, }; -const struct option data_options[] = { +static const struct option data_options[] = { OPT_INCR('v', "verbose", &verbose, "be more verbose"), OPT_STRING('i', "input", &input_name, "file", "input file name"), OPT_STRING(0, "to-json", &to_json, NULL, "Convert to JSON format"), diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 59bf1f72d12e..1b3df868849a 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -113,7 +113,7 @@ enum { COMPUTE_STREAM, /* After COMPUTE_MAX to avoid use current compute arrays */ }; -const char *compute_names[COMPUTE_MAX] = { +static const char *compute_names[COMPUTE_MAX] = { [COMPUTE_DELTA] = "delta", [COMPUTE_DELTA_ABS] = "delta-abs", [COMPUTE_RATIO] = "ratio", @@ -382,7 +382,7 @@ static void block_hist_free(void *he) free(bh); } -struct hist_entry_ops block_hist_ops = { +static struct hist_entry_ops block_hist_ops = { .new = block_hist_zalloc, .free = block_hist_free, }; @@ -1280,8 +1280,7 @@ static const struct option options[] = { OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator", "separator for columns, no spaces will be added between " "columns '.' is reserved."), - OPT_CALLBACK(0, "symfs", NULL, "directory", - "Look for files with symbols relative to this directory", + OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP, symbol__config_symfs), OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."), OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", @@ -1353,7 +1352,7 @@ static int cycles_printf(struct hist_entry *he, struct hist_entry *pair, /* * Avoid printing the warning "addr2line_init failed for ..." */ - symbol_conf.disable_add2line_warn = true; + symbol_conf.addr2line_disable_warn = true; bi = block_he->block_info; @@ -1892,7 +1891,7 @@ static int data_init(int argc, const char **argv) return -EINVAL; } - data__files = zalloc(sizeof(*data__files) * data__files_cnt); + data__files = calloc(data__files_cnt, sizeof(*data__files)); if (!data__files) return -ENOMEM; @@ -1987,7 +1986,7 @@ int cmd_diff(int argc, const char **argv) if (compute == COMPUTE_STREAM) { symbol_conf.show_branchflag_count = true; - symbol_conf.disable_add2line_warn = true; + symbol_conf.addr2line_disable_warn = true; callchain_param.mode = CHAIN_FLAT; callchain_param.key = CCKEY_SRCLINE; callchain_param.branch_callstack = 1; diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index 4cc33452d79b..8a7dbfb14535 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "debug.h" diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 5b29f4296861..f174bc69cec4 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -133,7 +133,7 @@ struct perf_inject { struct perf_file_section secs[HEADER_FEAT_BITS]; struct guest_session guest_session; struct strlist *known_build_ids; - const struct evsel *mmap_evsel; + struct evsel *mmap_evsel; struct ip_callchain *raw_callchain; }; @@ -270,9 +270,8 @@ static s64 perf_event__repipe_auxtrace(const struct perf_tool *tool, inject->have_auxtrace = true; if (!inject->output.is_pipe) { - off_t offset; + off_t offset = perf_data__seek(&inject->output, 0, SEEK_CUR); - offset = lseek(inject->output.file.fd, 0, SEEK_CUR); if (offset == -1) return -errno; ret = auxtrace_index__auxtrace_event(&session->auxtrace_index, @@ -519,7 +518,7 @@ static struct dso *findnew_dso(int pid, int tid, const char *filename, * processing mmap events. If not stashed, search the evlist for the first mmap * gathering event. */ -static const struct evsel *inject__mmap_evsel(struct perf_inject *inject) +static struct evsel *inject__mmap_evsel(struct perf_inject *inject) { struct evsel *pos; @@ -1023,7 +1022,6 @@ int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *e sample__for_each_callchain_node(thread, evsel, sample, PERF_MAX_STACK_DEPTH, /*symbols=*/false, mark_dso_hit_callback, &args); - thread__put(thread); repipe: perf_event__repipe(tool, event, sample, machine); @@ -1087,6 +1085,7 @@ static int perf_inject__sched_stat(const struct perf_tool *tool, struct perf_sample sample_sw; struct perf_inject *inject = container_of(tool, struct perf_inject, tool); u32 pid = evsel__intval(evsel, sample, "pid"); + int ret; list_for_each_entry(ent, &inject->samples, node) { if (pid == ent->tid) @@ -1103,7 +1102,9 @@ found: perf_event__synthesize_sample(event_sw, evsel->core.attr.sample_type, evsel->core.attr.read_format, &sample_sw); build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); - return perf_event__repipe(tool, event_sw, &sample_sw, machine); + ret = perf_event__repipe(tool, event_sw, &sample_sw, machine); + perf_sample__exit(&sample_sw); + return ret; } #endif @@ -1429,6 +1430,7 @@ static int synthesize_build_id(struct perf_inject *inject, struct dso *dso, pid_ { struct machine *machine = perf_session__findnew_machine(inject->session, machine_pid); struct perf_sample synth_sample = { + .evsel = inject__mmap_evsel(inject), .pid = -1, .tid = -1, .time = -1, @@ -1648,6 +1650,7 @@ static int guest_session__fetch(struct guest_session *gs) size_t hdr_sz = sizeof(*hdr); ssize_t ret; + perf_sample__init(&gs->ev.sample, /*all=*/false); buf = gs->ev.event_buf; if (!buf) { buf = malloc(PERF_SAMPLE_MAX_SIZE); @@ -1745,18 +1748,24 @@ static int guest_session__inject_events(struct guest_session *gs, u64 timestamp) if (!gs->fetched) { ret = guest_session__fetch(gs); if (ret) - return ret; + break; gs->fetched = true; } ev = gs->ev.event; sample = &gs->ev.sample; - if (!ev->header.size) - return 0; /* EOF */ - - if (sample->time > timestamp) - return 0; + if (!ev->header.size) { + /* EOF */ + perf_sample__exit(&gs->ev.sample); + gs->fetched = false; + ret = 0; + break; + } + if (sample->time > timestamp) { + ret = 0; + break; + } /* Change cpumode to guest */ cpumode = ev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; @@ -1779,12 +1788,14 @@ static int guest_session__inject_events(struct guest_session *gs, u64 timestamp) if (id_hdr_size & 7) { pr_err("Bad id_hdr_size %u\n", id_hdr_size); - return -EINVAL; + ret = -EINVAL; + break; } if (ev->header.size & 7) { pr_err("Bad event size %u\n", ev->header.size); - return -EINVAL; + ret = -EINVAL; + break; } /* Remove guest id sample */ @@ -1792,14 +1803,16 @@ static int guest_session__inject_events(struct guest_session *gs, u64 timestamp) if (ev->header.size & 7) { pr_err("Bad raw event size %u\n", ev->header.size); - return -EINVAL; + ret = -EINVAL; + break; } guest_id = guest_session__lookup_id(gs, id); if (!guest_id) { pr_err("Guest event with unknown id %llu\n", (unsigned long long)id); - return -EINVAL; + ret = -EINVAL; + break; } /* Change to host ID to avoid conflicting ID values */ @@ -1819,19 +1832,28 @@ static int guest_session__inject_events(struct guest_session *gs, u64 timestamp) /* New id sample with new ID and CPU */ ret = evlist__append_id_sample(inject->session->evlist, ev, sample); if (ret) - return ret; + break; if (ev->header.size & 7) { pr_err("Bad new event size %u\n", ev->header.size); - return -EINVAL; + ret = -EINVAL; + break; } - gs->fetched = false; - ret = output_bytes(inject, ev, ev->header.size); if (ret) - return ret; + break; + + /* Reset for next guest session event fetch. */ + perf_sample__exit(sample); + gs->fetched = false; } + if (ret && gs->fetched) { + /* Clear saved sample state on error. */ + perf_sample__exit(&gs->ev.sample); + gs->fetched = false; + } + return ret; } static int guest_session__flush_events(struct guest_session *gs) @@ -2134,6 +2156,7 @@ static bool keep_feat(struct perf_inject *inject, int feat) case HEADER_HYBRID_TOPOLOGY: case HEADER_PMU_CAPS: case HEADER_CPU_DOMAIN_INFO: + case HEADER_CLN_SIZE: return true; /* Information that can be updated */ case HEADER_BUILD_ID: @@ -2479,12 +2502,12 @@ int cmd_inject(int argc, const char **argv) .output = { .path = "-", .mode = PERF_DATA_MODE_WRITE, - .use_stdio = true, + .file.use_stdio = true, }, }; struct perf_data data = { .mode = PERF_DATA_MODE_READ, - .use_stdio = true, + .file.use_stdio = true, }; int ret; const char *known_build_ids = NULL; diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 7929a5fa5f46..9c64a0d74823 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -82,7 +82,7 @@ static unsigned long nr_allocs, nr_cross_allocs; /* filters for controlling start and stop of time of analysis */ static struct perf_time_interval ptime; -const char *time_str; +static const char *time_str; static int insert_alloc_stat(unsigned long call_site, unsigned long ptr, int bytes_req, int bytes_alloc, int cpu) diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index 7f3068264568..9d3a4c779a41 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -985,7 +985,7 @@ static int process_irq_handler_exit_event(const struct perf_tool *tool, return 0; } -const struct evsel_str_handler irq_tp_handlers[] = { +static const struct evsel_str_handler irq_tp_handlers[] = { { "irq:irq_handler_entry", process_irq_handler_entry_event, }, { "irq:irq_handler_exit", process_irq_handler_exit_event, }, }; @@ -1080,7 +1080,7 @@ static int process_softirq_exit_event(const struct perf_tool *tool, return 0; } -const struct evsel_str_handler softirq_tp_handlers[] = { +static const struct evsel_str_handler softirq_tp_handlers[] = { { "irq:softirq_raise", process_softirq_raise_event, }, { "irq:softirq_entry", process_softirq_entry_event, }, { "irq:softirq_exit", process_softirq_exit_event, }, @@ -1211,7 +1211,7 @@ static int process_workqueue_execute_end_event(const struct perf_tool *tool, return 0; } -const struct evsel_str_handler workqueue_tp_handlers[] = { +static const struct evsel_str_handler workqueue_tp_handlers[] = { { "workqueue:workqueue_activate_work", process_workqueue_activate_work_event, }, { "workqueue:workqueue_execute_start", process_workqueue_execute_start_event, }, { "workqueue:workqueue_execute_end", process_workqueue_execute_end_event, }, @@ -1281,7 +1281,7 @@ static int process_sched_switch_event(const struct perf_tool *tool, return 0; } -const struct evsel_str_handler sched_tp_handlers[] = { +static const struct evsel_str_handler sched_tp_handlers[] = { { "sched:sched_switch", process_sched_switch_event, }, }; @@ -1561,13 +1561,13 @@ static void print_bad_events(struct perf_kwork *kwork) } } -const char *graph_load = "||||||||||||||||||||||||||||||||||||||||||||||||"; -const char *graph_idle = " "; static void top_print_per_cpu_load(struct perf_kwork *kwork) { int i, load_width; u64 total, load, load_ratio; struct kwork_top_stat *stat = &kwork->top_stat; + const char *graph_load = "||||||||||||||||||||||||||||||||||||||||||||||||"; + const char *graph_idle = " "; for (i = 0; i < MAX_NR_CPUS; i++) { total = stat->cpus_runtime[i].total; @@ -2208,7 +2208,7 @@ static int perf_kwork__top(struct perf_kwork *kwork) struct __top_cpus_runtime *cpus_runtime; int ret = 0; - cpus_runtime = zalloc(sizeof(struct __top_cpus_runtime) * (MAX_NR_CPUS + 1)); + cpus_runtime = calloc(MAX_NR_CPUS + 1, sizeof(struct __top_cpus_runtime)); if (!cpus_runtime) return -1; @@ -2423,8 +2423,8 @@ int cmd_kwork(int argc, const char **argv) "Display call chains if present"), OPT_UINTEGER(0, "max-stack", &kwork.max_stack, "Maximum number of functions to display backtrace."), - OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", - "Look for files with symbols relative to this directory"), + OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP, + symbol__config_symfs), OPT_STRING(0, "time", &kwork.time_str, "str", "Time span for analysis (start,stop)"), OPT_STRING('C', "cpu", &kwork.cpu_list, "cpu", diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index e8962c985d34..5585aeb97684 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -2250,7 +2250,7 @@ static int parse_map_entry(const struct option *opt, const char *str, static int parse_max_stack(const struct option *opt, const char *str, int unset __maybe_unused) { - unsigned long *len = (unsigned long *)opt->value; + int *len = opt->value; long val; char *endptr; diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 1b4ba85ee019..a67b565278ae 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -597,8 +597,8 @@ __cmd_probe(int argc, const char **argv) OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, "Enable kernel symbol demangling"), OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"), - OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", - "Look for files with symbols relative to this directory"), + OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP, + symbol__config_symfs), OPT_CALLBACK(0, "target-ns", NULL, "pid", "target pid for namespace contexts", opt_set_target_ns), OPT_BOOLEAN(0, "bootconfig", &probe_conf.bootconfig, diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 60d764068302..4a5eba498c02 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -40,7 +40,6 @@ #include "util/perf_api_probe.h" #include "util/trigger.h" #include "util/perf-hooks.h" -#include "util/cpu-set-sched.h" #include "util/synthetic-events.h" #include "util/time-utils.h" #include "util/units.h" @@ -56,6 +55,7 @@ #include "asm/bug.h" #include "perf.h" #include "cputopo.h" +#include "dwarf-regs.h" #include #include @@ -453,7 +453,7 @@ static int record__aio_pushfn(struct mmap *map, void *to, void *buf, size_t size static int record__aio_push(struct record *rec, struct mmap *map, off_t *off) { int ret, idx; - int trace_fd = rec->session->data->file.fd; + int trace_fd = perf_data__fd(rec->session->data); struct record_aio aio = { .rec = rec, .size = 0 }; /* @@ -1070,12 +1070,12 @@ static int record__thread_data_init_maps(struct record_thread *thread_data, stru thread_data->nr_mmaps = bitmap_weight(thread_data->mask->maps.bits, thread_data->mask->maps.nbits); if (mmap) { - thread_data->maps = zalloc(thread_data->nr_mmaps * sizeof(struct mmap *)); + thread_data->maps = calloc(thread_data->nr_mmaps, sizeof(struct mmap *)); if (!thread_data->maps) return -ENOMEM; } if (overwrite_mmap) { - thread_data->overwrite_maps = zalloc(thread_data->nr_mmaps * sizeof(struct mmap *)); + thread_data->overwrite_maps = calloc(thread_data->nr_mmaps, sizeof(struct mmap *)); if (!thread_data->overwrite_maps) { zfree(&thread_data->maps); return -ENOMEM; @@ -1220,7 +1220,7 @@ static int record__alloc_thread_data(struct record *rec, struct evlist *evlist) int t, ret; struct record_thread *thread_data; - rec->thread_data = zalloc(rec->nr_threads * sizeof(*(rec->thread_data))); + rec->thread_data = calloc(rec->nr_threads, sizeof(*(rec->thread_data))); if (!rec->thread_data) { pr_err("Failed to allocate thread data\n"); return -ENOMEM; @@ -1640,7 +1640,7 @@ static int record__mmap_read_evlist(struct record *rec, struct evlist *evlist, int rc = 0; int nr_mmaps; struct mmap **maps; - int trace_fd = rec->data.file.fd; + int trace_fd = perf_data__fd(&rec->data); off_t off = 0; if (!evlist) @@ -1845,10 +1845,12 @@ record__finish_output(struct record *rec) } rec->session->header.data_size += rec->bytes_written; - data->file.size = lseek(perf_data__fd(data), 0, SEEK_CUR); + data->file.size = perf_data__seek(data, 0, SEEK_CUR); if (record__threads_enabled(rec)) { - for (i = 0; i < data->dir.nr; i++) - data->dir.files[i].size = lseek(data->dir.files[i].fd, 0, SEEK_CUR); + for (i = 0; i < data->dir.nr; i++) { + data->dir.files[i].size = + perf_data_file__seek(&data->dir.files[i], 0, SEEK_CUR); + } } /* Buildid scanning disabled or build ID in kernel and synthesized map events. */ @@ -2976,65 +2978,32 @@ out_delete_session: return status; } -static void callchain_debug(struct callchain_param *callchain) -{ - static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF", "LBR" }; - - pr_debug("callchain: type %s\n", str[callchain->record_mode]); - - if (callchain->record_mode == CALLCHAIN_DWARF) - pr_debug("callchain: stack dump size %d\n", - callchain->dump_size); -} - -int record_opts__parse_callchain(struct record_opts *record, - struct callchain_param *callchain, - const char *arg, bool unset) -{ - int ret; - callchain->enabled = !unset; - - /* --no-call-graph */ - if (unset) { - callchain->record_mode = CALLCHAIN_NONE; - pr_debug("callchain: disabled\n"); - return 0; - } - - ret = parse_callchain_record_opt(arg, callchain); - if (!ret) { - /* Enable data address sampling for DWARF unwind. */ - if (callchain->record_mode == CALLCHAIN_DWARF && - !record->record_data_mmap_set) - record->record_data_mmap = true; - callchain_debug(callchain); - } - - return ret; -} - -int record_parse_callchain_opt(const struct option *opt, +static int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset) { return record_opts__parse_callchain(opt->value, &callchain_param, arg, unset); } -int record_callchain_opt(const struct option *opt, - const char *arg __maybe_unused, - int unset __maybe_unused) +static int record_callchain_opt(const struct option *opt, + const char *arg __maybe_unused, + int unset) { - struct callchain_param *callchain = opt->value; + /* + * The -g option only sets the callchain if not already configured by + * .perfconfig. It does, however, enable it. + */ + if (callchain_param.record_mode != CALLCHAIN_NONE) { + callchain_param.enabled = true; + return 0; + } - callchain->enabled = true; - - if (callchain->record_mode == CALLCHAIN_NONE) - callchain->record_mode = CALLCHAIN_FP; - - callchain_debug(callchain); - return 0; + return record_opts__parse_callchain(opt->value, &callchain_param, + EM_HOST != EM_S390 ? "fp" : "dwarf", + unset); } + static int perf_record_config(const char *var, const char *value, void *cb) { struct record *rec = cb; @@ -3526,7 +3495,7 @@ static struct option __record_options[] = { OPT_CALLBACK(0, "mmap-flush", &record.opts, "number", "Minimal number of bytes that is extracted from mmap data pages (default: 1)", record__mmap_flush_parse), - OPT_CALLBACK_NOOPT('g', NULL, &callchain_param, + OPT_CALLBACK_NOOPT('g', NULL, &record.opts, NULL, "enables call-graph recording" , &record_callchain_opt), OPT_CALLBACK(0, "call-graph", &record.opts, @@ -3696,7 +3665,7 @@ struct option *record_options = __record_options; static int record__mmap_cpu_mask_init(struct mmap_cpu_mask *mask, struct perf_cpu_map *cpus) { struct perf_cpu cpu; - int idx; + unsigned int idx; if (cpu_map__is_dummy(cpus)) return 0; @@ -3743,7 +3712,7 @@ static int record__alloc_thread_masks(struct record *rec, int nr_threads, int nr { int t, ret; - rec->thread_masks = zalloc(nr_threads * sizeof(*(rec->thread_masks))); + rec->thread_masks = calloc(nr_threads, sizeof(*(rec->thread_masks))); if (!rec->thread_masks) { pr_err("Failed to allocate thread masks\n"); return -ENOMEM; @@ -3953,7 +3922,7 @@ static int record__init_thread_numa_masks(struct record *rec, struct perf_cpu_ma return -ENOMEM; } - spec = zalloc(topo->nr * sizeof(char *)); + spec = calloc(topo->nr, sizeof(char *)); if (!spec) { pr_err("Failed to allocate NUMA spec\n"); ret = -ENOMEM; @@ -4131,8 +4100,11 @@ int cmd_record(int argc, const char **argv) perf_debuginfod_setup(&record.debuginfod); - /* Make system wide (-a) the default target. */ - if (!argc && target__none(&rec->opts.target)) + /* + * Use system wide (-a) for the default target (ie. when no + * workload). User ID filtering also implies system-wide. + */ + if ((!argc && target__none(&rec->opts.target)) || rec->uid_str) rec->opts.target.system_wide = true; if (nr_cgroups && !rec->opts.target.system_wide) { @@ -4310,7 +4282,8 @@ int cmd_record(int argc, const char **argv) record.opts.tail_synthesize = true; if (rec->evlist->core.nr_entries == 0) { - struct evlist *def_evlist = evlist__new_default(); + struct evlist *def_evlist = evlist__new_default(&rec->opts.target, + callchain_param.enabled); if (!def_evlist) goto out; @@ -4339,9 +4312,6 @@ int cmd_record(int argc, const char **argv) err = parse_uid_filter(rec->evlist, uid); if (err) goto out; - - /* User ID filtering implies system wide. */ - rec->opts.target.system_wide = true; } /* Enable ignoring missing threads when -p option is defined. */ diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3b81f4b3dc49..95c0bdba6b11 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -245,25 +245,20 @@ static int process_feature_event(const struct perf_tool *tool, union perf_event *event) { struct report *rep = container_of(tool, struct report, tool); + int ret = perf_event__process_feature(tool, session, event); - if (event->feat.feat_id < HEADER_LAST_FEATURE) - return perf_event__process_feature(session, event); + if (ret == 0 && event->header.size == sizeof(struct perf_record_header_feature) && + (int)event->feat.feat_id >= session->header.last_feat) { + /* + * (feat_id = HEADER_LAST_FEATURE) is the end marker which means + * all features are received. + */ + if (rep->header_only) + session_done = 1; - if (event->feat.feat_id != HEADER_LAST_FEATURE) { - pr_err("failed: wrong feature ID: %" PRI_lu64 "\n", - event->feat.feat_id); - return -1; - } else if (rep->header_only) { - session_done = 1; + setup_forced_leader(rep, session->evlist); } - - /* - * (feat_id = HEADER_LAST_FEATURE) is the end marker which - * means all features are received, now we can force the - * group if needed. - */ - setup_forced_leader(rep, session->evlist); - return 0; + return ret; } static int process_sample_event(const struct perf_tool *tool, @@ -1416,8 +1411,7 @@ int cmd_report(int argc, const char **argv) "columns '.' is reserved."), OPT_BOOLEAN('U', "hide-unresolved", &symbol_conf.hide_unresolved, "Only display entries resolved to a symbol"), - OPT_CALLBACK(0, "symfs", NULL, "directory", - "Look for files with symbols relative to this directory", + OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP, symbol__config_symfs), OPT_STRING('C', "cpu", &report.cpu_list, "cpu", "list of cpus to profile"), diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 3f509cfdd58c..555247568e7a 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2405,7 +2405,7 @@ static int init_idle_threads(int ncpu) { int i, ret; - idle_threads = zalloc(ncpu * sizeof(struct thread *)); + idle_threads = calloc(ncpu, sizeof(struct thread *)); if (!idle_threads) return -ENOMEM; @@ -3483,7 +3483,7 @@ static int setup_cpus_switch_event(struct perf_sched *sched) if (!sched->cpu_last_switched) return -1; - sched->curr_pid = malloc(MAX_CPUS * sizeof(*(sched->curr_pid))); + sched->curr_pid = calloc(MAX_CPUS, sizeof(*(sched->curr_pid))); if (!sched->curr_pid) { zfree(&sched->cpu_last_switched); return -1; @@ -3559,7 +3559,7 @@ static int setup_map_cpus(struct perf_sched *sched) sched->max_cpu.cpu = sysconf(_SC_NPROCESSORS_CONF); if (sched->map.comp) { - sched->map.comp_cpus = zalloc(sched->max_cpu.cpu * sizeof(int)); + sched->map.comp_cpus = calloc(sched->max_cpu.cpu, sizeof(int)); if (!sched->map.comp_cpus) return -1; } @@ -4879,8 +4879,8 @@ int cmd_sched(int argc, const char **argv) "Display call chains if present (default on)"), OPT_UINTEGER(0, "max-stack", &sched.max_stack, "Maximum number of functions to display backtrace."), - OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", - "Look for files with symbols relative to this directory"), + OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP, + symbol__config_symfs), OPT_BOOLEAN('s', "summary", &sched.summary_only, "Show only syscall summary with statistics"), OPT_BOOLEAN('S', "with-summary", &sched.summary, @@ -4955,6 +4955,7 @@ int cmd_sched(int argc, const char **argv) .switch_event = replay_switch_event, .fork_event = replay_fork_event, }; + struct trace_sched_handler stats_ops = {}; int ret; perf_tool__init(&sched.tool, /*ordered_events=*/true); @@ -5037,6 +5038,7 @@ int cmd_sched(int argc, const char **argv) } else if (!strcmp(argv[0], "stats")) { const char *const stats_subcommands[] = {"record", "report", NULL}; + sched.tp_handler = &stats_ops; argc = parse_options_subcommand(argc, argv, stats_options, stats_subcommands, stats_usage, diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 7c743a303507..c8ac9f01a36b 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -166,7 +166,7 @@ struct perf_script { int range_num; }; -struct output_option { +static struct output_option { const char *str; enum perf_output_field field; } all_output_options[] = { @@ -1271,11 +1271,11 @@ static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, if (PRINT_FIELD(BRCNTR)) { struct evsel *pos = evsel__leader(evsel); - unsigned int i = 0, j, num, mask, width; + unsigned int i = 0, j, num, mask, width, numprinted = 0; perf_env__find_br_cntr_info(evsel__env(evsel), NULL, &width); mask = (1L << width) - 1; - printed += fprintf(fp, "br_cntr: "); + printed += fprintf(fp, "\t# br_cntr: "); evlist__for_each_entry_from(evsel->evlist, pos) { if (!(pos->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS)) continue; @@ -1283,16 +1283,20 @@ static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, break; num = (br_cntr >> (i++ * width)) & mask; + numprinted += num; if (!verbose) { for (j = 0; j < num; j++) printed += fprintf(fp, "%s", pos->abbr_name); } else printed += fprintf(fp, "%s %d ", pos->name, num); } - printed += fprintf(fp, "\t"); + if (numprinted == 0 && !verbose) + printed += fprintf(fp, "-"); + printed += fprintf(fp, " "); } - printed += fprintf(fp, "#%s%s%s%s", + printed += fprintf(fp, "%s%s%s%s%s", + !PRINT_FIELD(BRCNTR) ? "#" : "", en->flags.predicted ? " PRED" : "", en->flags.mispred ? " MISPRED" : "", en->flags.in_tx ? " INTX" : "", @@ -2568,7 +2572,6 @@ static struct scripting_ops *scripting_ops; static void __process_stat(struct evsel *counter, u64 tstamp) { int nthreads = perf_thread_map__nr(counter->core.threads); - int idx, thread; struct perf_cpu cpu; static int header_printed; @@ -2578,7 +2581,9 @@ static void __process_stat(struct evsel *counter, u64 tstamp) header_printed = 1; } - for (thread = 0; thread < nthreads; thread++) { + for (int thread = 0; thread < nthreads; thread++) { + unsigned int idx; + perf_cpu_map__for_each_cpu(cpu, idx, evsel__cpus(counter)) { struct perf_counts_values *counts; @@ -2905,8 +2910,12 @@ static int print_event_with_time(const struct perf_tool *tool, thread = machine__findnew_thread(machine, pid, tid); if (evsel) { + struct evsel *saved_evsel = sample->evsel; + + sample->evsel = evsel; perf_sample__fprintf_start(script, sample, thread, evsel, event->header.type, stdout); + sample->evsel = saved_evsel; } perf_event__fprintf(event, machine, stdout); @@ -3814,7 +3823,7 @@ out: static int have_cmd(int argc, const char **argv) { - char **__argv = malloc(sizeof(const char *) * argc); + char **__argv = calloc(argc, sizeof(const char *)); if (!__argv) { pr_err("malloc failed\n"); @@ -3939,15 +3948,6 @@ int process_cpu_map_event(const struct perf_tool *tool, return set_maps(script); } -static int process_feature_event(const struct perf_tool *tool __maybe_unused, - struct perf_session *session, - union perf_event *event) -{ - if (event->feat.feat_id < HEADER_LAST_FEATURE) - return perf_event__process_feature(session, event); - return 0; -} - static int perf_script__process_auxtrace_info(const struct perf_tool *tool, struct perf_session *session, union perf_event *event) @@ -4074,8 +4074,7 @@ int cmd_script(int argc, const char **argv) "file", "kallsyms pathname"), OPT_BOOLEAN('G', "hide-call-graph", &no_callchain, "When printing symbols do not display call chain"), - OPT_CALLBACK(0, "symfs", NULL, "directory", - "Look for files with symbols relative to this directory", + OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP, symbol__config_symfs), OPT_CALLBACK('F', "fields", NULL, "str", "comma separated output fields prepend with 'type:'. " @@ -4313,7 +4312,7 @@ int cmd_script(int argc, const char **argv) } } - __argv = malloc((argc + 6) * sizeof(const char *)); + __argv = calloc(argc + 6, sizeof(const char *)); if (!__argv) { pr_err("malloc failed\n"); err = -ENOMEM; @@ -4339,7 +4338,7 @@ int cmd_script(int argc, const char **argv) dup2(live_pipe[0], 0); close(live_pipe[1]); - __argv = malloc((argc + 4) * sizeof(const char *)); + __argv = calloc(argc + 4, sizeof(const char *)); if (!__argv) { pr_err("malloc failed\n"); err = -ENOMEM; @@ -4377,7 +4376,7 @@ script_found: } } - __argv = malloc((argc + 2) * sizeof(const char *)); + __argv = calloc(argc + 2, sizeof(const char *)); if (!__argv) { pr_err("malloc failed\n"); err = -ENOMEM; @@ -4423,7 +4422,7 @@ script_found: #ifdef HAVE_LIBTRACEEVENT script.tool.tracing_data = perf_event__process_tracing_data; #endif - script.tool.feature = process_feature_event; + script.tool.feature = perf_event__process_feature; script.tool.build_id = perf_event__process_build_id; script.tool.id_index = perf_event__process_id_index; script.tool.auxtrace_info = perf_script__process_auxtrace_info; diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 73c2ba7e3076..99d7db372b48 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -164,7 +164,7 @@ struct opt_aggr_mode { }; /* Turn command line option into most generic aggregation mode setting. */ -static enum aggr_mode opt_aggr_mode_to_aggr_mode(struct opt_aggr_mode *opt_mode) +static enum aggr_mode opt_aggr_mode_to_aggr_mode(const struct opt_aggr_mode *opt_mode) { enum aggr_mode mode = AGGR_GLOBAL; @@ -410,7 +410,7 @@ static int read_tool_counters(void) struct evsel *counter; evlist__for_each_entry(evsel_list, counter) { - int idx; + unsigned int idx; if (!evsel__is_tool(counter)) continue; @@ -1214,13 +1214,28 @@ static int parse_cputype(const struct option *opt, return 0; } +static int parse_pmu_filter(const struct option *opt, + const char *str, + int unset __maybe_unused) +{ + struct evlist *evlist = *(struct evlist **)opt->value; + + if (!list_empty(&evlist->core.entries)) { + fprintf(stderr, "Must define pmu-filter before events/metrics\n"); + return -1; + } + + parse_events_option_args.pmu_filter = str; + return 0; +} + static int parse_cache_level(const struct option *opt, const char *str, int unset __maybe_unused) { int level; - struct opt_aggr_mode *opt_aggr_mode = (struct opt_aggr_mode *)opt->value; - u32 *aggr_level = (u32 *)opt->data; + bool *per_cache = opt->value; + u32 *aggr_level = opt->data; /* * If no string is specified, aggregate based on the topology of @@ -1258,7 +1273,7 @@ static int parse_cache_level(const struct option *opt, return -EINVAL; } out: - opt_aggr_mode->cache = true; + *per_cache = true; *aggr_level = level; return 0; } @@ -1917,25 +1932,33 @@ static int default_evlist_evsel_cmp(void *priv __maybe_unused, const struct evsel *lhs = container_of(lhs_core, struct evsel, core); const struct perf_evsel *rhs_core = container_of(r, struct perf_evsel, node); const struct evsel *rhs = container_of(rhs_core, struct evsel, core); + const struct evsel *lhs_leader = evsel__leader(lhs); + const struct evsel *rhs_leader = evsel__leader(rhs); - if (evsel__leader(lhs) == evsel__leader(rhs)) { + if (lhs_leader == rhs_leader) { /* Within the same group, respect the original order. */ return lhs_core->idx - rhs_core->idx; } - /* Sort default metrics evsels first, and default show events before those. */ - if (lhs->default_metricgroup != rhs->default_metricgroup) - return lhs->default_metricgroup ? -1 : 1; + /* + * Compare using leader's attributes so that all members of a group + * stay together. This ensures leaders are opened before their members. + */ - if (lhs->default_show_events != rhs->default_show_events) - return lhs->default_show_events ? -1 : 1; + /* Sort default metrics evsels first, and default show events before those. */ + if (lhs_leader->default_metricgroup != rhs_leader->default_metricgroup) + return lhs_leader->default_metricgroup ? -1 : 1; + + if (lhs_leader->default_show_events != rhs_leader->default_show_events) + return lhs_leader->default_show_events ? -1 : 1; /* Sort by PMU type (prefers legacy types first). */ - if (lhs->pmu != rhs->pmu) - return lhs->pmu->type - rhs->pmu->type; + if (lhs_leader->pmu != rhs_leader->pmu) + return lhs_leader->pmu->type - rhs_leader->pmu->type; - /* Sort by name. */ - return strcmp(evsel__name((struct evsel *)lhs), evsel__name((struct evsel *)rhs)); + /* Sort by leader's name. */ + return strcmp(evsel__name((struct evsel *)lhs_leader), + evsel__name((struct evsel *)rhs_leader)); } /* @@ -2305,24 +2328,23 @@ static struct perf_stat perf_stat = { static int __cmd_report(int argc, const char **argv) { struct perf_session *session; + struct opt_aggr_mode opt_mode = {}; const struct option options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), - OPT_SET_UINT(0, "per-socket", &perf_stat.aggr_mode, - "aggregate counts per processor socket", AGGR_SOCKET), - OPT_SET_UINT(0, "per-die", &perf_stat.aggr_mode, - "aggregate counts per processor die", AGGR_DIE), - OPT_SET_UINT(0, "per-cluster", &perf_stat.aggr_mode, - "aggregate counts perf processor cluster", AGGR_CLUSTER), - OPT_CALLBACK_OPTARG(0, "per-cache", &perf_stat.aggr_mode, &perf_stat.aggr_level, - "cache level", - "aggregate count at this cache level (Default: LLC)", + OPT_BOOLEAN(0, "per-thread", &opt_mode.thread, "aggregate counts per thread"), + OPT_BOOLEAN(0, "per-socket", &opt_mode.socket, + "aggregate counts per processor socket"), + OPT_BOOLEAN(0, "per-die", &opt_mode.die, "aggregate counts per processor die"), + OPT_BOOLEAN(0, "per-cluster", &opt_mode.cluster, + "aggregate counts per processor cluster"), + OPT_CALLBACK_OPTARG(0, "per-cache", &opt_mode.cache, &perf_stat.aggr_level, + "cache level", "aggregate count at this cache level (Default: LLC)", parse_cache_level), - OPT_SET_UINT(0, "per-core", &perf_stat.aggr_mode, - "aggregate counts per physical processor core", AGGR_CORE), - OPT_SET_UINT(0, "per-node", &perf_stat.aggr_mode, - "aggregate counts per numa node", AGGR_NODE), - OPT_SET_UINT('A', "no-aggr", &perf_stat.aggr_mode, - "disable CPU count aggregation", AGGR_NONE), + OPT_BOOLEAN(0, "per-core", &opt_mode.core, + "aggregate counts per physical processor core"), + OPT_BOOLEAN(0, "per-node", &opt_mode.node, "aggregate counts per numa node"), + OPT_BOOLEAN('A', "no-aggr", &opt_mode.no_aggr, + "disable aggregation across CPUs or PMUs"), OPT_END() }; struct stat st; @@ -2330,6 +2352,10 @@ static int __cmd_report(int argc, const char **argv) argc = parse_options(argc, argv, options, stat_report_usage, 0); + perf_stat.aggr_mode = opt_aggr_mode_to_aggr_mode(&opt_mode); + if (perf_stat.aggr_mode == AGGR_GLOBAL) + perf_stat.aggr_mode = AGGR_UNSET; /* No option found so leave unset. */ + if (!input_name || !strlen(input_name)) { if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) input_name = "-"; @@ -2506,7 +2532,7 @@ int cmd_stat(int argc, const char **argv) OPT_BOOLEAN(0, "per-die", &opt_mode.die, "aggregate counts per processor die"), OPT_BOOLEAN(0, "per-cluster", &opt_mode.cluster, "aggregate counts per processor cluster"), - OPT_CALLBACK_OPTARG(0, "per-cache", &opt_mode, &stat_config.aggr_level, + OPT_CALLBACK_OPTARG(0, "per-cache", &opt_mode.cache, &stat_config.aggr_level, "cache level", "aggregate count at this cache level (Default: LLC)", parse_cache_level), OPT_BOOLEAN(0, "per-core", &opt_mode.core, @@ -2561,6 +2587,10 @@ int cmd_stat(int argc, const char **argv) "Only enable events on applying cpu with this type " "for hybrid platform (e.g. core or atom)", parse_cputype), + OPT_CALLBACK(0, "pmu-filter", &evsel_list, "pmu", + "Only enable events on applying pmu with specified " + "for multiple pmus with same type(e.g. hisi_sicl2_cpa0 or hisi_sicl0_cpa0)", + parse_pmu_filter), #ifdef HAVE_LIBPFM OPT_CALLBACK(0, "pfm-events", &evsel_list, "event", "libpfm4 event selector. use 'perf list' to list available events", @@ -2744,7 +2774,7 @@ int cmd_stat(int argc, const char **argv) } if (stat_config.walltime_run_table) { - stat_config.walltime_run = zalloc(stat_config.run_count * sizeof(stat_config.walltime_run[0])); + stat_config.walltime_run = calloc(stat_config.run_count, sizeof(stat_config.walltime_run[0])); if (!stat_config.walltime_run) { pr_err("failed to setup -r option"); goto out; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index f8b49d69e9a5..28f33e39895d 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -1951,8 +1951,7 @@ int cmd_timechart(int argc, const char **argv) OPT_CALLBACK('p', "process", NULL, "process", "process selector. Pass a pid or process name.", parse_process), - OPT_CALLBACK(0, "symfs", NULL, "directory", - "Look for files with symbols relative to this directory", + OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP, symbol__config_symfs), OPT_INTEGER('n', "proc-num", &tchart.proc_num, "min. number of tasks to print"), diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 710604c4f6f6..f6eb543de537 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -56,6 +56,7 @@ #include "util/debug.h" #include "util/ordered-events.h" #include "util/pfm.h" +#include "dwarf-regs.h" #include #include @@ -1386,13 +1387,6 @@ out_join_thread: return ret; } -static int -callchain_opt(const struct option *opt, const char *arg, int unset) -{ - symbol_conf.use_callchain = true; - return record_callchain_opt(opt, arg, unset); -} - static int parse_callchain_opt(const struct option *opt, const char *arg, int unset) { @@ -1413,6 +1407,24 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) return parse_callchain_top_opt(arg); } +static int +callchain_opt(const struct option *opt, const char *arg __maybe_unused, int unset) +{ + struct callchain_param *callchain = opt->value; + + /* + * The -g option only sets the callchain if not already configured by + * .perfconfig. It does, however, enable it. + */ + if (callchain->record_mode != CALLCHAIN_NONE) { + callchain->enabled = true; + return 0; + } + + return parse_callchain_opt(opt, EM_HOST != EM_S390 ? "fp" : "dwarf", unset); +} + + static int perf_top_config(const char *var, const char *value, void *cb __maybe_unused) { if (!strcmp(var, "top.call-graph")) { @@ -1437,11 +1449,10 @@ parse_percent_limit(const struct option *opt, const char *arg, return 0; } -const char top_callchain_help[] = CALLCHAIN_RECORD_HELP CALLCHAIN_REPORT_HELP - "\n\t\t\t\tDefault: fp,graph,0.5,caller,function"; - int cmd_top(int argc, const char **argv) { + static const char top_callchain_help[] = CALLCHAIN_RECORD_HELP CALLCHAIN_REPORT_HELP + "\n\t\t\t\tDefault: fp,graph,0.5,caller,function"; char errbuf[BUFSIZ]; struct perf_top top = { .count_filter = 5, @@ -1694,8 +1705,17 @@ int cmd_top(int argc, const char **argv) if (annotate_check_args() < 0) goto out_delete_evlist; + status = target__validate(target); + if (status) { + target__strerror(target, status, errbuf, BUFSIZ); + ui__warning("%s\n", errbuf); + } + + if (target__none(target)) + target->system_wide = true; + if (!top.evlist->core.nr_entries) { - struct evlist *def_evlist = evlist__new_default(); + struct evlist *def_evlist = evlist__new_default(target, callchain_param.enabled); if (!def_evlist) goto out_delete_evlist; @@ -1788,12 +1808,6 @@ int cmd_top(int argc, const char **argv) goto out_delete_evlist; } - status = target__validate(target); - if (status) { - target__strerror(target, status, errbuf, BUFSIZ); - ui__warning("%s\n", errbuf); - } - if (top.uid_str) { uid_t uid = parse_uid(top.uid_str); @@ -1807,9 +1821,6 @@ int cmd_top(int argc, const char **argv) goto out_delete_evlist; } - if (target__none(target)) - target->system_wide = true; - if (evlist__create_maps(top.evlist, target) < 0) { ui__error("Couldn't create thread/CPU maps: %s\n", errno == ENOENT ? "No such process" : str_error_r(errno, errbuf, sizeof(errbuf))); diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 311d9da9896a..e58c49d047a2 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -21,7 +21,6 @@ #include #include #endif -#include "util/bpf_map.h" #include "util/rlimit.h" #include "builtin.h" #include "util/cgroup.h" @@ -1565,7 +1564,9 @@ static bool syscall_id_equal(long key1, long key2, void *ctx __maybe_unused) static struct hashmap *alloc_syscall_stats(void) { - return hashmap__new(syscall_id_hash, syscall_id_equal, NULL); + struct hashmap *result = hashmap__new(syscall_id_hash, syscall_id_equal, NULL); + + return IS_ERR(result) ? NULL : result; } static void delete_syscall_stats(struct hashmap *syscall_stats) @@ -1573,7 +1574,7 @@ static void delete_syscall_stats(struct hashmap *syscall_stats) struct hashmap_entry *pos; size_t bkt; - if (syscall_stats == NULL) + if (!syscall_stats) return; hashmap__for_each_entry(syscall_stats, pos, bkt) @@ -1589,7 +1590,7 @@ static struct thread_trace *thread_trace__new(struct trace *trace) ttrace->files.max = -1; if (trace->summary) { ttrace->syscall_stats = alloc_syscall_stats(); - if (IS_ERR(ttrace->syscall_stats)) + if (!ttrace->syscall_stats) zfree(&ttrace); } } @@ -2003,9 +2004,13 @@ static int trace__symbols_init(struct trace *trace, int argc, const char **argv, if (err < 0) goto out; + if (trace->summary_only && trace->summary_mode != SUMMARY__BY_THREAD) + goto out; + err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target, evlist->core.threads, trace__tool_process, - /*needs_mmap=*/callchain_param.enabled, + /*needs_mmap=*/callchain_param.enabled && + !trace->summary_only, /*mmap_data=*/false, /*nr_threads_synthesize=*/1); out: @@ -2264,9 +2269,7 @@ static int trace__validate_ev_qualifier(struct trace *trace) struct str_node *pos; size_t nr_used = 0, nr_allocated = strlist__nr_entries(trace->ev_qualifier); - trace->ev_qualifier_ids.entries = malloc(nr_allocated * - sizeof(trace->ev_qualifier_ids.entries[0])); - + trace->ev_qualifier_ids.entries = calloc(nr_allocated, sizeof(trace->ev_qualifier_ids.entries[0])); if (trace->ev_qualifier_ids.entries == NULL) { fputs("Error:\tNot enough memory for allocating events qualifier ids\n", trace->output); @@ -2955,7 +2958,7 @@ static int trace__sys_exit(struct trace *trace, struct evsel *evsel, ++trace->stats.vfs_getname; } - if (ttrace->entry_time) { + if (ttrace->entry_time && sample->time >= ttrace->entry_time) { duration = sample->time - ttrace->entry_time; if (trace__filter_duration(trace, duration)) goto out; @@ -4464,7 +4467,7 @@ create_maps: if (trace->summary_mode == SUMMARY__BY_TOTAL && !trace->summary_bpf) { trace->syscall_stats = alloc_syscall_stats(); - if (IS_ERR(trace->syscall_stats)) + if (!trace->syscall_stats) goto out_delete_evlist; } @@ -4771,7 +4774,7 @@ static int trace__replay(struct trace *trace) if (trace->summary_mode == SUMMARY__BY_TOTAL) { trace->syscall_stats = alloc_syscall_stats(); - if (IS_ERR(trace->syscall_stats)) + if (!trace->syscall_stats) goto out; } @@ -5299,6 +5302,13 @@ static int trace__parse_summary_mode(const struct option *opt, const char *str, return 0; } +static int trace_parse_callchain_opt(const struct option *opt, + const char *arg, + int unset) +{ + return record_opts__parse_callchain(opt->value, &callchain_param, arg, unset); +} + static int trace__config(const char *var, const char *value, void *arg) { struct trace *trace = arg; @@ -5446,7 +5456,7 @@ int cmd_trace(int argc, const char **argv) OPT_BOOLEAN('f', "force", &trace.force, "don't complain, do it"), OPT_CALLBACK(0, "call-graph", &trace.opts, "record_mode[,record_size]", record_callchain_help, - &record_parse_callchain_opt), + &trace_parse_callchain_opt), OPT_BOOLEAN(0, "libtraceevent_print", &trace.libtraceevent_print, "Use libtraceevent to print the tracepoint arguments."), OPT_BOOLEAN(0, "kernel-syscall-graph", &trace.kernel_syscallchains, diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh index 31826621eebd..531c0e0e84df 100755 --- a/tools/perf/check-headers.sh +++ b/tools/perf/check-headers.sh @@ -6,10 +6,7 @@ NC='\033[0m' # No Color declare -a FILES=( "include/uapi/linux/const.h" - "include/uapi/drm/drm.h" - "include/uapi/drm/i915_drm.h" "include/uapi/linux/bits.h" - "include/uapi/linux/fadvise.h" "include/uapi/linux/fscrypt.h" "include/uapi/linux/genetlink.h" "include/uapi/linux/if_addr.h" @@ -90,7 +87,10 @@ declare -a SYNC_CHECK_FILES=( declare -a BEAUTY_FILES=( "arch/x86/include/asm/irq_vectors.h" "arch/x86/include/uapi/asm/prctl.h" + "include/uapi/drm/drm.h" + "include/uapi/drm/i915_drm.h" "include/linux/socket.h" + "include/uapi/linux/fadvise.h" "include/uapi/linux/fcntl.h" "include/uapi/linux/fs.h" "include/uapi/linux/mount.h" diff --git a/tools/perf/jvmti/libjvmti.c b/tools/perf/jvmti/libjvmti.c index 87bfd4781003..d3dc53010e76 100644 --- a/tools/perf/jvmti/libjvmti.c +++ b/tools/perf/jvmti/libjvmti.c @@ -98,7 +98,7 @@ get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t ** /* * Phase 2 -- allocate big enough line table */ - *tab = malloc(nr_total * sizeof(**tab)); + *tab = calloc(nr_total, sizeof(**tab)); if (!*tab) return JVMTI_ERROR_OUT_OF_MEMORY; @@ -262,11 +262,10 @@ compiled_method_load_cb(jvmtiEnv *jvmti, } nr_lines = 0; } else if (nr_lines > 0) { - line_file_names = malloc(sizeof(char*) * nr_lines); + line_file_names = calloc(nr_lines, sizeof(char *)); if (!line_file_names) { warnx("jvmti: cannot allocate space for line table method names"); } else { - memset(line_file_names, 0, sizeof(char*) * nr_lines); ret = fill_source_filenames(jvmti, nr_lines, line_tab, line_file_names); if (ret != JVMTI_ERROR_NONE) { warnx("jvmti: fill_source_filenames failed"); diff --git a/tools/perf/perf.c b/tools/perf/perf.c index f475a8664ffc..1f51e8de6b1b 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -48,7 +48,7 @@ struct cmd_struct { int option; }; -static struct cmd_struct commands[] = { +static const struct cmd_struct commands[] = { { "archive", NULL, 0 }, { "buildid-cache", cmd_buildid_cache, 0 }, { "buildid-list", cmd_buildid_list, 0 }, @@ -178,7 +178,7 @@ static int set_debug_file(const char *path) return 0; } -struct option options[] = { +static const struct option options[] = { OPT_ARGUMENT("help", "help"), OPT_ARGUMENT("version", "version"), OPT_ARGUMENT("exec-path", "exec-path"), @@ -280,7 +280,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) unsigned int i; for (i = 0; i < ARRAY_SIZE(commands); i++) { - struct cmd_struct *p = commands+i; + const struct cmd_struct *p = commands + i; printf("%s ", p->cmd); } putchar('\n'); @@ -289,7 +289,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) unsigned int i; for (i = 0; i < ARRAY_SIZE(options)-1; i++) { - struct option *p = options+i; + const struct option *p = options + i; printf("--%s ", p->long_name); } putchar('\n'); @@ -331,7 +331,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) #define RUN_SETUP (1<<0) #define USE_PAGER (1<<1) -static int run_builtin(struct cmd_struct *p, int argc, const char **argv) +static int run_builtin(const struct cmd_struct *p, int argc, const char **argv) { int status; struct stat st; @@ -390,7 +390,7 @@ static void handle_internal_command(int argc, const char **argv) } for (i = 0; i < ARRAY_SIZE(commands); i++) { - struct cmd_struct *p = commands+i; + const struct cmd_struct *p = commands+i; if (p->fn == NULL) continue; if (strcmp(p->cmd, cmd)) diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build index dc5f94862a3b..dc1df2d57ddc 100644 --- a/tools/perf/pmu-events/Build +++ b/tools/perf/pmu-events/Build @@ -211,10 +211,10 @@ ifneq ($(strip $(ORPHAN_FILES)),) # Message for $(call echo-cmd,rm). Generally cleaning files isn't part # of a build step. -quiet_cmd_rm = RM $^ +quiet_cmd_rm = RM ...$(words $^) orphan file(s)... +# The list of files can be long. Use xargs to prevent issues. prune_orphans: $(ORPHAN_FILES) - # The list of files can be long. Use xargs to prevent issues. $(Q)$(call echo-cmd,rm)echo "$^" | xargs rm -f JEVENTS_DEPS += prune_orphans diff --git a/tools/perf/pmu-events/arch/arm64/common-and-microarch.json b/tools/perf/pmu-events/arch/arm64/common-and-microarch.json index 468cb085d879..144325d87be4 100644 --- a/tools/perf/pmu-events/arch/arm64/common-and-microarch.json +++ b/tools/perf/pmu-events/arch/arm64/common-and-microarch.json @@ -1512,11 +1512,26 @@ "EventName": "L2D_CACHE_REFILL_PRFM", "BriefDescription": "Level 2 data cache refill, software preload" }, + { + "EventCode": "0x8150", + "EventName": "L3D_CACHE_RW", + "BriefDescription": "Level 3 data cache demand access." + }, + { + "EventCode": "0x8151", + "EventName": "L3D_CACHE_PRFM", + "BriefDescription": "Level 3 data cache software prefetch" + }, { "EventCode": "0x8152", "EventName": "L3D_CACHE_MISS", "BriefDescription": "Level 3 data cache demand access miss" }, + { + "EventCode": "0x8153", + "EventName": "L3D_CACHE_REFILL_PRFM", + "BriefDescription": "Level 3 data cache refill, software prefetch." + }, { "EventCode": "0x8154", "EventName": "L1D_CACHE_HWPRF", @@ -1527,6 +1542,11 @@ "EventName": "L2D_CACHE_HWPRF", "BriefDescription": "Level 2 data cache hardware prefetch." }, + { + "EventCode": "0x8156", + "EventName": "L3D_CACHE_HWPRF", + "BriefDescription": "Level 3 data cache hardware prefetch." + }, { "EventCode": "0x8158", "EventName": "STALL_FRONTEND_MEMBOUND", @@ -1682,6 +1702,11 @@ "EventName": "L2D_CACHE_REFILL_HWPRF", "BriefDescription": "Level 2 data cache refill, hardware prefetch." }, + { + "EventCode": "0x81BE", + "EventName": "L3D_CACHE_REFILL_HWPRF", + "BriefDescription": "Level 3 data cache refill, hardware prefetch." + }, { "EventCode": "0x81C0", "EventName": "L1I_CACHE_HIT_RD", @@ -1712,11 +1737,31 @@ "EventName": "L1I_CACHE_HIT_RD_FPRFM", "BriefDescription": "Level 1 instruction cache demand fetch first hit, fetched by software preload" }, + { + "EventCode": "0x81DC", + "EventName": "L1D_CACHE_HIT_RW_FPRFM", + "BriefDescription": "Level 1 data cache demand access first hit, fetched by software prefetch." + }, { "EventCode": "0x81E0", "EventName": "L1I_CACHE_HIT_RD_FHWPRF", "BriefDescription": "Level 1 instruction cache demand fetch first hit, fetched by hardware prefetcher" }, + { + "EventCode": "0x81EC", + "EventName": "L1D_CACHE_HIT_RW_FHWPRF", + "BriefDescription": "Level 1 data cache demand access first hit, fetched by hardware prefetcher." + }, + { + "EventCode": "0x81F0", + "EventName": "L1I_CACHE_HIT_RD_FPRF", + "BriefDescription": "Level 1 instruction cache demand fetch first hit, fetched by prefetch." + }, + { + "EventCode": "0x81FC", + "EventName": "L1D_CACHE_HIT_RW_FPRF", + "BriefDescription": "Level 1 data cache demand access first hit, fetched by prefetch." + }, { "EventCode": "0x8200", "EventName": "L1I_CACHE_HIT", @@ -1767,11 +1812,26 @@ "EventName": "L1I_LFB_HIT_RD_FPRFM", "BriefDescription": "Level 1 instruction cache demand fetch line-fill buffer first hit, recently fetched by software preload" }, + { + "EventCode": "0x825C", + "EventName": "L1D_LFB_HIT_RW_FPRFM", + "BriefDescription": "Level 1 data cache demand access line-fill buffer first hit, recently fetched by software prefetch." + }, { "EventCode": "0x8260", "EventName": "L1I_LFB_HIT_RD_FHWPRF", "BriefDescription": "Level 1 instruction cache demand fetch line-fill buffer first hit, recently fetched by hardware prefetcher" }, + { + "EventCode": "0x826C", + "EventName": "L1D_LFB_HIT_RW_FHWPRF", + "BriefDescription": "Level 1 data cache demand access line-fill buffer first hit, recently fetched by hardware prefetcher." + }, + { + "EventCode": "0x827C", + "EventName": "L1D_LFB_HIT_RW_FPRF", + "BriefDescription": "Level 1 data cache demand access line-fill buffer first hit, recently fetched by prefetch." + }, { "EventCode": "0x8280", "EventName": "L1I_CACHE_PRF", @@ -1807,6 +1867,11 @@ "EventName": "LL_CACHE_REFILL", "BriefDescription": "Last level cache refill" }, + { + "EventCode": "0x828E", + "EventName": "L3D_CACHE_REFILL_PRF", + "BriefDescription": "Level 3 data cache refill, prefetch." + }, { "EventCode": "0x8320", "EventName": "L1D_CACHE_REFILL_PERCYC", @@ -1872,6 +1937,16 @@ "EventName": "FP_FP8_MIN_SPEC", "BriefDescription": "Floating-point operation speculatively_executed, smallest type is 8-bit floating-point." }, + { + "EventCode": "0x8480", + "EventName": "FP_SP_FIXED_MIN_OPS_SPEC", + "BriefDescription": "Non-scalable element arithmetic operations speculatively executed, smallest type is single-precision floating-point." + }, + { + "EventCode": "0x8482", + "EventName": "FP_HP_FIXED_MIN_OPS_SPEC", + "BriefDescription": "Non-scalable element arithmetic operations speculatively executed, smallest type is half-precision floating-point." + }, { "EventCode": "0x8483", "EventName": "FP_BF16_FIXED_MIN_OPS_SPEC", @@ -1882,6 +1957,16 @@ "EventName": "FP_FP8_FIXED_MIN_OPS_SPEC", "BriefDescription": "Non-scalable element arithmetic operations speculatively executed, smallest type is 8-bit floating-point." }, + { + "EventCode": "0x8488", + "EventName": "FP_SP_SCALE_MIN_OPS_SPEC", + "BriefDescription": "Scalable element arithmetic operations speculatively executed, smallest type is single-precision floating-point." + }, + { + "EventCode": "0x848A", + "EventName": "FP_HP_SCALE_MIN_OPS_SPEC", + "BriefDescription": "Scalable element arithmetic operations speculatively executed, smallest type is half-precision floating-point." + }, { "EventCode": "0x848B", "EventName": "FP_BF16_SCALE_MIN_OPS_SPEC", diff --git a/tools/perf/pmu-events/arch/arm64/mapfile.csv b/tools/perf/pmu-events/arch/arm64/mapfile.csv index bb3fa8a33496..7f0eaa702048 100644 --- a/tools/perf/pmu-events/arch/arm64/mapfile.csv +++ b/tools/perf/pmu-events/arch/arm64/mapfile.csv @@ -46,3 +46,4 @@ 0x00000000500f0000,v1,ampere/emag,core 0x00000000c00fac30,v1,ampere/ampereone,core 0x00000000c00fac40,v1,ampere/ampereonex,core +0x000000004e0f0100,v1,nvidia/t410,core diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/branch.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/branch.json new file mode 100644 index 000000000000..ef4effc00ec3 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/branch.json @@ -0,0 +1,45 @@ +[ + { + "ArchStdEvent": "BR_MIS_PRED", + "PublicDescription": "This event counts branches which are speculatively executed and mispredicted." + }, + { + "ArchStdEvent": "BR_PRED", + "PublicDescription": "This event counts all speculatively executed branches." + }, + { + "EventCode": "0x017e", + "EventName": "BR_PRED_BTB_CTX_UPDATE", + "PublicDescription": "Branch context table update." + }, + { + "EventCode": "0x0188", + "EventName": "BR_MIS_PRED_DIR_RESOLVED", + "PublicDescription": "Number of branch misprediction due to direction misprediction." + }, + { + "EventCode": "0x0189", + "EventName": "BR_MIS_PRED_DIR_UNCOND_RESOLVED", + "PublicDescription": "Number of branch misprediction due to direction misprediction for unconditional branches." + }, + { + "EventCode": "0x018a", + "EventName": "BR_MIS_PRED_DIR_UNCOND_DIRECT_RESOLVED", + "PublicDescription": "Number of branch misprediction due to direction misprediction for unconditional direct branches." + }, + { + "EventCode": "0x018b", + "EventName": "BR_PRED_MULTI_RESOLVED", + "PublicDescription": "Number of resolved branch which made prediction by polymorphic indirect predictor." + }, + { + "EventCode": "0x018c", + "EventName": "BR_MIS_PRED_MULTI_RESOLVED", + "PublicDescription": "Number of branch misprediction which made prediction by polymorphic indirect predictor." + }, + { + "EventCode": "0x01e4", + "EventName": "BR_RGN_RECLAIM", + "PublicDescription": "This event counts the Indirect predictor entries flushed by region reclamation." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/brbe.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/brbe.json new file mode 100644 index 000000000000..9c315b2d7046 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/brbe.json @@ -0,0 +1,6 @@ +[ + { + "ArchStdEvent": "BRB_FILTRATE", + "PublicDescription": "This event counts each valid branch record captured in the branch record buffer. Branch records that are not captured because they are removed by filtering are not counted." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/bus.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/bus.json new file mode 100644 index 000000000000..5bb8de617c68 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/bus.json @@ -0,0 +1,48 @@ +[ + { + "ArchStdEvent": "BUS_ACCESS", + "PublicDescription": "This event counts the number of data-beat accesses between the CPU and the external bus. This count includes accesses due to read, write, and snoop. Each beat of data is counted individually." + }, + { + "ArchStdEvent": "BUS_CYCLES", + "PublicDescription": "This event counts bus cycles in the CPU. Bus cycles represent a clock cycle in which a transaction could be sent or received on the interface from the CPU to the external bus. Since that interface is driven at the same clock speed as the CPU, this event increments at the rate of CPU clock. Regardless of the WFE/WFI state of the PE, this event increments on each processor clock." + }, + { + "ArchStdEvent": "BUS_ACCESS_RD", + "PublicDescription": "This event counts memory Read transactions seen on the external bus. Each beat of data is counted individually." + }, + { + "ArchStdEvent": "BUS_ACCESS_WR", + "PublicDescription": "This event counts memory Write transactions seen on the external bus. Each beat of data is counted individually." + }, + { + "EventCode": "0x0154", + "EventName": "BUS_REQUEST_REQ", + "PublicDescription": "Bus request, request." + }, + { + "EventCode": "0x0155", + "EventName": "BUS_REQUEST_RETRY", + "PublicDescription": "Bus request, retry." + }, + { + "EventCode": "0x0198", + "EventName": "L2_CHI_CBUSY0", + "PublicDescription": "Number of RXDAT or RXRSP response received width CBusy of 0." + }, + { + "EventCode": "0x0199", + "EventName": "L2_CHI_CBUSY1", + "PublicDescription": "Number of RXDAT or RXRSP response received width CBusy of 1." + }, + { + "EventCode": "0x019a", + "EventName": "L2_CHI_CBUSY2", + "PublicDescription": "Number of RXDAT or RXRSP response received width CBusy of 2." + }, + { + "EventCode": "0x019b", + "EventName": "L2_CHI_CBUSY3", + "PublicDescription": "Number of RXDAT or RXRSP response received width CBusy of 3." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/exception.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/exception.json new file mode 100644 index 000000000000..ecd996c3610b --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/exception.json @@ -0,0 +1,62 @@ +[ + { + "ArchStdEvent": "EXC_TAKEN", + "PublicDescription": "This event counts any taken architecturally visible exceptions such as IRQ, FIQ, SError, and other synchronous exceptions. Exceptions are counted whether or not they are taken locally." + }, + { + "ArchStdEvent": "EXC_RETURN", + "PublicDescription": "This event counts any architecturally executed exception return instructions. For example: AArch64: ERET." + }, + { + "ArchStdEvent": "EXC_UNDEF", + "PublicDescription": "This event counts the number of synchronous exceptions which are taken locally that are due to attempting to execute an instruction that is UNDEFINED.\nAttempting to execute instruction bit patterns that have not been allocated.\nAttempting to execute instructions when they are disabled.\nAttempting to execute instructions at an inappropriate Exception level.\nAttempting to execute an instruction when the value of PSTATE.IL is 1." + }, + { + "ArchStdEvent": "EXC_SVC", + "PublicDescription": "This event counts SVC exceptions taken locally." + }, + { + "ArchStdEvent": "EXC_PABORT", + "PublicDescription": "This event counts synchronous exceptions that are taken locally and caused by Instruction Aborts." + }, + { + "ArchStdEvent": "EXC_DABORT", + "PublicDescription": "This event counts exceptions that are taken locally and are caused by data aborts or SErrors. Conditions that could cause those exceptions are attempting to read or write memory where the MMU generates a fault, attempting to read or write memory with a misaligned address, Interrupts from the nSEI inputs and internally generated SErrors." + }, + { + "ArchStdEvent": "EXC_IRQ", + "PublicDescription": "This event counts IRQ exceptions including the virtual IRQs that are taken locally." + }, + { + "ArchStdEvent": "EXC_FIQ", + "PublicDescription": "This event counts FIQ exceptions including the virtual FIQs that are taken locally." + }, + { + "ArchStdEvent": "EXC_SMC", + "PublicDescription": "This event counts SMC exceptions taken to EL3." + }, + { + "ArchStdEvent": "EXC_HVC", + "PublicDescription": "This event counts HVC exceptions taken to EL2." + }, + { + "ArchStdEvent": "EXC_TRAP_PABORT", + "PublicDescription": "This event counts exceptions which are traps not taken locally and are caused by Instruction Aborts. For example, attempting to execute an instruction with a misaligned PC." + }, + { + "ArchStdEvent": "EXC_TRAP_DABORT", + "PublicDescription": "This event counts exceptions which are traps not taken locally and are caused by Data Aborts or SError Interrupts. Conditions that could cause those exceptions are:\n* Attempting to read or write memory where the MMU generates a fault,\n* Attempting to read or write memory with a misaligned address,\n* Interrupts from the SEI input,\n* Internally generated SErrors." + }, + { + "ArchStdEvent": "EXC_TRAP_OTHER", + "PublicDescription": "This event counts the number of synchronous trap exceptions which are not taken locally and are not SVC, SMC, HVC, Data Aborts, Instruction Aborts, or Interrupts." + }, + { + "ArchStdEvent": "EXC_TRAP_IRQ", + "PublicDescription": "This event counts IRQ exceptions including the virtual IRQs that are not taken locally." + }, + { + "ArchStdEvent": "EXC_TRAP_FIQ", + "PublicDescription": "This event counts FIQs which are not taken locally but taken from EL0, EL1, or EL2 to EL3 (which would be the normal behavior for FIQs when not executing in EL3)." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/fp_operation.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/fp_operation.json new file mode 100644 index 000000000000..3588e130781d --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/fp_operation.json @@ -0,0 +1,78 @@ +[ + { + "ArchStdEvent": "FP_HP_SPEC", + "PublicDescription": "This event counts speculatively executed half precision floating point operations." + }, + { + "ArchStdEvent": "FP_SP_SPEC", + "PublicDescription": "This event counts speculatively executed single precision floating point operations." + }, + { + "ArchStdEvent": "FP_DP_SPEC", + "PublicDescription": "This event counts speculatively executed double precision floating point operations." + }, + { + "ArchStdEvent": "FP_SCALE_OPS_SPEC", + "PublicDescription": "This event counts speculatively executed scalable single precision floating point operations." + }, + { + "ArchStdEvent": "FP_FIXED_OPS_SPEC", + "PublicDescription": "This event counts speculatively executed non-scalable single precision floating point operations." + }, + { + "ArchStdEvent": "FP_HP_SCALE_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed scalable element arithmetic operation, due to an instruction where the largest type was half-precision floating-point, where v is a value such that (v*(VL/128)) is the number of arithmetic operations carried out by the operation or instruction which causes the counter to increment.\nThis event does not count operations that are counted by FP_FIXED_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_HP_FIXED_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed non-scalable element arithmetic operation, due to an instruction where the largest type was half-precision floating-point, where v is the number of arithmetic operations carried out by the operation or which instruction causes the event to increment.\nThis event does not count operations that are counted by FP_SCALE_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_SP_SCALE_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed scalable element arithmetic operation, due to an instruction where the largest type was single-precision floating-point, where v is a value such that (v*(VL/128)) is the number of arithmetic operations carried out by the operation or instruction which causes the event to increment.\nThis event does not count operations that are counted by FP_FIXED_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_SP_FIXED_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed non-scalable element arithmetic operation, due to an instruction where the largest type was single-precision floating-point, where v is the number of arithmetic operations carried out by the operation or instruction which causes the event to increment.\nThis event does not count operations that are counted by FP_SCALE_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_DP_SCALE_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed scalable element arithmetic operation, due to an instruction where the largest type was double-precision floating-point, where v is a value such that (v*(VL/128)) is the number of arithmetic operations carried out by the operation or instruction which causes the event to increment.\nThis event does not count operations that are counted by FP_FIXED_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_DP_FIXED_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed non-scalable element arithmetic operation, due to an instruction where the largest type was double-precision floating-point, where v is the number of arithmetic operations carried out by the operation or instruction which causes the event to increment.\nThis event does not count operations that are counted by FP_SCALE_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_SP_FIXED_MIN_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed non-scalable element arithmetic operation, due to an instruction where the smallest type was single-precision floating-point, where v is the number of arithmetic operations carried out by the operation or instruction which causes the event to increment.\nThis event does not count operations that are counted by FP_SCALE_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_HP_FIXED_MIN_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed non-scalable element arithmetic operation, due to an instruction where the smallest type was half-precision floating-point, where v is the number of arithmetic operations carried out by the operation or instruction which causes the event to increment.\nThis event does not count operations that are counted by FP_SCALE_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_BF16_FIXED_MIN_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed non-scalable element arithmetic operation, due to an instruction where the smallest type was BFloat16 floating-point. Where v is the number of arithmetic operations carried out by the operation or instruction which causes the event to increment. This event does not count operations that are counted by FP_SCALE_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_FP8_FIXED_MIN_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed non-scalable element arithmetic operation, due to an instruction where the smallest type was 8-bit floating-point, where v is the number of arithmetic operations carried out by the operation or instruction which causes the event to increment.\nThis event does not count operations that are counted by FP_SCALE_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_SP_SCALE_MIN_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed scalable element arithmetic operation, due to an instruction where the smallest type was single-precision floating-point, where v is a value such that (v*(VL/128)) is the number of arithmetic operations carried out by the operation or instruction which causes the event to increment.\nThis event does not count operations that are counted by FP_FIXED_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_HP_SCALE_MIN_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed scalable element arithmetic operation, due to an instruction where the smallest type was half-precision floating-point, where v is a value such that (v*(VL/128)) is the number of arithmetic operations carried out by the operation or instruction which causes the event to increment.\nThis event does not count operations that are counted by FP_FIXED_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_BF16_SCALE_MIN_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed scalable element arithmetic operation, due to an instruction where the smallest type was BFloat16 floating-point, where v is a value such that (v*(VL/128)) is the number of arithmetic operations carried out by the operation or instruction which causes the event to increment.\nThis event does not count operations that are counted by FP_FIXED_OPS_SPEC or FP_SCALE2_OPS_SPEC." + }, + { + "ArchStdEvent": "FP_FP8_SCALE_MIN_OPS_SPEC", + "PublicDescription": "This event increments by v for each speculatively executed scalable element arithmetic operation, due to an instruction where the smallest type was 8-bit floating-point, where v is a value such that (v*(VL/128)) is the number of arithmetic operations carried out by the operation or instruction which causes the event to increment.\nThis event does not count operations that are counted by FP_FIXED_OPS_SPEC or FP_SCALE2_OPS_SPEC." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/general.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/general.json new file mode 100644 index 000000000000..bd9c248387aa --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/general.json @@ -0,0 +1,15 @@ +[ + { + "ArchStdEvent": "CPU_CYCLES", + "PublicDescription": "This event counts CPU clock cycles when the PE is not in WFE/WFI. The clock measured by this event is defined as the physical clock driving the CPU logic." + }, + { + "ArchStdEvent": "CNT_CYCLES", + "PublicDescription": "This event increments at a constant frequency equal to the rate of increment of the System Counter, CNTPCT_EL0.\nThis event does not increment when the PE is in WFE/WFI." + }, + { + "EventCode": "0x01e1", + "EventName": "CPU_SLOT", + "PublicDescription": "Entitled CPU slots.\nThis event counts the number of slots. When in ST mode, this event shall increment by PMMIR_EL1.SLOTS quantities, and when in SMT partitioned resource mode (regardless of in WFI state or otherwise), this event is incremented by PMMIR_EL1.SLOTS/2 quantities." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/l1d_cache.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/l1d_cache.json new file mode 100644 index 000000000000..ed6f764eff24 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/l1d_cache.json @@ -0,0 +1,122 @@ +[ + { + "ArchStdEvent": "L1D_CACHE_REFILL", + "PublicDescription": "This event counts L1 D-cache refills caused by speculatively executed load or store operations, preload instructions, or hardware cache prefetching that missed in the L1 D-cache. This event only counts one event per cache line.\nSince the caches are Write-back only for this processor, there are no Write-through cache accesses." + }, + { + "ArchStdEvent": "L1D_CACHE", + "PublicDescription": "This event counts L1 D-cache accesses from any load/store operations, software preload, or hardware prefetch operations. Atomic operations that resolve in the CPU's caches (near atomic operations) count as both a write access and read access. Each access to a cache line is counted including the multiple accesses caused by single instructions such as LDM or STM. Each access to other L1 data or unified memory structures, for example refill buffers, write buffers, and write-back buffers, are also counted.\nThis event counts the sum of the following events:\nL1D_CACHE_RD,\nL1D_CACHE_WR,\nL1D_CACHE_PRFM, and\nL1D_CACHE_HWPRF." + }, + { + "ArchStdEvent": "L1D_CACHE_WB", + "PublicDescription": "This event counts write-backs of dirty data from the L1 D-cache to the L2 cache. This occurs when either a dirty cache line is evicted from L1 D-cache and allocated in the L2 cache or dirty data is written to the L2 and possibly to the next level of cache. This event counts both victim cache line evictions and cache write-backs from snoops or cache maintenance operations. The following cache operations are not counted:\n* Invalidations which do not result in data being transferred out of the L1 (such as evictions of clean data),\n* Full line writes which write to L2 without writing L1, such as write streaming mode.\nThis event is the sum of the following events:\nL1D_CACHE_WB_CLEAN and\nL1D_CACHE_WB_VICTIM." + }, + { + "ArchStdEvent": "L1D_CACHE_LMISS_RD", + "PublicDescription": "This event counts cache line refills into the L1 D-cache from any memory Read operations, that incurred additional latency.\nCounts same as L1D_CACHE_REFILL_RD on this CPU." + }, + { + "ArchStdEvent": "L1D_CACHE_RD", + "PublicDescription": "This event counts L1 D-cache accesses from any Load operation. Atomic Load operations that resolve in the CPU's caches count as both a write access and read access." + }, + { + "ArchStdEvent": "L1D_CACHE_WR", + "PublicDescription": "This event counts L1 D-cache accesses generated by Store operations. This event also counts accesses caused by a DC ZVA (D-cache zero, specified by virtual address) instruction. Near atomic operations that resolve in the CPU's caches count as a write access and read access.\nThis event is a subset of the L1D_CACHE event, except this event only counts memory Write operations." + }, + { + "ArchStdEvent": "L1D_CACHE_REFILL_RD", + "PublicDescription": "This event counts L1 D-cache refills caused by speculatively executed Load instructions where the memory Read operation misses in the L1 D-cache. This event only counts one event per cache line.\nThis event is a subset of the L1D_CACHE_REFILL event, but only counts memory Read operations. This event does not count reads caused by cache maintenance operations or preload instructions." + }, + { + "ArchStdEvent": "L1D_CACHE_REFILL_WR", + "PublicDescription": "This event counts L1 D-cache refills caused by speculatively executed Store instructions where the memory Write operation misses in the L1 D-cache. This event only counts one event per cache line.\nThis event is a subset of the L1D_CACHE_REFILL event, but only counts memory Write operations." + }, + { + "ArchStdEvent": "L1D_CACHE_REFILL_INNER", + "PublicDescription": "This event counts L1 D-cache refills (L1D_CACHE_REFILL) where the cache line data came from caches inside the immediate Cluster of the Core (L2 cache)." + }, + { + "ArchStdEvent": "L1D_CACHE_REFILL_OUTER", + "PublicDescription": "This event counts L1 D-cache refills (L1D_CACHE_REFILL) for which the cache line data came from outside the immediate Cluster of the Core, like an SLC in the system interconnect or DRAM or remote socket." + }, + { + "ArchStdEvent": "L1D_CACHE_WB_VICTIM", + "PublicDescription": "This event counts dirty cache line evictions from the L1 D-cache caused by a new cache line allocation. This event does not count evictions caused by cache maintenance operations.\nThis event is a subset of the L1D_CACHE_WB event, but only counts write-backs that are a result of the line being allocated for an access made by the CPU." + }, + { + "ArchStdEvent": "L1D_CACHE_WB_CLEAN", + "PublicDescription": "This event counts write-backs from the L1 D-cache that are a result of a coherency operation made by another CPU. Event counts include cache maintenance operations.\nThis event is a subset of the L1D_CACHE_WB event." + }, + { + "ArchStdEvent": "L1D_CACHE_INVAL", + "PublicDescription": "This event counts each explicit invalidation of a cache line in the L1 D-cache caused by:\n* Cache Maintenance Operations (CMO) that operate by a virtual address.\n* Broadcast cache coherency operations from another CPU in the system.\nThis event does not count for the following conditions:\n* A cache refill invalidates a cache line.\n* A CMO which is executed on that CPU and invalidates a cache line specified by Set/Way.\nNote that CMOs that operate by Set/Way cannot be broadcast from one CPU to another." + }, + { + "ArchStdEvent": "L1D_CACHE_RW", + "PublicDescription": "This event counts L1 data demand cache accesses from any Load or Store operation. Near atomic operations that resolve in the CPU's caches count as both a write access and read access.\nThis event is implemented as L1D_CACHE_RD + L1D_CACHE_WR" + }, + { + "ArchStdEvent": "L1D_CACHE_PRFM", + "PublicDescription": "This event counts L1 D-cache accesses from software preload or prefetch instructions." + }, + { + "ArchStdEvent": "L1D_CACHE_MISS", + "PublicDescription": "This event counts each demand access counted by L1D_CACHE_RW that misses in the L1 Data or unified cache, causing an access to outside of the L1 caches of this PE." + }, + { + "ArchStdEvent": "L1D_CACHE_REFILL_PRFM", + "PublicDescription": "This event counts L1 D-cache refills where the cache line access was generated by software preload or prefetch instructions." + }, + { + "ArchStdEvent": "L1D_CACHE_HWPRF", + "PublicDescription": "This event counts L1 D-cache accesses from any Load/Store operations generated by the hardware prefetcher." + }, + { + "ArchStdEvent": "L1D_CACHE_REFILL_HWPRF", + "PublicDescription": "This event counts each hardware prefetch access counted by L1D_CACHE_HWPRF that causes a refill of the L1 D-cache from outside of the L1 D-cache." + }, + { + "ArchStdEvent": "L1D_CACHE_HIT_RW_FPRFM", + "PublicDescription": "This event counts each demand access first hit counted by L1D_CACHE_HIT_RW_FPRF where the cache line was fetched in response to a prefetch instruction. That is, the L1D_CACHE_REFILL_PRFM event was generated when the cache line was fetched into the cache.\nOnly the first hit by a demand access is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + }, + { + "ArchStdEvent": "L1D_CACHE_HIT_RW_FHWPRF", + "PublicDescription": "This event counts each demand access first hit counted by L1D_CACHE_HIT_RW_FPRF where the cache line was fetched by a hardware prefetcher. That is, the L1D_CACHE_REFILL_HWPRF Event was generated when the cache line was fetched into the cache.\nOnly the first hit by a demand access is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + }, + { + "ArchStdEvent": "L1D_CACHE_HIT_RW_FPRF", + "PublicDescription": "This event counts each demand access first hit counted by L1D_CACHE_HIT_RW where the cache line was fetched in response to a prefetch instruction or by a hardware prefetcher. That is, the L1D_CACHE_REFILL_PRF event was generated when the cache line was fetched into the cache.\nOnly the first hit by a demand access is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + }, + { + "ArchStdEvent": "L1D_LFB_HIT_RW_FPRFM", + "PublicDescription": "This event counts each demand access line-fill buffer first hit counted by L1D_LFB_HIT_RW_FPRF where the cache line was fetched in response to a prefetch instruction. That is, the access hits a cache line that is in the process of being loaded into the L1 D-cache, and so does not generate a new refill, but has to wait for the previous refill to complete, and the L1D_CACHE_REFILL_PRFM event was generated when the cache line was fetched into the cache.\nOnly the first hit by a demand access is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + }, + { + "ArchStdEvent": "L1D_LFB_HIT_RW_FHWPRF", + "PublicDescription": "This event counts each demand access line-fill buffer first hit counted by L1D_LFB_HIT_RW_FPRF, where the cache line was fetched by a hardware prefetcher. That is, the access hits a cache line that is in the process of being loaded into the L1 D-cache, and so does not generate a new refill, but has to wait for the previous refill to complete, and the L1D_CACHE_REFILL_HWPRF Event was generated when the cache line was fetched into the cache.\nOnly the first hit by a demand access is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + }, + { + "ArchStdEvent": "L1D_LFB_HIT_RW_FPRF", + "PublicDescription": "This event counts each demand access line-fill buffer first hit counted by L1D_LFB_HIT_RW where the cache line was fetched in response to a prefetch instruction or by a hardware prefetcher. That is, the access hits a cache line that is in the process of being loaded into the L1 D-cache, and so does not generate a new refill, but has to wait for the previous refill to complete, and the L1D_CACHE_REFILL_PRF event was generated when the cache line was fetched into the cache.\nOnly the first hit by a demand access is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + }, + { + "EventCode": "0x01f5", + "EventName": "L1D_CACHE_REFILL_RW", + "PublicDescription": "L1 D-cache refill, demand Read and Write. This event counts demand Read and Write accesses that causes a refill of the L1 D-cache of this PE, from outside of this cache." + }, + { + "EventCode": "0x0204", + "EventName": "L1D_CACHE_REFILL_OUTER_LLC", + "PublicDescription": "This event counts L1D_CACHE_REFILL from L3 D-cache." + }, + { + "EventCode": "0x0205", + "EventName": "L1D_CACHE_REFILL_OUTER_DRAM", + "PublicDescription": "This event counts L1D_CACHE_REFILL from local memory." + }, + { + "EventCode": "0x0206", + "EventName": "L1D_CACHE_REFILL_OUTER_REMOTE", + "PublicDescription": "This event counts L1D_CACHE_REFILL from a remote memory." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/l1i_cache.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/l1i_cache.json new file mode 100644 index 000000000000..952454004d98 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/l1i_cache.json @@ -0,0 +1,114 @@ +[ + { + "ArchStdEvent": "L1I_CACHE_REFILL", + "PublicDescription": "This event counts cache line refills in the L1 I-cache caused by a missed instruction fetch (demand, hardware prefetch, and software preload accesses). Instruction fetches may include accessing multiple instructions, but the single cache line allocation is counted once." + }, + { + "ArchStdEvent": "L1I_CACHE", + "PublicDescription": "This event counts instruction fetches (demand, hardware prefetch, and software preload accesses) which access the L1 Instruction Cache. Instruction Cache accesses caused by cache maintenance operations are not counted." + }, + { + "ArchStdEvent": "L1I_CACHE_LMISS", + "PublicDescription": "This event counts cache line refills into the L1 I-cache, that incurred additional latency.\nCounts the same as L1I_CACHE_REFILL in this CPU." + }, + { + "ArchStdEvent": "L1I_CACHE_RD", + "PublicDescription": "This event counts demand instruction fetches which access the L1 I-cache." + }, + { + "ArchStdEvent": "L1I_CACHE_PRFM", + "PublicDescription": "This event counts instruction fetches generated by software preload or prefetch instructions which access the L1 I-cache." + }, + { + "ArchStdEvent": "L1I_CACHE_HWPRF", + "PublicDescription": "This event counts instruction fetches which access the L1 I-cache generated by the hardware prefetcher." + }, + { + "ArchStdEvent": "L1I_CACHE_REFILL_PRFM", + "PublicDescription": "This event counts cache line refills in the L1 I-cache caused by a missed instruction fetch generated by software preload or prefetch instructions. Instruction fetches may include accessing multiple instructions, but the single cache line allocation is counted once." + }, + { + "ArchStdEvent": "L1I_CACHE_REFILL_HWPRF", + "PublicDescription": "This event counts each hardware prefetch access counted by L1I_CACHE_HWPRF that causes a refill of the Level 1I-cache from outside of the L1 I-cache." + }, + { + "ArchStdEvent": "L1I_CACHE_HIT_RD", + "PublicDescription": "This event counts demand instruction fetches that access the L1 I-cache and hit in the L1 I-cache." + }, + { + "ArchStdEvent": "L1I_CACHE_HIT_RD_FPRF", + "PublicDescription": "This event counts each demand fetch first hit counted by L1I_CACHE_HIT_RD where the cache line was fetched in response to a software preload or by a hardware prefetcher. That is, the L1I_CACHE_REFILL_PRF event was generated when the cache line was fetched into the cache.\nOnly the first hit by a demand access is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + }, + { + "ArchStdEvent": "L1I_CACHE_HIT", + "PublicDescription": "This event counts instruction fetches that access the L1 I-cache (demand, hardware prefetch, and software preload accesses) and hit in the L1 I-cache. I-cache accesses caused by cache maintenance operations are not counted." + }, + { + "ArchStdEvent": "L1I_CACHE_HIT_PRFM", + "PublicDescription": "This event counts instruction fetches generated by software preload or prefetch instructions that access the L1 I-cache and hit in the L1 I-cache." + }, + { + "ArchStdEvent": "L1I_LFB_HIT_RD", + "PublicDescription": "This event counts demand instruction fetches that access the L1 I-cache and hit in a line that is in the process of being loaded into the L1 I-cache." + }, + { + "EventCode": "0x0174", + "EventName": "L1I_HWPRF_REQ_DROP", + "PublicDescription": "L1 I-cache hardware prefetch dropped." + }, + { + "EventCode": "0x01e3", + "EventName": "L1I_CACHE_REFILL_RD", + "PublicDescription": "L1 I-cache refill, Read.\nThis event counts demand instruction fetch that causes a refill of the L1 I-cache of this PE, from outside of this cache." + }, + { + "EventCode": "0x01ea", + "EventName": "L1I_CFC_ENTRIES", + "PublicDescription": "This event counts the CFC (Cache Fill Control) entries.\nThe CFC is the fill buffer for I-cache." + }, + { + "EventCode": "0x01ef", + "EventName": "L1I_CACHE_INVAL", + "PublicDescription": "L1 I-cache invalidate.\nThis event counts each explicit invalidation of a cache line in the L1 I-cache caused by:\n* Broadcast cache coherency operations from another CPU in the system.\n* Invalidation dues to capacity eviction in L2 D-cache.\nThis event does not count for the following conditions:\n* A cache refill invalidates a cache line.\n* A CMO which is executed on that CPU Core and invalidates a cache line specified by Set/Way.\n* Cache Maintenance Operations (CMO) that operate by a virtual address.\nNote that\n* CMOs that operate by Set/Way cannot be broadcast from one CPU Core to another.\n* The CMO is treated as No-op for the purposes of L1 I-cache line invalidation, as this Core implements fully coherent I-cache." + }, + { + "EventCode": "0x0212", + "EventName": "L1I_CACHE_HIT_HWPRF", + "PublicDescription": "This event counts each hardware prefetch access that hits an L1 I-cache." + }, + { + "EventCode": "0x0215", + "EventName": "L1I_LFB_HIT", + "PublicDescription": "L1 Line fill buffer hit.\nThis event counts each Demand or software preload or hardware prefetch induced instruction fetch that hits an L1 I-cache line that is in the process of being loaded into the L1 instruction cache, and so does not generate a new refill, but has to wait for the previous refill to complete." + }, + { + "EventCode": "0x0216", + "EventName": "L1I_LFB_HIT_PRFM", + "PublicDescription": "This event counts each software prefetch access that hits a cache line that is in the process of being loaded into the L1 instruction cache, and so does not generate a new refill, but has to wait for the previous refill to complete." + }, + { + "EventCode": "0x0219", + "EventName": "L1I_LFB_HIT_HWPRF", + "PublicDescription": "This event counts each hardware prefetch access that hits a cache line that is in the process of being loaded into the L1 instruction cache, and so does not generate a new refill, but has to wait for the previous refill to complete." + }, + { + "EventCode": "0x0221", + "EventName": "L1I_PRFM_REQ", + "PublicDescription": "L1 I-cache software prefetch requests." + }, + { + "EventCode": "0x0222", + "EventName": "L1I_HWPRF_REQ", + "PublicDescription": "L1 I-cache hardware prefetch requests." + }, + { + "EventCode": "0x0228", + "EventName": "L1I_CACHE_HIT_PRFM_FPRF", + "PublicDescription": "L1 I-cache software prefetch access first hit, fetched by hardware or software prefetch.\nThis event counts each software preload access first hit where the cache line was fetched in response to a hardware prefetcher or software preload instruction.\nOnly the first hit is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + }, + { + "EventCode": "0x022a", + "EventName": "L1I_CACHE_HIT_HWPRF_FPRF", + "PublicDescription": "L1 I-cache hardware prefetch access first hit, fetched by hardware or software prefetch.\nThis event counts each hardware prefetch access first hit where the cache line was fetched in response to a hardware or prefetch instruction.\nOnly the first hit is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/l2d_cache.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/l2d_cache.json new file mode 100644 index 000000000000..66f21a94381e --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/l2d_cache.json @@ -0,0 +1,134 @@ +[ + { + "ArchStdEvent": "L2D_CACHE", + "PublicDescription": "This event counts accesses to the L2 cache due to data accesses. L2 cache is a unified cache for data and instruction accesses. Accesses are for misses in the L1 D-cache or translation resolutions due to accesses. This event also counts write-back of dirty data from L1 D-cache to the L2 cache.\nI-cache accesses are included in this event. This event is the sum of the following events:\nL2D_CACHE_RD,\nL2D_CACHE_WR,\nL2D_CACHE_PRFM, and\nL2D_CACHE_HWPRF." + }, + { + "ArchStdEvent": "L2D_CACHE_REFILL", + "PublicDescription": "This event counts cache line refills into the L2 cache. L2 cache is a unified cache for data and instruction accesses. Accesses are for misses in the L1 D-cache or translation resolutions due to accesses.\nI-cache refills are included in this event. This event is the sum of the following events:\nL2D_CACHE_REFILL_RD,\nL2D_CACHE_REFILL_WR,\nL2D_CACHE_REFILL_HWPRF, and\nL2D_CACHE_REFILL_PRFM." + }, + { + "ArchStdEvent": "L2D_CACHE_WB", + "PublicDescription": "This event counts write-backs of data from the L2 cache to outside the CPU. This includes snoops to the L2 (from other CPUs) which return data even if the snoops cause an invalidation. L2 cache line invalidations which do not write data outside the CPU and snoops which return data from an L1 cache are not counted. Data would not be written outside the cache when invalidating a clean cache line.\nThis event is the sum of the following events:\nL2D_CACHE_WB_VICTIM and\nL2D_CACHE_WB_CLEAN." + }, + { + "ArchStdEvent": "L2D_CACHE_RD", + "PublicDescription": "This event counts L2 D-cache accesses due to memory Read operations. L2 cache is a unified cache for data and instruction accesses, accesses are for misses in the L1 D-cache or translation resolutions due to accesses.\nI-cache accesses are included in this event. This event is a subset of the L2D_CACHE event, but this event only counts memory Read operations." + }, + { + "ArchStdEvent": "L2D_CACHE_WR", + "PublicDescription": "This event counts L2 cache accesses due to memory Write operations. L2 cache is a unified cache for data and instruction accesses, accesses are for misses in the L1 D-cache or translation resolutions due to accesses.\nThis event is a subset of the L2D_CACHE event, but this event only counts memory Write operations." + }, + { + "ArchStdEvent": "L2D_CACHE_REFILL_RD", + "PublicDescription": "This event counts refills for memory accesses due to memory Read operation counted by L2D_CACHE_RD. L2 cache is a unified cache for data and instruction accesses, accesses are for misses in the L1 D-cache or translation resolutions due to accesses.\nThis CPU includes I-cache refills in this counter as an L2I equivalent event was not implemented. This event is a subset of the L2D_CACHE_REFILL event. This event does not count L2 refills caused by stashes into L2.\nThis count includes demand requests that encounter an L2 prefetch request or an L2 software prefetch request to the same cache line, which is still pending in the L2 LFB." + }, + { + "ArchStdEvent": "L2D_CACHE_REFILL_WR", + "PublicDescription": "This event counts refills for memory accesses due to memory Write operation counted by L2D_CACHE_WR. L2 cache is a unified cache for data and instruction accesses, accesses are for misses in the L1 D-cache or translation resolutions due to accesses.\nThis count includes demand requests that encounter an L2 prefetch request or an L2 software prefetch request to the same cache line, which is still pending in the L2 LFB." + }, + { + "ArchStdEvent": "L2D_CACHE_WB_VICTIM", + "PublicDescription": "This event counts evictions from the L2 cache because of a line being allocated into the L2 cache.\nThis event is a subset of the L2D_CACHE_WB event." + }, + { + "ArchStdEvent": "L2D_CACHE_WB_CLEAN", + "PublicDescription": "This event counts write-backs from the L2 cache that are a result of any of the following:\n* Cache maintenance operations,\n* Snoop responses, or\n* Direct cache transfers to another CPU due to a forwarding snoop request.\nThis event is a subset of the L2D_CACHE_WB event." + }, + { + "ArchStdEvent": "L2D_CACHE_INVAL", + "PublicDescription": "This event counts each explicit invalidation of a cache line in the L2 cache by cache maintenance operations that operate by a virtual address, or by external coherency operations. This event does not count if either:\n* A cache refill invalidates a cache line, or\n* A cache Maintenance Operation (CMO), which invalidates a cache line specified by Set/Way,\nis executed on that CPU.\nCMOs that operate by Set/Way cannot be broadcast from one CPU to another." + }, + { + "ArchStdEvent": "L2D_CACHE_LMISS_RD", + "PublicDescription": "This event counts cache line refills into the L2 unified cache from any memory Read operations that incurred additional latency.\nCounts the same as L2D_CACHE_REFILL_RD in this CPU" + }, + { + "ArchStdEvent": "L2D_CACHE_RW", + "PublicDescription": "This event counts L2 cache demand accesses from any Load/Store operations. L2 cache is a unified cache for data and instruction accesses, accesses are for misses in the L1 D-cache or translation resolutions due to accesses.\nI-cache accesses are included in this event.\nThis event is the sum of the following events:\nL2D_CACHE_RD and\nL2D_CACHE_WR." + }, + { + "ArchStdEvent": "L2D_CACHE_PRFM", + "PublicDescription": "This event counts L2 D-cache accesses generated by software preload or prefetch instructions with target = L1/L2/L3 cache.\nNote that a software preload or prefetch instructions with (target = L1/L2/L3) that hits in L1D will not result in an L2 D-cache access. Therefore, such a software preload or prefetch instructions will not be counted by this event." + }, + { + "ArchStdEvent": "L2D_CACHE_MISS", + "PublicDescription": "This event counts cache line misses in the L2 cache. L2 cache is a unified cache for data and instruction accesses. Accesses are for misses in the L1 D-cache or translation resolutions due to accesses.\nThis event counts the same as L2D_CACHE_REFILL_RD in this CPU." + }, + { + "ArchStdEvent": "L2D_CACHE_REFILL_PRFM", + "PublicDescription": "This event counts refills due to accesses generated as a result of software preload or prefetch instructions as counted by L2D_CACHE_PRFM. I-cache refills are included in this event." + }, + { + "ArchStdEvent": "L2D_CACHE_HWPRF", + "PublicDescription": "This event counts the L2 D-cache access caused by L1 or L2 hardware prefetcher." + }, + { + "ArchStdEvent": "L2D_CACHE_REFILL_HWPRF", + "PublicDescription": "This event counts each hardware prefetch access counted by L2D_CACHE_HWPRF that causes a refill of the L2 cache, or any L1 Data, or Instruction cache of this PE, from outside of those caches.\nThis does not include prefetch requests pending waiting for a refill in LFB and a new demand request to the same cache line hitting the LFB entry. All such refills are counted as L2D_LFB_HIT_RWL1PRF_FHWPRF." + }, + { + "ArchStdEvent": "L2D_CACHE_REFILL_PRF", + "PublicDescription": "This event counts each access to L2 Cache due to a prefetch instruction, or hardware prefetch that causes a refill of the L2 or any Level 1, from outside of those caches." + }, + { + "EventCode": "0x0108", + "EventName": "L2D_CACHE_IF_REFILL", + "PublicDescription": "L2 D-cache refill, instruction fetch.\nThis event counts demand instruction fetch that causes a refill of the L2 cache or L1 cache of this PE, from outside of those caches." + }, + { + "EventCode": "0x0109", + "EventName": "L2D_CACHE_TBW_REFILL", + "PublicDescription": "L2 D-cache refill, Page table walk.\nThis event counts demand translation table walk that causes a refill of the L2 cache or L1 cache of this PE, from outside of those caches." + }, + { + "EventCode": "0x010a", + "EventName": "L2D_CACHE_PF_REFILL", + "PublicDescription": "L2 D-cache refill, prefetch.\nThis event counts L1 or L2 hardware or software prefetch accesses that causes a refill of the L2 cache or L1 cache of this PE, from outside of those caches." + }, + { + "EventCode": "0x010b", + "EventName": "L2D_LFB_HIT_RWL1PRF_FHWPRF", + "PublicDescription": "L2 line fill buffer demand Read, demand Write or L1 prefetch first hit, fetched by hardware prefetch.\nThis event counts each of the following access that hit the line-fill buffer when the same cache line is already being fetched due to an L2 hardware prefetcher.\n* Demand Read or Write\n* L1I-HWPRF\n* L1D-HWPRF\n* L1I PRFM\n* L1D PRFM\nThese accesses hit a cache line that is currently being loaded into the L2 cache as a result of a hardware prefetcher to the same line. Consequently, this access does not initiate a new refill but waits for the completion of the previous refill.\nOnly the first hit is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + }, + { + "EventCode": "0x0179", + "EventName": "L2D_CACHE_HIT_RWL1PRF_FHWPRF", + "PublicDescription": "L2 D-cache demand Read, demand Write and L1 prefetch hit, fetched by hardware prefetch. This event counts each demand Read, demand Write and L1 hardware or software prefetch request that hit an L2 D-cache line that was refilled into L2 D-cache in response to an L2 hardware prefetch. Only the first hit is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + }, + { + "EventCode": "0x01b8", + "EventName": "L2D_CACHE_L1PRF", + "PublicDescription": "L2 D-cache access, L1 hardware or software prefetch. This event counts L1 Hardware or software prefetch access to L2 D-cache." + }, + { + "EventCode": "0x01b9", + "EventName": "L2D_CACHE_REFILL_L1PRF", + "PublicDescription": "L2 D-cache refill, L1 hardware or software prefetch.\nThis event counts each access counted by L2D_CACHE_L1PRF that causes a refill of the L2 cache or any L1 cache of this PE, from outside of those caches." + }, + { + "EventCode": "0x0201", + "EventName": "L2D_CACHE_BACKSNOOP_L1D_VIRT_ALIASING", + "PublicDescription": "This event counts when the L2 D-cache sends an invalidating back-snoop to the L1 D for an access initiated by the L1 D, where the corresponding line is already present in the L1 D-cache.\nThe L2 D-cache line tags the PE that refilled the line. It also retains specific bits of the VA to identify virtually aliased addresses.\nThe L1 D request requiring a back-snoop can originate either from the same PE that refilled the L2 D line or from a different PE. In either case, this event only counts those back snoop where the requested VA mismatch the VA stored in the L2 D tag.\nThis event is counted only by PE that initiated the original request necessitating a back-snoop.\nNote : The L1 D is VIPT, it identifies this access as a miss. Conversely, as L2 is PIPT, it identifies this as a hit. L2 D utilizes the back-snoop mechanism to refill L1 D with the snooped data." + }, + { + "EventCode": "0x0208", + "EventName": "L2D_CACHE_RWL1PRF", + "PublicDescription": "L2 D-cache access, demand Read, demand Write or L1 hardware or software prefetch.\nThis event counts each access to L2 D-cache due to the following:\n* Demand Read or Write.\n* L1 Hardware or software prefetch." + }, + { + "EventCode": "0x020a", + "EventName": "L2D_CACHE_REFILL_RWL1PRF", + "PublicDescription": "L2 D-cache refill, demand Read, demand Write or L1 hardware or software prefetch.\nThis event counts each access counted by L2D_CACHE_RWL1PRF that causes a refill of the L2 cache, or any L1 cache of this PE, from outside of those caches." + }, + { + "EventCode": "0x020c", + "EventName": "L2D_CACHE_HIT_RWL1PRF_FPRFM", + "PublicDescription": "L2 D-cache demand Read, demand Write and L1 prefetch hit, fetched by software prefetch.\nThis event counts each demand Read, demand Write and L1 hardware or software prefetch request that hit an L2 D-cache line that was refilled into L2 D-cache in response to an L2 software prefetch. Only the first hit is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + }, + { + "EventCode": "0x020e", + "EventName": "L2D_CACHE_HIT_RWL1PRF_FPRF", + "PublicDescription": "L2 D-cache demand Read, demand Write and L1 prefetch hit, fetched by software or hardware prefetch.\nThis event counts each demand Read, demand Write and L1 hardware or software prefetch request that hit an L2 D-cache line that was refilled into L2 D-cache in response to an L2 hardware prefetch or software prefetch. Only the first hit is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/ll_cache.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/ll_cache.json new file mode 100644 index 000000000000..851d0a70de9c --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/ll_cache.json @@ -0,0 +1,107 @@ +[ + { + "ArchStdEvent": "L3D_CACHE_ALLOCATE", + "PublicDescription": "This event counts each memory Write operation that writes an entire line into the L3 data without fetching data from outside the L3 Data. These are allocations of cache lines in the L3 Data that are not refills counted by\nL3D_CACHE_REFILL. For example:\nA Write-back of an entire cache line from an L2 cache to the L3 D-cache.\n* A Write of an entire cache line from a coalescing Write buffer.\n* An operation such as DC ZVA.\nThis counter does not count writes that write an entire line to beyond level 3. Thus this counter does not count the streaming writes to beyond L3 cache." + }, + { + "ArchStdEvent": "L3D_CACHE_REFILL", + "PublicDescription": "This event counts each access counted by L3D_CACHE that causes a refill of the L3 Data, or any L1 Data, instruction or L2 cache of this PE, from outside of those caches. This includes the refill due to hardware prefetch and software prefetch accesses.\nThis event is a sum of L3D_CACHE_MISS, L3D_CACHE_REFILL_PRFM and L3D_CACHE_REFILL_HWPRF event.\nA refill includes any access that causes data to be fetched from outside of the L1 to L3 caches, even if the data is ultimately not allocated into the L3 D-cache." + }, + { + "ArchStdEvent": "L3D_CACHE", + "PublicDescription": "This event counts each memory Read operation or memory Write operation that causes a cache access to the Level 3.\nThis event is a sum of the following Events:\n* L3D_CACHE_RD(0x00a0)\n* L3D_CACHE_ALLOCATE(0x0029)\n* L3D_CACHE_PRFM(0x8151)\n* L3D_CACHE_HWPRF(0x8156)\n* L2D_CACHE_WB(0x0018)" + }, + { + "ArchStdEvent": "LL_CACHE_RD", + "PublicDescription": "This is an alias to the event L3D_CACHE_RD (0x00a0)." + }, + { + "ArchStdEvent": "LL_CACHE_MISS_RD", + "PublicDescription": "This is an alias to the event L3D_CACHE_REFILL_RD (0x00a2)." + }, + { + "ArchStdEvent": "L3D_CACHE_RD", + "PublicDescription": "This event counts each Memory Read operation to L3 D-cache from instruction fetch, Load/Store, and MMU translation table accesses. This does not include hardware prefetcher or PRFM instruction accesses. This include L1 and L2 prefetcher accesses to L3 D-cache." + }, + { + "ArchStdEvent": "L3D_CACHE_REFILL_RD", + "PublicDescription": "This event counts each access counted by both L3D_CACHE_RD and L3D_CACHE_REFILL. That is, every refill of the L3 cache counted by L3D_CACHE_REFILL that is caused by a Memory Read operation.\nThe L3D_CACHE_MISS(0x8152), L3D_CACHE_REFILL_RD (0x00a2) and L3D_CACHE_LMISS_RD(0x400b) count the same event in the hardware." + }, + { + "ArchStdEvent": "L3D_CACHE_LMISS_RD", + "PublicDescription": "This event counts each memory Read operation to the L3 cache counted by L3D_CACHE that incurs additional latency because it returns data from outside of the L1 to L3 caches.\nThe L3D_CACHE_MISS(0x8152), L3D_CACHE_REFILL_RD (0x00a2) and L3D_CACHE_LMISS_RD(0x400b) count the same event in the hardware." + }, + { + "ArchStdEvent": "L3D_CACHE_RW", + "PublicDescription": "This event counts each access counted by L3D_CACHE that is due to a demand memory Read operation or demand memory Write operation.\nThis event is a sum of L3D_CACHE_RD(0x00a0), L3D_CACHE_ALLOCATE(0x0029) and L2D_CACHE_WB(0x0018).\nNote that this counter does not count that writes an entire line to beyond level 3. Thus this counter does not count the streaming Writes to beyond L3 cache." + }, + { + "ArchStdEvent": "L3D_CACHE_PRFM", + "PublicDescription": "This event counts each access counted by L3D_CACHE that is due to a prefetch instruction. This includes L3 Data accesses due to the L1, L2, or L3 prefetch instruction." + }, + { + "ArchStdEvent": "L3D_CACHE_MISS", + "PublicDescription": "This event counts each demand Read access counted by L3D_CACHE_RD that misses in the L1 to L3 Data, causing an access to outside of the L3 cache.\nThe L3D_CACHE_MISS(0x8152), L3D_CACHE_REFILL_RD (0x00a2) and L3D_CACHE_LMISS_RD(0x400b) count the same event in the hardware." + }, + { + "ArchStdEvent": "L3D_CACHE_REFILL_PRFM", + "PublicDescription": "This event counts each access counted by L3D_CACHE_PRFM that causes a refill of the L3 cache, or any L1 or L2 Data, from outside of those caches." + }, + { + "ArchStdEvent": "L3D_CACHE_HWPRF", + "PublicDescription": "This event counts each access to L3 cache that is due to a hardware prefetcher. This includes L3D accesses due to the Level-1 or Level-2 or Level-3 hardware prefetcher." + }, + { + "ArchStdEvent": "L3D_CACHE_REFILL_HWPRF", + "PublicDescription": "This event counts each hardware prefetch counted by L3D_CACHE_HWPRF that causes a refill of the L3 Data or unified cache, or any L1 or L2 Data, Instruction, or unified cache of this PE, from outside of those caches." + }, + { + "ArchStdEvent": "L3D_CACHE_REFILL_PRF", + "PublicDescription": "This event counts each access to L3 cache due to a prefetch instruction, or hardware prefetch that causes a refill of the L3 Data, or any L1 or L2 Data, from outside of those caches." + }, + { + "EventCode": "0x01e8", + "EventName": "L3D_CACHE_RWL1PRFL2PRF", + "PublicDescription": "L3 cache access, demand Read, demand Write, L1 hardware or software prefetch or L2 hardware or software prefetch.\nThis event counts each access to L3 D-cache due to the following:\n* Demand Read or Write.\n* L1 Hardware or software prefetch.\n* L2 Hardware or software prefetch." + }, + { + "EventCode": "0x01e9", + "EventName": "L3D_CACHE_REFILL_RWL1PRFL2PRF", + "PublicDescription": "L3 cache refill, demand Read, demand Write, L1 hardware or software prefetch or L2 hardware or software prefetch.\nThis event counts each access counted by L3D_CACHE_RWL1PRFL2PRF that causes a refill of the L3 cache, or any L1 or L2 cache of this PE, from outside of those caches." + }, + { + "EventCode": "0x01f6", + "EventName": "L3D_CACHE_REFILL_L2PRF", + "PublicDescription": "This event counts each access counted by L3D_CACHE_L2PRF that causes a refill of the L3 cache, or any L1 or L2 cache of this PE, from outside of those caches." + }, + { + "EventCode": "0x01f7", + "EventName": "L3D_CACHE_HIT_RWL1PRFL2PRF_FPRF", + "PublicDescription": "L3 cache demand Read, demand Write, L1 prefetch L2 prefetch first hit, fetched by software or hardware prefetch.\nThis event counts each demand Read, demand Write, L1 hardware or software prefetch request and L2 hardware or software prefetch that hit an L3 D-cache line that was refilled into L3 D-cache in response to an L3 hardware prefetch or software prefetch. Only the first hit is counted. After this event is generated for a cache line, the event is not generated again for the same cache line while it remains in the cache." + }, + { + "EventCode": "0x0225", + "EventName": "L3D_CACHE_REFILL_IF", + "PublicDescription": "L3 cache refill, instruction fetch.\nThis event counts demand instruction fetch that causes a refill of the L3 cache, or any L1 or L2 cache of this PE, from outside of those caches." + }, + { + "EventCode": "0x0226", + "EventName": "L3D_CACHE_REFILL_MM", + "PublicDescription": "L3 cache refill, translation table walk access.\nThis event counts demand translation table access that causes a refill of the L3 cache, or any L1 or L2 cache of this PE, from outside of those caches." + }, + { + "EventCode": "0x0227", + "EventName": "L3D_CACHE_REFILL_L1PRF", + "PublicDescription": "This event counts each access counted by L3D_CACHE_L1PRF that causes a refill of the L3 cache, or any L1 or L2 cache of this PE, from outside of those caches." + }, + { + "EventCode": "0x022c", + "EventName": "L3D_CACHE_L1PRF", + "PublicDescription": "This event counts the L3 D-cache access due to L1 hardware prefetch or software prefetch request.\nThe L1 hardware prefetch or software prefetch requests that miss the L1I, L1D and L2 D-cache are counted by this counter" + }, + { + "EventCode": "0x022d", + "EventName": "L3D_CACHE_L2PRF", + "PublicDescription": "This event counts the L3 D-cache access due to L2 hardware prefetch or software prefetch request.\nThe L2 hardware prefetch or software prefetch requests that miss the L2 D-cache are counted by this counter" + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/memory.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/memory.json new file mode 100644 index 000000000000..becd2d90bf39 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/memory.json @@ -0,0 +1,46 @@ +[ + { + "ArchStdEvent": "MEM_ACCESS", + "PublicDescription": "This event counts memory accesses issued by the CPU load/store unit, where those accesses are issued due to load or store operations. This event counts memory accesses regardless of whether the data is received from any level of cache hierarchy or external memory. If memory accesses are broken up into smaller transactions than what were specified in the load or store instructions, then the event counts those smaller memory transactions.\nMemory accesses generated by the following instructions or activity are not counted: instruction fetches, cache maintenance instructions, translation table walks or prefetches, memory prefetch operations. This event counts the sum of the following events:\nMEM_ACCESS_RD and\nMEM_ACCESS_WR." + }, + { + "ArchStdEvent": "MEMORY_ERROR", + "PublicDescription": "This event counts any detected correctable or uncorrectable physical memory errors (ECC or parity) in protected CPU RAMs. On the Core, this event counts errors in the caches (including data and tag RAMs). Any detected memory error (from either a speculative and abandoned access, or an architecturally executed access) is counted.\nNote that errors are only detected when the actual protected memory is accessed by an operation." + }, + { + "ArchStdEvent": "REMOTE_ACCESS", + "PublicDescription": "This event counts each external bus read access that causes an access to a remote device. That is, a socket that does not contain the PE." + }, + { + "ArchStdEvent": "MEM_ACCESS_RD", + "PublicDescription": "This event counts memory accesses issued by the CPU due to Load operations. This event counts any memory Load access, no matter whether the data is received from any level of cache hierarchy or external memory. This event also counts atomic Load operations. If memory accesses are broken up by the Load/Store unit into smaller transactions that are issued by the bus interface, then the event counts those smaller transactions.\nThe following instructions are not counted:\n1) Instruction fetches,\n2) Cache maintenance instructions,\n3) Translation table walks or prefetches,\n4) Memory prefetch operations.\nThis event is a subset of the MEM_ACCESS event but the event only counts memory-Read operations." + }, + { + "ArchStdEvent": "MEM_ACCESS_WR", + "PublicDescription": "This event counts memory accesses issued by the CPU due to Store operations. This event counts any memory Store access, no matter whether the data is located in any level of cache or external memory. This event also counts atomic Load and Store operations. If memory accesses are broken up by the Load/Store unit into smaller transactions that are issued by the bus interface, then the event counts those smaller transactions." + }, + { + "ArchStdEvent": "LDST_ALIGN_LAT", + "PublicDescription": "This event counts the number of memory Read and Write accesses in a cycle that incurred additional latency due to the alignment of the address and the size of data being accessed, which results in a store crossing a single cache line.\nThis event is implemented as the sum of the following events on this CPU:\nLD_ALIGN_LAT and\nST_ALIGN_LAT." + }, + { + "ArchStdEvent": "LD_ALIGN_LAT", + "PublicDescription": "This event counts the number of memory Read accesses in a cycle that incurred additional latency due to the alignment of the address and size of data being accessed, which results in a load crossing a single cache line." + }, + { + "ArchStdEvent": "ST_ALIGN_LAT", + "PublicDescription": "This event counts the number of memory Write accesses in a cycle that incurred additional latency due to the alignment of the address and size of data being accessed." + }, + { + "ArchStdEvent": "INST_FETCH_PERCYC", + "PublicDescription": "This event counts number of instruction fetches outstanding per cycle, which will provide an average latency of instruction fetch." + }, + { + "ArchStdEvent": "MEM_ACCESS_RD_PERCYC", + "PublicDescription": "This event counts the number of outstanding Loads or memory Read accesses per cycle." + }, + { + "ArchStdEvent": "INST_FETCH", + "PublicDescription": "This event counts instruction memory accesses that the PE makes." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/metrics.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/metrics.json new file mode 100644 index 000000000000..b825ede03f54 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/metrics.json @@ -0,0 +1,722 @@ +[ + { + "MetricName": "backend_bound", + "MetricExpr": "100 * (STALL_SLOT_BACKEND / CPU_SLOT)", + "BriefDescription": "This metric is the percentage of total slots that were stalled due to resource constraints in the backend of the processor.", + "ScaleUnit": "1percent of slots", + "MetricGroup": "TopdownL1" + }, + { + "MetricName": "backend_busy_bound", + "MetricExpr": "100 * (STALL_BACKEND_BUSY / STALL_BACKEND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the backend due to issue queues being full to accept operations for execution.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Backend" + }, + { + "MetricName": "backend_cache_l1d_bound", + "MetricExpr": "100 * (STALL_BACKEND_L1D / (STALL_BACKEND_L1D + STALL_BACKEND_MEM))", + "BriefDescription": "This metric is the percentage of total cycles stalled in the backend due to memory access latency issues caused by L1 D-cache misses.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Backend" + }, + { + "MetricName": "backend_cache_l2d_bound", + "MetricExpr": "100 * (STALL_BACKEND_MEM / (STALL_BACKEND_L1D + STALL_BACKEND_MEM))", + "BriefDescription": "This metric is the percentage of total cycles stalled in the backend due to memory access latency issues caused by L2 D-cache misses.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Backend" + }, + { + "MetricName": "backend_core_bound", + "MetricExpr": "100 * (STALL_BACKEND_CPUBOUND / STALL_BACKEND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the backend due to backend Core resource constraints not related to instruction fetch latency issues caused by memory access components.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Backend" + }, + { + "MetricName": "backend_core_rename_bound", + "MetricExpr": "100 * (STALL_BACKEND_RENAME / STALL_BACKEND_CPUBOUND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the backend as the rename unit registers are unavailable.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Backend" + }, + { + "MetricName": "backend_mem_bound", + "MetricExpr": "100 * (STALL_BACKEND_MEMBOUND / STALL_BACKEND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the backend due to backend Core resource constraints related to memory access latency issues caused by memory access components.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Backend" + }, + { + "MetricName": "backend_mem_cache_bound", + "MetricExpr": "100 * ((STALL_BACKEND_L1D + STALL_BACKEND_MEM) / STALL_BACKEND_MEMBOUND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the backend due to memory latency issues caused by D-cache misses.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Backend" + }, + { + "MetricName": "backend_mem_store_bound", + "MetricExpr": "100 * (STALL_BACKEND_ST / STALL_BACKEND_MEMBOUND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the backend due to memory Write pending caused by Stores stalled in the pre-commit stage.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Backend" + }, + { + "MetricName": "backend_mem_tlb_bound", + "MetricExpr": "100 * (STALL_BACKEND_TLB / STALL_BACKEND_MEMBOUND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the backend due to memory access latency issues caused by Data TLB misses.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Backend" + }, + { + "MetricName": "backend_stalled_cycles", + "MetricExpr": "100 * (STALL_BACKEND / CPU_CYCLES)", + "BriefDescription": "This metric is the percentage of cycles that were stalled due to resource constraints in the backend unit of the processor.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Cycle_Accounting" + }, + { + "MetricName": "bad_speculation", + "MetricExpr": "100 - (frontend_bound + retiring + backend_bound)", + "BriefDescription": "This metric is the percentage of total slots that executed operations and didn't retire due to a pipeline flush. This indicates cycles that were utilized but inefficiently.", + "ScaleUnit": "1percent of slots", + "MetricGroup": "TopdownL1" + }, + { + "MetricName": "barrier_percentage", + "MetricExpr": "100 * ((ISB_SPEC + DSB_SPEC + DMB_SPEC) / INST_SPEC)", + "BriefDescription": "This metric measures instruction and data barrier operations as a percentage of operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "Operation_Mix" + }, + { + "MetricName": "branch_direct_ratio", + "MetricExpr": "BR_IMMED_RETIRED / BR_RETIRED", + "BriefDescription": "This metric measures the ratio of direct branches retired to the total number of branches architecturally executed.", + "ScaleUnit": "1per branch", + "MetricGroup": "Branch_Effectiveness" + }, + { + "MetricName": "branch_indirect_ratio", + "MetricExpr": "BR_IND_RETIRED / BR_RETIRED", + "BriefDescription": "This metric measures the ratio of indirect branches retired, including function returns, to the total number of branches architecturally executed.", + "ScaleUnit": "1per branch", + "MetricGroup": "Branch_Effectiveness" + }, + { + "MetricName": "branch_misprediction_ratio", + "MetricExpr": "BR_MIS_PRED_RETIRED / BR_RETIRED", + "BriefDescription": "This metric measures the ratio of branches mispredicted to the total number of branches architecturally executed. This gives an indication of the effectiveness of the branch prediction unit.", + "ScaleUnit": "1per branch", + "MetricGroup": "Miss_Ratio;Branch_Effectiveness" + }, + { + "MetricName": "branch_mpki", + "MetricExpr": "1000 * (BR_MIS_PRED_RETIRED / INST_RETIRED)", + "BriefDescription": "This metric measures the number of branch mispredictions per thousand instructions executed.", + "ScaleUnit": "1MPKI", + "MetricGroup": "MPKI;Branch_Effectiveness" + }, + { + "MetricName": "branch_percentage", + "MetricExpr": "100 * ((BR_IMMED_SPEC + BR_INDIRECT_SPEC) / INST_SPEC)", + "BriefDescription": "This metric measures branch operations as a percentage of operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "Operation_Mix" + }, + { + "MetricName": "branch_return_ratio", + "MetricExpr": "BR_RETURN_RETIRED / BR_RETIRED", + "BriefDescription": "This metric measures the ratio of branches retired that are function returns to the total number of branches architecturally executed.", + "ScaleUnit": "1per branch", + "MetricGroup": "Branch_Effectiveness" + }, + { + "MetricName": "bus_bandwidth", + "MetricExpr": "BUS_ACCESS * 32 / duration_time ", + "BriefDescription": "This metric measures the bus-bandwidth of the data transferred between this PE's L2 with unCore in the system.", + "ScaleUnit": "1Bytes/sec" + }, + { + "MetricName": "cpu_cycles_fraction_in_st_mode", + "MetricExpr": "((CPU_SLOT/CPU_CYCLES) - 5) / 5", + "BriefDescription": "This metric counts fraction of the CPU cycles spent in ST mode during program execution.", + "ScaleUnit": "1fraction of cycles", + "MetricGroup": "SMT" + }, + { + "MetricName": "cpu_cycles_in_smt_mode", + "MetricExpr": "(1 - cpu_cycles_fraction_in_st_mode) * CPU_CYCLES", + "BriefDescription": "This metric counts CPU cycles in SMT mode during program execution.", + "ScaleUnit": "1CPU cycles", + "MetricGroup": "SMT" + }, + { + "MetricName": "cpu_cycles_in_st_mode", + "MetricExpr": "cpu_cycles_fraction_in_st_mode * CPU_CYCLES", + "BriefDescription": "This metric counts CPU cycles in ST mode during program execution.", + "ScaleUnit": "1CPU cycles", + "MetricGroup": "SMT" + }, + { + "MetricName": "crypto_percentage", + "MetricExpr": "100 * (CRYPTO_SPEC / INST_SPEC)", + "BriefDescription": "This metric measures crypto operations as a percentage of operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "Operation_Mix" + }, + { + "MetricName": "dtlb_mpki", + "MetricExpr": "1000 * (DTLB_WALK / INST_RETIRED)", + "BriefDescription": "This metric measures the number of Data TLB Walks per thousand instructions executed.", + "ScaleUnit": "1MPKI", + "MetricGroup": "MPKI;DTLB_Effectiveness" + }, + { + "MetricName": "dtlb_walk_average_latency", + "MetricExpr": "DTLB_WALK_PERCYC / DTLB_WALK", + "BriefDescription": "This metric measures the average latency of Data TLB walks in CPU cycles.", + "ScaleUnit": "1CPU cycles", + "MetricGroup": "Average_Latency" + }, + { + "MetricName": "dtlb_walk_ratio", + "MetricExpr": "DTLB_WALK / L1D_TLB", + "BriefDescription": "This metric measures the ratio of Data TLB Walks to the total number of Data TLB accesses. This gives an indication of the effectiveness of the Data TLB accesses.", + "ScaleUnit": "1per TLB access", + "MetricGroup": "Miss_Ratio;DTLB_Effectiveness" + }, + { + "MetricName": "fp16_percentage", + "MetricExpr": "100 * (FP_HP_SPEC / INST_SPEC)", + "BriefDescription": "This metric measures half-precision floating point operations as a percentage of operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "FP_Precision_Mix" + }, + { + "MetricName": "fp32_percentage", + "MetricExpr": "100 * (FP_SP_SPEC / INST_SPEC)", + "BriefDescription": "This metric measures single-precision floating point operations as a percentage of operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "FP_Precision_Mix" + }, + { + "MetricName": "fp64_percentage", + "MetricExpr": "100 * (FP_DP_SPEC / INST_SPEC)", + "BriefDescription": "This metric measures double-precision floating point operations as a percentage of operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "FP_Precision_Mix" + }, + { + "MetricName": "fp_ops_per_cycle", + "MetricExpr": "(FP_SCALE_OPS_SPEC + FP_FIXED_OPS_SPEC) / CPU_CYCLES", + "BriefDescription": "This metric measures floating point operations per cycle in any precision performed by any instruction. Operations are counted by computation and by vector lanes, fused computations such as multiply-add count as twice per vector lane for example.", + "ScaleUnit": "1operations per cycle", + "MetricGroup": "FP_Arithmetic_Intensity" + }, + { + "MetricName": "frontend_bound", + "MetricExpr": "100 * (STALL_SLOT_FRONTEND_WITHOUT_MISPRED / CPU_SLOT)", + "BriefDescription": "This metric is the percentage of total slots that were stalled due to resource constraints in the frontend of the processor.", + "ScaleUnit": "1percent of slots", + "MetricGroup": "TopdownL1" + }, + { + "MetricName": "frontend_cache_l1i_bound", + "MetricExpr": "100 * (STALL_FRONTEND_L1I / (STALL_FRONTEND_L1I + STALL_FRONTEND_MEM))", + "BriefDescription": "This metric is the percentage of total cycles stalled in the frontend due to memory access latency issues caused by L1 I-cache misses.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Frontend" + }, + { + "MetricName": "frontend_cache_l2i_bound", + "MetricExpr": "100 * (STALL_FRONTEND_MEM / (STALL_FRONTEND_L1I + STALL_FRONTEND_MEM))", + "BriefDescription": "This metric is the percentage of total cycles stalled in the frontend due to memory access latency issues caused by L2 I-cache misses.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Frontend" + }, + { + "MetricName": "frontend_core_bound", + "MetricExpr": "100 * (STALL_FRONTEND_CPUBOUND / STALL_FRONTEND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the frontend due to frontend Core resource constraints not related to instruction fetch latency issues caused by memory access components.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Frontend" + }, + { + "MetricName": "frontend_core_flow_bound", + "MetricExpr": "100 * (STALL_FRONTEND_FLOW / STALL_FRONTEND_CPUBOUND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the frontend as the decode unit is awaiting input from the branch prediction unit.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Frontend" + }, + { + "MetricName": "frontend_core_flush_bound", + "MetricExpr": "100 * (STALL_FRONTEND_FLUSH / STALL_FRONTEND_CPUBOUND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the frontend as the processor is recovering from a pipeline flush caused by bad speculation or other machine resteers.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Frontend" + }, + { + "MetricName": "frontend_mem_bound", + "MetricExpr": "100 * (STALL_FRONTEND_MEMBOUND / STALL_FRONTEND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the frontend due to frontend Core resource constraints related to the instruction fetch latency issues caused by memory access components.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Frontend" + }, + { + "MetricName": "frontend_mem_cache_bound", + "MetricExpr": "100 * ((STALL_FRONTEND_L1I + STALL_FRONTEND_MEM) / STALL_FRONTEND_MEMBOUND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the frontend due to instruction fetch latency issues caused by I-cache misses.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Frontend" + }, + { + "MetricName": "frontend_mem_tlb_bound", + "MetricExpr": "100 * (STALL_FRONTEND_TLB / STALL_FRONTEND_MEMBOUND)", + "BriefDescription": "This metric is the percentage of total cycles stalled in the frontend due to instruction fetch latency issues caused by Instruction TLB misses.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Topdown_Frontend" + }, + { + "MetricName": "frontend_stalled_cycles", + "MetricExpr": "100 * (STALL_FRONTEND / CPU_CYCLES)", + "BriefDescription": "This metric is the percentage of cycles that were stalled due to resource constraints in the frontend unit of the processor.", + "ScaleUnit": "1percent of cycles", + "MetricGroup": "Cycle_Accounting" + }, + { + "MetricName": "instruction_fetch_average_latency", + "MetricExpr": "INST_FETCH_PERCYC / INST_FETCH", + "BriefDescription": "This metric measures the average latency of instruction fetches in CPU cycles.", + "ScaleUnit": "1CPU cycles", + "MetricGroup": "Average_Latency" + }, + { + "MetricName": "integer_dp_percentage", + "MetricExpr": "100 * (DP_SPEC / INST_SPEC)", + "BriefDescription": "This metric measures scalar integer operations as a percentage of operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "Operation_Mix" + }, + { + "MetricName": "ipc", + "MetricExpr": "INST_RETIRED / CPU_CYCLES", + "BriefDescription": "This metric measures the number of instructions retired per cycle.", + "ScaleUnit": "1per cycle", + "MetricGroup": "General" + }, + { + "MetricName": "itlb_mpki", + "MetricExpr": "1000 * (ITLB_WALK / INST_RETIRED)", + "BriefDescription": "This metric measures the number of instruction TLB Walks per thousand instructions executed.", + "ScaleUnit": "1MPKI", + "MetricGroup": "MPKI;ITLB_Effectiveness" + }, + { + "MetricName": "itlb_walk_average_latency", + "MetricExpr": "ITLB_WALK_PERCYC / ITLB_WALK", + "BriefDescription": "This metric measures the average latency of instruction TLB walks in CPU cycles.", + "ScaleUnit": "1CPU cycles", + "MetricGroup": "Average_Latency" + }, + { + "MetricName": "itlb_walk_ratio", + "MetricExpr": "ITLB_WALK / L1I_TLB", + "BriefDescription": "This metric measures the ratio of instruction TLB Walks to the total number of Instruction TLB accesses. This gives an indication of the effectiveness of the Instruction TLB accesses.", + "ScaleUnit": "1per TLB access", + "MetricGroup": "Miss_Ratio;ITLB_Effectiveness" + }, + { + "MetricName": "l1d_cache_miss_ratio", + "MetricExpr": "L1D_CACHE_REFILL / L1D_CACHE", + "BriefDescription": "This metric measures the ratio of L1 D-cache accesses missed to the total number of L1 D-cache accesses. This gives an indication of the effectiveness of the L1 D-cache.", + "ScaleUnit": "1per cache access", + "MetricGroup": "Miss_Ratio;L1D_Cache_Effectiveness" + }, + { + "MetricName": "l1d_cache_mpki", + "MetricExpr": "1000 * (L1D_CACHE_REFILL / INST_RETIRED)", + "BriefDescription": "This metric measures the number of L1 D-cache accesses missed per thousand instructions executed.", + "ScaleUnit": "1MPKI", + "MetricGroup": "MPKI;L1D_Cache_Effectiveness" + }, + { + "MetricName": "l1d_cache_rw_miss_ratio", + "MetricExpr": "l1d_demand_misses / l1d_demand_accesses", + "BriefDescription": "This metric measures the ratio of L1 D-cache Read accesses missed to the total number of L1 D-cache accesses. This gives an indication of the effectiveness of the L1 D-cache for demand Load or Store traffic.", + "ScaleUnit": "1per cache access", + "MetricGroup": "L1I_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1d_demand_accesses", + "MetricExpr": "L1D_CACHE_RW", + "BriefDescription": "This metric measures the count of L1 D-cache accesses incurred on Load or Store by the instruction stream of the program.", + "ScaleUnit": "1count", + "MetricGroup": "L1I_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1d_demand_misses", + "MetricExpr": "L1D_CACHE_REFILL_RW", + "BriefDescription": "This metric measures the count of L1 D-cache misses incurred on a Load or Store by the instruction stream of the program.", + "ScaleUnit": "1count", + "MetricGroup": "L1I_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1d_prf_accuracy", + "MetricExpr": "100 * (l1d_useful_prf / l1d_refilled_prf)", + "BriefDescription": "This metric measures the fraction of prefetched memory addresses that are used by the instruction stream.", + "ScaleUnit": "1percent of prefetch", + "MetricGroup": "L1I_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1d_prf_coverage", + "MetricExpr": "100 * (l1d_useful_prf / (l1d_demand_misses + l1d_refilled_prf))", + "BriefDescription": "This metric measures the baseline demand cache misses which the prefetcher brings into the cache.", + "ScaleUnit": "1percent of cache access", + "MetricGroup": "L1I_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1d_refilled_prf", + "MetricExpr": "L1D_CACHE_REFILL_HWPRF + L1D_CACHE_REFILL_PRFM + L1D_LFB_HIT_RW_FHWPRF + L1D_LFB_HIT_RW_FPRFM", + "BriefDescription": "This metric measures the count of cache lines refilled by L1 data prefetcher (hardware prefetches or software preload) into L1 D-cache.", + "ScaleUnit": "1count", + "MetricGroup": "L1I_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1d_tlb_miss_ratio", + "MetricExpr": "L1D_TLB_REFILL / L1D_TLB", + "BriefDescription": "This metric measures the ratio of L1 Data TLB accesses missed to the total number of L1 Data TLB accesses. This gives an indication of the effectiveness of the L1 Data TLB.", + "ScaleUnit": "1per TLB access", + "MetricGroup": "Miss_Ratio;DTLB_Effectiveness" + }, + { + "MetricName": "l1d_tlb_mpki", + "MetricExpr": "1000 * (L1D_TLB_REFILL / INST_RETIRED)", + "BriefDescription": "This metric measures the number of L1 Data TLB accesses missed per thousand instructions executed.", + "ScaleUnit": "1MPKI", + "MetricGroup": "MPKI;DTLB_Effectiveness" + }, + { + "MetricName": "l1d_useful_prf", + "MetricExpr": "L1D_CACHE_HIT_RW_FPRF + L1D_LFB_HIT_RW_FHWPRF + L1D_LFB_HIT_RW_FPRFM", + "BriefDescription": "This metric measures the count of cache lines refilled by L1 data prefetcher (hardware prefetches or software preload) into L1 D-cache which are further used by Load or Store from the instruction stream of the program.", + "ScaleUnit": "1count", + "MetricGroup": "L1I_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1i_cache_miss_ratio", + "MetricExpr": "L1I_CACHE_REFILL / L1I_CACHE", + "BriefDescription": "This metric measures the ratio of L1 I-cache accesses missed to the total number of L1 I-cache accesses. This gives an indication of the effectiveness of the L1 I-cache.", + "ScaleUnit": "1per cache access", + "MetricGroup": "Miss_Ratio;L1I_Cache_Effectiveness" + }, + { + "MetricName": "l1i_cache_mpki", + "MetricExpr": "1000 * (L1I_CACHE_REFILL / INST_RETIRED)", + "BriefDescription": "This metric measures the number of L1 I-cache accesses missed per thousand instructions executed.", + "ScaleUnit": "1MPKI", + "MetricGroup": "MPKI;L1I_Cache_Effectiveness" + }, + { + "MetricName": "l1i_cache_rd_miss_ratio", + "MetricExpr": "l1i_demand_misses / l1i_demand_accesses", + "BriefDescription": "This metric measures the ratio of L1 I-cache Read accesses missed to the total number of L1 I-cache accesses. This gives an indication of the effectiveness of the L1 I-cache for demand instruction fetch traffic. Note that cache accesses in this cache are demand instruction fetch.", + "ScaleUnit": "1per cache access", + "MetricGroup": "L1D_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1i_demand_accesses", + "MetricExpr": "L1I_CACHE_RD", + "BriefDescription": "This metric measures the count of L1 I-cache accesses caused by an instruction fetch by the instruction stream of the program.", + "ScaleUnit": "1count", + "MetricGroup": "L1D_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1i_demand_misses", + "MetricExpr": "L1I_CACHE_REFILL_RD", + "BriefDescription": "This metric measures the count of L1 I-cache misses caused by an instruction fetch by the instruction stream of the program.", + "ScaleUnit": "1count", + "MetricGroup": "L1D_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1i_prf_accuracy", + "MetricExpr": "100 * (l1i_useful_prf / l1i_refilled_prf)", + "BriefDescription": "This metric measures the fraction of prefetched memory addresses that are used by the instruction stream.", + "ScaleUnit": "1percent of prefetch", + "MetricGroup": "L1D_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1i_prf_coverage", + "MetricExpr": "100 * (l1i_useful_prf / (l1i_demand_misses + l1i_refilled_prf))", + "BriefDescription": "This metric measures the baseline demand cache misses which the prefetcher brings into the cache.", + "ScaleUnit": "1percent of cache access", + "MetricGroup": "L1D_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1i_refilled_prf", + "MetricExpr": "L1I_CACHE_REFILL_HWPRF + L1I_CACHE_REFILL_PRFM", + "BriefDescription": "This metric measures the count of cache lines refilled by L1 instruction prefetcher (hardware prefetches or software preload) into L1 I-cache.", + "ScaleUnit": "1count", + "MetricGroup": "L1D_Prefetcher_Effectiveness" + }, + { + "MetricName": "l1i_tlb_miss_ratio", + "MetricExpr": "L1I_TLB_REFILL / L1I_TLB", + "BriefDescription": "This metric measures the ratio of L1 Instruction TLB accesses missed to the total number of L1 Instruction TLB accesses. This gives an indication of the effectiveness of the L1 Instruction TLB.", + "ScaleUnit": "1per TLB access", + "MetricGroup": "Miss_Ratio;ITLB_Effectiveness" + }, + { + "MetricName": "l1i_tlb_mpki", + "MetricExpr": "1000 * (L1I_TLB_REFILL / INST_RETIRED)", + "BriefDescription": "This metric measures the number of L1 Instruction TLB accesses missed per thousand instructions executed.", + "ScaleUnit": "1MPKI", + "MetricGroup": "MPKI;ITLB_Effectiveness" + }, + { + "MetricName": "l1i_useful_prf", + "MetricExpr": "L1I_CACHE_HIT_RD_FPRF", + "BriefDescription": "This metric measures the count of cache lines refilled by L1 instruction prefetcher (hardware prefetches or software preload) into L1 I-cache which are further used by instruction stream of the program.", + "ScaleUnit": "1count", + "MetricGroup": "L1D_Prefetcher_Effectiveness" + }, + { + "MetricName": "l2_cache_miss_ratio", + "MetricExpr": "L2D_CACHE_REFILL / L2D_CACHE", + "BriefDescription": "This metric measures the ratio of L2 cache accesses missed to the total number of L2 cache accesses. This gives an indication of the effectiveness of the L2 cache, which is a unified cache that stores both data and instruction.\nNote that cache accesses in this cache are either data memory access or instruction fetch as this is a unified cache.", + "ScaleUnit": "1per cache access", + "MetricGroup": "Miss_Ratio;L2_Cache_Effectiveness" + }, + { + "MetricName": "l2_cache_mpki", + "MetricExpr": "1000 * (l2d_demand_misses / INST_RETIRED)", + "BriefDescription": "This metric measures the number of L2 unified cache accesses missed per thousand instructions executed.\nNote that cache accesses in this cache are either data memory access or instruction fetch as this is a unified cache.", + "ScaleUnit": "1MPKI", + "MetricGroup": "MPKI;L2_Cache_Effectiveness" + }, + { + "MetricName": "l2_tlb_miss_ratio", + "MetricExpr": "L2D_TLB_REFILL / L2D_TLB", + "BriefDescription": "This metric measures the ratio of L2 unified TLB accesses missed to the total number of L2 unified TLB accesses.\nThis gives an indication of the effectiveness of the L2 TLB.", + "ScaleUnit": "1per TLB access", + "MetricGroup": "Miss_Ratio;ITLB_Effectiveness;DTLB_Effectiveness" + }, + { + "MetricName": "l2_tlb_mpki", + "MetricExpr": "1000 * (L2D_TLB_REFILL / INST_RETIRED)", + "BriefDescription": "This metric measures the number of L2 unified TLB accesses missed per thousand instructions executed.", + "ScaleUnit": "1MPKI", + "MetricGroup": "MPKI;ITLB_Effectiveness;DTLB_Effectiveness" + }, + { + "MetricName": "l2d_cache_rwl1prf_miss_ratio", + "MetricExpr": "l2d_demand_misses / l2d_demand_accesses", + "BriefDescription": "This metric measures the ratio of L2 D-cache Read accesses missed to the total number of L2 D-cache accesses.\nThis gives an indication of the effectiveness of the L2 D-cache for demand instruction fetch, Load, Store, or L1 prefetcher accesses traffic.", + "ScaleUnit": "1per cache access", + "MetricGroup": "L2_Prefetcher_Effectiveness" + }, + { + "MetricName": "l2d_demand_accesses", + "MetricExpr": "L2D_CACHE_RD + L2D_CACHE_WR + L2D_CACHE_L1PRF", + "BriefDescription": "This metric measures the count of L2 D-cache accesses incurred on an instruction fetch, Load, Store, or L1 prefetcher accesses by the instruction stream of the program.", + "ScaleUnit": "1count", + "MetricGroup": "L2_Prefetcher_Effectiveness" + }, + { + "MetricName": "l2d_demand_misses", + "MetricExpr": "L2D_CACHE_REFILL_RD + L2D_CACHE_REFILL_WR + L2D_CACHE_REFILL_L1PRF", + "BriefDescription": "This metric measures the count of L2 D-cache misses incurred on an instruction fetch, Load, Store, or L1 prefetcher accesses by the instruction stream of the program.", + "ScaleUnit": "1count", + "MetricGroup": "L2_Prefetcher_Effectiveness" + }, + { + "MetricName": "l2d_prf_accuracy", + "MetricExpr": "100 * (l2d_useful_prf / l2d_refilled_prf)", + "BriefDescription": "This metric measures the fraction of prefetched memory addresses that are used by the instruction stream.", + "ScaleUnit": "1percent of prefetch", + "MetricGroup": "L2_Prefetcher_Effectiveness" + }, + { + "MetricName": "l2d_prf_coverage", + "MetricExpr": "100 * (l2d_useful_prf / (l2d_demand_misses + l2d_refilled_prf))", + "BriefDescription": "This metric measures the baseline demand cache misses which the prefetcher brings into the cache.", + "ScaleUnit": "1percent of cache access", + "MetricGroup": "L2_Prefetcher_Effectiveness" + }, + { + "MetricName": "l2d_refilled_prf", + "MetricExpr": "(L2D_CACHE_REFILL_PRF - L2D_CACHE_REFILL_L1PRF) + L2D_LFB_HIT_RWL1PRF_FHWPRF", + "BriefDescription": "This metric measures the count of cache lines refilled by L2 data prefetcher (hardware prefetches or software preload) into L2 D-cache.", + "ScaleUnit": "1count", + "MetricGroup": "L2_Prefetcher_Effectiveness" + }, + { + "MetricName": "l2d_useful_prf", + "MetricExpr": "L2D_CACHE_HIT_RWL1PRF_FPRF + L2D_LFB_HIT_RWL1PRF_FHWPRF", + "BriefDescription": "This metric measures the count of cache lines refilled by L2 data prefetcher (hardware prefetches or software preload) into L2 D-cache which are further used by instruction fetch, Load, Store, or L1 prefetcher accesses from the instruction stream of the program.", + "ScaleUnit": "1count", + "MetricGroup": "L2_Prefetcher_Effectiveness" + }, + { + "MetricName": "l3d_cache_rwl1prfl2prf_miss_ratio", + "MetricExpr": "l3d_demand_misses / l3d_demand_accesses", + "BriefDescription": "This metric measures the ratio of L3 D-cache Read accesses missed to the total number of L3 D-cache accesses. This gives an indication of the effectiveness of the L2 D-cache for demand instruction fetch, Load, Store, L1 prefetcher, or L2 prefetcher accesses traffic.", + "ScaleUnit": "1per cache access", + "MetricGroup": "L3_Prefetcher_Effectiveness" + }, + { + "MetricName": "l3d_demand_accesses", + "MetricExpr": "L3D_CACHE_RWL1PRFL2PRF", + "BriefDescription": "This metric measures the count of L3 D-cache accesses incurred on an instruction fetch, Load, Store, L1 prefetcher, or L2 prefetcher accesses by the instruction stream of the program.", + "ScaleUnit": "1count", + "MetricGroup": "L3_Prefetcher_Effectiveness" + }, + { + "MetricName": "l3d_demand_misses", + "MetricExpr": "L3D_CACHE_REFILL_RWL1PRFL2PRF", + "BriefDescription": "This metric measures the count of L3 D-cache misses incurred on an instruction fetch, Load, Store, L1 prefetcher, or L2 prefetcher accesses by the instruction stream of the program.", + "ScaleUnit": "1count", + "MetricGroup": "L3_Prefetcher_Effectiveness" + }, + { + "MetricName": "l3d_prf_accuracy", + "MetricExpr": "100 * (l3d_useful_prf / l3d_refilled_prf)", + "BriefDescription": "This metric measures the fraction of prefetched memory addresses that are used by the instruction stream.", + "ScaleUnit": "1percent of prefetch", + "MetricGroup": "L3_Prefetcher_Effectiveness" + }, + { + "MetricName": "l3d_prf_coverage", + "MetricExpr": "100 * (l3d_useful_prf / (l3d_demand_misses + l3d_refilled_prf))", + "BriefDescription": "This metric measures the baseline demand cache misses which the prefetcher brings into the cache.", + "ScaleUnit": "1percent of cache access", + "MetricGroup": "L3_Prefetcher_Effectiveness" + }, + { + "MetricName": "l3d_refilled_prf", + "MetricExpr": "L3D_CACHE_REFILL_HWPRF + L3D_CACHE_REFILL_PRFM - L3D_CACHE_REFILL_L1PRF - L3D_CACHE_REFILL_L2PRF", + "BriefDescription": "This metric measures the count of cache lines refilled by L3 data prefetcher (hardware prefetches or software preload) into L3 D-cache.", + "ScaleUnit": "1count", + "MetricGroup": "L3_Prefetcher_Effectiveness" + }, + { + "MetricName": "l3d_useful_prf", + "MetricExpr": "L3D_CACHE_HIT_RWL1PRFL2PRF_FPRF", + "BriefDescription": "This metric measures the count of cache lines refilled by L3 data prefetcher (hardware prefetches or software preload) into L3 D-cache which are further used by instruction fetch, Load, Store, L1 prefetcher, or L2 prefetcher accesses from the instruction stream of the program.", + "ScaleUnit": "1count", + "MetricGroup": "L3_Prefetcher_Effectiveness" + }, + { + "MetricName": "ll_cache_read_hit_ratio", + "MetricExpr": "(LL_CACHE_RD - LL_CACHE_MISS_RD) / LL_CACHE_RD", + "BriefDescription": "This metric measures the ratio of last level cache Read accesses hit in the cache to the total number of last level cache accesses. This gives an indication of the effectiveness of the last level cache for Read traffic. Note that cache accesses in this cache are either data memory access or instruction fetch as this is a system level cache.", + "ScaleUnit": "1per cache access", + "MetricGroup": "LL_Cache_Effectiveness" + }, + { + "MetricName": "ll_cache_read_miss_ratio", + "MetricExpr": "LL_CACHE_MISS_RD / LL_CACHE_RD", + "BriefDescription": "This metric measures the ratio of last level cache Read accesses missed to the total number of last level cache accesses. This gives an indication of the effectiveness of the last level cache for Read traffic. Note that cache accesses in this cache are either data memory access or instruction fetch as this is a system level cache.", + "ScaleUnit": "1per cache access", + "MetricGroup": "Miss_Ratio;LL_Cache_Effectiveness" + }, + { + "MetricName": "ll_cache_read_mpki", + "MetricExpr": "1000 * (LL_CACHE_MISS_RD / INST_RETIRED)", + "BriefDescription": "This metric measures the number of last level cache Read accesses missed per thousand instructions executed.", + "ScaleUnit": "1MPKI", + "MetricGroup": "MPKI;LL_Cache_Effectiveness" + }, + { + "MetricName": "load_average_latency", + "MetricExpr": "MEM_ACCESS_RD_PERCYC / MEM_ACCESS", + "BriefDescription": "This metric measures the average latency of Load operations in CPU cycles.", + "ScaleUnit": "1CPU cycles", + "MetricGroup": "Average_Latency" + }, + { + "MetricName": "load_percentage", + "MetricExpr": "100 * (LD_SPEC / INST_SPEC)", + "BriefDescription": "This metric measures Load operations as a percentage of operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "Operation_Mix" + }, + { + "MetricName": "nonsve_fp_ops_per_cycle", + "MetricExpr": "FP_FIXED_OPS_SPEC / CPU_CYCLES", + "BriefDescription": "This metric measures floating point operations per cycle in any precision performed by an instruction that is not an SVE instruction. Operations are counted by computation and by vector lanes, fused computations such as multiply-add count as twice per vector lane for example.", + "ScaleUnit": "1operations per cycle", + "MetricGroup": "FP_Arithmetic_Intensity" + }, + { + "MetricName": "retiring", + "MetricExpr": "100 * ((OP_RETIRED/OP_SPEC) * (1 - (STALL_SLOT/CPU_SLOT)))", + "BriefDescription": "This metric is the percentage of total slots that retired operations, which indicates cycles that were utilized efficiently.", + "ScaleUnit": "1percent of slots", + "MetricGroup": "TopdownL1" + }, + { + "MetricName": "scalar_fp_percentage", + "MetricExpr": "100 * (VFP_SPEC / INST_SPEC)", + "BriefDescription": "This metric measures scalar floating point operations as a percentage of operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "Operation_Mix" + }, + { + "MetricName": "simd_percentage", + "MetricExpr": "100 * (ASE_SPEC / INST_SPEC)", + "BriefDescription": "This metric measures advanced SIMD operations as a percentage of total operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "Operation_Mix" + }, + { + "MetricName": "store_percentage", + "MetricExpr": "100 * (ST_SPEC / INST_SPEC)", + "BriefDescription": "This metric measures Store operations as a percentage of operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "Operation_Mix" + }, + { + "MetricName": "sve_all_percentage", + "MetricExpr": "100 * (SVE_INST_SPEC / INST_SPEC)", + "BriefDescription": "This metric measures scalable vector operations, including Loads and Stores, as a percentage of operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "Operation_Mix" + }, + { + "MetricName": "sve_fp_ops_per_cycle", + "MetricExpr": "FP_SCALE_OPS_SPEC / CPU_CYCLES", + "BriefDescription": "This metric measures floating point operations per cycle in any precision performed by SVE instructions. Operations are counted by computation and by vector lanes, fused computations such as multiply-add count as twice per vector lane for example.", + "ScaleUnit": "1operations per cycle", + "MetricGroup": "FP_Arithmetic_Intensity" + }, + { + "MetricName": "sve_predicate_empty_percentage", + "MetricExpr": "100 * (SVE_PRED_EMPTY_SPEC / SVE_PRED_SPEC)", + "BriefDescription": "This metric measures scalable vector operations with no active predicates as a percentage of SVE predicated operations speculatively executed.", + "ScaleUnit": "1percent of SVE predicated operations", + "MetricGroup": "SVE_Effectiveness" + }, + { + "MetricName": "sve_predicate_full_percentage", + "MetricExpr": "100 * (SVE_PRED_FULL_SPEC / SVE_PRED_SPEC)", + "BriefDescription": "This metric measures scalable vector operations with all active predicates as a percentage of SVE predicated operations speculatively executed.", + "ScaleUnit": "1percent of SVE predicated operations", + "MetricGroup": "SVE_Effectiveness" + }, + { + "MetricName": "sve_predicate_partial_percentage", + "MetricExpr": "100 * (SVE_PRED_PARTIAL_SPEC / SVE_PRED_SPEC)", + "BriefDescription": "This metric measures scalable vector operations with at least one active predicates as a percentage of SVE predicated operations speculatively executed.", + "ScaleUnit": "1percent of SVE predicated operations", + "MetricGroup": "SVE_Effectiveness" + }, + { + "MetricName": "sve_predicate_percentage", + "MetricExpr": "100 * (SVE_PRED_SPEC / INST_SPEC)", + "BriefDescription": "This metric measures scalable vector operations with predicates as a percentage of operations speculatively executed.", + "ScaleUnit": "1percent of operations", + "MetricGroup": "SVE_Effectiveness" + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/misc.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/misc.json new file mode 100644 index 000000000000..8ff87d844e52 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/misc.json @@ -0,0 +1,642 @@ +[ + { + "ArchStdEvent": "SW_INCR", + "PublicDescription": "This event counts software writes to the PMSWINC_EL0 (software PMU increment) register. The PMSWINC_EL0 register is a manually updated counter for use by application software.\nThis event could be used to measure any user program event, such as accesses to a particular data structure (by writing to the PMSWINC_EL0 register each time the data structure is accessed).\nTo use the PMSWINC_EL0 register and event, developers must insert instructions that write to the PMSWINC_EL0 register into the source code.\nSince the SW_INCR event records writes to the PMSWINC_EL0 register, there is no need to do a Read/Increment/Write sequence to the PMSWINC_EL0 register." + }, + { + "ArchStdEvent": "TRB_WRAP", + "PublicDescription": "This event is generated each time the trace buffer current Write pointer is wrapped to the trace buffer base pointer." + }, + { + "ArchStdEvent": "TRCEXTOUT0", + "PublicDescription": "Trace unit external output 0." + }, + { + "ArchStdEvent": "TRCEXTOUT1", + "PublicDescription": "Trace unit external output 1." + }, + { + "ArchStdEvent": "TRCEXTOUT2", + "PublicDescription": "Trace unit external output 2." + }, + { + "ArchStdEvent": "TRCEXTOUT3", + "PublicDescription": "Trace unit external output 3." + }, + { + "ArchStdEvent": "CTI_TRIGOUT4", + "PublicDescription": "Cross-trigger Interface output trigger 4." + }, + { + "ArchStdEvent": "CTI_TRIGOUT5", + "PublicDescription": "Cross-trigger Interface output trigger 5." + }, + { + "ArchStdEvent": "CTI_TRIGOUT6", + "PublicDescription": "Cross-trigger Interface output trigger 6." + }, + { + "ArchStdEvent": "CTI_TRIGOUT7", + "PublicDescription": "Cross-trigger Interface output trigger 7." + }, + { + "EventCode": "0x00e1", + "EventName": "L1I_PRFM_REQ_DROP", + "PublicDescription": "L1 I-cache software prefetch dropped." + }, + { + "EventCode": "0x0100", + "EventName": "L1_PF_REFILL", + "PublicDescription": "L1 prefetch requests, refilled to L1 cache." + }, + { + "EventCode": "0x0120", + "EventName": "FLUSH", + "PublicDescription": "This event counts both the CT flush and BX flush. The BR_MIS_PRED counts the BX flushes. So the FLUSH-BR_MIS_PRED gives the CT flushes." + }, + { + "EventCode": "0x0121", + "EventName": "FLUSH_MEM", + "PublicDescription": "Flushes due to memory hazards. This only includes CT flushes." + }, + { + "EventCode": "0x0122", + "EventName": "FLUSH_BAD_BRANCH", + "PublicDescription": "Flushes due to bad predicted branch. This only includes CT flushes." + }, + { + "EventCode": "0x0123", + "EventName": "FLUSH_STDBYPASS", + "PublicDescription": "Flushes due to bad predecode. This only includes CT flushes." + }, + { + "EventCode": "0x0124", + "EventName": "FLUSH_ISB", + "PublicDescription": "Flushes due to ISB or similar side-effects. This only includes CT flushes." + }, + { + "EventCode": "0x0125", + "EventName": "FLUSH_OTHER", + "PublicDescription": "Flushes due to other hazards. This only includes CT flushes." + }, + { + "EventCode": "0x0126", + "EventName": "STORE_STREAM", + "PublicDescription": "Stored lines in streaming no-Write-allocate mode." + }, + { + "EventCode": "0x0127", + "EventName": "NUKE_RAR", + "PublicDescription": "Load/Store nuke due to Read-after-Read ordering hazard." + }, + { + "EventCode": "0x0128", + "EventName": "NUKE_RAW", + "PublicDescription": "Load/Store nuke due to Read-after-Write ordering hazard." + }, + { + "EventCode": "0x0129", + "EventName": "L1_PF_GEN_PAGE", + "PublicDescription": "Load/Store prefetch to L1 generated, Page mode." + }, + { + "EventCode": "0x012a", + "EventName": "L1_PF_GEN_STRIDE", + "PublicDescription": "Load/Store prefetch to L1 generated, stride mode." + }, + { + "EventCode": "0x012b", + "EventName": "L2_PF_GEN_LD", + "PublicDescription": "Load prefetch to L2 generated." + }, + { + "EventCode": "0x012d", + "EventName": "LS_PF_TRAIN_TABLE_ALLOC", + "PublicDescription": "LS prefetch train table entry allocated." + }, + { + "EventCode": "0x0130", + "EventName": "LS_PF_GEN_TABLE_ALLOC", + "PublicDescription": "This event counts the number of cycles with at least one table allocation, for L2 hardware prefetches (including the software PRFM instructions that are converted into hardware prefetches due to D-TLB miss).\nLS prefetch gen table allocation (for L2 prefetches)." + }, + { + "EventCode": "0x0131", + "EventName": "LS_PF_GEN_TABLE_ALLOC_PF_PEND", + "PublicDescription": "This event counts the number of cycles in which at least one hardware prefetch is dropped due to the inability to identify a victim when the generation table is full. The hardware prefetch considered here includes the software PRFM that is converted into hardware prefetches due to D-TLB miss." + }, + { + "EventCode": "0x0132", + "EventName": "TBW", + "PublicDescription": "Tablewalks." + }, + { + "EventCode": "0x0134", + "EventName": "S1L2_HIT", + "PublicDescription": "Translation cache hit on S1L2 walk cache entry." + }, + { + "EventCode": "0x0135", + "EventName": "S1L1_HIT", + "PublicDescription": "Translation cache hit on S1L1 walk cache entry." + }, + { + "EventCode": "0x0136", + "EventName": "S1L0_HIT", + "PublicDescription": "Translation cache hit on S1L0 walk cache entry." + }, + { + "EventCode": "0x0137", + "EventName": "S2L2_HIT", + "PublicDescription": "Translation cache hit for S2L2 IPA walk cache entry." + }, + { + "EventCode": "0x0138", + "EventName": "IPA_REQ", + "PublicDescription": "Translation cache lookups for IPA to PA entries." + }, + { + "EventCode": "0x0139", + "EventName": "IPA_REFILL", + "PublicDescription": "Translation cache refills for IPA to PA entries." + }, + { + "EventCode": "0x013a", + "EventName": "S1_FLT", + "PublicDescription": "Stage1 tablewalk fault." + }, + { + "EventCode": "0x013b", + "EventName": "S2_FLT", + "PublicDescription": "Stage2 tablewalk fault." + }, + { + "EventCode": "0x013c", + "EventName": "COLT_REFILL", + "PublicDescription": "Aggregated page refill." + }, + { + "EventCode": "0x0145", + "EventName": "L1_PF_HIT", + "PublicDescription": "L1 prefetch requests, hitting in L1 cache." + }, + { + "EventCode": "0x0146", + "EventName": "L1_PF", + "PublicDescription": "L1 prefetch requests." + }, + { + "EventCode": "0x0147", + "EventName": "CACHE_LS_REFILL", + "PublicDescription": "L2 D-cache refill, Load/Store." + }, + { + "EventCode": "0x0148", + "EventName": "CACHE_PF", + "PublicDescription": "L2 prefetch requests." + }, + { + "EventCode": "0x0149", + "EventName": "CACHE_PF_HIT", + "PublicDescription": "L2 prefetch requests, hitting in L2 cache." + }, + { + "EventCode": "0x0150", + "EventName": "UNUSED_PF", + "PublicDescription": "L2 unused prefetch." + }, + { + "EventCode": "0x0151", + "EventName": "PFT_SENT", + "PublicDescription": "L2 prefetch TGT sent.\nNote that PFT_SENT != PFT_USEFUL + PFT_DROP. There may be PFT_SENT for which the accesses resulted in a SLC hit." + }, + { + "EventCode": "0x0152", + "EventName": "PFT_USEFUL", + "PublicDescription": "L2 prefetch TGT useful." + }, + { + "EventCode": "0x0153", + "EventName": "PFT_DROP", + "PublicDescription": "L2 prefetch TGT dropped." + }, + { + "EventCode": "0x0162", + "EventName": "LRQ_FULL", + "PublicDescription": "This event counts the number of cycles the LRQ is full." + }, + { + "EventCode": "0x0163", + "EventName": "FETCH_FQ_EMPTY", + "PublicDescription": "Fetch Queue empty cycles." + }, + { + "EventCode": "0x0164", + "EventName": "FPG2", + "PublicDescription": "Forward progress guarantee. Medium range livelock triggered." + }, + { + "EventCode": "0x0165", + "EventName": "FPG", + "PublicDescription": "Forward progress guarantee. Tofu global livelock buster is triggered." + }, + { + "EventCode": "0x0172", + "EventName": "DEADBLOCK", + "PublicDescription": "Write-back evictions converted to dataless EVICT.\nThe victim line is deemed deadblock if the likeliness of a reuse is low. The Core uses dataless evict to evict a deadblock; and it uses an evict with data to evict an L2 line that is not a deadblock." + }, + { + "EventCode": "0x0173", + "EventName": "PF_PRQ_ALLOC_PF_PEND", + "PublicDescription": "L1 prefetch prq allocation (replacing pending)." + }, + { + "EventCode": "0x0178", + "EventName": "FETCH_ICACHE_INSTR", + "PublicDescription": "Instructions fetched from I-cache." + }, + { + "EventCode": "0x017b", + "EventName": "NEAR_CAS", + "PublicDescription": "Near atomics: compare and swap." + }, + { + "EventCode": "0x017c", + "EventName": "NEAR_CAS_PASS", + "PublicDescription": "Near atomics: compare and swap pass." + }, + { + "EventCode": "0x017d", + "EventName": "FAR_CAS", + "PublicDescription": "Far atomics: compare and swap." + }, + { + "EventCode": "0x0186", + "EventName": "L2_BTB_RELOAD_MAIN_BTB", + "PublicDescription": "Number of completed L1 BTB update initiated by L2 BTB hit which swap branch information between L1 BTB and L2 BTB." + }, + { + "EventCode": "0x018f", + "EventName": "L1_PF_GEN_MCMC", + "PublicDescription": "Load/Store prefetch to L1 generated, MCMC." + }, + { + "EventCode": "0x0190", + "EventName": "PF_MODE_0_CYCLES", + "PublicDescription": "Number of cycles in which the hardware prefetcher is in the most aggressive mode." + }, + { + "EventCode": "0x0191", + "EventName": "PF_MODE_1_CYCLES", + "PublicDescription": "Number of cycles in which the hardware prefetcher is in the more aggressive mode." + }, + { + "EventCode": "0x0192", + "EventName": "PF_MODE_2_CYCLES", + "PublicDescription": "Number of cycles in which the hardware prefetcher is in the less aggressive mode." + }, + { + "EventCode": "0x0193", + "EventName": "PF_MODE_3_CYCLES", + "PublicDescription": "Number of cycles in which the hardware prefetcher is in the most conservative mode." + }, + { + "EventCode": "0x0194", + "EventName": "TXREQ_LIMIT_MAX_CYCLES", + "PublicDescription": "Number of cycles in which the dynamic TXREQ limit is the L2_TQ_SIZE." + }, + { + "EventCode": "0x0195", + "EventName": "TXREQ_LIMIT_3QUARTER_CYCLES", + "PublicDescription": "Number of cycles in which the dynamic TXREQ limit is between 3/4 of the L2_TQ_SIZE and the L2_TQ_SIZE-1." + }, + { + "EventCode": "0x0196", + "EventName": "TXREQ_LIMIT_HALF_CYCLES", + "PublicDescription": "Number of cycles in which the dynamic TXREQ limit is between 1/2 of the L2_TQ_SIZE and 3/4 of the L2_TQ_SIZE." + }, + { + "EventCode": "0x0197", + "EventName": "TXREQ_LIMIT_1QUARTER_CYCLES", + "PublicDescription": "Number of cycles in which the dynamic TXREQ limit is between 1/4 of the L2_TQ_SIZE and 1/2 of the L2_TQ_SIZE." + }, + { + "EventCode": "0x019d", + "EventName": "PREFETCH_LATE_CMC", + "PublicDescription": "LS/readclean or LS/readunique lookup hit on TQ entry allocated by CMC prefetch request." + }, + { + "EventCode": "0x019e", + "EventName": "PREFETCH_LATE_BO", + "PublicDescription": "LS/readclean or LS/readunique lookup hit on TQ entry allocated by BO prefetch request." + }, + { + "EventCode": "0x019f", + "EventName": "PREFETCH_LATE_STRIDE", + "PublicDescription": "LS/readclean or LS/readunique lookup hit on TQ entry allocated by STRIDE prefetch request." + }, + { + "EventCode": "0x01a0", + "EventName": "PREFETCH_LATE_SPATIAL", + "PublicDescription": "LS/readclean or LS/readunique lookup hit on TQ entry allocated by SPATIAL prefetch request." + }, + { + "EventCode": "0x01a2", + "EventName": "PREFETCH_LATE_TBW", + "PublicDescription": "LS/readclean or LS/readunique lookup hit on TQ entry allocated by TBW prefetch request." + }, + { + "EventCode": "0x01a3", + "EventName": "PREFETCH_LATE_PAGE", + "PublicDescription": "LS/readclean or LS/readunique lookup hit on TQ entry allocated by PAGE prefetch request." + }, + { + "EventCode": "0x01a4", + "EventName": "PREFETCH_LATE_GSMS", + "PublicDescription": "LS/readclean or LS/readunique lookup hit on TQ entry allocated by GSMS prefetch request." + }, + { + "EventCode": "0x01a5", + "EventName": "PREFETCH_LATE_SIP_CONS", + "PublicDescription": "LS/readclean or LS/readunique lookup hit on TQ entry allocated by SIP_CONS prefetch request." + }, + { + "EventCode": "0x01a6", + "EventName": "PREFETCH_REFILL_CMC", + "PublicDescription": "PF/prefetch or PF/readclean request from CMC pf engine filled the L2 cache." + }, + { + "EventCode": "0x01a7", + "EventName": "PREFETCH_REFILL_BO", + "PublicDescription": "PF/prefetch or PF/readclean request from BO pf engine filled the L2 cache." + }, + { + "EventCode": "0x01a8", + "EventName": "PREFETCH_REFILL_STRIDE", + "PublicDescription": "PF/prefetch or PF/readclean request from STRIDE pf engine filled the L2 cache." + }, + { + "EventCode": "0x01a9", + "EventName": "PREFETCH_REFILL_SPATIAL", + "PublicDescription": "PF/prefetch or PF/readclean request from SPATIAL pf engine filled the L2 cache." + }, + { + "EventCode": "0x01ab", + "EventName": "PREFETCH_REFILL_TBW", + "PublicDescription": "PF/prefetch or PF/readclean request from TBW pf engine filled the L2 cache." + }, + { + "EventCode": "0x01ac", + "EventName": "PREFETCH_REFILL_PAGE", + "PublicDescription": "PF/prefetch or PF/readclean request from PAGE pf engine filled the L2 cache." + }, + { + "EventCode": "0x01ad", + "EventName": "PREFETCH_REFILL_GSMS", + "PublicDescription": "PF/prefetch or PF/readclean request from GSMS pf engine filled the L2 cache." + }, + { + "EventCode": "0x01ae", + "EventName": "PREFETCH_REFILL_SIP_CONS", + "PublicDescription": "PF/prefetch or PF/readclean request from SIP_CONS pf engine filled the L2 cache." + }, + { + "EventCode": "0x01af", + "EventName": "CACHE_HIT_LINE_PF_CMC", + "PublicDescription": "LS/readclean or LS/readunique lookup hit in L2 cache on line filled by CMC prefetch request." + }, + { + "EventCode": "0x01b0", + "EventName": "CACHE_HIT_LINE_PF_BO", + "PublicDescription": "LS/readclean or LS/readunique lookup hit in L2 cache on line filled by BO prefetch request." + }, + { + "EventCode": "0x01b1", + "EventName": "CACHE_HIT_LINE_PF_STRIDE", + "PublicDescription": "LS/readclean or LS/readunique lookup hit in L2 cache on line filled by STRIDE prefetch request." + }, + { + "EventCode": "0x01b2", + "EventName": "CACHE_HIT_LINE_PF_SPATIAL", + "PublicDescription": "LS/readclean or LS/readunique lookup hit in L2 cache on line filled by SPATIAL prefetch request." + }, + { + "EventCode": "0x01b4", + "EventName": "CACHE_HIT_LINE_PF_TBW", + "PublicDescription": "LS/readclean or LS/readunique lookup hit in L2 cache on line filled by TBW prefetch request." + }, + { + "EventCode": "0x01b5", + "EventName": "CACHE_HIT_LINE_PF_PAGE", + "PublicDescription": "LS/readclean or LS/readunique lookup hit in L2 cache on line filled by PAGE prefetch request." + }, + { + "EventCode": "0x01b6", + "EventName": "CACHE_HIT_LINE_PF_GSMS", + "PublicDescription": "LS/readclean or LS/readunique lookup hit in L2 cache on line filled by GSMS prefetch request." + }, + { + "EventCode": "0x01b7", + "EventName": "CACHE_HIT_LINE_PF_SIP_CONS", + "PublicDescription": "LS/readclean or LS/readunique lookup hit in L2 cache on line filled by SIP_CONS prefetch request." + }, + { + "EventCode": "0x01ba", + "EventName": "PREFETCH_LATE_STORE_ISSUE", + "PublicDescription": "This event counts the number of demand requests that matches a Store-issue prefetcher's pending refill request. These are called late prefetch requests and are still counted as useful prefetcher requests for the sake of accuracy and coverage measurements." + }, + { + "EventCode": "0x01bb", + "EventName": "PREFETCH_LATE_STORE_STRIDE", + "PublicDescription": "This event counts the number of demand requests that matches a Store-stride prefetcher's pending refill request. These are called late prefetch requests and are still counted as useful prefetcher requests for the sake of accuracy and coverage measurements." + }, + { + "EventCode": "0x01bc", + "EventName": "PREFETCH_LATE_PC_OFFSET", + "PublicDescription": "This event counts the number of demand requests that matches a PC-offset prefetcher's pending refill request. These are called late prefetch requests and are still counted as useful prefetcher requests for the sake of accuracy and coverage measurements." + }, + { + "EventCode": "0x01bd", + "EventName": "PREFETCH_LATE_IFUPF", + "PublicDescription": "This event counts the number of demand requests that matches a IFU prefetcher's pending refill request. These are called late prefetch requests and are still counted as useful prefetcher requests for the sake of accuracy and coverage measurements." + }, + { + "EventCode": "0x01be", + "EventName": "PREFETCH_REFILL_STORE_ISSUE", + "PublicDescription": "This event counts the number of cache refills due to Store-Issue prefetcher." + }, + { + "EventCode": "0x01bf", + "EventName": "PREFETCH_REFILL_STORE_STRIDE", + "PublicDescription": "This event counts the number of cache refills due to Store-stride prefetcher." + }, + { + "EventCode": "0x01c0", + "EventName": "PREFETCH_REFILL_PC_OFFSET", + "PublicDescription": "This event counts the number of cache refills due to PC-offset prefetcher." + }, + { + "EventCode": "0x01c1", + "EventName": "PREFETCH_REFILL_IFUPF", + "PublicDescription": "This event counts the number of cache refills due to IFU prefetcher." + }, + { + "EventCode": "0x01c2", + "EventName": "CACHE_HIT_LINE_PF_STORE_ISSUE", + "PublicDescription": "This event counts the number of first hit to a cache line filled by Store-issue prefetcher." + }, + { + "EventCode": "0x01c3", + "EventName": "CACHE_HIT_LINE_PF_STORE_STRIDE", + "PublicDescription": "This event counts the number of first hit to a cache line filled by Store-stride prefetcher." + }, + { + "EventCode": "0x01c4", + "EventName": "CACHE_HIT_LINE_PF_PC_OFFSET", + "PublicDescription": "This event counts the number of first hit to a cache line filled by PC-offset prefetcher." + }, + { + "EventCode": "0x01c5", + "EventName": "CACHE_HIT_LINE_PF_IFUPF", + "PublicDescription": "This event counts the number of first hit to a cache line filled by IFU prefetcher." + }, + { + "EventCode": "0x01c6", + "EventName": "L2_PF_GEN_ST_ISSUE", + "PublicDescription": "Store-issue prefetch to L2 generated." + }, + { + "EventCode": "0x01c7", + "EventName": "L2_PF_GEN_ST_STRIDE", + "PublicDescription": "Store-stride prefetch to L2 generated" + }, + { + "EventCode": "0x01cb", + "EventName": "L2_TQ_OUTSTANDING", + "PublicDescription": "Outstanding tracker count, per cycle.\nThis event increments by the number of valid entries pertaining to this thread in the L2TQ, in each cycle.\nThis event can be used to calculate the occupancy of L2TQ by dividing this by the CPU_CYCLES event. The L2TQ queue tracks the outstanding Read, Write and Snoop transactions. The Read transaction and the Write transaction entries are attributable to PE, whereas the Snoop transactions are not always attributable to PE." + }, + { + "EventCode": "0x01cc", + "EventName": "TXREQ_LIMIT_COUNT_CYCLES", + "PublicDescription": "This event increments by the dynamic TXREQ value, in each cycle.\nThis is a companion event of TXREQ_LIMIT_MAX_CYCLES, TXREQ_LIMIT_3QUARTER_CYCLES, TXREQ_LIMIT_HALF_CYCLES, and TXREQ_LIMIT_1QUARTER_CYCLES." + }, + { + "EventCode": "0x01ce", + "EventName": "L3DPRFM_TO_L2PRQ_CONVERTED", + "PublicDescription": "This event counts the number of Converted-L3D-PRFMs. These are indeed L3D PRFM and activities around these PRFM are counted by the L3D_CACHE_PRFM, L3D_CACHE_REFILL_PRFM and L3D_CACHE_REFILL Events." + }, + { + "EventCode": "0x01d2", + "EventName": "DVM_TLBI_RCVD", + "PublicDescription": "This event counts the number of TLBI DVM message received over CHI interface, for *this* Core." + }, + { + "EventCode": "0x01d6", + "EventName": "DSB_COMMITING_LOCAL_TLBI", + "PublicDescription": "This event counts the number of DSB that are retired and committed at least one local TLBI instruction. This event increments no more than once (in a cycle) even if the DSB commits multiple local TLBI instruction." + }, + { + "EventCode": "0x01d7", + "EventName": "DSB_COMMITING_BROADCAST_TLBI", + "PublicDescription": "This event counts the number of DSB that are retired and committed at least one broadcast TLBI instruction. This event increments no more than once (in a cycle) even if the DSB commits multiple broadcast TLBI instruction." + }, + { + "EventCode": "0x01eb", + "EventName": "L1DPRFM_L2DPRFM_TO_L2PRQ_CONVERTED", + "PublicDescription": "This event counts the number of Converted-L1D-PRFMs and Converted-L2D-PRFM.\nActivities involving the Converted-L1D-PRFM are counted by the L1D_CACHE_PRFM. However they are *not* counted by the L1D_CACHE_REFILL_PRFM, and L1D_CACHE_REFILL, as these Converted-L1D-PRFM are treated as L2 D hardware prefetches. Activities around the Converted-L1D-PRFMs and Converted-L2D-PRFMs are counted by the L2D_CACHE_PRFM, L2D_CACHE_REFILL_PRFM and L2D_CACHE_REFILL Events." + }, + { + "EventCode": "0x01ec", + "EventName": "PREFETCH_LATE_CONVERTED_PRFM", + "PublicDescription": "This event counts the number of demand requests that matches a Converted-L1D-PRFM or Converted-L2D-PRFM pending refill request at L2 D-cache. These are called late prefetch requests and are still counted as useful prefetcher requests for the sake of accuracy and coverage measurements.\nNote that this event is not counted by the L2D_CACHE_HIT_RWL1PRF_LATE_HWPRF, though the Converted-L1D-PRFM or Converted-L2D-PRFM are replayed by the L2PRQ." + }, + { + "EventCode": "0x01ed", + "EventName": "PREFETCH_REFILL_CONVERTED_PRFM", + "PublicDescription": "This event counts the number of L2 D-cache refills due to Converted-L1D-PRFM or Converted-L2D-PRFM.\nNote : L2D_CACHE_REFILL_PRFM is inclusive of PREFETCH_REFILL_PRFM_CONVERTED, where both the PREFETCH_REFILL_PRFM_CONVERTED and the L2D_CACHE_REFILL_PRFM increment when L2 D-cache refills due to Converted-L1D-PRFM or Converted-L2D-PRFM." + }, + { + "EventCode": "0x01ee", + "EventName": "CACHE_HIT_LINE_PF_CONVERTED_PRFM", + "PublicDescription": "This event counts the number of first hit to a cache line filled by Converted-L1D-PRFM or Converted-L2D-PRFM.\nNote that L2D_CACHE_HIT_RWL1PRF_FPRFM is inclusive of CACHE_HIT_LINE_PF_CONVERTED_PRFM, where both the CACHE_HIT_LINE_PF_CONVERTED_PRFM and the L2D_CACHE_HIT_RWL1PRF_FPRFM increment on a first hit to L2 D-cache filled by Converted-L1D-PRFM or Converted-L2D-PRFM." + }, + { + "EventCode": "0x01f0", + "EventName": "TMS_ST_TO_SMT_LATENCY", + "PublicDescription": "This event counts the number of CPU cycles spent on TMS for ST-to-SMT switch.\nThis event is counted by both the threads - This event in both threads increment during TMS for ST-to-SMT switch." + }, + { + "EventCode": "0x01f1", + "EventName": "TMS_SMT_TO_ST_LATENCY", + "PublicDescription": "This event counts the number of CPU cycles spent on TMS for SMT-to-ST switch. The count also includes the CPU cycles spend due to an aborted SMT-to-ST TMS attempt.\nThis event is counted only by the thread that is not in WFI." + }, + { + "EventCode": "0x01f2", + "EventName": "TMS_ST_TO_SMT_COUNT", + "PublicDescription": "This event counts the number of completed TMS from ST-to-SMT.\nThis event is counted only by the active thread (the one that is not in WFI).\nNote: When an active thread enters the Debug state in ST-Full resource mode, it is switched to SMT mode. This is because the inactive thread cannot wake up while the other thread remains in the Debug state. To prEvent this issue, threads operating in ST-Full resource mode are transitioned to SMT mode upon entering Debug state. This event count will also reflect such switches from ST to SMT mode.\n(Also see the (NV_CPUACTLR14_EL1.chka_prEvent_st_tx_to_smt_when_tx_in_debug_state bit to disable this behavior.)" + }, + { + "EventCode": "0x01f3", + "EventName": "TMS_SMT_TO_ST_COUNT", + "PublicDescription": "This event counts the number of completed TMS from SMT-to-ST.\nThis event is counted only by the thread that is not in WFI." + }, + { + "EventCode": "0x01f4", + "EventName": "TMS_SMT_TO_ST_COUNT_ABRT", + "PublicDescription": "This event counts the number of aborted TMS from SMT-to-ST.\nThis event is counted only by the thread that is not in WFI." + }, + { + "EventCode": "0x0202", + "EventName": "L0I_CACHE_RD", + "PublicDescription": "This event counts the number of predict blocks serviced out of L0 I-cache.\nNote: The L0 I-cache performs at most 4 L0 I look-up in a cycle. Two of which are to service PB from L0 I. And the other two to refill L0 I-cache from L1 I. This event count only the L0 I-cache lookup pertaining to servicing the PB from L0 I." + }, + { + "EventCode": "0x0203", + "EventName": "L0I_CACHE_REFILL", + "PublicDescription": "This event counts the number of L0I cache refill from L1 I-cache." + }, + { + "EventCode": "0x0207", + "EventName": "INTR_LATENCY", + "PublicDescription": "This event counts the number of cycles elapsed between when an Interrupt is recognized (after masking) to when a uop associated with the first instruction in the destination exception level is allocated. If there is some other flush condition that pre-empts the Interrupt, then the cycles counted terminates early at the first instruction executed after that flush. In the event of dropped Interrupts (when an Interrupt is deasserted before it is taken), this counter measures the number of cycles that elapse from the moment an Interrupt is recognized (post-masking) until the Interrupt is dropped or deasserted.\nNote that\n* IESB(Implicit Error Synchronization Barrier) is an internal mop, so the latency of an implicit IESB mop executed before the Interrupt taken is included in the Interrupt latency count.\n* Nukes or TMS sequence within the window are also counted by the Interrupt latency Event.\n* A SMT to ST TMS will be aborted on detecting the wake condition for the WFI thread. The Interrupt latency count includes any additional penalty for an aborted TMS." + }, + { + "EventCode": "0x021c", + "EventName": "CWT_ALLOC_ENTRY", + "PublicDescription": "Cache Way Tracker Allocate entry." + }, + { + "EventCode": "0x021d", + "EventName": "CWT_ALLOC_LINE", + "PublicDescription": "Cache Way Tracker Allocate line." + }, + { + "EventCode": "0x021e", + "EventName": "CWT_HIT", + "PublicDescription": "Cache Way Tracker hit." + }, + { + "EventCode": "0x021f", + "EventName": "CWT_HIT_TAG", + "PublicDescription": "Cache Way Tracker hit when ITAG lookup suppressed." + }, + { + "EventCode": "0x0220", + "EventName": "CWT_REPLAY_TAG", + "PublicDescription": "Cache Way Tracker causes ITAG replay due to miss when ITAG lookup suppressed." + }, + { + "EventCode": "0x0250", + "EventName": "GPT_REQ", + "PublicDescription": "GPT lookup." + }, + { + "EventCode": "0x0251", + "EventName": "GPT_WC_HIT", + "PublicDescription": "GPT lookup hit in Walk cache." + }, + { + "EventCode": "0x0252", + "EventName": "GPT_PG_HIT", + "PublicDescription": "GPT lookup hit in TLB." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/retired.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/retired.json new file mode 100644 index 000000000000..34c7eefa66b0 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/retired.json @@ -0,0 +1,94 @@ +[ + { + "ArchStdEvent": "INST_RETIRED", + "PublicDescription": "This event counts instructions that have been architecturally executed." + }, + { + "ArchStdEvent": "CID_WRITE_RETIRED", + "PublicDescription": "This event counts architecturally executed writes to the CONTEXTIDR_EL1 register, which usually contains the kernel PID and can be output with hardware trace." + }, + { + "ArchStdEvent": "BR_IMMED_RETIRED", + "PublicDescription": "This event counts architecturally executed direct branches." + }, + { + "ArchStdEvent": "BR_RETURN_RETIRED", + "PublicDescription": "This event counts architecturally executed procedure returns." + }, + { + "ArchStdEvent": "TTBR_WRITE_RETIRED", + "PublicDescription": "This event counts architectural writes to TTBR0/1_EL1. If virtualization host extensions are enabled (by setting the HCR_EL2.E2H bit to 1), then accesses to TTBR0/1_EL1 that are redirected to TTBR0/1_EL2, or accesses to TTBR0/1_EL12, are counted. TTBRn registers are typically updated when the kernel is swapping user-space threads or applications." + }, + { + "ArchStdEvent": "BR_RETIRED", + "PublicDescription": "This event counts architecturally executed branches, whether the branch is taken or not. Instructions that explicitly write to the PC are also counted. Note that exception generating instructions, exception return instructions, and context synchronization instructions are not counted." + }, + { + "ArchStdEvent": "BR_MIS_PRED_RETIRED", + "PublicDescription": "This event counts branches counted by BR_RETIRED which were mispredicted and caused a pipeline flush." + }, + { + "ArchStdEvent": "OP_RETIRED", + "PublicDescription": "This event counts micro-operations that are architecturally executed. This is a count of number of micro-operations retired from the commit queue in a single cycle." + }, + { + "ArchStdEvent": "BR_INDNR_TAKEN_RETIRED", + "PublicDescription": "This event counts architecturally executed indirect branches excluding procedure returns that were taken." + }, + { + "ArchStdEvent": "BR_IMMED_PRED_RETIRED", + "PublicDescription": "This event counts architecturally executed direct branches that were correctly predicted." + }, + { + "ArchStdEvent": "BR_IMMED_MIS_PRED_RETIRED", + "PublicDescription": "This event counts architecturally executed direct branches that were mispredicted and caused a pipeline flush." + }, + { + "ArchStdEvent": "BR_IND_PRED_RETIRED", + "PublicDescription": "This event counts architecturally executed indirect branches including procedure returns that were correctly predicted." + }, + { + "ArchStdEvent": "BR_IND_MIS_PRED_RETIRED", + "PublicDescription": "This event counts architecturally executed indirect branches including procedure returns that were mispredicted and caused a pipeline flush." + }, + { + "ArchStdEvent": "BR_RETURN_PRED_RETIRED", + "PublicDescription": "This event counts architecturally executed procedure returns that were correctly predicted." + }, + { + "ArchStdEvent": "BR_RETURN_MIS_PRED_RETIRED", + "PublicDescription": "This event counts architecturally executed procedure returns that were mispredicted and caused a pipeline flush." + }, + { + "ArchStdEvent": "BR_INDNR_PRED_RETIRED", + "PublicDescription": "This event counts architecturally executed indirect branches excluding procedure returns that were correctly predicted." + }, + { + "ArchStdEvent": "BR_INDNR_MIS_PRED_RETIRED", + "PublicDescription": "This event counts architecturally executed indirect branches excluding procedure returns that were mispredicted and caused a pipeline flush." + }, + { + "ArchStdEvent": "BR_TAKEN_PRED_RETIRED", + "PublicDescription": "This event counts architecturally executed branches that were taken and were correctly predicted." + }, + { + "ArchStdEvent": "BR_TAKEN_MIS_PRED_RETIRED", + "PublicDescription": "This event counts architecturally executed branches that were taken and were mispredicted causing a pipeline flush." + }, + { + "ArchStdEvent": "BR_SKIP_PRED_RETIRED", + "PublicDescription": "This event counts architecturally executed branches that were not taken and were correctly predicted." + }, + { + "ArchStdEvent": "BR_SKIP_MIS_PRED_RETIRED", + "PublicDescription": "This event counts architecturally executed branches that were not taken and were mispredicted causing a pipeline flush." + }, + { + "ArchStdEvent": "BR_PRED_RETIRED", + "PublicDescription": "This event counts branch instructions counted by BR_RETIRED which were correctly predicted." + }, + { + "ArchStdEvent": "BR_IND_RETIRED", + "PublicDescription": "This event counts architecturally executed indirect branches including procedure returns." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/spe.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/spe.json new file mode 100644 index 000000000000..00d0c5051a48 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/spe.json @@ -0,0 +1,42 @@ +[ + { + "ArchStdEvent": "SAMPLE_POP", + "PublicDescription": "This event counts statistical profiling sample population, the count of all operations that could be sampled but may or may not be chosen for sampling." + }, + { + "ArchStdEvent": "SAMPLE_FEED", + "PublicDescription": "This event counts statistical profiling samples taken for sampling." + }, + { + "ArchStdEvent": "SAMPLE_FILTRATE", + "PublicDescription": "This event counts statistical profiling samples taken which are not removed by filtering." + }, + { + "ArchStdEvent": "SAMPLE_COLLISION", + "PublicDescription": "This event counts statistical profiling samples that have collided with a previous sample and so therefore not taken." + }, + { + "ArchStdEvent": "SAMPLE_FEED_BR", + "PublicDescription": "This event counts statistical profiling samples taken which are branches." + }, + { + "ArchStdEvent": "SAMPLE_FEED_LD", + "PublicDescription": "This event counts statistical profiling samples taken which are Loads or Load atomic operations." + }, + { + "ArchStdEvent": "SAMPLE_FEED_ST", + "PublicDescription": "This event counts statistical profiling samples taken which are Stores or Store atomic operations." + }, + { + "ArchStdEvent": "SAMPLE_FEED_OP", + "PublicDescription": "This event counts statistical profiling samples taken which are matching any operation type filters supported." + }, + { + "ArchStdEvent": "SAMPLE_FEED_EVENT", + "PublicDescription": "This event counts statistical profiling samples taken which are matching event packet filter constraints." + }, + { + "ArchStdEvent": "SAMPLE_FEED_LAT", + "PublicDescription": "This event counts statistical profiling samples taken which are exceeding minimum latency set by operation latency filter constraints." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/spec_operation.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/spec_operation.json new file mode 100644 index 000000000000..8bc802f5f350 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/spec_operation.json @@ -0,0 +1,230 @@ +[ + { + "ArchStdEvent": "INST_SPEC", + "PublicDescription": "This event counts operations that have been speculatively executed." + }, + { + "ArchStdEvent": "OP_SPEC", + "PublicDescription": "This event counts micro-operations speculatively executed. This is the count of the number of micro-operations dispatched in a cycle." + }, + { + "ArchStdEvent": "UNALIGNED_LD_SPEC", + "PublicDescription": "This event counts unaligned memory Read operations issued by the CPU. This event counts unaligned accesses (as defined by the actual instruction), even if they are subsequently issued as multiple aligned accesses.\nThis event does not count preload operations (PLD, PLI).\nThis event is a subset of the UNALIGNED_LDST_SPEC event." + }, + { + "ArchStdEvent": "UNALIGNED_ST_SPEC", + "PublicDescription": "This event counts unaligned memory Write operations issued by the CPU. This event counts unaligned accesses (as defined by the actual instruction), even if they are subsequently issued as multiple aligned accesses.\nThis event is a subset of the UNALIGNED_LDST_SPEC event." + }, + { + "ArchStdEvent": "UNALIGNED_LDST_SPEC", + "PublicDescription": "This event counts unaligned memory operations issued by the CPU. This event counts unaligned accesses (as defined by the actual instruction), even if they are subsequently issued as multiple aligned accesses.\nThis event is the sum of the following events:\nUNALIGNED_ST_SPEC and\nUNALIGNED_LD_SPEC." + }, + { + "ArchStdEvent": "LDREX_SPEC", + "PublicDescription": "This event counts Load-Exclusive operations that have been speculatively executed. For example: LDREX, LDX" + }, + { + "ArchStdEvent": "STREX_PASS_SPEC", + "PublicDescription": "This event counts Store-exclusive operations that have been speculatively executed and have successfully completed the Store operation." + }, + { + "ArchStdEvent": "STREX_FAIL_SPEC", + "PublicDescription": "This event counts Store-exclusive operations that have been speculatively executed and have not successfully completed the Store operation." + }, + { + "ArchStdEvent": "STREX_SPEC", + "PublicDescription": "This event counts Store-exclusive operations that have been speculatively executed.\nThis event is the sum of the following events:\nSTREX_PASS_SPEC and\nSTREX_FAIL_SPEC." + }, + { + "ArchStdEvent": "LD_SPEC", + "PublicDescription": "This event counts speculatively executed Load operations including Single Instruction Multiple Data (SIMD) Load operations." + }, + { + "ArchStdEvent": "ST_SPEC", + "PublicDescription": "This event counts speculatively executed Store operations including Single Instruction Multiple Data (SIMD) Store operations." + }, + { + "ArchStdEvent": "LDST_SPEC", + "PublicDescription": "This event counts Load and Store operations that have been speculatively executed." + }, + { + "ArchStdEvent": "DP_SPEC", + "PublicDescription": "This event counts speculatively executed logical or arithmetic instructions such as MOV/MVN operations." + }, + { + "ArchStdEvent": "ASE_SPEC", + "PublicDescription": "This event counts speculatively executed Advanced SIMD operations excluding Load, Store, and Move micro-operations that move data to or from SIMD (vector) registers." + }, + { + "ArchStdEvent": "VFP_SPEC", + "PublicDescription": "This event counts speculatively executed floating point operations. This event does not count operations that move data to or from floating point (vector) registers." + }, + { + "ArchStdEvent": "PC_WRITE_SPEC", + "PublicDescription": "This event counts speculatively executed operations which cause software changes of the PC. Those operations include all taken branch operations." + }, + { + "ArchStdEvent": "CRYPTO_SPEC", + "PublicDescription": "This event counts speculatively executed cryptographic operations except for PMULL and VMULL operations." + }, + { + "ArchStdEvent": "BR_IMMED_SPEC", + "PublicDescription": "This event counts direct branch operations which are speculatively executed." + }, + { + "ArchStdEvent": "BR_RETURN_SPEC", + "PublicDescription": "This event counts procedure return operations (RET, RETAA and RETAB) which are speculatively executed." + }, + { + "ArchStdEvent": "BR_INDIRECT_SPEC", + "PublicDescription": "This event counts indirect branch operations including procedure returns, which are speculatively executed. This includes operations that force a software change of the PC, other than exception-generating operations and direct branch instructions. Some examples of the instructions counted by this event include BR Xn, RET, etc." + }, + { + "ArchStdEvent": "ISB_SPEC", + "PublicDescription": "This event counts ISB operations that are executed." + }, + { + "ArchStdEvent": "DSB_SPEC", + "PublicDescription": "This event counts DSB operations that are speculatively issued to Load/Store unit in the CPU." + }, + { + "ArchStdEvent": "DMB_SPEC", + "PublicDescription": "This event counts DMB operations that are speculatively issued to the Load/Store unit in the CPU. This event does not count implied barriers from Load-acquire/Store-release operations." + }, + { + "ArchStdEvent": "CSDB_SPEC", + "PublicDescription": "This event counts CSDB operations that are speculatively issued to the Load/Store unit in the CPU. This event does not count implied barriers from Load-acquire/Store-release operations." + }, + { + "ArchStdEvent": "RC_LD_SPEC", + "PublicDescription": "This event counts any Load acquire operations that are speculatively executed. For example: LDAR, LDARH, LDARB" + }, + { + "ArchStdEvent": "RC_ST_SPEC", + "PublicDescription": "This event counts any Store release operations that are speculatively executed. For example: STLR, STLRH, STLRB" + }, + { + "ArchStdEvent": "SIMD_INST_SPEC", + "PublicDescription": "This event counts speculatively executed operations that are SIMD or SVE vector operations or Advanced SIMD non-scalar operations." + }, + { + "ArchStdEvent": "ASE_INST_SPEC", + "PublicDescription": "This event counts speculatively executed Advanced SIMD operations." + }, + { + "ArchStdEvent": "SVE_INST_SPEC", + "PublicDescription": "This event counts speculatively executed operations that are SVE operations." + }, + { + "ArchStdEvent": "INT_SPEC", + "PublicDescription": "This event counts speculatively executed integer arithmetic operations." + }, + { + "ArchStdEvent": "SVE_PRED_SPEC", + "PublicDescription": "This event counts speculatively executed predicated SVE operations.\nThis counter also counts SVE operation due to instruction with Governing predicate operand that determines the Active elements that do not write to any SVE Z vector destination register using either zeroing or merging predicate. Thus, the operations due to instructions such as INCP, DECP, UQINCP, UQDECP, SQINCP, SQDECP and PNEXT, are counted by the SVE_PRED_* events." + }, + { + "ArchStdEvent": "SVE_PRED_EMPTY_SPEC", + "PublicDescription": "This event counts speculatively executed predicated SVE operations with no active predicate elements.\nThis counter also counts SVE operation due to instruction with Governing predicate operand that determines the Active elements that do not write to any SVE Z vector destination register using either zeroing or merging predicate. Thus, the operations due to instructions such as INCP, DECP, UQINCP, UQDECP, SQINCP, SQDECP and PNEXT, are counted by the SVE_PRED_* events." + }, + { + "ArchStdEvent": "SVE_PRED_FULL_SPEC", + "PublicDescription": "This event counts speculatively executed predicated SVE operations with all predicate elements active.\nThis counter also counts SVE operation due to instruction with Governing predicate operand that determines the Active elements that do not write to any SVE Z vector destination register using either zeroing or merging predicate. Thus, the operations due to instructions such as INCP, DECP, UQINCP, UQDECP, SQINCP, SQDECP and PNEXT, are counted by the SVE_PRED_* events." + }, + { + "ArchStdEvent": "SVE_PRED_PARTIAL_SPEC", + "PublicDescription": "This event counts speculatively executed predicated SVE operations with at least one but not all active predicate elements.\nThis counter also counts SVE operation due to instruction with Governing predicate operand that determines the Active elements that do not write to any SVE Z vector destination register using either zeroing or merging predicate. Thus, the operations due to instructions such as INCP, DECP, UQINCP, UQDECP, SQINCP, SQDECP and PNEXT, are counted by the SVE_PRED_* events." + }, + { + "ArchStdEvent": "SVE_PRED_NOT_FULL_SPEC", + "PublicDescription": "This event counts speculatively executed predicated SVE operations with at least one non active predicate elements.\nThis counter also counts SVE operation due to instruction with Governing predicate operand that determines the Active elements that do not write to any SVE Z vector destination register using either zeroing or merging predicate. Thus, the operations due to instructions such as INCP, DECP, UQINCP, UQDECP, SQINCP, SQDECP and PNEXT, are counted by the SVE_PRED_* events." + }, + { + "ArchStdEvent": "PRF_SPEC", + "PublicDescription": "This event counts speculatively executed operations that prefetch memory. For example, Scalar: PRFM, SVE: PRFB, PRFD, PRFH, or PRFW." + }, + { + "ArchStdEvent": "SVE_LDFF_SPEC", + "PublicDescription": "This event counts speculatively executed SVE first fault or non-fault Load operations." + }, + { + "ArchStdEvent": "SVE_LDFF_FAULT_SPEC", + "PublicDescription": "This event counts speculatively executed SVE first fault or non-fault Load operations that clear at least one bit in the FFR." + }, + { + "ArchStdEvent": "ASE_SVE_INT8_SPEC", + "PublicDescription": "This event counts speculatively executed Advanced SIMD or SVE integer operations with the largest data type being an 8-bit integer." + }, + { + "ArchStdEvent": "ASE_SVE_INT16_SPEC", + "PublicDescription": "This event counts speculatively executed Advanced SIMD or SVE integer operations with the largest data type a 16-bit integer." + }, + { + "ArchStdEvent": "ASE_SVE_INT32_SPEC", + "PublicDescription": "This event counts speculatively executed Advanced SIMD or SVE integer operations with the largest data type a 32-bit integer." + }, + { + "ArchStdEvent": "ASE_SVE_INT64_SPEC", + "PublicDescription": "This event counts speculatively executed Advanced SIMD or SVE integer operations with the largest data type a 64-bit integer." + }, + { + "EventCode": "0x011d", + "EventName": "SPEC_RET_STACK_FULL", + "PublicDescription": "This event counts predict pipe stalls due to speculative return address predictor full." + }, + { + "EventCode": "0x011f", + "EventName": "MOPS_SPEC", + "PublicDescription": "Macro-ops speculatively decoded." + }, + { + "EventCode": "0x0180", + "EventName": "BR_SPEC_PRED_TAKEN", + "PublicDescription": "Number of predicted taken from branch predictor." + }, + { + "EventCode": "0x0181", + "EventName": "BR_SPEC_PRED_TAKEN_FROM_L2BTB", + "PublicDescription": "Number of predicted taken branch from L2 BTB." + }, + { + "EventCode": "0x0182", + "EventName": "BR_SPEC_PRED_TAKEN_MULTI", + "PublicDescription": "Number of predicted taken for polymorphic branch." + }, + { + "EventCode": "0x0185", + "EventName": "BR_SPEC_PRED_STATIC", + "PublicDescription": "Number of post fetch prediction." + }, + { + "EventCode": "0x01d0", + "EventName": "TLBI_LOCAL_SPEC", + "PublicDescription": "A non-broadcast TLBI instruction executed (Speculatively or otherwise) on *this* PE." + }, + { + "EventCode": "0x01d1", + "EventName": "TLBI_BROADCAST_SPEC", + "PublicDescription": "A broadcast TLBI instruction executed (Speculatively or otherwise) on *this* PE." + }, + { + "EventCode": "0x01e7", + "EventName": "BR_SPEC_PRED_ALN_REDIR", + "PublicDescription": "BPU predict pipe align redirect (either AL-APQ hit/miss)." + }, + { + "EventCode": "0x0200", + "EventName": "SIMD_CRYPTO_INST_SPEC", + "PublicDescription": "SIMD, SVE, and CRYPTO instructions speculatively decoded." + }, + { + "EventCode": "0x022e", + "EventName": "VPRED_LD_SPEC", + "PublicDescription": "This event counts the number of Speculatively-executed-Load operations with addresses produced by the value-prediction mechanism. The loaded data might be discarded if the predicted address differs from the actual address." + }, + { + "EventCode": "0x022f", + "EventName": "VPRED_LD_SPEC_MISMATCH", + "PublicDescription": "This event counts a subset of VPRED_LD_SPEC where the predicted Load address and the actual address mismatched." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/stall.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/stall.json new file mode 100644 index 000000000000..92d9e0866c24 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/stall.json @@ -0,0 +1,145 @@ +[ + { + "ArchStdEvent": "STALL_FRONTEND", + "PublicDescription": "This event counts cycles when frontend could not send any micro-operations to the rename stage because of frontend resource stalls caused by fetch memory latency or branch prediction flow stalls. STALL_FRONTEND_SLOTS counts SLOTS during the cycle when this event counts. STALL_SLOT_FRONTEND will count SLOTS when this event is counted on this CPU." + }, + { + "ArchStdEvent": "STALL_BACKEND", + "PublicDescription": "This event counts cycles whenever the rename unit is unable to send any micro-operations to the backend of the pipeline because of backend resource constraints. Backend resource constraints can include issue stage fullness, execution stage fullness, or other internal pipeline resource fullness. All the backend slots were empty during the cycle when this event counts." + }, + { + "ArchStdEvent": "STALL", + "PublicDescription": "This event counts cycles when no operations are sent to the rename unit from the frontend or from the rename unit to the backend for any reason (either frontend or backend stall). This event is the sum of the following events:\nSTALL_FRONTEND and\nSTALL_BACKEND." + }, + { + "ArchStdEvent": "STALL_SLOT_BACKEND", + "PublicDescription": "This event counts slots per cycle in which no operations are sent from the rename unit to the backend due to backend resource constraints. STALL_BACKEND counts during the cycle when STALL_SLOT_BACKEND counts at least 1. STALL_BACKEND counts during the cycle when STALL_SLOT_BACKEND is SLOTS." + }, + { + "ArchStdEvent": "STALL_SLOT_FRONTEND", + "PublicDescription": "This event counts slots per cycle in which no operations are sent to the rename unit from the frontend due to frontend resource constraints. STALL_FRONTEND counts during the cycle when STALL_SLOT_FRONTEND is SLOTS." + }, + { + "ArchStdEvent": "STALL_SLOT", + "PublicDescription": "This event counts slots per cycle in which no operations are sent to the rename unit from the frontend or from the rename unit to the backend for any reason (either frontend or backend stall).\nSTALL_SLOT is the sum of the following events:\nSTALL_SLOT_FRONTEND and\nSTALL_SLOT_BACKEND." + }, + { + "ArchStdEvent": "STALL_BACKEND_MEM", + "PublicDescription": "This event counts cycles when the backend is stalled because there is a pending demand Load request in progress in the last level Core cache.\nLast level cache in this CPU is Level 2, hence this event counts same as STALL_BACKEND_L2D." + }, + { + "ArchStdEvent": "STALL_FRONTEND_MEMBOUND", + "PublicDescription": "This event counts cycles when the frontend could not send any micro-operations to the rename stage due to resource constraints in the memory resources." + }, + { + "ArchStdEvent": "STALL_FRONTEND_L1I", + "PublicDescription": "This event counts cycles when the frontend is stalled because there is an instruction fetch request pending in the L1 I-cache." + }, + { + "ArchStdEvent": "STALL_FRONTEND_MEM", + "PublicDescription": "This event counts cycles when the frontend is stalled because there is an instruction fetch request pending in the last level Core cache.\nLast level cache in this CPU is Level 2, hence this event counts rather than STALL_FRONTEND_L2I." + }, + { + "ArchStdEvent": "STALL_FRONTEND_TLB", + "PublicDescription": "This event counts when the frontend is stalled on any TLB misses being handled. This event also counts the TLB accesses made by hardware prefetches." + }, + { + "ArchStdEvent": "STALL_FRONTEND_CPUBOUND", + "PublicDescription": "This event counts cycles when the frontend could not send any micro-operations to the rename stage due to resource constraints in the CPU resources excluding memory resources." + }, + { + "ArchStdEvent": "STALL_FRONTEND_FLOW", + "PublicDescription": "This event counts cycles when the frontend could not send any micro-operations to the rename stage due to resource constraints in the branch prediction unit." + }, + { + "ArchStdEvent": "STALL_FRONTEND_FLUSH", + "PublicDescription": "This event counts cycles when the frontend could not send any micro-operations to the rename stage as the frontend is recovering from a machine flush or resteer. Example scenarios that cause a flush include branch mispredictions, taken exceptions, microarchitectural flush etc." + }, + { + "ArchStdEvent": "STALL_BACKEND_MEMBOUND", + "PublicDescription": "This event counts cycles when the backend could not accept any micro-operations due to resource constraints in the memory resources." + }, + { + "ArchStdEvent": "STALL_BACKEND_L1D", + "PublicDescription": "This event counts cycles when the backend is stalled because there is a pending demand Load request in progress in the L1 D-cache." + }, + { + "ArchStdEvent": "STALL_BACKEND_TLB", + "PublicDescription": "This event counts cycles when the backend is stalled on any demand TLB misses being handled." + }, + { + "ArchStdEvent": "STALL_BACKEND_ST", + "PublicDescription": "This event counts cycles when the backend is stalled and there is a Store that has not reached the pre-commit stage." + }, + { + "ArchStdEvent": "STALL_BACKEND_CPUBOUND", + "PublicDescription": "This event counts cycles when the backend could not accept any micro-operations due to any resource constraints in the CPU excluding memory resources." + }, + { + "ArchStdEvent": "STALL_BACKEND_BUSY", + "PublicDescription": "This event counts cycles when the backend could not accept any micro-operations because the issue queues are full to take any operations for execution." + }, + { + "ArchStdEvent": "STALL_BACKEND_ILOCK", + "PublicDescription": "This event counts cycles when the backend could not accept any micro-operations due to resource constraints imposed by input dependency." + }, + { + "ArchStdEvent": "STALL_BACKEND_RENAME", + "PublicDescription": "This event counts cycles when backend is stalled even when operations are available from the frontend but at least one is not ready to be sent to the backend because no rename register is available." + }, + { + "EventCode": "0x0158", + "EventName": "FLAG_DISP_STALL", + "PublicDescription": "Rename stalled due to FRF(Flag register file) full." + }, + { + "EventCode": "0x0159", + "EventName": "GEN_DISP_STALL", + "PublicDescription": "Rename stalled due to GRF (General-purpose register file) full." + }, + { + "EventCode": "0x015a", + "EventName": "VEC_DISP_STALL", + "PublicDescription": "Rename stalled due to VRF (Vector register file) full." + }, + { + "EventCode": "0x015c", + "EventName": "SX_IQ_STALL", + "PublicDescription": "Dispatch stalled due to IQ full, SX." + }, + { + "EventCode": "0x015d", + "EventName": "MX_IQ_STALL", + "PublicDescription": "Dispatch stalled due to IQ full, MX." + }, + { + "EventCode": "0x015e", + "EventName": "LS_IQ_STALL", + "PublicDescription": "Dispatch stalled due to IQ full, LS." + }, + { + "EventCode": "0x015f", + "EventName": "VX_IQ_STALL", + "PublicDescription": "Dispatch stalled due to IQ full, VX." + }, + { + "EventCode": "0x0160", + "EventName": "MCQ_FULL_STALL", + "PublicDescription": "Dispatch stalled due to MCQ full." + }, + { + "EventCode": "0x01cf", + "EventName": "PRD_DISP_STALL", + "PublicDescription": "Rename stalled due to predicate registers (physical) are full." + }, + { + "EventCode": "0x01e0", + "EventName": "CSDB_STALL", + "PublicDescription": "Rename stalled due to CSDB." + }, + { + "EventCode": "0x01e2", + "EventName": "STALL_SLOT_FRONTEND_WITHOUT_MISPRED", + "PublicDescription": "Stall slot frontend during non-mispredicted branch.\nThis event counts the STALL_STOT_FRONTEND Events, except for the 4 cycles following a mispredicted branch Event or 4 cycles following a commit flush&restart Event." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/nvidia/t410/tlb.json b/tools/perf/pmu-events/arch/arm64/nvidia/t410/tlb.json new file mode 100644 index 000000000000..18ec5c348c87 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/nvidia/t410/tlb.json @@ -0,0 +1,158 @@ +[ + { + "ArchStdEvent": "L1I_TLB_REFILL", + "PublicDescription": "This event counts L1 Instruction TLB refills from any instruction fetch (demand, hardware prefetch, and software preload accesses). If there are multiple misses in the TLB that are resolved by the refill, then this event only counts once. This event will not count if the translation table walk results in a fault (such as a translation or access fault), since there is no new translation created for the TLB." + }, + { + "ArchStdEvent": "L1D_TLB_REFILL", + "PublicDescription": "This event counts L1 Data TLB accesses that resulted in TLB refills. If there are multiple misses in the TLB that are resolved by the refill, then this event only counts once. This event counts for refills caused by preload instructions or hardware prefetch accesses. This event counts regardless of whether the miss hits in L2 or results in a translation table walk. This event will not count if the translation table walk results in a fault (such as a translation or access fault), since there is no new translation created for the TLB. This event will not count on an access from an AT (Address Translation) instruction.\nThis event counts the sum of the following events:\nL1D_TLB_REFILL_RD and\nL1D_TLB_REFILL_WR." + }, + { + "ArchStdEvent": "L1D_TLB", + "PublicDescription": "This event counts L1 Data TLB accesses caused by any memory Load or Store operation.\nNote that Load or Store instructions can be broken up into multiple memory operations.\nThis event does not count TLB maintenance operations." + }, + { + "ArchStdEvent": "L1I_TLB", + "PublicDescription": "This event counts L1 instruction TLB accesses (caused by demand or hardware prefetch or software preload accesses), whether the access hits or misses in the TLB. This event counts both demand accesses and prefetch or preload generated accesses.\nThis event is a superset of the L1I_TLB_REFILL event." + }, + { + "ArchStdEvent": "L2D_TLB_REFILL", + "PublicDescription": "This event counts L2 TLB refills caused by memory operations from both data and instruction fetch, except for those caused by TLB maintenance operations and hardware prefetches.\nThis event is the sum of the following events:\nL2D_TLB_REFILL_RD and\nL2D_TLB_REFILL_WR." + }, + { + "ArchStdEvent": "L2D_TLB", + "PublicDescription": "This event counts L2 TLB accesses except those caused by TLB maintenance operations.\nThis event is the sum of the following events:\nL2D_TLB_RD and\nL2D_TLB_WR." + }, + { + "ArchStdEvent": "DTLB_WALK", + "PublicDescription": "This event counts number of demand data translation table walks caused by a miss in the L2 TLB and performing at least one memory access. Translation table walks are counted even if the translation ended up taking a translation fault for reasons different than EPD, E0PD and NFD. Note that partial translations that cause a translation table walk are also counted. Also note that this event counts walks triggered by software preloads, but not walks triggered by hardware prefetchers, and that this event does not count walks triggered by TLB maintenance operations.\nThis event does not include prefetches." + }, + { + "ArchStdEvent": "ITLB_WALK", + "PublicDescription": "This event counts number of instruction translation table walks caused by a miss in the L2 TLB and performing at least one memory access. Translation table walks are counted even if the translation ended up taking a translation fault for reasons different than EPD, E0PD and NFD. Note that partial translations that cause a translation table walk are also counted. Also note that this event does not count walks triggered by TLB maintenance operations.\nThis event does not include prefetches." + }, + { + "ArchStdEvent": "L1D_TLB_REFILL_RD", + "PublicDescription": "This event counts L1 Data TLB refills caused by memory Read operations. If there are multiple misses in the TLB that are resolved by the refill, then this event only counts once. This event counts for refills caused by preload instructions or hardware prefetch accesses. This event counts regardless of whether the miss hits in L2 or results in a translation table walk. This event will not count if the translation table walk results in a fault (such as a translation or access fault), since there is no new translation created for the TLB. This event will not count on an access from an Address Translation (AT) instruction.\nThis event is a subset of the L1D_TLB_REFILL event." + }, + { + "ArchStdEvent": "L1D_TLB_REFILL_WR", + "PublicDescription": "This event counts L1 Data TLB refills caused by data side memory Write operations. If there are multiple misses in the TLB that are resolved by the refill, then this event only counts once. This event counts for refills caused by preload instructions or hardware prefetch accesses. This event counts regardless of whether the miss hits in L2 or results in a translation table walk. This event will not count if the table walk results in a fault (such as a translation or access fault), since there is no new translation created for the TLB. This event will not count with an access from an Address Translation (AT) instruction.\nThis event is a subset of the L1D_TLB_REFILL event." + }, + { + "ArchStdEvent": "L1D_TLB_RD", + "PublicDescription": "This event counts L1 Data TLB accesses caused by memory Read operations. This event counts whether the access hits or misses in the TLB. This event does not count TLB maintenance operations." + }, + { + "ArchStdEvent": "L1D_TLB_WR", + "PublicDescription": "This event counts any L1 Data side TLB accesses caused by memory Write operations. This event counts whether the access hits or misses in the TLB. This event does not count TLB maintenance operations." + }, + { + "ArchStdEvent": "L2D_TLB_REFILL_RD", + "PublicDescription": "This event counts L2 TLB refills caused by memory Read operations from both data and instruction fetch except for those caused by TLB maintenance operations or hardware prefetches.\nThis event is a subset of the L2D_TLB_REFILL event." + }, + { + "ArchStdEvent": "L2D_TLB_REFILL_WR", + "PublicDescription": "This event counts L2 TLB refills caused by memory Write operations from both data and instruction fetch except for those caused by TLB maintenance operations.\nThis event is a subset of the L2D_TLB_REFILL event." + }, + { + "ArchStdEvent": "L2D_TLB_RD", + "PublicDescription": "This event counts L2 TLB accesses caused by memory Read operations from both data and instruction fetch except for those caused by TLB maintenance operations.\nThis event is a subset of the L2D_TLB event." + }, + { + "ArchStdEvent": "L2D_TLB_WR", + "PublicDescription": "This event counts L2 TLB accesses caused by memory Write operations from both data and instruction fetch except for those caused by TLB maintenance operations.\nThis event is a subset of the L2D_TLB event." + }, + { + "ArchStdEvent": "DTLB_WALK_PERCYC", + "PublicDescription": "This event counts the number of data translation table walks in progress per cycle." + }, + { + "ArchStdEvent": "ITLB_WALK_PERCYC", + "PublicDescription": "This event counts the number of instruction translation table walks in progress per cycle." + }, + { + "ArchStdEvent": "L1D_TLB_RW", + "PublicDescription": "This event counts L1 Data TLB demand accesses caused by memory Read or Write operations. This event counts whether the access hits or misses in the TLB. This event does not count TLB maintenance operations." + }, + { + "ArchStdEvent": "L1I_TLB_RD", + "PublicDescription": "This event counts L1 Instruction TLB demand accesses whether the access hits or misses in the TLB." + }, + { + "ArchStdEvent": "L1D_TLB_PRFM", + "PublicDescription": "This event counts L1 Data TLB accesses generated by software prefetch or preload memory accesses. Load or Store instructions can be broken into multiple memory operations. This event does not count TLB maintenance operations." + }, + { + "ArchStdEvent": "L1I_TLB_PRFM", + "PublicDescription": "This event counts L1 Instruction TLB accesses generated by software preload or prefetch instructions. This event counts whether the access hits or misses in the TLB. This event does not count TLB maintenance operations." + }, + { + "ArchStdEvent": "DTLB_HWUPD", + "PublicDescription": "This event counts number of memory accesses triggered by a data translation table walk and performing an update of a translation table entry. Memory accesses are counted even if the translation ended up taking a translation fault for reasons different than EPD, E0PD and NFD. Note that this event counts accesses triggered by software preloads, but not accesses triggered by hardware prefetchers." + }, + { + "ArchStdEvent": "ITLB_HWUPD", + "PublicDescription": "This event counts number of memory accesses triggered by an instruction translation table walk and performing an update of a translation table entry. Memory accesses are counted even if the translation ended up taking a translation fault for reasons different than EPD, E0PD and NFD." + }, + { + "ArchStdEvent": "DTLB_STEP", + "PublicDescription": "This event counts number of memory accesses triggered by a demand data translation table walk and performing a Read of a translation table entry. Memory accesses are counted even if the translation ended up taking a translation fault for reasons different than EPD, E0PD and NFD.\nNote that this event counts accesses triggered by software preloads, but not accesses triggered by hardware prefetchers." + }, + { + "ArchStdEvent": "ITLB_STEP", + "PublicDescription": "This event counts number of memory accesses triggered by an instruction translation table walk and performing a Read of a translation table entry. Memory accesses are counted even if the translation ended up taking a translation fault for reasons different than EPD, E0PD and NFD." + }, + { + "ArchStdEvent": "DTLB_WALK_LARGE", + "PublicDescription": "This event counts number of demand data translation table walks caused by a miss in the L2 TLB and yielding a large page. The set of large pages is defined as all pages with a final size higher than or equal to 2MB. Translation table walks that end up taking a translation fault are not counted, as the page size would be undefined in that case. If DTLB_WALK_BLOCK is implemented, then it is an alias for this event in this family.\nNote that partial translations that cause a translation table walk are also counted.\nAlso note that this event counts walks triggered by software preloads, but not walks triggered by hardware prefetchers, and that this event does not count walks triggered by TLB maintenance operations." + }, + { + "ArchStdEvent": "ITLB_WALK_LARGE", + "PublicDescription": "This event counts number of instruction translation table walks caused by a miss in the L2 TLB and yielding a large page. The set of large pages is defined as all pages with a final size higher than or equal to 2MB. Translation table walks that end up taking a translation fault are not counted, as the page size would be undefined in that case. In this family, this is equal to ITLB_WALK_BLOCK event.\nNote that partial translations that cause a translation table walk are also counted.\nAlso note that this event does not count walks triggered by TLB maintenance operations." + }, + { + "ArchStdEvent": "DTLB_WALK_SMALL", + "PublicDescription": "This event counts number of data translation table walks caused by a miss in the L2 TLB and yielding a small page. The set of small pages is defined as all pages with a final size lower than 2MB. Translation table walks that end up taking a translation fault are not counted, as the page size would be undefined in that case. If DTLB_WALK_PAGE event is implemented, then it is an alias for this event in this family. Note that partial translations that cause a translation table walk are also counted.\nAlso note that this event counts walks triggered by software preloads, but not walks triggered by hardware prefetchers, and that this event does not count walks triggered by TLB maintenance operations." + }, + { + "ArchStdEvent": "ITLB_WALK_SMALL", + "PublicDescription": "This event counts number of instruction translation table walks caused by a miss in the L2 TLB and yielding a small page. The set of small pages is defined as all pages with a final size lower than 2MB. Translation table walks that end up taking a translation fault are not counted, as the page size would be undefined in that case. In this family, this is equal to ITLB_WALK_PAGE event.\nNote that partial translations that cause a translation table walk are also counted.\nAlso note that this event does not count walks triggered by TLB maintenance operations." + }, + { + "ArchStdEvent": "DTLB_WALK_RW", + "PublicDescription": "This event counts number of demand data translation table walks caused by a miss in the L2 TLB and performing at least one memory access. Translation table walks are counted even if the translation ended up taking a translation fault for reasons different than EPD, E0PD and NFD.\nNote that partial translations that cause a translation table walk are also counted.\nAlso note that this event does not count walks triggered by TLB maintenance operations." + }, + { + "ArchStdEvent": "ITLB_WALK_RD", + "PublicDescription": "This event counts number of demand instruction translation table walks caused by a miss in the L2 TLB and performing at least one memory access. Translation table walks are counted even if the translation ended up taking a translation fault for reasons different than EPD, E0PD and NFD.\nNote that partial translations that cause a translation table walk are also counted.\nAlso note that this event does not count walks triggered by TLB maintenance operations." + }, + { + "ArchStdEvent": "DTLB_WALK_PRFM", + "PublicDescription": "This event counts number of software prefetches or preloads generated data translation table walks caused by a miss in the L2 TLB and performing at least one memory access. Translation table walks are counted even if the translation ended up taking a translation fault for reasons different than EPD, E0PD and NFD.\nNote that partial translations that cause a translation table walk are also counted.\nAlso note that this event does not count walks triggered by TLB maintenance operations." + }, + { + "ArchStdEvent": "ITLB_WALK_PRFM", + "PublicDescription": "This event counts number of software prefetches or preloads generated instruction translation table walks caused by a miss in the L2 TLB and performing at least one memory access. Translation table walks are counted even if the translation ended up taking a translation fault for reasons different than EPD, E0PD and NFD.\nNote that partial translations that cause a translation table walk are also counted.\nAlso note that this event does not count walks triggered by TLB maintenance operations." + }, + { + "EventCode": "0x010e", + "EventName": "L1D_TLB_REFILL_RD_PF", + "PublicDescription": "L1 Data TLB refill, Read, prefetch." + }, + { + "EventCode": "0x010f", + "EventName": "L2TLB_PF_REFILL", + "PublicDescription": "L2 Data TLB refill, Read, prefetch.\nThis event counts MMU refills due to internal PFStream requests." + }, + { + "EventCode": "0x0223", + "EventName": "L1I_TLB_REFILL_RD", + "PublicDescription": "L1 Instruction TLB refills due to Demand miss." + }, + { + "EventCode": "0x0224", + "EventName": "L1I_TLB_REFILL_PRFM", + "PublicDescription": "L1 Instruction TLB refills due to Software prefetch miss." + } +] diff --git a/tools/perf/pmu-events/arch/common/common/metrics.json b/tools/perf/pmu-events/arch/common/common/metrics.json index 0d010b3ebc6d..cefc8bfe7830 100644 --- a/tools/perf/pmu-events/arch/common/common/metrics.json +++ b/tools/perf/pmu-events/arch/common/common/metrics.json @@ -46,14 +46,14 @@ }, { "BriefDescription": "Max front or backend stalls per instruction", - "MetricExpr": "max(stalled\\-cycles\\-frontend, stalled\\-cycles\\-backend) / instructions", + "MetricExpr": "(max(stalled\\-cycles\\-frontend, stalled\\-cycles\\-backend) / instructions) if (has_event(stalled\\-cycles\\-frontend) & has_event(stalled\\-cycles\\-backend)) else ((stalled\\-cycles\\-frontend / instructions) if has_event(stalled\\-cycles\\-frontend) else ((stalled\\-cycles\\-backend / instructions) if has_event(stalled\\-cycles\\-backend) else 0))", "MetricGroup": "Default", "MetricName": "stalled_cycles_per_instruction", "DefaultShowEvents": "1" }, { "BriefDescription": "Frontend stalls per cycle", - "MetricExpr": "stalled\\-cycles\\-frontend / cpu\\-cycles", + "MetricExpr": "(stalled\\-cycles\\-frontend / cpu\\-cycles) if has_event(stalled\\-cycles\\-frontend) else 0", "MetricGroup": "Default", "MetricName": "frontend_cycles_idle", "MetricThreshold": "frontend_cycles_idle > 0.1", @@ -61,7 +61,7 @@ }, { "BriefDescription": "Backend stalls per cycle", - "MetricExpr": "stalled\\-cycles\\-backend / cpu\\-cycles", + "MetricExpr": "(stalled\\-cycles\\-backend / cpu\\-cycles) if has_event(stalled\\-cycles\\-backend) else 0", "MetricGroup": "Default", "MetricName": "backend_cycles_idle", "MetricThreshold": "backend_cycles_idle > 0.2", diff --git a/tools/perf/pmu-events/arch/x86/alderlake/cache.json b/tools/perf/pmu-events/arch/x86/alderlake/cache.json index be15a7f83717..5d0d824f3e7e 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/cache.json @@ -876,105 +876,97 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 128 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_128", "MSRIndex": "0x3F6", "MSRValue": "0x80", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 128 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 16 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 16. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_16", "MSRIndex": "0x3F6", "MSRValue": "0x10", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 16 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 256 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_256", "MSRIndex": "0x3F6", "MSRValue": "0x100", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 256 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 32 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 32. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_32", "MSRIndex": "0x3F6", "MSRValue": "0x20", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 32 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 4 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 4. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_4", "MSRIndex": "0x3F6", "MSRValue": "0x4", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 4 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 512 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 512. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_512", "MSRIndex": "0x3F6", "MSRValue": "0x200", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 512 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 64 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 64. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_64", "MSRIndex": "0x3F6", "MSRValue": "0x40", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 64 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 8 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 8. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_8", "MSRIndex": "0x3F6", "MSRValue": "0x8", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 8 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" @@ -1030,12 +1022,11 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of stores uops retired. Counts with or without PEBS enabled.", + "BriefDescription": "Counts the number of stores uops retired.", "Counter": "0,1,2,3,4,5", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.STORE_LATENCY", - "PublicDescription": "Counts the number of stores uops retired. Counts with or without PEBS enabled. If PEBS is enabled and a PEBS record is generated, will populate PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x6", "Unit": "cpu_atom" diff --git a/tools/perf/pmu-events/arch/x86/alderlake/frontend.json b/tools/perf/pmu-events/arch/x86/alderlake/frontend.json index ff3b30c2619a..11fc853f2d0b 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/frontend.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/frontend.json @@ -327,6 +327,24 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "ICACHE_TAG.STALLS_INUSE", + "Counter": "0,1,2,3", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_INUSE", + "SampleAfterValue": "200003", + "UMask": "0x10", + "Unit": "cpu_core" + }, + { + "BriefDescription": "ICACHE_TAG.STALLS_ISB", + "Counter": "0,1,2,3", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_ISB", + "SampleAfterValue": "200003", + "UMask": "0x8", + "Unit": "cpu_core" + }, { "BriefDescription": "Cycles Decode Stream Buffer (DSB) is delivering any Uop", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/alderlake/pipeline.json b/tools/perf/pmu-events/arch/x86/alderlake/pipeline.json index 57a8c78cdc49..80cad3c49d20 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/pipeline.json @@ -244,6 +244,15 @@ "UMask": "0xfb", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of near indirect JMP branch instructions retired.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.INDIRECT_JMP", + "SampleAfterValue": "200003", + "UMask": "0xef", + "Unit": "cpu_atom" + }, { "BriefDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.INDIRECT_CALL", "Counter": "0,1,2,3,4,5", @@ -464,6 +473,15 @@ "UMask": "0x2", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of mispredicted near indirect JMP branch instructions retired.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xc5", + "EventName": "BR_MISP_RETIRED.INDIRECT_JMP", + "SampleAfterValue": "200003", + "UMask": "0xef", + "Unit": "cpu_atom" + }, { "BriefDescription": "This event is deprecated. Refer to new event BR_MISP_RETIRED.INDIRECT_CALL", "Counter": "0,1,2,3,4,5", @@ -573,7 +591,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Counts the number of unhalted core clock cycles. (Fixed event)", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.CORE", "PublicDescription": "Counts the number of core cycles while the core is not in a halt state. The core enters the halt state when it is running the HLT instruction. The core frequency may change from time to time. For this reason this event may have a changing ratio with regards to time. This event uses fixed counter 1.", @@ -582,7 +600,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of unhalted core clock cycles.", + "BriefDescription": "Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD_P]", "Counter": "0,1,2,3,4,5", "EventCode": "0x3c", "EventName": "CPU_CLK_UNHALTED.CORE_P", @@ -651,7 +669,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Counts the number of unhalted reference clock cycles at TSC frequency. (Fixed event)", + "BriefDescription": "Fixed Counter: Counts the number of unhalted reference clock cycles at TSC frequency.", "Counter": "Fixed counter 2", "EventName": "CPU_CLK_UNHALTED.REF_TSC", "PublicDescription": "Counts the number of reference cycles that the core is not in a halt state. The core enters the halt state when it is running the HLT instruction. This event is not affected by core frequency changes and increments at a fixed frequency that is also used for the Time Stamp Counter (TSC). This event uses fixed counter 2.", @@ -689,7 +707,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Counts the number of unhalted core clock cycles. (Fixed event)", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.CORE]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.THREAD", "PublicDescription": "Counts the number of core cycles while the core is not in a halt state. The core enters the halt state when it is running the HLT instruction. The core frequency may change from time to time. For this reason this event may have a changing ratio with regards to time. This event uses fixed counter 1.", @@ -707,7 +725,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Counts the number of unhalted core clock cycles.", + "BriefDescription": "Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.CORE_P]", "Counter": "0,1,2,3,4,5", "EventCode": "0x3c", "EventName": "CPU_CLK_UNHALTED.THREAD_P", @@ -875,7 +893,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Counts the total number of instructions retired. (Fixed event)", + "BriefDescription": "Fixed Counter: Counts the total number of instructions retired.", "Counter": "Fixed counter 0", "EventName": "INST_RETIRED.ANY", "PublicDescription": "Counts the total number of instructions that retired. For instructions that consist of multiple uops, this event counts the retirement of the last uop of the instruction. This event continues counting during hardware interrupts, traps, and inside interrupt handlers. This event uses fixed counter 0. Available PDIST counters: 32", @@ -1273,6 +1291,42 @@ "UMask": "0x20", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of CLFLUSH, CLWB, and CLDEMOTE instructions retired.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.CL_INST", + "SampleAfterValue": "1000003", + "UMask": "0xff", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of LFENCE instructions retired.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.LFENCE", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of accesses to KeyLocker cache.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xe1", + "EventName": "MISC_RETIRED2.KEYLOCKER_ACCESS", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of misses to KeyLocker cache.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xe1", + "EventName": "MISC_RETIRED2.KEYLOCKER_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x11", + "Unit": "cpu_atom" + }, { "BriefDescription": "Cycles stalled due to no store buffers available. (not including draining form sync).", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/alderlaken/cache.json b/tools/perf/pmu-events/arch/x86/alderlaken/cache.json index 76a841675337..1f97a4dc6fb1 100644 --- a/tools/perf/pmu-events/arch/x86/alderlaken/cache.json +++ b/tools/perf/pmu-events/arch/x86/alderlaken/cache.json @@ -246,98 +246,90 @@ "UMask": "0x82" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 128 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_128", "MSRIndex": "0x3F6", "MSRValue": "0x80", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 128 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 16 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 16. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_16", "MSRIndex": "0x3F6", "MSRValue": "0x10", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 16 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 256 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_256", "MSRIndex": "0x3F6", "MSRValue": "0x100", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 256 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 32 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 32. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_32", "MSRIndex": "0x3F6", "MSRValue": "0x20", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 32 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 4 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 4. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_4", "MSRIndex": "0x3F6", "MSRValue": "0x4", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 4 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 512 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 512. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_512", "MSRIndex": "0x3F6", "MSRValue": "0x200", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 512 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 64 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 64. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_64", "MSRIndex": "0x3F6", "MSRValue": "0x40", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 64 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 8 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 8. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_8", "MSRIndex": "0x3F6", "MSRValue": "0x8", - "PublicDescription": "Counts the number of tagged loads with an instruction latency that exceeds or equals the threshold of 8 cycles as defined in MEC_CR_PEBS_LD_LAT_THRESHOLD (3F6H). Only counts with PEBS enabled. If a PEBS record is generated, will populate the PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x5" }, @@ -387,12 +379,11 @@ "UMask": "0x12" }, { - "BriefDescription": "Counts the number of stores uops retired. Counts with or without PEBS enabled.", + "BriefDescription": "Counts the number of stores uops retired.", "Counter": "0,1,2,3,4,5", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.STORE_LATENCY", - "PublicDescription": "Counts the number of stores uops retired. Counts with or without PEBS enabled. If PEBS is enabled and a PEBS record is generated, will populate PEBS Latency and PEBS Data Source fields accordingly.", "SampleAfterValue": "1000003", "UMask": "0x6" }, diff --git a/tools/perf/pmu-events/arch/x86/alderlaken/pipeline.json b/tools/perf/pmu-events/arch/x86/alderlaken/pipeline.json index d650cbd48c1f..a13851071624 100644 --- a/tools/perf/pmu-events/arch/x86/alderlaken/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/alderlaken/pipeline.json @@ -108,6 +108,14 @@ "SampleAfterValue": "200003", "UMask": "0xfb" }, + { + "BriefDescription": "Counts the number of near indirect JMP branch instructions retired.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.INDIRECT_JMP", + "SampleAfterValue": "200003", + "UMask": "0xef" + }, { "BriefDescription": "This event is deprecated. Refer to new event BR_INST_RETIRED.INDIRECT_CALL", "Counter": "0,1,2,3,4,5", @@ -225,6 +233,14 @@ "SampleAfterValue": "200003", "UMask": "0xfb" }, + { + "BriefDescription": "Counts the number of mispredicted near indirect JMP branch instructions retired.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xc5", + "EventName": "BR_MISP_RETIRED.INDIRECT_JMP", + "SampleAfterValue": "200003", + "UMask": "0xef" + }, { "BriefDescription": "This event is deprecated. Refer to new event BR_MISP_RETIRED.INDIRECT_CALL", "Counter": "0,1,2,3,4,5", @@ -278,7 +294,7 @@ "UMask": "0xfe" }, { - "BriefDescription": "Counts the number of unhalted core clock cycles. (Fixed event)", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.CORE", "PublicDescription": "Counts the number of core cycles while the core is not in a halt state. The core enters the halt state when it is running the HLT instruction. The core frequency may change from time to time. For this reason this event may have a changing ratio with regards to time. This event uses fixed counter 1.", @@ -286,7 +302,7 @@ "UMask": "0x2" }, { - "BriefDescription": "Counts the number of unhalted core clock cycles.", + "BriefDescription": "Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD_P]", "Counter": "0,1,2,3,4,5", "EventCode": "0x3c", "EventName": "CPU_CLK_UNHALTED.CORE_P", @@ -303,7 +319,7 @@ "UMask": "0x1" }, { - "BriefDescription": "Counts the number of unhalted reference clock cycles at TSC frequency. (Fixed event)", + "BriefDescription": "Fixed Counter: Counts the number of unhalted reference clock cycles at TSC frequency.", "Counter": "Fixed counter 2", "EventName": "CPU_CLK_UNHALTED.REF_TSC", "PublicDescription": "Counts the number of reference cycles that the core is not in a halt state. The core enters the halt state when it is running the HLT instruction. This event is not affected by core frequency changes and increments at a fixed frequency that is also used for the Time Stamp Counter (TSC). This event uses fixed counter 2.", @@ -320,7 +336,7 @@ "UMask": "0x1" }, { - "BriefDescription": "Counts the number of unhalted core clock cycles. (Fixed event)", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.CORE]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.THREAD", "PublicDescription": "Counts the number of core cycles while the core is not in a halt state. The core enters the halt state when it is running the HLT instruction. The core frequency may change from time to time. For this reason this event may have a changing ratio with regards to time. This event uses fixed counter 1.", @@ -328,7 +344,7 @@ "UMask": "0x2" }, { - "BriefDescription": "Counts the number of unhalted core clock cycles.", + "BriefDescription": "Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.CORE_P]", "Counter": "0,1,2,3,4,5", "EventCode": "0x3c", "EventName": "CPU_CLK_UNHALTED.THREAD_P", @@ -336,7 +352,7 @@ "SampleAfterValue": "2000003" }, { - "BriefDescription": "Counts the total number of instructions retired. (Fixed event)", + "BriefDescription": "Fixed Counter: Counts the total number of instructions retired.", "Counter": "Fixed counter 0", "EventName": "INST_RETIRED.ANY", "PublicDescription": "Counts the total number of instructions that retired. For instructions that consist of multiple uops, this event counts the retirement of the last uop of the instruction. This event continues counting during hardware interrupts, traps, and inside interrupt handlers. This event uses fixed counter 0. Available PDIST counters: 32", @@ -426,6 +442,38 @@ "SampleAfterValue": "1000003", "UMask": "0x1" }, + { + "BriefDescription": "Counts the number of CLFLUSH, CLWB, and CLDEMOTE instructions retired.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.CL_INST", + "SampleAfterValue": "1000003", + "UMask": "0xff" + }, + { + "BriefDescription": "Counts the number of LFENCE instructions retired.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.LFENCE", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of accesses to KeyLocker cache.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xe1", + "EventName": "MISC_RETIRED2.KEYLOCKER_ACCESS", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of misses to KeyLocker cache.", + "Counter": "0,1,2,3,4,5", + "EventCode": "0xe1", + "EventName": "MISC_RETIRED2.KEYLOCKER_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x11" + }, { "BriefDescription": "Counts the number of issue slots in a UMWAIT or TPAUSE instruction where no uop issues due to the instruction putting the CPU into the C0.1 activity state. For Tremont, UMWAIT and TPAUSE will only put the CPU into C0.1 activity state (not C0.2 activity state)", "Counter": "0,1,2,3,4,5", diff --git a/tools/perf/pmu-events/arch/x86/arrowlake/cache.json b/tools/perf/pmu-events/arch/x86/arrowlake/cache.json index fba4a0672f6c..4c3aa1fab5a8 100644 --- a/tools/perf/pmu-events/arch/x86/arrowlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/arrowlake/cache.json @@ -628,6 +628,15 @@ "UMask": "0x7f", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an instruction cache or TLB miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x35", + "EventName": "MEM_BOUND_STALLS_IFETCH.ALL", + "SampleAfterValue": "1000003", + "UMask": "0x7f", + "Unit": "cpu_lowpower" + }, { "BriefDescription": "Counts the number of cycles the core is stalled due to an instruction cache or TLB miss which hit in the L2 cache.", "Counter": "0,1,2,3,4,5,6,7", @@ -731,6 +740,24 @@ "UMask": "0x6", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of unhalted cycles that the core is stalled due to a demand load miss which hit in the LLC, no snoop was required, and the LLC provided data", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_HIT_NOSNOOP", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which hit in the LLC, a snoop was required, the snoop misses or the snoop hits but no fwd. LLC provides the data", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_HIT_SNOOP", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which missed all the local caches.", "Counter": "0,1,2,3,4,5,6,7", @@ -749,6 +776,24 @@ "UMask": "0x78", "Unit": "cpu_lowpower" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which missed all the caches. DRAM, MMIO or other LOCAL memory type provides the data", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_MISS_LOCALMEM", + "SampleAfterValue": "1000003", + "UMask": "0x50", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled to a demand load miss and the data was provided from an unknown source. If the core has access to an L3 cache, an LLC miss refers to an L3 cache miss, otherwise it is an L2 cache miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_MISS_LOCALMEM", + "SampleAfterValue": "1000003", + "UMask": "0x50", + "Unit": "cpu_lowpower" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled to a store buffer full condition", "Counter": "0,1,2,3,4,5,6,7", @@ -1081,6 +1126,15 @@ "UMask": "0x20", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of retired load ops with an unknown source", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd4", + "EventName": "MEM_LOAD_UOPS_MISC_RETIRED.LOCAL_DRAM", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of load ops retired that miss the L3 cache and hit in DRAM", "Counter": "0,1,2,3,4,5,6,7", @@ -1181,6 +1235,15 @@ "UMask": "0x1c", "Unit": "cpu_lowpower" }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which no snoop was required", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd1", + "EventName": "MEM_LOAD_UOPS_RETIRED.L3_HIT_NO_SNOOP", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of loads that hit in a write combining buffer (WCB), excluding the first load that caused the WCB to allocate.", "Counter": "0,1,2,3,4,5,6,7", @@ -1331,7 +1394,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1343,7 +1406,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1355,7 +1418,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1367,7 +1430,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 16.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1379,7 +1442,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 16. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1391,7 +1454,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 2048. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1403,7 +1466,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1415,7 +1478,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1427,7 +1490,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 32.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1439,7 +1502,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 32. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1451,7 +1514,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 4.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1463,7 +1526,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 4. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1475,7 +1538,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 512.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1487,7 +1550,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 512. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1499,7 +1562,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 64.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1511,7 +1574,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 64. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1523,7 +1586,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 8.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1535,7 +1598,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 8. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1707,7 +1770,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES", + "BriefDescription": "Counts the number of stores uops retired.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", @@ -1717,7 +1780,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES", + "BriefDescription": "Counts the number of stores uops retired.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", diff --git a/tools/perf/pmu-events/arch/x86/arrowlake/frontend.json b/tools/perf/pmu-events/arch/x86/arrowlake/frontend.json index a15de050a76c..21f00eafa98a 100644 --- a/tools/perf/pmu-events/arch/x86/arrowlake/frontend.json +++ b/tools/perf/pmu-events/arch/x86/arrowlake/frontend.json @@ -627,6 +627,24 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "Cycles where a code fetch is stalled due to L1 instruction cache In use-full", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_INUSE", + "SampleAfterValue": "200003", + "UMask": "0x10", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Cycles where a code fetch is stalled due to L1 instruction cache ISB-full", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_ISB", + "SampleAfterValue": "200003", + "UMask": "0x8", + "Unit": "cpu_core" + }, { "BriefDescription": "Cycles Decode Stream Buffer (DSB) is delivering any Uop", "Counter": "0,1,2,3,4,5,6,7,8,9", diff --git a/tools/perf/pmu-events/arch/x86/arrowlake/pipeline.json b/tools/perf/pmu-events/arch/x86/arrowlake/pipeline.json index 805616052925..fb973c75be57 100644 --- a/tools/perf/pmu-events/arch/x86/arrowlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/arrowlake/pipeline.json @@ -822,7 +822,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles.", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.CORE", "SampleAfterValue": "2000003", @@ -839,7 +839,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.CORE", "SampleAfterValue": "2000003", @@ -909,7 +909,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted reference clock cycles", + "BriefDescription": "Fixed Counter: Counts the number of unhalted reference clock cycles.", "Counter": "Fixed counter 2", "EventName": "CPU_CLK_UNHALTED.REF_TSC", "SampleAfterValue": "2000003", @@ -947,7 +947,7 @@ "Unit": "cpu_lowpower" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles.", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.CORE]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.THREAD", "SampleAfterValue": "2000003", @@ -964,7 +964,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.CORE]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.THREAD", "SampleAfterValue": "2000003", @@ -1134,10 +1134,10 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of instructions retired", + "BriefDescription": "Fixed Counter: Counts the number of instructions retired.", "Counter": "Fixed counter 0", "EventName": "INST_RETIRED.ANY", - "PublicDescription": "Fixed Counter: Counts the number of instructions retired Available PDIST counters: 32", + "PublicDescription": "Fixed Counter: Counts the number of instructions retired. Available PDIST counters: 32", "SampleAfterValue": "2000003", "UMask": "0x1", "Unit": "cpu_lowpower" @@ -1607,6 +1607,14 @@ "SampleAfterValue": "20003", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the total number of machine clears for any reason including, but not limited to, memory ordering, memory disambiguation, SMC, and FP assist.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.ANY", + "SampleAfterValue": "20003", + "Unit": "cpu_lowpower" + }, { "BriefDescription": "Counts the number of machine clears that flush the pipeline and restart the machine without the use of microcode.", "Counter": "0,1,2,3,4,5,6,7", @@ -1813,6 +1821,15 @@ "UMask": "0xff", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of CLFLUSH, CLWB, and CLDEMOTE instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.CL_INST", + "SampleAfterValue": "1000003", + "UMask": "0xff", + "Unit": "cpu_lowpower" + }, { "BriefDescription": "Counts the number of LFENCE instructions retired.", "Counter": "0,1,2,3,4,5,6,7", @@ -1822,6 +1839,15 @@ "UMask": "0x2", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of LFENCE instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.LFENCE", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_lowpower" + }, { "BriefDescription": "Counts the number of RDPMC, RDTSC, and RDTSCP instructions retired.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/emeraldrapids/cache.json b/tools/perf/pmu-events/arch/x86/emeraldrapids/cache.json index 26568e4b77f7..b2f8947f6741 100644 --- a/tools/perf/pmu-events/arch/x86/emeraldrapids/cache.json +++ b/tools/perf/pmu-events/arch/x86/emeraldrapids/cache.json @@ -514,7 +514,7 @@ "EventCode": "0xd3", "EventName": "MEM_LOAD_L3_MISS_RETIRED.REMOTE_DRAM", "PublicDescription": "MEM_LOAD_L3_MISS_RETIRED.REMOTE_DRAM Available PDIST counters: 0", - "SampleAfterValue": "1000003", + "SampleAfterValue": "100007", "UMask": "0x2" }, { @@ -534,7 +534,7 @@ "EventCode": "0xd3", "EventName": "MEM_LOAD_L3_MISS_RETIRED.REMOTE_HITM", "PublicDescription": "MEM_LOAD_L3_MISS_RETIRED.REMOTE_HITM Available PDIST counters: 0", - "SampleAfterValue": "1000003", + "SampleAfterValue": "100007", "UMask": "0x4" }, { diff --git a/tools/perf/pmu-events/arch/x86/emeraldrapids/frontend.json b/tools/perf/pmu-events/arch/x86/emeraldrapids/frontend.json index 793c486ffabe..e51f5e85ffd1 100644 --- a/tools/perf/pmu-events/arch/x86/emeraldrapids/frontend.json +++ b/tools/perf/pmu-events/arch/x86/emeraldrapids/frontend.json @@ -271,6 +271,22 @@ "SampleAfterValue": "200003", "UMask": "0x4" }, + { + "BriefDescription": "ICACHE_TAG.STALLS_INUSE", + "Counter": "0,1,2,3", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_INUSE", + "SampleAfterValue": "200003", + "UMask": "0x10" + }, + { + "BriefDescription": "ICACHE_TAG.STALLS_ISB", + "Counter": "0,1,2,3", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_ISB", + "SampleAfterValue": "200003", + "UMask": "0x8" + }, { "BriefDescription": "Cycles Decode Stream Buffer (DSB) is delivering any Uop", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-cache.json b/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-cache.json index 92cf47967f0b..3c8dcd9cff7c 100644 --- a/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-cache.json +++ b/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-cache.json @@ -3501,7 +3501,7 @@ "EventName": "UNC_CHA_SNOOP_RESP.RSPIFWD", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "Counts when a a transaction with the opcode type RspIFwd Snoop Response was received which indicates a remote caching agent forwarded the data and the requesting agent is able to acquire the data in E (Exclusive) or M (modified) states. This is commonly returned with RFO (the Read for Ownership issued before a write) transactions. The snoop could have either been to a cacheline in the M,E,F (Modified, Exclusive or Forward) states.", + "PublicDescription": "Counts when a transaction with the opcode type RspIFwd Snoop Response was received which indicates a remote caching agent forwarded the data and the requesting agent is able to acquire the data in E (Exclusive) or M (modified) states. This is commonly returned with RFO (the Read for Ownership issued before a write) transactions. The snoop could have either been to a cacheline in the M,E,F (Modified, Exclusive or Forward) states.", "UMask": "0x4", "Unit": "CHA" }, @@ -3523,7 +3523,7 @@ "EventName": "UNC_CHA_SNOOP_RESP.RSPSFWD", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "Counts when a a transaction with the opcode type RspSFwd Snoop Response was received which indicates a remote caching agent forwarded the data but held on to its current copy. This is common for data and code reads that hit in a remote socket in E (Exclusive) or F (Forward) state.", + "PublicDescription": "Counts when a transaction with the opcode type RspSFwd Snoop Response was received which indicates a remote caching agent forwarded the data but held on to its current copy. This is common for data and code reads that hit in a remote socket in E (Exclusive) or F (Forward) state.", "UMask": "0x8", "Unit": "CHA" }, diff --git a/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-io.json b/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-io.json index d4cf2199d46b..ddb0f65307f4 100644 --- a/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-io.json +++ b/tools/perf/pmu-events/arch/x86/emeraldrapids/uncore-io.json @@ -223,6 +223,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", + "PortMask": "0xff", "UMask": "0xff", "Unit": "IIO" }, @@ -234,7 +235,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x01", "PublicDescription": "x16 card plugged in to stack, Or x8 card plugged in to Lane 0/1, Or x4 card is plugged in to slot 0", "UMask": "0x1", "Unit": "IIO" @@ -247,7 +248,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x02", "PublicDescription": "x4 card is plugged in to slot 1", "UMask": "0x2", "Unit": "IIO" @@ -260,7 +261,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x04", "PublicDescription": "x8 card plugged in to Lane 2/3, Or x4 card is plugged in to slot 1", "UMask": "0x4", "Unit": "IIO" @@ -273,7 +274,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x08", "PublicDescription": "x4 card is plugged in to slot 3", "UMask": "0x8", "Unit": "IIO" @@ -286,7 +287,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x10", "PublicDescription": "x16 card plugged in to stack, Or x8 card plugged in to Lane 0/1, Or x4 card is plugged in to slot 0", "UMask": "0x10", "Unit": "IIO" @@ -299,7 +300,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x20", "PublicDescription": "x4 card is plugged in to slot 1", "UMask": "0x20", "Unit": "IIO" @@ -312,7 +313,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x40", "PublicDescription": "x8 card plugged in to Lane 2/3, Or x4 card is plugged in to slot 1", "UMask": "0x40", "Unit": "IIO" @@ -325,7 +326,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x80", "PublicDescription": "x4 card is plugged in to slot 3", "UMask": "0x80", "Unit": "IIO" diff --git a/tools/perf/pmu-events/arch/x86/grandridge/cache.json b/tools/perf/pmu-events/arch/x86/grandridge/cache.json index 9abddb06a837..0aa921ba89b4 100644 --- a/tools/perf/pmu-events/arch/x86/grandridge/cache.json +++ b/tools/perf/pmu-events/arch/x86/grandridge/cache.json @@ -285,8 +285,8 @@ "UMask": "0x82" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1,2,3,4,5,6,7", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024. Only counts with PEBS enabled.", + "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_1024", @@ -296,8 +296,8 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1,2,3,4,5,6,7", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128. Only counts with PEBS enabled.", + "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_128", @@ -307,8 +307,8 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1,2,3,4,5,6,7", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 16. Only counts with PEBS enabled.", + "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_16", @@ -318,8 +318,8 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1,2,3,4,5,6,7", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 2048. Only counts with PEBS enabled.", + "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_2048", @@ -329,8 +329,8 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1,2,3,4,5,6,7", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256. Only counts with PEBS enabled.", + "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_256", @@ -340,8 +340,8 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1,2,3,4,5,6,7", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 32. Only counts with PEBS enabled.", + "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_32", @@ -351,8 +351,8 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1,2,3,4,5,6,7", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 4. Only counts with PEBS enabled.", + "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_4", @@ -362,8 +362,8 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1,2,3,4,5,6,7", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 512. Only counts with PEBS enabled.", + "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_512", @@ -373,8 +373,8 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1,2,3,4,5,6,7", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 64. Only counts with PEBS enabled.", + "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_64", @@ -384,8 +384,8 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", - "Counter": "0,1,2,3,4,5,6,7", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 8. Only counts with PEBS enabled.", + "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_8", @@ -458,7 +458,7 @@ "UMask": "0x12" }, { - "BriefDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES", + "BriefDescription": "Counts the number of stores uops retired.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", diff --git a/tools/perf/pmu-events/arch/x86/grandridge/pipeline.json b/tools/perf/pmu-events/arch/x86/grandridge/pipeline.json index f56d8d816e53..20986b987e18 100644 --- a/tools/perf/pmu-events/arch/x86/grandridge/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/grandridge/pipeline.json @@ -178,7 +178,7 @@ "UMask": "0xf7" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.CORE", "SampleAfterValue": "2000003", @@ -192,7 +192,7 @@ "SampleAfterValue": "2000003" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted reference clock cycles", + "BriefDescription": "Fixed Counter: Counts the number of unhalted reference clock cycles.", "Counter": "Fixed counter 2", "EventName": "CPU_CLK_UNHALTED.REF_TSC", "SampleAfterValue": "2000003", @@ -208,7 +208,7 @@ "UMask": "0x1" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.CORE]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.THREAD", "SampleAfterValue": "2000003", @@ -222,10 +222,10 @@ "SampleAfterValue": "2000003" }, { - "BriefDescription": "Fixed Counter: Counts the number of instructions retired", + "BriefDescription": "Fixed Counter: Counts the number of instructions retired.", "Counter": "Fixed counter 0", "EventName": "INST_RETIRED.ANY", - "PublicDescription": "Fixed Counter: Counts the number of instructions retired Available PDIST counters: 32", + "PublicDescription": "Fixed Counter: Counts the number of instructions retired. Available PDIST counters: 32", "SampleAfterValue": "2000003", "UMask": "0x1" }, @@ -301,6 +301,38 @@ "SampleAfterValue": "1000003", "UMask": "0x1" }, + { + "BriefDescription": "Counts the number of CLFLUSH, CLWB, and CLDEMOTE instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.CL_INST", + "SampleAfterValue": "1000003", + "UMask": "0xff" + }, + { + "BriefDescription": "Counts the number of LFENCE instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.LFENCE", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of accesses to KeyLocker cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe1", + "EventName": "MISC_RETIRED2.KEYLOCKER_ACCESS", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of misses to KeyLocker cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe1", + "EventName": "MISC_RETIRED2.KEYLOCKER_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x11" + }, { "BriefDescription": "Counts the number of issue slots in a UMWAIT or TPAUSE instruction where no uop issues due to the instruction putting the CPU into the C0.1 activity state.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/graniterapids/frontend.json b/tools/perf/pmu-events/arch/x86/graniterapids/frontend.json index d580d305c926..1fdeaebb739f 100644 --- a/tools/perf/pmu-events/arch/x86/graniterapids/frontend.json +++ b/tools/perf/pmu-events/arch/x86/graniterapids/frontend.json @@ -325,6 +325,22 @@ "SampleAfterValue": "200003", "UMask": "0x4" }, + { + "BriefDescription": "ICACHE_TAG.STALLS_INUSE", + "Counter": "0,1,2,3", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_INUSE", + "SampleAfterValue": "200003", + "UMask": "0x10" + }, + { + "BriefDescription": "ICACHE_TAG.STALLS_ISB", + "Counter": "0,1,2,3", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_ISB", + "SampleAfterValue": "200003", + "UMask": "0x8" + }, { "BriefDescription": "Cycles Decode Stream Buffer (DSB) is delivering any Uop", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/graniterapids/gnr-metrics.json b/tools/perf/pmu-events/arch/x86/graniterapids/gnr-metrics.json index cc3c834ca286..299631fb8d53 100644 --- a/tools/perf/pmu-events/arch/x86/graniterapids/gnr-metrics.json +++ b/tools/perf/pmu-events/arch/x86/graniterapids/gnr-metrics.json @@ -143,6 +143,12 @@ "MetricName": "io_full_write_l3_miss", "ScaleUnit": "100%" }, + { + "BriefDescription": "The number of times per second that ownership of a cacheline was stolen from the integrated IO controller before it was able to write back the modified line", + "MetricExpr": "(UNC_I_MISC1.LOST_FWD + UNC_I_MISC1.SEC_RCVD_INVLD) / duration_time", + "MetricName": "io_lost_fwd", + "ScaleUnit": "1per_sec" + }, { "BriefDescription": "Message Signaled Interrupts (MSI) per second sent by the integrated I/O traffic controller (IIO) to System Configuration Controller (Ubox)", "MetricExpr": "UNC_IIO_NUM_REQ_OF_CPU_BY_TGT.UBOX_POSTED / duration_time", @@ -294,6 +300,27 @@ "MetricName": "memory_bandwidth_write", "ScaleUnit": "1MB/s" }, + { + "BriefDescription": "All reads to the local sub-numa cluster cache as a percentage of total memory read accesses", + "MetricExpr": "(L2_LINES_IN.ALL - (OCR.READS_TO_CORE.SNC_CACHE.HITM + OCR.READS_TO_CORE.SNC_CACHE.HIT_WITH_FWD + OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_FWD + OCR.READS_TO_CORE.REMOTE_MEMORY + OCR.READS_TO_CORE.L3_MISS_LOCAL)) / L2_LINES_IN.ALL", + "MetricName": "numa_percent_all_reads_to_local_cluster_cache", + "PublicDescription": "All reads to the local sub-numa cluster cache as a percentage of total memory read accesses. Includes demand and prefetch requests for data reads, code reads, read for ownerships (RFO), does not include LLC prefetches", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "All reads to the local sub-numa cluster memory as a percentage of total memory read accesses", + "MetricExpr": "OCR.READS_TO_CORE.L3_MISS_LOCAL / L2_LINES_IN.ALL", + "MetricName": "numa_percent_all_reads_to_local_cluster_memory", + "PublicDescription": "All reads to the local sub-numa cluster memory as a percentage of total memory read accesses. Includes demand and prefetch requests for data reads, code reads, read for ownerships (RFO), does not include LLC prefetches", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "All reads to a remote sub-numa cluster cache as a percentage of total memory read accesses", + "MetricExpr": "(OCR.READS_TO_CORE.SNC_CACHE.HIT_WITH_FWD + OCR.READS_TO_CORE.SNC_CACHE.HITM) / L2_LINES_IN.ALL", + "MetricName": "numa_percent_all_reads_to_remote_cluster_cache", + "PublicDescription": "All reads to a remote sub-numa cluster cache as a percentage of total memory read accesses. Includes demand and prefetch requests for data reads, code reads, read for ownerships (RFO), does not include LLC prefetches", + "ScaleUnit": "100%" + }, { "BriefDescription": "Memory read that miss the last level cache (LLC) addressed to local DRAM as a percentage of total memory read accesses, does not include LLC prefetches", "MetricExpr": "(UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL) / (UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE + UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE)", diff --git a/tools/perf/pmu-events/arch/x86/lunarlake/cache.json b/tools/perf/pmu-events/arch/x86/lunarlake/cache.json index 3d2616be8ec1..2db3e8a51fbd 100644 --- a/tools/perf/pmu-events/arch/x86/lunarlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/lunarlake/cache.json @@ -550,6 +550,24 @@ "UMask": "0x7e", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an icache or itlb miss which missed all the caches.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x35", + "EventName": "MEM_BOUND_STALLS_IFETCH.LLC_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x78", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an icache or itlb miss which missed all the caches. Local DRAM, MMIO or other local memory type provides the data.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x35", + "EventName": "MEM_BOUND_STALLS_IFETCH.LLC_MISS_LOCALMEM", + "SampleAfterValue": "1000003", + "UMask": "0x50", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an L1 demand load miss.", "Counter": "0,1,2,3,4,5,6,7", @@ -1088,7 +1106,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1100,7 +1118,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 16.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1112,7 +1130,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1124,7 +1142,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 32.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1136,7 +1154,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 4.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1148,7 +1166,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 512.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1160,7 +1178,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 64.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1172,7 +1190,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 8.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1274,7 +1292,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES", + "BriefDescription": "Counts the number of stores uops retired.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", diff --git a/tools/perf/pmu-events/arch/x86/lunarlake/frontend.json b/tools/perf/pmu-events/arch/x86/lunarlake/frontend.json index b21d602e9f1a..798eebf77436 100644 --- a/tools/perf/pmu-events/arch/x86/lunarlake/frontend.json +++ b/tools/perf/pmu-events/arch/x86/lunarlake/frontend.json @@ -424,6 +424,15 @@ "UMask": "0x1", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to Instruction L1 cache miss, that missed in the L2 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc9", + "EventName": "FRONTEND_RETIRED_SOURCE.ICACHE_L2_MISS", + "SampleAfterValue": "1000003", + "UMask": "0xe", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss that hit in the second level TLB.", "Counter": "0,1,2,3,4,5,6,7", @@ -500,6 +509,24 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "Cycles where a code fetch is stalled due to L1 instruction cache In use-full", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_INUSE", + "SampleAfterValue": "200003", + "UMask": "0x10", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Cycles where a code fetch is stalled due to L1 instruction cache ISB-full", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_ISB", + "SampleAfterValue": "200003", + "UMask": "0x8", + "Unit": "cpu_core" + }, { "BriefDescription": "Cycles Decode Stream Buffer (DSB) is delivering any Uop", "Counter": "0,1,2,3,4,5,6,7,8,9", diff --git a/tools/perf/pmu-events/arch/x86/lunarlake/pipeline.json b/tools/perf/pmu-events/arch/x86/lunarlake/pipeline.json index 97797f7b072e..d98723b3cd78 100644 --- a/tools/perf/pmu-events/arch/x86/lunarlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/lunarlake/pipeline.json @@ -634,7 +634,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles.", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.CORE", "SampleAfterValue": "2000003", @@ -725,7 +725,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles.", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.CORE]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.THREAD", "SampleAfterValue": "2000003", @@ -1530,8 +1530,9 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of accesses to KeyLocker cache.", + "BriefDescription": "This event is deprecated.", "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", "EventCode": "0xe1", "EventName": "MISC_RETIRED2.KEYLOCKER_ACCESS", "SampleAfterValue": "1000003", @@ -1539,8 +1540,9 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of misses to KeyLocker cache.", + "BriefDescription": "This event is deprecated.", "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", "EventCode": "0xe1", "EventName": "MISC_RETIRED2.KEYLOCKER_MISS", "SampleAfterValue": "1000003", diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index 149bbe7abaf5..8a9e1735e21e 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -1,7 +1,7 @@ Family-model,Version,Filename,EventType -GenuineIntel-6-(97|9A|B7|BA|BF),v1.35,alderlake,core -GenuineIntel-6-BE,v1.35,alderlaken,core -GenuineIntel-6-C[56],v1.14,arrowlake,core +GenuineIntel-6-(97|9A|B7|BA|BF),v1.37,alderlake,core +GenuineIntel-6-BE,v1.37,alderlaken,core +GenuineIntel-6-C[56],v1.16,arrowlake,core GenuineIntel-6-(1C|26|27|35|36),v5,bonnell,core GenuineIntel-6-(3D|47),v30,broadwell,core GenuineIntel-6-56,v12,broadwellde,core @@ -9,11 +9,11 @@ GenuineIntel-6-4F,v23,broadwellx,core GenuineIntel-6-55-[56789ABCDEF],v1.25,cascadelakex,core GenuineIntel-6-DD,v1.00,clearwaterforest,core GenuineIntel-6-9[6C],v1.05,elkhartlake,core -GenuineIntel-6-CF,v1.20,emeraldrapids,core +GenuineIntel-6-CF,v1.21,emeraldrapids,core GenuineIntel-6-5[CF],v13,goldmont,core GenuineIntel-6-7A,v1.01,goldmontplus,core -GenuineIntel-6-B6,v1.10,grandridge,core -GenuineIntel-6-A[DE],v1.16,graniterapids,core +GenuineIntel-6-B6,v1.11,grandridge,core +GenuineIntel-6-A[DE],v1.17,graniterapids,core GenuineIntel-6-(3C|45|46),v36,haswell,core GenuineIntel-6-3F,v29,haswellx,core GenuineIntel-6-7[DE],v1.24,icelake,core @@ -22,15 +22,15 @@ GenuineIntel-6-3A,v24,ivybridge,core GenuineIntel-6-3E,v24,ivytown,core GenuineIntel-6-2D,v24,jaketown,core GenuineIntel-6-(57|85),v16,knightslanding,core -GenuineIntel-6-BD,v1.19,lunarlake,core -GenuineIntel-6-(AA|AC|B5),v1.18,meteorlake,core +GenuineIntel-6-BD,v1.21,lunarlake,core +GenuineIntel-6-(AA|AC|B5),v1.20,meteorlake,core GenuineIntel-6-1[AEF],v4,nehalemep,core GenuineIntel-6-2E,v4,nehalemex,core -GenuineIntel-6-CC,v1.02,pantherlake,core +GenuineIntel-6-CC,v1.04,pantherlake,core GenuineIntel-6-A7,v1.04,rocketlake,core GenuineIntel-6-2A,v19,sandybridge,core -GenuineIntel-6-8F,v1.35,sapphirerapids,core -GenuineIntel-6-AF,v1.13,sierraforest,core +GenuineIntel-6-8F,v1.36,sapphirerapids,core +GenuineIntel-6-AF,v1.15,sierraforest,core GenuineIntel-6-(37|4A|4C|4D|5A),v15,silvermont,core GenuineIntel-6-(4E|5E|8E|9E|A5|A6),v59,skylake,core GenuineIntel-6-55-[01234],v1.37,skylakex,core diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/cache.json b/tools/perf/pmu-events/arch/x86/meteorlake/cache.json index d3fc04b2ffbd..4c1220c19456 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/cache.json @@ -513,6 +513,15 @@ "UMask": "0x6", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an ICACHE or ITLB miss which hit in the LLC, no snoop was required. LLC provides the data. If the core has access to an L3 cache, an LLC hit refers to an L3 cache hit, otherwise it counts zeros.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x35", + "EventName": "MEM_BOUND_STALLS_IFETCH.LLC_HIT_NOSNOOP", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an ICACHE or ITLB miss which missed all the caches. If the core has access to an L3 cache, an LLC miss refers to an L3 cache miss, otherwise it is an L2 cache miss.", "Counter": "0,1,2,3,4,5,6,7", @@ -522,6 +531,15 @@ "UMask": "0x78", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an ICACHE or ITLB miss which missed all the caches. DRAM, MMIO or other LOCAL memory type provides the data. If the core has access to an L3 cache, an LLC miss refers to an L3 cache miss, otherwise it is an L2 cache miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x35", + "EventName": "MEM_BOUND_STALLS_IFETCH.LLC_MISS_LOCALMEM", + "SampleAfterValue": "1000003", + "UMask": "0x50", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to an L1 demand load miss.", "Counter": "0,1,2,3,4,5,6,7", @@ -559,6 +577,24 @@ "UMask": "0x6", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which hit in the LLC, no snoop was required. LLC provides the data. If the core has access to an L3 cache, an LLC hit refers to an L3 cache hit, otherwise it counts zeros.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_HIT_NOSNOOP", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which hit in the LLC, a snoop was required, the snoop misses or the snoop hits but NO_FWD. LLC provides the data. If the core has access to an L3 cache, an LLC hit refers to an L3 cache hit, otherwise it counts zeros.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_HIT_SNOOP", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which missed all the local caches. If the core has access to an L3 cache, an LLC miss refers to an L3 cache miss, otherwise it is an L2 cache miss.", "Counter": "0,1,2,3,4,5,6,7", @@ -568,6 +604,15 @@ "UMask": "0x78", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled to a demand load miss and the data was provided from an unknown source. If the core has access to an L3 cache, an LLC miss refers to an L3 cache miss, otherwise it is an L2 cache miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_MISS_LOCALMEM", + "SampleAfterValue": "1000003", + "UMask": "0x50", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled to a store buffer full condition", "Counter": "0,1,2,3,4,5,6,7", @@ -969,7 +1014,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -981,7 +1026,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -993,7 +1038,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 16. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1005,7 +1050,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 2048. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1017,7 +1062,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1029,7 +1074,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 32. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1041,7 +1086,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 4. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1053,7 +1098,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 512. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1065,7 +1110,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 64. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1077,7 +1122,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 8. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -1159,7 +1204,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES", + "BriefDescription": "Counts the number of stores uops retired.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/frontend.json b/tools/perf/pmu-events/arch/x86/meteorlake/frontend.json index 6484834b1127..dcf8c8e720f3 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/frontend.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/frontend.json @@ -430,6 +430,24 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "ICACHE_TAG.STALLS_INUSE", + "Counter": "0,1,2,3", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_INUSE", + "SampleAfterValue": "200003", + "UMask": "0x10", + "Unit": "cpu_core" + }, + { + "BriefDescription": "ICACHE_TAG.STALLS_ISB", + "Counter": "0,1,2,3", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_ISB", + "SampleAfterValue": "200003", + "UMask": "0x8", + "Unit": "cpu_core" + }, { "BriefDescription": "Cycles Decode Stream Buffer (DSB) is delivering any Uop", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json b/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json index bfdaabe9377d..7662846745bd 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json @@ -517,7 +517,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.CORE", "SampleAfterValue": "2000003", @@ -583,7 +583,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted reference clock cycles", + "BriefDescription": "Fixed Counter: Counts the number of unhalted reference clock cycles.", "Counter": "Fixed counter 2", "EventName": "CPU_CLK_UNHALTED.REF_TSC", "SampleAfterValue": "2000003", @@ -620,7 +620,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.CORE]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.THREAD", "SampleAfterValue": "2000003", @@ -804,10 +804,10 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of instructions retired", + "BriefDescription": "Fixed Counter: Counts the number of instructions retired.", "Counter": "Fixed counter 0", "EventName": "INST_RETIRED.ANY", - "PublicDescription": "Fixed Counter: Counts the number of instructions retired Available PDIST counters: 32", + "PublicDescription": "Fixed Counter: Counts the number of instructions retired. Available PDIST counters: 32", "SampleAfterValue": "2000003", "UMask": "0x1", "Unit": "cpu_atom" @@ -1207,6 +1207,42 @@ "UMask": "0x20", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of CLFLUSH, CLWB, and CLDEMOTE instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.CL_INST", + "SampleAfterValue": "1000003", + "UMask": "0xff", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of LFENCE instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.LFENCE", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of accesses to KeyLocker cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe1", + "EventName": "MISC_RETIRED2.KEYLOCKER_ACCESS", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of misses to KeyLocker cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe1", + "EventName": "MISC_RETIRED2.KEYLOCKER_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x11", + "Unit": "cpu_atom" + }, { "BriefDescription": "Cycles stalled due to no store buffers available. (not including draining form sync).", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/cache.json b/tools/perf/pmu-events/arch/x86/pantherlake/cache.json index 91f5ab908926..e5323093eec0 100644 --- a/tools/perf/pmu-events/arch/x86/pantherlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/pantherlake/cache.json @@ -149,6 +149,60 @@ "UMask": "0xff", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Code Read requests. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_CODE_RD", + "SampleAfterValue": "1000003", + "UMask": "0xc4", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Code Read requests that resulted in a Miss. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_CODE_RD_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x44", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Data Read requests. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_DATA_RD", + "SampleAfterValue": "1000003", + "UMask": "0xc1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand Data Read requests that resulted in a Miss. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_DATA_RD_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x41", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand RFO requests. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_RFO", + "SampleAfterValue": "1000003", + "UMask": "0xc2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Demand RFO requests that resulted in a Miss. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.DEMAND_RFO_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x42", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of L2 cache accesses from front door requests that resulted in a Hit. Does not include rejects or recycles, per core event.", "Counter": "0,1,2,3,4,5,6,7", @@ -158,6 +212,24 @@ "UMask": "0x1bf", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door Hardware Prefetch requests. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.HWPF", + "SampleAfterValue": "1000003", + "UMask": "0xc8", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of L2 cache accesses from front door requests that resulted in a Miss. Does not include rejects or recycles, per core event.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x24", + "EventName": "L2_REQUEST.MISS", + "SampleAfterValue": "1000003", + "UMask": "0x17f", + "Unit": "cpu_atom" + }, { "BriefDescription": "Read requests with true-miss in L2 cache [This event is alias to L2_RQSTS.MISS]", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -365,6 +437,24 @@ "UMask": "0x6", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which hit in the LLC, no snoop was required. LLC provided data.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_HIT_NOSNOOP", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which hit in the LLC, a snoop was required, the snoop misses or the snoop hits but no fwd. LLC provides the data.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x34", + "EventName": "MEM_BOUND_STALLS_LOAD.LLC_HIT_SNOOP", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of unhalted cycles when the core is stalled due to a demand load miss which missed all the local caches.", "Counter": "0,1,2,3,4,5,6,7", @@ -716,6 +806,16 @@ "UMask": "0x20", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the total number of load ops retired that miss the L3 cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd3", + "EventName": "MEM_LOAD_UOPS_L3_MISS_RETIRED.ALL", + "PublicDescription": "Counts the total number of load ops retired that miss the L3 cache. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0xff", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of load ops retired that miss the L3 cache and hit in DRAM", "Counter": "0,1,2,3,4,5,6,7", @@ -746,6 +846,26 @@ "UMask": "0x8", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and no data was forwarded.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd4", + "EventName": "MEM_LOAD_UOPS_MISC_RETIRED.L3_HIT_SNOOP_NO_FWD", + "PublicDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and no data was forwarded. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x20", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and non-modified data was forwarded.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd4", + "EventName": "MEM_LOAD_UOPS_MISC_RETIRED.L3_HIT_SNOOP_WITH_FWD", + "PublicDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and non-modified data was forwarded. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of load ops retired that hit the L1 data cache.", "Counter": "0,1,2,3,4,5,6,7", @@ -796,6 +916,26 @@ "UMask": "0x1c", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which no snoop was required.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd1", + "EventName": "MEM_LOAD_UOPS_RETIRED.L3_HIT_NO_SNOOP", + "PublicDescription": "Counts the number of load ops retired that hit in the L3 cache in which no snoop was required. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and it hit and forwarded data, it hit and did not forward data, or it hit and the forwarded data was modified.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xd1", + "EventName": "MEM_LOAD_UOPS_RETIRED.L3_HIT_SNOOP_HIT", + "PublicDescription": "Counts the number of load ops retired that hit in the L3 cache in which a snoop was required and it hit and forwarded data, it hit and did not forward data, or it hit and the forwarded data was modified. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that uops are blocked for any of the following reasons: load buffer, store buffer or RSV full.", "Counter": "0,1,2,3,4,5,6,7", @@ -880,13 +1020,14 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024.", "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_1024", "MSRIndex": "0x3F6", "MSRValue": "0x400", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" @@ -894,6 +1035,7 @@ { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_128", "MSRIndex": "0x3F6", @@ -906,6 +1048,7 @@ { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_16", "MSRIndex": "0x3F6", @@ -916,13 +1059,14 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 2048.", "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_2048", "MSRIndex": "0x3F6", "MSRValue": "0x800", - "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled. Available PDIST counters: 0,1", + "PublicDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 2048. Available PDIST counters: 0,1", "SampleAfterValue": "1000003", "UMask": "0x5", "Unit": "cpu_atom" @@ -930,6 +1074,7 @@ { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_256", "MSRIndex": "0x3F6", @@ -942,6 +1087,7 @@ { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_32", "MSRIndex": "0x3F6", @@ -954,6 +1100,7 @@ { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_4", "MSRIndex": "0x3F6", @@ -966,6 +1113,7 @@ { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_512", "MSRIndex": "0x3F6", @@ -978,6 +1126,7 @@ { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_64", "MSRIndex": "0x3F6", @@ -990,6 +1139,7 @@ { "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.LOAD_LATENCY_GT_8", "MSRIndex": "0x3F6", @@ -1072,6 +1222,7 @@ { "BriefDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES", "Counter": "0,1,2,3,4,5,6,7", + "Data_LA": "1", "EventCode": "0xd0", "EventName": "MEM_UOPS_RETIRED.STORE_LATENCY", "PublicDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES Available PDIST counters: 0,1", diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/floating-point.json b/tools/perf/pmu-events/arch/x86/pantherlake/floating-point.json index e306a45b22ee..77f6c9028d93 100644 --- a/tools/perf/pmu-events/arch/x86/pantherlake/floating-point.json +++ b/tools/perf/pmu-events/arch/x86/pantherlake/floating-point.json @@ -1,4 +1,14 @@ [ + { + "BriefDescription": "Counts the number of cycles when any of the floating point dividers are active.", + "Counter": "0,1,2,3,4,5,6,7", + "CounterMask": "1", + "EventCode": "0xcd", + "EventName": "ARITH.FPDIV_ACTIVE", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, { "BriefDescription": "Cycles when floating-point divide unit is busy executing divide or square root operations.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -10,6 +20,24 @@ "UMask": "0x1", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of floating point dividers per cycle in the loop stage.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.FPDIV_OCCUPANCY", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of floating point divider uops executed per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.FPDIV_UOPS", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts all microcode FP assists.", "Counter": "0,1,2,3,4,5,6,7,8,9", diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/frontend.json b/tools/perf/pmu-events/arch/x86/pantherlake/frontend.json index d36faa683d3f..5e69b81742f5 100644 --- a/tools/perf/pmu-events/arch/x86/pantherlake/frontend.json +++ b/tools/perf/pmu-events/arch/x86/pantherlake/frontend.json @@ -422,6 +422,24 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "Cycles where a code fetch is stalled due to L1 instruction cache In use-full", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_INUSE", + "SampleAfterValue": "200003", + "UMask": "0x10", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Cycles where a code fetch is stalled due to L1 instruction cache ISB-full", + "Counter": "0,1,2,3,4,5,6,7,8,9", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_ISB", + "SampleAfterValue": "200003", + "UMask": "0x8", + "Unit": "cpu_core" + }, { "BriefDescription": "Cycles Decode Stream Buffer (DSB) is delivering any Uop", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -561,5 +579,23 @@ "SampleAfterValue": "1000003", "UMask": "0x1", "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts the number of cycles that the micro-sequencer is busy.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe7", + "EventName": "MS_DECODED.MS_BUSY", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of times entered into a ucode flow in the FEC. Includes inserted flows due to front-end detected faults or assists.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe7", + "EventName": "MS_DECODED.MS_ENTRY", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" } ] diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/memory.json b/tools/perf/pmu-events/arch/x86/pantherlake/memory.json index 3d31e620383d..4248cc101391 100644 --- a/tools/perf/pmu-events/arch/x86/pantherlake/memory.json +++ b/tools/perf/pmu-events/arch/x86/pantherlake/memory.json @@ -8,6 +8,24 @@ "UMask": "0xf4", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to a DL1 miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.L1_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to a DL1 miss.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.L1_MISS_AT_RET", + "SampleAfterValue": "1000003", + "UMask": "0x81", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer is stalled due to request buffers full or lock in progress.", "Counter": "0,1,2,3,4,5,6,7", @@ -17,6 +35,15 @@ "UMask": "0x2", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of cycles that the head (oldest load) of the load buffer and retirement are both stalled due to request buffers full or lock in progress.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x05", + "EventName": "LD_HEAD.WCB_FULL_AT_RET", + "SampleAfterValue": "1000003", + "UMask": "0x82", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of machine clears due to memory ordering caused by a snoop from an external agent. Does not count internally generated machine clears such as those due to memory disambiguation.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/other.json b/tools/perf/pmu-events/arch/x86/pantherlake/other.json index d49651d4f112..915c52f5abd1 100644 --- a/tools/perf/pmu-events/arch/x86/pantherlake/other.json +++ b/tools/perf/pmu-events/arch/x86/pantherlake/other.json @@ -30,6 +30,16 @@ "UMask": "0x1", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the total number of BTCLEARS.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe8", + "EventName": "PREDICTION.BTCLEAR", + "PublicDescription": "Counts the total number of BTCLEARS which occurs when the Branch Target Buffer (BTB) predicts a taken branch.", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, { "BriefDescription": "Cycles the uncore cannot take further requests", "Counter": "0,1,2,3,4,5,6,7,8,9", diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/pipeline.json b/tools/perf/pmu-events/arch/x86/pantherlake/pipeline.json index fb87d30c403d..86009237df2f 100644 --- a/tools/perf/pmu-events/arch/x86/pantherlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/pantherlake/pipeline.json @@ -1,4 +1,14 @@ [ + { + "BriefDescription": "Counts the number of cycles when any of the floating point or integer dividers are active.", + "Counter": "0,1,2,3,4,5,6,7", + "CounterMask": "1", + "EventCode": "0xcd", + "EventName": "ARITH.DIV_ACTIVE", + "SampleAfterValue": "1000003", + "UMask": "0x3", + "Unit": "cpu_atom" + }, { "BriefDescription": "Cycles when divide unit is busy executing divide or square root operations.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -10,6 +20,16 @@ "UMask": "0x9", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of cycles when any of the integer dividers are active.", + "Counter": "0,1,2,3,4,5,6,7", + "CounterMask": "1", + "EventCode": "0xcd", + "EventName": "ARITH.IDIV_ACTIVE", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, { "BriefDescription": "Cycles when integer divide unit is busy executing divide or square root operations.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -21,6 +41,24 @@ "UMask": "0x8", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts number of active integer dividers per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.IDIV_OCCUPANCY", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of integer divider uops executed per cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xcd", + "EventName": "ARITH.IDIV_UOPS", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_atom" + }, { "BriefDescription": "Number of occurrences where a microcode assist is invoked by hardware.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -58,6 +96,38 @@ "SampleAfterValue": "400009", "Unit": "cpu_core" }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.ALL_NEAR_IND", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x50", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_OR_RETURN]", + "Counter": "0,1,2,3,4,5,6,7", + "Deprecated": "1", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.ALL_NEAR_IND_OR_RET", + "PublicDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_OR_RETURN] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x58", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of conditional branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.COND", + "PublicDescription": "Counts the number of conditional branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x7", + "Unit": "cpu_atom" + }, { "BriefDescription": "Conditional branch instructions retired.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -88,6 +158,16 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of taken conditional branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.COND_TAKEN", + "PublicDescription": "Counts the number of taken conditional branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x3", + "Unit": "cpu_atom" + }, { "BriefDescription": "Taken conditional branch instructions retired.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -98,6 +178,16 @@ "UMask": "0x3", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of taken backward conditional branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.COND_TAKEN_BWD", + "PublicDescription": "Counts the number of taken backward conditional branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, { "BriefDescription": "Taken backward conditional branch instructions retired.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -108,6 +198,16 @@ "UMask": "0x1", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of taken forward conditional branch instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.COND_TAKEN_FWD", + "PublicDescription": "Counts the number of taken forward conditional branch instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, { "BriefDescription": "Taken forward conditional branch instructions retired.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -178,6 +278,16 @@ "UMask": "0x80", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of near indirect JMP and near indirect CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.ALL_NEAR_IND]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_INDIRECT", + "PublicDescription": "Counts the number of near indirect JMP and near indirect CALL branch instructions retired. [This event is alias to BR_INST_RETIRED.ALL_NEAR_IND] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x50", + "Unit": "cpu_atom" + }, { "BriefDescription": "Indirect near branch instructions retired (excluding returns) [This event is alias to BR_INST_RETIRED.INDIRECT]", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -208,6 +318,16 @@ "UMask": "0x40", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of near indirect JMP, near indirect CALL, and RET branch instructions retired. [This event is alias to BR_INST_RETIRED.ALL_NEAR_IND_OR_RET]", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc4", + "EventName": "BR_INST_RETIRED.NEAR_INDIRECT_OR_RETURN", + "PublicDescription": "Counts the number of near indirect JMP, near indirect CALL, and RET branch instructions retired. [This event is alias to BR_INST_RETIRED.ALL_NEAR_IND_OR_RET] Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x58", + "Unit": "cpu_atom" + }, { "BriefDescription": "This event is deprecated. [This event is alias to BR_INST_RETIRED.NEAR_INDIRECT_CALL]", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -283,7 +403,7 @@ "Unit": "cpu_atom" }, { - "BriefDescription": "Taken branch instructions retired.", + "BriefDescription": "Near Taken branch instructions retired.", "Counter": "0,1,2,3,4,5,6,7,8,9", "EventCode": "0xc4", "EventName": "BR_INST_RETIRED.NEAR_TAKEN", @@ -755,7 +875,7 @@ "Unit": "cpu_core" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles.", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.CORE", "SampleAfterValue": "2000003", @@ -1549,6 +1669,16 @@ "UMask": "0x1", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of CLFLUSH, CLWB, and CLDEMOTE instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.CL_INST", + "PublicDescription": "Counts the number of CLFLUSH, CLWB, and CLDEMOTE instructions retired. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0xff", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of LFENCE instructions retired.", "Counter": "0,1,2,3,4,5,6,7", @@ -1620,6 +1750,15 @@ "UMask": "0x4", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number issue slots not consumed due to a color request for an FCW or MXCSR control register when all 4 colors (copies) are already in use.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x75", + "EventName": "SERIALIZATION.COLOR_STALLS", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of issue slots where no uop could issue due to an IQ scoreboard that stalls allocation until a specified older uop retires or (in the case of jump scoreboard) executes. Commonly executed instructions with IQ scoreboards include LFENCE and MFENCE.", "Counter": "0,1,2,3,4,5,6,7", @@ -1732,6 +1871,15 @@ "UMask": "0x8", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the total number of issue slots that were not consumed by the backend because allocation is stalled due to a machine clear (nuke) of any kind including memory ordering and memory disambiguation.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x73", + "EventName": "TOPDOWN_BAD_SPECULATION.MACHINE_CLEARS", + "SampleAfterValue": "1000003", + "UMask": "0x3", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to Branch Mispredict", "Counter": "0,1,2,3,4,5,6,7", @@ -1795,6 +1943,15 @@ "UMask": "0x2", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to ROB full", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x74", + "EventName": "TOPDOWN_BE_BOUND.REORDER_BUFFER", + "SampleAfterValue": "1000003", + "UMask": "0x40", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of issue slots every cycle that were not consumed by the backend due to iq/jeu scoreboards or ms scb", "Counter": "0,1,2,3,4,5,6,7", @@ -2076,6 +2233,15 @@ "UMask": "0x10", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of uops issued by the front end every cycle.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x0e", + "EventName": "UOPS_ISSUED.ANY", + "PublicDescription": "Counts the number of uops issued by the front end every cycle. When 4-uops are requested and only 2-uops are delivered, the event counts 2. Uops_issued correlates to the number of ROB entries. If uop takes 2 ROB slots it counts as 2 uops_issued.", + "SampleAfterValue": "1000003", + "Unit": "cpu_atom" + }, { "BriefDescription": "Uops that RAT issues to RS", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -2107,6 +2273,16 @@ "UMask": "0x2", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of uops retired that are the last uop of a macro-instruction.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.EOM", + "PublicDescription": "Counts the number of uops retired that are the last uop of a macro-instruction. EOM uops indicate the 'end of a macro-instruction' and play a crucial role in the processor's control flow and recovery mechanisms.", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_atom" + }, { "BriefDescription": "Retired uops except the last uop of each instruction.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -2127,6 +2303,16 @@ "UMask": "0x80", "Unit": "cpu_atom" }, + { + "BriefDescription": "Counts the number of uops retired that originated from a loop stream detector.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.LSD", + "PublicDescription": "Counts the number of uops retired that originated from a loop stream detector. Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x20", + "Unit": "cpu_atom" + }, { "BriefDescription": "Counts the number of uops that are from the complex flows issued by the micro-sequencer (MS). This includes uops from flows due to complex instructions, faults, assists, and inserted flows.", "Counter": "0,1,2,3,4,5,6,7", @@ -2161,6 +2347,16 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "UOPS_RETIRED.NANO_CODE", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.NANO_CODE", + "PublicDescription": "UOPS_RETIRED.NANO_CODE Available PDIST counters: 0,1", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_atom" + }, { "BriefDescription": "This event counts a subset of the Topdown Slots event that are utilized by operations that eventually get retired (committed) by the processor pipeline. Usually, this event positively correlates with higher performance for example, as measured by the instructions-per-cycle metric.", "Counter": "0,1,2,3,4,5,6,7,8,9", diff --git a/tools/perf/pmu-events/arch/x86/pantherlake/virtual-memory.json b/tools/perf/pmu-events/arch/x86/pantherlake/virtual-memory.json index 8d56c16b2a39..8f3dd36707dc 100644 --- a/tools/perf/pmu-events/arch/x86/pantherlake/virtual-memory.json +++ b/tools/perf/pmu-events/arch/x86/pantherlake/virtual-memory.json @@ -78,6 +78,16 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of page walks completed due to load DTLB misses to a 4K page.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x08", + "EventName": "DTLB_LOAD_MISSES.WALK_COMPLETED_4K", + "PublicDescription": "Counts the number of page walks completed due to loads (including SW prefetches) whose address translations missed in all Translation Lookaside Buffer (TLB) levels and were mapped to 4K pages. Includes page walks that page fault.", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, { "BriefDescription": "Page walks completed due to a demand data load to a 4K page.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -178,6 +188,16 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of page walks completed due to store DTLB misses to a 4K page.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x49", + "EventName": "DTLB_STORE_MISSES.WALK_COMPLETED_4K", + "PublicDescription": "Counts the number of page walks completed due to stores whose address translations missed in all Translation Lookaside Buffer (TLB) levels and were mapped to 4K pages. Includes page walks that page fault.", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, { "BriefDescription": "Page walks completed due to a demand data store to a 4K page.", "Counter": "0,1,2,3,4,5,6,7,8,9", @@ -267,6 +287,16 @@ "UMask": "0x4", "Unit": "cpu_core" }, + { + "BriefDescription": "Counts the number of page walks completed due to instruction fetch misses to a 4K page.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0x85", + "EventName": "ITLB_MISSES.WALK_COMPLETED_4K", + "PublicDescription": "Counts the number of page walks completed due to instruction fetches whose address translations missed in all Translation Lookaside Buffer (TLB) levels and were mapped to 4K pages. Includes page walks that page fault.", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_atom" + }, { "BriefDescription": "Code miss in all TLB levels causes a page walk that completes. (4K)", "Counter": "0,1,2,3,4,5,6,7,8,9", diff --git a/tools/perf/pmu-events/arch/x86/sapphirerapids/cache.json b/tools/perf/pmu-events/arch/x86/sapphirerapids/cache.json index c66324d41a89..373b26c84448 100644 --- a/tools/perf/pmu-events/arch/x86/sapphirerapids/cache.json +++ b/tools/perf/pmu-events/arch/x86/sapphirerapids/cache.json @@ -514,7 +514,7 @@ "EventCode": "0xd3", "EventName": "MEM_LOAD_L3_MISS_RETIRED.REMOTE_DRAM", "PublicDescription": "MEM_LOAD_L3_MISS_RETIRED.REMOTE_DRAM Available PDIST counters: 0", - "SampleAfterValue": "1000003", + "SampleAfterValue": "100007", "UMask": "0x2" }, { @@ -534,7 +534,7 @@ "EventCode": "0xd3", "EventName": "MEM_LOAD_L3_MISS_RETIRED.REMOTE_HITM", "PublicDescription": "MEM_LOAD_L3_MISS_RETIRED.REMOTE_HITM Available PDIST counters: 0", - "SampleAfterValue": "1000003", + "SampleAfterValue": "100007", "UMask": "0x4" }, { diff --git a/tools/perf/pmu-events/arch/x86/sapphirerapids/frontend.json b/tools/perf/pmu-events/arch/x86/sapphirerapids/frontend.json index 793c486ffabe..e51f5e85ffd1 100644 --- a/tools/perf/pmu-events/arch/x86/sapphirerapids/frontend.json +++ b/tools/perf/pmu-events/arch/x86/sapphirerapids/frontend.json @@ -271,6 +271,22 @@ "SampleAfterValue": "200003", "UMask": "0x4" }, + { + "BriefDescription": "ICACHE_TAG.STALLS_INUSE", + "Counter": "0,1,2,3", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_INUSE", + "SampleAfterValue": "200003", + "UMask": "0x10" + }, + { + "BriefDescription": "ICACHE_TAG.STALLS_ISB", + "Counter": "0,1,2,3", + "EventCode": "0x83", + "EventName": "ICACHE_TAG.STALLS_ISB", + "SampleAfterValue": "200003", + "UMask": "0x8" + }, { "BriefDescription": "Cycles Decode Stream Buffer (DSB) is delivering any Uop", "Counter": "0,1,2,3", diff --git a/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-cache.json b/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-cache.json index 1bdda3c3ccbf..59f6fd2c7a8f 100644 --- a/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-cache.json +++ b/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-cache.json @@ -3501,7 +3501,7 @@ "EventName": "UNC_CHA_SNOOP_RESP.RSPIFWD", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "Counts when a a transaction with the opcode type RspIFwd Snoop Response was received which indicates a remote caching agent forwarded the data and the requesting agent is able to acquire the data in E (Exclusive) or M (modified) states. This is commonly returned with RFO (the Read for Ownership issued before a write) transactions. The snoop could have either been to a cacheline in the M,E,F (Modified, Exclusive or Forward) states.", + "PublicDescription": "Counts when a transaction with the opcode type RspIFwd Snoop Response was received which indicates a remote caching agent forwarded the data and the requesting agent is able to acquire the data in E (Exclusive) or M (modified) states. This is commonly returned with RFO (the Read for Ownership issued before a write) transactions. The snoop could have either been to a cacheline in the M,E,F (Modified, Exclusive or Forward) states.", "UMask": "0x4", "Unit": "CHA" }, @@ -3523,7 +3523,7 @@ "EventName": "UNC_CHA_SNOOP_RESP.RSPSFWD", "Experimental": "1", "PerPkg": "1", - "PublicDescription": "Counts when a a transaction with the opcode type RspSFwd Snoop Response was received which indicates a remote caching agent forwarded the data but held on to its current copy. This is common for data and code reads that hit in a remote socket in E (Exclusive) or F (Forward) state.", + "PublicDescription": "Counts when a transaction with the opcode type RspSFwd Snoop Response was received which indicates a remote caching agent forwarded the data but held on to its current copy. This is common for data and code reads that hit in a remote socket in E (Exclusive) or F (Forward) state.", "UMask": "0x8", "Unit": "CHA" }, diff --git a/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-io.json b/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-io.json index dac7e6c50f31..45675a1099e2 100644 --- a/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-io.json +++ b/tools/perf/pmu-events/arch/x86/sapphirerapids/uncore-io.json @@ -303,6 +303,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", + "PortMask": "0xff", "UMask": "0xff", "Unit": "IIO" }, @@ -314,7 +315,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x01", "PublicDescription": "x16 card plugged in to stack, Or x8 card plugged in to Lane 0/1, Or x4 card is plugged in to slot 0", "UMask": "0x1", "Unit": "IIO" @@ -327,7 +328,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x02", "PublicDescription": "x4 card is plugged in to slot 1", "UMask": "0x2", "Unit": "IIO" @@ -340,7 +341,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x04", "PublicDescription": "x8 card plugged in to Lane 2/3, Or x4 card is plugged in to slot 1", "UMask": "0x4", "Unit": "IIO" @@ -353,7 +354,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x08", "PublicDescription": "x4 card is plugged in to slot 3", "UMask": "0x8", "Unit": "IIO" @@ -366,7 +367,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x10", "PublicDescription": "x16 card plugged in to stack, Or x8 card plugged in to Lane 0/1, Or x4 card is plugged in to slot 0", "UMask": "0x10", "Unit": "IIO" @@ -379,7 +380,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x20", "PublicDescription": "x4 card is plugged in to slot 1", "UMask": "0x20", "Unit": "IIO" @@ -392,7 +393,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x40", "PublicDescription": "x8 card plugged in to Lane 2/3, Or x4 card is plugged in to slot 1", "UMask": "0x40", "Unit": "IIO" @@ -405,7 +406,7 @@ "Experimental": "1", "FCMask": "0x07", "PerPkg": "1", - "PortMask": "0x0000", + "PortMask": "0x80", "PublicDescription": "x4 card is plugged in to slot 3", "UMask": "0x80", "Unit": "IIO" diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/cache.json b/tools/perf/pmu-events/arch/x86/sierraforest/cache.json index de0e7661a52d..168f43557a0e 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/cache.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/cache.json @@ -326,7 +326,7 @@ "UMask": "0x82" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 1024. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -337,7 +337,7 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 128. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -348,7 +348,7 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 16. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -359,7 +359,7 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 2048. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -370,7 +370,7 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 256. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -381,7 +381,7 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 32. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -392,7 +392,7 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 4. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -403,7 +403,7 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 512. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -414,7 +414,7 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 64. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -425,7 +425,7 @@ "UMask": "0x5" }, { - "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold defined in MEC_CR_PEBS_LD_LAT_THRESHOLD - Only counts with PEBS enabled.", + "BriefDescription": "Counts the number of tagged load uops retired that exceed the latency threshold of 8. Only counts with PEBS enabled.", "Counter": "0,1", "Data_LA": "1", "EventCode": "0xd0", @@ -499,7 +499,7 @@ "UMask": "0x12" }, { - "BriefDescription": "Counts the number of stores uops retired same as MEM_UOPS_RETIRED.ALL_STORES", + "BriefDescription": "Counts the number of stores uops retired.", "Counter": "0,1,2,3,4,5,6,7", "Data_LA": "1", "EventCode": "0xd0", diff --git a/tools/perf/pmu-events/arch/x86/sierraforest/pipeline.json b/tools/perf/pmu-events/arch/x86/sierraforest/pipeline.json index 70af13143024..cf67ff6135e0 100644 --- a/tools/perf/pmu-events/arch/x86/sierraforest/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/sierraforest/pipeline.json @@ -186,7 +186,7 @@ "UMask": "0xf7" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.THREAD]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.CORE", "SampleAfterValue": "2000003", @@ -200,7 +200,7 @@ "SampleAfterValue": "2000003" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted reference clock cycles", + "BriefDescription": "Fixed Counter: Counts the number of unhalted reference clock cycles.", "Counter": "Fixed counter 2", "EventName": "CPU_CLK_UNHALTED.REF_TSC", "SampleAfterValue": "2000003", @@ -216,7 +216,7 @@ "UMask": "0x1" }, { - "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles", + "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles. [This event is alias to CPU_CLK_UNHALTED.CORE]", "Counter": "Fixed counter 1", "EventName": "CPU_CLK_UNHALTED.THREAD", "SampleAfterValue": "2000003", @@ -230,10 +230,10 @@ "SampleAfterValue": "2000003" }, { - "BriefDescription": "Fixed Counter: Counts the number of instructions retired", + "BriefDescription": "Fixed Counter: Counts the number of instructions retired.", "Counter": "Fixed counter 0", "EventName": "INST_RETIRED.ANY", - "PublicDescription": "Fixed Counter: Counts the number of instructions retired Available PDIST counters: 32", + "PublicDescription": "Fixed Counter: Counts the number of instructions retired. Available PDIST counters: 32", "SampleAfterValue": "2000003", "UMask": "0x1" }, @@ -309,6 +309,38 @@ "SampleAfterValue": "1000003", "UMask": "0x1" }, + { + "BriefDescription": "Counts the number of CLFLUSH, CLWB, and CLDEMOTE instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.CL_INST", + "SampleAfterValue": "1000003", + "UMask": "0xff" + }, + { + "BriefDescription": "Counts the number of LFENCE instructions retired.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe0", + "EventName": "MISC_RETIRED1.LFENCE", + "SampleAfterValue": "1000003", + "UMask": "0x2" + }, + { + "BriefDescription": "Counts the number of accesses to KeyLocker cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe1", + "EventName": "MISC_RETIRED2.KEYLOCKER_ACCESS", + "SampleAfterValue": "1000003", + "UMask": "0x10" + }, + { + "BriefDescription": "Counts the number of misses to KeyLocker cache.", + "Counter": "0,1,2,3,4,5,6,7", + "EventCode": "0xe1", + "EventName": "MISC_RETIRED2.KEYLOCKER_MISS", + "SampleAfterValue": "1000003", + "UMask": "0x11" + }, { "BriefDescription": "Counts the number of issue slots in a UMWAIT or TPAUSE instruction where no uop issues due to the instruction putting the CPU into the C0.1 activity state.", "Counter": "0,1,2,3,4,5,6,7", diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c index 76c395cf513c..a92dd0424f79 100644 --- a/tools/perf/pmu-events/empty-pmu-events.c +++ b/tools/perf/pmu-events/empty-pmu-events.c @@ -1310,33 +1310,33 @@ static const char *const big_c_string = /* offset=128375 */ "migrations_per_second\000Default\000software@cpu\\-migrations\\,name\\=cpu\\-migrations@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Process migrations to a new CPU per CPU second\000\0001migrations/sec\000\000\000\000011" /* offset=128635 */ "page_faults_per_second\000Default\000software@page\\-faults\\,name\\=page\\-faults@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Page faults per CPU second\000\0001faults/sec\000\000\000\000011" /* offset=128866 */ "insn_per_cycle\000Default\000instructions / cpu\\-cycles\000insn_per_cycle < 1\000Instructions Per Cycle\000\0001instructions\000\000\000\000001" -/* offset=128979 */ "stalled_cycles_per_instruction\000Default\000max(stalled\\-cycles\\-frontend, stalled\\-cycles\\-backend) / instructions\000\000Max front or backend stalls per instruction\000\000\000\000\000\000001" -/* offset=129143 */ "frontend_cycles_idle\000Default\000stalled\\-cycles\\-frontend / cpu\\-cycles\000frontend_cycles_idle > 0.1\000Frontend stalls per cycle\000\000\000\000\000\000001" -/* offset=129273 */ "backend_cycles_idle\000Default\000stalled\\-cycles\\-backend / cpu\\-cycles\000backend_cycles_idle > 0.2\000Backend stalls per cycle\000\000\000\000\000\000001" -/* offset=129399 */ "cycles_frequency\000Default\000cpu\\-cycles / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Cycles per CPU second\000\0001GHz\000\000\000\000011" -/* offset=129575 */ "branch_frequency\000Default\000branches / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Branches per CPU second\000\0001000M/sec\000\000\000\000011" -/* offset=129755 */ "branch_miss_rate\000Default\000branch\\-misses / branches\000branch_miss_rate > 0.05\000Branch miss rate\000\000100%\000\000\000\000001" -/* offset=129859 */ "l1d_miss_rate\000Default2\000L1\\-dcache\\-load\\-misses / L1\\-dcache\\-loads\000l1d_miss_rate > 0.05\000L1D miss rate\000\000100%\000\000\000\000001" -/* offset=129975 */ "llc_miss_rate\000Default2\000LLC\\-load\\-misses / LLC\\-loads\000llc_miss_rate > 0.05\000LLC miss rate\000\000100%\000\000\000\000001" -/* offset=130076 */ "l1i_miss_rate\000Default3\000L1\\-icache\\-load\\-misses / L1\\-icache\\-loads\000l1i_miss_rate > 0.05\000L1I miss rate\000\000100%\000\000\000\000001" -/* offset=130191 */ "dtlb_miss_rate\000Default3\000dTLB\\-load\\-misses / dTLB\\-loads\000dtlb_miss_rate > 0.05\000dTLB miss rate\000\000100%\000\000\000\000001" -/* offset=130297 */ "itlb_miss_rate\000Default3\000iTLB\\-load\\-misses / iTLB\\-loads\000itlb_miss_rate > 0.05\000iTLB miss rate\000\000100%\000\000\000\000001" -/* offset=130403 */ "l1_prefetch_miss_rate\000Default4\000L1\\-dcache\\-prefetch\\-misses / L1\\-dcache\\-prefetches\000l1_prefetch_miss_rate > 0.05\000L1 prefetch miss rate\000\000100%\000\000\000\000001" -/* offset=130551 */ "CPI\000\0001 / IPC\000\000\000\000\000\000\000\000000" -/* offset=130574 */ "IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\000000" -/* offset=130638 */ "Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\000000" -/* offset=130805 */ "dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000" -/* offset=130870 */ "icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000" -/* offset=130938 */ "cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\000000" -/* offset=131010 */ "DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\000000" -/* offset=131105 */ "DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\000000" -/* offset=131240 */ "DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\000000" -/* offset=131305 */ "DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\000000" -/* offset=131374 */ "DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\000000" -/* offset=131445 */ "M1\000\000ipc + M2\000\000\000\000\000\000\000\000000" -/* offset=131468 */ "M2\000\000ipc + M1\000\000\000\000\000\000\000\000000" -/* offset=131491 */ "M3\000\0001 / M3\000\000\000\000\000\000\000\000000" -/* offset=131512 */ "L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\000000" +/* offset=128979 */ "stalled_cycles_per_instruction\000Default\000(max(stalled\\-cycles\\-frontend, stalled\\-cycles\\-backend) / instructions if has_event(stalled\\-cycles\\-frontend) & has_event(stalled\\-cycles\\-backend) else (stalled\\-cycles\\-frontend / instructions if has_event(stalled\\-cycles\\-frontend) else (stalled\\-cycles\\-backend / instructions if has_event(stalled\\-cycles\\-backend) else 0)))\000\000Max front or backend stalls per instruction\000\000\000\000\000\000001" +/* offset=129404 */ "frontend_cycles_idle\000Default\000(stalled\\-cycles\\-frontend / cpu\\-cycles if has_event(stalled\\-cycles\\-frontend) else 0)\000frontend_cycles_idle > 0.1\000Frontend stalls per cycle\000\000\000\000\000\000001" +/* offset=129583 */ "backend_cycles_idle\000Default\000(stalled\\-cycles\\-backend / cpu\\-cycles if has_event(stalled\\-cycles\\-backend) else 0)\000backend_cycles_idle > 0.2\000Backend stalls per cycle\000\000\000\000\000\000001" +/* offset=129757 */ "cycles_frequency\000Default\000cpu\\-cycles / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Cycles per CPU second\000\0001GHz\000\000\000\000011" +/* offset=129933 */ "branch_frequency\000Default\000branches / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Branches per CPU second\000\0001000M/sec\000\000\000\000011" +/* offset=130113 */ "branch_miss_rate\000Default\000branch\\-misses / branches\000branch_miss_rate > 0.05\000Branch miss rate\000\000100%\000\000\000\000001" +/* offset=130217 */ "l1d_miss_rate\000Default2\000L1\\-dcache\\-load\\-misses / L1\\-dcache\\-loads\000l1d_miss_rate > 0.05\000L1D miss rate\000\000100%\000\000\000\000001" +/* offset=130333 */ "llc_miss_rate\000Default2\000LLC\\-load\\-misses / LLC\\-loads\000llc_miss_rate > 0.05\000LLC miss rate\000\000100%\000\000\000\000001" +/* offset=130434 */ "l1i_miss_rate\000Default3\000L1\\-icache\\-load\\-misses / L1\\-icache\\-loads\000l1i_miss_rate > 0.05\000L1I miss rate\000\000100%\000\000\000\000001" +/* offset=130549 */ "dtlb_miss_rate\000Default3\000dTLB\\-load\\-misses / dTLB\\-loads\000dtlb_miss_rate > 0.05\000dTLB miss rate\000\000100%\000\000\000\000001" +/* offset=130655 */ "itlb_miss_rate\000Default3\000iTLB\\-load\\-misses / iTLB\\-loads\000itlb_miss_rate > 0.05\000iTLB miss rate\000\000100%\000\000\000\000001" +/* offset=130761 */ "l1_prefetch_miss_rate\000Default4\000L1\\-dcache\\-prefetch\\-misses / L1\\-dcache\\-prefetches\000l1_prefetch_miss_rate > 0.05\000L1 prefetch miss rate\000\000100%\000\000\000\000001" +/* offset=130909 */ "CPI\000\0001 / IPC\000\000\000\000\000\000\000\000000" +/* offset=130932 */ "IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\000000" +/* offset=130996 */ "Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\000000" +/* offset=131163 */ "dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000" +/* offset=131228 */ "icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000" +/* offset=131296 */ "cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\000000" +/* offset=131368 */ "DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\000000" +/* offset=131463 */ "DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\000000" +/* offset=131598 */ "DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\000000" +/* offset=131663 */ "DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\000000" +/* offset=131732 */ "DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\000000" +/* offset=131803 */ "M1\000\000ipc + M2\000\000\000\000\000\000\000\000000" +/* offset=131826 */ "M2\000\000ipc + M1\000\000\000\000\000\000\000\000000" +/* offset=131849 */ "M3\000\0001 / M3\000\000\000\000\000\000\000\000000" +/* offset=131870 */ "L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\000000" ; static const struct compact_pmu_event pmu_events__common_default_core[] = { @@ -2626,22 +2626,22 @@ static const struct pmu_table_entry pmu_events__common[] = { static const struct compact_pmu_event pmu_metrics__common_default_core[] = { { 127956 }, /* CPUs_utilized\000Default\000(software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@) / (duration_time * 1e9)\000\000Average CPU utilization\000\0001CPUs\000\000\000\000011 */ -{ 129273 }, /* backend_cycles_idle\000Default\000stalled\\-cycles\\-backend / cpu\\-cycles\000backend_cycles_idle > 0.2\000Backend stalls per cycle\000\000\000\000\000\000001 */ -{ 129575 }, /* branch_frequency\000Default\000branches / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Branches per CPU second\000\0001000M/sec\000\000\000\000011 */ -{ 129755 }, /* branch_miss_rate\000Default\000branch\\-misses / branches\000branch_miss_rate > 0.05\000Branch miss rate\000\000100%\000\000\000\000001 */ +{ 129583 }, /* backend_cycles_idle\000Default\000(stalled\\-cycles\\-backend / cpu\\-cycles if has_event(stalled\\-cycles\\-backend) else 0)\000backend_cycles_idle > 0.2\000Backend stalls per cycle\000\000\000\000\000\000001 */ +{ 129933 }, /* branch_frequency\000Default\000branches / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Branches per CPU second\000\0001000M/sec\000\000\000\000011 */ +{ 130113 }, /* branch_miss_rate\000Default\000branch\\-misses / branches\000branch_miss_rate > 0.05\000Branch miss rate\000\000100%\000\000\000\000001 */ { 128142 }, /* cs_per_second\000Default\000software@context\\-switches\\,name\\=context\\-switches@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Context switches per CPU second\000\0001cs/sec\000\000\000\000011 */ -{ 129399 }, /* cycles_frequency\000Default\000cpu\\-cycles / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Cycles per CPU second\000\0001GHz\000\000\000\000011 */ -{ 130191 }, /* dtlb_miss_rate\000Default3\000dTLB\\-load\\-misses / dTLB\\-loads\000dtlb_miss_rate > 0.05\000dTLB miss rate\000\000100%\000\000\000\000001 */ -{ 129143 }, /* frontend_cycles_idle\000Default\000stalled\\-cycles\\-frontend / cpu\\-cycles\000frontend_cycles_idle > 0.1\000Frontend stalls per cycle\000\000\000\000\000\000001 */ +{ 129757 }, /* cycles_frequency\000Default\000cpu\\-cycles / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Cycles per CPU second\000\0001GHz\000\000\000\000011 */ +{ 130549 }, /* dtlb_miss_rate\000Default3\000dTLB\\-load\\-misses / dTLB\\-loads\000dtlb_miss_rate > 0.05\000dTLB miss rate\000\000100%\000\000\000\000001 */ +{ 129404 }, /* frontend_cycles_idle\000Default\000(stalled\\-cycles\\-frontend / cpu\\-cycles if has_event(stalled\\-cycles\\-frontend) else 0)\000frontend_cycles_idle > 0.1\000Frontend stalls per cycle\000\000\000\000\000\000001 */ { 128866 }, /* insn_per_cycle\000Default\000instructions / cpu\\-cycles\000insn_per_cycle < 1\000Instructions Per Cycle\000\0001instructions\000\000\000\000001 */ -{ 130297 }, /* itlb_miss_rate\000Default3\000iTLB\\-load\\-misses / iTLB\\-loads\000itlb_miss_rate > 0.05\000iTLB miss rate\000\000100%\000\000\000\000001 */ -{ 130403 }, /* l1_prefetch_miss_rate\000Default4\000L1\\-dcache\\-prefetch\\-misses / L1\\-dcache\\-prefetches\000l1_prefetch_miss_rate > 0.05\000L1 prefetch miss rate\000\000100%\000\000\000\000001 */ -{ 129859 }, /* l1d_miss_rate\000Default2\000L1\\-dcache\\-load\\-misses / L1\\-dcache\\-loads\000l1d_miss_rate > 0.05\000L1D miss rate\000\000100%\000\000\000\000001 */ -{ 130076 }, /* l1i_miss_rate\000Default3\000L1\\-icache\\-load\\-misses / L1\\-icache\\-loads\000l1i_miss_rate > 0.05\000L1I miss rate\000\000100%\000\000\000\000001 */ -{ 129975 }, /* llc_miss_rate\000Default2\000LLC\\-load\\-misses / LLC\\-loads\000llc_miss_rate > 0.05\000LLC miss rate\000\000100%\000\000\000\000001 */ +{ 130655 }, /* itlb_miss_rate\000Default3\000iTLB\\-load\\-misses / iTLB\\-loads\000itlb_miss_rate > 0.05\000iTLB miss rate\000\000100%\000\000\000\000001 */ +{ 130761 }, /* l1_prefetch_miss_rate\000Default4\000L1\\-dcache\\-prefetch\\-misses / L1\\-dcache\\-prefetches\000l1_prefetch_miss_rate > 0.05\000L1 prefetch miss rate\000\000100%\000\000\000\000001 */ +{ 130217 }, /* l1d_miss_rate\000Default2\000L1\\-dcache\\-load\\-misses / L1\\-dcache\\-loads\000l1d_miss_rate > 0.05\000L1D miss rate\000\000100%\000\000\000\000001 */ +{ 130434 }, /* l1i_miss_rate\000Default3\000L1\\-icache\\-load\\-misses / L1\\-icache\\-loads\000l1i_miss_rate > 0.05\000L1I miss rate\000\000100%\000\000\000\000001 */ +{ 130333 }, /* llc_miss_rate\000Default2\000LLC\\-load\\-misses / LLC\\-loads\000llc_miss_rate > 0.05\000LLC miss rate\000\000100%\000\000\000\000001 */ { 128375 }, /* migrations_per_second\000Default\000software@cpu\\-migrations\\,name\\=cpu\\-migrations@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Process migrations to a new CPU per CPU second\000\0001migrations/sec\000\000\000\000011 */ { 128635 }, /* page_faults_per_second\000Default\000software@page\\-faults\\,name\\=page\\-faults@ * 1e9 / (software@cpu\\-clock\\,name\\=cpu\\-clock@ if #target_cpu else software@task\\-clock\\,name\\=task\\-clock@)\000\000Page faults per CPU second\000\0001faults/sec\000\000\000\000011 */ -{ 128979 }, /* stalled_cycles_per_instruction\000Default\000max(stalled\\-cycles\\-frontend, stalled\\-cycles\\-backend) / instructions\000\000Max front or backend stalls per instruction\000\000\000\000\000\000001 */ +{ 128979 }, /* stalled_cycles_per_instruction\000Default\000(max(stalled\\-cycles\\-frontend, stalled\\-cycles\\-backend) / instructions if has_event(stalled\\-cycles\\-frontend) & has_event(stalled\\-cycles\\-backend) else (stalled\\-cycles\\-frontend / instructions if has_event(stalled\\-cycles\\-frontend) else (stalled\\-cycles\\-backend / instructions if has_event(stalled\\-cycles\\-backend) else 0)))\000\000Max front or backend stalls per instruction\000\000\000\000\000\000001 */ }; @@ -2714,21 +2714,21 @@ static const struct pmu_table_entry pmu_events__test_soc_cpu[] = { }; static const struct compact_pmu_event pmu_metrics__test_soc_cpu_default_core[] = { -{ 130551 }, /* CPI\000\0001 / IPC\000\000\000\000\000\000\000\000000 */ -{ 131240 }, /* DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\000000 */ -{ 131010 }, /* DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\000000 */ -{ 131105 }, /* DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\000000 */ -{ 131305 }, /* DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\000000 */ -{ 131374 }, /* DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\000000 */ -{ 130638 }, /* Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\000000 */ -{ 130574 }, /* IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\000000 */ -{ 131512 }, /* L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\000000 */ -{ 131445 }, /* M1\000\000ipc + M2\000\000\000\000\000\000\000\000000 */ -{ 131468 }, /* M2\000\000ipc + M1\000\000\000\000\000\000\000\000000 */ -{ 131491 }, /* M3\000\0001 / M3\000\000\000\000\000\000\000\000000 */ -{ 130938 }, /* cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\000000 */ -{ 130805 }, /* dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000 */ -{ 130870 }, /* icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000 */ +{ 130909 }, /* CPI\000\0001 / IPC\000\000\000\000\000\000\000\000000 */ +{ 131598 }, /* DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\000000 */ +{ 131368 }, /* DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\000000 */ +{ 131463 }, /* DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\000000 */ +{ 131663 }, /* DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\000000 */ +{ 131732 }, /* DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\000000 */ +{ 130996 }, /* Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\000000 */ +{ 130932 }, /* IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\000000 */ +{ 131870 }, /* L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\000000 */ +{ 131803 }, /* M1\000\000ipc + M2\000\000\000\000\000\000\000\000000 */ +{ 131826 }, /* M2\000\000ipc + M1\000\000\000\000\000\000\000\000000 */ +{ 131849 }, /* M3\000\0001 / M3\000\000\000\000\000\000\000\000000 */ +{ 131296 }, /* cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\000000 */ +{ 131163 }, /* dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000 */ +{ 131228 }, /* icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\000000 */ }; diff --git a/tools/perf/tests/bitmap.c b/tools/perf/tests/bitmap.c index 98956e0e0765..e7adf60be721 100644 --- a/tools/perf/tests/bitmap.c +++ b/tools/perf/tests/bitmap.c @@ -16,7 +16,7 @@ static unsigned long *get_bitmap(const char *str, int nbits) bm = bitmap_zalloc(nbits); if (map && bm) { - int i; + unsigned int i; struct perf_cpu cpu; perf_cpu_map__for_each_cpu(cpu, i, map) diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c index 3faeb5b6fe0b..f580ba7486b1 100644 --- a/tools/perf/tests/bp_signal.c +++ b/tools/perf/tests/bp_signal.c @@ -36,7 +36,7 @@ static int fd3; static int overflows; static int overflows_2; -volatile long the_var; +static volatile long the_var; /* diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 5927d1ea20e2..47043a3a2fb4 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c index 2354246afc5a..b051dce2cd86 100644 --- a/tools/perf/tests/cpumap.c +++ b/tools/perf/tests/cpumap.c @@ -156,7 +156,8 @@ static int test__cpu_map_print(struct test_suite *test __maybe_unused, int subte return 0; } -static int __test__cpu_map_merge(const char *lhs, const char *rhs, int nr, const char *expected) +static int __test__cpu_map_merge(const char *lhs, const char *rhs, unsigned int nr, + const char *expected) { struct perf_cpu_map *a = perf_cpu_map__new(lhs); struct perf_cpu_map *b = perf_cpu_map__new(rhs); @@ -204,7 +205,8 @@ static int test__cpu_map_merge(struct test_suite *test __maybe_unused, return ret; } -static int __test__cpu_map_intersect(const char *lhs, const char *rhs, int nr, const char *expected) +static int __test__cpu_map_intersect(const char *lhs, const char *rhs, unsigned int nr, + const char *expected) { struct perf_cpu_map *a = perf_cpu_map__new(lhs); struct perf_cpu_map *b = perf_cpu_map__new(rhs); diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c index a1fff4203b75..46bc3f597260 100644 --- a/tools/perf/tests/dso-data.c +++ b/tools/perf/tests/dso-data.c @@ -58,7 +58,7 @@ struct test_data_offset { int size; }; -struct test_data_offset offsets[] = { +static struct test_data_offset offsets[] = { /* Fill first cache page. */ { .offset = 10, diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c index cb9e6de2e033..facc65e29f20 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -8,6 +8,7 @@ #include "header.h" #include "machine.h" #include "util/synthetic-events.h" +#include "target.h" #include "tool.h" #include "tests.h" #include "debug.h" @@ -81,7 +82,8 @@ static int test__event_update(struct test_suite *test __maybe_unused, int subtes { struct evsel *evsel; struct event_name tmp; - struct evlist *evlist = evlist__new_default(); + struct target target = {}; + struct evlist *evlist = evlist__new_default(&target, /*sample_callchains=*/false); TEST_ASSERT_VAL("failed to get evlist", evlist); diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgroup.c index c7b32a220ca1..dd547f2f77cc 100644 --- a/tools/perf/tests/expand-cgroup.c +++ b/tools/perf/tests/expand-cgroup.c @@ -8,6 +8,7 @@ #include "parse-events.h" #include "pmu-events/pmu-events.h" #include "pfm.h" +#include "target.h" #include #include #include @@ -99,7 +100,8 @@ out: for (i = 0; i < nr_events; i++) static int expand_default_events(void) { int ret; - struct evlist *evlist = evlist__new_default(); + struct target target = {}; + struct evlist *evlist = evlist__new_default(&target, /*sample_callchains=*/false); TEST_ASSERT_VAL("failed to get evlist", evlist); diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 3eb9ef8d7ec6..606aa926a8fc 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c @@ -81,7 +81,7 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) { struct addr_location al; struct evsel *evsel = hists_to_evsel(hists); - struct perf_sample sample = { .period = 1000, }; + struct perf_sample sample = { .evsel = evsel, .period = 1000, }; size_t i; addr_location__init(&al); diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 1cebd20cc91c..cc6b26e373d1 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c @@ -70,6 +70,7 @@ static int add_hist_entries(struct evlist *evlist, }; struct hists *hists = evsel__hists(evsel); + sample.evsel = evsel; /* make sure it has no filter at first */ hists->thread_filter = NULL; hists->dso_filter = NULL; diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index ee5ec8bda60e..7818950d786e 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c @@ -51,7 +51,7 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) { struct addr_location al; struct evsel *evsel = hists_to_evsel(hists); - struct perf_sample sample = { .period = 100, }; + struct perf_sample sample = { .evsel = evsel, .period = 100, }; size_t i; addr_location__init(&al); diff --git a/tools/perf/tests/mem2node.c b/tools/perf/tests/mem2node.c index a0e88c496107..7ce1ad7b6ce5 100644 --- a/tools/perf/tests/mem2node.c +++ b/tools/perf/tests/mem2node.c @@ -30,7 +30,7 @@ static unsigned long *get_bitmap(const char *str, int nbits) if (map && bm) { struct perf_cpu cpu; - int i; + unsigned int i; perf_cpu_map__for_each_cpu(cpu, i, map) __set_bit(cpu.cpu, bm); diff --git a/tools/perf/tests/openat-syscall-all-cpus.c b/tools/perf/tests/openat-syscall-all-cpus.c index 3644d6f52c07..0be43f8db3bd 100644 --- a/tools/perf/tests/openat-syscall-all-cpus.c +++ b/tools/perf/tests/openat-syscall-all-cpus.c @@ -22,7 +22,8 @@ static int test__openat_syscall_event_on_all_cpus(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - int err = TEST_FAIL, fd, idx; + int err = TEST_FAIL, fd; + unsigned int idx; struct perf_cpu cpu; struct perf_cpu_map *cpus; struct evsel *evsel; diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 1d3cc224fbc2..05c3e899b425 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1796,31 +1796,38 @@ static bool test__acr_valid(void) static int test__ratio_to_prev(struct evlist *evlist) { - struct evsel *evsel; + struct evsel *evsel, *leader; TEST_ASSERT_VAL("wrong number of entries", 2 * perf_pmus__num_core_pmus() == evlist->core.nr_entries); - evlist__for_each_entry(evlist, evsel) { - if (!perf_pmu__has_format(evsel->pmu, "acr_mask")) - return TEST_OK; - - if (evsel == evlist__first(evlist)) { - TEST_ASSERT_VAL("wrong config2", 0 == evsel->core.attr.config2); - TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0); - TEST_ASSERT_EVSEL("unexpected event", - evsel__match(evsel, HARDWARE, HW_CPU_CYCLES), - evsel); - } else { - TEST_ASSERT_VAL("wrong config2", 0 == evsel->core.attr.config2); - TEST_ASSERT_VAL("wrong leader", !evsel__is_group_leader(evsel)); - TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 0); - TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); - TEST_ASSERT_EVSEL("unexpected event", - evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS), - evsel); + evlist__for_each_entry(evlist, evsel) { + if (evsel != evsel__leader(evsel) || + !perf_pmu__has_format(evsel->pmu, "acr_mask")) { + continue; } + leader = evsel; + /* cycles */ + TEST_ASSERT_VAL("wrong config2", 0 == leader->core.attr.config2); + TEST_ASSERT_VAL("wrong core.nr_members", leader->core.nr_members == 2); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(leader) == 0); + TEST_ASSERT_EVSEL("unexpected event", + evsel__match(leader, HARDWARE, HW_CPU_CYCLES), + leader); + /* + * The period value gets configured within evlist__config, + * while this test executes only parse events method. + */ + TEST_ASSERT_VAL("wrong period", 0 == leader->core.attr.sample_period); + + /* instructions/period=200000,ratio-to-prev=2.0/ */ + evsel = evsel__next(evsel); + TEST_ASSERT_VAL("wrong config2", 0 == evsel->core.attr.config2); + TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader)); + TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 0); + TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1); + TEST_ASSERT_EVSEL("unexpected event", + evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS), + evsel); /* * The period value gets configured within evlist__config, * while this test executes only parse events method. diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index efbd9cd60c63..ad44cc68820b 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -84,8 +84,11 @@ static int test__PERF_RECORD(struct test_suite *test __maybe_unused, int subtest CPU_ZERO_S(cpu_mask_size, cpu_mask); perf_sample__init(&sample, /*all=*/false); - if (evlist == NULL) /* Fallback for kernels lacking PERF_COUNT_SW_DUMMY */ - evlist = evlist__new_default(); + if (evlist == NULL) { /* Fallback for kernels lacking PERF_COUNT_SW_DUMMY */ + struct target target = {}; + + evlist = evlist__new_default(&target, /*sample_callchains=*/false); + } if (evlist == NULL) { pr_debug("Not enough memory to create evlist\n"); @@ -297,6 +300,7 @@ static int test__PERF_RECORD(struct test_suite *test __maybe_unused, int subtest } perf_mmap__consume(&md->core); + perf_sample__exit(&sample); } perf_mmap__read_done(&md->core); } diff --git a/tools/perf/tests/shell/data_type_profiling.sh b/tools/perf/tests/shell/data_type_profiling.sh index 2a7f8f7c42d0..eca694600a04 100755 --- a/tools/perf/tests/shell/data_type_profiling.sh +++ b/tools/perf/tests/shell/data_type_profiling.sh @@ -8,13 +8,17 @@ set -e # data type profiling manifestation # Values in testtypes and testprogs should match -testtypes=("# data-type: struct Buf" "# data-type: struct _buf") +testtypes=("# data-type: struct Buf" "# data-type: struct buf") testprogs=("perf test -w code_with_type" "perf test -w datasym") err=0 perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) perfout=$(mktemp /tmp/__perf_test.perf.out.XXXXX) +# Check for support of perf mem before trap handler +perf mem record -o /dev/null -- true 2>&1 | \ + grep -q "failed: no PMU supports the memory events" && exit 2 + cleanup() { rm -rf "${perfdata}" "${perfout}" rm -rf "${perfdata}".old diff --git a/tools/perf/tests/shell/kwork.sh b/tools/perf/tests/shell/kwork.sh new file mode 100755 index 000000000000..42bfd9382816 --- /dev/null +++ b/tools/perf/tests/shell/kwork.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# perf kwork tests +# SPDX-License-Identifier: GPL-2.0 + +set -e + +# Root permissions required for tracing events. +if [ "$(id -u)" != 0 ]; then + echo "[Skip] No root permission" + exit 2 +fi + +err=0 +perfdata=$(mktemp /tmp/__perf_test_kwork.perf.data.XXXXX) + +cleanup() { + rm -f "${perfdata}" + rm -f "${perfdata}".old + + trap - EXIT TERM INT +} + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +test_kwork_record() { + echo "Kwork record" + perf kwork record -o "${perfdata}" -- sleep 1 + echo "Kwork record [Success]" +} + +test_kwork_report() { + echo "Kwork report" + if ! perf kwork report -i "${perfdata}" | grep -q "Kwork Name"; then + echo "Kwork report [Failed missing output]" + err=1 + fi + echo "Kwork report [Success]" +} + +test_kwork_latency() { + echo "Kwork latency" + if ! perf kwork latency -i "${perfdata}" | grep -q "Avg delay"; then + echo "Kwork latency [Failed missing output]" + err=1 + fi + echo "Kwork latency [Success]" +} + +test_kwork_timehist() { + echo "Kwork timehist" + if ! perf kwork timehist -i "${perfdata}" | grep -q "Kwork name"; then + echo "Kwork timehist [Failed missing output]" + err=1 + fi + echo "Kwork timehist [Success]" +} + +test_kwork_top() { + echo "Kwork top" + if ! perf kwork top -i "${perfdata}" | grep -q "COMMAND"; then + echo "Kwork top [Failed missing output]" + err=1 + fi + echo "Kwork top [Success]" +} + +test_kwork_record +test_kwork_report +test_kwork_latency +test_kwork_timehist +test_kwork_top + +cleanup +exit $err diff --git a/tools/perf/tests/shell/perf_sched_stats.sh b/tools/perf/tests/shell/perf_sched_stats.sh index 2b1410b050d0..f13eb0a75b76 100755 --- a/tools/perf/tests/shell/perf_sched_stats.sh +++ b/tools/perf/tests/shell/perf_sched_stats.sh @@ -4,10 +4,34 @@ set -e +if [ "$(id -u)" != 0 ]; then + echo "[Skip] No root permission" + exit 2 +fi + +perfdata=$(mktemp /tmp/__perf_test_sched_stats.perf.data.XXXXX) +perfdata2=$(mktemp /tmp/__perf_test_sched_stats.perf.data.XXXXX) + +cleanup() { + rm -f "${perfdata}" + rm -f "${perfdata}".old + rm -f "${perfdata2}" + rm -f "${perfdata2}".old + + trap - EXIT TERM INT +} + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + err=0 test_perf_sched_stats_record() { echo "Basic perf sched stats record test" - if ! perf sched stats record true 2>&1 | \ + if ! perf sched stats record -o "${perfdata}" true 2>&1 | \ grep -E -q "[ perf sched stats: Wrote samples to perf.data ]" then echo "Basic perf sched stats record test [Failed]" @@ -19,15 +43,13 @@ test_perf_sched_stats_record() { test_perf_sched_stats_report() { echo "Basic perf sched stats report test" - perf sched stats record true > /dev/null - if ! perf sched stats report 2>&1 | grep -E -q "Description" + perf sched stats record -o "${perfdata}" true > /dev/null + if ! perf sched stats report -i "${perfdata}" 2>&1 | grep -E -q "Description" then echo "Basic perf sched stats report test [Failed]" err=1 - rm perf.data return fi - rm perf.data echo "Basic perf sched stats report test [Success]" } @@ -44,16 +66,14 @@ test_perf_sched_stats_live() { test_perf_sched_stats_diff() { echo "Basic perf sched stats diff test" - perf sched stats record true > /dev/null - perf sched stats record true > /dev/null - if ! perf sched stats diff > /dev/null + perf sched stats record -o "${perfdata}" true > /dev/null + perf sched stats record -o "${perfdata2}" true > /dev/null + if ! perf sched stats diff "${perfdata}" "${perfdata2}" > /dev/null then echo "Basic perf sched stats diff test [Failed]" err=1 - rm perf.data.old perf.data return fi - rm perf.data.old perf.data echo "Basic perf sched stats diff test [Success]" } @@ -61,4 +81,6 @@ test_perf_sched_stats_record test_perf_sched_stats_report test_perf_sched_stats_live test_perf_sched_stats_diff + +cleanup exit $err diff --git a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh index ab99bef556bf..eca629ee83f0 100755 --- a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh +++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh @@ -22,9 +22,9 @@ event_pattern='probe_libc:inet_pton(_[[:digit:]]+)?' add_libc_inet_pton_event() { - event_name=$(perf probe -f -x $libc -a inet_pton 2>&1 | tail -n +2 | head -n -5 | \ + event_name=$(perf probe -f -x $libc -a inet_pton 2>&1 | \ awk -v ep="$event_pattern" -v l="$libc" '$0 ~ ep && $0 ~ \ - ("\\(on inet_pton in " l "\\)") {print $1}') + ("\\(on inet_pton in " l "\\)") {print $1}' | head -n 1) if [ $? -ne 0 ] || [ -z "$event_name" ] ; then printf "FAIL: could not add event\n" @@ -40,12 +40,12 @@ trace_libc_inet_pton_backtrace() { echo ".*inet_pton\+0x[[:xdigit:]]+[[:space:]]\($libc|inlined\)$" >> $expected case "$(uname -m)" in s390x) - eventattr='call-graph=dwarf,max-stack=4' + eventattr='call-graph=dwarf,max-stack=8' echo "((__GI_)?getaddrinfo|text_to_binary_address)\+0x[[:xdigit:]]+[[:space:]]\($libc|inlined\)$" >> $expected echo "(gaih_inet|main)\+0x[[:xdigit:]]+[[:space:]]\(inlined|.*/bin/ping.*\)$" >> $expected ;; *) - eventattr='max-stack=4' + eventattr='call-graph=dwarf,max-stack=8' echo ".*(\+0x[[:xdigit:]]+|\[unknown\])[[:space:]]\(.*/bin/ping.*\)$" >> $expected ;; esac diff --git a/tools/perf/tests/shell/stat_bpf_counters.sh b/tools/perf/tests/shell/stat_bpf_counters.sh index f43e28a136d3..35463358b273 100755 --- a/tools/perf/tests/shell/stat_bpf_counters.sh +++ b/tools/perf/tests/shell/stat_bpf_counters.sh @@ -41,8 +41,14 @@ check_counts() test_bpf_counters() { printf "Testing --bpf-counters " - base_instructions=$(perf stat --no-big-num -e instructions -- $workload 2>&1 | awk '/instructions/ {print $1}') - bpf_instructions=$(perf stat --no-big-num --bpf-counters -e instructions -- $workload 2>&1 | awk '/instructions/ {print $1}') + base_instructions=$(perf stat --no-big-num -e instructions -- $workload 2>&1 | \ + awk -v i=0 -v c=0 '/instructions/ { \ + if ($1 != " 0) printf "%.0f", c; else print "&1 | \ + awk -v i=0 -v c=0 '/instructions/ { \ + if ($1 != " 0) printf "%.0f", c; else print "&1) - base_instructions=$(echo "$stat_output"| awk '/base_instructions/ {print $1}') - bpf_instructions=$(echo "$stat_output"| awk '/bpf_instructions/ {print $1}') + base_instructions=$(echo "$stat_output"| \ + awk -v i=0 -v c=0 '/base_instructions/ { \ + if ($1 != " 0) printf "%.0f", c; else print " 0) printf "%.0f", c; else print " "$TMPDIR/record.txt" 2>&1 perf script -i "$TMPDIR/perf.data" --fields brstacksym > "$TMPDIR/perf.script" @@ -73,59 +79,88 @@ test_user_branches() { perf script -i "$TMPDIR/perf.data" --fields brstack | \ tr ' ' '\n' > "$TMPDIR/perf.script" - # There should be no kernel addresses with the u option, in either - # source or target addresses. - if grep -E -m1 "0x[89a-f][0-9a-f]{15}" $TMPDIR/perf.script; then - echo "ERROR: Kernel address found in user mode" + # There should be no kernel addresses in the target with the u option. + local regex="0x[89a-f][0-9a-f]{15}" + if has_kaslr_bug; then + # If the system has a kaslr bug that may leak kernel addresses + # in the source of something like an ERET/SYSRET. Make the regex + # more specific and just check the target address is in user + # code. + regex="^0x[0-9a-f]{0,16}/0x[89a-f][0-9a-f]{15}/" + fi + if grep -q -E -m1 "$regex" $TMPDIR/perf.script; then + echo "Testing user branch stack sampling [Failed kernel address found in user mode]" err=1 fi # some branch types are still not being tested: # IND COND_CALL COND_RET SYSRET SERROR NO_TX + if [ $err -eq 0 ]; then + echo "Testing user branch stack sampling [Passed]" + err=$start_err + else + echo "Testing user branch stack sampling [Failed]" + fi } test_trap_eret_branches() { echo "Testing trap & eret branches" - if ! is_arm64; then - echo "skip: not arm64" - else - perf record -o $TMPDIR/perf.data --branch-filter any,save_type,u,k -- \ - perf test -w traploop 1000 - perf script -i $TMPDIR/perf.data --fields brstacksym | \ - tr ' ' '\n' > $TMPDIR/perf.script - # BRBINF.TYPE == TRAP are mapped to PERF_BR_IRQ by the BRBE driver - check_branches "^trap_bench\+[^ ]+/[^ ]/IRQ/" - check_branches "^[^ ]+/trap_bench\+[^ ]+/ERET/" + if ! is_arm64; then + echo "Testing trap & eret branches [Skipped not arm64]" + return + fi + start_err=$err + err=0 + perf record -o $TMPDIR/perf.data --branch-filter any,save_type,u,k -- \ + perf test -w traploop 1000 > "$TMPDIR/record.txt" 2>&1 + perf script -i $TMPDIR/perf.data --fields brstacksym | \ + tr ' ' '\n' > $TMPDIR/perf.script + + # BRBINF.TYPE == TRAP are mapped to PERF_BR_IRQ by the BRBE driver + check_branches "^trap_bench\+[^ ]+/[^ ]/IRQ/" + check_branches "^[^ ]+/trap_bench\+[^ ]+/ERET/" + if [ $err -eq 0 ]; then + echo "Testing trap & eret branches [Passed]" + err=$start_err + else + echo "Testing trap & eret branches [Failed]" fi } test_kernel_branches() { - echo "Testing that k option only includes kernel source addresses" + echo "Testing kernel branch sampling" - if ! perf record --branch-filter any,k -o- -- true > /dev/null; then - echo "skip: not enough privileges" + if ! perf record --branch-filter any,k -o- -- true > "$TMPDIR/record.txt" 2>&1; then + echo "Testing that k option [Skipped not enough privileges]" + return + fi + start_err=$err + err=0 + perf record -o $TMPDIR/perf.data --branch-filter any,k -- \ + perf bench syscall basic --loop 1000 > "$TMPDIR/record.txt" 2>&1 + perf script -i $TMPDIR/perf.data --fields brstack | \ + tr ' ' '\n' > $TMPDIR/perf.script + + # Example of branch entries: + # "0xffffffff93bda241/0xffffffff93bda20f/M/-/-/..." + # Source addresses come first in user or kernel code. Next is the target + # address that must be in the kernel. + + # Look for source addresses with top bit set + if ! grep -q -E -m1 "^0x[89a-f][0-9a-f]{15}" $TMPDIR/perf.script; then + echo "Testing kernel branch sampling [Failed kernel branches missing]" + err=1 + fi + # Look for no target addresses without top bit set + if grep -q -E -m1 "^0x[0-9a-f]{0,16}/0x[0-7][0-9a-f]{1,15}/" $TMPDIR/perf.script; then + echo "Testing kernel branch sampling [Failed user branches found]" + err=1 + fi + if [ $err -eq 0 ]; then + echo "Testing kernel branch sampling [Passed]" + err=$start_err else - perf record -o $TMPDIR/perf.data --branch-filter any,k -- \ - perf bench syscall basic --loop 1000 - perf script -i $TMPDIR/perf.data --fields brstack | \ - tr ' ' '\n' > $TMPDIR/perf.script - - # Example of branch entries: - # "0xffffffff93bda241/0xffffffff93bda20f/M/-/-/..." - # Source addresses come first and target address can be either - # userspace or kernel even with k option, as long as the source - # is in kernel. - - #Look for source addresses with top bit set - if ! grep -E -m1 "^0x[89a-f][0-9a-f]{15}" $TMPDIR/perf.script; then - echo "ERROR: Kernel branches missing" - err=1 - fi - # Look for no source addresses without top bit set - if grep -E -m1 "^0x[0-7][0-9a-f]{0,15}" $TMPDIR/perf.script; then - echo "ERROR: User branches found with kernel filter" - err=1 - fi + echo "Testing kernel branch sampling [Failed]" fi } @@ -136,14 +171,15 @@ test_filter() { test_filter_expect=$2 echo "Testing branch stack filtering permutation ($test_filter_filter,$test_filter_expect)" - perf record -o "$TMPDIR/perf.data" --branch-filter "$test_filter_filter,save_type,u" -- ${TESTPROG} > "$TMPDIR/record.txt" 2>&1 + perf record -o "$TMPDIR/perf.data" --branch-filter "$test_filter_filter,save_type,u" -- \ + ${TESTPROG} > "$TMPDIR/record.txt" 2>&1 perf script -i "$TMPDIR/perf.data" --fields brstack > "$TMPDIR/perf.script" # fail if we find any branch type that doesn't match any of the expected ones # also consider UNKNOWN branch types (-) if [ ! -s "$TMPDIR/perf.script" ] then - echo "Empty script output" + echo "Testing branch stack filtering [Failed empty script output]" err=1 return fi @@ -154,26 +190,36 @@ test_filter() { > "$TMPDIR/perf.script-filtered" || true if [ -s "$TMPDIR/perf.script-filtered" ] then - echo "Unexpected branch filter in script output" + echo "Testing branch stack filtering [Failed unexpected branch filter]" cat "$TMPDIR/perf.script" err=1 return fi + echo "Testing branch stack filtering [Passed]" } test_syscall() { echo "Testing syscalls" # skip if perf doesn't have enough privileges - if ! perf record --branch-filter any,k -o- -- true > /dev/null; then - echo "skip: not enough privileges" - else - perf record -o $TMPDIR/perf.data --branch-filter \ - any_call,save_type,u,k -c 10000 -- \ - perf bench syscall basic --loop 1000 - perf script -i $TMPDIR/perf.data --fields brstacksym | \ - tr ' ' '\n' > $TMPDIR/perf.script + if ! perf record --branch-filter any,k -o- -- true > "$TMPDIR/record.txt" 2>&1; then + echo "Testing syscalls [Skipped: not enough privileges]" + return + fi + start_err=$err + err=0 + perf record -o $TMPDIR/perf.data --branch-filter \ + any_call,save_type,u,k -c 10007 -- \ + perf bench syscall basic --loop 8000 > "$TMPDIR/record.txt" 2>&1 + perf script -i $TMPDIR/perf.data --fields brstacksym | \ + tr ' ' '\n' > $TMPDIR/perf.script - check_branches "getppid[^ ]*/SYSCALL/" + check_branches "getppid[^ ]*/SYSCALL/" + + if [ $err -eq 0 ]; then + echo "Testing syscalls [Passed]" + err=$start_err + else + echo "Testing syscalls [Failed]" fi } set -e diff --git a/tools/perf/tests/shell/test_task_analyzer.sh b/tools/perf/tests/shell/test_task_analyzer.sh index e194fcf61df3..0314412e63b4 100755 --- a/tools/perf/tests/shell/test_task_analyzer.sh +++ b/tools/perf/tests/shell/test_task_analyzer.sh @@ -3,6 +3,11 @@ # SPDX-License-Identifier: GPL-2.0 tmpdir=$(mktemp -d /tmp/perf-script-task-analyzer-XXXXX) +# TODO: perf script report only supports input from the CWD perf.data file, make +# it support input from any file. +perfdata="perf.data" +csv="$tmpdir/csv" +csvsummary="$tmpdir/csvsummary" err=0 # set PERF_EXEC_PATH to find scripts in the source directory @@ -15,11 +20,10 @@ fi export ASAN_OPTIONS=detect_leaks=0 cleanup() { - rm -f perf.data - rm -f perf.data.old - rm -f csv - rm -f csvsummary + rm -f "${perfdata}" + rm -f "${perfdata}".old rm -rf "$tmpdir" + trap - exit term int } @@ -61,10 +65,10 @@ skip_no_probe_record_support() { prepare_perf_data() { # 1s should be sufficient to catch at least some switches - perf record -e sched:sched_switch -a -- sleep 1 > /dev/null 2>&1 + perf record -e sched:sched_switch -a -o "${perfdata}" -- sleep 1 > /dev/null 2>&1 # check if perf data file got created in above step. - if [ ! -e "perf.data" ]; then - printf "FAIL: perf record failed to create \"perf.data\" \n" + if [ ! -e "${perfdata}" ]; then + printf "FAIL: perf record failed to create \"${perfdata}\" \n" return 1 fi } @@ -130,28 +134,28 @@ test_extended_times_summary_ns() { } test_csv() { - perf script report task-analyzer --csv csv > /dev/null - check_exec_0 "perf script report task-analyzer --csv csv" - find_str_or_fail "Comm;" csv "${FUNCNAME[0]}" + perf script report task-analyzer --csv "${csv}" > /dev/null + check_exec_0 "perf script report task-analyzer --csv ${csv}" + find_str_or_fail "Comm;" "${csv}" "${FUNCNAME[0]}" } test_csv_extended_times() { - perf script report task-analyzer --csv csv --extended-times > /dev/null - check_exec_0 "perf script report task-analyzer --csv csv --extended-times" - find_str_or_fail "Out-Out;" csv "${FUNCNAME[0]}" + perf script report task-analyzer --csv "${csv}" --extended-times > /dev/null + check_exec_0 "perf script report task-analyzer --csv ${csv} --extended-times" + find_str_or_fail "Out-Out;" "${csv}" "${FUNCNAME[0]}" } test_csvsummary() { - perf script report task-analyzer --csv-summary csvsummary > /dev/null - check_exec_0 "perf script report task-analyzer --csv-summary csvsummary" - find_str_or_fail "Comm;" csvsummary "${FUNCNAME[0]}" + perf script report task-analyzer --csv-summary "${csvsummary}" > /dev/null + check_exec_0 "perf script report task-analyzer --csv-summary ${csvsummary}" + find_str_or_fail "Comm;" "${csvsummary}" "${FUNCNAME[0]}" } test_csvsummary_extended() { - perf script report task-analyzer --csv-summary csvsummary --summary-extended \ + perf script report task-analyzer --csv-summary "${csvsummary}" --summary-extended \ >/dev/null - check_exec_0 "perf script report task-analyzer --csv-summary csvsummary --summary-extended" - find_str_or_fail "Out-Out;" csvsummary "${FUNCNAME[0]}" + check_exec_0 "perf script report task-analyzer --csv-summary ${csvsummary} --summary-extended" + find_str_or_fail "Out-Out;" "${csvsummary}" "${FUNCNAME[0]}" } skip_no_probe_record_support diff --git a/tools/perf/tests/shell/trace_btf_general.sh b/tools/perf/tests/shell/trace_btf_general.sh index ef2da806be6b..7a94a5743924 100755 --- a/tools/perf/tests/shell/trace_btf_general.sh +++ b/tools/perf/tests/shell/trace_btf_general.sh @@ -1,5 +1,5 @@ #!/bin/bash -# perf trace BTF general tests +# perf trace BTF general tests (exclusive) # SPDX-License-Identifier: GPL-2.0 err=0 diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c index 15791fcb76b2..72a8289e846d 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c @@ -239,11 +239,13 @@ static int add_event(struct evlist *evlist, struct list_head *events, if (!sample.time) { pr_debug("event with no time\n"); + perf_sample__exit(&sample); return -1; } node->event_time = sample.time; + perf_sample__exit(&sample); return 0; } diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c index 54209592168d..877868107455 100644 --- a/tools/perf/tests/thread-map.c +++ b/tools/perf/tests/thread-map.c @@ -9,7 +9,6 @@ #include "debug.h" #include "event.h" #include "util/synthetic-events.h" -#include #include #include diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c index ec01150d208d..f54502ebef4b 100644 --- a/tools/perf/tests/topology.c +++ b/tools/perf/tests/topology.c @@ -9,6 +9,7 @@ #include "evlist.h" #include "debug.h" #include "pmus.h" +#include "target.h" #include #define TEMPL "/tmp/perf-test-XXXXXX" @@ -37,11 +38,12 @@ static int session_write_header(char *path) .path = path, .mode = PERF_DATA_MODE_WRITE, }; + struct target target = {}; session = perf_session__new(&data, NULL); TEST_ASSERT_VAL("can't get session", !IS_ERR(session)); - session->evlist = evlist__new_default(); + session->evlist = evlist__new_default(&target, /*sample_callchains=*/false); TEST_ASSERT_VAL("can't get evlist", session->evlist); session->evlist->session = session; @@ -52,7 +54,8 @@ static int session_write_header(char *path) session->header.data_size += DATA_SIZE; TEST_ASSERT_VAL("failed to write header", - !perf_session__write_header(session, session->evlist, data.file.fd, true)); + !perf_session__write_header(session, session->evlist, + perf_data__fd(&data), true)); evlist__delete(session->evlist); perf_session__delete(session); @@ -67,7 +70,7 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) .path = path, .mode = PERF_DATA_MODE_READ, }; - int i; + unsigned int i; struct aggr_cpu_id id; struct perf_cpu cpu; struct perf_env *env; @@ -114,7 +117,7 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map) TEST_ASSERT_VAL("Session header CPU map not set", env->cpu); - for (i = 0; i < env->nr_cpus_avail; i++) { + for (i = 0; i < (unsigned int)env->nr_cpus_avail; i++) { cpu.cpu = i; if (!perf_cpu_map__has(map, cpu)) continue; diff --git a/tools/perf/tests/workloads/datasym.c b/tools/perf/tests/workloads/datasym.c index 1d0b7d64e1ba..19242c7255c0 100644 --- a/tools/perf/tests/workloads/datasym.c +++ b/tools/perf/tests/workloads/datasym.c @@ -4,14 +4,14 @@ #include #include "../tests.h" -typedef struct _buf { +struct buf { char data1; char reserved[55]; char data2; -} buf __attribute__((aligned(64))); +} __attribute__((aligned(64))); /* volatile to try to avoid the compiler seeing reserved as unused. */ -static volatile buf workload_datasym_buf1 = { +static volatile struct buf workload_datasym_buf1 = { /* to have this in the data section */ .reserved[0] = 1, }; diff --git a/tools/perf/tests/wp.c b/tools/perf/tests/wp.c index 6c178985e37f..69b31f00eed0 100644 --- a/tools/perf/tests/wp.c +++ b/tools/perf/tests/wp.c @@ -22,11 +22,11 @@ do { \ #ifdef __i386__ /* Only breakpoint length less-than 8 has hardware support on i386. */ -volatile u32 data1; +static volatile u32 data1; #else -volatile u64 data1; +static volatile u64 data1; #endif -volatile u8 data2[3]; +static volatile u8 data2[3]; #ifndef __s390x__ static int wp_read(int fd, long long *count, int size) diff --git a/tools/perf/trace/beauty/drm_ioctl.sh b/tools/perf/trace/beauty/drm_ioctl.sh index 9aa94fd523a9..f2f1a257bac8 100755 --- a/tools/perf/trace/beauty/drm_ioctl.sh +++ b/tools/perf/trace/beauty/drm_ioctl.sh @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: LGPL-2.1 -[ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/drm/ +[ $# -eq 1 ] && header_dir=$1 || header_dir=tools/perf/trace/beauty/include/uapi/drm/ printf "#ifndef DRM_COMMAND_BASE\n" grep "#define DRM_COMMAND_BASE" $header_dir/drm.h diff --git a/tools/perf/trace/beauty/fadvise.sh b/tools/perf/trace/beauty/fadvise.sh index 4d3dd6e56ded..e9857112fa51 100755 --- a/tools/perf/trace/beauty/fadvise.sh +++ b/tools/perf/trace/beauty/fadvise.sh @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: LGPL-2.1 -[ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/ +[ $# -eq 1 ] && header_dir=$1 || header_dir=tools/perf/trace/beauty/include/uapi/linux/ printf "static const char *fadvise_advices[] = {\n" regex='^[[:space:]]*#[[:space:]]*define[[:space:]]+POSIX_FADV_(\w+)[[:space:]]+([[:digit:]]+)[[:space:]]+.*' diff --git a/tools/include/uapi/drm/drm.h b/tools/perf/trace/beauty/include/uapi/drm/drm.h similarity index 100% rename from tools/include/uapi/drm/drm.h rename to tools/perf/trace/beauty/include/uapi/drm/drm.h diff --git a/tools/include/uapi/drm/i915_drm.h b/tools/perf/trace/beauty/include/uapi/drm/i915_drm.h similarity index 100% rename from tools/include/uapi/drm/i915_drm.h rename to tools/perf/trace/beauty/include/uapi/drm/i915_drm.h diff --git a/tools/include/uapi/linux/fadvise.h b/tools/perf/trace/beauty/include/uapi/linux/fadvise.h similarity index 100% rename from tools/include/uapi/linux/fadvise.h rename to tools/perf/trace/beauty/include/uapi/linux/fadvise.h diff --git a/tools/perf/util/Build b/tools/perf/util/Build index bcccad7487a9..70cc91d00804 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -149,7 +149,6 @@ endif perf-util-y += cs-etm-base.o perf-util-y += parse-branch-options.o -perf-util-y += dump-insn.o perf-util-y += parse-regs-options.o perf-util-y += parse-sublevel-options.o perf-util-y += term.o @@ -171,7 +170,6 @@ perf-util-y += mutex.o perf-util-y += sharded_mutex.o perf-util-y += intel-tpebs.o -perf-util-$(CONFIG_LIBBPF) += bpf_map.o perf-util-$(CONFIG_PERF_BPF_SKEL) += bpf_counter.o perf-util-$(CONFIG_PERF_BPF_SKEL) += bpf_counter_cgroup.o perf-util-$(CONFIG_PERF_BPF_SKEL) += bpf_ftrace.o diff --git a/tools/perf/util/addr2line.c b/tools/perf/util/addr2line.c index 31c0391fffa3..4b0d349ed334 100644 --- a/tools/perf/util/addr2line.c +++ b/tools/perf/util/addr2line.c @@ -18,9 +18,6 @@ #define MAX_INLINE_NEST 1024 -/* If addr2line doesn't return data for 5 seconds then timeout. */ -int addr2line_timeout_ms = 5 * 1000; - static int filename_split(char *filename, unsigned int *line_nr) { char *sep; @@ -123,7 +120,7 @@ static enum cmd_a2l_style cmd_addr2line_configure(struct child_process *a2l, con lines = 3; pr_debug3("Detected binutils addr2line style\n"); } else { - if (!symbol_conf.disable_add2line_warn) { + if (!symbol_conf.addr2line_disable_warn) { char *output = NULL; size_t output_len; @@ -310,7 +307,7 @@ int cmd__addr2line(const char *dso_name, u64 addr, } if (a2l == NULL) { - if (!symbol_conf.disable_add2line_warn) + if (!symbol_conf.addr2line_disable_warn) pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name); goto out; } @@ -330,16 +327,16 @@ int cmd__addr2line(const char *dso_name, u64 addr, len = snprintf(buf, sizeof(buf), "%016"PRIx64"\n,\n", addr); written = len > 0 ? write(a2l->in, buf, len) : -1; if (written != len) { - if (!symbol_conf.disable_add2line_warn) + if (!symbol_conf.addr2line_disable_warn) pr_warning("%s %s: could not send request\n", __func__, dso_name); goto out; } io__init(&io, a2l->out, buf, sizeof(buf)); - io.timeout_ms = addr2line_timeout_ms; + io.timeout_ms = symbol_conf.addr2line_timeout_ms; switch (read_addr2line_record(&io, cmd_a2l_style, dso_name, addr, /*first=*/true, &record_function, &record_filename, &record_line_nr)) { case -1: - if (!symbol_conf.disable_add2line_warn) + if (!symbol_conf.addr2line_disable_warn) pr_warning("%s %s: could not read first record\n", __func__, dso_name); goto out; case 0: @@ -355,7 +352,7 @@ int cmd__addr2line(const char *dso_name, u64 addr, /*addr=*/1, /*first=*/true, NULL, NULL, NULL)) { case -1: - if (!symbol_conf.disable_add2line_warn) + if (!symbol_conf.addr2line_disable_warn) pr_warning("%s %s: could not read sentinel record\n", __func__, dso_name); break; @@ -363,7 +360,7 @@ int cmd__addr2line(const char *dso_name, u64 addr, /* The sentinel as expected. */ break; default: - if (!symbol_conf.disable_add2line_warn) + if (!symbol_conf.addr2line_disable_warn) pr_warning("%s %s: unexpected record instead of sentinel", __func__, dso_name); break; diff --git a/tools/perf/util/addr2line.h b/tools/perf/util/addr2line.h index d35a47ba8dab..75989a92f16b 100644 --- a/tools/perf/util/addr2line.h +++ b/tools/perf/util/addr2line.h @@ -8,8 +8,6 @@ struct dso; struct inline_node; struct symbol; -extern int addr2line_timeout_ms; - int cmd__addr2line(const char *dso_name, u64 addr, char **file, unsigned int *line_nr, struct dso *dso, diff --git a/tools/perf/util/affinity.c b/tools/perf/util/affinity.c index 4fe851334296..6c64b5f69a4e 100644 --- a/tools/perf/util/affinity.c +++ b/tools/perf/util/affinity.c @@ -90,7 +90,7 @@ void cpu_map__set_affinity(const struct perf_cpu_map *cpumap) int cpu_set_size = get_cpu_set_size(); unsigned long *cpuset = bitmap_zalloc(cpu_set_size * 8); struct perf_cpu cpu; - int idx; + unsigned int idx; if (!cpuset) return; diff --git a/tools/perf/util/annotate-arch/annotate-loongarch.c b/tools/perf/util/annotate-arch/annotate-loongarch.c index 950f34e59e5c..c2addca77320 100644 --- a/tools/perf/util/annotate-arch/annotate-loongarch.c +++ b/tools/perf/util/annotate-arch/annotate-loongarch.c @@ -110,6 +110,7 @@ static int loongarch_jump__parse(const struct arch *arch, struct ins_operands *o } static const struct ins_ops loongarch_jump_ops = { + .free = jump__delete, .parse = loongarch_jump__parse, .scnprintf = jump__scnprintf, .is_jump = true, diff --git a/tools/perf/util/annotate-arch/annotate-x86.c b/tools/perf/util/annotate-arch/annotate-x86.c index eb9a649ca656..7e6136536393 100644 --- a/tools/perf/util/annotate-arch/annotate-x86.c +++ b/tools/perf/util/annotate-arch/annotate-x86.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include #include #include #include "../annotate-data.h" @@ -204,6 +205,15 @@ static int x86__cpuid_parse(struct arch *arch, const char *cpuid) } #ifdef HAVE_LIBDW_SUPPORT +static void invalidate_reg_state(struct type_state_reg *reg) +{ + reg->kind = TSR_KIND_INVALID; + reg->ok = false; + reg->lifetime_active = false; + reg->lifetime_end = 0; + reg->copied_from = -1; +} + static void update_insn_state_x86(struct type_state *state, struct data_loc_info *dloc, Dwarf_Die *cu_die, struct disasm_line *dl) @@ -222,24 +232,40 @@ static void update_insn_state_x86(struct type_state *state, if (ins__is_call(&dl->ins)) { struct symbol *func = dl->ops.target.sym; + const char *call_name; + u64 call_addr; - if (func == NULL) - return; + /* Try to resolve the call target name */ + if (func) + call_name = func->name; + else + call_name = dl->ops.target.name; /* __fentry__ will preserve all registers */ - if (!strcmp(func->name, "__fentry__")) + if (call_name && !strcmp(call_name, "__fentry__")) return; - pr_debug_dtp("call [%x] %s\n", insn_offset, func->name); + if (call_name) + pr_debug_dtp("call [%x] %s\n", insn_offset, call_name); + else + pr_debug_dtp("call [%x] \n", insn_offset); - /* Otherwise invalidate caller-saved registers after call */ + /* Invalidate caller-saved registers after call */ + call_addr = map__rip_2objdump(dloc->ms->map, + dloc->ms->sym->start + dl->al.offset); for (unsigned i = 0; i < ARRAY_SIZE(state->regs); i++) { - if (state->regs[i].caller_saved) - state->regs[i].ok = false; + struct type_state_reg *reg = &state->regs[i]; + + if (!reg->caller_saved) + continue; + /* Keep register valid within DWARF location lifetime */ + if (reg->lifetime_active && call_addr < reg->lifetime_end) + continue; + invalidate_reg_state(reg); } /* Update register with the return type (if any) */ - if (die_find_func_rettype(cu_die, func->name, &type_die)) { + if (call_name && die_find_func_rettype(cu_die, call_name, &type_die)) { tsr = &state->regs[state->ret_reg]; tsr->type = type_die; tsr->kind = TSR_KIND_TYPE; @@ -265,6 +291,8 @@ static void update_insn_state_x86(struct type_state *state, tsr = &state->regs[dst->reg1]; tsr->copied_from = -1; + tsr->lifetime_active = false; + tsr->lifetime_end = 0; if (src->imm) imm_value = src->offset; @@ -330,6 +358,8 @@ static void update_insn_state_x86(struct type_state *state, tsr = &state->regs[dst->reg1]; tsr->copied_from = -1; + tsr->lifetime_active = false; + tsr->lifetime_end = 0; if (src->imm) imm_value = src->offset; @@ -364,8 +394,7 @@ static void update_insn_state_x86(struct type_state *state, src_tsr = state->regs[sreg]; tsr = &state->regs[dst->reg1]; - tsr->copied_from = -1; - tsr->ok = false; + invalidate_reg_state(tsr); /* Case 1: Based on stack pointer or frame pointer */ if (sreg == fbreg || sreg == state->stack_reg) { @@ -433,8 +462,7 @@ static void update_insn_state_x86(struct type_state *state, !strncmp(dl->ins.name, "inc", 3) || !strncmp(dl->ins.name, "dec", 3)) { pr_debug_dtp("%s [%x] invalidate reg%d\n", dl->ins.name, insn_offset, dst->reg1); - state->regs[dst->reg1].ok = false; - state->regs[dst->reg1].copied_from = -1; + invalidate_reg_state(&state->regs[dst->reg1]); return; } @@ -446,6 +474,8 @@ static void update_insn_state_x86(struct type_state *state, state->regs[dst->reg1].kind = TSR_KIND_CONST; state->regs[dst->reg1].imm_value = 0; state->regs[dst->reg1].ok = true; + state->regs[dst->reg1].lifetime_active = false; + state->regs[dst->reg1].lifetime_end = 0; state->regs[dst->reg1].copied_from = -1; return; } @@ -496,7 +526,7 @@ static void update_insn_state_x86(struct type_state *state, if (!get_global_var_type(cu_die, dloc, ip, var_addr, &offset, &type_die) || !die_get_member_type(&type_die, offset, &type_die)) { - tsr->ok = false; + invalidate_reg_state(tsr); return; } @@ -524,7 +554,7 @@ static void update_insn_state_x86(struct type_state *state, if (!has_reg_type(state, src->reg1) || !state->regs[src->reg1].ok) { - tsr->ok = false; + invalidate_reg_state(tsr); return; } @@ -532,6 +562,8 @@ static void update_insn_state_x86(struct type_state *state, tsr->kind = state->regs[src->reg1].kind; tsr->imm_value = state->regs[src->reg1].imm_value; tsr->offset = state->regs[src->reg1].offset; + tsr->lifetime_active = state->regs[src->reg1].lifetime_active; + tsr->lifetime_end = state->regs[src->reg1].lifetime_end; tsr->ok = true; /* To copy back the variable type later (hopefully) */ @@ -560,7 +592,7 @@ retry: stack = find_stack_state(state, offset); if (stack == NULL) { - tsr->ok = false; + invalidate_reg_state(tsr); return; } else if (!stack->compound) { tsr->type = stack->type; @@ -575,7 +607,7 @@ retry: tsr->offset = 0; tsr->ok = true; } else { - tsr->ok = false; + invalidate_reg_state(tsr); return; } @@ -628,7 +660,7 @@ retry: if (!get_global_var_type(cu_die, dloc, ip, addr, &offset, &type_die) || !die_get_member_type(&type_die, offset, &type_die)) { - tsr->ok = false; + invalidate_reg_state(tsr); return; } @@ -679,7 +711,7 @@ retry: } pr_debug_type_name(&tsr->type, tsr->kind); } else { - tsr->ok = false; + invalidate_reg_state(tsr); } } /* And then dereference the calculated pointer if it has one */ @@ -721,7 +753,7 @@ retry: } } - tsr->ok = false; + invalidate_reg_state(tsr); } } /* Case 3. register to memory transfers */ diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 44fbd41e3845..1eff0a27237d 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -455,13 +455,6 @@ static const char *match_result_str(enum type_match_result tmr) } } -static bool is_pointer_type(Dwarf_Die *type_die) -{ - int tag = dwarf_tag(type_die); - - return tag == DW_TAG_pointer_type || tag == DW_TAG_array_type; -} - static bool is_compound_type(Dwarf_Die *type_die) { int tag = dwarf_tag(type_die); @@ -474,19 +467,24 @@ static bool is_better_type(Dwarf_Die *type_a, Dwarf_Die *type_b) { Dwarf_Word size_a, size_b; Dwarf_Die die_a, die_b; + Dwarf_Die ptr_a, ptr_b; + Dwarf_Die *ptr_type_a, *ptr_type_b; + + ptr_type_a = die_get_pointer_type(type_a, &ptr_a); + ptr_type_b = die_get_pointer_type(type_b, &ptr_b); /* pointer type is preferred */ - if (is_pointer_type(type_a) != is_pointer_type(type_b)) - return is_pointer_type(type_b); + if ((ptr_type_a != NULL) != (ptr_type_b != NULL)) + return ptr_type_b != NULL; - if (is_pointer_type(type_b)) { + if (ptr_type_b) { /* * We want to compare the target type, but 'void *' can fail to * get the target type. */ - if (die_get_real_type(type_a, &die_a) == NULL) + if (die_get_real_type(ptr_type_a, &die_a) == NULL) return true; - if (die_get_real_type(type_b, &die_b) == NULL) + if (die_get_real_type(ptr_type_b, &die_b) == NULL) return false; type_a = &die_a; @@ -539,7 +537,7 @@ static enum type_match_result check_variable(struct data_loc_info *dloc, * and local variables are accessed directly without a pointer. */ if (needs_pointer) { - if (!is_pointer_type(type_die) || + if (die_get_pointer_type(type_die, type_die) == NULL || __die_get_real_type(type_die, type_die) == NULL) return PERF_TMR_NO_POINTER; } @@ -776,12 +774,7 @@ static void global_var__collect(struct data_loc_info *dloc) if (!dwarf_offdie(dwarf, pos->die_off, &type_die)) continue; - if (!get_global_var_info(dloc, pos->addr, &var_name, - &var_offset)) - continue; - - if (var_offset != 0) - continue; + get_global_var_info(dloc, pos->addr, &var_name, &var_offset); global_var__add(dloc, pos->addr, var_name, &type_die); } @@ -816,9 +809,8 @@ bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc, } /* Try to get the variable by address first */ - if (die_find_variable_by_addr(cu_die, var_addr, &var_die, &offset) && - check_variable(dloc, &var_die, type_die, DWARF_REG_PC, offset, - /*is_fbreg=*/false) == PERF_TMR_OK) { + if (die_find_variable_by_addr(cu_die, var_addr, &var_die, type_die, + &offset)) { var_name = dwarf_diename(&var_die); *var_offset = offset; goto ok; @@ -848,6 +840,18 @@ static bool die_is_same(Dwarf_Die *die_a, Dwarf_Die *die_b) return (die_a->cu == die_b->cu) && (die_a->addr == die_b->addr); } +static void tsr_set_lifetime(struct type_state_reg *tsr, + const struct die_var_type *var) +{ + if (var && var->has_range && var->end > var->addr) { + tsr->lifetime_active = true; + tsr->lifetime_end = var->end; + } else { + tsr->lifetime_active = false; + tsr->lifetime_end = 0; + } +} + /** * update_var_state - Update type state using given variables * @state: type state table @@ -873,19 +877,29 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo } for (var = var_types; var != NULL; var = var->next) { - if (var->addr != addr) - continue; + /* Check if addr falls within the variable's valid range */ + if (var->has_range) { + if (addr < var->addr || (var->end && addr >= var->end)) + continue; + } else { + if (addr != var->addr) + continue; + } /* Get the type DIE using the offset */ if (!dwarf_offdie(dloc->di->dbg, var->die_off, &mem_die)) continue; if (var->reg == DWARF_REG_FB || var->reg == fbreg || var->reg == state->stack_reg) { + Dwarf_Die ptr_die; + Dwarf_Die *ptr_type; int offset = var->offset; struct type_state_stack *stack; + ptr_type = die_get_pointer_type(&mem_die, &ptr_die); + /* If the reg location holds the pointer value, dereference the type */ - if (!var->is_reg_var_addr && is_pointer_type(&mem_die) && - __die_get_real_type(&mem_die, &mem_die) == NULL) + if (!var->is_reg_var_addr && ptr_type && + __die_get_real_type(ptr_type, &mem_die) == NULL) continue; if (var->reg != DWARF_REG_FB) @@ -927,6 +941,7 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo reg->type = mem_die; reg->kind = TSR_KIND_POINTER; reg->ok = true; + tsr_set_lifetime(reg, var); pr_debug_dtp("var [%"PRIx64"] reg%d addr offset %x", insn_offset, var->reg, var->offset); @@ -943,6 +958,7 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo reg->type = mem_die; reg->kind = TSR_KIND_TYPE; reg->ok = true; + tsr_set_lifetime(reg, var); pr_debug_dtp("var [%"PRIx64"] reg%d offset %x", insn_offset, var->reg, var->offset); @@ -1110,7 +1126,9 @@ again: goto check_non_register; if (state->regs[reg].kind == TSR_KIND_TYPE) { + Dwarf_Die ptr_die; Dwarf_Die sized_type; + Dwarf_Die *ptr_type; struct strbuf sb; strbuf_init(&sb, 32); @@ -1122,7 +1140,8 @@ again: * Normal registers should hold a pointer (or array) to * dereference a memory location. */ - if (!is_pointer_type(&state->regs[reg].type)) { + ptr_type = die_get_pointer_type(&state->regs[reg].type, &ptr_die); + if (!ptr_type) { if (dloc->op->offset < 0 && reg != state->stack_reg) goto check_kernel; @@ -1130,7 +1149,7 @@ again: } /* Remove the pointer and get the target type */ - if (__die_get_real_type(&state->regs[reg].type, type_die) == NULL) + if (__die_get_real_type(ptr_type, type_die) == NULL) return PERF_TMR_NO_POINTER; dloc->type_offset = dloc->op->offset + state->regs[reg].offset; @@ -1230,6 +1249,11 @@ again: return PERF_TMR_BAIL_OUT; } + if (state->regs[reg].kind == TSR_KIND_CONST && + dso__kernel(map__dso(dloc->ms->map))) { + if (dloc->op->offset < 0 && reg != state->stack_reg && reg != dloc->fbreg) + goto check_kernel; + } check_non_register: if (reg == dloc->fbreg || reg == state->stack_reg) { struct type_state_stack *stack; @@ -1601,12 +1625,13 @@ retry: if (reg == DWARF_REG_PC) { if (!die_find_variable_by_addr(&scopes[i], dloc->var_addr, - &var_die, &type_offset)) + &var_die, &mem_die, + &type_offset)) continue; } else { /* Look up variables/parameters in this scope */ if (!die_find_variable_by_reg(&scopes[i], pc, reg, - &type_offset, is_fbreg, &var_die)) + &mem_die, &type_offset, is_fbreg, &var_die)) continue; } @@ -1614,26 +1639,22 @@ retry: dwarf_diename(&var_die), (long)dwarf_dieoffset(&var_die), i+1, nr_scopes, (long)dwarf_dieoffset(&scopes[i])); - /* Found a variable, see if it's correct */ - result = check_variable(dloc, &var_die, &mem_die, reg, type_offset, is_fbreg); - if (result == PERF_TMR_OK) { - if (reg == DWARF_REG_PC) { - pr_debug_dtp("addr=%#"PRIx64" type_offset=%#x\n", - dloc->var_addr, type_offset); - } else if (reg == DWARF_REG_FB || is_fbreg) { - pr_debug_dtp("stack_offset=%#x type_offset=%#x\n", - fb_offset, type_offset); - } else { - pr_debug_dtp("type_offset=%#x\n", type_offset); - } - - if (!found || is_better_type(type_die, &mem_die)) { - *type_die = mem_die; - dloc->type_offset = type_offset; - found = true; - } + if (reg == DWARF_REG_PC) { + pr_debug_dtp("addr=%#"PRIx64" type_offset=%#x\n", + dloc->var_addr, type_offset); + } else if (reg == DWARF_REG_FB || is_fbreg) { + pr_debug_dtp("stack_offset=%#x type_offset=%#x\n", + fb_offset, type_offset); } else { - pr_debug_dtp("failed: %s\n", match_result_str(result)); + pr_debug_dtp("type_offset=%#x\n", type_offset); + } + + if (!found || dloc->type_offset < type_offset || + (dloc->type_offset == type_offset && + !is_better_type(&mem_die, type_die))) { + *type_die = mem_die; + dloc->type_offset = type_offset; + found = true; } pr_debug_location(&var_die, pc, reg); diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h index 9b222869e42d..c26130744260 100644 --- a/tools/perf/util/annotate-data.h +++ b/tools/perf/util/annotate-data.h @@ -182,6 +182,9 @@ struct type_state_reg { s32 offset; bool ok; bool caller_saved; + /* DWARF location range tracking for register lifetime */ + bool lifetime_active; + u64 lifetime_end; u8 kind; u8 copied_from; }; diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 63f0ee9d4c03..e745f3034a0e 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -8,7 +8,6 @@ #include #include -#include #include #include "util.h" // hex_width() #include "ui/ui.h" @@ -1245,7 +1244,7 @@ int hist_entry__annotate_printf(struct hist_entry *he, struct evsel *evsel) if (opts->full_path) d_filename = filename; else - d_filename = basename(filename); + d_filename = perf_basename(filename); if (evsel__is_group_event(evsel)) { evsel__group_desc(evsel, buf, sizeof(buf)); diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index 70dd9bee47c7..e5835042acdf 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -353,12 +353,26 @@ static struct simd_flags arm_spe__synth_simd_flags(const struct arm_spe_record * if (record->op & ARM_SPE_OP_SVE) simd_flags.arch |= SIMD_OP_FLAGS_ARCH_SVE; + else if (record->op & ARM_SPE_OP_SME) + simd_flags.arch |= SIMD_OP_FLAGS_ARCH_SME; + else if (record->op & (ARM_SPE_OP_ASE | ARM_SPE_OP_SIMD_FP)) + simd_flags.arch |= SIMD_OP_FLAGS_ARCH_ASE; - if (record->type & ARM_SPE_SVE_PARTIAL_PRED) - simd_flags.pred |= SIMD_OP_FLAGS_PRED_PARTIAL; - - if (record->type & ARM_SPE_SVE_EMPTY_PRED) - simd_flags.pred |= SIMD_OP_FLAGS_PRED_EMPTY; + if (record->op & ARM_SPE_OP_SVE) { + if (!(record->op & ARM_SPE_OP_PRED)) + simd_flags.pred = SIMD_OP_FLAGS_PRED_DISABLED; + else if (record->type & ARM_SPE_SVE_PARTIAL_PRED) + simd_flags.pred = SIMD_OP_FLAGS_PRED_PARTIAL; + else if (record->type & ARM_SPE_SVE_EMPTY_PRED) + simd_flags.pred = SIMD_OP_FLAGS_PRED_EMPTY; + else + simd_flags.pred = SIMD_OP_FLAGS_PRED_FULL; + } else { + if (record->type & ARM_SPE_SVE_PARTIAL_PRED) + simd_flags.pred = SIMD_OP_FLAGS_PRED_PARTIAL; + else if (record->type & ARM_SPE_SVE_EMPTY_PRED) + simd_flags.pred = SIMD_OP_FLAGS_PRED_EMPTY; + } return simd_flags; } diff --git a/tools/perf/util/block-info.c b/tools/perf/util/block-info.c index 649392bee7ed..8d3a9a661f26 100644 --- a/tools/perf/util/block-info.c +++ b/tools/perf/util/block-info.c @@ -303,7 +303,7 @@ static int block_range_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, char buf[128]; char *start_line, *end_line; - symbol_conf.disable_add2line_warn = true; + symbol_conf.addr2line_disable_warn = true; start_line = map__srcline(he->ms.map, bi->sym->start + bi->start, he->ms.sym); diff --git a/tools/perf/util/block-range.c b/tools/perf/util/block-range.c index 15c42196c24c..7c559fcfd7e0 100644 --- a/tools/perf/util/block-range.c +++ b/tools/perf/util/block-range.c @@ -4,7 +4,7 @@ #include #include -struct { +static struct { struct rb_root root; u64 blocks; } block_ranges; diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 67e7786bb878..a27945c279ef 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -349,7 +349,7 @@ static struct bpf_metadata *bpf_metadata_alloc(__u32 nr_prog_tags, if (!metadata) return NULL; - metadata->prog_names = zalloc(nr_prog_tags * sizeof(char *)); + metadata->prog_names = calloc(nr_prog_tags, sizeof(char *)); if (!metadata->prog_names) { bpf_metadata_free(metadata); return NULL; diff --git a/tools/perf/util/bpf_counter.c b/tools/perf/util/bpf_counter.c index a5882b582205..34b6b0da18b7 100644 --- a/tools/perf/util/bpf_counter.c +++ b/tools/perf/util/bpf_counter.c @@ -294,7 +294,8 @@ static int bpf_program_profiler__read(struct evsel *evsel) struct perf_counts_values *counts; int reading_map_fd; __u32 key = 0; - int err, idx, bpf_cpu; + int err, bpf_cpu; + unsigned int idx; if (list_empty(&evsel->bpf_counter_list)) return -EAGAIN; @@ -318,11 +319,12 @@ static int bpf_program_profiler__read(struct evsel *evsel) } for (bpf_cpu = 0; bpf_cpu < num_cpu_bpf; bpf_cpu++) { - idx = perf_cpu_map__idx(evsel__cpus(evsel), - (struct perf_cpu){.cpu = bpf_cpu}); - if (idx == -1) + int i = perf_cpu_map__idx(evsel__cpus(evsel), + (struct perf_cpu){.cpu = bpf_cpu}); + + if (i == -1) continue; - counts = perf_counts(evsel->counts, idx, 0); + counts = perf_counts(evsel->counts, i, 0); counts->val += values[bpf_cpu].counter; counts->ena += values[bpf_cpu].enabled; counts->run += values[bpf_cpu].running; @@ -351,7 +353,7 @@ static int bpf_program_profiler__install_pe(struct evsel *evsel, int cpu_map_idx return 0; } -struct bpf_counter_ops bpf_program_profiler_ops = { +static struct bpf_counter_ops bpf_program_profiler_ops = { .load = bpf_program_profiler__load, .enable = bpf_program_profiler__enable, .disable = bpf_program_profiler__disable, @@ -668,7 +670,7 @@ static int bperf__install_pe(struct evsel *evsel, int cpu_map_idx, int fd) static int bperf_sync_counters(struct evsel *evsel) { struct perf_cpu cpu; - int idx; + unsigned int idx; perf_cpu_map__for_each_cpu(cpu, idx, evsel->core.cpus) bperf_trigger_reading(evsel->bperf_leader_prog_fd, cpu.cpu); @@ -695,13 +697,11 @@ static int bperf__read(struct evsel *evsel) struct bpf_perf_event_value values[num_cpu_bpf]; struct perf_counts_values *counts; int reading_map_fd, err = 0; - __u32 i; - int j; bperf_sync_counters(evsel); reading_map_fd = bpf_map__fd(skel->maps.accum_readings); - for (i = 0; i < filter_entry_cnt; i++) { + for (__u32 i = 0; i < filter_entry_cnt; i++) { struct perf_cpu entry; __u32 cpu; @@ -709,9 +709,10 @@ static int bperf__read(struct evsel *evsel) if (err) goto out; switch (evsel->follower_skel->bss->type) { - case BPERF_FILTER_GLOBAL: - assert(i == 0); + case BPERF_FILTER_GLOBAL: { + unsigned int j; + assert(i == 0); perf_cpu_map__for_each_cpu(entry, j, evsel__cpus(evsel)) { counts = perf_counts(evsel->counts, j, 0); counts->val = values[entry.cpu].counter; @@ -719,6 +720,7 @@ static int bperf__read(struct evsel *evsel) counts->run = values[entry.cpu].running; } break; + } case BPERF_FILTER_CPU: cpu = perf_cpu_map__cpu(evsel__cpus(evsel), i).cpu; assert(cpu >= 0); @@ -831,7 +833,7 @@ static int bperf__destroy(struct evsel *evsel) * the leader prog. */ -struct bpf_counter_ops bperf_ops = { +static struct bpf_counter_ops bperf_ops = { .load = bperf__load, .enable = bperf__enable, .disable = bperf__disable, diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_counter_cgroup.c index 17d7196c6589..519fee3dc3d0 100644 --- a/tools/perf/util/bpf_counter_cgroup.c +++ b/tools/perf/util/bpf_counter_cgroup.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -98,7 +97,7 @@ static int bperf_load_program(struct evlist *evlist) struct bpf_link *link; struct evsel *evsel; struct cgroup *cgrp, *leader_cgrp; - int i, j; + unsigned int i; struct perf_cpu cpu; int total_cpus = cpu__max_cpu().cpu; int map_fd, prog_fd, err; @@ -146,6 +145,8 @@ static int bperf_load_program(struct evlist *evlist) evlist__for_each_entry(evlist, evsel) { if (cgrp == NULL || evsel->cgrp == leader_cgrp) { + unsigned int j; + leader_cgrp = evsel->cgrp; evsel->cgrp = NULL; @@ -234,7 +235,7 @@ static int bperf_cgrp__install_pe(struct evsel *evsel __maybe_unused, static int bperf_cgrp__sync_counters(struct evlist *evlist) { struct perf_cpu cpu; - int idx; + unsigned int idx; int prog_fd = bpf_program__fd(skel->progs.trigger_read); perf_cpu_map__for_each_cpu(cpu, idx, evlist->core.all_cpus) @@ -286,7 +287,7 @@ static int bperf_cgrp__read(struct evsel *evsel) evlist__for_each_entry(evlist, evsel) { __u32 idx = evsel->core.idx; - int i; + unsigned int i; struct perf_cpu cpu; err = bpf_map_lookup_elem(reading_map_fd, &idx, values); diff --git a/tools/perf/util/bpf_kwork.c b/tools/perf/util/bpf_kwork.c index 5cff755c71fa..d3a2e548f2b6 100644 --- a/tools/perf/util/bpf_kwork.c +++ b/tools/perf/util/bpf_kwork.c @@ -148,7 +148,8 @@ static bool valid_kwork_class_type(enum kwork_class_type type) static int setup_filters(struct perf_kwork *kwork) { if (kwork->cpu_list != NULL) { - int idx, nr_cpus; + unsigned int idx; + int nr_cpus; struct perf_cpu_map *map; struct perf_cpu cpu; int fd = bpf_map__fd(skel->maps.perf_kwork_cpu_filter); diff --git a/tools/perf/util/bpf_kwork_top.c b/tools/perf/util/bpf_kwork_top.c index b6f187dd9136..189a29d2bc96 100644 --- a/tools/perf/util/bpf_kwork_top.c +++ b/tools/perf/util/bpf_kwork_top.c @@ -123,7 +123,8 @@ static bool valid_kwork_class_type(enum kwork_class_type type) static int setup_filters(struct perf_kwork *kwork) { if (kwork->cpu_list) { - int idx, nr_cpus, fd; + unsigned int idx; + int nr_cpus, fd; struct perf_cpu_map *map; struct perf_cpu cpu; diff --git a/tools/perf/util/bpf_map.c b/tools/perf/util/bpf_map.c deleted file mode 100644 index 442f91b4e8e1..000000000000 --- a/tools/perf/util/bpf_map.c +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) - -#include "util/bpf_map.h" -#include -#include -#include -#include -#include -#include -#include -#include - -static bool bpf_map__is_per_cpu(enum bpf_map_type type) -{ - return type == BPF_MAP_TYPE_PERCPU_HASH || - type == BPF_MAP_TYPE_PERCPU_ARRAY || - type == BPF_MAP_TYPE_LRU_PERCPU_HASH || - type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE; -} - -static void *bpf_map__alloc_value(const struct bpf_map *map) -{ - if (bpf_map__is_per_cpu(bpf_map__type(map))) - return malloc(round_up(bpf_map__value_size(map), 8) * - sysconf(_SC_NPROCESSORS_CONF)); - - return malloc(bpf_map__value_size(map)); -} - -int bpf_map__fprintf(struct bpf_map *map, FILE *fp) -{ - void *prev_key = NULL, *key, *value; - int fd = bpf_map__fd(map), err; - int printed = 0; - - if (fd < 0) - return fd; - - err = -ENOMEM; - key = malloc(bpf_map__key_size(map)); - if (key == NULL) - goto out; - - value = bpf_map__alloc_value(map); - if (value == NULL) - goto out_free_key; - - while ((err = bpf_map_get_next_key(fd, prev_key, key) == 0)) { - int intkey = *(int *)key; - - if (!bpf_map_lookup_elem(fd, key, value)) { - bool boolval = *(bool *)value; - if (boolval) - printed += fprintf(fp, "[%d] = %d,\n", intkey, boolval); - } else { - printed += fprintf(fp, "[%d] = ERROR,\n", intkey); - } - - prev_key = key; - } - - if (err == ENOENT) - err = printed; - - free(value); -out_free_key: - free(key); -out: - return err; -} diff --git a/tools/perf/util/bpf_map.h b/tools/perf/util/bpf_map.h deleted file mode 100644 index c2f7c13cba23..000000000000 --- a/tools/perf/util/bpf_map.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -#ifndef __PERF_BPF_MAP_H -#define __PERF_BPF_MAP_H 1 - -#include -struct bpf_map; - -#ifdef HAVE_LIBBPF_SUPPORT - -int bpf_map__fprintf(struct bpf_map *map, FILE *fp); - -#else - -#include - -static inline int bpf_map__fprintf(struct bpf_map *map __maybe_unused, FILE *fp __maybe_unused) -{ - return 0; -} - -#endif // HAVE_LIBBPF_SUPPORT - -#endif // __PERF_BPF_MAP_H diff --git a/tools/perf/util/bpf_off_cpu.c b/tools/perf/util/bpf_off_cpu.c index 88e0660c4bff..a3b699a5322f 100644 --- a/tools/perf/util/bpf_off_cpu.c +++ b/tools/perf/util/bpf_off_cpu.c @@ -39,7 +39,7 @@ union off_cpu_data { u64 array[1024 / sizeof(u64)]; }; -u64 off_cpu_raw[MAX_STACKS + 5]; +static u64 off_cpu_raw[MAX_STACKS + 5]; static int off_cpu_config(struct evlist *evlist) { @@ -67,7 +67,7 @@ static void off_cpu_start(void *arg) struct evlist *evlist = arg; struct evsel *evsel; struct perf_cpu pcpu; - int i; + unsigned int i; /* update task filter for the given workload */ if (skel->rodata->has_task && skel->rodata->uses_tgid && diff --git a/tools/perf/util/bpf_skel/syscall_summary.bpf.c b/tools/perf/util/bpf_skel/syscall_summary.bpf.c index 1bcd066a5199..4172f3c9fc48 100644 --- a/tools/perf/util/bpf_skel/syscall_summary.bpf.c +++ b/tools/perf/util/bpf_skel/syscall_summary.bpf.c @@ -118,13 +118,11 @@ int sys_enter(u64 *ctx) return 0; } -SEC("tp_btf/sys_exit") -int sys_exit(u64 *ctx) +static int do_exit(long ret) { int tid; int key = 0; u64 cgroup = 0; - long ret = ctx[1]; /* return value of the syscall */ struct syscall_trace *st; s64 delta; @@ -150,4 +148,18 @@ int sys_exit(u64 *ctx) return 0; } +SEC("tp_btf/sys_exit") +int sys_exit(u64 *ctx) +{ + long ret = ctx[1]; /* return value of the syscall */ + + return do_exit(ret); +} + +SEC("tp_btf/sched_process_exit") +int process_exit(u64 *ctx) +{ + return do_exit(0); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/perf/util/bpf_trace_augment.c b/tools/perf/util/bpf_trace_augment.c index 56ed17534caa..9e706f0fa53d 100644 --- a/tools/perf/util/bpf_trace_augment.c +++ b/tools/perf/util/bpf_trace_augment.c @@ -60,7 +60,7 @@ int augmented_syscalls__create_bpf_output(struct evlist *evlist) void augmented_syscalls__setup_bpf_output(void) { struct perf_cpu cpu; - int i; + unsigned int i; if (bpf_output == NULL) return; diff --git a/tools/perf/util/branch.h b/tools/perf/util/branch.h index 7429530fa774..a1d4736497c4 100644 --- a/tools/perf/util/branch.h +++ b/tools/perf/util/branch.h @@ -66,6 +66,9 @@ static inline struct branch_entry *perf_sample__branch_entries(struct perf_sampl { u64 *entry = (u64 *)sample->branch_stack; + if (entry == NULL) + return NULL; + entry++; if (sample->no_hw_idx) return (struct branch_entry *)entry; diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 8ff0898799ee..f031cbbeeba8 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -30,6 +30,7 @@ #include "map.h" #include "callchain.h" #include "branch.h" +#include "record.h" #include "symbol.h" #include "thread.h" #include "util.h" @@ -170,7 +171,7 @@ static int get_stack_size(const char *str, unsigned long *_size) static int __parse_callchain_report_opt(const char *arg, bool allow_record_opt) { - char *tok; + char *tok, *arg_copy; char *endptr, *saveptr = NULL; bool minpcnt_set = false; bool record_opt_set = false; @@ -182,12 +183,17 @@ __parse_callchain_report_opt(const char *arg, bool allow_record_opt) if (!arg) return 0; - while ((tok = strtok_r((char *)arg, ",", &saveptr)) != NULL) { + arg_copy = strdup(arg); + if (!arg_copy) + return -ENOMEM; + + tok = strtok_r(arg_copy, ",", &saveptr); + while (tok) { if (!strncmp(tok, "none", strlen(tok))) { callchain_param.mode = CHAIN_NONE; callchain_param.enabled = false; symbol_conf.use_callchain = false; - return 0; + goto out; } if (!parse_callchain_mode(tok) || @@ -214,30 +220,35 @@ try_numbers: unsigned long size = 0; if (get_stack_size(tok, &size) < 0) - return -1; + goto err_out; callchain_param.dump_size = size; try_stack_size = false; } else if (!minpcnt_set) { /* try to get the min percent */ callchain_param.min_percent = strtod(tok, &endptr); if (tok == endptr) - return -1; + goto err_out; minpcnt_set = true; } else { /* try print limit at last */ callchain_param.print_limit = strtoul(tok, &endptr, 0); if (tok == endptr) - return -1; + goto err_out; } next: - arg = NULL; + tok = strtok_r(NULL, ",", &saveptr); } if (callchain_register_param(&callchain_param) < 0) { pr_err("Can't register callchain params\n"); - return -1; + goto err_out; } +out: + free(arg_copy); return 0; +err_out: + free(arg_copy); + return -1; } int parse_callchain_report_opt(const char *arg) @@ -257,14 +268,12 @@ int parse_callchain_record(const char *arg, struct callchain_param *param) int ret = -1; /* We need buffer that we know we can write to. */ - buf = malloc(strlen(arg) + 1); + buf = strdup(arg); if (!buf) return -ENOMEM; - strcpy(buf, arg); - - tok = strtok_r((char *)buf, ",", &saveptr); - name = tok ? : (char *)buf; + tok = strtok_r(buf, ",", &saveptr); + name = tok ? : buf; do { /* Framepointer style */ @@ -328,6 +337,44 @@ int parse_callchain_record(const char *arg, struct callchain_param *param) return ret; } +static void callchain_debug(const struct callchain_param *callchain) +{ + static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF", "LBR" }; + + pr_debug("callchain: type %s\n", str[callchain->record_mode]); + + if (callchain->record_mode == CALLCHAIN_DWARF) + pr_debug("callchain: stack dump size %d\n", + callchain->dump_size); +} + +int record_opts__parse_callchain(struct record_opts *record, + struct callchain_param *callchain, + const char *arg, bool unset) +{ + int ret; + + callchain->enabled = !unset; + + /* --no-call-graph */ + if (unset) { + callchain->record_mode = CALLCHAIN_NONE; + pr_debug("callchain: disabled\n"); + return 0; + } + + ret = parse_callchain_record_opt(arg, callchain); + if (!ret) { + /* Enable data address sampling for DWARF unwind. */ + if (callchain->record_mode == CALLCHAIN_DWARF && + !record->record_data_mmap_set) + record->record_data_mmap = true; + callchain_debug(callchain); + } + + return ret; +} + int perf_callchain_config(const char *var, const char *value) { char *endptr; @@ -1854,16 +1901,19 @@ int sample__merge_deferred_callchain(struct perf_sample *sample_orig, u64 nr_deferred = sample_callchain->callchain->nr; struct ip_callchain *callchain; + if (sample_orig->merged_callchain) { + /* Already merged. */ + return -EINVAL; + } + if (sample_orig->callchain->nr < 2) { sample_orig->deferred_callchain = false; return -EINVAL; } callchain = calloc(1 + nr_orig + nr_deferred, sizeof(u64)); - if (callchain == NULL) { - sample_orig->deferred_callchain = false; + if (callchain == NULL) return -ENOMEM; - } callchain->nr = nr_orig + nr_deferred; /* copy original including PERF_CONTEXT_USER_DEFERRED (but the cookie) */ @@ -1872,6 +1922,7 @@ int sample__merge_deferred_callchain(struct perf_sample *sample_orig, memcpy(&callchain->ips[nr_orig], sample_callchain->callchain->ips, nr_deferred * sizeof(u64)); + sample_orig->merged_callchain = true; sample_orig->callchain = callchain; return 0; } diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index df54ddb8c0cb..06d463ccc7a0 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -9,11 +9,13 @@ struct addr_location; struct evsel; +struct hist_entry; +struct hists; struct ip_callchain; struct map; struct perf_sample; +struct record_opts; struct thread; -struct hists; #define HELP_PAD "\t\t\t\t" @@ -237,14 +239,6 @@ struct callchain_cursor *get_tls_callchain_cursor(void); int callchain_cursor__copy(struct callchain_cursor *dst, struct callchain_cursor *src); -struct option; -struct hist_entry; - -int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); -int record_callchain_opt(const struct option *opt, const char *arg, int unset); - -struct record_opts; - int record_opts__parse_callchain(struct record_opts *record, struct callchain_param *callchain, const char *arg, bool unset); diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 040eb75f0804..1b5664d1481f 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -417,7 +417,6 @@ static bool has_pattern_string(const char *str) int evlist__expand_cgroup(struct evlist *evlist, const char *str, bool open_cgroup) { struct evlist *orig_list, *tmp_list; - struct evsel *pos, *evsel, *leader; struct rblist orig_metric_events; struct cgroup *cgrp = NULL; struct cgroup_name *cn; @@ -452,6 +451,7 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, bool open_cgro goto out_err; list_for_each_entry(cn, &cgroup_list, list) { + struct evsel *pos; char *name; if (!cn->used) @@ -467,21 +467,37 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str, bool open_cgro if (cgrp == NULL) continue; - leader = NULL; + /* copy the list and set to the new cgroup. */ evlist__for_each_entry(orig_list, pos) { - evsel = evsel__clone(/*dest=*/NULL, pos); + struct evsel *evsel = evsel__clone(/*dest=*/NULL, pos); + if (evsel == NULL) goto out_err; + /* stash the copy during the copying. */ + pos->priv = evsel; cgroup__put(evsel->cgrp); evsel->cgrp = cgroup__get(cgrp); - if (evsel__is_group_leader(pos)) - leader = evsel; - evsel__set_leader(evsel, leader); - evlist__add(tmp_list, evsel); } + /* update leader information using stashed pointer to copy. */ + evlist__for_each_entry(orig_list, pos) { + struct evsel *evsel = pos->priv; + + if (evsel__leader(pos)) + evsel__set_leader(evsel, evsel__leader(pos)->priv); + + if (pos->metric_leader) + evsel->metric_leader = pos->metric_leader->priv; + + if (pos->first_wildcard_match) + evsel->first_wildcard_match = pos->first_wildcard_match->priv; + } + /* the stashed copy is no longer used. */ + evlist__for_each_entry(orig_list, pos) + pos->priv = NULL; + /* cgroup__new() has a refcount, release it here */ cgroup__put(cgrp); nr_cgroups++; diff --git a/tools/perf/util/clockid.h b/tools/perf/util/clockid.h index 9b49b4711c76..33dbd8673c1c 100644 --- a/tools/perf/util/clockid.h +++ b/tools/perf/util/clockid.h @@ -1,8 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ - #ifndef __PERF_CLOCKID_H #define __PERF_CLOCKID_H +#include + struct option; int parse_clockid(const struct option *opt, const char *str, int unset); diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 0452fbc6c085..087002fb1b9b 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -19,7 +19,6 @@ #include "util/hist.h" /* perf_hist_config */ #include "util/stat.h" /* perf_stat__set_big_num */ #include "util/evsel.h" /* evsel__hw_names, evsel__use_bpf_counters */ -#include "util/addr2line.h" /* addr2line_timeout_ms */ #include "srcline.h" #include "build-id.h" #include "debug.h" @@ -459,7 +458,10 @@ static int perf_default_core_config(const char *var, const char *value) proc_map_timeout = strtoul(value, NULL, 10); if (!strcmp(var, "core.addr2line-timeout")) - addr2line_timeout_ms = strtoul(value, NULL, 10); + symbol_conf.addr2line_timeout_ms = strtoul(value, NULL, 10); + + if (!strcmp(var, "core.addr2line-disable-warn")) + symbol_conf.addr2line_disable_warn = perf_config_bool(var, value); /* Add other config variables here. */ return 0; diff --git a/tools/perf/util/cpu-set-sched.h b/tools/perf/util/cpu-set-sched.h deleted file mode 100644 index 8cf4e40d322a..000000000000 --- a/tools/perf/util/cpu-set-sched.h +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -// Definitions taken from glibc for use with older systems, same licensing. -#ifndef _CPU_SET_SCHED_PERF_H -#define _CPU_SET_SCHED_PERF_H - -#include -#include - -#ifndef CPU_EQUAL -#ifndef __CPU_EQUAL_S -#if __GNUC_PREREQ (2, 91) -# define __CPU_EQUAL_S(setsize, cpusetp1, cpusetp2) \ - (__builtin_memcmp (cpusetp1, cpusetp2, setsize) == 0) -#else -# define __CPU_EQUAL_S(setsize, cpusetp1, cpusetp2) \ - (__extension__ \ - ({ const __cpu_mask *__arr1 = (cpusetp1)->__bits; \ - const __cpu_mask *__arr2 = (cpusetp2)->__bits; \ - size_t __imax = (setsize) / sizeof (__cpu_mask); \ - size_t __i; \ - for (__i = 0; __i < __imax; ++__i) \ - if (__arr1[__i] != __arr2[__i]) \ - break; \ - __i == __imax; })) -#endif -#endif // __CPU_EQUAL_S - -#define CPU_EQUAL(cpusetp1, cpusetp2) \ - __CPU_EQUAL_S (sizeof (cpu_set_t), cpusetp1, cpusetp2) -#endif // CPU_EQUAL - -#ifndef CPU_OR -#ifndef __CPU_OP_S -#define __CPU_OP_S(setsize, destset, srcset1, srcset2, op) \ - (__extension__ \ - ({ cpu_set_t *__dest = (destset); \ - const __cpu_mask *__arr1 = (srcset1)->__bits; \ - const __cpu_mask *__arr2 = (srcset2)->__bits; \ - size_t __imax = (setsize) / sizeof (__cpu_mask); \ - size_t __i; \ - for (__i = 0; __i < __imax; ++__i) \ - ((__cpu_mask *) __dest->__bits)[__i] = __arr1[__i] op __arr2[__i]; \ - __dest; })) -#endif // __CPU_OP_S - -#define CPU_OR(destset, srcset1, srcset2) \ - __CPU_OP_S (sizeof (cpu_set_t), destset, srcset1, srcset2, |) -#endif // CPU_OR - -#endif // _CPU_SET_SCHED_PERF_H diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index a80845038a5e..11922e1ded84 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -254,7 +254,7 @@ struct cpu_aggr_map *cpu_aggr_map__new(const struct perf_cpu_map *cpus, aggr_cpu_id_get_t get_id, void *data, bool needs_sort) { - int idx; + unsigned int idx; struct perf_cpu cpu; struct cpu_aggr_map *c = cpu_aggr_map__empty_new(perf_cpu_map__nr(cpus)); @@ -280,7 +280,7 @@ struct cpu_aggr_map *cpu_aggr_map__new(const struct perf_cpu_map *cpus, } } /* Trim. */ - if (c->nr != perf_cpu_map__nr(cpus)) { + if (c->nr != (int)perf_cpu_map__nr(cpus)) { struct cpu_aggr_map *trimmed_c = realloc(c, sizeof(struct cpu_aggr_map) + sizeof(struct aggr_cpu_id) * c->nr); @@ -631,9 +631,9 @@ size_t cpu_map__snprint(struct perf_cpu_map *map, char *buf, size_t size) #define COMMA first ? "" : "," - for (i = 0; i < perf_cpu_map__nr(map) + 1; i++) { + for (i = 0; i < (int)perf_cpu_map__nr(map) + 1; i++) { struct perf_cpu cpu = { .cpu = INT16_MAX }; - bool last = i == perf_cpu_map__nr(map); + bool last = i == (int)perf_cpu_map__nr(map); if (!last) cpu = perf_cpu_map__cpu(map, i); @@ -679,7 +679,7 @@ static char hex_char(unsigned char val) size_t cpu_map__snprint_mask(struct perf_cpu_map *map, char *buf, size_t size) { - int idx; + unsigned int idx; char *ptr = buf; unsigned char *bitmap; struct perf_cpu c, last_cpu = perf_cpu_map__max(map); diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c index 8bbeb2dc76fd..e0091804fe98 100644 --- a/tools/perf/util/cputopo.c +++ b/tools/perf/util/cputopo.c @@ -191,7 +191,7 @@ bool cpu_topology__core_wide(const struct cpu_topology *topology, const char *core_cpu_list = topology->core_cpus_list[i]; struct perf_cpu_map *core_cpus = perf_cpu_map__new(core_cpu_list); struct perf_cpu cpu; - int idx; + unsigned int idx; bool has_first, first = true; perf_cpu_map__for_each_cpu(cpu, idx, core_cpus) { diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c index 212f17a3dc72..dee3020ceaa9 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c @@ -22,12 +22,15 @@ /* use raw logging */ #ifdef CS_DEBUG_RAW #define CS_LOG_RAW_FRAMES +#define CS_PKT_MON 1 #ifdef CS_RAW_PACKED #define CS_RAW_DEBUG_FLAGS (OCSD_DFRMTR_UNPACKED_RAW_OUT | \ OCSD_DFRMTR_PACKED_RAW_OUT) #else #define CS_RAW_DEBUG_FLAGS (OCSD_DFRMTR_UNPACKED_RAW_OUT) #endif +#else +#define CS_PKT_MON 0 #endif /* @@ -237,46 +240,24 @@ cs_etm_decoder__init_def_logger_printing(struct cs_etm_decoder_params *d_params, (void *)decoder, cs_etm_decoder__print_str_cb); if (ret != 0) - ret = -1; - - return 0; -} + return -1; #ifdef CS_LOG_RAW_FRAMES -static void -cs_etm_decoder__init_raw_frame_logging(struct cs_etm_decoder_params *d_params, - struct cs_etm_decoder *decoder) -{ - /* Only log these during a --dump operation */ - if (d_params->operation == CS_ETM_OPERATION_PRINT) { - /* set up a library default logger to process the - * raw frame printer we add later - */ - ocsd_def_errlog_init(OCSD_ERR_SEV_ERROR, 1); - - /* no stdout / err / file output */ - ocsd_def_errlog_config_output(C_API_MSGLOGOUT_FLG_NONE, NULL); - - /* set the string CB for the default logger, - * passes strings to perf print logger. - */ - ocsd_def_errlog_set_strprint_cb(decoder->dcd_tree, - (void *)decoder, - cs_etm_decoder__print_str_cb); - + /* + * Only log raw frames if --dump operation and hardware is actually + * generating formatted CoreSight trace frames + */ + if ((d_params->operation == CS_ETM_OPERATION_PRINT) && + (d_params->formatted == true)) { /* use the built in library printer for the raw frames */ - ocsd_dt_set_raw_frame_printer(decoder->dcd_tree, - CS_RAW_DEBUG_FLAGS); + ret = ocsd_dt_set_raw_frame_printer(decoder->dcd_tree, + CS_RAW_DEBUG_FLAGS); + if (ret != 0) + return -1; } -} -#else -static void -cs_etm_decoder__init_raw_frame_logging( - struct cs_etm_decoder_params *d_params __maybe_unused, - struct cs_etm_decoder *decoder __maybe_unused) -{ -} #endif + return 0; +} static ocsd_datapath_resp_t cs_etm_decoder__do_soft_timestamp(struct cs_etm_queue *etmq, @@ -686,7 +667,7 @@ cs_etm_decoder__create_etm_decoder(struct cs_etm_decoder_params *d_params, trace_config, &csid)) return -1; - if (ocsd_dt_set_pkt_protocol_printer(decoder->dcd_tree, csid, 0)) + if (ocsd_dt_set_pkt_protocol_printer(decoder->dcd_tree, csid, CS_PKT_MON)) return -1; return 0; @@ -738,9 +719,6 @@ cs_etm_decoder__new(int decoders, struct cs_etm_decoder_params *d_params, if (ret != 0) goto err_free_decoder; - /* init raw frame logging if required */ - cs_etm_decoder__init_raw_frame_logging(d_params, decoder); - for (i = 0; i < decoders; i++) { ret = cs_etm_decoder__create_etm_decoder(d_params, &t_params[i], diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index ba1c8e48d495..3b8f2df823a9 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -1181,6 +1181,10 @@ static int add_event(struct ctf_writer *cw, struct evsel *evsel) const char *name = evsel__name(evsel); int ret; + if (evsel->priv) { + pr_err("Error: attempt to add already added event %s\n", name); + return -1; + } pr("Adding event '%s' (type %d)\n", name, evsel->core.attr.type); event_class = bt_ctf_event_class_create(name); @@ -1223,13 +1227,28 @@ err: return -1; } -static int setup_events(struct ctf_writer *cw, struct perf_session *session) +enum setup_events_type { + SETUP_EVENTS_ALL, + SETUP_EVENTS_NOT_TRACEPOINT, + SETUP_EVENTS_TRACEPOINT_ONLY, +}; + +static int setup_events(struct ctf_writer *cw, struct perf_session *session, + enum setup_events_type type) { struct evlist *evlist = session->evlist; struct evsel *evsel; int ret; evlist__for_each_entry(evlist, evsel) { + bool is_tracepoint = evsel->core.attr.type == PERF_TYPE_TRACEPOINT; + + if (is_tracepoint && type == SETUP_EVENTS_NOT_TRACEPOINT) + continue; + + if (!is_tracepoint && type == SETUP_EVENTS_TRACEPOINT_ONLY) + continue; + ret = add_event(cw, evsel); if (ret) return ret; @@ -1360,7 +1379,7 @@ static int setup_streams(struct ctf_writer *cw, struct perf_session *session) */ ncpus = env->nr_cpus_avail ?: MAX_CPUS; - stream = zalloc(sizeof(*stream) * ncpus); + stream = calloc(ncpus, sizeof(*stream)); if (!stream) { pr_err("Failed to allocate streams.\n"); return -ENOMEM; @@ -1412,15 +1431,24 @@ static int process_feature_event(const struct perf_tool *tool, struct convert *c = container_of(tool, struct convert, tool); struct ctf_writer *cw = &c->writer; struct perf_record_header_feature *fe = &event->feat; + int ret = perf_event__process_feature(tool, session, event); - if (event->feat.feat_id < HEADER_LAST_FEATURE) { - int ret = perf_event__process_feature(session, event); - - if (ret) - return ret; - } + if (ret) + return ret; switch (fe->feat_id) { + case HEADER_EVENT_DESC: + /* + * In non-pipe mode (not here) the evsels combine the desc with + * the perf_event_attr when it is parsed. In pipe mode the + * perf_event_attr events appear first and then the event desc + * feature events that set the names appear after. Once we have + * the full evsel data we can generate the babeltrace + * events. For tracepoint events we still don't have the tracing + * data and so need to wait until the tracing data event to add + * those events to babeltrace. + */ + return setup_events(cw, session, SETUP_EVENTS_NOT_TRACEPOINT); case HEADER_HOSTNAME: if (session->header.env.hostname) { return bt_ctf_writer_add_environment_field(cw->writer, "host", @@ -1451,6 +1479,26 @@ static int process_feature_event(const struct perf_tool *tool, return 0; } +static int process_tracing_data(const struct perf_tool *tool, + struct perf_session *session, + union perf_event *event) +{ + struct convert *c = container_of(tool, struct convert, tool); + struct ctf_writer *cw = &c->writer; + int ret; + + ret = perf_event__process_tracing_data(tool, session, event); + if (ret < 0) + return ret; + + /* + * Now the attr was set up by the attr event, the name by the feature + * event desc event and the tracepoint data set up above, the tracepoint + * babeltrace events can be added. + */ + return setup_events(cw, session, SETUP_EVENTS_TRACEPOINT_ONLY); +} + static int ctf_writer__setup_clock(struct ctf_writer *cw, struct perf_session *session, bool tod) @@ -1680,9 +1728,10 @@ int bt_convert__perf2ctf(const char *input, const char *path, c.tool.exit = perf_event__process_exit; c.tool.fork = perf_event__process_fork; c.tool.lost = perf_event__process_lost; - c.tool.tracing_data = perf_event__process_tracing_data; + c.tool.tracing_data = process_tracing_data; c.tool.build_id = perf_event__process_build_id; c.tool.namespaces = perf_event__process_namespaces; + c.tool.finished_round = perf_event__process_finished_round; c.tool.attr = perf_event__process_attr; c.tool.feature = process_feature_event; c.tool.ordering_requires_timestamps = true; @@ -1727,8 +1776,11 @@ int bt_convert__perf2ctf(const char *input, const char *path, if (ctf_writer__setup_env(cw, session)) goto free_writer; - /* CTF events setup */ - if (setup_events(cw, session)) + /* + * CTF events setup. Note, in pipe mode no events exist yet (they come + * in via header feature events) and so this does nothing. + */ + if (setup_events(cw, session, SETUP_EVENTS_ALL)) goto free_writer; if (opts->all && setup_non_sample_events(cw, session)) diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c index 6a626322476a..d526c91312ed 100644 --- a/tools/perf/util/data-convert-json.c +++ b/tools/perf/util/data-convert-json.c @@ -326,17 +326,7 @@ static void output_headers(struct perf_session *session, struct convert_json *c) output_json_format(out, false, 2, "]"); } -static int process_feature_event(const struct perf_tool *tool __maybe_unused, - struct perf_session *session, - union perf_event *event) -{ - if (event->feat.feat_id < HEADER_LAST_FEATURE) - return perf_event__process_feature(session, event); - - return 0; -} - -int bt_convert__perf2json(const char *input_name, const char *output_name, +int bt_convert__perf2json(const char *_input_name, const char *output_name, struct perf_data_convert_opts *opts __maybe_unused) { struct perf_session *session; @@ -352,7 +342,7 @@ int bt_convert__perf2json(const char *input_name, const char *output_name, }; struct perf_data data = { .mode = PERF_DATA_MODE_READ, - .path = input_name, + .path = _input_name, .force = opts->force, }; @@ -375,7 +365,7 @@ int bt_convert__perf2json(const char *input_name, const char *output_name, c.tool.auxtrace = perf_event__process_auxtrace; c.tool.event_update = perf_event__process_event_update; c.tool.attr = perf_event__process_attr; - c.tool.feature = process_feature_event; + c.tool.feature = perf_event__process_feature; c.tool.ordering_requires_timestamps = true; if (opts->all) { diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index 90df41da1a32..94dc534a7386 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c @@ -20,18 +20,33 @@ #include "rlimit.h" #include +static void perf_data_file__close(struct perf_data_file *file) +{ + if (file->use_stdio) { + if (file->fptr) { + fclose(file->fptr); + file->fptr = NULL; + } + } else { + close(file->fd); + file->fd = -1; + } + zfree(&file->path); +} + static void close_dir(struct perf_data_file *files, int nr) { - while (--nr >= 0) { - close(files[nr].fd); - zfree(&files[nr].path); - } + while (--nr >= 0) + perf_data_file__close(&files[nr]); + free(files); } void perf_data__close_dir(struct perf_data *data) { close_dir(data->dir.files, data->dir.nr); + data->dir.files = NULL; + data->dir.nr = 0; } int perf_data__create_dir(struct perf_data *data, int nr) @@ -43,7 +58,7 @@ int perf_data__create_dir(struct perf_data *data, int nr) if (WARN_ON(!data->is_dir)) return -EINVAL; - files = zalloc(nr * sizeof(*files)); + files = calloc(nr, sizeof(*files)); if (!files) return -ENOMEM; @@ -132,16 +147,21 @@ int perf_data__open_dir(struct perf_data *data) files = file; file = &files[nr++]; - file->path = strdup(path); + *file = (struct perf_data_file){ + .path = strdup(path), + .fd = -1, + .size = st.st_size, + .use_stdio = false, + }; if (!file->path) goto out_err; ret = open(file->path, O_RDONLY); - if (ret < 0) + if (ret < 0) { + ret = -errno; goto out_err; - + } file->fd = ret; - file->size = st.st_size; } closedir(dir); @@ -174,7 +194,7 @@ static bool check_pipe(struct perf_data *data) } if (is_pipe) { - if (data->use_stdio) { + if (data->file.use_stdio) { const char *mode; mode = perf_data__is_read(data) ? "r" : "w"; @@ -182,7 +202,7 @@ static bool check_pipe(struct perf_data *data) if (data->file.fptr == NULL) { data->file.fd = fd; - data->use_stdio = false; + data->file.use_stdio = false; } /* @@ -344,7 +364,7 @@ int perf_data__open(struct perf_data *data) return 0; /* currently it allows stdio for pipe only */ - data->use_stdio = false; + data->file.use_stdio = false; if (!data->path) data->path = "perf.data"; @@ -364,41 +384,57 @@ void perf_data__close(struct perf_data *data) if (perf_data__is_dir(data)) perf_data__close_dir(data); - zfree(&data->file.path); + perf_data_file__close(&data->file); +} - if (data->use_stdio) - fclose(data->file.fptr); - else - close(data->file.fd); +static ssize_t perf_data_file__read(struct perf_data_file *file, void *buf, size_t size) +{ + if (file->use_stdio) { + if (fread(buf, size, 1, file->fptr) == 1) + return size; + return feof(file->fptr) ? 0 : -1; + } + return readn(file->fd, buf, size); } ssize_t perf_data__read(struct perf_data *data, void *buf, size_t size) { - if (data->use_stdio) { - if (fread(buf, size, 1, data->file.fptr) == 1) - return size; - return feof(data->file.fptr) ? 0 : -1; - } - return readn(data->file.fd, buf, size); + return perf_data_file__read(&data->file, buf, size); } ssize_t perf_data_file__write(struct perf_data_file *file, void *buf, size_t size) { + if (file->use_stdio) { + if (fwrite(buf, size, /*nmemb=*/1, file->fptr) == 1) + return size; + return -1; + } return writen(file->fd, buf, size); } ssize_t perf_data__write(struct perf_data *data, void *buf, size_t size) { - if (data->use_stdio) { - if (fwrite(buf, size, 1, data->file.fptr) == 1) - return size; - return -1; - } return perf_data_file__write(&data->file, buf, size); } +off_t perf_data_file__seek(struct perf_data_file *file, off_t offset, int whence) +{ + if (file->use_stdio) { + off_t res = fseeko(file->fptr, offset, whence); + + return res < 0 ? -1 : ftello(file->fptr); + } + return lseek(file->fd, offset, whence); +} + +off_t perf_data__seek(struct perf_data *data, off_t offset, int whence) +{ + /* Note, a pipe fd will fail with -1 with errno of ESPIPE. */ + return perf_data_file__seek(&data->file, offset, whence); +} + int perf_data__switch(struct perf_data *data, const char *postfix, size_t pos, bool at_exit, @@ -420,19 +456,18 @@ int perf_data__switch(struct perf_data *data, pr_warning("Failed to rename %s to %s\n", data->path, *new_filepath); if (!at_exit) { - close(data->file.fd); + perf_data_file__close(&data->file); ret = perf_data__open(data); if (ret < 0) goto out; - if (lseek(data->file.fd, pos, SEEK_SET) == (off_t)-1) { + if (perf_data__seek(data, pos, SEEK_SET) == (off_t)-1) { ret = -errno; - pr_debug("Failed to lseek to %zu: %m\n", - pos); + pr_debug("Failed to seek to %zu: %m", pos); goto out; } } - ret = data->file.fd; + ret = perf_data__fd(data); out: return ret; } diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h index 1438e32e0451..8299fb5fa7da 100644 --- a/tools/perf/util/data.h +++ b/tools/perf/util/data.h @@ -17,32 +17,70 @@ enum perf_dir_version { PERF_DIR_VERSION = 1, }; +/** + * struct perf_data_file: A wrapper around a file used for perf.data reading or writing. Generally + * part of struct perf_data. + */ struct perf_data_file { + /** + * @path: Path of file. Generally a copy of perf_data.path but for a + * directory it is the file within the directory. + */ char *path; union { + /** @fd: File descriptor for read/writes. Valid if use_stdio is false. */ int fd; + /** + * @fptr: Stdio FILE. Valid if use_stdio is true, currently just + * pipes in perf inject. + */ FILE *fptr; }; + /** @size: Size of file when opened. */ unsigned long size; + /** @use_stdio: Use buffered stdio operations. */ + bool use_stdio; }; +/** + * struct perf_data: A wrapper around a file used for perf.data reading or writing. + */ struct perf_data { + /** @path: Path to open and of the file. NULL implies 'perf.data' will be used. */ const char *path; + /** @file: Underlying file to be used. */ struct perf_data_file file; + /** @is_pipe: Underlying file is a pipe. */ bool is_pipe; + /** @is_dir: Underlying file is a directory. */ bool is_dir; + /** @force: Ignore opening a file creating created by a different user. */ bool force; - bool use_stdio; + /** @in_place_update: A file opened for reading but will be written to. */ bool in_place_update; + /** @mode: Read or write mode. */ enum perf_data_mode mode; struct { + /** @version: perf_dir_version. */ u64 version; + /** @files: perf data files for the directory. */ struct perf_data_file *files; + /** @nr: Number of perf data files for the directory. */ int nr; } dir; }; +static inline int perf_data_file__fd(struct perf_data_file *file) +{ + return file->use_stdio ? fileno(file->fptr) : file->fd; +} + +ssize_t perf_data_file__write(struct perf_data_file *file, + void *buf, size_t size); +off_t perf_data_file__seek(struct perf_data_file *file, off_t offset, int whence); + + static inline bool perf_data__is_read(struct perf_data *data) { return data->mode == PERF_DATA_MODE_READ; @@ -70,10 +108,7 @@ static inline bool perf_data__is_single_file(struct perf_data *data) static inline int perf_data__fd(struct perf_data *data) { - if (data->use_stdio) - return fileno(data->file.fptr); - - return data->file.fd; + return perf_data_file__fd(&data->file); } int perf_data__open(struct perf_data *data); @@ -81,8 +116,7 @@ void perf_data__close(struct perf_data *data); ssize_t perf_data__read(struct perf_data *data, void *buf, size_t size); ssize_t perf_data__write(struct perf_data *data, void *buf, size_t size); -ssize_t perf_data_file__write(struct perf_data_file *file, - void *buf, size_t size); +off_t perf_data__seek(struct perf_data *data, off_t offset, int whence); /* * If at_exit is set, only rename current perf.data to * perf.data., continue write on original data. @@ -99,8 +133,10 @@ int perf_data__open_dir(struct perf_data *data); void perf_data__close_dir(struct perf_data *data); unsigned long perf_data__size(struct perf_data *data); int perf_data__make_kcore_dir(struct perf_data *data, char *buf, size_t buf_sz); -bool has_kcore_dir(const char *path); char *perf_data__kallsyms_name(struct perf_data *data); char *perf_data__guest_kallsyms_name(struct perf_data *data, pid_t machine_pid); + +bool has_kcore_dir(const char *path); bool is_perf_data(const char *path); + #endif /* __PERF_DATA_H */ diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c index ae9a9065aab7..cc2bb1af4243 100644 --- a/tools/perf/util/db-export.c +++ b/tools/perf/util/db-export.c @@ -19,7 +19,6 @@ #include "callchain.h" #include "call-path.h" #include "db-export.h" -#include int db_export__init(struct db_export *dbe) { diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 1dfa4d0eec4d..6b5ffe81f141 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -48,7 +48,7 @@ int debug_ordered_events; static int redirect_to_stderr; int debug_data_convert; static FILE *_debug_file; -bool debug_display_time; +static bool debug_display_time; int debug_type_profile; FILE *debug_file(void) diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c index 4a559b3e8cdc..0e35c13abd04 100644 --- a/tools/perf/util/debuginfo.c +++ b/tools/perf/util/debuginfo.c @@ -88,18 +88,17 @@ static struct debuginfo *__debuginfo__new(const char *path) return dbg; } -enum dso_binary_type distro_dwarf_types[] = { - DSO_BINARY_TYPE__FEDORA_DEBUGINFO, - DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, - DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, - DSO_BINARY_TYPE__BUILDID_DEBUGINFO, - DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO, - DSO_BINARY_TYPE__NOT_FOUND, -}; - struct debuginfo *debuginfo__new(const char *path) { - enum dso_binary_type *type; + static const enum dso_binary_type distro_dwarf_types[] = { + DSO_BINARY_TYPE__FEDORA_DEBUGINFO, + DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, + DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, + DSO_BINARY_TYPE__BUILDID_DEBUGINFO, + DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO, + DSO_BINARY_TYPE__NOT_FOUND, + }; + const enum dso_binary_type *type; char buf[PATH_MAX], nil = '\0'; struct dso *dso; struct debuginfo *dinfo = NULL; diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 9e0420e14be1..59ba88e1f744 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -13,6 +13,7 @@ #include #include +#include #include #include "annotate.h" @@ -451,7 +452,7 @@ int jump__scnprintf(const struct ins *ins, char *bf, size_t size, ops->target.offset); } -static void jump__delete(struct ins_operands *ops __maybe_unused) +void jump__delete(struct ins_operands *ops __maybe_unused) { /* * The ops->jump.raw_comment and ops->jump.raw_func_start belong to the @@ -908,13 +909,14 @@ static void annotation_line__init(struct annotation_line *al, al->offset = args->offset; al->line = strdup(args->line); al->line_nr = args->line_nr; - al->fileloc = args->fileloc; + al->fileloc = args->fileloc ? strdup(args->fileloc) : NULL; al->data_nr = nr; } static void annotation_line__exit(struct annotation_line *al) { zfree_srcline(&al->path); + zfree(&al->fileloc); zfree(&al->line); zfree(&al->cycles); zfree(&al->br_cntr); @@ -950,7 +952,7 @@ struct disasm_line *disasm_line__new(struct annotate_args *args) annotation_line__init(&dl->al, args, nr); if (dl->al.line == NULL) - goto out_delete; + goto out_free_line; if (args->offset != -1) { if (arch__is_powerpc(args->arch)) { @@ -965,8 +967,7 @@ struct disasm_line *disasm_line__new(struct annotate_args *args) return dl; out_free_line: - zfree(&dl->al.line); -out_delete: + annotation_line__exit(&dl->al); free(dl); return NULL; } diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h index a6e478caf61a..25756e3f47e4 100644 --- a/tools/perf/util/disasm.h +++ b/tools/perf/util/disasm.h @@ -161,6 +161,8 @@ int jump__scnprintf(const struct ins *ins, char *bf, size_t size, int mov__scnprintf(const struct ins *ins, char *bf, size_t size, struct ins_operands *ops, int max_ins_name); +void jump__delete(struct ins_operands *ops); + int symbol__disassemble(struct symbol *sym, struct annotate_args *args); char *expand_tabs(char *line, char **storage, size_t *storage_len); diff --git a/tools/perf/util/drm_pmu.c b/tools/perf/util/drm_pmu.c index b48a375e4584..b8badae7015c 100644 --- a/tools/perf/util/drm_pmu.c +++ b/tools/perf/util/drm_pmu.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -129,11 +130,6 @@ static struct drm_pmu *add_drm_pmu(struct list_head *pmus, char *line, size_t li } -static bool starts_with(const char *str, const char *prefix) -{ - return !strncmp(prefix, str, strlen(prefix)); -} - static int add_event(struct drm_pmu_event **events, int *num_events, const char *line, enum drm_pmu_unit unit, const char *desc) { @@ -174,7 +170,7 @@ static int read_drm_pmus_cb(void *args, int fdinfo_dir_fd, const char *fd_name) } while (io__getline(&io, &line, &line_len) > 0) { - if (starts_with(line, "drm-driver:")) { + if (strstarts(line, "drm-driver:")) { drm = add_drm_pmu(pmus, line, line_len); if (!drm) break; @@ -184,59 +180,59 @@ static int read_drm_pmus_cb(void *args, int fdinfo_dir_fd, const char *fd_name) * Note the string matching below is alphabetical, with more * specific matches appearing before less specific. */ - if (starts_with(line, "drm-active-")) { + if (strstarts(line, "drm-active-")) { add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, "Total memory active in one or more engines"); continue; } - if (starts_with(line, "drm-cycles-")) { + if (strstarts(line, "drm-cycles-")) { add_event(&events, &num_events, line, DRM_PMU_UNIT_CYCLES, "Busy cycles"); continue; } - if (starts_with(line, "drm-engine-capacity-")) { + if (strstarts(line, "drm-engine-capacity-")) { add_event(&events, &num_events, line, DRM_PMU_UNIT_CAPACITY, "Engine capacity"); continue; } - if (starts_with(line, "drm-engine-")) { + if (strstarts(line, "drm-engine-")) { add_event(&events, &num_events, line, DRM_PMU_UNIT_NS, "Utilization in ns"); continue; } - if (starts_with(line, "drm-maxfreq-")) { + if (strstarts(line, "drm-maxfreq-")) { add_event(&events, &num_events, line, DRM_PMU_UNIT_HZ, "Maximum frequency"); continue; } - if (starts_with(line, "drm-purgeable-")) { + if (strstarts(line, "drm-purgeable-")) { add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, "Size of resident and purgeable memory buffers"); continue; } - if (starts_with(line, "drm-resident-")) { + if (strstarts(line, "drm-resident-")) { add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, "Size of resident memory buffers"); continue; } - if (starts_with(line, "drm-shared-")) { + if (strstarts(line, "drm-shared-")) { add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, "Size of shared memory buffers"); continue; } - if (starts_with(line, "drm-total-cycles-")) { + if (strstarts(line, "drm-total-cycles-")) { add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, "Total busy cycles"); continue; } - if (starts_with(line, "drm-total-")) { + if (strstarts(line, "drm-total-")) { add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, "Size of shared and private memory"); continue; } - if (verbose > 1 && starts_with(line, "drm-") && - !starts_with(line, "drm-client-id:") && - !starts_with(line, "drm-pdev:")) + if (verbose > 1 && strstarts(line, "drm-") && + !strstarts(line, "drm-client-id:") && + !strstarts(line, "drm-pdev:")) pr_debug("Unhandled DRM PMU fdinfo line match '%s'\n", line); } if (drm) { @@ -261,7 +257,7 @@ bool drm_pmu__have_event(const struct perf_pmu *pmu, const char *name) { struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); - if (!starts_with(name, "drm-")) + if (!strstarts(name, "drm-")) return false; for (int i = 0; i < drm->num_events; i++) { diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c index 0a7645c7fae7..e927e707abac 100644 --- a/tools/perf/util/dsos.c +++ b/tools/perf/util/dsos.c @@ -6,7 +6,6 @@ #include "vdso.h" #include "namespaces.h" #include -#include #include #include #include // filename__read_build_id @@ -196,6 +195,9 @@ static struct dso *__dsos__find_by_longname_id(struct dsos *dsos, int __dsos__add(struct dsos *dsos, struct dso *dso) { + if (!dso) + return -EINVAL; + if (dsos->cnt == dsos->allocated) { unsigned int to_allocate = 2; struct dso **temp; @@ -294,34 +296,21 @@ struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short) static void dso__set_basename(struct dso *dso) { - char *base, *lname; + bool allocated = false; + const char *base; int tid; if (perf_pid_map_tid(dso__long_name(dso), &tid)) { - if (asprintf(&base, "[JIT] tid %d", tid) < 0) + char *jitname; + + if (asprintf(&jitname, "[JIT] tid %d", tid) < 0) return; + allocated = true; + base = jitname; } else { - /* - * basename() may modify path buffer, so we must pass - * a copy. - */ - lname = strdup(dso__long_name(dso)); - if (!lname) - return; - - /* - * basename() may return a pointer to internal - * storage which is reused in subsequent calls - * so copy the result. - */ - base = strdup(basename(lname)); - - free(lname); - - if (!base) - return; + base = perf_basename(dso__long_name(dso)); } - dso__set_short_name(dso, base, true); + dso__set_short_name(dso, base, allocated); } static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, const struct dso_id *id) diff --git a/tools/perf/util/dump-insn.c b/tools/perf/util/dump-insn.c deleted file mode 100644 index c1cc0ade48d0..000000000000 --- a/tools/perf/util/dump-insn.c +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include "dump-insn.h" - -/* Fallback code */ - -__weak -const char *dump_insn(struct perf_insn *x __maybe_unused, - u64 ip __maybe_unused, u8 *inbuf __maybe_unused, - int inlen __maybe_unused, int *lenp) -{ - if (lenp) - *lenp = 0; - return "?"; -} - -__weak -int arch_is_uncond_branch(const unsigned char *buf __maybe_unused, - size_t len __maybe_unused, - int x86_64 __maybe_unused) -{ - return 0; -} diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 9267af204c7d..92db2fccc788 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -303,6 +303,33 @@ Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) return vr_die; } +/** + * die_get_pointer_type - Get a pointer/array type die + * @type_die: a DIE of a type + * @die_mem: where to store a type DIE + * + * Get a pointer/array type DIE from @type_die. If the type is a typedef or + * qualifier (const, volatile, etc.), follow the chain to find the underlying + * pointer type. + */ +Dwarf_Die *die_get_pointer_type(Dwarf_Die *type_die, Dwarf_Die *die_mem) +{ + int tag; + + do { + tag = dwarf_tag(type_die); + if (tag == DW_TAG_pointer_type || tag == DW_TAG_array_type) + return type_die; + if (tag != DW_TAG_typedef && tag != DW_TAG_const_type && + tag != DW_TAG_restrict_type && tag != DW_TAG_volatile_type && + tag != DW_TAG_shared_type) + return NULL; + type_die = die_get_type(type_die, die_mem); + } while (type_die); + + return NULL; +} + /* Get attribute and translate it as a udata */ static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, Dwarf_Word *result) @@ -1378,6 +1405,8 @@ struct find_var_data { Dwarf_Addr addr; /* Target register */ unsigned reg; + /* Access data type */ + Dwarf_Die type; /* Access offset, set for global data */ int offset; /* True if the current register is the frame base */ @@ -1390,29 +1419,31 @@ struct find_var_data { static bool match_var_offset(Dwarf_Die *die_mem, struct find_var_data *data, s64 addr_offset, s64 addr_type, bool is_pointer) { - Dwarf_Die type_die; Dwarf_Word size; + Dwarf_Die ptr_die; + Dwarf_Die *ptr_type; s64 offset = addr_offset - addr_type; + if (offset < 0) + return false; + + if (__die_get_real_type(die_mem, &data->type) == NULL) + return false; + + ptr_type = die_get_pointer_type(&data->type, &ptr_die); + if (is_pointer && ptr_type) { + /* Get the target type of the pointer */ + if (__die_get_real_type(ptr_type, &data->type) == NULL) + return false; + } + if (offset == 0) { /* Update offset relative to the start of the variable */ data->offset = 0; return true; } - if (offset < 0) - return false; - - if (die_get_real_type(die_mem, &type_die) == NULL) - return false; - - if (is_pointer && dwarf_tag(&type_die) == DW_TAG_pointer_type) { - /* Get the target type of the pointer */ - if (die_get_real_type(&type_die, &type_die) == NULL) - return false; - } - - if (dwarf_aggregate_size(&type_die, &size) < 0) + if (dwarf_aggregate_size(&data->type, &size) < 0) return false; if ((u64)offset >= size) @@ -1529,7 +1560,7 @@ static int __die_find_var_reg_cb(Dwarf_Die *die_mem, void *arg) * when the variable is in the stack. */ Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg, - int *poffset, bool is_fbreg, + Dwarf_Die *type_die, int *poffset, bool is_fbreg, Dwarf_Die *die_mem) { struct find_var_data data = { @@ -1541,8 +1572,10 @@ Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg, Dwarf_Die *result; result = die_find_child(sc_die, __die_find_var_reg_cb, &data, die_mem); - if (result) + if (result) { *poffset = data.offset; + *type_die = data.type; + } return result; } @@ -1586,7 +1619,8 @@ static int __die_find_var_addr_cb(Dwarf_Die *die_mem, void *arg) * This is usually for global variables. */ Dwarf_Die *die_find_variable_by_addr(Dwarf_Die *sc_die, Dwarf_Addr addr, - Dwarf_Die *die_mem, int *offset) + Dwarf_Die *die_mem, Dwarf_Die *type_die, + int *offset) { struct find_var_data data = { .addr = addr, @@ -1594,8 +1628,10 @@ Dwarf_Die *die_find_variable_by_addr(Dwarf_Die *sc_die, Dwarf_Addr addr, Dwarf_Die *result; result = die_find_child(sc_die, __die_find_var_addr_cb, &data, die_mem); - if (result) + if (result) { *offset = data.offset; + *type_die = data.type; + } return result; } @@ -1605,10 +1641,11 @@ static int __die_collect_vars_cb(Dwarf_Die *die_mem, void *arg) Dwarf_Die type_die; int tag = dwarf_tag(die_mem); Dwarf_Attribute attr; - Dwarf_Addr base, start, end; + Dwarf_Addr base, start, end = 0; Dwarf_Op *ops; size_t nops; struct die_var_type *vt; + ptrdiff_t off; if (tag != DW_TAG_variable && tag != DW_TAG_formal_parameter) return DIE_FIND_CB_SIBLING; @@ -1616,39 +1653,40 @@ static int __die_collect_vars_cb(Dwarf_Die *die_mem, void *arg) if (dwarf_attr(die_mem, DW_AT_location, &attr) == NULL) return DIE_FIND_CB_SIBLING; - /* - * Only collect the first location as it can reconstruct the - * remaining state by following the instructions. - * start = 0 means it covers the whole range. - */ - if (dwarf_getlocations(&attr, 0, &base, &start, &end, &ops, &nops) <= 0) - return DIE_FIND_CB_SIBLING; - - if (!check_allowed_ops(ops, nops)) - return DIE_FIND_CB_SIBLING; - if (__die_get_real_type(die_mem, &type_die) == NULL) return DIE_FIND_CB_SIBLING; - vt = malloc(sizeof(*vt)); - if (vt == NULL) - return DIE_FIND_CB_END; + /* + * Collect all location entries as variables may have different + * locations across different address ranges. + */ + off = 0; + while ((off = dwarf_getlocations(&attr, off, &base, &start, &end, &ops, &nops)) > 0) { + if (!check_allowed_ops(ops, nops)) + continue; - /* Usually a register holds the value of a variable */ - vt->is_reg_var_addr = false; + vt = malloc(sizeof(*vt)); + if (vt == NULL) + return DIE_FIND_CB_END; - if (((ops->atom >= DW_OP_breg0 && ops->atom <= DW_OP_breg31) || - ops->atom == DW_OP_bregx || ops->atom == DW_OP_fbreg) && - !is_breg_access_indirect(ops, nops)) - /* The register contains an address of the variable. */ - vt->is_reg_var_addr = true; + /* Usually a register holds the value of a variable */ + vt->is_reg_var_addr = false; - vt->die_off = dwarf_dieoffset(&type_die); - vt->addr = start; - vt->reg = reg_from_dwarf_op(ops); - vt->offset = offset_from_dwarf_op(ops); - vt->next = *var_types; - *var_types = vt; + if (((ops->atom >= DW_OP_breg0 && ops->atom <= DW_OP_breg31) || + ops->atom == DW_OP_bregx || ops->atom == DW_OP_fbreg) && + !is_breg_access_indirect(ops, nops)) + /* The register contains an address of the variable. */ + vt->is_reg_var_addr = true; + + vt->die_off = dwarf_dieoffset(&type_die); + vt->addr = start; + vt->end = end; + vt->has_range = (end != 0 || start != 0); + vt->reg = reg_from_dwarf_op(ops); + vt->offset = offset_from_dwarf_op(ops); + vt->next = *var_types; + *var_types = vt; + } return DIE_FIND_CB_SIBLING; } @@ -1707,6 +1745,8 @@ static int __die_collect_global_vars_cb(Dwarf_Die *die_mem, void *arg) vt->die_off = dwarf_dieoffset(&type_die); vt->addr = ops->number; + vt->end = 0; + vt->has_range = false; vt->reg = -1; vt->offset = 0; vt->next = *var_types; @@ -2091,13 +2131,28 @@ Dwarf_Die *die_get_member_type(Dwarf_Die *type_die, int offset, tag = dwarf_tag(&mb_type); - if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type) { + if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type || + tag == DW_TAG_array_type) { Dwarf_Word loc; /* Update offset for the start of the member struct */ if (die_get_data_member_location(member, &loc) == 0) offset -= loc; } + + /* Handle array types: resolve to the element type by one level */ + if (tag == DW_TAG_array_type) { + Dwarf_Word size; + + if (die_get_real_type(&mb_type, &mb_type) == NULL) + return NULL; + + if (dwarf_aggregate_size(&mb_type, &size) < 0) + return NULL; + + offset = offset % size; + tag = dwarf_tag(&mb_type); + } } *die_mem = mb_type; return die_mem; diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index cd481ec9c5a1..a79968a2e573 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -60,6 +60,8 @@ Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); /* Get a type die, but skip qualifiers and typedef */ Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); +/* Get a pointer/array type, following typedefs/qualifiers */ +Dwarf_Die *die_get_pointer_type(Dwarf_Die *type_die, Dwarf_Die *die_mem); /* Check whether the DIE is signed or not */ bool die_is_signed_type(Dwarf_Die *tp_die); @@ -146,10 +148,12 @@ struct die_var_type { struct die_var_type *next; u64 die_off; u64 addr; + u64 end; /* end address of location range */ int reg; int offset; /* Whether the register holds a address to the type */ bool is_reg_var_addr; + bool has_range; /* whether end is valid */ }; /* Return type info of a member at offset */ @@ -163,12 +167,13 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf); /* Find a variable saved in the 'reg' at given address */ Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg, - int *poffset, bool is_fbreg, + Dwarf_Die *type_die, int *poffset, bool is_fbreg, Dwarf_Die *die_mem); /* Find a (global) variable located in the 'addr' */ Dwarf_Die *die_find_variable_by_addr(Dwarf_Die *sc_die, Dwarf_Addr addr, - Dwarf_Die *die_mem, int *offset); + Dwarf_Die *die_mem, Dwarf_Die *type_die, + int *offset); /* Save all variables and parameters in this scope */ void die_collect_vars(Dwarf_Die *sc_die, struct die_var_type **var_types); diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 93d475a80f14..1e54e2c86360 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -718,7 +718,7 @@ int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu) for (i = 0; i < env->nr_numa_nodes; i++) { struct perf_cpu tmp; - int j; + unsigned int j; nn = &env->numa_nodes[i]; perf_cpu_map__for_each_cpu(tmp, j, nn->map) diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index a4501cbca375..c7052ac1f856 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -112,6 +112,7 @@ struct perf_env { struct cpu_cache_level *caches; struct cpu_domain_map **cpu_domain; int caches_cnt; + unsigned int cln_size; u32 comp_ratio; u32 comp_ver; u32 comp_type; diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index bc045fddf7d5..66f4843bb235 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -12,7 +12,6 @@ #include #include /* To get things like MAP_HUGETLB even on older libc headers */ #include -#include #include "cpumap.h" #include "dso.h" #include "event.h" diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 591bdf0b3e2a..ee971d15b3c6 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -13,6 +13,7 @@ #include "util/mmap.h" #include "thread_map.h" #include "target.h" +#include "dwarf-regs.h" #include "evlist.h" #include "evsel.h" #include "record.h" @@ -98,38 +99,47 @@ struct evlist *evlist__new(void) return evlist; } -struct evlist *evlist__new_default(void) +struct evlist *evlist__new_default(const struct target *target, bool sample_callchains) { struct evlist *evlist = evlist__new(); bool can_profile_kernel; struct perf_pmu *pmu = NULL; + struct evsel *evsel; + char buf[256]; + int err; if (!evlist) return NULL; can_profile_kernel = perf_event_paranoid_check(1); - while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { - char buf[256]; - int err; - - snprintf(buf, sizeof(buf), "%s/cycles/%s", pmu->name, + if (EM_HOST == EM_S390 && sample_callchains) { + snprintf(buf, sizeof(buf), "software/%s/%s", + target__has_cpu(target) ? "cpu-clock" : "task-clock", can_profile_kernel ? "P" : "Pu"); err = parse_event(evlist, buf); - if (err) { - evlist__delete(evlist); - return NULL; + if (err) + goto out_err; + } else { + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { + snprintf(buf, sizeof(buf), "%s/cycles/%s", pmu->name, + can_profile_kernel ? "P" : "Pu"); + err = parse_event(evlist, buf); + if (err) + goto out_err; } } + /* If there is only 1 event a sample identifier isn't necessary. */ if (evlist->core.nr_entries > 1) { - struct evsel *evsel; - evlist__for_each_entry(evlist, evsel) evsel__set_sample_id(evsel, /*can_sample_identifier=*/false); } return evlist; +out_err: + evlist__delete(evlist); + return NULL; } struct evlist *evlist__new_dummy(void) @@ -815,9 +825,8 @@ static struct mmap *evlist__alloc_mmap(struct evlist *evlist, bool overwrite) { int i; - struct mmap *map; + struct mmap *map = calloc(evlist->core.nr_mmaps, sizeof(struct mmap)); - map = zalloc(evlist->core.nr_mmaps * sizeof(struct mmap)); if (!map) return NULL; @@ -1622,8 +1631,11 @@ int evlist__parse_sample(struct evlist *evlist, union perf_event *event, struct struct evsel *evsel = evlist__event2evsel(evlist, event); int ret; - if (!evsel) + if (!evsel) { + /* Ensure the sample is okay for perf_sample__exit. */ + perf_sample__init(sample, /*all=*/false); return -EFAULT; + } ret = evsel__parse_sample(evsel, event, sample); if (ret) return ret; diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index d17c3b57a409..e507f5f20ef6 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -104,7 +104,7 @@ struct evsel_str_handler { }; struct evlist *evlist__new(void); -struct evlist *evlist__new_default(void); +struct evlist *evlist__new_default(const struct target *target, bool sample_callchains); struct evlist *evlist__new_dummy(void); void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus, struct perf_thread_map *threads); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f59228c1a39e..2ee87fd84d3e 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -926,7 +926,8 @@ const char *evsel__name(struct evsel *evsel) break; case PERF_TYPE_TRACEPOINT: - scnprintf(bf, sizeof(bf), "%s", "unknown tracepoint"); + scnprintf(bf, sizeof(bf), "unknown tracepoint id=%#"PRIx64, + evsel->core.attr.config); break; case PERF_TYPE_BREAKPOINT: @@ -938,8 +939,8 @@ const char *evsel__name(struct evsel *evsel) break; default: - scnprintf(bf, sizeof(bf), "unknown attr type: %d", - evsel->core.attr.type); + scnprintf(bf, sizeof(bf), "unknown event PMU=%d config=%#"PRIx64, + evsel->core.attr.type, evsel->core.attr.config); break; } @@ -1015,12 +1016,17 @@ uint16_t evsel__e_machine(struct evsel *evsel, uint32_t *e_flags) return perf_session__e_machine(session, e_flags); } -static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *opts, - struct callchain_param *param) +static void __evsel__config_callchain(struct evsel *evsel, const struct record_opts *opts, + const struct callchain_param *param) { bool function = evsel__is_function_event(evsel); struct perf_event_attr *attr = &evsel->core.attr; + if (EM_HOST == EM_S390 && param->record_mode == CALLCHAIN_FP) { + pr_warning_once( + "Framepointer unwinding lacks kernel support. Use '--call-graph dwarf'\n"); + } + evsel__set_sample_bit(evsel, CALLCHAIN); attr->sample_max_stack = param->max_stack; @@ -1080,14 +1086,14 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o attr->defer_callchain = 1; } -void evsel__config_callchain(struct evsel *evsel, struct record_opts *opts, - struct callchain_param *param) +void evsel__config_callchain(struct evsel *evsel, const struct record_opts *opts, + const struct callchain_param *param) { if (param->enabled) return __evsel__config_callchain(evsel, opts, param); } -static void evsel__reset_callgraph(struct evsel *evsel, struct callchain_param *param) +static void evsel__reset_callgraph(struct evsel *evsel, const struct callchain_param *param) { struct perf_event_attr *attr = &evsel->core.attr; @@ -1106,7 +1112,7 @@ static void evsel__reset_callgraph(struct evsel *evsel, struct callchain_param * static void evsel__apply_ratio_to_prev(struct evsel *evsel, struct perf_event_attr *attr, - struct record_opts *opts, + const struct record_opts *opts, const char *buf) { struct perf_event_attr *prev_attr = NULL; @@ -1170,7 +1176,7 @@ static void evsel__apply_ratio_to_prev(struct evsel *evsel, } static void evsel__apply_config_terms(struct evsel *evsel, - struct record_opts *opts, bool track) + const struct record_opts *opts, bool track) { struct evsel_config_term *term; struct list_head *config_terms = &evsel->config_terms; @@ -1445,7 +1451,7 @@ void __weak arch_evsel__apply_ratio_to_prev(struct evsel *evsel __maybe_unused, { } -static void evsel__set_default_freq_period(struct record_opts *opts, +static void evsel__set_default_freq_period(const struct record_opts *opts, struct perf_event_attr *attr) { if (opts->freq) { @@ -1490,8 +1496,8 @@ bool evsel__is_offcpu_event(struct evsel *evsel) * enable/disable events specifically, as there's no * initial traced exec call. */ -void evsel__config(struct evsel *evsel, struct record_opts *opts, - struct callchain_param *callchain) +void evsel__config(struct evsel *evsel, const struct record_opts *opts, + const struct callchain_param *callchain) { struct evsel *leader = evsel__leader(evsel); struct perf_event_attr *attr = &evsel->core.attr; @@ -3067,7 +3073,7 @@ static inline bool overflow(const void *endp, u16 max_size, const void *offset, #define OVERFLOW_CHECK(offset, size, max_size) \ do { \ if (overflow(endp, (max_size), (offset), (size))) \ - return -EFAULT; \ + goto out_efault; \ } while (0) #define OVERFLOW_CHECK_u64(offset) \ @@ -3199,6 +3205,8 @@ static int __set_offcpu_sample(struct perf_sample *data) data->cgroup = *array; return 0; +out_efault: + return -EFAULT; } int evsel__parse_sample(struct evsel *evsel, union perf_event *event, @@ -3217,7 +3225,8 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, */ union u64_swap u; - memset(data, 0, sizeof(*data)); + perf_sample__init(data, /*all=*/true); + data->evsel = evsel; data->cpu = data->pid = data->tid = -1; data->stream_id = data->id = data->time = -1ULL; data->period = evsel->core.attr.sample_period; @@ -3231,25 +3240,26 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, data->callchain = (struct ip_callchain *)&event->callchain_deferred.nr; if (data->callchain->nr > max_callchain_nr) - return -EFAULT; + goto out_efault; data->deferred_cookie = event->callchain_deferred.cookie; if (evsel->core.attr.sample_id_all) perf_evsel__parse_id_sample(evsel, event, data); + return 0; } if (event->header.type != PERF_RECORD_SAMPLE) { - if (!evsel->core.attr.sample_id_all) - return 0; - return perf_evsel__parse_id_sample(evsel, event, data); + if (evsel->core.attr.sample_id_all) + perf_evsel__parse_id_sample(evsel, event, data); + return 0; } array = event->sample.array; if (perf_event__check_size(event, evsel->sample_size)) - return -EFAULT; + goto out_efault; if (type & PERF_SAMPLE_IDENTIFIER) { data->id = *array; @@ -3342,7 +3352,7 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, sizeof(struct sample_read_value); if (data->read.group.nr > max_group_nr) - return -EFAULT; + goto out_efault; sz = data->read.group.nr * sample_read_value_size(read_format); OVERFLOW_CHECK(array, sz, max_size); @@ -3370,7 +3380,7 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, data->callchain = (struct ip_callchain *)array++; callchain_nr = data->callchain->nr; if (callchain_nr > max_callchain_nr) - return -EFAULT; + goto out_efault; sz = callchain_nr * sizeof(u64); /* * Save the cookie for the deferred user callchain. The last 2 @@ -3428,7 +3438,7 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, data->branch_stack = (struct branch_stack *)array++; if (data->branch_stack->nr > max_branch_nr) - return -EFAULT; + goto out_efault; sz = data->branch_stack->nr * sizeof(struct branch_entry); if (evsel__has_branch_hw_idx(evsel)) { @@ -3505,7 +3515,7 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, data->user_stack.size = *array++; if (WARN_ONCE(data->user_stack.size > sz, "user stack dump failure\n")) - return -EFAULT; + goto out_efault; } } @@ -3582,10 +3592,15 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, array = (void *)array + sz; } - if (evsel__is_offcpu_event(evsel)) - return __set_offcpu_sample(data); + if (evsel__is_offcpu_event(evsel)) { + if (__set_offcpu_sample(data)) + goto out_efault; + } return 0; +out_efault: + perf_sample__exit(data); + return -EFAULT; } int evsel__parse_sample_timestamp(struct evsel *evsel, union perf_event *event, @@ -3785,25 +3800,42 @@ bool evsel__fallback(struct evsel *evsel, struct target *target, int err, { int paranoid; - if ((err == ENOENT || err == ENXIO || err == ENODEV) && - evsel->core.attr.type == PERF_TYPE_HARDWARE && - evsel->core.attr.config == PERF_COUNT_HW_CPU_CYCLES) { + if ((err == ENODEV || err == ENOENT || err == ENXIO) && + evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) { /* - * If it's cycles then fall back to hrtimer based cpu-clock sw - * counter, which is always available even if no PMU support. - * - * PPC returns ENXIO until 2.6.37 (behavior changed with commit - * b0a873e). + * If it's the legacy hardware cycles event fails then fall back + * to hrtimer based cpu-clock sw counter, which is always + * available even if no PMU support. PPC returned ENXIO rather + * than ENODEV or ENOENT until 2.6.37. */ - evsel->core.attr.type = PERF_TYPE_SOFTWARE; + evsel->pmu = perf_pmus__find_by_type(PERF_TYPE_SOFTWARE); + assert(evsel->pmu); /* software is a "well-known" and can't fail PMU type. */ + + /* Configure the event. */ + evsel->core.attr.type = PERF_TYPE_SOFTWARE; evsel->core.attr.config = target__has_cpu(target) ? PERF_COUNT_SW_CPU_CLOCK : PERF_COUNT_SW_TASK_CLOCK; - scnprintf(msg, msgsize, - "The cycles event is not supported, trying to fall back to %s", - target__has_cpu(target) ? "cpu-clock" : "task-clock"); + evsel->core.is_pmu_core = false; + /* Remove excludes for new event. */ + if (evsel->fallenback_eacces) { + evsel->core.attr.exclude_kernel = 0; + evsel->core.attr.exclude_hv = 0; + evsel->fallenback_eacces = false; + } + if (evsel->fallenback_eopnotsupp) { + evsel->core.attr.exclude_guest = 0; + evsel->fallenback_eopnotsupp = false; + } + + /* Name is recomputed by evsel__name. */ zfree(&evsel->name); + + /* Log message. */ + scnprintf(msg, msgsize, + "The cycles event is not supported, trying to fall back to %s", + evsel__name(evsel)); return true; } else if (err == EACCES && !evsel->core.attr.exclude_kernel && (paranoid = perf_event_paranoid()) > 1) { @@ -3830,7 +3862,7 @@ bool evsel__fallback(struct evsel *evsel, struct target *target, int err, " samples", paranoid); evsel->core.attr.exclude_kernel = 1; evsel->core.attr.exclude_hv = 1; - + evsel->fallenback_eacces = true; return true; } else if (err == EOPNOTSUPP && !evsel->core.attr.exclude_guest && !evsel->exclude_GH) { @@ -3851,7 +3883,7 @@ bool evsel__fallback(struct evsel *evsel, struct target *target, int err, /* Apple M1 requires exclude_guest */ scnprintf(msg, msgsize, "Trying to fall back to excluding guest samples"); evsel->core.attr.exclude_guest = 1; - + evsel->fallenback_eopnotsupp = true; return true; } no_fallback: diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index a3d754c029a0..339b5c08a33d 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -124,6 +124,8 @@ struct evsel { bool default_metricgroup; /* A member of the Default metricgroup */ bool default_show_events; /* If a default group member, show the event */ bool needs_uniquify; + bool fallenback_eacces; + bool fallenback_eopnotsupp; struct hashmap *per_pkg_mask; int err; int script_output_type; @@ -285,10 +287,10 @@ void evsel__set_priv_destructor(void (*destructor)(void *priv)); struct callchain_param; -void evsel__config(struct evsel *evsel, struct record_opts *opts, - struct callchain_param *callchain); -void evsel__config_callchain(struct evsel *evsel, struct record_opts *opts, - struct callchain_param *callchain); +void evsel__config(struct evsel *evsel, const struct record_opts *opts, + const struct callchain_param *callchain); +void evsel__config_callchain(struct evsel *evsel, const struct record_opts *opts, + const struct callchain_param *callchain); int __evsel__sample_size(u64 sample_type); void evsel__calc_id_pos(struct evsel *evsel); diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c index 465fe2e9bbbe..644769e92708 100644 --- a/tools/perf/util/expr.c +++ b/tools/perf/util/expr.c @@ -376,7 +376,8 @@ int expr__find_ids(const char *expr, const char *one, if (one) expr__del_id(ctx, one); - return ret; + /* A positive value means syntax error, convert to -EINVAL */ + return ret > 0 ? -EINVAL : ret; } double expr_id_data__value(const struct expr_id_data *data) @@ -406,9 +407,9 @@ double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx &count)) result = count; else - pr_err("Failure to read '%s'", literal); + pr_err("Failure to read '%s'\n", literal); } else { - pr_err("Unrecognized literal '%s'", literal); + pr_err("Unrecognized literal '%s'\n", literal); } pr_debug2("literal: %s = %f\n", literal, result); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 9142a8ba4019..f30e48eb3fc3 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -54,6 +54,7 @@ #include "bpf-event.h" #include "bpf-utils.h" #include "clockid.h" +#include "cacheline.h" #include #include @@ -62,6 +63,15 @@ #include #endif +#define MAX_BPF_DATA_LEN (256 * 1024 * 1024) +#define MAX_BPF_PROGS 131072 +#define MAX_CACHE_ENTRIES 32768 +#define MAX_GROUP_DESC 32768 +#define MAX_NUMA_NODES 4096 +#define MAX_PMU_CAPS 512 +#define MAX_PMU_MAPPINGS 4096 +#define MAX_SCHED_DOMAINS 64 + /* * magic2 = "PERFILE2" * must be a numerical value to let the endianness @@ -306,16 +316,19 @@ static int do_read_bitmap(struct feat_fd *ff, unsigned long **pset, u64 *psize) return 0; } -#ifdef HAVE_LIBTRACEEVENT static int write_tracing_data(struct feat_fd *ff, - struct evlist *evlist) + struct evlist *evlist __maybe_unused) { if (WARN(ff->buf, "Error: calling %s in pipe-mode.\n", __func__)) return -1; +#ifdef HAVE_LIBTRACEEVENT return read_tracing_data(ff->fd, &evlist->core.entries); -} +#else + pr_err("ERROR: Trying to write tracing data without libtraceevent support.\n"); + return -1; #endif +} static int write_build_id(struct feat_fd *ff, struct evlist *evlist __maybe_unused) @@ -1026,10 +1039,10 @@ static int write_dir_format(struct feat_fd *ff, return do_write(ff, &data->dir.version, sizeof(data->dir.version)); } -#ifdef HAVE_LIBBPF_SUPPORT -static int write_bpf_prog_info(struct feat_fd *ff, +static int write_bpf_prog_info(struct feat_fd *ff __maybe_unused, struct evlist *evlist __maybe_unused) { +#ifdef HAVE_LIBBPF_SUPPORT struct perf_env *env = &ff->ph->env; struct rb_root *root; struct rb_node *next; @@ -1067,11 +1080,16 @@ static int write_bpf_prog_info(struct feat_fd *ff, out: up_read(&env->bpf_progs.lock); return ret; +#else + pr_err("ERROR: Trying to write bpf_prog_info without libbpf support.\n"); + return -1; +#endif // HAVE_LIBBPF_SUPPORT } -static int write_bpf_btf(struct feat_fd *ff, +static int write_bpf_btf(struct feat_fd *ff __maybe_unused, struct evlist *evlist __maybe_unused) { +#ifdef HAVE_LIBBPF_SUPPORT struct perf_env *env = &ff->ph->env; struct rb_root *root; struct rb_node *next; @@ -1100,8 +1118,11 @@ static int write_bpf_btf(struct feat_fd *ff, out: up_read(&env->bpf_progs.lock); return ret; -} +#else + pr_err("ERROR: Trying to write btf data without libbpf support.\n"); + return -1; #endif // HAVE_LIBBPF_SUPPORT +} static int cpu_cache_level__sort(const void *a, const void *b) { @@ -1304,6 +1325,19 @@ out: return ret; } +static int write_cln_size(struct feat_fd *ff, + struct evlist *evlist __maybe_unused) +{ + int cln_size = cacheline_size(); + + if (!cln_size) + cln_size = DEFAULT_CACHELINE_SIZE; + + ff->ph->env.cln_size = cln_size; + + return do_write(ff, &cln_size, sizeof(cln_size)); +} + static int write_stat(struct feat_fd *ff __maybe_unused, struct evlist *evlist __maybe_unused) { @@ -1980,9 +2014,9 @@ static void print_dir_format(struct feat_fd *ff, FILE *fp) fprintf(fp, "# directory data version : %"PRIu64"\n", data->dir.version); } -#ifdef HAVE_LIBBPF_SUPPORT -static void print_bpf_prog_info(struct feat_fd *ff, FILE *fp) +static void print_bpf_prog_info(struct feat_fd *ff __maybe_unused, FILE *fp) { +#ifdef HAVE_LIBBPF_SUPPORT struct perf_env *env = &ff->ph->env; struct rb_root *root; struct rb_node *next; @@ -1993,7 +2027,7 @@ static void print_bpf_prog_info(struct feat_fd *ff, FILE *fp) next = rb_first(root); if (!next) - printf("# bpf_prog_info empty\n"); + fprintf(fp, "# bpf_prog_info empty\n"); while (next) { struct bpf_prog_info_node *node; @@ -2006,10 +2040,14 @@ static void print_bpf_prog_info(struct feat_fd *ff, FILE *fp) } up_read(&env->bpf_progs.lock); +#else + fprintf(fp, "# bpf_prog_info missing, no libbpf support\n"); +#endif // HAVE_LIBBPF_SUPPORT } -static void print_bpf_btf(struct feat_fd *ff, FILE *fp) +static void print_bpf_btf(struct feat_fd *ff __maybe_unused, FILE *fp) { +#ifdef HAVE_LIBBPF_SUPPORT struct perf_env *env = &ff->ph->env; struct rb_root *root; struct rb_node *next; @@ -2031,8 +2069,10 @@ static void print_bpf_btf(struct feat_fd *ff, FILE *fp) } up_read(&env->bpf_progs.lock); -} +#else + fprintf(fp, "# bpf btf data missing, no libbpf support\n"); #endif // HAVE_LIBBPF_SUPPORT +} static void free_event_desc(struct evsel *events) { @@ -2261,6 +2301,11 @@ static void print_cache(struct feat_fd *ff, FILE *fp __maybe_unused) } } +static void print_cln_size(struct feat_fd *ff, FILE *fp) +{ + fprintf(fp, "# cacheline size: %u\n", ff->ph->env.cln_size); +} + static void print_compressed(struct feat_fd *ff, FILE *fp) { fprintf(fp, "# compressed : %s, level = %d, ratio = %d\n", @@ -2545,6 +2590,11 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, perf_event_header__bswap(&old_bev.header); len = old_bev.header.size - sizeof(old_bev); + if (len < 0 || len >= PATH_MAX) { + pr_warning("invalid build_id filename length %zd\n", len); + return -1; + } + if (readn(input, filename, len) != len) return -1; @@ -2587,6 +2637,11 @@ static int perf_header__read_build_ids(struct perf_header *header, perf_event_header__bswap(&bev.header); len = bev.header.size - sizeof(bev); + if (len < 0 || len >= PATH_MAX) { + pr_warning("invalid build_id filename length %zd\n", len); + goto out; + } + if (readn(input, filename, len) != len) goto out; /* @@ -2644,14 +2699,17 @@ static int process_e_machine(struct feat_fd *ff, void *data __maybe_unused) return do_read_u32(ff, &ff->ph->env.e_flags); } -#ifdef HAVE_LIBTRACEEVENT -static int process_tracing_data(struct feat_fd *ff, void *data) +static int process_tracing_data(struct feat_fd *ff __maybe_unused, void *data __maybe_unused) { +#ifdef HAVE_LIBTRACEEVENT ssize_t ret = trace_report(ff->fd, data, false); return ret < 0 ? -1 : 0; -} +#else + pr_err("ERROR: Trying to read tracing data without libtraceevent support.\n"); + return -1; #endif +} static int process_build_id(struct feat_fd *ff, void *data __maybe_unused) { @@ -2673,6 +2731,13 @@ static int process_nrcpus(struct feat_fd *ff, void *data __maybe_unused) ret = do_read_u32(ff, &nr_cpus_online); if (ret) return ret; + + if (nr_cpus_online > nr_cpus_avail) { + pr_err("Invalid HEADER_NRCPUS: nr_cpus_online (%u) > nr_cpus_avail (%u)\n", + nr_cpus_online, nr_cpus_avail); + return -1; + } + env->nr_cpus_avail = (int)nr_cpus_avail; env->nr_cpus_online = (int)nr_cpus_online; return 0; @@ -2746,6 +2811,12 @@ process_event_desc(struct feat_fd *ff, void *data __maybe_unused) return 0; } +/* + * Some arbitrary max for the number of command line arguments, + * Wildcards can expand and end up with tons of command line args. + */ +#define MAX_CMDLINE_NR 1048576 + static int process_cmdline(struct feat_fd *ff, void *data __maybe_unused) { struct perf_env *env = &ff->ph->env; @@ -2755,13 +2826,16 @@ static int process_cmdline(struct feat_fd *ff, void *data __maybe_unused) if (do_read_u32(ff, &nr)) return -1; + if (nr > MAX_CMDLINE_NR) + return -1; + env->nr_cmdline = nr; cmdline = zalloc(ff->size + nr + 1); if (!cmdline) return -1; - argv = zalloc(sizeof(char *) * (nr + 1)); + argv = calloc(nr + 1, sizeof(char *)); if (!argv) goto error; @@ -2794,6 +2868,11 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused) int cpu_nr = env->nr_cpus_avail; u64 size = 0; + if (cpu_nr == 0) { + pr_err("Invalid HEADER_CPU_TOPOLOGY: missing HEADER_NRCPUS\n"); + return -1; + } + env->cpu = calloc(cpu_nr, sizeof(*env->cpu)); if (!env->cpu) return -1; @@ -2801,6 +2880,12 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused) if (do_read_u32(ff, &nr)) goto free_cpu; + if (nr > (u32)cpu_nr) { + pr_err("Invalid HEADER_CPU_TOPOLOGY: nr_sibling_cores (%u) > nr_cpus_avail (%d)\n", + nr, cpu_nr); + goto free_cpu; + } + env->nr_sibling_cores = nr; size += sizeof(u32); if (strbuf_init(&sb, 128) < 0) @@ -2820,7 +2905,13 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused) env->sibling_cores = strbuf_detach(&sb, NULL); if (do_read_u32(ff, &nr)) - return -1; + goto free_cpu; + + if (nr > (u32)cpu_nr) { + pr_err("Invalid HEADER_CPU_TOPOLOGY: nr_sibling_threads (%u) > nr_cpus_avail (%d)\n", + nr, cpu_nr); + goto free_cpu; + } env->nr_sibling_threads = nr; size += sizeof(u32); @@ -2869,7 +2960,13 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused) return 0; if (do_read_u32(ff, &nr)) - return -1; + goto free_cpu; + + if (nr > (u32)cpu_nr) { + pr_err("Invalid HEADER_CPU_TOPOLOGY: nr_sibling_dies (%u) > nr_cpus_avail (%d)\n", + nr, cpu_nr); + goto free_cpu; + } env->nr_sibling_dies = nr; size += sizeof(u32); @@ -2915,7 +3012,19 @@ static int process_numa_topology(struct feat_fd *ff, void *data __maybe_unused) if (do_read_u32(ff, &nr)) return -1; - nodes = zalloc(sizeof(*nodes) * nr); + if (nr > MAX_NUMA_NODES) { + pr_err("Invalid HEADER_NUMA_TOPOLOGY: nr_nodes (%u) > %u\n", + nr, MAX_NUMA_NODES); + return -1; + } + + if (ff->size < sizeof(u32) + nr * (sizeof(u32) + 2 * sizeof(u64))) { + pr_err("Invalid HEADER_NUMA_TOPOLOGY: section too small (%zu) for %u nodes\n", + ff->size, nr); + return -1; + } + + nodes = calloc(nr, sizeof(*nodes)); if (!nodes) return -ENOMEM; @@ -2966,6 +3075,18 @@ static int process_pmu_mappings(struct feat_fd *ff, void *data __maybe_unused) return 0; } + if (pmu_num > MAX_PMU_MAPPINGS) { + pr_err("Invalid HEADER_PMU_MAPPINGS: pmu_num (%u) > %u\n", + pmu_num, MAX_PMU_MAPPINGS); + return -1; + } + + if (ff->size < sizeof(u32) + pmu_num * 2 * sizeof(u32)) { + pr_err("Invalid HEADER_PMU_MAPPINGS: section too small (%zu) for %u PMUs\n", + ff->size, pmu_num); + return -1; + } + env->nr_pmu_mappings = pmu_num; if (strbuf_init(&sb, 128) < 0) return -1; @@ -3016,12 +3137,25 @@ static int process_group_desc(struct feat_fd *ff, void *data __maybe_unused) if (do_read_u32(ff, &nr_groups)) return -1; - env->nr_groups = nr_groups; if (!nr_groups) { pr_debug("group desc not available\n"); return 0; } + if (nr_groups > MAX_GROUP_DESC) { + pr_err("Invalid HEADER_GROUP_DESC: nr_groups (%u) > %u\n", + nr_groups, MAX_GROUP_DESC); + return -1; + } + + if (ff->size < sizeof(u32) + nr_groups * 3 * sizeof(u32)) { + pr_err("Invalid HEADER_GROUP_DESC: section too small (%zu) for %u groups\n", + ff->size, nr_groups); + return -1; + } + + env->nr_groups = nr_groups; + desc = calloc(nr_groups, sizeof(*desc)); if (!desc) return -1; @@ -3113,7 +3247,19 @@ static int process_cache(struct feat_fd *ff, void *data __maybe_unused) if (do_read_u32(ff, &cnt)) return -1; - caches = zalloc(sizeof(*caches) * cnt); + if (cnt > MAX_CACHE_ENTRIES) { + pr_err("Invalid HEADER_CACHE: cnt (%u) > %u\n", + cnt, MAX_CACHE_ENTRIES); + return -1; + } + + if (ff->size < 2 * sizeof(u32) + cnt * 7 * sizeof(u32)) { + pr_err("Invalid HEADER_CACHE: section too small (%zu) for %u entries\n", + ff->size, cnt); + return -1; + } + + caches = calloc(cnt, sizeof(*caches)); if (!caches) return -1; @@ -3154,6 +3300,16 @@ out_free_caches: return -1; } +static int process_cln_size(struct feat_fd *ff, void *data __maybe_unused) +{ + struct perf_env *env = &ff->ph->env; + + if (do_read_u32(ff, &env->cln_size)) + return -1; + + return 0; +} + static int process_sample_time(struct feat_fd *ff, void *data __maybe_unused) { struct perf_session *session; @@ -3195,7 +3351,19 @@ static int process_mem_topology(struct feat_fd *ff, if (do_read_u64(ff, &nr)) return -1; - nodes = zalloc(sizeof(*nodes) * nr); + if (nr > MAX_NUMA_NODES) { + pr_err("Invalid HEADER_MEM_TOPOLOGY: nr_nodes (%llu) > %u\n", + (unsigned long long)nr, MAX_NUMA_NODES); + return -1; + } + + if (ff->size < 3 * sizeof(u64) + nr * 2 * sizeof(u64)) { + pr_err("Invalid HEADER_MEM_TOPOLOGY: section too small (%zu) for %llu nodes\n", + ff->size, (unsigned long long)nr); + return -1; + } + + nodes = calloc(nr, sizeof(*nodes)); if (!nodes) return -1; @@ -3285,7 +3453,19 @@ static int process_hybrid_topology(struct feat_fd *ff, if (do_read_u32(ff, &nr)) return -1; - nodes = zalloc(sizeof(*nodes) * nr); + if (nr > MAX_PMU_MAPPINGS) { + pr_err("Invalid HEADER_HYBRID_TOPOLOGY: nr_nodes (%u) > %u\n", + nr, MAX_PMU_MAPPINGS); + return -1; + } + + if (ff->size < sizeof(u32) + nr * 2 * sizeof(u32)) { + pr_err("Invalid HEADER_HYBRID_TOPOLOGY: section too small (%zu) for %u nodes\n", + ff->size, nr); + return -1; + } + + nodes = calloc(nr, sizeof(*nodes)); if (!nodes) return -ENOMEM; @@ -3330,9 +3510,9 @@ static int process_dir_format(struct feat_fd *ff, return do_read_u64(ff, &data->dir.version); } -#ifdef HAVE_LIBBPF_SUPPORT -static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused) +static int process_bpf_prog_info(struct feat_fd *ff __maybe_unused, void *data __maybe_unused) { +#ifdef HAVE_LIBBPF_SUPPORT struct bpf_prog_info_node *info_node; struct perf_env *env = &ff->ph->env; struct perf_bpil *info_linear; @@ -3347,6 +3527,18 @@ static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused) if (do_read_u32(ff, &count)) return -1; + if (count > MAX_BPF_PROGS) { + pr_err("Invalid HEADER_BPF_PROG_INFO: count (%u) > %u\n", + count, MAX_BPF_PROGS); + return -1; + } + + if (ff->size < sizeof(u32) + count * (2 * sizeof(u32) + sizeof(u64))) { + pr_err("Invalid HEADER_BPF_PROG_INFO: section too small (%zu) for %u entries\n", + ff->size, count); + return -1; + } + down_write(&env->bpf_progs.lock); for (i = 0; i < count; ++i) { @@ -3364,6 +3556,12 @@ static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused) goto out; } + if (data_len > MAX_BPF_DATA_LEN) { + pr_warning("Invalid HEADER_BPF_PROG_INFO: data_len (%u) too large\n", + data_len); + goto out; + } + info_linear = malloc(sizeof(struct perf_bpil) + data_len); if (!info_linear) @@ -3402,10 +3600,15 @@ out: free(info_node); up_write(&env->bpf_progs.lock); return err; +#else + pr_err("ERROR: Trying to read bpf_prog_info without libbpf support.\n"); + return -1; +#endif // HAVE_LIBBPF_SUPPORT } -static int process_bpf_btf(struct feat_fd *ff, void *data __maybe_unused) +static int process_bpf_btf(struct feat_fd *ff __maybe_unused, void *data __maybe_unused) { +#ifdef HAVE_LIBBPF_SUPPORT struct perf_env *env = &ff->ph->env; struct btf_node *node = NULL; u32 count, i; @@ -3419,6 +3622,17 @@ static int process_bpf_btf(struct feat_fd *ff, void *data __maybe_unused) if (do_read_u32(ff, &count)) return -1; + if (count > MAX_BPF_PROGS) { + pr_err("bpf btf count %u too large (max %u)\n", count, MAX_BPF_PROGS); + return -1; + } + + if (ff->size < sizeof(u32) + count * 2 * sizeof(u32)) { + pr_err("Invalid HEADER_BPF_BTF: section too small (%zu) for %u entries\n", + ff->size, count); + return -1; + } + down_write(&env->bpf_progs.lock); for (i = 0; i < count; ++i) { @@ -3429,6 +3643,12 @@ static int process_bpf_btf(struct feat_fd *ff, void *data __maybe_unused) if (do_read_u32(ff, &data_size)) goto out; + if (data_size > MAX_BPF_DATA_LEN) { + pr_err("bpf btf data size %u too large (max %u)\n", + data_size, MAX_BPF_DATA_LEN); + goto out; + } + node = malloc(sizeof(struct btf_node) + data_size); if (!node) goto out; @@ -3449,8 +3669,11 @@ out: up_write(&env->bpf_progs.lock); free(node); return err; -} +#else + pr_err("ERROR: Trying to read btf data without libbpf support.\n"); + return -1; #endif // HAVE_LIBBPF_SUPPORT +} static int process_compressed(struct feat_fd *ff, void *data __maybe_unused) @@ -3492,7 +3715,13 @@ static int __process_pmu_caps(struct feat_fd *ff, int *nr_caps, if (!nr_pmu_caps) return 0; - *caps = zalloc(sizeof(char *) * nr_pmu_caps); + if (nr_pmu_caps > MAX_PMU_CAPS) { + pr_err("Invalid pmu caps: nr_pmu_caps (%u) > %u\n", + nr_pmu_caps, MAX_PMU_CAPS); + return -1; + } + + *caps = calloc(nr_pmu_caps, sizeof(char *)); if (!*caps) return -1; @@ -3569,7 +3798,19 @@ static int process_pmu_caps(struct feat_fd *ff, void *data __maybe_unused) return 0; } - pmu_caps = zalloc(sizeof(*pmu_caps) * nr_pmu); + if (nr_pmu > MAX_PMU_MAPPINGS) { + pr_err("Invalid HEADER_PMU_CAPS: nr_pmu (%u) > %u\n", + nr_pmu, MAX_PMU_MAPPINGS); + return -1; + } + + if (ff->size < sizeof(u32) + nr_pmu * sizeof(u32)) { + pr_err("Invalid HEADER_PMU_CAPS: section too small (%zu) for %u PMUs\n", + ff->size, nr_pmu); + return -1; + } + + pmu_caps = calloc(nr_pmu, sizeof(*pmu_caps)); if (!pmu_caps) return -ENOMEM; @@ -3622,7 +3863,18 @@ static int process_cpu_domain_info(struct feat_fd *ff, void *data __maybe_unused nra = env->nr_cpus_avail; nr = env->nr_cpus_online; - cd_map = zalloc(sizeof(*cd_map) * nra); + if (nra == 0 || nr == 0) { + pr_err("Invalid HEADER_CPU_DOMAIN_INFO: missing HEADER_NRCPUS\n"); + return -1; + } + + if (ff->size < 2 * sizeof(u32) + nr * 2 * sizeof(u32)) { + pr_err("Invalid HEADER_CPU_DOMAIN_INFO: section too small (%zu) for %u CPUs\n", + (size_t)ff->size, nr); + return -1; + } + + cd_map = calloc(nra, sizeof(*cd_map)); if (!cd_map) return -1; @@ -3638,12 +3890,34 @@ static int process_cpu_domain_info(struct feat_fd *ff, void *data __maybe_unused if (ret) return ret; + /* + * Sanity check: real systems have at most ~10 sched domain levels + * (SMT, CLS, MC, PKG + NUMA hops). Reject obviously bogus values + * from malformed perf.data files before they cause excessive + * allocation in the per-CPU loop. + */ + if (max_sched_domains > MAX_SCHED_DOMAINS) { + pr_err("Invalid HEADER_CPU_DOMAIN_INFO: max_sched_domains %u > %u\n", + max_sched_domains, MAX_SCHED_DOMAINS); + return -1; + } + env->max_sched_domains = max_sched_domains; for (i = 0; i < nr; i++) { if (do_read_u32(ff, &cpu)) return -1; + if (cpu >= nra) { + pr_err("Invalid HEADER_CPU_DOMAIN_INFO: cpu %d >= nr_cpus_avail (%d)\n", cpu, nra); + return -1; + } + + if (cd_map[cpu]) { + pr_err("Invalid HEADER_CPU_DOMAIN_INFO: duplicate cpu %u\n", cpu); + return -1; + } + cd_map[cpu] = zalloc(sizeof(*cd_map[cpu])); if (!cd_map[cpu]) return -1; @@ -3653,9 +3927,15 @@ static int process_cpu_domain_info(struct feat_fd *ff, void *data __maybe_unused if (do_read_u32(ff, &nr_domains)) return -1; + if (nr_domains > max_sched_domains) { + pr_err("Invalid HEADER_CPU_DOMAIN_INFO: nr_domains %u > max_sched_domains (%u)\n", + nr_domains, max_sched_domains); + return -1; + } + cd_map[cpu]->nr_domains = nr_domains; - cd_map[cpu]->domains = zalloc(sizeof(*d_info) * max_sched_domains); + cd_map[cpu]->domains = calloc(max_sched_domains, sizeof(*d_info)); if (!cd_map[cpu]->domains) return -1; @@ -3663,11 +3943,23 @@ static int process_cpu_domain_info(struct feat_fd *ff, void *data __maybe_unused if (do_read_u32(ff, &domain)) return -1; + if (domain >= max_sched_domains) { + pr_err("Invalid HEADER_CPU_DOMAIN_INFO: domain %d >= max_sched_domains (%d)\n", + domain, max_sched_domains); + return -1; + } + d_info = zalloc(sizeof(*d_info)); if (!d_info) return -1; - assert(cd_map[cpu]->domains[domain] == NULL); + if (cd_map[cpu]->domains[domain]) { + pr_err("Invalid HEADER_CPU_DOMAIN_INFO: duplicate domain %u for cpu %u\n", + domain, cpu); + free(d_info); + return -1; + } + cd_map[cpu]->domains[domain] = d_info; d_info->domain = domain; @@ -3726,9 +4018,7 @@ static int process_cpu_domain_info(struct feat_fd *ff, void *data __maybe_unused const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE]; const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = { -#ifdef HAVE_LIBTRACEEVENT FEAT_OPN(TRACING_DATA, tracing_data, false), -#endif FEAT_OPN(BUILD_ID, build_id, false), FEAT_OPR(HOSTNAME, hostname, false), FEAT_OPR(OSRELEASE, osrelease, false), @@ -3752,10 +4042,8 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = { FEAT_OPR(MEM_TOPOLOGY, mem_topology, true), FEAT_OPR(CLOCKID, clockid, false), FEAT_OPN(DIR_FORMAT, dir_format, false), -#ifdef HAVE_LIBBPF_SUPPORT FEAT_OPR(BPF_PROG_INFO, bpf_prog_info, false), FEAT_OPR(BPF_BTF, bpf_btf, false), -#endif FEAT_OPR(COMPRESSED, compressed, false), FEAT_OPR(CPU_PMU_CAPS, cpu_pmu_caps, false), FEAT_OPR(CLOCK_DATA, clock_data, false), @@ -3763,6 +4051,7 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = { FEAT_OPR(PMU_CAPS, pmu_caps, false), FEAT_OPR(CPU_DOMAIN_INFO, cpu_domain_info, true), FEAT_OPR(E_MACHINE, e_machine, false), + FEAT_OPR(CLN_SIZE, cln_size, false), }; struct header_print_data { @@ -3770,6 +4059,13 @@ struct header_print_data { bool full; /* extended list of headers */ }; +const char *header_feat__name(unsigned int id) +{ + if (id < HEADER_LAST_FEATURE) + return feat_ops[id].name ?: "INVALID"; + return "INVALID"; +} + static int perf_file_section__fprintf_info(struct perf_file_section *section, struct perf_header *ph, int feat, int fd, void *data) @@ -3778,11 +4074,11 @@ static int perf_file_section__fprintf_info(struct perf_file_section *section, struct feat_fd ff; if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { - pr_debug("Failed to lseek to %" PRIu64 " offset for feature " - "%d, continuing...\n", section->offset, feat); + pr_debug("Failed to lseek to %" PRIu64 " offset for feature %s (%d), continuing...\n", + section->offset, header_feat__name(feat), feat); return 0; } - if (feat >= HEADER_LAST_FEATURE) { + if (feat >= ph->last_feat) { pr_warning("unknown feature %d\n", feat); return 0; } @@ -3834,7 +4130,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) return 0; fprintf(fp, "# missing features: "); - for_each_clear_bit(bit, header->adds_features, HEADER_LAST_FEATURE) { + for_each_clear_bit(bit, header->adds_features, header->last_feat) { if (bit) fprintf(fp, "%s ", feat_ops[bit].name); } @@ -4164,7 +4460,7 @@ int perf_header__process_sections(struct perf_header *header, int fd, if (err < 0) goto out_free; - for_each_set_bit(feat, header->adds_features, HEADER_LAST_FEATURE) { + for_each_set_bit(feat, header->adds_features, header->last_feat) { err = process(sec++, header, feat, fd, data); if (err < 0) goto out_free; @@ -4379,6 +4675,7 @@ int perf_file_header__read(struct perf_file_header *header, ph->data_offset = header->data.offset; ph->data_size = header->data.size; ph->feat_offset = header->data.offset + header->data.size; + ph->last_feat = HEADER_LAST_FEATURE; return 0; } @@ -4394,8 +4691,8 @@ static int perf_file_section__process(struct perf_file_section *section, }; if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { - pr_debug("Failed to lseek to %" PRIu64 " offset for feature " - "%d, continuing...\n", section->offset, feat); + pr_debug("Failed to lseek to %" PRIu64 " offset for feature %s (%d), continuing...\n", + section->offset, header_feat__name(feat), feat); return 0; } @@ -4428,6 +4725,8 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, if (ph->needs_swap) header->size = bswap_64(header->size); + /* The last feature is written out as a 0 sized event and will update this value. */ + ph->last_feat = 0; return 0; } @@ -4660,31 +4959,68 @@ out_delete_evlist: return -ENOMEM; } -int perf_event__process_feature(struct perf_session *session, +int perf_event__process_feature(const struct perf_tool *tool __maybe_unused, + struct perf_session *session, union perf_event *event) { struct feat_fd ff = { .fd = 0 }; struct perf_record_header_feature *fe = (struct perf_record_header_feature *)event; + struct perf_header *header = &session->header; int type = fe->header.type; - u64 feat = fe->feat_id; + int feat = (int)fe->feat_id; int ret = 0; bool print = dump_trace; + bool last_feature_mark = false; if (type < 0 || type >= PERF_RECORD_HEADER_MAX) { pr_warning("invalid record type %d in pipe-mode\n", type); return 0; } - if (feat == HEADER_RESERVED || feat >= HEADER_LAST_FEATURE) { - pr_warning("invalid record type %d in pipe-mode\n", type); + if (feat == HEADER_RESERVED) { + pr_warning("invalid reserved record type in pipe-mode\n"); + return -1; + } + if (feat < 0 || feat == INT_MAX) { + pr_warning("invalid value for feature type %x\n", feat); + return -1; + } + if (feat >= header->last_feat) { + if (event->header.size == sizeof(*fe)) { + /* + * Either an unexpected zero size feature or the + * HEADER_LAST_FEATURE mark. + */ + if (feat > header->last_feat) + header->last_feat = min(feat, HEADER_LAST_FEATURE); + last_feature_mark = true; + } else { + /* + * A feature but beyond what is known as in + * bounds. Assume the last feature is 1 beyond this + * feature. + */ + session->header.last_feat = min(feat + 1, HEADER_LAST_FEATURE); + } + } + if (feat >= HEADER_LAST_FEATURE) { + if (!last_feature_mark) { + pr_warning("unknown feature %d for data file version (%s) in this version of perf (%s)\n", + feat, header->env.version, perf_version_string); + } + return 0; + } + if (event->header.size < sizeof(*fe)) { + pr_warning("feature header size too small\n"); return -1; } - ff.buf = (void *)fe->data; ff.size = event->header.size - sizeof(*fe); - ff.ph = &session->header; + ff.ph = header; if (feat_ops[feat].process && feat_ops[feat].process(&ff, NULL)) { - ret = -1; + // Processing failed, ignore when this is the last feature mark. + if (!last_feature_mark) + ret = -1; goto out; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index cc40ac796f52..86b1a72026d3 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -55,6 +55,7 @@ enum { HEADER_PMU_CAPS, HEADER_CPU_DOMAIN_INFO, HEADER_E_MACHINE, + HEADER_CLN_SIZE, HEADER_LAST_FEATURE, HEADER_FEAT_BITS = 256, }; @@ -109,6 +110,7 @@ struct perf_header { u64 data_size; u64 feat_offset; DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); + int last_feat; struct perf_env env; }; @@ -132,6 +134,8 @@ struct perf_header_feature_ops { extern const char perf_version_string[]; +const char *header_feat__name(unsigned int id); + int perf_session__read_header(struct perf_session *session); int perf_session__write_header(struct perf_session *session, struct evlist *evlist, @@ -170,7 +174,8 @@ int perf_header__process_sections(struct perf_header *header, int fd, int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); -int perf_event__process_feature(struct perf_session *session, +int perf_event__process_feature(const struct perf_tool *tool, + struct perf_session *session, union perf_event *event); int perf_event__process_attr(const struct perf_tool *tool, union perf_event *event, struct evlist **pevlist); @@ -202,6 +207,8 @@ int write_padded(struct feat_fd *fd, const void *bf, int build_caches_for_cpu(u32 cpu, struct cpu_cache_level caches[], u32 *cntp); +#define DEFAULT_CACHELINE_SIZE 64 + /* * arch specific callback */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 7ffaa3d9851b..747fdc455c80 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -110,6 +110,9 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) len = thread__comm_len(h->thread); if (hists__new_col_len(hists, HISTC_COMM, len)) hists__set_col_len(hists, HISTC_THREAD, len + 8); + if (hists->hpp_list->comm_nodigit) + hists__new_col_len(hists, HISTC_COMM_NODIGIT, + (u16) sort__comm_nodigit_len(h)); if (h->ms.map) { len = dso__name_len(map__dso(h->ms.map)); @@ -1148,7 +1151,7 @@ iter_prepare_cumulative_entry(struct hist_entry_iter *iter, * cumulated only one time to prevent entries more than 100% * overhead. */ - he_cache = malloc(sizeof(*he_cache) * (cursor->nr + 1)); + he_cache = calloc(cursor->nr + 1, sizeof(*he_cache)); if (he_cache == NULL) return -ENOMEM; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1d5ea632ca4e..d97a4efb9250 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -44,6 +44,7 @@ enum hist_column { HISTC_THREAD, HISTC_TGID, HISTC_COMM, + HISTC_COMM_NODIGIT, HISTC_CGROUP_ID, HISTC_CGROUP, HISTC_PARENT, @@ -522,6 +523,7 @@ struct perf_hpp_list { int socket; int thread; int comm; + int comm_nodigit; }; extern struct perf_hpp_list perf_hpp_list; diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c index 3c958d738ca6..8b615dc94e9e 100644 --- a/tools/perf/util/intel-tpebs.c +++ b/tools/perf/util/intel-tpebs.c @@ -22,7 +22,6 @@ #include "tool.h" #include "cpumap.h" #include "metricgroup.h" -#include "stat.h" #include #include #include @@ -217,15 +216,6 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, return 0; } -static int process_feature_event(const struct perf_tool *tool __maybe_unused, - struct perf_session *session, - union perf_event *event) -{ - if (event->feat.feat_id < HEADER_LAST_FEATURE) - return perf_event__process_feature(session, event); - return 0; -} - static void *__sample_reader(void *arg __maybe_unused) { struct perf_session *session; @@ -238,7 +228,7 @@ static void *__sample_reader(void *arg __maybe_unused) perf_tool__init(&tool, /*ordered_events=*/false); tool.sample = process_sample_event; - tool.feature = process_feature_event; + tool.feature = perf_event__process_feature; tool.attr = perf_event__process_attr; session = perf_session__new(&data, &tool); diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c index 63ea3fb53e77..c1c12308cc12 100644 --- a/tools/perf/util/libbfd.c +++ b/tools/perf/util/libbfd.c @@ -233,7 +233,7 @@ int libbfd__addr2line(const char *dso_name, u64 addr, } if (a2l == NULL) { - if (!symbol_conf.disable_add2line_warn) + if (!symbol_conf.addr2line_disable_warn) pr_warning("addr2line_init failed for %s\n", dso_name); return 0; } diff --git a/tools/perf/util/llvm.c b/tools/perf/util/llvm.c index 0d126d233c01..a0deb742a733 100644 --- a/tools/perf/util/llvm.c +++ b/tools/perf/util/llvm.c @@ -153,11 +153,17 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym, /*get_op_info=*/NULL, symbol_lookup_callback); } else { char triplet[64]; + const char *features = NULL; scnprintf(triplet, sizeof(triplet), "%s-linux-gnu", args->arch->name); - disasm = LLVMCreateDisasm(triplet, &storage, /*tag_type=*/0, - /*get_op_info=*/NULL, symbol_lookup_callback); + if (args->arch->id.e_machine == EM_AARCH64) + features = "+all"; + disasm = LLVMCreateDisasmCPUFeatures(triplet, /*cpu=*/"", + features, &storage, + /*tag_type=*/0, + /*get_op_info=*/NULL, + symbol_lookup_callback); } if (disasm == NULL) diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c index 4092211cff62..81a97ac34077 100644 --- a/tools/perf/util/maps.c +++ b/tools/perf/util/maps.c @@ -844,7 +844,6 @@ static int __maps__insert_sorted(struct maps *maps, unsigned int first_after_ind static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new) { int err = 0; - FILE *fp = debug_file(); unsigned int i, ni = INT_MAX; // Some gcc complain, but depends on maps_by_name... if (!maps__maps_by_address_sorted(maps)) @@ -872,8 +871,8 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new) dso__name(map__dso(new))); } else if (verbose >= 2) { pr_debug("overlapping maps:\n"); - map__fprintf(new, fp); - map__fprintf(pos, fp); + map__fprintf(new, debug_file()); + map__fprintf(pos, debug_file()); } if (maps_by_name) @@ -894,7 +893,7 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new) map__set_end(before, map__start(new)); if (verbose >= 2 && !use_browser) - map__fprintf(before, fp); + map__fprintf(before, debug_file()); } if (map__end(new) < map__end(pos)) { /* The new map isn't as long as the existing map. */ @@ -912,7 +911,7 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new) map__map_ip(after, map__end(new))); if (verbose >= 2 && !use_browser) - map__fprintf(after, fp); + map__fprintf(after, debug_file()); } /* * If adding one entry, for `before` or `after`, we can replace @@ -956,6 +955,7 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new) if (maps_by_name) { map__put(maps_by_name[ni]); maps_by_name[ni] = map__get(new); + maps__set_maps_by_name_sorted(maps, false); } err = __maps__insert_sorted(maps, i + 1, after, NULL); @@ -982,6 +982,7 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new) if (maps_by_name) { map__put(maps_by_name[ni]); maps_by_name[ni] = map__get(new); + maps__set_maps_by_name_sorted(maps, false); } check_invariants(maps); @@ -1080,16 +1081,9 @@ int maps__copy_from(struct maps *dest, struct maps *parent) map__put(new); } maps__set_maps_by_address_sorted(dest, maps__maps_by_address_sorted(parent)); - if (!err) { - RC_CHK_ACCESS(dest)->last_search_by_name_idx = - RC_CHK_ACCESS(parent)->last_search_by_name_idx; - maps__set_maps_by_name_sorted(dest, - dest_maps_by_name && - maps__maps_by_name_sorted(parent)); - } else { - RC_CHK_ACCESS(dest)->last_search_by_name_idx = 0; - maps__set_maps_by_name_sorted(dest, false); - } + RC_CHK_ACCESS(dest)->last_search_by_name_idx = 0; + /* Values were copied into the name array in address order. */ + maps__set_maps_by_name_sorted(dest, false); } else { /* Unexpected copying to a maps containing entries. */ for (unsigned int i = 0; !err && i < n; i++) { diff --git a/tools/perf/util/mem2node.c b/tools/perf/util/mem2node.c index 03a7d7b27737..51a2292cbf7e 100644 --- a/tools/perf/util/mem2node.c +++ b/tools/perf/util/mem2node.c @@ -59,7 +59,7 @@ int mem2node__init(struct mem2node *map, struct perf_env *env) max += bitmap_weight(n->set, n->size); } - entries = zalloc(sizeof(*entries) * max); + entries = calloc(max, sizeof(*entries)); if (!entries) return -ENOMEM; diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 7e39d469111b..4db9578efd81 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -387,8 +387,13 @@ static bool match_pm_metric_or_groups(const struct pmu_metric *pm, const char *p const char *metric_or_groups) { const char *pm_pmu = pm->pmu ?: "cpu"; + struct perf_pmu *perf_pmu = NULL; - if (strcmp(pmu, "all") && strcmp(pm_pmu, pmu)) + if (pm->pmu) + perf_pmu = perf_pmus__find(pm->pmu); + + if (strcmp(pmu, "all") && strcmp(pm_pmu, pmu) && + (perf_pmu && !perf_pmu__name_wildcard_match(perf_pmu, pmu))) return false; return match_metric_or_groups(pm->metric_group, metric_or_groups) || @@ -909,10 +914,9 @@ static int __add_metric(struct list_head *metric_list, expr = metric_no_threshold ? pm->metric_name : pm->metric_threshold; visited_node.name = "__threshold__"; } - if (expr__find_ids(expr, NULL, root_metric->pctx) < 0) { - /* Broken metric. */ - ret = -EINVAL; - } + + ret = expr__find_ids(expr, NULL, root_metric->pctx); + if (!ret) { /* Resolve referenced metrics. */ struct perf_pmu *pmu; @@ -1096,7 +1100,7 @@ static int metricgroup__add_metric(const char *pmu, const char *metric_name, con */ ret = metricgroup__for_each_metric(table, metricgroup__add_metric_callback, &data); if (!ret && !data.has_match) - ret = -EINVAL; + ret = -ENOENT; /* * add to metric_list so that they can be released @@ -1147,6 +1151,8 @@ static int metricgroup__add_metric_list(const char *pmu, const char *list, user_requested_cpu_list, system_wide, metric_list, table); if (ret == -EINVAL) + pr_err("Fail to parse metric or group `%s'\n", metric_name); + else if (ret == -ENOENT) pr_err("Cannot find metric or group `%s'\n", metric_name); if (ret) @@ -1259,7 +1265,8 @@ err_out: static int parse_ids(bool metric_no_merge, bool fake_pmu, struct expr_parse_ctx *ids, const char *modifier, bool group_events, const bool tool_events[TOOL_PMU__EVENT_MAX], - struct evlist **out_evlist) + struct evlist **out_evlist, + const char *filter_pmu) { struct parse_events_error parse_error; struct evlist *parsed_evlist; @@ -1313,7 +1320,7 @@ static int parse_ids(bool metric_no_merge, bool fake_pmu, } pr_debug("Parsing metric events '%s'\n", events.buf); parse_events_error__init(&parse_error); - ret = __parse_events(parsed_evlist, events.buf, /*pmu_filter=*/NULL, + ret = __parse_events(parsed_evlist, events.buf, filter_pmu, &parse_error, fake_pmu, /*warn_if_reordered=*/false, /*fake_tp=*/false); if (ret) { @@ -1416,7 +1423,8 @@ static int parse_groups(struct evlist *perf_evlist, /*modifier=*/NULL, /*group_events=*/false, tool_events, - &combined_evlist); + &combined_evlist, + (pmu && strcmp(pmu, "all") == 0) ? NULL : pmu); } if (combined) expr__ctx_free(combined); @@ -1471,7 +1479,8 @@ static int parse_groups(struct evlist *perf_evlist, } if (!metric_evlist) { ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier, - m->group_events, tool_events, &m->evlist); + m->group_events, tool_events, &m->evlist, + (pmu && strcmp(pmu, "all") == 0) ? NULL : pmu); if (ret) goto out; diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c index 8c62611f10aa..a5857f9f5af2 100644 --- a/tools/perf/util/ordered-events.c +++ b/tools/perf/util/ordered-events.c @@ -243,7 +243,7 @@ static int do_flush(struct ordered_events *oe, bool show_progress) if (iter->timestamp > limit) break; ret = oe->deliver(oe, iter); - if (ret) + if (ret < 0) return ret; ordered_events__delete(oe, iter); diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 7b4629625b1e..1497e1f2a08c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -429,7 +429,7 @@ bool parse_events__filter_pmu(const struct parse_events_state *parse_state, if (parse_state->pmu_filter == NULL) return false; - return strcmp(parse_state->pmu_filter, pmu->name) != 0; + return perf_pmu__wildcard_match(pmu, parse_state->pmu_filter) == 0; } static int parse_events_add_pmu(struct parse_events_state *parse_state, diff --git a/tools/perf/util/perf-regs-arch/perf_regs_s390.c b/tools/perf/util/perf-regs-arch/perf_regs_s390.c index c61df24edf0f..19f219225183 100644 --- a/tools/perf/util/perf-regs-arch/perf_regs_s390.c +++ b/tools/perf/util/perf-regs-arch/perf_regs_s390.c @@ -1,7 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 +#include +#include #include "../perf_regs.h" #include "../../arch/s390/include/perf_regs.h" +#include "debug.h" + +#include +#include uint64_t __perf_reg_mask_s390(bool intr __maybe_unused) { @@ -95,3 +101,75 @@ uint64_t __perf_reg_sp_s390(void) { return PERF_REG_S390_R15; } + +/* %rXX */ +#define SDT_OP_REGEX1 "^(%r([0-9]|1[0-5]))$" +/* +-###(%rXX) */ +#define SDT_OP_REGEX2 "^([+-]?[0-9]+\\(%r([0-9]|1[0-5])\\))$" +static regex_t sdt_op_regex1, sdt_op_regex2; + +static int sdt_init_op_regex(void) +{ + static int initialized; + int ret = 0; + + if (initialized) + return 0; + + ret = regcomp(&sdt_op_regex1, SDT_OP_REGEX1, REG_EXTENDED); + if (ret) + goto error; + initialized = 1; + + ret = regcomp(&sdt_op_regex2, SDT_OP_REGEX2, REG_EXTENDED); + if (ret) + goto free_regex1; + initialized = 2; + + return 0; + +free_regex1: + regfree(&sdt_op_regex1); +error: + pr_debug4("Regex compilation error, initialized %d\n", initialized); + initialized = 0; + return ret; +} + +/* + * Parse OP and convert it into uprobe format, which is, +/-NUM(%gprREG). + * Possible variants of OP are: + * Format Example + * ------------------------- + * NUM(%rREG) 48(%r1) + * -NUM(%rREG) -48(%r1) + * +NUM(%rREG) +48(%r1) + * %rREG %r1 + */ +int __perf_sdt_arg_parse_op_s390(char *old_op, char **new_op) +{ + int ret, new_len; + regmatch_t rm[6]; + + *new_op = NULL; + ret = sdt_init_op_regex(); + if (ret) + return -EINVAL; + + if (!regexec(&sdt_op_regex1, old_op, ARRAY_SIZE(rm), rm, 0) || + !regexec(&sdt_op_regex2, old_op, ARRAY_SIZE(rm), rm, 0)) { + new_len = 1; /* NULL byte */ + new_len += (int)(rm[1].rm_eo - rm[1].rm_so); + *new_op = zalloc(new_len); + if (!*new_op) + return -ENOMEM; + + scnprintf(*new_op, new_len, "%.*s", + (int)(rm[1].rm_eo - rm[1].rm_so), old_op + rm[1].rm_so); + } else { + pr_debug4("Skipping unsupported SDT argument: %s\n", old_op); + return SDT_ARG_SKIP; + } + + return SDT_ARG_VALID; +} diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c index 5b8f34beb24e..f52b0e1f7fc7 100644 --- a/tools/perf/util/perf_regs.c +++ b/tools/perf/util/perf_regs.c @@ -23,6 +23,9 @@ int perf_sdt_arg_parse_op(uint16_t e_machine, char *old_op, char **new_op) case EM_X86_64: ret = __perf_sdt_arg_parse_op_x86(old_op, new_op); break; + case EM_S390: + ret = __perf_sdt_arg_parse_op_s390(old_op, new_op); + break; default: pr_debug("Unknown ELF machine %d, standard arguments parse will be skipped.\n", e_machine); diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 7c04700bf837..573f0d1dfe04 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -62,6 +62,7 @@ uint64_t __perf_reg_mask_s390(bool intr); const char *__perf_reg_name_s390(int id); uint64_t __perf_reg_ip_s390(void); uint64_t __perf_reg_sp_s390(void); +int __perf_sdt_arg_parse_op_s390(char *old_op, char **new_op); int __perf_sdt_arg_parse_op_x86(char *old_op, char **new_op); uint64_t __perf_reg_mask_x86(bool intr); diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index 98be2eb8f1f0..9a2023ceeefd 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -621,7 +621,7 @@ void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *p while ((pmu = scan_fn(pmu)) != NULL) len += perf_pmu__num_events(pmu); - aliases = zalloc(sizeof(struct sevent) * len); + aliases = calloc(len, sizeof(struct sevent)); if (!aliases) { pr_err("FATAL: not enough memory to print PMU events\n"); return; diff --git a/tools/perf/util/powerpc-vpadtl.c b/tools/perf/util/powerpc-vpadtl.c index d1c3396f182f..710f3093f3f9 100644 --- a/tools/perf/util/powerpc-vpadtl.c +++ b/tools/perf/util/powerpc-vpadtl.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include "color.h" @@ -182,7 +183,9 @@ static int powerpc_vpadtl_sample(struct powerpc_vpadtl_entry *record, { struct perf_sample sample; union perf_event event; + int ret; + perf_sample__init(&sample, /*all=*/true); sample.ip = be64_to_cpu(record->srr0); sample.period = 1; sample.cpu = cpu; @@ -198,12 +201,12 @@ static int powerpc_vpadtl_sample(struct powerpc_vpadtl_entry *record, event.sample.header.misc = sample.cpumode; event.sample.header.size = sizeof(struct perf_event_header); - if (perf_session__deliver_synth_event(vpa->session, &event, &sample)) { + ret = perf_session__deliver_synth_event(vpa->session, &event, &sample); + if (ret) pr_debug("Failed to create sample for dtl entry\n"); - return -1; - } - return 0; + perf_sample__exit(&sample); + return ret; } static int powerpc_vpadtl_get_buffer(struct powerpc_vpadtl_queue *vpaq) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 710e4620923e..34b4badd2c14 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -229,7 +228,7 @@ static int convert_exec_to_group(const char *exec, char **result) if (!exec_copy) return -ENOMEM; - ptr1 = basename(exec_copy); + ptr1 = (char *)perf_basename(exec_copy); if (!ptr1) { ret = -EINVAL; goto out; @@ -1850,7 +1849,7 @@ int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) /* Copy arguments and ensure return probe has no C argument */ pev->nargs = argc - 1; - pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); + pev->args = calloc(pev->nargs, sizeof(struct perf_probe_arg)); if (pev->args == NULL) { ret = -ENOMEM; goto out; @@ -2000,7 +1999,7 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev) } tev->nargs = argc - 2; - tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); + tev->args = calloc(tev->nargs, sizeof(struct probe_trace_arg)); if (tev->args == NULL) { ret = -ENOMEM; goto out; @@ -2373,7 +2372,7 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev, /* Convert trace_arg to probe_arg */ pev->nargs = tev->nargs; - pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); + pev->args = calloc(pev->nargs, sizeof(struct perf_probe_arg)); if (pev->args == NULL) return -ENOMEM; for (i = 0; i < tev->nargs && ret >= 0; i++) { @@ -2480,7 +2479,7 @@ int perf_probe_event__copy(struct perf_probe_event *dst, if (perf_probe_point__copy(&dst->point, &src->point) < 0) goto out_err; - dst->args = zalloc(sizeof(struct perf_probe_arg) * src->nargs); + dst->args = calloc(src->nargs, sizeof(struct perf_probe_arg)); if (!dst->args) goto out_err; dst->nargs = src->nargs; @@ -3179,7 +3178,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, } /* Setup result trace-probe-events */ - *tevs = zalloc(sizeof(*tev) * num_matched_functions); + *tevs = calloc(num_matched_functions, sizeof(*tev)); if (!*tevs) { ret = -ENOMEM; goto out; @@ -3251,8 +3250,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, tev->uprobes = pev->uprobes; tev->nargs = pev->nargs; if (tev->nargs) { - tev->args = zalloc(sizeof(struct probe_trace_arg) * - tev->nargs); + tev->args = calloc(tev->nargs, sizeof(struct probe_trace_arg)); if (tev->args == NULL) goto nomem_out; } @@ -3363,7 +3361,7 @@ static int try_to_find_absolute_address(struct perf_probe_event *pev, } tev->nargs = pev->nargs; - tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); + tev->args = calloc(tev->nargs, sizeof(struct probe_trace_arg)); if (!tev->args) goto errout; @@ -3549,7 +3547,7 @@ static int find_probe_trace_events_from_cache(struct perf_probe_event *pev, goto out; } - *tevs = zalloc(ret * sizeof(*tev)); + *tevs = calloc(ret, sizeof(*tev)); if (!*tevs) { ret = -ENOMEM; goto out; diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index f78c3bc3d601..4032572cbf55 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -414,7 +414,7 @@ int probe_cache_entry__get_event(struct probe_cache_entry *entry, if (ret > probe_conf.max_probes) return -E2BIG; - *tevs = zalloc(ret * sizeof(*tev)); + *tevs = calloc(ret, sizeof(*tev)); if (!*tevs) return -ENOMEM; diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 5ffd97ee4898..64328abeef8b 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -1305,7 +1305,7 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) tev->point.offset); /* Expand special probe argument if exist */ - args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS); + args = calloc(MAX_PROBE_ARGS, sizeof(struct perf_probe_arg)); if (args == NULL) { ret = -ENOMEM; goto end; @@ -1316,7 +1316,7 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) goto end; tev->nargs = ret; - tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); + tev->args = calloc(tev->nargs, sizeof(struct probe_trace_arg)); if (tev->args == NULL) { ret = -ENOMEM; goto end; @@ -1393,7 +1393,7 @@ int debuginfo__find_trace_events(struct debuginfo *dbg, int ret, i; /* Allocate result tevs array */ - *tevs = zalloc(sizeof(struct probe_trace_event) * tf.max_tevs); + *tevs = calloc(tf.max_tevs, sizeof(struct probe_trace_event)); if (*tevs == NULL) return -ENOMEM; @@ -1566,7 +1566,7 @@ int debuginfo__find_available_vars_at(struct debuginfo *dbg, int ret; /* Allocate result vls array */ - *vls = zalloc(sizeof(struct variable_list) * af.max_vls); + *vls = calloc(af.max_vls, sizeof(struct variable_list)); if (*vls == NULL) return -ENOMEM; diff --git a/tools/perf/util/sample.c b/tools/perf/util/sample.c index 8f82aaf1aab6..cf73329326d7 100644 --- a/tools/perf/util/sample.c +++ b/tools/perf/util/sample.c @@ -19,15 +19,22 @@ void perf_sample__init(struct perf_sample *sample, bool all) if (all) { memset(sample, 0, sizeof(*sample)); } else { + sample->evsel = NULL; sample->user_regs = NULL; sample->intr_regs = NULL; + sample->merged_callchain = false; + sample->callchain = NULL; } } void perf_sample__exit(struct perf_sample *sample) { - free(sample->user_regs); - free(sample->intr_regs); + zfree(&sample->user_regs); + zfree(&sample->intr_regs); + if (sample->merged_callchain) { + zfree(&sample->callchain); + sample->merged_callchain = false; + } } struct regs_dump *perf_sample__user_regs(struct perf_sample *sample) diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h index 3cce8dd202aa..e556c9b656ea 100644 --- a/tools/perf/util/sample.h +++ b/tools/perf/util/sample.h @@ -5,6 +5,7 @@ #include #include +struct evsel; struct machine; struct thread; @@ -70,58 +71,177 @@ struct aux_sample { }; struct simd_flags { - u8 arch:1, /* architecture (isa) */ - pred:2; /* predication */ + u8 arch: 2, /* architecture (isa) */ + pred: 3, /* predication */ + resv: 3; /* reserved */ }; /* simd architecture flags */ -#define SIMD_OP_FLAGS_ARCH_SVE 0x01 /* ARM SVE */ +enum simd_op_flags { + SIMD_OP_FLAGS_ARCH_NONE = 0x0, /* No SIMD operation */ + SIMD_OP_FLAGS_ARCH_SVE, /* Arm SVE */ + SIMD_OP_FLAGS_ARCH_SME, /* Arm SME */ + SIMD_OP_FLAGS_ARCH_ASE, /* Arm Advanced SIMD */ +}; /* simd predicate flags */ -#define SIMD_OP_FLAGS_PRED_PARTIAL 0x01 /* partial predicate */ -#define SIMD_OP_FLAGS_PRED_EMPTY 0x02 /* empty predicate */ +enum simd_pred_flags { + SIMD_OP_FLAGS_PRED_NONE = 0x0, /* Not available */ + SIMD_OP_FLAGS_PRED_PARTIAL, /* partial predicate */ + SIMD_OP_FLAGS_PRED_EMPTY, /* empty predicate */ + SIMD_OP_FLAGS_PRED_FULL, /* full predicate */ + SIMD_OP_FLAGS_PRED_DISABLED, /* disabled predicate */ +}; +/** + * struct perf_sample + * + * A sample is generally filled in by evlist__parse_sample/evsel__parse_sample + * which fills in the variables from a "union perf_event *event" which is data + * from a perf ring buffer or perf.data file. The "event" sample is variable in + * length as determined by the perf_event_attr (in the evsel) and details within + * the sample event itself. A struct perf_sample avoids needing to care about + * the variable length nature of the original event. + * + * To avoid being excessively large parts of the struct perf_sample are pointers + * into the original sample event. In general the lifetime of a struct + * perf_sample needs to be less than the "union perf_event *event" it was + * derived from. + * + * The struct regs_dump user_regs and intr_regs are lazily allocated again for + * size reasons, due to them holding a cache of looked up registers. The + * function pair of perf_sample__init and perf_sample__exit correctly initialize + * and clean up these values. + */ struct perf_sample { + /** @evsel: Backward reference to the evsel used when constructing the sample. */ + struct evsel *evsel; + /** @ip: The sample event PERF_SAMPLE_IP value. */ u64 ip; - u32 pid, tid; + /** @pid: The sample event PERF_SAMPLE_TID pid value. */ + u32 pid; + /** @tid: The sample event PERF_SAMPLE_TID tid value. */ + u32 tid; + /** @time: The sample event PERF_SAMPLE_TIME value. */ u64 time; + /** @addr: The sample event PERF_SAMPLE_ADDR value. */ u64 addr; + /** @id: The sample event PERF_SAMPLE_ID or PERF_SAMPLE_IDENTIFIER value. */ u64 id; + /** @stream_id: The sample event PERF_SAMPLE_STREAM_ID value. */ u64 stream_id; + /** @period: The sample event PERF_SAMPLE_PERIOD value. */ u64 period; + /** @weight: Data determined by PERF_SAMPLE_WEIGHT or PERF_SAMPLE_WEIGHT_STRUCT. */ u64 weight; + /** @transaction: The sample event PERF_SAMPLE_TRANSACTION value. */ u64 transaction; + /** @insn_cnt: Filled in and used by intel-pt. */ u64 insn_cnt; + /** @cyc_cnt: Filled in and used by intel-pt. */ u64 cyc_cnt; + /** @cpu: The sample event PERF_SAMPLE_CPU value. */ u32 cpu; + /** + * @raw_size: The size in bytes of raw data from PERF_SAMPLE_RAW. For + * alignment reasons this should always be sizeof(u32) + * followed by a multiple of sizeof(u64). + */ u32 raw_size; + /** @data_src: The sample event PERF_SAMPLE_DATA_SRC value. */ u64 data_src; + /** @phys_addr: The sample event PERF_SAMPLE_PHYS_ADDR value. */ u64 phys_addr; + /** @data_page_size: The sample event PERF_SAMPLE_DATA_PAGE_SIZE value. */ u64 data_page_size; + /** @code_page_size: The sample event PERF_SAMPLE_CODE_PAGE_SIZE value. */ u64 code_page_size; + /** @cgroup: The sample event PERF_SAMPLE_CGROUP value. */ u64 cgroup; + /** @flags: Extra flag data from auxiliary events like intel-pt. */ u32 flags; + /** @machine_pid: The guest machine pid derived from the sample id. */ u32 machine_pid; + /** @vcpu: The guest machine vcpu derived from the sample id. */ u32 vcpu; + /** + * @insn_len: Instruction length from auxiliary events like + * intel-pt. The instruction itself is held in insn. + */ u16 insn_len; - u8 cpumode; + /** @misc: The entire struct perf_event_header misc variable. */ u16 misc; + /** + * @ins_lat: Instruction latency information from weight2 in + * PERF_SAMPLE_WEIGHT_STRUCT or auxiliary events like + * intel-pt. + */ u16 ins_lat; - /** @weight3: On x86 holds retire_lat, on powerpc holds p_stage_cyc. */ + /** + * @weight3: From PERF_SAMPLE_WEIGHT_STRUCT. On x86 holds retire_lat, on + * powerpc holds p_stage_cyc. + */ u16 weight3; - bool no_hw_idx; /* No hw_idx collected in branch_stack */ - bool deferred_callchain; /* Has deferred user callchains */ + /** + * @cpumode: The cpumode from struct perf_event_header misc variable + * masked with CPUMODE_MASK. Gives user, kernel and hypervisor + * information. + */ + u8 cpumode; + /** + * @no_hw_idx: For PERF_SAMPLE_BRANCH_STACK, true when + * PERF_SAMPLE_BRANCH_HW_INDEX isn't set. + */ + bool no_hw_idx; + /** + * @deferred_callchain: When processing PERF_SAMPLE_CALLCHAIN a deferred + * user callchain marker was encountered. + */ + bool deferred_callchain; + /** + * @merged_callchain: A synthesized merged callchain that is allocated + * and needs freeing. + */ + bool merged_callchain; + /** + * @deferred_cookie: Identifier of the deferred callchain in the later + * PERF_RECORD_CALLCHAIN_DEFERRED event. + */ u64 deferred_cookie; + /** @insn: A copy of the sampled instruction filled in by perf_sample__fetch_insn. */ char insn[MAX_INSN]; + /** @raw_data: Pointer into the original event for PERF_SAMPLE_RAW data. */ void *raw_data; + /** + * @callchain: Pointer into the original event for PERF_SAMPLE_CALLCHAIN + * data. For deferred callchains this may be a copy that + * needs freeing, see sample__merge_deferred_callchain. + */ struct ip_callchain *callchain; + /** @branch_stack: Pointer into the original event for PERF_SAMPLE_BRANCH_STACK data. */ struct branch_stack *branch_stack; + /** + * @branch_stack_cntr: Pointer into the original event for + * PERF_SAMPLE_BRANCH_COUNTERS data. + */ u64 *branch_stack_cntr; + /** @user_regs: Values and pointers into the sample for PERF_SAMPLE_REGS_USER. */ struct regs_dump *user_regs; + /** @intr_regs: Values and pointers into the sample for PERF_SAMPLE_REGS_INTR. */ struct regs_dump *intr_regs; + /** @user_stack: Size and pointer into the sample for PERF_SAMPLE_STACK_USER. */ struct stack_dump user_stack; + /** + * @read: The sample event PERF_SAMPLE_READ counter values. The valid + * values depend on the attr.read_format PERF_FORMAT_ values. + */ struct sample_read read; + /** + * @aux_sample: Similar to raw data but with a 64-bit size and + * alignment, PERF_SAMPLE_AUX data. + */ struct aux_sample aux_sample; + /** @simd_flags: SIMD flag information from ARM SPE auxiliary events. */ struct simd_flags simd_flags; }; diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 2b0df7bd9a46..5a30caaec73e 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -1701,7 +1701,7 @@ static void python_process_stat(struct perf_stat_config *config, struct perf_cpu_map *cpus = counter->core.cpus; for (int thread = 0; thread < perf_thread_map__nr(threads); thread++) { - int idx; + unsigned int idx; struct perf_cpu cpu; perf_cpu_map__for_each_cpu(cpu, idx, cpus) { diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 4b465abfa36c..fe0de2a0277f 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -131,10 +131,17 @@ static int ordered_events__deliver_event(struct ordered_events *oe, { struct perf_session *session = container_of(oe, struct perf_session, ordered_events); + int ret = perf_session__deliver_event(session, event->event, + session->tool, event->file_offset, + event->file_path); - return perf_session__deliver_event(session, event->event, - session->tool, event->file_offset, - event->file_path); + if (ret) { + pr_err("%#" PRIx64 " [%#x]: ordered event processing failed (%d) for event of type: %s (%d)\n", + event->file_offset, event->event->header.size, ret, + perf_event__name(event->event->header.type), + event->event->header.type); + } + return ret; } struct perf_session *__perf_session__new(struct perf_data *data, @@ -1257,8 +1264,9 @@ static int deliver_sample_value(struct evlist *evlist, bool per_thread) { struct perf_sample_id *sid = evlist__id2sid(evlist, v->id); - struct evsel *evsel; + struct evsel *saved_evsel = sample->evsel; u64 *storage = NULL; + int ret; if (sid) { storage = perf_sample_id__get_period_storage(sid, sample->tid, per_thread); @@ -1282,8 +1290,10 @@ static int deliver_sample_value(struct evlist *evlist, if (!sample->period) return 0; - evsel = container_of(sid->evsel, struct evsel, core); - return tool->sample(tool, event, sample, evsel, machine); + sample->evsel = container_of(sid->evsel, struct evsel, core); + ret = tool->sample(tool, event, sample, sample->evsel, machine); + sample->evsel = saved_evsel; + return ret; } static int deliver_sample_group(struct evlist *evlist, @@ -1355,39 +1365,44 @@ static int evlist__deliver_deferred_callchain(struct evlist *evlist, struct machine *machine) { struct deferred_event *de, *tmp; - struct evsel *evsel; int ret = 0; if (!tool->merge_deferred_callchains) { - evsel = evlist__id2evsel(evlist, sample->id); - return tool->callchain_deferred(tool, event, sample, - evsel, machine); + struct evsel *saved_evsel = sample->evsel; + + sample->evsel = evlist__id2evsel(evlist, sample->id); + ret = tool->callchain_deferred(tool, event, sample, + sample->evsel, machine); + sample->evsel = saved_evsel; + return ret; } list_for_each_entry_safe(de, tmp, &evlist->deferred_samples, list) { struct perf_sample orig_sample; + perf_sample__init(&orig_sample, /*all=*/false); ret = evlist__parse_sample(evlist, de->event, &orig_sample); if (ret < 0) { pr_err("failed to parse original sample\n"); + perf_sample__exit(&orig_sample); break; } - if (sample->tid != orig_sample.tid) + if (sample->tid != orig_sample.tid) { + perf_sample__exit(&orig_sample); continue; + } if (event->callchain_deferred.cookie == orig_sample.deferred_cookie) sample__merge_deferred_callchain(&orig_sample, sample); else orig_sample.deferred_callchain = false; - evsel = evlist__id2evsel(evlist, orig_sample.id); + orig_sample.evsel = evlist__id2evsel(evlist, orig_sample.id); ret = evlist__deliver_sample(evlist, tool, de->event, - &orig_sample, evsel, machine); - - if (orig_sample.deferred_callchain) - free(orig_sample.callchain); + &orig_sample, orig_sample.evsel, machine); + perf_sample__exit(&orig_sample); list_del(&de->list); free(de->event); free(de); @@ -1408,22 +1423,24 @@ static int session__flush_deferred_samples(struct perf_session *session, struct evlist *evlist = session->evlist; struct machine *machine = &session->machines.host; struct deferred_event *de, *tmp; - struct evsel *evsel; int ret = 0; list_for_each_entry_safe(de, tmp, &evlist->deferred_samples, list) { struct perf_sample sample; + perf_sample__init(&sample, /*all=*/false); ret = evlist__parse_sample(evlist, de->event, &sample); if (ret < 0) { pr_err("failed to parse original sample\n"); + perf_sample__exit(&sample); break; } - evsel = evlist__id2evsel(evlist, sample.id); + sample.evsel = evlist__id2evsel(evlist, sample.id); ret = evlist__deliver_sample(evlist, tool, de->event, - &sample, evsel, machine); + &sample, sample.evsel, machine); + perf_sample__exit(&sample); list_del(&de->list); free(de->event); free(de); @@ -1446,8 +1463,12 @@ static int machines__deliver_event(struct machines *machines, dump_event(evlist, event, file_offset, sample, file_path); - evsel = evlist__id2evsel(evlist, sample->id); + if (!sample->evsel) + sample->evsel = evlist__id2evsel(evlist, sample->id); + else + assert(sample->evsel == evlist__id2evsel(evlist, sample->id)); + evsel = sample->evsel; machine = machines__find_for_cpumode(machines, event, sample); switch (event->header.type) { @@ -2110,8 +2131,10 @@ more: } if ((skip = perf_session__process_event(session, event, head, "pipe")) < 0) { - pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", - head, event->header.size, event->header.type); + pr_err("%#" PRIx64 " [%#x]: piped event processing failed for event of type: %s (%d)\n", + head, event->header.size, + perf_event__name(event->header.type), + event->header.type); err = -EINVAL; goto out_err; } @@ -2225,8 +2248,10 @@ static int __perf_session__process_decomp_events(struct perf_session *session) if (size < sizeof(struct perf_event_header) || (skip = perf_session__process_event(session, event, decomp->file_pos, decomp->file_path)) < 0) { - pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", - decomp->file_pos + decomp->head, event->header.size, event->header.type); + pr_err("%#" PRIx64 " [%#x]: decompress event processing failed for event of type: %s (%d)\n", + decomp->file_pos + decomp->head, event->header.size, + perf_event__name(event->header.type), + event->header.type); return -EINVAL; } @@ -2382,8 +2407,9 @@ reader__read_event(struct reader *rd, struct perf_session *session, if (size < sizeof(struct perf_event_header) || (skip = rd->process(session, event, rd->file_pos, rd->path)) < 0) { errno = -skip; - pr_err("%#" PRIx64 " [%#x]: failed to process type: %d [%m]\n", + pr_err("%#" PRIx64 " [%#x]: processing failed for event of type: %s (%d) [%m]\n", rd->file_offset + rd->head, event->header.size, + perf_event__name(event->header.type), event->header.type); err = skip; goto out; @@ -2533,7 +2559,7 @@ static int __perf_session__process_dir_events(struct perf_session *session) nr_readers++; } - rd = zalloc(nr_readers * sizeof(struct reader)); + rd = calloc(nr_readers, sizeof(struct reader)); if (!rd) return -ENOMEM; @@ -2557,7 +2583,7 @@ static int __perf_session__process_dir_events(struct perf_session *session) if (!data->dir.files[i].size) continue; rd[readers] = (struct reader) { - .fd = data->dir.files[i].fd, + .fd = perf_data_file__fd(&data->dir.files[i]), .path = data->dir.files[i].path, .data_size = data->dir.files[i].size, .data_offset = 0, @@ -2766,7 +2792,8 @@ struct evsel *perf_session__find_first_evtype(struct perf_session *session, int perf_session__cpu_bitmap(struct perf_session *session, const char *cpu_list, unsigned long *cpu_bitmap) { - int i, err = -1; + unsigned int i; + int err = -1; struct perf_cpu_map *map; int nr_cpus = min(perf_session__env(session)->nr_cpus_avail, MAX_NR_CPUS); struct perf_cpu cpu; diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 42d5cd7ef4e2..0020089cb13c 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include #include #include @@ -30,6 +31,7 @@ #include "time-utils.h" #include "cgroup.h" #include "machine.h" +#include "session.h" #include "trace-event.h" #include #include @@ -42,11 +44,11 @@ regex_t parent_regex; const char default_parent_pattern[] = "^sys_|^do_page_fault"; const char *parent_pattern = default_parent_pattern; const char *default_sort_order = "comm,dso,symbol"; -const char default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cycles"; +static const char default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cycles"; const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked,blocked,local_ins_lat,local_p_stage_cyc"; -const char default_top_sort_order[] = "dso,symbol"; -const char default_diff_sort_order[] = "dso,symbol"; -const char default_tracepoint_sort_order[] = "trace"; +static const char default_top_sort_order[] = "dso,symbol"; +static const char default_diff_sort_order[] = "dso,symbol"; +static const char default_tracepoint_sort_order[] = "trace"; const char *sort_order; const char *field_order; regex_t ignore_callees_regex; @@ -171,7 +173,7 @@ static int hist_entry__tgid_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%7d:%-*.*s", tgid, width, width, comm ?: ""); } -struct sort_entry sort_tgid = { +static struct sort_entry sort_tgid = { .se_header = " Tgid:Command", .se_cmp = sort__tgid_cmp, .se_snprintf = hist_entry__tgid_snprintf, @@ -193,8 +195,12 @@ static const char *hist_entry__get_simd_name(struct simd_flags *simd_flags) { u64 arch = simd_flags->arch; - if (arch & SIMD_OP_FLAGS_ARCH_SVE) + if (arch == SIMD_OP_FLAGS_ARCH_SVE) return "SVE"; + else if (arch == SIMD_OP_FLAGS_ARCH_SME) + return "SME"; + else if (arch == SIMD_OP_FLAGS_ARCH_ASE) + return "ASE"; else return "n/a"; } @@ -203,21 +209,26 @@ static int hist_entry__simd_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width __maybe_unused) { const char *name; + const char *pred_str = "."; if (!he->simd_flags.arch) return repsep_snprintf(bf, size, ""); name = hist_entry__get_simd_name(&he->simd_flags); - if (he->simd_flags.pred & SIMD_OP_FLAGS_PRED_EMPTY) - return repsep_snprintf(bf, size, "[e] %s", name); - else if (he->simd_flags.pred & SIMD_OP_FLAGS_PRED_PARTIAL) - return repsep_snprintf(bf, size, "[p] %s", name); + if (he->simd_flags.pred == SIMD_OP_FLAGS_PRED_EMPTY) + pred_str = "e"; + else if (he->simd_flags.pred == SIMD_OP_FLAGS_PRED_PARTIAL) + pred_str = "p"; + else if (he->simd_flags.pred == SIMD_OP_FLAGS_PRED_DISABLED) + pred_str = "d"; + else if (he->simd_flags.pred == SIMD_OP_FLAGS_PRED_FULL) + pred_str = "f"; - return repsep_snprintf(bf, size, "[.] %s", name); + return repsep_snprintf(bf, size, "[%s] %s", pred_str, name); } -struct sort_entry sort_simd = { +static struct sort_entry sort_simd = { .se_header = "Simd ", .se_cmp = sort__simd_cmp, .se_snprintf = hist_entry__simd_snprintf, @@ -265,6 +276,115 @@ struct sort_entry sort_comm = { .se_width_idx = HISTC_COMM, }; +/* --sort comm_nodigit */ + +size_t sort__comm_nodigit_len(struct hist_entry *entry) +{ + const char *comm = comm__str(entry->comm); + size_t index, len_nodigit = 0; + bool in_number = false; + + if (!comm) + return 0; + + for (index = 0; comm[index]; index++) { + if (!isdigit((unsigned char)comm[index])) { + in_number = false; + len_nodigit++; + } else if (!in_number) { + in_number = true; + len_nodigit += 3; /* */ + } + } + + return len_nodigit; +} + +static int64_t strcmp_nodigit(const char *left, const char *right) +{ + for (;;) { + while (*left && isdigit((unsigned char)*left)) + left++; + while (*right && isdigit((unsigned char)*right)) + right++; + if (*left == *right && !*left) { + return 0; + } else if (*left == *right) { + left++; + right++; + } else { + return (int64_t)((unsigned char)*left - (unsigned char)*right); + } + } +} + +static int64_t +sort__comm_nodigit_cmp(struct hist_entry *left, struct hist_entry *right) +{ + return strcmp_nodigit(comm__str(right->comm), comm__str(left->comm)); +} + +static int64_t +sort__comm_nodigit_collapse(struct hist_entry *left, struct hist_entry *right) +{ + return strcmp_nodigit(comm__str(right->comm), comm__str(left->comm)); +} + +static int64_t +sort__comm_nodigit_sort(struct hist_entry *left, struct hist_entry *right) +{ + return strcmp_nodigit(comm__str(right->comm), comm__str(left->comm)); +} + +static int hist_entry__comm_nodigit_snprintf(struct hist_entry *he, char *bf, + size_t size, unsigned int width) +{ + int ret = 0; + unsigned int print_len, printed = 0, start = 0, end = 0; + bool in_digit; + const char *comm = comm__str(he->comm), *print; + + while (printed < width && printed < size && comm[start]) { + in_digit = !!isdigit((unsigned char)comm[start]); + end = start + 1; + while (comm[end] && !!isdigit((unsigned char)comm[end]) == in_digit) + end++; + if (in_digit) { + print_len = 3; /* */ + print = ""; + } else { + print_len = end - start; + print = &comm[start]; + } + print_len = min(print_len, width - printed); + ret = repsep_snprintf(bf + printed, size - printed, "%-.*s", + print_len, print); + if (ret < 0) + return ret; + start = end; + printed += ret; + } + /* Pad to width if necessary */ + if (printed < width && printed < size) { + ret = repsep_snprintf(bf + printed, size - printed, "%-*.*s", + width - printed, width - printed, ""); + if (ret < 0) + return ret; + printed += ret; + } + return printed; +} + +struct sort_entry sort_comm_nodigit = { + .se_header = "CommandNoDigit", + .se_cmp = sort__comm_nodigit_cmp, + .se_collapse = sort__comm_nodigit_collapse, + .se_sort = sort__comm_nodigit_sort, + .se_snprintf = hist_entry__comm_nodigit_snprintf, + .se_filter = hist_entry__thread_filter, + .se_width_idx = HISTC_COMM_NODIGIT, +}; + /* --sort dso */ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) @@ -494,7 +614,7 @@ hist_entry__symoff_snprintf(struct hist_entry *he, char *bf, size_t size, unsign return repsep_snprintf(bf, size, "[%c] %s+0x%llx", he->level, sym->name, he->ip - sym->start); } -struct sort_entry sort_sym_offset = { +static struct sort_entry sort_sym_offset = { .se_header = "Symbol Offset", .se_cmp = sort__symoff_cmp, .se_sort = sort__symoff_sort, @@ -605,7 +725,7 @@ static int hist_entry__srcline_from_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_from); } -struct sort_entry sort_srcline_from = { +static struct sort_entry sort_srcline_from = { .se_header = "From Source:Line", .se_cmp = sort__srcline_from_cmp, .se_collapse = sort__srcline_from_collapse, @@ -653,7 +773,7 @@ static int hist_entry__srcline_to_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_to); } -struct sort_entry sort_srcline_to = { +static struct sort_entry sort_srcline_to = { .se_header = "To Source:Line", .se_cmp = sort__srcline_to_cmp, .se_collapse = sort__srcline_to_collapse, @@ -689,7 +809,7 @@ static int hist_entry__sym_ipc_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-*s", width, tmp); } -struct sort_entry sort_sym_ipc = { +static struct sort_entry sort_sym_ipc = { .se_header = "IPC [IPC Coverage]", .se_cmp = sort__sym_cmp, .se_snprintf = hist_entry__sym_ipc_snprintf, @@ -707,7 +827,7 @@ static int hist_entry__sym_ipc_null_snprintf(struct hist_entry *he return repsep_snprintf(bf, size, "%-*s", width, tmp); } -struct sort_entry sort_sym_ipc_null = { +static struct sort_entry sort_sym_ipc_null = { .se_header = "IPC [IPC Coverage]", .se_cmp = sort__sym_cmp, .se_snprintf = hist_entry__sym_ipc_null_snprintf, @@ -740,7 +860,7 @@ static int hist_entry__callchain_branch_predicted_snprintf( return repsep_snprintf(bf, size, "%-*.*s", width, width, str); } -struct sort_entry sort_callchain_branch_predicted = { +static struct sort_entry sort_callchain_branch_predicted = { .se_header = "Predicted", .se_cmp = sort__callchain_branch_predicted_cmp, .se_snprintf = hist_entry__callchain_branch_predicted_snprintf, @@ -770,7 +890,7 @@ static int hist_entry__callchain_branch_abort_snprintf(struct hist_entry *he, return repsep_snprintf(bf, size, "%-*.*s", width, width, str); } -struct sort_entry sort_callchain_branch_abort = { +static struct sort_entry sort_callchain_branch_abort = { .se_header = "Abort", .se_cmp = sort__callchain_branch_abort_cmp, .se_snprintf = hist_entry__callchain_branch_abort_snprintf, @@ -803,7 +923,7 @@ static int hist_entry__callchain_branch_cycles_snprintf(struct hist_entry *he, return repsep_snprintf(bf, size, "%-*.*s", width, width, str); } -struct sort_entry sort_callchain_branch_cycles = { +static struct sort_entry sort_callchain_branch_cycles = { .se_header = "Cycles", .se_cmp = sort__callchain_branch_cycles_cmp, .se_snprintf = hist_entry__callchain_branch_cycles_snprintf, @@ -870,7 +990,7 @@ static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-.*s", width, he->srcfile); } -struct sort_entry sort_srcfile = { +static struct sort_entry sort_srcfile = { .se_header = "Source File", .se_cmp = sort__srcfile_cmp, .se_collapse = sort__srcfile_collapse, @@ -922,7 +1042,7 @@ static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu); } -struct sort_entry sort_cpu = { +static struct sort_entry sort_cpu = { .se_header = "CPU", .se_cmp = sort__cpu_cmp, .se_snprintf = hist_entry__cpu_snprintf, @@ -953,7 +1073,7 @@ static int hist_entry__parallelism_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%*d", width, he->parallelism); } -struct sort_entry sort_parallelism = { +static struct sort_entry sort_parallelism = { .se_header = "Parallelism", .se_cmp = sort__parallelism_cmp, .se_filter = hist_entry__parallelism_filter, @@ -994,7 +1114,7 @@ static int hist_entry__cgroup_id_snprintf(struct hist_entry *he, he->cgroup_id.ino); } -struct sort_entry sort_cgroup_id = { +static struct sort_entry sort_cgroup_id = { .se_header = "cgroup id (dev/inode)", .se_cmp = sort__cgroup_id_cmp, .se_snprintf = hist_entry__cgroup_id_snprintf, @@ -1027,7 +1147,7 @@ static int hist_entry__cgroup_snprintf(struct hist_entry *he, return repsep_snprintf(bf, size, "%s", cgrp_name); } -struct sort_entry sort_cgroup = { +static struct sort_entry sort_cgroup = { .se_header = "Cgroup", .se_cmp = sort__cgroup_cmp, .se_snprintf = hist_entry__cgroup_snprintf, @@ -1058,7 +1178,7 @@ static int hist_entry__socket_filter(struct hist_entry *he, int type, const void return sk >= 0 && he->socket != sk; } -struct sort_entry sort_socket = { +static struct sort_entry sort_socket = { .se_header = "Socket", .se_cmp = sort__socket_cmp, .se_snprintf = hist_entry__socket_snprintf, @@ -1089,7 +1209,7 @@ static int hist_entry__time_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-.*s", width, he_time); } -struct sort_entry sort_time = { +static struct sort_entry sort_time = { .se_header = "Time", .se_cmp = sort__time_cmp, .se_snprintf = hist_entry__time_snprintf, @@ -1158,7 +1278,7 @@ static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-.*s", width, he->trace_output); } -struct sort_entry sort_trace = { +static struct sort_entry sort_trace = { .se_header = "Trace output", .se_cmp = sort__trace_cmp, .se_snprintf = hist_entry__trace_snprintf, @@ -1453,7 +1573,7 @@ sort__addr_to_cmp(struct hist_entry *left, struct hist_entry *right) return _sort__addr_cmp(to_l->addr, to_r->addr); } -struct sort_entry sort_addr_from = { +static struct sort_entry sort_addr_from = { .se_header = "Source Address", .se_cmp = sort__addr_from_cmp, .se_snprintf = hist_entry__addr_from_snprintf, @@ -1461,7 +1581,7 @@ struct sort_entry sort_addr_from = { .se_width_idx = HISTC_ADDR_FROM, }; -struct sort_entry sort_addr_to = { +static struct sort_entry sort_addr_to = { .se_header = "Target Address", .se_cmp = sort__addr_to_cmp, .se_snprintf = hist_entry__addr_to_snprintf, @@ -1518,7 +1638,7 @@ static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf, he->branch_info->flags.cycles); } -struct sort_entry sort_cycles = { +static struct sort_entry sort_cycles = { .se_header = "Basic Block Cycles", .se_cmp = sort__cycles_cmp, .se_snprintf = hist_entry__cycles_snprintf, @@ -1808,7 +1928,7 @@ static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf, return _hist_entry__sym_snprintf(ms, addr, level, bf, size, width); } -struct sort_entry sort_mispredict = { +static struct sort_entry sort_mispredict = { .se_header = "Branch Mispredicted", .se_cmp = sort__mispredict_cmp, .se_snprintf = hist_entry__mispredict_snprintf, @@ -1827,7 +1947,7 @@ static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-*llu", width, he->weight); } -struct sort_entry sort_local_weight = { +static struct sort_entry sort_local_weight = { .se_header = "Local Weight", .se_cmp = sort__weight_cmp, .se_snprintf = hist_entry__local_weight_snprintf, @@ -1841,7 +1961,7 @@ static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf, he->weight * he->stat.nr_events); } -struct sort_entry sort_global_weight = { +static struct sort_entry sort_global_weight = { .se_header = "Weight", .se_cmp = sort__weight_cmp, .se_snprintf = hist_entry__global_weight_snprintf, @@ -1860,7 +1980,7 @@ static int hist_entry__local_ins_lat_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-*u", width, he->ins_lat); } -struct sort_entry sort_local_ins_lat = { +static struct sort_entry sort_local_ins_lat = { .se_header = "Local INSTR Latency", .se_cmp = sort__ins_lat_cmp, .se_snprintf = hist_entry__local_ins_lat_snprintf, @@ -1874,7 +1994,7 @@ static int hist_entry__global_ins_lat_snprintf(struct hist_entry *he, char *bf, he->ins_lat * he->stat.nr_events); } -struct sort_entry sort_global_ins_lat = { +static struct sort_entry sort_global_ins_lat = { .se_header = "INSTR Latency", .se_cmp = sort__ins_lat_cmp, .se_snprintf = hist_entry__global_ins_lat_snprintf, @@ -1900,70 +2020,70 @@ static int hist_entry__p_stage_cyc_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-*u", width, he->weight3); } -struct sort_entry sort_local_p_stage_cyc = { +static struct sort_entry sort_local_p_stage_cyc = { .se_header = "Local Pipeline Stage Cycle", .se_cmp = sort__p_stage_cyc_cmp, .se_snprintf = hist_entry__p_stage_cyc_snprintf, .se_width_idx = HISTC_LOCAL_P_STAGE_CYC, }; -struct sort_entry sort_global_p_stage_cyc = { +static struct sort_entry sort_global_p_stage_cyc = { .se_header = "Pipeline Stage Cycle", .se_cmp = sort__p_stage_cyc_cmp, .se_snprintf = hist_entry__global_p_stage_cyc_snprintf, .se_width_idx = HISTC_GLOBAL_P_STAGE_CYC, }; -struct sort_entry sort_mem_daddr_sym = { +static struct sort_entry sort_mem_daddr_sym = { .se_header = "Data Symbol", .se_cmp = sort__daddr_cmp, .se_snprintf = hist_entry__daddr_snprintf, .se_width_idx = HISTC_MEM_DADDR_SYMBOL, }; -struct sort_entry sort_mem_iaddr_sym = { +static struct sort_entry sort_mem_iaddr_sym = { .se_header = "Code Symbol", .se_cmp = sort__iaddr_cmp, .se_snprintf = hist_entry__iaddr_snprintf, .se_width_idx = HISTC_MEM_IADDR_SYMBOL, }; -struct sort_entry sort_mem_daddr_dso = { +static struct sort_entry sort_mem_daddr_dso = { .se_header = "Data Object", .se_cmp = sort__dso_daddr_cmp, .se_snprintf = hist_entry__dso_daddr_snprintf, .se_width_idx = HISTC_MEM_DADDR_DSO, }; -struct sort_entry sort_mem_locked = { +static struct sort_entry sort_mem_locked = { .se_header = "Locked", .se_cmp = sort__locked_cmp, .se_snprintf = hist_entry__locked_snprintf, .se_width_idx = HISTC_MEM_LOCKED, }; -struct sort_entry sort_mem_tlb = { +static struct sort_entry sort_mem_tlb = { .se_header = "TLB access", .se_cmp = sort__tlb_cmp, .se_snprintf = hist_entry__tlb_snprintf, .se_width_idx = HISTC_MEM_TLB, }; -struct sort_entry sort_mem_lvl = { +static struct sort_entry sort_mem_lvl = { .se_header = "Memory access", .se_cmp = sort__lvl_cmp, .se_snprintf = hist_entry__lvl_snprintf, .se_width_idx = HISTC_MEM_LVL, }; -struct sort_entry sort_mem_snoop = { +static struct sort_entry sort_mem_snoop = { .se_header = "Snoop", .se_cmp = sort__snoop_cmp, .se_snprintf = hist_entry__snoop_snprintf, .se_width_idx = HISTC_MEM_SNOOP, }; -struct sort_entry sort_mem_dcacheline = { +static struct sort_entry sort_mem_dcacheline = { .se_header = "Data Cacheline", .se_cmp = sort__dcacheline_cmp, .se_snprintf = hist_entry__dcacheline_snprintf, @@ -1998,7 +2118,7 @@ static int hist_entry__blocked_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%.*s", width, out); } -struct sort_entry sort_mem_blocked = { +static struct sort_entry sort_mem_blocked = { .se_header = "Blocked", .se_cmp = sort__blocked_cmp, .se_snprintf = hist_entry__blocked_snprintf, @@ -2039,7 +2159,7 @@ static int hist_entry__phys_daddr_snprintf(struct hist_entry *he, char *bf, return width; } -struct sort_entry sort_mem_phys_daddr = { +static struct sort_entry sort_mem_phys_daddr = { .se_header = "Data Physical Address", .se_cmp = sort__phys_daddr_cmp, .se_snprintf = hist_entry__phys_daddr_snprintf, @@ -2068,7 +2188,7 @@ static int hist_entry__data_page_size_snprintf(struct hist_entry *he, char *bf, get_page_size_name(mem_info__daddr(he->mem_info)->data_page_size, str)); } -struct sort_entry sort_mem_data_page_size = { +static struct sort_entry sort_mem_data_page_size = { .se_header = "Data Page Size", .se_cmp = sort__data_page_size_cmp, .se_snprintf = hist_entry__data_page_size_snprintf, @@ -2093,7 +2213,7 @@ static int hist_entry__code_page_size_snprintf(struct hist_entry *he, char *bf, get_page_size_name(he->code_page_size, str)); } -struct sort_entry sort_code_page_size = { +static struct sort_entry sort_code_page_size = { .se_header = "Code Page Size", .se_cmp = sort__code_page_size_cmp, .se_snprintf = hist_entry__code_page_size_snprintf, @@ -2125,7 +2245,7 @@ static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-*s", width, out); } -struct sort_entry sort_abort = { +static struct sort_entry sort_abort = { .se_header = "Transaction abort", .se_cmp = sort__abort_cmp, .se_snprintf = hist_entry__abort_snprintf, @@ -2157,7 +2277,7 @@ static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-*s", width, out); } -struct sort_entry sort_in_tx = { +static struct sort_entry sort_in_tx = { .se_header = "Branch in transaction", .se_cmp = sort__in_tx_cmp, .se_snprintf = hist_entry__in_tx_snprintf, @@ -2229,7 +2349,7 @@ static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-*s", width, buf); } -struct sort_entry sort_transaction = { +static struct sort_entry sort_transaction = { .se_header = "Transaction ", .se_cmp = sort__transaction_cmp, .se_snprintf = hist_entry__transaction_snprintf, @@ -2268,7 +2388,7 @@ static int hist_entry__sym_size_snprintf(struct hist_entry *he, char *bf, return _hist_entry__sym_size_snprintf(he->ms.sym, bf, size, width); } -struct sort_entry sort_sym_size = { +static struct sort_entry sort_sym_size = { .se_header = "Symbol size", .se_cmp = sort__sym_size_cmp, .se_snprintf = hist_entry__sym_size_snprintf, @@ -2307,7 +2427,7 @@ static int hist_entry__dso_size_snprintf(struct hist_entry *he, char *bf, return _hist_entry__dso_size_snprintf(he->ms.map, bf, size, width); } -struct sort_entry sort_dso_size = { +static struct sort_entry sort_dso_size = { .se_header = "DSO size", .se_cmp = sort__dso_size_cmp, .se_snprintf = hist_entry__dso_size_snprintf, @@ -2344,7 +2464,7 @@ static int hist_entry__addr_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-#*llx", width, ip); } -struct sort_entry sort_addr = { +static struct sort_entry sort_addr = { .se_header = "Address", .se_cmp = sort__addr_cmp, .se_snprintf = hist_entry__addr_snprintf, @@ -2462,7 +2582,7 @@ static int hist_entry__typeoff_snprintf(struct hist_entry *he, char *bf, he->mem_type_off, buf); } -struct sort_entry sort_type_offset = { +static struct sort_entry sort_type_offset = { .se_header = "Data Type Offset", .se_cmp = sort__type_cmp, .se_collapse = sort__typeoff_sort, @@ -2474,7 +2594,26 @@ struct sort_entry sort_type_offset = { /* --sort typecln */ -#define DEFAULT_CACHELINE_SIZE 64 +static int +hist_entry__cln_size(struct hist_entry *he) +{ + int ret = 0; + + if (he && he->hists) { + struct evsel *evsel = hists_to_evsel(he->hists); + + if (evsel) { + struct perf_session *session = evsel__session(evsel); + + ret = session->header.env.cln_size; + } + } + + if (ret < 1) + ret = DEFAULT_CACHELINE_SIZE; // avoid div/0 later + + return ret; +} static int64_t sort__typecln_sort(struct hist_entry *left, struct hist_entry *right) @@ -2482,11 +2621,9 @@ sort__typecln_sort(struct hist_entry *left, struct hist_entry *right) struct annotated_data_type *left_type = left->mem_type; struct annotated_data_type *right_type = right->mem_type; int64_t left_cln, right_cln; + int64_t cln_size_left = hist_entry__cln_size(left); + int64_t cln_size_right = hist_entry__cln_size(right); int64_t ret; - int cln_size = cacheline_size(); - - if (cln_size == 0) - cln_size = DEFAULT_CACHELINE_SIZE; if (!left_type) { sort__type_init(left); @@ -2502,8 +2639,8 @@ sort__typecln_sort(struct hist_entry *left, struct hist_entry *right) if (ret) return ret; - left_cln = left->mem_type_off / cln_size; - right_cln = right->mem_type_off / cln_size; + left_cln = left->mem_type_off / cln_size_left; + right_cln = right->mem_type_off / cln_size_right; return left_cln - right_cln; } @@ -2511,16 +2648,13 @@ static int hist_entry__typecln_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width __maybe_unused) { struct annotated_data_type *he_type = he->mem_type; - int cln_size = cacheline_size(); - - if (cln_size == 0) - cln_size = DEFAULT_CACHELINE_SIZE; + int cln_size = hist_entry__cln_size(he); return repsep_snprintf(bf, size, "%s: cache-line %d", he_type->self.type_name, he->mem_type_off / cln_size); } -struct sort_entry sort_type_cacheline = { +static struct sort_entry sort_type_cacheline = { .se_header = "Data Type Cacheline", .se_cmp = sort__type_cmp, .se_collapse = sort__typecln_sort, @@ -2583,6 +2717,7 @@ static struct sort_dimension common_sort_dimensions[] = { DIM(SORT_PID, "pid", sort_thread), DIM(SORT_TGID, "tgid", sort_tgid), DIM(SORT_COMM, "comm", sort_comm), + DIM(SORT_COMM_NODIGIT, "comm_nodigit", sort_comm_nodigit), DIM(SORT_DSO, "dso", sort_dso), DIM(SORT_SYM, "symbol", sort_sym), DIM(SORT_PARENT, "parent", sort_parent), @@ -3579,6 +3714,8 @@ static int __sort_dimension__update(struct sort_dimension *sd, list->thread = 1; } else if (sd->entry == &sort_comm) { list->comm = 1; + } else if (sd->entry == &sort_comm_nodigit) { + list->comm_nodigit = list->comm = 1; } else if (sd->entry == &sort_type_offset) { symbol_conf.annotate_data_member = true; } else if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) { @@ -4040,6 +4177,7 @@ static bool get_elide(int idx, FILE *output) case HISTC_DSO: return __get_elide(symbol_conf.dso_list, "dso", output); case HISTC_COMM: + case HISTC_COMM_NODIGIT: return __get_elide(symbol_conf.comm_list, "comm", output); default: break; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index d7787958e06b..c962e77e4b93 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -43,6 +43,7 @@ enum sort_type { /* common sort keys */ SORT_PID, SORT_COMM, + SORT_COMM_NODIGIT, SORT_DSO, SORT_SYM, SORT_PARENT, @@ -158,4 +159,5 @@ sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right); int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r); char *hist_entry__srcline(struct hist_entry *he); +size_t sort__comm_nodigit_len(struct hist_entry *entry); #endif /* __PERF_SORT_H */ diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 9be42f398440..db164d258163 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -8,10 +8,12 @@ #include "symbol.h" #include "libdw.h" #include "debug.h" +#include "util.h" #include #include #include +#include bool srcline_full_filename; @@ -73,14 +75,6 @@ int inline_list__append_tail(struct symbol *symbol, char *srcline, struct inline return 0; } -/* basename version that takes a const input string */ -static const char *gnu_basename(const char *path) -{ - const char *base = strrchr(path, '/'); - - return base ? base + 1 : path; -} - char *srcline_from_fileline(const char *file, unsigned int line) { char *srcline; @@ -89,7 +83,7 @@ char *srcline_from_fileline(const char *file, unsigned int line) return NULL; if (!srcline_full_filename) - file = gnu_basename(file); + file = perf_basename(file); if (asprintf(&srcline, "%s:%u", file, line) < 0) return NULL; diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c index dc2b66855f6c..993f4c4b8f44 100644 --- a/tools/perf/util/stat-display.c +++ b/tools/perf/util/stat-display.c @@ -897,7 +897,7 @@ static bool should_skip_zero_counter(struct perf_stat_config *config, const struct aggr_cpu_id *id) { struct perf_cpu cpu; - int idx; + unsigned int idx; /* * Skip unsupported default events when not verbose. (default events @@ -1125,7 +1125,7 @@ static void print_no_aggr_metric(struct perf_stat_config *config, struct evlist *evlist, struct outstate *os) { - int all_idx; + unsigned int all_idx; struct perf_cpu cpu; perf_cpu_map__for_each_cpu(cpu, all_idx, evlist->core.user_requested_cpus) { diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index 59d2cd4f2188..bc2d44df7baf 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c @@ -13,7 +13,6 @@ #include "metricgroup.h" #include "cgroup.h" #include "units.h" -#include #include "iostat.h" #include "util/hashmap.h" #include "tool_pmu.h" diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 976a06e63252..14d169e22e8f 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -246,9 +246,11 @@ void evlist__reset_prev_raw_counts(struct evlist *evlist) static void evsel__copy_prev_raw_counts(struct evsel *evsel) { - int idx, nthreads = perf_thread_map__nr(evsel->core.threads); + int nthreads = perf_thread_map__nr(evsel->core.threads); for (int thread = 0; thread < nthreads; thread++) { + unsigned int idx; + perf_cpu_map__for_each_idx(idx, evsel__cpus(evsel)) { *perf_counts(evsel->counts, idx, thread) = *perf_counts(evsel->prev_raw_counts, idx, thread); @@ -580,7 +582,7 @@ static void evsel__update_percore_stats(struct evsel *evsel, struct aggr_cpu_id struct perf_counts_values counts = { 0, }; struct aggr_cpu_id id; struct perf_cpu cpu; - int idx; + unsigned int idx; /* collect per-core counts */ perf_cpu_map__for_each_cpu(cpu, idx, evsel->core.cpus) { @@ -617,7 +619,7 @@ static void evsel__process_percore(struct evsel *evsel) struct perf_stat_evsel *ps = evsel->stats; struct aggr_cpu_id core_id; struct perf_cpu cpu; - int idx; + unsigned int idx; if (!evsel->percore) return; diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index b1d259f590e9..e360e7736c7b 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -726,7 +726,8 @@ static void scan_core_topology(int *map, struct topology *t, int nr_cpus) static int str_to_bitmap(char *s, cpumask_t *b, int nr_cpus) { - int idx, ret = 0; + unsigned int idx; + int ret = 0; struct perf_cpu_map *map; struct perf_cpu cpu; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 76912c62b6a0..7afa8a117139 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -372,10 +372,8 @@ static bool get_plt_sizes(struct dso *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr_plt, *plt_entry_size = 12; return true; case EM_AARCH64: - *plt_header_size = 32; - *plt_entry_size = 16; - return true; case EM_LOONGARCH: + case EM_RISCV: *plt_header_size = 32; *plt_entry_size = 16; return true; @@ -1054,15 +1052,15 @@ void symsrc__destroy(struct symsrc *ss) close(ss->fd); } -bool elf__needs_adjust_symbols(GElf_Ehdr ehdr) +static bool elf__needs_adjust_symbols(const GElf_Ehdr *ehdr) { /* * Usually vmlinux is an ELF file with type ET_EXEC for most * architectures; except Arm64 kernel is linked with option * '-share', so need to check type ET_DYN. */ - return ehdr.e_type == ET_EXEC || ehdr.e_type == ET_REL || - ehdr.e_type == ET_DYN; + return ehdr->e_type == ET_EXEC || ehdr->e_type == ET_REL || + ehdr->e_type == ET_DYN; } static Elf *read_gnu_debugdata(struct dso *dso, Elf *elf, const char *name, int *fd_ret) @@ -1235,7 +1233,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, if (dso__kernel(dso) == DSO_SPACE__USER) ss->adjust_symbols = true; else - ss->adjust_symbols = elf__needs_adjust_symbols(ehdr); + ss->adjust_symbols = elf__needs_adjust_symbols(&ehdr); ss->name = strdup(name); if (!ss->name) { @@ -1356,8 +1354,12 @@ static int dso__process_kernel_symbol(struct dso *dso, struct map *map, char dso_name[PATH_MAX]; /* Adjust symbol to map to file offset */ - if (adjust_kernel_syms) - sym->st_value -= shdr->sh_addr - shdr->sh_offset; + if (adjust_kernel_syms) { + if (dso__rel(dso)) + sym->st_value += shdr->sh_offset; + else + sym->st_value -= shdr->sh_addr - shdr->sh_offset; + } if (strcmp(section_name, (dso__short_name(curr_dso) + dso__short_name_len(dso))) == 0) return 0; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 8662001e1e25..fcaeeddbbb6b 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -26,7 +26,6 @@ #include "demangle-rust-v0.h" #include "dso.h" #include "util.h" // lsdir() -#include "debug.h" #include "event.h" #include "machine.h" #include "map.h" @@ -66,9 +65,11 @@ struct symbol_conf symbol_conf = { .time_quantum = 100 * NSEC_PER_MSEC, /* 100ms */ .show_hist_headers = true, .symfs = "", + .symfs_layout_flat = false, .event_group = true, .inline_name = true, .res_sample = 0, + .addr2line_timeout_ms = 5 * 1000, }; struct map_list_node { @@ -2363,7 +2364,8 @@ static int setup_parallelism_bitmap(void) { struct perf_cpu_map *map; struct perf_cpu cpu; - int i, err = -1; + unsigned int i; + int err = -1; if (symbol_conf.parallelism_list_str == NULL) return 0; @@ -2491,16 +2493,42 @@ int symbol__config_symfs(const struct option *opt __maybe_unused, const char *dir, int unset __maybe_unused) { char *bf = NULL; + const char *layout_str; + char *dir_copy; int ret; - symbol_conf.symfs = strdup(dir); - if (symbol_conf.symfs == NULL) - return -ENOMEM; + layout_str = strrchr(dir, ','); + if (layout_str) { + size_t dir_len = layout_str - dir; + + dir_copy = strndup(dir, dir_len); + if (dir_copy == NULL) + return -ENOMEM; + + symbol_conf.symfs = dir_copy; + + layout_str++; + if (!strcmp(layout_str, "flat")) + symbol_conf.symfs_layout_flat = true; + else if (!strcmp(layout_str, "hierarchy")) + symbol_conf.symfs_layout_flat = false; + else { + pr_err("Invalid layout: '%s', use 'hierarchy' or 'flat'\n", + layout_str); + free(dir_copy); + return -EINVAL; + } + } else { + symbol_conf.symfs = strdup(dir); + if (symbol_conf.symfs == NULL) + return -ENOMEM; + symbol_conf.symfs_layout_flat = false; + } /* skip the locally configured cache if a symfs is given, and * config buildid dir to symfs/.debug */ - ret = asprintf(&bf, "%s/%s", dir, ".debug"); + ret = asprintf(&bf, "%s/%s", symbol_conf.symfs, ".debug"); if (ret < 0) return -ENOMEM; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 3fb5d146d9b1..bd6eb90c8668 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -9,10 +9,12 @@ #include #include #include +#include #include "addr_location.h" #include "path.h" #include "symbol_conf.h" #include "spark.h" +#include "util.h" #ifdef HAVE_LIBELF_SUPPORT #include @@ -96,6 +98,9 @@ struct intlist; static inline int __symbol__join_symfs(char *bf, size_t size, const char *path) { + if (symbol_conf.symfs_layout_flat) + return path__join(bf, size, symbol_conf.symfs, perf_basename(path)); + return path__join(bf, size, symbol_conf.symfs, path); } @@ -169,6 +174,11 @@ size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); size_t symbol__fprintf(struct symbol *sym, FILE *fp); bool symbol__restricted_filename(const char *filename, const char *restricted_filename); + +#define SYMFS_HELP "setup root directory which contains debug files:\n" \ + "\t\t\t\t" "directory:\tLook for files with symbols relative to this directory.\n" \ + "\t\t\t\t" "layout: \tLayout of files, 'hierarchy' matches full path (default), 'flat' only matches base name.\n" + int symbol__config_symfs(const struct option *opt __maybe_unused, const char *dir, int unset __maybe_unused); @@ -217,7 +227,6 @@ int setup_intlist(struct intlist **list, const char *list_str, const char *list_name); #ifdef HAVE_LIBELF_SUPPORT -bool elf__needs_adjust_symbols(GElf_Ehdr ehdr); void arch__sym_update(struct symbol *s, GElf_Sym *sym); #endif diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h index 71bb17372a6c..6cd454d7c98e 100644 --- a/tools/perf/util/symbol_conf.h +++ b/tools/perf/util/symbol_conf.h @@ -51,7 +51,7 @@ struct symbol_conf { report_block, report_individual_block, inline_name, - disable_add2line_warn, + addr2line_disable_warn, no_buildid_mmap2, guest_code, lazy_load_kernel_maps, @@ -80,6 +80,7 @@ struct symbol_conf { *bt_stop_list_str; const char *addr2line_path; enum a2l_style addr2line_style[MAX_A2L_STYLE]; + int addr2line_timeout_ms; unsigned long time_quantum; struct strlist *dso_list, *comm_list, @@ -93,6 +94,7 @@ struct symbol_conf { *tid_list, *addr_list; const char *symfs; + bool symfs_layout_flat; int res_sample; int pad_output_len_dso; int group_sort_idx; diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index ddf1cbda1902..85bee747f4cd 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -1266,7 +1266,7 @@ static void synthesize_cpus(struct synthesize_cpu_map_data *data) static void synthesize_mask(struct synthesize_cpu_map_data *data) { - int idx; + unsigned int idx; struct perf_cpu cpu; /* Due to padding, the 4bytes per entry mask variant is always smaller. */ diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h index 84ebb9c940c6..bc2bff9c6842 100644 --- a/tools/perf/util/target.h +++ b/tools/perf/util/target.h @@ -49,22 +49,22 @@ uid_t parse_uid(const char *str); int target__strerror(struct target *target, int errnum, char *buf, size_t buflen); -static inline bool target__has_task(struct target *target) +static inline bool target__has_task(const struct target *target) { return target->tid || target->pid; } -static inline bool target__has_cpu(struct target *target) +static inline bool target__has_cpu(const struct target *target) { return target->system_wide || target->cpu_list; } -static inline bool target__none(struct target *target) +static inline bool target__none(const struct target *target) { return !target__has_task(target) && !target__has_cpu(target); } -static inline bool target__enable_on_exec(struct target *target) +static inline bool target__enable_on_exec(const struct target *target) { /* * Normally enable_on_exec should be set if: @@ -75,12 +75,12 @@ static inline bool target__enable_on_exec(struct target *target) return target__none(target) && !target->initial_delay; } -static inline bool target__has_per_thread(struct target *target) +static inline bool target__has_per_thread(const struct target *target) { return target->system_wide && target->per_thread; } -static inline bool target__uses_dummy_map(struct target *target) +static inline bool target__uses_dummy_map(const struct target *target) { bool use_dummy = false; diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index 5b39ce21e333..87d496e9dfa6 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -25,7 +25,6 @@ #include #include #include -#include #ifndef REMOTE_UNWIND_LIBUNWIND #include #include diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 8b893de35f77..25849434f0a4 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -77,8 +77,6 @@ bool sysctl__nmi_watchdog_enabled(void) return nmi_watchdog; } -bool test_attr__enabled; - bool exclude_GH_default; bool perf_host = true; @@ -547,3 +545,11 @@ int scandirat(int dirfd, const char *dirp, return err; } #endif + +/* basename version that takes a const input string */ +const char *perf_basename(const char *path) +{ + const char *base = strrchr(path, '/'); + + return base ? base + 1 : path; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 394dbfa944ac..87a0818a8c76 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -30,7 +30,6 @@ extern bool perf_guest; /* General helper functions */ void usage(const char *err) __noreturn; -void die(const char *err, ...) __noreturn __printf(1, 2); struct dirent; struct strlist; @@ -87,6 +86,8 @@ struct perf_debuginfod { }; void perf_debuginfod_setup(struct perf_debuginfod *di); +const char *perf_basename(const char *path); + char *filename_with_chroot(int pid, const char *filename); int do_realloc_array_as_needed(void **arr, size_t *arr_sz, size_t x, diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index ec72d29f3d58..6eaddfcf833e 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c @@ -13,9 +13,9 @@ int perf_read_values_init(struct perf_read_values *values) { values->threads_max = 16; - values->pid = malloc(values->threads_max * sizeof(*values->pid)); - values->tid = malloc(values->threads_max * sizeof(*values->tid)); - values->value = zalloc(values->threads_max * sizeof(*values->value)); + values->pid = calloc(values->threads_max, sizeof(*values->pid)); + values->tid = calloc(values->threads_max, sizeof(*values->tid)); + values->value = calloc(values->threads_max, sizeof(*values->value)); if (!values->pid || !values->tid || !values->value) { pr_debug("failed to allocate read_values threads arrays"); goto out_free_pid; @@ -96,7 +96,7 @@ static int perf_read_values__findnew_thread(struct perf_read_values *values, i = values->threads; - values->value[i] = zalloc(values->counters_max * sizeof(**values->value)); + values->value[i] = calloc(values->counters_max, sizeof(**values->value)); if (!values->value[i]) { pr_debug("failed to allocate read_values counters array"); return -ENOMEM; diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index dd9056ddb016..2faff1aead52 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -16,7 +16,7 @@ struct process_cmd_struct { int arg; }; -static const char *version_str = "v1.25"; +static const char *version_str = "v1.26"; static const int supported_api_ver = 3; static struct isst_if_platform_info isst_platform_info; @@ -26,7 +26,7 @@ static FILE *outf; static int cpu_model; static int cpu_stepping; -static int extended_family; +static int cpu_family; #define MAX_CPUS_IN_ONE_REQ 512 static short max_target_cpus; @@ -82,6 +82,11 @@ struct cpu_topology { static int read_only; +static void print_version(void) +{ + fprintf(outf, "Version %s\n", version_str); +} + static void check_privilege(void) { if (!read_only) @@ -158,7 +163,7 @@ int is_icx_platform(void) static int is_dmr_plus_platform(void) { - if (extended_family == 0x04) + if (cpu_family == 19) return 1; return 0; @@ -167,13 +172,14 @@ static int is_dmr_plus_platform(void) static int update_cpu_model(void) { unsigned int ebx, ecx, edx; - unsigned int fms, family; + unsigned int fms; __cpuid(1, fms, ebx, ecx, edx); - family = (fms >> 8) & 0xf; - extended_family = (fms >> 20) & 0x0f; + cpu_family = (fms >> 8) & 0xf; + if (cpu_family == 0xf) + cpu_family += (fms >> 20) & 0xff; cpu_model = (fms >> 4) & 0xf; - if (family == 6 || family == 0xf) + if (cpu_family == 6 || cpu_family == 0xf) cpu_model += ((fms >> 16) & 0xf) << 4; cpu_stepping = fms & 0xf; @@ -1137,8 +1143,9 @@ static int isst_fill_platform_info(void) close(fd); if (isst_platform_info.api_version > supported_api_ver) { + print_version(); printf("Incompatible API versions; Upgrade of tool is required\n"); - return -1; + exit(1); } set_platform_ops: @@ -1744,6 +1751,9 @@ static int no_turbo(void) return parse_int_file(0, "/sys/devices/system/cpu/intel_pstate/no_turbo"); } +#define U32_MAX ((unsigned int)~0U) +#define S32_MAX ((int)(U32_MAX >> 1)) + static void adjust_scaling_max_from_base_freq(int cpu) { int base_freq, scaling_max_freq; @@ -1751,7 +1761,7 @@ static void adjust_scaling_max_from_base_freq(int cpu) scaling_max_freq = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu); base_freq = get_cpufreq_base_freq(cpu); if (scaling_max_freq < base_freq || no_turbo()) - set_cpufreq_scaling_min_max(cpu, 1, base_freq); + set_cpufreq_scaling_min_max(cpu, 1, S32_MAX); } static void adjust_scaling_min_from_base_freq(int cpu) @@ -3191,12 +3201,6 @@ static void usage(void) printf("\tTo get full turbo-freq information dump:\n"); printf("\t\tintel-speed-select turbo-freq info -l 0\n"); } - exit(1); -} - -static void print_version(void) -{ - fprintf(outf, "Version %s\n", version_str); exit(0); } @@ -3246,8 +3250,10 @@ static void cmdline(int argc, char **argv) } ret = update_cpu_model(); - if (ret) - err(-1, "Invalid CPU model (%d)\n", cpu_model); + if (ret) { + fprintf(stderr, "Invalid CPU model (%d)\n", cpu_model); + exit(1); + } printf("Intel(R) Speed Select Technology\n"); printf("Executing on CPU model:%d[0x%x]\n", cpu_model, cpu_model); @@ -3311,6 +3317,7 @@ static void cmdline(int argc, char **argv) break; case 'v': print_version(); + exit(0); break; case 'b': oob_mode = 1; diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index e9e8ef72395a..920694c3c1ec 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -191,6 +191,7 @@ struct msr_counter bic[] = { { 0x0, "Any%C0", NULL, 0, 0, 0, NULL, 0 }, { 0x0, "GFX%C0", NULL, 0, 0, 0, NULL, 0 }, { 0x0, "CPUGFX%", NULL, 0, 0, 0, NULL, 0 }, + { 0x0, "Module", NULL, 0, 0, 0, NULL, 0 }, { 0x0, "Core", NULL, 0, 0, 0, NULL, 0 }, { 0x0, "CPU", NULL, 0, 0, 0, NULL, 0 }, { 0x0, "APIC", NULL, 0, 0, 0, NULL, 0 }, @@ -264,6 +265,7 @@ enum bic_names { BIC_Any_c0, BIC_GFX_c0, BIC_CPUGFX, + BIC_Module, BIC_Core, BIC_CPU, BIC_APIC, @@ -364,6 +366,7 @@ static void bic_groups_init(void) SET_BIC(BIC_Node, &bic_group_topology); SET_BIC(BIC_CoreCnt, &bic_group_topology); SET_BIC(BIC_PkgCnt, &bic_group_topology); + SET_BIC(BIC_Module, &bic_group_topology); SET_BIC(BIC_Core, &bic_group_topology); SET_BIC(BIC_CPU, &bic_group_topology); SET_BIC(BIC_Die, &bic_group_topology); @@ -2383,6 +2386,7 @@ struct platform_counters { struct cpu_topology { int cpu_id; int core_id; /* unique within a package */ + int module_id; int package_id; int die_id; int l3_id; @@ -2404,6 +2408,8 @@ struct topo_params { int allowed_cores; int max_cpu_num; int max_core_id; /* within a package */ + int min_module_id; /* system wide */ + int max_module_id; /* system wide */ int max_package_id; int max_die_id; int max_l3_id; @@ -2427,11 +2433,17 @@ char *sys_lpi_file_debugfs = "/sys/kernel/debug/pmc_core/slp_s0_residency_usec"; int cpu_is_not_present(int cpu) { + if (cpu < 0) + return 1; + return !CPU_ISSET_S(cpu, cpu_present_setsize, cpu_present_set); } int cpu_is_not_allowed(int cpu) { + if (cpu < 0) + return 1; + return !CPU_ISSET_S(cpu, cpu_allowed_setsize, cpu_allowed_set); } @@ -2443,6 +2455,22 @@ int cpu_is_not_allowed(int cpu) #define PER_THREAD_PARAMS struct thread_data *t, struct core_data *c, struct pkg_data *p +int has_allowed_lower_ht_sibling(int cpu) +{ + int i; + + for (i = 0; i <= cpus[cpu].ht_id; ++i) { + int sibling_cpu_id = cpus[cpu].ht_sibling_cpu_id[i]; + + if (sibling_cpu_id == cpu) + return 0; + + if (!cpu_is_not_allowed(sibling_cpu_id)) + return 1; + } + return 0; +} + int for_all_cpus(int (func) (struct thread_data *, struct core_data *, struct pkg_data *), struct thread_data *thread_base, struct core_data *core_base, struct pkg_data *pkg_base) { @@ -2460,7 +2488,7 @@ int for_all_cpus(int (func) (struct thread_data *, struct core_data *, struct pk if (cpu_is_not_allowed(cpu)) continue; - if (cpus[cpu].ht_id > 0) /* skip HT sibling */ + if (has_allowed_lower_ht_sibling(cpu)) /* skip HT sibling */ continue; t = &thread_base[cpu]; @@ -2469,13 +2497,22 @@ int for_all_cpus(int (func) (struct thread_data *, struct core_data *, struct pk retval |= func(t, c, p); - /* Handle HT sibling now */ + /* Handle other HT siblings now */ int i; - for (i = MAX_HT_ID; i > 0; --i) { /* ht_id 0 is self */ - if (cpus[cpu].ht_sibling_cpu_id[i] <= 0) + for (i = 0; i <= MAX_HT_ID; ++i) { + int sibling_cpu_id = cpus[cpu].ht_sibling_cpu_id[i]; + + if (sibling_cpu_id < 0) + break; + + if (sibling_cpu_id == cpu) continue; - t = &thread_base[cpus[cpu].ht_sibling_cpu_id[i]]; + + if (cpu_is_not_allowed(sibling_cpu_id)) + continue; + + t = &thread_base[sibling_cpu_id]; retval |= func(t, c, p); } @@ -2835,31 +2872,38 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) static inline int print_name(int width, int *printed, char *delim, char *name, enum counter_type type, enum counter_format format) { UNUSED(type); + char *sep = (*printed)++ ? delim : ""; if (format == FORMAT_RAW && width >= 64) - return (sprintf(outp, "%s%-8s", ((*printed)++ ? delim : ""), name)); + return sprintf(outp, "%s%-8s", sep, name); else - return (sprintf(outp, "%s%s", ((*printed)++ ? delim : ""), name)); + return sprintf(outp, "%s%s", sep, name); } static inline int print_hex_value(int width, int *printed, char *delim, unsigned long long value) { + char *sep = (*printed)++ ? delim : ""; + if (width <= 32) - return (sprintf(outp, "%s%08x", ((*printed)++ ? delim : ""), (unsigned int)value)); + return sprintf(outp, "%s%08llx", sep, value); else - return (sprintf(outp, "%s%016llx", ((*printed)++ ? delim : ""), value)); + return sprintf(outp, "%s%016llx", sep, value); } static inline int print_decimal_value(int width, int *printed, char *delim, unsigned long long value) { + char *sep = (*printed)++ ? delim : ""; + UNUSED(width); - return (sprintf(outp, "%s%lld", ((*printed)++ ? delim : ""), value)); + return sprintf(outp, "%s%lld", sep, value); } static inline int print_float_value(int *printed, char *delim, double value) { - return (sprintf(outp, "%s%0.2f", ((*printed)++ ? delim : ""), value)); + char *sep = (*printed)++ ? delim : ""; + + return sprintf(outp, "%s%0.2f", sep, value); } void print_header(char *delim) @@ -2881,6 +2925,8 @@ void print_header(char *delim) outp += sprintf(outp, "%sL3", (printed++ ? delim : "")); if (DO_BIC(BIC_Node)) outp += sprintf(outp, "%sNode", (printed++ ? delim : "")); + if (DO_BIC(BIC_Module)) + outp += sprintf(outp, "%sModule", (printed++ ? delim : "")); if (DO_BIC(BIC_Core)) outp += sprintf(outp, "%sCore", (printed++ ? delim : "")); if (DO_BIC(BIC_CPU)) @@ -3176,7 +3222,7 @@ int dump_counters(PER_THREAD_PARAMS) } if (c && is_cpu_first_thread_in_core(t, c)) { - outp += sprintf(outp, "core: %d\n", cpus[t->cpu_id].core_id); + outp += sprintf(outp, "core: 0x%x\n", cpus[t->cpu_id].core_id); outp += sprintf(outp, "c3: %016llX\n", c->c3); outp += sprintf(outp, "c6: %016llX\n", c->c6); outp += sprintf(outp, "c7: %016llX\n", c->c7); @@ -3350,6 +3396,8 @@ int format_counters(PER_THREAD_PARAMS) outp += sprintf(outp, "%s-", (printed++ ? delim : "")); if (DO_BIC(BIC_Node)) outp += sprintf(outp, "%s-", (printed++ ? delim : "")); + if (DO_BIC(BIC_Module)) + outp += sprintf(outp, "%s-", (printed++ ? delim : "")); if (DO_BIC(BIC_Core)) outp += sprintf(outp, "%s-", (printed++ ? delim : "")); if (DO_BIC(BIC_CPU)) @@ -3383,18 +3431,24 @@ int format_counters(PER_THREAD_PARAMS) else outp += sprintf(outp, "%s-", (printed++ ? delim : "")); } + if (DO_BIC(BIC_Module)) { + if (c) + outp += sprintf(outp, "%s0x%x", (printed++ ? delim : ""), cpus[t->cpu_id].module_id); + else + outp += sprintf(outp, "%s-", (printed++ ? delim : "")); + } if (DO_BIC(BIC_Core)) { if (c) - outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), cpus[t->cpu_id].core_id); + outp += sprintf(outp, "%s0x%x", (printed++ ? delim : ""), cpus[t->cpu_id].core_id); else outp += sprintf(outp, "%s-", (printed++ ? delim : "")); } if (DO_BIC(BIC_CPU)) outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), t->cpu_id); if (DO_BIC(BIC_APIC)) - outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), t->apic_id); + outp += sprintf(outp, "%s0x%x", (printed++ ? delim : ""), t->apic_id); if (DO_BIC(BIC_X2APIC)) - outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), t->x2apic_id); + outp += sprintf(outp, "%s0x%x", (printed++ ? delim : ""), t->x2apic_id); } if (DO_BIC(BIC_Avg_MHz)) @@ -5155,7 +5209,7 @@ static inline int get_rapl_num_domains(void) if (!platform->has_per_core_rapl) return topo.num_packages; - return topo.num_cores; + return GLOBAL_CORE_ID(topo.max_core_id, topo.num_packages) + 1; } static inline int get_rapl_domain_id(int cpu) @@ -6041,6 +6095,11 @@ int get_l3_id(int cpu) return parse_int_file("/sys/devices/system/cpu/cpu%d/cache/index3/id", cpu); } +int get_module_id(int cpu) +{ + return parse_int_file("/sys/devices/system/cpu/cpu%d/topology/cluster_id", cpu); +} + int get_core_id(int cpu) { return parse_int_file("/sys/devices/system/cpu/cpu%d/topology/core_id", cpu); @@ -6160,59 +6219,6 @@ static int parse_cpu_str(char *cpu_str, cpu_set_t *cpu_set, int cpu_set_size) return 0; } -int set_thread_siblings(struct cpu_topology *thiscpu) -{ - char path[80], character; - FILE *filep; - unsigned long map; - int so, shift, sib_core; - int cpu = thiscpu->cpu_id; - int offset = topo.max_cpu_num + 1; - size_t size; - int thread_id = 0; - - thiscpu->put_ids = CPU_ALLOC((topo.max_cpu_num + 1)); - if (thiscpu->ht_id < 0) - thiscpu->ht_id = thread_id++; - if (!thiscpu->put_ids) - return -1; - - size = CPU_ALLOC_SIZE((topo.max_cpu_num + 1)); - CPU_ZERO_S(size, thiscpu->put_ids); - - sprintf(path, "/sys/devices/system/cpu/cpu%d/topology/thread_siblings", cpu); - filep = fopen(path, "r"); - - if (!filep) { - warnx("%s: open failed", path); - return -1; - } - do { - offset -= BITMASK_SIZE; - if (fscanf(filep, "%lx%c", &map, &character) != 2) - err(1, "%s: failed to parse file", path); - for (shift = 0; shift < BITMASK_SIZE; shift++) { - if ((map >> shift) & 0x1) { - so = shift + offset; - sib_core = get_core_id(so); - if (sib_core == thiscpu->core_id) { - CPU_SET_S(so, size, thiscpu->put_ids); - if ((so != cpu) && (cpus[so].ht_id < 0)) { - cpus[so].ht_id = thread_id; - cpus[cpu].ht_sibling_cpu_id[thread_id] = so; - if (debug) - fprintf(stderr, "%s: cpu%d.ht_sibling_cpu_id[%d] = %d\n", __func__, cpu, thread_id, so); - thread_id += 1; - } - } - } - } - } while (character == ','); - fclose(filep); - - return CPU_COUNT_S(size, thiscpu->put_ids); -} - /* * run func(thread, core, package) in topology order * skip non-present cpus @@ -6236,7 +6242,7 @@ int for_all_cpus_2(int (func) (struct thread_data *, struct core_data *, if (cpu_is_not_allowed(cpu)) continue; - if (cpus[cpu].ht_id > 0) /* skip HT sibling */ + if (has_allowed_lower_ht_sibling(cpu)) /* skip HT sibling */ continue; t = &thread_base[cpu]; @@ -6251,11 +6257,20 @@ int for_all_cpus_2(int (func) (struct thread_data *, struct core_data *, /* Handle HT sibling now */ int i; - for (i = MAX_HT_ID; i > 0; --i) { /* ht_id 0 is self */ - if (cpus[cpu].ht_sibling_cpu_id[i] <= 0) + for (i = 0; i <= MAX_HT_ID; ++i) { + int sibling_cpu_id = cpus[cpu].ht_sibling_cpu_id[i]; + + if (sibling_cpu_id < 0) + break; + + if (sibling_cpu_id == cpu) continue; - t = &thread_base[cpus[cpu].ht_sibling_cpu_id[i]]; - t2 = &thread_base2[cpus[cpu].ht_sibling_cpu_id[i]]; + + if (cpu_is_not_allowed(sibling_cpu_id)) + continue; + + t = &thread_base[sibling_cpu_id]; + t2 = &thread_base2[sibling_cpu_id]; retval |= func(t, c, p, t2, c2, p2); } @@ -9475,6 +9490,37 @@ int dir_filter(const struct dirent *dirp) return 0; } +int set_thread_siblings(struct cpu_topology *thiscpu) +{ + char path[80]; + int cpu = thiscpu->cpu_id; + size_t size; + int ht_id = 0; + int i; + + thiscpu->put_ids = CPU_ALLOC((topo.max_cpu_num + 1)); + if (thiscpu->ht_id < 0) + thiscpu->ht_id = 0; /* first CPU in core */ + if (!thiscpu->put_ids) + return -1; + + size = CPU_ALLOC_SIZE((topo.max_cpu_num + 1)); + CPU_ZERO_S(size, thiscpu->put_ids); + + sprintf(path, "/sys/devices/system/cpu/cpu%d/topology", cpu); + + initialize_cpu_set_from_sysfs(thiscpu->put_ids, path, "thread_siblings_list"); + + for (i = 0; i <= topo.max_cpu_num; ++i) + if (CPU_ISSET_S(i, size, thiscpu->put_ids)) { + cpus[i].ht_id = ht_id; + cpus[cpu].ht_sibling_cpu_id[ht_id] = i; + ht_id += 1; + } + + return (ht_id - 1); +} + void topology_probe(bool startup) { int i; @@ -9505,6 +9551,8 @@ void topology_probe(bool startup) cpu_present_setsize = CPU_ALLOC_SIZE((topo.max_cpu_num + 1)); CPU_ZERO_S(cpu_present_setsize, cpu_present_set); for_all_proc_cpus(mark_cpu_present); + if (debug) + print_cpu_set("present set", cpu_present_set); /* * Allocate and initialize cpu_possible_set @@ -9515,6 +9563,8 @@ void topology_probe(bool startup) cpu_possible_setsize = CPU_ALLOC_SIZE((topo.max_cpu_num + 1)); CPU_ZERO_S(cpu_possible_setsize, cpu_possible_set); initialize_cpu_set_from_sysfs(cpu_possible_set, "/sys/devices/system/cpu", "possible"); + if (debug) + print_cpu_set("possible set", cpu_possible_set); /* * Allocate and initialize cpu_effective_set @@ -9525,6 +9575,8 @@ void topology_probe(bool startup) cpu_effective_setsize = CPU_ALLOC_SIZE((topo.max_cpu_num + 1)); CPU_ZERO_S(cpu_effective_setsize, cpu_effective_set); update_effective_set(startup); + if (debug) + print_cpu_set("effective set", cpu_effective_set); /* * Allocate and initialize cpu_allowed_set @@ -9568,6 +9620,8 @@ void topology_probe(bool startup) CPU_SET_S(i, cpu_allowed_setsize, cpu_allowed_set); } + if (debug) + print_cpu_set("allowed set", cpu_allowed_set); if (!CPU_COUNT_S(cpu_allowed_setsize, cpu_allowed_set)) err(-ENODEV, "No valid cpus found"); @@ -9590,6 +9644,7 @@ void topology_probe(bool startup) * For online cpus * find max_core_id, max_package_id, num_cores (per system) */ + topo.min_module_id = 0x7FFFFFFF; for (i = 0; i <= topo.max_cpu_num; ++i) { int siblings; @@ -9621,6 +9676,13 @@ void topology_probe(bool startup) if (cpus[i].physical_node_id > topo.max_node_num) topo.max_node_num = cpus[i].physical_node_id; + /* get module information */ + cpus[i].module_id = get_module_id(i); + if (cpus[i].module_id > topo.max_module_id) + topo.max_module_id = cpus[i].module_id; + if (cpus[i].module_id < topo.min_module_id) + topo.min_module_id = cpus[i].module_id; + /* get core information */ cpus[i].core_id = get_core_id(i); if (cpus[i].core_id > max_core_id) @@ -9642,6 +9704,11 @@ void topology_probe(bool startup) if (!summary_only) BIC_PRESENT(BIC_Core); + if (debug > 1) + fprintf(outf, "min_module_id %d max_module_id %d\n", topo.min_module_id, topo.max_module_id); + if (!summary_only && (topo.min_module_id != topo.max_module_id)) + BIC_PRESENT(BIC_Module); + topo.num_die = topo.max_die_id + 1; if (debug > 1) fprintf(outf, "max_die_id %d, sizing for %d die\n", topo.max_die_id, topo.num_die); @@ -9671,12 +9738,18 @@ void topology_probe(bool startup) return; for (i = 0; i <= topo.max_cpu_num; ++i) { + int ht_id; + if (cpu_is_not_present(i)) continue; fprintf(outf, - "cpu %d pkg %d die %d l3 %d node %d lnode %d core %d thread %d\n", + "cpu %d pkg %d die %d l3 %d node %d lnode %d module 0x%x core %d ht_id %d", i, cpus[i].package_id, cpus[i].die_id, cpus[i].l3_id, - cpus[i].physical_node_id, cpus[i].logical_node_id, cpus[i].core_id, cpus[i].ht_id); + cpus[i].physical_node_id, cpus[i].logical_node_id, cpus[i].module_id, cpus[i].core_id, cpus[i].ht_id); + fprintf(outf, " siblings"); + for (ht_id = 0; ht_id <= MAX_HT_ID; ++ht_id) + fprintf(outf, " %d", cpus[i].ht_sibling_cpu_id[ht_id]); + fprintf(outf, "\n"); } } @@ -9817,6 +9890,8 @@ void topology_update(void) topo.allowed_cores = 0; topo.allowed_packages = 0; for_all_cpus(update_topo, ODD_COUNTERS); + if (debug) + fprintf(stderr, "allowed_cpus %d allowed_cores %d allowed_packages %d\n", topo.allowed_cpus, topo.allowed_cores, topo.allowed_packages); } void setup_all_buffers(bool startup) @@ -10533,7 +10608,7 @@ int get_and_dump_counters(void) void print_version() { - fprintf(outf, "turbostat version 2026.02.14 - Len Brown \n"); + fprintf(outf, "turbostat version 2026.04.21 - Len Brown \n"); } #define COMMAND_LINE_SIZE 2048 @@ -11449,7 +11524,7 @@ void cmdline(int argc, char **argv) } optind = 0; - while ((opt = getopt_long_only(argc, argv, "+C:c:Dde:hi:Jn:N:o:qMST:v", long_options, &option_index)) != -1) { + while ((opt = getopt_long_only(argc, argv, "+C:c:Dde:hi:Jn:N:o:qMPST:v", long_options, &option_index)) != -1) { switch (opt) { case 'a': parse_add_command(optarg); diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8 b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8 index 0aa981c18e56..836553e9a92c 100644 --- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8 +++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8 @@ -15,6 +15,8 @@ x86_energy_perf_policy \- Manage Energy vs. Performance Policy .br .RB "other: (\-\-force | \-\-hwp-enable | \-\-turbo-enable) value)" .br +.RB "soc-slider: --soc-slider-balance # | --soc-slider-offset # | --platform-profile " +.br .RB "value: # | default | performance | balance-performance | balance-power | power" .SH DESCRIPTION \fBx86_energy_perf_policy\fP @@ -154,6 +156,26 @@ level on this processor, specified in multiples of 100 MHz. in the sliding window that HWP uses to maintain average frequency. This parameter is meaningful only when the "desired" field above is non-zero. Default is 0, allowing the HW to choose. +.SH SOC SLIDER OPTIONS +.PP +Note that the Platform Profile Name must be "SoC Slider", and the +Platform Profile must be "balanced" for the --soc-slider-balance +and --soc-slider-offset options to take effect. +.PP +\fB--soc-slider-balance #\fP write numeric value to the SoC Slider. +Values range from 0 to 6. +Lower values result in higher performance, +and higher values improve energy efficiency. +Actual values are model specific. +.PP +\fB--soc-slider-offset #\fP write the numeric value to the Soc Slider Offset. +The slider offset is the maximum value that software allows the SoC to +autonomously add to the SoC Slider to improve energy efficiency. +The value 0 prohibits the SoC from autonomously changing the slider. +.PP +\fB--platform-profile "\fP set the platform profile to . +Available choices are in platform-profile-0/choices. The Soc Slider +driver currently supports "low-power", "balanced", and "performance". .SH OTHER OPTIONS .PP \fB-f, --force\fP writes the specified values without bounds checking. @@ -208,6 +230,10 @@ runs only as root. EPB: /sys/devices/system/cpu/cpu*/power/energy_perf_bias EPP: /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference MSR: /dev/cpu/*/msr +Platform Profile Name: /sys/class/platform-profile/platform-profile-0/name +Platform Profile: /sys/class/platform-profile/platform-profile-0/profile +SOC Slider Balanced: /sys/module/processor_thermal_soc_slider/parameters/slider_balance +SOC Slider Balanced Offset: /sys/module/processor_thermal_soc_slider/parameters/slider_offset .fi .SH "SEE ALSO" .nf diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c index ac37132207a4..0dc959e30076 100644 --- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c @@ -4,7 +4,7 @@ * policy preference bias on recent X86 processors. */ /* - * Copyright (c) 2010 - 2025 Intel Corporation. + * Copyright (c) 2010 - 2026 Intel Corporation. * Len Brown */ @@ -82,21 +82,36 @@ size_t cpu_setsize; char *proc_stat = "/proc/stat"; -unsigned int has_epb; /* MSR_IA32_ENERGY_PERF_BIAS */ -unsigned int has_hwp; /* IA32_PM_ENABLE, IA32_HWP_CAPABILITIES */ +unsigned int has_epb; /* MSR_IA32_ENERGY_PERF_BIAS */ +unsigned int has_hwp; /* IA32_PM_ENABLE, IA32_HWP_CAPABILITIES */ /* IA32_HWP_REQUEST, IA32_HWP_STATUS */ -unsigned int has_hwp_notify; /* IA32_HWP_INTERRUPT */ +unsigned int has_hwp_notify; /* IA32_HWP_INTERRUPT */ unsigned int has_hwp_activity_window; /* IA32_HWP_REQUEST[bits 41:32] */ unsigned int has_hwp_epp; /* IA32_HWP_REQUEST[bits 31:24] */ unsigned int has_hwp_request_pkg; /* IA32_HWP_REQUEST_PKG */ unsigned int bdx_highest_ratio; +unsigned char update_soc_slider_balance; +unsigned char update_soc_slider_offset; +unsigned char update_platform_profile; +int soc_slider_balance; +int soc_slider_offset; +char platform_profile[64]; + #define PATH_TO_CPU "/sys/devices/system/cpu/" #define SYSFS_PATH_MAX 255 +#define PATH_SOC_SLIDER_BALANCE "/sys/module/processor_thermal_soc_slider/parameters/slider_balance" +#define PATH_SOC_SLIDER_OFFSET "/sys/module/processor_thermal_soc_slider/parameters/slider_offset" +#define PATH_PLATFORM_PROFILE "/sys/class/platform-profile/platform-profile-0/profile" +#define PATH_PLATFORM_PROFILE_NAME "/sys/class/platform-profile/platform-profile-0/name" +#define POWER_SLIDER_NAME "SoC Power Slider" static int use_android_msr_path; +static unsigned int read_sysfs(const char *, char *, size_t); +static int sysfs_read_string(const char *, char *, size_t); + /* * maintain compatibility with original implementation, but don't document it: */ @@ -106,8 +121,8 @@ void usage(void) fprintf(stderr, "scope: --cpu cpu-list [--hwp-use-pkg #] | --pkg pkg-list\n"); fprintf(stderr, "field: --all | --epb | --hwp-epp | --hwp-min | --hwp-max | --hwp-desired\n"); fprintf(stderr, "other: --hwp-enable | --turbo-enable (0 | 1) | --help | --force\n"); - fprintf(stderr, - "value: ( # | \"normal\" | \"performance\" | \"balance-performance\" | \"balance-power\"| \"power\")\n"); + fprintf(stderr, "soc-slider: --soc-slider-balance # | --soc-slider-offset # | --platform-profile \n"); + fprintf(stderr, "value: ( # | \"normal\" | \"performance\" | \"balance-performance\" | \"balance-power\"| \"power\")\n"); fprintf(stderr, "--hwp-window usec\n"); fprintf(stderr, "Specify only Energy Performance BIAS (legacy usage):\n"); @@ -135,6 +150,7 @@ int ratio_2_msr_perf(int ratio) return msr_perf; } + int msr_perf_2_ratio(int msr_perf) { int ratio; @@ -143,8 +159,8 @@ int msr_perf_2_ratio(int msr_perf) if (!bdx_highest_ratio) return msr_perf; - d = (double)msr_perf * (double) bdx_highest_ratio / 255.0; - d = d + 0.5; /* round */ + d = (double)msr_perf * (double)bdx_highest_ratio / 255.0; + d = d + 0.5; /* round */ ratio = (int)d; if (debug) @@ -152,6 +168,7 @@ int msr_perf_2_ratio(int msr_perf) return ratio; } + int parse_cmdline_epb(int i) { if (!has_epb) @@ -198,6 +215,7 @@ int parse_cmdline_hwp_min(int i) } return i; } + /* * "power" changes hwp_max to cap.lowest * All others leave it at cap.highest @@ -217,6 +235,7 @@ int parse_cmdline_hwp_max(int i) } return i; } + /* * for --hwp-des, all strings leave it in autonomous mode * If you want to change it, you need to explicitly pick a value @@ -254,7 +273,7 @@ int parse_cmdline_hwp_window(int i) fprintf(stderr, "--hwp-window: 0 for auto; 1 - 1270000000 usec for window duration\n"); usage(); } - for (exponent = 0; ; ++exponent) { + for (exponent = 0;; ++exponent) { if (debug) printf("%d 10^%d\n", i, exponent); @@ -268,6 +287,7 @@ int parse_cmdline_hwp_window(int i) return (exponent << 7) | i; } + int parse_cmdline_hwp_epp(int i) { update_hwp_epp = 1; @@ -289,6 +309,7 @@ int parse_cmdline_hwp_epp(int i) } return i; } + int parse_cmdline_turbo(int i) { update_turbo = 1; @@ -508,7 +529,7 @@ void parse_cmdline_pkg(char *s) } } -void for_packages(unsigned long long pkg_set, int (func)(int)) +void for_packages(unsigned long long pkg_set, int (func) (int)) { int pkg_num; @@ -518,9 +539,79 @@ void for_packages(unsigned long long pkg_set, int (func)(int)) } } +static int parse_cmdline_int(const char *s, int *out) +{ + char *endp; + long val; + + val = strtol(s, &endp, 0); + if (endp == s || errno == ERANGE) + return -1; + if (*endp != '\0') + return -1; + if (val < INT_MIN || val > INT_MAX) + return -1; + + *out = (int)val; + return 0; +} + void print_version(void) { - printf("x86_energy_perf_policy 2025.11.22 Len Brown \n"); + printf("x86_energy_perf_policy 2026.04.25 Len Brown \n"); +} + +static int platform_profile_access(int mode) +{ + if (access(PATH_PLATFORM_PROFILE, mode)) { + if (debug) + fprintf(stderr, "Can not access %s\n", PATH_PLATFORM_PROFILE); + return 0; + } + + return 1; +} + +static int platform_profile_name_is(char *name) +{ + char buf[64]; + + if (sysfs_read_string(PATH_PLATFORM_PROFILE_NAME, buf, sizeof(buf)) != 0) { + if (debug) + fprintf(stderr, "Can not read %s\n", PATH_PLATFORM_PROFILE_NAME); + return 0; + } + + if (strncmp(buf, name, 16)) { + if (debug) + fprintf(stderr, "%s does not match '%s'\n", PATH_PLATFORM_PROFILE_NAME, name); + return 0; + } + + return 1; +} + +static int soc_slider_access(int mode) +{ + if (!platform_profile_access(R_OK)) + return 0; + + if (!platform_profile_name_is(POWER_SLIDER_NAME)) + return 0; + + if (access(PATH_SOC_SLIDER_BALANCE, mode)) { + if (debug) + fprintf(stderr, "Can not access %s\n", PATH_SOC_SLIDER_BALANCE); + return 0; + } + + if (access(PATH_SOC_SLIDER_OFFSET, mode)) { + if (debug) + fprintf(stderr, "Can not access %s\n", PATH_SOC_SLIDER_OFFSET); + return 0; + } + + return 1; } void cmdline(int argc, char **argv) @@ -529,30 +620,32 @@ void cmdline(int argc, char **argv) int option_index = 0; static struct option long_options[] = { - {"all", required_argument, 0, 'a'}, - {"cpu", required_argument, 0, 'c'}, - {"pkg", required_argument, 0, 'p'}, - {"debug", no_argument, 0, 'd'}, - {"hwp-desired", required_argument, 0, 'D'}, - {"epb", required_argument, 0, 'B'}, - {"force", no_argument, 0, 'f'}, - {"hwp-enable", no_argument, 0, 'e'}, - {"help", no_argument, 0, 'h'}, - {"hwp-epp", required_argument, 0, 'P'}, - {"hwp-min", required_argument, 0, 'm'}, - {"hwp-max", required_argument, 0, 'M'}, - {"read", no_argument, 0, 'r'}, - {"turbo-enable", required_argument, 0, 't'}, - {"hwp-use-pkg", required_argument, 0, 'u'}, - {"version", no_argument, 0, 'v'}, - {"hwp-window", required_argument, 0, 'w'}, - {0, 0, 0, 0 } + { "all", required_argument, 0, 'a' }, + { "cpu", required_argument, 0, 'c' }, + { "pkg", required_argument, 0, 'p' }, + { "debug", no_argument, 0, 'd' }, + { "hwp-desired", required_argument, 0, 'D' }, + { "epb", required_argument, 0, 'B' }, + { "force", no_argument, 0, 'f' }, + { "hwp-enable", no_argument, 0, 'e' }, + { "help", no_argument, 0, 'h' }, + { "hwp-epp", required_argument, 0, 'P' }, + { "hwp-min", required_argument, 0, 'm' }, + { "hwp-max", required_argument, 0, 'M' }, + { "read", no_argument, 0, 'r' }, + { "turbo-enable", required_argument, 0, 't' }, + { "hwp-use-pkg", required_argument, 0, 'u' }, + { "version", no_argument, 0, 'v' }, + { "hwp-window", required_argument, 0, 'w' }, + { "soc-slider-balance", required_argument, 0, 'S' }, + { "soc-slider-offset", required_argument, 0, 'O' }, + { "platform-profile", required_argument, 0, 'F' }, + { 0, 0, 0, 0 } }; progname = argv[0]; - while ((opt = getopt_long_only(argc, argv, "+a:c:dD:E:e:f:m:M:rt:u:vw:", - long_options, &option_index)) != -1) { + while ((opt = getopt_long_only(argc, argv, "+a:c:dD:E:e:f:m:M:rt:u:vw::S:O:F:", long_options, &option_index)) != -1) { switch (opt) { case 'a': parse_cmdline_all(optarg); @@ -579,12 +672,27 @@ void cmdline(int argc, char **argv) case 'D': req_update.hwp_desired = parse_cmdline_hwp_desired(parse_optarg_string(optarg)); break; + case 'F': + if (strlen(optarg) >= sizeof(platform_profile)) + errx(1, "--platform-profile: value too long"); + if (!platform_profile_access(W_OK)) + errx(1, "Can not update platform-profile in '%s'", PATH_PLATFORM_PROFILE); + strcpy(platform_profile, optarg); + update_platform_profile = 1; + break; case 'm': req_update.hwp_min = parse_cmdline_hwp_min(parse_optarg_string(optarg)); break; case 'M': req_update.hwp_max = parse_cmdline_hwp_max(parse_optarg_string(optarg)); break; + case 'O': + if (parse_cmdline_int(optarg, &soc_slider_offset)) + errx(1, "--soc-slider-offset: invalid value"); + if (!soc_slider_access(W_OK)) + errx(1, "Unable to write SOC Slider Offset"); + update_soc_slider_offset = 1; + break; case 'p': parse_cmdline_pkg(optarg); break; @@ -594,6 +702,13 @@ void cmdline(int argc, char **argv) case 'r': /* v1 used -r to specify read-only mode, now the default */ break; + case 'S': + if (parse_cmdline_int(optarg, &soc_slider_balance)) + errx(1, "--soc-slider-balance: invalid value"); + if (!soc_slider_access(W_OK)) + errx(1, "Unable to write SOC Slider-Balance in '%s'", PATH_SOC_SLIDER_BALANCE); + update_soc_slider_balance = 1; + break; case 't': turbo_update_value = parse_cmdline_turbo(parse_optarg_string(optarg)); break; @@ -681,8 +796,7 @@ void err_on_hypervisor(void) free(buffer); if (hypervisor) - err(-1, - "not supported on this virtual machine"); + err(-1, "not supported on this virtual machine"); } int get_msr(int cpu, int offset, unsigned long long *msr) @@ -694,9 +808,7 @@ int get_msr(int cpu, int offset, unsigned long long *msr) sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", cpu); fd = open(pathname, O_RDONLY); if (fd < 0) - err(-1, "%s open failed, try chown or chmod +r %s, or run as root", - pathname, use_android_msr_path ? "/dev/msr*" : "/dev/cpu/*/msr"); - + err(-1, "%s open failed, try chown or chmod +r %s, or run as root", pathname, use_android_msr_path ? "/dev/msr*" : "/dev/cpu/*/msr"); retval = pread(fd, msr, sizeof(*msr), offset); if (retval != sizeof(*msr)) { @@ -720,8 +832,7 @@ int put_msr(int cpu, int offset, unsigned long long new_msr) sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", cpu); fd = open(pathname, O_RDWR); if (fd < 0) - err(-1, "%s open failed, try chown or chmod +r %s, or run as root", - pathname, use_android_msr_path ? "/dev/msr*" : "/dev/cpu/*/msr"); + err(-1, "%s open failed, try chown or chmod +r %s, or run as root", pathname, use_android_msr_path ? "/dev/msr*" : "/dev/cpu/*/msr"); retval = pwrite(fd, &new_msr, sizeof(new_msr), offset); if (retval != sizeof(new_msr)) @@ -753,7 +864,7 @@ static unsigned int read_sysfs(const char *path, char *buf, size_t buflen) buf[numread] = '\0'; close(fd); - return (unsigned int) numread; + return (unsigned int)numread; } static unsigned int write_sysfs(const char *path, char *buf, size_t buflen) @@ -767,14 +878,40 @@ static unsigned int write_sysfs(const char *path, char *buf, size_t buflen) numwritten = write(fd, buf, buflen - 1); if (numwritten < 1) { - perror("write failed\n"); + buf[strcspn(buf, "\n")] = '\0'; + warn("Write '%s' to '%s' failed", buf, path); close(fd); return -1; } close(fd); - return (unsigned int) numwritten; + return (unsigned int)numwritten; +} + +static int sysfs_read_string(const char *path, char *buf, size_t buflen) +{ + unsigned int len; + size_t n; + + len = read_sysfs(path, buf, buflen); + if (!len) + return -1; + + n = strcspn(buf, "\n"); + buf[n] = '\0'; + return 0; +} + +static int sysfs_write_string(const char *path, const char *buf) +{ + char tmp[128]; + int len; + + len = snprintf(tmp, sizeof(tmp), "%s\n", buf); + if (len < 0 || len >= (int)sizeof(tmp)) + return -1; + return write_sysfs(path, tmp, (size_t)len + 1) ? 0 : -1; } void print_hwp_cap(int cpu, struct msr_hwp_cap *cap, char *str) @@ -782,9 +919,9 @@ void print_hwp_cap(int cpu, struct msr_hwp_cap *cap, char *str) if (cpu != -1) printf("cpu%d: ", cpu); - printf("HWP_CAP: low %d eff %d guar %d high %d\n", - cap->lowest, cap->efficient, cap->guaranteed, cap->highest); + printf("HWP_CAP: low %d eff %d guar %d high %d\n", cap->lowest, cap->efficient, cap->guaranteed, cap->highest); } + void read_hwp_cap(int cpu, struct msr_hwp_cap *cap, unsigned int msr_offset) { unsigned long long msr; @@ -806,9 +943,9 @@ void print_hwp_request(int cpu, struct msr_hwp_request *h, char *str) printf("%s", str); printf("HWP_REQ: min %d max %d des %d epp %d window 0x%x (%d*10^%dus) use_pkg %d\n", - h->hwp_min, h->hwp_max, h->hwp_desired, h->hwp_epp, - h->hwp_window, h->hwp_window & 0x7F, (h->hwp_window >> 7) & 0x7, h->hwp_use_pkg); + h->hwp_min, h->hwp_max, h->hwp_desired, h->hwp_epp, h->hwp_window, h->hwp_window & 0x7F, (h->hwp_window >> 7) & 0x7, h->hwp_use_pkg); } + void print_hwp_request_pkg(int pkg, struct msr_hwp_request *h, char *str) { printf("pkg%d: ", pkg); @@ -817,9 +954,9 @@ void print_hwp_request_pkg(int pkg, struct msr_hwp_request *h, char *str) printf("%s", str); printf("HWP_REQ_PKG: min %d max %d des %d epp %d window 0x%x (%d*10^%dus)\n", - h->hwp_min, h->hwp_max, h->hwp_desired, h->hwp_epp, - h->hwp_window, h->hwp_window & 0x7F, (h->hwp_window >> 7) & 0x7); + h->hwp_min, h->hwp_max, h->hwp_desired, h->hwp_epp, h->hwp_window, h->hwp_window & 0x7F, (h->hwp_window >> 7) & 0x7); } + void read_hwp_request_msr(int cpu, struct msr_hwp_request *hwp_req, unsigned int msr_offset) { unsigned long long msr; @@ -840,9 +977,7 @@ void write_hwp_request_msr(int cpu, struct msr_hwp_request *hwp_req, unsigned in if (debug > 1) printf("cpu%d: requesting min %d max %d des %d epp %d window 0x%0x use_pkg %d\n", - cpu, hwp_req->hwp_min, hwp_req->hwp_max, - hwp_req->hwp_desired, hwp_req->hwp_epp, - hwp_req->hwp_window, hwp_req->hwp_use_pkg); + cpu, hwp_req->hwp_min, hwp_req->hwp_max, hwp_req->hwp_desired, hwp_req->hwp_epp, hwp_req->hwp_window, hwp_req->hwp_use_pkg); msr |= HWP_MIN_PERF(ratio_2_msr_perf(hwp_req->hwp_min)); msr |= HWP_MAX_PERF(ratio_2_msr_perf(hwp_req->hwp_max)); @@ -900,6 +1035,58 @@ static int set_epb_sysfs(int cpu, int val) return (int)val; } +static void print_soc_slider(void) +{ + char buf[64]; + + if (!soc_slider_access(R_OK)) + return; + + if (sysfs_read_string(PATH_SOC_SLIDER_BALANCE, buf, sizeof(buf)) == 0) + printf("soc-slider-balance: %s\n", buf); + + if (sysfs_read_string(PATH_SOC_SLIDER_OFFSET, buf, sizeof(buf)) == 0) + printf("soc-slider-offset: %s\n", buf); +} + +static void print_platform_profile(void) +{ + char buf[64]; + + if (!platform_profile_access(R_OK)) + return; + + if (sysfs_read_string(PATH_PLATFORM_PROFILE_NAME, buf, sizeof(buf)) == 0) + printf("platform-profile-name: %s\n", buf); + + if (sysfs_read_string(PATH_PLATFORM_PROFILE, buf, sizeof(buf)) == 0) + printf("platform-profile: %s\n", buf); +} + +static int update_soc_slider(void) +{ + char tmp[32]; + + if (update_soc_slider_balance) { + snprintf(tmp, sizeof(tmp), "%d", soc_slider_balance); + if (sysfs_write_string(PATH_SOC_SLIDER_BALANCE, tmp)) + err(1, "soc-slider-balance write failed"); + } + + if (update_soc_slider_offset) { + snprintf(tmp, sizeof(tmp), "%d", soc_slider_offset); + if (sysfs_write_string(PATH_SOC_SLIDER_OFFSET, tmp)) + err(1, "soc-slider-offset write failed"); + } + + if (update_platform_profile) { + if (sysfs_write_string(PATH_PLATFORM_PROFILE, platform_profile)) + err(1, "platform-profile write failed"); + } + + return 0; +} + int print_cpu_msrs(int cpu) { struct msr_hwp_request req; @@ -908,7 +1095,7 @@ int print_cpu_msrs(int cpu) epb = get_epb_sysfs(cpu); if (epb >= 0) - printf("cpu%d: EPB %u\n", cpu, (unsigned int) epb); + printf("cpu%d: EPB %u\n", cpu, (unsigned int)epb); if (!has_hwp) return 0; @@ -936,17 +1123,13 @@ int print_pkg_msrs(int pkg) if (has_hwp_notify) { get_msr(first_cpu_in_pkg[pkg], MSR_HWP_INTERRUPT, &msr); fprintf(stderr, - "pkg%d: MSR_HWP_INTERRUPT: 0x%08llx (Excursion_Min-%sabled, Guaranteed_Perf_Change-%sabled)\n", - pkg, msr, - ((msr) & 0x2) ? "EN" : "Dis", - ((msr) & 0x1) ? "EN" : "Dis"); + "pkg%d: MSR_HWP_INTERRUPT: 0x%08llx (Excursion_Min-%sabled, Guaranteed_Perf_Change-%sabled)\n", + pkg, msr, ((msr) & 0x2) ? "EN" : "Dis", ((msr) & 0x1) ? "EN" : "Dis"); } get_msr(first_cpu_in_pkg[pkg], MSR_HWP_STATUS, &msr); fprintf(stderr, "pkg%d: MSR_HWP_STATUS: 0x%08llx (%sExcursion_Min, %sGuaranteed_Perf_Change)\n", - pkg, msr, - ((msr) & 0x4) ? "" : "No-", - ((msr) & 0x1) ? "" : "No-"); + pkg, msr, ((msr) & 0x4) ? "" : "No-", ((msr) & 0x1) ? "" : "No-"); return 0; } @@ -960,6 +1143,7 @@ int ratio_2_sysfs_khz(int ratio) return ratio * bclk_khz; } + /* * If HWP is enabled and cpufreq sysfs attribtes are present, * then update via sysfs. The intel_pstate driver may modify (clip) @@ -976,8 +1160,7 @@ void update_cpufreq_scaling_freq(int is_max, int cpu, unsigned int ratio) int retval; int khz; - sprintf(pathname, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_%s_freq", - cpu, is_max ? "max" : "min"); + sprintf(pathname, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_%s_freq", cpu, is_max ? "max" : "min"); fp = fopen(pathname, "w"); if (!fp) { @@ -1029,19 +1212,16 @@ int verify_hwp_req_self_consistency(int cpu, struct msr_hwp_request *req) { /* fail if min > max requested */ if (req->hwp_min > req->hwp_max) { - errx(1, "cpu%d: requested hwp-min %d > hwp_max %d", - cpu, req->hwp_min, req->hwp_max); + errx(1, "cpu%d: requested hwp-min %d > hwp_max %d", cpu, req->hwp_min, req->hwp_max); } /* fail if desired > max requestd */ if (req->hwp_desired && (req->hwp_desired > req->hwp_max)) { - errx(1, "cpu%d: requested hwp-desired %d > hwp_max %d", - cpu, req->hwp_desired, req->hwp_max); + errx(1, "cpu%d: requested hwp-desired %d > hwp_max %d", cpu, req->hwp_desired, req->hwp_max); } /* fail if desired < min requestd */ if (req->hwp_desired && (req->hwp_desired < req->hwp_min)) { - errx(1, "cpu%d: requested hwp-desired %d < requested hwp_min %d", - cpu, req->hwp_desired, req->hwp_min); + errx(1, "cpu%d: requested hwp-desired %d < requested hwp_min %d", cpu, req->hwp_desired, req->hwp_min); } return 0; @@ -1051,39 +1231,30 @@ int check_hwp_request_v_hwp_capabilities(int cpu, struct msr_hwp_request *req, s { if (update_hwp_max) { if (req->hwp_max > cap->highest) - errx(1, "cpu%d: requested max %d > capabilities highest %d, use --force?", - cpu, req->hwp_max, cap->highest); + errx(1, "cpu%d: requested max %d > capabilities highest %d, use --force?", cpu, req->hwp_max, cap->highest); if (req->hwp_max < cap->lowest) - errx(1, "cpu%d: requested max %d < capabilities lowest %d, use --force?", - cpu, req->hwp_max, cap->lowest); + errx(1, "cpu%d: requested max %d < capabilities lowest %d, use --force?", cpu, req->hwp_max, cap->lowest); } if (update_hwp_min) { if (req->hwp_min > cap->highest) - errx(1, "cpu%d: requested min %d > capabilities highest %d, use --force?", - cpu, req->hwp_min, cap->highest); + errx(1, "cpu%d: requested min %d > capabilities highest %d, use --force?", cpu, req->hwp_min, cap->highest); if (req->hwp_min < cap->lowest) - errx(1, "cpu%d: requested min %d < capabilities lowest %d, use --force?", - cpu, req->hwp_min, cap->lowest); + errx(1, "cpu%d: requested min %d < capabilities lowest %d, use --force?", cpu, req->hwp_min, cap->lowest); } if (update_hwp_min && update_hwp_max && (req->hwp_min > req->hwp_max)) - errx(1, "cpu%d: requested min %d > requested max %d", - cpu, req->hwp_min, req->hwp_max); + errx(1, "cpu%d: requested min %d > requested max %d", cpu, req->hwp_min, req->hwp_max); if (update_hwp_desired && req->hwp_desired) { if (req->hwp_desired > req->hwp_max) - errx(1, "cpu%d: requested desired %d > requested max %d, use --force?", - cpu, req->hwp_desired, req->hwp_max); + errx(1, "cpu%d: requested desired %d > requested max %d, use --force?", cpu, req->hwp_desired, req->hwp_max); if (req->hwp_desired < req->hwp_min) - errx(1, "cpu%d: requested desired %d < requested min %d, use --force?", - cpu, req->hwp_desired, req->hwp_min); + errx(1, "cpu%d: requested desired %d < requested min %d, use --force?", cpu, req->hwp_desired, req->hwp_min); if (req->hwp_desired < cap->lowest) - errx(1, "cpu%d: requested desired %d < capabilities lowest %d, use --force?", - cpu, req->hwp_desired, cap->lowest); + errx(1, "cpu%d: requested desired %d < capabilities lowest %d, use --force?", cpu, req->hwp_desired, cap->lowest); if (req->hwp_desired > cap->highest) - errx(1, "cpu%d: requested desired %d > capabilities highest %d, use --force?", - cpu, req->hwp_desired, cap->highest); + errx(1, "cpu%d: requested desired %d > capabilities highest %d, use --force?", cpu, req->hwp_desired, cap->highest); } return 0; @@ -1134,6 +1305,7 @@ int update_hwp_request_msr(int cpu) } return 0; } + int update_hwp_request_pkg_msr(int pkg) { struct msr_hwp_request req; @@ -1205,8 +1377,7 @@ int update_cpu_epb_sysfs(int cpu) set_epb_sysfs(cpu, new_epb); if (verbose) - printf("cpu%d: ENERGY_PERF_BIAS old: %d new: %d\n", - cpu, epb, (unsigned int) new_epb); + printf("cpu%d: ENERGY_PERF_BIAS old: %d new: %d\n", cpu, epb, (unsigned int)new_epb); return 0; } @@ -1222,7 +1393,7 @@ int update_cpu_msrs(int cpu) turbo_is_present_and_disabled = ((msr & MSR_IA32_MISC_ENABLE_TURBO_DISABLE) != 0); - if (turbo_update_value == 1) { + if (turbo_update_value == 1) { if (turbo_is_present_and_disabled) { msr &= ~MSR_IA32_MISC_ENABLE_TURBO_DISABLE; put_msr(cpu, MSR_IA32_MISC_ENABLE, msr); @@ -1291,6 +1462,7 @@ int set_max_cpu_pkg_num(int cpu) return 0; } + int mark_cpu_present(int cpu) { CPU_SET_S(cpu, cpu_setsize, cpu_present_set); @@ -1301,7 +1473,7 @@ int mark_cpu_present(int cpu) * run func(cpu) on every cpu in /proc/stat * return max_cpu number */ -int for_all_proc_cpus(int (func)(int)) +int for_all_proc_cpus(int (func) (int)) { FILE *fp; int cpu_num; @@ -1328,7 +1500,7 @@ int for_all_proc_cpus(int (func)(int)) return 0; } -void for_all_cpus_in_set(size_t set_size, cpu_set_t *cpu_set, int (func)(int)) +void for_all_cpus_in_set(size_t set_size, cpu_set_t *cpu_set, int (func) (int)) { int cpu_num; @@ -1336,7 +1508,8 @@ void for_all_cpus_in_set(size_t set_size, cpu_set_t *cpu_set, int (func)(int)) if (CPU_ISSET_S(cpu_num, set_size, cpu_set)) func(cpu_num); } -int for_all_cpus_in_set_and(size_t set_size, cpu_set_t *cpu_set, int (func)(int)) + +int for_all_cpus_in_set_and(size_t set_size, cpu_set_t *cpu_set, int (func) (int)) { int cpu_num; int retval = 1; @@ -1385,7 +1558,7 @@ void verify_hwp_is_enabled(void) { int retval; - if (!has_hwp) /* set in early_cpuid() */ + if (!has_hwp) /* set in early_cpuid() */ return; retval = for_all_cpus_in_set_and(cpu_setsize, cpu_selected_set, is_hwp_enabled_on_cpu); @@ -1402,21 +1575,18 @@ int req_update_bounds_check(void) return 0; /* fail if min > max requested */ - if ((update_hwp_max && update_hwp_min) && - (req_update.hwp_min > req_update.hwp_max)) { + if ((update_hwp_max && update_hwp_min) && (req_update.hwp_min > req_update.hwp_max)) { printf("hwp-min %d > hwp_max %d\n", req_update.hwp_min, req_update.hwp_max); return -EINVAL; } /* fail if desired > max requestd */ - if (req_update.hwp_desired && update_hwp_max && - (req_update.hwp_desired > req_update.hwp_max)) { + if (req_update.hwp_desired && update_hwp_max && (req_update.hwp_desired > req_update.hwp_max)) { printf("hwp-desired cannot be greater than hwp_max\n"); return -EINVAL; } /* fail if desired < min requestd */ - if (req_update.hwp_desired && update_hwp_min && - (req_update.hwp_desired < req_update.hwp_min)) { + if (req_update.hwp_desired && update_hwp_min && (req_update.hwp_desired < req_update.hwp_min)) { printf("hwp-desired cannot be less than hwp_min\n"); return -EINVAL; } @@ -1459,9 +1629,7 @@ void probe_dev_msr(void) } } -static void get_cpuid_or_exit(unsigned int leaf, - unsigned int *eax, unsigned int *ebx, - unsigned int *ecx, unsigned int *edx) +static void get_cpuid_or_exit(unsigned int leaf, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { if (!__get_cpuid(leaf, eax, ebx, ecx, edx)) errx(1, "Processor not supported\n"); @@ -1515,8 +1683,7 @@ void parse_cpuid(void) genuine_intel = 1; if (debug) - fprintf(stderr, "CPUID(0): %.4s%.4s%.4s ", - (char *)&ebx, (char *)&edx, (char *)&ecx); + fprintf(stderr, "CPUID(0): %.4s%.4s%.4s ", (char *)&ebx, (char *)&edx, (char *)&ecx); get_cpuid_or_exit(1, &fms, &ebx, &ecx, &edx); family = (fms >> 8) & 0xf; @@ -1526,23 +1693,18 @@ void parse_cpuid(void) model += ((fms >> 16) & 0xf) << 4; if (debug) { - fprintf(stderr, "%d CPUID levels; family:model:stepping 0x%x:%x:%x (%d:%d:%d)\n", - max_level, family, model, stepping, family, model, stepping); + fprintf(stderr, "%d CPUID levels; family:model:stepping 0x%x:%x:%x (%d:%d:%d)\n", max_level, family, model, stepping, family, model, stepping); fprintf(stderr, "CPUID(1): %s %s %s %s %s %s %s %s\n", ecx & (1 << 0) ? "SSE3" : "-", ecx & (1 << 3) ? "MONITOR" : "-", ecx & (1 << 7) ? "EIST" : "-", ecx & (1 << 8) ? "TM2" : "-", - edx & (1 << 4) ? "TSC" : "-", - edx & (1 << 5) ? "MSR" : "-", - edx & (1 << 22) ? "ACPI-TM" : "-", - edx & (1 << 29) ? "TM" : "-"); + edx & (1 << 4) ? "TSC" : "-", edx & (1 << 5) ? "MSR" : "-", edx & (1 << 22) ? "ACPI-TM" : "-", edx & (1 << 29) ? "TM" : "-"); } if (!(edx & (1 << 5))) errx(1, "CPUID: no MSR"); - get_cpuid_or_exit(0x6, &eax, &ebx, &ecx, &edx); /* turbo_is_enabled already set */ /* has_hwp already set */ @@ -1562,12 +1724,9 @@ void parse_cpuid(void) turbo_is_enabled ? "" : "No-", has_hwp ? "" : "No-", has_hwp_notify ? "" : "No-", - has_hwp_activity_window ? "" : "No-", - has_hwp_epp ? "" : "No-", - has_hwp_request_pkg ? "" : "No-", - has_epb ? "" : "No-"); + has_hwp_activity_window ? "" : "No-", has_hwp_epp ? "" : "No-", has_hwp_request_pkg ? "" : "No-", has_epb ? "" : "No-"); - return; /* success */ + return; /* success */ } int main(int argc, char **argv) @@ -1577,7 +1736,7 @@ int main(int argc, char **argv) probe_dev_msr(); init_data_structures(); - early_cpuid(); /* initial cpuid parse before cmdline */ + early_cpuid(); /* initial cpuid parse before cmdline */ cmdline(argc, argv); @@ -1586,7 +1745,7 @@ int main(int argc, char **argv) parse_cpuid(); - /* If CPU-set and PKG-set are not initialized, default to all CPUs */ + /* If CPU-set and PKG-set are not initialized, default to all CPUs */ if ((cpu_selected_set == 0) && (pkg_selected_set == 0)) cpu_selected_set = cpu_present_set; @@ -1604,10 +1763,13 @@ int main(int argc, char **argv) return -EINVAL; /* display information only, no updates to settings */ - if (!update_epb && !update_turbo && !hwp_update_enabled()) { + if (!update_epb && !update_turbo && !hwp_update_enabled() && !update_soc_slider_balance && !update_soc_slider_offset && !update_platform_profile) { if (cpu_selected_set) for_all_cpus_in_set(cpu_setsize, cpu_selected_set, print_cpu_msrs); + print_soc_slider(); + print_platform_profile(); + if (has_hwp_request_pkg) { if (pkg_selected_set == 0) pkg_selected_set = pkg_present_set; @@ -1628,5 +1790,8 @@ int main(int argc, char **argv) } else if (pkg_selected_set) for_packages(pkg_selected_set, update_hwp_request_pkg_msr); + if (update_soc_slider_balance || update_soc_slider_offset || update_platform_profile) + update_soc_slider(); + return 0; } diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild index 53d84a6874b7..2be1df80fcc9 100644 --- a/tools/testing/cxl/Kbuild +++ b/tools/testing/cxl/Kbuild @@ -11,8 +11,12 @@ ldflags-y += --wrap=devm_cxl_endpoint_decoders_setup ldflags-y += --wrap=hmat_get_extended_linear_cache_size ldflags-y += --wrap=devm_cxl_add_dport_by_dev ldflags-y += --wrap=devm_cxl_switch_port_decoders_setup +ldflags-y += --wrap=walk_hmem_resources +ldflags-y += --wrap=region_intersects +ldflags-y += --wrap=region_intersects_soft_reserve DRIVERS := ../../../drivers +DAX_HMEM_SRC := $(DRIVERS)/dax/hmem CXL_SRC := $(DRIVERS)/cxl CXL_CORE_SRC := $(DRIVERS)/cxl/core ccflags-y := -I$(srctree)/drivers/cxl/ @@ -59,7 +63,7 @@ cxl_core-y += $(CXL_CORE_SRC)/hdm.o cxl_core-y += $(CXL_CORE_SRC)/pmu.o cxl_core-y += $(CXL_CORE_SRC)/cdat.o cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o -cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o +cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/region_pmem.o $(CXL_CORE_SRC)/region_dax.o cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE_SRC)/mce.o cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += $(CXL_CORE_SRC)/edac.o @@ -70,6 +74,9 @@ cxl_core-y += config_check.o cxl_core-y += cxl_core_test.o cxl_core-y += cxl_core_exports.o +obj-m += dax_hmem.o +dax_hmem-y := $(DAX_HMEM_SRC)/hmem.o + KBUILD_CFLAGS := $(filter-out -Wmissing-prototypes -Wmissing-declarations, $(KBUILD_CFLAGS)) obj-m += test/ diff --git a/tools/testing/cxl/test/Kbuild b/tools/testing/cxl/test/Kbuild index af50972c8b6d..c168e3c998a7 100644 --- a/tools/testing/cxl/test/Kbuild +++ b/tools/testing/cxl/test/Kbuild @@ -7,6 +7,7 @@ obj-m += cxl_mock_mem.o obj-m += cxl_translate.o cxl_test-y := cxl.o +cxl_test-y += hmem_test.o cxl_mock-y := mock.o cxl_mock_mem-y := mem.o diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 81e2aef3627a..418669927fb0 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -16,6 +16,7 @@ static int interleave_arithmetic; static bool extended_linear_cache; +static bool fail_autoassemble; #define FAKE_QTG_ID 42 @@ -51,6 +52,31 @@ struct platform_device *cxl_mem_single[NR_MEM_SINGLE]; static struct platform_device *cxl_rch[NR_CXL_RCH]; static struct platform_device *cxl_rcd[NR_CXL_RCH]; +/* + * Decoder registry + * + * Record decoder programming so that the topology can be reconstructed + * after cxl_acpi unbind/bind. This allows a user-created region config + * to be replayed as if firmware had provided the region at enumeration + * time. + * + * Entries are keyed by a stable port identity (port->uport_dev) combined + * with the decoder id. Decoder state is saved at initialization and + * updated on commit and reset. + * + * On re-enumeration mock_init_hdm_decoder() consults this registry to + * restore enabled decoders. Disabled decoders are reinitialized to a + * clean default state rather than replaying stale programming. + */ +static DEFINE_XARRAY(decoder_registry); + +/* + * When set, decoder reset will not update the registry. This allows + * region destroy operations to reset live decoders without erasing + * the saved programming needed for replay after re-enumeration. + */ +static bool decoder_reset_preserve_registry; + static inline bool is_multi_bridge(struct device *dev) { int i; @@ -704,6 +730,194 @@ static int map_targets(struct device *dev, void *data) return 0; } +/* + * Build a stable registry key from the decoder's upstream port identity + * and decoder id. + * + * Decoder objects and cxl_port objects are reallocated on each enumeration, + * so their addresses cannot be used directly as replay keys. However, + * port->uport_dev is stable for a given topology across cxl_acpi unbind/bind + * in cxl_test, so use that as the port identity and pack the local decoder + * id into the low bits. + * + * The key is formed as: + * ((unsigned long)port->uport_dev << 4) | cxld->id + * + * The low bits hold the decoder id (which must fit in 4 bits) while + * the remaining bits identify the upstream port. This key is only used + * within cxl_test to locate saved decoder state during replay. + */ +static unsigned long cxld_registry_index(struct cxl_decoder *cxld) +{ + struct cxl_port *port = to_cxl_port(cxld->dev.parent); + + dev_WARN_ONCE(&port->dev, cxld->id >= 16, + "decoder id:%d out of range\n", cxld->id); + return (((unsigned long)port->uport_dev) << 4) | cxld->id; +} + +struct cxl_test_decoder { + union { + struct cxl_switch_decoder cxlsd; + struct cxl_endpoint_decoder cxled; + }; + struct range dpa_range; +}; + +static struct cxl_test_decoder *cxld_registry_find(struct cxl_decoder *cxld) +{ + return xa_load(&decoder_registry, cxld_registry_index(cxld)); +} + +#define dbg_cxld(port, msg, cxld) \ + do { \ + struct cxl_decoder *___d = (cxld); \ + dev_dbg((port)->uport_dev, \ + "decoder%d: %s range: %#llx-%#llx iw: %d ig: %d flags: %#lx\n", \ + ___d->id, msg, ___d->hpa_range.start, \ + ___d->hpa_range.end + 1, ___d->interleave_ways, \ + ___d->interleave_granularity, ___d->flags); \ + } while (0) + +static int mock_decoder_commit(struct cxl_decoder *cxld); +static void mock_decoder_reset(struct cxl_decoder *cxld); +static void init_disabled_mock_decoder(struct cxl_decoder *cxld); + +static void cxld_copy(struct cxl_decoder *a, struct cxl_decoder *b) +{ + a->id = b->id; + a->hpa_range = b->hpa_range; + a->interleave_ways = b->interleave_ways; + a->interleave_granularity = b->interleave_granularity; + a->target_type = b->target_type; + a->flags = b->flags; + a->commit = mock_decoder_commit; + a->reset = mock_decoder_reset; +} + +/* + * Restore decoder programming saved in the registry. + * + * Only decoders that were saved enabled are restored. Disabled decoders + * are left in their default inactive state so that stale programming is + * not resurrected after topology replay. + * + * For endpoint decoders this also restores the DPA reservation needed + * to reconstruct committed mappings. + */ +static int cxld_registry_restore(struct cxl_decoder *cxld, + struct cxl_test_decoder *td) +{ + struct cxl_port *port = to_cxl_port(cxld->dev.parent); + int rc; + + if (is_switch_decoder(&cxld->dev)) { + struct cxl_switch_decoder *cxlsd = + to_cxl_switch_decoder(&cxld->dev); + + if (!(td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE)) + return 0; + + dbg_cxld(port, "restore", &td->cxlsd.cxld); + cxld_copy(cxld, &td->cxlsd.cxld); + WARN_ON(cxlsd->nr_targets != td->cxlsd.nr_targets); + + /* Restore saved target intent; live dport binding happens later */ + for (int i = 0; i < cxlsd->nr_targets; i++) { + cxlsd->target[i] = NULL; + cxld->target_map[i] = td->cxlsd.cxld.target_map[i]; + } + + port->commit_end = cxld->id; + + } else { + struct cxl_endpoint_decoder *cxled = + to_cxl_endpoint_decoder(&cxld->dev); + + if (!(td->cxled.cxld.flags & CXL_DECODER_F_ENABLE)) + return 0; + + dbg_cxld(port, "restore", &td->cxled.cxld); + cxld_copy(cxld, &td->cxled.cxld); + cxled->state = td->cxled.state; + cxled->skip = td->cxled.skip; + if (range_len(&td->dpa_range)) { + rc = devm_cxl_dpa_reserve(cxled, td->dpa_range.start, + range_len(&td->dpa_range), + td->cxled.skip); + if (rc) { + init_disabled_mock_decoder(cxld); + return rc; + } + } + port->commit_end = cxld->id; + } + + return 0; +} + +static void __cxld_registry_save(struct cxl_test_decoder *td, + struct cxl_decoder *cxld) +{ + if (is_switch_decoder(&cxld->dev)) { + struct cxl_switch_decoder *cxlsd = + to_cxl_switch_decoder(&cxld->dev); + + cxld_copy(&td->cxlsd.cxld, cxld); + td->cxlsd.nr_targets = cxlsd->nr_targets; + + /* Save target port_id as a stable identify for the dport */ + for (int i = 0; i < cxlsd->nr_targets; i++) { + struct cxl_dport *dport; + + if (!cxlsd->target[i]) + continue; + + dport = cxlsd->target[i]; + td->cxlsd.cxld.target_map[i] = dport->port_id; + } + } else { + struct cxl_endpoint_decoder *cxled = + to_cxl_endpoint_decoder(&cxld->dev); + + cxld_copy(&td->cxled.cxld, cxld); + td->cxled.state = cxled->state; + td->cxled.skip = cxled->skip; + + if (!(cxld->flags & CXL_DECODER_F_ENABLE)) { + td->dpa_range.start = 0; + td->dpa_range.end = -1; + } else if (cxled->dpa_res) { + td->dpa_range.start = cxled->dpa_res->start; + td->dpa_range.end = cxled->dpa_res->end; + } else { + td->dpa_range.start = 0; + td->dpa_range.end = -1; + } + } +} + +static void cxld_registry_save(struct cxl_test_decoder *td, + struct cxl_decoder *cxld) +{ + struct cxl_port *port = to_cxl_port(cxld->dev.parent); + + dbg_cxld(port, "save", cxld); + __cxld_registry_save(td, cxld); +} + +static void cxld_registry_update(struct cxl_decoder *cxld) +{ + struct cxl_test_decoder *td = cxld_registry_find(cxld); + struct cxl_port *port = to_cxl_port(cxld->dev.parent); + + if (WARN_ON_ONCE(!td)) + return; + + dbg_cxld(port, "update", cxld); + __cxld_registry_save(td, cxld); +} + static int mock_decoder_commit(struct cxl_decoder *cxld) { struct cxl_port *port = to_cxl_port(cxld->dev.parent); @@ -723,6 +937,13 @@ static int mock_decoder_commit(struct cxl_decoder *cxld) port->commit_end++; cxld->flags |= CXL_DECODER_F_ENABLE; + if (is_endpoint_decoder(&cxld->dev)) { + struct cxl_endpoint_decoder *cxled = + to_cxl_endpoint_decoder(&cxld->dev); + + cxled->state = CXL_DECODER_STATE_AUTO; + } + cxld_registry_update(cxld); return 0; } @@ -743,6 +964,65 @@ static void mock_decoder_reset(struct cxl_decoder *cxld) "%s: out of order reset, expected decoder%d.%d\n", dev_name(&cxld->dev), port->id, port->commit_end); cxld->flags &= ~CXL_DECODER_F_ENABLE; + + if (is_endpoint_decoder(&cxld->dev)) { + struct cxl_endpoint_decoder *cxled = + to_cxl_endpoint_decoder(&cxld->dev); + + cxled->state = CXL_DECODER_STATE_MANUAL; + cxled->skip = 0; + } + if (decoder_reset_preserve_registry) + dev_dbg(port->uport_dev, "decoder%d: skip registry update\n", + cxld->id); + else + cxld_registry_update(cxld); +} + +static struct cxl_test_decoder *cxld_registry_new(struct cxl_decoder *cxld) +{ + struct cxl_test_decoder *td __free(kfree) = + kzalloc(sizeof(*td), GFP_KERNEL); + unsigned long key = cxld_registry_index(cxld); + + if (!td) + return NULL; + + if (xa_insert(&decoder_registry, key, td, GFP_KERNEL)) { + WARN_ON(1); + return NULL; + } + + cxld_registry_save(td, cxld); + return no_free_ptr(td); +} + +static void init_disabled_mock_decoder(struct cxl_decoder *cxld) +{ + cxld->hpa_range.start = 0; + cxld->hpa_range.end = -1; + cxld->interleave_ways = 1; + cxld->interleave_granularity = 0; + cxld->target_type = CXL_DECODER_HOSTONLYMEM; + cxld->flags = 0; + cxld->commit = mock_decoder_commit; + cxld->reset = mock_decoder_reset; + + if (is_switch_decoder(&cxld->dev)) { + struct cxl_switch_decoder *cxlsd = + to_cxl_switch_decoder(&cxld->dev); + + for (int i = 0; i < cxlsd->nr_targets; i++) { + cxlsd->target[i] = NULL; + cxld->target_map[i] = 0; + } + } else { + struct cxl_endpoint_decoder *cxled = + to_cxl_endpoint_decoder(&cxld->dev); + + cxled->state = CXL_DECODER_STATE_MANUAL; + cxled->skip = 0; + } } static void default_mock_decoder(struct cxl_decoder *cxld) @@ -757,6 +1037,8 @@ static void default_mock_decoder(struct cxl_decoder *cxld) cxld->target_type = CXL_DECODER_HOSTONLYMEM; cxld->commit = mock_decoder_commit; cxld->reset = mock_decoder_reset; + + WARN_ON_ONCE(!cxld_registry_new(cxld)); } static int first_decoder(struct device *dev, const void *data) @@ -771,13 +1053,29 @@ static int first_decoder(struct device *dev, const void *data) return 0; } -static void mock_init_hdm_decoder(struct cxl_decoder *cxld) +/* + * Initialize a decoder during HDM enumeration. + * + * If a saved registry entry exists: + * - enabled decoders are restored from the saved programming + * - disabled decoders are initialized in a clean disabled state + * + * If no registry entry exists the decoder follows the normal mock + * initialization path, including the special auto-region setup for + * the first endpoints under host-bridge0. + * + * Returns true if decoder state was restored from the registry. In + * that case the saved decode configuration (including target mapping) + * has already been applied and the map_targets() is skipped. + */ +static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) { struct acpi_cedt_cfmws *window = mock_cfmws[0]; struct platform_device *pdev = NULL; struct cxl_endpoint_decoder *cxled; struct cxl_switch_decoder *cxlsd; struct cxl_port *port, *iter; + struct cxl_test_decoder *td; struct cxl_memdev *cxlmd; struct cxl_dport *dport; struct device *dev; @@ -804,6 +1102,24 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld) port = NULL; } while (port); port = cxled_to_port(cxled); + } else { + port = to_cxl_port(cxld->dev.parent); + } + + td = cxld_registry_find(cxld); + if (td) { + bool enabled; + + if (is_switch_decoder(&cxld->dev)) + enabled = td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE; + else + enabled = td->cxled.cxld.flags & CXL_DECODER_F_ENABLE; + + if (enabled) + return !cxld_registry_restore(cxld, td); + + init_disabled_mock_decoder(cxld); + return false; } /* @@ -814,9 +1130,16 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld) * * See 'cxl list -BMPu -m cxl_mem.0,cxl_mem.4' */ - if (!hb0 || pdev->id % 4 || pdev->id > 4 || cxld->id > 0) { + if (!is_endpoint_decoder(&cxld->dev) || !hb0 || pdev->id % 4 || + pdev->id > 4 || cxld->id > 0) { default_mock_decoder(cxld); - return; + return false; + } + + /* Simulate missing cxl_mem.4 configuration */ + if (hb0 && pdev->id == 4 && cxld->id == 0 && fail_autoassemble) { + default_mock_decoder(cxld); + return false; } base = window->base_hpa; @@ -838,6 +1161,7 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld) cxld->commit = mock_decoder_commit; cxld->reset = mock_decoder_reset; + WARN_ON_ONCE(!cxld_registry_new(cxld)); /* * Now that endpoint decoder is set up, walk up the hierarchy * and setup the switch and root port decoders targeting @cxlmd. @@ -859,14 +1183,14 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld) /* put cxl_mem.4 second in the decode order */ if (pdev->id == 4) { cxlsd->target[1] = dport; - cxld->target_map[1] = dport->port_id; + cxlsd->cxld.target_map[1] = dport->port_id; } else { cxlsd->target[0] = dport; - cxld->target_map[0] = dport->port_id; + cxlsd->cxld.target_map[0] = dport->port_id; } } else { cxlsd->target[0] = dport; - cxld->target_map[0] = dport->port_id; + cxlsd->cxld.target_map[0] = dport->port_id; } cxld = &cxlsd->cxld; cxld->target_type = CXL_DECODER_HOSTONLYMEM; @@ -885,8 +1209,14 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld) .start = base, .end = base + mock_auto_region_size - 1, }; + cxld->commit = mock_decoder_commit; + cxld->reset = mock_decoder_reset; + + cxld_registry_update(cxld); put_device(dev); } + + return false; } static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm, @@ -895,6 +1225,7 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm, struct cxl_port *port = cxlhdm->port; struct cxl_port *parent_port = to_cxl_port(port->dev.parent); int target_count, i; + bool restored; if (is_cxl_endpoint(port)) target_count = 0; @@ -934,10 +1265,8 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm, } ctx.target_map = cxld->target_map; - - mock_init_hdm_decoder(cxld); - - if (target_count) { + restored = mock_init_hdm_decoder(cxld); + if (target_count && !restored) { rc = device_for_each_child(port->uport_dev, &ctx, map_targets); if (rc) { @@ -1114,6 +1443,53 @@ static void mock_cxl_endpoint_parse_cdat(struct cxl_port *port) cxl_endpoint_get_perf_coordinates(port, ep_c); } +/* + * Simulate that the first half of mock CXL Window 0 is "Soft Reserve" capacity + */ +static int mock_walk_hmem_resources(struct device *host, walk_hmem_fn fn) +{ + struct acpi_cedt_cfmws *cfmws = mock_cfmws[0]; + struct resource window = + DEFINE_RES_MEM(cfmws->base_hpa, cfmws->window_size / 2); + + dev_dbg(host, "walk cxl_test resource: %pr\n", &window); + return fn(host, 0, &window); +} + +/* + * This should only be called by the dax_hmem case, treat mismatches (negative + * result) as "fallback to base region_intersects()". Simulate that the first + * half of mock CXL Window 0 is IORES_DESC_CXL capacity. + */ +static int mock_region_intersects(resource_size_t start, size_t size, + unsigned long flags, unsigned long desc) +{ + struct resource res = DEFINE_RES_MEM(start, size); + struct acpi_cedt_cfmws *cfmws = mock_cfmws[0]; + struct resource window = + DEFINE_RES_MEM(cfmws->base_hpa, cfmws->window_size / 2); + + if (resource_overlaps(&res, &window)) + return REGION_INTERSECTS; + pr_debug("warning: no cxl_test CXL intersection for %pr\n", &res); + return -1; +} + + +static int +mock_region_intersects_soft_reserve(resource_size_t start, size_t size) +{ + struct resource res = DEFINE_RES_MEM(start, size); + struct acpi_cedt_cfmws *cfmws = mock_cfmws[0]; + struct resource window = + DEFINE_RES_MEM(cfmws->base_hpa, cfmws->window_size / 2); + + if (resource_overlaps(&res, &window)) + return REGION_INTERSECTS; + pr_debug("warning: no cxl_test soft reserve intersection for %pr\n", &res); + return -1; +} + static struct cxl_mock_ops cxl_mock_ops = { .is_mock_adev = is_mock_adev, .is_mock_bridge = is_mock_bridge, @@ -1129,6 +1505,9 @@ static struct cxl_mock_ops cxl_mock_ops = { .devm_cxl_add_dport_by_dev = mock_cxl_add_dport_by_dev, .hmat_get_extended_linear_cache_size = mock_hmat_get_extended_linear_cache_size, + .walk_hmem_resources = mock_walk_hmem_resources, + .region_intersects = mock_region_intersects, + .region_intersects_soft_reserve = mock_region_intersects_soft_reserve, .list = LIST_HEAD_INIT(cxl_mock_ops.list), }; @@ -1415,6 +1794,33 @@ err_mem: return rc; } +static ssize_t +decoder_reset_preserve_registry_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%d\n", decoder_reset_preserve_registry); +} + +static ssize_t +decoder_reset_preserve_registry_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + + rc = kstrtobool(buf, &decoder_reset_preserve_registry); + if (rc) + return rc; + return count; +} + +static DEVICE_ATTR_RW(decoder_reset_preserve_registry); + +static struct attribute *cxl_acpi_attrs[] = { + &dev_attr_decoder_reset_preserve_registry.attr, NULL +}; +ATTRIBUTE_GROUPS(cxl_acpi); + static __init int cxl_test_init(void) { int rc, i; @@ -1545,6 +1951,7 @@ static __init int cxl_test_init(void) mock_companion(&acpi0017_mock, &cxl_acpi->dev); acpi0017_mock.dev.bus = &platform_bus_type; + cxl_acpi->dev.groups = cxl_acpi_groups; rc = platform_device_add(cxl_acpi); if (rc) @@ -1554,8 +1961,14 @@ static __init int cxl_test_init(void) if (rc) goto err_root; + rc = hmem_test_init(); + if (rc) + goto err_mem; + return 0; +err_mem: + cxl_mem_exit(); err_root: platform_device_put(cxl_acpi); err_rch: @@ -1589,10 +2002,22 @@ err_gen_pool_create: return rc; } +static void free_decoder_registry(void) +{ + unsigned long index; + void *entry; + + xa_for_each(&decoder_registry, index, entry) { + xa_erase(&decoder_registry, index); + kfree(entry); + } +} + static __exit void cxl_test_exit(void) { int i; + hmem_test_exit(); cxl_mem_exit(); platform_device_unregister(cxl_acpi); cxl_rch_topo_exit(); @@ -1614,12 +2039,16 @@ static __exit void cxl_test_exit(void) depopulate_all_mock_resources(); gen_pool_destroy(cxl_mock_pool); unregister_cxl_mock_ops(&cxl_mock_ops); + free_decoder_registry(); + xa_destroy(&decoder_registry); } module_param(interleave_arithmetic, int, 0444); MODULE_PARM_DESC(interleave_arithmetic, "Modulo:0, XOR:1"); module_param(extended_linear_cache, bool, 0444); MODULE_PARM_DESC(extended_linear_cache, "Enable extended linear cache support"); +module_param(fail_autoassemble, bool, 0444); +MODULE_PARM_DESC(fail_autoassemble, "Simulate missing member of an auto-region"); module_init(cxl_test_init); module_exit(cxl_test_exit); MODULE_LICENSE("GPL v2"); diff --git a/tools/testing/cxl/test/hmem_test.c b/tools/testing/cxl/test/hmem_test.c new file mode 100644 index 000000000000..3a1a089e1721 --- /dev/null +++ b/tools/testing/cxl/test/hmem_test.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2026 Intel Corporation */ +#include +#include +#include "../../../drivers/dax/bus.h" + +static bool hmem_test; + +static void hmem_test_work(struct work_struct *work) +{ +} + +static void hmem_test_release(struct device *dev) +{ + struct hmem_platform_device *hpdev = + container_of(dev, typeof(*hpdev), pdev.dev); + + memset(hpdev, 0, sizeof(*hpdev)); +} + +static struct hmem_platform_device hmem_test_device = { + .pdev = { + .name = "hmem_platform", + .id = 1, + .dev = { + .release = hmem_test_release, + }, + }, + .work = __WORK_INITIALIZER(hmem_test_device.work, hmem_test_work), +}; + +int hmem_test_init(void) +{ + if (!hmem_test) + return 0; + + return platform_device_register(&hmem_test_device.pdev); +} + +void hmem_test_exit(void) +{ + if (hmem_test) + platform_device_unregister(&hmem_test_device.pdev); +} + +module_param(hmem_test, bool, 0444); +MODULE_PARM_DESC(hmem_test, "Enable/disable the dax_hmem test platform device"); diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index cb87e8c0e63c..271c7ad8cc32 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -1695,6 +1695,9 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) struct cxl_dpa_info range_info = { 0 }; int rc; + /* Increase async probe race window */ + usleep_range(500*1000, 1000*1000); + mdata = devm_kzalloc(dev, sizeof(*mdata), GFP_KERNEL); if (!mdata) return -ENOMEM; @@ -1716,7 +1719,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) if (rc) return rc; - mds = cxl_memdev_state_create(dev); + mds = cxl_memdev_state_create(dev, pdev->id + 1, 0); if (IS_ERR(mds)) return PTR_ERR(mds); @@ -1732,7 +1735,6 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) mds->event.buf = (struct cxl_get_event_payload *) mdata->event_buf; INIT_DELAYED_WORK(&mds->security.poll_dwork, cxl_mockmem_sanitize_work); - cxlds->serial = pdev->id + 1; if (is_rcd(pdev)) cxlds->rcd = true; diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c index b8fcb50c1027..6454b868b122 100644 --- a/tools/testing/cxl/test/mock.c +++ b/tools/testing/cxl/test/mock.c @@ -251,6 +251,56 @@ struct cxl_dport *__wrap_devm_cxl_add_dport_by_dev(struct cxl_port *port, } EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_add_dport_by_dev, "CXL"); +int __wrap_region_intersects(resource_size_t start, size_t size, + unsigned long flags, unsigned long desc) +{ + int rc = -1; + int index; + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); + + if (ops) + rc = ops->region_intersects(start, size, flags, desc); + if (rc < 0) + rc = region_intersects(start, size, flags, desc); + put_cxl_mock_ops(index); + + return rc; +} +EXPORT_SYMBOL_GPL(__wrap_region_intersects); + +int __wrap_region_intersects_soft_reserve(resource_size_t start, size_t size) +{ + int rc = -1; + int index; + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); + + if (ops) + rc = ops->region_intersects_soft_reserve(start, size); + if (rc < 0) + rc = region_intersects_soft_reserve(start, size); + put_cxl_mock_ops(index); + + return rc; +} +EXPORT_SYMBOL_GPL(__wrap_region_intersects_soft_reserve); + +int __wrap_walk_hmem_resources(struct device *host, walk_hmem_fn fn) +{ + int index, rc = 0; + bool is_mock = strcmp(dev_name(host), "hmem_platform.1") == 0; + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); + + if (is_mock) { + if (ops) + rc = ops->walk_hmem_resources(host, fn); + } else { + rc = walk_hmem_resources(host, fn); + } + put_cxl_mock_ops(index); + return rc; +} +EXPORT_SYMBOL_GPL(__wrap_walk_hmem_resources); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("cxl_test: emulation module"); MODULE_IMPORT_NS("ACPI"); diff --git a/tools/testing/cxl/test/mock.h b/tools/testing/cxl/test/mock.h index 2684b89c8aa2..4f57dc80ae7d 100644 --- a/tools/testing/cxl/test/mock.h +++ b/tools/testing/cxl/test/mock.h @@ -2,6 +2,7 @@ #include #include +#include #include struct cxl_mock_ops { @@ -27,8 +28,15 @@ struct cxl_mock_ops { int (*hmat_get_extended_linear_cache_size)(struct resource *backing_res, int nid, resource_size_t *cache_size); + int (*walk_hmem_resources)(struct device *host, walk_hmem_fn fn); + int (*region_intersects)(resource_size_t start, size_t size, + unsigned long flags, unsigned long desc); + int (*region_intersects_soft_reserve)(resource_size_t start, + size_t size); }; +int hmem_test_init(void); +void hmem_test_exit(void); void register_cxl_mock_ops(struct cxl_mock_ops *ops); void unregister_cxl_mock_ops(struct cxl_mock_ops *ops); struct cxl_mock_ops *get_cxl_mock_ops(int *index); diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 112f9ca2444b..f94ed2e98887 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1855,7 +1855,7 @@ sub save_logs { my ($result, $basedir) = @_; my @t = localtime; my $date = sprintf "%04d%02d%02d%02d%02d%02d", - 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; + 1900+$t[5],$t[4]+1,$t[3],$t[2],$t[1],$t[0]; my $type = $build_type; if ($type =~ /useconfig/) { @@ -1878,6 +1878,12 @@ sub save_logs { "testlog" => $testlog, ); + if (defined($opt{"LOG_FILE"})) { + if (-f $opt{"LOG_FILE"}) { + cp $opt{"LOG_FILE"}, "$dir/logfile"; + } + } + while (my ($name, $source) = each(%files)) { if (-f "$source") { cp "$source", "$dir/$name" or diff --git a/tools/testing/memblock/internal.h b/tools/testing/memblock/internal.h index 009b97bbdd22..b6b1d147fd75 100644 --- a/tools/testing/memblock/internal.h +++ b/tools/testing/memblock/internal.h @@ -11,9 +11,22 @@ static int memblock_debug = 1; #define pr_warn_ratelimited(fmt, ...) printf(fmt, ##__VA_ARGS__) +#define K(x) ((x) << (PAGE_SHIFT-10)) + bool mirrored_kernelcore = false; struct page {}; +static inline void *page_address(struct page *page) +{ + BUG(); + return page; +} + +static inline struct page *virt_to_page(void *virt) +{ + BUG(); + return virt; +} void memblock_free_pages(unsigned long pfn, unsigned int order) { @@ -23,10 +36,34 @@ static inline void accept_memory(phys_addr_t start, unsigned long size) { } -static inline unsigned long free_reserved_area(void *start, void *end, - int poison, const char *s) +unsigned long free_reserved_area(void *start, void *end, int poison, const char *s); +void free_reserved_page(struct page *page); + +static inline bool deferred_pages_enabled(void) { - return 0; + return false; } +#define for_each_valid_pfn(pfn, start_pfn, end_pfn) \ + for ((pfn) = (start_pfn); (pfn) < (end_pfn); (pfn)++) + +static inline void *kasan_reset_tag(const void *addr) +{ + return (void *)addr; +} + +static inline bool __is_kernel(unsigned long addr) +{ + return false; +} + +#define for_each_valid_pfn(pfn, start_pfn, end_pfn) \ + for ((pfn) = (start_pfn); (pfn) < (end_pfn); (pfn)++) + +static inline void init_deferred_page(unsigned long pfn, int nid) +{ +} + +#define __SetPageReserved(p) ((void)(p)) + #endif diff --git a/tools/testing/memblock/linux/string_helpers.h b/tools/testing/memblock/linux/string_helpers.h new file mode 100644 index 000000000000..dbf015cfff31 --- /dev/null +++ b/tools/testing/memblock/linux/string_helpers.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_STRING_HELPERS_H_ +#define _LINUX_STRING_HELPERS_H_ + +/* + * Header stub to avoid test build breakage; we don't need to + * actually implement string_get_size() as it's not used in the tests. + */ + +#endif diff --git a/tools/testing/memblock/mmzone.c b/tools/testing/memblock/mmzone.c index d3d58851864e..e719450f81cb 100644 --- a/tools/testing/memblock/mmzone.c +++ b/tools/testing/memblock/mmzone.c @@ -11,10 +11,6 @@ struct pglist_data *next_online_pgdat(struct pglist_data *pgdat) return NULL; } -void reserve_bootmem_region(phys_addr_t start, phys_addr_t end, int nid) -{ -} - void atomic_long_set(atomic_long_t *v, long i) { } diff --git a/tools/testing/radix-tree/maple.c b/tools/testing/radix-tree/maple.c index feedd5ab7058..0607913a3022 100644 --- a/tools/testing/radix-tree/maple.c +++ b/tools/testing/radix-tree/maple.c @@ -2,7 +2,7 @@ /* * maple_tree.c: Userspace testing for maple tree test-suite * Copyright (c) 2018-2022 Oracle Corporation - * Author: Liam R. Howlett + * Author: Liam R. Howlett * * Any tests that require internal knowledge of the tree or threads and other * difficult to handle in kernel tests. diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 984abb6d42ab..6e59b8f63e41 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -98,6 +98,7 @@ TARGETS += proc TARGETS += pstore TARGETS += ptrace TARGETS += openat2 +TARGETS += rdma TARGETS += resctrl TARGETS += riscv TARGETS += rlimits @@ -213,14 +214,14 @@ export KHDR_INCLUDES .DEFAULT_GOAL := all all: - @ret=0; \ + @ret=1; \ for TARGET in $(TARGETS) $(INSTALL_DEP_TARGETS); do \ BUILD_TARGET=$$BUILD/$$TARGET; \ mkdir $$BUILD_TARGET -p; \ $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET \ O=$(abs_objtree) \ $(if $(FORCE_TARGETS),|| exit); \ - [ $$? -eq 0 ] || ret=1; \ + ret=$$((ret * $$?)); \ done; exit $$ret; run_tests: all @@ -278,7 +279,7 @@ ifdef INSTALL_PATH install -m 744 kselftest/ksft.py $(INSTALL_PATH)/kselftest/ install -m 744 run_kselftest.sh $(INSTALL_PATH)/ rm -f $(TEST_LIST) - @ret=0; \ + @ret=1; \ for TARGET in $(TARGETS) $(INSTALL_DEP_TARGETS); do \ BUILD_TARGET=$$BUILD/$$TARGET; \ $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install \ @@ -287,7 +288,7 @@ ifdef INSTALL_PATH OBJ_PATH=$(INSTALL_PATH) \ O=$(abs_objtree) \ $(if $(FORCE_TARGETS),|| exit); \ - [ $$? -eq 0 ] || ret=1; \ + ret=$$((ret * $$?)); \ done; exit $$ret; diff --git a/tools/testing/selftests/arm64/gcs/gcs-util.h b/tools/testing/selftests/arm64/gcs/gcs-util.h index c99a6b39ac14..7a81bb07ed4b 100644 --- a/tools/testing/selftests/arm64/gcs/gcs-util.h +++ b/tools/testing/selftests/arm64/gcs/gcs-util.h @@ -18,12 +18,6 @@ #ifndef NT_ARM_GCS #define NT_ARM_GCS 0x410 - -struct user_gcs { - __u64 features_enabled; - __u64 features_locked; - __u64 gcspr_el0; -}; #endif /* Shadow Stack/Guarded Control Stack interface */ diff --git a/tools/testing/selftests/arm64/gcs/libc-gcs.c b/tools/testing/selftests/arm64/gcs/libc-gcs.c index 17b2fabfec38..72e82bfbecc9 100644 --- a/tools/testing/selftests/arm64/gcs/libc-gcs.c +++ b/tools/testing/selftests/arm64/gcs/libc-gcs.c @@ -16,6 +16,7 @@ #include #include +#include #include diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 78e60040811e..6ef6872adbc3 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -751,6 +751,7 @@ TRUNNER_EXTRA_SOURCES := test_progs.c \ btf_helpers.c \ cap_helpers.c \ unpriv_helpers.c \ + sysctl_helpers.c \ netlink_helpers.c \ jit_disasm_helpers.c \ io_helpers.c \ diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c b/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c index f829b6f09bc9..fe30181e6336 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c @@ -112,6 +112,10 @@ static void test_cubic(void) ASSERT_EQ(cubic_skel->bss->bpf_cubic_acked_called, 1, "pkts_acked called"); + ASSERT_TRUE(cubic_skel->bss->nodelay_init_reject, "init reject nodelay option"); + ASSERT_TRUE(cubic_skel->bss->nodelay_cwnd_event_tx_start_reject, + "cwnd_event_tx_start reject nodelay option"); + bpf_link__destroy(link); bpf_cubic__destroy(cubic_skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/migrate_reuseport.c b/tools/testing/selftests/bpf/prog_tests/migrate_reuseport.c index 653b0a20fab9..c62907732c19 100644 --- a/tools/testing/selftests/bpf/prog_tests/migrate_reuseport.c +++ b/tools/testing/selftests/bpf/prog_tests/migrate_reuseport.c @@ -7,24 +7,29 @@ * 3. call listen() for 1 server socket. (migration target) * 4. update a map to migrate all child sockets * to the last server socket (migrate_map[cookie] = 4) - * 5. call shutdown() for first 4 server sockets + * 5. for TCP_ESTABLISHED and TCP_SYN_RECV cases, verify via epoll + * that the last server socket is not ready before migration. + * 6. call shutdown() for first 4 server sockets * and migrate the requests in the accept queue * to the last server socket. - * 6. call listen() for the second server socket. - * 7. call shutdown() for the last server + * 7. for TCP_ESTABLISHED and TCP_SYN_RECV cases, verify via epoll + * that the last server socket is ready after migration. + * 8. call listen() for the second server socket. + * 9. call shutdown() for the last server * and migrate the requests in the accept queue * to the second server socket. - * 8. call listen() for the last server. - * 9. call shutdown() for the second server + * 10. call listen() for the last server. + * 11. call shutdown() for the second server * and migrate the requests in the accept queue * to the last server socket. - * 10. call accept() for the last server socket. + * 12. call accept() for the last server socket. * * Author: Kuniyuki Iwashima */ #include #include +#include #include "test_progs.h" #include "test_migrate_reuseport.skel.h" @@ -350,21 +355,51 @@ static int update_maps(struct migrate_reuseport_test_case *test_case, static int migrate_dance(struct migrate_reuseport_test_case *test_case) { + struct epoll_event ev = { + .events = EPOLLIN, + }; + int epoll = -1, nfds; int i, err; + if (test_case->state != BPF_TCP_NEW_SYN_RECV) { + epoll = epoll_create1(0); + if (!ASSERT_NEQ(epoll, -1, "epoll_create1")) + return -1; + + ev.data.fd = test_case->servers[MIGRATED_TO]; + if (!ASSERT_OK(epoll_ctl(epoll, EPOLL_CTL_ADD, + test_case->servers[MIGRATED_TO], &ev), + "epoll_ctl")) + goto close_epoll; + + nfds = epoll_wait(epoll, &ev, 1, 0); + if (!ASSERT_EQ(nfds, 0, "epoll_wait 1")) + goto close_epoll; + } + /* Migrate TCP_ESTABLISHED and TCP_SYN_RECV requests * to the last listener based on eBPF. */ for (i = 0; i < MIGRATED_TO; i++) { err = shutdown(test_case->servers[i], SHUT_RDWR); if (!ASSERT_OK(err, "shutdown")) - return -1; + goto close_epoll; } /* No dance for TCP_NEW_SYN_RECV to migrate based on eBPF */ if (test_case->state == BPF_TCP_NEW_SYN_RECV) return 0; + nfds = epoll_wait(epoll, &ev, 1, 0); + if (!ASSERT_EQ(nfds, 1, "epoll_wait 2")) { +close_epoll: + if (epoll >= 0) + close(epoll); + return -1; + } + + close(epoll); + /* Note that we use the second listener instead of the * first one here. * diff --git a/tools/testing/selftests/bpf/prog_tests/snprintf.c b/tools/testing/selftests/bpf/prog_tests/snprintf.c index 594441acb707..4e4a82d54f79 100644 --- a/tools/testing/selftests/bpf/prog_tests/snprintf.c +++ b/tools/testing/selftests/bpf/prog_tests/snprintf.c @@ -114,7 +114,8 @@ static void test_snprintf_negative(void) ASSERT_ERR(load_single_snprintf("%--------"), "invalid specifier 5"); ASSERT_ERR(load_single_snprintf("%lc"), "invalid specifier 6"); ASSERT_ERR(load_single_snprintf("%llc"), "invalid specifier 7"); - ASSERT_ERR(load_single_snprintf("\x80"), "non ascii character"); + ASSERT_OK(load_single_snprintf("\x80"), "non ascii plain text"); + ASSERT_ERR(load_single_snprintf("%\x80"), "non ascii in specifier"); ASSERT_ERR(load_single_snprintf("\x1"), "non printable character"); ASSERT_ERR(load_single_snprintf("%p%"), "invalid specifier 8"); ASSERT_ERR(load_single_snprintf("%s%"), "invalid specifier 9"); diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c index 53637431ec5d..3a41c517b918 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c +++ b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c @@ -190,7 +190,7 @@ static int getsetsockopt(void) fd = socket(AF_NETLINK, SOCK_RAW, 0); if (fd < 0) { log_err("Failed to create AF_NETLINK socket"); - return -1; + goto err; } buf.u32 = 1; @@ -211,6 +211,21 @@ static int getsetsockopt(void) } ASSERT_EQ(optlen, 8, "Unexpected NETLINK_LIST_MEMBERSHIPS value"); + /* Trick bpf_tcp_sock() with IPPROTO_TCP */ + close(fd); + fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); + if (!ASSERT_OK_FD(fd, "socket")) + goto err; + + /* The BPF prog intercepts this before the kernel sees it, any + * optlen works. Go with 4 bytes for simplicity. + */ + buf.u32 = 1; + optlen = sizeof(buf.u32); + err = setsockopt(fd, SOL_TCP, TCP_SAVED_SYN, &buf, optlen); + if (!ASSERT_ERR(err, "setsockopt(TCP_SAVED_SYN)")) + goto err; + free(big_buf); close(fd); return 0; diff --git a/tools/testing/selftests/bpf/prog_tests/task_local_data.h b/tools/testing/selftests/bpf/prog_tests/task_local_data.h index 1e5c67c78ffb..8ae4fb2027f7 100644 --- a/tools/testing/selftests/bpf/prog_tests/task_local_data.h +++ b/tools/testing/selftests/bpf/prog_tests/task_local_data.h @@ -99,14 +99,20 @@ struct tld_meta_u { struct tld_metadata metadata[]; }; +/* + * The unused field ensures map_val.start > 0. On the BPF side, __tld_fetch_key() + * calculates off by summing map_val.start and tld_key_t.off and treats off == 0 + * as key not cached. + */ struct tld_data_u { - __u64 start; /* offset of tld_data_u->data in a page */ + __u64 unused; char data[] __attribute__((aligned(8))); }; struct tld_map_value { void *data; struct tld_meta_u *meta; + __u16 start; /* offset of tld_data_u->data in a page */ }; struct tld_meta_u * _Atomic tld_meta_p __attribute__((weak)); @@ -182,7 +188,7 @@ static int __tld_init_data_p(int map_fd) * is a page in BTF. */ map_val.data = (void *)(TLD_PAGE_MASK & (intptr_t)data); - data->start = (~TLD_PAGE_MASK & (intptr_t)data) + sizeof(struct tld_data_u); + map_val.start = (~TLD_PAGE_MASK & (intptr_t)data) + sizeof(struct tld_data_u); map_val.meta = tld_meta_p; err = bpf_map_update_elem(map_fd, &tid_fd, &map_val, 0); @@ -241,7 +247,8 @@ retry: * TLD_DYN_DATA_SIZE is allocated for tld_create_key() */ if (dyn_data) { - if (off + TLD_ROUND_UP(size, 8) > tld_meta_p->size) + if (off + TLD_ROUND_UP(size, 8) > tld_meta_p->size || + tld_meta_p->size > TLD_PAGE_SIZE - sizeof(struct tld_data_u)) return (tld_key_t){-E2BIG}; } else { if (off + TLD_ROUND_UP(size, 8) > TLD_PAGE_SIZE - sizeof(struct tld_data_u)) diff --git a/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c b/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c index 56685fc03c7e..80e6315da2a5 100644 --- a/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c +++ b/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c @@ -507,6 +507,10 @@ static void misc(void) ASSERT_EQ(misc_skel->bss->nr_hwtstamp, 0, "nr_hwtstamp"); + ASSERT_TRUE(misc_skel->bss->nodelay_est_ok, "nodelay_est_ok"); + ASSERT_TRUE(misc_skel->bss->nodelay_hdr_len_reject, "nodelay_hdr_len_reject"); + ASSERT_TRUE(misc_skel->bss->nodelay_write_hdr_reject, "nodelay_write_hdr_reject"); + check_linum: ASSERT_FALSE(check_error_linum(&sk_fds), "check_error_linum"); sk_fds_close(&sk_fds); diff --git a/tools/testing/selftests/bpf/prog_tests/test_task_local_data.c b/tools/testing/selftests/bpf/prog_tests/test_task_local_data.c index e219ff506b56..6a5806b36113 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_task_local_data.c +++ b/tools/testing/selftests/bpf/prog_tests/test_task_local_data.c @@ -3,8 +3,14 @@ #include #include +/* + * Only a page is pinned to kernel, so the maximum amount of dynamic data + * allowed is page_size - sizeof(struct tld_data_u) - static TLD fields. + */ +#define TLD_DYN_DATA_SIZE_MAX (getpagesize() - sizeof(struct tld_data_u) - 8) + #define TLD_FREE_DATA_ON_THREAD_EXIT -#define TLD_DYN_DATA_SIZE (getpagesize() - 8) +#define TLD_DYN_DATA_SIZE TLD_DYN_DATA_SIZE_MAX #include "task_local_data.h" struct test_tld_struct { @@ -24,12 +30,12 @@ TLD_DEFINE_KEY(value0_key, "value0", sizeof(int)); * sequentially. Users of task local data library should not touch * library internal. */ -static void reset_tld(void) +static void reset_tld(__u16 dyn_data_size) { if (tld_meta_p) { /* Remove TLDs created by tld_create_key() */ tld_meta_p->cnt = 1; - tld_meta_p->size = TLD_DYN_DATA_SIZE; + tld_meta_p->size = dyn_data_size + 8; memset(&tld_meta_p->metadata[1], 0, (TLD_MAX_DATA_CNT - 1) * sizeof(struct tld_metadata)); } @@ -127,7 +133,7 @@ static void test_task_local_data_basic(void) tld_key_t key; int i, err; - reset_tld(); + reset_tld(TLD_DYN_DATA_SIZE_MAX); ASSERT_OK(pthread_mutex_init(&global_mutex, NULL), "pthread_mutex_init"); @@ -147,11 +153,13 @@ static void test_task_local_data_basic(void) /* * Shouldn't be able to store data exceed a page. Create a TLD just big - * enough to exceed a page. TLDs already created are int value0, int - * value1, and struct test_tld_struct value2. + * enough to exceed a page. Data already contains struct tld_data_u, + * value0 and value1 of int type, and value 2 of struct test_tld_struct. */ - key = tld_create_key("value_not_exist", - TLD_PAGE_SIZE - 2 * sizeof(int) - sizeof(struct test_tld_struct) + 1); + key = tld_create_key("value_not_exist", TLD_PAGE_SIZE + 1 - + sizeof(struct tld_data_u) - + TLD_ROUND_UP(sizeof(int), 8) * 2 - + TLD_ROUND_UP(sizeof(struct test_tld_struct), 8)); ASSERT_EQ(tld_key_err_or_zero(key), -E2BIG, "tld_create_key"); key = tld_create_key("value2", sizeof(struct test_tld_struct)); @@ -239,7 +247,7 @@ static void test_task_local_data_race(void) tld_keys[0] = value0_key; for (j = 0; j < 100; j++) { - reset_tld(); + reset_tld(TLD_DYN_DATA_SIZE_MAX); for (i = 0; i < TEST_RACE_THREAD_NUM; i++) { /* @@ -288,10 +296,80 @@ out: test_task_local_data__destroy(skel); } +static void test_task_local_data_dyn_size(__u16 dyn_data_size) +{ + LIBBPF_OPTS(bpf_test_run_opts, opts); + struct test_task_local_data *skel; + int max_keys, i, err, fd, *data; + char name[TLD_NAME_LEN]; + tld_key_t key; + + reset_tld(dyn_data_size); + + skel = test_task_local_data__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) + return; + + tld_keys = calloc(TLD_MAX_DATA_CNT, sizeof(tld_key_t)); + if (!ASSERT_OK_PTR(tld_keys, "calloc tld_keys")) + goto out; + + fd = bpf_map__fd(skel->maps.tld_data_map); + + /* Create as many int-sized TLDs as the dynamic data size allows */ + max_keys = dyn_data_size / TLD_ROUND_UP(sizeof(int), 8); + for (i = 0; i < max_keys; i++) { + snprintf(name, TLD_NAME_LEN, "value_%d", i); + tld_keys[i] = tld_create_key(name, sizeof(int)); + if (!ASSERT_FALSE(tld_key_is_err(tld_keys[i]), "tld_create_key")) + goto out; + + data = tld_get_data(fd, tld_keys[i]); + if (!ASSERT_OK_PTR(data, "tld_get_data")) + goto out; + *data = i; + } + + /* The next key should fail with E2BIG */ + key = tld_create_key("overflow", sizeof(int)); + ASSERT_EQ(tld_key_err_or_zero(key), -E2BIG, "tld_create_key overflow"); + + /* Verify data for value_i do not overlap */ + for (i = 0; i < max_keys; i++) { + data = tld_get_data(fd, tld_keys[i]); + if (!ASSERT_OK_PTR(data, "tld_get_data")) + goto out; + + ASSERT_EQ(*data, i, "tld_get_data value_i"); + } + + /* Verify BPF side can still read the static key */ + data = tld_get_data(fd, value0_key); + if (!ASSERT_OK_PTR(data, "tld_get_data value0")) + goto out; + *data = 0xdeadbeef; + + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.task_main), &opts); + ASSERT_OK(err, "run task_main"); + ASSERT_EQ(skel->bss->test_value0, 0xdeadbeef, "tld_get_data value0"); + +out: + if (tld_keys) { + free(tld_keys); + tld_keys = NULL; + } + tld_free(); + test_task_local_data__destroy(skel); +} + void test_task_local_data(void) { if (test__start_subtest("task_local_data_basic")) test_task_local_data_basic(); if (test__start_subtest("task_local_data_race")) test_task_local_data_race(); + if (test__start_subtest("task_local_data_dyn_size_small")) + test_task_local_data_dyn_size(64); + if (test__start_subtest("task_local_data_dyn_size_zero")) + test_task_local_data_dyn_size(0); } diff --git a/tools/testing/selftests/bpf/prog_tests/token.c b/tools/testing/selftests/bpf/prog_tests/token.c index b81dde283052..f2f5d36ae00a 100644 --- a/tools/testing/selftests/bpf/prog_tests/token.c +++ b/tools/testing/selftests/bpf/prog_tests/token.c @@ -1,9 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ #define _GNU_SOURCE -#include #include -#include "cap_helpers.h" #include #include #include @@ -15,9 +13,17 @@ #include #include #include + +#include "bpf_util.h" +#include "cap_helpers.h" +#include "sysctl_helpers.h" +#include "test_progs.h" +#include "trace_helpers.h" + #include "priv_map.skel.h" #include "priv_prog.skel.h" #include "dummy_st_ops_success.skel.h" +#include "token_kallsyms.skel.h" #include "token_lsm.skel.h" #include "priv_freplace_prog.skel.h" @@ -1045,6 +1051,58 @@ err_out: return -EINVAL; } +static bool kallsyms_has_bpf_func(struct ksyms *ksyms, const char *func_name) +{ + char name[256]; + int i; + + for (i = 0; i < ksyms->sym_cnt; i++) { + if (sscanf(ksyms->syms[i].name, "bpf_prog_%*[^_]_%255s", name) == 1 && + strcmp(name, func_name) == 0) + return true; + } + return false; +} + +static int userns_obj_priv_prog_kallsyms(int mnt_fd, struct token_lsm *lsm_skel) +{ + const char *func_names[] = { "xdp_main", "token_ksym_subprog" }; + LIBBPF_OPTS(bpf_object_open_opts, opts); + struct token_kallsyms *skel; + struct ksyms *ksyms = NULL; + char buf[256]; + int i, err; + + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", mnt_fd); + opts.bpf_token_path = buf; + skel = token_kallsyms__open_opts(&opts); + if (!ASSERT_OK_PTR(skel, "token_kallsyms__open_opts")) + return -EINVAL; + + err = token_kallsyms__load(skel); + if (!ASSERT_OK(err, "token_kallsyms__load")) + goto cleanup; + + ksyms = load_kallsyms_local(); + if (!ASSERT_OK_PTR(ksyms, "load_kallsyms_local")) { + err = -EINVAL; + goto cleanup; + } + + for (i = 0; i < ARRAY_SIZE(func_names); i++) { + if (!ASSERT_TRUE(kallsyms_has_bpf_func(ksyms, func_names[i]), + func_names[i])) { + err = -EINVAL; + break; + } + } + +cleanup: + free_kallsyms_local(ksyms); + token_kallsyms__destroy(skel); + return err; +} + #define bit(n) (1ULL << (n)) static int userns_bpf_token_info(int mnt_fd, struct token_lsm *lsm_skel) @@ -1082,7 +1140,7 @@ cleanup: return err; } -void test_token(void) +void serial_test_token(void) { if (test__start_subtest("map_token")) { struct bpffs_opts opts = { @@ -1194,4 +1252,26 @@ void test_token(void) subtest_userns(&opts, userns_bpf_token_info); } + if (test__start_subtest("obj_priv_prog_kallsyms")) { + char perf_paranoid_orig[32] = {}; + char kptr_restrict_orig[32] = {}; + struct bpffs_opts opts = { + .cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD), + .progs = bit(BPF_PROG_TYPE_XDP), + .attachs = ~0ULL, + }; + + if (sysctl_set_or_fail("/proc/sys/kernel/perf_event_paranoid", perf_paranoid_orig, "0")) + goto cleanup; + if (sysctl_set_or_fail("/proc/sys/kernel/kptr_restrict", kptr_restrict_orig, "0")) + goto cleanup; + + subtest_userns(&opts, userns_obj_priv_prog_kallsyms); + +cleanup: + if (perf_paranoid_orig[0]) + sysctl_set_or_fail("/proc/sys/kernel/perf_event_paranoid", NULL, perf_paranoid_orig); + if (kptr_restrict_orig[0]) + sysctl_set_or_fail("/proc/sys/kernel/kptr_restrict", NULL, kptr_restrict_orig); + } } diff --git a/tools/testing/selftests/bpf/prog_tests/trace_printk.c b/tools/testing/selftests/bpf/prog_tests/trace_printk.c index e56e88596d64..a5a8104c1ddd 100644 --- a/tools/testing/selftests/bpf/prog_tests/trace_printk.c +++ b/tools/testing/selftests/bpf/prog_tests/trace_printk.c @@ -6,18 +6,21 @@ #include "trace_printk.lskel.h" #define SEARCHMSG "testing,testing" +#define SEARCHMSG_UTF8 "中文,测试" static void trace_pipe_cb(const char *str, void *data) { if (strstr(str, SEARCHMSG) != NULL) - (*(int *)data)++; + ((int *)data)[0]++; + if (strstr(str, SEARCHMSG_UTF8)) + ((int *)data)[1]++; } void serial_test_trace_printk(void) { struct trace_printk_lskel__bss *bss; struct trace_printk_lskel *skel; - int err = 0, found = 0; + int err = 0, found[2] = {}; skel = trace_printk_lskel__open(); if (!ASSERT_OK_PTR(skel, "trace_printk__open")) @@ -46,11 +49,24 @@ void serial_test_trace_printk(void) if (!ASSERT_GT(bss->trace_printk_ret, 0, "bss->trace_printk_ret")) goto cleanup; - /* verify our search string is in the trace buffer */ - ASSERT_OK(read_trace_pipe_iter(trace_pipe_cb, &found, 1000), - "read_trace_pipe_iter"); + if (!ASSERT_GT(bss->trace_printk_utf8_ran, 0, "bss->trace_printk_utf8_ran")) + goto cleanup; - if (!ASSERT_EQ(found, bss->trace_printk_ran, "found")) + if (!ASSERT_GT(bss->trace_printk_utf8_ret, 0, "bss->trace_printk_utf8_ret")) + goto cleanup; + + if (!ASSERT_LT(bss->trace_printk_invalid_spec_ret, 0, + "bss->trace_printk_invalid_spec_ret")) + goto cleanup; + + /* verify our search strings are in the trace buffer */ + ASSERT_OK(read_trace_pipe_iter(trace_pipe_cb, found, 1000), + "read_trace_pipe_iter"); + + if (!ASSERT_EQ(found[0], bss->trace_printk_ran, "found")) + goto cleanup; + + if (!ASSERT_EQ(found[1], bss->trace_printk_utf8_ran, "found_utf8")) goto cleanup; cleanup: diff --git a/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c b/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c index 472f4f9fa95f..64404602b9ab 100644 --- a/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c +++ b/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c @@ -8,6 +8,7 @@ #include "cap_helpers.h" #include "bpf_util.h" +#include "sysctl_helpers.h" /* Using CAP_LAST_CAP is risky here, since it can get pulled in from * an old /usr/include/linux/capability.h and be < CAP_BPF; as a result @@ -36,26 +37,6 @@ static void process_perfbuf(void *ctx, int cpu, void *data, __u32 len) got_perfbuf_val = *(__u32 *)data; } -static int sysctl_set(const char *sysctl_path, char *old_val, const char *new_val) -{ - int ret = 0; - FILE *fp; - - fp = fopen(sysctl_path, "r+"); - if (!fp) - return -errno; - if (old_val && fscanf(fp, "%s", old_val) <= 0) { - ret = -ENOENT; - } else if (!old_val || strcmp(old_val, new_val) != 0) { - fseek(fp, 0, SEEK_SET); - if (fprintf(fp, "%s", new_val) < 0) - ret = -errno; - } - fclose(fp); - - return ret; -} - static void test_unpriv_bpf_disabled_positive(struct test_unpriv_bpf_disabled *skel, __u32 prog_id, int prog_fd, int perf_fd, char **map_paths, int *map_fds) diff --git a/tools/testing/selftests/bpf/progs/bpf_cubic.c b/tools/testing/selftests/bpf/progs/bpf_cubic.c index ce18a4db813f..ebd5a1e69f56 100644 --- a/tools/testing/selftests/bpf/progs/bpf_cubic.c +++ b/tools/testing/selftests/bpf/progs/bpf_cubic.c @@ -16,6 +16,7 @@ #include "bpf_tracing_net.h" #include +#include char _license[] SEC("license") = "GPL"; @@ -170,10 +171,18 @@ static void bictcp_hystart_reset(struct sock *sk) ca->sample_cnt = 0; } +bool nodelay_init_reject = false; +bool nodelay_cwnd_event_tx_start_reject = false; + SEC("struct_ops") void BPF_PROG(bpf_cubic_init, struct sock *sk) { struct bpf_bictcp *ca = inet_csk_ca(sk); + int true_val = 1, ret; + + ret = bpf_setsockopt(sk, SOL_TCP, TCP_NODELAY, &true_val, sizeof(true_val)); + if (ret == -EOPNOTSUPP) + nodelay_init_reject = true; bictcp_reset(ca); @@ -189,8 +198,13 @@ void BPF_PROG(bpf_cubic_cwnd_event_tx_start, struct sock *sk) { struct bpf_bictcp *ca = inet_csk_ca(sk); __u32 now = tcp_jiffies32; + int true_val = 1, ret; __s32 delta; + ret = bpf_setsockopt(sk, SOL_TCP, TCP_NODELAY, &true_val, sizeof(true_val)); + if (ret == -EOPNOTSUPP) + nodelay_cwnd_event_tx_start_reject = true; + delta = now - tcp_sk(sk)->lsndtime; /* We were application limited (idle) for a while. diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_unix.c b/tools/testing/selftests/bpf/progs/bpf_iter_unix.c index fea275df9e22..a2652c8c3616 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter_unix.c +++ b/tools/testing/selftests/bpf/progs/bpf_iter_unix.c @@ -7,6 +7,13 @@ char _license[] SEC("license") = "GPL"; +SEC(".maps") struct { + __uint(type, BPF_MAP_TYPE_SOCKMAP); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} sockmap; + static long sock_i_ino(const struct sock *sk) { const struct socket *sk_socket = sk->sk_socket; @@ -76,5 +83,8 @@ int dump_unix(struct bpf_iter__unix *ctx) BPF_SEQ_PRINTF(seq, "\n"); + /* Test for deadlock. */ + bpf_map_update_elem(&sockmap, &(int){0}, sk, 0); + return 0; } diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index dcd78a3a9052..a0d7b15a24b1 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -263,8 +263,8 @@ #if __clang_major__ >= 18 && defined(ENABLE_ATOMICS_TESTS) && \ (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \ - (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64)) || \ - (defined(__TARGET_ARCH_powerpc)) + (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64) || \ + defined(__TARGET_ARCH_powerpc) || defined(__TARGET_ARCH_loongarch)) #define CAN_USE_LOAD_ACQ_STORE_REL #endif diff --git a/tools/testing/selftests/bpf/progs/map_kptr_fail.c b/tools/testing/selftests/bpf/progs/map_kptr_fail.c index 6443b320c732..ee053b24e6ca 100644 --- a/tools/testing/selftests/bpf/progs/map_kptr_fail.c +++ b/tools/testing/selftests/bpf/progs/map_kptr_fail.c @@ -385,4 +385,19 @@ int kptr_xchg_possibly_null(struct __sk_buff *ctx) return 0; } +SEC("?tc") +__failure __msg("invalid kptr access, R") +int reject_scalar_store_to_kptr(struct __sk_buff *ctx) +{ + struct map_value *v; + int key = 0; + + v = bpf_map_lookup_elem(&array_map, &key); + if (!v) + return 0; + + *(volatile u64 *)&v->unref_ptr = 0xBADC0DE; + return 0; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/sockopt_sk.c b/tools/testing/selftests/bpf/progs/sockopt_sk.c index cb990a7d3d45..5e0b27e7855c 100644 --- a/tools/testing/selftests/bpf/progs/sockopt_sk.c +++ b/tools/testing/selftests/bpf/progs/sockopt_sk.c @@ -149,6 +149,20 @@ int _setsockopt(struct bpf_sockopt *ctx) if (sk && sk->family == AF_NETLINK) goto out; + if (sk && sk->family == AF_INET && sk->type == SOCK_RAW) { + struct bpf_tcp_sock *tp = bpf_tcp_sock(sk); + + if (tp) { + char saved_syn[60]; + + bpf_getsockopt(sk, SOL_TCP, TCP_SAVED_SYN, + &saved_syn, sizeof(saved_syn)); + goto consumed; + } + + goto out; + } + /* Make sure bpf_get_netns_cookie is callable. */ if (bpf_get_netns_cookie(NULL) == 0) @@ -224,6 +238,8 @@ int _setsockopt(struct bpf_sockopt *ctx) return 0; /* couldn't get sk storage */ storage->val = optval[0]; + +consumed: ctx->optlen = -1; /* BPF has consumed this option, don't call kernel * setsockopt handler. */ diff --git a/tools/testing/selftests/bpf/progs/task_local_data.bpf.h b/tools/testing/selftests/bpf/progs/task_local_data.bpf.h index 1f396711f487..0df8a12fd61e 100644 --- a/tools/testing/selftests/bpf/progs/task_local_data.bpf.h +++ b/tools/testing/selftests/bpf/progs/task_local_data.bpf.h @@ -86,13 +86,14 @@ struct tld_meta_u { }; struct tld_data_u { - __u64 start; /* offset of tld_data_u->data in a page */ + __u64 unused; char data[__PAGE_SIZE - sizeof(__u64)] __attribute__((aligned(8))); }; struct tld_map_value { struct tld_data_u __uptr *data; struct tld_meta_u __uptr *meta; + __u16 start; /* offset of tld_data_u->data in a page */ }; typedef struct tld_uptr_dummy { @@ -176,7 +177,7 @@ static int __tld_fetch_key(struct tld_object *tld_obj, const char *name, int i_s if (!tld_obj->data_map || !tld_obj->data_map->data || !tld_obj->data_map->meta) return 0; - start = tld_obj->data_map->data->start; + start = tld_obj->data_map->start; cnt = tld_obj->data_map->meta->cnt; metadata = tld_obj->data_map->meta->metadata; diff --git a/tools/testing/selftests/bpf/progs/test_misc_tcp_hdr_options.c b/tools/testing/selftests/bpf/progs/test_misc_tcp_hdr_options.c index d487153a839d..ed5a0011b863 100644 --- a/tools/testing/selftests/bpf/progs/test_misc_tcp_hdr_options.c +++ b/tools/testing/selftests/bpf/progs/test_misc_tcp_hdr_options.c @@ -29,6 +29,10 @@ unsigned int nr_syn = 0; unsigned int nr_fin = 0; unsigned int nr_hwtstamp = 0; +bool nodelay_est_ok = false; +bool nodelay_hdr_len_reject = false; +bool nodelay_write_hdr_reject = false; + /* Check the header received from the active side */ static int __check_active_hdr_in(struct bpf_sock_ops *skops, bool check_syn) { @@ -300,7 +304,7 @@ static int handle_passive_estab(struct bpf_sock_ops *skops) SEC("sockops") int misc_estab(struct bpf_sock_ops *skops) { - int true_val = 1; + int true_val = 1, false_val = 0, ret; switch (skops->op) { case BPF_SOCK_OPS_TCP_LISTEN_CB: @@ -316,10 +320,19 @@ int misc_estab(struct bpf_sock_ops *skops) case BPF_SOCK_OPS_PARSE_HDR_OPT_CB: return handle_parse_hdr(skops); case BPF_SOCK_OPS_HDR_OPT_LEN_CB: + ret = bpf_setsockopt(skops, SOL_TCP, TCP_NODELAY, &true_val, sizeof(true_val)); + if (ret == -EOPNOTSUPP) + nodelay_hdr_len_reject = true; return handle_hdr_opt_len(skops); case BPF_SOCK_OPS_WRITE_HDR_OPT_CB: + ret = bpf_setsockopt(skops, SOL_TCP, TCP_NODELAY, &true_val, sizeof(true_val)); + if (ret == -EOPNOTSUPP) + nodelay_write_hdr_reject = true; return handle_write_hdr_opt(skops); case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: + ret = bpf_setsockopt(skops, SOL_TCP, TCP_NODELAY, &false_val, sizeof(false_val)); + if (!ret) + nodelay_est_ok = true; return handle_passive_estab(skops); } diff --git a/tools/testing/selftests/bpf/progs/timer_start_deadlock.c b/tools/testing/selftests/bpf/progs/timer_start_deadlock.c index 019518ee18cd..afabd15bdac4 100644 --- a/tools/testing/selftests/bpf/progs/timer_start_deadlock.c +++ b/tools/testing/selftests/bpf/progs/timer_start_deadlock.c @@ -27,13 +27,13 @@ static int timer_cb(void *map, int *key, struct elem *value) return 0; } -SEC("tp_btf/hrtimer_cancel") -int BPF_PROG(tp_hrtimer_cancel, struct hrtimer *hrtimer) +SEC("tp_btf/hrtimer_start") +int BPF_PROG(tp_hrtimer_start, struct hrtimer *hrtimer, enum hrtimer_mode mode, bool was_armed) { struct bpf_timer *timer; int key = 0; - if (!in_timer_start) + if (!in_timer_start || !was_armed) return 0; tp_called = 1; @@ -60,7 +60,7 @@ int start_timer(void *ctx) /* * call hrtimer_start() twice, so that 2nd call does - * remove_hrtimer() and trace_hrtimer_cancel() tracepoint. + * trace_hrtimer_start(was_armed=1) tracepoint. */ in_timer_start = 1; bpf_timer_start(timer, 1000000000, 0); diff --git a/tools/testing/selftests/bpf/progs/token_kallsyms.c b/tools/testing/selftests/bpf/progs/token_kallsyms.c new file mode 100644 index 000000000000..c9f9344f3eb2 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/token_kallsyms.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include + +char _license[] SEC("license") = "GPL"; + +__weak +int token_ksym_subprog(void) +{ + return 0; +} + +SEC("xdp") +int xdp_main(struct xdp_md *xdp) +{ + return token_ksym_subprog(); +} diff --git a/tools/testing/selftests/bpf/progs/trace_printk.c b/tools/testing/selftests/bpf/progs/trace_printk.c index 6695478c2b25..f4c538ec3ebd 100644 --- a/tools/testing/selftests/bpf/progs/trace_printk.c +++ b/tools/testing/selftests/bpf/progs/trace_printk.c @@ -10,13 +10,23 @@ char _license[] SEC("license") = "GPL"; int trace_printk_ret = 0; int trace_printk_ran = 0; +int trace_printk_invalid_spec_ret = 0; +int trace_printk_utf8_ret = 0; +int trace_printk_utf8_ran = 0; const char fmt[] = "Testing,testing %d\n"; +static const char utf8_fmt[] = "中文,测试 %d\n"; +/* Non-ASCII bytes after '%' must still be rejected. */ +static const char invalid_spec_fmt[] = "%\x80\n"; SEC("fentry/" SYS_PREFIX "sys_nanosleep") int sys_enter(void *ctx) { trace_printk_ret = bpf_trace_printk(fmt, sizeof(fmt), ++trace_printk_ran); + trace_printk_utf8_ret = bpf_trace_printk(utf8_fmt, sizeof(utf8_fmt), + ++trace_printk_utf8_ran); + trace_printk_invalid_spec_ret = bpf_trace_printk(invalid_spec_fmt, + sizeof(invalid_spec_fmt)); return 0; } diff --git a/tools/testing/selftests/bpf/progs/verifier_live_stack.c b/tools/testing/selftests/bpf/progs/verifier_live_stack.c index b7a9fa10e84d..401152b2b64f 100644 --- a/tools/testing/selftests/bpf/progs/verifier_live_stack.c +++ b/tools/testing/selftests/bpf/progs/verifier_live_stack.c @@ -2647,3 +2647,196 @@ __naked void spill_join_with_imprecise_off(void) "exit;" ::: __clobber_all); } + +/* + * Same as spill_join_with_multi_off but the write is BPF_ST (store + * immediate) instead of BPF_STX. BPF_ST goes through + * clear_stack_for_all_offs() rather than spill_to_stack(), and that + * path also needs to join instead of overwriting. + * + * fp-8 = &fp-24 + * fp-16 = &fp-32 + * r1 = fp-8 or fp-16 (two offsets from branch) + * *(u64 *)(r1 + 0) = 0 -- BPF_ST with immediate + * r0 = *(u64 *)(r10 - 16) -- fill from fp-16 + * r0 = *(u64 *)(r0 + 0) -- deref: should produce use + */ +SEC("socket") +__log_level(2) +__failure +__msg("15: (7a) *(u64 *)(r1 +0) = 0 fp-8: fp0-24 -> fp0-24|fp0+0 fp-16: fp0-32 -> fp0-32|fp0+0") +__msg("17: (79) r0 = *(u64 *)(r0 +0) ; use: fp0-32") +__naked void st_imm_join_with_multi_off(void) +{ + asm volatile ( + "*(u64 *)(r10 - 24) = 0;" + "*(u64 *)(r10 - 32) = 0;" + "r1 = r10;" + "r1 += -24;" + "*(u64 *)(r10 - 8) = r1;" + "r1 = r10;" + "r1 += -32;" + "*(u64 *)(r10 - 16) = r1;" + /* create r1 with two candidate offsets: fp-8 or fp-16 */ + "call %[bpf_get_prandom_u32];" + "if r0 == 0 goto 1f;" + "r1 = r10;" + "r1 += -8;" + "goto 2f;" +"1:" + "r1 = r10;" + "r1 += -16;" +"2:" + /* BPF_ST: store immediate through multi-offset r1 */ + "*(u64 *)(r1 + 0) = 0;" + /* read back fp-16 and deref */ + "r0 = *(u64 *)(r10 - 16);" + "r0 = *(u64 *)(r0 + 0);" + "r0 = 0;" + "exit;" + :: __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* + * Check that BPF_ST with a known offset fully overwrites stack slot + * from the arg tracking point of view. + */ +SEC("socket") +__log_level(2) +__success +__msg("5: (7a) *(u64 *)(r1 +0) = 0 fp-8: fp0-16 -> _{{$}}") +__naked void st_imm_join_with_single_off(void) +{ + asm volatile ( + "r2 = r10;" + "r2 += -16;" + "*(u64 *)(r10 - 8) = r2;" + "r1 = r10;" + "r1 += -8;" + "*(u64 *)(r1 + 0) = 0;" + "r0 = 0;" + "exit;" + ::: __clobber_all); +} + +/* + * Same as spill_join_with_imprecise_off but the write is BPF_ST. + * Use "r2 = -8; r1 += r2" to make arg tracking lose offset + * precision while the main verifier keeps r1 as fixed-offset. + * + * fp-8 = &fp-24 + * fp-16 = &fp-32 + * r1 = fp-8 (imprecise to arg tracking) + * *(u64 *)(r1 + 0) = 0 -- BPF_ST with immediate + * r0 = *(u64 *)(r10 - 16) -- fill from fp-16 + * r0 = *(u64 *)(r0 + 0) -- deref: should produce use + */ +SEC("socket") +__log_level(2) +__success +__msg("13: (79) r0 = *(u64 *)(r0 +0) ; use: fp0-32") +__naked void st_imm_join_with_imprecise_off(void) +{ + asm volatile ( + "*(u64 *)(r10 - 24) = 0;" + "*(u64 *)(r10 - 32) = 0;" + "r1 = r10;" + "r1 += -24;" + "*(u64 *)(r10 - 8) = r1;" + "r1 = r10;" + "r1 += -32;" + "*(u64 *)(r10 - 16) = r1;" + /* r1 = fp-8 but arg tracking sees off_cnt == 0 */ + "r1 = r10;" + "r2 = -8;" + "r1 += r2;" + /* store immediate through imprecise r1 */ + "*(u64 *)(r1 + 0) = 0;" + /* read back fp-16 */ + "r0 = *(u64 *)(r10 - 16);" + /* deref: should produce use */ + "r0 = *(u64 *)(r0 + 0);" + "r0 = 0;" + "exit;" + ::: __clobber_all); +} + +/* + * Test that spilling through an ARG_IMPRECISE pointer joins with + * existing at_stack values. Subprog receives r1 = fp0-24 and + * r2 = map_value, creates an ARG_IMPRECISE pointer by joining caller + * and callee FP on two branches. + * + * Setup: callee spills &fp1-16 to fp1-8 (precise, tracked). + * Then writes map_value through ARG_IMPRECISE r1 — on path A + * this hits fp1-8, on path B it hits caller stack. + * Since spill_to_stack is skipped for ARG_IMPRECISE dst, + * fp1-8 tracking isn't joined with none. + * + * Expected after the imprecise write: + * - arg tracking should show fp1-8 = fp1-16|fp1+0 (joined with none) + * - read from fp1-8 and deref should produce use for fp1-16 + * - write through it should NOT produce def for fp1-16 + */ +SEC("socket") +__log_level(2) +__success +__msg("26: (79) r0 = *(u64 *)(r10 -8) // r1=IMP3 r6=fp0-24 r7=fp1-16 fp-8=fp1-16|fp1+0") +__naked void imprecise_dst_spill_join(void) +{ + asm volatile ( + "*(u64 *)(r10 - 24) = 0;" + /* map lookup for a valid non-FP pointer */ + "*(u32 *)(r10 - 32) = 0;" + "r1 = %[map] ll;" + "r2 = r10;" + "r2 += -32;" + "call %[bpf_map_lookup_elem];" + "if r0 == 0 goto 1f;" + /* r1 = &caller_fp-24, r2 = map_value */ + "r1 = r10;" + "r1 += -24;" + "r2 = r0;" + "call imprecise_dst_spill_join_sub;" +"1:" + "r0 = 0;" + "exit;" + :: __imm_addr(map), + __imm(bpf_map_lookup_elem) + : __clobber_all); +} + +static __used __naked void imprecise_dst_spill_join_sub(void) +{ + asm volatile ( + /* r6 = &caller_fp-24 (frame=0), r8 = map_value */ + "r6 = r1;" + "r8 = r2;" + /* spill &fp1-16 to fp1-8: at_stack[0] = fp1-16 */ + "*(u64 *)(r10 - 16) = 0;" + "r7 = r10;" + "r7 += -16;" + "*(u64 *)(r10 - 8) = r7;" + /* branch to create ARG_IMPRECISE pointer */ + "call %[bpf_get_prandom_u32];" + /* path B: r1 = caller fp-24 (frame=0) */ + "r1 = r6;" + "if r0 == 0 goto 1f;" + /* path A: r1 = callee fp-8 (frame=1) */ + "r1 = r10;" + "r1 += -8;" +"1:" + /* r1 = ARG_IMPRECISE{mask=BIT(0)|BIT(1)}. + * Write map_value (non-FP) through r1. On path A this overwrites fp1-8. + * Should join at_stack[0] with none: fp1-16|fp1+0. + */ + "*(u64 *)(r1 + 0) = r8;" + /* read fp1-8: should be fp1-16|fp1+0 (joined) */ + "r0 = *(u64 *)(r10 - 8);" + "*(u64 *)(r0 + 0) = 42;" + "r0 = 0;" + "exit;" + :: __imm(bpf_get_prandom_u32) + : __clobber_all); +} diff --git a/tools/testing/selftests/bpf/progs/verifier_precision.c b/tools/testing/selftests/bpf/progs/verifier_precision.c index 4794903aec8e..6f325876efdd 100644 --- a/tools/testing/selftests/bpf/progs/verifier_precision.c +++ b/tools/testing/selftests/bpf/progs/verifier_precision.c @@ -75,8 +75,8 @@ __naked int bpf_end_to_be(void) #if (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \ (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64) || \ - defined(__TARGET_ARCH_arm) || defined(__TARGET_ARCH_s390)) && \ - __clang_major__ >= 18 + defined(__TARGET_ARCH_arm) || defined(__TARGET_ARCH_s390) || \ + defined(__TARGET_ARCH_loongarch)) && __clang_major__ >= 18 SEC("?raw_tp") __success __log_level(2) diff --git a/tools/testing/selftests/bpf/sysctl_helpers.c b/tools/testing/selftests/bpf/sysctl_helpers.c new file mode 100644 index 000000000000..e2bd824f12d5 --- /dev/null +++ b/tools/testing/selftests/bpf/sysctl_helpers.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +#include "sysctl_helpers.h" +#include "test_progs.h" + +int sysctl_set(const char *sysctl_path, char *old_val, const char *new_val) +{ + int ret = 0; + FILE *fp; + + fp = fopen(sysctl_path, "r+"); + if (!fp) + return -errno; + if (old_val && fscanf(fp, "%s", old_val) <= 0) { + ret = -ENOENT; + } else if (!old_val || strcmp(old_val, new_val) != 0) { + fseek(fp, 0, SEEK_SET); + if (fprintf(fp, "%s", new_val) < 0) + ret = -errno; + } + fclose(fp); + + return ret; +} + +int sysctl_set_or_fail(const char *sysctl_path, char *old_val, const char *new_val) +{ + int err; + + err = sysctl_set(sysctl_path, old_val, new_val); + if (err) + PRINT_FAIL("failed to set %s to %s: %s\n", sysctl_path, new_val, strerror(-err)); + return err; +} diff --git a/tools/testing/selftests/bpf/sysctl_helpers.h b/tools/testing/selftests/bpf/sysctl_helpers.h new file mode 100644 index 000000000000..35e37bfe1b3b --- /dev/null +++ b/tools/testing/selftests/bpf/sysctl_helpers.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __SYSCTL_HELPERS_H +#define __SYSCTL_HELPERS_H + +int sysctl_set(const char *sysctl_path, char *old_val, const char *new_val); +int sysctl_set_or_fail(const char *sysctl_path, char *old_val, const char *new_val); + +#endif diff --git a/tools/testing/selftests/breakpoints/Makefile b/tools/testing/selftests/breakpoints/Makefile index 9ec2c78de8ca..0b8f5acf7c78 100644 --- a/tools/testing/selftests/breakpoints/Makefile +++ b/tools/testing/selftests/breakpoints/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Taken from perf makefile -uname_M := $(shell uname -m 2>/dev/null || echo not) -ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) +ARCH ?= $(shell uname -m 2>/dev/null || echo not) +override ARCH := $(shell echo $(ARCH) | sed -e s/i.86/x86/ -e s/x86_64/x86/) TEST_GEN_PROGS := step_after_suspend_test diff --git a/tools/testing/selftests/cgroup/lib/cgroup_util.c b/tools/testing/selftests/cgroup/lib/cgroup_util.c index 6a7295347e90..42f54936f4bb 100644 --- a/tools/testing/selftests/cgroup/lib/cgroup_util.c +++ b/tools/testing/selftests/cgroup/lib/cgroup_util.c @@ -106,8 +106,9 @@ int cg_read_strcmp(const char *cgroup, const char *control, /* Handle the case of comparing against empty string */ if (!expected) return -1; - else - size = strlen(expected) + 1; + + /* needs size > 1, otherwise cg_read() reads 0 bytes */ + size = (expected[0] == '\0') ? 2 : strlen(expected) + 1; buf = malloc(size); if (!buf) diff --git a/tools/testing/selftests/cgroup/test_cpuset_v1_base.sh b/tools/testing/selftests/cgroup/test_cpuset_v1_base.sh index 42a6628fb8bc..1c0444729e70 100755 --- a/tools/testing/selftests/cgroup/test_cpuset_v1_base.sh +++ b/tools/testing/selftests/cgroup/test_cpuset_v1_base.sh @@ -18,7 +18,7 @@ write_test() { echo "testing $interface $value" echo $value > $dir/$interface new=$(cat $dir/$interface) - [[ $value -ne $(cat $dir/$interface) ]] && { + [[ "$value" != "$new" ]] && { echo "$interface write $value failed: new:$new" exit 1 } diff --git a/tools/testing/selftests/cgroup/test_kmem.c b/tools/testing/selftests/cgroup/test_kmem.c index eeabd34bf083..12f59925500b 100644 --- a/tools/testing/selftests/cgroup/test_kmem.c +++ b/tools/testing/selftests/cgroup/test_kmem.c @@ -368,11 +368,15 @@ static int test_percpu_basic(const char *root) for (i = 0; i < 1000; i++) { child = cg_name_indexed(parent, "child", i); - if (!child) - return -1; - - if (cg_create(child)) + if (!child) { + ret = -1; goto cleanup_children; + } + + if (cg_create(child)) { + free(child); + goto cleanup_children; + } free(child); } diff --git a/tools/testing/selftests/drivers/net/README.rst b/tools/testing/selftests/drivers/net/README.rst index c8588436c224..c6bed9a985bc 100644 --- a/tools/testing/selftests/drivers/net/README.rst +++ b/tools/testing/selftests/drivers/net/README.rst @@ -211,8 +211,8 @@ Avoid libraries and frameworks Test files should be relatively self contained. The libraries should only include very core or non-trivial code. -It may be tempting to "factor out" the common code, but fight that urge. -Library code increases the barrier of entry, and complexity in general. +It may be tempting to "factor out" the common code to lib/py/, but fight that +urge. Library code increases the barrier of entry, and complexity in general. Avoid mixing test code and boilerplate ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -290,6 +290,12 @@ or:: def test(cfg, mode, protocol): pass +Linters +~~~~~~~ + +We expect clean ``ruff check`` and ``pylint --disable=R``. +The code should be clean, avoid disabling pylint warnings explicitly! + Running tests CI-style ====================== diff --git a/tools/testing/selftests/drivers/net/bonding/lag_lib.sh b/tools/testing/selftests/drivers/net/bonding/lag_lib.sh index bf9bcd1b5ec0..f2e43b6c4c81 100644 --- a/tools/testing/selftests/drivers/net/bonding/lag_lib.sh +++ b/tools/testing/selftests/drivers/net/bonding/lag_lib.sh @@ -23,20 +23,9 @@ test_LAG_cleanup() ip link set dev dummy2 master "$name" elif [ "$driver" = "team" ]; then name="team0" - teamd -d -c ' - { - "device": "'"$name"'", - "runner": { - "name": "'"$mode"'" - }, - "ports": { - "dummy1": - {}, - "dummy2": - {} - } - } - ' + ip link add "$name" type team + ip link set dev dummy1 master "$name" + ip link set dev dummy2 master "$name" ip link set dev "$name" up else check_err 1 diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile index 85ca4d1ecf9e..82809d5b2478 100644 --- a/tools/testing/selftests/drivers/net/hw/Makefile +++ b/tools/testing/selftests/drivers/net/hw/Makefile @@ -31,6 +31,7 @@ TEST_PROGS = \ hw_stats_l3.sh \ hw_stats_l3_gre.sh \ iou-zcrx.py \ + ipsec_vxlan.py \ irq.py \ loopback.sh \ nic_timestamp.py \ diff --git a/tools/testing/selftests/drivers/net/hw/config b/tools/testing/selftests/drivers/net/hw/config index dd50cb8a7911..8c132ace2b8d 100644 --- a/tools/testing/selftests/drivers/net/hw/config +++ b/tools/testing/selftests/drivers/net/hw/config @@ -3,6 +3,10 @@ CONFIG_FAIL_FUNCTION=y CONFIG_FAULT_INJECTION=y CONFIG_FAULT_INJECTION_DEBUG_FS=y CONFIG_FUNCTION_ERROR_INJECTION=y +CONFIG_INET6_ESP=y +CONFIG_INET6_ESP_OFFLOAD=y +CONFIG_INET_ESP=y +CONFIG_INET_ESP_OFFLOAD=y CONFIG_IO_URING=y CONFIG_IPV6=y CONFIG_IPV6_GRE=y @@ -14,3 +18,4 @@ CONFIG_NETKIT=y CONFIG_NET_SCH_INGRESS=y CONFIG_UDMABUF=y CONFIG_VXLAN=y +CONFIG_XFRM_USER=y diff --git a/tools/testing/selftests/drivers/net/hw/ipsec_vxlan.py b/tools/testing/selftests/drivers/net/hw/ipsec_vxlan.py new file mode 100755 index 000000000000..0740a4d85240 --- /dev/null +++ b/tools/testing/selftests/drivers/net/hw/ipsec_vxlan.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +"""Traffic test for VXLAN + IPsec crypto-offload.""" + +import os + +from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_ge +from lib.py import ksft_variants, KsftNamedVariant, KsftSkipEx +from lib.py import CmdExitFailure, NetDrvEpEnv, cmd, defer, ethtool, ip +from lib.py import Iperf3Runner + +# Inner tunnel addresses - TEST-NET-2 (RFC 5737) / doc prefix (RFC 3849) +INNER_V4_LOCAL = "198.51.100.1" +INNER_V4_REMOTE = "198.51.100.2" +INNER_V6_LOCAL = "2001:db8:100::1" +INNER_V6_REMOTE = "2001:db8:100::2" + +# ESP parameters +SPI_OUT = "0x1000" +SPI_IN = "0x1001" +# 128-bit key + 32-bit salt = 20 bytes hex, 128-bit ICV +ESP_AEAD = "aead 'rfc4106(gcm(aes))' 0x" + "01" * 20 + " 128" + + +def xfrm(args, host=None): + """Runs 'ip xfrm' via shell to preserve parentheses in algo names.""" + cmd(f"ip xfrm {args}", shell=True, host=host) + + +def check_xfrm_offload_support(): + """Skips if iproute2 lacks xfrm offload support.""" + out = cmd("ip xfrm state help", fail=False) + if "offload" not in out.stdout + out.stderr: + raise KsftSkipEx("iproute2 too old, missing xfrm offload") + + +def check_esp_hw_offload(cfg): + """Skips if device lacks esp-hw-offload support.""" + check_xfrm_offload_support() + try: + feat = ethtool(f"-k {cfg.ifname}", json=True)[0] + except (CmdExitFailure, IndexError) as e: + raise KsftSkipEx(f"can't query features: {e}") from e + if not feat.get("esp-hw-offload", {}).get("active"): + raise KsftSkipEx("Device does not support esp-hw-offload") + + +def get_tx_drops(cfg): + """Returns TX dropped counter from the physical device.""" + stats = ip("-s -s link show dev " + cfg.ifname, json=True)[0] + return stats["stats64"]["tx"]["dropped"] + + +def setup_vxlan_ipsec(cfg, outer_ipver, inner_ipver): + """Sets up VXLAN tunnel with IPsec transport-mode crypto-offload.""" + vxlan_name = f"vx{os.getpid()}" + local_addr = cfg.addr_v[outer_ipver] + remote_addr = cfg.remote_addr_v[outer_ipver] + + if inner_ipver == "4": + inner_local = f"{INNER_V4_LOCAL}/24" + inner_remote = f"{INNER_V4_REMOTE}/24" + addr_extra = "" + else: + inner_local = f"{INNER_V6_LOCAL}/64" + inner_remote = f"{INNER_V6_REMOTE}/64" + addr_extra = " nodad" + + if outer_ipver == "6": + vxlan_opts = "udp6zerocsumtx udp6zerocsumrx" + else: + vxlan_opts = "noudpcsum" + + # VXLAN tunnel - local side + ip(f"link add {vxlan_name} type vxlan id 100 dstport 4789 {vxlan_opts} " + f"local {local_addr} remote {remote_addr} dev {cfg.ifname}") + defer(ip, f"link del {vxlan_name}") + ip(f"addr add {inner_local} dev {vxlan_name}{addr_extra}") + ip(f"link set {vxlan_name} up") + + # VXLAN tunnel - remote side + ip(f"link add {vxlan_name} type vxlan id 100 dstport 4789 {vxlan_opts} " + f"local {remote_addr} remote {local_addr} dev {cfg.remote_ifname}", + host=cfg.remote) + defer(ip, f"link del {vxlan_name}", host=cfg.remote) + ip(f"addr add {inner_remote} dev {vxlan_name}{addr_extra}", + host=cfg.remote) + ip(f"link set {vxlan_name} up", host=cfg.remote) + + # xfrm state - local outbound SA + xfrm(f"state add src {local_addr} dst {remote_addr} " + f"proto esp spi {SPI_OUT} " + f"{ESP_AEAD} " + f"mode transport offload crypto dev {cfg.ifname} dir out") + defer(xfrm, f"state del src {local_addr} dst {remote_addr} " + f"proto esp spi {SPI_OUT}") + + # xfrm state - local inbound SA + xfrm(f"state add src {remote_addr} dst {local_addr} " + f"proto esp spi {SPI_IN} " + f"{ESP_AEAD} " + f"mode transport offload crypto dev {cfg.ifname} dir in") + defer(xfrm, f"state del src {remote_addr} dst {local_addr} " + f"proto esp spi {SPI_IN}") + + # xfrm state - remote outbound SA (mirror, software crypto) + xfrm(f"state add src {remote_addr} dst {local_addr} " + f"proto esp spi {SPI_IN} " + f"{ESP_AEAD} " + f"mode transport", + host=cfg.remote) + defer(xfrm, f"state del src {remote_addr} dst {local_addr} " + f"proto esp spi {SPI_IN}", host=cfg.remote) + + # xfrm state - remote inbound SA (mirror, software crypto) + xfrm(f"state add src {local_addr} dst {remote_addr} " + f"proto esp spi {SPI_OUT} " + f"{ESP_AEAD} " + f"mode transport", + host=cfg.remote) + defer(xfrm, f"state del src {local_addr} dst {remote_addr} " + f"proto esp spi {SPI_OUT}", host=cfg.remote) + + # xfrm policy - local out + xfrm(f"policy add src {local_addr} dst {remote_addr} " + f"proto udp dport 4789 dir out " + f"tmpl src {local_addr} dst {remote_addr} proto esp mode transport") + defer(xfrm, f"policy del src {local_addr} dst {remote_addr} " + f"proto udp dport 4789 dir out") + + # xfrm policy - local in + xfrm(f"policy add src {remote_addr} dst {local_addr} " + f"proto udp dport 4789 dir in " + f"tmpl src {remote_addr} dst {local_addr} proto esp mode transport") + defer(xfrm, f"policy del src {remote_addr} dst {local_addr} " + f"proto udp dport 4789 dir in") + + # xfrm policy - remote out + xfrm(f"policy add src {remote_addr} dst {local_addr} " + f"proto udp dport 4789 dir out " + f"tmpl src {remote_addr} dst {local_addr} proto esp mode transport", + host=cfg.remote) + defer(xfrm, f"policy del src {remote_addr} dst {local_addr} " + f"proto udp dport 4789 dir out", host=cfg.remote) + + # xfrm policy - remote in + xfrm(f"policy add src {local_addr} dst {remote_addr} " + f"proto udp dport 4789 dir in " + f"tmpl src {local_addr} dst {remote_addr} proto esp mode transport", + host=cfg.remote) + defer(xfrm, f"policy del src {local_addr} dst {remote_addr} " + f"proto udp dport 4789 dir in", host=cfg.remote) + + +def _vxlan_ipsec_variants(): + """Generates outer/inner IP version variants.""" + for outer in ["4", "6"]: + for inner in ["4", "6"]: + yield KsftNamedVariant(f"outer_v{outer}_inner_v{inner}", outer, inner) + + +@ksft_variants(_vxlan_ipsec_variants()) +def test_vxlan_ipsec_crypto_offload(cfg, outer_ipver, inner_ipver): + """Tests VXLAN+IPsec crypto-offload has no TX drops.""" + cfg.require_ipver(outer_ipver) + check_esp_hw_offload(cfg) + + setup_vxlan_ipsec(cfg, outer_ipver, inner_ipver) + + if inner_ipver == "4": + inner_local = INNER_V4_LOCAL + inner_remote = INNER_V4_REMOTE + ping = "ping" + else: + inner_local = INNER_V6_LOCAL + inner_remote = INNER_V6_REMOTE + ping = "ping -6" + + cmd(f"{ping} -c 1 -W 2 {inner_remote}") + + drops_before = get_tx_drops(cfg) + + runner = Iperf3Runner(cfg, server_ip=inner_local, + client_ip=inner_remote) + bw_gbps = runner.measure_bandwidth(reverse=True) + + cfg.wait_hw_stats_settle() + drops_after = get_tx_drops(cfg) + + ksft_eq(drops_after - drops_before, 0, + comment="TX drops during VXLAN+IPsec") + ksft_ge(bw_gbps, 0.1, + comment="Minimum 100Mbps over VXLAN+IPsec") + + +def main(): + """Runs VXLAN+IPsec crypto-offload GSO selftest.""" + with NetDrvEpEnv(__file__, nsim_test=False) as cfg: + ksft_run([test_vxlan_ipsec_crypto_offload], args=(cfg,)) + ksft_exit() + + +if __name__ == "__main__": + main() diff --git a/tools/testing/selftests/drivers/net/lib/py/load.py b/tools/testing/selftests/drivers/net/lib/py/load.py index f181fa2d38fc..e24660e5c27f 100644 --- a/tools/testing/selftests/drivers/net/lib/py/load.py +++ b/tools/testing/selftests/drivers/net/lib/py/load.py @@ -48,7 +48,10 @@ class Iperf3Runner: Starts the iperf3 client with the configured options. """ cmdline = self._build_client(streams, duration, reverse) - return cmd(cmdline, background=background, host=self.env.remote) + kwargs = {"background": background, "host": self.env.remote} + if not background: + kwargs["timeout"] = duration + 5 + return cmd(cmdline, **kwargs) def measure_bandwidth(self, reverse=False): """ diff --git a/tools/testing/selftests/drivers/net/shaper.py b/tools/testing/selftests/drivers/net/shaper.py index 11310f19bfa0..e39d270e688d 100755 --- a/tools/testing/selftests/drivers/net/shaper.py +++ b/tools/testing/selftests/drivers/net/shaper.py @@ -1,7 +1,10 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0 -from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_true, KsftSkipEx +import errno + +from lib.py import ksft_run, ksft_exit +from lib.py import ksft_eq, ksft_raises, ksft_true, KsftSkipEx from lib.py import EthtoolFamily, NetshaperFamily from lib.py import NetDrvEnv from lib.py import NlError @@ -438,6 +441,21 @@ def queue_update(cfg, nl_shaper) -> None: nl_shaper.delete({'ifindex': cfg.ifindex, 'handle': {'scope': 'queue', 'id': i}}) +def dup_leaves(cfg, nl_shaper) -> None: + """ Ensure that the kernel rejects duplicate leaves. """ + if not cfg.groups: + raise KsftSkipEx("device does not support node scope") + + with ksft_raises(NlError) as cm: + nl_shaper.group({ + 'ifindex': cfg.ifindex, + 'leaves':[{'handle': {'scope': 'queue', 'id': 0}}, + {'handle': {'scope': 'queue', 'id': 0}}], + 'handle': {'scope':'node'}, + 'metric': 'bps', + 'bw-max': 10000}) + ksft_eq(cm.exception.error, errno.EINVAL) + def main() -> None: with NetDrvEnv(__file__, queue_count=4) as cfg: cfg.queues = False @@ -453,7 +471,9 @@ def main() -> None: basic_groups, qgroups, delegation, - queue_update], args=(cfg, NetshaperFamily())) + dup_leaves, + queue_update], + args=(cfg, NetshaperFamily())) ksft_exit() diff --git a/tools/testing/selftests/drivers/net/team/dev_addr_lists.sh b/tools/testing/selftests/drivers/net/team/dev_addr_lists.sh index b1ec7755b783..26469f3be022 100755 --- a/tools/testing/selftests/drivers/net/team/dev_addr_lists.sh +++ b/tools/testing/selftests/drivers/net/team/dev_addr_lists.sh @@ -42,8 +42,6 @@ team_cleanup() } -require_command teamd - trap cleanup EXIT tests_run diff --git a/tools/testing/selftests/fchmodat2/fchmodat2_test.c b/tools/testing/selftests/fchmodat2/fchmodat2_test.c index e397339495f6..8e1b6384b319 100644 --- a/tools/testing/selftests/fchmodat2/fchmodat2_test.c +++ b/tools/testing/selftests/fchmodat2/fchmodat2_test.c @@ -9,6 +9,11 @@ #include "kselftest.h" +struct testdir { + char *dirname; + int dfd; +}; + int sys_fchmodat2(int dfd, const char *filename, mode_t mode, int flags) { int ret = syscall(__NR_fchmodat2, dfd, filename, mode, flags); @@ -16,9 +21,9 @@ int sys_fchmodat2(int dfd, const char *filename, mode_t mode, int flags) return ret >= 0 ? ret : -errno; } -int setup_testdir(void) +static void setup_testdir(struct testdir *testdir) { - int dfd, ret; + int ret, dfd; char dirname[] = "/tmp/ksft-fchmodat2.XXXXXX"; /* Make the top-level directory. */ @@ -26,21 +31,48 @@ int setup_testdir(void) ksft_exit_fail_msg("%s: failed to create tmpdir\n", __func__); dfd = open(dirname, O_PATH | O_DIRECTORY); - if (dfd < 0) - ksft_exit_fail_msg("%s: failed to open tmpdir\n", __func__); + if (dfd < 0) { + ksft_perror("failed to open tmpdir"); + goto err; + } ret = openat(dfd, "regfile", O_CREAT | O_WRONLY | O_TRUNC, 0644); - if (ret < 0) - ksft_exit_fail_msg("%s: failed to create file in tmpdir\n", - __func__); + if (ret < 0) { + ksft_perror("failed to create file in tmpdir"); + goto err; + } close(ret); ret = symlinkat("regfile", dfd, "symlink"); - if (ret < 0) - ksft_exit_fail_msg("%s: failed to create symlink in tmpdir\n", - __func__); + if (ret < 0) { + ksft_perror("symlinkat() failed"); + goto err_regfile; + } - return dfd; + testdir->dirname = strdup(dirname); + if (!testdir->dirname) { + ksft_perror("Out of memory"); + goto err_symlink; + } + testdir->dfd = dfd; + + return; + +err_symlink: + unlinkat(testdir->dfd, "symlink", 0); +err_regfile: + unlinkat(testdir->dfd, "regfile", 0); +err: + unlink(dirname); + ksft_exit_fail(); +} + +static void cleanup_testdir(struct testdir *testdir) +{ + unlinkat(testdir->dfd, "regfile", 0); + unlinkat(testdir->dfd, "symlink", 0); + rmdir(testdir->dirname); + free(testdir->dirname); } int expect_mode(int dfd, const char *filename, mode_t expect_mode) @@ -48,61 +80,80 @@ int expect_mode(int dfd, const char *filename, mode_t expect_mode) struct stat st; int ret = fstatat(dfd, filename, &st, AT_SYMLINK_NOFOLLOW); - if (ret) - ksft_exit_fail_msg("%s: %s: fstatat failed\n", - __func__, filename); + if (ret) { + ksft_perror("fstatat() failed\n"); + return 0; + } return (st.st_mode == expect_mode); } void test_regfile(void) { - int dfd, ret; + struct testdir testdir; + int ret; - dfd = setup_testdir(); + setup_testdir(&testdir); - ret = sys_fchmodat2(dfd, "regfile", 0640, 0); + ret = sys_fchmodat2(testdir.dfd, "regfile", 0640, 0); - if (ret < 0) - ksft_exit_fail_msg("%s: fchmodat2(noflag) failed\n", __func__); + if (ret < 0) { + ksft_perror("fchmodat2(noflag) failed"); + goto out; + } - if (!expect_mode(dfd, "regfile", 0100640)) - ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2\n", + if (!expect_mode(testdir.dfd, "regfile", 0100640)) { + ksft_print_msg("%s: wrong file mode bits after fchmodat2\n", __func__); + ret = 1; + goto out; + } - ret = sys_fchmodat2(dfd, "regfile", 0600, AT_SYMLINK_NOFOLLOW); + ret = sys_fchmodat2(testdir.dfd, "regfile", 0600, AT_SYMLINK_NOFOLLOW); - if (ret < 0) - ksft_exit_fail_msg("%s: fchmodat2(AT_SYMLINK_NOFOLLOW) failed\n", - __func__); + if (ret < 0) { + ksft_perror("fchmodat2(AT_SYMLINK_NOFOLLOW) failed"); + goto out; + } - if (!expect_mode(dfd, "regfile", 0100600)) - ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2 with nofollow\n", - __func__); + if (!expect_mode(testdir.dfd, "regfile", 0100600)) { + ksft_print_msg("%s: wrong file mode bits after fchmodat2 with nofollow\n", + __func__); + ret = 1; + } - ksft_test_result_pass("fchmodat2(regfile)\n"); +out: + ksft_test_result(ret == 0, "fchmodat2(regfile)\n"); + cleanup_testdir(&testdir); } void test_symlink(void) { - int dfd, ret; + struct testdir testdir; + int ret; - dfd = setup_testdir(); + setup_testdir(&testdir); - ret = sys_fchmodat2(dfd, "symlink", 0640, 0); + ret = sys_fchmodat2(testdir.dfd, "symlink", 0640, 0); - if (ret < 0) - ksft_exit_fail_msg("%s: fchmodat2(noflag) failed\n", __func__); + if (ret < 0) { + ksft_perror("fchmodat2(noflag) failed"); + goto err; + } - if (!expect_mode(dfd, "regfile", 0100640)) - ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2\n", - __func__); + if (!expect_mode(testdir.dfd, "regfile", 0100640)) { + ksft_print_msg("%s: wrong file mode bits after fchmodat2\n", + __func__); + goto err; + } - if (!expect_mode(dfd, "symlink", 0120777)) - ksft_exit_fail_msg("%s: wrong symlink mode bits after fchmodat2\n", - __func__); + if (!expect_mode(testdir.dfd, "symlink", 0120777)) { + ksft_print_msg("%s: wrong symlink mode bits after fchmodat2\n", + __func__); + goto err; + } - ret = sys_fchmodat2(dfd, "symlink", 0600, AT_SYMLINK_NOFOLLOW); + ret = sys_fchmodat2(testdir.dfd, "symlink", 0600, AT_SYMLINK_NOFOLLOW); /* * On certain filesystems (xfs or btrfs), chmod operation fails. So we @@ -111,18 +162,28 @@ void test_symlink(void) * * https://sourceware.org/legacy-ml/libc-alpha/2020-02/msg00467.html */ - if (ret == 0 && !expect_mode(dfd, "symlink", 0120600)) - ksft_exit_fail_msg("%s: wrong symlink mode bits after fchmodat2 with nofollow\n", + if (ret == 0 && !expect_mode(testdir.dfd, "symlink", 0120600)) { + ksft_print_msg("%s: wrong symlink mode bits after fchmodat2 with nofollow\n", __func__); + ret = 1; + goto err; + } - if (!expect_mode(dfd, "regfile", 0100640)) - ksft_exit_fail_msg("%s: wrong file mode bits after fchmodat2 with nofollow\n", - __func__); + if (!expect_mode(testdir.dfd, "regfile", 0100640)) { + ksft_print_msg("%s: wrong file mode bits after fchmodat2 with nofollow\n", + __func__); + } if (ret != 0) ksft_test_result_skip("fchmodat2(symlink)\n"); else ksft_test_result_pass("fchmodat2(symlink)\n"); + cleanup_testdir(&testdir); + return; + +err: + ksft_test_result_fail("fchmodat2(symlink)\n"); + cleanup_testdir(&testdir); } #define NUM_TESTS 2 @@ -135,8 +196,5 @@ int main(int argc, char **argv) test_regfile(); test_symlink(); - if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0) - ksft_exit_fail(); - else - ksft_exit_pass(); + ksft_finished(); } diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc new file mode 100644 index 000000000000..2915206777b6 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc @@ -0,0 +1,87 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Generic dynamic event - add/remove fprobe events on module +# requires: dynamic_events "f[:[/][]] [%return] []":README enabled_functions + +rmmod trace-events-sample ||: +if ! modprobe trace-events-sample ; then + echo "No trace-events sample module - please make CONFIG_SAMPLE_TRACE_EVENTS=m" + exit_unresolved; +fi +trap "lsmod | grep -q trace_events_sample && rmmod trace-events-sample" EXIT + +echo 0 > events/enable +echo > dynamic_events + +FUNC1='foo_bar*' +FUNC2='vfs_read' + +:;: "Add an event on the test module" ;: +echo "f:test1 $FUNC1" >> dynamic_events +echo 1 > events/fprobes/test1/enable + +:;: "Ensure it is enabled" ;: +funcs=`cat enabled_functions | wc -l` +test $funcs -ne 0 + +:;: "Check the enabled_functions is cleared on unloading" ;: +rmmod trace-events-sample +funcs=`cat enabled_functions | wc -l` +test $funcs -eq 0 + +:;: "Check it is kept clean" ;: +modprobe trace-events-sample +echo 1 > events/fprobes/test1/enable || echo "OK" +funcs=`cat enabled_functions | wc -l` +test $funcs -eq 0 + +:;: "Add another event not on the test module" ;: +echo "f:test2 $FUNC2" >> dynamic_events +echo 1 > events/fprobes/test2/enable + +:;: "Ensure it is enabled" ;: +ofuncs=`cat enabled_functions | wc -l` +test $ofuncs -ne 0 + +:;: "Disable and remove the first event" +echo 0 > events/fprobes/test1/enable +echo "-:fprobes/test1" >> dynamic_events +funcs=`cat enabled_functions | wc -l` +test $ofuncs -eq $funcs + +:;: "Disable and remove other events" ;: +echo 0 > events/fprobes/enable +echo > dynamic_events +funcs=`cat enabled_functions | wc -l` +test $funcs -eq 0 + +rmmod trace-events-sample + +:;: "Add events on kernel and test module" ;: +modprobe trace-events-sample +echo "f:test1 $FUNC1" >> dynamic_events +echo 1 > events/fprobes/test1/enable +echo "f:test2 $FUNC2" >> dynamic_events +echo 1 > events/fprobes/test2/enable +ofuncs=`cat enabled_functions | wc -l` +test $ofuncs -ne 0 + +:;: "Unload module (ftrace entry should be removed)" ;: +rmmod trace-events-sample +funcs=`cat enabled_functions | wc -l` +test $funcs -ne 0 +test $ofuncs -ne $funcs + +:;: "Disable and remove core-kernel fprobe event" ;: +echo 0 > events/fprobes/test2/enable +echo "-:fprobes/test2" >> dynamic_events + +:;: "Ensure ftrace is disabled." ;: +funcs=`cat enabled_functions | wc -l` +test $funcs -eq 0 + +echo 0 > events/fprobes/enable +echo > dynamic_events + +trap "" EXIT +clear_trace diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_multiple_fprobe.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_multiple_fprobe.tc new file mode 100644 index 000000000000..f2cbf2ffd29b --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_multiple_fprobe.tc @@ -0,0 +1,69 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Generic dynamic event - add/remove multiple fprobe events on the same function +# requires: dynamic_events "f[:[/][]] [%return] []":README enabled_functions + +echo 0 > events/enable +echo > dynamic_events + +PLACE=vfs_read +PLACE2=vfs_open + +:;: 'Ensure no other ftrace user' ;: +test `cat enabled_functions | wc -l` -eq 0 || exit_unresolved + +:;: 'Test case 1: leave entry event' ;: +:;: 'Add entry and exit events on the same place' ;: +echo "f:event1 ${PLACE}" >> dynamic_events +echo "f:event2 ${PLACE}%return" >> dynamic_events + +:;: 'Enable both of them' ;: +echo 1 > events/fprobes/enable +test `cat enabled_functions | wc -l` -eq 1 + +:;: 'Disable and remove exit event' ;: +echo 0 > events/fprobes/event2/enable +echo -:event2 >> dynamic_events + +:;: 'Disable and remove all events' ;: +echo 0 > events/fprobes/enable +echo > dynamic_events + +:;: 'Add another event' ;: +echo "f:event3 ${PLACE2}%return" > dynamic_events +echo 1 > events/fprobes/enable +test `cat enabled_functions | wc -l` -eq 1 + +:;: 'No other ftrace user' ;: +echo 0 > events/fprobes/enable +echo > dynamic_events +test `cat enabled_functions | wc -l` -eq 0 + +:;: 'Test case 2: leave exit event' ;: +:;: 'Add entry and exit events on the same place' ;: +echo "f:event1 ${PLACE}" >> dynamic_events +echo "f:event2 ${PLACE}%return" >> dynamic_events + +:;: 'Enable both of them' ;: +echo 1 > events/fprobes/enable +test `cat enabled_functions | wc -l` -eq 1 + +:;: 'Disable and remove entry event' ;: +echo 0 > events/fprobes/event1/enable +echo -:event1 >> dynamic_events + +:;: 'Disable and remove all events' ;: +echo 0 > events/fprobes/enable +echo > dynamic_events + +:;: 'Add another event' ;: +echo "f:event3 ${PLACE2}" > dynamic_events +echo 1 > events/fprobes/enable +test `cat enabled_functions | wc -l` -eq 1 + +:;: 'No other ftrace user' ;: +echo 0 > events/fprobes/enable +echo > dynamic_events +test `cat enabled_functions | wc -l` -eq 0 + +clear_trace diff --git a/tools/testing/selftests/ftrace/test.d/remotes/functions b/tools/testing/selftests/ftrace/test.d/remotes/functions index 97a09d564a34..05224fac3653 100644 --- a/tools/testing/selftests/ftrace/test.d/remotes/functions +++ b/tools/testing/selftests/ftrace/test.d/remotes/functions @@ -24,12 +24,21 @@ setup_remote_test() assert_loaded() { - grep -q "(loaded)" buffer_size_kb + grep -q "(loaded)" buffer_size_kb || return 1 } assert_unloaded() { - grep -q "(unloaded)" buffer_size_kb + grep -q "(unloaded)" buffer_size_kb || return 1 +} + +reload_remote() +{ + echo 0 > tracing_on + clear_trace + assert_unloaded + echo 1 > tracing_on + assert_loaded } dump_trace_pipe() @@ -79,10 +88,12 @@ get_cpu_ids() sed -n 's/^processor\s*:\s*\([0-9]\+\).*/\1/p' /proc/cpuinfo } -get_page_size() { +get_page_size() +{ sed -ne 's/^.*data.*size:\([0-9][0-9]*\).*/\1/p' events/header_page } -get_selftest_event_size() { +get_selftest_event_size() +{ sed -ne 's/^.*field:.*;.*size:\([0-9][0-9]*\);.*/\1/p' events/*/selftest/format | awk '{s+=$1} END {print s}' } diff --git a/tools/testing/selftests/ftrace/test.d/remotes/hotplug.tc b/tools/testing/selftests/ftrace/test.d/remotes/hotplug.tc new file mode 100644 index 000000000000..145617eb8061 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/remotes/hotplug.tc @@ -0,0 +1,88 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Test trace remote read with an offline CPU +# requires: remotes/test + +. $TEST_DIR/remotes/functions + +hotunplug_one_cpu() +{ + [ "$(get_cpu_ids | wc -l)" -ge 2 ] || return 1 + + for cpu in $(get_cpu_ids); do + echo 0 > /sys/devices/system/cpu/cpu$cpu/online || return 1 + break + done + + echo $cpu +} + +# Check non-consuming and consuming read +check_read() +{ + for i in $(seq 1 8); do + echo $i > write_event + done + + check_trace 1 8 trace + + output=$(dump_trace_pipe) + check_trace 1 8 $output + rm $output +} + +test_hotplug() +{ + echo 0 > trace + assert_loaded + + # + # Test a trace buffer containing an offline CPU + # + + cpu=$(hotunplug_one_cpu) || exit_unsupported + trap "echo 1 > /sys/devices/system/cpu/cpu$cpu/online" EXIT + + check_read + + # + # Test a trace buffer with a missing CPU + # + + reload_remote + + check_read + + # + # Test a trace buffer with a CPU added later + # + + echo 1 > /sys/devices/system/cpu/cpu$cpu/online + trap "" EXIT + assert_loaded + + check_read + + # Test if the ring-buffer for the newly added CPU is both writable and + # readable + for i in $(seq 1 8); do + taskset -c $cpu echo $i > write_event + done + + cd per_cpu/cpu$cpu/ + + check_trace 1 8 trace + + output=$(dump_trace_pipe) + check_trace 1 8 $output + rm $output + + cd - +} + +if [ -z "$SOURCE_REMOTE_TEST" ]; then + set -e + + setup_remote_test + test_hotplug +fi diff --git a/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/buffer_size.tc b/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/buffer_size.tc new file mode 100644 index 000000000000..64bf859d6406 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/buffer_size.tc @@ -0,0 +1,11 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Test hypervisor trace buffer size +# requires: remotes/hypervisor/write_event + +SOURCE_REMOTE_TEST=1 +. $TEST_DIR/remotes/buffer_size.tc + +set -e +setup_remote "hypervisor" +test_buffer_size diff --git a/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/hotplug.tc b/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/hotplug.tc new file mode 100644 index 000000000000..580ec32c8f81 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/hotplug.tc @@ -0,0 +1,11 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Test hypervisor trace read with an offline CPU +# requires: remotes/hypervisor/write_event + +SOURCE_REMOTE_TEST=1 +. $TEST_DIR/remotes/hotplug.tc + +set -e +setup_remote "hypervisor" +test_hotplug diff --git a/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/reset.tc b/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/reset.tc new file mode 100644 index 000000000000..7fe3b09b34e3 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/reset.tc @@ -0,0 +1,11 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Test hypervisor trace buffer reset +# requires: remotes/hypervisor/write_event + +SOURCE_REMOTE_TEST=1 +. $TEST_DIR/remotes/reset.tc + +set -e +setup_remote "hypervisor" +test_reset diff --git a/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/trace.tc b/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/trace.tc new file mode 100644 index 000000000000..b937c19ca7f9 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/trace.tc @@ -0,0 +1,11 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Test hypervisor non-consuming trace read +# requires: remotes/hypervisor/write_event + +SOURCE_REMOTE_TEST=1 +. $TEST_DIR/remotes/trace.tc + +set -e +setup_remote "hypervisor" +test_trace diff --git a/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/trace_pipe.tc b/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/trace_pipe.tc new file mode 100644 index 000000000000..66aa1b76c147 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/trace_pipe.tc @@ -0,0 +1,11 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Test hypervisor consuming trace read +# requires: remotes/hypervisor/write_event + +SOURCE_REMOTE_TEST=1 +. $TEST_DIR/remotes/trace_pipe.tc + +set -e +setup_remote "hypervisor" +test_trace_pipe diff --git a/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/unloading.tc b/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/unloading.tc new file mode 100644 index 000000000000..1dafde3414ab --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/remotes/hypervisor/unloading.tc @@ -0,0 +1,11 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Test hypervisor trace buffer unloading +# requires: remotes/hypervisor/write_event + +SOURCE_REMOTE_TEST=1 +. $TEST_DIR/remotes/unloading.tc + +set -e +setup_remote "hypervisor" +test_unloading diff --git a/tools/testing/selftests/ftrace/test.d/remotes/trace.tc b/tools/testing/selftests/ftrace/test.d/remotes/trace.tc index 170f7648732a..bc9377a70e8d 100644 --- a/tools/testing/selftests/ftrace/test.d/remotes/trace.tc +++ b/tools/testing/selftests/ftrace/test.d/remotes/trace.tc @@ -58,11 +58,7 @@ test_trace() # # Ensure the writer is not on the reader page by reloading the buffer - echo 0 > tracing_on - echo 0 > trace - assert_unloaded - echo 1 > tracing_on - assert_loaded + reload_remote # Ensure ring-buffer overflow by emitting events from the same CPU for cpu in $(get_cpu_ids); do @@ -96,27 +92,6 @@ test_trace() cd - > /dev/null done - - # - # Test with hotplug - # - - [ "$(get_cpu_ids | wc -l)" -ge 2 ] || return 0 - - echo 0 > trace - - for cpu in $(get_cpu_ids); do - echo 0 > /sys/devices/system/cpu/cpu$cpu/online || return 0 - break - done - - for i in $(seq 1 8); do - echo $i > write_event - done - - check_trace 1 8 trace - - echo 1 > /sys/devices/system/cpu/cpu$cpu/online } if [ -z "$SOURCE_REMOTE_TEST" ]; then diff --git a/tools/testing/selftests/ftrace/test.d/remotes/trace_pipe.tc b/tools/testing/selftests/ftrace/test.d/remotes/trace_pipe.tc index 669a7288ed7c..7f7b7b79c490 100644 --- a/tools/testing/selftests/ftrace/test.d/remotes/trace_pipe.tc +++ b/tools/testing/selftests/ftrace/test.d/remotes/trace_pipe.tc @@ -92,31 +92,6 @@ test_trace_pipe() rm $output cd - > /dev/null done - - # - # Test interaction with hotplug - # - - [ "$(get_cpu_ids | wc -l)" -ge 2 ] || return 0 - - echo 0 > trace - - for cpu in $(get_cpu_ids); do - echo 0 > /sys/devices/system/cpu/cpu$cpu/online || return 0 - break - done - - for i in $(seq 1 8); do - echo $i > write_event - done - - output=$(dump_trace_pipe) - - check_trace 1 8 $output - - rm $output - - echo 1 > /sys/devices/system/cpu/cpu$cpu/online } if [ -z "$SOURCE_REMOTE_TEST" ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-fully-qualified-var-ref.tc b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-fully-qualified-var-ref.tc new file mode 100644 index 000000000000..8d12cdd06f1d --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-fully-qualified-var-ref.tc @@ -0,0 +1,34 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: event trigger - test fully-qualified variable reference support +# requires: set_event synthetic_events events/sched/sched_process_fork/hist ping:program + +fail() { #msg + echo $1 + exit_fail +} + +echo "Test fully-qualified variable reference support" + +echo 'wakeup_latency u64 lat; pid_t pid; int prio; char comm[16]' > synthetic_events +echo 'hist:keys=comm:ts0=common_timestamp.usecs if comm=="ping"' > events/sched/sched_waking/trigger +echo 'hist:keys=comm:ts0=common_timestamp.usecs if comm=="ping"' > events/sched/sched_wakeup/trigger +echo 'hist:keys=next_comm:wakeup_lat=common_timestamp.usecs-sched.sched_wakeup.$ts0:onmatch(sched.sched_waking).wakeup_latency($wakeup_lat,next_pid,sched.sched_waking.prio,next_comm) if next_comm=="ping"' > events/sched/sched_switch/trigger +echo 'hist:keys=pid,prio,comm:vals=lat:sort=pid,prio' > events/synthetic/wakeup_latency/trigger + +ping $LOCALHOST -c 3 +if ! grep -q "ping" events/synthetic/wakeup_latency/hist; then + fail "Failed to create inter-event histogram" +fi + +if ! grep -q "synthetic_prio=prio" events/sched/sched_waking/hist; then + fail "Failed to create histogram with fully-qualified variable reference" +fi + +echo '!hist:keys=next_comm:wakeup_lat=common_timestamp.usecs-sched.sched_wakeup.$ts0:onmatch(sched.sched_waking).wakeup_latency($wakeup_lat,next_pid,sched.sched_waking.prio,next_comm) if next_comm=="ping"' >> events/sched/sched_switch/trigger + +if grep -q "synthetic_prio=prio" events/sched/sched_waking/hist; then + fail "Failed to remove histogram with fully-qualified variable reference" +fi + +exit 0 diff --git a/tools/testing/selftests/ipc/Makefile b/tools/testing/selftests/ipc/Makefile index 50e9c299fc4a..fad10f2bb57b 100644 --- a/tools/testing/selftests/ipc/Makefile +++ b/tools/testing/selftests/ipc/Makefile @@ -1,12 +1,12 @@ # SPDX-License-Identifier: GPL-2.0 -uname_M := $(shell uname -m 2>/dev/null || echo not) -ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/) +ARCH ?= $(shell uname -m 2>/dev/null || echo not) +override ARCH := $(shell echo $(ARCH) | sed -e s/i.86/i386/) ifeq ($(ARCH),i386) - ARCH := x86 + override ARCH := x86 CFLAGS := -DCONFIG_X86_32 -D__i386__ endif ifeq ($(ARCH),x86_64) - ARCH := x86 + override ARCH := x86 CFLAGS := -DCONFIG_X86_64 -D__x86_64__ endif diff --git a/tools/testing/selftests/ipc/msgque.c b/tools/testing/selftests/ipc/msgque.c index e107379d185c..82f73cdae120 100644 --- a/tools/testing/selftests/ipc/msgque.c +++ b/tools/testing/selftests/ipc/msgque.c @@ -161,6 +161,9 @@ int dump_queue(struct msgque_data *msgque) ret = msgrcv(msgque->msq_id, &msgque->messages[i].mtype, MAX_MSG_SIZE, i, IPC_NOWAIT | MSG_COPY); if (ret < 0) { + if (errno == ENOSYS) + ksft_exit_skip("MSG_COPY not supported\n"); + ksft_test_result_fail("Failed to copy IPC message: %m (%d)\n", errno); return -errno; } diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h index 6d809f08ab7b..60838b61a2da 100644 --- a/tools/testing/selftests/kselftest.h +++ b/tools/testing/selftests/kselftest.h @@ -450,7 +450,7 @@ static inline __noreturn __printf(1, 2) void ksft_exit_skip(const char *msg, ... */ if (ksft_plan || ksft_test_num()) { ksft_cnt.ksft_xskip++; - printf("ok %u # SKIP ", 1 + ksft_test_num()); + printf("ok %u # SKIP ", ksft_test_num()); } else { printf("1..0 # SKIP "); } diff --git a/tools/testing/selftests/kselftest/runner.sh b/tools/testing/selftests/kselftest/runner.sh index 6da3390825fe..311811dc55a0 100644 --- a/tools/testing/selftests/kselftest/runner.sh +++ b/tools/testing/selftests/kselftest/runner.sh @@ -1,8 +1,17 @@ -#!/bin/bash +#!/bin/sh # SPDX-License-Identifier: GPL-2.0 # # Runs a set of tests in a given subdirectory. -. $(dirname "$(readlink -e "${BASH_SOURCE[0]}")")/ktap_helpers.sh + +# There isn't a shell-agnostic way to find the path of a sourced file, +# so we must rely on BASE_DIR being set to find other tools. +if [ -z "$BASE_DIR" ]; then + echo "Error: BASE_DIR must be set before sourcing." >&2 + exit 1 +fi + +. ${BASE_DIR}/kselftest/ktap_helpers.sh + export timeout_rc=124 export logfile=/dev/stdout export per_test_logging= @@ -14,13 +23,6 @@ export RUN_IN_NETNS= # over our soft timeout limit. export kselftest_default_timeout=45 -# There isn't a shell-agnostic way to find the path of a sourced file, -# so we must rely on BASE_DIR being set to find other tools. -if [ -z "$BASE_DIR" ]; then - echo "Error: BASE_DIR must be set before sourcing." >&2 - exit 1 -fi - TR_CMD=$(command -v tr) # If Perl is unavailable, we must fall back to line-at-a-time prefixing @@ -49,7 +51,6 @@ run_one() { DIR="$1" TEST="$2" - local rc test_num="$3" BASENAME_TEST=$(basename $TEST) @@ -106,7 +107,7 @@ run_one() echo "# $TEST_HDR_MSG" if [ ! -e "$TEST" ]; then ktap_print_msg "Warning: file $TEST is missing!" - ktap_test_fail "$test_num $TEST_HDR_MSG" + ktap_test_fail "$TEST_HDR_MSG" rc=$KSFT_FAIL else if [ -x /usr/bin/stdbuf ]; then @@ -125,7 +126,7 @@ run_one() interpreter=$(head -n 1 "$TEST" | cut -c 3-) cmd="$stdbuf $interpreter ./$BASENAME_TEST" else - ktap_test_fail "$test_num $TEST_HDR_MSG" + ktap_test_fail "$TEST_HDR_MSG" return $KSFT_FAIL fi fi @@ -136,15 +137,15 @@ run_one() rc=$? case "$rc" in "$KSFT_PASS") - ktap_test_pass "$test_num $TEST_HDR_MSG";; + ktap_test_pass "$TEST_HDR_MSG";; "$KSFT_SKIP") - ktap_test_skip "$test_num $TEST_HDR_MSG";; + ktap_test_skip "$TEST_HDR_MSG";; "$KSFT_XFAIL") - ktap_test_xfail "$test_num $TEST_HDR_MSG";; + ktap_test_xfail "$TEST_HDR_MSG";; "$timeout_rc") - ktap_test_fail "$test_num $TEST_HDR_MSG # TIMEOUT $kselftest_timeout seconds";; + ktap_test_fail "$TEST_HDR_MSG # TIMEOUT $kselftest_timeout seconds";; *) - ktap_test_fail "$test_num $TEST_HDR_MSG # exit=$rc";; + ktap_test_fail "$TEST_HDR_MSG # exit=$rc";; esac cd - >/dev/null fi @@ -159,7 +160,7 @@ in_netns() BASE_DIR=$BASE_DIR source $BASE_DIR/kselftest/runner.sh logfile=$logfile - run_one $DIR $TEST $test_num + run_one $DIR $TEST EOF } @@ -172,7 +173,7 @@ run_in_netns() ip netns add $netns if [ $? -ne 0 ]; then ktap_print_msg "Warning: Create namespace failed for $BASENAME_TEST" - ktap_test_fail "$test_num selftests: $DIR: $BASENAME_TEST # Create NS failed" + ktap_test_fail "selftests: $DIR: $BASENAME_TEST # Create NS failed" fi ip -n $netns link set lo up @@ -189,28 +190,26 @@ run_in_netns() run_many() { DIR="${PWD#${BASE_DIR}/}" - test_num=0 local rc - pids=() + pids= for TEST in "$@"; do BASENAME_TEST=$(basename $TEST) - test_num=$(( test_num + 1 )) if [ -n "$per_test_logging" ]; then logfile="$per_test_log_dir/$BASENAME_TEST" cat /dev/null > "$logfile" fi if [ -n "$RUN_IN_NETNS" ]; then run_in_netns & - pids+=($!) + pids="$pids $!" else - run_one "$DIR" "$TEST" "$test_num" + run_one "$DIR" "$TEST" fi done # These variables are outputs of ktap_helpers.sh but since we've # run the test in a subprocess we need to update them manually - for pid in "${pids[@]}"; do + for pid in $pids; do wait "$pid" rc=$? case "$rc" in diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h index 75fb016cd190..cfdce9cd252e 100644 --- a/tools/testing/selftests/kselftest_harness.h +++ b/tools/testing/selftests/kselftest_harness.h @@ -76,7 +76,7 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n) memset(s, c, n); } -#define KSELFTEST_PRIO_TEST_F 20000 +#define KSELFTEST_PRIO_TEST 20000 #define KSELFTEST_PRIO_XFAIL 20001 #define TEST_TIMEOUT_DEFAULT 30 @@ -194,7 +194,7 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n) .fixture = &_fixture_global, \ .termsig = _signal, \ .timeout = TEST_TIMEOUT_DEFAULT, }; \ - static void __attribute__((constructor)) _register_##test_name(void) \ + static void __attribute__((constructor(KSELFTEST_PRIO_TEST))) _register_##test_name(void) \ { \ __register_test(&_##test_name##_object); \ } \ @@ -238,7 +238,7 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n) FIXTURE_VARIANT(fixture_name); \ static struct __fixture_metadata _##fixture_name##_fixture_object = \ { .name = #fixture_name, }; \ - static void __attribute__((constructor)) \ + static void __attribute__((constructor(KSELFTEST_PRIO_TEST))) \ _register_##fixture_name##_data(void) \ { \ __register_fixture(&_##fixture_name##_fixture_object); \ @@ -364,7 +364,7 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n) _##fixture_name##_##variant_name##_object = \ { .name = #variant_name, \ .data = &_##fixture_name##_##variant_name##_variant}; \ - static void __attribute__((constructor)) \ + static void __attribute__((constructor(KSELFTEST_PRIO_TEST))) \ _register_##fixture_name##_##variant_name(void) \ { \ __register_fixture_variant(&_##fixture_name##_fixture_object, \ @@ -468,7 +468,7 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n) fixture_name##_teardown(_metadata, self, variant); \ } \ static struct __test_metadata *_##fixture_name##_##test_name##_object; \ - static void __attribute__((constructor(KSELFTEST_PRIO_TEST_F))) \ + static void __attribute__((constructor(KSELFTEST_PRIO_TEST))) \ _register_##fixture_name##_##test_name(void) \ { \ struct __test_metadata *object = mmap(NULL, sizeof(*object), \ @@ -1323,7 +1323,7 @@ static int test_harness_run(int argc, char **argv) return KSFT_FAIL; } -static void __attribute__((constructor)) __constructor_order_first(void) +static void __attribute__((constructor(KSELFTEST_PRIO_TEST))) __constructor_order_first(void) { __constructor_order_forward = true; } diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 6471fa214a9f..9118a5a51b89 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -64,6 +64,8 @@ TEST_GEN_PROGS_COMMON += kvm_binary_stats_test TEST_GEN_PROGS_COMMON += kvm_create_max_vcpus TEST_GEN_PROGS_COMMON += kvm_page_table_test TEST_GEN_PROGS_COMMON += set_memory_region_test +TEST_GEN_PROGS_COMMON += memslot_modification_stress_test +TEST_GEN_PROGS_COMMON += memslot_perf_test # Compiled test targets TEST_GEN_PROGS_x86 = $(TEST_GEN_PROGS_COMMON) @@ -111,8 +113,11 @@ TEST_GEN_PROGS_x86 += x86/state_test TEST_GEN_PROGS_x86 += x86/vmx_preemption_timer_test TEST_GEN_PROGS_x86 += x86/svm_vmcall_test TEST_GEN_PROGS_x86 += x86/svm_int_ctl_test +TEST_GEN_PROGS_x86 += x86/svm_nested_clear_efer_svme TEST_GEN_PROGS_x86 += x86/svm_nested_shutdown_test TEST_GEN_PROGS_x86 += x86/svm_nested_soft_inject_test +TEST_GEN_PROGS_x86 += x86/svm_nested_vmcb12_gpa +TEST_GEN_PROGS_x86 += x86/svm_lbr_nested_state TEST_GEN_PROGS_x86 += x86/tsc_scaling_sync TEST_GEN_PROGS_x86 += x86/sync_regs_test TEST_GEN_PROGS_x86 += x86/ucna_injection_test @@ -148,8 +153,6 @@ TEST_GEN_PROGS_x86 += coalesced_io_test TEST_GEN_PROGS_x86 += dirty_log_perf_test TEST_GEN_PROGS_x86 += guest_memfd_test TEST_GEN_PROGS_x86 += hardware_disable_test -TEST_GEN_PROGS_x86 += memslot_modification_stress_test -TEST_GEN_PROGS_x86 += memslot_perf_test TEST_GEN_PROGS_x86 += mmu_stress_test TEST_GEN_PROGS_x86 += rseq_test TEST_GEN_PROGS_x86 += steal_time @@ -177,8 +180,9 @@ TEST_GEN_PROGS_arm64 += arm64/vcpu_width_config TEST_GEN_PROGS_arm64 += arm64/vgic_init TEST_GEN_PROGS_arm64 += arm64/vgic_irq TEST_GEN_PROGS_arm64 += arm64/vgic_lpi_stress +TEST_GEN_PROGS_arm64 += arm64/vgic_v5 TEST_GEN_PROGS_arm64 += arm64/vpmu_counter_access -TEST_GEN_PROGS_arm64 += arm64/no-vgic-v3 +TEST_GEN_PROGS_arm64 += arm64/no-vgic TEST_GEN_PROGS_arm64 += arm64/idreg-idst TEST_GEN_PROGS_arm64 += arm64/kvm-uuid TEST_GEN_PROGS_arm64 += access_tracking_perf_test @@ -187,8 +191,6 @@ TEST_GEN_PROGS_arm64 += coalesced_io_test TEST_GEN_PROGS_arm64 += dirty_log_perf_test TEST_GEN_PROGS_arm64 += get-reg-list TEST_GEN_PROGS_arm64 += guest_memfd_test -TEST_GEN_PROGS_arm64 += memslot_modification_stress_test -TEST_GEN_PROGS_arm64 += memslot_perf_test TEST_GEN_PROGS_arm64 += mmu_stress_test TEST_GEN_PROGS_arm64 += rseq_test TEST_GEN_PROGS_arm64 += steal_time @@ -207,6 +209,7 @@ TEST_GEN_PROGS_s390 += s390/user_operexec TEST_GEN_PROGS_s390 += s390/keyop TEST_GEN_PROGS_s390 += rseq_test TEST_GEN_PROGS_s390 += s390/irq_routing +TEST_GEN_PROGS_s390 += mmu_stress_test TEST_GEN_PROGS_riscv = $(TEST_GEN_PROGS_COMMON) TEST_GEN_PROGS_riscv += riscv/sbi_pmu_test @@ -216,13 +219,12 @@ TEST_GEN_PROGS_riscv += arch_timer TEST_GEN_PROGS_riscv += coalesced_io_test TEST_GEN_PROGS_riscv += dirty_log_perf_test TEST_GEN_PROGS_riscv += get-reg-list -TEST_GEN_PROGS_riscv += memslot_modification_stress_test -TEST_GEN_PROGS_riscv += memslot_perf_test TEST_GEN_PROGS_riscv += mmu_stress_test TEST_GEN_PROGS_riscv += rseq_test TEST_GEN_PROGS_riscv += steal_time -TEST_GEN_PROGS_loongarch = arch_timer +TEST_GEN_PROGS_loongarch = loongarch/pmu_test +TEST_GEN_PROGS_loongarch += arch_timer TEST_GEN_PROGS_loongarch += coalesced_io_test TEST_GEN_PROGS_loongarch += demand_paging_test TEST_GEN_PROGS_loongarch += dirty_log_perf_test diff --git a/tools/testing/selftests/kvm/access_tracking_perf_test.c b/tools/testing/selftests/kvm/access_tracking_perf_test.c index b058f27b2141..e5bbdb5bbdc3 100644 --- a/tools/testing/selftests/kvm/access_tracking_perf_test.c +++ b/tools/testing/selftests/kvm/access_tracking_perf_test.c @@ -101,15 +101,15 @@ struct test_params { enum vm_mem_backing_src_type backing_src; /* The amount of memory to allocate for each vCPU. */ - uint64_t vcpu_memory_bytes; + u64 vcpu_memory_bytes; /* The number of vCPUs to create in the VM. */ int nr_vcpus; }; -static uint64_t pread_uint64(int fd, const char *filename, uint64_t index) +static u64 pread_u64(int fd, const char *filename, u64 index) { - uint64_t value; + u64 value; off_t offset = index * sizeof(value); TEST_ASSERT(pread(fd, &value, sizeof(value), offset) == sizeof(value), @@ -123,13 +123,13 @@ static uint64_t pread_uint64(int fd, const char *filename, uint64_t index) #define PAGEMAP_PRESENT (1ULL << 63) #define PAGEMAP_PFN_MASK ((1ULL << 55) - 1) -static uint64_t lookup_pfn(int pagemap_fd, struct kvm_vm *vm, uint64_t gva) +static u64 lookup_pfn(int pagemap_fd, struct kvm_vm *vm, gva_t gva) { - uint64_t hva = (uint64_t) addr_gva2hva(vm, gva); - uint64_t entry; - uint64_t pfn; + u64 hva = (u64)addr_gva2hva(vm, gva); + u64 entry; + u64 pfn; - entry = pread_uint64(pagemap_fd, "pagemap", hva / getpagesize()); + entry = pread_u64(pagemap_fd, "pagemap", hva / getpagesize()); if (!(entry & PAGEMAP_PRESENT)) return 0; @@ -139,16 +139,16 @@ static uint64_t lookup_pfn(int pagemap_fd, struct kvm_vm *vm, uint64_t gva) return pfn; } -static bool is_page_idle(int page_idle_fd, uint64_t pfn) +static bool is_page_idle(int page_idle_fd, u64 pfn) { - uint64_t bits = pread_uint64(page_idle_fd, "page_idle", pfn / 64); + u64 bits = pread_u64(page_idle_fd, "page_idle", pfn / 64); return !!((bits >> (pfn % 64)) & 1); } -static void mark_page_idle(int page_idle_fd, uint64_t pfn) +static void mark_page_idle(int page_idle_fd, u64 pfn) { - uint64_t bits = 1ULL << (pfn % 64); + u64 bits = 1ULL << (pfn % 64); TEST_ASSERT(pwrite(page_idle_fd, &bits, 8, 8 * (pfn / 64)) == 8, "Set page_idle bits for PFN 0x%" PRIx64, pfn); @@ -174,11 +174,11 @@ static void pageidle_mark_vcpu_memory_idle(struct kvm_vm *vm, struct memstress_vcpu_args *vcpu_args) { int vcpu_idx = vcpu_args->vcpu_idx; - uint64_t base_gva = vcpu_args->gva; - uint64_t pages = vcpu_args->pages; - uint64_t page; - uint64_t still_idle = 0; - uint64_t no_pfn = 0; + gva_t base_gva = vcpu_args->gva; + u64 pages = vcpu_args->pages; + u64 page; + u64 still_idle = 0; + u64 no_pfn = 0; int page_idle_fd; int pagemap_fd; @@ -193,8 +193,8 @@ static void pageidle_mark_vcpu_memory_idle(struct kvm_vm *vm, TEST_ASSERT(pagemap_fd > 0, "Failed to open pagemap."); for (page = 0; page < pages; page++) { - uint64_t gva = base_gva + page * memstress_args.guest_page_size; - uint64_t pfn = lookup_pfn(pagemap_fd, vm, gva); + gva_t gva = base_gva + page * memstress_args.guest_page_size; + u64 pfn = lookup_pfn(pagemap_fd, vm, gva); if (!pfn) { no_pfn++; @@ -297,10 +297,10 @@ static void lru_gen_mark_memory_idle(struct kvm_vm *vm) lru_gen_last_gen = new_gen; } -static void assert_ucall(struct kvm_vcpu *vcpu, uint64_t expected_ucall) +static void assert_ucall(struct kvm_vcpu *vcpu, u64 expected_ucall) { struct ucall uc; - uint64_t actual_ucall = get_ucall(vcpu, &uc); + u64 actual_ucall = get_ucall(vcpu, &uc); TEST_ASSERT(expected_ucall == actual_ucall, "Guest exited unexpectedly (expected ucall %" PRIu64 @@ -417,7 +417,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) */ test_pages = params->nr_vcpus * params->vcpu_memory_bytes / max(memstress_args.guest_page_size, - (uint64_t)getpagesize()); + (u64)getpagesize()); memstress_start_vcpu_threads(nr_vcpus, vcpu_thread_main); diff --git a/tools/testing/selftests/kvm/arch_timer.c b/tools/testing/selftests/kvm/arch_timer.c index cf8fb67104f1..90c475a61b22 100644 --- a/tools/testing/selftests/kvm/arch_timer.c +++ b/tools/testing/selftests/kvm/arch_timer.c @@ -78,9 +78,9 @@ static void *test_vcpu_run(void *arg) return NULL; } -static uint32_t test_get_pcpu(void) +static u32 test_get_pcpu(void) { - uint32_t pcpu; + u32 pcpu; unsigned int nproc_conf; cpu_set_t online_cpuset; @@ -98,7 +98,7 @@ static uint32_t test_get_pcpu(void) static int test_migrate_vcpu(unsigned int vcpu_idx) { int ret; - uint32_t new_pcpu = test_get_pcpu(); + u32 new_pcpu = test_get_pcpu(); pr_debug("Migrating vCPU: %u to pCPU: %u\n", vcpu_idx, new_pcpu); diff --git a/tools/testing/selftests/kvm/arm64/aarch32_id_regs.c b/tools/testing/selftests/kvm/arm64/aarch32_id_regs.c index 713005b6f508..8a019cbaf4c4 100644 --- a/tools/testing/selftests/kvm/arm64/aarch32_id_regs.c +++ b/tools/testing/selftests/kvm/arm64/aarch32_id_regs.c @@ -66,7 +66,7 @@ static void test_guest_raz(struct kvm_vcpu *vcpu) } } -static uint64_t raz_wi_reg_ids[] = { +static u64 raz_wi_reg_ids[] = { KVM_ARM64_SYS_REG(SYS_ID_PFR0_EL1), KVM_ARM64_SYS_REG(SYS_ID_PFR1_EL1), KVM_ARM64_SYS_REG(SYS_ID_DFR0_EL1), @@ -94,8 +94,8 @@ static void test_user_raz_wi(struct kvm_vcpu *vcpu) int i; for (i = 0; i < ARRAY_SIZE(raz_wi_reg_ids); i++) { - uint64_t reg_id = raz_wi_reg_ids[i]; - uint64_t val; + u64 reg_id = raz_wi_reg_ids[i]; + u64 val; val = vcpu_get_reg(vcpu, reg_id); TEST_ASSERT_EQ(val, 0); @@ -111,7 +111,7 @@ static void test_user_raz_wi(struct kvm_vcpu *vcpu) } } -static uint64_t raz_invariant_reg_ids[] = { +static u64 raz_invariant_reg_ids[] = { KVM_ARM64_SYS_REG(SYS_ID_AFR0_EL1), KVM_ARM64_SYS_REG(sys_reg(3, 0, 0, 3, 3)), KVM_ARM64_SYS_REG(SYS_ID_DFR1_EL1), @@ -123,8 +123,8 @@ static void test_user_raz_invariant(struct kvm_vcpu *vcpu) int i, r; for (i = 0; i < ARRAY_SIZE(raz_invariant_reg_ids); i++) { - uint64_t reg_id = raz_invariant_reg_ids[i]; - uint64_t val; + u64 reg_id = raz_invariant_reg_ids[i]; + u64 val; val = vcpu_get_reg(vcpu, reg_id); TEST_ASSERT_EQ(val, 0); @@ -142,7 +142,7 @@ static void test_user_raz_invariant(struct kvm_vcpu *vcpu) static bool vcpu_aarch64_only(struct kvm_vcpu *vcpu) { - uint64_t val, el0; + u64 val, el0; val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1)); diff --git a/tools/testing/selftests/kvm/arm64/arch_timer.c b/tools/testing/selftests/kvm/arm64/arch_timer.c index d592a4515399..5fa5c0ec2b3e 100644 --- a/tools/testing/selftests/kvm/arm64/arch_timer.c +++ b/tools/testing/selftests/kvm/arm64/arch_timer.c @@ -56,7 +56,7 @@ static void guest_validate_irq(unsigned int intid, struct test_vcpu_shared_data *shared_data) { enum guest_stage stage = shared_data->guest_stage; - uint64_t xcnt = 0, xcnt_diff_us, cval = 0; + u64 xcnt = 0, xcnt_diff_us, cval = 0; unsigned long xctl = 0; unsigned int timer_irq = 0; unsigned int accessor; @@ -105,7 +105,7 @@ static void guest_validate_irq(unsigned int intid, static void guest_irq_handler(struct ex_regs *regs) { unsigned int intid = gic_get_and_ack_irq(); - uint32_t cpu = guest_get_vcpuid(); + u32 cpu = guest_get_vcpuid(); struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; guest_validate_irq(intid, shared_data); @@ -116,7 +116,7 @@ static void guest_irq_handler(struct ex_regs *regs) static void guest_run_stage(struct test_vcpu_shared_data *shared_data, enum guest_stage stage) { - uint32_t irq_iter, config_iter; + u32 irq_iter, config_iter; shared_data->guest_stage = stage; shared_data->nr_iter = 0; @@ -140,7 +140,7 @@ static void guest_run_stage(struct test_vcpu_shared_data *shared_data, static void guest_code(void) { - uint32_t cpu = guest_get_vcpuid(); + u32 cpu = guest_get_vcpuid(); struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; local_irq_disable(); diff --git a/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c b/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c index 993c9e38e729..f7625eb711d6 100644 --- a/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c +++ b/tools/testing/selftests/kvm/arm64/arch_timer_edge_cases.c @@ -23,25 +23,25 @@ #include "vgic.h" /* Depends on counter width. */ -static uint64_t CVAL_MAX; +static u64 CVAL_MAX; /* tval is a signed 32-bit int. */ -static const int32_t TVAL_MAX = INT32_MAX; -static const int32_t TVAL_MIN = INT32_MIN; +static const s32 TVAL_MAX = INT32_MAX; +static const s32 TVAL_MIN = INT32_MIN; /* After how much time we say there is no IRQ. */ -static const uint32_t TIMEOUT_NO_IRQ_US = 50000; +static const u32 TIMEOUT_NO_IRQ_US = 50000; /* Counter value to use as the starting one for most tests. Set to CVAL_MAX/2 */ -static uint64_t DEF_CNT; +static u64 DEF_CNT; /* Number of runs. */ -static const uint32_t NR_TEST_ITERS_DEF = 5; +static const u32 NR_TEST_ITERS_DEF = 5; /* Default wait test time in ms. */ -static const uint32_t WAIT_TEST_MS = 10; +static const u32 WAIT_TEST_MS = 10; /* Default "long" wait test time in ms. */ -static const uint32_t LONG_WAIT_TEST_MS = 100; +static const u32 LONG_WAIT_TEST_MS = 100; /* Shared with IRQ handler. */ struct test_vcpu_shared_data { @@ -53,9 +53,9 @@ struct test_args { /* Virtual or physical timer and counter tests. */ enum arch_timer timer; /* Delay used for most timer tests. */ - uint64_t wait_ms; + u64 wait_ms; /* Delay used in the test_long_timer_delays test. */ - uint64_t long_wait_ms; + u64 long_wait_ms; /* Number of iterations. */ int iterations; /* Whether to test the physical timer. */ @@ -82,12 +82,12 @@ enum sync_cmd { NO_USERSPACE_CMD, }; -typedef void (*sleep_method_t)(enum arch_timer timer, uint64_t usec); +typedef void (*sleep_method_t)(enum arch_timer timer, u64 usec); -static void sleep_poll(enum arch_timer timer, uint64_t usec); -static void sleep_sched_poll(enum arch_timer timer, uint64_t usec); -static void sleep_in_userspace(enum arch_timer timer, uint64_t usec); -static void sleep_migrate(enum arch_timer timer, uint64_t usec); +static void sleep_poll(enum arch_timer timer, u64 usec); +static void sleep_sched_poll(enum arch_timer timer, u64 usec); +static void sleep_in_userspace(enum arch_timer timer, u64 usec); +static void sleep_migrate(enum arch_timer timer, u64 usec); sleep_method_t sleep_method[] = { sleep_poll, @@ -115,14 +115,14 @@ enum timer_view { TIMER_TVAL, }; -static void assert_irqs_handled(uint32_t n) +static void assert_irqs_handled(u32 n) { int h = atomic_read(&shared_data.handled); __GUEST_ASSERT(h == n, "Handled %d IRQS but expected %d", h, n); } -static void userspace_cmd(uint64_t cmd) +static void userspace_cmd(u64 cmd) { GUEST_SYNC_ARGS(cmd, 0, 0, 0, 0); } @@ -132,12 +132,12 @@ static void userspace_migrate_vcpu(void) userspace_cmd(USERSPACE_MIGRATE_SELF); } -static void userspace_sleep(uint64_t usecs) +static void userspace_sleep(u64 usecs) { GUEST_SYNC_ARGS(USERSPACE_USLEEP, usecs, 0, 0, 0); } -static void set_counter(enum arch_timer timer, uint64_t counter) +static void set_counter(enum arch_timer timer, u64 counter) { GUEST_SYNC_ARGS(SET_COUNTER_VALUE, counter, timer, 0, 0); } @@ -146,8 +146,8 @@ static void guest_irq_handler(struct ex_regs *regs) { unsigned int intid = gic_get_and_ack_irq(); enum arch_timer timer; - uint64_t cnt, cval; - uint32_t ctl; + u64 cnt, cval; + u32 ctl; bool timer_condition, istatus; if (intid == IAR_SPURIOUS) { @@ -178,8 +178,8 @@ out: gic_set_eoi(intid); } -static void set_cval_irq(enum arch_timer timer, uint64_t cval_cycles, - uint32_t ctl) +static void set_cval_irq(enum arch_timer timer, u64 cval_cycles, + u32 ctl) { atomic_set(&shared_data.handled, 0); atomic_set(&shared_data.spurious, 0); @@ -187,8 +187,8 @@ static void set_cval_irq(enum arch_timer timer, uint64_t cval_cycles, timer_set_ctl(timer, ctl); } -static void set_tval_irq(enum arch_timer timer, uint64_t tval_cycles, - uint32_t ctl) +static void set_tval_irq(enum arch_timer timer, u64 tval_cycles, + u32 ctl) { atomic_set(&shared_data.handled, 0); atomic_set(&shared_data.spurious, 0); @@ -196,7 +196,7 @@ static void set_tval_irq(enum arch_timer timer, uint64_t tval_cycles, timer_set_ctl(timer, ctl); } -static void set_xval_irq(enum arch_timer timer, uint64_t xval, uint32_t ctl, +static void set_xval_irq(enum arch_timer timer, u64 xval, u32 ctl, enum timer_view tv) { switch (tv) { @@ -275,13 +275,13 @@ static void wait_migrate_poll_for_irq(void) * Sleep for usec microseconds by polling in the guest or in * userspace (e.g. userspace_cmd=USERSPACE_SCHEDULE). */ -static void guest_poll(enum arch_timer test_timer, uint64_t usec, +static void guest_poll(enum arch_timer test_timer, u64 usec, enum sync_cmd usp_cmd) { - uint64_t cycles = usec_to_cycles(usec); + u64 cycles = usec_to_cycles(usec); /* Whichever timer we are testing with, sleep with the other. */ enum arch_timer sleep_timer = 1 - test_timer; - uint64_t start = timer_get_cntct(sleep_timer); + u64 start = timer_get_cntct(sleep_timer); while ((timer_get_cntct(sleep_timer) - start) < cycles) { if (usp_cmd == NO_USERSPACE_CMD) @@ -291,22 +291,22 @@ static void guest_poll(enum arch_timer test_timer, uint64_t usec, } } -static void sleep_poll(enum arch_timer timer, uint64_t usec) +static void sleep_poll(enum arch_timer timer, u64 usec) { guest_poll(timer, usec, NO_USERSPACE_CMD); } -static void sleep_sched_poll(enum arch_timer timer, uint64_t usec) +static void sleep_sched_poll(enum arch_timer timer, u64 usec) { guest_poll(timer, usec, USERSPACE_SCHED_YIELD); } -static void sleep_migrate(enum arch_timer timer, uint64_t usec) +static void sleep_migrate(enum arch_timer timer, u64 usec) { guest_poll(timer, usec, USERSPACE_MIGRATE_SELF); } -static void sleep_in_userspace(enum arch_timer timer, uint64_t usec) +static void sleep_in_userspace(enum arch_timer timer, u64 usec) { userspace_sleep(usec); } @@ -315,15 +315,15 @@ static void sleep_in_userspace(enum arch_timer timer, uint64_t usec) * Reset the timer state to some nice values like the counter not being close * to the edge, and the control register masked and disabled. */ -static void reset_timer_state(enum arch_timer timer, uint64_t cnt) +static void reset_timer_state(enum arch_timer timer, u64 cnt) { set_counter(timer, cnt); timer_set_ctl(timer, CTL_IMASK); } -static void test_timer_xval(enum arch_timer timer, uint64_t xval, +static void test_timer_xval(enum arch_timer timer, u64 xval, enum timer_view tv, irq_wait_method_t wm, bool reset_state, - uint64_t reset_cnt) + u64 reset_cnt) { local_irq_disable(); @@ -348,23 +348,23 @@ static void test_timer_xval(enum arch_timer timer, uint64_t xval, * the "runner", like: tools/testing/selftests/kselftest/runner.sh. */ -static void test_timer_cval(enum arch_timer timer, uint64_t cval, +static void test_timer_cval(enum arch_timer timer, u64 cval, irq_wait_method_t wm, bool reset_state, - uint64_t reset_cnt) + u64 reset_cnt) { test_timer_xval(timer, cval, TIMER_CVAL, wm, reset_state, reset_cnt); } -static void test_timer_tval(enum arch_timer timer, int32_t tval, +static void test_timer_tval(enum arch_timer timer, s32 tval, irq_wait_method_t wm, bool reset_state, - uint64_t reset_cnt) + u64 reset_cnt) { - test_timer_xval(timer, (uint64_t) tval, TIMER_TVAL, wm, reset_state, + test_timer_xval(timer, (u64)tval, TIMER_TVAL, wm, reset_state, reset_cnt); } -static void test_xval_check_no_irq(enum arch_timer timer, uint64_t xval, - uint64_t usec, enum timer_view timer_view, +static void test_xval_check_no_irq(enum arch_timer timer, u64 xval, + u64 usec, enum timer_view timer_view, sleep_method_t guest_sleep) { local_irq_disable(); @@ -379,17 +379,17 @@ static void test_xval_check_no_irq(enum arch_timer timer, uint64_t xval, assert_irqs_handled(0); } -static void test_cval_no_irq(enum arch_timer timer, uint64_t cval, - uint64_t usec, sleep_method_t wm) +static void test_cval_no_irq(enum arch_timer timer, u64 cval, + u64 usec, sleep_method_t wm) { test_xval_check_no_irq(timer, cval, usec, TIMER_CVAL, wm); } -static void test_tval_no_irq(enum arch_timer timer, int32_t tval, uint64_t usec, +static void test_tval_no_irq(enum arch_timer timer, s32 tval, u64 usec, sleep_method_t wm) { - /* tval will be cast to an int32_t in test_xval_check_no_irq */ - test_xval_check_no_irq(timer, (uint64_t) tval, usec, TIMER_TVAL, wm); + /* tval will be cast to an s32 in test_xval_check_no_irq */ + test_xval_check_no_irq(timer, (u64)tval, usec, TIMER_TVAL, wm); } /* Test masking/unmasking a timer using the timer mask (not the IRQ mask). */ @@ -463,7 +463,7 @@ static void test_timers_fired_multiple_times(enum arch_timer timer) * timeout for the wait: we use the wfi instruction. */ static void test_reprogramming_timer(enum arch_timer timer, irq_wait_method_t wm, - int32_t delta_1_ms, int32_t delta_2_ms) + s32 delta_1_ms, s32 delta_2_ms) { local_irq_disable(); reset_timer_state(timer, DEF_CNT); @@ -488,7 +488,7 @@ static void test_reprogramming_timer(enum arch_timer timer, irq_wait_method_t wm static void test_reprogram_timers(enum arch_timer timer) { int i; - uint64_t base_wait = test_args.wait_ms; + u64 base_wait = test_args.wait_ms; for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) { /* @@ -504,8 +504,8 @@ static void test_reprogram_timers(enum arch_timer timer) static void test_basic_functionality(enum arch_timer timer) { - int32_t tval = (int32_t) msec_to_cycles(test_args.wait_ms); - uint64_t cval = DEF_CNT + msec_to_cycles(test_args.wait_ms); + s32 tval = (s32)msec_to_cycles(test_args.wait_ms); + u64 cval = DEF_CNT + msec_to_cycles(test_args.wait_ms); int i; for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) { @@ -593,7 +593,7 @@ static void test_set_cnt_after_tval_max(enum arch_timer timer, irq_wait_method_t reset_timer_state(timer, DEF_CNT); set_cval_irq(timer, - (uint64_t) TVAL_MAX + + (u64)TVAL_MAX + msec_to_cycles(test_args.wait_ms) / 2, CTL_ENABLE); set_counter(timer, TVAL_MAX); @@ -608,7 +608,7 @@ static void test_set_cnt_after_tval_max(enum arch_timer timer, irq_wait_method_t /* Test timers set for: cval = now + TVAL_MAX + wait_ms / 2 */ static void test_timers_above_tval_max(enum arch_timer timer) { - uint64_t cval; + u64 cval; int i; /* @@ -638,8 +638,8 @@ static void test_timers_above_tval_max(enum arch_timer timer) * sets the counter to cnt_1, the [c|t]val, the counter to cnt_2, and * then waits for an IRQ. */ -static void test_set_cnt_after_xval(enum arch_timer timer, uint64_t cnt_1, - uint64_t xval, uint64_t cnt_2, +static void test_set_cnt_after_xval(enum arch_timer timer, u64 cnt_1, + u64 xval, u64 cnt_2, irq_wait_method_t wm, enum timer_view tv) { local_irq_disable(); @@ -662,8 +662,8 @@ static void test_set_cnt_after_xval(enum arch_timer timer, uint64_t cnt_1, * then waits for an IRQ. */ static void test_set_cnt_after_xval_no_irq(enum arch_timer timer, - uint64_t cnt_1, uint64_t xval, - uint64_t cnt_2, + u64 cnt_1, u64 xval, + u64 cnt_2, sleep_method_t guest_sleep, enum timer_view tv) { @@ -684,31 +684,31 @@ static void test_set_cnt_after_xval_no_irq(enum arch_timer timer, timer_set_ctl(timer, CTL_IMASK); } -static void test_set_cnt_after_tval(enum arch_timer timer, uint64_t cnt_1, - int32_t tval, uint64_t cnt_2, +static void test_set_cnt_after_tval(enum arch_timer timer, u64 cnt_1, + s32 tval, u64 cnt_2, irq_wait_method_t wm) { test_set_cnt_after_xval(timer, cnt_1, tval, cnt_2, wm, TIMER_TVAL); } -static void test_set_cnt_after_cval(enum arch_timer timer, uint64_t cnt_1, - uint64_t cval, uint64_t cnt_2, +static void test_set_cnt_after_cval(enum arch_timer timer, u64 cnt_1, + u64 cval, u64 cnt_2, irq_wait_method_t wm) { test_set_cnt_after_xval(timer, cnt_1, cval, cnt_2, wm, TIMER_CVAL); } static void test_set_cnt_after_tval_no_irq(enum arch_timer timer, - uint64_t cnt_1, int32_t tval, - uint64_t cnt_2, sleep_method_t wm) + u64 cnt_1, s32 tval, + u64 cnt_2, sleep_method_t wm) { test_set_cnt_after_xval_no_irq(timer, cnt_1, tval, cnt_2, wm, TIMER_TVAL); } static void test_set_cnt_after_cval_no_irq(enum arch_timer timer, - uint64_t cnt_1, uint64_t cval, - uint64_t cnt_2, sleep_method_t wm) + u64 cnt_1, u64 cval, + u64 cnt_2, sleep_method_t wm) { test_set_cnt_after_xval_no_irq(timer, cnt_1, cval, cnt_2, wm, TIMER_CVAL); @@ -718,7 +718,7 @@ static void test_set_cnt_after_cval_no_irq(enum arch_timer timer, static void test_move_counters_ahead_of_timers(enum arch_timer timer) { int i; - int32_t tval; + s32 tval; for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) { irq_wait_method_t wm = irq_wait_method[i]; @@ -730,8 +730,7 @@ static void test_move_counters_ahead_of_timers(enum arch_timer timer) test_set_cnt_after_tval(timer, 0, -1, DEF_CNT + 1, wm); test_set_cnt_after_tval(timer, 0, -1, TVAL_MAX, wm); tval = TVAL_MAX; - test_set_cnt_after_tval(timer, 0, tval, (uint64_t) tval + 1, - wm); + test_set_cnt_after_tval(timer, 0, tval, (u64)tval + 1, wm); } } @@ -754,8 +753,8 @@ static void test_move_counters_behind_timers(enum arch_timer timer) static void test_timers_in_the_past(enum arch_timer timer) { - int32_t tval = -1 * (int32_t) msec_to_cycles(test_args.wait_ms); - uint64_t cval; + s32 tval = -1 * (s32)msec_to_cycles(test_args.wait_ms); + u64 cval; int i; for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) { @@ -790,8 +789,8 @@ static void test_timers_in_the_past(enum arch_timer timer) static void test_long_timer_delays(enum arch_timer timer) { - int32_t tval = (int32_t) msec_to_cycles(test_args.long_wait_ms); - uint64_t cval = DEF_CNT + msec_to_cycles(test_args.long_wait_ms); + s32 tval = (s32)msec_to_cycles(test_args.long_wait_ms); + u64 cval = DEF_CNT + msec_to_cycles(test_args.long_wait_ms); int i; for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) { @@ -846,11 +845,11 @@ static void guest_code(enum arch_timer timer) static cpu_set_t default_cpuset; -static uint32_t next_pcpu(void) +static u32 next_pcpu(void) { - uint32_t max = get_nprocs(); - uint32_t cur = sched_getcpu(); - uint32_t next = cur; + u32 max = get_nprocs(); + u32 cur = sched_getcpu(); + u32 next = cur; cpu_set_t cpuset = default_cpuset; TEST_ASSERT(max > 1, "Need at least two physical cpus"); @@ -862,7 +861,7 @@ static uint32_t next_pcpu(void) return next; } -static void kvm_set_cntxct(struct kvm_vcpu *vcpu, uint64_t cnt, +static void kvm_set_cntxct(struct kvm_vcpu *vcpu, u64 cnt, enum arch_timer timer) { if (timer == PHYSICAL) @@ -874,7 +873,7 @@ static void kvm_set_cntxct(struct kvm_vcpu *vcpu, uint64_t cnt, static void handle_sync(struct kvm_vcpu *vcpu, struct ucall *uc) { enum sync_cmd cmd = uc->args[1]; - uint64_t val = uc->args[2]; + u64 val = uc->args[2]; enum arch_timer timer = uc->args[3]; switch (cmd) { @@ -1018,8 +1017,8 @@ static bool parse_args(int argc, char *argv[]) static void set_counter_defaults(void) { - const uint64_t MIN_ROLLOVER_SECS = 40ULL * 365 * 24 * 3600; - uint64_t freq = read_sysreg(CNTFRQ_EL0); + const u64 MIN_ROLLOVER_SECS = 40ULL * 365 * 24 * 3600; + u64 freq = read_sysreg(CNTFRQ_EL0); int width = ilog2(MIN_ROLLOVER_SECS * freq); width = clamp(width, 56, 64); diff --git a/tools/testing/selftests/kvm/arm64/at.c b/tools/testing/selftests/kvm/arm64/at.c index c8ee6f520734..ce5d312ef6ba 100644 --- a/tools/testing/selftests/kvm/arm64/at.c +++ b/tools/testing/selftests/kvm/arm64/at.c @@ -13,7 +13,6 @@ enum { CLEAR_ACCESS_FLAG, - TEST_ACCESS_FLAG, }; static u64 *ptep_hva; @@ -49,7 +48,6 @@ do { \ GUEST_ASSERT_EQ(FIELD_GET(SYS_PAR_EL1_ATTR, par), MAIR_ATTR_NORMAL); \ GUEST_ASSERT_EQ(FIELD_GET(SYS_PAR_EL1_SH, par), PTE_SHARED >> 8); \ GUEST_ASSERT_EQ(par & SYS_PAR_EL1_PA, TEST_ADDR); \ - GUEST_SYNC(TEST_ACCESS_FLAG); \ } \ } while (0) @@ -85,10 +83,6 @@ static void guest_code(void) if (!SYS_FIELD_GET(ID_AA64MMFR1_EL1, HAFDBS, read_sysreg(id_aa64mmfr1_el1))) GUEST_DONE(); - /* - * KVM's software PTW makes the implementation choice that the AT - * instruction sets the access flag. - */ sysreg_clear_set(tcr_el1, 0, TCR_HA); isb(); test_at(false); @@ -102,8 +96,8 @@ static void handle_sync(struct kvm_vcpu *vcpu, struct ucall *uc) case CLEAR_ACCESS_FLAG: /* * Delete + reinstall the memslot to invalidate stage-2 - * mappings of the stage-1 page tables, forcing KVM to - * use the 'slow' AT emulation path. + * mappings of the stage-1 page tables, allowing KVM to + * potentially use the 'slow' AT emulation path. * * This and clearing the access flag from host userspace * ensures that the access flag cannot be set speculatively @@ -112,10 +106,6 @@ static void handle_sync(struct kvm_vcpu *vcpu, struct ucall *uc) clear_bit(__ffs(PTE_AF), ptep_hva); vm_mem_region_reload(vcpu->vm, vcpu->vm->memslots[MEM_REGION_PT]); break; - case TEST_ACCESS_FLAG: - TEST_ASSERT(test_bit(__ffs(PTE_AF), ptep_hva), - "Expected access flag to be set (desc: %lu)", *ptep_hva); - break; default: TEST_FAIL("Unexpected SYNC arg: %lu", uc->args[1]); } diff --git a/tools/testing/selftests/kvm/arm64/debug-exceptions.c b/tools/testing/selftests/kvm/arm64/debug-exceptions.c index 1d431de8729c..3eb4b1b6682d 100644 --- a/tools/testing/selftests/kvm/arm64/debug-exceptions.c +++ b/tools/testing/selftests/kvm/arm64/debug-exceptions.c @@ -31,14 +31,14 @@ extern unsigned char sw_bp, sw_bp2, hw_bp, hw_bp2, bp_svc, bp_brk, hw_wp, ss_start, hw_bp_ctx; extern unsigned char iter_ss_begin, iter_ss_end; -static volatile uint64_t sw_bp_addr, hw_bp_addr; -static volatile uint64_t wp_addr, wp_data_addr; -static volatile uint64_t svc_addr; -static volatile uint64_t ss_addr[4], ss_idx; -#define PC(v) ((uint64_t)&(v)) +static volatile u64 sw_bp_addr, hw_bp_addr; +static volatile u64 wp_addr, wp_data_addr; +static volatile u64 svc_addr; +static volatile u64 ss_addr[4], ss_idx; +#define PC(v) ((u64)&(v)) #define GEN_DEBUG_WRITE_REG(reg_name) \ -static void write_##reg_name(int num, uint64_t val) \ +static void write_##reg_name(int num, u64 val) \ { \ switch (num) { \ case 0: \ @@ -102,8 +102,8 @@ GEN_DEBUG_WRITE_REG(dbgwvr) static void reset_debug_state(void) { - uint8_t brps, wrps, i; - uint64_t dfr0; + u8 brps, wrps, i; + u64 dfr0; asm volatile("msr daifset, #8"); @@ -140,7 +140,7 @@ static void enable_os_lock(void) static void enable_monitor_debug_exceptions(void) { - uint64_t mdscr; + u64 mdscr; asm volatile("msr daifclr, #8"); @@ -149,9 +149,9 @@ static void enable_monitor_debug_exceptions(void) isb(); } -static void install_wp(uint8_t wpn, uint64_t addr) +static void install_wp(u8 wpn, u64 addr) { - uint32_t wcr; + u32 wcr; wcr = DBGWCR_LEN8 | DBGWCR_RD | DBGWCR_WR | DBGWCR_EL1 | DBGWCR_E; write_dbgwcr(wpn, wcr); @@ -162,9 +162,9 @@ static void install_wp(uint8_t wpn, uint64_t addr) enable_monitor_debug_exceptions(); } -static void install_hw_bp(uint8_t bpn, uint64_t addr) +static void install_hw_bp(u8 bpn, u64 addr) { - uint32_t bcr; + u32 bcr; bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E; write_dbgbcr(bpn, bcr); @@ -174,11 +174,10 @@ static void install_hw_bp(uint8_t bpn, uint64_t addr) enable_monitor_debug_exceptions(); } -static void install_wp_ctx(uint8_t addr_wp, uint8_t ctx_bp, uint64_t addr, - uint64_t ctx) +static void install_wp_ctx(u8 addr_wp, u8 ctx_bp, u64 addr, u64 ctx) { - uint32_t wcr; - uint64_t ctx_bcr; + u32 wcr; + u64 ctx_bcr; /* Setup a context-aware breakpoint for Linked Context ID Match */ ctx_bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E | @@ -188,7 +187,7 @@ static void install_wp_ctx(uint8_t addr_wp, uint8_t ctx_bp, uint64_t addr, /* Setup a linked watchpoint (linked to the context-aware breakpoint) */ wcr = DBGWCR_LEN8 | DBGWCR_RD | DBGWCR_WR | DBGWCR_EL1 | DBGWCR_E | - DBGWCR_WT_LINK | ((uint32_t)ctx_bp << DBGWCR_LBN_SHIFT); + DBGWCR_WT_LINK | ((u32)ctx_bp << DBGWCR_LBN_SHIFT); write_dbgwcr(addr_wp, wcr); write_dbgwvr(addr_wp, addr); isb(); @@ -196,10 +195,9 @@ static void install_wp_ctx(uint8_t addr_wp, uint8_t ctx_bp, uint64_t addr, enable_monitor_debug_exceptions(); } -void install_hw_bp_ctx(uint8_t addr_bp, uint8_t ctx_bp, uint64_t addr, - uint64_t ctx) +void install_hw_bp_ctx(u8 addr_bp, u8 ctx_bp, u64 addr, u64 ctx) { - uint32_t addr_bcr, ctx_bcr; + u32 addr_bcr, ctx_bcr; /* Setup a context-aware breakpoint for Linked Context ID Match */ ctx_bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E | @@ -213,7 +211,7 @@ void install_hw_bp_ctx(uint8_t addr_bp, uint8_t ctx_bp, uint64_t addr, */ addr_bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E | DBGBCR_BT_ADDR_LINK_CTX | - ((uint32_t)ctx_bp << DBGBCR_LBN_SHIFT); + ((u32)ctx_bp << DBGBCR_LBN_SHIFT); write_dbgbcr(addr_bp, addr_bcr); write_dbgbvr(addr_bp, addr); isb(); @@ -223,7 +221,7 @@ void install_hw_bp_ctx(uint8_t addr_bp, uint8_t ctx_bp, uint64_t addr, static void install_ss(void) { - uint64_t mdscr; + u64 mdscr; asm volatile("msr daifclr, #8"); @@ -234,9 +232,9 @@ static void install_ss(void) static volatile char write_data; -static void guest_code(uint8_t bpn, uint8_t wpn, uint8_t ctx_bpn) +static void guest_code(u8 bpn, u8 wpn, u8 ctx_bpn) { - uint64_t ctx = 0xabcdef; /* a random context number */ + u64 ctx = 0xabcdef; /* a random context number */ /* Software-breakpoint */ reset_debug_state(); @@ -377,8 +375,8 @@ static void guest_svc_handler(struct ex_regs *regs) static void guest_code_ss(int test_cnt) { - uint64_t i; - uint64_t bvr, wvr, w_bvr, w_wvr; + u64 i; + u64 bvr, wvr, w_bvr, w_wvr; for (i = 0; i < test_cnt; i++) { /* Bits [1:0] of dbg{b,w}vr are RES0 */ @@ -416,12 +414,12 @@ static void guest_code_ss(int test_cnt) GUEST_DONE(); } -static int debug_version(uint64_t id_aa64dfr0) +static int debug_version(u64 id_aa64dfr0) { return FIELD_GET(ID_AA64DFR0_EL1_DebugVer, id_aa64dfr0); } -static void test_guest_debug_exceptions(uint8_t bpn, uint8_t wpn, uint8_t ctx_bpn) +static void test_guest_debug_exceptions(u8 bpn, u8 wpn, u8 ctx_bpn) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; @@ -468,8 +466,8 @@ void test_single_step_from_userspace(int test_cnt) struct kvm_vm *vm; struct ucall uc; struct kvm_run *run; - uint64_t pc, cmd; - uint64_t test_pc = 0; + u64 pc, cmd; + u64 test_pc = 0; bool ss_enable = false; struct kvm_guest_debug debug = {}; @@ -506,7 +504,7 @@ void test_single_step_from_userspace(int test_cnt) "Unexpected pc 0x%lx (expected 0x%lx)", pc, test_pc); - if ((pc + 4) == (uint64_t)&iter_ss_end) { + if ((pc + 4) == (u64)&iter_ss_end) { test_pc = 0; debug.control = KVM_GUESTDBG_ENABLE; ss_enable = false; @@ -519,8 +517,8 @@ void test_single_step_from_userspace(int test_cnt) * iter_ss_end, the pc for the next KVM_EXIT_DEBUG should * be the current pc + 4. */ - if ((pc >= (uint64_t)&iter_ss_begin) && - (pc < (uint64_t)&iter_ss_end)) + if ((pc >= (u64)&iter_ss_begin) && + (pc < (u64)&iter_ss_end)) test_pc = pc + 4; else test_pc = 0; @@ -533,9 +531,9 @@ void test_single_step_from_userspace(int test_cnt) * Run debug testing using the various breakpoint#, watchpoint# and * context-aware breakpoint# with the given ID_AA64DFR0_EL1 configuration. */ -void test_guest_debug_exceptions_all(uint64_t aa64dfr0) +void test_guest_debug_exceptions_all(u64 aa64dfr0) { - uint8_t brp_num, wrp_num, ctx_brp_num, normal_brp_num, ctx_brp_base; + u8 brp_num, wrp_num, ctx_brp_num, normal_brp_num, ctx_brp_base; int b, w, c; /* Number of breakpoints */ @@ -580,7 +578,7 @@ int main(int argc, char *argv[]) struct kvm_vm *vm; int opt; int ss_iteration = 10000; - uint64_t aa64dfr0; + u64 aa64dfr0; vm = vm_create_with_one_vcpu(&vcpu, guest_code); aa64dfr0 = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64DFR0_EL1)); diff --git a/tools/testing/selftests/kvm/arm64/hypercalls.c b/tools/testing/selftests/kvm/arm64/hypercalls.c index bf038a0371f4..5d96cdf382c4 100644 --- a/tools/testing/selftests/kvm/arm64/hypercalls.c +++ b/tools/testing/selftests/kvm/arm64/hypercalls.c @@ -29,9 +29,9 @@ #define KVM_REG_ARM_VENDOR_HYP_BMAP_2_RESET_VAL 0 struct kvm_fw_reg_info { - uint64_t reg; /* Register definition */ - uint64_t max_feat_bit; /* Bit that represents the upper limit of the feature-map */ - uint64_t reset_val; /* Reset value for the register */ + u64 reg; /* Register definition */ + u64 max_feat_bit; /* Bit that represents the upper limit of the feature-map */ + u64 reset_val; /* Reset value for the register */ }; #define FW_REG_INFO(r) \ @@ -59,8 +59,8 @@ enum test_stage { static int stage = TEST_STAGE_REG_IFACE; struct test_hvc_info { - uint32_t func_id; - uint64_t arg1; + u32 func_id; + u64 arg1; }; #define TEST_HVC_INFO(f, a1) \ @@ -152,9 +152,9 @@ static void guest_code(void) } struct st_time { - uint32_t rev; - uint32_t attr; - uint64_t st_time; + u32 rev; + u32 attr; + u64 st_time; }; #define STEAL_TIME_SIZE ((sizeof(struct st_time) + 63) & ~63) @@ -162,7 +162,7 @@ struct st_time { static void steal_time_init(struct kvm_vcpu *vcpu) { - uint64_t st_ipa = (ulong)ST_GPA_BASE; + u64 st_ipa = (ulong)ST_GPA_BASE; unsigned int gpages; gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE); @@ -174,13 +174,13 @@ static void steal_time_init(struct kvm_vcpu *vcpu) static void test_fw_regs_before_vm_start(struct kvm_vcpu *vcpu) { - uint64_t val; + u64 val; unsigned int i; int ret; for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) { const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i]; - uint64_t set_val; + u64 set_val; /* First 'read' should be the reset value for the reg */ val = vcpu_get_reg(vcpu, reg_info->reg); @@ -229,7 +229,7 @@ static void test_fw_regs_before_vm_start(struct kvm_vcpu *vcpu) static void test_fw_regs_after_vm_start(struct kvm_vcpu *vcpu) { - uint64_t val; + u64 val; unsigned int i; int ret; diff --git a/tools/testing/selftests/kvm/arm64/idreg-idst.c b/tools/testing/selftests/kvm/arm64/idreg-idst.c index 9ca9f125abdb..a3e84701d814 100644 --- a/tools/testing/selftests/kvm/arm64/idreg-idst.c +++ b/tools/testing/selftests/kvm/arm64/idreg-idst.c @@ -13,7 +13,7 @@ static volatile bool sys64, undef; #define __check_sr_read(r) \ ({ \ - uint64_t val; \ + u64 val; \ \ sys64 = false; \ undef = false; \ @@ -101,7 +101,7 @@ int main(int argc, char *argv[]) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; - uint64_t mmfr2; + u64 mmfr2; test_disable_default_vgic(); diff --git a/tools/testing/selftests/kvm/arm64/no-vgic-v3.c b/tools/testing/selftests/kvm/arm64/no-vgic-v3.c deleted file mode 100644 index 152c34776981..000000000000 --- a/tools/testing/selftests/kvm/arm64/no-vgic-v3.c +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -// Check that, on a GICv3 system, not configuring GICv3 correctly -// results in all of the sysregs generating an UNDEF exception. - -#include -#include -#include - -static volatile bool handled; - -#define __check_sr_read(r) \ - ({ \ - uint64_t val; \ - \ - handled = false; \ - dsb(sy); \ - val = read_sysreg_s(SYS_ ## r); \ - val; \ - }) - -#define __check_sr_write(r) \ - do { \ - handled = false; \ - dsb(sy); \ - write_sysreg_s(0, SYS_ ## r); \ - isb(); \ - } while(0) - -/* Fatal checks */ -#define check_sr_read(r) \ - do { \ - __check_sr_read(r); \ - __GUEST_ASSERT(handled, #r " no read trap"); \ - } while(0) - -#define check_sr_write(r) \ - do { \ - __check_sr_write(r); \ - __GUEST_ASSERT(handled, #r " no write trap"); \ - } while(0) - -#define check_sr_rw(r) \ - do { \ - check_sr_read(r); \ - check_sr_write(r); \ - } while(0) - -static void guest_code(void) -{ - uint64_t val; - - /* - * Check that we advertise that ID_AA64PFR0_EL1.GIC == 0, having - * hidden the feature at runtime without any other userspace action. - */ - __GUEST_ASSERT(FIELD_GET(ID_AA64PFR0_EL1_GIC, - read_sysreg(id_aa64pfr0_el1)) == 0, - "GICv3 wrongly advertised"); - - /* - * Access all GICv3 registers, and fail if we don't get an UNDEF. - * Note that we happily access all the APxRn registers without - * checking their existance, as all we want to see is a failure. - */ - check_sr_rw(ICC_PMR_EL1); - check_sr_read(ICC_IAR0_EL1); - check_sr_write(ICC_EOIR0_EL1); - check_sr_rw(ICC_HPPIR0_EL1); - check_sr_rw(ICC_BPR0_EL1); - check_sr_rw(ICC_AP0R0_EL1); - check_sr_rw(ICC_AP0R1_EL1); - check_sr_rw(ICC_AP0R2_EL1); - check_sr_rw(ICC_AP0R3_EL1); - check_sr_rw(ICC_AP1R0_EL1); - check_sr_rw(ICC_AP1R1_EL1); - check_sr_rw(ICC_AP1R2_EL1); - check_sr_rw(ICC_AP1R3_EL1); - check_sr_write(ICC_DIR_EL1); - check_sr_read(ICC_RPR_EL1); - check_sr_write(ICC_SGI1R_EL1); - check_sr_write(ICC_ASGI1R_EL1); - check_sr_write(ICC_SGI0R_EL1); - check_sr_read(ICC_IAR1_EL1); - check_sr_write(ICC_EOIR1_EL1); - check_sr_rw(ICC_HPPIR1_EL1); - check_sr_rw(ICC_BPR1_EL1); - check_sr_rw(ICC_CTLR_EL1); - check_sr_rw(ICC_IGRPEN0_EL1); - check_sr_rw(ICC_IGRPEN1_EL1); - - /* - * ICC_SRE_EL1 may not be trappable, as ICC_SRE_EL2.Enable can - * be RAO/WI. Engage in non-fatal accesses, starting with a - * write of 0 to try and disable SRE, and let's see if it - * sticks. - */ - __check_sr_write(ICC_SRE_EL1); - if (!handled) - GUEST_PRINTF("ICC_SRE_EL1 write not trapping (OK)\n"); - - val = __check_sr_read(ICC_SRE_EL1); - if (!handled) { - __GUEST_ASSERT((val & BIT(0)), - "ICC_SRE_EL1 not trapped but ICC_SRE_EL1.SRE not set\n"); - GUEST_PRINTF("ICC_SRE_EL1 read not trapping (OK)\n"); - } - - GUEST_DONE(); -} - -static void guest_undef_handler(struct ex_regs *regs) -{ - /* Success, we've gracefully exploded! */ - handled = true; - regs->pc += 4; -} - -static void test_run_vcpu(struct kvm_vcpu *vcpu) -{ - struct ucall uc; - - do { - vcpu_run(vcpu); - - switch (get_ucall(vcpu, &uc)) { - case UCALL_ABORT: - REPORT_GUEST_ASSERT(uc); - break; - case UCALL_PRINTF: - printf("%s", uc.buffer); - break; - case UCALL_DONE: - break; - default: - TEST_FAIL("Unknown ucall %lu", uc.cmd); - } - } while (uc.cmd != UCALL_DONE); -} - -static void test_guest_no_gicv3(void) -{ - struct kvm_vcpu *vcpu; - struct kvm_vm *vm; - - /* Create a VM without a GICv3 */ - vm = vm_create_with_one_vcpu(&vcpu, guest_code); - - vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vcpu); - - vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT, - ESR_ELx_EC_UNKNOWN, guest_undef_handler); - - test_run_vcpu(vcpu); - - kvm_vm_free(vm); -} - -int main(int argc, char *argv[]) -{ - struct kvm_vcpu *vcpu; - struct kvm_vm *vm; - uint64_t pfr0; - - test_disable_default_vgic(); - - vm = vm_create_with_one_vcpu(&vcpu, NULL); - pfr0 = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1)); - __TEST_REQUIRE(FIELD_GET(ID_AA64PFR0_EL1_GIC, pfr0), - "GICv3 not supported."); - kvm_vm_free(vm); - - test_guest_no_gicv3(); - - return 0; -} diff --git a/tools/testing/selftests/kvm/arm64/no-vgic.c b/tools/testing/selftests/kvm/arm64/no-vgic.c new file mode 100644 index 000000000000..25b2e3222f68 --- /dev/null +++ b/tools/testing/selftests/kvm/arm64/no-vgic.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Check that, on a GICv3-capable system (GICv3 native, or GICv5 with +// FEAT_GCIE_LEGACY), not configuring GICv3 correctly results in all +// of the sysregs generating an UNDEF exception. Do the same for GICv5 +// on a GICv5 host. + +#include +#include +#include + +#include + +static volatile bool handled; + +#define __check_sr_read(r) \ + ({ \ + u64 val; \ + \ + handled = false; \ + dsb(sy); \ + val = read_sysreg_s(SYS_ ## r); \ + val; \ + }) + +#define __check_sr_write(r) \ + do { \ + handled = false; \ + dsb(sy); \ + write_sysreg_s(0, SYS_ ## r); \ + isb(); \ + } while (0) + +#define __check_gicv5_gicr_op(r) \ + ({ \ + u64 val; \ + \ + handled = false; \ + dsb(sy); \ + val = read_sysreg_s(GICV5_OP_GICR_ ## r); \ + val; \ + }) + +#define __check_gicv5_gic_op(r) \ + do { \ + handled = false; \ + dsb(sy); \ + write_sysreg_s(0, GICV5_OP_GIC_ ## r); \ + isb(); \ + } while (0) + +/* Fatal checks */ +#define check_sr_read(r) \ + do { \ + __check_sr_read(r); \ + __GUEST_ASSERT(handled, #r " no read trap"); \ + } while (0) + +#define check_sr_write(r) \ + do { \ + __check_sr_write(r); \ + __GUEST_ASSERT(handled, #r " no write trap"); \ + } while (0) + +#define check_sr_rw(r) \ + do { \ + check_sr_read(r); \ + check_sr_write(r); \ + } while (0) + +#define check_gicv5_gicr_op(r) \ + do { \ + __check_gicv5_gicr_op(r); \ + __GUEST_ASSERT(handled, #r " no read trap"); \ + } while (0) + +#define check_gicv5_gic_op(r) \ + do { \ + __check_gicv5_gic_op(r); \ + __GUEST_ASSERT(handled, #r " no write trap"); \ + } while (0) + +static void guest_code_gicv3(void) +{ + u64 val; + + /* + * Check that we advertise that ID_AA64PFR0_EL1.GIC == 0, having + * hidden the feature at runtime without any other userspace action. + */ + __GUEST_ASSERT(FIELD_GET(ID_AA64PFR0_EL1_GIC, + read_sysreg(id_aa64pfr0_el1)) == 0, + "GICv3 wrongly advertised"); + + /* + * Access all GICv3 registers, and fail if we don't get an UNDEF. + * Note that we happily access all the APxRn registers without + * checking their existence, as all we want to see is a failure. + */ + check_sr_rw(ICC_PMR_EL1); + check_sr_read(ICC_IAR0_EL1); + check_sr_write(ICC_EOIR0_EL1); + check_sr_rw(ICC_HPPIR0_EL1); + check_sr_rw(ICC_BPR0_EL1); + check_sr_rw(ICC_AP0R0_EL1); + check_sr_rw(ICC_AP0R1_EL1); + check_sr_rw(ICC_AP0R2_EL1); + check_sr_rw(ICC_AP0R3_EL1); + check_sr_rw(ICC_AP1R0_EL1); + check_sr_rw(ICC_AP1R1_EL1); + check_sr_rw(ICC_AP1R2_EL1); + check_sr_rw(ICC_AP1R3_EL1); + check_sr_write(ICC_DIR_EL1); + check_sr_read(ICC_RPR_EL1); + check_sr_write(ICC_SGI1R_EL1); + check_sr_write(ICC_ASGI1R_EL1); + check_sr_write(ICC_SGI0R_EL1); + check_sr_read(ICC_IAR1_EL1); + check_sr_write(ICC_EOIR1_EL1); + check_sr_rw(ICC_HPPIR1_EL1); + check_sr_rw(ICC_BPR1_EL1); + check_sr_rw(ICC_CTLR_EL1); + check_sr_rw(ICC_IGRPEN0_EL1); + check_sr_rw(ICC_IGRPEN1_EL1); + + /* + * ICC_SRE_EL1 may not be trappable, as ICC_SRE_EL2.Enable can + * be RAO/WI. Engage in non-fatal accesses, starting with a + * write of 0 to try and disable SRE, and let's see if it + * sticks. + */ + __check_sr_write(ICC_SRE_EL1); + if (!handled) + GUEST_PRINTF("ICC_SRE_EL1 write not trapping (OK)\n"); + + val = __check_sr_read(ICC_SRE_EL1); + if (!handled) { + __GUEST_ASSERT((val & BIT(0)), + "ICC_SRE_EL1 not trapped but ICC_SRE_EL1.SRE not set\n"); + GUEST_PRINTF("ICC_SRE_EL1 read not trapping (OK)\n"); + } + + GUEST_DONE(); +} + +static void guest_code_gicv5(void) +{ + /* + * Check that we advertise that ID_AA64PFR2_EL1.GCIE == 0, having + * hidden the feature at runtime without any other userspace action. + */ + __GUEST_ASSERT(FIELD_GET(ID_AA64PFR2_EL1_GCIE, + read_sysreg_s(SYS_ID_AA64PFR2_EL1)) == 0, + "GICv5 wrongly advertised"); + + /* + * Try all GICv5 instructions, and fail if we don't get an UNDEF. + */ + check_gicv5_gic_op(CDAFF); + check_gicv5_gic_op(CDDI); + check_gicv5_gic_op(CDDIS); + check_gicv5_gic_op(CDEOI); + check_gicv5_gic_op(CDHM); + check_gicv5_gic_op(CDPEND); + check_gicv5_gic_op(CDPRI); + check_gicv5_gic_op(CDRCFG); + check_gicv5_gicr_op(CDIA); + check_gicv5_gicr_op(CDNMIA); + + /* Check General System Register acccesses */ + check_sr_rw(ICC_APR_EL1); + check_sr_rw(ICC_CR0_EL1); + check_sr_read(ICC_HPPIR_EL1); + check_sr_read(ICC_IAFFIDR_EL1); + check_sr_rw(ICC_ICSR_EL1); + check_sr_read(ICC_IDR0_EL1); + check_sr_rw(ICC_PCR_EL1); + + /* Check PPI System Register accessess */ + check_sr_rw(ICC_PPI_CACTIVER0_EL1); + check_sr_rw(ICC_PPI_CACTIVER1_EL1); + check_sr_rw(ICC_PPI_SACTIVER0_EL1); + check_sr_rw(ICC_PPI_SACTIVER1_EL1); + check_sr_rw(ICC_PPI_CPENDR0_EL1); + check_sr_rw(ICC_PPI_CPENDR1_EL1); + check_sr_rw(ICC_PPI_SPENDR0_EL1); + check_sr_rw(ICC_PPI_SPENDR1_EL1); + check_sr_rw(ICC_PPI_ENABLER0_EL1); + check_sr_rw(ICC_PPI_ENABLER1_EL1); + check_sr_read(ICC_PPI_HMR0_EL1); + check_sr_read(ICC_PPI_HMR1_EL1); + check_sr_rw(ICC_PPI_PRIORITYR0_EL1); + check_sr_rw(ICC_PPI_PRIORITYR1_EL1); + check_sr_rw(ICC_PPI_PRIORITYR2_EL1); + check_sr_rw(ICC_PPI_PRIORITYR3_EL1); + check_sr_rw(ICC_PPI_PRIORITYR4_EL1); + check_sr_rw(ICC_PPI_PRIORITYR5_EL1); + check_sr_rw(ICC_PPI_PRIORITYR6_EL1); + check_sr_rw(ICC_PPI_PRIORITYR7_EL1); + check_sr_rw(ICC_PPI_PRIORITYR8_EL1); + check_sr_rw(ICC_PPI_PRIORITYR9_EL1); + check_sr_rw(ICC_PPI_PRIORITYR10_EL1); + check_sr_rw(ICC_PPI_PRIORITYR11_EL1); + check_sr_rw(ICC_PPI_PRIORITYR12_EL1); + check_sr_rw(ICC_PPI_PRIORITYR13_EL1); + check_sr_rw(ICC_PPI_PRIORITYR14_EL1); + check_sr_rw(ICC_PPI_PRIORITYR15_EL1); + + GUEST_DONE(); +} + +static void guest_undef_handler(struct ex_regs *regs) +{ + /* Success, we've gracefully exploded! */ + handled = true; + regs->pc += 4; +} + +static void test_run_vcpu(struct kvm_vcpu *vcpu) +{ + struct ucall uc; + + do { + vcpu_run(vcpu); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + break; + case UCALL_PRINTF: + printf("%s", uc.buffer); + break; + case UCALL_DONE: + break; + default: + TEST_FAIL("Unknown ucall %lu", uc.cmd); + } + } while (uc.cmd != UCALL_DONE); +} + +static void test_guest_no_vgic(void *guest_code) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + + /* Create a VM without a GIC */ + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + + vm_init_descriptor_tables(vm); + vcpu_init_descriptor_tables(vcpu); + + vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT, + ESR_ELx_EC_UNKNOWN, guest_undef_handler); + + test_run_vcpu(vcpu); + + kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + bool has_v3, has_v5; + u64 pfr; + + test_disable_default_vgic(); + + vm = vm_create_with_one_vcpu(&vcpu, NULL); + + pfr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1)); + has_v3 = !!FIELD_GET(ID_AA64PFR0_EL1_GIC, pfr); + + pfr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR2_EL1)); + has_v5 = !!FIELD_GET(ID_AA64PFR2_EL1_GCIE, pfr); + + kvm_vm_free(vm); + + __TEST_REQUIRE(has_v3 || has_v5, + "Neither GICv3 nor GICv5 supported."); + + if (has_v3) { + pr_info("Testing no-vgic-v3\n"); + test_guest_no_vgic(guest_code_gicv3); + } else { + pr_info("No GICv3 support: skipping no-vgic-v3 test\n"); + } + + if (has_v5) { + pr_info("Testing no-vgic-v5\n"); + test_guest_no_vgic(guest_code_gicv5); + } else { + pr_info("No GICv5 support: skipping no-vgic-v5 test\n"); + } + + return 0; +} diff --git a/tools/testing/selftests/kvm/arm64/page_fault_test.c b/tools/testing/selftests/kvm/arm64/page_fault_test.c index 4ccbd389d133..6bb3d82906b2 100644 --- a/tools/testing/selftests/kvm/arm64/page_fault_test.c +++ b/tools/testing/selftests/kvm/arm64/page_fault_test.c @@ -23,7 +23,7 @@ #define TEST_PTE_GVA 0xb0000000 #define TEST_DATA 0x0123456789ABCDEF -static uint64_t *guest_test_memory = (uint64_t *)TEST_GVA; +static u64 *guest_test_memory = (u64 *)TEST_GVA; #define CMD_NONE (0) #define CMD_SKIP_TEST (1ULL << 1) @@ -48,7 +48,7 @@ static struct event_cnt { struct test_desc { const char *name; - uint64_t mem_mark_cmd; + u64 mem_mark_cmd; /* Skip the test if any prepare function returns false */ bool (*guest_prepare[PREPARE_FN_NR])(void); void (*guest_test)(void); @@ -59,8 +59,8 @@ struct test_desc { void (*iabt_handler)(struct ex_regs *regs); void (*mmio_handler)(struct kvm_vm *vm, struct kvm_run *run); void (*fail_vcpu_run_handler)(int ret); - uint32_t pt_memslot_flags; - uint32_t data_memslot_flags; + u32 pt_memslot_flags; + u32 data_memslot_flags; bool skip; struct event_cnt expected_events; }; @@ -70,9 +70,9 @@ struct test_params { struct test_desc *test_desc; }; -static inline void flush_tlb_page(uint64_t vaddr) +static inline void flush_tlb_page(gva_t gva) { - uint64_t page = vaddr >> 12; + gva_t page = gva >> 12; dsb(ishst); asm volatile("tlbi vaae1is, %0" :: "r" (page)); @@ -82,7 +82,7 @@ static inline void flush_tlb_page(uint64_t vaddr) static void guest_write64(void) { - uint64_t val; + u64 val; WRITE_ONCE(*guest_test_memory, TEST_DATA); val = READ_ONCE(*guest_test_memory); @@ -92,8 +92,8 @@ static void guest_write64(void) /* Check the system for atomic instructions. */ static bool guest_check_lse(void) { - uint64_t isar0 = read_sysreg(id_aa64isar0_el1); - uint64_t atomic; + u64 isar0 = read_sysreg(id_aa64isar0_el1); + u64 atomic; atomic = FIELD_GET(ID_AA64ISAR0_EL1_ATOMIC, isar0); return atomic >= 2; @@ -101,8 +101,8 @@ static bool guest_check_lse(void) static bool guest_check_dc_zva(void) { - uint64_t dczid = read_sysreg(dczid_el0); - uint64_t dzp = FIELD_GET(DCZID_EL0_DZP, dczid); + u64 dczid = read_sysreg(dczid_el0); + u64 dzp = FIELD_GET(DCZID_EL0_DZP, dczid); return dzp == 0; } @@ -110,7 +110,7 @@ static bool guest_check_dc_zva(void) /* Compare and swap instruction. */ static void guest_cas(void) { - uint64_t val; + u64 val; GUEST_ASSERT(guest_check_lse()); asm volatile(".arch_extension lse\n" @@ -122,7 +122,7 @@ static void guest_cas(void) static void guest_read64(void) { - uint64_t val; + u64 val; val = READ_ONCE(*guest_test_memory); GUEST_ASSERT_EQ(val, 0); @@ -131,7 +131,7 @@ static void guest_read64(void) /* Address translation instruction */ static void guest_at(void) { - uint64_t par; + u64 par; asm volatile("at s1e1r, %0" :: "r" (guest_test_memory)); isb(); @@ -148,7 +148,7 @@ static void guest_at(void) */ static void guest_dc_zva(void) { - uint16_t val; + u16 val; asm volatile("dc zva, %0" :: "r" (guest_test_memory)); dsb(ish); @@ -164,8 +164,8 @@ static void guest_dc_zva(void) */ static void guest_ld_preidx(void) { - uint64_t val; - uint64_t addr = TEST_GVA - 8; + u64 val; + u64 addr = TEST_GVA - 8; /* * This ends up accessing "TEST_GVA + 8 - 8", where "TEST_GVA - 8" is @@ -179,8 +179,8 @@ static void guest_ld_preidx(void) static void guest_st_preidx(void) { - uint64_t val = TEST_DATA; - uint64_t addr = TEST_GVA - 8; + u64 val = TEST_DATA; + u64 addr = TEST_GVA - 8; asm volatile("str %0, [%1, #8]!" : "+r" (val), "+r" (addr)); @@ -191,8 +191,8 @@ static void guest_st_preidx(void) static bool guest_set_ha(void) { - uint64_t mmfr1 = read_sysreg(id_aa64mmfr1_el1); - uint64_t hadbs, tcr; + u64 mmfr1 = read_sysreg(id_aa64mmfr1_el1); + u64 hadbs, tcr; /* Skip if HA is not supported. */ hadbs = FIELD_GET(ID_AA64MMFR1_EL1_HAFDBS, mmfr1); @@ -208,7 +208,7 @@ static bool guest_set_ha(void) static bool guest_clear_pte_af(void) { - *((uint64_t *)TEST_PTE_GVA) &= ~PTE_AF; + *((u64 *)TEST_PTE_GVA) &= ~PTE_AF; flush_tlb_page(TEST_GVA); return true; @@ -217,7 +217,7 @@ static bool guest_clear_pte_af(void) static void guest_check_pte_af(void) { dsb(ish); - GUEST_ASSERT_EQ(*((uint64_t *)TEST_PTE_GVA) & PTE_AF, PTE_AF); + GUEST_ASSERT_EQ(*((u64 *)TEST_PTE_GVA) & PTE_AF, PTE_AF); } static void guest_check_write_in_dirty_log(void) @@ -302,26 +302,26 @@ static void no_iabt_handler(struct ex_regs *regs) static struct uffd_args { char *copy; void *hva; - uint64_t paging_size; + u64 paging_size; } pt_args, data_args; /* Returns true to continue the test, and false if it should be skipped. */ static int uffd_generic_handler(int uffd_mode, int uffd, struct uffd_msg *msg, struct uffd_args *args) { - uint64_t addr = msg->arg.pagefault.address; - uint64_t flags = msg->arg.pagefault.flags; + u64 addr = msg->arg.pagefault.address; + u64 flags = msg->arg.pagefault.flags; struct uffdio_copy copy; int ret; TEST_ASSERT(uffd_mode == UFFDIO_REGISTER_MODE_MISSING, "The only expected UFFD mode is MISSING"); - TEST_ASSERT_EQ(addr, (uint64_t)args->hva); + TEST_ASSERT_EQ(addr, (u64)args->hva); pr_debug("uffd fault: addr=%p write=%d\n", (void *)addr, !!(flags & UFFD_PAGEFAULT_FLAG_WRITE)); - copy.src = (uint64_t)args->copy; + copy.src = (u64)args->copy; copy.dst = addr; copy.len = args->paging_size; copy.mode = 0; @@ -407,7 +407,7 @@ static bool punch_hole_in_backing_store(struct kvm_vm *vm, struct userspace_mem_region *region) { void *hva = (void *)region->region.userspace_addr; - uint64_t paging_size = region->region.memory_size; + u64 paging_size = region->region.memory_size; int ret, fd = region->fd; if (fd != -1) { @@ -438,7 +438,7 @@ static void mmio_on_test_gpa_handler(struct kvm_vm *vm, struct kvm_run *run) static void mmio_no_handler(struct kvm_vm *vm, struct kvm_run *run) { - uint64_t data; + u64 data; memcpy(&data, run->mmio.data, sizeof(data)); pr_debug("addr=%lld len=%d w=%d data=%lx\n", @@ -449,11 +449,11 @@ static void mmio_no_handler(struct kvm_vm *vm, struct kvm_run *run) static bool check_write_in_dirty_log(struct kvm_vm *vm, struct userspace_mem_region *region, - uint64_t host_pg_nr) + u64 host_pg_nr) { unsigned long *bmap; bool first_page_dirty; - uint64_t size = region->region.memory_size; + u64 size = region->region.memory_size; /* getpage_size() is not always equal to vm->page_size */ bmap = bitmap_zalloc(size / getpagesize()); @@ -468,7 +468,7 @@ static bool handle_cmd(struct kvm_vm *vm, int cmd) { struct userspace_mem_region *data_region, *pt_region; bool continue_test = true; - uint64_t pte_gpa, pte_pg; + u64 pte_gpa, pte_pg; data_region = vm_get_mem_region(vm, MEM_REGION_TEST_DATA); pt_region = vm_get_mem_region(vm, MEM_REGION_PT); @@ -510,7 +510,7 @@ void fail_vcpu_run_mmio_no_syndrome_handler(int ret) events.fail_vcpu_runs += 1; } -typedef uint32_t aarch64_insn_t; +typedef u32 aarch64_insn_t; extern aarch64_insn_t __exec_test[2]; noinline void __return_0x77(void) @@ -525,7 +525,7 @@ noinline void __return_0x77(void) */ static void load_exec_code_for_test(struct kvm_vm *vm) { - uint64_t *code; + u64 *code; struct userspace_mem_region *region; void *hva; @@ -552,7 +552,7 @@ static void setup_abort_handlers(struct kvm_vm *vm, struct kvm_vcpu *vcpu, static void setup_gva_maps(struct kvm_vm *vm) { struct userspace_mem_region *region; - uint64_t pte_gpa; + u64 pte_gpa; region = vm_get_mem_region(vm, MEM_REGION_TEST_DATA); /* Map TEST_GVA first. This will install a new PTE. */ @@ -574,12 +574,12 @@ enum pf_test_memslots { */ static void setup_memslots(struct kvm_vm *vm, struct test_params *p) { - uint64_t backing_src_pagesz = get_backing_src_pagesz(p->src_type); - uint64_t guest_page_size = vm->page_size; - uint64_t max_gfn = vm_compute_max_gfn(vm); + u64 backing_src_pagesz = get_backing_src_pagesz(p->src_type); + u64 guest_page_size = vm->page_size; + u64 max_gfn = vm_compute_max_gfn(vm); /* Enough for 2M of code when using 4K guest pages. */ - uint64_t code_npages = 512; - uint64_t pt_size, data_size, data_gpa; + u64 code_npages = 512; + u64 pt_size, data_size, data_gpa; /* * This test requires 1 pgd, 2 pud, 4 pmd, and 6 pte pages when using diff --git a/tools/testing/selftests/kvm/arm64/psci_test.c b/tools/testing/selftests/kvm/arm64/psci_test.c index 98e49f710aef..e775faf20868 100644 --- a/tools/testing/selftests/kvm/arm64/psci_test.c +++ b/tools/testing/selftests/kvm/arm64/psci_test.c @@ -22,8 +22,7 @@ #define CPU_ON_ENTRY_ADDR 0xfeedf00dul #define CPU_ON_CONTEXT_ID 0xdeadc0deul -static uint64_t psci_cpu_on(uint64_t target_cpu, uint64_t entry_addr, - uint64_t context_id) +static u64 psci_cpu_on(u64 target_cpu, u64 entry_addr, u64 context_id) { struct arm_smccc_res res; @@ -33,8 +32,7 @@ static uint64_t psci_cpu_on(uint64_t target_cpu, uint64_t entry_addr, return res.a0; } -static uint64_t psci_affinity_info(uint64_t target_affinity, - uint64_t lowest_affinity_level) +static u64 psci_affinity_info(u64 target_affinity, u64 lowest_affinity_level) { struct arm_smccc_res res; @@ -44,7 +42,7 @@ static uint64_t psci_affinity_info(uint64_t target_affinity, return res.a0; } -static uint64_t psci_system_suspend(uint64_t entry_addr, uint64_t context_id) +static u64 psci_system_suspend(u64 entry_addr, u64 context_id) { struct arm_smccc_res res; @@ -54,7 +52,7 @@ static uint64_t psci_system_suspend(uint64_t entry_addr, uint64_t context_id) return res.a0; } -static uint64_t psci_system_off2(uint64_t type, uint64_t cookie) +static u64 psci_system_off2(u64 type, u64 cookie) { struct arm_smccc_res res; @@ -63,7 +61,7 @@ static uint64_t psci_system_off2(uint64_t type, uint64_t cookie) return res.a0; } -static uint64_t psci_features(uint32_t func_id) +static u64 psci_features(u32 func_id) { struct arm_smccc_res res; @@ -110,7 +108,7 @@ static void enter_guest(struct kvm_vcpu *vcpu) static void assert_vcpu_reset(struct kvm_vcpu *vcpu) { - uint64_t obs_pc, obs_x0; + u64 obs_pc, obs_x0; obs_pc = vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc)); obs_x0 = vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.regs[0])); @@ -123,9 +121,9 @@ static void assert_vcpu_reset(struct kvm_vcpu *vcpu) obs_x0, CPU_ON_CONTEXT_ID); } -static void guest_test_cpu_on(uint64_t target_cpu) +static void guest_test_cpu_on(u64 target_cpu) { - uint64_t target_state; + u64 target_state; GUEST_ASSERT(!psci_cpu_on(target_cpu, CPU_ON_ENTRY_ADDR, CPU_ON_CONTEXT_ID)); @@ -142,7 +140,7 @@ static void guest_test_cpu_on(uint64_t target_cpu) static void host_test_cpu_on(void) { struct kvm_vcpu *source, *target; - uint64_t target_mpidr; + u64 target_mpidr; struct kvm_vm *vm; struct ucall uc; @@ -166,7 +164,7 @@ static void host_test_cpu_on(void) static void guest_test_system_suspend(void) { - uint64_t ret; + u64 ret; /* assert that SYSTEM_SUSPEND is discoverable */ GUEST_ASSERT(!psci_features(PSCI_1_0_FN_SYSTEM_SUSPEND)); @@ -200,7 +198,7 @@ static void host_test_system_suspend(void) static void guest_test_system_off2(void) { - uint64_t ret; + u64 ret; /* assert that SYSTEM_OFF2 is discoverable */ GUEST_ASSERT(psci_features(PSCI_1_3_FN_SYSTEM_OFF2) & @@ -238,7 +236,7 @@ static void host_test_system_off2(void) { struct kvm_vcpu *source, *target; struct kvm_mp_state mps; - uint64_t psci_version = 0; + u64 psci_version = 0; int nr_shutdowns = 0; struct kvm_run *run; struct ucall uc; diff --git a/tools/testing/selftests/kvm/arm64/sea_to_user.c b/tools/testing/selftests/kvm/arm64/sea_to_user.c index 573dd790aeb8..e96d8982c28b 100644 --- a/tools/testing/selftests/kvm/arm64/sea_to_user.c +++ b/tools/testing/selftests/kvm/arm64/sea_to_user.c @@ -51,18 +51,16 @@ #define EINJ_OFFSET 0x01234badUL #define EINJ_GVA ((START_GVA) + (EINJ_OFFSET)) -static vm_paddr_t einj_gpa; +static gpa_t einj_gpa; static void *einj_hva; -static uint64_t einj_hpa; +static u64 einj_hpa; static bool far_invalid; -static uint64_t translate_to_host_paddr(unsigned long vaddr) +static u64 translate_hva_to_hpa(unsigned long hva) { - uint64_t pinfo; - int64_t offset = vaddr / getpagesize() * sizeof(pinfo); + u64 pinfo; + s64 offset = hva / getpagesize() * sizeof(pinfo); int fd; - uint64_t page_addr; - uint64_t paddr; fd = open("/proc/self/pagemap", O_RDONLY); if (fd < 0) @@ -77,12 +75,11 @@ static uint64_t translate_to_host_paddr(unsigned long vaddr) if ((pinfo & PAGE_PRESENT) == 0) ksft_exit_fail_perror("Page not present"); - page_addr = (pinfo & PAGE_PHYSICAL) << MIN_PAGE_SHIFT; - paddr = page_addr + (vaddr & (getpagesize() - 1)); - return paddr; + return ((pinfo & PAGE_PHYSICAL) << MIN_PAGE_SHIFT) + + (hva & (getpagesize() - 1)); } -static void write_einj_entry(const char *einj_path, uint64_t val) +static void write_einj_entry(const char *einj_path, u64 val) { char cmd[256] = {0}; FILE *cmdfile = NULL; @@ -96,7 +93,7 @@ static void write_einj_entry(const char *einj_path, uint64_t val) ksft_exit_fail_perror("Failed to write EINJ entry"); } -static void inject_uer(uint64_t paddr) +static void inject_uer(u64 hpa) { if (access("/sys/firmware/acpi/tables/EINJ", R_OK) == -1) ksft_test_result_skip("EINJ table no available in firmware"); @@ -106,7 +103,7 @@ static void inject_uer(uint64_t paddr) write_einj_entry(EINJ_ETYPE, ERROR_TYPE_MEMORY_UER); write_einj_entry(EINJ_FLAGS, MASK_MEMORY_UER); - write_einj_entry(EINJ_ADDR, paddr); + write_einj_entry(EINJ_ADDR, hpa); write_einj_entry(EINJ_MASK, ~0x0UL); write_einj_entry(EINJ_NOTRIGGER, 1); write_einj_entry(EINJ_DOIT, 1); @@ -145,10 +142,10 @@ static void setup_sigbus_handler(void) static void guest_code(void) { - uint64_t guest_data; + u64 guest_data; /* Consumes error will cause a SEA. */ - guest_data = *(uint64_t *)EINJ_GVA; + guest_data = *(u64 *)EINJ_GVA; GUEST_FAIL("Poison not protected by SEA: gva=%#lx, guest_data=%#lx\n", EINJ_GVA, guest_data); @@ -253,8 +250,8 @@ static struct kvm_vm *vm_create_with_sea_handler(struct kvm_vcpu **vcpu) size_t backing_page_size; size_t guest_page_size; size_t alignment; - uint64_t num_guest_pages; - vm_paddr_t start_gpa; + u64 num_guest_pages; + gpa_t start_gpa; enum vm_mem_backing_src_type src_type = VM_MEM_SRC_ANONYMOUS_HUGETLB_1GB; struct kvm_vm *vm; @@ -278,7 +275,7 @@ static struct kvm_vm *vm_create_with_sea_handler(struct kvm_vcpu **vcpu) vm_userspace_mem_region_add( /*vm=*/vm, /*src_type=*/src_type, - /*guest_paddr=*/start_gpa, + /*gpa=*/start_gpa, /*slot=*/1, /*npages=*/num_guest_pages, /*flags=*/0); @@ -292,18 +289,18 @@ static struct kvm_vm *vm_create_with_sea_handler(struct kvm_vcpu **vcpu) static void vm_inject_memory_uer(struct kvm_vm *vm) { - uint64_t guest_data; + u64 guest_data; einj_gpa = addr_gva2gpa(vm, EINJ_GVA); einj_hva = addr_gva2hva(vm, EINJ_GVA); /* Populate certain data before injecting UER. */ - *(uint64_t *)einj_hva = 0xBAADCAFE; - guest_data = *(uint64_t *)einj_hva; + *(u64 *)einj_hva = 0xBAADCAFE; + guest_data = *(u64 *)einj_hva; ksft_print_msg("Before EINJect: data=%#lx\n", guest_data); - einj_hpa = translate_to_host_paddr((unsigned long)einj_hva); + einj_hpa = translate_hva_to_hpa((unsigned long)einj_hva); ksft_print_msg("EINJ_GVA=%#lx, einj_gpa=%#lx, einj_hva=%p, einj_hpa=%#lx\n", EINJ_GVA, einj_gpa, einj_hva, einj_hpa); diff --git a/tools/testing/selftests/kvm/arm64/set_id_regs.c b/tools/testing/selftests/kvm/arm64/set_id_regs.c index fa3478a6c914..7429a1055df5 100644 --- a/tools/testing/selftests/kvm/arm64/set_id_regs.c +++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c @@ -30,21 +30,24 @@ struct reg_ftr_bits { char *name; bool sign; enum ftr_type type; - uint8_t shift; - uint64_t mask; + u8 shift; + u64 mask; /* * For FTR_EXACT, safe_val is used as the exact safe value. * For FTR_LOWER_SAFE, safe_val is used as the minimal safe value. */ - int64_t safe_val; + s64 safe_val; + + /* Allowed to be changed by the host after run */ + bool mutable; }; struct test_feature_reg { - uint32_t reg; + u32 reg; const struct reg_ftr_bits *ftr_bits; }; -#define __REG_FTR_BITS(NAME, SIGNED, TYPE, SHIFT, MASK, SAFE_VAL) \ +#define __REG_FTR_BITS(NAME, SIGNED, TYPE, SHIFT, MASK, SAFE_VAL, MUT) \ { \ .name = #NAME, \ .sign = SIGNED, \ @@ -52,15 +55,20 @@ struct test_feature_reg { .shift = SHIFT, \ .mask = MASK, \ .safe_val = SAFE_VAL, \ + .mutable = MUT, \ } #define REG_FTR_BITS(type, reg, field, safe_val) \ __REG_FTR_BITS(reg##_##field, FTR_UNSIGNED, type, reg##_##field##_SHIFT, \ - reg##_##field##_MASK, safe_val) + reg##_##field##_MASK, safe_val, false) + +#define REG_FTR_BITS_MUTABLE(type, reg, field, safe_val) \ + __REG_FTR_BITS(reg##_##field, FTR_UNSIGNED, type, reg##_##field##_SHIFT, \ + reg##_##field##_MASK, safe_val, true) #define S_REG_FTR_BITS(type, reg, field, safe_val) \ __REG_FTR_BITS(reg##_##field, FTR_SIGNED, type, reg##_##field##_SHIFT, \ - reg##_##field##_MASK, safe_val) + reg##_##field##_MASK, safe_val, false) #define REG_FTR_END \ { \ @@ -135,7 +143,8 @@ static const struct reg_ftr_bits ftr_id_aa64pfr0_el1[] = { REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, CSV2, 0), REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, DIT, 0), REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, SEL2, 0), - REG_FTR_BITS(FTR_EXACT, ID_AA64PFR0_EL1, GIC, 0), + /* GICv3 support will be forced at run time if available */ + REG_FTR_BITS_MUTABLE(FTR_EXACT, ID_AA64PFR0_EL1, GIC, 0), REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, EL3, 1), REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, EL2, 1), REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, EL1, 1), @@ -266,9 +275,9 @@ static void guest_code(void) } /* Return a safe value to a given ftr_bits an ftr value */ -uint64_t get_safe_value(const struct reg_ftr_bits *ftr_bits, uint64_t ftr) +u64 get_safe_value(const struct reg_ftr_bits *ftr_bits, u64 ftr) { - uint64_t ftr_max = ftr_bits->mask >> ftr_bits->shift; + u64 ftr_max = ftr_bits->mask >> ftr_bits->shift; TEST_ASSERT(ftr_max > 1, "This test doesn't support single bit features"); @@ -320,16 +329,16 @@ uint64_t get_safe_value(const struct reg_ftr_bits *ftr_bits, uint64_t ftr) } /* Return an invalid value to a given ftr_bits an ftr value */ -uint64_t get_invalid_value(const struct reg_ftr_bits *ftr_bits, uint64_t ftr) +u64 get_invalid_value(const struct reg_ftr_bits *ftr_bits, u64 ftr) { - uint64_t ftr_max = ftr_bits->mask >> ftr_bits->shift; + u64 ftr_max = ftr_bits->mask >> ftr_bits->shift; TEST_ASSERT(ftr_max > 1, "This test doesn't support single bit features"); if (ftr_bits->sign == FTR_UNSIGNED) { switch (ftr_bits->type) { case FTR_EXACT: - ftr = max((uint64_t)ftr_bits->safe_val + 1, ftr + 1); + ftr = max((u64)ftr_bits->safe_val + 1, ftr + 1); break; case FTR_LOWER_SAFE: ftr++; @@ -349,7 +358,7 @@ uint64_t get_invalid_value(const struct reg_ftr_bits *ftr_bits, uint64_t ftr) } else if (ftr != ftr_max) { switch (ftr_bits->type) { case FTR_EXACT: - ftr = max((uint64_t)ftr_bits->safe_val + 1, ftr + 1); + ftr = max((u64)ftr_bits->safe_val + 1, ftr + 1); break; case FTR_LOWER_SAFE: ftr++; @@ -373,12 +382,12 @@ uint64_t get_invalid_value(const struct reg_ftr_bits *ftr_bits, uint64_t ftr) return ftr; } -static uint64_t test_reg_set_success(struct kvm_vcpu *vcpu, uint64_t reg, - const struct reg_ftr_bits *ftr_bits) +static u64 test_reg_set_success(struct kvm_vcpu *vcpu, u64 reg, + const struct reg_ftr_bits *ftr_bits) { - uint8_t shift = ftr_bits->shift; - uint64_t mask = ftr_bits->mask; - uint64_t val, new_val, ftr; + u8 shift = ftr_bits->shift; + u64 mask = ftr_bits->mask; + u64 val, new_val, ftr; val = vcpu_get_reg(vcpu, reg); ftr = (val & mask) >> shift; @@ -396,12 +405,12 @@ static uint64_t test_reg_set_success(struct kvm_vcpu *vcpu, uint64_t reg, return new_val; } -static void test_reg_set_fail(struct kvm_vcpu *vcpu, uint64_t reg, +static void test_reg_set_fail(struct kvm_vcpu *vcpu, u64 reg, const struct reg_ftr_bits *ftr_bits) { - uint8_t shift = ftr_bits->shift; - uint64_t mask = ftr_bits->mask; - uint64_t val, old_val, ftr; + u8 shift = ftr_bits->shift; + u64 mask = ftr_bits->mask; + u64 val, old_val, ftr; int r; val = vcpu_get_reg(vcpu, reg); @@ -422,7 +431,7 @@ static void test_reg_set_fail(struct kvm_vcpu *vcpu, uint64_t reg, TEST_ASSERT_EQ(val, old_val); } -static uint64_t test_reg_vals[KVM_ARM_FEATURE_ID_RANGE_SIZE]; +static u64 test_reg_vals[KVM_ARM_FEATURE_ID_RANGE_SIZE]; #define encoding_to_range_idx(encoding) \ KVM_ARM_FEATURE_ID_RANGE_IDX(sys_reg_Op0(encoding), sys_reg_Op1(encoding), \ @@ -432,7 +441,7 @@ static uint64_t test_reg_vals[KVM_ARM_FEATURE_ID_RANGE_SIZE]; static void test_vm_ftr_id_regs(struct kvm_vcpu *vcpu, bool aarch64_only) { - uint64_t masks[KVM_ARM_FEATURE_ID_RANGE_SIZE]; + u64 masks[KVM_ARM_FEATURE_ID_RANGE_SIZE]; struct reg_mask_range range = { .addr = (__u64)masks, }; @@ -449,8 +458,8 @@ static void test_vm_ftr_id_regs(struct kvm_vcpu *vcpu, bool aarch64_only) for (int i = 0; i < ARRAY_SIZE(test_regs); i++) { const struct reg_ftr_bits *ftr_bits = test_regs[i].ftr_bits; - uint32_t reg_id = test_regs[i].reg; - uint64_t reg = KVM_ARM64_SYS_REG(reg_id); + u32 reg_id = test_regs[i].reg; + u64 reg = KVM_ARM64_SYS_REG(reg_id); int idx; /* Get the index to masks array for the idreg */ @@ -480,11 +489,11 @@ static void test_vm_ftr_id_regs(struct kvm_vcpu *vcpu, bool aarch64_only) #define MPAM_IDREG_TEST 6 static void test_user_set_mpam_reg(struct kvm_vcpu *vcpu) { - uint64_t masks[KVM_ARM_FEATURE_ID_RANGE_SIZE]; + u64 masks[KVM_ARM_FEATURE_ID_RANGE_SIZE]; struct reg_mask_range range = { .addr = (__u64)masks, }; - uint64_t val; + u64 val; int idx, err; /* @@ -575,13 +584,13 @@ static void test_user_set_mpam_reg(struct kvm_vcpu *vcpu) #define MTE_IDREG_TEST 1 static void test_user_set_mte_reg(struct kvm_vcpu *vcpu) { - uint64_t masks[KVM_ARM_FEATURE_ID_RANGE_SIZE]; + u64 masks[KVM_ARM_FEATURE_ID_RANGE_SIZE]; struct reg_mask_range range = { .addr = (__u64)masks, }; - uint64_t val; - uint64_t mte; - uint64_t mte_frac; + u64 val; + u64 mte; + u64 mte_frac; int idx, err; val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR1_EL1)); @@ -635,12 +644,38 @@ static void test_user_set_mte_reg(struct kvm_vcpu *vcpu) ksft_test_result_pass("ID_AA64PFR1_EL1.MTE_frac no longer 0xF\n"); } +static u64 reset_mutable_bits(u32 id, u64 val) +{ + struct test_feature_reg *reg = NULL; + + for (int i = 0; i < ARRAY_SIZE(test_regs); i++) { + if (test_regs[i].reg == id) { + reg = &test_regs[i]; + break; + } + } + + if (!reg) + return val; + + for (const struct reg_ftr_bits *bits = reg->ftr_bits; bits->type != FTR_END; bits++) { + if (bits->mutable) { + val &= ~bits->mask; + val |= bits->safe_val << bits->shift; + } + } + + return val; +} + static void test_guest_reg_read(struct kvm_vcpu *vcpu) { bool done = false; struct ucall uc; while (!done) { + u64 val; + vcpu_run(vcpu); switch (get_ucall(vcpu, &uc)) { @@ -648,9 +683,11 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu) REPORT_GUEST_ASSERT(uc); break; case UCALL_SYNC: + val = test_reg_vals[encoding_to_range_idx(uc.args[2])]; + val = reset_mutable_bits(uc.args[2], val); + /* Make sure the written values are seen by guest */ - TEST_ASSERT_EQ(test_reg_vals[encoding_to_range_idx(uc.args[2])], - uc.args[3]); + TEST_ASSERT_EQ(val, reset_mutable_bits(uc.args[2], uc.args[3])); break; case UCALL_DONE: done = true; @@ -670,7 +707,7 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu) static void test_clidr(struct kvm_vcpu *vcpu) { - uint64_t clidr; + u64 clidr; int level; clidr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_CLIDR_EL1)); @@ -735,13 +772,14 @@ static void test_vcpu_non_ftr_id_regs(struct kvm_vcpu *vcpu) ksft_test_result_pass("%s\n", __func__); } -static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, uint32_t encoding) +static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, u32 encoding) { size_t idx = encoding_to_range_idx(encoding); - uint64_t observed; + u64 observed; observed = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(encoding)); - TEST_ASSERT_EQ(test_reg_vals[idx], observed); + TEST_ASSERT_EQ(reset_mutable_bits(encoding, test_reg_vals[idx]), + reset_mutable_bits(encoding, observed)); } static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu) @@ -770,7 +808,7 @@ int main(void) struct kvm_vcpu *vcpu; struct kvm_vm *vm; bool aarch64_only; - uint64_t val, el0; + u64 val, el0; int test_cnt, i, j; TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES)); diff --git a/tools/testing/selftests/kvm/arm64/smccc_filter.c b/tools/testing/selftests/kvm/arm64/smccc_filter.c index 1763b9d45400..21e41880261b 100644 --- a/tools/testing/selftests/kvm/arm64/smccc_filter.c +++ b/tools/testing/selftests/kvm/arm64/smccc_filter.c @@ -37,7 +37,7 @@ static bool test_runs_at_el2(void) for (conduit = test_runs_at_el2() ? SMC_INSN : HVC_INSN; \ conduit <= SMC_INSN; conduit++) -static void guest_main(uint32_t func_id, enum smccc_conduit conduit) +static void guest_main(u32 func_id, enum smccc_conduit conduit) { struct arm_smccc_res res; @@ -49,7 +49,7 @@ static void guest_main(uint32_t func_id, enum smccc_conduit conduit) GUEST_SYNC(res.a0); } -static int __set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t nr_functions, +static int __set_smccc_filter(struct kvm_vm *vm, u32 start, u32 nr_functions, enum kvm_smccc_filter_action action) { struct kvm_smccc_filter filter = { @@ -62,7 +62,7 @@ static int __set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t nr_fun KVM_ARM_VM_SMCCC_FILTER, &filter); } -static void set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t nr_functions, +static void set_smccc_filter(struct kvm_vm *vm, u32 start, u32 nr_functions, enum kvm_smccc_filter_action action) { int ret = __set_smccc_filter(vm, start, nr_functions, action); @@ -112,7 +112,7 @@ static void test_filter_reserved_range(void) { struct kvm_vcpu *vcpu; struct kvm_vm *vm = setup_vm(&vcpu); - uint32_t smc64_fn; + u32 smc64_fn; int r; r = __set_smccc_filter(vm, ARM_SMCCC_ARCH_WORKAROUND_1, @@ -217,7 +217,7 @@ static void test_filter_denied(void) } } -static void expect_call_fwd_to_user(struct kvm_vcpu *vcpu, uint32_t func_id, +static void expect_call_fwd_to_user(struct kvm_vcpu *vcpu, u32 func_id, enum smccc_conduit conduit) { struct kvm_run *run = vcpu->run; diff --git a/tools/testing/selftests/kvm/arm64/vgic_init.c b/tools/testing/selftests/kvm/arm64/vgic_init.c index 8d6d3a4ae4db..47e34b43afb2 100644 --- a/tools/testing/selftests/kvm/arm64/vgic_init.c +++ b/tools/testing/selftests/kvm/arm64/vgic_init.c @@ -19,7 +19,7 @@ #define NR_VCPUS 4 -#define REG_OFFSET(vcpu, offset) (((uint64_t)vcpu << 32) | offset) +#define REG_OFFSET(vcpu, offset) (((u64)vcpu << 32) | offset) #define VGIC_DEV_IS_V2(_d) ((_d) == KVM_DEV_TYPE_ARM_VGIC_V2) #define VGIC_DEV_IS_V3(_d) ((_d) == KVM_DEV_TYPE_ARM_VGIC_V3) @@ -27,10 +27,10 @@ struct vm_gic { struct kvm_vm *vm; int gic_fd; - uint32_t gic_dev_type; + u32 gic_dev_type; }; -static uint64_t max_phys_size; +static u64 max_phys_size; /* * Helpers to access a redistributor register and verify the ioctl() failed or @@ -39,17 +39,17 @@ static uint64_t max_phys_size; static void v3_redist_reg_get_errno(int gicv3_fd, int vcpu, int offset, int want, const char *msg) { - uint32_t ignored_val; + u32 ignored_val; int ret = __kvm_device_attr_get(gicv3_fd, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, REG_OFFSET(vcpu, offset), &ignored_val); TEST_ASSERT(ret && errno == want, "%s; want errno = %d", msg, want); } -static void v3_redist_reg_get(int gicv3_fd, int vcpu, int offset, uint32_t want, +static void v3_redist_reg_get(int gicv3_fd, int vcpu, int offset, u32 want, const char *msg) { - uint32_t val; + u32 val; kvm_device_attr_get(gicv3_fd, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, REG_OFFSET(vcpu, offset), &val); @@ -71,8 +71,8 @@ static int run_vcpu(struct kvm_vcpu *vcpu) return __vcpu_run(vcpu) ? -errno : 0; } -static struct vm_gic vm_gic_create_with_vcpus(uint32_t gic_dev_type, - uint32_t nr_vcpus, +static struct vm_gic vm_gic_create_with_vcpus(u32 gic_dev_type, + u32 nr_vcpus, struct kvm_vcpu *vcpus[]) { struct vm_gic v; @@ -84,7 +84,7 @@ static struct vm_gic vm_gic_create_with_vcpus(uint32_t gic_dev_type, return v; } -static struct vm_gic vm_gic_create_barebones(uint32_t gic_dev_type) +static struct vm_gic vm_gic_create_barebones(u32 gic_dev_type) { struct vm_gic v; @@ -103,9 +103,9 @@ static void vm_gic_destroy(struct vm_gic *v) } struct vgic_region_attr { - uint64_t attr; - uint64_t size; - uint64_t alignment; + u64 attr; + u64 size; + u64 alignment; }; struct vgic_region_attr gic_v3_dist_region = { @@ -143,7 +143,7 @@ struct vgic_region_attr gic_v2_cpu_region = { static void subtest_dist_rdist(struct vm_gic *v) { int ret; - uint64_t addr; + u64 addr; struct vgic_region_attr rdist; /* CPU interface in GICv2*/ struct vgic_region_attr dist; @@ -223,7 +223,7 @@ static void subtest_dist_rdist(struct vm_gic *v) /* Test the new REDIST region API */ static void subtest_v3_redist_regions(struct vm_gic *v) { - uint64_t addr, expected_addr; + u64 addr, expected_addr; int ret; ret = __kvm_has_device_attr(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, @@ -332,7 +332,7 @@ static void subtest_v3_redist_regions(struct vm_gic *v) * VGIC KVM device is created and initialized before the secondary CPUs * get created */ -static void test_vgic_then_vcpus(uint32_t gic_dev_type) +static void test_vgic_then_vcpus(u32 gic_dev_type) { struct kvm_vcpu *vcpus[NR_VCPUS]; struct vm_gic v; @@ -353,7 +353,7 @@ static void test_vgic_then_vcpus(uint32_t gic_dev_type) } /* All the VCPUs are created before the VGIC KVM device gets initialized */ -static void test_vcpus_then_vgic(uint32_t gic_dev_type) +static void test_vcpus_then_vgic(u32 gic_dev_type) { struct kvm_vcpu *vcpus[NR_VCPUS]; struct vm_gic v; @@ -408,7 +408,7 @@ static void test_v3_new_redist_regions(void) struct kvm_vcpu *vcpus[NR_VCPUS]; void *dummy = NULL; struct vm_gic v; - uint64_t addr; + u64 addr; int ret; v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus); @@ -460,7 +460,7 @@ static void test_v3_new_redist_regions(void) static void test_v3_typer_accesses(void) { struct vm_gic v; - uint64_t addr; + u64 addr; int ret, i; v.vm = vm_create(NR_VCPUS); @@ -518,7 +518,7 @@ static void test_v3_typer_accesses(void) } static struct vm_gic vm_gic_v3_create_with_vcpuids(int nr_vcpus, - uint32_t vcpuids[]) + u32 vcpuids[]) { struct vm_gic v; int i; @@ -544,9 +544,9 @@ static struct vm_gic vm_gic_v3_create_with_vcpuids(int nr_vcpus, */ static void test_v3_last_bit_redist_regions(void) { - uint32_t vcpuids[] = { 0, 3, 5, 4, 1, 2 }; + u32 vcpuids[] = { 0, 3, 5, 4, 1, 2 }; struct vm_gic v; - uint64_t addr; + u64 addr; v = vm_gic_v3_create_with_vcpuids(ARRAY_SIZE(vcpuids), vcpuids); @@ -578,9 +578,9 @@ static void test_v3_last_bit_redist_regions(void) /* Test last bit with legacy region */ static void test_v3_last_bit_single_rdist(void) { - uint32_t vcpuids[] = { 0, 3, 5, 4, 1, 2 }; + u32 vcpuids[] = { 0, 3, 5, 4, 1, 2 }; struct vm_gic v; - uint64_t addr; + u64 addr; v = vm_gic_v3_create_with_vcpuids(ARRAY_SIZE(vcpuids), vcpuids); @@ -606,7 +606,7 @@ static void test_v3_redist_ipa_range_check_at_vcpu_run(void) struct kvm_vcpu *vcpus[NR_VCPUS]; struct vm_gic v; int ret, i; - uint64_t addr; + u64 addr; v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, 1, vcpus); @@ -638,7 +638,7 @@ static void test_v3_its_region(void) { struct kvm_vcpu *vcpus[NR_VCPUS]; struct vm_gic v; - uint64_t addr; + u64 addr; int its_fd, ret; v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus); @@ -717,11 +717,11 @@ static void test_v3_nassgicap(void) /* * Returns 0 if it's possible to create GIC device of a given type (V2 or V3). */ -int test_kvm_device(uint32_t gic_dev_type) +int test_kvm_device(u32 gic_dev_type) { struct kvm_vcpu *vcpus[NR_VCPUS]; struct vm_gic v; - uint32_t other; + u32 other; int ret; v.vm = vm_create_with_vcpus(NR_VCPUS, guest_code, vcpus); @@ -968,7 +968,7 @@ static void test_v3_sysregs(void) kvm_vm_free(vm); } -void run_tests(uint32_t gic_dev_type) +void run_tests(u32 gic_dev_type) { test_vcpus_then_vgic(gic_dev_type); test_vgic_then_vcpus(gic_dev_type); diff --git a/tools/testing/selftests/kvm/arm64/vgic_irq.c b/tools/testing/selftests/kvm/arm64/vgic_irq.c index 2fb2c7939fe9..5e231998617e 100644 --- a/tools/testing/selftests/kvm/arm64/vgic_irq.c +++ b/tools/testing/selftests/kvm/arm64/vgic_irq.c @@ -24,12 +24,12 @@ * function. */ struct test_args { - uint32_t nr_irqs; /* number of KVM supported IRQs. */ + u32 nr_irqs; /* number of KVM supported IRQs. */ bool eoi_split; /* 1 is eoir+dir, 0 is eoir only */ bool level_sensitive; /* 1 is level, 0 is edge */ int kvm_max_routes; /* output of KVM_CAP_IRQ_ROUTING */ bool kvm_supports_irqfd; /* output of KVM_CAP_IRQFD */ - uint32_t shared_data; + u32 shared_data; }; /* @@ -64,15 +64,15 @@ typedef enum { struct kvm_inject_args { kvm_inject_cmd cmd; - uint32_t first_intid; - uint32_t num; + u32 first_intid; + u32 num; int level; bool expect_failure; }; /* Used on the guest side to perform the hypercall. */ -static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, - uint32_t num, int level, bool expect_failure); +static void kvm_inject_call(kvm_inject_cmd cmd, u32 first_intid, + u32 num, int level, bool expect_failure); /* Used on the host side to get the hypercall info. */ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, @@ -133,8 +133,8 @@ static struct kvm_inject_desc set_active_fns[] = { for_each_supported_inject_fn((args), (t), (f)) /* Shared between the guest main thread and the IRQ handlers. */ -volatile uint64_t irq_handled; -volatile uint32_t irqnr_received[MAX_SPI + 1]; +volatile u64 irq_handled; +volatile u32 irqnr_received[MAX_SPI + 1]; static void reset_stats(void) { @@ -145,25 +145,25 @@ static void reset_stats(void) irqnr_received[i] = 0; } -static uint64_t gic_read_ap1r0(void) +static u64 gic_read_ap1r0(void) { - uint64_t reg = read_sysreg_s(SYS_ICC_AP1R0_EL1); + u64 reg = read_sysreg_s(SYS_ICC_AP1R0_EL1); dsb(sy); return reg; } -static void gic_write_ap1r0(uint64_t val) +static void gic_write_ap1r0(u64 val) { write_sysreg_s(val, SYS_ICC_AP1R0_EL1); isb(); } -static void guest_set_irq_line(uint32_t intid, uint32_t level); +static void guest_set_irq_line(u32 intid, u32 level); static void guest_irq_generic_handler(bool eoi_split, bool level_sensitive) { - uint32_t intid = gic_get_and_ack_irq(); + u32 intid = gic_get_and_ack_irq(); if (intid == IAR_SPURIOUS) return; @@ -189,8 +189,8 @@ static void guest_irq_generic_handler(bool eoi_split, bool level_sensitive) GUEST_ASSERT(!gic_irq_get_pending(intid)); } -static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, - uint32_t num, int level, bool expect_failure) +static void kvm_inject_call(kvm_inject_cmd cmd, u32 first_intid, + u32 num, int level, bool expect_failure) { struct kvm_inject_args args = { .cmd = cmd, @@ -204,7 +204,7 @@ static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, #define GUEST_ASSERT_IAR_EMPTY() \ do { \ - uint32_t _intid; \ + u32 _intid; \ _intid = gic_get_and_ack_irq(); \ GUEST_ASSERT(_intid == IAR_SPURIOUS); \ } while (0) @@ -237,13 +237,13 @@ static void reset_priorities(struct test_args *args) gic_set_priority(i, IRQ_DEFAULT_PRIO_REG); } -static void guest_set_irq_line(uint32_t intid, uint32_t level) +static void guest_set_irq_line(u32 intid, u32 level) { kvm_inject_call(KVM_SET_IRQ_LINE, intid, 1, level, false); } static void test_inject_fail(struct test_args *args, - uint32_t intid, kvm_inject_cmd cmd) + u32 intid, kvm_inject_cmd cmd) { reset_stats(); @@ -255,10 +255,10 @@ static void test_inject_fail(struct test_args *args, } static void guest_inject(struct test_args *args, - uint32_t first_intid, uint32_t num, - kvm_inject_cmd cmd) + u32 first_intid, u32 num, + kvm_inject_cmd cmd) { - uint32_t i; + u32 i; reset_stats(); @@ -292,10 +292,10 @@ static void guest_inject(struct test_args *args, * deactivated yet. */ static void guest_restore_active(struct test_args *args, - uint32_t first_intid, uint32_t num, - kvm_inject_cmd cmd) + u32 first_intid, u32 num, + kvm_inject_cmd cmd) { - uint32_t prio, intid, ap1r; + u32 prio, intid, ap1r; int i; /* @@ -342,9 +342,9 @@ static void guest_restore_active(struct test_args *args, * This function should only be used in test_inject_preemption (with IRQs * masked). */ -static uint32_t wait_for_and_activate_irq(void) +static u32 wait_for_and_activate_irq(void) { - uint32_t intid; + u32 intid; do { asm volatile("wfi" : : : "memory"); @@ -360,11 +360,11 @@ static uint32_t wait_for_and_activate_irq(void) * interrupts for the whole test. */ static void test_inject_preemption(struct test_args *args, - uint32_t first_intid, int num, + u32 first_intid, int num, const unsigned long *exclude, kvm_inject_cmd cmd) { - uint32_t intid, prio, step = KVM_PRIO_STEPS; + u32 intid, prio, step = KVM_PRIO_STEPS; int i; /* Set the priorities of the first (KVM_NUM_PRIOS - 1) IRQs @@ -379,7 +379,7 @@ static void test_inject_preemption(struct test_args *args, local_irq_disable(); for (i = 0; i < num; i++) { - uint32_t tmp; + u32 tmp; intid = i + first_intid; if (exclude && test_bit(i, exclude)) @@ -431,7 +431,7 @@ static void test_inject_preemption(struct test_args *args, static void test_injection(struct test_args *args, struct kvm_inject_desc *f) { - uint32_t nr_irqs = args->nr_irqs; + u32 nr_irqs = args->nr_irqs; if (f->sgi) { guest_inject(args, MIN_SGI, 1, f->cmd); @@ -451,7 +451,7 @@ static void test_injection(struct test_args *args, struct kvm_inject_desc *f) static void test_injection_failure(struct test_args *args, struct kvm_inject_desc *f) { - uint32_t bad_intid[] = { args->nr_irqs, 1020, 1024, 1120, 5120, ~0U, }; + u32 bad_intid[] = { args->nr_irqs, 1020, 1024, 1120, 5120, ~0U, }; int i; for (i = 0; i < ARRAY_SIZE(bad_intid); i++) @@ -490,7 +490,7 @@ static void test_restore_active(struct test_args *args, struct kvm_inject_desc * static void guest_code(struct test_args *args) { - uint32_t i, nr_irqs = args->nr_irqs; + u32 i, nr_irqs = args->nr_irqs; bool level_sensitive = args->level_sensitive; struct kvm_inject_desc *f, *inject_fns; @@ -529,8 +529,8 @@ static void guest_code(struct test_args *args) GUEST_DONE(); } -static void kvm_irq_line_check(struct kvm_vm *vm, uint32_t intid, int level, - struct test_args *test_args, bool expect_failure) +static void kvm_irq_line_check(struct kvm_vm *vm, u32 intid, int level, + struct test_args *test_args, bool expect_failure) { int ret; @@ -548,8 +548,8 @@ static void kvm_irq_line_check(struct kvm_vm *vm, uint32_t intid, int level, } } -void kvm_irq_set_level_info_check(int gic_fd, uint32_t intid, int level, - bool expect_failure) +void kvm_irq_set_level_info_check(int gic_fd, u32 intid, int level, + bool expect_failure) { if (!expect_failure) { kvm_irq_set_level_info(gic_fd, intid, level); @@ -573,17 +573,18 @@ void kvm_irq_set_level_info_check(int gic_fd, uint32_t intid, int level, } static void kvm_set_gsi_routing_irqchip_check(struct kvm_vm *vm, - uint32_t intid, uint32_t num, uint32_t kvm_max_routes, - bool expect_failure) + u32 intid, u32 num, + u32 kvm_max_routes, + bool expect_failure) { struct kvm_irq_routing *routing; int ret; - uint64_t i; + u64 i; assert(num <= kvm_max_routes && kvm_max_routes <= KVM_MAX_IRQ_ROUTES); routing = kvm_gsi_routing_create(); - for (i = intid; i < (uint64_t)intid + num; i++) + for (i = intid; i < (u64)intid + num; i++) kvm_gsi_routing_irqchip_add(routing, i - MIN_SPI, i - MIN_SPI); if (!expect_failure) { @@ -591,7 +592,7 @@ static void kvm_set_gsi_routing_irqchip_check(struct kvm_vm *vm, } else { ret = _kvm_gsi_routing_write(vm, routing); /* The kernel only checks e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS */ - if (((uint64_t)intid + num - 1 - MIN_SPI) >= KVM_IRQCHIP_NUM_PINS) + if (((u64)intid + num - 1 - MIN_SPI) >= KVM_IRQCHIP_NUM_PINS) TEST_ASSERT(ret != 0 && errno == EINVAL, "Bad intid %u did not cause KVM_SET_GSI_ROUTING " "error: rc: %i errno: %i", intid, ret, errno); @@ -602,7 +603,7 @@ static void kvm_set_gsi_routing_irqchip_check(struct kvm_vm *vm, } } -static void kvm_irq_write_ispendr_check(int gic_fd, uint32_t intid, +static void kvm_irq_write_ispendr_check(int gic_fd, u32 intid, struct kvm_vcpu *vcpu, bool expect_failure) { @@ -618,13 +619,13 @@ static void kvm_irq_write_ispendr_check(int gic_fd, uint32_t intid, } static void kvm_routing_and_irqfd_check(struct kvm_vm *vm, - uint32_t intid, uint32_t num, uint32_t kvm_max_routes, - bool expect_failure) + u32 intid, u32 num, u32 kvm_max_routes, + bool expect_failure) { int fd[MAX_SPI]; - uint64_t val; + u64 val; int ret, f; - uint64_t i; + u64 i; /* * There is no way to try injecting an SGI or PPI as the interface @@ -643,29 +644,29 @@ static void kvm_routing_and_irqfd_check(struct kvm_vm *vm, * that no actual interrupt was injected for those cases. */ - for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) + for (f = 0, i = intid; i < (u64)intid + num; i++, f++) fd[f] = kvm_new_eventfd(); - for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { - assert(i <= (uint64_t)UINT_MAX); + for (f = 0, i = intid; i < (u64)intid + num; i++, f++) { + assert(i <= (u64)UINT_MAX); kvm_assign_irqfd(vm, i - MIN_SPI, fd[f]); } - for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { + for (f = 0, i = intid; i < (u64)intid + num; i++, f++) { val = 1; - ret = write(fd[f], &val, sizeof(uint64_t)); - TEST_ASSERT(ret == sizeof(uint64_t), + ret = write(fd[f], &val, sizeof(u64)); + TEST_ASSERT(ret == sizeof(u64), __KVM_SYSCALL_ERROR("write()", ret)); } - for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) + for (f = 0, i = intid; i < (u64)intid + num; i++, f++) kvm_close(fd[f]); } /* handles the valid case: intid=0xffffffff num=1 */ #define for_each_intid(first, num, tmp, i) \ for ((tmp) = (i) = (first); \ - (tmp) < (uint64_t)(first) + (uint64_t)(num); \ + (tmp) < (u64)(first) + (u64)(num); \ (tmp)++, (i)++) static void run_guest_cmd(struct kvm_vcpu *vcpu, int gic_fd, @@ -673,13 +674,13 @@ static void run_guest_cmd(struct kvm_vcpu *vcpu, int gic_fd, struct test_args *test_args) { kvm_inject_cmd cmd = inject_args->cmd; - uint32_t intid = inject_args->first_intid; - uint32_t num = inject_args->num; + u32 intid = inject_args->first_intid; + u32 num = inject_args->num; int level = inject_args->level; bool expect_failure = inject_args->expect_failure; struct kvm_vm *vm = vcpu->vm; - uint64_t tmp; - uint32_t i; + u64 tmp; + u32 i; /* handles the valid case: intid=0xffffffff num=1 */ assert(intid < UINT_MAX - num || num == 1); @@ -731,7 +732,7 @@ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, struct kvm_inject_args *args) { struct kvm_inject_args *kvm_args_hva; - vm_vaddr_t kvm_args_gva; + gva_t kvm_args_gva; kvm_args_gva = uc->args[1]; kvm_args_hva = (struct kvm_inject_args *)addr_gva2hva(vm, kvm_args_gva); @@ -745,14 +746,14 @@ static void print_args(struct test_args *args) args->eoi_split); } -static void test_vgic(uint32_t nr_irqs, bool level_sensitive, bool eoi_split) +static void test_vgic(u32 nr_irqs, bool level_sensitive, bool eoi_split) { struct ucall uc; int gic_fd; struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_inject_args inject_args; - vm_vaddr_t args_gva; + gva_t args_gva; struct test_args args = { .nr_irqs = nr_irqs, @@ -770,7 +771,7 @@ static void test_vgic(uint32_t nr_irqs, bool level_sensitive, bool eoi_split) vcpu_init_descriptor_tables(vcpu); /* Setup the guest args page (so it gets the args). */ - args_gva = vm_vaddr_alloc_page(vm); + args_gva = vm_alloc_page(vm); memcpy(addr_gva2hva(vm, args_gva), &args, sizeof(args)); vcpu_args_set(vcpu, 1, args_gva); @@ -810,7 +811,7 @@ static void guest_code_asym_dir(struct test_args *args, int cpuid) gic_set_priority_mask(CPU_PRIO_MASK); if (cpuid == 0) { - uint32_t intid; + u32 intid; local_irq_disable(); @@ -848,7 +849,7 @@ static void guest_code_asym_dir(struct test_args *args, int cpuid) static void guest_code_group_en(struct test_args *args, int cpuid) { - uint32_t intid; + u32 intid; gic_init(GIC_V3, 2); @@ -896,7 +897,7 @@ static void guest_code_group_en(struct test_args *args, int cpuid) static void guest_code_timer_spi(struct test_args *args, int cpuid) { - uint32_t intid; + u32 intid; u64 val; gic_init(GIC_V3, 2); @@ -986,7 +987,7 @@ static void test_vgic_two_cpus(void *gcode) struct kvm_vcpu *vcpus[2]; struct test_args args = {}; struct kvm_vm *vm; - vm_vaddr_t args_gva; + gva_t args_gva; int gic_fd, ret; vm = vm_create_with_vcpus(2, gcode, vcpus); @@ -996,7 +997,7 @@ static void test_vgic_two_cpus(void *gcode) vcpu_init_descriptor_tables(vcpus[1]); /* Setup the guest args page (so it gets the args). */ - args_gva = vm_vaddr_alloc_page(vm); + args_gva = vm_alloc_page(vm); memcpy(addr_gva2hva(vm, args_gva), &args, sizeof(args)); vcpu_args_set(vcpus[0], 2, args_gva, 0); vcpu_args_set(vcpus[1], 2, args_gva, 1); @@ -1033,7 +1034,7 @@ static void help(const char *name) int main(int argc, char **argv) { - uint32_t nr_irqs = 64; + u32 nr_irqs = 64; bool default_args = true; bool level_sensitive = false; int opt; diff --git a/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c b/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c index e857a605f577..d64d434d3f06 100644 --- a/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c +++ b/tools/testing/selftests/kvm/arm64/vgic_lpi_stress.c @@ -23,7 +23,7 @@ #define GIC_LPI_OFFSET 8192 static size_t nr_iterations = 1000; -static vm_paddr_t gpa_base; +static gpa_t gpa_base; static struct kvm_vm *vm; static struct kvm_vcpu **vcpus; @@ -35,14 +35,14 @@ static struct test_data { u32 nr_devices; u32 nr_event_ids; - vm_paddr_t device_table; - vm_paddr_t collection_table; - vm_paddr_t cmdq_base; + gpa_t device_table; + gpa_t collection_table; + gpa_t cmdq_base; void *cmdq_base_va; - vm_paddr_t itt_tables; + gpa_t itt_tables; - vm_paddr_t lpi_prop_table; - vm_paddr_t lpi_pend_tables; + gpa_t lpi_prop_table; + gpa_t lpi_pend_tables; } test_data = { .nr_cpus = 1, .nr_devices = 1, @@ -73,7 +73,7 @@ static void guest_setup_its_mappings(void) /* Round-robin the LPIs to all of the vCPUs in the VM */ coll_id = 0; for (device_id = 0; device_id < nr_devices; device_id++) { - vm_paddr_t itt_base = test_data.itt_tables + (device_id * SZ_64K); + gpa_t itt_base = test_data.itt_tables + (device_id * SZ_64K); its_send_mapd_cmd(test_data.cmdq_base_va, device_id, itt_base, SZ_64K, true); @@ -188,7 +188,7 @@ static void setup_test_data(void) size_t pages_per_64k = vm_calc_num_guest_pages(vm->mode, SZ_64K); u32 nr_devices = test_data.nr_devices; u32 nr_cpus = test_data.nr_cpus; - vm_paddr_t cmdq_base; + gpa_t cmdq_base; test_data.device_table = vm_phy_pages_alloc(vm, pages_per_64k, gpa_base, @@ -224,7 +224,7 @@ static void setup_gic(void) static void signal_lpi(u32 device_id, u32 event_id) { - vm_paddr_t db_addr = GITS_BASE_GPA + GITS_TRANSLATER; + gpa_t db_addr = GITS_BASE_GPA + GITS_TRANSLATER; struct kvm_msi msi = { .address_lo = db_addr, diff --git a/tools/testing/selftests/kvm/arm64/vgic_v5.c b/tools/testing/selftests/kvm/arm64/vgic_v5.c new file mode 100644 index 000000000000..d785b660d847 --- /dev/null +++ b/tools/testing/selftests/kvm/arm64/vgic_v5.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include + +#include + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" +#include "vgic.h" + +#define NR_VCPUS 1 + +struct vm_gic { + struct kvm_vm *vm; + int gic_fd; + u32 gic_dev_type; +}; + +static u64 max_phys_size; + +#define GUEST_CMD_IRQ_CDIA 10 +#define GUEST_CMD_IRQ_DIEOI 11 +#define GUEST_CMD_IS_AWAKE 12 +#define GUEST_CMD_IS_READY 13 + +static void guest_irq_handler(struct ex_regs *regs) +{ + bool valid; + u32 hwirq; + u64 ia; + static int count; + + /* + * We have pending interrupts. Should never actually enter WFI + * here! + */ + wfi(); + GUEST_SYNC(GUEST_CMD_IS_AWAKE); + + ia = gicr_insn(CDIA); + valid = GICV5_GICR_CDIA_VALID(ia); + + GUEST_SYNC(GUEST_CMD_IRQ_CDIA); + + if (!valid) + return; + + gsb_ack(); + isb(); + + hwirq = FIELD_GET(GICV5_GICR_CDIA_INTID, ia); + + gic_insn(hwirq, CDDI); + gic_insn(0, CDEOI); + + GUEST_SYNC(GUEST_CMD_IRQ_DIEOI); + + if (++count >= 2) + GUEST_DONE(); + + /* Ask for the next interrupt to be injected */ + GUEST_SYNC(GUEST_CMD_IS_READY); +} + +static void guest_code(void) +{ + local_irq_disable(); + + gicv5_cpu_enable_interrupts(); + local_irq_enable(); + + /* Enable the SW_PPI (3) */ + write_sysreg_s(BIT_ULL(3), SYS_ICC_PPI_ENABLER0_EL1); + + /* Ask for the first interrupt to be injected */ + GUEST_SYNC(GUEST_CMD_IS_READY); + + /* Loop forever waiting for interrupts */ + while (1); +} + + +/* we don't want to assert on run execution, hence that helper */ +static int run_vcpu(struct kvm_vcpu *vcpu) +{ + return __vcpu_run(vcpu) ? -errno : 0; +} + +static void vm_gic_destroy(struct vm_gic *v) +{ + close(v->gic_fd); + kvm_vm_free(v->vm); +} + +static void test_vgic_v5_ppis(u32 gic_dev_type) +{ + struct kvm_vcpu *vcpus[NR_VCPUS]; + struct ucall uc; + u64 user_ppis[2]; + struct vm_gic v; + int ret, i; + + v.gic_dev_type = gic_dev_type; + v.vm = __vm_create(VM_SHAPE_DEFAULT, NR_VCPUS, 0); + + v.gic_fd = kvm_create_device(v.vm, gic_dev_type); + + for (i = 0; i < NR_VCPUS; i++) + vcpus[i] = vm_vcpu_add(v.vm, i, guest_code); + + vm_init_descriptor_tables(v.vm); + vm_install_exception_handler(v.vm, VECTOR_IRQ_CURRENT, guest_irq_handler); + + for (i = 0; i < NR_VCPUS; i++) + vcpu_init_descriptor_tables(vcpus[i]); + + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); + + /* Read out the PPIs that user space is allowed to drive. */ + kvm_device_attr_get(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_USERSPACE_PPIS, &user_ppis); + + /* We should always be able to drive the SW_PPI. */ + TEST_ASSERT(user_ppis[0] & BIT(GICV5_ARCH_PPI_SW_PPI), + "SW_PPI is not drivable by userspace"); + + while (1) { + ret = run_vcpu(vcpus[0]); + + switch (get_ucall(vcpus[0], &uc)) { + case UCALL_SYNC: + /* + * The guest is ready for the next level change. Set + * high if ready, and lower if it has been consumed. + */ + if (uc.args[1] == GUEST_CMD_IS_READY || + uc.args[1] == GUEST_CMD_IRQ_DIEOI) { + u64 irq; + bool level = uc.args[1] == GUEST_CMD_IRQ_DIEOI ? 0 : 1; + + irq = FIELD_PREP(KVM_ARM_IRQ_NUM_MASK, 3); + irq |= KVM_ARM_IRQ_TYPE_PPI << KVM_ARM_IRQ_TYPE_SHIFT; + + _kvm_irq_line(v.vm, irq, level); + } else if (uc.args[1] == GUEST_CMD_IS_AWAKE) { + pr_info("Guest skipping WFI due to pending IRQ\n"); + } else if (uc.args[1] == GUEST_CMD_IRQ_CDIA) { + pr_info("Guest acknowledged IRQ\n"); + } + + continue; + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + break; + case UCALL_DONE: + goto done; + default: + TEST_FAIL("Unknown ucall %lu", uc.cmd); + } + } + +done: + TEST_ASSERT(ret == 0, "Failed to test GICv5 PPIs"); + + vm_gic_destroy(&v); +} + +/* + * Returns 0 if it's possible to create GIC device of a given type (V5). + */ +int test_kvm_device(u32 gic_dev_type) +{ + struct kvm_vcpu *vcpus[NR_VCPUS]; + struct vm_gic v; + int ret; + + v.vm = vm_create_with_vcpus(NR_VCPUS, guest_code, vcpus); + + /* try to create a non existing KVM device */ + ret = __kvm_test_create_device(v.vm, 0); + TEST_ASSERT(ret && errno == ENODEV, "unsupported device"); + + /* trial mode */ + ret = __kvm_test_create_device(v.vm, gic_dev_type); + if (ret) + return ret; + v.gic_fd = kvm_create_device(v.vm, gic_dev_type); + + ret = __kvm_create_device(v.vm, gic_dev_type); + TEST_ASSERT(ret < 0 && errno == EEXIST, "create GIC device twice"); + + vm_gic_destroy(&v); + + return 0; +} + +void run_tests(u32 gic_dev_type) +{ + pr_info("Test VGICv5 PPIs\n"); + test_vgic_v5_ppis(gic_dev_type); +} + +int main(int ac, char **av) +{ + int ret; + int pa_bits; + + test_disable_default_vgic(); + + pa_bits = vm_guest_mode_params[VM_MODE_DEFAULT].pa_bits; + max_phys_size = 1ULL << pa_bits; + + ret = test_kvm_device(KVM_DEV_TYPE_ARM_VGIC_V5); + if (ret) { + pr_info("No GICv5 support; Not running GIC_v5 tests.\n"); + exit(KSFT_SKIP); + } + + pr_info("Running VGIC_V5 tests.\n"); + run_tests(KVM_DEV_TYPE_ARM_VGIC_V5); + + return 0; +} diff --git a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c index ae36325c022f..22223395969e 100644 --- a/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c +++ b/tools/testing/selftests/kvm/arm64/vpmu_counter_access.c @@ -33,20 +33,20 @@ struct vpmu_vm { static struct vpmu_vm vpmu_vm; struct pmreg_sets { - uint64_t set_reg_id; - uint64_t clr_reg_id; + u64 set_reg_id; + u64 clr_reg_id; }; #define PMREG_SET(set, clr) {.set_reg_id = set, .clr_reg_id = clr} -static uint64_t get_pmcr_n(uint64_t pmcr) +static u64 get_pmcr_n(u64 pmcr) { return FIELD_GET(ARMV8_PMU_PMCR_N, pmcr); } -static uint64_t get_counters_mask(uint64_t n) +static u64 get_counters_mask(u64 n) { - uint64_t mask = BIT(ARMV8_PMU_CYCLE_IDX); + u64 mask = BIT(ARMV8_PMU_CYCLE_IDX); if (n) mask |= GENMASK(n - 1, 0); @@ -89,7 +89,7 @@ static inline void write_sel_evtyper(int sel, unsigned long val) static void pmu_disable_reset(void) { - uint64_t pmcr = read_sysreg(pmcr_el0); + u64 pmcr = read_sysreg(pmcr_el0); /* Reset all counters, disabling them */ pmcr &= ~ARMV8_PMU_PMCR_E; @@ -169,7 +169,7 @@ struct pmc_accessor pmc_accessors[] = { #define GUEST_ASSERT_BITMAP_REG(regname, mask, set_expected) \ { \ - uint64_t _tval = read_sysreg(regname); \ + u64 _tval = read_sysreg(regname); \ \ if (set_expected) \ __GUEST_ASSERT((_tval & mask), \ @@ -185,7 +185,7 @@ struct pmc_accessor pmc_accessors[] = { * Check if @mask bits in {PMCNTEN,PMINTEN,PMOVS}{SET,CLR} registers * are set or cleared as specified in @set_expected. */ -static void check_bitmap_pmu_regs(uint64_t mask, bool set_expected) +static void check_bitmap_pmu_regs(u64 mask, bool set_expected) { GUEST_ASSERT_BITMAP_REG(pmcntenset_el0, mask, set_expected); GUEST_ASSERT_BITMAP_REG(pmcntenclr_el0, mask, set_expected); @@ -207,7 +207,7 @@ static void check_bitmap_pmu_regs(uint64_t mask, bool set_expected) */ static void test_bitmap_pmu_regs(int pmc_idx, bool set_op) { - uint64_t pmcr_n, test_bit = BIT(pmc_idx); + u64 pmcr_n, test_bit = BIT(pmc_idx); bool set_expected = false; if (set_op) { @@ -232,7 +232,7 @@ static void test_bitmap_pmu_regs(int pmc_idx, bool set_op) */ static void test_access_pmc_regs(struct pmc_accessor *acc, int pmc_idx) { - uint64_t write_data, read_data; + u64 write_data, read_data; /* Disable all PMCs and reset all PMCs to zero. */ pmu_disable_reset(); @@ -287,11 +287,11 @@ static void test_access_pmc_regs(struct pmc_accessor *acc, int pmc_idx) } #define INVALID_EC (-1ul) -uint64_t expected_ec = INVALID_EC; +u64 expected_ec = INVALID_EC; static void guest_sync_handler(struct ex_regs *regs) { - uint64_t esr, ec; + u64 esr, ec; esr = read_sysreg(esr_el1); ec = ESR_ELx_EC(esr); @@ -351,9 +351,9 @@ static void test_access_invalid_pmc_regs(struct pmc_accessor *acc, int pmc_idx) * if reading/writing PMU registers for implemented or unimplemented * counters works as expected. */ -static void guest_code(uint64_t expected_pmcr_n) +static void guest_code(u64 expected_pmcr_n) { - uint64_t pmcr, pmcr_n, unimp_mask; + u64 pmcr, pmcr_n, unimp_mask; int i, pmc; __GUEST_ASSERT(expected_pmcr_n <= ARMV8_PMU_MAX_GENERAL_COUNTERS, @@ -402,12 +402,12 @@ static void guest_code(uint64_t expected_pmcr_n) static void create_vpmu_vm(void *guest_code) { struct kvm_vcpu_init init; - uint8_t pmuver, ec; - uint64_t dfr0, irq = 23; + u8 pmuver, ec; + u64 dfr0, irq = 23; struct kvm_device_attr irq_attr = { .group = KVM_ARM_VCPU_PMU_V3_CTRL, .attr = KVM_ARM_VCPU_PMU_V3_IRQ, - .addr = (uint64_t)&irq, + .addr = (u64)&irq, }; /* The test creates the vpmu_vm multiple times. Ensure a clean state */ @@ -443,7 +443,7 @@ static void destroy_vpmu_vm(void) kvm_vm_free(vpmu_vm.vm); } -static void run_vcpu(struct kvm_vcpu *vcpu, uint64_t pmcr_n) +static void run_vcpu(struct kvm_vcpu *vcpu, u64 pmcr_n) { struct ucall uc; @@ -489,9 +489,9 @@ static void test_create_vpmu_vm_with_nr_counters(unsigned int nr_counters, bool * Create a guest with one vCPU, set the PMCR_EL0.N for the vCPU to @pmcr_n, * and run the test. */ -static void run_access_test(uint64_t pmcr_n) +static void run_access_test(u64 pmcr_n) { - uint64_t sp; + u64 sp; struct kvm_vcpu *vcpu; struct kvm_vcpu_init init; @@ -514,7 +514,7 @@ static void run_access_test(uint64_t pmcr_n) aarch64_vcpu_setup(vcpu, &init); vcpu_init_descriptor_tables(vcpu); vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SP_EL1), sp); - vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.pc), (uint64_t)guest_code); + vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.pc), (u64)guest_code); run_vcpu(vcpu, pmcr_n); @@ -531,12 +531,12 @@ static struct pmreg_sets validity_check_reg_sets[] = { * Create a VM, and check if KVM handles the userspace accesses of * the PMU register sets in @validity_check_reg_sets[] correctly. */ -static void run_pmregs_validity_test(uint64_t pmcr_n) +static void run_pmregs_validity_test(u64 pmcr_n) { int i; struct kvm_vcpu *vcpu; - uint64_t set_reg_id, clr_reg_id, reg_val; - uint64_t valid_counters_mask, max_counters_mask; + u64 set_reg_id, clr_reg_id, reg_val; + u64 valid_counters_mask, max_counters_mask; test_create_vpmu_vm_with_nr_counters(pmcr_n, false); vcpu = vpmu_vm.vcpu; @@ -588,7 +588,7 @@ static void run_pmregs_validity_test(uint64_t pmcr_n) * the vCPU to @pmcr_n, which is larger than the host value. * The attempt should fail as @pmcr_n is too big to set for the vCPU. */ -static void run_error_test(uint64_t pmcr_n) +static void run_error_test(u64 pmcr_n) { pr_debug("Error test with pmcr_n %lu (larger than the host)\n", pmcr_n); @@ -600,9 +600,9 @@ static void run_error_test(uint64_t pmcr_n) * Return the default number of implemented PMU event counters excluding * the cycle counter (i.e. PMCR_EL0.N value) for the guest. */ -static uint64_t get_pmcr_n_limit(void) +static u64 get_pmcr_n_limit(void) { - uint64_t pmcr; + u64 pmcr; create_vpmu_vm(guest_code); pmcr = vcpu_get_reg(vpmu_vm.vcpu, KVM_ARM64_SYS_REG(SYS_PMCR_EL0)); @@ -624,7 +624,7 @@ static bool kvm_supports_nr_counters_attr(void) int main(void) { - uint64_t i, pmcr_n; + u64 i, pmcr_n; TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_PMU_V3)); TEST_REQUIRE(kvm_supports_vgic_v3()); diff --git a/tools/testing/selftests/kvm/coalesced_io_test.c b/tools/testing/selftests/kvm/coalesced_io_test.c index 60cb25454899..df4ed5e3877c 100644 --- a/tools/testing/selftests/kvm/coalesced_io_test.c +++ b/tools/testing/selftests/kvm/coalesced_io_test.c @@ -14,16 +14,16 @@ struct kvm_coalesced_io { struct kvm_coalesced_mmio_ring *ring; - uint32_t ring_size; - uint64_t mmio_gpa; - uint64_t *mmio; + u32 ring_size; + u64 mmio_gpa; + u64 *mmio; /* * x86-only, but define pio_port for all architectures to minimize the * amount of #ifdeffery and complexity, without having to sacrifice * verbose error messages. */ - uint8_t pio_port; + u8 pio_port; }; static struct kvm_coalesced_io kvm_builtin_io_ring; @@ -70,13 +70,13 @@ static void guest_code(struct kvm_coalesced_io *io) static void vcpu_run_and_verify_io_exit(struct kvm_vcpu *vcpu, struct kvm_coalesced_io *io, - uint32_t ring_start, - uint32_t expected_exit) + u32 ring_start, + u32 expected_exit) { const bool want_pio = expected_exit == KVM_EXIT_IO; struct kvm_coalesced_mmio_ring *ring = io->ring; struct kvm_run *run = vcpu->run; - uint32_t pio_value; + u32 pio_value; WRITE_ONCE(ring->first, ring_start); WRITE_ONCE(ring->last, ring_start); @@ -88,13 +88,13 @@ static void vcpu_run_and_verify_io_exit(struct kvm_vcpu *vcpu, * data_offset is garbage, e.g. an MMIO gpa. */ if (run->exit_reason == KVM_EXIT_IO) - pio_value = *(uint32_t *)((void *)run + run->io.data_offset); + pio_value = *(u32 *)((void *)run + run->io.data_offset); else pio_value = 0; TEST_ASSERT((!want_pio && (run->exit_reason == KVM_EXIT_MMIO && run->mmio.is_write && run->mmio.phys_addr == io->mmio_gpa && run->mmio.len == 8 && - *(uint64_t *)run->mmio.data == io->mmio_gpa + io->ring_size - 1)) || + *(u64 *)run->mmio.data == io->mmio_gpa + io->ring_size - 1)) || (want_pio && (run->exit_reason == KVM_EXIT_IO && run->io.port == io->pio_port && run->io.direction == KVM_EXIT_IO_OUT && run->io.count == 1 && pio_value == io->pio_port + io->ring_size - 1)), @@ -105,14 +105,14 @@ static void vcpu_run_and_verify_io_exit(struct kvm_vcpu *vcpu, want_pio ? (unsigned long long)io->pio_port : io->mmio_gpa, (want_pio ? io->pio_port : io->mmio_gpa) + io->ring_size - 1, run->exit_reason, run->exit_reason == KVM_EXIT_MMIO ? "MMIO" : run->exit_reason == KVM_EXIT_IO ? "PIO" : "other", - run->mmio.phys_addr, run->mmio.is_write, run->mmio.len, *(uint64_t *)run->mmio.data, + run->mmio.phys_addr, run->mmio.is_write, run->mmio.len, *(u64 *)run->mmio.data, run->io.port, run->io.direction, run->io.size, run->io.count, pio_value); } static void vcpu_run_and_verify_coalesced_io(struct kvm_vcpu *vcpu, struct kvm_coalesced_io *io, - uint32_t ring_start, - uint32_t expected_exit) + u32 ring_start, + u32 expected_exit) { struct kvm_coalesced_mmio_ring *ring = io->ring; int i; @@ -124,18 +124,18 @@ static void vcpu_run_and_verify_coalesced_io(struct kvm_vcpu *vcpu, ring->first, ring->last, io->ring_size, ring_start); for (i = 0; i < io->ring_size - 1; i++) { - uint32_t idx = (ring->first + i) % io->ring_size; + u32 idx = (ring->first + i) % io->ring_size; struct kvm_coalesced_mmio *entry = &ring->coalesced_mmio[idx]; #ifdef __x86_64__ if (i & 1) TEST_ASSERT(entry->phys_addr == io->pio_port && entry->len == 4 && entry->pio && - *(uint32_t *)entry->data == io->pio_port + i, + *(u32 *)entry->data == io->pio_port + i, "Wanted 4-byte port I/O 0x%x = 0x%x in entry %u, got %u-byte %s 0x%llx = 0x%x", io->pio_port, io->pio_port + i, i, entry->len, entry->pio ? "PIO" : "MMIO", - entry->phys_addr, *(uint32_t *)entry->data); + entry->phys_addr, *(u32 *)entry->data); else #endif TEST_ASSERT(entry->phys_addr == io->mmio_gpa && @@ -143,12 +143,12 @@ static void vcpu_run_and_verify_coalesced_io(struct kvm_vcpu *vcpu, "Wanted 8-byte MMIO to 0x%lx = %lx in entry %u, got %u-byte %s 0x%llx = 0x%lx", io->mmio_gpa, io->mmio_gpa + i, i, entry->len, entry->pio ? "PIO" : "MMIO", - entry->phys_addr, *(uint64_t *)entry->data); + entry->phys_addr, *(u64 *)entry->data); } } static void test_coalesced_io(struct kvm_vcpu *vcpu, - struct kvm_coalesced_io *io, uint32_t ring_start) + struct kvm_coalesced_io *io, u32 ring_start) { struct kvm_coalesced_mmio_ring *ring = io->ring; @@ -219,11 +219,11 @@ int main(int argc, char *argv[]) * the MMIO GPA identity mapped in the guest. */ .mmio_gpa = 4ull * SZ_1G, - .mmio = (uint64_t *)(4ull * SZ_1G), + .mmio = (u64 *)(4ull * SZ_1G), .pio_port = 0x80, }; - virt_map(vm, (uint64_t)kvm_builtin_io_ring.mmio, kvm_builtin_io_ring.mmio_gpa, 1); + virt_map(vm, (u64)kvm_builtin_io_ring.mmio, kvm_builtin_io_ring.mmio_gpa, 1); sync_global_to_guest(vm, kvm_builtin_io_ring); vcpu_args_set(vcpu, 1, &kvm_builtin_io_ring); diff --git a/tools/testing/selftests/kvm/demand_paging_test.c b/tools/testing/selftests/kvm/demand_paging_test.c index 0202b78f8680..302c4923d093 100644 --- a/tools/testing/selftests/kvm/demand_paging_test.c +++ b/tools/testing/selftests/kvm/demand_paging_test.c @@ -24,7 +24,7 @@ #ifdef __NR_userfaultfd static int nr_vcpus = 1; -static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE; +static u64 guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE; static size_t demand_paging_size; static char *guest_data_prototype; @@ -58,7 +58,7 @@ static int handle_uffd_page_request(int uffd_mode, int uffd, struct uffd_msg *msg) { pid_t tid = syscall(__NR_gettid); - uint64_t addr = msg->arg.pagefault.address; + u64 addr = msg->arg.pagefault.address; struct timespec start; struct timespec ts_diff; int r; @@ -68,7 +68,7 @@ static int handle_uffd_page_request(int uffd_mode, int uffd, if (uffd_mode == UFFDIO_REGISTER_MODE_MISSING) { struct uffdio_copy copy; - copy.src = (uint64_t)guest_data_prototype; + copy.src = (u64)guest_data_prototype; copy.dst = addr; copy.len = demand_paging_size; copy.mode = 0; @@ -138,7 +138,7 @@ struct test_params { bool partition_vcpu_memory_access; }; -static void prefault_mem(void *alias, uint64_t len) +static void prefault_mem(void *alias, u64 len) { size_t p; @@ -154,7 +154,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) struct memstress_vcpu_args *vcpu_args; struct test_params *p = arg; struct uffd_desc **uffd_descs = NULL; - uint64_t uffd_region_size; + u64 uffd_region_size; struct timespec start; struct timespec ts_diff; double vcpu_paging_rate; diff --git a/tools/testing/selftests/kvm/dirty_log_perf_test.c b/tools/testing/selftests/kvm/dirty_log_perf_test.c index 0a1ea1d1e2d8..ef779fa91827 100644 --- a/tools/testing/selftests/kvm/dirty_log_perf_test.c +++ b/tools/testing/selftests/kvm/dirty_log_perf_test.c @@ -24,7 +24,7 @@ #define TEST_HOST_LOOP_N 2UL static int nr_vcpus = 1; -static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE; +static u64 guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE; static bool run_vcpus_while_disabling_dirty_logging; /* Host variables */ @@ -37,7 +37,7 @@ static void vcpu_worker(struct memstress_vcpu_args *vcpu_args) { struct kvm_vcpu *vcpu = vcpu_args->vcpu; int vcpu_idx = vcpu_args->vcpu_idx; - uint64_t pages_count = 0; + u64 pages_count = 0; struct kvm_run *run; struct timespec start; struct timespec ts_diff; @@ -93,11 +93,11 @@ static void vcpu_worker(struct memstress_vcpu_args *vcpu_args) struct test_params { unsigned long iterations; - uint64_t phys_offset; + u64 phys_offset; bool partition_vcpu_memory_access; enum vm_mem_backing_src_type backing_src; int slots; - uint32_t write_percent; + u32 write_percent; bool random_access; }; @@ -106,9 +106,9 @@ static void run_test(enum vm_guest_mode mode, void *arg) struct test_params *p = arg; struct kvm_vm *vm; unsigned long **bitmaps; - uint64_t guest_num_pages; - uint64_t host_num_pages; - uint64_t pages_per_slot; + u64 guest_num_pages; + u64 host_num_pages; + u64 pages_per_slot; struct timespec start; struct timespec ts_diff; struct timespec get_dirty_log_total = (struct timespec){0}; diff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c index d58a641b0e6a..12446a4b6e8d 100644 --- a/tools/testing/selftests/kvm/dirty_log_test.c +++ b/tools/testing/selftests/kvm/dirty_log_test.c @@ -74,11 +74,11 @@ * the host. READ/WRITE_ONCE() should also be used with anything * that may change. */ -static uint64_t host_page_size; -static uint64_t guest_page_size; -static uint64_t guest_num_pages; -static uint64_t iteration; -static uint64_t nr_writes; +static u64 host_page_size; +static u64 guest_page_size; +static u64 guest_num_pages; +static u64 iteration; +static u64 nr_writes; static bool vcpu_stop; /* @@ -86,13 +86,13 @@ static bool vcpu_stop; * This will be set to the topmost valid physical address minus * the test memory size. */ -static uint64_t guest_test_phys_mem; +static u64 guest_test_phys_mem; /* * Guest virtual memory offset of the testing memory slot. * Must not conflict with identity mapped test code. */ -static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; +static u64 guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; /* * Continuously write to the first 8 bytes of a random pages within @@ -100,10 +100,10 @@ static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; */ static void guest_code(void) { - uint64_t addr; + u64 addr; #ifdef __s390x__ - uint64_t i; + u64 i; /* * On s390x, all pages of a 1M segment are initially marked as dirty @@ -113,7 +113,7 @@ static void guest_code(void) */ for (i = 0; i < guest_num_pages; i++) { addr = guest_test_virt_mem + i * guest_page_size; - vcpu_arch_put_guest(*(uint64_t *)addr, READ_ONCE(iteration)); + vcpu_arch_put_guest(*(u64 *)addr, READ_ONCE(iteration)); nr_writes++; } #endif @@ -125,7 +125,7 @@ static void guest_code(void) * guest_page_size; addr = align_down(addr, host_page_size); - vcpu_arch_put_guest(*(uint64_t *)addr, READ_ONCE(iteration)); + vcpu_arch_put_guest(*(u64 *)addr, READ_ONCE(iteration)); nr_writes++; } @@ -138,11 +138,11 @@ static bool host_quit; /* Points to the test VM memory region on which we track dirty logs */ static void *host_test_mem; -static uint64_t host_num_pages; +static u64 host_num_pages; /* For statistics only */ -static uint64_t host_dirty_count; -static uint64_t host_clear_count; +static u64 host_dirty_count; +static u64 host_clear_count; /* Whether dirty ring reset is requested, or finished */ static sem_t sem_vcpu_stop; @@ -169,7 +169,7 @@ static bool dirty_ring_vcpu_ring_full; * dirty gfn we've collected, so that if a mismatch of data found later in the * verifying process, we let it pass. */ -static uint64_t dirty_ring_last_page = -1ULL; +static u64 dirty_ring_last_page = -1ULL; /* * In addition to the above, it is possible (especially if this @@ -213,7 +213,7 @@ static uint64_t dirty_ring_last_page = -1ULL; * and also don't fail when it is reported in the next iteration, together with * an outdated iteration count. */ -static uint64_t dirty_ring_prev_iteration_last_page; +static u64 dirty_ring_prev_iteration_last_page; enum log_mode_t { /* Only use KVM_GET_DIRTY_LOG for logging */ @@ -236,7 +236,7 @@ static enum log_mode_t host_log_mode_option = LOG_MODE_ALL; /* Logging mode for current run */ static enum log_mode_t host_log_mode; static pthread_t vcpu_thread; -static uint32_t test_dirty_ring_count = TEST_DIRTY_RING_COUNT; +static u32 test_dirty_ring_count = TEST_DIRTY_RING_COUNT; static bool clear_log_supported(void) { @@ -255,15 +255,15 @@ static void clear_log_create_vm_done(struct kvm_vm *vm) } static void dirty_log_collect_dirty_pages(struct kvm_vcpu *vcpu, int slot, - void *bitmap, uint32_t num_pages, - uint32_t *unused) + void *bitmap, u32 num_pages, + u32 *unused) { kvm_vm_get_dirty_log(vcpu->vm, slot, bitmap); } static void clear_log_collect_dirty_pages(struct kvm_vcpu *vcpu, int slot, - void *bitmap, uint32_t num_pages, - uint32_t *unused) + void *bitmap, u32 num_pages, + u32 *unused) { kvm_vm_get_dirty_log(vcpu->vm, slot, bitmap); kvm_vm_clear_dirty_log(vcpu->vm, slot, bitmap, 0, num_pages); @@ -297,8 +297,8 @@ static bool dirty_ring_supported(void) static void dirty_ring_create_vm_done(struct kvm_vm *vm) { - uint64_t pages; - uint32_t limit; + u64 pages; + u32 limit; /* * We rely on vcpu exit due to full dirty ring state. Adjust @@ -333,12 +333,12 @@ static inline void dirty_gfn_set_collected(struct kvm_dirty_gfn *gfn) smp_store_release(&gfn->flags, KVM_DIRTY_GFN_F_RESET); } -static uint32_t dirty_ring_collect_one(struct kvm_dirty_gfn *dirty_gfns, - int slot, void *bitmap, - uint32_t num_pages, uint32_t *fetch_index) +static u32 dirty_ring_collect_one(struct kvm_dirty_gfn *dirty_gfns, + int slot, void *bitmap, + u32 num_pages, u32 *fetch_index) { struct kvm_dirty_gfn *cur; - uint32_t count = 0; + u32 count = 0; while (true) { cur = &dirty_gfns[*fetch_index % test_dirty_ring_count]; @@ -359,10 +359,10 @@ static uint32_t dirty_ring_collect_one(struct kvm_dirty_gfn *dirty_gfns, } static void dirty_ring_collect_dirty_pages(struct kvm_vcpu *vcpu, int slot, - void *bitmap, uint32_t num_pages, - uint32_t *ring_buf_idx) + void *bitmap, u32 num_pages, + u32 *ring_buf_idx) { - uint32_t count, cleared; + u32 count, cleared; /* Only have one vcpu */ count = dirty_ring_collect_one(vcpu_map_dirty_ring(vcpu), @@ -404,8 +404,8 @@ struct log_mode { void (*create_vm_done)(struct kvm_vm *vm); /* Hook to collect the dirty pages into the bitmap provided */ void (*collect_dirty_pages) (struct kvm_vcpu *vcpu, int slot, - void *bitmap, uint32_t num_pages, - uint32_t *ring_buf_idx); + void *bitmap, u32 num_pages, + u32 *ring_buf_idx); /* Hook to call when after each vcpu run */ void (*after_vcpu_run)(struct kvm_vcpu *vcpu); } log_modes[LOG_MODE_NUM] = { @@ -459,8 +459,8 @@ static void log_mode_create_vm_done(struct kvm_vm *vm) } static void log_mode_collect_dirty_pages(struct kvm_vcpu *vcpu, int slot, - void *bitmap, uint32_t num_pages, - uint32_t *ring_buf_idx) + void *bitmap, u32 num_pages, + u32 *ring_buf_idx) { struct log_mode *mode = &log_modes[host_log_mode]; @@ -494,11 +494,11 @@ static void *vcpu_worker(void *data) static void vm_dirty_log_verify(enum vm_guest_mode mode, unsigned long **bmap) { - uint64_t page, nr_dirty_pages = 0, nr_clean_pages = 0; - uint64_t step = vm_num_host_pages(mode, 1); + u64 page, nr_dirty_pages = 0, nr_clean_pages = 0; + u64 step = vm_num_host_pages(mode, 1); for (page = 0; page < host_num_pages; page += step) { - uint64_t val = *(uint64_t *)(host_test_mem + page * host_page_size); + u64 val = *(u64 *)(host_test_mem + page * host_page_size); bool bmap0_dirty = __test_and_clear_bit_le(page, bmap[0]); /* @@ -575,7 +575,7 @@ static void vm_dirty_log_verify(enum vm_guest_mode mode, unsigned long **bmap) } static struct kvm_vm *create_vm(enum vm_guest_mode mode, struct kvm_vcpu **vcpu, - uint64_t extra_mem_pages, void *guest_code) + u64 extra_mem_pages, void *guest_code) { struct kvm_vm *vm; @@ -592,7 +592,7 @@ static struct kvm_vm *create_vm(enum vm_guest_mode mode, struct kvm_vcpu **vcpu, struct test_params { unsigned long iterations; unsigned long interval; - uint64_t phys_offset; + u64 phys_offset; }; static void run_test(enum vm_guest_mode mode, void *arg) @@ -601,7 +601,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) struct kvm_vcpu *vcpu; struct kvm_vm *vm; unsigned long *bmap[2]; - uint32_t ring_buf_idx = 0; + u32 ring_buf_idx = 0; int sem_val; if (!log_mode_supported()) { @@ -641,9 +641,6 @@ static void run_test(enum vm_guest_mode mode, void *arg) } #ifdef __s390x__ - /* Align to 1M (segment size) */ - guest_test_phys_mem = align_down(guest_test_phys_mem, 1 << 20); - /* * The workaround in guest_code() to write all pages prior to the first * iteration isn't compatible with the dirty ring, as the dirty ring @@ -670,7 +667,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, guest_num_pages); /* Cache the HVA pointer of the region */ - host_test_mem = addr_gpa2hva(vm, (vm_paddr_t)guest_test_phys_mem); + host_test_mem = addr_gpa2hva(vm, (gpa_t)guest_test_phys_mem); /* Export the shared variables to the guest */ sync_global_to_guest(vm, host_page_size); diff --git a/tools/testing/selftests/kvm/get-reg-list.c b/tools/testing/selftests/kvm/get-reg-list.c index f4644c9d2d3b..216f10644c1a 100644 --- a/tools/testing/selftests/kvm/get-reg-list.c +++ b/tools/testing/selftests/kvm/get-reg-list.c @@ -216,7 +216,7 @@ static void run_test(struct vcpu_reg_list *c) * since we don't know the capabilities of any new registers. */ for_each_present_blessed_reg(i) { - uint8_t addr[2048 / 8]; + u8 addr[2048 / 8]; struct kvm_one_reg reg = { .id = reg_list->reg[i], .addr = (__u64)&addr, diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c index cc329b57ce2e..253e748c1d4a 100644 --- a/tools/testing/selftests/kvm/guest_memfd_test.c +++ b/tools/testing/selftests/kvm/guest_memfd_test.c @@ -171,6 +171,64 @@ static void test_numa_allocation(int fd, size_t total_size) kvm_munmap(mem, total_size); } +static void test_collapse(int fd, u64 flags) +{ + const size_t pmd_size = get_trans_hugepagesz(); + void *reserved_addr; + void *aligned_addr; + char *mem; + off_t i; + + /* + * To even reach the point where the guest_memfd folios will + * get collapsed, both the userspace address and the offset + * within the guest_memfd have to be aligned to pmd_size. + * + * To achieve that alignment, reserve virtual address space + * with regular mmap, then use MAP_FIXED to allocate memory + * from a pmd_size-aligned offset (0) at a known, available + * virtual address. + */ + reserved_addr = kvm_mmap(pmd_size * 2, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + aligned_addr = align_ptr_up(reserved_addr, pmd_size); + + mem = mmap(aligned_addr, pmd_size, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_SHARED, fd, 0); + TEST_ASSERT(IS_ALIGNED((u64)mem, pmd_size), + "Userspace address must be aligned to PMD size."); + + /* + * Use reads to populate page table to avoid setting dirty + * flag on page. + */ + for (i = 0; i < pmd_size; i += getpagesize()) + READ_ONCE(mem[i]); + + /* + * Advising the use of huge pages in guest_memfd should be + * fine... + */ + kvm_madvise(mem, pmd_size, MADV_HUGEPAGE); + + /* + * ... but collapsing folios must not be supported to avoid + * mapping beyond shared ranges into host userspace page + * tables. + */ + TEST_ASSERT_EQ(madvise(mem, pmd_size, MADV_COLLAPSE), -1); + TEST_ASSERT_EQ(errno, EINVAL); + + /* + * Removing from host page tables and re-faulting should be + * fine; should not end up faulting in a collapsed/huge folio. + */ + kvm_madvise(mem, pmd_size, MADV_DONTNEED); + READ_ONCE(mem[0]); + + kvm_munmap(reserved_addr, pmd_size * 2); +} + static void test_fault_sigbus(int fd, size_t accessible_size, size_t map_size) { const char val = 0xaa; @@ -288,7 +346,7 @@ static void test_invalid_punch_hole(int fd, size_t total_size) } static void test_create_guest_memfd_invalid_sizes(struct kvm_vm *vm, - uint64_t guest_memfd_flags) + u64 guest_memfd_flags) { size_t size; int fd; @@ -331,8 +389,8 @@ static void test_create_guest_memfd_multiple(struct kvm_vm *vm) static void test_guest_memfd_flags(struct kvm_vm *vm) { - uint64_t valid_flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS); - uint64_t flag; + u64 valid_flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS); + u64 flag; int fd; for (flag = BIT(0); flag; flag <<= 1) { @@ -350,15 +408,18 @@ static void test_guest_memfd_flags(struct kvm_vm *vm) } } -#define gmem_test(__test, __vm, __flags) \ +#define __gmem_test(__test, __vm, __flags, __gmem_size) \ do { \ - int fd = vm_create_guest_memfd(__vm, page_size * 4, __flags); \ + int fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \ \ - test_##__test(fd, page_size * 4); \ + test_##__test(fd, __gmem_size); \ close(fd); \ } while (0) -static void __test_guest_memfd(struct kvm_vm *vm, uint64_t flags) +#define gmem_test(__test, __vm, __flags) \ + __gmem_test(__test, __vm, __flags, page_size * 4) + +static void __test_guest_memfd(struct kvm_vm *vm, u64 flags) { test_create_guest_memfd_multiple(vm); test_create_guest_memfd_invalid_sizes(vm, flags); @@ -367,9 +428,12 @@ static void __test_guest_memfd(struct kvm_vm *vm, uint64_t flags) if (flags & GUEST_MEMFD_FLAG_MMAP) { if (flags & GUEST_MEMFD_FLAG_INIT_SHARED) { + size_t pmd_size = get_trans_hugepagesz(); + gmem_test(mmap_supported, vm, flags); gmem_test(fault_overflow, vm, flags); gmem_test(numa_allocation, vm, flags); + __gmem_test(collapse, vm, flags, pmd_size); } else { gmem_test(fault_private, vm, flags); } @@ -388,7 +452,7 @@ static void __test_guest_memfd(struct kvm_vm *vm, uint64_t flags) static void test_guest_memfd(unsigned long vm_type) { struct kvm_vm *vm = vm_create_barebones_type(vm_type); - uint64_t flags; + u64 flags; test_guest_memfd_flags(vm); @@ -406,7 +470,7 @@ static void test_guest_memfd(unsigned long vm_type) kvm_vm_free(vm); } -static void guest_code(uint8_t *mem, uint64_t size) +static void guest_code(u8 *mem, u64 size) { size_t i; @@ -425,12 +489,12 @@ static void test_guest_memfd_guest(void) * the guest's code, stack, and page tables, and low memory contains * the PCI hole and other MMIO regions that need to be avoided. */ - const uint64_t gpa = SZ_4G; + const gpa_t gpa = SZ_4G; const int slot = 1; struct kvm_vcpu *vcpu; struct kvm_vm *vm; - uint8_t *mem; + u8 *mem; size_t size; int fd, i; @@ -446,7 +510,12 @@ static void test_guest_memfd_guest(void) "Default VM type should support INIT_SHARED, supported flags = 0x%x", vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS)); - size = vm->page_size; + /* + * Use the max of the host or guest page size for all operations, as + * KVM requires guest_memfd files and memslots to be sized to multiples + * of the host page size. + */ + size = max_t(size_t, vm->page_size, page_size); fd = vm_create_guest_memfd(vm, size, GUEST_MEMFD_FLAG_MMAP | GUEST_MEMFD_FLAG_INIT_SHARED); vm_set_user_memory_region2(vm, slot, KVM_MEM_GUEST_MEMFD, gpa, size, NULL, fd, 0); @@ -455,7 +524,7 @@ static void test_guest_memfd_guest(void) memset(mem, 0xaa, size); kvm_munmap(mem, size); - virt_pg_map(vm, gpa, gpa); + virt_map(vm, gpa, gpa, size / vm->page_size); vcpu_args_set(vcpu, 2, gpa, size); vcpu_run(vcpu); diff --git a/tools/testing/selftests/kvm/guest_print_test.c b/tools/testing/selftests/kvm/guest_print_test.c index bcf582852db9..79d3fc326e91 100644 --- a/tools/testing/selftests/kvm/guest_print_test.c +++ b/tools/testing/selftests/kvm/guest_print_test.c @@ -16,22 +16,22 @@ #include "ucall_common.h" struct guest_vals { - uint64_t a; - uint64_t b; - uint64_t type; + u64 a; + u64 b; + u64 type; }; static struct guest_vals vals; /* GUEST_PRINTF()/GUEST_ASSERT_FMT() does not support float or double. */ #define TYPE_LIST \ -TYPE(test_type_i64, I64, "%ld", int64_t) \ -TYPE(test_type_u64, U64u, "%lu", uint64_t) \ -TYPE(test_type_x64, U64x, "0x%lx", uint64_t) \ -TYPE(test_type_X64, U64X, "0x%lX", uint64_t) \ -TYPE(test_type_u32, U32u, "%u", uint32_t) \ -TYPE(test_type_x32, U32x, "0x%x", uint32_t) \ -TYPE(test_type_X32, U32X, "0x%X", uint32_t) \ +TYPE(test_type_i64, I64, "%ld", s64) \ +TYPE(test_type_u64, U64u, "%lu", u64) \ +TYPE(test_type_x64, U64x, "0x%lx", u64) \ +TYPE(test_type_X64, U64X, "0x%lX", u64) \ +TYPE(test_type_u32, U32u, "%u", u32) \ +TYPE(test_type_x32, U32x, "0x%x", u32) \ +TYPE(test_type_X32, U32X, "0x%X", u32) \ TYPE(test_type_int, INT, "%d", int) \ TYPE(test_type_char, CHAR, "%c", char) \ TYPE(test_type_str, STR, "'%s'", const char *) \ @@ -56,7 +56,7 @@ static void fn(struct kvm_vcpu *vcpu, T a, T b) \ \ snprintf(expected_printf, UCALL_BUFFER_LEN, PRINTF_FMT_##ext, a, b); \ snprintf(expected_assert, UCALL_BUFFER_LEN, ASSERT_FMT_##ext, a, b); \ - vals = (struct guest_vals){ (uint64_t)a, (uint64_t)b, TYPE_##ext }; \ + vals = (struct guest_vals){ (u64)a, (u64)b, TYPE_##ext }; \ sync_global_to_guest(vcpu->vm, vals); \ run_test(vcpu, expected_printf, expected_assert); \ } diff --git a/tools/testing/selftests/kvm/hardware_disable_test.c b/tools/testing/selftests/kvm/hardware_disable_test.c index 94bd6ed24cf3..3147f5c97e94 100644 --- a/tools/testing/selftests/kvm/hardware_disable_test.c +++ b/tools/testing/selftests/kvm/hardware_disable_test.c @@ -80,7 +80,7 @@ static inline void check_join(pthread_t thread, void **retval) TEST_ASSERT(r == 0, "%s: failed to join thread", __func__); } -static void run_test(uint32_t run) +static void run_test(u32 run) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; @@ -88,7 +88,7 @@ static void run_test(uint32_t run) pthread_t threads[VCPU_NUM]; pthread_t throw_away; void *b; - uint32_t i, j; + u32 i, j; CPU_ZERO(&cpu_set); for (i = 0; i < VCPU_NUM; i++) @@ -149,7 +149,7 @@ void wait_for_child_setup(pid_t pid) int main(int argc, char **argv) { - uint32_t i; + u32 i; int s, r; pid_t pid; diff --git a/tools/testing/selftests/kvm/include/arm64/arch_timer.h b/tools/testing/selftests/kvm/include/arm64/arch_timer.h index e2c4e9f0010f..a5836d4ab7ee 100644 --- a/tools/testing/selftests/kvm/include/arm64/arch_timer.h +++ b/tools/testing/selftests/kvm/include/arm64/arch_timer.h @@ -18,20 +18,20 @@ enum arch_timer { #define CTL_ISTATUS (1 << 2) #define msec_to_cycles(msec) \ - (timer_get_cntfrq() * (uint64_t)(msec) / 1000) + (timer_get_cntfrq() * (u64)(msec) / 1000) #define usec_to_cycles(usec) \ - (timer_get_cntfrq() * (uint64_t)(usec) / 1000000) + (timer_get_cntfrq() * (u64)(usec) / 1000000) #define cycles_to_usec(cycles) \ - ((uint64_t)(cycles) * 1000000 / timer_get_cntfrq()) + ((u64)(cycles) * 1000000 / timer_get_cntfrq()) -static inline uint32_t timer_get_cntfrq(void) +static inline u32 timer_get_cntfrq(void) { return read_sysreg(cntfrq_el0); } -static inline uint64_t timer_get_cntct(enum arch_timer timer) +static inline u64 timer_get_cntct(enum arch_timer timer) { isb(); @@ -48,7 +48,7 @@ static inline uint64_t timer_get_cntct(enum arch_timer timer) return 0; } -static inline void timer_set_cval(enum arch_timer timer, uint64_t cval) +static inline void timer_set_cval(enum arch_timer timer, u64 cval) { switch (timer) { case VIRTUAL: @@ -64,7 +64,7 @@ static inline void timer_set_cval(enum arch_timer timer, uint64_t cval) isb(); } -static inline uint64_t timer_get_cval(enum arch_timer timer) +static inline u64 timer_get_cval(enum arch_timer timer) { switch (timer) { case VIRTUAL: @@ -79,7 +79,7 @@ static inline uint64_t timer_get_cval(enum arch_timer timer) return 0; } -static inline void timer_set_tval(enum arch_timer timer, int32_t tval) +static inline void timer_set_tval(enum arch_timer timer, s32 tval) { switch (timer) { case VIRTUAL: @@ -95,7 +95,7 @@ static inline void timer_set_tval(enum arch_timer timer, int32_t tval) isb(); } -static inline int32_t timer_get_tval(enum arch_timer timer) +static inline s32 timer_get_tval(enum arch_timer timer) { isb(); switch (timer) { @@ -111,7 +111,7 @@ static inline int32_t timer_get_tval(enum arch_timer timer) return 0; } -static inline void timer_set_ctl(enum arch_timer timer, uint32_t ctl) +static inline void timer_set_ctl(enum arch_timer timer, u32 ctl) { switch (timer) { case VIRTUAL: @@ -127,7 +127,7 @@ static inline void timer_set_ctl(enum arch_timer timer, uint32_t ctl) isb(); } -static inline uint32_t timer_get_ctl(enum arch_timer timer) +static inline u32 timer_get_ctl(enum arch_timer timer) { switch (timer) { case VIRTUAL: @@ -142,15 +142,15 @@ static inline uint32_t timer_get_ctl(enum arch_timer timer) return 0; } -static inline void timer_set_next_cval_ms(enum arch_timer timer, uint32_t msec) +static inline void timer_set_next_cval_ms(enum arch_timer timer, u32 msec) { - uint64_t now_ct = timer_get_cntct(timer); - uint64_t next_ct = now_ct + msec_to_cycles(msec); + u64 now_ct = timer_get_cntct(timer); + u64 next_ct = now_ct + msec_to_cycles(msec); timer_set_cval(timer, next_ct); } -static inline void timer_set_next_tval_ms(enum arch_timer timer, uint32_t msec) +static inline void timer_set_next_tval_ms(enum arch_timer timer, u32 msec) { timer_set_tval(timer, msec_to_cycles(msec)); } diff --git a/tools/testing/selftests/kvm/include/arm64/delay.h b/tools/testing/selftests/kvm/include/arm64/delay.h index 329e4f5079ea..6a5d4634af2c 100644 --- a/tools/testing/selftests/kvm/include/arm64/delay.h +++ b/tools/testing/selftests/kvm/include/arm64/delay.h @@ -8,10 +8,10 @@ #include "arch_timer.h" -static inline void __delay(uint64_t cycles) +static inline void __delay(u64 cycles) { enum arch_timer timer = VIRTUAL; - uint64_t start = timer_get_cntct(timer); + u64 start = timer_get_cntct(timer); while ((timer_get_cntct(timer) - start) < cycles) cpu_relax(); diff --git a/tools/testing/selftests/kvm/include/arm64/gic.h b/tools/testing/selftests/kvm/include/arm64/gic.h index cc7a7f34ed37..615745093c98 100644 --- a/tools/testing/selftests/kvm/include/arm64/gic.h +++ b/tools/testing/selftests/kvm/include/arm64/gic.h @@ -48,8 +48,8 @@ void gic_set_dir(unsigned int intid); * split is true, EOI drops the priority and deactivates the interrupt. */ void gic_set_eoi_split(bool split); -void gic_set_priority_mask(uint64_t mask); -void gic_set_priority(uint32_t intid, uint32_t prio); +void gic_set_priority_mask(u64 mask); +void gic_set_priority(u32 intid, u32 prio); void gic_irq_set_active(unsigned int intid); void gic_irq_clear_active(unsigned int intid); bool gic_irq_get_active(unsigned int intid); @@ -59,7 +59,7 @@ bool gic_irq_get_pending(unsigned int intid); void gic_irq_set_config(unsigned int intid, bool is_edge); void gic_irq_set_group(unsigned int intid, bool group); -void gic_rdist_enable_lpis(vm_paddr_t cfg_table, size_t cfg_table_size, - vm_paddr_t pend_table); +void gic_rdist_enable_lpis(gpa_t cfg_table, size_t cfg_table_size, + gpa_t pend_table); #endif /* SELFTEST_KVM_GIC_H */ diff --git a/tools/testing/selftests/kvm/include/arm64/gic_v3_its.h b/tools/testing/selftests/kvm/include/arm64/gic_v3_its.h index 58feef3eb386..a43a407e2d5c 100644 --- a/tools/testing/selftests/kvm/include/arm64/gic_v3_its.h +++ b/tools/testing/selftests/kvm/include/arm64/gic_v3_its.h @@ -5,11 +5,10 @@ #include -void its_init(vm_paddr_t coll_tbl, size_t coll_tbl_sz, - vm_paddr_t device_tbl, size_t device_tbl_sz, - vm_paddr_t cmdq, size_t cmdq_size); +void its_init(gpa_t coll_tbl, size_t coll_tbl_sz, gpa_t device_tbl, + size_t device_tbl_sz, gpa_t cmdq, size_t cmdq_size); -void its_send_mapd_cmd(void *cmdq_base, u32 device_id, vm_paddr_t itt_base, +void its_send_mapd_cmd(void *cmdq_base, u32 device_id, gpa_t itt_base, size_t itt_size, bool valid); void its_send_mapc_cmd(void *cmdq_base, u32 vcpu_id, u32 collection_id, bool valid); void its_send_mapti_cmd(void *cmdq_base, u32 device_id, u32 event_id, diff --git a/tools/testing/selftests/kvm/include/arm64/gic_v5.h b/tools/testing/selftests/kvm/include/arm64/gic_v5.h new file mode 100644 index 000000000000..eb523d9277cf --- /dev/null +++ b/tools/testing/selftests/kvm/include/arm64/gic_v5.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SELFTESTS_GIC_V5_H +#define __SELFTESTS_GIC_V5_H + +#include +#include + +#include + +#include "processor.h" + +/* + * Definitions for GICv5 instructions for the Current Domain + */ +#define GICV5_OP_GIC_CDAFF sys_insn(1, 0, 12, 1, 3) +#define GICV5_OP_GIC_CDDI sys_insn(1, 0, 12, 2, 0) +#define GICV5_OP_GIC_CDDIS sys_insn(1, 0, 12, 1, 0) +#define GICV5_OP_GIC_CDHM sys_insn(1, 0, 12, 2, 1) +#define GICV5_OP_GIC_CDEN sys_insn(1, 0, 12, 1, 1) +#define GICV5_OP_GIC_CDEOI sys_insn(1, 0, 12, 1, 7) +#define GICV5_OP_GIC_CDPEND sys_insn(1, 0, 12, 1, 4) +#define GICV5_OP_GIC_CDPRI sys_insn(1, 0, 12, 1, 2) +#define GICV5_OP_GIC_CDRCFG sys_insn(1, 0, 12, 1, 5) +#define GICV5_OP_GICR_CDIA sys_insn(1, 0, 12, 3, 0) +#define GICV5_OP_GICR_CDNMIA sys_insn(1, 0, 12, 3, 1) + +/* Definitions for GIC CDAFF */ +#define GICV5_GIC_CDAFF_IAFFID_MASK GENMASK_ULL(47, 32) +#define GICV5_GIC_CDAFF_TYPE_MASK GENMASK_ULL(31, 29) +#define GICV5_GIC_CDAFF_IRM_MASK BIT_ULL(28) +#define GICV5_GIC_CDAFF_ID_MASK GENMASK_ULL(23, 0) + +/* Definitions for GIC CDDI */ +#define GICV5_GIC_CDDI_TYPE_MASK GENMASK_ULL(31, 29) +#define GICV5_GIC_CDDI_ID_MASK GENMASK_ULL(23, 0) + +/* Definitions for GIC CDDIS */ +#define GICV5_GIC_CDDIS_TYPE_MASK GENMASK_ULL(31, 29) +#define GICV5_GIC_CDDIS_TYPE(r) FIELD_GET(GICV5_GIC_CDDIS_TYPE_MASK, r) +#define GICV5_GIC_CDDIS_ID_MASK GENMASK_ULL(23, 0) +#define GICV5_GIC_CDDIS_ID(r) FIELD_GET(GICV5_GIC_CDDIS_ID_MASK, r) + +/* Definitions for GIC CDEN */ +#define GICV5_GIC_CDEN_TYPE_MASK GENMASK_ULL(31, 29) +#define GICV5_GIC_CDEN_ID_MASK GENMASK_ULL(23, 0) + +/* Definitions for GIC CDHM */ +#define GICV5_GIC_CDHM_HM_MASK BIT_ULL(32) +#define GICV5_GIC_CDHM_TYPE_MASK GENMASK_ULL(31, 29) +#define GICV5_GIC_CDHM_ID_MASK GENMASK_ULL(23, 0) + +/* Definitions for GIC CDPEND */ +#define GICV5_GIC_CDPEND_PENDING_MASK BIT_ULL(32) +#define GICV5_GIC_CDPEND_TYPE_MASK GENMASK_ULL(31, 29) +#define GICV5_GIC_CDPEND_ID_MASK GENMASK_ULL(23, 0) + +/* Definitions for GIC CDPRI */ +#define GICV5_GIC_CDPRI_PRIORITY_MASK GENMASK_ULL(39, 35) +#define GICV5_GIC_CDPRI_TYPE_MASK GENMASK_ULL(31, 29) +#define GICV5_GIC_CDPRI_ID_MASK GENMASK_ULL(23, 0) + +/* Definitions for GIC CDRCFG */ +#define GICV5_GIC_CDRCFG_TYPE_MASK GENMASK_ULL(31, 29) +#define GICV5_GIC_CDRCFG_ID_MASK GENMASK_ULL(23, 0) + +/* Definitions for GICR CDIA */ +#define GICV5_GICR_CDIA_VALID_MASK BIT_ULL(32) +#define GICV5_GICR_CDIA_VALID(r) FIELD_GET(GICV5_GICR_CDIA_VALID_MASK, r) +#define GICV5_GICR_CDIA_TYPE_MASK GENMASK_ULL(31, 29) +#define GICV5_GICR_CDIA_ID_MASK GENMASK_ULL(23, 0) +#define GICV5_GICR_CDIA_INTID GENMASK_ULL(31, 0) + +/* Definitions for GICR CDNMIA */ +#define GICV5_GICR_CDNMIA_VALID_MASK BIT_ULL(32) +#define GICV5_GICR_CDNMIA_VALID(r) FIELD_GET(GICV5_GICR_CDNMIA_VALID_MASK, r) +#define GICV5_GICR_CDNMIA_TYPE_MASK GENMASK_ULL(31, 29) +#define GICV5_GICR_CDNMIA_ID_MASK GENMASK_ULL(23, 0) + +#define gicr_insn(insn) read_sysreg_s(GICV5_OP_GICR_##insn) +#define gic_insn(v, insn) write_sysreg_s(v, GICV5_OP_GIC_##insn) + +#define __GIC_BARRIER_INSN(op0, op1, CRn, CRm, op2, Rt) \ + __emit_inst(0xd5000000 | \ + sys_insn((op0), (op1), (CRn), (CRm), (op2)) | \ + ((Rt) & 0x1f)) + +#define GSB_SYS_BARRIER_INSN __GIC_BARRIER_INSN(1, 0, 12, 0, 0, 31) +#define GSB_ACK_BARRIER_INSN __GIC_BARRIER_INSN(1, 0, 12, 0, 1, 31) + +#define gsb_ack() asm volatile(GSB_ACK_BARRIER_INSN : : : "memory") +#define gsb_sys() asm volatile(GSB_SYS_BARRIER_INSN : : : "memory") + +#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) + +#define GICV5_IRQ_DEFAULT_PRI 0b10000 + +#define GICV5_ARCH_PPI_SW_PPI 0x3 + +void gicv5_ppi_priority_init(void) +{ + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR0_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR1_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR2_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR3_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR4_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR5_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR6_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR7_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR8_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR9_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR10_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR11_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR12_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR13_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR14_EL1); + write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_DEFAULT_PRI), SYS_ICC_PPI_PRIORITYR15_EL1); + + /* + * Context syncronization required to make sure system register writes + * effects are synchronised. + */ + isb(); +} + +void gicv5_cpu_disable_interrupts(void) +{ + u64 cr0; + + cr0 = FIELD_PREP(ICC_CR0_EL1_EN, 0); + write_sysreg_s(cr0, SYS_ICC_CR0_EL1); +} + +void gicv5_cpu_enable_interrupts(void) +{ + u64 cr0, pcr; + + write_sysreg_s(0, SYS_ICC_PPI_ENABLER0_EL1); + write_sysreg_s(0, SYS_ICC_PPI_ENABLER1_EL1); + + gicv5_ppi_priority_init(); + + pcr = FIELD_PREP(ICC_PCR_EL1_PRIORITY, GICV5_IRQ_DEFAULT_PRI); + write_sysreg_s(pcr, SYS_ICC_PCR_EL1); + + cr0 = FIELD_PREP(ICC_CR0_EL1_EN, 1); + write_sysreg_s(cr0, SYS_ICC_CR0_EL1); +} + +#endif diff --git a/tools/testing/selftests/kvm/include/arm64/processor.h b/tools/testing/selftests/kvm/include/arm64/processor.h index ac97a1c436fc..b8a902ba8573 100644 --- a/tools/testing/selftests/kvm/include/arm64/processor.h +++ b/tools/testing/selftests/kvm/include/arm64/processor.h @@ -128,7 +128,7 @@ #define PTE_ADDR_51_50_LPA2_SHIFT 8 void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init); -struct kvm_vcpu *aarch64_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, +struct kvm_vcpu *aarch64_vcpu_add(struct kvm_vm *vm, u32 vcpu_id, struct kvm_vcpu_init *init, void *guest_code); struct ex_regs { @@ -167,8 +167,8 @@ enum { (v) == VECTOR_SYNC_LOWER_64 || \ (v) == VECTOR_SYNC_LOWER_32) -void aarch64_get_supported_page_sizes(uint32_t ipa, uint32_t *ipa4k, - uint32_t *ipa16k, uint32_t *ipa64k); +void aarch64_get_supported_page_sizes(u32 ipa, u32 *ipa4k, + u32 *ipa16k, u32 *ipa64k); void vm_init_descriptor_tables(struct kvm_vm *vm); void vcpu_init_descriptor_tables(struct kvm_vcpu *vcpu); @@ -179,8 +179,8 @@ void vm_install_exception_handler(struct kvm_vm *vm, void vm_install_sync_handler(struct kvm_vm *vm, int vector, int ec, handler_fn handler); -uint64_t *virt_get_pte_hva_at_level(struct kvm_vm *vm, vm_vaddr_t gva, int level); -uint64_t *virt_get_pte_hva(struct kvm_vm *vm, vm_vaddr_t gva); +u64 *virt_get_pte_hva_at_level(struct kvm_vm *vm, gva_t gva, int level); +u64 *virt_get_pte_hva(struct kvm_vm *vm, gva_t gva); static inline void cpu_relax(void) { @@ -287,9 +287,9 @@ struct arm_smccc_res { * @res: pointer to write the return values from registers x0-x3 * */ -void smccc_hvc(uint32_t function_id, uint64_t arg0, uint64_t arg1, - uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, - uint64_t arg6, struct arm_smccc_res *res); +void smccc_hvc(u32 function_id, u64 arg0, u64 arg1, + u64 arg2, u64 arg3, u64 arg4, u64 arg5, + u64 arg6, struct arm_smccc_res *res); /** * smccc_smc - Invoke a SMCCC function using the smc conduit @@ -298,9 +298,9 @@ void smccc_hvc(uint32_t function_id, uint64_t arg0, uint64_t arg1, * @res: pointer to write the return values from registers x0-x3 * */ -void smccc_smc(uint32_t function_id, uint64_t arg0, uint64_t arg1, - uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, - uint64_t arg6, struct arm_smccc_res *res); +void smccc_smc(u32 function_id, u64 arg0, u64 arg1, + u64 arg2, u64 arg3, u64 arg4, u64 arg5, + u64 arg6, struct arm_smccc_res *res); /* Execute a Wait For Interrupt instruction. */ void wfi(void); diff --git a/tools/testing/selftests/kvm/include/arm64/ucall.h b/tools/testing/selftests/kvm/include/arm64/ucall.h index 4ec801f37f00..2210d3d94c40 100644 --- a/tools/testing/selftests/kvm/include/arm64/ucall.h +++ b/tools/testing/selftests/kvm/include/arm64/ucall.h @@ -10,9 +10,9 @@ * ucall_exit_mmio_addr holds per-VM values (global data is duplicated by each * VM), it must not be accessed from host code. */ -extern vm_vaddr_t *ucall_exit_mmio_addr; +extern gva_t *ucall_exit_mmio_addr; -static inline void ucall_arch_do_ucall(vm_vaddr_t uc) +static inline void ucall_arch_do_ucall(gva_t uc) { WRITE_ONCE(*ucall_exit_mmio_addr, uc); } diff --git a/tools/testing/selftests/kvm/include/arm64/vgic.h b/tools/testing/selftests/kvm/include/arm64/vgic.h index 688beccc9436..1f8b04373987 100644 --- a/tools/testing/selftests/kvm/include/arm64/vgic.h +++ b/tools/testing/selftests/kvm/include/arm64/vgic.h @@ -11,27 +11,27 @@ #include "kvm_util.h" #define REDIST_REGION_ATTR_ADDR(count, base, flags, index) \ - (((uint64_t)(count) << 52) | \ - ((uint64_t)((base) >> 16) << 16) | \ - ((uint64_t)(flags) << 12) | \ + (((u64)(count) << 52) | \ + ((u64)((base) >> 16) << 16) | \ + ((u64)(flags) << 12) | \ index) bool kvm_supports_vgic_v3(void); -int __vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs); +int __vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, u32 nr_irqs); void __vgic_v3_init(int fd); -int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs); +int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, u32 nr_irqs); #define VGIC_MAX_RESERVED 1023 -void kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level); -int _kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level); +void kvm_irq_set_level_info(int gic_fd, u32 intid, int level); +int _kvm_irq_set_level_info(int gic_fd, u32 intid, int level); -void kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level); -int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level); +void kvm_arm_irq_line(struct kvm_vm *vm, u32 intid, int level); +int _kvm_arm_irq_line(struct kvm_vm *vm, u32 intid, int level); /* The vcpu arg only applies to private interrupts. */ -void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu); -void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu); +void kvm_irq_write_ispendr(int gic_fd, u32 intid, struct kvm_vcpu *vcpu); +void kvm_irq_write_isactiver(int gic_fd, u32 intid, struct kvm_vcpu *vcpu); #define KVM_IRQCHIP_NUM_PINS (1020 - 32) diff --git a/tools/testing/selftests/kvm/include/kvm_syscalls.h b/tools/testing/selftests/kvm/include/kvm_syscalls.h index d4e613162bba..843c9904c46f 100644 --- a/tools/testing/selftests/kvm/include/kvm_syscalls.h +++ b/tools/testing/selftests/kvm/include/kvm_syscalls.h @@ -77,5 +77,6 @@ __KVM_SYSCALL_DEFINE(munmap, 2, void *, mem, size_t, size); __KVM_SYSCALL_DEFINE(close, 1, int, fd); __KVM_SYSCALL_DEFINE(fallocate, 4, int, fd, int, mode, loff_t, offset, loff_t, len); __KVM_SYSCALL_DEFINE(ftruncate, 2, unsigned int, fd, off_t, length); +__KVM_SYSCALL_DEFINE(madvise, 3, void *, addr, size_t, length, int, advice); #endif /* SELFTEST_KVM_SYSCALLS_H */ diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 8b39cb919f4f..2ecaaa0e9965 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -58,7 +58,7 @@ struct kvm_binary_stats { struct kvm_vcpu { struct list_head list; - uint32_t id; + u32 id; int fd; struct kvm_vm *vm; struct kvm_run *run; @@ -70,8 +70,8 @@ struct kvm_vcpu { #endif struct kvm_binary_stats stats; struct kvm_dirty_gfn *dirty_gfns; - uint32_t fetch_index; - uint32_t dirty_gfns_count; + u32 fetch_index; + u32 dirty_gfns_count; }; struct userspace_mem_regions { @@ -90,7 +90,7 @@ enum kvm_mem_region_type { struct kvm_mmu { bool pgd_created; - uint64_t pgd; + u64 pgd; int pgtable_levels; struct kvm_mmu_arch arch; @@ -105,16 +105,16 @@ struct kvm_vm { unsigned int page_shift; unsigned int pa_bits; unsigned int va_bits; - uint64_t max_gfn; + u64 max_gfn; struct list_head vcpus; struct userspace_mem_regions regions; struct sparsebit *vpages_valid; struct sparsebit *vpages_mapped; bool has_irqchip; - vm_paddr_t ucall_mmio_addr; - vm_vaddr_t handlers; - uint32_t dirty_ring_size; - uint64_t gpa_tag_mask; + gpa_t ucall_mmio_addr; + gva_t handlers; + u32 dirty_ring_size; + gpa_t gpa_tag_mask; /* * "mmu" is the guest's stage-1, with a short name because the vast @@ -132,7 +132,7 @@ struct kvm_vm { * allocators, e.g., lib/elf uses the memslots[MEM_REGION_CODE] * memslot. */ - uint32_t memslots[NR_MEM_REGIONS]; + u32 memslots[NR_MEM_REGIONS]; }; struct vcpu_reg_sublist { @@ -164,7 +164,7 @@ struct vcpu_reg_list { else struct userspace_mem_region * -memslot2region(struct kvm_vm *vm, uint32_t memslot); +memslot2region(struct kvm_vm *vm, u32 memslot); static inline struct userspace_mem_region *vm_get_mem_region(struct kvm_vm *vm, enum kvm_mem_region_type type) @@ -213,13 +213,13 @@ enum vm_guest_mode { }; struct vm_shape { - uint32_t type; - uint8_t mode; - uint8_t pad0; - uint16_t pad1; + u32 type; + u8 mode; + u8 pad0; + u16 pad1; }; -kvm_static_assert(sizeof(struct vm_shape) == sizeof(uint64_t)); +kvm_static_assert(sizeof(struct vm_shape) == sizeof(u64)); #define VM_TYPE_DEFAULT 0 @@ -404,21 +404,22 @@ static inline int vm_check_cap(struct kvm_vm *vm, long cap) return ret; } -static inline int __vm_enable_cap(struct kvm_vm *vm, uint32_t cap, uint64_t arg0) +static inline int __vm_enable_cap(struct kvm_vm *vm, u32 cap, u64 arg0) { struct kvm_enable_cap enable_cap = { .cap = cap, .args = { arg0 } }; return __vm_ioctl(vm, KVM_ENABLE_CAP, &enable_cap); } -static inline void vm_enable_cap(struct kvm_vm *vm, uint32_t cap, uint64_t arg0) + +static inline void vm_enable_cap(struct kvm_vm *vm, u32 cap, u64 arg0) { struct kvm_enable_cap enable_cap = { .cap = cap, .args = { arg0 } }; vm_ioctl(vm, KVM_ENABLE_CAP, &enable_cap); } -static inline void vm_set_memory_attributes(struct kvm_vm *vm, uint64_t gpa, - uint64_t size, uint64_t attributes) +static inline void vm_set_memory_attributes(struct kvm_vm *vm, gpa_t gpa, + u64 size, u64 attributes) { struct kvm_memory_attributes attr = { .attributes = attributes, @@ -438,35 +439,35 @@ static inline void vm_set_memory_attributes(struct kvm_vm *vm, uint64_t gpa, } -static inline void vm_mem_set_private(struct kvm_vm *vm, uint64_t gpa, - uint64_t size) +static inline void vm_mem_set_private(struct kvm_vm *vm, gpa_t gpa, + u64 size) { vm_set_memory_attributes(vm, gpa, size, KVM_MEMORY_ATTRIBUTE_PRIVATE); } -static inline void vm_mem_set_shared(struct kvm_vm *vm, uint64_t gpa, - uint64_t size) +static inline void vm_mem_set_shared(struct kvm_vm *vm, gpa_t gpa, + u64 size) { vm_set_memory_attributes(vm, gpa, size, 0); } -void vm_guest_mem_fallocate(struct kvm_vm *vm, uint64_t gpa, uint64_t size, +void vm_guest_mem_fallocate(struct kvm_vm *vm, gpa_t gpa, u64 size, bool punch_hole); -static inline void vm_guest_mem_punch_hole(struct kvm_vm *vm, uint64_t gpa, - uint64_t size) +static inline void vm_guest_mem_punch_hole(struct kvm_vm *vm, gpa_t gpa, + u64 size) { vm_guest_mem_fallocate(vm, gpa, size, true); } -static inline void vm_guest_mem_allocate(struct kvm_vm *vm, uint64_t gpa, - uint64_t size) +static inline void vm_guest_mem_allocate(struct kvm_vm *vm, gpa_t gpa, + u64 size) { vm_guest_mem_fallocate(vm, gpa, size, false); } -void vm_enable_dirty_ring(struct kvm_vm *vm, uint32_t ring_size); -const char *vm_guest_mode_string(uint32_t i); +void vm_enable_dirty_ring(struct kvm_vm *vm, u32 ring_size); +const char *vm_guest_mode_string(u32 i); void kvm_vm_free(struct kvm_vm *vmp); void kvm_vm_restart(struct kvm_vm *vmp); @@ -474,7 +475,7 @@ void kvm_vm_release(struct kvm_vm *vmp); void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename); int kvm_memfd_alloc(size_t size, bool hugepages); -void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent); +void vm_dump(FILE *stream, struct kvm_vm *vm, u8 indent); static inline void kvm_vm_get_dirty_log(struct kvm_vm *vm, int slot, void *log) { @@ -484,7 +485,7 @@ static inline void kvm_vm_get_dirty_log(struct kvm_vm *vm, int slot, void *log) } static inline void kvm_vm_clear_dirty_log(struct kvm_vm *vm, int slot, void *log, - uint64_t first_page, uint32_t num_pages) + u64 first_page, u32 num_pages) { struct kvm_clear_dirty_log args = { .dirty_bitmap = log, @@ -496,14 +497,14 @@ static inline void kvm_vm_clear_dirty_log(struct kvm_vm *vm, int slot, void *log vm_ioctl(vm, KVM_CLEAR_DIRTY_LOG, &args); } -static inline uint32_t kvm_vm_reset_dirty_ring(struct kvm_vm *vm) +static inline u32 kvm_vm_reset_dirty_ring(struct kvm_vm *vm) { return __vm_ioctl(vm, KVM_RESET_DIRTY_RINGS, NULL); } static inline void kvm_vm_register_coalesced_io(struct kvm_vm *vm, - uint64_t address, - uint64_t size, bool pio) + u64 address, + u64 size, bool pio) { struct kvm_coalesced_mmio_zone zone = { .addr = address, @@ -515,8 +516,8 @@ static inline void kvm_vm_register_coalesced_io(struct kvm_vm *vm, } static inline void kvm_vm_unregister_coalesced_io(struct kvm_vm *vm, - uint64_t address, - uint64_t size, bool pio) + u64 address, + u64 size, bool pio) { struct kvm_coalesced_mmio_zone zone = { .addr = address, @@ -535,8 +536,8 @@ static inline int vm_get_stats_fd(struct kvm_vm *vm) return fd; } -static inline int __kvm_irqfd(struct kvm_vm *vm, uint32_t gsi, int eventfd, - uint32_t flags) +static inline int __kvm_irqfd(struct kvm_vm *vm, u32 gsi, int eventfd, + u32 flags) { struct kvm_irqfd irqfd = { .fd = eventfd, @@ -548,20 +549,19 @@ static inline int __kvm_irqfd(struct kvm_vm *vm, uint32_t gsi, int eventfd, return __vm_ioctl(vm, KVM_IRQFD, &irqfd); } -static inline void kvm_irqfd(struct kvm_vm *vm, uint32_t gsi, int eventfd, - uint32_t flags) +static inline void kvm_irqfd(struct kvm_vm *vm, u32 gsi, int eventfd, u32 flags) { int ret = __kvm_irqfd(vm, gsi, eventfd, flags); TEST_ASSERT_VM_VCPU_IOCTL(!ret, KVM_IRQFD, ret, vm); } -static inline void kvm_assign_irqfd(struct kvm_vm *vm, uint32_t gsi, int eventfd) +static inline void kvm_assign_irqfd(struct kvm_vm *vm, u32 gsi, int eventfd) { kvm_irqfd(vm, gsi, eventfd, 0); } -static inline void kvm_deassign_irqfd(struct kvm_vm *vm, uint32_t gsi, int eventfd) +static inline void kvm_deassign_irqfd(struct kvm_vm *vm, u32 gsi, int eventfd) { kvm_irqfd(vm, gsi, eventfd, KVM_IRQFD_FLAG_DEASSIGN); } @@ -610,15 +610,15 @@ static inline struct kvm_stats_desc *get_stats_descriptor(struct kvm_stats_desc } void read_stat_data(int stats_fd, struct kvm_stats_header *header, - struct kvm_stats_desc *desc, uint64_t *data, + struct kvm_stats_desc *desc, u64 *data, size_t max_elements); void kvm_get_stat(struct kvm_binary_stats *stats, const char *name, - uint64_t *data, size_t max_elements); + u64 *data, size_t max_elements); #define __get_stat(stats, stat) \ ({ \ - uint64_t data; \ + u64 data; \ \ kvm_get_stat(stats, #stat, &data, 1); \ data; \ @@ -664,8 +664,8 @@ static inline bool is_smt_on(void) void vm_create_irqchip(struct kvm_vm *vm); -static inline int __vm_create_guest_memfd(struct kvm_vm *vm, uint64_t size, - uint64_t flags) +static inline int __vm_create_guest_memfd(struct kvm_vm *vm, u64 size, + u64 flags) { struct kvm_create_guest_memfd guest_memfd = { .size = size, @@ -675,8 +675,8 @@ static inline int __vm_create_guest_memfd(struct kvm_vm *vm, uint64_t size, return __vm_ioctl(vm, KVM_CREATE_GUEST_MEMFD, &guest_memfd); } -static inline int vm_create_guest_memfd(struct kvm_vm *vm, uint64_t size, - uint64_t flags) +static inline int vm_create_guest_memfd(struct kvm_vm *vm, u64 size, + u64 flags) { int fd = __vm_create_guest_memfd(vm, size, flags); @@ -684,24 +684,23 @@ static inline int vm_create_guest_memfd(struct kvm_vm *vm, uint64_t size, return fd; } -void vm_set_user_memory_region(struct kvm_vm *vm, uint32_t slot, uint32_t flags, - uint64_t gpa, uint64_t size, void *hva); -int __vm_set_user_memory_region(struct kvm_vm *vm, uint32_t slot, uint32_t flags, - uint64_t gpa, uint64_t size, void *hva); -void vm_set_user_memory_region2(struct kvm_vm *vm, uint32_t slot, uint32_t flags, - uint64_t gpa, uint64_t size, void *hva, - uint32_t guest_memfd, uint64_t guest_memfd_offset); -int __vm_set_user_memory_region2(struct kvm_vm *vm, uint32_t slot, uint32_t flags, - uint64_t gpa, uint64_t size, void *hva, - uint32_t guest_memfd, uint64_t guest_memfd_offset); +void vm_set_user_memory_region(struct kvm_vm *vm, u32 slot, u32 flags, + gpa_t gpa, u64 size, void *hva); +int __vm_set_user_memory_region(struct kvm_vm *vm, u32 slot, u32 flags, + gpa_t gpa, u64 size, void *hva); +void vm_set_user_memory_region2(struct kvm_vm *vm, u32 slot, u32 flags, + gpa_t gpa, u64 size, void *hva, + u32 guest_memfd, u64 guest_memfd_offset); +int __vm_set_user_memory_region2(struct kvm_vm *vm, u32 slot, u32 flags, + gpa_t gpa, u64 size, void *hva, + u32 guest_memfd, u64 guest_memfd_offset); void vm_userspace_mem_region_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type, - uint64_t gpa, uint32_t slot, uint64_t npages, - uint32_t flags); + gpa_t gpa, u32 slot, u64 npages, u32 flags); void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type, - uint64_t gpa, uint32_t slot, uint64_t npages, uint32_t flags, - int guest_memfd_fd, uint64_t guest_memfd_offset); + gpa_t gpa, u32 slot, u64 npages, u32 flags, + int guest_memfd_fd, u64 guest_memfd_offset); #ifndef vm_arch_has_protected_memory static inline bool vm_arch_has_protected_memory(struct kvm_vm *vm) @@ -710,36 +709,34 @@ static inline bool vm_arch_has_protected_memory(struct kvm_vm *vm) } #endif -void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags); -void vm_mem_region_reload(struct kvm_vm *vm, uint32_t slot); -void vm_mem_region_move(struct kvm_vm *vm, uint32_t slot, uint64_t new_gpa); -void vm_mem_region_delete(struct kvm_vm *vm, uint32_t slot); -struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id); -void vm_populate_vaddr_bitmap(struct kvm_vm *vm); -vm_vaddr_t vm_vaddr_unused_gap(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min); -vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min); -vm_vaddr_t __vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min, - enum kvm_mem_region_type type); -vm_vaddr_t vm_vaddr_alloc_shared(struct kvm_vm *vm, size_t sz, - vm_vaddr_t vaddr_min, - enum kvm_mem_region_type type); -vm_vaddr_t vm_vaddr_alloc_pages(struct kvm_vm *vm, int nr_pages); -vm_vaddr_t __vm_vaddr_alloc_page(struct kvm_vm *vm, - enum kvm_mem_region_type type); -vm_vaddr_t vm_vaddr_alloc_page(struct kvm_vm *vm); +void vm_mem_region_set_flags(struct kvm_vm *vm, u32 slot, u32 flags); +void vm_mem_region_reload(struct kvm_vm *vm, u32 slot); +void vm_mem_region_move(struct kvm_vm *vm, u32 slot, u64 new_gpa); +void vm_mem_region_delete(struct kvm_vm *vm, u32 slot); +struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, u32 vcpu_id); +void vm_populate_gva_bitmap(struct kvm_vm *vm); +gva_t vm_unused_gva_gap(struct kvm_vm *vm, size_t sz, gva_t min_gva); +gva_t vm_alloc(struct kvm_vm *vm, size_t sz, gva_t min_gva); +gva_t __vm_alloc(struct kvm_vm *vm, size_t sz, gva_t min_gva, + enum kvm_mem_region_type type); +gva_t vm_alloc_shared(struct kvm_vm *vm, size_t sz, gva_t min_gva, + enum kvm_mem_region_type type); +gva_t vm_alloc_pages(struct kvm_vm *vm, int nr_pages); +gva_t __vm_alloc_page(struct kvm_vm *vm, enum kvm_mem_region_type type); +gva_t vm_alloc_page(struct kvm_vm *vm); -void virt_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, +void virt_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa, unsigned int npages); -void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa); -void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva); -vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva); -void *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa); +void *addr_gpa2hva(struct kvm_vm *vm, gpa_t gpa); +void *addr_gva2hva(struct kvm_vm *vm, gva_t gva); +gpa_t addr_hva2gpa(struct kvm_vm *vm, void *hva); +void *addr_gpa2alias(struct kvm_vm *vm, gpa_t gpa); #ifndef vcpu_arch_put_guest #define vcpu_arch_put_guest(mem, val) do { (mem) = (val); } while (0) #endif -static inline vm_paddr_t vm_untag_gpa(struct kvm_vm *vm, vm_paddr_t gpa) +static inline gpa_t vm_untag_gpa(struct kvm_vm *vm, gpa_t gpa) { return gpa & ~vm->gpa_tag_mask; } @@ -755,8 +752,8 @@ static inline int __vcpu_run(struct kvm_vcpu *vcpu) void vcpu_run_complete_io(struct kvm_vcpu *vcpu); struct kvm_reg_list *vcpu_get_reg_list(struct kvm_vcpu *vcpu); -static inline void vcpu_enable_cap(struct kvm_vcpu *vcpu, uint32_t cap, - uint64_t arg0) +static inline void vcpu_enable_cap(struct kvm_vcpu *vcpu, u32 cap, + u64 arg0) { struct kvm_enable_cap enable_cap = { .cap = cap, .args = { arg0 } }; @@ -811,31 +808,34 @@ static inline void vcpu_fpu_set(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) vcpu_ioctl(vcpu, KVM_SET_FPU, fpu); } -static inline int __vcpu_get_reg(struct kvm_vcpu *vcpu, uint64_t id, void *addr) +static inline int __vcpu_get_reg(struct kvm_vcpu *vcpu, u64 id, void *addr) { - struct kvm_one_reg reg = { .id = id, .addr = (uint64_t)addr }; + struct kvm_one_reg reg = { .id = id, .addr = (u64)addr }; return __vcpu_ioctl(vcpu, KVM_GET_ONE_REG, ®); } -static inline int __vcpu_set_reg(struct kvm_vcpu *vcpu, uint64_t id, uint64_t val) + +static inline int __vcpu_set_reg(struct kvm_vcpu *vcpu, u64 id, u64 val) { - struct kvm_one_reg reg = { .id = id, .addr = (uint64_t)&val }; + struct kvm_one_reg reg = { .id = id, .addr = (u64)&val }; return __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); } -static inline uint64_t vcpu_get_reg(struct kvm_vcpu *vcpu, uint64_t id) + +static inline u64 vcpu_get_reg(struct kvm_vcpu *vcpu, u64 id) { - uint64_t val; - struct kvm_one_reg reg = { .id = id, .addr = (uint64_t)&val }; + u64 val; + struct kvm_one_reg reg = { .id = id, .addr = (u64)&val }; TEST_ASSERT(KVM_REG_SIZE(id) <= sizeof(val), "Reg %lx too big", id); vcpu_ioctl(vcpu, KVM_GET_ONE_REG, ®); return val; } -static inline void vcpu_set_reg(struct kvm_vcpu *vcpu, uint64_t id, uint64_t val) + +static inline void vcpu_set_reg(struct kvm_vcpu *vcpu, u64 id, u64 val) { - struct kvm_one_reg reg = { .id = id, .addr = (uint64_t)&val }; + struct kvm_one_reg reg = { .id = id, .addr = (u64)&val }; TEST_ASSERT(KVM_REG_SIZE(id) <= sizeof(val), "Reg %lx too big", id); @@ -880,75 +880,75 @@ static inline int vcpu_get_stats_fd(struct kvm_vcpu *vcpu) return fd; } -int __kvm_has_device_attr(int dev_fd, uint32_t group, uint64_t attr); +int __kvm_has_device_attr(int dev_fd, u32 group, u64 attr); -static inline void kvm_has_device_attr(int dev_fd, uint32_t group, uint64_t attr) +static inline void kvm_has_device_attr(int dev_fd, u32 group, u64 attr) { int ret = __kvm_has_device_attr(dev_fd, group, attr); TEST_ASSERT(!ret, "KVM_HAS_DEVICE_ATTR failed, rc: %i errno: %i", ret, errno); } -int __kvm_device_attr_get(int dev_fd, uint32_t group, uint64_t attr, void *val); +int __kvm_device_attr_get(int dev_fd, u32 group, u64 attr, void *val); -static inline void kvm_device_attr_get(int dev_fd, uint32_t group, - uint64_t attr, void *val) +static inline void kvm_device_attr_get(int dev_fd, u32 group, + u64 attr, void *val) { int ret = __kvm_device_attr_get(dev_fd, group, attr, val); TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret)); } -int __kvm_device_attr_set(int dev_fd, uint32_t group, uint64_t attr, void *val); +int __kvm_device_attr_set(int dev_fd, u32 group, u64 attr, void *val); -static inline void kvm_device_attr_set(int dev_fd, uint32_t group, - uint64_t attr, void *val) +static inline void kvm_device_attr_set(int dev_fd, u32 group, + u64 attr, void *val) { int ret = __kvm_device_attr_set(dev_fd, group, attr, val); TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret)); } -static inline int __vcpu_has_device_attr(struct kvm_vcpu *vcpu, uint32_t group, - uint64_t attr) +static inline int __vcpu_has_device_attr(struct kvm_vcpu *vcpu, u32 group, + u64 attr) { return __kvm_has_device_attr(vcpu->fd, group, attr); } -static inline void vcpu_has_device_attr(struct kvm_vcpu *vcpu, uint32_t group, - uint64_t attr) +static inline void vcpu_has_device_attr(struct kvm_vcpu *vcpu, u32 group, + u64 attr) { kvm_has_device_attr(vcpu->fd, group, attr); } -static inline int __vcpu_device_attr_get(struct kvm_vcpu *vcpu, uint32_t group, - uint64_t attr, void *val) +static inline int __vcpu_device_attr_get(struct kvm_vcpu *vcpu, u32 group, + u64 attr, void *val) { return __kvm_device_attr_get(vcpu->fd, group, attr, val); } -static inline void vcpu_device_attr_get(struct kvm_vcpu *vcpu, uint32_t group, - uint64_t attr, void *val) +static inline void vcpu_device_attr_get(struct kvm_vcpu *vcpu, u32 group, + u64 attr, void *val) { kvm_device_attr_get(vcpu->fd, group, attr, val); } -static inline int __vcpu_device_attr_set(struct kvm_vcpu *vcpu, uint32_t group, - uint64_t attr, void *val) +static inline int __vcpu_device_attr_set(struct kvm_vcpu *vcpu, u32 group, + u64 attr, void *val) { return __kvm_device_attr_set(vcpu->fd, group, attr, val); } -static inline void vcpu_device_attr_set(struct kvm_vcpu *vcpu, uint32_t group, - uint64_t attr, void *val) +static inline void vcpu_device_attr_set(struct kvm_vcpu *vcpu, u32 group, + u64 attr, void *val) { kvm_device_attr_set(vcpu->fd, group, attr, val); } -int __kvm_test_create_device(struct kvm_vm *vm, uint64_t type); -int __kvm_create_device(struct kvm_vm *vm, uint64_t type); +int __kvm_test_create_device(struct kvm_vm *vm, u64 type); +int __kvm_create_device(struct kvm_vm *vm, u64 type); -static inline int kvm_create_device(struct kvm_vm *vm, uint64_t type) +static inline int kvm_create_device(struct kvm_vm *vm, u64 type) { int fd = __kvm_create_device(vm, type); @@ -964,7 +964,7 @@ void *vcpu_map_dirty_ring(struct kvm_vcpu *vcpu); * Input Args: * vcpu - vCPU * num - number of arguments - * ... - arguments, each of type uint64_t + * ... - arguments, each of type u64 * * Output Args: None * @@ -972,40 +972,38 @@ void *vcpu_map_dirty_ring(struct kvm_vcpu *vcpu); * * Sets the first @num input parameters for the function at @vcpu's entry point, * per the C calling convention of the architecture, to the values given as - * variable args. Each of the variable args is expected to be of type uint64_t. + * variable args. Each of the variable args is expected to be of type u64. * The maximum @num can be is specific to the architecture. */ void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...); -void kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level); -int _kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level); +void kvm_irq_line(struct kvm_vm *vm, u32 irq, int level); +int _kvm_irq_line(struct kvm_vm *vm, u32 irq, int level); #define KVM_MAX_IRQ_ROUTES 4096 struct kvm_irq_routing *kvm_gsi_routing_create(void); void kvm_gsi_routing_irqchip_add(struct kvm_irq_routing *routing, - uint32_t gsi, uint32_t pin); + u32 gsi, u32 pin); int _kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); const char *exit_reason_str(unsigned int exit_reason); -vm_paddr_t vm_phy_page_alloc(struct kvm_vm *vm, vm_paddr_t paddr_min, - uint32_t memslot); -vm_paddr_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, - vm_paddr_t paddr_min, uint32_t memslot, - bool protected); -vm_paddr_t vm_alloc_page_table(struct kvm_vm *vm); +gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, u32 memslot); +gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, gpa_t min_gpa, + u32 memslot, bool protected); +gpa_t vm_alloc_page_table(struct kvm_vm *vm); -static inline vm_paddr_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, - vm_paddr_t paddr_min, uint32_t memslot) +static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, + gpa_t min_gpa, u32 memslot) { /* * By default, allocate memory as protected for VMs that support * protected memory, as the majority of memory for such VMs is * protected, i.e. using shared memory is effectively opt-in. */ - return __vm_phy_pages_alloc(vm, num, paddr_min, memslot, + return __vm_phy_pages_alloc(vm, num, min_gpa, memslot, vm_arch_has_protected_memory(vm)); } @@ -1016,8 +1014,8 @@ static inline vm_paddr_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, * calculate the amount of memory needed for per-vCPU data, e.g. stacks. */ struct kvm_vm *____vm_create(struct vm_shape shape); -struct kvm_vm *__vm_create(struct vm_shape shape, uint32_t nr_runnable_vcpus, - uint64_t nr_extra_pages); +struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, + u64 nr_extra_pages); static inline struct kvm_vm *vm_create_barebones(void) { @@ -1034,16 +1032,16 @@ static inline struct kvm_vm *vm_create_barebones_type(unsigned long type) return ____vm_create(shape); } -static inline struct kvm_vm *vm_create(uint32_t nr_runnable_vcpus) +static inline struct kvm_vm *vm_create(u32 nr_runnable_vcpus) { return __vm_create(VM_SHAPE_DEFAULT, nr_runnable_vcpus, 0); } -struct kvm_vm *__vm_create_with_vcpus(struct vm_shape shape, uint32_t nr_vcpus, - uint64_t extra_mem_pages, +struct kvm_vm *__vm_create_with_vcpus(struct vm_shape shape, u32 nr_vcpus, + u64 extra_mem_pages, void *guest_code, struct kvm_vcpu *vcpus[]); -static inline struct kvm_vm *vm_create_with_vcpus(uint32_t nr_vcpus, +static inline struct kvm_vm *vm_create_with_vcpus(u32 nr_vcpus, void *guest_code, struct kvm_vcpu *vcpus[]) { @@ -1054,7 +1052,7 @@ static inline struct kvm_vm *vm_create_with_vcpus(uint32_t nr_vcpus, struct kvm_vm *__vm_create_shape_with_one_vcpu(struct vm_shape shape, struct kvm_vcpu **vcpu, - uint64_t extra_mem_pages, + u64 extra_mem_pages, void *guest_code); /* @@ -1062,7 +1060,7 @@ struct kvm_vm *__vm_create_shape_with_one_vcpu(struct vm_shape shape, * additional pages of guest memory. Returns the VM and vCPU (via out param). */ static inline struct kvm_vm *__vm_create_with_one_vcpu(struct kvm_vcpu **vcpu, - uint64_t extra_mem_pages, + u64 extra_mem_pages, void *guest_code) { return __vm_create_shape_with_one_vcpu(VM_SHAPE_DEFAULT, vcpu, @@ -1084,7 +1082,7 @@ static inline struct kvm_vm *vm_create_shape_with_one_vcpu(struct vm_shape shape struct kvm_vcpu *vm_recreate_with_one_vcpu(struct kvm_vm *vm); -void kvm_set_files_rlimit(uint32_t nr_vcpus); +void kvm_set_files_rlimit(u32 nr_vcpus); int __pin_task_to_cpu(pthread_t task, int cpu); @@ -1115,7 +1113,7 @@ static inline int pin_self_to_any_cpu(void) } void kvm_print_vcpu_pinning_help(void); -void kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[], +void kvm_parse_vcpu_pinning(const char *pcpus_string, u32 vcpu_to_pcpu[], int nr_vcpus); unsigned long vm_compute_max_gfn(struct kvm_vm *vm); @@ -1127,20 +1125,16 @@ vm_adjust_num_guest_pages(enum vm_guest_mode mode, unsigned int num_guest_pages) { unsigned int n; n = vm_num_guest_pages(mode, vm_num_host_pages(mode, num_guest_pages)); -#ifdef __s390x__ - /* s390 requires 1M aligned guest sizes */ - n = (n + 255) & ~255; -#endif return n; } #define sync_global_to_guest(vm, g) ({ \ - typeof(g) *_p = addr_gva2hva(vm, (vm_vaddr_t)&(g)); \ + typeof(g) *_p = addr_gva2hva(vm, (gva_t)&(g)); \ memcpy(_p, &(g), sizeof(g)); \ }) #define sync_global_from_guest(vm, g) ({ \ - typeof(g) *_p = addr_gva2hva(vm, (vm_vaddr_t)&(g)); \ + typeof(g) *_p = addr_gva2hva(vm, (gva_t)&(g)); \ memcpy(&(g), _p, sizeof(g)); \ }) @@ -1151,7 +1145,7 @@ vm_adjust_num_guest_pages(enum vm_guest_mode mode, unsigned int num_guest_pages) * undesirable to change the host's copy of the global. */ #define write_guest_global(vm, g, val) ({ \ - typeof(g) *_p = addr_gva2hva(vm, (vm_vaddr_t)&(g)); \ + typeof(g) *_p = addr_gva2hva(vm, (gva_t)&(g)); \ typeof(g) _val = val; \ \ memcpy(_p, &(_val), sizeof(g)); \ @@ -1160,10 +1154,10 @@ vm_adjust_num_guest_pages(enum vm_guest_mode mode, unsigned int num_guest_pages) void assert_on_unhandled_exception(struct kvm_vcpu *vcpu); void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, - uint8_t indent); + u8 indent); static inline void vcpu_dump(FILE *stream, struct kvm_vcpu *vcpu, - uint8_t indent) + u8 indent) { vcpu_arch_dump(stream, vcpu, indent); } @@ -1175,10 +1169,10 @@ static inline void vcpu_dump(FILE *stream, struct kvm_vcpu *vcpu, * vm - Virtual Machine * vcpu_id - The id of the VCPU to add to the VM. */ -struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id); +struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id); void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code); -static inline struct kvm_vcpu *vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, +static inline struct kvm_vcpu *vm_vcpu_add(struct kvm_vm *vm, u32 vcpu_id, void *guest_code) { struct kvm_vcpu *vcpu = vm_arch_vcpu_add(vm, vcpu_id); @@ -1189,10 +1183,10 @@ static inline struct kvm_vcpu *vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, } /* Re-create a vCPU after restarting a VM, e.g. for state save/restore tests. */ -struct kvm_vcpu *vm_arch_vcpu_recreate(struct kvm_vm *vm, uint32_t vcpu_id); +struct kvm_vcpu *vm_arch_vcpu_recreate(struct kvm_vm *vm, u32 vcpu_id); static inline struct kvm_vcpu *vm_vcpu_recreate(struct kvm_vm *vm, - uint32_t vcpu_id) + u32 vcpu_id) { return vm_arch_vcpu_recreate(vm, vcpu_id); } @@ -1207,27 +1201,15 @@ static inline void virt_pgd_alloc(struct kvm_vm *vm) } /* - * VM Virtual Page Map - * - * Input Args: - * vm - Virtual Machine - * vaddr - VM Virtual Address - * paddr - VM Physical Address - * memslot - Memory region slot for new virtual translation tables - * - * Output Args: None - * - * Return: None - * * Within @vm, creates a virtual translation for the page starting - * at @vaddr to the page starting at @paddr. + * at @gva to the page starting at @gpa. */ -void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr); +void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa); -static inline void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) +static inline void virt_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa) { - virt_arch_pg_map(vm, vaddr, paddr); - sparsebit_set(vm->vpages_mapped, vaddr >> vm->page_shift); + virt_arch_pg_map(vm, gva, gpa); + sparsebit_set(vm->vpages_mapped, gva >> vm->page_shift); } @@ -1246,9 +1228,9 @@ static inline void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr * Returns the VM physical address of the translated VM virtual * address given by @gva. */ -vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva); +gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva); -static inline vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) +static inline gpa_t addr_gva2gpa(struct kvm_vm *vm, gva_t gva) { return addr_arch_gva2gpa(vm, gva); } @@ -1268,9 +1250,9 @@ static inline vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) * Dumps to the FILE stream given by @stream, the contents of all the * virtual translation tables for the VM given by @vm. */ -void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent); +void virt_arch_dump(FILE *stream, struct kvm_vm *vm, u8 indent); -static inline void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) +static inline void virt_dump(FILE *stream, struct kvm_vm *vm, u8 indent) { virt_arch_dump(stream, vm, indent); } @@ -1281,7 +1263,7 @@ static inline int __vm_disable_nx_huge_pages(struct kvm_vm *vm) return __vm_enable_cap(vm, KVM_CAP_VM_DISABLE_NX_HUGE_PAGES, 0); } -static inline uint64_t vm_page_align(struct kvm_vm *vm, uint64_t v) +static inline u64 vm_page_align(struct kvm_vm *vm, u64 v) { return (v + vm->page_size - 1) & ~(vm->page_size - 1); } @@ -1297,9 +1279,9 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus); void kvm_arch_vm_finalize_vcpus(struct kvm_vm *vm); void kvm_arch_vm_release(struct kvm_vm *vm); -bool vm_is_gpa_protected(struct kvm_vm *vm, vm_paddr_t paddr); +bool vm_is_gpa_protected(struct kvm_vm *vm, gpa_t gpa); -uint32_t guest_get_vcpuid(void); +u32 guest_get_vcpuid(void); bool kvm_arch_has_default_irqchip(void); diff --git a/tools/testing/selftests/kvm/include/kvm_util_types.h b/tools/testing/selftests/kvm/include/kvm_util_types.h index ec787b97cf18..ed0087e31674 100644 --- a/tools/testing/selftests/kvm/include/kvm_util_types.h +++ b/tools/testing/selftests/kvm/include/kvm_util_types.h @@ -2,6 +2,8 @@ #ifndef SELFTEST_KVM_UTIL_TYPES_H #define SELFTEST_KVM_UTIL_TYPES_H +#include + /* * Provide a version of static_assert() that is guaranteed to have an optional * message param. _GNU_SOURCE is defined for all KVM selftests, _GNU_SOURCE @@ -14,7 +16,9 @@ #define __kvm_static_assert(expr, msg, ...) _Static_assert(expr, msg) #define kvm_static_assert(expr, ...) __kvm_static_assert(expr, ##__VA_ARGS__, #expr) -typedef uint64_t vm_paddr_t; /* Virtual Machine (Guest) physical address */ -typedef uint64_t vm_vaddr_t; /* Virtual Machine (Guest) virtual address */ +typedef u64 gpa_t; /* Virtual Machine (Guest) physical address */ +typedef u64 gva_t; /* Virtual Machine (Guest) virtual address */ + +#define INVALID_GPA (~(u64)0) #endif /* SELFTEST_KVM_UTIL_TYPES_H */ diff --git a/tools/testing/selftests/kvm/include/loongarch/arch_timer.h b/tools/testing/selftests/kvm/include/loongarch/arch_timer.h index 2ed106b32c81..3888aeeb3524 100644 --- a/tools/testing/selftests/kvm/include/loongarch/arch_timer.h +++ b/tools/testing/selftests/kvm/include/loongarch/arch_timer.h @@ -70,9 +70,9 @@ static inline void timer_set_next_cmp_ms(unsigned int msec, bool period) csr_write(val, LOONGARCH_CSR_TCFG); } -static inline void __delay(uint64_t cycles) +static inline void __delay(u64 cycles) { - uint64_t start = timer_get_cycles(); + u64 start = timer_get_cycles(); while ((timer_get_cycles() - start) < cycles) cpu_relax(); diff --git a/tools/testing/selftests/kvm/include/loongarch/pmu.h b/tools/testing/selftests/kvm/include/loongarch/pmu.h new file mode 100644 index 000000000000..478e6a9bbb2b --- /dev/null +++ b/tools/testing/selftests/kvm/include/loongarch/pmu.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * LoongArch PMU specific interface + */ +#ifndef SELFTEST_KVM_PMU_H +#define SELFTEST_KVM_PMU_H + +#include "processor.h" + +#define LOONGARCH_CPUCFG6 0x6 +#define CPUCFG6_PMP BIT(0) +#define CPUCFG6_PAMVER GENMASK(3, 1) +#define CPUCFG6_PMNUM GENMASK(7, 4) +#define CPUCFG6_PMNUM_SHIFT 4 +#define CPUCFG6_PMBITS GENMASK(13, 8) +#define CPUCFG6_PMBITS_SHIFT 8 +#define CPUCFG6_UPM BIT(14) + +/* Performance Counter registers */ +#define LOONGARCH_CSR_PERFCTRL0 0x200 /* perf event 0 config */ +#define LOONGARCH_CSR_PERFCNTR0 0x201 /* perf event 0 count value */ +#define LOONGARCH_CSR_PERFCTRL1 0x202 /* perf event 1 config */ +#define LOONGARCH_CSR_PERFCNTR1 0x203 /* perf event 1 count value */ +#define LOONGARCH_CSR_PERFCTRL2 0x204 /* perf event 2 config */ +#define LOONGARCH_CSR_PERFCNTR2 0x205 /* perf event 2 count value */ +#define LOONGARCH_CSR_PERFCTRL3 0x206 /* perf event 3 config */ +#define LOONGARCH_CSR_PERFCNTR3 0x207 /* perf event 3 count value */ +#define CSR_PERFCTRL_PLV0 BIT(16) +#define CSR_PERFCTRL_PLV1 BIT(17) +#define CSR_PERFCTRL_PLV2 BIT(18) +#define CSR_PERFCTRL_PLV3 BIT(19) +#define CSR_PERFCTRL_PMIE BIT(20) +#define PMU_ENVENT_ENABLED (CSR_PERFCTRL_PLV0 | CSR_PERFCTRL_PLV1 | CSR_PERFCTRL_PLV2 | CSR_PERFCTRL_PLV3) + +/* Hardware event codes (from LoongArch perf_event.c */ +#define LOONGARCH_PMU_EVENT_CYCLES 0x00 /* CPU cycles */ +#define LOONGARCH_PMU_EVENT_INSTR_RETIRED 0x01 /* Instructions retired */ +#define PERF_COUNT_HW_BRANCH_INSTRUCTIONS 0x02 /* Branch instructions */ +#define PERF_COUNT_HW_BRANCH_MISSES 0x03 /* Branch misses */ + +#define NUM_LOOPS 1000 +#define EXPECTED_INSTR_MIN (NUM_LOOPS + 10) /* Loop + overhead */ +#define EXPECTED_CYCLES_MIN NUM_LOOPS /* At least 1 cycle per iteration */ +#define UPPER_BOUND (10 * NUM_LOOPS) + +#define PMU_OVERFLOW (1ULL << 63) + +static inline void pmu_irq_enable(void) +{ + unsigned long val; + + val = csr_read(LOONGARCH_CSR_ECFG); + val |= ECFGF_PMU; + csr_write(val, LOONGARCH_CSR_ECFG); +} + +static inline void pmu_irq_disable(void) +{ + unsigned long val; + + val = csr_read(LOONGARCH_CSR_ECFG); + val &= ~ECFGF_PMU; + csr_write(val, LOONGARCH_CSR_ECFG); +} + +#endif diff --git a/tools/testing/selftests/kvm/include/loongarch/processor.h b/tools/testing/selftests/kvm/include/loongarch/processor.h index 76840ddda57d..93dc1fbd2e79 100644 --- a/tools/testing/selftests/kvm/include/loongarch/processor.h +++ b/tools/testing/selftests/kvm/include/loongarch/processor.h @@ -83,6 +83,8 @@ #define LOONGARCH_CSR_PRMD 0x1 #define LOONGARCH_CSR_EUEN 0x2 #define LOONGARCH_CSR_ECFG 0x4 +#define ECFGB_PMU 10 +#define ECFGF_PMU (BIT_ULL(ECFGB_PMU)) #define ECFGB_TIMER 11 #define ECFGF_TIMER (BIT_ULL(ECFGB_TIMER)) #define LOONGARCH_CSR_ESTAT 0x5 /* Exception status */ @@ -90,6 +92,7 @@ #define CSR_ESTAT_EXC_WIDTH 6 #define CSR_ESTAT_EXC (0x3f << CSR_ESTAT_EXC_SHIFT) #define EXCCODE_INT 0 /* Interrupt */ +#define INT_PMI 10 /* PMU interrupt */ #define INT_TI 11 /* Timer interrupt*/ #define LOONGARCH_CSR_ERA 0x6 /* ERA */ #define LOONGARCH_CSR_BADV 0x7 /* Bad virtual address */ @@ -128,6 +131,17 @@ #define CSR_TLBREHI_PS_SHIFT 0 #define CSR_TLBREHI_PS (0x3fUL << CSR_TLBREHI_PS_SHIFT) +#define read_cpucfg(reg) \ +({ \ + register unsigned long __v; \ + __asm__ __volatile__( \ + "cpucfg %0, %1\n\t" \ + : "=r" (__v) \ + : "r" (reg) \ + : "memory"); \ + __v; \ +}) + #define csr_read(csr) \ ({ \ register unsigned long __v; \ @@ -178,6 +192,7 @@ struct handlers { handler_fn exception_handlers[VECTOR_NUM]; }; +void loongarch_vcpu_setup(struct kvm_vcpu *vcpu); void vm_init_descriptor_tables(struct kvm_vm *vm); void vm_install_exception_handler(struct kvm_vm *vm, int vector, handler_fn handler); diff --git a/tools/testing/selftests/kvm/include/loongarch/ucall.h b/tools/testing/selftests/kvm/include/loongarch/ucall.h index 4ec801f37f00..2210d3d94c40 100644 --- a/tools/testing/selftests/kvm/include/loongarch/ucall.h +++ b/tools/testing/selftests/kvm/include/loongarch/ucall.h @@ -10,9 +10,9 @@ * ucall_exit_mmio_addr holds per-VM values (global data is duplicated by each * VM), it must not be accessed from host code. */ -extern vm_vaddr_t *ucall_exit_mmio_addr; +extern gva_t *ucall_exit_mmio_addr; -static inline void ucall_arch_do_ucall(vm_vaddr_t uc) +static inline void ucall_arch_do_ucall(gva_t uc) { WRITE_ONCE(*ucall_exit_mmio_addr, uc); } diff --git a/tools/testing/selftests/kvm/include/memstress.h b/tools/testing/selftests/kvm/include/memstress.h index 9071eb6dea60..0d1d6230cc05 100644 --- a/tools/testing/selftests/kvm/include/memstress.h +++ b/tools/testing/selftests/kvm/include/memstress.h @@ -20,9 +20,9 @@ #define MEMSTRESS_MEM_SLOT_INDEX 1 struct memstress_vcpu_args { - uint64_t gpa; - uint64_t gva; - uint64_t pages; + gpa_t gpa; + gva_t gva; + u64 pages; /* Only used by the host userspace part of the vCPU thread */ struct kvm_vcpu *vcpu; @@ -32,11 +32,11 @@ struct memstress_vcpu_args { struct memstress_args { struct kvm_vm *vm; /* The starting address and size of the guest test region. */ - uint64_t gpa; - uint64_t size; - uint64_t guest_page_size; - uint32_t random_seed; - uint32_t write_percent; + gpa_t gpa; + u64 size; + u64 guest_page_size; + u32 random_seed; + u32 write_percent; /* Run vCPUs in L2 instead of L1, if the architecture supports it. */ bool nested; @@ -45,7 +45,7 @@ struct memstress_args { /* True if all vCPUs are pinned to pCPUs */ bool pin_vcpus; /* The vCPU=>pCPU pinning map. Only valid if pin_vcpus is true. */ - uint32_t vcpu_to_pcpu[KVM_MAX_VCPUS]; + u32 vcpu_to_pcpu[KVM_MAX_VCPUS]; /* Test is done, stop running vCPUs. */ bool stop_vcpus; @@ -56,27 +56,27 @@ struct memstress_args { extern struct memstress_args memstress_args; struct kvm_vm *memstress_create_vm(enum vm_guest_mode mode, int nr_vcpus, - uint64_t vcpu_memory_bytes, int slots, + u64 vcpu_memory_bytes, int slots, enum vm_mem_backing_src_type backing_src, bool partition_vcpu_memory_access); void memstress_destroy_vm(struct kvm_vm *vm); -void memstress_set_write_percent(struct kvm_vm *vm, uint32_t write_percent); +void memstress_set_write_percent(struct kvm_vm *vm, u32 write_percent); void memstress_set_random_access(struct kvm_vm *vm, bool random_access); void memstress_start_vcpu_threads(int vcpus, void (*vcpu_fn)(struct memstress_vcpu_args *)); void memstress_join_vcpu_threads(int vcpus); -void memstress_guest_code(uint32_t vcpu_id); +void memstress_guest_code(u32 vcpu_id); -uint64_t memstress_nested_pages(int nr_vcpus); +u64 memstress_nested_pages(int nr_vcpus); void memstress_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[]); void memstress_enable_dirty_logging(struct kvm_vm *vm, int slots); void memstress_disable_dirty_logging(struct kvm_vm *vm, int slots); void memstress_get_dirty_log(struct kvm_vm *vm, unsigned long *bitmaps[], int slots); void memstress_clear_dirty_log(struct kvm_vm *vm, unsigned long *bitmaps[], - int slots, uint64_t pages_per_slot); -unsigned long **memstress_alloc_bitmaps(int slots, uint64_t pages_per_slot); + int slots, u64 pages_per_slot); +unsigned long **memstress_alloc_bitmaps(int slots, u64 pages_per_slot); void memstress_free_bitmaps(unsigned long *bitmaps[], int slots); #endif /* SELFTEST_KVM_MEMSTRESS_H */ diff --git a/tools/testing/selftests/kvm/include/riscv/arch_timer.h b/tools/testing/selftests/kvm/include/riscv/arch_timer.h index 225d81dad064..28ffc014da2a 100644 --- a/tools/testing/selftests/kvm/include/riscv/arch_timer.h +++ b/tools/testing/selftests/kvm/include/riscv/arch_timer.h @@ -14,25 +14,25 @@ static unsigned long timer_freq; #define msec_to_cycles(msec) \ - ((timer_freq) * (uint64_t)(msec) / 1000) + ((timer_freq) * (u64)(msec) / 1000) #define usec_to_cycles(usec) \ - ((timer_freq) * (uint64_t)(usec) / 1000000) + ((timer_freq) * (u64)(usec) / 1000000) #define cycles_to_usec(cycles) \ - ((uint64_t)(cycles) * 1000000 / (timer_freq)) + ((u64)(cycles) * 1000000 / (timer_freq)) -static inline uint64_t timer_get_cycles(void) +static inline u64 timer_get_cycles(void) { return csr_read(CSR_TIME); } -static inline void timer_set_cmp(uint64_t cval) +static inline void timer_set_cmp(u64 cval) { csr_write(CSR_STIMECMP, cval); } -static inline uint64_t timer_get_cmp(void) +static inline u64 timer_get_cmp(void) { return csr_read(CSR_STIMECMP); } @@ -47,17 +47,17 @@ static inline void timer_irq_disable(void) csr_clear(CSR_SIE, IE_TIE); } -static inline void timer_set_next_cmp_ms(uint32_t msec) +static inline void timer_set_next_cmp_ms(u32 msec) { - uint64_t now_ct = timer_get_cycles(); - uint64_t next_ct = now_ct + msec_to_cycles(msec); + u64 now_ct = timer_get_cycles(); + u64 next_ct = now_ct + msec_to_cycles(msec); timer_set_cmp(next_ct); } -static inline void __delay(uint64_t cycles) +static inline void __delay(u64 cycles) { - uint64_t start = timer_get_cycles(); + u64 start = timer_get_cycles(); while ((timer_get_cycles() - start) < cycles) cpu_relax(); diff --git a/tools/testing/selftests/kvm/include/riscv/processor.h b/tools/testing/selftests/kvm/include/riscv/processor.h index 4dade8c4d18e..e3acf2ae9881 100644 --- a/tools/testing/selftests/kvm/include/riscv/processor.h +++ b/tools/testing/selftests/kvm/include/riscv/processor.h @@ -25,8 +25,7 @@ #define GET_RM(insn) (((insn) & INSN_MASK_FUNCT3) >> INSN_SHIFT_FUNCT3) #define GET_CSR_NUM(insn) (((insn) & INSN_CSR_MASK) >> INSN_CSR_SHIFT) -static inline uint64_t __kvm_reg_id(uint64_t type, uint64_t subtype, - uint64_t idx, uint64_t size) +static inline u64 __kvm_reg_id(u64 type, u64 subtype, u64 idx, u64 size) { return KVM_REG_RISCV | type | subtype | idx | size; } @@ -62,14 +61,14 @@ static inline uint64_t __kvm_reg_id(uint64_t type, uint64_t subtype, KVM_REG_RISCV_SBI_SINGLE, \ idx, KVM_REG_SIZE_ULONG) -bool __vcpu_has_ext(struct kvm_vcpu *vcpu, uint64_t ext); +bool __vcpu_has_ext(struct kvm_vcpu *vcpu, u64 ext); -static inline bool __vcpu_has_isa_ext(struct kvm_vcpu *vcpu, uint64_t isa_ext) +static inline bool __vcpu_has_isa_ext(struct kvm_vcpu *vcpu, u64 isa_ext) { return __vcpu_has_ext(vcpu, RISCV_ISA_EXT_REG(isa_ext)); } -static inline bool __vcpu_has_sbi_ext(struct kvm_vcpu *vcpu, uint64_t sbi_ext) +static inline bool __vcpu_has_sbi_ext(struct kvm_vcpu *vcpu, u64 sbi_ext) { return __vcpu_has_ext(vcpu, RISCV_SBI_EXT_REG(sbi_ext)); } diff --git a/tools/testing/selftests/kvm/include/riscv/sbi.h b/tools/testing/selftests/kvm/include/riscv/sbi.h index 046b432ae896..16f1815ac48f 100644 --- a/tools/testing/selftests/kvm/include/riscv/sbi.h +++ b/tools/testing/selftests/kvm/include/riscv/sbi.h @@ -97,6 +97,43 @@ enum sbi_pmu_hw_generic_events_t { SBI_PMU_HW_GENERAL_MAX, }; +enum sbi_pmu_fw_generic_events_t { + SBI_PMU_FW_MISALIGNED_LOAD = 0, + SBI_PMU_FW_MISALIGNED_STORE = 1, + SBI_PMU_FW_ACCESS_LOAD = 2, + SBI_PMU_FW_ACCESS_STORE = 3, + SBI_PMU_FW_ILLEGAL_INSN = 4, + SBI_PMU_FW_SET_TIMER = 5, + SBI_PMU_FW_IPI_SENT = 6, + SBI_PMU_FW_IPI_RCVD = 7, + SBI_PMU_FW_FENCE_I_SENT = 8, + SBI_PMU_FW_FENCE_I_RCVD = 9, + SBI_PMU_FW_SFENCE_VMA_SENT = 10, + SBI_PMU_FW_SFENCE_VMA_RCVD = 11, + SBI_PMU_FW_SFENCE_VMA_ASID_SENT = 12, + SBI_PMU_FW_SFENCE_VMA_ASID_RCVD = 13, + + SBI_PMU_FW_HFENCE_GVMA_SENT = 14, + SBI_PMU_FW_HFENCE_GVMA_RCVD = 15, + SBI_PMU_FW_HFENCE_GVMA_VMID_SENT = 16, + SBI_PMU_FW_HFENCE_GVMA_VMID_RCVD = 17, + + SBI_PMU_FW_HFENCE_VVMA_SENT = 18, + SBI_PMU_FW_HFENCE_VVMA_RCVD = 19, + SBI_PMU_FW_HFENCE_VVMA_ASID_SENT = 20, + SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD = 21, + SBI_PMU_FW_MAX, +}; + +/* SBI PMU event types */ +enum sbi_pmu_event_type { + SBI_PMU_EVENT_TYPE_HW = 0x0, + SBI_PMU_EVENT_TYPE_CACHE = 0x1, + SBI_PMU_EVENT_TYPE_RAW = 0x2, + SBI_PMU_EVENT_TYPE_RAW_V2 = 0x3, + SBI_PMU_EVENT_TYPE_FW = 0xf, +}; + /* SBI PMU counter types */ enum sbi_pmu_ctr_type { SBI_PMU_CTR_TYPE_HW = 0x0, diff --git a/tools/testing/selftests/kvm/include/riscv/ucall.h b/tools/testing/selftests/kvm/include/riscv/ucall.h index a695ae36f3e0..2de7c6a36096 100644 --- a/tools/testing/selftests/kvm/include/riscv/ucall.h +++ b/tools/testing/selftests/kvm/include/riscv/ucall.h @@ -7,11 +7,11 @@ #define UCALL_EXIT_REASON KVM_EXIT_RISCV_SBI -static inline void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) +static inline void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa) { } -static inline void ucall_arch_do_ucall(vm_vaddr_t uc) +static inline void ucall_arch_do_ucall(gva_t uc) { sbi_ecall(KVM_RISCV_SELFTESTS_SBI_EXT, KVM_RISCV_SELFTESTS_SBI_UCALL, diff --git a/tools/testing/selftests/kvm/include/s390/diag318_test_handler.h b/tools/testing/selftests/kvm/include/s390/diag318_test_handler.h index b0ed71302722..6deaf18fec22 100644 --- a/tools/testing/selftests/kvm/include/s390/diag318_test_handler.h +++ b/tools/testing/selftests/kvm/include/s390/diag318_test_handler.h @@ -8,6 +8,6 @@ #ifndef SELFTEST_KVM_DIAG318_TEST_HANDLER #define SELFTEST_KVM_DIAG318_TEST_HANDLER -uint64_t get_diag318_info(void); +u64 get_diag318_info(void); #endif diff --git a/tools/testing/selftests/kvm/include/s390/facility.h b/tools/testing/selftests/kvm/include/s390/facility.h index 00a1ced6538b..41a265742666 100644 --- a/tools/testing/selftests/kvm/include/s390/facility.h +++ b/tools/testing/selftests/kvm/include/s390/facility.h @@ -16,7 +16,7 @@ /* alt_stfle_fac_list[16] + stfle_fac_list[16] */ #define NB_STFL_DOUBLEWORDS 32 -extern uint64_t stfl_doublewords[NB_STFL_DOUBLEWORDS]; +extern u64 stfl_doublewords[NB_STFL_DOUBLEWORDS]; extern bool stfle_flag; static inline bool test_bit_inv(unsigned long nr, const unsigned long *ptr) @@ -24,7 +24,7 @@ static inline bool test_bit_inv(unsigned long nr, const unsigned long *ptr) return test_bit(nr ^ (BITS_PER_LONG - 1), ptr); } -static inline void stfle(uint64_t *fac, unsigned int nb_doublewords) +static inline void stfle(u64 *fac, unsigned int nb_doublewords) { register unsigned long r0 asm("0") = nb_doublewords - 1; diff --git a/tools/testing/selftests/kvm/include/s390/ucall.h b/tools/testing/selftests/kvm/include/s390/ucall.h index 8035a872a351..3907d629304f 100644 --- a/tools/testing/selftests/kvm/include/s390/ucall.h +++ b/tools/testing/selftests/kvm/include/s390/ucall.h @@ -6,11 +6,11 @@ #define UCALL_EXIT_REASON KVM_EXIT_S390_SIEIC -static inline void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) +static inline void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa) { } -static inline void ucall_arch_do_ucall(vm_vaddr_t uc) +static inline void ucall_arch_do_ucall(gva_t uc) { /* Exit via DIAGNOSE 0x501 (normally used for breakpoints) */ asm volatile ("diag 0,%0,0x501" : : "a"(uc) : "memory"); diff --git a/tools/testing/selftests/kvm/include/sparsebit.h b/tools/testing/selftests/kvm/include/sparsebit.h index bc760761e1a3..e027e5790946 100644 --- a/tools/testing/selftests/kvm/include/sparsebit.h +++ b/tools/testing/selftests/kvm/include/sparsebit.h @@ -6,7 +6,7 @@ * * Header file that describes API to the sparsebit library. * This library provides a memory efficient means of storing - * the settings of bits indexed via a uint64_t. Memory usage + * the settings of bits indexed via a u64. Memory usage * is reasonable, significantly less than (2^64 / 8) bytes, as * long as bits that are mostly set or mostly cleared are close * to each other. This library is efficient in memory usage @@ -25,8 +25,8 @@ extern "C" { #endif struct sparsebit; -typedef uint64_t sparsebit_idx_t; -typedef uint64_t sparsebit_num_t; +typedef u64 sparsebit_idx_t; +typedef u64 sparsebit_num_t; struct sparsebit *sparsebit_alloc(void); void sparsebit_free(struct sparsebit **sbitp); diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h index b4872ba8ed12..d9b433b834f1 100644 --- a/tools/testing/selftests/kvm/include/test_util.h +++ b/tools/testing/selftests/kvm/include/test_util.h @@ -22,6 +22,8 @@ #include #include "kselftest.h" +#include + #define msecs_to_usecs(msec) ((msec) * 1000ULL) static inline __printf(1, 2) int _no_printf(const char *format, ...) { return 0; } @@ -99,25 +101,25 @@ do { \ size_t parse_size(const char *size); -int64_t timespec_to_ns(struct timespec ts); -struct timespec timespec_add_ns(struct timespec ts, int64_t ns); +s64 timespec_to_ns(struct timespec ts); +struct timespec timespec_add_ns(struct timespec ts, s64 ns); struct timespec timespec_add(struct timespec ts1, struct timespec ts2); struct timespec timespec_sub(struct timespec ts1, struct timespec ts2); struct timespec timespec_elapsed(struct timespec start); struct timespec timespec_div(struct timespec ts, int divisor); struct guest_random_state { - uint32_t seed; + u32 seed; }; -extern uint32_t guest_random_seed; +extern u32 guest_random_seed; extern struct guest_random_state guest_rng; -struct guest_random_state new_guest_random_state(uint32_t seed); -uint32_t guest_random_u32(struct guest_random_state *state); +struct guest_random_state new_guest_random_state(u32 seed); +u32 guest_random_u32(struct guest_random_state *state); static inline bool __guest_random_bool(struct guest_random_state *state, - uint8_t percent) + u8 percent) { return (guest_random_u32(state) % 100) < percent; } @@ -127,9 +129,9 @@ static inline bool guest_random_bool(struct guest_random_state *state) return __guest_random_bool(state, 50); } -static inline uint64_t guest_random_u64(struct guest_random_state *state) +static inline u64 guest_random_u64(struct guest_random_state *state) { - return ((uint64_t)guest_random_u32(state) << 32) | guest_random_u32(state); + return ((u64)guest_random_u32(state) << 32) | guest_random_u32(state); } enum vm_mem_backing_src_type { @@ -158,7 +160,7 @@ enum vm_mem_backing_src_type { struct vm_mem_backing_src_alias { const char *name; - uint32_t flag; + u32 flag; }; #define MIN_RUN_DELAY_NS 200000UL @@ -166,9 +168,9 @@ struct vm_mem_backing_src_alias { bool thp_configured(void); size_t get_trans_hugepagesz(void); size_t get_def_hugetlb_pagesz(void); -const struct vm_mem_backing_src_alias *vm_mem_backing_src_alias(uint32_t i); -size_t get_backing_src_pagesz(uint32_t i); -bool is_backing_src_hugetlb(uint32_t i); +const struct vm_mem_backing_src_alias *vm_mem_backing_src_alias(u32 i); +size_t get_backing_src_pagesz(u32 i); +bool is_backing_src_hugetlb(u32 i); void backing_src_help(const char *flag); enum vm_mem_backing_src_type parse_backing_src_type(const char *type_name); long get_run_delay(void); @@ -189,18 +191,18 @@ static inline bool backing_src_can_be_huge(enum vm_mem_backing_src_type t) } /* Aligns x up to the next multiple of size. Size must be a power of 2. */ -static inline uint64_t align_up(uint64_t x, uint64_t size) +static inline u64 align_up(u64 x, u64 size) { - uint64_t mask = size - 1; + u64 mask = size - 1; TEST_ASSERT(size != 0 && !(size & (size - 1)), "size not a power of 2: %lu", size); return ((x + mask) & ~mask); } -static inline uint64_t align_down(uint64_t x, uint64_t size) +static inline u64 align_down(u64 x, u64 size) { - uint64_t x_aligned_up = align_up(x, size); + u64 x_aligned_up = align_up(x, size); if (x == x_aligned_up) return x; @@ -215,7 +217,7 @@ static inline void *align_ptr_up(void *x, size_t size) int atoi_paranoid(const char *num_str); -static inline uint32_t atoi_positive(const char *name, const char *num_str) +static inline u32 atoi_positive(const char *name, const char *num_str) { int num = atoi_paranoid(num_str); @@ -223,7 +225,7 @@ static inline uint32_t atoi_positive(const char *name, const char *num_str) return num; } -static inline uint32_t atoi_non_negative(const char *name, const char *num_str) +static inline u32 atoi_non_negative(const char *name, const char *num_str) { int num = atoi_paranoid(num_str); diff --git a/tools/testing/selftests/kvm/include/timer_test.h b/tools/testing/selftests/kvm/include/timer_test.h index 9b6edaafe6d4..b7d5d2c84701 100644 --- a/tools/testing/selftests/kvm/include/timer_test.h +++ b/tools/testing/selftests/kvm/include/timer_test.h @@ -18,21 +18,21 @@ /* Timer test cmdline parameters */ struct test_args { - uint32_t nr_vcpus; - uint32_t nr_iter; - uint32_t timer_period_ms; - uint32_t migration_freq_ms; - uint32_t timer_err_margin_us; + u32 nr_vcpus; + u32 nr_iter; + u32 timer_period_ms; + u32 migration_freq_ms; + u32 timer_err_margin_us; /* Members of struct kvm_arm_counter_offset */ - uint64_t counter_offset; - uint64_t reserved; + u64 counter_offset; + u64 reserved; }; /* Shared variables between host and guest */ struct test_vcpu_shared_data { - uint32_t nr_iter; + u32 nr_iter; int guest_stage; - uint64_t xcnt; + u64 xcnt; }; extern struct test_args test_args; diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h index d9d6581b8d4f..cbdcb0a50c4f 100644 --- a/tools/testing/selftests/kvm/include/ucall_common.h +++ b/tools/testing/selftests/kvm/include/ucall_common.h @@ -21,26 +21,26 @@ enum { #define UCALL_BUFFER_LEN 1024 struct ucall { - uint64_t cmd; - uint64_t args[UCALL_MAX_ARGS]; + u64 cmd; + u64 args[UCALL_MAX_ARGS]; char buffer[UCALL_BUFFER_LEN]; /* Host virtual address of this struct. */ struct ucall *hva; }; -void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa); -void ucall_arch_do_ucall(vm_vaddr_t uc); +void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa); +void ucall_arch_do_ucall(gva_t uc); void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu); -void ucall(uint64_t cmd, int nargs, ...); -__printf(2, 3) void ucall_fmt(uint64_t cmd, const char *fmt, ...); -__printf(5, 6) void ucall_assert(uint64_t cmd, const char *exp, +void ucall(u64 cmd, int nargs, ...); +__printf(2, 3) void ucall_fmt(u64 cmd, const char *fmt, ...); +__printf(5, 6) void ucall_assert(u64 cmd, const char *exp, const char *file, unsigned int line, const char *fmt, ...); -uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc); -void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa); -int ucall_nr_pages_required(uint64_t page_size); +u64 get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc); +void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa); +int ucall_nr_pages_required(u64 page_size); /* * Perform userspace call without any associated data. This bare call avoids @@ -48,7 +48,7 @@ int ucall_nr_pages_required(uint64_t page_size); * the full ucall() are problematic and/or unwanted. Note, this will come out * as UCALL_NONE on the backend. */ -#define GUEST_UCALL_NONE() ucall_arch_do_ucall((vm_vaddr_t)NULL) +#define GUEST_UCALL_NONE() ucall_arch_do_ucall((gva_t)NULL) #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \ ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4) diff --git a/tools/testing/selftests/kvm/include/userfaultfd_util.h b/tools/testing/selftests/kvm/include/userfaultfd_util.h index 60f7f9d435dc..0bc1dc16600e 100644 --- a/tools/testing/selftests/kvm/include/userfaultfd_util.h +++ b/tools/testing/selftests/kvm/include/userfaultfd_util.h @@ -25,7 +25,7 @@ struct uffd_reader_args { struct uffd_desc { int uffd; - uint64_t num_readers; + u64 num_readers; /* Holds the write ends of the pipes for killing the readers. */ int *pipefds; pthread_t *readers; @@ -33,8 +33,8 @@ struct uffd_desc { }; struct uffd_desc *uffd_setup_demand_paging(int uffd_mode, useconds_t delay, - void *hva, uint64_t len, - uint64_t num_readers, + void *hva, u64 len, + u64 num_readers, uffd_handler_t handler); void uffd_stop_demand_paging(struct uffd_desc *uffd); diff --git a/tools/testing/selftests/kvm/include/x86/apic.h b/tools/testing/selftests/kvm/include/x86/apic.h index 5ca6bacbd70e..31887bdc3d6c 100644 --- a/tools/testing/selftests/kvm/include/x86/apic.h +++ b/tools/testing/selftests/kvm/include/x86/apic.h @@ -79,42 +79,42 @@ void apic_disable(void); void xapic_enable(void); void x2apic_enable(void); -static inline uint32_t get_bsp_flag(void) +static inline u32 get_bsp_flag(void) { return rdmsr(MSR_IA32_APICBASE) & MSR_IA32_APICBASE_BSP; } -static inline uint32_t xapic_read_reg(unsigned int reg) +static inline u32 xapic_read_reg(unsigned int reg) { - return ((volatile uint32_t *)APIC_DEFAULT_GPA)[reg >> 2]; + return ((volatile u32 *)APIC_DEFAULT_GPA)[reg >> 2]; } -static inline void xapic_write_reg(unsigned int reg, uint32_t val) +static inline void xapic_write_reg(unsigned int reg, u32 val) { - ((volatile uint32_t *)APIC_DEFAULT_GPA)[reg >> 2] = val; + ((volatile u32 *)APIC_DEFAULT_GPA)[reg >> 2] = val; } -static inline uint64_t x2apic_read_reg(unsigned int reg) +static inline u64 x2apic_read_reg(unsigned int reg) { return rdmsr(APIC_BASE_MSR + (reg >> 4)); } -static inline uint8_t x2apic_write_reg_safe(unsigned int reg, uint64_t value) +static inline u8 x2apic_write_reg_safe(unsigned int reg, u64 value) { return wrmsr_safe(APIC_BASE_MSR + (reg >> 4), value); } -static inline void x2apic_write_reg(unsigned int reg, uint64_t value) +static inline void x2apic_write_reg(unsigned int reg, u64 value) { - uint8_t fault = x2apic_write_reg_safe(reg, value); + u8 fault = x2apic_write_reg_safe(reg, value); __GUEST_ASSERT(!fault, "Unexpected fault 0x%x on WRMSR(%x) = %lx\n", fault, APIC_BASE_MSR + (reg >> 4), value); } -static inline void x2apic_write_reg_fault(unsigned int reg, uint64_t value) +static inline void x2apic_write_reg_fault(unsigned int reg, u64 value) { - uint8_t fault = x2apic_write_reg_safe(reg, value); + u8 fault = x2apic_write_reg_safe(reg, value); __GUEST_ASSERT(fault == GP_VECTOR, "Wanted #GP on WRMSR(%x) = %lx, got 0x%x\n", diff --git a/tools/testing/selftests/kvm/include/x86/evmcs.h b/tools/testing/selftests/kvm/include/x86/evmcs.h index 5a74bb30e2f8..be79bda024bf 100644 --- a/tools/testing/selftests/kvm/include/x86/evmcs.h +++ b/tools/testing/selftests/kvm/include/x86/evmcs.h @@ -10,9 +10,9 @@ #include "hyperv.h" #include "vmx.h" -#define u16 uint16_t -#define u32 uint32_t -#define u64 uint64_t +#define u16 u16 +#define u32 u32 +#define u64 u64 #define EVMCS_VERSION 1 @@ -245,7 +245,7 @@ static inline void evmcs_enable(void) enable_evmcs = true; } -static inline int evmcs_vmptrld(uint64_t vmcs_pa, void *vmcs) +static inline int evmcs_vmptrld(u64 vmcs_pa, void *vmcs) { current_vp_assist->current_nested_vmcs = vmcs_pa; current_vp_assist->enlighten_vmentry = 1; @@ -265,7 +265,7 @@ static inline bool load_evmcs(struct hyperv_test_pages *hv) return true; } -static inline int evmcs_vmptrst(uint64_t *value) +static inline int evmcs_vmptrst(u64 *value) { *value = current_vp_assist->current_nested_vmcs & ~HV_X64_MSR_VP_ASSIST_PAGE_ENABLE; @@ -273,7 +273,7 @@ static inline int evmcs_vmptrst(uint64_t *value) return 0; } -static inline int evmcs_vmread(uint64_t encoding, uint64_t *value) +static inline int evmcs_vmread(u64 encoding, u64 *value) { switch (encoding) { case GUEST_RIP: @@ -672,7 +672,7 @@ static inline int evmcs_vmread(uint64_t encoding, uint64_t *value) return 0; } -static inline int evmcs_vmwrite(uint64_t encoding, uint64_t value) +static inline int evmcs_vmwrite(u64 encoding, u64 value) { switch (encoding) { case GUEST_RIP: @@ -1226,9 +1226,9 @@ static inline int evmcs_vmlaunch(void) "pop %%rbp;" : [ret]"=&a"(ret) : [host_rsp]"r" - ((uint64_t)¤t_evmcs->host_rsp), + ((u64)¤t_evmcs->host_rsp), [host_rip]"r" - ((uint64_t)¤t_evmcs->host_rip) + ((u64)¤t_evmcs->host_rip) : "memory", "cc", "rbx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"); return ret; @@ -1265,9 +1265,9 @@ static inline int evmcs_vmresume(void) "pop %%rbp;" : [ret]"=&a"(ret) : [host_rsp]"r" - ((uint64_t)¤t_evmcs->host_rsp), + ((u64)¤t_evmcs->host_rsp), [host_rip]"r" - ((uint64_t)¤t_evmcs->host_rip) + ((u64)¤t_evmcs->host_rip) : "memory", "cc", "rbx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"); return ret; diff --git a/tools/testing/selftests/kvm/include/x86/hyperv.h b/tools/testing/selftests/kvm/include/x86/hyperv.h index f13e532be240..78003f5a22f3 100644 --- a/tools/testing/selftests/kvm/include/x86/hyperv.h +++ b/tools/testing/selftests/kvm/include/x86/hyperv.h @@ -254,12 +254,12 @@ * Issue a Hyper-V hypercall. Returns exception vector raised or 0, 'hv_status' * is set to the hypercall status (if no exception occurred). */ -static inline uint8_t __hyperv_hypercall(u64 control, vm_vaddr_t input_address, - vm_vaddr_t output_address, - uint64_t *hv_status) +static inline u8 __hyperv_hypercall(u64 control, gva_t input_address, + gva_t output_address, + u64 *hv_status) { - uint64_t error_code; - uint8_t vector; + u64 error_code; + u8 vector; /* Note both the hypercall and the "asm safe" clobber r9-r11. */ asm volatile("mov %[output_address], %%r8\n\t" @@ -274,11 +274,11 @@ static inline uint8_t __hyperv_hypercall(u64 control, vm_vaddr_t input_address, } /* Issue a Hyper-V hypercall and assert that it succeeded. */ -static inline void hyperv_hypercall(u64 control, vm_vaddr_t input_address, - vm_vaddr_t output_address) +static inline void hyperv_hypercall(u64 control, gva_t input_address, + gva_t output_address) { - uint64_t hv_status; - uint8_t vector; + u64 hv_status; + u8 vector; vector = __hyperv_hypercall(control, input_address, output_address, &hv_status); @@ -327,27 +327,27 @@ struct hv_vp_assist_page { extern struct hv_vp_assist_page *current_vp_assist; -int enable_vp_assist(uint64_t vp_assist_pa, void *vp_assist); +int enable_vp_assist(u64 vp_assist_pa, void *vp_assist); struct hyperv_test_pages { /* VP assist page */ void *vp_assist_hva; - uint64_t vp_assist_gpa; + u64 vp_assist_gpa; void *vp_assist; /* Partition assist page */ void *partition_assist_hva; - uint64_t partition_assist_gpa; + u64 partition_assist_gpa; void *partition_assist; /* Enlightened VMCS */ void *enlightened_vmcs_hva; - uint64_t enlightened_vmcs_gpa; + u64 enlightened_vmcs_gpa; void *enlightened_vmcs; }; struct hyperv_test_pages *vcpu_alloc_hyperv_test_pages(struct kvm_vm *vm, - vm_vaddr_t *p_hv_pages_gva); + gva_t *p_hv_pages_gva); /* HV_X64_MSR_TSC_INVARIANT_CONTROL bits */ #define HV_INVARIANT_TSC_EXPOSED BIT_ULL(0) diff --git a/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h b/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h index be35d26bb320..c33ab6e04171 100644 --- a/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h +++ b/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h @@ -11,19 +11,19 @@ extern bool is_forced_emulation_enabled; struct pte_masks { - uint64_t present; - uint64_t writable; - uint64_t user; - uint64_t readable; - uint64_t executable; - uint64_t accessed; - uint64_t dirty; - uint64_t huge; - uint64_t nx; - uint64_t c; - uint64_t s; + u64 present; + u64 writable; + u64 user; + u64 readable; + u64 executable; + u64 accessed; + u64 dirty; + u64 huge; + u64 nx; + u64 c; + u64 s; - uint64_t always_set; + u64 always_set; }; struct kvm_mmu_arch { @@ -33,12 +33,12 @@ struct kvm_mmu_arch { struct kvm_mmu; struct kvm_vm_arch { - vm_vaddr_t gdt; - vm_vaddr_t tss; - vm_vaddr_t idt; + gva_t gdt; + gva_t tss; + gva_t idt; - uint64_t c_bit; - uint64_t s_bit; + u64 c_bit; + u64 s_bit; int sev_fd; bool is_pt_protected; }; @@ -62,7 +62,7 @@ do { \ : "+m" (mem) \ : "r" (val) : "memory"); \ } else { \ - uint64_t __old = READ_ONCE(mem); \ + u64 __old = READ_ONCE(mem); \ \ __asm__ __volatile__(KVM_FEP LOCK_PREFIX "cmpxchg %[new], %[ptr]" \ : [ptr] "+m" (mem), [old] "+a" (__old) \ diff --git a/tools/testing/selftests/kvm/include/x86/pmu.h b/tools/testing/selftests/kvm/include/x86/pmu.h index 72575eadb63a..98537cc8840d 100644 --- a/tools/testing/selftests/kvm/include/x86/pmu.h +++ b/tools/testing/selftests/kvm/include/x86/pmu.h @@ -6,8 +6,8 @@ #define SELFTEST_KVM_PMU_H #include -#include +#include #include #define KVM_PMU_EVENT_FILTER_MAX_EVENTS 300 @@ -104,14 +104,15 @@ enum amd_pmu_zen_events { NR_AMD_ZEN_EVENTS, }; -extern const uint64_t intel_pmu_arch_events[]; -extern const uint64_t amd_pmu_zen_events[]; +extern const u64 intel_pmu_arch_events[]; +extern const u64 amd_pmu_zen_events[]; enum pmu_errata { INSTRUCTIONS_RETIRED_OVERCOUNT, BRANCHES_RETIRED_OVERCOUNT, }; -extern uint64_t pmu_errata_mask; + +extern u64 pmu_errata_mask; void kvm_init_pmu_errata(void); diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h index 469a22122157..77f576ee7789 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -21,7 +21,9 @@ extern bool host_cpu_is_intel; extern bool host_cpu_is_amd; -extern uint64_t guest_tsc_khz; +extern bool host_cpu_is_hygon; +extern bool host_cpu_is_amd_compatible; +extern u64 guest_tsc_khz; #ifndef MAX_NR_CPUID_ENTRIES #define MAX_NR_CPUID_ENTRIES 100 @@ -397,17 +399,17 @@ struct gpr64_regs { }; struct desc64 { - uint16_t limit0; - uint16_t base0; + u16 limit0; + u16 base0; unsigned base1:8, type:4, s:1, dpl:2, p:1; unsigned limit1:4, avl:1, l:1, db:1, g:1, base2:8; - uint32_t base3; - uint32_t zero1; + u32 base3; + u32 zero1; } __attribute__((packed)); struct desc_ptr { - uint16_t size; - uint64_t address; + u16 size; + u64 address; } __attribute__((packed)); struct kvm_x86_state { @@ -425,18 +427,18 @@ struct kvm_x86_state { struct kvm_msrs msrs; }; -static inline uint64_t get_desc64_base(const struct desc64 *desc) +static inline u64 get_desc64_base(const struct desc64 *desc) { - return (uint64_t)desc->base3 << 32 | - (uint64_t)desc->base2 << 24 | - (uint64_t)desc->base1 << 16 | - (uint64_t)desc->base0; + return (u64)desc->base3 << 32 | + (u64)desc->base2 << 24 | + (u64)desc->base1 << 16 | + (u64)desc->base0; } -static inline uint64_t rdtsc(void) +static inline u64 rdtsc(void) { - uint32_t eax, edx; - uint64_t tsc_val; + u32 eax, edx; + u64 tsc_val; /* * The lfence is to wait (on Intel CPUs) until all previous * instructions have been executed. If software requires RDTSC to be @@ -444,39 +446,39 @@ static inline uint64_t rdtsc(void) * execute LFENCE immediately after RDTSC */ __asm__ __volatile__("lfence; rdtsc; lfence" : "=a"(eax), "=d"(edx)); - tsc_val = ((uint64_t)edx) << 32 | eax; + tsc_val = ((u64)edx) << 32 | eax; return tsc_val; } -static inline uint64_t rdtscp(uint32_t *aux) +static inline u64 rdtscp(u32 *aux) { - uint32_t eax, edx; + u32 eax, edx; __asm__ __volatile__("rdtscp" : "=a"(eax), "=d"(edx), "=c"(*aux)); - return ((uint64_t)edx) << 32 | eax; + return ((u64)edx) << 32 | eax; } -static inline uint64_t rdmsr(uint32_t msr) +static inline u64 rdmsr(u32 msr) { - uint32_t a, d; + u32 a, d; __asm__ __volatile__("rdmsr" : "=a"(a), "=d"(d) : "c"(msr) : "memory"); - return a | ((uint64_t) d << 32); + return a | ((u64)d << 32); } -static inline void wrmsr(uint32_t msr, uint64_t value) +static inline void wrmsr(u32 msr, u64 value) { - uint32_t a = value; - uint32_t d = value >> 32; + u32 a = value; + u32 d = value >> 32; __asm__ __volatile__("wrmsr" :: "a"(a), "d"(d), "c"(msr) : "memory"); } -static inline uint16_t inw(uint16_t port) +static inline u16 inw(u16 port) { - uint16_t tmp; + u16 tmp; __asm__ __volatile__("in %%dx, %%ax" : /* output */ "=a" (tmp) @@ -485,120 +487,120 @@ static inline uint16_t inw(uint16_t port) return tmp; } -static inline uint16_t get_es(void) +static inline u16 get_es(void) { - uint16_t es; + u16 es; __asm__ __volatile__("mov %%es, %[es]" : /* output */ [es]"=rm"(es)); return es; } -static inline uint16_t get_cs(void) +static inline u16 get_cs(void) { - uint16_t cs; + u16 cs; __asm__ __volatile__("mov %%cs, %[cs]" : /* output */ [cs]"=rm"(cs)); return cs; } -static inline uint16_t get_ss(void) +static inline u16 get_ss(void) { - uint16_t ss; + u16 ss; __asm__ __volatile__("mov %%ss, %[ss]" : /* output */ [ss]"=rm"(ss)); return ss; } -static inline uint16_t get_ds(void) +static inline u16 get_ds(void) { - uint16_t ds; + u16 ds; __asm__ __volatile__("mov %%ds, %[ds]" : /* output */ [ds]"=rm"(ds)); return ds; } -static inline uint16_t get_fs(void) +static inline u16 get_fs(void) { - uint16_t fs; + u16 fs; __asm__ __volatile__("mov %%fs, %[fs]" : /* output */ [fs]"=rm"(fs)); return fs; } -static inline uint16_t get_gs(void) +static inline u16 get_gs(void) { - uint16_t gs; + u16 gs; __asm__ __volatile__("mov %%gs, %[gs]" : /* output */ [gs]"=rm"(gs)); return gs; } -static inline uint16_t get_tr(void) +static inline u16 get_tr(void) { - uint16_t tr; + u16 tr; __asm__ __volatile__("str %[tr]" : /* output */ [tr]"=rm"(tr)); return tr; } -static inline uint64_t get_cr0(void) +static inline u64 get_cr0(void) { - uint64_t cr0; + u64 cr0; __asm__ __volatile__("mov %%cr0, %[cr0]" : /* output */ [cr0]"=r"(cr0)); return cr0; } -static inline void set_cr0(uint64_t val) +static inline void set_cr0(u64 val) { __asm__ __volatile__("mov %0, %%cr0" : : "r" (val) : "memory"); } -static inline uint64_t get_cr3(void) +static inline u64 get_cr3(void) { - uint64_t cr3; + u64 cr3; __asm__ __volatile__("mov %%cr3, %[cr3]" : /* output */ [cr3]"=r"(cr3)); return cr3; } -static inline void set_cr3(uint64_t val) +static inline void set_cr3(u64 val) { __asm__ __volatile__("mov %0, %%cr3" : : "r" (val) : "memory"); } -static inline uint64_t get_cr4(void) +static inline u64 get_cr4(void) { - uint64_t cr4; + u64 cr4; __asm__ __volatile__("mov %%cr4, %[cr4]" : /* output */ [cr4]"=r"(cr4)); return cr4; } -static inline void set_cr4(uint64_t val) +static inline void set_cr4(u64 val) { __asm__ __volatile__("mov %0, %%cr4" : : "r" (val) : "memory"); } -static inline uint64_t get_cr8(void) +static inline u64 get_cr8(void) { - uint64_t cr8; + u64 cr8; __asm__ __volatile__("mov %%cr8, %[cr8]" : [cr8]"=r"(cr8)); return cr8; } -static inline void set_cr8(uint64_t val) +static inline void set_cr8(u64 val) { __asm__ __volatile__("mov %0, %%cr8" : : "r" (val) : "memory"); } @@ -649,14 +651,14 @@ static inline struct desc_ptr get_idt(void) return idt; } -static inline void outl(uint16_t port, uint32_t value) +static inline void outl(u16 port, u32 value) { __asm__ __volatile__("outl %%eax, %%dx" : : "d"(port), "a"(value)); } -static inline void __cpuid(uint32_t function, uint32_t index, - uint32_t *eax, uint32_t *ebx, - uint32_t *ecx, uint32_t *edx) +static inline void __cpuid(u32 function, u32 index, + u32 *eax, u32 *ebx, + u32 *ecx, u32 *edx) { *eax = function; *ecx = index; @@ -670,35 +672,35 @@ static inline void __cpuid(uint32_t function, uint32_t index, : "memory"); } -static inline void cpuid(uint32_t function, - uint32_t *eax, uint32_t *ebx, - uint32_t *ecx, uint32_t *edx) +static inline void cpuid(u32 function, + u32 *eax, u32 *ebx, + u32 *ecx, u32 *edx) { return __cpuid(function, 0, eax, ebx, ecx, edx); } -static inline uint32_t this_cpu_fms(void) +static inline u32 this_cpu_fms(void) { - uint32_t eax, ebx, ecx, edx; + u32 eax, ebx, ecx, edx; cpuid(1, &eax, &ebx, &ecx, &edx); return eax; } -static inline uint32_t this_cpu_family(void) +static inline u32 this_cpu_family(void) { return x86_family(this_cpu_fms()); } -static inline uint32_t this_cpu_model(void) +static inline u32 this_cpu_model(void) { return x86_model(this_cpu_fms()); } static inline bool this_cpu_vendor_string_is(const char *vendor) { - const uint32_t *chunk = (const uint32_t *)vendor; - uint32_t eax, ebx, ecx, edx; + const u32 *chunk = (const u32 *)vendor; + u32 eax, ebx, ecx, edx; cpuid(0, &eax, &ebx, &ecx, &edx); return (ebx == chunk[0] && edx == chunk[1] && ecx == chunk[2]); @@ -717,10 +719,14 @@ static inline bool this_cpu_is_amd(void) return this_cpu_vendor_string_is("AuthenticAMD"); } -static inline uint32_t __this_cpu_has(uint32_t function, uint32_t index, - uint8_t reg, uint8_t lo, uint8_t hi) +static inline bool this_cpu_is_hygon(void) { - uint32_t gprs[4]; + return this_cpu_vendor_string_is("HygonGenuine"); +} + +static inline u32 __this_cpu_has(u32 function, u32 index, u8 reg, u8 lo, u8 hi) +{ + u32 gprs[4]; __cpuid(function, index, &gprs[KVM_CPUID_EAX], &gprs[KVM_CPUID_EBX], @@ -735,7 +741,7 @@ static inline bool this_cpu_has(struct kvm_x86_cpu_feature feature) feature.reg, feature.bit, feature.bit); } -static inline uint32_t this_cpu_property(struct kvm_x86_cpu_property property) +static inline u32 this_cpu_property(struct kvm_x86_cpu_property property) { return __this_cpu_has(property.function, property.index, property.reg, property.lo_bit, property.hi_bit); @@ -743,7 +749,7 @@ static inline uint32_t this_cpu_property(struct kvm_x86_cpu_property property) static __always_inline bool this_cpu_has_p(struct kvm_x86_cpu_property property) { - uint32_t max_leaf; + u32 max_leaf; switch (property.function & 0xc0000000) { case 0: @@ -763,7 +769,7 @@ static __always_inline bool this_cpu_has_p(struct kvm_x86_cpu_property property) static inline bool this_pmu_has(struct kvm_x86_pmu_feature feature) { - uint32_t nr_bits; + u32 nr_bits; if (feature.f.reg == KVM_CPUID_EBX) { nr_bits = this_cpu_property(X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH); @@ -775,13 +781,13 @@ static inline bool this_pmu_has(struct kvm_x86_pmu_feature feature) return nr_bits > feature.f.bit || this_cpu_has(feature.f); } -static __always_inline uint64_t this_cpu_supported_xcr0(void) +static __always_inline u64 this_cpu_supported_xcr0(void) { if (!this_cpu_has_p(X86_PROPERTY_SUPPORTED_XCR0_LO)) return 0; return this_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_LO) | - ((uint64_t)this_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_HI) << 32); + ((u64)this_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_HI) << 32); } typedef u32 __attribute__((vector_size(16))) sse128_t; @@ -860,7 +866,7 @@ static inline void cpu_relax(void) static inline void udelay(unsigned long usec) { - uint64_t start, now, cycles; + u64 start, now, cycles; GUEST_ASSERT(guest_tsc_khz); cycles = guest_tsc_khz / 1000 * usec; @@ -891,8 +897,8 @@ void kvm_x86_state_cleanup(struct kvm_x86_state *state); const struct kvm_msr_list *kvm_get_msr_index_list(void); const struct kvm_msr_list *kvm_get_feature_msr_index_list(void); -bool kvm_msr_is_in_save_restore_list(uint32_t msr_index); -uint64_t kvm_get_feature_msr(uint64_t msr_index); +bool kvm_msr_is_in_save_restore_list(u32 msr_index); +u64 kvm_get_feature_msr(u64 msr_index); static inline void vcpu_msrs_get(struct kvm_vcpu *vcpu, struct kvm_msrs *msrs) @@ -947,20 +953,20 @@ static inline void vcpu_xcrs_set(struct kvm_vcpu *vcpu, struct kvm_xcrs *xcrs) } const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid, - uint32_t function, uint32_t index); + u32 function, u32 index); const struct kvm_cpuid2 *kvm_get_supported_cpuid(void); -static inline uint32_t kvm_cpu_fms(void) +static inline u32 kvm_cpu_fms(void) { return get_cpuid_entry(kvm_get_supported_cpuid(), 0x1, 0)->eax; } -static inline uint32_t kvm_cpu_family(void) +static inline u32 kvm_cpu_family(void) { return x86_family(kvm_cpu_fms()); } -static inline uint32_t kvm_cpu_model(void) +static inline u32 kvm_cpu_model(void) { return x86_model(kvm_cpu_fms()); } @@ -973,17 +979,17 @@ static inline bool kvm_cpu_has(struct kvm_x86_cpu_feature feature) return kvm_cpuid_has(kvm_get_supported_cpuid(), feature); } -uint32_t kvm_cpuid_property(const struct kvm_cpuid2 *cpuid, - struct kvm_x86_cpu_property property); +u32 kvm_cpuid_property(const struct kvm_cpuid2 *cpuid, + struct kvm_x86_cpu_property property); -static inline uint32_t kvm_cpu_property(struct kvm_x86_cpu_property property) +static inline u32 kvm_cpu_property(struct kvm_x86_cpu_property property) { return kvm_cpuid_property(kvm_get_supported_cpuid(), property); } static __always_inline bool kvm_cpu_has_p(struct kvm_x86_cpu_property property) { - uint32_t max_leaf; + u32 max_leaf; switch (property.function & 0xc0000000) { case 0: @@ -1003,7 +1009,7 @@ static __always_inline bool kvm_cpu_has_p(struct kvm_x86_cpu_property property) static inline bool kvm_pmu_has(struct kvm_x86_pmu_feature feature) { - uint32_t nr_bits; + u32 nr_bits; if (feature.f.reg == KVM_CPUID_EBX) { nr_bits = kvm_cpu_property(X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH); @@ -1015,13 +1021,13 @@ static inline bool kvm_pmu_has(struct kvm_x86_pmu_feature feature) return nr_bits > feature.f.bit || kvm_cpu_has(feature.f); } -static __always_inline uint64_t kvm_cpu_supported_xcr0(void) +static __always_inline u64 kvm_cpu_supported_xcr0(void) { if (!kvm_cpu_has_p(X86_PROPERTY_SUPPORTED_XCR0_LO)) return 0; return kvm_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_LO) | - ((uint64_t)kvm_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_HI) << 32); + ((u64)kvm_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_HI) << 32); } static inline size_t kvm_cpuid2_size(int nr_entries) @@ -1055,8 +1061,8 @@ static inline void vcpu_get_cpuid(struct kvm_vcpu *vcpu) } static inline struct kvm_cpuid_entry2 *__vcpu_get_cpuid_entry(struct kvm_vcpu *vcpu, - uint32_t function, - uint32_t index) + u32 function, + u32 index) { TEST_ASSERT(vcpu->cpuid, "Must do vcpu_init_cpuid() first (or equivalent)"); @@ -1067,7 +1073,7 @@ static inline struct kvm_cpuid_entry2 *__vcpu_get_cpuid_entry(struct kvm_vcpu *v } static inline struct kvm_cpuid_entry2 *vcpu_get_cpuid_entry(struct kvm_vcpu *vcpu, - uint32_t function) + u32 function) { return __vcpu_get_cpuid_entry(vcpu, function, 0); } @@ -1097,10 +1103,10 @@ static inline void vcpu_set_cpuid(struct kvm_vcpu *vcpu) void vcpu_set_cpuid_property(struct kvm_vcpu *vcpu, struct kvm_x86_cpu_property property, - uint32_t value); -void vcpu_set_cpuid_maxphyaddr(struct kvm_vcpu *vcpu, uint8_t maxphyaddr); + u32 value); +void vcpu_set_cpuid_maxphyaddr(struct kvm_vcpu *vcpu, u8 maxphyaddr); -void vcpu_clear_cpuid_entry(struct kvm_vcpu *vcpu, uint32_t function); +void vcpu_clear_cpuid_entry(struct kvm_vcpu *vcpu, u32 function); static inline bool vcpu_cpuid_has(struct kvm_vcpu *vcpu, struct kvm_x86_cpu_feature feature) @@ -1128,8 +1134,8 @@ static inline void vcpu_clear_cpuid_feature(struct kvm_vcpu *vcpu, vcpu_set_or_clear_cpuid_feature(vcpu, feature, false); } -uint64_t vcpu_get_msr(struct kvm_vcpu *vcpu, uint64_t msr_index); -int _vcpu_set_msr(struct kvm_vcpu *vcpu, uint64_t msr_index, uint64_t msr_value); +u64 vcpu_get_msr(struct kvm_vcpu *vcpu, u64 msr_index); +int _vcpu_set_msr(struct kvm_vcpu *vcpu, u64 msr_index, u64 msr_value); /* * Assert on an MSR access(es) and pretty print the MSR name when possible. @@ -1154,14 +1160,14 @@ do { \ * is changing, etc. This is NOT an exhaustive list! The intent is to filter * out MSRs that are not durable _and_ that a selftest wants to write. */ -static inline bool is_durable_msr(uint32_t msr) +static inline bool is_durable_msr(u32 msr) { return msr != MSR_IA32_TSC; } #define vcpu_set_msr(vcpu, msr, val) \ do { \ - uint64_t r, v = val; \ + u64 r, v = val; \ \ TEST_ASSERT_MSR(_vcpu_set_msr(vcpu, msr, v) == 1, \ "KVM_SET_MSRS failed on %s, value = 0x%lx", msr, #msr, v); \ @@ -1175,28 +1181,28 @@ void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits); void kvm_init_vm_address_properties(struct kvm_vm *vm); struct ex_regs { - uint64_t rax, rcx, rdx, rbx; - uint64_t rbp, rsi, rdi; - uint64_t r8, r9, r10, r11; - uint64_t r12, r13, r14, r15; - uint64_t vector; - uint64_t error_code; - uint64_t rip; - uint64_t cs; - uint64_t rflags; + u64 rax, rcx, rdx, rbx; + u64 rbp, rsi, rdi; + u64 r8, r9, r10, r11; + u64 r12, r13, r14, r15; + u64 vector; + u64 error_code; + u64 rip; + u64 cs; + u64 rflags; }; struct idt_entry { - uint16_t offset0; - uint16_t selector; - uint16_t ist : 3; - uint16_t : 5; - uint16_t type : 4; - uint16_t : 1; - uint16_t dpl : 2; - uint16_t p : 1; - uint16_t offset1; - uint32_t offset2; uint32_t reserved; + u16 offset0; + u16 selector; + u16 ist : 3; + u16 : 5; + u16 type : 4; + u16 : 1; + u16 dpl : 2; + u16 p : 1; + u16 offset1; + u32 offset2; u32 reserved; }; void vm_install_exception_handler(struct kvm_vm *vm, int vector, @@ -1255,8 +1261,8 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, #define kvm_asm_safe(insn, inputs...) \ ({ \ - uint64_t ign_error_code; \ - uint8_t vector; \ + u64 ign_error_code; \ + u8 vector; \ \ asm volatile(KVM_ASM_SAFE(insn) \ : KVM_ASM_SAFE_OUTPUTS(vector, ign_error_code) \ @@ -1267,7 +1273,7 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, #define kvm_asm_safe_ec(insn, error_code, inputs...) \ ({ \ - uint8_t vector; \ + u8 vector; \ \ asm volatile(KVM_ASM_SAFE(insn) \ : KVM_ASM_SAFE_OUTPUTS(vector, error_code) \ @@ -1278,8 +1284,8 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, #define kvm_asm_safe_fep(insn, inputs...) \ ({ \ - uint64_t ign_error_code; \ - uint8_t vector; \ + u64 ign_error_code; \ + u8 vector; \ \ asm volatile(KVM_ASM_SAFE_FEP(insn) \ : KVM_ASM_SAFE_OUTPUTS(vector, ign_error_code) \ @@ -1290,7 +1296,7 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, #define kvm_asm_safe_ec_fep(insn, error_code, inputs...) \ ({ \ - uint8_t vector; \ + u8 vector; \ \ asm volatile(KVM_ASM_SAFE_FEP(insn) \ : KVM_ASM_SAFE_OUTPUTS(vector, error_code) \ @@ -1300,11 +1306,11 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, }) #define BUILD_READ_U64_SAFE_HELPER(insn, _fep, _FEP) \ -static inline uint8_t insn##_safe ##_fep(uint32_t idx, uint64_t *val) \ +static inline u8 insn##_safe ##_fep(u32 idx, u64 *val) \ { \ - uint64_t error_code; \ - uint8_t vector; \ - uint32_t a, d; \ + u64 error_code; \ + u8 vector; \ + u32 a, d; \ \ asm volatile(KVM_ASM_SAFE##_FEP(#insn) \ : "=a"(a), "=d"(d), \ @@ -1312,7 +1318,7 @@ static inline uint8_t insn##_safe ##_fep(uint32_t idx, uint64_t *val) \ : "c"(idx) \ : KVM_ASM_SAFE_CLOBBERS); \ \ - *val = (uint64_t)a | ((uint64_t)d << 32); \ + *val = (u64)a | ((u64)d << 32); \ return vector; \ } @@ -1328,12 +1334,12 @@ BUILD_READ_U64_SAFE_HELPERS(rdmsr) BUILD_READ_U64_SAFE_HELPERS(rdpmc) BUILD_READ_U64_SAFE_HELPERS(xgetbv) -static inline uint8_t wrmsr_safe(uint32_t msr, uint64_t val) +static inline u8 wrmsr_safe(u32 msr, u64 val) { return kvm_asm_safe("wrmsr", "a"(val & -1u), "d"(val >> 32), "c"(msr)); } -static inline uint8_t xsetbv_safe(uint32_t index, uint64_t value) +static inline u8 xsetbv_safe(u32 index, u64 value) { u32 eax = value; u32 edx = value >> 32; @@ -1383,23 +1389,25 @@ static inline bool kvm_is_ignore_msrs(void) return get_kvm_param_bool("ignore_msrs"); } -uint64_t *vm_get_pte(struct kvm_vm *vm, uint64_t vaddr); +static inline bool kvm_is_lbrv_enabled(void) +{ + return !!get_kvm_amd_param_integer("lbrv"); +} -uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, - uint64_t a3); -uint64_t __xen_hypercall(uint64_t nr, uint64_t a0, void *a1); -void xen_hypercall(uint64_t nr, uint64_t a0, void *a1); +u64 *vm_get_pte(struct kvm_vm *vm, gva_t gva); -static inline uint64_t __kvm_hypercall_map_gpa_range(uint64_t gpa, - uint64_t size, uint64_t flags) +u64 kvm_hypercall(u64 nr, u64 a0, u64 a1, u64 a2, u64 a3); +u64 __xen_hypercall(u64 nr, u64 a0, void *a1); +void xen_hypercall(u64 nr, u64 a0, void *a1); + +static inline u64 __kvm_hypercall_map_gpa_range(gpa_t gpa, u64 size, u64 flags) { return kvm_hypercall(KVM_HC_MAP_GPA_RANGE, gpa, size >> PAGE_SHIFT, flags, 0); } -static inline void kvm_hypercall_map_gpa_range(uint64_t gpa, uint64_t size, - uint64_t flags) +static inline void kvm_hypercall_map_gpa_range(gpa_t gpa, u64 size, u64 flags) { - uint64_t ret = __kvm_hypercall_map_gpa_range(gpa, size, flags); + u64 ret = __kvm_hypercall_map_gpa_range(gpa, size, flags); GUEST_ASSERT(!ret); } @@ -1444,7 +1452,7 @@ static inline void cli(void) asm volatile ("cli"); } -void __vm_xsave_require_permission(uint64_t xfeature, const char *name); +void __vm_xsave_require_permission(u64 xfeature, const char *name); #define vm_xsave_require_permission(xfeature) \ __vm_xsave_require_permission(xfeature, #xfeature) @@ -1499,17 +1507,17 @@ enum pg_level { void tdp_mmu_init(struct kvm_vm *vm, int pgtable_levels, struct pte_masks *pte_masks); -void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu *mmu, uint64_t vaddr, - uint64_t paddr, int level); -void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, - uint64_t nr_bytes, int level); +void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu *mmu, gva_t gva, + gpa_t gpa, int level); +void virt_map_level(struct kvm_vm *vm, gva_t gva, gpa_t gpa, + u64 nr_bytes, int level); void vm_enable_tdp(struct kvm_vm *vm); bool kvm_cpu_has_tdp(void); -void tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, uint64_t size); +void tdp_map(struct kvm_vm *vm, gpa_t l2_gpa, gpa_t gpa, u64 size); void tdp_identity_map_default_memslots(struct kvm_vm *vm); -void tdp_identity_map_1g(struct kvm_vm *vm, uint64_t addr, uint64_t size); -uint64_t *tdp_get_pte(struct kvm_vm *vm, uint64_t l2_gpa); +void tdp_identity_map_1g(struct kvm_vm *vm, u64 addr, u64 size); +u64 *tdp_get_pte(struct kvm_vm *vm, u64 l2_gpa); /* * Basic CPU control in CR0 diff --git a/tools/testing/selftests/kvm/include/x86/sev.h b/tools/testing/selftests/kvm/include/x86/sev.h index 008b4169f5e2..1af44c151d60 100644 --- a/tools/testing/selftests/kvm/include/x86/sev.h +++ b/tools/testing/selftests/kvm/include/x86/sev.h @@ -46,16 +46,16 @@ static inline bool is_sev_vm(struct kvm_vm *vm) return is_sev_es_vm(vm) || vm->type == KVM_X86_SEV_VM; } -void sev_vm_launch(struct kvm_vm *vm, uint32_t policy); -void sev_vm_launch_measure(struct kvm_vm *vm, uint8_t *measurement); +void sev_vm_launch(struct kvm_vm *vm, u32 policy); +void sev_vm_launch_measure(struct kvm_vm *vm, u8 *measurement); void sev_vm_launch_finish(struct kvm_vm *vm); -void snp_vm_launch_start(struct kvm_vm *vm, uint64_t policy); +void snp_vm_launch_start(struct kvm_vm *vm, u64 policy); void snp_vm_launch_update(struct kvm_vm *vm); void snp_vm_launch_finish(struct kvm_vm *vm); -struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t type, void *guest_code, +struct kvm_vm *vm_sev_create_with_one_vcpu(u32 type, void *guest_code, struct kvm_vcpu **cpu); -void vm_sev_launch(struct kvm_vm *vm, uint64_t policy, uint8_t *measurement); +void vm_sev_launch(struct kvm_vm *vm, u64 policy, u8 *measurement); kvm_static_assert(SEV_RET_SUCCESS == 0); @@ -85,7 +85,7 @@ static inline u64 snp_default_policy(void) unsigned long raw; \ } sev_cmd = { .c = { \ .id = (cmd), \ - .data = (uint64_t)(arg), \ + .data = (u64)(arg), \ .sev_fd = (vm)->arch.sev_fd, \ } }; \ \ @@ -120,8 +120,8 @@ static inline void sev_register_encrypted_memory(struct kvm_vm *vm, vm_ioctl(vm, KVM_MEMORY_ENCRYPT_REG_REGION, &range); } -static inline void sev_launch_update_data(struct kvm_vm *vm, vm_paddr_t gpa, - uint64_t size) +static inline void sev_launch_update_data(struct kvm_vm *vm, gpa_t gpa, + u64 size) { struct kvm_sev_launch_update_data update_data = { .uaddr = (unsigned long)addr_gpa2hva(vm, gpa), @@ -131,8 +131,8 @@ static inline void sev_launch_update_data(struct kvm_vm *vm, vm_paddr_t gpa, vm_sev_ioctl(vm, KVM_SEV_LAUNCH_UPDATE_DATA, &update_data); } -static inline void snp_launch_update_data(struct kvm_vm *vm, vm_paddr_t gpa, - uint64_t hva, uint64_t size, uint8_t type) +static inline void snp_launch_update_data(struct kvm_vm *vm, gpa_t gpa, + u64 hva, u64 size, u8 type) { struct kvm_sev_snp_launch_update update_data = { .uaddr = hva, diff --git a/tools/testing/selftests/kvm/include/x86/smm.h b/tools/testing/selftests/kvm/include/x86/smm.h index 19337c34f13e..2d1afa09819b 100644 --- a/tools/testing/selftests/kvm/include/x86/smm.h +++ b/tools/testing/selftests/kvm/include/x86/smm.h @@ -8,8 +8,7 @@ #define SMRAM_MEMSLOT ((1 << 16) | 1) #define SMRAM_PAGES (SMRAM_SIZE / PAGE_SIZE) -void setup_smram(struct kvm_vm *vm, struct kvm_vcpu *vcpu, - uint64_t smram_gpa, +void setup_smram(struct kvm_vm *vm, struct kvm_vcpu *vcpu, u64 smram_gpa, const void *smi_handler, size_t handler_size); void inject_smi(struct kvm_vcpu *vcpu); diff --git a/tools/testing/selftests/kvm/include/x86/svm.h b/tools/testing/selftests/kvm/include/x86/svm.h index 10b30b38bb3f..c8539166270e 100644 --- a/tools/testing/selftests/kvm/include/x86/svm.h +++ b/tools/testing/selftests/kvm/include/x86/svm.h @@ -97,13 +97,13 @@ struct __attribute__ ((__packed__)) vmcb_control_area { u64 exit_info_2; u32 exit_int_info; u32 exit_int_info_err; - u64 nested_ctl; + u64 misc_ctl; u64 avic_vapic_bar; u8 reserved_4[8]; u32 event_inj; u32 event_inj_err; u64 nested_cr3; - u64 virt_ext; + u64 misc_ctl2; u32 clean; u32 reserved_5; u64 next_rip; @@ -155,9 +155,6 @@ struct __attribute__ ((__packed__)) vmcb_control_area { #define AVIC_ENABLE_SHIFT 31 #define AVIC_ENABLE_MASK (1 << AVIC_ENABLE_SHIFT) -#define LBR_CTL_ENABLE_MASK BIT_ULL(0) -#define VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK BIT_ULL(1) - #define SVM_INTERRUPT_SHADOW_MASK 1 #define SVM_IOIO_STR_SHIFT 2 @@ -175,8 +172,11 @@ struct __attribute__ ((__packed__)) vmcb_control_area { #define SVM_VM_CR_SVM_LOCK_MASK 0x0008ULL #define SVM_VM_CR_SVM_DIS_MASK 0x0010ULL -#define SVM_NESTED_CTL_NP_ENABLE BIT(0) -#define SVM_NESTED_CTL_SEV_ENABLE BIT(1) +#define SVM_MISC_ENABLE_NP BIT(0) +#define SVM_MISC_ENABLE_SEV BIT(1) + +#define SVM_MISC2_ENABLE_V_LBR BIT_ULL(0) +#define SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE BIT_ULL(1) struct __attribute__ ((__packed__)) vmcb_seg { u16 selector; diff --git a/tools/testing/selftests/kvm/include/x86/svm_util.h b/tools/testing/selftests/kvm/include/x86/svm_util.h index 5d7c42534bc4..6c013eb838be 100644 --- a/tools/testing/selftests/kvm/include/x86/svm_util.h +++ b/tools/testing/selftests/kvm/include/x86/svm_util.h @@ -16,20 +16,20 @@ struct svm_test_data { /* VMCB */ struct vmcb *vmcb; /* gva */ void *vmcb_hva; - uint64_t vmcb_gpa; + u64 vmcb_gpa; /* host state-save area */ struct vmcb_save_area *save_area; /* gva */ void *save_area_hva; - uint64_t save_area_gpa; + u64 save_area_gpa; /* MSR-Bitmap */ void *msr; /* gva */ void *msr_hva; - uint64_t msr_gpa; + u64 msr_gpa; /* NPT */ - uint64_t ncr3_gpa; + u64 ncr3_gpa; }; static inline void vmmcall(void) @@ -56,9 +56,9 @@ static inline void vmmcall(void) "clgi\n" \ ) -struct svm_test_data *vcpu_alloc_svm(struct kvm_vm *vm, vm_vaddr_t *p_svm_gva); +struct svm_test_data *vcpu_alloc_svm(struct kvm_vm *vm, gva_t *p_svm_gva); void generic_svm_setup(struct svm_test_data *svm, void *guest_rip, void *guest_rsp); -void run_guest(struct vmcb *vmcb, uint64_t vmcb_gpa); +void run_guest(struct vmcb *vmcb, u64 vmcb_gpa); static inline bool kvm_cpu_has_npt(void) { diff --git a/tools/testing/selftests/kvm/include/x86/ucall.h b/tools/testing/selftests/kvm/include/x86/ucall.h index d3825dcc3cd9..0e4950041e3e 100644 --- a/tools/testing/selftests/kvm/include/x86/ucall.h +++ b/tools/testing/selftests/kvm/include/x86/ucall.h @@ -6,7 +6,7 @@ #define UCALL_EXIT_REASON KVM_EXIT_IO -static inline void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) +static inline void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa) { } diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/selftests/kvm/include/x86/vmx.h index 92b918700d24..90fffaf91595 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -285,16 +285,16 @@ enum vmcs_field { }; struct vmx_msr_entry { - uint32_t index; - uint32_t reserved; - uint64_t value; + u32 index; + u32 reserved; + u64 value; } __attribute__ ((aligned(16))); #include "evmcs.h" -static inline int vmxon(uint64_t phys) +static inline int vmxon(u64 phys) { - uint8_t ret; + u8 ret; __asm__ __volatile__ ("vmxon %[pa]; setna %[ret]" : [ret]"=rm"(ret) @@ -309,9 +309,9 @@ static inline void vmxoff(void) __asm__ __volatile__("vmxoff"); } -static inline int vmclear(uint64_t vmcs_pa) +static inline int vmclear(u64 vmcs_pa) { - uint8_t ret; + u8 ret; __asm__ __volatile__ ("vmclear %[pa]; setna %[ret]" : [ret]"=rm"(ret) @@ -321,9 +321,9 @@ static inline int vmclear(uint64_t vmcs_pa) return ret; } -static inline int vmptrld(uint64_t vmcs_pa) +static inline int vmptrld(u64 vmcs_pa) { - uint8_t ret; + u8 ret; if (enable_evmcs) return -1; @@ -336,10 +336,10 @@ static inline int vmptrld(uint64_t vmcs_pa) return ret; } -static inline int vmptrst(uint64_t *value) +static inline int vmptrst(u64 *value) { - uint64_t tmp; - uint8_t ret; + u64 tmp; + u8 ret; if (enable_evmcs) return evmcs_vmptrst(value); @@ -356,9 +356,9 @@ static inline int vmptrst(uint64_t *value) * A wrapper around vmptrst that ignores errors and returns zero if the * vmptrst instruction fails. */ -static inline uint64_t vmptrstz(void) +static inline u64 vmptrstz(void) { - uint64_t value = 0; + u64 value = 0; vmptrst(&value); return value; } @@ -391,8 +391,8 @@ static inline int vmlaunch(void) "pop %%rcx;" "pop %%rbp;" : [ret]"=&a"(ret) - : [host_rsp]"r"((uint64_t)HOST_RSP), - [host_rip]"r"((uint64_t)HOST_RIP) + : [host_rsp]"r"((u64)HOST_RSP), + [host_rip]"r"((u64)HOST_RIP) : "memory", "cc", "rbx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"); return ret; @@ -426,8 +426,8 @@ static inline int vmresume(void) "pop %%rcx;" "pop %%rbp;" : [ret]"=&a"(ret) - : [host_rsp]"r"((uint64_t)HOST_RSP), - [host_rip]"r"((uint64_t)HOST_RIP) + : [host_rsp]"r"((u64)HOST_RSP), + [host_rip]"r"((u64)HOST_RIP) : "memory", "cc", "rbx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"); return ret; @@ -447,10 +447,10 @@ static inline void vmcall(void) "r10", "r11", "r12", "r13", "r14", "r15"); } -static inline int vmread(uint64_t encoding, uint64_t *value) +static inline int vmread(u64 encoding, u64 *value) { - uint64_t tmp; - uint8_t ret; + u64 tmp; + u8 ret; if (enable_evmcs) return evmcs_vmread(encoding, value); @@ -468,16 +468,16 @@ static inline int vmread(uint64_t encoding, uint64_t *value) * A wrapper around vmread that ignores errors and returns zero if the * vmread instruction fails. */ -static inline uint64_t vmreadz(uint64_t encoding) +static inline u64 vmreadz(u64 encoding) { - uint64_t value = 0; + u64 value = 0; vmread(encoding, &value); return value; } -static inline int vmwrite(uint64_t encoding, uint64_t value) +static inline int vmwrite(u64 encoding, u64 value) { - uint8_t ret; + u8 ret; if (enable_evmcs) return evmcs_vmwrite(encoding, value); @@ -490,41 +490,41 @@ static inline int vmwrite(uint64_t encoding, uint64_t value) return ret; } -static inline uint32_t vmcs_revision(void) +static inline u32 vmcs_revision(void) { return rdmsr(MSR_IA32_VMX_BASIC); } struct vmx_pages { void *vmxon_hva; - uint64_t vmxon_gpa; + u64 vmxon_gpa; void *vmxon; void *vmcs_hva; - uint64_t vmcs_gpa; + u64 vmcs_gpa; void *vmcs; void *msr_hva; - uint64_t msr_gpa; + u64 msr_gpa; void *msr; void *shadow_vmcs_hva; - uint64_t shadow_vmcs_gpa; + u64 shadow_vmcs_gpa; void *shadow_vmcs; void *vmread_hva; - uint64_t vmread_gpa; + u64 vmread_gpa; void *vmread; void *vmwrite_hva; - uint64_t vmwrite_gpa; + u64 vmwrite_gpa; void *vmwrite; void *apic_access_hva; - uint64_t apic_access_gpa; + u64 apic_access_gpa; void *apic_access; - uint64_t eptp_gpa; + u64 eptp_gpa; }; union vmx_basic { @@ -550,7 +550,7 @@ union vmx_ctrl_msr { }; }; -struct vmx_pages *vcpu_alloc_vmx(struct kvm_vm *vm, vm_vaddr_t *p_vmx_gva); +struct vmx_pages *vcpu_alloc_vmx(struct kvm_vm *vm, gva_t *p_vmx_gva); bool prepare_for_vmx_operation(struct vmx_pages *vmx); void prepare_vmcs(struct vmx_pages *vmx, void *guest_rip, void *guest_rsp); bool load_vmcs(struct vmx_pages *vmx); diff --git a/tools/testing/selftests/kvm/kvm_page_table_test.c b/tools/testing/selftests/kvm/kvm_page_table_test.c index dd8b12f626d3..fc5242fb956f 100644 --- a/tools/testing/selftests/kvm/kvm_page_table_test.c +++ b/tools/testing/selftests/kvm/kvm_page_table_test.c @@ -46,12 +46,12 @@ static const char * const test_stage_string[] = { struct test_args { struct kvm_vm *vm; - uint64_t guest_test_virt_mem; - uint64_t host_page_size; - uint64_t host_num_pages; - uint64_t large_page_size; - uint64_t large_num_pages; - uint64_t host_pages_per_lpage; + u64 guest_test_virt_mem; + u64 host_page_size; + u64 host_num_pages; + u64 large_page_size; + u64 large_num_pages; + u64 host_pages_per_lpage; enum vm_mem_backing_src_type src_type; struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; }; @@ -63,7 +63,7 @@ struct test_args { static enum test_stage guest_test_stage; /* Host variables */ -static uint32_t nr_vcpus = 1; +static u32 nr_vcpus = 1; static struct test_args test_args; static enum test_stage *current_stage; static bool host_quit; @@ -77,19 +77,19 @@ static sem_t test_stage_completed; * This will be set to the topmost valid physical address minus * the test memory size. */ -static uint64_t guest_test_phys_mem; +static u64 guest_test_phys_mem; /* * Guest virtual memory offset of the testing memory slot. * Must not conflict with identity mapped test code. */ -static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; +static u64 guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; static void guest_code(bool do_write) { struct test_args *p = &test_args; enum test_stage *current_stage = &guest_test_stage; - uint64_t addr; + u64 addr; int i, j; while (true) { @@ -113,9 +113,9 @@ static void guest_code(bool do_write) case KVM_CREATE_MAPPINGS: for (i = 0; i < p->large_num_pages; i++) { if (do_write) - *(uint64_t *)addr = 0x0123456789ABCDEF; + *(u64 *)addr = 0x0123456789ABCDEF; else - READ_ONCE(*(uint64_t *)addr); + READ_ONCE(*(u64 *)addr); addr += p->large_page_size; } @@ -131,7 +131,7 @@ static void guest_code(bool do_write) case KVM_UPDATE_MAPPINGS: if (p->src_type == VM_MEM_SRC_ANONYMOUS) { for (i = 0; i < p->host_num_pages; i++) { - *(uint64_t *)addr = 0x0123456789ABCDEF; + *(u64 *)addr = 0x0123456789ABCDEF; addr += p->host_page_size; } break; @@ -142,7 +142,7 @@ static void guest_code(bool do_write) * Write to the first host page in each large * page region, and triger break of large pages. */ - *(uint64_t *)addr = 0x0123456789ABCDEF; + *(u64 *)addr = 0x0123456789ABCDEF; /* * Access the middle host pages in each large @@ -152,7 +152,7 @@ static void guest_code(bool do_write) */ addr += p->large_page_size / 2; for (j = 0; j < p->host_pages_per_lpage / 2; j++) { - READ_ONCE(*(uint64_t *)addr); + READ_ONCE(*(u64 *)addr); addr += p->host_page_size; } } @@ -167,7 +167,7 @@ static void guest_code(bool do_write) */ case KVM_ADJUST_MAPPINGS: for (i = 0; i < p->host_num_pages; i++) { - READ_ONCE(*(uint64_t *)addr); + READ_ONCE(*(u64 *)addr); addr += p->host_page_size; } break; @@ -227,8 +227,8 @@ static void *vcpu_worker(void *data) } struct test_params { - uint64_t phys_offset; - uint64_t test_mem_size; + u64 phys_offset; + u64 test_mem_size; enum vm_mem_backing_src_type src_type; }; @@ -237,12 +237,12 @@ static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg) int ret; struct test_params *p = arg; enum vm_mem_backing_src_type src_type = p->src_type; - uint64_t large_page_size = get_backing_src_pagesz(src_type); - uint64_t guest_page_size = vm_guest_mode_params[mode].page_size; - uint64_t host_page_size = getpagesize(); - uint64_t test_mem_size = p->test_mem_size; - uint64_t guest_num_pages; - uint64_t alignment; + u64 large_page_size = get_backing_src_pagesz(src_type); + u64 guest_page_size = vm_guest_mode_params[mode].page_size; + u64 host_page_size = getpagesize(); + u64 test_mem_size = p->test_mem_size; + u64 guest_num_pages; + u64 alignment; void *host_test_mem; struct kvm_vm *vm; @@ -261,9 +261,6 @@ static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg) guest_page_size; else guest_test_phys_mem = p->phys_offset; -#ifdef __s390x__ - alignment = max(0x100000UL, alignment); -#endif guest_test_phys_mem = align_down(guest_test_phys_mem, alignment); /* Set up the shared data structure test_args */ @@ -284,7 +281,7 @@ static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg) virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, guest_num_pages); /* Cache the HVA pointer of the region */ - host_test_mem = addr_gpa2hva(vm, (vm_paddr_t)guest_test_phys_mem); + host_test_mem = addr_gpa2hva(vm, (gpa_t)guest_test_phys_mem); /* Export shared structure test_args to guest */ sync_global_to_guest(vm, test_args); @@ -295,7 +292,7 @@ static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg) ret = sem_init(&test_stage_completed, 0, 0); TEST_ASSERT(ret == 0, "Error in sem_init"); - current_stage = addr_gva2hva(vm, (vm_vaddr_t)(&guest_test_stage)); + current_stage = addr_gva2hva(vm, (gva_t)(&guest_test_stage)); *current_stage = NUM_TEST_STAGES; pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode)); @@ -307,7 +304,7 @@ static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg) pr_info("Guest physical test memory offset: 0x%lx\n", guest_test_phys_mem); pr_info("Host virtual test memory offset: 0x%lx\n", - (uint64_t)host_test_mem); + (u64)host_test_mem); pr_info("Number of testing vCPUs: %d\n", nr_vcpus); return vm; diff --git a/tools/testing/selftests/kvm/lib/arm64/gic.c b/tools/testing/selftests/kvm/lib/arm64/gic.c index b023868fe0b8..011dfe1dfcb3 100644 --- a/tools/testing/selftests/kvm/lib/arm64/gic.c +++ b/tools/testing/selftests/kvm/lib/arm64/gic.c @@ -50,7 +50,7 @@ static void gic_dist_init(enum gic_type type, unsigned int nr_cpus) void gic_init(enum gic_type type, unsigned int nr_cpus) { - uint32_t cpu = guest_get_vcpuid(); + u32 cpu = guest_get_vcpuid(); GUEST_ASSERT(type < GIC_TYPE_MAX); GUEST_ASSERT(nr_cpus); @@ -73,7 +73,7 @@ void gic_irq_disable(unsigned int intid) unsigned int gic_get_and_ack_irq(void) { - uint64_t irqstat; + u64 irqstat; unsigned int intid; GUEST_ASSERT(gic_common_ops); @@ -102,7 +102,7 @@ void gic_set_eoi_split(bool split) gic_common_ops->gic_set_eoi_split(split); } -void gic_set_priority_mask(uint64_t pmr) +void gic_set_priority_mask(u64 pmr) { GUEST_ASSERT(gic_common_ops); gic_common_ops->gic_set_priority_mask(pmr); diff --git a/tools/testing/selftests/kvm/lib/arm64/gic_private.h b/tools/testing/selftests/kvm/lib/arm64/gic_private.h index b6a7e30c3eb1..6d393f5c5685 100644 --- a/tools/testing/selftests/kvm/lib/arm64/gic_private.h +++ b/tools/testing/selftests/kvm/lib/arm64/gic_private.h @@ -12,20 +12,20 @@ struct gic_common_ops { void (*gic_cpu_init)(unsigned int cpu); void (*gic_irq_enable)(unsigned int intid); void (*gic_irq_disable)(unsigned int intid); - uint64_t (*gic_read_iar)(void); - void (*gic_write_eoir)(uint32_t irq); - void (*gic_write_dir)(uint32_t irq); + u64 (*gic_read_iar)(void); + void (*gic_write_eoir)(u32 irq); + void (*gic_write_dir)(u32 irq); void (*gic_set_eoi_split)(bool split); - void (*gic_set_priority_mask)(uint64_t mask); - void (*gic_set_priority)(uint32_t intid, uint32_t prio); - void (*gic_irq_set_active)(uint32_t intid); - void (*gic_irq_clear_active)(uint32_t intid); - bool (*gic_irq_get_active)(uint32_t intid); - void (*gic_irq_set_pending)(uint32_t intid); - void (*gic_irq_clear_pending)(uint32_t intid); - bool (*gic_irq_get_pending)(uint32_t intid); - void (*gic_irq_set_config)(uint32_t intid, bool is_edge); - void (*gic_irq_set_group)(uint32_t intid, bool group); + void (*gic_set_priority_mask)(u64 mask); + void (*gic_set_priority)(u32 intid, u32 prio); + void (*gic_irq_set_active)(u32 intid); + void (*gic_irq_clear_active)(u32 intid); + bool (*gic_irq_get_active)(u32 intid); + void (*gic_irq_set_pending)(u32 intid); + void (*gic_irq_clear_pending)(u32 intid); + bool (*gic_irq_get_pending)(u32 intid); + void (*gic_irq_set_config)(u32 intid, bool is_edge); + void (*gic_irq_set_group)(u32 intid, bool group); }; extern const struct gic_common_ops gicv3_ops; diff --git a/tools/testing/selftests/kvm/lib/arm64/gic_v3.c b/tools/testing/selftests/kvm/lib/arm64/gic_v3.c index 50754a27f493..a99a53accfe9 100644 --- a/tools/testing/selftests/kvm/lib/arm64/gic_v3.c +++ b/tools/testing/selftests/kvm/lib/arm64/gic_v3.c @@ -50,13 +50,13 @@ static void gicv3_gicd_wait_for_rwp(void) } } -static inline volatile void *gicr_base_cpu(uint32_t cpu) +static inline volatile void *gicr_base_cpu(u32 cpu) { /* Align all the redistributors sequentially */ return GICR_BASE_GVA + cpu * SZ_64K * 2; } -static void gicv3_gicr_wait_for_rwp(uint32_t cpu) +static void gicv3_gicr_wait_for_rwp(u32 cpu) { unsigned int count = 100000; /* 1s */ @@ -66,7 +66,7 @@ static void gicv3_gicr_wait_for_rwp(uint32_t cpu) } } -static void gicv3_wait_for_rwp(uint32_t cpu_or_dist) +static void gicv3_wait_for_rwp(u32 cpu_or_dist) { if (cpu_or_dist & DIST_BIT) gicv3_gicd_wait_for_rwp(); @@ -91,34 +91,34 @@ static enum gicv3_intid_range get_intid_range(unsigned int intid) return INVALID_RANGE; } -static uint64_t gicv3_read_iar(void) +static u64 gicv3_read_iar(void) { - uint64_t irqstat = read_sysreg_s(SYS_ICC_IAR1_EL1); + u64 irqstat = read_sysreg_s(SYS_ICC_IAR1_EL1); dsb(sy); return irqstat; } -static void gicv3_write_eoir(uint32_t irq) +static void gicv3_write_eoir(u32 irq) { write_sysreg_s(irq, SYS_ICC_EOIR1_EL1); isb(); } -static void gicv3_write_dir(uint32_t irq) +static void gicv3_write_dir(u32 irq) { write_sysreg_s(irq, SYS_ICC_DIR_EL1); isb(); } -static void gicv3_set_priority_mask(uint64_t mask) +static void gicv3_set_priority_mask(u64 mask) { write_sysreg_s(mask, SYS_ICC_PMR_EL1); } static void gicv3_set_eoi_split(bool split) { - uint32_t val; + u32 val; /* * All other fields are read-only, so no need to read CTLR first. In @@ -129,29 +129,29 @@ static void gicv3_set_eoi_split(bool split) isb(); } -uint32_t gicv3_reg_readl(uint32_t cpu_or_dist, uint64_t offset) +u32 gicv3_reg_readl(u32 cpu_or_dist, u64 offset) { volatile void *base = cpu_or_dist & DIST_BIT ? GICD_BASE_GVA : sgi_base_from_redist(gicr_base_cpu(cpu_or_dist)); return readl(base + offset); } -void gicv3_reg_writel(uint32_t cpu_or_dist, uint64_t offset, uint32_t reg_val) +void gicv3_reg_writel(u32 cpu_or_dist, u64 offset, u32 reg_val) { volatile void *base = cpu_or_dist & DIST_BIT ? GICD_BASE_GVA : sgi_base_from_redist(gicr_base_cpu(cpu_or_dist)); writel(reg_val, base + offset); } -uint32_t gicv3_getl_fields(uint32_t cpu_or_dist, uint64_t offset, uint32_t mask) +u32 gicv3_getl_fields(u32 cpu_or_dist, u64 offset, u32 mask) { return gicv3_reg_readl(cpu_or_dist, offset) & mask; } -void gicv3_setl_fields(uint32_t cpu_or_dist, uint64_t offset, - uint32_t mask, uint32_t reg_val) +void gicv3_setl_fields(u32 cpu_or_dist, u64 offset, + u32 mask, u32 reg_val) { - uint32_t tmp = gicv3_reg_readl(cpu_or_dist, offset) & ~mask; + u32 tmp = gicv3_reg_readl(cpu_or_dist, offset) & ~mask; tmp |= (reg_val & mask); gicv3_reg_writel(cpu_or_dist, offset, tmp); @@ -165,14 +165,14 @@ void gicv3_setl_fields(uint32_t cpu_or_dist, uint64_t offset, * map that doesn't implement it; like GICR_WAKER's offset of 0x0014 being * marked as "Reserved" in the Distributor map. */ -static void gicv3_access_reg(uint32_t intid, uint64_t offset, - uint32_t reg_bits, uint32_t bits_per_field, - bool write, uint32_t *val) +static void gicv3_access_reg(u32 intid, u64 offset, + u32 reg_bits, u32 bits_per_field, + bool write, u32 *val) { - uint32_t cpu = guest_get_vcpuid(); + u32 cpu = guest_get_vcpuid(); enum gicv3_intid_range intid_range = get_intid_range(intid); - uint32_t fields_per_reg, index, mask, shift; - uint32_t cpu_or_dist; + u32 fields_per_reg, index, mask, shift; + u32 cpu_or_dist; GUEST_ASSERT(bits_per_field <= reg_bits); GUEST_ASSERT(!write || *val < (1U << bits_per_field)); @@ -197,32 +197,32 @@ static void gicv3_access_reg(uint32_t intid, uint64_t offset, *val = gicv3_getl_fields(cpu_or_dist, offset, mask) >> shift; } -static void gicv3_write_reg(uint32_t intid, uint64_t offset, - uint32_t reg_bits, uint32_t bits_per_field, uint32_t val) +static void gicv3_write_reg(u32 intid, u64 offset, + u32 reg_bits, u32 bits_per_field, u32 val) { gicv3_access_reg(intid, offset, reg_bits, bits_per_field, true, &val); } -static uint32_t gicv3_read_reg(uint32_t intid, uint64_t offset, - uint32_t reg_bits, uint32_t bits_per_field) +static u32 gicv3_read_reg(u32 intid, u64 offset, + u32 reg_bits, u32 bits_per_field) { - uint32_t val; + u32 val; gicv3_access_reg(intid, offset, reg_bits, bits_per_field, false, &val); return val; } -static void gicv3_set_priority(uint32_t intid, uint32_t prio) +static void gicv3_set_priority(u32 intid, u32 prio) { gicv3_write_reg(intid, GICD_IPRIORITYR, 32, 8, prio); } /* Sets the intid to be level-sensitive or edge-triggered. */ -static void gicv3_irq_set_config(uint32_t intid, bool is_edge) +static void gicv3_irq_set_config(u32 intid, bool is_edge) { - uint32_t val; + u32 val; /* N/A for private interrupts. */ GUEST_ASSERT(get_intid_range(intid) == SPI_RANGE); @@ -230,57 +230,57 @@ static void gicv3_irq_set_config(uint32_t intid, bool is_edge) gicv3_write_reg(intid, GICD_ICFGR, 32, 2, val); } -static void gicv3_irq_enable(uint32_t intid) +static void gicv3_irq_enable(u32 intid) { bool is_spi = get_intid_range(intid) == SPI_RANGE; - uint32_t cpu = guest_get_vcpuid(); + u32 cpu = guest_get_vcpuid(); gicv3_write_reg(intid, GICD_ISENABLER, 32, 1, 1); gicv3_wait_for_rwp(is_spi ? DIST_BIT : cpu); } -static void gicv3_irq_disable(uint32_t intid) +static void gicv3_irq_disable(u32 intid) { bool is_spi = get_intid_range(intid) == SPI_RANGE; - uint32_t cpu = guest_get_vcpuid(); + u32 cpu = guest_get_vcpuid(); gicv3_write_reg(intid, GICD_ICENABLER, 32, 1, 1); gicv3_wait_for_rwp(is_spi ? DIST_BIT : cpu); } -static void gicv3_irq_set_active(uint32_t intid) +static void gicv3_irq_set_active(u32 intid) { gicv3_write_reg(intid, GICD_ISACTIVER, 32, 1, 1); } -static void gicv3_irq_clear_active(uint32_t intid) +static void gicv3_irq_clear_active(u32 intid) { gicv3_write_reg(intid, GICD_ICACTIVER, 32, 1, 1); } -static bool gicv3_irq_get_active(uint32_t intid) +static bool gicv3_irq_get_active(u32 intid) { return gicv3_read_reg(intid, GICD_ISACTIVER, 32, 1); } -static void gicv3_irq_set_pending(uint32_t intid) +static void gicv3_irq_set_pending(u32 intid) { gicv3_write_reg(intid, GICD_ISPENDR, 32, 1, 1); } -static void gicv3_irq_clear_pending(uint32_t intid) +static void gicv3_irq_clear_pending(u32 intid) { gicv3_write_reg(intid, GICD_ICPENDR, 32, 1, 1); } -static bool gicv3_irq_get_pending(uint32_t intid) +static bool gicv3_irq_get_pending(u32 intid) { return gicv3_read_reg(intid, GICD_ISPENDR, 32, 1); } static void gicv3_enable_redist(volatile void *redist_base) { - uint32_t val = readl(redist_base + GICR_WAKER); + u32 val = readl(redist_base + GICR_WAKER); unsigned int count = 100000; /* 1s */ val &= ~GICR_WAKER_ProcessorSleep; @@ -293,10 +293,10 @@ static void gicv3_enable_redist(volatile void *redist_base) } } -static void gicv3_set_group(uint32_t intid, bool grp) +static void gicv3_set_group(u32 intid, bool grp) { - uint32_t cpu_or_dist; - uint32_t val; + u32 cpu_or_dist; + u32 val; cpu_or_dist = (get_intid_range(intid) == SPI_RANGE) ? DIST_BIT : guest_get_vcpuid(); val = gicv3_reg_readl(cpu_or_dist, GICD_IGROUPR + (intid / 32) * 4); @@ -424,8 +424,8 @@ const struct gic_common_ops gicv3_ops = { .gic_irq_set_group = gicv3_set_group, }; -void gic_rdist_enable_lpis(vm_paddr_t cfg_table, size_t cfg_table_size, - vm_paddr_t pend_table) +void gic_rdist_enable_lpis(gpa_t cfg_table, size_t cfg_table_size, + gpa_t pend_table) { volatile void *rdist_base = gicr_base_cpu(guest_get_vcpuid()); diff --git a/tools/testing/selftests/kvm/lib/arm64/gic_v3_its.c b/tools/testing/selftests/kvm/lib/arm64/gic_v3_its.c index 7f9fdcf42ae6..1188b578121d 100644 --- a/tools/testing/selftests/kvm/lib/arm64/gic_v3_its.c +++ b/tools/testing/selftests/kvm/lib/arm64/gic_v3_its.c @@ -54,7 +54,7 @@ static unsigned long its_find_baser(unsigned int type) return -1; } -static void its_install_table(unsigned int type, vm_paddr_t base, size_t size) +static void its_install_table(unsigned int type, gpa_t base, size_t size) { unsigned long offset = its_find_baser(type); u64 baser; @@ -69,7 +69,7 @@ static void its_install_table(unsigned int type, vm_paddr_t base, size_t size) its_write_u64(offset, baser); } -static void its_install_cmdq(vm_paddr_t base, size_t size) +static void its_install_cmdq(gpa_t base, size_t size) { u64 cbaser; @@ -82,9 +82,8 @@ static void its_install_cmdq(vm_paddr_t base, size_t size) its_write_u64(GITS_CBASER, cbaser); } -void its_init(vm_paddr_t coll_tbl, size_t coll_tbl_sz, - vm_paddr_t device_tbl, size_t device_tbl_sz, - vm_paddr_t cmdq, size_t cmdq_size) +void its_init(gpa_t coll_tbl, size_t coll_tbl_sz, gpa_t device_tbl, + size_t device_tbl_sz, gpa_t cmdq, size_t cmdq_size) { u32 ctlr; @@ -204,7 +203,7 @@ static void its_send_cmd(void *cmdq_base, struct its_cmd_block *cmd) } } -void its_send_mapd_cmd(void *cmdq_base, u32 device_id, vm_paddr_t itt_base, +void its_send_mapd_cmd(void *cmdq_base, u32 device_id, gpa_t itt_base, size_t itt_size, bool valid) { struct its_cmd_block cmd = {}; diff --git a/tools/testing/selftests/kvm/lib/arm64/processor.c b/tools/testing/selftests/kvm/lib/arm64/processor.c index 43ea40edc533..01325bf4d36f 100644 --- a/tools/testing/selftests/kvm/lib/arm64/processor.c +++ b/tools/testing/selftests/kvm/lib/arm64/processor.c @@ -19,20 +19,20 @@ #define DEFAULT_ARM64_GUEST_STACK_VADDR_MIN 0xac0000 -static vm_vaddr_t exception_handlers; +static gva_t exception_handlers; -static uint64_t pgd_index(struct kvm_vm *vm, vm_vaddr_t gva) +static u64 pgd_index(struct kvm_vm *vm, gva_t gva) { unsigned int shift = (vm->mmu.pgtable_levels - 1) * (vm->page_shift - 3) + vm->page_shift; - uint64_t mask = (1UL << (vm->va_bits - shift)) - 1; + u64 mask = (1UL << (vm->va_bits - shift)) - 1; return (gva >> shift) & mask; } -static uint64_t pud_index(struct kvm_vm *vm, vm_vaddr_t gva) +static u64 pud_index(struct kvm_vm *vm, gva_t gva) { unsigned int shift = 2 * (vm->page_shift - 3) + vm->page_shift; - uint64_t mask = (1UL << (vm->page_shift - 3)) - 1; + u64 mask = (1UL << (vm->page_shift - 3)) - 1; TEST_ASSERT(vm->mmu.pgtable_levels == 4, "Mode %d does not have 4 page table levels", vm->mode); @@ -40,10 +40,10 @@ static uint64_t pud_index(struct kvm_vm *vm, vm_vaddr_t gva) return (gva >> shift) & mask; } -static uint64_t pmd_index(struct kvm_vm *vm, vm_vaddr_t gva) +static u64 pmd_index(struct kvm_vm *vm, gva_t gva) { unsigned int shift = (vm->page_shift - 3) + vm->page_shift; - uint64_t mask = (1UL << (vm->page_shift - 3)) - 1; + u64 mask = (1UL << (vm->page_shift - 3)) - 1; TEST_ASSERT(vm->mmu.pgtable_levels >= 3, "Mode %d does not have >= 3 page table levels", vm->mode); @@ -51,9 +51,9 @@ static uint64_t pmd_index(struct kvm_vm *vm, vm_vaddr_t gva) return (gva >> shift) & mask; } -static uint64_t pte_index(struct kvm_vm *vm, vm_vaddr_t gva) +static u64 pte_index(struct kvm_vm *vm, gva_t gva) { - uint64_t mask = (1UL << (vm->page_shift - 3)) - 1; + u64 mask = (1UL << (vm->page_shift - 3)) - 1; return (gva >> vm->page_shift) & mask; } @@ -63,9 +63,9 @@ static inline bool use_lpa2_pte_format(struct kvm_vm *vm) (vm->pa_bits > 48 || vm->va_bits > 48); } -static uint64_t addr_pte(struct kvm_vm *vm, uint64_t pa, uint64_t attrs) +static u64 addr_pte(struct kvm_vm *vm, u64 pa, u64 attrs) { - uint64_t pte; + u64 pte; if (use_lpa2_pte_format(vm)) { pte = pa & PTE_ADDR_MASK_LPA2(vm->page_shift); @@ -81,9 +81,9 @@ static uint64_t addr_pte(struct kvm_vm *vm, uint64_t pa, uint64_t attrs) return pte; } -static uint64_t pte_addr(struct kvm_vm *vm, uint64_t pte) +static u64 pte_addr(struct kvm_vm *vm, u64 pte) { - uint64_t pa; + u64 pa; if (use_lpa2_pte_format(vm)) { pa = pte & PTE_ADDR_MASK_LPA2(vm->page_shift); @@ -97,13 +97,13 @@ static uint64_t pte_addr(struct kvm_vm *vm, uint64_t pte) return pa; } -static uint64_t ptrs_per_pgd(struct kvm_vm *vm) +static u64 ptrs_per_pgd(struct kvm_vm *vm) { unsigned int shift = (vm->mmu.pgtable_levels - 1) * (vm->page_shift - 3) + vm->page_shift; return 1 << (vm->va_bits - shift); } -static uint64_t __maybe_unused ptrs_per_pte(struct kvm_vm *vm) +static u64 __maybe_unused ptrs_per_pte(struct kvm_vm *vm) { return 1 << (vm->page_shift - 3); } @@ -121,47 +121,46 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) vm->mmu.pgd_created = true; } -static void _virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, - uint64_t flags) +static void _virt_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa, + u64 flags) { - uint8_t attr_idx = flags & (PTE_ATTRINDX_MASK >> PTE_ATTRINDX_SHIFT); - uint64_t pg_attr; - uint64_t *ptep; + u8 attr_idx = flags & (PTE_ATTRINDX_MASK >> PTE_ATTRINDX_SHIFT); + u64 pg_attr; + u64 *ptep; - TEST_ASSERT((vaddr % vm->page_size) == 0, + TEST_ASSERT((gva % vm->page_size) == 0, "Virtual address not on page boundary,\n" - " vaddr: 0x%lx vm->page_size: 0x%x", vaddr, vm->page_size); - TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, - (vaddr >> vm->page_shift)), - "Invalid virtual address, vaddr: 0x%lx", vaddr); - TEST_ASSERT((paddr % vm->page_size) == 0, - "Physical address not on page boundary,\n" - " paddr: 0x%lx vm->page_size: 0x%x", paddr, vm->page_size); - TEST_ASSERT((paddr >> vm->page_shift) <= vm->max_gfn, - "Physical address beyond beyond maximum supported,\n" - " paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", - paddr, vm->max_gfn, vm->page_size); + " gva: 0x%lx vm->page_size: 0x%x", gva, vm->page_size); + TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, (gva >> vm->page_shift)), + "Invalid virtual address, gva: 0x%lx", gva); + TEST_ASSERT((gpa % vm->page_size) == 0, + "Physical address not on page boundary,\n" + " gpa: 0x%lx vm->page_size: 0x%x", gpa, vm->page_size); + TEST_ASSERT((gpa >> vm->page_shift) <= vm->max_gfn, + "Physical address beyond beyond maximum supported,\n" + " gpa: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", + gpa, vm->max_gfn, vm->page_size); - ptep = addr_gpa2hva(vm, vm->mmu.pgd) + pgd_index(vm, vaddr) * 8; + ptep = addr_gpa2hva(vm, vm->mmu.pgd) + pgd_index(vm, gva) * 8; if (!*ptep) *ptep = addr_pte(vm, vm_alloc_page_table(vm), PGD_TYPE_TABLE | PTE_VALID); switch (vm->mmu.pgtable_levels) { case 4: - ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pud_index(vm, vaddr) * 8; + ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pud_index(vm, gva) * 8; if (!*ptep) *ptep = addr_pte(vm, vm_alloc_page_table(vm), PUD_TYPE_TABLE | PTE_VALID); /* fall through */ case 3: - ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pmd_index(vm, vaddr) * 8; + ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pmd_index(vm, gva) * 8; if (!*ptep) *ptep = addr_pte(vm, vm_alloc_page_table(vm), PMD_TYPE_TABLE | PTE_VALID); /* fall through */ case 2: - ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pte_index(vm, vaddr) * 8; + ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pte_index(vm, gva) * 8; break; default: TEST_FAIL("Page table levels must be 2, 3, or 4"); @@ -171,19 +170,19 @@ static void _virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, if (!use_lpa2_pte_format(vm)) pg_attr |= PTE_SHARED; - *ptep = addr_pte(vm, paddr, pg_attr); + *ptep = addr_pte(vm, gpa, pg_attr); } -void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) +void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa) { - uint64_t attr_idx = MT_NORMAL; + u64 attr_idx = MT_NORMAL; - _virt_pg_map(vm, vaddr, paddr, attr_idx); + _virt_pg_map(vm, gva, gpa, attr_idx); } -uint64_t *virt_get_pte_hva_at_level(struct kvm_vm *vm, vm_vaddr_t gva, int level) +u64 *virt_get_pte_hva_at_level(struct kvm_vm *vm, gva_t gva, int level) { - uint64_t *ptep; + u64 *ptep; if (!vm->mmu.pgd_created) goto unmapped_gva; @@ -225,23 +224,23 @@ unmapped_gva: exit(EXIT_FAILURE); } -uint64_t *virt_get_pte_hva(struct kvm_vm *vm, vm_vaddr_t gva) +u64 *virt_get_pte_hva(struct kvm_vm *vm, gva_t gva) { return virt_get_pte_hva_at_level(vm, gva, 3); } -vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) +gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva) { - uint64_t *ptep = virt_get_pte_hva(vm, gva); + u64 *ptep = virt_get_pte_hva(vm, gva); return pte_addr(vm, *ptep) + (gva & (vm->page_size - 1)); } -static void pte_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent, uint64_t page, int level) +static void pte_dump(FILE *stream, struct kvm_vm *vm, u8 indent, u64 page, int level) { #ifdef DEBUG static const char * const type[] = { "", "pud", "pmd", "pte" }; - uint64_t pte, *ptep; + u64 pte, *ptep; if (level == 4) return; @@ -256,10 +255,10 @@ static void pte_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent, uint64_t p #endif } -void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) +void virt_arch_dump(FILE *stream, struct kvm_vm *vm, u8 indent) { int level = 4 - (vm->mmu.pgtable_levels - 1); - uint64_t pgd, *ptep; + u64 pgd, *ptep; if (!vm->mmu.pgd_created) return; @@ -298,7 +297,7 @@ void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init) { struct kvm_vcpu_init default_init = { .target = -1, }; struct kvm_vm *vm = vcpu->vm; - uint64_t sctlr_el1, tcr_el1, ttbr0_el1; + u64 sctlr_el1, tcr_el1, ttbr0_el1; if (!init) { kvm_get_default_vcpu_target(vm, &default_init); @@ -397,9 +396,9 @@ void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init) HCR_EL2_RW | HCR_EL2_TGE | HCR_EL2_E2H); } -void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent) +void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent) { - uint64_t pstate, pc; + u64 pstate, pc; pstate = vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pstate)); pc = vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc)); @@ -410,29 +409,29 @@ void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent) void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code) { - vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.pc), (uint64_t)guest_code); + vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.pc), (u64)guest_code); } -static struct kvm_vcpu *__aarch64_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, +static struct kvm_vcpu *__aarch64_vcpu_add(struct kvm_vm *vm, u32 vcpu_id, struct kvm_vcpu_init *init) { size_t stack_size; - uint64_t stack_vaddr; + gva_t stack_gva; struct kvm_vcpu *vcpu = __vm_vcpu_add(vm, vcpu_id); stack_size = vm->page_size == 4096 ? DEFAULT_STACK_PGS * vm->page_size : vm->page_size; - stack_vaddr = __vm_vaddr_alloc(vm, stack_size, - DEFAULT_ARM64_GUEST_STACK_VADDR_MIN, - MEM_REGION_DATA); + stack_gva = __vm_alloc(vm, stack_size, + DEFAULT_ARM64_GUEST_STACK_VADDR_MIN, + MEM_REGION_DATA); aarch64_vcpu_setup(vcpu, init); - vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SP_EL1), stack_vaddr + stack_size); + vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_SP_EL1), stack_gva + stack_size); return vcpu; } -struct kvm_vcpu *aarch64_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, +struct kvm_vcpu *aarch64_vcpu_add(struct kvm_vm *vm, u32 vcpu_id, struct kvm_vcpu_init *init, void *guest_code) { struct kvm_vcpu *vcpu = __aarch64_vcpu_add(vm, vcpu_id, init); @@ -442,7 +441,7 @@ struct kvm_vcpu *aarch64_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, return vcpu; } -struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) +struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id) { return __aarch64_vcpu_add(vm, vcpu_id, NULL); } @@ -459,13 +458,13 @@ void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...) for (i = 0; i < num; i++) { vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.regs[i]), - va_arg(ap, uint64_t)); + va_arg(ap, u64)); } va_end(ap); } -void kvm_exit_unexpected_exception(int vector, uint64_t ec, bool valid_ec) +void kvm_exit_unexpected_exception(int vector, u64 ec, bool valid_ec) { ucall(UCALL_UNHANDLED, 3, vector, ec, valid_ec); while (1) @@ -498,7 +497,7 @@ void vcpu_init_descriptor_tables(struct kvm_vcpu *vcpu) { extern char vectors; - vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_VBAR_EL1), (uint64_t)&vectors); + vcpu_set_reg(vcpu, ctxt_reg_alias(vcpu, SYS_VBAR_EL1), (u64)&vectors); } void route_exception(struct ex_regs *regs, int vector) @@ -536,10 +535,10 @@ unexpected_exception: void vm_init_descriptor_tables(struct kvm_vm *vm) { - vm->handlers = __vm_vaddr_alloc(vm, sizeof(struct handlers), - vm->page_size, MEM_REGION_DATA); + vm->handlers = __vm_alloc(vm, sizeof(struct handlers), vm->page_size, + MEM_REGION_DATA); - *(vm_vaddr_t *)addr_gva2hva(vm, (vm_vaddr_t)(&exception_handlers)) = vm->handlers; + *(gva_t *)addr_gva2hva(vm, (gva_t)(&exception_handlers)) = vm->handlers; } void vm_install_sync_handler(struct kvm_vm *vm, int vector, int ec, @@ -563,13 +562,13 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, handlers->exception_handlers[vector][0] = handler; } -uint32_t guest_get_vcpuid(void) +u32 guest_get_vcpuid(void) { return read_sysreg(tpidr_el1); } -static uint32_t max_ipa_for_page_size(uint32_t vm_ipa, uint32_t gran, - uint32_t not_sup_val, uint32_t ipa52_min_val) +static u32 max_ipa_for_page_size(u32 vm_ipa, u32 gran, + u32 not_sup_val, u32 ipa52_min_val) { if (gran == not_sup_val) return 0; @@ -579,16 +578,16 @@ static uint32_t max_ipa_for_page_size(uint32_t vm_ipa, uint32_t gran, return min(vm_ipa, 48U); } -void aarch64_get_supported_page_sizes(uint32_t ipa, uint32_t *ipa4k, - uint32_t *ipa16k, uint32_t *ipa64k) +void aarch64_get_supported_page_sizes(u32 ipa, u32 *ipa4k, + u32 *ipa16k, u32 *ipa64k) { struct kvm_vcpu_init preferred_init; int kvm_fd, vm_fd, vcpu_fd, err; - uint64_t val; - uint32_t gran; + u64 val; + u32 gran; struct kvm_one_reg reg = { .id = KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR0_EL1), - .addr = (uint64_t)&val, + .addr = (u64)&val, }; kvm_fd = open_kvm_dev_path_or_exit(); @@ -646,17 +645,17 @@ void aarch64_get_supported_page_sizes(uint32_t ipa, uint32_t *ipa4k, : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7") -void smccc_hvc(uint32_t function_id, uint64_t arg0, uint64_t arg1, - uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, - uint64_t arg6, struct arm_smccc_res *res) +void smccc_hvc(u32 function_id, u64 arg0, u64 arg1, + u64 arg2, u64 arg3, u64 arg4, u64 arg5, + u64 arg6, struct arm_smccc_res *res) { __smccc_call(hvc, function_id, arg0, arg1, arg2, arg3, arg4, arg5, arg6, res); } -void smccc_smc(uint32_t function_id, uint64_t arg0, uint64_t arg1, - uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, - uint64_t arg6, struct arm_smccc_res *res) +void smccc_smc(u32 function_id, u64 arg0, u64 arg1, + u64 arg2, u64 arg3, u64 arg4, u64 arg5, + u64 arg6, struct arm_smccc_res *res) { __smccc_call(smc, function_id, arg0, arg1, arg2, arg3, arg4, arg5, arg6, res); @@ -671,7 +670,7 @@ void kvm_selftest_arch_init(void) guest_modes_append_default(); } -void vm_vaddr_populate_bitmap(struct kvm_vm *vm) +void vm_populate_gva_bitmap(struct kvm_vm *vm) { /* * arm64 selftests use only TTBR0_EL1, meaning that the valid VA space diff --git a/tools/testing/selftests/kvm/lib/arm64/ucall.c b/tools/testing/selftests/kvm/lib/arm64/ucall.c index ddab0ce89d4d..e0550ad5aa75 100644 --- a/tools/testing/selftests/kvm/lib/arm64/ucall.c +++ b/tools/testing/selftests/kvm/lib/arm64/ucall.c @@ -6,17 +6,17 @@ */ #include "kvm_util.h" -vm_vaddr_t *ucall_exit_mmio_addr; +gva_t *ucall_exit_mmio_addr; -void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) +void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa) { - vm_vaddr_t mmio_gva = vm_vaddr_unused_gap(vm, vm->page_size, KVM_UTIL_MIN_VADDR); + gva_t mmio_gva = vm_unused_gva_gap(vm, vm->page_size, KVM_UTIL_MIN_VADDR); virt_map(vm, mmio_gva, mmio_gpa, 1); vm->ucall_mmio_addr = mmio_gpa; - write_guest_global(vm, ucall_exit_mmio_addr, (vm_vaddr_t *)mmio_gva); + write_guest_global(vm, ucall_exit_mmio_addr, (gva_t *)mmio_gva); } void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) @@ -25,9 +25,9 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) if (run->exit_reason == KVM_EXIT_MMIO && run->mmio.phys_addr == vcpu->vm->ucall_mmio_addr) { - TEST_ASSERT(run->mmio.is_write && run->mmio.len == sizeof(uint64_t), + TEST_ASSERT(run->mmio.is_write && run->mmio.len == sizeof(u64), "Unexpected ucall exit mmio address access"); - return (void *)(*((uint64_t *)run->mmio.data)); + return (void *)(*((u64 *)run->mmio.data)); } return NULL; diff --git a/tools/testing/selftests/kvm/lib/arm64/vgic.c b/tools/testing/selftests/kvm/lib/arm64/vgic.c index d0f7bd0984b8..4ecebf3146a2 100644 --- a/tools/testing/selftests/kvm/lib/arm64/vgic.c +++ b/tools/testing/selftests/kvm/lib/arm64/vgic.c @@ -41,10 +41,10 @@ bool kvm_supports_vgic_v3(void) * redistributor regions of the guest. Since it depends on the number of * vCPUs for the VM, it must be called after all the vCPUs have been created. */ -int __vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs) +int __vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, u32 nr_irqs) { int gic_fd; - uint64_t attr; + u64 attr; unsigned int nr_gic_pages; /* Distributor setup */ @@ -77,7 +77,7 @@ void __vgic_v3_init(int fd) KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); } -int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs) +int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, u32 nr_irqs) { unsigned int nr_vcpus_created = 0; struct list_head *iter; @@ -104,11 +104,11 @@ int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs) } /* should only work for level sensitive interrupts */ -int _kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level) +int _kvm_irq_set_level_info(int gic_fd, u32 intid, int level) { - uint64_t attr = 32 * (intid / 32); - uint64_t index = intid % 32; - uint64_t val; + u64 attr = 32 * (intid / 32); + u64 index = intid % 32; + u64 val; int ret; ret = __kvm_device_attr_get(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, @@ -122,16 +122,16 @@ int _kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level) return ret; } -void kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level) +void kvm_irq_set_level_info(int gic_fd, u32 intid, int level) { int ret = _kvm_irq_set_level_info(gic_fd, intid, level); TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, ret)); } -int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level) +int _kvm_arm_irq_line(struct kvm_vm *vm, u32 intid, int level) { - uint32_t irq = intid & KVM_ARM_IRQ_NUM_MASK; + u32 irq = intid & KVM_ARM_IRQ_NUM_MASK; TEST_ASSERT(!INTID_IS_SGI(intid), "KVM_IRQ_LINE's interface itself " "doesn't allow injecting SGIs. There's no mask for it."); @@ -144,23 +144,23 @@ int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level) return _kvm_irq_line(vm, irq, level); } -void kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level) +void kvm_arm_irq_line(struct kvm_vm *vm, u32 intid, int level) { int ret = _kvm_arm_irq_line(vm, intid, level); TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_IRQ_LINE, ret)); } -static void vgic_poke_irq(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu, - uint64_t reg_off) +static void vgic_poke_irq(int gic_fd, u32 intid, struct kvm_vcpu *vcpu, + u64 reg_off) { - uint64_t reg = intid / 32; - uint64_t index = intid % 32; - uint64_t attr = reg_off + reg * 4; - uint64_t val; + u64 reg = intid / 32; + u64 index = intid % 32; + u64 attr = reg_off + reg * 4; + u64 val; bool intid_is_private = INTID_IS_SGI(intid) || INTID_IS_PPI(intid); - uint32_t group = intid_is_private ? KVM_DEV_ARM_VGIC_GRP_REDIST_REGS + u32 group = intid_is_private ? KVM_DEV_ARM_VGIC_GRP_REDIST_REGS : KVM_DEV_ARM_VGIC_GRP_DIST_REGS; if (intid_is_private) { @@ -183,12 +183,12 @@ static void vgic_poke_irq(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu, kvm_device_attr_set(gic_fd, group, attr, &val); } -void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu) +void kvm_irq_write_ispendr(int gic_fd, u32 intid, struct kvm_vcpu *vcpu) { vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISPENDR); } -void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu) +void kvm_irq_write_isactiver(int gic_fd, u32 intid, struct kvm_vcpu *vcpu) { vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISACTIVER); } diff --git a/tools/testing/selftests/kvm/lib/elf.c b/tools/testing/selftests/kvm/lib/elf.c index f34d926d9735..b689c4df4a01 100644 --- a/tools/testing/selftests/kvm/lib/elf.c +++ b/tools/testing/selftests/kvm/lib/elf.c @@ -156,21 +156,20 @@ void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename) TEST_ASSERT(phdr.p_memsz > 0, "Unexpected loadable segment " "memsize of 0,\n" " phdr index: %u p_memsz: 0x%" PRIx64, - n1, (uint64_t) phdr.p_memsz); - vm_vaddr_t seg_vstart = align_down(phdr.p_vaddr, vm->page_size); - vm_vaddr_t seg_vend = phdr.p_vaddr + phdr.p_memsz - 1; + n1, (u64)phdr.p_memsz); + gva_t seg_vstart = align_down(phdr.p_vaddr, vm->page_size); + gva_t seg_vend = phdr.p_vaddr + phdr.p_memsz - 1; seg_vend |= vm->page_size - 1; size_t seg_size = seg_vend - seg_vstart + 1; - vm_vaddr_t vaddr = __vm_vaddr_alloc(vm, seg_size, seg_vstart, - MEM_REGION_CODE); - TEST_ASSERT(vaddr == seg_vstart, "Unable to allocate " + gva_t gva = __vm_alloc(vm, seg_size, seg_vstart, MEM_REGION_CODE); + TEST_ASSERT(gva == seg_vstart, "Unable to allocate " "virtual memory for segment at requested min addr,\n" " segment idx: %u\n" " seg_vstart: 0x%lx\n" - " vaddr: 0x%lx", - n1, seg_vstart, vaddr); - memset(addr_gva2hva(vm, vaddr), 0, seg_size); + " gva: 0x%lx", + n1, seg_vstart, gva); + memset(addr_gva2hva(vm, gva), 0, seg_size); /* TODO(lhuemill): Set permissions of each memory segment * based on the least-significant 3 bits of phdr.p_flags. */ diff --git a/tools/testing/selftests/kvm/lib/guest_modes.c b/tools/testing/selftests/kvm/lib/guest_modes.c index ce3099630397..7a96c43b5704 100644 --- a/tools/testing/selftests/kvm/lib/guest_modes.c +++ b/tools/testing/selftests/kvm/lib/guest_modes.c @@ -20,7 +20,7 @@ void guest_modes_append_default(void) #ifdef __aarch64__ { unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE); - uint32_t ipa4k, ipa16k, ipa64k; + u32 ipa4k, ipa16k, ipa64k; int i; aarch64_get_supported_page_sizes(limit, &ipa4k, &ipa16k, &ipa64k); diff --git a/tools/testing/selftests/kvm/lib/guest_sprintf.c b/tools/testing/selftests/kvm/lib/guest_sprintf.c index 74627514c4d4..7a33965349a7 100644 --- a/tools/testing/selftests/kvm/lib/guest_sprintf.c +++ b/tools/testing/selftests/kvm/lib/guest_sprintf.c @@ -35,8 +35,8 @@ static int skip_atoi(const char **s) ({ \ int __res; \ \ - __res = ((uint64_t) n) % (uint32_t) base; \ - n = ((uint64_t) n) / (uint32_t) base; \ + __res = ((u64)n) % (u32)base; \ + n = ((u64)n) / (u32)base; \ __res; \ }) @@ -119,7 +119,7 @@ int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args) { char *str, *end; const char *s; - uint64_t num; + u64 num; int i, base; int len; @@ -216,7 +216,7 @@ repeat: while (--field_width > 0) APPEND_BUFFER_SAFE(str, end, ' '); APPEND_BUFFER_SAFE(str, end, - (uint8_t)va_arg(args, int)); + (u8)va_arg(args, int)); while (--field_width > 0) APPEND_BUFFER_SAFE(str, end, ' '); continue; @@ -240,7 +240,7 @@ repeat: flags |= SPECIAL | SMALL | ZEROPAD; } str = number(str, end, - (uint64_t)va_arg(args, void *), 16, + (u64)va_arg(args, void *), 16, field_width, precision, flags); continue; @@ -284,15 +284,15 @@ repeat: continue; } if (qualifier == 'l') - num = va_arg(args, uint64_t); + num = va_arg(args, u64); else if (qualifier == 'h') { - num = (uint16_t)va_arg(args, int); + num = (u16)va_arg(args, int); if (flags & SIGN) - num = (int16_t)num; + num = (s16)num; } else if (flags & SIGN) num = va_arg(args, int); else - num = va_arg(args, uint32_t); + num = va_arg(args, u32); str = number(str, end, num, base, field_width, precision, flags); } diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 1959bf556e88..2a76eca7029d 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -20,9 +20,9 @@ #define KVM_UTIL_MIN_PFN 2 -uint32_t guest_random_seed; +u32 guest_random_seed; struct guest_random_state guest_rng; -static uint32_t last_guest_seed; +static u32 last_guest_seed; static size_t vcpu_mmap_sz(void); @@ -165,7 +165,7 @@ unsigned int kvm_check_cap(long cap) return (unsigned int)ret; } -void vm_enable_dirty_ring(struct kvm_vm *vm, uint32_t ring_size) +void vm_enable_dirty_ring(struct kvm_vm *vm, u32 ring_size) { if (vm_check_cap(vm, KVM_CAP_DIRTY_LOG_RING_ACQ_REL)) vm_enable_cap(vm, KVM_CAP_DIRTY_LOG_RING_ACQ_REL, ring_size); @@ -189,7 +189,7 @@ static void vm_open(struct kvm_vm *vm) vm->stats.fd = -1; } -const char *vm_guest_mode_string(uint32_t i) +const char *vm_guest_mode_string(u32 i) { static const char * const strings[] = { [VM_MODE_P52V48_4K] = "PA-bits:52, VA-bits:48, 4K pages", @@ -267,7 +267,7 @@ _Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) * based on the MSB of the VA. On architectures with this behavior * the VA region spans [0, 2^(va_bits - 1)), [-(2^(va_bits - 1), -1]. */ -__weak void vm_vaddr_populate_bitmap(struct kvm_vm *vm) +__weak void vm_populate_gva_bitmap(struct kvm_vm *vm) { sparsebit_set_num(vm->vpages_valid, 0, (1ULL << (vm->va_bits - 1)) >> vm->page_shift); @@ -385,7 +385,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape) /* Limit to VA-bit canonical virtual addresses. */ vm->vpages_valid = sparsebit_alloc(); - vm_vaddr_populate_bitmap(vm); + vm_populate_gva_bitmap(vm); /* Limit physical addresses to PA-bits. */ vm->max_gfn = vm_compute_max_gfn(vm); @@ -396,12 +396,12 @@ struct kvm_vm *____vm_create(struct vm_shape shape) return vm; } -static uint64_t vm_nr_pages_required(enum vm_guest_mode mode, - uint32_t nr_runnable_vcpus, - uint64_t extra_mem_pages) +static u64 vm_nr_pages_required(enum vm_guest_mode mode, + u32 nr_runnable_vcpus, + u64 extra_mem_pages) { - uint64_t page_size = vm_guest_mode_params[mode].page_size; - uint64_t nr_pages; + u64 page_size = vm_guest_mode_params[mode].page_size; + u64 nr_pages; TEST_ASSERT(nr_runnable_vcpus, "Use vm_create_barebones() for VMs that _never_ have vCPUs"); @@ -435,7 +435,7 @@ static uint64_t vm_nr_pages_required(enum vm_guest_mode mode, return vm_adjust_num_guest_pages(mode, nr_pages); } -void kvm_set_files_rlimit(uint32_t nr_vcpus) +void kvm_set_files_rlimit(u32 nr_vcpus) { /* * Each vCPU will open two file descriptors: the vCPU itself and the @@ -476,10 +476,10 @@ static bool is_guest_memfd_required(struct vm_shape shape) #endif } -struct kvm_vm *__vm_create(struct vm_shape shape, uint32_t nr_runnable_vcpus, - uint64_t nr_extra_pages) +struct kvm_vm *__vm_create(struct vm_shape shape, u32 nr_runnable_vcpus, + u64 nr_extra_pages) { - uint64_t nr_pages = vm_nr_pages_required(shape.mode, nr_runnable_vcpus, + u64 nr_pages = vm_nr_pages_required(shape.mode, nr_runnable_vcpus, nr_extra_pages); struct userspace_mem_region *slot0; struct kvm_vm *vm; @@ -546,8 +546,8 @@ struct kvm_vm *__vm_create(struct vm_shape shape, uint32_t nr_runnable_vcpus, * extra_mem_pages is only used to calculate the maximum page table size, * no real memory allocation for non-slot0 memory in this function. */ -struct kvm_vm *__vm_create_with_vcpus(struct vm_shape shape, uint32_t nr_vcpus, - uint64_t extra_mem_pages, +struct kvm_vm *__vm_create_with_vcpus(struct vm_shape shape, u32 nr_vcpus, + u64 extra_mem_pages, void *guest_code, struct kvm_vcpu *vcpus[]) { struct kvm_vm *vm; @@ -566,7 +566,7 @@ struct kvm_vm *__vm_create_with_vcpus(struct vm_shape shape, uint32_t nr_vcpus, struct kvm_vm *__vm_create_shape_with_one_vcpu(struct vm_shape shape, struct kvm_vcpu **vcpu, - uint64_t extra_mem_pages, + u64 extra_mem_pages, void *guest_code) { struct kvm_vcpu *vcpus[1]; @@ -614,7 +614,7 @@ void kvm_vm_restart(struct kvm_vm *vmp) } __weak struct kvm_vcpu *vm_arch_vcpu_recreate(struct kvm_vm *vm, - uint32_t vcpu_id) + u32 vcpu_id) { return __vm_vcpu_add(vm, vcpu_id); } @@ -636,9 +636,9 @@ int __pin_task_to_cpu(pthread_t task, int cpu) return pthread_setaffinity_np(task, sizeof(cpuset), &cpuset); } -static uint32_t parse_pcpu(const char *cpu_str, const cpu_set_t *allowed_mask) +static u32 parse_pcpu(const char *cpu_str, const cpu_set_t *allowed_mask) { - uint32_t pcpu = atoi_non_negative("CPU number", cpu_str); + u32 pcpu = atoi_non_negative("CPU number", cpu_str); TEST_ASSERT(CPU_ISSET(pcpu, allowed_mask), "Not allowed to run on pCPU '%d', check cgroups?", pcpu); @@ -662,7 +662,7 @@ void kvm_print_vcpu_pinning_help(void) " (default: no pinning)\n", name, name); } -void kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[], +void kvm_parse_vcpu_pinning(const char *pcpus_string, u32 vcpu_to_pcpu[], int nr_vcpus) { cpu_set_t allowed_mask; @@ -715,15 +715,15 @@ void kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[], * region exists. */ static struct userspace_mem_region * -userspace_mem_region_find(struct kvm_vm *vm, uint64_t start, uint64_t end) +userspace_mem_region_find(struct kvm_vm *vm, u64 start, u64 end) { struct rb_node *node; for (node = vm->regions.gpa_tree.rb_node; node; ) { struct userspace_mem_region *region = container_of(node, struct userspace_mem_region, gpa_node); - uint64_t existing_start = region->region.guest_phys_addr; - uint64_t existing_end = region->region.guest_phys_addr + u64 existing_start = region->region.guest_phys_addr; + u64 existing_end = region->region.guest_phys_addr + region->region.memory_size - 1; if (start <= existing_end && end >= existing_start) return region; @@ -918,8 +918,8 @@ static void vm_userspace_mem_region_hva_insert(struct rb_root *hva_tree, } -int __vm_set_user_memory_region(struct kvm_vm *vm, uint32_t slot, uint32_t flags, - uint64_t gpa, uint64_t size, void *hva) +int __vm_set_user_memory_region(struct kvm_vm *vm, u32 slot, u32 flags, + gpa_t gpa, u64 size, void *hva) { struct kvm_userspace_memory_region region = { .slot = slot, @@ -932,8 +932,8 @@ int __vm_set_user_memory_region(struct kvm_vm *vm, uint32_t slot, uint32_t flags return ioctl(vm->fd, KVM_SET_USER_MEMORY_REGION, ®ion); } -void vm_set_user_memory_region(struct kvm_vm *vm, uint32_t slot, uint32_t flags, - uint64_t gpa, uint64_t size, void *hva) +void vm_set_user_memory_region(struct kvm_vm *vm, u32 slot, u32 flags, + gpa_t gpa, u64 size, void *hva) { int ret = __vm_set_user_memory_region(vm, slot, flags, gpa, size, hva); @@ -945,9 +945,9 @@ void vm_set_user_memory_region(struct kvm_vm *vm, uint32_t slot, uint32_t flags, __TEST_REQUIRE(kvm_has_cap(KVM_CAP_USER_MEMORY2), \ "KVM selftests now require KVM_SET_USER_MEMORY_REGION2 (introduced in v6.8)") -int __vm_set_user_memory_region2(struct kvm_vm *vm, uint32_t slot, uint32_t flags, - uint64_t gpa, uint64_t size, void *hva, - uint32_t guest_memfd, uint64_t guest_memfd_offset) +int __vm_set_user_memory_region2(struct kvm_vm *vm, u32 slot, u32 flags, + gpa_t gpa, u64 size, void *hva, + u32 guest_memfd, u64 guest_memfd_offset) { struct kvm_userspace_memory_region2 region = { .slot = slot, @@ -964,9 +964,9 @@ int __vm_set_user_memory_region2(struct kvm_vm *vm, uint32_t slot, uint32_t flag return ioctl(vm->fd, KVM_SET_USER_MEMORY_REGION2, ®ion); } -void vm_set_user_memory_region2(struct kvm_vm *vm, uint32_t slot, uint32_t flags, - uint64_t gpa, uint64_t size, void *hva, - uint32_t guest_memfd, uint64_t guest_memfd_offset) +void vm_set_user_memory_region2(struct kvm_vm *vm, u32 slot, u32 flags, + gpa_t gpa, u64 size, void *hva, + u32 guest_memfd, u64 guest_memfd_offset) { int ret = __vm_set_user_memory_region2(vm, slot, flags, gpa, size, hva, guest_memfd, guest_memfd_offset); @@ -978,14 +978,14 @@ void vm_set_user_memory_region2(struct kvm_vm *vm, uint32_t slot, uint32_t flags /* FIXME: This thing needs to be ripped apart and rewritten. */ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type, - uint64_t gpa, uint32_t slot, uint64_t npages, uint32_t flags, - int guest_memfd, uint64_t guest_memfd_offset) + gpa_t gpa, u32 slot, u64 npages, u32 flags, + int guest_memfd, u64 guest_memfd_offset) { int ret; struct userspace_mem_region *region; size_t backing_src_pagesz = get_backing_src_pagesz(src_type); size_t mem_size = npages * vm->page_size; - size_t alignment; + size_t alignment = 1; TEST_REQUIRE_SET_USER_MEMORY_REGION2(); @@ -1016,8 +1016,8 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type, " requested gpa: 0x%lx npages: 0x%lx page_size: 0x%x\n" " existing gpa: 0x%lx size: 0x%lx", gpa, npages, vm->page_size, - (uint64_t) region->region.guest_phys_addr, - (uint64_t) region->region.memory_size); + (u64)region->region.guest_phys_addr, + (u64)region->region.memory_size); /* Confirm no region with the requested slot already exists. */ hash_for_each_possible(vm->regions.slot_hash, region, slot_node, @@ -1027,11 +1027,11 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type, TEST_FAIL("A mem region with the requested slot " "already exists.\n" - " requested slot: %u paddr: 0x%lx npages: 0x%lx\n" - " existing slot: %u paddr: 0x%lx size: 0x%lx", + " requested slot: %u gpa: 0x%lx npages: 0x%lx\n" + " existing slot: %u gpa: 0x%lx size: 0x%lx", slot, gpa, npages, region->region.slot, - (uint64_t) region->region.guest_phys_addr, - (uint64_t) region->region.memory_size); + (u64)region->region.guest_phys_addr, + (u64)region->region.memory_size); } /* Allocate and initialize new mem region structure. */ @@ -1039,13 +1039,6 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type, TEST_ASSERT(region != NULL, "Insufficient Memory"); region->mmap_size = mem_size; -#ifdef __s390x__ - /* On s390x, the host address must be aligned to 1M (due to PGSTEs) */ - alignment = 0x100000; -#else - alignment = 1; -#endif - /* * When using THP mmap is not guaranteed to returned a hugepage aligned * address so we have to pad the mmap. Padding is not needed for HugeTLB @@ -1092,7 +1085,7 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type, if (flags & KVM_MEM_GUEST_MEMFD) { if (guest_memfd < 0) { - uint32_t guest_memfd_flags = 0; + u32 guest_memfd_flags = 0; TEST_ASSERT(!guest_memfd_offset, "Offset must be zero when creating new guest_memfd"); guest_memfd = vm_create_guest_memfd(vm, mem_size, guest_memfd_flags); @@ -1148,8 +1141,7 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type, void vm_userspace_mem_region_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type, - uint64_t gpa, uint32_t slot, uint64_t npages, - uint32_t flags) + gpa_t gpa, u32 slot, u64 npages, u32 flags) { vm_mem_add(vm, src_type, gpa, slot, npages, flags, -1, 0); } @@ -1170,7 +1162,7 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm, * memory slot ID). */ struct userspace_mem_region * -memslot2region(struct kvm_vm *vm, uint32_t memslot) +memslot2region(struct kvm_vm *vm, u32 memslot) { struct userspace_mem_region *region; @@ -1201,7 +1193,7 @@ memslot2region(struct kvm_vm *vm, uint32_t memslot) * Sets the flags of the memory region specified by the value of slot, * to the values given by flags. */ -void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags) +void vm_mem_region_set_flags(struct kvm_vm *vm, u32 slot, u32 flags) { int ret; struct userspace_mem_region *region; @@ -1217,7 +1209,7 @@ void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags) ret, errno, slot, flags); } -void vm_mem_region_reload(struct kvm_vm *vm, uint32_t slot) +void vm_mem_region_reload(struct kvm_vm *vm, u32 slot) { struct userspace_mem_region *region = memslot2region(vm, slot); struct kvm_userspace_memory_region2 tmp = region->region; @@ -1241,7 +1233,7 @@ void vm_mem_region_reload(struct kvm_vm *vm, uint32_t slot) * * Change the gpa of a memory region. */ -void vm_mem_region_move(struct kvm_vm *vm, uint32_t slot, uint64_t new_gpa) +void vm_mem_region_move(struct kvm_vm *vm, u32 slot, u64 new_gpa) { struct userspace_mem_region *region; int ret; @@ -1270,7 +1262,7 @@ void vm_mem_region_move(struct kvm_vm *vm, uint32_t slot, uint64_t new_gpa) * * Delete a memory region. */ -void vm_mem_region_delete(struct kvm_vm *vm, uint32_t slot) +void vm_mem_region_delete(struct kvm_vm *vm, u32 slot) { struct userspace_mem_region *region = memslot2region(vm, slot); @@ -1280,18 +1272,18 @@ void vm_mem_region_delete(struct kvm_vm *vm, uint32_t slot) __vm_mem_region_delete(vm, region); } -void vm_guest_mem_fallocate(struct kvm_vm *vm, uint64_t base, uint64_t size, +void vm_guest_mem_fallocate(struct kvm_vm *vm, u64 base, u64 size, bool punch_hole) { const int mode = FALLOC_FL_KEEP_SIZE | (punch_hole ? FALLOC_FL_PUNCH_HOLE : 0); struct userspace_mem_region *region; - uint64_t end = base + size; - uint64_t gpa, len; + u64 end = base + size; + gpa_t gpa, len; off_t fd_offset; int ret; for (gpa = base; gpa < end; gpa += len) { - uint64_t offset; + u64 offset; region = userspace_mem_region_find(vm, gpa, gpa); TEST_ASSERT(region && region->region.flags & KVM_MEM_GUEST_MEMFD, @@ -1299,7 +1291,7 @@ void vm_guest_mem_fallocate(struct kvm_vm *vm, uint64_t base, uint64_t size, offset = gpa - region->region.guest_phys_addr; fd_offset = region->region.guest_memfd_offset + offset; - len = min_t(uint64_t, end - gpa, region->region.memory_size - offset); + len = min_t(u64, end - gpa, region->region.memory_size - offset); ret = fallocate(region->region.guest_memfd, mode, fd_offset, len); TEST_ASSERT(!ret, "fallocate() failed to %s at %lx (len = %lu), fd = %d, mode = %x, offset = %lx", @@ -1324,7 +1316,7 @@ static size_t vcpu_mmap_sz(void) return ret; } -static bool vcpu_exists(struct kvm_vm *vm, uint32_t vcpu_id) +static bool vcpu_exists(struct kvm_vm *vm, u32 vcpu_id) { struct kvm_vcpu *vcpu; @@ -1340,7 +1332,7 @@ static bool vcpu_exists(struct kvm_vm *vm, uint32_t vcpu_id) * Adds a virtual CPU to the VM specified by vm with the ID given by vcpu_id. * No additional vCPU setup is done. Returns the vCPU. */ -struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) +struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, u32 vcpu_id) { struct kvm_vcpu *vcpu; @@ -1374,33 +1366,18 @@ struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) } /* - * VM Virtual Address Unused Gap - * - * Input Args: - * vm - Virtual Machine - * sz - Size (bytes) - * vaddr_min - Minimum Virtual Address - * - * Output Args: None - * - * Return: - * Lowest virtual address at or above vaddr_min, with at least - * sz unused bytes. TEST_ASSERT failure if no area of at least - * size sz is available. - * - * Within the VM specified by vm, locates the lowest starting virtual - * address >= vaddr_min, that has at least sz unallocated bytes. A + * Within the VM specified by @vm, locates the lowest starting guest virtual + * address >= @min_gva, that has at least @sz unallocated bytes. A * TEST_ASSERT failure occurs for invalid input or no area of at least - * sz unallocated bytes >= vaddr_min is available. + * @sz unallocated bytes >= @min_gva is available. */ -vm_vaddr_t vm_vaddr_unused_gap(struct kvm_vm *vm, size_t sz, - vm_vaddr_t vaddr_min) +gva_t vm_unused_gva_gap(struct kvm_vm *vm, size_t sz, gva_t min_gva) { - uint64_t pages = (sz + vm->page_size - 1) >> vm->page_shift; + u64 pages = (sz + vm->page_size - 1) >> vm->page_shift; /* Determine lowest permitted virtual page index. */ - uint64_t pgidx_start = (vaddr_min + vm->page_size - 1) >> vm->page_shift; - if ((pgidx_start * vm->page_size) < vaddr_min) + u64 pgidx_start = (min_gva + vm->page_size - 1) >> vm->page_shift; + if ((pgidx_start * vm->page_size) < min_gva) goto no_va_found; /* Loop over section with enough valid virtual page indexes. */ @@ -1437,7 +1414,7 @@ vm_vaddr_t vm_vaddr_unused_gap(struct kvm_vm *vm, size_t sz, } while (pgidx_start != 0); no_va_found: - TEST_FAIL("No vaddr of specified pages available, pages: 0x%lx", pages); + TEST_FAIL("No gva of specified pages available, pages: 0x%lx", pages); /* NOT REACHED */ return -1; @@ -1459,145 +1436,91 @@ va_found: return pgidx_start * vm->page_size; } -static vm_vaddr_t ____vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, - vm_vaddr_t vaddr_min, - enum kvm_mem_region_type type, - bool protected) +static gva_t ____vm_alloc(struct kvm_vm *vm, size_t sz, gva_t min_gva, + enum kvm_mem_region_type type, bool protected) { - uint64_t pages = (sz >> vm->page_shift) + ((sz % vm->page_size) != 0); + u64 pages = (sz >> vm->page_shift) + ((sz % vm->page_size) != 0); virt_pgd_alloc(vm); - vm_paddr_t paddr = __vm_phy_pages_alloc(vm, pages, - KVM_UTIL_MIN_PFN * vm->page_size, - vm->memslots[type], protected); + gpa_t gpa = __vm_phy_pages_alloc(vm, pages, + KVM_UTIL_MIN_PFN * vm->page_size, + vm->memslots[type], protected); /* * Find an unused range of virtual page addresses of at least * pages in length. */ - vm_vaddr_t vaddr_start = vm_vaddr_unused_gap(vm, sz, vaddr_min); + gva_t gva_start = vm_unused_gva_gap(vm, sz, min_gva); /* Map the virtual pages. */ - for (vm_vaddr_t vaddr = vaddr_start; pages > 0; - pages--, vaddr += vm->page_size, paddr += vm->page_size) { + for (gva_t gva = gva_start; pages > 0; + pages--, gva += vm->page_size, gpa += vm->page_size) { - virt_pg_map(vm, vaddr, paddr); + virt_pg_map(vm, gva, gpa); } - return vaddr_start; + return gva_start; } -vm_vaddr_t __vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min, - enum kvm_mem_region_type type) +gva_t __vm_alloc(struct kvm_vm *vm, size_t sz, gva_t min_gva, + enum kvm_mem_region_type type) { - return ____vm_vaddr_alloc(vm, sz, vaddr_min, type, - vm_arch_has_protected_memory(vm)); + return ____vm_alloc(vm, sz, min_gva, type, + vm_arch_has_protected_memory(vm)); } -vm_vaddr_t vm_vaddr_alloc_shared(struct kvm_vm *vm, size_t sz, - vm_vaddr_t vaddr_min, - enum kvm_mem_region_type type) +gva_t vm_alloc_shared(struct kvm_vm *vm, size_t sz, gva_t min_gva, + enum kvm_mem_region_type type) { - return ____vm_vaddr_alloc(vm, sz, vaddr_min, type, false); + return ____vm_alloc(vm, sz, min_gva, type, false); } /* - * VM Virtual Address Allocate - * - * Input Args: - * vm - Virtual Machine - * sz - Size in bytes - * vaddr_min - Minimum starting virtual address - * - * Output Args: None - * - * Return: - * Starting guest virtual address - * - * Allocates at least sz bytes within the virtual address space of the vm - * given by vm. The allocated bytes are mapped to a virtual address >= - * the address given by vaddr_min. Note that each allocation uses a - * a unique set of pages, with the minimum real allocation being at least - * a page. The allocated physical space comes from the TEST_DATA memory region. + * Allocates at least sz bytes within the virtual address space of the VM + * given by @vm. The allocated bytes are mapped to a virtual address >= the + * address given by @min_gva. Note that each allocation uses a a unique set + * of pages, with the minimum real allocation being at least a page. The + * allocated physical space comes from the TEST_DATA memory region. */ -vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min) +gva_t vm_alloc(struct kvm_vm *vm, size_t sz, gva_t min_gva) { - return __vm_vaddr_alloc(vm, sz, vaddr_min, MEM_REGION_TEST_DATA); + return __vm_alloc(vm, sz, min_gva, MEM_REGION_TEST_DATA); +} + +gva_t vm_alloc_pages(struct kvm_vm *vm, int nr_pages) +{ + return vm_alloc(vm, nr_pages * getpagesize(), KVM_UTIL_MIN_VADDR); +} + +gva_t __vm_alloc_page(struct kvm_vm *vm, enum kvm_mem_region_type type) +{ + return __vm_alloc(vm, getpagesize(), KVM_UTIL_MIN_VADDR, type); +} + +gva_t vm_alloc_page(struct kvm_vm *vm) +{ + return vm_alloc_pages(vm, 1); } /* - * VM Virtual Address Allocate Pages + * Map a range of VM virtual address to the VM's physical address. * - * Input Args: - * vm - Virtual Machine - * - * Output Args: None - * - * Return: - * Starting guest virtual address - * - * Allocates at least N system pages worth of bytes within the virtual address - * space of the vm. + * Within the VM given by @vm, creates a virtual translation for @npages + * starting at @gva to the page range starting at @gpa. */ -vm_vaddr_t vm_vaddr_alloc_pages(struct kvm_vm *vm, int nr_pages) -{ - return vm_vaddr_alloc(vm, nr_pages * getpagesize(), KVM_UTIL_MIN_VADDR); -} - -vm_vaddr_t __vm_vaddr_alloc_page(struct kvm_vm *vm, enum kvm_mem_region_type type) -{ - return __vm_vaddr_alloc(vm, getpagesize(), KVM_UTIL_MIN_VADDR, type); -} - -/* - * VM Virtual Address Allocate Page - * - * Input Args: - * vm - Virtual Machine - * - * Output Args: None - * - * Return: - * Starting guest virtual address - * - * Allocates at least one system page worth of bytes within the virtual address - * space of the vm. - */ -vm_vaddr_t vm_vaddr_alloc_page(struct kvm_vm *vm) -{ - return vm_vaddr_alloc_pages(vm, 1); -} - -/* - * Map a range of VM virtual address to the VM's physical address - * - * Input Args: - * vm - Virtual Machine - * vaddr - Virtuall address to map - * paddr - VM Physical Address - * npages - The number of pages to map - * - * Output Args: None - * - * Return: None - * - * Within the VM given by @vm, creates a virtual translation for - * @npages starting at @vaddr to the page range starting at @paddr. - */ -void virt_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, - unsigned int npages) +void virt_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa, unsigned int npages) { size_t page_size = vm->page_size; size_t size = npages * page_size; - TEST_ASSERT(vaddr + size > vaddr, "Vaddr overflow"); - TEST_ASSERT(paddr + size > paddr, "Paddr overflow"); + TEST_ASSERT(gva + size > gva, "Vaddr overflow"); + TEST_ASSERT(gpa + size > gpa, "Paddr overflow"); while (npages--) { - virt_pg_map(vm, vaddr, paddr); + virt_pg_map(vm, gva, gpa); - vaddr += page_size; - paddr += page_size; + gva += page_size; + gpa += page_size; } } @@ -1618,7 +1541,7 @@ void virt_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, * address providing the memory to the vm physical address is returned. * A TEST_ASSERT failure occurs if no region containing gpa exists. */ -void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa) +void *addr_gpa2hva(struct kvm_vm *vm, gpa_t gpa) { struct userspace_mem_region *region; @@ -1651,7 +1574,7 @@ void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa) * VM physical address is returned. A TEST_ASSERT failure occurs if no * region containing hva exists. */ -vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva) +gpa_t addr_hva2gpa(struct kvm_vm *vm, void *hva) { struct rb_node *node; @@ -1662,7 +1585,7 @@ vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva) if (hva >= region->host_mem) { if (hva <= (region->host_mem + region->region.memory_size - 1)) - return (vm_paddr_t)((uintptr_t) + return (gpa_t)((uintptr_t) region->region.guest_phys_addr + (hva - (uintptr_t)region->host_mem)); @@ -1694,7 +1617,7 @@ vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva) * memory without mapping said memory in the guest's address space. And, for * userfaultfd-based demand paging, to do so without triggering userfaults. */ -void *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa) +void *addr_gpa2alias(struct kvm_vm *vm, gpa_t gpa) { struct userspace_mem_region *region; uintptr_t offset; @@ -1788,8 +1711,8 @@ struct kvm_reg_list *vcpu_get_reg_list(struct kvm_vcpu *vcpu) void *vcpu_map_dirty_ring(struct kvm_vcpu *vcpu) { - uint32_t page_size = getpagesize(); - uint32_t size = vcpu->vm->dirty_ring_size; + u32 page_size = getpagesize(); + u32 size = vcpu->vm->dirty_ring_size; TEST_ASSERT(size > 0, "Should enable dirty ring first"); @@ -1818,7 +1741,7 @@ void *vcpu_map_dirty_ring(struct kvm_vcpu *vcpu) * Device Ioctl */ -int __kvm_has_device_attr(int dev_fd, uint32_t group, uint64_t attr) +int __kvm_has_device_attr(int dev_fd, u32 group, u64 attr) { struct kvm_device_attr attribute = { .group = group, @@ -1829,7 +1752,7 @@ int __kvm_has_device_attr(int dev_fd, uint32_t group, uint64_t attr) return ioctl(dev_fd, KVM_HAS_DEVICE_ATTR, &attribute); } -int __kvm_test_create_device(struct kvm_vm *vm, uint64_t type) +int __kvm_test_create_device(struct kvm_vm *vm, u64 type) { struct kvm_create_device create_dev = { .type = type, @@ -1839,7 +1762,7 @@ int __kvm_test_create_device(struct kvm_vm *vm, uint64_t type) return __vm_ioctl(vm, KVM_CREATE_DEVICE, &create_dev); } -int __kvm_create_device(struct kvm_vm *vm, uint64_t type) +int __kvm_create_device(struct kvm_vm *vm, u64 type) { struct kvm_create_device create_dev = { .type = type, @@ -1853,7 +1776,7 @@ int __kvm_create_device(struct kvm_vm *vm, uint64_t type) return err ? : create_dev.fd; } -int __kvm_device_attr_get(int dev_fd, uint32_t group, uint64_t attr, void *val) +int __kvm_device_attr_get(int dev_fd, u32 group, u64 attr, void *val) { struct kvm_device_attr kvmattr = { .group = group, @@ -1865,7 +1788,7 @@ int __kvm_device_attr_get(int dev_fd, uint32_t group, uint64_t attr, void *val) return __kvm_ioctl(dev_fd, KVM_GET_DEVICE_ATTR, &kvmattr); } -int __kvm_device_attr_set(int dev_fd, uint32_t group, uint64_t attr, void *val) +int __kvm_device_attr_set(int dev_fd, u32 group, u64 attr, void *val) { struct kvm_device_attr kvmattr = { .group = group, @@ -1881,7 +1804,7 @@ int __kvm_device_attr_set(int dev_fd, uint32_t group, uint64_t attr, void *val) * IRQ related functions. */ -int _kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level) +int _kvm_irq_line(struct kvm_vm *vm, u32 irq, int level) { struct kvm_irq_level irq_level = { .irq = irq, @@ -1891,7 +1814,7 @@ int _kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level) return __vm_ioctl(vm, KVM_IRQ_LINE, &irq_level); } -void kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level) +void kvm_irq_line(struct kvm_vm *vm, u32 irq, int level) { int ret = _kvm_irq_line(vm, irq, level); @@ -1913,7 +1836,7 @@ struct kvm_irq_routing *kvm_gsi_routing_create(void) } void kvm_gsi_routing_irqchip_add(struct kvm_irq_routing *routing, - uint32_t gsi, uint32_t pin) + u32 gsi, u32 pin) { int i; @@ -1963,7 +1886,7 @@ void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing) * Dumps the current state of the VM given by vm, to the FILE stream * given by stream. */ -void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) +void vm_dump(FILE *stream, struct kvm_vm *vm, u8 indent) { int ctr; struct userspace_mem_region *region; @@ -1976,8 +1899,8 @@ void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) hash_for_each(vm->regions.slot_hash, ctr, region, slot_node) { fprintf(stream, "%*sguest_phys: 0x%lx size: 0x%lx " "host_virt: %p\n", indent + 2, "", - (uint64_t) region->region.guest_phys_addr, - (uint64_t) region->region.memory_size, + (u64)region->region.guest_phys_addr, + (u64)region->region.memory_size, region->host_mem); fprintf(stream, "%*sunused_phy_pages: ", indent + 2, ""); sparsebit_dump(stream, region->unused_phy_pages, 0); @@ -2084,7 +2007,7 @@ const char *exit_reason_str(unsigned int exit_reason) * Input Args: * vm - Virtual Machine * num - number of pages - * paddr_min - Physical address minimum + * min_gpa - Physical address minimum * memslot - Memory region to allocate page from * protected - True if the pages will be used as protected/private memory * @@ -2094,29 +2017,29 @@ const char *exit_reason_str(unsigned int exit_reason) * Starting physical address * * Within the VM specified by vm, locates a range of available physical - * pages at or above paddr_min. If found, the pages are marked as in use + * pages at or above min_gpa. If found, the pages are marked as in use * and their base address is returned. A TEST_ASSERT failure occurs if - * not enough pages are available at or above paddr_min. + * not enough pages are available at or above min_gpa. */ -vm_paddr_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, - vm_paddr_t paddr_min, uint32_t memslot, - bool protected) +gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, + gpa_t min_gpa, u32 memslot, + bool protected) { struct userspace_mem_region *region; sparsebit_idx_t pg, base; TEST_ASSERT(num > 0, "Must allocate at least one page"); - TEST_ASSERT((paddr_min % vm->page_size) == 0, "Min physical address " + TEST_ASSERT((min_gpa % vm->page_size) == 0, "Min physical address " "not divisible by page size.\n" - " paddr_min: 0x%lx page_size: 0x%x", - paddr_min, vm->page_size); + " min_gpa: 0x%lx page_size: 0x%x", + min_gpa, vm->page_size); region = memslot2region(vm, memslot); TEST_ASSERT(!protected || region->protected_phy_pages, "Region doesn't support protected memory"); - base = pg = paddr_min >> vm->page_shift; + base = pg = min_gpa >> vm->page_shift; do { for (; pg < base + num; ++pg) { if (!sparsebit_is_set(region->unused_phy_pages, pg)) { @@ -2128,8 +2051,8 @@ vm_paddr_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, if (pg == 0) { fprintf(stderr, "No guest physical page available, " - "paddr_min: 0x%lx page_size: 0x%x memslot: %u\n", - paddr_min, vm->page_size, memslot); + "min_gpa: 0x%lx page_size: 0x%x memslot: %u\n", + min_gpa, vm->page_size, memslot); fputs("---- vm dump ----\n", stderr); vm_dump(stderr, vm, 2); abort(); @@ -2144,13 +2067,12 @@ vm_paddr_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, return base * vm->page_size; } -vm_paddr_t vm_phy_page_alloc(struct kvm_vm *vm, vm_paddr_t paddr_min, - uint32_t memslot) +gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, u32 memslot) { - return vm_phy_pages_alloc(vm, 1, paddr_min, memslot); + return vm_phy_pages_alloc(vm, 1, min_gpa, memslot); } -vm_paddr_t vm_alloc_page_table(struct kvm_vm *vm) +gpa_t vm_alloc_page_table(struct kvm_vm *vm) { return vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR, vm->memslots[MEM_REGION_PT]); @@ -2168,7 +2090,7 @@ vm_paddr_t vm_alloc_page_table(struct kvm_vm *vm) * Return: * Equivalent host virtual address */ -void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva) +void *addr_gva2hva(struct kvm_vm *vm, gva_t gva) { return addr_gpa2hva(vm, addr_gva2gpa(vm, gva)); } @@ -2266,7 +2188,7 @@ struct kvm_stats_desc *read_stats_descriptors(int stats_fd, * Read the data values of a specified stat from the binary stats interface. */ void read_stat_data(int stats_fd, struct kvm_stats_header *header, - struct kvm_stats_desc *desc, uint64_t *data, + struct kvm_stats_desc *desc, u64 *data, size_t max_elements) { size_t nr_elements = min_t(ssize_t, desc->size, max_elements); @@ -2287,7 +2209,7 @@ void read_stat_data(int stats_fd, struct kvm_stats_header *header, } void kvm_get_stat(struct kvm_binary_stats *stats, const char *name, - uint64_t *data, size_t max_elements) + u64 *data, size_t max_elements) { struct kvm_stats_desc *desc; size_t size_desc; @@ -2364,7 +2286,7 @@ void __attribute((constructor)) kvm_selftest_init(void) kvm_selftest_arch_init(); } -bool vm_is_gpa_protected(struct kvm_vm *vm, vm_paddr_t paddr) +bool vm_is_gpa_protected(struct kvm_vm *vm, gpa_t gpa) { sparsebit_idx_t pg = 0; struct userspace_mem_region *region; @@ -2372,10 +2294,10 @@ bool vm_is_gpa_protected(struct kvm_vm *vm, vm_paddr_t paddr) if (!vm_arch_has_protected_memory(vm)) return false; - region = userspace_mem_region_find(vm, paddr, paddr); - TEST_ASSERT(region, "No vm physical memory at 0x%lx", paddr); + region = userspace_mem_region_find(vm, gpa, gpa); + TEST_ASSERT(region, "No vm physical memory at 0x%lx", gpa); - pg = paddr >> vm->page_shift; + pg = gpa >> vm->page_shift; return sparsebit_is_set(region->protected_phy_pages, pg); } diff --git a/tools/testing/selftests/kvm/lib/loongarch/processor.c b/tools/testing/selftests/kvm/lib/loongarch/processor.c index 17aa55a2047a..64d91fb76522 100644 --- a/tools/testing/selftests/kvm/lib/loongarch/processor.c +++ b/tools/testing/selftests/kvm/lib/loongarch/processor.c @@ -5,38 +5,39 @@ #include #include "kvm_util.h" +#include "pmu.h" #include "processor.h" #include "ucall_common.h" #define LOONGARCH_PAGE_TABLE_PHYS_MIN 0x200000 #define LOONGARCH_GUEST_STACK_VADDR_MIN 0x200000 -static vm_paddr_t invalid_pgtable[4]; -static vm_vaddr_t exception_handlers; +static gpa_t invalid_pgtable[4]; +static gva_t exception_handlers; -static uint64_t virt_pte_index(struct kvm_vm *vm, vm_vaddr_t gva, int level) +static u64 virt_pte_index(struct kvm_vm *vm, gva_t gva, int level) { unsigned int shift; - uint64_t mask; + u64 mask; shift = level * (vm->page_shift - 3) + vm->page_shift; mask = (1UL << (vm->page_shift - 3)) - 1; return (gva >> shift) & mask; } -static uint64_t pte_addr(struct kvm_vm *vm, uint64_t entry) +static u64 pte_addr(struct kvm_vm *vm, u64 entry) { return entry & ~((0x1UL << vm->page_shift) - 1); } -static uint64_t ptrs_per_pte(struct kvm_vm *vm) +static u64 ptrs_per_pte(struct kvm_vm *vm) { return 1 << (vm->page_shift - 3); } -static void virt_set_pgtable(struct kvm_vm *vm, vm_paddr_t table, vm_paddr_t child) +static void virt_set_pgtable(struct kvm_vm *vm, gpa_t table, gpa_t child) { - uint64_t *ptep; + u64 *ptep; int i, ptrs_per_pte; ptep = addr_gpa2hva(vm, table); @@ -48,7 +49,7 @@ static void virt_set_pgtable(struct kvm_vm *vm, vm_paddr_t table, vm_paddr_t chi void virt_arch_pgd_alloc(struct kvm_vm *vm) { int i; - vm_paddr_t child, table; + gpa_t child, table; if (vm->mmu.pgd_created) return; @@ -66,16 +67,16 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) vm->mmu.pgd_created = true; } -static int virt_pte_none(uint64_t *ptep, int level) +static int virt_pte_none(u64 *ptep, int level) { return *ptep == invalid_pgtable[level]; } -static uint64_t *virt_populate_pte(struct kvm_vm *vm, vm_vaddr_t gva, int alloc) +static u64 *virt_populate_pte(struct kvm_vm *vm, gva_t gva, int alloc) { int level; - uint64_t *ptep; - vm_paddr_t child; + u64 *ptep; + gpa_t child; if (!vm->mmu.pgd_created) goto unmapped_gva; @@ -105,43 +106,42 @@ unmapped_gva: exit(EXIT_FAILURE); } -vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) +gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva) { - uint64_t *ptep; + u64 *ptep; ptep = virt_populate_pte(vm, gva, 0); - TEST_ASSERT(*ptep != 0, "Virtual address vaddr: 0x%lx not mapped\n", gva); + TEST_ASSERT(*ptep != 0, "Virtual address gva: 0x%lx not mapped\n", gva); return pte_addr(vm, *ptep) + (gva & (vm->page_size - 1)); } -void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) +void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa) { - uint32_t prot_bits; - uint64_t *ptep; + u32 prot_bits; + u64 *ptep; - TEST_ASSERT((vaddr % vm->page_size) == 0, + TEST_ASSERT((gva % vm->page_size) == 0, "Virtual address not on page boundary,\n" - "vaddr: 0x%lx vm->page_size: 0x%x", vaddr, vm->page_size); - TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, - (vaddr >> vm->page_shift)), - "Invalid virtual address, vaddr: 0x%lx", vaddr); - TEST_ASSERT((paddr % vm->page_size) == 0, + "gva: 0x%lx vm->page_size: 0x%x", gva, vm->page_size); + TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, (gva >> vm->page_shift)), + "Invalid virtual address, gva: 0x%lx", gva); + TEST_ASSERT((gpa % vm->page_size) == 0, "Physical address not on page boundary,\n" - "paddr: 0x%lx vm->page_size: 0x%x", paddr, vm->page_size); - TEST_ASSERT((paddr >> vm->page_shift) <= vm->max_gfn, + "gpa: 0x%lx vm->page_size: 0x%x", gpa, vm->page_size); + TEST_ASSERT((gpa >> vm->page_shift) <= vm->max_gfn, "Physical address beyond maximum supported,\n" - "paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", - paddr, vm->max_gfn, vm->page_size); + "gpa: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", + gpa, vm->max_gfn, vm->page_size); - ptep = virt_populate_pte(vm, vaddr, 1); + ptep = virt_populate_pte(vm, gva, 1); prot_bits = _PAGE_PRESENT | __READABLE | __WRITEABLE | _CACHE_CC | _PAGE_USER; - WRITE_ONCE(*ptep, paddr | prot_bits); + WRITE_ONCE(*ptep, gpa | prot_bits); } -static void pte_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent, uint64_t page, int level) +static void pte_dump(FILE *stream, struct kvm_vm *vm, u8 indent, u64 page, int level) { - uint64_t pte, *ptep; + u64 pte, *ptep; static const char * const type[] = { "pte", "pmd", "pud", "pgd"}; if (level < 0) @@ -157,7 +157,7 @@ static void pte_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent, uint64_t p } } -void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) +void virt_arch_dump(FILE *stream, struct kvm_vm *vm, u8 indent) { int level; @@ -168,7 +168,7 @@ void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) pte_dump(stream, vm, indent, vm->mmu.pgd, level); } -void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent) +void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent) { } @@ -205,8 +205,9 @@ void vm_init_descriptor_tables(struct kvm_vm *vm) { void *addr; - vm->handlers = __vm_vaddr_alloc(vm, sizeof(struct handlers), - LOONGARCH_GUEST_STACK_VADDR_MIN, MEM_REGION_DATA); + vm->handlers = __vm_alloc(vm, sizeof(struct handlers), + LOONGARCH_GUEST_STACK_VADDR_MIN, + MEM_REGION_DATA); addr = addr_gva2hva(vm, vm->handlers); memset(addr, 0, vm->page_size); @@ -222,7 +223,7 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, handler_fn hand handlers->exception_handlers[vector] = handler; } -uint32_t guest_get_vcpuid(void) +u32 guest_get_vcpuid(void) { return csr_read(LOONGARCH_CSR_CPUID); } @@ -240,36 +241,45 @@ void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...) va_start(ap, num); for (i = 0; i < num; i++) - regs.gpr[i + 4] = va_arg(ap, uint64_t); + regs.gpr[i + 4] = va_arg(ap, u64); va_end(ap); vcpu_regs_set(vcpu, ®s); } -static void loongarch_set_reg(struct kvm_vcpu *vcpu, uint64_t id, uint64_t val) +static void loongarch_set_reg(struct kvm_vcpu *vcpu, u64 id, u64 val) { __vcpu_set_reg(vcpu, id, val); } -static void loongarch_get_csr(struct kvm_vcpu *vcpu, uint64_t id, void *addr) +static void loongarch_set_cpucfg(struct kvm_vcpu *vcpu, u64 id, u64 val) { - uint64_t csrid; + u64 cfgid; + + cfgid = KVM_REG_LOONGARCH_CPUCFG | KVM_REG_SIZE_U64 | 8 * id; + __vcpu_set_reg(vcpu, cfgid, val); +} + +static void loongarch_get_csr(struct kvm_vcpu *vcpu, u64 id, void *addr) +{ + u64 csrid; csrid = KVM_REG_LOONGARCH_CSR | KVM_REG_SIZE_U64 | 8 * id; __vcpu_get_reg(vcpu, csrid, addr); } -static void loongarch_set_csr(struct kvm_vcpu *vcpu, uint64_t id, uint64_t val) +static void loongarch_set_csr(struct kvm_vcpu *vcpu, u64 id, u64 val) { - uint64_t csrid; + u64 csrid; csrid = KVM_REG_LOONGARCH_CSR | KVM_REG_SIZE_U64 | 8 * id; __vcpu_set_reg(vcpu, csrid, val); } -static void loongarch_vcpu_setup(struct kvm_vcpu *vcpu) +void loongarch_vcpu_setup(struct kvm_vcpu *vcpu) { int width; + unsigned int cfg; unsigned long val; struct kvm_vm *vm = vcpu->vm; @@ -282,6 +292,9 @@ static void loongarch_vcpu_setup(struct kvm_vcpu *vcpu) TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode); } + cfg = read_cpucfg(LOONGARCH_CPUCFG6); + loongarch_set_cpucfg(vcpu, LOONGARCH_CPUCFG6, cfg); + /* kernel mode and page enable mode */ val = PLV_KERN | CSR_CRMD_PG; loongarch_set_csr(vcpu, LOONGARCH_CSR_CRMD, val); @@ -341,8 +354,8 @@ static void loongarch_vcpu_setup(struct kvm_vcpu *vcpu) loongarch_set_csr(vcpu, LOONGARCH_CSR_STLBPGSIZE, PS_DEFAULT_SIZE); /* LOONGARCH_CSR_KS1 is used for exception stack */ - val = __vm_vaddr_alloc(vm, vm->page_size, - LOONGARCH_GUEST_STACK_VADDR_MIN, MEM_REGION_DATA); + val = __vm_alloc(vm, vm->page_size, LOONGARCH_GUEST_STACK_VADDR_MIN, + MEM_REGION_DATA); TEST_ASSERT(val != 0, "No memory for exception stack"); val = val + vm->page_size; loongarch_set_csr(vcpu, LOONGARCH_CSR_KS1, val); @@ -356,23 +369,23 @@ static void loongarch_vcpu_setup(struct kvm_vcpu *vcpu) loongarch_set_csr(vcpu, LOONGARCH_CSR_TMID, vcpu->id); } -struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) +struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id) { size_t stack_size; - uint64_t stack_vaddr; + u64 stack_gva; struct kvm_regs regs; struct kvm_vcpu *vcpu; vcpu = __vm_vcpu_add(vm, vcpu_id); stack_size = vm->page_size; - stack_vaddr = __vm_vaddr_alloc(vm, stack_size, - LOONGARCH_GUEST_STACK_VADDR_MIN, MEM_REGION_DATA); - TEST_ASSERT(stack_vaddr != 0, "No memory for vm stack"); + stack_gva = __vm_alloc(vm, stack_size, + LOONGARCH_GUEST_STACK_VADDR_MIN, MEM_REGION_DATA); + TEST_ASSERT(stack_gva != 0, "No memory for vm stack"); loongarch_vcpu_setup(vcpu); /* Setup guest general purpose registers */ vcpu_regs_get(vcpu, ®s); - regs.gpr[3] = stack_vaddr + stack_size; + regs.gpr[3] = stack_gva + stack_size; vcpu_regs_set(vcpu, ®s); return vcpu; @@ -384,6 +397,6 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code) /* Setup guest PC register */ vcpu_regs_get(vcpu, ®s); - regs.pc = (uint64_t)guest_code; + regs.pc = (u64)guest_code; vcpu_regs_set(vcpu, ®s); } diff --git a/tools/testing/selftests/kvm/lib/loongarch/ucall.c b/tools/testing/selftests/kvm/lib/loongarch/ucall.c index fc6cbb50573f..cd49a3440ead 100644 --- a/tools/testing/selftests/kvm/lib/loongarch/ucall.c +++ b/tools/testing/selftests/kvm/lib/loongarch/ucall.c @@ -9,17 +9,17 @@ * ucall_exit_mmio_addr holds per-VM values (global data is duplicated by each * VM), it must not be accessed from host code. */ -vm_vaddr_t *ucall_exit_mmio_addr; +gva_t *ucall_exit_mmio_addr; -void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) +void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa) { - vm_vaddr_t mmio_gva = vm_vaddr_unused_gap(vm, vm->page_size, KVM_UTIL_MIN_VADDR); + gva_t mmio_gva = vm_unused_gva_gap(vm, vm->page_size, KVM_UTIL_MIN_VADDR); virt_map(vm, mmio_gva, mmio_gpa, 1); vm->ucall_mmio_addr = mmio_gpa; - write_guest_global(vm, ucall_exit_mmio_addr, (vm_vaddr_t *)mmio_gva); + write_guest_global(vm, ucall_exit_mmio_addr, (gva_t *)mmio_gva); } void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) @@ -28,10 +28,10 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) if (run->exit_reason == KVM_EXIT_MMIO && run->mmio.phys_addr == vcpu->vm->ucall_mmio_addr) { - TEST_ASSERT(run->mmio.is_write && run->mmio.len == sizeof(uint64_t), + TEST_ASSERT(run->mmio.is_write && run->mmio.len == sizeof(u64), "Unexpected ucall exit mmio address access"); - return (void *)(*((uint64_t *)run->mmio.data)); + return (void *)(*((u64 *)run->mmio.data)); } return NULL; diff --git a/tools/testing/selftests/kvm/lib/memstress.c b/tools/testing/selftests/kvm/lib/memstress.c index 557c0a0a5658..6dcd15910a06 100644 --- a/tools/testing/selftests/kvm/lib/memstress.c +++ b/tools/testing/selftests/kvm/lib/memstress.c @@ -16,7 +16,7 @@ struct memstress_args memstress_args; * Guest virtual memory offset of the testing memory slot. * Must not conflict with identity mapped test code. */ -static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; +static u64 guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; struct vcpu_thread { /* The index of the vCPU. */ @@ -44,15 +44,15 @@ static struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; * Continuously write to the first 8 bytes of each page in the * specified region. */ -void memstress_guest_code(uint32_t vcpu_idx) +void memstress_guest_code(u32 vcpu_idx) { struct memstress_args *args = &memstress_args; struct memstress_vcpu_args *vcpu_args = &args->vcpu_args[vcpu_idx]; struct guest_random_state rand_state; - uint64_t gva; - uint64_t pages; - uint64_t addr; - uint64_t page; + gva_t gva; + u64 pages; + u64 addr; + u64 page; int i; rand_state = new_guest_random_state(guest_random_seed + vcpu_idx); @@ -76,9 +76,9 @@ void memstress_guest_code(uint32_t vcpu_idx) addr = gva + (page * args->guest_page_size); if (__guest_random_bool(&rand_state, args->write_percent)) - *(uint64_t *)addr = 0x0123456789ABCDEF; + *(u64 *)addr = 0x0123456789ABCDEF; else - READ_ONCE(*(uint64_t *)addr); + READ_ONCE(*(u64 *)addr); } GUEST_SYNC(1); @@ -87,7 +87,7 @@ void memstress_guest_code(uint32_t vcpu_idx) void memstress_setup_vcpus(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[], - uint64_t vcpu_memory_bytes, + u64 vcpu_memory_bytes, bool partition_vcpu_memory_access) { struct memstress_args *args = &memstress_args; @@ -122,15 +122,15 @@ void memstress_setup_vcpus(struct kvm_vm *vm, int nr_vcpus, } struct kvm_vm *memstress_create_vm(enum vm_guest_mode mode, int nr_vcpus, - uint64_t vcpu_memory_bytes, int slots, + u64 vcpu_memory_bytes, int slots, enum vm_mem_backing_src_type backing_src, bool partition_vcpu_memory_access) { struct memstress_args *args = &memstress_args; struct kvm_vm *vm; - uint64_t guest_num_pages, slot0_pages = 0; - uint64_t backing_src_pagesz = get_backing_src_pagesz(backing_src); - uint64_t region_end_gfn; + u64 guest_num_pages, slot0_pages = 0; + u64 backing_src_pagesz = get_backing_src_pagesz(backing_src); + u64 region_end_gfn; int i; pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode)); @@ -196,18 +196,14 @@ struct kvm_vm *memstress_create_vm(enum vm_guest_mode mode, int nr_vcpus, args->gpa = (region_end_gfn - guest_num_pages - 1) * args->guest_page_size; args->gpa = align_down(args->gpa, backing_src_pagesz); -#ifdef __s390x__ - /* Align to 1M (segment size) */ - args->gpa = align_down(args->gpa, 1 << 20); -#endif args->size = guest_num_pages * args->guest_page_size; pr_info("guest physical test memory: [0x%lx, 0x%lx)\n", args->gpa, args->gpa + args->size); /* Add extra memory slots for testing */ for (i = 0; i < slots; i++) { - uint64_t region_pages = guest_num_pages / slots; - vm_paddr_t region_start = args->gpa + region_pages * args->guest_page_size * i; + u64 region_pages = guest_num_pages / slots; + gpa_t region_start = args->gpa + region_pages * args->guest_page_size * i; vm_userspace_mem_region_add(vm, backing_src, region_start, MEMSTRESS_MEM_SLOT_INDEX + i, @@ -236,7 +232,7 @@ void memstress_destroy_vm(struct kvm_vm *vm) kvm_vm_free(vm); } -void memstress_set_write_percent(struct kvm_vm *vm, uint32_t write_percent) +void memstress_set_write_percent(struct kvm_vm *vm, u32 write_percent) { memstress_args.write_percent = write_percent; sync_global_to_guest(vm, memstress_args.write_percent); @@ -248,7 +244,7 @@ void memstress_set_random_access(struct kvm_vm *vm, bool random_access) sync_global_to_guest(vm, memstress_args.random_access); } -uint64_t __weak memstress_nested_pages(int nr_vcpus) +u64 __weak memstress_nested_pages(int nr_vcpus) { return 0; } @@ -353,7 +349,7 @@ void memstress_get_dirty_log(struct kvm_vm *vm, unsigned long *bitmaps[], int sl } void memstress_clear_dirty_log(struct kvm_vm *vm, unsigned long *bitmaps[], - int slots, uint64_t pages_per_slot) + int slots, u64 pages_per_slot) { int i; @@ -364,7 +360,7 @@ void memstress_clear_dirty_log(struct kvm_vm *vm, unsigned long *bitmaps[], } } -unsigned long **memstress_alloc_bitmaps(int slots, uint64_t pages_per_slot) +unsigned long **memstress_alloc_bitmaps(int slots, u64 pages_per_slot) { unsigned long **bitmaps; int i; diff --git a/tools/testing/selftests/kvm/lib/riscv/processor.c b/tools/testing/selftests/kvm/lib/riscv/processor.c index 51dd455ff52c..ded5429f3448 100644 --- a/tools/testing/selftests/kvm/lib/riscv/processor.c +++ b/tools/testing/selftests/kvm/lib/riscv/processor.c @@ -15,9 +15,9 @@ #define DEFAULT_RISCV_GUEST_STACK_VADDR_MIN 0xac0000 -static vm_vaddr_t exception_handlers; +static gva_t exception_handlers; -bool __vcpu_has_ext(struct kvm_vcpu *vcpu, uint64_t ext) +bool __vcpu_has_ext(struct kvm_vcpu *vcpu, u64 ext) { unsigned long value = 0; int ret; @@ -27,32 +27,32 @@ bool __vcpu_has_ext(struct kvm_vcpu *vcpu, uint64_t ext) return !ret && !!value; } -static uint64_t pte_addr(struct kvm_vm *vm, uint64_t entry) +static u64 pte_addr(struct kvm_vm *vm, u64 entry) { return ((entry & PGTBL_PTE_ADDR_MASK) >> PGTBL_PTE_ADDR_SHIFT) << PGTBL_PAGE_SIZE_SHIFT; } -static uint64_t ptrs_per_pte(struct kvm_vm *vm) +static u64 ptrs_per_pte(struct kvm_vm *vm) { - return PGTBL_PAGE_SIZE / sizeof(uint64_t); + return PGTBL_PAGE_SIZE / sizeof(u64); } -static uint64_t pte_index_mask[] = { +static u64 pte_index_mask[] = { PGTBL_L0_INDEX_MASK, PGTBL_L1_INDEX_MASK, PGTBL_L2_INDEX_MASK, PGTBL_L3_INDEX_MASK, }; -static uint32_t pte_index_shift[] = { +static u32 pte_index_shift[] = { PGTBL_L0_INDEX_SHIFT, PGTBL_L1_INDEX_SHIFT, PGTBL_L2_INDEX_SHIFT, PGTBL_L3_INDEX_SHIFT, }; -static uint64_t pte_index(struct kvm_vm *vm, vm_vaddr_t gva, int level) +static u64 pte_index(struct kvm_vm *vm, gva_t gva, int level) { TEST_ASSERT(level > -1, "Negative page table level (%d) not possible", level); @@ -75,26 +75,25 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) vm->mmu.pgd_created = true; } -void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) +void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa) { - uint64_t *ptep, next_ppn; + u64 *ptep, next_ppn; int level = vm->mmu.pgtable_levels - 1; - TEST_ASSERT((vaddr % vm->page_size) == 0, + TEST_ASSERT((gva % vm->page_size) == 0, "Virtual address not on page boundary,\n" - " vaddr: 0x%lx vm->page_size: 0x%x", vaddr, vm->page_size); - TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, - (vaddr >> vm->page_shift)), - "Invalid virtual address, vaddr: 0x%lx", vaddr); - TEST_ASSERT((paddr % vm->page_size) == 0, + " gva: 0x%lx vm->page_size: 0x%x", gva, vm->page_size); + TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, (gva >> vm->page_shift)), + "Invalid virtual address, gva: 0x%lx", gva); + TEST_ASSERT((gpa % vm->page_size) == 0, "Physical address not on page boundary,\n" - " paddr: 0x%lx vm->page_size: 0x%x", paddr, vm->page_size); - TEST_ASSERT((paddr >> vm->page_shift) <= vm->max_gfn, + " gpa: 0x%lx vm->page_size: 0x%x", gpa, vm->page_size); + TEST_ASSERT((gpa >> vm->page_shift) <= vm->max_gfn, "Physical address beyond maximum supported,\n" - " paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", - paddr, vm->max_gfn, vm->page_size); + " gpa: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", + gpa, vm->max_gfn, vm->page_size); - ptep = addr_gpa2hva(vm, vm->mmu.pgd) + pte_index(vm, vaddr, level) * 8; + ptep = addr_gpa2hva(vm, vm->mmu.pgd) + pte_index(vm, gva, level) * 8; if (!*ptep) { next_ppn = vm_alloc_page_table(vm) >> PGTBL_PAGE_SIZE_SHIFT; *ptep = (next_ppn << PGTBL_PTE_ADDR_SHIFT) | @@ -104,7 +103,7 @@ void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) while (level > -1) { ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + - pte_index(vm, vaddr, level) * 8; + pte_index(vm, gva, level) * 8; if (!*ptep && level > 0) { next_ppn = vm_alloc_page_table(vm) >> PGTBL_PAGE_SIZE_SHIFT; @@ -114,14 +113,14 @@ void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) level--; } - paddr = paddr >> PGTBL_PAGE_SIZE_SHIFT; - *ptep = (paddr << PGTBL_PTE_ADDR_SHIFT) | + gpa = gpa >> PGTBL_PAGE_SIZE_SHIFT; + *ptep = (gpa << PGTBL_PTE_ADDR_SHIFT) | PGTBL_PTE_PERM_MASK | PGTBL_PTE_VALID_MASK; } -vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) +gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva) { - uint64_t *ptep; + u64 *ptep; int level = vm->mmu.pgtable_levels - 1; if (!vm->mmu.pgd_created) @@ -148,12 +147,12 @@ unmapped_gva: exit(1); } -static void pte_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent, - uint64_t page, int level) +static void pte_dump(FILE *stream, struct kvm_vm *vm, u8 indent, + u64 page, int level) { #ifdef DEBUG static const char *const type[] = { "pte", "pmd", "pud", "p4d"}; - uint64_t pte, *ptep; + u64 pte, *ptep; if (level < 0) return; @@ -170,11 +169,11 @@ static void pte_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent, #endif } -void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) +void virt_arch_dump(FILE *stream, struct kvm_vm *vm, u8 indent) { struct kvm_mmu *mmu = &vm->mmu; int level = mmu->pgtable_levels - 1; - uint64_t pgd, *ptep; + u64 pgd, *ptep; if (!mmu->pgd_created) return; @@ -233,7 +232,7 @@ void riscv_vcpu_mmu_setup(struct kvm_vcpu *vcpu) vcpu_set_reg(vcpu, RISCV_GENERAL_CSR_REG(satp), satp); } -void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent) +void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent) { struct kvm_riscv_core core; @@ -311,20 +310,20 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code) vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.pc), (unsigned long)guest_code); } -struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) +struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id) { int r; size_t stack_size; - unsigned long stack_vaddr; + unsigned long stack_gva; unsigned long current_gp = 0; struct kvm_mp_state mps; struct kvm_vcpu *vcpu; stack_size = vm->page_size == 4096 ? DEFAULT_STACK_PGS * vm->page_size : vm->page_size; - stack_vaddr = __vm_vaddr_alloc(vm, stack_size, - DEFAULT_RISCV_GUEST_STACK_VADDR_MIN, - MEM_REGION_DATA); + stack_gva = __vm_alloc(vm, stack_size, + DEFAULT_RISCV_GUEST_STACK_VADDR_MIN, + MEM_REGION_DATA); vcpu = __vm_vcpu_add(vm, vcpu_id); riscv_vcpu_mmu_setup(vcpu); @@ -344,7 +343,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.gp), current_gp); /* Setup stack pointer and program counter of guest */ - vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.sp), stack_vaddr + stack_size); + vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.sp), stack_gva + stack_size); /* Setup sscratch for guest_get_vcpuid() */ vcpu_set_reg(vcpu, RISCV_GENERAL_CSR_REG(sscratch), vcpu_id); @@ -358,7 +357,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...) { va_list ap; - uint64_t id = RISCV_CORE_REG(regs.a0); + u64 id = RISCV_CORE_REG(regs.a0); int i; TEST_ASSERT(num >= 1 && num <= 8, "Unsupported number of args,\n" @@ -393,7 +392,7 @@ void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...) id = RISCV_CORE_REG(regs.a7); break; } - vcpu_set_reg(vcpu, id, va_arg(ap, uint64_t)); + vcpu_set_reg(vcpu, id, va_arg(ap, u64)); } va_end(ap); @@ -449,10 +448,10 @@ void vcpu_init_vector_tables(struct kvm_vcpu *vcpu) void vm_init_vector_tables(struct kvm_vm *vm) { - vm->handlers = __vm_vaddr_alloc(vm, sizeof(struct handlers), - vm->page_size, MEM_REGION_DATA); + vm->handlers = __vm_alloc(vm, sizeof(struct handlers), vm->page_size, + MEM_REGION_DATA); - *(vm_vaddr_t *)addr_gva2hva(vm, (vm_vaddr_t)(&exception_handlers)) = vm->handlers; + *(gva_t *)addr_gva2hva(vm, (gva_t)(&exception_handlers)) = vm->handlers; } void vm_install_exception_handler(struct kvm_vm *vm, int vector, exception_handler_fn handler) @@ -470,7 +469,7 @@ void vm_install_interrupt_handler(struct kvm_vm *vm, exception_handler_fn handle handlers->exception_handlers[1][0] = handler; } -uint32_t guest_get_vcpuid(void) +u32 guest_get_vcpuid(void) { return csr_read(CSR_SSCRATCH); } @@ -544,10 +543,10 @@ void kvm_selftest_arch_init(void) unsigned long riscv64_get_satp_mode(void) { int kvm_fd, vm_fd, vcpu_fd, err; - uint64_t val; + u64 val; struct kvm_one_reg reg = { .id = RISCV_CONFIG_REG(satp_mode), - .addr = (uint64_t)&val, + .addr = (u64)&val, }; kvm_fd = open_kvm_dev_path_or_exit(); @@ -566,3 +565,8 @@ unsigned long riscv64_get_satp_mode(void) return val; } + +bool kvm_arch_has_default_irqchip(void) +{ + return kvm_check_cap(KVM_CAP_IRQCHIP); +} diff --git a/tools/testing/selftests/kvm/lib/s390/diag318_test_handler.c b/tools/testing/selftests/kvm/lib/s390/diag318_test_handler.c index 2c432fa164f1..f5480473f192 100644 --- a/tools/testing/selftests/kvm/lib/s390/diag318_test_handler.c +++ b/tools/testing/selftests/kvm/lib/s390/diag318_test_handler.c @@ -13,7 +13,7 @@ static void guest_code(void) { - uint64_t diag318_info = 0x12345678; + u64 diag318_info = 0x12345678; asm volatile ("diag %0,0,0x318\n" : : "d" (diag318_info)); } @@ -23,13 +23,13 @@ static void guest_code(void) * we create an ad-hoc VM here to handle the instruction then extract the * necessary data. It is up to the caller to decide what to do with that data. */ -static uint64_t diag318_handler(void) +static u64 diag318_handler(void) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_run *run; - uint64_t reg; - uint64_t diag318_info; + u64 reg; + u64 diag318_info; vm = vm_create_with_one_vcpu(&vcpu, guest_code); vcpu_run(vcpu); @@ -51,9 +51,9 @@ static uint64_t diag318_handler(void) return diag318_info; } -uint64_t get_diag318_info(void) +u64 get_diag318_info(void) { - static uint64_t diag318_info; + static u64 diag318_info; static bool printed_skip; /* diff --git a/tools/testing/selftests/kvm/lib/s390/facility.c b/tools/testing/selftests/kvm/lib/s390/facility.c index d540812d911a..9a778054f07f 100644 --- a/tools/testing/selftests/kvm/lib/s390/facility.c +++ b/tools/testing/selftests/kvm/lib/s390/facility.c @@ -10,5 +10,5 @@ #include "facility.h" -uint64_t stfl_doublewords[NB_STFL_DOUBLEWORDS]; +u64 stfl_doublewords[NB_STFL_DOUBLEWORDS]; bool stfle_flag; diff --git a/tools/testing/selftests/kvm/lib/s390/processor.c b/tools/testing/selftests/kvm/lib/s390/processor.c index 6a9a660413a7..a9adb3782b35 100644 --- a/tools/testing/selftests/kvm/lib/s390/processor.c +++ b/tools/testing/selftests/kvm/lib/s390/processor.c @@ -12,7 +12,7 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) { - vm_paddr_t paddr; + gpa_t gpa; TEST_ASSERT(vm->page_size == PAGE_SIZE, "Unsupported page size: 0x%x", vm->page_size); @@ -20,12 +20,12 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) if (vm->mmu.pgd_created) return; - paddr = vm_phy_pages_alloc(vm, PAGES_PER_REGION, + gpa = vm_phy_pages_alloc(vm, PAGES_PER_REGION, KVM_GUEST_PAGE_TABLE_MIN_PADDR, vm->memslots[MEM_REGION_PT]); - memset(addr_gpa2hva(vm, paddr), 0xff, PAGES_PER_REGION * vm->page_size); + memset(addr_gpa2hva(vm, gpa), 0xff, PAGES_PER_REGION * vm->page_size); - vm->mmu.pgd = paddr; + vm->mmu.pgd = gpa; vm->mmu.pgd_created = true; } @@ -34,9 +34,9 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) * a page table (ri == 4). Returns a suitable region/segment table entry * which points to the freshly allocated pages. */ -static uint64_t virt_alloc_region(struct kvm_vm *vm, int ri) +static u64 virt_alloc_region(struct kvm_vm *vm, int ri) { - uint64_t taddr; + u64 taddr; taddr = vm_phy_pages_alloc(vm, ri < 4 ? PAGES_PER_REGION : 1, KVM_GUEST_PAGE_TABLE_MIN_PADDR, 0); @@ -47,26 +47,24 @@ static uint64_t virt_alloc_region(struct kvm_vm *vm, int ri) | ((ri < 4 ? (PAGES_PER_REGION - 1) : 0) & REGION_ENTRY_LENGTH); } -void virt_arch_pg_map(struct kvm_vm *vm, uint64_t gva, uint64_t gpa) +void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa) { int ri, idx; - uint64_t *entry; + u64 *entry; TEST_ASSERT((gva % vm->page_size) == 0, - "Virtual address not on page boundary,\n" - " vaddr: 0x%lx vm->page_size: 0x%x", - gva, vm->page_size); - TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, - (gva >> vm->page_shift)), - "Invalid virtual address, vaddr: 0x%lx", - gva); + "Virtual address not on page boundary,\n" + " gva: 0x%lx vm->page_size: 0x%x", + gva, vm->page_size); + TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, (gva >> vm->page_shift)), + "Invalid virtual address, gva: 0x%lx", gva); TEST_ASSERT((gpa % vm->page_size) == 0, "Physical address not on page boundary,\n" - " paddr: 0x%lx vm->page_size: 0x%x", + " gpa: 0x%lx vm->page_size: 0x%x", gva, vm->page_size); TEST_ASSERT((gpa >> vm->page_shift) <= vm->max_gfn, "Physical address beyond beyond maximum supported,\n" - " paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", + " gpa: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", gva, vm->max_gfn, vm->page_size); /* Walk through region and segment tables */ @@ -86,10 +84,10 @@ void virt_arch_pg_map(struct kvm_vm *vm, uint64_t gva, uint64_t gpa) entry[idx] = gpa; } -vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) +gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva) { int ri, idx; - uint64_t *entry; + u64 *entry; TEST_ASSERT(vm->page_size == PAGE_SIZE, "Unsupported page size: 0x%x", vm->page_size); @@ -111,10 +109,10 @@ vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) return (entry[idx] & ~0xffful) + (gva & 0xffful); } -static void virt_dump_ptes(FILE *stream, struct kvm_vm *vm, uint8_t indent, - uint64_t ptea_start) +static void virt_dump_ptes(FILE *stream, struct kvm_vm *vm, u8 indent, + u64 ptea_start) { - uint64_t *pte, ptea; + u64 *pte, ptea; for (ptea = ptea_start; ptea < ptea_start + 0x100 * 8; ptea += 8) { pte = addr_gpa2hva(vm, ptea); @@ -125,10 +123,10 @@ static void virt_dump_ptes(FILE *stream, struct kvm_vm *vm, uint8_t indent, } } -static void virt_dump_region(FILE *stream, struct kvm_vm *vm, uint8_t indent, - uint64_t reg_tab_addr) +static void virt_dump_region(FILE *stream, struct kvm_vm *vm, u8 indent, + u64 reg_tab_addr) { - uint64_t addr, *entry; + u64 addr, *entry; for (addr = reg_tab_addr; addr < reg_tab_addr + 0x400 * 8; addr += 8) { entry = addr_gpa2hva(vm, addr); @@ -147,7 +145,7 @@ static void virt_dump_region(FILE *stream, struct kvm_vm *vm, uint8_t indent, } } -void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) +void virt_arch_dump(FILE *stream, struct kvm_vm *vm, u8 indent) { if (!vm->mmu.pgd_created) return; @@ -160,10 +158,10 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code) vcpu->run->psw_addr = (uintptr_t)guest_code; } -struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) +struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id) { size_t stack_size = DEFAULT_STACK_PGS * getpagesize(); - uint64_t stack_vaddr; + u64 stack_gva; struct kvm_regs regs; struct kvm_sregs sregs; struct kvm_vcpu *vcpu; @@ -171,15 +169,14 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) TEST_ASSERT(vm->page_size == PAGE_SIZE, "Unsupported page size: 0x%x", vm->page_size); - stack_vaddr = __vm_vaddr_alloc(vm, stack_size, - DEFAULT_GUEST_STACK_VADDR_MIN, - MEM_REGION_DATA); + stack_gva = __vm_alloc(vm, stack_size, DEFAULT_GUEST_STACK_VADDR_MIN, + MEM_REGION_DATA); vcpu = __vm_vcpu_add(vm, vcpu_id); /* Setup guest registers */ vcpu_regs_get(vcpu, ®s); - regs.gprs[15] = stack_vaddr + (DEFAULT_STACK_PGS * getpagesize()) - 160; + regs.gprs[15] = stack_gva + (DEFAULT_STACK_PGS * getpagesize()) - 160; vcpu_regs_set(vcpu, ®s); vcpu_sregs_get(vcpu, &sregs); @@ -206,13 +203,13 @@ void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...) vcpu_regs_get(vcpu, ®s); for (i = 0; i < num; i++) - regs.gprs[i + 2] = va_arg(ap, uint64_t); + regs.gprs[i + 2] = va_arg(ap, u64); vcpu_regs_set(vcpu, ®s); va_end(ap); } -void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent) +void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent) { fprintf(stream, "%*spstate: psw: 0x%.16llx:0x%.16llx\n", indent, "", vcpu->run->psw_mask, vcpu->run->psw_addr); diff --git a/tools/testing/selftests/kvm/lib/sparsebit.c b/tools/testing/selftests/kvm/lib/sparsebit.c index a99188f87a38..4d845000de15 100644 --- a/tools/testing/selftests/kvm/lib/sparsebit.c +++ b/tools/testing/selftests/kvm/lib/sparsebit.c @@ -76,11 +76,11 @@ * the use of a binary-search tree, where each node contains at least * the following members: * - * typedef uint64_t sparsebit_idx_t; - * typedef uint64_t sparsebit_num_t; + * typedef u64 sparsebit_idx_t; + * typedef u64 sparsebit_num_t; * * sparsebit_idx_t idx; - * uint32_t mask; + * u32 mask; * sparsebit_num_t num_after; * * The idx member contains the bit index of the first bit described by this @@ -162,7 +162,7 @@ #define DUMP_LINE_MAX 100 /* Does not include indent amount */ -typedef uint32_t mask_t; +typedef u32 mask_t; #define MASK_BITS (sizeof(mask_t) * CHAR_BIT) struct node { @@ -2056,9 +2056,9 @@ unsigned char get8(void) return ch; } -uint64_t get64(void) +u64 get64(void) { - uint64_t x; + u64 x; x = get8(); x = (x << 8) | get8(); @@ -2074,9 +2074,9 @@ int main(void) { s = sparsebit_alloc(); for (;;) { - uint8_t op = get8() & 0xf; - uint64_t first = get64(); - uint64_t last = get64(); + u8 op = get8() & 0xf; + u64 first = get64(); + u64 last = get64(); operate(op, first, last); } diff --git a/tools/testing/selftests/kvm/lib/test_util.c b/tools/testing/selftests/kvm/lib/test_util.c index 8a1848586a85..bab1bd2b775b 100644 --- a/tools/testing/selftests/kvm/lib/test_util.c +++ b/tools/testing/selftests/kvm/lib/test_util.c @@ -30,15 +30,15 @@ void __attribute__((used)) expect_sigbus_handler(int signum) * Park-Miller LCG using standard constants. */ -struct guest_random_state new_guest_random_state(uint32_t seed) +struct guest_random_state new_guest_random_state(u32 seed) { struct guest_random_state s = {.seed = seed}; return s; } -uint32_t guest_random_u32(struct guest_random_state *state) +u32 guest_random_u32(struct guest_random_state *state) { - state->seed = (uint64_t)state->seed * 48271 % ((uint32_t)(1 << 31) - 1); + state->seed = (u64)state->seed * 48271 % ((u32)(1 << 31) - 1); return state->seed; } @@ -83,12 +83,12 @@ size_t parse_size(const char *size) return base << shift; } -int64_t timespec_to_ns(struct timespec ts) +s64 timespec_to_ns(struct timespec ts) { - return (int64_t)ts.tv_nsec + 1000000000LL * (int64_t)ts.tv_sec; + return (s64)ts.tv_nsec + 1000000000LL * (s64)ts.tv_sec; } -struct timespec timespec_add_ns(struct timespec ts, int64_t ns) +struct timespec timespec_add_ns(struct timespec ts, s64 ns) { struct timespec res; @@ -101,15 +101,15 @@ struct timespec timespec_add_ns(struct timespec ts, int64_t ns) struct timespec timespec_add(struct timespec ts1, struct timespec ts2) { - int64_t ns1 = timespec_to_ns(ts1); - int64_t ns2 = timespec_to_ns(ts2); + s64 ns1 = timespec_to_ns(ts1); + s64 ns2 = timespec_to_ns(ts2); return timespec_add_ns((struct timespec){0}, ns1 + ns2); } struct timespec timespec_sub(struct timespec ts1, struct timespec ts2) { - int64_t ns1 = timespec_to_ns(ts1); - int64_t ns2 = timespec_to_ns(ts2); + s64 ns1 = timespec_to_ns(ts1); + s64 ns2 = timespec_to_ns(ts2); return timespec_add_ns((struct timespec){0}, ns1 - ns2); } @@ -123,7 +123,7 @@ struct timespec timespec_elapsed(struct timespec start) struct timespec timespec_div(struct timespec ts, int divisor) { - int64_t ns = timespec_to_ns(ts) / divisor; + s64 ns = timespec_to_ns(ts) / divisor; return timespec_add_ns((struct timespec){0}, ns); } @@ -225,7 +225,7 @@ size_t get_def_hugetlb_pagesz(void) #define ANON_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS) #define ANON_HUGE_FLAGS (ANON_FLAGS | MAP_HUGETLB) -const struct vm_mem_backing_src_alias *vm_mem_backing_src_alias(uint32_t i) +const struct vm_mem_backing_src_alias *vm_mem_backing_src_alias(u32 i) { static const struct vm_mem_backing_src_alias aliases[] = { [VM_MEM_SRC_ANONYMOUS] = { @@ -317,9 +317,9 @@ const struct vm_mem_backing_src_alias *vm_mem_backing_src_alias(uint32_t i) #define MAP_HUGE_PAGE_SIZE(x) (1ULL << ((x >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK)) -size_t get_backing_src_pagesz(uint32_t i) +size_t get_backing_src_pagesz(u32 i) { - uint32_t flag = vm_mem_backing_src_alias(i)->flag; + u32 flag = vm_mem_backing_src_alias(i)->flag; switch (i) { case VM_MEM_SRC_ANONYMOUS: @@ -335,7 +335,7 @@ size_t get_backing_src_pagesz(uint32_t i) } } -bool is_backing_src_hugetlb(uint32_t i) +bool is_backing_src_hugetlb(u32 i) { return !!(vm_mem_backing_src_alias(i)->flag & MAP_HUGETLB); } diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c index 42151e571953..029ce21f9f2f 100644 --- a/tools/testing/selftests/kvm/lib/ucall_common.c +++ b/tools/testing/selftests/kvm/lib/ucall_common.c @@ -14,7 +14,7 @@ struct ucall_header { struct ucall ucalls[KVM_MAX_VCPUS]; }; -int ucall_nr_pages_required(uint64_t page_size) +int ucall_nr_pages_required(u64 page_size) { return align_up(sizeof(struct ucall_header), page_size) / page_size; } @@ -25,16 +25,16 @@ int ucall_nr_pages_required(uint64_t page_size) */ static struct ucall_header *ucall_pool; -void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) +void ucall_init(struct kvm_vm *vm, gpa_t mmio_gpa) { struct ucall_header *hdr; struct ucall *uc; - vm_vaddr_t vaddr; + gva_t gva; int i; - vaddr = vm_vaddr_alloc_shared(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR, - MEM_REGION_DATA); - hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr); + gva = vm_alloc_shared(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR, + MEM_REGION_DATA); + hdr = (struct ucall_header *)addr_gva2hva(vm, gva); memset(hdr, 0, sizeof(*hdr)); for (i = 0; i < KVM_MAX_VCPUS; ++i) { @@ -42,7 +42,7 @@ void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) uc->hva = uc; } - write_guest_global(vm, ucall_pool, (struct ucall_header *)vaddr); + write_guest_global(vm, ucall_pool, (struct ucall_header *)gva); ucall_arch_init(vm, mmio_gpa); } @@ -79,7 +79,7 @@ static void ucall_free(struct ucall *uc) clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use); } -void ucall_assert(uint64_t cmd, const char *exp, const char *file, +void ucall_assert(u64 cmd, const char *exp, const char *file, unsigned int line, const char *fmt, ...) { struct ucall *uc; @@ -88,20 +88,20 @@ void ucall_assert(uint64_t cmd, const char *exp, const char *file, uc = ucall_alloc(); uc->cmd = cmd; - WRITE_ONCE(uc->args[GUEST_ERROR_STRING], (uint64_t)(exp)); - WRITE_ONCE(uc->args[GUEST_FILE], (uint64_t)(file)); + WRITE_ONCE(uc->args[GUEST_ERROR_STRING], (u64)(exp)); + WRITE_ONCE(uc->args[GUEST_FILE], (u64)(file)); WRITE_ONCE(uc->args[GUEST_LINE], line); va_start(va, fmt); guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va); va_end(va); - ucall_arch_do_ucall((vm_vaddr_t)uc->hva); + ucall_arch_do_ucall((gva_t)uc->hva); ucall_free(uc); } -void ucall_fmt(uint64_t cmd, const char *fmt, ...) +void ucall_fmt(u64 cmd, const char *fmt, ...) { struct ucall *uc; va_list va; @@ -113,12 +113,12 @@ void ucall_fmt(uint64_t cmd, const char *fmt, ...) guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va); va_end(va); - ucall_arch_do_ucall((vm_vaddr_t)uc->hva); + ucall_arch_do_ucall((gva_t)uc->hva); ucall_free(uc); } -void ucall(uint64_t cmd, int nargs, ...) +void ucall(u64 cmd, int nargs, ...) { struct ucall *uc; va_list va; @@ -132,15 +132,15 @@ void ucall(uint64_t cmd, int nargs, ...) va_start(va, nargs); for (i = 0; i < nargs; ++i) - WRITE_ONCE(uc->args[i], va_arg(va, uint64_t)); + WRITE_ONCE(uc->args[i], va_arg(va, u64)); va_end(va); - ucall_arch_do_ucall((vm_vaddr_t)uc->hva); + ucall_arch_do_ucall((gva_t)uc->hva); ucall_free(uc); } -uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc) +u64 get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc) { struct ucall ucall; void *addr; diff --git a/tools/testing/selftests/kvm/lib/userfaultfd_util.c b/tools/testing/selftests/kvm/lib/userfaultfd_util.c index 5bde176cedd5..ef8d76f71f83 100644 --- a/tools/testing/selftests/kvm/lib/userfaultfd_util.c +++ b/tools/testing/selftests/kvm/lib/userfaultfd_util.c @@ -27,7 +27,7 @@ static void *uffd_handler_thread_fn(void *arg) { struct uffd_reader_args *reader_args = (struct uffd_reader_args *)arg; int uffd = reader_args->uffd; - int64_t pages = 0; + s64 pages = 0; struct timespec start; struct timespec ts_diff; struct epoll_event evt; @@ -100,8 +100,8 @@ static void *uffd_handler_thread_fn(void *arg) } struct uffd_desc *uffd_setup_demand_paging(int uffd_mode, useconds_t delay, - void *hva, uint64_t len, - uint64_t num_readers, + void *hva, u64 len, + u64 num_readers, uffd_handler_t handler) { struct uffd_desc *uffd_desc; @@ -109,7 +109,7 @@ struct uffd_desc *uffd_setup_demand_paging(int uffd_mode, useconds_t delay, int uffd; struct uffdio_api uffdio_api; struct uffdio_register uffdio_register; - uint64_t expected_ioctls = ((uint64_t) 1) << _UFFDIO_COPY; + u64 expected_ioctls = ((u64)1) << _UFFDIO_COPY; int ret, i; PER_PAGE_DEBUG("Userfaultfd %s mode, faults resolved with %s\n", @@ -132,7 +132,7 @@ struct uffd_desc *uffd_setup_demand_paging(int uffd_mode, useconds_t delay, /* In order to get minor faults, prefault via the alias. */ if (is_minor) - expected_ioctls = ((uint64_t) 1) << _UFFDIO_CONTINUE; + expected_ioctls = ((u64)1) << _UFFDIO_CONTINUE; uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); TEST_ASSERT(uffd >= 0, "uffd creation failed, errno: %d", errno); @@ -141,9 +141,9 @@ struct uffd_desc *uffd_setup_demand_paging(int uffd_mode, useconds_t delay, uffdio_api.features = 0; TEST_ASSERT(ioctl(uffd, UFFDIO_API, &uffdio_api) != -1, "ioctl UFFDIO_API failed: %" PRIu64, - (uint64_t)uffdio_api.api); + (u64)uffdio_api.api); - uffdio_register.range.start = (uint64_t)hva; + uffdio_register.range.start = (u64)hva; uffdio_register.range.len = len; uffdio_register.mode = uffd_mode; TEST_ASSERT(ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) != -1, diff --git a/tools/testing/selftests/kvm/lib/x86/apic.c b/tools/testing/selftests/kvm/lib/x86/apic.c index 89153a333e83..5182fd0d6a76 100644 --- a/tools/testing/selftests/kvm/lib/x86/apic.c +++ b/tools/testing/selftests/kvm/lib/x86/apic.c @@ -14,7 +14,7 @@ void apic_disable(void) void xapic_enable(void) { - uint64_t val = rdmsr(MSR_IA32_APICBASE); + u64 val = rdmsr(MSR_IA32_APICBASE); /* Per SDM: to enable xAPIC when in x2APIC must first disable APIC */ if (val & MSR_IA32_APICBASE_EXTD) { diff --git a/tools/testing/selftests/kvm/lib/x86/hyperv.c b/tools/testing/selftests/kvm/lib/x86/hyperv.c index 15bc8cd583aa..d200c5c26e2e 100644 --- a/tools/testing/selftests/kvm/lib/x86/hyperv.c +++ b/tools/testing/selftests/kvm/lib/x86/hyperv.c @@ -76,23 +76,23 @@ bool kvm_hv_cpu_has(struct kvm_x86_cpu_feature feature) } struct hyperv_test_pages *vcpu_alloc_hyperv_test_pages(struct kvm_vm *vm, - vm_vaddr_t *p_hv_pages_gva) + gva_t *p_hv_pages_gva) { - vm_vaddr_t hv_pages_gva = vm_vaddr_alloc_page(vm); + gva_t hv_pages_gva = vm_alloc_page(vm); struct hyperv_test_pages *hv = addr_gva2hva(vm, hv_pages_gva); /* Setup of a region of guest memory for the VP Assist page. */ - hv->vp_assist = (void *)vm_vaddr_alloc_page(vm); + hv->vp_assist = (void *)vm_alloc_page(vm); hv->vp_assist_hva = addr_gva2hva(vm, (uintptr_t)hv->vp_assist); hv->vp_assist_gpa = addr_gva2gpa(vm, (uintptr_t)hv->vp_assist); /* Setup of a region of guest memory for the partition assist page. */ - hv->partition_assist = (void *)vm_vaddr_alloc_page(vm); + hv->partition_assist = (void *)vm_alloc_page(vm); hv->partition_assist_hva = addr_gva2hva(vm, (uintptr_t)hv->partition_assist); hv->partition_assist_gpa = addr_gva2gpa(vm, (uintptr_t)hv->partition_assist); /* Setup of a region of guest memory for the enlightened VMCS. */ - hv->enlightened_vmcs = (void *)vm_vaddr_alloc_page(vm); + hv->enlightened_vmcs = (void *)vm_alloc_page(vm); hv->enlightened_vmcs_hva = addr_gva2hva(vm, (uintptr_t)hv->enlightened_vmcs); hv->enlightened_vmcs_gpa = addr_gva2gpa(vm, (uintptr_t)hv->enlightened_vmcs); @@ -100,9 +100,9 @@ struct hyperv_test_pages *vcpu_alloc_hyperv_test_pages(struct kvm_vm *vm, return hv; } -int enable_vp_assist(uint64_t vp_assist_pa, void *vp_assist) +int enable_vp_assist(u64 vp_assist_pa, void *vp_assist) { - uint64_t val = (vp_assist_pa & HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_MASK) | + u64 val = (vp_assist_pa & HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_MASK) | HV_X64_MSR_VP_ASSIST_PAGE_ENABLE; wrmsr(HV_X64_MSR_VP_ASSIST_PAGE, val); diff --git a/tools/testing/selftests/kvm/lib/x86/memstress.c b/tools/testing/selftests/kvm/lib/x86/memstress.c index f53414ba7103..61cf952cd2dc 100644 --- a/tools/testing/selftests/kvm/lib/x86/memstress.c +++ b/tools/testing/selftests/kvm/lib/x86/memstress.c @@ -16,7 +16,7 @@ #include "svm_util.h" #include "vmx.h" -void memstress_l2_guest_code(uint64_t vcpu_id) +void memstress_l2_guest_code(u64 vcpu_id) { memstress_guest_code(vcpu_id); vmcall(); @@ -32,7 +32,7 @@ __asm__( #define L2_GUEST_STACK_SIZE 64 -static void l1_vmx_code(struct vmx_pages *vmx, uint64_t vcpu_id) +static void l1_vmx_code(struct vmx_pages *vmx, u64 vcpu_id) { unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; unsigned long *rsp; @@ -51,7 +51,7 @@ static void l1_vmx_code(struct vmx_pages *vmx, uint64_t vcpu_id) GUEST_DONE(); } -static void l1_svm_code(struct svm_test_data *svm, uint64_t vcpu_id) +static void l1_svm_code(struct svm_test_data *svm, u64 vcpu_id) { unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; unsigned long *rsp; @@ -67,7 +67,7 @@ static void l1_svm_code(struct svm_test_data *svm, uint64_t vcpu_id) } -static void memstress_l1_guest_code(void *data, uint64_t vcpu_id) +static void memstress_l1_guest_code(void *data, u64 vcpu_id) { if (this_cpu_has(X86_FEATURE_VMX)) l1_vmx_code(data, vcpu_id); @@ -75,7 +75,7 @@ static void memstress_l1_guest_code(void *data, uint64_t vcpu_id) l1_svm_code(data, vcpu_id); } -uint64_t memstress_nested_pages(int nr_vcpus) +u64 memstress_nested_pages(int nr_vcpus) { /* * 513 page tables is enough to identity-map 256 TiB of L2 with 1G @@ -87,7 +87,7 @@ uint64_t memstress_nested_pages(int nr_vcpus) static void memstress_setup_ept_mappings(struct kvm_vm *vm) { - uint64_t start, end; + u64 start, end; /* * Identity map the first 4G and the test region with 1G pages so that @@ -104,7 +104,7 @@ static void memstress_setup_ept_mappings(struct kvm_vm *vm) void memstress_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[]) { struct kvm_regs regs; - vm_vaddr_t nested_gva; + gva_t nested_gva; int vcpu_id; TEST_REQUIRE(kvm_cpu_has_tdp()); diff --git a/tools/testing/selftests/kvm/lib/x86/pmu.c b/tools/testing/selftests/kvm/lib/x86/pmu.c index 34cb57d1d671..0851b74b4e46 100644 --- a/tools/testing/selftests/kvm/lib/x86/pmu.c +++ b/tools/testing/selftests/kvm/lib/x86/pmu.c @@ -11,7 +11,7 @@ #include "processor.h" #include "pmu.h" -const uint64_t intel_pmu_arch_events[] = { +const u64 intel_pmu_arch_events[] = { INTEL_ARCH_CPU_CYCLES, INTEL_ARCH_INSTRUCTIONS_RETIRED, INTEL_ARCH_REFERENCE_CYCLES, @@ -28,7 +28,7 @@ const uint64_t intel_pmu_arch_events[] = { }; kvm_static_assert(ARRAY_SIZE(intel_pmu_arch_events) == NR_INTEL_ARCH_EVENTS); -const uint64_t amd_pmu_zen_events[] = { +const u64 amd_pmu_zen_events[] = { AMD_ZEN_CORE_CYCLES, AMD_ZEN_INSTRUCTIONS_RETIRED, AMD_ZEN_BRANCHES_RETIRED, @@ -50,7 +50,7 @@ kvm_static_assert(ARRAY_SIZE(amd_pmu_zen_events) == NR_AMD_ZEN_EVENTS); * be overcounted on these certain instructions, but for Clearwater Forest * only "Instruction Retired" event is overcounted on these instructions. */ -static uint64_t get_pmu_errata(void) +static u64 get_pmu_errata(void) { if (!this_cpu_is_intel()) return 0; @@ -72,7 +72,7 @@ static uint64_t get_pmu_errata(void) } } -uint64_t pmu_errata_mask; +u64 pmu_errata_mask; void kvm_init_pmu_errata(void) { diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c index 23a44941e283..b51467d70f6e 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -21,11 +21,13 @@ #define KERNEL_DS 0x10 #define KERNEL_TSS 0x18 -vm_vaddr_t exception_handlers; +gva_t exception_handlers; bool host_cpu_is_amd; bool host_cpu_is_intel; +bool host_cpu_is_hygon; +bool host_cpu_is_amd_compatible; bool is_forced_emulation_enabled; -uint64_t guest_tsc_khz; +u64 guest_tsc_khz; const char *ex_str(int vector) { @@ -60,7 +62,7 @@ const char *ex_str(int vector) } } -static void regs_dump(FILE *stream, struct kvm_regs *regs, uint8_t indent) +static void regs_dump(FILE *stream, struct kvm_regs *regs, u8 indent) { fprintf(stream, "%*srax: 0x%.16llx rbx: 0x%.16llx " "rcx: 0x%.16llx rdx: 0x%.16llx\n", @@ -84,7 +86,7 @@ static void regs_dump(FILE *stream, struct kvm_regs *regs, uint8_t indent) } static void segment_dump(FILE *stream, struct kvm_segment *segment, - uint8_t indent) + u8 indent) { fprintf(stream, "%*sbase: 0x%.16llx limit: 0x%.8x " "selector: 0x%.4x type: 0x%.2x\n", @@ -101,7 +103,7 @@ static void segment_dump(FILE *stream, struct kvm_segment *segment, } static void dtable_dump(FILE *stream, struct kvm_dtable *dtable, - uint8_t indent) + u8 indent) { fprintf(stream, "%*sbase: 0x%.16llx limit: 0x%.4x " "padding: 0x%.4x 0x%.4x 0x%.4x\n", @@ -109,7 +111,7 @@ static void dtable_dump(FILE *stream, struct kvm_dtable *dtable, dtable->padding[0], dtable->padding[1], dtable->padding[2]); } -static void sregs_dump(FILE *stream, struct kvm_sregs *sregs, uint8_t indent) +static void sregs_dump(FILE *stream, struct kvm_sregs *sregs, u8 indent) { unsigned int i; @@ -205,37 +207,37 @@ void tdp_mmu_init(struct kvm_vm *vm, int pgtable_levels, } static void *virt_get_pte(struct kvm_vm *vm, struct kvm_mmu *mmu, - uint64_t *parent_pte, uint64_t vaddr, int level) + u64 *parent_pte, gva_t gva, int level) { - uint64_t pt_gpa = PTE_GET_PA(*parent_pte); - uint64_t *page_table = addr_gpa2hva(vm, pt_gpa); - int index = (vaddr >> PG_LEVEL_SHIFT(level)) & 0x1ffu; + u64 pt_gpa = PTE_GET_PA(*parent_pte); + u64 *page_table = addr_gpa2hva(vm, pt_gpa); + int index = (gva >> PG_LEVEL_SHIFT(level)) & 0x1ffu; TEST_ASSERT((*parent_pte == mmu->pgd) || is_present_pte(mmu, parent_pte), "Parent PTE (level %d) not PRESENT for gva: 0x%08lx", - level + 1, vaddr); + level + 1, gva); return &page_table[index]; } -static uint64_t *virt_create_upper_pte(struct kvm_vm *vm, - struct kvm_mmu *mmu, - uint64_t *parent_pte, - uint64_t vaddr, - uint64_t paddr, - int current_level, - int target_level) +static u64 *virt_create_upper_pte(struct kvm_vm *vm, + struct kvm_mmu *mmu, + u64 *parent_pte, + gva_t gva, + gpa_t gpa, + int current_level, + int target_level) { - uint64_t *pte = virt_get_pte(vm, mmu, parent_pte, vaddr, current_level); + u64 *pte = virt_get_pte(vm, mmu, parent_pte, gva, current_level); - paddr = vm_untag_gpa(vm, paddr); + gpa = vm_untag_gpa(vm, gpa); if (!is_present_pte(mmu, pte)) { *pte = PTE_PRESENT_MASK(mmu) | PTE_READABLE_MASK(mmu) | PTE_WRITABLE_MASK(mmu) | PTE_EXECUTABLE_MASK(mmu) | PTE_ALWAYS_SET_MASK(mmu); if (current_level == target_level) - *pte |= PTE_HUGE_MASK(mmu) | (paddr & PHYSICAL_PAGE_MASK); + *pte |= PTE_HUGE_MASK(mmu) | (gpa & PHYSICAL_PAGE_MASK); else *pte |= vm_alloc_page_table(vm) & PHYSICAL_PAGE_MASK; } else { @@ -245,39 +247,39 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm *vm, * this level. */ TEST_ASSERT(current_level != target_level, - "Cannot create hugepage at level: %u, vaddr: 0x%lx", - current_level, vaddr); + "Cannot create hugepage at level: %u, gva: 0x%lx", + current_level, gva); TEST_ASSERT(!is_huge_pte(mmu, pte), - "Cannot create page table at level: %u, vaddr: 0x%lx", - current_level, vaddr); + "Cannot create page table at level: %u, gva: 0x%lx", + current_level, gva); } return pte; } -void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu *mmu, uint64_t vaddr, - uint64_t paddr, int level) +void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu *mmu, gva_t gva, + gpa_t gpa, int level) { - const uint64_t pg_size = PG_LEVEL_SIZE(level); - uint64_t *pte = &mmu->pgd; + const u64 pg_size = PG_LEVEL_SIZE(level); + u64 *pte = &mmu->pgd; int current_level; TEST_ASSERT(vm->mode == VM_MODE_PXXVYY_4K, "Unknown or unsupported guest mode: 0x%x", vm->mode); - TEST_ASSERT((vaddr % pg_size) == 0, + TEST_ASSERT((gva % pg_size) == 0, "Virtual address not aligned,\n" - "vaddr: 0x%lx page size: 0x%lx", vaddr, pg_size); - TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, (vaddr >> vm->page_shift)), - "Invalid virtual address, vaddr: 0x%lx", vaddr); - TEST_ASSERT((paddr % pg_size) == 0, + "gva: 0x%lx page size: 0x%lx", gva, pg_size); + TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, (gva >> vm->page_shift)), + "Invalid virtual address, gva: 0x%lx", gva); + TEST_ASSERT((gpa % pg_size) == 0, "Physical address not aligned,\n" - " paddr: 0x%lx page size: 0x%lx", paddr, pg_size); - TEST_ASSERT((paddr >> vm->page_shift) <= vm->max_gfn, + " gpa: 0x%lx page size: 0x%lx", gpa, pg_size); + TEST_ASSERT((gpa >> vm->page_shift) <= vm->max_gfn, "Physical address beyond maximum supported,\n" - " paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", - paddr, vm->max_gfn, vm->page_size); - TEST_ASSERT(vm_untag_gpa(vm, paddr) == paddr, - "Unexpected bits in paddr: %lx", paddr); + " gpa: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", + gpa, vm->max_gfn, vm->page_size); + TEST_ASSERT(vm_untag_gpa(vm, gpa) == gpa, + "Unexpected bits in gpa: %lx", gpa); TEST_ASSERT(!PTE_EXECUTABLE_MASK(mmu) || !PTE_NX_MASK(mmu), "X and NX bit masks cannot be used simultaneously"); @@ -289,40 +291,40 @@ void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu *mmu, uint64_t vaddr, for (current_level = mmu->pgtable_levels; current_level > PG_LEVEL_4K; current_level--) { - pte = virt_create_upper_pte(vm, mmu, pte, vaddr, paddr, + pte = virt_create_upper_pte(vm, mmu, pte, gva, gpa, current_level, level); if (is_huge_pte(mmu, pte)) return; } /* Fill in page table entry. */ - pte = virt_get_pte(vm, mmu, pte, vaddr, PG_LEVEL_4K); + pte = virt_get_pte(vm, mmu, pte, gva, PG_LEVEL_4K); TEST_ASSERT(!is_present_pte(mmu, pte), - "PTE already present for 4k page at vaddr: 0x%lx", vaddr); + "PTE already present for 4k page at gva: 0x%lx", gva); *pte = PTE_PRESENT_MASK(mmu) | PTE_READABLE_MASK(mmu) | PTE_WRITABLE_MASK(mmu) | PTE_EXECUTABLE_MASK(mmu) | - PTE_ALWAYS_SET_MASK(mmu) | (paddr & PHYSICAL_PAGE_MASK); + PTE_ALWAYS_SET_MASK(mmu) | (gpa & PHYSICAL_PAGE_MASK); /* * Neither SEV nor TDX supports shared page tables, so only the final * leaf PTE needs manually set the C/S-bit. */ - if (vm_is_gpa_protected(vm, paddr)) + if (vm_is_gpa_protected(vm, gpa)) *pte |= PTE_C_BIT_MASK(mmu); else *pte |= PTE_S_BIT_MASK(mmu); } -void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) +void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa) { - __virt_pg_map(vm, &vm->mmu, vaddr, paddr, PG_LEVEL_4K); + __virt_pg_map(vm, &vm->mmu, gva, gpa, PG_LEVEL_4K); } -void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, - uint64_t nr_bytes, int level) +void virt_map_level(struct kvm_vm *vm, gva_t gva, gpa_t gpa, + u64 nr_bytes, int level) { - uint64_t pg_size = PG_LEVEL_SIZE(level); - uint64_t nr_pages = nr_bytes / pg_size; + u64 pg_size = PG_LEVEL_SIZE(level); + u64 nr_pages = nr_bytes / pg_size; int i; TEST_ASSERT(nr_bytes % pg_size == 0, @@ -330,16 +332,16 @@ void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, nr_bytes, pg_size); for (i = 0; i < nr_pages; i++) { - __virt_pg_map(vm, &vm->mmu, vaddr, paddr, level); - sparsebit_set_num(vm->vpages_mapped, vaddr >> vm->page_shift, + __virt_pg_map(vm, &vm->mmu, gva, gpa, level); + sparsebit_set_num(vm->vpages_mapped, gva >> vm->page_shift, nr_bytes / PAGE_SIZE); - vaddr += pg_size; - paddr += pg_size; + gva += pg_size; + gpa += pg_size; } } -static bool vm_is_target_pte(struct kvm_mmu *mmu, uint64_t *pte, +static bool vm_is_target_pte(struct kvm_mmu *mmu, u64 *pte, int *level, int current_level) { if (is_huge_pte(mmu, pte)) { @@ -352,13 +354,13 @@ static bool vm_is_target_pte(struct kvm_mmu *mmu, uint64_t *pte, return *level == current_level; } -static uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, - struct kvm_mmu *mmu, - uint64_t vaddr, - int *level) +static u64 *__vm_get_page_table_entry(struct kvm_vm *vm, + struct kvm_mmu *mmu, + gva_t gva, + int *level) { int va_width = 12 + (mmu->pgtable_levels) * 9; - uint64_t *pte = &mmu->pgd; + u64 *pte = &mmu->pgd; int current_level; TEST_ASSERT(!vm->arch.is_pt_protected, @@ -369,49 +371,46 @@ static uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, TEST_ASSERT(vm->mode == VM_MODE_PXXVYY_4K, "Unknown or unsupported guest mode: 0x%x", vm->mode); - TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, - (vaddr >> vm->page_shift)), - "Invalid virtual address, vaddr: 0x%lx", - vaddr); + TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, (gva >> vm->page_shift)), + "Invalid virtual address, gva: 0x%lx", gva); /* - * Check that the vaddr is a sign-extended va_width value. + * Check that the gva is a sign-extended va_width value. */ - TEST_ASSERT(vaddr == - (((int64_t)vaddr << (64 - va_width) >> (64 - va_width))), + TEST_ASSERT(gva == (((s64)gva << (64 - va_width) >> (64 - va_width))), "Canonical check failed. The virtual address is invalid."); for (current_level = mmu->pgtable_levels; current_level > PG_LEVEL_4K; current_level--) { - pte = virt_get_pte(vm, mmu, pte, vaddr, current_level); + pte = virt_get_pte(vm, mmu, pte, gva, current_level); if (vm_is_target_pte(mmu, pte, level, current_level)) return pte; } - return virt_get_pte(vm, mmu, pte, vaddr, PG_LEVEL_4K); + return virt_get_pte(vm, mmu, pte, gva, PG_LEVEL_4K); } -uint64_t *tdp_get_pte(struct kvm_vm *vm, uint64_t l2_gpa) +u64 *tdp_get_pte(struct kvm_vm *vm, u64 l2_gpa) { int level = PG_LEVEL_4K; return __vm_get_page_table_entry(vm, &vm->stage2_mmu, l2_gpa, &level); } -uint64_t *vm_get_pte(struct kvm_vm *vm, uint64_t vaddr) +u64 *vm_get_pte(struct kvm_vm *vm, gva_t gva) { int level = PG_LEVEL_4K; - return __vm_get_page_table_entry(vm, &vm->mmu, vaddr, &level); + return __vm_get_page_table_entry(vm, &vm->mmu, gva, &level); } -void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) +void virt_arch_dump(FILE *stream, struct kvm_vm *vm, u8 indent) { struct kvm_mmu *mmu = &vm->mmu; - uint64_t *pml4e, *pml4e_start; - uint64_t *pdpe, *pdpe_start; - uint64_t *pde, *pde_start; - uint64_t *pte, *pte_start; + u64 *pml4e, *pml4e_start; + u64 *pdpe, *pdpe_start; + u64 *pde, *pde_start; + u64 *pte, *pte_start; if (!mmu->pgd_created) return; @@ -421,8 +420,8 @@ void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) fprintf(stream, "%*s index hvaddr gpaddr " "addr w exec dirty\n", indent, ""); - pml4e_start = (uint64_t *) addr_gpa2hva(vm, mmu->pgd); - for (uint16_t n1 = 0; n1 <= 0x1ffu; n1++) { + pml4e_start = (u64 *)addr_gpa2hva(vm, mmu->pgd); + for (u16 n1 = 0; n1 <= 0x1ffu; n1++) { pml4e = &pml4e_start[n1]; if (!is_present_pte(mmu, pml4e)) continue; @@ -434,7 +433,7 @@ void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) is_writable_pte(mmu, pml4e), is_nx_pte(mmu, pml4e)); pdpe_start = addr_gpa2hva(vm, *pml4e & PHYSICAL_PAGE_MASK); - for (uint16_t n2 = 0; n2 <= 0x1ffu; n2++) { + for (u16 n2 = 0; n2 <= 0x1ffu; n2++) { pdpe = &pdpe_start[n2]; if (!is_present_pte(mmu, pdpe)) continue; @@ -447,7 +446,7 @@ void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) is_nx_pte(mmu, pdpe)); pde_start = addr_gpa2hva(vm, *pdpe & PHYSICAL_PAGE_MASK); - for (uint16_t n3 = 0; n3 <= 0x1ffu; n3++) { + for (u16 n3 = 0; n3 <= 0x1ffu; n3++) { pde = &pde_start[n3]; if (!is_present_pte(mmu, pde)) continue; @@ -459,7 +458,7 @@ void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) is_nx_pte(mmu, pde)); pte_start = addr_gpa2hva(vm, *pde & PHYSICAL_PAGE_MASK); - for (uint16_t n4 = 0; n4 <= 0x1ffu; n4++) { + for (u16 n4 = 0; n4 <= 0x1ffu; n4++) { pte = &pte_start[n4]; if (!is_present_pte(mmu, pte)) continue; @@ -473,10 +472,10 @@ void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) is_writable_pte(mmu, pte), is_nx_pte(mmu, pte), is_dirty_pte(mmu, pte), - ((uint64_t) n1 << 27) - | ((uint64_t) n2 << 18) - | ((uint64_t) n3 << 9) - | ((uint64_t) n4)); + ((u64)n1 << 27) + | ((u64)n2 << 18) + | ((u64)n3 << 9) + | ((u64)n4)); } } } @@ -496,26 +495,24 @@ bool kvm_cpu_has_tdp(void) return kvm_cpu_has_ept() || kvm_cpu_has_npt(); } -void __tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, - uint64_t size, int level) +void __tdp_map(struct kvm_vm *vm, gpa_t l2_gpa, gpa_t gpa, u64 size, int level) { size_t page_size = PG_LEVEL_SIZE(level); size_t npages = size / page_size; - TEST_ASSERT(nested_paddr + size > nested_paddr, "Vaddr overflow"); - TEST_ASSERT(paddr + size > paddr, "Paddr overflow"); + TEST_ASSERT(l2_gpa + size > l2_gpa, "L2 GPA overflow"); + TEST_ASSERT(gpa + size > gpa, "GPA overflow"); while (npages--) { - __virt_pg_map(vm, &vm->stage2_mmu, nested_paddr, paddr, level); - nested_paddr += page_size; - paddr += page_size; + __virt_pg_map(vm, &vm->stage2_mmu, l2_gpa, gpa, level); + l2_gpa += page_size; + gpa += page_size; } } -void tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, - uint64_t size) +void tdp_map(struct kvm_vm *vm, gpa_t l2_gpa, gpa_t gpa, u64 size) { - __tdp_map(vm, nested_paddr, paddr, size, PG_LEVEL_4K); + __tdp_map(vm, l2_gpa, gpa, size, PG_LEVEL_4K); } /* Prepare an identity extended page table that maps all the @@ -523,7 +520,7 @@ void tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, */ void tdp_identity_map_default_memslots(struct kvm_vm *vm) { - uint32_t s, memslot = 0; + u32 s, memslot = 0; sparsebit_idx_t i, last; struct userspace_mem_region *region = memslot2region(vm, memslot); @@ -538,13 +535,13 @@ void tdp_identity_map_default_memslots(struct kvm_vm *vm) if (i > last) break; - tdp_map(vm, (uint64_t)i << vm->page_shift, - (uint64_t)i << vm->page_shift, 1 << vm->page_shift); + tdp_map(vm, (u64)i << vm->page_shift, + (u64)i << vm->page_shift, 1 << vm->page_shift); } } /* Identity map a region with 1GiB Pages. */ -void tdp_identity_map_1g(struct kvm_vm *vm, uint64_t addr, uint64_t size) +void tdp_identity_map_1g(struct kvm_vm *vm, u64 addr, u64 size) { __tdp_map(vm, addr, addr, size, PG_LEVEL_1G); } @@ -616,10 +613,10 @@ static void kvm_seg_set_kernel_data_64bit(struct kvm_segment *segp) segp->present = true; } -vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) +gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva) { int level = PG_LEVEL_NONE; - uint64_t *pte = __vm_get_page_table_entry(vm, &vm->mmu, gva, &level); + u64 *pte = __vm_get_page_table_entry(vm, &vm->mmu, gva, &level); TEST_ASSERT(is_present_pte(&vm->mmu, pte), "Leaf PTE not PRESENT for gva: 0x%08lx", gva); @@ -631,7 +628,7 @@ vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) return vm_untag_gpa(vm, PTE_GET_PA(*pte)) | (gva & ~HUGEPAGE_MASK(level)); } -static void kvm_seg_set_tss_64bit(vm_vaddr_t base, struct kvm_segment *segp) +static void kvm_seg_set_tss_64bit(gva_t base, struct kvm_segment *segp) { memset(segp, 0, sizeof(*segp)); segp->base = base; @@ -744,16 +741,16 @@ static void vm_init_descriptor_tables(struct kvm_vm *vm) struct kvm_segment seg; int i; - vm->arch.gdt = __vm_vaddr_alloc_page(vm, MEM_REGION_DATA); - vm->arch.idt = __vm_vaddr_alloc_page(vm, MEM_REGION_DATA); - vm->handlers = __vm_vaddr_alloc_page(vm, MEM_REGION_DATA); - vm->arch.tss = __vm_vaddr_alloc_page(vm, MEM_REGION_DATA); + vm->arch.gdt = __vm_alloc_page(vm, MEM_REGION_DATA); + vm->arch.idt = __vm_alloc_page(vm, MEM_REGION_DATA); + vm->handlers = __vm_alloc_page(vm, MEM_REGION_DATA); + vm->arch.tss = __vm_alloc_page(vm, MEM_REGION_DATA); /* Handlers have the same address in both address spaces.*/ for (i = 0; i < NUM_INTERRUPTS; i++) set_idt_entry(vm, i, (unsigned long)(&idt_handlers)[i], 0, KERNEL_CS); - *(vm_vaddr_t *)addr_gva2hva(vm, (vm_vaddr_t)(&exception_handlers)) = vm->handlers; + *(gva_t *)addr_gva2hva(vm, (gva_t)(&exception_handlers)) = vm->handlers; kvm_seg_set_kernel_code_64bit(&seg); kvm_seg_fill_gdt_64bit(vm, &seg); @@ -768,9 +765,9 @@ static void vm_init_descriptor_tables(struct kvm_vm *vm) void vm_install_exception_handler(struct kvm_vm *vm, int vector, void (*handler)(struct ex_regs *)) { - vm_vaddr_t *handlers = (vm_vaddr_t *)addr_gva2hva(vm, vm->handlers); + gva_t *handlers = (gva_t *)addr_gva2hva(vm, vm->handlers); - handlers[vector] = (vm_vaddr_t)handler; + handlers[vector] = (gva_t)handler; } void assert_on_unhandled_exception(struct kvm_vcpu *vcpu) @@ -793,6 +790,8 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm, unsigned int nr_vcpus) sync_global_to_guest(vm, host_cpu_is_intel); sync_global_to_guest(vm, host_cpu_is_amd); + sync_global_to_guest(vm, host_cpu_is_hygon); + sync_global_to_guest(vm, host_cpu_is_amd_compatible); sync_global_to_guest(vm, is_forced_emulation_enabled); sync_global_to_guest(vm, pmu_errata_mask); @@ -817,18 +816,17 @@ void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code) vcpu_regs_set(vcpu, ®s); } -struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) +struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id) { struct kvm_mp_state mp_state; struct kvm_regs regs; - vm_vaddr_t stack_vaddr; + gva_t stack_gva; struct kvm_vcpu *vcpu; - stack_vaddr = __vm_vaddr_alloc(vm, DEFAULT_STACK_PGS * getpagesize(), - DEFAULT_GUEST_STACK_VADDR_MIN, - MEM_REGION_DATA); + stack_gva = __vm_alloc(vm, DEFAULT_STACK_PGS * getpagesize(), + DEFAULT_GUEST_STACK_VADDR_MIN, MEM_REGION_DATA); - stack_vaddr += DEFAULT_STACK_PGS * getpagesize(); + stack_gva += DEFAULT_STACK_PGS * getpagesize(); /* * Align stack to match calling sequence requirements in section "The @@ -839,9 +837,9 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) * If this code is ever used to launch a vCPU with 32-bit entry point it * may need to subtract 4 bytes instead of 8 bytes. */ - TEST_ASSERT(IS_ALIGNED(stack_vaddr, PAGE_SIZE), - "__vm_vaddr_alloc() did not provide a page-aligned address"); - stack_vaddr -= 8; + TEST_ASSERT(IS_ALIGNED(stack_gva, PAGE_SIZE), + "__vm_alloc() did not provide a page-aligned address"); + stack_gva -= 8; vcpu = __vm_vcpu_add(vm, vcpu_id); vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid()); @@ -851,7 +849,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) /* Setup guest general purpose registers */ vcpu_regs_get(vcpu, ®s); regs.rflags = regs.rflags | 0x2; - regs.rsp = stack_vaddr; + regs.rsp = stack_gva; vcpu_regs_set(vcpu, ®s); /* Setup the MP state */ @@ -868,7 +866,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) return vcpu; } -struct kvm_vcpu *vm_arch_vcpu_recreate(struct kvm_vm *vm, uint32_t vcpu_id) +struct kvm_vcpu *vm_arch_vcpu_recreate(struct kvm_vm *vm, u32 vcpu_id) { struct kvm_vcpu *vcpu = __vm_vcpu_add(vm, vcpu_id); @@ -903,9 +901,9 @@ const struct kvm_cpuid2 *kvm_get_supported_cpuid(void) return kvm_supported_cpuid; } -static uint32_t __kvm_cpu_has(const struct kvm_cpuid2 *cpuid, - uint32_t function, uint32_t index, - uint8_t reg, uint8_t lo, uint8_t hi) +static u32 __kvm_cpu_has(const struct kvm_cpuid2 *cpuid, + u32 function, u32 index, + u8 reg, u8 lo, u8 hi) { const struct kvm_cpuid_entry2 *entry; int i; @@ -932,14 +930,14 @@ bool kvm_cpuid_has(const struct kvm_cpuid2 *cpuid, feature.reg, feature.bit, feature.bit); } -uint32_t kvm_cpuid_property(const struct kvm_cpuid2 *cpuid, - struct kvm_x86_cpu_property property) +u32 kvm_cpuid_property(const struct kvm_cpuid2 *cpuid, + struct kvm_x86_cpu_property property) { return __kvm_cpu_has(cpuid, property.function, property.index, property.reg, property.lo_bit, property.hi_bit); } -uint64_t kvm_get_feature_msr(uint64_t msr_index) +u64 kvm_get_feature_msr(u64 msr_index) { struct { struct kvm_msrs header; @@ -958,7 +956,7 @@ uint64_t kvm_get_feature_msr(uint64_t msr_index) return buffer.entry.data; } -void __vm_xsave_require_permission(uint64_t xfeature, const char *name) +void __vm_xsave_require_permission(u64 xfeature, const char *name) { int kvm_fd; u64 bitmask; @@ -1015,7 +1013,7 @@ void vcpu_init_cpuid(struct kvm_vcpu *vcpu, const struct kvm_cpuid2 *cpuid) void vcpu_set_cpuid_property(struct kvm_vcpu *vcpu, struct kvm_x86_cpu_property property, - uint32_t value) + u32 value) { struct kvm_cpuid_entry2 *entry; @@ -1030,7 +1028,7 @@ void vcpu_set_cpuid_property(struct kvm_vcpu *vcpu, TEST_ASSERT_EQ(kvm_cpuid_property(vcpu->cpuid, property), value); } -void vcpu_clear_cpuid_entry(struct kvm_vcpu *vcpu, uint32_t function) +void vcpu_clear_cpuid_entry(struct kvm_vcpu *vcpu, u32 function) { struct kvm_cpuid_entry2 *entry = vcpu_get_cpuid_entry(vcpu, function); @@ -1059,7 +1057,7 @@ void vcpu_set_or_clear_cpuid_feature(struct kvm_vcpu *vcpu, vcpu_set_cpuid(vcpu); } -uint64_t vcpu_get_msr(struct kvm_vcpu *vcpu, uint64_t msr_index) +u64 vcpu_get_msr(struct kvm_vcpu *vcpu, u64 msr_index) { struct { struct kvm_msrs header; @@ -1074,7 +1072,7 @@ uint64_t vcpu_get_msr(struct kvm_vcpu *vcpu, uint64_t msr_index) return buffer.entry.data; } -int _vcpu_set_msr(struct kvm_vcpu *vcpu, uint64_t msr_index, uint64_t msr_value) +int _vcpu_set_msr(struct kvm_vcpu *vcpu, u64 msr_index, u64 msr_value) { struct { struct kvm_msrs header; @@ -1102,28 +1100,28 @@ void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...) vcpu_regs_get(vcpu, ®s); if (num >= 1) - regs.rdi = va_arg(ap, uint64_t); + regs.rdi = va_arg(ap, u64); if (num >= 2) - regs.rsi = va_arg(ap, uint64_t); + regs.rsi = va_arg(ap, u64); if (num >= 3) - regs.rdx = va_arg(ap, uint64_t); + regs.rdx = va_arg(ap, u64); if (num >= 4) - regs.rcx = va_arg(ap, uint64_t); + regs.rcx = va_arg(ap, u64); if (num >= 5) - regs.r8 = va_arg(ap, uint64_t); + regs.r8 = va_arg(ap, u64); if (num >= 6) - regs.r9 = va_arg(ap, uint64_t); + regs.r9 = va_arg(ap, u64); vcpu_regs_set(vcpu, ®s); va_end(ap); } -void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent) +void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent) { struct kvm_regs regs; struct kvm_sregs sregs; @@ -1192,7 +1190,7 @@ const struct kvm_msr_list *kvm_get_feature_msr_index_list(void) return list; } -bool kvm_msr_is_in_save_restore_list(uint32_t msr_index) +bool kvm_msr_is_in_save_restore_list(u32 msr_index) { const struct kvm_msr_list *list = kvm_get_msr_index_list(); int i; @@ -1323,7 +1321,7 @@ void kvm_init_vm_address_properties(struct kvm_vm *vm) } const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid, - uint32_t function, uint32_t index) + u32 function, u32 index) { int i; @@ -1340,7 +1338,7 @@ const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid, #define X86_HYPERCALL(inputs...) \ ({ \ - uint64_t r; \ + u64 r; \ \ asm volatile("test %[use_vmmcall], %[use_vmmcall]\n\t" \ "jnz 1f\n\t" \ @@ -1349,23 +1347,23 @@ const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid, "1: vmmcall\n\t" \ "2:" \ : "=a"(r) \ - : [use_vmmcall] "r" (host_cpu_is_amd), inputs); \ + : [use_vmmcall] "r" (host_cpu_is_amd_compatible), \ + inputs); \ \ r; \ }) -uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, - uint64_t a3) +u64 kvm_hypercall(u64 nr, u64 a0, u64 a1, u64 a2, u64 a3) { return X86_HYPERCALL("a"(nr), "b"(a0), "c"(a1), "d"(a2), "S"(a3)); } -uint64_t __xen_hypercall(uint64_t nr, uint64_t a0, void *a1) +u64 __xen_hypercall(u64 nr, u64 a0, void *a1) { return X86_HYPERCALL("a"(nr), "D"(a0), "S"(a1)); } -void xen_hypercall(uint64_t nr, uint64_t a0, void *a1) +void xen_hypercall(u64 nr, u64 a0, void *a1) { GUEST_ASSERT(!__xen_hypercall(nr, a0, a1)); } @@ -1374,7 +1372,7 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm) { const unsigned long num_ht_pages = 12 << (30 - vm->page_shift); /* 12 GiB */ unsigned long ht_gfn, max_gfn, max_pfn; - uint8_t maxphyaddr, guest_maxphyaddr; + u8 maxphyaddr, guest_maxphyaddr; /* * Use "guest MAXPHYADDR" from KVM if it's available. Guest MAXPHYADDR @@ -1389,8 +1387,8 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm) max_gfn = (1ULL << (guest_maxphyaddr - vm->page_shift)) - 1; - /* Avoid reserved HyperTransport region on AMD processors. */ - if (!host_cpu_is_amd) + /* Avoid reserved HyperTransport region on AMD or Hygon processors. */ + if (!host_cpu_is_amd_compatible) return max_gfn; /* On parts with <40 physical address bits, the area is fully hidden */ @@ -1404,7 +1402,7 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm) /* * Otherwise it's at the top of the physical address space, possibly - * reduced due to SME by bits 11:6 of CPUID[0x8000001f].EBX. Use + * reduced due to SME or CSV by bits 11:6 of CPUID[0x8000001f].EBX. Use * the old conservative value if MAXPHYADDR is not enumerated. */ if (!this_cpu_has_p(X86_PROPERTY_MAX_PHY_ADDR)) @@ -1425,6 +1423,8 @@ void kvm_selftest_arch_init(void) { host_cpu_is_intel = this_cpu_is_intel(); host_cpu_is_amd = this_cpu_is_amd(); + host_cpu_is_hygon = this_cpu_is_hygon(); + host_cpu_is_amd_compatible = host_cpu_is_amd || host_cpu_is_hygon; is_forced_emulation_enabled = kvm_is_forced_emulation_enabled(); kvm_init_pmu_errata(); @@ -1446,8 +1446,7 @@ bool kvm_arch_has_default_irqchip(void) return true; } -void setup_smram(struct kvm_vm *vm, struct kvm_vcpu *vcpu, - uint64_t smram_gpa, +void setup_smram(struct kvm_vm *vm, struct kvm_vcpu *vcpu, u64 smram_gpa, const void *smi_handler, size_t handler_size) { vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, smram_gpa, diff --git a/tools/testing/selftests/kvm/lib/x86/sev.c b/tools/testing/selftests/kvm/lib/x86/sev.c index c3a9838f4806..93f916903461 100644 --- a/tools/testing/selftests/kvm/lib/x86/sev.c +++ b/tools/testing/selftests/kvm/lib/x86/sev.c @@ -15,10 +15,10 @@ * expression would cause us to quit the loop. */ static void encrypt_region(struct kvm_vm *vm, struct userspace_mem_region *region, - uint8_t page_type, bool private) + u8 page_type, bool private) { const struct sparsebit *protected_phy_pages = region->protected_phy_pages; - const vm_paddr_t gpa_base = region->region.guest_phys_addr; + const gpa_t gpa_base = region->region.guest_phys_addr; const sparsebit_idx_t lowest_page_in_region = gpa_base >> vm->page_shift; sparsebit_idx_t i, j; @@ -29,15 +29,15 @@ static void encrypt_region(struct kvm_vm *vm, struct userspace_mem_region *regio sev_register_encrypted_memory(vm, region); sparsebit_for_each_set_range(protected_phy_pages, i, j) { - const uint64_t size = (j - i + 1) * vm->page_size; - const uint64_t offset = (i - lowest_page_in_region) * vm->page_size; + const u64 size = (j - i + 1) * vm->page_size; + const u64 offset = (i - lowest_page_in_region) * vm->page_size; if (private) vm_mem_set_private(vm, gpa_base + offset, size); if (is_sev_snp_vm(vm)) snp_launch_update_data(vm, gpa_base + offset, - (uint64_t)addr_gpa2hva(vm, gpa_base + offset), + (u64)addr_gpa2hva(vm, gpa_base + offset), size, page_type); else sev_launch_update_data(vm, gpa_base + offset, size); @@ -79,7 +79,7 @@ void snp_vm_init(struct kvm_vm *vm) vm_sev_ioctl(vm, KVM_SEV_INIT2, &init); } -void sev_vm_launch(struct kvm_vm *vm, uint32_t policy) +void sev_vm_launch(struct kvm_vm *vm, u32 policy) { struct kvm_sev_launch_start launch_start = { .policy = policy, @@ -103,7 +103,7 @@ void sev_vm_launch(struct kvm_vm *vm, uint32_t policy) vm->arch.is_pt_protected = true; } -void sev_vm_launch_measure(struct kvm_vm *vm, uint8_t *measurement) +void sev_vm_launch_measure(struct kvm_vm *vm, u8 *measurement) { struct kvm_sev_launch_measure launch_measure; struct kvm_sev_guest_status guest_status; @@ -131,7 +131,7 @@ void sev_vm_launch_finish(struct kvm_vm *vm) TEST_ASSERT_EQ(status.state, SEV_GUEST_STATE_RUNNING); } -void snp_vm_launch_start(struct kvm_vm *vm, uint64_t policy) +void snp_vm_launch_start(struct kvm_vm *vm, u64 policy) { struct kvm_sev_snp_launch_start launch_start = { .policy = policy, @@ -158,7 +158,7 @@ void snp_vm_launch_finish(struct kvm_vm *vm) vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_FINISH, &launch_finish); } -struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t type, void *guest_code, +struct kvm_vm *vm_sev_create_with_one_vcpu(u32 type, void *guest_code, struct kvm_vcpu **cpu) { struct vm_shape shape = { @@ -174,7 +174,7 @@ struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t type, void *guest_code, return vm; } -void vm_sev_launch(struct kvm_vm *vm, uint64_t policy, uint8_t *measurement) +void vm_sev_launch(struct kvm_vm *vm, u64 policy, u8 *measurement) { if (is_sev_snp_vm(vm)) { vm_enable_cap(vm, KVM_CAP_EXIT_HYPERCALL, BIT(KVM_HC_MAP_GPA_RANGE)); diff --git a/tools/testing/selftests/kvm/lib/x86/svm.c b/tools/testing/selftests/kvm/lib/x86/svm.c index 2e5c480c9afd..3b01605ab016 100644 --- a/tools/testing/selftests/kvm/lib/x86/svm.c +++ b/tools/testing/selftests/kvm/lib/x86/svm.c @@ -28,20 +28,20 @@ u64 rflags; * Pointer to structure with the addresses of the SVM areas. */ struct svm_test_data * -vcpu_alloc_svm(struct kvm_vm *vm, vm_vaddr_t *p_svm_gva) +vcpu_alloc_svm(struct kvm_vm *vm, gva_t *p_svm_gva) { - vm_vaddr_t svm_gva = vm_vaddr_alloc_page(vm); + gva_t svm_gva = vm_alloc_page(vm); struct svm_test_data *svm = addr_gva2hva(vm, svm_gva); - svm->vmcb = (void *)vm_vaddr_alloc_page(vm); + svm->vmcb = (void *)vm_alloc_page(vm); svm->vmcb_hva = addr_gva2hva(vm, (uintptr_t)svm->vmcb); svm->vmcb_gpa = addr_gva2gpa(vm, (uintptr_t)svm->vmcb); - svm->save_area = (void *)vm_vaddr_alloc_page(vm); + svm->save_area = (void *)vm_alloc_page(vm); svm->save_area_hva = addr_gva2hva(vm, (uintptr_t)svm->save_area); svm->save_area_gpa = addr_gva2gpa(vm, (uintptr_t)svm->save_area); - svm->msr = (void *)vm_vaddr_alloc_page(vm); + svm->msr = (void *)vm_alloc_page(vm); svm->msr_hva = addr_gva2hva(vm, (uintptr_t)svm->msr); svm->msr_gpa = addr_gva2gpa(vm, (uintptr_t)svm->msr); memset(svm->msr_hva, 0, getpagesize()); @@ -84,14 +84,14 @@ void vm_enable_npt(struct kvm_vm *vm) void generic_svm_setup(struct svm_test_data *svm, void *guest_rip, void *guest_rsp) { struct vmcb *vmcb = svm->vmcb; - uint64_t vmcb_gpa = svm->vmcb_gpa; + u64 vmcb_gpa = svm->vmcb_gpa; struct vmcb_save_area *save = &vmcb->save; struct vmcb_control_area *ctrl = &vmcb->control; u32 data_seg_attr = 3 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK | SVM_SELECTOR_DB_MASK | SVM_SELECTOR_G_MASK; u32 code_seg_attr = 9 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK | SVM_SELECTOR_L_MASK | SVM_SELECTOR_G_MASK; - uint64_t efer; + u64 efer; efer = rdmsr(MSR_EFER); wrmsr(MSR_EFER, efer | EFER_SVME); @@ -126,7 +126,7 @@ void generic_svm_setup(struct svm_test_data *svm, void *guest_rip, void *guest_r guest_regs.rdi = (u64)svm; if (svm->ncr3_gpa) { - ctrl->nested_ctl |= SVM_NESTED_CTL_NP_ENABLE; + ctrl->misc_ctl |= SVM_MISC_ENABLE_NP; ctrl->nested_cr3 = svm->ncr3_gpa; } } @@ -158,7 +158,7 @@ void generic_svm_setup(struct svm_test_data *svm, void *guest_rip, void *guest_r * for now. registers involved in LOAD/SAVE_GPR_C are eventually * unmodified so they do not need to be in the clobber list. */ -void run_guest(struct vmcb *vmcb, uint64_t vmcb_gpa) +void run_guest(struct vmcb *vmcb, u64 vmcb_gpa) { asm volatile ( "vmload %[vmcb_gpa]\n\t" diff --git a/tools/testing/selftests/kvm/lib/x86/ucall.c b/tools/testing/selftests/kvm/lib/x86/ucall.c index 1265cecc7dd1..e7dd5791959b 100644 --- a/tools/testing/selftests/kvm/lib/x86/ucall.c +++ b/tools/testing/selftests/kvm/lib/x86/ucall.c @@ -6,9 +6,9 @@ */ #include "kvm_util.h" -#define UCALL_PIO_PORT ((uint16_t)0x1000) +#define UCALL_PIO_PORT ((u16)0x1000) -void ucall_arch_do_ucall(vm_vaddr_t uc) +void ucall_arch_do_ucall(gva_t uc) { /* * FIXME: Revert this hack (the entire commit that added it) once nVMX diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c index c87b340362a9..67642759e4a0 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -27,7 +27,7 @@ struct hv_vp_assist_page *current_vp_assist; int vcpu_enable_evmcs(struct kvm_vcpu *vcpu) { - uint16_t evmcs_ver; + u16 evmcs_ver; vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_ENLIGHTENED_VMCS, (unsigned long)&evmcs_ver); @@ -79,39 +79,39 @@ void vm_enable_ept(struct kvm_vm *vm) * Pointer to structure with the addresses of the VMX areas. */ struct vmx_pages * -vcpu_alloc_vmx(struct kvm_vm *vm, vm_vaddr_t *p_vmx_gva) +vcpu_alloc_vmx(struct kvm_vm *vm, gva_t *p_vmx_gva) { - vm_vaddr_t vmx_gva = vm_vaddr_alloc_page(vm); + gva_t vmx_gva = vm_alloc_page(vm); struct vmx_pages *vmx = addr_gva2hva(vm, vmx_gva); /* Setup of a region of guest memory for the vmxon region. */ - vmx->vmxon = (void *)vm_vaddr_alloc_page(vm); + vmx->vmxon = (void *)vm_alloc_page(vm); vmx->vmxon_hva = addr_gva2hva(vm, (uintptr_t)vmx->vmxon); vmx->vmxon_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->vmxon); /* Setup of a region of guest memory for a vmcs. */ - vmx->vmcs = (void *)vm_vaddr_alloc_page(vm); + vmx->vmcs = (void *)vm_alloc_page(vm); vmx->vmcs_hva = addr_gva2hva(vm, (uintptr_t)vmx->vmcs); vmx->vmcs_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->vmcs); /* Setup of a region of guest memory for the MSR bitmap. */ - vmx->msr = (void *)vm_vaddr_alloc_page(vm); + vmx->msr = (void *)vm_alloc_page(vm); vmx->msr_hva = addr_gva2hva(vm, (uintptr_t)vmx->msr); vmx->msr_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->msr); memset(vmx->msr_hva, 0, getpagesize()); /* Setup of a region of guest memory for the shadow VMCS. */ - vmx->shadow_vmcs = (void *)vm_vaddr_alloc_page(vm); + vmx->shadow_vmcs = (void *)vm_alloc_page(vm); vmx->shadow_vmcs_hva = addr_gva2hva(vm, (uintptr_t)vmx->shadow_vmcs); vmx->shadow_vmcs_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->shadow_vmcs); /* Setup of a region of guest memory for the VMREAD and VMWRITE bitmaps. */ - vmx->vmread = (void *)vm_vaddr_alloc_page(vm); + vmx->vmread = (void *)vm_alloc_page(vm); vmx->vmread_hva = addr_gva2hva(vm, (uintptr_t)vmx->vmread); vmx->vmread_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->vmread); memset(vmx->vmread_hva, 0, getpagesize()); - vmx->vmwrite = (void *)vm_vaddr_alloc_page(vm); + vmx->vmwrite = (void *)vm_alloc_page(vm); vmx->vmwrite_hva = addr_gva2hva(vm, (uintptr_t)vmx->vmwrite); vmx->vmwrite_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->vmwrite); memset(vmx->vmwrite_hva, 0, getpagesize()); @@ -125,8 +125,8 @@ vcpu_alloc_vmx(struct kvm_vm *vm, vm_vaddr_t *p_vmx_gva) bool prepare_for_vmx_operation(struct vmx_pages *vmx) { - uint64_t feature_control; - uint64_t required; + u64 feature_control; + u64 required; unsigned long cr0; unsigned long cr4; @@ -160,7 +160,7 @@ bool prepare_for_vmx_operation(struct vmx_pages *vmx) wrmsr(MSR_IA32_FEAT_CTL, feature_control | required); /* Enter VMX root operation. */ - *(uint32_t *)(vmx->vmxon) = vmcs_revision(); + *(u32 *)(vmx->vmxon) = vmcs_revision(); if (vmxon(vmx->vmxon_gpa)) return false; @@ -170,7 +170,7 @@ bool prepare_for_vmx_operation(struct vmx_pages *vmx) bool load_vmcs(struct vmx_pages *vmx) { /* Load a VMCS. */ - *(uint32_t *)(vmx->vmcs) = vmcs_revision(); + *(u32 *)(vmx->vmcs) = vmcs_revision(); if (vmclear(vmx->vmcs_gpa)) return false; @@ -178,14 +178,14 @@ bool load_vmcs(struct vmx_pages *vmx) return false; /* Setup shadow VMCS, do not load it yet. */ - *(uint32_t *)(vmx->shadow_vmcs) = vmcs_revision() | 0x80000000ul; + *(u32 *)(vmx->shadow_vmcs) = vmcs_revision() | 0x80000000ul; if (vmclear(vmx->shadow_vmcs_gpa)) return false; return true; } -static bool ept_vpid_cap_supported(uint64_t mask) +static bool ept_vpid_cap_supported(u64 mask) { return rdmsr(MSR_IA32_VMX_EPT_VPID_CAP) & mask; } @@ -200,7 +200,7 @@ bool ept_1g_pages_supported(void) */ static inline void init_vmcs_control_fields(struct vmx_pages *vmx) { - uint32_t sec_exec_ctl = 0; + u32 sec_exec_ctl = 0; vmwrite(VIRTUAL_PROCESSOR_ID, 0); vmwrite(POSTED_INTR_NV, 0); @@ -208,7 +208,7 @@ static inline void init_vmcs_control_fields(struct vmx_pages *vmx) vmwrite(PIN_BASED_VM_EXEC_CONTROL, rdmsr(MSR_IA32_VMX_TRUE_PINBASED_CTLS)); if (vmx->eptp_gpa) { - uint64_t eptp = vmx->eptp_gpa | EPTP_WB | EPTP_PWL_4; + u64 eptp = vmx->eptp_gpa | EPTP_WB | EPTP_PWL_4; TEST_ASSERT((vmx->eptp_gpa & ~PHYSICAL_PAGE_MASK) == 0, "Illegal bits set in vmx->eptp_gpa"); @@ -259,7 +259,7 @@ static inline void init_vmcs_control_fields(struct vmx_pages *vmx) */ static inline void init_vmcs_host_state(void) { - uint32_t exit_controls = vmreadz(VM_EXIT_CONTROLS); + u32 exit_controls = vmreadz(VM_EXIT_CONTROLS); vmwrite(HOST_ES_SELECTOR, get_es()); vmwrite(HOST_CS_SELECTOR, get_cs()); @@ -358,8 +358,8 @@ static inline void init_vmcs_guest_state(void *rip, void *rsp) vmwrite(GUEST_GDTR_BASE, vmreadz(HOST_GDTR_BASE)); vmwrite(GUEST_IDTR_BASE, vmreadz(HOST_IDTR_BASE)); vmwrite(GUEST_DR7, 0x400); - vmwrite(GUEST_RSP, (uint64_t)rsp); - vmwrite(GUEST_RIP, (uint64_t)rip); + vmwrite(GUEST_RSP, (u64)rsp); + vmwrite(GUEST_RIP, (u64)rip); vmwrite(GUEST_RFLAGS, 2); vmwrite(GUEST_PENDING_DBG_EXCEPTIONS, 0); vmwrite(GUEST_SYSENTER_ESP, vmreadz(HOST_IA32_SYSENTER_ESP)); @@ -375,7 +375,7 @@ void prepare_vmcs(struct vmx_pages *vmx, void *guest_rip, void *guest_rsp) bool kvm_cpu_has_ept(void) { - uint64_t ctrl; + u64 ctrl; if (!kvm_cpu_has(X86_FEATURE_VMX)) return false; @@ -390,7 +390,7 @@ bool kvm_cpu_has_ept(void) void prepare_virtualize_apic_accesses(struct vmx_pages *vmx, struct kvm_vm *vm) { - vmx->apic_access = (void *)vm_vaddr_alloc_page(vm); + vmx->apic_access = (void *)vm_alloc_page(vm); vmx->apic_access_hva = addr_gva2hva(vm, (uintptr_t)vmx->apic_access); vmx->apic_access_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->apic_access); } diff --git a/tools/testing/selftests/kvm/loongarch/arch_timer.c b/tools/testing/selftests/kvm/loongarch/arch_timer.c index 355ecac30954..a7279ded8518 100644 --- a/tools/testing/selftests/kvm/loongarch/arch_timer.c +++ b/tools/testing/selftests/kvm/loongarch/arch_timer.c @@ -27,8 +27,8 @@ static void do_idle(void) static void guest_irq_handler(struct ex_regs *regs) { unsigned int intid; - uint32_t cpu = guest_get_vcpuid(); - uint64_t xcnt, val, cfg, xcnt_diff_us; + u32 cpu = guest_get_vcpuid(); + u64 xcnt, val, cfg, xcnt_diff_us; struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; intid = !!(regs->estat & BIT(INT_TI)); @@ -62,10 +62,10 @@ static void guest_irq_handler(struct ex_regs *regs) WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1); } -static void guest_test_period_timer(uint32_t cpu) +static void guest_test_period_timer(u32 cpu) { - uint32_t irq_iter, config_iter; - uint64_t us; + u32 irq_iter, config_iter; + u64 us; struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; shared_data->nr_iter = test_args.nr_iter; @@ -86,10 +86,10 @@ static void guest_test_period_timer(uint32_t cpu) irq_iter); } -static void guest_test_oneshot_timer(uint32_t cpu) +static void guest_test_oneshot_timer(u32 cpu) { - uint32_t irq_iter, config_iter; - uint64_t us; + u32 irq_iter, config_iter; + u64 us; struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; shared_data->nr_iter = 0; @@ -112,10 +112,10 @@ static void guest_test_oneshot_timer(uint32_t cpu) } } -static void guest_test_emulate_timer(uint32_t cpu) +static void guest_test_emulate_timer(u32 cpu) { - uint32_t config_iter; - uint64_t xcnt_diff_us, us; + u32 config_iter; + u64 xcnt_diff_us, us; struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; local_irq_disable(); @@ -136,9 +136,9 @@ static void guest_test_emulate_timer(uint32_t cpu) local_irq_enable(); } -static void guest_time_count_test(uint32_t cpu) +static void guest_time_count_test(u32 cpu) { - uint32_t config_iter; + u32 config_iter; unsigned long start, end, prev, us; /* Assuming that test case starts to run in 1 second */ @@ -165,7 +165,7 @@ static void guest_time_count_test(uint32_t cpu) static void guest_code(void) { - uint32_t cpu = guest_get_vcpuid(); + u32 cpu = guest_get_vcpuid(); /* must run at first */ guest_time_count_test(cpu); diff --git a/tools/testing/selftests/kvm/loongarch/pmu_test.c b/tools/testing/selftests/kvm/loongarch/pmu_test.c new file mode 100644 index 000000000000..ec3fefb9ea97 --- /dev/null +++ b/tools/testing/selftests/kvm/loongarch/pmu_test.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * LoongArch KVM PMU event counting test + * + * Test hardware event counting: CPU_CYCLES, INSTR_RETIRED, + * BRANCH_INSTRUCTIONS and BRANCH_MISSES. + */ +#include +#include "kvm_util.h" +#include "pmu.h" +#include "loongarch/processor.h" + +static int pmu_irq_count; + +/* Check PMU support */ +static bool has_pmu_support(void) +{ + u32 cfg6; + + /* Read CPUCFG6 to check PMU */ + cfg6 = read_cpucfg(LOONGARCH_CPUCFG6); + + /* Check PMU present bit */ + if (!(cfg6 & CPUCFG6_PMP)) + return false; + + /* Check that at least one counter exists */ + if (((cfg6 & CPUCFG6_PMNUM) >> CPUCFG6_PMNUM_SHIFT) == 0) + return false; + + return true; +} + +/* Dump PMU capabilities */ +static void dump_pmu_caps(void) +{ + u32 cfg6; + int nr_counters, counter_bits; + + cfg6 = read_cpucfg(LOONGARCH_CPUCFG6); + nr_counters = ((cfg6 & CPUCFG6_PMNUM) >> CPUCFG6_PMNUM_SHIFT) + 1; + counter_bits = ((cfg6 & CPUCFG6_PMBITS) >> CPUCFG6_PMBITS_SHIFT) + 1; + + pr_info("PMU capabilities:\n"); + pr_info(" Counters present: %s\n", cfg6 & CPUCFG6_PMP ? "yes" : "no"); + pr_info(" Number of counters: %d\n", nr_counters); + pr_info(" Counter width: %d bits\n", counter_bits); +} + +/* Guest test code - runs inside VM */ +static void guest_pmu_base_test(void) +{ + int i; + u32 cfg6, pmnum; + u64 cnt[4]; + + cfg6 = read_cpucfg(LOONGARCH_CPUCFG6); + pmnum = (cfg6 >> 4) & 0xf; + GUEST_PRINTF("CPUCFG6 = 0x%x\n", cfg6); + GUEST_PRINTF("PMP enabled: %s\n", (cfg6 & 0x1) ? "YES" : "NO"); + GUEST_PRINTF("Number of counters (PMNUM): %x\n", pmnum + 1); + GUEST_ASSERT(pmnum == 3); + + GUEST_PRINTF("Clean csr_perfcntr0-3\n"); + csr_write(0, LOONGARCH_CSR_PERFCNTR0); + csr_write(0, LOONGARCH_CSR_PERFCNTR1); + csr_write(0, LOONGARCH_CSR_PERFCNTR2); + csr_write(0, LOONGARCH_CSR_PERFCNTR3); + GUEST_PRINTF("Set csr_perfctrl0 for cycles event\n"); + csr_write(PMU_ENVENT_ENABLED | + LOONGARCH_PMU_EVENT_CYCLES, LOONGARCH_CSR_PERFCTRL0); + GUEST_PRINTF("Set csr_perfctrl1 for instr_retired event\n"); + csr_write(PMU_ENVENT_ENABLED | + LOONGARCH_PMU_EVENT_INSTR_RETIRED, LOONGARCH_CSR_PERFCTRL1); + GUEST_PRINTF("Set csr_perfctrl2 for branch_instructions event\n"); + csr_write(PMU_ENVENT_ENABLED | + PERF_COUNT_HW_BRANCH_INSTRUCTIONS, LOONGARCH_CSR_PERFCTRL2); + GUEST_PRINTF("Set csr_perfctrl3 for branch_misses event\n"); + csr_write(PMU_ENVENT_ENABLED | + PERF_COUNT_HW_BRANCH_MISSES, LOONGARCH_CSR_PERFCTRL3); + + for (i = 0; i < NUM_LOOPS; i++) + cpu_relax(); + + cnt[0] = csr_read(LOONGARCH_CSR_PERFCNTR0); + GUEST_PRINTF("csr_perfcntr0 is %lx\n", cnt[0]); + cnt[1] = csr_read(LOONGARCH_CSR_PERFCNTR1); + GUEST_PRINTF("csr_perfcntr1 is %lx\n", cnt[1]); + cnt[2] = csr_read(LOONGARCH_CSR_PERFCNTR2); + GUEST_PRINTF("csr_perfcntr2 is %lx\n", cnt[2]); + cnt[3] = csr_read(LOONGARCH_CSR_PERFCNTR3); + GUEST_PRINTF("csr_perfcntr3 is %lx\n", cnt[3]); + + GUEST_PRINTF("assert csr_perfcntr0 >EXPECTED_CYCLES_MIN && csr_perfcntr0 < UPPER_BOUND\n"); + GUEST_ASSERT(cnt[0] > EXPECTED_CYCLES_MIN && cnt[0] < UPPER_BOUND); + GUEST_PRINTF("assert csr_perfcntr1 > EXPECTED_INSTR_MIN && csr_perfcntr1 < UPPER_BOUND\n"); + GUEST_ASSERT(cnt[1] > EXPECTED_INSTR_MIN && cnt[1] < UPPER_BOUND); + GUEST_PRINTF("assert csr_perfcntr2 > 0 && csr_perfcntr2 < UPPER_BOUND\n"); + GUEST_ASSERT(cnt[2] > 0 && cnt[2] < UPPER_BOUND); + GUEST_PRINTF("assert csr_perfcntr3 > 0 && csr_perfcntr3 < UPPER_BOUND\n"); + GUEST_ASSERT(cnt[3] > 0 && cnt[3] < UPPER_BOUND); +} + +static void guest_irq_handler(struct ex_regs *regs) +{ + unsigned int intid; + + pmu_irq_disable(); + intid = !!(regs->estat & BIT(INT_PMI)); + GUEST_ASSERT_EQ(intid, 1); + GUEST_PRINTF("Get PMU interrupt\n"); + WRITE_ONCE(pmu_irq_count, pmu_irq_count + 1); +} + +static void guest_pmu_interrupt_test(void) +{ + u64 cnt; + + csr_write(PMU_OVERFLOW - 1, LOONGARCH_CSR_PERFCNTR0); + csr_write(PMU_ENVENT_ENABLED | CSR_PERFCTRL_PMIE | LOONGARCH_PMU_EVENT_CYCLES, LOONGARCH_CSR_PERFCTRL0); + + cpu_relax(); + + GUEST_ASSERT_EQ(pmu_irq_count, 1); + cnt = csr_read(LOONGARCH_CSR_PERFCNTR0); + GUEST_PRINTF("csr_perfcntr0 is %lx\n", cnt); + GUEST_PRINTF("PMU interrupt test success\n"); + +} + +static void guest_code(void) +{ + guest_pmu_base_test(); + + pmu_irq_enable(); + local_irq_enable(); + guest_pmu_interrupt_test(); + + GUEST_DONE(); +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + struct kvm_device_attr attr; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + struct ucall uc; + + /* Check host KVM PMU support */ + if (!has_pmu_support()) { + print_skip("PMU not supported by host hardware\n"); + dump_pmu_caps(); + return KSFT_SKIP; + } + pr_info("Host support PMU\n"); + + /* Dump PMU capabilities */ + dump_pmu_caps(); + + vm = vm_create(VM_MODE_P47V47_16K); + vcpu = vm_vcpu_add(vm, 0, guest_code); + + pmu_irq_count = 0; + vm_init_descriptor_tables(vm); + loongarch_vcpu_setup(vcpu); + vm_install_exception_handler(vm, EXCCODE_INT, guest_irq_handler); + sync_global_to_guest(vm, pmu_irq_count); + + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL, + attr.attr = KVM_LOONGARCH_VM_FEAT_PMU, + + ret = ioctl(vm->fd, KVM_HAS_DEVICE_ATTR, &attr); + + if (ret == 0) { + pr_info("PMU is enabled in VM\n"); + } else { + print_skip("PMU not enabled by VM config\n"); + return KSFT_SKIP; + } + + while (1) { + vcpu_run(vcpu); + switch (get_ucall(vcpu, &uc)) { + case UCALL_PRINTF: + printf("%s", (const char *)uc.buffer); + break; + case UCALL_DONE: + printf("PMU test PASSED\n"); + goto done; + case UCALL_ABORT: + printf("PMU test FAILED\n"); + ret = -1; + goto done; + default: + printf("Unexpected exit\n"); + ret = -1; + goto done; + } + } + +done: + kvm_vm_free(vm); + return ret; +} diff --git a/tools/testing/selftests/kvm/memslot_modification_stress_test.c b/tools/testing/selftests/kvm/memslot_modification_stress_test.c index 3cdfa3b19b85..9c7578a098c3 100644 --- a/tools/testing/selftests/kvm/memslot_modification_stress_test.c +++ b/tools/testing/selftests/kvm/memslot_modification_stress_test.c @@ -30,7 +30,7 @@ static int nr_vcpus = 1; -static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE; +static u64 guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE; static void vcpu_worker(struct memstress_vcpu_args *vcpu_args) { @@ -55,10 +55,10 @@ static void vcpu_worker(struct memstress_vcpu_args *vcpu_args) } static void add_remove_memslot(struct kvm_vm *vm, useconds_t delay, - uint64_t nr_modifications) + u64 nr_modifications) { - uint64_t pages = max_t(int, vm->page_size, getpagesize()) / vm->page_size; - uint64_t gpa; + u64 pages = max_t(int, vm->page_size, getpagesize()) / vm->page_size; + gpa_t gpa; int i; /* @@ -78,7 +78,7 @@ static void add_remove_memslot(struct kvm_vm *vm, useconds_t delay, struct test_params { useconds_t delay; - uint64_t nr_iterations; + u64 nr_iterations; bool partition_vcpu_memory_access; bool disable_slot_zap_quirk; }; diff --git a/tools/testing/selftests/kvm/memslot_perf_test.c b/tools/testing/selftests/kvm/memslot_perf_test.c index 5087d082c4b0..3d02db371422 100644 --- a/tools/testing/selftests/kvm/memslot_perf_test.c +++ b/tools/testing/selftests/kvm/memslot_perf_test.c @@ -85,17 +85,17 @@ struct vm_data { struct kvm_vm *vm; struct kvm_vcpu *vcpu; pthread_t vcpu_thread; - uint32_t nslots; - uint64_t npages; - uint64_t pages_per_slot; + u32 nslots; + u64 npages; + u64 pages_per_slot; void **hva_slots; bool mmio_ok; - uint64_t mmio_gpa_min; - uint64_t mmio_gpa_max; + u64 mmio_gpa_min; + u64 mmio_gpa_max; }; struct sync_area { - uint32_t guest_page_size; + u32 guest_page_size; atomic_bool start_flag; atomic_bool exit_flag; atomic_bool sync_flag; @@ -186,12 +186,12 @@ static void wait_for_vcpu(void) "sem_timedwait() failed: %d", errno); } -static void *vm_gpa2hva(struct vm_data *data, uint64_t gpa, uint64_t *rempages) +static void *vm_gpa2hva(struct vm_data *data, gpa_t gpa, u64 *rempages) { - uint64_t gpage, pgoffs; - uint32_t slot, slotoffs; + gpa_t gpage, pgoffs; + u32 slot, slotoffs; void *base; - uint32_t guest_page_size = data->vm->page_size; + u32 guest_page_size = data->vm->page_size; TEST_ASSERT(gpa >= MEM_GPA, "Too low gpa to translate"); TEST_ASSERT(gpa < MEM_GPA + data->npages * guest_page_size, @@ -200,11 +200,11 @@ static void *vm_gpa2hva(struct vm_data *data, uint64_t gpa, uint64_t *rempages) gpage = gpa / guest_page_size; pgoffs = gpa % guest_page_size; - slot = min(gpage / data->pages_per_slot, (uint64_t)data->nslots - 1); + slot = min(gpage / data->pages_per_slot, (u64)data->nslots - 1); slotoffs = gpage - (slot * data->pages_per_slot); if (rempages) { - uint64_t slotpages; + u64 slotpages; if (slot == data->nslots - 1) slotpages = data->npages - slot * data->pages_per_slot; @@ -217,12 +217,12 @@ static void *vm_gpa2hva(struct vm_data *data, uint64_t gpa, uint64_t *rempages) } base = data->hva_slots[slot]; - return (uint8_t *)base + slotoffs * guest_page_size + pgoffs; + return (u8 *)base + slotoffs * guest_page_size + pgoffs; } -static uint64_t vm_slot2gpa(struct vm_data *data, uint32_t slot) +static u64 vm_slot2gpa(struct vm_data *data, u32 slot) { - uint32_t guest_page_size = data->vm->page_size; + u32 guest_page_size = data->vm->page_size; TEST_ASSERT(slot < data->nslots, "Too high slot number"); @@ -243,8 +243,8 @@ static struct vm_data *alloc_vm(void) return data; } -static bool check_slot_pages(uint32_t host_page_size, uint32_t guest_page_size, - uint64_t pages_per_slot, uint64_t rempages) +static bool check_slot_pages(u32 host_page_size, u32 guest_page_size, + u64 pages_per_slot, u64 rempages) { if (!pages_per_slot) return false; @@ -259,11 +259,11 @@ static bool check_slot_pages(uint32_t host_page_size, uint32_t guest_page_size, } -static uint64_t get_max_slots(struct vm_data *data, uint32_t host_page_size) +static u64 get_max_slots(struct vm_data *data, u32 host_page_size) { - uint32_t guest_page_size = data->vm->page_size; - uint64_t mempages, pages_per_slot, rempages; - uint64_t slots; + u32 guest_page_size = data->vm->page_size; + u64 mempages, pages_per_slot, rempages; + u64 slots; mempages = data->npages; slots = data->nslots; @@ -281,13 +281,13 @@ static uint64_t get_max_slots(struct vm_data *data, uint32_t host_page_size) return 0; } -static bool prepare_vm(struct vm_data *data, int nslots, uint64_t *maxslots, - void *guest_code, uint64_t mem_size, +static bool prepare_vm(struct vm_data *data, int nslots, u64 *maxslots, + void *guest_code, u64 mem_size, struct timespec *slot_runtime) { - uint64_t mempages, rempages; - uint64_t guest_addr; - uint32_t slot, host_page_size, guest_page_size; + u64 mempages, rempages; + u64 guest_addr; + u32 slot, host_page_size, guest_page_size; struct timespec tstart; struct sync_area *sync; @@ -317,7 +317,7 @@ static bool prepare_vm(struct vm_data *data, int nslots, uint64_t *maxslots, clock_gettime(CLOCK_MONOTONIC, &tstart); for (slot = 1, guest_addr = MEM_GPA; slot <= data->nslots; slot++) { - uint64_t npages; + u64 npages; npages = data->pages_per_slot; if (slot == data->nslots) @@ -331,8 +331,8 @@ static bool prepare_vm(struct vm_data *data, int nslots, uint64_t *maxslots, *slot_runtime = timespec_elapsed(tstart); for (slot = 1, guest_addr = MEM_GPA; slot <= data->nslots; slot++) { - uint64_t npages; - uint64_t gpa; + u64 npages; + gpa_t gpa; npages = data->pages_per_slot; if (slot == data->nslots) @@ -448,7 +448,7 @@ static bool guest_perform_sync(void) static void guest_code_test_memslot_move(void) { struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA; - uint32_t page_size = (typeof(page_size))READ_ONCE(sync->guest_page_size); + u32 page_size = (typeof(page_size))READ_ONCE(sync->guest_page_size); uintptr_t base = (typeof(base))READ_ONCE(sync->move_area_ptr); GUEST_SYNC(0); @@ -460,7 +460,7 @@ static void guest_code_test_memslot_move(void) for (ptr = base; ptr < base + MEM_TEST_MOVE_SIZE; ptr += page_size) - *(uint64_t *)ptr = MEM_TEST_VAL_1; + *(u64 *)ptr = MEM_TEST_VAL_1; /* * No host sync here since the MMIO exits are so expensive @@ -477,7 +477,7 @@ static void guest_code_test_memslot_move(void) static void guest_code_test_memslot_map(void) { struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA; - uint32_t page_size = (typeof(page_size))READ_ONCE(sync->guest_page_size); + u32 page_size = (typeof(page_size))READ_ONCE(sync->guest_page_size); GUEST_SYNC(0); @@ -489,7 +489,7 @@ static void guest_code_test_memslot_map(void) for (ptr = MEM_TEST_GPA; ptr < MEM_TEST_GPA + MEM_TEST_MAP_SIZE / 2; ptr += page_size) - *(uint64_t *)ptr = MEM_TEST_VAL_1; + *(u64 *)ptr = MEM_TEST_VAL_1; if (!guest_perform_sync()) break; @@ -497,7 +497,7 @@ static void guest_code_test_memslot_map(void) for (ptr = MEM_TEST_GPA + MEM_TEST_MAP_SIZE / 2; ptr < MEM_TEST_GPA + MEM_TEST_MAP_SIZE; ptr += page_size) - *(uint64_t *)ptr = MEM_TEST_VAL_2; + *(u64 *)ptr = MEM_TEST_VAL_2; if (!guest_perform_sync()) break; @@ -526,13 +526,13 @@ static void guest_code_test_memslot_unmap(void) * * Just access a single page to be on the safe side. */ - *(uint64_t *)ptr = MEM_TEST_VAL_1; + *(u64 *)ptr = MEM_TEST_VAL_1; if (!guest_perform_sync()) break; ptr += MEM_TEST_UNMAP_SIZE / 2; - *(uint64_t *)ptr = MEM_TEST_VAL_2; + *(u64 *)ptr = MEM_TEST_VAL_2; if (!guest_perform_sync()) break; @@ -544,7 +544,7 @@ static void guest_code_test_memslot_unmap(void) static void guest_code_test_memslot_rw(void) { struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA; - uint32_t page_size = (typeof(page_size))READ_ONCE(sync->guest_page_size); + u32 page_size = (typeof(page_size))READ_ONCE(sync->guest_page_size); GUEST_SYNC(0); @@ -555,17 +555,17 @@ static void guest_code_test_memslot_rw(void) for (ptr = MEM_TEST_GPA; ptr < MEM_TEST_GPA + MEM_TEST_SIZE; ptr += page_size) - *(uint64_t *)ptr = MEM_TEST_VAL_1; + *(u64 *)ptr = MEM_TEST_VAL_1; if (!guest_perform_sync()) break; for (ptr = MEM_TEST_GPA + page_size / 2; ptr < MEM_TEST_GPA + MEM_TEST_SIZE; ptr += page_size) { - uint64_t val = *(uint64_t *)ptr; + u64 val = *(u64 *)ptr; GUEST_ASSERT_EQ(val, MEM_TEST_VAL_2); - *(uint64_t *)ptr = 0; + *(u64 *)ptr = 0; } if (!guest_perform_sync()) @@ -577,10 +577,10 @@ static void guest_code_test_memslot_rw(void) static bool test_memslot_move_prepare(struct vm_data *data, struct sync_area *sync, - uint64_t *maxslots, bool isactive) + u64 *maxslots, bool isactive) { - uint32_t guest_page_size = data->vm->page_size; - uint64_t movesrcgpa, movetestgpa; + u32 guest_page_size = data->vm->page_size; + u64 movesrcgpa, movetestgpa; #ifdef __x86_64__ if (disable_slot_zap_quirk) @@ -590,7 +590,7 @@ static bool test_memslot_move_prepare(struct vm_data *data, movesrcgpa = vm_slot2gpa(data, data->nslots - 1); if (isactive) { - uint64_t lastpages; + u64 lastpages; vm_gpa2hva(data, movesrcgpa, &lastpages); if (lastpages * guest_page_size < MEM_TEST_MOVE_SIZE / 2) { @@ -613,21 +613,21 @@ static bool test_memslot_move_prepare(struct vm_data *data, static bool test_memslot_move_prepare_active(struct vm_data *data, struct sync_area *sync, - uint64_t *maxslots) + u64 *maxslots) { return test_memslot_move_prepare(data, sync, maxslots, true); } static bool test_memslot_move_prepare_inactive(struct vm_data *data, struct sync_area *sync, - uint64_t *maxslots) + u64 *maxslots) { return test_memslot_move_prepare(data, sync, maxslots, false); } static void test_memslot_move_loop(struct vm_data *data, struct sync_area *sync) { - uint64_t movesrcgpa; + u64 movesrcgpa; movesrcgpa = vm_slot2gpa(data, data->nslots - 1); vm_mem_region_move(data->vm, data->nslots - 1 + 1, @@ -636,13 +636,13 @@ static void test_memslot_move_loop(struct vm_data *data, struct sync_area *sync) } static void test_memslot_do_unmap(struct vm_data *data, - uint64_t offsp, uint64_t count) + u64 offsp, u64 count) { - uint64_t gpa, ctr; - uint32_t guest_page_size = data->vm->page_size; + gpa_t gpa, ctr; + u32 guest_page_size = data->vm->page_size; for (gpa = MEM_TEST_GPA + offsp * guest_page_size, ctr = 0; ctr < count; ) { - uint64_t npages; + u64 npages; void *hva; int ret; @@ -661,11 +661,11 @@ static void test_memslot_do_unmap(struct vm_data *data, } static void test_memslot_map_unmap_check(struct vm_data *data, - uint64_t offsp, uint64_t valexp) + u64 offsp, u64 valexp) { - uint64_t gpa; - uint64_t *val; - uint32_t guest_page_size = data->vm->page_size; + gpa_t gpa; + u64 *val; + u32 guest_page_size = data->vm->page_size; if (!map_unmap_verify) return; @@ -680,8 +680,8 @@ static void test_memslot_map_unmap_check(struct vm_data *data, static void test_memslot_map_loop(struct vm_data *data, struct sync_area *sync) { - uint32_t guest_page_size = data->vm->page_size; - uint64_t guest_pages = MEM_TEST_MAP_SIZE / guest_page_size; + u32 guest_page_size = data->vm->page_size; + u64 guest_pages = MEM_TEST_MAP_SIZE / guest_page_size; /* * Unmap the second half of the test area while guest writes to (maps) @@ -718,11 +718,11 @@ static void test_memslot_map_loop(struct vm_data *data, struct sync_area *sync) static void test_memslot_unmap_loop_common(struct vm_data *data, struct sync_area *sync, - uint64_t chunk) + u64 chunk) { - uint32_t guest_page_size = data->vm->page_size; - uint64_t guest_pages = MEM_TEST_UNMAP_SIZE / guest_page_size; - uint64_t ctr; + u32 guest_page_size = data->vm->page_size; + u64 guest_pages = MEM_TEST_UNMAP_SIZE / guest_page_size; + u64 ctr; /* * Wait for the guest to finish mapping page(s) in the first half @@ -746,9 +746,9 @@ static void test_memslot_unmap_loop_common(struct vm_data *data, static void test_memslot_unmap_loop(struct vm_data *data, struct sync_area *sync) { - uint32_t host_page_size = getpagesize(); - uint32_t guest_page_size = data->vm->page_size; - uint64_t guest_chunk_pages = guest_page_size >= host_page_size ? + u32 host_page_size = getpagesize(); + u32 guest_page_size = data->vm->page_size; + u64 guest_chunk_pages = guest_page_size >= host_page_size ? 1 : host_page_size / guest_page_size; test_memslot_unmap_loop_common(data, sync, guest_chunk_pages); @@ -757,27 +757,27 @@ static void test_memslot_unmap_loop(struct vm_data *data, static void test_memslot_unmap_loop_chunked(struct vm_data *data, struct sync_area *sync) { - uint32_t guest_page_size = data->vm->page_size; - uint64_t guest_chunk_pages = MEM_TEST_UNMAP_CHUNK_SIZE / guest_page_size; + u32 guest_page_size = data->vm->page_size; + u64 guest_chunk_pages = MEM_TEST_UNMAP_CHUNK_SIZE / guest_page_size; test_memslot_unmap_loop_common(data, sync, guest_chunk_pages); } static void test_memslot_rw_loop(struct vm_data *data, struct sync_area *sync) { - uint64_t gptr; - uint32_t guest_page_size = data->vm->page_size; + u64 gptr; + u32 guest_page_size = data->vm->page_size; for (gptr = MEM_TEST_GPA + guest_page_size / 2; gptr < MEM_TEST_GPA + MEM_TEST_SIZE; gptr += guest_page_size) - *(uint64_t *)vm_gpa2hva(data, gptr, NULL) = MEM_TEST_VAL_2; + *(u64 *)vm_gpa2hva(data, gptr, NULL) = MEM_TEST_VAL_2; host_perform_sync(sync); for (gptr = MEM_TEST_GPA; gptr < MEM_TEST_GPA + MEM_TEST_SIZE; gptr += guest_page_size) { - uint64_t *vptr = (typeof(vptr))vm_gpa2hva(data, gptr, NULL); - uint64_t val = *vptr; + u64 *vptr = (typeof(vptr))vm_gpa2hva(data, gptr, NULL); + u64 val = *vptr; TEST_ASSERT(val == MEM_TEST_VAL_1, "Guest written values should read back correctly (is %"PRIu64" @ %"PRIx64")", @@ -790,21 +790,21 @@ static void test_memslot_rw_loop(struct vm_data *data, struct sync_area *sync) struct test_data { const char *name; - uint64_t mem_size; + u64 mem_size; void (*guest_code)(void); bool (*prepare)(struct vm_data *data, struct sync_area *sync, - uint64_t *maxslots); + u64 *maxslots); void (*loop)(struct vm_data *data, struct sync_area *sync); }; -static bool test_execute(int nslots, uint64_t *maxslots, +static bool test_execute(int nslots, u64 *maxslots, unsigned int maxtime, const struct test_data *tdata, - uint64_t *nloops, + u64 *nloops, struct timespec *slot_runtime, struct timespec *guest_runtime) { - uint64_t mem_size = tdata->mem_size ? : MEM_SIZE; + u64 mem_size = tdata->mem_size ? : MEM_SIZE; struct vm_data *data; struct sync_area *sync; struct timespec tstart; @@ -924,8 +924,8 @@ static void help(char *name, struct test_args *targs) static bool check_memory_sizes(void) { - uint32_t host_page_size = getpagesize(); - uint32_t guest_page_size = vm_guest_mode_params[VM_MODE_DEFAULT].page_size; + u32 host_page_size = getpagesize(); + u32 guest_page_size = vm_guest_mode_params[VM_MODE_DEFAULT].page_size; if (host_page_size > SZ_64K || guest_page_size > SZ_64K) { pr_info("Unsupported page size on host (0x%x) or guest (0x%x)\n", @@ -961,7 +961,7 @@ static bool check_memory_sizes(void) static bool parse_args(int argc, char *argv[], struct test_args *targs) { - uint32_t max_mem_slots; + u32 max_mem_slots; int opt; while ((opt = getopt(argc, argv, "hvdqs:f:e:l:r:")) != -1) { @@ -1040,8 +1040,8 @@ static bool parse_args(int argc, char *argv[], struct test_result { struct timespec slot_runtime, guest_runtime, iter_runtime; - int64_t slottimens, runtimens; - uint64_t nloops; + s64 slottimens, runtimens; + u64 nloops; }; static bool test_loop(const struct test_data *data, @@ -1049,7 +1049,7 @@ static bool test_loop(const struct test_data *data, struct test_result *rbestslottime, struct test_result *rbestruntime) { - uint64_t maxslots; + u64 maxslots; struct test_result result = {}; if (!test_execute(targs->nslots, &maxslots, targs->seconds, data, diff --git a/tools/testing/selftests/kvm/mmu_stress_test.c b/tools/testing/selftests/kvm/mmu_stress_test.c index 51c070556f3e..54d281419d31 100644 --- a/tools/testing/selftests/kvm/mmu_stress_test.c +++ b/tools/testing/selftests/kvm/mmu_stress_test.c @@ -20,19 +20,19 @@ static bool mprotect_ro_done; static bool all_vcpus_hit_ro_fault; -static void guest_code(uint64_t start_gpa, uint64_t end_gpa, uint64_t stride) +static void guest_code(u64 start_gpa, u64 end_gpa, u64 stride) { - uint64_t gpa; + gpa_t gpa; int i; for (i = 0; i < 2; i++) { for (gpa = start_gpa; gpa < end_gpa; gpa += stride) - vcpu_arch_put_guest(*((volatile uint64_t *)gpa), gpa); + vcpu_arch_put_guest(*((volatile u64 *)gpa), gpa); GUEST_SYNC(i); } for (gpa = start_gpa; gpa < end_gpa; gpa += stride) - *((volatile uint64_t *)gpa); + *((volatile u64 *)gpa); GUEST_SYNC(2); /* @@ -55,7 +55,7 @@ static void guest_code(uint64_t start_gpa, uint64_t end_gpa, uint64_t stride) #elif defined(__aarch64__) asm volatile("str %0, [%0]" :: "r" (gpa) : "memory"); #else - vcpu_arch_put_guest(*((volatile uint64_t *)gpa), gpa); + vcpu_arch_put_guest(*((volatile u64 *)gpa), gpa); #endif } while (!READ_ONCE(mprotect_ro_done) || !READ_ONCE(all_vcpus_hit_ro_fault)); @@ -68,7 +68,7 @@ static void guest_code(uint64_t start_gpa, uint64_t end_gpa, uint64_t stride) #endif for (gpa = start_gpa; gpa < end_gpa; gpa += stride) - vcpu_arch_put_guest(*((volatile uint64_t *)gpa), gpa); + vcpu_arch_put_guest(*((volatile u64 *)gpa), gpa); GUEST_SYNC(4); GUEST_ASSERT(0); @@ -76,8 +76,8 @@ static void guest_code(uint64_t start_gpa, uint64_t end_gpa, uint64_t stride) struct vcpu_info { struct kvm_vcpu *vcpu; - uint64_t start_gpa; - uint64_t end_gpa; + u64 start_gpa; + u64 end_gpa; }; static int nr_vcpus; @@ -203,10 +203,10 @@ static void *vcpu_worker(void *data) } static pthread_t *spawn_workers(struct kvm_vm *vm, struct kvm_vcpu **vcpus, - uint64_t start_gpa, uint64_t end_gpa) + u64 start_gpa, u64 end_gpa) { struct vcpu_info *info; - uint64_t gpa, nr_bytes; + gpa_t gpa, nr_bytes; pthread_t *threads; int i; @@ -217,7 +217,7 @@ static pthread_t *spawn_workers(struct kvm_vm *vm, struct kvm_vcpu **vcpus, TEST_ASSERT(info, "Failed to allocate vCPU gpa ranges"); nr_bytes = ((end_gpa - start_gpa) / nr_vcpus) & - ~((uint64_t)vm->page_size - 1); + ~((u64)vm->page_size - 1); TEST_ASSERT(nr_bytes, "C'mon, no way you have %d CPUs", nr_vcpus); for (i = 0, gpa = start_gpa; i < nr_vcpus; i++, gpa += nr_bytes) { @@ -278,11 +278,11 @@ int main(int argc, char *argv[]) * just below the 4gb boundary. This test could create memory at * 1gb-3gb,but it's simpler to skip straight to 4gb. */ - const uint64_t start_gpa = SZ_4G; + const u64 start_gpa = SZ_4G; const int first_slot = 1; struct timespec time_start, time_run1, time_reset, time_run2, time_ro, time_rw; - uint64_t max_gpa, gpa, slot_size, max_mem, i; + u64 max_gpa, gpa, slot_size, max_mem, i; int max_slots, slot, opt, fd; bool hugepages = false; struct kvm_vcpu **vcpus; @@ -347,7 +347,7 @@ int main(int argc, char *argv[]) /* Pre-fault the memory to avoid taking mmap_sem on guest page faults. */ for (i = 0; i < slot_size; i += vm->page_size) - ((uint8_t *)mem)[i] = 0xaa; + ((u8 *)mem)[i] = 0xaa; gpa = 0; for (slot = first_slot; slot < max_slots; slot++) { diff --git a/tools/testing/selftests/kvm/pre_fault_memory_test.c b/tools/testing/selftests/kvm/pre_fault_memory_test.c index 93e603d91311..fcb57fd034e6 100644 --- a/tools/testing/selftests/kvm/pre_fault_memory_test.c +++ b/tools/testing/selftests/kvm/pre_fault_memory_test.c @@ -17,13 +17,13 @@ #define TEST_NPAGES (TEST_SIZE / PAGE_SIZE) #define TEST_SLOT 10 -static void guest_code(uint64_t base_gva) +static void guest_code(u64 base_gva) { - volatile uint64_t val __used; + volatile u64 val __used; int i; for (i = 0; i < TEST_NPAGES; i++) { - uint64_t *src = (uint64_t *)(base_gva + i * PAGE_SIZE); + u64 *src = (u64 *)(base_gva + i * PAGE_SIZE); val = *src; } @@ -33,8 +33,8 @@ static void guest_code(uint64_t base_gva) struct slot_worker_data { struct kvm_vm *vm; - u64 gpa; - uint32_t flags; + gpa_t gpa; + u32 flags; bool worker_ready; bool prefault_ready; bool recreate_slot; @@ -161,7 +161,7 @@ static void pre_fault_memory(struct kvm_vcpu *vcpu, u64 base_gpa, u64 offset, static void __test_pre_fault_memory(unsigned long vm_type, bool private) { - uint64_t gpa, gva, alignment, guest_page_size; + gpa_t gpa, gva, alignment, guest_page_size; const struct vm_shape shape = { .mode = VM_MODE_DEFAULT, .type = vm_type, @@ -175,11 +175,7 @@ static void __test_pre_fault_memory(unsigned long vm_type, bool private) alignment = guest_page_size = vm_guest_mode_params[VM_MODE_DEFAULT].page_size; gpa = (vm->max_gfn - TEST_NPAGES) * guest_page_size; -#ifdef __s390x__ - alignment = max(0x100000UL, guest_page_size); -#else alignment = SZ_2M; -#endif gpa = align_down(gpa, alignment); gva = gpa & ((1ULL << (vm->va_bits - 1)) - 1); diff --git a/tools/testing/selftests/kvm/riscv/arch_timer.c b/tools/testing/selftests/kvm/riscv/arch_timer.c index f962fefc48fa..d67c918ee310 100644 --- a/tools/testing/selftests/kvm/riscv/arch_timer.c +++ b/tools/testing/selftests/kvm/riscv/arch_timer.c @@ -17,9 +17,9 @@ static int timer_irq = IRQ_S_TIMER; static void guest_irq_handler(struct pt_regs *regs) { - uint64_t xcnt, xcnt_diff_us, cmp; + u64 xcnt, xcnt_diff_us, cmp; unsigned int intid = regs->cause & ~CAUSE_IRQ_FLAG; - uint32_t cpu = guest_get_vcpuid(); + u32 cpu = guest_get_vcpuid(); struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; timer_irq_disable(); @@ -40,7 +40,7 @@ static void guest_irq_handler(struct pt_regs *regs) static void guest_run(struct test_vcpu_shared_data *shared_data) { - uint32_t irq_iter, config_iter; + u32 irq_iter, config_iter; shared_data->nr_iter = 0; shared_data->guest_stage = 0; @@ -66,7 +66,7 @@ static void guest_run(struct test_vcpu_shared_data *shared_data) static void guest_code(void) { - uint32_t cpu = guest_get_vcpuid(); + u32 cpu = guest_get_vcpuid(); struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; timer_irq_disable(); diff --git a/tools/testing/selftests/kvm/riscv/ebreak_test.c b/tools/testing/selftests/kvm/riscv/ebreak_test.c index 739d17befb5a..3f44b045a22e 100644 --- a/tools/testing/selftests/kvm/riscv/ebreak_test.c +++ b/tools/testing/selftests/kvm/riscv/ebreak_test.c @@ -8,10 +8,10 @@ #include "kvm_util.h" #include "ucall_common.h" -#define LABEL_ADDRESS(v) ((uint64_t)&(v)) +#define LABEL_ADDRESS(v) ((u64)&(v)) extern unsigned char sw_bp_1, sw_bp_2; -static uint64_t sw_bp_addr; +static u64 sw_bp_addr; static void guest_code(void) { @@ -37,7 +37,7 @@ int main(void) { struct kvm_vm *vm; struct kvm_vcpu *vcpu; - uint64_t pc; + u64 pc; struct kvm_guest_debug debug = { .control = KVM_GUESTDBG_ENABLE, }; diff --git a/tools/testing/selftests/kvm/riscv/get-reg-list.c b/tools/testing/selftests/kvm/riscv/get-reg-list.c index 8d6b951434eb..8d6fdb5d38b8 100644 --- a/tools/testing/selftests/kvm/riscv/get-reg-list.c +++ b/tools/testing/selftests/kvm/riscv/get-reg-list.c @@ -162,7 +162,7 @@ bool check_reject_set(int err) } static int override_vector_reg_size(struct kvm_vcpu *vcpu, struct vcpu_reg_sublist *s, - uint64_t feature) + u64 feature) { unsigned long vlenb_reg = 0; int rc; @@ -197,7 +197,7 @@ void finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) { unsigned long isa_ext_state[KVM_RISCV_ISA_EXT_MAX] = { 0 }; struct vcpu_reg_sublist *s; - uint64_t feature; + u64 feature; int rc; for (int i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) diff --git a/tools/testing/selftests/kvm/riscv/sbi_pmu_test.c b/tools/testing/selftests/kvm/riscv/sbi_pmu_test.c index 924a335d2262..e56a3dd6a51e 100644 --- a/tools/testing/selftests/kvm/riscv/sbi_pmu_test.c +++ b/tools/testing/selftests/kvm/riscv/sbi_pmu_test.c @@ -24,7 +24,7 @@ union sbi_pmu_ctr_info ctrinfo_arr[RISCV_MAX_PMU_COUNTERS]; /* Snapshot shared memory data */ #define PMU_SNAPSHOT_GPA_BASE BIT(30) static void *snapshot_gva; -static vm_paddr_t snapshot_gpa; +static gpa_t snapshot_gpa; static int vcpu_shared_irq_count; static int counter_in_use; @@ -86,7 +86,7 @@ unsigned long pmu_csr_read_num(int csr_num) #undef switchcase_csr_read } -static inline void dummy_func_loop(uint64_t iter) +static inline void dummy_func_loop(u64 iter) { int i = 0; @@ -259,7 +259,7 @@ static inline void verify_sbi_requirement_assert(void) __GUEST_ASSERT(0, "SBI implementation version doesn't support PMU Snapshot"); } -static void snapshot_set_shmem(vm_paddr_t gpa, unsigned long flags) +static void snapshot_set_shmem(gpa_t gpa, unsigned long flags) { unsigned long lo = (unsigned long)gpa; #if __riscv_xlen == 32 @@ -436,6 +436,7 @@ static void test_pmu_basic_sanity(void) struct sbiret ret; int num_counters = 0, i; union sbi_pmu_ctr_info ctrinfo; + unsigned long fw_eidx; probe = guest_sbi_probe_extension(SBI_EXT_PMU, &out_val); GUEST_ASSERT(probe && out_val == 1); @@ -461,7 +462,24 @@ static void test_pmu_basic_sanity(void) pmu_csr_read_num(ctrinfo.csr); GUEST_ASSERT(illegal_handler_invoked); } else if (ctrinfo.type == SBI_PMU_CTR_TYPE_FW) { - read_fw_counter(i, ctrinfo); + /* Read without configure should fail */ + ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_FW_READ, + i, 0, 0, 0, 0, 0); + GUEST_ASSERT(ret.error == SBI_ERR_INVALID_PARAM); + + /* + * Try to configure with a common firmware event. + * If configuration succeeds, verify we can read it. + */ + fw_eidx = ((unsigned long)SBI_PMU_EVENT_TYPE_FW << 16) | + SBI_PMU_FW_ACCESS_LOAD; + + ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, + i, 1, 0, fw_eidx, 0, 0); + if (ret.error == 0) { + GUEST_ASSERT(ret.value == i); + read_fw_counter(i, ctrinfo); + } } } @@ -592,7 +610,7 @@ static void test_vm_setup_snapshot_mem(struct kvm_vm *vm, struct kvm_vcpu *vcpu) virt_map(vm, PMU_SNAPSHOT_GPA_BASE, PMU_SNAPSHOT_GPA_BASE, 1); snapshot_gva = (void *)(PMU_SNAPSHOT_GPA_BASE); - snapshot_gpa = addr_gva2gpa(vcpu->vm, (vm_vaddr_t)snapshot_gva); + snapshot_gpa = addr_gva2gpa(vcpu->vm, (gva_t)snapshot_gva); sync_global_to_guest(vcpu->vm, snapshot_gva); sync_global_to_guest(vcpu->vm, snapshot_gpa); } diff --git a/tools/testing/selftests/kvm/s390/debug_test.c b/tools/testing/selftests/kvm/s390/debug_test.c index ad8095968601..751c61c0f056 100644 --- a/tools/testing/selftests/kvm/s390/debug_test.c +++ b/tools/testing/selftests/kvm/s390/debug_test.c @@ -17,7 +17,7 @@ asm("int_handler:\n" "j .\n"); static struct kvm_vm *test_step_int_1(struct kvm_vcpu **vcpu, void *guest_code, - size_t new_psw_off, uint64_t *new_psw) + size_t new_psw_off, u64 *new_psw) { struct kvm_guest_debug debug = {}; struct kvm_regs regs; @@ -27,7 +27,7 @@ static struct kvm_vm *test_step_int_1(struct kvm_vcpu **vcpu, void *guest_code, vm = vm_create_with_one_vcpu(vcpu, guest_code); lowcore = addr_gpa2hva(vm, 0); new_psw[0] = (*vcpu)->run->psw_mask; - new_psw[1] = (uint64_t)int_handler; + new_psw[1] = (u64)int_handler; memcpy(lowcore + new_psw_off, new_psw, 16); vcpu_regs_get(*vcpu, ®s); regs.gprs[2] = -1; @@ -42,7 +42,7 @@ static struct kvm_vm *test_step_int_1(struct kvm_vcpu **vcpu, void *guest_code, static void test_step_int(void *guest_code, size_t new_psw_off) { struct kvm_vcpu *vcpu; - uint64_t new_psw[2]; + u64 new_psw[2]; struct kvm_vm *vm; vm = test_step_int_1(&vcpu, guest_code, new_psw_off, new_psw); @@ -79,7 +79,7 @@ static void test_step_pgm_diag(void) .u.pgm.code = PGM_SPECIFICATION, }; struct kvm_vcpu *vcpu; - uint64_t new_psw[2]; + u64 new_psw[2]; struct kvm_vm *vm; vm = test_step_int_1(&vcpu, test_step_pgm_diag_guest_code, diff --git a/tools/testing/selftests/kvm/s390/irq_routing.c b/tools/testing/selftests/kvm/s390/irq_routing.c index 7819a0af19a8..f3839284ac08 100644 --- a/tools/testing/selftests/kvm/s390/irq_routing.c +++ b/tools/testing/selftests/kvm/s390/irq_routing.c @@ -27,7 +27,7 @@ static void test(void) struct kvm_irq_routing *routing; struct kvm_vcpu *vcpu; struct kvm_vm *vm; - vm_paddr_t mem; + gpa_t mem; int ret; struct kvm_irq_routing_entry ue = { diff --git a/tools/testing/selftests/kvm/s390/memop.c b/tools/testing/selftests/kvm/s390/memop.c index 4374b4cd2a80..0244848621b3 100644 --- a/tools/testing/selftests/kvm/s390/memop.c +++ b/tools/testing/selftests/kvm/s390/memop.c @@ -34,7 +34,7 @@ enum mop_access_mode { struct mop_desc { uintptr_t gaddr; uintptr_t gaddr_v; - uint64_t set_flags; + u64 set_flags; unsigned int f_check : 1; unsigned int f_inject : 1; unsigned int f_key : 1; @@ -42,19 +42,19 @@ struct mop_desc { unsigned int _set_flags : 1; unsigned int _sida_offset : 1; unsigned int _ar : 1; - uint32_t size; + u32 size; enum mop_target target; enum mop_access_mode mode; void *buf; - uint32_t sida_offset; + u32 sida_offset; void *old; - uint8_t old_value[16]; + u8 old_value[16]; bool *cmpxchg_success; - uint8_t ar; - uint8_t key; + u8 ar; + u8 key; }; -const uint8_t NO_KEY = 0xff; +const u8 NO_KEY = 0xff; static struct kvm_s390_mem_op ksmo_from_desc(struct mop_desc *desc) { @@ -85,7 +85,7 @@ static struct kvm_s390_mem_op ksmo_from_desc(struct mop_desc *desc) ksmo.op = KVM_S390_MEMOP_ABSOLUTE_WRITE; if (desc->mode == CMPXCHG) { ksmo.op = KVM_S390_MEMOP_ABSOLUTE_CMPXCHG; - ksmo.old_addr = (uint64_t)desc->old; + ksmo.old_addr = (u64)desc->old; memcpy(desc->old_value, desc->old, desc->size); } break; @@ -230,8 +230,8 @@ static void memop_ioctl(struct test_info info, struct kvm_s390_mem_op *ksmo, #define CR0_FETCH_PROTECTION_OVERRIDE (1UL << (63 - 38)) #define CR0_STORAGE_PROTECTION_OVERRIDE (1UL << (63 - 39)) -static uint8_t __aligned(PAGE_SIZE) mem1[65536]; -static uint8_t __aligned(PAGE_SIZE) mem2[65536]; +static u8 __aligned(PAGE_SIZE) mem1[65536]; +static u8 __aligned(PAGE_SIZE) mem2[65536]; struct test_default { struct kvm_vm *kvm_vm; @@ -296,7 +296,7 @@ static void prepare_mem12(void) TEST_ASSERT(!memcmp(p1, p2, size), "Memory contents do not match!") static void default_write_read(struct test_info copy_cpu, struct test_info mop_cpu, - enum mop_target mop_target, uint32_t size, uint8_t key) + enum mop_target mop_target, u32 size, u8 key) { prepare_mem12(); CHECK_N_DO(MOP, mop_cpu, mop_target, WRITE, mem1, size, @@ -308,7 +308,7 @@ static void default_write_read(struct test_info copy_cpu, struct test_info mop_c } static void default_read(struct test_info copy_cpu, struct test_info mop_cpu, - enum mop_target mop_target, uint32_t size, uint8_t key) + enum mop_target mop_target, u32 size, u8 key) { prepare_mem12(); CHECK_N_DO(MOP, mop_cpu, mop_target, WRITE, mem1, size, GADDR_V(mem1)); @@ -318,12 +318,12 @@ static void default_read(struct test_info copy_cpu, struct test_info mop_cpu, ASSERT_MEM_EQ(mem1, mem2, size); } -static void default_cmpxchg(struct test_default *test, uint8_t key) +static void default_cmpxchg(struct test_default *test, u8 key) { for (int size = 1; size <= 16; size *= 2) { for (int offset = 0; offset < 16; offset += size) { - uint8_t __aligned(16) new[16] = {}; - uint8_t __aligned(16) old[16]; + u8 __aligned(16) new[16] = {}; + u8 __aligned(16) old[16]; bool succ; prepare_mem12(); @@ -400,7 +400,7 @@ static void test_copy_access_register(void) kvm_vm_free(t.kvm_vm); } -static void set_storage_key_range(void *addr, size_t len, uint8_t key) +static void set_storage_key_range(void *addr, size_t len, u8 key) { uintptr_t _addr, abs, i; int not_mapped = 0; @@ -483,13 +483,13 @@ static __uint128_t cut_to_size(int size, __uint128_t val) { switch (size) { case 1: - return (uint8_t)val; + return (u8)val; case 2: - return (uint16_t)val; + return (u16)val; case 4: - return (uint32_t)val; + return (u32)val; case 8: - return (uint64_t)val; + return (u64)val; case 16: return val; } @@ -501,10 +501,10 @@ static bool popcount_eq(__uint128_t a, __uint128_t b) { unsigned int count_a, count_b; - count_a = __builtin_popcountl((uint64_t)(a >> 64)) + - __builtin_popcountl((uint64_t)a); - count_b = __builtin_popcountl((uint64_t)(b >> 64)) + - __builtin_popcountl((uint64_t)b); + count_a = __builtin_popcountl((u64)(a >> 64)) + + __builtin_popcountl((u64)a); + count_b = __builtin_popcountl((u64)(b >> 64)) + + __builtin_popcountl((u64)b); return count_a == count_b; } @@ -553,7 +553,7 @@ static __uint128_t permutate_bits(bool guest, int i, int size, __uint128_t old) if (swap) { int i, j; __uint128_t new; - uint8_t byte0, byte1; + u8 byte0, byte1; rand = rand * 3 + 1; i = rand % size; @@ -585,28 +585,28 @@ static bool _cmpxchg(int size, void *target, __uint128_t *old_addr, __uint128_t switch (size) { case 4: { - uint32_t old = *old_addr; + u32 old = *old_addr; asm volatile ("cs %[old],%[new],%[address]" : [old] "+d" (old), - [address] "+Q" (*(uint32_t *)(target)) - : [new] "d" ((uint32_t)new) + [address] "+Q" (*(u32 *)(target)) + : [new] "d" ((u32)new) : "cc" ); - ret = old == (uint32_t)*old_addr; + ret = old == (u32)*old_addr; *old_addr = old; return ret; } case 8: { - uint64_t old = *old_addr; + u64 old = *old_addr; asm volatile ("csg %[old],%[new],%[address]" : [old] "+d" (old), - [address] "+Q" (*(uint64_t *)(target)) - : [new] "d" ((uint64_t)new) + [address] "+Q" (*(u64 *)(target)) + : [new] "d" ((u64)new) : "cc" ); - ret = old == (uint64_t)*old_addr; + ret = old == (u64)*old_addr; *old_addr = old; return ret; } @@ -811,10 +811,10 @@ static void test_errors_cmpxchg_key(void) static void test_termination(void) { struct test_default t = test_default_init(guest_error_key); - uint64_t prefix; - uint64_t teid; - uint64_t teid_mask = BIT(63 - 56) | BIT(63 - 60) | BIT(63 - 61); - uint64_t psw[2]; + u64 prefix; + u64 teid; + u64 teid_mask = BIT(63 - 56) | BIT(63 - 60) | BIT(63 - 61); + u64 psw[2]; HOST_SYNC(t.vcpu, STAGE_INITED); HOST_SYNC(t.vcpu, STAGE_SKEYS_SET); @@ -855,7 +855,7 @@ static void test_errors_key_storage_prot_override(void) kvm_vm_free(t.kvm_vm); } -const uint64_t last_page_addr = -PAGE_SIZE; +const u64 last_page_addr = -PAGE_SIZE; static void guest_copy_key_fetch_prot_override(void) { @@ -878,10 +878,10 @@ static void guest_copy_key_fetch_prot_override(void) static void test_copy_key_fetch_prot_override(void) { struct test_default t = test_default_init(guest_copy_key_fetch_prot_override); - vm_vaddr_t guest_0_page, guest_last_page; + gva_t guest_0_page, guest_last_page; - guest_0_page = vm_vaddr_alloc(t.kvm_vm, PAGE_SIZE, 0); - guest_last_page = vm_vaddr_alloc(t.kvm_vm, PAGE_SIZE, last_page_addr); + guest_0_page = vm_alloc(t.kvm_vm, PAGE_SIZE, 0); + guest_last_page = vm_alloc(t.kvm_vm, PAGE_SIZE, last_page_addr); if (guest_0_page != 0 || guest_last_page != last_page_addr) { print_skip("did not allocate guest pages at required positions"); goto out; @@ -917,10 +917,10 @@ out: static void test_errors_key_fetch_prot_override_not_enabled(void) { struct test_default t = test_default_init(guest_copy_key_fetch_prot_override); - vm_vaddr_t guest_0_page, guest_last_page; + gva_t guest_0_page, guest_last_page; - guest_0_page = vm_vaddr_alloc(t.kvm_vm, PAGE_SIZE, 0); - guest_last_page = vm_vaddr_alloc(t.kvm_vm, PAGE_SIZE, last_page_addr); + guest_0_page = vm_alloc(t.kvm_vm, PAGE_SIZE, 0); + guest_last_page = vm_alloc(t.kvm_vm, PAGE_SIZE, last_page_addr); if (guest_0_page != 0 || guest_last_page != last_page_addr) { print_skip("did not allocate guest pages at required positions"); goto out; @@ -938,10 +938,10 @@ out: static void test_errors_key_fetch_prot_override_enabled(void) { struct test_default t = test_default_init(guest_copy_key_fetch_prot_override); - vm_vaddr_t guest_0_page, guest_last_page; + gva_t guest_0_page, guest_last_page; - guest_0_page = vm_vaddr_alloc(t.kvm_vm, PAGE_SIZE, 0); - guest_last_page = vm_vaddr_alloc(t.kvm_vm, PAGE_SIZE, last_page_addr); + guest_0_page = vm_alloc(t.kvm_vm, PAGE_SIZE, 0); + guest_last_page = vm_alloc(t.kvm_vm, PAGE_SIZE, last_page_addr); if (guest_0_page != 0 || guest_last_page != last_page_addr) { print_skip("did not allocate guest pages at required positions"); goto out; diff --git a/tools/testing/selftests/kvm/s390/resets.c b/tools/testing/selftests/kvm/s390/resets.c index b58f75b381e5..e3c7a2f148f9 100644 --- a/tools/testing/selftests/kvm/s390/resets.c +++ b/tools/testing/selftests/kvm/s390/resets.c @@ -20,7 +20,7 @@ struct kvm_s390_irq buf[ARBITRARY_NON_ZERO_VCPU_ID + LOCAL_IRQS]; -static uint8_t regs_null[512]; +static u8 regs_null[512]; static void guest_code_initial(void) { @@ -57,9 +57,9 @@ static void guest_code_initial(void) ); } -static void test_one_reg(struct kvm_vcpu *vcpu, uint64_t id, uint64_t value) +static void test_one_reg(struct kvm_vcpu *vcpu, u64 id, u64 value) { - uint64_t eval_reg; + u64 eval_reg; eval_reg = vcpu_get_reg(vcpu, id); TEST_ASSERT(eval_reg == value, "value == 0x%lx", value); diff --git a/tools/testing/selftests/kvm/s390/shared_zeropage_test.c b/tools/testing/selftests/kvm/s390/shared_zeropage_test.c index bba0d9a6dcc8..a9e5a01200b8 100644 --- a/tools/testing/selftests/kvm/s390/shared_zeropage_test.c +++ b/tools/testing/selftests/kvm/s390/shared_zeropage_test.c @@ -13,7 +13,7 @@ #include "kselftest.h" #include "ucall_common.h" -static void set_storage_key(void *addr, uint8_t skey) +static void set_storage_key(void *addr, u8 skey) { asm volatile("sske %0,%1" : : "d" (skey), "a" (addr)); } diff --git a/tools/testing/selftests/kvm/s390/tprot.c b/tools/testing/selftests/kvm/s390/tprot.c index 12d5e1cb62e3..8054d2b178f0 100644 --- a/tools/testing/selftests/kvm/s390/tprot.c +++ b/tools/testing/selftests/kvm/s390/tprot.c @@ -14,12 +14,12 @@ #define CR0_FETCH_PROTECTION_OVERRIDE (1UL << (63 - 38)) #define CR0_STORAGE_PROTECTION_OVERRIDE (1UL << (63 - 39)) -static __aligned(PAGE_SIZE) uint8_t pages[2][PAGE_SIZE]; -static uint8_t *const page_store_prot = pages[0]; -static uint8_t *const page_fetch_prot = pages[1]; +static __aligned(PAGE_SIZE) u8 pages[2][PAGE_SIZE]; +static u8 *const page_store_prot = pages[0]; +static u8 *const page_fetch_prot = pages[1]; /* Nonzero return value indicates that address not mapped */ -static int set_storage_key(void *addr, uint8_t key) +static int set_storage_key(void *addr, u8 key) { int not_mapped = 0; @@ -44,9 +44,9 @@ enum permission { TRANSL_UNAVAIL = 3, }; -static enum permission test_protection(void *addr, uint8_t key) +static enum permission test_protection(void *addr, u8 key) { - uint64_t mask; + u64 mask; asm volatile ( "tprot %[addr], 0(%[key])\n" @@ -72,7 +72,7 @@ enum stage { struct test { enum stage stage; void *addr; - uint8_t key; + u8 key; enum permission expected; } tests[] = { /* @@ -146,7 +146,7 @@ static enum stage perform_next_stage(int *i, bool mapped_0) /* * Some fetch protection override tests require that page 0 * be mapped, however, when the hosts tries to map that page via - * vm_vaddr_alloc, it may happen that some other page gets mapped + * vm_alloc, it may happen that some other page gets mapped * instead. * In order to skip these tests we detect this inside the guest */ @@ -207,7 +207,7 @@ int main(int argc, char *argv[]) struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_run *run; - vm_vaddr_t guest_0_page; + gva_t guest_0_page; ksft_print_header(); ksft_set_plan(STAGE_END); @@ -216,10 +216,10 @@ int main(int argc, char *argv[]) run = vcpu->run; HOST_SYNC(vcpu, STAGE_INIT_SIMPLE); - mprotect(addr_gva2hva(vm, (vm_vaddr_t)pages), PAGE_SIZE * 2, PROT_READ); + mprotect(addr_gva2hva(vm, (gva_t)pages), PAGE_SIZE * 2, PROT_READ); HOST_SYNC(vcpu, TEST_SIMPLE); - guest_0_page = vm_vaddr_alloc(vm, PAGE_SIZE, 0); + guest_0_page = vm_alloc(vm, PAGE_SIZE, 0); if (guest_0_page != 0) { /* Use NO_TAP so we don't get a PASS print */ HOST_SYNC_NO_TAP(vcpu, STAGE_INIT_FETCH_PROT_OVERRIDE); @@ -229,7 +229,7 @@ int main(int argc, char *argv[]) HOST_SYNC(vcpu, STAGE_INIT_FETCH_PROT_OVERRIDE); } if (guest_0_page == 0) - mprotect(addr_gva2hva(vm, (vm_vaddr_t)0), PAGE_SIZE, PROT_READ); + mprotect(addr_gva2hva(vm, (gva_t)0), PAGE_SIZE, PROT_READ); run->s.regs.crs[0] |= CR0_FETCH_PROTECTION_OVERRIDE; run->kvm_dirty_regs = KVM_SYNC_CRS; HOST_SYNC(vcpu, TEST_FETCH_PROT_OVERRIDE); diff --git a/tools/testing/selftests/kvm/s390/ucontrol_test.c b/tools/testing/selftests/kvm/s390/ucontrol_test.c index 50bc1c38225a..b8c6f37b53e0 100644 --- a/tools/testing/selftests/kvm/s390/ucontrol_test.c +++ b/tools/testing/selftests/kvm/s390/ucontrol_test.c @@ -111,7 +111,7 @@ FIXTURE(uc_kvm) uintptr_t base_hva; uintptr_t code_hva; int kvm_run_size; - vm_paddr_t pgd; + gpa_t pgd; void *vm_mem; int vcpu_fd; int kvm_fd; @@ -269,7 +269,7 @@ TEST(uc_cap_hpage) } /* calculate host virtual addr from guest physical addr */ -static void *gpa2hva(FIXTURE_DATA(uc_kvm) *self, u64 gpa) +static void *gpa2hva(FIXTURE_DATA(uc_kvm) *self, gpa_t gpa) { return (void *)(self->base_hva - self->base_gpa + gpa); } @@ -571,7 +571,7 @@ TEST_F(uc_kvm, uc_skey) { struct kvm_s390_sie_block *sie_block = self->sie_block; struct kvm_sync_regs *sync_regs = &self->run->s.regs; - u64 test_vaddr = VM_MEM_SIZE - (SZ_1M / 2); + u64 test_gva = VM_MEM_SIZE - (SZ_1M / 2); struct kvm_run *run = self->run; const u8 skeyvalue = 0x34; @@ -583,7 +583,7 @@ TEST_F(uc_kvm, uc_skey) /* set register content for test_skey_asm to access not mapped memory */ sync_regs->gprs[1] = skeyvalue; sync_regs->gprs[5] = self->base_gpa; - sync_regs->gprs[6] = test_vaddr; + sync_regs->gprs[6] = test_gva; run->kvm_dirty_regs |= KVM_SYNC_GPRS; /* DAT disabled + 64 bit mode */ diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c index 7fe427ff9b38..9b919a231c93 100644 --- a/tools/testing/selftests/kvm/set_memory_region_test.c +++ b/tools/testing/selftests/kvm/set_memory_region_test.c @@ -30,19 +30,19 @@ #define MEM_REGION_GPA 0xc0000000 #define MEM_REGION_SLOT 10 -static const uint64_t MMIO_VAL = 0xbeefull; +static const u64 MMIO_VAL = 0xbeefull; -extern const uint64_t final_rip_start; -extern const uint64_t final_rip_end; +extern const u64 final_rip_start; +extern const u64 final_rip_end; static sem_t vcpu_ready; -static inline uint64_t guest_spin_on_val(uint64_t spin_val) +static inline u64 guest_spin_on_val(u64 spin_val) { - uint64_t val; + u64 val; do { - val = READ_ONCE(*((uint64_t *)MEM_REGION_GPA)); + val = READ_ONCE(*((u64 *)MEM_REGION_GPA)); } while (val == spin_val); GUEST_SYNC(0); @@ -54,7 +54,7 @@ static void *vcpu_worker(void *data) struct kvm_vcpu *vcpu = data; struct kvm_run *run = vcpu->run; struct ucall uc; - uint64_t cmd; + u64 cmd; /* * Loop until the guest is done. Re-enter the guest on all MMIO exits, @@ -111,8 +111,8 @@ static struct kvm_vm *spawn_vm(struct kvm_vcpu **vcpu, pthread_t *vcpu_thread, void *guest_code) { struct kvm_vm *vm; - uint64_t *hva; - uint64_t gpa; + u64 *hva; + gpa_t gpa; vm = vm_create_with_one_vcpu(vcpu, guest_code); @@ -144,7 +144,7 @@ static struct kvm_vm *spawn_vm(struct kvm_vcpu **vcpu, pthread_t *vcpu_thread, static void guest_code_move_memory_region(void) { - uint64_t val; + u64 val; GUEST_SYNC(0); @@ -180,7 +180,7 @@ static void test_move_memory_region(bool disable_slot_zap_quirk) pthread_t vcpu_thread; struct kvm_vcpu *vcpu; struct kvm_vm *vm; - uint64_t *hva; + u64 *hva; vm = spawn_vm(&vcpu, &vcpu_thread, guest_code_move_memory_region); @@ -224,7 +224,7 @@ static void test_move_memory_region(bool disable_slot_zap_quirk) static void guest_code_delete_memory_region(void) { struct desc_ptr idt; - uint64_t val; + u64 val; /* * Clobber the IDT so that a #PF due to the memory region being deleted @@ -345,8 +345,8 @@ static void test_zero_memory_regions(void) static void test_invalid_memory_region_flags(void) { - uint32_t supported_flags = KVM_MEM_LOG_DIRTY_PAGES; - const uint32_t v2_only_flags = KVM_MEM_GUEST_MEMFD; + u32 supported_flags = KVM_MEM_LOG_DIRTY_PAGES; + const u32 v2_only_flags = KVM_MEM_GUEST_MEMFD; struct kvm_vm *vm; int r, i; @@ -410,17 +410,10 @@ static void test_add_max_memory_regions(void) { int ret; struct kvm_vm *vm; - uint32_t max_mem_slots; - uint32_t slot; + u32 max_mem_slots; + u32 slot; void *mem, *mem_aligned, *mem_extra; - size_t alignment; - -#ifdef __s390x__ - /* On s390x, the host address must be aligned to 1M (due to PGSTEs) */ - alignment = 0x100000; -#else - alignment = 1; -#endif + size_t alignment = 1; max_mem_slots = kvm_check_cap(KVM_CAP_NR_MEMSLOTS); TEST_ASSERT(max_mem_slots > 0, @@ -441,16 +434,16 @@ static void test_add_max_memory_regions(void) for (slot = 0; slot < max_mem_slots; slot++) vm_set_user_memory_region(vm, slot, 0, - ((uint64_t)slot * MEM_REGION_SIZE), + ((u64)slot * MEM_REGION_SIZE), MEM_REGION_SIZE, - mem_aligned + (uint64_t)slot * MEM_REGION_SIZE); + mem_aligned + (u64)slot * MEM_REGION_SIZE); /* Check it cannot be added memory slots beyond the limit */ mem_extra = kvm_mmap(MEM_REGION_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1); ret = __vm_set_user_memory_region(vm, max_mem_slots, 0, - (uint64_t)max_mem_slots * MEM_REGION_SIZE, + (u64)max_mem_slots * MEM_REGION_SIZE, MEM_REGION_SIZE, mem_extra); TEST_ASSERT(ret == -1 && errno == EINVAL, "Adding one more memory slot should fail with EINVAL"); @@ -563,7 +556,7 @@ static void guest_code_mmio_during_vectoring(void) set_idt(&idt_desc); /* Generate a #GP by dereferencing a non-canonical address */ - *((uint8_t *)NONCANONICAL) = 0x1; + *((u8 *)NONCANONICAL) = 0x1; GUEST_ASSERT(0); } diff --git a/tools/testing/selftests/kvm/steal_time.c b/tools/testing/selftests/kvm/steal_time.c index 7be8adfe5dd3..76fcdd1fd3cb 100644 --- a/tools/testing/selftests/kvm/steal_time.c +++ b/tools/testing/selftests/kvm/steal_time.c @@ -25,7 +25,7 @@ #define ST_GPA_BASE (1 << 30) static void *st_gva[NR_VCPUS]; -static uint64_t guest_stolen_time[NR_VCPUS]; +static u64 guest_stolen_time[NR_VCPUS]; #if defined(__x86_64__) @@ -42,9 +42,9 @@ static void check_status(struct kvm_steal_time *st) static void guest_code(int cpu) { struct kvm_steal_time *st = st_gva[cpu]; - uint32_t version; + u32 version; - GUEST_ASSERT_EQ(rdmsr(MSR_KVM_STEAL_TIME), ((uint64_t)st_gva[cpu] | KVM_MSR_ENABLED)); + GUEST_ASSERT_EQ(rdmsr(MSR_KVM_STEAL_TIME), ((u64)st_gva[cpu] | KVM_MSR_ENABLED)); memset(st, 0, sizeof(*st)); GUEST_SYNC(0); @@ -67,22 +67,16 @@ static bool is_steal_time_supported(struct kvm_vcpu *vcpu) return kvm_cpu_has(X86_FEATURE_KVM_STEAL_TIME); } -static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i) +static void steal_time_init(struct kvm_vcpu *vcpu, u32 i) { - int ret; - /* ST_GPA_BASE is identity mapped */ st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE); sync_global_to_guest(vcpu->vm, st_gva[i]); - ret = _vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME, - (ulong)st_gva[i] | KVM_STEAL_RESERVED_MASK); - TEST_ASSERT(ret == 0, "Bad GPA didn't fail"); - vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME, (ulong)st_gva[i] | KVM_MSR_ENABLED); } -static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx) +static void steal_time_dump(struct kvm_vm *vm, u32 vcpu_idx) { struct kvm_steal_time *st = addr_gva2hva(vm, (ulong)st_gva[vcpu_idx]); @@ -99,6 +93,21 @@ static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx) st->pad[8], st->pad[9], st->pad[10]); } +static void check_steal_time_uapi(void) +{ + struct kvm_vm *vm; + struct kvm_vcpu *vcpu; + int ret; + + vm = vm_create_with_one_vcpu(&vcpu, NULL); + + ret = _vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME, + (ulong)ST_GPA_BASE | KVM_STEAL_RESERVED_MASK); + TEST_ASSERT(ret == 0, "Bad GPA didn't fail"); + + kvm_vm_free(vm); +} + #elif defined(__aarch64__) /* PV_TIME_ST must have 64-byte alignment */ @@ -109,12 +118,12 @@ static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx) #define PV_TIME_ST 0xc5000021 struct st_time { - uint32_t rev; - uint32_t attr; - uint64_t st_time; + u32 rev; + u32 attr; + u64 st_time; }; -static int64_t smccc(uint32_t func, uint64_t arg) +static s64 smccc(u32 func, u64 arg) { struct arm_smccc_res res; @@ -131,7 +140,7 @@ static void check_status(struct st_time *st) static void guest_code(int cpu) { struct st_time *st; - int64_t status; + s64 status; status = smccc(SMCCC_ARCH_FEATURES, PV_TIME_FEATURES); GUEST_ASSERT_EQ(status, 0); @@ -166,36 +175,26 @@ static bool is_steal_time_supported(struct kvm_vcpu *vcpu) return !__vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &dev); } -static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i) +static void steal_time_init(struct kvm_vcpu *vcpu, u32 i) { struct kvm_vm *vm = vcpu->vm; - uint64_t st_ipa; - int ret; + u64 st_ipa; struct kvm_device_attr dev = { .group = KVM_ARM_VCPU_PVTIME_CTRL, .attr = KVM_ARM_VCPU_PVTIME_IPA, - .addr = (uint64_t)&st_ipa, + .addr = (u64)&st_ipa, }; - vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &dev); - /* ST_GPA_BASE is identity mapped */ st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE); sync_global_to_guest(vm, st_gva[i]); - st_ipa = (ulong)st_gva[i] | 1; - ret = __vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); - TEST_ASSERT(ret == -1 && errno == EINVAL, "Bad IPA didn't report EINVAL"); - st_ipa = (ulong)st_gva[i]; vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); - - ret = __vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); - TEST_ASSERT(ret == -1 && errno == EEXIST, "Set IPA twice without EEXIST"); } -static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx) +static void steal_time_dump(struct kvm_vm *vm, u32 vcpu_idx) { struct st_time *st = addr_gva2hva(vm, (ulong)st_gva[vcpu_idx]); @@ -205,22 +204,54 @@ static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx) ksft_print_msg(" st_time: %ld\n", st->st_time); } +static void check_steal_time_uapi(void) +{ + struct kvm_vm *vm; + struct kvm_vcpu *vcpu; + u64 st_ipa; + int ret; + + vm = vm_create_with_one_vcpu(&vcpu, NULL); + + struct kvm_device_attr dev = { + .group = KVM_ARM_VCPU_PVTIME_CTRL, + .attr = KVM_ARM_VCPU_PVTIME_IPA, + .addr = (u64)&st_ipa, + }; + + vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &dev); + vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, 1, 0); + virt_map(vm, ST_GPA_BASE, ST_GPA_BASE, 1); + + st_ipa = (ulong)ST_GPA_BASE | 1; + ret = __vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); + TEST_ASSERT(ret == -1 && errno == EINVAL, "Bad IPA didn't report EINVAL"); + + st_ipa = (ulong)ST_GPA_BASE; + vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); + + ret = __vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); + TEST_ASSERT(ret == -1 && errno == EEXIST, "Set IPA twice without EEXIST"); + + kvm_vm_free(vm); +} + #elif defined(__riscv) /* SBI STA shmem must have 64-byte alignment */ #define STEAL_TIME_SIZE ((sizeof(struct sta_struct) + 63) & ~63) -static vm_paddr_t st_gpa[NR_VCPUS]; +static gpa_t st_gpa[NR_VCPUS]; struct sta_struct { - uint32_t sequence; - uint32_t flags; - uint64_t steal; - uint8_t preempted; - uint8_t pad[47]; + u32 sequence; + u32 flags; + u64 steal; + u8 preempted; + u8 pad[47]; } __packed; -static void sta_set_shmem(vm_paddr_t gpa, unsigned long flags) +static void sta_set_shmem(gpa_t gpa, unsigned long flags) { unsigned long lo = (unsigned long)gpa; #if __riscv_xlen == 32 @@ -243,7 +274,7 @@ static void check_status(struct sta_struct *st) static void guest_code(int cpu) { struct sta_struct *st = st_gva[cpu]; - uint32_t sequence; + u32 sequence; long out_val = 0; bool probe; @@ -268,7 +299,7 @@ static void guest_code(int cpu) static bool is_steal_time_supported(struct kvm_vcpu *vcpu) { - uint64_t id = RISCV_SBI_EXT_REG(KVM_RISCV_SBI_EXT_STA); + u64 id = RISCV_SBI_EXT_REG(KVM_RISCV_SBI_EXT_STA); unsigned long enabled = vcpu_get_reg(vcpu, id); TEST_ASSERT(enabled == 0 || enabled == 1, "Expected boolean result"); @@ -276,16 +307,16 @@ static bool is_steal_time_supported(struct kvm_vcpu *vcpu) return enabled; } -static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i) +static void steal_time_init(struct kvm_vcpu *vcpu, u32 i) { /* ST_GPA_BASE is identity mapped */ st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE); - st_gpa[i] = addr_gva2gpa(vcpu->vm, (vm_vaddr_t)st_gva[i]); + st_gpa[i] = addr_gva2gpa(vcpu->vm, (gva_t)st_gva[i]); sync_global_to_guest(vcpu->vm, st_gva[i]); sync_global_to_guest(vcpu->vm, st_gpa[i]); } -static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx) +static void steal_time_dump(struct kvm_vm *vm, u32 vcpu_idx) { struct sta_struct *st = addr_gva2hva(vm, (ulong)st_gva[vcpu_idx]); int i; @@ -301,6 +332,41 @@ static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx) pr_info("\n"); } +static void check_steal_time_uapi(void) +{ + struct kvm_vm *vm; + struct kvm_vcpu *vcpu; + struct kvm_one_reg reg; + u64 shmem; + int ret; + + vm = vm_create_with_one_vcpu(&vcpu, NULL); + + reg.id = KVM_REG_RISCV | + KVM_REG_SIZE_ULONG | + KVM_REG_RISCV_SBI_STATE | + KVM_REG_RISCV_SBI_STA | + KVM_REG_RISCV_SBI_STA_REG(shmem_lo); + reg.addr = (u64)&shmem; + + shmem = ST_GPA_BASE + 1; + ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); + TEST_ASSERT(ret == -1 && errno == EINVAL, + "misaligned STA shmem returns -EINVAL"); + + shmem = ST_GPA_BASE; + ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); + TEST_ASSERT(ret == 0, + "aligned STA shmem succeeds"); + + shmem = INVALID_GPA; + ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); + TEST_ASSERT(ret == 0, + "all-ones for STA shmem succeeds"); + + kvm_vm_free(vm); +} + #elif defined(__loongarch__) /* steal_time must have 64-byte alignment */ @@ -324,7 +390,7 @@ static void check_status(struct kvm_steal_time *st) static void guest_code(int cpu) { - uint32_t version; + u32 version; struct kvm_steal_time *st = st_gva[cpu]; memset(st, 0, sizeof(*st)); @@ -346,11 +412,11 @@ static void guest_code(int cpu) static bool is_steal_time_supported(struct kvm_vcpu *vcpu) { int err; - uint64_t val; + u64 val; struct kvm_device_attr attr = { .group = KVM_LOONGARCH_VCPU_CPUCFG, .attr = CPUCFG_KVM_FEATURE, - .addr = (uint64_t)&val, + .addr = (u64)&val, }; err = __vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &attr); @@ -364,15 +430,15 @@ static bool is_steal_time_supported(struct kvm_vcpu *vcpu) return val & BIT(KVM_FEATURE_STEAL_TIME); } -static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i) +static void steal_time_init(struct kvm_vcpu *vcpu, u32 i) { int err; - uint64_t st_gpa; + u64 st_gpa; struct kvm_vm *vm = vcpu->vm; struct kvm_device_attr attr = { .group = KVM_LOONGARCH_VCPU_PVTIME_CTRL, .attr = KVM_LOONGARCH_VCPU_PVTIME_GPA, - .addr = (uint64_t)&st_gpa, + .addr = (u64)&st_gpa, }; /* ST_GPA_BASE is identity mapped */ @@ -387,7 +453,7 @@ static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i) TEST_ASSERT(err == 0, "Fail to set PV stealtime GPA"); } -static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx) +static void steal_time_dump(struct kvm_vm *vm, u32 vcpu_idx) { struct kvm_steal_time *st = addr_gva2hva(vm, (ulong)st_gva[vcpu_idx]); @@ -397,6 +463,11 @@ static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx) ksft_print_msg(" version: %d\n", st->version); ksft_print_msg(" preempted: %d\n", st->preempted); } + +static void check_steal_time_uapi(void) +{ + +} #endif static void *do_steal_time(void *arg) @@ -465,6 +536,8 @@ int main(int ac, char **av) TEST_REQUIRE(is_steal_time_supported(vcpus[0])); ksft_set_plan(NR_VCPUS); + check_steal_time_uapi(); + /* Run test on each VCPU */ for (i = 0; i < NR_VCPUS; ++i) { steal_time_init(vcpus[i], i); diff --git a/tools/testing/selftests/kvm/system_counter_offset_test.c b/tools/testing/selftests/kvm/system_counter_offset_test.c index 513d421a9bff..dc5e30b7b77f 100644 --- a/tools/testing/selftests/kvm/system_counter_offset_test.c +++ b/tools/testing/selftests/kvm/system_counter_offset_test.c @@ -17,7 +17,7 @@ #ifdef __x86_64__ struct test_case { - uint64_t tsc_offset; + u64 tsc_offset; }; static struct test_case test_cases[] = { @@ -39,12 +39,12 @@ static void setup_system_counter(struct kvm_vcpu *vcpu, struct test_case *test) &test->tsc_offset); } -static uint64_t guest_read_system_counter(struct test_case *test) +static u64 guest_read_system_counter(struct test_case *test) { return rdtsc(); } -static uint64_t host_read_guest_system_counter(struct test_case *test) +static u64 host_read_guest_system_counter(struct test_case *test) { return rdtsc() + test->tsc_offset; } @@ -69,9 +69,9 @@ static void guest_main(void) } } -static void handle_sync(struct ucall *uc, uint64_t start, uint64_t end) +static void handle_sync(struct ucall *uc, u64 start, u64 end) { - uint64_t obs = uc->args[2]; + u64 obs = uc->args[2]; TEST_ASSERT(start <= obs && obs <= end, "unexpected system counter value: %"PRIu64" expected range: [%"PRIu64", %"PRIu64"]", @@ -88,7 +88,7 @@ static void handle_abort(struct ucall *uc) static void enter_guest(struct kvm_vcpu *vcpu) { - uint64_t start, end; + u64 start, end; struct ucall uc; int i; diff --git a/tools/testing/selftests/kvm/x86/amx_test.c b/tools/testing/selftests/kvm/x86/amx_test.c index 37b166260ee3..4e63da2b1889 100644 --- a/tools/testing/selftests/kvm/x86/amx_test.c +++ b/tools/testing/selftests/kvm/x86/amx_test.c @@ -80,10 +80,10 @@ static inline void __tilerelease(void) asm volatile(".byte 0xc4, 0xe2, 0x78, 0x49, 0xc0" ::); } -static inline void __xsavec(struct xstate *xstate, uint64_t rfbm) +static inline void __xsavec(struct xstate *xstate, u64 rfbm) { - uint32_t rfbm_lo = rfbm; - uint32_t rfbm_hi = rfbm >> 32; + u32 rfbm_lo = rfbm; + u32 rfbm_hi = rfbm >> 32; asm volatile("xsavec (%%rdi)" : : "D" (xstate), "a" (rfbm_lo), "d" (rfbm_hi) @@ -236,7 +236,7 @@ int main(int argc, char *argv[]) struct kvm_x86_state *state; struct kvm_x86_state *tile_state = NULL; int xsave_restore_size; - vm_vaddr_t amx_cfg, tiledata, xstate; + gva_t amx_cfg, tiledata, xstate; struct ucall uc; int ret; @@ -263,15 +263,15 @@ int main(int argc, char *argv[]) vcpu_regs_get(vcpu, ®s1); /* amx cfg for guest_code */ - amx_cfg = vm_vaddr_alloc_page(vm); + amx_cfg = vm_alloc_page(vm); memset(addr_gva2hva(vm, amx_cfg), 0x0, getpagesize()); /* amx tiledata for guest_code */ - tiledata = vm_vaddr_alloc_pages(vm, 2); + tiledata = vm_alloc_pages(vm, 2); memset(addr_gva2hva(vm, tiledata), rand() | 1, 2 * getpagesize()); /* XSAVE state for guest_code */ - xstate = vm_vaddr_alloc_pages(vm, DIV_ROUND_UP(XSAVE_SIZE, PAGE_SIZE)); + xstate = vm_alloc_pages(vm, DIV_ROUND_UP(XSAVE_SIZE, PAGE_SIZE)); memset(addr_gva2hva(vm, xstate), 0, PAGE_SIZE * DIV_ROUND_UP(XSAVE_SIZE, PAGE_SIZE)); vcpu_args_set(vcpu, 3, amx_cfg, tiledata, xstate); diff --git a/tools/testing/selftests/kvm/x86/aperfmperf_test.c b/tools/testing/selftests/kvm/x86/aperfmperf_test.c index 8b15a13df939..c91660103137 100644 --- a/tools/testing/selftests/kvm/x86/aperfmperf_test.c +++ b/tools/testing/selftests/kvm/x86/aperfmperf_test.c @@ -35,9 +35,9 @@ static int open_dev_msr(int cpu) return open_path_or_exit(path, O_RDONLY); } -static uint64_t read_dev_msr(int msr_fd, uint32_t msr) +static u64 read_dev_msr(int msr_fd, u32 msr) { - uint64_t data; + u64 data; ssize_t rc; rc = pread(msr_fd, &data, sizeof(data), msr); @@ -107,8 +107,8 @@ static void guest_code(void *nested_test_data) static void guest_no_aperfmperf(void) { - uint64_t msr_val; - uint8_t vector; + u64 msr_val; + u8 vector; vector = rdmsr_safe(MSR_IA32_APERF, &msr_val); GUEST_ASSERT(vector == GP_VECTOR); @@ -122,8 +122,8 @@ static void guest_no_aperfmperf(void) int main(int argc, char *argv[]) { const bool has_nested = kvm_cpu_has(X86_FEATURE_SVM) || kvm_cpu_has(X86_FEATURE_VMX); - uint64_t host_aperf_before, host_mperf_before; - vm_vaddr_t nested_test_data_gva; + u64 host_aperf_before, host_mperf_before; + gva_t nested_test_data_gva; struct kvm_vcpu *vcpu; struct kvm_vm *vm; int msr_fd, cpu, i; @@ -166,8 +166,8 @@ int main(int argc, char *argv[]) host_mperf_before = read_dev_msr(msr_fd, MSR_IA32_MPERF); for (i = 0; i <= NUM_ITERATIONS * (1 + has_nested); i++) { - uint64_t host_aperf_after, host_mperf_after; - uint64_t guest_aperf, guest_mperf; + u64 host_aperf_after, host_mperf_after; + u64 guest_aperf, guest_mperf; struct ucall uc; vcpu_run(vcpu); diff --git a/tools/testing/selftests/kvm/x86/apic_bus_clock_test.c b/tools/testing/selftests/kvm/x86/apic_bus_clock_test.c index f8916bb34405..404f0028e110 100644 --- a/tools/testing/selftests/kvm/x86/apic_bus_clock_test.c +++ b/tools/testing/selftests/kvm/x86/apic_bus_clock_test.c @@ -19,8 +19,8 @@ * timer frequency. */ static const struct { - const uint32_t tdcr; - const uint32_t divide_count; + const u32 tdcr; + const u32 divide_count; } tdcrs[] = { {0x0, 2}, {0x1, 4}, @@ -42,12 +42,12 @@ static void apic_enable(void) xapic_enable(); } -static uint32_t apic_read_reg(unsigned int reg) +static u32 apic_read_reg(unsigned int reg) { return is_x2apic ? x2apic_read_reg(reg) : xapic_read_reg(reg); } -static void apic_write_reg(unsigned int reg, uint32_t val) +static void apic_write_reg(unsigned int reg, u32 val) { if (is_x2apic) x2apic_write_reg(reg, val); @@ -55,12 +55,12 @@ static void apic_write_reg(unsigned int reg, uint32_t val) xapic_write_reg(reg, val); } -static void apic_guest_code(uint64_t apic_hz, uint64_t delay_ms) +static void apic_guest_code(u64 apic_hz, u64 delay_ms) { - uint64_t tsc_hz = guest_tsc_khz * 1000; - const uint32_t tmict = ~0u; - uint64_t tsc0, tsc1, freq; - uint32_t tmcct; + u64 tsc_hz = guest_tsc_khz * 1000; + const u32 tmict = ~0u; + u64 tsc0, tsc1, freq; + u32 tmcct; int i; apic_enable(); @@ -121,7 +121,7 @@ static void test_apic_bus_clock(struct kvm_vcpu *vcpu) } } -static void run_apic_bus_clock_test(uint64_t apic_hz, uint64_t delay_ms, +static void run_apic_bus_clock_test(u64 apic_hz, u64 delay_ms, bool x2apic) { struct kvm_vcpu *vcpu; @@ -168,8 +168,8 @@ int main(int argc, char *argv[]) * Arbitrarilty default to 25MHz for the APIC bus frequency, which is * different enough from the default 1GHz to be interesting. */ - uint64_t apic_hz = 25 * 1000 * 1000; - uint64_t delay_ms = 100; + u64 apic_hz = 25 * 1000 * 1000; + u64 delay_ms = 100; int opt; TEST_REQUIRE(kvm_has_cap(KVM_CAP_X86_APIC_BUS_CYCLES_NS)); diff --git a/tools/testing/selftests/kvm/x86/cpuid_test.c b/tools/testing/selftests/kvm/x86/cpuid_test.c index f9ed14996977..ef0ddd240887 100644 --- a/tools/testing/selftests/kvm/x86/cpuid_test.c +++ b/tools/testing/selftests/kvm/x86/cpuid_test.c @@ -140,10 +140,10 @@ static void run_vcpu(struct kvm_vcpu *vcpu, int stage) } } -struct kvm_cpuid2 *vcpu_alloc_cpuid(struct kvm_vm *vm, vm_vaddr_t *p_gva, struct kvm_cpuid2 *cpuid) +struct kvm_cpuid2 *vcpu_alloc_cpuid(struct kvm_vm *vm, gva_t *p_gva, struct kvm_cpuid2 *cpuid) { int size = sizeof(*cpuid) + cpuid->nent * sizeof(cpuid->entries[0]); - vm_vaddr_t gva = vm_vaddr_alloc(vm, size, KVM_UTIL_MIN_VADDR); + gva_t gva = vm_alloc(vm, size, KVM_UTIL_MIN_VADDR); struct kvm_cpuid2 *guest_cpuids = addr_gva2hva(vm, gva); memcpy(guest_cpuids, cpuid, size); @@ -217,7 +217,7 @@ static void test_get_cpuid2(struct kvm_vcpu *vcpu) int main(void) { struct kvm_vcpu *vcpu; - vm_vaddr_t cpuid_gva; + gva_t cpuid_gva; struct kvm_vm *vm; int stage; diff --git a/tools/testing/selftests/kvm/x86/debug_regs.c b/tools/testing/selftests/kvm/x86/debug_regs.c index 2d814c1d1dc4..0dfaf03cd0a0 100644 --- a/tools/testing/selftests/kvm/x86/debug_regs.c +++ b/tools/testing/selftests/kvm/x86/debug_regs.c @@ -16,7 +16,7 @@ #define IRQ_VECTOR 0xAA /* For testing data access debug BP */ -uint32_t guest_value; +u32 guest_value; extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start; @@ -86,7 +86,7 @@ int main(void) struct kvm_run *run; struct kvm_vm *vm; struct ucall uc; - uint64_t cmd; + u64 cmd; int i; /* Instruction lengths starting at ss_start */ int ss_size[6] = { diff --git a/tools/testing/selftests/kvm/x86/dirty_log_page_splitting_test.c b/tools/testing/selftests/kvm/x86/dirty_log_page_splitting_test.c index b0d2b04a7ff2..388ba4101f97 100644 --- a/tools/testing/selftests/kvm/x86/dirty_log_page_splitting_test.c +++ b/tools/testing/selftests/kvm/x86/dirty_log_page_splitting_test.c @@ -23,7 +23,7 @@ #define SLOTS 2 #define ITERATIONS 2 -static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE; +static u64 guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE; static enum vm_mem_backing_src_type backing_src = VM_MEM_SRC_ANONYMOUS_HUGETLB; @@ -33,10 +33,10 @@ static int iteration; static int vcpu_last_completed_iteration[KVM_MAX_VCPUS]; struct kvm_page_stats { - uint64_t pages_4k; - uint64_t pages_2m; - uint64_t pages_1g; - uint64_t hugepages; + u64 pages_4k; + u64 pages_2m; + u64 pages_1g; + u64 hugepages; }; static void get_page_stats(struct kvm_vm *vm, struct kvm_page_stats *stats, const char *stage) @@ -89,9 +89,9 @@ static void run_test(enum vm_guest_mode mode, void *unused) { struct kvm_vm *vm; unsigned long **bitmaps; - uint64_t guest_num_pages; - uint64_t host_num_pages; - uint64_t pages_per_slot; + u64 guest_num_pages; + u64 host_num_pages; + u64 pages_per_slot; int i; struct kvm_page_stats stats_populated; struct kvm_page_stats stats_dirty_logging_enabled; diff --git a/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c b/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c index af7c90103396..5b3aef109cfc 100644 --- a/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c +++ b/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c @@ -29,13 +29,13 @@ * SMI handler: runs in real-address mode. * Reports SMRAM_STAGE via port IO, then does RSM. */ -static uint8_t smi_handler[] = { +static u8 smi_handler[] = { 0xb0, SMRAM_STAGE, /* mov $SMRAM_STAGE, %al */ 0xe4, SYNC_PORT, /* in $SYNC_PORT, %al */ 0x0f, 0xaa, /* rsm */ }; -static inline void sync_with_host(uint64_t phase) +static inline void sync_with_host(u64 phase) { asm volatile("in $" XSTR(SYNC_PORT) ", %%al \n" : "+a" (phase)); @@ -73,7 +73,7 @@ static void guest_code(struct vmx_pages *vmx_pages, int main(int argc, char *argv[]) { - vm_vaddr_t vmx_pages_gva = 0, hv_pages_gva = 0; + gva_t vmx_pages_gva = 0, hv_pages_gva = 0; struct hyperv_test_pages *hv; struct hv_enlightened_vmcs *evmcs; struct kvm_vcpu *vcpu; diff --git a/tools/testing/selftests/kvm/x86/fastops_test.c b/tools/testing/selftests/kvm/x86/fastops_test.c index 8926cfe0e209..c0d30ccd8767 100644 --- a/tools/testing/selftests/kvm/x86/fastops_test.c +++ b/tools/testing/selftests/kvm/x86/fastops_test.c @@ -15,7 +15,7 @@ "pop %[flags]\n\t" #define flags_constraint(flags_val) [flags]"=r"(flags_val) -#define bt_constraint(__bt_val) [bt_val]"rm"((uint32_t)__bt_val) +#define bt_constraint(__bt_val) [bt_val]"rm"((u32)__bt_val) #define guest_execute_fastop_1(FEP, insn, __val, __flags) \ ({ \ @@ -28,17 +28,17 @@ #define guest_test_fastop_1(insn, type_t, __val) \ ({ \ type_t val = __val, ex_val = __val, input = __val; \ - uint64_t flags, ex_flags; \ + u64 flags, ex_flags; \ \ guest_execute_fastop_1("", insn, ex_val, ex_flags); \ guest_execute_fastop_1(KVM_FEP, insn, val, flags); \ \ __GUEST_ASSERT(val == ex_val, \ "Wanted 0x%lx for '%s 0x%lx', got 0x%lx", \ - (uint64_t)ex_val, insn, (uint64_t)input, (uint64_t)val); \ + (u64)ex_val, insn, (u64)input, (u64)val); \ __GUEST_ASSERT(flags == ex_flags, \ "Wanted flags 0x%lx for '%s 0x%lx', got 0x%lx", \ - ex_flags, insn, (uint64_t)input, flags); \ + ex_flags, insn, (u64)input, flags); \ }) #define guest_execute_fastop_2(FEP, insn, __input, __output, __flags) \ @@ -52,18 +52,18 @@ #define guest_test_fastop_2(insn, type_t, __val1, __val2) \ ({ \ type_t input = __val1, input2 = __val2, output = __val2, ex_output = __val2; \ - uint64_t flags, ex_flags; \ + u64 flags, ex_flags; \ \ guest_execute_fastop_2("", insn, input, ex_output, ex_flags); \ guest_execute_fastop_2(KVM_FEP, insn, input, output, flags); \ \ __GUEST_ASSERT(output == ex_output, \ "Wanted 0x%lx for '%s 0x%lx 0x%lx', got 0x%lx", \ - (uint64_t)ex_output, insn, (uint64_t)input, \ - (uint64_t)input2, (uint64_t)output); \ + (u64)ex_output, insn, (u64)input, \ + (u64)input2, (u64)output); \ __GUEST_ASSERT(flags == ex_flags, \ "Wanted flags 0x%lx for '%s 0x%lx, 0x%lx', got 0x%lx", \ - ex_flags, insn, (uint64_t)input, (uint64_t)input2, flags); \ + ex_flags, insn, (u64)input, (u64)input2, flags); \ }) #define guest_execute_fastop_cl(FEP, insn, __shift, __output, __flags) \ @@ -77,25 +77,25 @@ #define guest_test_fastop_cl(insn, type_t, __val1, __val2) \ ({ \ type_t output = __val2, ex_output = __val2, input = __val2; \ - uint8_t shift = __val1; \ - uint64_t flags, ex_flags; \ + u8 shift = __val1; \ + u64 flags, ex_flags; \ \ guest_execute_fastop_cl("", insn, shift, ex_output, ex_flags); \ guest_execute_fastop_cl(KVM_FEP, insn, shift, output, flags); \ \ __GUEST_ASSERT(output == ex_output, \ "Wanted 0x%lx for '%s 0x%x, 0x%lx', got 0x%lx", \ - (uint64_t)ex_output, insn, shift, (uint64_t)input, \ - (uint64_t)output); \ + (u64)ex_output, insn, shift, (u64)input, \ + (u64)output); \ __GUEST_ASSERT(flags == ex_flags, \ "Wanted flags 0x%lx for '%s 0x%x, 0x%lx', got 0x%lx", \ - ex_flags, insn, shift, (uint64_t)input, flags); \ + ex_flags, insn, shift, (u64)input, flags); \ }) #define guest_execute_fastop_div(__KVM_ASM_SAFE, insn, __a, __d, __rm, __flags) \ ({ \ - uint64_t ign_error_code; \ - uint8_t vector; \ + u64 ign_error_code; \ + u8 vector; \ \ __asm__ __volatile__(fastop(__KVM_ASM_SAFE(insn " %[denom]")) \ : "+a"(__a), "+d"(__d), flags_constraint(__flags), \ @@ -109,8 +109,8 @@ ({ \ type_t _a = __val1, _d = __val1, rm = __val2; \ type_t a = _a, d = _d, ex_a = _a, ex_d = _d; \ - uint64_t flags, ex_flags; \ - uint8_t v, ex_v; \ + u64 flags, ex_flags; \ + u8 v, ex_v; \ \ ex_v = guest_execute_fastop_div(KVM_ASM_SAFE, insn, ex_a, ex_d, rm, ex_flags); \ v = guest_execute_fastop_div(KVM_ASM_SAFE_FEP, insn, a, d, rm, flags); \ @@ -118,17 +118,17 @@ GUEST_ASSERT_EQ(v, ex_v); \ __GUEST_ASSERT(v == ex_v, \ "Wanted vector 0x%x for '%s 0x%lx:0x%lx/0x%lx', got 0x%x", \ - ex_v, insn, (uint64_t)_a, (uint64_t)_d, (uint64_t)rm, v); \ + ex_v, insn, (u64)_a, (u64)_d, (u64)rm, v); \ __GUEST_ASSERT(a == ex_a && d == ex_d, \ "Wanted 0x%lx:0x%lx for '%s 0x%lx:0x%lx/0x%lx', got 0x%lx:0x%lx",\ - (uint64_t)ex_a, (uint64_t)ex_d, insn, (uint64_t)_a, \ - (uint64_t)_d, (uint64_t)rm, (uint64_t)a, (uint64_t)d); \ + (u64)ex_a, (u64)ex_d, insn, (u64)_a, \ + (u64)_d, (u64)rm, (u64)a, (u64)d); \ __GUEST_ASSERT(v || ex_v || (flags == ex_flags), \ "Wanted flags 0x%lx for '%s 0x%lx:0x%lx/0x%lx', got 0x%lx", \ - ex_flags, insn, (uint64_t)_a, (uint64_t)_d, (uint64_t)rm, flags);\ + ex_flags, insn, (u64)_a, (u64)_d, (u64)rm, flags);\ }) -static const uint64_t vals[] = { +static const u64 vals[] = { 0, 1, 2, @@ -185,10 +185,10 @@ if (sizeof(type_t) != 1) { \ static void guest_code(void) { - guest_test_fastops(uint8_t, "b"); - guest_test_fastops(uint16_t, "w"); - guest_test_fastops(uint32_t, "l"); - guest_test_fastops(uint64_t, "q"); + guest_test_fastops(u8, "b"); + guest_test_fastops(u16, "w"); + guest_test_fastops(u32, "l"); + guest_test_fastops(u64, "q"); GUEST_DONE(); } diff --git a/tools/testing/selftests/kvm/x86/feature_msrs_test.c b/tools/testing/selftests/kvm/x86/feature_msrs_test.c index a72f13ae2edb..158550701771 100644 --- a/tools/testing/selftests/kvm/x86/feature_msrs_test.c +++ b/tools/testing/selftests/kvm/x86/feature_msrs_test.c @@ -12,7 +12,7 @@ #include "kvm_util.h" #include "processor.h" -static bool is_kvm_controlled_msr(uint32_t msr) +static bool is_kvm_controlled_msr(u32 msr) { return msr == MSR_IA32_VMX_CR0_FIXED1 || msr == MSR_IA32_VMX_CR4_FIXED1; } @@ -21,7 +21,7 @@ static bool is_kvm_controlled_msr(uint32_t msr) * For VMX MSRs with a "true" variant, KVM requires userspace to set the "true" * MSR, and doesn't allow setting the hidden version. */ -static bool is_hidden_vmx_msr(uint32_t msr) +static bool is_hidden_vmx_msr(u32 msr) { switch (msr) { case MSR_IA32_VMX_PINBASED_CTLS: @@ -34,15 +34,15 @@ static bool is_hidden_vmx_msr(uint32_t msr) } } -static bool is_quirked_msr(uint32_t msr) +static bool is_quirked_msr(u32 msr) { return msr != MSR_AMD64_DE_CFG; } -static void test_feature_msr(uint32_t msr) +static void test_feature_msr(u32 msr) { - const uint64_t supported_mask = kvm_get_feature_msr(msr); - uint64_t reset_value = is_quirked_msr(msr) ? supported_mask : 0; + const u64 supported_mask = kvm_get_feature_msr(msr); + u64 reset_value = is_quirked_msr(msr) ? supported_mask : 0; struct kvm_vcpu *vcpu; struct kvm_vm *vm; diff --git a/tools/testing/selftests/kvm/x86/fix_hypercall_test.c b/tools/testing/selftests/kvm/x86/fix_hypercall_test.c index 762628f7d4ba..753a0e730ea8 100644 --- a/tools/testing/selftests/kvm/x86/fix_hypercall_test.c +++ b/tools/testing/selftests/kvm/x86/fix_hypercall_test.c @@ -26,18 +26,18 @@ static void guest_ud_handler(struct ex_regs *regs) regs->rip += HYPERCALL_INSN_SIZE; } -static const uint8_t vmx_vmcall[HYPERCALL_INSN_SIZE] = { 0x0f, 0x01, 0xc1 }; -static const uint8_t svm_vmmcall[HYPERCALL_INSN_SIZE] = { 0x0f, 0x01, 0xd9 }; +static const u8 vmx_vmcall[HYPERCALL_INSN_SIZE] = { 0x0f, 0x01, 0xc1 }; +static const u8 svm_vmmcall[HYPERCALL_INSN_SIZE] = { 0x0f, 0x01, 0xd9 }; -extern uint8_t hypercall_insn[HYPERCALL_INSN_SIZE]; -static uint64_t do_sched_yield(uint8_t apic_id) +extern u8 hypercall_insn[HYPERCALL_INSN_SIZE]; +static u64 do_sched_yield(u8 apic_id) { - uint64_t ret; + u64 ret; asm volatile("hypercall_insn:\n\t" ".byte 0xcc,0xcc,0xcc\n\t" : "=a"(ret) - : "a"((uint64_t)KVM_HC_SCHED_YIELD), "b"((uint64_t)apic_id) + : "a"((u64)KVM_HC_SCHED_YIELD), "b"((u64)apic_id) : "memory"); return ret; @@ -45,14 +45,14 @@ static uint64_t do_sched_yield(uint8_t apic_id) static void guest_main(void) { - const uint8_t *native_hypercall_insn; - const uint8_t *other_hypercall_insn; - uint64_t ret; + const u8 *native_hypercall_insn; + const u8 *other_hypercall_insn; + u64 ret; if (host_cpu_is_intel) { native_hypercall_insn = vmx_vmcall; other_hypercall_insn = svm_vmmcall; - } else if (host_cpu_is_amd) { + } else if (host_cpu_is_amd_compatible) { native_hypercall_insn = svm_vmmcall; other_hypercall_insn = vmx_vmcall; } else { @@ -72,7 +72,7 @@ static void guest_main(void) * the "right" hypercall. */ if (quirk_disabled) { - GUEST_ASSERT(ret == (uint64_t)-EFAULT); + GUEST_ASSERT(ret == (u64)-EFAULT); GUEST_ASSERT(!memcmp(other_hypercall_insn, hypercall_insn, HYPERCALL_INSN_SIZE)); } else { diff --git a/tools/testing/selftests/kvm/x86/flds_emulation.h b/tools/testing/selftests/kvm/x86/flds_emulation.h index 37b1a9f52864..fd6b6c67199a 100644 --- a/tools/testing/selftests/kvm/x86/flds_emulation.h +++ b/tools/testing/selftests/kvm/x86/flds_emulation.h @@ -12,7 +12,7 @@ * KVM to emulate the instruction (e.g. by providing an MMIO address) to * exercise emulation failures. */ -static inline void flds(uint64_t address) +static inline void flds(u64 address) { __asm__ __volatile__(FLDS_MEM_EAX :: "a"(address)); } @@ -21,8 +21,8 @@ static inline void handle_flds_emulation_failure_exit(struct kvm_vcpu *vcpu) { struct kvm_run *run = vcpu->run; struct kvm_regs regs; - uint8_t *insn_bytes; - uint64_t flags; + u8 *insn_bytes; + u64 flags; TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_INTERNAL_ERROR); diff --git a/tools/testing/selftests/kvm/x86/hwcr_msr_test.c b/tools/testing/selftests/kvm/x86/hwcr_msr_test.c index 10b1b0ba374e..8e20a03b3329 100644 --- a/tools/testing/selftests/kvm/x86/hwcr_msr_test.c +++ b/tools/testing/selftests/kvm/x86/hwcr_msr_test.c @@ -10,11 +10,11 @@ void test_hwcr_bit(struct kvm_vcpu *vcpu, unsigned int bit) { - const uint64_t ignored = BIT_ULL(3) | BIT_ULL(6) | BIT_ULL(8); - const uint64_t valid = BIT_ULL(18) | BIT_ULL(24); - const uint64_t legal = ignored | valid; - uint64_t val = BIT_ULL(bit); - uint64_t actual; + const u64 ignored = BIT_ULL(3) | BIT_ULL(6) | BIT_ULL(8); + const u64 valid = BIT_ULL(18) | BIT_ULL(24); + const u64 legal = ignored | valid; + u64 val = BIT_ULL(bit); + u64 actual; int r; r = _vcpu_set_msr(vcpu, MSR_K7_HWCR, val); diff --git a/tools/testing/selftests/kvm/x86/hyperv_clock.c b/tools/testing/selftests/kvm/x86/hyperv_clock.c index e058bc676cd6..c083cea546dc 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_clock.c +++ b/tools/testing/selftests/kvm/x86/hyperv_clock.c @@ -98,7 +98,7 @@ static inline void check_tsc_msr_tsc_page(struct ms_hyperv_tsc_page *tsc_page) GUEST_ASSERT(r2 >= t1 && r2 - t2 < 100000); } -static void guest_main(struct ms_hyperv_tsc_page *tsc_page, vm_paddr_t tsc_page_gpa) +static void guest_main(struct ms_hyperv_tsc_page *tsc_page, gpa_t tsc_page_gpa) { u64 tsc_scale, tsc_offset; @@ -208,7 +208,7 @@ int main(void) struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct ucall uc; - vm_vaddr_t tsc_page_gva; + gva_t tsc_page_gva; int stage; TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_TIME)); @@ -218,7 +218,7 @@ int main(void) vcpu_set_hv_cpuid(vcpu); - tsc_page_gva = vm_vaddr_alloc_page(vm); + tsc_page_gva = vm_alloc_page(vm); memset(addr_gva2hva(vm, tsc_page_gva), 0x0, getpagesize()); TEST_ASSERT((addr_gva2gpa(vm, tsc_page_gva) & (getpagesize() - 1)) == 0, "TSC page has to be page aligned"); diff --git a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c index 74cf19661309..c7fa114aee20 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_evmcs.c +++ b/tools/testing/selftests/kvm/x86/hyperv_evmcs.c @@ -30,7 +30,7 @@ static void guest_nmi_handler(struct ex_regs *regs) { } -static inline void rdmsr_from_l2(uint32_t msr) +static inline void rdmsr_from_l2(u32 msr) { /* Currently, L1 doesn't preserve GPRs during vmexits. */ __asm__ __volatile__ ("rdmsr" : : "c"(msr) : @@ -76,7 +76,7 @@ void l2_guest_code(void) } void guest_code(struct vmx_pages *vmx_pages, struct hyperv_test_pages *hv_pages, - vm_vaddr_t hv_hcall_page_gpa) + gpa_t hv_hcall_page_gpa) { #define L2_GUEST_STACK_SIZE 64 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; @@ -231,8 +231,8 @@ static struct kvm_vcpu *save_restore_vm(struct kvm_vm *vm, int main(int argc, char *argv[]) { - vm_vaddr_t vmx_pages_gva = 0, hv_pages_gva = 0; - vm_vaddr_t hcall_page; + gva_t vmx_pages_gva = 0, hv_pages_gva = 0; + gva_t hcall_page; struct kvm_vcpu *vcpu; struct kvm_vm *vm; @@ -246,7 +246,7 @@ int main(int argc, char *argv[]) vm = vm_create_with_one_vcpu(&vcpu, guest_code); - hcall_page = vm_vaddr_alloc_pages(vm, 1); + hcall_page = vm_alloc_pages(vm, 1); memset(addr_gva2hva(vm, hcall_page), 0x0, getpagesize()); vcpu_set_hv_cpuid(vcpu); diff --git a/tools/testing/selftests/kvm/x86/hyperv_extended_hypercalls.c b/tools/testing/selftests/kvm/x86/hyperv_extended_hypercalls.c index 949e08e98f31..ae047db7b1be 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_extended_hypercalls.c +++ b/tools/testing/selftests/kvm/x86/hyperv_extended_hypercalls.c @@ -15,19 +15,19 @@ /* Any value is fine */ #define EXT_CAPABILITIES 0xbull -static void guest_code(vm_paddr_t in_pg_gpa, vm_paddr_t out_pg_gpa, - vm_vaddr_t out_pg_gva) +static void guest_code(gpa_t in_pg_gpa, gpa_t out_pg_gpa, + gva_t out_pg_gva) { - uint64_t *output_gva; + u64 *output_gva; wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID); wrmsr(HV_X64_MSR_HYPERCALL, in_pg_gpa); - output_gva = (uint64_t *)out_pg_gva; + output_gva = (u64 *)out_pg_gva; hyperv_hypercall(HV_EXT_CALL_QUERY_CAPABILITIES, in_pg_gpa, out_pg_gpa); - /* TLFS states output will be a uint64_t value */ + /* TLFS states output will be a u64 value */ GUEST_ASSERT_EQ(*output_gva, EXT_CAPABILITIES); GUEST_DONE(); @@ -35,12 +35,12 @@ static void guest_code(vm_paddr_t in_pg_gpa, vm_paddr_t out_pg_gpa, int main(void) { - vm_vaddr_t hcall_out_page; - vm_vaddr_t hcall_in_page; + gva_t hcall_out_page; + gva_t hcall_in_page; struct kvm_vcpu *vcpu; struct kvm_run *run; struct kvm_vm *vm; - uint64_t *outval; + u64 *outval; struct ucall uc; TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_CPUID)); @@ -57,11 +57,11 @@ int main(void) vcpu_set_hv_cpuid(vcpu); /* Hypercall input */ - hcall_in_page = vm_vaddr_alloc_pages(vm, 1); + hcall_in_page = vm_alloc_pages(vm, 1); memset(addr_gva2hva(vm, hcall_in_page), 0x0, vm->page_size); /* Hypercall output */ - hcall_out_page = vm_vaddr_alloc_pages(vm, 1); + hcall_out_page = vm_alloc_pages(vm, 1); memset(addr_gva2hva(vm, hcall_out_page), 0x0, vm->page_size); vcpu_args_set(vcpu, 3, addr_gva2gpa(vm, hcall_in_page), diff --git a/tools/testing/selftests/kvm/x86/hyperv_features.c b/tools/testing/selftests/kvm/x86/hyperv_features.c index 130b9ce7e5dd..7347f1fe5157 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_features.c +++ b/tools/testing/selftests/kvm/x86/hyperv_features.c @@ -22,27 +22,27 @@ KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EBX, 0) struct msr_data { - uint32_t idx; + u32 idx; bool fault_expected; bool write; u64 write_val; }; struct hcall_data { - uint64_t control; - uint64_t expect; + u64 control; + u64 expect; bool ud_expected; }; -static bool is_write_only_msr(uint32_t msr) +static bool is_write_only_msr(u32 msr) { return msr == HV_X64_MSR_EOI; } static void guest_msr(struct msr_data *msr) { - uint8_t vector = 0; - uint64_t msr_val = 0; + u8 vector = 0; + u64 msr_val = 0; GUEST_ASSERT(msr->idx); @@ -82,10 +82,10 @@ done: GUEST_DONE(); } -static void guest_hcall(vm_vaddr_t pgs_gpa, struct hcall_data *hcall) +static void guest_hcall(gpa_t pgs_gpa, struct hcall_data *hcall) { u64 res, input, output; - uint8_t vector; + u8 vector; GUEST_ASSERT_NE(hcall->control, 0); @@ -134,14 +134,14 @@ static void guest_test_msrs_access(void) struct kvm_vm *vm; struct ucall uc; int stage = 0; - vm_vaddr_t msr_gva; + gva_t msr_gva; struct msr_data *msr; bool has_invtsc = kvm_cpu_has(X86_FEATURE_INVTSC); while (true) { vm = vm_create_with_one_vcpu(&vcpu, guest_msr); - msr_gva = vm_vaddr_alloc_page(vm); + msr_gva = vm_alloc_page(vm); memset(addr_gva2hva(vm, msr_gva), 0x0, getpagesize()); msr = addr_gva2hva(vm, msr_gva); @@ -523,17 +523,17 @@ static void guest_test_hcalls_access(void) struct kvm_vm *vm; struct ucall uc; int stage = 0; - vm_vaddr_t hcall_page, hcall_params; + gva_t hcall_page, hcall_params; struct hcall_data *hcall; while (true) { vm = vm_create_with_one_vcpu(&vcpu, guest_hcall); /* Hypercall input/output */ - hcall_page = vm_vaddr_alloc_pages(vm, 2); + hcall_page = vm_alloc_pages(vm, 2); memset(addr_gva2hva(vm, hcall_page), 0x0, 2 * getpagesize()); - hcall_params = vm_vaddr_alloc_page(vm); + hcall_params = vm_alloc_page(vm); memset(addr_gva2hva(vm, hcall_params), 0x0, getpagesize()); hcall = addr_gva2hva(vm, hcall_params); diff --git a/tools/testing/selftests/kvm/x86/hyperv_ipi.c b/tools/testing/selftests/kvm/x86/hyperv_ipi.c index ca61836c4e32..771535f9aad3 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_ipi.c +++ b/tools/testing/selftests/kvm/x86/hyperv_ipi.c @@ -18,7 +18,7 @@ #define IPI_VECTOR 0xfe -static volatile uint64_t ipis_rcvd[RECEIVER_VCPU_ID_2 + 1]; +static volatile u64 ipis_rcvd[RECEIVER_VCPU_ID_2 + 1]; struct hv_vpset { u64 format; @@ -45,13 +45,13 @@ struct hv_send_ipi_ex { struct hv_vpset vp_set; }; -static inline void hv_init(vm_vaddr_t pgs_gpa) +static inline void hv_init(gpa_t pgs_gpa) { wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID); wrmsr(HV_X64_MSR_HYPERCALL, pgs_gpa); } -static void receiver_code(void *hcall_page, vm_vaddr_t pgs_gpa) +static void receiver_code(void *hcall_page, gpa_t pgs_gpa) { u32 vcpu_id; @@ -85,7 +85,7 @@ static inline void nop_loop(void) asm volatile("nop"); } -static void sender_guest_code(void *hcall_page, vm_vaddr_t pgs_gpa) +static void sender_guest_code(void *hcall_page, gpa_t pgs_gpa) { struct hv_send_ipi *ipi = (struct hv_send_ipi *)hcall_page; struct hv_send_ipi_ex *ipi_ex = (struct hv_send_ipi_ex *)hcall_page; @@ -243,7 +243,7 @@ int main(int argc, char *argv[]) { struct kvm_vm *vm; struct kvm_vcpu *vcpu[3]; - vm_vaddr_t hcall_page; + gva_t hcall_page; pthread_t threads[2]; int stage = 1, r; struct ucall uc; @@ -253,7 +253,7 @@ int main(int argc, char *argv[]) vm = vm_create_with_one_vcpu(&vcpu[0], sender_guest_code); /* Hypercall input/output */ - hcall_page = vm_vaddr_alloc_pages(vm, 2); + hcall_page = vm_alloc_pages(vm, 2); memset(addr_gva2hva(vm, hcall_page), 0x0, 2 * getpagesize()); diff --git a/tools/testing/selftests/kvm/x86/hyperv_svm_test.c b/tools/testing/selftests/kvm/x86/hyperv_svm_test.c index 0ddb63229bcb..7a62f6a9d606 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_svm_test.c +++ b/tools/testing/selftests/kvm/x86/hyperv_svm_test.c @@ -21,7 +21,7 @@ #define L2_GUEST_STACK_SIZE 256 /* Exit to L1 from L2 with RDMSR instruction */ -static inline void rdmsr_from_l2(uint32_t msr) +static inline void rdmsr_from_l2(u32 msr) { /* Currently, L1 doesn't preserve GPRs during vmexits. */ __asm__ __volatile__ ("rdmsr" : : "c"(msr) : @@ -67,7 +67,7 @@ void l2_guest_code(void) static void __attribute__((__flatten__)) guest_code(struct svm_test_data *svm, struct hyperv_test_pages *hv_pages, - vm_vaddr_t pgs_gpa) + gpa_t pgs_gpa) { unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; struct vmcb *vmcb = svm->vmcb; @@ -149,8 +149,8 @@ static void __attribute__((__flatten__)) guest_code(struct svm_test_data *svm, int main(int argc, char *argv[]) { - vm_vaddr_t nested_gva = 0, hv_pages_gva = 0; - vm_vaddr_t hcall_page; + gva_t nested_gva = 0, hv_pages_gva = 0; + gva_t hcall_page; struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct ucall uc; @@ -165,7 +165,7 @@ int main(int argc, char *argv[]) vcpu_alloc_svm(vm, &nested_gva); vcpu_alloc_hyperv_test_pages(vm, &hv_pages_gva); - hcall_page = vm_vaddr_alloc_pages(vm, 1); + hcall_page = vm_alloc_pages(vm, 1); memset(addr_gva2hva(vm, hcall_page), 0x0, getpagesize()); vcpu_args_set(vcpu, 3, nested_gva, hv_pages_gva, addr_gva2gpa(vm, hcall_page)); diff --git a/tools/testing/selftests/kvm/x86/hyperv_tlb_flush.c b/tools/testing/selftests/kvm/x86/hyperv_tlb_flush.c index c542cc4762b1..15ee8b7bfc11 100644 --- a/tools/testing/selftests/kvm/x86/hyperv_tlb_flush.c +++ b/tools/testing/selftests/kvm/x86/hyperv_tlb_flush.c @@ -61,14 +61,14 @@ struct hv_tlb_flush_ex { * - GVAs of the test pages' PTEs */ struct test_data { - vm_vaddr_t hcall_gva; - vm_paddr_t hcall_gpa; - vm_vaddr_t test_pages; - vm_vaddr_t test_pages_pte[NTEST_PAGES]; + gva_t hcall_gva; + gpa_t hcall_gpa; + gva_t test_pages; + gva_t test_pages_pte[NTEST_PAGES]; }; /* 'Worker' vCPU code checking the contents of the test page */ -static void worker_guest_code(vm_vaddr_t test_data) +static void worker_guest_code(gva_t test_data) { struct test_data *data = (struct test_data *)test_data; u32 vcpu_id = rdmsr(HV_X64_MSR_VP_INDEX); @@ -133,12 +133,12 @@ static void set_expected_val(void *addr, u64 val, int vcpu_id) * Update PTEs swapping two test pages. * TODO: use swap()/xchg() when these are provided. */ -static void swap_two_test_pages(vm_paddr_t pte_gva1, vm_paddr_t pte_gva2) +static void swap_two_test_pages(gpa_t pte_gva1, gpa_t pte_gva2) { - uint64_t tmp = *(uint64_t *)pte_gva1; + u64 tmp = *(u64 *)pte_gva1; - *(uint64_t *)pte_gva1 = *(uint64_t *)pte_gva2; - *(uint64_t *)pte_gva2 = tmp; + *(u64 *)pte_gva1 = *(u64 *)pte_gva2; + *(u64 *)pte_gva2 = tmp; } /* @@ -196,12 +196,12 @@ static inline void post_test(struct test_data *data, u64 exp1, u64 exp2) #define TESTVAL2 0x0202020202020202 /* Main vCPU doing the test */ -static void sender_guest_code(vm_vaddr_t test_data) +static void sender_guest_code(gva_t test_data) { struct test_data *data = (struct test_data *)test_data; struct hv_tlb_flush *flush = (struct hv_tlb_flush *)data->hcall_gva; struct hv_tlb_flush_ex *flush_ex = (struct hv_tlb_flush_ex *)data->hcall_gva; - vm_paddr_t hcall_gpa = data->hcall_gpa; + gpa_t hcall_gpa = data->hcall_gpa; int i, stage = 1; wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID); @@ -581,9 +581,9 @@ int main(int argc, char *argv[]) struct kvm_vm *vm; struct kvm_vcpu *vcpu[3]; pthread_t threads[2]; - vm_vaddr_t test_data_page, gva; - vm_paddr_t gpa; - uint64_t *pte; + gva_t test_data_page, gva; + gpa_t gpa; + u64 *pte; struct test_data *data; struct ucall uc; int stage = 1, r, i; @@ -593,11 +593,11 @@ int main(int argc, char *argv[]) vm = vm_create_with_one_vcpu(&vcpu[0], sender_guest_code); /* Test data page */ - test_data_page = vm_vaddr_alloc_page(vm); + test_data_page = vm_alloc_page(vm); data = (struct test_data *)addr_gva2hva(vm, test_data_page); /* Hypercall input/output */ - data->hcall_gva = vm_vaddr_alloc_pages(vm, 2); + data->hcall_gva = vm_alloc_pages(vm, 2); data->hcall_gpa = addr_gva2gpa(vm, data->hcall_gva); memset(addr_gva2hva(vm, data->hcall_gva), 0x0, 2 * PAGE_SIZE); @@ -606,7 +606,7 @@ int main(int argc, char *argv[]) * and the test will swap their mappings. The third page keeps the indication * about the current state of mappings. */ - data->test_pages = vm_vaddr_alloc_pages(vm, NTEST_PAGES + 1); + data->test_pages = vm_alloc_pages(vm, NTEST_PAGES + 1); for (i = 0; i < NTEST_PAGES; i++) memset(addr_gva2hva(vm, data->test_pages + PAGE_SIZE * i), (u8)(i + 1), PAGE_SIZE); @@ -617,7 +617,7 @@ int main(int argc, char *argv[]) * Get PTE pointers for test pages and map them inside the guest. * Use separate page for each PTE for simplicity. */ - gva = vm_vaddr_unused_gap(vm, NTEST_PAGES * PAGE_SIZE, KVM_UTIL_MIN_VADDR); + gva = vm_unused_gva_gap(vm, NTEST_PAGES * PAGE_SIZE, KVM_UTIL_MIN_VADDR); for (i = 0; i < NTEST_PAGES; i++) { pte = vm_get_pte(vm, data->test_pages + i * PAGE_SIZE); gpa = addr_hva2gpa(vm, pte); diff --git a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c index d88500c118eb..52014a3210c8 100644 --- a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c +++ b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c @@ -73,7 +73,7 @@ static void guest_code(void *test_data) int main(int argc, char *argv[]) { const bool has_nested = kvm_cpu_has(X86_FEATURE_SVM) || kvm_cpu_has(X86_FEATURE_VMX); - vm_vaddr_t nested_test_data_gva; + gva_t nested_test_data_gva; struct kvm_vcpu *vcpu; struct kvm_run *run; struct kvm_vm *vm; diff --git a/tools/testing/selftests/kvm/x86/kvm_clock_test.c b/tools/testing/selftests/kvm/x86/kvm_clock_test.c index 5bc12222d87a..5ad4aeb8e373 100644 --- a/tools/testing/selftests/kvm/x86/kvm_clock_test.c +++ b/tools/testing/selftests/kvm/x86/kvm_clock_test.c @@ -17,8 +17,8 @@ #include "processor.h" struct test_case { - uint64_t kvmclock_base; - int64_t realtime_offset; + u64 kvmclock_base; + s64 realtime_offset; }; static struct test_case test_cases[] = { @@ -31,7 +31,7 @@ static struct test_case test_cases[] = { #define GUEST_SYNC_CLOCK(__stage, __val) \ GUEST_SYNC_ARGS(__stage, __val, 0, 0, 0) -static void guest_main(vm_paddr_t pvti_pa, struct pvclock_vcpu_time_info *pvti) +static void guest_main(gpa_t pvti_pa, struct pvclock_vcpu_time_info *pvti) { int i; @@ -52,7 +52,7 @@ static inline void assert_flags(struct kvm_clock_data *data) static void handle_sync(struct ucall *uc, struct kvm_clock_data *start, struct kvm_clock_data *end) { - uint64_t obs, exp_lo, exp_hi; + u64 obs, exp_lo, exp_hi; obs = uc->args[2]; exp_lo = start->clock; @@ -135,8 +135,8 @@ static void enter_guest(struct kvm_vcpu *vcpu) int main(void) { struct kvm_vcpu *vcpu; - vm_vaddr_t pvti_gva; - vm_paddr_t pvti_gpa; + gva_t pvti_gva; + gpa_t pvti_gpa; struct kvm_vm *vm; int flags; @@ -147,7 +147,7 @@ int main(void) vm = vm_create_with_one_vcpu(&vcpu, guest_main); - pvti_gva = vm_vaddr_alloc(vm, getpagesize(), 0x10000); + pvti_gva = vm_alloc(vm, getpagesize(), 0x10000); pvti_gpa = addr_gva2gpa(vm, pvti_gva); vcpu_args_set(vcpu, 2, pvti_gpa, pvti_gva); diff --git a/tools/testing/selftests/kvm/x86/kvm_pv_test.c b/tools/testing/selftests/kvm/x86/kvm_pv_test.c index 1b805cbdb47b..8ed5fa635021 100644 --- a/tools/testing/selftests/kvm/x86/kvm_pv_test.c +++ b/tools/testing/selftests/kvm/x86/kvm_pv_test.c @@ -13,7 +13,7 @@ #include "processor.h" struct msr_data { - uint32_t idx; + u32 idx; const char *name; }; @@ -40,8 +40,8 @@ static struct msr_data msrs_to_test[] = { static void test_msr(struct msr_data *msr) { - uint64_t ignored; - uint8_t vector; + u64 ignored; + u8 vector; PR_MSR(msr); @@ -53,7 +53,7 @@ static void test_msr(struct msr_data *msr) } struct hcall_data { - uint64_t nr; + u64 nr; const char *name; }; @@ -73,7 +73,7 @@ static struct hcall_data hcalls_to_test[] = { static void test_hcall(struct hcall_data *hc) { - uint64_t r; + u64 r; PR_HCALL(hc); r = kvm_hypercall(hc->nr, 0, 0, 0, 0); diff --git a/tools/testing/selftests/kvm/x86/monitor_mwait_test.c b/tools/testing/selftests/kvm/x86/monitor_mwait_test.c index e45c028d2a7e..9c156cf7db0e 100644 --- a/tools/testing/selftests/kvm/x86/monitor_mwait_test.c +++ b/tools/testing/selftests/kvm/x86/monitor_mwait_test.c @@ -67,7 +67,7 @@ static void guest_monitor_wait(void *arg) int main(int argc, char *argv[]) { - uint64_t disabled_quirks; + u64 disabled_quirks; struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct ucall uc; diff --git a/tools/testing/selftests/kvm/x86/msrs_test.c b/tools/testing/selftests/kvm/x86/msrs_test.c index 40d918aedce6..f7e39bf887ad 100644 --- a/tools/testing/selftests/kvm/x86/msrs_test.c +++ b/tools/testing/selftests/kvm/x86/msrs_test.c @@ -81,7 +81,7 @@ static u64 fixup_rdmsr_val(u32 msr, u64 want) * is supposed to emulate that behavior based on guest vendor model * (which is the same as the host vendor model for this test). */ - if (!host_cpu_is_amd) + if (!host_cpu_is_amd_compatible) return want; switch (msr) { @@ -175,7 +175,7 @@ void guest_test_reserved_val(const struct kvm_msr *msr) * If the CPU will truncate the written value (e.g. SYSENTER on AMD), * expect success and a truncated value, not #GP. */ - if (!this_cpu_has(msr->feature) || + if ((!this_cpu_has(msr->feature) && !this_cpu_has(msr->feature2)) || msr->rsvd_val == fixup_rdmsr_val(msr->index, msr->rsvd_val)) { u8 vec = wrmsr_safe(msr->index, msr->rsvd_val); diff --git a/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c b/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c index f001cb836bfa..761fec293408 100644 --- a/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c +++ b/tools/testing/selftests/kvm/x86/nested_close_kvm_test.c @@ -67,7 +67,7 @@ static void l1_guest_code(void *data) int main(int argc, char *argv[]) { - vm_vaddr_t guest_gva; + gva_t guest_gva; struct kvm_vcpu *vcpu; struct kvm_vm *vm; diff --git a/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c b/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c index 619229bbd693..0e67cce83570 100644 --- a/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c +++ b/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c @@ -47,10 +47,10 @@ #define TEST_SYNC_WRITE_FAULT BIT(1) #define TEST_SYNC_NO_FAULT BIT(2) -static void l2_guest_code(vm_vaddr_t base) +static void l2_guest_code(gva_t base) { - vm_vaddr_t page0 = TEST_GUEST_ADDR(base, 0); - vm_vaddr_t page1 = TEST_GUEST_ADDR(base, 1); + gva_t page0 = TEST_GUEST_ADDR(base, 0); + gva_t page1 = TEST_GUEST_ADDR(base, 1); READ_ONCE(*(u64 *)page0); GUEST_SYNC(page0 | TEST_SYNC_READ_FAULT); @@ -143,7 +143,7 @@ static void l1_guest_code(void *data) static void test_handle_ucall_sync(struct kvm_vm *vm, u64 arg, unsigned long *bmap) { - vm_vaddr_t gva = arg & ~(PAGE_SIZE - 1); + gva_t gva = arg & ~(PAGE_SIZE - 1); int page_nr, i; /* @@ -198,7 +198,7 @@ static void test_handle_ucall_sync(struct kvm_vm *vm, u64 arg, static void test_dirty_log(bool nested_tdp) { - vm_vaddr_t nested_gva = 0; + gva_t nested_gva = 0; unsigned long *bmap; struct kvm_vcpu *vcpu; struct kvm_vm *vm; diff --git a/tools/testing/selftests/kvm/x86/nested_emulation_test.c b/tools/testing/selftests/kvm/x86/nested_emulation_test.c index abc824dba04f..fb7dcbe53ac7 100644 --- a/tools/testing/selftests/kvm/x86/nested_emulation_test.c +++ b/tools/testing/selftests/kvm/x86/nested_emulation_test.c @@ -13,8 +13,8 @@ enum { struct emulated_instruction { const char name[32]; - uint8_t opcode[15]; - uint32_t exit_reason[NR_VIRTUALIZATION_FLAVORS]; + u8 opcode[15]; + u32 exit_reason[NR_VIRTUALIZATION_FLAVORS]; }; static struct emulated_instruction instructions[] = { @@ -32,13 +32,13 @@ static struct emulated_instruction instructions[] = { }, }; -static uint8_t kvm_fep[] = { 0x0f, 0x0b, 0x6b, 0x76, 0x6d }; /* ud2 ; .ascii "kvm" */ -static uint8_t l2_guest_code[sizeof(kvm_fep) + 15]; -static uint8_t *l2_instruction = &l2_guest_code[sizeof(kvm_fep)]; +static u8 kvm_fep[] = { 0x0f, 0x0b, 0x6b, 0x76, 0x6d }; /* ud2 ; .ascii "kvm" */ +static u8 l2_guest_code[sizeof(kvm_fep) + 15]; +static u8 *l2_instruction = &l2_guest_code[sizeof(kvm_fep)]; -static uint32_t get_instruction_length(struct emulated_instruction *insn) +static u32 get_instruction_length(struct emulated_instruction *insn) { - uint32_t i; + u32 i; for (i = 0; i < ARRAY_SIZE(insn->opcode) && insn->opcode[i]; i++) ; @@ -81,8 +81,8 @@ static void guest_code(void *test_data) for (i = 0; i < ARRAY_SIZE(instructions); i++) { struct emulated_instruction *insn = &instructions[i]; - uint32_t insn_len = get_instruction_length(insn); - uint32_t exit_insn_len; + u32 insn_len = get_instruction_length(insn); + u32 exit_insn_len; u32 exit_reason; /* @@ -122,7 +122,7 @@ static void guest_code(void *test_data) int main(int argc, char *argv[]) { - vm_vaddr_t nested_test_data_gva; + gva_t nested_test_data_gva; struct kvm_vcpu *vcpu; struct kvm_vm *vm; diff --git a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c index 3641a42934ac..186e980aa8ee 100644 --- a/tools/testing/selftests/kvm/x86/nested_exceptions_test.c +++ b/tools/testing/selftests/kvm/x86/nested_exceptions_test.c @@ -72,7 +72,7 @@ static void l2_ss_injected_tf_test(void) } static void svm_run_l2(struct svm_test_data *svm, void *l2_code, int vector, - uint32_t error_code) + u32 error_code) { struct vmcb *vmcb = svm->vmcb; struct vmcb_control_area *ctrl = &vmcb->control; @@ -111,7 +111,7 @@ static void l1_svm_code(struct svm_test_data *svm) GUEST_DONE(); } -static void vmx_run_l2(void *l2_code, int vector, uint32_t error_code) +static void vmx_run_l2(void *l2_code, int vector, u32 error_code) { GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_code)); @@ -216,7 +216,7 @@ static void queue_ss_exception(struct kvm_vcpu *vcpu, bool inject) */ int main(int argc, char *argv[]) { - vm_vaddr_t nested_test_data_gva; + gva_t nested_test_data_gva; struct kvm_vcpu_events events; struct kvm_vcpu *vcpu; struct kvm_vm *vm; diff --git a/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c b/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c index a6b6da9cf7fe..11fd2467d823 100644 --- a/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c +++ b/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c @@ -78,7 +78,7 @@ int main(int argc, char *argv[]) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; - vm_vaddr_t guest_gva = 0; + gva_t guest_gva = 0; TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX) || kvm_cpu_has(X86_FEATURE_SVM)); diff --git a/tools/testing/selftests/kvm/x86/nested_set_state_test.c b/tools/testing/selftests/kvm/x86/nested_set_state_test.c index 0f2102b43629..831380732671 100644 --- a/tools/testing/selftests/kvm/x86/nested_set_state_test.c +++ b/tools/testing/selftests/kvm/x86/nested_set_state_test.c @@ -250,14 +250,14 @@ void test_vmx_nested_state(struct kvm_vcpu *vcpu) static void vcpu_efer_enable_svm(struct kvm_vcpu *vcpu) { - uint64_t old_efer = vcpu_get_msr(vcpu, MSR_EFER); + u64 old_efer = vcpu_get_msr(vcpu, MSR_EFER); vcpu_set_msr(vcpu, MSR_EFER, old_efer | EFER_SVME); } static void vcpu_efer_disable_svm(struct kvm_vcpu *vcpu) { - uint64_t old_efer = vcpu_get_msr(vcpu, MSR_EFER); + u64 old_efer = vcpu_get_msr(vcpu, MSR_EFER); vcpu_set_msr(vcpu, MSR_EFER, old_efer & ~EFER_SVME); } diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c index 2839f650e5c9..f0e4adac4751 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_adjust_test.c @@ -53,9 +53,9 @@ enum { /* The virtual machine object. */ static struct kvm_vm *vm; -static void check_ia32_tsc_adjust(int64_t max) +static void check_ia32_tsc_adjust(s64 max) { - int64_t adjust; + s64 adjust; adjust = rdmsr(MSR_IA32_TSC_ADJUST); GUEST_SYNC(adjust); @@ -64,7 +64,7 @@ static void check_ia32_tsc_adjust(int64_t max) static void l2_guest_code(void) { - uint64_t l1_tsc = rdtsc() - TSC_OFFSET_VALUE; + u64 l1_tsc = rdtsc() - TSC_OFFSET_VALUE; wrmsr(MSR_IA32_TSC, l1_tsc - TSC_ADJUST_VALUE); check_ia32_tsc_adjust(-2 * TSC_ADJUST_VALUE); @@ -88,7 +88,7 @@ static void l1_guest_code(void *data) */ if (this_cpu_has(X86_FEATURE_VMX)) { struct vmx_pages *vmx_pages = data; - uint32_t control; + u32 control; GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); GUEST_ASSERT(load_vmcs(vmx_pages)); @@ -117,7 +117,7 @@ static void l1_guest_code(void *data) GUEST_DONE(); } -static void report(int64_t val) +static void report(s64 val) { pr_info("IA32_TSC_ADJUST is %ld (%lld * TSC_ADJUST_VALUE + %lld).\n", val, val / TSC_ADJUST_VALUE, val % TSC_ADJUST_VALUE); @@ -125,7 +125,7 @@ static void report(int64_t val) int main(int argc, char *argv[]) { - vm_vaddr_t nested_gva; + gva_t nested_gva; struct kvm_vcpu *vcpu; TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX) || diff --git a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c index 4260c9e4f489..190e93af20a1 100644 --- a/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86/nested_tsc_scaling_test.c @@ -19,7 +19,7 @@ /* L2 is scaled up (from L1's perspective) by this factor */ #define L2_SCALE_FACTOR 4ULL -#define TSC_OFFSET_L2 ((uint64_t) -33125236320908) +#define TSC_OFFSET_L2 ((u64)-33125236320908) #define TSC_MULTIPLIER_L2 (L2_SCALE_FACTOR << 48) #define L2_GUEST_STACK_SIZE 64 @@ -35,9 +35,9 @@ enum { USLEEP, UCHECK_L1, UCHECK_L2 }; * measurements, a difference of 1% between the actual and the expected value * is tolerated. */ -static void compare_tsc_freq(uint64_t actual, uint64_t expected) +static void compare_tsc_freq(u64 actual, u64 expected) { - uint64_t tolerance, thresh_low, thresh_high; + u64 tolerance, thresh_low, thresh_high; tolerance = expected / 100; thresh_low = expected - tolerance; @@ -55,7 +55,7 @@ static void compare_tsc_freq(uint64_t actual, uint64_t expected) static void check_tsc_freq(int level) { - uint64_t tsc_start, tsc_end, tsc_freq; + u64 tsc_start, tsc_end, tsc_freq; /* * Reading the TSC twice with about a second's difference should give @@ -106,7 +106,7 @@ static void l1_svm_code(struct svm_test_data *svm) static void l1_vmx_code(struct vmx_pages *vmx_pages) { unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; - uint32_t control; + u32 control; /* check that L1's frequency looks alright before launching L2 */ check_tsc_freq(UCHECK_L1); @@ -152,14 +152,14 @@ int main(int argc, char *argv[]) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; - vm_vaddr_t guest_gva = 0; + gva_t guest_gva = 0; - uint64_t tsc_start, tsc_end; - uint64_t tsc_khz; - uint64_t l1_scale_factor; - uint64_t l0_tsc_freq = 0; - uint64_t l1_tsc_freq = 0; - uint64_t l2_tsc_freq = 0; + u64 tsc_start, tsc_end; + u64 tsc_khz; + u64 l1_scale_factor; + u64 l0_tsc_freq = 0; + u64 l1_tsc_freq = 0; + u64 l2_tsc_freq = 0; TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX) || kvm_cpu_has(X86_FEATURE_SVM)); diff --git a/tools/testing/selftests/kvm/x86/nested_vmsave_vmload_test.c b/tools/testing/selftests/kvm/x86/nested_vmsave_vmload_test.c index 6764a48f9d4d..85d3f4cc76f3 100644 --- a/tools/testing/selftests/kvm/x86/nested_vmsave_vmload_test.c +++ b/tools/testing/selftests/kvm/x86/nested_vmsave_vmload_test.c @@ -79,8 +79,8 @@ static void l1_guest_code(struct svm_test_data *svm) svm->vmcb->control.intercept |= (BIT_ULL(INTERCEPT_VMSAVE) | BIT_ULL(INTERCEPT_VMLOAD)); - /* ..VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK cleared.. */ - svm->vmcb->control.virt_ext &= ~VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK; + /* ..SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE cleared.. */ + svm->vmcb->control.misc_ctl2 &= ~SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE; svm->vmcb->save.rip = (u64)l2_guest_code_vmsave; run_guest(svm->vmcb, svm->vmcb_gpa); @@ -90,8 +90,8 @@ static void l1_guest_code(struct svm_test_data *svm) run_guest(svm->vmcb, svm->vmcb_gpa); GUEST_ASSERT_EQ(svm->vmcb->control.exit_code, SVM_EXIT_VMLOAD); - /* ..and VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK set */ - svm->vmcb->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK; + /* ..and SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE set */ + svm->vmcb->control.misc_ctl2 |= SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE; svm->vmcb->save.rip = (u64)l2_guest_code_vmsave; run_guest(svm->vmcb, svm->vmcb_gpa); @@ -106,20 +106,20 @@ static void l1_guest_code(struct svm_test_data *svm) BIT_ULL(INTERCEPT_VMLOAD)); /* - * Without VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK, the GPA will be + * Without SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE, the GPA will be * interpreted as an L1 GPA, so VMCB0 should be used. */ svm->vmcb->save.rip = (u64)l2_guest_code_vmcb0; - svm->vmcb->control.virt_ext &= ~VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK; + svm->vmcb->control.misc_ctl2 &= ~SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE; run_guest(svm->vmcb, svm->vmcb_gpa); GUEST_ASSERT_EQ(svm->vmcb->control.exit_code, SVM_EXIT_VMMCALL); /* - * With VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK, the GPA will be interpeted as + * With SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE, the GPA will be interpeted as * an L2 GPA, and translated through the NPT to VMCB1. */ svm->vmcb->save.rip = (u64)l2_guest_code_vmcb1; - svm->vmcb->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK; + svm->vmcb->control.misc_ctl2 |= SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE; run_guest(svm->vmcb, svm->vmcb_gpa); GUEST_ASSERT_EQ(svm->vmcb->control.exit_code, SVM_EXIT_VMMCALL); @@ -128,7 +128,7 @@ static void l1_guest_code(struct svm_test_data *svm) int main(int argc, char *argv[]) { - vm_vaddr_t nested_gva = 0; + gva_t nested_gva = 0; struct vmcb *test_vmcb[2]; struct kvm_vcpu *vcpu; struct kvm_vm *vm; diff --git a/tools/testing/selftests/kvm/x86/nx_huge_pages_test.c b/tools/testing/selftests/kvm/x86/nx_huge_pages_test.c index c0d84827f736..70950067b989 100644 --- a/tools/testing/selftests/kvm/x86/nx_huge_pages_test.c +++ b/tools/testing/selftests/kvm/x86/nx_huge_pages_test.c @@ -32,7 +32,7 @@ #define RETURN_OPCODE 0xC3 /* Call the specified memory address. */ -static void guest_do_CALL(uint64_t target) +static void guest_do_CALL(u64 target) { ((void (*)(void)) target)(); } @@ -46,14 +46,14 @@ static void guest_do_CALL(uint64_t target) */ void guest_code(void) { - uint64_t hpage_1 = HPAGE_GVA; - uint64_t hpage_2 = hpage_1 + (PAGE_SIZE * 512); - uint64_t hpage_3 = hpage_2 + (PAGE_SIZE * 512); + u64 hpage_1 = HPAGE_GVA; + u64 hpage_2 = hpage_1 + (PAGE_SIZE * 512); + u64 hpage_3 = hpage_2 + (PAGE_SIZE * 512); - READ_ONCE(*(uint64_t *)hpage_1); + READ_ONCE(*(u64 *)hpage_1); GUEST_SYNC(1); - READ_ONCE(*(uint64_t *)hpage_2); + READ_ONCE(*(u64 *)hpage_2); GUEST_SYNC(2); guest_do_CALL(hpage_1); @@ -62,10 +62,10 @@ void guest_code(void) guest_do_CALL(hpage_3); GUEST_SYNC(4); - READ_ONCE(*(uint64_t *)hpage_1); + READ_ONCE(*(u64 *)hpage_1); GUEST_SYNC(5); - READ_ONCE(*(uint64_t *)hpage_3); + READ_ONCE(*(u64 *)hpage_3); GUEST_SYNC(6); } @@ -107,7 +107,7 @@ void run_test(int reclaim_period_ms, bool disable_nx_huge_pages, { struct kvm_vcpu *vcpu; struct kvm_vm *vm; - uint64_t nr_bytes; + u64 nr_bytes; void *hva; int r; diff --git a/tools/testing/selftests/kvm/x86/platform_info_test.c b/tools/testing/selftests/kvm/x86/platform_info_test.c index 9cbf283ebc55..80bb07e6531c 100644 --- a/tools/testing/selftests/kvm/x86/platform_info_test.c +++ b/tools/testing/selftests/kvm/x86/platform_info_test.c @@ -23,8 +23,8 @@ static void guest_code(void) { - uint64_t msr_platform_info; - uint8_t vector; + u64 msr_platform_info; + u8 vector; GUEST_SYNC(true); msr_platform_info = rdmsr(MSR_PLATFORM_INFO); @@ -42,7 +42,7 @@ int main(int argc, char *argv[]) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; - uint64_t msr_platform_info; + u64 msr_platform_info; struct ucall uc; TEST_REQUIRE(kvm_has_cap(KVM_CAP_MSR_PLATFORM_INFO)); diff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c index 3eaa216b96c0..dc6afac3aa91 100644 --- a/tools/testing/selftests/kvm/x86/pmu_counters_test.c +++ b/tools/testing/selftests/kvm/x86/pmu_counters_test.c @@ -30,9 +30,9 @@ #define NUM_INSNS_RETIRED (NUM_LOOPS * NUM_INSNS_PER_LOOP + NUM_EXTRA_INSNS) /* Track which architectural events are supported by hardware. */ -static uint32_t hardware_pmu_arch_events; +static u32 hardware_pmu_arch_events; -static uint8_t kvm_pmu_version; +static u8 kvm_pmu_version; static bool kvm_has_perf_caps; #define X86_PMU_FEATURE_NULL \ @@ -57,7 +57,7 @@ struct kvm_intel_pmu_event { * kvm_x86_pmu_feature use syntax that's only valid in function scope, and the * compiler often thinks the feature definitions aren't compile-time constants. */ -static struct kvm_intel_pmu_event intel_event_to_feature(uint8_t idx) +static struct kvm_intel_pmu_event intel_event_to_feature(u8 idx) { const struct kvm_intel_pmu_event __intel_event_to_feature[] = { [INTEL_ARCH_CPU_CYCLES_INDEX] = { X86_PMU_FEATURE_CPU_CYCLES, X86_PMU_FEATURE_CPU_CYCLES_FIXED }, @@ -89,8 +89,8 @@ static struct kvm_intel_pmu_event intel_event_to_feature(uint8_t idx) static struct kvm_vm *pmu_vm_create_with_one_vcpu(struct kvm_vcpu **vcpu, void *guest_code, - uint8_t pmu_version, - uint64_t perf_capabilities) + u8 pmu_version, + u64 perf_capabilities) { struct kvm_vm *vm; @@ -132,7 +132,7 @@ static void run_vcpu(struct kvm_vcpu *vcpu) } while (uc.cmd != UCALL_DONE); } -static uint8_t guest_get_pmu_version(void) +static u8 guest_get_pmu_version(void) { /* * Return the effective PMU version, i.e. the minimum between what KVM @@ -141,7 +141,7 @@ static uint8_t guest_get_pmu_version(void) * supported by KVM to verify KVM doesn't freak out and do something * bizarre with an architecturally valid, but unsupported, version. */ - return min_t(uint8_t, kvm_pmu_version, this_cpu_property(X86_PROPERTY_PMU_VERSION)); + return min_t(u8, kvm_pmu_version, this_cpu_property(X86_PROPERTY_PMU_VERSION)); } /* @@ -153,9 +153,9 @@ static uint8_t guest_get_pmu_version(void) * Sanity check that in all cases, the event doesn't count when it's disabled, * and that KVM correctly emulates the write of an arbitrary value. */ -static void guest_assert_event_count(uint8_t idx, uint32_t pmc, uint32_t pmc_msr) +static void guest_assert_event_count(u8 idx, u32 pmc, u32 pmc_msr) { - uint64_t count; + u64 count; count = _rdpmc(pmc); if (!(hardware_pmu_arch_events & BIT(idx))) @@ -236,7 +236,7 @@ do { \ FEP "xor %%eax, %%eax\n\t" \ FEP "xor %%edx, %%edx\n\t" \ "wrmsr\n\t" \ - :: "a"((uint32_t)_value), "d"(_value >> 32), \ + :: "a"((u32)_value), "d"(_value >> 32), \ "c"(_msr), "D"(_msr), [m]"m"(kvm_pmu_version) \ ); \ } while (0) @@ -255,8 +255,8 @@ do { \ guest_assert_event_count(_idx, _pmc, _pmc_msr); \ } while (0) -static void __guest_test_arch_event(uint8_t idx, uint32_t pmc, uint32_t pmc_msr, - uint32_t ctrl_msr, uint64_t ctrl_msr_value) +static void __guest_test_arch_event(u8 idx, u32 pmc, u32 pmc_msr, + u32 ctrl_msr, u64 ctrl_msr_value) { GUEST_TEST_EVENT(idx, pmc, pmc_msr, ctrl_msr, ctrl_msr_value, ""); @@ -264,14 +264,14 @@ static void __guest_test_arch_event(uint8_t idx, uint32_t pmc, uint32_t pmc_msr, GUEST_TEST_EVENT(idx, pmc, pmc_msr, ctrl_msr, ctrl_msr_value, KVM_FEP); } -static void guest_test_arch_event(uint8_t idx) +static void guest_test_arch_event(u8 idx) { - uint32_t nr_gp_counters = this_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS); - uint32_t pmu_version = guest_get_pmu_version(); + u32 nr_gp_counters = this_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS); + u32 pmu_version = guest_get_pmu_version(); /* PERF_GLOBAL_CTRL exists only for Architectural PMU Version 2+. */ bool guest_has_perf_global_ctrl = pmu_version >= 2; struct kvm_x86_pmu_feature gp_event, fixed_event; - uint32_t base_pmc_msr; + u32 base_pmc_msr; unsigned int i; /* The host side shouldn't invoke this without a guest PMU. */ @@ -289,7 +289,7 @@ static void guest_test_arch_event(uint8_t idx) GUEST_ASSERT(nr_gp_counters); for (i = 0; i < nr_gp_counters; i++) { - uint64_t eventsel = ARCH_PERFMON_EVENTSEL_OS | + u64 eventsel = ARCH_PERFMON_EVENTSEL_OS | ARCH_PERFMON_EVENTSEL_ENABLE | intel_pmu_arch_events[idx]; @@ -320,7 +320,7 @@ static void guest_test_arch_event(uint8_t idx) static void guest_test_arch_events(void) { - uint8_t i; + u8 i; for (i = 0; i < NR_INTEL_ARCH_EVENTS; i++) guest_test_arch_event(i); @@ -328,8 +328,8 @@ static void guest_test_arch_events(void) GUEST_DONE(); } -static void test_arch_events(uint8_t pmu_version, uint64_t perf_capabilities, - uint8_t length, uint32_t unavailable_mask) +static void test_arch_events(u8 pmu_version, u64 perf_capabilities, + u8 length, u32 unavailable_mask) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; @@ -373,11 +373,11 @@ __GUEST_ASSERT(expect_gp ? vector == GP_VECTOR : !vector, \ "Expected " #insn "(0x%x) to yield 0x%lx, got 0x%lx", \ msr, expected, val); -static void guest_test_rdpmc(uint32_t rdpmc_idx, bool expect_success, - uint64_t expected_val) +static void guest_test_rdpmc(u32 rdpmc_idx, bool expect_success, + u64 expected_val) { - uint8_t vector; - uint64_t val; + u8 vector; + u64 val; vector = rdpmc_safe(rdpmc_idx, &val); GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector); @@ -393,19 +393,19 @@ static void guest_test_rdpmc(uint32_t rdpmc_idx, bool expect_success, GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val); } -static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters, - uint8_t nr_counters, uint32_t or_mask) +static void guest_rd_wr_counters(u32 base_msr, u8 nr_possible_counters, + u8 nr_counters, u32 or_mask) { const bool pmu_has_fast_mode = !guest_get_pmu_version(); - uint8_t i; + u8 i; for (i = 0; i < nr_possible_counters; i++) { /* * TODO: Test a value that validates full-width writes and the * width of the counters. */ - const uint64_t test_val = 0xffff; - const uint32_t msr = base_msr + i; + const u64 test_val = 0xffff; + const u32 msr = base_msr + i; /* * Fixed counters are supported if the counter is less than the @@ -418,12 +418,12 @@ static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters * KVM drops writes to MSR_P6_PERFCTR[0|1] if the counters are * unsupported, i.e. doesn't #GP and reads back '0'. */ - const uint64_t expected_val = expect_success ? test_val : 0; + const u64 expected_val = expect_success ? test_val : 0; const bool expect_gp = !expect_success && msr != MSR_P6_PERFCTR0 && msr != MSR_P6_PERFCTR1; - uint32_t rdpmc_idx; - uint8_t vector; - uint64_t val; + u32 rdpmc_idx; + u8 vector; + u64 val; vector = wrmsr_safe(msr, test_val); GUEST_ASSERT_PMC_MSR_ACCESS(WRMSR, msr, expect_gp, vector); @@ -461,9 +461,9 @@ static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters static void guest_test_gp_counters(void) { - uint8_t pmu_version = guest_get_pmu_version(); - uint8_t nr_gp_counters = 0; - uint32_t base_msr; + u8 pmu_version = guest_get_pmu_version(); + u8 nr_gp_counters = 0; + u32 base_msr; if (pmu_version) nr_gp_counters = this_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS); @@ -477,7 +477,7 @@ static void guest_test_gp_counters(void) * counters, of which there are none. */ if (pmu_version > 1) { - uint64_t global_ctrl = rdmsr(MSR_CORE_PERF_GLOBAL_CTRL); + u64 global_ctrl = rdmsr(MSR_CORE_PERF_GLOBAL_CTRL); if (nr_gp_counters) GUEST_ASSERT_EQ(global_ctrl, GENMASK_ULL(nr_gp_counters - 1, 0)); @@ -495,8 +495,8 @@ static void guest_test_gp_counters(void) GUEST_DONE(); } -static void test_gp_counters(uint8_t pmu_version, uint64_t perf_capabilities, - uint8_t nr_gp_counters) +static void test_gp_counters(u8 pmu_version, u64 perf_capabilities, + u8 nr_gp_counters) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; @@ -514,9 +514,9 @@ static void test_gp_counters(uint8_t pmu_version, uint64_t perf_capabilities, static void guest_test_fixed_counters(void) { - uint64_t supported_bitmask = 0; - uint8_t nr_fixed_counters = 0; - uint8_t i; + u64 supported_bitmask = 0; + u8 nr_fixed_counters = 0; + u8 i; /* Fixed counters require Architectural vPMU Version 2+. */ if (guest_get_pmu_version() >= 2) @@ -533,8 +533,8 @@ static void guest_test_fixed_counters(void) nr_fixed_counters, supported_bitmask); for (i = 0; i < MAX_NR_FIXED_COUNTERS; i++) { - uint8_t vector; - uint64_t val; + u8 vector; + u64 val; if (i >= nr_fixed_counters && !(supported_bitmask & BIT_ULL(i))) { vector = wrmsr_safe(MSR_CORE_PERF_FIXED_CTR_CTRL, @@ -561,9 +561,8 @@ static void guest_test_fixed_counters(void) GUEST_DONE(); } -static void test_fixed_counters(uint8_t pmu_version, uint64_t perf_capabilities, - uint8_t nr_fixed_counters, - uint32_t supported_bitmask) +static void test_fixed_counters(u8 pmu_version, u64 perf_capabilities, + u8 nr_fixed_counters, u32 supported_bitmask) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; @@ -583,14 +582,14 @@ static void test_fixed_counters(uint8_t pmu_version, uint64_t perf_capabilities, static void test_intel_counters(void) { - uint8_t nr_fixed_counters = kvm_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS); - uint8_t nr_gp_counters = kvm_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS); - uint8_t pmu_version = kvm_cpu_property(X86_PROPERTY_PMU_VERSION); + u8 nr_fixed_counters = kvm_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS); + u8 nr_gp_counters = kvm_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS); + u8 pmu_version = kvm_cpu_property(X86_PROPERTY_PMU_VERSION); unsigned int i; - uint8_t v, j; - uint32_t k; + u8 v, j; + u32 k; - const uint64_t perf_caps[] = { + const u64 perf_caps[] = { 0, PMU_CAP_FW_WRITES, }; @@ -602,7 +601,7 @@ static void test_intel_counters(void) * as alternating bit sequencues, e.g. to detect if KVM is checking the * wrong bit(s). */ - const uint32_t unavailable_masks[] = { + const u32 unavailable_masks[] = { 0x0, 0xffffffffu, 0xaaaaaaaau, @@ -620,7 +619,7 @@ static void test_intel_counters(void) * Intel, i.e. is the last version that is guaranteed to be backwards * compatible with KVM's existing behavior. */ - uint8_t max_pmu_version = max_t(typeof(pmu_version), pmu_version, 5); + u8 max_pmu_version = max_t(typeof(pmu_version), pmu_version, 5); /* * Detect the existence of events that aren't supported by selftests. diff --git a/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c b/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c index 1c5b7611db24..c1232344fda8 100644 --- a/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c +++ b/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c @@ -53,11 +53,11 @@ static const struct __kvm_pmu_event_filter base_event_filter = { }; struct { - uint64_t loads; - uint64_t stores; - uint64_t loads_stores; - uint64_t branches_retired; - uint64_t instructions_retired; + u64 loads; + u64 stores; + u64 loads_stores; + u64 branches_retired; + u64 instructions_retired; } pmc_results; /* @@ -75,9 +75,9 @@ static void guest_gp_handler(struct ex_regs *regs) * * Return on success. GUEST_SYNC(0) on error. */ -static void check_msr(uint32_t msr, uint64_t bits_to_flip) +static void check_msr(u32 msr, u64 bits_to_flip) { - uint64_t v = rdmsr(msr) ^ bits_to_flip; + u64 v = rdmsr(msr) ^ bits_to_flip; wrmsr(msr, v); if (rdmsr(msr) != v) @@ -89,10 +89,10 @@ static void check_msr(uint32_t msr, uint64_t bits_to_flip) GUEST_SYNC(-EIO); } -static void run_and_measure_loop(uint32_t msr_base) +static void run_and_measure_loop(u32 msr_base) { - const uint64_t branches_retired = rdmsr(msr_base + 0); - const uint64_t insn_retired = rdmsr(msr_base + 1); + const u64 branches_retired = rdmsr(msr_base + 0); + const u64 insn_retired = rdmsr(msr_base + 1); __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); @@ -147,7 +147,7 @@ static void amd_guest_code(void) * Run the VM to the next GUEST_SYNC(value), and return the value passed * to the sync. Any other exit from the guest is fatal. */ -static uint64_t run_vcpu_to_sync(struct kvm_vcpu *vcpu) +static u64 run_vcpu_to_sync(struct kvm_vcpu *vcpu) { struct ucall uc; @@ -161,7 +161,7 @@ static uint64_t run_vcpu_to_sync(struct kvm_vcpu *vcpu) static void run_vcpu_and_sync_pmc_results(struct kvm_vcpu *vcpu) { - uint64_t r; + u64 r; memset(&pmc_results, 0, sizeof(pmc_results)); sync_global_to_guest(vcpu->vm, pmc_results); @@ -182,7 +182,7 @@ static void run_vcpu_and_sync_pmc_results(struct kvm_vcpu *vcpu) */ static bool sanity_check_pmu(struct kvm_vcpu *vcpu) { - uint64_t r; + u64 r; vm_install_exception_handler(vcpu->vm, GP_VECTOR, guest_gp_handler); r = run_vcpu_to_sync(vcpu); @@ -195,7 +195,7 @@ static bool sanity_check_pmu(struct kvm_vcpu *vcpu) * Remove the first occurrence of 'event' (if any) from the filter's * event list. */ -static void remove_event(struct __kvm_pmu_event_filter *f, uint64_t event) +static void remove_event(struct __kvm_pmu_event_filter *f, u64 event) { bool found = false; int i; @@ -212,8 +212,8 @@ static void remove_event(struct __kvm_pmu_event_filter *f, uint64_t event) #define ASSERT_PMC_COUNTING_INSTRUCTIONS() \ do { \ - uint64_t br = pmc_results.branches_retired; \ - uint64_t ir = pmc_results.instructions_retired; \ + u64 br = pmc_results.branches_retired; \ + u64 ir = pmc_results.instructions_retired; \ bool br_matched = this_pmu_has_errata(BRANCHES_RETIRED_OVERCOUNT) ? \ br >= NUM_BRANCHES : br == NUM_BRANCHES; \ \ @@ -228,8 +228,8 @@ do { \ #define ASSERT_PMC_NOT_COUNTING_INSTRUCTIONS() \ do { \ - uint64_t br = pmc_results.branches_retired; \ - uint64_t ir = pmc_results.instructions_retired; \ + u64 br = pmc_results.branches_retired; \ + u64 ir = pmc_results.instructions_retired; \ \ TEST_ASSERT(!br, "%s: Branch instructions retired = %lu (expected 0)", \ __func__, br); \ @@ -361,7 +361,8 @@ static bool use_intel_pmu(void) */ static bool use_amd_pmu(void) { - return host_cpu_is_amd && kvm_cpu_family() >= 0x17; + return (host_cpu_is_amd && kvm_cpu_family() >= 0x17) || + host_cpu_is_hygon; } /* @@ -377,7 +378,7 @@ static bool use_amd_pmu(void) static bool supports_event_mem_inst_retired(void) { - uint32_t eax, ebx, ecx, edx; + u32 eax, ebx, ecx, edx; cpuid(1, &eax, &ebx, &ecx, &edx); if (x86_family(eax) == 0x6) { @@ -414,15 +415,15 @@ static bool supports_event_mem_inst_retired(void) #define EXCLUDE_MASKED_ENTRY(event_select, mask, match) \ KVM_PMU_ENCODE_MASKED_ENTRY(event_select, mask, match, true) -static void masked_events_guest_test(uint32_t msr_base) +static void masked_events_guest_test(u32 msr_base) { /* * The actual value of the counters don't determine the outcome of * the test. Only that they are zero or non-zero. */ - const uint64_t loads = rdmsr(msr_base + 0); - const uint64_t stores = rdmsr(msr_base + 1); - const uint64_t loads_stores = rdmsr(msr_base + 2); + const u64 loads = rdmsr(msr_base + 0); + const u64 stores = rdmsr(msr_base + 1); + const u64 loads_stores = rdmsr(msr_base + 2); int val; @@ -475,7 +476,7 @@ static void amd_masked_events_guest_code(void) } static void run_masked_events_test(struct kvm_vcpu *vcpu, - const uint64_t masked_events[], + const u64 masked_events[], const int nmasked_events) { struct __kvm_pmu_event_filter f = { @@ -484,7 +485,7 @@ static void run_masked_events_test(struct kvm_vcpu *vcpu, .flags = KVM_PMU_EVENT_FLAG_MASKED_EVENTS, }; - memcpy(f.events, masked_events, sizeof(uint64_t) * nmasked_events); + memcpy(f.events, masked_events, sizeof(u64) * nmasked_events); test_with_filter(vcpu, &f); } @@ -493,12 +494,12 @@ static void run_masked_events_test(struct kvm_vcpu *vcpu, #define ALLOW_LOADS_STORES BIT(2) struct masked_events_test { - uint64_t intel_events[MAX_TEST_EVENTS]; - uint64_t intel_event_end; - uint64_t amd_events[MAX_TEST_EVENTS]; - uint64_t amd_event_end; + u64 intel_events[MAX_TEST_EVENTS]; + u64 intel_event_end; + u64 amd_events[MAX_TEST_EVENTS]; + u64 amd_event_end; const char *msg; - uint32_t flags; + u32 flags; }; /* @@ -581,9 +582,9 @@ const struct masked_events_test test_cases[] = { }; static int append_test_events(const struct masked_events_test *test, - uint64_t *events, int nevents) + u64 *events, int nevents) { - const uint64_t *evts; + const u64 *evts; int i; evts = use_intel_pmu() ? test->intel_events : test->amd_events; @@ -602,7 +603,7 @@ static bool bool_eq(bool a, bool b) return a == b; } -static void run_masked_events_tests(struct kvm_vcpu *vcpu, uint64_t *events, +static void run_masked_events_tests(struct kvm_vcpu *vcpu, u64 *events, int nevents) { int ntests = ARRAY_SIZE(test_cases); @@ -629,7 +630,7 @@ static void run_masked_events_tests(struct kvm_vcpu *vcpu, uint64_t *events, } } -static void add_dummy_events(uint64_t *events, int nevents) +static void add_dummy_events(u64 *events, int nevents) { int i; @@ -649,7 +650,7 @@ static void add_dummy_events(uint64_t *events, int nevents) static void test_masked_events(struct kvm_vcpu *vcpu) { int nevents = KVM_PMU_EVENT_FILTER_MAX_EVENTS - MAX_TEST_EVENTS; - uint64_t events[KVM_PMU_EVENT_FILTER_MAX_EVENTS]; + u64 events[KVM_PMU_EVENT_FILTER_MAX_EVENTS]; /* Run the test cases against a sparse PMU event filter. */ run_masked_events_tests(vcpu, events, 0); @@ -667,8 +668,8 @@ static int set_pmu_event_filter(struct kvm_vcpu *vcpu, return __vm_ioctl(vcpu->vm, KVM_SET_PMU_EVENT_FILTER, f); } -static int set_pmu_single_event_filter(struct kvm_vcpu *vcpu, uint64_t event, - uint32_t flags, uint32_t action) +static int set_pmu_single_event_filter(struct kvm_vcpu *vcpu, u64 event, + u32 flags, u32 action) { struct __kvm_pmu_event_filter f = { .nevents = 1, @@ -684,9 +685,9 @@ static int set_pmu_single_event_filter(struct kvm_vcpu *vcpu, uint64_t event, static void test_filter_ioctl(struct kvm_vcpu *vcpu) { - uint8_t nr_fixed_counters = kvm_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS); + u8 nr_fixed_counters = kvm_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS); struct __kvm_pmu_event_filter f; - uint64_t e = ~0ul; + u64 e = ~0ul; int r; /* @@ -728,7 +729,7 @@ static void test_filter_ioctl(struct kvm_vcpu *vcpu) TEST_ASSERT(!r, "Masking non-existent fixed counters should be allowed"); } -static void intel_run_fixed_counter_guest_code(uint8_t idx) +static void intel_run_fixed_counter_guest_code(u8 idx) { for (;;) { wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0); @@ -744,8 +745,8 @@ static void intel_run_fixed_counter_guest_code(uint8_t idx) } } -static uint64_t test_with_fixed_counter_filter(struct kvm_vcpu *vcpu, - uint32_t action, uint32_t bitmap) +static u64 test_with_fixed_counter_filter(struct kvm_vcpu *vcpu, + u32 action, u32 bitmap) { struct __kvm_pmu_event_filter f = { .action = action, @@ -756,9 +757,9 @@ static uint64_t test_with_fixed_counter_filter(struct kvm_vcpu *vcpu, return run_vcpu_to_sync(vcpu); } -static uint64_t test_set_gp_and_fixed_event_filter(struct kvm_vcpu *vcpu, - uint32_t action, - uint32_t bitmap) +static u64 test_set_gp_and_fixed_event_filter(struct kvm_vcpu *vcpu, + u32 action, + u32 bitmap) { struct __kvm_pmu_event_filter f = base_event_filter; @@ -769,12 +770,12 @@ static uint64_t test_set_gp_and_fixed_event_filter(struct kvm_vcpu *vcpu, return run_vcpu_to_sync(vcpu); } -static void __test_fixed_counter_bitmap(struct kvm_vcpu *vcpu, uint8_t idx, - uint8_t nr_fixed_counters) +static void __test_fixed_counter_bitmap(struct kvm_vcpu *vcpu, u8 idx, + u8 nr_fixed_counters) { unsigned int i; - uint32_t bitmap; - uint64_t count; + u32 bitmap; + u64 count; TEST_ASSERT(nr_fixed_counters < sizeof(bitmap) * 8, "Invalid nr_fixed_counters"); @@ -814,10 +815,10 @@ static void __test_fixed_counter_bitmap(struct kvm_vcpu *vcpu, uint8_t idx, static void test_fixed_counter_bitmap(void) { - uint8_t nr_fixed_counters = kvm_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS); + u8 nr_fixed_counters = kvm_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS); struct kvm_vm *vm; struct kvm_vcpu *vcpu; - uint8_t idx; + u8 idx; /* * Check that pmu_event_filter works as expected when it's applied to diff --git a/tools/testing/selftests/kvm/x86/private_mem_conversions_test.c b/tools/testing/selftests/kvm/x86/private_mem_conversions_test.c index 1969f4ab9b28..1d2f5d4fd45d 100644 --- a/tools/testing/selftests/kvm/x86/private_mem_conversions_test.c +++ b/tools/testing/selftests/kvm/x86/private_mem_conversions_test.c @@ -23,13 +23,13 @@ #include #define BASE_DATA_SLOT 10 -#define BASE_DATA_GPA ((uint64_t)(1ull << 32)) -#define PER_CPU_DATA_SIZE ((uint64_t)(SZ_2M + PAGE_SIZE)) +#define BASE_DATA_GPA ((u64)(1ull << 32)) +#define PER_CPU_DATA_SIZE ((u64)(SZ_2M + PAGE_SIZE)) /* Horrific macro so that the line info is captured accurately :-( */ #define memcmp_g(gpa, pattern, size) \ do { \ - uint8_t *mem = (uint8_t *)gpa; \ + u8 *mem = (u8 *)gpa; \ size_t i; \ \ for (i = 0; i < size; i++) \ @@ -38,7 +38,7 @@ do { \ pattern, i, gpa + i, mem[i]); \ } while (0) -static void memcmp_h(uint8_t *mem, uint64_t gpa, uint8_t pattern, size_t size) +static void memcmp_h(u8 *mem, gpa_t gpa, u8 pattern, size_t size) { size_t i; @@ -70,13 +70,13 @@ enum ucall_syncs { SYNC_PRIVATE, }; -static void guest_sync_shared(uint64_t gpa, uint64_t size, - uint8_t current_pattern, uint8_t new_pattern) +static void guest_sync_shared(gpa_t gpa, u64 size, + u8 current_pattern, u8 new_pattern) { GUEST_SYNC5(SYNC_SHARED, gpa, size, current_pattern, new_pattern); } -static void guest_sync_private(uint64_t gpa, uint64_t size, uint8_t pattern) +static void guest_sync_private(gpa_t gpa, u64 size, u8 pattern) { GUEST_SYNC4(SYNC_PRIVATE, gpa, size, pattern); } @@ -86,10 +86,10 @@ static void guest_sync_private(uint64_t gpa, uint64_t size, uint8_t pattern) #define MAP_GPA_SHARED BIT(1) #define MAP_GPA_DO_FALLOCATE BIT(2) -static void guest_map_mem(uint64_t gpa, uint64_t size, bool map_shared, +static void guest_map_mem(gpa_t gpa, u64 size, bool map_shared, bool do_fallocate) { - uint64_t flags = MAP_GPA_SET_ATTRIBUTES; + u64 flags = MAP_GPA_SET_ATTRIBUTES; if (map_shared) flags |= MAP_GPA_SHARED; @@ -98,19 +98,19 @@ static void guest_map_mem(uint64_t gpa, uint64_t size, bool map_shared, kvm_hypercall_map_gpa_range(gpa, size, flags); } -static void guest_map_shared(uint64_t gpa, uint64_t size, bool do_fallocate) +static void guest_map_shared(gpa_t gpa, u64 size, bool do_fallocate) { guest_map_mem(gpa, size, true, do_fallocate); } -static void guest_map_private(uint64_t gpa, uint64_t size, bool do_fallocate) +static void guest_map_private(gpa_t gpa, u64 size, bool do_fallocate) { guest_map_mem(gpa, size, false, do_fallocate); } struct { - uint64_t offset; - uint64_t size; + u64 offset; + u64 size; } static const test_ranges[] = { GUEST_STAGE(0, PAGE_SIZE), GUEST_STAGE(0, SZ_2M), @@ -119,11 +119,11 @@ struct { GUEST_STAGE(SZ_2M, PAGE_SIZE), }; -static void guest_test_explicit_conversion(uint64_t base_gpa, bool do_fallocate) +static void guest_test_explicit_conversion(u64 base_gpa, bool do_fallocate) { - const uint8_t def_p = 0xaa; - const uint8_t init_p = 0xcc; - uint64_t j; + const u8 def_p = 0xaa; + const u8 init_p = 0xcc; + u64 j; int i; /* Memory should be shared by default. */ @@ -134,12 +134,12 @@ static void guest_test_explicit_conversion(uint64_t base_gpa, bool do_fallocate) memcmp_g(base_gpa, init_p, PER_CPU_DATA_SIZE); for (i = 0; i < ARRAY_SIZE(test_ranges); i++) { - uint64_t gpa = base_gpa + test_ranges[i].offset; - uint64_t size = test_ranges[i].size; - uint8_t p1 = 0x11; - uint8_t p2 = 0x22; - uint8_t p3 = 0x33; - uint8_t p4 = 0x44; + gpa_t gpa = base_gpa + test_ranges[i].offset; + u64 size = test_ranges[i].size; + u8 p1 = 0x11; + u8 p2 = 0x22; + u8 p3 = 0x33; + u8 p4 = 0x44; /* * Set the test region to pattern one to differentiate it from @@ -214,10 +214,10 @@ skip: } } -static void guest_punch_hole(uint64_t gpa, uint64_t size) +static void guest_punch_hole(gpa_t gpa, u64 size) { /* "Mapping" memory shared via fallocate() is done via PUNCH_HOLE. */ - uint64_t flags = MAP_GPA_SHARED | MAP_GPA_DO_FALLOCATE; + u64 flags = MAP_GPA_SHARED | MAP_GPA_DO_FALLOCATE; kvm_hypercall_map_gpa_range(gpa, size, flags); } @@ -227,9 +227,9 @@ static void guest_punch_hole(uint64_t gpa, uint64_t size) * proper conversion. Freeing (PUNCH_HOLE) should zap SPTEs, and reallocating * (subsequent fault) should zero memory. */ -static void guest_test_punch_hole(uint64_t base_gpa, bool precise) +static void guest_test_punch_hole(u64 base_gpa, bool precise) { - const uint8_t init_p = 0xcc; + const u8 init_p = 0xcc; int i; /* @@ -239,8 +239,8 @@ static void guest_test_punch_hole(uint64_t base_gpa, bool precise) guest_map_private(base_gpa, PER_CPU_DATA_SIZE, false); for (i = 0; i < ARRAY_SIZE(test_ranges); i++) { - uint64_t gpa = base_gpa + test_ranges[i].offset; - uint64_t size = test_ranges[i].size; + gpa_t gpa = base_gpa + test_ranges[i].offset; + u64 size = test_ranges[i].size; /* * Free all memory before each iteration, even for the !precise @@ -268,7 +268,7 @@ static void guest_test_punch_hole(uint64_t base_gpa, bool precise) } } -static void guest_code(uint64_t base_gpa) +static void guest_code(u64 base_gpa) { /* * Run the conversion test twice, with and without doing fallocate() on @@ -289,8 +289,8 @@ static void guest_code(uint64_t base_gpa) static void handle_exit_hypercall(struct kvm_vcpu *vcpu) { struct kvm_run *run = vcpu->run; - uint64_t gpa = run->hypercall.args[0]; - uint64_t size = run->hypercall.args[1] * PAGE_SIZE; + gpa_t gpa = run->hypercall.args[0]; + u64 size = run->hypercall.args[1] * PAGE_SIZE; bool set_attributes = run->hypercall.args[2] & MAP_GPA_SET_ATTRIBUTES; bool map_shared = run->hypercall.args[2] & MAP_GPA_SHARED; bool do_fallocate = run->hypercall.args[2] & MAP_GPA_DO_FALLOCATE; @@ -337,7 +337,7 @@ static void *__test_mem_conversions(void *__vcpu) case UCALL_ABORT: REPORT_GUEST_ASSERT(uc); case UCALL_SYNC: { - uint64_t gpa = uc.args[1]; + gpa_t gpa = uc.args[1]; size_t size = uc.args[2]; size_t i; @@ -347,7 +347,7 @@ static void *__test_mem_conversions(void *__vcpu) for (i = 0; i < size; i += vm->page_size) { size_t nr_bytes = min_t(size_t, vm->page_size, size - i); - uint8_t *hva = addr_gpa2hva(vm, gpa + i); + u8 *hva = addr_gpa2hva(vm, gpa + i); /* In all cases, the host should observe the shared data. */ memcmp_h(hva, gpa + i, uc.args[3], nr_bytes); @@ -366,8 +366,8 @@ static void *__test_mem_conversions(void *__vcpu) } } -static void test_mem_conversions(enum vm_mem_backing_src_type src_type, uint32_t nr_vcpus, - uint32_t nr_memslots) +static void test_mem_conversions(enum vm_mem_backing_src_type src_type, u32 nr_vcpus, + u32 nr_memslots) { /* * Allocate enough memory so that each vCPU's chunk of memory can be @@ -402,7 +402,7 @@ static void test_mem_conversions(enum vm_mem_backing_src_type src_type, uint32_t KVM_MEM_GUEST_MEMFD, memfd, slot_size * i); for (i = 0; i < nr_vcpus; i++) { - uint64_t gpa = BASE_DATA_GPA + i * per_cpu_size; + gpa_t gpa = BASE_DATA_GPA + i * per_cpu_size; vcpu_args_set(vcpus[i], 1, gpa); @@ -450,8 +450,8 @@ static void usage(const char *cmd) int main(int argc, char *argv[]) { enum vm_mem_backing_src_type src_type = DEFAULT_VM_MEM_SRC; - uint32_t nr_memslots = 1; - uint32_t nr_vcpus = 1; + u32 nr_memslots = 1; + u32 nr_vcpus = 1; int opt; TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SW_PROTECTED_VM)); diff --git a/tools/testing/selftests/kvm/x86/private_mem_kvm_exits_test.c b/tools/testing/selftests/kvm/x86/private_mem_kvm_exits_test.c index 13e72fcec8dd..10db9fe6d906 100644 --- a/tools/testing/selftests/kvm/x86/private_mem_kvm_exits_test.c +++ b/tools/testing/selftests/kvm/x86/private_mem_kvm_exits_test.c @@ -17,17 +17,17 @@ #define EXITS_TEST_SIZE (EXITS_TEST_NPAGES * PAGE_SIZE) #define EXITS_TEST_SLOT 10 -static uint64_t guest_repeatedly_read(void) +static u64 guest_repeatedly_read(void) { - volatile uint64_t value; + volatile u64 value; while (true) - value = *((uint64_t *) EXITS_TEST_GVA); + value = *((u64 *)EXITS_TEST_GVA); return value; } -static uint32_t run_vcpu_get_exit_reason(struct kvm_vcpu *vcpu) +static u32 run_vcpu_get_exit_reason(struct kvm_vcpu *vcpu) { int r; @@ -50,7 +50,7 @@ static void test_private_access_memslot_deleted(void) struct kvm_vcpu *vcpu; pthread_t vm_thread; void *thread_return; - uint32_t exit_reason; + u32 exit_reason; vm = vm_create_shape_with_one_vcpu(protected_vm_shape, &vcpu, guest_repeatedly_read); @@ -72,7 +72,7 @@ static void test_private_access_memslot_deleted(void) vm_mem_region_delete(vm, EXITS_TEST_SLOT); pthread_join(vm_thread, &thread_return); - exit_reason = (uint32_t)(uint64_t)thread_return; + exit_reason = (u32)(u64)thread_return; TEST_ASSERT_EQ(exit_reason, KVM_EXIT_MEMORY_FAULT); TEST_ASSERT_EQ(vcpu->run->memory_fault.flags, KVM_MEMORY_EXIT_FLAG_PRIVATE); @@ -86,7 +86,7 @@ static void test_private_access_memslot_not_private(void) { struct kvm_vm *vm; struct kvm_vcpu *vcpu; - uint32_t exit_reason; + u32 exit_reason; vm = vm_create_shape_with_one_vcpu(protected_vm_shape, &vcpu, guest_repeatedly_read); diff --git a/tools/testing/selftests/kvm/x86/set_boot_cpu_id.c b/tools/testing/selftests/kvm/x86/set_boot_cpu_id.c index 49913784bc82..8e3898646c69 100644 --- a/tools/testing/selftests/kvm/x86/set_boot_cpu_id.c +++ b/tools/testing/selftests/kvm/x86/set_boot_cpu_id.c @@ -86,11 +86,11 @@ static void run_vcpu(struct kvm_vcpu *vcpu) } } -static struct kvm_vm *create_vm(uint32_t nr_vcpus, uint32_t bsp_vcpu_id, +static struct kvm_vm *create_vm(u32 nr_vcpus, u32 bsp_vcpu_id, struct kvm_vcpu *vcpus[]) { struct kvm_vm *vm; - uint32_t i; + u32 i; vm = vm_create(nr_vcpus); @@ -104,7 +104,7 @@ static struct kvm_vm *create_vm(uint32_t nr_vcpus, uint32_t bsp_vcpu_id, return vm; } -static void run_vm_bsp(uint32_t bsp_vcpu_id) +static void run_vm_bsp(u32 bsp_vcpu_id) { struct kvm_vcpu *vcpus[2]; struct kvm_vm *vm; diff --git a/tools/testing/selftests/kvm/x86/set_sregs_test.c b/tools/testing/selftests/kvm/x86/set_sregs_test.c index f4095a3d1278..8e654cc9ab16 100644 --- a/tools/testing/selftests/kvm/x86/set_sregs_test.c +++ b/tools/testing/selftests/kvm/x86/set_sregs_test.c @@ -46,9 +46,9 @@ do { \ X86_CR4_MCE | X86_CR4_PGE | X86_CR4_PCE | \ X86_CR4_OSFXSR | X86_CR4_OSXMMEXCPT) -static uint64_t calc_supported_cr4_feature_bits(void) +static u64 calc_supported_cr4_feature_bits(void) { - uint64_t cr4 = KVM_ALWAYS_ALLOWED_CR4; + u64 cr4 = KVM_ALWAYS_ALLOWED_CR4; if (kvm_cpu_has(X86_FEATURE_UMIP)) cr4 |= X86_CR4_UMIP; @@ -74,7 +74,7 @@ static uint64_t calc_supported_cr4_feature_bits(void) return cr4; } -static void test_cr_bits(struct kvm_vcpu *vcpu, uint64_t cr4) +static void test_cr_bits(struct kvm_vcpu *vcpu, u64 cr4) { struct kvm_sregs sregs; int rc, i; diff --git a/tools/testing/selftests/kvm/x86/sev_init2_tests.c b/tools/testing/selftests/kvm/x86/sev_init2_tests.c index b238615196ad..8eeba2327c7c 100644 --- a/tools/testing/selftests/kvm/x86/sev_init2_tests.c +++ b/tools/testing/selftests/kvm/x86/sev_init2_tests.c @@ -34,7 +34,7 @@ static int __sev_ioctl(int vm_fd, int cmd_id, void *data) { struct kvm_sev_cmd cmd = { .id = cmd_id, - .data = (uint64_t)data, + .data = (u64)data, .sev_fd = open_sev_dev_path_or_exit(), }; int ret; @@ -94,7 +94,7 @@ void test_vm_types(void) "VM type is KVM_X86_SW_PROTECTED_VM"); } -void test_flags(uint32_t vm_type) +void test_flags(u32 vm_type) { int i; @@ -104,7 +104,7 @@ void test_flags(uint32_t vm_type) "invalid flag"); } -void test_features(uint32_t vm_type, uint64_t supported_features) +void test_features(u32 vm_type, u64 supported_features) { int i; diff --git a/tools/testing/selftests/kvm/x86/sev_migrate_tests.c b/tools/testing/selftests/kvm/x86/sev_migrate_tests.c index 0a6dfba3905b..6b0928e69051 100644 --- a/tools/testing/selftests/kvm/x86/sev_migrate_tests.c +++ b/tools/testing/selftests/kvm/x86/sev_migrate_tests.c @@ -36,8 +36,6 @@ static struct kvm_vm *sev_vm_create(bool es) sev_vm_launch(vm, es ? SEV_POLICY_ES : 0); - if (es) - vm_sev_ioctl(vm, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL); return vm; } diff --git a/tools/testing/selftests/kvm/x86/sev_smoke_test.c b/tools/testing/selftests/kvm/x86/sev_smoke_test.c index 8bd37a476f15..1a49ee391586 100644 --- a/tools/testing/selftests/kvm/x86/sev_smoke_test.c +++ b/tools/testing/selftests/kvm/x86/sev_smoke_test.c @@ -13,9 +13,9 @@ #include "linux/psp-sev.h" #include "sev.h" -static void guest_sev_test_msr(uint32_t msr) +static void guest_sev_test_msr(u32 msr) { - uint64_t val = rdmsr(msr); + u64 val = rdmsr(msr); wrmsr(msr, val); GUEST_ASSERT(val == rdmsr(msr)); @@ -23,7 +23,7 @@ static void guest_sev_test_msr(uint32_t msr) #define guest_sev_test_reg(reg) \ do { \ - uint64_t val = get_##reg(); \ + u64 val = get_##reg(); \ \ set_##reg(val); \ GUEST_ASSERT(val == get_##reg()); \ @@ -42,7 +42,7 @@ static void guest_sev_test_regs(void) static void guest_snp_code(void) { - uint64_t sev_msr = rdmsr(MSR_AMD64_SEV); + u64 sev_msr = rdmsr(MSR_AMD64_SEV); GUEST_ASSERT(sev_msr & MSR_AMD64_SEV_ENABLED); GUEST_ASSERT(sev_msr & MSR_AMD64_SEV_ES_ENABLED); @@ -104,19 +104,19 @@ static void compare_xsave(u8 *from_host, u8 *from_guest) abort(); } -static void test_sync_vmsa(uint32_t type, uint64_t policy) +static void test_sync_vmsa(u32 type, u64 policy) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; - vm_vaddr_t gva; + gva_t gva; void *hva; double x87val = M_PI; struct kvm_xsave __attribute__((aligned(64))) xsave = { 0 }; vm = vm_sev_create_with_one_vcpu(type, guest_code_xsave, &vcpu); - gva = vm_vaddr_alloc_shared(vm, PAGE_SIZE, KVM_UTIL_MIN_VADDR, - MEM_REGION_TEST_DATA); + gva = vm_alloc_shared(vm, PAGE_SIZE, KVM_UTIL_MIN_VADDR, + MEM_REGION_TEST_DATA); hva = addr_gva2hva(vm, gva); vcpu_args_set(vcpu, 1, gva); @@ -150,7 +150,7 @@ static void test_sync_vmsa(uint32_t type, uint64_t policy) kvm_vm_free(vm); } -static void test_sev(void *guest_code, uint32_t type, uint64_t policy) +static void test_sev(void *guest_code, u32 type, u64 policy) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; @@ -201,7 +201,7 @@ static void guest_shutdown_code(void) __asm__ __volatile__("ud2"); } -static void test_sev_shutdown(uint32_t type, uint64_t policy) +static void test_sev_shutdown(u32 type, u64 policy) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; @@ -218,7 +218,7 @@ static void test_sev_shutdown(uint32_t type, uint64_t policy) kvm_vm_free(vm); } -static void test_sev_smoke(void *guest, uint32_t type, uint64_t policy) +static void test_sev_smoke(void *guest, u32 type, u64 policy) { const u64 xf_mask = XFEATURE_MASK_X87_AVX; diff --git a/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c b/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c index 0e8aec568010..3dca85e95478 100644 --- a/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c +++ b/tools/testing/selftests/kvm/x86/smaller_maxphyaddr_emulation_test.c @@ -20,8 +20,8 @@ static void guest_code(bool tdp_enabled) { - uint64_t error_code; - uint64_t vector; + u64 error_code; + u64 vector; vector = kvm_asm_safe_ec(FLDS_MEM_EAX, error_code, "a"(MEM_REGION_GVA)); @@ -47,8 +47,8 @@ int main(int argc, char *argv[]) struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct ucall uc; - uint64_t *hva; - uint64_t gpa; + u64 *hva; + gpa_t gpa; int rc; TEST_REQUIRE(kvm_has_cap(KVM_CAP_SMALLER_MAXPHYADDR)); diff --git a/tools/testing/selftests/kvm/x86/smm_test.c b/tools/testing/selftests/kvm/x86/smm_test.c index ade8412bf94a..740051167dbd 100644 --- a/tools/testing/selftests/kvm/x86/smm_test.c +++ b/tools/testing/selftests/kvm/x86/smm_test.c @@ -34,13 +34,13 @@ * independent subset of asm here. * SMI handler always report back fixed stage SMRAM_STAGE. */ -uint8_t smi_handler[] = { +u8 smi_handler[] = { 0xb0, SMRAM_STAGE, /* mov $SMRAM_STAGE, %al */ 0xe4, SYNC_PORT, /* in $SYNC_PORT, %al */ 0x0f, 0xaa, /* rsm */ }; -static inline void sync_with_host(uint64_t phase) +static inline void sync_with_host(u64 phase) { asm volatile("in $" XSTR(SYNC_PORT)", %%al \n" : "+a" (phase)); @@ -65,7 +65,7 @@ static void guest_code(void *arg) { #define L2_GUEST_STACK_SIZE 64 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; - uint64_t apicbase = rdmsr(MSR_IA32_APICBASE); + u64 apicbase = rdmsr(MSR_IA32_APICBASE); struct svm_test_data *svm = arg; struct vmx_pages *vmx_pages = arg; @@ -113,7 +113,7 @@ static void guest_code(void *arg) int main(int argc, char *argv[]) { - vm_vaddr_t nested_gva = 0; + gva_t nested_gva = 0; struct kvm_vcpu *vcpu; struct kvm_regs regs; diff --git a/tools/testing/selftests/kvm/x86/state_test.c b/tools/testing/selftests/kvm/x86/state_test.c index f2c7a1c297e3..409c6cc9f921 100644 --- a/tools/testing/selftests/kvm/x86/state_test.c +++ b/tools/testing/selftests/kvm/x86/state_test.c @@ -26,7 +26,9 @@ void svm_l2_guest_code(void) GUEST_SYNC(4); /* Exit to L1 */ vmcall(); + clgi(); GUEST_SYNC(6); + stgi(); /* Done, exit to L1 and never come back. */ vmcall(); } @@ -41,6 +43,8 @@ static void svm_l1_guest_code(struct svm_test_data *svm) generic_svm_setup(svm, svm_l2_guest_code, &l2_guest_stack[L2_GUEST_STACK_SIZE]); + vmcb->control.int_ctl |= (V_GIF_ENABLE_MASK | V_GIF_MASK); + GUEST_SYNC(3); run_guest(vmcb, svm->vmcb_gpa); GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL); @@ -140,8 +144,8 @@ static void __attribute__((__flatten__)) guest_code(void *arg) GUEST_SYNC(1); if (this_cpu_has(X86_FEATURE_XSAVE)) { - uint64_t supported_xcr0 = this_cpu_supported_xcr0(); - uint8_t buffer[PAGE_SIZE]; + u64 supported_xcr0 = this_cpu_supported_xcr0(); + u8 buffer[PAGE_SIZE]; memset(buffer, 0xcc, sizeof(buffer)); @@ -168,8 +172,8 @@ static void __attribute__((__flatten__)) guest_code(void *arg) } if (this_cpu_has(X86_FEATURE_MPX)) { - uint64_t bounds[2] = { 10, 0xffffffffull }; - uint64_t output[2] = { }; + u64 bounds[2] = { 10, 0xffffffffull }; + u64 output[2] = { }; GUEST_ASSERT(supported_xcr0 & XFEATURE_MASK_BNDREGS); GUEST_ASSERT(supported_xcr0 & XFEATURE_MASK_BNDCSR); @@ -222,10 +226,39 @@ static void __attribute__((__flatten__)) guest_code(void *arg) GUEST_DONE(); } +void svm_check_nested_state(int stage, struct kvm_x86_state *state) +{ + struct vmcb *vmcb = (struct vmcb *)state->nested.data.svm; + + if (kvm_cpu_has(X86_FEATURE_VGIF)) { + if (stage == 4) + TEST_ASSERT_EQ(!!(vmcb->control.int_ctl & V_GIF_MASK), 1); + if (stage == 6) + TEST_ASSERT_EQ(!!(vmcb->control.int_ctl & V_GIF_MASK), 0); + } + + if (kvm_cpu_has(X86_FEATURE_NRIPS)) { + /* + * GUEST_SYNC() causes IO emulation in KVM, in which case the + * RIP is advanced before exiting to userspace. Hence, the RIP + * in the saved state should be the same as nRIP saved by the + * CPU in the VMCB. + */ + if (stage == 6) + TEST_ASSERT_EQ(vmcb->control.next_rip, state->regs.rip); + } +} + +void check_nested_state(int stage, struct kvm_x86_state *state) +{ + if (kvm_has_cap(KVM_CAP_NESTED_STATE) && kvm_cpu_has(X86_FEATURE_SVM)) + svm_check_nested_state(stage, state); +} + int main(int argc, char *argv[]) { - uint64_t *xstate_bv, saved_xstate_bv; - vm_vaddr_t nested_gva = 0; + u64 *xstate_bv, saved_xstate_bv; + gva_t nested_gva = 0; struct kvm_cpuid2 empty_cpuid = {}; struct kvm_regs regs1, regs2; struct kvm_vcpu *vcpu, *vcpuN; @@ -278,6 +311,8 @@ int main(int argc, char *argv[]) kvm_vm_release(vm); + check_nested_state(stage, state); + /* Restore state in a new VM. */ vcpu = vm_recreate_with_one_vcpu(vm); vcpu_load_state(vcpu, state); @@ -296,7 +331,7 @@ int main(int argc, char *argv[]) * supported features, even if something goes awry in saving * the original snapshot. */ - xstate_bv = (void *)&((uint8_t *)state->xsave->region)[512]; + xstate_bv = (void *)&((u8 *)state->xsave->region)[512]; saved_xstate_bv = *xstate_bv; vcpuN = __vm_vcpu_add(vm, vcpu->id + 1); diff --git a/tools/testing/selftests/kvm/x86/svm_int_ctl_test.c b/tools/testing/selftests/kvm/x86/svm_int_ctl_test.c index 917b6066cfc1..d3cc5e4f7883 100644 --- a/tools/testing/selftests/kvm/x86/svm_int_ctl_test.c +++ b/tools/testing/selftests/kvm/x86/svm_int_ctl_test.c @@ -82,7 +82,7 @@ static void l1_guest_code(struct svm_test_data *svm) int main(int argc, char *argv[]) { struct kvm_vcpu *vcpu; - vm_vaddr_t svm_gva; + gva_t svm_gva; struct kvm_vm *vm; struct ucall uc; diff --git a/tools/testing/selftests/kvm/x86/svm_lbr_nested_state.c b/tools/testing/selftests/kvm/x86/svm_lbr_nested_state.c new file mode 100644 index 000000000000..7fbfaa054c95 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/svm_lbr_nested_state.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026, Google, Inc. + */ + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" +#include "svm_util.h" + + +#define L2_GUEST_STACK_SIZE 64 + +#define DO_BRANCH() do { asm volatile("jmp 1f\n 1: nop"); } while (0) + +struct lbr_branch { + u64 from, to; +}; + +volatile struct lbr_branch l2_branch; + +#define RECORD_AND_CHECK_BRANCH(b) \ +do { \ + wrmsr(MSR_IA32_DEBUGCTLMSR, DEBUGCTLMSR_LBR); \ + DO_BRANCH(); \ + (b)->from = rdmsr(MSR_IA32_LASTBRANCHFROMIP); \ + (b)->to = rdmsr(MSR_IA32_LASTBRANCHTOIP); \ + /* Disable LBR right after to avoid overriding the IPs */ \ + wrmsr(MSR_IA32_DEBUGCTLMSR, 0); \ + \ + GUEST_ASSERT_NE((b)->from, 0); \ + GUEST_ASSERT_NE((b)->to, 0); \ +} while (0) + +#define CHECK_BRANCH_MSRS(b) \ +do { \ + GUEST_ASSERT_EQ((b)->from, rdmsr(MSR_IA32_LASTBRANCHFROMIP)); \ + GUEST_ASSERT_EQ((b)->to, rdmsr(MSR_IA32_LASTBRANCHTOIP)); \ +} while (0) + +#define CHECK_BRANCH_VMCB(b, vmcb) \ +do { \ + GUEST_ASSERT_EQ((b)->from, vmcb->save.br_from); \ + GUEST_ASSERT_EQ((b)->to, vmcb->save.br_to); \ +} while (0) + +static void l2_guest_code(struct svm_test_data *svm) +{ + /* Record a branch, trigger save/restore, and make sure LBRs are intact */ + RECORD_AND_CHECK_BRANCH(&l2_branch); + GUEST_SYNC(true); + CHECK_BRANCH_MSRS(&l2_branch); + vmmcall(); +} + +static void l1_guest_code(struct svm_test_data *svm, bool nested_lbrv) +{ + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + struct vmcb *vmcb = svm->vmcb; + struct lbr_branch l1_branch; + + /* Record a branch, trigger save/restore, and make sure LBRs are intact */ + RECORD_AND_CHECK_BRANCH(&l1_branch); + GUEST_SYNC(true); + CHECK_BRANCH_MSRS(&l1_branch); + + /* Run L2, which will also do the same */ + generic_svm_setup(svm, l2_guest_code, + &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + if (nested_lbrv) + vmcb->control.misc_ctl2 = SVM_MISC2_ENABLE_V_LBR; + else + vmcb->control.misc_ctl2 &= ~SVM_MISC2_ENABLE_V_LBR; + + run_guest(vmcb, svm->vmcb_gpa); + GUEST_ASSERT(svm->vmcb->control.exit_code == SVM_EXIT_VMMCALL); + + /* Trigger save/restore one more time before checking, just for kicks */ + GUEST_SYNC(true); + + /* + * If LBR_CTL_ENABLE is set, L1 and L2 should have separate LBR MSRs, so + * expect L1's LBRs to remain intact and L2 LBRs to be in the VMCB. + * Otherwise, the MSRs are shared between L1 & L2 so expect L2's LBRs. + */ + if (nested_lbrv) { + CHECK_BRANCH_MSRS(&l1_branch); + CHECK_BRANCH_VMCB(&l2_branch, vmcb); + } else { + CHECK_BRANCH_MSRS(&l2_branch); + } + GUEST_DONE(); +} + +void test_lbrv_nested_state(bool nested_lbrv) +{ + struct kvm_x86_state *state = NULL; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + struct ucall uc; + gva_t svm_gva; + + pr_info("Testing with nested LBRV %s\n", nested_lbrv ? "enabled" : "disabled"); + + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); + vcpu_alloc_svm(vm, &svm_gva); + vcpu_args_set(vcpu, 2, svm_gva, nested_lbrv); + + for (;;) { + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); + switch (get_ucall(vcpu, &uc)) { + case UCALL_SYNC: + /* Save the vCPU state and restore it in a new VM on sync */ + pr_info("Guest triggered save/restore.\n"); + state = vcpu_save_state(vcpu); + kvm_vm_release(vm); + vcpu = vm_recreate_with_one_vcpu(vm); + vcpu_load_state(vcpu, state); + kvm_x86_state_cleanup(state); + break; + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + /* NOT REACHED */ + case UCALL_DONE: + goto done; + default: + TEST_FAIL("Unknown ucall %lu", uc.cmd); + } + } +done: + kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM)); + TEST_REQUIRE(kvm_is_lbrv_enabled()); + + test_lbrv_nested_state(/*nested_lbrv=*/false); + test_lbrv_nested_state(/*nested_lbrv=*/true); + + return 0; +} diff --git a/tools/testing/selftests/kvm/x86/svm_nested_clear_efer_svme.c b/tools/testing/selftests/kvm/x86/svm_nested_clear_efer_svme.c new file mode 100644 index 000000000000..6a89eaffc657 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/svm_nested_clear_efer_svme.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026, Google LLC. + */ +#include "kvm_util.h" +#include "vmx.h" +#include "svm_util.h" +#include "kselftest.h" + + +#define L2_GUEST_STACK_SIZE 64 + +static void l2_guest_code(void) +{ + unsigned long efer = rdmsr(MSR_EFER); + + /* generic_svm_setup() initializes EFER_SVME set for L2 */ + GUEST_ASSERT(efer & EFER_SVME); + wrmsr(MSR_EFER, efer & ~EFER_SVME); + + /* Unreachable, L1 should be shutdown */ + GUEST_ASSERT(0); +} + +static void l1_guest_code(struct svm_test_data *svm) +{ + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + + generic_svm_setup(svm, l2_guest_code, + &l2_guest_stack[L2_GUEST_STACK_SIZE]); + run_guest(svm->vmcb, svm->vmcb_gpa); + + /* Unreachable, L1 should be shutdown */ + GUEST_ASSERT(0); +} + +int main(int argc, char *argv[]) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + gva_t nested_gva = 0; + + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM)); + + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); + + vcpu_alloc_svm(vm, &nested_gva); + vcpu_args_set(vcpu, 1, nested_gva); + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN); + + kvm_vm_free(vm); + return 0; +} diff --git a/tools/testing/selftests/kvm/x86/svm_nested_shutdown_test.c b/tools/testing/selftests/kvm/x86/svm_nested_shutdown_test.c index 00135cbba35e..c6ea3d609a62 100644 --- a/tools/testing/selftests/kvm/x86/svm_nested_shutdown_test.c +++ b/tools/testing/selftests/kvm/x86/svm_nested_shutdown_test.c @@ -42,7 +42,7 @@ static void l1_guest_code(struct svm_test_data *svm, struct idt_entry *idt) int main(int argc, char *argv[]) { struct kvm_vcpu *vcpu; - vm_vaddr_t svm_gva; + gva_t svm_gva; struct kvm_vm *vm; TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM)); diff --git a/tools/testing/selftests/kvm/x86/svm_nested_soft_inject_test.c b/tools/testing/selftests/kvm/x86/svm_nested_soft_inject_test.c index 4bd1655f9e6d..f72f11d4c4f8 100644 --- a/tools/testing/selftests/kvm/x86/svm_nested_soft_inject_test.c +++ b/tools/testing/selftests/kvm/x86/svm_nested_soft_inject_test.c @@ -76,7 +76,7 @@ static void l2_guest_code_nmi(void) ud2(); } -static void l1_guest_code(struct svm_test_data *svm, uint64_t is_nmi, uint64_t idt_alt) +static void l1_guest_code(struct svm_test_data *svm, u64 is_nmi, u64 idt_alt) { #define L2_GUEST_STACK_SIZE 64 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; @@ -144,8 +144,8 @@ static void run_test(bool is_nmi) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; - vm_vaddr_t svm_gva; - vm_vaddr_t idt_alt_vm; + gva_t svm_gva; + gva_t idt_alt_vm; struct kvm_guest_debug debug; pr_info("Running %s test\n", is_nmi ? "NMI" : "soft int"); @@ -161,14 +161,14 @@ static void run_test(bool is_nmi) if (!is_nmi) { void *idt, *idt_alt; - idt_alt_vm = vm_vaddr_alloc_page(vm); + idt_alt_vm = vm_alloc_page(vm); idt_alt = addr_gva2hva(vm, idt_alt_vm); idt = addr_gva2hva(vm, vm->arch.idt); memcpy(idt_alt, idt, getpagesize()); } else { idt_alt_vm = 0; } - vcpu_args_set(vcpu, 3, svm_gva, (uint64_t)is_nmi, (uint64_t)idt_alt_vm); + vcpu_args_set(vcpu, 3, svm_gva, (u64)is_nmi, (u64)idt_alt_vm); memset(&debug, 0, sizeof(debug)); vcpu_guest_debug_set(vcpu, &debug); diff --git a/tools/testing/selftests/kvm/x86/svm_nested_vmcb12_gpa.c b/tools/testing/selftests/kvm/x86/svm_nested_vmcb12_gpa.c new file mode 100644 index 000000000000..a4935ce2fb99 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/svm_nested_vmcb12_gpa.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026, Google LLC. + */ +#include "kvm_util.h" +#include "vmx.h" +#include "svm_util.h" +#include "kselftest.h" +#include "kvm_test_harness.h" +#include "test_util.h" + + +#define L2_GUEST_STACK_SIZE 64 + +#define SYNC_GP 101 +#define SYNC_L2_STARTED 102 + +static unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + +static void guest_gp_handler(struct ex_regs *regs) +{ + GUEST_SYNC(SYNC_GP); +} + +static void l2_code(void) +{ + GUEST_SYNC(SYNC_L2_STARTED); + vmcall(); +} + +static void l1_vmrun(struct svm_test_data *svm, gpa_t gpa) +{ + generic_svm_setup(svm, l2_code, &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + asm volatile ("vmrun %[gpa]" : : [gpa] "a" (gpa) : "memory"); +} + +static void l1_vmload(struct svm_test_data *svm, gpa_t gpa) +{ + generic_svm_setup(svm, l2_code, &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + asm volatile ("vmload %[gpa]" : : [gpa] "a" (gpa) : "memory"); +} + +static void l1_vmsave(struct svm_test_data *svm, gpa_t gpa) +{ + generic_svm_setup(svm, l2_code, &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + asm volatile ("vmsave %[gpa]" : : [gpa] "a" (gpa) : "memory"); +} + +static void l1_vmexit(struct svm_test_data *svm, gpa_t gpa) +{ + generic_svm_setup(svm, l2_code, &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + run_guest(svm->vmcb, svm->vmcb_gpa); + GUEST_ASSERT(svm->vmcb->control.exit_code == SVM_EXIT_VMMCALL); + GUEST_DONE(); +} + +static u64 unmappable_gpa(struct kvm_vcpu *vcpu) +{ + struct userspace_mem_region *region; + u64 region_gpa_end, vm_gpa_end = 0; + int i; + + hash_for_each(vcpu->vm->regions.slot_hash, i, region, slot_node) { + region_gpa_end = region->region.guest_phys_addr + region->region.memory_size; + vm_gpa_end = max(vm_gpa_end, region_gpa_end); + } + + return vm_gpa_end; +} + +static void test_invalid_vmcb12(struct kvm_vcpu *vcpu) +{ + gva_t nested_gva = 0; + struct ucall uc; + + + vm_install_exception_handler(vcpu->vm, GP_VECTOR, guest_gp_handler); + vcpu_alloc_svm(vcpu->vm, &nested_gva); + vcpu_args_set(vcpu, 2, nested_gva, -1ULL); + vcpu_run(vcpu); + + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); + TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC); + TEST_ASSERT_EQ(uc.args[1], SYNC_GP); +} + +static void test_unmappable_vmcb12(struct kvm_vcpu *vcpu) +{ + gva_t nested_gva = 0; + + vcpu_alloc_svm(vcpu->vm, &nested_gva); + vcpu_args_set(vcpu, 2, nested_gva, unmappable_gpa(vcpu)); + vcpu_run(vcpu); + + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_INTERNAL_ERROR); + TEST_ASSERT_EQ(vcpu->run->emulation_failure.suberror, KVM_INTERNAL_ERROR_EMULATION); +} + +static void test_unmappable_vmcb12_vmexit(struct kvm_vcpu *vcpu) +{ + struct kvm_x86_state *state; + gva_t nested_gva = 0; + struct ucall uc; + + /* + * Enter L2 (with a legit vmcb12 GPA), then overwrite vmcb12 GPA with an + * unmappable GPA. KVM will fail to map vmcb12 on nested VM-Exit and + * cause a shutdown. + */ + vcpu_alloc_svm(vcpu->vm, &nested_gva); + vcpu_args_set(vcpu, 2, nested_gva, unmappable_gpa(vcpu)); + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); + TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC); + TEST_ASSERT_EQ(uc.args[1], SYNC_L2_STARTED); + + state = vcpu_save_state(vcpu); + state->nested.hdr.svm.vmcb_pa = unmappable_gpa(vcpu); + vcpu_load_state(vcpu, state); + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN); + + kvm_x86_state_cleanup(state); +} + +KVM_ONE_VCPU_TEST_SUITE(vmcb12_gpa); + +KVM_ONE_VCPU_TEST(vmcb12_gpa, vmrun_invalid, l1_vmrun) +{ + test_invalid_vmcb12(vcpu); +} + +KVM_ONE_VCPU_TEST(vmcb12_gpa, vmload_invalid, l1_vmload) +{ + test_invalid_vmcb12(vcpu); +} + +KVM_ONE_VCPU_TEST(vmcb12_gpa, vmsave_invalid, l1_vmsave) +{ + test_invalid_vmcb12(vcpu); +} + +KVM_ONE_VCPU_TEST(vmcb12_gpa, vmrun_unmappable, l1_vmrun) +{ + test_unmappable_vmcb12(vcpu); +} + +KVM_ONE_VCPU_TEST(vmcb12_gpa, vmload_unmappable, l1_vmload) +{ + test_unmappable_vmcb12(vcpu); +} + +KVM_ONE_VCPU_TEST(vmcb12_gpa, vmsave_unmappable, l1_vmsave) +{ + test_unmappable_vmcb12(vcpu); +} + +/* + * Invalid vmcb12_gpa cannot be test for #VMEXIT as KVM_SET_NESTED_STATE will + * reject it. + */ +KVM_ONE_VCPU_TEST(vmcb12_gpa, vmexit_unmappable, l1_vmexit) +{ + test_unmappable_vmcb12_vmexit(vcpu); +} + +int main(int argc, char *argv[]) +{ + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM)); + + return test_harness_run(argc, argv); +} diff --git a/tools/testing/selftests/kvm/x86/svm_vmcall_test.c b/tools/testing/selftests/kvm/x86/svm_vmcall_test.c index 8a62cca28cfb..b1887242f3b8 100644 --- a/tools/testing/selftests/kvm/x86/svm_vmcall_test.c +++ b/tools/testing/selftests/kvm/x86/svm_vmcall_test.c @@ -36,7 +36,7 @@ static void l1_guest_code(struct svm_test_data *svm) int main(int argc, char *argv[]) { struct kvm_vcpu *vcpu; - vm_vaddr_t svm_gva; + gva_t svm_gva; struct kvm_vm *vm; TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM)); diff --git a/tools/testing/selftests/kvm/x86/sync_regs_test.c b/tools/testing/selftests/kvm/x86/sync_regs_test.c index 8fa3948b0170..e0c52321f87c 100644 --- a/tools/testing/selftests/kvm/x86/sync_regs_test.c +++ b/tools/testing/selftests/kvm/x86/sync_regs_test.c @@ -20,7 +20,7 @@ #include "kvm_util.h" #include "processor.h" -#define UCALL_PIO_PORT ((uint16_t)0x1000) +#define UCALL_PIO_PORT ((u16)0x1000) struct ucall uc_none = { .cmd = UCALL_NONE, diff --git a/tools/testing/selftests/kvm/x86/triple_fault_event_test.c b/tools/testing/selftests/kvm/x86/triple_fault_event_test.c index 56306a19144a..f1c488e0d497 100644 --- a/tools/testing/selftests/kvm/x86/triple_fault_event_test.c +++ b/tools/testing/selftests/kvm/x86/triple_fault_event_test.c @@ -72,13 +72,13 @@ int main(void) if (has_vmx) { - vm_vaddr_t vmx_pages_gva; + gva_t vmx_pages_gva; vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code_vmx); vcpu_alloc_vmx(vm, &vmx_pages_gva); vcpu_args_set(vcpu, 1, vmx_pages_gva); } else { - vm_vaddr_t svm_gva; + gva_t svm_gva; vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code_svm); vcpu_alloc_svm(vm, &svm_gva); diff --git a/tools/testing/selftests/kvm/x86/tsc_msrs_test.c b/tools/testing/selftests/kvm/x86/tsc_msrs_test.c index 12b0964f4f13..91583969a14f 100644 --- a/tools/testing/selftests/kvm/x86/tsc_msrs_test.c +++ b/tools/testing/selftests/kvm/x86/tsc_msrs_test.c @@ -95,7 +95,7 @@ int main(void) { struct kvm_vcpu *vcpu; struct kvm_vm *vm; - uint64_t val; + u64 val; ksft_print_header(); ksft_set_plan(5); diff --git a/tools/testing/selftests/kvm/x86/tsc_scaling_sync.c b/tools/testing/selftests/kvm/x86/tsc_scaling_sync.c index 59c7304f805e..59da8d4da607 100644 --- a/tools/testing/selftests/kvm/x86/tsc_scaling_sync.c +++ b/tools/testing/selftests/kvm/x86/tsc_scaling_sync.c @@ -21,10 +21,10 @@ pthread_spinlock_t create_lock; #define TEST_TSC_KHZ 2345678UL #define TEST_TSC_OFFSET 200000000 -uint64_t tsc_sync; +u64 tsc_sync; static void guest_code(void) { - uint64_t start_tsc, local_tsc, tmp; + u64 start_tsc, local_tsc, tmp; start_tsc = rdtsc(); do { diff --git a/tools/testing/selftests/kvm/x86/ucna_injection_test.c b/tools/testing/selftests/kvm/x86/ucna_injection_test.c index 1e5e564523b3..df1ec8209c76 100644 --- a/tools/testing/selftests/kvm/x86/ucna_injection_test.c +++ b/tools/testing/selftests/kvm/x86/ucna_injection_test.c @@ -45,7 +45,7 @@ #define MCI_CTL2_RESERVED_BIT BIT_ULL(29) -static uint64_t supported_mcg_caps; +static u64 supported_mcg_caps; /* * Record states about the injected UCNA. @@ -53,30 +53,30 @@ static uint64_t supported_mcg_caps; * handler. Variables without the 'i_' prefixes are recorded in guest main * execution thread. */ -static volatile uint64_t i_ucna_rcvd; -static volatile uint64_t i_ucna_addr; -static volatile uint64_t ucna_addr; -static volatile uint64_t ucna_addr2; +static volatile u64 i_ucna_rcvd; +static volatile u64 i_ucna_addr; +static volatile u64 ucna_addr; +static volatile u64 ucna_addr2; struct thread_params { struct kvm_vcpu *vcpu; - uint64_t *p_i_ucna_rcvd; - uint64_t *p_i_ucna_addr; - uint64_t *p_ucna_addr; - uint64_t *p_ucna_addr2; + u64 *p_i_ucna_rcvd; + u64 *p_i_ucna_addr; + u64 *p_ucna_addr; + u64 *p_ucna_addr2; }; static void verify_apic_base_addr(void) { - uint64_t msr = rdmsr(MSR_IA32_APICBASE); - uint64_t base = GET_APIC_BASE(msr); + u64 msr = rdmsr(MSR_IA32_APICBASE); + u64 base = GET_APIC_BASE(msr); GUEST_ASSERT(base == APIC_DEFAULT_GPA); } static void ucna_injection_guest_code(void) { - uint64_t ctl2; + u64 ctl2; verify_apic_base_addr(); xapic_enable(); @@ -106,7 +106,7 @@ static void ucna_injection_guest_code(void) static void cmci_disabled_guest_code(void) { - uint64_t ctl2 = rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK)); + u64 ctl2 = rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK)); wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK), ctl2 | MCI_CTL2_CMCI_EN); GUEST_DONE(); @@ -114,7 +114,7 @@ static void cmci_disabled_guest_code(void) static void cmci_enabled_guest_code(void) { - uint64_t ctl2 = rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK)); + u64 ctl2 = rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK)); wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK), ctl2 | MCI_CTL2_RESERVED_BIT); GUEST_DONE(); @@ -145,14 +145,15 @@ static void run_vcpu_expect_gp(struct kvm_vcpu *vcpu) printf("vCPU received GP in guest.\n"); } -static void inject_ucna(struct kvm_vcpu *vcpu, uint64_t addr) { +static void inject_ucna(struct kvm_vcpu *vcpu, u64 addr) +{ /* * A UCNA error is indicated with VAL=1, UC=1, PCC=0, S=0 and AR=0 in * the IA32_MCi_STATUS register. * MSCOD=1 (BIT[16] - MscodDataRdErr). * MCACOD=0x0090 (Memory controller error format, channel 0) */ - uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN | + u64 status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | 0x10090; struct kvm_x86_mce mce = {}; mce.status = status; @@ -216,10 +217,10 @@ static void test_ucna_injection(struct kvm_vcpu *vcpu, struct thread_params *par { struct kvm_vm *vm = vcpu->vm; params->vcpu = vcpu; - params->p_i_ucna_rcvd = (uint64_t *)addr_gva2hva(vm, (uint64_t)&i_ucna_rcvd); - params->p_i_ucna_addr = (uint64_t *)addr_gva2hva(vm, (uint64_t)&i_ucna_addr); - params->p_ucna_addr = (uint64_t *)addr_gva2hva(vm, (uint64_t)&ucna_addr); - params->p_ucna_addr2 = (uint64_t *)addr_gva2hva(vm, (uint64_t)&ucna_addr2); + params->p_i_ucna_rcvd = (u64 *)addr_gva2hva(vm, (u64)&i_ucna_rcvd); + params->p_i_ucna_addr = (u64 *)addr_gva2hva(vm, (u64)&i_ucna_addr); + params->p_ucna_addr = (u64 *)addr_gva2hva(vm, (u64)&ucna_addr); + params->p_ucna_addr2 = (u64 *)addr_gva2hva(vm, (u64)&ucna_addr2); run_ucna_injection(params); @@ -242,7 +243,7 @@ static void test_ucna_injection(struct kvm_vcpu *vcpu, struct thread_params *par static void setup_mce_cap(struct kvm_vcpu *vcpu, bool enable_cmci_p) { - uint64_t mcg_caps = MCG_CTL_P | MCG_SER_P | MCG_LMCE_P | KVM_MAX_MCE_BANKS; + u64 mcg_caps = MCG_CTL_P | MCG_SER_P | MCG_LMCE_P | KVM_MAX_MCE_BANKS; if (enable_cmci_p) mcg_caps |= MCG_CMCI_P; @@ -250,7 +251,7 @@ static void setup_mce_cap(struct kvm_vcpu *vcpu, bool enable_cmci_p) vcpu_ioctl(vcpu, KVM_X86_SETUP_MCE, &mcg_caps); } -static struct kvm_vcpu *create_vcpu_with_mce_cap(struct kvm_vm *vm, uint32_t vcpuid, +static struct kvm_vcpu *create_vcpu_with_mce_cap(struct kvm_vm *vm, u32 vcpuid, bool enable_cmci_p, void *guest_code) { struct kvm_vcpu *vcpu = vm_vcpu_add(vm, vcpuid, guest_code); diff --git a/tools/testing/selftests/kvm/x86/userspace_io_test.c b/tools/testing/selftests/kvm/x86/userspace_io_test.c index be7d72f3c029..9c5a87576c2e 100644 --- a/tools/testing/selftests/kvm/x86/userspace_io_test.c +++ b/tools/testing/selftests/kvm/x86/userspace_io_test.c @@ -10,7 +10,7 @@ #include "kvm_util.h" #include "processor.h" -static void guest_ins_port80(uint8_t *buffer, unsigned int count) +static void guest_ins_port80(u8 *buffer, unsigned int count) { unsigned long end; @@ -26,7 +26,7 @@ static void guest_ins_port80(uint8_t *buffer, unsigned int count) static void guest_code(void) { - uint8_t buffer[8192]; + u8 buffer[8192]; int i; /* diff --git a/tools/testing/selftests/kvm/x86/userspace_msr_exit_test.c b/tools/testing/selftests/kvm/x86/userspace_msr_exit_test.c index 8463a9956410..2808ce727e5f 100644 --- a/tools/testing/selftests/kvm/x86/userspace_msr_exit_test.c +++ b/tools/testing/selftests/kvm/x86/userspace_msr_exit_test.c @@ -23,21 +23,21 @@ struct kvm_msr_filter filter_allow = { .nmsrs = 1, /* Test an MSR the kernel knows about. */ .base = MSR_IA32_XSS, - .bitmap = (uint8_t*)&deny_bits, + .bitmap = (u8 *)&deny_bits, }, { .flags = KVM_MSR_FILTER_READ | KVM_MSR_FILTER_WRITE, .nmsrs = 1, /* Test an MSR the kernel doesn't know about. */ .base = MSR_IA32_FLUSH_CMD, - .bitmap = (uint8_t*)&deny_bits, + .bitmap = (u8 *)&deny_bits, }, { .flags = KVM_MSR_FILTER_READ | KVM_MSR_FILTER_WRITE, .nmsrs = 1, /* Test a fabricated MSR that no one knows about. */ .base = MSR_NON_EXISTENT, - .bitmap = (uint8_t*)&deny_bits, + .bitmap = (u8 *)&deny_bits, }, }, }; @@ -49,7 +49,7 @@ struct kvm_msr_filter filter_fs = { .flags = KVM_MSR_FILTER_READ, .nmsrs = 1, .base = MSR_FS_BASE, - .bitmap = (uint8_t*)&deny_bits, + .bitmap = (u8 *)&deny_bits, }, }, }; @@ -61,12 +61,12 @@ struct kvm_msr_filter filter_gs = { .flags = KVM_MSR_FILTER_READ, .nmsrs = 1, .base = MSR_GS_BASE, - .bitmap = (uint8_t*)&deny_bits, + .bitmap = (u8 *)&deny_bits, }, }, }; -static uint64_t msr_non_existent_data; +static u64 msr_non_existent_data; static int guest_exception_count; static u32 msr_reads, msr_writes; @@ -77,7 +77,7 @@ static u8 bitmap_c0000000[KVM_MSR_FILTER_MAX_BITMAP_SIZE]; static u8 bitmap_c0000000_read[KVM_MSR_FILTER_MAX_BITMAP_SIZE]; static u8 bitmap_deadbeef[1] = { 0x1 }; -static void deny_msr(uint8_t *bitmap, u32 msr) +static void deny_msr(u8 *bitmap, u32 msr) { u32 idx = msr & (KVM_MSR_FILTER_MAX_BITMAP_SIZE - 1); @@ -142,26 +142,26 @@ struct kvm_msr_filter no_filter_deny = { * Note: Force test_rdmsr() to not be inlined to prevent the labels, * rdmsr_start and rdmsr_end, from being defined multiple times. */ -static noinline uint64_t test_rdmsr(uint32_t msr) +static noinline u64 test_rdmsr(u32 msr) { - uint32_t a, d; + u32 a, d; guest_exception_count = 0; __asm__ __volatile__("rdmsr_start: rdmsr; rdmsr_end:" : "=a"(a), "=d"(d) : "c"(msr) : "memory"); - return a | ((uint64_t) d << 32); + return a | ((u64)d << 32); } /* * Note: Force test_wrmsr() to not be inlined to prevent the labels, * wrmsr_start and wrmsr_end, from being defined multiple times. */ -static noinline void test_wrmsr(uint32_t msr, uint64_t value) +static noinline void test_wrmsr(u32 msr, u64 value) { - uint32_t a = value; - uint32_t d = value >> 32; + u32 a = value; + u32 d = value >> 32; guest_exception_count = 0; @@ -176,26 +176,26 @@ extern char wrmsr_start, wrmsr_end; * Note: Force test_em_rdmsr() to not be inlined to prevent the labels, * rdmsr_start and rdmsr_end, from being defined multiple times. */ -static noinline uint64_t test_em_rdmsr(uint32_t msr) +static noinline u64 test_em_rdmsr(u32 msr) { - uint32_t a, d; + u32 a, d; guest_exception_count = 0; __asm__ __volatile__(KVM_FEP "em_rdmsr_start: rdmsr; em_rdmsr_end:" : "=a"(a), "=d"(d) : "c"(msr) : "memory"); - return a | ((uint64_t) d << 32); + return a | ((u64)d << 32); } /* * Note: Force test_em_wrmsr() to not be inlined to prevent the labels, * wrmsr_start and wrmsr_end, from being defined multiple times. */ -static noinline void test_em_wrmsr(uint32_t msr, uint64_t value) +static noinline void test_em_wrmsr(u32 msr, u64 value) { - uint32_t a = value; - uint32_t d = value >> 32; + u32 a = value; + u32 d = value >> 32; guest_exception_count = 0; @@ -208,7 +208,7 @@ extern char em_wrmsr_start, em_wrmsr_end; static void guest_code_filter_allow(void) { - uint64_t data; + u64 data; /* * Test userspace intercepting rdmsr / wrmsr for MSR_IA32_XSS. @@ -328,7 +328,7 @@ static void guest_code_filter_deny(void) static void guest_code_permission_bitmap(void) { - uint64_t data; + u64 data; data = test_rdmsr(MSR_FS_BASE); GUEST_ASSERT(data == MSR_FS_BASE); @@ -391,7 +391,7 @@ static void check_for_guest_assert(struct kvm_vcpu *vcpu) } } -static void process_rdmsr(struct kvm_vcpu *vcpu, uint32_t msr_index) +static void process_rdmsr(struct kvm_vcpu *vcpu, u32 msr_index) { struct kvm_run *run = vcpu->run; @@ -423,7 +423,7 @@ static void process_rdmsr(struct kvm_vcpu *vcpu, uint32_t msr_index) } } -static void process_wrmsr(struct kvm_vcpu *vcpu, uint32_t msr_index) +static void process_wrmsr(struct kvm_vcpu *vcpu, u32 msr_index) { struct kvm_run *run = vcpu->run; @@ -464,7 +464,7 @@ static void process_ucall_done(struct kvm_vcpu *vcpu) uc.cmd, UCALL_DONE); } -static uint64_t process_ucall(struct kvm_vcpu *vcpu) +static u64 process_ucall(struct kvm_vcpu *vcpu) { struct ucall uc = {}; @@ -489,20 +489,20 @@ static uint64_t process_ucall(struct kvm_vcpu *vcpu) } static void run_guest_then_process_rdmsr(struct kvm_vcpu *vcpu, - uint32_t msr_index) + u32 msr_index) { vcpu_run(vcpu); process_rdmsr(vcpu, msr_index); } static void run_guest_then_process_wrmsr(struct kvm_vcpu *vcpu, - uint32_t msr_index) + u32 msr_index) { vcpu_run(vcpu); process_wrmsr(vcpu, msr_index); } -static uint64_t run_guest_then_process_ucall(struct kvm_vcpu *vcpu) +static u64 run_guest_then_process_ucall(struct kvm_vcpu *vcpu) { vcpu_run(vcpu); return process_ucall(vcpu); @@ -519,7 +519,7 @@ KVM_ONE_VCPU_TEST_SUITE(user_msr); KVM_ONE_VCPU_TEST(user_msr, msr_filter_allow, guest_code_filter_allow) { struct kvm_vm *vm = vcpu->vm; - uint64_t cmd; + u64 cmd; int rc; rc = kvm_check_cap(KVM_CAP_X86_USER_SPACE_MSR); @@ -732,7 +732,7 @@ static void run_msr_filter_flag_test(struct kvm_vm *vm) .flags = KVM_MSR_FILTER_READ, .nmsrs = 1, .base = 0, - .bitmap = (uint8_t *)&deny_bits, + .bitmap = (u8 *)&deny_bits, }, }, }; diff --git a/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c b/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c index a81a24761aac..1720113eae79 100644 --- a/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c @@ -38,7 +38,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages, unsigned long high_gpa) { #define L2_GUEST_STACK_SIZE 64 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; - uint32_t control; + u32 control; GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); GUEST_ASSERT(load_vmcs(vmx_pages)); @@ -72,7 +72,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages, unsigned long high_gpa) int main(int argc, char *argv[]) { unsigned long apic_access_addr = ~0ul; - vm_vaddr_t vmx_pages_gva; + gva_t vmx_pages_gva; unsigned long high_gpa; struct vmx_pages *vmx; bool done = false; diff --git a/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c b/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c index 337c53fddeff..80a4fd1e5bbb 100644 --- a/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_apicv_updates_test.c @@ -33,7 +33,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) { #define L2_GUEST_STACK_SIZE 64 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; - uint32_t control; + u32 control; GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); GUEST_ASSERT(load_vmcs(vmx_pages)); @@ -110,7 +110,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) int main(int argc, char *argv[]) { - vm_vaddr_t vmx_pages_gva; + gva_t vmx_pages_gva; struct vmx_pages *vmx; struct kvm_vcpu *vcpu; struct kvm_vm *vm; diff --git a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c index a100ee5f0009..a2eaceed9ad5 100644 --- a/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c +++ b/tools/testing/selftests/kvm/x86/vmx_invalid_nested_guest_state.c @@ -52,7 +52,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) int main(int argc, char *argv[]) { - vm_vaddr_t vmx_pages_gva; + gva_t vmx_pages_gva; struct kvm_sregs sregs; struct kvm_vcpu *vcpu; struct kvm_run *run; diff --git a/tools/testing/selftests/kvm/x86/vmx_msrs_test.c b/tools/testing/selftests/kvm/x86/vmx_msrs_test.c index 90720b6205f4..c1e8632a1bb6 100644 --- a/tools/testing/selftests/kvm/x86/vmx_msrs_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_msrs_test.c @@ -12,11 +12,10 @@ #include "kvm_util.h" #include "vmx.h" -static void vmx_fixed1_msr_test(struct kvm_vcpu *vcpu, uint32_t msr_index, - uint64_t mask) +static void vmx_fixed1_msr_test(struct kvm_vcpu *vcpu, u32 msr_index, u64 mask) { - uint64_t val = vcpu_get_msr(vcpu, msr_index); - uint64_t bit; + u64 val = vcpu_get_msr(vcpu, msr_index); + u64 bit; mask &= val; @@ -26,11 +25,10 @@ static void vmx_fixed1_msr_test(struct kvm_vcpu *vcpu, uint32_t msr_index, } } -static void vmx_fixed0_msr_test(struct kvm_vcpu *vcpu, uint32_t msr_index, - uint64_t mask) +static void vmx_fixed0_msr_test(struct kvm_vcpu *vcpu, u32 msr_index, u64 mask) { - uint64_t val = vcpu_get_msr(vcpu, msr_index); - uint64_t bit; + u64 val = vcpu_get_msr(vcpu, msr_index); + u64 bit; mask = ~mask | val; @@ -40,7 +38,7 @@ static void vmx_fixed0_msr_test(struct kvm_vcpu *vcpu, uint32_t msr_index, } } -static void vmx_fixed0and1_msr_test(struct kvm_vcpu *vcpu, uint32_t msr_index) +static void vmx_fixed0and1_msr_test(struct kvm_vcpu *vcpu, u32 msr_index) { vmx_fixed0_msr_test(vcpu, msr_index, GENMASK_ULL(31, 0)); vmx_fixed1_msr_test(vcpu, msr_index, GENMASK_ULL(63, 32)); @@ -68,10 +66,10 @@ static void vmx_save_restore_msrs_test(struct kvm_vcpu *vcpu) } static void __ia32_feature_control_msr_test(struct kvm_vcpu *vcpu, - uint64_t msr_bit, + u64 msr_bit, struct kvm_x86_cpu_feature feature) { - uint64_t val; + u64 val; vcpu_clear_cpuid_feature(vcpu, feature); @@ -90,7 +88,7 @@ static void __ia32_feature_control_msr_test(struct kvm_vcpu *vcpu, static void ia32_feature_control_msr_test(struct kvm_vcpu *vcpu) { - uint64_t supported_bits = FEAT_CTL_LOCKED | + u64 supported_bits = FEAT_CTL_LOCKED | FEAT_CTL_VMX_ENABLED_INSIDE_SMX | FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX | FEAT_CTL_SGX_LC_ENABLED | diff --git a/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c b/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c index 915c42001dba..f13dee317383 100644 --- a/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_nested_la57_state_test.c @@ -30,7 +30,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) #define L2_GUEST_STACK_SIZE 64 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; u64 guest_cr4; - vm_paddr_t pml5_pa, pml4_pa; + gpa_t pml5_pa, pml4_pa; u64 *pml5; u64 exit_reason; @@ -73,7 +73,7 @@ void guest_code(struct vmx_pages *vmx_pages) int main(int argc, char *argv[]) { - vm_vaddr_t vmx_pages_gva = 0; + gva_t vmx_pages_gva = 0; struct kvm_vm *vm; struct kvm_vcpu *vcpu; struct kvm_x86_state *state; diff --git a/tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c b/tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c index 7ff6f62e20a3..d004108dbdc6 100644 --- a/tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c @@ -52,16 +52,16 @@ static const union perf_capabilities format_caps = { .pebs_format = -1, }; -static void guest_test_perf_capabilities_gp(uint64_t val) +static void guest_test_perf_capabilities_gp(u64 val) { - uint8_t vector = wrmsr_safe(MSR_IA32_PERF_CAPABILITIES, val); + u8 vector = wrmsr_safe(MSR_IA32_PERF_CAPABILITIES, val); __GUEST_ASSERT(vector == GP_VECTOR, "Expected #GP for value '0x%lx', got %s", val, ex_str(vector)); } -static void guest_code(uint64_t current_val) +static void guest_code(u64 current_val) { int i; @@ -129,7 +129,7 @@ KVM_ONE_VCPU_TEST(vmx_pmu_caps, basic_perf_capabilities, guest_code) KVM_ONE_VCPU_TEST(vmx_pmu_caps, fungible_perf_capabilities, guest_code) { - const uint64_t fungible_caps = host_cap.capabilities & ~immutable_caps.capabilities; + const u64 fungible_caps = host_cap.capabilities & ~immutable_caps.capabilities; int bit; for_each_set_bit(bit, &fungible_caps, 64) { @@ -148,7 +148,7 @@ KVM_ONE_VCPU_TEST(vmx_pmu_caps, fungible_perf_capabilities, guest_code) */ KVM_ONE_VCPU_TEST(vmx_pmu_caps, immutable_perf_capabilities, guest_code) { - const uint64_t reserved_caps = (~host_cap.capabilities | + const u64 reserved_caps = (~host_cap.capabilities | immutable_caps.capabilities) & ~format_caps.capabilities; union perf_capabilities val = host_cap; @@ -210,7 +210,7 @@ KVM_ONE_VCPU_TEST(vmx_pmu_caps, lbr_perf_capabilities, guest_code) KVM_ONE_VCPU_TEST(vmx_pmu_caps, perf_capabilities_unsupported, guest_code) { - uint64_t val; + u64 val; int i, r; vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities); diff --git a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c index 00dd2ac07a61..1b7b6ba23de7 100644 --- a/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_preemption_timer_test.c @@ -152,7 +152,7 @@ void guest_code(struct vmx_pages *vmx_pages) int main(int argc, char *argv[]) { - vm_vaddr_t vmx_pages_gva = 0; + gva_t vmx_pages_gva = 0; struct kvm_regs regs1, regs2; struct kvm_vm *vm; diff --git a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c index ae4a4b6c05ca..39ce9a9369f5 100644 --- a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c +++ b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c @@ -48,20 +48,20 @@ * Incremented in the IPI handler. Provides evidence to the sender that the IPI * arrived at the destination */ -static volatile uint64_t ipis_rcvd; +static volatile u64 ipis_rcvd; /* Data struct shared between host main thread and vCPUs */ struct test_data_page { - uint32_t halter_apic_id; - volatile uint64_t hlt_count; - volatile uint64_t wake_count; - uint64_t ipis_sent; - uint64_t migrations_attempted; - uint64_t migrations_completed; - uint32_t icr; - uint32_t icr2; - uint32_t halter_tpr; - uint32_t halter_ppr; + u32 halter_apic_id; + volatile u64 hlt_count; + volatile u64 wake_count; + u64 ipis_sent; + u64 migrations_attempted; + u64 migrations_completed; + u32 icr; + u32 icr2; + u32 halter_tpr; + u32 halter_ppr; /* * Record local version register as a cross-check that APIC access @@ -69,19 +69,19 @@ struct test_data_page { * arch/x86/kvm/lapic.c). If test is failing, check that values match * to determine whether APIC access exits are working. */ - uint32_t halter_lvr; + u32 halter_lvr; }; struct thread_params { struct test_data_page *data; struct kvm_vcpu *vcpu; - uint64_t *pipis_rcvd; /* host address of ipis_rcvd global */ + u64 *pipis_rcvd; /* host address of ipis_rcvd global */ }; void verify_apic_base_addr(void) { - uint64_t msr = rdmsr(MSR_IA32_APICBASE); - uint64_t base = GET_APIC_BASE(msr); + u64 msr = rdmsr(MSR_IA32_APICBASE); + u64 base = GET_APIC_BASE(msr); GUEST_ASSERT(base == APIC_DEFAULT_GPA); } @@ -125,12 +125,12 @@ static void guest_ipi_handler(struct ex_regs *regs) static void sender_guest_code(struct test_data_page *data) { - uint64_t last_wake_count; - uint64_t last_hlt_count; - uint64_t last_ipis_rcvd_count; - uint32_t icr_val; - uint32_t icr2_val; - uint64_t tsc_start; + u64 last_wake_count; + u64 last_hlt_count; + u64 last_ipis_rcvd_count; + u32 icr_val; + u32 icr2_val; + u64 tsc_start; verify_apic_base_addr(); xapic_enable(); @@ -248,7 +248,7 @@ static void cancel_join_vcpu_thread(pthread_t thread, struct kvm_vcpu *vcpu) } void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, - uint64_t *pipis_rcvd) + u64 *pipis_rcvd) { long pages_not_moved; unsigned long nodemask = 0; @@ -259,9 +259,9 @@ void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs, int i; int from, to; unsigned long bit; - uint64_t hlt_count; - uint64_t wake_count; - uint64_t ipis_sent; + u64 hlt_count; + u64 wake_count; + u64 ipis_sent; fprintf(stderr, "Calling migrate_pages every %d microseconds\n", delay_usecs); @@ -393,12 +393,12 @@ int main(int argc, char *argv[]) int run_secs = 0; int delay_usecs = 0; struct test_data_page *data; - vm_vaddr_t test_data_page_vaddr; + gva_t test_data_page_gva; bool migrate = false; pthread_t threads[2]; struct thread_params params[2]; struct kvm_vm *vm; - uint64_t *pipis_rcvd; + u64 *pipis_rcvd; get_cmdline_args(argc, argv, &run_secs, &migrate, &delay_usecs); if (run_secs <= 0) @@ -414,16 +414,16 @@ int main(int argc, char *argv[]) params[1].vcpu = vm_vcpu_add(vm, 1, sender_guest_code); - test_data_page_vaddr = vm_vaddr_alloc_page(vm); - data = addr_gva2hva(vm, test_data_page_vaddr); + test_data_page_gva = vm_alloc_page(vm); + data = addr_gva2hva(vm, test_data_page_gva); memset(data, 0, sizeof(*data)); params[0].data = data; params[1].data = data; - vcpu_args_set(params[0].vcpu, 1, test_data_page_vaddr); - vcpu_args_set(params[1].vcpu, 1, test_data_page_vaddr); + vcpu_args_set(params[0].vcpu, 1, test_data_page_gva); + vcpu_args_set(params[1].vcpu, 1, test_data_page_gva); - pipis_rcvd = (uint64_t *)addr_gva2hva(vm, (uint64_t)&ipis_rcvd); + pipis_rcvd = (u64 *)addr_gva2hva(vm, (u64)&ipis_rcvd); params[0].pipis_rcvd = pipis_rcvd; params[1].pipis_rcvd = pipis_rcvd; diff --git a/tools/testing/selftests/kvm/x86/xapic_state_test.c b/tools/testing/selftests/kvm/x86/xapic_state_test.c index 3b4814c55722..637bb90c1d93 100644 --- a/tools/testing/selftests/kvm/x86/xapic_state_test.c +++ b/tools/testing/selftests/kvm/x86/xapic_state_test.c @@ -23,7 +23,7 @@ static void xapic_guest_code(void) xapic_enable(); while (1) { - uint64_t val = (u64)xapic_read_reg(APIC_IRR) | + u64 val = (u64)xapic_read_reg(APIC_IRR) | (u64)xapic_read_reg(APIC_IRR + 0x10) << 32; xapic_write_reg(APIC_ICR2, val >> 32); @@ -43,7 +43,7 @@ static void x2apic_guest_code(void) x2apic_enable(); do { - uint64_t val = x2apic_read_reg(APIC_IRR) | + u64 val = x2apic_read_reg(APIC_IRR) | x2apic_read_reg(APIC_IRR + 0x10) << 32; if (val & X2APIC_RSVD_BITS_MASK) { @@ -56,12 +56,12 @@ static void x2apic_guest_code(void) } while (1); } -static void ____test_icr(struct xapic_vcpu *x, uint64_t val) +static void ____test_icr(struct xapic_vcpu *x, u64 val) { struct kvm_vcpu *vcpu = x->vcpu; struct kvm_lapic_state xapic; struct ucall uc; - uint64_t icr; + u64 icr; /* * Tell the guest what ICR value to write. Use the IRR to pass info, @@ -93,7 +93,7 @@ static void ____test_icr(struct xapic_vcpu *x, uint64_t val) TEST_ASSERT_EQ(icr, val & ~APIC_ICR_BUSY); } -static void __test_icr(struct xapic_vcpu *x, uint64_t val) +static void __test_icr(struct xapic_vcpu *x, u64 val) { /* * The BUSY bit is reserved on both AMD and Intel, but only AMD treats @@ -109,7 +109,7 @@ static void __test_icr(struct xapic_vcpu *x, uint64_t val) static void test_icr(struct xapic_vcpu *x) { struct kvm_vcpu *vcpu = x->vcpu; - uint64_t icr, i, j; + u64 icr, i, j; icr = APIC_DEST_SELF | APIC_INT_ASSERT | APIC_DM_FIXED; for (i = 0; i <= 0xff; i++) @@ -142,9 +142,9 @@ static void test_icr(struct xapic_vcpu *x) __test_icr(x, -1ull & ~APIC_DM_FIXED_MASK); } -static void __test_apic_id(struct kvm_vcpu *vcpu, uint64_t apic_base) +static void __test_apic_id(struct kvm_vcpu *vcpu, u64 apic_base) { - uint32_t apic_id, expected; + u32 apic_id, expected; struct kvm_lapic_state xapic; vcpu_set_msr(vcpu, MSR_IA32_APICBASE, apic_base); @@ -170,9 +170,9 @@ static void __test_apic_id(struct kvm_vcpu *vcpu, uint64_t apic_base) */ static void test_apic_id(void) { - const uint32_t NR_VCPUS = 3; + const u32 NR_VCPUS = 3; struct kvm_vcpu *vcpus[NR_VCPUS]; - uint64_t apic_base; + u64 apic_base; struct kvm_vm *vm; int i; @@ -248,7 +248,7 @@ int main(int argc, char *argv[]) * drops writes, AMD does not). Account for the errata when checking * that KVM reads back what was written. */ - x.has_xavic_errata = host_cpu_is_amd && + x.has_xavic_errata = host_cpu_is_amd_compatible && get_kvm_amd_param_bool("avic"); vcpu_clear_cpuid_feature(x.vcpu, X86_FEATURE_X2APIC); diff --git a/tools/testing/selftests/kvm/x86/xapic_tpr_test.c b/tools/testing/selftests/kvm/x86/xapic_tpr_test.c index 3862134d9d40..ab25db2235d5 100644 --- a/tools/testing/selftests/kvm/x86/xapic_tpr_test.c +++ b/tools/testing/selftests/kvm/x86/xapic_tpr_test.c @@ -58,7 +58,7 @@ static void tpr_guest_irq_queue(void) if (is_x2apic) { x2apic_write_reg(APIC_SELF_IPI, IRQ_VECTOR); } else { - uint32_t icr, icr2; + u32 icr, icr2; icr = APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | IRQ_VECTOR; @@ -69,9 +69,9 @@ static void tpr_guest_irq_queue(void) } } -static uint8_t tpr_guest_tpr_get(void) +static u8 tpr_guest_tpr_get(void) { - uint32_t taskpri; + u32 taskpri; if (is_x2apic) taskpri = x2apic_read_reg(APIC_TASKPRI); @@ -81,9 +81,9 @@ static uint8_t tpr_guest_tpr_get(void) return GET_APIC_PRI(taskpri); } -static uint8_t tpr_guest_ppr_get(void) +static u8 tpr_guest_ppr_get(void) { - uint32_t procpri; + u32 procpri; if (is_x2apic) procpri = x2apic_read_reg(APIC_PROCPRI); @@ -93,9 +93,9 @@ static uint8_t tpr_guest_ppr_get(void) return GET_APIC_PRI(procpri); } -static uint8_t tpr_guest_cr8_get(void) +static u8 tpr_guest_cr8_get(void) { - uint64_t cr8; + u64 cr8; asm volatile ("mov %%cr8, %[cr8]\n\t" : [cr8] "=r"(cr8)); @@ -104,7 +104,7 @@ static uint8_t tpr_guest_cr8_get(void) static void tpr_guest_check_tpr_ppr_cr8_equal(void) { - uint8_t tpr; + u8 tpr; tpr = tpr_guest_tpr_get(); @@ -157,19 +157,19 @@ static void tpr_guest_code(void) GUEST_DONE(); } -static uint8_t lapic_tpr_get(struct kvm_lapic_state *xapic) +static u8 lapic_tpr_get(struct kvm_lapic_state *xapic) { return GET_APIC_PRI(*((u32 *)&xapic->regs[APIC_TASKPRI])); } -static void lapic_tpr_set(struct kvm_lapic_state *xapic, uint8_t val) +static void lapic_tpr_set(struct kvm_lapic_state *xapic, u8 val) { u32 *taskpri = (u32 *)&xapic->regs[APIC_TASKPRI]; *taskpri = SET_APIC_PRI(*taskpri, val); } -static uint8_t sregs_tpr(struct kvm_sregs *sregs) +static u8 sregs_tpr(struct kvm_sregs *sregs) { return sregs->cr8 & GENMASK(3, 0); } @@ -197,7 +197,7 @@ static void test_tpr_check_tpr_cr8_equal(struct kvm_vcpu *vcpu) static void test_tpr_set_tpr_for_irq(struct kvm_vcpu *vcpu, bool mask) { struct kvm_lapic_state xapic; - uint8_t tpr; + u8 tpr; static_assert(IRQ_VECTOR >= 16, "invalid IRQ vector number"); tpr = IRQ_VECTOR / 16; diff --git a/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c b/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c index d038c1571729..40dc9e6b3fad 100644 --- a/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c +++ b/tools/testing/selftests/kvm/x86/xcr0_cpuid_test.c @@ -21,7 +21,7 @@ */ #define ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0, xfeatures, dependencies) \ do { \ - uint64_t __supported = (supported_xcr0) & ((xfeatures) | (dependencies)); \ + u64 __supported = (supported_xcr0) & ((xfeatures) | (dependencies)); \ \ __GUEST_ASSERT((__supported & (xfeatures)) != (xfeatures) || \ __supported == ((xfeatures) | (dependencies)), \ @@ -39,7 +39,7 @@ do { \ */ #define ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0, xfeatures) \ do { \ - uint64_t __supported = (supported_xcr0) & (xfeatures); \ + u64 __supported = (supported_xcr0) & (xfeatures); \ \ __GUEST_ASSERT(!__supported || __supported == (xfeatures), \ "supported = 0x%lx, xfeatures = 0x%llx", \ @@ -48,8 +48,8 @@ do { \ static void guest_code(void) { - uint64_t initial_xcr0; - uint64_t supported_xcr0; + u64 initial_xcr0; + u64 supported_xcr0; int i, vector; set_cr4(get_cr4() | X86_CR4_OSXSAVE); diff --git a/tools/testing/selftests/kvm/x86/xen_shinfo_test.c b/tools/testing/selftests/kvm/x86/xen_shinfo_test.c index 23909b501ac2..5076f6a75455 100644 --- a/tools/testing/selftests/kvm/x86/xen_shinfo_test.c +++ b/tools/testing/selftests/kvm/x86/xen_shinfo_test.c @@ -116,15 +116,15 @@ struct pvclock_wall_clock { } __attribute__((__packed__)); struct vcpu_runstate_info { - uint32_t state; - uint64_t state_entry_time; - uint64_t time[5]; /* Extra field for overrun check */ + u32 state; + u64 state_entry_time; + u64 time[5]; /* Extra field for overrun check */ }; struct compat_vcpu_runstate_info { - uint32_t state; - uint64_t state_entry_time; - uint64_t time[5]; + u32 state; + u64 state_entry_time; + u64 time[5]; } __attribute__((__packed__)); struct arch_vcpu_info { @@ -133,8 +133,8 @@ struct arch_vcpu_info { }; struct vcpu_info { - uint8_t evtchn_upcall_pending; - uint8_t evtchn_upcall_mask; + u8 evtchn_upcall_pending; + u8 evtchn_upcall_mask; unsigned long evtchn_pending_sel; struct arch_vcpu_info arch; struct pvclock_vcpu_time_info time; @@ -145,7 +145,7 @@ struct shared_info { unsigned long evtchn_pending[64]; unsigned long evtchn_mask[64]; struct pvclock_wall_clock wc; - uint32_t wc_sec_hi; + u32 wc_sec_hi; /* arch_shared_info here */ }; @@ -658,7 +658,7 @@ int main(int argc, char *argv[]) printf("Testing RUNSTATE_ADJUST\n"); rst.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST; memset(&rst.u, 0, sizeof(rst.u)); - rst.u.runstate.state = (uint64_t)-1; + rst.u.runstate.state = (u64)-1; rst.u.runstate.time_blocked = 0x5a - rs->time[RUNSTATE_blocked]; rst.u.runstate.time_offline = @@ -1113,7 +1113,7 @@ int main(int argc, char *argv[]) /* Don't change the address, just trigger a write */ struct kvm_xen_vcpu_attr adj = { .type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST, - .u.runstate.state = (uint64_t)-1 + .u.runstate.state = (u64)-1 }; vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &adj); diff --git a/tools/testing/selftests/kvm/x86/xss_msr_test.c b/tools/testing/selftests/kvm/x86/xss_msr_test.c index f331a4e9bae3..12c63df6bbce 100644 --- a/tools/testing/selftests/kvm/x86/xss_msr_test.c +++ b/tools/testing/selftests/kvm/x86/xss_msr_test.c @@ -17,7 +17,7 @@ int main(int argc, char *argv[]) bool xss_in_msr_list; struct kvm_vm *vm; struct kvm_vcpu *vcpu; - uint64_t xss_val; + u64 xss_val; int i, r; /* Create VM */ diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c index c2878e3d5ef9..37c808fbe1e9 100644 --- a/tools/testing/selftests/liveupdate/liveupdate.c +++ b/tools/testing/selftests/liveupdate/liveupdate.c @@ -345,4 +345,45 @@ TEST_F(liveupdate_device, preserve_unsupported_fd) ASSERT_EQ(close(session_fd), 0); } +/* + * Test Case: Prevent Double Preservation + * + * Verifies that a file (memfd) can only be preserved once across all active + * sessions. Attempting to preserve it a second time, whether in the same or + * a different session, should fail with EBUSY. + */ +TEST_F(liveupdate_device, prevent_double_preservation) +{ + int session_fd1, session_fd2, mem_fd; + int ret; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + session_fd1 = create_session(self->fd1, "double-preserve-session-1"); + ASSERT_GE(session_fd1, 0); + session_fd2 = create_session(self->fd1, "double-preserve-session-2"); + ASSERT_GE(session_fd2, 0); + + mem_fd = memfd_create("test-memfd", 0); + ASSERT_GE(mem_fd, 0); + + /* First preservation should succeed */ + ASSERT_EQ(preserve_fd(session_fd1, mem_fd, 0x1111), 0); + + /* Second preservation in a different session should fail with EBUSY */ + ret = preserve_fd(session_fd2, mem_fd, 0x2222); + EXPECT_EQ(ret, -EBUSY); + + /* Second preservation in the same session (different token) should fail with EBUSY */ + ret = preserve_fd(session_fd1, mem_fd, 0x3333); + EXPECT_EQ(ret, -EBUSY); + + ASSERT_EQ(close(mem_fd), 0); + ASSERT_EQ(close(session_fd1), 0); + ASSERT_EQ(close(session_fd2), 0); +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh index 447769657634..44f4e703deb9 100755 --- a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh +++ b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh @@ -11,6 +11,11 @@ if [[ $(id -u) -ne 0 ]]; then exit $ksft_skip fi +if ! command -v killall >/dev/null 2>&1; then + echo "killall not available. Skipping..." + exit $ksft_skip +fi + nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages) fault_limit_file=limit_in_bytes diff --git a/tools/testing/selftests/mm/config b/tools/testing/selftests/mm/config index 1dbe2b4558ab..06f78bd232e2 100644 --- a/tools/testing/selftests/mm/config +++ b/tools/testing/selftests/mm/config @@ -13,3 +13,4 @@ CONFIG_PROFILING=y CONFIG_UPROBES=y CONFIG_MEMORY_FAILURE=y CONFIG_HWPOISON_INJECT=m +CONFIG_PROC_MEM_ALWAYS_FORCE=y diff --git a/tools/testing/selftests/mm/guard-regions.c b/tools/testing/selftests/mm/guard-regions.c index dbd21d66d383..48e8b1539be3 100644 --- a/tools/testing/selftests/mm/guard-regions.c +++ b/tools/testing/selftests/mm/guard-regions.c @@ -21,6 +21,7 @@ #include #include #include "vm_util.h" +#include "thp_settings.h" #include "../pidfd/pidfd.h" @@ -2195,6 +2196,9 @@ TEST_F(guard_regions, collapse) char *ptr; int i; + if (!thp_available()) + SKIP(return, "Transparent Hugepages not available\n"); + /* Need file to be correct size for tests for non-anon. */ if (variant->backing != ANON_BACKED) ASSERT_EQ(ftruncate(self->fd, size), 0); diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index e8328c89d855..788689497e92 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -34,6 +34,7 @@ */ #include #include +#include struct hmm_buffer { void *ptr; @@ -548,7 +549,7 @@ TEST_F(hmm, anon_write_child) for (migrate = 0; migrate < 2; ++migrate) { for (use_thp = 0; use_thp < 2; ++use_thp) { - npages = ALIGN(use_thp ? TWOMEG : HMM_BUFFER_SIZE, + npages = ALIGN(use_thp ? read_pmd_pagesize() : HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; ASSERT_NE(npages, 0); size = npages << self->page_shift; @@ -728,7 +729,7 @@ TEST_F(hmm, anon_write_huge) int *ptr; int ret; - size = 2 * TWOMEG; + size = 2 * read_pmd_pagesize(); buffer = malloc(sizeof(*buffer)); ASSERT_NE(buffer, NULL); @@ -744,7 +745,7 @@ TEST_F(hmm, anon_write_huge) buffer->fd, 0); ASSERT_NE(buffer->ptr, MAP_FAILED); - size = TWOMEG; + size /= 2; npages = size >> self->page_shift; map = (void *)ALIGN((uintptr_t)buffer->ptr, size); ret = madvise(map, size, MADV_HUGEPAGE); @@ -770,54 +771,6 @@ TEST_F(hmm, anon_write_huge) hmm_buffer_free(buffer); } -/* - * Read numeric data from raw and tagged kernel status files. Used to read - * /proc and /sys data (without a tag) and from /proc/meminfo (with a tag). - */ -static long file_read_ulong(char *file, const char *tag) -{ - int fd; - char buf[2048]; - int len; - char *p, *q; - long val; - - fd = open(file, O_RDONLY); - if (fd < 0) { - /* Error opening the file */ - return -1; - } - - len = read(fd, buf, sizeof(buf)); - close(fd); - if (len < 0) { - /* Error in reading the file */ - return -1; - } - if (len == sizeof(buf)) { - /* Error file is too large */ - return -1; - } - buf[len] = '\0'; - - /* Search for a tag if provided */ - if (tag) { - p = strstr(buf, tag); - if (!p) - return -1; /* looks like the line we want isn't there */ - p += strlen(tag); - } else - p = buf; - - val = strtol(p, &q, 0); - if (*q != ' ') { - /* Error parsing the file */ - return -1; - } - - return val; -} - /* * Write huge TLBFS page. */ @@ -826,15 +779,13 @@ TEST_F(hmm, anon_write_hugetlbfs) struct hmm_buffer *buffer; unsigned long npages; unsigned long size; - unsigned long default_hsize; + unsigned long default_hsize = default_huge_page_size(); unsigned long i; int *ptr; int ret; - default_hsize = file_read_ulong("/proc/meminfo", "Hugepagesize:"); - if (default_hsize < 0 || default_hsize*1024 < default_hsize) + if (!default_hsize) SKIP(return, "Huge page size could not be determined"); - default_hsize = default_hsize*1024; /* KB to B */ size = ALIGN(TWOMEG, default_hsize); npages = size >> self->page_shift; @@ -1606,7 +1557,7 @@ TEST_F(hmm, compound) struct hmm_buffer *buffer; unsigned long npages; unsigned long size; - unsigned long default_hsize; + unsigned long default_hsize = default_huge_page_size(); int *ptr; unsigned char *m; int ret; @@ -1614,10 +1565,8 @@ TEST_F(hmm, compound) /* Skip test if we can't allocate a hugetlbfs page. */ - default_hsize = file_read_ulong("/proc/meminfo", "Hugepagesize:"); - if (default_hsize < 0 || default_hsize*1024 < default_hsize) + if (!default_hsize) SKIP(return, "Huge page size could not be determined"); - default_hsize = default_hsize*1024; /* KB to B */ size = ALIGN(TWOMEG, default_hsize); npages = size >> self->page_shift; @@ -2106,7 +2055,7 @@ TEST_F(hmm, migrate_anon_huge_empty) int *ptr; int ret; - size = TWOMEG; + size = read_pmd_pagesize(); buffer = malloc(sizeof(*buffer)); ASSERT_NE(buffer, NULL); @@ -2158,7 +2107,7 @@ TEST_F(hmm, migrate_anon_huge_zero) int ret; int val; - size = TWOMEG; + size = read_pmd_pagesize(); buffer = malloc(sizeof(*buffer)); ASSERT_NE(buffer, NULL); @@ -2221,7 +2170,7 @@ TEST_F(hmm, migrate_anon_huge_free) int *ptr; int ret; - size = TWOMEG; + size = read_pmd_pagesize(); buffer = malloc(sizeof(*buffer)); ASSERT_NE(buffer, NULL); @@ -2280,7 +2229,7 @@ TEST_F(hmm, migrate_anon_huge_fault) int *ptr; int ret; - size = TWOMEG; + size = read_pmd_pagesize(); buffer = malloc(sizeof(*buffer)); ASSERT_NE(buffer, NULL); @@ -2332,7 +2281,7 @@ TEST_F(hmm, migrate_partial_unmap_fault) { struct hmm_buffer *buffer; unsigned long npages; - unsigned long size = TWOMEG; + unsigned long size = read_pmd_pagesize(); unsigned long i; void *old_ptr; void *map; @@ -2398,7 +2347,7 @@ TEST_F(hmm, migrate_remap_fault) { struct hmm_buffer *buffer; unsigned long npages; - unsigned long size = TWOMEG; + unsigned long size = read_pmd_pagesize(); unsigned long i; void *old_ptr, *new_ptr = NULL; void *map; @@ -2498,7 +2447,7 @@ TEST_F(hmm, migrate_anon_huge_err) int *ptr; int ret; - size = TWOMEG; + size = read_pmd_pagesize(); buffer = malloc(sizeof(*buffer)); ASSERT_NE(buffer, NULL); @@ -2593,7 +2542,7 @@ TEST_F(hmm, migrate_anon_huge_zero_err) int *ptr; int ret; - size = TWOMEG; + size = read_pmd_pagesize(); buffer = malloc(sizeof(*buffer)); ASSERT_NE(buffer, NULL); diff --git a/tools/testing/selftests/mm/hugetlb_dio.c b/tools/testing/selftests/mm/hugetlb_dio.c index 9ac62eb4c97d..31a054fa8134 100644 --- a/tools/testing/selftests/mm/hugetlb_dio.c +++ b/tools/testing/selftests/mm/hugetlb_dio.c @@ -17,12 +17,57 @@ #include #include #include +#include #include "vm_util.h" #include "kselftest.h" -void run_dio_using_hugetlb(unsigned int start_off, unsigned int end_off) +#ifndef STATX_DIOALIGN +#define STATX_DIOALIGN 0x00002000U +#endif + +static int get_dio_alignment(int fd) +{ + struct statx stx; + int ret; + + ret = syscall(__NR_statx, fd, "", AT_EMPTY_PATH, STATX_DIOALIGN, &stx); + if (ret < 0) + return -1; + + /* + * If STATX_DIOALIGN is unsupported, assume no alignment + * constraint and let the test proceed. + */ + if (!(stx.stx_mask & STATX_DIOALIGN) || !stx.stx_dio_offset_align) + return 1; + + return stx.stx_dio_offset_align; +} + +static bool check_dio_alignment(unsigned int start_off, + unsigned int end_off, unsigned int align) +{ + unsigned int writesize = end_off - start_off; + + /* + * The kernel's DIO path checks that file offset, length, and + * buffer address are all multiples of dio_offset_align. When + * this test case's parameters don't satisfy that, the write + * would fail with -EINVAL before exercising the hugetlb unpin + * path, so skip. + */ + if (start_off % align != 0 || writesize % align != 0) { + ksft_test_result_skip("DIO align=%u incompatible with offset %u writesize %u\n", + align, start_off, writesize); + return false; + } + + return true; +} + +static void run_dio_using_hugetlb(int fd, unsigned int start_off, + unsigned int end_off, unsigned int align) { - int fd; char *buffer = NULL; char *orig_buffer = NULL; size_t h_pagesize = 0; @@ -32,6 +77,9 @@ void run_dio_using_hugetlb(unsigned int start_off, unsigned int end_off) const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB; const int mmap_prot = PROT_READ | PROT_WRITE; + if (!check_dio_alignment(start_off, end_off, align)) + return; + writesize = end_off - start_off; /* Get the default huge page size */ @@ -39,10 +87,9 @@ void run_dio_using_hugetlb(unsigned int start_off, unsigned int end_off) if (!h_pagesize) ksft_exit_fail_msg("Unable to determine huge page size\n"); - /* Open the file to DIO */ - fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664); - if (fd < 0) - ksft_exit_fail_perror("Error opening file\n"); + /* Reset file position since fd is shared across tests */ + if (lseek(fd, 0, SEEK_SET) < 0) + ksft_exit_fail_perror("lseek failed\n"); /* Get the free huge pages before allocation */ free_hpage_b = get_free_hugepages(); @@ -71,7 +118,6 @@ void run_dio_using_hugetlb(unsigned int start_off, unsigned int end_off) /* unmap the huge page */ munmap(orig_buffer, h_pagesize); - close(fd); /* Get the free huge pages after unmap*/ free_hpage_a = get_free_hugepages(); @@ -89,37 +135,38 @@ void run_dio_using_hugetlb(unsigned int start_off, unsigned int end_off) int main(void) { - size_t pagesize = 0; - int fd; + int fd, align; + const size_t pagesize = psize(); ksft_print_header(); - /* Open the file to DIO */ - fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664); - if (fd < 0) - ksft_exit_skip("Unable to allocate file: %s\n", strerror(errno)); - close(fd); - /* Check if huge pages are free */ if (!get_free_hugepages()) ksft_exit_skip("No free hugepage, exiting\n"); + fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664); + if (fd < 0) + ksft_exit_skip("Unable to allocate file: %s\n", strerror(errno)); + + align = get_dio_alignment(fd); + if (align < 0) + ksft_exit_skip("Unable to obtain DIO alignment: %s\n", + strerror(errno)); ksft_set_plan(4); - /* Get base page size */ - pagesize = psize(); - /* start and end is aligned to pagesize */ - run_dio_using_hugetlb(0, (pagesize * 3)); + run_dio_using_hugetlb(fd, 0, (pagesize * 3), align); /* start is aligned but end is not aligned */ - run_dio_using_hugetlb(0, (pagesize * 3) - (pagesize / 2)); + run_dio_using_hugetlb(fd, 0, (pagesize * 3) - (pagesize / 2), align); /* start is unaligned and end is aligned */ - run_dio_using_hugetlb(pagesize / 2, (pagesize * 3)); + run_dio_using_hugetlb(fd, pagesize / 2, (pagesize * 3), align); /* both start and end are unaligned */ - run_dio_using_hugetlb(pagesize / 2, (pagesize * 3) + (pagesize / 2)); + run_dio_using_hugetlb(fd, pagesize / 2, (pagesize * 3) + (pagesize / 2), align); + + close(fd); ksft_finished(); } diff --git a/tools/testing/selftests/mm/merge.c b/tools/testing/selftests/mm/merge.c index 10b686102b79..519e5ac02db7 100644 --- a/tools/testing/selftests/mm/merge.c +++ b/tools/testing/selftests/mm/merge.c @@ -48,6 +48,19 @@ static pid_t do_fork(struct procmap_fd *procmap) return 0; } +#ifdef __NR_mseal +static int sys_mseal(void *ptr, size_t len, unsigned long flags) +{ + return syscall(__NR_mseal, (unsigned long)ptr, len, flags); +} +#else +static int sys_mseal(void *ptr, size_t len, unsigned long flags) +{ + errno = ENOSYS; + return -1; +} +#endif + FIXTURE_SETUP(merge) { self->page_size = psize(); @@ -1217,6 +1230,81 @@ TEST_F(merge, mremap_correct_placed_faulted) ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size); } +TEST_F(merge, merge_vmas_with_mseal) +{ + unsigned int page_size = self->page_size; + struct procmap_fd *procmap = &self->procmap; + char *ptr, *ptr2, *ptr3; + /* We need our own as cannot munmap() once sealed. */ + char *carveout; + + /* Invalid mseal() call to see if implemented. */ + ASSERT_EQ(sys_mseal(NULL, 0, ~0UL), -1); + if (errno == ENOSYS) + SKIP(return, "mseal not supported, skipping."); + + /* Map carveout. */ + carveout = mmap(NULL, 5 * page_size, PROT_NONE, + MAP_PRIVATE | MAP_ANON, -1, 0); + ASSERT_NE(carveout, MAP_FAILED); + + /* + * Map 3 separate VMAs: + * + * |-----------|-----------|-----------| + * | RW | RWE | RO | + * |-----------|-----------|-----------| + * ptr ptr2 ptr3 + */ + ptr = mmap(&carveout[page_size], page_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr, MAP_FAILED); + ptr2 = mmap(&carveout[2 * page_size], page_size, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr2, MAP_FAILED); + ptr3 = mmap(&carveout[3 * page_size], page_size, PROT_READ, + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); + ASSERT_NE(ptr3, MAP_FAILED); + + /* + * mseal the second VMA: + * + * |-----------|-----------|-----------| + * | RW | RWES | RO | + * |-----------|-----------|-----------| + * ptr ptr2 ptr3 + */ + ASSERT_EQ(sys_mseal(ptr2, page_size, 0), 0); + + /* Make first VMA mergeable upon mseal. */ + ASSERT_EQ(mprotect(ptr, page_size, + PROT_READ | PROT_WRITE | PROT_EXEC), 0); + /* + * At this point we have: + * + * |-----------|-----------|-----------| + * | RWE | RWES | RO | + * |-----------|-----------|-----------| + * ptr ptr2 ptr3 + * + * Now mseal all of the VMAs. + */ + ASSERT_EQ(sys_mseal(ptr, 3 * page_size, 0), 0); + + /* + * We should end up with: + * + * |-----------------------|-----------| + * | RWES | ROS | + * |-----------------------|-----------| + * ptr ptr3 + */ + ASSERT_TRUE(find_vma_procmap(procmap, ptr)); + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 2 * page_size); +} + TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_prev) { struct procmap_fd *procmap = &self->procmap; diff --git a/tools/testing/selftests/mm/soft-dirty.c b/tools/testing/selftests/mm/soft-dirty.c index 59c0dbe99a9b..bcfcac99b436 100644 --- a/tools/testing/selftests/mm/soft-dirty.c +++ b/tools/testing/selftests/mm/soft-dirty.c @@ -82,7 +82,9 @@ static void test_hugepage(int pagemap_fd, int pagesize) int i, ret; if (!thp_is_enabled()) { - ksft_test_result_skip("Transparent Hugepages not available\n"); + ksft_print_msg("Transparent Hugepages not available\n"); + ksft_test_result_skip("Test %s huge page allocation\n", __func__); + ksft_test_result_skip("Test %s huge page dirty bit\n", __func__); return; } diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index e0167111bdd1..500d07c4938b 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -21,6 +21,7 @@ #include #include "vm_util.h" #include "kselftest.h" +#include "thp_settings.h" uint64_t pagesize; unsigned int pageshift; @@ -255,21 +256,6 @@ static int check_after_split_folio_orders(char *vaddr_start, size_t len, return status; } -static void write_file(const char *path, const char *buf, size_t buflen) -{ - int fd; - ssize_t numwritten; - - fd = open(path, O_WRONLY); - if (fd == -1) - ksft_exit_fail_msg("%s open failed: %s\n", path, strerror(errno)); - - numwritten = write(fd, buf, buflen - 1); - close(fd); - if (numwritten < 1) - ksft_exit_fail_msg("Write failed\n"); -} - static void write_debugfs(const char *fmt, ...) { char input[INPUT_MAX]; @@ -772,6 +758,9 @@ int main(int argc, char **argv) ksft_finished(); } + if (!thp_is_enabled()) + ksft_exit_skip("Transparent Hugepages not available\n"); + if (argc > 1) optional_xfs_path = argv[1]; diff --git a/tools/testing/selftests/mm/thp_settings.c b/tools/testing/selftests/mm/thp_settings.c index 574bd0f8ae48..e748ebfb3d4e 100644 --- a/tools/testing/selftests/mm/thp_settings.c +++ b/tools/testing/selftests/mm/thp_settings.c @@ -6,6 +6,7 @@ #include #include +#include "vm_util.h" #include "thp_settings.h" #define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/" @@ -64,29 +65,6 @@ int read_file(const char *path, char *buf, size_t buflen) return (unsigned int) numread; } -int write_file(const char *path, const char *buf, size_t buflen) -{ - int fd; - ssize_t numwritten; - - fd = open(path, O_WRONLY); - if (fd == -1) { - printf("open(%s)\n", path); - exit(EXIT_FAILURE); - return 0; - } - - numwritten = write(fd, buf, buflen - 1); - close(fd); - if (numwritten < 1) { - printf("write(%s)\n", buf); - exit(EXIT_FAILURE); - return 0; - } - - return (unsigned int) numwritten; -} - unsigned long read_num(const char *path) { char buf[21]; @@ -104,10 +82,7 @@ void write_num(const char *path, unsigned long num) char buf[21]; sprintf(buf, "%ld", num); - if (!write_file(path, buf, strlen(buf) + 1)) { - perror(path); - exit(EXIT_FAILURE); - } + write_file(path, buf, strlen(buf) + 1); } int thp_read_string(const char *name, const char * const strings[]) @@ -165,11 +140,7 @@ void thp_write_string(const char *name, const char *val) printf("%s: Pathname is too long\n", __func__); exit(EXIT_FAILURE); } - - if (!write_file(path, val, strlen(val) + 1)) { - perror(path); - exit(EXIT_FAILURE); - } + write_file(path, val, strlen(val) + 1); } unsigned long thp_read_num(const char *name) diff --git a/tools/testing/selftests/mm/thp_settings.h b/tools/testing/selftests/mm/thp_settings.h index 76eeb712e5f1..7748a9009191 100644 --- a/tools/testing/selftests/mm/thp_settings.h +++ b/tools/testing/selftests/mm/thp_settings.h @@ -63,7 +63,6 @@ struct thp_settings { }; int read_file(const char *path, char *buf, size_t buflen); -int write_file(const char *path, const char *buf, size_t buflen); unsigned long read_num(const char *path); void write_num(const char *path, unsigned long num); diff --git a/tools/testing/selftests/mm/transhuge-stress.c b/tools/testing/selftests/mm/transhuge-stress.c index bcad47c09518..7a9f1035099b 100644 --- a/tools/testing/selftests/mm/transhuge-stress.c +++ b/tools/testing/selftests/mm/transhuge-stress.c @@ -17,6 +17,7 @@ #include #include "vm_util.h" #include "kselftest.h" +#include "thp_settings.h" int backing_fd = -1; int mmap_flags = MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE; @@ -37,6 +38,9 @@ int main(int argc, char **argv) ksft_print_header(); + if (!thp_is_enabled()) + ksft_exit_skip("Transparent Hugepages not available\n"); + ram = sysconf(_SC_PHYS_PAGES); if (ram > SIZE_MAX / psize() / 4) ram = SIZE_MAX / 4; diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c index a6d4ff7dfdc0..db94564f4431 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -764,3 +764,27 @@ int unpoison_memory(unsigned long pfn) return ret > 0 ? 0 : -errno; } + +void write_file(const char *path, const char *buf, size_t buflen) +{ + int fd, saved_errno; + ssize_t numwritten; + + if (buflen < 2) + ksft_exit_fail_msg("Incorrect buffer len: %zu\n", buflen); + + fd = open(path, O_WRONLY); + if (fd == -1) + ksft_exit_fail_msg("%s open failed: %s\n", path, strerror(errno)); + + numwritten = write(fd, buf, buflen - 1); + saved_errno = errno; + close(fd); + errno = saved_errno; + if (numwritten < 0) + ksft_exit_fail_msg("%s write(%.*s) failed: %s\n", path, (int)(buflen - 1), + buf, strerror(errno)); + if (numwritten != buflen - 1) + ksft_exit_fail_msg("%s write(%.*s) is truncated, expected %zu bytes, got %zd bytes\n", + path, (int)(buflen - 1), buf, buflen - 1, numwritten); +} diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h index e9c4e24769c1..1a07305ceff4 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -166,3 +166,5 @@ int unpoison_memory(unsigned long pfn); #define PAGEMAP_PRESENT(ent) (((ent) & (1ull << 63)) != 0) #define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1)) + +void write_file(const char *path, const char *buf, size_t buflen); diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index a275ed584026..f3da38c54d27 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -96,6 +96,7 @@ TEST_PROGS := \ srv6_hl2encap_red_l2vpn_test.sh \ srv6_iptunnel_cache.sh \ stress_reuseport_listen.sh \ + tcp_ecmp_failover.sh \ tcp_fastopen_backup_key.sh \ test_bpf.sh \ test_bridge_backup_port.sh \ diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config index 2a390cae41bf..94d722770420 100644 --- a/tools/testing/selftests/net/config +++ b/tools/testing/selftests/net/config @@ -101,6 +101,7 @@ CONFIG_NET_SCH_HTB=m CONFIG_NET_SCH_INGRESS=m CONFIG_NET_SCH_NETEM=y CONFIG_NET_SCH_PRIO=m +CONFIG_NET_TEAM=y CONFIG_NET_VRF=y CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_OVS=y diff --git a/tools/testing/selftests/net/fib_nexthops.sh b/tools/testing/selftests/net/fib_nexthops.sh index 6eb7f95e70e1..ac868a731694 100755 --- a/tools/testing/selftests/net/fib_nexthops.sh +++ b/tools/testing/selftests/net/fib_nexthops.sh @@ -1209,6 +1209,28 @@ ipv6_fcnal_runtime() run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 124" log_test $? 0 "IPv6 route using a group after replacing v4 gateways" + # Replacing an IPv6 nexthop with an IPv4 nexthop should update has_v4 + # for all groups using it, preventing IPv6 routes from referencing the + # group after the replace. + run_cmd "$IP nexthop add id 89 via 2001:db8:91::2 dev veth1" + run_cmd "$IP nexthop add id 125 group 89" + run_cmd "$IP nexthop replace id 89 via 172.16.1.1 dev veth1" + run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 125" + log_test $? 2 "IPv6 route can not use group after v6 nexthop replaced by v4" + + # Same scenario but with a blackhole nexthop: the group has no IPv6 + # routes yet when the replace happens, so fib6_check_nh_list returns + # early without checking. has_v4 must still be updated to block + # subsequent IPv6 route additions. + run_cmd "$IP nexthop flush >/dev/null 2>&1" + run_cmd "$IP -6 nexthop add id 90 blackhole" + run_cmd "$IP nexthop add id 125 group 90" + run_cmd "$IP nexthop replace id 90 blackhole" + run_cmd "$IP -6 ro add 2001:db8:101::1/128 nhid 125" + log_test $? 2 "IPv6 route reject v6 blackhole replaced by v4 blackhole" + run_cmd "ip netns exec $me ping -6 2001:db8:101::1 -c1 -w$PING_TIMEOUT" + log_test $? 2 "Ping unreachable after rejected route" + $IP nexthop flush >/dev/null 2>&1 # diff --git a/tools/testing/selftests/net/mptcp/diag.sh b/tools/testing/selftests/net/mptcp/diag.sh index d847ff1737c3..27cbda68144e 100755 --- a/tools/testing/selftests/net/mptcp/diag.sh +++ b/tools/testing/selftests/net/mptcp/diag.sh @@ -322,6 +322,33 @@ wait_connected() done } +chk_sndbuf() +{ + local server_sndbuf client_sndbuf msg + local port=${1} + + msg="....chk sndbuf server/client" + server_sndbuf=$(ss -N "${ns}" -inmHM "sport" "${port}" | \ + sed -n 's/.*tb\([0-9]\+\).*/\1/p') + client_sndbuf=$(ss -N "${ns}" -inmHM "dport" "${port}" | \ + sed -n 's/.*tb\([0-9]\+\).*/\1/p') + + mptcp_lib_print_title "${msg}" + if [ -z "${server_sndbuf}" ] || [ -z "${client_sndbuf}" ]; then + mptcp_lib_pr_fail "sndbuf S=${server_sndbuf} C=${client_sndbuf}" + mptcp_lib_result_fail "${msg}" + ret=${KSFT_FAIL} + elif [ "${server_sndbuf}" != "${client_sndbuf}" ]; then + mptcp_lib_pr_fail "sndbuf S=${server_sndbuf} != C=${client_sndbuf}" + mptcp_lib_result_fail "${msg}" + ret=${KSFT_FAIL} + else + mptcp_lib_pr_ok + mptcp_lib_result_pass "${msg}" + fi +} + + trap cleanup EXIT mptcp_lib_ns_init ns @@ -341,6 +368,7 @@ echo "b" | \ 127.0.0.1 >/dev/null & wait_connected $ns 10000 chk_msk_nr 2 "after MPC handshake" +chk_sndbuf 10000 chk_last_time_info 10000 chk_msk_remote_key_nr 2 "....chk remote_key" chk_msk_fallback_nr 0 "....chk no fallback" diff --git a/tools/testing/selftests/net/mptcp/mptcp_lib.sh b/tools/testing/selftests/net/mptcp/mptcp_lib.sh index 5fea7e7df628..989a5975dcea 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_lib.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_lib.sh @@ -474,20 +474,24 @@ mptcp_lib_wait_local_port_listen() { wait_local_port_listen "${@}" "tcp" } +# $1: error file, $2: cmd, $3: expected msg, [$4: expected error] mptcp_lib_check_output() { local err="${1}" local cmd="${2}" local expected="${3}" + local exp_error="${4:-0}" local cmd_ret=0 local out - if ! out=$(${cmd} 2>"${err}"); then - cmd_ret=${?} - fi + out=$(${cmd} 2>"${err}") || cmd_ret=1 - if [ ${cmd_ret} -ne 0 ]; then - mptcp_lib_pr_fail "command execution '${cmd}' stderr" - cat "${err}" + if [ "${cmd_ret}" != "${exp_error}" ]; then + mptcp_lib_pr_fail "unexpected returned code for '${cmd}', info:" + if [ "${exp_error}" = 0 ]; then + cat "${err}" + else + echo "${out}" + fi return 2 elif [ "${out}" = "${expected}" ]; then return 0 diff --git a/tools/testing/selftests/net/mptcp/pm_netlink.sh b/tools/testing/selftests/net/mptcp/pm_netlink.sh index 123d9d7a0278..04594dfc22b1 100755 --- a/tools/testing/selftests/net/mptcp/pm_netlink.sh +++ b/tools/testing/selftests/net/mptcp/pm_netlink.sh @@ -122,10 +122,12 @@ check() local cmd="$1" local expected="$2" local msg="$3" + local exp_error="$4" local rc=0 mptcp_lib_print_title "$msg" - mptcp_lib_check_output "${err}" "${cmd}" "${expected}" || rc=${?} + mptcp_lib_check_output "${err}" "${cmd}" "${expected}" "${exp_error}" || + rc=${?} if [ ${rc} -eq 2 ]; then mptcp_lib_result_fail "${msg} # error ${rc}" ret=${KSFT_FAIL} @@ -158,13 +160,13 @@ check "show_endpoints" \ "3,10.0.1.3,signal backup")" "dump addrs" del_endpoint 2 -check "get_endpoint 2" "" "simple del addr" +check "get_endpoint 2" "" "simple del addr" 1 check "show_endpoints" \ "$(format_endpoints "1,10.0.1.1" \ "3,10.0.1.3,signal backup")" "dump addrs after del" add_endpoint 10.0.1.3 2>/dev/null -check "get_endpoint 4" "" "duplicate addr" +check "get_endpoint 4" "" "duplicate addr" 1 add_endpoint 10.0.1.4 flags signal check "get_endpoint 4" "$(format_endpoints "4,10.0.1.4,signal")" "id addr increment" @@ -173,7 +175,7 @@ for i in $(seq 5 9); do add_endpoint "10.0.1.${i}" flags signal >/dev/null 2>&1 done check "get_endpoint 9" "$(format_endpoints "9,10.0.1.9,signal")" "hard addr limit" -check "get_endpoint 10" "" "above hard addr limit" +check "get_endpoint 10" "" "above hard addr limit" 1 del_endpoint 9 for i in $(seq 10 255); do @@ -192,9 +194,13 @@ check "show_endpoints" \ flush_endpoint check "show_endpoints" "" "flush addrs" -add_endpoint 10.0.1.1 flags unknown -check "show_endpoints" "$(format_endpoints "1,10.0.1.1")" "ignore unknown flags" -flush_endpoint +# "unknown" flag is only supported by pm_nl_ctl +if ! mptcp_lib_is_ip_mptcp; then + add_endpoint 10.0.1.1 flags unknown + check "show_endpoints" "$(format_endpoints "1,10.0.1.1")" \ + "ignore unknown flags" + flush_endpoint +fi set_limits 9 1 2>/dev/null check "get_limits" "${default_limits}" "rcv addrs above hard limit" diff --git a/tools/testing/selftests/net/openvswitch/openvswitch.sh b/tools/testing/selftests/net/openvswitch/openvswitch.sh index b327d3061ed5..3cdd953f6813 100755 --- a/tools/testing/selftests/net/openvswitch/openvswitch.sh +++ b/tools/testing/selftests/net/openvswitch/openvswitch.sh @@ -26,6 +26,7 @@ tests=" netlink_checks ovsnl: validate netlink attrs and settings upcall_interfaces ovs: test the upcall interfaces tunnel_metadata ovs: test extraction of tunnel metadata + tunnel_refcount ovs: test tunnel vport reference cleanup drop_reason drop: test drop reasons are emitted psample psample: Sampling packets with psample" @@ -830,6 +831,42 @@ test_tunnel_metadata() { return 0 } +test_tunnel_refcount() { + sbxname="test_tunnel_refcount" + sbx_add "${sbxname}" || return 1 + + ovs_sbx "${sbxname}" ip netns add trefns || return 1 + on_exit "ovs_sbx ${sbxname} ip netns del trefns" + + for tun_type in gre vxlan geneve; do + info "testing ${tun_type} tunnel vport refcount" + + ovs_sbx "${sbxname}" ip netns exec trefns \ + python3 $ovs_base/ovs-dpctl.py \ + add-dp dp-${tun_type} || return 1 + + ovs_sbx "${sbxname}" ip netns exec trefns \ + python3 $ovs_base/ovs-dpctl.py \ + add-if --no-lwt -t ${tun_type} \ + dp-${tun_type} ovs-${tun_type}0 || return 1 + + ovs_wait ip -netns trefns link show \ + ovs-${tun_type}0 >/dev/null 2>&1 || return 1 + + info "deleting dp - may hang if reference counting is broken" + ovs_sbx "${sbxname}" ip netns exec trefns \ + python3 $ovs_base/ovs-dpctl.py \ + del-dp dp-${tun_type} & + + dev_removed() { + ! ip -netns trefns link show "$1" >/dev/null 2>&1 + } + ovs_wait dev_removed dp-${tun_type} || return 1 + ovs_wait dev_removed ovs-${tun_type}0 || return 1 + done + return 0 +} + run_test() { ( tname="$1" diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py index 848f61fdcee0..bbe35e2718d2 100644 --- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py +++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py @@ -11,7 +11,6 @@ import logging import math import multiprocessing import re -import socket import struct import sys import time @@ -2069,7 +2068,7 @@ class OvsVport(GenericNetlinkSocket): elif vport_type == "internal": return OvsVport.OVS_VPORT_TYPE_INTERNAL elif vport_type == "gre": - return OvsVport.OVS_VPORT_TYPE_INTERNAL + return OvsVport.OVS_VPORT_TYPE_GRE elif vport_type == "vxlan": return OvsVport.OVS_VPORT_TYPE_VXLAN elif vport_type == "geneve": @@ -2121,6 +2120,7 @@ class OvsVport(GenericNetlinkSocket): ) TUNNEL_DEFAULTS = [("geneve", 6081), + ("gre", 0), ("vxlan", 4789)] for tnl in TUNNEL_DEFAULTS: @@ -2129,9 +2129,13 @@ class OvsVport(GenericNetlinkSocket): dport = tnl[1] if not lwt: + if tnl[0] == "gre": + # GRE tunnels have no options. + break + vportopt = OvsVport.ovs_vport_msg.vportopts() vportopt["attrs"].append( - ["OVS_TUNNEL_ATTR_DST_PORT", socket.htons(dport)] + ["OVS_TUNNEL_ATTR_DST_PORT", dport] ) msg["attrs"].append( ["OVS_VPORT_ATTR_OPTIONS", vportopt] @@ -2145,6 +2149,9 @@ class OvsVport(GenericNetlinkSocket): geneve_port=dport, geneve_collect_metadata=True, geneve_udp_zero_csum6_rx=1) + elif tnl[0] == "gre": + ipr.link("add", ifname=vport_ifname, kind="gretap", + gre_collect_metadata=True) elif tnl[0] == "vxlan": ipr.link("add", ifname=vport_ifname, kind=tnl[0], vxlan_learning=0, vxlan_collect_metadata=1, @@ -2563,7 +2570,7 @@ def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()): if vpo: dpo = vpo.get_attr("OVS_TUNNEL_ATTR_DST_PORT") if dpo: - opts += " tnl-dport:%s" % socket.ntohs(dpo) + opts += " tnl-dport:%s" % dpo print( " port %d: %s (%s%s)" % ( @@ -2632,7 +2639,7 @@ def main(argv): "--ptype", type=str, default="netdev", - choices=["netdev", "internal", "geneve", "vxlan"], + choices=["netdev", "internal", "gre", "geneve", "vxlan"], help="Interface type (default netdev)", ) addifcmd.add_argument( @@ -2645,7 +2652,7 @@ def main(argv): addifcmd.add_argument( "-l", "--lwt", - type=bool, + action=argparse.BooleanOptionalAction, default=True, help="Use LWT infrastructure instead of vport (default true)." ) diff --git a/tools/testing/selftests/net/ovpn/common.sh b/tools/testing/selftests/net/ovpn/common.sh index 4c08f756e63a..2d844eb3aa6e 100644 --- a/tools/testing/selftests/net/ovpn/common.sh +++ b/tools/testing/selftests/net/ovpn/common.sh @@ -4,62 +4,181 @@ # # Author: Antonio Quartulli -UDP_PEERS_FILE=${UDP_PEERS_FILE:-udp_peers.txt} -TCP_PEERS_FILE=${TCP_PEERS_FILE:-tcp_peers.txt} -OVPN_CLI=${OVPN_CLI:-./ovpn-cli} -YNL_CLI=${YNL_CLI:-../../../../net/ynl/pyynl/cli.py} -ALG=${ALG:-aes} -PROTO=${PROTO:-UDP} -FLOAT=${FLOAT:-0} -SYMMETRIC_ID=${SYMMETRIC_ID:-0} +OVPN_COMMON_DIR=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")") +source "$OVPN_COMMON_DIR/../../kselftest/ktap_helpers.sh" -export ID_OFFSET=$(( 9 * (SYMMETRIC_ID == 0) )) +OVPN_UDP_PEERS_FILE=${OVPN_UDP_PEERS_FILE:-udp_peers.txt} +OVPN_TCP_PEERS_FILE=${OVPN_TCP_PEERS_FILE:-tcp_peers.txt} +OVPN_CLI=${OVPN_CLI:-${OVPN_COMMON_DIR}/ovpn-cli} +OVPN_YNL=${OVPN_YNL:-${OVPN_COMMON_DIR}/../../../../net/ynl/pyynl/cli.py} +OVPN_ALG=${OVPN_ALG:-aes} +OVPN_PROTO=${OVPN_PROTO:-UDP} +OVPN_FLOAT=${OVPN_FLOAT:-0} +OVPN_SYMMETRIC_ID=${OVPN_SYMMETRIC_ID:-0} +OVPN_VERBOSE=${OVPN_VERBOSE:-0} -JQ_FILTER='map(select(.msg.peer | has("remote-ipv6") | not)) | +export OVPN_ID_OFFSET=$(( 9 * (OVPN_SYMMETRIC_ID == 0) )) + +OVPN_JQ_FILTER='map(if type == "array" then .[] else . end) | + map(select(.msg.peer | has("remote-ipv6") | not)) | map(del(.msg.ifindex)) | sort_by(.msg.peer.id)[]' -LAN_IP="11.11.11.11" +OVPN_LAN_IP="11.11.11.11" -declare -A tmp_jsons=() -declare -A listener_pids=() +declare -A OVPN_TMP_JSONS=() +declare -A OVPN_LISTENER_PIDS=() +OVPN_CURRENT_STAGE="" -create_ns() { - ip netns add peer${1} +ovpn_is_verbose() { + [[ "${OVPN_VERBOSE}" == "1" ]] } -setup_ns() { +ovpn_log() { + ovpn_is_verbose || return 0 + printf '%s\n' "$*" +} + +ovpn_print_cmd_output() { + local output_file="$1" + local line + + [[ -s "${output_file}" ]] || return 0 + + while IFS= read -r line; do + ovpn_log "${line}" + done < "${output_file}" +} + +ovpn_cmd_run() { + local mode="$1" + local label="$2" + local output_file + local rc + local ret=0 + + shift 2 + + output_file=$(mktemp) + if "$@" >"${output_file}" 2>&1; then + rc=0 + else + rc=$? + fi + + case "${mode}" in + ok) + if [[ "${rc}" -ne 0 ]]; then + cat "${output_file}" + printf '%s\n' \ + "${label}: command failed with rc=${rc}: $*" + ret="${rc}" + fi + ;; + mayfail) + ;; + fail) + [[ "${rc}" -eq 0 ]] && ret=1 + ;; + esac + + if ovpn_is_verbose && [[ "${rc}" -eq 0 || "${mode}" != "ok" ]]; then + ovpn_print_cmd_output "${output_file}" + fi + + rm -f "${output_file}" + return "${ret}" +} + +ovpn_cmd_ok() { + ovpn_cmd_run ok "$@" +} + +ovpn_cmd_mayfail() { + ovpn_cmd_run mayfail "$@" +} + +ovpn_cmd_fail() { + ovpn_cmd_run fail "$@" +} + +ovpn_run_bg() { + local pid_var="$1" + + shift + if ovpn_is_verbose; then + "$@" & + else + "$@" >/dev/null 2>&1 & + fi + + printf -v "${pid_var}" '%s' "$!" +} + +ovpn_run_stage() { + local label="$1" + + shift + OVPN_CURRENT_STAGE="${label}" + "$@" + OVPN_CURRENT_STAGE="" + ktap_test_pass "${label}" +} + +ovpn_stage_err() { + # ERR trap is global under set -eE: only report failures that happen + # while ovpn_run_stage() is actively executing a stage body. + if [[ -n "${OVPN_CURRENT_STAGE}" ]]; then + ktap_test_fail "${OVPN_CURRENT_STAGE}" + OVPN_CURRENT_STAGE="" + fi +} + +ovpn_create_ns() { + ip netns add "ovpn_peer${1}" +} + +ovpn_setup_ns() { + local peer="ovpn_peer${1}" + local server_ns="ovpn_peer0" + local peer_ns MODE="P2P" if [ ${1} -eq 0 ]; then MODE="MP" - for p in $(seq 1 ${NUM_PEERS}); do - ip link add veth${p} netns peer0 type veth peer name veth${p} netns peer${p} + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ip link add veth${p} netns "${server_ns}" type veth \ + peer name veth${p} netns "${peer_ns}" - ip -n peer0 addr add 10.10.${p}.1/24 dev veth${p} - ip -n peer0 addr add fd00:0:0:${p}::1/64 dev veth${p} - ip -n peer0 link set veth${p} up + ip -n "${server_ns}" addr add 10.10.${p}.1/24 dev \ + veth${p} + ip -n "${server_ns}" addr add fd00:0:0:${p}::1/64 dev \ + veth${p} + ip -n "${server_ns}" link set veth${p} up - ip -n peer${p} addr add 10.10.${p}.2/24 dev veth${p} - ip -n peer${p} addr add fd00:0:0:${p}::2/64 dev veth${p} - ip -n peer${p} link set veth${p} up + ip -n "${peer_ns}" addr add 10.10.${p}.2/24 dev veth${p} + ip -n "${peer_ns}" addr add fd00:0:0:${p}::2/64 dev \ + veth${p} + ip -n "${peer_ns}" link set veth${p} up done fi - ip netns exec peer${1} ${OVPN_CLI} new_iface tun${1} $MODE - ip -n peer${1} addr add ${2} dev tun${1} + ip netns exec "${peer}" ${OVPN_CLI} new_iface tun${1} $MODE + ip -n "${peer}" addr add ${2} dev tun${1} # add a secondary IP to peer 1, to test a LAN behind a client - if [ ${1} -eq 1 -a -n "${LAN_IP}" ]; then - ip -n peer${1} addr add ${LAN_IP} dev tun${1} - ip -n peer0 route add ${LAN_IP} via $(echo ${2} |sed -e s'!/.*!!') dev tun0 + if [ ${1} -eq 1 -a -n "${OVPN_LAN_IP}" ]; then + ip -n "${peer}" addr add ${OVPN_LAN_IP} dev tun${1} + ip -n "${server_ns}" route add ${OVPN_LAN_IP} via \ + $(echo ${2} |sed -e s'!/.*!!') dev tun0 fi if [ -n "${3}" ]; then - ip -n peer${1} link set mtu ${3} dev tun${1} + ip -n "${peer}" link set mtu ${3} dev tun${1} fi - ip -n peer${1} link set tun${1} up + ip -n "${peer}" link set tun${1} up } -build_capture_filter() { +ovpn_build_capture_filter() { # match the first four bytes of the openvpn data payload - if [ "${PROTO}" == "UDP" ]; then + if [ "${OVPN_PROTO}" == "UDP" ]; then # For UDP, libpcap transport indexing only works for IPv4, so # use an explicit IPv4 or IPv6 expression based on the peer # address. The IPv6 branch assumes there are no extension @@ -76,108 +195,170 @@ build_capture_filter() { fi } -setup_listener() { +ovpn_setup_listener() { + local peer="$1" + local file + local peer_ns="ovpn_peer${peer}" + file=$(mktemp) - PYTHONUNBUFFERED=1 ip netns exec peer${p} ${YNL_CLI} --family ovpn \ - --subscribe peers --output-json --duration 40 > ${file} & - listener_pids[$1]=$! - tmp_jsons[$1]="${file}" + PYTHONUNBUFFERED=1 ip netns exec "${peer_ns}" "${OVPN_YNL}" --family \ + ovpn --subscribe peers --output-json > "${file}" \ + 2>/dev/null & + OVPN_LISTENER_PIDS["${peer}"]=$! + OVPN_TMP_JSONS["${peer}"]="${file}" } -add_peer() { +ovpn_add_peer() { labels=("ASYMM" "SYMM") - M_ID=${labels[SYMMETRIC_ID]} + local peer_ns + local server_ns="ovpn_peer0" + M_ID=${labels[OVPN_SYMMETRIC_ID]} - if [ "${PROTO}" == "UDP" ]; then + if [ "${OVPN_PROTO}" == "UDP" ]; then if [ ${1} -eq 0 ]; then - ip netns exec peer0 ${OVPN_CLI} new_multi_peer tun0 1 \ - ${M_ID} ${UDP_PEERS_FILE} + ip netns exec "${server_ns}" ${OVPN_CLI} \ + new_multi_peer tun0 1 ${M_ID} \ + ${OVPN_UDP_PEERS_FILE} - for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer0 ${OVPN_CLI} new_key tun0 ${p} 1 0 ${ALG} 0 \ + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + ip netns exec "${server_ns}" ${OVPN_CLI} \ + new_key tun0 ${p} 1 0 ${OVPN_ALG} 0 \ data64.key done else - if [ "${SYMMETRIC_ID}" -eq 1 ]; then + peer_ns="ovpn_peer${1}" + if [ "${OVPN_SYMMETRIC_ID}" -eq 1 ]; then PEER_ID=${1} TX_ID="none" else PEER_ID=$(awk "NR == ${1} {print \$2}" \ - ${UDP_PEERS_FILE}) + ${OVPN_UDP_PEERS_FILE}) TX_ID=${1} fi - RADDR=$(awk "NR == ${1} {print \$3}" ${UDP_PEERS_FILE}) - RPORT=$(awk "NR == ${1} {print \$4}" ${UDP_PEERS_FILE}) - LPORT=$(awk "NR == ${1} {print \$6}" ${UDP_PEERS_FILE}) - ip netns exec peer${1} ${OVPN_CLI} new_peer tun${1} \ - ${PEER_ID} ${TX_ID} ${LPORT} ${RADDR} ${RPORT} - ip netns exec peer${1} ${OVPN_CLI} new_key tun${1} \ - ${PEER_ID} 1 0 ${ALG} 1 data64.key + RADDR=$(awk "NR == ${1} {print \$3}" \ + ${OVPN_UDP_PEERS_FILE}) + RPORT=$(awk "NR == ${1} {print \$4}" \ + ${OVPN_UDP_PEERS_FILE}) + LPORT=$(awk "NR == ${1} {print \$6}" \ + ${OVPN_UDP_PEERS_FILE}) + ip netns exec "${peer_ns}" ${OVPN_CLI} new_peer \ + tun${1} ${PEER_ID} ${TX_ID} ${LPORT} ${RADDR} \ + ${RPORT} + ip netns exec "${peer_ns}" ${OVPN_CLI} new_key tun${1} \ + ${PEER_ID} 1 0 ${OVPN_ALG} 1 data64.key fi else if [ ${1} -eq 0 ]; then - (ip netns exec peer0 ${OVPN_CLI} listen tun0 1 ${M_ID} \ - ${TCP_PEERS_FILE} && { - for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer0 ${OVPN_CLI} new_key tun0 ${p} 1 0 \ - ${ALG} 0 data64.key + (ip netns exec "${server_ns}" ${OVPN_CLI} listen tun0 \ + 1 ${M_ID} ${OVPN_TCP_PEERS_FILE} && { + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + ip netns exec "${server_ns}" \ + ${OVPN_CLI} new_key tun0 ${p} \ + 1 0 ${OVPN_ALG} 0 data64.key done }) & sleep 5 else - if [ "${SYMMETRIC_ID}" -eq 1 ]; then + peer_ns="ovpn_peer${1}" + if [ "${OVPN_SYMMETRIC_ID}" -eq 1 ]; then PEER_ID=${1} TX_ID="none" else PEER_ID=$(awk "NR == ${1} {print \$2}" \ - ${TCP_PEERS_FILE}) + ${OVPN_TCP_PEERS_FILE}) TX_ID=${1} fi - ip netns exec peer${1} ${OVPN_CLI} connect tun${1} \ + ip netns exec "${peer_ns}" ${OVPN_CLI} connect tun${1} \ ${PEER_ID} ${TX_ID} 10.10.${1}.1 1 data64.key fi fi } -compare_ntfs() { - if [ ${#tmp_jsons[@]} -gt 0 ]; then - suffix="" - [ "${SYMMETRIC_ID}" -eq 1 ] && suffix="${suffix}-symm" - [ "$FLOAT" == 1 ] && suffix="${suffix}-float" - expected="json/peer${1}${suffix}.json" - received="${tmp_jsons[$1]}" +ovpn_compare_ntfs() { + local diff_rc=0 + local diff_file - kill -TERM ${listener_pids[$1]} || true - wait ${listener_pids[$1]} || true + if [ ${#OVPN_TMP_JSONS[@]} -gt 0 ]; then + suffix="" + [ "${OVPN_SYMMETRIC_ID}" -eq 1 ] && suffix="${suffix}-symm" + [ "$OVPN_FLOAT" == 1 ] && suffix="${suffix}-float" + expected="json/peer${1}${suffix}.json" + received="${OVPN_TMP_JSONS[$1]}" + diff_file=$(mktemp) + + ovpn_stop_listener "${1}" 1 printf "Checking notifications for peer ${1}... " - if diff <(jq -s "${JQ_FILTER}" ${expected}) \ - <(jq -s "${JQ_FILTER}" ${received}); then + if diff <(jq -s "${OVPN_JQ_FILTER}" ${expected}) \ + <(jq -s "${OVPN_JQ_FILTER}" ${received}) \ + >"${diff_file}" 2>&1; then echo "OK" + else + diff_rc=$? + echo "failed" + cat "${diff_file}" fi - rm -f ${received} || true + rm -f "${diff_file}" || true + rm -f "${received}" || true + unset "OVPN_TMP_JSONS[$1]" + fi + + return "${diff_rc}" +} + +ovpn_stop_listener() { + local peer="$1" + local keep_json="${2:-0}" + local pid="${OVPN_LISTENER_PIDS[$peer]:-}" + local json="${OVPN_TMP_JSONS[$peer]:-}" + + if [[ -n "${pid}" ]]; then + kill -TERM "${pid}" 2>/dev/null || true + wait "${pid}" 2>/dev/null || true + unset "OVPN_LISTENER_PIDS[$peer]" + fi + + if [[ -n "${json}" && "${keep_json}" -eq 0 ]]; then + rm -f "${json}" || true + unset "OVPN_TMP_JSONS[$peer]" fi } -cleanup() { - # some ovpn-cli processes sleep in background so they need manual poking - killall $(basename ${OVPN_CLI}) 2>/dev/null || true +ovpn_cleanup_peer_ns() { + local peer="$1" + local peer_id="${peer#ovpn_peer}" - # netns peer0 is deleted without erasing ifaces first - for p in $(seq 1 10); do - ip -n peer${p} link set tun${p} down 2>/dev/null || true - ip netns exec peer${p} ${OVPN_CLI} del_iface tun${p} 2>/dev/null || true - done - for p in $(seq 1 10); do - ip -n peer0 link del veth${p} 2>/dev/null || true - done - for p in $(seq 0 10); do - ip netns del peer${p} 2>/dev/null || true - done + ip -n "${peer}" link set tun${peer_id} down 2>/dev/null || true + ip netns exec "${peer}" ${OVPN_CLI} del_iface tun${peer_id} \ + 1>/dev/null 2>&1 || true + ip netns del "${peer}" 2>/dev/null || true } -if [ "${PROTO}" == "UDP" ]; then - NUM_PEERS=${NUM_PEERS:-$(wc -l ${UDP_PEERS_FILE} | awk '{print $1}')} +ovpn_cleanup() { + local peer + + # some ovpn-cli processes sleep in background so they need manual poking + killall "$(basename "${OVPN_CLI}")" 2>/dev/null || true + + for peer in "${!OVPN_LISTENER_PIDS[@]}"; do + ovpn_stop_listener "${peer}" 2>/dev/null + done + + for p in $(seq 1 10); do + ip -n ovpn_peer0 link del veth${p} 2>/dev/null || true + done + + # remove from ovpn's netns pool + while IFS= read -r peer; do + [[ -n "${peer}" ]] || continue + ovpn_cleanup_peer_ns "${peer}" 2>/dev/null + done < <(ip netns list 2>/dev/null | awk '/^ovpn_/ {print $1}') +} + +if [ "${OVPN_PROTO}" == "UDP" ]; then + OVPN_NUM_PEERS=${OVPN_NUM_PEERS:-$(wc -l ${OVPN_UDP_PEERS_FILE} | \ + awk '{print $1}')} else - NUM_PEERS=${NUM_PEERS:-$(wc -l ${TCP_PEERS_FILE} | awk '{print $1}')} + OVPN_NUM_PEERS=${OVPN_NUM_PEERS:-$(wc -l ${OVPN_TCP_PEERS_FILE} | \ + awk '{print $1}')} fi diff --git a/tools/testing/selftests/net/ovpn/config b/tools/testing/selftests/net/ovpn/config index 42699740936d..d6cf033d555e 100644 --- a/tools/testing/selftests/net/ovpn/config +++ b/tools/testing/selftests/net/ovpn/config @@ -5,6 +5,9 @@ CONFIG_CRYPTO_GCM=y CONFIG_DST_CACHE=y CONFIG_INET=y CONFIG_NET=y +CONFIG_NETFILTER=y CONFIG_NET_UDP_TUNNEL=y +CONFIG_NF_TABLES=m +CONFIG_NF_TABLES_INET=y CONFIG_OVPN=m CONFIG_STREAM_PARSER=y diff --git a/tools/testing/selftests/net/ovpn/test-chachapoly.sh b/tools/testing/selftests/net/ovpn/test-chachapoly.sh index 32504079a2b8..cd3d94355d58 100755 --- a/tools/testing/selftests/net/ovpn/test-chachapoly.sh +++ b/tools/testing/selftests/net/ovpn/test-chachapoly.sh @@ -4,6 +4,6 @@ # # Author: Antonio Quartulli -ALG="chachapoly" +OVPN_ALG="chachapoly" source test.sh diff --git a/tools/testing/selftests/net/ovpn/test-close-socket-tcp.sh b/tools/testing/selftests/net/ovpn/test-close-socket-tcp.sh index 093d44772ffd..392d269bada5 100755 --- a/tools/testing/selftests/net/ovpn/test-close-socket-tcp.sh +++ b/tools/testing/selftests/net/ovpn/test-close-socket-tcp.sh @@ -4,6 +4,6 @@ # # Author: Antonio Quartulli -PROTO="TCP" +OVPN_PROTO="TCP" source test-close-socket.sh diff --git a/tools/testing/selftests/net/ovpn/test-close-socket.sh b/tools/testing/selftests/net/ovpn/test-close-socket.sh index 0d09df14fe8e..af1532b4d2da 100755 --- a/tools/testing/selftests/net/ovpn/test-close-socket.sh +++ b/tools/testing/selftests/net/ovpn/test-close-socket.sh @@ -5,41 +5,81 @@ # Author: Antonio Quartulli #set -x -set -e +set -eE source ./common.sh -cleanup +ovpn_test_finished=0 +ovpn_test_exit() { + ovpn_cleanup + modprobe -r ovpn || true + + if [ "${ovpn_test_finished}" -eq 0 ]; then + ktap_print_totals + fi +} + +ovpn_prepare_network() { + local p + local peer_ns + + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "create namespace peer${p}" ovpn_create_ns "${p}" + done + + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "configure peer${p} namespace" ovpn_setup_ns \ + "${p}" 5.5.5.$((p + 1))/24 + done + + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "register peer${p} in overlay" ovpn_add_peer "${p}" + done + + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "set peer0 timeout for peer ${p}" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} set_peer tun0 \ + ${p} 60 120 + ovpn_cmd_ok "set peer${p} timeout for peer ${p}" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \ + tun${p} $((p + OVPN_ID_OFFSET)) 60 120 + done +} + +ovpn_run_ping_traffic() { + local p + + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "send ping traffic to peer ${p}" \ + ip netns exec ovpn_peer0 ping -qfc 500 -w 3 \ + 5.5.5.$((p + 1)) + done +} + +ovpn_run_iperf() { + local iperf_pid + + ovpn_run_bg iperf_pid ip netns exec ovpn_peer0 iperf3 -1 -s + sleep 1 + ovpn_cmd_ok "run iperf throughput flow" \ + ip netns exec ovpn_peer1 iperf3 -Z -t 3 -c 5.5.5.1 + wait "${iperf_pid}" || return 1 +} + +trap ovpn_test_exit EXIT +trap ovpn_stage_err ERR + +ktap_print_header +ktap_set_plan 3 + +ovpn_cleanup modprobe -q ovpn || true -for p in $(seq 0 ${NUM_PEERS}); do - create_ns ${p} -done +ovpn_run_stage "setup network topology" ovpn_prepare_network +ovpn_run_stage "run ping traffic" ovpn_run_ping_traffic +ovpn_run_stage "run iperf throughput" ovpn_run_iperf -for p in $(seq 0 ${NUM_PEERS}); do - setup_ns ${p} 5.5.5.$((${p} + 1))/24 -done - -for p in $(seq 0 ${NUM_PEERS}); do - add_peer ${p} -done - -for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer0 ${OVPN_CLI} set_peer tun0 ${p} 60 120 - ip netns exec peer${p} ${OVPN_CLI} set_peer tun${p} $((${p}+9)) 60 120 -done - -sleep 1 - -for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer0 ping -qfc 500 -w 3 5.5.5.$((${p} + 1)) -done - -ip netns exec peer0 iperf3 -1 -s & -sleep 1 -ip netns exec peer1 iperf3 -Z -t 3 -c 5.5.5.1 - -cleanup - -modprobe -r ovpn || true +ovpn_test_finished=1 +ktap_finished diff --git a/tools/testing/selftests/net/ovpn/test-float.sh b/tools/testing/selftests/net/ovpn/test-float.sh index ba5d725e18b0..91f8e113718e 100755 --- a/tools/testing/selftests/net/ovpn/test-float.sh +++ b/tools/testing/selftests/net/ovpn/test-float.sh @@ -4,6 +4,6 @@ # # Author: Antonio Quartulli -FLOAT="1" +OVPN_FLOAT="1" source test.sh diff --git a/tools/testing/selftests/net/ovpn/test-mark.sh b/tools/testing/selftests/net/ovpn/test-mark.sh index 8534428ed3eb..5a8f47554286 100755 --- a/tools/testing/selftests/net/ovpn/test-mark.sh +++ b/tools/testing/selftests/net/ovpn/test-mark.sh @@ -6,91 +6,166 @@ # Antonio Quartulli #set -x -set -e +set -eE MARK=1056 +MARK_DROP_COUNTER=0 source ./common.sh -cleanup +ovpn_test_finished=0 +ovpn_test_exit() { + ovpn_cleanup + modprobe -r ovpn || true + + if [ "${ovpn_test_finished}" -eq 0 ]; then + ktap_print_totals + fi +} + +ovpn_mark_prepare_network() { + local p + local peer_ns + + for p in $(seq 0 "${OVPN_NUM_PEERS}"); do + ovpn_cmd_ok "create namespace peer${p}" ovpn_create_ns "${p}" + done + + for p in $(seq 0 3); do + ovpn_cmd_ok "configure peer${p} namespace" ovpn_setup_ns \ + "${p}" 5.5.5.$((p + 1))/24 + done + + ovpn_cmd_ok "create server-side multi-peer with fwmark" \ + ip netns exec ovpn_peer0 "${OVPN_CLI}" new_multi_peer tun0 1 \ + ASYMM "${OVPN_UDP_PEERS_FILE}" "${MARK}" + for p in $(seq 1 3); do + ovpn_cmd_ok "install server key for peer ${p}" \ + ip netns exec ovpn_peer0 "${OVPN_CLI}" new_key tun0 \ + "${p}" 1 0 "${OVPN_ALG}" 0 data64.key + done + + for p in $(seq 1 3); do + ovpn_cmd_ok "register peer${p} in overlay" ovpn_add_peer "${p}" + done + + for p in $(seq 1 3); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "set peer0 timeout for peer ${p}" \ + ip netns exec ovpn_peer0 "${OVPN_CLI}" set_peer tun0 \ + "${p}" 60 120 + ovpn_cmd_ok "set peer${p} timeout for peer ${p}" \ + ip netns exec "${peer_ns}" "${OVPN_CLI}" set_peer \ + tun"${p}" $((p + OVPN_ID_OFFSET)) 60 120 + done +} + +ovpn_mark_run_baseline_traffic() { + local p + + for p in $(seq 1 3); do + ovpn_cmd_ok "send baseline traffic to peer ${p}" \ + ip netns exec ovpn_peer0 ping -qfc 500 -w 3 \ + 5.5.5.$((p + 1)) + done +} + +ovpn_mark_add_drop_rule() { + ovpn_log "Adding an nftables drop rule based on mark value ${MARK}" + + ovpn_cmd_ok "flush nft ruleset" ip netns exec ovpn_peer0 nft flush \ + ruleset + ovpn_cmd_ok "create nft filter table" ip netns exec ovpn_peer0 nft \ + "add table inet filter" + ovpn_cmd_ok "create nft filter output chain" \ + ip netns exec ovpn_peer0 nft "add chain inet filter output { \ + type filter hook output priority 0; policy accept; }" + ovpn_cmd_ok "add nft drop rule for mark ${MARK}" \ + ip netns exec ovpn_peer0 nft add rule inet filter output \ + meta mark == "${MARK}" \ + counter drop + + MARK_DROP_COUNTER=$(ip netns exec ovpn_peer0 nft list chain inet \ + filter output | sed -n 's/.*packets \([0-9]*\).*/\1/p') + if [ -z "${MARK_DROP_COUNTER}" ]; then + printf '%s\n' "unable to read nft drop counter" + return 1 + fi +} + +ovpn_mark_verify_drop_traffic() { + local p + local ping_output + local lost_packets + local total_count + + for p in $(seq 1 3); do + if ping_output=$(ip netns exec ovpn_peer0 ping -qfc 500 -w 1 \ + 5.5.5.$((p + 1)) 2>&1); then + printf '%s\n' "expected ping to peer ${p} to fail \ + after nft drop rule" + return 1 + fi + ovpn_log "${ping_output}" + lost_packets=$(echo "${ping_output}" | \ + awk '/packets transmitted/ { print $1 }') + if [ -z "${lost_packets}" ]; then + printf '%s\n' "unable to parse lost packets for peer \ + ${p}" + return 1 + fi + MARK_DROP_COUNTER=$((MARK_DROP_COUNTER + lost_packets)) + done + + total_count=$(ip netns exec ovpn_peer0 nft list chain inet filter \ + output | sed -n 's/.*packets \([0-9]*\).*/\1/p') + if [ -z "${total_count}" ]; then + printf '%s\n' "unable to read final nft drop counter" + return 1 + fi + if [ "${MARK_DROP_COUNTER}" -ne "${total_count}" ]; then + printf '%s\n' "expected ${MARK_DROP_COUNTER} drops, got \ + ${total_count}" + return 1 + fi +} + +ovpn_mark_remove_drop_rule() { + ovpn_log "Removing the drop rule" + + ovpn_cmd_ok "flush nft ruleset" ip netns exec ovpn_peer0 nft flush \ + ruleset +} + +ovpn_mark_verify_traffic_recovery() { + local p + + sleep 1 + for p in $(seq 1 3); do + ovpn_cmd_ok "send recovery traffic to peer ${p}" \ + ip netns exec ovpn_peer0 ping -qfc 500 -w 3 \ + 5.5.5.$((p + 1)) + done +} + +trap ovpn_test_exit EXIT +trap ovpn_stage_err ERR + +ktap_print_header +ktap_set_plan 6 + +ovpn_cleanup modprobe -q ovpn || true -for p in $(seq 0 "${NUM_PEERS}"); do - create_ns "${p}" -done +ovpn_run_stage "setup marked network topology" ovpn_mark_prepare_network +ovpn_run_stage "run baseline traffic" ovpn_mark_run_baseline_traffic +ovpn_run_stage "install nft mark drop rule" ovpn_mark_add_drop_rule +ovpn_run_stage "drop marked traffic and count packets" \ + ovpn_mark_verify_drop_traffic +ovpn_run_stage "remove nft drop rule" ovpn_mark_remove_drop_rule +ovpn_run_stage "traffic recovers after drop removal" \ + ovpn_mark_verify_traffic_recovery -for p in $(seq 0 3); do - setup_ns "${p}" 5.5.5.$((p + 1))/24 -done - -# add peer0 with mark -ip netns exec peer0 "${OVPN_CLI}" new_multi_peer tun0 1 ASYMM \ - "${UDP_PEERS_FILE}" \ - ${MARK} -for p in $(seq 1 3); do - ip netns exec peer0 "${OVPN_CLI}" new_key tun0 "${p}" 1 0 "${ALG}" 0 \ - data64.key -done - -for p in $(seq 1 3); do - add_peer "${p}" -done - -for p in $(seq 1 3); do - ip netns exec peer0 "${OVPN_CLI}" set_peer tun0 "${p}" 60 120 - ip netns exec peer"${p}" "${OVPN_CLI}" set_peer tun"${p}" \ - $((p + 9)) 60 120 -done - -sleep 1 - -for p in $(seq 1 3); do - ip netns exec peer0 ping -qfc 500 -w 3 5.5.5.$((p + 1)) -done - -echo "Adding an nftables drop rule based on mark value ${MARK}" -ip netns exec peer0 nft flush ruleset -ip netns exec peer0 nft 'add table inet filter' -ip netns exec peer0 nft 'add chain inet filter output { - type filter hook output priority 0; - policy accept; -}' -ip netns exec peer0 nft add rule inet filter output \ - meta mark == ${MARK} \ - counter drop - -DROP_COUNTER=$(ip netns exec peer0 nft list chain inet filter output \ - | sed -n 's/.*packets \([0-9]*\).*/\1/p') -sleep 1 - -# ping should fail -for p in $(seq 1 3); do - PING_OUTPUT=$(ip netns exec peer0 ping \ - -qfc 500 -w 1 5.5.5.$((p + 1)) 2>&1) && exit 1 - echo "${PING_OUTPUT}" - LOST_PACKETS=$(echo "$PING_OUTPUT" \ - | awk '/packets transmitted/ { print $1 }') - # increment the drop counter by the amount of lost packets - DROP_COUNTER=$((DROP_COUNTER + LOST_PACKETS)) -done - -# check if the final nft counter matches our counter -TOTAL_COUNT=$(ip netns exec peer0 nft list chain inet filter output \ - | sed -n 's/.*packets \([0-9]*\).*/\1/p') -if [ "${DROP_COUNTER}" -ne "${TOTAL_COUNT}" ]; then - echo "Expected ${TOTAL_COUNT} drops, got ${DROP_COUNTER}" - exit 1 -fi - -echo "Removing the drop rule" -ip netns exec peer0 nft flush ruleset -sleep 1 - -for p in $(seq 1 3); do - ip netns exec peer0 ping -qfc 500 -w 3 5.5.5.$((p + 1)) -done - -cleanup - -modprobe -r ovpn || true +ovpn_test_finished=1 +ktap_finished diff --git a/tools/testing/selftests/net/ovpn/test-symmetric-id-float.sh b/tools/testing/selftests/net/ovpn/test-symmetric-id-float.sh index b3711a81b463..75296fe72c39 100755 --- a/tools/testing/selftests/net/ovpn/test-symmetric-id-float.sh +++ b/tools/testing/selftests/net/ovpn/test-symmetric-id-float.sh @@ -5,7 +5,7 @@ # Author: Ralf Lici # Antonio Quartulli -SYMMETRIC_ID="1" -FLOAT="1" +OVPN_SYMMETRIC_ID="1" +OVPN_FLOAT="1" source test.sh diff --git a/tools/testing/selftests/net/ovpn/test-symmetric-id-tcp.sh b/tools/testing/selftests/net/ovpn/test-symmetric-id-tcp.sh index 188cafb67b2f..680a465c49d2 100755 --- a/tools/testing/selftests/net/ovpn/test-symmetric-id-tcp.sh +++ b/tools/testing/selftests/net/ovpn/test-symmetric-id-tcp.sh @@ -5,7 +5,7 @@ # Author: Ralf Lici # Antonio Quartulli -PROTO="TCP" -SYMMETRIC_ID=1 +OVPN_PROTO="TCP" +OVPN_SYMMETRIC_ID=1 source test.sh diff --git a/tools/testing/selftests/net/ovpn/test-symmetric-id.sh b/tools/testing/selftests/net/ovpn/test-symmetric-id.sh index 35b119c72e4f..a2e2808959d9 100755 --- a/tools/testing/selftests/net/ovpn/test-symmetric-id.sh +++ b/tools/testing/selftests/net/ovpn/test-symmetric-id.sh @@ -5,6 +5,6 @@ # Author: Ralf Lici # Antonio Quartulli -SYMMETRIC_ID="1" +OVPN_SYMMETRIC_ID="1" source test.sh diff --git a/tools/testing/selftests/net/ovpn/test-tcp.sh b/tools/testing/selftests/net/ovpn/test-tcp.sh index ba3f1f315a34..27cc6e7b98bc 100755 --- a/tools/testing/selftests/net/ovpn/test-tcp.sh +++ b/tools/testing/selftests/net/ovpn/test-tcp.sh @@ -4,6 +4,6 @@ # # Author: Antonio Quartulli -PROTO="TCP" +OVPN_PROTO="TCP" source test.sh diff --git a/tools/testing/selftests/net/ovpn/test.sh b/tools/testing/selftests/net/ovpn/test.sh index b60e94a4094e..c06e3135fbef 100755 --- a/tools/testing/selftests/net/ovpn/test.sh +++ b/tools/testing/selftests/net/ovpn/test.sh @@ -5,161 +5,316 @@ # Author: Antonio Quartulli #set -x -set -e +set -eE source ./common.sh -cleanup +ovpn_test_finished=0 -modprobe -q ovpn || true +ovpn_test_exit() { + ovpn_cleanup + modprobe -r ovpn || true -for p in $(seq 0 ${NUM_PEERS}); do - create_ns ${p} -done + if [ "${ovpn_test_finished}" -eq 0 ]; then + ktap_print_totals + fi +} -for p in $(seq 0 ${NUM_PEERS}); do - setup_listener ${p} -done +ovpn_prepare_network() { + local p + local peer_ns -for p in $(seq 0 ${NUM_PEERS}); do - setup_ns ${p} 5.5.5.$((${p} + 1))/24 ${MTU} -done + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "create namespace peer${p}" ovpn_create_ns "${p}" + done -for p in $(seq 0 ${NUM_PEERS}); do - add_peer ${p} -done + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "start notification listener peer${p}" \ + ovpn_setup_listener "${p}" + # starting all YNL listeners back-to-back can intermittently + # stall their startup so serialize launches a bit + sleep 0.5 + done -for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer0 ${OVPN_CLI} set_peer tun0 ${p} 60 120 - ip netns exec peer${p} ${OVPN_CLI} set_peer tun${p} \ - $((${p}+ID_OFFSET)) 60 120 -done + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "configure peer${p} namespace" ovpn_setup_ns \ + "${p}" 5.5.5.$((p + 1))/24 "${MTU}" + done -sleep 1 + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "register peer${p} in overlay" ovpn_add_peer "${p}" + done -TCPDUMP_TIMEOUT="1.5s" -for p in $(seq 1 ${NUM_PEERS}); do - # The first part of the data packet header consists of: - # - TCP only: 2 bytes for the packet length - # - 5 bits for opcode ("9" for DATA_V2) - # - 3 bits for key-id ("0" at this point) - # - 12 bytes for peer-id: - # - with asymmetric ID: "${p}" one way and "${p} + 9" the other way - # - with symmetric ID: "${p}" both ways - HEADER1=$(printf "0x4800000%x" ${p}) - HEADER2=$(printf "0x4800000%x" $((${p} + ID_OFFSET))) - RADDR="" - if [ "${PROTO}" == "UDP" ]; then - RADDR=$(awk "NR == ${p} {print \$3}" ${UDP_PEERS_FILE}) + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "set peer0 timeout for peer ${p}" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} set_peer tun0 \ + ${p} 60 120 + ovpn_cmd_ok "set peer${p} timeout for peer ${p}" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \ + tun${p} $((p + OVPN_ID_OFFSET)) 60 120 + done +} + +ovpn_run_basic_traffic() { + local p + local header1 + local header2 + local peer_ns + local raddr + local tcpdump_pid1 + local tcpdump_pid2 + local tcpdump_timeout="1.5s" + + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + # The first part of the data packet header consists of: + # - TCP only: 2 bytes for the packet length + # - 5 bits for opcode ("9" for DATA_V2) + # - 3 bits for key-id ("0" at this point) + # - 12 bytes for peer-id: + # - with asymmetric ID: "${p}" one way and "${p} + 9" the + # other way + # - with symmetric ID: "${p}" both ways + header1=$(printf "0x4800000%x" ${p}) + header2=$(printf "0x4800000%x" $((p + OVPN_ID_OFFSET))) + raddr="" + if [ "${OVPN_PROTO}" == "UDP" ]; then + raddr=$(awk "NR == ${p} {print \$3}" \ + "${OVPN_UDP_PEERS_FILE}") + fi + peer_ns="ovpn_peer${p}" + + timeout ${tcpdump_timeout} ip netns exec "${peer_ns}" \ + tcpdump --immediate-mode -p -ni veth${p} -c 1 \ + "$(ovpn_build_capture_filter "${header1}" "${raddr}")" \ + >/dev/null 2>&1 & + tcpdump_pid1=$! + timeout ${tcpdump_timeout} ip netns exec "${peer_ns}" \ + tcpdump --immediate-mode -p -ni veth${p} -c 1 \ + "$(ovpn_build_capture_filter "${header2}" "${raddr}")" \ + >/dev/null 2>&1 & + tcpdump_pid2=$! + + sleep 0.3 + ovpn_cmd_ok "send baseline traffic to peer ${p}" \ + ip netns exec ovpn_peer0 \ + ping -qfc 100 -w 3 5.5.5.$((p + 1)) + ovpn_cmd_ok "send large-payload traffic to peer ${p}" \ + ip netns exec ovpn_peer0 \ + ping -qfc 100 -s 3000 -w 3 5.5.5.$((p + 1)) + + wait "${tcpdump_pid1}" || return 1 + wait "${tcpdump_pid2}" || return 1 + done +} + +ovpn_run_lan_traffic() { + ovpn_cmd_ok "ping LAN behind peer1" \ + ip netns exec ovpn_peer0 ping -qfc 500 -w 3 "${OVPN_LAN_IP}" +} + +ovpn_run_float_mode() { + local p + local peer_ns + + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "float: remove old transport address on peer${p}" \ + ip -n "${peer_ns}" addr del 10.10.${p}.2/24 dev veth${p} + ovpn_cmd_ok "float: add new transport address on peer${p}" \ + ip -n "${peer_ns}" addr add 10.10.${p}.3/24 dev veth${p} + done + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "ping tunnel after float peer ${p}" \ + ip netns exec "${peer_ns}" ping -qfc 500 -w 3 5.5.5.1 + done +} + +ovpn_run_iperf() { + local iperf_pid + + ovpn_run_bg iperf_pid ip netns exec ovpn_peer0 iperf3 -1 -s + sleep 1 + + ovpn_cmd_ok "run iperf throughput flow" \ + ip netns exec ovpn_peer1 iperf3 -Z -t 3 -c 5.5.5.1 + wait "${iperf_pid}" || return 1 +} + +ovpn_run_key_rollover() { + local p + local peer_ns + + ovpn_log "Adding secondary key and then swap:" + + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "add secondary key on peer0 for peer ${p}" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} new_key tun0 \ + ${p} 2 1 ${OVPN_ALG} 0 data64.key + ovpn_cmd_ok "add secondary key on peer${p} for peer ${p}" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} new_key tun${p} \ + $((p + OVPN_ID_OFFSET)) 2 1 ${OVPN_ALG} 1 \ + data64.key + ovpn_cmd_ok "swap keys on peer${p}" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} swap_keys \ + tun${p} $((p + OVPN_ID_OFFSET)) + done +} + +ovpn_run_queries() { + ovpn_log "Querying all peers:" + + ovpn_cmd_ok "query all peers from peer0" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} get_peer tun0 + ovpn_cmd_ok "query all peers from peer1" \ + ip netns exec ovpn_peer1 ${OVPN_CLI} get_peer tun1 + + ovpn_log "Querying peer 1:" + + ovpn_cmd_ok "query peer 1 from peer0" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} get_peer tun0 1 +} + +ovpn_query_peer_missing() { + ovpn_log "Querying non-existent peer 20:" + + ovpn_cmd_fail "query missing peer 20 on peer0" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} get_peer tun0 20 +} + +ovpn_run_peer_cleanup() { + local p + local peer_ns + + ovpn_log "Deleting peer 1:" + + ovpn_cmd_ok "delete peer1 on peer0" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} del_peer tun0 1 + ovpn_cmd_ok "delete peer1 on peer1" \ + ip netns exec ovpn_peer1 ${OVPN_CLI} del_peer tun1 \ + $((1 + OVPN_ID_OFFSET)) + + ovpn_log "Querying keys:" + + for p in $(seq 2 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "query peer${p} key 1" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} get_key tun${p} \ + $((p + OVPN_ID_OFFSET)) 1 + ovpn_cmd_ok "query peer${p} key 2" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} get_key tun${p} \ + $((p + OVPN_ID_OFFSET)) 2 + done +} + +ovpn_run_traffic_delete_peer() { + local ping_pid + + ovpn_log "Deleting peer while sending traffic:" + + ovpn_run_bg ping_pid ip netns exec ovpn_peer2 ping -qf -w 4 5.5.5.1 + sleep 2 + ovpn_cmd_ok "delete peer0 peer 2" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} del_peer tun0 2 + + if [ "${OVPN_PROTO}" == "TCP" ]; then + # In TCP mode this command is expected to fail for both peers. + ovpn_cmd_mayfail "delete peer2 peer 2 (TCP non-fatal)" \ + ip netns exec ovpn_peer2 ${OVPN_CLI} del_peer tun2 \ + $((2 + OVPN_ID_OFFSET)) + else + ovpn_cmd_ok "delete peer2 peer 2" ip netns exec ovpn_peer2 \ + ${OVPN_CLI} del_peer tun2 $((2 + OVPN_ID_OFFSET)) fi - timeout ${TCPDUMP_TIMEOUT} ip netns exec peer${p} \ - tcpdump --immediate-mode -p -ni veth${p} -c 1 \ - "$(build_capture_filter "${HEADER1}" "${RADDR}")" \ - >/dev/null 2>&1 & - TCPDUMP_PID1=$! - timeout ${TCPDUMP_TIMEOUT} ip netns exec peer${p} \ - tcpdump --immediate-mode -p -ni veth${p} -c 1 \ - "$(build_capture_filter "${HEADER2}" "${RADDR}")" \ - >/dev/null 2>&1 & - TCPDUMP_PID2=$! + wait "${ping_pid}" || true +} - sleep 0.3 - ip netns exec peer0 ping -qfc 500 -w 3 5.5.5.$((${p} + 1)) - ip netns exec peer0 ping -qfc 500 -s 3000 -w 3 5.5.5.$((${p} + 1)) +ovpn_run_key_cleanup() { + local p + local peer_ns - wait ${TCPDUMP_PID1} - wait ${TCPDUMP_PID2} -done + ovpn_log "Deleting keys:" -# ping LAN behind client 1 -ip netns exec peer0 ping -qfc 500 -w 3 ${LAN_IP} - -if [ "$FLOAT" == "1" ]; then - # make clients float.. - for p in $(seq 1 ${NUM_PEERS}); do - ip -n peer${p} addr del 10.10.${p}.2/24 dev veth${p} - ip -n peer${p} addr add 10.10.${p}.3/24 dev veth${p} + for p in $(seq 3 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "delete key 1 for peer${p}" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} del_key tun${p} \ + $((p + OVPN_ID_OFFSET)) 1 + ovpn_cmd_ok "delete key 2 for peer${p}" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} del_key tun${p} \ + $((p + OVPN_ID_OFFSET)) 2 done - for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer${p} ping -qfc 500 -w 3 5.5.5.1 +} + +ovpn_run_timeouts() { + local p + local peer_ns + + ovpn_log "Setting timeout to 3s MP:" + + for p in $(seq 3 ${OVPN_NUM_PEERS}); do + # Non-fatal: this may fail in some protocol modes. + ovpn_cmd_mayfail "set peer0 timeout for peer ${p} (non-fatal)" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} set_peer tun0 \ + ${p} 3 3 + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "disable timeout on peer${p} while peer0 adjusts \ + state" ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \ + tun${p} $((p + OVPN_ID_OFFSET)) 0 0 done + # wait for peers to timeout + sleep 5 + + ovpn_log "Setting timeout to 3s P2P:" + + for p in $(seq 3 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "set peer${p} P2P timeout" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \ + tun${p} $((p + OVPN_ID_OFFSET)) 3 3 + done + sleep 5 +} + +ovpn_run_notifications() { + local p + + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "validate listener output for peer ${p}" \ + ovpn_compare_ntfs "${p}" + done +} + +trap ovpn_test_exit EXIT +trap ovpn_stage_err ERR + +ktap_print_header +if [ "${OVPN_FLOAT}" == "1" ]; then + ktap_set_plan 13 +else + ktap_set_plan 12 fi -ip netns exec peer0 iperf3 -1 -s & -sleep 1 -ip netns exec peer1 iperf3 -Z -t 3 -c 5.5.5.1 +ovpn_cleanup +modprobe -q ovpn || true -echo "Adding secondary key and then swap:" -for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer0 ${OVPN_CLI} new_key tun0 ${p} 2 1 ${ALG} 0 \ - data64.key - ip netns exec peer${p} ${OVPN_CLI} new_key tun${p} \ - $((${p} + ID_OFFSET)) 2 1 ${ALG} 1 data64.key - ip netns exec peer${p} ${OVPN_CLI} swap_keys tun${p} \ - $((${p} + ID_OFFSET)) -done +ovpn_run_stage "setup network topology" ovpn_prepare_network +ovpn_run_stage "run baseline data traffic" ovpn_run_basic_traffic +ovpn_run_stage "run LAN traffic behind peer1" ovpn_run_lan_traffic +[ "${OVPN_FLOAT}" == "1" ] && ovpn_run_stage "run floating peer checks" \ + ovpn_run_float_mode +ovpn_run_stage "run iperf throughput" ovpn_run_iperf +ovpn_run_stage "run key rollout" ovpn_run_key_rollover +ovpn_run_stage "query peers" ovpn_run_queries +ovpn_run_stage "query missing peer fails" ovpn_query_peer_missing +ovpn_run_stage "peer lifecycle and key queries" ovpn_run_peer_cleanup +ovpn_run_stage "delete peer while traffic" ovpn_run_traffic_delete_peer +ovpn_run_stage "delete stale keys" ovpn_run_key_cleanup +ovpn_run_stage "check timeout behavior" ovpn_run_timeouts +ovpn_run_stage "validate notification output" ovpn_run_notifications -sleep 1 - -echo "Querying all peers:" -ip netns exec peer0 ${OVPN_CLI} get_peer tun0 -ip netns exec peer1 ${OVPN_CLI} get_peer tun1 - -echo "Querying peer 1:" -ip netns exec peer0 ${OVPN_CLI} get_peer tun0 1 - -echo "Querying non-existent peer 20:" -ip netns exec peer0 ${OVPN_CLI} get_peer tun0 20 || true - -echo "Deleting peer 1:" -ip netns exec peer0 ${OVPN_CLI} del_peer tun0 1 -ip netns exec peer1 ${OVPN_CLI} del_peer tun1 $((1 + ID_OFFSET)) - -echo "Querying keys:" -for p in $(seq 2 ${NUM_PEERS}); do - ip netns exec peer${p} ${OVPN_CLI} get_key tun${p} \ - $((${p} + ID_OFFSET)) 1 - ip netns exec peer${p} ${OVPN_CLI} get_key tun${p} \ - $((${p} + ID_OFFSET)) 2 -done - -echo "Deleting peer while sending traffic:" -(ip netns exec peer2 ping -qf -w 4 5.5.5.1)& -sleep 2 -ip netns exec peer0 ${OVPN_CLI} del_peer tun0 2 -# following command fails in TCP mode -# (both ends get conn reset when one peer disconnects) -ip netns exec peer2 ${OVPN_CLI} del_peer tun2 $((2 + ID_OFFSET)) || true - -echo "Deleting keys:" -for p in $(seq 3 ${NUM_PEERS}); do - ip netns exec peer${p} ${OVPN_CLI} del_key tun${p} \ - $((${p} + ID_OFFSET)) 1 - ip netns exec peer${p} ${OVPN_CLI} del_key tun${p} \ - $((${p} + ID_OFFSET)) 2 -done - -echo "Setting timeout to 3s MP:" -for p in $(seq 3 ${NUM_PEERS}); do - ip netns exec peer0 ${OVPN_CLI} set_peer tun0 ${p} 3 3 || true - ip netns exec peer${p} ${OVPN_CLI} set_peer tun${p} \ - $((${p} + ID_OFFSET)) 0 0 -done -# wait for peers to timeout -sleep 5 - -echo "Setting timeout to 3s P2P:" -for p in $(seq 3 ${NUM_PEERS}); do - ip netns exec peer${p} ${OVPN_CLI} set_peer tun${p} \ - $((${p} + ID_OFFSET)) 3 3 -done -sleep 5 - -for p in $(seq 0 ${NUM_PEERS}); do - compare_ntfs ${p} -done - -cleanup - -modprobe -r ovpn || true +ovpn_test_finished=1 +ktap_finished diff --git a/tools/testing/selftests/net/packetdrill/tcp_rfc5961_ack-out-of-window.pkt b/tools/testing/selftests/net/packetdrill/tcp_rfc5961_ack-out-of-window.pkt new file mode 100644 index 000000000000..2776b8728085 --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_rfc5961_ack-out-of-window.pkt @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// RFC 5961 Section 5.2 / RFC 793 Section 3.9: an incoming segment's +// ACK value must lie in [SND.UNA - MAX.SND.WND, SND.NXT]; otherwise +// the receiver MUST discard the segment and send a challenge ACK +// back. Exercise both edges of that window in a single connection. + +`./defaults.sh +sysctl -q net.ipv4.tcp_invalid_ratelimit=0 +` + + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +// Three-way handshake. Peer advertises rwnd = 1000 (no wscale), so +// MAX.SND.WND is tracked as 1000. + +0 < S 0:0(0) win 1000 + +0 > S. 0:0(0) ack 1 <...> ++.1 < . 1:1(0) ack 1 win 1000 + +0 accept(3, ..., ...) = 4 + +// ---- Upper edge: SEG.ACK > SND.NXT -------------------------------- +// Server has sent nothing yet, so SND.UNA = SND.NXT = 1. +// Peer sends a pure ACK with SEG.ACK = 2, beyond SND.NXT. + +0 < . 1:1(0) ack 2 win 1000 +// Expect a challenge ACK: . + +0 > . 1:1(0) ack 1 + +// Advance SND.UNA past MAX.SND.WND so that the lower edge becomes +// reachable. Issue two 1-MSS writes so each skb is exactly one MSS +// and PSH is set by tcp_push() at the end of each sendmsg, keeping +// the setup independent of the TSO / tcp_fragment split path. + +0 write(4, ..., 1000) = 1000 + +0 > P. 1:1001(1000) ack 1 ++.01 < . 1:1(0) ack 1001 win 1000 + +0 write(4, ..., 1000) = 1000 + +0 > P. 1001:2001(1000) ack 1 ++.01 < . 1:1(0) ack 2001 win 1000 +// Now SND.UNA = SND.NXT = 2001, MAX.SND.WND = 1000, bytes_acked = 2000. + +// ---- Lower edge: SEG.ACK < SND.UNA - MAX.SND.WND ------------------ +// SND.UNA - MAX.SND.WND = 2001 - 1000 = 1001, so SEG.ACK = 1000 falls +// below the acceptable range. + +0 < . 1:1(0) ack 1000 win 1000 +// Expect a challenge ACK: . + +0 > . 2001:2001(0) ack 1 diff --git a/tools/testing/selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt b/tools/testing/selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt index 174ce9a1bfc0..ee6baf7c36cf 100644 --- a/tools/testing/selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt +++ b/tools/testing/selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt @@ -19,7 +19,9 @@ // bad packet with high tsval (its ACK sequence is above our sndnxt) +0 < F. 1:1(0) ack 9999 win 20000 - +// Challenge ACK for SEG.ACK > SND.NXT (RFC 5961 5.2 / RFC 793 3.9). +// ecr=200 (not 200000) proves ts_recent was not updated from the bad packet. + +0 > . 1:1(0) ack 1 +0 < . 1:1001(1000) ack 1 win 20000 +0 > . 1:1(0) ack 1001 diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index 5a5ff88321d5..c499953d4885 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -23,6 +23,7 @@ ALL_TESTS=" kci_test_encap kci_test_macsec kci_test_macsec_vlan + kci_test_team_bridge_macvlan kci_test_ipsec kci_test_ipsec_offload kci_test_fdb_get @@ -636,6 +637,49 @@ kci_test_macsec_vlan() end_test "PASS: macsec_vlan" } +# Test ndo_change_rx_flags call from dev_uc_add under addr_list_lock spinlock. +# When we are flipping the promisc, make sure it runs on the work queue. +# +# https://lore.kernel.org/netdev/20260214033859.43857-1-jiayuan.chen@linux.dev/ +# With (more conventional) macvlan instead of macsec. +# macvlan -> bridge -> team -> dummy +kci_test_team_bridge_macvlan() +{ + local vlan="test_macv1" + local bridge="test_br1" + local team="test_team1" + local dummy="test_dummy1" + local ret=0 + + run_cmd ip link add $team type team + if [ $ret -ne 0 ]; then + end_test "SKIP: team_bridge_macvlan: can't add team interface" + return $ksft_skip + fi + + run_cmd ip link add $dummy type dummy + run_cmd ip link set $dummy master $team + run_cmd ip link set $team up + run_cmd ip link add $bridge type bridge vlan_filtering 1 + run_cmd ip link set $bridge up + run_cmd ip link set $team master $bridge + run_cmd ip link add link $bridge name $vlan \ + address 00:aa:bb:cc:dd:ee type macvlan mode bridge + run_cmd ip link set $vlan up + + run_cmd ip link del $vlan + run_cmd ip link del $bridge + run_cmd ip link del $team + run_cmd ip link del $dummy + + if [ $ret -ne 0 ]; then + end_test "FAIL: team_bridge_macvlan" + return 1 + fi + + end_test "PASS: team_bridge_macvlan" +} + #------------------------------------------------------------------- # Example commands # ip x s add proto esp src 14.0.0.52 dst 14.0.0.70 \ diff --git a/tools/testing/selftests/net/tcp_ao/config b/tools/testing/selftests/net/tcp_ao/config index 971cb6fa2d63..f22148512365 100644 --- a/tools/testing/selftests/net/tcp_ao/config +++ b/tools/testing/selftests/net/tcp_ao/config @@ -1,3 +1,4 @@ +CONFIG_CRYPTO_CMAC=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_RMD160=y CONFIG_CRYPTO_SHA1=y diff --git a/tools/testing/selftests/net/tcp_ecmp_failover.sh b/tools/testing/selftests/net/tcp_ecmp_failover.sh new file mode 100755 index 000000000000..5768aa8bff6a --- /dev/null +++ b/tools/testing/selftests/net/tcp_ecmp_failover.sh @@ -0,0 +1,216 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright 2026 Google LLC. +# +# This test verifies TCP flow failover between ECMP routes +# upon carrier loss on the active device. +# +# socat -----------------------------> socat +# | +# .-- veth-c1 -|- veth-s1 --. +# dummy0 -| | |-- dummy0 +# '-- veth-c2 -|- veth-s2 --' +# | +# + +REQUIRE_JQ=no +REQUIRE_MZ=no +NUM_NETIFS=0 + +source forwarding/lib.sh + +CLIENT_IP="10.0.59.1" +SERVER_IP="10.0.92.1" +CLIENT_IP6="2001:db8:5a9a::1" +SERVER_IP6="2001:db8:9292::1" + +setup_server() +{ + IP="ip -n $server" + NS_EXEC="ip netns exec $server" + + $IP link add dummy0 type dummy + $IP link set dummy0 up + + $IP -4 addr add $SERVER_IP/32 dev dummy0 + $IP -6 addr add $SERVER_IP6/128 dev dummy0 nodad + + $IP link set veth-s1 up + $IP link set veth-s2 up + + $IP -4 addr add 192.168.1.2/24 dev veth-s1 + $IP -4 addr add 192.168.2.2/24 dev veth-s2 + + $IP -4 route add $CLIENT_IP/32 \ + nexthop via 192.168.1.1 dev veth-s1 weight 1 \ + nexthop via 192.168.2.1 dev veth-s2 weight 1 + + $IP -6 addr add 2001:db8:1::2/64 dev veth-s1 nodad + $IP -6 addr add 2001:db8:2::2/64 dev veth-s2 nodad + + $IP -6 route add $CLIENT_IP6/128 \ + nexthop via 2001:db8:1::1 dev veth-s1 weight 1 \ + nexthop via 2001:db8:2::1 dev veth-s2 weight 1 +} + +setup_client() +{ + IP="ip -n $client" + NS_EXEC="ip netns exec $client" + + $IP link add dummy0 type dummy + $IP link set dummy0 up + + $IP -4 addr add $CLIENT_IP/32 dev dummy0 + $IP -6 addr add $CLIENT_IP6/128 dev dummy0 nodad + + $IP link set veth-c1 up + $IP link set veth-c2 up + + $IP -4 addr add 192.168.1.1/24 dev veth-c1 + $IP -4 addr add 192.168.2.1/24 dev veth-c2 + + $IP -4 route add $SERVER_IP/32 \ + nexthop via 192.168.1.2 dev veth-c1 weight 1 \ + nexthop via 192.168.2.2 dev veth-c2 weight 1 + + $IP -6 addr add 2001:db8:1::1/64 dev veth-c1 nodad + $IP -6 addr add 2001:db8:2::1/64 dev veth-c2 nodad + + $IP -6 route add $SERVER_IP6/128 \ + nexthop via 2001:db8:1::2 dev veth-c1 weight 1 \ + nexthop via 2001:db8:2::2 dev veth-c2 weight 1 + + # By default, tcp_retries1=3 triggers a route refresh + # after 3 retransmits (~5s). Ensure this never occurs + # for test stability. + $NS_EXEC sysctl -qw net.ipv4.tcp_retries1=100 + + # When NETDEV_CHANGE is issued for a dev tied to an ECMP + # route, RTNH_F_LINKDOWN is flagged and the sernum is + # bumped to invalidate the route via sk_dst_check(). + # + # Without ignore_routes_with_linkdown=1, subsequent + # lookups may still select the same RTNH_F_LINKDOWN route. + $NS_EXEC sysctl -qw net.ipv4.conf.veth-c1.ignore_routes_with_linkdown=1 + $NS_EXEC sysctl -qw net.ipv4.conf.veth-c2.ignore_routes_with_linkdown=1 + + $NS_EXEC sysctl -qw net.ipv6.conf.veth-c1.ignore_routes_with_linkdown=1 + $NS_EXEC sysctl -qw net.ipv6.conf.veth-c2.ignore_routes_with_linkdown=1 +} + +setup() +{ + setup_ns client server + + ip -n "$client" link add veth-c1 type veth peer veth-s1 netns "$server" + ip -n "$client" link add veth-c2 type veth peer veth-s2 netns "$server" + + setup_server + setup_client +} + +cleanup() +{ + cleanup_all_ns > /dev/null 2>&1 +} + +tcp_ecmp_failover() +{ + local pf=$1; shift + local server_ip=$1; shift + local client_ip=$1; shift + + RET=0 + + tcpdump_start veth-s1 "$server" + tcpdump_start veth-s2 "$server" + + ip netns exec "$server" \ + socat -u TCP-LISTEN:8080,pf="$pf",bind="$server_ip",reuseaddr /dev/null & + server_pid=$! + + # Wait for server to start listening. + # Sometimes client fails without this sleep. + sleep 1 + + ip netns exec "$client" \ + socat -u /dev/zero TCP:"$server_ip":8080,pf="$pf",bind="$client_ip" & + client_pid=$! + + # To capture enough packets. + sleep 3 + + tcpdump_stop veth-s1 + tcpdump_stop veth-s2 + + pkts_s1=$(tcpdump_show veth-s1 | wc -l) + pkts_s2=$(tcpdump_show veth-s2 | wc -l) + + tcpdump_cleanup veth-s1 + tcpdump_cleanup veth-s2 + + # Detect the device chosen by the client + if [ "$pkts_s1" -gt "$pkts_s2" ]; then + veth_down=veth-s1 + veth_up=veth-s2 + else + veth_down=veth-s2 + veth_up=veth-s1 + fi + + # Taking down $veth_down causes its peer to lose carrier, + # triggering NETDEV_CHANGE. This flags RTNH_F_LINKDOWN + # and bumps the sernum for the route associated with that + # peer, invalidating the cached dst in the TCP socket. + # + # Consequently, sk_dst_check() fails, forcing the subsequent + # lookup to select the remaining healthy route via $veth_up. + ip -n "$server" link set "$veth_down" down + + tcpdump_start "$veth_up" "$server" + + # To capture enough packets. + sleep 3 + + tcpdump_stop "$veth_up" + + kill -9 "$client_pid" > /dev/null 2>&1 + kill -9 "$server_pid" > /dev/null 2>&1 + wait 2> /dev/null + + pkts=$(tcpdump_show $veth_up | wc -l) + + tcpdump_cleanup "$veth_up" + + if [ "$pkts" -lt 1000 ]; then + RET=$ksft_fail + fi +} + +test_ipv4() +{ + setup + tcp_ecmp_failover IPv4 $SERVER_IP $CLIENT_IP + log_test "TCP IPv4 failover" + cleanup +} + +test_ipv6() +{ + setup + tcp_ecmp_failover IPv6 "[$SERVER_IP6]" "[$CLIENT_IP6]" + log_test "TCP IPv6 failover" + cleanup +} + +require_command socat +require_command tcpdump + +trap cleanup EXIT + +test_ipv4 +test_ipv6 + +exit "$EXIT_STATUS" diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 9e2ccea13d70..30a236b8e9f7 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -946,6 +946,49 @@ TEST_F(tls, peek_and_splice) EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0); } +TEST_F(tls, splice_to_pipe_small) +{ + int send_len = TLS_PAYLOAD_MAX_LEN; + char mem_send[TLS_PAYLOAD_MAX_LEN]; + char mem_recv[TLS_PAYLOAD_MAX_LEN]; + size_t total = 0; + int p[2]; + + memrnd(mem_send, sizeof(mem_send)); + + ASSERT_GE(pipe(p), 0); + + /* Shrink pipe to 1 page (typically 4096 bytes) to force multiple + * splice iterations for a 16384-byte TLS record. + */ + EXPECT_GE(fcntl(p[1], F_SETPIPE_SZ, 4096), 4096); + + EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len); + + while (total < (size_t)send_len) { + ssize_t spliced, drained; + + spliced = splice(self->cfd, NULL, p[1], NULL, + send_len - total, 0); + EXPECT_GT(spliced, 0); + if (spliced <= 0) + break; + + drained = read(p[0], mem_recv + total, spliced); + EXPECT_EQ(drained, spliced); + if (drained <= 0) + break; + + total += drained; + } + + EXPECT_EQ(total, (size_t)send_len); + EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0); + + close(p[0]); + close(p[1]); +} + #define MAX_FRAGS 48 TEST_F(tls, splice_short) { diff --git a/tools/testing/selftests/prctl/Makefile b/tools/testing/selftests/prctl/Makefile index 01dc90fbb509..e770e86fad9a 100644 --- a/tools/testing/selftests/prctl/Makefile +++ b/tools/testing/selftests/prctl/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 ifndef CROSS_COMPILE -uname_M := $(shell uname -m 2>/dev/null || echo not) -ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) +ARCH ?= $(shell uname -m 2>/dev/null || echo not) +override ARCH := $(shell echo $(ARCH) | sed -e s/i.86/x86/ -e s/x86_64/x86/) ifeq ($(ARCH),x86) TEST_PROGS := disable-tsc-ctxt-sw-stress-test disable-tsc-on-off-stress-test \ diff --git a/tools/testing/selftests/rdma/Makefile b/tools/testing/selftests/rdma/Makefile new file mode 100644 index 000000000000..7dd7cba7a73c --- /dev/null +++ b/tools/testing/selftests/rdma/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +TEST_PROGS := rxe_rping_between_netns.sh \ + rxe_ipv6.sh \ + rxe_socket_with_netns.sh \ + rxe_test_NETDEV_UNREGISTER.sh + +include ../lib.mk diff --git a/tools/testing/selftests/rdma/config b/tools/testing/selftests/rdma/config new file mode 100644 index 000000000000..4ffb814e253b --- /dev/null +++ b/tools/testing/selftests/rdma/config @@ -0,0 +1,3 @@ +CONFIG_TUN +CONFIG_VETH +CONFIG_RDMA_RXE diff --git a/tools/testing/selftests/rdma/rxe_ipv6.sh b/tools/testing/selftests/rdma/rxe_ipv6.sh new file mode 100755 index 000000000000..b7059bfd6d7c --- /dev/null +++ b/tools/testing/selftests/rdma/rxe_ipv6.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# Configuration +NS_NAME="net6" +VETH_HOST="veth0" +VETH_NS="veth1" +RXE_NAME="rxe6" +PORT=4791 +IP6_ADDR="2001:db8::1/64" + +exec > /dev/null + +# Cleanup function to run on exit (even on failure) +cleanup() { + ip netns del "$NS_NAME" 2>/dev/null + modprobe -r rdma_rxe 2>/dev/null + echo "Done." +} +trap cleanup EXIT + +# 1. Prerequisites check +for mod in tun veth rdma_rxe; do + if ! modinfo "$mod" >/dev/null 2>&1; then + echo "Error: Kernel module '$mod' not found." + exit 1 + fi +done + +modprobe rdma_rxe + +# 2. Setup Namespace and Networking +echo "Setting up IPv6 network namespace..." +ip netns add "$NS_NAME" +ip link add "$VETH_HOST" type veth peer name "$VETH_NS" +ip link set "$VETH_NS" netns "$NS_NAME" +ip netns exec "$NS_NAME" ip addr add "$IP6_ADDR" dev "$VETH_NS" +ip netns exec "$NS_NAME" ip link set "$VETH_NS" up +ip link set "$VETH_HOST" up + +# 3. Add RDMA Link +echo "Adding RDMA RXE link..." +if ! ip netns exec "$NS_NAME" rdma link add "$RXE_NAME" type rxe netdev "$VETH_NS"; then + echo "Error: Failed to create RXE link." + exit 1 +fi + +# 4. Verification: Port should be listening +# Using -H to skip headers and -q for quiet exit codes +if ! ip netns exec "$NS_NAME" ss -Hul6n sport = :$PORT | grep -q ":$PORT"; then + echo "Error: UDP port $PORT is NOT listening after link creation." + exit 1 +fi +echo "Verified: Port $PORT is active." + +# 5. Removal and Verification +echo "Deleting RDMA link..." +ip netns exec "$NS_NAME" rdma link del "$RXE_NAME" + +if ip netns exec "$NS_NAME" ss -Hul6n sport = :$PORT | grep -q ":$PORT"; then + echo "Error: UDP port $PORT still active after link deletion." + exit 1 +fi +echo "Verified: Port $PORT closed successfully." diff --git a/tools/testing/selftests/rdma/rxe_rping_between_netns.sh b/tools/testing/selftests/rdma/rxe_rping_between_netns.sh new file mode 100755 index 000000000000..e5b876f58c6e --- /dev/null +++ b/tools/testing/selftests/rdma/rxe_rping_between_netns.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# Configuration +NS="test1" +VETH_A="veth-a" +VETH_B="veth-b" +IP_A="1.1.1.1" +IP_B="1.1.1.2" +PORT=4791 + +exec > /dev/null + +# --- Cleanup Routine --- +cleanup() { + echo "Cleaning up resources..." + rdma link del rxe1 2>/dev/null + ip netns exec "$NS" rdma link del rxe0 2>/dev/null + ip link delete "$VETH_B" 2>/dev/null + ip netns del "$NS" 2>/dev/null + modprobe -r rdma_rxe 2>/dev/null +} +trap cleanup EXIT + +# --- Prerequisite Checks --- +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root" + exit 1 +fi + +modprobe rdma_rxe || { echo "Failed to load rdma_rxe"; exit 1; } + +# --- Setup Network Topology --- +echo "Setting up network namespace and veth pair..." +ip netns add "$NS" +ip link add "$VETH_A" type veth peer name "$VETH_B" +ip link set "$VETH_A" netns "$NS" + +# Configure Namespace side (test1) +ip netns exec "$NS" ip addr add "$IP_A/24" dev "$VETH_A" +ip netns exec "$NS" ip link set "$VETH_A" up +ip netns exec "$NS" ip link set lo up + +# Configure Host side +ip addr add "$IP_B/24" dev "$VETH_B" +ip link set "$VETH_B" up + +# --- RXE Link Creation --- +echo "Creating RDMA links..." +ip netns exec "$NS" rdma link add rxe0 type rxe netdev "$VETH_A" +rdma link add rxe1 type rxe netdev "$VETH_B" + +# Verify UDP 4791 is listening +check_port() { + local target=$1 # "host" or "ns" + if [ "$target" == "ns" ]; then + ip netns exec "$NS" ss -Huln sport == :$PORT | grep -q ":$PORT" + else + ss -Huln sport == :$PORT | grep -q ":$PORT" + fi +} + +check_port "ns" || { echo "Error: RXE port not listening in namespace"; exit 1; } +check_port "host" || { echo "Error: RXE port not listening on host"; exit 1; } + +# --- Connectivity Test --- +echo "Testing connectivity with rping..." +ping -c 2 -W 1 "$IP_A" > /dev/null || { echo "Ping failed"; exit 1; } + +# Start rping server in background +ip netns exec "$NS" rping -s -a "$IP_A" -v > /dev/null 2>&1 & +RPING_PID=$! +sleep 1 # Allow server to bind + +# Run rping client +rping -c -a "$IP_A" -d -v -C 3 +RESULT=$? + +kill $RPING_PID 2>/dev/null + +if [ $RESULT -eq 0 ]; then + echo "SUCCESS: RDMA traffic verified." +else + echo "FAILURE: rping failed." + exit 1 +fi diff --git a/tools/testing/selftests/rdma/rxe_socket_with_netns.sh b/tools/testing/selftests/rdma/rxe_socket_with_netns.sh new file mode 100755 index 000000000000..002e5098f751 --- /dev/null +++ b/tools/testing/selftests/rdma/rxe_socket_with_netns.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# Configuration +PORT=4791 +MODS=("tun" "rdma_rxe") + +exec > /dev/null + +# --- Helper: Cleanup Routine --- +cleanup() { + echo "Cleaning up resources..." + rdma link del rxe1 2>/dev/null + rdma link del rxe0 2>/dev/null + ip link del tun0 2>/dev/null + ip link del tun1 2>/dev/null + for m in "${MODS[@]}"; do modprobe -r "$m" 2>/dev/null; done +} + +# Ensure cleanup runs on script exit or interrupt +trap cleanup EXIT + +# --- Phase 1: Environment Check --- +if [[ $EUID -ne 0 ]]; then + echo "Error: This script must be run as root." + exit 1 +fi + +for m in "${MODS[@]}"; do + modprobe "$m" || { echo "Error: Failed to load $m"; exit 1; } +done + +# --- Phase 2: Create Interfaces & RXE Links --- +echo "Creating tun0 (1.1.1.1) and rxe0..." +ip tuntap add mode tun tun0 +ip addr add 1.1.1.1/24 dev tun0 +ip link set tun0 up +rdma link add rxe0 type rxe netdev tun0 + +# Verify port 4791 is listening +if ! ss -Huln sport = :$PORT | grep -q ":$PORT"; then + echo "Error: UDP port $PORT not found after rxe0 creation" + exit 1 +fi + +echo "Creating tun1 (2.2.2.2) and rxe1..." +ip tuntap add mode tun tun1 +ip addr add 2.2.2.2/24 dev tun1 +ip link set tun1 up +rdma link add rxe1 type rxe netdev tun1 + +# Verify port 4791 is still listening +if ! ss -Huln sport = :$PORT | grep -q ":$PORT"; then + echo "Error: UDP port $PORT missing after rxe1 creation" + exit 1 +fi + +# --- Phase 3: Targeted Deletion --- +echo "Deleting rxe1..." +rdma link del rxe1 + +# Port should still be active because rxe0 is still alive +if ! ss -Huln sport = :$PORT | grep -q ":$PORT"; then + echo "Error: UDP port $PORT closed prematurely" + exit 1 +fi + +echo "Deleting rxe0..." +rdma link del rxe0 + +# Port should now be gone +if ss -Huln sport = :$PORT | grep -q ":$PORT"; then + echo "Error: UDP port $PORT still exists after all links deleted" + exit 1 +fi + +echo "Test passed successfully." diff --git a/tools/testing/selftests/rdma/rxe_test_NETDEV_UNREGISTER.sh b/tools/testing/selftests/rdma/rxe_test_NETDEV_UNREGISTER.sh new file mode 100755 index 000000000000..021ca451499d --- /dev/null +++ b/tools/testing/selftests/rdma/rxe_test_NETDEV_UNREGISTER.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# Configuration +DEV_NAME="tun0" +RXE_NAME="rxe0" +RDMA_PORT=4791 + +exec > /dev/null + +# --- Cleanup Routine --- +# Ensures environment is clean even if the script hits an error +cleanup() { + echo "Performing cleanup..." + rdma link del $RXE_NAME 2>/dev/null + ip link del $DEV_NAME 2>/dev/null + modprobe -r rdma_rxe 2>/dev/null +} +trap cleanup EXIT + +# 1. Dependency Check +if ! modinfo rdma_rxe >/dev/null 2>&1; then + echo "Error: rdma_rxe module not found." + exit 1 +fi + +modprobe rdma_rxe + +# 2. Setup TUN Device +echo "Creating $DEV_NAME..." +ip tuntap add mode tun "$DEV_NAME" +ip addr add 1.1.1.1/24 dev "$DEV_NAME" +ip link set "$DEV_NAME" up + +# 3. Attach RXE Link +echo "Attaching RXE link $RXE_NAME to $DEV_NAME..." +rdma link add "$RXE_NAME" type rxe netdev "$DEV_NAME" + +# 4. Verification: Port Check +# Use -H (no header) and -q (quiet) for cleaner scripting logic +if ! ss -Huln sport == :$RDMA_PORT | grep -q ":$RDMA_PORT"; then + echo "Error: UDP port $RDMA_PORT is not listening." + exit 1 +fi +echo "Verified: RXE is listening on UDP $RDMA_PORT." + +# 5. Trigger NETDEV_UNREGISTER +# We delete the underlying device without deleting the RDMA link first. +echo "Triggering NETDEV_UNREGISTER by deleting $DEV_NAME..." +ip link del "$DEV_NAME" + +# 6. Final Verification +# The RXE link and the UDP port should be automatically cleaned up by the kernel. +if rdma link show "$RXE_NAME" 2>/dev/null; then + echo "Error: $RXE_NAME still exists after netdev removal." + exit 1 +fi + +if ss -Huln sport == :$RDMA_PORT | grep -q ":$RDMA_PORT"; then + echo "Error: UDP port $RDMA_PORT still listening after netdev removal." + exit 1 +fi + +echo "Success: NETDEV_UNREGISTER handled correctly." diff --git a/tools/testing/selftests/riscv/cfi/Makefile b/tools/testing/selftests/riscv/cfi/Makefile index 96a4dc4b69c3..93b4738c0e2e 100644 --- a/tools/testing/selftests/riscv/cfi/Makefile +++ b/tools/testing/selftests/riscv/cfi/Makefile @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + CFLAGS += $(KHDR_INCLUDES) CFLAGS += -I$(top_srcdir)/tools/include diff --git a/tools/testing/selftests/rseq/Makefile b/tools/testing/selftests/rseq/Makefile index 4ef90823b652..50d69e22ee7a 100644 --- a/tools/testing/selftests/rseq/Makefile +++ b/tools/testing/selftests/rseq/Makefile @@ -14,14 +14,20 @@ LDLIBS += -lpthread -ldl # still track changes to header files and depend on shared object. OVERRIDE_TARGETS = 1 -TEST_GEN_PROGS = basic_test basic_percpu_ops_test basic_percpu_ops_mm_cid_test param_test \ - param_test_benchmark param_test_compare_twice param_test_mm_cid \ - param_test_mm_cid_benchmark param_test_mm_cid_compare_twice \ - syscall_errors_test slice_test +TEST_GEN_PROGS = basic_test basic_percpu_ops_test basic_percpu_ops_mm_cid_test \ + param_test_benchmark param_test_mm_cid_benchmark -TEST_GEN_PROGS_EXTENDED = librseq.so +TEST_GEN_PROGS_EXTENDED = librseq.so \ + param_test \ + param_test_compare_twice \ + param_test_mm_cid \ + param_test_mm_cid_compare_twice \ + syscall_errors_test \ + legacy_check \ + slice_test \ + check_optimized -TEST_PROGS = run_param_test.sh run_syscall_errors_test.sh +TEST_PROGS = run_param_test.sh run_syscall_errors_test.sh run_legacy_check.sh run_timeslice_test.sh TEST_FILES := settings @@ -62,3 +68,6 @@ $(OUTPUT)/syscall_errors_test: syscall_errors_test.c $(TEST_GEN_PROGS_EXTENDED) $(OUTPUT)/slice_test: slice_test.c $(TEST_GEN_PROGS_EXTENDED) rseq.h rseq-*.h $(CC) $(CFLAGS) $< $(LDLIBS) -lrseq -o $@ + +$(OUTPUT)/check_optimized: check_optimized.c $(TEST_GEN_PROGS_EXTENDED) rseq.h rseq-*.h + $(CC) $(CFLAGS) $< $(LDLIBS) -lrseq -o $@ diff --git a/tools/testing/selftests/rseq/check_optimized.c b/tools/testing/selftests/rseq/check_optimized.c new file mode 100644 index 000000000000..a13e3f2c8fc6 --- /dev/null +++ b/tools/testing/selftests/rseq/check_optimized.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: LGPL-2.1 +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "rseq.h" + +int main(int argc, char **argv) +{ + if (__rseq_register_current_thread(true, false)) + return -1; + return 0; +} diff --git a/tools/testing/selftests/rseq/legacy_check.c b/tools/testing/selftests/rseq/legacy_check.c new file mode 100644 index 000000000000..3f7de4e28303 --- /dev/null +++ b/tools/testing/selftests/rseq/legacy_check.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include + +#include "rseq.h" + +#include "../kselftest_harness.h" + +FIXTURE(legacy) +{ +}; + +static int cpu_id_in_sigfn = -1; + +static void sigfn(int sig) +{ + struct rseq_abi *rs = rseq_get_abi(); + + cpu_id_in_sigfn = rs->cpu_id_start; +} + +FIXTURE_SETUP(legacy) +{ + int res = __rseq_register_current_thread(true, true); + + switch (res) { + case -ENOSYS: + SKIP(return, "RSEQ not enabled\n"); + case -EBUSY: + SKIP(return, "GLIBC owns RSEQ. Disable GLIBC RSEQ registration\n"); + default: + ASSERT_EQ(res, 0); + } + + ASSERT_NE(signal(SIGUSR1, sigfn), SIG_ERR); +} + +FIXTURE_TEARDOWN(legacy) +{ +} + +TEST_F(legacy, legacy_test) +{ + struct rseq_abi *rs = rseq_get_abi(); + + ASSERT_NE(rs, NULL); + + /* Overwrite rs::cpu_id_start */ + rs->cpu_id_start = -1; + sleep(1); + ASSERT_NE(rs->cpu_id_start, -1); + + rs->cpu_id_start = -1; + ASSERT_EQ(raise(SIGUSR1), 0); + ASSERT_NE(rs->cpu_id_start, -1); + ASSERT_NE(cpu_id_in_sigfn, -1); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/rseq/param_test.c b/tools/testing/selftests/rseq/param_test.c index 05d03e679e06..e1e98dbabe4b 100644 --- a/tools/testing/selftests/rseq/param_test.c +++ b/tools/testing/selftests/rseq/param_test.c @@ -38,7 +38,7 @@ static int opt_modulo, verbose; static int opt_yield, opt_signal, opt_sleep, opt_disable_rseq, opt_threads = 200, opt_disable_mod = 0, opt_test = 's'; - +static bool opt_rseq_legacy; static long long opt_reps = 5000; static __thread __attribute__((tls_model("initial-exec"))) @@ -281,9 +281,12 @@ unsigned int yield_mod_cnt, nr_abort; } \ } +#define rseq_no_glibc true + #else #define printf_verbose(fmt, ...) +#define rseq_no_glibc false #endif /* BENCHMARK */ @@ -481,7 +484,7 @@ void *test_percpu_spinlock_thread(void *arg) long long i, reps; if (!opt_disable_rseq && thread_data->reg && - rseq_register_current_thread()) + __rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy)) abort(); reps = thread_data->reps; for (i = 0; i < reps; i++) { @@ -558,7 +561,7 @@ void *test_percpu_inc_thread(void *arg) long long i, reps; if (!opt_disable_rseq && thread_data->reg && - rseq_register_current_thread()) + __rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy)) abort(); reps = thread_data->reps; for (i = 0; i < reps; i++) { @@ -712,7 +715,7 @@ void *test_percpu_list_thread(void *arg) long long i, reps; struct percpu_list *list = (struct percpu_list *)arg; - if (!opt_disable_rseq && rseq_register_current_thread()) + if (!opt_disable_rseq && __rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy)) abort(); reps = opt_reps; @@ -895,7 +898,7 @@ void *test_percpu_buffer_thread(void *arg) long long i, reps; struct percpu_buffer *buffer = (struct percpu_buffer *)arg; - if (!opt_disable_rseq && rseq_register_current_thread()) + if (!opt_disable_rseq && __rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy)) abort(); reps = opt_reps; @@ -1105,7 +1108,7 @@ void *test_percpu_memcpy_buffer_thread(void *arg) long long i, reps; struct percpu_memcpy_buffer *buffer = (struct percpu_memcpy_buffer *)arg; - if (!opt_disable_rseq && rseq_register_current_thread()) + if (!opt_disable_rseq && __rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy)) abort(); reps = opt_reps; @@ -1258,7 +1261,7 @@ void *test_membarrier_worker_thread(void *arg) const int iters = opt_reps; int i; - if (rseq_register_current_thread()) { + if (__rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy)) { fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n", errno, strerror(errno)); abort(); @@ -1323,7 +1326,7 @@ void *test_membarrier_manager_thread(void *arg) intptr_t expect_a = 0, expect_b = 0; int cpu_a = 0, cpu_b = 0; - if (rseq_register_current_thread()) { + if (__rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy)) { fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n", errno, strerror(errno)); abort(); @@ -1475,6 +1478,7 @@ static void show_usage(int argc, char **argv) printf(" [-D M] Disable rseq for each M threads\n"); printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n"); printf(" [-M] Push into buffer and memcpy buffer with memory barriers.\n"); + printf(" [-O] Test with optimized RSEQ\n"); printf(" [-v] Verbose output.\n"); printf(" [-h] Show this help.\n"); printf("\n"); @@ -1602,6 +1606,9 @@ int main(int argc, char **argv) case 'M': opt_mo = RSEQ_MO_RELEASE; break; + case 'L': + opt_rseq_legacy = true; + break; default: show_usage(argc, argv); goto error; @@ -1618,7 +1625,7 @@ int main(int argc, char **argv) if (set_signal_handler()) goto error; - if (!opt_disable_rseq && rseq_register_current_thread()) + if (!opt_disable_rseq && __rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy)) goto error; if (!opt_disable_rseq && !rseq_validate_cpu_id()) { fprintf(stderr, "Error: cpu id getter unavailable\n"); diff --git a/tools/testing/selftests/rseq/rseq-abi.h b/tools/testing/selftests/rseq/rseq-abi.h index ecef315204b2..5f4ea2152c2f 100644 --- a/tools/testing/selftests/rseq/rseq-abi.h +++ b/tools/testing/selftests/rseq/rseq-abi.h @@ -191,10 +191,15 @@ struct rseq_abi { */ struct rseq_abi_slice_ctrl slice_ctrl; + /* + * Place holder to push the size above 32 bytes. + */ + __u8 __reserved; + /* * Flexible array member at end of structure, after last feature field. */ char end[]; -} __attribute__((aligned(4 * sizeof(__u64)))); +} __attribute__((aligned(256))); #endif /* _RSEQ_ABI_H */ diff --git a/tools/testing/selftests/rseq/rseq.c b/tools/testing/selftests/rseq/rseq.c index a736727b83c1..be0d0a97031e 100644 --- a/tools/testing/selftests/rseq/rseq.c +++ b/tools/testing/selftests/rseq/rseq.c @@ -56,6 +56,7 @@ ptrdiff_t rseq_offset; * unsuccessful. */ unsigned int rseq_size = -1U; +static unsigned int rseq_alloc_size; /* Flags used during rseq registration. */ unsigned int rseq_flags; @@ -115,29 +116,17 @@ bool rseq_available(void) } } -/* The rseq areas need to be at least 32 bytes. */ -static -unsigned int get_rseq_min_alloc_size(void) -{ - unsigned int alloc_size = rseq_size; - - if (alloc_size < ORIG_RSEQ_ALLOC_SIZE) - alloc_size = ORIG_RSEQ_ALLOC_SIZE; - return alloc_size; -} - /* * Return the feature size supported by the kernel. * * Depending on the value returned by getauxval(AT_RSEQ_FEATURE_SIZE): * - * 0: Return ORIG_RSEQ_FEATURE_SIZE (20) + * 0: Return ORIG_RSEQ_FEATURE_SIZE (20) * > 0: Return the value from getauxval(AT_RSEQ_FEATURE_SIZE). * * It should never return a value below ORIG_RSEQ_FEATURE_SIZE. */ -static -unsigned int get_rseq_kernel_feature_size(void) +static unsigned int get_rseq_kernel_feature_size(void) { unsigned long auxv_rseq_feature_size, auxv_rseq_align; @@ -152,15 +141,24 @@ unsigned int get_rseq_kernel_feature_size(void) return ORIG_RSEQ_FEATURE_SIZE; } -int rseq_register_current_thread(void) +int __rseq_register_current_thread(bool nolibc, bool legacy) { + unsigned int size; int rc; if (!rseq_ownership) { /* Treat libc's ownership as a successful registration. */ - return 0; + return nolibc ? -EBUSY : 0; } - rc = sys_rseq(&__rseq.abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG); + + /* The minimal allocation size is 32, which is the legacy allocation size */ + size = get_rseq_kernel_feature_size(); + if (legacy || size < ORIG_RSEQ_ALLOC_SIZE) + rseq_alloc_size = ORIG_RSEQ_ALLOC_SIZE; + else + rseq_alloc_size = size; + + rc = sys_rseq(&__rseq.abi, rseq_alloc_size, 0, RSEQ_SIG); if (rc) { /* * After at least one thread has registered successfully @@ -179,9 +177,8 @@ int rseq_register_current_thread(void) * The first thread to register sets the rseq_size to mimic the libc * behavior. */ - if (RSEQ_READ_ONCE(rseq_size) == 0) { - RSEQ_WRITE_ONCE(rseq_size, get_rseq_kernel_feature_size()); - } + if (RSEQ_READ_ONCE(rseq_size) == 0) + RSEQ_WRITE_ONCE(rseq_size, size); return 0; } @@ -194,7 +191,7 @@ int rseq_unregister_current_thread(void) /* Treat libc's ownership as a successful unregistration. */ return 0; } - rc = sys_rseq(&__rseq.abi, get_rseq_min_alloc_size(), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG); + rc = sys_rseq(&__rseq.abi, rseq_alloc_size, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG); if (rc) return -1; return 0; diff --git a/tools/testing/selftests/rseq/rseq.h b/tools/testing/selftests/rseq/rseq.h index f51a5fdb0444..c62ebb9290c0 100644 --- a/tools/testing/selftests/rseq/rseq.h +++ b/tools/testing/selftests/rseq/rseq.h @@ -8,6 +8,7 @@ #ifndef RSEQ_H #define RSEQ_H +#include #include #include #include @@ -142,7 +143,12 @@ static inline struct rseq_abi *rseq_get_abi(void) * succeed. A restartable sequence executed from a non-registered * thread will always fail. */ -int rseq_register_current_thread(void); +int __rseq_register_current_thread(bool nolibc, bool legacy); + +static inline int rseq_register_current_thread(void) +{ + return __rseq_register_current_thread(false, false); +} /* * Unregister rseq for current thread. diff --git a/tools/testing/selftests/rseq/run_legacy_check.sh b/tools/testing/selftests/rseq/run_legacy_check.sh new file mode 100755 index 000000000000..5577b46ea092 --- /dev/null +++ b/tools/testing/selftests/rseq/run_legacy_check.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0" ./legacy_check diff --git a/tools/testing/selftests/rseq/run_param_test.sh b/tools/testing/selftests/rseq/run_param_test.sh index 8d31426ab41f..69a3fa049929 100755 --- a/tools/testing/selftests/rseq/run_param_test.sh +++ b/tools/testing/selftests/rseq/run_param_test.sh @@ -34,6 +34,11 @@ REPS=1000 SLOW_REPS=100 NR_THREADS=$((6*${NR_CPUS})) +# Prevent GLIBC from registering RSEQ so the selftest can run in legacy and +# performance optimized mode. +GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0" +export GLIBC_TUNABLES + function do_tests() { local i=0 @@ -103,6 +108,40 @@ function inject_blocking() NR_LOOPS= } +echo "Testing in legacy RSEQ mode" +echo "Yield injection (25%)" +inject_blocking -m 4 -y -L + +echo "Yield injection (50%)" +inject_blocking -m 2 -y -L + +echo "Yield injection (100%)" +inject_blocking -m 1 -y -L + +echo "Kill injection (25%)" +inject_blocking -m 4 -k -L + +echo "Kill injection (50%)" +inject_blocking -m 2 -k -L + +echo "Kill injection (100%)" +inject_blocking -m 1 -k -L + +echo "Sleep injection (1ms, 25%)" +inject_blocking -m 4 -s 1 -L + +echo "Sleep injection (1ms, 50%)" +inject_blocking -m 2 -s 1 -L + +echo "Sleep injection (1ms, 100%)" +inject_blocking -m 1 -s 1 -L + +./check_optimized || { + echo "Skipping optimized RSEQ mode test. Not supported"; + exit 0 +} + +echo "Testing in optimized RSEQ mode" echo "Yield injection (25%)" inject_blocking -m 4 -y diff --git a/tools/testing/selftests/rseq/run_timeslice_test.sh b/tools/testing/selftests/rseq/run_timeslice_test.sh new file mode 100755 index 000000000000..551ebed71ec6 --- /dev/null +++ b/tools/testing/selftests/rseq/run_timeslice_test.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ + +# Prevent GLIBC from registering RSEQ so the selftest can run in legacy +# and performance optimized mode. +GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0" +export GLIBC_TUNABLES + +./check_optimized || { + echo "Skipping optimized RSEQ mode test. Not supported"; + exit 0 +} + +./slice_test diff --git a/tools/testing/selftests/rseq/slice_test.c b/tools/testing/selftests/rseq/slice_test.c index 357122dcb487..e402d4440bc2 100644 --- a/tools/testing/selftests/rseq/slice_test.c +++ b/tools/testing/selftests/rseq/slice_test.c @@ -124,6 +124,13 @@ FIXTURE_SETUP(slice_ext) { cpu_set_t affinity; + if (__rseq_register_current_thread(true, false)) + SKIP(return, "RSEQ not supported\n"); + + if (prctl(PR_RSEQ_SLICE_EXTENSION, PR_RSEQ_SLICE_EXTENSION_SET, + PR_RSEQ_SLICE_EXT_ENABLE, 0, 0)) + SKIP(return, "Time slice extension not supported\n"); + ASSERT_EQ(sched_getaffinity(0, sizeof(affinity), &affinity), 0); /* Pin it on a single CPU. Avoid CPU 0 */ @@ -137,11 +144,6 @@ FIXTURE_SETUP(slice_ext) break; } - ASSERT_EQ(rseq_register_current_thread(), 0); - - ASSERT_EQ(prctl(PR_RSEQ_SLICE_EXTENSION, PR_RSEQ_SLICE_EXTENSION_SET, - PR_RSEQ_SLICE_EXT_ENABLE, 0, 0), 0); - self->noise_params.noise_nsecs = variant->noise_nsecs; self->noise_params.sleep_nsecs = variant->sleep_nsecs; self->noise_params.run = 1; diff --git a/tools/testing/selftests/sparc64/Makefile b/tools/testing/selftests/sparc64/Makefile index a19531dba4dc..88f7be76f962 100644 --- a/tools/testing/selftests/sparc64/Makefile +++ b/tools/testing/selftests/sparc64/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -uname_M := $(shell uname -m 2>/dev/null || echo not) -ARCH ?= $(shell echo $(uname_M) | sed -e s/x86_64/x86/) +ARCH ?= $(shell uname -m 2>/dev/null || echo not) +override ARCH := $(shell echo $(ARCH) | sed -e s/x86_64/x86/) ifneq ($(ARCH),sparc64) nothing: diff --git a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json index eefadd0546d3..848696c373fc 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json +++ b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json @@ -1136,5 +1136,194 @@ "teardown": [ "$TC qdisc del dev $DUMMY handle 1: root" ] + }, + { + "id": "7a5f", + "name": "Force red to dequeue from its child's gso_skb with qfq leaf", + "category": [ + "qdisc", + "tbf", + "red", + "qfq" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.11.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY root handle 1: tbf rate 88bit burst 1661b peakrate 2257333 minburst 1024 limit 7b", + "$TC qdisc add dev $DUMMY parent 1: handle 2: red limit 757 min 16 max 24 avpkt 16", + "$TC qdisc add dev $DUMMY parent 2: handle 3: qfq", + "$TC class add dev $DUMMY classid 3:1 parent 3: qfq maxpkt 512 weight 1", + "$TC filter add dev $DUMMY parent 3: protocol ip prio 1 matchall classid 3:1 action ok" + ], + "cmdUnderTest": "ping -c 1 10.10.10.1 -W0.01 -I$DUMMY || true", + "expExitCode": "0", + "verifyCmd": "$TC -s -j qdisc ls dev $DUMMY parent 1:", + "matchJSON": [ + { + "kind": "red", + "handle": "2:", + "bytes": 98, + "packets": 1, + "backlog": 0, + "qlen": 0 + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "cdae", + "name": "Force sfb to dequeue from its child's gso_skb with qfq leaf", + "category": [ + "qdisc", + "tbf", + "sfb", + "qfq" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.11.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY root handle 1: tbf rate 88bit burst 1661b peakrate 2257333 minburst 1024 limit 7b", + "$TC qdisc add dev $DUMMY parent 1: handle 2: sfb", + "$TC qdisc add dev $DUMMY parent 2: handle 3: qfq", + "$TC class add dev $DUMMY classid 3:1 parent 3: qfq maxpkt 512 weight 1", + "$TC filter add dev $DUMMY parent 3: protocol ip prio 1 matchall classid 3:1 action ok" + ], + "cmdUnderTest": "ping -c 1 10.10.10.1 -W0.01 -I$DUMMY || true", + "expExitCode": "0", + "verifyCmd": "$TC -s -j qdisc ls dev $DUMMY parent 1:", + "matchJSON": [ + { + "kind": "sfb", + "handle": "2:", + "bytes": 98, + "packets": 1, + "backlog": 0, + "qlen": 0 + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "291d", + "name": "Force red to dequeue from its child's gso_skb with dualpi2 leaf", + "category": [ + "qdisc", + "tbf", + "red", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.11.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY root handle 1: tbf rate 88bit burst 1661b peakrate 2257333 minburst 1024 limit 7b", + "$TC qdisc add dev $DUMMY parent 1: handle 2: red limit 757 min 16 max 24 avpkt 16", + "$TC qdisc add dev $DUMMY parent 2: handle 3: dualpi2" + ], + "cmdUnderTest": "ping -c 1 10.10.10.1 -W0.01 -I$DUMMY || true", + "expExitCode": "0", + "verifyCmd": "$TC -s -j qdisc ls dev $DUMMY parent 1:", + "matchJSON": [ + { + "kind": "red", + "handle": "2:", + "bytes": 98, + "packets": 1, + "backlog": 0, + "qlen": 0 + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "9c6d", + "name": "Force sfb to dequeue from its child's gso_skb with dualpi2 leaf", + "category": [ + "qdisc", + "tbf", + "sfb", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.11.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY root handle 1: tbf rate 88bit burst 1661b peakrate 2257333 minburst 1024 limit 7b", + "$TC qdisc add dev $DUMMY parent 1: handle 2: sfb", + "$TC qdisc add dev $DUMMY parent 2: handle 3: dualpi2" + ], + "cmdUnderTest": "ping -c 1 10.10.10.1 -W0.01 -I$DUMMY || true", + "expExitCode": "0", + "verifyCmd": "$TC -s -j qdisc ls dev $DUMMY parent 1:", + "matchJSON": [ + { + "kind": "sfb", + "handle": "2:", + "bytes": 98, + "packets": 1, + "backlog": 0, + "qlen": 0 + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] + }, + { + "id": "3a62", + "name": "Try to create a qlen underflow with QFQ/CBS", + "category": [ + "qdisc", + "qfq", + "cbs" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.10.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY root handle 1: qfq", + "$TC class add dev $DUMMY classid 1:1 parent 1: qfq", + "$TC class add dev $DUMMY classid 1:2 parent 1: qfq", + "$TC qdisc add dev $DUMMY handle 2: parent 1:1 cbs", + "$TC qdisc add dev $DUMMY handle 3: parent 2: netem delay 5000000000", + "$TC filter add dev $DUMMY parent 1: prio 1 u32 match ip dst 10.10.10.1 classid 1:1 action ok", + "$TC filter add dev $DUMMY parent 1: prio 2 u32 match ip dst 10.10.10.2 classid 1:2 action ok", + "ping -c 1 10.10.10.1 -W0.01 -I$DUMMY || true", + "$IP l set $DUMMY down", + "$IP l set $DUMMY up", + "$TC qdisc replace dev $DUMMY handle 4: parent 2: pfifo" + ], + "cmdUnderTest": "ping -c 1 10.10.10.2 -W0.01 -I$DUMMY", + "expExitCode": "1", + "verifyCmd": "$TC -s -j qdisc ls dev $DUMMY parent 1:1", + "matchJSON": [ + { + "kind": "cbs", + "handle": "2:", + "bytes": 0, + "packets": 0 + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root" + ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json index 557fb074acf0..cd19d05925e4 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json @@ -302,5 +302,31 @@ "$TC qdisc del dev $ETH root", "echo \"1\" > /sys/bus/netdevsim/del_device" ] + }, + { + "id": "c7e1", + "name": "Class dump after graft and delete of explicit child qdisc", + "category": [ + "qdisc", + "taprio" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "echo \"1 1 8\" > /sys/bus/netdevsim/new_device", + "$TC qdisc replace dev $ETH handle 8001: parent root taprio num_tc 8 map 0 1 2 3 4 5 6 7 queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 base-time 0 sched-entry S ff 20000000 clockid CLOCK_TAI", + "$TC qdisc add dev $ETH parent 8001:1 handle 8002: pfifo", + "$TC qdisc del dev $ETH parent 8001:1 handle 8002:" + ], + "cmdUnderTest": "$TC class show dev $ETH", + "expExitCode": "0", + "verifyCmd": "$TC class show dev $ETH", + "matchPattern": "class taprio 8001:[0-9]+ root", + "matchCount": "8", + "teardown": [ + "$TC qdisc del dev $ETH root", + "echo \"1\" > /sys/bus/netdevsim/del_device" + ] } ] diff --git a/tools/testing/selftests/thermal/intel/power_floor/Makefile b/tools/testing/selftests/thermal/intel/power_floor/Makefile index 9b88e57dbba5..07463c2160e0 100644 --- a/tools/testing/selftests/thermal/intel/power_floor/Makefile +++ b/tools/testing/selftests/thermal/intel/power_floor/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 ifndef CROSS_COMPILE -uname_M := $(shell uname -m 2>/dev/null || echo not) -ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) +ARCH ?= $(shell uname -m 2>/dev/null || echo not) +override ARCH := $(shell echo $(ARCH) | sed -e s/i.86/x86/ -e s/x86_64/x86/) ifeq ($(ARCH),x86) TEST_GEN_PROGS := power_floor_test diff --git a/tools/testing/selftests/thermal/intel/workload_hint/Makefile b/tools/testing/selftests/thermal/intel/workload_hint/Makefile index 37ff3286283b..49e3bc2b20f9 100644 --- a/tools/testing/selftests/thermal/intel/workload_hint/Makefile +++ b/tools/testing/selftests/thermal/intel/workload_hint/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 ifndef CROSS_COMPILE -uname_M := $(shell uname -m 2>/dev/null || echo not) -ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) +ARCH ?= $(shell uname -m 2>/dev/null || echo not) +override ARCH := $(shell echo $(ARCH) | sed -e s/i.86/x86/ -e s/x86_64/x86/) ifeq ($(ARCH),x86) TEST_GEN_PROGS := workload_hint_test diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile index ec6a8ce83d38..6e4fe8d1fed1 100644 --- a/tools/testing/selftests/ublk/Makefile +++ b/tools/testing/selftests/ublk/Makefile @@ -37,6 +37,7 @@ TEST_PROGS += test_loop_07.sh TEST_PROGS += test_integrity_01.sh TEST_PROGS += test_integrity_02.sh +TEST_PROGS += test_integrity_03.sh TEST_PROGS += test_recover_01.sh TEST_PROGS += test_recover_02.sh diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c index fbd9b1e7342a..0b23c09daea5 100644 --- a/tools/testing/selftests/ublk/kublk.c +++ b/tools/testing/selftests/ublk/kublk.c @@ -1735,6 +1735,17 @@ static int __cmd_dev_add(const struct dev_ctx *ctx) goto fail; } + /* + * The kernel may reduce nr_hw_queues (e.g. capped to nr_cpu_ids). + * Cap nthreads to the actual queue count to avoid creating extra + * handler threads that will hang during device removal. + * + * per_io_tasks mode is excluded: threads interleave across all + * queues so nthreads > nr_hw_queues is valid and intentional. + */ + if (!ctx->per_io_tasks && dev->nthreads > info->nr_hw_queues) + dev->nthreads = info->nr_hw_queues; + ret = ublk_start_daemon(ctx, dev); ublk_dbg(UBLK_DBG_DEV, "%s: daemon exit %d\n", __func__, ret); if (ret < 0) diff --git a/tools/testing/selftests/ublk/test_batch_01.sh b/tools/testing/selftests/ublk/test_batch_01.sh index a18fb39af8be..6e19303706a9 100755 --- a/tools/testing/selftests/ublk/test_batch_01.sh +++ b/tools/testing/selftests/ublk/test_batch_01.sh @@ -18,7 +18,7 @@ dev_id=$(_add_ublk_dev -t loop -q 2 -b "${UBLK_BACKFILES[0]}") _check_add_dev $TID $? if ! _mkfs_mount_test /dev/ublkb"${dev_id}"; then - _cleanup_test "generic" + _cleanup_test _show_result $TID 255 fi @@ -27,5 +27,5 @@ _check_add_dev $TID $? _mkfs_mount_test /dev/ublkb"${dev_id}" ERR_CODE=$? -_cleanup_test "generic" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_batch_02.sh b/tools/testing/selftests/ublk/test_batch_02.sh index 7ca384d11987..7c683f755379 100755 --- a/tools/testing/selftests/ublk/test_batch_02.sh +++ b/tools/testing/selftests/ublk/test_batch_02.sh @@ -25,5 +25,5 @@ fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrit --iodepth=32 --size=100M --numjobs=4 > /dev/null 2>&1 ERR_CODE=$? -_cleanup_test "generic" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_batch_03.sh b/tools/testing/selftests/ublk/test_batch_03.sh index aca9cf144b55..914ccd6a335d 100755 --- a/tools/testing/selftests/ublk/test_batch_03.sh +++ b/tools/testing/selftests/ublk/test_batch_03.sh @@ -25,5 +25,5 @@ fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrit --iodepth=32 --size=100M --numjobs=4 > /dev/null 2>&1 ERR_CODE=$? -_cleanup_test "generic" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_generic_02.sh b/tools/testing/selftests/ublk/test_generic_02.sh index 46b657143fd6..2afc8cdbed8f 100755 --- a/tools/testing/selftests/ublk/test_generic_02.sh +++ b/tools/testing/selftests/ublk/test_generic_02.sh @@ -29,7 +29,7 @@ for _ in $(seq 100); do done if ! kill -0 "$btrace_pid" 2>/dev/null; then - _cleanup_test "null" + _cleanup_test exit "$UBLK_SKIP_CODE" fi @@ -51,5 +51,5 @@ if grep -q "^out_of_order:" "$UBLK_TMP"; then grep "^out_of_order:" "$UBLK_TMP" ERR_CODE=255 fi -_cleanup_test "null" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_generic_03.sh b/tools/testing/selftests/ublk/test_generic_03.sh index 8934ea926762..8e78be860d34 100755 --- a/tools/testing/selftests/ublk/test_generic_03.sh +++ b/tools/testing/selftests/ublk/test_generic_03.sh @@ -23,5 +23,5 @@ fi if [ "$max_segment_size" != "32768" ]; then ERR_CODE=255 fi -_cleanup_test "null" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_generic_06.sh b/tools/testing/selftests/ublk/test_generic_06.sh index 14a05054fcd8..a8b3634b6b4b 100755 --- a/tools/testing/selftests/ublk/test_generic_06.sh +++ b/tools/testing/selftests/ublk/test_generic_06.sh @@ -36,5 +36,5 @@ if [ $ELAPSED -ge 5 ]; then ERR_CODE=255 fi -_cleanup_test "fault_inject" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_generic_07.sh b/tools/testing/selftests/ublk/test_generic_07.sh index 8dcfd8978f50..d2c5e65bd124 100755 --- a/tools/testing/selftests/ublk/test_generic_07.sh +++ b/tools/testing/selftests/ublk/test_generic_07.sh @@ -23,5 +23,5 @@ if [ "$ERR_CODE" -eq 0 ]; then ERR_CODE=$? fi -_cleanup_test "generic" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_generic_08.sh b/tools/testing/selftests/ublk/test_generic_08.sh index ce88c31d6b9c..77a18b313f3d 100755 --- a/tools/testing/selftests/ublk/test_generic_08.sh +++ b/tools/testing/selftests/ublk/test_generic_08.sh @@ -18,7 +18,7 @@ dev_id=$(_add_ublk_dev -t loop -q 2 --auto_zc "${UBLK_BACKFILES[0]}") _check_add_dev $TID $? if ! _mkfs_mount_test /dev/ublkb"${dev_id}"; then - _cleanup_test "generic" + _cleanup_test _show_result $TID 255 fi @@ -27,5 +27,5 @@ _check_add_dev $TID $? _mkfs_mount_test /dev/ublkb"${dev_id}" ERR_CODE=$? -_cleanup_test "generic" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_generic_09.sh b/tools/testing/selftests/ublk/test_generic_09.sh index 744d0cdaa242..6c25242f245f 100755 --- a/tools/testing/selftests/ublk/test_generic_09.sh +++ b/tools/testing/selftests/ublk/test_generic_09.sh @@ -22,6 +22,6 @@ _check_add_dev $TID $? fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite --iodepth=32 --size=256M > /dev/null 2>&1 ERR_CODE=$? -_cleanup_test "null" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_generic_10.sh b/tools/testing/selftests/ublk/test_generic_10.sh index 4b4293b9081f..fdabc9d9075e 100755 --- a/tools/testing/selftests/ublk/test_generic_10.sh +++ b/tools/testing/selftests/ublk/test_generic_10.sh @@ -25,5 +25,5 @@ if [ "$new_size" != "$size" ]; then ERR_CODE=255 fi -_cleanup_test "null" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_generic_12.sh b/tools/testing/selftests/ublk/test_generic_12.sh index 54b81ddfe9f9..435497f8da8d 100755 --- a/tools/testing/selftests/ublk/test_generic_12.sh +++ b/tools/testing/selftests/ublk/test_generic_12.sh @@ -25,7 +25,7 @@ btrace_pid=$! sleep 2 if ! kill -0 "$btrace_pid" > /dev/null 2>&1; then - _cleanup_test "null" + _cleanup_test exit "$UBLK_SKIP_CODE" fi @@ -54,5 +54,5 @@ if [[ $NR_THREADS_THAT_HANDLED_IO -ne $NTHREADS ]]; then ERR_CODE=255 fi -_cleanup_test "null" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_generic_13.sh b/tools/testing/selftests/ublk/test_generic_13.sh index 922115aa14f4..2c1be6286db8 100755 --- a/tools/testing/selftests/ublk/test_generic_13.sh +++ b/tools/testing/selftests/ublk/test_generic_13.sh @@ -15,5 +15,5 @@ if ${UBLK_PROG} features | grep -q unknown; then ERR_CODE=255 fi -_cleanup_test "null" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_generic_16.sh b/tools/testing/selftests/ublk/test_generic_16.sh index 3ef367836ac5..6a4952146ea1 100755 --- a/tools/testing/selftests/ublk/test_generic_16.sh +++ b/tools/testing/selftests/ublk/test_generic_16.sh @@ -9,7 +9,7 @@ _prep_test "null" "stop --safe command" # Check if SAFE_STOP_DEV feature is supported if ! _have_feature "SAFE_STOP_DEV"; then - _cleanup_test "null" + _cleanup_test exit "$UBLK_SKIP_CODE" fi @@ -52,5 +52,5 @@ wait $dd_pid 2>/dev/null _ublk_del_dev "${dev_id}" udevadm settle -_cleanup_test "null" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_generic_17.sh b/tools/testing/selftests/ublk/test_generic_17.sh index 2278b5fc9dba..b483d53a897a 100755 --- a/tools/testing/selftests/ublk/test_generic_17.sh +++ b/tools/testing/selftests/ublk/test_generic_17.sh @@ -31,5 +31,5 @@ fi # time out here _ublk_del_dev "${dev_id}" -_cleanup_test "fault_inject" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_integrity_02.sh b/tools/testing/selftests/ublk/test_integrity_02.sh index aaf1f52da559..2c35fbc8a7cc 100755 --- a/tools/testing/selftests/ublk/test_integrity_02.sh +++ b/tools/testing/selftests/ublk/test_integrity_02.sh @@ -7,9 +7,10 @@ if ! _have_program fio; then exit $UBLK_SKIP_CODE fi +min_fio_version=fio-3.42 fio_version=$(fio --version) -if [[ "$fio_version" =~ fio-[0-9]+\.[0-9]+$ ]]; then - echo "Requires development fio version with https://github.com/axboe/fio/pull/1992" +if ! sort --version-sort --check=quiet <(printf "%s\n%s\n" "$min_fio_version" "$fio_version"); then + echo "Requires fio version with https://github.com/axboe/fio/pull/1992" exit $UBLK_SKIP_CODE fi diff --git a/tools/testing/selftests/ublk/test_integrity_03.sh b/tools/testing/selftests/ublk/test_integrity_03.sh new file mode 100755 index 000000000000..10f02339ea2d --- /dev/null +++ b/tools/testing/selftests/ublk/test_integrity_03.sh @@ -0,0 +1,103 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh + +if ! _have_program fio; then + exit $UBLK_SKIP_CODE +fi + +_test_fill_and_verify() { + fio --name fill --rw randwrite $fio_args > /dev/null + if [ $? != 0 ]; then + echo "fio fill failed" + ERR_CODE=255 + return 1 + fi + + fio --name verify --rw randread $fio_args > /dev/null + if [ $? != 0 ]; then + echo "fio verify failed" + ERR_CODE=255 + return 1 + fi +} + +_test_corrupted_reftag() { + local dd_reftag_args="bs=1 seek=58 count=6 oflag=dsync conv=notrunc status=none" + + # Overwrite 6-byte reftag at offset 48 + 10 = 58 + dd if=/dev/urandom "of=${UBLK_BACKFILES[1]}" $dd_reftag_args + if [ $? != 0 ]; then + echo "dd corrupted_reftag failed" + ERR_CODE=255 + return 1 + fi + + if fio --name corrupted_reftag --rw randread $fio_args > /dev/null 2> "$fio_err"; then + echo "fio corrupted_reftag unexpectedly succeeded" + ERR_CODE=255 + return 1 + fi + + if ! grep -q "$expected_err" "$fio_err"; then + echo "fio corrupted_reftag message not found: $expected_err" + ERR_CODE=255 + return 1 + fi + + # Reset to 0 + dd if=/dev/zero "of=${UBLK_BACKFILES[1]}" $dd_reftag_args + if [ $? != 0 ]; then + echo "dd restore corrupted_reftag failed" + ERR_CODE=255 + return 1 + fi +} + +_test_corrupted_data() { + local dd_data_args="bs=512 count=1 oflag=direct,dsync conv=notrunc status=none" + + dd if=/dev/zero "of=${UBLK_BACKFILES[0]}" $dd_data_args + if [ $? != 0 ]; then + echo "dd corrupted_data failed" + ERR_CODE=255 + return 1 + fi + + if fio --name corrupted_data --rw randread $fio_args > /dev/null 2> "$fio_err"; then + echo "fio corrupted_data unexpectedly succeeded" + ERR_CODE=255 + return 1 + fi + + if ! grep -q "$expected_err" "$fio_err"; then + echo "fio corrupted_data message not found: $expected_err" + ERR_CODE=255 + return 1 + fi +} + +_prep_test "loop" "end-to-end auto integrity" + +_create_backfile 0 256M +_create_backfile 1 32M # 256M * (64 integrity bytes / 512 data bytes) +integrity_params="--integrity_capable --integrity_reftag + --metadata_size 64 --pi_offset 48 --csum_type nvme" +dev_id=$(_add_ublk_dev -t loop -u $integrity_params "${UBLK_BACKFILES[@]}") +_check_add_dev "$TID" $? + +fio_args="--ioengine libaio --direct 1 --bsrange 512-1M --iodepth 32 + --filename /dev/ublkb$dev_id" +fio_err=$(mktemp "${UBLK_TEST_DIR}"/fio_err_XXXXX) +ERR_CODE=0 + +expected_err="Invalid or incomplete multibyte or wide character: read offset=0" +_test_fill_and_verify && \ +_test_corrupted_reftag && \ +_test_corrupted_data + +rm -f "$fio_err" + +_cleanup_test +_show_result "$TID" $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_loop_01.sh b/tools/testing/selftests/ublk/test_loop_01.sh index 338a235fd82a..c0f5b619ad6e 100755 --- a/tools/testing/selftests/ublk/test_loop_01.sh +++ b/tools/testing/selftests/ublk/test_loop_01.sh @@ -20,6 +20,6 @@ _check_add_dev $TID $? _run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=256M ERR_CODE=$? -_cleanup_test "loop" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_loop_02.sh b/tools/testing/selftests/ublk/test_loop_02.sh index 04c52454e2ec..f4191ea71f50 100755 --- a/tools/testing/selftests/ublk/test_loop_02.sh +++ b/tools/testing/selftests/ublk/test_loop_02.sh @@ -14,6 +14,6 @@ _check_add_dev $TID $? _mkfs_mount_test /dev/ublkb"${dev_id}" ERR_CODE=$? -_cleanup_test "loop" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_loop_03.sh b/tools/testing/selftests/ublk/test_loop_03.sh index 6e8f649fe93d..aaac0c59a5ad 100755 --- a/tools/testing/selftests/ublk/test_loop_03.sh +++ b/tools/testing/selftests/ublk/test_loop_03.sh @@ -19,6 +19,6 @@ _check_add_dev $TID $? _run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=256M ERR_CODE=$? -_cleanup_test "loop" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_loop_04.sh b/tools/testing/selftests/ublk/test_loop_04.sh index 9f6774ec0de6..f584c119f1d2 100755 --- a/tools/testing/selftests/ublk/test_loop_04.sh +++ b/tools/testing/selftests/ublk/test_loop_04.sh @@ -15,6 +15,6 @@ _check_add_dev $TID $? _mkfs_mount_test /dev/ublkb"${dev_id}" ERR_CODE=$? -_cleanup_test "loop" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_loop_05.sh b/tools/testing/selftests/ublk/test_loop_05.sh index 2b8d99e007be..ca1a5df5f9de 100755 --- a/tools/testing/selftests/ublk/test_loop_05.sh +++ b/tools/testing/selftests/ublk/test_loop_05.sh @@ -20,6 +20,6 @@ _check_add_dev $TID $? _run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=256M ERR_CODE=$? -_cleanup_test "loop" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_loop_06.sh b/tools/testing/selftests/ublk/test_loop_06.sh index e73f6f4844db..26f710ba9db7 100755 --- a/tools/testing/selftests/ublk/test_loop_06.sh +++ b/tools/testing/selftests/ublk/test_loop_06.sh @@ -19,6 +19,6 @@ _check_add_dev $TID $? _run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=256M ERR_CODE=$? -_cleanup_test "loop" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_loop_07.sh b/tools/testing/selftests/ublk/test_loop_07.sh index 264d20e7c530..a9ab0b671cb2 100755 --- a/tools/testing/selftests/ublk/test_loop_07.sh +++ b/tools/testing/selftests/ublk/test_loop_07.sh @@ -15,6 +15,6 @@ _check_add_dev $TID $? _mkfs_mount_test /dev/ublkb"${dev_id}" ERR_CODE=$? -_cleanup_test "loop" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_null_01.sh b/tools/testing/selftests/ublk/test_null_01.sh index eebce8076530..d2c38cbb2dd5 100755 --- a/tools/testing/selftests/ublk/test_null_01.sh +++ b/tools/testing/selftests/ublk/test_null_01.sh @@ -18,6 +18,6 @@ _check_add_dev $TID $? fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite --iodepth=32 --size=256M > /dev/null 2>&1 ERR_CODE=$? -_cleanup_test "null" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_null_02.sh b/tools/testing/selftests/ublk/test_null_02.sh index 654bdff39664..7b205ca56367 100755 --- a/tools/testing/selftests/ublk/test_null_02.sh +++ b/tools/testing/selftests/ublk/test_null_02.sh @@ -18,6 +18,6 @@ _check_add_dev $TID $? fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite --iodepth=32 --size=256M > /dev/null 2>&1 ERR_CODE=$? -_cleanup_test "null" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_null_03.sh b/tools/testing/selftests/ublk/test_null_03.sh index 29cd09f06672..eee7a87a60da 100755 --- a/tools/testing/selftests/ublk/test_null_03.sh +++ b/tools/testing/selftests/ublk/test_null_03.sh @@ -18,6 +18,6 @@ _check_add_dev $TID $? fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite --iodepth=32 --size=256M > /dev/null 2>&1 ERR_CODE=$? -_cleanup_test "null" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_part_01.sh b/tools/testing/selftests/ublk/test_part_01.sh index 8028f6e4b3a5..fa3b1a9af894 100755 --- a/tools/testing/selftests/ublk/test_part_01.sh +++ b/tools/testing/selftests/ublk/test_part_01.sh @@ -82,7 +82,7 @@ fi _prep_test "generic" "test UBLK_F_NO_AUTO_PART_SCAN" if ! _have_feature "UBLK_F_NO_AUTO_PART_SCAN"; then - _cleanup_test "generic" + _cleanup_test exit "$UBLK_SKIP_CODE" fi @@ -100,5 +100,5 @@ format_backing_file "${UBLK_BACKFILES[0]}" [ "$ERR_CODE" -eq 0 ] && test_no_auto_part_scan "${UBLK_BACKFILES[0]}" [ $? -ne 0 ] && ERR_CODE=255 -_cleanup_test "generic" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_part_02.sh b/tools/testing/selftests/ublk/test_part_02.sh index 7d42ab4d6e83..d9ec06f36aee 100755 --- a/tools/testing/selftests/ublk/test_part_02.sh +++ b/tools/testing/selftests/ublk/test_part_02.sh @@ -63,5 +63,5 @@ _test_partition_scan_no_hang "no" "DEAD" # Test 2: With recovery support - should transition to QUIESCED _test_partition_scan_no_hang "yes" "QUIESCED" -_cleanup_test "partition_scan" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_recover_01.sh b/tools/testing/selftests/ublk/test_recover_01.sh index 2672f9c40fa8..1cddc2345dab 100755 --- a/tools/testing/selftests/ublk/test_recover_01.sh +++ b/tools/testing/selftests/ublk/test_recover_01.sh @@ -40,5 +40,5 @@ ublk_run_recover_test -t loop -q 2 -r 1 -i 1 "${UBLK_BACKFILES[0]}" & ublk_run_recover_test -t stripe -q 2 -r 1 -i 1 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & wait -_cleanup_test "recover" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_recover_02.sh b/tools/testing/selftests/ublk/test_recover_02.sh index bda5064bc31f..9c3f481880d3 100755 --- a/tools/testing/selftests/ublk/test_recover_02.sh +++ b/tools/testing/selftests/ublk/test_recover_02.sh @@ -44,5 +44,5 @@ ublk_run_recover_test -t loop -q 2 -r 1 -z -i 1 "${UBLK_BACKFILES[0]}" & ublk_run_recover_test -t stripe -q 2 -r 1 -z -i 1 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & wait -_cleanup_test "recover" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_recover_03.sh b/tools/testing/selftests/ublk/test_recover_03.sh index e0dc0b8fe5d6..2554805e5b02 100755 --- a/tools/testing/selftests/ublk/test_recover_03.sh +++ b/tools/testing/selftests/ublk/test_recover_03.sh @@ -39,5 +39,5 @@ ublk_run_quiesce_recover -t loop -q 2 -r 1 -i 1 "${UBLK_BACKFILES[0]}" & ublk_run_quiesce_recover -t stripe -q 2 -r 1 -i 1 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & wait -_cleanup_test "quiesce" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_recover_04.sh b/tools/testing/selftests/ublk/test_recover_04.sh index 178443394ca5..4c83c1840c68 100755 --- a/tools/testing/selftests/ublk/test_recover_04.sh +++ b/tools/testing/selftests/ublk/test_recover_04.sh @@ -35,5 +35,5 @@ ublk_run_recover_test -t loop -q 2 -r 1 -u -i 1 "${UBLK_BACKFILES[0]}" & ublk_run_recover_test -t stripe -q 2 -r 1 -u -i 1 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & wait -_cleanup_test "recover" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_shmemzc_01.sh b/tools/testing/selftests/ublk/test_shmemzc_01.sh index 47210af2aa20..b244ab3479a2 100755 --- a/tools/testing/selftests/ublk/test_shmemzc_01.sh +++ b/tools/testing/selftests/ublk/test_shmemzc_01.sh @@ -67,6 +67,6 @@ umount "$HTLB_MNT" rmdir "$HTLB_MNT" echo "$OLD_NR_HP" > /proc/sys/vm/nr_hugepages -_cleanup_test "shmem_zc" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_shmemzc_02.sh b/tools/testing/selftests/ublk/test_shmemzc_02.sh index aed9262494e9..810dccba6d84 100755 --- a/tools/testing/selftests/ublk/test_shmemzc_02.sh +++ b/tools/testing/selftests/ublk/test_shmemzc_02.sh @@ -63,6 +63,6 @@ umount "$HTLB_MNT" rmdir "$HTLB_MNT" echo "$OLD_NR_HP" > /proc/sys/vm/nr_hugepages -_cleanup_test "shmem_zc" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_shmemzc_03.sh b/tools/testing/selftests/ublk/test_shmemzc_03.sh index db967a9ffe81..606362491a32 100755 --- a/tools/testing/selftests/ublk/test_shmemzc_03.sh +++ b/tools/testing/selftests/ublk/test_shmemzc_03.sh @@ -64,6 +64,6 @@ umount "$HTLB_MNT" rmdir "$HTLB_MNT" echo "$OLD_NR_HP" > /proc/sys/vm/nr_hugepages -_cleanup_test "shmem_zc" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_shmemzc_04.sh b/tools/testing/selftests/ublk/test_shmemzc_04.sh index 899de088ece4..9a2a6c2e8abe 100755 --- a/tools/testing/selftests/ublk/test_shmemzc_04.sh +++ b/tools/testing/selftests/ublk/test_shmemzc_04.sh @@ -67,6 +67,6 @@ umount "$HTLB_MNT" rmdir "$HTLB_MNT" echo "$OLD_NR_HP" > /proc/sys/vm/nr_hugepages -_cleanup_test "shmem_zc" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stress_01.sh b/tools/testing/selftests/ublk/test_stress_01.sh index a9322ce496e9..f91783f27649 100755 --- a/tools/testing/selftests/ublk/test_stress_01.sh +++ b/tools/testing/selftests/ublk/test_stress_01.sh @@ -29,5 +29,5 @@ ublk_io_and_remove 256M -t loop -q 4 "${UBLK_BACKFILES[0]}" & ublk_io_and_remove 256M -t stripe -q 4 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & wait -_cleanup_test "stress" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stress_02.sh b/tools/testing/selftests/ublk/test_stress_02.sh index 6c114194f9c9..b128d11658a8 100755 --- a/tools/testing/selftests/ublk/test_stress_02.sh +++ b/tools/testing/selftests/ublk/test_stress_02.sh @@ -31,5 +31,5 @@ for nr_queue in 1 4; do wait done -_cleanup_test "stress" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stress_03.sh b/tools/testing/selftests/ublk/test_stress_03.sh index 4e81ca0db758..a0f0aba8eebc 100755 --- a/tools/testing/selftests/ublk/test_stress_03.sh +++ b/tools/testing/selftests/ublk/test_stress_03.sh @@ -49,5 +49,5 @@ if _have_feature "PER_IO_DAEMON"; then wait fi -_cleanup_test "stress" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stress_04.sh b/tools/testing/selftests/ublk/test_stress_04.sh index 6c6f44b172bc..896eae68d444 100755 --- a/tools/testing/selftests/ublk/test_stress_04.sh +++ b/tools/testing/selftests/ublk/test_stress_04.sh @@ -48,5 +48,5 @@ if _have_feature "PER_IO_DAEMON"; then wait fi -_cleanup_test "stress" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stress_05.sh b/tools/testing/selftests/ublk/test_stress_05.sh index 7e9324de2030..d6c00c72080d 100755 --- a/tools/testing/selftests/ublk/test_stress_05.sh +++ b/tools/testing/selftests/ublk/test_stress_05.sh @@ -79,5 +79,5 @@ if _have_feature "PER_IO_DAEMON"; then fi wait -_cleanup_test "stress" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stress_06.sh b/tools/testing/selftests/ublk/test_stress_06.sh index c72e5d0b14be..9481a273a4b4 100755 --- a/tools/testing/selftests/ublk/test_stress_06.sh +++ b/tools/testing/selftests/ublk/test_stress_06.sh @@ -34,5 +34,5 @@ ublk_io_and_remove 256M -t loop -q 4 -u --nthreads 8 --per_io_tasks "${UBLK_BACK ublk_io_and_remove 256M -t stripe -q 4 -u --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & wait -_cleanup_test "stress" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stress_07.sh b/tools/testing/selftests/ublk/test_stress_07.sh index 04c2764d5238..3e01c037cffb 100755 --- a/tools/testing/selftests/ublk/test_stress_07.sh +++ b/tools/testing/selftests/ublk/test_stress_07.sh @@ -34,5 +34,5 @@ ublk_io_and_kill_daemon 256M -t loop -q 4 -u --nthreads 8 --per_io_tasks "${UBLK ublk_io_and_kill_daemon 256M -t stripe -q 4 -u --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & wait -_cleanup_test "stress" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stress_08.sh b/tools/testing/selftests/ublk/test_stress_08.sh index 37f7d204879a..5f32424d2892 100755 --- a/tools/testing/selftests/ublk/test_stress_08.sh +++ b/tools/testing/selftests/ublk/test_stress_08.sh @@ -40,5 +40,5 @@ ublk_io_and_remove 256M -t stripe -q 4 --auto_zc -b "${UBLK_BACKFILES[1]}" "${UB ublk_io_and_remove 8G -t null -q 4 -z --auto_zc --auto_zc_fallback -b & wait -_cleanup_test "stress" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stress_09.sh b/tools/testing/selftests/ublk/test_stress_09.sh index 53c1e3b2ab30..64cb8d9b0438 100755 --- a/tools/testing/selftests/ublk/test_stress_09.sh +++ b/tools/testing/selftests/ublk/test_stress_09.sh @@ -39,5 +39,5 @@ ublk_io_and_kill_daemon 256M -t stripe -q 4 -b "${UBLK_BACKFILES[1]}" "${UBLK_BA ublk_io_and_kill_daemon 8G -t null -q 4 -z --auto_zc --auto_zc_fallback -b & wait -_cleanup_test "stress" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stripe_01.sh b/tools/testing/selftests/ublk/test_stripe_01.sh index 3bc821aadad8..9ffce477b461 100755 --- a/tools/testing/selftests/ublk/test_stripe_01.sh +++ b/tools/testing/selftests/ublk/test_stripe_01.sh @@ -21,5 +21,5 @@ _check_add_dev $TID $? _run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=512M ERR_CODE=$? -_cleanup_test "stripe" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stripe_02.sh b/tools/testing/selftests/ublk/test_stripe_02.sh index 4a7d2b21a6bf..4c172950a247 100755 --- a/tools/testing/selftests/ublk/test_stripe_02.sh +++ b/tools/testing/selftests/ublk/test_stripe_02.sh @@ -16,5 +16,5 @@ _check_add_dev $TID $? _mkfs_mount_test /dev/ublkb"${dev_id}" ERR_CODE=$? -_cleanup_test "stripe" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stripe_03.sh b/tools/testing/selftests/ublk/test_stripe_03.sh index a1c159d54e53..2cdf9f958988 100755 --- a/tools/testing/selftests/ublk/test_stripe_03.sh +++ b/tools/testing/selftests/ublk/test_stripe_03.sh @@ -21,5 +21,5 @@ _check_add_dev $TID $? _run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=512M ERR_CODE=$? -_cleanup_test "stripe" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stripe_04.sh b/tools/testing/selftests/ublk/test_stripe_04.sh index 0c30bd6c2b3b..e24120eaca0e 100755 --- a/tools/testing/selftests/ublk/test_stripe_04.sh +++ b/tools/testing/selftests/ublk/test_stripe_04.sh @@ -16,5 +16,5 @@ _check_add_dev $TID $? _mkfs_mount_test /dev/ublkb"${dev_id}" ERR_CODE=$? -_cleanup_test "stripe" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stripe_05.sh b/tools/testing/selftests/ublk/test_stripe_05.sh index 6ddfa88ad226..f3de2d5cdfe4 100755 --- a/tools/testing/selftests/ublk/test_stripe_05.sh +++ b/tools/testing/selftests/ublk/test_stripe_05.sh @@ -21,5 +21,5 @@ _check_add_dev $TID $? _run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=512M ERR_CODE=$? -_cleanup_test "stripe" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/selftests/ublk/test_stripe_06.sh b/tools/testing/selftests/ublk/test_stripe_06.sh index a2c7bf4cc613..3fd5cd902956 100755 --- a/tools/testing/selftests/ublk/test_stripe_06.sh +++ b/tools/testing/selftests/ublk/test_stripe_06.sh @@ -16,5 +16,5 @@ _check_add_dev $TID $? _mkfs_mount_test /dev/ublkb"${dev_id}" ERR_CODE=$? -_cleanup_test "stripe" +_cleanup_test _show_result $TID $ERR_CODE diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h index b4864aad2db0..9e0dfd3a85b0 100644 --- a/tools/testing/vma/include/dup.h +++ b/tools/testing/vma/include/dup.h @@ -1330,7 +1330,7 @@ static inline int __compat_vma_mmap(struct vm_area_desc *desc, /* Update the VMA from the descriptor. */ compat_set_vma_from_desc(vma, desc); /* Complete any specified mmap actions. */ - return mmap_action_complete(vma, &desc->action); + return mmap_action_complete(vma, &desc->action, /*is_compat=*/true); } static inline int compat_vma_mmap(struct file *file, struct vm_area_struct *vma) diff --git a/tools/testing/vma/include/stubs.h b/tools/testing/vma/include/stubs.h index a30b8bc84955..64164e25658f 100644 --- a/tools/testing/vma/include/stubs.h +++ b/tools/testing/vma/include/stubs.h @@ -87,7 +87,8 @@ static inline int mmap_action_prepare(struct vm_area_desc *desc) } static inline int mmap_action_complete(struct vm_area_struct *vma, - struct mmap_action *action) + struct mmap_action *action, + bool is_compat) { return 0; } diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c index 1fe1338c79cd..fe316b02a590 100644 --- a/tools/testing/vsock/util.c +++ b/tools/testing/vsock/util.c @@ -381,7 +381,13 @@ void send_buf(int fd, const void *buf, size_t len, int flags, } } +#define RECV_PEEK_RETRY_USEC (10 * 1000) + /* Receive bytes in a buffer and check the return value. + * + * When MSG_PEEK is set, recv() is retried until it returns at least + * expected_ret bytes. The function returns on error, EOF, or timeout + * as usual. * * expected_ret: * <0 Negative errno (for testing errors) @@ -403,6 +409,15 @@ void recv_buf(int fd, void *buf, size_t len, int flags, ssize_t expected_ret) if (ret <= 0) break; + if (flags & MSG_PEEK) { + if (ret >= expected_ret) { + nread = ret; + break; + } + timeout_usleep(RECV_PEEK_RETRY_USEC); + continue; + } + nread += ret; } while (nread < len); timeout_end(); diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index 5bd20ccd9335..76be0e4a7f0e 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -346,6 +346,38 @@ static void test_stream_msg_peek_server(const struct test_opts *opts) return test_msg_peek_server(opts, false); } +static void test_stream_peek_after_recv_server(const struct test_opts *opts) +{ + unsigned char buf_normal[MSG_PEEK_BUF_LEN]; + unsigned char buf_peek[MSG_PEEK_BUF_LEN]; + int fd; + + fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL); + if (fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + control_writeln("SRVREADY"); + + /* Partial recv to advance offset within the skb */ + recv_buf(fd, buf_normal, 1, 0, 1); + + /* Peek with a buffer larger than the remaining data */ + recv_buf(fd, buf_peek, sizeof(buf_peek), MSG_PEEK, sizeof(buf_peek) - 1); + + /* Consume the remaining data */ + recv_buf(fd, buf_normal, sizeof(buf_normal) - 1, 0, sizeof(buf_normal) - 1); + + /* Compare full peek and normal read. */ + if (memcmp(buf_peek, buf_normal, sizeof(buf_peek) - 1)) { + fprintf(stderr, "Full peek data mismatch\n"); + exit(EXIT_FAILURE); + } + + close(fd); +} + #define SOCK_BUF_SIZE (2 * 1024 * 1024) #define SOCK_BUF_SIZE_SMALL (64 * 1024) #define MAX_MSG_PAGES 4 @@ -1500,18 +1532,7 @@ static void test_stream_credit_update_test(const struct test_opts *opts, } /* Wait until there will be 128KB of data in rx queue. */ - while (1) { - ssize_t res; - - res = recv(fd, buf, buf_size, MSG_PEEK); - if (res == buf_size) - break; - - if (res <= 0) { - fprintf(stderr, "unexpected 'recv()' return: %zi\n", res); - exit(EXIT_FAILURE); - } - } + recv_buf(fd, buf, buf_size, MSG_PEEK, buf_size); /* There is 128KB of data in the socket's rx queue, dequeue first * 64KB, credit update is sent if 'low_rx_bytes_test' == true. @@ -2520,6 +2541,11 @@ static struct test_case test_cases[] = { .run_client = test_stream_tx_credit_bounds_client, .run_server = test_stream_tx_credit_bounds_server, }, + { + .name = "SOCK_STREAM MSG_PEEK after partial recv", + .run_client = test_stream_msg_peek_client, + .run_server = test_stream_peek_after_recv_server, + }, {}, }; diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c index 927a151fa9aa..1dfbb76ab26c 100644 --- a/tools/usb/usbip/libsrc/usbip_device_driver.c +++ b/tools/usb/usbip/libsrc/usbip_device_driver.c @@ -137,9 +137,9 @@ static int usbip_device_driver_open(struct usbip_host_driver *hdriver) INIT_LIST_HEAD(&hdriver->edev_list); ret = usbip_generic_driver_open(hdriver); - if (ret) - err("please load " USBIP_CORE_MOD_NAME ".ko and " - USBIP_DEVICE_DRV_NAME ".ko!"); + if (ret || hdriver->ndevs == 0) + info("please load " USBIP_CORE_MOD_NAME ".ko and " + USBIP_DEVICE_DRV_NAME ".ko"); return ret; } diff --git a/tools/usb/usbip/libsrc/usbip_host_common.c b/tools/usb/usbip/libsrc/usbip_host_common.c index ca78aa368476..01599cb2fa7b 100644 --- a/tools/usb/usbip/libsrc/usbip_host_common.c +++ b/tools/usb/usbip/libsrc/usbip_host_common.c @@ -149,6 +149,9 @@ static int refresh_exported_devices(struct usbip_host_driver *hdriver) } } + if (hdriver->ndevs == 0) + info("Please load appropriate modules or export devices."); + return 0; } diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.c b/tools/usb/usbip/libsrc/usbip_host_driver.c index 573e73ec36bd..bd8a6b84de0e 100644 --- a/tools/usb/usbip/libsrc/usbip_host_driver.c +++ b/tools/usb/usbip/libsrc/usbip_host_driver.c @@ -32,9 +32,10 @@ static int usbip_host_driver_open(struct usbip_host_driver *hdriver) INIT_LIST_HEAD(&hdriver->edev_list); ret = usbip_generic_driver_open(hdriver); - if (ret) - err("please load " USBIP_CORE_MOD_NAME ".ko and " - USBIP_HOST_DRV_NAME ".ko!"); + if (ret || hdriver->ndevs == 0) + info("please load " USBIP_CORE_MOD_NAME ".ko and " + USBIP_HOST_DRV_NAME ".ko"); + return ret; } diff --git a/virt/kvm/dirty_ring.c b/virt/kvm/dirty_ring.c index 02bc6b00d76c..572b854edf74 100644 --- a/virt/kvm/dirty_ring.c +++ b/virt/kvm/dirty_ring.c @@ -63,7 +63,8 @@ static void kvm_reset_dirty_gfn(struct kvm *kvm, u32 slot, u64 offset, u64 mask) memslot = id_to_memslot(__kvm_memslots(kvm, as_id), id); - if (!memslot || (offset + __fls(mask)) >= memslot->npages) + if (!memslot || offset >= memslot->npages || + offset + __fls(mask) >= memslot->npages) return; KVM_MMU_LOCK(kvm); diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index 42b237491c4e..69c9d6d546b2 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -127,14 +127,13 @@ static struct folio *kvm_gmem_get_folio(struct inode *inode, pgoff_t index) * Fast-path: See if folio is already present in mapping to avoid * policy_lookup. */ - folio = __filemap_get_folio(inode->i_mapping, index, - FGP_LOCK | FGP_ACCESSED, 0); + folio = filemap_lock_folio(inode->i_mapping, index); if (!IS_ERR(folio)) return folio; policy = mpol_shared_policy_lookup(&GMEM_I(inode)->policy, index); folio = __filemap_get_folio_mpol(inode->i_mapping, index, - FGP_LOCK | FGP_ACCESSED | FGP_CREAT, + FGP_LOCK | FGP_CREAT, mapping_gfp_mask(inode->i_mapping), policy); mpol_cond_put(policy); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 82433f46c438..89489996fbc1 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -75,22 +75,22 @@ MODULE_DESCRIPTION("Kernel-based Virtual Machine (KVM) Hypervisor"); MODULE_LICENSE("GPL"); /* Architectures should define their poll value according to the halt latency */ -unsigned int halt_poll_ns = KVM_HALT_POLL_NS_DEFAULT; +unsigned int __read_mostly halt_poll_ns = KVM_HALT_POLL_NS_DEFAULT; module_param(halt_poll_ns, uint, 0644); EXPORT_SYMBOL_FOR_KVM_INTERNAL(halt_poll_ns); /* Default doubles per-vcpu halt_poll_ns. */ -unsigned int halt_poll_ns_grow = 2; +unsigned int __read_mostly halt_poll_ns_grow = 2; module_param(halt_poll_ns_grow, uint, 0644); EXPORT_SYMBOL_FOR_KVM_INTERNAL(halt_poll_ns_grow); /* The start value to grow halt_poll_ns from */ -unsigned int halt_poll_ns_grow_start = 10000; /* 10us */ +unsigned int __read_mostly halt_poll_ns_grow_start = 10000; /* 10us */ module_param(halt_poll_ns_grow_start, uint, 0644); EXPORT_SYMBOL_FOR_KVM_INTERNAL(halt_poll_ns_grow_start); /* Default halves per-vcpu halt_poll_ns. */ -unsigned int halt_poll_ns_shrink = 2; +unsigned int __read_mostly halt_poll_ns_shrink = 2; module_param(halt_poll_ns_shrink, uint, 0644); EXPORT_SYMBOL_FOR_KVM_INTERNAL(halt_poll_ns_shrink); @@ -98,7 +98,7 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(halt_poll_ns_shrink); * Allow direct access (from KVM or the CPU) without MMU notifier protection * to unpinned pages. */ -static bool allow_unsafe_mappings; +static bool __ro_after_init allow_unsafe_mappings; module_param(allow_unsafe_mappings, bool, 0444); /* @@ -1092,6 +1092,9 @@ static inline struct kvm_io_bus *kvm_get_bus_for_destruction(struct kvm *kvm, !refcount_read(&kvm->users_count)); } +static int kvm_enable_virtualization(void); +static void kvm_disable_virtualization(void); + static struct kvm *kvm_create_vm(unsigned long type, const char *fdname) { struct kvm *kvm = kvm_arch_alloc_vm(); @@ -5564,17 +5567,19 @@ static struct miscdevice kvm_dev = { }; #ifdef CONFIG_KVM_GENERIC_HARDWARE_ENABLING -bool enable_virt_at_load = true; +bool __ro_after_init enable_virt_at_load = true; module_param(enable_virt_at_load, bool, 0444); EXPORT_SYMBOL_FOR_KVM_INTERNAL(enable_virt_at_load); -__visible bool kvm_rebooting; -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_rebooting); - static DEFINE_PER_CPU(bool, virtualization_enabled); static DEFINE_MUTEX(kvm_usage_lock); static int kvm_usage_count; +__weak void kvm_arch_shutdown(void) +{ + +} + __weak void kvm_arch_enable_virtualization(void) { @@ -5628,10 +5633,9 @@ static int kvm_offline_cpu(unsigned int cpu) static void kvm_shutdown(void *data) { + kvm_arch_shutdown(); + /* - * Disable hardware virtualization and set kvm_rebooting to indicate - * that KVM has asynchronously disabled hardware virtualization, i.e. - * that relevant errors and exceptions aren't entirely unexpected. * Some flavors of hardware virtualization need to be disabled before * transferring control to firmware (to perform shutdown/reboot), e.g. * on x86, virtualization can block INIT interrupts, which are used by @@ -5640,7 +5644,6 @@ static void kvm_shutdown(void *data) * 100% comprehensive. */ pr_info("kvm: exiting hardware virtualization\n"); - kvm_rebooting = true; on_each_cpu(kvm_disable_virtualization_cpu, NULL, 1); } @@ -5679,7 +5682,7 @@ static struct syscore kvm_syscore = { .ops = &kvm_syscore_ops, }; -int kvm_enable_virtualization(void) +static int kvm_enable_virtualization(void) { int r; @@ -5724,9 +5727,8 @@ err_cpuhp: --kvm_usage_count; return r; } -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_enable_virtualization); -void kvm_disable_virtualization(void) +static void kvm_disable_virtualization(void) { guard(mutex)(&kvm_usage_lock); @@ -5737,7 +5739,6 @@ void kvm_disable_virtualization(void) cpuhp_remove_state(CPUHP_AP_KVM_ONLINE); kvm_arch_disable_virtualization(); } -EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_disable_virtualization); static int kvm_init_virtualization(void) { @@ -5753,6 +5754,14 @@ static void kvm_uninit_virtualization(void) kvm_disable_virtualization(); } #else /* CONFIG_KVM_GENERIC_HARDWARE_ENABLING */ +static int kvm_enable_virtualization(void) +{ + return 0; +} +static void kvm_disable_virtualization(void) +{ + +} static int kvm_init_virtualization(void) { return 0;